@node9/proxy 2.47.2 → 2.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/hook.js CHANGED
@@ -18474,17 +18474,17 @@ var init_audit = __esm({
18474
18474
  // src/hook-entry.ts
18475
18475
  init_cjs_shims();
18476
18476
  var import_commander = require("commander");
18477
- var import_fs35 = __toESM(require("fs"));
18478
- var import_os28 = __toESM(require("os"));
18479
- var import_path37 = __toESM(require("path"));
18477
+ var import_fs37 = __toESM(require("fs"));
18478
+ var import_os29 = __toESM(require("os"));
18479
+ var import_path39 = __toESM(require("path"));
18480
18480
 
18481
18481
  // src/cli/commands/check.ts
18482
18482
  init_cjs_shims();
18483
18483
  var import_chalk2 = __toESM(require("chalk"));
18484
- var import_fs33 = __toESM(require("fs"));
18484
+ var import_fs35 = __toESM(require("fs"));
18485
18485
  var import_child_process3 = require("child_process");
18486
- var import_path35 = __toESM(require("path"));
18487
- var import_os26 = __toESM(require("os"));
18486
+ var import_path37 = __toESM(require("path"));
18487
+ var import_os27 = __toESM(require("os"));
18488
18488
 
18489
18489
  // src/auth/orchestrator.ts
18490
18490
  init_cjs_shims();
@@ -20945,8 +20945,8 @@ var ConfigFileSchema = import_zod.z.object({
20945
20945
  }).strict();
20946
20946
  function formatIssues(issues) {
20947
20947
  const lines = issues.map((issue) => {
20948
- const path38 = issue.path.length > 0 ? issue.path.map(String).join(".") : "root";
20949
- return ` \u2022 ${path38}: ${issue.message}`;
20948
+ const path40 = issue.path.length > 0 ? issue.path.map(String).join(".") : "root";
20949
+ return ` \u2022 ${path40}: ${issue.message}`;
20950
20950
  });
20951
20951
  return `Invalid config:
20952
20952
  ${lines.join("\n")}`;
@@ -20959,9 +20959,9 @@ function prunePaths(root, paths) {
20959
20959
  const yi = y[y.length - 1];
20960
20960
  return typeof xi === "number" && typeof yi === "number" ? yi - xi : 0;
20961
20961
  });
20962
- for (const path38 of ordered) {
20962
+ for (const path40 of ordered) {
20963
20963
  let cur = root;
20964
- for (const key of path38.slice(0, -1)) {
20964
+ for (const key of path40.slice(0, -1)) {
20965
20965
  if (cur === null || typeof cur !== "object") {
20966
20966
  cur = void 0;
20967
20967
  break;
@@ -20969,7 +20969,7 @@ function prunePaths(root, paths) {
20969
20969
  cur = cur[key];
20970
20970
  }
20971
20971
  if (cur === null || typeof cur !== "object") continue;
20972
- const last = path38[path38.length - 1];
20972
+ const last = path40[path40.length - 1];
20973
20973
  if (Array.isArray(cur)) {
20974
20974
  const i = Number(last);
20975
20975
  if (Number.isInteger(i) && i >= 0 && i < cur.length) {
@@ -21005,7 +21005,7 @@ function sanitizeConfig(raw) {
21005
21005
  return issue.keys.map((k) => [...at, k]);
21006
21006
  }
21007
21007
  return [at.slice(0, -level || void 0)];
21008
- }).filter((path38) => path38.length > 0);
21008
+ }).filter((path40) => path40.length > 0);
21009
21009
  if (paths.length === 0 || !prunePaths(working, paths)) break;
21010
21010
  }
21011
21011
  if (ConfigFileSchema.safeParse(working).success) break;
@@ -22226,13 +22226,13 @@ function getConfig(cwd) {
22226
22226
  }
22227
22227
  if (Array.isArray(mc.jailPaths)) {
22228
22228
  for (const jp of mc.jailPaths) {
22229
- const path38 = typeof jp?.path === "string" ? jp.path.trim() : "";
22230
- if (!path38) continue;
22229
+ const path40 = typeof jp?.path === "string" ? jp.path.trim() : "";
22230
+ if (!path40) continue;
22231
22231
  const verdict = jp?.verdict === "review" ? "review" : "block";
22232
- for (const r of pathRules(path38, verdict, "org-managed jail")) {
22232
+ for (const r of pathRules(path40, verdict, "org-managed jail")) {
22233
22233
  mergedPolicy.smartRules.push({ ...r, name: `org:${r.name}` });
22234
22234
  }
22235
- mergedPolicy.managedJailPaths.push({ path: path38, verdict });
22235
+ mergedPolicy.managedJailPaths.push({ path: path40, verdict });
22236
22236
  }
22237
22237
  }
22238
22238
  if (Array.isArray(mc.trustedHosts)) {
@@ -24159,6 +24159,79 @@ function recordAndCheck(tool, args, threshold = 3, windowMs = 12e4) {
24159
24159
 
24160
24160
  // src/policy/negotiation.ts
24161
24161
  init_cjs_shims();
24162
+ init_dist();
24163
+ var USER_LINE_ICON = "\u{1F6E1}\uFE0F";
24164
+ var QUOTE_INSTRUCTION = "Start your reply with the line above, exactly as written, on its own line.";
24165
+ var USER_LINE_PART_MAX = 160;
24166
+ var ONE_LINE_INPUT_MAX = 1e3;
24167
+ var PICTOGRAPHS = /[\p{Extended_Pictographic}\u200D\uFE0F]/gu;
24168
+ function oneLine(text) {
24169
+ return stripTerminalEscapes(text.slice(0, ONE_LINE_INPUT_MAX)).replace(/\s*\u2014\s*/g, ", ").replace(/\s+/g, " ").trim();
24170
+ }
24171
+ function bounded(text, max = USER_LINE_PART_MAX) {
24172
+ return text.length > max ? `${text.slice(0, max - 3).trimEnd()}...` : text;
24173
+ }
24174
+ function userLinePart(text) {
24175
+ return bounded(oneLine(text).replace(/[.\s]+$/, ""));
24176
+ }
24177
+ function userLineValue(text, max = 80) {
24178
+ const v = bounded(oneLine(text).replace(/`/g, "'"), max);
24179
+ return v ? `\`${v}\`` : "";
24180
+ }
24181
+ function userLineOf(message) {
24182
+ const first = message.split("\n", 1)[0];
24183
+ return first.startsWith(USER_LINE_ICON) ? first : void 0;
24184
+ }
24185
+ function withUserLine(line, body) {
24186
+ return `${USER_LINE_ICON} ${line}
24187
+ ${QUOTE_INSTRUCTION}
24188
+ ${body}`;
24189
+ }
24190
+ function blockedLine(context, fallback) {
24191
+ return `Node9 blocked: ${ruleWhat(context?.ruleDescription) || fallback}`;
24192
+ }
24193
+ function firstSentence(text) {
24194
+ const plain = oneLine(text.replace(PICTOGRAPHS, ""));
24195
+ return plain.split(/(?<=[.!?])\s+(?=[A-Z])/)[0] ?? plain;
24196
+ }
24197
+ function withoutLeadingVerdict(text) {
24198
+ return text.replace(/^node9\b\s*:?\s*/i, "").replace(/^blocked\b\s*:?\s*/i, "");
24199
+ }
24200
+ var IS_BLOCKED_BY = /^(.+?) (?:is|are) blocked by (.+?)\.?$/i;
24201
+ var IS_BLOCKED = /^(.+?) (?:is|are) blocked\.?$/i;
24202
+ var COMMA_BLOCKED_BY = /^(.+?), blocked by (.+?)\.?$/i;
24203
+ function ruleWhat(description) {
24204
+ if (!description) return "";
24205
+ const s = firstSentence(description).replace(/^node9\s*:\s*/i, "").replace(/^blocked\s*:\s*/i, "");
24206
+ if (!/[\p{L}\p{N}]/u.test(s)) return "";
24207
+ const by = s.match(IS_BLOCKED_BY) ?? s.match(COMMA_BLOCKED_BY);
24208
+ if (by) return userLinePart(`${by[1]} (${by[2]})`);
24209
+ const bare = s.match(IS_BLOCKED);
24210
+ return userLinePart(bare ? bare[1] : s);
24211
+ }
24212
+ function reasonSentence(reason) {
24213
+ if (!reason) return "";
24214
+ const s = withoutLeadingVerdict(firstSentence(reason));
24215
+ if (/^("[^"]*")?\.?$/.test(s)) return "";
24216
+ return userLinePart(s);
24217
+ }
24218
+ function maliciousPackageLine(reason) {
24219
+ if (!reason) return "";
24220
+ const names = [...reason.matchAll(/"([^"]+)"/g)].map((m) => {
24221
+ const spec = m[1];
24222
+ const at = spec.lastIndexOf("@");
24223
+ return at > 0 ? spec.slice(0, at) : spec;
24224
+ });
24225
+ if (names.length === 0) return "";
24226
+ const advisories = [...reason.matchAll(/\b((?:MAL|GHSA|CVE|OSV|PYSEC|RUSTSEC)-[\w-]+)/g)].map(
24227
+ (m) => m[1]
24228
+ );
24229
+ const unique2 = [...new Set(names)];
24230
+ const suffix = advisories.length ? ` (${[...new Set(advisories)].join(", ")})` : "";
24231
+ return userLinePart(
24232
+ unique2.length === 1 ? `${unique2[0]} is a known malicious package${suffix}` : `${unique2.join(", ")} are known malicious packages${suffix}`
24233
+ );
24234
+ }
24162
24235
  function buildNegotiationMessage(blockedByLabel, isHumanDecision2, humanReason, recoveryCommand, context) {
24163
24236
  if (isHumanDecision2 && context?.redirect && humanReason) {
24164
24237
  return `NODE9: The user stopped this action and gave you an instruction.
@@ -24168,88 +24241,138 @@ INSTRUCTIONS:
24168
24241
  - Retry the original action only if the instruction says you may.`;
24169
24242
  }
24170
24243
  if (isHumanDecision2) {
24171
- return `NODE9: The human user rejected this action.
24244
+ return withUserLine(
24245
+ "Node9: you blocked this action",
24246
+ `NODE9: The human user rejected this action.
24172
24247
  REASON: ${humanReason || "No specific reason provided."}
24173
24248
  INSTRUCTIONS:
24174
24249
  - Do NOT retry this exact command.
24175
24250
  - Acknowledge the block to the user and ask if there is an alternative approach.
24176
- - If you believe this action is critical, explain your reasoning and ask them to run "node9 pause 15m" to proceed.`;
24251
+ - If you believe this action is critical, explain your reasoning and ask them to run "node9 pause 15m" to proceed.`
24252
+ );
24177
24253
  }
24178
24254
  if (context?.kind === "protected-path") {
24179
24255
  const subject = context.path ? `${context.path} is a protected file` : "This file is protected";
24180
- return `NODE9: Blocked. ${subject}, and node9 stopped this call to it. The call did not run, so nothing was exposed.
24256
+ const shown = context.path ? userLineValue(context.path) : "";
24257
+ const line2 = shown ? `Node9 blocked access to ${shown} (protected file)` : "Node9 blocked access to a protected file";
24258
+ return withUserLine(
24259
+ line2,
24260
+ `NODE9: Blocked. ${subject}, and node9 stopped this call to it. The call did not run, so nothing was exposed.
24181
24261
  INSTRUCTIONS:
24182
24262
  - Do NOT retry, and do NOT reach the file another way (cat, grep, a copy, a script, a redirect).
24183
24263
  - If this step needs that file, ask the user to provide what you need or to do the step themselves.
24184
- - Tell the user node9 protected this file, then ask how to proceed.`;
24264
+ - Tell the user node9 protected this file, then ask how to proceed.`
24265
+ );
24185
24266
  }
24186
24267
  const label2 = blockedByLabel.toLowerCase();
24187
24268
  if (label2.includes("dlp") || label2.includes("secret detected") || label2.includes("credential review")) {
24188
- return `NODE9 SECURITY ALERT: A sensitive credential (API key, token, or private key) was found in your tool call arguments.
24269
+ return withUserLine(
24270
+ "Node9 blocked this command: it contains a secret",
24271
+ `NODE9 SECURITY ALERT: A sensitive credential (API key, token, or private key) was found in your tool call arguments.
24189
24272
  CRITICAL INSTRUCTION: Do NOT retry this action.
24190
24273
  REQUIRED ACTIONS:
24191
24274
  1. Remove the hardcoded credential from your command or code.
24192
24275
  2. Use an environment variable or a dedicated secrets manager instead.
24193
24276
  3. Treat the leaked credential as compromised and rotate it immediately.
24194
24277
  Do NOT attempt to bypass this check or pass the credential through another tool.
24195
- - Tell the user node9 blocked this and why, then ask how to proceed.`;
24278
+ - Tell the user node9 blocked this and why, then ask how to proceed.`
24279
+ );
24196
24280
  }
24197
24281
  if (label2.includes("package check")) {
24198
- return `NODE9: Blocked \u2014 ${humanReason || "a package in this command is known to be malicious."}
24282
+ const pkg = maliciousPackageLine(humanReason);
24283
+ return withUserLine(
24284
+ pkg ? `Node9 blocked: ${pkg}` : "Node9 blocked: this package is known to be malicious",
24285
+ `NODE9: Blocked \u2014 ${humanReason || "a package in this command is known to be malicious."}
24199
24286
  INSTRUCTIONS:
24200
24287
  - Do NOT install this package, and do NOT fetch it another way (a different package manager, a tarball URL, a git URL, a mirror).
24201
24288
  - If a dependency is needed, suggest a well-known alternative and let the user choose.
24202
- - Tell the user node9 blocked this install and name the advisory, then ask how to proceed.`;
24289
+ - Tell the user node9 blocked this install and name the advisory, then ask how to proceed.`
24290
+ );
24203
24291
  }
24204
24292
  if (label2.includes("sql safety") && label2.includes("delete without where")) {
24205
- return `NODE9: Blocked \u2014 DELETE without WHERE clause would wipe the entire table.
24293
+ return withUserLine(
24294
+ blockedLine(context, "DELETE without WHERE would wipe the whole table"),
24295
+ `NODE9: Blocked \u2014 DELETE without WHERE clause would wipe the entire table.
24206
24296
  INSTRUCTION: Add a WHERE clause to scope the deletion (e.g. WHERE id = <value>).
24207
24297
  Do NOT retry without a WHERE clause.
24208
- - Tell the user node9 blocked this and why, then ask how to proceed.`;
24298
+ - Tell the user node9 blocked this and why, then ask how to proceed.`
24299
+ );
24209
24300
  }
24210
24301
  if (label2.includes("sql safety") && label2.includes("update without where")) {
24211
- return `NODE9: Blocked \u2014 UPDATE without WHERE clause would update every row.
24302
+ return withUserLine(
24303
+ blockedLine(context, "UPDATE without WHERE would change every row"),
24304
+ `NODE9: Blocked \u2014 UPDATE without WHERE clause would update every row.
24212
24305
  INSTRUCTION: Add a WHERE clause to scope the update (e.g. WHERE id = <value>).
24213
24306
  Do NOT retry without a WHERE clause.
24214
- - Tell the user node9 blocked this and why, then ask how to proceed.`;
24307
+ - Tell the user node9 blocked this and why, then ask how to proceed.`
24308
+ );
24215
24309
  }
24216
24310
  if (label2.includes("dangerous word")) {
24217
24311
  const match = blockedByLabel.match(/dangerous word: "([^"]+)"/i);
24218
24312
  const word = match?.[1] ?? "a dangerous keyword";
24219
- return `NODE9: Blocked \u2014 command contains forbidden keyword "${word}".
24313
+ return withUserLine(
24314
+ blockedLine(context, `the command contains ${userLineValue(word)}`),
24315
+ `NODE9: Blocked \u2014 command contains forbidden keyword "${word}".
24220
24316
  INSTRUCTION: Do NOT use "${word}". Use a non-destructive alternative.
24221
24317
  Do NOT attempt to bypass this with shell tricks or aliases \u2014 it will be blocked again.
24222
- - Tell the user node9 blocked this and why, then ask how to proceed.`;
24318
+ - Tell the user node9 blocked this and why, then ask how to proceed.`
24319
+ );
24223
24320
  }
24224
24321
  if (label2.includes("path blocked") || label2.includes("sandbox")) {
24225
- return `NODE9: Blocked \u2014 operation targets a path outside the allowed sandbox.
24322
+ return withUserLine(
24323
+ blockedLine(context, "this path is outside the allowed sandbox"),
24324
+ `NODE9: Blocked \u2014 operation targets a path outside the allowed sandbox.
24226
24325
  INSTRUCTION: Move your output to an allowed directory such as /tmp/ or the project directory.
24227
24326
  Do NOT retry on the same path.
24228
- - Tell the user node9 blocked this and why, then ask how to proceed.`;
24327
+ - Tell the user node9 blocked this and why, then ask how to proceed.`
24328
+ );
24229
24329
  }
