@node9/proxy 1.20.1 → 1.21.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -36
- package/dist/cli.js +367 -403
- package/dist/cli.mjs +366 -402
- package/dist/scan-ink.mjs +1681 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -10505,7 +10505,7 @@ function originForRule(ruleName, sections) {
|
|
|
10505
10505
|
return "";
|
|
10506
10506
|
}
|
|
10507
10507
|
function registerScanCommand(program2) {
|
|
10508
|
-
program2.command("scan").description("Forecast: scan agent history and show what node9 would catch if installed").option("--all", "Scan all history (default: last 90 days)").option("--days <n>", "Scan last N days of history", "90").option("--top <n>", "Max findings to show per rule (default: 5)", "5").option("--drill-down", "Show all findings with full commands and session IDs").option("--compact", "Compact one-screen scorecard \u2014 for screenshots and sharing").option("--narrative", "Severity-grouped report \u2014 for video / dramatic sharing").option(
|
|
10508
|
+
program2.command("scan").description("Forecast: scan agent history and show what node9 would catch if installed").option("--all", "Scan all history (default: last 90 days)").option("--days <n>", "Scan last N days of history", "90").option("--top <n>", "Max findings to show per rule (default: 5)", "5").option("--drill-down", "Show all findings with full commands and session IDs").option("--compact", "Compact one-screen scorecard \u2014 for screenshots and sharing").option("--narrative", "Severity-grouped report \u2014 for video / dramatic sharing").option("--classic", "Original chalk-based scorecard layout (default: new Ink-rendered view)").option(
|
|
10509
10509
|
"--json",
|
|
10510
10510
|
"Emit machine-readable JSON to stdout (suppresses banner, progress, and renderer)"
|
|
10511
10511
|
).option(
|
|
@@ -10539,7 +10539,8 @@ function registerScanCommand(program2) {
|
|
|
10539
10539
|
})();
|
|
10540
10540
|
const isWired = getAgentsStatus().some((a) => a.wired);
|
|
10541
10541
|
const screenshotMode = options.compact || options.narrative;
|
|
10542
|
-
const
|
|
10542
|
+
const useInk = !options.classic && !drillDown;
|
|
10543
|
+
const quiet = screenshotMode || options.json || useInk;
|
|
10543
10544
|
if (!quiet) {
|
|
10544
10545
|
console.log("");
|
|
10545
10546
|
if (!isWired) {
|
|
@@ -10677,6 +10678,7 @@ function registerScanCommand(program2) {
|
|
|
10677
10678
|
});
|
|
10678
10679
|
return;
|
|
10679
10680
|
}
|
|
10681
|
+
const useInkForHero = !options.classic && !drillDown;
|
|
10680
10682
|
if (totalFindings === 0 && scan.dlpFindings.length === 0) {
|
|
10681
10683
|
console.log(import_chalk5.default.green(" \u2705 No risky operations found in your history."));
|
|
10682
10684
|
console.log(
|
|
@@ -10696,9 +10698,11 @@ function registerScanCommand(program2) {
|
|
|
10696
10698
|
const since = daysAgo === 0 ? "today" : daysAgo === 1 ? "yesterday" : `${daysAgo} days ago`;
|
|
10697
10699
|
return import_chalk5.default.dim(" \xB7 ") + arrow + import_chalk5.default.dim(` since ${since}`);
|
|
10698
10700
|
})();
|
|
10699
|
-
|
|
10700
|
-
|
|
10701
|
-
|
|
10701
|
+
if (!useInkForHero) {
|
|
10702
|
+
console.log(
|
|
10703
|
+
" " + (score.band === "critical" ? import_chalk5.default.red.bold("\u26A0 ") : "") + import_chalk5.default.bold("Security Score ") + score.color.bold(`${blast.score}/100`) + " " + severityDisplay + trendSuffix + import_chalk5.default.dim(" \xB7 ") + (totalRisky > 0 ? import_chalk5.default.red.bold(`${totalRisky} risky operation${totalRisky !== 1 ? "s" : ""}`) : import_chalk5.default.green("No risky operations"))
|
|
10704
|
+
);
|
|
10705
|
+
}
|
|
10702
10706
|
const cardParts = [];
|
|
10703
10707
|
if (scan.dlpFindings.length > 0) {
|
|
10704
10708
|
cardParts.push(
|
|
@@ -10727,10 +10731,10 @@ function registerScanCommand(program2) {
|
|
|
10727
10731
|
import_chalk5.default.red("\u{1F52D} ") + import_chalk5.default.red.bold(String(blastExposures)) + import_chalk5.default.dim(" exposures")
|
|
10728
10732
|
);
|
|
10729
10733
|
}
|
|
10730
|
-
if (cardParts.length > 0) {
|
|
10734
|
+
if (cardParts.length > 0 && !useInkForHero) {
|
|
10731
10735
|
console.log(" " + cardParts.join(import_chalk5.default.dim(" ")));
|
|
10732
10736
|
}
|
|
10733
|
-
if (scan.totalCostUSD > 0) {
|
|
10737
|
+
if (scan.totalCostUSD > 0 && !useInkForHero) {
|
|
10734
10738
|
console.log(
|
|
10735
10739
|
" " + import_chalk5.default.dim("AI spend ") + import_chalk5.default.bold(fmtCost(scan.totalCostUSD)) + (summary.loopWastedUSD > 0 ? import_chalk5.default.dim(" \xB7 wasted on loops ") + import_chalk5.default.yellow("~" + fmtCost(summary.loopWastedUSD)) : "")
|
|
10736
10740
|
);
|
|
@@ -10742,16 +10746,38 @@ function registerScanCommand(program2) {
|
|
|
10742
10746
|
)
|
|
10743
10747
|
);
|
|
10744
10748
|
}
|
|
10745
|
-
|
|
10749
|
+
if (!useInkForHero) {
|
|
10750
|
+
console.log("");
|
|
10751
|
+
}
|
|
10746
10752
|
if (!drillDown) {
|
|
10747
|
-
|
|
10748
|
-
|
|
10749
|
-
|
|
10750
|
-
|
|
10751
|
-
|
|
10752
|
-
|
|
10753
|
-
|
|
10754
|
-
|
|
10753
|
+
const useInk2 = !options.classic;
|
|
10754
|
+
if (useInk2) {
|
|
10755
|
+
const scanInkPath = import_path21.default.join(__dirname, "scan-ink.mjs");
|
|
10756
|
+
const dynamicImport = new Function("id", "return import(id)");
|
|
10757
|
+
const mod = await dynamicImport(`file://${scanInkPath}`);
|
|
10758
|
+
const rangeLabel2 = options.all ? "all time" : `last ${options.days ?? 90} days`;
|
|
10759
|
+
mod.renderScanScorecardInk(
|
|
10760
|
+
{
|
|
10761
|
+
scan,
|
|
10762
|
+
summary,
|
|
10763
|
+
blast,
|
|
10764
|
+
blastExposures,
|
|
10765
|
+
blockedCount,
|
|
10766
|
+
reviewCount
|
|
10767
|
+
},
|
|
10768
|
+
rangeLabel2
|
|
10769
|
+
);
|
|
10770
|
+
console.log("");
|
|
10771
|
+
} else {
|
|
10772
|
+
renderPanelScorecard({
|
|
10773
|
+
scan,
|
|
10774
|
+
summary,
|
|
10775
|
+
blast,
|
|
10776
|
+
blastExposures,
|
|
10777
|
+
blockedCount,
|
|
10778
|
+
reviewCount
|
|
10779
|
+
});
|
|
10780
|
+
}
|
|
10755
10781
|
const cta = isWired ? "\u2705 node9 is active" : "\u2192 install node9 to enable protection";
|
|
10756
10782
|
console.log(" " + import_chalk5.default.green(cta));
|
|
10757
10783
|
console.log(
|
|
@@ -13702,10 +13728,10 @@ function readSessionUsage() {
|
|
|
13702
13728
|
}
|
|
13703
13729
|
}
|
|
13704
13730
|
function formatContextStat(stat) {
|
|
13705
|
-
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;
|
|
13706
13732
|
const k = (n) => `${Math.round(n / 1e3)}k`;
|
|
13707
13733
|
const modelShort = stat.model.replace(/@.*$/, "").replace(/-\d{8}$/, "").replace(/^claude-/, "");
|
|
13708
|
-
return
|
|
13734
|
+
return import_chalk29.default.dim("ctx: ") + pctColor(`${stat.fillPct}%`) + import_chalk29.default.dim(
|
|
13709
13735
|
` (${k(stat.inputTokens)}/${k(getModelContextLimit(stat.model))} out ${k(stat.outputTokens)} \xB7 ${modelShort})`
|
|
13710
13736
|
);
|
|
13711
13737
|
}
|
|
@@ -13728,11 +13754,11 @@ function agentLabel(agent, mcpServer, sessionId) {
|
|
|
13728
13754
|
const tag = sessionTag(sessionId);
|
|
13729
13755
|
const tagSuffix = tag ? `\xB7${tag}` : "";
|
|
13730
13756
|
if (!agent || agent === "Terminal") {
|
|
13731
|
-
return mcpServer ?
|
|
13757
|
+
return mcpServer ? import_chalk29.default.dim(`[\u2192 ${mcpServer}] `) : "";
|
|
13732
13758
|
}
|
|
13733
13759
|
const short = agent === "Claude Code" ? "Claude" : agent === "Gemini CLI" ? "Gemini" : agent === "Unknown Agent" ? "" : agent.split(" ")[0];
|
|
13734
|
-
if (!short) return mcpServer ?
|
|
13735
|
-
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}] `);
|
|
13736
13762
|
}
|
|
13737
13763
|
function formatBase(activity) {
|
|
13738
13764
|
const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
|
|
@@ -13740,20 +13766,20 @@ function formatBase(activity) {
|
|
|
13740
13766
|
const toolName = activity.tool.slice(0, 16).padEnd(16);
|
|
13741
13767
|
const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(import_os41.default.homedir(), "~");
|
|
13742
13768
|
const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
|
|
13743
|
-
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)}`;
|
|
13744
13770
|
}
|
|
13745
13771
|
function renderResult(activity, result) {
|
|
13746
13772
|
const base = formatBase(activity);
|
|
13747
13773
|
let status;
|
|
13748
13774
|
if (result.status === "allow") {
|
|
13749
|
-
status =
|
|
13775
|
+
status = import_chalk29.default.green("\u2713 ALLOW");
|
|
13750
13776
|
} else if (result.status === "dlp") {
|
|
13751
|
-
status =
|
|
13777
|
+
status = import_chalk29.default.bgRed.white.bold(" \u{1F6E1}\uFE0F DLP ");
|
|
13752
13778
|
} else {
|
|
13753
|
-
status =
|
|
13779
|
+
status = import_chalk29.default.red("\u2717 BLOCK");
|
|
13754
13780
|
}
|
|
13755
13781
|
const cost = result.costEstimate ?? activity.costEstimate;
|
|
13756
|
-
const costSuffix = cost == null ? "" :
|
|
13782
|
+
const costSuffix = cost == null ? "" : import_chalk29.default.dim(` ~$${cost >= 1e-3 ? cost.toFixed(3) : "0.000"}`);
|
|
13757
13783
|
if (process.stdout.isTTY) {
|
|
13758
13784
|
if (pendingShownForId === activity.id && pendingWrappedLines > 1) {
|
|
13759
13785
|
import_readline6.default.moveCursor(process.stdout, 0, -(pendingWrappedLines - 1));
|
|
@@ -13770,7 +13796,7 @@ function renderResult(activity, result) {
|
|
|
13770
13796
|
}
|
|
13771
13797
|
function renderPending(activity) {
|
|
13772
13798
|
if (!process.stdout.isTTY) return;
|
|
13773
|
-
const line = `${formatBase(activity)} ${
|
|
13799
|
+
const line = `${formatBase(activity)} ${import_chalk29.default.yellow("\u25CF \u2026")}`;
|
|
13774
13800
|
pendingShownForId = activity.id;
|
|
13775
13801
|
pendingWrappedLines = wrappedLineCount(line);
|
|
13776
13802
|
process.stdout.write(`${line}\r`);
|
|
@@ -13782,7 +13808,7 @@ async function ensureDaemon() {
|
|
|
13782
13808
|
const { port } = JSON.parse(import_fs45.default.readFileSync(PID_FILE, "utf-8"));
|
|
13783
13809
|
pidPort = port;
|
|
13784
13810
|
} catch {
|
|
13785
|
-
console.error(
|
|
13811
|
+
console.error(import_chalk29.default.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
|
|
13786
13812
|
}
|
|
13787
13813
|
}
|
|
13788
13814
|
const checkPort = pidPort ?? DAEMON_PORT;
|
|
@@ -13793,8 +13819,8 @@ async function ensureDaemon() {
|
|
|
13793
13819
|
if (res.ok) return checkPort;
|
|
13794
13820
|
} catch {
|
|
13795
13821
|
}
|
|
13796
|
-
console.log(
|
|
13797
|
-
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"], {
|
|
13798
13824
|
detached: true,
|
|
13799
13825
|
stdio: "ignore",
|
|
13800
13826
|
env: { ...process.env, NODE9_AUTO_STARTED: "1" }
|
|
@@ -13810,7 +13836,7 @@ async function ensureDaemon() {
|
|
|
13810
13836
|
} catch {
|
|
13811
13837
|
}
|
|
13812
13838
|
}
|
|
13813
|
-
console.error(
|
|
13839
|
+
console.error(import_chalk29.default.red("\u274C Daemon failed to start. Try: node9 daemon start"));
|
|
13814
13840
|
process.exit(1);
|
|
13815
13841
|
}
|
|
13816
13842
|
function postDecisionHttp(id, decision, authToken, port, opts) {
|
|
@@ -13879,7 +13905,7 @@ function buildCardLines(req, localCount = 0) {
|
|
|
13879
13905
|
const severityIcon = isBlock ? `${RED}\u{1F6D1}` : `${YELLOW}\u26A0 `;
|
|
13880
13906
|
const rawDesc = req.riskMetadata?.ruleDescription ?? "";
|
|
13881
13907
|
const description = rawDesc ? cleanReason(rawDesc) : "";
|
|
13882
|
-
const agentSuffix = req.agent && req.agent !== "Terminal" ? ` ${RESET2}${
|
|
13908
|
+
const agentSuffix = req.agent && req.agent !== "Terminal" ? ` ${RESET2}${import_chalk29.default.dim(`(${req.agent})`)}` : "";
|
|
13883
13909
|
const lines = [
|
|
13884
13910
|
``,
|
|
13885
13911
|
`${BOLD2}${CYAN}\u2554\u2550\u2550 Node9 Approval Required \u2550\u2550\u2557${RESET2}`,
|
|
@@ -13948,7 +13974,7 @@ function approverStatusLine() {
|
|
|
13948
13974
|
const a = readApproversFromDisk();
|
|
13949
13975
|
const fmt = (label, key) => {
|
|
13950
13976
|
const on = a[key] !== false;
|
|
13951
|
-
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")}`;
|
|
13952
13978
|
};
|
|
13953
13979
|
return `${fmt("native", "native")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
|
|
13954
13980
|
}
|
|
@@ -13993,7 +14019,7 @@ async function startTail(options = {}) {
|
|
|
13993
14019
|
req2.end();
|
|
13994
14020
|
});
|
|
13995
14021
|
if (result.ok) {
|
|
13996
|
-
console.log(
|
|
14022
|
+
console.log(import_chalk29.default.green("\u2713 Flight Recorder buffer cleared."));
|
|
13997
14023
|
} else if (result.code === "ECONNREFUSED") {
|
|
13998
14024
|
throw new Error("Daemon is not running. Start it with: node9 daemon start");
|
|
13999
14025
|
} else if (result.code === "ETIMEDOUT") {
|
|
@@ -14039,7 +14065,7 @@ async function startTail(options = {}) {
|
|
|
14039
14065
|
const channel = name === "n" ? "native" : name === "c" ? "cloud" : name === "t" ? "terminal" : null;
|
|
14040
14066
|
if (channel) {
|
|
14041
14067
|
toggleApprover(channel);
|
|
14042
|
-
console.log(
|
|
14068
|
+
console.log(import_chalk29.default.dim(` Approvers: ${approverStatusLine()}`));
|
|
14043
14069
|
}
|
|
14044
14070
|
};
|
|
14045
14071
|
process.stdin.on("keypress", idleKeypressHandler);
|
|
@@ -14105,7 +14131,7 @@ async function startTail(options = {}) {
|
|
|
14105
14131
|
localAllowCounts.get(req2.toolName) ?? 0
|
|
14106
14132
|
)
|
|
14107
14133
|
);
|
|
14108
|
-
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");
|
|
14109
14135
|
stampedLines.push(` ${BOLD2}\u2192${RESET2} ${decisionStamp} ${GRAY}(terminal)${RESET2}`, ``);
|
|
14110
14136
|
for (const line of stampedLines) process.stdout.write(line + "\n");
|
|
14111
14137
|
process.stdout.write(SHOW_CURSOR);
|
|
@@ -14156,7 +14182,7 @@ async function startTail(options = {}) {
|
|
|
14156
14182
|
);
|
|
14157
14183
|
const stampedLines = buildCardLines(req2, priorCount);
|
|
14158
14184
|
if (externalDecision) {
|
|
14159
|
-
const source = externalDecision === "allow" ?
|
|
14185
|
+
const source = externalDecision === "allow" ? import_chalk29.default.green("\u2713 ALLOWED") : import_chalk29.default.red("\u2717 DENIED");
|
|
14160
14186
|
stampedLines.push(` ${BOLD2}\u2192${RESET2} ${source} ${GRAY}(external)${RESET2}`, ``);
|
|
14161
14187
|
}
|
|
14162
14188
|
for (const line of stampedLines) process.stdout.write(line + "\n");
|
|
@@ -14204,25 +14230,25 @@ async function startTail(options = {}) {
|
|
|
14204
14230
|
if (unackedDlp > 0) {
|
|
14205
14231
|
console.log("");
|
|
14206
14232
|
console.log(
|
|
14207
|
-
|
|
14233
|
+
import_chalk29.default.bgRed.white.bold(
|
|
14208
14234
|
` \u26A0\uFE0F DLP ALERT: ${unackedDlp} secret${unackedDlp !== 1 ? "s" : ""} found in Claude response text \u2014 run: node9 dlp `
|
|
14209
14235
|
)
|
|
14210
14236
|
);
|
|
14211
14237
|
}
|
|
14212
14238
|
} catch {
|
|
14213
14239
|
}
|
|
14214
|
-
console.log(
|
|
14240
|
+
console.log(import_chalk29.default.cyan.bold(`
|
|
14215
14241
|
\u{1F6F0}\uFE0F Node9 tail`));
|
|
14216
14242
|
if (canApprove) {
|
|
14217
|
-
console.log(
|
|
14218
|
-
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`));
|
|
14219
14245
|
}
|
|
14220
14246
|
const ctxStat = readSessionUsage();
|
|
14221
14247
|
if (ctxStat) console.log(" " + formatContextStat(ctxStat));
|
|
14222
14248
|
if (options.history) {
|
|
14223
|
-
console.log(
|
|
14249
|
+
console.log(import_chalk29.default.dim("Showing history + live events.\n"));
|
|
14224
14250
|
} else {
|
|
14225
|
-
console.log(
|
|
14251
|
+
console.log(import_chalk29.default.dim("Showing live events only. Use --history to include past.\n"));
|
|
14226
14252
|
}
|
|
14227
14253
|
process.on("SIGINT", () => {
|
|
14228
14254
|
exitIdleMode();
|
|
@@ -14232,7 +14258,7 @@ async function startTail(options = {}) {
|
|
|
14232
14258
|
import_readline6.default.clearLine(process.stdout, 0);
|
|
14233
14259
|
import_readline6.default.cursorTo(process.stdout, 0);
|
|
14234
14260
|
}
|
|
14235
|
-
console.log(
|
|
14261
|
+
console.log(import_chalk29.default.dim("\n\u{1F6F0}\uFE0F Disconnected."));
|
|
14236
14262
|
process.exit(0);
|
|
14237
14263
|
});
|
|
14238
14264
|
const STALL_THRESHOLD_MS = 6e4;
|
|
@@ -14244,7 +14270,7 @@ async function startTail(options = {}) {
|
|
|
14244
14270
|
if (Date.now() - auditMtime >= STALL_THRESHOLD_MS) return;
|
|
14245
14271
|
console.log("");
|
|
14246
14272
|
console.log(
|
|
14247
|
-
|
|
14273
|
+
import_chalk29.default.yellow(
|
|
14248
14274
|
"\u26A0\uFE0F Tail appears stalled \u2014 hooks are firing but no events are arriving. Try: node9 daemon restart"
|
|
14249
14275
|
)
|
|
14250
14276
|
);
|
|
@@ -14261,7 +14287,7 @@ async function startTail(options = {}) {
|
|
|
14261
14287
|
},
|
|
14262
14288
|
(res) => {
|
|
14263
14289
|
if (res.statusCode !== 200) {
|
|
14264
|
-
console.error(
|
|
14290
|
+
console.error(import_chalk29.default.red(`Failed to connect: HTTP ${res.statusCode}`));
|
|
14265
14291
|
process.exit(1);
|
|
14266
14292
|
}
|
|
14267
14293
|
if (canApprove) enterIdleMode();
|
|
@@ -14292,7 +14318,7 @@ async function startTail(options = {}) {
|
|
|
14292
14318
|
import_readline6.default.clearLine(process.stdout, 0);
|
|
14293
14319
|
import_readline6.default.cursorTo(process.stdout, 0);
|
|
14294
14320
|
}
|
|
14295
|
-
console.log(
|
|
14321
|
+
console.log(import_chalk29.default.red("\n\u274C Daemon disconnected."));
|
|
14296
14322
|
process.exit(1);
|
|
14297
14323
|
});
|
|
14298
14324
|
}
|
|
@@ -14305,7 +14331,7 @@ async function startTail(options = {}) {
|
|
|
14305
14331
|
const parsed = JSON.parse(rawData);
|
|
14306
14332
|
const msg = parsed.message ?? "Flight recorder is down \u2014 run: node9 daemon restart";
|
|
14307
14333
|
console.log("");
|
|
14308
|
-
console.log(
|
|
14334
|
+
console.log(import_chalk29.default.bgRed.white.bold(` \u26A0\uFE0F ${msg} `));
|
|
14309
14335
|
} catch {
|
|
14310
14336
|
}
|
|
14311
14337
|
return;
|
|
@@ -14390,9 +14416,9 @@ async function startTail(options = {}) {
|
|
|
14390
14416
|
const rawSummary = data.argsSummary ?? data.tool;
|
|
14391
14417
|
const summary = shortenPathSummary(rawSummary);
|
|
14392
14418
|
const fileCount = data.fileCount ?? 0;
|
|
14393
|
-
const files = fileCount > 0 ?
|
|
14419
|
+
const files = fileCount > 0 ? import_chalk29.default.dim(` \xB7 ${fileCount} file${fileCount === 1 ? "" : "s"}`) : "";
|
|
14394
14420
|
process.stdout.write(
|
|
14395
|
-
`${
|
|
14421
|
+
`${import_chalk29.default.dim(time)} ${import_chalk29.default.cyan("\u{1F4F8} snapshot")} ${import_chalk29.default.dim(hash)} ${summary}${files}
|
|
14396
14422
|
`
|
|
14397
14423
|
);
|
|
14398
14424
|
return;
|
|
@@ -14409,33 +14435,33 @@ async function startTail(options = {}) {
|
|
|
14409
14435
|
if (event === "execution-result") {
|
|
14410
14436
|
const exec = data;
|
|
14411
14437
|
const time = new Date(Date.now()).toLocaleTimeString([], { hour12: false });
|
|
14412
|
-
const arrow = exec.isError ?
|
|
14438
|
+
const arrow = exec.isError ? import_chalk29.default.red(" \u21B3 \u2717") : import_chalk29.default.green(" \u21B3 \u2713");
|
|
14413
14439
|
const label = agentLabel(exec.agent, exec.mcpServer);
|
|
14414
14440
|
const tool = (exec.tool ?? "").slice(0, 16);
|
|
14415
|
-
const duration = typeof exec.durationMs === "number" ?
|
|
14441
|
+
const duration = typeof exec.durationMs === "number" ? import_chalk29.default.dim(` (${exec.durationMs}ms)`) : "";
|
|
14416
14442
|
console.log(
|
|
14417
|
-
`${
|
|
14443
|
+
`${import_chalk29.default.gray(time)} ${arrow} ${label}${import_chalk29.default.dim(tool)}${import_chalk29.default.dim(" completed")}${duration}`
|
|
14418
14444
|
);
|
|
14419
14445
|
}
|
|
14420
14446
|
}
|
|
14421
14447
|
req.on("error", (err2) => {
|
|
14422
14448
|
const msg = err2.code === "ECONNREFUSED" ? "Daemon is not running. Start it with: node9 daemon start" : err2.message;
|
|
14423
|
-
console.error(
|
|
14449
|
+
console.error(import_chalk29.default.red(`
|
|
14424
14450
|
\u274C ${msg}`));
|
|
14425
14451
|
process.exit(1);
|
|
14426
14452
|
});
|
|
14427
14453
|
}
|
|
14428
|
-
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;
|
|
14429
14455
|
var init_tail = __esm({
|
|
14430
14456
|
"src/tui/tail.ts"() {
|
|
14431
14457
|
"use strict";
|
|
14432
14458
|
import_http2 = __toESM(require("http"));
|
|
14433
|
-
|
|
14459
|
+
import_chalk29 = __toESM(require("chalk"));
|
|
14434
14460
|
import_fs45 = __toESM(require("fs"));
|
|
14435
14461
|
import_os41 = __toESM(require("os"));
|
|
14436
14462
|
import_path47 = __toESM(require("path"));
|
|
14437
14463
|
import_readline6 = __toESM(require("readline"));
|
|
14438
|
-
|
|
14464
|
+
import_child_process12 = require("child_process");
|
|
14439
14465
|
init_daemon2();
|
|
14440
14466
|
init_daemon();
|
|
14441
14467
|
PID_FILE = import_path47.default.join(import_os41.default.homedir(), ".node9", "daemon.pid");
|
|
@@ -14863,7 +14889,7 @@ var import_commander = require("commander");
|
|
|
14863
14889
|
init_core();
|
|
14864
14890
|
init_setup();
|
|
14865
14891
|
init_daemon2();
|
|
14866
|
-
var
|
|
14892
|
+
var import_chalk30 = __toESM(require("chalk"));
|
|
14867
14893
|
var import_fs47 = __toESM(require("fs"));
|
|
14868
14894
|
var import_path49 = __toESM(require("path"));
|
|
14869
14895
|
var import_os43 = __toESM(require("os"));
|
|
@@ -15657,7 +15683,7 @@ function detectAiAgent(payload) {
|
|
|
15657
15683
|
return "Terminal";
|
|
15658
15684
|
}
|
|
15659
15685
|
function registerCheckCommand(program2) {
|
|
15660
|
-
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) => {
|
|
15661
15687
|
const processPayload = async (raw) => {
|
|
15662
15688
|
try {
|
|
15663
15689
|
if (!raw || raw.trim() === "") process.exit(0);
|
|
@@ -16055,7 +16081,7 @@ function sanitize3(value) {
|
|
|
16055
16081
|
return value.replace(/[\x00-\x1F\x7F]/g, "");
|
|
16056
16082
|
}
|
|
16057
16083
|
function registerLogCommand(program2) {
|
|
16058
|
-
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) => {
|
|
16059
16085
|
const logPayload = async (raw) => {
|
|
16060
16086
|
try {
|
|
16061
16087
|
if (!raw || raw.trim() === "") process.exit(0);
|
|
@@ -18221,7 +18247,7 @@ function registerInitCommand(program2) {
|
|
|
18221
18247
|
const agentList = found.join(", ");
|
|
18222
18248
|
console.log(import_chalk16.default.green.bold(`\u{1F6E1}\uFE0F Node9 is protecting ${agentList}!`));
|
|
18223
18249
|
console.log("");
|
|
18224
|
-
console.log(import_chalk16.default.white(" Watch live: ") + import_chalk16.default.cyan("node9
|
|
18250
|
+
console.log(import_chalk16.default.white(" Watch live: ") + import_chalk16.default.cyan("node9 monitor"));
|
|
18225
18251
|
console.log("");
|
|
18226
18252
|
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"));
|
|
18227
18253
|
console.log(
|
|
@@ -18518,71 +18544,10 @@ function registerUndoCommand(program2) {
|
|
|
18518
18544
|
});
|
|
18519
18545
|
}
|
|
18520
18546
|
|
|
18521
|
-
// src/cli/commands/watch.ts
|
|
18522
|
-
var import_chalk19 = __toESM(require("chalk"));
|
|
18523
|
-
var import_child_process10 = require("child_process");
|
|
18524
|
-
init_daemon();
|
|
18525
|
-
function registerWatchCommand(program2) {
|
|
18526
|
-
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) => {
|
|
18527
|
-
let port = DAEMON_PORT;
|
|
18528
|
-
try {
|
|
18529
|
-
const res = await fetch(`http://127.0.0.1:${DAEMON_PORT}/settings`, {
|
|
18530
|
-
signal: AbortSignal.timeout(500)
|
|
18531
|
-
});
|
|
18532
|
-
if (res.ok) {
|
|
18533
|
-
const data = await res.json();
|
|
18534
|
-
if (typeof data.port === "number") port = data.port;
|
|
18535
|
-
} else {
|
|
18536
|
-
throw new Error("not running");
|
|
18537
|
-
}
|
|
18538
|
-
} catch {
|
|
18539
|
-
console.error(import_chalk19.default.dim("\u{1F6E1}\uFE0F Starting Node9 daemon (watch mode)..."));
|
|
18540
|
-
const child = (0, import_child_process10.spawn)(process.execPath, [process.argv[1], "daemon"], {
|
|
18541
|
-
detached: true,
|
|
18542
|
-
stdio: "ignore",
|
|
18543
|
-
env: { ...process.env, NODE9_AUTO_STARTED: "1", NODE9_WATCH_MODE: "1" }
|
|
18544
|
-
});
|
|
18545
|
-
child.unref();
|
|
18546
|
-
let ready = false;
|
|
18547
|
-
for (let i = 0; i < 20; i++) {
|
|
18548
|
-
await new Promise((r) => setTimeout(r, 250));
|
|
18549
|
-
try {
|
|
18550
|
-
const r = await fetch(`http://127.0.0.1:${DAEMON_PORT}/settings`, {
|
|
18551
|
-
signal: AbortSignal.timeout(500)
|
|
18552
|
-
});
|
|
18553
|
-
if (r.ok) {
|
|
18554
|
-
ready = true;
|
|
18555
|
-
break;
|
|
18556
|
-
}
|
|
18557
|
-
} catch {
|
|
18558
|
-
}
|
|
18559
|
-
}
|
|
18560
|
-
if (!ready) {
|
|
18561
|
-
console.error(import_chalk19.default.red("\u274C Daemon failed to start. Try: node9 daemon start"));
|
|
18562
|
-
process.exit(1);
|
|
18563
|
-
}
|
|
18564
|
-
}
|
|
18565
|
-
console.error(
|
|
18566
|
-
import_chalk19.default.cyan.bold("\u{1F6E1}\uFE0F Node9 watch") + import_chalk19.default.dim(` \u2192 localhost:${port}`) + import_chalk19.default.dim(
|
|
18567
|
-
"\n Tip: run `node9 tail` in another terminal to review and approve AI actions.\n"
|
|
18568
|
-
)
|
|
18569
|
-
);
|
|
18570
|
-
const result = (0, import_child_process10.spawnSync)(cmd, args, {
|
|
18571
|
-
stdio: "inherit",
|
|
18572
|
-
env: { ...process.env, NODE9_WATCH_MODE: "1" }
|
|
18573
|
-
});
|
|
18574
|
-
if (result.error) {
|
|
18575
|
-
console.error(import_chalk19.default.red(`\u274C Failed to run command: ${result.error.message}`));
|
|
18576
|
-
process.exit(1);
|
|
18577
|
-
}
|
|
18578
|
-
process.exit(result.status ?? 0);
|
|
18579
|
-
});
|
|
18580
|
-
}
|
|
18581
|
-
|
|
18582
18547
|
// src/mcp-gateway/index.ts
|
|
18583
18548
|
var import_readline4 = __toESM(require("readline"));
|
|
18584
|
-
var
|
|
18585
|
-
var
|
|
18549
|
+
var import_chalk19 = __toESM(require("chalk"));
|
|
18550
|
+
var import_child_process10 = require("child_process");
|
|
18586
18551
|
var import_execa3 = require("execa");
|
|
18587
18552
|
init_orchestrator();
|
|
18588
18553
|
init_provenance();
|
|
@@ -18743,13 +18708,13 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
18743
18708
|
const prov = checkProvenance(executable);
|
|
18744
18709
|
if (prov.trustLevel === "suspect") {
|
|
18745
18710
|
console.error(
|
|
18746
|
-
|
|
18711
|
+
import_chalk19.default.red(
|
|
18747
18712
|
`\u26A0\uFE0F Node9: Upstream MCP server binary is suspect \u2014 ${prov.reason} (${prov.resolvedPath})`
|
|
18748
18713
|
)
|
|
18749
18714
|
);
|
|
18750
|
-
console.error(
|
|
18715
|
+
console.error(import_chalk19.default.red(" Verify this binary is trusted before proceeding."));
|
|
18751
18716
|
}
|
|
18752
|
-
console.error(
|
|
18717
|
+
console.error(import_chalk19.default.green(`\u{1F680} Node9 MCP Gateway: Monitoring [${upstreamCommand}]`));
|
|
18753
18718
|
const UPSTREAM_INJECTOR_VARS = /* @__PURE__ */ new Set([
|
|
18754
18719
|
"NODE_OPTIONS",
|
|
18755
18720
|
"NODE_PATH",
|
|
@@ -18768,7 +18733,7 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
18768
18733
|
const safeEnv = Object.fromEntries(
|
|
18769
18734
|
Object.entries(process.env).filter(([k]) => !UPSTREAM_INJECTOR_VARS.has(k))
|
|
18770
18735
|
);
|
|
18771
|
-
const child = (0,
|
|
18736
|
+
const child = (0, import_child_process10.spawn)(executable, cmdArgs, {
|
|
18772
18737
|
stdio: ["pipe", "pipe", "inherit"],
|
|
18773
18738
|
// control stdin/stdout; inherit stderr
|
|
18774
18739
|
shell: false,
|
|
@@ -18862,10 +18827,10 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
18862
18827
|
mcpServer
|
|
18863
18828
|
});
|
|
18864
18829
|
if (!result.approved) {
|
|
18865
|
-
console.error(
|
|
18830
|
+
console.error(import_chalk19.default.red(`
|
|
18866
18831
|
\u{1F6D1} Node9 MCP Gateway: Action Blocked`));
|
|
18867
|
-
console.error(
|
|
18868
|
-
console.error(
|
|
18832
|
+
console.error(import_chalk19.default.gray(` Tool: ${toolName}`));
|
|
18833
|
+
console.error(import_chalk19.default.gray(` Reason: ${result.reason ?? "Security Policy"}
|
|
18869
18834
|
`));
|
|
18870
18835
|
const blockedByLabel = result.blockedByLabel ?? result.reason ?? "Security Policy";
|
|
18871
18836
|
const isHumanDecision = blockedByLabel.toLowerCase().includes("user") || blockedByLabel.toLowerCase().includes("daemon") || blockedByLabel.toLowerCase().includes("decision");
|
|
@@ -18977,7 +18942,7 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
18977
18942
|
updatePin(serverKey, upstreamCommand, currentHash, toolNames);
|
|
18978
18943
|
pinState = "validated";
|
|
18979
18944
|
console.error(
|
|
18980
|
-
|
|
18945
|
+
import_chalk19.default.green(
|
|
18981
18946
|
`\u{1F512} Node9: Pinned ${toolNames.length} tool definition(s) for this MCP server`
|
|
18982
18947
|
)
|
|
18983
18948
|
);
|
|
@@ -18990,11 +18955,11 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
18990
18955
|
} else if (pinStatus === "corrupt") {
|
|
18991
18956
|
pinState = "quarantined";
|
|
18992
18957
|
console.error(
|
|
18993
|
-
|
|
18958
|
+
import_chalk19.default.red("\n\u{1F6A8} Node9: MCP pin file is corrupt or unreadable \u2014 session quarantined!")
|
|
18994
18959
|
);
|
|
18995
|
-
console.error(
|
|
18960
|
+
console.error(import_chalk19.default.red(" Tool calls are blocked until the pin file is repaired."));
|
|
18996
18961
|
console.error(
|
|
18997
|
-
|
|
18962
|
+
import_chalk19.default.yellow(` Run: node9 mcp pin reset (to clear and re-pin on next connect)
|
|
18998
18963
|
`)
|
|
18999
18964
|
);
|
|
19000
18965
|
const errorResponse = {
|
|
@@ -19011,13 +18976,13 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
19011
18976
|
} else {
|
|
19012
18977
|
pinState = "quarantined";
|
|
19013
18978
|
console.error(
|
|
19014
|
-
|
|
18979
|
+
import_chalk19.default.red("\n\u{1F6A8} Node9: MCP tool definitions have changed since last verified!")
|
|
19015
18980
|
);
|
|
19016
18981
|
console.error(
|
|
19017
|
-
|
|
18982
|
+
import_chalk19.default.red(" This could indicate a supply chain attack (tool poisoning / rug pull).")
|
|
19018
18983
|
);
|
|
19019
|
-
console.error(
|
|
19020
|
-
console.error(
|
|
18984
|
+
console.error(import_chalk19.default.red(" Session quarantined \u2014 all tool calls blocked."));
|
|
18985
|
+
console.error(import_chalk19.default.yellow(` Run: node9 mcp pin update ${serverKey}
|
|
19021
18986
|
`));
|
|
19022
18987
|
const errorResponse = {
|
|
19023
18988
|
jsonrpc: "2.0",
|
|
@@ -19060,7 +19025,7 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
19060
19025
|
const toolName = callId !== void 0 ? pendingCallNames.get(callId) ?? "unknown" : "unknown";
|
|
19061
19026
|
if (callId !== void 0) pendingCallNames.delete(callId);
|
|
19062
19027
|
console.error(
|
|
19063
|
-
|
|
19028
|
+
import_chalk19.default.yellow(
|
|
19064
19029
|
`\u26A1 Node9: Large MCP response from '${toolName}' (${(line.length / 1024).toFixed(0)}KB) \u2014 context window enlarged`
|
|
19065
19030
|
)
|
|
19066
19031
|
);
|
|
@@ -19114,7 +19079,7 @@ var import_readline5 = __toESM(require("readline"));
|
|
|
19114
19079
|
var import_fs39 = __toESM(require("fs"));
|
|
19115
19080
|
var import_os35 = __toESM(require("os"));
|
|
19116
19081
|
var import_path41 = __toESM(require("path"));
|
|
19117
|
-
var
|
|
19082
|
+
var import_child_process11 = require("child_process");
|
|
19118
19083
|
init_core();
|
|
19119
19084
|
init_daemon();
|
|
19120
19085
|
init_shields();
|
|
@@ -19588,7 +19553,7 @@ function handleRuleAdd(args) {
|
|
|
19588
19553
|
return `Rule "${name}" added to ~/.node9/config.json \u2014 verdict: ${verdict} when ${field} matches "${pattern}"`;
|
|
19589
19554
|
}
|
|
19590
19555
|
function runCliCommand(subArgs) {
|
|
19591
|
-
const result = (0,
|
|
19556
|
+
const result = (0, import_child_process11.spawnSync)(process.execPath, [process.argv[1], ...subArgs], {
|
|
19592
19557
|
encoding: "utf-8",
|
|
19593
19558
|
timeout: 6e4,
|
|
19594
19559
|
// Disable colors — stdout is piped (not a TTY), chalk auto-detects, but be explicit
|
|
@@ -19782,7 +19747,7 @@ function registerMcpServerCommand(program2) {
|
|
|
19782
19747
|
}
|
|
19783
19748
|
|
|
19784
19749
|
// src/cli/commands/trust.ts
|
|
19785
|
-
var
|
|
19750
|
+
var import_chalk20 = __toESM(require("chalk"));
|
|
19786
19751
|
init_trusted_hosts();
|
|
19787
19752
|
function isValidHost(host) {
|
|
19788
19753
|
return /^(\*\.)?[a-z0-9][a-z0-9.-]*\.[a-z]{2,}$/.test(host);
|
|
@@ -19793,51 +19758,51 @@ function registerTrustCommand(program2) {
|
|
|
19793
19758
|
const normalized = normalizeHost(host.trim());
|
|
19794
19759
|
if (!isValidHost(normalized)) {
|
|
19795
19760
|
console.error(
|
|
19796
|
-
|
|
19761
|
+
import_chalk20.default.red(`
|
|
19797
19762
|
\u274C Invalid host: "${host}"
|
|
19798
|
-
`) +
|
|
19763
|
+
`) + import_chalk20.default.gray(" Use an FQDN like api.mycompany.com or *.mycompany.com\n")
|
|
19799
19764
|
);
|
|
19800
19765
|
process.exit(1);
|
|
19801
19766
|
}
|
|
19802
19767
|
addTrustedHost(normalized);
|
|
19803
|
-
console.log(
|
|
19768
|
+
console.log(import_chalk20.default.green(`
|
|
19804
19769
|
\u2705 ${normalized} added to trusted hosts.`));
|
|
19805
19770
|
console.log(
|
|
19806
|
-
|
|
19771
|
+
import_chalk20.default.gray(" Pipe-chain blocks to this host: critical \u2192 review, high \u2192 allow\n")
|
|
19807
19772
|
);
|
|
19808
19773
|
});
|
|
19809
19774
|
trustCmd.command("remove <host>").description("Remove a trusted host").action((host) => {
|
|
19810
19775
|
const normalized = normalizeHost(host.trim());
|
|
19811
19776
|
const removed = removeTrustedHost(normalized);
|
|
19812
19777
|
if (!removed) {
|
|
19813
|
-
console.error(
|
|
19778
|
+
console.error(import_chalk20.default.yellow(`
|
|
19814
19779
|
\u26A0\uFE0F "${normalized}" is not in the trusted hosts list.
|
|
19815
19780
|
`));
|
|
19816
19781
|
process.exit(1);
|
|
19817
19782
|
}
|
|
19818
|
-
console.log(
|
|
19783
|
+
console.log(import_chalk20.default.green(`
|
|
19819
19784
|
\u2705 ${normalized} removed from trusted hosts.
|
|
19820
19785
|
`));
|
|
19821
19786
|
});
|
|
19822
19787
|
trustCmd.command("list").description("Show all trusted hosts").action(() => {
|
|
19823
19788
|
const hosts = readTrustedHosts();
|
|
19824
19789
|
if (hosts.length === 0) {
|
|
19825
|
-
console.log(
|
|
19826
|
-
console.log(` Add one: ${
|
|
19790
|
+
console.log(import_chalk20.default.gray("\n No trusted hosts configured.\n"));
|
|
19791
|
+
console.log(` Add one: ${import_chalk20.default.cyan("node9 trust add api.mycompany.com")}
|
|
19827
19792
|
`);
|
|
19828
19793
|
return;
|
|
19829
19794
|
}
|
|
19830
|
-
console.log(
|
|
19795
|
+
console.log(import_chalk20.default.bold("\n\u{1F513} Trusted Hosts\n"));
|
|
19831
19796
|
for (const entry of hosts) {
|
|
19832
19797
|
const date = new Date(entry.addedAt).toLocaleDateString();
|
|
19833
|
-
console.log(` ${
|
|
19798
|
+
console.log(` ${import_chalk20.default.cyan(entry.host.padEnd(40))} ${import_chalk20.default.gray(`added ${date}`)}`);
|
|
19834
19799
|
}
|
|
19835
19800
|
console.log("");
|
|
19836
19801
|
});
|
|
19837
19802
|
}
|
|
19838
19803
|
|
|
19839
19804
|
// src/cli/commands/mcp-pin.ts
|
|
19840
|
-
var
|
|
19805
|
+
var import_chalk21 = __toESM(require("chalk"));
|
|
19841
19806
|
function registerMcpPinCommand(program2) {
|
|
19842
19807
|
const pinCmd = program2.command("mcp").description("Manage MCP server tool definition pinning (rug pull defense)");
|
|
19843
19808
|
const pinSubCmd = pinCmd.command("pin").description("Manage pinned MCP server tool definitions");
|
|
@@ -19845,31 +19810,31 @@ function registerMcpPinCommand(program2) {
|
|
|
19845
19810
|
const result = readMcpPinsSafe();
|
|
19846
19811
|
if (!result.ok) {
|
|
19847
19812
|
if (result.reason === "missing") {
|
|
19848
|
-
console.log(
|
|
19813
|
+
console.log(import_chalk21.default.gray("\nNo MCP servers are pinned yet."));
|
|
19849
19814
|
console.log(
|
|
19850
|
-
|
|
19815
|
+
import_chalk21.default.gray("Pins are created automatically when the MCP gateway first connects.\n")
|
|
19851
19816
|
);
|
|
19852
19817
|
return;
|
|
19853
19818
|
}
|
|
19854
|
-
console.error(
|
|
19819
|
+
console.error(import_chalk21.default.red(`
|
|
19855
19820
|
\u274C Pin file is corrupt: ${result.detail}`));
|
|
19856
|
-
console.error(
|
|
19821
|
+
console.error(import_chalk21.default.yellow(" Run: node9 mcp pin reset\n"));
|
|
19857
19822
|
process.exit(1);
|
|
19858
19823
|
}
|
|
19859
19824
|
const entries = Object.entries(result.pins.servers);
|
|
19860
19825
|
if (entries.length === 0) {
|
|
19861
|
-
console.log(
|
|
19826
|
+
console.log(import_chalk21.default.gray("\nNo MCP servers are pinned yet."));
|
|
19862
19827
|
console.log(
|
|
19863
|
-
|
|
19828
|
+
import_chalk21.default.gray("Pins are created automatically when the MCP gateway first connects.\n")
|
|
19864
19829
|
);
|
|
19865
19830
|
return;
|
|
19866
19831
|
}
|
|
19867
|
-
console.log(
|
|
19832
|
+
console.log(import_chalk21.default.bold("\n\u{1F512} Pinned MCP Servers\n"));
|
|
19868
19833
|
for (const [key, entry] of entries) {
|
|
19869
|
-
console.log(` ${
|
|
19870
|
-
console.log(` Tools (${entry.toolCount}): ${
|
|
19871
|
-
console.log(` Hash: ${
|
|
19872
|
-
console.log(` Pinned: ${
|
|
19834
|
+
console.log(` ${import_chalk21.default.cyan(key)} ${import_chalk21.default.gray(entry.label)}`);
|
|
19835
|
+
console.log(` Tools (${entry.toolCount}): ${import_chalk21.default.white(entry.toolNames.join(", "))}`);
|
|
19836
|
+
console.log(` Hash: ${import_chalk21.default.gray(entry.toolsHash.slice(0, 16))}...`);
|
|
19837
|
+
console.log(` Pinned: ${import_chalk21.default.gray(entry.pinnedAt)}`);
|
|
19873
19838
|
console.log("");
|
|
19874
19839
|
}
|
|
19875
19840
|
});
|
|
@@ -19880,127 +19845,127 @@ function registerMcpPinCommand(program2) {
|
|
|
19880
19845
|
try {
|
|
19881
19846
|
pins = readMcpPins();
|
|
19882
19847
|
} catch {
|
|
19883
|
-
console.error(
|
|
19884
|
-
console.error(
|
|
19848
|
+
console.error(import_chalk21.default.red("\n\u274C Pin file is corrupt."));
|
|
19849
|
+
console.error(import_chalk21.default.yellow(" Run: node9 mcp pin reset\n"));
|
|
19885
19850
|
process.exit(1);
|
|
19886
19851
|
}
|
|
19887
19852
|
if (!pins.servers[serverKey]) {
|
|
19888
|
-
console.error(
|
|
19853
|
+
console.error(import_chalk21.default.red(`
|
|
19889
19854
|
\u274C No pin found for server key "${serverKey}"
|
|
19890
19855
|
`));
|
|
19891
|
-
console.error(`Run ${
|
|
19856
|
+
console.error(`Run ${import_chalk21.default.cyan("node9 mcp pin list")} to see pinned servers.
|
|
19892
19857
|
`);
|
|
19893
19858
|
process.exit(1);
|
|
19894
19859
|
}
|
|
19895
19860
|
const label = pins.servers[serverKey].label;
|
|
19896
19861
|
removePin2(serverKey);
|
|
19897
|
-
console.log(
|
|
19898
|
-
\u{1F513} Pin removed for ${
|
|
19899
|
-
console.log(
|
|
19900
|
-
console.log(
|
|
19862
|
+
console.log(import_chalk21.default.green(`
|
|
19863
|
+
\u{1F513} Pin removed for ${import_chalk21.default.cyan(serverKey)}`));
|
|
19864
|
+
console.log(import_chalk21.default.gray(` Server: ${label}`));
|
|
19865
|
+
console.log(import_chalk21.default.gray(" Next connection will re-pin with current tool definitions.\n"));
|
|
19901
19866
|
});
|
|
19902
19867
|
pinSubCmd.command("reset").description("Clear all MCP pins (next connection to each server will re-pin)").action(() => {
|
|
19903
19868
|
const result = readMcpPinsSafe();
|
|
19904
19869
|
if (!result.ok && result.reason === "missing") {
|
|
19905
|
-
console.log(
|
|
19870
|
+
console.log(import_chalk21.default.gray("\nNo pins to clear.\n"));
|
|
19906
19871
|
return;
|
|
19907
19872
|
}
|
|
19908
19873
|
const count = result.ok ? Object.keys(result.pins.servers).length : "?";
|
|
19909
19874
|
clearAllPins2();
|
|
19910
|
-
console.log(
|
|
19875
|
+
console.log(import_chalk21.default.green(`
|
|
19911
19876
|
\u{1F513} Cleared ${count} MCP pin(s).`));
|
|
19912
|
-
console.log(
|
|
19877
|
+
console.log(import_chalk21.default.gray(" Next connection to each server will re-pin.\n"));
|
|
19913
19878
|
});
|
|
19914
19879
|
}
|
|
19915
19880
|
|
|
19916
19881
|
// src/cli/commands/sync.ts
|
|
19917
|
-
var
|
|
19882
|
+
var import_chalk22 = __toESM(require("chalk"));
|
|
19918
19883
|
init_sync();
|
|
19919
19884
|
function registerSyncCommand(program2) {
|
|
19920
19885
|
const policy = program2.command("policy").description("Manage cloud policy rules");
|
|
19921
19886
|
policy.command("sync").description("Sync cloud policy rules to local cache (~/.node9/rules-cache.json)").action(async () => {
|
|
19922
|
-
process.stdout.write(
|
|
19887
|
+
process.stdout.write(import_chalk22.default.cyan("Syncing cloud policy rules\u2026"));
|
|
19923
19888
|
const result = await runCloudSync();
|
|
19924
19889
|
process.stdout.write("\n");
|
|
19925
19890
|
if (!result.ok) {
|
|
19926
|
-
console.error(
|
|
19891
|
+
console.error(import_chalk22.default.red(`\u2717 ${result.reason}`));
|
|
19927
19892
|
process.exit(1);
|
|
19928
19893
|
}
|
|
19929
19894
|
if (result.unchanged) {
|
|
19930
19895
|
console.log(
|
|
19931
|
-
|
|
19896
|
+
import_chalk22.default.green(
|
|
19932
19897
|
`\u2713 Already up to date \u2014 ${result.rules} rule${result.rules === 1 ? "" : "s"} cached`
|
|
19933
19898
|
)
|
|
19934
19899
|
);
|
|
19935
|
-
console.log(
|
|
19936
|
-
console.log(
|
|
19900
|
+
console.log(import_chalk22.default.gray(` Cached at: ${result.fetchedAt}`));
|
|
19901
|
+
console.log(import_chalk22.default.gray(` Server returned 304 (no changes since last sync)`));
|
|
19937
19902
|
} else {
|
|
19938
19903
|
console.log(
|
|
19939
|
-
|
|
19904
|
+
import_chalk22.default.green(`\u2713 Synced ${result.rules} rule${result.rules === 1 ? "" : "s"} from cloud`)
|
|
19940
19905
|
);
|
|
19941
|
-
console.log(
|
|
19942
|
-
console.log(
|
|
19906
|
+
console.log(import_chalk22.default.gray(` Cached at: ${result.fetchedAt}`));
|
|
19907
|
+
console.log(import_chalk22.default.gray(` File: ~/.node9/rules-cache.json`));
|
|
19943
19908
|
}
|
|
19944
19909
|
});
|
|
19945
19910
|
policy.command("show").description("List all cloud policy rules in the local cache").action(() => {
|
|
19946
19911
|
const status = getCloudSyncStatus();
|
|
19947
19912
|
if (!status.cached) {
|
|
19948
|
-
console.log(
|
|
19913
|
+
console.log(import_chalk22.default.yellow("\n No cloud rules cached \u2014 run: node9 policy sync\n"));
|
|
19949
19914
|
return;
|
|
19950
19915
|
}
|
|
19951
19916
|
const rules = getCloudRules() ?? [];
|
|
19952
19917
|
const age = Math.round((Date.now() - new Date(status.fetchedAt).getTime()) / 6e4);
|
|
19953
19918
|
console.log(
|
|
19954
|
-
|
|
19955
|
-
Cloud policy rules`) +
|
|
19919
|
+
import_chalk22.default.bold(`
|
|
19920
|
+
Cloud policy rules`) + import_chalk22.default.gray(
|
|
19956
19921
|
` (${rules.length} rule${rules.length === 1 ? "" : "s"}, synced ${age}m ago)
|
|
19957
19922
|
`
|
|
19958
19923
|
)
|
|
19959
19924
|
);
|
|
19960
19925
|
if (rules.length === 0) {
|
|
19961
|
-
console.log(
|
|
19926
|
+
console.log(import_chalk22.default.gray(" No rules defined in cloud policy.\n"));
|
|
19962
19927
|
return;
|
|
19963
19928
|
}
|
|
19964
19929
|
for (const rule of rules) {
|
|
19965
19930
|
const r = rule;
|
|
19966
|
-
const verdictColor = r.verdict === "block" ?
|
|
19931
|
+
const verdictColor = r.verdict === "block" ? import_chalk22.default.red : r.verdict === "allow" ? import_chalk22.default.green : import_chalk22.default.yellow;
|
|
19967
19932
|
console.log(
|
|
19968
19933
|
` ${verdictColor(
|
|
19969
19934
|
String(r.verdict ?? "unknown").toUpperCase().padEnd(6)
|
|
19970
|
-
)} ${
|
|
19935
|
+
)} ${import_chalk22.default.white(String(r.name ?? "(unnamed)"))}`
|
|
19971
19936
|
);
|
|
19972
|
-
if (r.reason) console.log(
|
|
19937
|
+
if (r.reason) console.log(import_chalk22.default.gray(` ${String(r.reason)}`));
|
|
19973
19938
|
}
|
|
19974
19939
|
console.log("");
|
|
19975
19940
|
});
|
|
19976
19941
|
policy.command("status").description("Show current cloud policy cache status").action(() => {
|
|
19977
19942
|
const s = getCloudSyncStatus();
|
|
19978
19943
|
if (!s.cached) {
|
|
19979
|
-
console.log(
|
|
19944
|
+
console.log(import_chalk22.default.yellow("\n No cache yet \u2014 run: node9 policy sync\n"));
|
|
19980
19945
|
return;
|
|
19981
19946
|
}
|
|
19982
19947
|
const age = Math.round((Date.now() - new Date(s.fetchedAt).getTime()) / 6e4);
|
|
19983
19948
|
console.log(`
|
|
19984
|
-
Rules : ${
|
|
19949
|
+
Rules : ${import_chalk22.default.green(String(s.rules))} cloud rules loaded`);
|
|
19985
19950
|
console.log(
|
|
19986
|
-
` Synced : ${
|
|
19951
|
+
` Synced : ${import_chalk22.default.gray(`${age} minute${age === 1 ? "" : "s"} ago`)} (${s.fetchedAt})`
|
|
19987
19952
|
);
|
|
19988
19953
|
if (s.workspaceId) {
|
|
19989
|
-
console.log(` Workspace: ${
|
|
19954
|
+
console.log(` Workspace: ${import_chalk22.default.gray(s.workspaceId)}`);
|
|
19990
19955
|
}
|
|
19991
19956
|
if (s.panicMode) {
|
|
19992
19957
|
console.log(
|
|
19993
|
-
` ${
|
|
19958
|
+
` ${import_chalk22.default.red.bold("\u{1F6A8} Panic mode : ON")} ` + import_chalk22.default.dim("(every review-verdict becomes block)")
|
|
19994
19959
|
);
|
|
19995
19960
|
}
|
|
19996
19961
|
if (s.shadowMode) {
|
|
19997
19962
|
console.log(
|
|
19998
|
-
` ${
|
|
19963
|
+
` ${import_chalk22.default.yellow.bold("\u{1F441} Shadow mode : ON")} ` + import_chalk22.default.dim("(blocks become would-block log entries)")
|
|
19999
19964
|
);
|
|
20000
19965
|
}
|
|
20001
19966
|
if (s.syncIntervalHours) {
|
|
20002
19967
|
console.log(
|
|
20003
|
-
|
|
19968
|
+
import_chalk22.default.gray(
|
|
20004
19969
|
` Polling : every ${s.syncIntervalHours} hour${s.syncIntervalHours === 1 ? "" : "s"}`
|
|
20005
19970
|
)
|
|
20006
19971
|
);
|
|
@@ -20010,7 +19975,7 @@ function registerSyncCommand(program2) {
|
|
|
20010
19975
|
}
|
|
20011
19976
|
|
|
20012
19977
|
// src/cli/commands/agents.ts
|
|
20013
|
-
var
|
|
19978
|
+
var import_chalk23 = __toESM(require("chalk"));
|
|
20014
19979
|
init_setup();
|
|
20015
19980
|
var SETUP_FN = {
|
|
20016
19981
|
claude: setupClaude,
|
|
@@ -20040,23 +20005,23 @@ function registerAgentsCommand(program2) {
|
|
|
20040
20005
|
console.log(` ${"Agent".padEnd(14)}${"Installed".padEnd(11)}${"Wired".padEnd(8)}Mode`);
|
|
20041
20006
|
console.log(" " + "\u2500".repeat(44));
|
|
20042
20007
|
for (const s of statuses) {
|
|
20043
|
-
const installed = s.installed ?
|
|
20044
|
-
const wired = !s.installed ?
|
|
20045
|
-
const mode = s.mode ?
|
|
20046
|
-
const hint = s.installed && !s.wired ?
|
|
20008
|
+
const installed = s.installed ? import_chalk23.default.green("\u2713") : import_chalk23.default.gray("\u2717");
|
|
20009
|
+
const wired = !s.installed ? import_chalk23.default.gray("\u2014") : s.wired ? import_chalk23.default.green("\u2713") : import_chalk23.default.yellow("\u2717");
|
|
20010
|
+
const mode = s.mode ? import_chalk23.default.gray(s.mode) : import_chalk23.default.gray("\u2014");
|
|
20011
|
+
const hint = s.installed && !s.wired ? import_chalk23.default.gray(` \u2190 node9 agents add ${s.name}`) : "";
|
|
20047
20012
|
console.log(` ${s.label.padEnd(14)}${installed} ${wired} ${mode}${hint}`);
|
|
20048
20013
|
}
|
|
20049
20014
|
console.log("");
|
|
20050
20015
|
if (!anyInstalled) {
|
|
20051
20016
|
console.log(
|
|
20052
|
-
|
|
20017
|
+
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")
|
|
20053
20018
|
);
|
|
20054
20019
|
return;
|
|
20055
20020
|
}
|
|
20056
20021
|
const unwired = statuses.filter((s) => s.installed && !s.wired);
|
|
20057
20022
|
if (unwired.length > 0) {
|
|
20058
20023
|
console.log(
|
|
20059
|
-
|
|
20024
|
+
import_chalk23.default.yellow(` ${unwired.length} agent(s) not yet wired. Run: `) + import_chalk23.default.white(`node9 agents add ${unwired[0].name}`) + "\n"
|
|
20060
20025
|
);
|
|
20061
20026
|
}
|
|
20062
20027
|
});
|
|
@@ -20064,7 +20029,7 @@ function registerAgentsCommand(program2) {
|
|
|
20064
20029
|
const name = agent.toLowerCase();
|
|
20065
20030
|
const fn = SETUP_FN[name];
|
|
20066
20031
|
if (!fn) {
|
|
20067
|
-
console.error(
|
|
20032
|
+
console.error(import_chalk23.default.red(`Unknown agent: "${agent}". Supported: ${AGENT_NAMES.join(", ")}`));
|
|
20068
20033
|
process.exit(1);
|
|
20069
20034
|
}
|
|
20070
20035
|
await fn();
|
|
@@ -20073,14 +20038,14 @@ function registerAgentsCommand(program2) {
|
|
|
20073
20038
|
const name = agent.toLowerCase();
|
|
20074
20039
|
const fn = TEARDOWN_FN[name];
|
|
20075
20040
|
if (!fn) {
|
|
20076
|
-
console.error(
|
|
20041
|
+
console.error(import_chalk23.default.red(`Unknown agent: "${agent}". Supported: ${AGENT_NAMES.join(", ")}`));
|
|
20077
20042
|
process.exit(1);
|
|
20078
20043
|
}
|
|
20079
|
-
console.log(
|
|
20044
|
+
console.log(import_chalk23.default.cyan(`
|
|
20080
20045
|
\u{1F6E1}\uFE0F Node9: removing from ${name}...
|
|
20081
20046
|
`));
|
|
20082
20047
|
fn();
|
|
20083
|
-
console.log(
|
|
20048
|
+
console.log(import_chalk23.default.gray("\n Restart the agent for changes to take effect."));
|
|
20084
20049
|
});
|
|
20085
20050
|
}
|
|
20086
20051
|
|
|
@@ -20088,7 +20053,7 @@ function registerAgentsCommand(program2) {
|
|
|
20088
20053
|
init_scan();
|
|
20089
20054
|
|
|
20090
20055
|
// src/cli/commands/sessions.ts
|
|
20091
|
-
var
|
|
20056
|
+
var import_chalk24 = __toESM(require("chalk"));
|
|
20092
20057
|
var import_fs40 = __toESM(require("fs"));
|
|
20093
20058
|
var import_path42 = __toESM(require("path"));
|
|
20094
20059
|
var import_os36 = __toESM(require("os"));
|
|
@@ -20597,11 +20562,11 @@ function toolInputSummary(tool, input) {
|
|
|
20597
20562
|
}
|
|
20598
20563
|
function toolColor(tool) {
|
|
20599
20564
|
const t = tool.toLowerCase();
|
|
20600
|
-
if (t === "bash" || t === "execute_bash") return
|
|
20601
|
-
if (t === "write") return
|
|
20602
|
-
if (t === "edit" || t === "notebookedit") return
|
|
20603
|
-
if (t === "read") return
|
|
20604
|
-
return
|
|
20565
|
+
if (t === "bash" || t === "execute_bash") return import_chalk24.default.red;
|
|
20566
|
+
if (t === "write") return import_chalk24.default.green;
|
|
20567
|
+
if (t === "edit" || t === "notebookedit") return import_chalk24.default.yellow;
|
|
20568
|
+
if (t === "read") return import_chalk24.default.cyan;
|
|
20569
|
+
return import_chalk24.default.gray;
|
|
20605
20570
|
}
|
|
20606
20571
|
function barStr2(value, max, width) {
|
|
20607
20572
|
if (max === 0 || width <= 0) return "\u2591".repeat(width);
|
|
@@ -20611,7 +20576,7 @@ function barStr2(value, max, width) {
|
|
|
20611
20576
|
function colorBar2(value, max, width) {
|
|
20612
20577
|
const s = barStr2(value, max, width);
|
|
20613
20578
|
const filled = Math.max(1, Math.round(max > 0 ? value / max * width : 0));
|
|
20614
|
-
return
|
|
20579
|
+
return import_chalk24.default.cyan(s.slice(0, filled)) + import_chalk24.default.dim(s.slice(filled));
|
|
20615
20580
|
}
|
|
20616
20581
|
function renderSummary(summaries) {
|
|
20617
20582
|
const totalTools = summaries.reduce((n, s) => n + s.toolCalls.length, 0);
|
|
@@ -20641,45 +20606,45 @@ function renderSummary(summaries) {
|
|
|
20641
20606
|
}
|
|
20642
20607
|
const topProjects = [...projCosts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
|
|
20643
20608
|
const W = 20;
|
|
20644
|
-
console.log(
|
|
20609
|
+
console.log(import_chalk24.default.dim(" " + "\u2500".repeat(70)));
|
|
20645
20610
|
console.log(
|
|
20646
|
-
" " +
|
|
20611
|
+
" " + 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") : "")
|
|
20647
20612
|
);
|
|
20648
20613
|
console.log(
|
|
20649
|
-
" " +
|
|
20614
|
+
" " + 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`)
|
|
20650
20615
|
);
|
|
20651
20616
|
console.log("");
|
|
20652
|
-
console.log(" " +
|
|
20617
|
+
console.log(" " + import_chalk24.default.dim("Tool breakdown:"));
|
|
20653
20618
|
const maxGroup = Math.max(...Object.values(groups));
|
|
20654
20619
|
for (const [label, count] of Object.entries(groups)) {
|
|
20655
20620
|
if (count === 0) continue;
|
|
20656
20621
|
const pct = totalTools > 0 ? Math.round(count / totalTools * 100) : 0;
|
|
20657
20622
|
console.log(
|
|
20658
|
-
" " + label.padEnd(6) + " " + colorBar2(count, maxGroup, W) + " " +
|
|
20623
|
+
" " + label.padEnd(6) + " " + colorBar2(count, maxGroup, W) + " " + import_chalk24.default.white(String(count).padStart(4)) + import_chalk24.default.dim(` (${String(pct)}%)`)
|
|
20659
20624
|
);
|
|
20660
20625
|
}
|
|
20661
20626
|
console.log("");
|
|
20662
20627
|
if (topProjects.length > 1) {
|
|
20663
|
-
console.log(" " +
|
|
20628
|
+
console.log(" " + import_chalk24.default.dim("Cost by project:"));
|
|
20664
20629
|
const maxProjCost = topProjects[0][1];
|
|
20665
20630
|
for (const [proj, cost] of topProjects) {
|
|
20666
20631
|
console.log(
|
|
20667
|
-
" " + proj.slice(0, 28).padEnd(28) + " " + colorBar2(cost, maxProjCost, W) + " " +
|
|
20632
|
+
" " + proj.slice(0, 28).padEnd(28) + " " + colorBar2(cost, maxProjCost, W) + " " + import_chalk24.default.yellow(fmtCost3(cost))
|
|
20668
20633
|
);
|
|
20669
20634
|
}
|
|
20670
20635
|
console.log("");
|
|
20671
20636
|
}
|
|
20672
|
-
console.log(
|
|
20637
|
+
console.log(import_chalk24.default.dim(" " + "\u2500".repeat(70)));
|
|
20673
20638
|
console.log("");
|
|
20674
20639
|
}
|
|
20675
20640
|
function renderList(summaries, totalCost) {
|
|
20676
20641
|
if (summaries.length === 0) {
|
|
20677
|
-
console.log(
|
|
20642
|
+
console.log(import_chalk24.default.yellow(" No sessions found in the requested range.\n"));
|
|
20678
20643
|
return;
|
|
20679
20644
|
}
|
|
20680
|
-
const totalLabel = totalCost > 0 ?
|
|
20645
|
+
const totalLabel = totalCost > 0 ? import_chalk24.default.dim(" ~" + fmtCost3(totalCost) + " total") : "";
|
|
20681
20646
|
console.log(
|
|
20682
|
-
" " +
|
|
20647
|
+
" " + import_chalk24.default.white(String(summaries.length)) + import_chalk24.default.dim(` session${summaries.length !== 1 ? "s" : ""}`) + totalLabel
|
|
20683
20648
|
);
|
|
20684
20649
|
console.log("");
|
|
20685
20650
|
let lastGroup = "";
|
|
@@ -20687,49 +20652,49 @@ function renderList(summaries, totalCost) {
|
|
|
20687
20652
|
const activeDate = fmtDate2(s.lastActiveTime);
|
|
20688
20653
|
const group = activeDate + " " + s.projectLabel;
|
|
20689
20654
|
if (group !== lastGroup) {
|
|
20690
|
-
console.log(
|
|
20655
|
+
console.log(import_chalk24.default.dim(" \u2500\u2500\u2500 ") + import_chalk24.default.bold(activeDate) + import_chalk24.default.dim(" " + s.projectLabel));
|
|
20691
20656
|
lastGroup = group;
|
|
20692
20657
|
}
|
|
20693
20658
|
const startDate = fmtDate2(s.startTime);
|
|
20694
|
-
const dateRange = startDate !== activeDate ?
|
|
20695
|
-
const timeStr =
|
|
20696
|
-
const prompt =
|
|
20697
|
-
const tools = s.toolCalls.length > 0 ?
|
|
20698
|
-
const cost = s.costUSD > 0 ?
|
|
20699
|
-
const blocked = s.blockedCalls.length > 0 ?
|
|
20700
|
-
const snap = s.hasSnapshot ?
|
|
20701
|
-
const agentBadge = s.agent === "gemini" ?
|
|
20702
|
-
const sid =
|
|
20659
|
+
const dateRange = startDate !== activeDate ? import_chalk24.default.dim(" (" + startDate + " \u2192 " + activeDate + ")") : "";
|
|
20660
|
+
const timeStr = import_chalk24.default.dim(fmtTime(s.startTime));
|
|
20661
|
+
const prompt = import_chalk24.default.white(truncate(s.firstPrompt.replace(/\n/g, " "), 50).padEnd(50));
|
|
20662
|
+
const tools = s.toolCalls.length > 0 ? import_chalk24.default.dim(String(s.toolCalls.length).padStart(3) + " tools") : import_chalk24.default.dim(" 0 tools");
|
|
20663
|
+
const cost = s.costUSD > 0 ? import_chalk24.default.dim(" " + fmtCost3(s.costUSD).padEnd(8)) : " ";
|
|
20664
|
+
const blocked = s.blockedCalls.length > 0 ? import_chalk24.default.red(" \u{1F6D1} " + String(s.blockedCalls.length)) : "";
|
|
20665
|
+
const snap = s.hasSnapshot ? import_chalk24.default.green(" \u{1F4F8}") : "";
|
|
20666
|
+
const agentBadge = s.agent === "gemini" ? import_chalk24.default.blue(" [Gemini]") : s.agent === "codex" ? import_chalk24.default.magenta(" [Codex]") : import_chalk24.default.cyan(" [Claude]");
|
|
20667
|
+
const sid = import_chalk24.default.dim(" " + s.sessionId.slice(0, 8));
|
|
20703
20668
|
console.log(
|
|
20704
20669
|
` ${timeStr} ${prompt} ${tools}${cost}${blocked}${snap}${agentBadge}${sid}${dateRange}`
|
|
20705
20670
|
);
|
|
20706
20671
|
}
|
|
20707
20672
|
console.log("");
|
|
20708
20673
|
console.log(
|
|
20709
|
-
|
|
20674
|
+
import_chalk24.default.dim(" Run") + " " + import_chalk24.default.cyan("node9 sessions --detail <session-id>") + import_chalk24.default.dim(" for full tool trace.")
|
|
20710
20675
|
);
|
|
20711
20676
|
console.log("");
|
|
20712
20677
|
}
|
|
20713
20678
|
function renderDetail(s) {
|
|
20714
20679
|
console.log("");
|
|
20715
|
-
console.log(
|
|
20680
|
+
console.log(import_chalk24.default.bold(" Session ") + import_chalk24.default.dim(s.sessionId));
|
|
20716
20681
|
console.log(
|
|
20717
|
-
|
|
20682
|
+
import_chalk24.default.bold(" Prompt ") + import_chalk24.default.white(s.firstPrompt.replace(/\n/g, " ").slice(0, 120))
|
|
20718
20683
|
);
|
|
20719
|
-
console.log(
|
|
20684
|
+
console.log(import_chalk24.default.bold(" Project ") + import_chalk24.default.white(s.projectLabel));
|
|
20720
20685
|
if (s.agent) {
|
|
20721
|
-
const agentLabel2 = s.agent === "gemini" ?
|
|
20722
|
-
console.log(
|
|
20686
|
+
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");
|
|
20687
|
+
console.log(import_chalk24.default.bold(" Agent ") + agentLabel2);
|
|
20723
20688
|
}
|
|
20724
|
-
console.log(
|
|
20689
|
+
console.log(import_chalk24.default.bold(" When ") + import_chalk24.default.white(fmtDateTime(s.startTime)));
|
|
20725
20690
|
if (s.costUSD > 0)
|
|
20726
|
-
console.log(
|
|
20691
|
+
console.log(import_chalk24.default.bold(" Cost ") + import_chalk24.default.yellow("~" + fmtCost3(s.costUSD)));
|
|
20727
20692
|
console.log(
|
|
20728
|
-
|
|
20693
|
+
import_chalk24.default.bold(" Snapshot ") + (s.hasSnapshot ? import_chalk24.default.green("\u2713 taken") : import_chalk24.default.dim("none"))
|
|
20729
20694
|
);
|
|
20730
20695
|
console.log("");
|
|
20731
20696
|
if (s.toolCalls.length === 0 && s.blockedCalls.length === 0) {
|
|
20732
|
-
console.log(
|
|
20697
|
+
console.log(import_chalk24.default.dim(" No tool calls recorded.\n"));
|
|
20733
20698
|
return;
|
|
20734
20699
|
}
|
|
20735
20700
|
const timeline = [
|
|
@@ -20742,32 +20707,32 @@ function renderDetail(s) {
|
|
|
20742
20707
|
});
|
|
20743
20708
|
const headerParts = [`Tool calls (${s.toolCalls.length})`];
|
|
20744
20709
|
if (s.blockedCalls.length > 0)
|
|
20745
|
-
headerParts.push(
|
|
20746
|
-
console.log(
|
|
20710
|
+
headerParts.push(import_chalk24.default.red(`${s.blockedCalls.length} blocked by node9`));
|
|
20711
|
+
console.log(import_chalk24.default.bold(" " + headerParts.join(" \xB7 ")));
|
|
20747
20712
|
console.log("");
|
|
20748
20713
|
for (const entry of timeline) {
|
|
20749
20714
|
if (entry.kind === "tool") {
|
|
20750
20715
|
const tc = entry.tc;
|
|
20751
20716
|
const colorFn = toolColor(tc.tool);
|
|
20752
20717
|
const toolPad = colorFn(tc.tool.padEnd(16));
|
|
20753
|
-
const detail =
|
|
20754
|
-
const ts = tc.timestamp ?
|
|
20718
|
+
const detail = import_chalk24.default.gray(truncate(toolInputSummary(tc.tool, tc.input), 70));
|
|
20719
|
+
const ts = tc.timestamp ? import_chalk24.default.dim(fmtTime(tc.timestamp) + " ") : " ";
|
|
20755
20720
|
console.log(` ${ts}${toolPad} ${detail}`);
|
|
20756
20721
|
} else {
|
|
20757
20722
|
const bc = entry.bc;
|
|
20758
|
-
const ts = bc.timestamp ?
|
|
20759
|
-
const label =
|
|
20760
|
-
const toolName =
|
|
20761
|
-
const argsSummary = bc.args ?
|
|
20762
|
-
const reason = bc.checkedBy ?
|
|
20723
|
+
const ts = bc.timestamp ? import_chalk24.default.dim(fmtTime(bc.timestamp) + " ") : " ";
|
|
20724
|
+
const label = import_chalk24.default.red("\u{1F6D1} BLOCKED".padEnd(16));
|
|
20725
|
+
const toolName = import_chalk24.default.red(bc.tool.padEnd(10));
|
|
20726
|
+
const argsSummary = bc.args ? import_chalk24.default.gray(truncate(toolInputSummary(bc.tool, bc.args), 40)) : import_chalk24.default.dim("[args not logged]");
|
|
20727
|
+
const reason = bc.checkedBy ? import_chalk24.default.dim(" \u2190 " + bc.checkedBy) : "";
|
|
20763
20728
|
console.log(` ${ts}${label} ${toolName} ${argsSummary}${reason}`);
|
|
20764
20729
|
}
|
|
20765
20730
|
}
|
|
20766
20731
|
console.log("");
|
|
20767
20732
|
if (s.modifiedFiles.length > 0) {
|
|
20768
|
-
console.log(
|
|
20733
|
+
console.log(import_chalk24.default.bold(` Files modified (${s.modifiedFiles.length}):`));
|
|
20769
20734
|
for (const f of s.modifiedFiles) {
|
|
20770
|
-
console.log(" " +
|
|
20735
|
+
console.log(" " + import_chalk24.default.yellow(f));
|
|
20771
20736
|
}
|
|
20772
20737
|
console.log("");
|
|
20773
20738
|
}
|
|
@@ -20775,19 +20740,19 @@ function renderDetail(s) {
|
|
|
20775
20740
|
function registerSessionsCommand(program2) {
|
|
20776
20741
|
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) => {
|
|
20777
20742
|
console.log("");
|
|
20778
|
-
console.log(
|
|
20743
|
+
console.log(import_chalk24.default.cyan.bold("\u{1F4CB} node9 sessions") + import_chalk24.default.dim(" \u2014 what your AI agent did"));
|
|
20779
20744
|
console.log("");
|
|
20780
20745
|
const historyPath = import_path42.default.join(import_os36.default.homedir(), ".claude", "history.jsonl");
|
|
20781
20746
|
if (!import_fs40.default.existsSync(historyPath)) {
|
|
20782
|
-
console.log(
|
|
20783
|
-
console.log(
|
|
20747
|
+
console.log(import_chalk24.default.yellow(" No Claude session history found at ~/.claude/history.jsonl"));
|
|
20748
|
+
console.log(import_chalk24.default.gray(" Install Claude Code, run a few sessions, then try again.\n"));
|
|
20784
20749
|
return;
|
|
20785
20750
|
}
|
|
20786
20751
|
const days = options.detail || options.all ? null : Math.max(1, parseInt(options.days, 10) || 7);
|
|
20787
20752
|
const rangeLabel = options.detail ? "all time" : options.all ? "all time" : `last ${String(days)} days`;
|
|
20788
|
-
console.log(
|
|
20753
|
+
console.log(import_chalk24.default.dim(" " + rangeLabel));
|
|
20789
20754
|
console.log("");
|
|
20790
|
-
process.stdout.write(
|
|
20755
|
+
process.stdout.write(import_chalk24.default.dim(" Loading\u2026"));
|
|
20791
20756
|
const summaries = buildSessions(days);
|
|
20792
20757
|
if (process.stdout.isTTY) {
|
|
20793
20758
|
process.stdout.clearLine(0);
|
|
@@ -20800,8 +20765,8 @@ function registerSessionsCommand(program2) {
|
|
|
20800
20765
|
(s) => s.sessionId === options.detail || s.sessionId.startsWith(options.detail)
|
|
20801
20766
|
);
|
|
20802
20767
|
if (!target) {
|
|
20803
|
-
console.log(
|
|
20804
|
-
console.log(
|
|
20768
|
+
console.log(import_chalk24.default.red(` Session not found: ${options.detail}`));
|
|
20769
|
+
console.log(import_chalk24.default.dim(" Run `node9 sessions` to list recent sessions.\n"));
|
|
20805
20770
|
return;
|
|
20806
20771
|
}
|
|
20807
20772
|
renderDetail(target);
|
|
@@ -20814,7 +20779,7 @@ function registerSessionsCommand(program2) {
|
|
|
20814
20779
|
}
|
|
20815
20780
|
|
|
20816
20781
|
// src/cli/commands/skill-pin.ts
|
|
20817
|
-
var
|
|
20782
|
+
var import_chalk25 = __toESM(require("chalk"));
|
|
20818
20783
|
var import_fs41 = __toESM(require("fs"));
|
|
20819
20784
|
var import_os37 = __toESM(require("os"));
|
|
20820
20785
|
var import_path43 = __toESM(require("path"));
|
|
@@ -20834,29 +20799,29 @@ function registerSkillPinCommand(program2) {
|
|
|
20834
20799
|
const result = readSkillPinsSafe();
|
|
20835
20800
|
if (!result.ok) {
|
|
20836
20801
|
if (result.reason === "missing") {
|
|
20837
|
-
console.log(
|
|
20802
|
+
console.log(import_chalk25.default.gray("\nNo skill roots are pinned yet."));
|
|
20838
20803
|
console.log(
|
|
20839
|
-
|
|
20804
|
+
import_chalk25.default.gray("Pins are created automatically on the first tool call of each session.\n")
|
|
20840
20805
|
);
|
|
20841
20806
|
return;
|
|
20842
20807
|
}
|
|
20843
|
-
console.error(
|
|
20808
|
+
console.error(import_chalk25.default.red(`
|
|
20844
20809
|
\u274C Pin file is corrupt: ${result.detail}`));
|
|
20845
|
-
console.error(
|
|
20810
|
+
console.error(import_chalk25.default.yellow(" Run: node9 skill pin reset\n"));
|
|
20846
20811
|
process.exit(1);
|
|
20847
20812
|
}
|
|
20848
20813
|
const entries = Object.entries(result.pins.roots);
|
|
20849
20814
|
if (entries.length === 0) {
|
|
20850
|
-
console.log(
|
|
20815
|
+
console.log(import_chalk25.default.gray("\nNo skill roots are pinned yet.\n"));
|
|
20851
20816
|
return;
|
|
20852
20817
|
}
|
|
20853
|
-
console.log(
|
|
20818
|
+
console.log(import_chalk25.default.bold("\n\u{1F512} Pinned Skill Roots\n"));
|
|
20854
20819
|
for (const [key, entry] of entries) {
|
|
20855
|
-
const missing = entry.exists ? "" :
|
|
20856
|
-
console.log(` ${
|
|
20820
|
+
const missing = entry.exists ? "" : import_chalk25.default.yellow(" (not present at pin time)");
|
|
20821
|
+
console.log(` ${import_chalk25.default.cyan(key)} ${import_chalk25.default.gray(entry.rootPath)}${missing}`);
|
|
20857
20822
|
console.log(` Files (${entry.fileCount})`);
|
|
20858
|
-
console.log(` Hash: ${
|
|
20859
|
-
console.log(` Pinned: ${
|
|
20823
|
+
console.log(` Hash: ${import_chalk25.default.gray(entry.contentHash.slice(0, 16))}...`);
|
|
20824
|
+
console.log(` Pinned: ${import_chalk25.default.gray(entry.pinnedAt)}
|
|
20860
20825
|
`);
|
|
20861
20826
|
}
|
|
20862
20827
|
});
|
|
@@ -20865,39 +20830,39 @@ function registerSkillPinCommand(program2) {
|
|
|
20865
20830
|
try {
|
|
20866
20831
|
pins = readSkillPins();
|
|
20867
20832
|
} catch {
|
|
20868
|
-
console.error(
|
|
20869
|
-
console.error(
|
|
20833
|
+
console.error(import_chalk25.default.red("\n\u274C Pin file is corrupt."));
|
|
20834
|
+
console.error(import_chalk25.default.yellow(" Run: node9 skill pin reset\n"));
|
|
20870
20835
|
process.exit(1);
|
|
20871
20836
|
}
|
|
20872
20837
|
if (!pins.roots[rootKey]) {
|
|
20873
|
-
console.error(
|
|
20838
|
+
console.error(import_chalk25.default.red(`
|
|
20874
20839
|
\u274C No pin found for root key "${rootKey}"
|
|
20875
20840
|
`));
|
|
20876
|
-
console.error(`Run ${
|
|
20841
|
+
console.error(`Run ${import_chalk25.default.cyan("node9 skill pin list")} to see pinned roots.
|
|
20877
20842
|
`);
|
|
20878
20843
|
process.exit(1);
|
|
20879
20844
|
}
|
|
20880
20845
|
const rootPath = pins.roots[rootKey].rootPath;
|
|
20881
20846
|
removePin(rootKey);
|
|
20882
20847
|
wipeSkillSessions();
|
|
20883
|
-
console.log(
|
|
20884
|
-
\u{1F513} Pin removed for ${
|
|
20885
|
-
console.log(
|
|
20886
|
-
console.log(
|
|
20848
|
+
console.log(import_chalk25.default.green(`
|
|
20849
|
+
\u{1F513} Pin removed for ${import_chalk25.default.cyan(rootKey)}`));
|
|
20850
|
+
console.log(import_chalk25.default.gray(` ${rootPath}`));
|
|
20851
|
+
console.log(import_chalk25.default.gray(" Next session will re-pin with current state.\n"));
|
|
20887
20852
|
});
|
|
20888
20853
|
pinSubCmd.command("reset").description("Clear all skill pins and wipe session verification flags").action(() => {
|
|
20889
20854
|
const result = readSkillPinsSafe();
|
|
20890
20855
|
if (!result.ok && result.reason === "missing") {
|
|
20891
20856
|
wipeSkillSessions();
|
|
20892
|
-
console.log(
|
|
20857
|
+
console.log(import_chalk25.default.gray("\nNo pins to clear.\n"));
|
|
20893
20858
|
return;
|
|
20894
20859
|
}
|
|
20895
20860
|
const count = result.ok ? Object.keys(result.pins.roots).length : "?";
|
|
20896
20861
|
clearAllPins();
|
|
20897
20862
|
wipeSkillSessions();
|
|
20898
|
-
console.log(
|
|
20863
|
+
console.log(import_chalk25.default.green(`
|
|
20899
20864
|
\u{1F513} Cleared ${count} skill pin(s).`));
|
|
20900
|
-
console.log(
|
|
20865
|
+
console.log(import_chalk25.default.gray(" Next session will re-pin with current state.\n"));
|
|
20901
20866
|
});
|
|
20902
20867
|
}
|
|
20903
20868
|
|
|
@@ -20905,7 +20870,7 @@ function registerSkillPinCommand(program2) {
|
|
|
20905
20870
|
var import_fs42 = __toESM(require("fs"));
|
|
20906
20871
|
var import_os38 = __toESM(require("os"));
|
|
20907
20872
|
var import_path44 = __toESM(require("path"));
|
|
20908
|
-
var
|
|
20873
|
+
var import_chalk26 = __toESM(require("chalk"));
|
|
20909
20874
|
var DECISIONS_FILE2 = import_path44.default.join(import_os38.default.homedir(), ".node9", "decisions.json");
|
|
20910
20875
|
function readDecisions() {
|
|
20911
20876
|
try {
|
|
@@ -20934,55 +20899,55 @@ function registerDecisionsCommand(program2) {
|
|
|
20934
20899
|
const decisions = readDecisions();
|
|
20935
20900
|
const entries = Object.entries(decisions);
|
|
20936
20901
|
if (entries.length === 0) {
|
|
20937
|
-
console.log(
|
|
20902
|
+
console.log(import_chalk26.default.gray(" No persistent decisions stored."));
|
|
20938
20903
|
console.log(
|
|
20939
|
-
|
|
20940
|
-
`) +
|
|
20904
|
+
import_chalk26.default.gray(` File: ${DECISIONS_FILE2}
|
|
20905
|
+
`) + import_chalk26.default.gray(' Decisions are written when you click "Always Allow" or')
|
|
20941
20906
|
);
|
|
20942
|
-
console.log(
|
|
20907
|
+
console.log(import_chalk26.default.gray(' "Always Deny" in node9 tail or the native popup.'));
|
|
20943
20908
|
return;
|
|
20944
20909
|
}
|
|
20945
|
-
console.log(
|
|
20910
|
+
console.log(import_chalk26.default.bold(`
|
|
20946
20911
|
Persistent decisions (${entries.length})
|
|
20947
20912
|
`));
|
|
20948
20913
|
const w = Math.max(...entries.map(([k]) => k.length));
|
|
20949
20914
|
for (const [tool, verdict] of entries.sort()) {
|
|
20950
|
-
const colored = verdict === "allow" ?
|
|
20915
|
+
const colored = verdict === "allow" ? import_chalk26.default.green(verdict) : import_chalk26.default.red(verdict);
|
|
20951
20916
|
console.log(` ${tool.padEnd(w)} ${colored}`);
|
|
20952
20917
|
}
|
|
20953
20918
|
console.log(
|
|
20954
|
-
|
|
20919
|
+
import_chalk26.default.gray(`
|
|
20955
20920
|
Stored in ${DECISIONS_FILE2}
|
|
20956
|
-
`) +
|
|
20921
|
+
`) + import_chalk26.default.gray(" Run `node9 decisions clear <tool>` to remove an entry.")
|
|
20957
20922
|
);
|
|
20958
20923
|
});
|
|
20959
20924
|
cmd.command("clear <toolName>").description("Remove a persistent decision for one tool").action((toolName) => {
|
|
20960
20925
|
const decisions = readDecisions();
|
|
20961
20926
|
if (!(toolName in decisions)) {
|
|
20962
|
-
console.log(
|
|
20927
|
+
console.log(import_chalk26.default.yellow(` No persistent decision for "${toolName}". Nothing to clear.`));
|
|
20963
20928
|
process.exitCode = 1;
|
|
20964
20929
|
return;
|
|
20965
20930
|
}
|
|
20966
20931
|
delete decisions[toolName];
|
|
20967
20932
|
writeDecisions(decisions);
|
|
20968
|
-
console.log(
|
|
20933
|
+
console.log(import_chalk26.default.green(` \u2713 Cleared persistent decision for "${toolName}".`));
|
|
20969
20934
|
});
|
|
20970
20935
|
cmd.command("clear-all").description("Remove every persistent decision (irreversible)").action(() => {
|
|
20971
20936
|
const decisions = readDecisions();
|
|
20972
20937
|
const count = Object.keys(decisions).length;
|
|
20973
20938
|
if (count === 0) {
|
|
20974
|
-
console.log(
|
|
20939
|
+
console.log(import_chalk26.default.gray(" Nothing to clear \u2014 no persistent decisions stored."));
|
|
20975
20940
|
return;
|
|
20976
20941
|
}
|
|
20977
20942
|
writeDecisions({});
|
|
20978
20943
|
console.log(
|
|
20979
|
-
|
|
20944
|
+
import_chalk26.default.green(` \u2713 Cleared ${count} persistent decision${count === 1 ? "" : "s"}.`)
|
|
20980
20945
|
);
|
|
20981
20946
|
});
|
|
20982
20947
|
}
|
|
20983
20948
|
|
|
20984
20949
|
// src/cli/commands/dlp.ts
|
|
20985
|
-
var
|
|
20950
|
+
var import_chalk27 = __toESM(require("chalk"));
|
|
20986
20951
|
var import_fs43 = __toESM(require("fs"));
|
|
20987
20952
|
var import_path45 = __toESM(require("path"));
|
|
20988
20953
|
var import_os39 = __toESM(require("os"));
|
|
@@ -21037,14 +21002,14 @@ function registerDlpCommand(program2) {
|
|
|
21037
21002
|
cmd.command("resolve").description("Mark all current DLP findings as resolved").action(() => {
|
|
21038
21003
|
const findings = loadDlpFindings();
|
|
21039
21004
|
if (findings.length === 0) {
|
|
21040
|
-
console.log(
|
|
21005
|
+
console.log(import_chalk27.default.green("\n \u2705 No response-DLP findings to resolve.\n"));
|
|
21041
21006
|
return;
|
|
21042
21007
|
}
|
|
21043
21008
|
const resolved = loadResolved();
|
|
21044
21009
|
for (const e of findings) resolved.add(entryKey(e));
|
|
21045
21010
|
saveResolved(resolved);
|
|
21046
21011
|
console.log(
|
|
21047
|
-
|
|
21012
|
+
import_chalk27.default.green(
|
|
21048
21013
|
`
|
|
21049
21014
|
\u2705 ${findings.length} finding${findings.length !== 1 ? "s" : ""} marked as resolved.
|
|
21050
21015
|
`
|
|
@@ -21058,54 +21023,54 @@ function registerDlpCommand(program2) {
|
|
|
21058
21023
|
const resolvedCount = findings.length - open.length;
|
|
21059
21024
|
console.log("");
|
|
21060
21025
|
console.log(
|
|
21061
|
-
|
|
21026
|
+
import_chalk27.default.bold.cyan("\u{1F510} node9 dlp") + import_chalk27.default.dim(" \u2014 secrets found in Claude response text")
|
|
21062
21027
|
);
|
|
21063
21028
|
console.log("");
|
|
21064
21029
|
if (open.length === 0) {
|
|
21065
21030
|
if (resolvedCount > 0) {
|
|
21066
|
-
console.log(
|
|
21031
|
+
console.log(import_chalk27.default.green(` \u2705 No open findings \xB7 ${resolvedCount} previously resolved`));
|
|
21067
21032
|
} else {
|
|
21068
21033
|
console.log(
|
|
21069
|
-
|
|
21034
|
+
import_chalk27.default.green(" \u2705 No findings \u2014 Claude has not leaked secrets in response text")
|
|
21070
21035
|
);
|
|
21071
21036
|
}
|
|
21072
21037
|
console.log("");
|
|
21073
21038
|
return;
|
|
21074
21039
|
}
|
|
21075
21040
|
console.log(
|
|
21076
|
-
|
|
21041
|
+
import_chalk27.default.bgRed.white.bold(` \u26A0\uFE0F ${open.length} open finding${open.length !== 1 ? "s" : ""} `) + import_chalk27.default.dim(resolvedCount > 0 ? ` (${resolvedCount} resolved)` : "")
|
|
21077
21042
|
);
|
|
21078
21043
|
console.log("");
|
|
21079
21044
|
console.log(
|
|
21080
|
-
|
|
21045
|
+
import_chalk27.default.dim(" These secrets were included in Claude's response text \u2014 NOT blocked.")
|
|
21081
21046
|
);
|
|
21082
|
-
console.log(
|
|
21047
|
+
console.log(import_chalk27.default.dim(" Rotate each affected key immediately.\n"));
|
|
21083
21048
|
for (const e of open) {
|
|
21084
21049
|
console.log(
|
|
21085
|
-
" " +
|
|
21050
|
+
" " + import_chalk27.default.red("\u25CF") + " " + import_chalk27.default.white(e.dlpPattern ?? "Secret") + import_chalk27.default.dim(" " + fmtDate3(e.ts))
|
|
21086
21051
|
);
|
|
21087
21052
|
if (e.dlpSample) {
|
|
21088
|
-
console.log(" " +
|
|
21053
|
+
console.log(" " + import_chalk27.default.dim("Sample: ") + import_chalk27.default.yellow(stripAnsi(e.dlpSample)));
|
|
21089
21054
|
}
|
|
21090
21055
|
if (e.project) {
|
|
21091
|
-
console.log(" " +
|
|
21056
|
+
console.log(" " + import_chalk27.default.dim("Project: ") + import_chalk27.default.dim(stripAnsi(e.project)));
|
|
21092
21057
|
}
|
|
21093
21058
|
console.log("");
|
|
21094
21059
|
}
|
|
21095
|
-
console.log(" " +
|
|
21096
|
-
console.log(" " +
|
|
21060
|
+
console.log(" " + import_chalk27.default.bold("Next steps:"));
|
|
21061
|
+
console.log(" " + import_chalk27.default.cyan("1.") + " Rotate any exposed keys shown above");
|
|
21097
21062
|
console.log(
|
|
21098
|
-
" " +
|
|
21063
|
+
" " + import_chalk27.default.cyan("2.") + " Run " + import_chalk27.default.white("node9 dlp resolve") + " to acknowledge"
|
|
21099
21064
|
);
|
|
21100
21065
|
console.log(
|
|
21101
|
-
" " +
|
|
21066
|
+
" " + import_chalk27.default.cyan("3.") + " Run " + import_chalk27.default.white("node9 report") + " for full audit history"
|
|
21102
21067
|
);
|
|
21103
21068
|
console.log("");
|
|
21104
21069
|
});
|
|
21105
21070
|
}
|
|
21106
21071
|
|
|
21107
21072
|
// src/cli/commands/mask.ts
|
|
21108
|
-
var
|
|
21073
|
+
var import_chalk28 = __toESM(require("chalk"));
|
|
21109
21074
|
var import_fs44 = __toESM(require("fs"));
|
|
21110
21075
|
var import_path46 = __toESM(require("path"));
|
|
21111
21076
|
var import_os40 = __toESM(require("os"));
|
|
@@ -21242,12 +21207,12 @@ function registerMaskCommand(program2) {
|
|
|
21242
21207
|
}
|
|
21243
21208
|
}) : allFiles;
|
|
21244
21209
|
if (filtered.length === 0) {
|
|
21245
|
-
console.log(
|
|
21210
|
+
console.log(import_chalk28.default.yellow(" No session files found."));
|
|
21246
21211
|
return;
|
|
21247
21212
|
}
|
|
21248
21213
|
console.log("");
|
|
21249
21214
|
if (dryRun) {
|
|
21250
|
-
console.log(
|
|
21215
|
+
console.log(import_chalk28.default.dim(" Dry run \u2014 no files will be modified.\n"));
|
|
21251
21216
|
}
|
|
21252
21217
|
let totalFiles = 0;
|
|
21253
21218
|
let totalLines = 0;
|
|
@@ -21263,23 +21228,23 @@ function registerMaskCommand(program2) {
|
|
|
21263
21228
|
});
|
|
21264
21229
|
const verb = dryRun ? "Would redact" : "Redacted";
|
|
21265
21230
|
console.log(
|
|
21266
|
-
" " +
|
|
21231
|
+
" " + 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" : ""})`)
|
|
21267
21232
|
);
|
|
21268
21233
|
}
|
|
21269
21234
|
}
|
|
21270
21235
|
console.log("");
|
|
21271
21236
|
if (totalFiles === 0) {
|
|
21272
|
-
console.log(
|
|
21237
|
+
console.log(import_chalk28.default.green(" No secrets found in session history."));
|
|
21273
21238
|
} else {
|
|
21274
21239
|
const verb = dryRun ? "would be modified" : "modified";
|
|
21275
21240
|
console.log(
|
|
21276
|
-
|
|
21241
|
+
import_chalk28.default.bold(` ${totalFiles} file${totalFiles !== 1 ? "s" : ""} ${verb}`) + import_chalk28.default.dim(`, ${totalLines} line${totalLines !== 1 ? "s" : ""} redacted`)
|
|
21277
21242
|
);
|
|
21278
|
-
console.log(" Patterns: " +
|
|
21243
|
+
console.log(" Patterns: " + import_chalk28.default.yellow(totalPatterns.join(", ")));
|
|
21279
21244
|
if (!dryRun) {
|
|
21280
21245
|
console.log("");
|
|
21281
21246
|
console.log(
|
|
21282
|
-
|
|
21247
|
+
import_chalk28.default.dim(
|
|
21283
21248
|
" Note: secrets were already sent to the AI provider during the active session.\n This cleans your local disk only. Rotate any exposed keys."
|
|
21284
21249
|
)
|
|
21285
21250
|
);
|
|
@@ -21343,17 +21308,17 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
21343
21308
|
import_fs47.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
21344
21309
|
}
|
|
21345
21310
|
if (options.profile && profileName !== "default") {
|
|
21346
|
-
console.log(
|
|
21347
|
-
console.log(
|
|
21311
|
+
console.log(import_chalk30.default.green(`\u2705 Profile "${profileName}" saved`));
|
|
21312
|
+
console.log(import_chalk30.default.gray(` Switch to it per-session: NODE9_PROFILE=${profileName} claude`));
|
|
21348
21313
|
} else if (options.local) {
|
|
21349
|
-
console.log(
|
|
21350
|
-
console.log(
|
|
21314
|
+
console.log(import_chalk30.default.green(`\u2705 Privacy mode \u{1F6E1}\uFE0F`));
|
|
21315
|
+
console.log(import_chalk30.default.gray(` All decisions stay on this machine.`));
|
|
21351
21316
|
} else {
|
|
21352
|
-
console.log(
|
|
21353
|
-
console.log(
|
|
21317
|
+
console.log(import_chalk30.default.green(`\u2705 Logged in \u2014 agent mode`));
|
|
21318
|
+
console.log(import_chalk30.default.gray(` Team policy enforced for all calls via Node9 cloud.`));
|
|
21354
21319
|
}
|
|
21355
21320
|
});
|
|
21356
|
-
program.command("addto").description("Integrate Node9 with an AI agent").addHelpText(
|
|
21321
|
+
program.command("addto", { hidden: true }).description("Integrate Node9 with an AI agent").addHelpText(
|
|
21357
21322
|
"after",
|
|
21358
21323
|
"\n Supported targets: claude gemini cursor codex windsurf vscode hud"
|
|
21359
21324
|
).argument(
|
|
@@ -21368,13 +21333,13 @@ program.command("addto").description("Integrate Node9 with an AI agent").addHelp
|
|
|
21368
21333
|
if (target === "vscode") return await setupVSCode();
|
|
21369
21334
|
if (target === "hud") return setupHud();
|
|
21370
21335
|
console.error(
|
|
21371
|
-
|
|
21336
|
+
import_chalk30.default.red(
|
|
21372
21337
|
`Unknown target: "${target}". Supported: claude, gemini, cursor, codex, windsurf, vscode, hud`
|
|
21373
21338
|
)
|
|
21374
21339
|
);
|
|
21375
21340
|
process.exit(1);
|
|
21376
21341
|
});
|
|
21377
|
-
program.command("setup").description('Alias for "addto" \u2014 integrate Node9 with an AI agent').addHelpText(
|
|
21342
|
+
program.command("setup", { hidden: true }).description('Alias for "addto" \u2014 integrate Node9 with an AI agent').addHelpText(
|
|
21378
21343
|
"after",
|
|
21379
21344
|
"\n Supported targets: claude gemini cursor codex windsurf vscode hud"
|
|
21380
21345
|
).argument(
|
|
@@ -21382,17 +21347,17 @@ program.command("setup").description('Alias for "addto" \u2014 integrate Node9 w
|
|
|
21382
21347
|
"The agent to protect: claude | gemini | cursor | codex | windsurf | vscode | hud"
|
|
21383
21348
|
).action(async (target) => {
|
|
21384
21349
|
if (!target) {
|
|
21385
|
-
console.log(
|
|
21386
|
-
console.log(" Usage: " +
|
|
21350
|
+
console.log(import_chalk30.default.cyan("\n\u{1F6E1}\uFE0F Node9 Setup \u2014 integrate with your AI agent\n"));
|
|
21351
|
+
console.log(" Usage: " + import_chalk30.default.white("node9 setup <target>") + "\n");
|
|
21387
21352
|
console.log(" Targets:");
|
|
21388
|
-
console.log(" " +
|
|
21389
|
-
console.log(" " +
|
|
21390
|
-
console.log(" " +
|
|
21391
|
-
console.log(" " +
|
|
21392
|
-
console.log(" " +
|
|
21393
|
-
console.log(" " +
|
|
21353
|
+
console.log(" " + import_chalk30.default.green("claude") + " \u2014 Claude Code (hook mode)");
|
|
21354
|
+
console.log(" " + import_chalk30.default.green("gemini") + " \u2014 Gemini CLI (hook mode)");
|
|
21355
|
+
console.log(" " + import_chalk30.default.green("cursor") + " \u2014 Cursor (MCP proxy)");
|
|
21356
|
+
console.log(" " + import_chalk30.default.green("codex") + " \u2014 OpenAI Codex CLI (MCP proxy)");
|
|
21357
|
+
console.log(" " + import_chalk30.default.green("windsurf") + " \u2014 Windsurf (MCP proxy)");
|
|
21358
|
+
console.log(" " + import_chalk30.default.green("vscode") + " \u2014 VSCode / Copilot (MCP proxy)");
|
|
21394
21359
|
process.stdout.write(
|
|
21395
|
-
" " +
|
|
21360
|
+
" " + import_chalk30.default.green("hud") + " \u2014 Claude Code security statusline\n"
|
|
21396
21361
|
);
|
|
21397
21362
|
console.log("");
|
|
21398
21363
|
return;
|
|
@@ -21406,13 +21371,13 @@ program.command("setup").description('Alias for "addto" \u2014 integrate Node9 w
|
|
|
21406
21371
|
if (t === "vscode") return await setupVSCode();
|
|
21407
21372
|
if (t === "hud") return setupHud();
|
|
21408
21373
|
console.error(
|
|
21409
|
-
|
|
21374
|
+
import_chalk30.default.red(
|
|
21410
21375
|
`Unknown target: "${target}". Supported: claude, gemini, cursor, codex, windsurf, vscode, hud`
|
|
21411
21376
|
)
|
|
21412
21377
|
);
|
|
21413
21378
|
process.exit(1);
|
|
21414
21379
|
});
|
|
21415
|
-
program.command("removefrom").description("Remove Node9 hooks from an AI agent configuration").addHelpText(
|
|
21380
|
+
program.command("removefrom", { hidden: true }).description("Remove Node9 hooks from an AI agent configuration").addHelpText(
|
|
21416
21381
|
"after",
|
|
21417
21382
|
"\n Supported targets: claude gemini cursor codex windsurf vscode hud"
|
|
21418
21383
|
).argument(
|
|
@@ -21429,33 +21394,33 @@ program.command("removefrom").description("Remove Node9 hooks from an AI agent c
|
|
|
21429
21394
|
else if (target === "hud") fn = teardownHud;
|
|
21430
21395
|
else {
|
|
21431
21396
|
console.error(
|
|
21432
|
-
|
|
21397
|
+
import_chalk30.default.red(
|
|
21433
21398
|
`Unknown target: "${target}". Supported: claude, gemini, cursor, codex, windsurf, vscode, hud`
|
|
21434
21399
|
)
|
|
21435
21400
|
);
|
|
21436
21401
|
process.exit(1);
|
|
21437
21402
|
}
|
|
21438
|
-
console.log(
|
|
21403
|
+
console.log(import_chalk30.default.cyan(`
|
|
21439
21404
|
\u{1F6E1}\uFE0F Node9: removing hooks from ${target}...
|
|
21440
21405
|
`));
|
|
21441
21406
|
try {
|
|
21442
21407
|
fn();
|
|
21443
21408
|
} catch (err2) {
|
|
21444
|
-
console.error(
|
|
21409
|
+
console.error(import_chalk30.default.red(` \u26A0\uFE0F Failed: ${err2 instanceof Error ? err2.message : String(err2)}`));
|
|
21445
21410
|
process.exit(1);
|
|
21446
21411
|
}
|
|
21447
|
-
console.log(
|
|
21412
|
+
console.log(import_chalk30.default.gray("\n Restart the agent for changes to take effect."));
|
|
21448
21413
|
});
|
|
21449
21414
|
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) => {
|
|
21450
|
-
console.log(
|
|
21451
|
-
console.log(
|
|
21415
|
+
console.log(import_chalk30.default.cyan("\n\u{1F6E1}\uFE0F Node9 Uninstall\n"));
|
|
21416
|
+
console.log(import_chalk30.default.bold("Stopping daemon..."));
|
|
21452
21417
|
try {
|
|
21453
21418
|
stopDaemon();
|
|
21454
|
-
console.log(
|
|
21419
|
+
console.log(import_chalk30.default.green(" \u2705 Daemon stopped"));
|
|
21455
21420
|
} catch {
|
|
21456
|
-
console.log(
|
|
21421
|
+
console.log(import_chalk30.default.blue(" \u2139\uFE0F Daemon was not running"));
|
|
21457
21422
|
}
|
|
21458
|
-
console.log(
|
|
21423
|
+
console.log(import_chalk30.default.bold("\nRemoving hooks..."));
|
|
21459
21424
|
let teardownFailed = false;
|
|
21460
21425
|
for (const [label, fn] of [
|
|
21461
21426
|
["Claude", teardownClaude],
|
|
@@ -21470,7 +21435,7 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
21470
21435
|
} catch (err2) {
|
|
21471
21436
|
teardownFailed = true;
|
|
21472
21437
|
console.error(
|
|
21473
|
-
|
|
21438
|
+
import_chalk30.default.red(
|
|
21474
21439
|
` \u26A0\uFE0F Failed to remove ${label} hooks: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
21475
21440
|
)
|
|
21476
21441
|
);
|
|
@@ -21487,28 +21452,28 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
21487
21452
|
import_fs47.default.rmSync(node9Dir, { recursive: true });
|
|
21488
21453
|
if (import_fs47.default.existsSync(node9Dir)) {
|
|
21489
21454
|
console.error(
|
|
21490
|
-
|
|
21455
|
+
import_chalk30.default.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
|
|
21491
21456
|
);
|
|
21492
21457
|
} else {
|
|
21493
|
-
console.log(
|
|
21458
|
+
console.log(import_chalk30.default.green("\n \u2705 Deleted ~/.node9/ (config, audit log, credentials)"));
|
|
21494
21459
|
}
|
|
21495
21460
|
} else {
|
|
21496
|
-
console.log(
|
|
21461
|
+
console.log(import_chalk30.default.yellow("\n Skipped \u2014 ~/.node9/ was not deleted."));
|
|
21497
21462
|
}
|
|
21498
21463
|
} else {
|
|
21499
|
-
console.log(
|
|
21464
|
+
console.log(import_chalk30.default.blue("\n \u2139\uFE0F ~/.node9/ not found \u2014 nothing to delete"));
|
|
21500
21465
|
}
|
|
21501
21466
|
} else {
|
|
21502
21467
|
console.log(
|
|
21503
|
-
|
|
21468
|
+
import_chalk30.default.gray("\n ~/.node9/ kept \u2014 run with --purge to delete config and audit log")
|
|
21504
21469
|
);
|
|
21505
21470
|
}
|
|
21506
21471
|
if (teardownFailed) {
|
|
21507
|
-
console.error(
|
|
21472
|
+
console.error(import_chalk30.default.red("\n \u26A0\uFE0F Some hooks could not be removed \u2014 see errors above."));
|
|
21508
21473
|
process.exit(1);
|
|
21509
21474
|
}
|
|
21510
|
-
console.log(
|
|
21511
|
-
console.log(
|
|
21475
|
+
console.log(import_chalk30.default.green.bold("\n\u{1F6E1}\uFE0F Node9 removed. Run: npm uninstall -g @node9/proxy"));
|
|
21476
|
+
console.log(import_chalk30.default.gray(" Restart any open AI agent sessions for changes to take effect.\n"));
|
|
21512
21477
|
});
|
|
21513
21478
|
registerDoctorCommand(program, version);
|
|
21514
21479
|
program.command("explain").description(
|
|
@@ -21521,7 +21486,7 @@ program.command("explain").description(
|
|
|
21521
21486
|
try {
|
|
21522
21487
|
args = JSON.parse(trimmed);
|
|
21523
21488
|
} catch {
|
|
21524
|
-
console.error(
|
|
21489
|
+
console.error(import_chalk30.default.red(`
|
|
21525
21490
|
\u274C Invalid JSON: ${trimmed}
|
|
21526
21491
|
`));
|
|
21527
21492
|
process.exit(1);
|
|
@@ -21532,54 +21497,54 @@ program.command("explain").description(
|
|
|
21532
21497
|
}
|
|
21533
21498
|
const result = await explainPolicy(tool, args);
|
|
21534
21499
|
console.log("");
|
|
21535
|
-
console.log(
|
|
21500
|
+
console.log(import_chalk30.default.cyan.bold("\u{1F6E1}\uFE0F Node9 Explain"));
|
|
21536
21501
|
console.log("");
|
|
21537
|
-
console.log(` ${
|
|
21502
|
+
console.log(` ${import_chalk30.default.bold("Tool:")} ${import_chalk30.default.white(result.tool)}`);
|
|
21538
21503
|
if (argsRaw) {
|
|
21539
21504
|
const preview2 = argsRaw.length > 80 ? argsRaw.slice(0, 77) + "\u2026" : argsRaw;
|
|
21540
|
-
console.log(` ${
|
|
21505
|
+
console.log(` ${import_chalk30.default.bold("Input:")} ${import_chalk30.default.gray(preview2)}`);
|
|
21541
21506
|
}
|
|
21542
21507
|
console.log("");
|
|
21543
|
-
console.log(
|
|
21508
|
+
console.log(import_chalk30.default.bold("Config Sources (Waterfall):"));
|
|
21544
21509
|
for (const tier of result.waterfall) {
|
|
21545
|
-
const num3 =
|
|
21510
|
+
const num3 = import_chalk30.default.gray(` ${tier.tier}.`);
|
|
21546
21511
|
const label = tier.label.padEnd(16);
|
|
21547
21512
|
let statusStr;
|
|
21548
21513
|
if (tier.tier === 1) {
|
|
21549
|
-
statusStr =
|
|
21514
|
+
statusStr = import_chalk30.default.gray(tier.note ?? "");
|
|
21550
21515
|
} else if (tier.status === "active") {
|
|
21551
|
-
const loc = tier.path ?
|
|
21552
|
-
const note = tier.note ?
|
|
21553
|
-
statusStr =
|
|
21516
|
+
const loc = tier.path ? import_chalk30.default.gray(tier.path) : "";
|
|
21517
|
+
const note = tier.note ? import_chalk30.default.gray(`(${tier.note})`) : "";
|
|
21518
|
+
statusStr = import_chalk30.default.green("\u2713 active") + (loc ? " " + loc : "") + (note ? " " + note : "");
|
|
21554
21519
|
} else {
|
|
21555
|
-
statusStr =
|
|
21520
|
+
statusStr = import_chalk30.default.gray("\u25CB " + (tier.note ?? "not found"));
|
|
21556
21521
|
}
|
|
21557
|
-
console.log(`${num3} ${
|
|
21522
|
+
console.log(`${num3} ${import_chalk30.default.white(label)} ${statusStr}`);
|
|
21558
21523
|
}
|
|
21559
21524
|
console.log("");
|
|
21560
|
-
console.log(
|
|
21525
|
+
console.log(import_chalk30.default.bold("Policy Evaluation:"));
|
|
21561
21526
|
for (const step of result.steps) {
|
|
21562
21527
|
const isFinal = step.isFinal;
|
|
21563
21528
|
let icon;
|
|
21564
|
-
if (step.outcome === "allow") icon =
|
|
21565
|
-
else if (step.outcome === "review") icon =
|
|
21566
|
-
else if (step.outcome === "skip") icon =
|
|
21567
|
-
else icon =
|
|
21529
|
+
if (step.outcome === "allow") icon = import_chalk30.default.green(" \u2705");
|
|
21530
|
+
else if (step.outcome === "review") icon = import_chalk30.default.red(" \u{1F534}");
|
|
21531
|
+
else if (step.outcome === "skip") icon = import_chalk30.default.gray(" \u2500 ");
|
|
21532
|
+
else icon = import_chalk30.default.gray(" \u25CB ");
|
|
21568
21533
|
const name = step.name.padEnd(18);
|
|
21569
|
-
const nameStr = isFinal ?
|
|
21570
|
-
const detail = isFinal ?
|
|
21571
|
-
const arrow = isFinal ?
|
|
21534
|
+
const nameStr = isFinal ? import_chalk30.default.white.bold(name) : import_chalk30.default.white(name);
|
|
21535
|
+
const detail = isFinal ? import_chalk30.default.white(step.detail) : import_chalk30.default.gray(step.detail);
|
|
21536
|
+
const arrow = isFinal ? import_chalk30.default.yellow(" \u2190 STOP") : "";
|
|
21572
21537
|
console.log(`${icon} ${nameStr} ${detail}${arrow}`);
|
|
21573
21538
|
}
|
|
21574
21539
|
console.log("");
|
|
21575
21540
|
if (result.decision === "allow") {
|
|
21576
|
-
console.log(
|
|
21541
|
+
console.log(import_chalk30.default.green.bold(" Decision: \u2705 ALLOW") + import_chalk30.default.gray(" \u2014 no approval needed"));
|
|
21577
21542
|
} else {
|
|
21578
21543
|
console.log(
|
|
21579
|
-
|
|
21544
|
+
import_chalk30.default.red.bold(" Decision: \u{1F534} REVIEW") + import_chalk30.default.gray(" \u2014 human approval required")
|
|
21580
21545
|
);
|
|
21581
21546
|
if (result.blockedByLabel) {
|
|
21582
|
-
console.log(
|
|
21547
|
+
console.log(import_chalk30.default.gray(` Reason: ${result.blockedByLabel}`));
|
|
21583
21548
|
}
|
|
21584
21549
|
}
|
|
21585
21550
|
console.log("");
|
|
@@ -21594,7 +21559,7 @@ program.command("tail").description("Stream live agent activity to the terminal"
|
|
|
21594
21559
|
try {
|
|
21595
21560
|
await startTail2(options);
|
|
21596
21561
|
} catch (err2) {
|
|
21597
|
-
console.error(
|
|
21562
|
+
console.error(import_chalk30.default.red(`\u274C ${err2 instanceof Error ? err2.message : String(err2)}`));
|
|
21598
21563
|
process.exit(1);
|
|
21599
21564
|
}
|
|
21600
21565
|
});
|
|
@@ -21605,11 +21570,10 @@ program.command("monitor").description("Live interactive dashboard \u2014 activi
|
|
|
21605
21570
|
const mod = await dynamicImport(`file://${dashboardPath}`);
|
|
21606
21571
|
await mod.startMonitor();
|
|
21607
21572
|
} catch (err2) {
|
|
21608
|
-
console.error(
|
|
21573
|
+
console.error(import_chalk30.default.red(`\u274C ${err2 instanceof Error ? err2.message : String(err2)}`));
|
|
21609
21574
|
process.exit(1);
|
|
21610
21575
|
}
|
|
21611
21576
|
});
|
|
21612
|
-
registerWatchCommand(program);
|
|
21613
21577
|
registerMcpGatewayCommand(program);
|
|
21614
21578
|
registerMcpServerCommand(program);
|
|
21615
21579
|
registerMcpPinCommand(program);
|
|
@@ -21617,7 +21581,7 @@ registerSkillPinCommand(program);
|
|
|
21617
21581
|
registerDecisionsCommand(program);
|
|
21618
21582
|
registerCheckCommand(program);
|
|
21619
21583
|
registerLogCommand(program);
|
|
21620
|
-
program.command("hud").description("Render node9 security statusline (spawned by Claude Code statusLine)").addHelpText(
|
|
21584
|
+
program.command("hud", { hidden: true }).description("Render node9 security statusline (spawned by Claude Code statusLine)").addHelpText(
|
|
21621
21585
|
"after",
|
|
21622
21586
|
`
|
|
21623
21587
|
Outputs up to 3 lines to stdout, then exits:
|
|
@@ -21661,7 +21625,7 @@ program.command("pause").description("Temporarily disable Node9 protection for a
|
|
|
21661
21625
|
const ms = parseDuration(options.duration);
|
|
21662
21626
|
if (ms === null) {
|
|
21663
21627
|
console.error(
|
|
21664
|
-
|
|
21628
|
+
import_chalk30.default.red(`
|
|
21665
21629
|
\u274C Invalid duration: "${options.duration}". Use format like 15m, 1h, 30s.
|
|
21666
21630
|
`)
|
|
21667
21631
|
);
|
|
@@ -21669,20 +21633,20 @@ program.command("pause").description("Temporarily disable Node9 protection for a
|
|
|
21669
21633
|
}
|
|
21670
21634
|
pauseNode9(ms, options.duration);
|
|
21671
21635
|
const expiresAt = new Date(Date.now() + ms).toLocaleTimeString();
|
|
21672
|
-
console.log(
|
|
21636
|
+
console.log(import_chalk30.default.yellow(`
|
|
21673
21637
|
\u23F8 Node9 paused until ${expiresAt}`));
|
|
21674
|
-
console.log(
|
|
21675
|
-
console.log(
|
|
21638
|
+
console.log(import_chalk30.default.gray(` All tool calls will be allowed without review.`));
|
|
21639
|
+
console.log(import_chalk30.default.gray(` Run "node9 resume" to re-enable early.
|
|
21676
21640
|
`));
|
|
21677
21641
|
});
|
|
21678
21642
|
program.command("resume").description("Re-enable Node9 protection immediately").action(() => {
|
|
21679
21643
|
const { paused } = checkPause();
|
|
21680
21644
|
if (!paused) {
|
|
21681
|
-
console.log(
|
|
21645
|
+
console.log(import_chalk30.default.gray("\nNode9 is already active \u2014 nothing to resume.\n"));
|
|
21682
21646
|
return;
|
|
21683
21647
|
}
|
|
21684
21648
|
resumeNode9();
|
|
21685
|
-
console.log(
|
|
21649
|
+
console.log(import_chalk30.default.green("\n\u25B6 Node9 resumed \u2014 protection is active.\n"));
|
|
21686
21650
|
});
|
|
21687
21651
|
var HOOK_BASED_AGENTS = {
|
|
21688
21652
|
claude: "claude",
|
|
@@ -21695,15 +21659,15 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
21695
21659
|
if (HOOK_BASED_AGENTS[firstArg2] !== void 0) {
|
|
21696
21660
|
const target = HOOK_BASED_AGENTS[firstArg2];
|
|
21697
21661
|
console.error(
|
|
21698
|
-
|
|
21662
|
+
import_chalk30.default.yellow(`
|
|
21699
21663
|
\u26A0\uFE0F Node9 proxy mode does not support "${target}" directly.`)
|
|
21700
21664
|
);
|
|
21701
|
-
console.error(
|
|
21665
|
+
console.error(import_chalk30.default.white(`
|
|
21702
21666
|
"${target}" uses its own hook system. Use:`));
|
|
21703
21667
|
console.error(
|
|
21704
|
-
|
|
21668
|
+
import_chalk30.default.green(` node9 addto ${target} `) + import_chalk30.default.gray("# one-time setup")
|
|
21705
21669
|
);
|
|
21706
|
-
console.error(
|
|
21670
|
+
console.error(import_chalk30.default.green(` ${target} `) + import_chalk30.default.gray("# run normally"));
|
|
21707
21671
|
process.exit(1);
|
|
21708
21672
|
}
|
|
21709
21673
|
const runArgs = firstArg2 === "shell" ? commandArgs.slice(1) : commandArgs;
|
|
@@ -21720,7 +21684,7 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
21720
21684
|
}
|
|
21721
21685
|
);
|
|
21722
21686
|
if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && getConfig().settings.autoStartDaemon) {
|
|
21723
|
-
console.error(
|
|
21687
|
+
console.error(import_chalk30.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically..."));
|
|
21724
21688
|
const daemonReady = await autoStartDaemonAndWait();
|
|
21725
21689
|
if (daemonReady) result = await authorizeHeadless("shell", { command: fullCommand });
|
|
21726
21690
|
}
|
|
@@ -21733,12 +21697,12 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
21733
21697
|
}
|
|
21734
21698
|
if (!result.approved) {
|
|
21735
21699
|
console.error(
|
|
21736
|
-
|
|
21700
|
+
import_chalk30.default.red(`
|
|
21737
21701
|
\u274C Node9 Blocked: ${result.reason || "Dangerous command detected."}`)
|
|
21738
21702
|
);
|
|
21739
21703
|
process.exit(1);
|
|
21740
21704
|
}
|
|
21741
|
-
console.error(
|
|
21705
|
+
console.error(import_chalk30.default.green("\n\u2705 Approved \u2014 running command...\n"));
|
|
21742
21706
|
await runProxy(fullCommand);
|
|
21743
21707
|
} else {
|
|
21744
21708
|
program.help();
|