github-router 0.3.239 → 0.3.241

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.
@@ -2,63 +2,164 @@ import consola from "consola";
2
2
  import { randomBytes } from "node:crypto";
3
3
  import fs from "node:fs/promises";
4
4
  import os from "node:os";
5
- import nodePath from "node:path";
6
-
5
+ import path from "node:path";
7
6
  //#region src/lib/paths.ts
8
7
  function appDir() {
9
- return nodePath.join(os.homedir(), ".local", "share", "github-router");
8
+ return path.join(os.homedir(), ".local", "share", "github-router");
10
9
  }
11
10
  const PATHS = {
12
11
  get APP_DIR() {
13
12
  return appDir();
14
13
  },
15
14
  get GITHUB_TOKEN_PATH() {
16
- return nodePath.join(appDir(), "github_token");
15
+ return path.join(appDir(), "github_token");
17
16
  },
17
+ /**
18
+ * Second, WRITE-capable GitHub token for the first-mate
19
+ * agent-orchestration surface (`--agents`), stored apart from
20
+ * `github_token` (the Copilot App token). Minted by
21
+ * `setupGitHubAgentToken()` via a device-flow login against the GitHub
22
+ * CLI OAuth client. Mode 0o600.
23
+ */
18
24
  get GITHUB_AGENT_TOKEN_PATH() {
19
- return nodePath.join(appDir(), "github_agent_token");
25
+ return path.join(appDir(), "github_agent_token");
20
26
  },
27
+ /**
28
+ * Root of the durable first-mate fleet registry (`--agents`): the
29
+ * global `missions.json` mission index + per-repo `<owner>__<repo>.json`
30
+ * unit ledgers + the decisions/approvals ledger. Cross-launch and
31
+ * OUTSIDE the per-launch CLAUDE_CONFIG_DIR mirror (which is swept each
32
+ * launch) — the first mate must survive proxy restarts / context
33
+ * clears. Modeled on the worker-agent lifecycle ledger.
34
+ */
21
35
  get FIRST_MATE_DIR() {
22
- return process.env.GH_ROUTER_FIRST_MATE_DIR ?? nodePath.join(appDir(), "first-mate");
36
+ return process.env.GH_ROUTER_FIRST_MATE_DIR ?? path.join(appDir(), "first-mate");
23
37
  },
24
38
  get ERROR_LOG_PATH() {
25
- return nodePath.join(appDir(), "error.log");
39
+ return path.join(appDir(), "error.log");
26
40
  },
41
+ /**
42
+ * Durable, router-owned directory for worker OVERFLOW artifacts. Two
43
+ * kinds land here, both kept OUT of the MCP result relay so they don't
44
+ * overflow Claude Code's 25k-token cap:
45
+ * - `<pid>-<8hex>.patch` — a `worker_implement`/`worker_test` worktree
46
+ * diff larger than `PREVIEW_CAP` (worker-agent/worktree.ts): the FULL
47
+ * `git diff --binary --full-index` patch, written BEFORE the worktree
48
+ * is removed so the (possibly binary) change stays `git apply`-able.
49
+ * - `<pid>-<8hex>.txt` — any worker result over the relay byte cap
50
+ * (worker-agent/relay-cap.ts `relaySafeText`): the full result text,
51
+ * with a bounded preview + this path returned in its place.
52
+ * `sweepAgedWorkerDiffs()` reaps entries older than 7 days on each write.
53
+ * Cross-launch cache (like TOOLBELT_BIN_DIR) — deliberately OUTSIDE the
54
+ * per-launch CLAUDE_CONFIG_DIR mirror and OUTSIDE the user's repo.
55
+ */
27
56
  get WORKER_DIFFS_DIR() {
28
- return nodePath.join(appDir(), "worker-diffs");
57
+ return path.join(appDir(), "worker-diffs");
29
58
  },
59
+ /**
60
+ * Isolated CODEX_HOME for the spawned Codex CLI. Masks any cached
61
+ * ChatGPT subscription login (openai/codex#2733 — cached login can
62
+ * override OPENAI_API_KEY) so the proxy's dummy key is authoritative.
63
+ */
30
64
  get CODEX_HOME() {
31
- return nodePath.join(appDir(), "codex-isolated");
65
+ return path.join(appDir(), "codex-isolated");
32
66
  },
67
+ /**
68
+ * Router-owned install + data dir for CloudCLI (the `serve` browser control
69
+ * plane). Holds the pinned local `npm install` (node_modules/), an isolated
70
+ * `auth.db` (so we never collide with a user's standalone CloudCLI DB), and
71
+ * the persisted `.serve-secret.json` login. See docs/serve-control-plane.md.
72
+ */
33
73
  get CLOUDCLI_HOME() {
34
- return nodePath.join(appDir(), "cloudcli");
74
+ return path.join(appDir(), "cloudcli");
35
75
  },
76
+ /**
77
+ * Runtime tempfiles for the per-launch peer-MCP wiring (the
78
+ * `--mcp-config` JSON and `--agents` JSON written before spawning
79
+ * Claude Code). Mode 0o700 to match the security review's mandate;
80
+ * cleaned on shutdown via the per-launch `cleanup()`, plus a
81
+ * boot-time sweep of stale files (dead PIDs, >24h old).
82
+ */
36
83
  get CLAUDE_RUNTIME_DIR() {
37
- return nodePath.join(appDir(), "runtime");
84
+ return path.join(appDir(), "runtime");
38
85
  },
86
+ /**
87
+ * Router-owned CLAUDE_CONFIG_DIR. The spawned Claude Code (and any
88
+ * teammates it spawns via the agent-teams primitive) reads its
89
+ * config — including `.credentials.json` — from this dir. We
90
+ * snapshot-copy the user's `~/.claude/` here at startup (excluding
91
+ * `.credentials.json` and volatile state), then write our own
92
+ * synthetic Console OAuth credential. The teammate-spawn allowlist
93
+ * propagates `CLAUDE_CONFIG_DIR` to children, so teammates find the
94
+ * synthetic credential and authenticate instead of falling into the
95
+ * "Not logged in · Run /login" gate that would otherwise leave
96
+ * them mute. See `ensureClaudeConfigMirror` below.
97
+ *
98
+ * Per-launch dir: `<appDir>/claude-config/<pid>-<8 hex>`. Two
99
+ * concurrent `github-router claude` launches each get their own
100
+ * isolated mirror, so per-launch state (synthetic credential,
101
+ * snapshot copy of `~/.claude/`, future per-launch `.claude.json`
102
+ * mutation with the peer-MCP entry) cannot cross-talk. The
103
+ * per-launch suffix is cached on first access (see
104
+ * `claudeConfigDirSuffix()`) so all callers within a single proxy
105
+ * lifetime see the same value. Boot-time `sweepStaleClaudeConfigMirrors`
106
+ * reaps mirrors from crashed prior PIDs.
107
+ */
39
108
  get CLAUDE_CONFIG_DIR() {
40
- return nodePath.join(appDir(), "claude-config", claudeConfigDirSuffix());
109
+ return path.join(appDir(), "claude-config", claudeConfigDirSuffix());
41
110
  },
111
+ /**
112
+ * Durable router-owned `bin/` for the LLM toolbelt — curated CLI
113
+ * tools (rg/fd/jq/sd/sg/yq) materialized here and prepended to the
114
+ * spawned agent's PATH. Intentionally OUTSIDE the per-launch
115
+ * CLAUDE_CONFIG_DIR mirror (which is swept per launch): the toolbelt
116
+ * is a cross-launch cache, like `last-update-check`.
117
+ */
42
118
  get TOOLBELT_BIN_DIR() {
43
- return nodePath.join(appDir(), "bin");
119
+ return path.join(appDir(), "bin");
44
120
  },
121
+ /**
122
+ * Root of the github-router-managed ColBERT semantic-search sidecar
123
+ * (the `--semantic-search` opt-in). Everything heavy that the
124
+ * `colgrep` child needs is provisioned, SHA-verified, and stored here
125
+ * by the router — never in the user's repo, never in colgrep's own
126
+ * global `%APPDATA%\colgrep` / `~/.config/colgrep`. Cross-launch cache
127
+ * (like TOOLBELT_BIN_DIR), deliberately OUTSIDE the per-launch
128
+ * CLAUDE_CONFIG_DIR mirror.
129
+ *
130
+ * Layout (see docs/research/colbert-sidecar-design.md §4):
131
+ * colbert/
132
+ * bin/colgrep[.exe] # provisioned binary
133
+ * models/LateOn-Code-edge/<rev>/ # 5 model files
134
+ * onnxruntime/1.23.0/cpu/<libname> # ORT dylib (ORT_DYLIB_PATH)
135
+ * indices/ # === COLGREP_DATA_DIR ===
136
+ * <project>-<xxh3(path|model)>/ # colgrep-owned per-(path,model)
137
+ * .gh-router-meta/<hash>.json # router-owned sidecar metadata
138
+ */
45
139
  get COLBERT_DIR() {
46
- return nodePath.join(appDir(), "colbert");
140
+ return path.join(appDir(), "colbert");
47
141
  },
48
142
  get COLBERT_BIN_DIR() {
49
- return nodePath.join(appDir(), "colbert", "bin");
143
+ return path.join(appDir(), "colbert", "bin");
50
144
  },
51
145
  get COLBERT_MODELS_DIR() {
52
- return nodePath.join(appDir(), "colbert", "models");
146
+ return path.join(appDir(), "colbert", "models");
53
147
  },
54
148
  get COLBERT_ORT_DIR() {
55
- return nodePath.join(appDir(), "colbert", "onnxruntime");
149
+ return path.join(appDir(), "colbert", "onnxruntime");
56
150
  },
151
+ /**
152
+ * Value passed to colgrep as `COLGREP_DATA_DIR`. colgrep keys the
153
+ * physical index dir by `xxh3(canonical_project_path | model)` under
154
+ * here, so every distinct workspace gets its own index dir for free.
155
+ * The router-owned freshness sidecar lives at
156
+ * `<COLBERT_INDICES_DIR>/.gh-router-meta/<hash>.json`.
157
+ */
57
158
  get COLBERT_INDICES_DIR() {
58
- return nodePath.join(appDir(), "colbert", "indices");
159
+ return path.join(appDir(), "colbert", "indices");
59
160
  },
60
161
  get COLBERT_META_DIR() {
61
- return nodePath.join(appDir(), "colbert", "indices", ".gh-router-meta");
162
+ return path.join(appDir(), "colbert", "indices", ".gh-router-meta");
62
163
  }
63
164
  };