24230
24330
  if (label2.includes("inline execution")) {
24231
- return `NODE9: Blocked \u2014 inline code execution (e.g. bash -c "...") is not allowed.
24331
+ return withUserLine(
24332
+ blockedLine(context, "inline code execution is not allowed"),
24333
+ `NODE9: Blocked \u2014 inline code execution (e.g. bash -c "...") is not allowed.
24232
24334
  INSTRUCTION: Use individual tool calls instead of embedding code in a shell string.
24233
- - Tell the user node9 blocked this and why, then ask how to proceed.`;
24335
+ - Tell the user node9 blocked this and why, then ask how to proceed.`
24336
+ );
24234
24337
  }
24235
24338
  if (label2.includes("strict mode")) {
24236
- return `NODE9: Blocked \u2014 the Block all level is on. All tool calls require explicit human approval.
24237
- INSTRUCTION: Inform the user this action is pending approval. Wait for them to approve via the dashboard or run "node9 pause".`;
24339
+ return withUserLine(
24340
+ "Node9: the Block all level is on, so every action needs your approval",
24341
+ `NODE9: Blocked \u2014 the Block all level is on. All tool calls require explicit human approval.
24342
+ INSTRUCTION: Inform the user this action is pending approval. Wait for them to approve via the dashboard or run "node9 pause".`
24343
+ );
24238
24344
  }
