|
| 1 | +#!/usr/bin/env node |
| 2 | +/** |
| 3 | + * Safely inspect or quarantine stale test/temp workspace memory stores. |
| 4 | + * |
| 5 | + * Default mode is dry-run. Quarantine moves only definite temp/test residues. |
| 6 | + * Unknown missing roots are reported but skipped unless --include-orphans is set. |
| 7 | + */ |
| 8 | + |
| 9 | +import { cleanupWorkspaceResidues } from "../../src/workspace-cleanup.ts"; |
| 10 | + |
| 11 | +type CliOptions = { |
| 12 | + mode: "dry-run" | "quarantine"; |
| 13 | + dataHome?: string; |
| 14 | + olderThanDays?: number; |
| 15 | + includeOrphans: boolean; |
| 16 | +}; |
| 17 | + |
| 18 | +function usage(): string { |
| 19 | + return `Usage: |
| 20 | + npm run cleanup:workspaces -- --dry-run |
| 21 | + npm run cleanup:workspaces -- --quarantine |
| 22 | + npm run cleanup:workspaces -- --quarantine --older-than-days 1 |
| 23 | +
|
| 24 | +Options: |
| 25 | + --dry-run List candidates without moving anything (default) |
| 26 | + --quarantine Move definite temp/test residues to quarantine |
| 27 | + --data-home <path> Override XDG data home for testing/admin work |
| 28 | + --older-than-days <n> Only consider workspace dirs older than n days |
| 29 | + --include-orphans Also quarantine missing non-temp roots (off by default) |
| 30 | + --help Show this help |
| 31 | +`; |
| 32 | +} |
| 33 | + |
| 34 | +function parseArgs(argv: string[]): CliOptions { |
| 35 | + const options: CliOptions = { mode: "dry-run", includeOrphans: false }; |
| 36 | + |
| 37 | + for (let i = 0; i < argv.length; i++) { |
| 38 | + const arg = argv[i]; |
| 39 | + switch (arg) { |
| 40 | + case "--dry-run": |
| 41 | + options.mode = "dry-run"; |
| 42 | + break; |
| 43 | + case "--quarantine": |
| 44 | + options.mode = "quarantine"; |
| 45 | + break; |
| 46 | + case "--data-home": |
| 47 | + options.dataHome = argv[++i]; |
| 48 | + if (!options.dataHome) throw new Error("--data-home requires a path"); |
| 49 | + break; |
| 50 | + case "--older-than-days": { |
| 51 | + const value = Number(argv[++i]); |
| 52 | + if (!Number.isFinite(value) || value < 0) throw new Error("--older-than-days requires a non-negative number"); |
| 53 | + options.olderThanDays = value; |
| 54 | + break; |
| 55 | + } |
| 56 | + case "--include-orphans": |
| 57 | + options.includeOrphans = true; |
| 58 | + break; |
| 59 | + case "--help": |
| 60 | + case "-h": |
| 61 | + console.log(usage()); |
| 62 | + process.exit(0); |
| 63 | + default: |
| 64 | + throw new Error(`Unknown option: ${arg}\n${usage()}`); |
| 65 | + } |
| 66 | + } |
| 67 | + |
| 68 | + return options; |
| 69 | +} |
| 70 | + |
| 71 | +const options = parseArgs(process.argv.slice(2)); |
| 72 | +const result = await cleanupWorkspaceResidues({ |
| 73 | + dataHome: options.dataHome, |
| 74 | + mode: options.mode, |
| 75 | + includeOrphans: options.includeOrphans, |
| 76 | + minAgeMs: options.olderThanDays === undefined ? undefined : options.olderThanDays * 24 * 60 * 60 * 1_000, |
| 77 | +}); |
| 78 | + |
| 79 | +console.log(`Mode: ${result.mode}`); |
| 80 | +console.log(`Scanned: ${result.results.length}`); |
| 81 | +console.log(`Candidates: ${result.candidates.length}`); |
| 82 | + |
| 83 | +if (result.candidates.length > 0) { |
| 84 | + console.log("\nCandidates:"); |
| 85 | + for (const candidate of result.candidates) { |
| 86 | + console.log(`- ${candidate.workspaceKey} ${candidate.classification} root=${candidate.root ?? "<missing>"}`); |
| 87 | + console.log(` reasons=${candidate.reasons.join(",")}`); |
| 88 | + } |
| 89 | +} |
| 90 | + |
| 91 | +if (result.quarantined.length > 0) { |
| 92 | + console.log(`\nQuarantined: ${result.quarantined.length}`); |
| 93 | + console.log(`Quarantine dir: ${result.quarantineDir}`); |
| 94 | +} |
| 95 | + |
| 96 | +const unknownOrphans = result.results.filter(item => item.classification === "orphan_unknown"); |
| 97 | +if (unknownOrphans.length > 0 && !options.includeOrphans) { |
| 98 | + console.log(`\nUnknown missing-root workspaces skipped: ${unknownOrphans.length}`); |
| 99 | + console.log("Use --include-orphans only after manually confirming they are safe to quarantine."); |
| 100 | +} |
0 commit comments