@node9/proxy 1.28.0 → 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 +1094 -118
- package/dist/cli.mjs +1094 -118
- package/dist/dashboard.mjs +10 -0
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -7172,6 +7172,12 @@ async function setupClaude() {
|
|
|
7172
7172
|
}
|
|
7173
7173
|
}
|
|
7174
7174
|
async function setupGemini() {
|
|
7175
|
+
console.log(
|
|
7176
|
+
chalk.yellow(
|
|
7177
|
+
" \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"
|
|
7178
|
+
)
|
|
7179
|
+
);
|
|
7180
|
+
console.log("");
|
|
7175
7181
|
seedMcpPinsIfMissing();
|
|
7176
7182
|
const homeDir2 = os12.homedir();
|
|
7177
7183
|
const settingsPath = path15.join(homeDir2, ".gemini", "settings.json");
|
|
@@ -7269,6 +7275,355 @@ async function setupGemini() {
|
|
|
7269
7275
|
printDaemonTip();
|
|
7270
7276
|
}
|
|
7271
7277
|
}
|
|
7278
|
+
async function setupAntigravity() {
|
|
7279
|
+
seedMcpPinsIfMissing();
|
|
7280
|
+
const homeDir2 = os12.homedir();
|
|
7281
|
+
const hooksPath = path15.join(homeDir2, ".gemini", "config", "hooks.json");
|
|
7282
|
+
const mcpPath = path15.join(homeDir2, ".gemini", "config", "mcp_config.json");
|
|
7283
|
+
const hooksFile = readJson(hooksPath) ?? {};
|
|
7284
|
+
const mcpConfig = readJson(mcpPath) ?? {};
|
|
7285
|
+
const servers = mcpConfig.mcpServers ?? {};
|
|
7286
|
+
let hooksChanged = false;
|
|
7287
|
+
let anythingChanged = false;
|
|
7288
|
+
if (!hooksFile.hooks) hooksFile.hooks = {};
|
|
7289
|
+
const hasPreHook = hooksFile.hooks.PreToolUse?.some(
|
|
7290
|
+
(m) => m.hooks.some((h) => isNode9Hook(h.command))
|
|
7291
|
+
);
|
|
7292
|
+
if (!hasPreHook) {
|
|
7293
|
+
if (!Array.isArray(hooksFile.hooks.PreToolUse)) hooksFile.hooks.PreToolUse = [];
|
|
7294
|
+
hooksFile.hooks.PreToolUse.push({
|
|
7295
|
+
matcher: ".*",
|
|
7296
|
+
hooks: [
|
|
7297
|
+
{
|
|
7298
|
+
name: "node9-check",
|
|
7299
|
+
type: "command",
|
|
7300
|
+
command: fullPathCommand("check --agent antigravity"),
|
|
7301
|
+
timeout: 600
|
|
7302
|
+
}
|
|
7303
|
+
]
|
|
7304
|
+
});
|
|
7305
|
+
console.log(chalk.green(" \u2705 PreToolUse hook added \u2192 node9 check"));
|
|
7306
|
+
hooksChanged = true;
|
|
7307
|
+
anythingChanged = true;
|
|
7308
|
+
} else if (hooksFile.hooks.PreToolUse) {
|
|
7309
|
+
for (const matcher of hooksFile.hooks.PreToolUse) {
|
|
7310
|
+
for (const h of matcher.hooks) {
|
|
7311
|
+
const cmd = h.command ?? "";
|
|
7312
|
+
if (isNode9Hook(cmd) && needsRewrite(cmd)) {
|
|
7313
|
+
h.command = fullPathCommand("check --agent antigravity");
|
|
7314
|
+
console.log(chalk.yellow(" \u{1F527} PreToolUse hook repaired (stale path \u2192 current binary)"));
|
|
7315
|
+
hooksChanged = true;
|
|
7316
|
+
anythingChanged = true;
|
|
7317
|
+
}
|
|
7318
|
+
}
|
|
7319
|
+
}
|
|
7320
|
+
}
|
|
7321
|
+
const hasPostHook = hooksFile.hooks.PostToolUse?.some(
|
|
7322
|
+
(m) => m.hooks.some((h) => isNode9Hook(h.command))
|
|
7323
|
+
);
|
|
7324
|
+
if (!hasPostHook) {
|
|
7325
|
+
if (!Array.isArray(hooksFile.hooks.PostToolUse)) hooksFile.hooks.PostToolUse = [];
|
|
7326
|
+
hooksFile.hooks.PostToolUse.push({
|
|
7327
|
+
matcher: ".*",
|
|
7328
|
+
hooks: [
|
|
7329
|
+
{
|
|
7330
|
+
name: "node9-log",
|
|
7331
|
+
type: "command",
|
|
7332
|
+
command: fullPathCommand("log --agent antigravity"),
|
|
7333
|
+
timeout: 600
|
|
7334
|
+
}
|
|
7335
|
+
]
|
|
7336
|
+
});
|
|
7337
|
+
console.log(chalk.green(" \u2705 PostToolUse hook added \u2192 node9 log"));
|
|
7338
|
+
hooksChanged = true;
|
|
7339
|
+
anythingChanged = true;
|
|
7340
|
+
} else if (hooksFile.hooks.PostToolUse) {
|
|
7341
|
+
for (const matcher of hooksFile.hooks.PostToolUse) {
|
|
7342
|
+
for (const h of matcher.hooks) {
|
|
7343
|
+
const cmd = h.command ?? "";
|
|
7344
|
+
if (isNode9Hook(cmd) && needsRewrite(cmd)) {
|
|
7345
|
+
h.command = fullPathCommand("log --agent antigravity");
|
|
7346
|
+
console.log(chalk.yellow(" \u{1F527} PostToolUse hook repaired (stale path \u2192 current binary)"));
|
|
7347
|
+
hooksChanged = true;
|
|
7348
|
+
anythingChanged = true;
|
|
7349
|
+
}
|
|
7350
|
+
}
|
|
7351
|
+
}
|
|
7352
|
+
}
|
|
7353
|
+
if (hooksChanged) {
|
|
7354
|
+
writeJson(hooksPath, hooksFile);
|
|
7355
|
+
console.log("");
|
|
7356
|
+
}
|
|
7357
|
+
if (!hasNode9McpServer(servers)) {
|
|
7358
|
+
servers["node9"] = NODE9_MCP_SERVER_ENTRY;
|
|
7359
|
+
mcpConfig.mcpServers = servers;
|
|
7360
|
+
writeJson(mcpPath, mcpConfig);
|
|
7361
|
+
console.log(chalk.green(" \u2705 node9 MCP server added \u2192 node9 mcp-server"));
|
|
7362
|
+
anythingChanged = true;
|
|
7363
|
+
}
|
|
7364
|
+
const legacySettings = readJson(path15.join(homeDir2, ".gemini", "settings.json"));
|
|
7365
|
+
const legacyHasNode9 = ["BeforeTool", "AfterTool"].some(
|
|
7366
|
+
(ev) => legacySettings?.hooks?.[ev]?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)))
|
|
7367
|
+
);
|
|
7368
|
+
if (legacyHasNode9) {
|
|
7369
|
+
console.log(
|
|
7370
|
+
chalk.yellow(
|
|
7371
|
+
" \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)."
|
|
7372
|
+
)
|
|
7373
|
+
);
|
|
7374
|
+
const clean = await confirm({
|
|
7375
|
+
message: "Remove the legacy Gemini CLI hooks?",
|
|
7376
|
+
default: false
|
|
7377
|
+
});
|
|
7378
|
+
if (clean) {
|
|
7379
|
+
teardownGemini();
|
|
7380
|
+
anythingChanged = true;
|
|
7381
|
+
}
|
|
7382
|
+
console.log("");
|
|
7383
|
+
}
|
|
7384
|
+
const serversToWrap = [];
|
|
7385
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
7386
|
+
if (!server.command || server.command === "node9") continue;
|
|
7387
|
+
serversToWrap.push({ name, upstream: [server.command, ...server.args ?? []].join(" ") });
|
|
7388
|
+
}
|
|
7389
|
+
if (serversToWrap.length > 0) {
|
|
7390
|
+
console.log(chalk.bold("The following existing entries will be modified:\n"));
|
|
7391
|
+
console.log(chalk.white(` ${mcpPath}`));
|
|
7392
|
+
for (const { name, upstream } of serversToWrap) {
|
|
7393
|
+
console.log(chalk.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
|
|
7394
|
+
}
|
|
7395
|
+
console.log("");
|
|
7396
|
+
const proceed = await confirm({ message: "Wrap these MCP servers?", default: true });
|
|
7397
|
+
if (proceed) {
|
|
7398
|
+
for (const { name, upstream } of serversToWrap) {
|
|
7399
|
+
servers[name] = {
|
|
7400
|
+
...servers[name],
|
|
7401
|
+
command: "node9",
|
|
7402
|
+
args: ["mcp", "--upstream", upstream]
|
|
7403
|
+
};
|
|
7404
|
+
}
|
|
7405
|
+
mcpConfig.mcpServers = servers;
|
|
7406
|
+
writeJson(mcpPath, mcpConfig);
|
|
7407
|
+
console.log(chalk.green(`
|
|
7408
|
+
\u2705 ${serversToWrap.length} MCP server(s) wrapped`));
|
|
7409
|
+
anythingChanged = true;
|
|
7410
|
+
} else {
|
|
7411
|
+
console.log(chalk.yellow(" Skipped MCP server wrapping."));
|
|
7412
|
+
}
|
|
7413
|
+
console.log("");
|
|
7414
|
+
}
|
|
7415
|
+
if (!anythingChanged && serversToWrap.length === 0) {
|
|
7416
|
+
console.log(chalk.blue("\u2139\uFE0F Node9 is already fully configured for Antigravity."));
|
|
7417
|
+
printDaemonTip();
|
|
7418
|
+
return;
|
|
7419
|
+
}
|
|
7420
|
+
if (anythingChanged) {
|
|
7421
|
+
console.log(chalk.green.bold("\u{1F6E1}\uFE0F Node9 is now protecting Antigravity!"));
|
|
7422
|
+
console.log(
|
|
7423
|
+
chalk.gray(
|
|
7424
|
+
" Covers both the agy CLI and the Antigravity IDE (shared ~/.gemini/config).\n Restart agy / the IDE for changes to take effect."
|
|
7425
|
+
)
|
|
7426
|
+
);
|
|
7427
|
+
printDaemonTip();
|
|
7428
|
+
}
|
|
7429
|
+
}
|
|
7430
|
+
function teardownAntigravity() {
|
|
7431
|
+
const homeDir2 = os12.homedir();
|
|
7432
|
+
const hooksPath = path15.join(homeDir2, ".gemini", "config", "hooks.json");
|
|
7433
|
+
const mcpPath = path15.join(homeDir2, ".gemini", "config", "mcp_config.json");
|
|
7434
|
+
let changed = false;
|
|
7435
|
+
const hooksFile = readJson(hooksPath);
|
|
7436
|
+
if (hooksFile?.hooks) {
|
|
7437
|
+
for (const event of ["PreToolUse", "PostToolUse"]) {
|
|
7438
|
+
const before = hooksFile.hooks[event]?.length ?? 0;
|
|
7439
|
+
hooksFile.hooks[event] = hooksFile.hooks[event]?.filter(
|
|
7440
|
+
(m) => !m.hooks.some((h) => isNode9Hook(h.command))
|
|
7441
|
+
);
|
|
7442
|
+
if ((hooksFile.hooks[event]?.length ?? 0) < before) changed = true;
|
|
7443
|
+
if (hooksFile.hooks[event]?.length === 0) delete hooksFile.hooks[event];
|
|
7444
|
+
}
|
|
7445
|
+
if (changed) {
|
|
7446
|
+
writeJson(hooksPath, hooksFile);
|
|
7447
|
+
console.log(
|
|
7448
|
+
chalk.green(" \u2705 Removed PreToolUse / PostToolUse hooks from ~/.gemini/config/hooks.json")
|
|
7449
|
+
);
|
|
7450
|
+
} else {
|
|
7451
|
+
console.log(chalk.blue(" \u2139\uFE0F No Node9 hooks found in ~/.gemini/config/hooks.json"));
|
|
7452
|
+
}
|
|
7453
|
+
} else {
|
|
7454
|
+
console.log(chalk.blue(" \u2139\uFE0F ~/.gemini/config/hooks.json not found \u2014 nothing to remove"));
|
|
7455
|
+
}
|
|
7456
|
+
const mcpConfig = readJson(mcpPath);
|
|
7457
|
+
if (mcpConfig?.mcpServers) {
|
|
7458
|
+
let mcpChanged = false;
|
|
7459
|
+
if (removeNode9McpServer(mcpConfig.mcpServers)) {
|
|
7460
|
+
mcpChanged = true;
|
|
7461
|
+
console.log(
|
|
7462
|
+
chalk.green(" \u2705 Removed node9 MCP server entry from ~/.gemini/config/mcp_config.json")
|
|
7463
|
+
);
|
|
7464
|
+
}
|
|
7465
|
+
for (const [name, server] of Object.entries(mcpConfig.mcpServers)) {
|
|
7466
|
+
const args = server.args;
|
|
7467
|
+
if (server.command === "node9" && Array.isArray(args) && args[0] === "mcp" && args[1] === "--upstream" && typeof args[2] === "string") {
|
|
7468
|
+
const [originalCmd, ...originalArgs] = args[2].split(" ");
|
|
7469
|
+
mcpConfig.mcpServers[name] = {
|
|
7470
|
+
...server,
|
|
7471
|
+
command: originalCmd,
|
|
7472
|
+
args: originalArgs.length ? originalArgs : void 0
|
|
7473
|
+
};
|
|
7474
|
+
mcpChanged = true;
|
|
7475
|
+
}
|
|
7476
|
+
}
|
|
7477
|
+
if (mcpChanged) {
|
|
7478
|
+
writeJson(mcpPath, mcpConfig);
|
|
7479
|
+
console.log(chalk.green(" \u2705 Unwrapped MCP servers in ~/.gemini/config/mcp_config.json"));
|
|
7480
|
+
}
|
|
7481
|
+
}
|
|
7482
|
+
}
|
|
7483
|
+
async function setupCopilot() {
|
|
7484
|
+
seedMcpPinsIfMissing();
|
|
7485
|
+
const homeDir2 = os12.homedir();
|
|
7486
|
+
const hooksPath = path15.join(homeDir2, ".copilot", "hooks", "node9.json");
|
|
7487
|
+
const mcpPath = path15.join(homeDir2, ".copilot", "mcp-config.json");
|
|
7488
|
+
const hooksFile = readJson(hooksPath) ?? { version: 1 };
|
|
7489
|
+
if (!hooksFile.version) hooksFile.version = 1;
|
|
7490
|
+
if (!hooksFile.hooks) hooksFile.hooks = {};
|
|
7491
|
+
const mcpConfig = readJson(mcpPath) ?? {};
|
|
7492
|
+
const servers = mcpConfig.mcpServers ?? {};
|
|
7493
|
+
let hooksChanged = false;
|
|
7494
|
+
let anythingChanged = false;
|
|
7495
|
+
const addHook = (event, subcommand) => {
|
|
7496
|
+
const arr = Array.isArray(hooksFile.hooks[event]) ? hooksFile.hooks[event] : [];
|
|
7497
|
+
const present = arr.some((h) => isNode9Hook(h.command));
|
|
7498
|
+
if (!present) {
|
|
7499
|
+
arr.push({ type: "command", command: fullPathCommand(subcommand), timeoutSec: 600 });
|
|
7500
|
+
hooksFile.hooks[event] = arr;
|
|
7501
|
+
console.log(chalk.green(` \u2705 ${event} hook added \u2192 node9 ${subcommand.split(" ")[0]}`));
|
|
7502
|
+
hooksChanged = true;
|
|
7503
|
+
anythingChanged = true;
|
|
7504
|
+
} else {
|
|
7505
|
+
for (const h of arr) {
|
|
7506
|
+
if (h.command && isNode9Hook(h.command) && needsRewrite(h.command)) {
|
|
7507
|
+
h.command = fullPathCommand(subcommand);
|
|
7508
|
+
console.log(chalk.yellow(` \u{1F527} ${event} hook repaired (stale path \u2192 current binary)`));
|
|
7509
|
+
hooksChanged = true;
|
|
7510
|
+
anythingChanged = true;
|
|
7511
|
+
}
|
|
7512
|
+
}
|
|
7513
|
+
}
|
|
7514
|
+
};
|
|
7515
|
+
addHook("PreToolUse", "check --agent copilot");
|
|
7516
|
+
addHook("PostToolUse", "log --agent copilot");
|
|
7517
|
+
addHook("UserPromptSubmit", "check --agent copilot");
|
|
7518
|
+
if (hooksChanged) {
|
|
7519
|
+
writeJson(hooksPath, hooksFile);
|
|
7520
|
+
console.log("");
|
|
7521
|
+
}
|
|
7522
|
+
if (!hasNode9McpServer(servers)) {
|
|
7523
|
+
servers["node9"] = NODE9_MCP_SERVER_ENTRY;
|
|
7524
|
+
mcpConfig.mcpServers = servers;
|
|
7525
|
+
writeJson(mcpPath, mcpConfig);
|
|
7526
|
+
console.log(chalk.green(" \u2705 node9 MCP server added \u2192 node9 mcp-server"));
|
|
7527
|
+
anythingChanged = true;
|
|
7528
|
+
}
|
|
7529
|
+
const serversToWrap = [];
|
|
7530
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
7531
|
+
if (!server.command || server.command === "node9") continue;
|
|
7532
|
+
serversToWrap.push({ name, upstream: [server.command, ...server.args ?? []].join(" ") });
|
|
7533
|
+
}
|
|
7534
|
+
if (serversToWrap.length > 0) {
|
|
7535
|
+
console.log(chalk.bold("The following existing entries will be modified:\n"));
|
|
7536
|
+
console.log(chalk.white(` ${mcpPath}`));
|
|
7537
|
+
for (const { name, upstream } of serversToWrap) {
|
|
7538
|
+
console.log(chalk.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
|
|
7539
|
+
}
|
|
7540
|
+
console.log("");
|
|
7541
|
+
const proceed = await confirm({ message: "Wrap these MCP servers?", default: true });
|
|
7542
|
+
if (proceed) {
|
|
7543
|
+
for (const { name, upstream } of serversToWrap) {
|
|
7544
|
+
servers[name] = {
|
|
7545
|
+
...servers[name],
|
|
7546
|
+
command: "node9",
|
|
7547
|
+
args: ["mcp", "--upstream", upstream]
|
|
7548
|
+
};
|
|
7549
|
+
}
|
|
7550
|
+
mcpConfig.mcpServers = servers;
|
|
7551
|
+
writeJson(mcpPath, mcpConfig);
|
|
7552
|
+
console.log(chalk.green(`
|
|
7553
|
+
\u2705 ${serversToWrap.length} MCP server(s) wrapped`));
|
|
7554
|
+
anythingChanged = true;
|
|
7555
|
+
} else {
|
|
7556
|
+
console.log(chalk.yellow(" Skipped MCP server wrapping."));
|
|
7557
|
+
}
|
|
7558
|
+
console.log("");
|
|
7559
|
+
}
|
|
7560
|
+
if (!anythingChanged) {
|
|
7561
|
+
console.log(chalk.blue("\u2139\uFE0F Node9 is already fully configured for GitHub Copilot CLI."));
|
|
7562
|
+
printDaemonTip();
|
|
7563
|
+
return;
|
|
7564
|
+
}
|
|
7565
|
+
console.log(chalk.green.bold("\u{1F6E1}\uFE0F Node9 is now protecting GitHub Copilot CLI!"));
|
|
7566
|
+
console.log(chalk.gray(" Restart Copilot CLI for changes to take effect."));
|
|
7567
|
+
printDaemonTip();
|
|
7568
|
+
}
|
|
7569
|
+
function teardownCopilot() {
|
|
7570
|
+
const homeDir2 = os12.homedir();
|
|
7571
|
+
const hooksPath = path15.join(homeDir2, ".copilot", "hooks", "node9.json");
|
|
7572
|
+
const mcpPath = path15.join(homeDir2, ".copilot", "mcp-config.json");
|
|
7573
|
+
const hooksFile = readJson(hooksPath);
|
|
7574
|
+
let changed = false;
|
|
7575
|
+
if (hooksFile?.hooks) {
|
|
7576
|
+
for (const event of ["PreToolUse", "PostToolUse", "UserPromptSubmit"]) {
|
|
7577
|
+
const before = hooksFile.hooks[event]?.length ?? 0;
|
|
7578
|
+
hooksFile.hooks[event] = hooksFile.hooks[event]?.filter((h) => !isNode9Hook(h.command));
|
|
7579
|
+
if ((hooksFile.hooks[event]?.length ?? 0) < before) changed = true;
|
|
7580
|
+
if (hooksFile.hooks[event]?.length === 0) delete hooksFile.hooks[event];
|
|
7581
|
+
}
|
|
7582
|
+
if (changed) {
|
|
7583
|
+
if (Object.keys(hooksFile.hooks).length === 0) {
|
|
7584
|
+
try {
|
|
7585
|
+
fs13.unlinkSync(hooksPath);
|
|
7586
|
+
console.log(chalk.green(" \u2705 Removed ~/.copilot/hooks/node9.json"));
|
|
7587
|
+
} catch {
|
|
7588
|
+
writeJson(hooksPath, hooksFile);
|
|
7589
|
+
}
|
|
7590
|
+
} else {
|
|
7591
|
+
writeJson(hooksPath, hooksFile);
|
|
7592
|
+
console.log(chalk.green(" \u2705 Removed Node9 hooks from ~/.copilot/hooks/node9.json"));
|
|
7593
|
+
}
|
|
7594
|
+
} else {
|
|
7595
|
+
console.log(chalk.blue(" \u2139\uFE0F No Node9 hooks found in ~/.copilot/hooks/node9.json"));
|
|
7596
|
+
}
|
|
7597
|
+
} else {
|
|
7598
|
+
console.log(chalk.blue(" \u2139\uFE0F ~/.copilot/hooks/node9.json not found \u2014 nothing to remove"));
|
|
7599
|
+
}
|
|
7600
|
+
const mcpConfig = readJson(mcpPath);
|
|
7601
|
+
if (mcpConfig?.mcpServers) {
|
|
7602
|
+
let mcpChanged = false;
|
|
7603
|
+
if (removeNode9McpServer(mcpConfig.mcpServers)) {
|
|
7604
|
+
mcpChanged = true;
|
|
7605
|
+
console.log(
|
|
7606
|
+
chalk.green(" \u2705 Removed node9 MCP server entry from ~/.copilot/mcp-config.json")
|
|
7607
|
+
);
|
|
7608
|
+
}
|
|
7609
|
+
for (const [name, server] of Object.entries(mcpConfig.mcpServers)) {
|
|
7610
|
+
const args = server.args;
|
|
7611
|
+
if (server.command === "node9" && Array.isArray(args) && args[0] === "mcp" && args[1] === "--upstream" && typeof args[2] === "string") {
|
|
7612
|
+
const [originalCmd, ...originalArgs] = args[2].split(" ");
|
|
7613
|
+
mcpConfig.mcpServers[name] = {
|
|
7614
|
+
...server,
|
|
7615
|
+
command: originalCmd,
|
|
7616
|
+
args: originalArgs.length ? originalArgs : void 0
|
|
7617
|
+
};
|
|
7618
|
+
mcpChanged = true;
|
|
7619
|
+
}
|
|
7620
|
+
}
|
|
7621
|
+
if (mcpChanged) {
|
|
7622
|
+
writeJson(mcpPath, mcpConfig);
|
|
7623
|
+
console.log(chalk.green(" \u2705 Unwrapped MCP servers in ~/.copilot/mcp-config.json"));
|
|
7624
|
+
}
|
|
7625
|
+
}
|
|
7626
|
+
}
|
|
7272
7627
|
function claudeDesktopConfigPath(homeDir2 = os12.homedir()) {
|
|
7273
7628
|
if (process.platform === "darwin") {
|
|
7274
7629
|
return path15.join(
|
|
@@ -7316,7 +7671,23 @@ function detectAgents(homeDir2 = os12.homedir()) {
|
|
|
7316
7671
|
const desktopPath = claudeDesktopConfigPath(homeDir2);
|
|
7317
7672
|
return {
|
|
7318
7673
|
claude: exists(path15.join(homeDir2, ".claude")) || exists(path15.join(homeDir2, ".claude.json")),
|
|
7319
|
-
|
|
7674
|
+
// Antigravity (agy) shares the ~/.gemini root, so a bare
|
|
7675
|
+
// `exists(~/.gemini)` would report the (EOL'd) Gemini CLI as
|
|
7676
|
+
// installed on every agy machine — and `node9 init` would then
|
|
7677
|
+
// write BeforeTool hooks into settings.json, a file agy ignores,
|
|
7678
|
+
// while reporting the machine protected (silent protection gap,
|
|
7679
|
+
// verified live on a machine with both installed). Gemini CLI
|
|
7680
|
+
// creates ~/.gemini/settings.json on first run; agy does not touch
|
|
7681
|
+
// it (it uses antigravity-cli/settings.json) — that file is the
|
|
7682
|
+
// legacy-CLI discriminator.
|
|
7683
|
+
gemini: exists(path15.join(homeDir2, ".gemini", "settings.json")) || binaryInPath("gemini"),
|
|
7684
|
+
// agy creates ~/.gemini/antigravity-cli/ on first launch; the IDE
|
|
7685
|
+
// creates antigravity-ide/. PATH fallback covers installed-but-
|
|
7686
|
+
// never-launched (same class as opencode #186).
|
|
7687
|
+
antigravity: exists(path15.join(homeDir2, ".gemini", "antigravity-cli")) || exists(path15.join(homeDir2, ".gemini", "antigravity-ide")) || binaryInPath("agy"),
|
|
7688
|
+
// GitHub Copilot CLI creates ~/.copilot on first launch; PATH
|
|
7689
|
+
// fallback covers installed-but-never-launched (same as opencode #186).
|
|
7690
|
+
copilot: exists(path15.join(homeDir2, ".copilot")) || binaryInPath("copilot"),
|
|
7320
7691
|
cursor: exists(path15.join(homeDir2, ".cursor")),
|
|
7321
7692
|
codex: exists(path15.join(homeDir2, ".codex")),
|
|
7322
7693
|
windsurf: exists(path15.join(homeDir2, ".codeium", "windsurf")),
|
|
@@ -8317,6 +8688,18 @@ function getAgentsStatus(homeDir2 = os12.homedir()) {
|
|
|
8317
8688
|
const settings = readJson(path15.join(homeDir2, ".gemini", "settings.json"));
|
|
8318
8689
|
return !!settings?.hooks?.BeforeTool?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)));
|
|
8319
8690
|
})();
|
|
8691
|
+
const antigravityWired = (() => {
|
|
8692
|
+
const hooksFile = readJson(
|
|
8693
|
+
path15.join(homeDir2, ".gemini", "config", "hooks.json")
|
|
8694
|
+
);
|
|
8695
|
+
return !!hooksFile?.hooks?.PreToolUse?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)));
|
|
8696
|
+
})();
|
|
8697
|
+
const copilotWired = (() => {
|
|
8698
|
+
const hooksFile = readJson(
|
|
8699
|
+
path15.join(homeDir2, ".copilot", "hooks", "node9.json")
|
|
8700
|
+
);
|
|
8701
|
+
return !!hooksFile?.hooks?.PreToolUse?.some((h) => isNode9Hook(h.command));
|
|
8702
|
+
})();
|
|
8320
8703
|
const cursorWired = (() => {
|
|
8321
8704
|
const cfg = readJson(path15.join(homeDir2, ".cursor", "mcp.json"));
|
|
8322
8705
|
return !!(cfg?.mcpServers && hasNode9McpServer(cfg.mcpServers));
|
|
@@ -8350,6 +8733,20 @@ function getAgentsStatus(homeDir2 = os12.homedir()) {
|
|
|
8350
8733
|
wired: geminiWired,
|
|
8351
8734
|
mode: detected.gemini ? "hooks" : null
|
|
8352
8735
|
},
|
|
8736
|
+
{
|
|
8737
|
+
name: "antigravity",
|
|
8738
|
+
label: "Antigravity",
|
|
8739
|
+
installed: detected.antigravity,
|
|
8740
|
+
wired: antigravityWired,
|
|
8741
|
+
mode: detected.antigravity ? "hooks" : null
|
|
8742
|
+
},
|
|
8743
|
+
{
|
|
8744
|
+
name: "copilot",
|
|
8745
|
+
label: "GitHub Copilot",
|
|
8746
|
+
installed: detected.copilot,
|
|
8747
|
+
wired: copilotWired,
|
|
8748
|
+
mode: detected.copilot ? "hooks" : null
|
|
8749
|
+
},
|
|
8353
8750
|
{
|
|
8354
8751
|
name: "cursor",
|
|
8355
8752
|
label: "Cursor",
|
|
@@ -8460,7 +8857,84 @@ var init_setup = __esm({
|
|
|
8460
8857
|
}
|
|
8461
8858
|
});
|
|
8462
8859
|
|
|
8860
|
+
// src/utils/hook-payload.ts
|
|
8861
|
+
function extractToolName(payload, defaultValue = "") {
|
|
8862
|
+
return payload.tool_name ?? payload.name ?? payload.toolCall?.name ?? defaultValue;
|
|
8863
|
+
}
|
|
8864
|
+
function extractToolInput(payload) {
|
|
8865
|
+
return payload.tool_input ?? payload.args ?? payload.toolCall?.args ?? {};
|
|
8866
|
+
}
|
|
8867
|
+
function canonicalToolName(name) {
|
|
8868
|
+
switch (name) {
|
|
8869
|
+
// Hermes Agent
|
|
8870
|
+
case "terminal":
|
|
8871
|
+
return "Bash";
|
|
8872
|
+
case "write_file":
|
|
8873
|
+
return "Write";
|
|
8874
|
+
case "patch":
|
|
8875
|
+
return "Edit";
|
|
8876
|
+
case "read_file":
|
|
8877
|
+
return "Read";
|
|
8878
|
+
case "search_files":
|
|
8879
|
+
return "Grep";
|
|
8880
|
+
// Antigravity (agy) — shell tool renamed from Gemini's run_shell_command
|
|
8881
|
+
case "run_command":
|
|
8882
|
+
return "Bash";
|
|
8883
|
+
default:
|
|
8884
|
+
return name;
|
|
8885
|
+
}
|
|
8886
|
+
}
|
|
8887
|
+
function agentLabelFromFlag(flag) {
|
|
8888
|
+
if (typeof flag !== "string") return void 0;
|
|
8889
|
+
switch (flag.toLowerCase()) {
|
|
8890
|
+
case "antigravity":
|
|
8891
|
+
case "agy":
|
|
8892
|
+
return "Antigravity";
|
|
8893
|
+
case "copilot":
|
|
8894
|
+
return "GitHub Copilot";
|
|
8895
|
+
default:
|
|
8896
|
+
return void 0;
|
|
8897
|
+
}
|
|
8898
|
+
}
|
|
8899
|
+
function canonicalToolInput(rawToolName, input) {
|
|
8900
|
+
if (rawToolName !== "run_command") return input;
|
|
8901
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) return input;
|
|
8902
|
+
const args = input;
|
|
8903
|
+
if (typeof args.CommandLine !== "string") return input;
|
|
8904
|
+
const { CommandLine, Cwd, ...rest } = args;
|
|
8905
|
+
const canonical = { ...rest, command: CommandLine };
|
|
8906
|
+
if (typeof Cwd === "string" && Cwd.length > 0) canonical.cwd = Cwd;
|
|
8907
|
+
return canonical;
|
|
8908
|
+
}
|
|
8909
|
+
var init_hook_payload = __esm({
|
|
8910
|
+
"src/utils/hook-payload.ts"() {
|
|
8911
|
+
"use strict";
|
|
8912
|
+
}
|
|
8913
|
+
});
|
|
8914
|
+
|
|
8463
8915
|
// src/scan-summary.ts
|
|
8916
|
+
function agentDisplayName(agent) {
|
|
8917
|
+
return AGENT_LONG[agent] ?? "Claude Code";
|
|
8918
|
+
}
|
|
8919
|
+
function agentBadgeText(agent, width = 10) {
|
|
8920
|
+
return `[${AGENT_SHORT[agent] ?? "Claude"}]`.padEnd(width);
|
|
8921
|
+
}
|
|
8922
|
+
function agentColorName(agent) {
|
|
8923
|
+
switch (agent) {
|
|
8924
|
+
case "gemini":
|
|
8925
|
+
return "blue";
|
|
8926
|
+
case "codex":
|
|
8927
|
+
return "magenta";
|
|
8928
|
+
case "antigravity":
|
|
8929
|
+
return "yellow";
|
|
8930
|
+
case "copilot":
|
|
8931
|
+
return "green";
|
|
8932
|
+
case "shell":
|
|
8933
|
+
return "yellow";
|
|
8934
|
+
default:
|
|
8935
|
+
return "cyan";
|
|
8936
|
+
}
|
|
8937
|
+
}
|
|
8464
8938
|
function buildScanSummary(agents) {
|
|
8465
8939
|
const stats = {
|
|
8466
8940
|
sessions: 0,
|
|
@@ -8637,12 +9111,29 @@ function fullCommandOf(input) {
|
|
|
8637
9111
|
const raw = input.command ?? input.query ?? input.file_path ?? JSON.stringify(input);
|
|
8638
9112
|
return String(raw).replace(/\s+/g, " ").trim();
|
|
8639
9113
|
}
|
|
9114
|
+
var AGENT_SHORT, AGENT_LONG;
|
|
8640
9115
|
var init_scan_summary = __esm({
|
|
8641
9116
|
"src/scan-summary.ts"() {
|
|
8642
9117
|
"use strict";
|
|
8643
9118
|
init_shields();
|
|
8644
9119
|
init_dist();
|
|
8645
9120
|
init_dist();
|
|
9121
|
+
AGENT_SHORT = {
|
|
9122
|
+
claude: "Claude",
|
|
9123
|
+
gemini: "Gemini",
|
|
9124
|
+
codex: "Codex",
|
|
9125
|
+
antigravity: "Agy",
|
|
9126
|
+
copilot: "Copilot",
|
|
9127
|
+
shell: "Shell"
|
|
9128
|
+
};
|
|
9129
|
+
AGENT_LONG = {
|
|
9130
|
+
claude: "Claude Code",
|
|
9131
|
+
gemini: "Gemini CLI",
|
|
9132
|
+
codex: "Codex",
|
|
9133
|
+
antigravity: "Antigravity",
|
|
9134
|
+
copilot: "GitHub Copilot",
|
|
9135
|
+
shell: "Shell"
|
|
9136
|
+
};
|
|
8646
9137
|
}
|
|
8647
9138
|
});
|
|
8648
9139
|
|
|
@@ -10287,6 +10778,34 @@ function countScanFiles() {
|
|
|
10287
10778
|
} catch {
|
|
10288
10779
|
}
|
|
10289
10780
|
}
|
|
10781
|
+
for (const surface of ["antigravity-cli", "antigravity-ide"]) {
|
|
10782
|
+
const brainDir = path22.join(os19.homedir(), ".gemini", surface, "brain");
|
|
10783
|
+
if (!fs20.existsSync(brainDir)) continue;
|
|
10784
|
+
try {
|
|
10785
|
+
for (const conv of fs20.readdirSync(brainDir)) {
|
|
10786
|
+
const convPath = path22.join(brainDir, conv);
|
|
10787
|
+
try {
|
|
10788
|
+
if (!fs20.statSync(convPath).isDirectory()) continue;
|
|
10789
|
+
const logsDir = path22.join(convPath, ".system_generated", "logs");
|
|
10790
|
+
if (fs20.existsSync(path22.join(logsDir, "transcript_full.jsonl")) || fs20.existsSync(path22.join(logsDir, "transcript.jsonl"))) {
|
|
10791
|
+
total += 1;
|
|
10792
|
+
}
|
|
10793
|
+
} catch {
|
|
10794
|
+
continue;
|
|
10795
|
+
}
|
|
10796
|
+
}
|
|
10797
|
+
} catch {
|
|
10798
|
+
}
|
|
10799
|
+
}
|
|
10800
|
+
const copilotDir = path22.join(os19.homedir(), ".copilot", "session-state");
|
|
10801
|
+
if (fs20.existsSync(copilotDir)) {
|
|
10802
|
+
try {
|
|
10803
|
+
for (const sid of fs20.readdirSync(copilotDir)) {
|
|
10804
|
+
if (fs20.existsSync(path22.join(copilotDir, sid, "events.jsonl"))) total += 1;
|
|
10805
|
+
}
|
|
10806
|
+
} catch {
|
|
10807
|
+
}
|
|
10808
|
+
}
|
|
10290
10809
|
const codexDir = path22.join(os19.homedir(), ".codex", "sessions");
|
|
10291
10810
|
if (fs20.existsSync(codexDir)) {
|
|
10292
10811
|
try {
|
|
@@ -10657,33 +11176,248 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
10657
11176
|
if (!fs20.existsSync(chatsDir)) continue;
|
|
10658
11177
|
let chatFiles;
|
|
10659
11178
|
try {
|
|
10660
|
-
chatFiles = fs20.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
|
|
11179
|
+
chatFiles = fs20.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
|
|
11180
|
+
} catch {
|
|
11181
|
+
continue;
|
|
11182
|
+
}
|
|
11183
|
+
for (const chatFile of chatFiles) {
|
|
11184
|
+
result.filesScanned++;
|
|
11185
|
+
onProgress?.(result.filesScanned);
|
|
11186
|
+
const sessionId = chatFile.replace(/\.json$/, "");
|
|
11187
|
+
let raw;
|
|
11188
|
+
try {
|
|
11189
|
+
raw = fs20.readFileSync(path22.join(chatsDir, chatFile), "utf-8");
|
|
11190
|
+
} catch {
|
|
11191
|
+
continue;
|
|
11192
|
+
}
|
|
11193
|
+
const sessionCalls = [];
|
|
11194
|
+
let session;
|
|
11195
|
+
try {
|
|
11196
|
+
session = JSON.parse(raw);
|
|
11197
|
+
} catch {
|
|
11198
|
+
continue;
|
|
11199
|
+
}
|
|
11200
|
+
result.sessions++;
|
|
11201
|
+
for (const msg of session.messages ?? []) {
|
|
11202
|
+
onLine?.();
|
|
11203
|
+
if (msg.type === "user") {
|
|
11204
|
+
const content = msg.content;
|
|
11205
|
+
const text = Array.isArray(content) ? content.map((c) => c.text ?? "").join("\n") : typeof content === "string" ? content : "";
|
|
11206
|
+
if (text) {
|
|
11207
|
+
const dlpMatch = scanArgs({ text });
|
|
11208
|
+
if (dlpMatch) {
|
|
11209
|
+
const k = dlpKey(dlpMatch.patternName, dlpMatch.redactedSample, projLabel);
|
|
11210
|
+
if (!dedup.dlpKeys.has(k)) {
|
|
11211
|
+
dedup.dlpKeys.add(k);
|
|
11212
|
+
result.dlpFindings.push({
|
|
11213
|
+
patternName: dlpMatch.patternName,
|
|
11214
|
+
redactedSample: dlpMatch.redactedSample,
|
|
11215
|
+
toolName: "user-prompt",
|
|
11216
|
+
timestamp: msg.timestamp ?? "",
|
|
11217
|
+
project: projLabel,
|
|
11218
|
+
sessionId,
|
|
11219
|
+
agent: "gemini"
|
|
11220
|
+
});
|
|
11221
|
+
}
|
|
11222
|
+
}
|
|
11223
|
+
}
|
|
11224
|
+
continue;
|
|
11225
|
+
}
|
|
11226
|
+
if (msg.type !== "gemini") continue;
|
|
11227
|
+
if (startDate && msg.timestamp && new Date(msg.timestamp) < startDate) continue;
|
|
11228
|
+
if (msg.timestamp) {
|
|
11229
|
+
if (!result.firstDate || msg.timestamp < result.firstDate)
|
|
11230
|
+
result.firstDate = msg.timestamp;
|
|
11231
|
+
if (!result.lastDate || msg.timestamp > result.lastDate) result.lastDate = msg.timestamp;
|
|
11232
|
+
}
|
|
11233
|
+
const tokens = msg.tokens;
|
|
11234
|
+
const model = msg.model;
|
|
11235
|
+
if (tokens && model) {
|
|
11236
|
+
const p = geminiModelPrice(model);
|
|
11237
|
+
if (p) {
|
|
11238
|
+
const nonCached = Math.max(0, tokens.input - tokens.cached);
|
|
11239
|
+
result.totalCostUSD += nonCached * p.i + tokens.cached * p.cr + tokens.output * p.o;
|
|
11240
|
+
}
|
|
11241
|
+
}
|
|
11242
|
+
for (const tc of msg.toolCalls ?? []) {
|
|
11243
|
+
result.totalToolCalls++;
|
|
11244
|
+
const toolName = tc.name ?? "";
|
|
11245
|
+
const toolNameLower = toolName.toLowerCase();
|
|
11246
|
+
const input = tc.args ?? {};
|
|
11247
|
+
sessionCalls.push({ toolName, input, timestamp: msg.timestamp ?? "" });
|
|
11248
|
+
if (toolNameLower === "run_shell_command" || toolNameLower === "shell") {
|
|
11249
|
+
result.bashCalls++;
|
|
11250
|
+
}
|
|
11251
|
+
const rawCmd = String(input.command ?? "").trimStart();
|
|
11252
|
+
if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd))
|
|
11253
|
+
continue;
|
|
11254
|
+
const dlpMatch = scanArgs(input);
|
|
11255
|
+
if (dlpMatch) {
|
|
11256
|
+
const k = dlpKey(dlpMatch.patternName, dlpMatch.redactedSample, projLabel);
|
|
11257
|
+
if (!dedup.dlpKeys.has(k)) {
|
|
11258
|
+
dedup.dlpKeys.add(k);
|
|
11259
|
+
result.dlpFindings.push({
|
|
11260
|
+
patternName: dlpMatch.patternName,
|
|
11261
|
+
redactedSample: dlpMatch.redactedSample,
|
|
11262
|
+
toolName,
|
|
11263
|
+
timestamp: msg.timestamp ?? "",
|
|
11264
|
+
project: projLabel,
|
|
11265
|
+
sessionId,
|
|
11266
|
+
agent: "gemini"
|
|
11267
|
+
});
|
|
11268
|
+
}
|
|
11269
|
+
}
|
|
11270
|
+
let astFsMatched = false;
|
|
11271
|
+
const astRanForBash = toolNameLower === "run_shell_command" || toolNameLower === "shell";
|
|
11272
|
+
if (astRanForBash) {
|
|
11273
|
+
astFsMatched = pushFsOpAstFinding(
|
|
11274
|
+
String(input.command ?? ""),
|
|
11275
|
+
toolName,
|
|
11276
|
+
input,
|
|
11277
|
+
msg.timestamp ?? "",
|
|
11278
|
+
projLabel,
|
|
11279
|
+
sessionId,
|
|
11280
|
+
"gemini",
|
|
11281
|
+
result,
|
|
11282
|
+
dedup
|
|
11283
|
+
);
|
|
11284
|
+
}
|
|
11285
|
+
let ruleMatched = astFsMatched;
|
|
11286
|
+
for (const source of ruleSources) {
|
|
11287
|
+
const { rule } = source;
|
|
11288
|
+
if (rule.verdict === "allow") continue;
|
|
11289
|
+
if (rule.tool && !matchesPattern(toolNameLower, rule.tool)) continue;
|
|
11290
|
+
if (astRanForBash && rule.name && AST_FS_REGEX_RULES.has(rule.name)) continue;
|
|
11291
|
+
if (!evaluateSmartConditions(input, rule)) continue;
|
|
11292
|
+
const inputPreview = preview(input, 120);
|
|
11293
|
+
const k = findingKey(rule.name, inputPreview, projLabel);
|
|
11294
|
+
if (!dedup.findingsKeys.has(k)) {
|
|
11295
|
+
dedup.findingsKeys.add(k);
|
|
11296
|
+
result.findings.push({
|
|
11297
|
+
source,
|
|
11298
|
+
toolName,
|
|
11299
|
+
input,
|
|
11300
|
+
timestamp: msg.timestamp ?? "",
|
|
11301
|
+
project: projLabel,
|
|
11302
|
+
sessionId,
|
|
11303
|
+
agent: "gemini"
|
|
11304
|
+
});
|
|
11305
|
+
}
|
|
11306
|
+
ruleMatched = true;
|
|
11307
|
+
break;
|
|
11308
|
+
}
|
|
11309
|
+
const isShellTool = ["bash", "execute_bash", "run_shell_command", "shell"].includes(
|
|
11310
|
+
toolNameLower
|
|
11311
|
+
);
|
|
11312
|
+
if (!ruleMatched && isShellTool) {
|
|
11313
|
+
const shellVerdict = detectDangerousShellExec(String(input.command ?? ""));
|
|
11314
|
+
if (shellVerdict) {
|
|
11315
|
+
const astRule = {
|
|
11316
|
+
name: `ast:bash-safe:${shellVerdict}-shell-exec-remote`,
|
|
11317
|
+
tool: "bash",
|
|
11318
|
+
conditions: [],
|
|
11319
|
+
verdict: shellVerdict,
|
|
11320
|
+
reason: `Shell execution of remote download detected by AST analysis (bash-safe)`
|
|
11321
|
+
};
|
|
11322
|
+
const inputPreview = preview(input, 120);
|
|
11323
|
+
const k = findingKey(astRule.name, inputPreview, projLabel);
|
|
11324
|
+
if (!dedup.findingsKeys.has(k)) {
|
|
11325
|
+
dedup.findingsKeys.add(k);
|
|
11326
|
+
result.findings.push({
|
|
11327
|
+
source: {
|
|
11328
|
+
shieldName: "bash-safe",
|
|
11329
|
+
shieldLabel: "bash-safe (AST)",
|
|
11330
|
+
sourceType: "shield",
|
|
11331
|
+
rule: astRule
|
|
11332
|
+
},
|
|
11333
|
+
toolName,
|
|
11334
|
+
input,
|
|
11335
|
+
timestamp: msg.timestamp ?? "",
|
|
11336
|
+
project: projLabel,
|
|
11337
|
+
sessionId,
|
|
11338
|
+
agent: "gemini"
|
|
11339
|
+
});
|
|
11340
|
+
}
|
|
11341
|
+
}
|
|
11342
|
+
}
|
|
11343
|
+
}
|
|
11344
|
+
}
|
|
11345
|
+
result.loopFindings.push(...detectLoops(sessionCalls, projLabel, sessionId, "gemini"));
|
|
11346
|
+
}
|
|
11347
|
+
}
|
|
11348
|
+
return result;
|
|
11349
|
+
}
|
|
11350
|
+
function antigravityBrainDirs() {
|
|
11351
|
+
return ["antigravity-cli", "antigravity-ide"].map((surface) => path22.join(os19.homedir(), ".gemini", surface, "brain")).filter((p) => fs20.existsSync(p));
|
|
11352
|
+
}
|
|
11353
|
+
function antigravityTranscriptPath(convPath) {
|
|
11354
|
+
const logsDir = path22.join(convPath, ".system_generated", "logs");
|
|
11355
|
+
for (const name of ["transcript_full.jsonl", "transcript.jsonl"]) {
|
|
11356
|
+
const p = path22.join(logsDir, name);
|
|
11357
|
+
if (fs20.existsSync(p)) return p;
|
|
11358
|
+
}
|
|
11359
|
+
return null;
|
|
11360
|
+
}
|
|
11361
|
+
function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
11362
|
+
const result = {
|
|
11363
|
+
filesScanned: 0,
|
|
11364
|
+
sessions: 0,
|
|
11365
|
+
totalToolCalls: 0,
|
|
11366
|
+
bashCalls: 0,
|
|
11367
|
+
findings: [],
|
|
11368
|
+
dlpFindings: [],
|
|
11369
|
+
loopFindings: [],
|
|
11370
|
+
totalCostUSD: 0,
|
|
11371
|
+
// transcripts carry no token/model data
|
|
11372
|
+
firstDate: null,
|
|
11373
|
+
lastDate: null,
|
|
11374
|
+
sessionsWithEarlySecrets: 0
|
|
11375
|
+
};
|
|
11376
|
+
const dedup = emptyScanDedup();
|
|
11377
|
+
const brainDirs = antigravityBrainDirs();
|
|
11378
|
+
if (brainDirs.length === 0) return result;
|
|
11379
|
+
const ruleSources = buildRuleSources();
|
|
11380
|
+
for (const brainDir of brainDirs) {
|
|
11381
|
+
let convDirs;
|
|
11382
|
+
try {
|
|
11383
|
+
convDirs = fs20.readdirSync(brainDir);
|
|
10661
11384
|
} catch {
|
|
10662
11385
|
continue;
|
|
10663
11386
|
}
|
|
10664
|
-
for (const
|
|
10665
|
-
|
|
10666
|
-
onProgress?.(result.filesScanned);
|
|
10667
|
-
const sessionId = chatFile.replace(/\.json$/, "");
|
|
10668
|
-
let raw;
|
|
11387
|
+
for (const conv of convDirs) {
|
|
11388
|
+
const convPath = path22.join(brainDir, conv);
|
|
10669
11389
|
try {
|
|
10670
|
-
|
|
11390
|
+
if (!fs20.statSync(convPath).isDirectory()) continue;
|
|
10671
11391
|
} catch {
|
|
10672
11392
|
continue;
|
|
10673
11393
|
}
|
|
10674
|
-
const
|
|
10675
|
-
|
|
11394
|
+
const transcriptFile = antigravityTranscriptPath(convPath);
|
|
11395
|
+
if (!transcriptFile) continue;
|
|
11396
|
+
result.filesScanned++;
|
|
11397
|
+
onProgress?.(result.filesScanned);
|
|
11398
|
+
let raw;
|
|
10676
11399
|
try {
|
|
10677
|
-
|
|
11400
|
+
raw = fs20.readFileSync(transcriptFile, "utf-8");
|
|
10678
11401
|
} catch {
|
|
10679
11402
|
continue;
|
|
10680
11403
|
}
|
|
11404
|
+
const sessionId = conv;
|
|
11405
|
+
let projLabel = conv.slice(0, 8);
|
|
11406
|
+
const sessionCalls = [];
|
|
10681
11407
|
result.sessions++;
|
|
10682
|
-
for (const
|
|
11408
|
+
for (const line of raw.split("\n")) {
|
|
11409
|
+
if (!line.trim()) continue;
|
|
10683
11410
|
onLine?.();
|
|
10684
|
-
|
|
10685
|
-
|
|
10686
|
-
|
|
11411
|
+
let step;
|
|
11412
|
+
try {
|
|
11413
|
+
step = JSON.parse(line);
|
|
11414
|
+
} catch {
|
|
11415
|
+
continue;
|
|
11416
|
+
}
|
|
11417
|
+
const timestamp = step.created_at ?? "";
|
|
11418
|
+
if (startDate && timestamp && new Date(timestamp) < startDate) continue;
|
|
11419
|
+
if (step.type === "USER_INPUT") {
|
|
11420
|
+
const text = typeof step.content === "string" ? step.content : "";
|
|
10687
11421
|
if (text) {
|
|
10688
11422
|
const dlpMatch = scanArgs({ text });
|
|
10689
11423
|
if (dlpMatch) {
|
|
@@ -10694,40 +11428,34 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
10694
11428
|
patternName: dlpMatch.patternName,
|
|
10695
11429
|
redactedSample: dlpMatch.redactedSample,
|
|
10696
11430
|
toolName: "user-prompt",
|
|
10697
|
-
timestamp
|
|
11431
|
+
timestamp,
|
|
10698
11432
|
project: projLabel,
|
|
10699
11433
|
sessionId,
|
|
10700
|
-
agent: "
|
|
11434
|
+
agent: "antigravity"
|
|
10701
11435
|
});
|
|
10702
11436
|
}
|
|
10703
11437
|
}
|
|
10704
11438
|
}
|
|
10705
11439
|
continue;
|
|
10706
11440
|
}
|
|
10707
|
-
if (
|
|
10708
|
-
if (
|
|
10709
|
-
|
|
10710
|
-
if (!result.
|
|
10711
|
-
result.firstDate = msg.timestamp;
|
|
10712
|
-
if (!result.lastDate || msg.timestamp > result.lastDate) result.lastDate = msg.timestamp;
|
|
10713
|
-
}
|
|
10714
|
-
const tokens = msg.tokens;
|
|
10715
|
-
const model = msg.model;
|
|
10716
|
-
if (tokens && model) {
|
|
10717
|
-
const p = geminiModelPrice(model);
|
|
10718
|
-
if (p) {
|
|
10719
|
-
const nonCached = Math.max(0, tokens.input - tokens.cached);
|
|
10720
|
-
result.totalCostUSD += nonCached * p.i + tokens.cached * p.cr + tokens.output * p.o;
|
|
10721
|
-
}
|
|
11441
|
+
if (!Array.isArray(step.tool_calls) || step.tool_calls.length === 0) continue;
|
|
11442
|
+
if (timestamp) {
|
|
11443
|
+
if (!result.firstDate || timestamp < result.firstDate) result.firstDate = timestamp;
|
|
11444
|
+
if (!result.lastDate || timestamp > result.lastDate) result.lastDate = timestamp;
|
|
10722
11445
|
}
|
|
10723
|
-
for (const tc of
|
|
11446
|
+
for (const tc of step.tool_calls) {
|
|
10724
11447
|
result.totalToolCalls++;
|
|
10725
11448
|
const toolName = tc.name ?? "";
|
|
10726
11449
|
const toolNameLower = toolName.toLowerCase();
|
|
10727
|
-
const input = tc.args ?? {};
|
|
10728
|
-
sessionCalls.push({ toolName, input, timestamp
|
|
10729
|
-
|
|
11450
|
+
const input = canonicalToolInput(toolName, tc.args ?? {});
|
|
11451
|
+
sessionCalls.push({ toolName, input, timestamp });
|
|
11452
|
+
const isShellTool = toolNameLower === "run_command";
|
|
11453
|
+
if (isShellTool) {
|
|
10730
11454
|
result.bashCalls++;
|
|
11455
|
+
const cwd = String(input.cwd ?? "");
|
|
11456
|
+
if (cwd && projLabel === conv.slice(0, 8)) {
|
|
11457
|
+
projLabel = stripTerminalEscapes(cwd).replace(os19.homedir(), "~").slice(0, 40);
|
|
11458
|
+
}
|
|
10731
11459
|
}
|
|
10732
11460
|
const rawCmd = String(input.command ?? "").trimStart();
|
|
10733
11461
|
if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd))
|
|
@@ -10741,24 +11469,23 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
10741
11469
|
patternName: dlpMatch.patternName,
|
|
10742
11470
|
redactedSample: dlpMatch.redactedSample,
|
|
10743
11471
|
toolName,
|
|
10744
|
-
timestamp
|
|
11472
|
+
timestamp,
|
|
10745
11473
|
project: projLabel,
|
|
10746
11474
|
sessionId,
|
|
10747
|
-
agent: "
|
|
11475
|
+
agent: "antigravity"
|
|
10748
11476
|
});
|
|
10749
11477
|
}
|
|
10750
11478
|
}
|
|
10751
11479
|
let astFsMatched = false;
|
|
10752
|
-
|
|
10753
|
-
if (astRanForBash) {
|
|
11480
|
+
if (isShellTool) {
|
|
10754
11481
|
astFsMatched = pushFsOpAstFinding(
|
|
10755
11482
|
String(input.command ?? ""),
|
|
10756
11483
|
toolName,
|
|
10757
11484
|
input,
|
|
10758
|
-
|
|
11485
|
+
timestamp,
|
|
10759
11486
|
projLabel,
|
|
10760
11487
|
sessionId,
|
|
10761
|
-
"
|
|
11488
|
+
"antigravity",
|
|
10762
11489
|
result,
|
|
10763
11490
|
dedup
|
|
10764
11491
|
);
|
|
@@ -10767,8 +11494,9 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
10767
11494
|
for (const source of ruleSources) {
|
|
10768
11495
|
const { rule } = source;
|
|
10769
11496
|
if (rule.verdict === "allow") continue;
|
|
10770
|
-
|
|
10771
|
-
if (
|
|
11497
|
+
const ruleToolName = isShellTool ? "bash" : toolNameLower;
|
|
11498
|
+
if (rule.tool && !matchesPattern(ruleToolName, rule.tool)) continue;
|
|
11499
|
+
if (isShellTool && rule.name && AST_FS_REGEX_RULES.has(rule.name)) continue;
|
|
10772
11500
|
if (!evaluateSmartConditions(input, rule)) continue;
|
|
10773
11501
|
const inputPreview = preview(input, 120);
|
|
10774
11502
|
const k = findingKey(rule.name, inputPreview, projLabel);
|
|
@@ -10778,18 +11506,15 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
10778
11506
|
source,
|
|
10779
11507
|
toolName,
|
|
10780
11508
|
input,
|
|
10781
|
-
timestamp
|
|
11509
|
+
timestamp,
|
|
10782
11510
|
project: projLabel,
|
|
10783
11511
|
sessionId,
|
|
10784
|
-
agent: "
|
|
11512
|
+
agent: "antigravity"
|
|
10785
11513
|
});
|
|
10786
11514
|
}
|
|
10787
11515
|
ruleMatched = true;
|
|
10788
11516
|
break;
|
|
10789
11517
|
}
|
|
10790
|
-
const isShellTool = ["bash", "execute_bash", "run_shell_command", "shell"].includes(
|
|
10791
|
-
toolNameLower
|
|
10792
|
-
);
|
|
10793
11518
|
if (!ruleMatched && isShellTool) {
|
|
10794
11519
|
const shellVerdict = detectDangerousShellExec(String(input.command ?? ""));
|
|
10795
11520
|
if (shellVerdict) {
|
|
@@ -10813,18 +11538,201 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
10813
11538
|
},
|
|
10814
11539
|
toolName,
|
|
10815
11540
|
input,
|
|
10816
|
-
timestamp
|
|
11541
|
+
timestamp,
|
|
10817
11542
|
project: projLabel,
|
|
10818
11543
|
sessionId,
|
|
10819
|
-
agent: "
|
|
11544
|
+
agent: "antigravity"
|
|
10820
11545
|
});
|
|
10821
11546
|
}
|
|
10822
11547
|
}
|
|
10823
11548
|
}
|
|
10824
11549
|
}
|
|
10825
11550
|
}
|
|
10826
|
-
result.loopFindings.push(...detectLoops(sessionCalls, projLabel, sessionId, "
|
|
11551
|
+
result.loopFindings.push(...detectLoops(sessionCalls, projLabel, sessionId, "antigravity"));
|
|
11552
|
+
}
|
|
11553
|
+
}
|
|
11554
|
+
return result;
|
|
11555
|
+
}
|
|
11556
|
+
function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
11557
|
+
const sessionDir = path22.join(os19.homedir(), ".copilot", "session-state");
|
|
11558
|
+
const result = {
|
|
11559
|
+
filesScanned: 0,
|
|
11560
|
+
sessions: 0,
|
|
11561
|
+
totalToolCalls: 0,
|
|
11562
|
+
bashCalls: 0,
|
|
11563
|
+
findings: [],
|
|
11564
|
+
dlpFindings: [],
|
|
11565
|
+
loopFindings: [],
|
|
11566
|
+
totalCostUSD: 0,
|
|
11567
|
+
// event logs carry no token/cost rollup
|
|
11568
|
+
firstDate: null,
|
|
11569
|
+
lastDate: null,
|
|
11570
|
+
sessionsWithEarlySecrets: 0
|
|
11571
|
+
};
|
|
11572
|
+
const dedup = emptyScanDedup();
|
|
11573
|
+
if (!fs20.existsSync(sessionDir)) return result;
|
|
11574
|
+
let sessionIds;
|
|
11575
|
+
try {
|
|
11576
|
+
sessionIds = fs20.readdirSync(sessionDir);
|
|
11577
|
+
} catch {
|
|
11578
|
+
return result;
|
|
11579
|
+
}
|
|
11580
|
+
const ruleSources = buildRuleSources();
|
|
11581
|
+
for (const sessionId of sessionIds) {
|
|
11582
|
+
const eventsPath = path22.join(sessionDir, sessionId, "events.jsonl");
|
|
11583
|
+
if (!fs20.existsSync(eventsPath)) continue;
|
|
11584
|
+
result.filesScanned++;
|
|
11585
|
+
onProgress?.(result.filesScanned);
|
|
11586
|
+
let raw;
|
|
11587
|
+
try {
|
|
11588
|
+
raw = fs20.readFileSync(eventsPath, "utf-8");
|
|
11589
|
+
} catch {
|
|
11590
|
+
continue;
|
|
11591
|
+
}
|
|
11592
|
+
let projLabel = sessionId.slice(0, 8);
|
|
11593
|
+
const sessionCalls = [];
|
|
11594
|
+
result.sessions++;
|
|
11595
|
+
for (const line of raw.split("\n")) {
|
|
11596
|
+
if (!line.trim()) continue;
|
|
11597
|
+
onLine?.();
|
|
11598
|
+
let ev;
|
|
11599
|
+
try {
|
|
11600
|
+
ev = JSON.parse(line);
|
|
11601
|
+
} catch {
|
|
11602
|
+
continue;
|
|
11603
|
+
}
|
|
11604
|
+
const timestamp = ev.timestamp ?? "";
|
|
11605
|
+
if (ev.type === "session.start") {
|
|
11606
|
+
const cwd = ev.data?.context?.cwd;
|
|
11607
|
+
if (typeof cwd === "string" && cwd) {
|
|
11608
|
+
projLabel = stripTerminalEscapes(cwd).replace(os19.homedir(), "~").slice(0, 40);
|
|
11609
|
+
}
|
|
11610
|
+
continue;
|
|
11611
|
+
}
|
|
11612
|
+
if (startDate && timestamp && new Date(timestamp) < startDate) continue;
|
|
11613
|
+
if (ev.type === "user.message") {
|
|
11614
|
+
const text = ev.data?.content ?? ev.data?.text ?? "";
|
|
11615
|
+
if (typeof text === "string" && text) {
|
|
11616
|
+
const dlpMatch2 = scanArgs({ text });
|
|
11617
|
+
if (dlpMatch2) {
|
|
11618
|
+
const k = dlpKey(dlpMatch2.patternName, dlpMatch2.redactedSample, projLabel);
|
|
11619
|
+
if (!dedup.dlpKeys.has(k)) {
|
|
11620
|
+
dedup.dlpKeys.add(k);
|
|
11621
|
+
result.dlpFindings.push({
|
|
11622
|
+
patternName: dlpMatch2.patternName,
|
|
11623
|
+
redactedSample: dlpMatch2.redactedSample,
|
|
11624
|
+
toolName: "user-prompt",
|
|
11625
|
+
timestamp,
|
|
11626
|
+
project: projLabel,
|
|
11627
|
+
sessionId,
|
|
11628
|
+
agent: "copilot"
|
|
11629
|
+
});
|
|
11630
|
+
}
|
|
11631
|
+
}
|
|
11632
|
+
}
|
|
11633
|
+
continue;
|
|
11634
|
+
}
|
|
11635
|
+
if (ev.type !== "tool.execution_start") continue;
|
|
11636
|
+
const toolName = ev.data?.toolName ?? "";
|
|
11637
|
+
const toolNameLower = toolName.toLowerCase();
|
|
11638
|
+
const input = ev.data?.arguments ?? {};
|
|
11639
|
+
result.totalToolCalls++;
|
|
11640
|
+
sessionCalls.push({ toolName, input, timestamp });
|
|
11641
|
+
const isShellTool = toolNameLower === "bash" || toolNameLower === "shell";
|
|
11642
|
+
if (isShellTool) result.bashCalls++;
|
|
11643
|
+
if (timestamp) {
|
|
11644
|
+
if (!result.firstDate || timestamp < result.firstDate) result.firstDate = timestamp;
|
|
11645
|
+
if (!result.lastDate || timestamp > result.lastDate) result.lastDate = timestamp;
|
|
11646
|
+
}
|
|
11647
|
+
const rawCmd = String(input.command ?? "").trimStart();
|
|
11648
|
+
if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd)) continue;
|
|
11649
|
+
const dlpMatch = scanArgs(input);
|
|
11650
|
+
if (dlpMatch) {
|
|
11651
|
+
const k = dlpKey(dlpMatch.patternName, dlpMatch.redactedSample, projLabel);
|
|
11652
|
+
if (!dedup.dlpKeys.has(k)) {
|
|
11653
|
+
dedup.dlpKeys.add(k);
|
|
11654
|
+
result.dlpFindings.push({
|
|
11655
|
+
patternName: dlpMatch.patternName,
|
|
11656
|
+
redactedSample: dlpMatch.redactedSample,
|
|
11657
|
+
toolName,
|
|
11658
|
+
timestamp,
|
|
11659
|
+
project: projLabel,
|
|
11660
|
+
sessionId,
|
|
11661
|
+
agent: "copilot"
|
|
11662
|
+
});
|
|
11663
|
+
}
|
|
11664
|
+
}
|
|
11665
|
+
let astFsMatched = false;
|
|
11666
|
+
if (isShellTool) {
|
|
11667
|
+
astFsMatched = pushFsOpAstFinding(
|
|
11668
|
+
String(input.command ?? ""),
|
|
11669
|
+
toolName,
|
|
11670
|
+
input,
|
|
11671
|
+
timestamp,
|
|
11672
|
+
projLabel,
|
|
11673
|
+
sessionId,
|
|
11674
|
+
"copilot",
|
|
11675
|
+
result,
|
|
11676
|
+
dedup
|
|
11677
|
+
);
|
|
11678
|
+
}
|
|
11679
|
+
let ruleMatched = astFsMatched;
|
|
11680
|
+
for (const source of ruleSources) {
|
|
11681
|
+
const { rule } = source;
|
|
11682
|
+
if (rule.verdict === "allow") continue;
|
|
11683
|
+
if (rule.tool && !matchesPattern(toolNameLower, rule.tool)) continue;
|
|
11684
|
+
if (isShellTool && rule.name && AST_FS_REGEX_RULES.has(rule.name)) continue;
|
|
11685
|
+
if (!evaluateSmartConditions(input, rule)) continue;
|
|
11686
|
+
const inputPreview = preview(input, 120);
|
|
11687
|
+
const k = findingKey(rule.name, inputPreview, projLabel);
|
|
11688
|
+
if (!dedup.findingsKeys.has(k)) {
|
|
11689
|
+
dedup.findingsKeys.add(k);
|
|
11690
|
+
result.findings.push({
|
|
11691
|
+
source,
|
|
11692
|
+
toolName,
|
|
11693
|
+
input,
|
|
11694
|
+
timestamp,
|
|
11695
|
+
project: projLabel,
|
|
11696
|
+
sessionId,
|
|
11697
|
+
agent: "copilot"
|
|
11698
|
+
});
|
|
11699
|
+
}
|
|
11700
|
+
ruleMatched = true;
|
|
11701
|
+
break;
|
|
11702
|
+
}
|
|
11703
|
+
if (!ruleMatched && isShellTool) {
|
|
11704
|
+
const shellVerdict = detectDangerousShellExec(String(input.command ?? ""));
|
|
11705
|
+
if (shellVerdict) {
|
|
11706
|
+
const astRule = {
|
|
11707
|
+
name: `ast:bash-safe:${shellVerdict}-shell-exec-remote`,
|
|
11708
|
+
tool: "bash",
|
|
11709
|
+
conditions: [],
|
|
11710
|
+
verdict: shellVerdict,
|
|
11711
|
+
reason: `Shell execution of remote download detected by AST analysis (bash-safe)`
|
|
11712
|
+
};
|
|
11713
|
+
const inputPreview = preview(input, 120);
|
|
11714
|
+
const k = findingKey(astRule.name, inputPreview, projLabel);
|
|
11715
|
+
if (!dedup.findingsKeys.has(k)) {
|
|
11716
|
+
dedup.findingsKeys.add(k);
|
|
11717
|
+
result.findings.push({
|
|
11718
|
+
source: {
|
|
11719
|
+
shieldName: "bash-safe",
|
|
11720
|
+
shieldLabel: "bash-safe (AST)",
|
|
11721
|
+
sourceType: "shield",
|
|
11722
|
+
rule: astRule
|
|
11723
|
+
},
|
|
11724
|
+
toolName,
|
|
11725
|
+
input,
|
|
11726
|
+
timestamp,
|
|
11727
|
+
project: projLabel,
|
|
11728
|
+
sessionId,
|
|
11729
|
+
agent: "copilot"
|
|
11730
|
+
});
|
|
11731
|
+
}
|
|
11732
|
+
}
|
|
11733
|
+
}
|
|
10827
11734
|
}
|
|
11735
|
+
result.loopFindings.push(...detectLoops(sessionCalls, projLabel, sessionId, "copilot"));
|
|
10828
11736
|
}
|
|
10829
11737
|
return result;
|
|
10830
11738
|
}
|
|
@@ -11123,8 +12031,8 @@ function printFindingRow(f, drillDown, showSessionId, previewWidth) {
|
|
|
11123
12031
|
const stale = isStaleFinding(f.timestamp);
|
|
11124
12032
|
const ts = f.timestamp ? chalk5.dim(fmtTs(f.timestamp) + " ") : "";
|
|
11125
12033
|
const proj = chalk5.dim(f.project.slice(0, 22).padEnd(22) + " ");
|
|
11126
|
-
const agentLabel2 = f.agent
|
|
11127
|
-
const agentBadge = stale ? chalk5.dim(agentLabel2) :
|
|
12034
|
+
const agentLabel2 = agentBadgeText(f.agent);
|
|
12035
|
+
const agentBadge = stale ? chalk5.dim(agentLabel2) : chalk5[agentColorName(f.agent)](agentLabel2);
|
|
11128
12036
|
let cmdText;
|
|
11129
12037
|
if (drillDown) {
|
|
11130
12038
|
cmdText = f.fullCommand;
|
|
@@ -11687,16 +12595,35 @@ function registerScanCommand(program2) {
|
|
|
11687
12595
|
(done) => onProgress(claudeScan.filesScanned + done),
|
|
11688
12596
|
onLine
|
|
11689
12597
|
);
|
|
11690
|
-
const
|
|
12598
|
+
const antigravityScan = scanAntigravityHistory(
|
|
11691
12599
|
startDate,
|
|
11692
12600
|
(done) => onProgress(claudeScan.filesScanned + geminiScan.filesScanned + done),
|
|
11693
12601
|
onLine
|
|
11694
12602
|
);
|
|
11695
|
-
const
|
|
12603
|
+
const copilotScan = scanCopilotHistory(
|
|
12604
|
+
startDate,
|
|
12605
|
+
(done) => onProgress(
|
|
12606
|
+
claudeScan.filesScanned + geminiScan.filesScanned + antigravityScan.filesScanned + done
|
|
12607
|
+
),
|
|
12608
|
+
onLine
|
|
12609
|
+
);
|
|
12610
|
+
const codexScan = scanCodexHistory(
|
|
12611
|
+
startDate,
|
|
12612
|
+
(done) => onProgress(
|
|
12613
|
+
claudeScan.filesScanned + geminiScan.filesScanned + antigravityScan.filesScanned + copilotScan.filesScanned + done
|
|
12614
|
+
),
|
|
12615
|
+
onLine
|
|
12616
|
+
);
|
|
12617
|
+
const scan = mergeScans(
|
|
12618
|
+
mergeScans(mergeScans(mergeScans(claudeScan, geminiScan), antigravityScan), copilotScan),
|
|
12619
|
+
codexScan
|
|
12620
|
+
);
|
|
11696
12621
|
scan.dlpFindings.push(...scanShellConfig());
|
|
11697
12622
|
const summary = buildScanSummary([
|
|
11698
12623
|
{ id: "claude", label: "Claude", icon: "\u{1F916}", scan: claudeScan },
|
|
11699
12624
|
{ id: "gemini", label: "Gemini", icon: "\u264A", scan: geminiScan },
|
|
12625
|
+
{ id: "antigravity", label: "Antigravity", icon: "\u{1F680}", scan: antigravityScan },
|
|
12626
|
+
{ id: "copilot", label: "Copilot", icon: "\u{1F419}", scan: copilotScan },
|
|
11700
12627
|
{ id: "codex", label: "Codex", icon: "\u{1F52E}", scan: codexScan }
|
|
11701
12628
|
]);
|
|
11702
12629
|
if (useTTY) process.stdout.write("\r" + " ".repeat(60) + "\r");
|
|
@@ -11704,7 +12631,7 @@ function registerScanCommand(program2) {
|
|
|
11704
12631
|
console.log(chalk5.yellow(" No session history found."));
|
|
11705
12632
|
console.log(
|
|
11706
12633
|
chalk5.gray(
|
|
11707
|
-
" Supported: Claude Code (~/.claude/projects/) \xB7 Gemini CLI (~/.gemini/tmp/)\n"
|
|
12634
|
+
" Supported: Claude Code (~/.claude/projects/) \xB7 Gemini CLI (~/.gemini/tmp/) \xB7 Antigravity (~/.gemini/antigravity-*/brain/) \xB7 Copilot CLI (~/.copilot/session-state/)\n"
|
|
11708
12635
|
)
|
|
11709
12636
|
);
|
|
11710
12637
|
return;
|
|
@@ -11716,6 +12643,12 @@ function registerScanCommand(program2) {
|
|
|
11716
12643
|
breakdownParts.push(chalk5.cyan(String(claudeScan.sessions)) + chalk5.dim(" Claude"));
|
|
11717
12644
|
if (geminiScan.sessions > 0)
|
|
11718
12645
|
breakdownParts.push(chalk5.blue(String(geminiScan.sessions)) + chalk5.dim(" Gemini"));
|
|
12646
|
+
if (antigravityScan.sessions > 0)
|
|
12647
|
+
breakdownParts.push(
|
|
12648
|
+
chalk5.yellow(String(antigravityScan.sessions)) + chalk5.dim(" Antigravity")
|
|
12649
|
+
);
|
|
12650
|
+
if (copilotScan.sessions > 0)
|
|
12651
|
+
breakdownParts.push(chalk5.green(String(copilotScan.sessions)) + chalk5.dim(" Copilot"));
|
|
11719
12652
|
if (codexScan.sessions > 0)
|
|
11720
12653
|
breakdownParts.push(chalk5.magenta(String(codexScan.sessions)) + chalk5.dim(" Codex"));
|
|
11721
12654
|
const sessionBreakdown = breakdownParts.length > 1 ? chalk5.dim("(") + breakdownParts.join(chalk5.dim(" \xB7 ")) + chalk5.dim(")") : "";
|
|
@@ -11904,7 +12837,7 @@ function registerScanCommand(program2) {
|
|
|
11904
12837
|
const stale = isStaleFinding(f.timestamp);
|
|
11905
12838
|
const ts = f.timestamp ? chalk5.dim(fmtTs(f.timestamp) + " ") : "";
|
|
11906
12839
|
const proj = chalk5.dim(f.project.slice(0, 22).padEnd(22) + " ");
|
|
11907
|
-
const agentBadge =
|
|
12840
|
+
const agentBadge = chalk5[agentColorName(f.agent)](agentBadgeText(f.agent));
|
|
11908
12841
|
const sessionSuffix = f.sessionId ? chalk5.dim(` \u2192 ${f.sessionId.slice(0, 8)}`) : "";
|
|
11909
12842
|
const recurringBadge = recurringPatterns.has(f.patternName) ? chalk5.red.bold(" \u26A0\uFE0F recurring ") : "";
|
|
11910
12843
|
const patternDisplay = stale ? chalk5.dim(f.patternName) : chalk5.yellow(f.patternName);
|
|
@@ -11952,8 +12885,8 @@ function registerScanCommand(program2) {
|
|
|
11952
12885
|
const stale = isStaleFinding(f.timestamp);
|
|
11953
12886
|
const ts = f.timestamp ? chalk5.dim(fmtTs(f.timestamp) + " ") : "";
|
|
11954
12887
|
const proj = chalk5.dim(f.project.slice(0, 22).padEnd(22) + " ");
|
|
11955
|
-
const agentLabel2 = f.agent
|
|
11956
|
-
const agentBadge = stale ? chalk5.dim(agentLabel2) :
|
|
12888
|
+
const agentLabel2 = agentBadgeText(f.agent);
|
|
12889
|
+
const agentBadge = stale ? chalk5.dim(agentLabel2) : chalk5[agentColorName(f.agent)](agentLabel2);
|
|
11957
12890
|
const toolDisplay = stale ? chalk5.dim(f.toolName) : chalk5.yellow(f.toolName);
|
|
11958
12891
|
const cmdDisplay = stale ? chalk5.dim(f.commandPreview) : chalk5.gray(f.commandPreview);
|
|
11959
12892
|
const sessionSuffix = f.sessionId ? chalk5.dim(` \u2192 ${f.sessionId.slice(0, 8)}`) : "";
|
|
@@ -12087,6 +13020,7 @@ var init_scan = __esm({
|
|
|
12087
13020
|
init_policy();
|
|
12088
13021
|
init_dist();
|
|
12089
13022
|
init_dlp();
|
|
13023
|
+
init_hook_payload();
|
|
12090
13024
|
init_dist();
|
|
12091
13025
|
init_scan_summary();
|
|
12092
13026
|
init_setup();
|
|
@@ -12165,6 +13099,8 @@ var init_scan = __esm({
|
|
|
12165
13099
|
"exec_command",
|
|
12166
13100
|
"shell",
|
|
12167
13101
|
"run_shell_command",
|
|
13102
|
+
"run_command",
|
|
13103
|
+
// Antigravity (agy)
|
|
12168
13104
|
"write",
|
|
12169
13105
|
"edit",
|
|
12170
13106
|
"multiedit"
|
|
@@ -16751,33 +17687,7 @@ function resolveUserSkillRoot(entry, cwd) {
|
|
|
16751
17687
|
// src/cli/commands/check.ts
|
|
16752
17688
|
init_dlp();
|
|
16753
17689
|
init_audit();
|
|
16754
|
-
|
|
16755
|
-
// src/utils/hook-payload.ts
|
|
16756
|
-
function extractToolName(payload, defaultValue = "") {
|
|
16757
|
-
return payload.tool_name ?? payload.name ?? defaultValue;
|
|
16758
|
-
}
|
|
16759
|
-
function extractToolInput(payload) {
|
|
16760
|
-
return payload.tool_input ?? payload.args ?? {};
|
|
16761
|
-
}
|
|
16762
|
-
function canonicalToolName(name) {
|
|
16763
|
-
switch (name) {
|
|
16764
|
-
// Hermes Agent
|
|
16765
|
-
case "terminal":
|
|
16766
|
-
return "Bash";
|
|
16767
|
-
case "write_file":
|
|
16768
|
-
return "Write";
|
|
16769
|
-
case "patch":
|
|
16770
|
-
return "Edit";
|
|
16771
|
-
case "read_file":
|
|
16772
|
-
return "Read";
|
|
16773
|
-
case "search_files":
|
|
16774
|
-
return "Grep";
|
|
16775
|
-
default:
|
|
16776
|
-
return name;
|
|
16777
|
-
}
|
|
16778
|
-
}
|
|
16779
|
-
|
|
16780
|
-
// src/cli/commands/check.ts
|
|
17690
|
+
init_hook_payload();
|
|
16781
17691
|
function sanitize2(value) {
|
|
16782
17692
|
return value.replace(/[\x00-\x1F\x7F]/g, "");
|
|
16783
17693
|
}
|
|
@@ -16790,6 +17700,9 @@ function detectAiAgent(payload) {
|
|
|
16790
17700
|
if (payload.turn_id !== void 0) {
|
|
16791
17701
|
return "Codex";
|
|
16792
17702
|
}
|
|
17703
|
+
if (payload.toolCall !== void 0 || payload.conversationId !== void 0) {
|
|
17704
|
+
return "Antigravity";
|
|
17705
|
+
}
|
|
16793
17706
|
if (payload.hook_event_name === "PreToolUse" || payload.hook_event_name === "PostToolUse" || payload.tool_use_id !== void 0 || payload.permission_mode !== void 0) {
|
|
16794
17707
|
return "Claude Code";
|
|
16795
17708
|
}
|
|
@@ -16805,6 +17718,9 @@ function detectAiAgent(payload) {
|
|
|
16805
17718
|
if (process.env.HERMES_SESSION_ID || process.env.HERMES_HOME || process.env.HERMES_INTERACTIVE) {
|
|
16806
17719
|
return "Hermes";
|
|
16807
17720
|
}
|
|
17721
|
+
if (process.env.ANTIGRAVITY_CONVERSATION_ID) {
|
|
17722
|
+
return "Antigravity";
|
|
17723
|
+
}
|
|
16808
17724
|
if (process.env.GEMINI_CLI_VERSION || process.env.GEMINI_API_KEY) {
|
|
16809
17725
|
return "Gemini CLI";
|
|
16810
17726
|
}
|
|
@@ -16820,7 +17736,11 @@ function detectAiAgent(payload) {
|
|
|
16820
17736
|
return "Terminal";
|
|
16821
17737
|
}
|
|
16822
17738
|
function registerCheckCommand(program2) {
|
|
16823
|
-
program2.command("check", { hidden: true }).description("Hook handler \u2014 evaluates a tool call before execution").argument("[data]", "JSON string of the tool call").
|
|
17739
|
+
program2.command("check", { hidden: true }).description("Hook handler \u2014 evaluates a tool call before execution").argument("[data]", "JSON string of the tool call").option(
|
|
17740
|
+
"--agent <name>",
|
|
17741
|
+
"Agent identity override, set by node9-authored hook registrations (e.g. antigravity)"
|
|
17742
|
+
).action(async (data, opts) => {
|
|
17743
|
+
const agentOverride = agentLabelFromFlag(opts?.agent);
|
|
16824
17744
|
const processPayload = async (raw) => {
|
|
16825
17745
|
try {
|
|
16826
17746
|
if (!raw || raw.trim() === "") process.exit(0);
|
|
@@ -16860,7 +17780,7 @@ RAW: ${raw}
|
|
|
16860
17780
|
if (!prompt) process.exit(0);
|
|
16861
17781
|
const dlpMatch = scanArgs({ prompt });
|
|
16862
17782
|
if (!dlpMatch) process.exit(0);
|
|
16863
|
-
const agent2 = detectAiAgent(payload);
|
|
17783
|
+
const agent2 = agentOverride ?? detectAiAgent(payload);
|
|
16864
17784
|
const sessionId2 = typeof payload.session_id === "string" ? payload.session_id : void 0;
|
|
16865
17785
|
appendLocalAudit(
|
|
16866
17786
|
"UserPromptSubmit",
|
|
@@ -16901,7 +17821,8 @@ RAW: ${raw}
|
|
|
16901
17821
|
);
|
|
16902
17822
|
process.exit(2);
|
|
16903
17823
|
}
|
|
16904
|
-
const
|
|
17824
|
+
const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
|
|
17825
|
+
const safeCwdForConfig = typeof payloadCwd === "string" && path33.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
16905
17826
|
const config = getConfig(safeCwdForConfig);
|
|
16906
17827
|
if (config.settings.autoStartDaemon && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON) {
|
|
16907
17828
|
try {
|
|
@@ -16951,9 +17872,10 @@ RAW: ${raw}
|
|
|
16951
17872
|
fs32.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
|
|
16952
17873
|
`);
|
|
16953
17874
|
}
|
|
16954
|
-
const
|
|
16955
|
-
const
|
|
16956
|
-
const
|
|
17875
|
+
const rawToolName = sanitize2(extractToolName(payload));
|
|
17876
|
+
const toolName = canonicalToolName(rawToolName);
|
|
17877
|
+
const toolInput = canonicalToolInput(rawToolName, extractToolInput(payload));
|
|
17878
|
+
const agent = agentOverride ?? detectAiAgent(payload);
|
|
16957
17879
|
const mcpMatch = toolName.match(/^mcp__([^_](?:[^_]|_(?!_))*?)__/i);
|
|
16958
17880
|
const mcpServer = mcpMatch?.[1];
|
|
16959
17881
|
const sendBlock = (msg, result2) => {
|
|
@@ -16991,6 +17913,21 @@ RAW: ${raw}
|
|
|
16991
17913
|
msg,
|
|
16992
17914
|
result2?.recoveryCommand
|
|
16993
17915
|
);
|
|
17916
|
+
if (agent === "Antigravity") {
|
|
17917
|
+
process.stdout.write(
|
|
17918
|
+
JSON.stringify({ decision: "deny", reason: aiFeedbackMessage }) + "\n"
|
|
17919
|
+
);
|
|
17920
|
+
process.exit(0);
|
|
17921
|
+
}
|
|
17922
|
+
if (agent === "GitHub Copilot") {
|
|
17923
|
+
process.stdout.write(
|
|
17924
|
+
JSON.stringify({
|
|
17925
|
+
permissionDecision: "deny",
|
|
17926
|
+
permissionDecisionReason: aiFeedbackMessage
|
|
17927
|
+
}) + "\n"
|
|
17928
|
+
);
|
|
17929
|
+
process.exit(0);
|
|
17930
|
+
}
|
|
16994
17931
|
process.stdout.write(
|
|
16995
17932
|
JSON.stringify({
|
|
16996
17933
|
decision: "block",
|
|
@@ -17009,11 +17946,11 @@ RAW: ${raw}
|
|
|
17009
17946
|
sendBlock("Node9: unrecognised hook payload \u2014 tool name missing.");
|
|
17010
17947
|
return;
|
|
17011
17948
|
}
|
|
17012
|
-
const sessionId = typeof payload.session_id === "string" ? payload.session_id : void 0;
|
|
17013
|
-
const transcriptPath = typeof payload.transcript_path === "string" ? payload.transcript_path : void 0;
|
|
17949
|
+
const sessionId = typeof payload.session_id === "string" ? payload.session_id : typeof payload.conversationId === "string" ? payload.conversationId : void 0;
|
|
17950
|
+
const transcriptPath = typeof payload.transcript_path === "string" ? payload.transcript_path : typeof payload.transcriptPath === "string" ? payload.transcriptPath : void 0;
|
|
17014
17951
|
const meta = { agent, mcpServer, sessionId, transcriptPath };
|
|
17015
17952
|
const skillPinCfg = config.policy.skillPinning;
|
|
17016
|
-
const rawSessionId =
|
|
17953
|
+
const rawSessionId = sessionId ?? "";
|
|
17017
17954
|
const safeSessionId = /^[A-Za-z0-9_\-]{1,128}$/.test(rawSessionId) ? rawSessionId : "";
|
|
17018
17955
|
if (skillPinCfg.enabled && safeSessionId) {
|
|
17019
17956
|
try {
|
|
@@ -17070,7 +18007,7 @@ RAW: ${raw}
|
|
|
17070
18007
|
return;
|
|
17071
18008
|
}
|
|
17072
18009
|
if (!flag || flag.state !== "verified" && flag.state !== "warned") {
|
|
17073
|
-
const absoluteCwd = typeof
|
|
18010
|
+
const absoluteCwd = typeof payloadCwd === "string" && path33.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
17074
18011
|
const extraRoots = skillPinCfg.roots;
|
|
17075
18012
|
const resolvedExtra = extraRoots.map((r) => resolveUserSkillRoot(r, absoluteCwd)).filter((r) => typeof r === "string");
|
|
17076
18013
|
const roots = [...defaultSkillRoots(absoluteCwd), ...resolvedExtra];
|
|
@@ -17136,7 +18073,7 @@ RAW: ${raw}
|
|
|
17136
18073
|
if (shouldSnapshot(toolName, toolInput, config)) {
|
|
17137
18074
|
await createShadowSnapshot(toolName, toolInput, config.policy.snapshot.ignorePaths);
|
|
17138
18075
|
}
|
|
17139
|
-
const safeCwdForAuth = typeof
|
|
18076
|
+
const safeCwdForAuth = typeof payloadCwd === "string" && path33.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
17140
18077
|
const result = await authorizeHeadless(toolName, toolInput, meta, {
|
|
17141
18078
|
cwd: safeCwdForAuth
|
|
17142
18079
|
});
|
|
@@ -17261,6 +18198,7 @@ function containsShellMetachar(token) {
|
|
|
17261
18198
|
}
|
|
17262
18199
|
|
|
17263
18200
|
// src/cli/commands/log.ts
|
|
18201
|
+
init_hook_payload();
|
|
17264
18202
|
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;
|
|
17265
18203
|
function detectTestResult(command, output) {
|
|
17266
18204
|
if (!TEST_COMMAND_RE2.test(command)) return null;
|
|
@@ -17279,14 +18217,19 @@ function sanitize3(value) {
|
|
|
17279
18217
|
return value.replace(/[\x00-\x1F\x7F]/g, "");
|
|
17280
18218
|
}
|
|
17281
18219
|
function registerLogCommand(program2) {
|
|
17282
|
-
program2.command("log", { hidden: true }).description("PostToolUse hook \u2014 records executed tool calls").argument("[data]", "JSON string of the tool call").
|
|
18220
|
+
program2.command("log", { hidden: true }).description("PostToolUse hook \u2014 records executed tool calls").argument("[data]", "JSON string of the tool call").option(
|
|
18221
|
+
"--agent <name>",
|
|
18222
|
+
"Agent identity override, set by node9-authored hook registrations (e.g. antigravity)"
|
|
18223
|
+
).action(async (data, opts) => {
|
|
18224
|
+
const agentOverride = agentLabelFromFlag(opts?.agent);
|
|
17283
18225
|
const logPayload = async (raw) => {
|
|
17284
18226
|
try {
|
|
17285
18227
|
if (!raw || raw.trim() === "") process.exit(0);
|
|
17286
18228
|
const payload = JSON.parse(raw);
|
|
18229
|
+
if (payload.toolCall === null) process.exit(0);
|
|
17287
18230
|
const rawToolName = sanitize3(extractToolName(payload, "unknown"));
|
|
17288
18231
|
const tool = canonicalToolName(rawToolName);
|
|
17289
|
-
const rawInput = extractToolInput(payload);
|
|
18232
|
+
const rawInput = canonicalToolInput(rawToolName, extractToolInput(payload));
|
|
17290
18233
|
const metaTag = (() => {
|
|
17291
18234
|
const m = payload.meta;
|
|
17292
18235
|
if (m && typeof m === "object") {
|
|
@@ -17295,7 +18238,7 @@ function registerLogCommand(program2) {
|
|
|
17295
18238
|
}
|
|
17296
18239
|
return void 0;
|
|
17297
18240
|
})();
|
|
17298
|
-
const agent = metaTag !== void 0 ? metaTag : payload.turn_id !== void 0 ? "Codex" : 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" : void 0;
|
|
18241
|
+
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;
|
|
17299
18242
|
const entry = {
|
|
17300
18243
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17301
18244
|
tool,
|
|
@@ -17305,7 +18248,8 @@ function registerLogCommand(program2) {
|
|
|
17305
18248
|
};
|
|
17306
18249
|
if (agent) entry.agent = agent;
|
|
17307
18250
|
if (rawToolName !== tool) entry.agentToolName = rawToolName;
|
|
17308
|
-
|
|
18251
|
+
const payloadSessionId = payload.session_id ?? payload.conversationId;
|
|
18252
|
+
if (payloadSessionId) entry.sessionId = payloadSessionId;
|
|
17309
18253
|
const logPath = path34.join(os29.homedir(), ".node9", "audit.log");
|
|
17310
18254
|
if (!fs33.existsSync(path34.dirname(logPath)))
|
|
17311
18255
|
fs33.mkdirSync(path34.dirname(logPath), { recursive: true });
|
|
@@ -17342,7 +18286,8 @@ function registerLogCommand(program2) {
|
|
|
17342
18286
|
}
|
|
17343
18287
|
}
|
|
17344
18288
|
}
|
|
17345
|
-
const
|
|
18289
|
+
const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
|
|
18290
|
+
const safeCwd = typeof payloadCwd === "string" && path34.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
17346
18291
|
const config = getConfig(safeCwd);
|
|
17347
18292
|
if ((tool === "Bash" || tool === "bash") && config.settings.enableUndo !== false) {
|
|
17348
18293
|
const bashCommand = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
|
|
@@ -19430,12 +20375,12 @@ function registerInitCommand(program2) {
|
|
|
19430
20375
|
if (found.length === 0) {
|
|
19431
20376
|
console.log(
|
|
19432
20377
|
chalk16.gray(
|
|
19433
|
-
"No AI agents detected. Install one of the supported agents (Claude Code, Codex, Gemini CLI, Cursor, Windsurf, VSCode, Claude Desktop, Opencode, Pi, or Hermes Agent)."
|
|
20378
|
+
"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)."
|
|
19434
20379
|
)
|
|
19435
20380
|
);
|
|
19436
20381
|
console.log(
|
|
19437
20382
|
chalk16.gray(
|
|
19438
|
-
"then run: node9 agents add <claude|codex|gemini|cursor|windsurf|vscode|claudeDesktop|opencode|pi|hermes>"
|
|
20383
|
+
"then run: node9 agents add <claude|codex|antigravity|gemini|copilot|cursor|windsurf|vscode|claudeDesktop|opencode|pi|hermes>"
|
|
19439
20384
|
)
|
|
19440
20385
|
);
|
|
19441
20386
|
return;
|
|
@@ -19449,6 +20394,8 @@ function registerInitCommand(program2) {
|
|
|
19449
20394
|
console.log(chalk16.bold(`Wiring ${agent}...`));
|
|
19450
20395
|
if (agent === "claude") await setupClaude();
|
|
19451
20396
|
else if (agent === "gemini") await setupGemini();
|
|
20397
|
+
else if (agent === "antigravity") await setupAntigravity();
|
|
20398
|
+
else if (agent === "copilot") await setupCopilot();
|
|
19452
20399
|
else if (agent === "cursor") await setupCursor();
|
|
19453
20400
|
else if (agent === "codex") await setupCodex();
|
|
19454
20401
|
else if (agent === "windsurf") await setupWindsurf();
|
|
@@ -21212,6 +22159,8 @@ import chalk23 from "chalk";
|
|
|
21212
22159
|
var SETUP_FN = {
|
|
21213
22160
|
claude: setupClaude,
|
|
21214
22161
|
gemini: setupGemini,
|
|
22162
|
+
antigravity: setupAntigravity,
|
|
22163
|
+
copilot: setupCopilot,
|
|
21215
22164
|
cursor: setupCursor,
|
|
21216
22165
|
codex: setupCodex,
|
|
21217
22166
|
windsurf: setupWindsurf,
|
|
@@ -21224,6 +22173,8 @@ var SETUP_FN = {
|
|
|
21224
22173
|
var TEARDOWN_FN = {
|
|
21225
22174
|
claude: teardownClaude,
|
|
21226
22175
|
gemini: teardownGemini,
|
|
22176
|
+
antigravity: teardownAntigravity,
|
|
22177
|
+
copilot: teardownCopilot,
|
|
21227
22178
|
cursor: teardownCursor,
|
|
21228
22179
|
codex: teardownCodex,
|
|
21229
22180
|
windsurf: teardownWindsurf,
|
|
@@ -21234,6 +22185,10 @@ var TEARDOWN_FN = {
|
|
|
21234
22185
|
hermes: teardownHermes
|
|
21235
22186
|
};
|
|
21236
22187
|
var AGENT_NAMES = Object.keys(SETUP_FN);
|
|
22188
|
+
function resolveAgentName(agent) {
|
|
22189
|
+
const lower = agent.toLowerCase();
|
|
22190
|
+
return lower === "agy" ? "antigravity" : lower;
|
|
22191
|
+
}
|
|
21237
22192
|
function registerAgentsCommand(program2) {
|
|
21238
22193
|
const agents = program2.command("agents").description("List and manage AI agent integrations");
|
|
21239
22194
|
agents.command("list").description("Show all supported agents and their Node9 status").action(() => {
|
|
@@ -21262,9 +22217,16 @@ function registerAgentsCommand(program2) {
|
|
|
21262
22217
|
chalk23.yellow(` ${unwired.length} agent(s) not yet wired. Run: `) + chalk23.white(`node9 agents add ${unwired[0].name}`) + "\n"
|
|
21263
22218
|
);
|
|
21264
22219
|
}
|
|
22220
|
+
if (statuses.some((s) => s.name === "gemini" && s.installed)) {
|
|
22221
|
+
console.log(
|
|
22222
|
+
chalk23.yellow(
|
|
22223
|
+
" \u26A0\uFE0F Gemini CLI stops serving AI Pro/Ultra and free tiers on 2026-06-18.\n"
|
|
22224
|
+
) + chalk23.gray(" Migrate to Antigravity: ") + chalk23.white("node9 agents add antigravity") + "\n"
|
|
22225
|
+
);
|
|
22226
|
+
}
|
|
21265
22227
|
});
|
|
21266
22228
|
agents.command("add").description("Wire Node9 into an agent").argument("<agent>", `Agent to wire: ${AGENT_NAMES.join(" | ")}`).action(async (agent) => {
|
|
21267
|
-
const name = agent
|
|
22229
|
+
const name = resolveAgentName(agent);
|
|
21268
22230
|
const fn = SETUP_FN[name];
|
|
21269
22231
|
if (!fn) {
|
|
21270
22232
|
console.error(chalk23.red(`Unknown agent: "${agent}". Supported: ${AGENT_NAMES.join(", ")}`));
|
|
@@ -21273,7 +22235,7 @@ function registerAgentsCommand(program2) {
|
|
|
21273
22235
|
await fn();
|
|
21274
22236
|
});
|
|
21275
22237
|
agents.command("remove").description("Remove Node9 from an agent").argument("<agent>", `Agent to unwire: ${AGENT_NAMES.join(" | ")}`).action((agent) => {
|
|
21276
|
-
const name = agent
|
|
22238
|
+
const name = resolveAgentName(agent);
|
|
21277
22239
|
const fn = TEARDOWN_FN[name];
|
|
21278
22240
|
if (!fn) {
|
|
21279
22241
|
console.error(chalk23.red(`Unknown agent: "${agent}". Supported: ${AGENT_NAMES.join(", ")}`));
|
|
@@ -21291,6 +22253,7 @@ function registerAgentsCommand(program2) {
|
|
|
21291
22253
|
init_scan();
|
|
21292
22254
|
|
|
21293
22255
|
// src/cli/commands/sessions.ts
|
|
22256
|
+
init_scan_summary();
|
|
21294
22257
|
import chalk24 from "chalk";
|
|
21295
22258
|
import fs41 from "fs";
|
|
21296
22259
|
import path42 from "path";
|
|
@@ -21901,7 +22864,9 @@ function renderList(summaries, totalCost) {
|
|
|
21901
22864
|
const cost = s.costUSD > 0 ? chalk24.dim(" " + fmtCost3(s.costUSD).padEnd(8)) : " ";
|
|
21902
22865
|
const blocked = s.blockedCalls.length > 0 ? chalk24.red(" \u{1F6D1} " + String(s.blockedCalls.length)) : "";
|
|
21903
22866
|
const snap = s.hasSnapshot ? chalk24.green(" \u{1F4F8}") : "";
|
|
21904
|
-
const agentBadge =
|
|
22867
|
+
const agentBadge = chalk24[agentColorName(s.agent ?? "claude")](
|
|
22868
|
+
" " + agentBadgeText(s.agent ?? "claude", 0)
|
|
22869
|
+
);
|
|
21905
22870
|
const sid = chalk24.dim(" " + s.sessionId.slice(0, 8));
|
|
21906
22871
|
console.log(
|
|
21907
22872
|
` ${timeStr} ${prompt} ${tools}${cost}${blocked}${snap}${agentBadge}${sid}${dateRange}`
|
|
@@ -21921,7 +22886,7 @@ function renderDetail(s) {
|
|
|
21921
22886
|
);
|
|
21922
22887
|
console.log(chalk24.bold(" Project ") + chalk24.white(s.projectLabel));
|
|
21923
22888
|
if (s.agent) {
|
|
21924
|
-
const agentLabel2 = s.agent
|
|
22889
|
+
const agentLabel2 = chalk24[agentColorName(s.agent)](agentDisplayName(s.agent));
|
|
21925
22890
|
console.log(chalk24.bold(" Agent ") + agentLabel2);
|
|
21926
22891
|
}
|
|
21927
22892
|
console.log(chalk24.bold(" When ") + chalk24.white(fmtDateTime(s.startTime)));
|
|
@@ -22558,12 +23523,14 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
22558
23523
|
});
|
|
22559
23524
|
program.command("addto", { hidden: true }).description("Integrate Node9 with an AI agent").addHelpText(
|
|
22560
23525
|
"after",
|
|
22561
|
-
"\n Supported targets: claude gemini cursor codex windsurf vscode hud"
|
|
23526
|
+
"\n Supported targets: claude antigravity copilot gemini cursor codex windsurf vscode hud"
|
|
22562
23527
|
).argument(
|
|
22563
23528
|
"<target>",
|
|
22564
|
-
"The agent to protect: claude | gemini | cursor | codex | windsurf | vscode | hud"
|
|
23529
|
+
"The agent to protect: claude | antigravity | copilot | gemini | cursor | codex | windsurf | vscode | hud"
|
|
22565
23530
|
).action(async (target) => {
|
|
22566
23531
|
if (target === "gemini") return await setupGemini();
|
|
23532
|
+
if (target === "antigravity" || target === "agy") return await setupAntigravity();
|
|
23533
|
+
if (target === "copilot") return await setupCopilot();
|
|
22567
23534
|
if (target === "claude") return await setupClaude();
|
|
22568
23535
|
if (target === "cursor") return await setupCursor();
|
|
22569
23536
|
if (target === "codex") return await setupCodex();
|
|
@@ -22573,17 +23540,17 @@ program.command("addto", { hidden: true }).description("Integrate Node9 with an
|
|
|
22573
23540
|
if (target === "hud") return setupHud();
|
|
22574
23541
|
console.error(
|
|
22575
23542
|
chalk30.red(
|
|
22576
|
-
`Unknown target: "${target}". Supported: claude, gemini, cursor, codex, windsurf, vscode, hermes, hud`
|
|
23543
|
+
`Unknown target: "${target}". Supported: claude, antigravity, copilot, gemini, cursor, codex, windsurf, vscode, hermes, hud`
|
|
22577
23544
|
)
|
|
22578
23545
|
);
|
|
22579
23546
|
process.exit(1);
|
|
22580
23547
|
});
|
|
22581
23548
|
program.command("setup", { hidden: true }).description('Alias for "addto" \u2014 integrate Node9 with an AI agent').addHelpText(
|
|
22582
23549
|
"after",
|
|
22583
|
-
"\n Supported targets: claude gemini cursor codex windsurf vscode hud"
|
|
23550
|
+
"\n Supported targets: claude antigravity copilot gemini cursor codex windsurf vscode hud"
|
|
22584
23551
|
).argument(
|
|
22585
23552
|
"[target]",
|
|
22586
|
-
"The agent to protect: claude | gemini | cursor | codex | windsurf | vscode | hud"
|
|
23553
|
+
"The agent to protect: claude | antigravity | copilot | gemini | cursor | codex | windsurf | vscode | hud"
|
|
22587
23554
|
).action(async (target) => {
|
|
22588
23555
|
if (!target) {
|
|
22589
23556
|
console.log(chalk30.cyan("\n\u{1F6E1}\uFE0F Node9 Setup \u2014 integrate with your AI agent\n"));
|
|
@@ -22591,6 +23558,8 @@ program.command("setup", { hidden: true }).description('Alias for "addto" \u2014
|
|
|
22591
23558
|
console.log(" Targets:");
|
|
22592
23559
|
console.log(" " + chalk30.green("claude") + " \u2014 Claude Code (hook mode)");
|
|
22593
23560
|
console.log(" " + chalk30.green("gemini") + " \u2014 Gemini CLI (hook mode)");
|
|
23561
|
+
console.log(" " + chalk30.green("antigravity") + " \u2014 Antigravity / agy (hook mode)");
|
|
23562
|
+
console.log(" " + chalk30.green("copilot") + " \u2014 GitHub Copilot CLI (hook mode)");
|
|
22594
23563
|
console.log(" " + chalk30.green("cursor") + " \u2014 Cursor (MCP proxy)");
|
|
22595
23564
|
console.log(" " + chalk30.green("codex") + " \u2014 OpenAI Codex CLI (MCP proxy)");
|
|
22596
23565
|
console.log(" " + chalk30.green("windsurf") + " \u2014 Windsurf (MCP proxy)");
|
|
@@ -22604,6 +23573,8 @@ program.command("setup", { hidden: true }).description('Alias for "addto" \u2014
|
|
|
22604
23573
|
}
|
|
22605
23574
|
const t = target.toLowerCase();
|
|
22606
23575
|
if (t === "gemini") return await setupGemini();
|
|
23576
|
+
if (t === "antigravity" || t === "agy") return await setupAntigravity();
|
|
23577
|
+
if (t === "copilot") return await setupCopilot();
|
|
22607
23578
|
if (t === "claude") return await setupClaude();
|
|
22608
23579
|
if (t === "cursor") return await setupCursor();
|
|
22609
23580
|
if (t === "codex") return await setupCodex();
|
|
@@ -22613,21 +23584,23 @@ program.command("setup", { hidden: true }).description('Alias for "addto" \u2014
|
|
|
22613
23584
|
if (t === "hud") return setupHud();
|
|
22614
23585
|
console.error(
|
|
22615
23586
|
chalk30.red(
|
|
22616
|
-
`Unknown target: "${target}". Supported: claude, gemini, cursor, codex, windsurf, vscode, hermes, hud`
|
|
23587
|
+
`Unknown target: "${target}". Supported: claude, antigravity, copilot, gemini, cursor, codex, windsurf, vscode, hermes, hud`
|
|
22617
23588
|
)
|
|
22618
23589
|
);
|
|
22619
23590
|
process.exit(1);
|
|
22620
23591
|
});
|
|
22621
23592
|
program.command("removefrom", { hidden: true }).description("Remove Node9 hooks from an AI agent configuration").addHelpText(
|
|
22622
23593
|
"after",
|
|
22623
|
-
"\n Supported targets: claude gemini cursor codex windsurf vscode hud"
|
|
23594
|
+
"\n Supported targets: claude antigravity copilot gemini cursor codex windsurf vscode hud"
|
|
22624
23595
|
).argument(
|
|
22625
23596
|
"<target>",
|
|
22626
|
-
"The agent to remove from: claude | gemini | cursor | codex | windsurf | vscode | hud"
|
|
23597
|
+
"The agent to remove from: claude | antigravity | copilot | gemini | cursor | codex | windsurf | vscode | hud"
|
|
22627
23598
|
).action((target) => {
|
|
22628
23599
|
let fn;
|
|
22629
23600
|
if (target === "claude") fn = teardownClaude;
|
|
22630
23601
|
else if (target === "gemini") fn = teardownGemini;
|
|
23602
|
+
else if (target === "antigravity" || target === "agy") fn = teardownAntigravity;
|
|
23603
|
+
else if (target === "copilot") fn = teardownCopilot;
|
|
22631
23604
|
else if (target === "cursor") fn = teardownCursor;
|
|
22632
23605
|
else if (target === "codex") fn = teardownCodex;
|
|
22633
23606
|
else if (target === "windsurf") fn = teardownWindsurf;
|
|
@@ -22637,7 +23610,7 @@ program.command("removefrom", { hidden: true }).description("Remove Node9 hooks
|
|
|
22637
23610
|
else {
|
|
22638
23611
|
console.error(
|
|
22639
23612
|
chalk30.red(
|
|
22640
|
-
`Unknown target: "${target}". Supported: claude, gemini, cursor, codex, windsurf, vscode, hermes, hud`
|
|
23613
|
+
`Unknown target: "${target}". Supported: claude, antigravity, copilot, gemini, cursor, codex, windsurf, vscode, hermes, hud`
|
|
22641
23614
|
)
|
|
22642
23615
|
);
|
|
22643
23616
|
process.exit(1);
|
|
@@ -22894,6 +23867,9 @@ program.command("resume").description("Re-enable Node9 protection immediately").
|
|
|
22894
23867
|
var HOOK_BASED_AGENTS = {
|
|
22895
23868
|
claude: "claude",
|
|
22896
23869
|
gemini: "gemini",
|
|
23870
|
+
antigravity: "antigravity",
|
|
23871
|
+
agy: "antigravity",
|
|
23872
|
+
copilot: "copilot",
|
|
22897
23873
|
cursor: "cursor"
|
|
22898
23874
|
};
|
|
22899
23875
|
program.argument("[command...]", "The agent command to run (e.g., gemini)").action(async (commandArgs) => {
|