24239
24345
  if (label2.includes("rule") && label2.includes("default block")) {
24240
24346
  const match = blockedByLabel.match(/rule "([^"]+)"/i);
24241
- const rule = match?.[1] ?? "a policy rule";
24242
- return `NODE9: Blocked \u2014 action "${rule}" is forbidden by security policy.
24243
- INSTRUCTION: Do NOT use "${rule}". Find a read-only or non-destructive alternative.
24347
+ const rule2 = match?.[1] ?? "a policy rule";
24348
+ return withUserLine(
24349
+ blockedLine(context, `${userLineValue(rule2)} is not allowed by policy`),
24350
+ `NODE9: Blocked \u2014 action "${rule2}" is forbidden by security policy.
24351
+ INSTRUCTION: Do NOT use "${rule2}". Find a read-only or non-destructive alternative.
24244
24352
  Do NOT attempt to bypass this rule.
24245
- - Tell the user node9 blocked this and why, then ask how to proceed.`;
24353
+ - Tell the user node9 blocked this and why, then ask how to proceed.`
24354
+ );
24355
+ }
24356
+ const timedOut = label2.includes("approval timeout");
24357
+ const rule = ruleWhat(context?.ruleDescription);
24358
+ let line;
24359
+ if (timedOut) {
24360
+ line = rule ? `Node9 blocked: no one approved this in time (${rule.replace(/ \(([^()]+)\)$/, ", $1")})` : "Node9 blocked: no one approved this in time";
24361
+ } else if (rule) {
24362
+ line = `Node9 blocked: ${rule}`;
24363
+ } else {
24364
+ const why = reasonSentence(humanReason);
24365
+ line = why ? `Node9 blocked: ${why}` : `Node9 blocked this action (${userLinePart(agentFacingLabel(blockedByLabel))})`;
24246
24366
  }
