@node9/proxy 1.50.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 +742 -576
- package/dist/cli.mjs +1591 -1425
- package/dist/dashboard.mjs +1 -1
- package/dist/index.js +26 -0
- package/dist/index.mjs +26 -0
- 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
|
}
|
|
@@ -4243,12 +4243,28 @@ function applyManagedEgress(local, managed, locked) {
|
|
|
4243
4243
|
}
|
|
4244
4244
|
return next;
|
|
4245
4245
|
}
|
|
4246
|
-
|
|
4246
|
+
function applyManagedDlp(local, managed, locked) {
|
|
4247
|
+
const next = { ...local };
|
|
4248
|
+
if (typeof managed.enabled === "boolean") {
|
|
4249
|
+
next.enabled = locked.includes("dlpEnabled") ? managed.enabled : local.enabled || managed.enabled;
|
|
4250
|
+
}
|
|
4251
|
+
if (typeof managed.pii === "string") {
|
|
4252
|
+
next.pii = resolveByOrder(
|
|
4253
|
+
DLP_PII_ORDER,
|
|
4254
|
+
local.pii ?? "off",
|
|
4255
|
+
managed.pii,
|
|
4256
|
+
locked.includes("dlpPii")
|
|
4257
|
+
);
|
|
4258
|
+
}
|
|
4259
|
+
return next;
|
|
4260
|
+
}
|
|
4261
|
+
var MODE_ORDER, EGRESS_MODE_ORDER, DLP_PII_ORDER;
|
|
4247
4262
|
var init_managed = __esm({
|
|
4248
4263
|
"src/config/managed.ts"() {
|
|
4249
4264
|
"use strict";
|
|
4250
4265
|
MODE_ORDER = ["observe", "audit", "standard", "strict"];
|
|
4251
4266
|
EGRESS_MODE_ORDER = ["off", "review", "block"];
|
|
4267
|
+
DLP_PII_ORDER = ["off", "block"];
|
|
4252
4268
|
}
|
|
4253
4269
|
});
|
|
4254
4270
|
|
|
@@ -4281,11 +4297,11 @@ function getGlobalSettings() {
|
|
|
4281
4297
|
};
|
|
4282
4298
|
}
|
|
4283
4299
|
function getCredentials() {
|
|
4284
|
-
const
|
|
4300
|
+
const DEFAULT_API_URL3 = "https://api.node9.ai/api/v1/intercept";
|
|
4285
4301
|
if (process.env.NODE9_API_KEY) {
|
|
4286
4302
|
return {
|
|
4287
4303
|
apiKey: process.env.NODE9_API_KEY,
|
|
4288
|
-
apiUrl: process.env.NODE9_API_URL ||
|
|
4304
|
+
apiUrl: process.env.NODE9_API_URL || DEFAULT_API_URL3
|
|
4289
4305
|
};
|
|
4290
4306
|
}
|
|
4291
4307
|
try {
|
|
@@ -4297,13 +4313,13 @@ function getCredentials() {
|
|
|
4297
4313
|
if (profile?.apiKey) {
|
|
4298
4314
|
return {
|
|
4299
4315
|
apiKey: profile.apiKey,
|
|
4300
|
-
apiUrl: profile.apiUrl ||
|
|
4316
|
+
apiUrl: profile.apiUrl || DEFAULT_API_URL3
|
|
4301
4317
|
};
|
|
4302
4318
|
}
|
|
4303
4319
|
if (creds.apiKey) {
|
|
4304
4320
|
return {
|
|
4305
4321
|
apiKey: creds.apiKey,
|
|
4306
|
-
apiUrl: creds.apiUrl ||
|
|
4322
|
+
apiUrl: creds.apiUrl || DEFAULT_API_URL3
|
|
4307
4323
|
};
|
|
4308
4324
|
}
|
|
4309
4325
|
}
|
|
@@ -4482,6 +4498,16 @@ function getConfig(cwd) {
|
|
|
4482
4498
|
locked
|
|
4483
4499
|
);
|
|
4484
4500
|
}
|
|
4501
|
+
if (mc.dlp && typeof mc.dlp === "object") {
|
|
4502
|
+
mergedPolicy.dlp = applyManagedDlp(
|
|
4503
|
+
mergedPolicy.dlp,
|
|
4504
|
+
{
|
|
4505
|
+
enabled: typeof mc.dlp.enabled === "boolean" ? mc.dlp.enabled : void 0,
|
|
4506
|
+
pii: typeof mc.dlp.pii === "string" ? mc.dlp.pii : void 0
|
|
4507
|
+
},
|
|
4508
|
+
locked
|
|
4509
|
+
);
|
|
4510
|
+
}
|
|
4485
4511
|
}
|
|
4486
4512
|
if (raw.panicMode === true) {
|
|
4487
4513
|
mergedSettings.panicMode = true;
|
|
@@ -7761,6 +7787,35 @@ var init_setup_pi_shim = __esm({
|
|
|
7761
7787
|
});
|
|
7762
7788
|
|
|
7763
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
|
+
}
|
|
7764
7819
|
function hasNode9McpServer(servers) {
|
|
7765
7820
|
const entry = servers["node9"];
|
|
7766
7821
|
return !!entry && entry.command === "node9" && Array.isArray(entry.args) && entry.args[0] === "mcp-server";
|
|
@@ -8081,7 +8136,7 @@ async function setupClaude() {
|
|
|
8081
8136
|
console.log(import_chalk.default.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
|
|
8082
8137
|
}
|
|
8083
8138
|
console.log("");
|
|
8084
|
-
const proceed = await
|
|
8139
|
+
const proceed = await confirm({ message: "Wrap these MCP servers?", default: true });
|
|
8085
8140
|
if (proceed) {
|
|
8086
8141
|
for (const { name, upstream } of serversToWrap) {
|
|
8087
8142
|
servers[name] = {
|
|
@@ -8186,7 +8241,7 @@ async function setupGemini() {
|
|
|
8186
8241
|
console.log(import_chalk.default.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
|
|
8187
8242
|
}
|
|
8188
8243
|
console.log("");
|
|
8189
|
-
const proceed = await
|
|
8244
|
+
const proceed = await confirm({ message: "Wrap these MCP servers?", default: true });
|
|
8190
8245
|
if (proceed) {
|
|
8191
8246
|
for (const { name, upstream } of serversToWrap) {
|
|
8192
8247
|
servers[name] = {
|
|
@@ -8312,7 +8367,7 @@ async function setupAntigravity() {
|
|
|
8312
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)."
|
|
8313
8368
|
)
|
|
8314
8369
|
);
|
|
8315
|
-
const clean = await
|
|
8370
|
+
const clean = await confirm({
|
|
8316
8371
|
message: "Remove the legacy Gemini CLI hooks?",
|
|
8317
8372
|
default: false
|
|
8318
8373
|
});
|
|
@@ -8334,7 +8389,7 @@ async function setupAntigravity() {
|
|
|
8334
8389
|
console.log(import_chalk.default.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
|
|
8335
8390
|
}
|
|
8336
8391
|
console.log("");
|
|
8337
|
-
const proceed = await
|
|
8392
|
+
const proceed = await confirm({ message: "Wrap these MCP servers?", default: true });
|
|
8338
8393
|
if (proceed) {
|
|
8339
8394
|
for (const { name, upstream } of serversToWrap) {
|
|
8340
8395
|
servers[name] = {
|
|
@@ -8479,7 +8534,7 @@ async function setupCopilot() {
|
|
|
8479
8534
|
console.log(import_chalk.default.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
|
|
8480
8535
|
}
|
|
8481
8536
|
console.log("");
|
|
8482
|
-
const proceed = await
|
|
8537
|
+
const proceed = await confirm({ message: "Wrap these MCP servers?", default: true });
|
|
8483
8538
|
if (proceed) {
|
|
8484
8539
|
for (const { name, upstream } of serversToWrap) {
|
|
8485
8540
|
servers[name] = {
|
|
@@ -8680,7 +8735,7 @@ async function setupCursor() {
|
|
|
8680
8735
|
console.log(import_chalk.default.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
|
|
8681
8736
|
}
|
|
8682
8737
|
console.log("");
|
|
8683
|
-
const proceed = await
|
|
8738
|
+
const proceed = await confirm({ message: "Wrap these MCP servers?", default: true });
|
|
8684
8739
|
if (proceed) {
|
|
8685
8740
|
for (const { name, upstream } of serversToWrap) {
|
|
8686
8741
|
servers[name] = {
|
|
@@ -8831,7 +8886,7 @@ async function setupCodex() {
|
|
|
8831
8886
|
console.log(import_chalk.default.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
|
|
8832
8887
|
}
|
|
8833
8888
|
console.log("");
|
|
8834
|
-
const proceed = await
|
|
8889
|
+
const proceed = await confirm({ message: "Wrap these MCP servers?", default: true });
|
|
8835
8890
|
if (proceed) {
|
|
8836
8891
|
for (const { name, upstream } of serversToWrap) {
|
|
8837
8892
|
servers[name] = {
|
|
@@ -9008,7 +9063,7 @@ async function setupWindsurf() {
|
|
|
9008
9063
|
console.log(import_chalk.default.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
|
|
9009
9064
|
}
|
|
9010
9065
|
console.log("");
|
|
9011
|
-
const proceed = await
|
|
9066
|
+
const proceed = await confirm({ message: "Wrap these MCP servers?", default: true });
|
|
9012
9067
|
if (proceed) {
|
|
9013
9068
|
for (const { name, upstream } of serversToWrap) {
|
|
9014
9069
|
servers[name] = {
|
|
@@ -9112,7 +9167,7 @@ async function setupVSCode() {
|
|
|
9112
9167
|
console.log(import_chalk.default.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
|
|
9113
9168
|
}
|
|
9114
9169
|
console.log("");
|
|
9115
|
-
const proceed = await
|
|
9170
|
+
const proceed = await confirm({ message: "Wrap these MCP servers?", default: true });
|
|
9116
9171
|
if (proceed) {
|
|
9117
9172
|
for (const { name, upstream } of serversToWrap) {
|
|
9118
9173
|
servers[name] = {
|
|
@@ -9212,7 +9267,7 @@ async function setupClaudeDesktop() {
|
|
|
9212
9267
|
console.log(import_chalk.default.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
|
|
9213
9268
|
}
|
|
9214
9269
|
console.log("");
|
|
9215
|
-
const proceed = await
|
|
9270
|
+
const proceed = await confirm({ message: "Wrap these MCP servers?", default: true });
|
|
9216
9271
|
if (proceed) {
|
|
9217
9272
|
for (const { name, upstream } of serversToWrap) {
|
|
9218
9273
|
servers[name] = {
|
|
@@ -16854,7 +16909,7 @@ function readCredentials() {
|
|
|
16854
16909
|
if (process.env.NODE9_API_KEY) {
|
|
16855
16910
|
return {
|
|
16856
16911
|
apiKey: process.env.NODE9_API_KEY,
|
|
16857
|
-
apiUrl: process.env.NODE9_API_URL ??
|
|
16912
|
+
apiUrl: process.env.NODE9_API_URL ?? DEFAULT_API_URL2
|
|
16858
16913
|
};
|
|
16859
16914
|
}
|
|
16860
16915
|
try {
|
|
@@ -16873,11 +16928,11 @@ function readCredentials() {
|
|
|
16873
16928
|
// Anything else is taken as-is so users can override the full
|
|
16874
16929
|
// URL via NODE9_API_URL or a non-standard apiUrl.
|
|
16875
16930
|
/\/intercept$/.test(profile.apiUrl) ? profile.apiUrl + "/policies/sync" : profile.apiUrl
|
|
16876
|
-
) :
|
|
16931
|
+
) : DEFAULT_API_URL2
|
|
16877
16932
|
};
|
|
16878
16933
|
}
|
|
16879
16934
|
if (typeof creds.apiKey === "string" && creds.apiKey.length > 0) {
|
|
16880
|
-
return { apiKey: creds.apiKey, apiUrl:
|
|
16935
|
+
return { apiKey: creds.apiKey, apiUrl: DEFAULT_API_URL2 };
|
|
16881
16936
|
}
|
|
16882
16937
|
} catch {
|
|
16883
16938
|
}
|
|
@@ -16961,7 +17016,13 @@ function extractManagedConfig(body) {
|
|
|
16961
17016
|
if (typeof mc.egress.mode === "string") e.mode = mc.egress.mode;
|
|
16962
17017
|
if (e.enabled !== void 0 || e.mode !== void 0) out.egress = e;
|
|
16963
17018
|
}
|
|
16964
|
-
|
|
17019
|
+
if (mc.dlp && typeof mc.dlp === "object") {
|
|
17020
|
+
const d = {};
|
|
17021
|
+
if (typeof mc.dlp.enabled === "boolean") d.enabled = mc.dlp.enabled;
|
|
17022
|
+
if (typeof mc.dlp.pii === "string") d.pii = mc.dlp.pii;
|
|
17023
|
+
if (d.enabled !== void 0 || d.pii !== void 0) out.dlp = d;
|
|
17024
|
+
}
|
|
17025
|
+
return out.mode !== void 0 || out.egress !== void 0 || out.dlp !== void 0 ? out : void 0;
|
|
16965
17026
|
}
|
|
16966
17027
|
function writeCache2(cache) {
|
|
16967
17028
|
const dir = import_path31.default.dirname(rulesCacheFile());
|
|
@@ -17219,7 +17280,7 @@ function startForensicBroadcast() {
|
|
|
17219
17280
|
const recurring = setInterval(() => void tick(), FORENSIC_BROADCAST_INTERVAL_MS);
|
|
17220
17281
|
recurring.unref();
|
|
17221
17282
|
}
|
|
17222
|
-
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;
|
|
17223
17284
|
var init_sync = __esm({
|
|
17224
17285
|
"src/daemon/sync.ts"() {
|
|
17225
17286
|
"use strict";
|
|
@@ -17251,7 +17312,7 @@ var init_sync = __esm({
|
|
|
17251
17312
|
"long-output-redacted": "longOutputRedactions"
|
|
17252
17313
|
};
|
|
17253
17314
|
rulesCacheFile = () => import_path31.default.join(import_os29.default.homedir(), ".node9", "rules-cache.json");
|
|
17254
|
-
|
|
17315
|
+
DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept/policies/sync";
|
|
17255
17316
|
DEFAULT_INTERVAL_HOURS = 5;
|
|
17256
17317
|
MIN_INTERVAL_SECONDS = 15;
|
|
17257
17318
|
MAX_INTERVAL_SECONDS = 24 * 60 * 60;
|
|
@@ -19053,10 +19114,10 @@ function readSessionUsage() {
|
|
|
19053
19114
|
}
|
|
19054
19115
|
}
|
|
19055
19116
|
function formatContextStat(stat) {
|
|
19056
|
-
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;
|
|
19057
19118
|
const k = (n) => `${Math.round(n / 1e3)}k`;
|
|
19058
19119
|
const modelShort = stat.model.replace(/@.*$/, "").replace(/-\d{8}$/, "").replace(/^claude-/, "");
|
|
19059
|
-
return
|
|
19120
|
+
return import_chalk36.default.dim("ctx: ") + pctColor(`${stat.fillPct}%`) + import_chalk36.default.dim(
|
|
19060
19121
|
` (${k(stat.inputTokens)}/${k(getModelContextLimit(stat.model))} out ${k(stat.outputTokens)} \xB7 ${modelShort})`
|
|
19061
19122
|
);
|
|
19062
19123
|
}
|
|
@@ -19079,11 +19140,11 @@ function agentLabel(agent, mcpServer, sessionId) {
|
|
|
19079
19140
|
const tag = sessionTag(sessionId);
|
|
19080
19141
|
const tagSuffix = tag ? `\xB7${tag}` : "";
|
|
19081
19142
|
if (!agent || agent === "Terminal") {
|
|
19082
|
-
return mcpServer ?
|
|
19143
|
+
return mcpServer ? import_chalk36.default.dim(`[\u2192 ${mcpServer}] `) : "";
|
|
19083
19144
|
}
|
|
19084
19145
|
const short = agent === "Claude Code" ? "Claude" : agent === "Gemini CLI" ? "Gemini" : agent === "Unknown Agent" ? "" : agent.split(" ")[0];
|
|
19085
|
-
if (!short) return mcpServer ?
|
|
19086
|
-
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}] `);
|
|
19087
19148
|
}
|
|
19088
19149
|
function formatBase(activity) {
|
|
19089
19150
|
const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
|
|
@@ -19091,20 +19152,20 @@ function formatBase(activity) {
|
|
|
19091
19152
|
const toolName = activity.tool.slice(0, 16).padEnd(16);
|
|
19092
19153
|
const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(import_os55.default.homedir(), "~");
|
|
19093
19154
|
const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
|
|
19094
|
-
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)}`;
|
|
19095
19156
|
}
|
|
19096
19157
|
function renderResult(activity, result) {
|
|
19097
19158
|
const base = formatBase(activity);
|
|
19098
19159
|
let status;
|
|
19099
19160
|
if (result.status === "allow") {
|
|
19100
|
-
status =
|
|
19161
|
+
status = import_chalk36.default.green("\u2713 ALLOW");
|
|
19101
19162
|
} else if (result.status === "dlp") {
|
|
19102
|
-
status =
|
|
19163
|
+
status = import_chalk36.default.bgRed.white.bold(" \u{1F6E1}\uFE0F DLP ");
|
|
19103
19164
|
} else {
|
|
19104
|
-
status =
|
|
19165
|
+
status = import_chalk36.default.red("\u2717 BLOCK");
|
|
19105
19166
|
}
|
|
19106
19167
|
const cost = result.costEstimate ?? activity.costEstimate;
|
|
19107
|
-
const costSuffix = cost == null ? "" :
|
|
19168
|
+
const costSuffix = cost == null ? "" : import_chalk36.default.dim(` ~$${cost >= 1e-3 ? cost.toFixed(3) : "0.000"}`);
|
|
19108
19169
|
if (process.stdout.isTTY) {
|
|
19109
19170
|
if (pendingShownForId === activity.id && pendingWrappedLines > 1) {
|
|
19110
19171
|
import_readline6.default.moveCursor(process.stdout, 0, -(pendingWrappedLines - 1));
|
|
@@ -19121,7 +19182,7 @@ function renderResult(activity, result) {
|
|
|
19121
19182
|
}
|
|
19122
19183
|
function renderPending(activity) {
|
|
19123
19184
|
if (!process.stdout.isTTY) return;
|
|
19124
|
-
const line = `${formatBase(activity)} ${
|
|
19185
|
+
const line = `${formatBase(activity)} ${import_chalk36.default.yellow("\u25CF \u2026")}`;
|
|
19125
19186
|
pendingShownForId = activity.id;
|
|
19126
19187
|
pendingWrappedLines = wrappedLineCount(line);
|
|
19127
19188
|
process.stdout.write(`${line}\r`);
|
|
@@ -19133,7 +19194,7 @@ async function ensureDaemon() {
|
|
|
19133
19194
|
const { port } = JSON.parse(import_fs64.default.readFileSync(PID_FILE, "utf-8"));
|
|
19134
19195
|
pidPort = port;
|
|
19135
19196
|
} catch {
|
|
19136
|
-
console.error(
|
|
19197
|
+
console.error(import_chalk36.default.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
|
|
19137
19198
|
}
|
|
19138
19199
|
}
|
|
19139
19200
|
const checkPort = pidPort ?? DAEMON_PORT;
|
|
@@ -19144,7 +19205,7 @@ async function ensureDaemon() {
|
|
|
19144
19205
|
if (res.ok) return checkPort;
|
|
19145
19206
|
} catch {
|
|
19146
19207
|
}
|
|
19147
|
-
console.log(
|
|
19208
|
+
console.log(import_chalk36.default.dim("\u{1F6E1}\uFE0F Starting Node9 daemon..."));
|
|
19148
19209
|
const child = (0, import_child_process14.spawn)(process.execPath, [process.argv[1], "daemon"], {
|
|
19149
19210
|
detached: true,
|
|
19150
19211
|
stdio: "ignore",
|
|
@@ -19161,7 +19222,7 @@ async function ensureDaemon() {
|
|
|
19161
19222
|
} catch {
|
|
19162
19223
|
}
|
|
19163
19224
|
}
|
|
19164
|
-
console.error(
|
|
19225
|
+
console.error(import_chalk36.default.red("\u274C Daemon failed to start. Try: node9 daemon start"));
|
|
19165
19226
|
process.exit(1);
|
|
19166
19227
|
}
|
|
19167
19228
|
function postDecisionHttp(id, decision, authToken, port, opts) {
|
|
@@ -19171,7 +19232,7 @@ function postDecisionHttp(id, decision, authToken, port, opts) {
|
|
|
19171
19232
|
if (opts?.trustDuration) bodyObj.trustDuration = opts.trustDuration;
|
|
19172
19233
|
if (opts?.reason) bodyObj.reason = opts.reason;
|
|
19173
19234
|
const body = JSON.stringify(bodyObj);
|
|
19174
|
-
const req =
|
|
19235
|
+
const req = import_http5.default.request(
|
|
19175
19236
|
{
|
|
19176
19237
|
hostname: "127.0.0.1",
|
|
19177
19238
|
port,
|
|
@@ -19230,7 +19291,7 @@ function buildCardLines(req, localCount = 0) {
|
|
|
19230
19291
|
const severityIcon = isBlock ? `${RED}\u{1F6D1}` : `${YELLOW}\u26A0 `;
|
|
19231
19292
|
const rawDesc = req.riskMetadata?.ruleDescription ?? "";
|
|
19232
19293
|
const description = rawDesc ? cleanReason(rawDesc) : "";
|
|
19233
|
-
const agentSuffix = req.agent && req.agent !== "Terminal" ? ` ${RESET2}${
|
|
19294
|
+
const agentSuffix = req.agent && req.agent !== "Terminal" ? ` ${RESET2}${import_chalk36.default.dim(`(${req.agent})`)}` : "";
|
|
19234
19295
|
const lines = [
|
|
19235
19296
|
``,
|
|
19236
19297
|
`${BOLD2}${CYAN}\u2554\u2550\u2550 Node9 Approval Required \u2550\u2550\u2557${RESET2}`,
|
|
@@ -19299,7 +19360,7 @@ function approverStatusLine() {
|
|
|
19299
19360
|
const a = readApproversFromDisk();
|
|
19300
19361
|
const fmt = (label2, key) => {
|
|
19301
19362
|
const on = a[key] !== false;
|
|
19302
|
-
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")}`;
|
|
19303
19364
|
};
|
|
19304
19365
|
return `${fmt("native", "native")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
|
|
19305
19366
|
}
|
|
@@ -19322,7 +19383,7 @@ async function startTail(options = {}) {
|
|
|
19322
19383
|
const port = await ensureDaemon();
|
|
19323
19384
|
if (options.clear) {
|
|
19324
19385
|
const result = await new Promise((resolve) => {
|
|
19325
|
-
const req2 =
|
|
19386
|
+
const req2 = import_http5.default.request(
|
|
19326
19387
|
{ method: "POST", hostname: "127.0.0.1", port, path: "/events/clear" },
|
|
19327
19388
|
(res) => {
|
|
19328
19389
|
const status = res.statusCode ?? 0;
|
|
@@ -19344,7 +19405,7 @@ async function startTail(options = {}) {
|
|
|
19344
19405
|
req2.end();
|
|
19345
19406
|
});
|
|
19346
19407
|
if (result.ok) {
|
|
19347
|
-
console.log(
|
|
19408
|
+
console.log(import_chalk36.default.green("\u2713 Flight Recorder buffer cleared."));
|
|
19348
19409
|
} else if (result.code === "ECONNREFUSED") {
|
|
19349
19410
|
throw new Error("Daemon is not running. Start it with: node9 daemon start");
|
|
19350
19411
|
} else if (result.code === "ETIMEDOUT") {
|
|
@@ -19390,7 +19451,7 @@ async function startTail(options = {}) {
|
|
|
19390
19451
|
const channel = name === "n" ? "native" : name === "c" ? "cloud" : name === "t" ? "terminal" : null;
|
|
19391
19452
|
if (channel) {
|
|
19392
19453
|
toggleApprover(channel);
|
|
19393
|
-
console.log(
|
|
19454
|
+
console.log(import_chalk36.default.dim(` Approvers: ${approverStatusLine()}`));
|
|
19394
19455
|
}
|
|
19395
19456
|
};
|
|
19396
19457
|
process.stdin.on("keypress", idleKeypressHandler);
|
|
@@ -19456,7 +19517,7 @@ async function startTail(options = {}) {
|
|
|
19456
19517
|
localAllowCounts.get(req2.toolName) ?? 0
|
|
19457
19518
|
)
|
|
19458
19519
|
);
|
|
19459
|
-
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");
|
|
19460
19521
|
stampedLines.push(` ${BOLD2}\u2192${RESET2} ${decisionStamp} ${GRAY}(terminal)${RESET2}`, ``);
|
|
19461
19522
|
for (const line of stampedLines) process.stdout.write(line + "\n");
|
|
19462
19523
|
process.stdout.write(SHOW_CURSOR);
|
|
@@ -19507,7 +19568,7 @@ async function startTail(options = {}) {
|
|
|
19507
19568
|
);
|
|
19508
19569
|
const stampedLines = buildCardLines(req2, priorCount);
|
|
19509
19570
|
if (externalDecision) {
|
|
19510
|
-
const source = externalDecision === "allow" ?
|
|
19571
|
+
const source = externalDecision === "allow" ? import_chalk36.default.green("\u2713 ALLOWED") : import_chalk36.default.red("\u2717 DENIED");
|
|
19511
19572
|
stampedLines.push(` ${BOLD2}\u2192${RESET2} ${source} ${GRAY}(external)${RESET2}`, ``);
|
|
19512
19573
|
}
|
|
19513
19574
|
for (const line of stampedLines) process.stdout.write(line + "\n");
|
|
@@ -19555,25 +19616,25 @@ async function startTail(options = {}) {
|
|
|
19555
19616
|
if (unackedDlp > 0) {
|
|
19556
19617
|
console.log("");
|
|
19557
19618
|
console.log(
|
|
19558
|
-
|
|
19619
|
+
import_chalk36.default.bgRed.white.bold(
|
|
19559
19620
|
` \u26A0\uFE0F DLP ALERT: ${unackedDlp} secret${unackedDlp !== 1 ? "s" : ""} found in Claude response text \u2014 run: node9 dlp `
|
|
19560
19621
|
)
|
|
19561
19622
|
);
|
|
19562
19623
|
}
|
|
19563
19624
|
} catch {
|
|
19564
19625
|
}
|
|
19565
|
-
console.log(
|
|
19626
|
+
console.log(import_chalk36.default.cyan.bold(`
|
|
19566
19627
|
\u{1F6F0}\uFE0F Node9 tail`));
|
|
19567
19628
|
if (canApprove) {
|
|
19568
|
-
console.log(
|
|
19569
|
-
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`));
|
|
19570
19631
|
}
|
|
19571
19632
|
const ctxStat = readSessionUsage();
|
|
19572
19633
|
if (ctxStat) console.log(" " + formatContextStat(ctxStat));
|
|
19573
19634
|
if (options.history) {
|
|
19574
|
-
console.log(
|
|
19635
|
+
console.log(import_chalk36.default.dim("Showing history + live events.\n"));
|
|
19575
19636
|
} else {
|
|
19576
|
-
console.log(
|
|
19637
|
+
console.log(import_chalk36.default.dim("Showing live events only. Use --history to include past.\n"));
|
|
19577
19638
|
}
|
|
19578
19639
|
process.on("SIGINT", () => {
|
|
19579
19640
|
exitIdleMode();
|
|
@@ -19583,7 +19644,7 @@ async function startTail(options = {}) {
|
|
|
19583
19644
|
import_readline6.default.clearLine(process.stdout, 0);
|
|
19584
19645
|
import_readline6.default.cursorTo(process.stdout, 0);
|
|
19585
19646
|
}
|
|
19586
|
-
console.log(
|
|
19647
|
+
console.log(import_chalk36.default.dim("\n\u{1F6F0}\uFE0F Disconnected."));
|
|
19587
19648
|
process.exit(0);
|
|
19588
19649
|
});
|
|
19589
19650
|
const STALL_THRESHOLD_MS = 6e4;
|
|
@@ -19595,7 +19656,7 @@ async function startTail(options = {}) {
|
|
|
19595
19656
|
if (Date.now() - auditMtime >= STALL_THRESHOLD_MS) return;
|
|
19596
19657
|
console.log("");
|
|
19597
19658
|
console.log(
|
|
19598
|
-
|
|
19659
|
+
import_chalk36.default.yellow(
|
|
19599
19660
|
"\u26A0\uFE0F Tail appears stalled \u2014 hooks are firing but no events are arriving. Try: node9 daemon restart"
|
|
19600
19661
|
)
|
|
19601
19662
|
);
|
|
@@ -19605,14 +19666,14 @@ async function startTail(options = {}) {
|
|
|
19605
19666
|
}, STALL_THRESHOLD_MS / 2);
|
|
19606
19667
|
stallWatchdog.unref();
|
|
19607
19668
|
const sseUrl = `http://127.0.0.1:${port}/events?capabilities=input`;
|
|
19608
|
-
const req =
|
|
19669
|
+
const req = import_http5.default.get(
|
|
19609
19670
|
sseUrl,
|
|
19610
19671
|
{
|
|
19611
19672
|
headers: authToken ? { "X-Node9-Internal": authToken } : {}
|
|
19612
19673
|
},
|
|
19613
19674
|
(res) => {
|
|
19614
19675
|
if (res.statusCode !== 200) {
|
|
19615
|
-
console.error(
|
|
19676
|
+
console.error(import_chalk36.default.red(`Failed to connect: HTTP ${res.statusCode}`));
|
|
19616
19677
|
process.exit(1);
|
|
19617
19678
|
}
|
|
19618
19679
|
if (canApprove) enterIdleMode();
|
|
@@ -19643,7 +19704,7 @@ async function startTail(options = {}) {
|
|
|
19643
19704
|
import_readline6.default.clearLine(process.stdout, 0);
|
|
19644
19705
|
import_readline6.default.cursorTo(process.stdout, 0);
|
|
19645
19706
|
}
|
|
19646
|
-
console.log(
|
|
19707
|
+
console.log(import_chalk36.default.red("\n\u274C Daemon disconnected."));
|
|
19647
19708
|
process.exit(1);
|
|
19648
19709
|
});
|
|
19649
19710
|
}
|
|
@@ -19656,7 +19717,7 @@ async function startTail(options = {}) {
|
|
|
19656
19717
|
const parsed = JSON.parse(rawData);
|
|
19657
19718
|
const msg = parsed.message ?? "Flight recorder is down \u2014 run: node9 daemon restart";
|
|
19658
19719
|
console.log("");
|
|
19659
|
-
console.log(
|
|
19720
|
+
console.log(import_chalk36.default.bgRed.white.bold(` \u26A0\uFE0F ${msg} `));
|
|
19660
19721
|
} catch {
|
|
19661
19722
|
}
|
|
19662
19723
|
return;
|
|
@@ -19741,9 +19802,9 @@ async function startTail(options = {}) {
|
|
|
19741
19802
|
const rawSummary = data.argsSummary ?? data.tool;
|
|
19742
19803
|
const summary = shortenPathSummary(rawSummary);
|
|
19743
19804
|
const fileCount = data.fileCount ?? 0;
|
|
19744
|
-
const files = fileCount > 0 ?
|
|
19805
|
+
const files = fileCount > 0 ? import_chalk36.default.dim(` \xB7 ${fileCount} file${fileCount === 1 ? "" : "s"}`) : "";
|
|
19745
19806
|
process.stdout.write(
|
|
19746
|
-
`${
|
|
19807
|
+
`${import_chalk36.default.dim(time)} ${import_chalk36.default.cyan("\u{1F4F8} snapshot")} ${import_chalk36.default.dim(hash)} ${summary}${files}
|
|
19747
19808
|
`
|
|
19748
19809
|
);
|
|
19749
19810
|
return;
|
|
@@ -19760,28 +19821,28 @@ async function startTail(options = {}) {
|
|
|
19760
19821
|
if (event === "execution-result") {
|
|
19761
19822
|
const exec = data;
|
|
19762
19823
|
const time = new Date(Date.now()).toLocaleTimeString([], { hour12: false });
|
|
19763
|
-
const arrow = exec.isError ?
|
|
19824
|
+
const arrow = exec.isError ? import_chalk36.default.red(" \u21B3 \u2717") : import_chalk36.default.green(" \u21B3 \u2713");
|
|
19764
19825
|
const label2 = agentLabel(exec.agent, exec.mcpServer);
|
|
19765
19826
|
const tool = (exec.tool ?? "").slice(0, 16);
|
|
19766
|
-
const duration = typeof exec.durationMs === "number" ?
|
|
19827
|
+
const duration = typeof exec.durationMs === "number" ? import_chalk36.default.dim(` (${exec.durationMs}ms)`) : "";
|
|
19767
19828
|
console.log(
|
|
19768
|
-
`${
|
|
19829
|
+
`${import_chalk36.default.gray(time)} ${arrow} ${label2}${import_chalk36.default.dim(tool)}${import_chalk36.default.dim(" completed")}${duration}`
|
|
19769
19830
|
);
|
|
19770
19831
|
}
|
|
19771
19832
|
}
|
|
19772
19833
|
req.on("error", (err2) => {
|
|
19773
19834
|
const msg = err2.code === "ECONNREFUSED" ? "Daemon is not running. Start it with: node9 daemon start" : err2.message;
|
|
19774
|
-
console.error(
|
|
19835
|
+
console.error(import_chalk36.default.red(`
|
|
19775
19836
|
\u274C ${msg}`));
|
|
19776
19837
|
process.exit(1);
|
|
19777
19838
|
});
|
|
19778
19839
|
}
|
|
19779
|
-
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;
|
|
19780
19841
|
var init_tail = __esm({
|
|
19781
19842
|
"src/tui/tail.ts"() {
|
|
19782
19843
|
"use strict";
|
|
19783
|
-
|
|
19784
|
-
|
|
19844
|
+
import_http5 = __toESM(require("http"));
|
|
19845
|
+
import_chalk36 = __toESM(require("chalk"));
|
|
19785
19846
|
import_fs64 = __toESM(require("fs"));
|
|
19786
19847
|
import_os55 = __toESM(require("os"));
|
|
19787
19848
|
import_path62 = __toESM(require("path"));
|
|
@@ -19854,7 +19915,7 @@ function queryDaemon() {
|
|
|
19854
19915
|
return new Promise((resolve) => {
|
|
19855
19916
|
const timeout = setTimeout(() => resolve(null), 50);
|
|
19856
19917
|
try {
|
|
19857
|
-
const req =
|
|
19918
|
+
const req = import_http6.default.get(
|
|
19858
19919
|
`http://${DAEMON_HOST}:${DAEMON_PORT}/status`,
|
|
19859
19920
|
{ timeout: 50 },
|
|
19860
19921
|
(res) => {
|
|
@@ -20182,14 +20243,14 @@ async function main() {
|
|
|
20182
20243
|
renderOffline();
|
|
20183
20244
|
}
|
|
20184
20245
|
}
|
|
20185
|
-
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;
|
|
20186
20247
|
var init_hud = __esm({
|
|
20187
20248
|
"src/cli/hud.ts"() {
|
|
20188
20249
|
"use strict";
|
|
20189
20250
|
import_fs65 = __toESM(require("fs"));
|
|
20190
20251
|
import_path63 = __toESM(require("path"));
|
|
20191
20252
|
import_os56 = __toESM(require("os"));
|
|
20192
|
-
|
|
20253
|
+
import_http6 = __toESM(require("http"));
|
|
20193
20254
|
init_daemon();
|
|
20194
20255
|
RESET3 = "\x1B[0m";
|
|
20195
20256
|
BOLD3 = "\x1B[1m";
|
|
@@ -20246,8 +20307,69 @@ function agentTeardownTargets() {
|
|
|
20246
20307
|
|
|
20247
20308
|
// src/cli.ts
|
|
20248
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
|
|
20249
20371
|
init_daemon2();
|
|
20250
|
-
var
|
|
20372
|
+
var import_chalk37 = __toESM(require("chalk"));
|
|
20251
20373
|
var import_fs66 = __toESM(require("fs"));
|
|
20252
20374
|
var import_path64 = __toESM(require("path"));
|
|
20253
20375
|
var import_os57 = __toESM(require("os"));
|
|
@@ -24122,18 +24244,7 @@ function registerInitCommand(program2) {
|
|
|
24122
24244
|
console.log("");
|
|
24123
24245
|
for (const agent of found) {
|
|
24124
24246
|
console.log(import_chalk16.default.bold(`Wiring ${agent}...`));
|
|
24125
|
-
|
|
24126
|
-
else if (agent === "gemini") await setupGemini();
|
|
24127
|
-
else if (agent === "antigravity") await setupAntigravity();
|
|
24128
|
-
else if (agent === "copilot") await setupCopilot();
|
|
24129
|
-
else if (agent === "cursor") await setupCursor();
|
|
24130
|
-
else if (agent === "codex") await setupCodex();
|
|
24131
|
-
else if (agent === "windsurf") await setupWindsurf();
|
|
24132
|
-
else if (agent === "vscode") await setupVSCode();
|
|
24133
|
-
else if (agent === "claudeDesktop") await setupClaudeDesktop();
|
|
24134
|
-
else if (agent === "opencode") await setupOpencode();
|
|
24135
|
-
else if (agent === "pi") await setupPi();
|
|
24136
|
-
else if (agent === "hermes") setupHermes();
|
|
24247
|
+
await setupAgent(agent);
|
|
24137
24248
|
console.log("");
|
|
24138
24249
|
}
|
|
24139
24250
|
if ((process.platform === "darwin" || process.platform === "linux") && process.stdout.isTTY) {
|
|
@@ -24196,13 +24307,110 @@ function registerInitCommand(program2) {
|
|
|
24196
24307
|
);
|
|
24197
24308
|
}
|
|
24198
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
|
+
|
|
24199
24407
|
// src/cli/commands/undo.ts
|
|
24200
24408
|
var import_path50 = __toESM(require("path"));
|
|
24201
|
-
var
|
|
24409
|
+
var import_chalk19 = __toESM(require("chalk"));
|
|
24202
24410
|
|
|
24203
24411
|
// src/tui/undo-navigator.ts
|
|
24204
24412
|
var import_readline3 = __toESM(require("readline"));
|
|
24205
|
-
var
|
|
24413
|
+
var import_chalk18 = __toESM(require("chalk"));
|
|
24206
24414
|
var RESET = "\x1B[0m";
|
|
24207
24415
|
var BOLD = "\x1B[1m";
|
|
24208
24416
|
var CLEAR_SCREEN = "\x1B[2J\x1B[H";
|
|
@@ -24220,15 +24428,15 @@ function renderDiff(raw) {
|
|
|
24220
24428
|
);
|
|
24221
24429
|
for (const line of lines) {
|
|
24222
24430
|
if (line.startsWith("+++") || line.startsWith("---")) {
|
|
24223
|
-
process.stdout.write(
|
|
24431
|
+
process.stdout.write(import_chalk18.default.bold(line) + "\n");
|
|
24224
24432
|
} else if (line.startsWith("+")) {
|
|
24225
|
-
process.stdout.write(
|
|
24433
|
+
process.stdout.write(import_chalk18.default.green(line) + "\n");
|
|
24226
24434
|
} else if (line.startsWith("-")) {
|
|
24227
|
-
process.stdout.write(
|
|
24435
|
+
process.stdout.write(import_chalk18.default.red(line) + "\n");
|
|
24228
24436
|
} else if (line.startsWith("@@")) {
|
|
24229
|
-
process.stdout.write(
|
|
24437
|
+
process.stdout.write(import_chalk18.default.cyan(line) + "\n");
|
|
24230
24438
|
} else {
|
|
24231
|
-
process.stdout.write(
|
|
24439
|
+
process.stdout.write(import_chalk18.default.gray(line) + "\n");
|
|
24232
24440
|
}
|
|
24233
24441
|
}
|
|
24234
24442
|
}
|
|
@@ -24247,23 +24455,23 @@ function render(entries, idx) {
|
|
|
24247
24455
|
const step = idx + 1;
|
|
24248
24456
|
process.stdout.write(CLEAR_SCREEN);
|
|
24249
24457
|
process.stdout.write(
|
|
24250
|
-
|
|
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(
|
|
24251
24459
|
` \u2500\u2500 ${entry.files.slice(0, 2).join(", ")}${entry.files.length > 2 ? ` +${entry.files.length - 2} more` : ""}`
|
|
24252
24460
|
) : "") + "\n\n"
|
|
24253
24461
|
);
|
|
24254
24462
|
process.stdout.write(
|
|
24255
|
-
` ${BOLD}Tool:${RESET} ${
|
|
24463
|
+
` ${BOLD}Tool:${RESET} ${import_chalk18.default.cyan(entry.tool)}` + (entry.argsSummary ? import_chalk18.default.gray(" \u2192 " + entry.argsSummary) : "") + "\n"
|
|
24256
24464
|
);
|
|
24257
|
-
process.stdout.write(` ${BOLD}When:${RESET} ${
|
|
24465
|
+
process.stdout.write(` ${BOLD}When:${RESET} ${import_chalk18.default.gray(formatAge(entry.timestamp))}
|
|
24258
24466
|
`);
|
|
24259
|
-
process.stdout.write(` ${BOLD}Dir: ${RESET} ${
|
|
24467
|
+
process.stdout.write(` ${BOLD}Dir: ${RESET} ${import_chalk18.default.gray(entry.cwd)}
|
|
24260
24468
|
`);
|
|
24261
24469
|
if (entry.files && entry.files.length > 0) {
|
|
24262
|
-
process.stdout.write(` ${BOLD}Files:${RESET} ${
|
|
24470
|
+
process.stdout.write(` ${BOLD}Files:${RESET} ${import_chalk18.default.gray(entry.files.join(", "))}
|
|
24263
24471
|
`);
|
|
24264
24472
|
}
|
|
24265
24473
|
if (idx < total - 1 && isSessionBoundary(entries, idx + 1)) {
|
|
24266
|
-
process.stdout.write(
|
|
24474
|
+
process.stdout.write(import_chalk18.default.gray("\n \u2500\u2500 session boundary above \u2500\u2500\n"));
|
|
24267
24475
|
}
|
|
24268
24476
|
process.stdout.write("\n");
|
|
24269
24477
|
const diff = entry.diff ?? computeUndoDiff(entry.hash, entry.cwd);
|
|
@@ -24271,12 +24479,12 @@ function render(entries, idx) {
|
|
|
24271
24479
|
renderDiff(diff);
|
|
24272
24480
|
} else {
|
|
24273
24481
|
process.stdout.write(
|
|
24274
|
-
|
|
24482
|
+
import_chalk18.default.gray(" (no diff \u2014 working tree may already match this snapshot)\n")
|
|
24275
24483
|
);
|
|
24276
24484
|
}
|
|
24277
24485
|
process.stdout.write("\n");
|
|
24278
24486
|
process.stdout.write(
|
|
24279
|
-
|
|
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"
|
|
24280
24488
|
);
|
|
24281
24489
|
}
|
|
24282
24490
|
async function runUndoNavigator(entries) {
|
|
@@ -24330,19 +24538,19 @@ async function runUndoNavigator(entries) {
|
|
|
24330
24538
|
cleanup();
|
|
24331
24539
|
process.stdout.write(CLEAR_SCREEN);
|
|
24332
24540
|
const entry = display[idx];
|
|
24333
|
-
process.stdout.write(
|
|
24541
|
+
process.stdout.write(import_chalk18.default.magenta.bold("\n\u23EA Restoring snapshot...\n\n"));
|
|
24334
24542
|
if (applyUndo(entry.hash, entry.cwd)) {
|
|
24335
|
-
process.stdout.write(
|
|
24543
|
+
process.stdout.write(import_chalk18.default.green("\u2705 Reverted successfully.\n\n"));
|
|
24336
24544
|
resolve({ restored: true });
|
|
24337
24545
|
} else {
|
|
24338
|
-
process.stdout.write(
|
|
24546
|
+
process.stdout.write(import_chalk18.default.red("\u274C Undo failed.\n\n"));
|
|
24339
24547
|
resolve({ restored: false });
|
|
24340
24548
|
}
|
|
24341
24549
|
} else if (name === "q" || key?.ctrl && name === "c") {
|
|
24342
24550
|
done = true;
|
|
24343
24551
|
cleanup();
|
|
24344
24552
|
process.stdout.write(CLEAR_SCREEN);
|
|
24345
|
-
process.stdout.write(
|
|
24553
|
+
process.stdout.write(import_chalk18.default.gray("\nCancelled.\n\n"));
|
|
24346
24554
|
resolve({ restored: false });
|
|
24347
24555
|
}
|
|
24348
24556
|
};
|
|
@@ -24378,39 +24586,39 @@ function registerUndoCommand(program2) {
|
|
|
24378
24586
|
if (history.length === 0) {
|
|
24379
24587
|
if (!options.all && allHistory.length > 0) {
|
|
24380
24588
|
console.log(
|
|
24381
|
-
|
|
24589
|
+
import_chalk19.default.yellow(
|
|
24382
24590
|
`
|
|
24383
24591
|
\u2139\uFE0F No snapshots found for the current directory (${process.cwd()}).
|
|
24384
|
-
Run ${
|
|
24592
|
+
Run ${import_chalk19.default.cyan("node9 undo --all")} to see snapshots from all projects.
|
|
24385
24593
|
`
|
|
24386
24594
|
)
|
|
24387
24595
|
);
|
|
24388
24596
|
} else {
|
|
24389
|
-
console.log(
|
|
24597
|
+
console.log(import_chalk19.default.yellow("\n\u2139\uFE0F No undo snapshots found.\n"));
|
|
24390
24598
|
}
|
|
24391
24599
|
return;
|
|
24392
24600
|
}
|
|
24393
24601
|
if (options.list) {
|
|
24394
|
-
console.log(
|
|
24602
|
+
console.log(import_chalk19.default.magenta.bold("\n\u23EA Snapshot History\n"));
|
|
24395
24603
|
console.log(
|
|
24396
|
-
|
|
24604
|
+
import_chalk19.default.gray(
|
|
24397
24605
|
` ${"#".padEnd(3)} ${"File / Command".padEnd(30)} ${"Tool".padEnd(8)} ${"When".padEnd(10)} Dir`
|
|
24398
24606
|
)
|
|
24399
24607
|
);
|
|
24400
|
-
console.log(
|
|
24608
|
+
console.log(import_chalk19.default.gray(" " + "\u2500".repeat(80)));
|
|
24401
24609
|
const display = [...history].reverse();
|
|
24402
24610
|
let prevTs = null;
|
|
24403
24611
|
for (let i = 0; i < display.length; i++) {
|
|
24404
24612
|
const e = display[i];
|
|
24405
24613
|
const isGap = prevTs !== null && prevTs - e.timestamp > 6e4;
|
|
24406
|
-
if (isGap) console.log(
|
|
24614
|
+
if (isGap) console.log(import_chalk19.default.gray(" \u2500\u2500 earlier \u2500\u2500"));
|
|
24407
24615
|
const label2 = (e.argsSummary || e.files?.[0] || "\u2014").slice(0, 30).padEnd(30);
|
|
24408
24616
|
const tool = e.tool.slice(0, 8).padEnd(8);
|
|
24409
24617
|
const when = formatAge2(e.timestamp).padEnd(10);
|
|
24410
24618
|
const dir = e.cwd.length > 30 ? "\u2026" + e.cwd.slice(-29) : e.cwd;
|
|
24411
24619
|
console.log(
|
|
24412
|
-
|
|
24413
|
-
` ${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)}`
|
|
24414
24622
|
)
|
|
24415
24623
|
);
|
|
24416
24624
|
prevTs = e.timestamp;
|
|
@@ -24423,7 +24631,7 @@ function registerUndoCommand(program2) {
|
|
|
24423
24631
|
const idx = history.length - steps;
|
|
24424
24632
|
if (idx < 0) {
|
|
24425
24633
|
console.log(
|
|
24426
|
-
|
|
24634
|
+
import_chalk19.default.yellow(
|
|
24427
24635
|
`
|
|
24428
24636
|
\u2139\uFE0F Only ${history.length} snapshot(s) available, cannot go back ${steps}.
|
|
24429
24637
|
`
|
|
@@ -24434,47 +24642,47 @@ function registerUndoCommand(program2) {
|
|
|
24434
24642
|
const snapshot = history[idx];
|
|
24435
24643
|
const ageStr = formatAge2(snapshot.timestamp);
|
|
24436
24644
|
console.log(
|
|
24437
|
-
|
|
24645
|
+
import_chalk19.default.magenta.bold(`
|
|
24438
24646
|
\u23EA Node9 Undo${steps > 1 ? ` (${steps} steps back)` : ""}`)
|
|
24439
24647
|
);
|
|
24440
24648
|
console.log(
|
|
24441
|
-
|
|
24442
|
-
` Tool: ${
|
|
24649
|
+
import_chalk19.default.white(
|
|
24650
|
+
` Tool: ${import_chalk19.default.cyan(snapshot.tool)}${snapshot.argsSummary ? import_chalk19.default.gray(" \u2192 " + snapshot.argsSummary) : ""}`
|
|
24443
24651
|
)
|
|
24444
24652
|
);
|
|
24445
|
-
console.log(
|
|
24446
|
-
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)}`));
|
|
24447
24655
|
if (steps > 1)
|
|
24448
24656
|
console.log(
|
|
24449
|
-
|
|
24657
|
+
import_chalk19.default.yellow(` Note: This will also undo the ${steps - 1} action(s) after it.`)
|
|
24450
24658
|
);
|
|
24451
24659
|
console.log("");
|
|
24452
24660
|
const diff = snapshot.diff ?? computeUndoDiff(snapshot.hash, snapshot.cwd);
|
|
24453
24661
|
if (diff) {
|
|
24454
24662
|
const lines = diff.split("\n").filter((l) => !l.startsWith("diff --git") && !l.startsWith("index "));
|
|
24455
24663
|
for (const line of lines) {
|
|
24456
|
-
if (line.startsWith("+++") || line.startsWith("---")) console.log(
|
|
24457
|
-
else if (line.startsWith("+")) console.log(
|
|
24458
|
-
else if (line.startsWith("-")) console.log(
|
|
24459
|
-
else if (line.startsWith("@@")) console.log(
|
|
24460
|
-
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));
|
|
24461
24669
|
}
|
|
24462
24670
|
console.log("");
|
|
24463
24671
|
} else {
|
|
24464
24672
|
console.log(
|
|
24465
|
-
|
|
24673
|
+
import_chalk19.default.gray(" (no diff available \u2014 working tree may already match snapshot)\n")
|
|
24466
24674
|
);
|
|
24467
24675
|
}
|
|
24468
24676
|
const { confirm: confirm3 } = await import("@inquirer/prompts");
|
|
24469
24677
|
const proceed = await confirm3({ message: `Revert to this snapshot?`, default: false });
|
|
24470
24678
|
if (proceed) {
|
|
24471
24679
|
if (applyUndo(snapshot.hash, snapshot.cwd)) {
|
|
24472
|
-
console.log(
|
|
24680
|
+
console.log(import_chalk19.default.green("\n\u2705 Reverted successfully.\n"));
|
|
24473
24681
|
} else {
|
|
24474
|
-
console.error(
|
|
24682
|
+
console.error(import_chalk19.default.red("\n\u274C Undo failed. Ensure you are in a Git repository.\n"));
|
|
24475
24683
|
}
|
|
24476
24684
|
} else {
|
|
24477
|
-
console.log(
|
|
24685
|
+
console.log(import_chalk19.default.gray("\nCancelled.\n"));
|
|
24478
24686
|
}
|
|
24479
24687
|
return;
|
|
24480
24688
|
}
|
|
@@ -24484,7 +24692,7 @@ function registerUndoCommand(program2) {
|
|
|
24484
24692
|
|
|
24485
24693
|
// src/mcp-gateway/index.ts
|
|
24486
24694
|
var import_readline4 = __toESM(require("readline"));
|
|
24487
|
-
var
|
|
24695
|
+
var import_chalk20 = __toESM(require("chalk"));
|
|
24488
24696
|
var import_child_process10 = require("child_process");
|
|
24489
24697
|
var import_execa3 = require("execa");
|
|
24490
24698
|
init_orchestrator();
|
|
@@ -24620,13 +24828,13 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
24620
24828
|
const prov = checkProvenance(executable);
|
|
24621
24829
|
if (prov.trustLevel === "suspect") {
|
|
24622
24830
|
console.error(
|
|
24623
|
-
|
|
24831
|
+
import_chalk20.default.red(
|
|
24624
24832
|
`\u26A0\uFE0F Node9: Upstream MCP server binary is suspect \u2014 ${prov.reason} (${prov.resolvedPath})`
|
|
24625
24833
|
)
|
|
24626
24834
|
);
|
|
24627
|
-
console.error(
|
|
24835
|
+
console.error(import_chalk20.default.red(" Verify this binary is trusted before proceeding."));
|
|
24628
24836
|
}
|
|
24629
|
-
console.error(
|
|
24837
|
+
console.error(import_chalk20.default.green(`\u{1F680} Node9 MCP Gateway: Monitoring [${upstreamCommand}]`));
|
|
24630
24838
|
const UPSTREAM_INJECTOR_VARS = /* @__PURE__ */ new Set([
|
|
24631
24839
|
"NODE_OPTIONS",
|
|
24632
24840
|
"NODE_PATH",
|
|
@@ -24739,10 +24947,10 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
24739
24947
|
mcpServer
|
|
24740
24948
|
});
|
|
24741
24949
|
if (!result.approved) {
|
|
24742
|
-
console.error(
|
|
24950
|
+
console.error(import_chalk20.default.red(`
|
|
24743
24951
|
\u{1F6D1} Node9 MCP Gateway: Action Blocked`));
|
|
24744
|
-
console.error(
|
|
24745
|
-
console.error(
|
|
24952
|
+
console.error(import_chalk20.default.gray(` Tool: ${toolName}`));
|
|
24953
|
+
console.error(import_chalk20.default.gray(` Reason: ${result.reason ?? "Security Policy"}
|
|
24746
24954
|
`));
|
|
24747
24955
|
const blockedByLabel = result.blockedByLabel ?? result.reason ?? "Security Policy";
|
|
24748
24956
|
const isHumanDecision = blockedByLabel.toLowerCase().includes("user") || blockedByLabel.toLowerCase().includes("daemon") || blockedByLabel.toLowerCase().includes("decision");
|
|
@@ -24855,7 +25063,7 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
24855
25063
|
updatePin(serverKey, upstreamCommand, currentHash, toolNames);
|
|
24856
25064
|
pinState = "validated";
|
|
24857
25065
|
console.error(
|
|
24858
|
-
|
|
25066
|
+
import_chalk20.default.green(
|
|
24859
25067
|
`\u{1F512} Node9: Pinned ${toolNames.length} tool definition(s) for this MCP server`
|
|
24860
25068
|
)
|
|
24861
25069
|
);
|
|
@@ -24868,11 +25076,11 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
24868
25076
|
} else if (pinStatus === "corrupt") {
|
|
24869
25077
|
pinState = "quarantined";
|
|
24870
25078
|
console.error(
|
|
24871
|
-
|
|
25079
|
+
import_chalk20.default.red("\n\u{1F6A8} Node9: MCP pin file is corrupt or unreadable \u2014 session quarantined!")
|
|
24872
25080
|
);
|
|
24873
|
-
console.error(
|
|
25081
|
+
console.error(import_chalk20.default.red(" Tool calls are blocked until the pin file is repaired."));
|
|
24874
25082
|
console.error(
|
|
24875
|
-
|
|
25083
|
+
import_chalk20.default.yellow(` Run: node9 mcp pin reset (to clear and re-pin on next connect)
|
|
24876
25084
|
`)
|
|
24877
25085
|
);
|
|
24878
25086
|
const errorResponse = {
|
|
@@ -24889,13 +25097,13 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
24889
25097
|
} else {
|
|
24890
25098
|
pinState = "quarantined";
|
|
24891
25099
|
console.error(
|
|
24892
|
-
|
|
25100
|
+
import_chalk20.default.red("\n\u{1F6A8} Node9: MCP tool definitions have changed since last verified!")
|
|
24893
25101
|
);
|
|
24894
25102
|
console.error(
|
|
24895
|
-
|
|
25103
|
+
import_chalk20.default.red(" This could indicate a supply chain attack (tool poisoning / rug pull).")
|
|
24896
25104
|
);
|
|
24897
|
-
console.error(
|
|
24898
|
-
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}
|
|
24899
25107
|
`));
|
|
24900
25108
|
reportPinMismatchToCloud(serverKey, clientName);
|
|
24901
25109
|
const errorResponse = {
|
|
@@ -24939,7 +25147,7 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
24939
25147
|
const toolName = callId !== void 0 ? pendingCallNames.get(callId) ?? "unknown" : "unknown";
|
|
24940
25148
|
if (callId !== void 0) pendingCallNames.delete(callId);
|
|
24941
25149
|
console.error(
|
|
24942
|
-
|
|
25150
|
+
import_chalk20.default.yellow(
|
|
24943
25151
|
`\u26A1 Node9: Large MCP response from '${toolName}' (${(line.length / 1024).toFixed(0)}KB) \u2014 context window enlarged`
|
|
24944
25152
|
)
|
|
24945
25153
|
);
|
|
@@ -25906,7 +26114,7 @@ function registerMcpServerCommand(program2) {
|
|
|
25906
26114
|
}
|
|
25907
26115
|
|
|
25908
26116
|
// src/cli/commands/trust.ts
|
|
25909
|
-
var
|
|
26117
|
+
var import_chalk21 = __toESM(require("chalk"));
|
|
25910
26118
|
init_trusted_hosts();
|
|
25911
26119
|
function isValidHost(host) {
|
|
25912
26120
|
return /^(\*\.)?[a-z0-9][a-z0-9.-]*\.[a-z]{2,}$/.test(host);
|
|
@@ -25917,51 +26125,51 @@ function registerTrustCommand(program2) {
|
|
|
25917
26125
|
const normalized = normalizeHost(host.trim());
|
|
25918
26126
|
if (!isValidHost(normalized)) {
|
|
25919
26127
|
console.error(
|
|
25920
|
-
|
|
26128
|
+
import_chalk21.default.red(`
|
|
25921
26129
|
\u274C Invalid host: "${host}"
|
|
25922
|
-
`) +
|
|
26130
|
+
`) + import_chalk21.default.gray(" Use an FQDN like api.mycompany.com or *.mycompany.com\n")
|
|
25923
26131
|
);
|
|
25924
26132
|
process.exit(1);
|
|
25925
26133
|
}
|
|
25926
26134
|
addTrustedHost(normalized);
|
|
25927
|
-
console.log(
|
|
26135
|
+
console.log(import_chalk21.default.green(`
|
|
25928
26136
|
\u2705 ${normalized} added to trusted hosts.`));
|
|
25929
26137
|
console.log(
|
|
25930
|
-
|
|
26138
|
+
import_chalk21.default.gray(" Pipe-chain blocks to this host: critical \u2192 review, high \u2192 allow\n")
|
|
25931
26139
|
);
|
|
25932
26140
|
});
|
|
25933
26141
|
trustCmd.command("remove <host>").description("Remove a trusted host").action((host) => {
|
|
25934
26142
|
const normalized = normalizeHost(host.trim());
|
|
25935
26143
|
const removed = removeTrustedHost(normalized);
|
|
25936
26144
|
if (!removed) {
|
|
25937
|
-
console.error(
|
|
26145
|
+
console.error(import_chalk21.default.yellow(`
|
|
25938
26146
|
\u26A0\uFE0F "${normalized}" is not in the trusted hosts list.
|
|
25939
26147
|
`));
|
|
25940
26148
|
process.exit(1);
|
|
25941
26149
|
}
|
|
25942
|
-
console.log(
|
|
26150
|
+
console.log(import_chalk21.default.green(`
|
|
25943
26151
|
\u2705 ${normalized} removed from trusted hosts.
|
|
25944
26152
|
`));
|
|
25945
26153
|
});
|
|
25946
26154
|
trustCmd.command("list").description("Show all trusted hosts").action(() => {
|
|
25947
26155
|
const hosts = readTrustedHosts();
|
|
25948
26156
|
if (hosts.length === 0) {
|
|
25949
|
-
console.log(
|
|
25950
|
-
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")}
|
|
25951
26159
|
`);
|
|
25952
26160
|
return;
|
|
25953
26161
|
}
|
|
25954
|
-
console.log(
|
|
26162
|
+
console.log(import_chalk21.default.bold("\n\u{1F513} Trusted Hosts\n"));
|
|
25955
26163
|
for (const entry of hosts) {
|
|
25956
26164
|
const date = new Date(entry.addedAt).toLocaleDateString();
|
|
25957
|
-
console.log(` ${
|
|
26165
|
+
console.log(` ${import_chalk21.default.cyan(entry.host.padEnd(40))} ${import_chalk21.default.gray(`added ${date}`)}`);
|
|
25958
26166
|
}
|
|
25959
26167
|
console.log("");
|
|
25960
26168
|
});
|
|
25961
26169
|
}
|
|
25962
26170
|
|
|
25963
26171
|
// src/cli/commands/mcp-pin.ts
|
|
25964
|
-
var
|
|
26172
|
+
var import_chalk22 = __toESM(require("chalk"));
|
|
25965
26173
|
init_mcp_pin();
|
|
25966
26174
|
var import_fs54 = __toESM(require("fs"));
|
|
25967
26175
|
function registerMcpPinCommand(program2) {
|
|
@@ -25982,14 +26190,14 @@ function registerMcpPinCommand(program2) {
|
|
|
25982
26190
|
}
|
|
25983
26191
|
}
|
|
25984
26192
|
if (repoCorrupt) {
|
|
25985
|
-
console.error(
|
|
26193
|
+
console.error(import_chalk22.default.red(`
|
|
25986
26194
|
\u274C Repo pin file at ${found.path} is corrupt or unreadable.`));
|
|
25987
26195
|
process.exit(1);
|
|
25988
26196
|
}
|
|
25989
26197
|
if (!homeResult.ok && homeResult.reason === "corrupt") {
|
|
25990
|
-
console.error(
|
|
26198
|
+
console.error(import_chalk22.default.red(`
|
|
25991
26199
|
\u274C Home pin file is corrupt: ${homeResult.detail}`));
|
|
25992
|
-
console.error(
|
|
26200
|
+
console.error(import_chalk22.default.yellow(" Run: node9 mcp pin reset\n"));
|
|
25993
26201
|
process.exit(1);
|
|
25994
26202
|
}
|
|
25995
26203
|
const homeEntries = homeResult.ok ? homeResult.pins.servers : {};
|
|
@@ -26001,25 +26209,25 @@ function registerMcpPinCommand(program2) {
|
|
|
26001
26209
|
merged.set(key, { entry, source: "repo" });
|
|
26002
26210
|
}
|
|
26003
26211
|
if (merged.size === 0) {
|
|
26004
|
-
console.log(
|
|
26212
|
+
console.log(import_chalk22.default.gray("\nNo MCP servers are pinned yet."));
|
|
26005
26213
|
console.log(
|
|
26006
|
-
|
|
26214
|
+
import_chalk22.default.gray("Pins are created automatically when the MCP gateway first connects.\n")
|
|
26007
26215
|
);
|
|
26008
26216
|
return;
|
|
26009
26217
|
}
|
|
26010
|
-
console.log(
|
|
26218
|
+
console.log(import_chalk22.default.bold("\n\u{1F512} Pinned MCP Servers\n"));
|
|
26011
26219
|
const showSource = found.source === "repo";
|
|
26012
26220
|
for (const [key, { entry, source }] of merged) {
|
|
26013
|
-
const tag = showSource ? ` ${
|
|
26014
|
-
console.log(` ${
|
|
26015
|
-
console.log(` Tools (${entry.toolCount}): ${
|
|
26016
|
-
console.log(` Hash: ${
|
|
26017
|
-
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)}`);
|
|
26018
26226
|
console.log("");
|
|
26019
26227
|
}
|
|
26020
26228
|
if (showSource) {
|
|
26021
|
-
console.log(
|
|
26022
|
-
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"));
|
|
26023
26231
|
}
|
|
26024
26232
|
});
|
|
26025
26233
|
pinSubCmd.command("promote <serverKey>").description(
|
|
@@ -26029,22 +26237,22 @@ function registerMcpPinCommand(program2) {
|
|
|
26029
26237
|
const { repoPath, created } = promotePin(serverKey, process.cwd());
|
|
26030
26238
|
if (created) {
|
|
26031
26239
|
console.log(
|
|
26032
|
-
|
|
26240
|
+
import_chalk22.default.green(
|
|
26033
26241
|
`
|
|
26034
|
-
\u2705 Created ${repoPath} with the promoted pin for ${
|
|
26242
|
+
\u2705 Created ${repoPath} with the promoted pin for ${import_chalk22.default.cyan(serverKey)}.`
|
|
26035
26243
|
)
|
|
26036
26244
|
);
|
|
26037
26245
|
} else {
|
|
26038
|
-
console.log(
|
|
26039
|
-
\u2705 Promoted ${
|
|
26246
|
+
console.log(import_chalk22.default.green(`
|
|
26247
|
+
\u2705 Promoted ${import_chalk22.default.cyan(serverKey)} into ${repoPath}.`));
|
|
26040
26248
|
}
|
|
26041
|
-
console.log(
|
|
26042
|
-
console.log(
|
|
26043
|
-
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)"`));
|
|
26044
26252
|
console.log("");
|
|
26045
26253
|
} catch (err2) {
|
|
26046
26254
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
26047
|
-
console.error(
|
|
26255
|
+
console.error(import_chalk22.default.red(`
|
|
26048
26256
|
\u274C ${msg}
|
|
26049
26257
|
`));
|
|
26050
26258
|
process.exit(1);
|
|
@@ -26057,138 +26265,138 @@ function registerMcpPinCommand(program2) {
|
|
|
26057
26265
|
try {
|
|
26058
26266
|
pins = readMcpPins();
|
|
26059
26267
|
} catch {
|
|
26060
|
-
console.error(
|
|
26061
|
-
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"));
|
|
26062
26270
|
process.exit(1);
|
|
26063
26271
|
}
|
|
26064
26272
|
if (!pins.servers[serverKey]) {
|
|
26065
|
-
console.error(
|
|
26273
|
+
console.error(import_chalk22.default.red(`
|
|
26066
26274
|
\u274C No pin found for server key "${serverKey}"
|
|
26067
26275
|
`));
|
|
26068
|
-
console.error(`Run ${
|
|
26276
|
+
console.error(`Run ${import_chalk22.default.cyan("node9 mcp pin list")} to see pinned servers.
|
|
26069
26277
|
`);
|
|
26070
26278
|
process.exit(1);
|
|
26071
26279
|
}
|
|
26072
26280
|
const label2 = pins.servers[serverKey].label;
|
|
26073
26281
|
removePin(serverKey);
|
|
26074
|
-
console.log(
|
|
26075
|
-
\u{1F513} Pin removed for ${
|
|
26076
|
-
console.log(
|
|
26077
|
-
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"));
|
|
26078
26286
|
});
|
|
26079
26287
|
pinSubCmd.command("reset").description("Clear all MCP pins (next connection to each server will re-pin)").action(() => {
|
|
26080
26288
|
const result = readMcpPinsSafe();
|
|
26081
26289
|
if (!result.ok && result.reason === "missing") {
|
|
26082
|
-
console.log(
|
|
26290
|
+
console.log(import_chalk22.default.gray("\nNo pins to clear.\n"));
|
|
26083
26291
|
return;
|
|
26084
26292
|
}
|
|
26085
26293
|
const count = result.ok ? Object.keys(result.pins.servers).length : "?";
|
|
26086
26294
|
clearAllPins();
|
|
26087
|
-
console.log(
|
|
26295
|
+
console.log(import_chalk22.default.green(`
|
|
26088
26296
|
\u{1F513} Cleared ${count} MCP pin(s).`));
|
|
26089
|
-
console.log(
|
|
26297
|
+
console.log(import_chalk22.default.gray(" Next connection to each server will re-pin.\n"));
|
|
26090
26298
|
});
|
|
26091
26299
|
}
|
|
26092
26300
|
|
|
26093
26301
|
// src/cli/commands/sync.ts
|
|
26094
|
-
var
|
|
26302
|
+
var import_chalk23 = __toESM(require("chalk"));
|
|
26095
26303
|
init_sync();
|
|
26096
26304
|
function registerSyncCommand(program2) {
|
|
26097
26305
|
const policy = program2.command("policy").description("Manage cloud policy rules");
|
|
26098
26306
|
policy.command("push").description("Push this machine's effective policy to the node9 dashboard").action(async () => {
|
|
26099
|
-
process.stdout.write(
|
|
26307
|
+
process.stdout.write(import_chalk23.default.cyan("Pushing policy to the dashboard\u2026"));
|
|
26100
26308
|
const result = await runPolicyPush();
|
|
26101
26309
|
process.stdout.write("\n");
|
|
26102
26310
|
if (!result.ok) {
|
|
26103
|
-
console.error(
|
|
26311
|
+
console.error(import_chalk23.default.red(`\u2717 ${result.reason}`));
|
|
26104
26312
|
process.exit(1);
|
|
26105
26313
|
}
|
|
26106
|
-
console.log(
|
|
26107
|
-
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"));
|
|
26108
26316
|
});
|
|
26109
26317
|
policy.command("sync").description("Sync cloud policy rules to local cache (~/.node9/rules-cache.json)").action(async () => {
|
|
26110
|
-
process.stdout.write(
|
|
26318
|
+
process.stdout.write(import_chalk23.default.cyan("Syncing cloud policy rules\u2026"));
|
|
26111
26319
|
const result = await runCloudSync();
|
|
26112
26320
|
process.stdout.write("\n");
|
|
26113
26321
|
if (!result.ok) {
|
|
26114
|
-
console.error(
|
|
26322
|
+
console.error(import_chalk23.default.red(`\u2717 ${result.reason}`));
|
|
26115
26323
|
process.exit(1);
|
|
26116
26324
|
}
|
|
26117
26325
|
if (result.unchanged) {
|
|
26118
26326
|
console.log(
|
|
26119
|
-
|
|
26327
|
+
import_chalk23.default.green(
|
|
26120
26328
|
`\u2713 Already up to date \u2014 ${result.rules} rule${result.rules === 1 ? "" : "s"} cached`
|
|
26121
26329
|
)
|
|
26122
26330
|
);
|
|
26123
|
-
console.log(
|
|
26124
|
-
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)`));
|
|
26125
26333
|
} else {
|
|
26126
26334
|
console.log(
|
|
26127
|
-
|
|
26335
|
+
import_chalk23.default.green(`\u2713 Synced ${result.rules} rule${result.rules === 1 ? "" : "s"} from cloud`)
|
|
26128
26336
|
);
|
|
26129
|
-
console.log(
|
|
26130
|
-
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`));
|
|
26131
26339
|
}
|
|
26132
26340
|
});
|
|
26133
26341
|
policy.command("show").description("List all cloud policy rules in the local cache").action(() => {
|
|
26134
26342
|
const status = getCloudSyncStatus();
|
|
26135
26343
|
if (!status.cached) {
|
|
26136
|
-
console.log(
|
|
26344
|
+
console.log(import_chalk23.default.yellow("\n No cloud rules cached \u2014 run: node9 policy sync\n"));
|
|
26137
26345
|
return;
|
|
26138
26346
|
}
|
|
26139
26347
|
const rules = getCloudRules() ?? [];
|
|
26140
26348
|
const age = Math.round((Date.now() - new Date(status.fetchedAt).getTime()) / 6e4);
|
|
26141
26349
|
console.log(
|
|
26142
|
-
|
|
26143
|
-
Cloud policy rules`) +
|
|
26350
|
+
import_chalk23.default.bold(`
|
|
26351
|
+
Cloud policy rules`) + import_chalk23.default.gray(
|
|
26144
26352
|
` (${rules.length} rule${rules.length === 1 ? "" : "s"}, synced ${age}m ago)
|
|
26145
26353
|
`
|
|
26146
26354
|
)
|
|
26147
26355
|
);
|
|
26148
26356
|
if (rules.length === 0) {
|
|
26149
|
-
console.log(
|
|
26357
|
+
console.log(import_chalk23.default.gray(" No rules defined in cloud policy.\n"));
|
|
26150
26358
|
return;
|
|
26151
26359
|
}
|
|
26152
26360
|
for (const rule of rules) {
|
|
26153
26361
|
const r = rule;
|
|
26154
|
-
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;
|
|
26155
26363
|
console.log(
|
|
26156
26364
|
` ${verdictColor(
|
|
26157
26365
|
String(r.verdict ?? "unknown").toUpperCase().padEnd(6)
|
|
26158
|
-
)} ${
|
|
26366
|
+
)} ${import_chalk23.default.white(String(r.name ?? "(unnamed)"))}`
|
|
26159
26367
|
);
|
|
26160
|
-
if (r.reason) console.log(
|
|
26368
|
+
if (r.reason) console.log(import_chalk23.default.gray(` ${String(r.reason)}`));
|
|
26161
26369
|
}
|
|
26162
26370
|
console.log("");
|
|
26163
26371
|
});
|
|
26164
26372
|
policy.command("status").description("Show current cloud policy cache status").action(() => {
|
|
26165
26373
|
const s = getCloudSyncStatus();
|
|
26166
26374
|
if (!s.cached) {
|
|
26167
|
-
console.log(
|
|
26375
|
+
console.log(import_chalk23.default.yellow("\n No cache yet \u2014 run: node9 policy sync\n"));
|
|
26168
26376
|
return;
|
|
26169
26377
|
}
|
|
26170
26378
|
const age = Math.round((Date.now() - new Date(s.fetchedAt).getTime()) / 6e4);
|
|
26171
26379
|
console.log(`
|
|
26172
|
-
Rules : ${
|
|
26380
|
+
Rules : ${import_chalk23.default.green(String(s.rules))} cloud rules loaded`);
|
|
26173
26381
|
console.log(
|
|
26174
|
-
` Synced : ${
|
|
26382
|
+
` Synced : ${import_chalk23.default.gray(`${age} minute${age === 1 ? "" : "s"} ago`)} (${s.fetchedAt})`
|
|
26175
26383
|
);
|
|
26176
26384
|
if (s.workspaceId) {
|
|
26177
|
-
console.log(` Workspace: ${
|
|
26385
|
+
console.log(` Workspace: ${import_chalk23.default.gray(s.workspaceId)}`);
|
|
26178
26386
|
}
|
|
26179
26387
|
if (s.panicMode) {
|
|
26180
26388
|
console.log(
|
|
26181
|
-
` ${
|
|
26389
|
+
` ${import_chalk23.default.red.bold("\u{1F6A8} Panic mode : ON")} ` + import_chalk23.default.dim("(every review-verdict becomes block)")
|
|
26182
26390
|
);
|
|
26183
26391
|
}
|
|
26184
26392
|
if (s.shadowMode) {
|
|
26185
26393
|
console.log(
|
|
26186
|
-
` ${
|
|
26394
|
+
` ${import_chalk23.default.yellow.bold("\u{1F441} Shadow mode : ON")} ` + import_chalk23.default.dim("(blocks become would-block log entries)")
|
|
26187
26395
|
);
|
|
26188
26396
|
}
|
|
26189
26397
|
if (s.syncIntervalHours) {
|
|
26190
26398
|
console.log(
|
|
26191
|
-
|
|
26399
|
+
import_chalk23.default.gray(
|
|
26192
26400
|
` Polling : every ${s.syncIntervalHours} hour${s.syncIntervalHours === 1 ? "" : "s"}`
|
|
26193
26401
|
)
|
|
26194
26402
|
);
|
|
@@ -26198,7 +26406,7 @@ function registerSyncCommand(program2) {
|
|
|
26198
26406
|
}
|
|
26199
26407
|
|
|
26200
26408
|
// src/cli/commands/agents.ts
|
|
26201
|
-
var
|
|
26409
|
+
var import_chalk24 = __toESM(require("chalk"));
|
|
26202
26410
|
init_setup();
|
|
26203
26411
|
var SETUP_FN = {
|
|
26204
26412
|
claude: setupClaude,
|
|
@@ -26242,30 +26450,30 @@ function registerAgentsCommand(program2) {
|
|
|
26242
26450
|
console.log(` ${"Agent".padEnd(14)}${"Installed".padEnd(11)}${"Wired".padEnd(8)}Mode`);
|
|
26243
26451
|
console.log(" " + "\u2500".repeat(44));
|
|
26244
26452
|
for (const s of statuses) {
|
|
26245
|
-
const installed = s.installed ?
|
|
26246
|
-
const wired = !s.installed ?
|
|
26247
|
-
const mode = s.mode ?
|
|
26248
|
-
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}`) : "";
|
|
26249
26457
|
console.log(` ${s.label.padEnd(14)}${installed} ${wired} ${mode}${hint}`);
|
|
26250
26458
|
}
|
|
26251
26459
|
console.log("");
|
|
26252
26460
|
if (!anyInstalled) {
|
|
26253
26461
|
console.log(
|
|
26254
|
-
|
|
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")
|
|
26255
26463
|
);
|
|
26256
26464
|
return;
|
|
26257
26465
|
}
|
|
26258
26466
|
const unwired = statuses.filter((s) => s.installed && !s.wired);
|
|
26259
26467
|
if (unwired.length > 0) {
|
|
26260
26468
|
console.log(
|
|
26261
|
-
|
|
26469
|
+
import_chalk24.default.yellow(` ${unwired.length} agent(s) not yet wired. Run: `) + import_chalk24.default.white(`node9 agents add ${unwired[0].name}`) + "\n"
|
|
26262
26470
|
);
|
|
26263
26471
|
}
|
|
26264
26472
|
if (statuses.some((s) => s.name === "gemini" && s.installed)) {
|
|
26265
26473
|
console.log(
|
|
26266
|
-
|
|
26474
|
+
import_chalk24.default.yellow(
|
|
26267
26475
|
" \u26A0\uFE0F Gemini CLI stops serving AI Pro/Ultra and free tiers on 2026-06-18.\n"
|
|
26268
|
-
) +
|
|
26476
|
+
) + import_chalk24.default.gray(" Migrate to Antigravity: ") + import_chalk24.default.white("node9 agents add antigravity") + "\n"
|
|
26269
26477
|
);
|
|
26270
26478
|
}
|
|
26271
26479
|
});
|
|
@@ -26273,7 +26481,7 @@ function registerAgentsCommand(program2) {
|
|
|
26273
26481
|
const name = resolveAgentName(agent);
|
|
26274
26482
|
const fn = SETUP_FN[name];
|
|
26275
26483
|
if (!fn) {
|
|
26276
|
-
console.error(
|
|
26484
|
+
console.error(import_chalk24.default.red(`Unknown agent: "${agent}". Supported: ${AGENT_NAMES.join(", ")}`));
|
|
26277
26485
|
process.exit(1);
|
|
26278
26486
|
}
|
|
26279
26487
|
await fn();
|
|
@@ -26282,14 +26490,14 @@ function registerAgentsCommand(program2) {
|
|
|
26282
26490
|
const name = resolveAgentName(agent);
|
|
26283
26491
|
const fn = TEARDOWN_FN[name];
|
|
26284
26492
|
if (!fn) {
|
|
26285
|
-
console.error(
|
|
26493
|
+
console.error(import_chalk24.default.red(`Unknown agent: "${agent}". Supported: ${AGENT_NAMES.join(", ")}`));
|
|
26286
26494
|
process.exit(1);
|
|
26287
26495
|
}
|
|
26288
|
-
console.log(
|
|
26496
|
+
console.log(import_chalk24.default.cyan(`
|
|
26289
26497
|
\u{1F6E1}\uFE0F Node9: removing from ${name}...
|
|
26290
26498
|
`));
|
|
26291
26499
|
fn();
|
|
26292
|
-
console.log(
|
|
26500
|
+
console.log(import_chalk24.default.gray("\n Restart the agent for changes to take effect."));
|
|
26293
26501
|
});
|
|
26294
26502
|
}
|
|
26295
26503
|
|
|
@@ -26297,22 +26505,22 @@ function registerAgentsCommand(program2) {
|
|
|
26297
26505
|
init_scan();
|
|
26298
26506
|
|
|
26299
26507
|
// src/cli/commands/posture.ts
|
|
26300
|
-
var
|
|
26508
|
+
var import_chalk26 = __toESM(require("chalk"));
|
|
26301
26509
|
init_posture();
|
|
26302
26510
|
|
|
26303
26511
|
// src/posture/render.ts
|
|
26304
|
-
var
|
|
26512
|
+
var import_chalk25 = __toESM(require("chalk"));
|
|
26305
26513
|
init_score();
|
|
26306
26514
|
var ICON = {
|
|
26307
|
-
critical:
|
|
26308
|
-
high:
|
|
26309
|
-
medium:
|
|
26310
|
-
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 ")
|
|
26311
26519
|
};
|
|
26312
26520
|
var TIER_LABEL = {
|
|
26313
|
-
good:
|
|
26314
|
-
"at-risk":
|
|
26315
|
-
critical:
|
|
26521
|
+
good: import_chalk25.default.green("Good"),
|
|
26522
|
+
"at-risk": import_chalk25.default.yellow("At risk"),
|
|
26523
|
+
critical: import_chalk25.default.red("Critical")
|
|
26316
26524
|
};
|
|
26317
26525
|
function wrap(text, width) {
|
|
26318
26526
|
const out = [];
|
|
@@ -26330,35 +26538,35 @@ function wrap(text, width) {
|
|
|
26330
26538
|
}
|
|
26331
26539
|
var LABEL_WIDTH = 14;
|
|
26332
26540
|
function label(category) {
|
|
26333
|
-
return
|
|
26541
|
+
return import_chalk25.default.bold(category.padEnd(Math.max(LABEL_WIDTH, category.length + 1)));
|
|
26334
26542
|
}
|
|
26335
26543
|
function renderFinding(f, showWeight = false) {
|
|
26336
26544
|
const lines = [];
|
|
26337
|
-
const wt = showWeight && f.scoreWeight ?
|
|
26545
|
+
const wt = showWeight && f.scoreWeight ? import_chalk25.default.cyan.bold(`+${f.scoreWeight} `) : "";
|
|
26338
26546
|
lines.push(` ${ICON[f.severity]} ${label(f.category)}${wt}${f.title}`);
|
|
26339
26547
|
const indent = " ".repeat(2 + 3 + LABEL_WIDTH);
|
|
26340
26548
|
const width = 80 - indent.length;
|
|
26341
26549
|
for (const s of [f.what, f.why, f.who]) {
|
|
26342
|
-
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));
|
|
26343
26551
|
}
|
|
26344
|
-
for (const d of f.detail) lines.push(indent +
|
|
26552
|
+
for (const d of f.detail) lines.push(indent + import_chalk25.default.gray(d));
|
|
26345
26553
|
if (f.fix) {
|
|
26346
26554
|
let first = true;
|
|
26347
26555
|
for (const seg of f.fix.split("\n")) {
|
|
26348
26556
|
for (const l of wrap(seg, width - 2)) {
|
|
26349
|
-
lines.push(indent +
|
|
26557
|
+
lines.push(indent + import_chalk25.default.cyan(first ? "\u2192 " + l : " " + l));
|
|
26350
26558
|
first = false;
|
|
26351
26559
|
}
|
|
26352
26560
|
}
|
|
26353
26561
|
}
|
|
26354
26562
|
const tradeoff = [
|
|
26355
|
-
[f.gain, "gain: ",
|
|
26356
|
-
[f.cost, "cost: ",
|
|
26563
|
+
[f.gain, "gain: ", import_chalk25.default.green],
|
|
26564
|
+
[f.cost, "cost: ", import_chalk25.default.yellow]
|
|
26357
26565
|
];
|
|
26358
26566
|
for (const [text, lbl, color2] of tradeoff) {
|
|
26359
26567
|
if (!text) continue;
|
|
26360
26568
|
wrap(text, width - 6).forEach((l, i) => {
|
|
26361
|
-
lines.push(indent + (i === 0 ? color2(lbl) : " ") +
|
|
26569
|
+
lines.push(indent + (i === 0 ? color2(lbl) : " ") + import_chalk25.default.gray(l));
|
|
26362
26570
|
});
|
|
26363
26571
|
}
|
|
26364
26572
|
return lines;
|
|
@@ -26368,12 +26576,12 @@ function renderPosture(result) {
|
|
|
26368
26576
|
const tier = TIER_LABEL[result.tier];
|
|
26369
26577
|
lines.push("");
|
|
26370
26578
|
lines.push(
|
|
26371
|
-
|
|
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})`
|
|
26372
26580
|
);
|
|
26373
26581
|
const headroom = openHeadroom(result.findings);
|
|
26374
26582
|
if (headroom > 0) {
|
|
26375
26583
|
lines.push(
|
|
26376
|
-
" " +
|
|
26584
|
+
" " + import_chalk25.default.gray(
|
|
26377
26585
|
`${headroom} pts of headroom below \u2014 each layer adds security at some cost to flexibility. You choose which to close.`
|
|
26378
26586
|
)
|
|
26379
26587
|
);
|
|
@@ -26381,21 +26589,21 @@ function renderPosture(result) {
|
|
|
26381
26589
|
lines.push("");
|
|
26382
26590
|
if (result.headline) {
|
|
26383
26591
|
const indent = " ";
|
|
26384
|
-
lines.push(` ${
|
|
26385
|
-
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));
|
|
26386
26594
|
const action = wrap(`Do this first: ${result.headline.action}`, 72);
|
|
26387
|
-
action.forEach((l, i) => lines.push(indent +
|
|
26595
|
+
action.forEach((l, i) => lines.push(indent + import_chalk25.default.cyan(i === 0 ? "\u2192 " + l : " " + l)));
|
|
26388
26596
|
lines.push("");
|
|
26389
26597
|
}
|
|
26390
26598
|
const covered = result.findings.filter((f) => f.coverage?.state === "covered");
|
|
26391
26599
|
const open = result.findings.filter((f) => f.coverage?.state !== "covered");
|
|
26392
26600
|
if (covered.length > 0) {
|
|
26393
|
-
lines.push(" " +
|
|
26601
|
+
lines.push(" " + import_chalk25.default.green("\u{1F7E2} ON NOW \u2014 node9 is enforcing these (your floor)"));
|
|
26394
26602
|
for (const f of covered) {
|
|
26395
26603
|
const gated = f.coverage?.level === "review" ? "approval-gating" : "blocking";
|
|
26396
26604
|
const via = f.coverage?.via ?? "node9";
|
|
26397
26605
|
lines.push(
|
|
26398
|
-
` ${
|
|
26606
|
+
` ${import_chalk25.default.green("\u2705")} ${label(f.category)}${import_chalk25.default.gray(`${via} is ${gated} this`)}`
|
|
26399
26607
|
);
|
|
26400
26608
|
}
|
|
26401
26609
|
lines.push("");
|
|
@@ -26404,24 +26612,24 @@ function renderPosture(result) {
|
|
|
26404
26612
|
const reduceOpen = open.filter((f) => f.owner !== "node9" && f.node9Reduces);
|
|
26405
26613
|
const osOpen = open.filter((f) => f.owner !== "node9" && !f.node9Reduces);
|
|
26406
26614
|
if (node9Open.length > 0) {
|
|
26407
|
-
lines.push(" " +
|
|
26615
|
+
lines.push(" " + import_chalk25.default.cyan.bold("\u{1F527} node9 can fix these \u2014 run the command"));
|
|
26408
26616
|
for (const f of node9Open) lines.push(...renderFinding(f, true));
|
|
26409
26617
|
}
|
|
26410
26618
|
if (reduceOpen.length > 0) {
|
|
26411
26619
|
if (node9Open.length > 0) lines.push("");
|
|
26412
|
-
lines.push(" " +
|
|
26620
|
+
lines.push(" " + import_chalk25.default.yellow.bold("\u{1F512} AVAILABLE \u2014 turn on to harden (each has a tradeoff)"));
|
|
26413
26621
|
for (const f of reduceOpen) lines.push(...renderFinding(f, true));
|
|
26414
26622
|
}
|
|
26415
26623
|
if (osOpen.length > 0) {
|
|
26416
26624
|
if (node9Open.length > 0 || reduceOpen.length > 0) lines.push("");
|
|
26417
|
-
lines.push(" " +
|
|
26625
|
+
lines.push(" " + import_chalk25.default.bold("\u{1F9F1} YOUR PART \u2014 node9 can't fix these (OS-level)"));
|
|
26418
26626
|
for (const f of osOpen) lines.push(...renderFinding(f));
|
|
26419
26627
|
}
|
|
26420
26628
|
for (const cat of result.passedCategories) {
|
|
26421
|
-
lines.push(` ${
|
|
26629
|
+
lines.push(` ${import_chalk25.default.green("\u2705")} ${label(cat)}${import_chalk25.default.gray("no issues found")}`);
|
|
26422
26630
|
}
|
|
26423
26631
|
for (const cat of result.erroredCategories) {
|
|
26424
|
-
lines.push(` ${
|
|
26632
|
+
lines.push(` ${import_chalk25.default.gray("\u2022")} ${label(cat)}${import_chalk25.default.gray("could not be checked")}`);
|
|
26425
26633
|
}
|
|
26426
26634
|
lines.push("");
|
|
26427
26635
|
const crit = open.filter((f) => f.severity === "critical").length;
|
|
@@ -26429,16 +26637,16 @@ function renderPosture(result) {
|
|
|
26429
26637
|
const med = open.filter((f) => f.severity === "medium").length;
|
|
26430
26638
|
const adv = open.filter((f) => f.severity === "advisory").length;
|
|
26431
26639
|
const parts = [];
|
|
26432
|
-
if (crit) parts.push(
|
|
26433
|
-
if (high) parts.push(
|
|
26434
|
-
if (med) parts.push(
|
|
26435
|
-
if (adv) parts.push(
|
|
26436
|
-
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");
|
|
26437
26645
|
lines.push(` ${summary}`);
|
|
26438
26646
|
lines.push("");
|
|
26439
|
-
lines.push(" " +
|
|
26647
|
+
lines.push(" " + import_chalk25.default.bold("Track this across your fleet & keep it green:"));
|
|
26440
26648
|
lines.push(
|
|
26441
|
-
" " +
|
|
26649
|
+
" " + import_chalk25.default.dim("\u2192 ") + import_chalk25.default.cyan.underline("https://node9.ai/auth/signup?ref=cli_posture")
|
|
26442
26650
|
);
|
|
26443
26651
|
lines.push("");
|
|
26444
26652
|
return lines.join("\n");
|
|
@@ -26458,11 +26666,11 @@ function registerPostureCommand(program2) {
|
|
|
26458
26666
|
if (opts.ship) {
|
|
26459
26667
|
const creds = readCredentials();
|
|
26460
26668
|
if (!creds) {
|
|
26461
|
-
console.error(
|
|
26669
|
+
console.error(import_chalk26.default.gray(" Run `node9 login` to ship this to your dashboard."));
|
|
26462
26670
|
} else {
|
|
26463
26671
|
const ok2 = await shipPosture(result, creds);
|
|
26464
26672
|
console.error(
|
|
26465
|
-
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.")
|
|
26466
26674
|
);
|
|
26467
26675
|
}
|
|
26468
26676
|
}
|
|
@@ -26471,7 +26679,7 @@ function registerPostureCommand(program2) {
|
|
|
26471
26679
|
}
|
|
26472
26680
|
|
|
26473
26681
|
// src/cli/commands/egress.ts
|
|
26474
|
-
var
|
|
26682
|
+
var import_chalk27 = __toESM(require("chalk"));
|
|
26475
26683
|
init_config();
|
|
26476
26684
|
init_dist();
|
|
26477
26685
|
function guard(fn) {
|
|
@@ -26479,7 +26687,7 @@ function guard(fn) {
|
|
|
26479
26687
|
fn();
|
|
26480
26688
|
return true;
|
|
26481
26689
|
} catch (err2) {
|
|
26482
|
-
console.error(
|
|
26690
|
+
console.error(import_chalk27.default.red(`
|
|
26483
26691
|
\u2717 ${err2.message}
|
|
26484
26692
|
`));
|
|
26485
26693
|
process.exitCode = 1;
|
|
@@ -26494,19 +26702,19 @@ function addHost(list, host) {
|
|
|
26494
26702
|
}
|
|
26495
26703
|
function showStatus() {
|
|
26496
26704
|
const e = getConfig().policy.egress;
|
|
26497
|
-
const state = !e.enabled ?
|
|
26498
|
-
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"));
|
|
26499
26707
|
console.log(" State: " + state);
|
|
26500
26708
|
console.log(
|
|
26501
|
-
|
|
26709
|
+
import_chalk27.default.gray(
|
|
26502
26710
|
` ${DEFAULT_EGRESS_ALLOWLIST.length} common dev/LLM hosts are always allowed (github, npm, pypi, anthropic, \u2026).`
|
|
26503
26711
|
)
|
|
26504
26712
|
);
|
|
26505
26713
|
if (e.allow.length) console.log(" Your allow: " + e.allow.join(", "));
|
|
26506
26714
|
if (e.deny.length) console.log(" Your deny: " + e.deny.join(", "));
|
|
26507
26715
|
if (!e.enabled) {
|
|
26508
|
-
console.log(
|
|
26509
|
-
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)"));
|
|
26510
26718
|
}
|
|
26511
26719
|
console.log("");
|
|
26512
26720
|
}
|
|
@@ -26514,43 +26722,43 @@ function registerEgressCommand(program2) {
|
|
|
26514
26722
|
const egress = program2.command("egress").description("Control where your agent can send data (egress allowlist)");
|
|
26515
26723
|
egress.command("watch").description("Prompt before the agent reaches an unknown host (review mode)").action(() => {
|
|
26516
26724
|
if (!mutate({ enabled: true, mode: "review" })) return;
|
|
26517
|
-
console.log(
|
|
26725
|
+
console.log(import_chalk27.default.green("\n\u2713 Egress is now watched (review mode)."));
|
|
26518
26726
|
console.log(
|
|
26519
|
-
|
|
26727
|
+
import_chalk27.default.gray(" Routine hosts (LLM APIs, package registries, localhost) are allowed.")
|
|
26520
26728
|
);
|
|
26521
26729
|
console.log(
|
|
26522
|
-
|
|
26730
|
+
import_chalk27.default.gray(" An unknown host will prompt you \u2014 run `node9 egress lock` to hard-block.\n")
|
|
26523
26731
|
);
|
|
26524
26732
|
});
|
|
26525
26733
|
egress.command("lock").description("Block the agent from reaching unknown hosts (block mode)").action(() => {
|
|
26526
26734
|
if (!mutate({ enabled: true, mode: "block" })) return;
|
|
26527
|
-
console.log(
|
|
26528
|
-
console.log(
|
|
26529
|
-
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"));
|
|
26530
26738
|
});
|
|
26531
26739
|
egress.command("allow <host>").description("Allow an extra host (glob, e.g. *.mycorp.com)").action((host) => {
|
|
26532
26740
|
if (!addHost("allow", host)) return;
|
|
26533
|
-
console.log(
|
|
26741
|
+
console.log(import_chalk27.default.green(`
|
|
26534
26742
|
\u2713 Allowed egress to ${host}.
|
|
26535
26743
|
`));
|
|
26536
26744
|
});
|
|
26537
26745
|
egress.command("deny <host>").description("Block an extra host (deny always wins)").action((host) => {
|
|
26538
26746
|
if (!addHost("deny", host)) return;
|
|
26539
|
-
console.log(
|
|
26747
|
+
console.log(import_chalk27.default.green(`
|
|
26540
26748
|
\u2713 Denied egress to ${host}.
|
|
26541
26749
|
`));
|
|
26542
26750
|
});
|
|
26543
26751
|
egress.command("off").description("Turn egress control off").action(() => {
|
|
26544
26752
|
if (!mutate({ enabled: false })) return;
|
|
26545
26753
|
console.log(
|
|
26546
|
-
|
|
26754
|
+
import_chalk27.default.yellow("\n\u2713 Egress control is off \u2014 the agent can reach any host again.\n")
|
|
26547
26755
|
);
|
|
26548
26756
|
});
|
|
26549
26757
|
egress.action(showStatus);
|
|
26550
26758
|
}
|
|
26551
26759
|
|
|
26552
26760
|
// src/cli/commands/jail.ts
|
|
26553
|
-
var
|
|
26761
|
+
var import_chalk28 = __toESM(require("chalk"));
|
|
26554
26762
|
|
|
26555
26763
|
// src/shields/jail.ts
|
|
26556
26764
|
var import_fs55 = __toESM(require("fs"));
|
|
@@ -26645,20 +26853,20 @@ function registerJailCommand(program2) {
|
|
|
26645
26853
|
regenerateUserJail(paths);
|
|
26646
26854
|
appendConfigAudit({ event: "jail-add", path: p.trim(), verdict });
|
|
26647
26855
|
} catch (err2) {
|
|
26648
|
-
console.error(
|
|
26856
|
+
console.error(import_chalk28.default.red(`
|
|
26649
26857
|
\u274C ${err2.message}
|
|
26650
26858
|
`));
|
|
26651
26859
|
process.exit(1);
|
|
26652
26860
|
return;
|
|
26653
26861
|
}
|
|
26654
|
-
console.log(
|
|
26862
|
+
console.log(import_chalk28.default.green(`
|
|
26655
26863
|
\u2705 Jailed ${p} (${verdict}).`));
|
|
26656
26864
|
console.log(
|
|
26657
|
-
|
|
26865
|
+
import_chalk28.default.gray(
|
|
26658
26866
|
` AI reads of this path now ${verdict === "block" ? "BLOCK" : "require approval"}.`
|
|
26659
26867
|
)
|
|
26660
26868
|
);
|
|
26661
|
-
console.log(
|
|
26869
|
+
console.log(import_chalk28.default.gray(` Preview: ${import_chalk28.default.cyan(`node9 explain bash "cat ${p}"`)}
|
|
26662
26870
|
`));
|
|
26663
26871
|
});
|
|
26664
26872
|
jail.command("remove <path>").description("Remove a user-added jail path (built-in paths are not removable)").action((p) => {
|
|
@@ -26666,17 +26874,17 @@ function registerJailCommand(program2) {
|
|
|
26666
26874
|
try {
|
|
26667
26875
|
result = removeJailPath(p);
|
|
26668
26876
|
} catch (err2) {
|
|
26669
|
-
console.error(
|
|
26877
|
+
console.error(import_chalk28.default.red(`
|
|
26670
26878
|
\u274C ${err2.message}
|
|
26671
26879
|
`));
|
|
26672
26880
|
process.exit(1);
|
|
26673
26881
|
return;
|
|
26674
26882
|
}
|
|
26675
26883
|
if (!result.removed) {
|
|
26676
|
-
console.error(
|
|
26884
|
+
console.error(import_chalk28.default.yellow(`
|
|
26677
26885
|
\u2139\uFE0F "${p}" is not a user-added jail path.
|
|
26678
26886
|
`));
|
|
26679
|
-
console.error(
|
|
26887
|
+
console.error(import_chalk28.default.gray(` Run ${import_chalk28.default.cyan("node9 jail list")} to see your paths.
|
|
26680
26888
|
`));
|
|
26681
26889
|
process.exit(1);
|
|
26682
26890
|
return;
|
|
@@ -26685,39 +26893,39 @@ function registerJailCommand(program2) {
|
|
|
26685
26893
|
regenerateUserJail(result.paths);
|
|
26686
26894
|
appendConfigAudit({ event: "jail-remove", path: p.trim() });
|
|
26687
26895
|
} catch (err2) {
|
|
26688
|
-
console.error(
|
|
26896
|
+
console.error(import_chalk28.default.red(`
|
|
26689
26897
|
\u274C ${err2.message}
|
|
26690
26898
|
`));
|
|
26691
26899
|
process.exit(1);
|
|
26692
26900
|
return;
|
|
26693
26901
|
}
|
|
26694
|
-
console.log(
|
|
26902
|
+
console.log(import_chalk28.default.green(`
|
|
26695
26903
|
\u2705 Removed ${p} from the jail.
|
|
26696
26904
|
`));
|
|
26697
26905
|
});
|
|
26698
26906
|
jail.command("list").description("Show built-in + user-added jail paths").action(() => {
|
|
26699
|
-
console.log(
|
|
26700
|
-
console.log(
|
|
26701
|
-
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}`);
|
|
26702
26910
|
console.log("");
|
|
26703
26911
|
let user;
|
|
26704
26912
|
try {
|
|
26705
26913
|
user = readJailPaths();
|
|
26706
26914
|
} catch (err2) {
|
|
26707
|
-
console.error(
|
|
26915
|
+
console.error(import_chalk28.default.red(` \u2717 ${err2.message}
|
|
26708
26916
|
`));
|
|
26709
26917
|
process.exit(1);
|
|
26710
26918
|
return;
|
|
26711
26919
|
}
|
|
26712
26920
|
if (user.length === 0) {
|
|
26713
26921
|
console.log(
|
|
26714
|
-
|
|
26922
|
+
import_chalk28.default.gray(" Your paths: (none) \u2014 add one with ") + import_chalk28.default.cyan("node9 jail add <path>")
|
|
26715
26923
|
);
|
|
26716
26924
|
} else {
|
|
26717
|
-
console.log(
|
|
26925
|
+
console.log(import_chalk28.default.gray(" Your paths (removable):"));
|
|
26718
26926
|
for (const u of user) {
|
|
26719
|
-
const v = u.verdict === "block" ?
|
|
26720
|
-
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)}`);
|
|
26721
26929
|
}
|
|
26722
26930
|
}
|
|
26723
26931
|
console.log("");
|
|
@@ -26725,7 +26933,7 @@ function registerJailCommand(program2) {
|
|
|
26725
26933
|
}
|
|
26726
26934
|
|
|
26727
26935
|
// src/cli/commands/sandbox.ts
|
|
26728
|
-
var
|
|
26936
|
+
var import_chalk29 = __toESM(require("chalk"));
|
|
26729
26937
|
var import_fs58 = __toESM(require("fs"));
|
|
26730
26938
|
var import_path56 = __toESM(require("path"));
|
|
26731
26939
|
var import_child_process13 = require("child_process");
|
|
@@ -26963,16 +27171,16 @@ function registerSandboxCommand(program2, version2) {
|
|
|
26963
27171
|
const p = sandboxConfigPath();
|
|
26964
27172
|
if (import_fs58.default.existsSync(p)) {
|
|
26965
27173
|
console.log(
|
|
26966
|
-
|
|
27174
|
+
import_chalk29.default.yellow(` ${SANDBOX_CONFIG_FILE} already exists \u2014 leaving it untouched.`)
|
|
26967
27175
|
);
|
|
26968
27176
|
return;
|
|
26969
27177
|
}
|
|
26970
27178
|
import_fs58.default.writeFileSync(p, scaffoldSandboxYaml(agent));
|
|
26971
27179
|
console.log(
|
|
26972
|
-
|
|
27180
|
+
import_chalk29.default.green(` \u2713 wrote ${SANDBOX_CONFIG_FILE}`) + import_chalk29.default.dim(` (agent: ${agent})`)
|
|
26973
27181
|
);
|
|
26974
27182
|
console.log(
|
|
26975
|
-
|
|
27183
|
+
import_chalk29.default.dim(" Edit it (mounts / allow / expose), then: ") + import_chalk29.default.cyan("node9 sandbox run")
|
|
26976
27184
|
);
|
|
26977
27185
|
});
|
|
26978
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) => {
|
|
@@ -26982,7 +27190,7 @@ function registerSandboxCommand(program2, version2) {
|
|
|
26982
27190
|
const engine = detectEngine(sandbox.runtime.engine);
|
|
26983
27191
|
if (!engine.available) {
|
|
26984
27192
|
console.error(
|
|
26985
|
-
|
|
27193
|
+
import_chalk29.default.red(` ${sandbox.runtime.engine} not found.`) + import_chalk29.default.dim(` Install it first \u2014 node9 sandbox needs a container runtime.`)
|
|
26986
27194
|
);
|
|
26987
27195
|
process.exit(1);
|
|
26988
27196
|
}
|
|
@@ -26995,11 +27203,11 @@ function registerSandboxCommand(program2, version2) {
|
|
|
26995
27203
|
});
|
|
26996
27204
|
if (compiled.rejected.length) {
|
|
26997
27205
|
console.log(
|
|
26998
|
-
|
|
27206
|
+
import_chalk29.default.yellow(` \u26A0 ignoring invalid allow hosts: ${compiled.rejected.join(", ")}`)
|
|
26999
27207
|
);
|
|
27000
27208
|
}
|
|
27001
27209
|
if (compiled.denied.length) {
|
|
27002
|
-
console.log(
|
|
27210
|
+
console.log(import_chalk29.default.dim(` (denied: ${compiled.denied.join(", ")})`));
|
|
27003
27211
|
}
|
|
27004
27212
|
const allowlistPath = writeAllowlist(cwd, compiled.allow);
|
|
27005
27213
|
const dockerfile = renderDockerfile(sandbox, node9Version2);
|
|
@@ -27012,12 +27220,12 @@ function registerSandboxCommand(program2, version2) {
|
|
|
27012
27220
|
const imageExists = (0, import_child_process13.spawnSync)(sandbox.runtime.engine, ["image", "inspect", image], { stdio: "ignore" }).status === 0;
|
|
27013
27221
|
const needBuild = sandbox.runtime.rebuild === "always" || !imageExists || sandbox.runtime.rebuild !== "never" && lastHash !== hash;
|
|
27014
27222
|
if (needBuild) {
|
|
27015
|
-
console.log(
|
|
27223
|
+
console.log(import_chalk29.default.dim(` building ${image} \u2026`));
|
|
27016
27224
|
const b = (0, import_child_process13.spawnSync)(sandbox.runtime.engine, ["build", "-t", image, buildDir], {
|
|
27017
27225
|
stdio: "inherit"
|
|
27018
27226
|
});
|
|
27019
27227
|
if (b.status !== 0) {
|
|
27020
|
-
console.error(
|
|
27228
|
+
console.error(import_chalk29.default.red(" build failed."));
|
|
27021
27229
|
process.exit(b.status ?? 1);
|
|
27022
27230
|
}
|
|
27023
27231
|
import_fs58.default.writeFileSync(hashFile, hash);
|
|
@@ -27035,15 +27243,15 @@ function registerSandboxCommand(program2, version2) {
|
|
|
27035
27243
|
if (sandbox.node9.mountAgentCredentials) {
|
|
27036
27244
|
const creds = agentCredentialsMount(sandbox.agent);
|
|
27037
27245
|
if (import_fs58.default.existsSync(creds.hostPath)) {
|
|
27038
|
-
console.log(
|
|
27246
|
+
console.log(import_chalk29.default.dim(` mounting ${creds.hostPath} (agent credentials, rw)`));
|
|
27039
27247
|
} else {
|
|
27040
27248
|
console.log(
|
|
27041
|
-
|
|
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.`)
|
|
27042
27250
|
);
|
|
27043
27251
|
}
|
|
27044
27252
|
}
|
|
27045
27253
|
console.log(
|
|
27046
|
-
|
|
27254
|
+
import_chalk29.default.green(` \u{1F6E1}\uFE0F ${sandbox.agent} jailed \u2014 ${compiled.allow.length} hosts allowed
|
|
27047
27255
|
`)
|
|
27048
27256
|
);
|
|
27049
27257
|
const r = (0, import_child_process13.spawnSync)(sandbox.runtime.engine, runArgs, { stdio: "inherit" });
|
|
@@ -27052,7 +27260,7 @@ function registerSandboxCommand(program2, version2) {
|
|
|
27052
27260
|
cmd.command("tail").description("Stream the sandbox's audit log (host-side)").action(() => {
|
|
27053
27261
|
const auditPath = import_path56.default.join(sandboxDataDir(), "audit.log");
|
|
27054
27262
|
if (!import_fs58.default.existsSync(auditPath)) {
|
|
27055
|
-
console.log(
|
|
27263
|
+
console.log(import_chalk29.default.dim(" no sandbox audit yet."));
|
|
27056
27264
|
return;
|
|
27057
27265
|
}
|
|
27058
27266
|
(0, import_child_process13.spawnSync)("tail", ["-f", auditPath], { stdio: "inherit" });
|
|
@@ -27060,7 +27268,7 @@ function registerSandboxCommand(program2, version2) {
|
|
|
27060
27268
|
cmd.command("logs").description("Dump the sandbox's audit log").action(() => {
|
|
27061
27269
|
const auditPath = import_path56.default.join(sandboxDataDir(), "audit.log");
|
|
27062
27270
|
if (!import_fs58.default.existsSync(auditPath)) {
|
|
27063
|
-
console.log(
|
|
27271
|
+
console.log(import_chalk29.default.dim(" no sandbox audit yet."));
|
|
27064
27272
|
return;
|
|
27065
27273
|
}
|
|
27066
27274
|
process.stdout.write(import_fs58.default.readFileSync(auditPath, "utf-8"));
|
|
@@ -27078,12 +27286,12 @@ function registerSandboxCommand(program2, version2) {
|
|
|
27078
27286
|
});
|
|
27079
27287
|
}
|
|
27080
27288
|
import_fs58.default.rmSync(import_path56.default.join(cwd, ".node9", "sandbox"), { recursive: true, force: true });
|
|
27081
|
-
console.log(
|
|
27289
|
+
console.log(import_chalk29.default.green(" \u2713 sandbox image + build + data removed."));
|
|
27082
27290
|
});
|
|
27083
27291
|
}
|
|
27084
27292
|
|
|
27085
27293
|
// src/cli/commands/sessions.ts
|
|
27086
|
-
var
|
|
27294
|
+
var import_chalk30 = __toESM(require("chalk"));
|
|
27087
27295
|
var import_fs59 = __toESM(require("fs"));
|
|
27088
27296
|
var import_path57 = __toESM(require("path"));
|
|
27089
27297
|
var import_os50 = __toESM(require("os"));
|
|
@@ -27579,11 +27787,11 @@ function toolInputSummary(tool, input) {
|
|
|
27579
27787
|
}
|
|
27580
27788
|
function toolColor(tool) {
|
|
27581
27789
|
const t = tool.toLowerCase();
|
|
27582
|
-
if (t === "bash" || t === "execute_bash") return
|
|
27583
|
-
if (t === "write") return
|
|
27584
|
-
if (t === "edit" || t === "notebookedit") return
|
|
27585
|
-
if (t === "read") return
|
|
27586
|
-
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;
|
|
27587
27795
|
}
|
|
27588
27796
|
function barStr2(value, max, width) {
|
|
27589
27797
|
if (max === 0 || width <= 0) return "\u2591".repeat(width);
|
|
@@ -27593,7 +27801,7 @@ function barStr2(value, max, width) {
|
|
|
27593
27801
|
function colorBar2(value, max, width) {
|
|
27594
27802
|
const s = barStr2(value, max, width);
|
|
27595
27803
|
const filled = Math.max(1, Math.round(max > 0 ? value / max * width : 0));
|
|
27596
|
-
return
|
|
27804
|
+
return import_chalk30.default.cyan(s.slice(0, filled)) + import_chalk30.default.dim(s.slice(filled));
|
|
27597
27805
|
}
|
|
27598
27806
|
function renderSummary(summaries) {
|
|
27599
27807
|
const totalTools = summaries.reduce((n, s) => n + s.toolCalls.length, 0);
|
|
@@ -27623,45 +27831,45 @@ function renderSummary(summaries) {
|
|
|
27623
27831
|
}
|
|
27624
27832
|
const topProjects = [...projCosts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
|
|
27625
27833
|
const W = 20;
|
|
27626
|
-
console.log(
|
|
27834
|
+
console.log(import_chalk30.default.dim(" " + "\u2500".repeat(70)));
|
|
27627
27835
|
console.log(
|
|
27628
|
-
" " +
|
|
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") : "")
|
|
27629
27837
|
);
|
|
27630
27838
|
console.log(
|
|
27631
|
-
" " +
|
|
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`)
|
|
27632
27840
|
);
|
|
27633
27841
|
console.log("");
|
|
27634
|
-
console.log(" " +
|
|
27842
|
+
console.log(" " + import_chalk30.default.dim("Tool breakdown:"));
|
|
27635
27843
|
const maxGroup = Math.max(...Object.values(groups));
|
|
27636
27844
|
for (const [label2, count] of Object.entries(groups)) {
|
|
27637
27845
|
if (count === 0) continue;
|
|
27638
27846
|
const pct = totalTools > 0 ? Math.round(count / totalTools * 100) : 0;
|
|
27639
27847
|
console.log(
|
|
27640
|
-
" " + 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)}%)`)
|
|
27641
27849
|
);
|
|
27642
27850
|
}
|
|
27643
27851
|
console.log("");
|
|
27644
27852
|
if (topProjects.length > 1) {
|
|
27645
|
-
console.log(" " +
|
|
27853
|
+
console.log(" " + import_chalk30.default.dim("Cost by project:"));
|
|
27646
27854
|
const maxProjCost = topProjects[0][1];
|
|
27647
27855
|
for (const [proj, cost] of topProjects) {
|
|
27648
27856
|
console.log(
|
|
27649
|
-
" " + 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))
|
|
27650
27858
|
);
|
|
27651
27859
|
}
|
|
27652
27860
|
console.log("");
|
|
27653
27861
|
}
|
|
27654
|
-
console.log(
|
|
27862
|
+
console.log(import_chalk30.default.dim(" " + "\u2500".repeat(70)));
|
|
27655
27863
|
console.log("");
|
|
27656
27864
|
}
|
|
27657
27865
|
function renderList(summaries, totalCost) {
|
|
27658
27866
|
if (summaries.length === 0) {
|
|
27659
|
-
console.log(
|
|
27867
|
+
console.log(import_chalk30.default.yellow(" No sessions found in the requested range.\n"));
|
|
27660
27868
|
return;
|
|
27661
27869
|
}
|
|
27662
|
-
const totalLabel = totalCost > 0 ?
|
|
27870
|
+
const totalLabel = totalCost > 0 ? import_chalk30.default.dim(" ~" + fmtCost3(totalCost) + " total") : "";
|
|
27663
27871
|
console.log(
|
|
27664
|
-
" " +
|
|
27872
|
+
" " + import_chalk30.default.white(String(summaries.length)) + import_chalk30.default.dim(` session${summaries.length !== 1 ? "s" : ""}`) + totalLabel
|
|
27665
27873
|
);
|
|
27666
27874
|
console.log("");
|
|
27667
27875
|
let lastGroup = "";
|
|
@@ -27669,51 +27877,51 @@ function renderList(summaries, totalCost) {
|
|
|
27669
27877
|
const activeDate = fmtDate2(s.lastActiveTime);
|
|
27670
27878
|
const group = activeDate + " " + s.projectLabel;
|
|
27671
27879
|
if (group !== lastGroup) {
|
|
27672
|
-
console.log(
|
|
27880
|
+
console.log(import_chalk30.default.dim(" \u2500\u2500\u2500 ") + import_chalk30.default.bold(activeDate) + import_chalk30.default.dim(" " + s.projectLabel));
|
|
27673
27881
|
lastGroup = group;
|
|
27674
27882
|
}
|
|
27675
27883
|
const startDate = fmtDate2(s.startTime);
|
|
27676
|
-
const dateRange = startDate !== activeDate ?
|
|
27677
|
-
const timeStr =
|
|
27678
|
-
const prompt =
|
|
27679
|
-
const tools = s.toolCalls.length > 0 ?
|
|
27680
|
-
const cost = s.costUSD > 0 ?
|
|
27681
|
-
const blocked = s.blockedCalls.length > 0 ?
|
|
27682
|
-
const snap = s.hasSnapshot ?
|
|
27683
|
-
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")](
|
|
27684
27892
|
" " + agentBadgeText(s.agent ?? "claude", 0)
|
|
27685
27893
|
);
|
|
27686
|
-
const sid =
|
|
27894
|
+
const sid = import_chalk30.default.dim(" " + s.sessionId.slice(0, 8));
|
|
27687
27895
|
console.log(
|
|
27688
27896
|
` ${timeStr} ${prompt} ${tools}${cost}${blocked}${snap}${agentBadge}${sid}${dateRange}`
|
|
27689
27897
|
);
|
|
27690
27898
|
}
|
|
27691
27899
|
console.log("");
|
|
27692
27900
|
console.log(
|
|
27693
|
-
|
|
27901
|
+
import_chalk30.default.dim(" Run") + " " + import_chalk30.default.cyan("node9 sessions --detail <session-id>") + import_chalk30.default.dim(" for full tool trace.")
|
|
27694
27902
|
);
|
|
27695
27903
|
console.log("");
|
|
27696
27904
|
}
|
|
27697
27905
|
function renderDetail(s) {
|
|
27698
27906
|
console.log("");
|
|
27699
|
-
console.log(
|
|
27907
|
+
console.log(import_chalk30.default.bold(" Session ") + import_chalk30.default.dim(s.sessionId));
|
|
27700
27908
|
console.log(
|
|
27701
|
-
|
|
27909
|
+
import_chalk30.default.bold(" Prompt ") + import_chalk30.default.white(s.firstPrompt.replace(/\n/g, " ").slice(0, 120))
|
|
27702
27910
|
);
|
|
27703
|
-
console.log(
|
|
27911
|
+
console.log(import_chalk30.default.bold(" Project ") + import_chalk30.default.white(s.projectLabel));
|
|
27704
27912
|
if (s.agent) {
|
|
27705
|
-
const agentLabel2 =
|
|
27706
|
-
console.log(
|
|
27913
|
+
const agentLabel2 = import_chalk30.default[agentColorName(s.agent)](agentDisplayName(s.agent));
|
|
27914
|
+
console.log(import_chalk30.default.bold(" Agent ") + agentLabel2);
|
|
27707
27915
|
}
|
|
27708
|
-
console.log(
|
|
27916
|
+
console.log(import_chalk30.default.bold(" When ") + import_chalk30.default.white(fmtDateTime(s.startTime)));
|
|
27709
27917
|
if (s.costUSD > 0)
|
|
27710
|
-
console.log(
|
|
27918
|
+
console.log(import_chalk30.default.bold(" Cost ") + import_chalk30.default.yellow("~" + fmtCost3(s.costUSD)));
|
|
27711
27919
|
console.log(
|
|
27712
|
-
|
|
27920
|
+
import_chalk30.default.bold(" Snapshot ") + (s.hasSnapshot ? import_chalk30.default.green("\u2713 taken") : import_chalk30.default.dim("none"))
|
|
27713
27921
|
);
|
|
27714
27922
|
console.log("");
|
|
27715
27923
|
if (s.toolCalls.length === 0 && s.blockedCalls.length === 0) {
|
|
27716
|
-
console.log(
|
|
27924
|
+
console.log(import_chalk30.default.dim(" No tool calls recorded.\n"));
|
|
27717
27925
|
return;
|
|
27718
27926
|
}
|
|
27719
27927
|
const timeline = [
|
|
@@ -27726,32 +27934,32 @@ function renderDetail(s) {
|
|
|
27726
27934
|
});
|
|
27727
27935
|
const headerParts = [`Tool calls (${s.toolCalls.length})`];
|
|
27728
27936
|
if (s.blockedCalls.length > 0)
|
|
27729
|
-
headerParts.push(
|
|
27730
|
-
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 ")));
|
|
27731
27939
|
console.log("");
|
|
27732
27940
|
for (const entry of timeline) {
|
|
27733
27941
|
if (entry.kind === "tool") {
|
|
27734
27942
|
const tc = entry.tc;
|
|
27735
27943
|
const colorFn = toolColor(tc.tool);
|
|
27736
27944
|
const toolPad = colorFn(tc.tool.padEnd(16));
|
|
27737
|
-
const detail =
|
|
27738
|
-
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) + " ") : " ";
|
|
27739
27947
|
console.log(` ${ts}${toolPad} ${detail}`);
|
|
27740
27948
|
} else {
|
|
27741
27949
|
const bc = entry.bc;
|
|
27742
|
-
const ts = bc.timestamp ?
|
|
27743
|
-
const label2 =
|
|
27744
|
-
const toolName =
|
|
27745
|
-
const argsSummary = bc.args ?
|
|
27746
|
-
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) : "";
|
|
27747
27955
|
console.log(` ${ts}${label2} ${toolName} ${argsSummary}${reason}`);
|
|
27748
27956
|
}
|
|
27749
27957
|
}
|
|
27750
27958
|
console.log("");
|
|
27751
27959
|
if (s.modifiedFiles.length > 0) {
|
|
27752
|
-
console.log(
|
|
27960
|
+
console.log(import_chalk30.default.bold(` Files modified (${s.modifiedFiles.length}):`));
|
|
27753
27961
|
for (const f of s.modifiedFiles) {
|
|
27754
|
-
console.log(" " +
|
|
27962
|
+
console.log(" " + import_chalk30.default.yellow(f));
|
|
27755
27963
|
}
|
|
27756
27964
|
console.log("");
|
|
27757
27965
|
}
|
|
@@ -27759,13 +27967,13 @@ function renderDetail(s) {
|
|
|
27759
27967
|
function registerSessionsCommand(program2) {
|
|
27760
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) => {
|
|
27761
27969
|
console.log("");
|
|
27762
|
-
console.log(
|
|
27970
|
+
console.log(import_chalk30.default.cyan.bold("\u{1F4CB} node9 sessions") + import_chalk30.default.dim(" \u2014 what your AI agent did"));
|
|
27763
27971
|
console.log("");
|
|
27764
27972
|
const days = options.detail || options.all ? null : Math.max(1, parseInt(options.days, 10) || 7);
|
|
27765
27973
|
const rangeLabel = options.detail ? "all time" : options.all ? "all time" : `last ${String(days)} days`;
|
|
27766
|
-
console.log(
|
|
27974
|
+
console.log(import_chalk30.default.dim(" " + rangeLabel));
|
|
27767
27975
|
console.log("");
|
|
27768
|
-
process.stdout.write(
|
|
27976
|
+
process.stdout.write(import_chalk30.default.dim(" Loading\u2026"));
|
|
27769
27977
|
const summaries = buildSessions(days);
|
|
27770
27978
|
if (process.stdout.isTTY) {
|
|
27771
27979
|
process.stdout.clearLine(0);
|
|
@@ -27778,8 +27986,8 @@ function registerSessionsCommand(program2) {
|
|
|
27778
27986
|
(s) => s.sessionId === options.detail || s.sessionId.startsWith(options.detail)
|
|
27779
27987
|
);
|
|
27780
27988
|
if (!target) {
|
|
27781
|
-
console.log(
|
|
27782
|
-
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"));
|
|
27783
27991
|
return;
|
|
27784
27992
|
}
|
|
27785
27993
|
renderDetail(target);
|
|
@@ -27792,7 +28000,7 @@ function registerSessionsCommand(program2) {
|
|
|
27792
28000
|
}
|
|
27793
28001
|
|
|
27794
28002
|
// src/cli/commands/session-taint.ts
|
|
27795
|
-
var
|
|
28003
|
+
var import_chalk31 = __toESM(require("chalk"));
|
|
27796
28004
|
init_daemon();
|
|
27797
28005
|
function resolveSessionId(records, query) {
|
|
27798
28006
|
const exact = records.find((r) => r.sessionId === query);
|
|
@@ -27819,22 +28027,22 @@ function registerSessionTaintCommand(program2) {
|
|
|
27819
28027
|
const records = await listSessionTaints();
|
|
27820
28028
|
console.log("");
|
|
27821
28029
|
if (records.length === 0) {
|
|
27822
|
-
console.log(
|
|
27823
|
-
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");
|
|
27824
28032
|
return;
|
|
27825
28033
|
}
|
|
27826
28034
|
console.log(
|
|
27827
|
-
" " +
|
|
28035
|
+
" " + import_chalk31.default.bold(String(records.length)) + import_chalk31.default.dim(` tainted session${records.length !== 1 ? "s" : ""}`)
|
|
27828
28036
|
);
|
|
27829
28037
|
console.log("");
|
|
27830
28038
|
for (const r of records) {
|
|
27831
28039
|
console.log(
|
|
27832
|
-
" " +
|
|
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))
|
|
27833
28041
|
);
|
|
27834
28042
|
}
|
|
27835
28043
|
console.log("");
|
|
27836
28044
|
console.log(
|
|
27837
|
-
|
|
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"
|
|
27838
28046
|
);
|
|
27839
28047
|
});
|
|
27840
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) => {
|
|
@@ -27842,32 +28050,32 @@ function registerSessionTaintCommand(program2) {
|
|
|
27842
28050
|
if (opts.all) {
|
|
27843
28051
|
const res2 = await clearSessionTaint({ all: true });
|
|
27844
28052
|
if (res2.daemonUnavailable) {
|
|
27845
|
-
console.log(
|
|
28053
|
+
console.log(import_chalk31.default.dim(" node9 daemon not running \u2014 no active taints to clear.") + "\n");
|
|
27846
28054
|
return;
|
|
27847
28055
|
}
|
|
27848
28056
|
console.log(
|
|
27849
|
-
|
|
28057
|
+
import_chalk31.default.green(" \u2713 ") + `Cleared ${import_chalk31.default.bold(String(res2.cleared))} session taint${res2.cleared !== 1 ? "s" : ""}.
|
|
27850
28058
|
`
|
|
27851
28059
|
);
|
|
27852
28060
|
return;
|
|
27853
28061
|
}
|
|
27854
28062
|
if (!sessionId) {
|
|
27855
|
-
console.log(
|
|
27856
|
-
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");
|
|
27857
28065
|
return;
|
|
27858
28066
|
}
|
|
27859
28067
|
const records = await listSessionTaints();
|
|
27860
28068
|
if (records.length === 0) {
|
|
27861
|
-
console.log(
|
|
28069
|
+
console.log(import_chalk31.default.dim(" No tainted sessions \u2014 nothing to clear.") + "\n");
|
|
27862
28070
|
return;
|
|
27863
28071
|
}
|
|
27864
28072
|
const resolved = resolveSessionId(records, sessionId);
|
|
27865
28073
|
if ("error" in resolved) {
|
|
27866
28074
|
if (resolved.error === "not-found") {
|
|
27867
|
-
console.log(
|
|
28075
|
+
console.log(import_chalk31.default.red(` No tainted session matches "${sessionId}".`));
|
|
27868
28076
|
} else {
|
|
27869
|
-
console.log(
|
|
27870
|
-
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));
|
|
27871
28079
|
}
|
|
27872
28080
|
console.log("");
|
|
27873
28081
|
return;
|
|
@@ -27875,18 +28083,18 @@ function registerSessionTaintCommand(program2) {
|
|
|
27875
28083
|
const res = await clearSessionTaint({ sessionId: resolved.record.sessionId });
|
|
27876
28084
|
if (res.cleared > 0) {
|
|
27877
28085
|
console.log(
|
|
27878
|
-
|
|
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"
|
|
27879
28087
|
);
|
|
27880
28088
|
} else {
|
|
27881
28089
|
console.log(
|
|
27882
|
-
|
|
28090
|
+
import_chalk31.default.dim(` Session ${resolved.record.sessionId.slice(0, 8)} was already clear.`) + "\n"
|
|
27883
28091
|
);
|
|
27884
28092
|
}
|
|
27885
28093
|
});
|
|
27886
28094
|
}
|
|
27887
28095
|
|
|
27888
28096
|
// src/cli/commands/skill-pin.ts
|
|
27889
|
-
var
|
|
28097
|
+
var import_chalk32 = __toESM(require("chalk"));
|
|
27890
28098
|
var import_fs60 = __toESM(require("fs"));
|
|
27891
28099
|
var import_os51 = __toESM(require("os"));
|
|
27892
28100
|
var import_path58 = __toESM(require("path"));
|
|
@@ -27906,29 +28114,29 @@ function registerSkillPinCommand(program2) {
|
|
|
27906
28114
|
const result = readSkillPinsSafe();
|
|
27907
28115
|
if (!result.ok) {
|
|
27908
28116
|
if (result.reason === "missing") {
|
|
27909
|
-
console.log(
|
|
28117
|
+
console.log(import_chalk32.default.gray("\nNo skill roots are pinned yet."));
|
|
27910
28118
|
console.log(
|
|
27911
|
-
|
|
28119
|
+
import_chalk32.default.gray("Pins are created automatically on the first tool call of each session.\n")
|
|
27912
28120
|
);
|
|
27913
28121
|
return;
|
|
27914
28122
|
}
|
|
27915
|
-
console.error(
|
|
28123
|
+
console.error(import_chalk32.default.red(`
|
|
27916
28124
|
\u274C Pin file is corrupt: ${result.detail}`));
|
|
27917
|
-
console.error(
|
|
28125
|
+
console.error(import_chalk32.default.yellow(" Run: node9 skill pin reset\n"));
|
|
27918
28126
|
process.exit(1);
|
|
27919
28127
|
}
|
|
27920
28128
|
const entries = Object.entries(result.pins.roots);
|
|
27921
28129
|
if (entries.length === 0) {
|
|
27922
|
-
console.log(
|
|
28130
|
+
console.log(import_chalk32.default.gray("\nNo skill roots are pinned yet.\n"));
|
|
27923
28131
|
return;
|
|
27924
28132
|
}
|
|
27925
|
-
console.log(
|
|
28133
|
+
console.log(import_chalk32.default.bold("\n\u{1F512} Pinned Skill Roots\n"));
|
|
27926
28134
|
for (const [key, entry] of entries) {
|
|
27927
|
-
const missing = entry.exists ? "" :
|
|
27928
|
-
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}`);
|
|
27929
28137
|
console.log(` Files (${entry.fileCount})`);
|
|
27930
|
-
console.log(` Hash: ${
|
|
27931
|
-
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)}
|
|
27932
28140
|
`);
|
|
27933
28141
|
}
|
|
27934
28142
|
});
|
|
@@ -27937,39 +28145,39 @@ function registerSkillPinCommand(program2) {
|
|
|
27937
28145
|
try {
|
|
27938
28146
|
pins = readSkillPins();
|
|
27939
28147
|
} catch {
|
|
27940
|
-
console.error(
|
|
27941
|
-
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"));
|
|
27942
28150
|
process.exit(1);
|
|
27943
28151
|
}
|
|
27944
28152
|
if (!pins.roots[rootKey]) {
|
|
27945
|
-
console.error(
|
|
28153
|
+
console.error(import_chalk32.default.red(`
|
|
27946
28154
|
\u274C No pin found for root key "${rootKey}"
|
|
27947
28155
|
`));
|
|
27948
|
-
console.error(`Run ${
|
|
28156
|
+
console.error(`Run ${import_chalk32.default.cyan("node9 skill pin list")} to see pinned roots.
|
|
27949
28157
|
`);
|
|
27950
28158
|
process.exit(1);
|
|
27951
28159
|
}
|
|
27952
28160
|
const rootPath = pins.roots[rootKey].rootPath;
|
|
27953
28161
|
removePin2(rootKey);
|
|
27954
28162
|
wipeSkillSessions();
|
|
27955
|
-
console.log(
|
|
27956
|
-
\u{1F513} Pin removed for ${
|
|
27957
|
-
console.log(
|
|
27958
|
-
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"));
|
|
27959
28167
|
});
|
|
27960
28168
|
pinSubCmd.command("reset").description("Clear all skill pins and wipe session verification flags").action(() => {
|
|
27961
28169
|
const result = readSkillPinsSafe();
|
|
27962
28170
|
if (!result.ok && result.reason === "missing") {
|
|
27963
28171
|
wipeSkillSessions();
|
|
27964
|
-
console.log(
|
|
28172
|
+
console.log(import_chalk32.default.gray("\nNo pins to clear.\n"));
|
|
27965
28173
|
return;
|
|
27966
28174
|
}
|
|
27967
28175
|
const count = result.ok ? Object.keys(result.pins.roots).length : "?";
|
|
27968
28176
|
clearAllPins2();
|
|
27969
28177
|
wipeSkillSessions();
|
|
27970
|
-
console.log(
|
|
28178
|
+
console.log(import_chalk32.default.green(`
|
|
27971
28179
|
\u{1F513} Cleared ${count} skill pin(s).`));
|
|
27972
|
-
console.log(
|
|
28180
|
+
console.log(import_chalk32.default.gray(" Next session will re-pin with current state.\n"));
|
|
27973
28181
|
});
|
|
27974
28182
|
}
|
|
27975
28183
|
|
|
@@ -27977,7 +28185,7 @@ function registerSkillPinCommand(program2) {
|
|
|
27977
28185
|
var import_fs61 = __toESM(require("fs"));
|
|
27978
28186
|
var import_os52 = __toESM(require("os"));
|
|
27979
28187
|
var import_path59 = __toESM(require("path"));
|
|
27980
|
-
var
|
|
28188
|
+
var import_chalk33 = __toESM(require("chalk"));
|
|
27981
28189
|
var DECISIONS_FILE2 = import_path59.default.join(import_os52.default.homedir(), ".node9", "decisions.json");
|
|
27982
28190
|
function readDecisions() {
|
|
27983
28191
|
try {
|
|
@@ -28006,55 +28214,55 @@ function registerDecisionsCommand(program2) {
|
|
|
28006
28214
|
const decisions = readDecisions();
|
|
28007
28215
|
const entries = Object.entries(decisions);
|
|
28008
28216
|
if (entries.length === 0) {
|
|
28009
|
-
console.log(
|
|
28217
|
+
console.log(import_chalk33.default.gray(" No persistent decisions stored."));
|
|
28010
28218
|
console.log(
|
|
28011
|
-
|
|
28012
|
-
`) +
|
|
28219
|
+
import_chalk33.default.gray(` File: ${DECISIONS_FILE2}
|
|
28220
|
+
`) + import_chalk33.default.gray(' Decisions are written when you click "Always Allow" or')
|
|
28013
28221
|
);
|
|
28014
|
-
console.log(
|
|
28222
|
+
console.log(import_chalk33.default.gray(' "Always Deny" in node9 tail or the native popup.'));
|
|
28015
28223
|
return;
|
|
28016
28224
|
}
|
|
28017
|
-
console.log(
|
|
28225
|
+
console.log(import_chalk33.default.bold(`
|
|
28018
28226
|
Persistent decisions (${entries.length})
|
|
28019
28227
|
`));
|
|
28020
28228
|
const w = Math.max(...entries.map(([k]) => k.length));
|
|
28021
28229
|
for (const [tool, verdict] of entries.sort()) {
|
|
28022
|
-
const colored = verdict === "allow" ?
|
|
28230
|
+
const colored = verdict === "allow" ? import_chalk33.default.green(verdict) : import_chalk33.default.red(verdict);
|
|
28023
28231
|
console.log(` ${tool.padEnd(w)} ${colored}`);
|
|
28024
28232
|
}
|
|
28025
28233
|
console.log(
|
|
28026
|
-
|
|
28234
|
+
import_chalk33.default.gray(`
|
|
28027
28235
|
Stored in ${DECISIONS_FILE2}
|
|
28028
|
-
`) +
|
|
28236
|
+
`) + import_chalk33.default.gray(" Run `node9 decisions clear <tool>` to remove an entry.")
|
|
28029
28237
|
);
|
|
28030
28238
|
});
|
|
28031
28239
|
cmd.command("clear <toolName>").description("Remove a persistent decision for one tool").action((toolName) => {
|
|
28032
28240
|
const decisions = readDecisions();
|
|
28033
28241
|
if (!(toolName in decisions)) {
|
|
28034
|
-
console.log(
|
|
28242
|
+
console.log(import_chalk33.default.yellow(` No persistent decision for "${toolName}". Nothing to clear.`));
|
|
28035
28243
|
process.exitCode = 1;
|
|
28036
28244
|
return;
|
|
28037
28245
|
}
|
|
28038
28246
|
delete decisions[toolName];
|
|
28039
28247
|
writeDecisions(decisions);
|
|
28040
|
-
console.log(
|
|
28248
|
+
console.log(import_chalk33.default.green(` \u2713 Cleared persistent decision for "${toolName}".`));
|
|
28041
28249
|
});
|
|
28042
28250
|
cmd.command("clear-all").description("Remove every persistent decision (irreversible)").action(() => {
|
|
28043
28251
|
const decisions = readDecisions();
|
|
28044
28252
|
const count = Object.keys(decisions).length;
|
|
28045
28253
|
if (count === 0) {
|
|
28046
|
-
console.log(
|
|
28254
|
+
console.log(import_chalk33.default.gray(" Nothing to clear \u2014 no persistent decisions stored."));
|
|
28047
28255
|
return;
|
|
28048
28256
|
}
|
|
28049
28257
|
writeDecisions({});
|
|
28050
28258
|
console.log(
|
|
28051
|
-
|
|
28259
|
+
import_chalk33.default.green(` \u2713 Cleared ${count} persistent decision${count === 1 ? "" : "s"}.`)
|
|
28052
28260
|
);
|
|
28053
28261
|
});
|
|
28054
28262
|
}
|
|
28055
28263
|
|
|
28056
28264
|
// src/cli/commands/dlp.ts
|
|
28057
|
-
var
|
|
28265
|
+
var import_chalk34 = __toESM(require("chalk"));
|
|
28058
28266
|
var import_fs62 = __toESM(require("fs"));
|
|
28059
28267
|
var import_path60 = __toESM(require("path"));
|
|
28060
28268
|
var import_os53 = __toESM(require("os"));
|
|
@@ -28109,14 +28317,14 @@ function registerDlpCommand(program2) {
|
|
|
28109
28317
|
cmd.command("resolve").description("Mark all current DLP findings as resolved").action(() => {
|
|
28110
28318
|
const findings = loadDlpFindings();
|
|
28111
28319
|
if (findings.length === 0) {
|
|
28112
|
-
console.log(
|
|
28320
|
+
console.log(import_chalk34.default.green("\n \u2705 No response-DLP findings to resolve.\n"));
|
|
28113
28321
|
return;
|
|
28114
28322
|
}
|
|
28115
28323
|
const resolved = loadResolved();
|
|
28116
28324
|
for (const e of findings) resolved.add(entryKey2(e));
|
|
28117
28325
|
saveResolved(resolved);
|
|
28118
28326
|
console.log(
|
|
28119
|
-
|
|
28327
|
+
import_chalk34.default.green(
|
|
28120
28328
|
`
|
|
28121
28329
|
\u2705 ${findings.length} finding${findings.length !== 1 ? "s" : ""} marked as resolved.
|
|
28122
28330
|
`
|
|
@@ -28130,54 +28338,54 @@ function registerDlpCommand(program2) {
|
|
|
28130
28338
|
const resolvedCount = findings.length - open.length;
|
|
28131
28339
|
console.log("");
|
|
28132
28340
|
console.log(
|
|
28133
|
-
|
|
28341
|
+
import_chalk34.default.bold.cyan("\u{1F510} node9 dlp") + import_chalk34.default.dim(" \u2014 secrets found in Claude response text")
|
|
28134
28342
|
);
|
|
28135
28343
|
console.log("");
|
|
28136
28344
|
if (open.length === 0) {
|
|
28137
28345
|
if (resolvedCount > 0) {
|
|
28138
|
-
console.log(
|
|
28346
|
+
console.log(import_chalk34.default.green(` \u2705 No open findings \xB7 ${resolvedCount} previously resolved`));
|
|
28139
28347
|
} else {
|
|
28140
28348
|
console.log(
|
|
28141
|
-
|
|
28349
|
+
import_chalk34.default.green(" \u2705 No findings \u2014 Claude has not leaked secrets in response text")
|
|
28142
28350
|
);
|
|
28143
28351
|
}
|
|
28144
28352
|
console.log("");
|
|
28145
28353
|
return;
|
|
28146
28354
|
}
|
|
28147
28355
|
console.log(
|
|
28148
|
-
|
|
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)` : "")
|
|
28149
28357
|
);
|
|
28150
28358
|
console.log("");
|
|
28151
28359
|
console.log(
|
|
28152
|
-
|
|
28360
|
+
import_chalk34.default.dim(" These secrets were included in Claude's response text \u2014 NOT blocked.")
|
|
28153
28361
|
);
|
|
28154
|
-
console.log(
|
|
28362
|
+
console.log(import_chalk34.default.dim(" Rotate each affected key immediately.\n"));
|
|
28155
28363
|
for (const e of open) {
|
|
28156
28364
|
console.log(
|
|
28157
|
-
" " +
|
|
28365
|
+
" " + import_chalk34.default.red("\u25CF") + " " + import_chalk34.default.white(e.dlpPattern ?? "Secret") + import_chalk34.default.dim(" " + fmtDate3(e.ts))
|
|
28158
28366
|
);
|
|
28159
28367
|
if (e.dlpSample) {
|
|
28160
|
-
console.log(" " +
|
|
28368
|
+
console.log(" " + import_chalk34.default.dim("Sample: ") + import_chalk34.default.yellow(stripAnsi(e.dlpSample)));
|
|
28161
28369
|
}
|
|
28162
28370
|
if (e.project) {
|
|
28163
|
-
console.log(" " +
|
|
28371
|
+
console.log(" " + import_chalk34.default.dim("Project: ") + import_chalk34.default.dim(stripAnsi(e.project)));
|
|
28164
28372
|
}
|
|
28165
28373
|
console.log("");
|
|
28166
28374
|
}
|
|
28167
|
-
console.log(" " +
|
|
28168
|
-
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");
|
|
28169
28377
|
console.log(
|
|
28170
|
-
" " +
|
|
28378
|
+
" " + import_chalk34.default.cyan("2.") + " Run " + import_chalk34.default.white("node9 dlp resolve") + " to acknowledge"
|
|
28171
28379
|
);
|
|
28172
28380
|
console.log(
|
|
28173
|
-
" " +
|
|
28381
|
+
" " + import_chalk34.default.cyan("3.") + " Run " + import_chalk34.default.white("node9 report") + " for full audit history"
|
|
28174
28382
|
);
|
|
28175
28383
|
console.log("");
|
|
28176
28384
|
});
|
|
28177
28385
|
}
|
|
28178
28386
|
|
|
28179
28387
|
// src/cli/commands/mask.ts
|
|
28180
|
-
var
|
|
28388
|
+
var import_chalk35 = __toESM(require("chalk"));
|
|
28181
28389
|
var import_fs63 = __toESM(require("fs"));
|
|
28182
28390
|
var import_path61 = __toESM(require("path"));
|
|
28183
28391
|
var import_os54 = __toESM(require("os"));
|
|
@@ -28314,12 +28522,12 @@ function registerMaskCommand(program2) {
|
|
|
28314
28522
|
}
|
|
28315
28523
|
}) : allFiles;
|
|
28316
28524
|
if (filtered.length === 0) {
|
|
28317
|
-
console.log(
|
|
28525
|
+
console.log(import_chalk35.default.yellow(" No session files found."));
|
|
28318
28526
|
return;
|
|
28319
28527
|
}
|
|
28320
28528
|
console.log("");
|
|
28321
28529
|
if (dryRun) {
|
|
28322
|
-
console.log(
|
|
28530
|
+
console.log(import_chalk35.default.dim(" Dry run \u2014 no files will be modified.\n"));
|
|
28323
28531
|
}
|
|
28324
28532
|
let totalFiles = 0;
|
|
28325
28533
|
let totalLines = 0;
|
|
@@ -28335,23 +28543,23 @@ function registerMaskCommand(program2) {
|
|
|
28335
28543
|
});
|
|
28336
28544
|
const verb = dryRun ? "Would redact" : "Redacted";
|
|
28337
28545
|
console.log(
|
|
28338
|
-
" " +
|
|
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" : ""})`)
|
|
28339
28547
|
);
|
|
28340
28548
|
}
|
|
28341
28549
|
}
|
|
28342
28550
|
console.log("");
|
|
28343
28551
|
if (totalFiles === 0) {
|
|
28344
|
-
console.log(
|
|
28552
|
+
console.log(import_chalk35.default.green(" No secrets found in session history."));
|
|
28345
28553
|
} else {
|
|
28346
28554
|
const verb = dryRun ? "would be modified" : "modified";
|
|
28347
28555
|
console.log(
|
|
28348
|
-
|
|
28556
|
+
import_chalk35.default.bold(` ${totalFiles} file${totalFiles !== 1 ? "s" : ""} ${verb}`) + import_chalk35.default.dim(`, ${totalLines} line${totalLines !== 1 ? "s" : ""} redacted`)
|
|
28349
28557
|
);
|
|
28350
|
-
console.log(" Patterns: " +
|
|
28558
|
+
console.log(" Patterns: " + import_chalk35.default.yellow(totalPatterns.join(", ")));
|
|
28351
28559
|
if (!dryRun) {
|
|
28352
28560
|
console.log("");
|
|
28353
28561
|
console.log(
|
|
28354
|
-
|
|
28562
|
+
import_chalk35.default.dim(
|
|
28355
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."
|
|
28356
28564
|
)
|
|
28357
28565
|
);
|
|
@@ -28369,77 +28577,34 @@ var { version } = JSON.parse(
|
|
|
28369
28577
|
var program = new import_commander.Command();
|
|
28370
28578
|
program.name("node9").description("The Sudo Command for AI Agents").version(version);
|
|
28371
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) => {
|
|
28372
|
-
const
|
|
28373
|
-
|
|
28374
|
-
|
|
28375
|
-
|
|
28376
|
-
const profileName = options.profile || "default";
|
|
28377
|
-
let existingCreds = {};
|
|
28378
|
-
try {
|
|
28379
|
-
if (import_fs66.default.existsSync(credPath)) {
|
|
28380
|
-
const raw = JSON.parse(import_fs66.default.readFileSync(credPath, "utf-8"));
|
|
28381
|
-
if (raw.apiKey) {
|
|
28382
|
-
existingCreds = {
|
|
28383
|
-
default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL2 }
|
|
28384
|
-
};
|
|
28385
|
-
} else {
|
|
28386
|
-
existingCreds = raw;
|
|
28387
|
-
}
|
|
28388
|
-
}
|
|
28389
|
-
} catch {
|
|
28390
|
-
}
|
|
28391
|
-
existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL2 };
|
|
28392
|
-
import_fs66.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
|
|
28393
|
-
let effectiveCloud = null;
|
|
28394
|
-
if (profileName === "default") {
|
|
28395
|
-
const configPath = import_path64.default.join(import_os57.default.homedir(), ".node9", "config.json");
|
|
28396
|
-
let config = {};
|
|
28397
|
-
try {
|
|
28398
|
-
if (import_fs66.default.existsSync(configPath))
|
|
28399
|
-
config = JSON.parse(import_fs66.default.readFileSync(configPath, "utf-8"));
|
|
28400
|
-
} catch {
|
|
28401
|
-
}
|
|
28402
|
-
if (!config.settings || typeof config.settings !== "object") config.settings = {};
|
|
28403
|
-
const s = config.settings;
|
|
28404
|
-
const approvers = s.approvers || {
|
|
28405
|
-
native: true,
|
|
28406
|
-
browser: true,
|
|
28407
|
-
cloud: true,
|
|
28408
|
-
terminal: true
|
|
28409
|
-
};
|
|
28410
|
-
if (options.local) {
|
|
28411
|
-
approvers.cloud = false;
|
|
28412
|
-
}
|
|
28413
|
-
s.approvers = approvers;
|
|
28414
|
-
if (!import_fs66.default.existsSync(import_path64.default.dirname(configPath)))
|
|
28415
|
-
import_fs66.default.mkdirSync(import_path64.default.dirname(configPath), { recursive: true });
|
|
28416
|
-
import_fs66.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
28417
|
-
effectiveCloud = approvers.cloud === true;
|
|
28418
|
-
}
|
|
28580
|
+
const { profileName, effectiveCloud } = writeCredentialsAndConfig(apiKey, {
|
|
28581
|
+
profileName: options.profile,
|
|
28582
|
+
isLocal: options.local
|
|
28583
|
+
});
|
|
28419
28584
|
if (options.profile && profileName !== "default") {
|
|
28420
|
-
console.log(
|
|
28421
|
-
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`));
|
|
28422
28587
|
} else if (options.local || effectiveCloud === false) {
|
|
28423
|
-
console.log(
|
|
28424
|
-
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.`));
|
|
28425
28590
|
if (!options.local) {
|
|
28426
28591
|
console.log(
|
|
28427
|
-
|
|
28592
|
+
import_chalk37.default.yellow(` Your config has cloud approvals OFF (settings.approvers.cloud).`)
|
|
28428
28593
|
);
|
|
28429
28594
|
console.log(
|
|
28430
|
-
|
|
28595
|
+
import_chalk37.default.gray(` To enable team policy + dashboard sync: set it to true, or re-init.`)
|
|
28431
28596
|
);
|
|
28432
28597
|
}
|
|
28433
28598
|
} else {
|
|
28434
|
-
console.log(
|
|
28435
|
-
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.`));
|
|
28436
28601
|
}
|
|
28437
28602
|
});
|
|
28438
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) => {
|
|
28439
28604
|
const route = options.login ? "auth/login" : "auth/signup";
|
|
28440
28605
|
const url = `https://node9.ai/${route}?ref=cli_cmd`;
|
|
28441
28606
|
console.log("");
|
|
28442
|
-
console.log(" " +
|
|
28607
|
+
console.log(" " + import_chalk37.default.dim("Opening ") + import_chalk37.default.cyan.underline(url));
|
|
28443
28608
|
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
28444
28609
|
try {
|
|
28445
28610
|
const child = (0, import_child_process15.spawn)(opener, [url], {
|
|
@@ -28472,7 +28637,7 @@ program.command("addto", { hidden: true }).description("Integrate Node9 with an
|
|
|
28472
28637
|
if (target === "hermes") return setupHermes();
|
|
28473
28638
|
if (target === "hud") return setupHud();
|
|
28474
28639
|
console.error(
|
|
28475
|
-
|
|
28640
|
+
import_chalk37.default.red(
|
|
28476
28641
|
`Unknown target: "${target}". Supported: claude, antigravity, copilot, gemini, cursor, codex, windsurf, vscode, hermes, hud`
|
|
28477
28642
|
)
|
|
28478
28643
|
);
|
|
@@ -28486,20 +28651,20 @@ program.command("setup", { hidden: true }).description('Alias for "addto" \u2014
|
|
|
28486
28651
|
"The agent to protect: claude | antigravity | copilot | gemini | cursor | codex | windsurf | vscode | hud"
|
|
28487
28652
|
).action(async (target) => {
|
|
28488
28653
|
if (!target) {
|
|
28489
|
-
console.log(
|
|
28490
|
-
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");
|
|
28491
28656
|
console.log(" Targets:");
|
|
28492
|
-
console.log(" " +
|
|
28493
|
-
console.log(" " +
|
|
28494
|
-
console.log(" " +
|
|
28495
|
-
console.log(" " +
|
|
28496
|
-
console.log(" " +
|
|
28497
|
-
console.log(" " +
|
|
28498
|
-
console.log(" " +
|
|
28499
|
-
console.log(" " +
|
|
28500
|
-
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)");
|
|
28501
28666
|
process.stdout.write(
|
|
28502
|
-
" " +
|
|
28667
|
+
" " + import_chalk37.default.green("hud") + " \u2014 Claude Code security statusline\n"
|
|
28503
28668
|
);
|
|
28504
28669
|
console.log("");
|
|
28505
28670
|
return;
|
|
@@ -28516,7 +28681,7 @@ program.command("setup", { hidden: true }).description('Alias for "addto" \u2014
|
|
|
28516
28681
|
if (t === "hermes") return setupHermes();
|
|
28517
28682
|
if (t === "hud") return setupHud();
|
|
28518
28683
|
console.error(
|
|
28519
|
-
|
|
28684
|
+
import_chalk37.default.red(
|
|
28520
28685
|
`Unknown target: "${target}". Supported: claude, antigravity, copilot, gemini, cursor, codex, windsurf, vscode, hermes, hud`
|
|
28521
28686
|
)
|
|
28522
28687
|
);
|
|
@@ -28527,32 +28692,32 @@ program.command("removefrom", { hidden: true }).description("Remove Node9 hooks
|
|
|
28527
28692
|
const agent = resolveAgentTeardown(target);
|
|
28528
28693
|
if (!agent) {
|
|
28529
28694
|
console.error(
|
|
28530
|
-
|
|
28695
|
+
import_chalk37.default.red(`Unknown target: "${target}". Supported: ${agentTeardownTargets().join(", ")}`)
|
|
28531
28696
|
);
|
|
28532
28697
|
process.exit(1);
|
|
28533
28698
|
return;
|
|
28534
28699
|
}
|
|
28535
|
-
console.log(
|
|
28700
|
+
console.log(import_chalk37.default.cyan(`
|
|
28536
28701
|
\u{1F6E1}\uFE0F Node9: removing hooks from ${agent.label}...
|
|
28537
28702
|
`));
|
|
28538
28703
|
try {
|
|
28539
28704
|
agent.fn();
|
|
28540
28705
|
} catch (err2) {
|
|
28541
|
-
console.error(
|
|
28706
|
+
console.error(import_chalk37.default.red(` \u26A0\uFE0F Failed: ${err2 instanceof Error ? err2.message : String(err2)}`));
|
|
28542
28707
|
process.exit(1);
|
|
28543
28708
|
}
|
|
28544
|
-
console.log(
|
|
28709
|
+
console.log(import_chalk37.default.gray("\n Restart the agent for changes to take effect."));
|
|
28545
28710
|
});
|
|
28546
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) => {
|
|
28547
|
-
console.log(
|
|
28548
|
-
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..."));
|
|
28549
28714
|
try {
|
|
28550
28715
|
stopDaemon();
|
|
28551
|
-
console.log(
|
|
28716
|
+
console.log(import_chalk37.default.green(" \u2705 Daemon stopped"));
|
|
28552
28717
|
} catch {
|
|
28553
|
-
console.log(
|
|
28718
|
+
console.log(import_chalk37.default.blue(" \u2139\uFE0F Daemon was not running"));
|
|
28554
28719
|
}
|
|
28555
|
-
console.log(
|
|
28720
|
+
console.log(import_chalk37.default.bold("\nRemoving hooks..."));
|
|
28556
28721
|
let teardownFailed = false;
|
|
28557
28722
|
for (const { label: label2, fn } of AGENT_TEARDOWNS) {
|
|
28558
28723
|
try {
|
|
@@ -28560,7 +28725,7 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
28560
28725
|
} catch (err2) {
|
|
28561
28726
|
teardownFailed = true;
|
|
28562
28727
|
console.error(
|
|
28563
|
-
|
|
28728
|
+
import_chalk37.default.red(
|
|
28564
28729
|
` \u26A0\uFE0F Failed to remove ${label2} hooks: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
28565
28730
|
)
|
|
28566
28731
|
);
|
|
@@ -28569,12 +28734,12 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
28569
28734
|
try {
|
|
28570
28735
|
const residual = getAgentWiring().filter((a) => a.wireState === "wired");
|
|
28571
28736
|
if (residual.length === 0) {
|
|
28572
|
-
console.log(
|
|
28737
|
+
console.log(import_chalk37.default.green(" \u2705 Verified \u2014 no node9 hooks or plugin shims remain"));
|
|
28573
28738
|
} else {
|
|
28574
28739
|
teardownFailed = true;
|
|
28575
|
-
console.error(
|
|
28740
|
+
console.error(import_chalk37.default.red(" \u26A0\uFE0F Still wired after teardown:"));
|
|
28576
28741
|
for (const a of residual) {
|
|
28577
|
-
console.error(
|
|
28742
|
+
console.error(import_chalk37.default.red(` \u2022 ${a.label} \u2014 ${a.settingsPath}`));
|
|
28578
28743
|
}
|
|
28579
28744
|
}
|
|
28580
28745
|
} catch {
|
|
@@ -28590,28 +28755,28 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
28590
28755
|
import_fs66.default.rmSync(node9Dir, { recursive: true });
|
|
28591
28756
|
if (import_fs66.default.existsSync(node9Dir)) {
|
|
28592
28757
|
console.error(
|
|
28593
|
-
|
|
28758
|
+
import_chalk37.default.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
|
|
28594
28759
|
);
|
|
28595
28760
|
} else {
|
|
28596
|
-
console.log(
|
|
28761
|
+
console.log(import_chalk37.default.green("\n \u2705 Deleted ~/.node9/ (config, audit log, credentials)"));
|
|
28597
28762
|
}
|
|
28598
28763
|
} else {
|
|
28599
|
-
console.log(
|
|
28764
|
+
console.log(import_chalk37.default.yellow("\n Skipped \u2014 ~/.node9/ was not deleted."));
|
|
28600
28765
|
}
|
|
28601
28766
|
} else {
|
|
28602
|
-
console.log(
|
|
28767
|
+
console.log(import_chalk37.default.blue("\n \u2139\uFE0F ~/.node9/ not found \u2014 nothing to delete"));
|
|
28603
28768
|
}
|
|
28604
28769
|
} else {
|
|
28605
28770
|
console.log(
|
|
28606
|
-
|
|
28771
|
+
import_chalk37.default.gray("\n ~/.node9/ kept \u2014 run with --purge to delete config and audit log")
|
|
28607
28772
|
);
|
|
28608
28773
|
}
|
|
28609
28774
|
if (teardownFailed) {
|
|
28610
|
-
console.error(
|
|
28775
|
+
console.error(import_chalk37.default.red("\n \u26A0\uFE0F Some hooks could not be removed \u2014 see errors above."));
|
|
28611
28776
|
process.exit(1);
|
|
28612
28777
|
}
|
|
28613
|
-
console.log(
|
|
28614
|
-
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"));
|
|
28615
28780
|
});
|
|
28616
28781
|
registerDoctorCommand(program, version);
|
|
28617
28782
|
program.command("explain").description(
|
|
@@ -28624,7 +28789,7 @@ program.command("explain").description(
|
|
|
28624
28789
|
try {
|
|
28625
28790
|
args = JSON.parse(trimmed);
|
|
28626
28791
|
} catch {
|
|
28627
|
-
console.error(
|
|
28792
|
+
console.error(import_chalk37.default.red(`
|
|
28628
28793
|
\u274C Invalid JSON: ${trimmed}
|
|
28629
28794
|
`));
|
|
28630
28795
|
process.exit(1);
|
|
@@ -28635,67 +28800,68 @@ program.command("explain").description(
|
|
|
28635
28800
|
}
|
|
28636
28801
|
const result = await explainPolicy(tool, args);
|
|
28637
28802
|
console.log("");
|
|
28638
|
-
console.log(
|
|
28803
|
+
console.log(import_chalk37.default.cyan.bold("\u{1F6E1}\uFE0F Node9 Explain"));
|
|
28639
28804
|
console.log("");
|
|
28640
|
-
console.log(` ${
|
|
28805
|
+
console.log(` ${import_chalk37.default.bold("Tool:")} ${import_chalk37.default.white(result.tool)}`);
|
|
28641
28806
|
if (argsRaw) {
|
|
28642
28807
|
const preview2 = argsRaw.length > 80 ? argsRaw.slice(0, 77) + "\u2026" : argsRaw;
|
|
28643
|
-
console.log(` ${
|
|
28808
|
+
console.log(` ${import_chalk37.default.bold("Input:")} ${import_chalk37.default.gray(preview2)}`);
|
|
28644
28809
|
}
|
|
28645
28810
|
console.log("");
|
|
28646
|
-
console.log(
|
|
28811
|
+
console.log(import_chalk37.default.bold("Config Sources (Waterfall):"));
|
|
28647
28812
|
for (const tier of result.waterfall) {
|
|
28648
|
-
const num3 =
|
|
28813
|
+
const num3 = import_chalk37.default.gray(` ${tier.tier}.`);
|
|
28649
28814
|
const label2 = tier.label.padEnd(16);
|
|
28650
28815
|
let statusStr;
|
|
28651
28816
|
if (tier.tier === 1) {
|
|
28652
|
-
statusStr =
|
|
28817
|
+
statusStr = import_chalk37.default.gray(tier.note ?? "");
|
|
28653
28818
|
} else if (tier.status === "active") {
|
|
28654
|
-
const loc = tier.path ?
|
|
28655
|
-
const note = tier.note ?
|
|
28656
|
-
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 : "");
|
|
28657
28822
|
} else {
|
|
28658
|
-
statusStr =
|
|
28823
|
+
statusStr = import_chalk37.default.gray("\u25CB " + (tier.note ?? "not found"));
|
|
28659
28824
|
}
|
|
28660
|
-
console.log(`${num3} ${
|
|
28825
|
+
console.log(`${num3} ${import_chalk37.default.white(label2)} ${statusStr}`);
|
|
28661
28826
|
}
|
|
28662
28827
|
console.log("");
|
|
28663
|
-
console.log(
|
|
28828
|
+
console.log(import_chalk37.default.bold("Policy Evaluation:"));
|
|
28664
28829
|
for (const step of result.steps) {
|
|
28665
28830
|
const isFinal = step.isFinal;
|
|
28666
28831
|
let icon;
|
|
28667
|
-
if (step.outcome === "allow") icon =
|
|
28668
|
-
else if (step.outcome === "block") icon =
|
|
28669
|
-
else if (step.outcome === "review") icon =
|
|
28670
|
-
else if (step.outcome === "skip") icon =
|
|
28671
|
-
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 ");
|
|
28672
28837
|
const name = step.name.padEnd(18);
|
|
28673
|
-
const nameStr = isFinal ?
|
|
28674
|
-
const detail = isFinal ?
|
|
28675
|
-
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") : "";
|
|
28676
28841
|
console.log(`${icon} ${nameStr} ${detail}${arrow}`);
|
|
28677
28842
|
}
|
|
28678
28843
|
console.log("");
|
|
28679
28844
|
if (result.decision === "allow") {
|
|
28680
|
-
console.log(
|
|
28845
|
+
console.log(import_chalk37.default.green.bold(" Decision: \u2705 ALLOW") + import_chalk37.default.gray(" \u2014 no approval needed"));
|
|
28681
28846
|
} else if (result.decision === "block") {
|
|
28682
28847
|
console.log(
|
|
28683
|
-
|
|
28848
|
+
import_chalk37.default.red.bold(" Decision: \u{1F6D1} BLOCK") + import_chalk37.default.gray(" \u2014 this action is blocked")
|
|
28684
28849
|
);
|
|
28685
28850
|
if (result.blockedByLabel) {
|
|
28686
|
-
console.log(
|
|
28851
|
+
console.log(import_chalk37.default.gray(` Reason: ${result.blockedByLabel}`));
|
|
28687
28852
|
}
|
|
28688
28853
|
} else {
|
|
28689
28854
|
console.log(
|
|
28690
|
-
|
|
28855
|
+
import_chalk37.default.red.bold(" Decision: \u{1F534} REVIEW") + import_chalk37.default.gray(" \u2014 human approval required")
|
|
28691
28856
|
);
|
|
28692
28857
|
if (result.blockedByLabel) {
|
|
28693
|
-
console.log(
|
|
28858
|
+
console.log(import_chalk37.default.gray(` Reason: ${result.blockedByLabel}`));
|
|
28694
28859
|
}
|
|
28695
28860
|
}
|
|
28696
28861
|
console.log("");
|
|
28697
28862
|
});
|
|
28698
28863
|
registerInitCommand(program);
|
|
28864
|
+
registerConnectCommand(program);
|
|
28699
28865
|
registerAuditCommand(program);
|
|
28700
28866
|
registerReportCommand(program);
|
|
28701
28867
|
registerStatusCommand(program);
|
|
@@ -28705,7 +28871,7 @@ program.command("tail").description("Stream live agent activity to the terminal"
|
|
|
28705
28871
|
try {
|
|
28706
28872
|
await startTail2(options);
|
|
28707
28873
|
} catch (err2) {
|
|
28708
|
-
console.error(
|
|
28874
|
+
console.error(import_chalk37.default.red(`\u274C ${err2 instanceof Error ? err2.message : String(err2)}`));
|
|
28709
28875
|
process.exit(1);
|
|
28710
28876
|
}
|
|
28711
28877
|
});
|
|
@@ -28716,7 +28882,7 @@ program.command("monitor").description("Live interactive dashboard \u2014 activi
|
|
|
28716
28882
|
const mod = await dynamicImport(`file://${dashboardPath}`);
|
|
28717
28883
|
await mod.startMonitor();
|
|
28718
28884
|
} catch (err2) {
|
|
28719
|
-
console.error(
|
|
28885
|
+
console.error(import_chalk37.default.red(`\u274C ${err2 instanceof Error ? err2.message : String(err2)}`));
|
|
28720
28886
|
process.exit(1);
|
|
28721
28887
|
}
|
|
28722
28888
|
});
|
|
@@ -28771,7 +28937,7 @@ program.command("pause").description("Temporarily disable Node9 protection for a
|
|
|
28771
28937
|
const ms = parseDuration(options.duration);
|
|
28772
28938
|
if (ms === null) {
|
|
28773
28939
|
console.error(
|
|
28774
|
-
|
|
28940
|
+
import_chalk37.default.red(`
|
|
28775
28941
|
\u274C Invalid duration: "${options.duration}". Use format like 15m, 1h, 30s.
|
|
28776
28942
|
`)
|
|
28777
28943
|
);
|
|
@@ -28779,20 +28945,20 @@ program.command("pause").description("Temporarily disable Node9 protection for a
|
|
|
28779
28945
|
}
|
|
28780
28946
|
pauseNode9(ms, options.duration);
|
|
28781
28947
|
const expiresAt = new Date(Date.now() + ms).toLocaleTimeString();
|
|
28782
|
-
console.log(
|
|
28948
|
+
console.log(import_chalk37.default.yellow(`
|
|
28783
28949
|
\u23F8 Node9 paused until ${expiresAt}`));
|
|
28784
|
-
console.log(
|
|
28785
|
-
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.
|
|
28786
28952
|
`));
|
|
28787
28953
|
});
|
|
28788
28954
|
program.command("resume").description("Re-enable Node9 protection immediately").action(() => {
|
|
28789
28955
|
const { paused } = checkPause();
|
|
28790
28956
|
if (!paused) {
|
|
28791
|
-
console.log(
|
|
28957
|
+
console.log(import_chalk37.default.gray("\nNode9 is already active \u2014 nothing to resume.\n"));
|
|
28792
28958
|
return;
|
|
28793
28959
|
}
|
|
28794
28960
|
resumeNode9();
|
|
28795
|
-
console.log(
|
|
28961
|
+
console.log(import_chalk37.default.green("\n\u25B6 Node9 resumed \u2014 protection is active.\n"));
|
|
28796
28962
|
});
|
|
28797
28963
|
var HOOK_BASED_AGENTS = {
|
|
28798
28964
|
claude: "claude",
|
|
@@ -28808,15 +28974,15 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
28808
28974
|
if (HOOK_BASED_AGENTS[firstArg2] !== void 0) {
|
|
28809
28975
|
const target = HOOK_BASED_AGENTS[firstArg2];
|
|
28810
28976
|
console.error(
|
|
28811
|
-
|
|
28977
|
+
import_chalk37.default.yellow(`
|
|
28812
28978
|
\u26A0\uFE0F Node9 proxy mode does not support "${target}" directly.`)
|
|
28813
28979
|
);
|
|
28814
|
-
console.error(
|
|
28980
|
+
console.error(import_chalk37.default.white(`
|
|
28815
28981
|
"${target}" uses its own hook system. Use:`));
|
|
28816
28982
|
console.error(
|
|
28817
|
-
|
|
28983
|
+
import_chalk37.default.green(` node9 addto ${target} `) + import_chalk37.default.gray("# one-time setup")
|
|
28818
28984
|
);
|
|
28819
|
-
console.error(
|
|
28985
|
+
console.error(import_chalk37.default.green(` ${target} `) + import_chalk37.default.gray("# run normally"));
|
|
28820
28986
|
process.exit(1);
|
|
28821
28987
|
}
|
|
28822
28988
|
const runArgs = firstArg2 === "shell" ? commandArgs.slice(1) : commandArgs;
|
|
@@ -28833,7 +28999,7 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
28833
28999
|
}
|
|
28834
29000
|
);
|
|
28835
29001
|
if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && getConfig().settings.autoStartDaemon) {
|
|
28836
|
-
console.error(
|
|
29002
|
+
console.error(import_chalk37.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically..."));
|
|
28837
29003
|
const daemonReady = await autoStartDaemonAndWait();
|
|
28838
29004
|
if (daemonReady) result = await authorizeHeadless("shell", { command: fullCommand });
|
|
28839
29005
|
}
|
|
@@ -28846,12 +29012,12 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
28846
29012
|
}
|
|
28847
29013
|
if (!result.approved) {
|
|
28848
29014
|
console.error(
|
|
28849
|
-
|
|
29015
|
+
import_chalk37.default.red(`
|
|
28850
29016
|
\u274C Node9 Blocked: ${result.reason || "Dangerous command detected."}`)
|
|
28851
29017
|
);
|
|
28852
29018
|
process.exit(1);
|
|
28853
29019
|
}
|
|
28854
|
-
console.error(
|
|
29020
|
+
console.error(import_chalk37.default.green("\n\u2705 Approved \u2014 running command...\n"));
|
|
28855
29021
|
await runProxy(fullCommand);
|
|
28856
29022
|
} else {
|
|
28857
29023
|
program.help();
|