64
165
  /**
@@ -97,10 +198,10 @@ function claudeConfigDirSuffix() {
97
198
  * refusal lives at the consuming helper.
98
199
  */
99
200
  function isUnderClaudeConfigMirror(target) {
100
- const resolvedRoot = nodePath.resolve(PATHS.CLAUDE_CONFIG_DIR);
101
- const resolvedTarget = nodePath.resolve(target);
201
+ const resolvedRoot = path.resolve(PATHS.CLAUDE_CONFIG_DIR);
202
+ const resolvedTarget = path.resolve(target);
102
203
  if (resolvedTarget === resolvedRoot) return true;
103
- return resolvedTarget.startsWith(resolvedRoot + nodePath.sep);
204
+ return resolvedTarget.startsWith(resolvedRoot + path.sep);
104
205
  }
105
206
  async function ensurePaths() {
106
207
  await fs.mkdir(PATHS.APP_DIR, { recursive: true });
@@ -120,17 +221,17 @@ async function ensurePaths() {
120
221
  consola.debug("Peer-agent .md sweep skipped:", err);
121
222
  });
122
223
  await (async () => {
123
- await (await import("./lifecycle-DF2ygqz8.js")).sweepStaleWorktreesAtBoot();
224
+ await (await import("./lifecycle-hzPwXc3I.js")).sweepStaleWorktreesAtBoot();
124
225
  })().catch((err) => {
125
226
  consola.debug("Worker worktree boot sweep skipped:", err);
126
227
  });