24247
24367
  const recovery = recoveryCommand ? `
24248
24368
  REQUIRED ACTION: Run \`${recoveryCommand}\` first, then retry your original command.` : "\n- Pivot to a non-destructive or read-only alternative.";
24249
- return `NODE9: Action blocked by security policy [${agentFacingLabel(blockedByLabel)}].
24369
+ return withUserLine(
24370
+ line,
24371
+ `NODE9: Action blocked by security policy [${agentFacingLabel(blockedByLabel)}].
24250
24372
  INSTRUCTIONS:
24251
24373
  - Do NOT retry this exact command or attempt to bypass the rule.${recovery}
24252
- - Inform the user which security rule was triggered and ask how to proceed.`;
24374
+ - Inform the user which security rule was triggered and ask how to proceed.`
24375
+ );
24253
24376
  }
24254
24377
  function buildReviewMessage(blockedByLabel, ruleDescription, reason) {
24255
24378
  const why = ruleDescription || (reason && reason !== blockedByLabel ? reason : void 0) || blockedByLabel || "this action needs your review";
@@ -24264,7 +24387,7 @@ function isHumanDecision(label2, blockedBy, decidedBy) {
24264
24387
  return l.includes("user") || l.includes("daemon") || l.includes("decision");
24265
24388
  }
24266
24389
  function agentFacingLabel(label2) {
24267
- const plain = label2.replace(/[\p{Extended_Pictographic}\u200D\uFE0F]/gu, "").replace(/\bNode9\b/g, "node9").replace(/\s+/g, " ").trim();
24390
+ const plain = label2.replace(PICTOGRAPHS, "").replace(/\bNode9\b/g, "node9").replace(/\s+/g, " ").trim();
24268
24391
  return plain || "Security Policy";
24269
24392
  }
24270
24393
  var BLOCKED_BY = " is blocked by ";
@@ -26308,6 +26431,109 @@ function canonicalToolInput(rawToolName, input) {
26308
26431
  return canonical;
26309
26432
  }
26310
26433
 
26434
+ // src/utils/own-dir.ts
26435
+ init_cjs_shims();
26436
+ var import_fs33 = __toESM(require("fs"));
26437
+ var import_path35 = __toESM(require("path"));
26438
+ var SESSION_FLAG_SUFFIX = ".node9-flag";
26439
+ var SESSION_FLAG_NAME = /^[A-Za-z0-9_-]{1,128}\.node9-flag$/;
26440
+ function isSessionFlagName(name) {
26441
+ return SESSION_FLAG_NAME.test(name);
26442
+ }
26443
+ function ownRealDir(st) {
26444
+ if (st.isSymbolicLink() || !st.isDirectory()) return false;
26445
+ return typeof process.getuid !== "function" || st.uid === process.getuid();
26446
+ }
26447
+ function ensureOwnDir(dir2) {
26448
+ try {
26449
+ import_fs33.default.mkdirSync(dir2, { recursive: true, mode: 448 });
26450
+ const st = import_fs33.default.lstatSync(dir2);
26451
+ return ownRealDir(st) ? { dev: st.dev, ino: st.ino } : null;
26452
+ } catch {
26453
+ return null;
26454
+ }
26455
+ }
26456
+ function isSameOwnDir(dir2, seen) {
26457
+ try {
26458
+ const st = import_fs33.default.lstatSync(dir2);
26459
+ return ownRealDir(st) && st.dev === seen.dev && st.ino === seen.ino;
26460
+ } catch {
26461
+ return false;
26462
+ }
26463
+ }
26464
+ function removeOldFlag(dir2, seen, name, cutoffMs) {
26465
+ if (!isSessionFlagName(name)) return;
26466
+ const p = import_path35.default.join(dir2, name);
26467
+ try {
26468
+ const st = import_fs33.default.lstatSync(p);
26469
+ if (!st.isFile() || st.mtimeMs >= cutoffMs) return;
26470
+ if (!isSameOwnDir(dir2, seen)) return;
26471
+ import_fs33.default.unlinkSync(p);
26472
+ } catch {
26473
+ }
26474
+ }
26475
+
26476
+ // src/notices/session-notice.ts
26477
+ init_cjs_shims();
26478
+ var import_fs34 = __toESM(require("fs"));
26479
+ var import_os26 = __toESM(require("os"));
26480
+ var import_path36 = __toESM(require("path"));
26481
+ init_dist();
26482
+ var ICON = "\u{1F6E1}\uFE0F";
26483
+ var LEVEL_NAMES = {
26484
+ audit: "Audit",
26485
+ soft: "Soft",
26486
+ standard: "Standard",
26487
+ strict: "Strict",
26488
+ "block-all": "Block all"
26489
+ };
26490
+ var SESSION_NOTICE_INSTRUCTION = "Start your next reply to the user with the line above, exactly as written, on its own line.";
26491
+ var FLAG_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
26492
+ var SAFE_SESSION_ID = /^[A-Za-z0-9_-]{1,128}$/;
26493
+ function sessionNoticeLine(mode) {
26494
+ const level = levelOf(mode);
26495
+ if (level === "audit") {
26496
+ return `${ICON} Node9 is in Audit mode: actions are logged, nothing is blocked.`;
26497
+ }
26498
+ return `${ICON} Node9 is on (${LEVEL_NAMES[level]}). Every action in this session is checked.`;
26499
+ }
26500
+ function sessionNoticeOutput(mode) {
26501
+ const line = sessionNoticeLine(mode);
26502
+ return JSON.stringify({
26503
+ hookSpecificOutput: {
26504
+ hookEventName: "PreToolUse",
26505
+ additionalContext: `${line}
26506
+ ${SESSION_NOTICE_INSTRUCTION}`
26507
+ },
26508
+ systemMessage: line
26509
+ });
26510
+ }
26511
+ function sessionNoticeEnabled(env = process.env) {
26512
+ if (env.NODE9_SESSION_NOTICE === "1") return true;
26513
+ return env.NODE9_TESTING !== "1";
26514
+ }
26515
+ function claimSessionNotice(sessionId, home = import_os26.default.homedir()) {
26516
+ if (!sessionId || !SAFE_SESSION_ID.test(sessionId)) return false;
26517
+ const dir2 = import_path36.default.join(home, ".node9", "notice-sessions");
26518
+ const seen = ensureOwnDir(dir2);
26519
+ if (!seen) return false;
26520
+ try {
26521
+ if (!isSameOwnDir(dir2, seen)) return false;
26522
+ import_fs34.default.closeSync(import_fs34.default.openSync(import_path36.default.join(dir2, sessionId + SESSION_FLAG_SUFFIX), "wx"));
26523
+ } catch {
26524
+ return false;
26525
+ }
26526
+ removeOldFlags(dir2, seen);
26527
+ return true;
26528
+ }
26529
+ function removeOldFlags(dir2, seen) {
26530
+ const cutoff = Date.now() - FLAG_TTL_MS;
26531
+ try {
26532
+ for (const name of import_fs34.default.readdirSync(dir2)) removeOldFlag(dir2, seen, name, cutoff);
26533
+ } catch {
26534
+ }
26535
+ }
26536
+
26311
26537
  // src/cli/commands/check.ts
26312
26538
  function detectAiAgent(payload) {
26313
26539
  const meta = payload.meta;
@@ -26386,9 +26612,9 @@ function registerCheckCommand(program2) {
26386
26612
  } catch (err) {
26387
26613
  const tempConfig = getConfig();
26388
26614
  if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
26389
- const logPath = import_path35.default.join(import_os26.default.homedir(), ".node9", "hook-debug.log");
26615
+ const logPath = import_path37.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
26390
26616
  const errMsg = err instanceof Error ? err.message : String(err);
26391
- import_fs33.default.appendFileSync(
26617
+ import_fs35.default.appendFileSync(
26392
26618
  logPath,
26393
26619
  `[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
26394
26620
  RAW: ${raw}
@@ -26401,14 +26627,14 @@ RAW: ${raw}
26401
26627
  const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
26402
26628
  if (process.env.NODE9_DEBUG === "1") {
26403
26629
  try {
26404
- const logPath = import_path35.default.join(import_os26.default.homedir(), ".node9", "hook-debug.log");
26405
- if (!import_fs33.default.existsSync(import_path35.default.dirname(logPath)))
26406
- import_fs33.default.mkdirSync(import_path35.default.dirname(logPath), { recursive: true });
26630
+ const logPath = import_path37.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
26631
+ if (!import_fs35.default.existsSync(import_path37.default.dirname(logPath)))
26632
+ import_fs35.default.mkdirSync(import_path37.default.dirname(logPath), { recursive: true });
26407
26633
  const sanitized = JSON.stringify({
26408
26634
  ...payload,
26409
26635
  prompt: `<redacted, ${prompt.length} bytes>`
26410
26636
  });
26411
- import_fs33.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${sanitized}
26637
+ import_fs35.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${sanitized}
26412
26638
  `);
26413
26639
  } catch {
26414
26640
  }
@@ -26426,7 +26652,7 @@ RAW: ${raw}
26426
26652
  if (!dlpMatch && !canaryHit) {
26427
26653
  try {
26428
26654
  const sid = typeof payload.session_id === "string" ? payload.session_id : typeof payload.conversationId === "string" ? payload.conversationId : "";
26429
- const promptCwd = typeof payload.cwd === "string" && import_path35.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
26655
+ const promptCwd = typeof payload.cwd === "string" && import_path37.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
26430
26656
  if (sid && getConfig(promptCwd).policy.untrustedContent?.enabled) {
26431
26657
  await notifyUserMentions(sid, extractContentDestinations(prompt));
26432
26658
  }
@@ -26467,8 +26693,8 @@ RAW: ${raw}
26467
26693
  );
26468
26694
  const reason = `\u{1F6A8} Node9 DLP: ${shownPattern} detected in prompt (${shownSample}). Prompt was not submitted \u2014 remove the credential and try again.`;
26469
26695
  try {
26470
- const ttyFd = import_fs33.default.openSync("/dev/tty", "w");
26471
- import_fs33.default.writeSync(
26696
+ const ttyFd = import_fs35.default.openSync("/dev/tty", "w");
26697
+ import_fs35.default.writeSync(
26472
26698
  ttyFd,
26473
26699
  import_chalk2.default.bgRed.white.bold(`
26474
26700
  \u{1F6A8} NODE9 DLP \u2014 PROMPT BLOCKED
@@ -26478,7 +26704,7 @@ RAW: ${raw}
26478
26704
 
26479
26705
  `)
26480
26706
  );
26481
- import_fs33.default.closeSync(ttyFd);
26707
+ import_fs35.default.closeSync(ttyFd);
26482
26708
  } catch {
26483
26709
  }
26484
26710
  const isCodex = agent2 === "Codex";
@@ -26497,17 +26723,17 @@ RAW: ${raw}
26497
26723
  process.exit(2);
26498
26724
  }
26499
26725
  const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
26500
- const safeCwdForConfig = typeof payloadCwd === "string" && import_path35.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
26726
+ const safeCwdForConfig = typeof payloadCwd === "string" && import_path37.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
26501
26727
  const config = getConfig(safeCwdForConfig);
26502
26728
  const daemonDown = !isDaemonRunning();
26503
26729
  if (config.settings.autoStartDaemon && daemonDown && !process.env.NODE9_NO_AUTO_DAEMON) {
26504
26730
  try {
26505
26731
  const scriptPath = process.argv[1];
26506
- if (typeof scriptPath !== "string" || !import_path35.default.isAbsolute(scriptPath))
26732
+ if (typeof scriptPath !== "string" || !import_path37.default.isAbsolute(scriptPath))
26507
26733
  throw new Error("node9: argv[1] is not an absolute path");
26508
- const resolvedScript = import_fs33.default.realpathSync(scriptPath);
26509
- const packageDist = import_fs33.default.realpathSync(import_path35.default.resolve(__dirname, "../.."));
26510
- if (!resolvedScript.startsWith(packageDist + import_path35.default.sep) && resolvedScript !== packageDist)
26734
+ const resolvedScript = import_fs35.default.realpathSync(scriptPath);
26735
+ const packageDist = import_fs35.default.realpathSync(import_path37.default.resolve(__dirname, "../.."));
26736
+ if (!resolvedScript.startsWith(packageDist + import_path37.default.sep) && resolvedScript !== packageDist)
26511
26737
  throw new Error(
26512
26738
  `node9: daemon spawn aborted \u2014 argv[1] (${resolvedScript}) is outside package dist (${packageDist})`
26513
26739
  );
@@ -26537,17 +26763,17 @@ RAW: ${raw}
26537
26763
  } finally {
26538
26764
  if (startupFd !== void 0) {
26539
26765
  try {
26540
- import_fs33.default.closeSync(startupFd);
26766
+ import_fs35.default.closeSync(startupFd);
26541
26767
  } catch {
26542
26768
  }
26543
26769
  }
26544
26770
  }
26545
26771
  } catch (spawnErr) {
26546
- const logPath = import_path35.default.join(import_os26.default.homedir(), ".node9", "hook-debug.log");
26772
+ const logPath = import_path37.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
26547
26773
  const msg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);
26548
26774
  recordStartupState("failed", "spawn-aborted", msg);
26549
26775
  try {
26550
- import_fs33.default.appendFileSync(
26776
+ import_fs35.default.appendFileSync(
26551
26777
  logPath,
26552
26778
  `[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-autostart-failed: ${msg}
26553
26779
  `
@@ -26561,10 +26787,10 @@ RAW: ${raw}
26561
26787
  );
26562
26788
  }
26563
26789
  if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
26564
- const logPath = import_path35.default.join(import_os26.default.homedir(), ".node9", "hook-debug.log");
26565
- if (!import_fs33.default.existsSync(import_path35.default.dirname(logPath)))
26566
- import_fs33.default.mkdirSync(import_path35.default.dirname(logPath), { recursive: true });
26567
- import_fs33.default.appendFileSync(
26790
+ const logPath = import_path37.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
26791
+ if (!import_fs35.default.existsSync(import_path37.default.dirname(logPath)))
26792
+ import_fs35.default.mkdirSync(import_path37.default.dirname(logPath), { recursive: true });
26793
+ import_fs35.default.appendFileSync(
26568
26794
  logPath,
26569
26795
  `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${JSON.stringify(raw)}
26570
26796
  `
@@ -26585,8 +26811,8 @@ RAW: ${raw}
26585
26811
  );
26586
26812
  let ttyFd = null;
26587
26813
  try {
26588
- ttyFd = import_fs33.default.openSync("/dev/tty", "w");
26589
- const writeTty = (line) => import_fs33.default.writeSync(ttyFd, line + "\n");
26814
+ ttyFd = import_fs35.default.openSync("/dev/tty", "w");
26815
+ const writeTty = (line) => import_fs35.default.writeSync(ttyFd, line + "\n");
26590
26816
  if (result2?.blockKind === "protected-path") {
26591
26817
  writeTty(
26592
26818
  import_chalk2.default.red.bold(
@@ -26613,7 +26839,7 @@ RAW: ${raw}
26613
26839
  } finally {
26614
26840
  if (ttyFd !== null)
26615
26841
  try {
26616
- import_fs33.default.closeSync(ttyFd);
26842
+ import_fs35.default.closeSync(ttyFd);
26617
26843
  } catch {
26618
26844
  }
26619
26845
  }
@@ -26622,7 +26848,12 @@ RAW: ${raw}
26622
26848
  humanDecision,
26623
26849
  msg,
26624
26850
  result2?.recoveryCommand,
26625
- { kind: result2?.blockKind, path: result2?.blockedPath, redirect: !!result2?.redirect }
26851
+ {
26852
+ kind: result2?.blockKind,
26853
+ path: result2?.blockedPath,
26854
+ redirect: !!result2?.redirect,
26855
+ ruleDescription: result2?.ruleDescription
26856
+ }
26626
26857
  );
26627
26858
  if (agent === "Antigravity") {
26628
26859
  process.stdout.write(
@@ -26639,11 +26870,13 @@ RAW: ${raw}
26639
26870
  );
26640
26871
  process.exit(0);
26641
26872
  }
26873
+ const userLine = userLineOf(aiFeedbackMessage);
26642
26874
  process.stdout.write(
26643
26875
  JSON.stringify({
26644
26876
  decision: "block",
26645
26877
  reason: aiFeedbackMessage,
26646
26878
  // This is the core instruction
26879
+ ...agent === "Claude Code" && userLine ? { systemMessage: userLine } : {},
26647
26880
  hookSpecificOutput: {
26648
26881
  hookEventName: "PreToolUse",
26649
26882
  permissionDecision: "deny",
@@ -26677,8 +26910,8 @@ RAW: ${raw}
26677
26910
  } catch {
26678
26911
  }
26679
26912
  try {
26680
- const ttyFd = import_fs33.default.openSync("/dev/tty", "w");
26681
- import_fs33.default.writeSync(
26913
+ const ttyFd = import_fs35.default.openSync("/dev/tty", "w");
26914
+ import_fs35.default.writeSync(
26682
26915
  ttyFd,
26683
26916
  import_chalk2.default.yellow(
26684
26917
  `
@@ -26686,7 +26919,7 @@ RAW: ${raw}
26686
26919
  `
26687
26920
  )
26688
26921
  );
26689
- import_fs33.default.closeSync(ttyFd);
26922
+ import_fs35.default.closeSync(ttyFd);
26690
26923
  } catch {
26691
26924
  }
26692
26925
  if (agent === "GitHub Copilot") {
@@ -26718,17 +26951,20 @@ RAW: ${raw}
26718
26951
  const safeSessionId = /^[A-Za-z0-9_\-]{1,128}$/.test(rawSessionId) ? rawSessionId : "";
26719
26952
  if (skillPinCfg.enabled && safeSessionId) {
26720
26953
  try {
26721
- const sessionsDir = import_path35.default.join(import_os26.default.homedir(), ".node9", "skill-sessions");
26722
- const flagPath = import_path35.default.join(sessionsDir, `${safeSessionId}.json`);
26954
+ const sessionsDir = import_path37.default.join(import_os27.default.homedir(), ".node9", "skill-sessions");
26955
+ const flagPath = import_path37.default.join(sessionsDir, `${safeSessionId}${SESSION_FLAG_SUFFIX}`);
26956
+ const memo = ensureOwnDir(sessionsDir);
26723
26957
  let flag = null;
26724
26958
  try {
26725
- flag = JSON.parse(import_fs33.default.readFileSync(flagPath, "utf-8"));
26959
+ const st = memo ? import_fs35.default.lstatSync(flagPath) : null;
26960
+ if (st && st.isFile() && st.nlink === 1)
26961
+ flag = JSON.parse(import_fs35.default.readFileSync(flagPath, "utf-8"));
26726
26962
  } catch {
26727
26963
  }
26728
26964
  const writeFlag = (data2) => {
26965
+ if (!memo || !isSameOwnDir(sessionsDir, memo)) return;
26729
26966
  try {
26730
- import_fs33.default.mkdirSync(sessionsDir, { recursive: true });
26731
- import_fs33.default.writeFileSync(
26967
+ atomicWriteSync(
26732
26968
  flagPath,
26733
26969
  JSON.stringify({ ...data2, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
26734
26970
  { mode: 384 }
@@ -26739,8 +26975,8 @@ RAW: ${raw}
26739
26975
  const sendSkillWarn = (detail, recoveryCmd) => {
26740
26976
  let ttyFd = null;
26741
26977
  try {
26742
- ttyFd = import_fs33.default.openSync("/dev/tty", "w");
26743
- const w = (line) => import_fs33.default.writeSync(ttyFd, line + "\n");
26978
+ ttyFd = import_fs35.default.openSync("/dev/tty", "w");
26979
+ const w = (line) => import_fs35.default.writeSync(ttyFd, line + "\n");
26744
26980
  w(import_chalk2.default.yellow(`
26745
26981
  \u26A0\uFE0F Node9: installed skill drift detected`));
26746
26982
  w(import_chalk2.default.gray(` ${detail}`));
@@ -26755,7 +26991,7 @@ RAW: ${raw}
26755
26991
  } finally {
26756
26992
  if (ttyFd !== null)
26757
26993
  try {
26758
- import_fs33.default.closeSync(ttyFd);
26994
+ import_fs35.default.closeSync(ttyFd);
26759
26995
  } catch {
26760
26996
  }
26761
26997
  }
@@ -26771,7 +27007,7 @@ RAW: ${raw}
26771
27007
  return;
26772
27008
  }
26773
27009
  if (!flag || flag.state !== "verified" && flag.state !== "warned") {
26774
- const absoluteCwd = typeof payloadCwd === "string" && import_path35.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
27010
+ const absoluteCwd = typeof payloadCwd === "string" && import_path37.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
26775
27011
  const extraRoots = skillPinCfg.roots;
26776
27012
  const resolvedExtra = extraRoots.map((r) => resolveUserSkillRoot(r, absoluteCwd)).filter((r) => typeof r === "string");
26777
27013
  const roots = [...defaultSkillRoots(absoluteCwd), ...resolvedExtra];
@@ -26810,31 +27046,27 @@ RAW: ${raw}
26810
27046
  } else {
26811
27047
  writeFlag({ state: "verified" });
26812
27048
  }
26813
- try {
26814
- const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1e3;
26815
- for (const name of import_fs33.default.readdirSync(sessionsDir)) {
26816
- const p = import_path35.default.join(sessionsDir, name);
26817
- try {
26818
- if (import_fs33.default.statSync(p).mtimeMs < cutoff) import_fs33.default.unlinkSync(p);
26819
- } catch {
26820
- }
27049
+ if (memo)
27050
+ try {
27051
+ const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1e3;
27052
+ for (const name of import_fs35.default.readdirSync(sessionsDir))
27053
+ removeOldFlag(sessionsDir, memo, name, cutoff);
27054
+ } catch {
26821
27055
  }
26822
- } catch {
26823
- }
26824
27056
  }
26825
27057
  } catch (err) {
26826
27058
  if (process.env.NODE9_DEBUG === "1") {
26827
27059
  try {
26828
- const dbg = import_path35.default.join(import_os26.default.homedir(), ".node9", "hook-debug.log");
27060
+ const dbg = import_path37.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
26829
27061
  const msg = err instanceof Error ? err.message : String(err);
26830
- import_fs33.default.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
27062
+ import_fs35.default.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
26831
27063
  `);
26832
27064
  } catch {
26833
27065
  }
26834
27066
  }
26835
27067
  }
26836
27068
  }
26837
- const safeCwdForAuth = typeof payloadCwd === "string" && import_path35.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
27069
+ const safeCwdForAuth = typeof payloadCwd === "string" && import_path37.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
26838
27070
  const askMode = resolveAskMode(agent, opts, config);
26839
27071
  const result = await authorizeHeadless(toolName, toolInput, meta, {
26840
27072
  cwd: safeCwdForAuth,
@@ -26847,11 +27079,21 @@ RAW: ${raw}
26847
27079
  } catch {
26848
27080
  }
26849
27081
  }
27082
+ const isSubagentCall = typeof payload.agent_id === "string" && payload.agent_id !== "";
27083
+ const allowExit = (checkedBy) => {
27084
+ if (checkedBy !== "paused" && agent === "Claude Code" && !isSubagentCall && sessionNoticeEnabled() && claimSessionNotice(safeSessionId)) {
27085
+ try {
27086
+ import_fs35.default.writeSync(1, sessionNoticeOutput(config.settings.mode) + "\n");
27087
+ } catch {
27088
+ }
27089
+ }
27090
+ process.exit(0);
27091
+ };
26850
27092
  if (result.approved) {
26851
27093
  if (result.checkedBy && process.env.NODE9_DEBUG === "1")
26852
27094
  process.stderr.write(`\u2713 node9 [${result.checkedBy}]: "${toolName}" allowed
26853
27095
  `);
26854
- process.exit(0);
27096
+ allowExit(result.checkedBy);
26855
27097
  }
26856
27098
  if (result.review) {
26857
27099
  sendAsk(result);
@@ -26859,12 +27101,12 @@ RAW: ${raw}
26859
27101
  }
26860
27102
  if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && !process.stdout.isTTY && config.settings.autoStartDaemon) {
26861
27103
  try {
26862
- const tty = import_fs33.default.openSync("/dev/tty", "w");
26863
- import_fs33.default.writeSync(
27104
+ const tty = import_fs35.default.openSync("/dev/tty", "w");
27105
+ import_fs35.default.writeSync(
26864
27106
  tty,
26865
27107
  import_chalk2.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically...\n")
26866
27108
  );
26867
- import_fs33.default.closeSync(tty);
27109
+ import_fs35.default.closeSync(tty);
26868
27110
  } catch {
26869
27111
  }
26870
27112
  const daemonReady = await autoStartDaemonAndWait();
@@ -26876,7 +27118,7 @@ RAW: ${raw}
26876
27118
  if (retry.checkedBy && process.env.NODE9_DEBUG === "1")
26877
27119
  process.stderr.write(`\u2713 node9 [${retry.checkedBy}]: "${toolName}" allowed
26878
27120
  `);
26879
- process.exit(0);
27121
+ allowExit(retry.checkedBy);
26880
27122
  }
26881
27123
  sendBlock(retry.reason ?? `Node9 blocked "${toolName}".`, {
26882
27124
  ...retry,
@@ -26891,9 +27133,9 @@ RAW: ${raw}
26891
27133
  });
26892
27134
  } catch (err) {
26893
27135
  if (process.env.NODE9_DEBUG === "1") {
26894
- const logPath = import_path35.default.join(import_os26.default.homedir(), ".node9", "hook-debug.log");
27136
+ const logPath = import_path37.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
26895
27137
  const errMsg = err instanceof Error ? err.message : String(err);
26896
- import_fs33.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
27138
+ import_fs35.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
26897
27139
  `);
26898
27140
  }
26899
27141
  process.exit(0);
@@ -26928,9 +27170,9 @@ RAW: ${raw}
26928
27170
 
26929
27171
  // src/cli/commands/log.ts
26930
27172
  init_cjs_shims();
26931
- var import_fs34 = __toESM(require("fs"));
26932
- var import_path36 = __toESM(require("path"));
26933
- var import_os27 = __toESM(require("os"));
27173
+ var import_fs36 = __toESM(require("fs"));
27174
+ var import_path38 = __toESM(require("path"));
27175
+ var import_os28 = __toESM(require("os"));
26934
27176
  init_audit();
26935
27177
  init_dist();
26936
27178
 
@@ -27198,12 +27440,12 @@ function registerLogCommand(program2) {
27198
27440
  if (rawToolName !== tool) entry.agentToolName = rawToolName;
27199
27441
  const payloadSessionId = payload.session_id ?? payload.conversationId;
27200
27442
  if (payloadSessionId) entry.sessionId = payloadSessionId;
27201
- const logPath = import_path36.default.join(import_os27.default.homedir(), ".node9", "audit.log");
27202
- if (!import_fs34.default.existsSync(import_path36.default.dirname(logPath)))
27203
- import_fs34.default.mkdirSync(import_path36.default.dirname(logPath), { recursive: true });
27204
- import_fs34.default.appendFileSync(logPath, JSON.stringify(entry) + "\n");
27443
+ const logPath = import_path38.default.join(import_os28.default.homedir(), ".node9", "audit.log");
27444
+ if (!import_fs36.default.existsSync(import_path38.default.dirname(logPath)))
27445
+ import_fs36.default.mkdirSync(import_path38.default.dirname(logPath), { recursive: true });
27446
+ import_fs36.default.appendFileSync(logPath, JSON.stringify(entry) + "\n");
27205
27447
  const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
27206
- const safeCwd = typeof payloadCwd === "string" && import_path36.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
27448
+ const safeCwd = typeof payloadCwd === "string" && import_path38.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
27207
27449
  if ((tool === "Bash" || tool === "bash") && isDaemonRunning()) {
27208
27450
  const command = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
27209
27451
  if (command) {
@@ -27424,9 +27666,9 @@ function registerLogCommand(program2) {
27424
27666
  const msg = err instanceof Error ? err.message : String(err);
27425
27667
  process.stderr.write(`[Node9] audit log error: ${msg}
27426
27668
  `);
27427
- const debugPath = import_path36.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
27669
+ const debugPath = import_path38.default.join(import_os28.default.homedir(), ".node9", "hook-debug.log");
27428
27670
  try {
27429
- import_fs34.default.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
27671
+ import_fs36.default.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
27430
27672
  `);
27431
27673
  } catch {
27432
27674
  }
@@ -27449,9 +27691,9 @@ function registerLogCommand(program2) {
27449
27691
  });
27450
27692
  }
27451
27693
  function absolutePathFor(spelled, cwd) {
27452
- const expanded = spelled === "~" || spelled.startsWith("~/") ? import_path36.default.join(import_os27.default.homedir(), spelled.slice(1)) : spelled;
27453
- if (import_path36.default.isAbsolute(expanded)) return expanded;
27454
- return cwd ? import_path36.default.resolve(cwd, expanded) : import_path36.default.resolve(expanded);
27694
+ const expanded = spelled === "~" || spelled.startsWith("~/") ? import_path38.default.join(import_os28.default.homedir(), spelled.slice(1)) : spelled;
27695
+ if (import_path38.default.isAbsolute(expanded)) return expanded;
27696
+ return cwd ? import_path38.default.resolve(cwd, expanded) : import_path38.default.resolve(expanded);
27455
27697
  }
27456
27698
  async function carryTaintAcrossWrites(command, cwd) {
27457
27699
  let pairs;
@@ -27470,13 +27712,13 @@ async function carryTaintAcrossWrites(command, cwd) {
27470
27712
  continue;
27471
27713
  }
27472
27714
  const sensitive = scanFilePath(src, cwd);
27473
- if (sensitive) await notifyTaint(dst, `copy:${sensitive.patternName}:${import_path36.default.basename(src)}`);
27715
+ if (sensitive) await notifyTaint(dst, `copy:${sensitive.patternName}:${import_path38.default.basename(src)}`);
27474
27716
  }
27475
27717
  }
27476
27718
 
27477
27719
  // src/hook-entry.ts
27478
27720
  var { version } = JSON.parse(
27479
- import_fs35.default.readFileSync(import_path37.default.join(__dirname, "../package.json"), "utf-8")
27721
+ import_fs37.default.readFileSync(import_path39.default.join(__dirname, "../package.json"), "utf-8")
27480
27722
  );
27481
27723
  var program = new import_commander.Command();
27482
27724
  program.name("node9").description("The Sudo Command for AI Agents").version(version);
@@ -27485,9 +27727,9 @@ registerLogCommand(program);
27485
27727
  process.on("unhandledRejection", (reason) => {
27486
27728
  if (process.argv[2] === "check") {
27487
27729
  if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
27488
- const logPath = import_path37.default.join(import_os28.default.homedir(), ".node9", "hook-debug.log");
27730
+ const logPath = import_path39.default.join(import_os29.default.homedir(), ".node9", "hook-debug.log");
27489
27731
  const msg = reason instanceof Error ? reason.message : String(reason);
27490
- import_fs35.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
27732
+ import_fs37.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
27491
27733
  `);
27492
27734
  }
27493
27735
  process.exit(0);