@node9/proxy 2.49.0 → 2.50.1

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)) {
@@ -24527,13 +24531,15 @@ function privateDir(dir2) {
24527
24531
  }
24528
24532
  return isPrivateDir(dir2);
24529
24533
  }
24534
+ function perHomeRuntimeDir(env, home) {
24535
+ const xdg = env.XDG_RUNTIME_DIR;
24536
+ if (!xdg || !import_path16.default.isAbsolute(xdg)) return null;
24537
+ return import_path16.default.join(xdg, `node9-${(0, import_crypto10.createHash)("sha256").update(home).digest("hex").slice(0, 12)}`);
24538
+ }
24530
24539
  function chooseActivitySocketPath(env, home, platform = process.platform) {
24531
24540
  const candidates2 = [];
24532
- const xdg = env.XDG_RUNTIME_DIR;
24533
- if (xdg && import_path16.default.isAbsolute(xdg) && isPrivateDir(xdg))
24534
- candidates2.push(
24535
- import_path16.default.join(xdg, `node9-${(0, import_crypto10.createHash)("sha256").update(home).digest("hex").slice(0, 12)}`)
24536
- );
24541
+ const xdgDir = perHomeRuntimeDir(env, home);
24542
+ if (xdgDir && isPrivateDir(import_path16.default.dirname(xdgDir))) candidates2.push(xdgDir);
24537
24543
  candidates2.push(import_path16.default.join(home, ".node9", "run"));
24538
24544
  for (const dir2 of candidates2) {
24539
24545
  const socketPath = import_path16.default.join(dir2, SOCKET_NAME);
@@ -24568,6 +24574,19 @@ function isOwnStaleSocket(socketPath) {
24568
24574
  return false;
24569
24575
  }
24570
24576
  }
24577
+ function removeOwnSocketDir(socketPath, env, home, platform = process.platform) {
24578
+ if (platform === "win32") return false;
24579
+ const dir2 = import_path16.default.dirname(socketPath);
24580
+ if (dir2 !== perHomeRuntimeDir(env, home)) return false;
24581
+ if (!isPrivateDir(dir2)) return false;
24582
+ try {
24583
+ if (import_fs16.default.readdirSync(dir2).length > 0) return false;
24584
+ import_fs16.default.rmdirSync(dir2);
24585
+ return true;
24586
+ } catch {
24587
+ return false;
24588
+ }
24589
+ }
24571
24590
  var import_fs16, import_path16, import_crypto10, SOCKET_NAME, currentUid, WINDOWS_PIPE;
24572
24591
  var init_activity_socket = __esm({
24573
24592
  "src/utils/activity-socket.ts"() {
@@ -28457,7 +28476,9 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
28457
28476
  systemAccessHandled: !isManual && fileAccess.handledPaths.has(filePath)
28458
28477
  }) : null) ?? argumentScan(),
28459
28478
  pii: () => detectArgsPii(args),
28460
- packageCheck: () => packageCheck?.enabled && !isManual ? runPackageCheck(toolName, args, packageCheck, options?.cwd, { offline: !!forecast }) : Promise.resolve(null)
28479
+ packageCheck: () => packageCheck?.enabled && !isManual ? runPackageCheck(toolName, args, packageCheck, options?.cwd, {
28480
+ offline: !!forecast && forecast.onlinePackages !== true
28481
+ }) : Promise.resolve(null)
28461
28482
  });
28462
28483
  const dlpMatch = gates.stop?.dlpMatch ?? gates.reviews.dlp ?? null;
