@node9/proxy 1.2.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +138 -18
- package/dist/cli.js +312 -110
- package/dist/cli.mjs +308 -106
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -5069,27 +5069,27 @@ function formatBase(activity) {
|
|
|
5069
5069
|
const toolName = activity.tool.slice(0, 16).padEnd(16);
|
|
5070
5070
|
const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ");
|
|
5071
5071
|
const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
|
|
5072
|
-
return `${
|
|
5072
|
+
return `${import_chalk14.default.gray(time)} ${icon} ${import_chalk14.default.white.bold(toolName)} ${import_chalk14.default.dim(argsPreview)}`;
|
|
5073
5073
|
}
|
|
5074
5074
|
function renderResult(activity, result) {
|
|
5075
5075
|
const base = formatBase(activity);
|
|
5076
5076
|
let status;
|
|
5077
5077
|
if (result.status === "allow") {
|
|
5078
|
-
status =
|
|
5078
|
+
status = import_chalk14.default.green("\u2713 ALLOW");
|
|
5079
5079
|
} else if (result.status === "dlp") {
|
|
5080
|
-
status =
|
|
5080
|
+
status = import_chalk14.default.bgRed.white.bold(" \u{1F6E1}\uFE0F DLP ");
|
|
5081
5081
|
} else {
|
|
5082
|
-
status =
|
|
5082
|
+
status = import_chalk14.default.red("\u2717 BLOCK");
|
|
5083
5083
|
}
|
|
5084
5084
|
if (process.stdout.isTTY) {
|
|
5085
|
-
|
|
5086
|
-
|
|
5085
|
+
import_readline3.default.clearLine(process.stdout, 0);
|
|
5086
|
+
import_readline3.default.cursorTo(process.stdout, 0);
|
|
5087
5087
|
}
|
|
5088
5088
|
console.log(`${base} ${status}`);
|
|
5089
5089
|
}
|
|
5090
5090
|
function renderPending(activity) {
|
|
5091
5091
|
if (!process.stdout.isTTY) return;
|
|
5092
|
-
process.stdout.write(`${formatBase(activity)} ${
|
|
5092
|
+
process.stdout.write(`${formatBase(activity)} ${import_chalk14.default.yellow("\u25CF \u2026")}\r`);
|
|
5093
5093
|
}
|
|
5094
5094
|
async function ensureDaemon() {
|
|
5095
5095
|
let pidPort = null;
|
|
@@ -5098,7 +5098,7 @@ async function ensureDaemon() {
|
|
|
5098
5098
|
const { port } = JSON.parse(import_fs19.default.readFileSync(PID_FILE, "utf-8"));
|
|
5099
5099
|
pidPort = port;
|
|
5100
5100
|
} catch {
|
|
5101
|
-
console.error(
|
|
5101
|
+
console.error(import_chalk14.default.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
|
|
5102
5102
|
}
|
|
5103
5103
|
}
|
|
5104
5104
|
const checkPort = pidPort ?? DAEMON_PORT;
|
|
@@ -5109,8 +5109,8 @@ async function ensureDaemon() {
|
|
|
5109
5109
|
if (res.ok) return checkPort;
|
|
5110
5110
|
} catch {
|
|
5111
5111
|
}
|
|
5112
|
-
console.log(
|
|
5113
|
-
const child = (0,
|
|
5112
|
+
console.log(import_chalk14.default.dim("\u{1F6E1}\uFE0F Starting Node9 daemon..."));
|
|
5113
|
+
const child = (0, import_child_process13.spawn)(process.execPath, [process.argv[1], "daemon"], {
|
|
5114
5114
|
detached: true,
|
|
5115
5115
|
stdio: "ignore",
|
|
5116
5116
|
env: { ...process.env, NODE9_AUTO_STARTED: "1" }
|
|
@@ -5126,7 +5126,7 @@ async function ensureDaemon() {
|
|
|
5126
5126
|
} catch {
|
|
5127
5127
|
}
|
|
5128
5128
|
}
|
|
5129
|
-
console.error(
|
|
5129
|
+
console.error(import_chalk14.default.red("\u274C Daemon failed to start. Try: node9 daemon start"));
|
|
5130
5130
|
process.exit(1);
|
|
5131
5131
|
}
|
|
5132
5132
|
function postDecisionHttp(id, decision, csrfToken, port) {
|
|
@@ -5197,7 +5197,7 @@ async function startTail(options = {}) {
|
|
|
5197
5197
|
req2.end();
|
|
5198
5198
|
});
|
|
5199
5199
|
if (result.ok) {
|
|
5200
|
-
console.log(
|
|
5200
|
+
console.log(import_chalk14.default.green("\u2713 Flight Recorder buffer cleared."));
|
|
5201
5201
|
} else if (result.code === "ECONNREFUSED") {
|
|
5202
5202
|
throw new Error("Daemon is not running. Start it with: node9 daemon start");
|
|
5203
5203
|
} else if (result.code === "ETIMEDOUT") {
|
|
@@ -5215,7 +5215,7 @@ async function startTail(options = {}) {
|
|
|
5215
5215
|
let cardLineCount = 0;
|
|
5216
5216
|
let cancelActiveCard = null;
|
|
5217
5217
|
const canApprove = process.stdout.isTTY && process.stdin.isTTY;
|
|
5218
|
-
if (canApprove)
|
|
5218
|
+
if (canApprove) import_readline3.default.emitKeypressEvents(process.stdin);
|
|
5219
5219
|
function clearCard() {
|
|
5220
5220
|
if (cardLineCount > 0) {
|
|
5221
5221
|
process.stdout.write(RESTORE_CURSOR + ERASE_DOWN);
|
|
@@ -5268,8 +5268,8 @@ async function startTail(options = {}) {
|
|
|
5268
5268
|
} catch {
|
|
5269
5269
|
}
|
|
5270
5270
|
});
|
|
5271
|
-
const decisionLabel = decision === "allow" ?
|
|
5272
|
-
console.log(`${
|
|
5271
|
+
const decisionLabel = decision === "allow" ? import_chalk14.default.green("\u2713 ALLOWED (terminal)") : import_chalk14.default.red("\u2717 DENIED (terminal)");
|
|
5272
|
+
console.log(`${import_chalk14.default.cyan("\u25C6")} ${import_chalk14.default.bold(req2.toolName.padEnd(16))} ${decisionLabel}`);
|
|
5273
5273
|
approvalQueue.shift();
|
|
5274
5274
|
cardActive = false;
|
|
5275
5275
|
showNextCard();
|
|
@@ -5299,46 +5299,46 @@ async function startTail(options = {}) {
|
|
|
5299
5299
|
try {
|
|
5300
5300
|
const browserEnabled = getConfig().settings.approvers?.browser !== false;
|
|
5301
5301
|
if (browserEnabled) {
|
|
5302
|
-
if (process.platform === "darwin") (0,
|
|
5302
|
+
if (process.platform === "darwin") (0, import_child_process13.execSync)(`open "${dashboardUrl}"`, { stdio: "ignore" });
|
|
5303
5303
|
else if (process.platform === "win32")
|
|
5304
|
-
(0,
|
|
5305
|
-
else (0,
|
|
5304
|
+
(0, import_child_process13.execSync)(`cmd /c start "" "${dashboardUrl}"`, { stdio: "ignore" });
|
|
5305
|
+
else (0, import_child_process13.execSync)(`xdg-open "${dashboardUrl}"`, { stdio: "ignore" });
|
|
5306
5306
|
}
|
|
5307
5307
|
} catch {
|
|
5308
5308
|
}
|
|
5309
|
-
console.log(
|
|
5310
|
-
\u{1F6F0}\uFE0F Node9 tail `) +
|
|
5309
|
+
console.log(import_chalk14.default.cyan.bold(`
|
|
5310
|
+
\u{1F6F0}\uFE0F Node9 tail `) + import_chalk14.default.dim(`\u2192 ${dashboardUrl}`));
|
|
5311
5311
|
if (canApprove) {
|
|
5312
|
-
console.log(
|
|
5312
|
+
console.log(import_chalk14.default.dim("Interactive approvals enabled. [A] Allow [D] Deny"));
|
|
5313
5313
|
}
|
|
5314
5314
|
if (options.history) {
|
|
5315
|
-
console.log(
|
|
5315
|
+
console.log(import_chalk14.default.dim("Showing history + live events. Press Ctrl+C to exit.\n"));
|
|
5316
5316
|
} else {
|
|
5317
5317
|
console.log(
|
|
5318
|
-
|
|
5318
|
+
import_chalk14.default.dim("Showing live events only. Use --history to include past. Press Ctrl+C to exit.\n")
|
|
5319
5319
|
);
|
|
5320
5320
|
}
|
|
5321
5321
|
process.on("SIGINT", () => {
|
|
5322
5322
|
clearCard();
|
|
5323
5323
|
process.stdout.write(SHOW_CURSOR);
|
|
5324
5324
|
if (process.stdout.isTTY) {
|
|
5325
|
-
|
|
5326
|
-
|
|
5325
|
+
import_readline3.default.clearLine(process.stdout, 0);
|
|
5326
|
+
import_readline3.default.cursorTo(process.stdout, 0);
|
|
5327
5327
|
}
|
|
5328
|
-
console.log(
|
|
5328
|
+
console.log(import_chalk14.default.dim("\n\u{1F6F0}\uFE0F Disconnected."));
|
|
5329
5329
|
process.exit(0);
|
|
5330
5330
|
});
|
|
5331
5331
|
const sseUrl = `http://127.0.0.1:${port}/events?capabilities=input`;
|
|
5332
5332
|
const req = import_http2.default.get(sseUrl, (res) => {
|
|
5333
5333
|
if (res.statusCode !== 200) {
|
|
5334
|
-
console.error(
|
|
5334
|
+
console.error(import_chalk14.default.red(`Failed to connect: HTTP ${res.statusCode}`));
|
|
5335
5335
|
process.exit(1);
|
|
5336
5336
|
}
|
|
5337
5337
|
let currentEvent = "";
|
|
5338
5338
|
let currentData = "";
|
|
5339
5339
|
res.on("error", () => {
|
|
5340
5340
|
});
|
|
5341
|
-
const rl =
|
|
5341
|
+
const rl = import_readline3.default.createInterface({ input: res, crlfDelay: Infinity });
|
|
5342
5342
|
rl.on("error", () => {
|
|
5343
5343
|
});
|
|
5344
5344
|
rl.on("line", (line) => {
|
|
@@ -5358,10 +5358,10 @@ async function startTail(options = {}) {
|
|
|
5358
5358
|
clearCard();
|
|
5359
5359
|
process.stdout.write(SHOW_CURSOR);
|
|
5360
5360
|
if (process.stdout.isTTY) {
|
|
5361
|
-
|
|
5362
|
-
|
|
5361
|
+
import_readline3.default.clearLine(process.stdout, 0);
|
|
5362
|
+
import_readline3.default.cursorTo(process.stdout, 0);
|
|
5363
5363
|
}
|
|
5364
|
-
console.log(
|
|
5364
|
+
console.log(import_chalk14.default.red("\n\u274C Daemon disconnected."));
|
|
5365
5365
|
process.exit(1);
|
|
5366
5366
|
});
|
|
5367
5367
|
});
|
|
@@ -5441,22 +5441,22 @@ async function startTail(options = {}) {
|
|
|
5441
5441
|
}
|
|
5442
5442
|
req.on("error", (err) => {
|
|
5443
5443
|
const msg = err.code === "ECONNREFUSED" ? "Daemon is not running. Start it with: node9 daemon start" : err.message;
|
|
5444
|
-
console.error(
|
|
5444
|
+
console.error(import_chalk14.default.red(`
|
|
5445
5445
|
\u274C ${msg}`));
|
|
5446
5446
|
process.exit(1);
|
|
5447
5447
|
});
|
|
5448
5448
|
}
|
|
5449
|
-
var import_http2,
|
|
5449
|
+
var import_http2, import_chalk14, import_fs19, import_os17, import_path20, import_readline3, import_child_process13, PID_FILE, ICONS, RESET, BOLD, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, SAVE_CURSOR, RESTORE_CURSOR;
|
|
5450
5450
|
var init_tail = __esm({
|
|
5451
5451
|
"src/tui/tail.ts"() {
|
|
5452
5452
|
"use strict";
|
|
5453
5453
|
import_http2 = __toESM(require("http"));
|
|
5454
|
-
|
|
5454
|
+
import_chalk14 = __toESM(require("chalk"));
|
|
5455
5455
|
import_fs19 = __toESM(require("fs"));
|
|
5456
5456
|
import_os17 = __toESM(require("os"));
|
|
5457
5457
|
import_path20 = __toESM(require("path"));
|
|
5458
|
-
|
|
5459
|
-
|
|
5458
|
+
import_readline3 = __toESM(require("readline"));
|
|
5459
|
+
import_child_process13 = require("child_process");
|
|
5460
5460
|
init_daemon2();
|
|
5461
5461
|
init_core();
|
|
5462
5462
|
PID_FILE = import_path20.default.join(import_os17.default.homedir(), ".node9", "daemon.pid");
|
|
@@ -5865,7 +5865,7 @@ async function setupCursor() {
|
|
|
5865
5865
|
|
|
5866
5866
|
// src/cli.ts
|
|
5867
5867
|
init_daemon2();
|
|
5868
|
-
var
|
|
5868
|
+
var import_chalk15 = __toESM(require("chalk"));
|
|
5869
5869
|
var import_fs20 = __toESM(require("fs"));
|
|
5870
5870
|
var import_path21 = __toESM(require("path"));
|
|
5871
5871
|
var import_os18 = __toESM(require("os"));
|
|
@@ -7397,6 +7397,207 @@ function registerWatchCommand(program2) {
|
|
|
7397
7397
|
});
|
|
7398
7398
|
}
|
|
7399
7399
|
|
|
7400
|
+
// src/mcp-gateway/index.ts
|
|
7401
|
+
var import_readline2 = __toESM(require("readline"));
|
|
7402
|
+
var import_chalk13 = __toESM(require("chalk"));
|
|
7403
|
+
var import_child_process12 = require("child_process");
|
|
7404
|
+
var import_execa3 = require("execa");
|
|
7405
|
+
init_orchestrator();
|
|
7406
|
+
function sanitize4(value) {
|
|
7407
|
+
return value.replace(/[\x00-\x1F\x7F]/g, "");
|
|
7408
|
+
}
|
|
7409
|
+
var RPC_INVALID_REQUEST = -32600;
|
|
7410
|
+
var RPC_SERVER_ERROR = -32e3;
|
|
7411
|
+
function isValidId(id) {
|
|
7412
|
+
return id === null || typeof id === "string" || typeof id === "number";
|
|
7413
|
+
}
|
|
7414
|
+
function extractMcpServer(toolName) {
|
|
7415
|
+
const match = toolName.match(/^mcp__([^_](?:[^_]|_(?!_))*?)__/i);
|
|
7416
|
+
return match?.[1];
|
|
7417
|
+
}
|
|
7418
|
+
function tokenize2(cmd) {
|
|
7419
|
+
const tokens = [];
|
|
7420
|
+
let current = "";
|
|
7421
|
+
let inDouble = false;
|
|
7422
|
+
let i = 0;
|
|
7423
|
+
while (i < cmd.length) {
|
|
7424
|
+
const ch = cmd[i];
|
|
7425
|
+
if (inDouble) {
|
|
7426
|
+
if (ch === '"') {
|
|
7427
|
+
inDouble = false;
|
|
7428
|
+
} else if (ch === "\\" && i + 1 < cmd.length) {
|
|
7429
|
+
current += cmd[++i];
|
|
7430
|
+
} else {
|
|
7431
|
+
current += ch;
|
|
7432
|
+
}
|
|
7433
|
+
} else {
|
|
7434
|
+
if (ch === '"') {
|
|
7435
|
+
inDouble = true;
|
|
7436
|
+
} else if (ch === " " || ch === " ") {
|
|
7437
|
+
if (current) {
|
|
7438
|
+
tokens.push(current);
|
|
7439
|
+
current = "";
|
|
7440
|
+
}
|
|
7441
|
+
} else if (ch === "\\" && i + 1 < cmd.length) {
|
|
7442
|
+
current += cmd[++i];
|
|
7443
|
+
} else {
|
|
7444
|
+
current += ch;
|
|
7445
|
+
}
|
|
7446
|
+
}
|
|
7447
|
+
i++;
|
|
7448
|
+
}
|
|
7449
|
+
if (current) tokens.push(current);
|
|
7450
|
+
return tokens;
|
|
7451
|
+
}
|
|
7452
|
+
async function runMcpGateway(upstreamCommand) {
|
|
7453
|
+
const commandParts = tokenize2(upstreamCommand);
|
|
7454
|
+
const cmd = commandParts[0];
|
|
7455
|
+
const cmdArgs = commandParts.slice(1);
|
|
7456
|
+
let executable = cmd;
|
|
7457
|
+
try {
|
|
7458
|
+
const { stdout } = await (0, import_execa3.execa)("which", [cmd]);
|
|
7459
|
+
if (stdout) executable = stdout.trim();
|
|
7460
|
+
} catch {
|
|
7461
|
+
}
|
|
7462
|
+
console.error(import_chalk13.default.green(`\u{1F680} Node9 MCP Gateway: Monitoring [${upstreamCommand}]`));
|
|
7463
|
+
const UPSTREAM_INJECTOR_VARS = /* @__PURE__ */ new Set([
|
|
7464
|
+
"NODE_OPTIONS",
|
|
7465
|
+
"NODE_PATH",
|
|
7466
|
+
"LD_PRELOAD",
|
|
7467
|
+
"LD_LIBRARY_PATH",
|
|
7468
|
+
"DYLD_INSERT_LIBRARIES",
|
|
7469
|
+
"PYTHONPATH",
|
|
7470
|
+
"PYTHONSTARTUP",
|
|
7471
|
+
"PERL5LIB",
|
|
7472
|
+
"PERL5OPT",
|
|
7473
|
+
"RUBYLIB",
|
|
7474
|
+
"RUBYOPT",
|
|
7475
|
+
"JAVA_TOOL_OPTIONS",
|
|
7476
|
+
"JDK_JAVA_OPTIONS"
|
|
7477
|
+
]);
|
|
7478
|
+
const safeEnv = Object.fromEntries(
|
|
7479
|
+
Object.entries(process.env).filter(([k]) => !UPSTREAM_INJECTOR_VARS.has(k))
|
|
7480
|
+
);
|
|
7481
|
+
const child = (0, import_child_process12.spawn)(executable, cmdArgs, {
|
|
7482
|
+
stdio: ["pipe", "pipe", "inherit"],
|
|
7483
|
+
// control stdin/stdout; inherit stderr
|
|
7484
|
+
shell: false,
|
|
7485
|
+
env: { ...safeEnv, FORCE_COLOR: "1" }
|
|
7486
|
+
});
|
|
7487
|
+
let authPending = false;
|
|
7488
|
+
let deferredExitCode = null;
|
|
7489
|
+
let deferredStdinEnd = false;
|
|
7490
|
+
const agentIn = import_readline2.default.createInterface({ input: process.stdin, terminal: false });
|
|
7491
|
+
agentIn.on("line", async (line) => {
|
|
7492
|
+
let message;
|
|
7493
|
+
try {
|
|
7494
|
+
const parsed = JSON.parse(line);
|
|
7495
|
+
if ("id" in parsed && !isValidId(parsed.id)) {
|
|
7496
|
+
const errorResponse = {
|
|
7497
|
+
jsonrpc: "2.0",
|
|
7498
|
+
id: null,
|
|
7499
|
+
error: {
|
|
7500
|
+
code: RPC_INVALID_REQUEST,
|
|
7501
|
+
message: "Invalid Request: id must be string, number, or null"
|
|
7502
|
+
}
|
|
7503
|
+
};
|
|
7504
|
+
process.stdout.write(JSON.stringify(errorResponse) + "\n");
|
|
7505
|
+
return;
|
|
7506
|
+
}
|
|
7507
|
+
message = { ...parsed, id: parsed.id };
|
|
7508
|
+
} catch {
|
|
7509
|
+
child.stdin.write(line + "\n");
|
|
7510
|
+
return;
|
|
7511
|
+
}
|
|
7512
|
+
if (message.method === "tools/call" || message.method === "call_tool" || message.method === "use_tool") {
|
|
7513
|
+
agentIn.pause();
|
|
7514
|
+
authPending = true;
|
|
7515
|
+
try {
|
|
7516
|
+
const toolName = sanitize4(
|
|
7517
|
+
String(message.params?.name ?? message.params?.tool_name ?? "unknown")
|
|
7518
|
+
);
|
|
7519
|
+
const toolArgs = message.params?.arguments ?? message.params?.tool_input ?? {};
|
|
7520
|
+
const mcpServer = extractMcpServer(toolName);
|
|
7521
|
+
const result = await authorizeHeadless(toolName, toolArgs, {
|
|
7522
|
+
agent: "MCP-Gateway",
|
|
7523
|
+
mcpServer
|
|
7524
|
+
});
|
|
7525
|
+
if (!result.approved) {
|
|
7526
|
+
console.error(import_chalk13.default.red(`
|
|
7527
|
+
\u{1F6D1} Node9 MCP Gateway: Action Blocked`));
|
|
7528
|
+
console.error(import_chalk13.default.gray(` Tool: ${toolName}`));
|
|
7529
|
+
console.error(import_chalk13.default.gray(` Reason: ${result.reason ?? "Security Policy"}
|
|
7530
|
+
`));
|
|
7531
|
+
const blockedByLabel = result.blockedByLabel ?? result.reason ?? "Security Policy";
|
|
7532
|
+
const isHumanDecision = blockedByLabel.toLowerCase().includes("user") || blockedByLabel.toLowerCase().includes("daemon") || blockedByLabel.toLowerCase().includes("decision");
|
|
7533
|
+
const aiInstruction = buildNegotiationMessage(
|
|
7534
|
+
blockedByLabel,
|
|
7535
|
+
isHumanDecision,
|
|
7536
|
+
result.reason
|
|
7537
|
+
);
|
|
7538
|
+
const errorResponse = {
|
|
7539
|
+
jsonrpc: "2.0",
|
|
7540
|
+
id: message.id ?? null,
|
|
7541
|
+
error: {
|
|
7542
|
+
code: RPC_SERVER_ERROR,
|
|
7543
|
+
message: aiInstruction,
|
|
7544
|
+
data: { reason: result.reason, blockedBy: result.blockedByLabel }
|
|
7545
|
+
}
|
|
7546
|
+
};
|
|
7547
|
+
process.stdout.write(JSON.stringify(errorResponse) + "\n");
|
|
7548
|
+
return;
|
|
7549
|
+
}
|
|
7550
|
+
child.stdin.write(line + "\n");
|
|
7551
|
+
} catch {
|
|
7552
|
+
const errorResponse = {
|
|
7553
|
+
jsonrpc: "2.0",
|
|
7554
|
+
id: message.id ?? null,
|
|
7555
|
+
error: {
|
|
7556
|
+
code: -32e3,
|
|
7557
|
+
message: "Node9: Security engine encountered an error. Action blocked for safety."
|
|
7558
|
+
}
|
|
7559
|
+
};
|
|
7560
|
+
process.stdout.write(JSON.stringify(errorResponse) + "\n");
|
|
7561
|
+
return;
|
|
7562
|
+
} finally {
|
|
7563
|
+
authPending = false;
|
|
7564
|
+
agentIn.resume();
|
|
7565
|
+
if (deferredStdinEnd) child.stdin.end();
|
|
7566
|
+
if (deferredExitCode !== null) process.exit(deferredExitCode);
|
|
7567
|
+
}
|
|
7568
|
+
return;
|
|
7569
|
+
}
|
|
7570
|
+
child.stdin.write(line + "\n");
|
|
7571
|
+
});
|
|
7572
|
+
child.stdout.pipe(process.stdout);
|
|
7573
|
+
process.stdin.on("close", () => {
|
|
7574
|
+
if (authPending) {
|
|
7575
|
+
deferredStdinEnd = true;
|
|
7576
|
+
} else {
|
|
7577
|
+
child.stdin.end();
|
|
7578
|
+
}
|
|
7579
|
+
});
|
|
7580
|
+
child.on("exit", (code) => {
|
|
7581
|
+
if (authPending) {
|
|
7582
|
+
deferredExitCode = code ?? 0;
|
|
7583
|
+
} else {
|
|
7584
|
+
process.exit(code ?? 0);
|
|
7585
|
+
}
|
|
7586
|
+
});
|
|
7587
|
+
}
|
|
7588
|
+
|
|
7589
|
+
// src/cli/commands/mcp-gateway.ts
|
|
7590
|
+
function registerMcpGatewayCommand(program2) {
|
|
7591
|
+
program2.command("mcp-gateway").description(
|
|
7592
|
+
"Run Node9 as an MCP gateway \u2014 intercepts and authorizes tool calls before forwarding to the upstream MCP server"
|
|
7593
|
+
).requiredOption(
|
|
7594
|
+
"--upstream <command>",
|
|
7595
|
+
'The upstream MCP server command to wrap (e.g. "npx -y @modelcontextprotocol/server-filesystem /workspace")'
|
|
7596
|
+
).action(async (options) => {
|
|
7597
|
+
await runMcpGateway(options.upstream);
|
|
7598
|
+
});
|
|
7599
|
+
}
|
|
7600
|
+
|
|
7400
7601
|
// src/cli.ts
|
|
7401
7602
|
var { version } = JSON.parse(
|
|
7402
7603
|
import_fs20.default.readFileSync(import_path21.default.join(__dirname, "../package.json"), "utf-8")
|
|
@@ -7450,31 +7651,31 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
7450
7651
|
import_fs20.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
7451
7652
|
}
|
|
7452
7653
|
if (options.profile && profileName !== "default") {
|
|
7453
|
-
console.log(
|
|
7454
|
-
console.log(
|
|
7654
|
+
console.log(import_chalk15.default.green(`\u2705 Profile "${profileName}" saved`));
|
|
7655
|
+
console.log(import_chalk15.default.gray(` Switch to it per-session: NODE9_PROFILE=${profileName} claude`));
|
|
7455
7656
|
} else if (options.local) {
|
|
7456
|
-
console.log(
|
|
7457
|
-
console.log(
|
|
7657
|
+
console.log(import_chalk15.default.green(`\u2705 Privacy mode \u{1F6E1}\uFE0F`));
|
|
7658
|
+
console.log(import_chalk15.default.gray(` All decisions stay on this machine.`));
|
|
7458
7659
|
} else {
|
|
7459
|
-
console.log(
|
|
7460
|
-
console.log(
|
|
7660
|
+
console.log(import_chalk15.default.green(`\u2705 Logged in \u2014 agent mode`));
|
|
7661
|
+
console.log(import_chalk15.default.gray(` Team policy enforced for all calls via Node9 cloud.`));
|
|
7461
7662
|
}
|
|
7462
7663
|
});
|
|
7463
7664
|
program.command("addto").description("Integrate Node9 with an AI agent").addHelpText("after", "\n Supported targets: claude gemini cursor").argument("<target>", "The agent to protect: claude | gemini | cursor").action(async (target) => {
|
|
7464
7665
|
if (target === "gemini") return await setupGemini();
|
|
7465
7666
|
if (target === "claude") return await setupClaude();
|
|
7466
7667
|
if (target === "cursor") return await setupCursor();
|
|
7467
|
-
console.error(
|
|
7668
|
+
console.error(import_chalk15.default.red(`Unknown target: "${target}". Supported: claude, gemini, cursor`));
|
|
7468
7669
|
process.exit(1);
|
|
7469
7670
|
});
|
|
7470
7671
|
program.command("setup").description('Alias for "addto" \u2014 integrate Node9 with an AI agent').addHelpText("after", "\n Supported targets: claude gemini cursor").argument("[target]", "The agent to protect: claude | gemini | cursor").action(async (target) => {
|
|
7471
7672
|
if (!target) {
|
|
7472
|
-
console.log(
|
|
7473
|
-
console.log(" Usage: " +
|
|
7673
|
+
console.log(import_chalk15.default.cyan("\n\u{1F6E1}\uFE0F Node9 Setup \u2014 integrate with your AI agent\n"));
|
|
7674
|
+
console.log(" Usage: " + import_chalk15.default.white("node9 setup <target>") + "\n");
|
|
7474
7675
|
console.log(" Targets:");
|
|
7475
|
-
console.log(" " +
|
|
7476
|
-
console.log(" " +
|
|
7477
|
-
console.log(" " +
|
|
7676
|
+
console.log(" " + import_chalk15.default.green("claude") + " \u2014 Claude Code (hook mode)");
|
|
7677
|
+
console.log(" " + import_chalk15.default.green("gemini") + " \u2014 Gemini CLI (hook mode)");
|
|
7678
|
+
console.log(" " + import_chalk15.default.green("cursor") + " \u2014 Cursor (hook mode)");
|
|
7478
7679
|
console.log("");
|
|
7479
7680
|
return;
|
|
7480
7681
|
}
|
|
@@ -7482,7 +7683,7 @@ program.command("setup").description('Alias for "addto" \u2014 integrate Node9 w
|
|
|
7482
7683
|
if (t === "gemini") return await setupGemini();
|
|
7483
7684
|
if (t === "claude") return await setupClaude();
|
|
7484
7685
|
if (t === "cursor") return await setupCursor();
|
|
7485
|
-
console.error(
|
|
7686
|
+
console.error(import_chalk15.default.red(`Unknown target: "${target}". Supported: claude, gemini, cursor`));
|
|
7486
7687
|
process.exit(1);
|
|
7487
7688
|
});
|
|
7488
7689
|
program.command("removefrom").description("Remove Node9 hooks from an AI agent configuration").addHelpText("after", "\n Supported targets: claude gemini cursor").argument("<target>", "The agent to remove from: claude | gemini | cursor").action((target) => {
|
|
@@ -7491,30 +7692,30 @@ program.command("removefrom").description("Remove Node9 hooks from an AI agent c
|
|
|
7491
7692
|
else if (target === "gemini") fn = teardownGemini;
|
|
7492
7693
|
else if (target === "cursor") fn = teardownCursor;
|
|
7493
7694
|
else {
|
|
7494
|
-
console.error(
|
|
7695
|
+
console.error(import_chalk15.default.red(`Unknown target: "${target}". Supported: claude, gemini, cursor`));
|
|
7495
7696
|
process.exit(1);
|
|
7496
7697
|
}
|
|
7497
|
-
console.log(
|
|
7698
|
+
console.log(import_chalk15.default.cyan(`
|
|
7498
7699
|
\u{1F6E1}\uFE0F Node9: removing hooks from ${target}...
|
|
7499
7700
|
`));
|
|
7500
7701
|
try {
|
|
7501
7702
|
fn();
|
|
7502
7703
|
} catch (err) {
|
|
7503
|
-
console.error(
|
|
7704
|
+
console.error(import_chalk15.default.red(` \u26A0\uFE0F Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
7504
7705
|
process.exit(1);
|
|
7505
7706
|
}
|
|
7506
|
-
console.log(
|
|
7707
|
+
console.log(import_chalk15.default.gray("\n Restart the agent for changes to take effect."));
|
|
7507
7708
|
});
|
|
7508
7709
|
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) => {
|
|
7509
|
-
console.log(
|
|
7510
|
-
console.log(
|
|
7710
|
+
console.log(import_chalk15.default.cyan("\n\u{1F6E1}\uFE0F Node9 Uninstall\n"));
|
|
7711
|
+
console.log(import_chalk15.default.bold("Stopping daemon..."));
|
|
7511
7712
|
try {
|
|
7512
7713
|
stopDaemon();
|
|
7513
|
-
console.log(
|
|
7714
|
+
console.log(import_chalk15.default.green(" \u2705 Daemon stopped"));
|
|
7514
7715
|
} catch {
|
|
7515
|
-
console.log(
|
|
7716
|
+
console.log(import_chalk15.default.blue(" \u2139\uFE0F Daemon was not running"));
|
|
7516
7717
|
}
|
|
7517
|
-
console.log(
|
|
7718
|
+
console.log(import_chalk15.default.bold("\nRemoving hooks..."));
|
|
7518
7719
|
let teardownFailed = false;
|
|
7519
7720
|
for (const [label, fn] of [
|
|
7520
7721
|
["Claude", teardownClaude],
|
|
@@ -7526,7 +7727,7 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
7526
7727
|
} catch (err) {
|
|
7527
7728
|
teardownFailed = true;
|
|
7528
7729
|
console.error(
|
|
7529
|
-
|
|
7730
|
+
import_chalk15.default.red(
|
|
7530
7731
|
` \u26A0\uFE0F Failed to remove ${label} hooks: ${err instanceof Error ? err.message : String(err)}`
|
|
7531
7732
|
)
|
|
7532
7733
|
);
|
|
@@ -7543,28 +7744,28 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
7543
7744
|
import_fs20.default.rmSync(node9Dir, { recursive: true });
|
|
7544
7745
|
if (import_fs20.default.existsSync(node9Dir)) {
|
|
7545
7746
|
console.error(
|
|
7546
|
-
|
|
7747
|
+
import_chalk15.default.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
|
|
7547
7748
|
);
|
|
7548
7749
|
} else {
|
|
7549
|
-
console.log(
|
|
7750
|
+
console.log(import_chalk15.default.green("\n \u2705 Deleted ~/.node9/ (config, audit log, credentials)"));
|
|
7550
7751
|
}
|
|
7551
7752
|
} else {
|
|
7552
|
-
console.log(
|
|
7753
|
+
console.log(import_chalk15.default.yellow("\n Skipped \u2014 ~/.node9/ was not deleted."));
|
|
7553
7754
|
}
|
|
7554
7755
|
} else {
|
|
7555
|
-
console.log(
|
|
7756
|
+
console.log(import_chalk15.default.blue("\n \u2139\uFE0F ~/.node9/ not found \u2014 nothing to delete"));
|
|
7556
7757
|
}
|
|
7557
7758
|
} else {
|
|
7558
7759
|
console.log(
|
|
7559
|
-
|
|
7760
|
+
import_chalk15.default.gray("\n ~/.node9/ kept \u2014 run with --purge to delete config and audit log")
|
|
7560
7761
|
);
|
|
7561
7762
|
}
|
|
7562
7763
|
if (teardownFailed) {
|
|
7563
|
-
console.error(
|
|
7764
|
+
console.error(import_chalk15.default.red("\n \u26A0\uFE0F Some hooks could not be removed \u2014 see errors above."));
|
|
7564
7765
|
process.exit(1);
|
|
7565
7766
|
}
|
|
7566
|
-
console.log(
|
|
7567
|
-
console.log(
|
|
7767
|
+
console.log(import_chalk15.default.green.bold("\n\u{1F6E1}\uFE0F Node9 removed. Run: npm uninstall -g @node9/proxy"));
|
|
7768
|
+
console.log(import_chalk15.default.gray(" Restart any open AI agent sessions for changes to take effect.\n"));
|
|
7568
7769
|
});
|
|
7569
7770
|
registerDoctorCommand(program, version);
|
|
7570
7771
|
program.command("explain").description(
|
|
@@ -7577,7 +7778,7 @@ program.command("explain").description(
|
|
|
7577
7778
|
try {
|
|
7578
7779
|
args = JSON.parse(trimmed);
|
|
7579
7780
|
} catch {
|
|
7580
|
-
console.error(
|
|
7781
|
+
console.error(import_chalk15.default.red(`
|
|
7581
7782
|
\u274C Invalid JSON: ${trimmed}
|
|
7582
7783
|
`));
|
|
7583
7784
|
process.exit(1);
|
|
@@ -7588,54 +7789,54 @@ program.command("explain").description(
|
|
|
7588
7789
|
}
|
|
7589
7790
|
const result = await explainPolicy(tool, args);
|
|
7590
7791
|
console.log("");
|
|
7591
|
-
console.log(
|
|
7792
|
+
console.log(import_chalk15.default.cyan.bold("\u{1F6E1}\uFE0F Node9 Explain"));
|
|
7592
7793
|
console.log("");
|
|
7593
|
-
console.log(` ${
|
|
7794
|
+
console.log(` ${import_chalk15.default.bold("Tool:")} ${import_chalk15.default.white(result.tool)}`);
|
|
7594
7795
|
if (argsRaw) {
|
|
7595
7796
|
const preview = argsRaw.length > 80 ? argsRaw.slice(0, 77) + "\u2026" : argsRaw;
|
|
7596
|
-
console.log(` ${
|
|
7797
|
+
console.log(` ${import_chalk15.default.bold("Input:")} ${import_chalk15.default.gray(preview)}`);
|
|
7597
7798
|
}
|
|
7598
7799
|
console.log("");
|
|
7599
|
-
console.log(
|
|
7800
|
+
console.log(import_chalk15.default.bold("Config Sources (Waterfall):"));
|
|
7600
7801
|
for (const tier of result.waterfall) {
|
|
7601
|
-
const num =
|
|
7802
|
+
const num = import_chalk15.default.gray(` ${tier.tier}.`);
|
|
7602
7803
|
const label = tier.label.padEnd(16);
|
|
7603
7804
|
let statusStr;
|
|
7604
7805
|
if (tier.tier === 1) {
|
|
7605
|
-
statusStr =
|
|
7806
|
+
statusStr = import_chalk15.default.gray(tier.note ?? "");
|
|
7606
7807
|
} else if (tier.status === "active") {
|
|
7607
|
-
const loc = tier.path ?
|
|
7608
|
-
const note = tier.note ?
|
|
7609
|
-
statusStr =
|
|
7808
|
+
const loc = tier.path ? import_chalk15.default.gray(tier.path) : "";
|
|
7809
|
+
const note = tier.note ? import_chalk15.default.gray(`(${tier.note})`) : "";
|
|
7810
|
+
statusStr = import_chalk15.default.green("\u2713 active") + (loc ? " " + loc : "") + (note ? " " + note : "");
|
|
7610
7811
|
} else {
|
|
7611
|
-
statusStr =
|
|
7812
|
+
statusStr = import_chalk15.default.gray("\u25CB " + (tier.note ?? "not found"));
|
|
7612
7813
|
}
|
|
7613
|
-
console.log(`${num} ${
|
|
7814
|
+
console.log(`${num} ${import_chalk15.default.white(label)} ${statusStr}`);
|
|
7614
7815
|
}
|
|
7615
7816
|
console.log("");
|
|
7616
|
-
console.log(
|
|
7817
|
+
console.log(import_chalk15.default.bold("Policy Evaluation:"));
|
|
7617
7818
|
for (const step of result.steps) {
|
|
7618
7819
|
const isFinal = step.isFinal;
|
|
7619
7820
|
let icon;
|
|
7620
|
-
if (step.outcome === "allow") icon =
|
|
7621
|
-
else if (step.outcome === "review") icon =
|
|
7622
|
-
else if (step.outcome === "skip") icon =
|
|
7623
|
-
else icon =
|
|
7821
|
+
if (step.outcome === "allow") icon = import_chalk15.default.green(" \u2705");
|
|
7822
|
+
else if (step.outcome === "review") icon = import_chalk15.default.red(" \u{1F534}");
|
|
7823
|
+
else if (step.outcome === "skip") icon = import_chalk15.default.gray(" \u2500 ");
|
|
7824
|
+
else icon = import_chalk15.default.gray(" \u25CB ");
|
|
7624
7825
|
const name = step.name.padEnd(18);
|
|
7625
|
-
const nameStr = isFinal ?
|
|
7626
|
-
const detail = isFinal ?
|
|
7627
|
-
const arrow = isFinal ?
|
|
7826
|
+
const nameStr = isFinal ? import_chalk15.default.white.bold(name) : import_chalk15.default.white(name);
|
|
7827
|
+
const detail = isFinal ? import_chalk15.default.white(step.detail) : import_chalk15.default.gray(step.detail);
|
|
7828
|
+
const arrow = isFinal ? import_chalk15.default.yellow(" \u2190 STOP") : "";
|
|
7628
7829
|
console.log(`${icon} ${nameStr} ${detail}${arrow}`);
|
|
7629
7830
|
}
|
|
7630
7831
|
console.log("");
|
|
7631
7832
|
if (result.decision === "allow") {
|
|
7632
|
-
console.log(
|
|
7833
|
+
console.log(import_chalk15.default.green.bold(" Decision: \u2705 ALLOW") + import_chalk15.default.gray(" \u2014 no approval needed"));
|
|
7633
7834
|
} else {
|
|
7634
7835
|
console.log(
|
|
7635
|
-
|
|
7836
|
+
import_chalk15.default.red.bold(" Decision: \u{1F534} REVIEW") + import_chalk15.default.gray(" \u2014 human approval required")
|
|
7636
7837
|
);
|
|
7637
7838
|
if (result.blockedByLabel) {
|
|
7638
|
-
console.log(
|
|
7839
|
+
console.log(import_chalk15.default.gray(` Reason: ${result.blockedByLabel}`));
|
|
7639
7840
|
}
|
|
7640
7841
|
}
|
|
7641
7842
|
console.log("");
|
|
@@ -7643,8 +7844,8 @@ program.command("explain").description(
|
|
|
7643
7844
|
program.command("init").description("Create ~/.node9/config.json with default policy (safe to run multiple times)").option("--force", "Overwrite existing config").option("-m, --mode <mode>", "Set initial security mode (standard, strict, audit)", "standard").action((options) => {
|
|
7644
7845
|
const configPath = import_path21.default.join(import_os18.default.homedir(), ".node9", "config.json");
|
|
7645
7846
|
if (import_fs20.default.existsSync(configPath) && !options.force) {
|
|
7646
|
-
console.log(
|
|
7647
|
-
console.log(
|
|
7847
|
+
console.log(import_chalk15.default.yellow(`\u2139\uFE0F Global config already exists: ${configPath}`));
|
|
7848
|
+
console.log(import_chalk15.default.gray(` Run with --force to overwrite.`));
|
|
7648
7849
|
return;
|
|
7649
7850
|
}
|
|
7650
7851
|
const requestedMode = options.mode.toLowerCase();
|
|
@@ -7659,10 +7860,10 @@ program.command("init").description("Create ~/.node9/config.json with default po
|
|
|
7659
7860
|
const dir = import_path21.default.dirname(configPath);
|
|
7660
7861
|
if (!import_fs20.default.existsSync(dir)) import_fs20.default.mkdirSync(dir, { recursive: true });
|
|
7661
7862
|
import_fs20.default.writeFileSync(configPath, JSON.stringify(configToSave, null, 2));
|
|
7662
|
-
console.log(
|
|
7663
|
-
console.log(
|
|
7863
|
+
console.log(import_chalk15.default.green(`\u2705 Global config created: ${configPath}`));
|
|
7864
|
+
console.log(import_chalk15.default.cyan(` Mode set to: ${safeMode}`));
|
|
7664
7865
|
console.log(
|
|
7665
|
-
|
|
7866
|
+
import_chalk15.default.gray(` Undo Engine is ENABLED by default. Use 'node9 undo' to revert AI changes.`)
|
|
7666
7867
|
);
|
|
7667
7868
|
});
|
|
7668
7869
|
registerAuditCommand(program);
|
|
@@ -7673,18 +7874,19 @@ program.command("tail").description("Stream live agent activity to the terminal"
|
|
|
7673
7874
|
try {
|
|
7674
7875
|
await startTail2(options);
|
|
7675
7876
|
} catch (err) {
|
|
7676
|
-
console.error(
|
|
7877
|
+
console.error(import_chalk15.default.red(`\u274C ${err instanceof Error ? err.message : String(err)}`));
|
|
7677
7878
|
process.exit(1);
|
|
7678
7879
|
}
|
|
7679
7880
|
});
|
|
7680
7881
|
registerWatchCommand(program);
|
|
7882
|
+
registerMcpGatewayCommand(program);
|
|
7681
7883
|
registerCheckCommand(program);
|
|
7682
7884
|
registerLogCommand(program);
|
|
7683
7885
|
program.command("pause").description("Temporarily disable Node9 protection for a set duration").option("-d, --duration <duration>", "How long to pause (e.g. 15m, 1h, 30s)", "15m").action((options) => {
|
|
7684
7886
|
const ms = parseDuration(options.duration);
|
|
7685
7887
|
if (ms === null) {
|
|
7686
7888
|
console.error(
|
|
7687
|
-
|
|
7889
|
+
import_chalk15.default.red(`
|
|
7688
7890
|
\u274C Invalid duration: "${options.duration}". Use format like 15m, 1h, 30s.
|
|
7689
7891
|
`)
|
|
7690
7892
|
);
|
|
@@ -7692,20 +7894,20 @@ program.command("pause").description("Temporarily disable Node9 protection for a
|
|
|
7692
7894
|
}
|
|
7693
7895
|
pauseNode9(ms, options.duration);
|
|
7694
7896
|
const expiresAt = new Date(Date.now() + ms).toLocaleTimeString();
|
|
7695
|
-
console.log(
|
|
7897
|
+
console.log(import_chalk15.default.yellow(`
|
|
7696
7898
|
\u23F8 Node9 paused until ${expiresAt}`));
|
|
7697
|
-
console.log(
|
|
7698
|
-
console.log(
|
|
7899
|
+
console.log(import_chalk15.default.gray(` All tool calls will be allowed without review.`));
|
|
7900
|
+
console.log(import_chalk15.default.gray(` Run "node9 resume" to re-enable early.
|
|
7699
7901
|
`));
|
|
7700
7902
|
});
|
|
7701
7903
|
program.command("resume").description("Re-enable Node9 protection immediately").action(() => {
|
|
7702
7904
|
const { paused } = checkPause();
|
|
7703
7905
|
if (!paused) {
|
|
7704
|
-
console.log(
|
|
7906
|
+
console.log(import_chalk15.default.gray("\nNode9 is already active \u2014 nothing to resume.\n"));
|
|
7705
7907
|
return;
|
|
7706
7908
|
}
|
|
7707
7909
|
resumeNode9();
|
|
7708
|
-
console.log(
|
|
7910
|
+
console.log(import_chalk15.default.green("\n\u25B6 Node9 resumed \u2014 protection is active.\n"));
|
|
7709
7911
|
});
|
|
7710
7912
|
var HOOK_BASED_AGENTS = {
|
|
7711
7913
|
claude: "claude",
|
|
@@ -7718,15 +7920,15 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
7718
7920
|
if (HOOK_BASED_AGENTS[firstArg2] !== void 0) {
|
|
7719
7921
|
const target = HOOK_BASED_AGENTS[firstArg2];
|
|
7720
7922
|
console.error(
|
|
7721
|
-
|
|
7923
|
+
import_chalk15.default.yellow(`
|
|
7722
7924
|
\u26A0\uFE0F Node9 proxy mode does not support "${target}" directly.`)
|
|
7723
7925
|
);
|
|
7724
|
-
console.error(
|
|
7926
|
+
console.error(import_chalk15.default.white(`
|
|
7725
7927
|
"${target}" uses its own hook system. Use:`));
|
|
7726
7928
|
console.error(
|
|
7727
|
-
|
|
7929
|
+
import_chalk15.default.green(` node9 addto ${target} `) + import_chalk15.default.gray("# one-time setup")
|
|
7728
7930
|
);
|
|
7729
|
-
console.error(
|
|
7931
|
+
console.error(import_chalk15.default.green(` ${target} `) + import_chalk15.default.gray("# run normally"));
|
|
7730
7932
|
process.exit(1);
|
|
7731
7933
|
}
|
|
7732
7934
|
const runArgs = firstArg2 === "shell" ? commandArgs.slice(1) : commandArgs;
|
|
@@ -7743,7 +7945,7 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
7743
7945
|
}
|
|
7744
7946
|
);
|
|
7745
7947
|
if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && getConfig().settings.autoStartDaemon) {
|
|
7746
|
-
console.error(
|
|
7948
|
+
console.error(import_chalk15.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically..."));
|
|
7747
7949
|
const daemonReady = await autoStartDaemonAndWait();
|
|
7748
7950
|
if (daemonReady) result = await authorizeHeadless("shell", { command: fullCommand });
|
|
7749
7951
|
}
|
|
@@ -7756,12 +7958,12 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
7756
7958
|
}
|
|
7757
7959
|
if (!result.approved) {
|
|
7758
7960
|
console.error(
|
|
7759
|
-
|
|
7961
|
+
import_chalk15.default.red(`
|
|
7760
7962
|
\u274C Node9 Blocked: ${result.reason || "Dangerous command detected."}`)
|
|
7761
7963
|
);
|
|
7762
7964
|
process.exit(1);
|
|
7763
7965
|
}
|
|
7764
|
-
console.error(
|
|
7966
|
+
console.error(import_chalk15.default.green("\n\u2705 Approved \u2014 running command...\n"));
|
|
7765
7967
|
await runProxy(fullCommand);
|
|
7766
7968
|
} else {
|
|
7767
7969
|
program.help();
|