@saadnvd1/agent-os 0.3.662 → 0.3.671

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/app/api/audits/[kind]/[id]/route.test.ts +2 -2
  2. package/app/api/mcp-auth/callback/route.test.ts +42 -0
  3. package/app/api/mcp-auth/callback/route.ts +56 -0
  4. package/app/api/projects/init/route.test.ts +2 -4
  5. package/app/api/sessions/[id]/mcp-auth/route.test.ts +86 -0
  6. package/app/api/sessions/[id]/mcp-auth/route.ts +58 -0
  7. package/app/api/sessions/[id]/route.ts +3 -3
  8. package/app/api/shares/route.test.ts +3 -4
  9. package/app/api/workspaces/[id]/route.ts +9 -1
  10. package/app/api/worktree-sweep/route.test.ts +40 -1
  11. package/app/api/worktree-sweep/route.ts +45 -6
  12. package/components/Chat/ChatPanel.tsx +6 -3
  13. package/components/Chat/McpServers.tsx +61 -24
  14. package/components/Chat/McpSignIn.tsx +122 -0
  15. package/components/Chat/session.ts +8 -0
  16. package/components/Health/SweepRuns.tsx +77 -1
  17. package/components/Workspaces/WorkspaceSettings.tsx +6 -0
  18. package/data/chat/mcpAuth.ts +42 -0
  19. package/data/workspaces/index.ts +6 -0
  20. package/data/worktree-sweep/index.ts +15 -0
  21. package/lib/agentos-home.ts +11 -0
  22. package/lib/agents/launch.ts +3 -7
  23. package/lib/artifacts/store.ts +2 -5
  24. package/lib/chat/driver.ts +24 -2
  25. package/lib/chat/drivers/claude-approvals.ts +7 -0
  26. package/lib/chat/drivers/claude-mcp-auth.ts +82 -0
  27. package/lib/chat/drivers/claude.ts +7 -0
  28. package/lib/chat/drivers/pi-approvals.ts +2 -2
  29. package/lib/chat/events.ts +12 -0
  30. package/lib/chat/mcp-auth.ts +193 -0
  31. package/lib/chat/registry.ts +2 -0
  32. package/lib/chat/runner.ts +18 -0
  33. package/lib/chat/worker/client.ts +35 -1
  34. package/lib/chat/worker/host.ts +9 -0
  35. package/lib/chat/worker/main.ts +1 -1
  36. package/lib/chat/worker/mcp-auth.ts +119 -0
  37. package/lib/chat/worker/protocol.ts +21 -2
  38. package/lib/connect/config.ts +2 -3
  39. package/lib/db/backup.ts +2 -2
  40. package/lib/db/migrations/20261018T1600_health_boot_deploys.ts +13 -0
  41. package/lib/db/migrations/20261018T1730_mcp_sign_ins.ts +22 -0
  42. package/lib/db/migrations/20261018T2100_orch_full_access.ts +16 -0
  43. package/lib/db/types.ts +3 -0
  44. package/lib/dev-instance/processInfo.ts +48 -27
  45. package/lib/dev-instance/startDevServer.ts +1 -1
  46. package/lib/dev-instance/stopDevServer.ts +20 -17
  47. package/lib/dev-servers.ts +2 -1
  48. package/lib/done/index.ts +2 -2
  49. package/lib/done/testing.ts +2 -2
  50. package/lib/env-setup.ts +8 -1
  51. package/lib/health/boots.ts +31 -0
  52. package/lib/health/ci.ts +23 -2
  53. package/lib/health/detectors.ts +2 -1
  54. package/lib/health/evidence.ts +1 -1
  55. package/lib/health/facts.ts +3 -3
  56. package/lib/health/fixtures.ts +1 -0
  57. package/lib/health/tick.ts +6 -1
  58. package/lib/health/types.ts +2 -0
  59. package/lib/orchestrator/armed.test-helpers.ts +3 -2
  60. package/lib/orchestrator/brief.ts +14 -9
  61. package/lib/orchestrator/external-gates.ts +62 -1
  62. package/lib/orchestrator/full-access.ts +21 -0
  63. package/lib/orchestrator/home.ts +2 -5
  64. package/lib/orchestrator/quick.ts +47 -4
  65. package/lib/orchestrator/serve.ts +11 -2
  66. package/lib/orchestrator/shell-guard.ts +24 -0
  67. package/lib/orchestrator/signoff.test-helpers.ts +3 -2
  68. package/lib/orchestrator/sweep-arm.ts +180 -0
  69. package/lib/orchestrator/tool-names.ts +4 -1
  70. package/lib/orchestrator/tool-schemas.ts +33 -1
  71. package/lib/orchestrator/tools.ts +6 -2
  72. package/lib/orchestrator/write-guard.ts +37 -9
  73. package/lib/plans/features.ts +2 -2
  74. package/lib/program-status/claude-hooks.ts +2 -2
  75. package/lib/program-status/tap.ts +2 -2
  76. package/lib/project-config/brief.ts +3 -1
  77. package/lib/project-config/database.ts +77 -16
  78. package/lib/project-config/index.ts +108 -21
  79. package/lib/project-config/schema.ts +10 -4
  80. package/lib/project-config/session.ts +27 -6
  81. package/lib/security/net.ts +7 -4
  82. package/lib/security/tailnet-https.ts +2 -2
  83. package/lib/session-artifacts/collect.ts +18 -1
  84. package/lib/session-artifacts/keep.ts +2 -4
  85. package/lib/session-audit/store.ts +2 -5
  86. package/lib/session-logs/folder.ts +2 -3
  87. package/lib/sessions/launch.ts +2 -2
  88. package/lib/sessions/leftovers.ts +61 -0
  89. package/lib/simulator/agent-device.ts +2 -2
  90. package/lib/tasks/finish.ts +2 -2
  91. package/lib/tasks/import.ts +3 -5
  92. package/lib/tasks/move-testing.ts +2 -2
  93. package/lib/tasks/move.ts +2 -2
  94. package/lib/tasks/moved-away.ts +2 -0
  95. package/lib/tasks/state.ts +7 -3
  96. package/lib/tasks/ui-brief.ts +2 -2
  97. package/lib/test-temp.ts +30 -0
  98. package/lib/workspaces.ts +6 -2
  99. package/lib/worktree-deps/fastcopy.ts +2 -2
  100. package/lib/worktree-deps/spare.ts +2 -2
  101. package/lib/worktree-sweep/caches.ts +129 -0
  102. package/lib/worktree-sweep/databases.ts +222 -0
  103. package/lib/worktree-sweep/du.ts +13 -0
  104. package/lib/worktree-sweep/extras.ts +196 -0
  105. package/lib/worktree-sweep/findOrphans.ts +19 -8
  106. package/lib/worktree-sweep/report.ts +86 -0
  107. package/lib/worktree-sweep/runSweep.ts +46 -13
  108. package/lib/worktree-sweep/settings.ts +24 -0
  109. package/lib/worktree-sweep/state.ts +5 -0
  110. package/lib/worktree-sweep/store.ts +11 -0
  111. package/lib/worktree-sweep/testTemp.ts +113 -0
  112. package/lib/worktree-sweep/types.ts +46 -1
  113. package/lib/worktrees.ts +3 -0
  114. package/package.json +2 -1
  115. package/scripts/autodeploy.test.ts +2 -2
  116. package/scripts/check-bundle.test.ts +2 -2
  117. package/scripts/disk-dry-run.ts +57 -0
  118. package/scripts/hooks/banned-terms.test.ts +3 -2
  119. package/scripts/hooks/staged-secrets.test.ts +2 -2
  120. package/scripts/lib/build-swap.test.ts +2 -3
  121. package/scripts/lib/wsl.test.ts +2 -3
  122. package/scripts/update-run.test.ts +2 -2
