@node9/proxy 1.27.1 → 1.29.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/README.md +3 -3
- package/dist/cli.js +1347 -124
- package/dist/cli.mjs +1347 -124
- package/dist/dashboard.mjs +19 -1
- package/dist/index.js +10 -1
- package/dist/index.mjs +10 -1
- package/package.json +3 -1
package/dist/cli.js
CHANGED
|
@@ -119,9 +119,11 @@ function appendLocalAudit(toolName, args, decision, checkedBy, meta, auditHashAr
|
|
|
119
119
|
const argsField = auditHashArgsEnabled ? { argsHash: hashArgs(args) } : { args: args ? JSON.parse(redactSecrets(JSON.stringify(args))) : {} };
|
|
120
120
|
const testRun = isTestCall(toolName, args) || process.env.NODE9_TESTING === "1" ? { testRun: true } : {};
|
|
121
121
|
const ruleNameField = meta?.ruleName ? { ruleName: meta.ruleName } : {};
|
|
122
|
+
const agentToolNameField = meta?.agentToolName ? { agentToolName: meta.agentToolName } : {};
|
|
122
123
|
appendToLog(LOCAL_AUDIT_LOG, {
|
|
123
124
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
124
125
|
tool: toolName,
|
|
126
|
+
...agentToolNameField,
|
|
125
127
|
...argsField,
|
|
126
128
|
decision,
|
|
127
129
|
checkedBy,
|
|
@@ -3945,7 +3947,14 @@ var init_config = __esm({
|
|
|
3945
3947
|
"edit_file",
|
|
3946
3948
|
"create_file",
|
|
3947
3949
|
"edit",
|
|
3948
|
-
"replace"
|
|
3950
|
+
"replace",
|
|
3951
|
+
// Claude / canonicalised Hermes — shouldSnapshot lowercases the
|
|
3952
|
+
// incoming name before set-membership, so we list the lowercase
|
|
3953
|
+
// forms of `Bash`/`Write`/`Edit`/`MultiEdit`. Without these,
|
|
3954
|
+
// post-canonicalisation Hermes `patch` / `write_file` (which now
|
|
3955
|
+
// arrive as `Edit` / `Write`) silently skipped snapshotting.
|
|
3956
|
+
"write",
|
|
3957
|
+
"multiedit"
|
|
3949
3958
|
],
|
|
3950
3959
|
onlyPaths: [],
|
|
3951
3960
|
ignorePaths: ["**/node_modules/**", "dist/**", "build/**", ".next/**", "**/*.log"]
|
|
@@ -7181,6 +7190,12 @@ async function setupClaude() {
|
|
|
7181
7190
|
}
|
|
7182
7191
|
}
|
|
7183
7192
|
async function setupGemini() {
|
|
7193
|
+
console.log(
|
|
7194
|
+
import_chalk.default.yellow(
|
|
7195
|
+
" \u26A0\uFE0F Gemini CLI stops serving AI Pro/Ultra and free tiers on 2026-06-18\n (replaced by Antigravity). If you use agy, run: node9 agents add antigravity"
|
|
7196
|
+
)
|
|
7197
|
+
);
|
|
7198
|
+
console.log("");
|
|
7184
7199
|
seedMcpPinsIfMissing();
|
|
7185
7200
|
const homeDir2 = import_os12.default.homedir();
|
|
7186
7201
|
const settingsPath = import_path15.default.join(homeDir2, ".gemini", "settings.json");
|
|
@@ -7278,6 +7293,355 @@ async function setupGemini() {
|
|
|
7278
7293
|
printDaemonTip();
|
|
7279
7294
|
}
|
|
7280
7295
|
}
|
|
7296
|
+
async function setupAntigravity() {
|
|
7297
|
+
seedMcpPinsIfMissing();
|
|
7298
|
+
const homeDir2 = import_os12.default.homedir();
|
|
7299
|
+
const hooksPath = import_path15.default.join(homeDir2, ".gemini", "config", "hooks.json");
|
|
7300
|
+
const mcpPath = import_path15.default.join(homeDir2, ".gemini", "config", "mcp_config.json");
|
|
7301
|
+
const hooksFile = readJson(hooksPath) ?? {};
|
|
7302
|
+
const mcpConfig = readJson(mcpPath) ?? {};
|
|
7303
|
+
const servers = mcpConfig.mcpServers ?? {};
|
|
7304
|
+
let hooksChanged = false;
|
|
7305
|
+
let anythingChanged = false;
|
|
7306
|
+
if (!hooksFile.hooks) hooksFile.hooks = {};
|
|
7307
|
+
const hasPreHook = hooksFile.hooks.PreToolUse?.some(
|
|
7308
|
+
(m) => m.hooks.some((h) => isNode9Hook(h.command))
|
|
7309
|
+
);
|
|
7310
|
+
if (!hasPreHook) {
|
|
7311
|
+
if (!Array.isArray(hooksFile.hooks.PreToolUse)) hooksFile.hooks.PreToolUse = [];
|
|
7312
|
+
hooksFile.hooks.PreToolUse.push({
|
|
7313
|
+
matcher: ".*",
|
|
7314
|
+
hooks: [
|
|
7315
|
+
{
|
|
7316
|
+
name: "node9-check",
|
|
7317
|
+
type: "command",
|
|
7318
|
+
command: fullPathCommand("check --agent antigravity"),
|
|
7319
|
+
timeout: 600
|
|
7320
|
+
}
|
|
7321
|
+
]
|
|
7322
|
+
});
|
|
7323
|
+
console.log(import_chalk.default.green(" \u2705 PreToolUse hook added \u2192 node9 check"));
|
|
7324
|
+
hooksChanged = true;
|
|
7325
|
+
anythingChanged = true;
|
|
7326
|
+
} else if (hooksFile.hooks.PreToolUse) {
|
|
7327
|
+
for (const matcher of hooksFile.hooks.PreToolUse) {
|
|
7328
|
+
for (const h of matcher.hooks) {
|
|
7329
|
+
const cmd = h.command ?? "";
|
|
7330
|
+
if (isNode9Hook(cmd) && needsRewrite(cmd)) {
|
|
7331
|
+
h.command = fullPathCommand("check --agent antigravity");
|
|
7332
|
+
console.log(import_chalk.default.yellow(" \u{1F527} PreToolUse hook repaired (stale path \u2192 current binary)"));
|
|
7333
|
+
hooksChanged = true;
|
|
7334
|
+
anythingChanged = true;
|
|
7335
|
+
}
|
|
7336
|
+
}
|
|
7337
|
+
}
|
|
7338
|
+
}
|
|
7339
|
+
const hasPostHook = hooksFile.hooks.PostToolUse?.some(
|
|
7340
|
+
(m) => m.hooks.some((h) => isNode9Hook(h.command))
|
|
7341
|
+
);
|
|
7342
|
+
if (!hasPostHook) {
|
|
7343
|
+
if (!Array.isArray(hooksFile.hooks.PostToolUse)) hooksFile.hooks.PostToolUse = [];
|
|
7344
|
+
hooksFile.hooks.PostToolUse.push({
|
|
7345
|
+
matcher: ".*",
|
|
7346
|
+
hooks: [
|
|
7347
|
+
{
|
|
7348
|
+
name: "node9-log",
|
|
7349
|
+
type: "command",
|
|
7350
|
+
command: fullPathCommand("log --agent antigravity"),
|
|
7351
|
+
timeout: 600
|
|
7352
|
+
}
|
|
7353
|
+
]
|
|
7354
|
+
});
|
|
7355
|
+
console.log(import_chalk.default.green(" \u2705 PostToolUse hook added \u2192 node9 log"));
|
|
7356
|
+
hooksChanged = true;
|
|
7357
|
+
anythingChanged = true;
|
|
7358
|
+
} else if (hooksFile.hooks.PostToolUse) {
|
|
7359
|
+
for (const matcher of hooksFile.hooks.PostToolUse) {
|
|
7360
|
+
for (const h of matcher.hooks) {
|
|
7361
|
+
const cmd = h.command ?? "";
|
|
7362
|
+
if (isNode9Hook(cmd) && needsRewrite(cmd)) {
|
|
7363
|
+
h.command = fullPathCommand("log --agent antigravity");
|
|
7364
|
+
console.log(import_chalk.default.yellow(" \u{1F527} PostToolUse hook repaired (stale path \u2192 current binary)"));
|
|
7365
|
+
hooksChanged = true;
|
|
7366
|
+
anythingChanged = true;
|
|
7367
|
+
}
|
|
7368
|
+
}
|
|
7369
|
+
}
|
|
7370
|
+
}
|
|
7371
|
+
if (hooksChanged) {
|
|
7372
|
+
writeJson(hooksPath, hooksFile);
|
|
7373
|
+
console.log("");
|
|
7374
|
+
}
|
|
7375
|
+
if (!hasNode9McpServer(servers)) {
|
|
7376
|
+
servers["node9"] = NODE9_MCP_SERVER_ENTRY;
|
|
7377
|
+
mcpConfig.mcpServers = servers;
|
|
7378
|
+
writeJson(mcpPath, mcpConfig);
|
|
7379
|
+
console.log(import_chalk.default.green(" \u2705 node9 MCP server added \u2192 node9 mcp-server"));
|
|
7380
|
+
anythingChanged = true;
|
|
7381
|
+
}
|
|
7382
|
+
const legacySettings = readJson(import_path15.default.join(homeDir2, ".gemini", "settings.json"));
|
|
7383
|
+
const legacyHasNode9 = ["BeforeTool", "AfterTool"].some(
|
|
7384
|
+
(ev) => legacySettings?.hooks?.[ev]?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)))
|
|
7385
|
+
);
|
|
7386
|
+
if (legacyHasNode9) {
|
|
7387
|
+
console.log(
|
|
7388
|
+
import_chalk.default.yellow(
|
|
7389
|
+
" \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)."
|
|
7390
|
+
)
|
|
7391
|
+
);
|
|
7392
|
+
const clean = await (0, import_prompts.confirm)({
|
|
7393
|
+
message: "Remove the legacy Gemini CLI hooks?",
|
|
7394
|
+
default: false
|
|
7395
|
+
});
|
|
7396
|
+
if (clean) {
|
|
7397
|
+
teardownGemini();
|
|
7398
|
+
anythingChanged = true;
|
|
7399
|
+
}
|
|
7400
|
+
console.log("");
|
|
7401
|
+
}
|
|
7402
|
+
const serversToWrap = [];
|
|
7403
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
7404
|
+
if (!server.command || server.command === "node9") continue;
|
|
7405
|
+
serversToWrap.push({ name, upstream: [server.command, ...server.args ?? []].join(" ") });
|
|
7406
|
+
}
|
|
7407
|
+
if (serversToWrap.length > 0) {
|
|
7408
|
+
console.log(import_chalk.default.bold("The following existing entries will be modified:\n"));
|
|
7409
|
+
console.log(import_chalk.default.white(` ${mcpPath}`));
|
|
7410
|
+
for (const { name, upstream } of serversToWrap) {
|
|
7411
|
+
console.log(import_chalk.default.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
|
|
7412
|
+
}
|
|
7413
|
+
console.log("");
|
|
7414
|
+
const proceed = await (0, import_prompts.confirm)({ message: "Wrap these MCP servers?", default: true });
|
|
7415
|
+
if (proceed) {
|
|
7416
|
+
for (const { name, upstream } of serversToWrap) {
|
|
7417
|
+
servers[name] = {
|
|
7418
|
+
...servers[name],
|
|
7419
|
+
command: "node9",
|
|
7420
|
+
args: ["mcp", "--upstream", upstream]
|
|
7421
|
+
};
|
|
7422
|
+
}
|
|
7423
|
+
mcpConfig.mcpServers = servers;
|
|
7424
|
+
writeJson(mcpPath, mcpConfig);
|
|
7425
|
+
console.log(import_chalk.default.green(`
|
|
7426
|
+
\u2705 ${serversToWrap.length} MCP server(s) wrapped`));
|
|
7427
|
+
anythingChanged = true;
|
|
7428
|
+
} else {
|
|
7429
|
+
console.log(import_chalk.default.yellow(" Skipped MCP server wrapping."));
|
|
7430
|
+
}
|
|
7431
|
+
console.log("");
|
|
7432
|
+
}
|
|
7433
|
+
if (!anythingChanged && serversToWrap.length === 0) {
|
|
7434
|
+
console.log(import_chalk.default.blue("\u2139\uFE0F Node9 is already fully configured for Antigravity."));
|
|
7435
|
+
printDaemonTip();
|
|
7436
|
+
return;
|
|
7437
|
+
}
|
|
7438
|
+
if (anythingChanged) {
|
|
7439
|
+
console.log(import_chalk.default.green.bold("\u{1F6E1}\uFE0F Node9 is now protecting Antigravity!"));
|
|
7440
|
+
console.log(
|
|
7441
|
+
import_chalk.default.gray(
|
|
7442
|
+
" Covers both the agy CLI and the Antigravity IDE (shared ~/.gemini/config).\n Restart agy / the IDE for changes to take effect."
|
|
7443
|
+
)
|
|
7444
|
+
);
|
|
7445
|
+
printDaemonTip();
|
|
7446
|
+
}
|
|
7447
|
+
}
|
|
7448
|
+
function teardownAntigravity() {
|
|
7449
|
+
const homeDir2 = import_os12.default.homedir();
|
|
7450
|
+
const hooksPath = import_path15.default.join(homeDir2, ".gemini", "config", "hooks.json");
|
|
7451
|
+
const mcpPath = import_path15.default.join(homeDir2, ".gemini", "config", "mcp_config.json");
|
|
7452
|
+
let changed = false;
|
|
7453
|
+
const hooksFile = readJson(hooksPath);
|
|
7454
|
+
if (hooksFile?.hooks) {
|
|
7455
|
+
for (const event of ["PreToolUse", "PostToolUse"]) {
|
|
7456
|
+
const before = hooksFile.hooks[event]?.length ?? 0;
|
|
7457
|
+
hooksFile.hooks[event] = hooksFile.hooks[event]?.filter(
|
|
7458
|
+
(m) => !m.hooks.some((h) => isNode9Hook(h.command))
|
|
7459
|
+
);
|
|
7460
|
+
if ((hooksFile.hooks[event]?.length ?? 0) < before) changed = true;
|
|
7461
|
+
if (hooksFile.hooks[event]?.length === 0) delete hooksFile.hooks[event];
|
|
7462
|
+
}
|
|
7463
|
+
if (changed) {
|
|
7464
|
+
writeJson(hooksPath, hooksFile);
|
|
7465
|
+
console.log(
|
|
7466
|
+
import_chalk.default.green(" \u2705 Removed PreToolUse / PostToolUse hooks from ~/.gemini/config/hooks.json")
|
|
7467
|
+
);
|
|
7468
|
+
} else {
|
|
7469
|
+
console.log(import_chalk.default.blue(" \u2139\uFE0F No Node9 hooks found in ~/.gemini/config/hooks.json"));
|
|
7470
|
+
}
|
|
7471
|
+
} else {
|
|
7472
|
+
console.log(import_chalk.default.blue(" \u2139\uFE0F ~/.gemini/config/hooks.json not found \u2014 nothing to remove"));
|
|
7473
|
+
}
|
|
7474
|
+
const mcpConfig = readJson(mcpPath);
|
|
7475
|
+
if (mcpConfig?.mcpServers) {
|
|
7476
|
+
let mcpChanged = false;
|
|
7477
|
+
if (removeNode9McpServer(mcpConfig.mcpServers)) {
|
|
7478
|
+
mcpChanged = true;
|
|
7479
|
+
console.log(
|
|
7480
|
+
import_chalk.default.green(" \u2705 Removed node9 MCP server entry from ~/.gemini/config/mcp_config.json")
|
|
7481
|
+
);
|
|
7482
|
+
}
|
|
7483
|
+
for (const [name, server] of Object.entries(mcpConfig.mcpServers)) {
|
|
7484
|
+
const args = server.args;
|
|
7485
|
+
if (server.command === "node9" && Array.isArray(args) && args[0] === "mcp" && args[1] === "--upstream" && typeof args[2] === "string") {
|
|
7486
|
+
const [originalCmd, ...originalArgs] = args[2].split(" ");
|
|
7487
|
+
mcpConfig.mcpServers[name] = {
|
|
7488
|
+
...server,
|
|
7489
|
+
command: originalCmd,
|
|
7490
|
+
args: originalArgs.length ? originalArgs : void 0
|
|
7491
|
+
};
|
|
7492
|
+
mcpChanged = true;
|
|
7493
|
+
}
|
|
7494
|
+
}
|
|
7495
|
+
if (mcpChanged) {
|
|
7496
|
+
writeJson(mcpPath, mcpConfig);
|
|
7497
|
+
console.log(import_chalk.default.green(" \u2705 Unwrapped MCP servers in ~/.gemini/config/mcp_config.json"));
|
|
7498
|
+
}
|
|
7499
|
+
}
|
|
7500
|
+
}
|
|
7501
|
+
async function setupCopilot() {
|
|
7502
|
+
seedMcpPinsIfMissing();
|
|
7503
|
+
const homeDir2 = import_os12.default.homedir();
|
|
7504
|
+
const hooksPath = import_path15.default.join(homeDir2, ".copilot", "hooks", "node9.json");
|
|
7505
|
+
const mcpPath = import_path15.default.join(homeDir2, ".copilot", "mcp-config.json");
|
|
7506
|
+
const hooksFile = readJson(hooksPath) ?? { version: 1 };
|
|
7507
|
+
if (!hooksFile.version) hooksFile.version = 1;
|
|
7508
|
+
if (!hooksFile.hooks) hooksFile.hooks = {};
|
|
7509
|
+
const mcpConfig = readJson(mcpPath) ?? {};
|
|
7510
|
+
const servers = mcpConfig.mcpServers ?? {};
|
|
7511
|
+
let hooksChanged = false;
|
|
7512
|
+
let anythingChanged = false;
|
|
7513
|
+
const addHook = (event, subcommand) => {
|
|
7514
|
+
const arr = Array.isArray(hooksFile.hooks[event]) ? hooksFile.hooks[event] : [];
|
|
7515
|
+
const present = arr.some((h) => isNode9Hook(h.command));
|
|
7516
|
+
if (!present) {
|
|
7517
|
+
arr.push({ type: "command", command: fullPathCommand(subcommand), timeoutSec: 600 });
|
|
7518
|
+
hooksFile.hooks[event] = arr;
|
|
7519
|
+
console.log(import_chalk.default.green(` \u2705 ${event} hook added \u2192 node9 ${subcommand.split(" ")[0]}`));
|
|
7520
|
+
hooksChanged = true;
|
|
7521
|
+
anythingChanged = true;
|
|
7522
|
+
} else {
|
|
7523
|
+
for (const h of arr) {
|
|
7524
|
+
if (h.command && isNode9Hook(h.command) && needsRewrite(h.command)) {
|
|
7525
|
+
h.command = fullPathCommand(subcommand);
|
|
7526
|
+
console.log(import_chalk.default.yellow(` \u{1F527} ${event} hook repaired (stale path \u2192 current binary)`));
|
|
7527
|
+
hooksChanged = true;
|
|
7528
|
+
anythingChanged = true;
|
|
7529
|
+
}
|
|
7530
|
+
}
|
|
7531
|
+
}
|
|
7532
|
+
};
|
|
7533
|
+
addHook("PreToolUse", "check --agent copilot");
|
|
7534
|
+
addHook("PostToolUse", "log --agent copilot");
|
|
7535
|
+
addHook("UserPromptSubmit", "check --agent copilot");
|
|
7536
|
+
if (hooksChanged) {
|
|
7537
|
+
writeJson(hooksPath, hooksFile);
|
|
7538
|
+
console.log("");
|
|
7539
|
+
}
|
|
7540
|
+
if (!hasNode9McpServer(servers)) {
|
|
7541
|
+
servers["node9"] = NODE9_MCP_SERVER_ENTRY;
|
|
7542
|
+
mcpConfig.mcpServers = servers;
|
|
7543
|
+
writeJson(mcpPath, mcpConfig);
|
|
7544
|
+
console.log(import_chalk.default.green(" \u2705 node9 MCP server added \u2192 node9 mcp-server"));
|
|
7545
|
+
anythingChanged = true;
|
|
7546
|
+
}
|
|
7547
|
+
const serversToWrap = [];
|
|
7548
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
7549
|
+
if (!server.command || server.command === "node9") continue;
|
|
7550
|
+
serversToWrap.push({ name, upstream: [server.command, ...server.args ?? []].join(" ") });
|
|
7551
|
+
}
|
|
7552
|
+
if (serversToWrap.length > 0) {
|
|
7553
|
+
console.log(import_chalk.default.bold("The following existing entries will be modified:\n"));
|
|
7554
|
+
console.log(import_chalk.default.white(` ${mcpPath}`));
|
|
7555
|
+
for (const { name, upstream } of serversToWrap) {
|
|
7556
|
+
console.log(import_chalk.default.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
|
|
7557
|
+
}
|
|
7558
|
+
console.log("");
|
|
7559
|
+
const proceed = await (0, import_prompts.confirm)({ message: "Wrap these MCP servers?", default: true });
|
|
7560
|
+
if (proceed) {
|
|
7561
|
+
for (const { name, upstream } of serversToWrap) {
|
|
7562
|
+
servers[name] = {
|
|
7563
|
+
...servers[name],
|
|
7564
|
+
command: "node9",
|
|
7565
|
+
args: ["mcp", "--upstream", upstream]
|
|
7566
|
+
};
|
|
7567
|
+
}
|
|
7568
|
+
mcpConfig.mcpServers = servers;
|
|
7569
|
+
writeJson(mcpPath, mcpConfig);
|
|
7570
|
+
console.log(import_chalk.default.green(`
|
|
7571
|
+
\u2705 ${serversToWrap.length} MCP server(s) wrapped`));
|
|
7572
|
+
anythingChanged = true;
|
|
7573
|
+
} else {
|
|
7574
|
+
console.log(import_chalk.default.yellow(" Skipped MCP server wrapping."));
|
|
7575
|
+
}
|
|
7576
|
+
console.log("");
|
|
7577
|
+
}
|
|
7578
|
+
if (!anythingChanged) {
|
|
7579
|
+
console.log(import_chalk.default.blue("\u2139\uFE0F Node9 is already fully configured for GitHub Copilot CLI."));
|
|
7580
|
+
printDaemonTip();
|
|
7581
|
+
return;
|
|
7582
|
+
}
|
|
7583
|
+
console.log(import_chalk.default.green.bold("\u{1F6E1}\uFE0F Node9 is now protecting GitHub Copilot CLI!"));
|
|
7584
|
+
console.log(import_chalk.default.gray(" Restart Copilot CLI for changes to take effect."));
|
|
7585
|
+
printDaemonTip();
|
|
7586
|
+
}
|
|
7587
|
+
function teardownCopilot() {
|
|
7588
|
+
const homeDir2 = import_os12.default.homedir();
|
|
7589
|
+
const hooksPath = import_path15.default.join(homeDir2, ".copilot", "hooks", "node9.json");
|
|
7590
|
+
const mcpPath = import_path15.default.join(homeDir2, ".copilot", "mcp-config.json");
|
|
7591
|
+
const hooksFile = readJson(hooksPath);
|
|
7592
|
+
let changed = false;
|
|
7593
|
+
if (hooksFile?.hooks) {
|
|
7594
|
+
for (const event of ["PreToolUse", "PostToolUse", "UserPromptSubmit"]) {
|
|
7595
|
+
const before = hooksFile.hooks[event]?.length ?? 0;
|
|
7596
|
+
hooksFile.hooks[event] = hooksFile.hooks[event]?.filter((h) => !isNode9Hook(h.command));
|
|
7597
|
+
if ((hooksFile.hooks[event]?.length ?? 0) < before) changed = true;
|
|
7598
|
+
if (hooksFile.hooks[event]?.length === 0) delete hooksFile.hooks[event];
|
|
7599
|
+
}
|
|
7600
|
+
if (changed) {
|
|
7601
|
+
if (Object.keys(hooksFile.hooks).length === 0) {
|
|
7602
|
+
try {
|
|
7603
|
+
import_fs13.default.unlinkSync(hooksPath);
|
|
7604
|
+
console.log(import_chalk.default.green(" \u2705 Removed ~/.copilot/hooks/node9.json"));
|
|
7605
|
+
} catch {
|
|
7606
|
+
writeJson(hooksPath, hooksFile);
|
|
7607
|
+
}
|
|
7608
|
+
} else {
|
|
7609
|
+
writeJson(hooksPath, hooksFile);
|
|
7610
|
+
console.log(import_chalk.default.green(" \u2705 Removed Node9 hooks from ~/.copilot/hooks/node9.json"));
|
|
7611
|
+
}
|
|
7612
|
+
} else {
|
|
7613
|
+
console.log(import_chalk.default.blue(" \u2139\uFE0F No Node9 hooks found in ~/.copilot/hooks/node9.json"));
|
|
7614
|
+
}
|
|
7615
|
+
} else {
|
|
7616
|
+
console.log(import_chalk.default.blue(" \u2139\uFE0F ~/.copilot/hooks/node9.json not found \u2014 nothing to remove"));
|
|
7617
|
+
}
|
|
7618
|
+
const mcpConfig = readJson(mcpPath);
|
|
7619
|
+
if (mcpConfig?.mcpServers) {
|
|
7620
|
+
let mcpChanged = false;
|
|
7621
|
+
if (removeNode9McpServer(mcpConfig.mcpServers)) {
|
|
7622
|
+
mcpChanged = true;
|
|
7623
|
+
console.log(
|
|
7624
|
+
import_chalk.default.green(" \u2705 Removed node9 MCP server entry from ~/.copilot/mcp-config.json")
|
|
7625
|
+
);
|
|
7626
|
+
}
|
|
7627
|
+
for (const [name, server] of Object.entries(mcpConfig.mcpServers)) {
|
|
7628
|
+
const args = server.args;
|
|
7629
|
+
if (server.command === "node9" && Array.isArray(args) && args[0] === "mcp" && args[1] === "--upstream" && typeof args[2] === "string") {
|
|
7630
|
+
const [originalCmd, ...originalArgs] = args[2].split(" ");
|
|
7631
|
+
mcpConfig.mcpServers[name] = {
|
|
7632
|
+
...server,
|
|
7633
|
+
command: originalCmd,
|
|
7634
|
+
args: originalArgs.length ? originalArgs : void 0
|
|
7635
|
+
};
|
|
7636
|
+
mcpChanged = true;
|
|
7637
|
+
}
|
|
7638
|
+
}
|
|
7639
|
+
if (mcpChanged) {
|
|
7640
|
+
writeJson(mcpPath, mcpConfig);
|
|
7641
|
+
console.log(import_chalk.default.green(" \u2705 Unwrapped MCP servers in ~/.copilot/mcp-config.json"));
|
|
7642
|
+
}
|
|
7643
|
+
}
|
|
7644
|
+
}
|
|
7281
7645
|
function claudeDesktopConfigPath(homeDir2 = import_os12.default.homedir()) {
|
|
7282
7646
|
if (process.platform === "darwin") {
|
|
7283
7647
|
return import_path15.default.join(
|
|
@@ -7325,7 +7689,23 @@ function detectAgents(homeDir2 = import_os12.default.homedir()) {
|
|
|
7325
7689
|
const desktopPath = claudeDesktopConfigPath(homeDir2);
|
|
7326
7690
|
return {
|
|
7327
7691
|
claude: exists(import_path15.default.join(homeDir2, ".claude")) || exists(import_path15.default.join(homeDir2, ".claude.json")),
|
|
7328
|
-
|
|
7692
|
+
// Antigravity (agy) shares the ~/.gemini root, so a bare
|
|
7693
|
+
// `exists(~/.gemini)` would report the (EOL'd) Gemini CLI as
|
|
7694
|
+
// installed on every agy machine — and `node9 init` would then
|
|
7695
|
+
// write BeforeTool hooks into settings.json, a file agy ignores,
|
|
7696
|
+
// while reporting the machine protected (silent protection gap,
|
|
7697
|
+
// verified live on a machine with both installed). Gemini CLI
|
|
7698
|
+
// creates ~/.gemini/settings.json on first run; agy does not touch
|
|
7699
|
+
// it (it uses antigravity-cli/settings.json) — that file is the
|
|
7700
|
+
// legacy-CLI discriminator.
|
|
7701
|
+
gemini: exists(import_path15.default.join(homeDir2, ".gemini", "settings.json")) || binaryInPath("gemini"),
|
|
7702
|
+
// agy creates ~/.gemini/antigravity-cli/ on first launch; the IDE
|
|
7703
|
+
// creates antigravity-ide/. PATH fallback covers installed-but-
|
|
7704
|
+
// never-launched (same class as opencode #186).
|
|
7705
|
+
antigravity: exists(import_path15.default.join(homeDir2, ".gemini", "antigravity-cli")) || exists(import_path15.default.join(homeDir2, ".gemini", "antigravity-ide")) || binaryInPath("agy"),
|
|
7706
|
+
// GitHub Copilot CLI creates ~/.copilot on first launch; PATH
|
|
7707
|
+
// fallback covers installed-but-never-launched (same as opencode #186).
|
|
7708
|
+
copilot: exists(import_path15.default.join(homeDir2, ".copilot")) || binaryInPath("copilot"),
|
|
7329
7709
|
cursor: exists(import_path15.default.join(homeDir2, ".cursor")),
|
|
7330
7710
|
codex: exists(import_path15.default.join(homeDir2, ".codex")),
|
|
7331
7711
|
windsurf: exists(import_path15.default.join(homeDir2, ".codeium", "windsurf")),
|
|
@@ -7340,7 +7720,13 @@ function detectAgents(homeDir2 = import_os12.default.homedir()) {
|
|
|
7340
7720
|
// dir lazily on first launch — same class of bug as opencode's #186
|
|
7341
7721
|
// (design R6) — so fall back to PATH lookup for installed-but-never-
|
|
7342
7722
|
// launched pi.
|
|
7343
|
-
pi: exists(import_path15.default.join(homeDir2, ".pi", "agent")) || binaryInPath("pi")
|
|
7723
|
+
pi: exists(import_path15.default.join(homeDir2, ".pi", "agent")) || binaryInPath("pi"),
|
|
7724
|
+
// Hermes Agent (https://github.com/NousResearch/hermes-agent): home dir
|
|
7725
|
+
// is $HERMES_HOME (default ~/.hermes) per hermes_constants.py:30. config.yaml
|
|
7726
|
+
// appears after `hermes setup` has run; the directory alone exists from
|
|
7727
|
+
// install time. PATH fallback covers the rare case where the user blew
|
|
7728
|
+
// away ~/.hermes but kept the binary.
|
|
7729
|
+
hermes: exists(hermesHomeDir(homeDir2)) || binaryInPath("hermes")
|
|
7344
7730
|
};
|
|
7345
7731
|
}
|
|
7346
7732
|
async function setupCursor() {
|
|
@@ -8147,6 +8533,169 @@ function teardownPi() {
|
|
|
8147
8533
|
console.log(import_chalk.default.yellow(` \u26A0\uFE0F Could not remove ${extensionPath}: ${String(err2)}`));
|
|
8148
8534
|
}
|
|
8149
8535
|
}
|
|
8536
|
+
function hermesHomeDir(homeDir2 = import_os12.default.homedir()) {
|
|
8537
|
+
const env = process.env.HERMES_HOME?.trim();
|
|
8538
|
+
if (env && import_path15.default.isAbsolute(env)) return env;
|
|
8539
|
+
return import_path15.default.join(homeDir2, ".hermes");
|
|
8540
|
+
}
|
|
8541
|
+
function hermesConfigPath(homeDir2 = import_os12.default.homedir()) {
|
|
8542
|
+
return import_path15.default.join(hermesHomeDir(homeDir2), HERMES_CONFIG_FILENAME);
|
|
8543
|
+
}
|
|
8544
|
+
function hermesAllowlistPath(homeDir2 = import_os12.default.homedir()) {
|
|
8545
|
+
return import_path15.default.join(hermesHomeDir(homeDir2), HERMES_ALLOWLIST_FILENAME);
|
|
8546
|
+
}
|
|
8547
|
+
function setupHermes() {
|
|
8548
|
+
const homeDir2 = import_os12.default.homedir();
|
|
8549
|
+
const configPath = hermesConfigPath(homeDir2);
|
|
8550
|
+
const allowlistPath = hermesAllowlistPath(homeDir2);
|
|
8551
|
+
if (!import_fs13.default.existsSync(configPath)) {
|
|
8552
|
+
console.log(import_chalk.default.yellow(` \u26A0\uFE0F Hermes config not found at ${configPath}`));
|
|
8553
|
+
console.log(import_chalk.default.gray(" Run `hermes setup` first, then re-run node9 setup hermes."));
|
|
8554
|
+
return;
|
|
8555
|
+
}
|
|
8556
|
+
let anythingChanged = false;
|
|
8557
|
+
const raw = import_fs13.default.readFileSync(configPath, "utf-8");
|
|
8558
|
+
const doc = yaml.parseDocument(raw);
|
|
8559
|
+
if (doc.errors.length > 0) {
|
|
8560
|
+
console.log(import_chalk.default.yellow(` \u26A0\uFE0F Hermes config.yaml has YAML parse errors:`));
|
|
8561
|
+
for (const err2 of doc.errors.slice(0, 3)) {
|
|
8562
|
+
console.log(import_chalk.default.gray(` \u2022 ${err2.message}`));
|
|
8563
|
+
}
|
|
8564
|
+
console.log(
|
|
8565
|
+
import_chalk.default.gray(" Fix the file (or run `hermes config edit`), then re-run node9 setup hermes.")
|
|
8566
|
+
);
|
|
8567
|
+
return;
|
|
8568
|
+
}
|
|
8569
|
+
const current = doc.toJS() ?? {};
|
|
8570
|
+
for (const { event, subcmd } of HERMES_HOOK_PLAN) {
|
|
8571
|
+
const command = fullPathCommand(subcmd);
|
|
8572
|
+
const existing = current.hooks?.[event] ?? [];
|
|
8573
|
+
const node9Idx = existing.findIndex(
|
|
8574
|
+
(e) => typeof e?.command === "string" && isNode9Hook(e.command)
|
|
8575
|
+
);
|
|
8576
|
+
if (node9Idx === -1) {
|
|
8577
|
+
const newEntries = [...existing, { command, timeout: 10 }];
|
|
8578
|
+
doc.setIn(["hooks", event], newEntries);
|
|
8579
|
+
console.log(import_chalk.default.green(` \u2705 Hermes ${event} hook added \u2192 node9 ${subcmd}`));
|
|
8580
|
+
anythingChanged = true;
|
|
8581
|
+
} else if (existing[node9Idx].command !== command || isStaleHookCommand(existing[node9Idx].command ?? "")) {
|
|
8582
|
+
const newEntries = [...existing];
|
|
8583
|
+
newEntries[node9Idx] = { ...newEntries[node9Idx], command };
|
|
8584
|
+
doc.setIn(["hooks", event], newEntries);
|
|
8585
|
+
console.log(import_chalk.default.yellow(` \u{1F527} Hermes ${event} hook repaired (stale path \u2192 current binary)`));
|
|
8586
|
+
anythingChanged = true;
|
|
8587
|
+
}
|
|
8588
|
+
}
|
|
8589
|
+
if (current.hooks_auto_accept !== true) {
|
|
8590
|
+
doc.set("hooks_auto_accept", true);
|
|
8591
|
+
console.log(import_chalk.default.green(" \u2705 hooks_auto_accept set to true"));
|
|
8592
|
+
anythingChanged = true;
|
|
8593
|
+
}
|
|
8594
|
+
if (anythingChanged) {
|
|
8595
|
+
import_fs13.default.writeFileSync(configPath, doc.toString());
|
|
8596
|
+
}
|
|
8597
|
+
let allowlist = {};
|
|
8598
|
+
if (import_fs13.default.existsSync(allowlistPath)) {
|
|
8599
|
+
try {
|
|
8600
|
+
allowlist = JSON.parse(import_fs13.default.readFileSync(allowlistPath, "utf-8"));
|
|
8601
|
+
} catch {
|
|
8602
|
+
allowlist = {};
|
|
8603
|
+
}
|
|
8604
|
+
}
|
|
8605
|
+
if (!Array.isArray(allowlist.approvals)) allowlist.approvals = [];
|
|
8606
|
+
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
8607
|
+
let allowlistChanged = false;
|
|
8608
|
+
for (const { event, subcmd } of HERMES_HOOK_PLAN) {
|
|
8609
|
+
const command = fullPathCommand(subcmd);
|
|
8610
|
+
const existingIdx = allowlist.approvals.findIndex(
|
|
8611
|
+
(e) => e?.event === event && typeof e?.command === "string" && isNode9Hook(e.command)
|
|
8612
|
+
);
|
|
8613
|
+
if (existingIdx === -1) {
|
|
8614
|
+
allowlist.approvals.push({ event, command, approved_at: nowIso });
|
|
8615
|
+
allowlistChanged = true;
|
|
8616
|
+
} else if (allowlist.approvals[existingIdx].command !== command) {
|
|
8617
|
+
allowlist.approvals[existingIdx] = { event, command, approved_at: nowIso };
|
|
8618
|
+
allowlistChanged = true;
|
|
8619
|
+
}
|
|
8620
|
+
}
|
|
8621
|
+
if (allowlistChanged) {
|
|
8622
|
+
import_fs13.default.mkdirSync(import_path15.default.dirname(allowlistPath), { recursive: true });
|
|
8623
|
+
import_fs13.default.writeFileSync(allowlistPath, JSON.stringify(allowlist, null, 2) + "\n");
|
|
8624
|
+
console.log(import_chalk.default.green(" \u2705 Hermes shell-hooks allowlist populated"));
|
|
8625
|
+
anythingChanged = true;
|
|
8626
|
+
}
|
|
8627
|
+
if (anythingChanged) {
|
|
8628
|
+
console.log(import_chalk.default.green.bold("\u{1F6E1}\uFE0F Node9 is now protecting Hermes Agent!"));
|
|
8629
|
+
console.log(import_chalk.default.gray(" Restart Hermes for changes to take effect."));
|
|
8630
|
+
printDaemonTip();
|
|
8631
|
+
} else {
|
|
8632
|
+
console.log(import_chalk.default.blue("\u2139\uFE0F Node9 is already fully configured for Hermes Agent."));
|
|
8633
|
+
printDaemonTip();
|
|
8634
|
+
}
|
|
8635
|
+
}
|
|
8636
|
+
function teardownHermes() {
|
|
8637
|
+
const homeDir2 = import_os12.default.homedir();
|
|
8638
|
+
const configPath = hermesConfigPath(homeDir2);
|
|
8639
|
+
const allowlistPath = hermesAllowlistPath(homeDir2);
|
|
8640
|
+
if (!import_fs13.default.existsSync(configPath)) {
|
|
8641
|
+
console.log(import_chalk.default.blue(` \u2139\uFE0F ${configPath} not found \u2014 nothing to remove`));
|
|
8642
|
+
return;
|
|
8643
|
+
}
|
|
8644
|
+
const raw = import_fs13.default.readFileSync(configPath, "utf-8");
|
|
8645
|
+
const doc = yaml.parseDocument(raw);
|
|
8646
|
+
if (doc.errors.length > 0) {
|
|
8647
|
+
console.log(
|
|
8648
|
+
import_chalk.default.yellow(` \u26A0\uFE0F Skipping ${configPath} \u2014 file has YAML parse errors, fix it manually.`)
|
|
8649
|
+
);
|
|
8650
|
+
} else {
|
|
8651
|
+
teardownHermesConfigDoc(doc, configPath);
|
|
8652
|
+
}
|
|
8653
|
+
teardownHermesAllowlist(allowlistPath);
|
|
8654
|
+
}
|
|
8655
|
+
function teardownHermesConfigDoc(doc, configPath) {
|
|
8656
|
+
let anythingChanged = false;
|
|
8657
|
+
const current = doc.toJS() ?? {};
|
|
8658
|
+
for (const { event } of HERMES_HOOK_PLAN) {
|
|
8659
|
+
const existing = current.hooks?.[event] ?? [];
|
|
8660
|
+
const filtered = existing.filter(
|
|
8661
|
+
(e) => !(typeof e?.command === "string" && isNode9Hook(e.command))
|
|
8662
|
+
);
|
|
8663
|
+
if (filtered.length === existing.length) continue;
|
|
8664
|
+
if (filtered.length === 0) {
|
|
8665
|
+
doc.deleteIn(["hooks", event]);
|
|
8666
|
+
} else {
|
|
8667
|
+
doc.setIn(["hooks", event], filtered);
|
|
8668
|
+
}
|
|
8669
|
+
anythingChanged = true;
|
|
8670
|
+
}
|
|
8671
|
+
const afterHooks = doc.toJS()?.hooks;
|
|
8672
|
+
if (afterHooks && typeof afterHooks === "object" && Object.keys(afterHooks).length === 0) {
|
|
8673
|
+
doc.delete("hooks");
|
|
8674
|
+
anythingChanged = true;
|
|
8675
|
+
}
|
|
8676
|
+
if (anythingChanged) {
|
|
8677
|
+
import_fs13.default.writeFileSync(configPath, doc.toString());
|
|
8678
|
+
console.log(import_chalk.default.green(` \u2705 Removed Node9 hooks from ${configPath}`));
|
|
8679
|
+
} else {
|
|
8680
|
+
console.log(import_chalk.default.blue(` \u2139\uFE0F No Node9 hooks found in ${configPath}`));
|
|
8681
|
+
}
|
|
8682
|
+
}
|
|
8683
|
+
function teardownHermesAllowlist(allowlistPath) {
|
|
8684
|
+
if (!import_fs13.default.existsSync(allowlistPath)) return;
|
|
8685
|
+
try {
|
|
8686
|
+
const raw = import_fs13.default.readFileSync(allowlistPath, "utf-8");
|
|
8687
|
+
const allowlist = JSON.parse(raw);
|
|
8688
|
+
if (!Array.isArray(allowlist.approvals)) return;
|
|
8689
|
+
const before = allowlist.approvals.length;
|
|
8690
|
+
allowlist.approvals = allowlist.approvals.filter(
|
|
8691
|
+
(e) => !(typeof e?.command === "string" && isNode9Hook(e.command))
|
|
8692
|
+
);
|
|
8693
|
+
if (allowlist.approvals.length === before) return;
|
|
8694
|
+
import_fs13.default.writeFileSync(allowlistPath, JSON.stringify(allowlist, null, 2) + "\n");
|
|
8695
|
+
console.log(import_chalk.default.green(` \u2705 Removed Node9 entries from ${allowlistPath}`));
|
|
8696
|
+
} catch {
|
|
8697
|
+
}
|
|
8698
|
+
}
|
|
8150
8699
|
function getAgentsStatus(homeDir2 = import_os12.default.homedir()) {
|
|
8151
8700
|
const detected = detectAgents(homeDir2);
|
|
8152
8701
|
const claudeWired = (() => {
|
|
@@ -8157,6 +8706,18 @@ function getAgentsStatus(homeDir2 = import_os12.default.homedir()) {
|
|
|
8157
8706
|
const settings = readJson(import_path15.default.join(homeDir2, ".gemini", "settings.json"));
|
|
8158
8707
|
return !!settings?.hooks?.BeforeTool?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)));
|
|
8159
8708
|
})();
|
|
8709
|
+
const antigravityWired = (() => {
|
|
8710
|
+
const hooksFile = readJson(
|
|
8711
|
+
import_path15.default.join(homeDir2, ".gemini", "config", "hooks.json")
|
|
8712
|
+
);
|
|
8713
|
+
return !!hooksFile?.hooks?.PreToolUse?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)));
|
|
8714
|
+
})();
|
|
8715
|
+
const copilotWired = (() => {
|
|
8716
|
+
const hooksFile = readJson(
|
|
8717
|
+
import_path15.default.join(homeDir2, ".copilot", "hooks", "node9.json")
|
|
8718
|
+
);
|
|
8719
|
+
return !!hooksFile?.hooks?.PreToolUse?.some((h) => isNode9Hook(h.command));
|
|
8720
|
+
})();
|
|
8160
8721
|
const cursorWired = (() => {
|
|
8161
8722
|
const cfg = readJson(import_path15.default.join(homeDir2, ".cursor", "mcp.json"));
|
|
8162
8723
|
return !!(cfg?.mcpServers && hasNode9McpServer(cfg.mcpServers));
|
|
@@ -8190,6 +8751,20 @@ function getAgentsStatus(homeDir2 = import_os12.default.homedir()) {
|
|
|
8190
8751
|
wired: geminiWired,
|
|
8191
8752
|
mode: detected.gemini ? "hooks" : null
|
|
8192
8753
|
},
|
|
8754
|
+
{
|
|
8755
|
+
name: "antigravity",
|
|
8756
|
+
label: "Antigravity",
|
|
8757
|
+
installed: detected.antigravity,
|
|
8758
|
+
wired: antigravityWired,
|
|
8759
|
+
mode: detected.antigravity ? "hooks" : null
|
|
8760
|
+
},
|
|
8761
|
+
{
|
|
8762
|
+
name: "copilot",
|
|
8763
|
+
label: "GitHub Copilot",
|
|
8764
|
+
installed: detected.copilot,
|
|
8765
|
+
wired: copilotWired,
|
|
8766
|
+
mode: detected.copilot ? "hooks" : null
|
|
8767
|
+
},
|
|
8193
8768
|
{
|
|
8194
8769
|
name: "cursor",
|
|
8195
8770
|
label: "Cursor",
|
|
@@ -8258,10 +8833,29 @@ function getAgentsStatus(homeDir2 = import_os12.default.homedir()) {
|
|
|
8258
8833
|
// simple existence check on the canonical install location.
|
|
8259
8834
|
wired: import_fs13.default.existsSync(import_path15.default.join(homeDir2, ".pi", "agent", "extensions", PI_EXTENSION_NAME)),
|
|
8260
8835
|
mode: detected.pi ? "hooks" : null
|
|
8836
|
+
},
|
|
8837
|
+
{
|
|
8838
|
+
name: "hermes",
|
|
8839
|
+
label: "Hermes Agent",
|
|
8840
|
+
installed: detected.hermes,
|
|
8841
|
+
// Wired = node9 hook entry exists in the parsed config.yaml.
|
|
8842
|
+
// Reading the YAML cheaply via yaml.parse — we don't need the
|
|
8843
|
+
// Document API for a boolean status check.
|
|
8844
|
+
wired: (() => {
|
|
8845
|
+
try {
|
|
8846
|
+
const raw = import_fs13.default.readFileSync(hermesConfigPath(homeDir2), "utf-8");
|
|
8847
|
+
const cfg = yaml.parse(raw);
|
|
8848
|
+
const pre = cfg?.hooks?.["pre_tool_call"] ?? [];
|
|
8849
|
+
return pre.some((e) => typeof e?.command === "string" && isNode9Hook(e.command));
|
|
8850
|
+
} catch {
|
|
8851
|
+
return false;
|
|
8852
|
+
}
|
|
8853
|
+
})(),
|
|
8854
|
+
mode: detected.hermes ? "hooks" : null
|
|
8261
8855
|
}
|
|
8262
8856
|
];
|
|
8263
8857
|
}
|
|
8264
|
-
var import_fs13, import_path15, import_os12, import_chalk, import_prompts, import_smol_toml, NODE9_MCP_SERVER_ENTRY, CODEX_PRE_TOOL_MATCHERS, OPENCODE_PLUGIN_NAME, PI_EXTENSION_NAME;
|
|
8858
|
+
var import_fs13, import_path15, import_os12, import_chalk, import_prompts, import_smol_toml, yaml, NODE9_MCP_SERVER_ENTRY, CODEX_PRE_TOOL_MATCHERS, OPENCODE_PLUGIN_NAME, PI_EXTENSION_NAME, HERMES_CONFIG_FILENAME, HERMES_ALLOWLIST_FILENAME, HERMES_HOOK_PLAN;
|
|
8265
8859
|
var init_setup = __esm({
|
|
8266
8860
|
"src/setup.ts"() {
|
|
8267
8861
|
"use strict";
|
|
@@ -8271,6 +8865,7 @@ var init_setup = __esm({
|
|
|
8271
8865
|
import_chalk = __toESM(require("chalk"));
|
|
8272
8866
|
import_prompts = require("@inquirer/prompts");
|
|
8273
8867
|
import_smol_toml = require("smol-toml");
|
|
8868
|
+
yaml = __toESM(require("yaml"));
|
|
8274
8869
|
init_mcp_pin();
|
|
8275
8870
|
init_setup_opencode_shim();
|
|
8276
8871
|
init_setup_pi_shim();
|
|
@@ -8278,10 +8873,93 @@ var init_setup = __esm({
|
|
|
8278
8873
|
CODEX_PRE_TOOL_MATCHERS = ["^Bash$", "^apply_patch$", "^mcp__.*"];
|
|
8279
8874
|
OPENCODE_PLUGIN_NAME = "node9.js";
|
|
8280
8875
|
PI_EXTENSION_NAME = "node9.js";
|
|
8876
|
+
HERMES_CONFIG_FILENAME = "config.yaml";
|
|
8877
|
+
HERMES_ALLOWLIST_FILENAME = "shell-hooks-allowlist.json";
|
|
8878
|
+
HERMES_HOOK_PLAN = [
|
|
8879
|
+
{ event: "pre_tool_call", subcmd: "check" },
|
|
8880
|
+
{ event: "post_tool_call", subcmd: "log" }
|
|
8881
|
+
];
|
|
8882
|
+
}
|
|
8883
|
+
});
|
|
8884
|
+
|
|
8885
|
+
// src/utils/hook-payload.ts
|
|
8886
|
+
function extractToolName(payload, defaultValue = "") {
|
|
8887
|
+
return payload.tool_name ?? payload.name ?? payload.toolCall?.name ?? defaultValue;
|
|
8888
|
+
}
|
|
8889
|
+
function extractToolInput(payload) {
|
|
8890
|
+
return payload.tool_input ?? payload.args ?? payload.toolCall?.args ?? {};
|
|
8891
|
+
}
|
|
8892
|
+
function canonicalToolName(name) {
|
|
8893
|
+
switch (name) {
|
|
8894
|
+
// Hermes Agent
|
|
8895
|
+
case "terminal":
|
|
8896
|
+
return "Bash";
|
|
8897
|
+
case "write_file":
|
|
8898
|
+
return "Write";
|
|
8899
|
+
case "patch":
|
|
8900
|
+
return "Edit";
|
|
8901
|
+
case "read_file":
|
|
8902
|
+
return "Read";
|
|
8903
|
+
case "search_files":
|
|
8904
|
+
return "Grep";
|
|
8905
|
+
// Antigravity (agy) — shell tool renamed from Gemini's run_shell_command
|
|
8906
|
+
case "run_command":
|
|
8907
|
+
return "Bash";
|
|
8908
|
+
default:
|
|
8909
|
+
return name;
|
|
8910
|
+
}
|
|
8911
|
+
}
|
|
8912
|
+
function agentLabelFromFlag(flag) {
|
|
8913
|
+
if (typeof flag !== "string") return void 0;
|
|
8914
|
+
switch (flag.toLowerCase()) {
|
|
8915
|
+
case "antigravity":
|
|
8916
|
+
case "agy":
|
|
8917
|
+
return "Antigravity";
|
|
8918
|
+
case "copilot":
|
|
8919
|
+
return "GitHub Copilot";
|
|
8920
|
+
default:
|
|
8921
|
+
return void 0;
|
|
8922
|
+
}
|
|
8923
|
+
}
|
|
8924
|
+
function canonicalToolInput(rawToolName, input) {
|
|
8925
|
+
if (rawToolName !== "run_command") return input;
|
|
8926
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) return input;
|
|
8927
|
+
const args = input;
|
|
8928
|
+
if (typeof args.CommandLine !== "string") return input;
|
|
8929
|
+
const { CommandLine, Cwd, ...rest } = args;
|
|
8930
|
+
const canonical = { ...rest, command: CommandLine };
|
|
8931
|
+
if (typeof Cwd === "string" && Cwd.length > 0) canonical.cwd = Cwd;
|
|
8932
|
+
return canonical;
|
|
8933
|
+
}
|
|
8934
|
+
var init_hook_payload = __esm({
|
|
8935
|
+
"src/utils/hook-payload.ts"() {
|
|
8936
|
+
"use strict";
|
|
8281
8937
|
}
|
|
8282
8938
|
});
|
|
8283
8939
|
|
|
8284
8940
|
// src/scan-summary.ts
|
|
8941
|
+
function agentDisplayName(agent) {
|
|
8942
|
+
return AGENT_LONG[agent] ?? "Claude Code";
|
|
8943
|
+
}
|
|
8944
|
+
function agentBadgeText(agent, width = 10) {
|
|
8945
|
+
return `[${AGENT_SHORT[agent] ?? "Claude"}]`.padEnd(width);
|
|
8946
|
+
}
|
|
8947
|
+
function agentColorName(agent) {
|
|
8948
|
+
switch (agent) {
|
|
8949
|
+
case "gemini":
|
|
8950
|
+
return "blue";
|
|
8951
|
+
case "codex":
|
|
8952
|
+
return "magenta";
|
|
8953
|
+
case "antigravity":
|
|
8954
|
+
return "yellow";
|
|
8955
|
+
case "copilot":
|
|
8956
|
+
return "green";
|
|
8957
|
+
case "shell":
|
|
8958
|
+
return "yellow";
|
|
8959
|
+
default:
|
|
8960
|
+
return "cyan";
|
|
8961
|
+
}
|
|
8962
|
+
}
|
|
8285
8963
|
function buildScanSummary(agents) {
|
|
8286
8964
|
const stats = {
|
|
8287
8965
|
sessions: 0,
|
|
@@ -8458,12 +9136,29 @@ function fullCommandOf(input) {
|
|
|
8458
9136
|
const raw = input.command ?? input.query ?? input.file_path ?? JSON.stringify(input);
|
|
8459
9137
|
return String(raw).replace(/\s+/g, " ").trim();
|
|
8460
9138
|
}
|
|
9139
|
+
var AGENT_SHORT, AGENT_LONG;
|
|
8461
9140
|
var init_scan_summary = __esm({
|
|
8462
9141
|
"src/scan-summary.ts"() {
|
|
8463
9142
|
"use strict";
|
|
8464
9143
|
init_shields();
|
|
8465
9144
|
init_dist();
|
|
8466
9145
|
init_dist();
|
|
9146
|
+
AGENT_SHORT = {
|
|
9147
|
+
claude: "Claude",
|
|
9148
|
+
gemini: "Gemini",
|
|
9149
|
+
codex: "Codex",
|
|
9150
|
+
antigravity: "Agy",
|
|
9151
|
+
copilot: "Copilot",
|
|
9152
|
+
shell: "Shell"
|
|
9153
|
+
};
|
|
9154
|
+
AGENT_LONG = {
|
|
9155
|
+
claude: "Claude Code",
|
|
9156
|
+
gemini: "Gemini CLI",
|
|
9157
|
+
codex: "Codex",
|
|
9158
|
+
antigravity: "Antigravity",
|
|
9159
|
+
copilot: "GitHub Copilot",
|
|
9160
|
+
shell: "Shell"
|
|
9161
|
+
};
|
|
8467
9162
|
}
|
|
8468
9163
|
});
|
|
8469
9164
|
|
|
@@ -10104,6 +10799,34 @@ function countScanFiles() {
|
|
|
10104
10799
|
} catch {
|
|
10105
10800
|
}
|
|
10106
10801
|
}
|
|
10802
|
+
for (const surface of ["antigravity-cli", "antigravity-ide"]) {
|
|
10803
|
+
const brainDir = import_path22.default.join(import_os19.default.homedir(), ".gemini", surface, "brain");
|
|
10804
|
+
if (!import_fs20.default.existsSync(brainDir)) continue;
|
|
10805
|
+
try {
|
|
10806
|
+
for (const conv of import_fs20.default.readdirSync(brainDir)) {
|
|
10807
|
+
const convPath = import_path22.default.join(brainDir, conv);
|
|
10808
|
+
try {
|
|
10809
|
+
if (!import_fs20.default.statSync(convPath).isDirectory()) continue;
|
|
10810
|
+
const logsDir = import_path22.default.join(convPath, ".system_generated", "logs");
|
|
10811
|
+
if (import_fs20.default.existsSync(import_path22.default.join(logsDir, "transcript_full.jsonl")) || import_fs20.default.existsSync(import_path22.default.join(logsDir, "transcript.jsonl"))) {
|
|
10812
|
+
total += 1;
|
|
10813
|
+
}
|
|
10814
|
+
} catch {
|
|
10815
|
+
continue;
|
|
10816
|
+
}
|
|
10817
|
+
}
|
|
10818
|
+
} catch {
|
|
10819
|
+
}
|
|
10820
|
+
}
|
|
10821
|
+
const copilotDir = import_path22.default.join(import_os19.default.homedir(), ".copilot", "session-state");
|
|
10822
|
+
if (import_fs20.default.existsSync(copilotDir)) {
|
|
10823
|
+
try {
|
|
10824
|
+
for (const sid of import_fs20.default.readdirSync(copilotDir)) {
|
|
10825
|
+
if (import_fs20.default.existsSync(import_path22.default.join(copilotDir, sid, "events.jsonl"))) total += 1;
|
|
10826
|
+
}
|
|
10827
|
+
} catch {
|
|
10828
|
+
}
|
|
10829
|
+
}
|
|
10107
10830
|
const codexDir = import_path22.default.join(import_os19.default.homedir(), ".codex", "sessions");
|
|
10108
10831
|
if (import_fs20.default.existsSync(codexDir)) {
|
|
10109
10832
|
try {
|
|
@@ -10432,8 +11155,231 @@ function scanClaudeHistory(startDate, onProgress, onLine) {
|
|
|
10432
11155
|
}
|
|
10433
11156
|
return result;
|
|
10434
11157
|
}
|
|
10435
|
-
function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
10436
|
-
const tmpDir = import_path22.default.join(import_os19.default.homedir(), ".gemini", "tmp");
|
|
11158
|
+
function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
11159
|
+
const tmpDir = import_path22.default.join(import_os19.default.homedir(), ".gemini", "tmp");
|
|
11160
|
+
const result = {
|
|
11161
|
+
filesScanned: 0,
|
|
11162
|
+
sessions: 0,
|
|
11163
|
+
totalToolCalls: 0,
|
|
11164
|
+
bashCalls: 0,
|
|
11165
|
+
findings: [],
|
|
11166
|
+
dlpFindings: [],
|
|
11167
|
+
loopFindings: [],
|
|
11168
|
+
totalCostUSD: 0,
|
|
11169
|
+
firstDate: null,
|
|
11170
|
+
lastDate: null,
|
|
11171
|
+
sessionsWithEarlySecrets: 0
|
|
11172
|
+
};
|
|
11173
|
+
const dedup = emptyScanDedup();
|
|
11174
|
+
if (!import_fs20.default.existsSync(tmpDir)) return result;
|
|
11175
|
+
let slugDirs;
|
|
11176
|
+
try {
|
|
11177
|
+
slugDirs = import_fs20.default.readdirSync(tmpDir);
|
|
11178
|
+
} catch {
|
|
11179
|
+
return result;
|
|
11180
|
+
}
|
|
11181
|
+
const ruleSources = buildRuleSources();
|
|
11182
|
+
for (const slug of slugDirs) {
|
|
11183
|
+
const slugPath = import_path22.default.join(tmpDir, slug);
|
|
11184
|
+
try {
|
|
11185
|
+
if (!import_fs20.default.statSync(slugPath).isDirectory()) continue;
|
|
11186
|
+
} catch {
|
|
11187
|
+
continue;
|
|
11188
|
+
}
|
|
11189
|
+
let projLabel = stripTerminalEscapes(slug).slice(0, 40);
|
|
11190
|
+
try {
|
|
11191
|
+
projLabel = stripTerminalEscapes(
|
|
11192
|
+
import_fs20.default.readFileSync(import_path22.default.join(slugPath, ".project_root"), "utf-8").trim()
|
|
11193
|
+
).replace(import_os19.default.homedir(), "~").slice(0, 40);
|
|
11194
|
+
} catch {
|
|
11195
|
+
}
|
|
11196
|
+
const chatsDir = import_path22.default.join(slugPath, "chats");
|
|
11197
|
+
if (!import_fs20.default.existsSync(chatsDir)) continue;
|
|
11198
|
+
let chatFiles;
|
|
11199
|
+
try {
|
|
11200
|
+
chatFiles = import_fs20.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
|
|
11201
|
+
} catch {
|
|
11202
|
+
continue;
|
|
11203
|
+
}
|
|
11204
|
+
for (const chatFile of chatFiles) {
|
|
11205
|
+
result.filesScanned++;
|
|
11206
|
+
onProgress?.(result.filesScanned);
|
|
11207
|
+
const sessionId = chatFile.replace(/\.json$/, "");
|
|
11208
|
+
let raw;
|
|
11209
|
+
try {
|
|
11210
|
+
raw = import_fs20.default.readFileSync(import_path22.default.join(chatsDir, chatFile), "utf-8");
|
|
11211
|
+
} catch {
|
|
11212
|
+
continue;
|
|
11213
|
+
}
|
|
11214
|
+
const sessionCalls = [];
|
|
11215
|
+
let session;
|
|
11216
|
+
try {
|
|
11217
|
+
session = JSON.parse(raw);
|
|
11218
|
+
} catch {
|
|
11219
|
+
continue;
|
|
11220
|
+
}
|
|
11221
|
+
result.sessions++;
|
|
11222
|
+
for (const msg of session.messages ?? []) {
|
|
11223
|
+
onLine?.();
|
|
11224
|
+
if (msg.type === "user") {
|
|
11225
|
+
const content = msg.content;
|
|
11226
|
+
const text = Array.isArray(content) ? content.map((c) => c.text ?? "").join("\n") : typeof content === "string" ? content : "";
|
|
11227
|
+
if (text) {
|
|
11228
|
+
const dlpMatch = scanArgs({ text });
|
|
11229
|
+
if (dlpMatch) {
|
|
11230
|
+
const k = dlpKey(dlpMatch.patternName, dlpMatch.redactedSample, projLabel);
|
|
11231
|
+
if (!dedup.dlpKeys.has(k)) {
|
|
11232
|
+
dedup.dlpKeys.add(k);
|
|
11233
|
+
result.dlpFindings.push({
|
|
11234
|
+
patternName: dlpMatch.patternName,
|
|
11235
|
+
redactedSample: dlpMatch.redactedSample,
|
|
11236
|
+
toolName: "user-prompt",
|
|
11237
|
+
timestamp: msg.timestamp ?? "",
|
|
11238
|
+
project: projLabel,
|
|
11239
|
+
sessionId,
|
|
11240
|
+
agent: "gemini"
|
|
11241
|
+
});
|
|
11242
|
+
}
|
|
11243
|
+
}
|
|
11244
|
+
}
|
|
11245
|
+
continue;
|
|
11246
|
+
}
|
|
11247
|
+
if (msg.type !== "gemini") continue;
|
|
11248
|
+
if (startDate && msg.timestamp && new Date(msg.timestamp) < startDate) continue;
|
|
11249
|
+
if (msg.timestamp) {
|
|
11250
|
+
if (!result.firstDate || msg.timestamp < result.firstDate)
|
|
11251
|
+
result.firstDate = msg.timestamp;
|
|
11252
|
+
if (!result.lastDate || msg.timestamp > result.lastDate) result.lastDate = msg.timestamp;
|
|
11253
|
+
}
|
|
11254
|
+
const tokens = msg.tokens;
|
|
11255
|
+
const model = msg.model;
|
|
11256
|
+
if (tokens && model) {
|
|
11257
|
+
const p = geminiModelPrice(model);
|
|
11258
|
+
if (p) {
|
|
11259
|
+
const nonCached = Math.max(0, tokens.input - tokens.cached);
|
|
11260
|
+
result.totalCostUSD += nonCached * p.i + tokens.cached * p.cr + tokens.output * p.o;
|
|
11261
|
+
}
|
|
11262
|
+
}
|
|
11263
|
+
for (const tc of msg.toolCalls ?? []) {
|
|
11264
|
+
result.totalToolCalls++;
|
|
11265
|
+
const toolName = tc.name ?? "";
|
|
11266
|
+
const toolNameLower = toolName.toLowerCase();
|
|
11267
|
+
const input = tc.args ?? {};
|
|
11268
|
+
sessionCalls.push({ toolName, input, timestamp: msg.timestamp ?? "" });
|
|
11269
|
+
if (toolNameLower === "run_shell_command" || toolNameLower === "shell") {
|
|
11270
|
+
result.bashCalls++;
|
|
11271
|
+
}
|
|
11272
|
+
const rawCmd = String(input.command ?? "").trimStart();
|
|
11273
|
+
if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd))
|
|
11274
|
+
continue;
|
|
11275
|
+
const dlpMatch = scanArgs(input);
|
|
11276
|
+
if (dlpMatch) {
|
|
11277
|
+
const k = dlpKey(dlpMatch.patternName, dlpMatch.redactedSample, projLabel);
|
|
11278
|
+
if (!dedup.dlpKeys.has(k)) {
|
|
11279
|
+
dedup.dlpKeys.add(k);
|
|
11280
|
+
result.dlpFindings.push({
|
|
11281
|
+
patternName: dlpMatch.patternName,
|
|
11282
|
+
redactedSample: dlpMatch.redactedSample,
|
|
11283
|
+
toolName,
|
|
11284
|
+
timestamp: msg.timestamp ?? "",
|
|
11285
|
+
project: projLabel,
|
|
11286
|
+
sessionId,
|
|
11287
|
+
agent: "gemini"
|
|
11288
|
+
});
|
|
11289
|
+
}
|
|
11290
|
+
}
|
|
11291
|
+
let astFsMatched = false;
|
|
11292
|
+
const astRanForBash = toolNameLower === "run_shell_command" || toolNameLower === "shell";
|
|
11293
|
+
if (astRanForBash) {
|
|
11294
|
+
astFsMatched = pushFsOpAstFinding(
|
|
11295
|
+
String(input.command ?? ""),
|
|
11296
|
+
toolName,
|
|
11297
|
+
input,
|
|
11298
|
+
msg.timestamp ?? "",
|
|
11299
|
+
projLabel,
|
|
11300
|
+
sessionId,
|
|
11301
|
+
"gemini",
|
|
11302
|
+
result,
|
|
11303
|
+
dedup
|
|
11304
|
+
);
|
|
11305
|
+
}
|
|
11306
|
+
let ruleMatched = astFsMatched;
|
|
11307
|
+
for (const source of ruleSources) {
|
|
11308
|
+
const { rule } = source;
|
|
11309
|
+
if (rule.verdict === "allow") continue;
|
|
11310
|
+
if (rule.tool && !matchesPattern(toolNameLower, rule.tool)) continue;
|
|
11311
|
+
if (astRanForBash && rule.name && AST_FS_REGEX_RULES.has(rule.name)) continue;
|
|
11312
|
+
if (!evaluateSmartConditions(input, rule)) continue;
|
|
11313
|
+
const inputPreview = preview(input, 120);
|
|
11314
|
+
const k = findingKey(rule.name, inputPreview, projLabel);
|
|
11315
|
+
if (!dedup.findingsKeys.has(k)) {
|
|
11316
|
+
dedup.findingsKeys.add(k);
|
|
11317
|
+
result.findings.push({
|
|
11318
|
+
source,
|
|
11319
|
+
toolName,
|
|
11320
|
+
input,
|
|
11321
|
+
timestamp: msg.timestamp ?? "",
|
|
11322
|
+
project: projLabel,
|
|
11323
|
+
sessionId,
|
|
11324
|
+
agent: "gemini"
|
|
11325
|
+
});
|
|
11326
|
+
}
|
|
11327
|
+
ruleMatched = true;
|
|
11328
|
+
break;
|
|
11329
|
+
}
|
|
11330
|
+
const isShellTool = ["bash", "execute_bash", "run_shell_command", "shell"].includes(
|
|
11331
|
+
toolNameLower
|
|
11332
|
+
);
|
|
11333
|
+
if (!ruleMatched && isShellTool) {
|
|
11334
|
+
const shellVerdict = detectDangerousShellExec(String(input.command ?? ""));
|
|
11335
|
+
if (shellVerdict) {
|
|
11336
|
+
const astRule = {
|
|
11337
|
+
name: `ast:bash-safe:${shellVerdict}-shell-exec-remote`,
|
|
11338
|
+
tool: "bash",
|
|
11339
|
+
conditions: [],
|
|
11340
|
+
verdict: shellVerdict,
|
|
11341
|
+
reason: `Shell execution of remote download detected by AST analysis (bash-safe)`
|
|
11342
|
+
};
|
|
11343
|
+
const inputPreview = preview(input, 120);
|
|
11344
|
+
const k = findingKey(astRule.name, inputPreview, projLabel);
|
|
11345
|
+
if (!dedup.findingsKeys.has(k)) {
|
|
11346
|
+
dedup.findingsKeys.add(k);
|
|
11347
|
+
result.findings.push({
|
|
11348
|
+
source: {
|
|
11349
|
+
shieldName: "bash-safe",
|
|
11350
|
+
shieldLabel: "bash-safe (AST)",
|
|
11351
|
+
sourceType: "shield",
|
|
11352
|
+
rule: astRule
|
|
11353
|
+
},
|
|
11354
|
+
toolName,
|
|
11355
|
+
input,
|
|
11356
|
+
timestamp: msg.timestamp ?? "",
|
|
11357
|
+
project: projLabel,
|
|
11358
|
+
sessionId,
|
|
11359
|
+
agent: "gemini"
|
|
11360
|
+
});
|
|
11361
|
+
}
|
|
11362
|
+
}
|
|
11363
|
+
}
|
|
11364
|
+
}
|
|
11365
|
+
}
|
|
11366
|
+
result.loopFindings.push(...detectLoops(sessionCalls, projLabel, sessionId, "gemini"));
|
|
11367
|
+
}
|
|
11368
|
+
}
|
|
11369
|
+
return result;
|
|
11370
|
+
}
|
|
11371
|
+
function antigravityBrainDirs() {
|
|
11372
|
+
return ["antigravity-cli", "antigravity-ide"].map((surface) => import_path22.default.join(import_os19.default.homedir(), ".gemini", surface, "brain")).filter((p) => import_fs20.default.existsSync(p));
|
|
11373
|
+
}
|
|
11374
|
+
function antigravityTranscriptPath(convPath) {
|
|
11375
|
+
const logsDir = import_path22.default.join(convPath, ".system_generated", "logs");
|
|
11376
|
+
for (const name of ["transcript_full.jsonl", "transcript.jsonl"]) {
|
|
11377
|
+
const p = import_path22.default.join(logsDir, name);
|
|
11378
|
+
if (import_fs20.default.existsSync(p)) return p;
|
|
11379
|
+
}
|
|
11380
|
+
return null;
|
|
11381
|
+
}
|
|
11382
|
+
function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
10437
11383
|
const result = {
|
|
10438
11384
|
filesScanned: 0,
|
|
10439
11385
|
sessions: 0,
|
|
@@ -10443,64 +11389,56 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
10443
11389
|
dlpFindings: [],
|
|
10444
11390
|
loopFindings: [],
|
|
10445
11391
|
totalCostUSD: 0,
|
|
11392
|
+
// transcripts carry no token/model data
|
|
10446
11393
|
firstDate: null,
|
|
10447
11394
|
lastDate: null,
|
|
10448
11395
|
sessionsWithEarlySecrets: 0
|
|
10449
11396
|
};
|
|
10450
11397
|
const dedup = emptyScanDedup();
|
|
10451
|
-
|
|
10452
|
-
|
|
10453
|
-
try {
|
|
10454
|
-
slugDirs = import_fs20.default.readdirSync(tmpDir);
|
|
10455
|
-
} catch {
|
|
10456
|
-
return result;
|
|
10457
|
-
}
|
|
11398
|
+
const brainDirs = antigravityBrainDirs();
|
|
11399
|
+
if (brainDirs.length === 0) return result;
|
|
10458
11400
|
const ruleSources = buildRuleSources();
|
|
10459
|
-
for (const
|
|
10460
|
-
|
|
11401
|
+
for (const brainDir of brainDirs) {
|
|
11402
|
+
let convDirs;
|
|
10461
11403
|
try {
|
|
10462
|
-
|
|
10463
|
-
} catch {
|
|
10464
|
-
continue;
|
|
10465
|
-
}
|
|
10466
|
-
let projLabel = stripTerminalEscapes(slug).slice(0, 40);
|
|
10467
|
-
try {
|
|
10468
|
-
projLabel = stripTerminalEscapes(
|
|
10469
|
-
import_fs20.default.readFileSync(import_path22.default.join(slugPath, ".project_root"), "utf-8").trim()
|
|
10470
|
-
).replace(import_os19.default.homedir(), "~").slice(0, 40);
|
|
10471
|
-
} catch {
|
|
10472
|
-
}
|
|
10473
|
-
const chatsDir = import_path22.default.join(slugPath, "chats");
|
|
10474
|
-
if (!import_fs20.default.existsSync(chatsDir)) continue;
|
|
10475
|
-
let chatFiles;
|
|
10476
|
-
try {
|
|
10477
|
-
chatFiles = import_fs20.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
|
|
11404
|
+
convDirs = import_fs20.default.readdirSync(brainDir);
|
|
10478
11405
|
} catch {
|
|
10479
11406
|
continue;
|
|
10480
11407
|
}
|
|
10481
|
-
for (const
|
|
10482
|
-
|
|
10483
|
-
onProgress?.(result.filesScanned);
|
|
10484
|
-
const sessionId = chatFile.replace(/\.json$/, "");
|
|
10485
|
-
let raw;
|
|
11408
|
+
for (const conv of convDirs) {
|
|
11409
|
+
const convPath = import_path22.default.join(brainDir, conv);
|
|
10486
11410
|
try {
|
|
10487
|
-
|
|
11411
|
+
if (!import_fs20.default.statSync(convPath).isDirectory()) continue;
|
|
10488
11412
|
} catch {
|
|
10489
11413
|
continue;
|
|
10490
11414
|
}
|
|
10491
|
-
const
|
|
10492
|
-
|
|
11415
|
+
const transcriptFile = antigravityTranscriptPath(convPath);
|
|
11416
|
+
if (!transcriptFile) continue;
|
|
11417
|
+
result.filesScanned++;
|
|
11418
|
+
onProgress?.(result.filesScanned);
|
|
11419
|
+
let raw;
|
|
10493
11420
|
try {
|
|
10494
|
-
|
|
11421
|
+
raw = import_fs20.default.readFileSync(transcriptFile, "utf-8");
|
|
10495
11422
|
} catch {
|
|
10496
11423
|
continue;
|
|
10497
11424
|
}
|
|
11425
|
+
const sessionId = conv;
|
|
11426
|
+
let projLabel = conv.slice(0, 8);
|
|
11427
|
+
const sessionCalls = [];
|
|
10498
11428
|
result.sessions++;
|
|
10499
|
-
for (const
|
|
11429
|
+
for (const line of raw.split("\n")) {
|
|
11430
|
+
if (!line.trim()) continue;
|
|
10500
11431
|
onLine?.();
|
|
10501
|
-
|
|
10502
|
-
|
|
10503
|
-
|
|
11432
|
+
let step;
|
|
11433
|
+
try {
|
|
11434
|
+
step = JSON.parse(line);
|
|
11435
|
+
} catch {
|
|
11436
|
+
continue;
|
|
11437
|
+
}
|
|
11438
|
+
const timestamp = step.created_at ?? "";
|
|
11439
|
+
if (startDate && timestamp && new Date(timestamp) < startDate) continue;
|
|
11440
|
+
if (step.type === "USER_INPUT") {
|
|
11441
|
+
const text = typeof step.content === "string" ? step.content : "";
|
|
10504
11442
|
if (text) {
|
|
10505
11443
|
const dlpMatch = scanArgs({ text });
|
|
10506
11444
|
if (dlpMatch) {
|
|
@@ -10511,40 +11449,34 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
10511
11449
|
patternName: dlpMatch.patternName,
|
|
10512
11450
|
redactedSample: dlpMatch.redactedSample,
|
|
10513
11451
|
toolName: "user-prompt",
|
|
10514
|
-
timestamp
|
|
11452
|
+
timestamp,
|
|
10515
11453
|
project: projLabel,
|
|
10516
11454
|
sessionId,
|
|
10517
|
-
agent: "
|
|
11455
|
+
agent: "antigravity"
|
|
10518
11456
|
});
|
|
10519
11457
|
}
|
|
10520
11458
|
}
|
|
10521
11459
|
}
|
|
10522
11460
|
continue;
|
|
10523
11461
|
}
|
|
10524
|
-
if (
|
|
10525
|
-
if (
|
|
10526
|
-
|
|
10527
|
-
if (!result.
|
|
10528
|
-
result.firstDate = msg.timestamp;
|
|
10529
|
-
if (!result.lastDate || msg.timestamp > result.lastDate) result.lastDate = msg.timestamp;
|
|
10530
|
-
}
|
|
10531
|
-
const tokens = msg.tokens;
|
|
10532
|
-
const model = msg.model;
|
|
10533
|
-
if (tokens && model) {
|
|
10534
|
-
const p = geminiModelPrice(model);
|
|
10535
|
-
if (p) {
|
|
10536
|
-
const nonCached = Math.max(0, tokens.input - tokens.cached);
|
|
10537
|
-
result.totalCostUSD += nonCached * p.i + tokens.cached * p.cr + tokens.output * p.o;
|
|
10538
|
-
}
|
|
11462
|
+
if (!Array.isArray(step.tool_calls) || step.tool_calls.length === 0) continue;
|
|
11463
|
+
if (timestamp) {
|
|
11464
|
+
if (!result.firstDate || timestamp < result.firstDate) result.firstDate = timestamp;
|
|
11465
|
+
if (!result.lastDate || timestamp > result.lastDate) result.lastDate = timestamp;
|
|
10539
11466
|
}
|
|
10540
|
-
for (const tc of
|
|
11467
|
+
for (const tc of step.tool_calls) {
|
|
10541
11468
|
result.totalToolCalls++;
|
|
10542
11469
|
const toolName = tc.name ?? "";
|
|
10543
11470
|
const toolNameLower = toolName.toLowerCase();
|
|
10544
|
-
const input = tc.args ?? {};
|
|
10545
|
-
sessionCalls.push({ toolName, input, timestamp
|
|
10546
|
-
|
|
11471
|
+
const input = canonicalToolInput(toolName, tc.args ?? {});
|
|
11472
|
+
sessionCalls.push({ toolName, input, timestamp });
|
|
11473
|
+
const isShellTool = toolNameLower === "run_command";
|
|
11474
|
+
if (isShellTool) {
|
|
10547
11475
|
result.bashCalls++;
|
|
11476
|
+
const cwd = String(input.cwd ?? "");
|
|
11477
|
+
if (cwd && projLabel === conv.slice(0, 8)) {
|
|
11478
|
+
projLabel = stripTerminalEscapes(cwd).replace(import_os19.default.homedir(), "~").slice(0, 40);
|
|
11479
|
+
}
|
|
10548
11480
|
}
|
|
10549
11481
|
const rawCmd = String(input.command ?? "").trimStart();
|
|
10550
11482
|
if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd))
|
|
@@ -10558,24 +11490,23 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
10558
11490
|
patternName: dlpMatch.patternName,
|
|
10559
11491
|
redactedSample: dlpMatch.redactedSample,
|
|
10560
11492
|
toolName,
|
|
10561
|
-
timestamp
|
|
11493
|
+
timestamp,
|
|
10562
11494
|
project: projLabel,
|
|
10563
11495
|
sessionId,
|
|
10564
|
-
agent: "
|
|
11496
|
+
agent: "antigravity"
|
|
10565
11497
|
});
|
|
10566
11498
|
}
|
|
10567
11499
|
}
|
|
10568
11500
|
let astFsMatched = false;
|
|
10569
|
-
|
|
10570
|
-
if (astRanForBash) {
|
|
11501
|
+
if (isShellTool) {
|
|
10571
11502
|
astFsMatched = pushFsOpAstFinding(
|
|
10572
11503
|
String(input.command ?? ""),
|
|
10573
11504
|
toolName,
|
|
10574
11505
|
input,
|
|
10575
|
-
|
|
11506
|
+
timestamp,
|
|
10576
11507
|
projLabel,
|
|
10577
11508
|
sessionId,
|
|
10578
|
-
"
|
|
11509
|
+
"antigravity",
|
|
10579
11510
|
result,
|
|
10580
11511
|
dedup
|
|
10581
11512
|
);
|
|
@@ -10584,8 +11515,9 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
10584
11515
|
for (const source of ruleSources) {
|
|
10585
11516
|
const { rule } = source;
|
|
10586
11517
|
if (rule.verdict === "allow") continue;
|
|
10587
|
-
|
|
10588
|
-
if (
|
|
11518
|
+
const ruleToolName = isShellTool ? "bash" : toolNameLower;
|
|
11519
|
+
if (rule.tool && !matchesPattern(ruleToolName, rule.tool)) continue;
|
|
11520
|
+
if (isShellTool && rule.name && AST_FS_REGEX_RULES.has(rule.name)) continue;
|
|
10589
11521
|
if (!evaluateSmartConditions(input, rule)) continue;
|
|
10590
11522
|
const inputPreview = preview(input, 120);
|
|
10591
11523
|
const k = findingKey(rule.name, inputPreview, projLabel);
|
|
@@ -10595,18 +11527,15 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
10595
11527
|
source,
|
|
10596
11528
|
toolName,
|
|
10597
11529
|
input,
|
|
10598
|
-
timestamp
|
|
11530
|
+
timestamp,
|
|
10599
11531
|
project: projLabel,
|
|
10600
11532
|
sessionId,
|
|
10601
|
-
agent: "
|
|
11533
|
+
agent: "antigravity"
|
|
10602
11534
|
});
|
|
10603
11535
|
}
|
|
10604
11536
|
ruleMatched = true;
|
|
10605
11537
|
break;
|
|
10606
11538
|
}
|
|
10607
|
-
const isShellTool = ["bash", "execute_bash", "run_shell_command", "shell"].includes(
|
|
10608
|
-
toolNameLower
|
|
10609
|
-
);
|
|
10610
11539
|
if (!ruleMatched && isShellTool) {
|
|
10611
11540
|
const shellVerdict = detectDangerousShellExec(String(input.command ?? ""));
|
|
10612
11541
|
if (shellVerdict) {
|
|
@@ -10630,18 +11559,201 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
10630
11559
|
},
|
|
10631
11560
|
toolName,
|
|
10632
11561
|
input,
|
|
10633
|
-
timestamp
|
|
11562
|
+
timestamp,
|
|
10634
11563
|
project: projLabel,
|
|
10635
11564
|
sessionId,
|
|
10636
|
-
agent: "
|
|
11565
|
+
agent: "antigravity"
|
|
10637
11566
|
});
|
|
10638
11567
|
}
|
|
10639
11568
|
}
|
|
10640
11569
|
}
|
|
10641
11570
|
}
|
|
10642
11571
|
}
|
|
10643
|
-
result.loopFindings.push(...detectLoops(sessionCalls, projLabel, sessionId, "
|
|
11572
|
+
result.loopFindings.push(...detectLoops(sessionCalls, projLabel, sessionId, "antigravity"));
|
|
11573
|
+
}
|
|
11574
|
+
}
|
|
11575
|
+
return result;
|
|
11576
|
+
}
|
|
11577
|
+
function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
11578
|
+
const sessionDir = import_path22.default.join(import_os19.default.homedir(), ".copilot", "session-state");
|
|
11579
|
+
const result = {
|
|
11580
|
+
filesScanned: 0,
|
|
11581
|
+
sessions: 0,
|
|
11582
|
+
totalToolCalls: 0,
|
|
11583
|
+
bashCalls: 0,
|
|
11584
|
+
findings: [],
|
|
11585
|
+
dlpFindings: [],
|
|
11586
|
+
loopFindings: [],
|
|
11587
|
+
totalCostUSD: 0,
|
|
11588
|
+
// event logs carry no token/cost rollup
|
|
11589
|
+
firstDate: null,
|
|
11590
|
+
lastDate: null,
|
|
11591
|
+
sessionsWithEarlySecrets: 0
|
|
11592
|
+
};
|
|
11593
|
+
const dedup = emptyScanDedup();
|
|
11594
|
+
if (!import_fs20.default.existsSync(sessionDir)) return result;
|
|
11595
|
+
let sessionIds;
|
|
11596
|
+
try {
|
|
11597
|
+
sessionIds = import_fs20.default.readdirSync(sessionDir);
|
|
11598
|
+
} catch {
|
|
11599
|
+
return result;
|
|
11600
|
+
}
|
|
11601
|
+
const ruleSources = buildRuleSources();
|
|
11602
|
+
for (const sessionId of sessionIds) {
|
|
11603
|
+
const eventsPath = import_path22.default.join(sessionDir, sessionId, "events.jsonl");
|
|
11604
|
+
if (!import_fs20.default.existsSync(eventsPath)) continue;
|
|
11605
|
+
result.filesScanned++;
|
|
11606
|
+
onProgress?.(result.filesScanned);
|
|
11607
|
+
let raw;
|
|
11608
|
+
try {
|
|
11609
|
+
raw = import_fs20.default.readFileSync(eventsPath, "utf-8");
|
|
11610
|
+
} catch {
|
|
11611
|
+
continue;
|
|
11612
|
+
}
|
|
11613
|
+
let projLabel = sessionId.slice(0, 8);
|
|
11614
|
+
const sessionCalls = [];
|
|
11615
|
+
result.sessions++;
|
|
11616
|
+
for (const line of raw.split("\n")) {
|
|
11617
|
+
if (!line.trim()) continue;
|
|
11618
|
+
onLine?.();
|
|
11619
|
+
let ev;
|
|
11620
|
+
try {
|
|
11621
|
+
ev = JSON.parse(line);
|
|
11622
|
+
} catch {
|
|
11623
|
+
continue;
|
|
11624
|
+
}
|
|
11625
|
+
const timestamp = ev.timestamp ?? "";
|
|
11626
|
+
if (ev.type === "session.start") {
|
|
11627
|
+
const cwd = ev.data?.context?.cwd;
|
|
11628
|
+
if (typeof cwd === "string" && cwd) {
|
|
11629
|
+
projLabel = stripTerminalEscapes(cwd).replace(import_os19.default.homedir(), "~").slice(0, 40);
|
|
11630
|
+
}
|
|
11631
|
+
continue;
|
|
11632
|
+
}
|
|
11633
|
+
if (startDate && timestamp && new Date(timestamp) < startDate) continue;
|
|
11634
|
+
if (ev.type === "user.message") {
|
|
11635
|
+
const text = ev.data?.content ?? ev.data?.text ?? "";
|
|
11636
|
+
if (typeof text === "string" && text) {
|
|
11637
|
+
const dlpMatch2 = scanArgs({ text });
|
|
11638
|
+
if (dlpMatch2) {
|
|
11639
|
+
const k = dlpKey(dlpMatch2.patternName, dlpMatch2.redactedSample, projLabel);
|
|
11640
|
+
if (!dedup.dlpKeys.has(k)) {
|
|
11641
|
+
dedup.dlpKeys.add(k);
|
|
11642
|
+
result.dlpFindings.push({
|
|
11643
|
+
patternName: dlpMatch2.patternName,
|
|
11644
|
+
redactedSample: dlpMatch2.redactedSample,
|
|
11645
|
+
toolName: "user-prompt",
|
|
11646
|
+
timestamp,
|
|
11647
|
+
project: projLabel,
|
|
11648
|
+
sessionId,
|
|
11649
|
+
agent: "copilot"
|
|
11650
|
+
});
|
|
11651
|
+
}
|
|
11652
|
+
}
|
|
11653
|
+
}
|
|
11654
|
+
continue;
|
|
11655
|
+
}
|
|
11656
|
+
if (ev.type !== "tool.execution_start") continue;
|
|
11657
|
+
const toolName = ev.data?.toolName ?? "";
|
|
11658
|
+
const toolNameLower = toolName.toLowerCase();
|
|
11659
|
+
const input = ev.data?.arguments ?? {};
|
|
11660
|
+
result.totalToolCalls++;
|
|
11661
|
+
sessionCalls.push({ toolName, input, timestamp });
|
|
11662
|
+
const isShellTool = toolNameLower === "bash" || toolNameLower === "shell";
|
|
11663
|
+
if (isShellTool) result.bashCalls++;
|
|
11664
|
+
if (timestamp) {
|
|
11665
|
+
if (!result.firstDate || timestamp < result.firstDate) result.firstDate = timestamp;
|
|
11666
|
+
if (!result.lastDate || timestamp > result.lastDate) result.lastDate = timestamp;
|
|
11667
|
+
}
|
|
11668
|
+
const rawCmd = String(input.command ?? "").trimStart();
|
|
11669
|
+
if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd)) continue;
|
|
11670
|
+
const dlpMatch = scanArgs(input);
|
|
11671
|
+
if (dlpMatch) {
|
|
11672
|
+
const k = dlpKey(dlpMatch.patternName, dlpMatch.redactedSample, projLabel);
|
|
11673
|
+
if (!dedup.dlpKeys.has(k)) {
|
|
11674
|
+
dedup.dlpKeys.add(k);
|
|
11675
|
+
result.dlpFindings.push({
|
|
11676
|
+
patternName: dlpMatch.patternName,
|
|
11677
|
+
redactedSample: dlpMatch.redactedSample,
|
|
11678
|
+
toolName,
|
|
11679
|
+
timestamp,
|
|
11680
|
+
project: projLabel,
|
|
11681
|
+
sessionId,
|
|
11682
|
+
agent: "copilot"
|
|
11683
|
+
});
|
|
11684
|
+
}
|
|
11685
|
+
}
|
|
11686
|
+
let astFsMatched = false;
|
|
11687
|
+
if (isShellTool) {
|
|
11688
|
+
astFsMatched = pushFsOpAstFinding(
|
|
11689
|
+
String(input.command ?? ""),
|
|
11690
|
+
toolName,
|
|
11691
|
+
input,
|
|
11692
|
+
timestamp,
|
|
11693
|
+
projLabel,
|
|
11694
|
+
sessionId,
|
|
11695
|
+
"copilot",
|
|
11696
|
+
result,
|
|
11697
|
+
dedup
|
|
11698
|
+
);
|
|
11699
|
+
}
|
|
11700
|
+
let ruleMatched = astFsMatched;
|
|
11701
|
+
for (const source of ruleSources) {
|
|
11702
|
+
const { rule } = source;
|
|
11703
|
+
if (rule.verdict === "allow") continue;
|
|
11704
|
+
if (rule.tool && !matchesPattern(toolNameLower, rule.tool)) continue;
|
|
11705
|
+
if (isShellTool && rule.name && AST_FS_REGEX_RULES.has(rule.name)) continue;
|
|
11706
|
+
if (!evaluateSmartConditions(input, rule)) continue;
|
|
11707
|
+
const inputPreview = preview(input, 120);
|
|
11708
|
+
const k = findingKey(rule.name, inputPreview, projLabel);
|
|
11709
|
+
if (!dedup.findingsKeys.has(k)) {
|
|
11710
|
+
dedup.findingsKeys.add(k);
|
|
11711
|
+
result.findings.push({
|
|
11712
|
+
source,
|
|
11713
|
+
toolName,
|
|
11714
|
+
input,
|
|
11715
|
+
timestamp,
|
|
11716
|
+
project: projLabel,
|
|
11717
|
+
sessionId,
|
|
11718
|
+
agent: "copilot"
|
|
11719
|
+
});
|
|
11720
|
+
}
|
|
11721
|
+
ruleMatched = true;
|
|
11722
|
+
break;
|
|
11723
|
+
}
|
|
11724
|
+
if (!ruleMatched && isShellTool) {
|
|
11725
|
+
const shellVerdict = detectDangerousShellExec(String(input.command ?? ""));
|
|
11726
|
+
if (shellVerdict) {
|
|
11727
|
+
const astRule = {
|
|
11728
|
+
name: `ast:bash-safe:${shellVerdict}-shell-exec-remote`,
|
|
11729
|
+
tool: "bash",
|
|
11730
|
+
conditions: [],
|
|
11731
|
+
verdict: shellVerdict,
|
|
11732
|
+
reason: `Shell execution of remote download detected by AST analysis (bash-safe)`
|
|
11733
|
+
};
|
|
11734
|
+
const inputPreview = preview(input, 120);
|
|
11735
|
+
const k = findingKey(astRule.name, inputPreview, projLabel);
|
|
11736
|
+
if (!dedup.findingsKeys.has(k)) {
|
|
11737
|
+
dedup.findingsKeys.add(k);
|
|
11738
|
+
result.findings.push({
|
|
11739
|
+
source: {
|
|
11740
|
+
shieldName: "bash-safe",
|
|
11741
|
+
shieldLabel: "bash-safe (AST)",
|
|
11742
|
+
sourceType: "shield",
|
|
11743
|
+
rule: astRule
|
|
11744
|
+
},
|
|
11745
|
+
toolName,
|
|
11746
|
+
input,
|
|
11747
|
+
timestamp,
|
|
11748
|
+
project: projLabel,
|
|
11749
|
+
sessionId,
|
|
11750
|
+
agent: "copilot"
|
|
11751
|
+
});
|
|
11752
|
+
}
|
|
11753
|
+
}
|
|
11754
|
+
}
|
|
10644
11755
|
}
|
|
11756
|
+
result.loopFindings.push(...detectLoops(sessionCalls, projLabel, sessionId, "copilot"));
|
|
10645
11757
|
}
|
|
10646
11758
|
return result;
|
|
10647
11759
|
}
|
|
@@ -10940,8 +12052,8 @@ function printFindingRow(f, drillDown, showSessionId, previewWidth) {
|
|
|
10940
12052
|
const stale = isStaleFinding(f.timestamp);
|
|
10941
12053
|
const ts = f.timestamp ? import_chalk5.default.dim(fmtTs(f.timestamp) + " ") : "";
|
|
10942
12054
|
const proj = import_chalk5.default.dim(f.project.slice(0, 22).padEnd(22) + " ");
|
|
10943
|
-
const agentLabel2 = f.agent
|
|
10944
|
-
const agentBadge = stale ? import_chalk5.default.dim(agentLabel2) :
|
|
12055
|
+
const agentLabel2 = agentBadgeText(f.agent);
|
|
12056
|
+
const agentBadge = stale ? import_chalk5.default.dim(agentLabel2) : import_chalk5.default[agentColorName(f.agent)](agentLabel2);
|
|
10945
12057
|
let cmdText;
|
|
10946
12058
|
if (drillDown) {
|
|
10947
12059
|
cmdText = f.fullCommand;
|
|
@@ -11504,16 +12616,35 @@ function registerScanCommand(program2) {
|
|
|
11504
12616
|
(done) => onProgress(claudeScan.filesScanned + done),
|
|
11505
12617
|
onLine
|
|
11506
12618
|
);
|
|
11507
|
-
const
|
|
12619
|
+
const antigravityScan = scanAntigravityHistory(
|
|
11508
12620
|
startDate,
|
|
11509
12621
|
(done) => onProgress(claudeScan.filesScanned + geminiScan.filesScanned + done),
|
|
11510
12622
|
onLine
|
|
11511
12623
|
);
|
|
11512
|
-
const
|
|
12624
|
+
const copilotScan = scanCopilotHistory(
|
|
12625
|
+
startDate,
|
|
12626
|
+
(done) => onProgress(
|
|
12627
|
+
claudeScan.filesScanned + geminiScan.filesScanned + antigravityScan.filesScanned + done
|
|
12628
|
+
),
|
|
12629
|
+
onLine
|
|
12630
|
+
);
|
|
12631
|
+
const codexScan = scanCodexHistory(
|
|
12632
|
+
startDate,
|
|
12633
|
+
(done) => onProgress(
|
|
12634
|
+
claudeScan.filesScanned + geminiScan.filesScanned + antigravityScan.filesScanned + copilotScan.filesScanned + done
|
|
12635
|
+
),
|
|
12636
|
+
onLine
|
|
12637
|
+
);
|
|
12638
|
+
const scan = mergeScans(
|
|
12639
|
+
mergeScans(mergeScans(mergeScans(claudeScan, geminiScan), antigravityScan), copilotScan),
|
|
12640
|
+
codexScan
|
|
12641
|
+
);
|
|
11513
12642
|
scan.dlpFindings.push(...scanShellConfig());
|
|
11514
12643
|
const summary = buildScanSummary([
|
|
11515
12644
|
{ id: "claude", label: "Claude", icon: "\u{1F916}", scan: claudeScan },
|
|
11516
12645
|
{ id: "gemini", label: "Gemini", icon: "\u264A", scan: geminiScan },
|
|
12646
|
+
{ id: "antigravity", label: "Antigravity", icon: "\u{1F680}", scan: antigravityScan },
|
|
12647
|
+
{ id: "copilot", label: "Copilot", icon: "\u{1F419}", scan: copilotScan },
|
|
11517
12648
|
{ id: "codex", label: "Codex", icon: "\u{1F52E}", scan: codexScan }
|
|
11518
12649
|
]);
|
|
11519
12650
|
if (useTTY) process.stdout.write("\r" + " ".repeat(60) + "\r");
|
|
@@ -11521,7 +12652,7 @@ function registerScanCommand(program2) {
|
|
|
11521
12652
|
console.log(import_chalk5.default.yellow(" No session history found."));
|
|
11522
12653
|
console.log(
|
|
11523
12654
|
import_chalk5.default.gray(
|
|
11524
|
-
" Supported: Claude Code (~/.claude/projects/) \xB7 Gemini CLI (~/.gemini/tmp/)\n"
|
|
12655
|
+
" Supported: Claude Code (~/.claude/projects/) \xB7 Gemini CLI (~/.gemini/tmp/) \xB7 Antigravity (~/.gemini/antigravity-*/brain/) \xB7 Copilot CLI (~/.copilot/session-state/)\n"
|
|
11525
12656
|
)
|
|
11526
12657
|
);
|
|
11527
12658
|
return;
|
|
@@ -11533,6 +12664,12 @@ function registerScanCommand(program2) {
|
|
|
11533
12664
|
breakdownParts.push(import_chalk5.default.cyan(String(claudeScan.sessions)) + import_chalk5.default.dim(" Claude"));
|
|
11534
12665
|
if (geminiScan.sessions > 0)
|
|
11535
12666
|
breakdownParts.push(import_chalk5.default.blue(String(geminiScan.sessions)) + import_chalk5.default.dim(" Gemini"));
|
|
12667
|
+
if (antigravityScan.sessions > 0)
|
|
12668
|
+
breakdownParts.push(
|
|
12669
|
+
import_chalk5.default.yellow(String(antigravityScan.sessions)) + import_chalk5.default.dim(" Antigravity")
|
|
12670
|
+
);
|
|
12671
|
+
if (copilotScan.sessions > 0)
|
|
12672
|
+
breakdownParts.push(import_chalk5.default.green(String(copilotScan.sessions)) + import_chalk5.default.dim(" Copilot"));
|
|
11536
12673
|
if (codexScan.sessions > 0)
|
|
11537
12674
|
breakdownParts.push(import_chalk5.default.magenta(String(codexScan.sessions)) + import_chalk5.default.dim(" Codex"));
|
|
11538
12675
|
const sessionBreakdown = breakdownParts.length > 1 ? import_chalk5.default.dim("(") + breakdownParts.join(import_chalk5.default.dim(" \xB7 ")) + import_chalk5.default.dim(")") : "";
|
|
@@ -11721,7 +12858,7 @@ function registerScanCommand(program2) {
|
|
|
11721
12858
|
const stale = isStaleFinding(f.timestamp);
|
|
11722
12859
|
const ts = f.timestamp ? import_chalk5.default.dim(fmtTs(f.timestamp) + " ") : "";
|
|
11723
12860
|
const proj = import_chalk5.default.dim(f.project.slice(0, 22).padEnd(22) + " ");
|
|
11724
|
-
const agentBadge =
|
|
12861
|
+
const agentBadge = import_chalk5.default[agentColorName(f.agent)](agentBadgeText(f.agent));
|
|
11725
12862
|
const sessionSuffix = f.sessionId ? import_chalk5.default.dim(` \u2192 ${f.sessionId.slice(0, 8)}`) : "";
|
|
11726
12863
|
const recurringBadge = recurringPatterns.has(f.patternName) ? import_chalk5.default.red.bold(" \u26A0\uFE0F recurring ") : "";
|
|
11727
12864
|
const patternDisplay = stale ? import_chalk5.default.dim(f.patternName) : import_chalk5.default.yellow(f.patternName);
|
|
@@ -11769,8 +12906,8 @@ function registerScanCommand(program2) {
|
|
|
11769
12906
|
const stale = isStaleFinding(f.timestamp);
|
|
11770
12907
|
const ts = f.timestamp ? import_chalk5.default.dim(fmtTs(f.timestamp) + " ") : "";
|
|
11771
12908
|
const proj = import_chalk5.default.dim(f.project.slice(0, 22).padEnd(22) + " ");
|
|
11772
|
-
const agentLabel2 = f.agent
|
|
11773
|
-
const agentBadge = stale ? import_chalk5.default.dim(agentLabel2) :
|
|
12909
|
+
const agentLabel2 = agentBadgeText(f.agent);
|
|
12910
|
+
const agentBadge = stale ? import_chalk5.default.dim(agentLabel2) : import_chalk5.default[agentColorName(f.agent)](agentLabel2);
|
|
11774
12911
|
const toolDisplay = stale ? import_chalk5.default.dim(f.toolName) : import_chalk5.default.yellow(f.toolName);
|
|
11775
12912
|
const cmdDisplay = stale ? import_chalk5.default.dim(f.commandPreview) : import_chalk5.default.gray(f.commandPreview);
|
|
11776
12913
|
const sessionSuffix = f.sessionId ? import_chalk5.default.dim(` \u2192 ${f.sessionId.slice(0, 8)}`) : "";
|
|
@@ -11908,6 +13045,7 @@ var init_scan = __esm({
|
|
|
11908
13045
|
init_policy();
|
|
11909
13046
|
init_dist();
|
|
11910
13047
|
init_dlp();
|
|
13048
|
+
init_hook_payload();
|
|
11911
13049
|
init_dist();
|
|
11912
13050
|
init_scan_summary();
|
|
11913
13051
|
init_setup();
|
|
@@ -11987,6 +13125,8 @@ var init_scan = __esm({
|
|
|
11987
13125
|
"exec_command",
|
|
11988
13126
|
"shell",
|
|
11989
13127
|
"run_shell_command",
|
|
13128
|
+
"run_command",
|
|
13129
|
+
// Antigravity (agy)
|
|
11990
13130
|
"write",
|
|
11991
13131
|
"edit",
|
|
11992
13132
|
"multiedit"
|
|
@@ -16575,6 +17715,7 @@ function resolveUserSkillRoot(entry, cwd) {
|
|
|
16575
17715
|
// src/cli/commands/check.ts
|
|
16576
17716
|
init_dlp();
|
|
16577
17717
|
init_audit();
|
|
17718
|
+
init_hook_payload();
|
|
16578
17719
|
function sanitize2(value) {
|
|
16579
17720
|
return value.replace(/[\x00-\x1F\x7F]/g, "");
|
|
16580
17721
|
}
|
|
@@ -16587,15 +17728,27 @@ function detectAiAgent(payload) {
|
|
|
16587
17728
|
if (payload.turn_id !== void 0) {
|
|
16588
17729
|
return "Codex";
|
|
16589
17730
|
}
|
|
17731
|
+
if (payload.toolCall !== void 0 || payload.conversationId !== void 0) {
|
|
17732
|
+
return "Antigravity";
|
|
17733
|
+
}
|
|
16590
17734
|
if (payload.hook_event_name === "PreToolUse" || payload.hook_event_name === "PostToolUse" || payload.tool_use_id !== void 0 || payload.permission_mode !== void 0) {
|
|
16591
17735
|
return "Claude Code";
|
|
16592
17736
|
}
|
|
16593
17737
|
if (payload.hook_event_name === "BeforeTool" || payload.hook_event_name === "AfterTool" || payload.timestamp !== void 0) {
|
|
16594
17738
|
return "Gemini CLI";
|
|
16595
17739
|
}
|
|
17740
|
+
if (payload.hook_event_name === "pre_tool_call" || payload.hook_event_name === "post_tool_call") {
|
|
17741
|
+
return "Hermes";
|
|
17742
|
+
}
|
|
16596
17743
|
if (process.env.CLAUDECODE === "1" || process.env.CLAUDE_CODE_SESSION_ID) {
|
|
16597
17744
|
return "Claude Code";
|
|
16598
17745
|
}
|
|
17746
|
+
if (process.env.HERMES_SESSION_ID || process.env.HERMES_HOME || process.env.HERMES_INTERACTIVE) {
|
|
17747
|
+
return "Hermes";
|
|
17748
|
+
}
|
|
17749
|
+
if (process.env.ANTIGRAVITY_CONVERSATION_ID) {
|
|
17750
|
+
return "Antigravity";
|
|
17751
|
+
}
|
|
16599
17752
|
if (process.env.GEMINI_CLI_VERSION || process.env.GEMINI_API_KEY) {
|
|
16600
17753
|
return "Gemini CLI";
|
|
16601
17754
|
}
|
|
@@ -16611,7 +17764,11 @@ function detectAiAgent(payload) {
|
|
|
16611
17764
|
return "Terminal";
|
|
16612
17765
|
}
|
|
16613
17766
|
function registerCheckCommand(program2) {
|
|
16614
|
-
program2.command("check", { hidden: true }).description("Hook handler \u2014 evaluates a tool call before execution").argument("[data]", "JSON string of the tool call").
|
|
17767
|
+
program2.command("check", { hidden: true }).description("Hook handler \u2014 evaluates a tool call before execution").argument("[data]", "JSON string of the tool call").option(
|
|
17768
|
+
"--agent <name>",
|
|
17769
|
+
"Agent identity override, set by node9-authored hook registrations (e.g. antigravity)"
|
|
17770
|
+
).action(async (data, opts) => {
|
|
17771
|
+
const agentOverride = agentLabelFromFlag(opts?.agent);
|
|
16615
17772
|
const processPayload = async (raw) => {
|
|
16616
17773
|
try {
|
|
16617
17774
|
if (!raw || raw.trim() === "") process.exit(0);
|
|
@@ -16651,7 +17808,7 @@ RAW: ${raw}
|
|
|
16651
17808
|
if (!prompt) process.exit(0);
|
|
16652
17809
|
const dlpMatch = scanArgs({ prompt });
|
|
16653
17810
|
if (!dlpMatch) process.exit(0);
|
|
16654
|
-
const agent2 = detectAiAgent(payload);
|
|
17811
|
+
const agent2 = agentOverride ?? detectAiAgent(payload);
|
|
16655
17812
|
const sessionId2 = typeof payload.session_id === "string" ? payload.session_id : void 0;
|
|
16656
17813
|
appendLocalAudit(
|
|
16657
17814
|
"UserPromptSubmit",
|
|
@@ -16692,7 +17849,8 @@ RAW: ${raw}
|
|
|
16692
17849
|
);
|
|
16693
17850
|
process.exit(2);
|
|
16694
17851
|
}
|
|
16695
|
-
const
|
|
17852
|
+
const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
|
|
17853
|
+
const safeCwdForConfig = typeof payloadCwd === "string" && import_path33.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
16696
17854
|
const config = getConfig(safeCwdForConfig);
|
|
16697
17855
|
if (config.settings.autoStartDaemon && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON) {
|
|
16698
17856
|
try {
|
|
@@ -16742,9 +17900,10 @@ RAW: ${raw}
|
|
|
16742
17900
|
import_fs32.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
|
|
16743
17901
|
`);
|
|
16744
17902
|
}
|
|
16745
|
-
const
|
|
16746
|
-
const
|
|
16747
|
-
const
|
|
17903
|
+
const rawToolName = sanitize2(extractToolName(payload));
|
|
17904
|
+
const toolName = canonicalToolName(rawToolName);
|
|
17905
|
+
const toolInput = canonicalToolInput(rawToolName, extractToolInput(payload));
|
|
17906
|
+
const agent = agentOverride ?? detectAiAgent(payload);
|
|
16748
17907
|
const mcpMatch = toolName.match(/^mcp__([^_](?:[^_]|_(?!_))*?)__/i);
|
|
16749
17908
|
const mcpServer = mcpMatch?.[1];
|
|
16750
17909
|
const sendBlock = (msg, result2) => {
|
|
@@ -16782,6 +17941,21 @@ RAW: ${raw}
|
|
|
16782
17941
|
msg,
|
|
16783
17942
|
result2?.recoveryCommand
|
|
16784
17943
|
);
|
|
17944
|
+
if (agent === "Antigravity") {
|
|
17945
|
+
process.stdout.write(
|
|
17946
|
+
JSON.stringify({ decision: "deny", reason: aiFeedbackMessage }) + "\n"
|
|
17947
|
+
);
|
|
17948
|
+
process.exit(0);
|
|
17949
|
+
}
|
|
17950
|
+
if (agent === "GitHub Copilot") {
|
|
17951
|
+
process.stdout.write(
|
|
17952
|
+
JSON.stringify({
|
|
17953
|
+
permissionDecision: "deny",
|
|
17954
|
+
permissionDecisionReason: aiFeedbackMessage
|
|
17955
|
+
}) + "\n"
|
|
17956
|
+
);
|
|
17957
|
+
process.exit(0);
|
|
17958
|
+
}
|
|
16785
17959
|
process.stdout.write(
|
|
16786
17960
|
JSON.stringify({
|
|
16787
17961
|
decision: "block",
|
|
@@ -16800,11 +17974,11 @@ RAW: ${raw}
|
|
|
16800
17974
|
sendBlock("Node9: unrecognised hook payload \u2014 tool name missing.");
|
|
16801
17975
|
return;
|
|
16802
17976
|
}
|
|
16803
|
-
const sessionId = typeof payload.session_id === "string" ? payload.session_id : void 0;
|
|
16804
|
-
const transcriptPath = typeof payload.transcript_path === "string" ? payload.transcript_path : void 0;
|
|
17977
|
+
const sessionId = typeof payload.session_id === "string" ? payload.session_id : typeof payload.conversationId === "string" ? payload.conversationId : void 0;
|
|
17978
|
+
const transcriptPath = typeof payload.transcript_path === "string" ? payload.transcript_path : typeof payload.transcriptPath === "string" ? payload.transcriptPath : void 0;
|
|
16805
17979
|
const meta = { agent, mcpServer, sessionId, transcriptPath };
|
|
16806
17980
|
const skillPinCfg = config.policy.skillPinning;
|
|
16807
|
-
const rawSessionId =
|
|
17981
|
+
const rawSessionId = sessionId ?? "";
|
|
16808
17982
|
const safeSessionId = /^[A-Za-z0-9_\-]{1,128}$/.test(rawSessionId) ? rawSessionId : "";
|
|
16809
17983
|
if (skillPinCfg.enabled && safeSessionId) {
|
|
16810
17984
|
try {
|
|
@@ -16861,7 +18035,7 @@ RAW: ${raw}
|
|
|
16861
18035
|
return;
|
|
16862
18036
|
}
|
|
16863
18037
|
if (!flag || flag.state !== "verified" && flag.state !== "warned") {
|
|
16864
|
-
const absoluteCwd = typeof
|
|
18038
|
+
const absoluteCwd = typeof payloadCwd === "string" && import_path33.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
16865
18039
|
const extraRoots = skillPinCfg.roots;
|
|
16866
18040
|
const resolvedExtra = extraRoots.map((r) => resolveUserSkillRoot(r, absoluteCwd)).filter((r) => typeof r === "string");
|
|
16867
18041
|
const roots = [...defaultSkillRoots(absoluteCwd), ...resolvedExtra];
|
|
@@ -16927,7 +18101,7 @@ RAW: ${raw}
|
|
|
16927
18101
|
if (shouldSnapshot(toolName, toolInput, config)) {
|
|
16928
18102
|
await createShadowSnapshot(toolName, toolInput, config.policy.snapshot.ignorePaths);
|
|
16929
18103
|
}
|
|
16930
|
-
const safeCwdForAuth = typeof
|
|
18104
|
+
const safeCwdForAuth = typeof payloadCwd === "string" && import_path33.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
16931
18105
|
const result = await authorizeHeadless(toolName, toolInput, meta, {
|
|
16932
18106
|
cwd: safeCwdForAuth
|
|
16933
18107
|
});
|
|
@@ -17052,6 +18226,7 @@ function containsShellMetachar(token) {
|
|
|
17052
18226
|
}
|
|
17053
18227
|
|
|
17054
18228
|
// src/cli/commands/log.ts
|
|
18229
|
+
init_hook_payload();
|
|
17055
18230
|
var TEST_COMMAND_RE2 = /(?:^|\s)(npm\s+(?:run\s+)?test|npx\s+(?:vitest|jest|mocha)|yarn\s+(?:run\s+)?test|pnpm\s+(?:run\s+)?test|vitest|jest|mocha|pytest|py\.test|cargo\s+test|go\s+test|bundle\s+exec\s+rspec|rspec|phpunit|dotnet\s+test)\b/i;
|
|
17056
18231
|
function detectTestResult(command, output) {
|
|
17057
18232
|
if (!TEST_COMMAND_RE2.test(command)) return null;
|
|
@@ -17070,13 +18245,19 @@ function sanitize3(value) {
|
|
|
17070
18245
|
return value.replace(/[\x00-\x1F\x7F]/g, "");
|
|
17071
18246
|
}
|
|
17072
18247
|
function registerLogCommand(program2) {
|
|
17073
|
-
program2.command("log", { hidden: true }).description("PostToolUse hook \u2014 records executed tool calls").argument("[data]", "JSON string of the tool call").
|
|
18248
|
+
program2.command("log", { hidden: true }).description("PostToolUse hook \u2014 records executed tool calls").argument("[data]", "JSON string of the tool call").option(
|
|
18249
|
+
"--agent <name>",
|
|
18250
|
+
"Agent identity override, set by node9-authored hook registrations (e.g. antigravity)"
|
|
18251
|
+
).action(async (data, opts) => {
|
|
18252
|
+
const agentOverride = agentLabelFromFlag(opts?.agent);
|
|
17074
18253
|
const logPayload = async (raw) => {
|
|
17075
18254
|
try {
|
|
17076
18255
|
if (!raw || raw.trim() === "") process.exit(0);
|
|
17077
18256
|
const payload = JSON.parse(raw);
|
|
17078
|
-
|
|
17079
|
-
const
|
|
18257
|
+
if (payload.toolCall === null) process.exit(0);
|
|
18258
|
+
const rawToolName = sanitize3(extractToolName(payload, "unknown"));
|
|
18259
|
+
const tool = canonicalToolName(rawToolName);
|
|
18260
|
+
const rawInput = canonicalToolInput(rawToolName, extractToolInput(payload));
|
|
17080
18261
|
const metaTag = (() => {
|
|
17081
18262
|
const m = payload.meta;
|
|
17082
18263
|
if (m && typeof m === "object") {
|
|
@@ -17085,7 +18266,7 @@ function registerLogCommand(program2) {
|
|
|
17085
18266
|
}
|
|
17086
18267
|
return void 0;
|
|
17087
18268
|
})();
|
|
17088
|
-
const agent = metaTag !== void 0 ? metaTag : payload.turn_id !== void 0 ? "Codex" : payload.hook_event_name === "PreToolUse" || payload.hook_event_name === "PostToolUse" || payload.tool_use_id !== void 0 || payload.permission_mode !== void 0 ? "Claude Code" : payload.hook_event_name === "BeforeTool" || payload.hook_event_name === "AfterTool" || payload.timestamp !== void 0 ? "Gemini CLI" : void 0;
|
|
18269
|
+
const agent = agentOverride !== void 0 ? agentOverride : metaTag !== void 0 ? metaTag : payload.turn_id !== void 0 ? "Codex" : payload.toolCall !== void 0 || payload.conversationId !== void 0 ? "Antigravity" : payload.hook_event_name === "pre_tool_call" || payload.hook_event_name === "post_tool_call" ? "Hermes" : payload.hook_event_name === "PreToolUse" || payload.hook_event_name === "PostToolUse" || payload.tool_use_id !== void 0 || payload.permission_mode !== void 0 ? "Claude Code" : payload.hook_event_name === "BeforeTool" || payload.hook_event_name === "AfterTool" || payload.timestamp !== void 0 ? "Gemini CLI" : process.env.HERMES_SESSION_ID || process.env.HERMES_HOME || process.env.HERMES_INTERACTIVE ? "Hermes" : process.env.ANTIGRAVITY_CONVERSATION_ID ? "Antigravity" : void 0;
|
|
17089
18270
|
const entry = {
|
|
17090
18271
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17091
18272
|
tool,
|
|
@@ -17094,7 +18275,9 @@ function registerLogCommand(program2) {
|
|
|
17094
18275
|
source: "post-hook"
|
|
17095
18276
|
};
|
|
17096
18277
|
if (agent) entry.agent = agent;
|
|
17097
|
-
if (
|
|
18278
|
+
if (rawToolName !== tool) entry.agentToolName = rawToolName;
|
|
18279
|
+
const payloadSessionId = payload.session_id ?? payload.conversationId;
|
|
18280
|
+
if (payloadSessionId) entry.sessionId = payloadSessionId;
|
|
17098
18281
|
const logPath = import_path34.default.join(import_os29.default.homedir(), ".node9", "audit.log");
|
|
17099
18282
|
if (!import_fs33.default.existsSync(import_path34.default.dirname(logPath)))
|
|
17100
18283
|
import_fs33.default.mkdirSync(import_path34.default.dirname(logPath), { recursive: true });
|
|
@@ -17131,7 +18314,8 @@ function registerLogCommand(program2) {
|
|
|
17131
18314
|
}
|
|
17132
18315
|
}
|
|
17133
18316
|
}
|
|
17134
|
-
const
|
|
18317
|
+
const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
|
|
18318
|
+
const safeCwd = typeof payloadCwd === "string" && import_path34.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
17135
18319
|
const config = getConfig(safeCwd);
|
|
17136
18320
|
if ((tool === "Bash" || tool === "bash") && config.settings.enableUndo !== false) {
|
|
17137
18321
|
const bashCommand = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
|
|
@@ -19219,12 +20403,12 @@ function registerInitCommand(program2) {
|
|
|
19219
20403
|
if (found.length === 0) {
|
|
19220
20404
|
console.log(
|
|
19221
20405
|
import_chalk16.default.gray(
|
|
19222
|
-
"No AI agents detected. Install one of the supported agents (Claude Code, Codex, Gemini CLI, Cursor, Windsurf, VSCode, Claude Desktop, Opencode, or
|
|
20406
|
+
"No AI agents detected. Install one of the supported agents (Claude Code, Codex, Antigravity, Gemini CLI, GitHub Copilot CLI, Cursor, Windsurf, VSCode, Claude Desktop, Opencode, Pi, or Hermes Agent)."
|
|
19223
20407
|
)
|
|
19224
20408
|
);
|
|
19225
20409
|
console.log(
|
|
19226
20410
|
import_chalk16.default.gray(
|
|
19227
|
-
"then run: node9 agents add <claude|codex|gemini|cursor|windsurf|vscode|claudeDesktop|opencode|pi>"
|
|
20411
|
+
"then run: node9 agents add <claude|codex|antigravity|gemini|copilot|cursor|windsurf|vscode|claudeDesktop|opencode|pi|hermes>"
|
|
19228
20412
|
)
|
|
19229
20413
|
);
|
|
19230
20414
|
return;
|
|
@@ -19238,6 +20422,8 @@ function registerInitCommand(program2) {
|
|
|
19238
20422
|
console.log(import_chalk16.default.bold(`Wiring ${agent}...`));
|
|
19239
20423
|
if (agent === "claude") await setupClaude();
|
|
19240
20424
|
else if (agent === "gemini") await setupGemini();
|
|
20425
|
+
else if (agent === "antigravity") await setupAntigravity();
|
|
20426
|
+
else if (agent === "copilot") await setupCopilot();
|
|
19241
20427
|
else if (agent === "cursor") await setupCursor();
|
|
19242
20428
|
else if (agent === "codex") await setupCodex();
|
|
19243
20429
|
else if (agent === "windsurf") await setupWindsurf();
|
|
@@ -19245,6 +20431,7 @@ function registerInitCommand(program2) {
|
|
|
19245
20431
|
else if (agent === "claudeDesktop") await setupClaudeDesktop();
|
|
19246
20432
|
else if (agent === "opencode") await setupOpencode();
|
|
19247
20433
|
else if (agent === "pi") await setupPi();
|
|
20434
|
+
else if (agent === "hermes") setupHermes();
|
|
19248
20435
|
console.log("");
|
|
19249
20436
|
}
|
|
19250
20437
|
if ((process.platform === "darwin" || process.platform === "linux") && process.stdout.isTTY) {
|
|
@@ -21000,26 +22187,36 @@ init_setup();
|
|
|
21000
22187
|
var SETUP_FN = {
|
|
21001
22188
|
claude: setupClaude,
|
|
21002
22189
|
gemini: setupGemini,
|
|
22190
|
+
antigravity: setupAntigravity,
|
|
22191
|
+
copilot: setupCopilot,
|
|
21003
22192
|
cursor: setupCursor,
|
|
21004
22193
|
codex: setupCodex,
|
|
21005
22194
|
windsurf: setupWindsurf,
|
|
21006
22195
|
vscode: setupVSCode,
|
|
21007
22196
|
claudeDesktop: setupClaudeDesktop,
|
|
21008
22197
|
opencode: setupOpencode,
|
|
21009
|
-
pi: setupPi
|
|
22198
|
+
pi: setupPi,
|
|
22199
|
+
hermes: setupHermes
|
|
21010
22200
|
};
|
|
21011
22201
|
var TEARDOWN_FN = {
|
|
21012
22202
|
claude: teardownClaude,
|
|
21013
22203
|
gemini: teardownGemini,
|
|
22204
|
+
antigravity: teardownAntigravity,
|
|
22205
|
+
copilot: teardownCopilot,
|
|
21014
22206
|
cursor: teardownCursor,
|
|
21015
22207
|
codex: teardownCodex,
|
|
21016
22208
|
windsurf: teardownWindsurf,
|
|
21017
22209
|
vscode: teardownVSCode,
|
|
21018
22210
|
claudeDesktop: teardownClaudeDesktop,
|
|
21019
22211
|
opencode: teardownOpencode,
|
|
21020
|
-
pi: teardownPi
|
|
22212
|
+
pi: teardownPi,
|
|
22213
|
+
hermes: teardownHermes
|
|
21021
22214
|
};
|
|
21022
22215
|
var AGENT_NAMES = Object.keys(SETUP_FN);
|
|
22216
|
+
function resolveAgentName(agent) {
|
|
22217
|
+
const lower = agent.toLowerCase();
|
|
22218
|
+
return lower === "agy" ? "antigravity" : lower;
|
|
22219
|
+
}
|
|
21023
22220
|
function registerAgentsCommand(program2) {
|
|
21024
22221
|
const agents = program2.command("agents").description("List and manage AI agent integrations");
|
|
21025
22222
|
agents.command("list").description("Show all supported agents and their Node9 status").action(() => {
|
|
@@ -21048,9 +22245,16 @@ function registerAgentsCommand(program2) {
|
|
|
21048
22245
|
import_chalk23.default.yellow(` ${unwired.length} agent(s) not yet wired. Run: `) + import_chalk23.default.white(`node9 agents add ${unwired[0].name}`) + "\n"
|
|
21049
22246
|
);
|
|
21050
22247
|
}
|
|
22248
|
+
if (statuses.some((s) => s.name === "gemini" && s.installed)) {
|
|
22249
|
+
console.log(
|
|
22250
|
+
import_chalk23.default.yellow(
|
|
22251
|
+
" \u26A0\uFE0F Gemini CLI stops serving AI Pro/Ultra and free tiers on 2026-06-18.\n"
|
|
22252
|
+
) + import_chalk23.default.gray(" Migrate to Antigravity: ") + import_chalk23.default.white("node9 agents add antigravity") + "\n"
|
|
22253
|
+
);
|
|
22254
|
+
}
|
|
21051
22255
|
});
|
|
21052
22256
|
agents.command("add").description("Wire Node9 into an agent").argument("<agent>", `Agent to wire: ${AGENT_NAMES.join(" | ")}`).action(async (agent) => {
|
|
21053
|
-
const name = agent
|
|
22257
|
+
const name = resolveAgentName(agent);
|
|
21054
22258
|
const fn = SETUP_FN[name];
|
|
21055
22259
|
if (!fn) {
|
|
21056
22260
|
console.error(import_chalk23.default.red(`Unknown agent: "${agent}". Supported: ${AGENT_NAMES.join(", ")}`));
|
|
@@ -21059,7 +22263,7 @@ function registerAgentsCommand(program2) {
|
|
|
21059
22263
|
await fn();
|
|
21060
22264
|
});
|
|
21061
22265
|
agents.command("remove").description("Remove Node9 from an agent").argument("<agent>", `Agent to unwire: ${AGENT_NAMES.join(" | ")}`).action((agent) => {
|
|
21062
|
-
const name = agent
|
|
22266
|
+
const name = resolveAgentName(agent);
|
|
21063
22267
|
const fn = TEARDOWN_FN[name];
|
|
21064
22268
|
if (!fn) {
|
|
21065
22269
|
console.error(import_chalk23.default.red(`Unknown agent: "${agent}". Supported: ${AGENT_NAMES.join(", ")}`));
|
|
@@ -21081,6 +22285,7 @@ var import_chalk24 = __toESM(require("chalk"));
|
|
|
21081
22285
|
var import_fs41 = __toESM(require("fs"));
|
|
21082
22286
|
var import_path42 = __toESM(require("path"));
|
|
21083
22287
|
var import_os36 = __toESM(require("os"));
|
|
22288
|
+
init_scan_summary();
|
|
21084
22289
|
var CLAUDE_PRICING3 = {
|
|
21085
22290
|
"claude-opus-4-6": { i: 5e-6, o: 25e-6, cw: 625e-8, cr: 5e-7 },
|
|
21086
22291
|
"claude-opus-4-5": { i: 5e-6, o: 25e-6, cw: 625e-8, cr: 5e-7 },
|
|
@@ -21687,7 +22892,9 @@ function renderList(summaries, totalCost) {
|
|
|
21687
22892
|
const cost = s.costUSD > 0 ? import_chalk24.default.dim(" " + fmtCost3(s.costUSD).padEnd(8)) : " ";
|
|
21688
22893
|
const blocked = s.blockedCalls.length > 0 ? import_chalk24.default.red(" \u{1F6D1} " + String(s.blockedCalls.length)) : "";
|
|
21689
22894
|
const snap = s.hasSnapshot ? import_chalk24.default.green(" \u{1F4F8}") : "";
|
|
21690
|
-
const agentBadge =
|
|
22895
|
+
const agentBadge = import_chalk24.default[agentColorName(s.agent ?? "claude")](
|
|
22896
|
+
" " + agentBadgeText(s.agent ?? "claude", 0)
|
|
22897
|
+
);
|
|
21691
22898
|
const sid = import_chalk24.default.dim(" " + s.sessionId.slice(0, 8));
|
|
21692
22899
|
console.log(
|
|
21693
22900
|
` ${timeStr} ${prompt} ${tools}${cost}${blocked}${snap}${agentBadge}${sid}${dateRange}`
|
|
@@ -21707,7 +22914,7 @@ function renderDetail(s) {
|
|
|
21707
22914
|
);
|
|
21708
22915
|
console.log(import_chalk24.default.bold(" Project ") + import_chalk24.default.white(s.projectLabel));
|
|
21709
22916
|
if (s.agent) {
|
|
21710
|
-
const agentLabel2 =
|
|
22917
|
+
const agentLabel2 = import_chalk24.default[agentColorName(s.agent)](agentDisplayName(s.agent));
|
|
21711
22918
|
console.log(import_chalk24.default.bold(" Agent ") + agentLabel2);
|
|
21712
22919
|
}
|
|
21713
22920
|
console.log(import_chalk24.default.bold(" When ") + import_chalk24.default.white(fmtDateTime(s.startTime)));
|
|
@@ -22344,31 +23551,34 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
22344
23551
|
});
|
|
22345
23552
|
program.command("addto", { hidden: true }).description("Integrate Node9 with an AI agent").addHelpText(
|
|
22346
23553
|
"after",
|
|
22347
|
-
"\n Supported targets: claude gemini cursor codex windsurf vscode hud"
|
|
23554
|
+
"\n Supported targets: claude antigravity copilot gemini cursor codex windsurf vscode hud"
|
|
22348
23555
|
).argument(
|
|
22349
23556
|
"<target>",
|
|
22350
|
-
"The agent to protect: claude | gemini | cursor | codex | windsurf | vscode | hud"
|
|
23557
|
+
"The agent to protect: claude | antigravity | copilot | gemini | cursor | codex | windsurf | vscode | hud"
|
|
22351
23558
|
).action(async (target) => {
|
|
22352
23559
|
if (target === "gemini") return await setupGemini();
|
|
23560
|
+
if (target === "antigravity" || target === "agy") return await setupAntigravity();
|
|
23561
|
+
if (target === "copilot") return await setupCopilot();
|
|
22353
23562
|
if (target === "claude") return await setupClaude();
|
|
22354
23563
|
if (target === "cursor") return await setupCursor();
|
|
22355
23564
|
if (target === "codex") return await setupCodex();
|
|
22356
23565
|
if (target === "windsurf") return await setupWindsurf();
|
|
22357
23566
|
if (target === "vscode") return await setupVSCode();
|
|
23567
|
+
if (target === "hermes") return setupHermes();
|
|
22358
23568
|
if (target === "hud") return setupHud();
|
|
22359
23569
|
console.error(
|
|
22360
23570
|
import_chalk30.default.red(
|
|
22361
|
-
`Unknown target: "${target}". Supported: claude, gemini, cursor, codex, windsurf, vscode, hud`
|
|
23571
|
+
`Unknown target: "${target}". Supported: claude, antigravity, copilot, gemini, cursor, codex, windsurf, vscode, hermes, hud`
|
|
22362
23572
|
)
|
|
22363
23573
|
);
|
|
22364
23574
|
process.exit(1);
|
|
22365
23575
|
});
|
|
22366
23576
|
program.command("setup", { hidden: true }).description('Alias for "addto" \u2014 integrate Node9 with an AI agent').addHelpText(
|
|
22367
23577
|
"after",
|
|
22368
|
-
"\n Supported targets: claude gemini cursor codex windsurf vscode hud"
|
|
23578
|
+
"\n Supported targets: claude antigravity copilot gemini cursor codex windsurf vscode hud"
|
|
22369
23579
|
).argument(
|
|
22370
23580
|
"[target]",
|
|
22371
|
-
"The agent to protect: claude | gemini | cursor | codex | windsurf | vscode | hud"
|
|
23581
|
+
"The agent to protect: claude | antigravity | copilot | gemini | cursor | codex | windsurf | vscode | hud"
|
|
22372
23582
|
).action(async (target) => {
|
|
22373
23583
|
if (!target) {
|
|
22374
23584
|
console.log(import_chalk30.default.cyan("\n\u{1F6E1}\uFE0F Node9 Setup \u2014 integrate with your AI agent\n"));
|
|
@@ -22376,10 +23586,13 @@ program.command("setup", { hidden: true }).description('Alias for "addto" \u2014
|
|
|
22376
23586
|
console.log(" Targets:");
|
|
22377
23587
|
console.log(" " + import_chalk30.default.green("claude") + " \u2014 Claude Code (hook mode)");
|
|
22378
23588
|
console.log(" " + import_chalk30.default.green("gemini") + " \u2014 Gemini CLI (hook mode)");
|
|
23589
|
+
console.log(" " + import_chalk30.default.green("antigravity") + " \u2014 Antigravity / agy (hook mode)");
|
|
23590
|
+
console.log(" " + import_chalk30.default.green("copilot") + " \u2014 GitHub Copilot CLI (hook mode)");
|
|
22379
23591
|
console.log(" " + import_chalk30.default.green("cursor") + " \u2014 Cursor (MCP proxy)");
|
|
22380
23592
|
console.log(" " + import_chalk30.default.green("codex") + " \u2014 OpenAI Codex CLI (MCP proxy)");
|
|
22381
23593
|
console.log(" " + import_chalk30.default.green("windsurf") + " \u2014 Windsurf (MCP proxy)");
|
|
22382
23594
|
console.log(" " + import_chalk30.default.green("vscode") + " \u2014 VSCode / Copilot (MCP proxy)");
|
|
23595
|
+
console.log(" " + import_chalk30.default.green("hermes") + " \u2014 Hermes Agent (hook mode)");
|
|
22383
23596
|
process.stdout.write(
|
|
22384
23597
|
" " + import_chalk30.default.green("hud") + " \u2014 Claude Code security statusline\n"
|
|
22385
23598
|
);
|
|
@@ -22388,38 +23601,44 @@ program.command("setup", { hidden: true }).description('Alias for "addto" \u2014
|
|
|
22388
23601
|
}
|
|
22389
23602
|
const t = target.toLowerCase();
|
|
22390
23603
|
if (t === "gemini") return await setupGemini();
|
|
23604
|
+
if (t === "antigravity" || t === "agy") return await setupAntigravity();
|
|
23605
|
+
if (t === "copilot") return await setupCopilot();
|
|
22391
23606
|
if (t === "claude") return await setupClaude();
|
|
22392
23607
|
if (t === "cursor") return await setupCursor();
|
|
22393
23608
|
if (t === "codex") return await setupCodex();
|
|
22394
23609
|
if (t === "windsurf") return await setupWindsurf();
|
|
22395
23610
|
if (t === "vscode") return await setupVSCode();
|
|
23611
|
+
if (t === "hermes") return setupHermes();
|
|
22396
23612
|
if (t === "hud") return setupHud();
|
|
22397
23613
|
console.error(
|
|
22398
23614
|
import_chalk30.default.red(
|
|
22399
|
-
`Unknown target: "${target}". Supported: claude, gemini, cursor, codex, windsurf, vscode, hud`
|
|
23615
|
+
`Unknown target: "${target}". Supported: claude, antigravity, copilot, gemini, cursor, codex, windsurf, vscode, hermes, hud`
|
|
22400
23616
|
)
|
|
22401
23617
|
);
|
|
22402
23618
|
process.exit(1);
|
|
22403
23619
|
});
|
|
22404
23620
|
program.command("removefrom", { hidden: true }).description("Remove Node9 hooks from an AI agent configuration").addHelpText(
|
|
22405
23621
|
"after",
|
|
22406
|
-
"\n Supported targets: claude gemini cursor codex windsurf vscode hud"
|
|
23622
|
+
"\n Supported targets: claude antigravity copilot gemini cursor codex windsurf vscode hud"
|
|
22407
23623
|
).argument(
|
|
22408
23624
|
"<target>",
|
|
22409
|
-
"The agent to remove from: claude | gemini | cursor | codex | windsurf | vscode | hud"
|
|
23625
|
+
"The agent to remove from: claude | antigravity | copilot | gemini | cursor | codex | windsurf | vscode | hud"
|
|
22410
23626
|
).action((target) => {
|
|
22411
23627
|
let fn;
|
|
22412
23628
|
if (target === "claude") fn = teardownClaude;
|
|
22413
23629
|
else if (target === "gemini") fn = teardownGemini;
|
|
23630
|
+
else if (target === "antigravity" || target === "agy") fn = teardownAntigravity;
|
|
23631
|
+
else if (target === "copilot") fn = teardownCopilot;
|
|
22414
23632
|
else if (target === "cursor") fn = teardownCursor;
|
|
22415
23633
|
else if (target === "codex") fn = teardownCodex;
|
|
22416
23634
|
else if (target === "windsurf") fn = teardownWindsurf;
|
|
22417
23635
|
else if (target === "vscode") fn = teardownVSCode;
|
|
23636
|
+
else if (target === "hermes") fn = teardownHermes;
|
|
22418
23637
|
else if (target === "hud") fn = teardownHud;
|
|
22419
23638
|
else {
|
|
22420
23639
|
console.error(
|
|
22421
23640
|
import_chalk30.default.red(
|
|
22422
|
-
`Unknown target: "${target}". Supported: claude, gemini, cursor, codex, windsurf, vscode, hud`
|
|
23641
|
+
`Unknown target: "${target}". Supported: claude, antigravity, copilot, gemini, cursor, codex, windsurf, vscode, hermes, hud`
|
|
22423
23642
|
)
|
|
22424
23643
|
);
|
|
22425
23644
|
process.exit(1);
|
|
@@ -22452,7 +23671,8 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
22452
23671
|
["Cursor", teardownCursor],
|
|
22453
23672
|
["Codex", teardownCodex],
|
|
22454
23673
|
["Windsurf", teardownWindsurf],
|
|
22455
|
-
["VSCode", teardownVSCode]
|
|
23674
|
+
["VSCode", teardownVSCode],
|
|
23675
|
+
["Hermes", teardownHermes]
|
|
22456
23676
|
]) {
|
|
22457
23677
|
try {
|
|
22458
23678
|
fn();
|
|
@@ -22675,6 +23895,9 @@ program.command("resume").description("Re-enable Node9 protection immediately").
|
|
|
22675
23895
|
var HOOK_BASED_AGENTS = {
|
|
22676
23896
|
claude: "claude",
|
|
22677
23897
|
gemini: "gemini",
|
|
23898
|
+
antigravity: "antigravity",
|
|
23899
|
+
agy: "antigravity",
|
|
23900
|
+
copilot: "copilot",
|
|
22678
23901
|
cursor: "cursor"
|
|
22679
23902
|
};
|
|
22680
23903
|
program.argument("[command...]", "The agent command to run (e.g., gemini)").action(async (commandArgs) => {
|