@node9/proxy 2.49.0 → 2.50.0

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.
package/dist/cli-main.js CHANGED
@@ -520,6 +520,9 @@ function levelChecks(mode) {
520
520
  function levelPackageSignals(mode) {
521
521
  return levelOf(mode) === "soft" ? { newPackage: "off", installScript: "off" } : void 0;
522
522
  }
523
+ function isAsiId(v) {
524
+ return typeof v === "string" && Object.prototype.hasOwnProperty.call(ASI, v);
525
+ }
523
526
  function packRowOwasp(id, shield) {
524
527
  return PACK_RULE_OWASP[id] ?? PACK_SHIELD_OWASP[shield];
525
528
  }
@@ -542,12 +545,13 @@ function packRows() {
542
545
  if (!rule.name || SHIELD_RULES_FOLDED_INTO_PRODUCT[rule.name]) continue;
543
546
  const id = packRowId(shield.name, rule.name);
544
547
  if (!id) continue;
545
- const owasp = packRowOwasp(id, shield.name);
546
- if (!owasp) throw new Error(`pack row ${id} has no OWASP risk: add the shield to owasp.ts`);
547
548
  rows.push({
548
549
  id,
549
550
  group: "commands",
550
- owasp,
551
+ // A shield missing from the table falls back to tool misuse instead of
552
+ // throwing here: this module loads on every hook call, and a mapping
553
+ // omission must fail a test (owasp.spec.ts), never the gate.
554
+ owasp: packRowOwasp(id, shield.name) ?? "ASI02",
551
555
  title: rule.name.replace(SHIELD_RULE_NAME, "$3").replace(/-/g, " "),
552
556
  catches: rule.description ?? rule.reason ?? "",
553
557
  values: ALL,
@@ -19924,8 +19928,8 @@ var init_audit = __esm({
19924
19928
  // src/config-schema.ts
19925
19929
  function formatIssues(issues) {
19926
19930
  const lines = issues.map((issue) => {
19927
- const path113 = issue.path.length > 0 ? issue.path.map(String).join(".") : "root";
19928
- return ` \u2022 ${path113}: ${issue.message}`;
19931
+ const path114 = issue.path.length > 0 ? issue.path.map(String).join(".") : "root";
19932
+ return ` \u2022 ${path114}: ${issue.message}`;
19929
19933
  });
19930
19934
  return `Invalid config:
19931
19935
  ${lines.join("\n")}`;
@@ -19938,9 +19942,9 @@ function prunePaths(root, paths) {
19938
19942
  const yi = y[y.length - 1];
19939
19943
  return typeof xi === "number" && typeof yi === "number" ? yi - xi : 0;
19940
19944
  });
19941
- for (const path113 of ordered) {
19945
+ for (const path114 of ordered) {
19942
19946
  let cur = root;
19943
- for (const key of path113.slice(0, -1)) {
19947
+ for (const key of path114.slice(0, -1)) {
19944
19948
  if (cur === null || typeof cur !== "object") {
19945
19949
  cur = void 0;
19946
19950
  break;
@@ -19948,7 +19952,7 @@ function prunePaths(root, paths) {
19948
19952
  cur = cur[key];
19949
19953
  }
19950
19954
  if (cur === null || typeof cur !== "object") continue;
19951
- const last = path113[path113.length - 1];
19955
+ const last = path114[path114.length - 1];
19952
19956
  if (Array.isArray(cur)) {
19953
19957
  const i = Number(last);
19954
19958
  if (Number.isInteger(i) && i >= 0 && i < cur.length) {
@@ -19984,7 +19988,7 @@ function sanitizeConfig(raw) {
19984
19988
  return issue.keys.map((k) => [...at, k]);
19985
19989
  }
19986
19990
  return [at.slice(0, -level || void 0)];
19987
- }).filter((path113) => path113.length > 0);
19991
+ }).filter((path114) => path114.length > 0);
19988
19992
  if (paths.length === 0 || !prunePaths(working, paths)) break;
19989
19993
  }
19990
19994
  if (ConfigFileSchema.safeParse(working).success) break;
@@ -21578,13 +21582,13 @@ function getConfig(cwd) {
21578
21582
  }
21579
21583
  if (Array.isArray(mc.jailPaths)) {
21580
21584
  for (const jp of mc.jailPaths) {
21581
- const path113 = typeof jp?.path === "string" ? jp.path.trim() : "";
21582
- if (!path113) continue;
21585
+ const path114 = typeof jp?.path === "string" ? jp.path.trim() : "";
21586
+ if (!path114) continue;
21583
21587
  const verdict = jp?.verdict === "review" ? "review" : "block";
21584
- for (const r of pathRules(path113, verdict, "org-managed jail")) {
21588
+ for (const r of pathRules(path114, verdict, "org-managed jail")) {
21585
21589
  mergedPolicy.smartRules.push({ ...r, name: `org:${r.name}` });
21586
21590
  }
21587
- mergedPolicy.managedJailPaths.push({ path: path113, verdict });
21591
+ mergedPolicy.managedJailPaths.push({ path: path114, verdict });
21588
21592
  }
21589
21593
  }
21590
21594
  if (Array.isArray(mc.trustedHosts)) {
@@ -28457,7 +28461,9 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
28457
28461
  systemAccessHandled: !isManual && fileAccess.handledPaths.has(filePath)
28458
28462
  }) : null) ?? argumentScan(),
28459
28463
  pii: () => detectArgsPii(args),
28460
- packageCheck: () => packageCheck?.enabled && !isManual ? runPackageCheck(toolName, args, packageCheck, options?.cwd, { offline: !!forecast }) : Promise.resolve(null)
28464
+ packageCheck: () => packageCheck?.enabled && !isManual ? runPackageCheck(toolName, args, packageCheck, options?.cwd, {
28465
+ offline: !!forecast && forecast.onlinePackages !== true
28466
+ }) : Promise.resolve(null)
28461
28467
  });
28462
28468
  const dlpMatch = gates.stop?.dlpMatch ?? gates.reviews.dlp ?? null;
28463
28469
  if (dlpMatch) {
@@ -34065,7 +34071,8 @@ async function forecastDecision(toolName, args, meta, opts = {}) {
34065
34071
  session,
34066
34072
  config,
34067
34073
  unknown: /* @__PURE__ */ new Set(),
34068
- now: opts.at ?? Date.now()
34074
+ now: opts.at ?? Date.now(),
34075
+ ...opts.onlinePackages && { onlinePackages: true }
34069
34076
  };
34070
34077
  const result = await runForecast(
34071
34078
  run,
@@ -34160,6 +34167,20 @@ var init_forecast = __esm({
34160
34167
  }
34161
34168
  });
34162
34169
 
34170
+ // src/exposure/shell-fetch.ts
34171
+ function shellFetch(tool, rawInput) {
34172
+ if (tool !== "Bash" && tool !== "bash") return null;
34173
+ const cmd = rawInput && typeof rawInput === "object" && !Array.isArray(rawInput) ? rawInput.command : void 0;
34174
+ return typeof cmd === "string" && cmd.length > 0 ? classifyShellExposure(cmd) : null;
34175
+ }
34176
+ var init_shell_fetch = __esm({
34177
+ "src/exposure/shell-fetch.ts"() {
34178
+ "use strict";
34179
+ init_cjs_shims();
34180
+ init_dist();
34181
+ }
34182
+ });
34183
+
34163
34184
  // src/version.ts
34164
34185
  function readVersion() {
34165
34186
  let dir2 = __dirname;
@@ -34460,6 +34481,8 @@ var init_scan_judge = __esm({
34460
34481
  init_forecast();
34461
34482
  init_forecast_state();
34462
34483
  init_roles();
34484
+ init_shell_fetch();
34485
+ init_dist();
34463
34486
  init_scan_cache();
34464
34487
  init_dlp();
34465
34488
  import_fs45 = __toESM(require("fs"));
@@ -34534,16 +34557,26 @@ var init_scan_judge = __esm({
34534
34557
  this.session(sessionId).noteOutputSecret(patternName, clockOf(timestamp2));
34535
34558
  }
34536
34559
  /** A tool that brings outside content returned something. */
34537
- noteToolResult(sessionId, toolName, timestamp2, cwd) {
34560
+ /**
34561
+ * A tool result reached the session. A source read marks it exposed, as the
34562
+ * PostToolUse hook does (log.ts): a source tool, or a shell command that
34563
+ * fetches outside content (`gh issue view`). `read.output` is the result's
34564
+ * text: the destinations it names are recorded as the hook records them.
34565
+ */
34566
+ noteToolResult(sessionId, toolName, timestamp2, cwd, read3 = {}) {
34538
34567
  const policy = this.configFor(cwd).policy.untrustedContent;
34539
34568
  if (!policy?.enabled) return;
34540
- if (!toolRole(toolName, policy).source) return;
34569
+ const shell = shellFetch(toolName, read3.input);
34570
+ if (!toolRole(toolName, policy).source && !shell?.fetches) return;
34541
34571
  const mcp = /^mcp__([^_]+(?:_[^_]+)*)__/.exec(toolName);
34542
- this.session(sessionId).noteOutsideContent({
34543
- tool: toolName,
34544
- ...mcp && { server: mcp[1] },
34545
- at: clockOf(timestamp2)
34546
- });
34572
+ this.session(sessionId).noteOutsideContent(
34573
+ {
34574
+ tool: toolName,
34575
+ ...mcp && { server: mcp[1] },
34576
+ at: clockOf(timestamp2)
34577
+ },
34578
+ read3.output ? extractContentDestinations(read3.output) : []
34579
+ );
34547
34580
  }
34548
34581
  async judge(call, result, dedup) {
34549
34582
  const config = this.configFor(call.cwd);
@@ -37034,8 +37067,8 @@ function isOfficialInstaller(u) {
37034
37067
  if (u.username || u.password) return false;
37035
37068
  const host2 = u.hostname.toLowerCase().replace(/^www\./, "");
37036
37069
  if (OFFICIAL_HOSTS.has(host2)) return true;
37037
- const path113 = OFFICIAL_PATHS[host2];
37038
- return !!path113 && path113.test(u.pathname);
37070
+ const path114 = OFFICIAL_PATHS[host2];
37071
+ return !!path114 && path114.test(u.pathname);
37039
37072
  }
37040
37073
  function onlyOfficialInstaller(s) {
37041
37074
  const urls = s.match(URL_RE3);
@@ -37181,21 +37214,21 @@ function appSkillDirsOf(paths, skillDirs) {
37181
37214
  }
37182
37215
  return apps;
37183
37216
  }
37184
- function isSkillSupportFile(path113, skillDirs, appDirs) {
37185
- if (!path113.endsWith(".md") || /(^|\/)[Ss][Kk][Ii][Ll][Ll]\.md$/.test(path113)) return false;
37186
- for (let d = path113.lastIndexOf("/"); d > 0; d = path113.lastIndexOf("/", d - 1)) {
37187
- const dir2 = path113.slice(0, d);
37217
+ function isSkillSupportFile(path114, skillDirs, appDirs) {
37218
+ if (!path114.endsWith(".md") || /(^|\/)[Ss][Kk][Ii][Ll][Ll]\.md$/.test(path114)) return false;
37219
+ for (let d = path114.lastIndexOf("/"); d > 0; d = path114.lastIndexOf("/", d - 1)) {
37220
+ const dir2 = path114.slice(0, d);
37188
37221
  if (!skillDirs.has(dir2)) continue;
37189
- return !appDirs?.has(dir2) || SKILL_LAYOUT_RE.test(path113.slice(d + 1));
37222
+ return !appDirs?.has(dir2) || SKILL_LAYOUT_RE.test(path114.slice(d + 1));
37190
37223
  }
37191
37224
  return false;
37192
37225
  }
37193
- function isAppDoc(path113, skillDirs, appDirs) {
37194
- if (!path113.endsWith(".md") || INSTRUCTION_FILE_RE.test(path113)) return false;
37195
- for (let d = path113.lastIndexOf("/"); d > 0; d = path113.lastIndexOf("/", d - 1)) {
37196
- const dir2 = path113.slice(0, d);
37226
+ function isAppDoc(path114, skillDirs, appDirs) {
37227
+ if (!path114.endsWith(".md") || INSTRUCTION_FILE_RE.test(path114)) return false;
37228
+ for (let d = path114.lastIndexOf("/"); d > 0; d = path114.lastIndexOf("/", d - 1)) {
37229
+ const dir2 = path114.slice(0, d);
37197
37230
  if (!skillDirs.has(dir2)) continue;
37198
- return appDirs.has(dir2) && !SKILL_LAYOUT_RE.test(path113.slice(d + 1));
37231
+ return appDirs.has(dir2) && !SKILL_LAYOUT_RE.test(path114.slice(d + 1));
37199
37232
  }
37200
37233
  return false;
37201
37234
  }
@@ -37217,20 +37250,20 @@ function namedDocs(dir2, skillMd) {
37217
37250
  }
37218
37251
  return out;
37219
37252
  }
37220
- function isHookScript(path113) {
37221
- return /(^|\/)\.claude\/hooks\/.+/.test(path113) && SCRIPT_EXT_RE.test(path113);
37253
+ function isHookScript(path114) {
37254
+ return /(^|\/)\.claude\/hooks\/.+/.test(path114) && SCRIPT_EXT_RE.test(path114);
37222
37255
  }
37223
- function isSkillScript(path113, skillDirs) {
37224
- if (!SCRIPT_EXT_RE.test(path113)) return false;
37225
- for (let d = path113.lastIndexOf("/"); d > 0; d = path113.lastIndexOf("/", d - 1)) {
37226
- if (!skillDirs.has(path113.slice(0, d))) continue;
37227
- const rel = path113.slice(d + 1);
37256
+ function isSkillScript(path114, skillDirs) {
37257
+ if (!SCRIPT_EXT_RE.test(path114)) return false;
37258
+ for (let d = path114.lastIndexOf("/"); d > 0; d = path114.lastIndexOf("/", d - 1)) {
37259
+ if (!skillDirs.has(path114.slice(0, d))) continue;
37260
+ const rel = path114.slice(d + 1);
37228
37261
  return !rel.includes("/") || /^(scripts|bin)\//.test(rel);
37229
37262
  }
37230
37263
  return false;
37231
37264
  }
37232
- function isInstructionFile(path113, skillDirs, appDirs) {
37233
- return INSTRUCTION_FILE_RE.test(path113) || isSkillSupportFile(path113, skillDirs, appDirs);
37265
+ function isInstructionFile(path114, skillDirs, appDirs) {
37266
+ return INSTRUCTION_FILE_RE.test(path114) || isSkillSupportFile(path114, skillDirs, appDirs);
37234
37267
  }
37235
37268
  function findFetchObeyUnless(text, skip) {
37236
37269
  for (let start = 0; start <= text.length; ) {
@@ -37354,20 +37387,20 @@ function decodeSuspiciousBase64(text) {
37354
37387
  }
37355
37388
  return out;
37356
37389
  }
37357
- function mk2(rule, severity, title, signals, fix, path113, line) {
37390
+ function mk2(rule, severity, title, signals, fix, path114, line) {
37358
37391
  return {
37359
37392
  check: "CI-6",
37360
37393
  rule,
37361
37394
  dimension: "instructions",
37362
37395
  severity,
37363
37396
  title,
37364
- file: path113,
37397
+ file: path114,
37365
37398
  ...line ? { line } : {},
37366
37399
  signals,
37367
37400
  fix
37368
37401
  };
37369
37402
  }
37370
- function analyzeInstructionFile(path113, content) {
37403
+ function analyzeInstructionFile(path114, content) {
37371
37404
  const findings = [];
37372
37405
  const decoded = decodeSuspiciousBase64(content);
37373
37406
  const glyph = homoglyphWord(content);
@@ -37381,7 +37414,7 @@ function analyzeInstructionFile(path113, content) {
37381
37414
  `\`${glyph.folded}\` is written with ${glyph.count} non-Latin look-alike letter(s): it reads the same to a reviewer, but a search, a replace or a policy check on the real word misses it`
37382
37415
  ],
37383
37416
  "Spell the word with plain Latin letters, or explain on the same line why the look-alikes are there.",
37384
- path113,
37417
+ path114,
37385
37418
  glyph.line
37386
37419
  )
37387
37420
  );
@@ -37395,7 +37428,7 @@ function analyzeInstructionFile(path113, content) {
37395
37428
  "contains Unicode tag characters (U+E0000\u2013E007F): an invisible instruction-smuggling channel with no legitimate use in text"
37396
37429
  ],
37397
37430
  "Remove the tag characters. Instruction files must be plain, reviewable text.",
37398
- path113,
37431
+ path114,
37399
37432
  lineAt(content, content.search(TAG_CHARS))
37400
37433
  )
37401
37434
  );
@@ -37409,7 +37442,7 @@ function analyzeInstructionFile(path113, content) {
37409
37442
  "contains a bidi override (U+202D/U+202E): a Trojan-Source technique that visually reorders text so a human reads something different from what the agent parses"
37410
37443
  ],
37411
37444
  "Remove the bidi override characters.",
37412
- path113,
37445
+ path114,
37413
37446
  lineAt(content, content.search(BIDI_OVERRIDE))
37414
37447
  )
37415
37448
  );
@@ -37423,7 +37456,7 @@ function analyzeInstructionFile(path113, content) {
37423
37456
  "contains bidi embed/isolate characters (U+202A\u2013202C / U+2066\u20132069): legitimate in right-to-left text, but confirm they are not being used to hide or reorder instructions"
37424
37457
  ],
37425
37458
  "Confirm the bidi marks are legitimate RTL formatting; remove otherwise.",
37426
- path113,
37459
+ path114,
37427
37460
  lineAt(content, content.search(BIDI_EMBED_ISOLATE))
37428
37461
  )
37429
37462
  );
@@ -37439,7 +37472,7 @@ function analyzeInstructionFile(path113, content) {
37439
37472
  revealed ? "a zero-width character conceals a prompt-override directive that only appears once the hidden characters are stripped" : "a zero-width character splits a visible Latin word: a concealment technique (hides text from human review while the agent reads it as contiguous)"
37440
37473
  ],
37441
37474
  "Remove the zero-width characters. Instruction files must be plain, reviewable text.",
37442
- path113,
37475
+ path114,
37443
37476
  lineAt(content, zw.first)
37444
37477
  )
37445
37478
  );
@@ -37458,7 +37491,7 @@ function analyzeInstructionFile(path113, content) {
37458
37491
  `contains a prompt-override / role-impersonation directive (\`${safeText(m[0].slice(0, 60).trim(), 60)}\`)${ovEnc ? ", concealed in a base64 blob" : ""}`
37459
37492
  ],
37460
37493
  "Remove the override text. An instruction file should not tell the agent to ignore its own rules.",
37461
- path113,
37494
+ path114,
37462
37495
  ov ? lineAt(content, ov.index) : void 0
37463
37496
  // concealed in base64: no line, never a wrong one
37464
37497
  )
@@ -37475,7 +37508,7 @@ function analyzeInstructionFile(path113, content) {
37475
37508
  `\`${safeText(fo[0].slice(0, 70).trim(), 70)}\`: fetch-and-obey, outside an install/setup section`
37476
37509
  ],
37477
37510
  "Do not instruct the agent to pipe remote content into a shell; pin and vendor scripts instead.",
37478
- path113,
37511
+ path114,
37479
37512
  lineAt(content, fo.index)
37480
37513
  )
37481
37514
  );
@@ -37491,7 +37524,7 @@ function analyzeInstructionFile(path113, content) {
37491
37524
  `references \`${safeText(sp[0].slice(0, 50).trim(), 50)}\`: directs the agent toward secrets`
37492
37525
  ],
37493
37526
  "Do not reference credential files or paths in agent instructions.",
37494
- path113,
37527
+ path114,
37495
37528
  lineAt(content, sp.index)
37496
37529
  )
37497
37530
  );
@@ -37514,7 +37547,7 @@ function analyzeInstructionFile(path113, content) {
37514
37547
  "Instruction directs the agent to send data to an external endpoint",
37515
37548
  [`\`${safeText(ex[0].slice(0, 70).trim(), 70)}\`: possible exfiltration directive`],
37516
37549
  "Remove external post/upload directives from agent instructions.",
37517
- path113,
37550
+ path114,
37518
37551
  lineAt(content, ex.index)
37519
37552
  )
37520
37553
  );
@@ -37747,7 +37780,7 @@ function cmdLine(content, cmd) {
37747
37780
  const i = jsonValueIndex(content, cmd);
37748
37781
  return i >= 0 ? lineAtIndex(content, i) : void 0;
37749
37782
  }
37750
- function analyzeAgentConfig(path113, content, tree) {
37783
+ function analyzeAgentConfig(path114, content, tree) {
37751
37784
  let cfg;
37752
37785
  try {
37753
37786
  cfg = JSON.parse(content);
@@ -37770,7 +37803,7 @@ function analyzeAgentConfig(path113, content, tree) {
37770
37803
  dimension: "toolRules",
37771
37804
  severity: high ? "high" : "medium",
37772
37805
  title: high ? "Agent hook runs UNPINNED/remote third-party code on every action" : "Agent hook runs third-party code in the agent hot path",
37773
- file: path113,
37806
+ file: path114,
37774
37807
  ...cmdLine(content, cmd) ? { line: cmdLine(content, cmd) } : {},
37775
37808
  signals: [
37776
37809
  `hook command: \`${safeText(cmd, 120)}\``,
@@ -37791,7 +37824,7 @@ function analyzeAgentConfig(path113, content, tree) {
37791
37824
  dimension: "toolRules",
37792
37825
  severity: "medium",
37793
37826
  title: "Agent hook runs a script that is not committed",
37794
- file: path113,
37827
+ file: path114,
37795
37828
  ...cmdLine(content, cmd) ? { line: cmdLine(content, cmd) } : {},
37796
37829
  signals: [
37797
37830
  `hook command: \`${safeText(cmd, 120)}\``,
@@ -37807,7 +37840,7 @@ function analyzeAgentConfig(path113, content, tree) {
37807
37840
  dimension: "toolRules",
37808
37841
  severity: "advisory",
37809
37842
  title: "Agent hook runs a committed script this scan did not read",
37810
- file: path113,
37843
+ file: path114,
37811
37844
  ...cmdLine(content, cmd) ? { line: cmdLine(content, cmd) } : {},
37812
37845
  signals: [
37813
37846
  `hook command: \`${safeText(cmd, 120)}\``,
@@ -37836,12 +37869,12 @@ function analyzeAgentConfig(path113, content, tree) {
37836
37869
  dimension: "toolRules",
37837
37870
  severity: high ? "high" : "medium",
37838
37871
  title: high ? "Committed agent config pre-authorizes broad tools with no deny backstop" : "Committed agent config pre-authorizes broad tools",
37839
- file: path113,
37872
+ file: path114,
37840
37873
  signals,
37841
37874
  fix: "Scope the allow-list to specific read-only subcommands (e.g. `Bash(gh pr view:*)`); avoid bare `Bash`/`git:`/`Write`, or add a `deny` backstop.",
37842
37875
  explain: explainGrant({
37843
37876
  high,
37844
- file: path113,
37877
+ file: path114,
37845
37878
  broad: grade.broad,
37846
37879
  bareShell: grade.bareShell,
37847
37880
  hasBackstop: grade.bareShell ? deny.some((d) => /^Bash\b/.test(d)) : deny.some((d) => /Bash|Write|Edit/.test(d))
@@ -37858,7 +37891,7 @@ function analyzeAgentConfig(path113, content, tree) {
37858
37891
  dimension: "toolRules",
37859
37892
  severity: "medium",
37860
37893
  title: "Committed agent config pre-approves a command that can run other programs",
37861
- file: path113,
37894
+ file: path114,
37862
37895
  signals: [
37863
37896
  `pre-approved: ${escapes.slice(0, 5).map((e) => `\`${safeText(e, 80)}\``).join(", ")}`,
37864
37897
  "`find -exec` and `make -n --eval` run other programs without a prompt"
@@ -37868,8 +37901,8 @@ function analyzeAgentConfig(path113, content, tree) {
37868
37901
  }
37869
37902
  return findings;
37870
37903
  }
37871
- function skillReadsUntrusted(path113, content, allow, broad) {
37872
- if (!ROOT_SURFACE_RE.test(path113)) return null;
37904
+ function skillReadsUntrusted(path114, content, allow, broad) {
37905
+ if (!ROOT_SURFACE_RE.test(path114)) return null;
37873
37906
  const body = content.replace(/^---[\s\S]*?\n---/, "");
37874
37907
  const runsCode = broad.map((a) => a.replace(/^PowerShell(?=$|\()/, "Bash")).some((a) => /^Bash$|^Bash\(\s*\*/.test(a) || openRunner(a) && !/^Bash\(\s*gh\b/.test(a));
37875
37908
  const webGrant = allow.some((a) => /^(WebFetch|WebSearch)\b/.test(a));
@@ -37884,8 +37917,8 @@ function skillReadsUntrusted(path113, content, allow, broad) {
37884
37917
  return "it reads pull request text that strangers write, and may edit files and run git/gh on it";
37885
37918
  return null;
37886
37919
  }
37887
- function analyzeSkillGrants(path113, content) {
37888
- if (!GRANT_CARRIER_RE.test(path113)) return [];
37920
+ function analyzeSkillGrants(path114, content) {
37921
+ if (!GRANT_CARRIER_RE.test(path114)) return [];
37889
37922
  const allow = allowedToolsOf(parseFrontmatter(content));
37890
37923
  if (allow.length === 0) return [];
37891
37924
  const grade = gradeBroadGrant(allow, [], {
@@ -37893,9 +37926,9 @@ function analyzeSkillGrants(path113, content) {
37893
37926
  scope: "whenever this skill or command is active, for everyone who opens this repo with the agent"
37894
37927
  });
37895
37928
  if (!grade) return [];
37896
- const kind = /commands\//.test(path113) ? "slash command" : "skill";
37929
+ const kind = /commands\//.test(path114) ? "slash command" : "skill";
37897
37930
  const at = content.search(/^allowed-tools\s*:/m);
37898
- const untrusted = skillReadsUntrusted(path113, content, allow, grade.broad);
37931
+ const untrusted = skillReadsUntrusted(path114, content, allow, grade.broad);
37899
37932
  if (untrusted)
37900
37933
  return [
37901
37934
  {
@@ -37905,7 +37938,7 @@ function analyzeSkillGrants(path113, content) {
37905
37938
  dimension: "toolRules",
37906
37939
  severity: "advisory",
37907
37940
  title: `Committed ${kind} runs code or writes with text strangers control`,
37908
- file: path113,
37941
+ file: path114,
37909
37942
  signals: [...grade.signals, untrusted],
37910
37943
  fix: "Drop the shell or interpreter grant from a skill that reads PRs, issues or the web, or have it treat that text as data in a read-only step first."
37911
37944
  }
@@ -37922,7 +37955,7 @@ function analyzeSkillGrants(path113, content) {
37922
37955
  // shell — the cap is on reach, not on honesty.
37923
37956
  severity: "medium",
37924
37957
  title: grade.high ? `Committed ${kind} pre-authorizes an unrestricted shell while active` : `Committed ${kind} pre-authorizes broad tools while active`,
37925
- file: path113,
37958
+ file: path114,
37926
37959
  signals: grade.signals,
37927
37960
  fix: "Scope `allowed-tools` to the specific commands the skill needs (e.g. `Bash(git status:*)`); avoid a bare `Bash`, `Write` or `Edit`."
37928
37961
  }
@@ -38054,7 +38087,7 @@ function ownPackageNames(manifests) {
38054
38087
  }
38055
38088
  return out;
38056
38089
  }
38057
- function analyzeMcp(path113, content, own = /* @__PURE__ */ new Set()) {
38090
+ function analyzeMcp(path114, content, own = /* @__PURE__ */ new Set()) {
38058
38091
  let cfg;
38059
38092
  try {
38060
38093
  cfg = JSON.parse(content);
@@ -38090,9 +38123,9 @@ function analyzeMcp(path113, content, own = /* @__PURE__ */ new Set()) {
38090
38123
  const key = (count.get(name) ?? 0) > 1 ? `${section}.${name}` : name;
38091
38124
  servers[key] = { ...spec, anchor: name };
38092
38125
  }
38093
- return analyzeMcpServers(servers, path113, content, own);
38126
+ return analyzeMcpServers(servers, path114, content, own);
38094
38127
  }
38095
- function analyzeMcpServers(servers, path113, content = "", own = /* @__PURE__ */ new Set()) {
38128
+ function analyzeMcpServers(servers, path114, content = "", own = /* @__PURE__ */ new Set()) {
38096
38129
  const findings = [];
38097
38130
  for (const [name, srv] of Object.entries(servers ?? {})) {
38098
38131
  if (!srv || srv.disabled) continue;
@@ -38111,7 +38144,7 @@ function analyzeMcpServers(servers, path113, content = "", own = /* @__PURE__ */
38111
38144
  dimension: "mcp",
38112
38145
  severity: "medium",
38113
38146
  title: `MCP server "${name}" runs an unpinned executable`,
38114
- file: path113,
38147
+ file: path114,
38115
38148
  ...line ? { line } : {},
38116
38149
  signals: [
38117
38150
  why.launcher === "npx" ? `\`${safeText(argv, 120)}\`: unversioned/@latest npx` : `\`${safeText(argv, 120)}\`: unversioned/latest ${why.launcher}`
@@ -38136,7 +38169,7 @@ function analyzeMcpServers(servers, path113, content = "", own = /* @__PURE__ */
38136
38169
  dimension: "mcp",
38137
38170
  severity: "high",
38138
38171
  title: `MCP server "${name}" has an inline credential`,
38139
- file: path113,
38172
+ file: path114,
38140
38173
  ...line ? { line } : {},
38141
38174
  signals: [
38142
38175
  `env.${k} matches ${hit.patternName}: agent-reachable secret committed to the repo`
@@ -38195,7 +38228,7 @@ var init_mcp = __esm({
38195
38228
  });
38196
38229
 
38197
38230
  // src/ci-check/codex.ts
38198
- function analyzeCodexConfig(path113, content, own = /* @__PURE__ */ new Set()) {
38231
+ function analyzeCodexConfig(path114, content, own = /* @__PURE__ */ new Set()) {
38199
38232
  let cfg;
38200
38233
  try {
38201
38234
  cfg = (0, import_smol_toml6.parse)(content);
@@ -38203,7 +38236,7 @@ function analyzeCodexConfig(path113, content, own = /* @__PURE__ */ new Set()) {
38203
38236
  return [];
38204
38237
  }
38205
38238
  const findings = [];
38206
- findings.push(...analyzeMcpServers(cfg.mcp_servers ?? {}, path113, content, own));
38239
+ findings.push(...analyzeMcpServers(cfg.mcp_servers ?? {}, path114, content, own));
38207
38240
  const sandbox = typeof cfg.sandbox_mode === "string" ? cfg.sandbox_mode : "";
38208
38241
  const approval = typeof cfg.approval_policy === "string" ? cfg.approval_policy : "";
38209
38242
  const fullAccess = /danger-full-access/i.test(sandbox);
@@ -38226,7 +38259,7 @@ function analyzeCodexConfig(path113, content, own = /* @__PURE__ */ new Set()) {
38226
38259
  dimension: "toolRules",
38227
38260
  severity: fullAccess ? "high" : "medium",
38228
38261
  title: fullAccess ? "Codex config grants a full-access sandbox" : "Codex config never requires approval",
38229
- file: path113,
38262
+ file: path114,
38230
38263
  signals,
38231
38264
  fix: 'Commit a least-privilege Codex config: prefer `sandbox_mode = "read-only"` (or `"workspace-write"`) and `approval_policy = "on-request"`/`"on-failure"`. A repo-committed config applies to every contributor who runs Codex here.'
38232
38265
  });
@@ -38395,7 +38428,7 @@ var init_agent_permissions = __esm({
38395
38428
  init_codex();
38396
38429
  init_read_small_file();
38397
38430
  CATEGORY = "Agent permissions";
38398
- CLAUDE_NO_PROMPT_MODES = /* @__PURE__ */ new Set(["bypassPermissions", "dontAsk"]);
38431
+ CLAUDE_NO_PROMPT_MODES = /* @__PURE__ */ new Set(["bypassPermissions"]);
38399
38432
  MAX_FILE_BYTES3 = 2 * 1024 * 1024;
38400
38433
  CI_SEVERITY = {
38401
38434
  critical: "critical",
@@ -38430,6 +38463,15 @@ function checkOutsideContent(ctx) {
38430
38463
  const roles = config.policy.untrustedContent?.roles ?? {};
38431
38464
  const sources = outsideSources(home, roles);
38432
38465
  if (sources.length === 0) return [];
38466
+ const byServer = /* @__PURE__ */ new Map();
38467
+ for (const src of sources) {
38468
+ const e = byServer.get(src.server);
38469
+ if (e) {
38470
+ if (!e.agents.includes(src.agent)) e.agents.push(src.agent);
38471
+ if (src.how === "role") e.how = "role";
38472
+ } else byServer.set(src.server, { agents: [src.agent], how: src.how });
38473
+ }
38474
+ const servers = byServer.size;
38433
38475
  const egress = config.policy.egress;
38434
38476
  const egressGated = !!egress?.enabled && (egress.mode === "block" || egress.mode === "review");
38435
38477
  const hold = checkValue(
@@ -38446,11 +38488,14 @@ function checkOutsideContent(ctx) {
38446
38488
  {
38447
38489
  category: CATEGORY2,
38448
38490
  severity: "high",
38449
- title: `${sources.length} MCP server${sources.length === 1 ? "" : "s"} bring${sources.length === 1 ? "s" : ""} outside content in, and the way out is open`,
38491
+ title: `${servers} MCP server${servers === 1 ? "" : "s"} bring${servers === 1 ? "s" : ""} outside content in, and the way out is open`,
38450
38492
  what: "An agent here reads pages, issues or messages written by others, and what it does next is not held.",
38451
38493
  why: gaps.join("; ") + ".",
38452
38494
  who: "One poisoned page or issue is enough: the agent reads it, then sends or deletes as it says.",
38453
- detail: [...sources.slice(0, 8).map((s) => `${s.server} (${s.agent}, by ${s.how})`), ...gaps],
38495
+ detail: [
38496
+ ...[...byServer].slice(0, 8).map(([name, e]) => `${name} (${e.agents.join(", ")}, by ${e.how})`),
38497
+ ...gaps
38498
+ ],
38454
38499
  owner: "node9",
38455
38500
  fix: 'Run `node9 egress watch` (or `node9 egress lock`), keep behavior.untrusted-content on, and mark each server\'s reading tools as "Brings outside content" on the Apps page so the hold applies to them.'
38456
38501
  }
@@ -38597,8 +38642,8 @@ var init_instruction_files = __esm({
38597
38642
  });
38598
38643
 
38599
38644
  // src/skill-pin.ts
38600
- function getPinsFilePath2() {
38601
- return import_path57.default.join(import_os46.default.homedir(), ".node9", "skill-pins.json");
38645
+ function getPinsFilePath2(home = import_os46.default.homedir()) {
38646
+ return import_path57.default.join(home, ".node9", "skill-pins.json");
38602
38647
  }
38603
38648
  function sha256Bytes(buf) {
38604
38649
  return import_crypto17.default.createHash("sha256").update(buf).digest("hex");
@@ -38668,8 +38713,8 @@ function hashSkillRoot(absPath) {
38668
38713
  function getRootKey(absPath) {
38669
38714
  return import_crypto17.default.createHash("sha256").update(absPath).digest("hex").slice(0, 16);
38670
38715
  }
38671
- function readSkillPinsSafe() {
38672
- const filePath = getPinsFilePath2();
38716
+ function readSkillPinsSafe(home) {
38717
+ const filePath = getPinsFilePath2(home);
38673
38718
  try {
38674
38719
  const raw = import_fs55.default.readFileSync(filePath, "utf-8");
38675
38720
  if (!raw.trim()) return { ok: false, reason: "corrupt", detail: "empty file" };
@@ -38853,7 +38898,7 @@ function checkSupplyChain(ctx) {
38853
38898
  }
38854
38899
  function pluginPinState(home) {
38855
38900
  const roots = defaultSkillRoots(void 0, home);
38856
- const pins = readSkillPinsSafe();
38901
+ const pins = readSkillPinsSafe(home);
38857
38902
  const pinned = pins.ok ? pins.pins.roots : {};
38858
38903
  return { roots, unpinned: roots.filter((r) => !pinned[getRootKey(r)]) };
38859
38904
  }
@@ -38866,7 +38911,7 @@ function pluginsAndPackages(ctx, home) {
38866
38911
  if (roots.length > 0 && tamper === "off") {
38867
38912
  findings.push({
38868
38913
  category: "Supply chain",
38869
- severity: "medium",
38914
+ severity: "advisory",
38870
38915
  title: `${roots.length} installed plugin${roots.length === 1 ? "" : "s"} load${roots.length === 1 ? "s" : ""} unverified`,
38871
38916
  what: "Plugins and skills the agent loads are code that runs with its rights, and no content hash is kept for them.",
38872
38917
  why: "The check loading.skill-tamper is off, so node9 does not pin them.",
@@ -38878,7 +38923,7 @@ function pluginsAndPackages(ctx, home) {
38878
38923
  } else if (unpinned.length > 0) {
38879
38924
  findings.push({
38880
38925
  category: "Supply chain",
38881
- severity: "medium",
38926
+ severity: "advisory",
38882
38927
  title: `${unpinned.length} of ${roots.length} installed plugin${roots.length === 1 ? "" : "s"} ${unpinned.length === 1 ? "has" : "have"} no pin yet`,
38883
38928
  what: "A plugin the agent can load has no recorded content hash yet.",
38884
38929
  why: "node9 pins a plugin the first time it loads; these have not loaded since pinning was turned on.",
@@ -39244,7 +39289,8 @@ function checkCoverage(ctx) {
39244
39289
  });
39245
39290
  return findings;
39246
39291
  }
39247
- const mode = getConfig(ctx.cwd).settings.mode;
39292
+ const config = getConfig(ctx.cwd);
39293
+ const mode = config.settings.mode;
39248
39294
  if (isRecordOnly(mode)) {
39249
39295
  const level = levelOf(mode);
39250
39296
  findings.push({
@@ -39259,7 +39305,6 @@ function checkCoverage(ctx) {
39259
39305
  fix: "Set mode to `standard` (or `strict`) to enforce in-path."
39260
39306
  });
39261
39307
  }
39262
- const config = getConfig(ctx.cwd);
39263
39308
  if (config.settings.autoStartDaemon !== false) {
39264
39309
  const state = autostartState();
39265
39310
  if (state === "disabled" || state === "absent") {
@@ -39580,8 +39625,6 @@ async function annotateCoverage(findings, ctx) {
39580
39625
  }
39581
39626
  if (probe.kind === "egress") {
39582
39627
  f.coverage = egressCoverage(env);
39583
- if (f.coverage.state === "open" && config.policy.egress.enabled && config.policy.egress.mode !== "off" && isRecordOnly(mode))
39584
- f.fix = auditModeFix(mode, "node9 egress is set");
39585
39628
  continue;
39586
39629
  }
39587
39630
  if (probe.kind === "gateWired") {
@@ -41318,6 +41361,7 @@ async function processClaudeFile(file2, projPath, projLabel, judge, judgeFrom, r
41318
41361
  const sessionCalls = [];
41319
41362
  const toolUseFilePaths = /* @__PURE__ */ new Map();
41320
41363
  const toolUseNames = /* @__PURE__ */ new Map();
41364
+ const toolUseInputs = /* @__PURE__ */ new Map();
41321
41365
  let firstDlpTs = null;
41322
41366
  let firstEditTs = null;
41323
41367
  for (const line of raw.split("\n")) {
@@ -41388,7 +41432,10 @@ async function processClaudeFile(file2, projPath, projLabel, judge, judgeFrom, r
41388
41432
  if (isNode9SelfOutput(resultText)) continue;
41389
41433
  const resultTool = block.tool_use_id ? toolUseNames.get(block.tool_use_id) : void 0;
41390
41434
  if (resultTool)
41391
- judge.noteToolResult(sessionId, resultTool, entry.timestamp ?? "", entry.cwd);
41435
+ judge.noteToolResult(sessionId, resultTool, entry.timestamp ?? "", entry.cwd, {
41436
+ input: block.tool_use_id ? toolUseInputs.get(block.tool_use_id) : void 0,
41437
+ output: resultText
41438
+ });
41392
41439
  const decoysHere1 = recordCanaries(
41393
41440
  { text: resultText },
41394
41441
  "tool-result",
@@ -41453,6 +41500,7 @@ async function processClaudeFile(file2, projPath, projLabel, judge, judgeFrom, r
41453
41500
  toolUseFilePaths.set(block.id, input.file_path);
41454
41501
  }
41455
41502
  if (block.id) toolUseNames.set(block.id, toolName);
41503
+ if (block.id) toolUseInputs.set(block.id, input);
41456
41504
  sessionCalls.push({ toolName, input, timestamp: entry.timestamp ?? "" });
41457
41505
  if (toolNameLower === "bash" || toolNameLower === "execute_bash") {
41458
41506
  result.bashCalls++;
@@ -45667,6 +45715,23 @@ function writeCache2(cache7) {
45667
45715
  } catch {
45668
45716
  }
45669
45717
  }
45718
+ function cacheFromSyncBody(body, etag) {
45719
+ return {
45720
+ fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
45721
+ rules: extractRules(body),
45722
+ etag,
45723
+ panicMode: body.panicMode,
45724
+ shadowMode: body.shadowMode,
45725
+ syncIntervalHours: body.syncIntervalHours,
45726
+ workspaceId: body.workspaceId,
45727
+ shields: extractShields(body),
45728
+ managedConfig: extractManagedConfig(body),
45729
+ config: extractWorkspaceConfig(body)
45730
+ };
45731
+ }
45732
+ function writePolicyCache(body, etag) {
45733
+ writeCache2(cacheFromSyncBody(body, etag));
45734
+ }
45670
45735
  async function syncOnce() {
45671
45736
  const creds = readCredentials();
45672
45737
  if (!creds) return;
@@ -45675,19 +45740,7 @@ async function syncOnce() {
45675
45740
  if (result.kind === "unchanged") {
45676
45741
  recordSyncHealth({ ok: true });
45677
45742
  } else {
45678
- const cache7 = {
45679
- fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
45680
- rules: extractRules(result.body),
45681
- etag: result.etag,
45682
- panicMode: result.body.panicMode,
45683
- shadowMode: result.body.shadowMode,
45684
- syncIntervalHours: result.body.syncIntervalHours,
45685
- workspaceId: result.body.workspaceId,
45686
- shields: extractShields(result.body),
45687
- managedConfig: extractManagedConfig(result.body),
45688
- config: extractWorkspaceConfig(result.body)
45689
- };
45690
- writeCache2(cache7);
45743
+ writeCache2(cacheFromSyncBody(result.body, result.etag));
45691
45744
  recordSyncHealth({ ok: true, changed: true });
45692
45745
  }
45693
45746
  } catch (err2) {
@@ -45905,18 +45958,7 @@ async function runCloudSync() {
45905
45958
  recordSyncHealth({ ok: true });
45906
45959
  return status.cached ? { ok: true, rules: status.rules, fetchedAt: status.fetchedAt, unchanged: true } : { ok: true, rules: 0, fetchedAt: (/* @__PURE__ */ new Date()).toISOString(), unchanged: true };
45907
45960
  }
45908
- const cache7 = {
45909
- fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
45910
- rules: extractRules(result.body),
45911
- etag: result.etag,
45912
- panicMode: result.body.panicMode,
45913
- shadowMode: result.body.shadowMode,
45914
- syncIntervalHours: result.body.syncIntervalHours,
45915
- workspaceId: result.body.workspaceId,
45916
- shields: extractShields(result.body),
45917
- managedConfig: extractManagedConfig(result.body),
45918
- config: extractWorkspaceConfig(result.body)
45919
- };
45961
+ const cache7 = cacheFromSyncBody(result.body, result.etag);
45920
45962
  writeCache2(cache7);
45921
45963
  recordSyncHealth({ ok: true, changed: true });
45922
45964
  return { ok: true, rules: cache7.rules.length, fetchedAt: cache7.fetchedAt };
@@ -46040,6 +46082,7 @@ var init_sync = __esm({
46040
46082
  var audit_shipper_exports = {};
46041
46083
  __export(audit_shipper_exports, {
46042
46084
  AUDIT_SHIP_WATERMARK: () => AUDIT_SHIP_WATERMARK,
46085
+ OWASP_FIELDS_RETRY_MS: () => OWASP_FIELDS_RETRY_MS,
46043
46086
  _resetOwaspWireFields: () => _resetOwaspWireFields,
46044
46087
  buildBatchEndpoint: () => buildBatchEndpoint,
46045
46088
  buildWireRows: () => buildWireRows,
@@ -46052,7 +46095,7 @@ __export(audit_shipper_exports, {
46052
46095
  writeWatermark: () => writeWatermark
46053
46096
  });
46054
46097
  function _resetOwaspWireFields() {
46055
- serverRejectsOwaspFields = false;
46098
+ owaspFieldsRejectedAt = null;
46056
46099
  }
46057
46100
  function withoutOwaspFields(rows) {
46058
46101
  return rows.map((r) => {
@@ -46176,6 +46219,7 @@ async function shipOnce(deps = {}) {
46176
46219
  const auditLogPath = deps.auditLogPath ?? LOCAL_AUDIT_LOG;
46177
46220
  const watermarkPath = deps.watermarkPath ?? AUDIT_SHIP_WATERMARK;
46178
46221
  const fetchImpl = deps.fetchImpl ?? fetch;
46222
+ const now = deps.now ?? Date.now;
46179
46223
  let cloudEnabled = deps.cloudEnabled;
46180
46224
  if (cloudEnabled === void 0) {
46181
46225
  try {
@@ -46213,7 +46257,8 @@ async function shipOnce(deps = {}) {
46213
46257
  if (consumed === 0) break;
46214
46258
  for (let i = 0; i < rows.length; i += MAX_BATCH) {
46215
46259
  const slice = rows.slice(i, i + MAX_BATCH);
46216
- let batch = serverRejectsOwaspFields ? withoutOwaspFields(slice) : slice;
46260
+ const stripFields = owaspFieldsRejectedAt !== null && now() - owaspFieldsRejectedAt < OWASP_FIELDS_RETRY_MS;
46261
+ let batch = stripFields ? withoutOwaspFields(slice) : slice;
46217
46262
  const post = async (body) => {
46218
46263
  const controller = new AbortController();
46219
46264
  const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
@@ -46235,7 +46280,7 @@ async function shipOnce(deps = {}) {
46235
46280
  if (res.status === 400 && hasOwaspFields(batch)) {
46236
46281
  batch = withoutOwaspFields(batch);
46237
46282
  res = await post(batch);
46238
- if (res.ok) serverRejectsOwaspFields = true;
46283
+ if (res.ok) owaspFieldsRejectedAt = now();
46239
46284
  }
46240
46285
  if (!res.ok) throw new Error(`audit/batch HTTP ${res.status}`);
46241
46286
  shipped += batch.length;
@@ -46322,7 +46367,7 @@ function outboxBacklog(auditLogPath = LOCAL_AUDIT_LOG, watermarkPath = AUDIT_SHI
46322
46367
  };
46323
46368
  }
46324
46369
  }
46325
- var import_fs67, import_path70, import_os57, import_crypto21, DECIDED_BY_VALUES, AUDIT_SHIP_WATERMARK, DEFAULT_INTERVAL_MS, MAX_BATCH, MAX_CHUNK_BYTES, MAX_CHUNKS_PER_TICK, FETCH_TIMEOUT_MS, SKIP_CHECKED_BY, CHECK_ID_RE, CANARY_KINDS, OWASP_WIRE_FIELDS, serverRejectsOwaspFields, shipperStarted;
46370
+ var import_fs67, import_path70, import_os57, import_crypto21, DECIDED_BY_VALUES, AUDIT_SHIP_WATERMARK, DEFAULT_INTERVAL_MS, MAX_BATCH, MAX_CHUNK_BYTES, MAX_CHUNKS_PER_TICK, FETCH_TIMEOUT_MS, SKIP_CHECKED_BY, CHECK_ID_RE, CANARY_KINDS, OWASP_WIRE_FIELDS, owaspFieldsRejectedAt, OWASP_FIELDS_RETRY_MS, shipperStarted;
46326
46371
  var init_audit_shipper = __esm({
46327
46372
  "src/daemon/audit-shipper.ts"() {
46328
46373
  "use strict";
@@ -46358,7 +46403,8 @@ var init_audit_shipper = __esm({
46358
46403
  "ssh-key"
46359
46404
  ]);
46360
46405
  OWASP_WIRE_FIELDS = ["checkId", "canary", "canaryKind"];
46361
- serverRejectsOwaspFields = false;
46406
+ owaspFieldsRejectedAt = null;
46407
+ OWASP_FIELDS_RETRY_MS = 60 * 60 * 1e3;
46362
46408
  shipperStarted = false;
46363
46409
  }
46364
46410
  });
@@ -50892,14 +50938,14 @@ var require_util = __commonJS({
50892
50938
  }
50893
50939
  const port = url.port != null ? url.port : url.protocol === "https:" ? 443 : 80;
50894
50940
  let origin = url.origin != null ? url.origin : `${url.protocol || ""}//${url.hostname || ""}:${port}`;
50895
- let path113 = url.path != null ? url.path : `${url.pathname || ""}${url.search || ""}`;
50941
+ let path114 = url.path != null ? url.path : `${url.pathname || ""}${url.search || ""}`;
50896
50942
  if (origin[origin.length - 1] === "/") {
50897
50943
  origin = origin.slice(0, origin.length - 1);
50898
50944
  }
50899
- if (path113 && path113[0] !== "/") {
50900
- path113 = `/${path113}`;
50945
+ if (path114 && path114[0] !== "/") {
50946
+ path114 = `/${path114}`;
50901
50947
  }
50902
- return new URL(`${origin}${path113}`);
50948
+ return new URL(`${origin}${path114}`);
50903
50949
  }
50904
50950
  if (!isHttpOrHttpsPrefixed(url.origin || url.protocol)) {
50905
50951
  throw new InvalidArgumentError("Invalid URL protocol: the URL must start with `http:` or `https:`.");
@@ -51722,9 +51768,9 @@ var require_diagnostics = __commonJS({
51722
51768
  "undici:client:sendHeaders",
51723
51769
  (evt) => {
51724
51770
  const {
51725
- request: { method, path: path113, origin }
51771
+ request: { method, path: path114, origin }
51726
51772
  } = evt;
51727
- debugLog2("sending request to %s %s%s", method, origin, path113);
51773
+ debugLog2("sending request to %s %s%s", method, origin, path114);
51728
51774
  }
51729
51775
  );
51730
51776
  }
@@ -51742,14 +51788,14 @@ var require_diagnostics = __commonJS({
51742
51788
  "undici:request:headers",
51743
51789
  (evt) => {
51744
51790
  const {
51745
- request: { method, path: path113, origin },
51791
+ request: { method, path: path114, origin },
51746
51792
  response: { statusCode }
51747
51793
  } = evt;
51748
51794
  debugLog2(
51749
51795
  "received response to %s %s%s - HTTP %d",
51750
51796
  method,
51751
51797
  origin,
51752
- path113,
51798
+ path114,
51753
51799
  statusCode
51754
51800
  );
51755
51801
  }
@@ -51758,23 +51804,23 @@ var require_diagnostics = __commonJS({
51758
51804
  "undici:request:trailers",
51759
51805
  (evt) => {
51760
51806
  const {
51761
- request: { method, path: path113, origin }
51807
+ request: { method, path: path114, origin }
51762
51808
  } = evt;
51763
- debugLog2("trailers received from %s %s%s", method, origin, path113);
51809
+ debugLog2("trailers received from %s %s%s", method, origin, path114);
51764
51810
  }
51765
51811
  );
51766
51812
  diagnosticsChannel.subscribe(
51767
51813
  "undici:request:error",
51768
51814
  (evt) => {
51769
51815
  const {
51770
- request: { method, path: path113, origin },
51816
+ request: { method, path: path114, origin },
51771
51817
  error
51772
51818
  } = evt;
51773
51819
  debugLog2(
51774
51820
  "request to %s %s%s errored - %s",
51775
51821
  method,
51776
51822
  origin,
51777
- path113,
51823
+ path114,
51778
51824
  error.message
51779
51825
  );
51780
51826
  }
@@ -51890,7 +51936,7 @@ var require_request = __commonJS({
51890
51936
  var kHandler = /* @__PURE__ */ Symbol("handler");
51891
51937
  var Request = class {
51892
51938
  constructor(origin, {
51893
- path: path113,
51939
+ path: path114,
51894
51940
  method,
51895
51941
  body,
51896
51942
  headers,
@@ -51907,11 +51953,11 @@ var require_request = __commonJS({
51907
51953
  maxRedirections,
51908
51954
  typeOfService
51909
51955
  }, handler) {
51910
- if (typeof path113 !== "string") {
51956
+ if (typeof path114 !== "string") {
51911
51957
  throw new InvalidArgumentError("path must be a string");
51912
- } else if (path113[0] !== "/" && !(path113.startsWith("http://") || path113.startsWith("https://")) && method !== "CONNECT") {
51958
+ } else if (path114[0] !== "/" && !(path114.startsWith("http://") || path114.startsWith("https://")) && method !== "CONNECT") {
51913
51959
  throw new InvalidArgumentError("path must be an absolute URL or start with a slash");
51914
- } else if (invalidPathRegex.test(path113)) {
51960
+ } else if (invalidPathRegex.test(path114)) {
51915
51961
  throw new InvalidArgumentError("invalid request path");
51916
51962
  }
51917
51963
  if (typeof method !== "string") {
@@ -51986,7 +52032,7 @@ var require_request = __commonJS({
51986
52032
  this.completed = false;
51987
52033
  this.aborted = false;
51988
52034
  this.upgrade = upgrade || null;
51989
- this.path = query ? serializePathWithQuery(path113, query) : path113;
52035
+ this.path = query ? serializePathWithQuery(path114, query) : path114;
51990
52036
  this.origin = origin;
51991
52037
  this.protocol = getProtocolFromUrlString(origin);
51992
52038
  this.idempotent = idempotent == null ? method === "HEAD" || method === "GET" : idempotent;
@@ -57233,7 +57279,7 @@ var require_client_h1 = __commonJS({
57233
57279
  return method !== "GET" && method !== "HEAD" && method !== "OPTIONS" && method !== "TRACE" && method !== "CONNECT";
57234
57280
  }
57235
57281
  function writeH1(client, request2) {
57236
- const { method, path: path113, host: host2, upgrade, blocking, reset } = request2;
57282
+ const { method, path: path114, host: host2, upgrade, blocking, reset } = request2;
57237
57283
  let { body, headers, contentLength } = request2;
57238
57284
  const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH" || method === "QUERY" || method === "PROPFIND" || method === "PROPPATCH";
57239
57285
  if (util.isFormDataLike(body)) {
@@ -57311,7 +57357,7 @@ var require_client_h1 = __commonJS({
57311
57357
  if (socket.setTypeOfService) {
57312
57358
  socket.setTypeOfService(request2.typeOfService);
57313
57359
  }
57314
- let header = `${method} ${path113} HTTP/1.1\r
57360
+ let header = `${method} ${path114} HTTP/1.1\r
57315
57361
  `;
57316
57362
  if (typeof host2 === "string") {
57317
57363
  header += `host: ${host2}\r
@@ -57989,7 +58035,7 @@ var require_client_h2 = __commonJS({
57989
58035
  const headersTimeout = request2.headersTimeout ?? client[kHeadersTimeout];
57990
58036
  const bodyTimeout = request2.bodyTimeout ?? client[kBodyTimeout];
57991
58037
  const session = client[kHTTP2Session];
57992
- const { method, path: path113, host: host2, upgrade, expectContinue, signal, protocol, headers: reqHeaders } = request2;
58038
+ const { method, path: path114, host: host2, upgrade, expectContinue, signal, protocol, headers: reqHeaders } = request2;
57993
58039
  let { body } = request2;
57994
58040
  if (upgrade != null && upgrade !== "websocket") {
57995
58041
  util.errorRequest(client, request2, new InvalidArgumentError(`Custom upgrade "${upgrade}" not supported over HTTP/2`));
@@ -58058,7 +58104,7 @@ var require_client_h2 = __commonJS({
58058
58104
  }
58059
58105
  headers[HTTP2_HEADER_METHOD] = "CONNECT";
58060
58106
  headers[HTTP2_HEADER_PROTOCOL] = "websocket";
58061
- headers[HTTP2_HEADER_PATH] = path113;
58107
+ headers[HTTP2_HEADER_PATH] = path114;
58062
58108
  if (protocol === "ws:" || protocol === "wss:") {
58063
58109
  headers[HTTP2_HEADER_SCHEME] = protocol === "ws:" ? "http" : "https";
58064
58110
  } else {
@@ -58110,7 +58156,7 @@ var require_client_h2 = __commonJS({
58110
58156
  stream.setTimeout(headersTimeout);
58111
58157
  return true;
58112
58158
  }
58113
- headers[HTTP2_HEADER_PATH] = path113;
58159
+ headers[HTTP2_HEADER_PATH] = path114;
58114
58160
  headers[HTTP2_HEADER_SCHEME] = protocol === "http:" ? "http" : "https";
58115
58161
  const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH";
58116
58162
  if (body && typeof body.read === "function") {
@@ -60477,10 +60523,10 @@ var require_proxy_agent = __commonJS({
60477
60523
  };
60478
60524
  const {
60479
60525
  origin,
60480
- path: path113 = "/",
60526
+ path: path114 = "/",
60481
60527
  headers = {}
60482
60528
  } = opts;
60483
- opts.path = origin + path113;
60529
+ opts.path = origin + path114;
60484
60530
  if (!("host" in headers) && !("Host" in headers)) {
60485
60531
  const { host: host2 } = new URL(origin);
60486
60532
  headers.host = host2;
@@ -62655,20 +62701,20 @@ var require_mock_utils = __commonJS({
62655
62701
  }
62656
62702
  return normalizedQp;
62657
62703
  }
62658
- function safeUrl(path113) {
62659
- if (typeof path113 !== "string") {
62660
- return path113;
62704
+ function safeUrl(path114) {
62705
+ if (typeof path114 !== "string") {
62706
+ return path114;
62661
62707
  }
62662
- const pathSegments = path113.split("?", 3);
62708
+ const pathSegments = path114.split("?", 3);
62663
62709
  if (pathSegments.length !== 2) {
62664
- return path113;
62710
+ return path114;
62665
62711
  }
62666
62712
  const qp = new URLSearchParams(pathSegments.pop());
62667
62713
  qp.sort();
62668
62714
  return [...pathSegments, qp.toString()].join("?");
62669
62715
  }
62670
- function matchKey(mockDispatch2, { path: path113, method, body, headers }) {
62671
- const pathMatch = matchValue(mockDispatch2.path, path113);
62716
+ function matchKey(mockDispatch2, { path: path114, method, body, headers }) {
62717
+ const pathMatch = matchValue(mockDispatch2.path, path114);
62672
62718
  const methodMatch = matchValue(mockDispatch2.method, method);
62673
62719
  const bodyMatch = typeof mockDispatch2.body !== "undefined" ? matchValue(mockDispatch2.body, body) : true;
62674
62720
  const headersMatch = matchHeaders(mockDispatch2, headers);
@@ -62693,8 +62739,8 @@ var require_mock_utils = __commonJS({
62693
62739
  const basePath = key.query ? serializePathWithQuery(key.path, key.query) : key.path;
62694
62740
  const resolvedPath = typeof basePath === "string" ? safeUrl(basePath) : basePath;
62695
62741
  const resolvedPathWithoutTrailingSlash = removeTrailingSlash(resolvedPath);
62696
- let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path: path113, ignoreTrailingSlash }) => {
62697
- return ignoreTrailingSlash ? matchValue(removeTrailingSlash(safeUrl(path113)), resolvedPathWithoutTrailingSlash) : matchValue(safeUrl(path113), resolvedPath);
62742
+ let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path: path114, ignoreTrailingSlash }) => {
62743
+ return ignoreTrailingSlash ? matchValue(removeTrailingSlash(safeUrl(path114)), resolvedPathWithoutTrailingSlash) : matchValue(safeUrl(path114), resolvedPath);
62698
62744
  });
62699
62745
  if (matchedMockDispatches.length === 0) {
62700
62746
  throw new MockNotMatchedError(`Mock dispatch not matched for path '${resolvedPath}'`);
@@ -62733,19 +62779,19 @@ var require_mock_utils = __commonJS({
62733
62779
  mockDispatches.splice(index, 1);
62734
62780
  }
62735
62781
  }
62736
- function removeTrailingSlash(path113) {
62737
- while (path113.endsWith("/")) {
62738
- path113 = path113.slice(0, -1);
62782
+ function removeTrailingSlash(path114) {
62783
+ while (path114.endsWith("/")) {
62784
+ path114 = path114.slice(0, -1);
62739
62785
  }
62740
- if (path113.length === 0) {
62741
- path113 = "/";
62786
+ if (path114.length === 0) {
62787
+ path114 = "/";
62742
62788
  }
62743
- return path113;
62789
+ return path114;
62744
62790
  }
62745
62791
  function buildKey(opts) {
62746
- const { path: path113, method, body, headers, query } = opts;
62792
+ const { path: path114, method, body, headers, query } = opts;
62747
62793
  return {
62748
- path: path113,
62794
+ path: path114,
62749
62795
  method,
62750
62796
  body,
62751
62797
  headers,
@@ -63440,10 +63486,10 @@ var require_pending_interceptors_formatter = __commonJS({
63440
63486
  }
63441
63487
  format(pendingInterceptors) {
63442
63488
  const withPrettyHeaders = pendingInterceptors.map(
63443
- ({ method, path: path113, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
63489
+ ({ method, path: path114, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
63444
63490
  Method: method,
63445
63491
  Origin: origin,
63446
- Path: path113,
63492
+ Path: path114,
63447
63493
  "Status code": statusCode,
63448
63494
  Persistent: persist ? PERSISTENT : NOT_PERSISTENT,
63449
63495
  Invocations: timesInvoked,
@@ -63526,9 +63572,9 @@ var require_mock_agent = __commonJS({
63526
63572
  const acceptNonStandardSearchParameters = this[kMockAgentAcceptsNonStandardSearchParameters];
63527
63573
  const dispatchOpts = { ...opts };
63528
63574
  if (acceptNonStandardSearchParameters && dispatchOpts.path) {
63529
- const [path113, searchParams] = dispatchOpts.path.split("?");
63575
+ const [path114, searchParams] = dispatchOpts.path.split("?");
63530
63576
  const normalizedSearchParams = normalizeSearchParams(searchParams, acceptNonStandardSearchParameters);
63531
- dispatchOpts.path = `${path113}?${normalizedSearchParams}`;
63577
+ dispatchOpts.path = `${path114}?${normalizedSearchParams}`;
63532
63578
  }
63533
63579
  return this[kAgent].dispatch(dispatchOpts, handler);
63534
63580
  }
@@ -63931,12 +63977,12 @@ var require_snapshot_recorder = __commonJS({
63931
63977
  * @return {Promise<void>} - Resolves when snapshots are loaded
63932
63978
  */
63933
63979
  async loadSnapshots(filePath) {
63934
- const path113 = filePath || this.#snapshotPath;
63935
- if (!path113) {
63980
+ const path114 = filePath || this.#snapshotPath;
63981
+ if (!path114) {
63936
63982
  throw new InvalidArgumentError("Snapshot path is required");
63937
63983
  }
63938
63984
  try {
63939
- const data = await readFile(resolve2(path113), "utf8");
63985
+ const data = await readFile(resolve2(path114), "utf8");
63940
63986
  const parsed = JSON.parse(data);
63941
63987
  if (Array.isArray(parsed)) {
63942
63988
  this.#snapshots.clear();
@@ -63950,7 +63996,7 @@ var require_snapshot_recorder = __commonJS({
63950
63996
  if (error.code === "ENOENT") {
63951
63997
  this.#snapshots.clear();
63952
63998
  } else {
63953
- throw new UndiciError(`Failed to load snapshots from ${path113}`, { cause: error });
63999
+ throw new UndiciError(`Failed to load snapshots from ${path114}`, { cause: error });
63954
64000
  }
63955
64001
  }
63956
64002
  }
@@ -63961,11 +64007,11 @@ var require_snapshot_recorder = __commonJS({
63961
64007
  * @returns {Promise<void>} - Resolves when snapshots are saved
63962
64008
  */
63963
64009
  async saveSnapshots(filePath) {
63964
- const path113 = filePath || this.#snapshotPath;
63965
- if (!path113) {
64010
+ const path114 = filePath || this.#snapshotPath;
64011
+ if (!path114) {
63966
64012
  throw new InvalidArgumentError("Snapshot path is required");
63967
64013
  }
63968
- const resolvedPath = resolve2(path113);
64014
+ const resolvedPath = resolve2(path114);
63969
64015
  await mkdir(dirname3(resolvedPath), { recursive: true });
63970
64016
  const data = Array.from(this.#snapshots.entries()).map(([hash2, snapshot]) => ({
63971
64017
  hash: hash2,
@@ -64606,15 +64652,15 @@ var require_redirect_handler = __commonJS({
64606
64652
  }
64607
64653
  this.history[kRedirectCacheControl].push(headers["cache-control"]);
64608
64654
  const { origin, pathname, search } = util.parseURL(new URL(this.location, this.opts.origin && new URL(this.opts.path, this.opts.origin)));
64609
- const path113 = search ? `${pathname}${search}` : pathname;
64610
- const redirectUrlString = `${origin}${path113}`;
64655
+ const path114 = search ? `${pathname}${search}` : pathname;
64656
+ const redirectUrlString = `${origin}${path114}`;
64611
64657
  for (const historyUrl of this.history) {
64612
64658
  if (historyUrl.toString() === redirectUrlString) {
64613
64659
  throw new InvalidArgumentError(`Redirect loop detected. Cannot redirect to ${origin}. This typically happens when using a Client or Pool with cross-origin redirects. Use an Agent for cross-origin redirects.`);
64614
64660
  }
64615
64661
  }
64616
64662
  this.opts.headers = cleanRequestHeaders(this.opts.headers, statusCode === 303, this.opts.origin !== origin);
64617
- this.opts.path = path113;
64663
+ this.opts.path = path114;
64618
64664
  this.opts.origin = origin;
64619
64665
  this.opts.query = null;
64620
64666
  }
@@ -66390,10 +66436,10 @@ var require_cache_handler = __commonJS({
66390
66436
  }
66391
66437
  return locationUrl.pathname + locationUrl.search;
66392
66438
  }
66393
- function deleteCachedUri(store, cacheKey, path113) {
66439
+ function deleteCachedUri(store, cacheKey, path114) {
66394
66440
  deleteCachedValue(store, {
66395
66441
  ...cacheKey,
66396
- path: path113
66442
+ path: path114
66397
66443
  });
66398
66444
  for (let i = 0; i < util.safeHTTPMethods.length; i++) {
66399
66445
  const method = util.safeHTTPMethods[i];
@@ -66401,7 +66447,7 @@ var require_cache_handler = __commonJS({
66401
66447
  deleteCachedValue(store, {
66402
66448
  ...cacheKey,
66403
66449
  method,
66404
- path: path113
66450
+ path: path114
66405
66451
  });
66406
66452
  }
66407
66453
  }
@@ -66412,9 +66458,9 @@ var require_cache_handler = __commonJS({
66412
66458
  }
66413
66459
  const values = Array.isArray(headerValue) ? headerValue : [headerValue];
66414
66460
  for (let i = 0; i < values.length; i++) {
66415
- const path113 = getSameOriginPath(cacheKey, values[i]);
66416
- if (path113 !== void 0) {
66417
- deleteCachedUri(store, cacheKey, path113);
66461
+ const path114 = getSameOriginPath(cacheKey, values[i]);
66462
+ if (path114 !== void 0) {
66463
+ deleteCachedUri(store, cacheKey, path114);
66418
66464
  }
66419
66465
  }
66420
66466
  }
@@ -71637,11 +71683,11 @@ var require_fetch = __commonJS({
71637
71683
  function dispatch({ body }) {
71638
71684
  const url = requestCurrentURL(request2);
71639
71685
  const agent = fetchParams.controller.dispatcher;
71640
- const path113 = url.pathname + url.search;
71686
+ const path114 = url.pathname + url.search;
71641
71687
  const hasTrailingQuestionMark = url.search.length === 0 && url.href[url.href.length - url.hash.length - 1] === "?";
71642
71688
  return new Promise((resolve2, reject) => agent.dispatch(
71643
71689
  {
71644
- path: hasTrailingQuestionMark ? `${path113}?` : path113,
71690
+ path: hasTrailingQuestionMark ? `${path114}?` : path114,
71645
71691
  origin: url.origin,
71646
71692
  method: request2.method,
71647
71693
  body: agent.isMockActive ? request2.body && (request2.body.source || request2.body.stream) : body,
@@ -72593,9 +72639,9 @@ var require_util4 = __commonJS({
72593
72639
  }
72594
72640
  }
72595
72641
  }
72596
- function validateCookiePath(path113) {
72597
- for (let i = 0; i < path113.length; ++i) {
72598
- const code2 = path113.charCodeAt(i);
72642
+ function validateCookiePath(path114) {
72643
+ for (let i = 0; i < path114.length; ++i) {
72644
+ const code2 = path114.charCodeAt(i);
72599
72645
  if (code2 < 32 || // exclude CTLs (0-31)
72600
72646
  code2 > 126 || // exclude DEL and non-ascii
72601
72647
  code2 === 59) {
@@ -76048,11 +76094,11 @@ var require_undici = __commonJS({
76048
76094
  if (typeof opts.path !== "string") {
76049
76095
  throw new InvalidArgumentError("invalid opts.path");
76050
76096
  }
76051
- let path113 = opts.path;
76097
+ let path114 = opts.path;
76052
76098
  if (!opts.path.startsWith("/")) {
76053
- path113 = `/${path113}`;
76099
+ path114 = `/${path114}`;
76054
76100
  }
76055
- url = new URL(util.parseOrigin(url).origin + path113);
76101
+ url = new URL(util.parseOrigin(url).origin + path114);
76056
76102
  } else {
76057
76103
  if (!opts) {
76058
76104
  opts = typeof url === "object" ? url : {};
@@ -76174,10 +76220,10 @@ function readFirstRunEnv() {
76174
76220
  stdinTTY: !!process.stdin.isTTY,
76175
76221
  stdoutTTY: !!process.stdout.isTTY,
76176
76222
  ci: isCI() || process.env.NODE9_NONINTERACTIVE === "1",
76177
- hasConfig: import_fs106.default.existsSync(import_path105.default.join(import_os86.default.homedir(), ".node9/config.json")),
76223
+ hasConfig: import_fs107.default.existsSync(import_path106.default.join(import_os87.default.homedir(), ".node9/config.json")),
76178
76224
  // Any stored profile means this is not a never-configured machine. Treat
76179
76225
  // malformed credential files conservatively and leave repair to explicit setup.
76180
- hasCredentials: !!getCredentials() || import_fs106.default.existsSync(import_path105.default.join(import_os86.default.homedir(), ".node9/credentials.json"))
76226
+ hasCredentials: !!getCredentials() || import_fs107.default.existsSync(import_path106.default.join(import_os87.default.homedir(), ".node9/credentials.json"))
76181
76227
  };
76182
76228
  }
76183
76229
  function shouldOfferFirstRun(env) {
@@ -76282,14 +76328,14 @@ async function runSetupWizard(opts) {
76282
76328
  process.exitCode = 130;
76283
76329
  }
76284
76330
  }
76285
- var import_fs106, import_os86, import_path105;
76331
+ var import_fs107, import_os87, import_path106;
76286
76332
  var init_first_run = __esm({
76287
76333
  "src/cli/first-run.ts"() {
76288
76334
  "use strict";
76289
76335
  init_cjs_shims();
76290
- import_fs106 = __toESM(require("fs"));
76291
- import_os86 = __toESM(require("os"));
76292
- import_path105 = __toESM(require("path"));
76336
+ import_fs107 = __toESM(require("fs"));
76337
+ import_os87 = __toESM(require("os"));
76338
+ import_path106 = __toESM(require("path"));
76293
76339
  init_config();
76294
76340
  init_agent_wiring();
76295
76341
  init_browser_login();
@@ -76335,20 +76381,20 @@ function getModelContextLimit(model) {
76335
76381
  return 2e5;
76336
76382
  }
76337
76383
  function readSessionUsage() {
76338
- const projectsDir = import_path106.default.join(import_os87.default.homedir(), ".claude", "projects");
76339
- if (!import_fs107.default.existsSync(projectsDir)) return null;
76384
+ const projectsDir = import_path107.default.join(import_os88.default.homedir(), ".claude", "projects");
76385
+ if (!import_fs108.default.existsSync(projectsDir)) return null;
76340
76386
  let latestFile = null;
76341
76387
  let latestMtime = 0;
76342
76388
  try {
76343
- for (const dir2 of import_fs107.default.readdirSync(projectsDir)) {
76344
- const dirPath = import_path106.default.join(projectsDir, dir2);
76389
+ for (const dir2 of import_fs108.default.readdirSync(projectsDir)) {
76390
+ const dirPath = import_path107.default.join(projectsDir, dir2);
76345
76391
  try {
76346
- if (!import_fs107.default.statSync(dirPath).isDirectory()) continue;
76347
- for (const file2 of import_fs107.default.readdirSync(dirPath)) {
76392
+ if (!import_fs108.default.statSync(dirPath).isDirectory()) continue;
76393
+ for (const file2 of import_fs108.default.readdirSync(dirPath)) {
76348
76394
  if (!file2.endsWith(".jsonl") || file2.startsWith("agent-")) continue;
76349
- const filePath = import_path106.default.join(dirPath, file2);
76395
+ const filePath = import_path107.default.join(dirPath, file2);
76350
76396
  try {
76351
- const mtime = import_fs107.default.statSync(filePath).mtimeMs;
76397
+ const mtime = import_fs108.default.statSync(filePath).mtimeMs;
76352
76398
  if (mtime > latestMtime) {
76353
76399
  latestMtime = mtime;
76354
76400
  latestFile = filePath;
@@ -76363,7 +76409,7 @@ function readSessionUsage() {
76363
76409
  }
76364
76410
  if (!latestFile) return null;
76365
76411
  try {
76366
- const lines = import_fs107.default.readFileSync(latestFile, "utf-8").split("\n");
76412
+ const lines = import_fs108.default.readFileSync(latestFile, "utf-8").split("\n");
76367
76413
  let lastModel = "";
76368
76414
  let lastInput = 0;
76369
76415
  let lastOutput = 0;
@@ -76424,7 +76470,7 @@ function formatBase(activity) {
76424
76470
  const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
76425
76471
  const icon = getIcon(activity.tool);
76426
76472
  const toolName = activity.tool.slice(0, 16).padEnd(16);
76427
- const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(import_os87.default.homedir(), "~");
76473
+ const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(import_os88.default.homedir(), "~");
76428
76474
  const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
76429
76475
  return `${import_chalk49.default.gray(time)} ${icon} ${agentLabel(activity.agent, activity.mcpServer, activity.sessionId)}${import_chalk49.default.white.bold(toolName)} ${import_chalk49.default.dim(argsPreview)}`;
76430
76476
  }
@@ -76442,12 +76488,12 @@ function renderResult(activity, result) {
76442
76488
  const costSuffix = cost == null ? "" : import_chalk49.default.dim(` ~$${cost >= 1e-3 ? cost.toFixed(3) : "0.000"}`);
76443
76489
  if (process.stdout.isTTY) {
76444
76490
  if (pendingShownForId === activity.id && pendingWrappedLines > 1) {
76445
- import_readline5.default.moveCursor(process.stdout, 0, -(pendingWrappedLines - 1));
76446
- import_readline5.default.cursorTo(process.stdout, 0);
76491
+ import_readline6.default.moveCursor(process.stdout, 0, -(pendingWrappedLines - 1));
76492
+ import_readline6.default.cursorTo(process.stdout, 0);
76447
76493
  process.stdout.write(ERASE_DOWN);
76448
76494
  } else {
76449
- import_readline5.default.clearLine(process.stdout, 0);
76450
- import_readline5.default.cursorTo(process.stdout, 0);
76495
+ import_readline6.default.clearLine(process.stdout, 0);
76496
+ import_readline6.default.cursorTo(process.stdout, 0);
76451
76497
  }
76452
76498
  pendingShownForId = null;
76453
76499
  pendingWrappedLines = 0;
@@ -76463,9 +76509,9 @@ function renderPending(activity) {
76463
76509
  }
76464
76510
  async function ensureDaemon() {
76465
76511
  let pidPort = null;
76466
- if (import_fs107.default.existsSync(PID_FILE)) {
76512
+ if (import_fs108.default.existsSync(PID_FILE)) {
76467
76513
  try {
76468
- const { port } = JSON.parse(import_fs107.default.readFileSync(PID_FILE, "utf-8"));
76514
+ const { port } = JSON.parse(import_fs108.default.readFileSync(PID_FILE, "utf-8"));
76469
76515
  pidPort = port;
76470
76516
  } catch {
76471
76517
  console.error(import_chalk49.default.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
@@ -76491,7 +76537,7 @@ async function ensureDaemon() {
76491
76537
  child.unref();
76492
76538
  if (startupFd !== void 0) {
76493
76539
  try {
76494
- import_fs107.default.closeSync(startupFd);
76540
+ import_fs108.default.closeSync(startupFd);
76495
76541
  } catch {
76496
76542
  }
76497
76543
  }
@@ -76735,7 +76781,7 @@ async function startTail(options = {}) {
76735
76781
  let cancelActiveCard = null;
76736
76782
  const localAllowCounts = /* @__PURE__ */ new Map();
76737
76783
  const canApprove = process.stdout.isTTY && process.stdin.isTTY;
76738
- if (canApprove) import_readline5.default.emitKeypressEvents(process.stdin);
76784
+ if (canApprove) import_readline6.default.emitKeypressEvents(process.stdin);
76739
76785
  let idleKeypressHandler = null;
76740
76786
  function enterIdleMode() {
76741
76787
  if (!canApprove || idleKeypressHandler !== null) return;
@@ -76776,7 +76822,7 @@ async function startTail(options = {}) {
76776
76822
  }
76777
76823
  function clearCard() {
76778
76824
  if (cardLineCount > 0) {
76779
- import_readline5.default.moveCursor(process.stdout, 0, -cardLineCount);
76825
+ import_readline6.default.moveCursor(process.stdout, 0, -cardLineCount);
76780
76826
  process.stdout.write(ERASE_DOWN);
76781
76827
  cardLineCount = 0;
76782
76828
  }
@@ -76861,8 +76907,8 @@ async function startTail(options = {}) {
76861
76907
  );
76862
76908
  }).catch((err2) => {
76863
76909
  try {
76864
- import_fs107.default.appendFileSync(
76865
- import_path106.default.join(import_os87.default.homedir(), ".node9", "hook-debug.log"),
76910
+ import_fs108.default.appendFileSync(
76911
+ import_path107.default.join(import_os88.default.homedir(), ".node9", "hook-debug.log"),
76866
76912
  `[tail] POST /decision failed: ${String(err2)}
76867
76913
  `
76868
76914
  );
@@ -76928,9 +76974,9 @@ async function startTail(options = {}) {
76928
76974
  };
76929
76975
  process.stdin.on("keypress", onKeypress);
76930
76976
  }
76931
- const auditLog = import_path106.default.join(import_os87.default.homedir(), ".node9", "audit.log");
76977
+ const auditLog = import_path107.default.join(import_os88.default.homedir(), ".node9", "audit.log");
76932
76978
  try {
76933
- const unackedDlp = import_fs107.default.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
76979
+ const unackedDlp = import_fs108.default.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
76934
76980
  if (unackedDlp > 0) {
76935
76981
  console.log("");
76936
76982
  console.log(
@@ -76959,8 +77005,8 @@ async function startTail(options = {}) {
76959
77005
  clearCard();
76960
77006
  process.stdout.write(SHOW_CURSOR);
76961
77007
  if (process.stdout.isTTY) {
76962
- import_readline5.default.clearLine(process.stdout, 0);
76963
- import_readline5.default.cursorTo(process.stdout, 0);
77008
+ import_readline6.default.clearLine(process.stdout, 0);
77009
+ import_readline6.default.cursorTo(process.stdout, 0);
76964
77010
  }
76965
77011
  console.log(import_chalk49.default.dim("\n\u{1F6F0}\uFE0F Disconnected."));
76966
77012
  process.exit(0);
@@ -76970,7 +77016,7 @@ async function startTail(options = {}) {
76970
77016
  if (stallWarned) return;
76971
77017
  if (Date.now() - lastActivityFromDaemon < STALL_THRESHOLD_MS) return;
76972
77018
  try {
76973
- const auditMtime = import_fs107.default.statSync(auditLog).mtimeMs;
77019
+ const auditMtime = import_fs108.default.statSync(auditLog).mtimeMs;
76974
77020
  if (Date.now() - auditMtime >= STALL_THRESHOLD_MS) return;
76975
77021
  console.log("");
76976
77022
  console.log(
@@ -76999,7 +77045,7 @@ async function startTail(options = {}) {
76999
77045
  let currentData = "";
77000
77046
  res.on("error", () => {
77001
77047
  });
77002
- const rl = import_readline5.default.createInterface({ input: res, crlfDelay: Infinity });
77048
+ const rl = import_readline6.default.createInterface({ input: res, crlfDelay: Infinity });
77003
77049
  rl.on("error", () => {
77004
77050
  });
77005
77051
  rl.on("line", (line) => {
@@ -77019,8 +77065,8 @@ async function startTail(options = {}) {
77019
77065
  clearCard();
77020
77066
  process.stdout.write(SHOW_CURSOR);
77021
77067
  if (process.stdout.isTTY) {
77022
- import_readline5.default.clearLine(process.stdout, 0);
77023
- import_readline5.default.cursorTo(process.stdout, 0);
77068
+ import_readline6.default.clearLine(process.stdout, 0);
77069
+ import_readline6.default.cursorTo(process.stdout, 0);
77024
77070
  }
77025
77071
  console.log(import_chalk49.default.red("\n\u274C Daemon disconnected."));
77026
77072
  process.exit(1);
@@ -77142,7 +77188,7 @@ async function startTail(options = {}) {
77142
77188
  process.exit(1);
77143
77189
  });
77144
77190
  }
77145
- var import_http6, import_chalk49, import_fs107, import_os87, import_path106, import_readline5, import_child_process17, PID_FILE, ICONS, MODEL_CONTEXT_LIMITS, RESET, BOLD, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, pendingShownForId, pendingWrappedLines, DIVIDER;
77191
+ var import_http6, import_chalk49, import_fs108, import_os88, import_path107, import_readline6, import_child_process17, PID_FILE, ICONS, MODEL_CONTEXT_LIMITS, RESET, BOLD, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, pendingShownForId, pendingWrappedLines, DIVIDER;
77146
77192
  var init_tail = __esm({
77147
77193
  "src/tui/tail.ts"() {
77148
77194
  "use strict";
@@ -77150,16 +77196,16 @@ var init_tail = __esm({
77150
77196
  import_http6 = __toESM(require("http"));
77151
77197
  init_write();
77152
77198
  import_chalk49 = __toESM(require("chalk"));
77153
- import_fs107 = __toESM(require("fs"));
77154
- import_os87 = __toESM(require("os"));
77155
- import_path106 = __toESM(require("path"));
77156
- import_readline5 = __toESM(require("readline"));
77199
+ import_fs108 = __toESM(require("fs"));
77200
+ import_os88 = __toESM(require("os"));
77201
+ import_path107 = __toESM(require("path"));
77202
+ import_readline6 = __toESM(require("readline"));
77157
77203
  import_child_process17 = require("child_process");
77158
77204
  init_startup_log();
77159
77205
  init_daemon3();
77160
77206
  init_daemon();
77161
77207
  init_safe_text();
77162
- PID_FILE = import_path106.default.join(import_os87.default.homedir(), ".node9", "daemon.pid");
77208
+ PID_FILE = import_path107.default.join(import_os88.default.homedir(), ".node9", "daemon.pid");
77163
77209
  ICONS = {
77164
77210
  bash: "\u{1F4BB}",
77165
77211
  shell: "\u{1F4BB}",
@@ -77281,9 +77327,9 @@ function formatTimeLeft(resetsAt) {
77281
77327
  return ` (${m}m left)`;
77282
77328
  }
77283
77329
  function safeReadJson(filePath) {
77284
- if (!import_fs108.default.existsSync(filePath)) return null;
77330
+ if (!import_fs109.default.existsSync(filePath)) return null;
77285
77331
  try {
77286
- return JSON.parse(import_fs108.default.readFileSync(filePath, "utf-8"));
77332
+ return JSON.parse(import_fs109.default.readFileSync(filePath, "utf-8"));
77287
77333
  } catch {
77288
77334
  return null;
77289
77335
  }
@@ -77304,12 +77350,12 @@ function countHooksInFile(filePath) {
77304
77350
  return Object.keys(cfg.hooks).length;
77305
77351
  }
77306
77352
  function countRulesInDir(rulesDir) {
77307
- if (!import_fs108.default.existsSync(rulesDir)) return 0;
77353
+ if (!import_fs109.default.existsSync(rulesDir)) return 0;
77308
77354
  let count = 0;
77309
77355
  try {
77310
- for (const entry of import_fs108.default.readdirSync(rulesDir, { withFileTypes: true })) {
77356
+ for (const entry of import_fs109.default.readdirSync(rulesDir, { withFileTypes: true })) {
77311
77357
  if (entry.isDirectory()) {
77312
- count += countRulesInDir(import_path107.default.join(rulesDir, entry.name));
77358
+ count += countRulesInDir(import_path108.default.join(rulesDir, entry.name));
77313
77359
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
77314
77360
  count++;
77315
77361
  }
@@ -77320,46 +77366,46 @@ function countRulesInDir(rulesDir) {
77320
77366
  }
77321
77367
  function isSamePath(a, b) {
77322
77368
  try {
77323
- return import_path107.default.resolve(a) === import_path107.default.resolve(b);
77369
+ return import_path108.default.resolve(a) === import_path108.default.resolve(b);
77324
77370
  } catch {
77325
77371
  return false;
77326
77372
  }
77327
77373
  }
77328
77374
  function countConfigs(cwd) {
77329
- const homeDir2 = import_os88.default.homedir();
77330
- const claudeDir = import_path107.default.join(homeDir2, ".claude");
77375
+ const homeDir2 = import_os89.default.homedir();
77376
+ const claudeDir = import_path108.default.join(homeDir2, ".claude");
77331
77377
  let claudeMdCount = 0;
77332
77378
  let rulesCount = 0;
77333
77379
  let hooksCount = 0;
77334
77380
  const userMcpServers = /* @__PURE__ */ new Set();
77335
77381
  const projectMcpServers = /* @__PURE__ */ new Set();
77336
- if (import_fs108.default.existsSync(import_path107.default.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
77337
- rulesCount += countRulesInDir(import_path107.default.join(claudeDir, "rules"));
77338
- const userSettings = import_path107.default.join(claudeDir, "settings.json");
77382
+ if (import_fs109.default.existsSync(import_path108.default.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
77383
+ rulesCount += countRulesInDir(import_path108.default.join(claudeDir, "rules"));
77384
+ const userSettings = import_path108.default.join(claudeDir, "settings.json");
77339
77385
  for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
77340
77386
  hooksCount += countHooksInFile(userSettings);
77341
- const userClaudeJson = import_path107.default.join(homeDir2, ".claude.json");
77387
+ const userClaudeJson = import_path108.default.join(homeDir2, ".claude.json");
77342
77388
  for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
77343
77389
  for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
77344
77390
  userMcpServers.delete(name);
77345
77391
  }
77346
77392
  if (cwd) {
77347
- if (import_fs108.default.existsSync(import_path107.default.join(cwd, "CLAUDE.md"))) claudeMdCount++;
77348
- if (import_fs108.default.existsSync(import_path107.default.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
77349
- const projectClaudeDir = import_path107.default.join(cwd, ".claude");
77393
+ if (import_fs109.default.existsSync(import_path108.default.join(cwd, "CLAUDE.md"))) claudeMdCount++;
77394
+ if (import_fs109.default.existsSync(import_path108.default.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
77395
+ const projectClaudeDir = import_path108.default.join(cwd, ".claude");
77350
77396
  const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
77351
77397
  if (!overlapsUserScope) {
77352
- if (import_fs108.default.existsSync(import_path107.default.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
77353
- rulesCount += countRulesInDir(import_path107.default.join(projectClaudeDir, "rules"));
77354
- const projSettings = import_path107.default.join(projectClaudeDir, "settings.json");
77398
+ if (import_fs109.default.existsSync(import_path108.default.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
77399
+ rulesCount += countRulesInDir(import_path108.default.join(projectClaudeDir, "rules"));
77400
+ const projSettings = import_path108.default.join(projectClaudeDir, "settings.json");
77355
77401
  for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
77356
77402
  hooksCount += countHooksInFile(projSettings);
77357
77403
  }
77358
- if (import_fs108.default.existsSync(import_path107.default.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
77359
- const localSettings = import_path107.default.join(projectClaudeDir, "settings.local.json");
77404
+ if (import_fs109.default.existsSync(import_path108.default.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
77405
+ const localSettings = import_path108.default.join(projectClaudeDir, "settings.local.json");
77360
77406
  for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
77361
77407
  hooksCount += countHooksInFile(localSettings);
77362
- const mcpJsonServers = getMcpServerNames(import_path107.default.join(cwd, ".mcp.json"));
77408
+ const mcpJsonServers = getMcpServerNames(import_path108.default.join(cwd, ".mcp.json"));
77363
77409
  const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
77364
77410
  for (const name of disabledMcpJson) mcpJsonServers.delete(name);
77365
77411
  for (const name of mcpJsonServers) projectMcpServers.add(name);
@@ -77392,12 +77438,12 @@ function readActiveShieldsHud() {
77392
77438
  return shieldsCache.value;
77393
77439
  }
77394
77440
  try {
77395
- const shieldsPath = import_path107.default.join(import_os88.default.homedir(), ".node9", "shields.json");
77396
- if (!import_fs108.default.existsSync(shieldsPath)) {
77441
+ const shieldsPath = import_path108.default.join(import_os89.default.homedir(), ".node9", "shields.json");
77442
+ if (!import_fs109.default.existsSync(shieldsPath)) {
77397
77443
  shieldsCache = { value: [], ts: now };
77398
77444
  return [];
77399
77445
  }
77400
- const parsed = JSON.parse(import_fs108.default.readFileSync(shieldsPath, "utf-8"));
77446
+ const parsed = JSON.parse(import_fs109.default.readFileSync(shieldsPath, "utf-8"));
77401
77447
  if (!Array.isArray(parsed.active)) {
77402
77448
  shieldsCache = { value: [], ts: now };
77403
77449
  return [];
@@ -77499,17 +77545,17 @@ function renderContextLine(stdin) {
77499
77545
  async function main() {
77500
77546
  try {
77501
77547
  const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
77502
- if (import_fs108.default.existsSync(import_path107.default.join(import_os88.default.homedir(), ".node9", "hud-debug"))) {
77548
+ if (import_fs109.default.existsSync(import_path108.default.join(import_os89.default.homedir(), ".node9", "hud-debug"))) {
77503
77549
  try {
77504
- const logPath = import_path107.default.join(import_os88.default.homedir(), ".node9", "hud-debug.log");
77550
+ const logPath = import_path108.default.join(import_os89.default.homedir(), ".node9", "hud-debug.log");
77505
77551
  const MAX_LOG_SIZE = 10 * 1024 * 1024;
77506
77552
  let size = 0;
77507
77553
  try {
77508
- size = import_fs108.default.statSync(logPath).size;
77554
+ size = import_fs109.default.statSync(logPath).size;
77509
77555
  } catch {
77510
77556
  }
77511
77557
  if (size < MAX_LOG_SIZE) {
77512
- import_fs108.default.appendFileSync(
77558
+ import_fs109.default.appendFileSync(
77513
77559
  logPath,
77514
77560
  JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
77515
77561
  );
@@ -77530,11 +77576,11 @@ async function main() {
77530
77576
  try {
77531
77577
  const cwd = stdin.cwd ?? process.cwd();
77532
77578
  for (const configPath2 of [
77533
- import_path107.default.join(cwd, "node9.config.json"),
77534
- import_path107.default.join(import_os88.default.homedir(), ".node9", "config.json")
77579
+ import_path108.default.join(cwd, "node9.config.json"),
77580
+ import_path108.default.join(import_os89.default.homedir(), ".node9", "config.json")
77535
77581
  ]) {
77536
- if (!import_fs108.default.existsSync(configPath2)) continue;
77537
- const cfg = JSON.parse(import_fs108.default.readFileSync(configPath2, "utf-8"));
77582
+ if (!import_fs109.default.existsSync(configPath2)) continue;
77583
+ const cfg = JSON.parse(import_fs109.default.readFileSync(configPath2, "utf-8"));
77538
77584
  const hud = cfg.settings?.hud;
77539
77585
  if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
77540
77586
  }
@@ -77552,14 +77598,14 @@ async function main() {
77552
77598
  renderOffline();
77553
77599
  }
77554
77600
  }
77555
- var import_fs108, import_path107, import_os88, import_http7, RESET2, BOLD2, DIM, RED2, GREEN2, YELLOW2, BLUE, MAGENTA, CYAN2, WHITE, BAR_FILLED, BAR_EMPTY, BAR_WIDTH, shieldsCache, SHIELDS_CACHE_TTL_MS;
77601
+ var import_fs109, import_path108, import_os89, import_http7, RESET2, BOLD2, DIM, RED2, GREEN2, YELLOW2, BLUE, MAGENTA, CYAN2, WHITE, BAR_FILLED, BAR_EMPTY, BAR_WIDTH, shieldsCache, SHIELDS_CACHE_TTL_MS;
77556
77602
  var init_hud = __esm({
77557
77603
  "src/cli/hud.ts"() {
77558
77604
  "use strict";
77559
77605
  init_cjs_shims();
77560
- import_fs108 = __toESM(require("fs"));
77561
- import_path107 = __toESM(require("path"));
77562
- import_os88 = __toESM(require("os"));
77606
+ import_fs109 = __toESM(require("fs"));
77607
+ import_path108 = __toESM(require("path"));
77608
+ import_os89 = __toESM(require("os"));
77563
77609
  import_http7 = __toESM(require("http"));
77564
77610
  init_daemon();
77565
77611
  init_dist();
@@ -77624,9 +77670,9 @@ init_agent_wiring();
77624
77670
  init_credentials();
77625
77671
  init_daemon3();
77626
77672
  var import_chalk50 = __toESM(require("chalk"));
77627
- var import_fs109 = __toESM(require("fs"));
77628
- var import_path108 = __toESM(require("path"));
77629
- var import_os89 = __toESM(require("os"));
77673
+ var import_fs110 = __toESM(require("fs"));
77674
+ var import_path109 = __toESM(require("path"));
77675
+ var import_os90 = __toESM(require("os"));
77630
77676
  var import_prompts4 = require("@inquirer/prompts");
77631
77677
 
77632
77678
  // src/utils/duration.ts
@@ -78697,6 +78743,7 @@ init_audit();
78697
78743
  init_config();
78698
78744
  init_daemon();
78699
78745
  init_dist();
78746
+ init_shell_fetch();
78700
78747
 
78701
78748
  // src/exposure/source.ts
78702
78749
  init_cjs_shims();
@@ -78921,11 +78968,6 @@ function buildArgsField(rawInput) {
78921
78968
  return hashed;
78922
78969
  }
78923
78970
  }
78924
- function shellFetch(tool, rawInput) {
78925
- if (tool !== "Bash" && tool !== "bash") return null;
78926
- const cmd = rawInput && typeof rawInput === "object" && !Array.isArray(rawInput) ? rawInput.command : void 0;
78927
- return typeof cmd === "string" && cmd.length > 0 ? classifyShellExposure(cmd) : null;
78928
- }
78929
78971
  function registerLogCommand(program2) {
78930
78972
  program2.command("log", { hidden: true }).description("PostToolUse hook \u2014 records executed tool calls").argument("[data]", "JSON string of the tool call").option(
78931
78973
  "--agent <name>",
@@ -85566,6 +85608,373 @@ function registerAgentsCommand(program2) {
85566
85608
  // src/cli.ts
85567
85609
  init_scan();
85568
85610
 
85611
+ // src/cli/commands/forecast-cases.ts
85612
+ init_cjs_shims();
85613
+ var import_fs96 = __toESM(require("fs"));
85614
+ var import_os80 = __toESM(require("os"));
85615
+ var import_path95 = __toESM(require("path"));
85616
+ var import_readline5 = __toESM(require("readline"));
85617
+ init_dist();
85618
+ init_config();
85619
+ init_forecast();
85620
+ init_scan_judge();
85621
+ init_dlp();
85622
+ init_jail();
85623
+ init_registry2();
85624
+ init_version();
85625
+ init_sync();
85626
+ function materialize(t, env) {
85627
+ if (typeof t === "string")
85628
+ return t.replaceAll("{{HOME}}", env.home).replaceAll("{{CWD}}", env.cwd);
85629
+ if (t === null || typeof t !== "object") return t;
85630
+ if (Array.isArray(t)) return t.map((x) => materialize(x, env));
85631
+ const o = t;
85632
+ const keys = Object.keys(o);
85633
+ if (keys.length === 1 && keys[0] === "$join" && Array.isArray(o.$join))
85634
+ return materialize(o.$join.join(""), env);
85635
+ const out = {};
85636
+ for (const [k, v] of Object.entries(o)) out[k] = materialize(v, env);
85637
+ return out;
85638
+ }
85639
+ var PROFILE_NEEDS = {
85640
+ clean: null,
85641
+ "asb-sandbox": null,
85642
+ "git-guardrails": null,
85643
+ "jailed-agent": "user-jail-path",
85644
+ "user-jail": "user-jail-path",
85645
+ "org-jail": "managed-jail-path",
85646
+ canary: "decoy",
85647
+ "egress-lock": "egress-block",
85648
+ "egress-watch": "egress-gated",
85649
+ "exposure-org": "organisation-domain",
85650
+ "ask-before-dangerous-actions": "hold-action-checks",
85651
+ "injection-optin": "injection-scan"
85652
+ };
85653
+ function profileNeed(setup) {
85654
+ if (setup in PROFILE_NEEDS) return PROFILE_NEEDS[setup];
85655
+ if (setup.startsWith("egress-deny-")) return "egress-deny-list";
85656
+ return void 0;
85657
+ }
85658
+ function homeHas(config) {
85659
+ const settings = catalogSettingsFrom(config.settings, config.policy);
85660
+ const value = (id) => checkValue(settings, id);
85661
+ const egress = config.policy.egress;
85662
+ const gated = !!egress?.enabled && (egress.mode === "block" || egress.mode === "review");
85663
+ let userJail = false;
85664
+ try {
85665
+ userJail = readJailPaths().length > 0;
85666
+ } catch {
85667
+ userJail = false;
85668
+ }
85669
+ let decoy = false;
85670
+ try {
85671
+ decoy = loadCanaries().length > 0;
85672
+ } catch {
85673
+ decoy = false;
85674
+ }
85675
+ const held = (id) => value(id) === "review" || value(id) === "block";
85676
+ return {
85677
+ "user-jail-path": userJail,
85678
+ "managed-jail-path": (config.policy.managedJailPaths ?? []).length > 0,
85679
+ decoy,
85680
+ "egress-block": !!egress?.enabled && egress.mode === "block",
85681
+ "egress-gated": gated,
85682
+ "egress-deny-list": gated && (egress?.deny?.length ?? 0) > 0,
85683
+ "organisation-domain": (config.policy.untrustedContent?.orgDomains ?? []).length > 0,
85684
+ "hold-action-checks": held("actions.money") && held("actions.access") && held("actions.publish"),
85685
+ "injection-scan": value("behavior.prompt-injection") !== "off"
85686
+ };
85687
+ }
85688
+ var emptyCell = () => ({
85689
+ attacks: 0,
85690
+ stopped: 0,
85691
+ loggedOnly: 0,
85692
+ passed: 0,
85693
+ benign: 0,
85694
+ interrupted: 0,
85695
+ notApplicable: 0,
85696
+ notSimulated: 0,
85697
+ volatile: 0,
85698
+ undetermined: 0,
85699
+ errors: 0
85700
+ });
85701
+ function summarize(results, config) {
85702
+ const settings = catalogSettingsFrom(config.settings, config.policy);
85703
+ const byRisk = {};
85704
+ const passedByCheck = {};
85705
+ const passedUnattributed = {};
85706
+ const notApplicableBy = {};
85707
+ const cell = (r) => {
85708
+ const risk = byRisk[r.owasp] ??= {
85709
+ headline: emptyCell(),
85710
+ vendor: emptyCell(),
85711
+ unknown: emptyCell()
85712
+ };
85713
+ return risk[r.section];
85714
+ };
85715
+ for (const r of results) {
85716
+ const c = cell(r);
85717
+ if (r.error) {
85718
+ c.errors++;
85719
+ continue;
85720
+ }
85721
+ if (r.notApplicable) {
85722
+ c.notApplicable++;
85723
+ notApplicableBy[r.notApplicable] = (notApplicableBy[r.notApplicable] ?? 0) + 1;
85724
+ continue;
85725
+ }
85726
+ if (r.notSimulated) {
85727
+ c.notSimulated++;
85728
+ continue;
85729
+ }
85730
+ if (r.volatile) {
85731
+ c.volatile++;
85732
+ continue;
85733
+ }
85734
+ const stopped = r.enforced === true && (r.decision === "review" || r.decision === "block");
85735
+ const recorded = !stopped && r.decision !== void 0 && r.decision !== "allow";
85736
+ if (r.expected === "allow") {
85737
+ c.benign++;
85738
+ if (stopped) c.interrupted++;
85739
+ continue;
85740
+ }
85741
+ if (!stopped && (r.unknown?.length ?? 0) > 0) {
85742
+ c.undetermined++;
85743
+ continue;
85744
+ }
85745
+ c.attacks++;
85746
+ if (stopped) c.stopped++;
85747
+ else if (recorded) {
85748
+ c.loggedOnly++;
85749
+ if (r.checkId) {
85750
+ const e = passedByCheck[r.checkId] ??= {
85751
+ value: checkValue(settings, r.checkId),
85752
+ count: 0
85753
+ };
85754
+ e.count++;
85755
+ } else passedUnattributed[r.owasp] = (passedUnattributed[r.owasp] ?? 0) + 1;
85756
+ } else {
85757
+ c.passed++;
85758
+ passedUnattributed[r.owasp] = (passedUnattributed[r.owasp] ?? 0) + 1;
85759
+ }
85760
+ }
85761
+ return {
85762
+ engineVersion: PROXY_VERSION,
85763
+ mode: config.settings.mode ?? "standard",
85764
+ policySource: config.policySource,
85765
+ cases: results.length,
85766
+ byRisk,
85767
+ passedByCheck,
85768
+ passedUnattributed,
85769
+ notApplicableBy
85770
+ };
85771
+ }
85772
+ function toolOf(payload, fallback) {
85773
+ return typeof payload.tool_name === "string" && payload.tool_name ? payload.tool_name : fallback;
85774
+ }
85775
+ function notePostCall(judge, sessionId, tool, input, output, at, cwd) {
85776
+ const m = scanArgs(output);
85777
+ if (m) judge.noteOutputSecret(sessionId, m.patternName, at);
85778
+ judge.noteToolResult(sessionId, tool, at, cwd, {
85779
+ input,
85780
+ output: collectToolOutputText(output).text
85781
+ });
85782
+ }
85783
+ async function runCase(judge, rec, env, has2, at, online = false) {
85784
+ const base3 = {
85785
+ id: rec.id,
85786
+ owasp: isAsiId(rec.owasp) ? rec.owasp : "unknown",
85787
+ section: rec.section === "headline" || rec.section === "vendor" ? rec.section : "unknown",
85788
+ setup: rec.setup,
85789
+ expected: rec.expected,
85790
+ ...rec.volatile && { volatile: true }
85791
+ };
85792
+ const need = profileNeed(rec.setup);
85793
+ if (need && !has2[need]) return { ...base3, notApplicable: need };
85794
+ if (rec.stage === "post") return { ...base3, notSimulated: "post-stage" };
85795
+ const sessionId = `forecast-cases:${rec.id}`;
85796
+ const cwd = env.cwd;
85797
+ const config = judge.configFor(cwd);
85798
+ const meta = { agent: "claude", sessionId };
85799
+ for (const p of rec.prior ?? []) {
85800
+ const payload2 = materialize(p.input, env) ?? {};
85801
+ const tool = toolOf(payload2, rec.tool);
85802
+ if (p.stage === "post") {
85803
+ notePostCall(judge, sessionId, tool, payload2.tool_input, materialize(p.output, env), at, cwd);
85804
+ continue;
85805
+ }
85806
+ await forecastDecision(tool, payload2.tool_input ?? {}, meta, {
85807
+ cwd,
85808
+ config,
85809
+ session: judge.session(sessionId),
85810
+ at: Date.parse(at),
85811
+ ...online && { onlinePackages: true }
85812
+ });
85813
+ }
85814
+ const payload = materialize(rec.input, env) ?? {};
85815
+ const v = await forecastDecision(toolOf(payload, rec.tool), payload.tool_input ?? {}, meta, {
85816
+ cwd,
85817
+ config,
85818
+ session: judge.session(sessionId),
85819
+ at: Date.parse(at),
85820
+ ...online && { onlinePackages: true }
85821
+ });
85822
+ return {
85823
+ ...base3,
85824
+ decision: v.decision,
85825
+ enforced: v.enforced,
85826
+ ...v.checkId && { checkId: v.checkId },
85827
+ layer: v.layer,
85828
+ ...v.notEnforcedBecause && { notEnforcedBecause: v.notEnforcedBecause },
85829
+ ...v.unknown.length > 0 && { unknown: v.unknown }
85830
+ };
85831
+ }
85832
+ function materializeProfileFiles(profile, env) {
85833
+ for (const f of profile.files ?? []) {
85834
+ const target = materialize(f.path, env);
85835
+ const rel = import_path95.default.relative(env.home, target);
85836
+ if (rel.startsWith("..") || import_path95.default.isAbsolute(rel)) continue;
85837
+ import_fs96.default.mkdirSync(import_path95.default.dirname(target), { recursive: true });
85838
+ const content = materialize(f.content, env);
85839
+ import_fs96.default.writeFileSync(target, typeof content === "string" ? content : JSON.stringify(content));
85840
+ }
85841
+ }
85842
+ var POLICY_HOME_GUARDS = ["rules-cache.json", "credentials.json"];
85843
+ var PolicyHomeTaken = class extends Error {
85844
+ };
85845
+ async function runForecastCases(opts) {
85846
+ const env = {
85847
+ home: opts.home,
85848
+ cwd: opts.cwd ?? import_path95.default.join(opts.home, "bench-project")
85849
+ };
85850
+ import_fs96.default.mkdirSync(env.cwd, { recursive: true });
85851
+ if (opts.policy) {
85852
+ const taken = POLICY_HOME_GUARDS.filter(
85853
+ (f) => import_fs96.default.existsSync(import_path95.default.join(opts.home, ".node9", f))
85854
+ );
85855
+ if (taken.length > 0)
85856
+ throw new PolicyHomeTaken(
85857
+ `--policy needs a fresh HOME; ${opts.home} already has ${taken.map((f) => `.node9/${f}`).join(" and ")}`
85858
+ );
85859
+ writePolicyCache(opts.policy, opts.policy.etag);
85860
+ if (!process.env.NODE9_API_KEY) process.env.NODE9_API_KEY = "forecast-cases";
85861
+ }
85862
+ const judge = new HistoryJudge();
85863
+ const config = judge.configFor(env.cwd);
85864
+ const has2 = homeHas(config);
85865
+ const profiles = /* @__PURE__ */ new Map();
85866
+ const profileOf = (name) => {
85867
+ if (!opts.profilesDir) return null;
85868
+ if (profiles.has(name)) return profiles.get(name);
85869
+ let p = null;
85870
+ try {
85871
+ p = JSON.parse(
85872
+ import_fs96.default.readFileSync(import_path95.default.join(opts.profilesDir, `${name}.json`), "utf8")
85873
+ );
85874
+ materializeProfileFiles(p, env);
85875
+ } catch {
85876
+ p = null;
85877
+ }
85878
+ profiles.set(name, p);
85879
+ return p;
85880
+ };
85881
+ const results = [];
85882
+ const rl = import_readline5.default.createInterface({
85883
+ input: import_fs96.default.createReadStream(opts.casesPath, { encoding: "utf8" }),
85884
+ crlfDelay: Infinity
85885
+ });
85886
+ for await (const line of rl) {
85887
+ if (!line.trim()) continue;
85888
+ let rec;
85889
+ try {
85890
+ rec = JSON.parse(line);
85891
+ } catch {
85892
+ continue;
85893
+ }
85894
+ if (!rec || typeof rec.id !== "string") continue;
85895
+ profileOf(rec.setup);
85896
+ let result;
85897
+ try {
85898
+ result = await runCase(judge, rec, env, has2, opts.at, opts.onlinePackages === true);
85899
+ } catch (err2) {
85900
+ result = {
85901
+ id: rec.id,
85902
+ owasp: isAsiId(rec.owasp) ? rec.owasp : "unknown",
85903
+ section: rec.section === "headline" || rec.section === "vendor" ? rec.section : "unknown",
85904
+ setup: rec.setup,
85905
+ expected: rec.expected,
85906
+ error: err2 instanceof Error ? err2.message : String(err2)
85907
+ };
85908
+ }
85909
+ results.push(result);
85910
+ opts.write(JSON.stringify({ case: result }));
85911
+ }
85912
+ const summary = summarize(results, config);
85913
+ opts.write(JSON.stringify({ summary }));
85914
+ return summary;
85915
+ }
85916
+ function registerForecastCasesCommand(program2) {
85917
+ program2.command("forecast-cases <cases>", { hidden: true }).description(
85918
+ "Judge a file of benchmark cases through the forecast entry under this HOME; JSON per case, then a summary"
85919
+ ).option(
85920
+ "--profiles <dir>",
85921
+ "The benchmark profiles directory: its fixture files are created under HOME"
85922
+ ).option("--at <iso>", "The clock every case is judged at", "2026-01-01T00:00:00.000Z").option(
85923
+ "--online-packages",
85924
+ "Let the package check query the advisory service and the registries, as the hook does (default: offline)"
85925
+ ).option(
85926
+ "--cwd <dir>",
85927
+ "The project directory the cases are judged in ({{CWD}}); default HOME/bench-project"
85928
+ ).option(
85929
+ "--policy <file>",
85930
+ "A cloud policy sync response body (JSON), written as this HOME's rules cache first"
85931
+ ).action(
85932
+ async (cases, opts) => {
85933
+ const casesPath = import_path95.default.resolve(cases);
85934
+ if (!import_fs96.default.existsSync(casesPath)) {
85935
+ console.error(`forecast-cases: no such file: ${casesPath}`);
85936
+ process.exitCode = 2;
85937
+ return;
85938
+ }
85939
+ let policy;
85940
+ if (opts.policy) {
85941
+ const parsed = readPolicyBody(import_path95.default.resolve(opts.policy));
85942
+ if (!parsed) {
85943
+ console.error(`forecast-cases: --policy is not a JSON object: ${opts.policy}`);
85944
+ process.exitCode = 2;
85945
+ return;
85946
+ }
85947
+ policy = parsed;
85948
+ }
85949
+ try {
85950
+ await runForecastCases({
85951
+ casesPath,
85952
+ profilesDir: opts.profiles ? import_path95.default.resolve(opts.profiles) : void 0,
85953
+ home: import_os80.default.homedir(),
85954
+ cwd: opts.cwd ? import_path95.default.resolve(opts.cwd) : void 0,
85955
+ policy,
85956
+ onlinePackages: opts.onlinePackages === true,
85957
+ at: opts.at,
85958
+ write: (line) => process.stdout.write(line + "\n")
85959
+ });
85960
+ } catch (err2) {
85961
+ if (!(err2 instanceof PolicyHomeTaken)) throw err2;
85962
+ console.error(`forecast-cases: ${err2.message}`);
85963
+ process.exitCode = 2;
85964
+ }
85965
+ }
85966
+ );
85967
+ }
85968
+ function readPolicyBody(file2) {
85969
+ try {
85970
+ const v = JSON.parse(import_fs96.default.readFileSync(file2, "utf8"));
85971
+ if (!v || typeof v !== "object" || Array.isArray(v)) return void 0;
85972
+ return v;
85973
+ } catch {
85974
+ return void 0;
85975
+ }
85976
+ }
85977
+
85569
85978
  // src/cli/commands/posture.ts
85570
85979
  init_cjs_shims();
85571
85980
  var import_chalk35 = __toESM(require("chalk"));
@@ -85605,8 +86014,8 @@ init_cjs_shims();
85605
86014
 
85606
86015
  // src/ci-check/fetch.ts
85607
86016
  init_cjs_shims();
85608
- var import_fs96 = __toESM(require("fs"));
85609
- var import_path95 = __toESM(require("path"));
86017
+ var import_fs97 = __toESM(require("fs"));
86018
+ var import_path96 = __toESM(require("path"));
85610
86019
  var import_node_child_process = require("child_process");
85611
86020
  var import_undici = __toESM(require_undici());
85612
86021
 
@@ -85616,18 +86025,18 @@ init_instructions();
85616
86025
  init_suppress();
85617
86026
  var MCP_CONFIG_RE = /\.mcp\.json$|\.cursor\/mcp\.json$|(^|\/)\.vscode\/mcp\.json$|(^|\/)\.kiro\/settings\/mcp\.json$|(^|\/)\.gemini\/settings\.json$|(^|\/)opencode\.json$|(^|\/)\.opencode\/config\.json$/;
85618
86027
  var ISSUE_FORM_RE = /^\.github\/ISSUE_TEMPLATE\/[^/]+\.(ya?ml|md)$/i;
85619
- function routeOf(path113, skillDirs, appDirs = /* @__PURE__ */ new Set()) {
85620
- if (path113 === SUPPRESSIONS_FILE) return "suppressions";
85621
- if (/\.github\/workflows\/.+\.ya?ml$/.test(path113)) return "workflow";
85622
- if (ISSUE_FORM_RE.test(path113)) return "issue-form";
85623
- if (path113 === "package.json" || path113 === "pyproject.toml") return "manifest";
85624
- if (/\.claude\/settings(\.local)?\.json$/.test(path113)) return "agent-config";
85625
- if (isHookScript(path113)) return "hook-script";
85626
- if (isSkillScript(path113, skillDirs)) return "skill-script";
85627
- if (MCP_CONFIG_RE.test(path113)) return "mcp";
85628
- if (/(^|\/)\.codex\/config\.toml$/.test(path113)) return "codex";
85629
- if (isInstructionFile(path113, skillDirs, appDirs)) return "instruction";
85630
- if (isAppDoc(path113, skillDirs, appDirs)) return "app-doc";
86028
+ function routeOf(path114, skillDirs, appDirs = /* @__PURE__ */ new Set()) {
86029
+ if (path114 === SUPPRESSIONS_FILE) return "suppressions";
86030
+ if (/\.github\/workflows\/.+\.ya?ml$/.test(path114)) return "workflow";
86031
+ if (ISSUE_FORM_RE.test(path114)) return "issue-form";
86032
+ if (path114 === "package.json" || path114 === "pyproject.toml") return "manifest";
86033
+ if (/\.claude\/settings(\.local)?\.json$/.test(path114)) return "agent-config";
86034
+ if (isHookScript(path114)) return "hook-script";
86035
+ if (isSkillScript(path114, skillDirs)) return "skill-script";
86036
+ if (MCP_CONFIG_RE.test(path114)) return "mcp";
86037
+ if (/(^|\/)\.codex\/config\.toml$/.test(path114)) return "codex";
86038
+ if (isInstructionFile(path114, skillDirs, appDirs)) return "instruction";
86039
+ if (isAppDoc(path114, skillDirs, appDirs)) return "app-doc";
85631
86040
  return null;
85632
86041
  }
85633
86042
 
@@ -85757,7 +86166,7 @@ function parseRepoUrl(input) {
85757
86166
  function isLocalPath(input) {
85758
86167
  if (input.startsWith(".") || input.startsWith("/") || input.startsWith("~")) return true;
85759
86168
  try {
85760
- return import_fs96.default.existsSync(input) && import_fs96.default.statSync(input).isDirectory();
86169
+ return import_fs97.default.existsSync(input) && import_fs97.default.statSync(input).isDirectory();
85761
86170
  } catch {
85762
86171
  return false;
85763
86172
  }
@@ -86118,7 +86527,7 @@ function capNested(reads, cap, notes) {
86118
86527
  }
86119
86528
  var overLimit = (text) => Buffer.byteLength(text, "utf8") > MAX_FILE_BYTES5;
86120
86529
  var RAW_LIMIT = 2 * MAX_FILE_BYTES5;
86121
- var OPEN_READ = import_fs96.default.constants.O_RDONLY | (import_fs96.default.constants.O_NOFOLLOW ?? 0) | (import_fs96.default.constants.O_NONBLOCK ?? 0);
86530
+ var OPEN_READ = import_fs97.default.constants.O_RDONLY | (import_fs97.default.constants.O_NOFOLLOW ?? 0) | (import_fs97.default.constants.O_NONBLOCK ?? 0);
86122
86531
  var MAX_WALK_ENTRIES = 1e6;
86123
86532
  var direntKind = (e) => e.isSymbolicLink() ? "link" : e.isDirectory() ? "dir" : e.isFile() ? "file" : "other";
86124
86533
  function walkDisk(root, notes) {
@@ -86133,7 +86542,7 @@ function walkDisk(root, notes) {
86133
86542
  const relDir = stack.pop();
86134
86543
  let dirents;
86135
86544
  try {
86136
- dirents = import_fs96.default.readdirSync(import_path95.default.join(root, relDir), { withFileTypes: true });
86545
+ dirents = import_fs97.default.readdirSync(import_path96.default.join(root, relDir), { withFileTypes: true });
86137
86546
  } catch (e) {
86138
86547
  unlistable.push(`${relDir || "."} (${e?.code ?? "error"})`);
86139
86548
  continue;
@@ -86147,7 +86556,7 @@ function walkDisk(root, notes) {
86147
86556
  const k = direntKind(e);
86148
86557
  kinds2.set(rel, k);
86149
86558
  if (k !== "dir" || isUnwalked(`${rel}/`)) continue;
86150
- if (import_fs96.default.existsSync(import_path95.default.join(root, rel, ".git"))) {
86559
+ if (import_fs97.default.existsSync(import_path96.default.join(root, rel, ".git"))) {
86151
86560
  sealed.add(rel);
86152
86561
  nestedRepos.push(rel);
86153
86562
  } else stack.push(rel);
@@ -86170,7 +86579,7 @@ function walkDisk(root, notes) {
86170
86579
  }
86171
86580
  function gitmodulePaths(root) {
86172
86581
  try {
86173
- const text = readLocalFile(import_path95.default.join(root, ".gitmodules"), ".gitmodules");
86582
+ const text = readLocalFile(import_path96.default.join(root, ".gitmodules"), ".gitmodules");
86174
86583
  return [...text.matchAll(/^\s*path\s*=\s*(.+?)\s*$/gm)].map((m) => m[1].replace(/\/+$/, ""));
86175
86584
  } catch {
86176
86585
  return [];
@@ -86178,9 +86587,9 @@ function gitmodulePaths(root) {
86178
86587
  }
86179
86588
  function localLinkText(root, rel) {
86180
86589
  try {
86181
- const t = import_fs96.default.readlinkSync(import_path95.default.join(root, rel));
86590
+ const t = import_fs97.default.readlinkSync(import_path96.default.join(root, rel));
86182
86591
  if (process.platform !== "win32") return t;
86183
- return import_path95.default.win32.isAbsolute(t) ? `/${t.replace(/\\/g, "/")}` : t.replace(/\\/g, "/");
86592
+ return import_path96.default.win32.isAbsolute(t) ? `/${t.replace(/\\/g, "/")}` : t.replace(/\\/g, "/");
86184
86593
  } catch {
86185
86594
  return null;
86186
86595
  }
@@ -86188,11 +86597,11 @@ function localLinkText(root, rel) {
86188
86597
  function readLocalFile(abs, rel) {
86189
86598
  let fd;
86190
86599
  try {
86191
- fd = import_fs96.default.openSync(abs, OPEN_READ);
86192
- const st = import_fs96.default.fstatSync(fd);
86600
+ fd = import_fs97.default.openSync(abs, OPEN_READ);
86601
+ const st = import_fs97.default.fstatSync(fd);
86193
86602
  if (!st.isFile()) throw new Error(unreadNote(rel, "no longer a regular file"));
86194
86603
  if (st.size > RAW_LIMIT) throw new Error(tooLargeNote(rel));
86195
- const text = lf(import_fs96.default.readFileSync(fd, "utf8"));
86604
+ const text = lf(import_fs97.default.readFileSync(fd, "utf8"));
86196
86605
  if (overLimit(text)) throw new Error(tooLargeNote(rel));
86197
86606
  return text;
86198
86607
  } catch (e) {
@@ -86200,7 +86609,7 @@ function readLocalFile(abs, rel) {
86200
86609
  if (msg.includes(INCOMPLETE)) throw e;
86201
86610
  throw new Error(unreadNote(rel, e?.code ?? "error"));
86202
86611
  } finally {
86203
- if (fd !== void 0) import_fs96.default.closeSync(fd);
86612
+ if (fd !== void 0) import_fs97.default.closeSync(fd);
86204
86613
  }
86205
86614
  }
86206
86615
  function localGitHubRepo(root) {
@@ -86221,10 +86630,10 @@ function readLocalTree(dir2) {
86221
86630
  notes.push(...plan.notes);
86222
86631
  const files = [];
86223
86632
  for (const { rel, real, route } of plan.reads) {
86224
- const abs = import_path95.default.join(root, real);
86633
+ const abs = import_path96.default.join(root, real);
86225
86634
  let size = 0;
86226
86635
  try {
86227
- size = import_fs96.default.lstatSync(abs).size;
86636
+ size = import_fs97.default.lstatSync(abs).size;
86228
86637
  } catch {
86229
86638
  }
86230
86639
  if (size > RAW_LIMIT) {
@@ -87386,17 +87795,17 @@ function localTarget(uses, self) {
87386
87795
  if (!f) return null;
87387
87796
  return f[1].toLowerCase() === self.owner.toLowerCase() && f[2].toLowerCase() === self.repo.toLowerCase() ? `.github/workflows/${f[3]}` : null;
87388
87797
  }
87389
- function localCallsOf(path113, content, autoLabels, trustedRunSources, self) {
87798
+ function localCallsOf(path114, content, autoLabels, trustedRunSources, self) {
87390
87799
  AUTO_LABELS = autoLabels ?? /* @__PURE__ */ new Set();
87391
87800
  TRUSTED_RUN_SOURCES = trustedRunSources ?? /* @__PURE__ */ new Set();
87392
87801
  try {
87393
- return localCallsOfInner(path113, content, self);
87802
+ return localCallsOfInner(path114, content, self);
87394
87803
  } finally {
87395
87804
  AUTO_LABELS = /* @__PURE__ */ new Set();
87396
87805
  TRUSTED_RUN_SOURCES = /* @__PURE__ */ new Set();
87397
87806
  }
87398
87807
  }
87399
- function localCallsOfInner(path113, content, self) {
87808
+ function localCallsOfInner(path114, content, self) {
87400
87809
  let raw;
87401
87810
  try {
87402
87811
  raw = loadWorkflow(content).raw;
@@ -87414,7 +87823,7 @@ function localCallsOfInner(path113, content, self) {
87414
87823
  out.push({
87415
87824
  target,
87416
87825
  caller: {
87417
- file: path113,
87826
+ file: path114,
87418
87827
  keys: reach.keys,
87419
87828
  untrusted: reach.untrusted,
87420
87829
  secretExposed: reach.secretExposed,
@@ -87650,7 +88059,7 @@ function upstreamRunTrusted(wf, raw) {
87650
88059
  const names = Array.isArray(run?.workflows) ? run.workflows.map(String) : [];
87651
88060
  return names.length > 0 && names.every((n) => TRUSTED_RUN_SOURCES.has(n));
87652
88061
  }
87653
- function runSourceOf(path113, content, autoLabels) {
88062
+ function runSourceOf(path114, content, autoLabels) {
87654
88063
  AUTO_LABELS = autoLabels ?? /* @__PURE__ */ new Set();
87655
88064
  try {
87656
88065
  let raw;
@@ -87663,7 +88072,7 @@ function runSourceOf(path113, content, autoLabels) {
87663
88072
  const wf = raw;
87664
88073
  CALLERS.delete(wf);
87665
88074
  const reach = triggerReach(wf, raw);
87666
- const name = typeof raw["name"] === "string" && raw["name"] ? raw["name"] : path113;
88075
+ const name = typeof raw["name"] === "string" && raw["name"] ? raw["name"] : path114;
87667
88076
  return { name, trusted: !reach.untrusted && !reach.reusable };
87668
88077
  } finally {
87669
88078
  AUTO_LABELS = /* @__PURE__ */ new Set();
@@ -88133,18 +88542,18 @@ function severityFromScore(score) {
88133
88542
  if (score >= 1) return "advisory";
88134
88543
  return null;
88135
88544
  }
88136
- function analyzeWorkflow(path113, content, callers, autoLabels, trustedRunSources) {
88545
+ function analyzeWorkflow(path114, content, callers, autoLabels, trustedRunSources) {
88137
88546
  AUTO_LABELS = autoLabels ?? /* @__PURE__ */ new Set();
88138
88547
  TRUSTED_RUN_SOURCES = trustedRunSources ?? /* @__PURE__ */ new Set();
88139
88548
  try {
88140
- return analyzeWorkflowInner(path113, content, callers);
88549
+ return analyzeWorkflowInner(path114, content, callers);
88141
88550
  } finally {
88142
88551
  AUTO_LABELS = /* @__PURE__ */ new Set();
88143
88552
  TRUSTED_RUN_SOURCES = /* @__PURE__ */ new Set();
88144
88553
  UPSTREAM_TRUSTED = false;
88145
88554
  }
88146
88555
  }
88147
- function analyzeWorkflowInner(path113, content, callers) {
88556
+ function analyzeWorkflowInner(path114, content, callers) {
88148
88557
  let raw;
88149
88558
  try {
88150
88559
  raw = loadWorkflow(content).raw;
@@ -88353,7 +88762,7 @@ function analyzeWorkflowInner(path113, content, callers) {
88353
88762
  dimension: "workflows",
88354
88763
  severity,
88355
88764
  title,
88356
- file: path113,
88765
+ file: path114,
88357
88766
  signals,
88358
88767
  mitigations: mitigations.length ? mitigations : void 0,
88359
88768
  fix: head === "root" && privileged ? "Do not check out the untrusted PR head into the workspace root under a privileged trigger (pull_request_target/workflow_run): check out the base ref, or isolate the head in a subdir (--add-dir). Add an actor gate and scope the agent tools." : head === "root" ? "This runs under `pull_request` (fork PRs get a read-only token), so the head checkout is low-risk today: keep it on `pull_request` (not `pull_request_target`) and keep the actor gate + scoped tools." : "Add/verify an actor gate, scope the agent tools to read-only, and env-deny secrets. See Anthropic\u2019s claude-code-action security doc."
@@ -88457,18 +88866,18 @@ function evalAgentJob(job, wf, raw, untrustedTrigger, reusable) {
88457
88866
  if (reusable && !loadedGun && SEVERITY_RANK2[severity] > SEVERITY_RANK2.medium) severity = "medium";
88458
88867
  return { severity, secrets, injectable, canReadEnv };
88459
88868
  }
88460
- function analyzeWorkflowSecrets(path113, content, callers, autoLabels, trustedRunSources) {
88869
+ function analyzeWorkflowSecrets(path114, content, callers, autoLabels, trustedRunSources) {
88461
88870
  AUTO_LABELS = autoLabels ?? /* @__PURE__ */ new Set();
88462
88871
  TRUSTED_RUN_SOURCES = trustedRunSources ?? /* @__PURE__ */ new Set();
88463
88872
  try {
88464
- return analyzeWorkflowSecretsInner(path113, content, callers);
88873
+ return analyzeWorkflowSecretsInner(path114, content, callers);
88465
88874
  } finally {
88466
88875
  AUTO_LABELS = /* @__PURE__ */ new Set();
88467
88876
  TRUSTED_RUN_SOURCES = /* @__PURE__ */ new Set();
88468
88877
  UPSTREAM_TRUSTED = false;
88469
88878
  }
88470
88879
  }
88471
- function analyzeWorkflowSecretsInner(path113, content, callers) {
88880
+ function analyzeWorkflowSecretsInner(path114, content, callers) {
88472
88881
  let raw;
88473
88882
  try {
88474
88883
  raw = loadWorkflow(content).raw;
@@ -88495,7 +88904,7 @@ function analyzeWorkflowSecretsInner(path113, content, callers) {
88495
88904
  dimension: "data",
88496
88905
  severity: worst.severity,
88497
88906
  title: worst.severity === "advisory" ? "Secrets reachable by the agent: hardening" : "Exfiltratable secrets reachable by an injectable agent",
88498
- file: path113,
88907
+ file: path114,
88499
88908
  signals: [
88500
88909
  `agent can reach: ${worst.secrets.map((s) => s.name).join(", ")}`,
88501
88910
  worst.injectable ? "the agent is externally triggerable (untrusted trigger, no gate)" : "gated / not externally triggerable: latent risk only",
@@ -88517,7 +88926,7 @@ function marketplaceOwner(market) {
88517
88926
  market
88518
88927
  )?.[1] ?? /^([\w.-]{1,100})\/[\w.-]{1,100}(?:[#@].*)?$/.exec(market)?.[1];
88519
88928
  }
88520
- function analyzeWorkflowPlugins(path113, content, owner) {
88929
+ function analyzeWorkflowPlugins(path114, content, owner) {
88521
88930
  let raw;
88522
88931
  try {
88523
88932
  raw = loadWorkflow(content).raw;
@@ -88547,7 +88956,7 @@ function analyzeWorkflowPlugins(path113, content, owner) {
88547
88956
  dimension: "mcp",
88548
88957
  severity: "medium",
88549
88958
  title: "Agent installs plugins from a third-party marketplace on every run",
88550
- file: path113,
88959
+ file: path114,
88551
88960
  signals: [
88552
88961
  `\`plugin_marketplaces: ${safeText(market, 120)}\` is not pinned to a commit: whoever controls it changes the code and prompts the agent runs`
88553
88962
  ],
@@ -88563,11 +88972,11 @@ function readsItemList(text) {
88563
88972
  if (text.length > 2e5) return ITEM_LIST_RE.test(text);
88564
88973
  return text.replace(/\\\r?\n/g, " ").split("\n").some((l) => ITEM_LIST_RE.test(l) && !sanitizedListing(l));
88565
88974
  }
88566
- function analyzeTrustedReaders(path113, content, autoLabels, callers, trustedRunSources) {
88975
+ function analyzeTrustedReaders(path114, content, autoLabels, callers, trustedRunSources) {
88567
88976
  AUTO_LABELS = autoLabels ?? /* @__PURE__ */ new Set();
88568
88977
  TRUSTED_RUN_SOURCES = trustedRunSources ?? /* @__PURE__ */ new Set();
88569
88978
  try {
88570
- return analyzeTrustedReadersInner(path113, content, callers);
88979
+ return analyzeTrustedReadersInner(path114, content, callers);
88571
88980
  } finally {
88572
88981
  AUTO_LABELS = /* @__PURE__ */ new Set();
88573
88982
  TRUSTED_RUN_SOURCES = /* @__PURE__ */ new Set();
@@ -88576,7 +88985,7 @@ function analyzeTrustedReaders(path113, content, autoLabels, callers, trustedRun
88576
88985
  function keysWithCallers(keys, callers) {
88577
88986
  return [.../* @__PURE__ */ new Set([...keys, ...(callers ?? []).flatMap((c) => c.keys)])];
88578
88987
  }
88579
- function analyzeTrustedReadersInner(path113, content, callers) {
88988
+ function analyzeTrustedReadersInner(path114, content, callers) {
88580
88989
  let raw;
88581
88990
  try {
88582
88991
  raw = loadWorkflow(content).raw;
@@ -88637,7 +89046,7 @@ function analyzeTrustedReadersInner(path113, content, callers) {
88637
89046
  dimension: "workflows",
88638
89047
  severity: "advisory",
88639
89048
  title: "Agent on a trusted trigger reads content strangers write, with a write token",
88640
- file: path113,
89049
+ file: path114,
88641
89050
  signals: [
88642
89051
  `runs on ${keys.map((k) => safeText(k, 40)).join(", ")}: no stranger starts it`,
88643
89052
  ...reads.map((r) => `the agent ${r}`),
@@ -88725,17 +89134,17 @@ function requiresTrustedAuthor(src) {
88725
89134
  };
88726
89135
  return req(x);
88727
89136
  }
88728
- function analyzeForkCodeOnTrust(path113, content, callers, autoLabels, trustedRunSources) {
89137
+ function analyzeForkCodeOnTrust(path114, content, callers, autoLabels, trustedRunSources) {
88729
89138
  AUTO_LABELS = autoLabels ?? /* @__PURE__ */ new Set();
88730
89139
  TRUSTED_RUN_SOURCES = trustedRunSources ?? /* @__PURE__ */ new Set();
88731
89140
  try {
88732
- return analyzeForkCodeOnTrustInner(path113, content, callers);
89141
+ return analyzeForkCodeOnTrustInner(path114, content, callers);
88733
89142
  } finally {
88734
89143
  AUTO_LABELS = /* @__PURE__ */ new Set();
88735
89144
  TRUSTED_RUN_SOURCES = /* @__PURE__ */ new Set();
88736
89145
  }
88737
89146
  }
88738
- function analyzeForkCodeOnTrustInner(path113, content, callers) {
89147
+ function analyzeForkCodeOnTrustInner(path114, content, callers) {
88739
89148
  let raw;
88740
89149
  try {
88741
89150
  raw = loadWorkflow(content).raw;
@@ -88774,7 +89183,7 @@ function analyzeForkCodeOnTrustInner(path113, content, callers) {
88774
89183
  dimension: "workflows",
88775
89184
  severity: "advisory",
88776
89185
  title: "A maintainer's action runs the agent inside the fork's code",
88777
- file: path113,
89186
+ file: path114,
88778
89187
  signals: [
88779
89188
  `triggered by ${forkKeys.map((k) => safeText(k, 40)).join(", ")}, behind a gate a maintainer passes (a comment, a label, a push to the PR branch, a reopen)`,
88780
89189
  "checks out the untrusted PR head into the workspace root",
@@ -88806,18 +89215,18 @@ function laterSecrets(wf, job, from) {
88806
89215
  if (!/^GITHUB_TOKEN$/i.test(m[1])) out.add(m[1]);
88807
89216
  return [...out];
88808
89217
  }
88809
- function analyzeAgentOutputToEnv(path113, content, callers, autoLabels, trustedRunSources) {
89218
+ function analyzeAgentOutputToEnv(path114, content, callers, autoLabels, trustedRunSources) {
88810
89219
  AUTO_LABELS = autoLabels ?? /* @__PURE__ */ new Set();
88811
89220
  TRUSTED_RUN_SOURCES = trustedRunSources ?? /* @__PURE__ */ new Set();
88812
89221
  try {
88813
- return analyzeAgentOutputToEnvInner(path113, content, callers);
89222
+ return analyzeAgentOutputToEnvInner(path114, content, callers);
88814
89223
  } finally {
88815
89224
  AUTO_LABELS = /* @__PURE__ */ new Set();
88816
89225
  TRUSTED_RUN_SOURCES = /* @__PURE__ */ new Set();
88817
89226
  UPSTREAM_TRUSTED = false;
88818
89227
  }
88819
89228
  }
88820
- function analyzeAgentOutputToEnvInner(path113, content, callers) {
89229
+ function analyzeAgentOutputToEnvInner(path114, content, callers) {
88821
89230
  let raw;
88822
89231
  try {
88823
89232
  raw = loadWorkflow(content).raw;
@@ -88913,7 +89322,7 @@ function analyzeAgentOutputToEnvInner(path113, content, callers) {
88913
89322
  dimension: "workflows",
88914
89323
  severity,
88915
89324
  title: sink.target === "GITHUB_PATH" ? "The agent's output is added to the job's PATH" : "The agent's output is written into the job's environment",
88916
- file: path113,
89325
+ file: path114,
88917
89326
  signals,
88918
89327
  fix: `Write the agent's result to a file and read it with a strict parser in the next step (a fixed set of values, or a number); never append free text from the agent to \`${target}\`. If a value must go there, strip newlines (\`tr -d '\\n'\`) and write it under a fixed name, or use a delimiter generated at run time.`,
88919
89328
  explain: explainAgentOutputEnv({
@@ -88991,7 +89400,7 @@ function reviewQuestion(rule) {
88991
89400
  return "Is this an instruction the agent follows, or text for a person?";
88992
89401
  }
88993
89402
  var RANK2 = { critical: 4, high: 3, medium: 2, advisory: 1 };
88994
- var worstRank = (fs114) => Math.max(...fs114.map((f) => RANK2[f.severity] ?? 0));
89403
+ var worstRank = (fs115) => Math.max(...fs115.map((f) => RANK2[f.severity] ?? 0));
88995
89404
  var fileOrder = (a, b) => worstRank(b[1]) - worstRank(a[1]) || b[1].length - a[1].length;
88996
89405
  function groupReview(findings) {
88997
89406
  const byFile = /* @__PURE__ */ new Map();
@@ -89062,7 +89471,7 @@ function logicalLines2(content) {
89062
89471
  }
89063
89472
  return out;
89064
89473
  }
89065
- function analyzeScript(path113, content, rulePrefix) {
89474
+ function analyzeScript(path114, content, rulePrefix) {
89066
89475
  const check = rulePrefix.startsWith("CI-1") ? "CI-1" : "CI-6";
89067
89476
  const dimension = check === "CI-1" ? "toolRules" : "instructions";
89068
89477
  const mk3 = (suffix, severity, title, signal, fix, line) => ({
@@ -89071,7 +89480,7 @@ function analyzeScript(path113, content, rulePrefix) {
89071
89480
  dimension,
89072
89481
  severity,
89073
89482
  title,
89074
- file: path113,
89483
+ file: path114,
89075
89484
  ...line ? { line } : {},
89076
89485
  signals: [signal],
89077
89486
  fix
@@ -89501,7 +89910,7 @@ async function scanRepo(input, onProgress) {
89501
89910
  init_cjs_shims();
89502
89911
  var import_chalk36 = __toESM(require("chalk"));
89503
89912
  init_suppress();
89504
- var listed = (fs114) => fs114.filter((f) => f.tier !== "review");
89913
+ var listed = (fs115) => fs115.filter((f) => f.tier !== "review");
89505
89914
  function excerpt(f) {
89506
89915
  const m = /`([^`]+)`/.exec(f.signals[0] ?? "");
89507
89916
  return safeText(m ? m[1] : f.title, 120).replace(/`/g, "'");
@@ -89517,14 +89926,14 @@ function reviewMd(findings) {
89517
89926
  L.push("");
89518
89927
  const files = [...byFile.keys()];
89519
89928
  for (const file2 of files.slice(0, REVIEW_FILES)) {
89520
- const fs114 = byFile.get(file2);
89929
+ const fs115 = byFile.get(file2);
89521
89930
  L.push(`- \`${safeText(file2, 300)}\``);
89522
- for (const f of fs114.slice(0, REVIEW_LINES_PER_FILE))
89931
+ for (const f of fs115.slice(0, REVIEW_LINES_PER_FILE))
89523
89932
  L.push(
89524
89933
  ` - ${f.line ? `line ${Number(f.line)}: ` : ""}\`${excerpt(f)}\`. Check: ${reviewQuestion(f.rule)}`
89525
89934
  );
89526
- if (fs114.length > REVIEW_LINES_PER_FILE)
89527
- L.push(` - and ${fs114.length - REVIEW_LINES_PER_FILE} similar lines in this file`);
89935
+ if (fs115.length > REVIEW_LINES_PER_FILE)
89936
+ L.push(` - and ${fs115.length - REVIEW_LINES_PER_FILE} similar lines in this file`);
89528
89937
  }
89529
89938
  const more = files.length - REVIEW_FILES;
89530
89939
  if (more > 0) L.push(`- and ${more} more file${more === 1 ? "" : "s"}`);
@@ -89540,7 +89949,7 @@ function reviewMd(findings) {
89540
89949
  }
89541
89950
  function reviewTerminal(findings) {
89542
89951
  const { byFile, grants } = groupReview(findings);
89543
- const items = [...byFile.values()].reduce((a, fs114) => a + fs114.length, 0);
89952
+ const items = [...byFile.values()].reduce((a, fs115) => a + fs115.length, 0);
89544
89953
  if (!items && !grants) return [];
89545
89954
  const L = [];
89546
89955
  if (items) {
@@ -89549,12 +89958,12 @@ function reviewTerminal(findings) {
89549
89958
  );
89550
89959
  const files = [...byFile.keys()];
89551
89960
  for (const file2 of files.slice(0, REVIEW_FILES)) {
89552
- const fs114 = byFile.get(file2);
89961
+ const fs115 = byFile.get(file2);
89553
89962
  L.push(import_chalk36.default.gray(` ${file2}`));
89554
- for (const f of fs114.slice(0, REVIEW_LINES_PER_FILE))
89963
+ for (const f of fs115.slice(0, REVIEW_LINES_PER_FILE))
89555
89964
  L.push(` \u2022 ${f.line ? `line ${f.line}: ` : ""}${excerpt(f)}`);
89556
- if (fs114.length > REVIEW_LINES_PER_FILE)
89557
- L.push(import_chalk36.default.gray(` and ${fs114.length - REVIEW_LINES_PER_FILE} similar lines`));
89965
+ if (fs115.length > REVIEW_LINES_PER_FILE)
89966
+ L.push(import_chalk36.default.gray(` and ${fs115.length - REVIEW_LINES_PER_FILE} similar lines`));
89558
89967
  }
89559
89968
  const more = files.length - REVIEW_FILES;
89560
89969
  if (more > 0) L.push(import_chalk36.default.gray(` and ${more} more file${more === 1 ? "" : "s"}`));
@@ -90208,16 +90617,16 @@ var import_chalk40 = __toESM(require("chalk"));
90208
90617
 
90209
90618
  // src/canary/plant.ts
90210
90619
  init_cjs_shims();
90211
- var import_fs98 = __toESM(require("fs"));
90212
- var import_os80 = __toESM(require("os"));
90213
- var import_path97 = __toESM(require("path"));
90620
+ var import_fs99 = __toESM(require("fs"));
90621
+ var import_os81 = __toESM(require("os"));
90622
+ var import_path98 = __toESM(require("path"));
90214
90623
  init_jail();
90215
90624
  init_registry2();
90216
90625
 
90217
90626
  // src/canary/sites.ts
90218
90627
  init_cjs_shims();
90219
- var import_fs97 = __toESM(require("fs"));
90220
- var import_path96 = __toESM(require("path"));
90628
+ var import_fs98 = __toESM(require("fs"));
90629
+ var import_path97 = __toESM(require("path"));
90221
90630
 
90222
90631
  // src/canary/generate.ts
90223
90632
  init_cjs_shims();
@@ -90312,26 +90721,26 @@ function generateSshKey() {
90312
90721
  var SITES = {
90313
90722
  "aws-profile": {
90314
90723
  kind: "aws-profile",
90315
- primary: (h) => import_path96.default.join(h, ".aws", "credentials"),
90316
- fallback: (h) => import_path96.default.join(h, ".aws", "credentials.bak"),
90724
+ primary: (h) => import_path97.default.join(h, ".aws", "credentials"),
90725
+ fallback: (h) => import_path97.default.join(h, ".aws", "credentials.bak"),
90317
90726
  // SSO / profile-only / credential_process setups have ~/.aws/config and no
90318
90727
  // credentials file; the SDK and CLI then parse ~/.aws/credentials on every
90319
90728
  // profile lookup. A planted primary would be a file real tools read.
90320
- preferFallback: (h) => import_fs97.default.existsSync(import_path96.default.join(h, ".aws", "config")),
90729
+ preferFallback: (h) => import_fs98.default.existsSync(import_path97.default.join(h, ".aws", "config")),
90321
90730
  dirMode: 448,
90322
90731
  generate: generateAwsProfile
90323
90732
  },
90324
90733
  "env-file": {
90325
90734
  kind: "env-file",
90326
- primary: (h) => import_path96.default.join(h, ".env.bak"),
90327
- fallback: (h) => import_path96.default.join(h, ".env.local.bak"),
90735
+ primary: (h) => import_path97.default.join(h, ".env.bak"),
90736
+ fallback: (h) => import_path97.default.join(h, ".env.local.bak"),
90328
90737
  dirMode: 493,
90329
90738
  generate: generateEnvFile
90330
90739
  },
90331
90740
  "ssh-key": {
90332
90741
  kind: "ssh-key",
90333
- primary: (h) => import_path96.default.join(h, ".ssh", "id_rsa_backup"),
90334
- fallback: (h) => import_path96.default.join(h, ".ssh", "id_rsa.old"),
90742
+ primary: (h) => import_path97.default.join(h, ".ssh", "id_rsa_backup"),
90743
+ fallback: (h) => import_path97.default.join(h, ".ssh", "id_rsa.old"),
90335
90744
  // ssh refuses a group-writable ~/.ssh for config / known_hosts reads.
90336
90745
  dirMode: 448,
90337
90746
  generate: generateSshKey
@@ -90344,12 +90753,12 @@ var isKind = (s) => ALL_KINDS.includes(s);
90344
90753
  var liveRecords = (kind) => loadCanaries().filter((r) => r.kind === kind && !r.retiredAt);
90345
90754
  var fileHashOf = (p) => {
90346
90755
  try {
90347
- return sha256Hex(import_fs98.default.readFileSync(p));
90756
+ return sha256Hex(import_fs99.default.readFileSync(p));
90348
90757
  } catch {
90349
90758
  return null;
90350
90759
  }
90351
90760
  };
90352
- function plantKind(kind, home = import_os80.default.homedir()) {
90761
+ function plantKind(kind, home = import_os81.default.homedir()) {
90353
90762
  const site = SITES[kind];
90354
90763
  const live = liveRecords(kind);
90355
90764
  if (live.length > 0) {
@@ -90368,8 +90777,8 @@ function plantKind(kind, home = import_os80.default.homedir()) {
90368
90777
  const primary = site.primary(home);
90369
90778
  const fallback = site.fallback(home);
90370
90779
  let target;
90371
- if (!import_fs98.default.existsSync(primary) && !site.preferFallback?.(home)) target = primary;
90372
- else if (!import_fs98.default.existsSync(fallback)) target = fallback;
90780
+ if (!import_fs99.default.existsSync(primary) && !site.preferFallback?.(home)) target = primary;
90781
+ else if (!import_fs99.default.existsSync(fallback)) target = fallback;
90373
90782
  else {
90374
90783
  return {
90375
90784
  kind,
@@ -90380,11 +90789,11 @@ function plantKind(kind, home = import_os80.default.homedir()) {
90380
90789
  recordIds: []
90381
90790
  };
90382
90791
  }
90383
- const dir2 = import_path97.default.dirname(target);
90792
+ const dir2 = import_path98.default.dirname(target);
90384
90793
  const gen = site.generate();
90385
- const createdDir = import_fs98.default.mkdirSync(dir2, { recursive: true, mode: site.dirMode }) !== void 0;
90386
- import_fs98.default.writeFileSync(target, gen.text, { mode: 384, flag: "wx" });
90387
- import_fs98.default.chmodSync(target, 384);
90794
+ const createdDir = import_fs99.default.mkdirSync(dir2, { recursive: true, mode: site.dirMode }) !== void 0;
90795
+ import_fs99.default.writeFileSync(target, gen.text, { mode: 384, flag: "wx" });
90796
+ import_fs99.default.chmodSync(target, 384);
90388
90797
  const fileHash = sha256Hex(gen.text);
90389
90798
  regenerateUserJail(addJailPath(target, "block"));
90390
90799
  const recordIds = gen.values.map(
@@ -90402,14 +90811,14 @@ function plantKind(kind, home = import_os80.default.homedir()) {
90402
90811
  }
90403
90812
  function allowedPathsFor(kind, home) {
90404
90813
  const site = SITES[kind];
90405
- return [site.primary(home), site.fallback(home)].map((x) => import_path97.default.resolve(x));
90814
+ return [site.primary(home), site.fallback(home)].map((x) => import_path98.default.resolve(x));
90406
90815
  }
90407
- function removeKind(kind, home = import_os80.default.homedir()) {
90816
+ function removeKind(kind, home = import_os81.default.homedir()) {
90408
90817
  const live = liveRecords(kind);
90409
90818
  if (live.length === 0) return { kind, action: "absent" };
90410
90819
  const p = live[0].path;
90411
90820
  const { fileHash, createdDir } = live[0];
90412
- if (!allowedPathsFor(kind, home).includes(import_path97.default.resolve(p))) {
90821
+ if (!allowedPathsFor(kind, home).includes(import_path98.default.resolve(p))) {
90413
90822
  return {
90414
90823
  kind,
90415
90824
  action: "refused",
@@ -90417,7 +90826,7 @@ function removeKind(kind, home = import_os80.default.homedir()) {
90417
90826
  reason: `${p} is not a path node9 plants a ${kind} decoy at; refusing to delete it. If you believe this is a decoy, remove it by hand.`
90418
90827
  };
90419
90828
  }
90420
- if (!import_fs98.default.existsSync(p)) {
90829
+ if (!import_fs99.default.existsSync(p)) {
90421
90830
  for (const r of live) retireCanary(r.id);
90422
90831
  unjail(p);
90423
90832
  return { kind, action: "already-gone", path: p };
@@ -90430,13 +90839,13 @@ function removeKind(kind, home = import_os80.default.homedir()) {
90430
90839
  reason: `${p} changed since node9 wrote it; inspect and remove it by hand`
90431
90840
  };
90432
90841
  }
90433
- import_fs98.default.unlinkSync(p);
90842
+ import_fs99.default.unlinkSync(p);
90434
90843
  let dirRemoved = false;
90435
90844
  if (createdDir) {
90436
- const dir2 = import_path97.default.dirname(p);
90845
+ const dir2 = import_path98.default.dirname(p);
90437
90846
  try {
90438
- if (import_fs98.default.readdirSync(dir2).length === 0) {
90439
- import_fs98.default.rmdirSync(dir2);
90847
+ if (import_fs99.default.readdirSync(dir2).length === 0) {
90848
+ import_fs99.default.rmdirSync(dir2);
90440
90849
  dirRemoved = true;
90441
90850
  }
90442
90851
  } catch {
@@ -90460,14 +90869,14 @@ function statusAll() {
90460
90869
  const p = live[0].path;
90461
90870
  return {
90462
90871
  kind,
90463
- state: import_fs98.default.existsSync(p) ? "planted" : "missing",
90872
+ state: import_fs99.default.existsSync(p) ? "planted" : "missing",
90464
90873
  path: p,
90465
90874
  records: live.length,
90466
90875
  plantedAt: live[0].plantedAt
90467
90876
  };
90468
90877
  });
90469
90878
  }
90470
- function rotateKind(kind, home = import_os80.default.homedir()) {
90879
+ function rotateKind(kind, home = import_os81.default.homedir()) {
90471
90880
  const removed = removeKind(kind);
90472
90881
  if (removed.action === "refused") return { removed };
90473
90882
  return { removed, planted: plantKind(kind, home) };
@@ -90556,15 +90965,15 @@ init_daemon3();
90556
90965
  // src/cli/commands/sandbox.ts
90557
90966
  init_cjs_shims();
90558
90967
  var import_chalk41 = __toESM(require("chalk"));
90559
- var import_fs101 = __toESM(require("fs"));
90560
- var import_path100 = __toESM(require("path"));
90968
+ var import_fs102 = __toESM(require("fs"));
90969
+ var import_path101 = __toESM(require("path"));
90561
90970
  var import_child_process16 = require("child_process");
90562
90971
  init_config();
90563
90972
 
90564
90973
  // src/sandbox/config.ts
90565
90974
  init_cjs_shims();
90566
- var import_fs99 = __toESM(require("fs"));
90567
- var import_path98 = __toESM(require("path"));
90975
+ var import_fs100 = __toESM(require("fs"));
90976
+ var import_path99 = __toESM(require("path"));
90568
90977
  var import_yaml2 = require("yaml");
90569
90978
  var SANDBOX_CONFIG_FILE = "node9.sandbox.yaml";
90570
90979
  var FORBIDDEN_ENV = /* @__PURE__ */ new Set(["NODE9_API_KEY", "NODE9_API_URL"]);
@@ -90637,16 +91046,16 @@ function scaffoldSandboxYaml(agent) {
90637
91046
  return header + (0, import_yaml2.stringify)(defaultSandboxConfig(agent));
90638
91047
  }
90639
91048
  function sandboxConfigPath(cwd = process.cwd()) {
90640
- return import_path98.default.join(cwd, SANDBOX_CONFIG_FILE);
91049
+ return import_path99.default.join(cwd, SANDBOX_CONFIG_FILE);
90641
91050
  }
90642
91051
  function loadSandboxConfig(cwd = process.cwd(), fallbackAgent = "claude") {
90643
91052
  const p = sandboxConfigPath(cwd);
90644
- if (!import_fs99.default.existsSync(p)) {
91053
+ if (!import_fs100.default.existsSync(p)) {
90645
91054
  throw new Error(`sandbox: ${SANDBOX_CONFIG_FILE} not found \u2014 run \`node9 sandbox new\` first.`);
90646
91055
  }
90647
91056
  let raw;
90648
91057
  try {
90649
- raw = (0, import_yaml2.parse)(import_fs99.default.readFileSync(p, "utf-8"));
91058
+ raw = (0, import_yaml2.parse)(import_fs100.default.readFileSync(p, "utf-8"));
90650
91059
  } catch (err2) {
90651
91060
  throw new Error(
90652
91061
  `sandbox: ${SANDBOX_CONFIG_FILE} is not valid YAML \u2014 ${err2.message}`
@@ -90707,14 +91116,14 @@ init_atomic_write();
90707
91116
 
90708
91117
  // src/sandbox/runtime.ts
90709
91118
  init_cjs_shims();
90710
- var import_fs100 = __toESM(require("fs"));
90711
- var import_os81 = __toESM(require("os"));
90712
- var import_path99 = __toESM(require("path"));
91119
+ var import_fs101 = __toESM(require("fs"));
91120
+ var import_os82 = __toESM(require("os"));
91121
+ var import_path100 = __toESM(require("path"));
90713
91122
  var import_crypto27 = __toESM(require("crypto"));
90714
91123
  var import_child_process15 = require("child_process");
90715
91124
  init_templates();
90716
91125
  function sandboxDataDir(cwd = process.cwd()) {
90717
- return import_path99.default.join(cwd, ".node9", "sandbox", "data");
91126
+ return import_path100.default.join(cwd, ".node9", "sandbox", "data");
90718
91127
  }
90719
91128
  function detectEngine(engine) {
90720
91129
  const r = (0, import_child_process15.spawnSync)(engine, ["--version"], { encoding: "utf-8" });
@@ -90725,7 +91134,7 @@ function detectEngine(engine) {
90725
91134
  }
90726
91135
  function agentCredentialsMount(agent) {
90727
91136
  const rel = agent === "codex" ? ".codex/auth.json" : ".claude/.credentials.json";
90728
- return { hostPath: import_path99.default.join(import_os81.default.homedir(), rel), target: `/home/${RUN_AS_USER}/${rel}` };
91137
+ return { hostPath: import_path100.default.join(import_os82.default.homedir(), rel), target: `/home/${RUN_AS_USER}/${rel}` };
90729
91138
  }
90730
91139
  function buildRunArgs(opts) {
90731
91140
  const { config, workspaceHostPath, dataHostPath, allowlistHostPath, agentArgs } = opts;
@@ -90735,7 +91144,7 @@ function buildRunArgs(opts) {
90735
91144
  args.push("-v", `${allowlistHostPath}:${ALLOWED_DOMAINS_PATH}:ro`);
90736
91145
  if (config.node9.mountAgentCredentials) {
90737
91146
  const creds = agentCredentialsMount(config.agent);
90738
- if (import_fs100.default.existsSync(creds.hostPath)) {
91147
+ if (import_fs101.default.existsSync(creds.hostPath)) {
90739
91148
  args.push("-v", `${creds.hostPath}:${creds.target}`);
90740
91149
  }
90741
91150
  }
@@ -90753,30 +91162,30 @@ function imageContentHash(dockerfile, entrypoint) {
90753
91162
  return import_crypto27.default.createHash("sha256").update(dockerfile).update("\0").update(entrypoint).digest("hex").slice(0, 16);
90754
91163
  }
90755
91164
  function sandboxBuildDir(cwd = process.cwd()) {
90756
- return import_path99.default.join(cwd, ".node9", "sandbox", "build");
91165
+ return import_path100.default.join(cwd, ".node9", "sandbox", "build");
90757
91166
  }
90758
91167
  function writeBuildContext(cwd, dockerfile, entrypoint) {
90759
91168
  const dir2 = sandboxBuildDir(cwd);
90760
- import_fs100.default.mkdirSync(dir2, { recursive: true });
90761
- import_fs100.default.writeFileSync(import_path99.default.join(dir2, "Dockerfile"), dockerfile);
90762
- import_fs100.default.writeFileSync(import_path99.default.join(dir2, "entrypoint.sh"), entrypoint);
91169
+ import_fs101.default.mkdirSync(dir2, { recursive: true });
91170
+ import_fs101.default.writeFileSync(import_path100.default.join(dir2, "Dockerfile"), dockerfile);
91171
+ import_fs101.default.writeFileSync(import_path100.default.join(dir2, "entrypoint.sh"), entrypoint);
90763
91172
  return dir2;
90764
91173
  }
90765
91174
  function writeAllowlist(cwd, hosts) {
90766
- const dir2 = import_path99.default.join(cwd, ".node9", "sandbox");
90767
- import_fs100.default.mkdirSync(dir2, { recursive: true });
90768
- const p = import_path99.default.join(dir2, "allowed-domains.txt");
90769
- import_fs100.default.writeFileSync(p, hosts.join("\n") + "\n");
91175
+ const dir2 = import_path100.default.join(cwd, ".node9", "sandbox");
91176
+ import_fs101.default.mkdirSync(dir2, { recursive: true });
91177
+ const p = import_path100.default.join(dir2, "allowed-domains.txt");
91178
+ import_fs101.default.writeFileSync(p, hosts.join("\n") + "\n");
90770
91179
  return p;
90771
91180
  }
90772
91181
  function resolveHomePath(p) {
90773
- return p.startsWith("~") ? import_path99.default.join(import_os81.default.homedir(), p.slice(1)) : import_path99.default.resolve(p);
91182
+ return p.startsWith("~") ? import_path100.default.join(import_os82.default.homedir(), p.slice(1)) : import_path100.default.resolve(p);
90774
91183
  }
90775
91184
 
90776
91185
  // src/cli/commands/sandbox.ts
90777
91186
  function seedDataDirConfig(dataDir, sandbox) {
90778
- import_fs101.default.mkdirSync(dataDir, { recursive: true });
90779
- const configPath2 = import_path100.default.join(dataDir, "config.json");
91187
+ import_fs102.default.mkdirSync(dataDir, { recursive: true });
91188
+ const configPath2 = import_path101.default.join(dataDir, "config.json");
90780
91189
  const seed = {
90781
91190
  settings: {
90782
91191
  approvers: {
@@ -90787,7 +91196,7 @@ function seedDataDirConfig(dataDir, sandbox) {
90787
91196
  }
90788
91197
  }
90789
91198
  };
90790
- import_fs101.default.writeFileSync(configPath2, JSON.stringify(seed, null, 2), { mode: 384 });
91199
+ import_fs102.default.writeFileSync(configPath2, JSON.stringify(seed, null, 2), { mode: 384 });
90791
91200
  }
90792
91201
  function registerSandboxCommand(program2, version2) {
90793
91202
  const node9Version2 = pinnedNode9Version(version2);
@@ -90796,7 +91205,7 @@ function registerSandboxCommand(program2, version2) {
90796
91205
  const agent = opts.agent === "codex" ? "codex" : "claude";
90797
91206
  const p = sandboxConfigPath();
90798
91207
  try {
90799
- import_fs101.default.writeFileSync(p, scaffoldSandboxYaml(agent), { flag: "wx" });
91208
+ import_fs102.default.writeFileSync(p, scaffoldSandboxYaml(agent), { flag: "wx" });
90800
91209
  } catch (e) {
90801
91210
  if (e.code !== "EEXIST") throw e;
90802
91211
  console.log(
@@ -90843,8 +91252,8 @@ function registerSandboxCommand(program2, version2) {
90843
91252
  const buildDir = writeBuildContext(cwd, dockerfile, entrypoint);
90844
91253
  const hash2 = imageContentHash(dockerfile, entrypoint);
90845
91254
  const image = sandbox.runtime.image;
90846
- const hashFile = import_path100.default.join(sandboxBuildDir(cwd), ".image-hash");
90847
- const lastHash = import_fs101.default.existsSync(hashFile) ? import_fs101.default.readFileSync(hashFile, "utf-8").trim() : "";
91255
+ const hashFile = import_path101.default.join(sandboxBuildDir(cwd), ".image-hash");
91256
+ const lastHash = import_fs102.default.existsSync(hashFile) ? import_fs102.default.readFileSync(hashFile, "utf-8").trim() : "";
90848
91257
  const imageExists = (0, import_child_process16.spawnSync)(sandbox.runtime.engine, ["image", "inspect", image], { stdio: "ignore" }).status === 0;
90849
91258
  const needBuild = sandbox.runtime.rebuild === "always" || !imageExists || sandbox.runtime.rebuild !== "never" && lastHash !== hash2;
90850
91259
  if (needBuild) {
@@ -90870,7 +91279,7 @@ function registerSandboxCommand(program2, version2) {
90870
91279
  });
90871
91280
  if (sandbox.node9.mountAgentCredentials) {
90872
91281
  const creds = agentCredentialsMount(sandbox.agent);
90873
- if (import_fs101.default.existsSync(creds.hostPath)) {
91282
+ if (import_fs102.default.existsSync(creds.hostPath)) {
90874
91283
  console.log(import_chalk41.default.dim(` mounting ${creds.hostPath} (agent credentials, rw)`));
90875
91284
  } else {
90876
91285
  console.log(
@@ -90886,20 +91295,20 @@ function registerSandboxCommand(program2, version2) {
90886
91295
  process.exit(r.status ?? 0);
90887
91296
  });
90888
91297
  cmd.command("tail").description("Stream the sandbox's audit log (host-side)").action(() => {
90889
- const auditPath = import_path100.default.join(sandboxDataDir(), "audit.log");
90890
- if (!import_fs101.default.existsSync(auditPath)) {
91298
+ const auditPath = import_path101.default.join(sandboxDataDir(), "audit.log");
91299
+ if (!import_fs102.default.existsSync(auditPath)) {
90891
91300
  console.log(import_chalk41.default.dim(" no sandbox audit yet."));
90892
91301
  return;
90893
91302
  }
90894
91303
  (0, import_child_process16.spawnSync)("tail", ["-f", auditPath], { stdio: "inherit" });
90895
91304
  });
90896
91305
  cmd.command("logs").description("Dump the sandbox's audit log").action(() => {
90897
- const auditPath = import_path100.default.join(sandboxDataDir(), "audit.log");
90898
- if (!import_fs101.default.existsSync(auditPath)) {
91306
+ const auditPath = import_path101.default.join(sandboxDataDir(), "audit.log");
91307
+ if (!import_fs102.default.existsSync(auditPath)) {
90899
91308
  console.log(import_chalk41.default.dim(" no sandbox audit yet."));
90900
91309
  return;
90901
91310
  }
90902
- process.stdout.write(import_fs101.default.readFileSync(auditPath, "utf-8"));
91311
+ process.stdout.write(import_fs102.default.readFileSync(auditPath, "utf-8"));
90903
91312
  });
90904
91313
  cmd.command("clean").description("Remove the sandbox image, build context, and data").action(() => {
90905
91314
  const cwd = process.cwd();
@@ -90913,7 +91322,7 @@ function registerSandboxCommand(program2, version2) {
90913
91322
  stdio: "ignore"
90914
91323
  });
90915
91324
  }
90916
- import_fs101.default.rmSync(import_path100.default.join(cwd, ".node9", "sandbox"), { recursive: true, force: true });
91325
+ import_fs102.default.rmSync(import_path101.default.join(cwd, ".node9", "sandbox"), { recursive: true, force: true });
90917
91326
  console.log(import_chalk41.default.green(" \u2713 sandbox image + build + data removed."));
90918
91327
  });
90919
91328
  }
@@ -91142,13 +91551,13 @@ function registerPackageIndexCommand(program2) {
91142
91551
  // src/cli/commands/skill-pin.ts
91143
91552
  init_cjs_shims();
91144
91553
  var import_chalk45 = __toESM(require("chalk"));
91145
- var import_fs102 = __toESM(require("fs"));
91146
- var import_os82 = __toESM(require("os"));
91147
- var import_path101 = __toESM(require("path"));
91554
+ var import_fs103 = __toESM(require("fs"));
91555
+ var import_os83 = __toESM(require("os"));
91556
+ var import_path102 = __toESM(require("path"));
91148
91557
  init_skill_pin();
91149
91558
  function wipeSkillSessions() {
91150
91559
  try {
91151
- import_fs102.default.rmSync(import_path101.default.join(import_os82.default.homedir(), ".node9", "skill-sessions"), {
91560
+ import_fs103.default.rmSync(import_path102.default.join(import_os83.default.homedir(), ".node9", "skill-sessions"), {
91152
91561
  recursive: true,
91153
91562
  force: true
91154
91563
  });
@@ -91231,15 +91640,15 @@ function registerSkillPinCommand(program2) {
91231
91640
 
91232
91641
  // src/cli/commands/decisions.ts
91233
91642
  init_cjs_shims();
91234
- var import_fs103 = __toESM(require("fs"));
91235
- var import_os83 = __toESM(require("os"));
91236
- var import_path102 = __toESM(require("path"));
91643
+ var import_fs104 = __toESM(require("fs"));
91644
+ var import_os84 = __toESM(require("os"));
91645
+ var import_path103 = __toESM(require("path"));
91237
91646
  var import_chalk46 = __toESM(require("chalk"));
91238
- var DECISIONS_FILE2 = import_path102.default.join(import_os83.default.homedir(), ".node9", "decisions.json");
91647
+ var DECISIONS_FILE2 = import_path103.default.join(import_os84.default.homedir(), ".node9", "decisions.json");
91239
91648
  function readDecisions() {
91240
91649
  try {
91241
- if (!import_fs103.default.existsSync(DECISIONS_FILE2)) return {};
91242
- const raw = import_fs103.default.readFileSync(DECISIONS_FILE2, "utf-8");
91650
+ if (!import_fs104.default.existsSync(DECISIONS_FILE2)) return {};
91651
+ const raw = import_fs104.default.readFileSync(DECISIONS_FILE2, "utf-8");
91243
91652
  const parsed = JSON.parse(raw);
91244
91653
  const out = {};
91245
91654
  for (const [k, v] of Object.entries(parsed)) {
@@ -91251,11 +91660,11 @@ function readDecisions() {
91251
91660
  }
91252
91661
  }
91253
91662
  function writeDecisions(d) {
91254
- const dir2 = import_path102.default.dirname(DECISIONS_FILE2);
91255
- if (!import_fs103.default.existsSync(dir2)) import_fs103.default.mkdirSync(dir2, { recursive: true });
91663
+ const dir2 = import_path103.default.dirname(DECISIONS_FILE2);
91664
+ if (!import_fs104.default.existsSync(dir2)) import_fs104.default.mkdirSync(dir2, { recursive: true });
91256
91665
  const tmp = `${DECISIONS_FILE2}.${process.pid}.tmp`;
91257
- import_fs103.default.writeFileSync(tmp, JSON.stringify(d, null, 2));
91258
- import_fs103.default.renameSync(tmp, DECISIONS_FILE2);
91666
+ import_fs104.default.writeFileSync(tmp, JSON.stringify(d, null, 2));
91667
+ import_fs104.default.renameSync(tmp, DECISIONS_FILE2);
91259
91668
  }
91260
91669
  function registerDecisionsCommand(program2) {
91261
91670
  const cmd = program2.command("decisions").description('Manage persistent "Always Allow" / "Always Deny" tool decisions');
@@ -91313,15 +91722,15 @@ Persistent decisions (${entries.length})
91313
91722
  // src/cli/commands/dlp.ts
91314
91723
  init_cjs_shims();
91315
91724
  var import_chalk47 = __toESM(require("chalk"));
91316
- var import_fs104 = __toESM(require("fs"));
91317
- var import_path103 = __toESM(require("path"));
91318
- var import_os84 = __toESM(require("os"));
91725
+ var import_fs105 = __toESM(require("fs"));
91726
+ var import_path104 = __toESM(require("path"));
91727
+ var import_os85 = __toESM(require("os"));
91319
91728
  init_safe_text();
91320
- var AUDIT_LOG = import_path103.default.join(import_os84.default.homedir(), ".node9", "audit.log");
91321
- var RESOLVED_FILE = import_path103.default.join(import_os84.default.homedir(), ".node9", "dlp-resolved.json");
91729
+ var AUDIT_LOG = import_path104.default.join(import_os85.default.homedir(), ".node9", "audit.log");
91730
+ var RESOLVED_FILE = import_path104.default.join(import_os85.default.homedir(), ".node9", "dlp-resolved.json");
91322
91731
  function loadResolved() {
91323
91732
  try {
91324
- const raw = JSON.parse(import_fs104.default.readFileSync(RESOLVED_FILE, "utf-8"));
91733
+ const raw = JSON.parse(import_fs105.default.readFileSync(RESOLVED_FILE, "utf-8"));
91325
91734
  return new Set(raw);
91326
91735
  } catch {
91327
91736
  return /* @__PURE__ */ new Set();
@@ -91329,13 +91738,13 @@ function loadResolved() {
91329
91738
  }
91330
91739
  function saveResolved(resolved) {
91331
91740
  try {
91332
- import_fs104.default.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
91741
+ import_fs105.default.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
91333
91742
  } catch {
91334
91743
  }
91335
91744
  }
91336
91745
  function loadDlpFindings() {
91337
- if (!import_fs104.default.existsSync(AUDIT_LOG)) return [];
91338
- return import_fs104.default.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
91746
+ if (!import_fs105.default.existsSync(AUDIT_LOG)) return [];
91747
+ return import_fs105.default.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
91339
91748
  if (!line.trim()) return [];
91340
91749
  try {
91341
91750
  const e = JSON.parse(line);
@@ -91434,15 +91843,15 @@ function registerDlpCommand(program2) {
91434
91843
  // src/cli/commands/mask.ts
91435
91844
  init_cjs_shims();
91436
91845
  var import_chalk48 = __toESM(require("chalk"));
91437
- var import_fs105 = __toESM(require("fs"));
91438
- var import_path104 = __toESM(require("path"));
91439
- var import_os85 = __toESM(require("os"));
91846
+ var import_fs106 = __toESM(require("fs"));
91847
+ var import_path105 = __toESM(require("path"));
91848
+ var import_os86 = __toESM(require("os"));
91440
91849
  init_dlp();
91441
91850
  function findJsonlFiles(dir2) {
91442
91851
  const results = [];
91443
- if (!import_fs105.default.existsSync(dir2)) return results;
91444
- for (const entry of import_fs105.default.readdirSync(dir2, { withFileTypes: true })) {
91445
- const full = import_path104.default.join(dir2, entry.name);
91852
+ if (!import_fs106.default.existsSync(dir2)) return results;
91853
+ for (const entry of import_fs106.default.readdirSync(dir2, { withFileTypes: true })) {
91854
+ const full = import_path105.default.join(dir2, entry.name);
91446
91855
  if (entry.isDirectory()) results.push(...findJsonlFiles(full));
91447
91856
  else if (entry.isFile() && entry.name.endsWith(".jsonl")) results.push(full);
91448
91857
  }
@@ -91485,7 +91894,7 @@ function redactJson(obj) {
91485
91894
  function processFile(filePath, dryRun) {
91486
91895
  let raw;
91487
91896
  try {
91488
- raw = import_fs105.default.readFileSync(filePath, "utf-8");
91897
+ raw = import_fs106.default.readFileSync(filePath, "utf-8");
91489
91898
  } catch {
91490
91899
  return { redactedLines: 0, patterns: [] };
91491
91900
  }
@@ -91517,14 +91926,14 @@ function processFile(filePath, dryRun) {
91517
91926
  }
91518
91927
  }
91519
91928
  if (!dryRun && redactedLines > 0) {
91520
- import_fs105.default.writeFileSync(filePath, newLines.join("\n"), "utf-8");
91929
+ import_fs106.default.writeFileSync(filePath, newLines.join("\n"), "utf-8");
91521
91930
  }
91522
91931
  return { redactedLines, patterns };
91523
91932
  }
91524
91933
  function processJsonFile(filePath, dryRun) {
91525
91934
  let raw;
91526
91935
  try {
91527
- raw = import_fs105.default.readFileSync(filePath, "utf-8");
91936
+ raw = import_fs106.default.readFileSync(filePath, "utf-8");
91528
91937
  } catch {
91529
91938
  return { redactedLines: 0, patterns: [] };
91530
91939
  }
@@ -91537,15 +91946,15 @@ function processJsonFile(filePath, dryRun) {
91537
91946
  const { value, modified, found } = redactJson(parsed);
91538
91947
  if (!modified) return { redactedLines: 0, patterns: [] };
91539
91948
  if (!dryRun) {
91540
- import_fs105.default.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
91949
+ import_fs106.default.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
91541
91950
  }
91542
91951
  return { redactedLines: 1, patterns: found };
91543
91952
  }
91544
91953
  function findJsonFiles(dir2) {
91545
91954
  const results = [];
91546
- if (!import_fs105.default.existsSync(dir2)) return results;
91547
- for (const entry of import_fs105.default.readdirSync(dir2, { withFileTypes: true })) {
91548
- const full = import_path104.default.join(dir2, entry.name);
91955
+ if (!import_fs106.default.existsSync(dir2)) return results;
91956
+ for (const entry of import_fs106.default.readdirSync(dir2, { withFileTypes: true })) {
91957
+ const full = import_path105.default.join(dir2, entry.name);
91549
91958
  if (entry.isDirectory()) results.push(...findJsonFiles(full));
91550
91959
  else if (entry.isFile() && entry.name.endsWith(".json")) results.push(full);
91551
91960
  }
@@ -91554,9 +91963,9 @@ function findJsonFiles(dir2) {
91554
91963
  function registerMaskCommand(program2) {
91555
91964
  program2.command("mask").description("Redact plaintext secrets from local AI session history files").option("--dry-run", "show what would be redacted without making changes").option("--all", "scan all history (default: last 30 days)").action(async (options) => {
91556
91965
  const dryRun = !!options.dryRun;
91557
- const home = import_os85.default.homedir();
91558
- const claudeDir = import_path104.default.join(home, ".claude", "projects");
91559
- const geminiDir = import_path104.default.join(home, ".gemini", "tmp");
91966
+ const home = import_os86.default.homedir();
91967
+ const claudeDir = import_path105.default.join(home, ".claude", "projects");
91968
+ const geminiDir = import_path105.default.join(home, ".gemini", "tmp");
91560
91969
  const allFiles = [
91561
91970
  ...findJsonlFiles(claudeDir).map((p) => ({ path: p, type: "jsonl" })),
91562
91971
  ...findJsonFiles(geminiDir).map((p) => ({ path: p, type: "json" }))
@@ -91564,7 +91973,7 @@ function registerMaskCommand(program2) {
91564
91973
  const cutoff = options.all ? null : new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
91565
91974
  const filtered = cutoff ? allFiles.filter((f) => {
91566
91975
  try {
91567
- return import_fs105.default.statSync(f.path).mtime >= cutoff;
91976
+ return import_fs106.default.statSync(f.path).mtime >= cutoff;
91568
91977
  } catch {
91569
91978
  return false;
91570
91979
  }
@@ -91621,7 +92030,7 @@ function registerMaskCommand(program2) {
91621
92030
  init_blast();
91622
92031
  init_safe_text();
91623
92032
  var { version } = JSON.parse(
91624
- import_fs109.default.readFileSync(import_path108.default.join(__dirname, "../package.json"), "utf-8")
92033
+ import_fs110.default.readFileSync(import_path109.default.join(__dirname, "../package.json"), "utf-8")
91625
92034
  );
91626
92035
  var program = new import_commander.Command();
91627
92036
  program.name("node9").description("The Sudo Command for AI Agents").version(version);
@@ -91779,8 +92188,8 @@ function undoLeftoverNote() {
91779
92188
  program.command("uninstall").description("Remove all Node9 hooks and optionally delete config files").option("--purge", "Also delete ~/.node9/ directory (config, audit log, credentials)").action(async (options) => {
91780
92189
  console.log(import_chalk50.default.cyan("\n\u{1F6E1}\uFE0F Node9 Uninstall\n"));
91781
92190
  try {
91782
- const credRaw = import_fs109.default.readFileSync(
91783
- import_path108.default.join(import_os89.default.homedir(), ".node9", "credentials.json"),
92191
+ const credRaw = import_fs110.default.readFileSync(
92192
+ import_path109.default.join(import_os90.default.homedir(), ".node9", "credentials.json"),
91784
92193
  "utf-8"
91785
92194
  );
91786
92195
  const prof = JSON.parse(credRaw)[process.env.NODE9_PROFILE || "default"];
@@ -91855,7 +92264,7 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
91855
92264
  if (removed.length > 0 || refused.length > 0) {
91856
92265
  console.log(import_chalk50.default.bold("\nRemoving decoy credentials..."));
91857
92266
  for (const r of removed) {
91858
- if (r.path) console.log(import_chalk50.default.green(` \u2705 ${r.path.replace(import_os89.default.homedir(), "~")}`));
92267
+ if (r.path) console.log(import_chalk50.default.green(` \u2705 ${r.path.replace(import_os90.default.homedir(), "~")}`));
91859
92268
  }
91860
92269
  for (const r of refused) {
91861
92270
  decoysLeft.push(r.path ?? "(unknown path)");
@@ -91872,8 +92281,8 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
91872
92281
  decoysLeft = ["(the decoy registry could not be read)"];
91873
92282
  }
91874
92283
  if (options.purge) {
91875
- const node9Dir = import_path108.default.join(import_os89.default.homedir(), ".node9");
91876
- if (import_fs109.default.existsSync(node9Dir)) {
92284
+ const node9Dir = import_path109.default.join(import_os90.default.homedir(), ".node9");
92285
+ if (import_fs110.default.existsSync(node9Dir)) {
91877
92286
  const alsoSnapshots = undoLeftoverPaths().length > 0 ? ", old undo snapshots" : "";
91878
92287
  if (decoysLeft.length > 0) {
91879
92288
  console.error(import_chalk50.default.red("\n \u26A0\uFE0F Decoy credential files are still on disk:"));
@@ -91889,8 +92298,8 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
91889
92298
  default: false
91890
92299
  });
91891
92300
  if (confirmed) {
91892
- import_fs109.default.rmSync(node9Dir, { recursive: true });
91893
- if (import_fs109.default.existsSync(node9Dir)) {
92301
+ import_fs110.default.rmSync(node9Dir, { recursive: true });
92302
+ if (import_fs110.default.existsSync(node9Dir)) {
91894
92303
  console.error(
91895
92304
  import_chalk50.default.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
91896
92305
  );
@@ -92046,7 +92455,7 @@ program.command("tail").description("Stream live agent activity to the terminal"
92046
92455
  });
92047
92456
  program.command("monitor").description("Live interactive dashboard \u2014 activity feed, approvals, security signals").action(async () => {
92048
92457
  try {
92049
- const dashboardPath = import_path108.default.join(__dirname, "dashboard.mjs");
92458
+ const dashboardPath = import_path109.default.join(__dirname, "dashboard.mjs");
92050
92459
  const dynamicImport = new Function("id", "return import(id)");
92051
92460
  const mod = await dynamicImport(`file://${dashboardPath}`);
92052
92461
  await mod.startMonitor();
@@ -92084,14 +92493,14 @@ Claude Code spawns this command every ~300ms and writes a JSON payload to stdin.
92084
92493
  Run "node9 addto claude" to register it as the statusLine.`
92085
92494
  ).argument("[subcommand]", 'Optional: "debug on" / "debug off" to toggle stdin logging').argument("[state]", 'on|off \u2014 used with "debug" subcommand').action(async (subcommand, state) => {
92086
92495
  if (subcommand === "debug") {
92087
- const flagFile = import_path108.default.join(import_os89.default.homedir(), ".node9", "hud-debug");
92496
+ const flagFile = import_path109.default.join(import_os90.default.homedir(), ".node9", "hud-debug");
92088
92497
  if (state === "on") {
92089
- import_fs109.default.mkdirSync(import_path108.default.dirname(flagFile), { recursive: true });
92090
- import_fs109.default.writeFileSync(flagFile, "");
92498
+ import_fs110.default.mkdirSync(import_path109.default.dirname(flagFile), { recursive: true });
92499
+ import_fs110.default.writeFileSync(flagFile, "");
92091
92500
  console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
92092
92501
  console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
92093
92502
  } else if (state === "off") {
92094
- if (import_fs109.default.existsSync(flagFile)) import_fs109.default.unlinkSync(flagFile);
92503
+ if (import_fs110.default.existsSync(flagFile)) import_fs110.default.unlinkSync(flagFile);
92095
92504
  console.log("HUD debug logging disabled.");
92096
92505
  } else {
92097
92506
  console.error("Usage: node9 hud debug on|off");
@@ -92206,6 +92615,7 @@ registerTrustCommand(program);
92206
92615
  registerSyncCommand(program);
92207
92616
  registerAgentsCommand(program);
92208
92617
  registerScanCommand(program);
92618
+ registerForecastCasesCommand(program);
92209
92619
  registerPostureCommand(program);
92210
92620
  registerScanRepoCommand(program);
92211
92621
  registerEgressCommand(program);
@@ -92225,9 +92635,9 @@ if (process.argv[2] !== "daemon") {
92225
92635
  const isCheckHook = process.argv[2] === "check";
92226
92636
  if (isCheckHook) {
92227
92637
  if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
92228
- const logPath = import_path108.default.join(import_os89.default.homedir(), ".node9", "hook-debug.log");
92638
+ const logPath = import_path109.default.join(import_os90.default.homedir(), ".node9", "hook-debug.log");
92229
92639
  const msg = reason instanceof Error ? reason.message : String(reason);
92230
- import_fs109.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
92640
+ import_fs110.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
92231
92641
  `);
92232
92642
  }
92233
92643
  process.exit(0);