28463
28484
  if (dlpMatch) {
@@ -34065,7 +34086,8 @@ async function forecastDecision(toolName, args, meta, opts = {}) {
34065
34086
  session,
34066
34087
  config,
34067
34088
  unknown: /* @__PURE__ */ new Set(),
34068
- now: opts.at ?? Date.now()
34089
+ now: opts.at ?? Date.now(),
34090
+ ...opts.onlinePackages && { onlinePackages: true }
34069
34091
  };
34070
34092
  const result = await runForecast(
34071
34093
  run,
@@ -34160,6 +34182,20 @@ var init_forecast = __esm({
34160
34182
  }
34161
34183
  });
34162
34184
 
34185
+ // src/exposure/shell-fetch.ts
34186
+ function shellFetch(tool, rawInput) {
34187
+ if (tool !== "Bash" && tool !== "bash") return null;
34188
+ const cmd = rawInput && typeof rawInput === "object" && !Array.isArray(rawInput) ? rawInput.command : void 0;
34189
+ return typeof cmd === "string" && cmd.length > 0 ? classifyShellExposure(cmd) : null;
34190
+ }
34191
+ var init_shell_fetch = __esm({
34192
+ "src/exposure/shell-fetch.ts"() {
34193
+ "use strict";
34194
+ init_cjs_shims();
34195
+ init_dist();
34196
+ }
34197
+ });
34198
+
34163
34199
  // src/version.ts
34164
34200
  function readVersion() {
34165
34201
  let dir2 = __dirname;
@@ -34460,6 +34496,8 @@ var init_scan_judge = __esm({
34460
34496
  init_forecast();
34461
34497
  init_forecast_state();
34462
34498
  init_roles();
34499
+ init_shell_fetch();
34500
+ init_dist();
34463
34501
  init_scan_cache();
34464
34502
  init_dlp();
34465
34503
  import_fs45 = __toESM(require("fs"));
@@ -34534,16 +34572,26 @@ var init_scan_judge = __esm({
34534
34572
  this.session(sessionId).noteOutputSecret(patternName, clockOf(timestamp2));
34535
34573
  }
34536
34574
  /** A tool that brings outside content returned something. */
34537
- noteToolResult(sessionId, toolName, timestamp2, cwd) {
34575
+ /**
34576
+ * A tool result reached the session. A source read marks it exposed, as the
34577
+ * PostToolUse hook does (log.ts): a source tool, or a shell command that
34578
+ * fetches outside content (`gh issue view`). `read.output` is the result's
34579
+ * text: the destinations it names are recorded as the hook records them.
34580
+ */
34581
+ noteToolResult(sessionId, toolName, timestamp2, cwd, read3 = {}) {
34538
34582
  const policy = this.configFor(cwd).policy.untrustedContent;
34539
34583
  if (!policy?.enabled) return;
34540
- if (!toolRole(toolName, policy).source) return;
34584
+ const shell = shellFetch(toolName, read3.input);
34585
+ if (!toolRole(toolName, policy).source && !shell?.fetches) return;
34541
34586
  const mcp = /^mcp__([^_]+(?:_[^_]+)*)__/.exec(toolName);
34542
- this.session(sessionId).noteOutsideContent({
34543
- tool: toolName,
34544
- ...mcp && { server: mcp[1] },
34545
- at: clockOf(timestamp2)
34546
- });
34587
+ this.session(sessionId).noteOutsideContent(
34588
+ {
34589
+ tool: toolName,
34590
+ ...mcp && { server: mcp[1] },
34591
+ at: clockOf(timestamp2)
34592
+ },
34593
+ read3.output ? extractContentDestinations(read3.output) : []
34594
+ );
34547
34595
  }
34548
34596
  async judge(call, result, dedup) {
34549
34597
  const config = this.configFor(call.cwd);
@@ -37034,8 +37082,8 @@ function isOfficialInstaller(u) {
37034
37082
  if (u.username || u.password) return false;
37035
37083
  const host2 = u.hostname.toLowerCase().replace(/^www\./, "");
37036
37084
  if (OFFICIAL_HOSTS.has(host2)) return true;
37037
- const path113 = OFFICIAL_PATHS[host2];
37038
- return !!path113 && path113.test(u.pathname);
37085
+ const path114 = OFFICIAL_PATHS[host2];
37086
+ return !!path114 && path114.test(u.pathname);
37039
37087
  }
37040
37088
  function onlyOfficialInstaller(s) {
37041
37089
  const urls = s.match(URL_RE3);
@@ -37181,21 +37229,21 @@ function appSkillDirsOf(paths, skillDirs) {
37181
37229
  }
37182
37230
  return apps;
37183
37231
  }
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);
37232
+ function isSkillSupportFile(path114, skillDirs, appDirs) {
37233
+ if (!path114.endsWith(".md") || /(^|\/)[Ss][Kk][Ii][Ll][Ll]\.md$/.test(path114)) return false;
37234
+ for (let d = path114.lastIndexOf("/"); d > 0; d = path114.lastIndexOf("/", d - 1)) {
37235
+ const dir2 = path114.slice(0, d);
37188
37236
  if (!skillDirs.has(dir2)) continue;
37189
- return !appDirs?.has(dir2) || SKILL_LAYOUT_RE.test(path113.slice(d + 1));
37237
+ return !appDirs?.has(dir2) || SKILL_LAYOUT_RE.test(path114.slice(d + 1));
37190
37238
  }
37191
37239
  return false;
37192
37240
  }
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);
37241
+ function isAppDoc(path114, skillDirs, appDirs) {
37242
+ if (!path114.endsWith(".md") || INSTRUCTION_FILE_RE.test(path114)) return false;
37243
+ for (let d = path114.lastIndexOf("/"); d > 0; d = path114.lastIndexOf("/", d - 1)) {
37244
+ const dir2 = path114.slice(0, d);
37197
37245
  if (!skillDirs.has(dir2)) continue;
37198
- return appDirs.has(dir2) && !SKILL_LAYOUT_RE.test(path113.slice(d + 1));
37246
+ return appDirs.has(dir2) && !SKILL_LAYOUT_RE.test(path114.slice(d + 1));
37199
37247
  }
37200
37248
  return false;
37201
37249
  }
@@ -37217,20 +37265,20 @@ function namedDocs(dir2, skillMd) {
37217
37265
  }
37218
37266
  return out;
37219
37267
  }
37220
- function isHookScript(path113) {
37221
- return /(^|\/)\.claude\/hooks\/.+/.test(path113) && SCRIPT_EXT_RE.test(path113);
37268
+ function isHookScript(path114) {
37269
+ return /(^|\/)\.claude\/hooks\/.+/.test(path114) && SCRIPT_EXT_RE.test(path114);
37222
37270
  }
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);
37271
+ function isSkillScript(path114, skillDirs) {
37272
+ if (!SCRIPT_EXT_RE.test(path114)) return false;
37273
+ for (let d = path114.lastIndexOf("/"); d > 0; d = path114.lastIndexOf("/", d - 1)) {
37274
+ if (!skillDirs.has(path114.slice(0, d))) continue;
37275
+ const rel = path114.slice(d + 1);
37228
37276
  return !rel.includes("/") || /^(scripts|bin)\//.test(rel);
37229
37277
  }
37230
37278
  return false;
37231
37279
  }
37232
- function isInstructionFile(path113, skillDirs, appDirs) {
37233
- return INSTRUCTION_FILE_RE.test(path113) || isSkillSupportFile(path113, skillDirs, appDirs);
37280
+ function isInstructionFile(path114, skillDirs, appDirs) {
37281
+ return INSTRUCTION_FILE_RE.test(path114) || isSkillSupportFile(path114, skillDirs, appDirs);
37234
37282
  }
37235
37283
  function findFetchObeyUnless(text, skip) {
37236
37284
  for (let start = 0; start <= text.length; ) {
@@ -37354,20 +37402,20 @@ function decodeSuspiciousBase64(text) {
37354
37402
  }
37355
37403
  return out;
37356
37404
  }
37357
- function mk2(rule, severity, title, signals, fix, path113, line) {
37405
+ function mk2(rule, severity, title, signals, fix, path114, line) {
37358
37406
  return {
37359
37407
  check: "CI-6",
37360
37408
  rule,
37361
37409
  dimension: "instructions",
37362
37410
  severity,
37363
37411
  title,
37364
- file: path113,
37412
+ file: path114,
37365
37413
  ...line ? { line } : {},
37366
37414
  signals,
37367
37415
  fix
37368
37416
  };
37369
37417
  }
37370
- function analyzeInstructionFile(path113, content) {
37418
+ function analyzeInstructionFile(path114, content) {
37371
37419
  const findings = [];
37372
37420
  const decoded = decodeSuspiciousBase64(content);
37373
37421
  const glyph = homoglyphWord(content);
@@ -37381,7 +37429,7 @@ function analyzeInstructionFile(path113, content) {
37381
37429
  `\`${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
37430
  ],
37383
37431
  "Spell the word with plain Latin letters, or explain on the same line why the look-alikes are there.",
37384
- path113,
37432
+ path114,
37385
37433
  glyph.line
37386
37434
  )
37387
37435
  );
@@ -37395,7 +37443,7 @@ function analyzeInstructionFile(path113, content) {
37395
37443
  "contains Unicode tag characters (U+E0000\u2013E007F): an invisible instruction-smuggling channel with no legitimate use in text"
37396
37444
  ],
37397
37445
  "Remove the tag characters. Instruction files must be plain, reviewable text.",
37398
- path113,
37446
+ path114,
37399
37447
  lineAt(content, content.search(TAG_CHARS))
37400
37448
  )
37401
37449
  );
@@ -37409,7 +37457,7 @@ function analyzeInstructionFile(path113, content) {
37409
37457
  "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
37458
  ],
37411
37459
  "Remove the bidi override characters.",
37412
- path113,
37460
+ path114,
37413
37461
  lineAt(content, content.search(BIDI_OVERRIDE))
37414
37462
  )
37415
37463
  );
@@ -37423,7 +37471,7 @@ function analyzeInstructionFile(path113, content) {
37423
37471
  "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
37472
  ],
37425
37473
  "Confirm the bidi marks are legitimate RTL formatting; remove otherwise.",
37426
- path113,
37474
+ path114,
37427
37475
  lineAt(content, content.search(BIDI_EMBED_ISOLATE))
37428
37476
  )
37429
37477
  );
@@ -37439,7 +37487,7 @@ function analyzeInstructionFile(path113, content) {
37439
37487
  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
37488
  ],
37441
37489
  "Remove the zero-width characters. Instruction files must be plain, reviewable text.",
37442
- path113,
37490
+ path114,
37443
37491
  lineAt(content, zw.first)
37444
37492
  )
37445
37493
  );
@@ -37458,7 +37506,7 @@ function analyzeInstructionFile(path113, content) {
37458
37506
  `contains a prompt-override / role-impersonation directive (\`${safeText(m[0].slice(0, 60).trim(), 60)}\`)${ovEnc ? ", concealed in a base64 blob" : ""}`
37459
37507
  ],
37460
37508
  "Remove the override text. An instruction file should not tell the agent to ignore its own rules.",
37461
- path113,
37509
+ path114,
37462
37510
  ov ? lineAt(content, ov.index) : void 0
37463
37511
  // concealed in base64: no line, never a wrong one
37464
37512
  )
@@ -37475,7 +37523,7 @@ function analyzeInstructionFile(path113, content) {
37475
37523
  `\`${safeText(fo[0].slice(0, 70).trim(), 70)}\`: fetch-and-obey, outside an install/setup section`
37476
37524
  ],
37477
37525
  "Do not instruct the agent to pipe remote content into a shell; pin and vendor scripts instead.",
37478
- path113,
37526
+ path114,
37479
37527
  lineAt(content, fo.index)
37480
37528
  )
37481
37529
  );
@@ -37491,7 +37539,7 @@ function analyzeInstructionFile(path113, content) {
37491
37539
  `references \`${safeText(sp[0].slice(0, 50).trim(), 50)}\`: directs the agent toward secrets`
37492
37540
  ],
37493
37541
  "Do not reference credential files or paths in agent instructions.",
37494
- path113,
37542
+ path114,
37495
37543
  lineAt(content, sp.index)
37496
37544
  )
37497
37545
  );
@@ -37514,7 +37562,7 @@ function analyzeInstructionFile(path113, content) {
37514
37562
  "Instruction directs the agent to send data to an external endpoint",
37515
37563
  [`\`${safeText(ex[0].slice(0, 70).trim(), 70)}\`: possible exfiltration directive`],
37516
37564
  "Remove external post/upload directives from agent instructions.",
37517
- path113,
37565
+ path114,
37518
37566
  lineAt(content, ex.index)
37519
37567
  )
37520
37568
  );
@@ -37747,7 +37795,7 @@ function cmdLine(content, cmd) {
37747
37795
  const i = jsonValueIndex(content, cmd);
37748
37796
  return i >= 0 ? lineAtIndex(content, i) : void 0;
37749
37797
  }
37750
- function analyzeAgentConfig(path113, content, tree) {
37798
+ function analyzeAgentConfig(path114, content, tree) {
37751
37799
  let cfg;
37752
37800
  try {
37753
37801
  cfg = JSON.parse(content);
@@ -37770,7 +37818,7 @@ function analyzeAgentConfig(path113, content, tree) {
37770
37818
  dimension: "toolRules",
37771
37819
  severity: high ? "high" : "medium",
37772
37820
  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,
37821
+ file: path114,
37774
37822
  ...cmdLine(content, cmd) ? { line: cmdLine(content, cmd) } : {},
37775
37823
  signals: [
37776
37824
  `hook command: \`${safeText(cmd, 120)}\``,
@@ -37791,7 +37839,7 @@ function analyzeAgentConfig(path113, content, tree) {
37791
37839
  dimension: "toolRules",
37792
37840
  severity: "medium",
37793
37841
  title: "Agent hook runs a script that is not committed",
37794
- file: path113,
37842
+ file: path114,
37795
37843
  ...cmdLine(content, cmd) ? { line: cmdLine(content, cmd) } : {},
37796
37844
  signals: [
37797
37845
  `hook command: \`${safeText(cmd, 120)}\``,
@@ -37807,7 +37855,7 @@ function analyzeAgentConfig(path113, content, tree) {
37807
37855
  dimension: "toolRules",
37808
37856
  severity: "advisory",
37809
37857
  title: "Agent hook runs a committed script this scan did not read",
37810
- file: path113,
37858
+ file: path114,
37811
37859
  ...cmdLine(content, cmd) ? { line: cmdLine(content, cmd) } : {},
37812
37860
  signals: [
37813
37861
  `hook command: \`${safeText(cmd, 120)}\``,
@@ -37836,12 +37884,12 @@ function analyzeAgentConfig(path113, content, tree) {
37836
37884
  dimension: "toolRules",
37837
37885
  severity: high ? "high" : "medium",
37838
37886
  title: high ? "Committed agent config pre-authorizes broad tools with no deny backstop" : "Committed agent config pre-authorizes broad tools",
37839
- file: path113,
37887
+ file: path114,
37840
37888
  signals,
37841
37889
  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
37890
  explain: explainGrant({
37843
37891
  high,
37844
- file: path113,
37892
+ file: path114,
37845
37893
  broad: grade.broad,
37846
37894
  bareShell: grade.bareShell,
37847
37895
  hasBackstop: grade.bareShell ? deny.some((d) => /^Bash\b/.test(d)) : deny.some((d) => /Bash|Write|Edit/.test(d))
@@ -37858,7 +37906,7 @@ function analyzeAgentConfig(path113, content, tree) {
37858
37906
  dimension: "toolRules",
37859
37907
  severity: "medium",
37860
37908
  title: "Committed agent config pre-approves a command that can run other programs",
37861
- file: path113,
37909
+ file: path114,
37862
37910
  signals: [
37863
37911
  `pre-approved: ${escapes.slice(0, 5).map((e) => `\`${safeText(e, 80)}\``).join(", ")}`,
37864
37912
  "`find -exec` and `make -n --eval` run other programs without a prompt"
@@ -37868,8 +37916,8 @@ function analyzeAgentConfig(path113, content, tree) {
37868
37916
  }
37869
37917
  return findings;
37870
37918
  }
37871
- function skillReadsUntrusted(path113, content, allow, broad) {
37872
- if (!ROOT_SURFACE_RE.test(path113)) return null;
37919
+ function skillReadsUntrusted(path114, content, allow, broad) {
37920
+ if (!ROOT_SURFACE_RE.test(path114)) return null;
37873
37921
  const body = content.replace(/^---[\s\S]*?\n---/, "");
37874
37922
  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
37923
  const webGrant = allow.some((a) => /^(WebFetch|WebSearch)\b/.test(a));
@@ -37884,8 +37932,8 @@ function skillReadsUntrusted(path113, content, allow, broad) {
37884
37932
  return "it reads pull request text that strangers write, and may edit files and run git/gh on it";
37885
37933
  return null;
37886
37934
  }
37887
- function analyzeSkillGrants(path113, content) {
37888
- if (!GRANT_CARRIER_RE.test(path113)) return [];
37935
+ function analyzeSkillGrants(path114, content) {
37936
+ if (!GRANT_CARRIER_RE.test(path114)) return [];
37889
37937
  const allow = allowedToolsOf(parseFrontmatter(content));
37890
37938
  if (allow.length === 0) return [];
37891
37939
  const grade = gradeBroadGrant(allow, [], {
@@ -37893,9 +37941,9 @@ function analyzeSkillGrants(path113, content) {
37893
37941
  scope: "whenever this skill or command is active, for everyone who opens this repo with the agent"
37894
37942
  });
37895
37943
  if (!grade) return [];
37896
- const kind = /commands\//.test(path113) ? "slash command" : "skill";
37944
+ const kind = /commands\//.test(path114) ? "slash command" : "skill";
37897
37945
  const at = content.search(/^allowed-tools\s*:/m);
37898
- const untrusted = skillReadsUntrusted(path113, content, allow, grade.broad);
37946
+ const untrusted = skillReadsUntrusted(path114, content, allow, grade.broad);
37899
37947
  if (untrusted)
37900
37948
  return [
37901
37949
  {
@@ -37905,7 +37953,7 @@ function analyzeSkillGrants(path113, content) {
37905
37953
  dimension: "toolRules",
37906
37954
  severity: "advisory",
37907
37955
  title: `Committed ${kind} runs code or writes with text strangers control`,
37908
- file: path113,
37956
+ file: path114,
37909
37957
  signals: [...grade.signals, untrusted],
37910
37958
  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
37959
  }
@@ -37922,7 +37970,7 @@ function analyzeSkillGrants(path113, content) {
37922
37970
  // shell — the cap is on reach, not on honesty.
37923
37971
  severity: "medium",
37924
37972
  title: grade.high ? `Committed ${kind} pre-authorizes an unrestricted shell while active` : `Committed ${kind} pre-authorizes broad tools while active`,
37925
- file: path113,
37973
+ file: path114,
37926
37974
  signals: grade.signals,
37927
37975
  fix: "Scope `allowed-tools` to the specific commands the skill needs (e.g. `Bash(git status:*)`); avoid a bare `Bash`, `Write` or `Edit`."
37928
37976
  }
@@ -38054,7 +38102,7 @@ function ownPackageNames(manifests) {
38054
38102
  }
38055
38103
  return out;
38056
38104
  }
38057
- function analyzeMcp(path113, content, own = /* @__PURE__ */ new Set()) {
38105
+ function analyzeMcp(path114, content, own = /* @__PURE__ */ new Set()) {
38058
38106
  let cfg;
38059
38107
  try {
38060
38108
  cfg = JSON.parse(content);
@@ -38090,9 +38138,9 @@ function analyzeMcp(path113, content, own = /* @__PURE__ */ new Set()) {
38090
38138
  const key = (count.get(name) ?? 0) > 1 ? `${section}.${name}` : name;
38091
38139
  servers[key] = { ...spec, anchor: name };
38092
38140
  }
38093
- return analyzeMcpServers(servers, path113, content, own);
38141
+ return analyzeMcpServers(servers, path114, content, own);
38094
38142
  }
38095
- function analyzeMcpServers(servers, path113, content = "", own = /* @__PURE__ */ new Set()) {
38143
+ function analyzeMcpServers(servers, path114, content = "", own = /* @__PURE__ */ new Set()) {
38096
38144
  const findings = [];
38097
38145
  for (const [name, srv] of Object.entries(servers ?? {})) {
38098
38146
  if (!srv || srv.disabled) continue;
@@ -38111,7 +38159,7 @@ function analyzeMcpServers(servers, path113, content = "", own = /* @__PURE__ */
38111
38159
  dimension: "mcp",
38112
38160
  severity: "medium",
38113
38161
  title: `MCP server "${name}" runs an unpinned executable`,
38114
- file: path113,
38162
+ file: path114,
38115
38163
  ...line ? { line } : {},
38116
38164
  signals: [
38117
38165
  why.launcher === "npx" ? `\`${safeText(argv, 120)}\`: unversioned/@latest npx` : `\`${safeText(argv, 120)}\`: unversioned/latest ${why.launcher}`
@@ -38136,7 +38184,7 @@ function analyzeMcpServers(servers, path113, content = "", own = /* @__PURE__ */
38136
38184
  dimension: "mcp",
38137
38185
  severity: "high",
38138
38186
  title: `MCP server "${name}" has an inline credential`,
38139
- file: path113,
38187
+ file: path114,
38140
38188
  ...line ? { line } : {},
38141
38189
  signals: [
38142
38190
  `env.${k} matches ${hit.patternName}: agent-reachable secret committed to the repo`
@@ -38195,7 +38243,7 @@ var init_mcp = __esm({
38195
38243
  });
38196
38244
 
38197
38245
  // src/ci-check/codex.ts
38198
- function analyzeCodexConfig(path113, content, own = /* @__PURE__ */ new Set()) {
38246
+ function analyzeCodexConfig(path114, content, own = /* @__PURE__ */ new Set()) {
38199
38247
  let cfg;
38200
38248
  try {
38201
38249
  cfg = (0, import_smol_toml6.parse)(content);
@@ -38203,7 +38251,7 @@ function analyzeCodexConfig(path113, content, own = /* @__PURE__ */ new Set()) {
38203
38251
  return [];
38204
38252
  }
38205
38253
  const findings = [];
38206
- findings.push(...analyzeMcpServers(cfg.mcp_servers ?? {}, path113, content, own));
38254
+ findings.push(...analyzeMcpServers(cfg.mcp_servers ?? {}, path114, content, own));
38207
38255
  const sandbox = typeof cfg.sandbox_mode === "string" ? cfg.sandbox_mode : "";
38208
38256
  const approval = typeof cfg.approval_policy === "string" ? cfg.approval_policy : "";
38209
38257
  const fullAccess = /danger-full-access/i.test(sandbox);
@@ -38226,7 +38274,7 @@ function analyzeCodexConfig(path113, content, own = /* @__PURE__ */ new Set()) {
38226
38274
  dimension: "toolRules",
38227
38275
  severity: fullAccess ? "high" : "medium",
38228
38276
  title: fullAccess ? "Codex config grants a full-access sandbox" : "Codex config never requires approval",
38229
- file: path113,
38277
+ file: path114,
38230
38278
  signals,
38231
38279
  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
38280
  });
@@ -38395,7 +38443,7 @@ var init_agent_permissions = __esm({
38395
38443
  init_codex();
38396
38444
  init_read_small_file();
38397
38445
  CATEGORY = "Agent permissions";
38398
- CLAUDE_NO_PROMPT_MODES = /* @__PURE__ */ new Set(["bypassPermissions", "dontAsk"]);
38446
+ CLAUDE_NO_PROMPT_MODES = /* @__PURE__ */ new Set(["bypassPermissions"]);
38399
38447
  MAX_FILE_BYTES3 = 2 * 1024 * 1024;
38400
38448
  CI_SEVERITY = {
38401
38449
  critical: "critical",
@@ -38430,6 +38478,15 @@ function checkOutsideContent(ctx) {
38430
38478
  const roles = config.policy.untrustedContent?.roles ?? {};
38431
38479
  const sources = outsideSources(home, roles);
38432
38480
  if (sources.length === 0) return [];
38481
+ const byServer = /* @__PURE__ */ new Map();
38482
+ for (const src of sources) {
38483
+ const e = byServer.get(src.server);
38484
+ if (e) {
38485
+ if (!e.agents.includes(src.agent)) e.agents.push(src.agent);
38486
+ if (src.how === "role") e.how = "role";
38487
+ } else byServer.set(src.server, { agents: [src.agent], how: src.how });
38488
+ }
38489
+ const servers = byServer.size;
38433
38490
  const egress = config.policy.egress;
38434
38491
  const egressGated = !!egress?.enabled && (egress.mode === "block" || egress.mode === "review");
38435
38492
  const hold = checkValue(
@@ -38446,11 +38503,14 @@ function checkOutsideContent(ctx) {
38446
38503
  {
38447
38504
  category: CATEGORY2,
38448
38505
  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`,
38506
+ title: `${servers} MCP server${servers === 1 ? "" : "s"} bring${servers === 1 ? "s" : ""} outside content in, and the way out is open`,
38450
38507
  what: "An agent here reads pages, issues or messages written by others, and what it does next is not held.",
38451
38508
  why: gaps.join("; ") + ".",
38452
38509
  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],
38510
+ detail: [
38511
+ ...[...byServer].slice(0, 8).map(([name, e]) => `${name} (${e.agents.join(", ")}, by ${e.how})`),
38512
+ ...gaps
38513
+ ],
38454
38514
  owner: "node9",
38455
38515
  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
38516
  }
@@ -38597,8 +38657,8 @@ var init_instruction_files = __esm({
38597
38657
  });
38598
38658
 
38599
38659
  // src/skill-pin.ts
38600
- function getPinsFilePath2() {
38601
- return import_path57.default.join(import_os46.default.homedir(), ".node9", "skill-pins.json");
38660
+ function getPinsFilePath2(home = import_os46.default.homedir()) {
38661
+ return import_path57.default.join(home, ".node9", "skill-pins.json");
38602
38662
  }
38603
38663
  function sha256Bytes(buf) {
38604
38664
  return import_crypto17.default.createHash("sha256").update(buf).digest("hex");
@@ -38668,8 +38728,8 @@ function hashSkillRoot(absPath) {
38668
38728
  function getRootKey(absPath) {
38669
38729
  return import_crypto17.default.createHash("sha256").update(absPath).digest("hex").slice(0, 16);
38670
38730
  }
38671
- function readSkillPinsSafe() {
38672
- const filePath = getPinsFilePath2();
38731
+ function readSkillPinsSafe(home) {
38732
+ const filePath = getPinsFilePath2(home);
38673
38733
  try {
38674
38734
  const raw = import_fs55.default.readFileSync(filePath, "utf-8");
38675
38735
  if (!raw.trim()) return { ok: false, reason: "corrupt", detail: "empty file" };
@@ -38853,7 +38913,7 @@ function checkSupplyChain(ctx) {
38853
38913
  }
38854
38914
  function pluginPinState(home) {
38855
38915
  const roots = defaultSkillRoots(void 0, home);
38856
- const pins = readSkillPinsSafe();
38916
+ const pins = readSkillPinsSafe(home);
38857
38917
  const pinned = pins.ok ? pins.pins.roots : {};
38858
38918
  return { roots, unpinned: roots.filter((r) => !pinned[getRootKey(r)]) };
38859
38919
  }
@@ -38866,7 +38926,7 @@ function pluginsAndPackages(ctx, home) {
38866
38926
  if (roots.length > 0 && tamper === "off") {
38867
38927
  findings.push({
38868
38928
  category: "Supply chain",
38869
- severity: "medium",
38929
+ severity: "advisory",
38870
38930
  title: `${roots.length} installed plugin${roots.length === 1 ? "" : "s"} load${roots.length === 1 ? "s" : ""} unverified`,
38871
38931
  what: "Plugins and skills the agent loads are code that runs with its rights, and no content hash is kept for them.",
38872
38932
  why: "The check loading.skill-tamper is off, so node9 does not pin them.",
@@ -38878,7 +38938,7 @@ function pluginsAndPackages(ctx, home) {
38878
38938
  } else if (unpinned.length > 0) {
38879
38939
  findings.push({
38880
38940
  category: "Supply chain",
38881
- severity: "medium",
38941
+ severity: "advisory",
38882
38942
  title: `${unpinned.length} of ${roots.length} installed plugin${roots.length === 1 ? "" : "s"} ${unpinned.length === 1 ? "has" : "have"} no pin yet`,
38883
38943
  what: "A plugin the agent can load has no recorded content hash yet.",
38884
38944
  why: "node9 pins a plugin the first time it loads; these have not loaded since pinning was turned on.",
@@ -39244,7 +39304,8 @@ function checkCoverage(ctx) {
39244
39304
  });
39245
39305
  return findings;
39246
39306
  }
39247
- const mode = getConfig(ctx.cwd).settings.mode;
39307
+ const config = getConfig(ctx.cwd);
39308
+ const mode = config.settings.mode;
39248
39309
  if (isRecordOnly(mode)) {
39249
39310
  const level = levelOf(mode);
39250
39311
  findings.push({
@@ -39259,7 +39320,6 @@ function checkCoverage(ctx) {
39259
39320
  fix: "Set mode to `standard` (or `strict`) to enforce in-path."
39260
39321
  });
39261
39322
  }
39262
- const config = getConfig(ctx.cwd);
39263
39323
  if (config.settings.autoStartDaemon !== false) {
39264
39324
  const state = autostartState();
39265
39325
  if (state === "disabled" || state === "absent") {
@@ -39580,8 +39640,6 @@ async function annotateCoverage(findings, ctx) {
39580
39640
  }
39581
39641
  if (probe.kind === "egress") {
39582
39642
  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
39643
  continue;
39586
39644
  }
39587
39645
  if (probe.kind === "gateWired") {
@@ -41318,6 +41376,7 @@ async function processClaudeFile(file2, projPath, projLabel, judge, judgeFrom, r
41318
41376
  const sessionCalls = [];
41319
41377
  const toolUseFilePaths = /* @__PURE__ */ new Map();
41320
41378
  const toolUseNames = /* @__PURE__ */ new Map();
41379
+ const toolUseInputs = /* @__PURE__ */ new Map();
41321
41380
  let firstDlpTs = null;
41322
41381
  let firstEditTs = null;
41323
41382
  for (const line of raw.split("\n")) {
@@ -41388,7 +41447,10 @@ async function processClaudeFile(file2, projPath, projLabel, judge, judgeFrom, r
41388
41447
  if (isNode9SelfOutput(resultText)) continue;
41389
41448
  const resultTool = block.tool_use_id ? toolUseNames.get(block.tool_use_id) : void 0;
41390
41449
  if (resultTool)
41391
- judge.noteToolResult(sessionId, resultTool, entry.timestamp ?? "", entry.cwd);
41450
+ judge.noteToolResult(sessionId, resultTool, entry.timestamp ?? "", entry.cwd, {
41451
+ input: block.tool_use_id ? toolUseInputs.get(block.tool_use_id) : void 0,
41452
+ output: resultText
41453
+ });
41392
41454
  const decoysHere1 = recordCanaries(
41393
41455
  { text: resultText },
41394
41456
  "tool-result",
@@ -41453,6 +41515,7 @@ async function processClaudeFile(file2, projPath, projLabel, judge, judgeFrom, r
41453
41515
  toolUseFilePaths.set(block.id, input.file_path);
41454
41516
  }
41455
41517
  if (block.id) toolUseNames.set(block.id, toolName);
41518
+ if (block.id) toolUseInputs.set(block.id, input);
41456
41519
  sessionCalls.push({ toolName, input, timestamp: entry.timestamp ?? "" });
41457
41520
  if (toolNameLower === "bash" || toolNameLower === "execute_bash") {
41458
41521
  result.bashCalls++;
@@ -44412,6 +44475,9 @@ function getDaemonServer() {
44412
44475
  function markRejectionHandlerRegistered() {
44413
44476
  daemonRejectionHandlerRegistered = true;
44414
44477
  }
44478
+ function markSignalHandlersRegistered() {
44479
+ daemonSignalHandlersRegistered = true;
44480
+ }
44415
44481
  function getActivitySocketPath() {
44416
44482
  return ACTIVITY_SOCKET_PATH;
44417
44483
  }
@@ -44635,14 +44701,23 @@ function startActivitySocket() {
44635
44701
  if (ACTIVITY_SOCKET_PATH && !import_fs65.default.existsSync(ACTIVITY_SOCKET_PATH)) attemptRebind("health-probe");
44636
44702
  }, ACTIVITY_HEALTH_PROBE_MS);
44637
44703
  activityHealthInterval.unref();
44638
- process.on("exit", () => {
44639
- if (activityHealthInterval) clearInterval(activityHealthInterval);
44640
- try {
44641
- if (ACTIVITY_SOCKET_PATH && isOwnStaleSocket(ACTIVITY_SOCKET_PATH))
44642
- import_fs65.default.unlinkSync(ACTIVITY_SOCKET_PATH);
44643
- } catch {
44644
- }
44645
- });
44704
+ process.on("exit", stopActivitySocket);
44705
+ }
44706
+ function stopActivitySocket() {
44707
+ if (activityHealthInterval) clearInterval(activityHealthInterval);
44708
+ activityHealthInterval = null;
44709
+ const socketPath = ACTIVITY_SOCKET_PATH;
44710
+ const bound = activitySocketIdentity;
44711
+ ACTIVITY_SOCKET_PATH = null;
44712
+ activitySocketIdentity = null;
44713
+ if (!socketPath || !bound || process.platform === "win32") return;
44714
+ try {
44715
+ const st = import_fs65.default.lstatSync(socketPath);
44716
+ if (isOwnStaleSocket(socketPath) && st.dev === bound.dev && st.ino === bound.ino)
44717
+ import_fs65.default.unlinkSync(socketPath);
44718
+ } catch {
44719
+ }
44720
+ removeOwnSocketDir(socketPath, process.env, import_os55.default.homedir());
44646
44721
  }
44647
44722
  function attemptRebind(reason) {
44648
44723
  if (!shouldRebind()) {
@@ -44776,6 +44851,8 @@ function bindActivitySocket() {
44776
44851
  if (process.platform !== "win32") {
44777
44852
  try {
44778
44853
  import_fs65.default.chmodSync(socketPath, 384);
44854
+ const st = import_fs65.default.lstatSync(socketPath);
44855
+ activitySocketIdentity = { dev: st.dev, ino: st.ino };
44779
44856
  } catch {
44780
44857
  }
44781
44858
  }
@@ -44783,7 +44860,7 @@ function bindActivitySocket() {
44783
44860
  });
44784
44861
  activitySocketServer = unixServer;
44785
44862
  }
44786
- var import_net5, import_fs65, import_path68, import_os55, import_crypto20, homeDir, DAEMON_PID_FILE, DECISIONS_FILE, AUDIT_LOG_FILE, TRUST_FILE2, GLOBAL_CONFIG_FILE, CREDENTIALS_FILE, INSIGHT_COUNTS_FILE, sseClients, cards, suggestionTracker, taintStore, sessionTaintStore, sessionExposureStore, sessionWriteStore, insightCounts, _abandonTimer, _hadBrowserClient, _daemonServer, daemonRejectionHandlerRegistered, AUTO_DENY_MS, TRUST_DURATIONS, autoStarted, ACTIVITY_SOCKET_PATH, activitySocketPathListeners, ACTIVITY_RING_SIZE, activityRing, LARGE_RESPONSE_RING_SIZE, largeResponseRing, cachedScanResult, cachedScanTs, SCAN_CACHE_TTL_MS, SECRET_KEY_RE, INPUT_PRICE_PER_1M, OUTPUT_PRICE_PER_1M, BYTES_PER_TOKEN, CRITICAL_FORENSIC_CATEGORIES, WRITE_TOOL_NAMES, ACTIVITY_REBIND_MAX_ATTEMPTS, ACTIVITY_REBIND_WINDOW_MS, ACTIVITY_HEALTH_PROBE_MS, activitySocketServer, activityHealthInterval, activityRebindAttempts, activityCircuitTripped;
44863
+ var import_net5, import_fs65, import_path68, import_os55, import_crypto20, homeDir, DAEMON_PID_FILE, DECISIONS_FILE, AUDIT_LOG_FILE, TRUST_FILE2, GLOBAL_CONFIG_FILE, CREDENTIALS_FILE, INSIGHT_COUNTS_FILE, sseClients, cards, suggestionTracker, taintStore, sessionTaintStore, sessionExposureStore, sessionWriteStore, insightCounts, _abandonTimer, _hadBrowserClient, _daemonServer, daemonRejectionHandlerRegistered, daemonSignalHandlersRegistered, AUTO_DENY_MS, TRUST_DURATIONS, autoStarted, ACTIVITY_SOCKET_PATH, activitySocketPathListeners, ACTIVITY_RING_SIZE, activityRing, LARGE_RESPONSE_RING_SIZE, largeResponseRing, cachedScanResult, cachedScanTs, SCAN_CACHE_TTL_MS, SECRET_KEY_RE, INPUT_PRICE_PER_1M, OUTPUT_PRICE_PER_1M, BYTES_PER_TOKEN, CRITICAL_FORENSIC_CATEGORIES, WRITE_TOOL_NAMES, ACTIVITY_REBIND_MAX_ATTEMPTS, ACTIVITY_REBIND_WINDOW_MS, ACTIVITY_HEALTH_PROBE_MS, activitySocketServer, activitySocketIdentity, activityHealthInterval, activityRebindAttempts, activityCircuitTripped;
44787
44864
  var init_state3 = __esm({
44788
44865
  "src/daemon/state.ts"() {
44789
44866
  "use strict";
@@ -44828,6 +44905,7 @@ var init_state3 = __esm({
44828
44905
  _hadBrowserClient = false;
44829
44906
  _daemonServer = null;
44830
44907
  daemonRejectionHandlerRegistered = false;
44908
+ daemonSignalHandlersRegistered = false;
44831
44909
  AUTO_DENY_MS = 12e4;
44832
44910
  TRUST_DURATIONS = {
44833
44911
  "30m": 30 * 6e4,
@@ -44868,6 +44946,7 @@ var init_state3 = __esm({
44868
44946
  ACTIVITY_REBIND_WINDOW_MS = 6e4;
44869
44947
  ACTIVITY_HEALTH_PROBE_MS = 2e3;
44870
44948
  activitySocketServer = null;
44949
+ activitySocketIdentity = null;
44871
44950
  activityHealthInterval = null;
44872
44951
  activityRebindAttempts = [];
44873
44952
  activityCircuitTripped = false;
@@ -45667,6 +45746,23 @@ function writeCache2(cache7) {
45667
45746
  } catch {
45668
45747
  }
45669
45748
  }
45749
+ function cacheFromSyncBody(body, etag) {
45750
+ return {
45751
+ fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
45752
+ rules: extractRules(body),
45753
+ etag,
45754
+ panicMode: body.panicMode,
45755
+ shadowMode: body.shadowMode,
45756
+ syncIntervalHours: body.syncIntervalHours,
45757
+ workspaceId: body.workspaceId,
45758
+ shields: extractShields(body),
45759
+ managedConfig: extractManagedConfig(body),
45760
+ config: extractWorkspaceConfig(body)
45761
+ };
45762
+ }
45763
+ function writePolicyCache(body, etag) {
45764
+ writeCache2(cacheFromSyncBody(body, etag));
45765
+ }
45670
45766
  async function syncOnce() {
45671
45767
  const creds = readCredentials();
45672
45768
  if (!creds) return;
@@ -45675,19 +45771,7 @@ async function syncOnce() {
45675
45771
  if (result.kind === "unchanged") {
45676
45772
  recordSyncHealth({ ok: true });
45677
45773
  } 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);
45774
+ writeCache2(cacheFromSyncBody(result.body, result.etag));
45691
45775
  recordSyncHealth({ ok: true, changed: true });
45692
45776
  }
45693
45777
  } catch (err2) {
@@ -45905,18 +45989,7 @@ async function runCloudSync() {
45905
45989
  recordSyncHealth({ ok: true });
45906
45990
  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
45991
  }
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
- };
45992
+ const cache7 = cacheFromSyncBody(result.body, result.etag);
45920
45993
  writeCache2(cache7);
45921
45994
  recordSyncHealth({ ok: true, changed: true });
45922
45995
  return { ok: true, rules: cache7.rules.length, fetchedAt: cache7.fetchedAt };
@@ -46040,6 +46113,7 @@ var init_sync = __esm({
46040
46113
  var audit_shipper_exports = {};
46041
46114
  __export(audit_shipper_exports, {
46042
46115
  AUDIT_SHIP_WATERMARK: () => AUDIT_SHIP_WATERMARK,
46116
+ OWASP_FIELDS_RETRY_MS: () => OWASP_FIELDS_RETRY_MS,
46043
46117
  _resetOwaspWireFields: () => _resetOwaspWireFields,
46044
46118
  buildBatchEndpoint: () => buildBatchEndpoint,
46045
46119
  buildWireRows: () => buildWireRows,
@@ -46052,7 +46126,7 @@ __export(audit_shipper_exports, {
46052
46126
  writeWatermark: () => writeWatermark
46053
46127
  });
46054
46128
  function _resetOwaspWireFields() {
46055
- serverRejectsOwaspFields = false;
46129
+ owaspFieldsRejectedAt = null;
46056
46130
  }
46057
46131
  function withoutOwaspFields(rows) {
46058
46132
  return rows.map((r) => {
@@ -46176,6 +46250,7 @@ async function shipOnce(deps = {}) {
46176
46250
  const auditLogPath = deps.auditLogPath ?? LOCAL_AUDIT_LOG;
46177
46251
  const watermarkPath = deps.watermarkPath ?? AUDIT_SHIP_WATERMARK;
46178
46252
  const fetchImpl = deps.fetchImpl ?? fetch;
46253
+ const now = deps.now ?? Date.now;
46179
46254
  let cloudEnabled = deps.cloudEnabled;
46180
46255
  if (cloudEnabled === void 0) {
46181
46256
  try {
@@ -46213,7 +46288,8 @@ async function shipOnce(deps = {}) {
46213
46288
  if (consumed === 0) break;
46214
46289
  for (let i = 0; i < rows.length; i += MAX_BATCH) {
46215
46290
  const slice = rows.slice(i, i + MAX_BATCH);
46216
- let batch = serverRejectsOwaspFields ? withoutOwaspFields(slice) : slice;
46291
+ const stripFields = owaspFieldsRejectedAt !== null && now() - owaspFieldsRejectedAt < OWASP_FIELDS_RETRY_MS;
46292
+ let batch = stripFields ? withoutOwaspFields(slice) : slice;
46217
46293
  const post = async (body) => {
46218
46294
  const controller = new AbortController();
46219
46295
  const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
@@ -46235,7 +46311,7 @@ async function shipOnce(deps = {}) {
46235
46311
  if (res.status === 400 && hasOwaspFields(batch)) {
46236
46312
  batch = withoutOwaspFields(batch);
46237
46313
  res = await post(batch);
46238
- if (res.ok) serverRejectsOwaspFields = true;
46314
+ if (res.ok) owaspFieldsRejectedAt = now();
46239
46315
  }
46240
46316
  if (!res.ok) throw new Error(`audit/batch HTTP ${res.status}`);
46241
46317
  shipped += batch.length;
@@ -46322,7 +46398,7 @@ function outboxBacklog(auditLogPath = LOCAL_AUDIT_LOG, watermarkPath = AUDIT_SHI
46322
46398
  };
46323
46399
  }
46324
46400
  }
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;
46401
+ 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
46402
  var init_audit_shipper = __esm({
46327
46403
  "src/daemon/audit-shipper.ts"() {
46328
46404
  "use strict";
@@ -46358,7 +46434,8 @@ var init_audit_shipper = __esm({
46358
46434
  "ssh-key"
46359
46435
  ]);
46360
46436
  OWASP_WIRE_FIELDS = ["checkId", "canary", "canaryKind"];
46361
- serverRejectsOwaspFields = false;
46437
+ owaspFieldsRejectedAt = null;
46438
+ OWASP_FIELDS_RETRY_MS = 60 * 60 * 1e3;
46362
46439
  shipperStarted = false;
46363
46440
  }
46364
46441
  });
@@ -48482,6 +48559,12 @@ data: ${JSON.stringify(item.data)}
48482
48559
  console.error(import_chalk8.default.red("[node9 daemon] unhandled rejection \u2014 keeping daemon alive:"), stack);
48483
48560
  });
48484
48561
  }
48562
+ if (!daemonSignalHandlersRegistered) {
48563
+ markSignalHandlersRegistered();
48564
+ process.once("SIGTERM", () => process.exit(0));
48565
+ process.once("SIGINT", () => process.exit(130));
48566
+ process.once("SIGHUP", () => process.exit(129));
48567
+ }
48485
48568
  const writeOwnPidFile = () => atomicWriteSync(
48486
48569
  DAEMON_PID_FILE,
48487
48570
  JSON.stringify({
@@ -48498,6 +48581,7 @@ data: ${JSON.stringify(item.data)}
48498
48581
  { mode: 384 }
48499
48582
  );
48500
48583
  server.listen(DAEMON_PORT, DAEMON_HOST, () => {
48584
+ startActivitySocket();
48501
48585
  writeOwnPidFile();
48502
48586
  onActivitySocketPathChange(() => {
48503
48587
  try {
@@ -48512,7 +48596,6 @@ data: ${JSON.stringify(item.data)}
48512
48596
  if (watchMode) {
48513
48597
  console.error(import_chalk8.default.cyan("\u{1F6F0}\uFE0F Flight Recorder active \u2014 daemon will not idle-timeout"));
48514
48598
  }
48515
- startActivitySocket();
48516
48599
  }
48517
48600
  var import_http4, import_fs72, import_path74, import_os60, import_crypto23, import_child_process5, import_chalk8;
48518
48601
  var init_server = __esm({
@@ -50892,14 +50975,14 @@ var require_util = __commonJS({
50892
50975
  }
50893
50976
  const port = url.port != null ? url.port : url.protocol === "https:" ? 443 : 80;
50894
50977
  let origin = url.origin != null ? url.origin : `${url.protocol || ""}//${url.hostname || ""}:${port}`;
50895
- let path113 = url.path != null ? url.path : `${url.pathname || ""}${url.search || ""}`;
50978
+ let path114 = url.path != null ? url.path : `${url.pathname || ""}${url.search || ""}`;
50896
50979
  if (origin[origin.length - 1] === "/") {
50897
50980
  origin = origin.slice(0, origin.length - 1);
50898
50981
  }
50899
- if (path113 && path113[0] !== "/") {
50900
- path113 = `/${path113}`;
50982
+ if (path114 && path114[0] !== "/") {
50983
+ path114 = `/${path114}`;
50901
50984
  }
50902
- return new URL(`${origin}${path113}`);
50985
+ return new URL(`${origin}${path114}`);
50903
50986
  }
50904
50987
  if (!isHttpOrHttpsPrefixed(url.origin || url.protocol)) {
50905
50988
  throw new InvalidArgumentError("Invalid URL protocol: the URL must start with `http:` or `https:`.");
@@ -51722,9 +51805,9 @@ var require_diagnostics = __commonJS({
51722
51805
  "undici:client:sendHeaders",
51723
51806
  (evt) => {
51724
51807
  const {
51725
- request: { method, path: path113, origin }
51808
+ request: { method, path: path114, origin }
51726
51809
  } = evt;
51727
- debugLog2("sending request to %s %s%s", method, origin, path113);
51810
+ debugLog2("sending request to %s %s%s", method, origin, path114);
51728
51811
  }
51729
51812
  );
51730
51813
  }
@@ -51742,14 +51825,14 @@ var require_diagnostics = __commonJS({
51742
51825
  "undici:request:headers",
51743
51826
  (evt) => {
51744
51827
  const {
51745
- request: { method, path: path113, origin },
51828
+ request: { method, path: path114, origin },
51746
51829
  response: { statusCode }
51747
51830
  } = evt;
51748
51831
  debugLog2(
51749
51832
  "received response to %s %s%s - HTTP %d",
51750
51833
  method,
51751
51834
  origin,
51752
- path113,
51835
+ path114,
51753
51836
  statusCode
51754
51837
  );
51755
51838
  }
@@ -51758,23 +51841,23 @@ var require_diagnostics = __commonJS({
51758
51841
  "undici:request:trailers",
51759
51842
  (evt) => {
51760
51843
  const {
51761
- request: { method, path: path113, origin }
51844
+ request: { method, path: path114, origin }
51762
51845
  } = evt;
51763
- debugLog2("trailers received from %s %s%s", method, origin, path113);
51846
+ debugLog2("trailers received from %s %s%s", method, origin, path114);
51764
51847
  }
51765
51848
  );
51766
51849
  diagnosticsChannel.subscribe(
51767
51850
  "undici:request:error",
51768
51851
  (evt) => {
51769
51852
  const {
51770
- request: { method, path: path113, origin },
51853
+ request: { method, path: path114, origin },
51771
51854
  error
51772
51855
  } = evt;
51773
51856
  debugLog2(
51774
51857
  "request to %s %s%s errored - %s",
51775
51858
  method,
51776
51859
  origin,
51777
- path113,
51860
+ path114,
51778
51861
  error.message
51779
51862
  );
51780
51863
  }
@@ -51890,7 +51973,7 @@ var require_request = __commonJS({
51890
51973
  var kHandler = /* @__PURE__ */ Symbol("handler");
51891
51974
  var Request = class {
51892
51975
  constructor(origin, {
51893
- path: path113,
51976
+ path: path114,
51894
51977
  method,
51895
51978
  body,
51896
51979
  headers,
@@ -51907,11 +51990,11 @@ var require_request = __commonJS({
51907
51990
  maxRedirections,
51908
51991
  typeOfService
51909
51992
  }, handler) {
51910
- if (typeof path113 !== "string") {
51993
+ if (typeof path114 !== "string") {
51911
51994
  throw new InvalidArgumentError("path must be a string");
51912
- } else if (path113[0] !== "/" && !(path113.startsWith("http://") || path113.startsWith("https://")) && method !== "CONNECT") {
51995
+ } else if (path114[0] !== "/" && !(path114.startsWith("http://") || path114.startsWith("https://")) && method !== "CONNECT") {
51913
51996
  throw new InvalidArgumentError("path must be an absolute URL or start with a slash");
51914
- } else if (invalidPathRegex.test(path113)) {
51997
+ } else if (invalidPathRegex.test(path114)) {
51915
51998
  throw new InvalidArgumentError("invalid request path");
51916
51999
  }
51917
52000
  if (typeof method !== "string") {
@@ -51986,7 +52069,7 @@ var require_request = __commonJS({
51986
52069
  this.completed = false;
51987
52070
  this.aborted = false;
51988
52071
  this.upgrade = upgrade || null;
51989
- this.path = query ? serializePathWithQuery(path113, query) : path113;
52072
+ this.path = query ? serializePathWithQuery(path114, query) : path114;
51990
52073
  this.origin = origin;
51991
52074
  this.protocol = getProtocolFromUrlString(origin);
51992
52075
  this.idempotent = idempotent == null ? method === "HEAD" || method === "GET" : idempotent;
@@ -57233,7 +57316,7 @@ var require_client_h1 = __commonJS({
57233
57316
  return method !== "GET" && method !== "HEAD" && method !== "OPTIONS" && method !== "TRACE" && method !== "CONNECT";
57234
57317
  }
57235
57318
  function writeH1(client, request2) {
57236
- const { method, path: path113, host: host2, upgrade, blocking, reset } = request2;
57319
+ const { method, path: path114, host: host2, upgrade, blocking, reset } = request2;
57237
57320
  let { body, headers, contentLength } = request2;
57238
57321
  const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH" || method === "QUERY" || method === "PROPFIND" || method === "PROPPATCH";
57239
57322
  if (util.isFormDataLike(body)) {
@@ -57311,7 +57394,7 @@ var require_client_h1 = __commonJS({
57311
57394
  if (socket.setTypeOfService) {
57312
57395
  socket.setTypeOfService(request2.typeOfService);
57313
57396
  }
57314
- let header = `${method} ${path113} HTTP/1.1\r
57397
+ let header = `${method} ${path114} HTTP/1.1\r
57315
57398
  `;
57316
57399
  if (typeof host2 === "string") {
57317
57400
  header += `host: ${host2}\r
@@ -57989,7 +58072,7 @@ var require_client_h2 = __commonJS({
57989
58072
  const headersTimeout = request2.headersTimeout ?? client[kHeadersTimeout];
57990
58073
  const bodyTimeout = request2.bodyTimeout ?? client[kBodyTimeout];
57991
58074
  const session = client[kHTTP2Session];
57992
- const { method, path: path113, host: host2, upgrade, expectContinue, signal, protocol, headers: reqHeaders } = request2;
58075
+ const { method, path: path114, host: host2, upgrade, expectContinue, signal, protocol, headers: reqHeaders } = request2;
57993
58076
  let { body } = request2;
57994
58077
  if (upgrade != null && upgrade !== "websocket") {
57995
58078
  util.errorRequest(client, request2, new InvalidArgumentError(`Custom upgrade "${upgrade}" not supported over HTTP/2`));
@@ -58058,7 +58141,7 @@ var require_client_h2 = __commonJS({
58058
58141
  }
58059
58142
  headers[HTTP2_HEADER_METHOD] = "CONNECT";
58060
58143
  headers[HTTP2_HEADER_PROTOCOL] = "websocket";
58061
- headers[HTTP2_HEADER_PATH] = path113;
58144
+ headers[HTTP2_HEADER_PATH] = path114;
58062
58145
  if (protocol === "ws:" || protocol === "wss:") {
58063
58146
  headers[HTTP2_HEADER_SCHEME] = protocol === "ws:" ? "http" : "https";
58064
58147
  } else {
@@ -58110,7 +58193,7 @@ var require_client_h2 = __commonJS({
58110
58193
  stream.setTimeout(headersTimeout);
58111
58194
  return true;
58112
58195
  }
58113
- headers[HTTP2_HEADER_PATH] = path113;
58196
+ headers[HTTP2_HEADER_PATH] = path114;
58114
58197
  headers[HTTP2_HEADER_SCHEME] = protocol === "http:" ? "http" : "https";
58115
58198
  const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH";
58116
58199
  if (body && typeof body.read === "function") {
@@ -60477,10 +60560,10 @@ var require_proxy_agent = __commonJS({
60477
60560
  };
60478
60561
  const {
60479
60562
  origin,
60480
- path: path113 = "/",
60563
+ path: path114 = "/",
60481
60564
  headers = {}
60482
60565
  } = opts;
60483
- opts.path = origin + path113;
60566
+ opts.path = origin + path114;
60484
60567
  if (!("host" in headers) && !("Host" in headers)) {
60485
60568
  const { host: host2 } = new URL(origin);
60486
60569
  headers.host = host2;
@@ -62655,20 +62738,20 @@ var require_mock_utils = __commonJS({
62655
62738
  }
62656
62739
  return normalizedQp;
62657
62740
  }
62658
- function safeUrl(path113) {
62659
- if (typeof path113 !== "string") {
62660
- return path113;
62741
+ function safeUrl(path114) {
62742
+ if (typeof path114 !== "string") {
62743
+ return path114;
62661
62744
  }
62662
- const pathSegments = path113.split("?", 3);
62745
+ const pathSegments = path114.split("?", 3);
62663
62746
  if (pathSegments.length !== 2) {
62664
- return path113;
62747
+ return path114;
62665
62748
  }
62666
62749
  const qp = new URLSearchParams(pathSegments.pop());
62667
62750
  qp.sort();
62668
62751
  return [...pathSegments, qp.toString()].join("?");
62669
62752
  }
62670
- function matchKey(mockDispatch2, { path: path113, method, body, headers }) {
62671
- const pathMatch = matchValue(mockDispatch2.path, path113);
62753
+ function matchKey(mockDispatch2, { path: path114, method, body, headers }) {
62754
+ const pathMatch = matchValue(mockDispatch2.path, path114);
62672
62755
  const methodMatch = matchValue(mockDispatch2.method, method);
62673
62756
  const bodyMatch = typeof mockDispatch2.body !== "undefined" ? matchValue(mockDispatch2.body, body) : true;
62674
62757
  const headersMatch = matchHeaders(mockDispatch2, headers);
@@ -62693,8 +62776,8 @@ var require_mock_utils = __commonJS({
62693
62776
  const basePath = key.query ? serializePathWithQuery(key.path, key.query) : key.path;
62694
62777
  const resolvedPath = typeof basePath === "string" ? safeUrl(basePath) : basePath;
62695
62778
  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);
62779
+ let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path: path114, ignoreTrailingSlash }) => {
62780
+ return ignoreTrailingSlash ? matchValue(removeTrailingSlash(safeUrl(path114)), resolvedPathWithoutTrailingSlash) : matchValue(safeUrl(path114), resolvedPath);
62698
62781
  });
62699
62782
  if (matchedMockDispatches.length === 0) {
62700
62783
  throw new MockNotMatchedError(`Mock dispatch not matched for path '${resolvedPath}'`);
@@ -62733,19 +62816,19 @@ var require_mock_utils = __commonJS({
62733
62816
  mockDispatches.splice(index, 1);
62734
62817
  }
62735
62818
  }
62736
- function removeTrailingSlash(path113) {
62737
- while (path113.endsWith("/")) {
62738
- path113 = path113.slice(0, -1);
62819
+ function removeTrailingSlash(path114) {
62820
+ while (path114.endsWith("/")) {
62821
+ path114 = path114.slice(0, -1);
62739
62822
  }
62740
- if (path113.length === 0) {
62741
- path113 = "/";
62823
+ if (path114.length === 0) {
62824
+ path114 = "/";
62742
62825
  }
62743
- return path113;
62826
+ return path114;
62744
62827
  }
62745
62828
  function buildKey(opts) {
62746
- const { path: path113, method, body, headers, query } = opts;
62829
+ const { path: path114, method, body, headers, query } = opts;
62747
62830
  return {
62748
- path: path113,
62831
+ path: path114,
62749
62832
  method,
62750
62833
  body,
62751
62834
  headers,
@@ -63440,10 +63523,10 @@ var require_pending_interceptors_formatter = __commonJS({
63440
63523
  }
63441
63524
  format(pendingInterceptors) {
63442
63525
  const withPrettyHeaders = pendingInterceptors.map(
63443
- ({ method, path: path113, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
63526
+ ({ method, path: path114, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
63444
63527
  Method: method,
63445
63528
  Origin: origin,
63446
- Path: path113,
63529
+ Path: path114,
63447
63530
  "Status code": statusCode,
63448
63531
  Persistent: persist ? PERSISTENT : NOT_PERSISTENT,
63449
63532
  Invocations: timesInvoked,
@@ -63526,9 +63609,9 @@ var require_mock_agent = __commonJS({
63526
63609
  const acceptNonStandardSearchParameters = this[kMockAgentAcceptsNonStandardSearchParameters];
63527
63610
  const dispatchOpts = { ...opts };
63528
63611
  if (acceptNonStandardSearchParameters && dispatchOpts.path) {
63529
- const [path113, searchParams] = dispatchOpts.path.split("?");
63612
+ const [path114, searchParams] = dispatchOpts.path.split("?");
63530
63613
  const normalizedSearchParams = normalizeSearchParams(searchParams, acceptNonStandardSearchParameters);
63531
- dispatchOpts.path = `${path113}?${normalizedSearchParams}`;
63614
+ dispatchOpts.path = `${path114}?${normalizedSearchParams}`;
63532
63615
  }
63533
63616
  return this[kAgent].dispatch(dispatchOpts, handler);
63534
63617
  }
@@ -63931,12 +64014,12 @@ var require_snapshot_recorder = __commonJS({
63931
64014
  * @return {Promise<void>} - Resolves when snapshots are loaded
63932
64015
  */
63933
64016
  async loadSnapshots(filePath) {
63934
- const path113 = filePath || this.#snapshotPath;
63935
- if (!path113) {
64017
+ const path114 = filePath || this.#snapshotPath;
64018
+ if (!path114) {
63936
64019
  throw new InvalidArgumentError("Snapshot path is required");
63937
64020
  }
63938
64021
  try {
63939
- const data = await readFile(resolve2(path113), "utf8");
64022
+ const data = await readFile(resolve2(path114), "utf8");
63940
64023
  const parsed = JSON.parse(data);
63941
64024
  if (Array.isArray(parsed)) {
63942
64025
  this.#snapshots.clear();
@@ -63950,7 +64033,7 @@ var require_snapshot_recorder = __commonJS({
63950
64033
  if (error.code === "ENOENT") {
63951
64034
  this.#snapshots.clear();
63952
64035
  } else {
63953
- throw new UndiciError(`Failed to load snapshots from ${path113}`, { cause: error });
64036
+ throw new UndiciError(`Failed to load snapshots from ${path114}`, { cause: error });
63954
64037
  }
63955
64038
  }
63956
64039
  }
@@ -63961,11 +64044,11 @@ var require_snapshot_recorder = __commonJS({
63961
64044
  * @returns {Promise<void>} - Resolves when snapshots are saved
63962
64045
  */
63963
64046
  async saveSnapshots(filePath) {
63964
- const path113 = filePath || this.#snapshotPath;
63965
- if (!path113) {
64047
+ const path114 = filePath || this.#snapshotPath;
64048
+ if (!path114) {
63966
64049
  throw new InvalidArgumentError("Snapshot path is required");
63967
64050
  }
63968
- const resolvedPath = resolve2(path113);
64051
+ const resolvedPath = resolve2(path114);
63969
64052
  await mkdir(dirname3(resolvedPath), { recursive: true });
63970
64053
  const data = Array.from(this.#snapshots.entries()).map(([hash2, snapshot]) => ({
63971
64054
  hash: hash2,
@@ -64606,15 +64689,15 @@ var require_redirect_handler = __commonJS({
64606
64689
  }
64607
64690
  this.history[kRedirectCacheControl].push(headers["cache-control"]);
64608
64691
  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}`;
64692
+ const path114 = search ? `${pathname}${search}` : pathname;
64693
+ const redirectUrlString = `${origin}${path114}`;
64611
64694
  for (const historyUrl of this.history) {
64612
64695
  if (historyUrl.toString() === redirectUrlString) {
64613
64696
  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
64697
  }
64615
64698
  }
64616
64699
  this.opts.headers = cleanRequestHeaders(this.opts.headers, statusCode === 303, this.opts.origin !== origin);
64617
- this.opts.path = path113;
64700
+ this.opts.path = path114;
64618
64701
  this.opts.origin = origin;
64619
64702
  this.opts.query = null;
64620
64703
  }
@@ -66390,10 +66473,10 @@ var require_cache_handler = __commonJS({
66390
66473
  }
66391
66474
  return locationUrl.pathname + locationUrl.search;
66392
66475
  }
66393
- function deleteCachedUri(store, cacheKey, path113) {
66476
+ function deleteCachedUri(store, cacheKey, path114) {
66394
66477
  deleteCachedValue(store, {
66395
66478
  ...cacheKey,
66396
- path: path113
66479
+ path: path114
66397
66480
  });
66398
66481
  for (let i = 0; i < util.safeHTTPMethods.length; i++) {
66399
66482
  const method = util.safeHTTPMethods[i];
@@ -66401,7 +66484,7 @@ var require_cache_handler = __commonJS({
66401
66484
  deleteCachedValue(store, {
66402
66485
  ...cacheKey,
66403
66486
  method,
66404
- path: path113
66487
+ path: path114
66405
66488
  });
66406
66489
  }
66407
66490
  }
@@ -66412,9 +66495,9 @@ var require_cache_handler = __commonJS({
66412
66495
  }
66413
66496
  const values = Array.isArray(headerValue) ? headerValue : [headerValue];
66414
66497
  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);
66498
+ const path114 = getSameOriginPath(cacheKey, values[i]);
66499
+ if (path114 !== void 0) {
66500
+ deleteCachedUri(store, cacheKey, path114);
66418
66501
  }
66419
66502
  }
66420
66503
  }
@@ -71637,11 +71720,11 @@ var require_fetch = __commonJS({
71637
71720
  function dispatch({ body }) {
71638
71721
  const url = requestCurrentURL(request2);
71639
71722
  const agent = fetchParams.controller.dispatcher;
71640
- const path113 = url.pathname + url.search;
71723
+ const path114 = url.pathname + url.search;
71641
71724
  const hasTrailingQuestionMark = url.search.length === 0 && url.href[url.href.length - url.hash.length - 1] === "?";
71642
71725
  return new Promise((resolve2, reject) => agent.dispatch(
71643
71726
  {
71644
- path: hasTrailingQuestionMark ? `${path113}?` : path113,
71727
+ path: hasTrailingQuestionMark ? `${path114}?` : path114,
71645
71728
  origin: url.origin,
71646
71729
  method: request2.method,
71647
71730
  body: agent.isMockActive ? request2.body && (request2.body.source || request2.body.stream) : body,
@@ -72593,9 +72676,9 @@ var require_util4 = __commonJS({
72593
72676
  }
72594
72677
  }
72595
72678
  }
72596
- function validateCookiePath(path113) {
72597
- for (let i = 0; i < path113.length; ++i) {
72598
- const code2 = path113.charCodeAt(i);
72679
+ function validateCookiePath(path114) {
72680
+ for (let i = 0; i < path114.length; ++i) {
72681
+ const code2 = path114.charCodeAt(i);
72599
72682
  if (code2 < 32 || // exclude CTLs (0-31)
72600
72683
  code2 > 126 || // exclude DEL and non-ascii
72601
72684
  code2 === 59) {
@@ -76048,11 +76131,11 @@ var require_undici = __commonJS({
76048
76131
  if (typeof opts.path !== "string") {
76049
76132
  throw new InvalidArgumentError("invalid opts.path");
76050
76133
  }
76051
- let path113 = opts.path;
76134
+ let path114 = opts.path;
76052
76135
  if (!opts.path.startsWith("/")) {
76053
- path113 = `/${path113}`;
76136
+ path114 = `/${path114}`;
76054
76137
  }
76055
- url = new URL(util.parseOrigin(url).origin + path113);
76138
+ url = new URL(util.parseOrigin(url).origin + path114);
76056
76139
  } else {
76057
76140
  if (!opts) {
76058
76141
  opts = typeof url === "object" ? url : {};
@@ -76174,10 +76257,10 @@ function readFirstRunEnv() {
76174
76257
  stdinTTY: !!process.stdin.isTTY,
76175
76258
  stdoutTTY: !!process.stdout.isTTY,
76176
76259
  ci: isCI() || process.env.NODE9_NONINTERACTIVE === "1",
76177
- hasConfig: import_fs106.default.existsSync(import_path105.default.join(import_os86.default.homedir(), ".node9/config.json")),
76260
+ hasConfig: import_fs107.default.existsSync(import_path106.default.join(import_os87.default.homedir(), ".node9/config.json")),
76178
76261
  // Any stored profile means this is not a never-configured machine. Treat
76179
76262
  // 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"))
76263
+ hasCredentials: !!getCredentials() || import_fs107.default.existsSync(import_path106.default.join(import_os87.default.homedir(), ".node9/credentials.json"))
76181
76264
  };
76182
76265
  }
76183
76266
  function shouldOfferFirstRun(env) {
@@ -76282,14 +76365,14 @@ async function runSetupWizard(opts) {
76282
76365
  process.exitCode = 130;
76283
76366
  }
76284
76367
  }
76285
- var import_fs106, import_os86, import_path105;
76368
+ var import_fs107, import_os87, import_path106;
76286
76369
  var init_first_run = __esm({
76287
76370
  "src/cli/first-run.ts"() {
76288
76371
  "use strict";
76289
76372
  init_cjs_shims();
76290
- import_fs106 = __toESM(require("fs"));
76291
- import_os86 = __toESM(require("os"));
76292
- import_path105 = __toESM(require("path"));
76373
+ import_fs107 = __toESM(require("fs"));
76374
+ import_os87 = __toESM(require("os"));
76375
+ import_path106 = __toESM(require("path"));
76293
76376
  init_config();
76294
76377
  init_agent_wiring();
76295
76378
  init_browser_login();
@@ -76335,20 +76418,20 @@ function getModelContextLimit(model) {
76335
76418
  return 2e5;
76336
76419
  }
76337
76420
  function readSessionUsage() {
76338
- const projectsDir = import_path106.default.join(import_os87.default.homedir(), ".claude", "projects");
76339
- if (!import_fs107.default.existsSync(projectsDir)) return null;
76421
+ const projectsDir = import_path107.default.join(import_os88.default.homedir(), ".claude", "projects");
76422
+ if (!import_fs108.default.existsSync(projectsDir)) return null;
76340
76423
  let latestFile = null;
76341
76424
  let latestMtime = 0;
76342
76425
  try {
76343
- for (const dir2 of import_fs107.default.readdirSync(projectsDir)) {
76344
- const dirPath = import_path106.default.join(projectsDir, dir2);
76426
+ for (const dir2 of import_fs108.default.readdirSync(projectsDir)) {
76427
+ const dirPath = import_path107.default.join(projectsDir, dir2);
76345
76428
  try {
76346
- if (!import_fs107.default.statSync(dirPath).isDirectory()) continue;
76347
- for (const file2 of import_fs107.default.readdirSync(dirPath)) {
76429
+ if (!import_fs108.default.statSync(dirPath).isDirectory()) continue;
76430
+ for (const file2 of import_fs108.default.readdirSync(dirPath)) {
76348
76431
  if (!file2.endsWith(".jsonl") || file2.startsWith("agent-")) continue;
76349
- const filePath = import_path106.default.join(dirPath, file2);
76432
+ const filePath = import_path107.default.join(dirPath, file2);
76350
76433
  try {
76351
- const mtime = import_fs107.default.statSync(filePath).mtimeMs;
76434
+ const mtime = import_fs108.default.statSync(filePath).mtimeMs;
76352
76435
  if (mtime > latestMtime) {
76353
76436
  latestMtime = mtime;
76354
76437
  latestFile = filePath;
@@ -76363,7 +76446,7 @@ function readSessionUsage() {
76363
76446
  }
76364
76447
  if (!latestFile) return null;
76365
76448
  try {
76366
- const lines = import_fs107.default.readFileSync(latestFile, "utf-8").split("\n");
76449
+ const lines = import_fs108.default.readFileSync(latestFile, "utf-8").split("\n");
76367
76450
  let lastModel = "";
76368
76451
  let lastInput = 0;
76369
76452
  let lastOutput = 0;
@@ -76424,7 +76507,7 @@ function formatBase(activity) {
76424
76507
  const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
76425
76508
  const icon = getIcon(activity.tool);
76426
76509
  const toolName = activity.tool.slice(0, 16).padEnd(16);
76427
- const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(import_os87.default.homedir(), "~");
76510
+ const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(import_os88.default.homedir(), "~");
76428
76511
  const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
76429
76512
  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
76513
  }
@@ -76442,12 +76525,12 @@ function renderResult(activity, result) {
76442
76525
  const costSuffix = cost == null ? "" : import_chalk49.default.dim(` ~$${cost >= 1e-3 ? cost.toFixed(3) : "0.000"}`);
76443
76526
  if (process.stdout.isTTY) {
76444
76527
  if (pendingShownForId === activity.id && pendingWrappedLines > 1) {
76445
- import_readline5.default.moveCursor(process.stdout, 0, -(pendingWrappedLines - 1));
76446
- import_readline5.default.cursorTo(process.stdout, 0);
76528
+ import_readline6.default.moveCursor(process.stdout, 0, -(pendingWrappedLines - 1));
76529
+ import_readline6.default.cursorTo(process.stdout, 0);
76447
76530
  process.stdout.write(ERASE_DOWN);
76448
76531
  } else {
76449
- import_readline5.default.clearLine(process.stdout, 0);
76450
- import_readline5.default.cursorTo(process.stdout, 0);
76532
+ import_readline6.default.clearLine(process.stdout, 0);
76533
+ import_readline6.default.cursorTo(process.stdout, 0);
76451
76534
  }
76452
76535
  pendingShownForId = null;
76453
76536
  pendingWrappedLines = 0;
@@ -76463,9 +76546,9 @@ function renderPending(activity) {
76463
76546
  }
76464
76547
  async function ensureDaemon() {
76465
76548
  let pidPort = null;
76466
- if (import_fs107.default.existsSync(PID_FILE)) {
76549
+ if (import_fs108.default.existsSync(PID_FILE)) {
76467
76550
  try {
76468
- const { port } = JSON.parse(import_fs107.default.readFileSync(PID_FILE, "utf-8"));
76551
+ const { port } = JSON.parse(import_fs108.default.readFileSync(PID_FILE, "utf-8"));
76469
76552
  pidPort = port;
76470
76553
  } catch {
76471
76554
  console.error(import_chalk49.default.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
@@ -76491,7 +76574,7 @@ async function ensureDaemon() {
76491
76574
  child.unref();
76492
76575
  if (startupFd !== void 0) {
76493
76576
  try {
76494
- import_fs107.default.closeSync(startupFd);
76577
+ import_fs108.default.closeSync(startupFd);
76495
76578
  } catch {
76496
76579
  }
76497
76580
  }
@@ -76735,7 +76818,7 @@ async function startTail(options = {}) {
76735
76818
  let cancelActiveCard = null;
76736
76819
  const localAllowCounts = /* @__PURE__ */ new Map();
76737
76820
  const canApprove = process.stdout.isTTY && process.stdin.isTTY;
76738
- if (canApprove) import_readline5.default.emitKeypressEvents(process.stdin);
76821
+ if (canApprove) import_readline6.default.emitKeypressEvents(process.stdin);
76739
76822
  let idleKeypressHandler = null;
76740
76823
  function enterIdleMode() {
76741
76824
  if (!canApprove || idleKeypressHandler !== null) return;
@@ -76776,7 +76859,7 @@ async function startTail(options = {}) {
76776
76859
  }
76777
76860
  function clearCard() {
76778
76861
  if (cardLineCount > 0) {
76779
- import_readline5.default.moveCursor(process.stdout, 0, -cardLineCount);
76862
+ import_readline6.default.moveCursor(process.stdout, 0, -cardLineCount);
76780
76863
  process.stdout.write(ERASE_DOWN);
76781
76864
  cardLineCount = 0;
76782
76865
  }
@@ -76861,8 +76944,8 @@ async function startTail(options = {}) {
76861
76944
  );
76862
76945
  }).catch((err2) => {
76863
76946
  try {
76864
- import_fs107.default.appendFileSync(
76865
- import_path106.default.join(import_os87.default.homedir(), ".node9", "hook-debug.log"),
76947
+ import_fs108.default.appendFileSync(
76948
+ import_path107.default.join(import_os88.default.homedir(), ".node9", "hook-debug.log"),
76866
76949
  `[tail] POST /decision failed: ${String(err2)}
76867
76950
  `
76868
76951
  );
@@ -76928,9 +77011,9 @@ async function startTail(options = {}) {
76928
77011
  };
76929
77012
  process.stdin.on("keypress", onKeypress);
76930
77013
  }
76931
- const auditLog = import_path106.default.join(import_os87.default.homedir(), ".node9", "audit.log");
77014
+ const auditLog = import_path107.default.join(import_os88.default.homedir(), ".node9", "audit.log");
76932
77015
  try {
76933
- const unackedDlp = import_fs107.default.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
77016
+ const unackedDlp = import_fs108.default.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
76934
77017
  if (unackedDlp > 0) {
76935
77018
  console.log("");
76936
77019
  console.log(
@@ -76959,8 +77042,8 @@ async function startTail(options = {}) {
76959
77042
  clearCard();
76960
77043
  process.stdout.write(SHOW_CURSOR);
76961
77044
  if (process.stdout.isTTY) {
76962
- import_readline5.default.clearLine(process.stdout, 0);
76963
- import_readline5.default.cursorTo(process.stdout, 0);
77045
+ import_readline6.default.clearLine(process.stdout, 0);
77046
+ import_readline6.default.cursorTo(process.stdout, 0);
76964
77047
  }
76965
77048
  console.log(import_chalk49.default.dim("\n\u{1F6F0}\uFE0F Disconnected."));
76966
77049
  process.exit(0);
@@ -76970,7 +77053,7 @@ async function startTail(options = {}) {
76970
77053
  if (stallWarned) return;
76971
77054
  if (Date.now() - lastActivityFromDaemon < STALL_THRESHOLD_MS) return;
76972
77055
  try {
76973
- const auditMtime = import_fs107.default.statSync(auditLog).mtimeMs;
77056
+ const auditMtime = import_fs108.default.statSync(auditLog).mtimeMs;
76974
77057
  if (Date.now() - auditMtime >= STALL_THRESHOLD_MS) return;
76975
77058
  console.log("");
76976
77059
  console.log(
@@ -76999,7 +77082,7 @@ async function startTail(options = {}) {
76999
77082
  let currentData = "";
77000
77083
  res.on("error", () => {
77001
77084
  });
77002
- const rl = import_readline5.default.createInterface({ input: res, crlfDelay: Infinity });
77085
+ const rl = import_readline6.default.createInterface({ input: res, crlfDelay: Infinity });
77003
77086
  rl.on("error", () => {
77004
77087
  });
77005
77088
  rl.on("line", (line) => {
@@ -77019,8 +77102,8 @@ async function startTail(options = {}) {
77019
77102
  clearCard();
77020
77103
  process.stdout.write(SHOW_CURSOR);
77021
77104
  if (process.stdout.isTTY) {
77022
- import_readline5.default.clearLine(process.stdout, 0);
77023
- import_readline5.default.cursorTo(process.stdout, 0);
77105
+ import_readline6.default.clearLine(process.stdout, 0);
77106
+ import_readline6.default.cursorTo(process.stdout, 0);
77024
77107
  }
77025
77108
  console.log(import_chalk49.default.red("\n\u274C Daemon disconnected."));
77026
77109
  process.exit(1);
@@ -77142,7 +77225,7 @@ async function startTail(options = {}) {
77142
77225
  process.exit(1);
77143
77226
  });
77144
77227
  }
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;
77228
+ 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
77229
  var init_tail = __esm({
77147
77230
  "src/tui/tail.ts"() {
77148
77231
  "use strict";
@@ -77150,16 +77233,16 @@ var init_tail = __esm({
77150
77233
  import_http6 = __toESM(require("http"));
77151
77234
  init_write();
77152
77235
  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"));
77236
+ import_fs108 = __toESM(require("fs"));
77237
+ import_os88 = __toESM(require("os"));
77238
+ import_path107 = __toESM(require("path"));
77239
+ import_readline6 = __toESM(require("readline"));
77157
77240
  import_child_process17 = require("child_process");
77158
77241
  init_startup_log();
77159
77242
  init_daemon3();
77160
77243
  init_daemon();
77161
77244
  init_safe_text();
77162
- PID_FILE = import_path106.default.join(import_os87.default.homedir(), ".node9", "daemon.pid");
77245
+ PID_FILE = import_path107.default.join(import_os88.default.homedir(), ".node9", "daemon.pid");
77163
77246
  ICONS = {
77164
77247
  bash: "\u{1F4BB}",
77165
77248
  shell: "\u{1F4BB}",
@@ -77281,9 +77364,9 @@ function formatTimeLeft(resetsAt) {
77281
77364
  return ` (${m}m left)`;
77282
77365
  }
77283
77366
  function safeReadJson(filePath) {
77284
- if (!import_fs108.default.existsSync(filePath)) return null;
77367
+ if (!import_fs109.default.existsSync(filePath)) return null;
77285
77368
  try {
77286
- return JSON.parse(import_fs108.default.readFileSync(filePath, "utf-8"));
77369
+ return JSON.parse(import_fs109.default.readFileSync(filePath, "utf-8"));
77287
77370
  } catch {
77288
77371
  return null;
77289
77372
  }
@@ -77304,12 +77387,12 @@ function countHooksInFile(filePath) {
77304
77387
  return Object.keys(cfg.hooks).length;
77305
77388
  }
77306
77389
  function countRulesInDir(rulesDir) {
77307
- if (!import_fs108.default.existsSync(rulesDir)) return 0;
77390
+ if (!import_fs109.default.existsSync(rulesDir)) return 0;
77308
77391
  let count = 0;
77309
77392
  try {
77310
- for (const entry of import_fs108.default.readdirSync(rulesDir, { withFileTypes: true })) {
77393
+ for (const entry of import_fs109.default.readdirSync(rulesDir, { withFileTypes: true })) {
77311
77394
  if (entry.isDirectory()) {
77312
- count += countRulesInDir(import_path107.default.join(rulesDir, entry.name));
77395
+ count += countRulesInDir(import_path108.default.join(rulesDir, entry.name));
77313
77396
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
77314
77397
  count++;
77315
77398
  }
@@ -77320,46 +77403,46 @@ function countRulesInDir(rulesDir) {
77320
77403
  }
77321
77404
  function isSamePath(a, b) {
77322
77405
  try {
77323
- return import_path107.default.resolve(a) === import_path107.default.resolve(b);
77406
+ return import_path108.default.resolve(a) === import_path108.default.resolve(b);
77324
77407
  } catch {
77325
77408
  return false;
77326
77409
  }
77327
77410
  }
77328
77411
  function countConfigs(cwd) {
77329
- const homeDir2 = import_os88.default.homedir();
77330
- const claudeDir = import_path107.default.join(homeDir2, ".claude");
77412
+ const homeDir2 = import_os89.default.homedir();
77413
+ const claudeDir = import_path108.default.join(homeDir2, ".claude");
77331
77414
  let claudeMdCount = 0;
77332
77415
  let rulesCount = 0;
77333
77416
  let hooksCount = 0;
77334
77417
  const userMcpServers = /* @__PURE__ */ new Set();
77335
77418
  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");
77419
+ if (import_fs109.default.existsSync(import_path108.default.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
77420
+ rulesCount += countRulesInDir(import_path108.default.join(claudeDir, "rules"));
77421
+ const userSettings = import_path108.default.join(claudeDir, "settings.json");
77339
77422
  for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
77340
77423
  hooksCount += countHooksInFile(userSettings);
77341
- const userClaudeJson = import_path107.default.join(homeDir2, ".claude.json");
77424
+ const userClaudeJson = import_path108.default.join(homeDir2, ".claude.json");
77342
77425
  for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
77343
77426
  for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
77344
77427
  userMcpServers.delete(name);
77345
77428
  }
77346
77429
  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");
77430
+ if (import_fs109.default.existsSync(import_path108.default.join(cwd, "CLAUDE.md"))) claudeMdCount++;
77431
+ if (import_fs109.default.existsSync(import_path108.default.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
77432
+ const projectClaudeDir = import_path108.default.join(cwd, ".claude");
77350
77433
  const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
77351
77434
  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");
77435
+ if (import_fs109.default.existsSync(import_path108.default.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
77436
+ rulesCount += countRulesInDir(import_path108.default.join(projectClaudeDir, "rules"));
77437
+ const projSettings = import_path108.default.join(projectClaudeDir, "settings.json");
77355
77438
  for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
77356
77439
  hooksCount += countHooksInFile(projSettings);
77357
77440
  }
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");
77441
+ if (import_fs109.default.existsSync(import_path108.default.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
77442
+ const localSettings = import_path108.default.join(projectClaudeDir, "settings.local.json");
77360
77443
  for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
77361
77444
  hooksCount += countHooksInFile(localSettings);
77362
- const mcpJsonServers = getMcpServerNames(import_path107.default.join(cwd, ".mcp.json"));
77445
+ const mcpJsonServers = getMcpServerNames(import_path108.default.join(cwd, ".mcp.json"));
77363
77446
  const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
77364
77447
  for (const name of disabledMcpJson) mcpJsonServers.delete(name);
77365
77448
  for (const name of mcpJsonServers) projectMcpServers.add(name);
@@ -77392,12 +77475,12 @@ function readActiveShieldsHud() {
77392
77475
  return shieldsCache.value;
77393
77476
  }
77394
77477
  try {
77395
- const shieldsPath = import_path107.default.join(import_os88.default.homedir(), ".node9", "shields.json");
77396
- if (!import_fs108.default.existsSync(shieldsPath)) {
77478
+ const shieldsPath = import_path108.default.join(import_os89.default.homedir(), ".node9", "shields.json");
77479
+ if (!import_fs109.default.existsSync(shieldsPath)) {
77397
77480
  shieldsCache = { value: [], ts: now };
77398
77481
  return [];
77399
77482
  }
77400
- const parsed = JSON.parse(import_fs108.default.readFileSync(shieldsPath, "utf-8"));
77483
+ const parsed = JSON.parse(import_fs109.default.readFileSync(shieldsPath, "utf-8"));
77401
77484
  if (!Array.isArray(parsed.active)) {
77402
77485
  shieldsCache = { value: [], ts: now };
77403
77486
  return [];
@@ -77499,17 +77582,17 @@ function renderContextLine(stdin) {
77499
77582
  async function main() {
77500
77583
  try {
77501
77584
  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"))) {
77585
+ if (import_fs109.default.existsSync(import_path108.default.join(import_os89.default.homedir(), ".node9", "hud-debug"))) {
77503
77586
  try {
77504
- const logPath = import_path107.default.join(import_os88.default.homedir(), ".node9", "hud-debug.log");
77587
+ const logPath = import_path108.default.join(import_os89.default.homedir(), ".node9", "hud-debug.log");
77505
77588
  const MAX_LOG_SIZE = 10 * 1024 * 1024;
77506
77589
  let size = 0;
77507
77590
  try {
77508
- size = import_fs108.default.statSync(logPath).size;
77591
+ size = import_fs109.default.statSync(logPath).size;
77509
77592
  } catch {
77510
77593
  }
77511
77594
  if (size < MAX_LOG_SIZE) {
77512
- import_fs108.default.appendFileSync(
77595
+ import_fs109.default.appendFileSync(
77513
77596
  logPath,
77514
77597
  JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
77515
77598
  );
@@ -77530,11 +77613,11 @@ async function main() {
77530
77613
  try {
77531
77614
  const cwd = stdin.cwd ?? process.cwd();
77532
77615
  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")
77616
+ import_path108.default.join(cwd, "node9.config.json"),
77617
+ import_path108.default.join(import_os89.default.homedir(), ".node9", "config.json")
77535
77618
  ]) {
77536
- if (!import_fs108.default.existsSync(configPath2)) continue;
77537
- const cfg = JSON.parse(import_fs108.default.readFileSync(configPath2, "utf-8"));
77619
+ if (!import_fs109.default.existsSync(configPath2)) continue;
77620
+ const cfg = JSON.parse(import_fs109.default.readFileSync(configPath2, "utf-8"));
77538
77621
  const hud = cfg.settings?.hud;
77539
77622
  if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
77540
77623
  }
@@ -77552,14 +77635,14 @@ async function main() {
77552
77635
  renderOffline();
77553
77636
  }
77554
77637
  }
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;
77638
+ 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
77639
  var init_hud = __esm({
77557
77640
  "src/cli/hud.ts"() {
77558
77641
  "use strict";
77559
77642
  init_cjs_shims();
77560
- import_fs108 = __toESM(require("fs"));
77561
- import_path107 = __toESM(require("path"));
77562
- import_os88 = __toESM(require("os"));
77643
+ import_fs109 = __toESM(require("fs"));
77644
+ import_path108 = __toESM(require("path"));
77645
+ import_os89 = __toESM(require("os"));
77563
77646
  import_http7 = __toESM(require("http"));
77564
77647
  init_daemon();
77565
77648
  init_dist();
@@ -77624,9 +77707,9 @@ init_agent_wiring();
77624
77707
  init_credentials();
77625
77708
  init_daemon3();
77626
77709
  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"));
77710
+ var import_fs110 = __toESM(require("fs"));
77711
+ var import_path109 = __toESM(require("path"));
77712
+ var import_os90 = __toESM(require("os"));
77630
77713
  var import_prompts4 = require("@inquirer/prompts");
77631
77714
 
77632
77715
  // src/utils/duration.ts
@@ -78697,6 +78780,7 @@ init_audit();
78697
78780
  init_config();
78698
78781
  init_daemon();
78699
78782
  init_dist();
78783
+ init_shell_fetch();
78700
78784
 
78701
78785
  // src/exposure/source.ts
78702
78786
  init_cjs_shims();
@@ -78921,11 +79005,6 @@ function buildArgsField(rawInput) {
78921
79005
  return hashed;
78922
79006
  }
78923
79007
  }
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
79008
  function registerLogCommand(program2) {
78930
79009
  program2.command("log", { hidden: true }).description("PostToolUse hook \u2014 records executed tool calls").argument("[data]", "JSON string of the tool call").option(
78931
79010
  "--agent <name>",
@@ -85566,6 +85645,374 @@ function registerAgentsCommand(program2) {
85566
85645
  // src/cli.ts
85567
85646
  init_scan();
85568
85647
 
85648
+ // src/cli/commands/forecast-cases.ts
85649
+ init_cjs_shims();
85650
+ var import_fs96 = __toESM(require("fs"));
85651
+ var import_os80 = __toESM(require("os"));
85652
+ var import_path95 = __toESM(require("path"));
85653
+ var import_readline5 = __toESM(require("readline"));
85654
+ init_dist();
85655
+ init_config();
85656
+ init_forecast();
85657
+ init_scan_judge();
85658
+ init_dlp();
85659
+ init_jail();
85660
+ init_registry2();
85661
+ init_version();
85662
+ init_sync();
85663
+ function materialize(t, env) {
85664
+ if (typeof t === "string")
85665
+ return t.replaceAll("{{HOME}}", env.home).replaceAll("{{CWD}}", env.cwd);
85666
+ if (t === null || typeof t !== "object") return t;
85667
+ if (Array.isArray(t)) return t.map((x) => materialize(x, env));
85668
+ const o = t;
85669
+ const keys = Object.keys(o);
85670
+ if (keys.length === 1 && keys[0] === "$join" && Array.isArray(o.$join))
85671
+ return materialize(o.$join.join(""), env);
85672
+ const out = {};
85673
+ for (const [k, v] of Object.entries(o)) out[k] = materialize(v, env);
85674
+ return out;
85675
+ }
85676
+ var PROFILE_NEEDS = {
85677
+ clean: null,
85678
+ "asb-sandbox": null,
85679
+ "git-guardrails": null,
85680
+ "jailed-agent": "user-jail-path",
85681
+ "user-jail": "user-jail-path",
85682
+ "org-jail": "managed-jail-path",
85683
+ canary: "decoy",
85684
+ "egress-lock": "egress-block",
85685
+ "egress-watch": "egress-gated",
85686
+ "exposure-org": "organisation-domain",
85687
+ "ask-before-dangerous-actions": "hold-action-checks",
85688
+ "injection-optin": "injection-scan"
85689
+ };
85690
+ function profileNeed(setup) {
85691
+ if (setup in PROFILE_NEEDS) return PROFILE_NEEDS[setup];
85692
+ if (setup.startsWith("egress-deny-")) return "egress-deny-list";
85693
+ return void 0;
85694
+ }
85695
+ function homeHas(config) {
85696
+ const settings = catalogSettingsFrom(config.settings, config.policy);
85697
+ const value = (id) => checkValue(settings, id);
85698
+ const egress = config.policy.egress;
85699
+ const gated = !!egress?.enabled && (egress.mode === "block" || egress.mode === "review");
85700
+ let userJail = false;
85701
+ try {
85702
+ userJail = readJailPaths().length > 0;
85703
+ } catch {
85704
+ userJail = false;
85705
+ }
85706
+ let decoy = false;
85707
+ try {
85708
+ decoy = loadCanaries().length > 0;
85709
+ } catch {
85710
+ decoy = false;
85711
+ }
85712
+ const held = (id) => value(id) === "review" || value(id) === "block";
85713
+ return {
85714
+ "user-jail-path": userJail,
85715
+ "managed-jail-path": (config.policy.managedJailPaths ?? []).length > 0,
85716
+ decoy,
85717
+ "egress-block": !!egress?.enabled && egress.mode === "block",
85718
+ "egress-gated": gated,
85719
+ "egress-deny-list": gated && (egress?.deny?.length ?? 0) > 0,
85720
+ "organisation-domain": (config.policy.untrustedContent?.orgDomains ?? []).length > 0,
85721
+ "hold-action-checks": held("actions.money") && held("actions.access") && held("actions.publish"),
85722
+ "injection-scan": value("behavior.prompt-injection") !== "off"
85723
+ };
85724
+ }
85725
+ var emptyCell = () => ({
85726
+ attacks: 0,
85727
+ stopped: 0,
85728
+ loggedOnly: 0,
85729
+ passed: 0,
85730
+ benign: 0,
85731
+ interrupted: 0,
85732
+ notApplicable: 0,
85733
+ notSimulated: 0,
85734
+ volatile: 0,
85735
+ undetermined: 0,
85736
+ errors: 0
85737
+ });
85738
+ function summarize(results, config) {
85739
+ const settings = catalogSettingsFrom(config.settings, config.policy);
85740
+ const byRisk = {};
85741
+ const passedByCheck = {};
85742
+ const passedUnattributed = {};
85743
+ const notApplicableBy = {};
85744
+ const cell = (r) => {
85745
+ const risk = byRisk[r.owasp] ??= {
85746
+ headline: emptyCell(),
85747
+ vendor: emptyCell(),
85748
+ unknown: emptyCell()
85749
+ };
85750
+ return risk[r.section];
85751
+ };
85752
+ for (const r of results) {
85753
+ const c = cell(r);
85754
+ if (r.error) {
85755
+ c.errors++;
85756
+ continue;
85757
+ }
85758
+ if (r.notApplicable) {
85759
+ c.notApplicable++;
85760
+ notApplicableBy[r.notApplicable] = (notApplicableBy[r.notApplicable] ?? 0) + 1;
85761
+ continue;
85762
+ }
85763
+ if (r.notSimulated) {
85764
+ c.notSimulated++;
85765
+ continue;
85766
+ }
85767
+ if (r.volatile) {
85768
+ c.volatile++;
85769
+ continue;
85770
+ }
85771
+ const stopped = r.enforced === true && (r.decision === "review" || r.decision === "block");
85772
+ const recorded = !stopped && r.decision !== void 0 && r.decision !== "allow";
85773
+ if (r.expected === "allow") {
85774
+ c.benign++;
85775
+ if (stopped) c.interrupted++;
85776
+ continue;
85777
+ }
85778
+ const notEnforcedStop = r.enforced === false && (r.decision === "review" || r.decision === "block");
85779
+ if (!stopped && !notEnforcedStop && (r.unknown?.length ?? 0) > 0) {
85780
+ c.undetermined++;
85781
+ continue;
85782
+ }
85783
+ c.attacks++;
85784
+ if (stopped) c.stopped++;
85785
+ else if (recorded) {
85786
+ c.loggedOnly++;
85787
+ if (r.checkId) {
85788
+ const e = passedByCheck[r.checkId] ??= {
85789
+ value: checkValue(settings, r.checkId),
85790
+ count: 0
85791
+ };
85792
+ e.count++;
85793
+ } else passedUnattributed[r.owasp] = (passedUnattributed[r.owasp] ?? 0) + 1;
85794
+ } else {
85795
+ c.passed++;
85796
+ passedUnattributed[r.owasp] = (passedUnattributed[r.owasp] ?? 0) + 1;
85797
+ }
85798
+ }
85799
+ return {
85800
+ engineVersion: PROXY_VERSION,
85801
+ mode: config.settings.mode ?? "standard",
85802
+ policySource: config.policySource,
85803
+ cases: results.length,
85804
+ byRisk,
85805
+ passedByCheck,
85806
+ passedUnattributed,
85807
+ notApplicableBy
85808
+ };
85809
+ }
85810
+ function toolOf(payload, fallback) {
85811
+ return typeof payload.tool_name === "string" && payload.tool_name ? payload.tool_name : fallback;
85812
+ }
85813
+ function notePostCall(judge, sessionId, tool, input, output, at, cwd) {
85814
+ const m = scanArgs(output);
85815
+ if (m) judge.noteOutputSecret(sessionId, m.patternName, at);
85816
+ judge.noteToolResult(sessionId, tool, at, cwd, {
85817
+ input,
85818
+ output: collectToolOutputText(output).text
85819
+ });
85820
+ }
85821
+ async function runCase(judge, rec, env, has2, at, online = false) {
85822
+ const base3 = {
85823
+ id: rec.id,
85824
+ owasp: isAsiId(rec.owasp) ? rec.owasp : "unknown",
85825
+ section: rec.section === "headline" || rec.section === "vendor" ? rec.section : "unknown",
85826
+ setup: rec.setup,
85827
+ expected: rec.expected,
85828
+ ...rec.volatile && { volatile: true }
85829
+ };
85830
+ const need = profileNeed(rec.setup);
85831
+ if (need && !has2[need]) return { ...base3, notApplicable: need };
85832
+ if (rec.stage === "post") return { ...base3, notSimulated: "post-stage" };
85833
+ const sessionId = `forecast-cases:${rec.id}`;
85834
+ const cwd = env.cwd;
85835
+ const config = judge.configFor(cwd);
85836
+ const meta = { agent: "claude", sessionId };
85837
+ for (const p of rec.prior ?? []) {
85838
+ const payload2 = materialize(p.input, env) ?? {};
85839
+ const tool = toolOf(payload2, rec.tool);
85840
+ if (p.stage === "post") {
85841
+ notePostCall(judge, sessionId, tool, payload2.tool_input, materialize(p.output, env), at, cwd);
85842
+ continue;
85843
+ }
85844
+ await forecastDecision(tool, payload2.tool_input ?? {}, meta, {
85845
+ cwd,
85846
+ config,
85847
+ session: judge.session(sessionId),
85848
+ at: Date.parse(at),
85849
+ ...online && { onlinePackages: true }
85850
+ });
85851
+ }
85852
+ const payload = materialize(rec.input, env) ?? {};
85853
+ const v = await forecastDecision(toolOf(payload, rec.tool), payload.tool_input ?? {}, meta, {
85854
+ cwd,
85855
+ config,
85856
+ session: judge.session(sessionId),
85857
+ at: Date.parse(at),
85858
+ ...online && { onlinePackages: true }
85859
+ });
85860
+ return {
85861
+ ...base3,
85862
+ decision: v.decision,
85863
+ enforced: v.enforced,
85864
+ ...v.checkId && { checkId: v.checkId },
85865
+ layer: v.layer,
85866
+ ...v.notEnforcedBecause && { notEnforcedBecause: v.notEnforcedBecause },
85867
+ ...v.unknown.length > 0 && { unknown: v.unknown }
85868
+ };
85869
+ }
85870
+ function materializeProfileFiles(profile, env) {
85871
+ for (const f of profile.files ?? []) {
85872
+ const target = materialize(f.path, env);
85873
+ const rel = import_path95.default.relative(env.home, target);
85874
+ if (rel.startsWith("..") || import_path95.default.isAbsolute(rel)) continue;
85875
+ import_fs96.default.mkdirSync(import_path95.default.dirname(target), { recursive: true });
85876
+ const content = materialize(f.content, env);
85877
+ import_fs96.default.writeFileSync(target, typeof content === "string" ? content : JSON.stringify(content));
85878
+ }
85879
+ }
85880
+ var POLICY_HOME_GUARDS = ["rules-cache.json", "credentials.json"];
85881
+ var PolicyHomeTaken = class extends Error {
85882
+ };
85883
+ async function runForecastCases(opts) {
85884
+ const env = {
85885
+ home: opts.home,
85886
+ cwd: opts.cwd ?? import_path95.default.join(opts.home, "bench-project")
85887
+ };
85888
+ import_fs96.default.mkdirSync(env.cwd, { recursive: true });
85889
+ if (opts.policy) {
85890
+ const taken = POLICY_HOME_GUARDS.filter(
85891
+ (f) => import_fs96.default.existsSync(import_path95.default.join(opts.home, ".node9", f))
85892
+ );
85893
+ if (taken.length > 0)
85894
+ throw new PolicyHomeTaken(
85895
+ `--policy needs a fresh HOME; ${opts.home} already has ${taken.map((f) => `.node9/${f}`).join(" and ")}`
85896
+ );
85897
+ writePolicyCache(opts.policy, opts.policy.etag);
85898
+ if (!process.env.NODE9_API_KEY) process.env.NODE9_API_KEY = "forecast-cases";
85899
+ }
85900
+ const judge = new HistoryJudge();
85901
+ const config = judge.configFor(env.cwd);
85902
+ const has2 = homeHas(config);
85903
+ const profiles = /* @__PURE__ */ new Map();
85904
+ const profileOf = (name) => {
85905
+ if (!opts.profilesDir) return null;
85906
+ if (profiles.has(name)) return profiles.get(name);
85907
+ let p = null;
85908
+ try {
85909
+ p = JSON.parse(
85910
+ import_fs96.default.readFileSync(import_path95.default.join(opts.profilesDir, `${name}.json`), "utf8")
85911
+ );
85912
+ materializeProfileFiles(p, env);
85913
+ } catch {
85914
+ p = null;
85915
+ }
85916
+ profiles.set(name, p);
85917
+ return p;
85918
+ };
85919
+ const results = [];
85920
+ const rl = import_readline5.default.createInterface({
85921
+ input: import_fs96.default.createReadStream(opts.casesPath, { encoding: "utf8" }),
85922
+ crlfDelay: Infinity
85923
+ });
85924
+ for await (const line of rl) {
85925
+ if (!line.trim()) continue;
85926
+ let rec;
85927
+ try {
85928
+ rec = JSON.parse(line);
85929
+ } catch {
85930
+ continue;
85931
+ }
85932
+ if (!rec || typeof rec.id !== "string") continue;
85933
+ profileOf(rec.setup);
85934
+ let result;
85935
+ try {
85936
+ result = await runCase(judge, rec, env, has2, opts.at, opts.onlinePackages === true);
85937
+ } catch (err2) {
85938
+ result = {
85939
+ id: rec.id,
85940
+ owasp: isAsiId(rec.owasp) ? rec.owasp : "unknown",
85941
+ section: rec.section === "headline" || rec.section === "vendor" ? rec.section : "unknown",
85942
+ setup: rec.setup,
85943
+ expected: rec.expected,
85944
+ error: err2 instanceof Error ? err2.message : String(err2)
85945
+ };
85946
+ }
85947
+ results.push(result);
85948
+ opts.write(JSON.stringify({ case: result }));
85949
+ }
85950
+ const summary = summarize(results, config);
85951
+ opts.write(JSON.stringify({ summary }));
85952
+ return summary;
85953
+ }
85954
+ function registerForecastCasesCommand(program2) {
85955
+ program2.command("forecast-cases <cases>", { hidden: true }).description(
85956
+ "Judge a file of benchmark cases through the forecast entry under this HOME; JSON per case, then a summary"
85957
+ ).option(
85958
+ "--profiles <dir>",
85959
+ "The benchmark profiles directory: its fixture files are created under HOME"
85960
+ ).option("--at <iso>", "The clock every case is judged at", "2026-01-01T00:00:00.000Z").option(
85961
+ "--online-packages",
85962
+ "Let the package check query the advisory service and the registries, as the hook does (default: offline)"
85963
+ ).option(
85964
+ "--cwd <dir>",
85965
+ "The project directory the cases are judged in ({{CWD}}); default HOME/bench-project"
85966
+ ).option(
85967
+ "--policy <file>",
85968
+ "A cloud policy sync response body (JSON), written as this HOME's rules cache first"
85969
+ ).action(
85970
+ async (cases, opts) => {
85971
+ const casesPath = import_path95.default.resolve(cases);
85972
+ if (!import_fs96.default.existsSync(casesPath)) {
85973
+ console.error(`forecast-cases: no such file: ${casesPath}`);
85974
+ process.exitCode = 2;
85975
+ return;
85976
+ }
85977
+ let policy;
85978
+ if (opts.policy) {
85979
+ const parsed = readPolicyBody(import_path95.default.resolve(opts.policy));
85980
+ if (!parsed) {
85981
+ console.error(`forecast-cases: --policy is not a JSON object: ${opts.policy}`);
85982
+ process.exitCode = 2;
85983
+ return;
85984
+ }
85985
+ policy = parsed;
85986
+ }
85987
+ try {
85988
+ await runForecastCases({
85989
+ casesPath,
85990
+ profilesDir: opts.profiles ? import_path95.default.resolve(opts.profiles) : void 0,
85991
+ home: import_os80.default.homedir(),
85992
+ cwd: opts.cwd ? import_path95.default.resolve(opts.cwd) : void 0,
85993
+ policy,
85994
+ onlinePackages: opts.onlinePackages === true,
85995
+ at: opts.at,
85996
+ write: (line) => process.stdout.write(line + "\n")
85997
+ });
85998
+ } catch (err2) {
85999
+ if (!(err2 instanceof PolicyHomeTaken)) throw err2;
86000
+ console.error(`forecast-cases: ${err2.message}`);
86001
+ process.exitCode = 2;
86002
+ }
86003
+ }
86004
+ );
86005
+ }
86006
+ function readPolicyBody(file2) {
86007
+ try {
86008
+ const v = JSON.parse(import_fs96.default.readFileSync(file2, "utf8"));
86009
+ if (!v || typeof v !== "object" || Array.isArray(v)) return void 0;
86010
+ return v;
86011
+ } catch {
86012
+ return void 0;
86013
+ }
86014
+ }
86015
+
85569
86016
  // src/cli/commands/posture.ts
85570
86017
  init_cjs_shims();
85571
86018
  var import_chalk35 = __toESM(require("chalk"));
@@ -85605,8 +86052,8 @@ init_cjs_shims();
85605
86052
 
85606
86053
  // src/ci-check/fetch.ts
85607
86054
  init_cjs_shims();
85608
- var import_fs96 = __toESM(require("fs"));
85609
- var import_path95 = __toESM(require("path"));
86055
+ var import_fs97 = __toESM(require("fs"));
86056
+ var import_path96 = __toESM(require("path"));
85610
86057
  var import_node_child_process = require("child_process");
85611
86058
  var import_undici = __toESM(require_undici());
85612
86059
 
@@ -85616,18 +86063,18 @@ init_instructions();
85616
86063
  init_suppress();
85617
86064
  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
86065
  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";
86066
+ function routeOf(path114, skillDirs, appDirs = /* @__PURE__ */ new Set()) {
86067
+ if (path114 === SUPPRESSIONS_FILE) return "suppressions";
86068
+ if (/\.github\/workflows\/.+\.ya?ml$/.test(path114)) return "workflow";
86069
+ if (ISSUE_FORM_RE.test(path114)) return "issue-form";
86070
+ if (path114 === "package.json" || path114 === "pyproject.toml") return "manifest";
86071
+ if (/\.claude\/settings(\.local)?\.json$/.test(path114)) return "agent-config";
86072
+ if (isHookScript(path114)) return "hook-script";
86073
+ if (isSkillScript(path114, skillDirs)) return "skill-script";
86074
+ if (MCP_CONFIG_RE.test(path114)) return "mcp";
86075
+ if (/(^|\/)\.codex\/config\.toml$/.test(path114)) return "codex";
86076
+ if (isInstructionFile(path114, skillDirs, appDirs)) return "instruction";
86077
+ if (isAppDoc(path114, skillDirs, appDirs)) return "app-doc";
85631
86078
  return null;
85632
86079
  }
85633
86080
 
@@ -85757,7 +86204,7 @@ function parseRepoUrl(input) {
85757
86204
  function isLocalPath(input) {
85758
86205
  if (input.startsWith(".") || input.startsWith("/") || input.startsWith("~")) return true;
85759
86206
  try {
85760
- return import_fs96.default.existsSync(input) && import_fs96.default.statSync(input).isDirectory();
86207
+ return import_fs97.default.existsSync(input) && import_fs97.default.statSync(input).isDirectory();
85761
86208
  } catch {
85762
86209
  return false;
85763
86210
  }
@@ -86118,7 +86565,7 @@ function capNested(reads, cap, notes) {
86118
86565
  }
86119
86566
  var overLimit = (text) => Buffer.byteLength(text, "utf8") > MAX_FILE_BYTES5;
86120
86567
  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);
86568
+ var OPEN_READ = import_fs97.default.constants.O_RDONLY | (import_fs97.default.constants.O_NOFOLLOW ?? 0) | (import_fs97.default.constants.O_NONBLOCK ?? 0);
86122
86569
  var MAX_WALK_ENTRIES = 1e6;
86123
86570
  var direntKind = (e) => e.isSymbolicLink() ? "link" : e.isDirectory() ? "dir" : e.isFile() ? "file" : "other";
86124
86571
  function walkDisk(root, notes) {
@@ -86133,7 +86580,7 @@ function walkDisk(root, notes) {
86133
86580
  const relDir = stack.pop();
86134
86581
  let dirents;
86135
86582
  try {
86136
- dirents = import_fs96.default.readdirSync(import_path95.default.join(root, relDir), { withFileTypes: true });
86583
+ dirents = import_fs97.default.readdirSync(import_path96.default.join(root, relDir), { withFileTypes: true });
86137
86584
  } catch (e) {
86138
86585
  unlistable.push(`${relDir || "."} (${e?.code ?? "error"})`);
86139
86586
  continue;
@@ -86147,7 +86594,7 @@ function walkDisk(root, notes) {
86147
86594
  const k = direntKind(e);
86148
86595
  kinds2.set(rel, k);
86149
86596
  if (k !== "dir" || isUnwalked(`${rel}/`)) continue;
86150
- if (import_fs96.default.existsSync(import_path95.default.join(root, rel, ".git"))) {
86597
+ if (import_fs97.default.existsSync(import_path96.default.join(root, rel, ".git"))) {
86151
86598
  sealed.add(rel);
86152
86599
  nestedRepos.push(rel);
86153
86600
  } else stack.push(rel);
@@ -86170,7 +86617,7 @@ function walkDisk(root, notes) {
86170
86617
  }
86171
86618
  function gitmodulePaths(root) {
86172
86619
  try {
86173
- const text = readLocalFile(import_path95.default.join(root, ".gitmodules"), ".gitmodules");
86620
+ const text = readLocalFile(import_path96.default.join(root, ".gitmodules"), ".gitmodules");
86174
86621
  return [...text.matchAll(/^\s*path\s*=\s*(.+?)\s*$/gm)].map((m) => m[1].replace(/\/+$/, ""));
86175
86622
  } catch {
86176
86623
  return [];
@@ -86178,9 +86625,9 @@ function gitmodulePaths(root) {
86178
86625
  }
86179
86626
  function localLinkText(root, rel) {
86180
86627
  try {
86181
- const t = import_fs96.default.readlinkSync(import_path95.default.join(root, rel));
86628
+ const t = import_fs97.default.readlinkSync(import_path96.default.join(root, rel));
86182
86629
  if (process.platform !== "win32") return t;
86183
- return import_path95.default.win32.isAbsolute(t) ? `/${t.replace(/\\/g, "/")}` : t.replace(/\\/g, "/");
86630
+ return import_path96.default.win32.isAbsolute(t) ? `/${t.replace(/\\/g, "/")}` : t.replace(/\\/g, "/");
86184
86631
  } catch {
86185
86632
  return null;
86186
86633
  }
@@ -86188,11 +86635,11 @@ function localLinkText(root, rel) {
86188
86635
  function readLocalFile(abs, rel) {
86189
86636
  let fd;
86190
86637
  try {
86191
- fd = import_fs96.default.openSync(abs, OPEN_READ);
86192
- const st = import_fs96.default.fstatSync(fd);
86638
+ fd = import_fs97.default.openSync(abs, OPEN_READ);
86639
+ const st = import_fs97.default.fstatSync(fd);
86193
86640
  if (!st.isFile()) throw new Error(unreadNote(rel, "no longer a regular file"));
86194
86641
  if (st.size > RAW_LIMIT) throw new Error(tooLargeNote(rel));
86195
- const text = lf(import_fs96.default.readFileSync(fd, "utf8"));
86642
+ const text = lf(import_fs97.default.readFileSync(fd, "utf8"));
86196
86643
  if (overLimit(text)) throw new Error(tooLargeNote(rel));
86197
86644
  return text;
86198
86645
  } catch (e) {
@@ -86200,7 +86647,7 @@ function readLocalFile(abs, rel) {
86200
86647
  if (msg.includes(INCOMPLETE)) throw e;
86201
86648
  throw new Error(unreadNote(rel, e?.code ?? "error"));
86202
86649
  } finally {
86203
- if (fd !== void 0) import_fs96.default.closeSync(fd);
86650
+ if (fd !== void 0) import_fs97.default.closeSync(fd);
86204
86651
  }
86205
86652
  }
86206
86653
  function localGitHubRepo(root) {
@@ -86221,10 +86668,10 @@ function readLocalTree(dir2) {
86221
86668
  notes.push(...plan.notes);
86222
86669
  const files = [];
86223
86670
  for (const { rel, real, route } of plan.reads) {
86224
- const abs = import_path95.default.join(root, real);
86671
+ const abs = import_path96.default.join(root, real);
86225
86672
  let size = 0;
86226
86673
  try {
86227
- size = import_fs96.default.lstatSync(abs).size;
86674
+ size = import_fs97.default.lstatSync(abs).size;
86228
86675
  } catch {
86229
86676
  }
86230
86677
  if (size > RAW_LIMIT) {
@@ -87386,17 +87833,17 @@ function localTarget(uses, self) {
87386
87833
  if (!f) return null;
87387
87834
  return f[1].toLowerCase() === self.owner.toLowerCase() && f[2].toLowerCase() === self.repo.toLowerCase() ? `.github/workflows/${f[3]}` : null;
87388
87835
  }
87389
- function localCallsOf(path113, content, autoLabels, trustedRunSources, self) {
87836
+ function localCallsOf(path114, content, autoLabels, trustedRunSources, self) {
87390
87837
  AUTO_LABELS = autoLabels ?? /* @__PURE__ */ new Set();
87391
87838
  TRUSTED_RUN_SOURCES = trustedRunSources ?? /* @__PURE__ */ new Set();
87392
87839
  try {
87393
- return localCallsOfInner(path113, content, self);
87840
+ return localCallsOfInner(path114, content, self);
87394
87841
  } finally {
87395
87842
  AUTO_LABELS = /* @__PURE__ */ new Set();
87396
87843
  TRUSTED_RUN_SOURCES = /* @__PURE__ */ new Set();
87397
87844
  }
87398
87845
  }
87399
- function localCallsOfInner(path113, content, self) {
87846
+ function localCallsOfInner(path114, content, self) {
87400
87847
  let raw;
87401
87848
  try {
87402
87849
  raw = loadWorkflow(content).raw;
@@ -87414,7 +87861,7 @@ function localCallsOfInner(path113, content, self) {
87414
87861
  out.push({
87415
87862
  target,
87416
87863
  caller: {
87417
- file: path113,
87864
+ file: path114,
87418
87865
  keys: reach.keys,
87419
87866
  untrusted: reach.untrusted,
87420
87867
  secretExposed: reach.secretExposed,
@@ -87650,7 +88097,7 @@ function upstreamRunTrusted(wf, raw) {
87650
88097
  const names = Array.isArray(run?.workflows) ? run.workflows.map(String) : [];
87651
88098
  return names.length > 0 && names.every((n) => TRUSTED_RUN_SOURCES.has(n));
87652
88099
  }
87653
- function runSourceOf(path113, content, autoLabels) {
88100
+ function runSourceOf(path114, content, autoLabels) {
87654
88101
  AUTO_LABELS = autoLabels ?? /* @__PURE__ */ new Set();
87655
88102
  try {
87656
88103
  let raw;
@@ -87663,7 +88110,7 @@ function runSourceOf(path113, content, autoLabels) {
87663
88110
  const wf = raw;
87664
88111
  CALLERS.delete(wf);
87665
88112
  const reach = triggerReach(wf, raw);
87666
- const name = typeof raw["name"] === "string" && raw["name"] ? raw["name"] : path113;
88113
+ const name = typeof raw["name"] === "string" && raw["name"] ? raw["name"] : path114;
87667
88114
  return { name, trusted: !reach.untrusted && !reach.reusable };
87668
88115
  } finally {
87669
88116
  AUTO_LABELS = /* @__PURE__ */ new Set();
@@ -88133,18 +88580,18 @@ function severityFromScore(score) {
88133
88580
  if (score >= 1) return "advisory";
88134
88581
  return null;
88135
88582
  }
88136
- function analyzeWorkflow(path113, content, callers, autoLabels, trustedRunSources) {
88583
+ function analyzeWorkflow(path114, content, callers, autoLabels, trustedRunSources) {
88137
88584
  AUTO_LABELS = autoLabels ?? /* @__PURE__ */ new Set();
88138
88585
  TRUSTED_RUN_SOURCES = trustedRunSources ?? /* @__PURE__ */ new Set();
88139
88586
  try {
88140
- return analyzeWorkflowInner(path113, content, callers);
88587
+ return analyzeWorkflowInner(path114, content, callers);
88141
88588
  } finally {
88142
88589
  AUTO_LABELS = /* @__PURE__ */ new Set();
88143
88590
  TRUSTED_RUN_SOURCES = /* @__PURE__ */ new Set();
88144
88591
  UPSTREAM_TRUSTED = false;
88145
88592
  }
88146
88593
  }
88147
- function analyzeWorkflowInner(path113, content, callers) {
88594
+ function analyzeWorkflowInner(path114, content, callers) {
88148
88595
  let raw;
88149
88596
  try {
88150
88597
  raw = loadWorkflow(content).raw;
@@ -88353,7 +88800,7 @@ function analyzeWorkflowInner(path113, content, callers) {
88353
88800
  dimension: "workflows",
88354
88801
  severity,
88355
88802
  title,
88356
- file: path113,
88803
+ file: path114,
88357
88804
  signals,
88358
88805
  mitigations: mitigations.length ? mitigations : void 0,
88359
88806
  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 +88904,18 @@ function evalAgentJob(job, wf, raw, untrustedTrigger, reusable) {
88457
88904
  if (reusable && !loadedGun && SEVERITY_RANK2[severity] > SEVERITY_RANK2.medium) severity = "medium";
88458
88905
  return { severity, secrets, injectable, canReadEnv };
88459
88906
  }
88460
- function analyzeWorkflowSecrets(path113, content, callers, autoLabels, trustedRunSources) {
88907
+ function analyzeWorkflowSecrets(path114, content, callers, autoLabels, trustedRunSources) {
88461
88908
  AUTO_LABELS = autoLabels ?? /* @__PURE__ */ new Set();
88462
88909
  TRUSTED_RUN_SOURCES = trustedRunSources ?? /* @__PURE__ */ new Set();
88463
88910
  try {
88464
- return analyzeWorkflowSecretsInner(path113, content, callers);
88911
+ return analyzeWorkflowSecretsInner(path114, content, callers);
88465
88912
  } finally {
88466
88913
  AUTO_LABELS = /* @__PURE__ */ new Set();
88467
88914
  TRUSTED_RUN_SOURCES = /* @__PURE__ */ new Set();
88468
88915
  UPSTREAM_TRUSTED = false;
88469
88916
  }
88470
88917
  }
88471
- function analyzeWorkflowSecretsInner(path113, content, callers) {
88918
+ function analyzeWorkflowSecretsInner(path114, content, callers) {
88472
88919
  let raw;
88473
88920
  try {
88474
88921
  raw = loadWorkflow(content).raw;
@@ -88495,7 +88942,7 @@ function analyzeWorkflowSecretsInner(path113, content, callers) {
88495
88942
  dimension: "data",
88496
88943
  severity: worst.severity,
88497
88944
  title: worst.severity === "advisory" ? "Secrets reachable by the agent: hardening" : "Exfiltratable secrets reachable by an injectable agent",
88498
- file: path113,
88945
+ file: path114,
88499
88946
  signals: [
88500
88947
  `agent can reach: ${worst.secrets.map((s) => s.name).join(", ")}`,
88501
88948
  worst.injectable ? "the agent is externally triggerable (untrusted trigger, no gate)" : "gated / not externally triggerable: latent risk only",
@@ -88517,7 +88964,7 @@ function marketplaceOwner(market) {
88517
88964
  market
88518
88965
  )?.[1] ?? /^([\w.-]{1,100})\/[\w.-]{1,100}(?:[#@].*)?$/.exec(market)?.[1];
88519
88966
  }
88520
- function analyzeWorkflowPlugins(path113, content, owner) {
88967
+ function analyzeWorkflowPlugins(path114, content, owner) {
88521
88968
  let raw;
88522
88969
  try {
88523
88970
  raw = loadWorkflow(content).raw;
@@ -88547,7 +88994,7 @@ function analyzeWorkflowPlugins(path113, content, owner) {
88547
88994
  dimension: "mcp",
88548
88995
  severity: "medium",
88549
88996
  title: "Agent installs plugins from a third-party marketplace on every run",
88550
- file: path113,
88997
+ file: path114,
88551
88998
  signals: [
88552
88999
  `\`plugin_marketplaces: ${safeText(market, 120)}\` is not pinned to a commit: whoever controls it changes the code and prompts the agent runs`
88553
89000
  ],
@@ -88563,11 +89010,11 @@ function readsItemList(text) {
88563
89010
  if (text.length > 2e5) return ITEM_LIST_RE.test(text);
88564
89011
  return text.replace(/\\\r?\n/g, " ").split("\n").some((l) => ITEM_LIST_RE.test(l) && !sanitizedListing(l));
88565
89012
  }
88566
- function analyzeTrustedReaders(path113, content, autoLabels, callers, trustedRunSources) {
89013
+ function analyzeTrustedReaders(path114, content, autoLabels, callers, trustedRunSources) {
88567
89014
  AUTO_LABELS = autoLabels ?? /* @__PURE__ */ new Set();
88568
89015
  TRUSTED_RUN_SOURCES = trustedRunSources ?? /* @__PURE__ */ new Set();
88569
89016
  try {
88570
- return analyzeTrustedReadersInner(path113, content, callers);
89017
+ return analyzeTrustedReadersInner(path114, content, callers);
88571
89018
  } finally {
88572
89019
  AUTO_LABELS = /* @__PURE__ */ new Set();
88573
89020
  TRUSTED_RUN_SOURCES = /* @__PURE__ */ new Set();
@@ -88576,7 +89023,7 @@ function analyzeTrustedReaders(path113, content, autoLabels, callers, trustedRun
88576
89023
  function keysWithCallers(keys, callers) {
88577
89024
  return [.../* @__PURE__ */ new Set([...keys, ...(callers ?? []).flatMap((c) => c.keys)])];
88578
89025
  }
88579
- function analyzeTrustedReadersInner(path113, content, callers) {
89026
+ function analyzeTrustedReadersInner(path114, content, callers) {
88580
89027
  let raw;
88581
89028
  try {
88582
89029
  raw = loadWorkflow(content).raw;
@@ -88637,7 +89084,7 @@ function analyzeTrustedReadersInner(path113, content, callers) {
88637
89084
  dimension: "workflows",
88638
89085
  severity: "advisory",
88639
89086
  title: "Agent on a trusted trigger reads content strangers write, with a write token",
88640
- file: path113,
89087
+ file: path114,
88641
89088
  signals: [
88642
89089
  `runs on ${keys.map((k) => safeText(k, 40)).join(", ")}: no stranger starts it`,
88643
89090
  ...reads.map((r) => `the agent ${r}`),
@@ -88725,17 +89172,17 @@ function requiresTrustedAuthor(src) {
88725
89172
  };
88726
89173
  return req(x);
88727
89174
  }
88728
- function analyzeForkCodeOnTrust(path113, content, callers, autoLabels, trustedRunSources) {
89175
+ function analyzeForkCodeOnTrust(path114, content, callers, autoLabels, trustedRunSources) {
88729
89176
  AUTO_LABELS = autoLabels ?? /* @__PURE__ */ new Set();
88730
89177
  TRUSTED_RUN_SOURCES = trustedRunSources ?? /* @__PURE__ */ new Set();
88731
89178
  try {
88732
- return analyzeForkCodeOnTrustInner(path113, content, callers);
89179
+ return analyzeForkCodeOnTrustInner(path114, content, callers);
88733
89180
  } finally {
88734
89181
  AUTO_LABELS = /* @__PURE__ */ new Set();
88735
89182
  TRUSTED_RUN_SOURCES = /* @__PURE__ */ new Set();
88736
89183
  }
88737
89184
  }
88738
- function analyzeForkCodeOnTrustInner(path113, content, callers) {
89185
+ function analyzeForkCodeOnTrustInner(path114, content, callers) {
88739
89186
  let raw;
88740
89187
  try {
88741
89188
  raw = loadWorkflow(content).raw;
@@ -88774,7 +89221,7 @@ function analyzeForkCodeOnTrustInner(path113, content, callers) {
88774
89221
  dimension: "workflows",
88775
89222
  severity: "advisory",
88776
89223
  title: "A maintainer's action runs the agent inside the fork's code",
88777
- file: path113,
89224
+ file: path114,
88778
89225
  signals: [
88779
89226
  `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
89227
  "checks out the untrusted PR head into the workspace root",
@@ -88806,18 +89253,18 @@ function laterSecrets(wf, job, from) {
88806
89253
  if (!/^GITHUB_TOKEN$/i.test(m[1])) out.add(m[1]);
88807
89254
  return [...out];
88808
89255
  }
88809
- function analyzeAgentOutputToEnv(path113, content, callers, autoLabels, trustedRunSources) {
89256
+ function analyzeAgentOutputToEnv(path114, content, callers, autoLabels, trustedRunSources) {
88810
89257
  AUTO_LABELS = autoLabels ?? /* @__PURE__ */ new Set();
88811
89258
  TRUSTED_RUN_SOURCES = trustedRunSources ?? /* @__PURE__ */ new Set();
88812
89259
  try {
88813
- return analyzeAgentOutputToEnvInner(path113, content, callers);
89260
+ return analyzeAgentOutputToEnvInner(path114, content, callers);
88814
89261
  } finally {
88815
89262
  AUTO_LABELS = /* @__PURE__ */ new Set();
88816
89263
  TRUSTED_RUN_SOURCES = /* @__PURE__ */ new Set();
88817
89264
  UPSTREAM_TRUSTED = false;
88818
89265
  }
88819
89266
  }
88820
- function analyzeAgentOutputToEnvInner(path113, content, callers) {
89267
+ function analyzeAgentOutputToEnvInner(path114, content, callers) {
88821
89268
  let raw;
88822
89269
  try {
88823
89270
  raw = loadWorkflow(content).raw;
@@ -88913,7 +89360,7 @@ function analyzeAgentOutputToEnvInner(path113, content, callers) {
88913
89360
  dimension: "workflows",
88914
89361
  severity,
88915
89362
  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,
89363
+ file: path114,
88917
89364
  signals,
88918
89365
  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
89366
  explain: explainAgentOutputEnv({
@@ -88991,7 +89438,7 @@ function reviewQuestion(rule) {
88991
89438
  return "Is this an instruction the agent follows, or text for a person?";
88992
89439
  }
88993
89440
  var RANK2 = { critical: 4, high: 3, medium: 2, advisory: 1 };
88994
- var worstRank = (fs114) => Math.max(...fs114.map((f) => RANK2[f.severity] ?? 0));
89441
+ var worstRank = (fs115) => Math.max(...fs115.map((f) => RANK2[f.severity] ?? 0));
88995
89442
  var fileOrder = (a, b) => worstRank(b[1]) - worstRank(a[1]) || b[1].length - a[1].length;
88996
89443
  function groupReview(findings) {
88997
89444
  const byFile = /* @__PURE__ */ new Map();
@@ -89062,7 +89509,7 @@ function logicalLines2(content) {
89062
89509
  }
89063
89510
  return out;
89064
89511
  }
89065
- function analyzeScript(path113, content, rulePrefix) {
89512
+ function analyzeScript(path114, content, rulePrefix) {
89066
89513
  const check = rulePrefix.startsWith("CI-1") ? "CI-1" : "CI-6";
89067
89514
  const dimension = check === "CI-1" ? "toolRules" : "instructions";
89068
89515
  const mk3 = (suffix, severity, title, signal, fix, line) => ({
@@ -89071,7 +89518,7 @@ function analyzeScript(path113, content, rulePrefix) {
89071
89518
  dimension,
89072
89519
  severity,
89073
89520
  title,
89074
- file: path113,
89521
+ file: path114,
89075
89522
  ...line ? { line } : {},
89076
89523
  signals: [signal],
89077
89524
  fix
@@ -89501,7 +89948,7 @@ async function scanRepo(input, onProgress) {
89501
89948
  init_cjs_shims();
89502
89949
  var import_chalk36 = __toESM(require("chalk"));
89503
89950
  init_suppress();
89504
- var listed = (fs114) => fs114.filter((f) => f.tier !== "review");
89951
+ var listed = (fs115) => fs115.filter((f) => f.tier !== "review");
89505
89952
  function excerpt(f) {
89506
89953
  const m = /`([^`]+)`/.exec(f.signals[0] ?? "");
89507
89954
  return safeText(m ? m[1] : f.title, 120).replace(/`/g, "'");
@@ -89517,14 +89964,14 @@ function reviewMd(findings) {
89517
89964
  L.push("");
89518
89965
  const files = [...byFile.keys()];
89519
89966
  for (const file2 of files.slice(0, REVIEW_FILES)) {
89520
- const fs114 = byFile.get(file2);
89967
+ const fs115 = byFile.get(file2);
89521
89968
  L.push(`- \`${safeText(file2, 300)}\``);
89522
- for (const f of fs114.slice(0, REVIEW_LINES_PER_FILE))
89969
+ for (const f of fs115.slice(0, REVIEW_LINES_PER_FILE))
89523
89970
  L.push(
89524
89971
  ` - ${f.line ? `line ${Number(f.line)}: ` : ""}\`${excerpt(f)}\`. Check: ${reviewQuestion(f.rule)}`
89525
89972
  );
89526
- if (fs114.length > REVIEW_LINES_PER_FILE)
89527
- L.push(` - and ${fs114.length - REVIEW_LINES_PER_FILE} similar lines in this file`);
89973
+ if (fs115.length > REVIEW_LINES_PER_FILE)
89974
+ L.push(` - and ${fs115.length - REVIEW_LINES_PER_FILE} similar lines in this file`);
89528
89975
  }
89529
89976
  const more = files.length - REVIEW_FILES;
89530
89977
  if (more > 0) L.push(`- and ${more} more file${more === 1 ? "" : "s"}`);
@@ -89540,7 +89987,7 @@ function reviewMd(findings) {
89540
89987
  }
89541
89988
  function reviewTerminal(findings) {
89542
89989
  const { byFile, grants } = groupReview(findings);
89543
- const items = [...byFile.values()].reduce((a, fs114) => a + fs114.length, 0);
89990
+ const items = [...byFile.values()].reduce((a, fs115) => a + fs115.length, 0);
89544
89991
  if (!items && !grants) return [];
89545
89992
  const L = [];
89546
89993
  if (items) {
@@ -89549,12 +89996,12 @@ function reviewTerminal(findings) {
89549
89996
  );
89550
89997
  const files = [...byFile.keys()];
89551
89998
  for (const file2 of files.slice(0, REVIEW_FILES)) {
89552
- const fs114 = byFile.get(file2);
89999
+ const fs115 = byFile.get(file2);
89553
90000
  L.push(import_chalk36.default.gray(` ${file2}`));
89554
- for (const f of fs114.slice(0, REVIEW_LINES_PER_FILE))
90001
+ for (const f of fs115.slice(0, REVIEW_LINES_PER_FILE))
89555
90002
  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`));
90003
+ if (fs115.length > REVIEW_LINES_PER_FILE)
90004
+ L.push(import_chalk36.default.gray(` and ${fs115.length - REVIEW_LINES_PER_FILE} similar lines`));
89558
90005
  }
89559
90006
  const more = files.length - REVIEW_FILES;
89560
90007
  if (more > 0) L.push(import_chalk36.default.gray(` and ${more} more file${more === 1 ? "" : "s"}`));
@@ -90208,16 +90655,16 @@ var import_chalk40 = __toESM(require("chalk"));
90208
90655
 
90209
90656
  // src/canary/plant.ts
90210
90657
  init_cjs_shims();
90211
- var import_fs98 = __toESM(require("fs"));
90212
- var import_os80 = __toESM(require("os"));
90213
- var import_path97 = __toESM(require("path"));
90658
+ var import_fs99 = __toESM(require("fs"));
90659
+ var import_os81 = __toESM(require("os"));
90660
+ var import_path98 = __toESM(require("path"));
90214
90661
  init_jail();
90215
90662
  init_registry2();
90216
90663
 
90217
90664
  // src/canary/sites.ts
90218
90665
  init_cjs_shims();
90219
- var import_fs97 = __toESM(require("fs"));
90220
- var import_path96 = __toESM(require("path"));
90666
+ var import_fs98 = __toESM(require("fs"));
90667
+ var import_path97 = __toESM(require("path"));
90221
90668
 
90222
90669
  // src/canary/generate.ts
90223
90670
  init_cjs_shims();
@@ -90312,26 +90759,26 @@ function generateSshKey() {
90312
90759
  var SITES = {
90313
90760
  "aws-profile": {
90314
90761
  kind: "aws-profile",
90315
- primary: (h) => import_path96.default.join(h, ".aws", "credentials"),
90316
- fallback: (h) => import_path96.default.join(h, ".aws", "credentials.bak"),
90762
+ primary: (h) => import_path97.default.join(h, ".aws", "credentials"),
90763
+ fallback: (h) => import_path97.default.join(h, ".aws", "credentials.bak"),
90317
90764
  // SSO / profile-only / credential_process setups have ~/.aws/config and no
90318
90765
  // credentials file; the SDK and CLI then parse ~/.aws/credentials on every
90319
90766
  // 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")),
90767
+ preferFallback: (h) => import_fs98.default.existsSync(import_path97.default.join(h, ".aws", "config")),
90321
90768
  dirMode: 448,
90322
90769
  generate: generateAwsProfile
90323
90770
  },
90324
90771
  "env-file": {
90325
90772
  kind: "env-file",
90326
- primary: (h) => import_path96.default.join(h, ".env.bak"),
90327
- fallback: (h) => import_path96.default.join(h, ".env.local.bak"),
90773
+ primary: (h) => import_path97.default.join(h, ".env.bak"),
90774
+ fallback: (h) => import_path97.default.join(h, ".env.local.bak"),
90328
90775
  dirMode: 493,
90329
90776
  generate: generateEnvFile
90330
90777
  },
90331
90778
  "ssh-key": {
90332
90779
  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"),
90780
+ primary: (h) => import_path97.default.join(h, ".ssh", "id_rsa_backup"),
90781
+ fallback: (h) => import_path97.default.join(h, ".ssh", "id_rsa.old"),
90335
90782
  // ssh refuses a group-writable ~/.ssh for config / known_hosts reads.
90336
90783
  dirMode: 448,
90337
90784
  generate: generateSshKey
@@ -90344,12 +90791,12 @@ var isKind = (s) => ALL_KINDS.includes(s);
90344
90791
  var liveRecords = (kind) => loadCanaries().filter((r) => r.kind === kind && !r.retiredAt);
90345
90792
  var fileHashOf = (p) => {
90346
90793
  try {
90347
- return sha256Hex(import_fs98.default.readFileSync(p));
90794
+ return sha256Hex(import_fs99.default.readFileSync(p));
90348
90795
  } catch {
90349
90796
  return null;
90350
90797
  }
90351
90798
  };
90352
- function plantKind(kind, home = import_os80.default.homedir()) {
90799
+ function plantKind(kind, home = import_os81.default.homedir()) {
90353
90800
  const site = SITES[kind];
90354
90801
  const live = liveRecords(kind);
90355
90802
  if (live.length > 0) {
@@ -90368,8 +90815,8 @@ function plantKind(kind, home = import_os80.default.homedir()) {
90368
90815
  const primary = site.primary(home);
90369
90816
  const fallback = site.fallback(home);
90370
90817
  let target;
90371
- if (!import_fs98.default.existsSync(primary) && !site.preferFallback?.(home)) target = primary;
90372
- else if (!import_fs98.default.existsSync(fallback)) target = fallback;
90818
+ if (!import_fs99.default.existsSync(primary) && !site.preferFallback?.(home)) target = primary;
90819
+ else if (!import_fs99.default.existsSync(fallback)) target = fallback;
90373
90820
  else {
90374
90821
  return {
90375
90822
  kind,
@@ -90380,11 +90827,11 @@ function plantKind(kind, home = import_os80.default.homedir()) {
90380
90827
  recordIds: []
90381
90828
  };
90382
90829
  }
90383
- const dir2 = import_path97.default.dirname(target);
90830
+ const dir2 = import_path98.default.dirname(target);
90384
90831
  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);
90832
+ const createdDir = import_fs99.default.mkdirSync(dir2, { recursive: true, mode: site.dirMode }) !== void 0;
90833
+ import_fs99.default.writeFileSync(target, gen.text, { mode: 384, flag: "wx" });
90834
+ import_fs99.default.chmodSync(target, 384);
90388
90835
  const fileHash = sha256Hex(gen.text);
90389
90836
  regenerateUserJail(addJailPath(target, "block"));
90390
90837
  const recordIds = gen.values.map(
@@ -90402,14 +90849,14 @@ function plantKind(kind, home = import_os80.default.homedir()) {
90402
90849
  }
90403
90850
  function allowedPathsFor(kind, home) {
90404
90851
  const site = SITES[kind];
90405
- return [site.primary(home), site.fallback(home)].map((x) => import_path97.default.resolve(x));
90852
+ return [site.primary(home), site.fallback(home)].map((x) => import_path98.default.resolve(x));
90406
90853
  }
90407
- function removeKind(kind, home = import_os80.default.homedir()) {
90854
+ function removeKind(kind, home = import_os81.default.homedir()) {
90408
90855
  const live = liveRecords(kind);
90409
90856
  if (live.length === 0) return { kind, action: "absent" };
90410
90857
  const p = live[0].path;
90411
90858
  const { fileHash, createdDir } = live[0];
90412
- if (!allowedPathsFor(kind, home).includes(import_path97.default.resolve(p))) {
90859
+ if (!allowedPathsFor(kind, home).includes(import_path98.default.resolve(p))) {
90413
90860
  return {
90414
90861
  kind,
90415
90862
  action: "refused",
@@ -90417,7 +90864,7 @@ function removeKind(kind, home = import_os80.default.homedir()) {
90417
90864
  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
90865
  };
90419
90866
  }
90420
- if (!import_fs98.default.existsSync(p)) {
90867
+ if (!import_fs99.default.existsSync(p)) {
90421
90868
  for (const r of live) retireCanary(r.id);
90422
90869
  unjail(p);
90423
90870
  return { kind, action: "already-gone", path: p };
@@ -90430,13 +90877,13 @@ function removeKind(kind, home = import_os80.default.homedir()) {
90430
90877
  reason: `${p} changed since node9 wrote it; inspect and remove it by hand`
90431
90878
  };
90432
90879
  }
90433
- import_fs98.default.unlinkSync(p);
90880
+ import_fs99.default.unlinkSync(p);
90434
90881
  let dirRemoved = false;
90435
90882
  if (createdDir) {
90436
- const dir2 = import_path97.default.dirname(p);
90883
+ const dir2 = import_path98.default.dirname(p);
90437
90884
  try {
90438
- if (import_fs98.default.readdirSync(dir2).length === 0) {
90439
- import_fs98.default.rmdirSync(dir2);
90885
+ if (import_fs99.default.readdirSync(dir2).length === 0) {
90886
+ import_fs99.default.rmdirSync(dir2);
90440
90887
  dirRemoved = true;
90441
90888
  }
90442
90889
  } catch {
@@ -90460,14 +90907,14 @@ function statusAll() {
90460
90907
  const p = live[0].path;
90461
90908
  return {
90462
90909
  kind,
90463
- state: import_fs98.default.existsSync(p) ? "planted" : "missing",
90910
+ state: import_fs99.default.existsSync(p) ? "planted" : "missing",
90464
90911
  path: p,
90465
90912
  records: live.length,
90466
90913
  plantedAt: live[0].plantedAt
90467
90914
  };
90468
90915
  });
90469
90916
  }
90470
- function rotateKind(kind, home = import_os80.default.homedir()) {
90917
+ function rotateKind(kind, home = import_os81.default.homedir()) {
90471
90918
  const removed = removeKind(kind);
90472
90919
  if (removed.action === "refused") return { removed };
90473
90920
  return { removed, planted: plantKind(kind, home) };
@@ -90556,15 +91003,15 @@ init_daemon3();
90556
91003
  // src/cli/commands/sandbox.ts
90557
91004
  init_cjs_shims();
90558
91005
  var import_chalk41 = __toESM(require("chalk"));
90559
- var import_fs101 = __toESM(require("fs"));
90560
- var import_path100 = __toESM(require("path"));
91006
+ var import_fs102 = __toESM(require("fs"));
91007
+ var import_path101 = __toESM(require("path"));
90561
91008
  var import_child_process16 = require("child_process");
90562
91009
  init_config();
90563
91010
 
90564
91011
  // src/sandbox/config.ts
90565
91012
  init_cjs_shims();
90566
- var import_fs99 = __toESM(require("fs"));
90567
- var import_path98 = __toESM(require("path"));
91013
+ var import_fs100 = __toESM(require("fs"));
91014
+ var import_path99 = __toESM(require("path"));
90568
91015
  var import_yaml2 = require("yaml");
90569
91016
  var SANDBOX_CONFIG_FILE = "node9.sandbox.yaml";
90570
91017
  var FORBIDDEN_ENV = /* @__PURE__ */ new Set(["NODE9_API_KEY", "NODE9_API_URL"]);
@@ -90637,16 +91084,16 @@ function scaffoldSandboxYaml(agent) {
90637
91084
  return header + (0, import_yaml2.stringify)(defaultSandboxConfig(agent));
90638
91085
  }
90639
91086
  function sandboxConfigPath(cwd = process.cwd()) {
90640
- return import_path98.default.join(cwd, SANDBOX_CONFIG_FILE);
91087
+ return import_path99.default.join(cwd, SANDBOX_CONFIG_FILE);
90641
91088
  }
90642
91089
  function loadSandboxConfig(cwd = process.cwd(), fallbackAgent = "claude") {
90643
91090
  const p = sandboxConfigPath(cwd);
90644
- if (!import_fs99.default.existsSync(p)) {
91091
+ if (!import_fs100.default.existsSync(p)) {
90645
91092
  throw new Error(`sandbox: ${SANDBOX_CONFIG_FILE} not found \u2014 run \`node9 sandbox new\` first.`);
90646
91093
  }
90647
91094
  let raw;
90648
91095
  try {
90649
- raw = (0, import_yaml2.parse)(import_fs99.default.readFileSync(p, "utf-8"));
91096
+ raw = (0, import_yaml2.parse)(import_fs100.default.readFileSync(p, "utf-8"));
90650
91097
  } catch (err2) {
90651
91098
  throw new Error(
90652
91099
  `sandbox: ${SANDBOX_CONFIG_FILE} is not valid YAML \u2014 ${err2.message}`
@@ -90707,14 +91154,14 @@ init_atomic_write();
90707
91154
 
90708
91155
  // src/sandbox/runtime.ts
90709
91156
  init_cjs_shims();
90710
- var import_fs100 = __toESM(require("fs"));
90711
- var import_os81 = __toESM(require("os"));
90712
- var import_path99 = __toESM(require("path"));
91157
+ var import_fs101 = __toESM(require("fs"));
91158
+ var import_os82 = __toESM(require("os"));
91159
+ var import_path100 = __toESM(require("path"));
90713
91160
  var import_crypto27 = __toESM(require("crypto"));
90714
91161
  var import_child_process15 = require("child_process");
90715
91162
  init_templates();
90716
91163
  function sandboxDataDir(cwd = process.cwd()) {
90717
- return import_path99.default.join(cwd, ".node9", "sandbox", "data");
91164
+ return import_path100.default.join(cwd, ".node9", "sandbox", "data");
90718
91165
  }
90719
91166
  function detectEngine(engine) {
90720
91167
  const r = (0, import_child_process15.spawnSync)(engine, ["--version"], { encoding: "utf-8" });
@@ -90725,7 +91172,7 @@ function detectEngine(engine) {
90725
91172
  }
90726
91173
  function agentCredentialsMount(agent) {
90727
91174
  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}` };
91175
+ return { hostPath: import_path100.default.join(import_os82.default.homedir(), rel), target: `/home/${RUN_AS_USER}/${rel}` };
90729
91176
  }
90730
91177
  function buildRunArgs(opts) {
90731
91178
  const { config, workspaceHostPath, dataHostPath, allowlistHostPath, agentArgs } = opts;
@@ -90735,7 +91182,7 @@ function buildRunArgs(opts) {
90735
91182
  args.push("-v", `${allowlistHostPath}:${ALLOWED_DOMAINS_PATH}:ro`);
90736
91183
  if (config.node9.mountAgentCredentials) {
90737
91184
  const creds = agentCredentialsMount(config.agent);
90738
- if (import_fs100.default.existsSync(creds.hostPath)) {
91185
+ if (import_fs101.default.existsSync(creds.hostPath)) {
90739
91186
  args.push("-v", `${creds.hostPath}:${creds.target}`);
90740
91187
  }
90741
91188
  }
@@ -90753,30 +91200,30 @@ function imageContentHash(dockerfile, entrypoint) {
90753
91200
  return import_crypto27.default.createHash("sha256").update(dockerfile).update("\0").update(entrypoint).digest("hex").slice(0, 16);
90754
91201
  }
90755
91202
  function sandboxBuildDir(cwd = process.cwd()) {
90756
- return import_path99.default.join(cwd, ".node9", "sandbox", "build");
91203
+ return import_path100.default.join(cwd, ".node9", "sandbox", "build");
90757
91204
  }
90758
91205
  function writeBuildContext(cwd, dockerfile, entrypoint) {
90759
91206
  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);
91207
+ import_fs101.default.mkdirSync(dir2, { recursive: true });
91208
+ import_fs101.default.writeFileSync(import_path100.default.join(dir2, "Dockerfile"), dockerfile);
91209
+ import_fs101.default.writeFileSync(import_path100.default.join(dir2, "entrypoint.sh"), entrypoint);
90763
91210
  return dir2;
90764
91211
  }
90765
91212
  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");
91213
+ const dir2 = import_path100.default.join(cwd, ".node9", "sandbox");
91214
+ import_fs101.default.mkdirSync(dir2, { recursive: true });
91215
+ const p = import_path100.default.join(dir2, "allowed-domains.txt");
91216
+ import_fs101.default.writeFileSync(p, hosts.join("\n") + "\n");
90770
91217
  return p;
90771
91218
  }
90772
91219
  function resolveHomePath(p) {
90773
- return p.startsWith("~") ? import_path99.default.join(import_os81.default.homedir(), p.slice(1)) : import_path99.default.resolve(p);
91220
+ return p.startsWith("~") ? import_path100.default.join(import_os82.default.homedir(), p.slice(1)) : import_path100.default.resolve(p);
90774
91221
  }
90775
91222
 
90776
91223
  // src/cli/commands/sandbox.ts
90777
91224
  function seedDataDirConfig(dataDir, sandbox) {
90778
- import_fs101.default.mkdirSync(dataDir, { recursive: true });
90779
- const configPath2 = import_path100.default.join(dataDir, "config.json");
91225
+ import_fs102.default.mkdirSync(dataDir, { recursive: true });
91226
+ const configPath2 = import_path101.default.join(dataDir, "config.json");
90780
91227
  const seed = {
90781
91228
  settings: {
90782
91229
  approvers: {
@@ -90787,7 +91234,7 @@ function seedDataDirConfig(dataDir, sandbox) {
90787
91234
  }
90788
91235
  }
90789
91236
  };
90790
- import_fs101.default.writeFileSync(configPath2, JSON.stringify(seed, null, 2), { mode: 384 });
91237
+ import_fs102.default.writeFileSync(configPath2, JSON.stringify(seed, null, 2), { mode: 384 });
90791
91238
  }
90792
91239
  function registerSandboxCommand(program2, version2) {
90793
91240
  const node9Version2 = pinnedNode9Version(version2);
@@ -90796,7 +91243,7 @@ function registerSandboxCommand(program2, version2) {
90796
91243
  const agent = opts.agent === "codex" ? "codex" : "claude";
90797
91244
  const p = sandboxConfigPath();
90798
91245
  try {
90799
- import_fs101.default.writeFileSync(p, scaffoldSandboxYaml(agent), { flag: "wx" });
91246
+ import_fs102.default.writeFileSync(p, scaffoldSandboxYaml(agent), { flag: "wx" });
90800
91247
  } catch (e) {
90801
91248
  if (e.code !== "EEXIST") throw e;
90802
91249
  console.log(
@@ -90843,8 +91290,8 @@ function registerSandboxCommand(program2, version2) {
90843
91290
  const buildDir = writeBuildContext(cwd, dockerfile, entrypoint);
90844
91291
  const hash2 = imageContentHash(dockerfile, entrypoint);
90845
91292
  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() : "";
91293
+ const hashFile = import_path101.default.join(sandboxBuildDir(cwd), ".image-hash");
91294
+ const lastHash = import_fs102.default.existsSync(hashFile) ? import_fs102.default.readFileSync(hashFile, "utf-8").trim() : "";
90848
91295
  const imageExists = (0, import_child_process16.spawnSync)(sandbox.runtime.engine, ["image", "inspect", image], { stdio: "ignore" }).status === 0;
90849
91296
  const needBuild = sandbox.runtime.rebuild === "always" || !imageExists || sandbox.runtime.rebuild !== "never" && lastHash !== hash2;
90850
91297
  if (needBuild) {
@@ -90870,7 +91317,7 @@ function registerSandboxCommand(program2, version2) {
90870
91317
  });
90871
91318
  if (sandbox.node9.mountAgentCredentials) {
90872
91319
  const creds = agentCredentialsMount(sandbox.agent);
90873
- if (import_fs101.default.existsSync(creds.hostPath)) {
91320
+ if (import_fs102.default.existsSync(creds.hostPath)) {
90874
91321
  console.log(import_chalk41.default.dim(` mounting ${creds.hostPath} (agent credentials, rw)`));
90875
91322
  } else {
90876
91323
  console.log(
@@ -90886,20 +91333,20 @@ function registerSandboxCommand(program2, version2) {
90886
91333
  process.exit(r.status ?? 0);
90887
91334
  });
90888
91335
  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)) {
91336
+ const auditPath = import_path101.default.join(sandboxDataDir(), "audit.log");
91337
+ if (!import_fs102.default.existsSync(auditPath)) {
90891
91338
  console.log(import_chalk41.default.dim(" no sandbox audit yet."));
90892
91339
  return;
90893
91340
  }
90894
91341
  (0, import_child_process16.spawnSync)("tail", ["-f", auditPath], { stdio: "inherit" });
90895
91342
  });
90896
91343
  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)) {
91344
+ const auditPath = import_path101.default.join(sandboxDataDir(), "audit.log");
91345
+ if (!import_fs102.default.existsSync(auditPath)) {
90899
91346
  console.log(import_chalk41.default.dim(" no sandbox audit yet."));
90900
91347
  return;
90901
91348
  }
90902
- process.stdout.write(import_fs101.default.readFileSync(auditPath, "utf-8"));
91349
+ process.stdout.write(import_fs102.default.readFileSync(auditPath, "utf-8"));
90903
91350
  });
90904
91351
  cmd.command("clean").description("Remove the sandbox image, build context, and data").action(() => {
90905
91352
  const cwd = process.cwd();
@@ -90913,7 +91360,7 @@ function registerSandboxCommand(program2, version2) {
90913
91360
  stdio: "ignore"
90914
91361
  });
90915
91362
  }
90916
- import_fs101.default.rmSync(import_path100.default.join(cwd, ".node9", "sandbox"), { recursive: true, force: true });
91363
+ import_fs102.default.rmSync(import_path101.default.join(cwd, ".node9", "sandbox"), { recursive: true, force: true });
90917
91364
  console.log(import_chalk41.default.green(" \u2713 sandbox image + build + data removed."));
90918
91365
  });
90919
91366
  }
@@ -91142,13 +91589,13 @@ function registerPackageIndexCommand(program2) {
91142
91589
  // src/cli/commands/skill-pin.ts
91143
91590
  init_cjs_shims();
91144
91591
  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"));
91592
+ var import_fs103 = __toESM(require("fs"));
91593
+ var import_os83 = __toESM(require("os"));
91594
+ var import_path102 = __toESM(require("path"));
91148
91595
  init_skill_pin();
91149
91596
  function wipeSkillSessions() {
91150
91597
  try {
91151
- import_fs102.default.rmSync(import_path101.default.join(import_os82.default.homedir(), ".node9", "skill-sessions"), {
91598
+ import_fs103.default.rmSync(import_path102.default.join(import_os83.default.homedir(), ".node9", "skill-sessions"), {
91152
91599
  recursive: true,
91153
91600
  force: true
91154
91601
  });
@@ -91231,15 +91678,15 @@ function registerSkillPinCommand(program2) {
91231
91678
 
91232
91679
  // src/cli/commands/decisions.ts
91233
91680
  init_cjs_shims();
91234
- var import_fs103 = __toESM(require("fs"));
91235
- var import_os83 = __toESM(require("os"));
91236
- var import_path102 = __toESM(require("path"));
91681
+ var import_fs104 = __toESM(require("fs"));
91682
+ var import_os84 = __toESM(require("os"));
91683
+ var import_path103 = __toESM(require("path"));
91237
91684
  var import_chalk46 = __toESM(require("chalk"));
91238
- var DECISIONS_FILE2 = import_path102.default.join(import_os83.default.homedir(), ".node9", "decisions.json");
91685
+ var DECISIONS_FILE2 = import_path103.default.join(import_os84.default.homedir(), ".node9", "decisions.json");
91239
91686
  function readDecisions() {
91240
91687
  try {
91241
- if (!import_fs103.default.existsSync(DECISIONS_FILE2)) return {};
91242
- const raw = import_fs103.default.readFileSync(DECISIONS_FILE2, "utf-8");
91688
+ if (!import_fs104.default.existsSync(DECISIONS_FILE2)) return {};
91689
+ const raw = import_fs104.default.readFileSync(DECISIONS_FILE2, "utf-8");
91243
91690
  const parsed = JSON.parse(raw);
91244
91691
  const out = {};
91245
91692
  for (const [k, v] of Object.entries(parsed)) {
@@ -91251,11 +91698,11 @@ function readDecisions() {
91251
91698
  }
91252
91699
  }
91253
91700
  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 });
91701
+ const dir2 = import_path103.default.dirname(DECISIONS_FILE2);
91702
+ if (!import_fs104.default.existsSync(dir2)) import_fs104.default.mkdirSync(dir2, { recursive: true });
91256
91703
  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);
91704
+ import_fs104.default.writeFileSync(tmp, JSON.stringify(d, null, 2));
91705
+ import_fs104.default.renameSync(tmp, DECISIONS_FILE2);
91259
91706
  }
91260
91707
  function registerDecisionsCommand(program2) {
91261
91708
  const cmd = program2.command("decisions").description('Manage persistent "Always Allow" / "Always Deny" tool decisions');
@@ -91313,15 +91760,15 @@ Persistent decisions (${entries.length})
91313
91760
  // src/cli/commands/dlp.ts
91314
91761
  init_cjs_shims();
91315
91762
  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"));
91763
+ var import_fs105 = __toESM(require("fs"));
91764
+ var import_path104 = __toESM(require("path"));
91765
+ var import_os85 = __toESM(require("os"));
91319
91766
  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");
91767
+ var AUDIT_LOG = import_path104.default.join(import_os85.default.homedir(), ".node9", "audit.log");
91768
+ var RESOLVED_FILE = import_path104.default.join(import_os85.default.homedir(), ".node9", "dlp-resolved.json");
91322
91769
  function loadResolved() {
91323
91770
  try {
91324
- const raw = JSON.parse(import_fs104.default.readFileSync(RESOLVED_FILE, "utf-8"));
91771
+ const raw = JSON.parse(import_fs105.default.readFileSync(RESOLVED_FILE, "utf-8"));
91325
91772
  return new Set(raw);
91326
91773
  } catch {
91327
91774
  return /* @__PURE__ */ new Set();
@@ -91329,13 +91776,13 @@ function loadResolved() {
91329
91776
  }
91330
91777
  function saveResolved(resolved) {
91331
91778
  try {
91332
- import_fs104.default.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
91779
+ import_fs105.default.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
91333
91780
  } catch {
91334
91781
  }
91335
91782
  }
91336
91783
  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) => {
91784
+ if (!import_fs105.default.existsSync(AUDIT_LOG)) return [];
91785
+ return import_fs105.default.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
91339
91786
  if (!line.trim()) return [];
91340
91787
  try {
91341
91788
  const e = JSON.parse(line);
@@ -91434,15 +91881,15 @@ function registerDlpCommand(program2) {
91434
91881
  // src/cli/commands/mask.ts
91435
91882
  init_cjs_shims();
91436
91883
  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"));
91884
+ var import_fs106 = __toESM(require("fs"));
91885
+ var import_path105 = __toESM(require("path"));
91886
+ var import_os86 = __toESM(require("os"));
91440
91887
  init_dlp();
91441
91888
  function findJsonlFiles(dir2) {
91442
91889
  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);
91890
+ if (!import_fs106.default.existsSync(dir2)) return results;
91891
+ for (const entry of import_fs106.default.readdirSync(dir2, { withFileTypes: true })) {
91892
+ const full = import_path105.default.join(dir2, entry.name);
91446
91893
  if (entry.isDirectory()) results.push(...findJsonlFiles(full));
91447
91894
  else if (entry.isFile() && entry.name.endsWith(".jsonl")) results.push(full);
91448
91895
  }
@@ -91485,7 +91932,7 @@ function redactJson(obj) {
91485
91932
  function processFile(filePath, dryRun) {
91486
91933
  let raw;
91487
91934
  try {
91488
- raw = import_fs105.default.readFileSync(filePath, "utf-8");
91935
+ raw = import_fs106.default.readFileSync(filePath, "utf-8");
91489
91936
  } catch {
91490
91937
  return { redactedLines: 0, patterns: [] };
91491
91938
  }
@@ -91517,14 +91964,14 @@ function processFile(filePath, dryRun) {
91517
91964
  }
91518
91965
  }
91519
91966
  if (!dryRun && redactedLines > 0) {
91520
- import_fs105.default.writeFileSync(filePath, newLines.join("\n"), "utf-8");
91967
+ import_fs106.default.writeFileSync(filePath, newLines.join("\n"), "utf-8");
91521
91968
  }
91522
91969
  return { redactedLines, patterns };
91523
91970
  }
91524
91971
  function processJsonFile(filePath, dryRun) {
91525
91972
  let raw;
91526
91973
  try {
91527
- raw = import_fs105.default.readFileSync(filePath, "utf-8");
91974
+ raw = import_fs106.default.readFileSync(filePath, "utf-8");
91528
91975
  } catch {
91529
91976
  return { redactedLines: 0, patterns: [] };
91530
91977
  }
@@ -91537,15 +91984,15 @@ function processJsonFile(filePath, dryRun) {
91537
91984
  const { value, modified, found } = redactJson(parsed);
91538
91985
  if (!modified) return { redactedLines: 0, patterns: [] };
91539
91986
  if (!dryRun) {
91540
- import_fs105.default.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
91987
+ import_fs106.default.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
91541
91988
  }
91542
91989
  return { redactedLines: 1, patterns: found };
91543
91990
  }
91544
91991
  function findJsonFiles(dir2) {
91545
91992
  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);
91993
+ if (!import_fs106.default.existsSync(dir2)) return results;
91994
+ for (const entry of import_fs106.default.readdirSync(dir2, { withFileTypes: true })) {
91995
+ const full = import_path105.default.join(dir2, entry.name);
91549
91996
  if (entry.isDirectory()) results.push(...findJsonFiles(full));
91550
91997
  else if (entry.isFile() && entry.name.endsWith(".json")) results.push(full);
91551
91998
  }
@@ -91554,9 +92001,9 @@ function findJsonFiles(dir2) {
91554
92001
  function registerMaskCommand(program2) {
91555
92002
  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
92003
  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");
92004
+ const home = import_os86.default.homedir();
92005
+ const claudeDir = import_path105.default.join(home, ".claude", "projects");
92006
+ const geminiDir = import_path105.default.join(home, ".gemini", "tmp");
91560
92007
  const allFiles = [
91561
92008
  ...findJsonlFiles(claudeDir).map((p) => ({ path: p, type: "jsonl" })),
91562
92009
  ...findJsonFiles(geminiDir).map((p) => ({ path: p, type: "json" }))
@@ -91564,7 +92011,7 @@ function registerMaskCommand(program2) {
91564
92011
  const cutoff = options.all ? null : new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
91565
92012
  const filtered = cutoff ? allFiles.filter((f) => {
91566
92013
  try {
91567
- return import_fs105.default.statSync(f.path).mtime >= cutoff;
92014
+ return import_fs106.default.statSync(f.path).mtime >= cutoff;
91568
92015
  } catch {
91569
92016
  return false;
91570
92017
  }
@@ -91621,7 +92068,7 @@ function registerMaskCommand(program2) {
91621
92068
  init_blast();
91622
92069
  init_safe_text();
91623
92070
  var { version } = JSON.parse(
91624
- import_fs109.default.readFileSync(import_path108.default.join(__dirname, "../package.json"), "utf-8")
92071
+ import_fs110.default.readFileSync(import_path109.default.join(__dirname, "../package.json"), "utf-8")
91625
92072
  );
91626
92073
  var program = new import_commander.Command();
91627
92074
  program.name("node9").description("The Sudo Command for AI Agents").version(version);
@@ -91779,8 +92226,8 @@ function undoLeftoverNote() {
91779
92226
  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
92227
  console.log(import_chalk50.default.cyan("\n\u{1F6E1}\uFE0F Node9 Uninstall\n"));
91781
92228
  try {
91782
- const credRaw = import_fs109.default.readFileSync(
91783
- import_path108.default.join(import_os89.default.homedir(), ".node9", "credentials.json"),
92229
+ const credRaw = import_fs110.default.readFileSync(
92230
+ import_path109.default.join(import_os90.default.homedir(), ".node9", "credentials.json"),
91784
92231
  "utf-8"
91785
92232
  );
91786
92233
  const prof = JSON.parse(credRaw)[process.env.NODE9_PROFILE || "default"];
@@ -91855,7 +92302,7 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
91855
92302
  if (removed.length > 0 || refused.length > 0) {
91856
92303
  console.log(import_chalk50.default.bold("\nRemoving decoy credentials..."));
91857
92304
  for (const r of removed) {
91858
- if (r.path) console.log(import_chalk50.default.green(` \u2705 ${r.path.replace(import_os89.default.homedir(), "~")}`));
92305
+ if (r.path) console.log(import_chalk50.default.green(` \u2705 ${r.path.replace(import_os90.default.homedir(), "~")}`));
91859
92306
  }
91860
92307
  for (const r of refused) {
91861
92308
  decoysLeft.push(r.path ?? "(unknown path)");
@@ -91872,8 +92319,8 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
91872
92319
  decoysLeft = ["(the decoy registry could not be read)"];
91873
92320
  }
91874
92321
  if (options.purge) {
91875
- const node9Dir = import_path108.default.join(import_os89.default.homedir(), ".node9");
91876
- if (import_fs109.default.existsSync(node9Dir)) {
92322
+ const node9Dir = import_path109.default.join(import_os90.default.homedir(), ".node9");
92323
+ if (import_fs110.default.existsSync(node9Dir)) {
91877
92324
  const alsoSnapshots = undoLeftoverPaths().length > 0 ? ", old undo snapshots" : "";
91878
92325
  if (decoysLeft.length > 0) {
91879
92326
  console.error(import_chalk50.default.red("\n \u26A0\uFE0F Decoy credential files are still on disk:"));
@@ -91889,8 +92336,8 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
91889
92336
  default: false
91890
92337
  });
91891
92338
  if (confirmed) {
91892
- import_fs109.default.rmSync(node9Dir, { recursive: true });
91893
- if (import_fs109.default.existsSync(node9Dir)) {
92339
+ import_fs110.default.rmSync(node9Dir, { recursive: true });
92340
+ if (import_fs110.default.existsSync(node9Dir)) {
91894
92341
  console.error(
91895
92342
  import_chalk50.default.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
91896
92343
  );
@@ -92046,7 +92493,7 @@ program.command("tail").description("Stream live agent activity to the terminal"
92046
92493
  });
92047
92494
  program.command("monitor").description("Live interactive dashboard \u2014 activity feed, approvals, security signals").action(async () => {
92048
92495
  try {
92049
- const dashboardPath = import_path108.default.join(__dirname, "dashboard.mjs");
92496
+ const dashboardPath = import_path109.default.join(__dirname, "dashboard.mjs");
92050
92497
  const dynamicImport = new Function("id", "return import(id)");
92051
92498
  const mod = await dynamicImport(`file://${dashboardPath}`);
92052
92499
  await mod.startMonitor();
@@ -92084,14 +92531,14 @@ Claude Code spawns this command every ~300ms and writes a JSON payload to stdin.
92084
92531
  Run "node9 addto claude" to register it as the statusLine.`
92085
92532
  ).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
92533
  if (subcommand === "debug") {
92087
- const flagFile = import_path108.default.join(import_os89.default.homedir(), ".node9", "hud-debug");
92534
+ const flagFile = import_path109.default.join(import_os90.default.homedir(), ".node9", "hud-debug");
92088
92535
  if (state === "on") {
92089
- import_fs109.default.mkdirSync(import_path108.default.dirname(flagFile), { recursive: true });
92090
- import_fs109.default.writeFileSync(flagFile, "");
92536
+ import_fs110.default.mkdirSync(import_path109.default.dirname(flagFile), { recursive: true });
92537
+ import_fs110.default.writeFileSync(flagFile, "");
92091
92538
  console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
92092
92539
  console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
92093
92540
  } else if (state === "off") {
92094
- if (import_fs109.default.existsSync(flagFile)) import_fs109.default.unlinkSync(flagFile);
92541
+ if (import_fs110.default.existsSync(flagFile)) import_fs110.default.unlinkSync(flagFile);
92095
92542
  console.log("HUD debug logging disabled.");
92096
92543
  } else {
92097
92544
  console.error("Usage: node9 hud debug on|off");
@@ -92206,6 +92653,7 @@ registerTrustCommand(program);
92206
92653
  registerSyncCommand(program);
92207
92654
  registerAgentsCommand(program);
92208
92655
  registerScanCommand(program);
92656
+ registerForecastCasesCommand(program);
92209
92657
  registerPostureCommand(program);
92210
92658
  registerScanRepoCommand(program);
92211
92659
  registerEgressCommand(program);
@@ -92225,9 +92673,9 @@ if (process.argv[2] !== "daemon") {
92225
92673
  const isCheckHook = process.argv[2] === "check";
92226
92674
  if (isCheckHook) {
92227
92675
  if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
92228
- const logPath = import_path108.default.join(import_os89.default.homedir(), ".node9", "hook-debug.log");
92676
+ const logPath = import_path109.default.join(import_os90.default.homedir(), ".node9", "hook-debug.log");
92229
92677
  const msg = reason instanceof Error ? reason.message : String(reason);
92230
- import_fs109.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
92678
+ import_fs110.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
92231
92679
  `);
92232
92680
  }
92233
92681
  process.exit(0);