127
228
  await (async () => {
128
- await (await import("./lifecycle-wIIt0wGo.js")).sweepStaleColbertMetaAtBoot();
229
+ await (await import("./lifecycle-0rXrwjWk.js")).sweepStaleColbertMetaAtBoot();
129
230
  })().catch((err) => {
130
231
  consola.debug("ColBERT meta boot sweep skipped:", err);
131
232
  });
132
233
  }
133
- const CLAUDE_HOME_POLICY = new Map([
234
+ const CLAUDE_HOME_POLICY = /* @__PURE__ */ new Map([
134
235
  [".credentials.json", "ISOLATED"],
135
236
  [".credentials.json.lock", "ISOLATED"],
136
237
  [".oauth_refresh.lock", "ISOLATED"],
@@ -300,7 +401,7 @@ const SYNTHETIC_CLAUDE_JSON_FIELDS = {
300
401
  */
301
402
  async function ensureClaudeConfigMirror(opts = {}) {
302
403
  const realHome = opts.realHome ?? os.homedir();
303
- const sourceDir = nodePath.join(realHome, ".claude");
404
+ const sourceDir = path.join(realHome, ".claude");
304
405
  const targetDir = PATHS.CLAUDE_CONFIG_DIR;
305
406
  await fs.mkdir(targetDir, {
306
407
  recursive: true,
@@ -314,11 +415,11 @@ async function ensureClaudeConfigMirror(opts = {}) {
314
415
  if (err.code !== "ENOENT") consola.debug(`ensureClaudeConfigMirror: cannot stat ${sourceDir}:`, err);
315
416
  }
316
417
  if (sourceExists) await mirrorDirRecursive(sourceDir, targetDir, "");
317
- await fs.mkdir(nodePath.join(targetDir, "agents"), { recursive: true });
418
+ await fs.mkdir(path.join(targetDir, "agents"), { recursive: true });
318
419
  for (const name of SHARED_TOPLEVEL_NAMES) await ensureSharedSymlink(name, sourceDir, targetDir).catch((err) => {
319
420
  consola.debug(`ensureClaudeConfigMirror: SHARED symlink for ${name} skipped:`, err);
320
421
  });
321
- const credentialsPath = nodePath.join(targetDir, ".credentials.json");
422
+ const credentialsPath = path.join(targetDir, ".credentials.json");
322
423
  const desiredJson = JSON.stringify(SYNTHETIC_CREDENTIAL, null, 2);
323
424
  let needsWrite = true;
324
425
  try {
@@ -345,7 +446,7 @@ async function ensureClaudeConfigMirror(opts = {}) {
345
446
  await chmodIfPossible(credentialsPath, 384);
346
447
  await injectSyntheticClaudeJsonFields(targetDir, sourceDir, realHome);
347
448
  await assertOnboardingGateInjected(targetDir);
348
- const markerPath = nodePath.join(targetDir, MANAGED_MARKER_FILENAME);
449
+ const markerPath = path.join(targetDir, MANAGED_MARKER_FILENAME);
349
450
  let markerExists = false;
350
451
  try {
351
452
  const markerStat = await fs.lstat(markerPath);
@@ -437,7 +538,7 @@ async function ensureClaudeConfigMirror(opts = {}) {
437
538
  * degradation path is converted to a fail-loud launch error.
438
539
  */
439
540
  async function injectSyntheticClaudeJsonFields(targetDir, sourceDir, realHome) {
440
- const claudeJsonPath = nodePath.join(targetDir, ".claude.json");
541
+ const claudeJsonPath = path.join(targetDir, ".claude.json");
441
542
  let mirrorIsRegularFile = false;
442
543
  try {
443
544
  const mirrorStat = await fs.lstat(claudeJsonPath);
@@ -454,8 +555,8 @@ async function injectSyntheticClaudeJsonFields(targetDir, sourceDir, realHome) {
454
555
  }
455
556
  let existing = {};
456
557
  let selectedSource = null;
457
- const homeLevelClaudeJson = nodePath.join(realHome, ".claude.json");
458
- const subdirClaudeJson = nodePath.join(sourceDir, ".claude.json");
558
+ const homeLevelClaudeJson = path.join(realHome, ".claude.json");
559
+ const subdirClaudeJson = path.join(sourceDir, ".claude.json");
459
560
  const sourceCandidates = [
460
561
  homeLevelClaudeJson,
461
562
  mirrorIsRegularFile ? claudeJsonPath : null,
@@ -464,9 +565,9 @@ async function injectSyntheticClaudeJsonFields(targetDir, sourceDir, realHome) {
464
565
  for (const sourceCandidate of sourceCandidates) {
465
566
  if (sourceCandidate === null) continue;
466
567
  try {
467
- const stat$1 = await fs.stat(sourceCandidate);
468
- if (stat$1.size > MAX_CLAUDE_JSON_BYTES) {
469
- consola.warn(`ensureClaudeConfigMirror: ${sourceCandidate} is ${stat$1.size} bytes (> ${MAX_CLAUDE_JSON_BYTES} cap); skipping this source to avoid OOM. Inspect for accidental log/state accumulation.`);
568
+ const stat = await fs.stat(sourceCandidate);
569
+ if (stat.size > MAX_CLAUDE_JSON_BYTES) {
570
+ consola.warn(`ensureClaudeConfigMirror: ${sourceCandidate} is ${stat.size} bytes (> ${MAX_CLAUDE_JSON_BYTES} cap); skipping this source to avoid OOM. Inspect for accidental log/state accumulation.`);
470
571
  continue;
471
572
  }
472
573
  const parsed = parseExistingClaudeJson(await fs.readFile(sourceCandidate, "utf8"), sourceCandidate);
@@ -521,7 +622,7 @@ async function injectSyntheticClaudeJsonFields(targetDir, sourceDir, realHome) {
521
622
  * blocks the event loop and risks OOM. 50 MiB is comfortably above
522
623
  * any legitimate Claude Code state and well below typical RSS budgets.
523
624
  */
524
- const MAX_CLAUDE_JSON_BYTES = 50 * 1024 * 1024;
625
+ const MAX_CLAUDE_JSON_BYTES = 52428800;
525
626
  /**
526
627
  * Structural check for `oauthAccount`: must be a non-null, non-array
527
628
  * object whose `accountUuid` AND `organizationUuid` are non-empty
@@ -579,19 +680,19 @@ function parseExistingClaudeJson(raw, contextPath) {
579
680
  * upward by hand.
580
681
  */
581
682
  async function assertOnboardingGateInjected(targetDir) {
582
- const claudeJsonPath = nodePath.join(targetDir, ".claude.json");
683
+ const claudeJsonPath = path.join(targetDir, ".claude.json");
583
684
  let lst;
584
685
  try {
585
686
  lst = await fs.lstat(claudeJsonPath);
586
687
  } catch (err) {
587
- throw new Error(`ensureClaudeConfigMirror: postcondition failed — cannot lstat ${claudeJsonPath} after injection (synthetic onboarding-skip fields are required to prevent Claude Code's first-launch wizard, which would defeat the synthetic credential): ${err.message}`);
688
+ throw new Error(`ensureClaudeConfigMirror: postcondition failed — cannot lstat ${claudeJsonPath} after injection (synthetic onboarding-skip fields are required to prevent Claude Code's first-launch wizard, which would defeat the synthetic credential): ${err.message}`, { cause: err });
588
689
  }
589
690
  if (!lst.isFile()) throw new Error(`ensureClaudeConfigMirror: postcondition failed — ${claudeJsonPath} exists but is not a regular file (mode=${lst.mode.toString(8)}). Check the preceding warn-level log lines for the underlying cause; without a regular file holding the gate fields, Claude Code's first-launch wizard fires and defeats the synthetic credential.`);
590
691
  let raw;
591
692
  try {
592
693
  raw = await fs.readFile(claudeJsonPath, "utf8");
593
694
  } catch (err) {
594
- throw new Error(`ensureClaudeConfigMirror: postcondition failed — cannot read ${claudeJsonPath} after injection: ${err.message}`);
695
+ throw new Error(`ensureClaudeConfigMirror: postcondition failed — cannot read ${claudeJsonPath} after injection: ${err.message}`, { cause: err });
595
696
  }
596
697
  let parsed;
597
698
  try {
@@ -599,7 +700,7 @@ async function assertOnboardingGateInjected(targetDir) {
599
700
  if (!json || typeof json !== "object" || Array.isArray(json)) throw new Error(`parsed to non-object (typeof=${typeof json})`);
600
701
  parsed = json;
601
702
  } catch (err) {
602
- throw new Error(`ensureClaudeConfigMirror: postcondition failed — ${claudeJsonPath} is not a valid JSON object after injection: ${err.message}`);
703
+ throw new Error(`ensureClaudeConfigMirror: postcondition failed — ${claudeJsonPath} is not a valid JSON object after injection: ${err.message}`, { cause: err });
603
704
  }
604
705
  if (parsed.hasCompletedOnboarding !== true || parsed.bypassPermissionsModeAccepted !== true) throw new Error(`ensureClaudeConfigMirror: postcondition failed — ${claudeJsonPath} is missing required gate fields after injection (hasCompletedOnboarding=${String(parsed.hasCompletedOnboarding)}, bypassPermissionsModeAccepted=${String(parsed.bypassPermissionsModeAccepted)}). Check the preceding warn-level log lines for the underlying cause; without these fields Claude Code's first-launch wizard fires and defeats the synthetic credential.`);
605
706
  if (!isUsableOauthAccount(parsed.oauthAccount)) throw new Error(`ensureClaudeConfigMirror: postcondition failed — ${claudeJsonPath} has invalid oauthAccount after injection (got ${JSON.stringify(parsed.oauthAccount)}). The synthetic identity blob is required so Claude Code's identity-vs-token cross-checks don't trigger a re-login that defeats the synthetic credential.`);
@@ -617,7 +718,7 @@ async function assertOnboardingGateInjected(targetDir) {
617
718
  * - Files copy only if source mtime > target mtime (idempotent).
618
719
  */
619
720
  async function mirrorDirRecursive(sourceDir, targetDir, relPath) {
620
- const sourcePath = nodePath.join(sourceDir, relPath);
721
+ const sourcePath = path.join(sourceDir, relPath);
621
722
  let entries;
622
723
  try {
623
724
  entries = await fs.readdir(sourcePath);
@@ -631,10 +732,10 @@ async function mirrorDirRecursive(sourceDir, targetDir, relPath) {
631
732
  const policy = policyFor(name);
632
733
  if (policy === "ISOLATED" || policy === "SHARED") continue;
633
734
  }
634
- const childRel = relPath === "" ? name : nodePath.join(relPath, name);
735
+ const childRel = relPath === "" ? name : path.join(relPath, name);
635
736
  if (relPath === "agents" && PEER_AGENT_MD_FILENAME.test(name)) continue;
636
- const childSource = nodePath.join(sourceDir, childRel);
637
- const childTarget = nodePath.join(targetDir, childRel);
737
+ const childSource = path.join(sourceDir, childRel);
738
+ const childTarget = path.join(targetDir, childRel);
638
739
  let stats;
639
740
  try {
640
741
  stats = await fs.lstat(childSource);
@@ -698,8 +799,8 @@ async function mirrorDirRecursive(sourceDir, targetDir, relPath) {
698
799
  * Claude Code's writes through the symlink don't fail with ENOENT.
699
800
  */
700
801
  async function ensureSharedSymlink(name, sourceDir, mirrorDir) {
701
- const sourcePath = nodePath.join(sourceDir, name);
702
- const mirrorPath = nodePath.join(mirrorDir, name);
802
+ const sourcePath = path.join(sourceDir, name);
803
+ const mirrorPath = path.join(mirrorDir, name);
703
804
  try {
704
805
  await fs.mkdir(sourcePath, { recursive: true });
705
806
  } catch (err) {
@@ -797,7 +898,7 @@ async function writeArtifactCredsToMirror(insecureTLS, env = process.env) {
797
898
  const token = env.AIORDIE_TOKEN;
798
899
  const sessionId = env.AIORDIE_SESSION_ID;
799
900
  if (!baseUrl || !token || !sessionId) return;
800
- const target = nodePath.join(PATHS.CLAUDE_CONFIG_DIR, ".aiordie-artifact.json");
901
+ const target = path.join(PATHS.CLAUDE_CONFIG_DIR, ".aiordie-artifact.json");
801
902
  const tempPath = `${target}.${process.pid}.tmp`;
802
903
  const body = JSON.stringify({
803
904
  baseUrl,
@@ -848,7 +949,7 @@ async function sweepStaleRuntimeFiles() {
848
949
  const match = /^peer-(?:mcp|agents)-(\d+)(?:-[0-9a-f]+)?\.json$/.exec(name);
849
950
  if (!match) continue;
850
951
  const pid = Number.parseInt(match[1], 10);
851
- const filePath = nodePath.join(dir, name);
952
+ const filePath = path.join(dir, name);
852
953
  if (isPidAlive(pid)) continue;
853
954
  await fs.unlink(filePath).catch(() => {});
854
955
  }
@@ -899,7 +1000,7 @@ function isPidAlive(pid) {
899
1000
  * `mirrorDirRecursive`, which skips them so they never register.
900
1001
  */
901
1002
  async function sweepStalePeerAgentMdFiles() {
902
- const dir = nodePath.join(PATHS.CLAUDE_CONFIG_DIR, "agents");
1003
+ const dir = path.join(PATHS.CLAUDE_CONFIG_DIR, "agents");
903
1004
  let entries;
904
1005
  try {
905
1006
  entries = await fs.readdir(dir);
@@ -911,7 +1012,7 @@ async function sweepStalePeerAgentMdFiles() {
911
1012
  const match = PEER_AGENT_MD_FILENAME.exec(name);
912
1013
  if (!match) continue;
913
1014
  if (isPidAlive(Number.parseInt(match[1], 10))) continue;
914
- await fs.unlink(nodePath.join(dir, name)).catch(() => {});
1015
+ await fs.unlink(path.join(dir, name)).catch(() => {});
915
1016
  }
916
1017
  }
917
1018
  /**
@@ -965,7 +1066,7 @@ const CLAUDE_CONFIG_MIRROR_DIR = /^(\d+)-[0-9a-f]{8}$/;
965
1066
  * Tolerates missing parent dir (first-ever launch, or user wiped it).
966
1067
  */
967
1068
  async function sweepStaleClaudeConfigMirrors() {
968
- const parent = nodePath.join(appDir(), "claude-config");
1069
+ const parent = path.join(appDir(), "claude-config");
969
1070
  let entries;
970
1071
  try {
971
1072
  entries = await fs.readdir(parent);
@@ -977,7 +1078,7 @@ async function sweepStaleClaudeConfigMirrors() {
977
1078
  const match = CLAUDE_CONFIG_MIRROR_DIR.exec(name);
978
1079
  if (!match) continue;
979
1080
  if (isPidAlive(Number.parseInt(match[1], 10))) continue;
980
- await fs.rm(nodePath.join(parent, name), {
1081
+ await fs.rm(path.join(parent, name), {
981
1082
  recursive: true,
982
1083
  force: true
983
1084
  }).catch((err) => {
@@ -1006,7 +1107,7 @@ async function removeOwnClaudeConfigMirror() {
1006
1107
  consola.debug(`removeOwnClaudeConfigMirror: rm ${dir} skipped:`, err);
1007
1108
  });
1008
1109
  }
1009
-
1010
1110
  //#endregion
1011
1111
  export { isUnderClaudeConfigMirror as a, sweepStalePeerAgentMdFiles as c, writeRuntimeFileSecure as d, ensurePaths as i, sweepStaleRuntimeFiles as l, PEER_AGENT_MD_FILENAME as n, removeOwnClaudeConfigMirror as o, ensureClaudeConfigMirror as r, sweepStaleClaudeConfigMirrors as s, PATHS as t, writeArtifactCredsToMirror as u };
1012
- //# sourceMappingURL=paths-Srxs-aR-.js.map
1112
+
1113
+ //# sourceMappingURL=paths-BjTMI_xK.js.map