()
| 417 | } |
| 418 | |
| 419 | async function main() { |
| 420 | const rawOptions = parseArgs(process.argv.slice(2)); |
| 421 | if (rawOptions.help) { |
| 422 | printUsage(); |
| 423 | return; |
| 424 | } |
| 425 | |
| 426 | const options = resolveInstanceOptions(rawOptions); |
| 427 | // Refuse to operate inside a hijacked/symlinked profile root before we stop or |
| 428 | // remove anything under it. |
| 429 | await assertSecureDirIfPresent(options.profileRoot); |
| 430 | const summary = await stopInstance(options.instancePath, { |
| 431 | dryRun: rawOptions.dryRun, |
| 432 | profileRoot: options.profileRoot, |
| 433 | }); |
| 434 | |
| 435 | let pruneResult = { scanned: 0, reaped: [] }; |
| 436 | if (rawOptions.prune) { |
| 437 | pruneResult = await reapOrphanedInstances({ |
| 438 | profileRoot: options.profileRoot, |
| 439 | exceptInstancePath: options.instancePath, |
| 440 | dryRun: rawOptions.dryRun, |
| 441 | log: rawOptions.json ? () => {} : console.error, |
| 442 | }); |
| 443 | } |
| 444 | |
| 445 | const result = { |
| 446 | ...summary, |
| 447 | vaultName: options.vaultName, |
| 448 | prune: pruneResult, |
| 449 | }; |
| 450 | if (rawOptions.json) { |
| 451 | console.log(JSON.stringify(result, null, 2)); |
| 452 | return; |
| 453 | } |
| 454 | |
| 455 | const verb = rawOptions.dryRun ? "Would stop" : "Stopped"; |
| 456 | console.log(`${verb} Obsidian E2E instance ${options.vaultName}`); |
| 457 | console.log(`Instance dir: ${options.instancePath}`); |
| 458 | console.log( |
| 459 | summary.pids.length > 0 |
| 460 | ? `Process tree: ${summary.pids.join(", ")}` |
| 461 | : "Process tree: none running", |
| 462 | ); |
| 463 | if (rawOptions.prune && pruneResult.reaped.length > 0) { |
| 464 | console.log(`Reaped ${pruneResult.reaped.length} orphaned instance(s).`); |
| 465 | } |
| 466 | } |
| 467 | |
| 468 | if (import.meta.url === `file://${process.argv[1]}`) { |
| 469 | main().catch((error) => { |
no test coverage detected