@node9/proxy 1.12.11 → 1.13.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/README.md +22 -19
- package/dist/cli.js +543 -208
- package/dist/cli.mjs +541 -206
- package/dist/index.js +15 -3
- package/dist/index.mjs +15 -3
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -168,8 +168,8 @@ function sanitizeConfig(raw) {
|
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
const lines = result.error.issues.map((issue) => {
|
|
171
|
-
const
|
|
172
|
-
return ` \u2022 ${
|
|
171
|
+
const path46 = issue.path.length > 0 ? issue.path.join(".") : "root";
|
|
172
|
+
return ` \u2022 ${path46}: ${issue.message}`;
|
|
173
173
|
});
|
|
174
174
|
return {
|
|
175
175
|
sanitized,
|
|
@@ -1220,7 +1220,26 @@ function scanText(text) {
|
|
|
1220
1220
|
}
|
|
1221
1221
|
return null;
|
|
1222
1222
|
}
|
|
1223
|
-
|
|
1223
|
+
function redactText(text) {
|
|
1224
|
+
const t = text.length > MAX_STRING_BYTES ? text.slice(0, MAX_STRING_BYTES) : text;
|
|
1225
|
+
let result = t;
|
|
1226
|
+
const found = [];
|
|
1227
|
+
const lower = t.toLowerCase();
|
|
1228
|
+
for (const { pattern, globalRegex } of DLP_PATTERNS_GLOBAL) {
|
|
1229
|
+
if (pattern.keywords && !pattern.keywords.some((kw) => lower.includes(kw.toLowerCase()))) {
|
|
1230
|
+
continue;
|
|
1231
|
+
}
|
|
1232
|
+
result = result.replace(globalRegex, (match) => {
|
|
1233
|
+
if (DLP_STOPWORDS.some((sw) => match.toLowerCase().includes(sw))) return match;
|
|
1234
|
+
if (pattern.minEntropy !== void 0 && shannonEntropy(match) < pattern.minEntropy)
|
|
1235
|
+
return match;
|
|
1236
|
+
if (!found.includes(pattern.name)) found.push(pattern.name);
|
|
1237
|
+
return `[node9-redacted:${pattern.name}]`;
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
1240
|
+
return { result, found };
|
|
1241
|
+
}
|
|
1242
|
+
var import_fs4, import_path4, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES;
|
|
1224
1243
|
var init_dlp = __esm({
|
|
1225
1244
|
"src/dlp.ts"() {
|
|
1226
1245
|
"use strict";
|
|
@@ -1266,7 +1285,8 @@ var init_dlp = __esm({
|
|
|
1266
1285
|
name: "GitHub Token",
|
|
1267
1286
|
regex: /\bgh[pous]_[A-Za-z0-9]{36}\b/,
|
|
1268
1287
|
severity: "block",
|
|
1269
|
-
keywords: ["ghp_", "gho_", "ghu_", "ghs_"]
|
|
1288
|
+
keywords: ["ghp_", "gho_", "ghu_", "ghs_"],
|
|
1289
|
+
minEntropy: 3
|
|
1270
1290
|
},
|
|
1271
1291
|
{
|
|
1272
1292
|
name: "GitHub Fine-Grained PAT",
|
|
@@ -1317,7 +1337,8 @@ var init_dlp = __esm({
|
|
|
1317
1337
|
name: "GCP API Key",
|
|
1318
1338
|
regex: /\bAIza[0-9A-Za-z_-]{35}\b/,
|
|
1319
1339
|
severity: "block",
|
|
1320
|
-
keywords: ["aiza"]
|
|
1340
|
+
keywords: ["aiza"],
|
|
1341
|
+
minEntropy: 3
|
|
1321
1342
|
},
|
|
1322
1343
|
{
|
|
1323
1344
|
name: "GCP Service Account",
|
|
@@ -1564,7 +1585,8 @@ var init_dlp = __esm({
|
|
|
1564
1585
|
name: "Mapbox Access Token",
|
|
1565
1586
|
regex: /\bpk\.eyJ1[a-zA-Z0-9._-]{20,}\b/,
|
|
1566
1587
|
severity: "block",
|
|
1567
|
-
keywords: ["pk.eyj1"]
|
|
1588
|
+
keywords: ["pk.eyj1"],
|
|
1589
|
+
minEntropy: 3
|
|
1568
1590
|
},
|
|
1569
1591
|
// ── Notion ────────────────────────────────────────────────────────────────
|
|
1570
1592
|
{
|
|
@@ -1637,6 +1659,15 @@ var init_dlp = __esm({
|
|
|
1637
1659
|
keywords: ["age-secret-key-"]
|
|
1638
1660
|
}
|
|
1639
1661
|
];
|
|
1662
|
+
DLP_PATTERNS_GLOBAL = DLP_PATTERNS.map(
|
|
1663
|
+
(p) => ({
|
|
1664
|
+
pattern: p,
|
|
1665
|
+
globalRegex: new RegExp(
|
|
1666
|
+
p.regex.source,
|
|
1667
|
+
p.regex.flags.includes("g") ? p.regex.flags : p.regex.flags + "g"
|
|
1668
|
+
)
|
|
1669
|
+
})
|
|
1670
|
+
);
|
|
1640
1671
|
SENSITIVE_PATH_PATTERNS = [
|
|
1641
1672
|
/[/\\]\.ssh[/\\]/i,
|
|
1642
1673
|
/[/\\]\.aws[/\\]/i,
|
|
@@ -2198,9 +2229,9 @@ function matchesPattern(text, patterns) {
|
|
|
2198
2229
|
const withoutDotSlash = text.replace(/^\.\//, "");
|
|
2199
2230
|
return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
|
|
2200
2231
|
}
|
|
2201
|
-
function getNestedValue(obj,
|
|
2232
|
+
function getNestedValue(obj, path46) {
|
|
2202
2233
|
if (!obj || typeof obj !== "object") return null;
|
|
2203
|
-
return
|
|
2234
|
+
return path46.split(".").reduce((prev, curr) => prev?.[curr], obj);
|
|
2204
2235
|
}
|
|
2205
2236
|
function normalizeCommandForPolicy(command) {
|
|
2206
2237
|
try {
|
|
@@ -9173,14 +9204,30 @@ var init_ui = __esm({
|
|
|
9173
9204
|
// \u2500\u2500 Leaks (shown first: credential exposure is highest-severity) \u2500\u2500\u2500\u2500\u2500
|
|
9174
9205
|
if (leaksByPattern.length) {
|
|
9175
9206
|
html +=
|
|
9176
|
-
'<div class="scan-rule-section-label" style="color:#e5534b">\u{1F511} Credential Leaks \u2014 secrets found in
|
|
9207
|
+
'<div class="scan-rule-section-label" style="color:#e5534b">\u{1F511} Credential Leaks \u2014 secrets found in history or shell config</div>';
|
|
9177
9208
|
html += leaksByPattern
|
|
9178
9209
|
.map(([pattern, group]) => {
|
|
9179
9210
|
const count = group.length;
|
|
9180
9211
|
const barPct = Math.round((count / maxBar) * 100);
|
|
9181
9212
|
const detailId = 'detail-' + Math.random().toString(36).slice(2);
|
|
9182
9213
|
const rows = group
|
|
9183
|
-
.map((l) =>
|
|
9214
|
+
.map((l) => {
|
|
9215
|
+
const agentBadge =
|
|
9216
|
+
l.agent === 'gemini'
|
|
9217
|
+
? '[Gemini]'
|
|
9218
|
+
: l.agent === 'codex'
|
|
9219
|
+
? '[Codex]'
|
|
9220
|
+
: l.agent === 'shell'
|
|
9221
|
+
? '[Shell]'
|
|
9222
|
+
: '[Claude]';
|
|
9223
|
+
return findingRow(
|
|
9224
|
+
l.timestamp,
|
|
9225
|
+
'<span style="opacity:0.6;margin-right:6px">' +
|
|
9226
|
+
esc(agentBadge) +
|
|
9227
|
+
'</span>' +
|
|
9228
|
+
esc(l.redactedSample || '')
|
|
9229
|
+
);
|
|
9230
|
+
})
|
|
9184
9231
|
.join('');
|
|
9185
9232
|
return (
|
|
9186
9233
|
'<div class="scan-rule-row" onclick="var d=document.getElementById(\\'' +
|
|
@@ -9929,6 +9976,7 @@ function scanClaudeHistory(startDate, onProgress, onLine) {
|
|
|
9929
9976
|
continue;
|
|
9930
9977
|
}
|
|
9931
9978
|
const sessionCalls = [];
|
|
9979
|
+
const toolUseFilePaths = /* @__PURE__ */ new Map();
|
|
9932
9980
|
for (const line of raw.split("\n")) {
|
|
9933
9981
|
if (!line.trim()) continue;
|
|
9934
9982
|
onLine?.();
|
|
@@ -9971,6 +10019,33 @@ function scanClaudeHistory(startDate, onProgress, onLine) {
|
|
|
9971
10019
|
}
|
|
9972
10020
|
}
|
|
9973
10021
|
}
|
|
10022
|
+
for (const block of content2) {
|
|
10023
|
+
if (block.type !== "tool_result") continue;
|
|
10024
|
+
const filePath = block.tool_use_id ? toolUseFilePaths.get(block.tool_use_id) : void 0;
|
|
10025
|
+
if (filePath) {
|
|
10026
|
+
const ext = import_path17.default.extname(filePath).toLowerCase();
|
|
10027
|
+
if (CODE_EXTENSIONS.has(ext)) continue;
|
|
10028
|
+
}
|
|
10029
|
+
const resultText = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map((c) => c.text ?? "").join("\n") : null;
|
|
10030
|
+
if (!resultText) continue;
|
|
10031
|
+
const dlpMatch = scanArgs({ text: resultText });
|
|
10032
|
+
if (dlpMatch) {
|
|
10033
|
+
const isDupe = result.dlpFindings.some(
|
|
10034
|
+
(f) => f.patternName === dlpMatch.patternName && f.redactedSample === dlpMatch.redactedSample && f.project === projLabel
|
|
10035
|
+
);
|
|
10036
|
+
if (!isDupe) {
|
|
10037
|
+
result.dlpFindings.push({
|
|
10038
|
+
patternName: dlpMatch.patternName,
|
|
10039
|
+
redactedSample: dlpMatch.redactedSample,
|
|
10040
|
+
toolName: "tool-result",
|
|
10041
|
+
timestamp: entry.timestamp ?? "",
|
|
10042
|
+
project: projLabel,
|
|
10043
|
+
sessionId,
|
|
10044
|
+
agent: "claude"
|
|
10045
|
+
});
|
|
10046
|
+
}
|
|
10047
|
+
}
|
|
10048
|
+
}
|
|
9974
10049
|
}
|
|
9975
10050
|
continue;
|
|
9976
10051
|
}
|
|
@@ -9990,6 +10065,9 @@ function scanClaudeHistory(startDate, onProgress, onLine) {
|
|
|
9990
10065
|
const toolName = block.name ?? "";
|
|
9991
10066
|
const toolNameLower = toolName.toLowerCase();
|
|
9992
10067
|
const input = block.input ?? {};
|
|
10068
|
+
if (block.id && typeof input.file_path === "string") {
|
|
10069
|
+
toolUseFilePaths.set(block.id, input.file_path);
|
|
10070
|
+
}
|
|
9993
10071
|
sessionCalls.push({ toolName, input, timestamp: entry.timestamp ?? "" });
|
|
9994
10072
|
if (toolNameLower === "bash" || toolNameLower === "execute_bash") {
|
|
9995
10073
|
result.bashCalls++;
|
|
@@ -9997,6 +10075,9 @@ function scanClaudeHistory(startDate, onProgress, onLine) {
|
|
|
9997
10075
|
const rawCmd = String(input.command ?? "").trimStart();
|
|
9998
10076
|
if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd))
|
|
9999
10077
|
continue;
|
|
10078
|
+
const inputFilePath = typeof input.file_path === "string" ? input.file_path : "";
|
|
10079
|
+
const inputFileExt = inputFilePath ? import_path17.default.extname(inputFilePath).toLowerCase() : "";
|
|
10080
|
+
if (CODE_EXTENSIONS.has(inputFileExt)) continue;
|
|
10000
10081
|
const dlpMatch = scanArgs(input);
|
|
10001
10082
|
if (dlpMatch) {
|
|
10002
10083
|
const isDupe = result.dlpFindings.some(
|
|
@@ -10493,6 +10574,44 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
10493
10574
|
}
|
|
10494
10575
|
return result;
|
|
10495
10576
|
}
|
|
10577
|
+
function scanShellConfig() {
|
|
10578
|
+
const home = import_os12.default.homedir();
|
|
10579
|
+
const configFiles = [".zshrc", ".bashrc", ".bash_profile", ".profile"].map(
|
|
10580
|
+
(f) => import_path17.default.join(home, f)
|
|
10581
|
+
);
|
|
10582
|
+
const findings = [];
|
|
10583
|
+
for (const filePath of configFiles) {
|
|
10584
|
+
if (!import_fs14.default.existsSync(filePath)) continue;
|
|
10585
|
+
let lines;
|
|
10586
|
+
try {
|
|
10587
|
+
lines = import_fs14.default.readFileSync(filePath, "utf-8").split("\n");
|
|
10588
|
+
} catch {
|
|
10589
|
+
continue;
|
|
10590
|
+
}
|
|
10591
|
+
const shortPath = filePath.replace(home, "~");
|
|
10592
|
+
for (const line of lines) {
|
|
10593
|
+
const trimmed = line.trim();
|
|
10594
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
10595
|
+
const dlpMatch = scanArgs({ text: trimmed });
|
|
10596
|
+
if (!dlpMatch) continue;
|
|
10597
|
+
const isDupe = findings.some(
|
|
10598
|
+
(f) => f.patternName === dlpMatch.patternName && f.redactedSample === dlpMatch.redactedSample && f.project === shortPath
|
|
10599
|
+
);
|
|
10600
|
+
if (!isDupe) {
|
|
10601
|
+
findings.push({
|
|
10602
|
+
patternName: dlpMatch.patternName,
|
|
10603
|
+
redactedSample: dlpMatch.redactedSample,
|
|
10604
|
+
toolName: "shell-config",
|
|
10605
|
+
timestamp: "",
|
|
10606
|
+
project: shortPath,
|
|
10607
|
+
sessionId: "",
|
|
10608
|
+
agent: "shell"
|
|
10609
|
+
});
|
|
10610
|
+
}
|
|
10611
|
+
}
|
|
10612
|
+
}
|
|
10613
|
+
return findings;
|
|
10614
|
+
}
|
|
10496
10615
|
function mergeScans(a, b) {
|
|
10497
10616
|
const dates = [a.firstDate, b.firstDate].filter(Boolean);
|
|
10498
10617
|
const lastDates = [a.lastDate, b.lastDate].filter(Boolean);
|
|
@@ -10607,6 +10726,7 @@ function registerScanCommand(program2) {
|
|
|
10607
10726
|
onLine
|
|
10608
10727
|
);
|
|
10609
10728
|
const scan = mergeScans(mergeScans(claudeScan, geminiScan), codexScan);
|
|
10729
|
+
scan.dlpFindings.push(...scanShellConfig());
|
|
10610
10730
|
const summary = buildScanSummary([
|
|
10611
10731
|
{ id: "claude", label: "Claude", icon: "\u{1F916}", scan: claudeScan },
|
|
10612
10732
|
{ id: "gemini", label: "Gemini", icon: "\u264A", scan: geminiScan },
|
|
@@ -10660,7 +10780,7 @@ function registerScanCommand(program2) {
|
|
|
10660
10780
|
}
|
|
10661
10781
|
if (scan.dlpFindings.length > 0) {
|
|
10662
10782
|
console.log(
|
|
10663
|
-
" " + import_chalk2.default.red("\u{1F511} Credential leak") + " " + import_chalk2.default.red.bold(String(scan.dlpFindings.length).padStart(5)) + import_chalk2.default.dim(" secret detected in
|
|
10783
|
+
" " + import_chalk2.default.red("\u{1F511} Credential leak") + " " + import_chalk2.default.red.bold(String(scan.dlpFindings.length).padStart(5)) + import_chalk2.default.dim(" secret detected in history or shell config")
|
|
10664
10784
|
);
|
|
10665
10785
|
}
|
|
10666
10786
|
if (blockedCount > 0) {
|
|
@@ -10669,8 +10789,9 @@ function registerScanCommand(program2) {
|
|
|
10669
10789
|
);
|
|
10670
10790
|
}
|
|
10671
10791
|
if (scan.loopFindings.length > 0) {
|
|
10792
|
+
const loopCost = summary.loopWastedUSD > 0 ? import_chalk2.default.dim(" \xB7 ") + import_chalk2.default.yellow("~" + fmtCost(summary.loopWastedUSD) + " wasted") : "";
|
|
10672
10793
|
console.log(
|
|
10673
|
-
" " + import_chalk2.default.yellow("\u{1F501} Loop detected") + " " + import_chalk2.default.yellow.bold(String(scan.loopFindings.length).padStart(5)) + import_chalk2.default.dim(" repeated tool call patterns found")
|
|
10794
|
+
" " + import_chalk2.default.yellow("\u{1F501} Loop detected") + " " + import_chalk2.default.yellow.bold(String(scan.loopFindings.length).padStart(5)) + import_chalk2.default.dim(" repeated tool call patterns found") + loopCost
|
|
10674
10795
|
);
|
|
10675
10796
|
}
|
|
10676
10797
|
if (reviewCount > 0) {
|
|
@@ -10690,7 +10811,7 @@ function registerScanCommand(program2) {
|
|
|
10690
10811
|
for (const f of shownDlp) {
|
|
10691
10812
|
const ts = f.timestamp ? import_chalk2.default.dim(fmtTs(f.timestamp) + " ") : "";
|
|
10692
10813
|
const proj = import_chalk2.default.dim(f.project.slice(0, 22).padEnd(22) + " ");
|
|
10693
|
-
const agentBadge = f.agent === "gemini" ? import_chalk2.default.blue("[Gemini] ") : f.agent === "codex" ? import_chalk2.default.magenta("[Codex] ") : import_chalk2.default.cyan("[Claude] ");
|
|
10814
|
+
const agentBadge = f.agent === "gemini" ? import_chalk2.default.blue("[Gemini] ") : f.agent === "codex" ? import_chalk2.default.magenta("[Codex] ") : f.agent === "shell" ? import_chalk2.default.yellow("[Shell] ") : import_chalk2.default.cyan("[Claude] ");
|
|
10694
10815
|
const sessionSuffix = f.sessionId ? import_chalk2.default.dim(` \u2192 ${f.sessionId.slice(0, 8)}`) : "";
|
|
10695
10816
|
console.log(
|
|
10696
10817
|
` \u{1F6A8} ${ts}${proj}${agentBadge}` + import_chalk2.default.yellow(f.patternName) + import_chalk2.default.dim(" ") + import_chalk2.default.gray(f.redactedSample) + sessionSuffix
|
|
@@ -10722,10 +10843,11 @@ function registerScanCommand(program2) {
|
|
|
10722
10843
|
}
|
|
10723
10844
|
if (scan.loopFindings.length > 0) {
|
|
10724
10845
|
console.log(" " + import_chalk2.default.dim("\u2500".repeat(70)));
|
|
10846
|
+
const loopCostLabel = summary.loopWastedUSD > 0 ? import_chalk2.default.dim(" \xB7 ") + import_chalk2.default.yellow("~" + fmtCost(summary.loopWastedUSD) + " wasted") : "";
|
|
10725
10847
|
console.log(
|
|
10726
10848
|
" " + import_chalk2.default.yellow.bold("\u{1F501} Agent Loops") + import_chalk2.default.dim(" \xB7 ") + import_chalk2.default.yellow(
|
|
10727
10849
|
`${num(scan.loopFindings.length)} repeated pattern${scan.loopFindings.length !== 1 ? "s" : ""} found`
|
|
10728
|
-
)
|
|
10850
|
+
) + loopCostLabel
|
|
10729
10851
|
);
|
|
10730
10852
|
const shownLoops = drillDown ? scan.loopFindings : scan.loopFindings.slice(0, topN);
|
|
10731
10853
|
for (const f of shownLoops) {
|
|
@@ -10848,7 +10970,7 @@ function registerScanCommand(program2) {
|
|
|
10848
10970
|
}
|
|
10849
10971
|
});
|
|
10850
10972
|
}
|
|
10851
|
-
var import_chalk2, import_fs14, import_path17, import_os12, CLAUDE_PRICING, GEMINI_PRICING, LOOP_TOOLS, LOOP_THRESHOLD, DEFAULT_RULE_NAMES;
|
|
10973
|
+
var import_chalk2, import_fs14, import_path17, import_os12, CLAUDE_PRICING, GEMINI_PRICING, CODE_EXTENSIONS, LOOP_TOOLS, LOOP_THRESHOLD, DEFAULT_RULE_NAMES;
|
|
10852
10974
|
var init_scan = __esm({
|
|
10853
10975
|
"src/cli/commands/scan.ts"() {
|
|
10854
10976
|
"use strict";
|
|
@@ -10884,6 +11006,33 @@ var init_scan = __esm({
|
|
|
10884
11006
|
"gemini-1.5-flash": { i: 75e-9, o: 3e-7, cr: 1875e-11 },
|
|
10885
11007
|
"gemini-3-flash": { i: 1e-7, o: 4e-7, cr: 25e-9 }
|
|
10886
11008
|
};
|
|
11009
|
+
CODE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
11010
|
+
".ts",
|
|
11011
|
+
".tsx",
|
|
11012
|
+
".js",
|
|
11013
|
+
".jsx",
|
|
11014
|
+
".mjs",
|
|
11015
|
+
".cjs",
|
|
11016
|
+
".py",
|
|
11017
|
+
".rb",
|
|
11018
|
+
".go",
|
|
11019
|
+
".rs",
|
|
11020
|
+
".java",
|
|
11021
|
+
".kt",
|
|
11022
|
+
".swift",
|
|
11023
|
+
".c",
|
|
11024
|
+
".cpp",
|
|
11025
|
+
".h",
|
|
11026
|
+
".cs",
|
|
11027
|
+
".php",
|
|
11028
|
+
".sh",
|
|
11029
|
+
".bash",
|
|
11030
|
+
".html",
|
|
11031
|
+
".css",
|
|
11032
|
+
".scss",
|
|
11033
|
+
".vue",
|
|
11034
|
+
".svelte"
|
|
11035
|
+
]);
|
|
10887
11036
|
LOOP_TOOLS = /* @__PURE__ */ new Set([
|
|
10888
11037
|
"bash",
|
|
10889
11038
|
"execute_bash",
|
|
@@ -13590,20 +13739,20 @@ function getModelContextLimit(model) {
|
|
|
13590
13739
|
return 2e5;
|
|
13591
13740
|
}
|
|
13592
13741
|
function readSessionUsage() {
|
|
13593
|
-
const projectsDir =
|
|
13594
|
-
if (!
|
|
13742
|
+
const projectsDir = import_path43.default.join(import_os36.default.homedir(), ".claude", "projects");
|
|
13743
|
+
if (!import_fs40.default.existsSync(projectsDir)) return null;
|
|
13595
13744
|
let latestFile = null;
|
|
13596
13745
|
let latestMtime = 0;
|
|
13597
13746
|
try {
|
|
13598
|
-
for (const dir of
|
|
13599
|
-
const dirPath =
|
|
13747
|
+
for (const dir of import_fs40.default.readdirSync(projectsDir)) {
|
|
13748
|
+
const dirPath = import_path43.default.join(projectsDir, dir);
|
|
13600
13749
|
try {
|
|
13601
|
-
if (!
|
|
13602
|
-
for (const file of
|
|
13750
|
+
if (!import_fs40.default.statSync(dirPath).isDirectory()) continue;
|
|
13751
|
+
for (const file of import_fs40.default.readdirSync(dirPath)) {
|
|
13603
13752
|
if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
|
|
13604
|
-
const filePath =
|
|
13753
|
+
const filePath = import_path43.default.join(dirPath, file);
|
|
13605
13754
|
try {
|
|
13606
|
-
const mtime =
|
|
13755
|
+
const mtime = import_fs40.default.statSync(filePath).mtimeMs;
|
|
13607
13756
|
if (mtime > latestMtime) {
|
|
13608
13757
|
latestMtime = mtime;
|
|
13609
13758
|
latestFile = filePath;
|
|
@@ -13618,7 +13767,7 @@ function readSessionUsage() {
|
|
|
13618
13767
|
}
|
|
13619
13768
|
if (!latestFile) return null;
|
|
13620
13769
|
try {
|
|
13621
|
-
const lines =
|
|
13770
|
+
const lines = import_fs40.default.readFileSync(latestFile, "utf-8").split("\n");
|
|
13622
13771
|
let lastModel = "";
|
|
13623
13772
|
let lastInput = 0;
|
|
13624
13773
|
let lastOutput = 0;
|
|
@@ -13643,10 +13792,10 @@ function readSessionUsage() {
|
|
|
13643
13792
|
}
|
|
13644
13793
|
}
|
|
13645
13794
|
function formatContextStat(stat) {
|
|
13646
|
-
const pctColor = stat.fillPct >= 80 ?
|
|
13795
|
+
const pctColor = stat.fillPct >= 80 ? import_chalk26.default.red : stat.fillPct >= 50 ? import_chalk26.default.yellow : import_chalk26.default.cyan;
|
|
13647
13796
|
const k = (n) => `${Math.round(n / 1e3)}k`;
|
|
13648
13797
|
const modelShort = stat.model.replace(/@.*$/, "").replace(/-\d{8}$/, "").replace(/^claude-/, "");
|
|
13649
|
-
return
|
|
13798
|
+
return import_chalk26.default.dim("ctx: ") + pctColor(`${stat.fillPct}%`) + import_chalk26.default.dim(
|
|
13650
13799
|
` (${k(stat.inputTokens)}/${k(getModelContextLimit(stat.model))} out ${k(stat.outputTokens)} \xB7 ${modelShort})`
|
|
13651
13800
|
);
|
|
13652
13801
|
}
|
|
@@ -13662,28 +13811,28 @@ function wrappedLineCount(text) {
|
|
|
13662
13811
|
function agentLabel(agent) {
|
|
13663
13812
|
if (!agent || agent === "Terminal") return "";
|
|
13664
13813
|
const short = agent === "Claude Code" ? "Claude" : agent === "Gemini CLI" ? "Gemini" : agent === "Unknown Agent" ? "" : agent.split(" ")[0];
|
|
13665
|
-
return short ?
|
|
13814
|
+
return short ? import_chalk26.default.dim(`[${short}] `) : "";
|
|
13666
13815
|
}
|
|
13667
13816
|
function formatBase(activity) {
|
|
13668
13817
|
const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
|
|
13669
13818
|
const icon = getIcon(activity.tool);
|
|
13670
13819
|
const toolName = activity.tool.slice(0, 16).padEnd(16);
|
|
13671
|
-
const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(
|
|
13820
|
+
const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(import_os36.default.homedir(), "~");
|
|
13672
13821
|
const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
|
|
13673
|
-
return `${
|
|
13822
|
+
return `${import_chalk26.default.gray(time)} ${icon} ${agentLabel(activity.agent)}${import_chalk26.default.white.bold(toolName)} ${import_chalk26.default.dim(argsPreview)}`;
|
|
13674
13823
|
}
|
|
13675
13824
|
function renderResult(activity, result) {
|
|
13676
13825
|
const base = formatBase(activity);
|
|
13677
13826
|
let status;
|
|
13678
13827
|
if (result.status === "allow") {
|
|
13679
|
-
status =
|
|
13828
|
+
status = import_chalk26.default.green("\u2713 ALLOW");
|
|
13680
13829
|
} else if (result.status === "dlp") {
|
|
13681
|
-
status =
|
|
13830
|
+
status = import_chalk26.default.bgRed.white.bold(" \u{1F6E1}\uFE0F DLP ");
|
|
13682
13831
|
} else {
|
|
13683
|
-
status =
|
|
13832
|
+
status = import_chalk26.default.red("\u2717 BLOCK");
|
|
13684
13833
|
}
|
|
13685
13834
|
const cost = result.costEstimate ?? activity.costEstimate;
|
|
13686
|
-
const costSuffix = cost == null ? "" :
|
|
13835
|
+
const costSuffix = cost == null ? "" : import_chalk26.default.dim(` ~$${cost >= 1e-3 ? cost.toFixed(3) : "0.000"}`);
|
|
13687
13836
|
if (process.stdout.isTTY) {
|
|
13688
13837
|
if (pendingShownForId === activity.id && pendingWrappedLines > 1) {
|
|
13689
13838
|
import_readline5.default.moveCursor(process.stdout, 0, -(pendingWrappedLines - 1));
|
|
@@ -13700,19 +13849,19 @@ function renderResult(activity, result) {
|
|
|
13700
13849
|
}
|
|
13701
13850
|
function renderPending(activity) {
|
|
13702
13851
|
if (!process.stdout.isTTY) return;
|
|
13703
|
-
const line = `${formatBase(activity)} ${
|
|
13852
|
+
const line = `${formatBase(activity)} ${import_chalk26.default.yellow("\u25CF \u2026")}`;
|
|
13704
13853
|
pendingShownForId = activity.id;
|
|
13705
13854
|
pendingWrappedLines = wrappedLineCount(line);
|
|
13706
13855
|
process.stdout.write(`${line}\r`);
|
|
13707
13856
|
}
|
|
13708
13857
|
async function ensureDaemon() {
|
|
13709
13858
|
let pidPort = null;
|
|
13710
|
-
if (
|
|
13859
|
+
if (import_fs40.default.existsSync(PID_FILE)) {
|
|
13711
13860
|
try {
|
|
13712
|
-
const { port } = JSON.parse(
|
|
13861
|
+
const { port } = JSON.parse(import_fs40.default.readFileSync(PID_FILE, "utf-8"));
|
|
13713
13862
|
pidPort = port;
|
|
13714
13863
|
} catch {
|
|
13715
|
-
console.error(
|
|
13864
|
+
console.error(import_chalk26.default.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
|
|
13716
13865
|
}
|
|
13717
13866
|
}
|
|
13718
13867
|
const checkPort = pidPort ?? DAEMON_PORT;
|
|
@@ -13723,7 +13872,7 @@ async function ensureDaemon() {
|
|
|
13723
13872
|
if (res.ok) return checkPort;
|
|
13724
13873
|
} catch {
|
|
13725
13874
|
}
|
|
13726
|
-
console.log(
|
|
13875
|
+
console.log(import_chalk26.default.dim("\u{1F6E1}\uFE0F Starting Node9 daemon..."));
|
|
13727
13876
|
const child = (0, import_child_process16.spawn)(process.execPath, [process.argv[1], "daemon"], {
|
|
13728
13877
|
detached: true,
|
|
13729
13878
|
stdio: "ignore",
|
|
@@ -13740,7 +13889,7 @@ async function ensureDaemon() {
|
|
|
13740
13889
|
} catch {
|
|
13741
13890
|
}
|
|
13742
13891
|
}
|
|
13743
|
-
console.error(
|
|
13892
|
+
console.error(import_chalk26.default.red("\u274C Daemon failed to start. Try: node9 daemon start"));
|
|
13744
13893
|
process.exit(1);
|
|
13745
13894
|
}
|
|
13746
13895
|
function postDecisionHttp(id, decision, csrfToken, port, opts) {
|
|
@@ -13806,7 +13955,7 @@ function buildCardLines(req, localCount = 0) {
|
|
|
13806
13955
|
const severityIcon = isBlock ? `${RED}\u{1F6D1}` : `${YELLOW}\u26A0 `;
|
|
13807
13956
|
const rawDesc = req.riskMetadata?.ruleDescription ?? "";
|
|
13808
13957
|
const description = rawDesc ? cleanReason(rawDesc) : "";
|
|
13809
|
-
const agentSuffix = req.agent && req.agent !== "Terminal" ? ` ${RESET2}${
|
|
13958
|
+
const agentSuffix = req.agent && req.agent !== "Terminal" ? ` ${RESET2}${import_chalk26.default.dim(`(${req.agent})`)}` : "";
|
|
13810
13959
|
const lines = [
|
|
13811
13960
|
``,
|
|
13812
13961
|
`${BOLD2}${CYAN}\u2554\u2550\u2550 Node9 Approval Required \u2550\u2550\u2557${RESET2}`,
|
|
@@ -13862,9 +14011,9 @@ function buildRecoveryCardLines(req) {
|
|
|
13862
14011
|
];
|
|
13863
14012
|
}
|
|
13864
14013
|
function readApproversFromDisk() {
|
|
13865
|
-
const configPath =
|
|
14014
|
+
const configPath = import_path43.default.join(import_os36.default.homedir(), ".node9", "config.json");
|
|
13866
14015
|
try {
|
|
13867
|
-
const raw = JSON.parse(
|
|
14016
|
+
const raw = JSON.parse(import_fs40.default.readFileSync(configPath, "utf-8"));
|
|
13868
14017
|
const settings = raw.settings ?? {};
|
|
13869
14018
|
return settings.approvers ?? {};
|
|
13870
14019
|
} catch {
|
|
@@ -13875,20 +14024,20 @@ function approverStatusLine() {
|
|
|
13875
14024
|
const a = readApproversFromDisk();
|
|
13876
14025
|
const fmt = (label, key) => {
|
|
13877
14026
|
const on = a[key] !== false;
|
|
13878
|
-
return `[${key[0]}]${label.slice(1)} ${on ?
|
|
14027
|
+
return `[${key[0]}]${label.slice(1)} ${on ? import_chalk26.default.green("\u2713") : import_chalk26.default.dim("\u2717")}`;
|
|
13879
14028
|
};
|
|
13880
14029
|
return `${fmt("native", "native")} ${fmt("browser", "browser")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
|
|
13881
14030
|
}
|
|
13882
14031
|
function toggleApprover(channel) {
|
|
13883
|
-
const configPath =
|
|
14032
|
+
const configPath = import_path43.default.join(import_os36.default.homedir(), ".node9", "config.json");
|
|
13884
14033
|
try {
|
|
13885
|
-
const raw = JSON.parse(
|
|
14034
|
+
const raw = JSON.parse(import_fs40.default.readFileSync(configPath, "utf-8"));
|
|
13886
14035
|
const settings = raw.settings ?? {};
|
|
13887
14036
|
const approvers = settings.approvers ?? {};
|
|
13888
14037
|
approvers[channel] = approvers[channel] === false;
|
|
13889
14038
|
settings.approvers = approvers;
|
|
13890
14039
|
raw.settings = settings;
|
|
13891
|
-
|
|
14040
|
+
import_fs40.default.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
|
|
13892
14041
|
} catch (err2) {
|
|
13893
14042
|
process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
|
|
13894
14043
|
`);
|
|
@@ -13920,7 +14069,7 @@ async function startTail(options = {}) {
|
|
|
13920
14069
|
req2.end();
|
|
13921
14070
|
});
|
|
13922
14071
|
if (result.ok) {
|
|
13923
|
-
console.log(
|
|
14072
|
+
console.log(import_chalk26.default.green("\u2713 Flight Recorder buffer cleared."));
|
|
13924
14073
|
} else if (result.code === "ECONNREFUSED") {
|
|
13925
14074
|
throw new Error("Daemon is not running. Start it with: node9 daemon start");
|
|
13926
14075
|
} else if (result.code === "ETIMEDOUT") {
|
|
@@ -13964,7 +14113,7 @@ async function startTail(options = {}) {
|
|
|
13964
14113
|
const channel = name === "n" ? "native" : name === "b" ? "browser" : name === "c" ? "cloud" : name === "t" ? "terminal" : null;
|
|
13965
14114
|
if (channel) {
|
|
13966
14115
|
toggleApprover(channel);
|
|
13967
|
-
console.log(
|
|
14116
|
+
console.log(import_chalk26.default.dim(` Approvers: ${approverStatusLine()}`));
|
|
13968
14117
|
}
|
|
13969
14118
|
};
|
|
13970
14119
|
process.stdin.on("keypress", idleKeypressHandler);
|
|
@@ -14030,7 +14179,7 @@ async function startTail(options = {}) {
|
|
|
14030
14179
|
localAllowCounts.get(req2.toolName) ?? 0
|
|
14031
14180
|
)
|
|
14032
14181
|
);
|
|
14033
|
-
const decisionStamp = action === "always-allow" ?
|
|
14182
|
+
const decisionStamp = action === "always-allow" ? import_chalk26.default.yellow("\u2605 ALWAYS ALLOW") : action === "trust" ? import_chalk26.default.cyan("\u23F1 TRUST 30m") : action === "allow" ? import_chalk26.default.green("\u2713 ALLOWED") : action === "redirect" ? import_chalk26.default.yellow("\u21A9 REDIRECT AI") : import_chalk26.default.red("\u2717 DENIED");
|
|
14034
14183
|
stampedLines.push(` ${BOLD2}\u2192${RESET2} ${decisionStamp} ${GRAY}(terminal)${RESET2}`, ``);
|
|
14035
14184
|
for (const line of stampedLines) process.stdout.write(line + "\n");
|
|
14036
14185
|
process.stdout.write(SHOW_CURSOR);
|
|
@@ -14058,8 +14207,8 @@ async function startTail(options = {}) {
|
|
|
14058
14207
|
}
|
|
14059
14208
|
postDecisionHttp(req2.id, httpDecision, csrfToken, port, httpOpts).catch((err2) => {
|
|
14060
14209
|
try {
|
|
14061
|
-
|
|
14062
|
-
|
|
14210
|
+
import_fs40.default.appendFileSync(
|
|
14211
|
+
import_path43.default.join(import_os36.default.homedir(), ".node9", "hook-debug.log"),
|
|
14063
14212
|
`[tail] POST /decision failed: ${String(err2)}
|
|
14064
14213
|
`
|
|
14065
14214
|
);
|
|
@@ -14081,7 +14230,7 @@ async function startTail(options = {}) {
|
|
|
14081
14230
|
);
|
|
14082
14231
|
const stampedLines = buildCardLines(req2, priorCount);
|
|
14083
14232
|
if (externalDecision) {
|
|
14084
|
-
const source = externalDecision === "allow" ?
|
|
14233
|
+
const source = externalDecision === "allow" ? import_chalk26.default.green("\u2713 ALLOWED") : import_chalk26.default.red("\u2717 DENIED");
|
|
14085
14234
|
stampedLines.push(` ${BOLD2}\u2192${RESET2} ${source} ${GRAY}(external)${RESET2}`, ``);
|
|
14086
14235
|
}
|
|
14087
14236
|
for (const line of stampedLines) process.stdout.write(line + "\n");
|
|
@@ -14140,31 +14289,31 @@ async function startTail(options = {}) {
|
|
|
14140
14289
|
}
|
|
14141
14290
|
} catch {
|
|
14142
14291
|
}
|
|
14143
|
-
const auditLog =
|
|
14292
|
+
const auditLog = import_path43.default.join(import_os36.default.homedir(), ".node9", "audit.log");
|
|
14144
14293
|
try {
|
|
14145
|
-
const unackedDlp =
|
|
14294
|
+
const unackedDlp = import_fs40.default.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
|
|
14146
14295
|
if (unackedDlp > 0) {
|
|
14147
14296
|
console.log("");
|
|
14148
14297
|
console.log(
|
|
14149
|
-
|
|
14298
|
+
import_chalk26.default.bgRed.white.bold(
|
|
14150
14299
|
` \u26A0\uFE0F DLP ALERT: ${unackedDlp} secret${unackedDlp !== 1 ? "s" : ""} found in Claude response text \u2014 run: node9 dlp `
|
|
14151
14300
|
)
|
|
14152
14301
|
);
|
|
14153
14302
|
}
|
|
14154
14303
|
} catch {
|
|
14155
14304
|
}
|
|
14156
|
-
console.log(
|
|
14157
|
-
\u{1F6F0}\uFE0F Node9 tail `) +
|
|
14305
|
+
console.log(import_chalk26.default.cyan.bold(`
|
|
14306
|
+
\u{1F6F0}\uFE0F Node9 tail `) + import_chalk26.default.dim(`\u2192 ${dashboardUrl}`));
|
|
14158
14307
|
if (canApprove) {
|
|
14159
|
-
console.log(
|
|
14160
|
-
console.log(
|
|
14308
|
+
console.log(import_chalk26.default.dim("Card: [\u21B5/y] Allow [n] Deny [a] Always [t] Trust 30m"));
|
|
14309
|
+
console.log(import_chalk26.default.dim(`Approvers (toggle): ${approverStatusLine()} [q] quit`));
|
|
14161
14310
|
}
|
|
14162
14311
|
const ctxStat = readSessionUsage();
|
|
14163
14312
|
if (ctxStat) console.log(" " + formatContextStat(ctxStat));
|
|
14164
14313
|
if (options.history) {
|
|
14165
|
-
console.log(
|
|
14314
|
+
console.log(import_chalk26.default.dim("Showing history + live events.\n"));
|
|
14166
14315
|
} else {
|
|
14167
|
-
console.log(
|
|
14316
|
+
console.log(import_chalk26.default.dim("Showing live events only. Use --history to include past.\n"));
|
|
14168
14317
|
}
|
|
14169
14318
|
process.on("SIGINT", () => {
|
|
14170
14319
|
exitIdleMode();
|
|
@@ -14174,13 +14323,13 @@ async function startTail(options = {}) {
|
|
|
14174
14323
|
import_readline5.default.clearLine(process.stdout, 0);
|
|
14175
14324
|
import_readline5.default.cursorTo(process.stdout, 0);
|
|
14176
14325
|
}
|
|
14177
|
-
console.log(
|
|
14326
|
+
console.log(import_chalk26.default.dim("\n\u{1F6F0}\uFE0F Disconnected."));
|
|
14178
14327
|
process.exit(0);
|
|
14179
14328
|
});
|
|
14180
14329
|
const sseUrl = `http://127.0.0.1:${port}/events?capabilities=input`;
|
|
14181
14330
|
const req = import_http2.default.get(sseUrl, (res) => {
|
|
14182
14331
|
if (res.statusCode !== 200) {
|
|
14183
|
-
console.error(
|
|
14332
|
+
console.error(import_chalk26.default.red(`Failed to connect: HTTP ${res.statusCode}`));
|
|
14184
14333
|
process.exit(1);
|
|
14185
14334
|
}
|
|
14186
14335
|
if (canApprove) enterIdleMode();
|
|
@@ -14211,7 +14360,7 @@ async function startTail(options = {}) {
|
|
|
14211
14360
|
import_readline5.default.clearLine(process.stdout, 0);
|
|
14212
14361
|
import_readline5.default.cursorTo(process.stdout, 0);
|
|
14213
14362
|
}
|
|
14214
|
-
console.log(
|
|
14363
|
+
console.log(import_chalk26.default.red("\n\u274C Daemon disconnected."));
|
|
14215
14364
|
process.exit(1);
|
|
14216
14365
|
});
|
|
14217
14366
|
});
|
|
@@ -14303,9 +14452,9 @@ async function startTail(options = {}) {
|
|
|
14303
14452
|
const hash = data.hash ?? "";
|
|
14304
14453
|
const summary = data.argsSummary ?? data.tool;
|
|
14305
14454
|
const fileCount = data.fileCount ?? 0;
|
|
14306
|
-
const files = fileCount > 0 ?
|
|
14455
|
+
const files = fileCount > 0 ? import_chalk26.default.dim(` \xB7 ${fileCount} file${fileCount === 1 ? "" : "s"}`) : "";
|
|
14307
14456
|
process.stdout.write(
|
|
14308
|
-
`${
|
|
14457
|
+
`${import_chalk26.default.dim(time)} ${import_chalk26.default.cyan("\u{1F4F8} snapshot")} ${import_chalk26.default.dim(hash)} ${summary}${files}
|
|
14309
14458
|
`
|
|
14310
14459
|
);
|
|
14311
14460
|
return;
|
|
@@ -14322,26 +14471,26 @@ async function startTail(options = {}) {
|
|
|
14322
14471
|
}
|
|
14323
14472
|
req.on("error", (err2) => {
|
|
14324
14473
|
const msg = err2.code === "ECONNREFUSED" ? "Daemon is not running. Start it with: node9 daemon start" : err2.message;
|
|
14325
|
-
console.error(
|
|
14474
|
+
console.error(import_chalk26.default.red(`
|
|
14326
14475
|
\u274C ${msg}`));
|
|
14327
14476
|
process.exit(1);
|
|
14328
14477
|
});
|
|
14329
14478
|
}
|
|
14330
|
-
var import_http2,
|
|
14479
|
+
var import_http2, import_chalk26, import_fs40, import_os36, import_path43, import_readline5, import_child_process16, PID_FILE, ICONS, MODEL_CONTEXT_LIMITS, RESET2, BOLD2, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, pendingShownForId, pendingWrappedLines, DIVIDER;
|
|
14331
14480
|
var init_tail = __esm({
|
|
14332
14481
|
"src/tui/tail.ts"() {
|
|
14333
14482
|
"use strict";
|
|
14334
14483
|
import_http2 = __toESM(require("http"));
|
|
14335
|
-
|
|
14336
|
-
|
|
14337
|
-
|
|
14338
|
-
|
|
14484
|
+
import_chalk26 = __toESM(require("chalk"));
|
|
14485
|
+
import_fs40 = __toESM(require("fs"));
|
|
14486
|
+
import_os36 = __toESM(require("os"));
|
|
14487
|
+
import_path43 = __toESM(require("path"));
|
|
14339
14488
|
import_readline5 = __toESM(require("readline"));
|
|
14340
14489
|
import_child_process16 = require("child_process");
|
|
14341
14490
|
init_daemon2();
|
|
14342
14491
|
init_daemon();
|
|
14343
14492
|
init_core();
|
|
14344
|
-
PID_FILE =
|
|
14493
|
+
PID_FILE = import_path43.default.join(import_os36.default.homedir(), ".node9", "daemon.pid");
|
|
14345
14494
|
ICONS = {
|
|
14346
14495
|
bash: "\u{1F4BB}",
|
|
14347
14496
|
shell: "\u{1F4BB}",
|
|
@@ -14463,9 +14612,9 @@ function formatTimeLeft(resetsAt) {
|
|
|
14463
14612
|
return ` (${m}m left)`;
|
|
14464
14613
|
}
|
|
14465
14614
|
function safeReadJson(filePath) {
|
|
14466
|
-
if (!
|
|
14615
|
+
if (!import_fs41.default.existsSync(filePath)) return null;
|
|
14467
14616
|
try {
|
|
14468
|
-
return JSON.parse(
|
|
14617
|
+
return JSON.parse(import_fs41.default.readFileSync(filePath, "utf-8"));
|
|
14469
14618
|
} catch {
|
|
14470
14619
|
return null;
|
|
14471
14620
|
}
|
|
@@ -14486,12 +14635,12 @@ function countHooksInFile(filePath) {
|
|
|
14486
14635
|
return Object.keys(cfg.hooks).length;
|
|
14487
14636
|
}
|
|
14488
14637
|
function countRulesInDir(rulesDir) {
|
|
14489
|
-
if (!
|
|
14638
|
+
if (!import_fs41.default.existsSync(rulesDir)) return 0;
|
|
14490
14639
|
let count = 0;
|
|
14491
14640
|
try {
|
|
14492
|
-
for (const entry of
|
|
14641
|
+
for (const entry of import_fs41.default.readdirSync(rulesDir, { withFileTypes: true })) {
|
|
14493
14642
|
if (entry.isDirectory()) {
|
|
14494
|
-
count += countRulesInDir(
|
|
14643
|
+
count += countRulesInDir(import_path44.default.join(rulesDir, entry.name));
|
|
14495
14644
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
14496
14645
|
count++;
|
|
14497
14646
|
}
|
|
@@ -14502,46 +14651,46 @@ function countRulesInDir(rulesDir) {
|
|
|
14502
14651
|
}
|
|
14503
14652
|
function isSamePath(a, b) {
|
|
14504
14653
|
try {
|
|
14505
|
-
return
|
|
14654
|
+
return import_path44.default.resolve(a) === import_path44.default.resolve(b);
|
|
14506
14655
|
} catch {
|
|
14507
14656
|
return false;
|
|
14508
14657
|
}
|
|
14509
14658
|
}
|
|
14510
14659
|
function countConfigs(cwd) {
|
|
14511
|
-
const homeDir2 =
|
|
14512
|
-
const claudeDir =
|
|
14660
|
+
const homeDir2 = import_os37.default.homedir();
|
|
14661
|
+
const claudeDir = import_path44.default.join(homeDir2, ".claude");
|
|
14513
14662
|
let claudeMdCount = 0;
|
|
14514
14663
|
let rulesCount = 0;
|
|
14515
14664
|
let hooksCount = 0;
|
|
14516
14665
|
const userMcpServers = /* @__PURE__ */ new Set();
|
|
14517
14666
|
const projectMcpServers = /* @__PURE__ */ new Set();
|
|
14518
|
-
if (
|
|
14519
|
-
rulesCount += countRulesInDir(
|
|
14520
|
-
const userSettings =
|
|
14667
|
+
if (import_fs41.default.existsSync(import_path44.default.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
14668
|
+
rulesCount += countRulesInDir(import_path44.default.join(claudeDir, "rules"));
|
|
14669
|
+
const userSettings = import_path44.default.join(claudeDir, "settings.json");
|
|
14521
14670
|
for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
|
|
14522
14671
|
hooksCount += countHooksInFile(userSettings);
|
|
14523
|
-
const userClaudeJson =
|
|
14672
|
+
const userClaudeJson = import_path44.default.join(homeDir2, ".claude.json");
|
|
14524
14673
|
for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
|
|
14525
14674
|
for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
|
|
14526
14675
|
userMcpServers.delete(name);
|
|
14527
14676
|
}
|
|
14528
14677
|
if (cwd) {
|
|
14529
|
-
if (
|
|
14530
|
-
if (
|
|
14531
|
-
const projectClaudeDir =
|
|
14678
|
+
if (import_fs41.default.existsSync(import_path44.default.join(cwd, "CLAUDE.md"))) claudeMdCount++;
|
|
14679
|
+
if (import_fs41.default.existsSync(import_path44.default.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
|
|
14680
|
+
const projectClaudeDir = import_path44.default.join(cwd, ".claude");
|
|
14532
14681
|
const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
|
|
14533
14682
|
if (!overlapsUserScope) {
|
|
14534
|
-
if (
|
|
14535
|
-
rulesCount += countRulesInDir(
|
|
14536
|
-
const projSettings =
|
|
14683
|
+
if (import_fs41.default.existsSync(import_path44.default.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
14684
|
+
rulesCount += countRulesInDir(import_path44.default.join(projectClaudeDir, "rules"));
|
|
14685
|
+
const projSettings = import_path44.default.join(projectClaudeDir, "settings.json");
|
|
14537
14686
|
for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
|
|
14538
14687
|
hooksCount += countHooksInFile(projSettings);
|
|
14539
14688
|
}
|
|
14540
|
-
if (
|
|
14541
|
-
const localSettings =
|
|
14689
|
+
if (import_fs41.default.existsSync(import_path44.default.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
|
|
14690
|
+
const localSettings = import_path44.default.join(projectClaudeDir, "settings.local.json");
|
|
14542
14691
|
for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
|
|
14543
14692
|
hooksCount += countHooksInFile(localSettings);
|
|
14544
|
-
const mcpJsonServers = getMcpServerNames(
|
|
14693
|
+
const mcpJsonServers = getMcpServerNames(import_path44.default.join(cwd, ".mcp.json"));
|
|
14545
14694
|
const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
|
|
14546
14695
|
for (const name of disabledMcpJson) mcpJsonServers.delete(name);
|
|
14547
14696
|
for (const name of mcpJsonServers) projectMcpServers.add(name);
|
|
@@ -14574,12 +14723,12 @@ function readActiveShieldsHud() {
|
|
|
14574
14723
|
return shieldsCache.value;
|
|
14575
14724
|
}
|
|
14576
14725
|
try {
|
|
14577
|
-
const shieldsPath =
|
|
14578
|
-
if (!
|
|
14726
|
+
const shieldsPath = import_path44.default.join(import_os37.default.homedir(), ".node9", "shields.json");
|
|
14727
|
+
if (!import_fs41.default.existsSync(shieldsPath)) {
|
|
14579
14728
|
shieldsCache = { value: [], ts: now };
|
|
14580
14729
|
return [];
|
|
14581
14730
|
}
|
|
14582
|
-
const parsed = JSON.parse(
|
|
14731
|
+
const parsed = JSON.parse(import_fs41.default.readFileSync(shieldsPath, "utf-8"));
|
|
14583
14732
|
if (!Array.isArray(parsed.active)) {
|
|
14584
14733
|
shieldsCache = { value: [], ts: now };
|
|
14585
14734
|
return [];
|
|
@@ -14681,17 +14830,17 @@ function renderContextLine(stdin) {
|
|
|
14681
14830
|
async function main() {
|
|
14682
14831
|
try {
|
|
14683
14832
|
const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
|
|
14684
|
-
if (
|
|
14833
|
+
if (import_fs41.default.existsSync(import_path44.default.join(import_os37.default.homedir(), ".node9", "hud-debug"))) {
|
|
14685
14834
|
try {
|
|
14686
|
-
const logPath =
|
|
14835
|
+
const logPath = import_path44.default.join(import_os37.default.homedir(), ".node9", "hud-debug.log");
|
|
14687
14836
|
const MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
14688
14837
|
let size = 0;
|
|
14689
14838
|
try {
|
|
14690
|
-
size =
|
|
14839
|
+
size = import_fs41.default.statSync(logPath).size;
|
|
14691
14840
|
} catch {
|
|
14692
14841
|
}
|
|
14693
14842
|
if (size < MAX_LOG_SIZE) {
|
|
14694
|
-
|
|
14843
|
+
import_fs41.default.appendFileSync(
|
|
14695
14844
|
logPath,
|
|
14696
14845
|
JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
|
|
14697
14846
|
);
|
|
@@ -14712,11 +14861,11 @@ async function main() {
|
|
|
14712
14861
|
try {
|
|
14713
14862
|
const cwd = stdin.cwd ?? process.cwd();
|
|
14714
14863
|
for (const configPath of [
|
|
14715
|
-
|
|
14716
|
-
|
|
14864
|
+
import_path44.default.join(cwd, "node9.config.json"),
|
|
14865
|
+
import_path44.default.join(import_os37.default.homedir(), ".node9", "config.json")
|
|
14717
14866
|
]) {
|
|
14718
|
-
if (!
|
|
14719
|
-
const cfg = JSON.parse(
|
|
14867
|
+
if (!import_fs41.default.existsSync(configPath)) continue;
|
|
14868
|
+
const cfg = JSON.parse(import_fs41.default.readFileSync(configPath, "utf-8"));
|
|
14720
14869
|
const hud = cfg.settings?.hud;
|
|
14721
14870
|
if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
|
|
14722
14871
|
}
|
|
@@ -14734,13 +14883,13 @@ async function main() {
|
|
|
14734
14883
|
renderOffline();
|
|
14735
14884
|
}
|
|
14736
14885
|
}
|
|
14737
|
-
var
|
|
14886
|
+
var import_fs41, import_path44, import_os37, import_http3, RESET3, BOLD3, DIM, RED2, GREEN2, YELLOW2, BLUE, MAGENTA, CYAN2, WHITE, BAR_FILLED, BAR_EMPTY, BAR_WIDTH, shieldsCache, SHIELDS_CACHE_TTL_MS;
|
|
14738
14887
|
var init_hud = __esm({
|
|
14739
14888
|
"src/cli/hud.ts"() {
|
|
14740
14889
|
"use strict";
|
|
14741
|
-
|
|
14742
|
-
|
|
14743
|
-
|
|
14890
|
+
import_fs41 = __toESM(require("fs"));
|
|
14891
|
+
import_path44 = __toESM(require("path"));
|
|
14892
|
+
import_os37 = __toESM(require("os"));
|
|
14744
14893
|
import_http3 = __toESM(require("http"));
|
|
14745
14894
|
init_daemon();
|
|
14746
14895
|
RESET3 = "\x1B[0m";
|
|
@@ -14766,10 +14915,10 @@ var import_commander = require("commander");
|
|
|
14766
14915
|
init_core();
|
|
14767
14916
|
init_setup();
|
|
14768
14917
|
init_daemon2();
|
|
14769
|
-
var
|
|
14770
|
-
var
|
|
14771
|
-
var
|
|
14772
|
-
var
|
|
14918
|
+
var import_chalk27 = __toESM(require("chalk"));
|
|
14919
|
+
var import_fs42 = __toESM(require("fs"));
|
|
14920
|
+
var import_path45 = __toESM(require("path"));
|
|
14921
|
+
var import_os38 = __toESM(require("os"));
|
|
14773
14922
|
var import_prompts2 = require("@inquirer/prompts");
|
|
14774
14923
|
|
|
14775
14924
|
// src/utils/duration.ts
|
|
@@ -20479,22 +20628,207 @@ function registerDlpCommand(program2) {
|
|
|
20479
20628
|
});
|
|
20480
20629
|
}
|
|
20481
20630
|
|
|
20631
|
+
// src/cli/commands/mask.ts
|
|
20632
|
+
var import_chalk25 = __toESM(require("chalk"));
|
|
20633
|
+
var import_fs39 = __toESM(require("fs"));
|
|
20634
|
+
var import_path42 = __toESM(require("path"));
|
|
20635
|
+
var import_os35 = __toESM(require("os"));
|
|
20636
|
+
init_dlp();
|
|
20637
|
+
function findJsonlFiles(dir) {
|
|
20638
|
+
const results = [];
|
|
20639
|
+
if (!import_fs39.default.existsSync(dir)) return results;
|
|
20640
|
+
for (const entry of import_fs39.default.readdirSync(dir, { withFileTypes: true })) {
|
|
20641
|
+
const full = import_path42.default.join(dir, entry.name);
|
|
20642
|
+
if (entry.isDirectory()) results.push(...findJsonlFiles(full));
|
|
20643
|
+
else if (entry.isFile() && entry.name.endsWith(".jsonl")) results.push(full);
|
|
20644
|
+
}
|
|
20645
|
+
return results;
|
|
20646
|
+
}
|
|
20647
|
+
function redactJson(obj) {
|
|
20648
|
+
if (typeof obj === "string") {
|
|
20649
|
+
const { result, found } = redactText(obj);
|
|
20650
|
+
return { value: result, modified: result !== obj, found };
|
|
20651
|
+
}
|
|
20652
|
+
if (Array.isArray(obj)) {
|
|
20653
|
+
let modified = false;
|
|
20654
|
+
const found = [];
|
|
20655
|
+
const value = obj.map((item) => {
|
|
20656
|
+
const r = redactJson(item);
|
|
20657
|
+
if (r.modified) modified = true;
|
|
20658
|
+
r.found.forEach((f) => {
|
|
20659
|
+
if (!found.includes(f)) found.push(f);
|
|
20660
|
+
});
|
|
20661
|
+
return r.value;
|
|
20662
|
+
});
|
|
20663
|
+
return { value, modified, found };
|
|
20664
|
+
}
|
|
20665
|
+
if (obj !== null && typeof obj === "object") {
|
|
20666
|
+
let modified = false;
|
|
20667
|
+
const found = [];
|
|
20668
|
+
const value = {};
|
|
20669
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
20670
|
+
const r = redactJson(v);
|
|
20671
|
+
value[k] = r.value;
|
|
20672
|
+
if (r.modified) modified = true;
|
|
20673
|
+
r.found.forEach((f) => {
|
|
20674
|
+
if (!found.includes(f)) found.push(f);
|
|
20675
|
+
});
|
|
20676
|
+
}
|
|
20677
|
+
return { value, modified, found };
|
|
20678
|
+
}
|
|
20679
|
+
return { value: obj, modified: false, found: [] };
|
|
20680
|
+
}
|
|
20681
|
+
function processFile(filePath, dryRun) {
|
|
20682
|
+
let raw;
|
|
20683
|
+
try {
|
|
20684
|
+
raw = import_fs39.default.readFileSync(filePath, "utf-8");
|
|
20685
|
+
} catch {
|
|
20686
|
+
return { redactedLines: 0, patterns: [] };
|
|
20687
|
+
}
|
|
20688
|
+
const lines = raw.split("\n");
|
|
20689
|
+
let redactedLines = 0;
|
|
20690
|
+
const patterns = [];
|
|
20691
|
+
const newLines = [];
|
|
20692
|
+
for (const line of lines) {
|
|
20693
|
+
if (!line.trim()) {
|
|
20694
|
+
newLines.push(line);
|
|
20695
|
+
continue;
|
|
20696
|
+
}
|
|
20697
|
+
let parsed;
|
|
20698
|
+
try {
|
|
20699
|
+
parsed = JSON.parse(line);
|
|
20700
|
+
} catch {
|
|
20701
|
+
newLines.push(line);
|
|
20702
|
+
continue;
|
|
20703
|
+
}
|
|
20704
|
+
const { value, modified, found } = redactJson(parsed);
|
|
20705
|
+
if (modified) {
|
|
20706
|
+
redactedLines++;
|
|
20707
|
+
found.forEach((f) => {
|
|
20708
|
+
if (!patterns.includes(f)) patterns.push(f);
|
|
20709
|
+
});
|
|
20710
|
+
newLines.push(JSON.stringify(value));
|
|
20711
|
+
} else {
|
|
20712
|
+
newLines.push(line);
|
|
20713
|
+
}
|
|
20714
|
+
}
|
|
20715
|
+
if (!dryRun && redactedLines > 0) {
|
|
20716
|
+
import_fs39.default.writeFileSync(filePath, newLines.join("\n"), "utf-8");
|
|
20717
|
+
}
|
|
20718
|
+
return { redactedLines, patterns };
|
|
20719
|
+
}
|
|
20720
|
+
function processJsonFile(filePath, dryRun) {
|
|
20721
|
+
let raw;
|
|
20722
|
+
try {
|
|
20723
|
+
raw = import_fs39.default.readFileSync(filePath, "utf-8");
|
|
20724
|
+
} catch {
|
|
20725
|
+
return { redactedLines: 0, patterns: [] };
|
|
20726
|
+
}
|
|
20727
|
+
let parsed;
|
|
20728
|
+
try {
|
|
20729
|
+
parsed = JSON.parse(raw);
|
|
20730
|
+
} catch {
|
|
20731
|
+
return { redactedLines: 0, patterns: [] };
|
|
20732
|
+
}
|
|
20733
|
+
const { value, modified, found } = redactJson(parsed);
|
|
20734
|
+
if (!modified) return { redactedLines: 0, patterns: [] };
|
|
20735
|
+
if (!dryRun) {
|
|
20736
|
+
import_fs39.default.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
|
|
20737
|
+
}
|
|
20738
|
+
return { redactedLines: 1, patterns: found };
|
|
20739
|
+
}
|
|
20740
|
+
function findJsonFiles(dir) {
|
|
20741
|
+
const results = [];
|
|
20742
|
+
if (!import_fs39.default.existsSync(dir)) return results;
|
|
20743
|
+
for (const entry of import_fs39.default.readdirSync(dir, { withFileTypes: true })) {
|
|
20744
|
+
const full = import_path42.default.join(dir, entry.name);
|
|
20745
|
+
if (entry.isDirectory()) results.push(...findJsonFiles(full));
|
|
20746
|
+
else if (entry.isFile() && entry.name.endsWith(".json")) results.push(full);
|
|
20747
|
+
}
|
|
20748
|
+
return results;
|
|
20749
|
+
}
|
|
20750
|
+
function registerMaskCommand(program2) {
|
|
20751
|
+
program2.command("mask").description("Redact plaintext secrets from local AI session history files").option("--dry-run", "show what would be redacted without making changes").option("--all", "scan all history (default: last 30 days)").action(async (options) => {
|
|
20752
|
+
const dryRun = !!options.dryRun;
|
|
20753
|
+
const home = import_os35.default.homedir();
|
|
20754
|
+
const claudeDir = import_path42.default.join(home, ".claude", "projects");
|
|
20755
|
+
const geminiDir = import_path42.default.join(home, ".gemini", "tmp");
|
|
20756
|
+
const allFiles = [
|
|
20757
|
+
...findJsonlFiles(claudeDir).map((p) => ({ path: p, type: "jsonl" })),
|
|
20758
|
+
...findJsonFiles(geminiDir).map((p) => ({ path: p, type: "json" }))
|
|
20759
|
+
];
|
|
20760
|
+
const cutoff = options.all ? null : new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
|
|
20761
|
+
const filtered = cutoff ? allFiles.filter((f) => {
|
|
20762
|
+
try {
|
|
20763
|
+
return import_fs39.default.statSync(f.path).mtime >= cutoff;
|
|
20764
|
+
} catch {
|
|
20765
|
+
return false;
|
|
20766
|
+
}
|
|
20767
|
+
}) : allFiles;
|
|
20768
|
+
if (filtered.length === 0) {
|
|
20769
|
+
console.log(import_chalk25.default.yellow(" No session files found."));
|
|
20770
|
+
return;
|
|
20771
|
+
}
|
|
20772
|
+
console.log("");
|
|
20773
|
+
if (dryRun) {
|
|
20774
|
+
console.log(import_chalk25.default.dim(" Dry run \u2014 no files will be modified.\n"));
|
|
20775
|
+
}
|
|
20776
|
+
let totalFiles = 0;
|
|
20777
|
+
let totalLines = 0;
|
|
20778
|
+
const totalPatterns = [];
|
|
20779
|
+
for (const file of filtered) {
|
|
20780
|
+
const shortPath = file.path.replace(home, "~");
|
|
20781
|
+
const { redactedLines, patterns } = file.type === "jsonl" ? processFile(file.path, dryRun) : processJsonFile(file.path, dryRun);
|
|
20782
|
+
if (redactedLines > 0) {
|
|
20783
|
+
totalFiles++;
|
|
20784
|
+
totalLines += redactedLines;
|
|
20785
|
+
patterns.forEach((p) => {
|
|
20786
|
+
if (!totalPatterns.includes(p)) totalPatterns.push(p);
|
|
20787
|
+
});
|
|
20788
|
+
const verb = dryRun ? "Would redact" : "Redacted";
|
|
20789
|
+
console.log(
|
|
20790
|
+
" " + import_chalk25.default.dim(shortPath.slice(0, 60).padEnd(62)) + import_chalk25.default.red(`${verb}: `) + import_chalk25.default.yellow(patterns.join(", ")) + import_chalk25.default.dim(` (${redactedLines} line${redactedLines !== 1 ? "s" : ""})`)
|
|
20791
|
+
);
|
|
20792
|
+
}
|
|
20793
|
+
}
|
|
20794
|
+
console.log("");
|
|
20795
|
+
if (totalFiles === 0) {
|
|
20796
|
+
console.log(import_chalk25.default.green(" No secrets found in session history."));
|
|
20797
|
+
} else {
|
|
20798
|
+
const verb = dryRun ? "would be modified" : "modified";
|
|
20799
|
+
console.log(
|
|
20800
|
+
import_chalk25.default.bold(` ${totalFiles} file${totalFiles !== 1 ? "s" : ""} ${verb}`) + import_chalk25.default.dim(`, ${totalLines} line${totalLines !== 1 ? "s" : ""} redacted`)
|
|
20801
|
+
);
|
|
20802
|
+
console.log(" Patterns: " + import_chalk25.default.yellow(totalPatterns.join(", ")));
|
|
20803
|
+
if (!dryRun) {
|
|
20804
|
+
console.log("");
|
|
20805
|
+
console.log(
|
|
20806
|
+
import_chalk25.default.dim(
|
|
20807
|
+
" Note: secrets were already sent to the AI provider during the active session.\n This cleans your local disk only. Rotate any exposed keys."
|
|
20808
|
+
)
|
|
20809
|
+
);
|
|
20810
|
+
}
|
|
20811
|
+
}
|
|
20812
|
+
console.log("");
|
|
20813
|
+
});
|
|
20814
|
+
}
|
|
20815
|
+
|
|
20482
20816
|
// src/cli.ts
|
|
20483
20817
|
var { version } = JSON.parse(
|
|
20484
|
-
|
|
20818
|
+
import_fs42.default.readFileSync(import_path45.default.join(__dirname, "../package.json"), "utf-8")
|
|
20485
20819
|
);
|
|
20486
20820
|
var program = new import_commander.Command();
|
|
20487
20821
|
program.name("node9").description("The Sudo Command for AI Agents").version(version);
|
|
20488
20822
|
program.command("login").argument("<apiKey>").option("--local", "Save key for audit/logging only \u2014 local config still controls all decisions").option("--profile <name>", 'Save as a named profile (default: "default")').action((apiKey, options) => {
|
|
20489
20823
|
const DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept";
|
|
20490
|
-
const credPath =
|
|
20491
|
-
if (!
|
|
20492
|
-
|
|
20824
|
+
const credPath = import_path45.default.join(import_os38.default.homedir(), ".node9", "credentials.json");
|
|
20825
|
+
if (!import_fs42.default.existsSync(import_path45.default.dirname(credPath)))
|
|
20826
|
+
import_fs42.default.mkdirSync(import_path45.default.dirname(credPath), { recursive: true });
|
|
20493
20827
|
const profileName = options.profile || "default";
|
|
20494
20828
|
let existingCreds = {};
|
|
20495
20829
|
try {
|
|
20496
|
-
if (
|
|
20497
|
-
const raw = JSON.parse(
|
|
20830
|
+
if (import_fs42.default.existsSync(credPath)) {
|
|
20831
|
+
const raw = JSON.parse(import_fs42.default.readFileSync(credPath, "utf-8"));
|
|
20498
20832
|
if (raw.apiKey) {
|
|
20499
20833
|
existingCreds = {
|
|
20500
20834
|
default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL2 }
|
|
@@ -20506,13 +20840,13 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
20506
20840
|
} catch {
|
|
20507
20841
|
}
|
|
20508
20842
|
existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL2 };
|
|
20509
|
-
|
|
20843
|
+
import_fs42.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
|
|
20510
20844
|
if (profileName === "default") {
|
|
20511
|
-
const configPath =
|
|
20845
|
+
const configPath = import_path45.default.join(import_os38.default.homedir(), ".node9", "config.json");
|
|
20512
20846
|
let config = {};
|
|
20513
20847
|
try {
|
|
20514
|
-
if (
|
|
20515
|
-
config = JSON.parse(
|
|
20848
|
+
if (import_fs42.default.existsSync(configPath))
|
|
20849
|
+
config = JSON.parse(import_fs42.default.readFileSync(configPath, "utf-8"));
|
|
20516
20850
|
} catch {
|
|
20517
20851
|
}
|
|
20518
20852
|
if (!config.settings || typeof config.settings !== "object") config.settings = {};
|
|
@@ -20527,19 +20861,19 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
20527
20861
|
approvers.cloud = false;
|
|
20528
20862
|
}
|
|
20529
20863
|
s.approvers = approvers;
|
|
20530
|
-
if (!
|
|
20531
|
-
|
|
20532
|
-
|
|
20864
|
+
if (!import_fs42.default.existsSync(import_path45.default.dirname(configPath)))
|
|
20865
|
+
import_fs42.default.mkdirSync(import_path45.default.dirname(configPath), { recursive: true });
|
|
20866
|
+
import_fs42.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
20533
20867
|
}
|
|
20534
20868
|
if (options.profile && profileName !== "default") {
|
|
20535
|
-
console.log(
|
|
20536
|
-
console.log(
|
|
20869
|
+
console.log(import_chalk27.default.green(`\u2705 Profile "${profileName}" saved`));
|
|
20870
|
+
console.log(import_chalk27.default.gray(` Switch to it per-session: NODE9_PROFILE=${profileName} claude`));
|
|
20537
20871
|
} else if (options.local) {
|
|
20538
|
-
console.log(
|
|
20539
|
-
console.log(
|
|
20872
|
+
console.log(import_chalk27.default.green(`\u2705 Privacy mode \u{1F6E1}\uFE0F`));
|
|
20873
|
+
console.log(import_chalk27.default.gray(` All decisions stay on this machine.`));
|
|
20540
20874
|
} else {
|
|
20541
|
-
console.log(
|
|
20542
|
-
console.log(
|
|
20875
|
+
console.log(import_chalk27.default.green(`\u2705 Logged in \u2014 agent mode`));
|
|
20876
|
+
console.log(import_chalk27.default.gray(` Team policy enforced for all calls via Node9 cloud.`));
|
|
20543
20877
|
}
|
|
20544
20878
|
});
|
|
20545
20879
|
program.command("addto").description("Integrate Node9 with an AI agent").addHelpText(
|
|
@@ -20557,7 +20891,7 @@ program.command("addto").description("Integrate Node9 with an AI agent").addHelp
|
|
|
20557
20891
|
if (target === "vscode") return await setupVSCode();
|
|
20558
20892
|
if (target === "hud") return setupHud();
|
|
20559
20893
|
console.error(
|
|
20560
|
-
|
|
20894
|
+
import_chalk27.default.red(
|
|
20561
20895
|
`Unknown target: "${target}". Supported: claude, gemini, cursor, codex, windsurf, vscode, hud`
|
|
20562
20896
|
)
|
|
20563
20897
|
);
|
|
@@ -20571,17 +20905,17 @@ program.command("setup").description('Alias for "addto" \u2014 integrate Node9 w
|
|
|
20571
20905
|
"The agent to protect: claude | gemini | cursor | codex | windsurf | vscode | hud"
|
|
20572
20906
|
).action(async (target) => {
|
|
20573
20907
|
if (!target) {
|
|
20574
|
-
console.log(
|
|
20575
|
-
console.log(" Usage: " +
|
|
20908
|
+
console.log(import_chalk27.default.cyan("\n\u{1F6E1}\uFE0F Node9 Setup \u2014 integrate with your AI agent\n"));
|
|
20909
|
+
console.log(" Usage: " + import_chalk27.default.white("node9 setup <target>") + "\n");
|
|
20576
20910
|
console.log(" Targets:");
|
|
20577
|
-
console.log(" " +
|
|
20578
|
-
console.log(" " +
|
|
20579
|
-
console.log(" " +
|
|
20580
|
-
console.log(" " +
|
|
20581
|
-
console.log(" " +
|
|
20582
|
-
console.log(" " +
|
|
20911
|
+
console.log(" " + import_chalk27.default.green("claude") + " \u2014 Claude Code (hook mode)");
|
|
20912
|
+
console.log(" " + import_chalk27.default.green("gemini") + " \u2014 Gemini CLI (hook mode)");
|
|
20913
|
+
console.log(" " + import_chalk27.default.green("cursor") + " \u2014 Cursor (MCP proxy)");
|
|
20914
|
+
console.log(" " + import_chalk27.default.green("codex") + " \u2014 OpenAI Codex CLI (MCP proxy)");
|
|
20915
|
+
console.log(" " + import_chalk27.default.green("windsurf") + " \u2014 Windsurf (MCP proxy)");
|
|
20916
|
+
console.log(" " + import_chalk27.default.green("vscode") + " \u2014 VSCode / Copilot (MCP proxy)");
|
|
20583
20917
|
process.stdout.write(
|
|
20584
|
-
" " +
|
|
20918
|
+
" " + import_chalk27.default.green("hud") + " \u2014 Claude Code security statusline\n"
|
|
20585
20919
|
);
|
|
20586
20920
|
console.log("");
|
|
20587
20921
|
return;
|
|
@@ -20595,7 +20929,7 @@ program.command("setup").description('Alias for "addto" \u2014 integrate Node9 w
|
|
|
20595
20929
|
if (t === "vscode") return await setupVSCode();
|
|
20596
20930
|
if (t === "hud") return setupHud();
|
|
20597
20931
|
console.error(
|
|
20598
|
-
|
|
20932
|
+
import_chalk27.default.red(
|
|
20599
20933
|
`Unknown target: "${target}". Supported: claude, gemini, cursor, codex, windsurf, vscode, hud`
|
|
20600
20934
|
)
|
|
20601
20935
|
);
|
|
@@ -20618,33 +20952,33 @@ program.command("removefrom").description("Remove Node9 hooks from an AI agent c
|
|
|
20618
20952
|
else if (target === "hud") fn = teardownHud;
|
|
20619
20953
|
else {
|
|
20620
20954
|
console.error(
|
|
20621
|
-
|
|
20955
|
+
import_chalk27.default.red(
|
|
20622
20956
|
`Unknown target: "${target}". Supported: claude, gemini, cursor, codex, windsurf, vscode, hud`
|
|
20623
20957
|
)
|
|
20624
20958
|
);
|
|
20625
20959
|
process.exit(1);
|
|
20626
20960
|
}
|
|
20627
|
-
console.log(
|
|
20961
|
+
console.log(import_chalk27.default.cyan(`
|
|
20628
20962
|
\u{1F6E1}\uFE0F Node9: removing hooks from ${target}...
|
|
20629
20963
|
`));
|
|
20630
20964
|
try {
|
|
20631
20965
|
fn();
|
|
20632
20966
|
} catch (err2) {
|
|
20633
|
-
console.error(
|
|
20967
|
+
console.error(import_chalk27.default.red(` \u26A0\uFE0F Failed: ${err2 instanceof Error ? err2.message : String(err2)}`));
|
|
20634
20968
|
process.exit(1);
|
|
20635
20969
|
}
|
|
20636
|
-
console.log(
|
|
20970
|
+
console.log(import_chalk27.default.gray("\n Restart the agent for changes to take effect."));
|
|
20637
20971
|
});
|
|
20638
20972
|
program.command("uninstall").description("Remove all Node9 hooks and optionally delete config files").option("--purge", "Also delete ~/.node9/ directory (config, audit log, credentials)").action(async (options) => {
|
|
20639
|
-
console.log(
|
|
20640
|
-
console.log(
|
|
20973
|
+
console.log(import_chalk27.default.cyan("\n\u{1F6E1}\uFE0F Node9 Uninstall\n"));
|
|
20974
|
+
console.log(import_chalk27.default.bold("Stopping daemon..."));
|
|
20641
20975
|
try {
|
|
20642
20976
|
stopDaemon();
|
|
20643
|
-
console.log(
|
|
20977
|
+
console.log(import_chalk27.default.green(" \u2705 Daemon stopped"));
|
|
20644
20978
|
} catch {
|
|
20645
|
-
console.log(
|
|
20979
|
+
console.log(import_chalk27.default.blue(" \u2139\uFE0F Daemon was not running"));
|
|
20646
20980
|
}
|
|
20647
|
-
console.log(
|
|
20981
|
+
console.log(import_chalk27.default.bold("\nRemoving hooks..."));
|
|
20648
20982
|
let teardownFailed = false;
|
|
20649
20983
|
for (const [label, fn] of [
|
|
20650
20984
|
["Claude", teardownClaude],
|
|
@@ -20659,45 +20993,45 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
20659
20993
|
} catch (err2) {
|
|
20660
20994
|
teardownFailed = true;
|
|
20661
20995
|
console.error(
|
|
20662
|
-
|
|
20996
|
+
import_chalk27.default.red(
|
|
20663
20997
|
` \u26A0\uFE0F Failed to remove ${label} hooks: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
20664
20998
|
)
|
|
20665
20999
|
);
|
|
20666
21000
|
}
|
|
20667
21001
|
}
|
|
20668
21002
|
if (options.purge) {
|
|
20669
|
-
const node9Dir =
|
|
20670
|
-
if (
|
|
21003
|
+
const node9Dir = import_path45.default.join(import_os38.default.homedir(), ".node9");
|
|
21004
|
+
if (import_fs42.default.existsSync(node9Dir)) {
|
|
20671
21005
|
const confirmed = await (0, import_prompts2.confirm)({
|
|
20672
21006
|
message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
|
|
20673
21007
|
default: false
|
|
20674
21008
|
});
|
|
20675
21009
|
if (confirmed) {
|
|
20676
|
-
|
|
20677
|
-
if (
|
|
21010
|
+
import_fs42.default.rmSync(node9Dir, { recursive: true });
|
|
21011
|
+
if (import_fs42.default.existsSync(node9Dir)) {
|
|
20678
21012
|
console.error(
|
|
20679
|
-
|
|
21013
|
+
import_chalk27.default.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
|
|
20680
21014
|
);
|
|
20681
21015
|
} else {
|
|
20682
|
-
console.log(
|
|
21016
|
+
console.log(import_chalk27.default.green("\n \u2705 Deleted ~/.node9/ (config, audit log, credentials)"));
|
|
20683
21017
|
}
|
|
20684
21018
|
} else {
|
|
20685
|
-
console.log(
|
|
21019
|
+
console.log(import_chalk27.default.yellow("\n Skipped \u2014 ~/.node9/ was not deleted."));
|
|
20686
21020
|
}
|
|
20687
21021
|
} else {
|
|
20688
|
-
console.log(
|
|
21022
|
+
console.log(import_chalk27.default.blue("\n \u2139\uFE0F ~/.node9/ not found \u2014 nothing to delete"));
|
|
20689
21023
|
}
|
|
20690
21024
|
} else {
|
|
20691
21025
|
console.log(
|
|
20692
|
-
|
|
21026
|
+
import_chalk27.default.gray("\n ~/.node9/ kept \u2014 run with --purge to delete config and audit log")
|
|
20693
21027
|
);
|
|
20694
21028
|
}
|
|
20695
21029
|
if (teardownFailed) {
|
|
20696
|
-
console.error(
|
|
21030
|
+
console.error(import_chalk27.default.red("\n \u26A0\uFE0F Some hooks could not be removed \u2014 see errors above."));
|
|
20697
21031
|
process.exit(1);
|
|
20698
21032
|
}
|
|
20699
|
-
console.log(
|
|
20700
|
-
console.log(
|
|
21033
|
+
console.log(import_chalk27.default.green.bold("\n\u{1F6E1}\uFE0F Node9 removed. Run: npm uninstall -g @node9/proxy"));
|
|
21034
|
+
console.log(import_chalk27.default.gray(" Restart any open AI agent sessions for changes to take effect.\n"));
|
|
20701
21035
|
});
|
|
20702
21036
|
registerDoctorCommand(program, version);
|
|
20703
21037
|
program.command("explain").description(
|
|
@@ -20710,7 +21044,7 @@ program.command("explain").description(
|
|
|
20710
21044
|
try {
|
|
20711
21045
|
args = JSON.parse(trimmed);
|
|
20712
21046
|
} catch {
|
|
20713
|
-
console.error(
|
|
21047
|
+
console.error(import_chalk27.default.red(`
|
|
20714
21048
|
\u274C Invalid JSON: ${trimmed}
|
|
20715
21049
|
`));
|
|
20716
21050
|
process.exit(1);
|
|
@@ -20721,54 +21055,54 @@ program.command("explain").description(
|
|
|
20721
21055
|
}
|
|
20722
21056
|
const result = await explainPolicy(tool, args);
|
|
20723
21057
|
console.log("");
|
|
20724
|
-
console.log(
|
|
21058
|
+
console.log(import_chalk27.default.cyan.bold("\u{1F6E1}\uFE0F Node9 Explain"));
|
|
20725
21059
|
console.log("");
|
|
20726
|
-
console.log(` ${
|
|
21060
|
+
console.log(` ${import_chalk27.default.bold("Tool:")} ${import_chalk27.default.white(result.tool)}`);
|
|
20727
21061
|
if (argsRaw) {
|
|
20728
21062
|
const preview2 = argsRaw.length > 80 ? argsRaw.slice(0, 77) + "\u2026" : argsRaw;
|
|
20729
|
-
console.log(` ${
|
|
21063
|
+
console.log(` ${import_chalk27.default.bold("Input:")} ${import_chalk27.default.gray(preview2)}`);
|
|
20730
21064
|
}
|
|
20731
21065
|
console.log("");
|
|
20732
|
-
console.log(
|
|
21066
|
+
console.log(import_chalk27.default.bold("Config Sources (Waterfall):"));
|
|
20733
21067
|
for (const tier of result.waterfall) {
|
|
20734
|
-
const num3 =
|
|
21068
|
+
const num3 = import_chalk27.default.gray(` ${tier.tier}.`);
|
|
20735
21069
|
const label = tier.label.padEnd(16);
|
|
20736
21070
|
let statusStr;
|
|
20737
21071
|
if (tier.tier === 1) {
|
|
20738
|
-
statusStr =
|
|
21072
|
+
statusStr = import_chalk27.default.gray(tier.note ?? "");
|
|
20739
21073
|
} else if (tier.status === "active") {
|
|
20740
|
-
const loc = tier.path ?
|
|
20741
|
-
const note = tier.note ?
|
|
20742
|
-
statusStr =
|
|
21074
|
+
const loc = tier.path ? import_chalk27.default.gray(tier.path) : "";
|
|
21075
|
+
const note = tier.note ? import_chalk27.default.gray(`(${tier.note})`) : "";
|
|
21076
|
+
statusStr = import_chalk27.default.green("\u2713 active") + (loc ? " " + loc : "") + (note ? " " + note : "");
|
|
20743
21077
|
} else {
|
|
20744
|
-
statusStr =
|
|
21078
|
+
statusStr = import_chalk27.default.gray("\u25CB " + (tier.note ?? "not found"));
|
|
20745
21079
|
}
|
|
20746
|
-
console.log(`${num3} ${
|
|
21080
|
+
console.log(`${num3} ${import_chalk27.default.white(label)} ${statusStr}`);
|
|
20747
21081
|
}
|
|
20748
21082
|
console.log("");
|
|
20749
|
-
console.log(
|
|
21083
|
+
console.log(import_chalk27.default.bold("Policy Evaluation:"));
|
|
20750
21084
|
for (const step of result.steps) {
|
|
20751
21085
|
const isFinal = step.isFinal;
|
|
20752
21086
|
let icon;
|
|
20753
|
-
if (step.outcome === "allow") icon =
|
|
20754
|
-
else if (step.outcome === "review") icon =
|
|
20755
|
-
else if (step.outcome === "skip") icon =
|
|
20756
|
-
else icon =
|
|
21087
|
+
if (step.outcome === "allow") icon = import_chalk27.default.green(" \u2705");
|
|
21088
|
+
else if (step.outcome === "review") icon = import_chalk27.default.red(" \u{1F534}");
|
|
21089
|
+
else if (step.outcome === "skip") icon = import_chalk27.default.gray(" \u2500 ");
|
|
21090
|
+
else icon = import_chalk27.default.gray(" \u25CB ");
|
|
20757
21091
|
const name = step.name.padEnd(18);
|
|
20758
|
-
const nameStr = isFinal ?
|
|
20759
|
-
const detail = isFinal ?
|
|
20760
|
-
const arrow = isFinal ?
|
|
21092
|
+
const nameStr = isFinal ? import_chalk27.default.white.bold(name) : import_chalk27.default.white(name);
|
|
21093
|
+
const detail = isFinal ? import_chalk27.default.white(step.detail) : import_chalk27.default.gray(step.detail);
|
|
21094
|
+
const arrow = isFinal ? import_chalk27.default.yellow(" \u2190 STOP") : "";
|
|
20761
21095
|
console.log(`${icon} ${nameStr} ${detail}${arrow}`);
|
|
20762
21096
|
}
|
|
20763
21097
|
console.log("");
|
|
20764
21098
|
if (result.decision === "allow") {
|
|
20765
|
-
console.log(
|
|
21099
|
+
console.log(import_chalk27.default.green.bold(" Decision: \u2705 ALLOW") + import_chalk27.default.gray(" \u2014 no approval needed"));
|
|
20766
21100
|
} else {
|
|
20767
21101
|
console.log(
|
|
20768
|
-
|
|
21102
|
+
import_chalk27.default.red.bold(" Decision: \u{1F534} REVIEW") + import_chalk27.default.gray(" \u2014 human approval required")
|
|
20769
21103
|
);
|
|
20770
21104
|
if (result.blockedByLabel) {
|
|
20771
|
-
console.log(
|
|
21105
|
+
console.log(import_chalk27.default.gray(` Reason: ${result.blockedByLabel}`));
|
|
20772
21106
|
}
|
|
20773
21107
|
}
|
|
20774
21108
|
console.log("");
|
|
@@ -20783,7 +21117,7 @@ program.command("tail").description("Stream live agent activity to the terminal"
|
|
|
20783
21117
|
try {
|
|
20784
21118
|
await startTail2(options);
|
|
20785
21119
|
} catch (err2) {
|
|
20786
|
-
console.error(
|
|
21120
|
+
console.error(import_chalk27.default.red(`\u274C ${err2 instanceof Error ? err2.message : String(err2)}`));
|
|
20787
21121
|
process.exit(1);
|
|
20788
21122
|
}
|
|
20789
21123
|
});
|
|
@@ -20816,14 +21150,14 @@ Claude Code spawns this command every ~300ms and writes a JSON payload to stdin.
|
|
|
20816
21150
|
Run "node9 addto claude" to register it as the statusLine.`
|
|
20817
21151
|
).argument("[subcommand]", 'Optional: "debug on" / "debug off" to toggle stdin logging').argument("[state]", 'on|off \u2014 used with "debug" subcommand').action(async (subcommand, state) => {
|
|
20818
21152
|
if (subcommand === "debug") {
|
|
20819
|
-
const flagFile =
|
|
21153
|
+
const flagFile = import_path45.default.join(import_os38.default.homedir(), ".node9", "hud-debug");
|
|
20820
21154
|
if (state === "on") {
|
|
20821
|
-
|
|
20822
|
-
|
|
21155
|
+
import_fs42.default.mkdirSync(import_path45.default.dirname(flagFile), { recursive: true });
|
|
21156
|
+
import_fs42.default.writeFileSync(flagFile, "");
|
|
20823
21157
|
console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
|
|
20824
21158
|
console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
|
|
20825
21159
|
} else if (state === "off") {
|
|
20826
|
-
if (
|
|
21160
|
+
if (import_fs42.default.existsSync(flagFile)) import_fs42.default.unlinkSync(flagFile);
|
|
20827
21161
|
console.log("HUD debug logging disabled.");
|
|
20828
21162
|
} else {
|
|
20829
21163
|
console.error("Usage: node9 hud debug on|off");
|
|
@@ -20838,7 +21172,7 @@ program.command("pause").description("Temporarily disable Node9 protection for a
|
|
|
20838
21172
|
const ms = parseDuration(options.duration);
|
|
20839
21173
|
if (ms === null) {
|
|
20840
21174
|
console.error(
|
|
20841
|
-
|
|
21175
|
+
import_chalk27.default.red(`
|
|
20842
21176
|
\u274C Invalid duration: "${options.duration}". Use format like 15m, 1h, 30s.
|
|
20843
21177
|
`)
|
|
20844
21178
|
);
|
|
@@ -20846,20 +21180,20 @@ program.command("pause").description("Temporarily disable Node9 protection for a
|
|
|
20846
21180
|
}
|
|
20847
21181
|
pauseNode9(ms, options.duration);
|
|
20848
21182
|
const expiresAt = new Date(Date.now() + ms).toLocaleTimeString();
|
|
20849
|
-
console.log(
|
|
21183
|
+
console.log(import_chalk27.default.yellow(`
|
|
20850
21184
|
\u23F8 Node9 paused until ${expiresAt}`));
|
|
20851
|
-
console.log(
|
|
20852
|
-
console.log(
|
|
21185
|
+
console.log(import_chalk27.default.gray(` All tool calls will be allowed without review.`));
|
|
21186
|
+
console.log(import_chalk27.default.gray(` Run "node9 resume" to re-enable early.
|
|
20853
21187
|
`));
|
|
20854
21188
|
});
|
|
20855
21189
|
program.command("resume").description("Re-enable Node9 protection immediately").action(() => {
|
|
20856
21190
|
const { paused } = checkPause();
|
|
20857
21191
|
if (!paused) {
|
|
20858
|
-
console.log(
|
|
21192
|
+
console.log(import_chalk27.default.gray("\nNode9 is already active \u2014 nothing to resume.\n"));
|
|
20859
21193
|
return;
|
|
20860
21194
|
}
|
|
20861
21195
|
resumeNode9();
|
|
20862
|
-
console.log(
|
|
21196
|
+
console.log(import_chalk27.default.green("\n\u25B6 Node9 resumed \u2014 protection is active.\n"));
|
|
20863
21197
|
});
|
|
20864
21198
|
var HOOK_BASED_AGENTS = {
|
|
20865
21199
|
claude: "claude",
|
|
@@ -20872,15 +21206,15 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
20872
21206
|
if (HOOK_BASED_AGENTS[firstArg2] !== void 0) {
|
|
20873
21207
|
const target = HOOK_BASED_AGENTS[firstArg2];
|
|
20874
21208
|
console.error(
|
|
20875
|
-
|
|
21209
|
+
import_chalk27.default.yellow(`
|
|
20876
21210
|
\u26A0\uFE0F Node9 proxy mode does not support "${target}" directly.`)
|
|
20877
21211
|
);
|
|
20878
|
-
console.error(
|
|
21212
|
+
console.error(import_chalk27.default.white(`
|
|
20879
21213
|
"${target}" uses its own hook system. Use:`));
|
|
20880
21214
|
console.error(
|
|
20881
|
-
|
|
21215
|
+
import_chalk27.default.green(` node9 addto ${target} `) + import_chalk27.default.gray("# one-time setup")
|
|
20882
21216
|
);
|
|
20883
|
-
console.error(
|
|
21217
|
+
console.error(import_chalk27.default.green(` ${target} `) + import_chalk27.default.gray("# run normally"));
|
|
20884
21218
|
process.exit(1);
|
|
20885
21219
|
}
|
|
20886
21220
|
const runArgs = firstArg2 === "shell" ? commandArgs.slice(1) : commandArgs;
|
|
@@ -20897,7 +21231,7 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
20897
21231
|
}
|
|
20898
21232
|
);
|
|
20899
21233
|
if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && getConfig().settings.autoStartDaemon) {
|
|
20900
|
-
console.error(
|
|
21234
|
+
console.error(import_chalk27.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically..."));
|
|
20901
21235
|
const daemonReady = await autoStartDaemonAndWait();
|
|
20902
21236
|
if (daemonReady) result = await authorizeHeadless("shell", { command: fullCommand });
|
|
20903
21237
|
}
|
|
@@ -20910,12 +21244,12 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
20910
21244
|
}
|
|
20911
21245
|
if (!result.approved) {
|
|
20912
21246
|
console.error(
|
|
20913
|
-
|
|
21247
|
+
import_chalk27.default.red(`
|
|
20914
21248
|
\u274C Node9 Blocked: ${result.reason || "Dangerous command detected."}`)
|
|
20915
21249
|
);
|
|
20916
21250
|
process.exit(1);
|
|
20917
21251
|
}
|
|
20918
|
-
console.error(
|
|
21252
|
+
console.error(import_chalk27.default.green("\n\u2705 Approved \u2014 running command...\n"));
|
|
20919
21253
|
await runProxy(fullCommand);
|
|
20920
21254
|
} else {
|
|
20921
21255
|
program.help();
|
|
@@ -20930,14 +21264,15 @@ registerAgentsCommand(program);
|
|
|
20930
21264
|
registerScanCommand(program);
|
|
20931
21265
|
registerSessionsCommand(program);
|
|
20932
21266
|
registerDlpCommand(program);
|
|
21267
|
+
registerMaskCommand(program);
|
|
20933
21268
|
if (process.argv[2] !== "daemon") {
|
|
20934
21269
|
process.on("unhandledRejection", (reason) => {
|
|
20935
21270
|
const isCheckHook = process.argv[2] === "check";
|
|
20936
21271
|
if (isCheckHook) {
|
|
20937
21272
|
if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
|
|
20938
|
-
const logPath =
|
|
21273
|
+
const logPath = import_path45.default.join(import_os38.default.homedir(), ".node9", "hook-debug.log");
|
|
20939
21274
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
20940
|
-
|
|
21275
|
+
import_fs42.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
|
|
20941
21276
|
`);
|
|
20942
21277
|
}
|
|
20943
21278
|
process.exit(0);
|