@@ -81,3 +81,27 @@ export function projectSweepOverrides(): Array<{
81
81
  return days === null ? [] : [{ projectId, days }];
82
82
  });
83
83
  }
84
+
85
+ // The full clean-up (test leftovers, task databases, spares, temp folders,
86
+ // build caches) removes nothing until Saad turns it on for this machine, or
87
+ // the orchestrator does on his word after a dry run from the last hour.
88
+ const ARMED_KEY = "worktree_sweep.reaper";
89
+
90
+ export function reaperArmed(): boolean {
91
+ const row = db
92
+ .prepare(`SELECT value FROM settings WHERE key = ?`)
93
+ .get(ARMED_KEY) as { value: string } | undefined;
94
+ if (!row) return false;
95
+ try {
96
+ return JSON.parse(row.value)?.armed === true;
97
+ } catch {
98
+ return false;
99
+ }
100
+ }
101
+
102
+ export function setReaperArmed(armed: boolean): void {
103
+ db.prepare(
104
+ `INSERT INTO settings (key, value) VALUES (?, ?)
105
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value`
106
+ ).run(ARMED_KEY, JSON.stringify({ armed, at: new Date().toISOString() }));
107
+ }
@@ -3,6 +3,7 @@ import {
3
3
  DEFAULT_SWEEP_DAYS,
4
4
  globalSweepDays,
5
5
  projectSweepOverrides,
6
+ reaperArmed,
6
7
  } from "./settings";
