@node9/proxy 1.51.0 → 1.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +708 -574
- package/dist/cli.mjs +1557 -1423
- package/dist/dashboard.mjs +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -240,8 +240,8 @@ function sanitizeConfig(raw) {
|
|
|
240
240
|
}
|
|
241
241
|
}
|
|
242
242
|
const lines = result.error.issues.map((issue) => {
|
|
243
|
-
const
|
|
244
|
-
return ` \u2022 ${
|
|
243
|
+
const path66 = issue.path.length > 0 ? issue.path.join(".") : "root";
|
|
244
|
+
return ` \u2022 ${path66}: ${issue.message}`;
|
|
245
245
|
});
|
|
246
246
|
return {
|
|
247
247
|
sanitized,
|
|
@@ -1362,9 +1362,9 @@ function matchesPattern(text, patterns) {
|
|
|
1362
1362
|
const withoutDotSlash = text.replace(/^\.\//, "");
|
|
1363
1363
|
return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
|
|
1364
1364
|
}
|
|
1365
|
-
function getNestedValue(obj,
|
|
1365
|
+
function getNestedValue(obj, path66) {
|
|
1366
1366
|
if (!obj || typeof obj !== "object") return null;
|
|
1367
|
-
const segments =
|
|
1367
|
+
const segments = path66.split(".");
|
|
1368
1368
|
for (const seg of segments) {
|
|
1369
1369
|
if (FORBIDDEN_PATH_SEGMENTS.has(seg)) return null;
|
|
1370
1370
|
}
|
|
@@ -4297,11 +4297,11 @@ function getGlobalSettings() {
|
|
|
4297
4297
|
};
|
|
4298
4298
|
}
|
|
4299
4299
|
function getCredentials() {
|
|
4300
|
-
const
|
|
4300
|
+
const DEFAULT_API_URL3 = "https://api.node9.ai/api/v1/intercept";
|
|
4301
4301
|
if (process.env.NODE9_API_KEY) {
|
|
4302
4302
|
return {
|
|
4303
4303
|
apiKey: process.env.NODE9_API_KEY,
|
|
4304
|
-
apiUrl: process.env.NODE9_API_URL ||
|
|
4304
|
+
apiUrl: process.env.NODE9_API_URL || DEFAULT_API_URL3
|
|
4305
4305
|
};
|
|
4306
4306
|
}
|
|
4307
4307
|
try {
|
|
@@ -4313,13 +4313,13 @@ function getCredentials() {
|
|
|
4313
4313
|
if (profile?.apiKey) {
|
|
4314
4314
|
return {
|
|
4315
4315
|
apiKey: profile.apiKey,
|
|
4316
|
-
apiUrl: profile.apiUrl ||
|
|
4316
|
+
apiUrl: profile.apiUrl || DEFAULT_API_URL3
|
|
4317
4317
|
};
|
|
4318
4318
|
}
|
|
4319
4319
|
if (creds.apiKey) {
|
|
4320
4320
|
return {
|
|
4321
4321
|
apiKey: creds.apiKey,
|
|
4322
|
-
apiUrl: creds.apiUrl ||
|
|
4322
|
+
apiUrl: creds.apiUrl || DEFAULT_API_URL3
|
|
4323
4323
|
};
|
|
4324
4324
|
}
|
|
4325
4325
|
}
|
|
@@ -7787,6 +7787,35 @@ var init_setup_pi_shim = __esm({
|
|
|
7787
7787
|
});
|
|
7788
7788
|
|
|
7789
7789
|
// src/setup.ts
|
|
7790
|
+
function isNonInteractive() {
|
|
7791
|
+
return process.env.NODE9_NONINTERACTIVE === "1";
|
|
7792
|
+
}
|
|
7793
|
+
async function confirm(opts) {
|
|
7794
|
+
if (isNonInteractive()) return opts.default ?? false;
|
|
7795
|
+
return (0, import_prompts.confirm)(opts);
|
|
7796
|
+
}
|
|
7797
|
+
async function setupAgent(agent) {
|
|
7798
|
+
if (agent === "claude") await setupClaude();
|
|
7799
|
+
else if (agent === "gemini") await setupGemini();
|
|
7800
|
+
else if (agent === "antigravity") await setupAntigravity();
|
|
7801
|
+
else if (agent === "copilot") await setupCopilot();
|
|
7802
|
+
else if (agent === "cursor") await setupCursor();
|
|
7803
|
+
else if (agent === "codex") await setupCodex();
|
|
7804
|
+
else if (agent === "windsurf") await setupWindsurf();
|
|
7805
|
+
else if (agent === "vscode") await setupVSCode();
|
|
7806
|
+
else if (agent === "claudeDesktop") await setupClaudeDesktop();
|
|
7807
|
+
else if (agent === "opencode") await setupOpencode();
|
|
7808
|
+
else if (agent === "pi") await setupPi();
|
|
7809
|
+
else if (agent === "hermes") setupHermes();
|
|
7810
|
+
}
|
|
7811
|
+
async function setupDetectedAgents() {
|
|
7812
|
+
const detected = detectAgents();
|
|
7813
|
+
const found = Object.keys(detected).filter((k) => detected[k]);
|
|
7814
|
+
for (const agent of found) {
|
|
7815
|
+
await setupAgent(agent);
|
|
7816
|
+
}
|
|
7817
|
+
return found;
|
|
7818
|
+
}
|
|
7790
7819
|
function hasNode9McpServer(servers) {
|
|
7791
7820
|
const entry = servers["node9"];
|
|
7792
7821
|
return !!entry && entry.command === "node9" && Array.isArray(entry.args) && entry.args[0] === "mcp-server";
|
|
@@ -8107,7 +8136,7 @@ async function setupClaude() {
|
|
|
8107
8136
|
console.log(import_chalk.default.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
|
|
8108
8137
|
}
|
|
8109
8138
|
console.log("");
|
|
8110
|
-
const proceed = await
|
|
8139
|
+
const proceed = await confirm({ message: "Wrap these MCP servers?", default: true });
|
|
8111
8140
|
if (proceed) {
|
|
8112
8141
|
for (const { name, upstream } of serversToWrap) {
|
|
8113
8142
|
servers[name] = {
|
|
@@ -8212,7 +8241,7 @@ async function setupGemini() {
|
|
|
8212
8241
|
console.log(import_chalk.default.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
|
|
8213
8242
|
}
|
|
8214
8243
|
console.log("");
|
|
8215
|
-
const proceed = await
|
|
8244
|
+
const proceed = await confirm({ message: "Wrap these MCP servers?", default: true });
|
|
8216
8245
|
if (proceed) {
|
|
8217
8246
|
for (const { name, upstream } of serversToWrap) {
|
|
8218
8247
|
servers[name] = {
|
|
@@ -8338,7 +8367,7 @@ async function setupAntigravity() {
|
|
|
8338
8367
|
" \u26A0\uFE0F Found node9 hooks for the legacy Gemini CLI in ~/.gemini/settings.json.\n Gemini CLI stops serving AI Pro/Ultra and free tiers on 2026-06-18.\n Keep them only if you still use Gemini CLI (e.g. enterprise Code Assist)."
|
|
8339
8368
|
)
|
|
8340
8369
|
);
|
|
8341
|
-
const clean = await
|
|
8370
|
+
const clean = await confirm({
|
|
8342
8371
|
message: "Remove the legacy Gemini CLI hooks?",
|
|
8343
8372
|
default: false
|
|
8344
8373
|
});
|
|
@@ -8360,7 +8389,7 @@ async function setupAntigravity() {
|
|
|
8360
8389
|
console.log(import_chalk.default.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
|
|
8361
8390
|
}
|
|
8362
8391
|
console.log("");
|
|
8363
|
-
const proceed = await
|
|
8392
|
+
const proceed = await confirm({ message: "Wrap these MCP servers?", default: true });
|
|
8364
8393
|
if (proceed) {
|
|
8365
8394
|
for (const { name, upstream } of serversToWrap) {
|
|
8366
8395
|
servers[name] = {
|
|
@@ -8505,7 +8534,7 @@ async function setupCopilot() {
|
|
|
8505
8534
|
console.log(import_chalk.default.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
|
|
8506
8535
|
}
|
|
8507
8536
|
console.log("");
|
|
8508
|
-
const proceed = await
|
|
8537
|
+
const proceed = await confirm({ message: "Wrap these MCP servers?", default: true });
|
|
8509
8538
|
if (proceed) {
|
|
8510
8539
|
for (const { name, upstream } of serversToWrap) {
|
|
8511
8540
|
servers[name] = {
|
|
@@ -8706,7 +8735,7 @@ async function setupCursor() {
|
|
|
8706
8735
|
console.log(import_chalk.default.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
|
|
8707
8736
|
}
|
|
8708
8737
|
console.log("");
|
|
8709
|
-
const proceed = await
|
|
8738
|
+
const proceed = await confirm({ message: "Wrap these MCP servers?", default: true });
|
|
8710
8739
|
if (proceed) {
|
|
8711
8740
|
for (const { name, upstream } of serversToWrap) {
|
|
8712
8741
|
servers[name] = {
|
|
@@ -8857,7 +8886,7 @@ async function setupCodex() {
|
|
|
8857
8886
|
console.log(import_chalk.default.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
|
|
8858
8887
|
}
|
|
8859
8888
|
console.log("");
|
|
8860
|
-
const proceed = await
|
|
8889
|
+
const proceed = await confirm({ message: "Wrap these MCP servers?", default: true });
|
|
8861
8890
|
if (proceed) {
|
|
8862
8891
|
for (const { name, upstream } of serversToWrap) {
|
|
8863
8892
|
servers[name] = {
|
|
@@ -9034,7 +9063,7 @@ async function setupWindsurf() {
|
|
|
9034
9063
|
console.log(import_chalk.default.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
|
|
9035
9064
|
}
|
|
9036
9065
|
console.log("");
|
|
9037
|
-
const proceed = await
|
|
9066
|
+
const proceed = await confirm({ message: "Wrap these MCP servers?", default: true });
|
|
9038
9067
|
if (proceed) {
|
|
9039
9068
|
for (const { name, upstream } of serversToWrap) {
|
|
9040
9069
|
servers[name] = {
|
|
@@ -9138,7 +9167,7 @@ async function setupVSCode() {
|
|
|
9138
9167
|
console.log(import_chalk.default.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
|
|
9139
9168
|
}
|
|
9140
9169
|
console.log("");
|
|
9141
|
-
const proceed = await
|
|
9170
|
+
const proceed = await confirm({ message: "Wrap these MCP servers?", default: true });
|
|
9142
9171
|
if (proceed) {
|
|
9143
9172
|
for (const { name, upstream } of serversToWrap) {
|
|
9144
9173
|
servers[name] = {
|
|
@@ -9238,7 +9267,7 @@ async function setupClaudeDesktop() {
|
|
|
9238
9267
|
console.log(import_chalk.default.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
|
|
9239
9268
|
}
|
|
9240
9269
|
console.log("");
|
|
9241
|
-
const proceed = await
|
|
9270
|
+
const proceed = await confirm({ message: "Wrap these MCP servers?", default: true });
|
|
9242
9271
|
if (proceed) {
|
|
9243
9272
|
for (const { name, upstream } of serversToWrap) {
|
|
9244
9273
|
servers[name] = {
|
|
@@ -16880,7 +16909,7 @@ function readCredentials() {
|
|
|
16880
16909
|
if (process.env.NODE9_API_KEY) {
|
|
16881
16910
|
return {
|
|
16882
16911
|
apiKey: process.env.NODE9_API_KEY,
|
|
16883
|
-
apiUrl: process.env.NODE9_API_URL ??
|
|
16912
|
+
apiUrl: process.env.NODE9_API_URL ?? DEFAULT_API_URL2
|
|
16884
16913
|
};
|
|
16885
16914
|
}
|
|
16886
16915
|
try {
|
|
@@ -16899,11 +16928,11 @@ function readCredentials() {
|
|
|
16899
16928
|
// Anything else is taken as-is so users can override the full
|
|
16900
16929
|
// URL via NODE9_API_URL or a non-standard apiUrl.
|
|
16901
16930
|
/\/intercept$/.test(profile.apiUrl) ? profile.apiUrl + "/policies/sync" : profile.apiUrl
|
|
16902
|
-
) :
|
|
16931
|
+
) : DEFAULT_API_URL2
|
|
16903
16932
|
};
|
|
16904
16933
|
}
|
|
16905
16934
|
if (typeof creds.apiKey === "string" && creds.apiKey.length > 0) {
|
|
16906
|
-
return { apiKey: creds.apiKey, apiUrl:
|
|
16935
|
+
return { apiKey: creds.apiKey, apiUrl: DEFAULT_API_URL2 };
|
|
16907
16936
|
}
|
|
16908
16937
|
} catch {
|
|
16909
16938
|
}
|
|
@@ -17251,7 +17280,7 @@ function startForensicBroadcast() {
|
|
|
17251
17280
|
const recurring = setInterval(() => void tick(), FORENSIC_BROADCAST_INTERVAL_MS);
|
|
17252
17281
|
recurring.unref();
|
|
17253
17282
|
}
|
|
17254
|
-
var import_fs32, import_https4, import_os29, import_path31, FINDING_TO_SIGNAL3, rulesCacheFile,
|
|
17283
|
+
var import_fs32, import_https4, import_os29, import_path31, FINDING_TO_SIGNAL3, rulesCacheFile, DEFAULT_API_URL2, DEFAULT_INTERVAL_HOURS, MIN_INTERVAL_SECONDS, MAX_INTERVAL_SECONDS, FORENSIC_BROADCAST_INTERVAL_MS, FORENSIC_INITIAL_DELAY_MS, forensicBroadcastOffsets;
|
|
17255
17284
|
var init_sync = __esm({
|
|
17256
17285
|
"src/daemon/sync.ts"() {
|
|
17257
17286
|
"use strict";
|
|
@@ -17283,7 +17312,7 @@ var init_sync = __esm({
|
|
|
17283
17312
|
"long-output-redacted": "longOutputRedactions"
|
|
17284
17313
|
};
|
|
17285
17314
|
rulesCacheFile = () => import_path31.default.join(import_os29.default.homedir(), ".node9", "rules-cache.json");
|
|
17286
|
-
|
|
17315
|
+
DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept/policies/sync";
|
|
17287
17316
|
DEFAULT_INTERVAL_HOURS = 5;
|
|
17288
17317
|
MIN_INTERVAL_SECONDS = 15;
|
|
17289
17318
|
MAX_INTERVAL_SECONDS = 24 * 60 * 60;
|
|
@@ -19085,10 +19114,10 @@ function readSessionUsage() {
|
|
|
19085
19114
|
}
|
|
19086
19115
|
}
|
|
19087
19116
|
function formatContextStat(stat) {
|
|
19088
|
-
const pctColor = stat.fillPct >= 80 ?
|
|
19117
|
+
const pctColor = stat.fillPct >= 80 ? import_chalk36.default.red : stat.fillPct >= 50 ? import_chalk36.default.yellow : import_chalk36.default.cyan;
|
|
19089
19118
|
const k = (n) => `${Math.round(n / 1e3)}k`;
|
|
19090
19119
|
const modelShort = stat.model.replace(/@.*$/, "").replace(/-\d{8}$/, "").replace(/^claude-/, "");
|
|
19091
|
-
return
|
|
19120
|
+
return import_chalk36.default.dim("ctx: ") + pctColor(`${stat.fillPct}%`) + import_chalk36.default.dim(
|
|
19092
19121
|
` (${k(stat.inputTokens)}/${k(getModelContextLimit(stat.model))} out ${k(stat.outputTokens)} \xB7 ${modelShort})`
|
|
19093
19122
|
);
|
|
19094
19123
|
}
|
|
@@ -19111,11 +19140,11 @@ function agentLabel(agent, mcpServer, sessionId) {
|
|
|
19111
19140
|
const tag = sessionTag(sessionId);
|
|
19112
19141
|
const tagSuffix = tag ? `\xB7${tag}` : "";
|
|
19113
19142
|
if (!agent || agent === "Terminal") {
|
|
19114
|
-
return mcpServer ?
|
|
19143
|
+
return mcpServer ? import_chalk36.default.dim(`[\u2192 ${mcpServer}] `) : "";
|
|
19115
19144
|
}
|
|
19116
19145
|
const short = agent === "Claude Code" ? "Claude" : agent === "Gemini CLI" ? "Gemini" : agent === "Unknown Agent" ? "" : agent.split(" ")[0];
|
|
19117
|
-
if (!short) return mcpServer ?
|
|
19118
|
-
return mcpServer ?
|
|
19146
|
+
if (!short) return mcpServer ? import_chalk36.default.dim(`[\u2192 ${mcpServer}] `) : "";
|
|
19147
|
+
return mcpServer ? import_chalk36.default.dim(`[${short}${tagSuffix} \u2192 ${mcpServer}] `) : import_chalk36.default.dim(`[${short}${tagSuffix}] `);
|
|
19119
19148
|
}
|
|
19120
19149
|
function formatBase(activity) {
|
|
19121
19150
|
const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
|
|
@@ -19123,20 +19152,20 @@ function formatBase(activity) {
|
|
|
19123
19152
|
const toolName = activity.tool.slice(0, 16).padEnd(16);
|
|
19124
19153
|
const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(import_os55.default.homedir(), "~");
|
|
19125
19154
|
const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
|
|
19126
|
-
return `${
|
|
19155
|
+
return `${import_chalk36.default.gray(time)} ${icon} ${agentLabel(activity.agent, activity.mcpServer, activity.sessionId)}${import_chalk36.default.white.bold(toolName)} ${import_chalk36.default.dim(argsPreview)}`;
|
|
19127
19156
|
}
|
|
19128
19157
|
function renderResult(activity, result) {
|
|
19129
19158
|
const base = formatBase(activity);
|
|
19130
19159
|
let status;
|
|
19131
19160
|
if (result.status === "allow") {
|
|
19132
|
-
status =
|
|
19161
|
+
status = import_chalk36.default.green("\u2713 ALLOW");
|
|
19133
19162
|
} else if (result.status === "dlp") {
|
|
19134
|
-
status =
|
|
19163
|
+
status = import_chalk36.default.bgRed.white.bold(" \u{1F6E1}\uFE0F DLP ");
|
|
19135
19164
|
} else {
|
|
19136
|
-
status =
|
|
19165
|
+
status = import_chalk36.default.red("\u2717 BLOCK");
|
|
19137
19166
|
}
|
|
19138
19167
|
const cost = result.costEstimate ?? activity.costEstimate;
|
|
19139
|
-
const costSuffix = cost == null ? "" :
|
|
19168
|
+
const costSuffix = cost == null ? "" : import_chalk36.default.dim(` ~$${cost >= 1e-3 ? cost.toFixed(3) : "0.000"}`);
|
|
19140
19169
|
if (process.stdout.isTTY) {
|
|
19141
19170
|
if (pendingShownForId === activity.id && pendingWrappedLines > 1) {
|
|
19142
19171
|
import_readline6.default.moveCursor(process.stdout, 0, -(pendingWrappedLines - 1));
|
|
@@ -19153,7 +19182,7 @@ function renderResult(activity, result) {
|
|
|
19153
19182
|
}
|
|
19154
19183
|
function renderPending(activity) {
|
|
19155
19184
|
if (!process.stdout.isTTY) return;
|
|
19156
|
-
const line = `${formatBase(activity)} ${
|
|
19185
|
+
const line = `${formatBase(activity)} ${import_chalk36.default.yellow("\u25CF \u2026")}`;
|
|
19157
19186
|
pendingShownForId = activity.id;
|
|
19158
19187
|
pendingWrappedLines = wrappedLineCount(line);
|
|
19159
19188
|
process.stdout.write(`${line}\r`);
|
|
@@ -19165,7 +19194,7 @@ async function ensureDaemon() {
|
|
|
19165
19194
|
const { port } = JSON.parse(import_fs64.default.readFileSync(PID_FILE, "utf-8"));
|
|
19166
19195
|
pidPort = port;
|
|
19167
19196
|
} catch {
|
|
19168
|
-
console.error(
|
|
19197
|
+
console.error(import_chalk36.default.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
|
|
19169
19198
|
}
|
|
19170
19199
|
}
|
|
19171
19200
|
const checkPort = pidPort ?? DAEMON_PORT;
|
|
@@ -19176,7 +19205,7 @@ async function ensureDaemon() {
|
|
|
19176
19205
|
if (res.ok) return checkPort;
|
|
19177
19206
|
} catch {
|
|
19178
19207
|
}
|
|
19179
|
-
console.log(
|
|
19208
|
+
console.log(import_chalk36.default.dim("\u{1F6E1}\uFE0F Starting Node9 daemon..."));
|
|
19180
19209
|
const child = (0, import_child_process14.spawn)(process.execPath, [process.argv[1], "daemon"], {
|
|
19181
19210
|
detached: true,
|
|
19182
19211
|
stdio: "ignore",
|
|
@@ -19193,7 +19222,7 @@ async function ensureDaemon() {
|
|
|
19193
19222
|
} catch {
|
|
19194
19223
|
}
|
|
19195
19224
|
}
|
|
19196
|
-
console.error(
|
|
19225
|
+
console.error(import_chalk36.default.red("\u274C Daemon failed to start. Try: node9 daemon start"));
|
|
19197
19226
|
process.exit(1);
|
|
19198
19227
|
}
|
|
19199
19228
|
function postDecisionHttp(id, decision, authToken, port, opts) {
|
|
@@ -19203,7 +19232,7 @@ function postDecisionHttp(id, decision, authToken, port, opts) {
|
|
|
19203
19232
|
if (opts?.trustDuration) bodyObj.trustDuration = opts.trustDuration;
|
|
19204
19233
|
if (opts?.reason) bodyObj.reason = opts.reason;
|
|
19205
19234
|
const body = JSON.stringify(bodyObj);
|
|
19206
|
-
const req =
|
|
19235
|
+
const req = import_http5.default.request(
|
|
19207
19236
|
{
|
|
19208
19237
|
hostname: "127.0.0.1",
|
|
19209
19238
|
port,
|
|
@@ -19262,7 +19291,7 @@ function buildCardLines(req, localCount = 0) {
|
|
|
19262
19291
|
const severityIcon = isBlock ? `${RED}\u{1F6D1}` : `${YELLOW}\u26A0 `;
|
|
19263
19292
|
const rawDesc = req.riskMetadata?.ruleDescription ?? "";
|
|
19264
19293
|
const description = rawDesc ? cleanReason(rawDesc) : "";
|
|
19265
|
-
const agentSuffix = req.agent && req.agent !== "Terminal" ? ` ${RESET2}${
|
|
19294
|
+
const agentSuffix = req.agent && req.agent !== "Terminal" ? ` ${RESET2}${import_chalk36.default.dim(`(${req.agent})`)}` : "";
|
|
19266
19295
|
const lines = [
|
|
19267
19296
|
``,
|
|
19268
19297
|
`${BOLD2}${CYAN}\u2554\u2550\u2550 Node9 Approval Required \u2550\u2550\u2557${RESET2}`,
|
|
@@ -19331,7 +19360,7 @@ function approverStatusLine() {
|
|
|
19331
19360
|
const a = readApproversFromDisk();
|
|
19332
19361
|
const fmt = (label2, key) => {
|
|
19333
19362
|
const on = a[key] !== false;
|
|
19334
|
-
return `[${key[0]}]${label2.slice(1)} ${on ?
|
|
19363
|
+
return `[${key[0]}]${label2.slice(1)} ${on ? import_chalk36.default.green("\u2713") : import_chalk36.default.dim("\u2717")}`;
|
|
19335
19364
|
};
|
|
19336
19365
|
return `${fmt("native", "native")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
|
|
19337
19366
|
}
|
|
@@ -19354,7 +19383,7 @@ async function startTail(options = {}) {
|
|
|
19354
19383
|
const port = await ensureDaemon();
|
|
19355
19384
|
if (options.clear) {
|
|
19356
19385
|
const result = await new Promise((resolve) => {
|
|
19357
|
-
const req2 =
|
|
19386
|
+
const req2 = import_http5.default.request(
|
|
19358
19387
|
{ method: "POST", hostname: "127.0.0.1", port, path: "/events/clear" },
|
|
19359
19388
|
(res) => {
|
|
19360
19389
|
const status = res.statusCode ?? 0;
|
|
@@ -19376,7 +19405,7 @@ async function startTail(options = {}) {
|
|
|
19376
19405
|
req2.end();
|
|
19377
19406
|
});
|
|
19378
19407
|
if (result.ok) {
|
|
19379
|
-
console.log(
|
|
19408
|
+
console.log(import_chalk36.default.green("\u2713 Flight Recorder buffer cleared."));
|
|
19380
19409
|
} else if (result.code === "ECONNREFUSED") {
|
|
19381
19410
|
throw new Error("Daemon is not running. Start it with: node9 daemon start");
|
|
19382
19411
|
} else if (result.code === "ETIMEDOUT") {
|
|
@@ -19422,7 +19451,7 @@ async function startTail(options = {}) {
|
|
|
19422
19451
|
const channel = name === "n" ? "native" : name === "c" ? "cloud" : name === "t" ? "terminal" : null;
|
|
19423
19452
|
if (channel) {
|
|
19424
19453
|
toggleApprover(channel);
|
|
19425
|
-
console.log(
|
|
19454
|
+
console.log(import_chalk36.default.dim(` Approvers: ${approverStatusLine()}`));
|
|
19426
19455
|
}
|
|
19427
19456
|
};
|
|
19428
19457
|
process.stdin.on("keypress", idleKeypressHandler);
|
|
@@ -19488,7 +19517,7 @@ async function startTail(options = {}) {
|
|
|
19488
19517
|
localAllowCounts.get(req2.toolName) ?? 0
|
|
19489
19518
|
)
|
|
19490
19519
|
);
|
|
19491
|
-
const decisionStamp = action === "always-allow" ?
|
|
19520
|
+
const decisionStamp = action === "always-allow" ? import_chalk36.default.yellow("\u2605 ALWAYS ALLOW") : action === "trust" ? import_chalk36.default.cyan("\u23F1 TRUST 30m") : action === "allow" ? import_chalk36.default.green("\u2713 ALLOWED") : action === "redirect" ? import_chalk36.default.yellow("\u21A9 REDIRECT AI") : import_chalk36.default.red("\u2717 DENIED");
|
|
19492
19521
|
stampedLines.push(` ${BOLD2}\u2192${RESET2} ${decisionStamp} ${GRAY}(terminal)${RESET2}`, ``);
|
|
19493
19522
|
for (const line of stampedLines) process.stdout.write(line + "\n");
|
|
19494
19523
|
process.stdout.write(SHOW_CURSOR);
|
|
@@ -19539,7 +19568,7 @@ async function startTail(options = {}) {
|
|
|
19539
19568
|
);
|
|
19540
19569
|
const stampedLines = buildCardLines(req2, priorCount);
|
|
19541
19570
|
if (externalDecision) {
|
|
19542
|
-
const source = externalDecision === "allow" ?
|
|
19571
|
+
const source = externalDecision === "allow" ? import_chalk36.default.green("\u2713 ALLOWED") : import_chalk36.default.red("\u2717 DENIED");
|
|
19543
19572
|
stampedLines.push(` ${BOLD2}\u2192${RESET2} ${source} ${GRAY}(external)${RESET2}`, ``);
|
|
19544
19573
|
}
|
|
19545
19574
|
for (const line of stampedLines) process.stdout.write(line + "\n");
|
|
@@ -19587,25 +19616,25 @@ async function startTail(options = {}) {
|
|
|
19587
19616
|
if (unackedDlp > 0) {
|
|
19588
19617
|
console.log("");
|
|
19589
19618
|
console.log(
|
|
19590
|
-
|
|
19619
|
+
import_chalk36.default.bgRed.white.bold(
|
|
19591
19620
|
` \u26A0\uFE0F DLP ALERT: ${unackedDlp} secret${unackedDlp !== 1 ? "s" : ""} found in Claude response text \u2014 run: node9 dlp `
|
|
19592
19621
|
)
|
|
19593
19622
|
);
|
|
19594
19623
|
}
|
|
19595
19624
|
} catch {
|
|
19596
19625
|
}
|
|
19597
|
-
console.log(
|
|
19626
|
+
console.log(import_chalk36.default.cyan.bold(`
|
|
19598
19627
|
\u{1F6F0}\uFE0F Node9 tail`));
|
|
19599
19628
|
if (canApprove) {
|
|
19600
|
-
console.log(
|
|
19601
|
-
console.log(
|
|
19629
|
+
console.log(import_chalk36.default.dim("Card: [\u21B5/y] Allow [n] Deny [a] Always [t] Trust 30m"));
|
|
19630
|
+
console.log(import_chalk36.default.dim(`Approvers (toggle): ${approverStatusLine()} [q] quit`));
|
|
19602
19631
|
}
|
|
19603
19632
|
const ctxStat = readSessionUsage();
|
|
19604
19633
|
if (ctxStat) console.log(" " + formatContextStat(ctxStat));
|
|
19605
19634
|
if (options.history) {
|
|
19606
|
-
console.log(
|
|
19635
|
+
console.log(import_chalk36.default.dim("Showing history + live events.\n"));
|
|
19607
19636
|
} else {
|
|
19608
|
-
console.log(
|
|
19637
|
+
console.log(import_chalk36.default.dim("Showing live events only. Use --history to include past.\n"));
|
|
19609
19638
|
}
|
|
19610
19639
|
process.on("SIGINT", () => {
|
|
19611
19640
|
exitIdleMode();
|
|
@@ -19615,7 +19644,7 @@ async function startTail(options = {}) {
|
|
|
19615
19644
|
import_readline6.default.clearLine(process.stdout, 0);
|
|
19616
19645
|
import_readline6.default.cursorTo(process.stdout, 0);
|
|
19617
19646
|
}
|
|
19618
|
-
console.log(
|
|
19647
|
+
console.log(import_chalk36.default.dim("\n\u{1F6F0}\uFE0F Disconnected."));
|
|
19619
19648
|
process.exit(0);
|
|
19620
19649
|
});
|
|
19621
19650
|
const STALL_THRESHOLD_MS = 6e4;
|
|
@@ -19627,7 +19656,7 @@ async function startTail(options = {}) {
|
|
|
19627
19656
|
if (Date.now() - auditMtime >= STALL_THRESHOLD_MS) return;
|
|
19628
19657
|
console.log("");
|
|
19629
19658
|
console.log(
|
|
19630
|
-
|
|
19659
|
+
import_chalk36.default.yellow(
|
|
19631
19660
|
"\u26A0\uFE0F Tail appears stalled \u2014 hooks are firing but no events are arriving. Try: node9 daemon restart"
|
|
19632
19661
|
)
|
|
19633
19662
|
);
|
|
@@ -19637,14 +19666,14 @@ async function startTail(options = {}) {
|
|
|
19637
19666
|
}, STALL_THRESHOLD_MS / 2);
|
|
19638
19667
|
stallWatchdog.unref();
|
|
19639
19668
|
const sseUrl = `http://127.0.0.1:${port}/events?capabilities=input`;
|
|
19640
|
-
const req =
|
|
19669
|
+
const req = import_http5.default.get(
|
|
19641
19670
|
sseUrl,
|
|
19642
19671
|
{
|
|
19643
19672
|
headers: authToken ? { "X-Node9-Internal": authToken } : {}
|
|
19644
19673
|
},
|
|
19645
19674
|
(res) => {
|
|
19646
19675
|
if (res.statusCode !== 200) {
|
|
19647
|
-
console.error(
|
|
19676
|
+
console.error(import_chalk36.default.red(`Failed to connect: HTTP ${res.statusCode}`));
|
|
19648
19677
|
process.exit(1);
|
|
19649
19678
|
}
|
|
19650
19679
|
if (canApprove) enterIdleMode();
|
|
@@ -19675,7 +19704,7 @@ async function startTail(options = {}) {
|
|
|
19675
19704
|
import_readline6.default.clearLine(process.stdout, 0);
|
|
19676
19705
|
import_readline6.default.cursorTo(process.stdout, 0);
|
|
19677
19706
|
}
|
|
19678
|
-
console.log(
|
|
19707
|
+
console.log(import_chalk36.default.red("\n\u274C Daemon disconnected."));
|
|
19679
19708
|
process.exit(1);
|
|
19680
19709
|
});
|
|
19681
19710
|
}
|
|
@@ -19688,7 +19717,7 @@ async function startTail(options = {}) {
|
|
|
19688
19717
|
const parsed = JSON.parse(rawData);
|
|
19689
19718
|
const msg = parsed.message ?? "Flight recorder is down \u2014 run: node9 daemon restart";
|
|
19690
19719
|
console.log("");
|
|
19691
|
-
console.log(
|
|
19720
|
+
console.log(import_chalk36.default.bgRed.white.bold(` \u26A0\uFE0F ${msg} `));
|
|
19692
19721
|
} catch {
|
|
19693
19722
|
}
|
|
19694
19723
|
return;
|
|
@@ -19773,9 +19802,9 @@ async function startTail(options = {}) {
|
|
|
19773
19802
|
const rawSummary = data.argsSummary ?? data.tool;
|
|
19774
19803
|
const summary = shortenPathSummary(rawSummary);
|
|
19775
19804
|
const fileCount = data.fileCount ?? 0;
|
|
19776
|
-
const files = fileCount > 0 ?
|
|
19805
|
+
const files = fileCount > 0 ? import_chalk36.default.dim(` \xB7 ${fileCount} file${fileCount === 1 ? "" : "s"}`) : "";
|
|
19777
19806
|
process.stdout.write(
|
|
19778
|
-
`${
|
|
19807
|
+
`${import_chalk36.default.dim(time)} ${import_chalk36.default.cyan("\u{1F4F8} snapshot")} ${import_chalk36.default.dim(hash)} ${summary}${files}
|
|
19779
19808
|
`
|
|
19780
19809
|
);
|
|
19781
19810
|
return;
|
|
@@ -19792,28 +19821,28 @@ async function startTail(options = {}) {
|
|
|
19792
19821
|
if (event === "execution-result") {
|
|
19793
19822
|
const exec = data;
|
|
19794
19823
|
const time = new Date(Date.now()).toLocaleTimeString([], { hour12: false });
|
|
19795
|
-
const arrow = exec.isError ?
|
|
19824
|
+
const arrow = exec.isError ? import_chalk36.default.red(" \u21B3 \u2717") : import_chalk36.default.green(" \u21B3 \u2713");
|
|
19796
19825
|
const label2 = agentLabel(exec.agent, exec.mcpServer);
|
|
19797
19826
|
const tool = (exec.tool ?? "").slice(0, 16);
|
|
19798
|
-
const duration = typeof exec.durationMs === "number" ?
|
|
19827
|
+
const duration = typeof exec.durationMs === "number" ? import_chalk36.default.dim(` (${exec.durationMs}ms)`) : "";
|
|
19799
19828
|
console.log(
|
|
19800
|
-
`${
|
|
19829
|
+
`${import_chalk36.default.gray(time)} ${arrow} ${label2}${import_chalk36.default.dim(tool)}${import_chalk36.default.dim(" completed")}${duration}`
|
|
19801
19830
|
);
|
|
19802
19831
|
}
|
|
19803
19832
|
}
|
|
19804
19833
|
req.on("error", (err2) => {
|
|
19805
19834
|
const msg = err2.code === "ECONNREFUSED" ? "Daemon is not running. Start it with: node9 daemon start" : err2.message;
|
|
19806
|
-
console.error(
|
|
19835
|
+
console.error(import_chalk36.default.red(`
|
|
19807
19836
|
\u274C ${msg}`));
|
|
19808
19837
|
process.exit(1);
|
|
19809
19838
|
});
|
|
19810
19839
|
}
|
|
19811
|
-
var
|
|
19840
|
+
var import_http5, import_chalk36, import_fs64, import_os55, import_path62, import_readline6, import_child_process14, PID_FILE, ICONS, MODEL_CONTEXT_LIMITS, RESET2, BOLD2, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, pendingShownForId, pendingWrappedLines, DIVIDER;
|
|
19812
19841
|
var init_tail = __esm({
|
|
19813
19842
|
"src/tui/tail.ts"() {
|
|
19814
19843
|
"use strict";
|
|
19815
|
-
|
|
19816
|
-
|
|
19844
|
+
import_http5 = __toESM(require("http"));
|
|
19845
|
+
import_chalk36 = __toESM(require("chalk"));
|
|
19817
19846
|
import_fs64 = __toESM(require("fs"));
|
|
19818
19847
|
import_os55 = __toESM(require("os"));
|
|
19819
19848
|
import_path62 = __toESM(require("path"));
|
|
@@ -19886,7 +19915,7 @@ function queryDaemon() {
|
|
|
19886
19915
|
return new Promise((resolve) => {
|
|
19887
19916
|
const timeout = setTimeout(() => resolve(null), 50);
|
|
19888
19917
|
try {
|
|
19889
|
-
const req =
|
|
19918
|
+
const req = import_http6.default.get(
|
|
19890
19919
|
`http://${DAEMON_HOST}:${DAEMON_PORT}/status`,
|
|
19891
19920
|
{ timeout: 50 },
|
|
19892
19921
|
(res) => {
|
|
@@ -20214,14 +20243,14 @@ async function main() {
|
|
|
20214
20243
|
renderOffline();
|
|
20215
20244
|
}
|
|
20216
20245
|
}
|
|
20217
|
-
var import_fs65, import_path63, import_os56,
|
|
20246
|
+
var import_fs65, import_path63, import_os56, import_http6, RESET3, BOLD3, DIM, RED2, GREEN2, YELLOW2, BLUE, MAGENTA, CYAN2, WHITE, BAR_FILLED, BAR_EMPTY, BAR_WIDTH, shieldsCache, SHIELDS_CACHE_TTL_MS;
|
|
20218
20247
|
var init_hud = __esm({
|
|
20219
20248
|
"src/cli/hud.ts"() {
|
|
20220
20249
|
"use strict";
|
|
20221
20250
|
import_fs65 = __toESM(require("fs"));
|
|
20222
20251
|
import_path63 = __toESM(require("path"));
|
|
20223
20252
|
import_os56 = __toESM(require("os"));
|
|
20224
|
-
|
|
20253
|
+
import_http6 = __toESM(require("http"));
|
|
20225
20254
|
init_daemon();
|
|
20226
20255
|
RESET3 = "\x1B[0m";
|
|
20227
20256
|
BOLD3 = "\x1B[1m";
|
|
@@ -20278,8 +20307,69 @@ function agentTeardownTargets() {
|
|
|
20278
20307
|
|
|
20279
20308
|
// src/cli.ts
|
|
20280
20309
|
init_agent_wiring();
|
|
20310
|
+
|
|
20311
|
+
// src/credentials.ts
|
|
20312
|
+
var fs15 = __toESM(require("fs"));
|
|
20313
|
+
var os14 = __toESM(require("os"));
|
|
20314
|
+
var path17 = __toESM(require("path"));
|
|
20315
|
+
var DEFAULT_API_URL = "https://api.node9.ai/api/v1/intercept";
|
|
20316
|
+
function writeCredentialsAndConfig(apiKey, opts = {}) {
|
|
20317
|
+
const profileName = opts.profileName || "default";
|
|
20318
|
+
const home = opts.homeDir ?? os14.homedir();
|
|
20319
|
+
const credPath = path17.join(home, ".node9", "credentials.json");
|
|
20320
|
+
if (!fs15.existsSync(path17.dirname(credPath))) {
|
|
20321
|
+
fs15.mkdirSync(path17.dirname(credPath), { recursive: true });
|
|
20322
|
+
}
|
|
20323
|
+
let existingCreds = {};
|
|
20324
|
+
try {
|
|
20325
|
+
if (fs15.existsSync(credPath)) {
|
|
20326
|
+
const raw = JSON.parse(fs15.readFileSync(credPath, "utf-8"));
|
|
20327
|
+
existingCreds = raw.apiKey ? { default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL } } : raw;
|
|
20328
|
+
}
|
|
20329
|
+
} catch {
|
|
20330
|
+
}
|
|
20331
|
+
existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL };
|
|
20332
|
+
fs15.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), {
|
|
20333
|
+
mode: 384
|
|
20334
|
+
});
|
|
20335
|
+
let effectiveCloud = null;
|
|
20336
|
+
if (profileName === "default") {
|
|
20337
|
+
const configPath = path17.join(home, ".node9", "config.json");
|
|
20338
|
+
let config = {};
|
|
20339
|
+
try {
|
|
20340
|
+
if (fs15.existsSync(configPath)) {
|
|
20341
|
+
config = JSON.parse(fs15.readFileSync(configPath, "utf-8"));
|
|
20342
|
+
}
|
|
20343
|
+
} catch {
|
|
20344
|
+
}
|
|
20345
|
+
if (!config.settings || typeof config.settings !== "object") {
|
|
20346
|
+
config.settings = {};
|
|
20347
|
+
}
|
|
20348
|
+
const s = config.settings;
|
|
20349
|
+
const approvers = s.approvers || {
|
|
20350
|
+
native: true,
|
|
20351
|
+
browser: true,
|
|
20352
|
+
cloud: true,
|
|
20353
|
+
terminal: true
|
|
20354
|
+
};
|
|
20355
|
+
if (opts.isLocal) {
|
|
20356
|
+
approvers.cloud = false;
|
|
20357
|
+
}
|
|
20358
|
+
s.approvers = approvers;
|
|
20359
|
+
if (!fs15.existsSync(path17.dirname(configPath))) {
|
|
20360
|
+
fs15.mkdirSync(path17.dirname(configPath), { recursive: true });
|
|
20361
|
+
}
|
|
20362
|
+
fs15.writeFileSync(configPath, JSON.stringify(config, null, 2), {
|
|
20363
|
+
mode: 384
|
|
20364
|
+
});
|
|
20365
|
+
effectiveCloud = approvers.cloud === true;
|
|
20366
|
+
}
|
|
20367
|
+
return { profileName, effectiveCloud };
|
|
20368
|
+
}
|
|
20369
|
+
|
|
20370
|
+
// src/cli.ts
|
|
20281
20371
|
init_daemon2();
|
|
20282
|
-
var
|
|
20372
|
+
var import_chalk37 = __toESM(require("chalk"));
|
|
20283
20373
|
var import_fs66 = __toESM(require("fs"));
|
|
20284
20374
|
var import_path64 = __toESM(require("path"));
|
|
20285
20375
|
var import_os57 = __toESM(require("os"));
|
|
@@ -24154,18 +24244,7 @@ function registerInitCommand(program2) {
|
|
|
24154
24244
|
console.log("");
|
|
24155
24245
|
for (const agent of found) {
|
|
24156
24246
|
console.log(import_chalk16.default.bold(`Wiring ${agent}...`));
|
|
24157
|
-
|
|
24158
|
-
else if (agent === "gemini") await setupGemini();
|
|
24159
|
-
else if (agent === "antigravity") await setupAntigravity();
|
|
24160
|
-
else if (agent === "copilot") await setupCopilot();
|
|
24161
|
-
else if (agent === "cursor") await setupCursor();
|
|
24162
|
-
else if (agent === "codex") await setupCodex();
|
|
24163
|
-
else if (agent === "windsurf") await setupWindsurf();
|
|
24164
|
-
else if (agent === "vscode") await setupVSCode();
|
|
24165
|
-
else if (agent === "claudeDesktop") await setupClaudeDesktop();
|
|
24166
|
-
else if (agent === "opencode") await setupOpencode();
|
|
24167
|
-
else if (agent === "pi") await setupPi();
|
|
24168
|
-
else if (agent === "hermes") setupHermes();
|
|
24247
|
+
await setupAgent(agent);
|
|
24169
24248
|
console.log("");
|
|
24170
24249
|
}
|
|
24171
24250
|
if ((process.platform === "darwin" || process.platform === "linux") && process.stdout.isTTY) {
|
|
@@ -24228,13 +24307,110 @@ function registerInitCommand(program2) {
|
|
|
24228
24307
|
);
|
|
24229
24308
|
}
|
|
24230
24309
|
|
|
24310
|
+
// src/cli/commands/connect.ts
|
|
24311
|
+
var import_http4 = __toESM(require("http"));
|
|
24312
|
+
var import_https7 = __toESM(require("https"));
|
|
24313
|
+
var import_url3 = require("url");
|
|
24314
|
+
var import_chalk17 = __toESM(require("chalk"));
|
|
24315
|
+
init_setup();
|
|
24316
|
+
init_sync();
|
|
24317
|
+
var DEFAULT_CONNECT_URL = "https://api.node9.ai/api/v1/cli/connect";
|
|
24318
|
+
function resolveConnectUrl(apiUrl) {
|
|
24319
|
+
if (apiUrl) return apiUrl;
|
|
24320
|
+
const base = process.env.NODE9_API_URL;
|
|
24321
|
+
if (base) return base.replace(/\/intercept\/?$/, "") + "/cli/connect";
|
|
24322
|
+
return DEFAULT_CONNECT_URL;
|
|
24323
|
+
}
|
|
24324
|
+
function postConnect(url, token) {
|
|
24325
|
+
return new Promise((resolve, reject) => {
|
|
24326
|
+
const body = JSON.stringify({ token });
|
|
24327
|
+
const u = new import_url3.URL(url);
|
|
24328
|
+
const lib = u.protocol === "http:" ? import_http4.default : import_https7.default;
|
|
24329
|
+
const req = lib.request(
|
|
24330
|
+
{
|
|
24331
|
+
method: "POST",
|
|
24332
|
+
hostname: u.hostname,
|
|
24333
|
+
port: u.port || (u.protocol === "http:" ? 80 : 443),
|
|
24334
|
+
path: u.pathname + u.search,
|
|
24335
|
+
headers: {
|
|
24336
|
+
"Content-Type": "application/json",
|
|
24337
|
+
"Content-Length": Buffer.byteLength(body)
|
|
24338
|
+
},
|
|
24339
|
+
timeout: 15e3
|
|
24340
|
+
},
|
|
24341
|
+
(res) => {
|
|
24342
|
+
let data = "";
|
|
24343
|
+
res.on("data", (c) => data += c);
|
|
24344
|
+
res.on("end", () => {
|
|
24345
|
+
const code = res.statusCode ?? 0;
|
|
24346
|
+
if (code >= 200 && code < 300) {
|
|
24347
|
+
try {
|
|
24348
|
+
resolve(JSON.parse(data));
|
|
24349
|
+
} catch {
|
|
24350
|
+
reject(new Error("Unexpected response from the server."));
|
|
24351
|
+
}
|
|
24352
|
+
} else if (code === 400 || code === 401) {
|
|
24353
|
+
reject(
|
|
24354
|
+
new Error(
|
|
24355
|
+
"This connect link expired or was already used \u2014 generate a new one in the dashboard."
|
|
24356
|
+
)
|
|
24357
|
+
);
|
|
24358
|
+
} else {
|
|
24359
|
+
reject(new Error(`Connect failed (HTTP ${code}).`));
|
|
24360
|
+
}
|
|
24361
|
+
});
|
|
24362
|
+
}
|
|
24363
|
+
);
|
|
24364
|
+
req.on("error", (e) => reject(e));
|
|
24365
|
+
req.on("timeout", () => {
|
|
24366
|
+
req.destroy();
|
|
24367
|
+
reject(new Error("Connection timed out."));
|
|
24368
|
+
});
|
|
24369
|
+
req.write(body);
|
|
24370
|
+
req.end();
|
|
24371
|
+
});
|
|
24372
|
+
}
|
|
24373
|
+
function registerConnectCommand(program2) {
|
|
24374
|
+
program2.command("connect").argument("<token>", "A connect token from the dashboard").option("--profile <name>", 'Save as a named profile (default: "default")').option("--api-url <url>", "Override the connect endpoint (self-host / dev)").description("Connect this machine to a node9 workspace using a dashboard token").action(async (token, options) => {
|
|
24375
|
+
let resp;
|
|
24376
|
+
try {
|
|
24377
|
+
resp = await postConnect(resolveConnectUrl(options.apiUrl), token);
|
|
24378
|
+
} catch (e) {
|
|
24379
|
+
console.error(import_chalk17.default.red(`\u2717 ${e instanceof Error ? e.message : "Connect failed."}`));
|
|
24380
|
+
process.exitCode = 1;
|
|
24381
|
+
return;
|
|
24382
|
+
}
|
|
24383
|
+
writeCredentialsAndConfig(resp.apiKey, { profileName: options.profile });
|
|
24384
|
+
process.env.NODE9_NONINTERACTIVE = "1";
|
|
24385
|
+
let wired = [];
|
|
24386
|
+
try {
|
|
24387
|
+
wired = await setupDetectedAgents();
|
|
24388
|
+
} catch {
|
|
24389
|
+
}
|
|
24390
|
+
if (!isTestingMode()) {
|
|
24391
|
+
try {
|
|
24392
|
+
await runCloudSync();
|
|
24393
|
+
} catch {
|
|
24394
|
+
}
|
|
24395
|
+
}
|
|
24396
|
+
console.log(import_chalk17.default.green(`\u2705 Connected to ${resp.workspaceName}`));
|
|
24397
|
+
if (wired.length) {
|
|
24398
|
+
console.log(import_chalk17.default.gray(` Wired: ${wired.join(", ")}`));
|
|
24399
|
+
} else {
|
|
24400
|
+
console.log(
|
|
24401
|
+
import_chalk17.default.gray(" No agents detected yet \u2014 run `node9 init` after installing one.")
|
|
24402
|
+
);
|
|
24403
|
+
}
|
|
24404
|
+
});
|
|
24405
|
+
}
|
|
24406
|
+
|
|
24231
24407
|
// src/cli/commands/undo.ts
|
|
24232
24408
|
var import_path50 = __toESM(require("path"));
|
|
24233
|
-
var
|
|
24409
|
+
var import_chalk19 = __toESM(require("chalk"));
|
|
24234
24410
|
|
|
24235
24411
|
// src/tui/undo-navigator.ts
|
|
24236
24412
|
var import_readline3 = __toESM(require("readline"));
|
|
24237
|
-
var
|
|
24413
|
+
var import_chalk18 = __toESM(require("chalk"));
|
|
24238
24414
|
var RESET = "\x1B[0m";
|
|
24239
24415
|
var BOLD = "\x1B[1m";
|
|
24240
24416
|
var CLEAR_SCREEN = "\x1B[2J\x1B[H";
|
|
@@ -24252,15 +24428,15 @@ function renderDiff(raw) {
|
|
|
24252
24428
|
);
|
|
24253
24429
|
for (const line of lines) {
|
|
24254
24430
|
if (line.startsWith("+++") || line.startsWith("---")) {
|
|
24255
|
-
process.stdout.write(
|
|
24431
|
+
process.stdout.write(import_chalk18.default.bold(line) + "\n");
|
|
24256
24432
|
} else if (line.startsWith("+")) {
|
|
24257
|
-
process.stdout.write(
|
|
24433
|
+
process.stdout.write(import_chalk18.default.green(line) + "\n");
|
|
24258
24434
|
} else if (line.startsWith("-")) {
|
|
24259
|
-
process.stdout.write(
|
|
24435
|
+
process.stdout.write(import_chalk18.default.red(line) + "\n");
|
|
24260
24436
|
} else if (line.startsWith("@@")) {
|
|
24261
|
-
process.stdout.write(
|
|
24437
|
+
process.stdout.write(import_chalk18.default.cyan(line) + "\n");
|
|
24262
24438
|
} else {
|
|
24263
|
-
process.stdout.write(
|
|
24439
|
+
process.stdout.write(import_chalk18.default.gray(line) + "\n");
|
|
24264
24440
|
}
|
|
24265
24441
|
}
|
|
24266
24442
|
}
|
|
@@ -24279,23 +24455,23 @@ function render(entries, idx) {
|
|
|
24279
24455
|
const step = idx + 1;
|
|
24280
24456
|
process.stdout.write(CLEAR_SCREEN);
|
|
24281
24457
|
process.stdout.write(
|
|
24282
|
-
|
|
24458
|
+
import_chalk18.default.magenta.bold(`\u23EA Node9 Undo`) + import_chalk18.default.gray(` \u2500\u2500 step ${step} of ${total}`) + (entry.files?.length ? import_chalk18.default.gray(
|
|
24283
24459
|
` \u2500\u2500 ${entry.files.slice(0, 2).join(", ")}${entry.files.length > 2 ? ` +${entry.files.length - 2} more` : ""}`
|
|
24284
24460
|
) : "") + "\n\n"
|
|
24285
24461
|
);
|
|
24286
24462
|
process.stdout.write(
|
|
24287
|
-
` ${BOLD}Tool:${RESET} ${
|
|
24463
|
+
` ${BOLD}Tool:${RESET} ${import_chalk18.default.cyan(entry.tool)}` + (entry.argsSummary ? import_chalk18.default.gray(" \u2192 " + entry.argsSummary) : "") + "\n"
|
|
24288
24464
|
);
|
|
24289
|
-
process.stdout.write(` ${BOLD}When:${RESET} ${
|
|
24465
|
+
process.stdout.write(` ${BOLD}When:${RESET} ${import_chalk18.default.gray(formatAge(entry.timestamp))}
|
|
24290
24466
|
`);
|
|
24291
|
-
process.stdout.write(` ${BOLD}Dir: ${RESET} ${
|
|
24467
|
+
process.stdout.write(` ${BOLD}Dir: ${RESET} ${import_chalk18.default.gray(entry.cwd)}
|
|
24292
24468
|
`);
|
|
24293
24469
|
if (entry.files && entry.files.length > 0) {
|
|
24294
|
-
process.stdout.write(` ${BOLD}Files:${RESET} ${
|
|
24470
|
+
process.stdout.write(` ${BOLD}Files:${RESET} ${import_chalk18.default.gray(entry.files.join(", "))}
|
|
24295
24471
|
`);
|
|
24296
24472
|
}
|
|
24297
24473
|
if (idx < total - 1 && isSessionBoundary(entries, idx + 1)) {
|
|
24298
|
-
process.stdout.write(
|
|
24474
|
+
process.stdout.write(import_chalk18.default.gray("\n \u2500\u2500 session boundary above \u2500\u2500\n"));
|
|
24299
24475
|
}
|
|
24300
24476
|
process.stdout.write("\n");
|
|
24301
24477
|
const diff = entry.diff ?? computeUndoDiff(entry.hash, entry.cwd);
|
|
@@ -24303,12 +24479,12 @@ function render(entries, idx) {
|
|
|
24303
24479
|
renderDiff(diff);
|
|
24304
24480
|
} else {
|
|
24305
24481
|
process.stdout.write(
|
|
24306
|
-
|
|
24482
|
+
import_chalk18.default.gray(" (no diff \u2014 working tree may already match this snapshot)\n")
|
|
24307
24483
|
);
|
|
24308
24484
|
}
|
|
24309
24485
|
process.stdout.write("\n");
|
|
24310
24486
|
process.stdout.write(
|
|
24311
|
-
|
|
24487
|
+
import_chalk18.default.gray(" ") + (idx < total - 1 ? import_chalk18.default.white("[\u2190] older") : import_chalk18.default.gray("[\u2190] older")) + import_chalk18.default.gray(" ") + (idx > 0 ? import_chalk18.default.white("[\u2192] newer") : import_chalk18.default.gray("[\u2192] newer")) + import_chalk18.default.gray(" ") + import_chalk18.default.green("[\u21B5] restore here") + import_chalk18.default.gray(" ") + import_chalk18.default.yellow("[s] session start") + import_chalk18.default.gray(" ") + import_chalk18.default.gray("[q] quit") + "\n"
|
|
24312
24488
|
);
|
|
24313
24489
|
}
|
|
24314
24490
|
async function runUndoNavigator(entries) {
|
|
@@ -24362,19 +24538,19 @@ async function runUndoNavigator(entries) {
|
|
|
24362
24538
|
cleanup();
|
|
24363
24539
|
process.stdout.write(CLEAR_SCREEN);
|
|
24364
24540
|
const entry = display[idx];
|
|
24365
|
-
process.stdout.write(
|
|
24541
|
+
process.stdout.write(import_chalk18.default.magenta.bold("\n\u23EA Restoring snapshot...\n\n"));
|
|
24366
24542
|
if (applyUndo(entry.hash, entry.cwd)) {
|
|
24367
|
-
process.stdout.write(
|
|
24543
|
+
process.stdout.write(import_chalk18.default.green("\u2705 Reverted successfully.\n\n"));
|
|
24368
24544
|
resolve({ restored: true });
|
|
24369
24545
|
} else {
|
|
24370
|
-
process.stdout.write(
|
|
24546
|
+
process.stdout.write(import_chalk18.default.red("\u274C Undo failed.\n\n"));
|
|
24371
24547
|
resolve({ restored: false });
|
|
24372
24548
|
}
|
|
24373
24549
|
} else if (name === "q" || key?.ctrl && name === "c") {
|
|
24374
24550
|
done = true;
|
|
24375
24551
|
cleanup();
|
|
24376
24552
|
process.stdout.write(CLEAR_SCREEN);
|
|
24377
|
-
process.stdout.write(
|
|
24553
|
+
process.stdout.write(import_chalk18.default.gray("\nCancelled.\n\n"));
|
|
24378
24554
|
resolve({ restored: false });
|
|
24379
24555
|
}
|
|
24380
24556
|
};
|
|
@@ -24410,39 +24586,39 @@ function registerUndoCommand(program2) {
|
|
|
24410
24586
|
if (history.length === 0) {
|
|
24411
24587
|
if (!options.all && allHistory.length > 0) {
|
|
24412
24588
|
console.log(
|
|
24413
|
-
|
|
24589
|
+
import_chalk19.default.yellow(
|
|
24414
24590
|
`
|
|
24415
24591
|
\u2139\uFE0F No snapshots found for the current directory (${process.cwd()}).
|
|
24416
|
-
Run ${
|
|
24592
|
+
Run ${import_chalk19.default.cyan("node9 undo --all")} to see snapshots from all projects.
|
|
24417
24593
|
`
|
|
24418
24594
|
)
|
|
24419
24595
|
);
|
|
24420
24596
|
} else {
|
|
24421
|
-
console.log(
|
|
24597
|
+
console.log(import_chalk19.default.yellow("\n\u2139\uFE0F No undo snapshots found.\n"));
|
|
24422
24598
|
}
|
|
24423
24599
|
return;
|
|
24424
24600
|
}
|
|
24425
24601
|
if (options.list) {
|
|
24426
|
-
console.log(
|
|
24602
|
+
console.log(import_chalk19.default.magenta.bold("\n\u23EA Snapshot History\n"));
|
|
24427
24603
|
console.log(
|
|
24428
|
-
|
|
24604
|
+
import_chalk19.default.gray(
|
|
24429
24605
|
` ${"#".padEnd(3)} ${"File / Command".padEnd(30)} ${"Tool".padEnd(8)} ${"When".padEnd(10)} Dir`
|
|
24430
24606
|
)
|
|
24431
24607
|
);
|
|
24432
|
-
console.log(
|
|
24608
|
+
console.log(import_chalk19.default.gray(" " + "\u2500".repeat(80)));
|
|
24433
24609
|
const display = [...history].reverse();
|
|
24434
24610
|
let prevTs = null;
|
|
24435
24611
|
for (let i = 0; i < display.length; i++) {
|
|
24436
24612
|
const e = display[i];
|
|
24437
24613
|
const isGap = prevTs !== null && prevTs - e.timestamp > 6e4;
|
|
24438
|
-
if (isGap) console.log(
|
|
24614
|
+
if (isGap) console.log(import_chalk19.default.gray(" \u2500\u2500 earlier \u2500\u2500"));
|
|
24439
24615
|
const label2 = (e.argsSummary || e.files?.[0] || "\u2014").slice(0, 30).padEnd(30);
|
|
24440
24616
|
const tool = e.tool.slice(0, 8).padEnd(8);
|
|
24441
24617
|
const when = formatAge2(e.timestamp).padEnd(10);
|
|
24442
24618
|
const dir = e.cwd.length > 30 ? "\u2026" + e.cwd.slice(-29) : e.cwd;
|
|
24443
24619
|
console.log(
|
|
24444
|
-
|
|
24445
|
-
` ${String(i + 1).padEnd(3)} ${label2} ${
|
|
24620
|
+
import_chalk19.default.white(
|
|
24621
|
+
` ${String(i + 1).padEnd(3)} ${label2} ${import_chalk19.default.cyan(tool)} ${import_chalk19.default.gray(when)} ${import_chalk19.default.gray(dir)}`
|
|
24446
24622
|
)
|
|
24447
24623
|
);
|
|
24448
24624
|
prevTs = e.timestamp;
|
|
@@ -24455,7 +24631,7 @@ function registerUndoCommand(program2) {
|
|
|
24455
24631
|
const idx = history.length - steps;
|
|
24456
24632
|
if (idx < 0) {
|
|
24457
24633
|
console.log(
|
|
24458
|
-
|
|
24634
|
+
import_chalk19.default.yellow(
|
|
24459
24635
|
`
|
|
24460
24636
|
\u2139\uFE0F Only ${history.length} snapshot(s) available, cannot go back ${steps}.
|
|
24461
24637
|
`
|
|
@@ -24466,47 +24642,47 @@ function registerUndoCommand(program2) {
|
|
|
24466
24642
|
const snapshot = history[idx];
|
|
24467
24643
|
const ageStr = formatAge2(snapshot.timestamp);
|
|
24468
24644
|
console.log(
|
|
24469
|
-
|
|
24645
|
+
import_chalk19.default.magenta.bold(`
|
|
24470
24646
|
\u23EA Node9 Undo${steps > 1 ? ` (${steps} steps back)` : ""}`)
|
|
24471
24647
|
);
|
|
24472
24648
|
console.log(
|
|
24473
|
-
|
|
24474
|
-
` Tool: ${
|
|
24649
|
+
import_chalk19.default.white(
|
|
24650
|
+
` Tool: ${import_chalk19.default.cyan(snapshot.tool)}${snapshot.argsSummary ? import_chalk19.default.gray(" \u2192 " + snapshot.argsSummary) : ""}`
|
|
24475
24651
|
)
|
|
24476
24652
|
);
|
|
24477
|
-
console.log(
|
|
24478
|
-
console.log(
|
|
24653
|
+
console.log(import_chalk19.default.white(` When: ${import_chalk19.default.gray(ageStr)}`));
|
|
24654
|
+
console.log(import_chalk19.default.white(` Dir: ${import_chalk19.default.gray(snapshot.cwd)}`));
|
|
24479
24655
|
if (steps > 1)
|
|
24480
24656
|
console.log(
|
|
24481
|
-
|
|
24657
|
+
import_chalk19.default.yellow(` Note: This will also undo the ${steps - 1} action(s) after it.`)
|
|
24482
24658
|
);
|
|
24483
24659
|
console.log("");
|
|
24484
24660
|
const diff = snapshot.diff ?? computeUndoDiff(snapshot.hash, snapshot.cwd);
|
|
24485
24661
|
if (diff) {
|
|
24486
24662
|
const lines = diff.split("\n").filter((l) => !l.startsWith("diff --git") && !l.startsWith("index "));
|
|
24487
24663
|
for (const line of lines) {
|
|
24488
|
-
if (line.startsWith("+++") || line.startsWith("---")) console.log(
|
|
24489
|
-
else if (line.startsWith("+")) console.log(
|
|
24490
|
-
else if (line.startsWith("-")) console.log(
|
|
24491
|
-
else if (line.startsWith("@@")) console.log(
|
|
24492
|
-
else console.log(
|
|
24664
|
+
if (line.startsWith("+++") || line.startsWith("---")) console.log(import_chalk19.default.bold(line));
|
|
24665
|
+
else if (line.startsWith("+")) console.log(import_chalk19.default.green(line));
|
|
24666
|
+
else if (line.startsWith("-")) console.log(import_chalk19.default.red(line));
|
|
24667
|
+
else if (line.startsWith("@@")) console.log(import_chalk19.default.cyan(line));
|
|
24668
|
+
else console.log(import_chalk19.default.gray(line));
|
|
24493
24669
|
}
|
|
24494
24670
|
console.log("");
|
|
24495
24671
|
} else {
|
|
24496
24672
|
console.log(
|
|
24497
|
-
|
|
24673
|
+
import_chalk19.default.gray(" (no diff available \u2014 working tree may already match snapshot)\n")
|
|
24498
24674
|
);
|
|
24499
24675
|
}
|
|
24500
24676
|
const { confirm: confirm3 } = await import("@inquirer/prompts");
|
|
24501
24677
|
const proceed = await confirm3({ message: `Revert to this snapshot?`, default: false });
|
|
24502
24678
|
if (proceed) {
|
|
24503
24679
|
if (applyUndo(snapshot.hash, snapshot.cwd)) {
|
|
24504
|
-
console.log(
|
|
24680
|
+
console.log(import_chalk19.default.green("\n\u2705 Reverted successfully.\n"));
|
|
24505
24681
|
} else {
|
|
24506
|
-
console.error(
|
|
24682
|
+
console.error(import_chalk19.default.red("\n\u274C Undo failed. Ensure you are in a Git repository.\n"));
|
|
24507
24683
|
}
|
|
24508
24684
|
} else {
|
|
24509
|
-
console.log(
|
|
24685
|
+
console.log(import_chalk19.default.gray("\nCancelled.\n"));
|
|
24510
24686
|
}
|
|
24511
24687
|
return;
|
|
24512
24688
|
}
|
|
@@ -24516,7 +24692,7 @@ function registerUndoCommand(program2) {
|
|
|
24516
24692
|
|
|
24517
24693
|
// src/mcp-gateway/index.ts
|
|
24518
24694
|
var import_readline4 = __toESM(require("readline"));
|
|
24519
|
-
var
|
|
24695
|
+
var import_chalk20 = __toESM(require("chalk"));
|
|
24520
24696
|
var import_child_process10 = require("child_process");
|
|
24521
24697
|
var import_execa3 = require("execa");
|
|
24522
24698
|
init_orchestrator();
|
|
@@ -24652,13 +24828,13 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
24652
24828
|
const prov = checkProvenance(executable);
|
|
24653
24829
|
if (prov.trustLevel === "suspect") {
|
|
24654
24830
|
console.error(
|
|
24655
|
-
|
|
24831
|
+
import_chalk20.default.red(
|
|
24656
24832
|
`\u26A0\uFE0F Node9: Upstream MCP server binary is suspect \u2014 ${prov.reason} (${prov.resolvedPath})`
|
|
24657
24833
|
)
|
|
24658
24834
|
);
|
|
24659
|
-
console.error(
|
|
24835
|
+
console.error(import_chalk20.default.red(" Verify this binary is trusted before proceeding."));
|
|
24660
24836
|
}
|
|
24661
|
-
console.error(
|
|
24837
|
+
console.error(import_chalk20.default.green(`\u{1F680} Node9 MCP Gateway: Monitoring [${upstreamCommand}]`));
|
|
24662
24838
|
const UPSTREAM_INJECTOR_VARS = /* @__PURE__ */ new Set([
|
|
24663
24839
|
"NODE_OPTIONS",
|
|
24664
24840
|
"NODE_PATH",
|
|
@@ -24771,10 +24947,10 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
24771
24947
|
mcpServer
|
|
24772
24948
|
});
|
|
24773
24949
|
if (!result.approved) {
|
|
24774
|
-
console.error(
|
|
24950
|
+
console.error(import_chalk20.default.red(`
|
|
24775
24951
|
\u{1F6D1} Node9 MCP Gateway: Action Blocked`));
|
|
24776
|
-
console.error(
|
|
24777
|
-
console.error(
|
|
24952
|
+
console.error(import_chalk20.default.gray(` Tool: ${toolName}`));
|
|
24953
|
+
console.error(import_chalk20.default.gray(` Reason: ${result.reason ?? "Security Policy"}
|
|
24778
24954
|
`));
|
|
24779
24955
|
const blockedByLabel = result.blockedByLabel ?? result.reason ?? "Security Policy";
|
|
24780
24956
|
const isHumanDecision = blockedByLabel.toLowerCase().includes("user") || blockedByLabel.toLowerCase().includes("daemon") || blockedByLabel.toLowerCase().includes("decision");
|
|
@@ -24887,7 +25063,7 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
24887
25063
|
updatePin(serverKey, upstreamCommand, currentHash, toolNames);
|
|
24888
25064
|
pinState = "validated";
|
|
24889
25065
|
console.error(
|
|
24890
|
-
|
|
25066
|
+
import_chalk20.default.green(
|
|
24891
25067
|
`\u{1F512} Node9: Pinned ${toolNames.length} tool definition(s) for this MCP server`
|
|
24892
25068
|
)
|
|
24893
25069
|
);
|
|
@@ -24900,11 +25076,11 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
24900
25076
|
} else if (pinStatus === "corrupt") {
|
|
24901
25077
|
pinState = "quarantined";
|
|
24902
25078
|
console.error(
|
|
24903
|
-
|
|
25079
|
+
import_chalk20.default.red("\n\u{1F6A8} Node9: MCP pin file is corrupt or unreadable \u2014 session quarantined!")
|
|
24904
25080
|
);
|
|
24905
|
-
console.error(
|
|
25081
|
+
console.error(import_chalk20.default.red(" Tool calls are blocked until the pin file is repaired."));
|
|
24906
25082
|
console.error(
|
|
24907
|
-
|
|
25083
|
+
import_chalk20.default.yellow(` Run: node9 mcp pin reset (to clear and re-pin on next connect)
|
|
24908
25084
|
`)
|
|
24909
25085
|
);
|
|
24910
25086
|
const errorResponse = {
|
|
@@ -24921,13 +25097,13 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
24921
25097
|
} else {
|
|
24922
25098
|
pinState = "quarantined";
|
|
24923
25099
|
console.error(
|
|
24924
|
-
|
|
25100
|
+
import_chalk20.default.red("\n\u{1F6A8} Node9: MCP tool definitions have changed since last verified!")
|
|
24925
25101
|
);
|
|
24926
25102
|
console.error(
|
|
24927
|
-
|
|
25103
|
+
import_chalk20.default.red(" This could indicate a supply chain attack (tool poisoning / rug pull).")
|
|
24928
25104
|
);
|
|
24929
|
-
console.error(
|
|
24930
|
-
console.error(
|
|
25105
|
+
console.error(import_chalk20.default.red(" Session quarantined \u2014 all tool calls blocked."));
|
|
25106
|
+
console.error(import_chalk20.default.yellow(` Run: node9 mcp pin update ${serverKey}
|
|
24931
25107
|
`));
|
|
24932
25108
|
reportPinMismatchToCloud(serverKey, clientName);
|
|
24933
25109
|
const errorResponse = {
|
|
@@ -24971,7 +25147,7 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
24971
25147
|
const toolName = callId !== void 0 ? pendingCallNames.get(callId) ?? "unknown" : "unknown";
|
|
24972
25148
|
if (callId !== void 0) pendingCallNames.delete(callId);
|
|
24973
25149
|
console.error(
|
|
24974
|
-
|
|
25150
|
+
import_chalk20.default.yellow(
|
|
24975
25151
|
`\u26A1 Node9: Large MCP response from '${toolName}' (${(line.length / 1024).toFixed(0)}KB) \u2014 context window enlarged`
|
|
24976
25152
|
)
|
|
24977
25153
|
);
|
|
@@ -25938,7 +26114,7 @@ function registerMcpServerCommand(program2) {
|
|
|
25938
26114
|
}
|
|
25939
26115
|
|
|
25940
26116
|
// src/cli/commands/trust.ts
|
|
25941
|
-
var
|
|
26117
|
+
var import_chalk21 = __toESM(require("chalk"));
|
|
25942
26118
|
init_trusted_hosts();
|
|
25943
26119
|
function isValidHost(host) {
|
|
25944
26120
|
return /^(\*\.)?[a-z0-9][a-z0-9.-]*\.[a-z]{2,}$/.test(host);
|
|
@@ -25949,51 +26125,51 @@ function registerTrustCommand(program2) {
|
|
|
25949
26125
|
const normalized = normalizeHost(host.trim());
|
|
25950
26126
|
if (!isValidHost(normalized)) {
|
|
25951
26127
|
console.error(
|
|
25952
|
-
|
|
26128
|
+
import_chalk21.default.red(`
|
|
25953
26129
|
\u274C Invalid host: "${host}"
|
|
25954
|
-
`) +
|
|
26130
|
+
`) + import_chalk21.default.gray(" Use an FQDN like api.mycompany.com or *.mycompany.com\n")
|
|
25955
26131
|
);
|
|
25956
26132
|
process.exit(1);
|
|
25957
26133
|
}
|
|
25958
26134
|
addTrustedHost(normalized);
|
|
25959
|
-
console.log(
|
|
26135
|
+
console.log(import_chalk21.default.green(`
|
|
25960
26136
|
\u2705 ${normalized} added to trusted hosts.`));
|
|
25961
26137
|
console.log(
|
|
25962
|
-
|
|
26138
|
+
import_chalk21.default.gray(" Pipe-chain blocks to this host: critical \u2192 review, high \u2192 allow\n")
|
|
25963
26139
|
);
|
|
25964
26140
|
});
|
|
25965
26141
|
trustCmd.command("remove <host>").description("Remove a trusted host").action((host) => {
|
|
25966
26142
|
const normalized = normalizeHost(host.trim());
|
|
25967
26143
|
const removed = removeTrustedHost(normalized);
|
|
25968
26144
|
if (!removed) {
|
|
25969
|
-
console.error(
|
|
26145
|
+
console.error(import_chalk21.default.yellow(`
|
|
25970
26146
|
\u26A0\uFE0F "${normalized}" is not in the trusted hosts list.
|
|
25971
26147
|
`));
|
|
25972
26148
|
process.exit(1);
|
|
25973
26149
|
}
|
|
25974
|
-
console.log(
|
|
26150
|
+
console.log(import_chalk21.default.green(`
|
|
25975
26151
|
\u2705 ${normalized} removed from trusted hosts.
|
|
25976
26152
|
`));
|
|
25977
26153
|
});
|
|
25978
26154
|
trustCmd.command("list").description("Show all trusted hosts").action(() => {
|
|
25979
26155
|
const hosts = readTrustedHosts();
|
|
25980
26156
|
if (hosts.length === 0) {
|
|
25981
|
-
console.log(
|
|
25982
|
-
console.log(` Add one: ${
|
|
26157
|
+
console.log(import_chalk21.default.gray("\n No trusted hosts configured.\n"));
|
|
26158
|
+
console.log(` Add one: ${import_chalk21.default.cyan("node9 trust add api.mycompany.com")}
|
|
25983
26159
|
`);
|
|
25984
26160
|
return;
|
|
25985
26161
|
}
|
|
25986
|
-
console.log(
|
|
26162
|
+
console.log(import_chalk21.default.bold("\n\u{1F513} Trusted Hosts\n"));
|
|
25987
26163
|
for (const entry of hosts) {
|
|
25988
26164
|
const date = new Date(entry.addedAt).toLocaleDateString();
|
|
25989
|
-
console.log(` ${
|
|
26165
|
+
console.log(` ${import_chalk21.default.cyan(entry.host.padEnd(40))} ${import_chalk21.default.gray(`added ${date}`)}`);
|
|
25990
26166
|
}
|
|
25991
26167
|
console.log("");
|
|
25992
26168
|
});
|
|
25993
26169
|
}
|
|
25994
26170
|
|
|
25995
26171
|
// src/cli/commands/mcp-pin.ts
|
|
25996
|
-
var
|
|
26172
|
+
var import_chalk22 = __toESM(require("chalk"));
|
|
25997
26173
|
init_mcp_pin();
|
|
25998
26174
|
var import_fs54 = __toESM(require("fs"));
|
|
25999
26175
|
function registerMcpPinCommand(program2) {
|
|
@@ -26014,14 +26190,14 @@ function registerMcpPinCommand(program2) {
|
|
|
26014
26190
|
}
|
|
26015
26191
|
}
|
|
26016
26192
|
if (repoCorrupt) {
|
|
26017
|
-
console.error(
|
|
26193
|
+
console.error(import_chalk22.default.red(`
|
|
26018
26194
|
\u274C Repo pin file at ${found.path} is corrupt or unreadable.`));
|
|
26019
26195
|
process.exit(1);
|
|
26020
26196
|
}
|
|
26021
26197
|
if (!homeResult.ok && homeResult.reason === "corrupt") {
|
|
26022
|
-
console.error(
|
|
26198
|
+
console.error(import_chalk22.default.red(`
|
|
26023
26199
|
\u274C Home pin file is corrupt: ${homeResult.detail}`));
|
|
26024
|
-
console.error(
|
|
26200
|
+
console.error(import_chalk22.default.yellow(" Run: node9 mcp pin reset\n"));
|
|
26025
26201
|
process.exit(1);
|
|
26026
26202
|
}
|
|
26027
26203
|
const homeEntries = homeResult.ok ? homeResult.pins.servers : {};
|
|
@@ -26033,25 +26209,25 @@ function registerMcpPinCommand(program2) {
|
|
|
26033
26209
|
merged.set(key, { entry, source: "repo" });
|
|
26034
26210
|
}
|
|
26035
26211
|
if (merged.size === 0) {
|
|
26036
|
-
console.log(
|
|
26212
|
+
console.log(import_chalk22.default.gray("\nNo MCP servers are pinned yet."));
|
|
26037
26213
|
console.log(
|
|
26038
|
-
|
|
26214
|
+
import_chalk22.default.gray("Pins are created automatically when the MCP gateway first connects.\n")
|
|
26039
26215
|
);
|
|
26040
26216
|
return;
|
|
26041
26217
|
}
|
|
26042
|
-
console.log(
|
|
26218
|
+
console.log(import_chalk22.default.bold("\n\u{1F512} Pinned MCP Servers\n"));
|
|
26043
26219
|
const showSource = found.source === "repo";
|
|
26044
26220
|
for (const [key, { entry, source }] of merged) {
|
|
26045
|
-
const tag = showSource ? ` ${
|
|
26046
|
-
console.log(` ${
|
|
26047
|
-
console.log(` Tools (${entry.toolCount}): ${
|
|
26048
|
-
console.log(` Hash: ${
|
|
26049
|
-
console.log(` Pinned: ${
|
|
26221
|
+
const tag = showSource ? ` ${import_chalk22.default.yellow(`[${source}]`)}` : "";
|
|
26222
|
+
console.log(` ${import_chalk22.default.cyan(key)}${tag} ${import_chalk22.default.gray(entry.label)}`);
|
|
26223
|
+
console.log(` Tools (${entry.toolCount}): ${import_chalk22.default.white(entry.toolNames.join(", "))}`);
|
|
26224
|
+
console.log(` Hash: ${import_chalk22.default.gray(entry.toolsHash.slice(0, 16))}...`);
|
|
26225
|
+
console.log(` Pinned: ${import_chalk22.default.gray(entry.pinnedAt)}`);
|
|
26050
26226
|
console.log("");
|
|
26051
26227
|
}
|
|
26052
26228
|
if (showSource) {
|
|
26053
|
-
console.log(
|
|
26054
|
-
console.log(
|
|
26229
|
+
console.log(import_chalk22.default.gray(` [repo] entries come from ${found.path}`));
|
|
26230
|
+
console.log(import_chalk22.default.gray(" [home] entries come from ~/.node9/mcp-pins.json\n"));
|
|
26055
26231
|
}
|
|
26056
26232
|
});
|
|
26057
26233
|
pinSubCmd.command("promote <serverKey>").description(
|
|
@@ -26061,22 +26237,22 @@ function registerMcpPinCommand(program2) {
|
|
|
26061
26237
|
const { repoPath, created } = promotePin(serverKey, process.cwd());
|
|
26062
26238
|
if (created) {
|
|
26063
26239
|
console.log(
|
|
26064
|
-
|
|
26240
|
+
import_chalk22.default.green(
|
|
26065
26241
|
`
|
|
26066
|
-
\u2705 Created ${repoPath} with the promoted pin for ${
|
|
26242
|
+
\u2705 Created ${repoPath} with the promoted pin for ${import_chalk22.default.cyan(serverKey)}.`
|
|
26067
26243
|
)
|
|
26068
26244
|
);
|
|
26069
26245
|
} else {
|
|
26070
|
-
console.log(
|
|
26071
|
-
\u2705 Promoted ${
|
|
26246
|
+
console.log(import_chalk22.default.green(`
|
|
26247
|
+
\u2705 Promoted ${import_chalk22.default.cyan(serverKey)} into ${repoPath}.`));
|
|
26072
26248
|
}
|
|
26073
|
-
console.log(
|
|
26074
|
-
console.log(
|
|
26075
|
-
console.log(
|
|
26249
|
+
console.log(import_chalk22.default.gray(" Review the change and commit it:"));
|
|
26250
|
+
console.log(import_chalk22.default.cyan(` git add ${repoPath}`));
|
|
26251
|
+
console.log(import_chalk22.default.cyan(` git commit -m "pin ${serverKey} (node9)"`));
|
|
26076
26252
|
console.log("");
|
|
26077
26253
|
} catch (err2) {
|
|
26078
26254
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
26079
|
-
console.error(
|
|
26255
|
+
console.error(import_chalk22.default.red(`
|
|
26080
26256
|
\u274C ${msg}
|
|
26081
26257
|
`));
|
|
26082
26258
|
process.exit(1);
|
|
@@ -26089,138 +26265,138 @@ function registerMcpPinCommand(program2) {
|
|
|
26089
26265
|
try {
|
|
26090
26266
|
pins = readMcpPins();
|
|
26091
26267
|
} catch {
|
|
26092
|
-
console.error(
|
|
26093
|
-
console.error(
|
|
26268
|
+
console.error(import_chalk22.default.red("\n\u274C Pin file is corrupt."));
|
|
26269
|
+
console.error(import_chalk22.default.yellow(" Run: node9 mcp pin reset\n"));
|
|
26094
26270
|
process.exit(1);
|
|
26095
26271
|
}
|
|
26096
26272
|
if (!pins.servers[serverKey]) {
|
|
26097
|
-
console.error(
|
|
26273
|
+
console.error(import_chalk22.default.red(`
|
|
26098
26274
|
\u274C No pin found for server key "${serverKey}"
|
|
26099
26275
|
`));
|
|
26100
|
-
console.error(`Run ${
|
|
26276
|
+
console.error(`Run ${import_chalk22.default.cyan("node9 mcp pin list")} to see pinned servers.
|
|
26101
26277
|
`);
|
|
26102
26278
|
process.exit(1);
|
|
26103
26279
|
}
|
|
26104
26280
|
const label2 = pins.servers[serverKey].label;
|
|
26105
26281
|
removePin(serverKey);
|
|
26106
|
-
console.log(
|
|
26107
|
-
\u{1F513} Pin removed for ${
|
|
26108
|
-
console.log(
|
|
26109
|
-
console.log(
|
|
26282
|
+
console.log(import_chalk22.default.green(`
|
|
26283
|
+
\u{1F513} Pin removed for ${import_chalk22.default.cyan(serverKey)}`));
|
|
26284
|
+
console.log(import_chalk22.default.gray(` Server: ${label2}`));
|
|
26285
|
+
console.log(import_chalk22.default.gray(" Next connection will re-pin with current tool definitions.\n"));
|
|
26110
26286
|
});
|
|
26111
26287
|
pinSubCmd.command("reset").description("Clear all MCP pins (next connection to each server will re-pin)").action(() => {
|
|
26112
26288
|
const result = readMcpPinsSafe();
|
|
26113
26289
|
if (!result.ok && result.reason === "missing") {
|
|
26114
|
-
console.log(
|
|
26290
|
+
console.log(import_chalk22.default.gray("\nNo pins to clear.\n"));
|
|
26115
26291
|
return;
|
|
26116
26292
|
}
|
|
26117
26293
|
const count = result.ok ? Object.keys(result.pins.servers).length : "?";
|
|
26118
26294
|
clearAllPins();
|
|
26119
|
-
console.log(
|
|
26295
|
+
console.log(import_chalk22.default.green(`
|
|
26120
26296
|
\u{1F513} Cleared ${count} MCP pin(s).`));
|
|
26121
|
-
console.log(
|
|
26297
|
+
console.log(import_chalk22.default.gray(" Next connection to each server will re-pin.\n"));
|
|
26122
26298
|
});
|
|
26123
26299
|
}
|
|
26124
26300
|
|
|
26125
26301
|
// src/cli/commands/sync.ts
|
|
26126
|
-
var
|
|
26302
|
+
var import_chalk23 = __toESM(require("chalk"));
|
|
26127
26303
|
init_sync();
|
|
26128
26304
|
function registerSyncCommand(program2) {
|
|
26129
26305
|
const policy = program2.command("policy").description("Manage cloud policy rules");
|
|
26130
26306
|
policy.command("push").description("Push this machine's effective policy to the node9 dashboard").action(async () => {
|
|
26131
|
-
process.stdout.write(
|
|
26307
|
+
process.stdout.write(import_chalk23.default.cyan("Pushing policy to the dashboard\u2026"));
|
|
26132
26308
|
const result = await runPolicyPush();
|
|
26133
26309
|
process.stdout.write("\n");
|
|
26134
26310
|
if (!result.ok) {
|
|
26135
|
-
console.error(
|
|
26311
|
+
console.error(import_chalk23.default.red(`\u2717 ${result.reason}`));
|
|
26136
26312
|
process.exit(1);
|
|
26137
26313
|
}
|
|
26138
|
-
console.log(
|
|
26139
|
-
console.log(
|
|
26314
|
+
console.log(import_chalk23.default.green("\u2713 Policy mirrored to the dashboard"));
|
|
26315
|
+
console.log(import_chalk23.default.gray(" See it under Security Policy \u2192 Machines"));
|
|
26140
26316
|
});
|
|
26141
26317
|
policy.command("sync").description("Sync cloud policy rules to local cache (~/.node9/rules-cache.json)").action(async () => {
|
|
26142
|
-
process.stdout.write(
|
|
26318
|
+
process.stdout.write(import_chalk23.default.cyan("Syncing cloud policy rules\u2026"));
|
|
26143
26319
|
const result = await runCloudSync();
|
|
26144
26320
|
process.stdout.write("\n");
|
|
26145
26321
|
if (!result.ok) {
|
|
26146
|
-
console.error(
|
|
26322
|
+
console.error(import_chalk23.default.red(`\u2717 ${result.reason}`));
|
|
26147
26323
|
process.exit(1);
|
|
26148
26324
|
}
|
|
26149
26325
|
if (result.unchanged) {
|
|
26150
26326
|
console.log(
|
|
26151
|
-
|
|
26327
|
+
import_chalk23.default.green(
|
|
26152
26328
|
`\u2713 Already up to date \u2014 ${result.rules} rule${result.rules === 1 ? "" : "s"} cached`
|
|
26153
26329
|
)
|
|
26154
26330
|
);
|
|
26155
|
-
console.log(
|
|
26156
|
-
console.log(
|
|
26331
|
+
console.log(import_chalk23.default.gray(` Cached at: ${result.fetchedAt}`));
|
|
26332
|
+
console.log(import_chalk23.default.gray(` Server returned 304 (no changes since last sync)`));
|
|
26157
26333
|
} else {
|
|
26158
26334
|
console.log(
|
|
26159
|
-
|
|
26335
|
+
import_chalk23.default.green(`\u2713 Synced ${result.rules} rule${result.rules === 1 ? "" : "s"} from cloud`)
|
|
26160
26336
|
);
|
|
26161
|
-
console.log(
|
|
26162
|
-
console.log(
|
|
26337
|
+
console.log(import_chalk23.default.gray(` Cached at: ${result.fetchedAt}`));
|
|
26338
|
+
console.log(import_chalk23.default.gray(` File: ~/.node9/rules-cache.json`));
|
|
26163
26339
|
}
|
|
26164
26340
|
});
|
|
26165
26341
|
policy.command("show").description("List all cloud policy rules in the local cache").action(() => {
|
|
26166
26342
|
const status = getCloudSyncStatus();
|
|
26167
26343
|
if (!status.cached) {
|
|
26168
|
-
console.log(
|
|
26344
|
+
console.log(import_chalk23.default.yellow("\n No cloud rules cached \u2014 run: node9 policy sync\n"));
|
|
26169
26345
|
return;
|
|
26170
26346
|
}
|
|
26171
26347
|
const rules = getCloudRules() ?? [];
|
|
26172
26348
|
const age = Math.round((Date.now() - new Date(status.fetchedAt).getTime()) / 6e4);
|
|
26173
26349
|
console.log(
|
|
26174
|
-
|
|
26175
|
-
Cloud policy rules`) +
|
|
26350
|
+
import_chalk23.default.bold(`
|
|
26351
|
+
Cloud policy rules`) + import_chalk23.default.gray(
|
|
26176
26352
|
` (${rules.length} rule${rules.length === 1 ? "" : "s"}, synced ${age}m ago)
|
|
26177
26353
|
`
|
|
26178
26354
|
)
|
|
26179
26355
|
);
|
|
26180
26356
|
if (rules.length === 0) {
|
|
26181
|
-
console.log(
|
|
26357
|
+
console.log(import_chalk23.default.gray(" No rules defined in cloud policy.\n"));
|
|
26182
26358
|
return;
|
|
26183
26359
|
}
|
|
26184
26360
|
for (const rule of rules) {
|
|
26185
26361
|
const r = rule;
|
|
26186
|
-
const verdictColor = r.verdict === "block" ?
|
|
26362
|
+
const verdictColor = r.verdict === "block" ? import_chalk23.default.red : r.verdict === "allow" ? import_chalk23.default.green : import_chalk23.default.yellow;
|
|
26187
26363
|
console.log(
|
|
26188
26364
|
` ${verdictColor(
|
|
26189
26365
|
String(r.verdict ?? "unknown").toUpperCase().padEnd(6)
|
|
26190
|
-
)} ${
|
|
26366
|
+
)} ${import_chalk23.default.white(String(r.name ?? "(unnamed)"))}`
|
|
26191
26367
|
);
|
|
26192
|
-
if (r.reason) console.log(
|
|
26368
|
+
if (r.reason) console.log(import_chalk23.default.gray(` ${String(r.reason)}`));
|
|
26193
26369
|
}
|
|
26194
26370
|
console.log("");
|
|
26195
26371
|
});
|
|
26196
26372
|
policy.command("status").description("Show current cloud policy cache status").action(() => {
|
|
26197
26373
|
const s = getCloudSyncStatus();
|
|
26198
26374
|
if (!s.cached) {
|
|
26199
|
-
console.log(
|
|
26375
|
+
console.log(import_chalk23.default.yellow("\n No cache yet \u2014 run: node9 policy sync\n"));
|
|
26200
26376
|
return;
|
|
26201
26377
|
}
|
|
26202
26378
|
const age = Math.round((Date.now() - new Date(s.fetchedAt).getTime()) / 6e4);
|
|
26203
26379
|
console.log(`
|
|
26204
|
-
Rules : ${
|
|
26380
|
+
Rules : ${import_chalk23.default.green(String(s.rules))} cloud rules loaded`);
|
|
26205
26381
|
console.log(
|
|
26206
|
-
` Synced : ${
|
|
26382
|
+
` Synced : ${import_chalk23.default.gray(`${age} minute${age === 1 ? "" : "s"} ago`)} (${s.fetchedAt})`
|
|
26207
26383
|
);
|
|
26208
26384
|
if (s.workspaceId) {
|
|
26209
|
-
console.log(` Workspace: ${
|
|
26385
|
+
console.log(` Workspace: ${import_chalk23.default.gray(s.workspaceId)}`);
|
|
26210
26386
|
}
|
|
26211
26387
|
if (s.panicMode) {
|
|
26212
26388
|
console.log(
|
|
26213
|
-
` ${
|
|
26389
|
+
` ${import_chalk23.default.red.bold("\u{1F6A8} Panic mode : ON")} ` + import_chalk23.default.dim("(every review-verdict becomes block)")
|
|
26214
26390
|
);
|
|
26215
26391
|
}
|
|
26216
26392
|
if (s.shadowMode) {
|
|
26217
26393
|
console.log(
|
|
26218
|
-
` ${
|
|
26394
|
+
` ${import_chalk23.default.yellow.bold("\u{1F441} Shadow mode : ON")} ` + import_chalk23.default.dim("(blocks become would-block log entries)")
|
|
26219
26395
|
);
|
|
26220
26396
|
}
|
|
26221
26397
|
if (s.syncIntervalHours) {
|
|
26222
26398
|
console.log(
|
|
26223
|
-
|
|
26399
|
+
import_chalk23.default.gray(
|
|
26224
26400
|
` Polling : every ${s.syncIntervalHours} hour${s.syncIntervalHours === 1 ? "" : "s"}`
|
|
26225
26401
|
)
|
|
26226
26402
|
);
|
|
@@ -26230,7 +26406,7 @@ function registerSyncCommand(program2) {
|
|
|
26230
26406
|
}
|
|
26231
26407
|
|
|
26232
26408
|
// src/cli/commands/agents.ts
|
|
26233
|
-
var
|
|
26409
|
+
var import_chalk24 = __toESM(require("chalk"));
|
|
26234
26410
|
init_setup();
|
|
26235
26411
|
var SETUP_FN = {
|
|
26236
26412
|
claude: setupClaude,
|
|
@@ -26274,30 +26450,30 @@ function registerAgentsCommand(program2) {
|
|
|
26274
26450
|
console.log(` ${"Agent".padEnd(14)}${"Installed".padEnd(11)}${"Wired".padEnd(8)}Mode`);
|
|
26275
26451
|
console.log(" " + "\u2500".repeat(44));
|
|
26276
26452
|
for (const s of statuses) {
|
|
26277
|
-
const installed = s.installed ?
|
|
26278
|
-
const wired = !s.installed ?
|
|
26279
|
-
const mode = s.mode ?
|
|
26280
|
-
const hint = s.installed && !s.wired ?
|
|
26453
|
+
const installed = s.installed ? import_chalk24.default.green("\u2713") : import_chalk24.default.gray("\u2717");
|
|
26454
|
+
const wired = !s.installed ? import_chalk24.default.gray("\u2014") : s.wired ? import_chalk24.default.green("\u2713") : import_chalk24.default.yellow("\u2717");
|
|
26455
|
+
const mode = s.mode ? import_chalk24.default.gray(s.mode) : import_chalk24.default.gray("\u2014");
|
|
26456
|
+
const hint = s.installed && !s.wired ? import_chalk24.default.gray(` \u2190 node9 agents add ${s.name}`) : "";
|
|
26281
26457
|
console.log(` ${s.label.padEnd(14)}${installed} ${wired} ${mode}${hint}`);
|
|
26282
26458
|
}
|
|
26283
26459
|
console.log("");
|
|
26284
26460
|
if (!anyInstalled) {
|
|
26285
26461
|
console.log(
|
|
26286
|
-
|
|
26462
|
+
import_chalk24.default.gray(" No AI agents detected. Install Claude Code, Gemini CLI, Cursor,\n") + import_chalk24.default.gray(" Windsurf, VSCode, or Codex then run: node9 agents list\n")
|
|
26287
26463
|
);
|
|
26288
26464
|
return;
|
|
26289
26465
|
}
|
|
26290
26466
|
const unwired = statuses.filter((s) => s.installed && !s.wired);
|
|
26291
26467
|
if (unwired.length > 0) {
|
|
26292
26468
|
console.log(
|
|
26293
|
-
|
|
26469
|
+
import_chalk24.default.yellow(` ${unwired.length} agent(s) not yet wired. Run: `) + import_chalk24.default.white(`node9 agents add ${unwired[0].name}`) + "\n"
|
|
26294
26470
|
);
|
|
26295
26471
|
}
|
|
26296
26472
|
if (statuses.some((s) => s.name === "gemini" && s.installed)) {
|
|
26297
26473
|
console.log(
|
|
26298
|
-
|
|
26474
|
+
import_chalk24.default.yellow(
|
|
26299
26475
|
" \u26A0\uFE0F Gemini CLI stops serving AI Pro/Ultra and free tiers on 2026-06-18.\n"
|
|
26300
|
-
) +
|
|
26476
|
+
) + import_chalk24.default.gray(" Migrate to Antigravity: ") + import_chalk24.default.white("node9 agents add antigravity") + "\n"
|
|
26301
26477
|
);
|
|
26302
26478
|
}
|
|
26303
26479
|
});
|
|
@@ -26305,7 +26481,7 @@ function registerAgentsCommand(program2) {
|
|
|
26305
26481
|
const name = resolveAgentName(agent);
|
|
26306
26482
|
const fn = SETUP_FN[name];
|
|
26307
26483
|
if (!fn) {
|
|
26308
|
-
console.error(
|
|
26484
|
+
console.error(import_chalk24.default.red(`Unknown agent: "${agent}". Supported: ${AGENT_NAMES.join(", ")}`));
|
|
26309
26485
|
process.exit(1);
|
|
26310
26486
|
}
|
|
26311
26487
|
await fn();
|
|
@@ -26314,14 +26490,14 @@ function registerAgentsCommand(program2) {
|
|
|
26314
26490
|
const name = resolveAgentName(agent);
|
|
26315
26491
|
const fn = TEARDOWN_FN[name];
|
|
26316
26492
|
if (!fn) {
|
|
26317
|
-
console.error(
|
|
26493
|
+
console.error(import_chalk24.default.red(`Unknown agent: "${agent}". Supported: ${AGENT_NAMES.join(", ")}`));
|
|
26318
26494
|
process.exit(1);
|
|
26319
26495
|
}
|
|
26320
|
-
console.log(
|
|
26496
|
+
console.log(import_chalk24.default.cyan(`
|
|
26321
26497
|
\u{1F6E1}\uFE0F Node9: removing from ${name}...
|
|
26322
26498
|
`));
|
|
26323
26499
|
fn();
|
|
26324
|
-
console.log(
|
|
26500
|
+
console.log(import_chalk24.default.gray("\n Restart the agent for changes to take effect."));
|
|
26325
26501
|
});
|
|
26326
26502
|
}
|
|
26327
26503
|
|
|
@@ -26329,22 +26505,22 @@ function registerAgentsCommand(program2) {
|
|
|
26329
26505
|
init_scan();
|
|
26330
26506
|
|
|
26331
26507
|
// src/cli/commands/posture.ts
|
|
26332
|
-
var
|
|
26508
|
+
var import_chalk26 = __toESM(require("chalk"));
|
|
26333
26509
|
init_posture();
|
|
26334
26510
|
|
|
26335
26511
|
// src/posture/render.ts
|
|
26336
|
-
var
|
|
26512
|
+
var import_chalk25 = __toESM(require("chalk"));
|
|
26337
26513
|
init_score();
|
|
26338
26514
|
var ICON = {
|
|
26339
|
-
critical:
|
|
26340
|
-
high:
|
|
26341
|
-
medium:
|
|
26342
|
-
advisory:
|
|
26515
|
+
critical: import_chalk25.default.red("\u274C"),
|
|
26516
|
+
high: import_chalk25.default.red("\u274C"),
|
|
26517
|
+
medium: import_chalk25.default.yellow("\u26A0\uFE0F "),
|
|
26518
|
+
advisory: import_chalk25.default.gray("\u26A0\uFE0F ")
|
|
26343
26519
|
};
|
|
26344
26520
|
var TIER_LABEL = {
|
|
26345
|
-
good:
|
|
26346
|
-
"at-risk":
|
|
26347
|
-
critical:
|
|
26521
|
+
good: import_chalk25.default.green("Good"),
|
|
26522
|
+
"at-risk": import_chalk25.default.yellow("At risk"),
|
|
26523
|
+
critical: import_chalk25.default.red("Critical")
|
|
26348
26524
|
};
|
|
26349
26525
|
function wrap(text, width) {
|
|
26350
26526
|
const out = [];
|
|
@@ -26362,35 +26538,35 @@ function wrap(text, width) {
|
|
|
26362
26538
|
}
|
|
26363
26539
|
var LABEL_WIDTH = 14;
|
|
26364
26540
|
function label(category) {
|
|
26365
|
-
return
|
|
26541
|
+
return import_chalk25.default.bold(category.padEnd(Math.max(LABEL_WIDTH, category.length + 1)));
|
|
26366
26542
|
}
|
|
26367
26543
|
function renderFinding(f, showWeight = false) {
|
|
26368
26544
|
const lines = [];
|
|
26369
|
-
const wt = showWeight && f.scoreWeight ?
|
|
26545
|
+
const wt = showWeight && f.scoreWeight ? import_chalk25.default.cyan.bold(`+${f.scoreWeight} `) : "";
|
|
26370
26546
|
lines.push(` ${ICON[f.severity]} ${label(f.category)}${wt}${f.title}`);
|
|
26371
26547
|
const indent = " ".repeat(2 + 3 + LABEL_WIDTH);
|
|
26372
26548
|
const width = 80 - indent.length;
|
|
26373
26549
|
for (const s of [f.what, f.why, f.who]) {
|
|
26374
|
-
if (s) for (const l of wrap(s, width)) lines.push(indent +
|
|
26550
|
+
if (s) for (const l of wrap(s, width)) lines.push(indent + import_chalk25.default.gray(l));
|
|
26375
26551
|
}
|
|
26376
|
-
for (const d of f.detail) lines.push(indent +
|
|
26552
|
+
for (const d of f.detail) lines.push(indent + import_chalk25.default.gray(d));
|
|
26377
26553
|
if (f.fix) {
|
|
26378
26554
|
let first = true;
|
|
26379
26555
|
for (const seg of f.fix.split("\n")) {
|
|
26380
26556
|
for (const l of wrap(seg, width - 2)) {
|
|
26381
|
-
lines.push(indent +
|
|
26557
|
+
lines.push(indent + import_chalk25.default.cyan(first ? "\u2192 " + l : " " + l));
|
|
26382
26558
|
first = false;
|
|
26383
26559
|
}
|
|
26384
26560
|
}
|
|
26385
26561
|
}
|
|
26386
26562
|
const tradeoff = [
|
|
26387
|
-
[f.gain, "gain: ",
|
|
26388
|
-
[f.cost, "cost: ",
|
|
26563
|
+
[f.gain, "gain: ", import_chalk25.default.green],
|
|
26564
|
+
[f.cost, "cost: ", import_chalk25.default.yellow]
|
|
26389
26565
|
];
|
|
26390
26566
|
for (const [text, lbl, color2] of tradeoff) {
|
|
26391
26567
|
if (!text) continue;
|
|
26392
26568
|
wrap(text, width - 6).forEach((l, i) => {
|
|
26393
|
-
lines.push(indent + (i === 0 ? color2(lbl) : " ") +
|
|
26569
|
+
lines.push(indent + (i === 0 ? color2(lbl) : " ") + import_chalk25.default.gray(l));
|
|
26394
26570
|
});
|
|
26395
26571
|
}
|
|
26396
26572
|
return lines;
|
|
@@ -26400,12 +26576,12 @@ function renderPosture(result) {
|
|
|
26400
26576
|
const tier = TIER_LABEL[result.tier];
|
|
26401
26577
|
lines.push("");
|
|
26402
26578
|
lines.push(
|
|
26403
|
-
|
|
26579
|
+
import_chalk25.default.cyan.bold(`\u{1F6E1}\uFE0F Node9 Posture`) + import_chalk25.default.gray(` \u2014 ${result.agent}`) + ` ${import_chalk25.default.bold(`Score: ${result.score}/100`)} (${tier})`
|
|
26404
26580
|
);
|
|
26405
26581
|
const headroom = openHeadroom(result.findings);
|
|
26406
26582
|
if (headroom > 0) {
|
|
26407
26583
|
lines.push(
|
|
26408
|
-
" " +
|
|
26584
|
+
" " + import_chalk25.default.gray(
|
|
26409
26585
|
`${headroom} pts of headroom below \u2014 each layer adds security at some cost to flexibility. You choose which to close.`
|
|
26410
26586
|
)
|
|
26411
26587
|
);
|
|
@@ -26413,21 +26589,21 @@ function renderPosture(result) {
|
|
|
26413
26589
|
lines.push("");
|
|
26414
26590
|
if (result.headline) {
|
|
26415
26591
|
const indent = " ";
|
|
26416
|
-
lines.push(` ${
|
|
26417
|
-
for (const l of wrap(result.headline.risk, 74)) lines.push(indent +
|
|
26592
|
+
lines.push(` ${import_chalk25.default.red.bold("\u{1F525} Biggest risk")}`);
|
|
26593
|
+
for (const l of wrap(result.headline.risk, 74)) lines.push(indent + import_chalk25.default.white(l));
|
|
26418
26594
|
const action = wrap(`Do this first: ${result.headline.action}`, 72);
|
|
26419
|
-
action.forEach((l, i) => lines.push(indent +
|
|
26595
|
+
action.forEach((l, i) => lines.push(indent + import_chalk25.default.cyan(i === 0 ? "\u2192 " + l : " " + l)));
|
|
26420
26596
|
lines.push("");
|
|
26421
26597
|
}
|
|
26422
26598
|
const covered = result.findings.filter((f) => f.coverage?.state === "covered");
|
|
26423
26599
|
const open = result.findings.filter((f) => f.coverage?.state !== "covered");
|
|
26424
26600
|
if (covered.length > 0) {
|
|
26425
|
-
lines.push(" " +
|
|
26601
|
+
lines.push(" " + import_chalk25.default.green("\u{1F7E2} ON NOW \u2014 node9 is enforcing these (your floor)"));
|
|
26426
26602
|
for (const f of covered) {
|
|
26427
26603
|
const gated = f.coverage?.level === "review" ? "approval-gating" : "blocking";
|
|
26428
26604
|
const via = f.coverage?.via ?? "node9";
|
|
26429
26605
|
lines.push(
|
|
26430
|
-
` ${
|
|
26606
|
+
` ${import_chalk25.default.green("\u2705")} ${label(f.category)}${import_chalk25.default.gray(`${via} is ${gated} this`)}`
|
|
26431
26607
|
);
|
|
26432
26608
|
}
|
|
26433
26609
|
lines.push("");
|
|
@@ -26436,24 +26612,24 @@ function renderPosture(result) {
|
|
|
26436
26612
|
const reduceOpen = open.filter((f) => f.owner !== "node9" && f.node9Reduces);
|
|
26437
26613
|
const osOpen = open.filter((f) => f.owner !== "node9" && !f.node9Reduces);
|
|
26438
26614
|
if (node9Open.length > 0) {
|
|
26439
|
-
lines.push(" " +
|
|
26615
|
+
lines.push(" " + import_chalk25.default.cyan.bold("\u{1F527} node9 can fix these \u2014 run the command"));
|
|
26440
26616
|
for (const f of node9Open) lines.push(...renderFinding(f, true));
|
|
26441
26617
|
}
|
|
26442
26618
|
if (reduceOpen.length > 0) {
|
|
26443
26619
|
if (node9Open.length > 0) lines.push("");
|
|
26444
|
-
lines.push(" " +
|
|
26620
|
+
lines.push(" " + import_chalk25.default.yellow.bold("\u{1F512} AVAILABLE \u2014 turn on to harden (each has a tradeoff)"));
|
|
26445
26621
|
for (const f of reduceOpen) lines.push(...renderFinding(f, true));
|
|
26446
26622
|
}
|
|
26447
26623
|
if (osOpen.length > 0) {
|
|
26448
26624
|
if (node9Open.length > 0 || reduceOpen.length > 0) lines.push("");
|
|
26449
|
-
lines.push(" " +
|
|
26625
|
+
lines.push(" " + import_chalk25.default.bold("\u{1F9F1} YOUR PART \u2014 node9 can't fix these (OS-level)"));
|
|
26450
26626
|
for (const f of osOpen) lines.push(...renderFinding(f));
|
|
26451
26627
|
}
|
|
26452
26628
|
for (const cat of result.passedCategories) {
|
|
26453
|
-
lines.push(` ${
|
|
26629
|
+
lines.push(` ${import_chalk25.default.green("\u2705")} ${label(cat)}${import_chalk25.default.gray("no issues found")}`);
|
|
26454
26630
|
}
|
|
26455
26631
|
for (const cat of result.erroredCategories) {
|
|
26456
|
-
lines.push(` ${
|
|
26632
|
+
lines.push(` ${import_chalk25.default.gray("\u2022")} ${label(cat)}${import_chalk25.default.gray("could not be checked")}`);
|
|
26457
26633
|
}
|
|
26458
26634
|
lines.push("");
|
|
26459
26635
|
const crit = open.filter((f) => f.severity === "critical").length;
|
|
@@ -26461,16 +26637,16 @@ function renderPosture(result) {
|
|
|
26461
26637
|
const med = open.filter((f) => f.severity === "medium").length;
|
|
26462
26638
|
const adv = open.filter((f) => f.severity === "advisory").length;
|
|
26463
26639
|
const parts = [];
|
|
26464
|
-
if (crit) parts.push(
|
|
26465
|
-
if (high) parts.push(
|
|
26466
|
-
if (med) parts.push(
|
|
26467
|
-
if (adv) parts.push(
|
|
26468
|
-
const summary = parts.length ? parts.join(" \xB7 ") :
|
|
26640
|
+
if (crit) parts.push(import_chalk25.default.red(`${crit} critical`));
|
|
26641
|
+
if (high) parts.push(import_chalk25.default.red(`${high} high`));
|
|
26642
|
+
if (med) parts.push(import_chalk25.default.yellow(`${med} medium`));
|
|
26643
|
+
if (adv) parts.push(import_chalk25.default.gray(`${adv} advisory`));
|
|
26644
|
+
const summary = parts.length ? parts.join(" \xB7 ") : import_chalk25.default.green("no findings");
|
|
26469
26645
|
lines.push(` ${summary}`);
|
|
26470
26646
|
lines.push("");
|
|
26471
|
-
lines.push(" " +
|
|
26647
|
+
lines.push(" " + import_chalk25.default.bold("Track this across your fleet & keep it green:"));
|
|
26472
26648
|
lines.push(
|
|
26473
|
-
" " +
|
|
26649
|
+
" " + import_chalk25.default.dim("\u2192 ") + import_chalk25.default.cyan.underline("https://node9.ai/auth/signup?ref=cli_posture")
|
|
26474
26650
|
);
|
|
26475
26651
|
lines.push("");
|
|
26476
26652
|
return lines.join("\n");
|
|
@@ -26490,11 +26666,11 @@ function registerPostureCommand(program2) {
|
|
|
26490
26666
|
if (opts.ship) {
|
|
26491
26667
|
const creds = readCredentials();
|
|
26492
26668
|
if (!creds) {
|
|
26493
|
-
console.error(
|
|
26669
|
+
console.error(import_chalk26.default.gray(" Run `node9 login` to ship this to your dashboard."));
|
|
26494
26670
|
} else {
|
|
26495
26671
|
const ok2 = await shipPosture(result, creds);
|
|
26496
26672
|
console.error(
|
|
26497
|
-
ok2 ?
|
|
26673
|
+
ok2 ? import_chalk26.default.gray(" \u2713 Shipped to your node9 dashboard.") : import_chalk26.default.gray(" Could not reach the dashboard \u2014 saved locally only.")
|
|
26498
26674
|
);
|
|
26499
26675
|
}
|
|
26500
26676
|
}
|
|
@@ -26503,7 +26679,7 @@ function registerPostureCommand(program2) {
|
|
|
26503
26679
|
}
|
|
26504
26680
|
|
|
26505
26681
|
// src/cli/commands/egress.ts
|
|
26506
|
-
var
|
|
26682
|
+
var import_chalk27 = __toESM(require("chalk"));
|
|
26507
26683
|
init_config();
|
|
26508
26684
|
init_dist();
|
|
26509
26685
|
function guard(fn) {
|
|
@@ -26511,7 +26687,7 @@ function guard(fn) {
|
|
|
26511
26687
|
fn();
|
|
26512
26688
|
return true;
|
|
26513
26689
|
} catch (err2) {
|
|
26514
|
-
console.error(
|
|
26690
|
+
console.error(import_chalk27.default.red(`
|
|
26515
26691
|
\u2717 ${err2.message}
|
|
26516
26692
|
`));
|
|
26517
26693
|
process.exitCode = 1;
|
|
@@ -26526,19 +26702,19 @@ function addHost(list, host) {
|
|
|
26526
26702
|
}
|
|
26527
26703
|
function showStatus() {
|
|
26528
26704
|
const e = getConfig().policy.egress;
|
|
26529
|
-
const state = !e.enabled ?
|
|
26530
|
-
console.log(
|
|
26705
|
+
const state = !e.enabled ? import_chalk27.default.red("OFF \u2014 your agent can reach any host") : e.mode === "block" ? import_chalk27.default.green("LOCKED (block) \u2014 unknown hosts are denied") : import_chalk27.default.yellow("WATCHING (review) \u2014 unknown hosts prompt you");
|
|
26706
|
+
console.log(import_chalk27.default.cyan.bold("\n\u{1F310} Egress control"));
|
|
26531
26707
|
console.log(" State: " + state);
|
|
26532
26708
|
console.log(
|
|
26533
|
-
|
|
26709
|
+
import_chalk27.default.gray(
|
|
26534
26710
|
` ${DEFAULT_EGRESS_ALLOWLIST.length} common dev/LLM hosts are always allowed (github, npm, pypi, anthropic, \u2026).`
|
|
26535
26711
|
)
|
|
26536
26712
|
);
|
|
26537
26713
|
if (e.allow.length) console.log(" Your allow: " + e.allow.join(", "));
|
|
26538
26714
|
if (e.deny.length) console.log(" Your deny: " + e.deny.join(", "));
|
|
26539
26715
|
if (!e.enabled) {
|
|
26540
|
-
console.log(
|
|
26541
|
-
console.log(
|
|
26716
|
+
console.log(import_chalk27.default.gray("\n Turn it on: node9 egress watch (prompt on unknown hosts)"));
|
|
26717
|
+
console.log(import_chalk27.default.gray(" node9 egress lock (hard-block unknown hosts)"));
|
|
26542
26718
|
}
|
|
26543
26719
|
console.log("");
|
|
26544
26720
|
}
|
|
@@ -26546,43 +26722,43 @@ function registerEgressCommand(program2) {
|
|
|
26546
26722
|
const egress = program2.command("egress").description("Control where your agent can send data (egress allowlist)");
|
|
26547
26723
|
egress.command("watch").description("Prompt before the agent reaches an unknown host (review mode)").action(() => {
|
|
26548
26724
|
if (!mutate({ enabled: true, mode: "review" })) return;
|
|
26549
|
-
console.log(
|
|
26725
|
+
console.log(import_chalk27.default.green("\n\u2713 Egress is now watched (review mode)."));
|
|
26550
26726
|
console.log(
|
|
26551
|
-
|
|
26727
|
+
import_chalk27.default.gray(" Routine hosts (LLM APIs, package registries, localhost) are allowed.")
|
|
26552
26728
|
);
|
|
26553
26729
|
console.log(
|
|
26554
|
-
|
|
26730
|
+
import_chalk27.default.gray(" An unknown host will prompt you \u2014 run `node9 egress lock` to hard-block.\n")
|
|
26555
26731
|
);
|
|
26556
26732
|
});
|
|
26557
26733
|
egress.command("lock").description("Block the agent from reaching unknown hosts (block mode)").action(() => {
|
|
26558
26734
|
if (!mutate({ enabled: true, mode: "block" })) return;
|
|
26559
|
-
console.log(
|
|
26560
|
-
console.log(
|
|
26561
|
-
console.log(
|
|
26735
|
+
console.log(import_chalk27.default.green("\n\u2713 Egress is now locked (block mode)."));
|
|
26736
|
+
console.log(import_chalk27.default.gray(" Routine hosts are still allowed; unknown hosts are denied."));
|
|
26737
|
+
console.log(import_chalk27.default.gray(" Allow a specific host with `node9 egress allow <host>`.\n"));
|
|
26562
26738
|
});
|
|
26563
26739
|
egress.command("allow <host>").description("Allow an extra host (glob, e.g. *.mycorp.com)").action((host) => {
|
|
26564
26740
|
if (!addHost("allow", host)) return;
|
|
26565
|
-
console.log(
|
|
26741
|
+
console.log(import_chalk27.default.green(`
|
|
26566
26742
|
\u2713 Allowed egress to ${host}.
|
|
26567
26743
|
`));
|
|
26568
26744
|
});
|
|
26569
26745
|
egress.command("deny <host>").description("Block an extra host (deny always wins)").action((host) => {
|
|
26570
26746
|
if (!addHost("deny", host)) return;
|
|
26571
|
-
console.log(
|
|
26747
|
+
console.log(import_chalk27.default.green(`
|
|
26572
26748
|
\u2713 Denied egress to ${host}.
|
|
26573
26749
|
`));
|
|
26574
26750
|
});
|
|
26575
26751
|
egress.command("off").description("Turn egress control off").action(() => {
|
|
26576
26752
|
if (!mutate({ enabled: false })) return;
|
|
26577
26753
|
console.log(
|
|
26578
|
-
|
|
26754
|
+
import_chalk27.default.yellow("\n\u2713 Egress control is off \u2014 the agent can reach any host again.\n")
|
|
26579
26755
|
);
|
|
26580
26756
|
});
|
|
26581
26757
|
egress.action(showStatus);
|
|
26582
26758
|
}
|
|
26583
26759
|
|
|
26584
26760
|
// src/cli/commands/jail.ts
|
|
26585
|
-
var
|
|
26761
|
+
var import_chalk28 = __toESM(require("chalk"));
|
|
26586
26762
|
|
|
26587
26763
|
// src/shields/jail.ts
|
|
26588
26764
|
var import_fs55 = __toESM(require("fs"));
|
|
@@ -26677,20 +26853,20 @@ function registerJailCommand(program2) {
|
|
|
26677
26853
|
regenerateUserJail(paths);
|
|
26678
26854
|
appendConfigAudit({ event: "jail-add", path: p.trim(), verdict });
|
|
26679
26855
|
} catch (err2) {
|
|
26680
|
-
console.error(
|
|
26856
|
+
console.error(import_chalk28.default.red(`
|
|
26681
26857
|
\u274C ${err2.message}
|
|
26682
26858
|
`));
|
|
26683
26859
|
process.exit(1);
|
|
26684
26860
|
return;
|
|
26685
26861
|
}
|
|
26686
|
-
console.log(
|
|
26862
|
+
console.log(import_chalk28.default.green(`
|
|
26687
26863
|
\u2705 Jailed ${p} (${verdict}).`));
|
|
26688
26864
|
console.log(
|
|
26689
|
-
|
|
26865
|
+
import_chalk28.default.gray(
|
|
26690
26866
|
` AI reads of this path now ${verdict === "block" ? "BLOCK" : "require approval"}.`
|
|
26691
26867
|
)
|
|
26692
26868
|
);
|
|
26693
|
-
console.log(
|
|
26869
|
+
console.log(import_chalk28.default.gray(` Preview: ${import_chalk28.default.cyan(`node9 explain bash "cat ${p}"`)}
|
|
26694
26870
|
`));
|
|
26695
26871
|
});
|
|
26696
26872
|
jail.command("remove <path>").description("Remove a user-added jail path (built-in paths are not removable)").action((p) => {
|
|
@@ -26698,17 +26874,17 @@ function registerJailCommand(program2) {
|
|
|
26698
26874
|
try {
|
|
26699
26875
|
result = removeJailPath(p);
|
|
26700
26876
|
} catch (err2) {
|
|
26701
|
-
console.error(
|
|
26877
|
+
console.error(import_chalk28.default.red(`
|
|
26702
26878
|
\u274C ${err2.message}
|
|
26703
26879
|
`));
|
|
26704
26880
|
process.exit(1);
|
|
26705
26881
|
return;
|
|
26706
26882
|
}
|
|
26707
26883
|
if (!result.removed) {
|
|
26708
|
-
console.error(
|
|
26884
|
+
console.error(import_chalk28.default.yellow(`
|
|
26709
26885
|
\u2139\uFE0F "${p}" is not a user-added jail path.
|
|
26710
26886
|
`));
|
|
26711
|
-
console.error(
|
|
26887
|
+
console.error(import_chalk28.default.gray(` Run ${import_chalk28.default.cyan("node9 jail list")} to see your paths.
|
|
26712
26888
|
`));
|
|
26713
26889
|
process.exit(1);
|
|
26714
26890
|
return;
|
|
@@ -26717,39 +26893,39 @@ function registerJailCommand(program2) {
|
|
|
26717
26893
|
regenerateUserJail(result.paths);
|
|
26718
26894
|
appendConfigAudit({ event: "jail-remove", path: p.trim() });
|
|
26719
26895
|
} catch (err2) {
|
|
26720
|
-
console.error(
|
|
26896
|
+
console.error(import_chalk28.default.red(`
|
|
26721
26897
|
\u274C ${err2.message}
|
|
26722
26898
|
`));
|
|
26723
26899
|
process.exit(1);
|
|
26724
26900
|
return;
|
|
26725
26901
|
}
|
|
26726
|
-
console.log(
|
|
26902
|
+
console.log(import_chalk28.default.green(`
|
|
26727
26903
|
\u2705 Removed ${p} from the jail.
|
|
26728
26904
|
`));
|
|
26729
26905
|
});
|
|
26730
26906
|
jail.command("list").description("Show built-in + user-added jail paths").action(() => {
|
|
26731
|
-
console.log(
|
|
26732
|
-
console.log(
|
|
26733
|
-
for (const b of BUILTIN_JAIL) console.log(` ${
|
|
26907
|
+
console.log(import_chalk28.default.bold("\n\u{1F512} Credential Jail\n"));
|
|
26908
|
+
console.log(import_chalk28.default.gray(" Built-in (always on, not removable):"));
|
|
26909
|
+
for (const b of BUILTIN_JAIL) console.log(` ${import_chalk28.default.gray("\u2022")} ${b}`);
|
|
26734
26910
|
console.log("");
|
|
26735
26911
|
let user;
|
|
26736
26912
|
try {
|
|
26737
26913
|
user = readJailPaths();
|
|
26738
26914
|
} catch (err2) {
|
|
26739
|
-
console.error(
|
|
26915
|
+
console.error(import_chalk28.default.red(` \u2717 ${err2.message}
|
|
26740
26916
|
`));
|
|
26741
26917
|
process.exit(1);
|
|
26742
26918
|
return;
|
|
26743
26919
|
}
|
|
26744
26920
|
if (user.length === 0) {
|
|
26745
26921
|
console.log(
|
|
26746
|
-
|
|
26922
|
+
import_chalk28.default.gray(" Your paths: (none) \u2014 add one with ") + import_chalk28.default.cyan("node9 jail add <path>")
|
|
26747
26923
|
);
|
|
26748
26924
|
} else {
|
|
26749
|
-
console.log(
|
|
26925
|
+
console.log(import_chalk28.default.gray(" Your paths (removable):"));
|
|
26750
26926
|
for (const u of user) {
|
|
26751
|
-
const v = u.verdict === "block" ?
|
|
26752
|
-
console.log(` ${v} ${
|
|
26927
|
+
const v = u.verdict === "block" ? import_chalk28.default.red("block ") : import_chalk28.default.yellow("review");
|
|
26928
|
+
console.log(` ${v} ${import_chalk28.default.cyan(u.path)}`);
|
|
26753
26929
|
}
|
|
26754
26930
|
}
|
|
26755
26931
|
console.log("");
|
|
@@ -26757,7 +26933,7 @@ function registerJailCommand(program2) {
|
|
|
26757
26933
|
}
|
|
26758
26934
|
|
|
26759
26935
|
// src/cli/commands/sandbox.ts
|
|
26760
|
-
var
|
|
26936
|
+
var import_chalk29 = __toESM(require("chalk"));
|
|
26761
26937
|
var import_fs58 = __toESM(require("fs"));
|
|
26762
26938
|
var import_path56 = __toESM(require("path"));
|
|
26763
26939
|
var import_child_process13 = require("child_process");
|
|
@@ -26995,16 +27171,16 @@ function registerSandboxCommand(program2, version2) {
|
|
|
26995
27171
|
const p = sandboxConfigPath();
|
|
26996
27172
|
if (import_fs58.default.existsSync(p)) {
|
|
26997
27173
|
console.log(
|
|
26998
|
-
|
|
27174
|
+
import_chalk29.default.yellow(` ${SANDBOX_CONFIG_FILE} already exists \u2014 leaving it untouched.`)
|
|
26999
27175
|
);
|
|
27000
27176
|
return;
|
|
27001
27177
|
}
|
|
27002
27178
|
import_fs58.default.writeFileSync(p, scaffoldSandboxYaml(agent));
|
|
27003
27179
|
console.log(
|
|
27004
|
-
|
|
27180
|
+
import_chalk29.default.green(` \u2713 wrote ${SANDBOX_CONFIG_FILE}`) + import_chalk29.default.dim(` (agent: ${agent})`)
|
|
27005
27181
|
);
|
|
27006
27182
|
console.log(
|
|
27007
|
-
|
|
27183
|
+
import_chalk29.default.dim(" Edit it (mounts / allow / expose), then: ") + import_chalk29.default.cyan("node9 sandbox run")
|
|
27008
27184
|
);
|
|
27009
27185
|
});
|
|
27010
27186
|
cmd.command("run [agent]").description("Build (if needed) + run the agent jailed. Extra args after -- go to the agent.").allowUnknownOption(true).allowExcessArguments(true).action((agentArg, _opts, command) => {
|
|
@@ -27014,7 +27190,7 @@ function registerSandboxCommand(program2, version2) {
|
|
|
27014
27190
|
const engine = detectEngine(sandbox.runtime.engine);
|
|
27015
27191
|
if (!engine.available) {
|
|
27016
27192
|
console.error(
|
|
27017
|
-
|
|
27193
|
+
import_chalk29.default.red(` ${sandbox.runtime.engine} not found.`) + import_chalk29.default.dim(` Install it first \u2014 node9 sandbox needs a container runtime.`)
|
|
27018
27194
|
);
|
|
27019
27195
|
process.exit(1);
|
|
27020
27196
|
}
|
|
@@ -27027,11 +27203,11 @@ function registerSandboxCommand(program2, version2) {
|
|
|
27027
27203
|
});
|
|
27028
27204
|
if (compiled.rejected.length) {
|
|
27029
27205
|
console.log(
|
|
27030
|
-
|
|
27206
|
+
import_chalk29.default.yellow(` \u26A0 ignoring invalid allow hosts: ${compiled.rejected.join(", ")}`)
|
|
27031
27207
|
);
|
|
27032
27208
|
}
|
|
27033
27209
|
if (compiled.denied.length) {
|
|
27034
|
-
console.log(
|
|
27210
|
+
console.log(import_chalk29.default.dim(` (denied: ${compiled.denied.join(", ")})`));
|
|
27035
27211
|
}
|
|
27036
27212
|
const allowlistPath = writeAllowlist(cwd, compiled.allow);
|
|
27037
27213
|
const dockerfile = renderDockerfile(sandbox, node9Version2);
|
|
@@ -27044,12 +27220,12 @@ function registerSandboxCommand(program2, version2) {
|
|
|
27044
27220
|
const imageExists = (0, import_child_process13.spawnSync)(sandbox.runtime.engine, ["image", "inspect", image], { stdio: "ignore" }).status === 0;
|
|
27045
27221
|
const needBuild = sandbox.runtime.rebuild === "always" || !imageExists || sandbox.runtime.rebuild !== "never" && lastHash !== hash;
|
|
27046
27222
|
if (needBuild) {
|
|
27047
|
-
console.log(
|
|
27223
|
+
console.log(import_chalk29.default.dim(` building ${image} \u2026`));
|
|
27048
27224
|
const b = (0, import_child_process13.spawnSync)(sandbox.runtime.engine, ["build", "-t", image, buildDir], {
|
|
27049
27225
|
stdio: "inherit"
|
|
27050
27226
|
});
|
|
27051
27227
|
if (b.status !== 0) {
|
|
27052
|
-
console.error(
|
|
27228
|
+
console.error(import_chalk29.default.red(" build failed."));
|
|
27053
27229
|
process.exit(b.status ?? 1);
|
|
27054
27230
|
}
|
|
27055
27231
|
import_fs58.default.writeFileSync(hashFile, hash);
|
|
@@ -27067,15 +27243,15 @@ function registerSandboxCommand(program2, version2) {
|
|
|
27067
27243
|
if (sandbox.node9.mountAgentCredentials) {
|
|
27068
27244
|
const creds = agentCredentialsMount(sandbox.agent);
|
|
27069
27245
|
if (import_fs58.default.existsSync(creds.hostPath)) {
|
|
27070
|
-
console.log(
|
|
27246
|
+
console.log(import_chalk29.default.dim(` mounting ${creds.hostPath} (agent credentials, rw)`));
|
|
27071
27247
|
} else {
|
|
27072
27248
|
console.log(
|
|
27073
|
-
|
|
27249
|
+
import_chalk29.default.yellow(` \u26A0 ${creds.hostPath} not found \u2014 `) + import_chalk29.default.dim(`the agent must auth via an env key in env.pass.`)
|
|
27074
27250
|
);
|
|
27075
27251
|
}
|
|
27076
27252
|
}
|
|
27077
27253
|
console.log(
|
|
27078
|
-
|
|
27254
|
+
import_chalk29.default.green(` \u{1F6E1}\uFE0F ${sandbox.agent} jailed \u2014 ${compiled.allow.length} hosts allowed
|
|
27079
27255
|
`)
|
|
27080
27256
|
);
|
|
27081
27257
|
const r = (0, import_child_process13.spawnSync)(sandbox.runtime.engine, runArgs, { stdio: "inherit" });
|
|
@@ -27084,7 +27260,7 @@ function registerSandboxCommand(program2, version2) {
|
|
|
27084
27260
|
cmd.command("tail").description("Stream the sandbox's audit log (host-side)").action(() => {
|
|
27085
27261
|
const auditPath = import_path56.default.join(sandboxDataDir(), "audit.log");
|
|
27086
27262
|
if (!import_fs58.default.existsSync(auditPath)) {
|
|
27087
|
-
console.log(
|
|
27263
|
+
console.log(import_chalk29.default.dim(" no sandbox audit yet."));
|
|
27088
27264
|
return;
|
|
27089
27265
|
}
|
|
27090
27266
|
(0, import_child_process13.spawnSync)("tail", ["-f", auditPath], { stdio: "inherit" });
|
|
@@ -27092,7 +27268,7 @@ function registerSandboxCommand(program2, version2) {
|
|
|
27092
27268
|
cmd.command("logs").description("Dump the sandbox's audit log").action(() => {
|
|
27093
27269
|
const auditPath = import_path56.default.join(sandboxDataDir(), "audit.log");
|
|
27094
27270
|
if (!import_fs58.default.existsSync(auditPath)) {
|
|
27095
|
-
console.log(
|
|
27271
|
+
console.log(import_chalk29.default.dim(" no sandbox audit yet."));
|
|
27096
27272
|
return;
|
|
27097
27273
|
}
|
|
27098
27274
|
process.stdout.write(import_fs58.default.readFileSync(auditPath, "utf-8"));
|
|
@@ -27110,12 +27286,12 @@ function registerSandboxCommand(program2, version2) {
|
|
|
27110
27286
|
});
|
|
27111
27287
|
}
|
|
27112
27288
|
import_fs58.default.rmSync(import_path56.default.join(cwd, ".node9", "sandbox"), { recursive: true, force: true });
|
|
27113
|
-
console.log(
|
|
27289
|
+
console.log(import_chalk29.default.green(" \u2713 sandbox image + build + data removed."));
|
|
27114
27290
|
});
|
|
27115
27291
|
}
|
|
27116
27292
|
|
|
27117
27293
|
// src/cli/commands/sessions.ts
|
|
27118
|
-
var
|
|
27294
|
+
var import_chalk30 = __toESM(require("chalk"));
|
|
27119
27295
|
var import_fs59 = __toESM(require("fs"));
|
|
27120
27296
|
var import_path57 = __toESM(require("path"));
|
|
27121
27297
|
var import_os50 = __toESM(require("os"));
|
|
@@ -27611,11 +27787,11 @@ function toolInputSummary(tool, input) {
|
|
|
27611
27787
|
}
|
|
27612
27788
|
function toolColor(tool) {
|
|
27613
27789
|
const t = tool.toLowerCase();
|
|
27614
|
-
if (t === "bash" || t === "execute_bash") return
|
|
27615
|
-
if (t === "write") return
|
|
27616
|
-
if (t === "edit" || t === "notebookedit") return
|
|
27617
|
-
if (t === "read") return
|
|
27618
|
-
return
|
|
27790
|
+
if (t === "bash" || t === "execute_bash") return import_chalk30.default.red;
|
|
27791
|
+
if (t === "write") return import_chalk30.default.green;
|
|
27792
|
+
if (t === "edit" || t === "notebookedit") return import_chalk30.default.yellow;
|
|
27793
|
+
if (t === "read") return import_chalk30.default.cyan;
|
|
27794
|
+
return import_chalk30.default.gray;
|
|
27619
27795
|
}
|
|
27620
27796
|
function barStr2(value, max, width) {
|
|
27621
27797
|
if (max === 0 || width <= 0) return "\u2591".repeat(width);
|
|
@@ -27625,7 +27801,7 @@ function barStr2(value, max, width) {
|
|
|
27625
27801
|
function colorBar2(value, max, width) {
|
|
27626
27802
|
const s = barStr2(value, max, width);
|
|
27627
27803
|
const filled = Math.max(1, Math.round(max > 0 ? value / max * width : 0));
|
|
27628
|
-
return
|
|
27804
|
+
return import_chalk30.default.cyan(s.slice(0, filled)) + import_chalk30.default.dim(s.slice(filled));
|
|
27629
27805
|
}
|
|
27630
27806
|
function renderSummary(summaries) {
|
|
27631
27807
|
const totalTools = summaries.reduce((n, s) => n + s.toolCalls.length, 0);
|
|
@@ -27655,45 +27831,45 @@ function renderSummary(summaries) {
|
|
|
27655
27831
|
}
|
|
27656
27832
|
const topProjects = [...projCosts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
|
|
27657
27833
|
const W = 20;
|
|
27658
|
-
console.log(
|
|
27834
|
+
console.log(import_chalk30.default.dim(" " + "\u2500".repeat(70)));
|
|
27659
27835
|
console.log(
|
|
27660
|
-
" " +
|
|
27836
|
+
" " + import_chalk30.default.bold.white(String(summaries.length).padEnd(4)) + import_chalk30.default.dim("sessions ") + import_chalk30.default.bold.yellow(fmtCost3(totalCost).padEnd(10)) + import_chalk30.default.dim("total ") + import_chalk30.default.bold.white(String(totalTools).padEnd(6)) + import_chalk30.default.dim("tool calls ") + import_chalk30.default.bold.white(String(totalFiles)) + import_chalk30.default.dim(" files modified") + (totalBlocked > 0 ? import_chalk30.default.dim(" ") + import_chalk30.default.red.bold(String(totalBlocked)) + import_chalk30.default.dim(" blocked by node9") : "")
|
|
27661
27837
|
);
|
|
27662
27838
|
console.log(
|
|
27663
|
-
" " +
|
|
27839
|
+
" " + import_chalk30.default.dim("avg ") + import_chalk30.default.white(fmtCost3(avgCost).padEnd(10)) + import_chalk30.default.dim("/session ") + import_chalk30.default.green(String(snapshots)) + import_chalk30.default.dim(` of ${summaries.length} sessions had snapshots`)
|
|
27664
27840
|
);
|
|
27665
27841
|
console.log("");
|
|
27666
|
-
console.log(" " +
|
|
27842
|
+
console.log(" " + import_chalk30.default.dim("Tool breakdown:"));
|
|
27667
27843
|
const maxGroup = Math.max(...Object.values(groups));
|
|
27668
27844
|
for (const [label2, count] of Object.entries(groups)) {
|
|
27669
27845
|
if (count === 0) continue;
|
|
27670
27846
|
const pct = totalTools > 0 ? Math.round(count / totalTools * 100) : 0;
|
|
27671
27847
|
console.log(
|
|
27672
|
-
" " + label2.padEnd(6) + " " + colorBar2(count, maxGroup, W) + " " +
|
|
27848
|
+
" " + label2.padEnd(6) + " " + colorBar2(count, maxGroup, W) + " " + import_chalk30.default.white(String(count).padStart(4)) + import_chalk30.default.dim(` (${String(pct)}%)`)
|
|
27673
27849
|
);
|
|
27674
27850
|
}
|
|
27675
27851
|
console.log("");
|
|
27676
27852
|
if (topProjects.length > 1) {
|
|
27677
|
-
console.log(" " +
|
|
27853
|
+
console.log(" " + import_chalk30.default.dim("Cost by project:"));
|
|
27678
27854
|
const maxProjCost = topProjects[0][1];
|
|
27679
27855
|
for (const [proj, cost] of topProjects) {
|
|
27680
27856
|
console.log(
|
|
27681
|
-
" " + proj.slice(0, 28).padEnd(28) + " " + colorBar2(cost, maxProjCost, W) + " " +
|
|
27857
|
+
" " + proj.slice(0, 28).padEnd(28) + " " + colorBar2(cost, maxProjCost, W) + " " + import_chalk30.default.yellow(fmtCost3(cost))
|
|
27682
27858
|
);
|
|
27683
27859
|
}
|
|
27684
27860
|
console.log("");
|
|
27685
27861
|
}
|
|
27686
|
-
console.log(
|
|
27862
|
+
console.log(import_chalk30.default.dim(" " + "\u2500".repeat(70)));
|
|
27687
27863
|
console.log("");
|
|
27688
27864
|
}
|
|
27689
27865
|
function renderList(summaries, totalCost) {
|
|
27690
27866
|
if (summaries.length === 0) {
|
|
27691
|
-
console.log(
|
|
27867
|
+
console.log(import_chalk30.default.yellow(" No sessions found in the requested range.\n"));
|
|
27692
27868
|
return;
|
|
27693
27869
|
}
|
|
27694
|
-
const totalLabel = totalCost > 0 ?
|
|
27870
|
+
const totalLabel = totalCost > 0 ? import_chalk30.default.dim(" ~" + fmtCost3(totalCost) + " total") : "";
|
|
27695
27871
|
console.log(
|
|
27696
|
-
" " +
|
|
27872
|
+
" " + import_chalk30.default.white(String(summaries.length)) + import_chalk30.default.dim(` session${summaries.length !== 1 ? "s" : ""}`) + totalLabel
|
|
27697
27873
|
);
|
|
27698
27874
|
console.log("");
|
|
27699
27875
|
let lastGroup = "";
|
|
@@ -27701,51 +27877,51 @@ function renderList(summaries, totalCost) {
|
|
|
27701
27877
|
const activeDate = fmtDate2(s.lastActiveTime);
|
|
27702
27878
|
const group = activeDate + " " + s.projectLabel;
|
|
27703
27879
|
if (group !== lastGroup) {
|
|
27704
|
-
console.log(
|
|
27880
|
+
console.log(import_chalk30.default.dim(" \u2500\u2500\u2500 ") + import_chalk30.default.bold(activeDate) + import_chalk30.default.dim(" " + s.projectLabel));
|
|
27705
27881
|
lastGroup = group;
|
|
27706
27882
|
}
|
|
27707
27883
|
const startDate = fmtDate2(s.startTime);
|
|
27708
|
-
const dateRange = startDate !== activeDate ?
|
|
27709
|
-
const timeStr =
|
|
27710
|
-
const prompt =
|
|
27711
|
-
const tools = s.toolCalls.length > 0 ?
|
|
27712
|
-
const cost = s.costUSD > 0 ?
|
|
27713
|
-
const blocked = s.blockedCalls.length > 0 ?
|
|
27714
|
-
const snap = s.hasSnapshot ?
|
|
27715
|
-
const agentBadge =
|
|
27884
|
+
const dateRange = startDate !== activeDate ? import_chalk30.default.dim(" (" + startDate + " \u2192 " + activeDate + ")") : "";
|
|
27885
|
+
const timeStr = import_chalk30.default.dim(fmtTime(s.startTime));
|
|
27886
|
+
const prompt = import_chalk30.default.white(truncate(s.firstPrompt.replace(/\n/g, " "), 50).padEnd(50));
|
|
27887
|
+
const tools = s.toolCalls.length > 0 ? import_chalk30.default.dim(String(s.toolCalls.length).padStart(3) + " tools") : import_chalk30.default.dim(" 0 tools");
|
|
27888
|
+
const cost = s.costUSD > 0 ? import_chalk30.default.dim(" " + fmtCost3(s.costUSD).padEnd(8)) : " ";
|
|
27889
|
+
const blocked = s.blockedCalls.length > 0 ? import_chalk30.default.red(" \u{1F6D1} " + String(s.blockedCalls.length)) : "";
|
|
27890
|
+
const snap = s.hasSnapshot ? import_chalk30.default.green(" \u{1F4F8}") : "";
|
|
27891
|
+
const agentBadge = import_chalk30.default[agentColorName(s.agent ?? "claude")](
|
|
27716
27892
|
" " + agentBadgeText(s.agent ?? "claude", 0)
|
|
27717
27893
|
);
|
|
27718
|
-
const sid =
|
|
27894
|
+
const sid = import_chalk30.default.dim(" " + s.sessionId.slice(0, 8));
|
|
27719
27895
|
console.log(
|
|
27720
27896
|
` ${timeStr} ${prompt} ${tools}${cost}${blocked}${snap}${agentBadge}${sid}${dateRange}`
|
|
27721
27897
|
);
|
|
27722
27898
|
}
|
|
27723
27899
|
console.log("");
|
|
27724
27900
|
console.log(
|
|
27725
|
-
|
|
27901
|
+
import_chalk30.default.dim(" Run") + " " + import_chalk30.default.cyan("node9 sessions --detail <session-id>") + import_chalk30.default.dim(" for full tool trace.")
|
|
27726
27902
|
);
|
|
27727
27903
|
console.log("");
|
|
27728
27904
|
}
|
|
27729
27905
|
function renderDetail(s) {
|
|
27730
27906
|
console.log("");
|
|
27731
|
-
console.log(
|
|
27907
|
+
console.log(import_chalk30.default.bold(" Session ") + import_chalk30.default.dim(s.sessionId));
|
|
27732
27908
|
console.log(
|
|
27733
|
-
|
|
27909
|
+
import_chalk30.default.bold(" Prompt ") + import_chalk30.default.white(s.firstPrompt.replace(/\n/g, " ").slice(0, 120))
|
|
27734
27910
|
);
|
|
27735
|
-
console.log(
|
|
27911
|
+
console.log(import_chalk30.default.bold(" Project ") + import_chalk30.default.white(s.projectLabel));
|
|
27736
27912
|
if (s.agent) {
|
|
27737
|
-
const agentLabel2 =
|
|
27738
|
-
console.log(
|
|
27913
|
+
const agentLabel2 = import_chalk30.default[agentColorName(s.agent)](agentDisplayName(s.agent));
|
|
27914
|
+
console.log(import_chalk30.default.bold(" Agent ") + agentLabel2);
|
|
27739
27915
|
}
|
|
27740
|
-
console.log(
|
|
27916
|
+
console.log(import_chalk30.default.bold(" When ") + import_chalk30.default.white(fmtDateTime(s.startTime)));
|
|
27741
27917
|
if (s.costUSD > 0)
|
|
27742
|
-
console.log(
|
|
27918
|
+
console.log(import_chalk30.default.bold(" Cost ") + import_chalk30.default.yellow("~" + fmtCost3(s.costUSD)));
|
|
27743
27919
|
console.log(
|
|
27744
|
-
|
|
27920
|
+
import_chalk30.default.bold(" Snapshot ") + (s.hasSnapshot ? import_chalk30.default.green("\u2713 taken") : import_chalk30.default.dim("none"))
|
|
27745
27921
|
);
|
|
27746
27922
|
console.log("");
|
|
27747
27923
|
if (s.toolCalls.length === 0 && s.blockedCalls.length === 0) {
|
|
27748
|
-
console.log(
|
|
27924
|
+
console.log(import_chalk30.default.dim(" No tool calls recorded.\n"));
|
|
27749
27925
|
return;
|
|
27750
27926
|
}
|
|
27751
27927
|
const timeline = [
|
|
@@ -27758,32 +27934,32 @@ function renderDetail(s) {
|
|
|
27758
27934
|
});
|
|
27759
27935
|
const headerParts = [`Tool calls (${s.toolCalls.length})`];
|
|
27760
27936
|
if (s.blockedCalls.length > 0)
|
|
27761
|
-
headerParts.push(
|
|
27762
|
-
console.log(
|
|
27937
|
+
headerParts.push(import_chalk30.default.red(`${s.blockedCalls.length} blocked by node9`));
|
|
27938
|
+
console.log(import_chalk30.default.bold(" " + headerParts.join(" \xB7 ")));
|
|
27763
27939
|
console.log("");
|
|
27764
27940
|
for (const entry of timeline) {
|
|
27765
27941
|
if (entry.kind === "tool") {
|
|
27766
27942
|
const tc = entry.tc;
|
|
27767
27943
|
const colorFn = toolColor(tc.tool);
|
|
27768
27944
|
const toolPad = colorFn(tc.tool.padEnd(16));
|
|
27769
|
-
const detail =
|
|
27770
|
-
const ts = tc.timestamp ?
|
|
27945
|
+
const detail = import_chalk30.default.gray(truncate(toolInputSummary(tc.tool, tc.input), 70));
|
|
27946
|
+
const ts = tc.timestamp ? import_chalk30.default.dim(fmtTime(tc.timestamp) + " ") : " ";
|
|
27771
27947
|
console.log(` ${ts}${toolPad} ${detail}`);
|
|
27772
27948
|
} else {
|
|
27773
27949
|
const bc = entry.bc;
|
|
27774
|
-
const ts = bc.timestamp ?
|
|
27775
|
-
const label2 =
|
|
27776
|
-
const toolName =
|
|
27777
|
-
const argsSummary = bc.args ?
|
|
27778
|
-
const reason = bc.checkedBy ?
|
|
27950
|
+
const ts = bc.timestamp ? import_chalk30.default.dim(fmtTime(bc.timestamp) + " ") : " ";
|
|
27951
|
+
const label2 = import_chalk30.default.red("\u{1F6D1} BLOCKED".padEnd(16));
|
|
27952
|
+
const toolName = import_chalk30.default.red(bc.tool.padEnd(10));
|
|
27953
|
+
const argsSummary = bc.args ? import_chalk30.default.gray(truncate(toolInputSummary(bc.tool, bc.args), 40)) : import_chalk30.default.dim("[args not logged]");
|
|
27954
|
+
const reason = bc.checkedBy ? import_chalk30.default.dim(" \u2190 " + bc.checkedBy) : "";
|
|
27779
27955
|
console.log(` ${ts}${label2} ${toolName} ${argsSummary}${reason}`);
|
|
27780
27956
|
}
|
|
27781
27957
|
}
|
|
27782
27958
|
console.log("");
|
|
27783
27959
|
if (s.modifiedFiles.length > 0) {
|
|
27784
|
-
console.log(
|
|
27960
|
+
console.log(import_chalk30.default.bold(` Files modified (${s.modifiedFiles.length}):`));
|
|
27785
27961
|
for (const f of s.modifiedFiles) {
|
|
27786
|
-
console.log(" " +
|
|
27962
|
+
console.log(" " + import_chalk30.default.yellow(f));
|
|
27787
27963
|
}
|
|
27788
27964
|
console.log("");
|
|
27789
27965
|
}
|
|
@@ -27791,13 +27967,13 @@ function renderDetail(s) {
|
|
|
27791
27967
|
function registerSessionsCommand(program2) {
|
|
27792
27968
|
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) => {
|
|
27793
27969
|
console.log("");
|
|
27794
|
-
console.log(
|
|
27970
|
+
console.log(import_chalk30.default.cyan.bold("\u{1F4CB} node9 sessions") + import_chalk30.default.dim(" \u2014 what your AI agent did"));
|
|
27795
27971
|
console.log("");
|
|
27796
27972
|
const days = options.detail || options.all ? null : Math.max(1, parseInt(options.days, 10) || 7);
|
|
27797
27973
|
const rangeLabel = options.detail ? "all time" : options.all ? "all time" : `last ${String(days)} days`;
|
|
27798
|
-
console.log(
|
|
27974
|
+
console.log(import_chalk30.default.dim(" " + rangeLabel));
|
|
27799
27975
|
console.log("");
|
|
27800
|
-
process.stdout.write(
|
|
27976
|
+
process.stdout.write(import_chalk30.default.dim(" Loading\u2026"));
|
|
27801
27977
|
const summaries = buildSessions(days);
|
|
27802
27978
|
if (process.stdout.isTTY) {
|
|
27803
27979
|
process.stdout.clearLine(0);
|
|
@@ -27810,8 +27986,8 @@ function registerSessionsCommand(program2) {
|
|
|
27810
27986
|
(s) => s.sessionId === options.detail || s.sessionId.startsWith(options.detail)
|
|
27811
27987
|
);
|
|
27812
27988
|
if (!target) {
|
|
27813
|
-
console.log(
|
|
27814
|
-
console.log(
|
|
27989
|
+
console.log(import_chalk30.default.red(` Session not found: ${options.detail}`));
|
|
27990
|
+
console.log(import_chalk30.default.dim(" Run `node9 sessions` to list recent sessions.\n"));
|
|
27815
27991
|
return;
|
|
27816
27992
|
}
|
|
27817
27993
|
renderDetail(target);
|
|
@@ -27824,7 +28000,7 @@ function registerSessionsCommand(program2) {
|
|
|
27824
28000
|
}
|
|
27825
28001
|
|
|
27826
28002
|
// src/cli/commands/session-taint.ts
|
|
27827
|
-
var
|
|
28003
|
+
var import_chalk31 = __toESM(require("chalk"));
|
|
27828
28004
|
init_daemon();
|
|
27829
28005
|
function resolveSessionId(records, query) {
|
|
27830
28006
|
const exact = records.find((r) => r.sessionId === query);
|
|
@@ -27851,22 +28027,22 @@ function registerSessionTaintCommand(program2) {
|
|
|
27851
28027
|
const records = await listSessionTaints();
|
|
27852
28028
|
console.log("");
|
|
27853
28029
|
if (records.length === 0) {
|
|
27854
|
-
console.log(
|
|
27855
|
-
console.log(
|
|
28030
|
+
console.log(import_chalk31.default.dim(" No tainted sessions."));
|
|
28031
|
+
console.log(import_chalk31.default.dim(" (Taint lives in the daemon \u2014 a stopped daemon has none.)") + "\n");
|
|
27856
28032
|
return;
|
|
27857
28033
|
}
|
|
27858
28034
|
console.log(
|
|
27859
|
-
" " +
|
|
28035
|
+
" " + import_chalk31.default.bold(String(records.length)) + import_chalk31.default.dim(` tainted session${records.length !== 1 ? "s" : ""}`)
|
|
27860
28036
|
);
|
|
27861
28037
|
console.log("");
|
|
27862
28038
|
for (const r of records) {
|
|
27863
28039
|
console.log(
|
|
27864
|
-
" " +
|
|
28040
|
+
" " + import_chalk31.default.yellow(r.sessionId.slice(0, 8).padEnd(10)) + import_chalk31.default.red(r.source) + sourceGap(r.source) + import_chalk31.default.dim("clears in " + fmtRemaining(r.expiresAt))
|
|
27865
28041
|
);
|
|
27866
28042
|
}
|
|
27867
28043
|
console.log("");
|
|
27868
28044
|
console.log(
|
|
27869
|
-
|
|
28045
|
+
import_chalk31.default.dim(" Run ") + import_chalk31.default.cyan("node9 session-taint clear <id>") + import_chalk31.default.dim(" to release one, or ") + import_chalk31.default.cyan("--all") + import_chalk31.default.dim(" for every session.") + "\n"
|
|
27870
28046
|
);
|
|
27871
28047
|
});
|
|
27872
28048
|
cmd.command("clear").description("Clear a session's taint so its next network/write action isn't held for review").argument("[sessionId]", "Session id to clear (the 8-char prefix from `list` is accepted)").option("--all", "Clear every session taint").action(async (sessionId, opts) => {
|
|
@@ -27874,32 +28050,32 @@ function registerSessionTaintCommand(program2) {
|
|
|
27874
28050
|
if (opts.all) {
|
|
27875
28051
|
const res2 = await clearSessionTaint({ all: true });
|
|
27876
28052
|
if (res2.daemonUnavailable) {
|
|
27877
|
-
console.log(
|
|
28053
|
+
console.log(import_chalk31.default.dim(" node9 daemon not running \u2014 no active taints to clear.") + "\n");
|
|
27878
28054
|
return;
|
|
27879
28055
|
}
|
|
27880
28056
|
console.log(
|
|
27881
|
-
|
|
28057
|
+
import_chalk31.default.green(" \u2713 ") + `Cleared ${import_chalk31.default.bold(String(res2.cleared))} session taint${res2.cleared !== 1 ? "s" : ""}.
|
|
27882
28058
|
`
|
|
27883
28059
|
);
|
|
27884
28060
|
return;
|
|
27885
28061
|
}
|
|
27886
28062
|
if (!sessionId) {
|
|
27887
|
-
console.log(
|
|
27888
|
-
console.log(
|
|
28063
|
+
console.log(import_chalk31.default.red(" Provide a session id or --all."));
|
|
28064
|
+
console.log(import_chalk31.default.dim(" Run `node9 session-taint list` to see tainted sessions.") + "\n");
|
|
27889
28065
|
return;
|
|
27890
28066
|
}
|
|
27891
28067
|
const records = await listSessionTaints();
|
|
27892
28068
|
if (records.length === 0) {
|
|
27893
|
-
console.log(
|
|
28069
|
+
console.log(import_chalk31.default.dim(" No tainted sessions \u2014 nothing to clear.") + "\n");
|
|
27894
28070
|
return;
|
|
27895
28071
|
}
|
|
27896
28072
|
const resolved = resolveSessionId(records, sessionId);
|
|
27897
28073
|
if ("error" in resolved) {
|
|
27898
28074
|
if (resolved.error === "not-found") {
|
|
27899
|
-
console.log(
|
|
28075
|
+
console.log(import_chalk31.default.red(` No tainted session matches "${sessionId}".`));
|
|
27900
28076
|
} else {
|
|
27901
|
-
console.log(
|
|
27902
|
-
for (const m of resolved.matches) console.log(
|
|
28077
|
+
console.log(import_chalk31.default.red(` "${sessionId}" is ambiguous \u2014 matches:`));
|
|
28078
|
+
for (const m of resolved.matches) console.log(import_chalk31.default.dim(" " + m));
|
|
27903
28079
|
}
|
|
27904
28080
|
console.log("");
|
|
27905
28081
|
return;
|
|
@@ -27907,18 +28083,18 @@ function registerSessionTaintCommand(program2) {
|
|
|
27907
28083
|
const res = await clearSessionTaint({ sessionId: resolved.record.sessionId });
|
|
27908
28084
|
if (res.cleared > 0) {
|
|
27909
28085
|
console.log(
|
|
27910
|
-
|
|
28086
|
+
import_chalk31.default.green(" \u2713 ") + `Cleared taint for ${import_chalk31.default.yellow(resolved.record.sessionId.slice(0, 8))} ` + import_chalk31.default.dim(`(was flagged by ${resolved.record.source}).`) + "\n"
|
|
27911
28087
|
);
|
|
27912
28088
|
} else {
|
|
27913
28089
|
console.log(
|
|
27914
|
-
|
|
28090
|
+
import_chalk31.default.dim(` Session ${resolved.record.sessionId.slice(0, 8)} was already clear.`) + "\n"
|
|
27915
28091
|
);
|
|
27916
28092
|
}
|
|
27917
28093
|
});
|
|
27918
28094
|
}
|
|
27919
28095
|
|
|
27920
28096
|
// src/cli/commands/skill-pin.ts
|
|
27921
|
-
var
|
|
28097
|
+
var import_chalk32 = __toESM(require("chalk"));
|
|
27922
28098
|
var import_fs60 = __toESM(require("fs"));
|
|
27923
28099
|
var import_os51 = __toESM(require("os"));
|
|
27924
28100
|
var import_path58 = __toESM(require("path"));
|
|
@@ -27938,29 +28114,29 @@ function registerSkillPinCommand(program2) {
|
|
|
27938
28114
|
const result = readSkillPinsSafe();
|
|
27939
28115
|
if (!result.ok) {
|
|
27940
28116
|
if (result.reason === "missing") {
|
|
27941
|
-
console.log(
|
|
28117
|
+
console.log(import_chalk32.default.gray("\nNo skill roots are pinned yet."));
|
|
27942
28118
|
console.log(
|
|
27943
|
-
|
|
28119
|
+
import_chalk32.default.gray("Pins are created automatically on the first tool call of each session.\n")
|
|
27944
28120
|
);
|
|
27945
28121
|
return;
|
|
27946
28122
|
}
|
|
27947
|
-
console.error(
|
|
28123
|
+
console.error(import_chalk32.default.red(`
|
|
27948
28124
|
\u274C Pin file is corrupt: ${result.detail}`));
|
|
27949
|
-
console.error(
|
|
28125
|
+
console.error(import_chalk32.default.yellow(" Run: node9 skill pin reset\n"));
|
|
27950
28126
|
process.exit(1);
|
|
27951
28127
|
}
|
|
27952
28128
|
const entries = Object.entries(result.pins.roots);
|
|
27953
28129
|
if (entries.length === 0) {
|
|
27954
|
-
console.log(
|
|
28130
|
+
console.log(import_chalk32.default.gray("\nNo skill roots are pinned yet.\n"));
|
|
27955
28131
|
return;
|
|
27956
28132
|
}
|
|
27957
|
-
console.log(
|
|
28133
|
+
console.log(import_chalk32.default.bold("\n\u{1F512} Pinned Skill Roots\n"));
|
|
27958
28134
|
for (const [key, entry] of entries) {
|
|
27959
|
-
const missing = entry.exists ? "" :
|
|
27960
|
-
console.log(` ${
|
|
28135
|
+
const missing = entry.exists ? "" : import_chalk32.default.yellow(" (not present at pin time)");
|
|
28136
|
+
console.log(` ${import_chalk32.default.cyan(key)} ${import_chalk32.default.gray(entry.rootPath)}${missing}`);
|
|
27961
28137
|
console.log(` Files (${entry.fileCount})`);
|
|
27962
|
-
console.log(` Hash: ${
|
|
27963
|
-
console.log(` Pinned: ${
|
|
28138
|
+
console.log(` Hash: ${import_chalk32.default.gray(entry.contentHash.slice(0, 16))}...`);
|
|
28139
|
+
console.log(` Pinned: ${import_chalk32.default.gray(entry.pinnedAt)}
|
|
27964
28140
|
`);
|
|
27965
28141
|
}
|
|
27966
28142
|
});
|
|
@@ -27969,39 +28145,39 @@ function registerSkillPinCommand(program2) {
|
|
|
27969
28145
|
try {
|
|
27970
28146
|
pins = readSkillPins();
|
|
27971
28147
|
} catch {
|
|
27972
|
-
console.error(
|
|
27973
|
-
console.error(
|
|
28148
|
+
console.error(import_chalk32.default.red("\n\u274C Pin file is corrupt."));
|
|
28149
|
+
console.error(import_chalk32.default.yellow(" Run: node9 skill pin reset\n"));
|
|
27974
28150
|
process.exit(1);
|
|
27975
28151
|
}
|
|
27976
28152
|
if (!pins.roots[rootKey]) {
|
|
27977
|
-
console.error(
|
|
28153
|
+
console.error(import_chalk32.default.red(`
|
|
27978
28154
|
\u274C No pin found for root key "${rootKey}"
|
|
27979
28155
|
`));
|
|
27980
|
-
console.error(`Run ${
|
|
28156
|
+
console.error(`Run ${import_chalk32.default.cyan("node9 skill pin list")} to see pinned roots.
|
|
27981
28157
|
`);
|
|
27982
28158
|
process.exit(1);
|
|
27983
28159
|
}
|
|
27984
28160
|
const rootPath = pins.roots[rootKey].rootPath;
|
|
27985
28161
|
removePin2(rootKey);
|
|
27986
28162
|
wipeSkillSessions();
|
|
27987
|
-
console.log(
|
|
27988
|
-
\u{1F513} Pin removed for ${
|
|
27989
|
-
console.log(
|
|
27990
|
-
console.log(
|
|
28163
|
+
console.log(import_chalk32.default.green(`
|
|
28164
|
+
\u{1F513} Pin removed for ${import_chalk32.default.cyan(rootKey)}`));
|
|
28165
|
+
console.log(import_chalk32.default.gray(` ${rootPath}`));
|
|
28166
|
+
console.log(import_chalk32.default.gray(" Next session will re-pin with current state.\n"));
|
|
27991
28167
|
});
|
|
27992
28168
|
pinSubCmd.command("reset").description("Clear all skill pins and wipe session verification flags").action(() => {
|
|
27993
28169
|
const result = readSkillPinsSafe();
|
|
27994
28170
|
if (!result.ok && result.reason === "missing") {
|
|
27995
28171
|
wipeSkillSessions();
|
|
27996
|
-
console.log(
|
|
28172
|
+
console.log(import_chalk32.default.gray("\nNo pins to clear.\n"));
|
|
27997
28173
|
return;
|
|
27998
28174
|
}
|
|
27999
28175
|
const count = result.ok ? Object.keys(result.pins.roots).length : "?";
|
|
28000
28176
|
clearAllPins2();
|
|
28001
28177
|
wipeSkillSessions();
|
|
28002
|
-
console.log(
|
|
28178
|
+
console.log(import_chalk32.default.green(`
|
|
28003
28179
|
\u{1F513} Cleared ${count} skill pin(s).`));
|
|
28004
|
-
console.log(
|
|
28180
|
+
console.log(import_chalk32.default.gray(" Next session will re-pin with current state.\n"));
|
|
28005
28181
|
});
|
|
28006
28182
|
}
|
|
28007
28183
|
|
|
@@ -28009,7 +28185,7 @@ function registerSkillPinCommand(program2) {
|
|
|
28009
28185
|
var import_fs61 = __toESM(require("fs"));
|
|
28010
28186
|
var import_os52 = __toESM(require("os"));
|
|
28011
28187
|
var import_path59 = __toESM(require("path"));
|
|
28012
|
-
var
|
|
28188
|
+
var import_chalk33 = __toESM(require("chalk"));
|
|
28013
28189
|
var DECISIONS_FILE2 = import_path59.default.join(import_os52.default.homedir(), ".node9", "decisions.json");
|
|
28014
28190
|
function readDecisions() {
|
|
28015
28191
|
try {
|
|
@@ -28038,55 +28214,55 @@ function registerDecisionsCommand(program2) {
|
|
|
28038
28214
|
const decisions = readDecisions();
|
|
28039
28215
|
const entries = Object.entries(decisions);
|
|
28040
28216
|
if (entries.length === 0) {
|
|
28041
|
-
console.log(
|
|
28217
|
+
console.log(import_chalk33.default.gray(" No persistent decisions stored."));
|
|
28042
28218
|
console.log(
|
|
28043
|
-
|
|
28044
|
-
`) +
|
|
28219
|
+
import_chalk33.default.gray(` File: ${DECISIONS_FILE2}
|
|
28220
|
+
`) + import_chalk33.default.gray(' Decisions are written when you click "Always Allow" or')
|
|
28045
28221
|
);
|
|
28046
|
-
console.log(
|
|
28222
|
+
console.log(import_chalk33.default.gray(' "Always Deny" in node9 tail or the native popup.'));
|
|
28047
28223
|
return;
|
|
28048
28224
|
}
|
|
28049
|
-
console.log(
|
|
28225
|
+
console.log(import_chalk33.default.bold(`
|
|
28050
28226
|
Persistent decisions (${entries.length})
|
|
28051
28227
|
`));
|
|
28052
28228
|
const w = Math.max(...entries.map(([k]) => k.length));
|
|
28053
28229
|
for (const [tool, verdict] of entries.sort()) {
|
|
28054
|
-
const colored = verdict === "allow" ?
|
|
28230
|
+
const colored = verdict === "allow" ? import_chalk33.default.green(verdict) : import_chalk33.default.red(verdict);
|
|
28055
28231
|
console.log(` ${tool.padEnd(w)} ${colored}`);
|
|
28056
28232
|
}
|
|
28057
28233
|
console.log(
|
|
28058
|
-
|
|
28234
|
+
import_chalk33.default.gray(`
|
|
28059
28235
|
Stored in ${DECISIONS_FILE2}
|
|
28060
|
-
`) +
|
|
28236
|
+
`) + import_chalk33.default.gray(" Run `node9 decisions clear <tool>` to remove an entry.")
|
|
28061
28237
|
);
|
|
28062
28238
|
});
|
|
28063
28239
|
cmd.command("clear <toolName>").description("Remove a persistent decision for one tool").action((toolName) => {
|
|
28064
28240
|
const decisions = readDecisions();
|
|
28065
28241
|
if (!(toolName in decisions)) {
|
|
28066
|
-
console.log(
|
|
28242
|
+
console.log(import_chalk33.default.yellow(` No persistent decision for "${toolName}". Nothing to clear.`));
|
|
28067
28243
|
process.exitCode = 1;
|
|
28068
28244
|
return;
|
|
28069
28245
|
}
|
|
28070
28246
|
delete decisions[toolName];
|
|
28071
28247
|
writeDecisions(decisions);
|
|
28072
|
-
console.log(
|
|
28248
|
+
console.log(import_chalk33.default.green(` \u2713 Cleared persistent decision for "${toolName}".`));
|
|
28073
28249
|
});
|
|
28074
28250
|
cmd.command("clear-all").description("Remove every persistent decision (irreversible)").action(() => {
|
|
28075
28251
|
const decisions = readDecisions();
|
|
28076
28252
|
const count = Object.keys(decisions).length;
|
|
28077
28253
|
if (count === 0) {
|
|
28078
|
-
console.log(
|
|
28254
|
+
console.log(import_chalk33.default.gray(" Nothing to clear \u2014 no persistent decisions stored."));
|
|
28079
28255
|
return;
|
|
28080
28256
|
}
|
|
28081
28257
|
writeDecisions({});
|
|
28082
28258
|
console.log(
|
|
28083
|
-
|
|
28259
|
+
import_chalk33.default.green(` \u2713 Cleared ${count} persistent decision${count === 1 ? "" : "s"}.`)
|
|
28084
28260
|
);
|
|
28085
28261
|
});
|
|
28086
28262
|
}
|
|
28087
28263
|
|
|
28088
28264
|
// src/cli/commands/dlp.ts
|
|
28089
|
-
var
|
|
28265
|
+
var import_chalk34 = __toESM(require("chalk"));
|
|
28090
28266
|
var import_fs62 = __toESM(require("fs"));
|
|
28091
28267
|
var import_path60 = __toESM(require("path"));
|
|
28092
28268
|
var import_os53 = __toESM(require("os"));
|
|
@@ -28141,14 +28317,14 @@ function registerDlpCommand(program2) {
|
|
|
28141
28317
|
cmd.command("resolve").description("Mark all current DLP findings as resolved").action(() => {
|
|
28142
28318
|
const findings = loadDlpFindings();
|
|
28143
28319
|
if (findings.length === 0) {
|
|
28144
|
-
console.log(
|
|
28320
|
+
console.log(import_chalk34.default.green("\n \u2705 No response-DLP findings to resolve.\n"));
|
|
28145
28321
|
return;
|
|
28146
28322
|
}
|
|
28147
28323
|
const resolved = loadResolved();
|
|
28148
28324
|
for (const e of findings) resolved.add(entryKey2(e));
|
|
28149
28325
|
saveResolved(resolved);
|
|
28150
28326
|
console.log(
|
|
28151
|
-
|
|
28327
|
+
import_chalk34.default.green(
|
|
28152
28328
|
`
|
|
28153
28329
|
\u2705 ${findings.length} finding${findings.length !== 1 ? "s" : ""} marked as resolved.
|
|
28154
28330
|
`
|
|
@@ -28162,54 +28338,54 @@ function registerDlpCommand(program2) {
|
|
|
28162
28338
|
const resolvedCount = findings.length - open.length;
|
|
28163
28339
|
console.log("");
|
|
28164
28340
|
console.log(
|
|
28165
|
-
|
|
28341
|
+
import_chalk34.default.bold.cyan("\u{1F510} node9 dlp") + import_chalk34.default.dim(" \u2014 secrets found in Claude response text")
|
|
28166
28342
|
);
|
|
28167
28343
|
console.log("");
|
|
28168
28344
|
if (open.length === 0) {
|
|
28169
28345
|
if (resolvedCount > 0) {
|
|
28170
|
-
console.log(
|
|
28346
|
+
console.log(import_chalk34.default.green(` \u2705 No open findings \xB7 ${resolvedCount} previously resolved`));
|
|
28171
28347
|
} else {
|
|
28172
28348
|
console.log(
|
|
28173
|
-
|
|
28349
|
+
import_chalk34.default.green(" \u2705 No findings \u2014 Claude has not leaked secrets in response text")
|
|
28174
28350
|
);
|
|
28175
28351
|
}
|
|
28176
28352
|
console.log("");
|
|
28177
28353
|
return;
|
|
28178
28354
|
}
|
|
28179
28355
|
console.log(
|
|
28180
|
-
|
|
28356
|
+
import_chalk34.default.bgRed.white.bold(` \u26A0\uFE0F ${open.length} open finding${open.length !== 1 ? "s" : ""} `) + import_chalk34.default.dim(resolvedCount > 0 ? ` (${resolvedCount} resolved)` : "")
|
|
28181
28357
|
);
|
|
28182
28358
|
console.log("");
|
|
28183
28359
|
console.log(
|
|
28184
|
-
|
|
28360
|
+
import_chalk34.default.dim(" These secrets were included in Claude's response text \u2014 NOT blocked.")
|
|
28185
28361
|
);
|
|
28186
|
-
console.log(
|
|
28362
|
+
console.log(import_chalk34.default.dim(" Rotate each affected key immediately.\n"));
|
|
28187
28363
|
for (const e of open) {
|
|
28188
28364
|
console.log(
|
|
28189
|
-
" " +
|
|
28365
|
+
" " + import_chalk34.default.red("\u25CF") + " " + import_chalk34.default.white(e.dlpPattern ?? "Secret") + import_chalk34.default.dim(" " + fmtDate3(e.ts))
|
|
28190
28366
|
);
|
|
28191
28367
|
if (e.dlpSample) {
|
|
28192
|
-
console.log(" " +
|
|
28368
|
+
console.log(" " + import_chalk34.default.dim("Sample: ") + import_chalk34.default.yellow(stripAnsi(e.dlpSample)));
|
|
28193
28369
|
}
|
|
28194
28370
|
if (e.project) {
|
|
28195
|
-
console.log(" " +
|
|
28371
|
+
console.log(" " + import_chalk34.default.dim("Project: ") + import_chalk34.default.dim(stripAnsi(e.project)));
|
|
28196
28372
|
}
|
|
28197
28373
|
console.log("");
|
|
28198
28374
|
}
|
|
28199
|
-
console.log(" " +
|
|
28200
|
-
console.log(" " +
|
|
28375
|
+
console.log(" " + import_chalk34.default.bold("Next steps:"));
|
|
28376
|
+
console.log(" " + import_chalk34.default.cyan("1.") + " Rotate any exposed keys shown above");
|
|
28201
28377
|
console.log(
|
|
28202
|
-
" " +
|
|
28378
|
+
" " + import_chalk34.default.cyan("2.") + " Run " + import_chalk34.default.white("node9 dlp resolve") + " to acknowledge"
|
|
28203
28379
|
);
|
|
28204
28380
|
console.log(
|
|
28205
|
-
" " +
|
|
28381
|
+
" " + import_chalk34.default.cyan("3.") + " Run " + import_chalk34.default.white("node9 report") + " for full audit history"
|
|
28206
28382
|
);
|
|
28207
28383
|
console.log("");
|
|
28208
28384
|
});
|
|
28209
28385
|
}
|
|
28210
28386
|
|
|
28211
28387
|
// src/cli/commands/mask.ts
|
|
28212
|
-
var
|
|
28388
|
+
var import_chalk35 = __toESM(require("chalk"));
|
|
28213
28389
|
var import_fs63 = __toESM(require("fs"));
|
|
28214
28390
|
var import_path61 = __toESM(require("path"));
|
|
28215
28391
|
var import_os54 = __toESM(require("os"));
|
|
@@ -28346,12 +28522,12 @@ function registerMaskCommand(program2) {
|
|
|
28346
28522
|
}
|
|
28347
28523
|
}) : allFiles;
|
|
28348
28524
|
if (filtered.length === 0) {
|
|
28349
|
-
console.log(
|
|
28525
|
+
console.log(import_chalk35.default.yellow(" No session files found."));
|
|
28350
28526
|
return;
|
|
28351
28527
|
}
|
|
28352
28528
|
console.log("");
|
|
28353
28529
|
if (dryRun) {
|
|
28354
|
-
console.log(
|
|
28530
|
+
console.log(import_chalk35.default.dim(" Dry run \u2014 no files will be modified.\n"));
|
|
28355
28531
|
}
|
|
28356
28532
|
let totalFiles = 0;
|
|
28357
28533
|
let totalLines = 0;
|
|
@@ -28367,23 +28543,23 @@ function registerMaskCommand(program2) {
|
|
|
28367
28543
|
});
|
|
28368
28544
|
const verb = dryRun ? "Would redact" : "Redacted";
|
|
28369
28545
|
console.log(
|
|
28370
|
-
" " +
|
|
28546
|
+
" " + import_chalk35.default.dim(shortPath.slice(0, 60).padEnd(62)) + import_chalk35.default.red(`${verb}: `) + import_chalk35.default.yellow(patterns.join(", ")) + import_chalk35.default.dim(` (${redactedLines} line${redactedLines !== 1 ? "s" : ""})`)
|
|
28371
28547
|
);
|
|
28372
28548
|
}
|
|
28373
28549
|
}
|
|
28374
28550
|
console.log("");
|
|
28375
28551
|
if (totalFiles === 0) {
|
|
28376
|
-
console.log(
|
|
28552
|
+
console.log(import_chalk35.default.green(" No secrets found in session history."));
|
|
28377
28553
|
} else {
|
|
28378
28554
|
const verb = dryRun ? "would be modified" : "modified";
|
|
28379
28555
|
console.log(
|
|
28380
|
-
|
|
28556
|
+
import_chalk35.default.bold(` ${totalFiles} file${totalFiles !== 1 ? "s" : ""} ${verb}`) + import_chalk35.default.dim(`, ${totalLines} line${totalLines !== 1 ? "s" : ""} redacted`)
|
|
28381
28557
|
);
|
|
28382
|
-
console.log(" Patterns: " +
|
|
28558
|
+
console.log(" Patterns: " + import_chalk35.default.yellow(totalPatterns.join(", ")));
|
|
28383
28559
|
if (!dryRun) {
|
|
28384
28560
|
console.log("");
|
|
28385
28561
|
console.log(
|
|
28386
|
-
|
|
28562
|
+
import_chalk35.default.dim(
|
|
28387
28563
|
" Note: secrets were already sent to the AI provider during the active session.\n This cleans your local disk only. Rotate any exposed keys."
|
|
28388
28564
|
)
|
|
28389
28565
|
);
|
|
@@ -28401,77 +28577,34 @@ var { version } = JSON.parse(
|
|
|
28401
28577
|
var program = new import_commander.Command();
|
|
28402
28578
|
program.name("node9").description("The Sudo Command for AI Agents").version(version);
|
|
28403
28579
|
program.command("login").argument("<apiKey>").option("--local", "Save key for audit/logging only \u2014 local config still controls all decisions").option("--profile <name>", 'Save as a named profile (default: "default")').action((apiKey, options) => {
|
|
28404
|
-
const
|
|
28405
|
-
|
|
28406
|
-
|
|
28407
|
-
|
|
28408
|
-
const profileName = options.profile || "default";
|
|
28409
|
-
let existingCreds = {};
|
|
28410
|
-
try {
|
|
28411
|
-
if (import_fs66.default.existsSync(credPath)) {
|
|
28412
|
-
const raw = JSON.parse(import_fs66.default.readFileSync(credPath, "utf-8"));
|
|
28413
|
-
if (raw.apiKey) {
|
|
28414
|
-
existingCreds = {
|
|
28415
|
-
default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL2 }
|
|
28416
|
-
};
|
|
28417
|
-
} else {
|
|
28418
|
-
existingCreds = raw;
|
|
28419
|
-
}
|
|
28420
|
-
}
|
|
28421
|
-
} catch {
|
|
28422
|
-
}
|
|
28423
|
-
existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL2 };
|
|
28424
|
-
import_fs66.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
|
|
28425
|
-
let effectiveCloud = null;
|
|
28426
|
-
if (profileName === "default") {
|
|
28427
|
-
const configPath = import_path64.default.join(import_os57.default.homedir(), ".node9", "config.json");
|
|
28428
|
-
let config = {};
|
|
28429
|
-
try {
|
|
28430
|
-
if (import_fs66.default.existsSync(configPath))
|
|
28431
|
-
config = JSON.parse(import_fs66.default.readFileSync(configPath, "utf-8"));
|
|
28432
|
-
} catch {
|
|
28433
|
-
}
|
|
28434
|
-
if (!config.settings || typeof config.settings !== "object") config.settings = {};
|
|
28435
|
-
const s = config.settings;
|
|
28436
|
-
const approvers = s.approvers || {
|
|
28437
|
-
native: true,
|
|
28438
|
-
browser: true,
|
|
28439
|
-
cloud: true,
|
|
28440
|
-
terminal: true
|
|
28441
|
-
};
|
|
28442
|
-
if (options.local) {
|
|
28443
|
-
approvers.cloud = false;
|
|
28444
|
-
}
|
|
28445
|
-
s.approvers = approvers;
|
|
28446
|
-
if (!import_fs66.default.existsSync(import_path64.default.dirname(configPath)))
|
|
28447
|
-
import_fs66.default.mkdirSync(import_path64.default.dirname(configPath), { recursive: true });
|
|
28448
|
-
import_fs66.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
28449
|
-
effectiveCloud = approvers.cloud === true;
|
|
28450
|
-
}
|
|
28580
|
+
const { profileName, effectiveCloud } = writeCredentialsAndConfig(apiKey, {
|
|
28581
|
+
profileName: options.profile,
|
|
28582
|
+
isLocal: options.local
|
|
28583
|
+
});
|
|
28451
28584
|
if (options.profile && profileName !== "default") {
|
|
28452
|
-
console.log(
|
|
28453
|
-
console.log(
|
|
28585
|
+
console.log(import_chalk37.default.green(`\u2705 Profile "${profileName}" saved`));
|
|
28586
|
+
console.log(import_chalk37.default.gray(` Switch to it per-session: NODE9_PROFILE=${profileName} claude`));
|
|
28454
28587
|
} else if (options.local || effectiveCloud === false) {
|
|
28455
|
-
console.log(
|
|
28456
|
-
console.log(
|
|
28588
|
+
console.log(import_chalk37.default.green(`\u2705 Key saved \u2014 Privacy mode \u{1F6E1}\uFE0F`));
|
|
28589
|
+
console.log(import_chalk37.default.gray(` All decisions stay on this machine. Nothing syncs to the cloud.`));
|
|
28457
28590
|
if (!options.local) {
|
|
28458
28591
|
console.log(
|
|
28459
|
-
|
|
28592
|
+
import_chalk37.default.yellow(` Your config has cloud approvals OFF (settings.approvers.cloud).`)
|
|
28460
28593
|
);
|
|
28461
28594
|
console.log(
|
|
28462
|
-
|
|
28595
|
+
import_chalk37.default.gray(` To enable team policy + dashboard sync: set it to true, or re-init.`)
|
|
28463
28596
|
);
|
|
28464
28597
|
}
|
|
28465
28598
|
} else {
|
|
28466
|
-
console.log(
|
|
28467
|
-
console.log(
|
|
28599
|
+
console.log(import_chalk37.default.green(`\u2705 Logged in \u2014 agent mode`));
|
|
28600
|
+
console.log(import_chalk37.default.gray(` Team policy enforced for all calls via Node9 cloud.`));
|
|
28468
28601
|
}
|
|
28469
28602
|
});
|
|
28470
28603
|
program.command("signup").description("Create your node9 account / open the dashboard in your browser").option("--login", "Open the login page instead of signup").action((options) => {
|
|
28471
28604
|
const route = options.login ? "auth/login" : "auth/signup";
|
|
28472
28605
|
const url = `https://node9.ai/${route}?ref=cli_cmd`;
|
|
28473
28606
|
console.log("");
|
|
28474
|
-
console.log(" " +
|
|
28607
|
+
console.log(" " + import_chalk37.default.dim("Opening ") + import_chalk37.default.cyan.underline(url));
|
|
28475
28608
|
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
28476
28609
|
try {
|
|
28477
28610
|
const child = (0, import_child_process15.spawn)(opener, [url], {
|
|
@@ -28504,7 +28637,7 @@ program.command("addto", { hidden: true }).description("Integrate Node9 with an
|
|
|
28504
28637
|
if (target === "hermes") return setupHermes();
|
|
28505
28638
|
if (target === "hud") return setupHud();
|
|
28506
28639
|
console.error(
|
|
28507
|
-
|
|
28640
|
+
import_chalk37.default.red(
|
|
28508
28641
|
`Unknown target: "${target}". Supported: claude, antigravity, copilot, gemini, cursor, codex, windsurf, vscode, hermes, hud`
|
|
28509
28642
|
)
|
|
28510
28643
|
);
|
|
@@ -28518,20 +28651,20 @@ program.command("setup", { hidden: true }).description('Alias for "addto" \u2014
|
|
|
28518
28651
|
"The agent to protect: claude | antigravity | copilot | gemini | cursor | codex | windsurf | vscode | hud"
|
|
28519
28652
|
).action(async (target) => {
|
|
28520
28653
|
if (!target) {
|
|
28521
|
-
console.log(
|
|
28522
|
-
console.log(" Usage: " +
|
|
28654
|
+
console.log(import_chalk37.default.cyan("\n\u{1F6E1}\uFE0F Node9 Setup \u2014 integrate with your AI agent\n"));
|
|
28655
|
+
console.log(" Usage: " + import_chalk37.default.white("node9 setup <target>") + "\n");
|
|
28523
28656
|
console.log(" Targets:");
|
|
28524
|
-
console.log(" " +
|
|
28525
|
-
console.log(" " +
|
|
28526
|
-
console.log(" " +
|
|
28527
|
-
console.log(" " +
|
|
28528
|
-
console.log(" " +
|
|
28529
|
-
console.log(" " +
|
|
28530
|
-
console.log(" " +
|
|
28531
|
-
console.log(" " +
|
|
28532
|
-
console.log(" " +
|
|
28657
|
+
console.log(" " + import_chalk37.default.green("claude") + " \u2014 Claude Code (hook mode)");
|
|
28658
|
+
console.log(" " + import_chalk37.default.green("gemini") + " \u2014 Gemini CLI (hook mode)");
|
|
28659
|
+
console.log(" " + import_chalk37.default.green("antigravity") + " \u2014 Antigravity / agy (hook mode)");
|
|
28660
|
+
console.log(" " + import_chalk37.default.green("copilot") + " \u2014 GitHub Copilot CLI (hook mode)");
|
|
28661
|
+
console.log(" " + import_chalk37.default.green("cursor") + " \u2014 Cursor (MCP proxy)");
|
|
28662
|
+
console.log(" " + import_chalk37.default.green("codex") + " \u2014 OpenAI Codex CLI (MCP proxy)");
|
|
28663
|
+
console.log(" " + import_chalk37.default.green("windsurf") + " \u2014 Windsurf (MCP proxy)");
|
|
28664
|
+
console.log(" " + import_chalk37.default.green("vscode") + " \u2014 VSCode / Copilot (MCP proxy)");
|
|
28665
|
+
console.log(" " + import_chalk37.default.green("hermes") + " \u2014 Hermes Agent (hook mode)");
|
|
28533
28666
|
process.stdout.write(
|
|
28534
|
-
" " +
|
|
28667
|
+
" " + import_chalk37.default.green("hud") + " \u2014 Claude Code security statusline\n"
|
|
28535
28668
|
);
|
|
28536
28669
|
console.log("");
|
|
28537
28670
|
return;
|
|
@@ -28548,7 +28681,7 @@ program.command("setup", { hidden: true }).description('Alias for "addto" \u2014
|
|
|
28548
28681
|
if (t === "hermes") return setupHermes();
|
|
28549
28682
|
if (t === "hud") return setupHud();
|
|
28550
28683
|
console.error(
|
|
28551
|
-
|
|
28684
|
+
import_chalk37.default.red(
|
|
28552
28685
|
`Unknown target: "${target}". Supported: claude, antigravity, copilot, gemini, cursor, codex, windsurf, vscode, hermes, hud`
|
|
28553
28686
|
)
|
|
28554
28687
|
);
|
|
@@ -28559,32 +28692,32 @@ program.command("removefrom", { hidden: true }).description("Remove Node9 hooks
|
|
|
28559
28692
|
const agent = resolveAgentTeardown(target);
|
|
28560
28693
|
if (!agent) {
|
|
28561
28694
|
console.error(
|
|
28562
|
-
|
|
28695
|
+
import_chalk37.default.red(`Unknown target: "${target}". Supported: ${agentTeardownTargets().join(", ")}`)
|
|
28563
28696
|
);
|
|
28564
28697
|
process.exit(1);
|
|
28565
28698
|
return;
|
|
28566
28699
|
}
|
|
28567
|
-
console.log(
|
|
28700
|
+
console.log(import_chalk37.default.cyan(`
|
|
28568
28701
|
\u{1F6E1}\uFE0F Node9: removing hooks from ${agent.label}...
|
|
28569
28702
|
`));
|
|
28570
28703
|
try {
|
|
28571
28704
|
agent.fn();
|
|
28572
28705
|
} catch (err2) {
|
|
28573
|
-
console.error(
|
|
28706
|
+
console.error(import_chalk37.default.red(` \u26A0\uFE0F Failed: ${err2 instanceof Error ? err2.message : String(err2)}`));
|
|
28574
28707
|
process.exit(1);
|
|
28575
28708
|
}
|
|
28576
|
-
console.log(
|
|
28709
|
+
console.log(import_chalk37.default.gray("\n Restart the agent for changes to take effect."));
|
|
28577
28710
|
});
|
|
28578
28711
|
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) => {
|
|
28579
|
-
console.log(
|
|
28580
|
-
console.log(
|
|
28712
|
+
console.log(import_chalk37.default.cyan("\n\u{1F6E1}\uFE0F Node9 Uninstall\n"));
|
|
28713
|
+
console.log(import_chalk37.default.bold("Stopping daemon..."));
|
|
28581
28714
|
try {
|
|
28582
28715
|
stopDaemon();
|
|
28583
|
-
console.log(
|
|
28716
|
+
console.log(import_chalk37.default.green(" \u2705 Daemon stopped"));
|
|
28584
28717
|
} catch {
|
|
28585
|
-
console.log(
|
|
28718
|
+
console.log(import_chalk37.default.blue(" \u2139\uFE0F Daemon was not running"));
|
|
28586
28719
|
}
|
|
28587
|
-
console.log(
|
|
28720
|
+
console.log(import_chalk37.default.bold("\nRemoving hooks..."));
|
|
28588
28721
|
let teardownFailed = false;
|
|
28589
28722
|
for (const { label: label2, fn } of AGENT_TEARDOWNS) {
|
|
28590
28723
|
try {
|
|
@@ -28592,7 +28725,7 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
28592
28725
|
} catch (err2) {
|
|
28593
28726
|
teardownFailed = true;
|
|
28594
28727
|
console.error(
|
|
28595
|
-
|
|
28728
|
+
import_chalk37.default.red(
|
|
28596
28729
|
` \u26A0\uFE0F Failed to remove ${label2} hooks: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
28597
28730
|
)
|
|
28598
28731
|
);
|
|
@@ -28601,12 +28734,12 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
28601
28734
|
try {
|
|
28602
28735
|
const residual = getAgentWiring().filter((a) => a.wireState === "wired");
|
|
28603
28736
|
if (residual.length === 0) {
|
|
28604
|
-
console.log(
|
|
28737
|
+
console.log(import_chalk37.default.green(" \u2705 Verified \u2014 no node9 hooks or plugin shims remain"));
|
|
28605
28738
|
} else {
|
|
28606
28739
|
teardownFailed = true;
|
|
28607
|
-
console.error(
|
|
28740
|
+
console.error(import_chalk37.default.red(" \u26A0\uFE0F Still wired after teardown:"));
|
|
28608
28741
|
for (const a of residual) {
|
|
28609
|
-
console.error(
|
|
28742
|
+
console.error(import_chalk37.default.red(` \u2022 ${a.label} \u2014 ${a.settingsPath}`));
|
|
28610
28743
|
}
|
|
28611
28744
|
}
|
|
28612
28745
|
} catch {
|
|
@@ -28622,28 +28755,28 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
28622
28755
|
import_fs66.default.rmSync(node9Dir, { recursive: true });
|
|
28623
28756
|
if (import_fs66.default.existsSync(node9Dir)) {
|
|
28624
28757
|
console.error(
|
|
28625
|
-
|
|
28758
|
+
import_chalk37.default.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
|
|
28626
28759
|
);
|
|
28627
28760
|
} else {
|
|
28628
|
-
console.log(
|
|
28761
|
+
console.log(import_chalk37.default.green("\n \u2705 Deleted ~/.node9/ (config, audit log, credentials)"));
|
|
28629
28762
|
}
|
|
28630
28763
|
} else {
|
|
28631
|
-
console.log(
|
|
28764
|
+
console.log(import_chalk37.default.yellow("\n Skipped \u2014 ~/.node9/ was not deleted."));
|
|
28632
28765
|
}
|
|
28633
28766
|
} else {
|
|
28634
|
-
console.log(
|
|
28767
|
+
console.log(import_chalk37.default.blue("\n \u2139\uFE0F ~/.node9/ not found \u2014 nothing to delete"));
|
|
28635
28768
|
}
|
|
28636
28769
|
} else {
|
|
28637
28770
|
console.log(
|
|
28638
|
-
|
|
28771
|
+
import_chalk37.default.gray("\n ~/.node9/ kept \u2014 run with --purge to delete config and audit log")
|
|
28639
28772
|
);
|
|
28640
28773
|
}
|
|
28641
28774
|
if (teardownFailed) {
|
|
28642
|
-
console.error(
|
|
28775
|
+
console.error(import_chalk37.default.red("\n \u26A0\uFE0F Some hooks could not be removed \u2014 see errors above."));
|
|
28643
28776
|
process.exit(1);
|
|
28644
28777
|
}
|
|
28645
|
-
console.log(
|
|
28646
|
-
console.log(
|
|
28778
|
+
console.log(import_chalk37.default.green.bold("\n\u{1F6E1}\uFE0F Node9 removed. Run: npm uninstall -g node9-ai"));
|
|
28779
|
+
console.log(import_chalk37.default.gray(" Restart any open AI agent sessions for changes to take effect.\n"));
|
|
28647
28780
|
});
|
|
28648
28781
|
registerDoctorCommand(program, version);
|
|
28649
28782
|
program.command("explain").description(
|
|
@@ -28656,7 +28789,7 @@ program.command("explain").description(
|
|
|
28656
28789
|
try {
|
|
28657
28790
|
args = JSON.parse(trimmed);
|
|
28658
28791
|
} catch {
|
|
28659
|
-
console.error(
|
|
28792
|
+
console.error(import_chalk37.default.red(`
|
|
28660
28793
|
\u274C Invalid JSON: ${trimmed}
|
|
28661
28794
|
`));
|
|
28662
28795
|
process.exit(1);
|
|
@@ -28667,67 +28800,68 @@ program.command("explain").description(
|
|
|
28667
28800
|
}
|
|
28668
28801
|
const result = await explainPolicy(tool, args);
|
|
28669
28802
|
console.log("");
|
|
28670
|
-
console.log(
|
|
28803
|
+
console.log(import_chalk37.default.cyan.bold("\u{1F6E1}\uFE0F Node9 Explain"));
|
|
28671
28804
|
console.log("");
|
|
28672
|
-
console.log(` ${
|
|
28805
|
+
console.log(` ${import_chalk37.default.bold("Tool:")} ${import_chalk37.default.white(result.tool)}`);
|
|
28673
28806
|
if (argsRaw) {
|
|
28674
28807
|
const preview2 = argsRaw.length > 80 ? argsRaw.slice(0, 77) + "\u2026" : argsRaw;
|
|
28675
|
-
console.log(` ${
|
|
28808
|
+
console.log(` ${import_chalk37.default.bold("Input:")} ${import_chalk37.default.gray(preview2)}`);
|
|
28676
28809
|
}
|
|
28677
28810
|
console.log("");
|
|
28678
|
-
console.log(
|
|
28811
|
+
console.log(import_chalk37.default.bold("Config Sources (Waterfall):"));
|
|
28679
28812
|
for (const tier of result.waterfall) {
|
|
28680
|
-
const num3 =
|
|
28813
|
+
const num3 = import_chalk37.default.gray(` ${tier.tier}.`);
|
|
28681
28814
|
const label2 = tier.label.padEnd(16);
|
|
28682
28815
|
let statusStr;
|
|
28683
28816
|
if (tier.tier === 1) {
|
|
28684
|
-
statusStr =
|
|
28817
|
+
statusStr = import_chalk37.default.gray(tier.note ?? "");
|
|
28685
28818
|
} else if (tier.status === "active") {
|
|
28686
|
-
const loc = tier.path ?
|
|
28687
|
-
const note = tier.note ?
|
|
28688
|
-
statusStr =
|
|
28819
|
+
const loc = tier.path ? import_chalk37.default.gray(tier.path) : "";
|
|
28820
|
+
const note = tier.note ? import_chalk37.default.gray(`(${tier.note})`) : "";
|
|
28821
|
+
statusStr = import_chalk37.default.green("\u2713 active") + (loc ? " " + loc : "") + (note ? " " + note : "");
|
|
28689
28822
|
} else {
|
|
28690
|
-
statusStr =
|
|
28823
|
+
statusStr = import_chalk37.default.gray("\u25CB " + (tier.note ?? "not found"));
|
|
28691
28824
|
}
|
|
28692
|
-
console.log(`${num3} ${
|
|
28825
|
+
console.log(`${num3} ${import_chalk37.default.white(label2)} ${statusStr}`);
|
|
28693
28826
|
}
|
|
28694
28827
|
console.log("");
|
|
28695
|
-
console.log(
|
|
28828
|
+
console.log(import_chalk37.default.bold("Policy Evaluation:"));
|
|
28696
28829
|
for (const step of result.steps) {
|
|
28697
28830
|
const isFinal = step.isFinal;
|
|
28698
28831
|
let icon;
|
|
28699
|
-
if (step.outcome === "allow") icon =
|
|
28700
|
-
else if (step.outcome === "block") icon =
|
|
28701
|
-
else if (step.outcome === "review") icon =
|
|
28702
|
-
else if (step.outcome === "skip") icon =
|
|
28703
|
-
else icon =
|
|
28832
|
+
if (step.outcome === "allow") icon = import_chalk37.default.green(" \u2705");
|
|
28833
|
+
else if (step.outcome === "block") icon = import_chalk37.default.red(" \u{1F6D1}");
|
|
28834
|
+
else if (step.outcome === "review") icon = import_chalk37.default.red(" \u{1F534}");
|
|
28835
|
+
else if (step.outcome === "skip") icon = import_chalk37.default.gray(" \u2500 ");
|
|
28836
|
+
else icon = import_chalk37.default.gray(" \u25CB ");
|
|
28704
28837
|
const name = step.name.padEnd(18);
|
|
28705
|
-
const nameStr = isFinal ?
|
|
28706
|
-
const detail = isFinal ?
|
|
28707
|
-
const arrow = isFinal ?
|
|
28838
|
+
const nameStr = isFinal ? import_chalk37.default.white.bold(name) : import_chalk37.default.white(name);
|
|
28839
|
+
const detail = isFinal ? import_chalk37.default.white(step.detail) : import_chalk37.default.gray(step.detail);
|
|
28840
|
+
const arrow = isFinal ? import_chalk37.default.yellow(" \u2190 STOP") : "";
|
|
28708
28841
|
console.log(`${icon} ${nameStr} ${detail}${arrow}`);
|
|
28709
28842
|
}
|
|
28710
28843
|
console.log("");
|
|
28711
28844
|
if (result.decision === "allow") {
|
|
28712
|
-
console.log(
|
|
28845
|
+
console.log(import_chalk37.default.green.bold(" Decision: \u2705 ALLOW") + import_chalk37.default.gray(" \u2014 no approval needed"));
|
|
28713
28846
|
} else if (result.decision === "block") {
|
|
28714
28847
|
console.log(
|
|
28715
|
-
|
|
28848
|
+
import_chalk37.default.red.bold(" Decision: \u{1F6D1} BLOCK") + import_chalk37.default.gray(" \u2014 this action is blocked")
|
|
28716
28849
|
);
|
|
28717
28850
|
if (result.blockedByLabel) {
|
|
28718
|
-
console.log(
|
|
28851
|
+
console.log(import_chalk37.default.gray(` Reason: ${result.blockedByLabel}`));
|
|
28719
28852
|
}
|
|
28720
28853
|
} else {
|
|
28721
28854
|
console.log(
|
|
28722
|
-
|
|
28855
|
+
import_chalk37.default.red.bold(" Decision: \u{1F534} REVIEW") + import_chalk37.default.gray(" \u2014 human approval required")
|
|
28723
28856
|
);
|
|
28724
28857
|
if (result.blockedByLabel) {
|
|
28725
|
-
console.log(
|
|
28858
|
+
console.log(import_chalk37.default.gray(` Reason: ${result.blockedByLabel}`));
|
|
28726
28859
|
}
|
|
28727
28860
|
}
|
|
28728
28861
|
console.log("");
|
|
28729
28862
|
});
|
|
28730
28863
|
registerInitCommand(program);
|
|
28864
|
+
registerConnectCommand(program);
|
|
28731
28865
|
registerAuditCommand(program);
|
|
28732
28866
|
registerReportCommand(program);
|
|
28733
28867
|
registerStatusCommand(program);
|
|
@@ -28737,7 +28871,7 @@ program.command("tail").description("Stream live agent activity to the terminal"
|
|
|
28737
28871
|
try {
|
|
28738
28872
|
await startTail2(options);
|
|
28739
28873
|
} catch (err2) {
|
|
28740
|
-
console.error(
|
|
28874
|
+
console.error(import_chalk37.default.red(`\u274C ${err2 instanceof Error ? err2.message : String(err2)}`));
|
|
28741
28875
|
process.exit(1);
|
|
28742
28876
|
}
|
|
28743
28877
|
});
|
|
@@ -28748,7 +28882,7 @@ program.command("monitor").description("Live interactive dashboard \u2014 activi
|
|
|
28748
28882
|
const mod = await dynamicImport(`file://${dashboardPath}`);
|
|
28749
28883
|
await mod.startMonitor();
|
|
28750
28884
|
} catch (err2) {
|
|
28751
|
-
console.error(
|
|
28885
|
+
console.error(import_chalk37.default.red(`\u274C ${err2 instanceof Error ? err2.message : String(err2)}`));
|
|
28752
28886
|
process.exit(1);
|
|
28753
28887
|
}
|
|
28754
28888
|
});
|
|
@@ -28803,7 +28937,7 @@ program.command("pause").description("Temporarily disable Node9 protection for a
|
|
|
28803
28937
|
const ms = parseDuration(options.duration);
|
|
28804
28938
|
if (ms === null) {
|
|
28805
28939
|
console.error(
|
|
28806
|
-
|
|
28940
|
+
import_chalk37.default.red(`
|
|
28807
28941
|
\u274C Invalid duration: "${options.duration}". Use format like 15m, 1h, 30s.
|
|
28808
28942
|
`)
|
|
28809
28943
|
);
|
|
@@ -28811,20 +28945,20 @@ program.command("pause").description("Temporarily disable Node9 protection for a
|
|
|
28811
28945
|
}
|
|
28812
28946
|
pauseNode9(ms, options.duration);
|
|
28813
28947
|
const expiresAt = new Date(Date.now() + ms).toLocaleTimeString();
|
|
28814
|
-
console.log(
|
|
28948
|
+
console.log(import_chalk37.default.yellow(`
|
|
28815
28949
|
\u23F8 Node9 paused until ${expiresAt}`));
|
|
28816
|
-
console.log(
|
|
28817
|
-
console.log(
|
|
28950
|
+
console.log(import_chalk37.default.gray(` All tool calls will be allowed without review.`));
|
|
28951
|
+
console.log(import_chalk37.default.gray(` Run "node9 resume" to re-enable early.
|
|
28818
28952
|
`));
|
|
28819
28953
|
});
|
|
28820
28954
|
program.command("resume").description("Re-enable Node9 protection immediately").action(() => {
|
|
28821
28955
|
const { paused } = checkPause();
|
|
28822
28956
|
if (!paused) {
|
|
28823
|
-
console.log(
|
|
28957
|
+
console.log(import_chalk37.default.gray("\nNode9 is already active \u2014 nothing to resume.\n"));
|
|
28824
28958
|
return;
|
|
28825
28959
|
}
|
|
28826
28960
|
resumeNode9();
|
|
28827
|
-
console.log(
|
|
28961
|
+
console.log(import_chalk37.default.green("\n\u25B6 Node9 resumed \u2014 protection is active.\n"));
|
|
28828
28962
|
});
|
|
28829
28963
|
var HOOK_BASED_AGENTS = {
|
|
28830
28964
|
claude: "claude",
|
|
@@ -28840,15 +28974,15 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
28840
28974
|
if (HOOK_BASED_AGENTS[firstArg2] !== void 0) {
|
|
28841
28975
|
const target = HOOK_BASED_AGENTS[firstArg2];
|
|
28842
28976
|
console.error(
|
|
28843
|
-
|
|
28977
|
+
import_chalk37.default.yellow(`
|
|
28844
28978
|
\u26A0\uFE0F Node9 proxy mode does not support "${target}" directly.`)
|
|
28845
28979
|
);
|
|
28846
|
-
console.error(
|
|
28980
|
+
console.error(import_chalk37.default.white(`
|
|
28847
28981
|
"${target}" uses its own hook system. Use:`));
|
|
28848
28982
|
console.error(
|
|
28849
|
-
|
|
28983
|
+
import_chalk37.default.green(` node9 addto ${target} `) + import_chalk37.default.gray("# one-time setup")
|
|
28850
28984
|
);
|
|
28851
|
-
console.error(
|
|
28985
|
+
console.error(import_chalk37.default.green(` ${target} `) + import_chalk37.default.gray("# run normally"));
|
|
28852
28986
|
process.exit(1);
|
|
28853
28987
|
}
|
|
28854
28988
|
const runArgs = firstArg2 === "shell" ? commandArgs.slice(1) : commandArgs;
|
|
@@ -28865,7 +28999,7 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
28865
28999
|
}
|
|
28866
29000
|
);
|
|
28867
29001
|
if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && getConfig().settings.autoStartDaemon) {
|
|
28868
|
-
console.error(
|
|
29002
|
+
console.error(import_chalk37.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically..."));
|
|
28869
29003
|
const daemonReady = await autoStartDaemonAndWait();
|
|
28870
29004
|
if (daemonReady) result = await authorizeHeadless("shell", { command: fullCommand });
|
|
28871
29005
|
}
|
|
@@ -28878,12 +29012,12 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
28878
29012
|
}
|
|
28879
29013
|
if (!result.approved) {
|
|
28880
29014
|
console.error(
|
|
28881
|
-
|
|
29015
|
+
import_chalk37.default.red(`
|
|
28882
29016
|
\u274C Node9 Blocked: ${result.reason || "Dangerous command detected."}`)
|
|
28883
29017
|
);
|
|
28884
29018
|
process.exit(1);
|
|
28885
29019
|
}
|
|
28886
|
-
console.error(
|
|
29020
|
+
console.error(import_chalk37.default.green("\n\u2705 Approved \u2014 running command...\n"));
|
|
28887
29021
|
await runProxy(fullCommand);
|
|
28888
29022
|
} else {
|
|
28889
29023
|
program.help();
|