@node9/proxy 1.21.0 → 1.21.2
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.js +365 -390
- package/dist/cli.mjs +364 -389
- package/dist/dashboard.mjs +48 -10
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -13728,10 +13728,10 @@ function readSessionUsage() {
|
|
|
13728
13728
|
}
|
|
13729
13729
|
}
|
|
13730
13730
|
function formatContextStat(stat) {
|
|
13731
|
-
const pctColor = stat.fillPct >= 80 ?
|
|
13731
|
+
const pctColor = stat.fillPct >= 80 ? import_chalk29.default.red : stat.fillPct >= 50 ? import_chalk29.default.yellow : import_chalk29.default.cyan;
|
|
13732
13732
|
const k = (n) => `${Math.round(n / 1e3)}k`;
|
|
13733
13733
|
const modelShort = stat.model.replace(/@.*$/, "").replace(/-\d{8}$/, "").replace(/^claude-/, "");
|
|
13734
|
-
return
|
|
13734
|
+
return import_chalk29.default.dim("ctx: ") + pctColor(`${stat.fillPct}%`) + import_chalk29.default.dim(
|
|
13735
13735
|
` (${k(stat.inputTokens)}/${k(getModelContextLimit(stat.model))} out ${k(stat.outputTokens)} \xB7 ${modelShort})`
|
|
13736
13736
|
);
|
|
13737
13737
|
}
|
|
@@ -13754,11 +13754,11 @@ function agentLabel(agent, mcpServer, sessionId) {
|
|
|
13754
13754
|
const tag = sessionTag(sessionId);
|
|
13755
13755
|
const tagSuffix = tag ? `\xB7${tag}` : "";
|
|
13756
13756
|
if (!agent || agent === "Terminal") {
|
|
13757
|
-
return mcpServer ?
|
|
13757
|
+
return mcpServer ? import_chalk29.default.dim(`[\u2192 ${mcpServer}] `) : "";
|
|
13758
13758
|
}
|
|
13759
13759
|
const short = agent === "Claude Code" ? "Claude" : agent === "Gemini CLI" ? "Gemini" : agent === "Unknown Agent" ? "" : agent.split(" ")[0];
|
|
13760
|
-
if (!short) return mcpServer ?
|
|
13761
|
-
return mcpServer ?
|
|
13760
|
+
if (!short) return mcpServer ? import_chalk29.default.dim(`[\u2192 ${mcpServer}] `) : "";
|
|
13761
|
+
return mcpServer ? import_chalk29.default.dim(`[${short}${tagSuffix} \u2192 ${mcpServer}] `) : import_chalk29.default.dim(`[${short}${tagSuffix}] `);
|
|
13762
13762
|
}
|
|
13763
13763
|
function formatBase(activity) {
|
|
13764
13764
|
const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
|
|
@@ -13766,20 +13766,20 @@ function formatBase(activity) {
|
|
|
13766
13766
|
const toolName = activity.tool.slice(0, 16).padEnd(16);
|
|
13767
13767
|
const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(import_os41.default.homedir(), "~");
|
|
13768
13768
|
const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
|
|
13769
|
-
return `${
|
|
13769
|
+
return `${import_chalk29.default.gray(time)} ${icon} ${agentLabel(activity.agent, activity.mcpServer, activity.sessionId)}${import_chalk29.default.white.bold(toolName)} ${import_chalk29.default.dim(argsPreview)}`;
|
|
13770
13770
|
}
|
|
13771
13771
|
function renderResult(activity, result) {
|
|
13772
13772
|
const base = formatBase(activity);
|
|
13773
13773
|
let status;
|
|
13774
13774
|
if (result.status === "allow") {
|
|
13775
|
-
status =
|
|
13775
|
+
status = import_chalk29.default.green("\u2713 ALLOW");
|
|
13776
13776
|
} else if (result.status === "dlp") {
|
|
13777
|
-
status =
|
|
13777
|
+
status = import_chalk29.default.bgRed.white.bold(" \u{1F6E1}\uFE0F DLP ");
|
|
13778
13778
|
} else {
|
|
13779
|
-
status =
|
|
13779
|
+
status = import_chalk29.default.red("\u2717 BLOCK");
|
|
13780
13780
|
}
|
|
13781
13781
|
const cost = result.costEstimate ?? activity.costEstimate;
|
|
13782
|
-
const costSuffix = cost == null ? "" :
|
|
13782
|
+
const costSuffix = cost == null ? "" : import_chalk29.default.dim(` ~$${cost >= 1e-3 ? cost.toFixed(3) : "0.000"}`);
|
|
13783
13783
|
if (process.stdout.isTTY) {
|
|
13784
13784
|
if (pendingShownForId === activity.id && pendingWrappedLines > 1) {
|
|
13785
13785
|
import_readline6.default.moveCursor(process.stdout, 0, -(pendingWrappedLines - 1));
|
|
@@ -13796,7 +13796,7 @@ function renderResult(activity, result) {
|
|
|
13796
13796
|
}
|
|
13797
13797
|
function renderPending(activity) {
|
|
13798
13798
|
if (!process.stdout.isTTY) return;
|
|
13799
|
-
const line = `${formatBase(activity)} ${
|
|
13799
|
+
const line = `${formatBase(activity)} ${import_chalk29.default.yellow("\u25CF \u2026")}`;
|
|
13800
13800
|
pendingShownForId = activity.id;
|
|
13801
13801
|
pendingWrappedLines = wrappedLineCount(line);
|
|
13802
13802
|
process.stdout.write(`${line}\r`);
|
|
@@ -13808,7 +13808,7 @@ async function ensureDaemon() {
|
|
|
13808
13808
|
const { port } = JSON.parse(import_fs45.default.readFileSync(PID_FILE, "utf-8"));
|
|
13809
13809
|
pidPort = port;
|
|
13810
13810
|
} catch {
|
|
13811
|
-
console.error(
|
|
13811
|
+
console.error(import_chalk29.default.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
|
|
13812
13812
|
}
|
|
13813
13813
|
}
|
|
13814
13814
|
const checkPort = pidPort ?? DAEMON_PORT;
|
|
@@ -13819,8 +13819,8 @@ async function ensureDaemon() {
|
|
|
13819
13819
|
if (res.ok) return checkPort;
|
|
13820
13820
|
} catch {
|
|
13821
13821
|
}
|
|
13822
|
-
console.log(
|
|
13823
|
-
const child = (0,
|
|
13822
|
+
console.log(import_chalk29.default.dim("\u{1F6E1}\uFE0F Starting Node9 daemon..."));
|
|
13823
|
+
const child = (0, import_child_process12.spawn)(process.execPath, [process.argv[1], "daemon"], {
|
|
13824
13824
|
detached: true,
|
|
13825
13825
|
stdio: "ignore",
|
|
13826
13826
|
env: { ...process.env, NODE9_AUTO_STARTED: "1" }
|
|
@@ -13836,7 +13836,7 @@ async function ensureDaemon() {
|
|
|
13836
13836
|
} catch {
|
|
13837
13837
|
}
|
|
13838
13838
|
}
|
|
13839
|
-
console.error(
|
|
13839
|
+
console.error(import_chalk29.default.red("\u274C Daemon failed to start. Try: node9 daemon start"));
|
|
13840
13840
|
process.exit(1);
|
|
13841
13841
|
}
|
|
13842
13842
|
function postDecisionHttp(id, decision, authToken, port, opts) {
|
|
@@ -13905,7 +13905,7 @@ function buildCardLines(req, localCount = 0) {
|
|
|
13905
13905
|
const severityIcon = isBlock ? `${RED}\u{1F6D1}` : `${YELLOW}\u26A0 `;
|
|
13906
13906
|
const rawDesc = req.riskMetadata?.ruleDescription ?? "";
|
|
13907
13907
|
const description = rawDesc ? cleanReason(rawDesc) : "";
|
|
13908
|
-
const agentSuffix = req.agent && req.agent !== "Terminal" ? ` ${RESET2}${
|
|
13908
|
+
const agentSuffix = req.agent && req.agent !== "Terminal" ? ` ${RESET2}${import_chalk29.default.dim(`(${req.agent})`)}` : "";
|
|
13909
13909
|
const lines = [
|
|
13910
13910
|
``,
|
|
13911
13911
|
`${BOLD2}${CYAN}\u2554\u2550\u2550 Node9 Approval Required \u2550\u2550\u2557${RESET2}`,
|
|
@@ -13974,7 +13974,7 @@ function approverStatusLine() {
|
|
|
13974
13974
|
const a = readApproversFromDisk();
|
|
13975
13975
|
const fmt = (label, key) => {
|
|
13976
13976
|
const on = a[key] !== false;
|
|
13977
|
-
return `[${key[0]}]${label.slice(1)} ${on ?
|
|
13977
|
+
return `[${key[0]}]${label.slice(1)} ${on ? import_chalk29.default.green("\u2713") : import_chalk29.default.dim("\u2717")}`;
|
|
13978
13978
|
};
|
|
13979
13979
|
return `${fmt("native", "native")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
|
|
13980
13980
|
}
|
|
@@ -14019,7 +14019,7 @@ async function startTail(options = {}) {
|
|
|
14019
14019
|
req2.end();
|
|
14020
14020
|
});
|
|
14021
14021
|
if (result.ok) {
|
|
14022
|
-
console.log(
|
|
14022
|
+
console.log(import_chalk29.default.green("\u2713 Flight Recorder buffer cleared."));
|
|
14023
14023
|
} else if (result.code === "ECONNREFUSED") {
|
|
14024
14024
|
throw new Error("Daemon is not running. Start it with: node9 daemon start");
|
|
14025
14025
|
} else if (result.code === "ETIMEDOUT") {
|
|
@@ -14065,7 +14065,7 @@ async function startTail(options = {}) {
|
|
|
14065
14065
|
const channel = name === "n" ? "native" : name === "c" ? "cloud" : name === "t" ? "terminal" : null;
|
|
14066
14066
|
if (channel) {
|
|
14067
14067
|
toggleApprover(channel);
|
|
14068
|
-
console.log(
|
|
14068
|
+
console.log(import_chalk29.default.dim(` Approvers: ${approverStatusLine()}`));
|
|
14069
14069
|
}
|
|
14070
14070
|
};
|
|
14071
14071
|
process.stdin.on("keypress", idleKeypressHandler);
|
|
@@ -14131,7 +14131,7 @@ async function startTail(options = {}) {
|
|
|
14131
14131
|
localAllowCounts.get(req2.toolName) ?? 0
|
|
14132
14132
|
)
|
|
14133
14133
|
);
|
|
14134
|
-
const decisionStamp = action === "always-allow" ?
|
|
14134
|
+
const decisionStamp = action === "always-allow" ? import_chalk29.default.yellow("\u2605 ALWAYS ALLOW") : action === "trust" ? import_chalk29.default.cyan("\u23F1 TRUST 30m") : action === "allow" ? import_chalk29.default.green("\u2713 ALLOWED") : action === "redirect" ? import_chalk29.default.yellow("\u21A9 REDIRECT AI") : import_chalk29.default.red("\u2717 DENIED");
|
|
14135
14135
|
stampedLines.push(` ${BOLD2}\u2192${RESET2} ${decisionStamp} ${GRAY}(terminal)${RESET2}`, ``);
|
|
14136
14136
|
for (const line of stampedLines) process.stdout.write(line + "\n");
|
|
14137
14137
|
process.stdout.write(SHOW_CURSOR);
|
|
@@ -14182,7 +14182,7 @@ async function startTail(options = {}) {
|
|
|
14182
14182
|
);
|
|
14183
14183
|
const stampedLines = buildCardLines(req2, priorCount);
|
|
14184
14184
|
if (externalDecision) {
|
|
14185
|
-
const source = externalDecision === "allow" ?
|
|
14185
|
+
const source = externalDecision === "allow" ? import_chalk29.default.green("\u2713 ALLOWED") : import_chalk29.default.red("\u2717 DENIED");
|
|
14186
14186
|
stampedLines.push(` ${BOLD2}\u2192${RESET2} ${source} ${GRAY}(external)${RESET2}`, ``);
|
|
14187
14187
|
}
|
|
14188
14188
|
for (const line of stampedLines) process.stdout.write(line + "\n");
|
|
@@ -14230,25 +14230,25 @@ async function startTail(options = {}) {
|
|
|
14230
14230
|
if (unackedDlp > 0) {
|
|
14231
14231
|
console.log("");
|
|
14232
14232
|
console.log(
|
|
14233
|
-
|
|
14233
|
+
import_chalk29.default.bgRed.white.bold(
|
|
14234
14234
|
` \u26A0\uFE0F DLP ALERT: ${unackedDlp} secret${unackedDlp !== 1 ? "s" : ""} found in Claude response text \u2014 run: node9 dlp `
|
|
14235
14235
|
)
|
|
14236
14236
|
);
|
|
14237
14237
|
}
|
|
14238
14238
|
} catch {
|
|
14239
14239
|
}
|
|
14240
|
-
console.log(
|
|
14240
|
+
console.log(import_chalk29.default.cyan.bold(`
|
|
14241
14241
|
\u{1F6F0}\uFE0F Node9 tail`));
|
|
14242
14242
|
if (canApprove) {
|
|
14243
|
-
console.log(
|
|
14244
|
-
console.log(
|
|
14243
|
+
console.log(import_chalk29.default.dim("Card: [\u21B5/y] Allow [n] Deny [a] Always [t] Trust 30m"));
|
|
14244
|
+
console.log(import_chalk29.default.dim(`Approvers (toggle): ${approverStatusLine()} [q] quit`));
|
|
14245
14245
|
}
|
|
14246
14246
|
const ctxStat = readSessionUsage();
|
|
14247
14247
|
if (ctxStat) console.log(" " + formatContextStat(ctxStat));
|
|
14248
14248
|
if (options.history) {
|
|
14249
|
-
console.log(
|
|
14249
|
+
console.log(import_chalk29.default.dim("Showing history + live events.\n"));
|
|
14250
14250
|
} else {
|
|
14251
|
-
console.log(
|
|
14251
|
+
console.log(import_chalk29.default.dim("Showing live events only. Use --history to include past.\n"));
|
|
14252
14252
|
}
|
|
14253
14253
|
process.on("SIGINT", () => {
|
|
14254
14254
|
exitIdleMode();
|
|
@@ -14258,7 +14258,7 @@ async function startTail(options = {}) {
|
|
|
14258
14258
|
import_readline6.default.clearLine(process.stdout, 0);
|
|
14259
14259
|
import_readline6.default.cursorTo(process.stdout, 0);
|
|
14260
14260
|
}
|
|
14261
|
-
console.log(
|
|
14261
|
+
console.log(import_chalk29.default.dim("\n\u{1F6F0}\uFE0F Disconnected."));
|
|
14262
14262
|
process.exit(0);
|
|
14263
14263
|
});
|
|
14264
14264
|
const STALL_THRESHOLD_MS = 6e4;
|
|
@@ -14270,7 +14270,7 @@ async function startTail(options = {}) {
|
|
|
14270
14270
|
if (Date.now() - auditMtime >= STALL_THRESHOLD_MS) return;
|
|
14271
14271
|
console.log("");
|
|
14272
14272
|
console.log(
|
|
14273
|
-
|
|
14273
|
+
import_chalk29.default.yellow(
|
|
14274
14274
|
"\u26A0\uFE0F Tail appears stalled \u2014 hooks are firing but no events are arriving. Try: node9 daemon restart"
|
|
14275
14275
|
)
|
|
14276
14276
|
);
|
|
@@ -14287,7 +14287,7 @@ async function startTail(options = {}) {
|
|
|
14287
14287
|
},
|
|
14288
14288
|
(res) => {
|
|
14289
14289
|
if (res.statusCode !== 200) {
|
|
14290
|
-
console.error(
|
|
14290
|
+
console.error(import_chalk29.default.red(`Failed to connect: HTTP ${res.statusCode}`));
|
|
14291
14291
|
process.exit(1);
|
|
14292
14292
|
}
|
|
14293
14293
|
if (canApprove) enterIdleMode();
|
|
@@ -14318,7 +14318,7 @@ async function startTail(options = {}) {
|
|
|
14318
14318
|
import_readline6.default.clearLine(process.stdout, 0);
|
|
14319
14319
|
import_readline6.default.cursorTo(process.stdout, 0);
|
|
14320
14320
|
}
|
|
14321
|
-
console.log(
|
|
14321
|
+
console.log(import_chalk29.default.red("\n\u274C Daemon disconnected."));
|
|
14322
14322
|
process.exit(1);
|
|
14323
14323
|
});
|
|
14324
14324
|
}
|
|
@@ -14331,7 +14331,7 @@ async function startTail(options = {}) {
|
|
|
14331
14331
|
const parsed = JSON.parse(rawData);
|
|
14332
14332
|
const msg = parsed.message ?? "Flight recorder is down \u2014 run: node9 daemon restart";
|
|
14333
14333
|
console.log("");
|
|
14334
|
-
console.log(
|
|
14334
|
+
console.log(import_chalk29.default.bgRed.white.bold(` \u26A0\uFE0F ${msg} `));
|
|
14335
14335
|
} catch {
|
|
14336
14336
|
}
|
|
14337
14337
|
return;
|
|
@@ -14416,9 +14416,9 @@ async function startTail(options = {}) {
|
|
|
14416
14416
|
const rawSummary = data.argsSummary ?? data.tool;
|
|
14417
14417
|
const summary = shortenPathSummary(rawSummary);
|
|
14418
14418
|
const fileCount = data.fileCount ?? 0;
|
|
14419
|
-
const files = fileCount > 0 ?
|
|
14419
|
+
const files = fileCount > 0 ? import_chalk29.default.dim(` \xB7 ${fileCount} file${fileCount === 1 ? "" : "s"}`) : "";
|
|
14420
14420
|
process.stdout.write(
|
|
14421
|
-
`${
|
|
14421
|
+
`${import_chalk29.default.dim(time)} ${import_chalk29.default.cyan("\u{1F4F8} snapshot")} ${import_chalk29.default.dim(hash)} ${summary}${files}
|
|
14422
14422
|
`
|
|
14423
14423
|
);
|
|
14424
14424
|
return;
|
|
@@ -14435,33 +14435,33 @@ async function startTail(options = {}) {
|
|
|
14435
14435
|
if (event === "execution-result") {
|
|
14436
14436
|
const exec = data;
|
|
14437
14437
|
const time = new Date(Date.now()).toLocaleTimeString([], { hour12: false });
|
|
14438
|
-
const arrow = exec.isError ?
|
|
14438
|
+
const arrow = exec.isError ? import_chalk29.default.red(" \u21B3 \u2717") : import_chalk29.default.green(" \u21B3 \u2713");
|
|
14439
14439
|
const label = agentLabel(exec.agent, exec.mcpServer);
|
|
14440
14440
|
const tool = (exec.tool ?? "").slice(0, 16);
|
|
14441
|
-
const duration = typeof exec.durationMs === "number" ?
|
|
14441
|
+
const duration = typeof exec.durationMs === "number" ? import_chalk29.default.dim(` (${exec.durationMs}ms)`) : "";
|
|
14442
14442
|
console.log(
|
|
14443
|
-
`${
|
|
14443
|
+
`${import_chalk29.default.gray(time)} ${arrow} ${label}${import_chalk29.default.dim(tool)}${import_chalk29.default.dim(" completed")}${duration}`
|
|
14444
14444
|
);
|
|
14445
14445
|
}
|
|
14446
14446
|
}
|
|
14447
14447
|
req.on("error", (err2) => {
|
|
14448
14448
|
const msg = err2.code === "ECONNREFUSED" ? "Daemon is not running. Start it with: node9 daemon start" : err2.message;
|
|
14449
|
-
console.error(
|
|
14449
|
+
console.error(import_chalk29.default.red(`
|
|
14450
14450
|
\u274C ${msg}`));
|
|
14451
14451
|
process.exit(1);
|
|
14452
14452
|
});
|
|
14453
14453
|
}
|
|
14454
|
-
var import_http2,
|
|
14454
|
+
var import_http2, import_chalk29, import_fs45, import_os41, import_path47, import_readline6, import_child_process12, PID_FILE, ICONS, MODEL_CONTEXT_LIMITS, RESET2, BOLD2, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, pendingShownForId, pendingWrappedLines, DIVIDER;
|
|
14455
14455
|
var init_tail = __esm({
|
|
14456
14456
|
"src/tui/tail.ts"() {
|
|
14457
14457
|
"use strict";
|
|
14458
14458
|
import_http2 = __toESM(require("http"));
|
|
14459
|
-
|
|
14459
|
+
import_chalk29 = __toESM(require("chalk"));
|
|
14460
14460
|
import_fs45 = __toESM(require("fs"));
|
|
14461
14461
|
import_os41 = __toESM(require("os"));
|
|
14462
14462
|
import_path47 = __toESM(require("path"));
|
|
14463
14463
|
import_readline6 = __toESM(require("readline"));
|
|
14464
|
-
|
|
14464
|
+
import_child_process12 = require("child_process");
|
|
14465
14465
|
init_daemon2();
|
|
14466
14466
|
init_daemon();
|
|
14467
14467
|
PID_FILE = import_path47.default.join(import_os41.default.homedir(), ".node9", "daemon.pid");
|
|
@@ -14889,7 +14889,7 @@ var import_commander = require("commander");
|
|
|
14889
14889
|
init_core();
|
|
14890
14890
|
init_setup();
|
|
14891
14891
|
init_daemon2();
|
|
14892
|
-
var
|
|
14892
|
+
var import_chalk30 = __toESM(require("chalk"));
|
|
14893
14893
|
var import_fs47 = __toESM(require("fs"));
|
|
14894
14894
|
var import_path49 = __toESM(require("path"));
|
|
14895
14895
|
var import_os43 = __toESM(require("os"));
|
|
@@ -15683,7 +15683,7 @@ function detectAiAgent(payload) {
|
|
|
15683
15683
|
return "Terminal";
|
|
15684
15684
|
}
|
|
15685
15685
|
function registerCheckCommand(program2) {
|
|
15686
|
-
program2.command("check").description("Hook handler \u2014 evaluates a tool call before execution").argument("[data]", "JSON string of the tool call").action(async (data) => {
|
|
15686
|
+
program2.command("check", { hidden: true }).description("Hook handler \u2014 evaluates a tool call before execution").argument("[data]", "JSON string of the tool call").action(async (data) => {
|
|
15687
15687
|
const processPayload = async (raw) => {
|
|
15688
15688
|
try {
|
|
15689
15689
|
if (!raw || raw.trim() === "") process.exit(0);
|
|
@@ -16081,7 +16081,7 @@ function sanitize3(value) {
|
|
|
16081
16081
|
return value.replace(/[\x00-\x1F\x7F]/g, "");
|
|
16082
16082
|
}
|
|
16083
16083
|
function registerLogCommand(program2) {
|
|
16084
|
-
program2.command("log").description("PostToolUse hook \u2014 records executed tool calls").argument("[data]", "JSON string of the tool call").action(async (data) => {
|
|
16084
|
+
program2.command("log", { hidden: true }).description("PostToolUse hook \u2014 records executed tool calls").argument("[data]", "JSON string of the tool call").action(async (data) => {
|
|
16085
16085
|
const logPayload = async (raw) => {
|
|
16086
16086
|
try {
|
|
16087
16087
|
if (!raw || raw.trim() === "") process.exit(0);
|
|
@@ -16834,6 +16834,35 @@ function isTestEntry(entry, testTs) {
|
|
|
16834
16834
|
}
|
|
16835
16835
|
return false;
|
|
16836
16836
|
}
|
|
16837
|
+
var SUPERSEDE_WINDOW_MS = 6e4;
|
|
16838
|
+
function buildSupersededSet(entries) {
|
|
16839
|
+
const superseded = /* @__PURE__ */ new Set();
|
|
16840
|
+
for (let i = 0; i < entries.length; i++) {
|
|
16841
|
+
const e = entries[i];
|
|
16842
|
+
if (e.decision !== "deny") continue;
|
|
16843
|
+
if (e.checkedBy !== "smart-rule-block-override") continue;
|
|
16844
|
+
if (!e.argsHash || !e.sessionId) continue;
|
|
16845
|
+
const eTs = Date.parse(e.ts);
|
|
16846
|
+
if (Number.isNaN(eTs)) continue;
|
|
16847
|
+
for (let j = i + 1; j < entries.length; j++) {
|
|
16848
|
+
const next = entries[j];
|
|
16849
|
+
const nextTs = Date.parse(next.ts);
|
|
16850
|
+
if (Number.isNaN(nextTs)) continue;
|
|
16851
|
+
if (nextTs - eTs > SUPERSEDE_WINDOW_MS) break;
|
|
16852
|
+
if (next.argsHash !== e.argsHash) continue;
|
|
16853
|
+
if (next.sessionId !== e.sessionId) continue;
|
|
16854
|
+
if (next.tool !== e.tool) continue;
|
|
16855
|
+
if (next.decision === "allow" && next.checkedBy === "daemon") {
|
|
16856
|
+
superseded.add(`${e.ts}|${e.argsHash}`);
|
|
16857
|
+
break;
|
|
16858
|
+
}
|
|
16859
|
+
}
|
|
16860
|
+
}
|
|
16861
|
+
return superseded;
|
|
16862
|
+
}
|
|
16863
|
+
function supersedeKey(e) {
|
|
16864
|
+
return `${e.ts}|${e.argsHash ?? ""}`;
|
|
16865
|
+
}
|
|
16837
16866
|
function getDateRange(period, now) {
|
|
16838
16867
|
const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
16839
16868
|
const end = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
|
|
@@ -17285,6 +17314,7 @@ function aggregateReportFromAudit(period, opts = {}) {
|
|
|
17285
17314
|
}
|
|
17286
17315
|
return true;
|
|
17287
17316
|
});
|
|
17317
|
+
const superseded = buildSupersededSet(entries);
|
|
17288
17318
|
let userApproved = 0;
|
|
17289
17319
|
let userDenied = 0;
|
|
17290
17320
|
let timedOut = 0;
|
|
@@ -17302,6 +17332,7 @@ function aggregateReportFromAudit(period, opts = {}) {
|
|
|
17302
17332
|
const dailyMap = /* @__PURE__ */ new Map();
|
|
17303
17333
|
const hourMap = /* @__PURE__ */ new Map();
|
|
17304
17334
|
for (const e of entries) {
|
|
17335
|
+
if (superseded.has(supersedeKey(e))) continue;
|
|
17305
17336
|
const allow = isAllow(e.decision);
|
|
17306
17337
|
const dateKey = e.ts.slice(0, 10);
|
|
17307
17338
|
const userInteracted = e.source === "daemon";
|
|
@@ -17312,6 +17343,7 @@ function aggregateReportFromAudit(period, opts = {}) {
|
|
|
17312
17343
|
if (e.checkedBy === "timeout") timedOut++;
|
|
17313
17344
|
else if (e.checkedBy === "observe-mode-dlp-would-block") observeDlp++;
|
|
17314
17345
|
else if (isDlp(e.checkedBy)) dlpBlocked++;
|
|
17346
|
+
else if (e.checkedBy === "local-decision") userDenied++;
|
|
17315
17347
|
else if (e.checkedBy !== "loop-detected") hardBlocked++;
|
|
17316
17348
|
}
|
|
17317
17349
|
if (e.checkedBy === "loop-detected") loopHits++;
|
|
@@ -17319,8 +17351,11 @@ function aggregateReportFromAudit(period, opts = {}) {
|
|
|
17319
17351
|
t.calls++;
|
|
17320
17352
|
if (!allow) t.blocked++;
|
|
17321
17353
|
toolMap.set(e.tool, t);
|
|
17322
|
-
if (!allow
|
|
17323
|
-
|
|
17354
|
+
if (!allow) {
|
|
17355
|
+
const key = e.checkedBy ?? (e.source === "daemon" ? "local-decision" : null);
|
|
17356
|
+
if (key) {
|
|
17357
|
+
blockMap.set(key, (blockMap.get(key) ?? 0) + 1);
|
|
17358
|
+
}
|
|
17324
17359
|
}
|
|
17325
17360
|
if (!allow && e.ruleName) {
|
|
17326
17361
|
ruleMap.set(e.ruleName, (ruleMap.get(e.ruleName) ?? 0) + 1);
|
|
@@ -17349,7 +17384,9 @@ function aggregateReportFromAudit(period, opts = {}) {
|
|
|
17349
17384
|
start,
|
|
17350
17385
|
end,
|
|
17351
17386
|
excludedTests,
|
|
17352
|
-
|
|
17387
|
+
// Subtract superseded rows so the headline event count agrees with
|
|
17388
|
+
// the bucket counters (which skip them in the loop above).
|
|
17389
|
+
total: entries.length - superseded.size,
|
|
17353
17390
|
userApproved,
|
|
17354
17391
|
userDenied,
|
|
17355
17392
|
timedOut,
|
|
@@ -18247,7 +18284,7 @@ function registerInitCommand(program2) {
|
|
|
18247
18284
|
const agentList = found.join(", ");
|
|
18248
18285
|
console.log(import_chalk16.default.green.bold(`\u{1F6E1}\uFE0F Node9 is protecting ${agentList}!`));
|
|
18249
18286
|
console.log("");
|
|
18250
|
-
console.log(import_chalk16.default.white(" Watch live: ") + import_chalk16.default.cyan("node9
|
|
18287
|
+
console.log(import_chalk16.default.white(" Watch live: ") + import_chalk16.default.cyan("node9 monitor"));
|
|
18251
18288
|
console.log("");
|
|
18252
18289
|
console.log(import_chalk16.default.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
18253
18290
|
console.log(
|
|
@@ -18544,71 +18581,10 @@ function registerUndoCommand(program2) {
|
|
|
18544
18581
|
});
|
|
18545
18582
|
}
|
|
18546
18583
|
|
|
18547
|
-
// src/cli/commands/watch.ts
|
|
18548
|
-
var import_chalk19 = __toESM(require("chalk"));
|
|
18549
|
-
var import_child_process10 = require("child_process");
|
|
18550
|
-
init_daemon();
|
|
18551
|
-
function registerWatchCommand(program2) {
|
|
18552
|
-
program2.command("watch").description("Run a command under Node9 watch mode (daemon stays alive for the session)").argument("<command>", "Command to run").argument("[args...]", "Arguments for the command").action(async (cmd, args) => {
|
|
18553
|
-
let port = DAEMON_PORT;
|
|
18554
|
-
try {
|
|
18555
|
-
const res = await fetch(`http://127.0.0.1:${DAEMON_PORT}/settings`, {
|
|
18556
|
-
signal: AbortSignal.timeout(500)
|
|
18557
|
-
});
|
|
18558
|
-
if (res.ok) {
|
|
18559
|
-
const data = await res.json();
|
|
18560
|
-
if (typeof data.port === "number") port = data.port;
|
|
18561
|
-
} else {
|
|
18562
|
-
throw new Error("not running");
|
|
18563
|
-
}
|
|
18564
|
-
} catch {
|
|
18565
|
-
console.error(import_chalk19.default.dim("\u{1F6E1}\uFE0F Starting Node9 daemon (watch mode)..."));
|
|
18566
|
-
const child = (0, import_child_process10.spawn)(process.execPath, [process.argv[1], "daemon"], {
|
|
18567
|
-
detached: true,
|
|
18568
|
-
stdio: "ignore",
|
|
18569
|
-
env: { ...process.env, NODE9_AUTO_STARTED: "1", NODE9_WATCH_MODE: "1" }
|
|
18570
|
-
});
|
|
18571
|
-
child.unref();
|
|
18572
|
-
let ready = false;
|
|
18573
|
-
for (let i = 0; i < 20; i++) {
|
|
18574
|
-
await new Promise((r) => setTimeout(r, 250));
|
|
18575
|
-
try {
|
|
18576
|
-
const r = await fetch(`http://127.0.0.1:${DAEMON_PORT}/settings`, {
|
|
18577
|
-
signal: AbortSignal.timeout(500)
|
|
18578
|
-
});
|
|
18579
|
-
if (r.ok) {
|
|
18580
|
-
ready = true;
|
|
18581
|
-
break;
|
|
18582
|
-
}
|
|
18583
|
-
} catch {
|
|
18584
|
-
}
|
|
18585
|
-
}
|
|
18586
|
-
if (!ready) {
|
|
18587
|
-
console.error(import_chalk19.default.red("\u274C Daemon failed to start. Try: node9 daemon start"));
|
|
18588
|
-
process.exit(1);
|
|
18589
|
-
}
|
|
18590
|
-
}
|
|
18591
|
-
console.error(
|
|
18592
|
-
import_chalk19.default.cyan.bold("\u{1F6E1}\uFE0F Node9 watch") + import_chalk19.default.dim(` \u2192 localhost:${port}`) + import_chalk19.default.dim(
|
|
18593
|
-
"\n Tip: run `node9 tail` in another terminal to review and approve AI actions.\n"
|
|
18594
|
-
)
|
|
18595
|
-
);
|
|
18596
|
-
const result = (0, import_child_process10.spawnSync)(cmd, args, {
|
|
18597
|
-
stdio: "inherit",
|
|
18598
|
-
env: { ...process.env, NODE9_WATCH_MODE: "1" }
|
|
18599
|
-
});
|
|
18600
|
-
if (result.error) {
|
|
18601
|
-
console.error(import_chalk19.default.red(`\u274C Failed to run command: ${result.error.message}`));
|
|
18602
|
-
process.exit(1);
|
|
18603
|
-
}
|
|
18604
|
-
process.exit(result.status ?? 0);
|
|
18605
|
-
});
|
|
18606
|
-
}
|
|
18607
|
-
|
|
18608
18584
|
// src/mcp-gateway/index.ts
|
|
18609
18585
|
var import_readline4 = __toESM(require("readline"));
|
|
18610
|
-
var
|
|
18611
|
-
var
|
|
18586
|
+
var import_chalk19 = __toESM(require("chalk"));
|
|
18587
|
+
var import_child_process10 = require("child_process");
|
|
18612
18588
|
var import_execa3 = require("execa");
|
|
18613
18589
|
init_orchestrator();
|
|
18614
18590
|
init_provenance();
|
|
@@ -18769,13 +18745,13 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
18769
18745
|
const prov = checkProvenance(executable);
|
|
18770
18746
|
if (prov.trustLevel === "suspect") {
|
|
18771
18747
|
console.error(
|
|
18772
|
-
|
|
18748
|
+
import_chalk19.default.red(
|
|
18773
18749
|
`\u26A0\uFE0F Node9: Upstream MCP server binary is suspect \u2014 ${prov.reason} (${prov.resolvedPath})`
|
|
18774
18750
|
)
|
|
18775
18751
|
);
|
|
18776
|
-
console.error(
|
|
18752
|
+
console.error(import_chalk19.default.red(" Verify this binary is trusted before proceeding."));
|
|
18777
18753
|
}
|
|
18778
|
-
console.error(
|
|
18754
|
+
console.error(import_chalk19.default.green(`\u{1F680} Node9 MCP Gateway: Monitoring [${upstreamCommand}]`));
|
|
18779
18755
|
const UPSTREAM_INJECTOR_VARS = /* @__PURE__ */ new Set([
|
|
18780
18756
|
"NODE_OPTIONS",
|
|
18781
18757
|
"NODE_PATH",
|
|
@@ -18794,7 +18770,7 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
18794
18770
|
const safeEnv = Object.fromEntries(
|
|
18795
18771
|
Object.entries(process.env).filter(([k]) => !UPSTREAM_INJECTOR_VARS.has(k))
|
|
18796
18772
|
);
|
|
18797
|
-
const child = (0,
|
|
18773
|
+
const child = (0, import_child_process10.spawn)(executable, cmdArgs, {
|
|
18798
18774
|
stdio: ["pipe", "pipe", "inherit"],
|
|
18799
18775
|
// control stdin/stdout; inherit stderr
|
|
18800
18776
|
shell: false,
|
|
@@ -18888,10 +18864,10 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
18888
18864
|
mcpServer
|
|
18889
18865
|
});
|
|
18890
18866
|
if (!result.approved) {
|
|
18891
|
-
console.error(
|
|
18867
|
+
console.error(import_chalk19.default.red(`
|
|
18892
18868
|
\u{1F6D1} Node9 MCP Gateway: Action Blocked`));
|
|
18893
|
-
console.error(
|
|
18894
|
-
console.error(
|
|
18869
|
+
console.error(import_chalk19.default.gray(` Tool: ${toolName}`));
|
|
18870
|
+
console.error(import_chalk19.default.gray(` Reason: ${result.reason ?? "Security Policy"}
|
|
18895
18871
|
`));
|
|
18896
18872
|
const blockedByLabel = result.blockedByLabel ?? result.reason ?? "Security Policy";
|
|
18897
18873
|
const isHumanDecision = blockedByLabel.toLowerCase().includes("user") || blockedByLabel.toLowerCase().includes("daemon") || blockedByLabel.toLowerCase().includes("decision");
|
|
@@ -19003,7 +18979,7 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
19003
18979
|
updatePin(serverKey, upstreamCommand, currentHash, toolNames);
|
|
19004
18980
|
pinState = "validated";
|
|
19005
18981
|
console.error(
|
|
19006
|
-
|
|
18982
|
+
import_chalk19.default.green(
|
|
19007
18983
|
`\u{1F512} Node9: Pinned ${toolNames.length} tool definition(s) for this MCP server`
|
|
19008
18984
|
)
|
|
19009
18985
|
);
|
|
@@ -19016,11 +18992,11 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
19016
18992
|
} else if (pinStatus === "corrupt") {
|
|
19017
18993
|
pinState = "quarantined";
|
|
19018
18994
|
console.error(
|
|
19019
|
-
|
|
18995
|
+
import_chalk19.default.red("\n\u{1F6A8} Node9: MCP pin file is corrupt or unreadable \u2014 session quarantined!")
|
|
19020
18996
|
);
|
|
19021
|
-
console.error(
|
|
18997
|
+
console.error(import_chalk19.default.red(" Tool calls are blocked until the pin file is repaired."));
|
|
19022
18998
|
console.error(
|
|
19023
|
-
|
|
18999
|
+
import_chalk19.default.yellow(` Run: node9 mcp pin reset (to clear and re-pin on next connect)
|
|
19024
19000
|
`)
|
|
19025
19001
|
);
|
|
19026
19002
|
const errorResponse = {
|
|
@@ -19037,13 +19013,13 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
19037
19013
|
} else {
|
|
19038
19014
|
pinState = "quarantined";
|
|
19039
19015
|
console.error(
|
|
19040
|
-
|
|
19016
|
+
import_chalk19.default.red("\n\u{1F6A8} Node9: MCP tool definitions have changed since last verified!")
|
|
19041
19017
|
);
|
|
19042
19018
|
console.error(
|
|
19043
|
-
|
|
19019
|
+
import_chalk19.default.red(" This could indicate a supply chain attack (tool poisoning / rug pull).")
|
|
19044
19020
|
);
|
|
19045
|
-
console.error(
|
|
19046
|
-
console.error(
|
|
19021
|
+
console.error(import_chalk19.default.red(" Session quarantined \u2014 all tool calls blocked."));
|
|
19022
|
+
console.error(import_chalk19.default.yellow(` Run: node9 mcp pin update ${serverKey}
|
|
19047
19023
|
`));
|
|
19048
19024
|
const errorResponse = {
|
|
19049
19025
|
jsonrpc: "2.0",
|
|
@@ -19086,7 +19062,7 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
19086
19062
|
const toolName = callId !== void 0 ? pendingCallNames.get(callId) ?? "unknown" : "unknown";
|
|
19087
19063
|
if (callId !== void 0) pendingCallNames.delete(callId);
|
|
19088
19064
|
console.error(
|
|
19089
|
-
|
|
19065
|
+
import_chalk19.default.yellow(
|
|
19090
19066
|
`\u26A1 Node9: Large MCP response from '${toolName}' (${(line.length / 1024).toFixed(0)}KB) \u2014 context window enlarged`
|
|
19091
19067
|
)
|
|
19092
19068
|
);
|
|
@@ -19140,7 +19116,7 @@ var import_readline5 = __toESM(require("readline"));
|
|
|
19140
19116
|
var import_fs39 = __toESM(require("fs"));
|
|
19141
19117
|
var import_os35 = __toESM(require("os"));
|
|
19142
19118
|
var import_path41 = __toESM(require("path"));
|
|
19143
|
-
var
|
|
19119
|
+
var import_child_process11 = require("child_process");
|
|
19144
19120
|
init_core();
|
|
19145
19121
|
init_daemon();
|
|
19146
19122
|
init_shields();
|
|
@@ -19614,7 +19590,7 @@ function handleRuleAdd(args) {
|
|
|
19614
19590
|
return `Rule "${name}" added to ~/.node9/config.json \u2014 verdict: ${verdict} when ${field} matches "${pattern}"`;
|
|
19615
19591
|
}
|
|
19616
19592
|
function runCliCommand(subArgs) {
|
|
19617
|
-
const result = (0,
|
|
19593
|
+
const result = (0, import_child_process11.spawnSync)(process.execPath, [process.argv[1], ...subArgs], {
|
|
19618
19594
|
encoding: "utf-8",
|
|
19619
19595
|
timeout: 6e4,
|
|
19620
19596
|
// Disable colors — stdout is piped (not a TTY), chalk auto-detects, but be explicit
|
|
@@ -19808,7 +19784,7 @@ function registerMcpServerCommand(program2) {
|
|
|
19808
19784
|
}
|
|
19809
19785
|
|
|
19810
19786
|
// src/cli/commands/trust.ts
|
|
19811
|
-
var
|
|
19787
|
+
var import_chalk20 = __toESM(require("chalk"));
|
|
19812
19788
|
init_trusted_hosts();
|
|
19813
19789
|
function isValidHost(host) {
|
|
19814
19790
|
return /^(\*\.)?[a-z0-9][a-z0-9.-]*\.[a-z]{2,}$/.test(host);
|
|
@@ -19819,51 +19795,51 @@ function registerTrustCommand(program2) {
|
|
|
19819
19795
|
const normalized = normalizeHost(host.trim());
|
|
19820
19796
|
if (!isValidHost(normalized)) {
|
|
19821
19797
|
console.error(
|
|
19822
|
-
|
|
19798
|
+
import_chalk20.default.red(`
|
|
19823
19799
|
\u274C Invalid host: "${host}"
|
|
19824
|
-
`) +
|
|
19800
|
+
`) + import_chalk20.default.gray(" Use an FQDN like api.mycompany.com or *.mycompany.com\n")
|
|
19825
19801
|
);
|
|
19826
19802
|
process.exit(1);
|
|
19827
19803
|
}
|
|
19828
19804
|
addTrustedHost(normalized);
|
|
19829
|
-
console.log(
|
|
19805
|
+
console.log(import_chalk20.default.green(`
|
|
19830
19806
|
\u2705 ${normalized} added to trusted hosts.`));
|
|
19831
19807
|
console.log(
|
|
19832
|
-
|
|
19808
|
+
import_chalk20.default.gray(" Pipe-chain blocks to this host: critical \u2192 review, high \u2192 allow\n")
|
|
19833
19809
|
);
|
|
19834
19810
|
});
|
|
19835
19811
|
trustCmd.command("remove <host>").description("Remove a trusted host").action((host) => {
|
|
19836
19812
|
const normalized = normalizeHost(host.trim());
|
|
19837
19813
|
const removed = removeTrustedHost(normalized);
|
|
19838
19814
|
if (!removed) {
|
|
19839
|
-
console.error(
|
|
19815
|
+
console.error(import_chalk20.default.yellow(`
|
|
19840
19816
|
\u26A0\uFE0F "${normalized}" is not in the trusted hosts list.
|
|
19841
19817
|
`));
|
|
19842
19818
|
process.exit(1);
|
|
19843
19819
|
}
|
|
19844
|
-
console.log(
|
|
19820
|
+
console.log(import_chalk20.default.green(`
|
|
19845
19821
|
\u2705 ${normalized} removed from trusted hosts.
|
|
19846
19822
|
`));
|
|
19847
19823
|
});
|
|
19848
19824
|
trustCmd.command("list").description("Show all trusted hosts").action(() => {
|
|
19849
19825
|
const hosts = readTrustedHosts();
|
|
19850
19826
|
if (hosts.length === 0) {
|
|
19851
|
-
console.log(
|
|
19852
|
-
console.log(` Add one: ${
|
|
19827
|
+
console.log(import_chalk20.default.gray("\n No trusted hosts configured.\n"));
|
|
19828
|
+
console.log(` Add one: ${import_chalk20.default.cyan("node9 trust add api.mycompany.com")}
|
|
19853
19829
|
`);
|
|
19854
19830
|
return;
|
|
19855
19831
|
}
|
|
19856
|
-
console.log(
|
|
19832
|
+
console.log(import_chalk20.default.bold("\n\u{1F513} Trusted Hosts\n"));
|
|
19857
19833
|
for (const entry of hosts) {
|
|
19858
19834
|
const date = new Date(entry.addedAt).toLocaleDateString();
|
|
19859
|
-
console.log(` ${
|
|
19835
|
+
console.log(` ${import_chalk20.default.cyan(entry.host.padEnd(40))} ${import_chalk20.default.gray(`added ${date}`)}`);
|
|
19860
19836
|
}
|
|
19861
19837
|
console.log("");
|
|
19862
19838
|
});
|
|
19863
19839
|
}
|
|
19864
19840
|
|
|
19865
19841
|
// src/cli/commands/mcp-pin.ts
|
|
19866
|
-
var
|
|
19842
|
+
var import_chalk21 = __toESM(require("chalk"));
|
|
19867
19843
|
function registerMcpPinCommand(program2) {
|
|
19868
19844
|
const pinCmd = program2.command("mcp").description("Manage MCP server tool definition pinning (rug pull defense)");
|
|
19869
19845
|
const pinSubCmd = pinCmd.command("pin").description("Manage pinned MCP server tool definitions");
|
|
@@ -19871,31 +19847,31 @@ function registerMcpPinCommand(program2) {
|
|
|
19871
19847
|
const result = readMcpPinsSafe();
|
|
19872
19848
|
if (!result.ok) {
|
|
19873
19849
|
if (result.reason === "missing") {
|
|
19874
|
-
console.log(
|
|
19850
|
+
console.log(import_chalk21.default.gray("\nNo MCP servers are pinned yet."));
|
|
19875
19851
|
console.log(
|
|
19876
|
-
|
|
19852
|
+
import_chalk21.default.gray("Pins are created automatically when the MCP gateway first connects.\n")
|
|
19877
19853
|
);
|
|
19878
19854
|
return;
|
|
19879
19855
|
}
|
|
19880
|
-
console.error(
|
|
19856
|
+
console.error(import_chalk21.default.red(`
|
|
19881
19857
|
\u274C Pin file is corrupt: ${result.detail}`));
|
|
19882
|
-
console.error(
|
|
19858
|
+
console.error(import_chalk21.default.yellow(" Run: node9 mcp pin reset\n"));
|
|
19883
19859
|
process.exit(1);
|
|
19884
19860
|
}
|
|
19885
19861
|
const entries = Object.entries(result.pins.servers);
|
|
19886
19862
|
if (entries.length === 0) {
|
|
19887
|
-
console.log(
|
|
19863
|
+
console.log(import_chalk21.default.gray("\nNo MCP servers are pinned yet."));
|
|
19888
19864
|
console.log(
|
|
19889
|
-
|
|
19865
|
+
import_chalk21.default.gray("Pins are created automatically when the MCP gateway first connects.\n")
|
|
19890
19866
|
);
|
|
19891
19867
|
return;
|
|
19892
19868
|
}
|
|
19893
|
-
console.log(
|
|
19869
|
+
console.log(import_chalk21.default.bold("\n\u{1F512} Pinned MCP Servers\n"));
|
|
19894
19870
|
for (const [key, entry] of entries) {
|
|
19895
|
-
console.log(` ${
|
|
19896
|
-
console.log(` Tools (${entry.toolCount}): ${
|
|
19897
|
-
console.log(` Hash: ${
|
|
19898
|
-
console.log(` Pinned: ${
|
|
19871
|
+
console.log(` ${import_chalk21.default.cyan(key)} ${import_chalk21.default.gray(entry.label)}`);
|
|
19872
|
+
console.log(` Tools (${entry.toolCount}): ${import_chalk21.default.white(entry.toolNames.join(", "))}`);
|
|
19873
|
+
console.log(` Hash: ${import_chalk21.default.gray(entry.toolsHash.slice(0, 16))}...`);
|
|
19874
|
+
console.log(` Pinned: ${import_chalk21.default.gray(entry.pinnedAt)}`);
|
|
19899
19875
|
console.log("");
|
|
19900
19876
|
}
|
|
19901
19877
|
});
|
|
@@ -19906,127 +19882,127 @@ function registerMcpPinCommand(program2) {
|
|
|
19906
19882
|
try {
|
|
19907
19883
|
pins = readMcpPins();
|
|
19908
19884
|
} catch {
|
|
19909
|
-
console.error(
|
|
19910
|
-
console.error(
|
|
19885
|
+
console.error(import_chalk21.default.red("\n\u274C Pin file is corrupt."));
|
|
19886
|
+
console.error(import_chalk21.default.yellow(" Run: node9 mcp pin reset\n"));
|
|
19911
19887
|
process.exit(1);
|
|
19912
19888
|
}
|
|
19913
19889
|
if (!pins.servers[serverKey]) {
|
|
19914
|
-
console.error(
|
|
19890
|
+
console.error(import_chalk21.default.red(`
|
|
19915
19891
|
\u274C No pin found for server key "${serverKey}"
|
|
19916
19892
|
`));
|
|
19917
|
-
console.error(`Run ${
|
|
19893
|
+
console.error(`Run ${import_chalk21.default.cyan("node9 mcp pin list")} to see pinned servers.
|
|
19918
19894
|
`);
|
|
19919
19895
|
process.exit(1);
|
|
19920
19896
|
}
|
|
19921
19897
|
const label = pins.servers[serverKey].label;
|
|
19922
19898
|
removePin2(serverKey);
|
|
19923
|
-
console.log(
|
|
19924
|
-
\u{1F513} Pin removed for ${
|
|
19925
|
-
console.log(
|
|
19926
|
-
console.log(
|
|
19899
|
+
console.log(import_chalk21.default.green(`
|
|
19900
|
+
\u{1F513} Pin removed for ${import_chalk21.default.cyan(serverKey)}`));
|
|
19901
|
+
console.log(import_chalk21.default.gray(` Server: ${label}`));
|
|
19902
|
+
console.log(import_chalk21.default.gray(" Next connection will re-pin with current tool definitions.\n"));
|
|
19927
19903
|
});
|
|
19928
19904
|
pinSubCmd.command("reset").description("Clear all MCP pins (next connection to each server will re-pin)").action(() => {
|
|
19929
19905
|
const result = readMcpPinsSafe();
|
|
19930
19906
|
if (!result.ok && result.reason === "missing") {
|
|
19931
|
-
console.log(
|
|
19907
|
+
console.log(import_chalk21.default.gray("\nNo pins to clear.\n"));
|
|
19932
19908
|
return;
|
|
19933
19909
|
}
|
|
19934
19910
|
const count = result.ok ? Object.keys(result.pins.servers).length : "?";
|
|
19935
19911
|
clearAllPins2();
|
|
19936
|
-
console.log(
|
|
19912
|
+
console.log(import_chalk21.default.green(`
|
|
19937
19913
|
\u{1F513} Cleared ${count} MCP pin(s).`));
|
|
19938
|
-
console.log(
|
|
19914
|
+
console.log(import_chalk21.default.gray(" Next connection to each server will re-pin.\n"));
|
|
19939
19915
|
});
|
|
19940
19916
|
}
|
|
19941
19917
|
|
|
19942
19918
|
// src/cli/commands/sync.ts
|
|
19943
|
-
var
|
|
19919
|
+
var import_chalk22 = __toESM(require("chalk"));
|
|
19944
19920
|
init_sync();
|
|
19945
19921
|
function registerSyncCommand(program2) {
|
|
19946
19922
|
const policy = program2.command("policy").description("Manage cloud policy rules");
|
|
19947
19923
|
policy.command("sync").description("Sync cloud policy rules to local cache (~/.node9/rules-cache.json)").action(async () => {
|
|
19948
|
-
process.stdout.write(
|
|
19924
|
+
process.stdout.write(import_chalk22.default.cyan("Syncing cloud policy rules\u2026"));
|
|
19949
19925
|
const result = await runCloudSync();
|
|
19950
19926
|
process.stdout.write("\n");
|
|
19951
19927
|
if (!result.ok) {
|
|
19952
|
-
console.error(
|
|
19928
|
+
console.error(import_chalk22.default.red(`\u2717 ${result.reason}`));
|
|
19953
19929
|
process.exit(1);
|
|
19954
19930
|
}
|
|
19955
19931
|
if (result.unchanged) {
|
|
19956
19932
|
console.log(
|
|
19957
|
-
|
|
19933
|
+
import_chalk22.default.green(
|
|
19958
19934
|
`\u2713 Already up to date \u2014 ${result.rules} rule${result.rules === 1 ? "" : "s"} cached`
|
|
19959
19935
|
)
|
|
19960
19936
|
);
|
|
19961
|
-
console.log(
|
|
19962
|
-
console.log(
|
|
19937
|
+
console.log(import_chalk22.default.gray(` Cached at: ${result.fetchedAt}`));
|
|
19938
|
+
console.log(import_chalk22.default.gray(` Server returned 304 (no changes since last sync)`));
|
|
19963
19939
|
} else {
|
|
19964
19940
|
console.log(
|
|
19965
|
-
|
|
19941
|
+
import_chalk22.default.green(`\u2713 Synced ${result.rules} rule${result.rules === 1 ? "" : "s"} from cloud`)
|
|
19966
19942
|
);
|
|
19967
|
-
console.log(
|
|
19968
|
-
console.log(
|
|
19943
|
+
console.log(import_chalk22.default.gray(` Cached at: ${result.fetchedAt}`));
|
|
19944
|
+
console.log(import_chalk22.default.gray(` File: ~/.node9/rules-cache.json`));
|
|
19969
19945
|
}
|
|
19970
19946
|
});
|
|
19971
19947
|
policy.command("show").description("List all cloud policy rules in the local cache").action(() => {
|
|
19972
19948
|
const status = getCloudSyncStatus();
|
|
19973
19949
|
if (!status.cached) {
|
|
19974
|
-
console.log(
|
|
19950
|
+
console.log(import_chalk22.default.yellow("\n No cloud rules cached \u2014 run: node9 policy sync\n"));
|
|
19975
19951
|
return;
|
|
19976
19952
|
}
|
|
19977
19953
|
const rules = getCloudRules() ?? [];
|
|
19978
19954
|
const age = Math.round((Date.now() - new Date(status.fetchedAt).getTime()) / 6e4);
|
|
19979
19955
|
console.log(
|
|
19980
|
-
|
|
19981
|
-
Cloud policy rules`) +
|
|
19956
|
+
import_chalk22.default.bold(`
|
|
19957
|
+
Cloud policy rules`) + import_chalk22.default.gray(
|
|
19982
19958
|
` (${rules.length} rule${rules.length === 1 ? "" : "s"}, synced ${age}m ago)
|
|
19983
19959
|
`
|
|
19984
19960
|
)
|
|
19985
19961
|
);
|
|
19986
19962
|
if (rules.length === 0) {
|
|
19987
|
-
console.log(
|
|
19963
|
+
console.log(import_chalk22.default.gray(" No rules defined in cloud policy.\n"));
|
|
19988
19964
|
return;
|
|
19989
19965
|
}
|
|
19990
19966
|
for (const rule of rules) {
|
|
19991
19967
|
const r = rule;
|
|
19992
|
-
const verdictColor = r.verdict === "block" ?
|
|
19968
|
+
const verdictColor = r.verdict === "block" ? import_chalk22.default.red : r.verdict === "allow" ? import_chalk22.default.green : import_chalk22.default.yellow;
|
|
19993
19969
|
console.log(
|
|
19994
19970
|
` ${verdictColor(
|
|
19995
19971
|
String(r.verdict ?? "unknown").toUpperCase().padEnd(6)
|
|
19996
|
-
)} ${
|
|
19972
|
+
)} ${import_chalk22.default.white(String(r.name ?? "(unnamed)"))}`
|
|
19997
19973
|
);
|
|
19998
|
-
if (r.reason) console.log(
|
|
19974
|
+
if (r.reason) console.log(import_chalk22.default.gray(` ${String(r.reason)}`));
|
|
19999
19975
|
}
|
|
20000
19976
|
console.log("");
|
|
20001
19977
|
});
|
|
20002
19978
|
policy.command("status").description("Show current cloud policy cache status").action(() => {
|
|
20003
19979
|
const s = getCloudSyncStatus();
|
|
20004
19980
|
if (!s.cached) {
|
|
20005
|
-
console.log(
|
|
19981
|
+
console.log(import_chalk22.default.yellow("\n No cache yet \u2014 run: node9 policy sync\n"));
|
|
20006
19982
|
return;
|
|
20007
19983
|
}
|
|
20008
19984
|
const age = Math.round((Date.now() - new Date(s.fetchedAt).getTime()) / 6e4);
|
|
20009
19985
|
console.log(`
|
|
20010
|
-
Rules : ${
|
|
19986
|
+
Rules : ${import_chalk22.default.green(String(s.rules))} cloud rules loaded`);
|
|
20011
19987
|
console.log(
|
|
20012
|
-
` Synced : ${
|
|
19988
|
+
` Synced : ${import_chalk22.default.gray(`${age} minute${age === 1 ? "" : "s"} ago`)} (${s.fetchedAt})`
|
|
20013
19989
|
);
|
|
20014
19990
|
if (s.workspaceId) {
|
|
20015
|
-
console.log(` Workspace: ${
|
|
19991
|
+
console.log(` Workspace: ${import_chalk22.default.gray(s.workspaceId)}`);
|
|
20016
19992
|
}
|
|
20017
19993
|
if (s.panicMode) {
|
|
20018
19994
|
console.log(
|
|
20019
|
-
` ${
|
|
19995
|
+
` ${import_chalk22.default.red.bold("\u{1F6A8} Panic mode : ON")} ` + import_chalk22.default.dim("(every review-verdict becomes block)")
|
|
20020
19996
|
);
|
|
20021
19997
|
}
|
|
20022
19998
|
if (s.shadowMode) {
|
|
20023
19999
|
console.log(
|
|
20024
|
-
` ${
|
|
20000
|
+
` ${import_chalk22.default.yellow.bold("\u{1F441} Shadow mode : ON")} ` + import_chalk22.default.dim("(blocks become would-block log entries)")
|
|
20025
20001
|
);
|
|
20026
20002
|
}
|
|
20027
20003
|
if (s.syncIntervalHours) {
|
|
20028
20004
|
console.log(
|
|
20029
|
-
|
|
20005
|
+
import_chalk22.default.gray(
|
|
20030
20006
|
` Polling : every ${s.syncIntervalHours} hour${s.syncIntervalHours === 1 ? "" : "s"}`
|
|
20031
20007
|
)
|
|
20032
20008
|
);
|
|
@@ -20036,7 +20012,7 @@ function registerSyncCommand(program2) {
|
|
|
20036
20012
|
}
|
|
20037
20013
|
|
|
20038
20014
|
// src/cli/commands/agents.ts
|
|
20039
|
-
var
|
|
20015
|
+
var import_chalk23 = __toESM(require("chalk"));
|
|
20040
20016
|
init_setup();
|
|
20041
20017
|
var SETUP_FN = {
|
|
20042
20018
|
claude: setupClaude,
|
|
@@ -20066,23 +20042,23 @@ function registerAgentsCommand(program2) {
|
|
|
20066
20042
|
console.log(` ${"Agent".padEnd(14)}${"Installed".padEnd(11)}${"Wired".padEnd(8)}Mode`);
|
|
20067
20043
|
console.log(" " + "\u2500".repeat(44));
|
|
20068
20044
|
for (const s of statuses) {
|
|
20069
|
-
const installed = s.installed ?
|
|
20070
|
-
const wired = !s.installed ?
|
|
20071
|
-
const mode = s.mode ?
|
|
20072
|
-
const hint = s.installed && !s.wired ?
|
|
20045
|
+
const installed = s.installed ? import_chalk23.default.green("\u2713") : import_chalk23.default.gray("\u2717");
|
|
20046
|
+
const wired = !s.installed ? import_chalk23.default.gray("\u2014") : s.wired ? import_chalk23.default.green("\u2713") : import_chalk23.default.yellow("\u2717");
|
|
20047
|
+
const mode = s.mode ? import_chalk23.default.gray(s.mode) : import_chalk23.default.gray("\u2014");
|
|
20048
|
+
const hint = s.installed && !s.wired ? import_chalk23.default.gray(` \u2190 node9 agents add ${s.name}`) : "";
|
|
20073
20049
|
console.log(` ${s.label.padEnd(14)}${installed} ${wired} ${mode}${hint}`);
|
|
20074
20050
|
}
|
|
20075
20051
|
console.log("");
|
|
20076
20052
|
if (!anyInstalled) {
|
|
20077
20053
|
console.log(
|
|
20078
|
-
|
|
20054
|
+
import_chalk23.default.gray(" No AI agents detected. Install Claude Code, Gemini CLI, Cursor,\n") + import_chalk23.default.gray(" Windsurf, VSCode, or Codex then run: node9 agents list\n")
|
|
20079
20055
|
);
|
|
20080
20056
|
return;
|
|
20081
20057
|
}
|
|
20082
20058
|
const unwired = statuses.filter((s) => s.installed && !s.wired);
|
|
20083
20059
|
if (unwired.length > 0) {
|
|
20084
20060
|
console.log(
|
|
20085
|
-
|
|
20061
|
+
import_chalk23.default.yellow(` ${unwired.length} agent(s) not yet wired. Run: `) + import_chalk23.default.white(`node9 agents add ${unwired[0].name}`) + "\n"
|
|
20086
20062
|
);
|
|
20087
20063
|
}
|
|
20088
20064
|
});
|
|
@@ -20090,7 +20066,7 @@ function registerAgentsCommand(program2) {
|
|
|
20090
20066
|
const name = agent.toLowerCase();
|
|
20091
20067
|
const fn = SETUP_FN[name];
|
|
20092
20068
|
if (!fn) {
|
|
20093
|
-
console.error(
|
|
20069
|
+
console.error(import_chalk23.default.red(`Unknown agent: "${agent}". Supported: ${AGENT_NAMES.join(", ")}`));
|
|
20094
20070
|
process.exit(1);
|
|
20095
20071
|
}
|
|
20096
20072
|
await fn();
|
|
@@ -20099,14 +20075,14 @@ function registerAgentsCommand(program2) {
|
|
|
20099
20075
|
const name = agent.toLowerCase();
|
|
20100
20076
|
const fn = TEARDOWN_FN[name];
|
|
20101
20077
|
if (!fn) {
|
|
20102
|
-
console.error(
|
|
20078
|
+
console.error(import_chalk23.default.red(`Unknown agent: "${agent}". Supported: ${AGENT_NAMES.join(", ")}`));
|
|
20103
20079
|
process.exit(1);
|
|
20104
20080
|
}
|
|
20105
|
-
console.log(
|
|
20081
|
+
console.log(import_chalk23.default.cyan(`
|
|
20106
20082
|
\u{1F6E1}\uFE0F Node9: removing from ${name}...
|
|
20107
20083
|
`));
|
|
20108
20084
|
fn();
|
|
20109
|
-
console.log(
|
|
20085
|
+
console.log(import_chalk23.default.gray("\n Restart the agent for changes to take effect."));
|
|
20110
20086
|
});
|
|
20111
20087
|
}
|
|
20112
20088
|
|
|
@@ -20114,7 +20090,7 @@ function registerAgentsCommand(program2) {
|
|
|
20114
20090
|
init_scan();
|
|
20115
20091
|
|
|
20116
20092
|
// src/cli/commands/sessions.ts
|
|
20117
|
-
var
|
|
20093
|
+
var import_chalk24 = __toESM(require("chalk"));
|
|
20118
20094
|
var import_fs40 = __toESM(require("fs"));
|
|
20119
20095
|
var import_path42 = __toESM(require("path"));
|
|
20120
20096
|
var import_os36 = __toESM(require("os"));
|
|
@@ -20623,11 +20599,11 @@ function toolInputSummary(tool, input) {
|
|
|
20623
20599
|
}
|
|
20624
20600
|
function toolColor(tool) {
|
|
20625
20601
|
const t = tool.toLowerCase();
|
|
20626
|
-
if (t === "bash" || t === "execute_bash") return
|
|
20627
|
-
if (t === "write") return
|
|
20628
|
-
if (t === "edit" || t === "notebookedit") return
|
|
20629
|
-
if (t === "read") return
|
|
20630
|
-
return
|
|
20602
|
+
if (t === "bash" || t === "execute_bash") return import_chalk24.default.red;
|
|
20603
|
+
if (t === "write") return import_chalk24.default.green;
|
|
20604
|
+
if (t === "edit" || t === "notebookedit") return import_chalk24.default.yellow;
|
|
20605
|
+
if (t === "read") return import_chalk24.default.cyan;
|
|
20606
|
+
return import_chalk24.default.gray;
|
|
20631
20607
|
}
|
|
20632
20608
|
function barStr2(value, max, width) {
|
|
20633
20609
|
if (max === 0 || width <= 0) return "\u2591".repeat(width);
|
|
@@ -20637,7 +20613,7 @@ function barStr2(value, max, width) {
|
|
|
20637
20613
|
function colorBar2(value, max, width) {
|
|
20638
20614
|
const s = barStr2(value, max, width);
|
|
20639
20615
|
const filled = Math.max(1, Math.round(max > 0 ? value / max * width : 0));
|
|
20640
|
-
return
|
|
20616
|
+
return import_chalk24.default.cyan(s.slice(0, filled)) + import_chalk24.default.dim(s.slice(filled));
|
|
20641
20617
|
}
|
|
20642
20618
|
function renderSummary(summaries) {
|
|
20643
20619
|
const totalTools = summaries.reduce((n, s) => n + s.toolCalls.length, 0);
|
|
@@ -20667,45 +20643,45 @@ function renderSummary(summaries) {
|
|
|
20667
20643
|
}
|
|
20668
20644
|
const topProjects = [...projCosts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
|
|
20669
20645
|
const W = 20;
|
|
20670
|
-
console.log(
|
|
20646
|
+
console.log(import_chalk24.default.dim(" " + "\u2500".repeat(70)));
|
|
20671
20647
|
console.log(
|
|
20672
|
-
" " +
|
|
20648
|
+
" " + import_chalk24.default.bold.white(String(summaries.length).padEnd(4)) + import_chalk24.default.dim("sessions ") + import_chalk24.default.bold.yellow(fmtCost3(totalCost).padEnd(10)) + import_chalk24.default.dim("total ") + import_chalk24.default.bold.white(String(totalTools).padEnd(6)) + import_chalk24.default.dim("tool calls ") + import_chalk24.default.bold.white(String(totalFiles)) + import_chalk24.default.dim(" files modified") + (totalBlocked > 0 ? import_chalk24.default.dim(" ") + import_chalk24.default.red.bold(String(totalBlocked)) + import_chalk24.default.dim(" blocked by node9") : "")
|
|
20673
20649
|
);
|
|
20674
20650
|
console.log(
|
|
20675
|
-
" " +
|
|
20651
|
+
" " + import_chalk24.default.dim("avg ") + import_chalk24.default.white(fmtCost3(avgCost).padEnd(10)) + import_chalk24.default.dim("/session ") + import_chalk24.default.green(String(snapshots)) + import_chalk24.default.dim(` of ${summaries.length} sessions had snapshots`)
|
|
20676
20652
|
);
|
|
20677
20653
|
console.log("");
|
|
20678
|
-
console.log(" " +
|
|
20654
|
+
console.log(" " + import_chalk24.default.dim("Tool breakdown:"));
|
|
20679
20655
|
const maxGroup = Math.max(...Object.values(groups));
|
|
20680
20656
|
for (const [label, count] of Object.entries(groups)) {
|
|
20681
20657
|
if (count === 0) continue;
|
|
20682
20658
|
const pct = totalTools > 0 ? Math.round(count / totalTools * 100) : 0;
|
|
20683
20659
|
console.log(
|
|
20684
|
-
" " + label.padEnd(6) + " " + colorBar2(count, maxGroup, W) + " " +
|
|
20660
|
+
" " + label.padEnd(6) + " " + colorBar2(count, maxGroup, W) + " " + import_chalk24.default.white(String(count).padStart(4)) + import_chalk24.default.dim(` (${String(pct)}%)`)
|
|
20685
20661
|
);
|
|
20686
20662
|
}
|
|
20687
20663
|
console.log("");
|
|
20688
20664
|
if (topProjects.length > 1) {
|
|
20689
|
-
console.log(" " +
|
|
20665
|
+
console.log(" " + import_chalk24.default.dim("Cost by project:"));
|
|
20690
20666
|
const maxProjCost = topProjects[0][1];
|
|
20691
20667
|
for (const [proj, cost] of topProjects) {
|
|
20692
20668
|
console.log(
|
|
20693
|
-
" " + proj.slice(0, 28).padEnd(28) + " " + colorBar2(cost, maxProjCost, W) + " " +
|
|
20669
|
+
" " + proj.slice(0, 28).padEnd(28) + " " + colorBar2(cost, maxProjCost, W) + " " + import_chalk24.default.yellow(fmtCost3(cost))
|
|
20694
20670
|
);
|
|
20695
20671
|
}
|
|
20696
20672
|
console.log("");
|
|
20697
20673
|
}
|
|
20698
|
-
console.log(
|
|
20674
|
+
console.log(import_chalk24.default.dim(" " + "\u2500".repeat(70)));
|
|
20699
20675
|
console.log("");
|
|
20700
20676
|
}
|
|
20701
20677
|
function renderList(summaries, totalCost) {
|
|
20702
20678
|
if (summaries.length === 0) {
|
|
20703
|
-
console.log(
|
|
20679
|
+
console.log(import_chalk24.default.yellow(" No sessions found in the requested range.\n"));
|
|
20704
20680
|
return;
|
|
20705
20681
|
}
|
|
20706
|
-
const totalLabel = totalCost > 0 ?
|
|
20682
|
+
const totalLabel = totalCost > 0 ? import_chalk24.default.dim(" ~" + fmtCost3(totalCost) + " total") : "";
|
|
20707
20683
|
console.log(
|
|
20708
|
-
" " +
|
|
20684
|
+
" " + import_chalk24.default.white(String(summaries.length)) + import_chalk24.default.dim(` session${summaries.length !== 1 ? "s" : ""}`) + totalLabel
|
|
20709
20685
|
);
|
|
20710
20686
|
console.log("");
|
|
20711
20687
|
let lastGroup = "";
|
|
@@ -20713,49 +20689,49 @@ function renderList(summaries, totalCost) {
|
|
|
20713
20689
|
const activeDate = fmtDate2(s.lastActiveTime);
|
|
20714
20690
|
const group = activeDate + " " + s.projectLabel;
|
|
20715
20691
|
if (group !== lastGroup) {
|
|
20716
|
-
console.log(
|
|
20692
|
+
console.log(import_chalk24.default.dim(" \u2500\u2500\u2500 ") + import_chalk24.default.bold(activeDate) + import_chalk24.default.dim(" " + s.projectLabel));
|
|
20717
20693
|
lastGroup = group;
|
|
20718
20694
|
}
|
|
20719
20695
|
const startDate = fmtDate2(s.startTime);
|
|
20720
|
-
const dateRange = startDate !== activeDate ?
|
|
20721
|
-
const timeStr =
|
|
20722
|
-
const prompt =
|
|
20723
|
-
const tools = s.toolCalls.length > 0 ?
|
|
20724
|
-
const cost = s.costUSD > 0 ?
|
|
20725
|
-
const blocked = s.blockedCalls.length > 0 ?
|
|
20726
|
-
const snap = s.hasSnapshot ?
|
|
20727
|
-
const agentBadge = s.agent === "gemini" ?
|
|
20728
|
-
const sid =
|
|
20696
|
+
const dateRange = startDate !== activeDate ? import_chalk24.default.dim(" (" + startDate + " \u2192 " + activeDate + ")") : "";
|
|
20697
|
+
const timeStr = import_chalk24.default.dim(fmtTime(s.startTime));
|
|
20698
|
+
const prompt = import_chalk24.default.white(truncate(s.firstPrompt.replace(/\n/g, " "), 50).padEnd(50));
|
|
20699
|
+
const tools = s.toolCalls.length > 0 ? import_chalk24.default.dim(String(s.toolCalls.length).padStart(3) + " tools") : import_chalk24.default.dim(" 0 tools");
|
|
20700
|
+
const cost = s.costUSD > 0 ? import_chalk24.default.dim(" " + fmtCost3(s.costUSD).padEnd(8)) : " ";
|
|
20701
|
+
const blocked = s.blockedCalls.length > 0 ? import_chalk24.default.red(" \u{1F6D1} " + String(s.blockedCalls.length)) : "";
|
|
20702
|
+
const snap = s.hasSnapshot ? import_chalk24.default.green(" \u{1F4F8}") : "";
|
|
20703
|
+
const agentBadge = s.agent === "gemini" ? import_chalk24.default.blue(" [Gemini]") : s.agent === "codex" ? import_chalk24.default.magenta(" [Codex]") : import_chalk24.default.cyan(" [Claude]");
|
|
20704
|
+
const sid = import_chalk24.default.dim(" " + s.sessionId.slice(0, 8));
|
|
20729
20705
|
console.log(
|
|
20730
20706
|
` ${timeStr} ${prompt} ${tools}${cost}${blocked}${snap}${agentBadge}${sid}${dateRange}`
|
|
20731
20707
|
);
|
|
20732
20708
|
}
|
|
20733
20709
|
console.log("");
|
|
20734
20710
|
console.log(
|
|
20735
|
-
|
|
20711
|
+
import_chalk24.default.dim(" Run") + " " + import_chalk24.default.cyan("node9 sessions --detail <session-id>") + import_chalk24.default.dim(" for full tool trace.")
|
|
20736
20712
|
);
|
|
20737
20713
|
console.log("");
|
|
20738
20714
|
}
|
|
20739
20715
|
function renderDetail(s) {
|
|
20740
20716
|
console.log("");
|
|
20741
|
-
console.log(
|
|
20717
|
+
console.log(import_chalk24.default.bold(" Session ") + import_chalk24.default.dim(s.sessionId));
|
|
20742
20718
|
console.log(
|
|
20743
|
-
|
|
20719
|
+
import_chalk24.default.bold(" Prompt ") + import_chalk24.default.white(s.firstPrompt.replace(/\n/g, " ").slice(0, 120))
|
|
20744
20720
|
);
|
|
20745
|
-
console.log(
|
|
20721
|
+
console.log(import_chalk24.default.bold(" Project ") + import_chalk24.default.white(s.projectLabel));
|
|
20746
20722
|
if (s.agent) {
|
|
20747
|
-
const agentLabel2 = s.agent === "gemini" ?
|
|
20748
|
-
console.log(
|
|
20723
|
+
const agentLabel2 = s.agent === "gemini" ? import_chalk24.default.blue("Gemini CLI") : s.agent === "codex" ? import_chalk24.default.magenta("Codex") : import_chalk24.default.cyan("Claude Code");
|
|
20724
|
+
console.log(import_chalk24.default.bold(" Agent ") + agentLabel2);
|
|
20749
20725
|
}
|
|
20750
|
-
console.log(
|
|
20726
|
+
console.log(import_chalk24.default.bold(" When ") + import_chalk24.default.white(fmtDateTime(s.startTime)));
|
|
20751
20727
|
if (s.costUSD > 0)
|
|
20752
|
-
console.log(
|
|
20728
|
+
console.log(import_chalk24.default.bold(" Cost ") + import_chalk24.default.yellow("~" + fmtCost3(s.costUSD)));
|
|
20753
20729
|
console.log(
|
|
20754
|
-
|
|
20730
|
+
import_chalk24.default.bold(" Snapshot ") + (s.hasSnapshot ? import_chalk24.default.green("\u2713 taken") : import_chalk24.default.dim("none"))
|
|
20755
20731
|
);
|
|
20756
20732
|
console.log("");
|
|
20757
20733
|
if (s.toolCalls.length === 0 && s.blockedCalls.length === 0) {
|
|
20758
|
-
console.log(
|
|
20734
|
+
console.log(import_chalk24.default.dim(" No tool calls recorded.\n"));
|
|
20759
20735
|
return;
|
|
20760
20736
|
}
|
|
20761
20737
|
const timeline = [
|
|
@@ -20768,32 +20744,32 @@ function renderDetail(s) {
|
|
|
20768
20744
|
});
|
|
20769
20745
|
const headerParts = [`Tool calls (${s.toolCalls.length})`];
|
|
20770
20746
|
if (s.blockedCalls.length > 0)
|
|
20771
|
-
headerParts.push(
|
|
20772
|
-
console.log(
|
|
20747
|
+
headerParts.push(import_chalk24.default.red(`${s.blockedCalls.length} blocked by node9`));
|
|
20748
|
+
console.log(import_chalk24.default.bold(" " + headerParts.join(" \xB7 ")));
|
|
20773
20749
|
console.log("");
|
|
20774
20750
|
for (const entry of timeline) {
|
|
20775
20751
|
if (entry.kind === "tool") {
|
|
20776
20752
|
const tc = entry.tc;
|
|
20777
20753
|
const colorFn = toolColor(tc.tool);
|
|
20778
20754
|
const toolPad = colorFn(tc.tool.padEnd(16));
|
|
20779
|
-
const detail =
|
|
20780
|
-
const ts = tc.timestamp ?
|
|
20755
|
+
const detail = import_chalk24.default.gray(truncate(toolInputSummary(tc.tool, tc.input), 70));
|
|
20756
|
+
const ts = tc.timestamp ? import_chalk24.default.dim(fmtTime(tc.timestamp) + " ") : " ";
|
|
20781
20757
|
console.log(` ${ts}${toolPad} ${detail}`);
|
|
20782
20758
|
} else {
|
|
20783
20759
|
const bc = entry.bc;
|
|
20784
|
-
const ts = bc.timestamp ?
|
|
20785
|
-
const label =
|
|
20786
|
-
const toolName =
|
|
20787
|
-
const argsSummary = bc.args ?
|
|
20788
|
-
const reason = bc.checkedBy ?
|
|
20760
|
+
const ts = bc.timestamp ? import_chalk24.default.dim(fmtTime(bc.timestamp) + " ") : " ";
|
|
20761
|
+
const label = import_chalk24.default.red("\u{1F6D1} BLOCKED".padEnd(16));
|
|
20762
|
+
const toolName = import_chalk24.default.red(bc.tool.padEnd(10));
|
|
20763
|
+
const argsSummary = bc.args ? import_chalk24.default.gray(truncate(toolInputSummary(bc.tool, bc.args), 40)) : import_chalk24.default.dim("[args not logged]");
|
|
20764
|
+
const reason = bc.checkedBy ? import_chalk24.default.dim(" \u2190 " + bc.checkedBy) : "";
|
|
20789
20765
|
console.log(` ${ts}${label} ${toolName} ${argsSummary}${reason}`);
|
|
20790
20766
|
}
|
|
20791
20767
|
}
|
|
20792
20768
|
console.log("");
|
|
20793
20769
|
if (s.modifiedFiles.length > 0) {
|
|
20794
|
-
console.log(
|
|
20770
|
+
console.log(import_chalk24.default.bold(` Files modified (${s.modifiedFiles.length}):`));
|
|
20795
20771
|
for (const f of s.modifiedFiles) {
|
|
20796
|
-
console.log(" " +
|
|
20772
|
+
console.log(" " + import_chalk24.default.yellow(f));
|
|
20797
20773
|
}
|
|
20798
20774
|
console.log("");
|
|
20799
20775
|
}
|
|
@@ -20801,19 +20777,19 @@ function renderDetail(s) {
|
|
|
20801
20777
|
function registerSessionsCommand(program2) {
|
|
20802
20778
|
program2.command("sessions").description("Show what your AI agent did \u2014 sessions, tool calls, cost, and file changes").option("--all", "Show all sessions (default: last 7 days)").option("--days <n>", "Show last N days of sessions", "7").option("--detail <sessionId>", "Show full tool trace for a session").action((options) => {
|
|
20803
20779
|
console.log("");
|
|
20804
|
-
console.log(
|
|
20780
|
+
console.log(import_chalk24.default.cyan.bold("\u{1F4CB} node9 sessions") + import_chalk24.default.dim(" \u2014 what your AI agent did"));
|
|
20805
20781
|
console.log("");
|
|
20806
20782
|
const historyPath = import_path42.default.join(import_os36.default.homedir(), ".claude", "history.jsonl");
|
|
20807
20783
|
if (!import_fs40.default.existsSync(historyPath)) {
|
|
20808
|
-
console.log(
|
|
20809
|
-
console.log(
|
|
20784
|
+
console.log(import_chalk24.default.yellow(" No Claude session history found at ~/.claude/history.jsonl"));
|
|
20785
|
+
console.log(import_chalk24.default.gray(" Install Claude Code, run a few sessions, then try again.\n"));
|
|
20810
20786
|
return;
|
|
20811
20787
|
}
|
|
20812
20788
|
const days = options.detail || options.all ? null : Math.max(1, parseInt(options.days, 10) || 7);
|
|
20813
20789
|
const rangeLabel = options.detail ? "all time" : options.all ? "all time" : `last ${String(days)} days`;
|
|
20814
|
-
console.log(
|
|
20790
|
+
console.log(import_chalk24.default.dim(" " + rangeLabel));
|
|
20815
20791
|
console.log("");
|
|
20816
|
-
process.stdout.write(
|
|
20792
|
+
process.stdout.write(import_chalk24.default.dim(" Loading\u2026"));
|
|
20817
20793
|
const summaries = buildSessions(days);
|
|
20818
20794
|
if (process.stdout.isTTY) {
|
|
20819
20795
|
process.stdout.clearLine(0);
|
|
@@ -20826,8 +20802,8 @@ function registerSessionsCommand(program2) {
|
|
|
20826
20802
|
(s) => s.sessionId === options.detail || s.sessionId.startsWith(options.detail)
|
|
20827
20803
|
);
|
|
20828
20804
|
if (!target) {
|
|
20829
|
-
console.log(
|
|
20830
|
-
console.log(
|
|
20805
|
+
console.log(import_chalk24.default.red(` Session not found: ${options.detail}`));
|
|
20806
|
+
console.log(import_chalk24.default.dim(" Run `node9 sessions` to list recent sessions.\n"));
|
|
20831
20807
|
return;
|
|
20832
20808
|
}
|
|
20833
20809
|
renderDetail(target);
|
|
@@ -20840,7 +20816,7 @@ function registerSessionsCommand(program2) {
|
|
|
20840
20816
|
}
|
|
20841
20817
|
|
|
20842
20818
|
// src/cli/commands/skill-pin.ts
|
|
20843
|
-
var
|
|
20819
|
+
var import_chalk25 = __toESM(require("chalk"));
|
|
20844
20820
|
var import_fs41 = __toESM(require("fs"));
|
|
20845
20821
|
var import_os37 = __toESM(require("os"));
|
|
20846
20822
|
var import_path43 = __toESM(require("path"));
|
|
@@ -20860,29 +20836,29 @@ function registerSkillPinCommand(program2) {
|
|
|
20860
20836
|
const result = readSkillPinsSafe();
|
|
20861
20837
|
if (!result.ok) {
|
|
20862
20838
|
if (result.reason === "missing") {
|
|
20863
|
-
console.log(
|
|
20839
|
+
console.log(import_chalk25.default.gray("\nNo skill roots are pinned yet."));
|
|
20864
20840
|
console.log(
|
|
20865
|
-
|
|
20841
|
+
import_chalk25.default.gray("Pins are created automatically on the first tool call of each session.\n")
|
|
20866
20842
|
);
|
|
20867
20843
|
return;
|
|
20868
20844
|
}
|
|
20869
|
-
console.error(
|
|
20845
|
+
console.error(import_chalk25.default.red(`
|
|
20870
20846
|
\u274C Pin file is corrupt: ${result.detail}`));
|
|
20871
|
-
console.error(
|
|
20847
|
+
console.error(import_chalk25.default.yellow(" Run: node9 skill pin reset\n"));
|
|
20872
20848
|
process.exit(1);
|
|
20873
20849
|
}
|
|
20874
20850
|
const entries = Object.entries(result.pins.roots);
|
|
20875
20851
|
if (entries.length === 0) {
|
|
20876
|
-
console.log(
|
|
20852
|
+
console.log(import_chalk25.default.gray("\nNo skill roots are pinned yet.\n"));
|
|
20877
20853
|
return;
|
|
20878
20854
|
}
|
|
20879
|
-
console.log(
|
|
20855
|
+
console.log(import_chalk25.default.bold("\n\u{1F512} Pinned Skill Roots\n"));
|
|
20880
20856
|
for (const [key, entry] of entries) {
|
|
20881
|
-
const missing = entry.exists ? "" :
|
|
20882
|
-
console.log(` ${
|
|
20857
|
+
const missing = entry.exists ? "" : import_chalk25.default.yellow(" (not present at pin time)");
|
|
20858
|
+
console.log(` ${import_chalk25.default.cyan(key)} ${import_chalk25.default.gray(entry.rootPath)}${missing}`);
|
|
20883
20859
|
console.log(` Files (${entry.fileCount})`);
|
|
20884
|
-
console.log(` Hash: ${
|
|
20885
|
-
console.log(` Pinned: ${
|
|
20860
|
+
console.log(` Hash: ${import_chalk25.default.gray(entry.contentHash.slice(0, 16))}...`);
|
|
20861
|
+
console.log(` Pinned: ${import_chalk25.default.gray(entry.pinnedAt)}
|
|
20886
20862
|
`);
|
|
20887
20863
|
}
|
|
20888
20864
|
});
|
|
@@ -20891,39 +20867,39 @@ function registerSkillPinCommand(program2) {
|
|
|
20891
20867
|
try {
|
|
20892
20868
|
pins = readSkillPins();
|
|
20893
20869
|
} catch {
|
|
20894
|
-
console.error(
|
|
20895
|
-
console.error(
|
|
20870
|
+
console.error(import_chalk25.default.red("\n\u274C Pin file is corrupt."));
|
|
20871
|
+
console.error(import_chalk25.default.yellow(" Run: node9 skill pin reset\n"));
|
|
20896
20872
|
process.exit(1);
|
|
20897
20873
|
}
|
|
20898
20874
|
if (!pins.roots[rootKey]) {
|
|
20899
|
-
console.error(
|
|
20875
|
+
console.error(import_chalk25.default.red(`
|
|
20900
20876
|
\u274C No pin found for root key "${rootKey}"
|
|
20901
20877
|
`));
|
|
20902
|
-
console.error(`Run ${
|
|
20878
|
+
console.error(`Run ${import_chalk25.default.cyan("node9 skill pin list")} to see pinned roots.
|
|
20903
20879
|
`);
|
|
20904
20880
|
process.exit(1);
|
|
20905
20881
|
}
|
|
20906
20882
|
const rootPath = pins.roots[rootKey].rootPath;
|
|
20907
20883
|
removePin(rootKey);
|
|
20908
20884
|
wipeSkillSessions();
|
|
20909
|
-
console.log(
|
|
20910
|
-
\u{1F513} Pin removed for ${
|
|
20911
|
-
console.log(
|
|
20912
|
-
console.log(
|
|
20885
|
+
console.log(import_chalk25.default.green(`
|
|
20886
|
+
\u{1F513} Pin removed for ${import_chalk25.default.cyan(rootKey)}`));
|
|
20887
|
+
console.log(import_chalk25.default.gray(` ${rootPath}`));
|
|
20888
|
+
console.log(import_chalk25.default.gray(" Next session will re-pin with current state.\n"));
|
|
20913
20889
|
});
|
|
20914
20890
|
pinSubCmd.command("reset").description("Clear all skill pins and wipe session verification flags").action(() => {
|
|
20915
20891
|
const result = readSkillPinsSafe();
|
|
20916
20892
|
if (!result.ok && result.reason === "missing") {
|
|
20917
20893
|
wipeSkillSessions();
|
|
20918
|
-
console.log(
|
|
20894
|
+
console.log(import_chalk25.default.gray("\nNo pins to clear.\n"));
|
|
20919
20895
|
return;
|
|
20920
20896
|
}
|
|
20921
20897
|
const count = result.ok ? Object.keys(result.pins.roots).length : "?";
|
|
20922
20898
|
clearAllPins();
|
|
20923
20899
|
wipeSkillSessions();
|
|
20924
|
-
console.log(
|
|
20900
|
+
console.log(import_chalk25.default.green(`
|
|
20925
20901
|
\u{1F513} Cleared ${count} skill pin(s).`));
|
|
20926
|
-
console.log(
|
|
20902
|
+
console.log(import_chalk25.default.gray(" Next session will re-pin with current state.\n"));
|
|
20927
20903
|
});
|
|
20928
20904
|
}
|
|
20929
20905
|
|
|
@@ -20931,7 +20907,7 @@ function registerSkillPinCommand(program2) {
|
|
|
20931
20907
|
var import_fs42 = __toESM(require("fs"));
|
|
20932
20908
|
var import_os38 = __toESM(require("os"));
|
|
20933
20909
|
var import_path44 = __toESM(require("path"));
|
|
20934
|
-
var
|
|
20910
|
+
var import_chalk26 = __toESM(require("chalk"));
|
|
20935
20911
|
var DECISIONS_FILE2 = import_path44.default.join(import_os38.default.homedir(), ".node9", "decisions.json");
|
|
20936
20912
|
function readDecisions() {
|
|
20937
20913
|
try {
|
|
@@ -20960,55 +20936,55 @@ function registerDecisionsCommand(program2) {
|
|
|
20960
20936
|
const decisions = readDecisions();
|
|
20961
20937
|
const entries = Object.entries(decisions);
|
|
20962
20938
|
if (entries.length === 0) {
|
|
20963
|
-
console.log(
|
|
20939
|
+
console.log(import_chalk26.default.gray(" No persistent decisions stored."));
|
|
20964
20940
|
console.log(
|
|
20965
|
-
|
|
20966
|
-
`) +
|
|
20941
|
+
import_chalk26.default.gray(` File: ${DECISIONS_FILE2}
|
|
20942
|
+
`) + import_chalk26.default.gray(' Decisions are written when you click "Always Allow" or')
|
|
20967
20943
|
);
|
|
20968
|
-
console.log(
|
|
20944
|
+
console.log(import_chalk26.default.gray(' "Always Deny" in node9 tail or the native popup.'));
|
|
20969
20945
|
return;
|
|
20970
20946
|
}
|
|
20971
|
-
console.log(
|
|
20947
|
+
console.log(import_chalk26.default.bold(`
|
|
20972
20948
|
Persistent decisions (${entries.length})
|
|
20973
20949
|
`));
|
|
20974
20950
|
const w = Math.max(...entries.map(([k]) => k.length));
|
|
20975
20951
|
for (const [tool, verdict] of entries.sort()) {
|
|
20976
|
-
const colored = verdict === "allow" ?
|
|
20952
|
+
const colored = verdict === "allow" ? import_chalk26.default.green(verdict) : import_chalk26.default.red(verdict);
|
|
20977
20953
|
console.log(` ${tool.padEnd(w)} ${colored}`);
|
|
20978
20954
|
}
|
|
20979
20955
|
console.log(
|
|
20980
|
-
|
|
20956
|
+
import_chalk26.default.gray(`
|
|
20981
20957
|
Stored in ${DECISIONS_FILE2}
|
|
20982
|
-
`) +
|
|
20958
|
+
`) + import_chalk26.default.gray(" Run `node9 decisions clear <tool>` to remove an entry.")
|
|
20983
20959
|
);
|
|
20984
20960
|
});
|
|
20985
20961
|
cmd.command("clear <toolName>").description("Remove a persistent decision for one tool").action((toolName) => {
|
|
20986
20962
|
const decisions = readDecisions();
|
|
20987
20963
|
if (!(toolName in decisions)) {
|
|
20988
|
-
console.log(
|
|
20964
|
+
console.log(import_chalk26.default.yellow(` No persistent decision for "${toolName}". Nothing to clear.`));
|
|
20989
20965
|
process.exitCode = 1;
|
|
20990
20966
|
return;
|
|
20991
20967
|
}
|
|
20992
20968
|
delete decisions[toolName];
|
|
20993
20969
|
writeDecisions(decisions);
|
|
20994
|
-
console.log(
|
|
20970
|
+
console.log(import_chalk26.default.green(` \u2713 Cleared persistent decision for "${toolName}".`));
|
|
20995
20971
|
});
|
|
20996
20972
|
cmd.command("clear-all").description("Remove every persistent decision (irreversible)").action(() => {
|
|
20997
20973
|
const decisions = readDecisions();
|
|
20998
20974
|
const count = Object.keys(decisions).length;
|
|
20999
20975
|
if (count === 0) {
|
|
21000
|
-
console.log(
|
|
20976
|
+
console.log(import_chalk26.default.gray(" Nothing to clear \u2014 no persistent decisions stored."));
|
|
21001
20977
|
return;
|
|
21002
20978
|
}
|
|
21003
20979
|
writeDecisions({});
|
|
21004
20980
|
console.log(
|
|
21005
|
-
|
|
20981
|
+
import_chalk26.default.green(` \u2713 Cleared ${count} persistent decision${count === 1 ? "" : "s"}.`)
|
|
21006
20982
|
);
|
|
21007
20983
|
});
|
|
21008
20984
|
}
|
|
21009
20985
|
|
|
21010
20986
|
// src/cli/commands/dlp.ts
|
|
21011
|
-
var
|
|
20987
|
+
var import_chalk27 = __toESM(require("chalk"));
|
|
21012
20988
|
var import_fs43 = __toESM(require("fs"));
|
|
21013
20989
|
var import_path45 = __toESM(require("path"));
|
|
21014
20990
|
var import_os39 = __toESM(require("os"));
|
|
@@ -21063,14 +21039,14 @@ function registerDlpCommand(program2) {
|
|
|
21063
21039
|
cmd.command("resolve").description("Mark all current DLP findings as resolved").action(() => {
|
|
21064
21040
|
const findings = loadDlpFindings();
|
|
21065
21041
|
if (findings.length === 0) {
|
|
21066
|
-
console.log(
|
|
21042
|
+
console.log(import_chalk27.default.green("\n \u2705 No response-DLP findings to resolve.\n"));
|
|
21067
21043
|
return;
|
|
21068
21044
|
}
|
|
21069
21045
|
const resolved = loadResolved();
|
|
21070
21046
|
for (const e of findings) resolved.add(entryKey(e));
|
|
21071
21047
|
saveResolved(resolved);
|
|
21072
21048
|
console.log(
|
|
21073
|
-
|
|
21049
|
+
import_chalk27.default.green(
|
|
21074
21050
|
`
|
|
21075
21051
|
\u2705 ${findings.length} finding${findings.length !== 1 ? "s" : ""} marked as resolved.
|
|
21076
21052
|
`
|
|
@@ -21084,54 +21060,54 @@ function registerDlpCommand(program2) {
|
|
|
21084
21060
|
const resolvedCount = findings.length - open.length;
|
|
21085
21061
|
console.log("");
|
|
21086
21062
|
console.log(
|
|
21087
|
-
|
|
21063
|
+
import_chalk27.default.bold.cyan("\u{1F510} node9 dlp") + import_chalk27.default.dim(" \u2014 secrets found in Claude response text")
|
|
21088
21064
|
);
|
|
21089
21065
|
console.log("");
|
|
21090
21066
|
if (open.length === 0) {
|
|
21091
21067
|
if (resolvedCount > 0) {
|
|
21092
|
-
console.log(
|
|
21068
|
+
console.log(import_chalk27.default.green(` \u2705 No open findings \xB7 ${resolvedCount} previously resolved`));
|
|
21093
21069
|
} else {
|
|
21094
21070
|
console.log(
|
|
21095
|
-
|
|
21071
|
+
import_chalk27.default.green(" \u2705 No findings \u2014 Claude has not leaked secrets in response text")
|
|
21096
21072
|
);
|
|
21097
21073
|
}
|
|
21098
21074
|
console.log("");
|
|
21099
21075
|
return;
|
|
21100
21076
|
}
|
|
21101
21077
|
console.log(
|
|
21102
|
-
|
|
21078
|
+
import_chalk27.default.bgRed.white.bold(` \u26A0\uFE0F ${open.length} open finding${open.length !== 1 ? "s" : ""} `) + import_chalk27.default.dim(resolvedCount > 0 ? ` (${resolvedCount} resolved)` : "")
|
|
21103
21079
|
);
|
|
21104
21080
|
console.log("");
|
|
21105
21081
|
console.log(
|
|
21106
|
-
|
|
21082
|
+
import_chalk27.default.dim(" These secrets were included in Claude's response text \u2014 NOT blocked.")
|
|
21107
21083
|
);
|
|
21108
|
-
console.log(
|
|
21084
|
+
console.log(import_chalk27.default.dim(" Rotate each affected key immediately.\n"));
|
|
21109
21085
|
for (const e of open) {
|
|
21110
21086
|
console.log(
|
|
21111
|
-
" " +
|
|
21087
|
+
" " + import_chalk27.default.red("\u25CF") + " " + import_chalk27.default.white(e.dlpPattern ?? "Secret") + import_chalk27.default.dim(" " + fmtDate3(e.ts))
|
|
21112
21088
|
);
|
|
21113
21089
|
if (e.dlpSample) {
|
|
21114
|
-
console.log(" " +
|
|
21090
|
+
console.log(" " + import_chalk27.default.dim("Sample: ") + import_chalk27.default.yellow(stripAnsi(e.dlpSample)));
|
|
21115
21091
|
}
|
|
21116
21092
|
if (e.project) {
|
|
21117
|
-
console.log(" " +
|
|
21093
|
+
console.log(" " + import_chalk27.default.dim("Project: ") + import_chalk27.default.dim(stripAnsi(e.project)));
|
|
21118
21094
|
}
|
|
21119
21095
|
console.log("");
|
|
21120
21096
|
}
|
|
21121
|
-
console.log(" " +
|
|
21122
|
-
console.log(" " +
|
|
21097
|
+
console.log(" " + import_chalk27.default.bold("Next steps:"));
|
|
21098
|
+
console.log(" " + import_chalk27.default.cyan("1.") + " Rotate any exposed keys shown above");
|
|
21123
21099
|
console.log(
|
|
21124
|
-
" " +
|
|
21100
|
+
" " + import_chalk27.default.cyan("2.") + " Run " + import_chalk27.default.white("node9 dlp resolve") + " to acknowledge"
|
|
21125
21101
|
);
|
|
21126
21102
|
console.log(
|
|
21127
|
-
" " +
|
|
21103
|
+
" " + import_chalk27.default.cyan("3.") + " Run " + import_chalk27.default.white("node9 report") + " for full audit history"
|
|
21128
21104
|
);
|
|
21129
21105
|
console.log("");
|
|
21130
21106
|
});
|
|
21131
21107
|
}
|
|
21132
21108
|
|
|
21133
21109
|
// src/cli/commands/mask.ts
|
|
21134
|
-
var
|
|
21110
|
+
var import_chalk28 = __toESM(require("chalk"));
|
|
21135
21111
|
var import_fs44 = __toESM(require("fs"));
|
|
21136
21112
|
var import_path46 = __toESM(require("path"));
|
|
21137
21113
|
var import_os40 = __toESM(require("os"));
|
|
@@ -21268,12 +21244,12 @@ function registerMaskCommand(program2) {
|
|
|
21268
21244
|
}
|
|
21269
21245
|
}) : allFiles;
|
|
21270
21246
|
if (filtered.length === 0) {
|
|
21271
|
-
console.log(
|
|
21247
|
+
console.log(import_chalk28.default.yellow(" No session files found."));
|
|
21272
21248
|
return;
|
|
21273
21249
|
}
|
|
21274
21250
|
console.log("");
|
|
21275
21251
|
if (dryRun) {
|
|
21276
|
-
console.log(
|
|
21252
|
+
console.log(import_chalk28.default.dim(" Dry run \u2014 no files will be modified.\n"));
|
|
21277
21253
|
}
|
|
21278
21254
|
let totalFiles = 0;
|
|
21279
21255
|
let totalLines = 0;
|
|
@@ -21289,23 +21265,23 @@ function registerMaskCommand(program2) {
|
|
|
21289
21265
|
});
|
|
21290
21266
|
const verb = dryRun ? "Would redact" : "Redacted";
|
|
21291
21267
|
console.log(
|
|
21292
|
-
" " +
|
|
21268
|
+
" " + import_chalk28.default.dim(shortPath.slice(0, 60).padEnd(62)) + import_chalk28.default.red(`${verb}: `) + import_chalk28.default.yellow(patterns.join(", ")) + import_chalk28.default.dim(` (${redactedLines} line${redactedLines !== 1 ? "s" : ""})`)
|
|
21293
21269
|
);
|
|
21294
21270
|
}
|
|
21295
21271
|
}
|
|
21296
21272
|
console.log("");
|
|
21297
21273
|
if (totalFiles === 0) {
|
|
21298
|
-
console.log(
|
|
21274
|
+
console.log(import_chalk28.default.green(" No secrets found in session history."));
|
|
21299
21275
|
} else {
|
|
21300
21276
|
const verb = dryRun ? "would be modified" : "modified";
|
|
21301
21277
|
console.log(
|
|
21302
|
-
|
|
21278
|
+
import_chalk28.default.bold(` ${totalFiles} file${totalFiles !== 1 ? "s" : ""} ${verb}`) + import_chalk28.default.dim(`, ${totalLines} line${totalLines !== 1 ? "s" : ""} redacted`)
|
|
21303
21279
|
);
|
|
21304
|
-
console.log(" Patterns: " +
|
|
21280
|
+
console.log(" Patterns: " + import_chalk28.default.yellow(totalPatterns.join(", ")));
|
|
21305
21281
|
if (!dryRun) {
|
|
21306
21282
|
console.log("");
|
|
21307
21283
|
console.log(
|
|
21308
|
-
|
|
21284
|
+
import_chalk28.default.dim(
|
|
21309
21285
|
" Note: secrets were already sent to the AI provider during the active session.\n This cleans your local disk only. Rotate any exposed keys."
|
|
21310
21286
|
)
|
|
21311
21287
|
);
|
|
@@ -21369,17 +21345,17 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
21369
21345
|
import_fs47.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
21370
21346
|
}
|
|
21371
21347
|
if (options.profile && profileName !== "default") {
|
|
21372
|
-
console.log(
|
|
21373
|
-
console.log(
|
|
21348
|
+
console.log(import_chalk30.default.green(`\u2705 Profile "${profileName}" saved`));
|
|
21349
|
+
console.log(import_chalk30.default.gray(` Switch to it per-session: NODE9_PROFILE=${profileName} claude`));
|
|
21374
21350
|
} else if (options.local) {
|
|
21375
|
-
console.log(
|
|
21376
|
-
console.log(
|
|
21351
|
+
console.log(import_chalk30.default.green(`\u2705 Privacy mode \u{1F6E1}\uFE0F`));
|
|
21352
|
+
console.log(import_chalk30.default.gray(` All decisions stay on this machine.`));
|
|
21377
21353
|
} else {
|
|
21378
|
-
console.log(
|
|
21379
|
-
console.log(
|
|
21354
|
+
console.log(import_chalk30.default.green(`\u2705 Logged in \u2014 agent mode`));
|
|
21355
|
+
console.log(import_chalk30.default.gray(` Team policy enforced for all calls via Node9 cloud.`));
|
|
21380
21356
|
}
|
|
21381
21357
|
});
|
|
21382
|
-
program.command("addto").description("Integrate Node9 with an AI agent").addHelpText(
|
|
21358
|
+
program.command("addto", { hidden: true }).description("Integrate Node9 with an AI agent").addHelpText(
|
|
21383
21359
|
"after",
|
|
21384
21360
|
"\n Supported targets: claude gemini cursor codex windsurf vscode hud"
|
|
21385
21361
|
).argument(
|
|
@@ -21394,13 +21370,13 @@ program.command("addto").description("Integrate Node9 with an AI agent").addHelp
|
|
|
21394
21370
|
if (target === "vscode") return await setupVSCode();
|
|
21395
21371
|
if (target === "hud") return setupHud();
|
|
21396
21372
|
console.error(
|
|
21397
|
-
|
|
21373
|
+
import_chalk30.default.red(
|
|
21398
21374
|
`Unknown target: "${target}". Supported: claude, gemini, cursor, codex, windsurf, vscode, hud`
|
|
21399
21375
|
)
|
|
21400
21376
|
);
|
|
21401
21377
|
process.exit(1);
|
|
21402
21378
|
});
|
|
21403
|
-
program.command("setup").description('Alias for "addto" \u2014 integrate Node9 with an AI agent').addHelpText(
|
|
21379
|
+
program.command("setup", { hidden: true }).description('Alias for "addto" \u2014 integrate Node9 with an AI agent').addHelpText(
|
|
21404
21380
|
"after",
|
|
21405
21381
|
"\n Supported targets: claude gemini cursor codex windsurf vscode hud"
|
|
21406
21382
|
).argument(
|
|
@@ -21408,17 +21384,17 @@ program.command("setup").description('Alias for "addto" \u2014 integrate Node9 w
|
|
|
21408
21384
|
"The agent to protect: claude | gemini | cursor | codex | windsurf | vscode | hud"
|
|
21409
21385
|
).action(async (target) => {
|
|
21410
21386
|
if (!target) {
|
|
21411
|
-
console.log(
|
|
21412
|
-
console.log(" Usage: " +
|
|
21387
|
+
console.log(import_chalk30.default.cyan("\n\u{1F6E1}\uFE0F Node9 Setup \u2014 integrate with your AI agent\n"));
|
|
21388
|
+
console.log(" Usage: " + import_chalk30.default.white("node9 setup <target>") + "\n");
|
|
21413
21389
|
console.log(" Targets:");
|
|
21414
|
-
console.log(" " +
|
|
21415
|
-
console.log(" " +
|
|
21416
|
-
console.log(" " +
|
|
21417
|
-
console.log(" " +
|
|
21418
|
-
console.log(" " +
|
|
21419
|
-
console.log(" " +
|
|
21390
|
+
console.log(" " + import_chalk30.default.green("claude") + " \u2014 Claude Code (hook mode)");
|
|
21391
|
+
console.log(" " + import_chalk30.default.green("gemini") + " \u2014 Gemini CLI (hook mode)");
|
|
21392
|
+
console.log(" " + import_chalk30.default.green("cursor") + " \u2014 Cursor (MCP proxy)");
|
|
21393
|
+
console.log(" " + import_chalk30.default.green("codex") + " \u2014 OpenAI Codex CLI (MCP proxy)");
|
|
21394
|
+
console.log(" " + import_chalk30.default.green("windsurf") + " \u2014 Windsurf (MCP proxy)");
|
|
21395
|
+
console.log(" " + import_chalk30.default.green("vscode") + " \u2014 VSCode / Copilot (MCP proxy)");
|
|
21420
21396
|
process.stdout.write(
|
|
21421
|
-
" " +
|
|
21397
|
+
" " + import_chalk30.default.green("hud") + " \u2014 Claude Code security statusline\n"
|
|
21422
21398
|
);
|
|
21423
21399
|
console.log("");
|
|
21424
21400
|
return;
|
|
@@ -21432,13 +21408,13 @@ program.command("setup").description('Alias for "addto" \u2014 integrate Node9 w
|
|
|
21432
21408
|
if (t === "vscode") return await setupVSCode();
|
|
21433
21409
|
if (t === "hud") return setupHud();
|
|
21434
21410
|
console.error(
|
|
21435
|
-
|
|
21411
|
+
import_chalk30.default.red(
|
|
21436
21412
|
`Unknown target: "${target}". Supported: claude, gemini, cursor, codex, windsurf, vscode, hud`
|
|
21437
21413
|
)
|
|
21438
21414
|
);
|
|
21439
21415
|
process.exit(1);
|
|
21440
21416
|
});
|
|
21441
|
-
program.command("removefrom").description("Remove Node9 hooks from an AI agent configuration").addHelpText(
|
|
21417
|
+
program.command("removefrom", { hidden: true }).description("Remove Node9 hooks from an AI agent configuration").addHelpText(
|
|
21442
21418
|
"after",
|
|
21443
21419
|
"\n Supported targets: claude gemini cursor codex windsurf vscode hud"
|
|
21444
21420
|
).argument(
|
|
@@ -21455,33 +21431,33 @@ program.command("removefrom").description("Remove Node9 hooks from an AI agent c
|
|
|
21455
21431
|
else if (target === "hud") fn = teardownHud;
|
|
21456
21432
|
else {
|
|
21457
21433
|
console.error(
|
|
21458
|
-
|
|
21434
|
+
import_chalk30.default.red(
|
|
21459
21435
|
`Unknown target: "${target}". Supported: claude, gemini, cursor, codex, windsurf, vscode, hud`
|
|
21460
21436
|
)
|
|
21461
21437
|
);
|
|
21462
21438
|
process.exit(1);
|
|
21463
21439
|
}
|
|
21464
|
-
console.log(
|
|
21440
|
+
console.log(import_chalk30.default.cyan(`
|
|
21465
21441
|
\u{1F6E1}\uFE0F Node9: removing hooks from ${target}...
|
|
21466
21442
|
`));
|
|
21467
21443
|
try {
|
|
21468
21444
|
fn();
|
|
21469
21445
|
} catch (err2) {
|
|
21470
|
-
console.error(
|
|
21446
|
+
console.error(import_chalk30.default.red(` \u26A0\uFE0F Failed: ${err2 instanceof Error ? err2.message : String(err2)}`));
|
|
21471
21447
|
process.exit(1);
|
|
21472
21448
|
}
|
|
21473
|
-
console.log(
|
|
21449
|
+
console.log(import_chalk30.default.gray("\n Restart the agent for changes to take effect."));
|
|
21474
21450
|
});
|
|
21475
21451
|
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) => {
|
|
21476
|
-
console.log(
|
|
21477
|
-
console.log(
|
|
21452
|
+
console.log(import_chalk30.default.cyan("\n\u{1F6E1}\uFE0F Node9 Uninstall\n"));
|
|
21453
|
+
console.log(import_chalk30.default.bold("Stopping daemon..."));
|
|
21478
21454
|
try {
|
|
21479
21455
|
stopDaemon();
|
|
21480
|
-
console.log(
|
|
21456
|
+
console.log(import_chalk30.default.green(" \u2705 Daemon stopped"));
|
|
21481
21457
|
} catch {
|
|
21482
|
-
console.log(
|
|
21458
|
+
console.log(import_chalk30.default.blue(" \u2139\uFE0F Daemon was not running"));
|
|
21483
21459
|
}
|
|
21484
|
-
console.log(
|
|
21460
|
+
console.log(import_chalk30.default.bold("\nRemoving hooks..."));
|
|
21485
21461
|
let teardownFailed = false;
|
|
21486
21462
|
for (const [label, fn] of [
|
|
21487
21463
|
["Claude", teardownClaude],
|
|
@@ -21496,7 +21472,7 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
21496
21472
|
} catch (err2) {
|
|
21497
21473
|
teardownFailed = true;
|
|
21498
21474
|
console.error(
|
|
21499
|
-
|
|
21475
|
+
import_chalk30.default.red(
|
|
21500
21476
|
` \u26A0\uFE0F Failed to remove ${label} hooks: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
21501
21477
|
)
|
|
21502
21478
|
);
|
|
@@ -21513,28 +21489,28 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
21513
21489
|
import_fs47.default.rmSync(node9Dir, { recursive: true });
|
|
21514
21490
|
if (import_fs47.default.existsSync(node9Dir)) {
|
|
21515
21491
|
console.error(
|
|
21516
|
-
|
|
21492
|
+
import_chalk30.default.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
|
|
21517
21493
|
);
|
|
21518
21494
|
} else {
|
|
21519
|
-
console.log(
|
|
21495
|
+
console.log(import_chalk30.default.green("\n \u2705 Deleted ~/.node9/ (config, audit log, credentials)"));
|
|
21520
21496
|
}
|
|
21521
21497
|
} else {
|
|
21522
|
-
console.log(
|
|
21498
|
+
console.log(import_chalk30.default.yellow("\n Skipped \u2014 ~/.node9/ was not deleted."));
|
|
21523
21499
|
}
|
|
21524
21500
|
} else {
|
|
21525
|
-
console.log(
|
|
21501
|
+
console.log(import_chalk30.default.blue("\n \u2139\uFE0F ~/.node9/ not found \u2014 nothing to delete"));
|
|
21526
21502
|
}
|
|
21527
21503
|
} else {
|
|
21528
21504
|
console.log(
|
|
21529
|
-
|
|
21505
|
+
import_chalk30.default.gray("\n ~/.node9/ kept \u2014 run with --purge to delete config and audit log")
|
|
21530
21506
|
);
|
|
21531
21507
|
}
|
|
21532
21508
|
if (teardownFailed) {
|
|
21533
|
-
console.error(
|
|
21509
|
+
console.error(import_chalk30.default.red("\n \u26A0\uFE0F Some hooks could not be removed \u2014 see errors above."));
|
|
21534
21510
|
process.exit(1);
|
|
21535
21511
|
}
|
|
21536
|
-
console.log(
|
|
21537
|
-
console.log(
|
|
21512
|
+
console.log(import_chalk30.default.green.bold("\n\u{1F6E1}\uFE0F Node9 removed. Run: npm uninstall -g @node9/proxy"));
|
|
21513
|
+
console.log(import_chalk30.default.gray(" Restart any open AI agent sessions for changes to take effect.\n"));
|
|
21538
21514
|
});
|
|
21539
21515
|
registerDoctorCommand(program, version);
|
|
21540
21516
|
program.command("explain").description(
|
|
@@ -21547,7 +21523,7 @@ program.command("explain").description(
|
|
|
21547
21523
|
try {
|
|
21548
21524
|
args = JSON.parse(trimmed);
|
|
21549
21525
|
} catch {
|
|
21550
|
-
console.error(
|
|
21526
|
+
console.error(import_chalk30.default.red(`
|
|
21551
21527
|
\u274C Invalid JSON: ${trimmed}
|
|
21552
21528
|
`));
|
|
21553
21529
|
process.exit(1);
|
|
@@ -21558,54 +21534,54 @@ program.command("explain").description(
|
|
|
21558
21534
|
}
|
|
21559
21535
|
const result = await explainPolicy(tool, args);
|
|
21560
21536
|
console.log("");
|
|
21561
|
-
console.log(
|
|
21537
|
+
console.log(import_chalk30.default.cyan.bold("\u{1F6E1}\uFE0F Node9 Explain"));
|
|
21562
21538
|
console.log("");
|
|
21563
|
-
console.log(` ${
|
|
21539
|
+
console.log(` ${import_chalk30.default.bold("Tool:")} ${import_chalk30.default.white(result.tool)}`);
|
|
21564
21540
|
if (argsRaw) {
|
|
21565
21541
|
const preview2 = argsRaw.length > 80 ? argsRaw.slice(0, 77) + "\u2026" : argsRaw;
|
|
21566
|
-
console.log(` ${
|
|
21542
|
+
console.log(` ${import_chalk30.default.bold("Input:")} ${import_chalk30.default.gray(preview2)}`);
|
|
21567
21543
|
}
|
|
21568
21544
|
console.log("");
|
|
21569
|
-
console.log(
|
|
21545
|
+
console.log(import_chalk30.default.bold("Config Sources (Waterfall):"));
|
|
21570
21546
|
for (const tier of result.waterfall) {
|
|
21571
|
-
const num3 =
|
|
21547
|
+
const num3 = import_chalk30.default.gray(` ${tier.tier}.`);
|
|
21572
21548
|
const label = tier.label.padEnd(16);
|
|
21573
21549
|
let statusStr;
|
|
21574
21550
|
if (tier.tier === 1) {
|
|
21575
|
-
statusStr =
|
|
21551
|
+
statusStr = import_chalk30.default.gray(tier.note ?? "");
|
|
21576
21552
|
} else if (tier.status === "active") {
|
|
21577
|
-
const loc = tier.path ?
|
|
21578
|
-
const note = tier.note ?
|
|
21579
|
-
statusStr =
|
|
21553
|
+
const loc = tier.path ? import_chalk30.default.gray(tier.path) : "";
|
|
21554
|
+
const note = tier.note ? import_chalk30.default.gray(`(${tier.note})`) : "";
|
|
21555
|
+
statusStr = import_chalk30.default.green("\u2713 active") + (loc ? " " + loc : "") + (note ? " " + note : "");
|
|
21580
21556
|
} else {
|
|
21581
|
-
statusStr =
|
|
21557
|
+
statusStr = import_chalk30.default.gray("\u25CB " + (tier.note ?? "not found"));
|
|
21582
21558
|
}
|
|
21583
|
-
console.log(`${num3} ${
|
|
21559
|
+
console.log(`${num3} ${import_chalk30.default.white(label)} ${statusStr}`);
|
|
21584
21560
|
}
|
|
21585
21561
|
console.log("");
|
|
21586
|
-
console.log(
|
|
21562
|
+
console.log(import_chalk30.default.bold("Policy Evaluation:"));
|
|
21587
21563
|
for (const step of result.steps) {
|
|
21588
21564
|
const isFinal = step.isFinal;
|
|
21589
21565
|
let icon;
|
|
21590
|
-
if (step.outcome === "allow") icon =
|
|
21591
|
-
else if (step.outcome === "review") icon =
|
|
21592
|
-
else if (step.outcome === "skip") icon =
|
|
21593
|
-
else icon =
|
|
21566
|
+
if (step.outcome === "allow") icon = import_chalk30.default.green(" \u2705");
|
|
21567
|
+
else if (step.outcome === "review") icon = import_chalk30.default.red(" \u{1F534}");
|
|
21568
|
+
else if (step.outcome === "skip") icon = import_chalk30.default.gray(" \u2500 ");
|
|
21569
|
+
else icon = import_chalk30.default.gray(" \u25CB ");
|
|
21594
21570
|
const name = step.name.padEnd(18);
|
|
21595
|
-
const nameStr = isFinal ?
|
|
21596
|
-
const detail = isFinal ?
|
|
21597
|
-
const arrow = isFinal ?
|
|
21571
|
+
const nameStr = isFinal ? import_chalk30.default.white.bold(name) : import_chalk30.default.white(name);
|
|
21572
|
+
const detail = isFinal ? import_chalk30.default.white(step.detail) : import_chalk30.default.gray(step.detail);
|
|
21573
|
+
const arrow = isFinal ? import_chalk30.default.yellow(" \u2190 STOP") : "";
|
|
21598
21574
|
console.log(`${icon} ${nameStr} ${detail}${arrow}`);
|
|
21599
21575
|
}
|
|
21600
21576
|
console.log("");
|
|
21601
21577
|
if (result.decision === "allow") {
|
|
21602
|
-
console.log(
|
|
21578
|
+
console.log(import_chalk30.default.green.bold(" Decision: \u2705 ALLOW") + import_chalk30.default.gray(" \u2014 no approval needed"));
|
|
21603
21579
|
} else {
|
|
21604
21580
|
console.log(
|
|
21605
|
-
|
|
21581
|
+
import_chalk30.default.red.bold(" Decision: \u{1F534} REVIEW") + import_chalk30.default.gray(" \u2014 human approval required")
|
|
21606
21582
|
);
|
|
21607
21583
|
if (result.blockedByLabel) {
|
|
21608
|
-
console.log(
|
|
21584
|
+
console.log(import_chalk30.default.gray(` Reason: ${result.blockedByLabel}`));
|
|
21609
21585
|
}
|
|
21610
21586
|
}
|
|
21611
21587
|
console.log("");
|
|
@@ -21620,7 +21596,7 @@ program.command("tail").description("Stream live agent activity to the terminal"
|
|
|
21620
21596
|
try {
|
|
21621
21597
|
await startTail2(options);
|
|
21622
21598
|
} catch (err2) {
|
|
21623
|
-
console.error(
|
|
21599
|
+
console.error(import_chalk30.default.red(`\u274C ${err2 instanceof Error ? err2.message : String(err2)}`));
|
|
21624
21600
|
process.exit(1);
|
|
21625
21601
|
}
|
|
21626
21602
|
});
|
|
@@ -21631,11 +21607,10 @@ program.command("monitor").description("Live interactive dashboard \u2014 activi
|
|
|
21631
21607
|
const mod = await dynamicImport(`file://${dashboardPath}`);
|
|
21632
21608
|
await mod.startMonitor();
|
|
21633
21609
|
} catch (err2) {
|
|
21634
|
-
console.error(
|
|
21610
|
+
console.error(import_chalk30.default.red(`\u274C ${err2 instanceof Error ? err2.message : String(err2)}`));
|
|
21635
21611
|
process.exit(1);
|
|
21636
21612
|
}
|
|
21637
21613
|
});
|
|
21638
|
-
registerWatchCommand(program);
|
|
21639
21614
|
registerMcpGatewayCommand(program);
|
|
21640
21615
|
registerMcpServerCommand(program);
|
|
21641
21616
|
registerMcpPinCommand(program);
|
|
@@ -21643,7 +21618,7 @@ registerSkillPinCommand(program);
|
|
|
21643
21618
|
registerDecisionsCommand(program);
|
|
21644
21619
|
registerCheckCommand(program);
|
|
21645
21620
|
registerLogCommand(program);
|
|
21646
|
-
program.command("hud").description("Render node9 security statusline (spawned by Claude Code statusLine)").addHelpText(
|
|
21621
|
+
program.command("hud", { hidden: true }).description("Render node9 security statusline (spawned by Claude Code statusLine)").addHelpText(
|
|
21647
21622
|
"after",
|
|
21648
21623
|
`
|
|
21649
21624
|
Outputs up to 3 lines to stdout, then exits:
|
|
@@ -21687,7 +21662,7 @@ program.command("pause").description("Temporarily disable Node9 protection for a
|
|
|
21687
21662
|
const ms = parseDuration(options.duration);
|
|
21688
21663
|
if (ms === null) {
|
|
21689
21664
|
console.error(
|
|
21690
|
-
|
|
21665
|
+
import_chalk30.default.red(`
|
|
21691
21666
|
\u274C Invalid duration: "${options.duration}". Use format like 15m, 1h, 30s.
|
|
21692
21667
|
`)
|
|
21693
21668
|
);
|
|
@@ -21695,20 +21670,20 @@ program.command("pause").description("Temporarily disable Node9 protection for a
|
|
|
21695
21670
|
}
|
|
21696
21671
|
pauseNode9(ms, options.duration);
|
|
21697
21672
|
const expiresAt = new Date(Date.now() + ms).toLocaleTimeString();
|
|
21698
|
-
console.log(
|
|
21673
|
+
console.log(import_chalk30.default.yellow(`
|
|
21699
21674
|
\u23F8 Node9 paused until ${expiresAt}`));
|
|
21700
|
-
console.log(
|
|
21701
|
-
console.log(
|
|
21675
|
+
console.log(import_chalk30.default.gray(` All tool calls will be allowed without review.`));
|
|
21676
|
+
console.log(import_chalk30.default.gray(` Run "node9 resume" to re-enable early.
|
|
21702
21677
|
`));
|
|
21703
21678
|
});
|
|
21704
21679
|
program.command("resume").description("Re-enable Node9 protection immediately").action(() => {
|
|
21705
21680
|
const { paused } = checkPause();
|
|
21706
21681
|
if (!paused) {
|
|
21707
|
-
console.log(
|
|
21682
|
+
console.log(import_chalk30.default.gray("\nNode9 is already active \u2014 nothing to resume.\n"));
|
|
21708
21683
|
return;
|
|
21709
21684
|
}
|
|
21710
21685
|
resumeNode9();
|
|
21711
|
-
console.log(
|
|
21686
|
+
console.log(import_chalk30.default.green("\n\u25B6 Node9 resumed \u2014 protection is active.\n"));
|
|
21712
21687
|
});
|
|
21713
21688
|
var HOOK_BASED_AGENTS = {
|
|
21714
21689
|
claude: "claude",
|
|
@@ -21721,15 +21696,15 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
21721
21696
|
if (HOOK_BASED_AGENTS[firstArg2] !== void 0) {
|
|
21722
21697
|
const target = HOOK_BASED_AGENTS[firstArg2];
|
|
21723
21698
|
console.error(
|
|
21724
|
-
|
|
21699
|
+
import_chalk30.default.yellow(`
|
|
21725
21700
|
\u26A0\uFE0F Node9 proxy mode does not support "${target}" directly.`)
|
|
21726
21701
|
);
|
|
21727
|
-
console.error(
|
|
21702
|
+
console.error(import_chalk30.default.white(`
|
|
21728
21703
|
"${target}" uses its own hook system. Use:`));
|
|
21729
21704
|
console.error(
|
|
21730
|
-
|
|
21705
|
+
import_chalk30.default.green(` node9 addto ${target} `) + import_chalk30.default.gray("# one-time setup")
|
|
21731
21706
|
);
|
|
21732
|
-
console.error(
|
|
21707
|
+
console.error(import_chalk30.default.green(` ${target} `) + import_chalk30.default.gray("# run normally"));
|
|
21733
21708
|
process.exit(1);
|
|
21734
21709
|
}
|
|
21735
21710
|
const runArgs = firstArg2 === "shell" ? commandArgs.slice(1) : commandArgs;
|
|
@@ -21746,7 +21721,7 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
21746
21721
|
}
|
|
21747
21722
|
);
|
|
21748
21723
|
if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && getConfig().settings.autoStartDaemon) {
|
|
21749
|
-
console.error(
|
|
21724
|
+
console.error(import_chalk30.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically..."));
|
|
21750
21725
|
const daemonReady = await autoStartDaemonAndWait();
|
|
21751
21726
|
if (daemonReady) result = await authorizeHeadless("shell", { command: fullCommand });
|
|
21752
21727
|
}
|
|
@@ -21759,12 +21734,12 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
21759
21734
|
}
|
|
21760
21735
|
if (!result.approved) {
|
|
21761
21736
|
console.error(
|
|
21762
|
-
|
|
21737
|
+
import_chalk30.default.red(`
|
|
21763
21738
|
\u274C Node9 Blocked: ${result.reason || "Dangerous command detected."}`)
|
|
21764
21739
|
);
|
|
21765
21740
|
process.exit(1);
|
|
21766
21741
|
}
|
|
21767
|
-
console.error(
|
|
21742
|
+
console.error(import_chalk30.default.green("\n\u2705 Approved \u2014 running command...\n"));
|
|
21768
21743
|
await runProxy(fullCommand);
|
|
21769
21744
|
} else {
|
|
21770
21745
|
program.help();
|