7
8
  import { recentRuns } from "./store";
8
9
  import type { SweepRun } from "./types";
@@ -13,6 +14,9 @@ export interface SweepState {
13
14
  defaultDays: number;
14
15
  overrides: Array<{ projectId: string; name: string; days: number }>;
15
16
  runs: SweepRun[];
17
+ // Whether the full clean-up (test leftovers, task databases, spares, temp
18
+ // folders, build caches) removes anything on this machine.
19
+ armed: boolean;
16
20
  }
17
21
 
18
22
  export function sweepState(): SweepState {
@@ -32,5 +36,6 @@ export function sweepState(): SweepState {
32
36
  : []
33
37
  ),
34
38
  runs: recentRuns(),
39
+ armed: reaperArmed(),
35
40
  };
36
41
  }
@@ -51,6 +51,17 @@ export function finishRun(
51
51
  summary.skipped.length,
52
52
  JSON.stringify({
53
53
  ...summary,
54
+ // The biggest first: they are what a person looks for.
55
+ removed: [...summary.removed]
56
+ .sort((a, b) => b.bytes - a.bytes)
57
+ .slice(0, DETAIL_CAP),
58
+ pending: [...(summary.pending ?? [])]
59
+ .sort((a, b) => b.bytes - a.bytes)
60
+ .slice(0, DETAIL_CAP),
61
+ pendingTotal: {
62
+ count: summary.pending?.length ?? 0,
63
+ bytes: (summary.pending ?? []).reduce((n, p) => n + p.bytes, 0),
64
+ },
54
65
  skipped: summary.skipped.slice(0, DETAIL_CAP),
55
66
  }),
56
67
  error,
@@ -0,0 +1,113 @@
1
+ // Folders the test suite makes in the OS temp folder, by name. The sweep
2
+ // takes only these, never another aos-… or agent-os-… folder, so AgentOS's
3
+ // own temp folders (aos-review-, aos-rebase-…) and a person's are safe.
4
+ // lib/test-temp.test.ts fails when a test uses a prefix missing here.
5
+ export const TEST_TEMP_PREFIXES: readonly string[] = [
6
+ "agent-os-git-",
7
+ "agent-os-home-",
8
+ "agent-os-kept-",
9
+ "agent-os-limits-",
10
+ "agent-os-session-logs-",
11
+ "agent-os-test-",
12
+ "aos-acc-",
13
+ "aos-add-",
14
+ "aos-armed-repo-",
15
+ "aos-art-",
16
+ "aos-autodeploy-",
17
+ "aos-caches-",
18
+ "aos-channel-",
19
+ "aos-clone-",
20
+ "aos-config-",
21
+ "aos-crypt-",
22
+ "aos-db-",
23
+ "aos-db-drops-",
24
+ "aos-db-wt-",
25
+ "aos-decl-",
26
+ "aos-device-keys-",
27
+ "aos-done-home-",
28
+ "aos-done-repo-",
29
+ "aos-env-",
30
+ "aos-fast-",
31
+ "aos-files-",
32
+ "aos-git-",
33
+ "aos-gr-",
34
+ "aos-heavy-",
35
+ "aos-init-route-",
36
+ "aos-install-",
37
+ "aos-kind-",
38
+ "aos-launch-plain-",
39
+ "aos-launch-repo-",
40
+ "aos-limits-",
41
+ "aos-mcp-",
42
+ "aos-merge-policy-",
43
+ "aos-mig-",
44
+ "aos-move-",
45
+ "aos-no-approvals-",
46
+ "aos-nogit-",
47
+ "aos-notify-",
48
+ "aos-orch-act-",
49
+ "aos-orch-add-",
50
+ "aos-orch-approve-",
51
+ "aos-orch-armed-",
52
+ "aos-orch-asks-",
53
+ "aos-orch-brakes-",
54
+ "aos-orch-ext-repo-",
55
+ "aos-orch-external-",
56
+ "aos-orch-home-",
57
+ "aos-orch-move-",
58
+ "aos-orch-plain-",
59
+ "aos-orch-questions-",
60
+ "aos-orch-recipes-",
61
+ "aos-orch-remind-",
62
+ "aos-orch-repo-",
63
+ "aos-orch-rules-",
64
+ "aos-orch-settle-",
65
+ "aos-orch-signoff-",
66
+ "aos-orch-sweep-",
67
+ "aos-origin-",
68
+ "aos-path-",
69
+ "aos-plain-",
70
+ "aos-plan-",
71
+ "aos-ports-",
72
+ "aos-pr-",
73
+ "aos-pr-async-",
74
+ "aos-pr-create-",
75
+ "aos-pr-gen-",
76
+ "aos-pr-route-",
77
+ "aos-prompts-",
78
+ "aos-quick-",
79
+ "aos-quick-repo-",
80
+ "aos-relay-",
81
+ "aos-relay-none-",
82
+ "aos-report-",
83
+ "aos-resurrect-claude-",
84
+ "aos-resurrect-home-",
85
+ "aos-run-",
86
+ "aos-runner-",
87
+ "aos-sec-",
88
+ "aos-shot-",
89
+ "aos-sim-",
90
+ "aos-spare-",
91
+ "aos-stack-",
92
+ "aos-start-opts-",
93
+ "aos-start-plain-",
94
+ "aos-sweep-own-",
95
+ "aos-t-",
96
+ "aos-task-diff-",
97
+ "aos-temp-helper-",
98
+ "aos-tmux-",
99
+ "aos-transcripts-",
100
+ "aos-ui-",
101
+ "aos-upd-",
102
+ "aos-usage-",
103
+ "aos-wg-",
104
+ "aos-wg-path-",
105
+ "aos-wl-",
106
+ "aos-worker-env-",
107
+ "aos-worker-tmux-",
108
+ "aos-wsl-",
109
+ "aos-wt-",
110
+ ];
111
+
112
+ export const isTestTempName = (name: string) =>
113
+ TEST_TEMP_PREFIXES.some((p) => name.startsWith(p));
@@ -1,4 +1,23 @@
1
- export type SweepKind = "settled" | "orphan" | "test-leak";
1
+ export type SweepKind =
2
+ | "settled"
3
+ | "orphan"
4
+ | "test-leak"
5
+ // What a task left beside its checkout, found by the full clean-up.
6
+ | "database"
7
+ | "spare"
8
+ | "temp"
9
+ | "test-temp"
10
+ | "derived-data";
11
+
12
+ // Kinds that go only once the full clean-up is turned on.
13
+ export const ARMED_KINDS: ReadonlySet<SweepKind> = new Set([
14
+ "test-leak",
15
+ "database",
16
+ "spare",
17
+ "temp",
18
+ "test-temp",
19
+ "derived-data",
20
+ ]);
2
21
 
3
22
  export interface SweepRemoved {
4
23
  path: string;
@@ -15,10 +34,31 @@ export interface SweepSkipped {
15
34
  sessionId?: string;
16
35
  }
17
36
 
37
+ export interface CategoryTotal {
38
+ count: number;
39
+ bytes: number;
40
+ }
41
+
42
+ // What is on disk after the run, for the one-line summary.
43
+ export interface SweepTotals {
44
+ worktrees: CategoryTotal;
45
+ databases: CategoryTotal;
46
+ spares: CategoryTotal;
47
+ temp: CategoryTotal;
48
+ }
49
+
18
50
  export interface SweepSummary {
19
51
  removed: SweepRemoved[];
20
52
  skipped: SweepSkipped[];
21
53
  freedBytes: number;
54
+ // Found but left, because the full clean-up isn't turned on.
55
+ pending?: SweepRemoved[];
56
+ // The whole of pending, once a stored run keeps only its biggest.
57
+ pendingTotal?: CategoryTotal;
58
+ totals?: SweepTotals;
59
+ armed?: boolean;
60
+ // Where a dry run wrote its list.
61
+ report?: string;
22
62
  }
23
63
 
24
64
  export interface SweepRun {
@@ -50,4 +90,9 @@ export interface Candidate {
50
90
  // The PR head a merge took, when the session has one.
51
91
  prNumber?: number | null;
52
92
  branch?: string | null;
93
+ // A database: the server it lives on, and why it's reapable.
94
+ server?: { host?: string; port?: number };
95
+ why?: string;
96
+ // Known without measuring (a database's size).
97
+ bytes?: number;
53
98
  }
package/lib/worktrees.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  * Git Worktree management for isolated feature development
3
3
  */
4
4
 
5
+ import { stopDevServer } from "./dev-instance/stopDevServer";
5
6
  import * as path from "path";
6
7
  import * as fs from "fs";
7
8
  import * as os from "os";
@@ -301,6 +302,8 @@ export function deleteWorktree(
301
302
  let marker: string | null = null;
302
303
  const done = (async () => {
303
304
  await branch;
305
+ // A dev server left running would outlive the folder it runs in.
306
+ await stopDevServer(key).catch(() => {});
304
307
  // Before anything is removed, so no reader catches it half-deleted.
305
308
  marker = markRemoving(key);
306
309
  await removeWorktree(key, repo);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saadnvd1/agent-os",
3
- "version": "0.3.662",
3
+ "version": "0.3.671",
4
4
  "description": "Self-hosted web UI for managing Claude Code sessions",
5
5
  "bin": {
6
6
  "agent-os": "scripts/agent-os"
@@ -33,6 +33,7 @@
33
33
  "dev": "AGENTOS_DEV=1 tsx server.ts",
34
34
  "dev:task": "tsx scripts/dev-task.ts",
35
35
  "dev:stop": "tsx scripts/dev-stop.ts",
36
+ "disk:dry-run": "tsx scripts/disk-dry-run.ts",
36
37
  "prebuild": "node scripts/gen-migrations-index.mjs",
37
38
  "build": "next build",
38
39
  "prestart": "node scripts/ensure-native.mjs && node scripts/gen-migrations-index.mjs",
@@ -1,8 +1,8 @@
1
1
  import { execFileSync } from "child_process";
2
2
  import * as fs from "fs";
3
- import * as os from "os";
4
3
  import * as path from "path";
5
4
  import { describe, expect, it } from "vitest";
5
+ import { tempDir } from "@/lib/test-temp";
6
6
 
7
7
  // Runs the real scripts/autodeploy in a scratch clone with a stub redeploy
8
8
  // (a pull) and wait-quiet, against a scratch origin and HOME.
@@ -11,7 +11,7 @@ const sh = (cwd: string, ...args: string[]) =>
11
11
  execFileSync("git", args, { cwd, encoding: "utf-8" }).trim();
12
12
 
13
13
  function setup() {
14
- const root = fs.mkdtempSync(path.join(os.tmpdir(), "aos-autodeploy-"));
14
+ const root = tempDir("aos-autodeploy-");
15
15
  const origin = path.join(root, "origin.git");
16
16
  const work = path.join(root, "work");
17
17
  const clone = path.join(root, "live");
@@ -1,13 +1,13 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
  import { spawnSync } from "child_process";
3
3
  import fs from "fs";
4
- import os from "os";
5
4
  import path from "path";
5
+ import { tempDir } from "@/lib/test-temp";
6
6
 
7
7
  const script = path.join(__dirname, "check-bundle.mjs");
8
8
 
9
9
  function run(stats: unknown) {
10
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), "bundle-"));
10
+ const dir = tempDir("bundle-");
11
11
  const file = path.join(dir, "stats.json");
12
12
  fs.writeFileSync(file, JSON.stringify(stats));
13
13
  const r = spawnSync(process.execPath, [script, file], { encoding: "utf-8" });
@@ -0,0 +1,57 @@
1
+ /**
2
+ * What the disk clean-up would remove, without removing anything: runs a dry
3
+ * sweep against a copy of the live database and writes the list to
4
+ * ~/.agent-os/audits/disk-cleanup/<date>.md.
5
+ *
6
+ * npm run disk:dry-run -- --db <path to the live agent-os.db>
7
+ *
8
+ * The live database is opened read-only for the copy and never written; the
9
+ * copy is deleted afterwards. Nothing on disk or in Postgres is touched.
10
+ */
11
+
12
+ import fs from "fs";
13
+ import os from "os";
14
+ import path from "path";
15
+ import Database from "better-sqlite3";
16
+
17
+ const arg = process.argv.indexOf("--db");
18
+ const live: string = (arg > 0 && process.argv[arg + 1]) || "";
19
+ if (!live || !fs.existsSync(live)) {
20
+ console.error("usage: npm run disk:dry-run -- --db <path to agent-os.db>");
21
+ process.exit(1);
22
+ }
23
+
24
+ async function main() {
25
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "aos-disk-dry-"));
26
+ const copy = path.join(dir, "agent-os.db");
27
+ try {
28
+ const src = new Database(live, { readonly: true });
29
+ await src.backup(copy);
30
+ src.close();
31
+ process.env.DB_PATH = copy;
32
+
33
+ const { sweep, defaultSweepDeps } =
34
+ await import("../lib/worktree-sweep/runSweep");
35
+ const { summary } = await sweep(
36
+ { dryRun: true },
37
+ { ...defaultSweepDeps(), busy: () => false }
38
+ );
39
+ const all = [...summary.removed, ...(summary.pending ?? [])];
40
+ const gb = (b: number) => `${(b / 1024 ** 3).toFixed(2)} GB`;
41
+ for (const kind of new Set(all.map((i) => i.kind))) {
42
+ const items = all.filter((i) => i.kind === kind);
43
+ console.log(
44
+ `${kind}: ${items.length} (${gb(items.reduce((n, i) => n + i.bytes, 0))})`
45
+ );
46
+ }
47
+ console.log(`kept: ${summary.skipped.length}`);
48
+ console.log(`list: ${summary.report ?? "(not written)"}`);
49
+ } finally {
50
+ fs.rmSync(dir, { recursive: true, force: true });
51
+ }
52
+ }
53
+
54
+ main().catch((error) => {
55
+ console.error(error);
56
+ process.exit(1);
57
+ });
@@ -4,11 +4,12 @@ import fs from "fs";
4
4
  import os from "os";
5
5
  import path from "path";
6
6
  import { GENERIC_TERMS, loadTerms, matcher, scanTree } from "./banned-terms";
7
+ import { tempDir } from "@/lib/test-temp";
7
8
 
8
9
  const generic = GENERIC_TERMS[0];
9
10
 
10
11
  function tempRepo() {
11
- const repo = fs.mkdtempSync(path.join(os.tmpdir(), "banned-terms-"));
12
+ const repo = tempDir("banned-terms-");
12
13
  const git = (...args: string[]) =>
13
14
  execFileSync("git", args, { cwd: repo, stdio: "pipe" });
14
15
  git("init", "-q");
@@ -22,7 +23,7 @@ describe("loadTerms", () => {
22
23
  });
23
24
 
24
25
  it("adds the private regexes, skipping blanks and comments", () => {
25
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), "banned-names-"));
26
+ const dir = tempDir("banned-names-");
26
27
  const file = path.join(dir, "banned-names");
27
28
  fs.writeFileSync(file, "# names\nacme[ -]?widgets\n\n zorp \n");
28
29
  try {
@@ -1,9 +1,9 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
  import { execFileSync, spawnSync } from "child_process";
3
3
  import fs from "fs";
4
- import os from "os";
5
4
  import path from "path";
6
5
  import { scanDiff } from "./staged-secrets";
6
+ import { tempDir } from "@/lib/test-temp";
7
7
 
8
8
  // Fakes are built here, never written whole: a literal would be the very
9
9
  // thing this scan (and the merge gate) refuses.
@@ -62,7 +62,7 @@ describe("scanDiff", () => {
62
62
  });
63
63
 
64
64
  it("fails the commit on a staged secret, and passes once it's gone", () => {
65
- const repo = fs.mkdtempSync(path.join(os.tmpdir(), "staged-secrets-"));
65
+ const repo = tempDir("staged-secrets-");
66
66
  const git = (...args: string[]) =>
67
67
  execFileSync("git", args, { cwd: repo, stdio: "pipe" });
68
68
  const scan = () =>
@@ -2,15 +2,14 @@ import { spawnSync } from "node:child_process";
2
2
  import {
3
3
  existsSync,
4
4
  mkdirSync,
5
- mkdtempSync,
6
5
  readFileSync,
7
6
  rmSync,
8
7
  utimesSync,
9
8
  writeFileSync,
10
9
  } from "node:fs";
11
- import { tmpdir } from "node:os";
12
10
  import { join } from "node:path";
13
11
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
12
+ import { tempDir } from "@/lib/test-temp";
14
13
 
15
14
  const LIB = join(import.meta.dirname, "build-swap.sh");
16
15
 
@@ -41,7 +40,7 @@ const BUILD = `
41
40
  `;
42
41
 
43
42
  beforeEach(() => {
44
- dir = mkdtempSync(join(tmpdir(), "build-swap-"));
43
+ dir = tempDir("build-swap-");
45
44
  mkdirSync(join(dir, ".next/cache/turbopack"), { recursive: true });
46
45
  writeFileSync(join(dir, ".next/BUILD_ID"), "old\n");
47
46
  writeFileSync(join(dir, ".next/cache/turbopack/db"), "cache\n");
@@ -3,15 +3,14 @@ import {
3
3
  chmodSync,
4
4
  existsSync,
5
5
  mkdirSync,
6
- mkdtempSync,
7
6
  readFileSync,
8
7
  rmSync,
9
8
  symlinkSync,
10
9
  writeFileSync,
11
10
  } from "node:fs";
12
- import { tmpdir } from "node:os";
13
11
  import { join } from "node:path";
14
12
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
13
+ import { tempDir } from "@/lib/test-temp";
15
14
 
16
15
  // The installer's WSL paths, run on any OS: `uname`, `ps`, the browser
17
16
  // openers and the package managers are fakes, and the kernel release is a
@@ -60,7 +59,7 @@ function run(
60
59
  }
61
60
 
62
61
  beforeEach(() => {
63
- dir = mkdtempSync(join(tmpdir(), "aos-wsl-"));
62
+ dir = tempDir("aos-wsl-");
64
63
  mkdirSync(join(dir, "bin"));
65
64
  for (const tool of TOOLS) {
66
65
  const real = ["/usr/bin", "/bin"]
@@ -1,16 +1,16 @@
1
1
  import { execFile } from "child_process";
2
2
  import fs from "fs";
3
- import os from "os";
4
3
  import path from "path";
5
4
  import { promisify } from "util";
6
5
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
6
+ import { tempDir } from "@/lib/test-temp";
7
7
 
8
8
  const exec = promisify(execFile);
9
9
  const runner = path.join(__dirname, "update-run.mjs");
10
10
 
11
11
  let dir: string;
12
12
  beforeEach(() => {
13
- dir = fs.mkdtempSync(path.join(os.tmpdir(), "aos-runner-"));
13
+ dir = tempDir("aos-runner-");
14
14
  fs.writeFileSync(
15
15
  path.join(dir, "state.json"),
16
16
  JSON.stringify({ id: "r1", phase: "starting", from: "a" })