@node9/proxy 2.47.2 → 2.48.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 +959 -660
- package/dist/dashboard.mjs +1 -0
- package/dist/hook.js +407 -118
- package/dist/index.js +1 -0
- package/dist/index.mjs +1 -0
- package/package.json +1 -1
- package/dist/cli-main.d.mts +0 -1
- package/dist/cli-main.d.ts +0 -1
- package/dist/cli-main.mjs +0 -90554
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
|
|
18478
|
-
var
|
|
18479
|
-
var
|
|
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
|
|
18484
|
+
var import_fs35 = __toESM(require("fs"));
|
|
18485
18485
|
var import_child_process3 = require("child_process");
|
|
18486
|
-
var
|
|
18487
|
-
var
|
|
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
|
|
20949
|
-
return ` \u2022 ${
|
|
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
|
|
20962
|
+
for (const path40 of ordered) {
|
|
20963
20963
|
let cur = root;
|
|
20964
|
-
for (const key of
|
|
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 =
|
|
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((
|
|
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
|
|
22230
|
-
if (!
|
|
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(
|
|
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:
|
|
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 about this blocked action with the line above, exactly as written, on its own line. Leave it out of later replies.";
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
24237
|
-
|
|
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
|
|
24242
|
-
return
|
|
24243
|
-
|
|
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
|
|
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(
|
|
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,155 @@ 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_NOTE = "node9 shows the user this line itself. Do not mention it unless the user asks about node9.";
|
|
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, kind = "first") {
|
|
26494
|
+
const level = levelOf(mode);
|
|
26495
|
+
const name = LEVEL_NAMES[level];
|
|
26496
|
+
if (kind === "changed") {
|
|
26497
|
+
return level === "audit" ? `${ICON} Node9 level changed to Audit: actions are logged, nothing is blocked.` : `${ICON} Node9 level changed to ${name}. Every action in this session is checked.`;
|
|
26498
|
+
}
|
|
26499
|
+
if (level === "audit") {
|
|
26500
|
+
return `${ICON} Node9 is in Audit mode: actions are logged, nothing is blocked.`;
|
|
26501
|
+
}
|
|
26502
|
+
return `${ICON} Node9 is on (${name}). Every action in this session is checked.`;
|
|
26503
|
+
}
|
|
26504
|
+
function sessionNoticeOutput(mode, kind = "first") {
|
|
26505
|
+
const line = sessionNoticeLine(mode, kind);
|
|
26506
|
+
return JSON.stringify({
|
|
26507
|
+
hookSpecificOutput: {
|
|
26508
|
+
hookEventName: "PreToolUse",
|
|
26509
|
+
additionalContext: `${line}
|
|
26510
|
+
${SESSION_NOTICE_NOTE}`
|
|
26511
|
+
},
|
|
26512
|
+
systemMessage: line
|
|
26513
|
+
});
|
|
26514
|
+
}
|
|
26515
|
+
function sessionNoticeEnabled(env = process.env) {
|
|
26516
|
+
if (env.NODE9_SESSION_NOTICE === "1") return true;
|
|
26517
|
+
return env.NODE9_TESTING !== "1";
|
|
26518
|
+
}
|
|
26519
|
+
var FLAG_MAX_BYTES = 32;
|
|
26520
|
+
function claimSessionNotice(sessionId, home = import_os26.default.homedir(), level = "standard") {
|
|
26521
|
+
if (!sessionId || !SAFE_SESSION_ID.test(sessionId)) return null;
|
|
26522
|
+
const dir2 = import_path36.default.join(home, ".node9", "notice-sessions");
|
|
26523
|
+
const seen = ensureOwnDir(dir2);
|
|
26524
|
+
if (!seen) return null;
|
|
26525
|
+
const flag = import_path36.default.join(dir2, sessionId + SESSION_FLAG_SUFFIX);
|
|
26526
|
+
let created = false;
|
|
26527
|
+
try {
|
|
26528
|
+
if (!isSameOwnDir(dir2, seen)) return null;
|
|
26529
|
+
const fd = import_fs34.default.openSync(flag, "wx", 384);
|
|
26530
|
+
created = true;
|
|
26531
|
+
try {
|
|
26532
|
+
import_fs34.default.writeSync(fd, level);
|
|
26533
|
+
} finally {
|
|
26534
|
+
import_fs34.default.closeSync(fd);
|
|
26535
|
+
}
|
|
26536
|
+
} catch (err) {
|
|
26537
|
+
if (created) {
|
|
26538
|
+
try {
|
|
26539
|
+
import_fs34.default.unlinkSync(flag);
|
|
26540
|
+
} catch {
|
|
26541
|
+
}
|
|
26542
|
+
return null;
|
|
26543
|
+
}
|
|
26544
|
+
if (err.code !== "EEXIST") return null;
|
|
26545
|
+
return levelChange(dir2, seen, flag, level);
|
|
26546
|
+
}
|
|
26547
|
+
removeOldFlags(dir2, seen);
|
|
26548
|
+
return "first";
|
|
26549
|
+
}
|
|
26550
|
+
function levelChange(dir2, seen, flag, level) {
|
|
26551
|
+
try {
|
|
26552
|
+
const told = readFlag(flag);
|
|
26553
|
+
if (told === null) return null;
|
|
26554
|
+
if (told === level) return null;
|
|
26555
|
+
if (!isSameOwnDir(dir2, seen)) return null;
|
|
26556
|
+
atomicWriteSync(flag, level, { mode: 384 });
|
|
26557
|
+
return told === "" ? null : "changed";
|
|
26558
|
+
} catch {
|
|
26559
|
+
return null;
|
|
26560
|
+
}
|
|
26561
|
+
}
|
|
26562
|
+
function readFlag(flag) {
|
|
26563
|
+
const c = import_fs34.default.constants;
|
|
26564
|
+
const fd = import_fs34.default.openSync(flag, c.O_RDONLY | (c.O_NOFOLLOW ?? 0) | (c.O_NONBLOCK ?? 0));
|
|
26565
|
+
try {
|
|
26566
|
+
const st = import_fs34.default.fstatSync(fd);
|
|
26567
|
+
if (!st.isFile() || st.nlink !== 1 || st.size > FLAG_MAX_BYTES) return null;
|
|
26568
|
+
const buf = Buffer.alloc(FLAG_MAX_BYTES);
|
|
26569
|
+
const n = import_fs34.default.readSync(fd, buf, 0, FLAG_MAX_BYTES, 0);
|
|
26570
|
+
return buf.toString("utf8", 0, n).trim();
|
|
26571
|
+
} finally {
|
|
26572
|
+
import_fs34.default.closeSync(fd);
|
|
26573
|
+
}
|
|
26574
|
+
}
|
|
26575
|
+
function removeOldFlags(dir2, seen) {
|
|
26576
|
+
const cutoff = Date.now() - FLAG_TTL_MS;
|
|
26577
|
+
try {
|
|
26578
|
+
for (const name of import_fs34.default.readdirSync(dir2)) removeOldFlag(dir2, seen, name, cutoff);
|
|
26579
|
+
} catch {
|
|
26580
|
+
}
|
|
26581
|
+
}
|
|
26582
|
+
|
|
26311
26583
|
// src/cli/commands/check.ts
|
|
26312
26584
|
function detectAiAgent(payload) {
|
|
26313
26585
|
const meta = payload.meta;
|
|
@@ -26386,9 +26658,9 @@ function registerCheckCommand(program2) {
|
|
|
26386
26658
|
} catch (err) {
|
|
26387
26659
|
const tempConfig = getConfig();
|
|
26388
26660
|
if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
|
|
26389
|
-
const logPath =
|
|
26661
|
+
const logPath = import_path37.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
|
|
26390
26662
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
26391
|
-
|
|
26663
|
+
import_fs35.default.appendFileSync(
|
|
26392
26664
|
logPath,
|
|
26393
26665
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
|
|
26394
26666
|
RAW: ${raw}
|
|
@@ -26401,14 +26673,14 @@ RAW: ${raw}
|
|
|
26401
26673
|
const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
|
|
26402
26674
|
if (process.env.NODE9_DEBUG === "1") {
|
|
26403
26675
|
try {
|
|
26404
|
-
const logPath =
|
|
26405
|
-
if (!
|
|
26406
|
-
|
|
26676
|
+
const logPath = import_path37.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
|
|
26677
|
+
if (!import_fs35.default.existsSync(import_path37.default.dirname(logPath)))
|
|
26678
|
+
import_fs35.default.mkdirSync(import_path37.default.dirname(logPath), { recursive: true });
|
|
26407
26679
|
const sanitized = JSON.stringify({
|
|
26408
26680
|
...payload,
|
|
26409
26681
|
prompt: `<redacted, ${prompt.length} bytes>`
|
|
26410
26682
|
});
|
|
26411
|
-
|
|
26683
|
+
import_fs35.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${sanitized}
|
|
26412
26684
|
`);
|
|
26413
26685
|
} catch {
|
|
26414
26686
|
}
|
|
@@ -26426,7 +26698,7 @@ RAW: ${raw}
|
|
|
26426
26698
|
if (!dlpMatch && !canaryHit) {
|
|
26427
26699
|
try {
|
|
26428
26700
|
const sid = typeof payload.session_id === "string" ? payload.session_id : typeof payload.conversationId === "string" ? payload.conversationId : "";
|
|
26429
|
-
const promptCwd = typeof payload.cwd === "string" &&
|
|
26701
|
+
const promptCwd = typeof payload.cwd === "string" && import_path37.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
26430
26702
|
if (sid && getConfig(promptCwd).policy.untrustedContent?.enabled) {
|
|
26431
26703
|
await notifyUserMentions(sid, extractContentDestinations(prompt));
|
|
26432
26704
|
}
|
|
@@ -26467,8 +26739,8 @@ RAW: ${raw}
|
|
|
26467
26739
|
);
|
|
26468
26740
|
const reason = `\u{1F6A8} Node9 DLP: ${shownPattern} detected in prompt (${shownSample}). Prompt was not submitted \u2014 remove the credential and try again.`;
|
|
26469
26741
|
try {
|
|
26470
|
-
const ttyFd =
|
|
26471
|
-
|
|
26742
|
+
const ttyFd = import_fs35.default.openSync("/dev/tty", "w");
|
|
26743
|
+
import_fs35.default.writeSync(
|
|
26472
26744
|
ttyFd,
|
|
26473
26745
|
import_chalk2.default.bgRed.white.bold(`
|
|
26474
26746
|
\u{1F6A8} NODE9 DLP \u2014 PROMPT BLOCKED
|
|
@@ -26478,7 +26750,7 @@ RAW: ${raw}
|
|
|
26478
26750
|
|
|
26479
26751
|
`)
|
|
26480
26752
|
);
|
|
26481
|
-
|
|
26753
|
+
import_fs35.default.closeSync(ttyFd);
|
|
26482
26754
|
} catch {
|
|
26483
26755
|
}
|
|
26484
26756
|
const isCodex = agent2 === "Codex";
|
|
@@ -26497,17 +26769,17 @@ RAW: ${raw}
|
|
|
26497
26769
|
process.exit(2);
|
|
26498
26770
|
}
|
|
26499
26771
|
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" &&
|
|
26772
|
+
const safeCwdForConfig = typeof payloadCwd === "string" && import_path37.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
26501
26773
|
const config = getConfig(safeCwdForConfig);
|
|
26502
26774
|
const daemonDown = !isDaemonRunning();
|
|
26503
26775
|
if (config.settings.autoStartDaemon && daemonDown && !process.env.NODE9_NO_AUTO_DAEMON) {
|
|
26504
26776
|
try {
|
|
26505
26777
|
const scriptPath = process.argv[1];
|
|
26506
|
-
if (typeof scriptPath !== "string" || !
|
|
26778
|
+
if (typeof scriptPath !== "string" || !import_path37.default.isAbsolute(scriptPath))
|
|
26507
26779
|
throw new Error("node9: argv[1] is not an absolute path");
|
|
26508
|
-
const resolvedScript =
|
|
26509
|
-
const packageDist =
|
|
26510
|
-
if (!resolvedScript.startsWith(packageDist +
|
|
26780
|
+
const resolvedScript = import_fs35.default.realpathSync(scriptPath);
|
|
26781
|
+
const packageDist = import_fs35.default.realpathSync(import_path37.default.resolve(__dirname, "../.."));
|
|
26782
|
+
if (!resolvedScript.startsWith(packageDist + import_path37.default.sep) && resolvedScript !== packageDist)
|
|
26511
26783
|
throw new Error(
|
|
26512
26784
|
`node9: daemon spawn aborted \u2014 argv[1] (${resolvedScript}) is outside package dist (${packageDist})`
|
|
26513
26785
|
);
|
|
@@ -26537,17 +26809,17 @@ RAW: ${raw}
|
|
|
26537
26809
|
} finally {
|
|
26538
26810
|
if (startupFd !== void 0) {
|
|
26539
26811
|
try {
|
|
26540
|
-
|
|
26812
|
+
import_fs35.default.closeSync(startupFd);
|
|
26541
26813
|
} catch {
|
|
26542
26814
|
}
|
|
26543
26815
|
}
|
|
26544
26816
|
}
|
|
26545
26817
|
} catch (spawnErr) {
|
|
26546
|
-
const logPath =
|
|
26818
|
+
const logPath = import_path37.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
|
|
26547
26819
|
const msg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);
|
|
26548
26820
|
recordStartupState("failed", "spawn-aborted", msg);
|
|
26549
26821
|
try {
|
|
26550
|
-
|
|
26822
|
+
import_fs35.default.appendFileSync(
|
|
26551
26823
|
logPath,
|
|
26552
26824
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-autostart-failed: ${msg}
|
|
26553
26825
|
`
|
|
@@ -26561,10 +26833,10 @@ RAW: ${raw}
|
|
|
26561
26833
|
);
|
|
26562
26834
|
}
|
|
26563
26835
|
if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
|
|
26564
|
-
const logPath =
|
|
26565
|
-
if (!
|
|
26566
|
-
|
|
26567
|
-
|
|
26836
|
+
const logPath = import_path37.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
|
|
26837
|
+
if (!import_fs35.default.existsSync(import_path37.default.dirname(logPath)))
|
|
26838
|
+
import_fs35.default.mkdirSync(import_path37.default.dirname(logPath), { recursive: true });
|
|
26839
|
+
import_fs35.default.appendFileSync(
|
|
26568
26840
|
logPath,
|
|
26569
26841
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${JSON.stringify(raw)}
|
|
26570
26842
|
`
|
|
@@ -26585,8 +26857,8 @@ RAW: ${raw}
|
|
|
26585
26857
|
);
|
|
26586
26858
|
let ttyFd = null;
|
|
26587
26859
|
try {
|
|
26588
|
-
ttyFd =
|
|
26589
|
-
const writeTty = (line) =>
|
|
26860
|
+
ttyFd = import_fs35.default.openSync("/dev/tty", "w");
|
|
26861
|
+
const writeTty = (line) => import_fs35.default.writeSync(ttyFd, line + "\n");
|
|
26590
26862
|
if (result2?.blockKind === "protected-path") {
|
|
26591
26863
|
writeTty(
|
|
26592
26864
|
import_chalk2.default.red.bold(
|
|
@@ -26613,7 +26885,7 @@ RAW: ${raw}
|
|
|
26613
26885
|
} finally {
|
|
26614
26886
|
if (ttyFd !== null)
|
|
26615
26887
|
try {
|
|
26616
|
-
|
|
26888
|
+
import_fs35.default.closeSync(ttyFd);
|
|
26617
26889
|
} catch {
|
|
26618
26890
|
}
|
|
26619
26891
|
}
|
|
@@ -26622,7 +26894,12 @@ RAW: ${raw}
|
|
|
26622
26894
|
humanDecision,
|
|
26623
26895
|
msg,
|
|
26624
26896
|
result2?.recoveryCommand,
|
|
26625
|
-
{
|
|
26897
|
+
{
|
|
26898
|
+
kind: result2?.blockKind,
|
|
26899
|
+
path: result2?.blockedPath,
|
|
26900
|
+
redirect: !!result2?.redirect,
|
|
26901
|
+
ruleDescription: result2?.ruleDescription
|
|
26902
|
+
}
|
|
26626
26903
|
);
|
|
26627
26904
|
if (agent === "Antigravity") {
|
|
26628
26905
|
process.stdout.write(
|
|
@@ -26639,11 +26916,13 @@ RAW: ${raw}
|
|
|
26639
26916
|
);
|
|
26640
26917
|
process.exit(0);
|
|
26641
26918
|
}
|
|
26919
|
+
const userLine = userLineOf(aiFeedbackMessage);
|
|
26642
26920
|
process.stdout.write(
|
|
26643
26921
|
JSON.stringify({
|
|
26644
26922
|
decision: "block",
|
|
26645
26923
|
reason: aiFeedbackMessage,
|
|
26646
26924
|
// This is the core instruction
|
|
26925
|
+
...agent === "Claude Code" && userLine ? { systemMessage: userLine } : {},
|
|
26647
26926
|
hookSpecificOutput: {
|
|
26648
26927
|
hookEventName: "PreToolUse",
|
|
26649
26928
|
permissionDecision: "deny",
|
|
@@ -26677,8 +26956,8 @@ RAW: ${raw}
|
|
|
26677
26956
|
} catch {
|
|
26678
26957
|
}
|
|
26679
26958
|
try {
|
|
26680
|
-
const ttyFd =
|
|
26681
|
-
|
|
26959
|
+
const ttyFd = import_fs35.default.openSync("/dev/tty", "w");
|
|
26960
|
+
import_fs35.default.writeSync(
|
|
26682
26961
|
ttyFd,
|
|
26683
26962
|
import_chalk2.default.yellow(
|
|
26684
26963
|
`
|
|
@@ -26686,7 +26965,7 @@ RAW: ${raw}
|
|
|
26686
26965
|
`
|
|
26687
26966
|
)
|
|
26688
26967
|
);
|
|
26689
|
-
|
|
26968
|
+
import_fs35.default.closeSync(ttyFd);
|
|
26690
26969
|
} catch {
|
|
26691
26970
|
}
|
|
26692
26971
|
if (agent === "GitHub Copilot") {
|
|
@@ -26718,17 +26997,20 @@ RAW: ${raw}
|
|
|
26718
26997
|
const safeSessionId = /^[A-Za-z0-9_\-]{1,128}$/.test(rawSessionId) ? rawSessionId : "";
|
|
26719
26998
|
if (skillPinCfg.enabled && safeSessionId) {
|
|
26720
26999
|
try {
|
|
26721
|
-
const sessionsDir =
|
|
26722
|
-
const flagPath =
|
|
27000
|
+
const sessionsDir = import_path37.default.join(import_os27.default.homedir(), ".node9", "skill-sessions");
|
|
27001
|
+
const flagPath = import_path37.default.join(sessionsDir, `${safeSessionId}${SESSION_FLAG_SUFFIX}`);
|
|
27002
|
+
const memo = ensureOwnDir(sessionsDir);
|
|
26723
27003
|
let flag = null;
|
|
26724
27004
|
try {
|
|
26725
|
-
|
|
27005
|
+
const st = memo ? import_fs35.default.lstatSync(flagPath) : null;
|
|
27006
|
+
if (st && st.isFile() && st.nlink === 1)
|
|
27007
|
+
flag = JSON.parse(import_fs35.default.readFileSync(flagPath, "utf-8"));
|
|
26726
27008
|
} catch {
|
|
26727
27009
|
}
|
|
26728
27010
|
const writeFlag = (data2) => {
|
|
27011
|
+
if (!memo || !isSameOwnDir(sessionsDir, memo)) return;
|
|
26729
27012
|
try {
|
|
26730
|
-
|
|
26731
|
-
import_fs33.default.writeFileSync(
|
|
27013
|
+
atomicWriteSync(
|
|
26732
27014
|
flagPath,
|
|
26733
27015
|
JSON.stringify({ ...data2, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
|
|
26734
27016
|
{ mode: 384 }
|
|
@@ -26739,8 +27021,8 @@ RAW: ${raw}
|
|
|
26739
27021
|
const sendSkillWarn = (detail, recoveryCmd) => {
|
|
26740
27022
|
let ttyFd = null;
|
|
26741
27023
|
try {
|
|
26742
|
-
ttyFd =
|
|
26743
|
-
const w = (line) =>
|
|
27024
|
+
ttyFd = import_fs35.default.openSync("/dev/tty", "w");
|
|
27025
|
+
const w = (line) => import_fs35.default.writeSync(ttyFd, line + "\n");
|
|
26744
27026
|
w(import_chalk2.default.yellow(`
|
|
26745
27027
|
\u26A0\uFE0F Node9: installed skill drift detected`));
|
|
26746
27028
|
w(import_chalk2.default.gray(` ${detail}`));
|
|
@@ -26755,7 +27037,7 @@ RAW: ${raw}
|
|
|
26755
27037
|
} finally {
|
|
26756
27038
|
if (ttyFd !== null)
|
|
26757
27039
|
try {
|
|
26758
|
-
|
|
27040
|
+
import_fs35.default.closeSync(ttyFd);
|
|
26759
27041
|
} catch {
|
|
26760
27042
|
}
|
|
26761
27043
|
}
|
|
@@ -26771,7 +27053,7 @@ RAW: ${raw}
|
|
|
26771
27053
|
return;
|
|
26772
27054
|
}
|
|
26773
27055
|
if (!flag || flag.state !== "verified" && flag.state !== "warned") {
|
|
26774
|
-
const absoluteCwd = typeof payloadCwd === "string" &&
|
|
27056
|
+
const absoluteCwd = typeof payloadCwd === "string" && import_path37.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
26775
27057
|
const extraRoots = skillPinCfg.roots;
|
|
26776
27058
|
const resolvedExtra = extraRoots.map((r) => resolveUserSkillRoot(r, absoluteCwd)).filter((r) => typeof r === "string");
|
|
26777
27059
|
const roots = [...defaultSkillRoots(absoluteCwd), ...resolvedExtra];
|
|
@@ -26810,31 +27092,27 @@ RAW: ${raw}
|
|
|
26810
27092
|
} else {
|
|
26811
27093
|
writeFlag({ state: "verified" });
|
|
26812
27094
|
}
|
|
26813
|
-
|
|
26814
|
-
|
|
26815
|
-
|
|
26816
|
-
const
|
|
26817
|
-
|
|
26818
|
-
|
|
26819
|
-
} catch {
|
|
26820
|
-
}
|
|
27095
|
+
if (memo)
|
|
27096
|
+
try {
|
|
27097
|
+
const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1e3;
|
|
27098
|
+
for (const name of import_fs35.default.readdirSync(sessionsDir))
|
|
27099
|
+
removeOldFlag(sessionsDir, memo, name, cutoff);
|
|
27100
|
+
} catch {
|
|
26821
27101
|
}
|
|
26822
|
-
} catch {
|
|
26823
|
-
}
|
|
26824
27102
|
}
|
|
26825
27103
|
} catch (err) {
|
|
26826
27104
|
if (process.env.NODE9_DEBUG === "1") {
|
|
26827
27105
|
try {
|
|
26828
|
-
const dbg =
|
|
27106
|
+
const dbg = import_path37.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
|
|
26829
27107
|
const msg = err instanceof Error ? err.message : String(err);
|
|
26830
|
-
|
|
27108
|
+
import_fs35.default.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
|
|
26831
27109
|
`);
|
|
26832
27110
|
} catch {
|
|
26833
27111
|
}
|
|
26834
27112
|
}
|
|
26835
27113
|
}
|
|
26836
27114
|
}
|
|
26837
|
-
const safeCwdForAuth = typeof payloadCwd === "string" &&
|
|
27115
|
+
const safeCwdForAuth = typeof payloadCwd === "string" && import_path37.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
26838
27116
|
const askMode = resolveAskMode(agent, opts, config);
|
|
26839
27117
|
const result = await authorizeHeadless(toolName, toolInput, meta, {
|
|
26840
27118
|
cwd: safeCwdForAuth,
|
|
@@ -26847,11 +27125,22 @@ RAW: ${raw}
|
|
|
26847
27125
|
} catch {
|
|
26848
27126
|
}
|
|
26849
27127
|
}
|
|
27128
|
+
const isSubagentCall = typeof payload.agent_id === "string" && payload.agent_id !== "";
|
|
27129
|
+
const allowExit = (checkedBy) => {
|
|
27130
|
+
const notice = checkedBy !== "paused" && agent === "Claude Code" && !isSubagentCall && sessionNoticeEnabled() ? claimSessionNotice(safeSessionId, import_os27.default.homedir(), levelOf(config.settings.mode)) : null;
|
|
27131
|
+
if (notice) {
|
|
27132
|
+
try {
|
|
27133
|
+
import_fs35.default.writeSync(1, sessionNoticeOutput(config.settings.mode, notice) + "\n");
|
|
27134
|
+
} catch {
|
|
27135
|
+
}
|
|
27136
|
+
}
|
|
27137
|
+
process.exit(0);
|
|
27138
|
+
};
|
|
26850
27139
|
if (result.approved) {
|
|
26851
27140
|
if (result.checkedBy && process.env.NODE9_DEBUG === "1")
|
|
26852
27141
|
process.stderr.write(`\u2713 node9 [${result.checkedBy}]: "${toolName}" allowed
|
|
26853
27142
|
`);
|
|
26854
|
-
|
|
27143
|
+
allowExit(result.checkedBy);
|
|
26855
27144
|
}
|
|
26856
27145
|
if (result.review) {
|
|
26857
27146
|
sendAsk(result);
|
|
@@ -26859,12 +27148,12 @@ RAW: ${raw}
|
|
|
26859
27148
|
}
|
|
26860
27149
|
if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && !process.stdout.isTTY && config.settings.autoStartDaemon) {
|
|
26861
27150
|
try {
|
|
26862
|
-
const tty =
|
|
26863
|
-
|
|
27151
|
+
const tty = import_fs35.default.openSync("/dev/tty", "w");
|
|
27152
|
+
import_fs35.default.writeSync(
|
|
26864
27153
|
tty,
|
|
26865
27154
|
import_chalk2.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically...\n")
|
|
26866
27155
|
);
|
|
26867
|
-
|
|
27156
|
+
import_fs35.default.closeSync(tty);
|
|
26868
27157
|
} catch {
|
|
26869
27158
|
}
|
|
26870
27159
|
const daemonReady = await autoStartDaemonAndWait();
|
|
@@ -26876,7 +27165,7 @@ RAW: ${raw}
|
|
|
26876
27165
|
if (retry.checkedBy && process.env.NODE9_DEBUG === "1")
|
|
26877
27166
|
process.stderr.write(`\u2713 node9 [${retry.checkedBy}]: "${toolName}" allowed
|
|
26878
27167
|
`);
|
|
26879
|
-
|
|
27168
|
+
allowExit(retry.checkedBy);
|
|
26880
27169
|
}
|
|
26881
27170
|
sendBlock(retry.reason ?? `Node9 blocked "${toolName}".`, {
|
|
26882
27171
|
...retry,
|
|
@@ -26891,9 +27180,9 @@ RAW: ${raw}
|
|
|
26891
27180
|
});
|
|
26892
27181
|
} catch (err) {
|
|
26893
27182
|
if (process.env.NODE9_DEBUG === "1") {
|
|
26894
|
-
const logPath =
|
|
27183
|
+
const logPath = import_path37.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
|
|
26895
27184
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
26896
|
-
|
|
27185
|
+
import_fs35.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
|
|
26897
27186
|
`);
|
|
26898
27187
|
}
|
|
26899
27188
|
process.exit(0);
|
|
@@ -26928,9 +27217,9 @@ RAW: ${raw}
|
|
|
26928
27217
|
|
|
26929
27218
|
// src/cli/commands/log.ts
|
|
26930
27219
|
init_cjs_shims();
|
|
26931
|
-
var
|
|
26932
|
-
var
|
|
26933
|
-
var
|
|
27220
|
+
var import_fs36 = __toESM(require("fs"));
|
|
27221
|
+
var import_path38 = __toESM(require("path"));
|
|
27222
|
+
var import_os28 = __toESM(require("os"));
|
|
26934
27223
|
init_audit();
|
|
26935
27224
|
init_dist();
|
|
26936
27225
|
|
|
@@ -27198,12 +27487,12 @@ function registerLogCommand(program2) {
|
|
|
27198
27487
|
if (rawToolName !== tool) entry.agentToolName = rawToolName;
|
|
27199
27488
|
const payloadSessionId = payload.session_id ?? payload.conversationId;
|
|
27200
27489
|
if (payloadSessionId) entry.sessionId = payloadSessionId;
|
|
27201
|
-
const logPath =
|
|
27202
|
-
if (!
|
|
27203
|
-
|
|
27204
|
-
|
|
27490
|
+
const logPath = import_path38.default.join(import_os28.default.homedir(), ".node9", "audit.log");
|
|
27491
|
+
if (!import_fs36.default.existsSync(import_path38.default.dirname(logPath)))
|
|
27492
|
+
import_fs36.default.mkdirSync(import_path38.default.dirname(logPath), { recursive: true });
|
|
27493
|
+
import_fs36.default.appendFileSync(logPath, JSON.stringify(entry) + "\n");
|
|
27205
27494
|
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" &&
|
|
27495
|
+
const safeCwd = typeof payloadCwd === "string" && import_path38.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
27207
27496
|
if ((tool === "Bash" || tool === "bash") && isDaemonRunning()) {
|
|
27208
27497
|
const command = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
|
|
27209
27498
|
if (command) {
|
|
@@ -27424,9 +27713,9 @@ function registerLogCommand(program2) {
|
|
|
27424
27713
|
const msg = err instanceof Error ? err.message : String(err);
|
|
27425
27714
|
process.stderr.write(`[Node9] audit log error: ${msg}
|
|
27426
27715
|
`);
|
|
27427
|
-
const debugPath =
|
|
27716
|
+
const debugPath = import_path38.default.join(import_os28.default.homedir(), ".node9", "hook-debug.log");
|
|
27428
27717
|
try {
|
|
27429
|
-
|
|
27718
|
+
import_fs36.default.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
|
|
27430
27719
|
`);
|
|
27431
27720
|
} catch {
|
|
27432
27721
|
}
|
|
@@ -27449,9 +27738,9 @@ function registerLogCommand(program2) {
|
|
|
27449
27738
|
});
|
|
27450
27739
|
}
|
|
27451
27740
|
function absolutePathFor(spelled, cwd) {
|
|
27452
|
-
const expanded = spelled === "~" || spelled.startsWith("~/") ?
|
|
27453
|
-
if (
|
|
27454
|
-
return cwd ?
|
|
27741
|
+
const expanded = spelled === "~" || spelled.startsWith("~/") ? import_path38.default.join(import_os28.default.homedir(), spelled.slice(1)) : spelled;
|
|
27742
|
+
if (import_path38.default.isAbsolute(expanded)) return expanded;
|
|
27743
|
+
return cwd ? import_path38.default.resolve(cwd, expanded) : import_path38.default.resolve(expanded);
|
|
27455
27744
|
}
|
|
27456
27745
|
async function carryTaintAcrossWrites(command, cwd) {
|
|
27457
27746
|
let pairs;
|
|
@@ -27470,13 +27759,13 @@ async function carryTaintAcrossWrites(command, cwd) {
|
|
|
27470
27759
|
continue;
|
|
27471
27760
|
}
|
|
27472
27761
|
const sensitive = scanFilePath(src, cwd);
|
|
27473
|
-
if (sensitive) await notifyTaint(dst, `copy:${sensitive.patternName}:${
|
|
27762
|
+
if (sensitive) await notifyTaint(dst, `copy:${sensitive.patternName}:${import_path38.default.basename(src)}`);
|
|
27474
27763
|
}
|
|
27475
27764
|
}
|
|
27476
27765
|
|
|
27477
27766
|
// src/hook-entry.ts
|
|
27478
27767
|
var { version } = JSON.parse(
|
|
27479
|
-
|
|
27768
|
+
import_fs37.default.readFileSync(import_path39.default.join(__dirname, "../package.json"), "utf-8")
|
|
27480
27769
|
);
|
|
27481
27770
|
var program = new import_commander.Command();
|
|
27482
27771
|
program.name("node9").description("The Sudo Command for AI Agents").version(version);
|
|
@@ -27485,9 +27774,9 @@ registerLogCommand(program);
|
|
|
27485
27774
|
process.on("unhandledRejection", (reason) => {
|
|
27486
27775
|
if (process.argv[2] === "check") {
|
|
27487
27776
|
if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
|
|
27488
|
-
const logPath =
|
|
27777
|
+
const logPath = import_path39.default.join(import_os29.default.homedir(), ".node9", "hook-debug.log");
|
|
27489
27778
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
27490
|
-
|
|
27779
|
+
import_fs37.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
|
|
27491
27780
|
`);
|
|
27492
27781
|
}
|
|
27493
27782
|
process.exit(0);
|