@node9/proxy 1.21.5 → 1.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1146 -812
- package/dist/cli.mjs +1116 -783
- package/dist/dashboard.mjs +8 -0
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -6281,10 +6281,167 @@ var init_core = __esm({
|
|
|
6281
6281
|
}
|
|
6282
6282
|
});
|
|
6283
6283
|
|
|
6284
|
-
// src/
|
|
6284
|
+
// src/mcp-pin.ts
|
|
6285
6285
|
import fs12 from "fs";
|
|
6286
6286
|
import path14 from "path";
|
|
6287
6287
|
import os11 from "os";
|
|
6288
|
+
import crypto3 from "crypto";
|
|
6289
|
+
function getHomePinsFilePath() {
|
|
6290
|
+
return path14.join(os11.homedir(), ".node9", "mcp-pins.json");
|
|
6291
|
+
}
|
|
6292
|
+
function getPinsFilePath() {
|
|
6293
|
+
return getHomePinsFilePath();
|
|
6294
|
+
}
|
|
6295
|
+
function findPinsFilePath(cwd) {
|
|
6296
|
+
const homeDir2 = os11.homedir();
|
|
6297
|
+
const homePath = getHomePinsFilePath();
|
|
6298
|
+
let current = path14.resolve(cwd ?? process.cwd());
|
|
6299
|
+
while (true) {
|
|
6300
|
+
if (current === homeDir2) break;
|
|
6301
|
+
const candidate = path14.join(current, ".node9", "mcp-pins.json");
|
|
6302
|
+
if (fs12.existsSync(candidate)) {
|
|
6303
|
+
return { path: candidate, source: "repo" };
|
|
6304
|
+
}
|
|
6305
|
+
const next = path14.dirname(current);
|
|
6306
|
+
if (next === current) break;
|
|
6307
|
+
current = next;
|
|
6308
|
+
}
|
|
6309
|
+
return { path: homePath, source: "home" };
|
|
6310
|
+
}
|
|
6311
|
+
function hashToolDefinitions(tools) {
|
|
6312
|
+
const sorted = [...tools].sort((a, b) => {
|
|
6313
|
+
const nameA = a.name ?? "";
|
|
6314
|
+
const nameB = b.name ?? "";
|
|
6315
|
+
return nameA.localeCompare(nameB);
|
|
6316
|
+
});
|
|
6317
|
+
const canonical = JSON.stringify(sorted);
|
|
6318
|
+
return crypto3.createHash("sha256").update(canonical).digest("hex");
|
|
6319
|
+
}
|
|
6320
|
+
function getServerKey(upstreamCommand) {
|
|
6321
|
+
return crypto3.createHash("sha256").update(upstreamCommand).digest("hex").slice(0, 16);
|
|
6322
|
+
}
|
|
6323
|
+
function readPinsFile(filePath) {
|
|
6324
|
+
try {
|
|
6325
|
+
const raw = fs12.readFileSync(filePath, "utf-8");
|
|
6326
|
+
if (!raw.trim()) {
|
|
6327
|
+
return { ok: false, reason: "corrupt", detail: "empty file" };
|
|
6328
|
+
}
|
|
6329
|
+
const parsed = JSON.parse(raw);
|
|
6330
|
+
if (!parsed.servers || typeof parsed.servers !== "object" || Array.isArray(parsed.servers)) {
|
|
6331
|
+
return { ok: false, reason: "corrupt", detail: "invalid structure: missing servers object" };
|
|
6332
|
+
}
|
|
6333
|
+
return { ok: true, pins: { servers: parsed.servers } };
|
|
6334
|
+
} catch (err2) {
|
|
6335
|
+
if (err2.code === "ENOENT") {
|
|
6336
|
+
return { ok: false, reason: "missing" };
|
|
6337
|
+
}
|
|
6338
|
+
return { ok: false, reason: "corrupt", detail: String(err2) };
|
|
6339
|
+
}
|
|
6340
|
+
}
|
|
6341
|
+
function readMcpPinsSafe() {
|
|
6342
|
+
return readPinsFile(getHomePinsFilePath());
|
|
6343
|
+
}
|
|
6344
|
+
function readMcpPins() {
|
|
6345
|
+
const result = readMcpPinsSafe();
|
|
6346
|
+
if (result.ok) return result.pins;
|
|
6347
|
+
if (result.reason === "missing") return { servers: {} };
|
|
6348
|
+
throw new Error(`[node9] MCP pin file is corrupt: ${result.detail}`);
|
|
6349
|
+
}
|
|
6350
|
+
function writePinsFile(filePath, data) {
|
|
6351
|
+
fs12.mkdirSync(path14.dirname(filePath), { recursive: true });
|
|
6352
|
+
const tmp = `${filePath}.${crypto3.randomBytes(6).toString("hex")}.tmp`;
|
|
6353
|
+
const isHome = filePath === getHomePinsFilePath();
|
|
6354
|
+
fs12.writeFileSync(tmp, JSON.stringify(data, null, 2), isHome ? { mode: 384 } : {});
|
|
6355
|
+
fs12.renameSync(tmp, filePath);
|
|
6356
|
+
}
|
|
6357
|
+
function writeMcpPins(data) {
|
|
6358
|
+
writePinsFile(getHomePinsFilePath(), data);
|
|
6359
|
+
}
|
|
6360
|
+
function seedMcpPinsIfMissing() {
|
|
6361
|
+
const filePath = getPinsFilePath();
|
|
6362
|
+
if (fs12.existsSync(filePath)) return;
|
|
6363
|
+
writeMcpPins({ servers: {} });
|
|
6364
|
+
}
|
|
6365
|
+
function checkPin(serverKey, currentHash, cwd) {
|
|
6366
|
+
const found = findPinsFilePath(cwd);
|
|
6367
|
+
let repoEntry;
|
|
6368
|
+
if (found.source === "repo") {
|
|
6369
|
+
const repoResult = readPinsFile(found.path);
|
|
6370
|
+
if (!repoResult.ok) {
|
|
6371
|
+
return "corrupt";
|
|
6372
|
+
}
|
|
6373
|
+
repoEntry = repoResult.pins.servers[serverKey];
|
|
6374
|
+
}
|
|
6375
|
+
if (repoEntry) {
|
|
6376
|
+
return repoEntry.toolsHash === currentHash ? "match" : "mismatch";
|
|
6377
|
+
}
|
|
6378
|
+
const homeResult = readPinsFile(getHomePinsFilePath());
|
|
6379
|
+
if (!homeResult.ok) {
|
|
6380
|
+
if (homeResult.reason === "missing") return "new";
|
|
6381
|
+
return "corrupt";
|
|
6382
|
+
}
|
|
6383
|
+
const homeEntry = homeResult.pins.servers[serverKey];
|
|
6384
|
+
if (!homeEntry) return "new";
|
|
6385
|
+
return homeEntry.toolsHash === currentHash ? "match" : "mismatch";
|
|
6386
|
+
}
|
|
6387
|
+
function updatePin(serverKey, label, toolsHash, toolNames) {
|
|
6388
|
+
const pins = readMcpPins();
|
|
6389
|
+
pins.servers[serverKey] = {
|
|
6390
|
+
label,
|
|
6391
|
+
toolsHash,
|
|
6392
|
+
toolNames,
|
|
6393
|
+
toolCount: toolNames.length,
|
|
6394
|
+
pinnedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6395
|
+
};
|
|
6396
|
+
writeMcpPins(pins);
|
|
6397
|
+
}
|
|
6398
|
+
function removePin(serverKey) {
|
|
6399
|
+
const pins = readMcpPins();
|
|
6400
|
+
delete pins.servers[serverKey];
|
|
6401
|
+
writeMcpPins(pins);
|
|
6402
|
+
}
|
|
6403
|
+
function clearAllPins() {
|
|
6404
|
+
writeMcpPins({ servers: {} });
|
|
6405
|
+
}
|
|
6406
|
+
function promotePin(serverKey, cwd) {
|
|
6407
|
+
const homePins = readMcpPins();
|
|
6408
|
+
const homeEntry = homePins.servers[serverKey];
|
|
6409
|
+
if (!homeEntry) {
|
|
6410
|
+
throw new Error(
|
|
6411
|
+
`[node9] Server "${serverKey}" is not pinned in ~/.node9/mcp-pins.json. Run \`node9 mcp pin list\` to see what's pinned.`
|
|
6412
|
+
);
|
|
6413
|
+
}
|
|
6414
|
+
const found = findPinsFilePath(cwd);
|
|
6415
|
+
let repoPath;
|
|
6416
|
+
let repoPins;
|
|
6417
|
+
let created = false;
|
|
6418
|
+
if (found.source === "repo") {
|
|
6419
|
+
repoPath = found.path;
|
|
6420
|
+
const result = readPinsFile(repoPath);
|
|
6421
|
+
if (!result.ok) {
|
|
6422
|
+
const detail = result.reason === "corrupt" ? result.detail : "missing";
|
|
6423
|
+
throw new Error(`[node9] Repo pin file at ${repoPath} is unreadable: ${detail}`);
|
|
6424
|
+
}
|
|
6425
|
+
repoPins = result.pins;
|
|
6426
|
+
} else {
|
|
6427
|
+
repoPath = path14.join(cwd ?? process.cwd(), ".node9", "mcp-pins.json");
|
|
6428
|
+
repoPins = { servers: {} };
|
|
6429
|
+
created = true;
|
|
6430
|
+
}
|
|
6431
|
+
repoPins.servers[serverKey] = { ...homeEntry };
|
|
6432
|
+
writePinsFile(repoPath, repoPins);
|
|
6433
|
+
return { repoPath, created };
|
|
6434
|
+
}
|
|
6435
|
+
var init_mcp_pin = __esm({
|
|
6436
|
+
"src/mcp-pin.ts"() {
|
|
6437
|
+
"use strict";
|
|
6438
|
+
}
|
|
6439
|
+
});
|
|
6440
|
+
|
|
6441
|
+
// src/setup.ts
|
|
6442
|
+
import fs13 from "fs";
|
|
6443
|
+
import path15 from "path";
|
|
6444
|
+
import os12 from "os";
|
|
6288
6445
|
import chalk from "chalk";
|
|
6289
6446
|
import { confirm } from "@inquirer/prompts";
|
|
6290
6447
|
import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
|
|
@@ -6312,36 +6469,36 @@ function isStaleHookCommand(command) {
|
|
|
6312
6469
|
const tokens = command.split(/\s+/);
|
|
6313
6470
|
for (const tok of tokens) {
|
|
6314
6471
|
if (!tok.startsWith("/")) continue;
|
|
6315
|
-
if (!
|
|
6472
|
+
if (!fs13.existsSync(tok)) return true;
|
|
6316
6473
|
}
|
|
6317
6474
|
return false;
|
|
6318
6475
|
}
|
|
6319
6476
|
function readJson(filePath) {
|
|
6320
6477
|
try {
|
|
6321
|
-
if (
|
|
6322
|
-
return JSON.parse(
|
|
6478
|
+
if (fs13.existsSync(filePath)) {
|
|
6479
|
+
return JSON.parse(fs13.readFileSync(filePath, "utf-8"));
|
|
6323
6480
|
}
|
|
6324
6481
|
} catch {
|
|
6325
6482
|
}
|
|
6326
6483
|
return null;
|
|
6327
6484
|
}
|
|
6328
6485
|
function writeJson(filePath, data) {
|
|
6329
|
-
const dir =
|
|
6330
|
-
if (!
|
|
6331
|
-
|
|
6486
|
+
const dir = path15.dirname(filePath);
|
|
6487
|
+
if (!fs13.existsSync(dir)) fs13.mkdirSync(dir, { recursive: true });
|
|
6488
|
+
fs13.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
|
|
6332
6489
|
}
|
|
6333
6490
|
function isNode9Hook(cmd) {
|
|
6334
6491
|
if (!cmd) return false;
|
|
6335
6492
|
return /(?:^|[\s/\\])node9 (?:check|log)/.test(cmd) || /(?:^|[\s/\\])cli\.js (?:check|log)/.test(cmd);
|
|
6336
6493
|
}
|
|
6337
6494
|
function teardownClaude() {
|
|
6338
|
-
const homeDir2 =
|
|
6339
|
-
const hooksPath =
|
|
6340
|
-
const mcpPath =
|
|
6495
|
+
const homeDir2 = os12.homedir();
|
|
6496
|
+
const hooksPath = path15.join(homeDir2, ".claude", "settings.json");
|
|
6497
|
+
const mcpPath = path15.join(homeDir2, ".claude", ".mcp.json");
|
|
6341
6498
|
let changed = false;
|
|
6342
6499
|
const settings = readJson(hooksPath);
|
|
6343
6500
|
if (settings?.hooks) {
|
|
6344
|
-
for (const event of ["PreToolUse", "PostToolUse"]) {
|
|
6501
|
+
for (const event of ["PreToolUse", "PostToolUse", "UserPromptSubmit"]) {
|
|
6345
6502
|
const before = settings.hooks[event]?.length ?? 0;
|
|
6346
6503
|
settings.hooks[event] = settings.hooks[event]?.filter(
|
|
6347
6504
|
(m) => !m.hooks.some((h) => isNode9Hook(h.command))
|
|
@@ -6385,8 +6542,8 @@ function teardownClaude() {
|
|
|
6385
6542
|
}
|
|
6386
6543
|
}
|
|
6387
6544
|
function teardownGemini() {
|
|
6388
|
-
const homeDir2 =
|
|
6389
|
-
const settingsPath =
|
|
6545
|
+
const homeDir2 = os12.homedir();
|
|
6546
|
+
const settingsPath = path15.join(homeDir2, ".gemini", "settings.json");
|
|
6390
6547
|
const settings = readJson(settingsPath);
|
|
6391
6548
|
if (!settings) {
|
|
6392
6549
|
console.log(chalk.blue(" \u2139\uFE0F ~/.gemini/settings.json not found \u2014 nothing to remove"));
|
|
@@ -6429,8 +6586,8 @@ function teardownGemini() {
|
|
|
6429
6586
|
}
|
|
6430
6587
|
}
|
|
6431
6588
|
function teardownCursor() {
|
|
6432
|
-
const homeDir2 =
|
|
6433
|
-
const mcpPath =
|
|
6589
|
+
const homeDir2 = os12.homedir();
|
|
6590
|
+
const mcpPath = path15.join(homeDir2, ".cursor", "mcp.json");
|
|
6434
6591
|
const mcpConfig = readJson(mcpPath);
|
|
6435
6592
|
if (!mcpConfig?.mcpServers) {
|
|
6436
6593
|
console.log(chalk.blue(" \u2139\uFE0F ~/.cursor/mcp.json not found \u2014 nothing to remove"));
|
|
@@ -6461,9 +6618,10 @@ function teardownCursor() {
|
|
|
6461
6618
|
}
|
|
6462
6619
|
}
|
|
6463
6620
|
async function setupClaude() {
|
|
6464
|
-
|
|
6465
|
-
const
|
|
6466
|
-
const
|
|
6621
|
+
seedMcpPinsIfMissing();
|
|
6622
|
+
const homeDir2 = os12.homedir();
|
|
6623
|
+
const mcpPath = path15.join(homeDir2, ".claude", ".mcp.json");
|
|
6624
|
+
const hooksPath = path15.join(homeDir2, ".claude", "settings.json");
|
|
6467
6625
|
const claudeConfig = readJson(mcpPath) ?? {};
|
|
6468
6626
|
const settings = readJson(hooksPath) ?? {};
|
|
6469
6627
|
const servers = claudeConfig.mcpServers ?? {};
|
|
@@ -6522,6 +6680,33 @@ async function setupClaude() {
|
|
|
6522
6680
|
}
|
|
6523
6681
|
}
|
|
6524
6682
|
}
|
|
6683
|
+
const hasPromptHook = settings.hooks.UserPromptSubmit?.some(
|
|
6684
|
+
(m) => m.hooks.some((h) => isNode9Hook(h.command))
|
|
6685
|
+
);
|
|
6686
|
+
if (!hasPromptHook) {
|
|
6687
|
+
if (!settings.hooks.UserPromptSubmit) settings.hooks.UserPromptSubmit = [];
|
|
6688
|
+
settings.hooks.UserPromptSubmit.push({
|
|
6689
|
+
matcher: ".*",
|
|
6690
|
+
hooks: [{ type: "command", command: fullPathCommand("check"), timeout: 600 }]
|
|
6691
|
+
});
|
|
6692
|
+
console.log(chalk.green(" \u2705 UserPromptSubmit hook added \u2192 node9 check (prompt DLP)"));
|
|
6693
|
+
hooksChanged = true;
|
|
6694
|
+
anythingChanged = true;
|
|
6695
|
+
} else if (settings.hooks.UserPromptSubmit) {
|
|
6696
|
+
for (const matcher of settings.hooks.UserPromptSubmit) {
|
|
6697
|
+
for (const h of matcher.hooks) {
|
|
6698
|
+
const cmd = h.command ?? "";
|
|
6699
|
+
if (isNode9Hook(cmd) && isStaleHookCommand(cmd)) {
|
|
6700
|
+
h.command = fullPathCommand("check");
|
|
6701
|
+
console.log(
|
|
6702
|
+
chalk.yellow(" \u{1F527} UserPromptSubmit hook repaired (stale path \u2192 current binary)")
|
|
6703
|
+
);
|
|
6704
|
+
hooksChanged = true;
|
|
6705
|
+
anythingChanged = true;
|
|
6706
|
+
}
|
|
6707
|
+
}
|
|
6708
|
+
}
|
|
6709
|
+
}
|
|
6525
6710
|
if (!hasNode9McpServer(servers)) {
|
|
6526
6711
|
servers["node9"] = NODE9_MCP_SERVER_ENTRY;
|
|
6527
6712
|
claudeConfig.mcpServers = servers;
|
|
@@ -6586,8 +6771,9 @@ async function setupClaude() {
|
|
|
6586
6771
|
}
|
|
6587
6772
|
}
|
|
6588
6773
|
async function setupGemini() {
|
|
6589
|
-
|
|
6590
|
-
const
|
|
6774
|
+
seedMcpPinsIfMissing();
|
|
6775
|
+
const homeDir2 = os12.homedir();
|
|
6776
|
+
const settingsPath = path15.join(homeDir2, ".gemini", "settings.json");
|
|
6591
6777
|
const settings = readJson(settingsPath) ?? {};
|
|
6592
6778
|
const servers = settings.mcpServers ?? {};
|
|
6593
6779
|
let hooksChanged = false;
|
|
@@ -6682,9 +6868,9 @@ async function setupGemini() {
|
|
|
6682
6868
|
printDaemonTip();
|
|
6683
6869
|
}
|
|
6684
6870
|
}
|
|
6685
|
-
function claudeDesktopConfigPath(homeDir2 =
|
|
6871
|
+
function claudeDesktopConfigPath(homeDir2 = os12.homedir()) {
|
|
6686
6872
|
if (process.platform === "darwin") {
|
|
6687
|
-
return
|
|
6873
|
+
return path15.join(
|
|
6688
6874
|
homeDir2,
|
|
6689
6875
|
"Library",
|
|
6690
6876
|
"Application Support",
|
|
@@ -6693,18 +6879,18 @@ function claudeDesktopConfigPath(homeDir2 = os11.homedir()) {
|
|
|
6693
6879
|
);
|
|
6694
6880
|
}
|
|
6695
6881
|
if (process.platform === "linux") {
|
|
6696
|
-
return
|
|
6882
|
+
return path15.join(homeDir2, ".config", "Claude", "claude_desktop_config.json");
|
|
6697
6883
|
}
|
|
6698
6884
|
if (process.platform === "win32") {
|
|
6699
|
-
const appData = process.env.APPDATA ??
|
|
6700
|
-
return
|
|
6885
|
+
const appData = process.env.APPDATA ?? path15.join(homeDir2, "AppData", "Roaming");
|
|
6886
|
+
return path15.join(appData, "Claude", "claude_desktop_config.json");
|
|
6701
6887
|
}
|
|
6702
6888
|
return null;
|
|
6703
6889
|
}
|
|
6704
|
-
function detectAgents(homeDir2 =
|
|
6890
|
+
function detectAgents(homeDir2 = os12.homedir()) {
|
|
6705
6891
|
const exists = (p) => {
|
|
6706
6892
|
try {
|
|
6707
|
-
return
|
|
6893
|
+
return fs13.existsSync(p);
|
|
6708
6894
|
} catch (err2) {
|
|
6709
6895
|
const code = err2.code;
|
|
6710
6896
|
if (code !== "ENOENT") {
|
|
@@ -6716,18 +6902,19 @@ function detectAgents(homeDir2 = os11.homedir()) {
|
|
|
6716
6902
|
};
|
|
6717
6903
|
const desktopPath = claudeDesktopConfigPath(homeDir2);
|
|
6718
6904
|
return {
|
|
6719
|
-
claude: exists(
|
|
6720
|
-
gemini: exists(
|
|
6721
|
-
cursor: exists(
|
|
6722
|
-
codex: exists(
|
|
6723
|
-
windsurf: exists(
|
|
6724
|
-
vscode: exists(
|
|
6725
|
-
claudeDesktop: desktopPath !== null && exists(
|
|
6905
|
+
claude: exists(path15.join(homeDir2, ".claude")) || exists(path15.join(homeDir2, ".claude.json")),
|
|
6906
|
+
gemini: exists(path15.join(homeDir2, ".gemini")),
|
|
6907
|
+
cursor: exists(path15.join(homeDir2, ".cursor")),
|
|
6908
|
+
codex: exists(path15.join(homeDir2, ".codex")),
|
|
6909
|
+
windsurf: exists(path15.join(homeDir2, ".codeium", "windsurf")),
|
|
6910
|
+
vscode: exists(path15.join(homeDir2, ".vscode")),
|
|
6911
|
+
claudeDesktop: desktopPath !== null && exists(path15.dirname(desktopPath))
|
|
6726
6912
|
};
|
|
6727
6913
|
}
|
|
6728
6914
|
async function setupCursor() {
|
|
6729
|
-
|
|
6730
|
-
const
|
|
6915
|
+
seedMcpPinsIfMissing();
|
|
6916
|
+
const homeDir2 = os12.homedir();
|
|
6917
|
+
const mcpPath = path15.join(homeDir2, ".cursor", "mcp.json");
|
|
6731
6918
|
const mcpConfig = readJson(mcpPath) ?? {};
|
|
6732
6919
|
const servers = mcpConfig.mcpServers ?? {};
|
|
6733
6920
|
let anythingChanged = false;
|
|
@@ -6793,24 +6980,95 @@ async function setupCursor() {
|
|
|
6793
6980
|
}
|
|
6794
6981
|
function readToml(filePath) {
|
|
6795
6982
|
try {
|
|
6796
|
-
if (
|
|
6797
|
-
return parseToml(
|
|
6983
|
+
if (fs13.existsSync(filePath)) {
|
|
6984
|
+
return parseToml(fs13.readFileSync(filePath, "utf-8"));
|
|
6798
6985
|
}
|
|
6799
6986
|
} catch {
|
|
6800
6987
|
}
|
|
6801
6988
|
return null;
|
|
6802
6989
|
}
|
|
6803
6990
|
function writeToml(filePath, data) {
|
|
6804
|
-
const dir =
|
|
6805
|
-
if (!
|
|
6806
|
-
|
|
6991
|
+
const dir = path15.dirname(filePath);
|
|
6992
|
+
if (!fs13.existsSync(dir)) fs13.mkdirSync(dir, { recursive: true });
|
|
6993
|
+
fs13.writeFileSync(filePath, stringifyToml(data));
|
|
6807
6994
|
}
|
|
6808
6995
|
async function setupCodex() {
|
|
6809
|
-
|
|
6810
|
-
const
|
|
6996
|
+
seedMcpPinsIfMissing();
|
|
6997
|
+
const homeDir2 = os12.homedir();
|
|
6998
|
+
const configPath = path15.join(homeDir2, ".codex", "config.toml");
|
|
6999
|
+
const hooksPath = path15.join(homeDir2, ".codex", "hooks.json");
|
|
6811
7000
|
const config = readToml(configPath) ?? {};
|
|
6812
7001
|
const servers = config.mcp_servers ?? {};
|
|
6813
7002
|
let anythingChanged = false;
|
|
7003
|
+
const hooksFile = readJson(hooksPath) ?? {};
|
|
7004
|
+
if (!hooksFile.hooks) hooksFile.hooks = {};
|
|
7005
|
+
let hooksChanged = false;
|
|
7006
|
+
if (!hooksFile.hooks.PreToolUse) hooksFile.hooks.PreToolUse = [];
|
|
7007
|
+
for (const matcher of CODEX_PRE_TOOL_MATCHERS) {
|
|
7008
|
+
const existing = hooksFile.hooks.PreToolUse.find((m) => m.matcher === matcher);
|
|
7009
|
+
if (!existing) {
|
|
7010
|
+
hooksFile.hooks.PreToolUse.push({
|
|
7011
|
+
matcher,
|
|
7012
|
+
hooks: [{ type: "command", command: fullPathCommand("check"), timeout: 600 }]
|
|
7013
|
+
});
|
|
7014
|
+
hooksChanged = true;
|
|
7015
|
+
} else {
|
|
7016
|
+
for (const h of existing.hooks) {
|
|
7017
|
+
const cmd = h.command ?? "";
|
|
7018
|
+
if (isNode9Hook(cmd) && isStaleHookCommand(cmd)) {
|
|
7019
|
+
h.command = fullPathCommand("check");
|
|
7020
|
+
hooksChanged = true;
|
|
7021
|
+
}
|
|
7022
|
+
}
|
|
7023
|
+
}
|
|
7024
|
+
}
|
|
7025
|
+
if (!hooksFile.hooks.UserPromptSubmit) hooksFile.hooks.UserPromptSubmit = [];
|
|
7026
|
+
const hasPromptHook = hooksFile.hooks.UserPromptSubmit.some(
|
|
7027
|
+
(m) => m.hooks.some((h) => isNode9Hook(h.command))
|
|
7028
|
+
);
|
|
7029
|
+
if (!hasPromptHook) {
|
|
7030
|
+
hooksFile.hooks.UserPromptSubmit.push({
|
|
7031
|
+
hooks: [{ type: "command", command: fullPathCommand("check"), timeout: 600 }]
|
|
7032
|
+
});
|
|
7033
|
+
hooksChanged = true;
|
|
7034
|
+
} else {
|
|
7035
|
+
for (const m of hooksFile.hooks.UserPromptSubmit) {
|
|
7036
|
+
for (const h of m.hooks) {
|
|
7037
|
+
const cmd = h.command ?? "";
|
|
7038
|
+
if (isNode9Hook(cmd) && isStaleHookCommand(cmd)) {
|
|
7039
|
+
h.command = fullPathCommand("check");
|
|
7040
|
+
hooksChanged = true;
|
|
7041
|
+
}
|
|
7042
|
+
}
|
|
7043
|
+
}
|
|
7044
|
+
}
|
|
7045
|
+
if (!hooksFile.hooks.PostToolUse) hooksFile.hooks.PostToolUse = [];
|
|
7046
|
+
const hasPostHook = hooksFile.hooks.PostToolUse.some(
|
|
7047
|
+
(m) => m.hooks.some((h) => isNode9Hook(h.command))
|
|
7048
|
+
);
|
|
7049
|
+
if (!hasPostHook) {
|
|
7050
|
+
hooksFile.hooks.PostToolUse.push({
|
|
7051
|
+
matcher: ".*",
|
|
7052
|
+
hooks: [{ type: "command", command: fullPathCommand("log"), timeout: 600 }]
|
|
7053
|
+
});
|
|
7054
|
+
hooksChanged = true;
|
|
7055
|
+
} else {
|
|
7056
|
+
for (const m of hooksFile.hooks.PostToolUse) {
|
|
7057
|
+
for (const h of m.hooks) {
|
|
7058
|
+
const cmd = h.command ?? "";
|
|
7059
|
+
if (isNode9Hook(cmd) && isStaleHookCommand(cmd)) {
|
|
7060
|
+
h.command = fullPathCommand("log");
|
|
7061
|
+
hooksChanged = true;
|
|
7062
|
+
}
|
|
7063
|
+
}
|
|
7064
|
+
}
|
|
7065
|
+
}
|
|
7066
|
+
if (hooksChanged) {
|
|
7067
|
+
writeJson(hooksPath, hooksFile);
|
|
7068
|
+
console.log(chalk.green(" \u2705 Codex hooks added \u2192 node9 check / node9 log"));
|
|
7069
|
+
anythingChanged = true;
|
|
7070
|
+
}
|
|
7071
|
+
const hooksInstalled = (hooksFile.hooks?.PreToolUse?.length ?? 0) > 0;
|
|
6814
7072
|
if (!hasNode9McpServer(servers)) {
|
|
6815
7073
|
servers["node9"] = NODE9_MCP_SERVER_ENTRY;
|
|
6816
7074
|
config.mcp_servers = servers;
|
|
@@ -6850,30 +7108,63 @@ async function setupCodex() {
|
|
|
6850
7108
|
}
|
|
6851
7109
|
console.log("");
|
|
6852
7110
|
}
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
" \u26A0\uFE0F Note: Codex does not yet support native pre-execution hooks.\n MCP proxy wrapping is the only supported protection mode for Codex.\n Native bash and file operations are not monitored."
|
|
6856
|
-
)
|
|
6857
|
-
);
|
|
6858
|
-
console.log("");
|
|
6859
|
-
if (!anythingChanged && serversToWrap.length === 0) {
|
|
7111
|
+
const hooksDisabled = config.features?.hooks === false || config.codex_hooks === false;
|
|
7112
|
+
if (hooksDisabled) {
|
|
6860
7113
|
console.log(
|
|
6861
|
-
chalk.
|
|
6862
|
-
"\
|
|
7114
|
+
chalk.yellow(
|
|
7115
|
+
" \u26A0\uFE0F Codex hooks are disabled in ~/.codex/config.toml ([features].hooks = false).\n Re-enable hooks to activate Node9 shield evaluation on Bash, apply_patch,\n MCP tool calls, and prompt submissions. Until then, only MCP proxy wrapping\n is active."
|
|
7116
|
+
)
|
|
7117
|
+
);
|
|
7118
|
+
console.log("");
|
|
7119
|
+
}
|
|
7120
|
+
const printCodexTrustReminder = () => {
|
|
7121
|
+
console.log(
|
|
7122
|
+
chalk.yellow(
|
|
7123
|
+
" \u279C Open Codex and run /hooks to review and trust the Node9 entries.\n Until trusted, only MCP proxy wrapping is active."
|
|
6863
7124
|
)
|
|
6864
7125
|
);
|
|
7126
|
+
};
|
|
7127
|
+
if (!anythingChanged && serversToWrap.length === 0) {
|
|
7128
|
+
if (hooksInstalled) {
|
|
7129
|
+
console.log(chalk.blue("\u2139\uFE0F Codex hooks already installed."));
|
|
7130
|
+
printCodexTrustReminder();
|
|
7131
|
+
} else {
|
|
7132
|
+
console.log(
|
|
7133
|
+
chalk.blue(
|
|
7134
|
+
"\u2139\uFE0F No MCP servers found to wrap. Add MCP servers to ~/.codex/config.toml and re-run."
|
|
7135
|
+
)
|
|
7136
|
+
);
|
|
7137
|
+
}
|
|
6865
7138
|
printDaemonTip();
|
|
6866
7139
|
return;
|
|
6867
7140
|
}
|
|
6868
7141
|
if (anythingChanged) {
|
|
6869
|
-
console.log(chalk.green.bold("\u{1F6E1}\uFE0F Node9
|
|
7142
|
+
console.log(chalk.green.bold("\u{1F6E1}\uFE0F Node9 hooks installed for Codex."));
|
|
7143
|
+
printCodexTrustReminder();
|
|
6870
7144
|
console.log(chalk.gray(" Restart Codex for changes to take effect."));
|
|
6871
7145
|
printDaemonTip();
|
|
6872
7146
|
}
|
|
6873
7147
|
}
|
|
6874
7148
|
function teardownCodex() {
|
|
6875
|
-
const homeDir2 =
|
|
6876
|
-
const configPath =
|
|
7149
|
+
const homeDir2 = os12.homedir();
|
|
7150
|
+
const configPath = path15.join(homeDir2, ".codex", "config.toml");
|
|
7151
|
+
const hooksPath = path15.join(homeDir2, ".codex", "hooks.json");
|
|
7152
|
+
const hooksFile = readJson(hooksPath);
|
|
7153
|
+
if (hooksFile?.hooks) {
|
|
7154
|
+
let hooksChanged = false;
|
|
7155
|
+
for (const event of ["PreToolUse", "PostToolUse", "UserPromptSubmit"]) {
|
|
7156
|
+
const before = hooksFile.hooks[event]?.length ?? 0;
|
|
7157
|
+
hooksFile.hooks[event] = hooksFile.hooks[event]?.filter(
|
|
7158
|
+
(m) => !m.hooks.some((h) => isNode9Hook(h.command))
|
|
7159
|
+
);
|
|
7160
|
+
if ((hooksFile.hooks[event]?.length ?? 0) < before) hooksChanged = true;
|
|
7161
|
+
if (hooksFile.hooks[event]?.length === 0) delete hooksFile.hooks[event];
|
|
7162
|
+
}
|
|
7163
|
+
if (hooksChanged) {
|
|
7164
|
+
writeJson(hooksPath, hooksFile);
|
|
7165
|
+
console.log(chalk.green(" \u2705 Removed Node9 hooks from ~/.codex/hooks.json"));
|
|
7166
|
+
}
|
|
7167
|
+
}
|
|
6877
7168
|
const config = readToml(configPath);
|
|
6878
7169
|
if (!config?.mcp_servers) {
|
|
6879
7170
|
console.log(chalk.blue(" \u2139\uFE0F ~/.codex/config.toml not found \u2014 nothing to remove"));
|
|
@@ -6904,8 +7195,8 @@ function teardownCodex() {
|
|
|
6904
7195
|
}
|
|
6905
7196
|
}
|
|
6906
7197
|
function setupHud() {
|
|
6907
|
-
const homeDir2 =
|
|
6908
|
-
const hooksPath =
|
|
7198
|
+
const homeDir2 = os12.homedir();
|
|
7199
|
+
const hooksPath = path15.join(homeDir2, ".claude", "settings.json");
|
|
6909
7200
|
const settings = readJson(hooksPath) ?? {};
|
|
6910
7201
|
const hudCommand = fullPathCommand("hud");
|
|
6911
7202
|
const statusLineObj = { type: "command", command: hudCommand };
|
|
@@ -6931,8 +7222,8 @@ function setupHud() {
|
|
|
6931
7222
|
console.log(chalk.gray(" Restart Claude Code to activate."));
|
|
6932
7223
|
}
|
|
6933
7224
|
function teardownHud() {
|
|
6934
|
-
const homeDir2 =
|
|
6935
|
-
const hooksPath =
|
|
7225
|
+
const homeDir2 = os12.homedir();
|
|
7226
|
+
const hooksPath = path15.join(homeDir2, ".claude", "settings.json");
|
|
6936
7227
|
const settings = readJson(hooksPath);
|
|
6937
7228
|
if (!settings) {
|
|
6938
7229
|
console.log(chalk.blue(" \u2139\uFE0F ~/.claude/settings.json not found \u2014 nothing to remove"));
|
|
@@ -6950,8 +7241,9 @@ function teardownHud() {
|
|
|
6950
7241
|
console.log(chalk.gray(" Restart Claude Code for changes to take effect."));
|
|
6951
7242
|
}
|
|
6952
7243
|
async function setupWindsurf() {
|
|
6953
|
-
|
|
6954
|
-
const
|
|
7244
|
+
seedMcpPinsIfMissing();
|
|
7245
|
+
const homeDir2 = os12.homedir();
|
|
7246
|
+
const mcpPath = path15.join(homeDir2, ".codeium", "windsurf", "mcp_config.json");
|
|
6955
7247
|
const mcpConfig = readJson(mcpPath) ?? {};
|
|
6956
7248
|
const servers = mcpConfig.mcpServers ?? {};
|
|
6957
7249
|
let anythingChanged = false;
|
|
@@ -7011,8 +7303,8 @@ async function setupWindsurf() {
|
|
|
7011
7303
|
}
|
|
7012
7304
|
}
|
|
7013
7305
|
function teardownWindsurf() {
|
|
7014
|
-
const homeDir2 =
|
|
7015
|
-
const mcpPath =
|
|
7306
|
+
const homeDir2 = os12.homedir();
|
|
7307
|
+
const mcpPath = path15.join(homeDir2, ".codeium", "windsurf", "mcp_config.json");
|
|
7016
7308
|
const mcpConfig = readJson(mcpPath);
|
|
7017
7309
|
if (!mcpConfig?.mcpServers) {
|
|
7018
7310
|
console.log(
|
|
@@ -7053,8 +7345,9 @@ function hasNode9McpServerVSCode(servers) {
|
|
|
7053
7345
|
return !!entry && entry.command === "node9" && Array.isArray(entry.args) && entry.args[0] === "mcp-server";
|
|
7054
7346
|
}
|
|
7055
7347
|
async function setupVSCode() {
|
|
7056
|
-
|
|
7057
|
-
const
|
|
7348
|
+
seedMcpPinsIfMissing();
|
|
7349
|
+
const homeDir2 = os12.homedir();
|
|
7350
|
+
const mcpPath = path15.join(homeDir2, ".vscode", "mcp.json");
|
|
7058
7351
|
const mcpConfig = readJson(mcpPath) ?? {};
|
|
7059
7352
|
const servers = mcpConfig.servers ?? {};
|
|
7060
7353
|
let anythingChanged = false;
|
|
@@ -7115,8 +7408,8 @@ async function setupVSCode() {
|
|
|
7115
7408
|
}
|
|
7116
7409
|
}
|
|
7117
7410
|
function teardownVSCode() {
|
|
7118
|
-
const homeDir2 =
|
|
7119
|
-
const mcpPath =
|
|
7411
|
+
const homeDir2 = os12.homedir();
|
|
7412
|
+
const mcpPath = path15.join(homeDir2, ".vscode", "mcp.json");
|
|
7120
7413
|
const mcpConfig = readJson(mcpPath);
|
|
7121
7414
|
if (!mcpConfig?.servers) {
|
|
7122
7415
|
console.log(chalk.blue(" \u2139\uFE0F ~/.vscode/mcp.json not found \u2014 nothing to remove"));
|
|
@@ -7149,6 +7442,7 @@ function teardownVSCode() {
|
|
|
7149
7442
|
}
|
|
7150
7443
|
}
|
|
7151
7444
|
async function setupClaudeDesktop() {
|
|
7445
|
+
seedMcpPinsIfMissing();
|
|
7152
7446
|
const configPath = claudeDesktopConfigPath();
|
|
7153
7447
|
if (!configPath) {
|
|
7154
7448
|
console.log(chalk.yellow(" \u26A0\uFE0F Claude Desktop is not supported on this platform."));
|
|
@@ -7247,32 +7541,32 @@ function teardownClaudeDesktop() {
|
|
|
7247
7541
|
console.log(chalk.blue(" \u2139\uFE0F No Node9-wrapped MCP servers found in Claude Desktop config"));
|
|
7248
7542
|
}
|
|
7249
7543
|
}
|
|
7250
|
-
function getAgentsStatus(homeDir2 =
|
|
7544
|
+
function getAgentsStatus(homeDir2 = os12.homedir()) {
|
|
7251
7545
|
const detected = detectAgents(homeDir2);
|
|
7252
7546
|
const claudeWired = (() => {
|
|
7253
|
-
const settings = readJson(
|
|
7547
|
+
const settings = readJson(path15.join(homeDir2, ".claude", "settings.json"));
|
|
7254
7548
|
return !!settings?.hooks?.PreToolUse?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)));
|
|
7255
7549
|
})();
|
|
7256
7550
|
const geminiWired = (() => {
|
|
7257
|
-
const settings = readJson(
|
|
7551
|
+
const settings = readJson(path15.join(homeDir2, ".gemini", "settings.json"));
|
|
7258
7552
|
return !!settings?.hooks?.BeforeTool?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)));
|
|
7259
7553
|
})();
|
|
7260
7554
|
const cursorWired = (() => {
|
|
7261
|
-
const cfg = readJson(
|
|
7555
|
+
const cfg = readJson(path15.join(homeDir2, ".cursor", "mcp.json"));
|
|
7262
7556
|
return !!(cfg?.mcpServers && hasNode9McpServer(cfg.mcpServers));
|
|
7263
7557
|
})();
|
|
7264
7558
|
const codexWired = (() => {
|
|
7265
|
-
const cfg = readToml(
|
|
7559
|
+
const cfg = readToml(path15.join(homeDir2, ".codex", "config.toml"));
|
|
7266
7560
|
return !!(cfg?.mcp_servers && hasNode9McpServer(cfg.mcp_servers));
|
|
7267
7561
|
})();
|
|
7268
7562
|
const windsurfWired = (() => {
|
|
7269
7563
|
const cfg = readJson(
|
|
7270
|
-
|
|
7564
|
+
path15.join(homeDir2, ".codeium", "windsurf", "mcp_config.json")
|
|
7271
7565
|
);
|
|
7272
7566
|
return !!(cfg?.mcpServers && hasNode9McpServer(cfg.mcpServers));
|
|
7273
7567
|
})();
|
|
7274
7568
|
const vscodeWired = (() => {
|
|
7275
|
-
const cfg = readJson(
|
|
7569
|
+
const cfg = readJson(path15.join(homeDir2, ".vscode", "mcp.json"));
|
|
7276
7570
|
return !!(cfg?.servers && hasNode9McpServerVSCode(cfg.servers));
|
|
7277
7571
|
})();
|
|
7278
7572
|
return [
|
|
@@ -7332,11 +7626,13 @@ function getAgentsStatus(homeDir2 = os11.homedir()) {
|
|
|
7332
7626
|
}
|
|
7333
7627
|
];
|
|
7334
7628
|
}
|
|
7335
|
-
var NODE9_MCP_SERVER_ENTRY;
|
|
7629
|
+
var NODE9_MCP_SERVER_ENTRY, CODEX_PRE_TOOL_MATCHERS;
|
|
7336
7630
|
var init_setup = __esm({
|
|
7337
7631
|
"src/setup.ts"() {
|
|
7338
7632
|
"use strict";
|
|
7633
|
+
init_mcp_pin();
|
|
7339
7634
|
NODE9_MCP_SERVER_ENTRY = { command: "node9", args: ["mcp-server"] };
|
|
7635
|
+
CODEX_PRE_TOOL_MATCHERS = ["^Bash$", "^apply_patch$", "^mcp__.*"];
|
|
7340
7636
|
}
|
|
7341
7637
|
});
|
|
7342
7638
|
|
|
@@ -7528,85 +7824,85 @@ var init_scan_summary = __esm({
|
|
|
7528
7824
|
|
|
7529
7825
|
// src/cli/commands/blast.ts
|
|
7530
7826
|
import chalk2 from "chalk";
|
|
7531
|
-
import
|
|
7532
|
-
import
|
|
7533
|
-
import
|
|
7827
|
+
import fs14 from "fs";
|
|
7828
|
+
import path16 from "path";
|
|
7829
|
+
import os13 from "os";
|
|
7534
7830
|
function buildSensitivePaths(home, cwd) {
|
|
7535
7831
|
return [
|
|
7536
7832
|
{
|
|
7537
|
-
full:
|
|
7833
|
+
full: path16.join(home, ".ssh", "id_rsa"),
|
|
7538
7834
|
label: "~/.ssh/id_rsa",
|
|
7539
7835
|
description: "RSA private key \u2014 grants SSH access to your servers",
|
|
7540
7836
|
score: 20
|
|
7541
7837
|
},
|
|
7542
7838
|
{
|
|
7543
|
-
full:
|
|
7839
|
+
full: path16.join(home, ".ssh", "id_ed25519"),
|
|
7544
7840
|
label: "~/.ssh/id_ed25519",
|
|
7545
7841
|
description: "Ed25519 private key \u2014 grants SSH access to your servers",
|
|
7546
7842
|
score: 20
|
|
7547
7843
|
},
|
|
7548
7844
|
{
|
|
7549
|
-
full:
|
|
7845
|
+
full: path16.join(home, ".ssh", "id_ecdsa"),
|
|
7550
7846
|
label: "~/.ssh/id_ecdsa",
|
|
7551
7847
|
description: "ECDSA private key \u2014 grants SSH access to your servers",
|
|
7552
7848
|
score: 20
|
|
7553
7849
|
},
|
|
7554
7850
|
{
|
|
7555
|
-
full:
|
|
7851
|
+
full: path16.join(home, ".aws", "credentials"),
|
|
7556
7852
|
label: "~/.aws/credentials",
|
|
7557
7853
|
description: "AWS access keys \u2014 full cloud account access",
|
|
7558
7854
|
score: 20
|
|
7559
7855
|
},
|
|
7560
7856
|
{
|
|
7561
|
-
full:
|
|
7857
|
+
full: path16.join(home, ".aws", "config"),
|
|
7562
7858
|
label: "~/.aws/config",
|
|
7563
7859
|
description: "AWS configuration \u2014 account and region settings",
|
|
7564
7860
|
score: 5
|
|
7565
7861
|
},
|
|
7566
7862
|
{
|
|
7567
|
-
full:
|
|
7863
|
+
full: path16.join(home, ".config", "gcloud", "credentials.db"),
|
|
7568
7864
|
label: "~/.config/gcloud/credentials.db",
|
|
7569
7865
|
description: "Google Cloud credentials",
|
|
7570
7866
|
score: 15
|
|
7571
7867
|
},
|
|
7572
7868
|
{
|
|
7573
|
-
full:
|
|
7869
|
+
full: path16.join(home, ".docker", "config.json"),
|
|
7574
7870
|
label: "~/.docker/config.json",
|
|
7575
7871
|
description: "Docker registry auth tokens",
|
|
7576
7872
|
score: 10
|
|
7577
7873
|
},
|
|
7578
7874
|
{
|
|
7579
|
-
full:
|
|
7875
|
+
full: path16.join(home, ".netrc"),
|
|
7580
7876
|
label: "~/.netrc",
|
|
7581
7877
|
description: "FTP/HTTP credentials in plain text",
|
|
7582
7878
|
score: 15
|
|
7583
7879
|
},
|
|
7584
7880
|
{
|
|
7585
|
-
full:
|
|
7881
|
+
full: path16.join(home, ".npmrc"),
|
|
7586
7882
|
label: "~/.npmrc",
|
|
7587
7883
|
description: "npm auth token \u2014 can publish packages as you",
|
|
7588
7884
|
score: 10
|
|
7589
7885
|
},
|
|
7590
7886
|
{
|
|
7591
|
-
full:
|
|
7887
|
+
full: path16.join(home, ".node9", "credentials.json"),
|
|
7592
7888
|
label: "~/.node9/credentials.json",
|
|
7593
7889
|
description: "Node9 cloud API key",
|
|
7594
7890
|
score: 10
|
|
7595
7891
|
},
|
|
7596
7892
|
{
|
|
7597
|
-
full:
|
|
7893
|
+
full: path16.join(cwd, ".env"),
|
|
7598
7894
|
label: ".env (current folder)",
|
|
7599
7895
|
description: "App secrets \u2014 database passwords, API keys",
|
|
7600
7896
|
score: 20
|
|
7601
7897
|
},
|
|
7602
7898
|
{
|
|
7603
|
-
full:
|
|
7899
|
+
full: path16.join(cwd, ".env.local"),
|
|
7604
7900
|
label: ".env.local (current folder)",
|
|
7605
7901
|
description: "Local overrides \u2014 often contains real credentials",
|
|
7606
7902
|
score: 15
|
|
7607
7903
|
},
|
|
7608
7904
|
{
|
|
7609
|
-
full:
|
|
7905
|
+
full: path16.join(cwd, ".env.production"),
|
|
7610
7906
|
label: ".env.production (current folder)",
|
|
7611
7907
|
description: "Production secrets",
|
|
7612
7908
|
score: 20
|
|
@@ -7615,7 +7911,7 @@ function buildSensitivePaths(home, cwd) {
|
|
|
7615
7911
|
}
|
|
7616
7912
|
function isReadable(filePath) {
|
|
7617
7913
|
try {
|
|
7618
|
-
|
|
7914
|
+
fs14.accessSync(filePath, fs14.constants.R_OK);
|
|
7619
7915
|
return true;
|
|
7620
7916
|
} catch {
|
|
7621
7917
|
return false;
|
|
@@ -7628,13 +7924,13 @@ function scoreLabel(score) {
|
|
|
7628
7924
|
return chalk2.red.bold(`${score}/100 Critical`);
|
|
7629
7925
|
}
|
|
7630
7926
|
function runBlast() {
|
|
7631
|
-
const home =
|
|
7927
|
+
const home = os13.homedir();
|
|
7632
7928
|
const cwd = process.cwd();
|
|
7633
7929
|
const paths = buildSensitivePaths(home, cwd);
|
|
7634
7930
|
let scoreDeduction = 0;
|
|
7635
7931
|
const reachable = [];
|
|
7636
7932
|
for (const p of paths) {
|
|
7637
|
-
if (
|
|
7933
|
+
if (fs14.existsSync(p.full) && isReadable(p.full)) {
|
|
7638
7934
|
reachable.push(p);
|
|
7639
7935
|
scoreDeduction += p.score;
|
|
7640
7936
|
}
|
|
@@ -7652,7 +7948,7 @@ function runBlast() {
|
|
|
7652
7948
|
}
|
|
7653
7949
|
function registerBlastCommand(program2) {
|
|
7654
7950
|
program2.command("blast").description("Map what an AI agent can currently reach on this machine").action(() => {
|
|
7655
|
-
const home =
|
|
7951
|
+
const home = os13.homedir();
|
|
7656
7952
|
const cwd = process.cwd();
|
|
7657
7953
|
const { reachable, envFindings, score } = runBlast();
|
|
7658
7954
|
console.log("");
|
|
@@ -7828,17 +8124,17 @@ var init_scan_json = __esm({
|
|
|
7828
8124
|
});
|
|
7829
8125
|
|
|
7830
8126
|
// src/cli/render/scan-history.ts
|
|
7831
|
-
import
|
|
7832
|
-
import
|
|
7833
|
-
import
|
|
8127
|
+
import fs15 from "fs";
|
|
8128
|
+
import path17 from "path";
|
|
8129
|
+
import os14 from "os";
|
|
7834
8130
|
function defaultHistoryPath() {
|
|
7835
|
-
return
|
|
8131
|
+
return path17.join(os14.homedir(), ".node9", "scan-history.json");
|
|
7836
8132
|
}
|
|
7837
8133
|
function readPreviousScan(opts = {}) {
|
|
7838
8134
|
const filePath = opts.path ?? defaultHistoryPath();
|
|
7839
8135
|
try {
|
|
7840
|
-
if (!
|
|
7841
|
-
const raw =
|
|
8136
|
+
if (!fs15.existsSync(filePath)) return null;
|
|
8137
|
+
const raw = fs15.readFileSync(filePath, "utf8");
|
|
7842
8138
|
const parsed = JSON.parse(raw);
|
|
7843
8139
|
if (!Array.isArray(parsed) || parsed.length === 0) return null;
|
|
7844
8140
|
const last = parsed[parsed.length - 1];
|
|
@@ -7852,11 +8148,11 @@ function appendScanHistory(record, opts = {}) {
|
|
|
7852
8148
|
const filePath = opts.path ?? defaultHistoryPath();
|
|
7853
8149
|
const cap = opts.cap ?? SCAN_HISTORY_CAP;
|
|
7854
8150
|
try {
|
|
7855
|
-
|
|
8151
|
+
fs15.mkdirSync(path17.dirname(filePath), { recursive: true });
|
|
7856
8152
|
let history = [];
|
|
7857
|
-
if (
|
|
8153
|
+
if (fs15.existsSync(filePath)) {
|
|
7858
8154
|
try {
|
|
7859
|
-
const parsed = JSON.parse(
|
|
8155
|
+
const parsed = JSON.parse(fs15.readFileSync(filePath, "utf8"));
|
|
7860
8156
|
if (Array.isArray(parsed)) {
|
|
7861
8157
|
history = parsed.filter(isValidRecord);
|
|
7862
8158
|
}
|
|
@@ -7867,7 +8163,7 @@ function appendScanHistory(record, opts = {}) {
|
|
|
7867
8163
|
if (history.length > cap) {
|
|
7868
8164
|
history = history.slice(history.length - cap);
|
|
7869
8165
|
}
|
|
7870
|
-
|
|
8166
|
+
fs15.writeFileSync(filePath, JSON.stringify(history, null, 2));
|
|
7871
8167
|
} catch (err2) {
|
|
7872
8168
|
process.stderr.write(
|
|
7873
8169
|
`[node9] Warning: could not write scan-history.json: ${err2.message}
|
|
@@ -7898,15 +8194,15 @@ var init_scan_history = __esm({
|
|
|
7898
8194
|
});
|
|
7899
8195
|
|
|
7900
8196
|
// src/pricing/litellm.ts
|
|
7901
|
-
import
|
|
7902
|
-
import
|
|
7903
|
-
import
|
|
8197
|
+
import fs16 from "fs";
|
|
8198
|
+
import path18 from "path";
|
|
8199
|
+
import os15 from "os";
|
|
7904
8200
|
function normalizeModel(raw) {
|
|
7905
8201
|
return raw.replace(/-\d{8}$/, "").toLowerCase();
|
|
7906
8202
|
}
|
|
7907
8203
|
function readCache() {
|
|
7908
8204
|
try {
|
|
7909
|
-
const raw = JSON.parse(
|
|
8205
|
+
const raw = JSON.parse(fs16.readFileSync(CACHE_FILE(), "utf-8"));
|
|
7910
8206
|
if (typeof raw.fetchedAt !== "string" || typeof raw.prices !== "object" || raw.prices === null) {
|
|
7911
8207
|
return null;
|
|
7912
8208
|
}
|
|
@@ -7920,18 +8216,18 @@ function readCache() {
|
|
|
7920
8216
|
function writeCache(prices) {
|
|
7921
8217
|
try {
|
|
7922
8218
|
const target = CACHE_FILE();
|
|
7923
|
-
const dir =
|
|
7924
|
-
if (!
|
|
8219
|
+
const dir = path18.dirname(target);
|
|
8220
|
+
if (!fs16.existsSync(dir)) fs16.mkdirSync(dir, { recursive: true });
|
|
7925
8221
|
const tmp = target + ".tmp";
|
|
7926
8222
|
const body = {
|
|
7927
8223
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7928
8224
|
prices
|
|
7929
8225
|
};
|
|
7930
|
-
|
|
7931
|
-
|
|
8226
|
+
fs16.writeFileSync(tmp, JSON.stringify(body) + "\n", "utf-8");
|
|
8227
|
+
fs16.renameSync(tmp, target);
|
|
7932
8228
|
} catch (err2) {
|
|
7933
8229
|
try {
|
|
7934
|
-
|
|
8230
|
+
fs16.appendFileSync(
|
|
7935
8231
|
HOOK_DEBUG_LOG,
|
|
7936
8232
|
`[pricing] cache write failed: ${err2.message}
|
|
7937
8233
|
`
|
|
@@ -8054,7 +8350,7 @@ var init_litellm = __esm({
|
|
|
8054
8350
|
"gemini-2.0-flash": [75e-9, 3e-7, 0, 0],
|
|
8055
8351
|
"gemini-1.5-pro": [125e-8, 5e-6, 0, 0]
|
|
8056
8352
|
};
|
|
8057
|
-
CACHE_FILE = () =>
|
|
8353
|
+
CACHE_FILE = () => path18.join(os15.homedir(), ".node9", "model-pricing.json");
|
|
8058
8354
|
TTL_MS = 24 * 60 * 60 * 1e3;
|
|
8059
8355
|
memCache = null;
|
|
8060
8356
|
memCacheAt = 0;
|
|
@@ -8063,17 +8359,17 @@ var init_litellm = __esm({
|
|
|
8063
8359
|
});
|
|
8064
8360
|
|
|
8065
8361
|
// src/costSync.ts
|
|
8066
|
-
import
|
|
8067
|
-
import
|
|
8068
|
-
import
|
|
8362
|
+
import fs17 from "fs";
|
|
8363
|
+
import path19 from "path";
|
|
8364
|
+
import os16 from "os";
|
|
8069
8365
|
function decodeProjectDirName(dirName) {
|
|
8070
8366
|
return dirName.replace(/-/g, "/");
|
|
8071
8367
|
}
|
|
8072
8368
|
function parseJSONLFile(filePath, fallbackWorkingDir) {
|
|
8073
|
-
const runId =
|
|
8369
|
+
const runId = path19.basename(filePath, ".jsonl");
|
|
8074
8370
|
let content;
|
|
8075
8371
|
try {
|
|
8076
|
-
content =
|
|
8372
|
+
content = fs17.readFileSync(filePath, "utf8");
|
|
8077
8373
|
} catch {
|
|
8078
8374
|
return /* @__PURE__ */ new Map();
|
|
8079
8375
|
}
|
|
@@ -8129,34 +8425,34 @@ function parseJSONLFile(filePath, fallbackWorkingDir) {
|
|
|
8129
8425
|
return daily;
|
|
8130
8426
|
}
|
|
8131
8427
|
function collectEntries(sinceMs) {
|
|
8132
|
-
const projectsDir =
|
|
8133
|
-
if (!
|
|
8428
|
+
const projectsDir = path19.join(os16.homedir(), ".claude", "projects");
|
|
8429
|
+
if (!fs17.existsSync(projectsDir)) return [];
|
|
8134
8430
|
const combined = /* @__PURE__ */ new Map();
|
|
8135
8431
|
let dirs;
|
|
8136
8432
|
try {
|
|
8137
|
-
dirs =
|
|
8433
|
+
dirs = fs17.readdirSync(projectsDir);
|
|
8138
8434
|
} catch {
|
|
8139
8435
|
return [];
|
|
8140
8436
|
}
|
|
8141
8437
|
for (const dir of dirs) {
|
|
8142
|
-
const dirPath =
|
|
8438
|
+
const dirPath = path19.join(projectsDir, dir);
|
|
8143
8439
|
try {
|
|
8144
|
-
if (!
|
|
8440
|
+
if (!fs17.statSync(dirPath).isDirectory()) continue;
|
|
8145
8441
|
} catch {
|
|
8146
8442
|
continue;
|
|
8147
8443
|
}
|
|
8148
8444
|
let files;
|
|
8149
8445
|
try {
|
|
8150
|
-
files =
|
|
8446
|
+
files = fs17.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
|
|
8151
8447
|
} catch {
|
|
8152
8448
|
continue;
|
|
8153
8449
|
}
|
|
8154
8450
|
const fallbackWorkingDir = decodeProjectDirName(dir);
|
|
8155
8451
|
for (const file of files) {
|
|
8156
|
-
const filePath =
|
|
8452
|
+
const filePath = path19.join(dirPath, file);
|
|
8157
8453
|
if (sinceMs !== void 0) {
|
|
8158
8454
|
try {
|
|
8159
|
-
if (
|
|
8455
|
+
if (fs17.statSync(filePath).mtimeMs < sinceMs) continue;
|
|
8160
8456
|
} catch {
|
|
8161
8457
|
continue;
|
|
8162
8458
|
}
|
|
@@ -8186,10 +8482,10 @@ async function syncCost() {
|
|
|
8186
8482
|
if (entries.length === 0) return;
|
|
8187
8483
|
let username = "unknown";
|
|
8188
8484
|
try {
|
|
8189
|
-
username =
|
|
8485
|
+
username = os16.userInfo().username;
|
|
8190
8486
|
} catch {
|
|
8191
8487
|
}
|
|
8192
|
-
const machineId = `${
|
|
8488
|
+
const machineId = `${os16.hostname()}:${username}`;
|
|
8193
8489
|
try {
|
|
8194
8490
|
const res = await fetch(`${creds.apiUrl}/cost-sync`, {
|
|
8195
8491
|
method: "POST",
|
|
@@ -8198,11 +8494,11 @@ async function syncCost() {
|
|
|
8198
8494
|
signal: AbortSignal.timeout(15e3)
|
|
8199
8495
|
});
|
|
8200
8496
|
if (!res.ok) {
|
|
8201
|
-
|
|
8497
|
+
fs17.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
|
|
8202
8498
|
`);
|
|
8203
8499
|
}
|
|
8204
8500
|
} catch (err2) {
|
|
8205
|
-
|
|
8501
|
+
fs17.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
|
|
8206
8502
|
`);
|
|
8207
8503
|
}
|
|
8208
8504
|
}
|
|
@@ -8238,9 +8534,9 @@ __export(scan_watermark_exports, {
|
|
|
8238
8534
|
tickForensicBroadcast: () => tickForensicBroadcast,
|
|
8239
8535
|
tickScanWatcher: () => tickScanWatcher
|
|
8240
8536
|
});
|
|
8241
|
-
import
|
|
8242
|
-
import
|
|
8243
|
-
import
|
|
8537
|
+
import fs18 from "fs";
|
|
8538
|
+
import os17 from "os";
|
|
8539
|
+
import path20 from "path";
|
|
8244
8540
|
import readline from "readline";
|
|
8245
8541
|
function freshWatermark() {
|
|
8246
8542
|
return {
|
|
@@ -8253,7 +8549,7 @@ function freshWatermark() {
|
|
|
8253
8549
|
function loadWatermark() {
|
|
8254
8550
|
let raw;
|
|
8255
8551
|
try {
|
|
8256
|
-
raw =
|
|
8552
|
+
raw = fs18.readFileSync(WATERMARK_FILE(), "utf-8");
|
|
8257
8553
|
} catch {
|
|
8258
8554
|
return { status: "fresh", wm: freshWatermark() };
|
|
8259
8555
|
}
|
|
@@ -8305,28 +8601,28 @@ function loadWatermark() {
|
|
|
8305
8601
|
function saveWatermark(wm) {
|
|
8306
8602
|
if (wm.schemaVersion > WATERMARK_SCHEMA_VERSION) return;
|
|
8307
8603
|
const target = WATERMARK_FILE();
|
|
8308
|
-
const dir =
|
|
8309
|
-
if (!
|
|
8604
|
+
const dir = path20.dirname(target);
|
|
8605
|
+
if (!fs18.existsSync(dir)) fs18.mkdirSync(dir, { recursive: true });
|
|
8310
8606
|
const tmp = target + ".tmp";
|
|
8311
|
-
|
|
8312
|
-
|
|
8607
|
+
fs18.writeFileSync(tmp, JSON.stringify(wm, null, 2) + "\n", "utf-8");
|
|
8608
|
+
fs18.renameSync(tmp, target);
|
|
8313
8609
|
}
|
|
8314
8610
|
function listJsonlFiles() {
|
|
8315
8611
|
const root = PROJECTS_DIR();
|
|
8316
|
-
if (!
|
|
8612
|
+
if (!fs18.existsSync(root)) return [];
|
|
8317
8613
|
const out = [];
|
|
8318
|
-
for (const entry of
|
|
8614
|
+
for (const entry of fs18.readdirSync(root, { withFileTypes: true })) {
|
|
8319
8615
|
if (!entry.isDirectory()) continue;
|
|
8320
|
-
const projectDir =
|
|
8616
|
+
const projectDir = path20.join(root, entry.name);
|
|
8321
8617
|
let inner;
|
|
8322
8618
|
try {
|
|
8323
|
-
inner =
|
|
8619
|
+
inner = fs18.readdirSync(projectDir, { withFileTypes: true });
|
|
8324
8620
|
} catch {
|
|
8325
8621
|
continue;
|
|
8326
8622
|
}
|
|
8327
8623
|
for (const file of inner) {
|
|
8328
8624
|
if (file.isFile() && file.name.endsWith(".jsonl")) {
|
|
8329
|
-
out.push(
|
|
8625
|
+
out.push(path20.join(projectDir, file.name));
|
|
8330
8626
|
}
|
|
8331
8627
|
}
|
|
8332
8628
|
}
|
|
@@ -8334,7 +8630,7 @@ function listJsonlFiles() {
|
|
|
8334
8630
|
}
|
|
8335
8631
|
function fileSize(p) {
|
|
8336
8632
|
try {
|
|
8337
|
-
return
|
|
8633
|
+
return fs18.statSync(p).size;
|
|
8338
8634
|
} catch {
|
|
8339
8635
|
return 0;
|
|
8340
8636
|
}
|
|
@@ -8342,7 +8638,7 @@ function fileSize(p) {
|
|
|
8342
8638
|
async function scanDelta(filePath, fromByte, onLine) {
|
|
8343
8639
|
const size = fileSize(filePath);
|
|
8344
8640
|
if (size <= fromByte) return fromByte;
|
|
8345
|
-
const stream =
|
|
8641
|
+
const stream = fs18.createReadStream(filePath, {
|
|
8346
8642
|
start: fromByte,
|
|
8347
8643
|
end: size - 1,
|
|
8348
8644
|
highWaterMark: 64 * 1024
|
|
@@ -8454,7 +8750,7 @@ async function tickForensicBroadcast(offsets) {
|
|
|
8454
8750
|
continue;
|
|
8455
8751
|
}
|
|
8456
8752
|
if (size <= offset) continue;
|
|
8457
|
-
const sessionId =
|
|
8753
|
+
const sessionId = path20.basename(file, ".jsonl");
|
|
8458
8754
|
const newOffset = await scanDelta(file, offset, (obj, lineIndex) => {
|
|
8459
8755
|
out.push(...extractFindingsFromLine(obj, sessionId, lineIndex));
|
|
8460
8756
|
});
|
|
@@ -8513,7 +8809,7 @@ function emptyTick(uploadAs) {
|
|
|
8513
8809
|
function readRawWatermarkPreservingOffsets() {
|
|
8514
8810
|
let raw;
|
|
8515
8811
|
try {
|
|
8516
|
-
raw =
|
|
8812
|
+
raw = fs18.readFileSync(WATERMARK_FILE(), "utf-8");
|
|
8517
8813
|
} catch {
|
|
8518
8814
|
return null;
|
|
8519
8815
|
}
|
|
@@ -8547,13 +8843,13 @@ async function runActualTick(wm) {
|
|
|
8547
8843
|
if (!known) {
|
|
8548
8844
|
let mtimeMs = 0;
|
|
8549
8845
|
try {
|
|
8550
|
-
mtimeMs =
|
|
8846
|
+
mtimeMs = fs18.statSync(filePath).mtime.getTime();
|
|
8551
8847
|
} catch {
|
|
8552
8848
|
continue;
|
|
8553
8849
|
}
|
|
8554
8850
|
if (mtimeMs >= watermarkCreatedAt) {
|
|
8555
8851
|
filesNew++;
|
|
8556
|
-
const sessionId2 =
|
|
8852
|
+
const sessionId2 = path20.basename(filePath, ".jsonl");
|
|
8557
8853
|
const newScannedTo2 = await scanDelta(filePath, 0, (obj, lineIndex) => {
|
|
8558
8854
|
totalToolCalls++;
|
|
8559
8855
|
toolCallsBySession[sessionId2] = (toolCallsBySession[sessionId2] ?? 0) + 1;
|
|
@@ -8571,7 +8867,7 @@ async function runActualTick(wm) {
|
|
|
8571
8867
|
filesSkipped++;
|
|
8572
8868
|
continue;
|
|
8573
8869
|
}
|
|
8574
|
-
const sessionId =
|
|
8870
|
+
const sessionId = path20.basename(filePath, ".jsonl");
|
|
8575
8871
|
const newScannedTo = await scanDelta(filePath, known.scannedTo, (obj, lineIndex) => {
|
|
8576
8872
|
totalToolCalls++;
|
|
8577
8873
|
toolCallsBySession[sessionId] = (toolCallsBySession[sessionId] ?? 0) + 1;
|
|
@@ -8599,8 +8895,8 @@ var init_scan_watermark = __esm({
|
|
|
8599
8895
|
"use strict";
|
|
8600
8896
|
init_dlp();
|
|
8601
8897
|
init_dist();
|
|
8602
|
-
PROJECTS_DIR = () =>
|
|
8603
|
-
WATERMARK_FILE = () =>
|
|
8898
|
+
PROJECTS_DIR = () => path20.join(os17.homedir(), ".claude", "projects");
|
|
8899
|
+
WATERMARK_FILE = () => path20.join(os17.homedir(), ".node9", "scan-watermark.json");
|
|
8604
8900
|
MAX_LINE_BYTES = 2 * 1024 * 1024;
|
|
8605
8901
|
WATERMARK_SCHEMA_VERSION = 2;
|
|
8606
8902
|
LONG_OUTPUT_THRESHOLD_BYTES2 = LONG_OUTPUT_THRESHOLD_BYTES;
|
|
@@ -8615,10 +8911,10 @@ __export(scan_upload_history_exports, {
|
|
|
8615
8911
|
parseSinceCutoff: () => parseSinceCutoff,
|
|
8616
8912
|
runUploadHistory: () => runUploadHistory
|
|
8617
8913
|
});
|
|
8618
|
-
import
|
|
8914
|
+
import fs19 from "fs";
|
|
8619
8915
|
import https from "https";
|
|
8620
|
-
import
|
|
8621
|
-
import
|
|
8916
|
+
import os18 from "os";
|
|
8917
|
+
import path21 from "path";
|
|
8622
8918
|
import chalk4 from "chalk";
|
|
8623
8919
|
function emptySignals2() {
|
|
8624
8920
|
return {
|
|
@@ -8653,40 +8949,40 @@ function parseSinceCutoff(raw, now = /* @__PURE__ */ new Date()) {
|
|
|
8653
8949
|
return now.getTime() - 90 * 864e5;
|
|
8654
8950
|
}
|
|
8655
8951
|
function* iterateJsonlFiles(cutoffMs) {
|
|
8656
|
-
const projectsDir =
|
|
8952
|
+
const projectsDir = path21.join(os18.homedir(), ".claude", "projects");
|
|
8657
8953
|
let dirs;
|
|
8658
8954
|
try {
|
|
8659
|
-
dirs =
|
|
8955
|
+
dirs = fs19.readdirSync(projectsDir);
|
|
8660
8956
|
} catch {
|
|
8661
8957
|
return;
|
|
8662
8958
|
}
|
|
8663
8959
|
for (const dir of dirs) {
|
|
8664
|
-
const dirPath =
|
|
8960
|
+
const dirPath = path21.join(projectsDir, dir);
|
|
8665
8961
|
let stats;
|
|
8666
8962
|
try {
|
|
8667
|
-
stats =
|
|
8963
|
+
stats = fs19.statSync(dirPath);
|
|
8668
8964
|
} catch {
|
|
8669
8965
|
continue;
|
|
8670
8966
|
}
|
|
8671
8967
|
if (!stats.isDirectory()) continue;
|
|
8672
8968
|
let files;
|
|
8673
8969
|
try {
|
|
8674
|
-
files =
|
|
8970
|
+
files = fs19.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
|
|
8675
8971
|
} catch {
|
|
8676
8972
|
continue;
|
|
8677
8973
|
}
|
|
8678
8974
|
for (const file of files) {
|
|
8679
|
-
const filePath =
|
|
8975
|
+
const filePath = path21.join(dirPath, file);
|
|
8680
8976
|
let mtime = 0;
|
|
8681
8977
|
try {
|
|
8682
|
-
mtime =
|
|
8978
|
+
mtime = fs19.statSync(filePath).mtimeMs;
|
|
8683
8979
|
} catch {
|
|
8684
8980
|
continue;
|
|
8685
8981
|
}
|
|
8686
8982
|
if (mtime < cutoffMs) continue;
|
|
8687
8983
|
yield {
|
|
8688
8984
|
filePath,
|
|
8689
|
-
sessionId:
|
|
8985
|
+
sessionId: path21.basename(file, ".jsonl"),
|
|
8690
8986
|
projectDir: dir
|
|
8691
8987
|
};
|
|
8692
8988
|
}
|
|
@@ -8739,7 +9035,7 @@ async function runUploadHistory(opts) {
|
|
|
8739
9035
|
filesScanned++;
|
|
8740
9036
|
let content;
|
|
8741
9037
|
try {
|
|
8742
|
-
content =
|
|
9038
|
+
content = fs19.readFileSync(filePath, "utf8");
|
|
8743
9039
|
} catch {
|
|
8744
9040
|
continue;
|
|
8745
9041
|
}
|
|
@@ -8825,10 +9121,10 @@ async function runUploadHistory(opts) {
|
|
|
8825
9121
|
const costUrl = creds.apiUrl.endsWith("/policies/sync") ? creds.apiUrl.replace(/\/policies\/sync$/, "/cost-sync") : `${creds.apiUrl.replace(/\/$/, "")}/cost-sync`;
|
|
8826
9122
|
let username = "unknown";
|
|
8827
9123
|
try {
|
|
8828
|
-
username =
|
|
9124
|
+
username = os18.userInfo().username;
|
|
8829
9125
|
} catch {
|
|
8830
9126
|
}
|
|
8831
|
-
const machineId = `${
|
|
9127
|
+
const machineId = `${os18.hostname()}:${username}`;
|
|
8832
9128
|
await postJson(costUrl, creds.apiKey, {
|
|
8833
9129
|
machineId,
|
|
8834
9130
|
entries: dailyEntries
|
|
@@ -8902,9 +9198,9 @@ var init_scan_upload_history = __esm({
|
|
|
8902
9198
|
|
|
8903
9199
|
// src/cli/commands/scan.ts
|
|
8904
9200
|
import chalk5 from "chalk";
|
|
8905
|
-
import
|
|
8906
|
-
import
|
|
8907
|
-
import
|
|
9201
|
+
import fs20 from "fs";
|
|
9202
|
+
import path22 from "path";
|
|
9203
|
+
import os19 from "os";
|
|
8908
9204
|
import stringWidth2 from "string-width";
|
|
8909
9205
|
function claudeModelPrice(model) {
|
|
8910
9206
|
const base = model.replace(/@.*$/, "").replace(/-\d{8}$/, "");
|
|
@@ -9131,14 +9427,14 @@ function buildRuleSources() {
|
|
|
9131
9427
|
}
|
|
9132
9428
|
function countScanFiles() {
|
|
9133
9429
|
let total = 0;
|
|
9134
|
-
const claudeDir =
|
|
9135
|
-
if (
|
|
9430
|
+
const claudeDir = path22.join(os19.homedir(), ".claude", "projects");
|
|
9431
|
+
if (fs20.existsSync(claudeDir)) {
|
|
9136
9432
|
try {
|
|
9137
|
-
for (const proj of
|
|
9138
|
-
const p =
|
|
9433
|
+
for (const proj of fs20.readdirSync(claudeDir)) {
|
|
9434
|
+
const p = path22.join(claudeDir, proj);
|
|
9139
9435
|
try {
|
|
9140
|
-
if (!
|
|
9141
|
-
total +=
|
|
9436
|
+
if (!fs20.statSync(p).isDirectory()) continue;
|
|
9437
|
+
total += fs20.readdirSync(p).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).length;
|
|
9142
9438
|
} catch {
|
|
9143
9439
|
continue;
|
|
9144
9440
|
}
|
|
@@ -9146,17 +9442,17 @@ function countScanFiles() {
|
|
|
9146
9442
|
} catch {
|
|
9147
9443
|
}
|
|
9148
9444
|
}
|
|
9149
|
-
const geminiDir =
|
|
9150
|
-
if (
|
|
9445
|
+
const geminiDir = path22.join(os19.homedir(), ".gemini", "tmp");
|
|
9446
|
+
if (fs20.existsSync(geminiDir)) {
|
|
9151
9447
|
try {
|
|
9152
|
-
for (const slug of
|
|
9153
|
-
const p =
|
|
9448
|
+
for (const slug of fs20.readdirSync(geminiDir)) {
|
|
9449
|
+
const p = path22.join(geminiDir, slug);
|
|
9154
9450
|
try {
|
|
9155
|
-
if (!
|
|
9156
|
-
const chatsDir =
|
|
9157
|
-
if (
|
|
9451
|
+
if (!fs20.statSync(p).isDirectory()) continue;
|
|
9452
|
+
const chatsDir = path22.join(p, "chats");
|
|
9453
|
+
if (fs20.existsSync(chatsDir)) {
|
|
9158
9454
|
try {
|
|
9159
|
-
total +=
|
|
9455
|
+
total += fs20.readdirSync(chatsDir).filter((f) => f.endsWith(".json")).length;
|
|
9160
9456
|
} catch {
|
|
9161
9457
|
}
|
|
9162
9458
|
}
|
|
@@ -9167,22 +9463,22 @@ function countScanFiles() {
|
|
|
9167
9463
|
} catch {
|
|
9168
9464
|
}
|
|
9169
9465
|
}
|
|
9170
|
-
const codexDir =
|
|
9171
|
-
if (
|
|
9466
|
+
const codexDir = path22.join(os19.homedir(), ".codex", "sessions");
|
|
9467
|
+
if (fs20.existsSync(codexDir)) {
|
|
9172
9468
|
try {
|
|
9173
|
-
for (const year of
|
|
9174
|
-
const yp =
|
|
9469
|
+
for (const year of fs20.readdirSync(codexDir)) {
|
|
9470
|
+
const yp = path22.join(codexDir, year);
|
|
9175
9471
|
try {
|
|
9176
|
-
if (!
|
|
9177
|
-
for (const month of
|
|
9178
|
-
const mp =
|
|
9472
|
+
if (!fs20.statSync(yp).isDirectory()) continue;
|
|
9473
|
+
for (const month of fs20.readdirSync(yp)) {
|
|
9474
|
+
const mp = path22.join(yp, month);
|
|
9179
9475
|
try {
|
|
9180
|
-
if (!
|
|
9181
|
-
for (const day of
|
|
9182
|
-
const dp =
|
|
9476
|
+
if (!fs20.statSync(mp).isDirectory()) continue;
|
|
9477
|
+
for (const day of fs20.readdirSync(mp)) {
|
|
9478
|
+
const dp = path22.join(mp, day);
|
|
9183
9479
|
try {
|
|
9184
|
-
if (!
|
|
9185
|
-
total +=
|
|
9480
|
+
if (!fs20.statSync(dp).isDirectory()) continue;
|
|
9481
|
+
total += fs20.readdirSync(dp).filter((f) => f.endsWith(".jsonl")).length;
|
|
9186
9482
|
} catch {
|
|
9187
9483
|
continue;
|
|
9188
9484
|
}
|
|
@@ -9218,7 +9514,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
9218
9514
|
const sessionId = file.replace(/\.jsonl$/, "");
|
|
9219
9515
|
let raw;
|
|
9220
9516
|
try {
|
|
9221
|
-
raw =
|
|
9517
|
+
raw = fs20.readFileSync(path22.join(projPath, file), "utf-8");
|
|
9222
9518
|
} catch {
|
|
9223
9519
|
return;
|
|
9224
9520
|
}
|
|
@@ -9270,7 +9566,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
9270
9566
|
if (block.type !== "tool_result") continue;
|
|
9271
9567
|
const filePath = block.tool_use_id ? toolUseFilePaths.get(block.tool_use_id) : void 0;
|
|
9272
9568
|
if (filePath) {
|
|
9273
|
-
const ext =
|
|
9569
|
+
const ext = path22.extname(filePath).toLowerCase();
|
|
9274
9570
|
if (CODE_EXTENSIONS.has(ext)) continue;
|
|
9275
9571
|
}
|
|
9276
9572
|
const resultText = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map((c) => c.text ?? "").join("\n") : null;
|
|
@@ -9327,7 +9623,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
9327
9623
|
const rawCmd = String(input.command ?? "").trimStart();
|
|
9328
9624
|
if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd)) continue;
|
|
9329
9625
|
const inputFilePath = typeof input.file_path === "string" ? input.file_path : "";
|
|
9330
|
-
const inputFileExt = inputFilePath ?
|
|
9626
|
+
const inputFileExt = inputFilePath ? path22.extname(inputFilePath).toLowerCase() : "";
|
|
9331
9627
|
if (CODE_EXTENSIONS.has(inputFileExt)) continue;
|
|
9332
9628
|
const dlpMatch = scanArgs(input);
|
|
9333
9629
|
if (dlpMatch) {
|
|
@@ -9424,19 +9720,19 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
9424
9720
|
}
|
|
9425
9721
|
}
|
|
9426
9722
|
function processClaudeProject(proj, projectsDir, ruleSources, startDate, result, dedup, onProgress, onLine) {
|
|
9427
|
-
const projPath =
|
|
9723
|
+
const projPath = path22.join(projectsDir, proj);
|
|
9428
9724
|
try {
|
|
9429
|
-
if (!
|
|
9725
|
+
if (!fs20.statSync(projPath).isDirectory()) return;
|
|
9430
9726
|
} catch {
|
|
9431
9727
|
return;
|
|
9432
9728
|
}
|
|
9433
|
-
const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(
|
|
9729
|
+
const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(os19.homedir(), "~")).slice(
|
|
9434
9730
|
0,
|
|
9435
9731
|
40
|
|
9436
9732
|
);
|
|
9437
9733
|
let files;
|
|
9438
9734
|
try {
|
|
9439
|
-
files =
|
|
9735
|
+
files = fs20.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
9440
9736
|
} catch {
|
|
9441
9737
|
return;
|
|
9442
9738
|
}
|
|
@@ -9470,12 +9766,12 @@ function emptyClaudeScan() {
|
|
|
9470
9766
|
};
|
|
9471
9767
|
}
|
|
9472
9768
|
function scanClaudeHistory(startDate, onProgress, onLine) {
|
|
9473
|
-
const projectsDir =
|
|
9769
|
+
const projectsDir = path22.join(os19.homedir(), ".claude", "projects");
|
|
9474
9770
|
const result = emptyClaudeScan();
|
|
9475
|
-
if (!
|
|
9771
|
+
if (!fs20.existsSync(projectsDir)) return result;
|
|
9476
9772
|
let projDirs;
|
|
9477
9773
|
try {
|
|
9478
|
-
projDirs =
|
|
9774
|
+
projDirs = fs20.readdirSync(projectsDir);
|
|
9479
9775
|
} catch {
|
|
9480
9776
|
return result;
|
|
9481
9777
|
}
|
|
@@ -9496,7 +9792,7 @@ function scanClaudeHistory(startDate, onProgress, onLine) {
|
|
|
9496
9792
|
return result;
|
|
9497
9793
|
}
|
|
9498
9794
|
function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
9499
|
-
const tmpDir =
|
|
9795
|
+
const tmpDir = path22.join(os19.homedir(), ".gemini", "tmp");
|
|
9500
9796
|
const result = {
|
|
9501
9797
|
filesScanned: 0,
|
|
9502
9798
|
sessions: 0,
|
|
@@ -9511,33 +9807,33 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
9511
9807
|
sessionsWithEarlySecrets: 0
|
|
9512
9808
|
};
|
|
9513
9809
|
const dedup = emptyScanDedup();
|
|
9514
|
-
if (!
|
|
9810
|
+
if (!fs20.existsSync(tmpDir)) return result;
|
|
9515
9811
|
let slugDirs;
|
|
9516
9812
|
try {
|
|
9517
|
-
slugDirs =
|
|
9813
|
+
slugDirs = fs20.readdirSync(tmpDir);
|
|
9518
9814
|
} catch {
|
|
9519
9815
|
return result;
|
|
9520
9816
|
}
|
|
9521
9817
|
const ruleSources = buildRuleSources();
|
|
9522
9818
|
for (const slug of slugDirs) {
|
|
9523
|
-
const slugPath =
|
|
9819
|
+
const slugPath = path22.join(tmpDir, slug);
|
|
9524
9820
|
try {
|
|
9525
|
-
if (!
|
|
9821
|
+
if (!fs20.statSync(slugPath).isDirectory()) continue;
|
|
9526
9822
|
} catch {
|
|
9527
9823
|
continue;
|
|
9528
9824
|
}
|
|
9529
9825
|
let projLabel = stripTerminalEscapes(slug).slice(0, 40);
|
|
9530
9826
|
try {
|
|
9531
9827
|
projLabel = stripTerminalEscapes(
|
|
9532
|
-
|
|
9533
|
-
).replace(
|
|
9828
|
+
fs20.readFileSync(path22.join(slugPath, ".project_root"), "utf-8").trim()
|
|
9829
|
+
).replace(os19.homedir(), "~").slice(0, 40);
|
|
9534
9830
|
} catch {
|
|
9535
9831
|
}
|
|
9536
|
-
const chatsDir =
|
|
9537
|
-
if (!
|
|
9832
|
+
const chatsDir = path22.join(slugPath, "chats");
|
|
9833
|
+
if (!fs20.existsSync(chatsDir)) continue;
|
|
9538
9834
|
let chatFiles;
|
|
9539
9835
|
try {
|
|
9540
|
-
chatFiles =
|
|
9836
|
+
chatFiles = fs20.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
|
|
9541
9837
|
} catch {
|
|
9542
9838
|
continue;
|
|
9543
9839
|
}
|
|
@@ -9547,7 +9843,7 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
9547
9843
|
const sessionId = chatFile.replace(/\.json$/, "");
|
|
9548
9844
|
let raw;
|
|
9549
9845
|
try {
|
|
9550
|
-
raw =
|
|
9846
|
+
raw = fs20.readFileSync(path22.join(chatsDir, chatFile), "utf-8");
|
|
9551
9847
|
} catch {
|
|
9552
9848
|
continue;
|
|
9553
9849
|
}
|
|
@@ -9709,7 +10005,7 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
9709
10005
|
return result;
|
|
9710
10006
|
}
|
|
9711
10007
|
function scanCodexHistory(startDate, onProgress, onLine) {
|
|
9712
|
-
const sessionsBase =
|
|
10008
|
+
const sessionsBase = path22.join(os19.homedir(), ".codex", "sessions");
|
|
9713
10009
|
const result = {
|
|
9714
10010
|
filesScanned: 0,
|
|
9715
10011
|
sessions: 0,
|
|
@@ -9724,32 +10020,32 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
9724
10020
|
sessionsWithEarlySecrets: 0
|
|
9725
10021
|
};
|
|
9726
10022
|
const dedup = emptyScanDedup();
|
|
9727
|
-
if (!
|
|
10023
|
+
if (!fs20.existsSync(sessionsBase)) return result;
|
|
9728
10024
|
const jsonlFiles = [];
|
|
9729
10025
|
try {
|
|
9730
|
-
for (const year of
|
|
9731
|
-
const yearPath =
|
|
10026
|
+
for (const year of fs20.readdirSync(sessionsBase)) {
|
|
10027
|
+
const yearPath = path22.join(sessionsBase, year);
|
|
9732
10028
|
try {
|
|
9733
|
-
if (!
|
|
10029
|
+
if (!fs20.statSync(yearPath).isDirectory()) continue;
|
|
9734
10030
|
} catch {
|
|
9735
10031
|
continue;
|
|
9736
10032
|
}
|
|
9737
|
-
for (const month of
|
|
9738
|
-
const monthPath =
|
|
10033
|
+
for (const month of fs20.readdirSync(yearPath)) {
|
|
10034
|
+
const monthPath = path22.join(yearPath, month);
|
|
9739
10035
|
try {
|
|
9740
|
-
if (!
|
|
10036
|
+
if (!fs20.statSync(monthPath).isDirectory()) continue;
|
|
9741
10037
|
} catch {
|
|
9742
10038
|
continue;
|
|
9743
10039
|
}
|
|
9744
|
-
for (const day of
|
|
9745
|
-
const dayPath =
|
|
10040
|
+
for (const day of fs20.readdirSync(monthPath)) {
|
|
10041
|
+
const dayPath = path22.join(monthPath, day);
|
|
9746
10042
|
try {
|
|
9747
|
-
if (!
|
|
10043
|
+
if (!fs20.statSync(dayPath).isDirectory()) continue;
|
|
9748
10044
|
} catch {
|
|
9749
10045
|
continue;
|
|
9750
10046
|
}
|
|
9751
|
-
for (const file of
|
|
9752
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(
|
|
10047
|
+
for (const file of fs20.readdirSync(dayPath)) {
|
|
10048
|
+
if (file.endsWith(".jsonl")) jsonlFiles.push(path22.join(dayPath, file));
|
|
9753
10049
|
}
|
|
9754
10050
|
}
|
|
9755
10051
|
}
|
|
@@ -9763,7 +10059,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
9763
10059
|
onProgress?.(result.filesScanned);
|
|
9764
10060
|
let lines;
|
|
9765
10061
|
try {
|
|
9766
|
-
lines =
|
|
10062
|
+
lines = fs20.readFileSync(filePath, "utf-8").split("\n");
|
|
9767
10063
|
} catch {
|
|
9768
10064
|
continue;
|
|
9769
10065
|
}
|
|
@@ -9789,7 +10085,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
9789
10085
|
sessionId = String(payload["id"] ?? filePath);
|
|
9790
10086
|
startTime = String(payload["timestamp"] ?? "");
|
|
9791
10087
|
const cwd = String(payload["cwd"] ?? "");
|
|
9792
|
-
projLabel = stripTerminalEscapes(cwd.replace(
|
|
10088
|
+
projLabel = stripTerminalEscapes(cwd.replace(os19.homedir(), "~")).slice(0, 40);
|
|
9793
10089
|
continue;
|
|
9794
10090
|
}
|
|
9795
10091
|
if (entry.type === "event_msg" && payload["type"] === "token_count") {
|
|
@@ -9942,17 +10238,17 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
9942
10238
|
return result;
|
|
9943
10239
|
}
|
|
9944
10240
|
function scanShellConfig() {
|
|
9945
|
-
const home =
|
|
10241
|
+
const home = os19.homedir();
|
|
9946
10242
|
const configFiles = [".zshrc", ".bashrc", ".bash_profile", ".profile"].map(
|
|
9947
|
-
(f) =>
|
|
10243
|
+
(f) => path22.join(home, f)
|
|
9948
10244
|
);
|
|
9949
10245
|
const findings = [];
|
|
9950
10246
|
const seen = /* @__PURE__ */ new Set();
|
|
9951
10247
|
for (const filePath of configFiles) {
|
|
9952
|
-
if (!
|
|
10248
|
+
if (!fs20.existsSync(filePath)) continue;
|
|
9953
10249
|
let lines;
|
|
9954
10250
|
try {
|
|
9955
|
-
lines =
|
|
10251
|
+
lines = fs20.readFileSync(filePath, "utf-8").split("\n");
|
|
9956
10252
|
} catch {
|
|
9957
10253
|
continue;
|
|
9958
10254
|
}
|
|
@@ -10732,7 +11028,7 @@ function registerScanCommand(program2) {
|
|
|
10732
11028
|
if (!drillDown) {
|
|
10733
11029
|
const useInk2 = !options.classic;
|
|
10734
11030
|
if (useInk2) {
|
|
10735
|
-
const scanInkPath =
|
|
11031
|
+
const scanInkPath = path22.join(__dirname, "scan-ink.mjs");
|
|
10736
11032
|
const dynamicImport = new Function("id", "return import(id)");
|
|
10737
11033
|
const mod = await dynamicImport(`file://${scanInkPath}`);
|
|
10738
11034
|
const rangeLabel2 = options.all ? "all time" : `last ${options.days ?? 90} days`;
|
|
@@ -11150,8 +11446,8 @@ var init_suggestion_tracker = __esm({
|
|
|
11150
11446
|
});
|
|
11151
11447
|
|
|
11152
11448
|
// src/daemon/taint-store.ts
|
|
11153
|
-
import
|
|
11154
|
-
import
|
|
11449
|
+
import fs21 from "fs";
|
|
11450
|
+
import path23 from "path";
|
|
11155
11451
|
var DEFAULT_TTL_MS, TaintStore;
|
|
11156
11452
|
var init_taint_store = __esm({
|
|
11157
11453
|
"src/daemon/taint-store.ts"() {
|
|
@@ -11220,9 +11516,9 @@ var init_taint_store = __esm({
|
|
|
11220
11516
|
/** Resolve to absolute path, falling back to path.resolve if file doesn't exist yet. */
|
|
11221
11517
|
_resolve(filePath) {
|
|
11222
11518
|
try {
|
|
11223
|
-
return
|
|
11519
|
+
return fs21.realpathSync.native(path23.resolve(filePath));
|
|
11224
11520
|
} catch {
|
|
11225
|
-
return
|
|
11521
|
+
return path23.resolve(filePath);
|
|
11226
11522
|
}
|
|
11227
11523
|
}
|
|
11228
11524
|
};
|
|
@@ -11339,14 +11635,14 @@ var init_session_history = __esm({
|
|
|
11339
11635
|
|
|
11340
11636
|
// src/daemon/state.ts
|
|
11341
11637
|
import net2 from "net";
|
|
11342
|
-
import
|
|
11343
|
-
import
|
|
11344
|
-
import
|
|
11638
|
+
import fs22 from "fs";
|
|
11639
|
+
import path24 from "path";
|
|
11640
|
+
import os20 from "os";
|
|
11345
11641
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
11346
11642
|
function loadInsightCounts() {
|
|
11347
11643
|
try {
|
|
11348
|
-
if (!
|
|
11349
|
-
const data = JSON.parse(
|
|
11644
|
+
if (!fs22.existsSync(INSIGHT_COUNTS_FILE)) return;
|
|
11645
|
+
const data = JSON.parse(fs22.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
|
|
11350
11646
|
for (const [tool, count] of Object.entries(data)) {
|
|
11351
11647
|
if (typeof count === "number" && count > 0) insightCounts.set(tool, count);
|
|
11352
11648
|
}
|
|
@@ -11385,23 +11681,23 @@ function markRejectionHandlerRegistered() {
|
|
|
11385
11681
|
daemonRejectionHandlerRegistered = true;
|
|
11386
11682
|
}
|
|
11387
11683
|
function atomicWriteSync2(filePath, data, options) {
|
|
11388
|
-
const dir =
|
|
11389
|
-
if (!
|
|
11684
|
+
const dir = path24.dirname(filePath);
|
|
11685
|
+
if (!fs22.existsSync(dir)) fs22.mkdirSync(dir, { recursive: true });
|
|
11390
11686
|
const tmpPath = `${filePath}.${randomUUID3()}.tmp`;
|
|
11391
11687
|
try {
|
|
11392
|
-
|
|
11688
|
+
fs22.writeFileSync(tmpPath, data, options);
|
|
11393
11689
|
} catch (err2) {
|
|
11394
11690
|
try {
|
|
11395
|
-
|
|
11691
|
+
fs22.unlinkSync(tmpPath);
|
|
11396
11692
|
} catch {
|
|
11397
11693
|
}
|
|
11398
11694
|
throw err2;
|
|
11399
11695
|
}
|
|
11400
11696
|
try {
|
|
11401
|
-
|
|
11697
|
+
fs22.renameSync(tmpPath, filePath);
|
|
11402
11698
|
} catch (err2) {
|
|
11403
11699
|
try {
|
|
11404
|
-
|
|
11700
|
+
fs22.unlinkSync(tmpPath);
|
|
11405
11701
|
} catch {
|
|
11406
11702
|
}
|
|
11407
11703
|
throw err2;
|
|
@@ -11425,16 +11721,16 @@ function appendAuditLog(data) {
|
|
|
11425
11721
|
decision: data.decision,
|
|
11426
11722
|
source: "daemon"
|
|
11427
11723
|
};
|
|
11428
|
-
const dir =
|
|
11429
|
-
if (!
|
|
11430
|
-
|
|
11724
|
+
const dir = path24.dirname(AUDIT_LOG_FILE);
|
|
11725
|
+
if (!fs22.existsSync(dir)) fs22.mkdirSync(dir, { recursive: true });
|
|
11726
|
+
fs22.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
|
|
11431
11727
|
} catch {
|
|
11432
11728
|
}
|
|
11433
11729
|
}
|
|
11434
11730
|
function getAuditHistory(limit = 20) {
|
|
11435
11731
|
try {
|
|
11436
|
-
if (!
|
|
11437
|
-
const lines =
|
|
11732
|
+
if (!fs22.existsSync(AUDIT_LOG_FILE)) return [];
|
|
11733
|
+
const lines = fs22.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
|
|
11438
11734
|
if (lines.length === 1 && lines[0] === "") return [];
|
|
11439
11735
|
return lines.slice(-limit).map((l) => JSON.parse(l)).reverse();
|
|
11440
11736
|
} catch {
|
|
@@ -11443,7 +11739,7 @@ function getAuditHistory(limit = 20) {
|
|
|
11443
11739
|
}
|
|
11444
11740
|
function getOrgName() {
|
|
11445
11741
|
try {
|
|
11446
|
-
if (
|
|
11742
|
+
if (fs22.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
|
|
11447
11743
|
} catch {
|
|
11448
11744
|
}
|
|
11449
11745
|
return null;
|
|
@@ -11451,8 +11747,8 @@ function getOrgName() {
|
|
|
11451
11747
|
function writeGlobalSetting(key, value) {
|
|
11452
11748
|
let config = {};
|
|
11453
11749
|
try {
|
|
11454
|
-
if (
|
|
11455
|
-
config = JSON.parse(
|
|
11750
|
+
if (fs22.existsSync(GLOBAL_CONFIG_FILE)) {
|
|
11751
|
+
config = JSON.parse(fs22.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
|
|
11456
11752
|
}
|
|
11457
11753
|
} catch {
|
|
11458
11754
|
}
|
|
@@ -11464,8 +11760,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
|
|
|
11464
11760
|
try {
|
|
11465
11761
|
let trust = { entries: [] };
|
|
11466
11762
|
try {
|
|
11467
|
-
if (
|
|
11468
|
-
trust = JSON.parse(
|
|
11763
|
+
if (fs22.existsSync(TRUST_FILE2))
|
|
11764
|
+
trust = JSON.parse(fs22.readFileSync(TRUST_FILE2, "utf-8"));
|
|
11469
11765
|
} catch {
|
|
11470
11766
|
}
|
|
11471
11767
|
trust.entries = trust.entries.filter(
|
|
@@ -11482,8 +11778,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
|
|
|
11482
11778
|
}
|
|
11483
11779
|
function readPersistentDecisions() {
|
|
11484
11780
|
try {
|
|
11485
|
-
if (
|
|
11486
|
-
return JSON.parse(
|
|
11781
|
+
if (fs22.existsSync(DECISIONS_FILE)) {
|
|
11782
|
+
return JSON.parse(fs22.readFileSync(DECISIONS_FILE, "utf-8"));
|
|
11487
11783
|
}
|
|
11488
11784
|
} catch {
|
|
11489
11785
|
}
|
|
@@ -11511,7 +11807,7 @@ function estimateToolCost(tool, args) {
|
|
|
11511
11807
|
const filePath = a.file_path ?? a.path;
|
|
11512
11808
|
if (filePath) {
|
|
11513
11809
|
try {
|
|
11514
|
-
const bytes =
|
|
11810
|
+
const bytes = fs22.statSync(filePath).size;
|
|
11515
11811
|
return bytes / BYTES_PER_TOKEN / 1e6 * INPUT_PRICE_PER_1M;
|
|
11516
11812
|
} catch {
|
|
11517
11813
|
}
|
|
@@ -11582,7 +11878,7 @@ function abandonPending() {
|
|
|
11582
11878
|
});
|
|
11583
11879
|
if (autoStarted) {
|
|
11584
11880
|
try {
|
|
11585
|
-
|
|
11881
|
+
fs22.unlinkSync(DAEMON_PID_FILE);
|
|
11586
11882
|
} catch {
|
|
11587
11883
|
}
|
|
11588
11884
|
setTimeout(() => {
|
|
@@ -11593,8 +11889,8 @@ function abandonPending() {
|
|
|
11593
11889
|
}
|
|
11594
11890
|
function logActivitySocket(msg) {
|
|
11595
11891
|
try {
|
|
11596
|
-
|
|
11597
|
-
|
|
11892
|
+
fs22.appendFileSync(
|
|
11893
|
+
path24.join(homeDir, ".node9", "hook-debug.log"),
|
|
11598
11894
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] [activity-socket] ${msg}
|
|
11599
11895
|
`
|
|
11600
11896
|
);
|
|
@@ -11616,13 +11912,13 @@ function shouldRebind(now = Date.now()) {
|
|
|
11616
11912
|
function startActivitySocket() {
|
|
11617
11913
|
bindActivitySocket();
|
|
11618
11914
|
activityHealthInterval = setInterval(() => {
|
|
11619
|
-
if (!
|
|
11915
|
+
if (!fs22.existsSync(ACTIVITY_SOCKET_PATH2)) attemptRebind("health-probe");
|
|
11620
11916
|
}, ACTIVITY_HEALTH_PROBE_MS);
|
|
11621
11917
|
activityHealthInterval.unref();
|
|
11622
11918
|
process.on("exit", () => {
|
|
11623
11919
|
if (activityHealthInterval) clearInterval(activityHealthInterval);
|
|
11624
11920
|
try {
|
|
11625
|
-
|
|
11921
|
+
fs22.unlinkSync(ACTIVITY_SOCKET_PATH2);
|
|
11626
11922
|
} catch {
|
|
11627
11923
|
}
|
|
11628
11924
|
});
|
|
@@ -11650,7 +11946,7 @@ function attemptRebind(reason) {
|
|
|
11650
11946
|
}
|
|
11651
11947
|
function bindActivitySocket() {
|
|
11652
11948
|
try {
|
|
11653
|
-
|
|
11949
|
+
fs22.unlinkSync(ACTIVITY_SOCKET_PATH2);
|
|
11654
11950
|
} catch {
|
|
11655
11951
|
}
|
|
11656
11952
|
const ACTIVITY_MAX_BYTES = 1024 * 1024;
|
|
@@ -11763,14 +12059,14 @@ var init_state2 = __esm({
|
|
|
11763
12059
|
init_taint_store();
|
|
11764
12060
|
init_session_counters();
|
|
11765
12061
|
init_session_history();
|
|
11766
|
-
homeDir =
|
|
11767
|
-
DAEMON_PID_FILE =
|
|
11768
|
-
DECISIONS_FILE =
|
|
11769
|
-
AUDIT_LOG_FILE =
|
|
11770
|
-
TRUST_FILE2 =
|
|
11771
|
-
GLOBAL_CONFIG_FILE =
|
|
11772
|
-
CREDENTIALS_FILE =
|
|
11773
|
-
INSIGHT_COUNTS_FILE =
|
|
12062
|
+
homeDir = os20.homedir();
|
|
12063
|
+
DAEMON_PID_FILE = path24.join(homeDir, ".node9", "daemon.pid");
|
|
12064
|
+
DECISIONS_FILE = path24.join(homeDir, ".node9", "decisions.json");
|
|
12065
|
+
AUDIT_LOG_FILE = path24.join(homeDir, ".node9", "audit.log");
|
|
12066
|
+
TRUST_FILE2 = path24.join(homeDir, ".node9", "trust.json");
|
|
12067
|
+
GLOBAL_CONFIG_FILE = path24.join(homeDir, ".node9", "config.json");
|
|
12068
|
+
CREDENTIALS_FILE = path24.join(homeDir, ".node9", "credentials.json");
|
|
12069
|
+
INSIGHT_COUNTS_FILE = path24.join(homeDir, ".node9", "insight-counts.json");
|
|
11774
12070
|
pending = /* @__PURE__ */ new Map();
|
|
11775
12071
|
sseClients = /* @__PURE__ */ new Set();
|
|
11776
12072
|
suggestionTracker = new SuggestionTracker(3);
|
|
@@ -11787,7 +12083,7 @@ var init_state2 = __esm({
|
|
|
11787
12083
|
"2h": 2 * 60 * 6e4
|
|
11788
12084
|
};
|
|
11789
12085
|
autoStarted = process.env.NODE9_AUTO_STARTED === "1";
|
|
11790
|
-
ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" :
|
|
12086
|
+
ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path24.join(os20.tmpdir(), "node9-activity.sock");
|
|
11791
12087
|
ACTIVITY_RING_SIZE = 100;
|
|
11792
12088
|
activityRing = [];
|
|
11793
12089
|
LARGE_RESPONSE_RING_SIZE = 20;
|
|
@@ -11826,10 +12122,10 @@ var init_state2 = __esm({
|
|
|
11826
12122
|
});
|
|
11827
12123
|
|
|
11828
12124
|
// src/daemon/sync.ts
|
|
11829
|
-
import
|
|
12125
|
+
import fs23 from "fs";
|
|
11830
12126
|
import https2 from "https";
|
|
11831
|
-
import
|
|
11832
|
-
import
|
|
12127
|
+
import os21 from "os";
|
|
12128
|
+
import path25 from "path";
|
|
11833
12129
|
function emptySignals3() {
|
|
11834
12130
|
return {
|
|
11835
12131
|
dlpFindings: 0,
|
|
@@ -11869,8 +12165,8 @@ function readCredentials() {
|
|
|
11869
12165
|
};
|
|
11870
12166
|
}
|
|
11871
12167
|
try {
|
|
11872
|
-
const credPath =
|
|
11873
|
-
const creds = JSON.parse(
|
|
12168
|
+
const credPath = path25.join(os21.homedir(), ".node9", "credentials.json");
|
|
12169
|
+
const creds = JSON.parse(fs23.readFileSync(credPath, "utf-8"));
|
|
11874
12170
|
const profileName = process.env.NODE9_PROFILE ?? "default";
|
|
11875
12171
|
const profile = creds[profileName];
|
|
11876
12172
|
if (typeof profile?.apiKey === "string" && profile.apiKey.length > 0) {
|
|
@@ -11896,7 +12192,7 @@ function readCredentials() {
|
|
|
11896
12192
|
}
|
|
11897
12193
|
function readCachedEtag() {
|
|
11898
12194
|
try {
|
|
11899
|
-
const raw = JSON.parse(
|
|
12195
|
+
const raw = JSON.parse(fs23.readFileSync(rulesCacheFile(), "utf-8"));
|
|
11900
12196
|
return typeof raw.etag === "string" ? raw.etag : void 0;
|
|
11901
12197
|
} catch {
|
|
11902
12198
|
return void 0;
|
|
@@ -11957,9 +12253,9 @@ function extractRules(body) {
|
|
|
11957
12253
|
return [];
|
|
11958
12254
|
}
|
|
11959
12255
|
function writeCache2(cache) {
|
|
11960
|
-
const dir =
|
|
11961
|
-
if (!
|
|
11962
|
-
|
|
12256
|
+
const dir = path25.dirname(rulesCacheFile());
|
|
12257
|
+
if (!fs23.existsSync(dir)) fs23.mkdirSync(dir, { recursive: true });
|
|
12258
|
+
fs23.writeFileSync(rulesCacheFile(), JSON.stringify(cache, null, 2) + "\n", "utf-8");
|
|
11963
12259
|
}
|
|
11964
12260
|
async function syncOnce() {
|
|
11965
12261
|
const creds = readCredentials();
|
|
@@ -12116,7 +12412,7 @@ async function runCloudSync() {
|
|
|
12116
12412
|
}
|
|
12117
12413
|
function getCloudSyncStatus() {
|
|
12118
12414
|
try {
|
|
12119
|
-
const raw = JSON.parse(
|
|
12415
|
+
const raw = JSON.parse(fs23.readFileSync(rulesCacheFile(), "utf-8"));
|
|
12120
12416
|
if (!Array.isArray(raw.rules) || typeof raw.fetchedAt !== "string") return { cached: false };
|
|
12121
12417
|
return {
|
|
12122
12418
|
cached: true,
|
|
@@ -12133,7 +12429,7 @@ function getCloudSyncStatus() {
|
|
|
12133
12429
|
}
|
|
12134
12430
|
function getCloudRules() {
|
|
12135
12431
|
try {
|
|
12136
|
-
const raw = JSON.parse(
|
|
12432
|
+
const raw = JSON.parse(fs23.readFileSync(rulesCacheFile(), "utf-8"));
|
|
12137
12433
|
return Array.isArray(raw.rules) ? raw.rules : null;
|
|
12138
12434
|
} catch {
|
|
12139
12435
|
return null;
|
|
@@ -12189,7 +12485,7 @@ var init_sync = __esm({
|
|
|
12189
12485
|
loop: "loops",
|
|
12190
12486
|
"long-output-redacted": "longOutputRedactions"
|
|
12191
12487
|
};
|
|
12192
|
-
rulesCacheFile = () =>
|
|
12488
|
+
rulesCacheFile = () => path25.join(os21.homedir(), ".node9", "rules-cache.json");
|
|
12193
12489
|
DEFAULT_API_URL = "https://api.node9.ai/api/v1/intercept/policies/sync";
|
|
12194
12490
|
DEFAULT_INTERVAL_HOURS = 5;
|
|
12195
12491
|
MIN_INTERVAL_HOURS = 1;
|
|
@@ -12200,73 +12496,73 @@ var init_sync = __esm({
|
|
|
12200
12496
|
});
|
|
12201
12497
|
|
|
12202
12498
|
// src/daemon/dlp-scanner.ts
|
|
12203
|
-
import
|
|
12204
|
-
import
|
|
12205
|
-
import
|
|
12499
|
+
import fs24 from "fs";
|
|
12500
|
+
import path26 from "path";
|
|
12501
|
+
import os22 from "os";
|
|
12206
12502
|
function loadIndex() {
|
|
12207
12503
|
try {
|
|
12208
|
-
return JSON.parse(
|
|
12504
|
+
return JSON.parse(fs24.readFileSync(INDEX_FILE, "utf-8"));
|
|
12209
12505
|
} catch {
|
|
12210
12506
|
return {};
|
|
12211
12507
|
}
|
|
12212
12508
|
}
|
|
12213
12509
|
function saveIndex(index) {
|
|
12214
12510
|
try {
|
|
12215
|
-
|
|
12511
|
+
fs24.writeFileSync(INDEX_FILE, JSON.stringify(index), { encoding: "utf-8", mode: 384 });
|
|
12216
12512
|
} catch {
|
|
12217
12513
|
}
|
|
12218
12514
|
}
|
|
12219
12515
|
function appendAuditEntry(entry) {
|
|
12220
12516
|
try {
|
|
12221
|
-
|
|
12517
|
+
fs24.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
|
|
12222
12518
|
} catch {
|
|
12223
12519
|
}
|
|
12224
12520
|
}
|
|
12225
12521
|
function runDlpScan() {
|
|
12226
|
-
if (!
|
|
12522
|
+
if (!fs24.existsSync(PROJECTS_DIR2)) return;
|
|
12227
12523
|
const index = loadIndex();
|
|
12228
12524
|
let updated = false;
|
|
12229
12525
|
let projDirs;
|
|
12230
12526
|
try {
|
|
12231
|
-
projDirs =
|
|
12527
|
+
projDirs = fs24.readdirSync(PROJECTS_DIR2);
|
|
12232
12528
|
} catch {
|
|
12233
12529
|
return;
|
|
12234
12530
|
}
|
|
12235
12531
|
for (const proj of projDirs) {
|
|
12236
|
-
const projPath =
|
|
12532
|
+
const projPath = path26.join(PROJECTS_DIR2, proj);
|
|
12237
12533
|
try {
|
|
12238
|
-
if (!
|
|
12239
|
-
const real =
|
|
12240
|
-
if (!real.startsWith(PROJECTS_DIR2 +
|
|
12534
|
+
if (!fs24.lstatSync(projPath).isDirectory()) continue;
|
|
12535
|
+
const real = fs24.realpathSync(projPath);
|
|
12536
|
+
if (!real.startsWith(PROJECTS_DIR2 + path26.sep) && real !== PROJECTS_DIR2) continue;
|
|
12241
12537
|
} catch {
|
|
12242
12538
|
continue;
|
|
12243
12539
|
}
|
|
12244
12540
|
let files;
|
|
12245
12541
|
try {
|
|
12246
|
-
files =
|
|
12542
|
+
files = fs24.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
12247
12543
|
} catch {
|
|
12248
12544
|
continue;
|
|
12249
12545
|
}
|
|
12250
12546
|
for (const file of files) {
|
|
12251
|
-
const filePath =
|
|
12547
|
+
const filePath = path26.join(projPath, file);
|
|
12252
12548
|
const lastOffset = index[filePath] ?? 0;
|
|
12253
12549
|
let size;
|
|
12254
12550
|
try {
|
|
12255
|
-
size =
|
|
12551
|
+
size = fs24.statSync(filePath).size;
|
|
12256
12552
|
} catch {
|
|
12257
12553
|
continue;
|
|
12258
12554
|
}
|
|
12259
12555
|
if (size <= lastOffset) continue;
|
|
12260
12556
|
let fd;
|
|
12261
12557
|
try {
|
|
12262
|
-
fd =
|
|
12558
|
+
fd = fs24.openSync(filePath, "r");
|
|
12263
12559
|
} catch {
|
|
12264
12560
|
continue;
|
|
12265
12561
|
}
|
|
12266
12562
|
try {
|
|
12267
12563
|
const chunkSize = size - lastOffset;
|
|
12268
12564
|
const buf = Buffer.alloc(chunkSize);
|
|
12269
|
-
|
|
12565
|
+
fs24.readSync(fd, buf, 0, chunkSize, lastOffset);
|
|
12270
12566
|
const chunk = buf.toString("utf-8");
|
|
12271
12567
|
for (const line of chunk.split("\n")) {
|
|
12272
12568
|
if (!line.trim()) continue;
|
|
@@ -12286,7 +12582,7 @@ function runDlpScan() {
|
|
|
12286
12582
|
if (typeof text !== "string") continue;
|
|
12287
12583
|
const match = scanText(text);
|
|
12288
12584
|
if (!match) continue;
|
|
12289
|
-
const projLabel = decodeURIComponent(proj).replace(
|
|
12585
|
+
const projLabel = decodeURIComponent(proj).replace(os22.homedir(), "~").slice(0, 40);
|
|
12290
12586
|
const ts = entry.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
12291
12587
|
appendAuditEntry({
|
|
12292
12588
|
ts,
|
|
@@ -12311,7 +12607,7 @@ Run: node9 report --period 30d`
|
|
|
12311
12607
|
updated = true;
|
|
12312
12608
|
} finally {
|
|
12313
12609
|
try {
|
|
12314
|
-
|
|
12610
|
+
fs24.closeSync(fd);
|
|
12315
12611
|
} catch {
|
|
12316
12612
|
}
|
|
12317
12613
|
}
|
|
@@ -12344,23 +12640,23 @@ var init_dlp_scanner = __esm({
|
|
|
12344
12640
|
init_dlp();
|
|
12345
12641
|
init_native();
|
|
12346
12642
|
init_state2();
|
|
12347
|
-
INDEX_FILE =
|
|
12348
|
-
PROJECTS_DIR2 =
|
|
12643
|
+
INDEX_FILE = path26.join(os22.homedir(), ".node9", "dlp-index.json");
|
|
12644
|
+
PROJECTS_DIR2 = path26.join(os22.homedir(), ".claude", "projects");
|
|
12349
12645
|
}
|
|
12350
12646
|
});
|
|
12351
12647
|
|
|
12352
12648
|
// src/daemon/mcp-tools.ts
|
|
12353
|
-
import
|
|
12354
|
-
import
|
|
12355
|
-
import
|
|
12649
|
+
import fs25 from "fs";
|
|
12650
|
+
import path27 from "path";
|
|
12651
|
+
import os23 from "os";
|
|
12356
12652
|
function getMcpToolsFile() {
|
|
12357
|
-
return
|
|
12653
|
+
return path27.join(os23.homedir(), ".node9", "mcp-tools.json");
|
|
12358
12654
|
}
|
|
12359
12655
|
function readMcpToolsConfig() {
|
|
12360
12656
|
try {
|
|
12361
12657
|
const file = getMcpToolsFile();
|
|
12362
|
-
if (!
|
|
12363
|
-
const raw =
|
|
12658
|
+
if (!fs25.existsSync(file)) return {};
|
|
12659
|
+
const raw = fs25.readFileSync(file, "utf-8");
|
|
12364
12660
|
return JSON.parse(raw);
|
|
12365
12661
|
} catch {
|
|
12366
12662
|
return {};
|
|
@@ -12369,11 +12665,11 @@ function readMcpToolsConfig() {
|
|
|
12369
12665
|
function writeMcpToolsConfig(config) {
|
|
12370
12666
|
try {
|
|
12371
12667
|
const file = getMcpToolsFile();
|
|
12372
|
-
const dir =
|
|
12373
|
-
if (!
|
|
12374
|
-
const tmpPath = `${file}.${
|
|
12375
|
-
|
|
12376
|
-
|
|
12668
|
+
const dir = path27.dirname(file);
|
|
12669
|
+
if (!fs25.existsSync(dir)) fs25.mkdirSync(dir, { recursive: true });
|
|
12670
|
+
const tmpPath = `${file}.${os23.hostname()}.${process.pid}.tmp`;
|
|
12671
|
+
fs25.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
|
|
12672
|
+
fs25.renameSync(tmpPath, file);
|
|
12377
12673
|
} catch (e) {
|
|
12378
12674
|
console.error("Failed to write mcp-tools.json", e);
|
|
12379
12675
|
}
|
|
@@ -12420,9 +12716,9 @@ var init_mcp_tools = __esm({
|
|
|
12420
12716
|
|
|
12421
12717
|
// src/daemon/server.ts
|
|
12422
12718
|
import http from "http";
|
|
12423
|
-
import
|
|
12424
|
-
import
|
|
12425
|
-
import
|
|
12719
|
+
import fs26 from "fs";
|
|
12720
|
+
import path28 from "path";
|
|
12721
|
+
import os24 from "os";
|
|
12426
12722
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
12427
12723
|
import { spawnSync } from "child_process";
|
|
12428
12724
|
import chalk6 from "chalk";
|
|
@@ -12443,7 +12739,7 @@ function startDaemon() {
|
|
|
12443
12739
|
idleTimer = setTimeout(() => {
|
|
12444
12740
|
if (autoStarted) {
|
|
12445
12741
|
try {
|
|
12446
|
-
|
|
12742
|
+
fs26.unlinkSync(DAEMON_PID_FILE);
|
|
12447
12743
|
} catch {
|
|
12448
12744
|
}
|
|
12449
12745
|
}
|
|
@@ -12588,7 +12884,7 @@ data: ${JSON.stringify(item.data)}
|
|
|
12588
12884
|
mcpServer: entry.mcpServer
|
|
12589
12885
|
});
|
|
12590
12886
|
}
|
|
12591
|
-
const projectCwd = typeof cwd === "string" &&
|
|
12887
|
+
const projectCwd = typeof cwd === "string" && path28.isAbsolute(cwd) ? cwd : void 0;
|
|
12592
12888
|
const projectConfig = getConfig(projectCwd);
|
|
12593
12889
|
const browserEnabled = projectConfig.settings.approvers?.browser !== false;
|
|
12594
12890
|
const terminalEnabled = projectConfig.settings.approvers?.terminal !== false;
|
|
@@ -12880,8 +13176,8 @@ data: ${JSON.stringify(item.data)}
|
|
|
12880
13176
|
if (!validToken(req)) return res.writeHead(403).end();
|
|
12881
13177
|
const periodParam = reqUrl.searchParams.get("period") || "7d";
|
|
12882
13178
|
const period = ["today", "7d", "30d", "month"].includes(periodParam) ? periodParam : "7d";
|
|
12883
|
-
const logPath =
|
|
12884
|
-
if (!
|
|
13179
|
+
const logPath = path28.join(os24.homedir(), ".node9", "audit.log");
|
|
13180
|
+
if (!fs26.existsSync(logPath)) {
|
|
12885
13181
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
12886
13182
|
return res.end(
|
|
12887
13183
|
JSON.stringify({
|
|
@@ -12894,7 +13190,7 @@ data: ${JSON.stringify(item.data)}
|
|
|
12894
13190
|
);
|
|
12895
13191
|
}
|
|
12896
13192
|
try {
|
|
12897
|
-
const raw =
|
|
13193
|
+
const raw = fs26.readFileSync(logPath, "utf-8");
|
|
12898
13194
|
const allEntries = raw.split("\n").flatMap((line) => {
|
|
12899
13195
|
if (!line.trim()) return [];
|
|
12900
13196
|
try {
|
|
@@ -13217,14 +13513,14 @@ data: ${JSON.stringify(item.data)}
|
|
|
13217
13513
|
server.on("error", (e) => {
|
|
13218
13514
|
if (e.code === "EADDRINUSE") {
|
|
13219
13515
|
try {
|
|
13220
|
-
if (
|
|
13221
|
-
const { pid } = JSON.parse(
|
|
13516
|
+
if (fs26.existsSync(DAEMON_PID_FILE)) {
|
|
13517
|
+
const { pid } = JSON.parse(fs26.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
13222
13518
|
process.kill(pid, 0);
|
|
13223
13519
|
return process.exit(0);
|
|
13224
13520
|
}
|
|
13225
13521
|
} catch {
|
|
13226
13522
|
try {
|
|
13227
|
-
|
|
13523
|
+
fs26.unlinkSync(DAEMON_PID_FILE);
|
|
13228
13524
|
} catch {
|
|
13229
13525
|
}
|
|
13230
13526
|
server.listen(DAEMON_PORT, DAEMON_HOST);
|
|
@@ -13312,15 +13608,15 @@ var init_server = __esm({
|
|
|
13312
13608
|
});
|
|
13313
13609
|
|
|
13314
13610
|
// src/daemon/service.ts
|
|
13315
|
-
import
|
|
13316
|
-
import
|
|
13317
|
-
import
|
|
13611
|
+
import fs27 from "fs";
|
|
13612
|
+
import path29 from "path";
|
|
13613
|
+
import os25 from "os";
|
|
13318
13614
|
import { spawnSync as spawnSync2, execFileSync } from "child_process";
|
|
13319
13615
|
function resolveNode9Binary() {
|
|
13320
13616
|
try {
|
|
13321
13617
|
const script = process.argv[1];
|
|
13322
|
-
if (typeof script === "string" &&
|
|
13323
|
-
return
|
|
13618
|
+
if (typeof script === "string" && path29.isAbsolute(script) && fs27.existsSync(script)) {
|
|
13619
|
+
return fs27.realpathSync(script);
|
|
13324
13620
|
}
|
|
13325
13621
|
} catch {
|
|
13326
13622
|
}
|
|
@@ -13338,11 +13634,11 @@ function xmlEscape(s) {
|
|
|
13338
13634
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
13339
13635
|
}
|
|
13340
13636
|
function launchdPlist(binaryPath) {
|
|
13341
|
-
const logDir =
|
|
13637
|
+
const logDir = path29.join(os25.homedir(), ".node9");
|
|
13342
13638
|
const nodePath = xmlEscape(process.execPath);
|
|
13343
13639
|
const scriptPath = xmlEscape(binaryPath);
|
|
13344
|
-
const outLog = xmlEscape(
|
|
13345
|
-
const errLog = xmlEscape(
|
|
13640
|
+
const outLog = xmlEscape(path29.join(logDir, "daemon.log"));
|
|
13641
|
+
const errLog = xmlEscape(path29.join(logDir, "daemon-error.log"));
|
|
13346
13642
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
13347
13643
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
13348
13644
|
<plist version="1.0">
|
|
@@ -13375,9 +13671,9 @@ function launchdPlist(binaryPath) {
|
|
|
13375
13671
|
`;
|
|
13376
13672
|
}
|
|
13377
13673
|
function installLaunchd(binaryPath) {
|
|
13378
|
-
const dir =
|
|
13379
|
-
if (!
|
|
13380
|
-
|
|
13674
|
+
const dir = path29.dirname(LAUNCHD_PLIST);
|
|
13675
|
+
if (!fs27.existsSync(dir)) fs27.mkdirSync(dir, { recursive: true });
|
|
13676
|
+
fs27.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
|
|
13381
13677
|
spawnSync2("launchctl", ["unload", LAUNCHD_PLIST], { encoding: "utf8" });
|
|
13382
13678
|
const r = spawnSync2("launchctl", ["load", "-w", LAUNCHD_PLIST], {
|
|
13383
13679
|
encoding: "utf8",
|
|
@@ -13388,13 +13684,13 @@ function installLaunchd(binaryPath) {
|
|
|
13388
13684
|
}
|
|
13389
13685
|
}
|
|
13390
13686
|
function uninstallLaunchd() {
|
|
13391
|
-
if (
|
|
13687
|
+
if (fs27.existsSync(LAUNCHD_PLIST)) {
|
|
13392
13688
|
spawnSync2("launchctl", ["unload", "-w", LAUNCHD_PLIST], { encoding: "utf8", timeout: 5e3 });
|
|
13393
|
-
|
|
13689
|
+
fs27.unlinkSync(LAUNCHD_PLIST);
|
|
13394
13690
|
}
|
|
13395
13691
|
}
|
|
13396
13692
|
function isLaunchdInstalled() {
|
|
13397
|
-
return
|
|
13693
|
+
return fs27.existsSync(LAUNCHD_PLIST);
|
|
13398
13694
|
}
|
|
13399
13695
|
function systemdUnit(binaryPath) {
|
|
13400
13696
|
return `[Unit]
|
|
@@ -13413,12 +13709,12 @@ WantedBy=default.target
|
|
|
13413
13709
|
`;
|
|
13414
13710
|
}
|
|
13415
13711
|
function installSystemd(binaryPath) {
|
|
13416
|
-
if (!
|
|
13417
|
-
|
|
13712
|
+
if (!fs27.existsSync(SYSTEMD_UNIT_DIR)) {
|
|
13713
|
+
fs27.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
|
|
13418
13714
|
}
|
|
13419
|
-
|
|
13715
|
+
fs27.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
|
|
13420
13716
|
try {
|
|
13421
|
-
execFileSync("loginctl", ["enable-linger",
|
|
13717
|
+
execFileSync("loginctl", ["enable-linger", os25.userInfo().username], { timeout: 3e3 });
|
|
13422
13718
|
} catch {
|
|
13423
13719
|
}
|
|
13424
13720
|
const reload = spawnSync2("systemctl", ["--user", "daemon-reload"], {
|
|
@@ -13438,23 +13734,23 @@ function installSystemd(binaryPath) {
|
|
|
13438
13734
|
}
|
|
13439
13735
|
}
|
|
13440
13736
|
function uninstallSystemd() {
|
|
13441
|
-
if (
|
|
13737
|
+
if (fs27.existsSync(SYSTEMD_UNIT)) {
|
|
13442
13738
|
spawnSync2("systemctl", ["--user", "disable", "--now", "node9-daemon"], {
|
|
13443
13739
|
encoding: "utf8",
|
|
13444
13740
|
timeout: 5e3
|
|
13445
13741
|
});
|
|
13446
13742
|
spawnSync2("systemctl", ["--user", "daemon-reload"], { encoding: "utf8", timeout: 5e3 });
|
|
13447
|
-
|
|
13743
|
+
fs27.unlinkSync(SYSTEMD_UNIT);
|
|
13448
13744
|
}
|
|
13449
13745
|
}
|
|
13450
13746
|
function isSystemdInstalled() {
|
|
13451
|
-
return
|
|
13747
|
+
return fs27.existsSync(SYSTEMD_UNIT);
|
|
13452
13748
|
}
|
|
13453
13749
|
function stopRunningDaemon() {
|
|
13454
|
-
const pidFile =
|
|
13455
|
-
if (!
|
|
13750
|
+
const pidFile = path29.join(os25.homedir(), ".node9", "daemon.pid");
|
|
13751
|
+
if (!fs27.existsSync(pidFile)) return;
|
|
13456
13752
|
try {
|
|
13457
|
-
const data = JSON.parse(
|
|
13753
|
+
const data = JSON.parse(fs27.readFileSync(pidFile, "utf-8"));
|
|
13458
13754
|
const pid = data.pid;
|
|
13459
13755
|
const MAX_PID2 = 4194304;
|
|
13460
13756
|
if (typeof pid === "number" && Number.isInteger(pid) && pid > 0 && pid <= MAX_PID2) {
|
|
@@ -13474,7 +13770,7 @@ function stopRunningDaemon() {
|
|
|
13474
13770
|
}
|
|
13475
13771
|
}
|
|
13476
13772
|
try {
|
|
13477
|
-
|
|
13773
|
+
fs27.unlinkSync(pidFile);
|
|
13478
13774
|
} catch {
|
|
13479
13775
|
}
|
|
13480
13776
|
} catch {
|
|
@@ -13549,19 +13845,19 @@ var init_service = __esm({
|
|
|
13549
13845
|
"src/daemon/service.ts"() {
|
|
13550
13846
|
"use strict";
|
|
13551
13847
|
LAUNCHD_LABEL = "ai.node9.daemon";
|
|
13552
|
-
LAUNCHD_PLIST =
|
|
13553
|
-
SYSTEMD_UNIT_DIR =
|
|
13554
|
-
SYSTEMD_UNIT =
|
|
13848
|
+
LAUNCHD_PLIST = path29.join(os25.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
|
|
13849
|
+
SYSTEMD_UNIT_DIR = path29.join(os25.homedir(), ".config", "systemd", "user");
|
|
13850
|
+
SYSTEMD_UNIT = path29.join(SYSTEMD_UNIT_DIR, "node9-daemon.service");
|
|
13555
13851
|
}
|
|
13556
13852
|
});
|
|
13557
13853
|
|
|
13558
13854
|
// src/daemon/index.ts
|
|
13559
|
-
import
|
|
13855
|
+
import fs28 from "fs";
|
|
13560
13856
|
import chalk7 from "chalk";
|
|
13561
13857
|
function stopDaemon() {
|
|
13562
|
-
if (!
|
|
13858
|
+
if (!fs28.existsSync(DAEMON_PID_FILE)) return console.log(chalk7.yellow("Not running."));
|
|
13563
13859
|
try {
|
|
13564
|
-
const data = JSON.parse(
|
|
13860
|
+
const data = JSON.parse(fs28.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
13565
13861
|
const pid = data.pid;
|
|
13566
13862
|
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
|
|
13567
13863
|
console.log(chalk7.gray("Cleaned up invalid PID file."));
|
|
@@ -13573,7 +13869,7 @@ function stopDaemon() {
|
|
|
13573
13869
|
console.log(chalk7.gray("Cleaned up stale PID file."));
|
|
13574
13870
|
} finally {
|
|
13575
13871
|
try {
|
|
13576
|
-
|
|
13872
|
+
fs28.unlinkSync(DAEMON_PID_FILE);
|
|
13577
13873
|
} catch {
|
|
13578
13874
|
}
|
|
13579
13875
|
}
|
|
@@ -13582,9 +13878,9 @@ function daemonStatus() {
|
|
|
13582
13878
|
const serviceInstalled = isDaemonServiceInstalled();
|
|
13583
13879
|
const serviceLabel = serviceInstalled ? chalk7.green("installed (starts on login)") : chalk7.yellow("not installed \u2014 run: node9 daemon install");
|
|
13584
13880
|
let processStatus;
|
|
13585
|
-
if (
|
|
13881
|
+
if (fs28.existsSync(DAEMON_PID_FILE)) {
|
|
13586
13882
|
try {
|
|
13587
|
-
const data = JSON.parse(
|
|
13883
|
+
const data = JSON.parse(fs28.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
13588
13884
|
const pid = data.pid;
|
|
13589
13885
|
const port = data.port;
|
|
13590
13886
|
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
|
|
@@ -13629,7 +13925,7 @@ __export(tail_exports, {
|
|
|
13629
13925
|
});
|
|
13630
13926
|
import http2 from "http";
|
|
13631
13927
|
import chalk29 from "chalk";
|
|
13632
|
-
import
|
|
13928
|
+
import fs46 from "fs";
|
|
13633
13929
|
import os41 from "os";
|
|
13634
13930
|
import path47 from "path";
|
|
13635
13931
|
import readline6 from "readline";
|
|
@@ -13656,19 +13952,19 @@ function getModelContextLimit(model) {
|
|
|
13656
13952
|
}
|
|
13657
13953
|
function readSessionUsage() {
|
|
13658
13954
|
const projectsDir = path47.join(os41.homedir(), ".claude", "projects");
|
|
13659
|
-
if (!
|
|
13955
|
+
if (!fs46.existsSync(projectsDir)) return null;
|
|
13660
13956
|
let latestFile = null;
|
|
13661
13957
|
let latestMtime = 0;
|
|
13662
13958
|
try {
|
|
13663
|
-
for (const dir of
|
|
13959
|
+
for (const dir of fs46.readdirSync(projectsDir)) {
|
|
13664
13960
|
const dirPath = path47.join(projectsDir, dir);
|
|
13665
13961
|
try {
|
|
13666
|
-
if (!
|
|
13667
|
-
for (const file of
|
|
13962
|
+
if (!fs46.statSync(dirPath).isDirectory()) continue;
|
|
13963
|
+
for (const file of fs46.readdirSync(dirPath)) {
|
|
13668
13964
|
if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
|
|
13669
13965
|
const filePath = path47.join(dirPath, file);
|
|
13670
13966
|
try {
|
|
13671
|
-
const mtime =
|
|
13967
|
+
const mtime = fs46.statSync(filePath).mtimeMs;
|
|
13672
13968
|
if (mtime > latestMtime) {
|
|
13673
13969
|
latestMtime = mtime;
|
|
13674
13970
|
latestFile = filePath;
|
|
@@ -13683,7 +13979,7 @@ function readSessionUsage() {
|
|
|
13683
13979
|
}
|
|
13684
13980
|
if (!latestFile) return null;
|
|
13685
13981
|
try {
|
|
13686
|
-
const lines =
|
|
13982
|
+
const lines = fs46.readFileSync(latestFile, "utf-8").split("\n");
|
|
13687
13983
|
let lastModel = "";
|
|
13688
13984
|
let lastInput = 0;
|
|
13689
13985
|
let lastOutput = 0;
|
|
@@ -13783,9 +14079,9 @@ function renderPending(activity) {
|
|
|
13783
14079
|
}
|
|
13784
14080
|
async function ensureDaemon() {
|
|
13785
14081
|
let pidPort = null;
|
|
13786
|
-
if (
|
|
14082
|
+
if (fs46.existsSync(PID_FILE)) {
|
|
13787
14083
|
try {
|
|
13788
|
-
const { port } = JSON.parse(
|
|
14084
|
+
const { port } = JSON.parse(fs46.readFileSync(PID_FILE, "utf-8"));
|
|
13789
14085
|
pidPort = port;
|
|
13790
14086
|
} catch {
|
|
13791
14087
|
console.error(chalk29.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
|
|
@@ -13943,7 +14239,7 @@ function buildRecoveryCardLines(req) {
|
|
|
13943
14239
|
function readApproversFromDisk() {
|
|
13944
14240
|
const configPath = path47.join(os41.homedir(), ".node9", "config.json");
|
|
13945
14241
|
try {
|
|
13946
|
-
const raw = JSON.parse(
|
|
14242
|
+
const raw = JSON.parse(fs46.readFileSync(configPath, "utf-8"));
|
|
13947
14243
|
const settings = raw.settings ?? {};
|
|
13948
14244
|
return settings.approvers ?? {};
|
|
13949
14245
|
} catch {
|
|
@@ -13961,13 +14257,13 @@ function approverStatusLine() {
|
|
|
13961
14257
|
function toggleApprover(channel) {
|
|
13962
14258
|
const configPath = path47.join(os41.homedir(), ".node9", "config.json");
|
|
13963
14259
|
try {
|
|
13964
|
-
const raw = JSON.parse(
|
|
14260
|
+
const raw = JSON.parse(fs46.readFileSync(configPath, "utf-8"));
|
|
13965
14261
|
const settings = raw.settings ?? {};
|
|
13966
14262
|
const approvers = settings.approvers ?? {};
|
|
13967
14263
|
approvers[channel] = approvers[channel] === false;
|
|
13968
14264
|
settings.approvers = approvers;
|
|
13969
14265
|
raw.settings = settings;
|
|
13970
|
-
|
|
14266
|
+
fs46.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
|
|
13971
14267
|
} catch (err2) {
|
|
13972
14268
|
process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
|
|
13973
14269
|
`);
|
|
@@ -14139,7 +14435,7 @@ async function startTail(options = {}) {
|
|
|
14139
14435
|
}
|
|
14140
14436
|
postDecisionHttp(req2.id, httpDecision, authToken, port, httpOpts).catch((err2) => {
|
|
14141
14437
|
try {
|
|
14142
|
-
|
|
14438
|
+
fs46.appendFileSync(
|
|
14143
14439
|
path47.join(os41.homedir(), ".node9", "hook-debug.log"),
|
|
14144
14440
|
`[tail] POST /decision failed: ${String(err2)}
|
|
14145
14441
|
`
|
|
@@ -14206,7 +14502,7 @@ async function startTail(options = {}) {
|
|
|
14206
14502
|
}
|
|
14207
14503
|
const auditLog = path47.join(os41.homedir(), ".node9", "audit.log");
|
|
14208
14504
|
try {
|
|
14209
|
-
const unackedDlp =
|
|
14505
|
+
const unackedDlp = fs46.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
|
|
14210
14506
|
if (unackedDlp > 0) {
|
|
14211
14507
|
console.log("");
|
|
14212
14508
|
console.log(
|
|
@@ -14246,7 +14542,7 @@ async function startTail(options = {}) {
|
|
|
14246
14542
|
if (stallWarned) return;
|
|
14247
14543
|
if (Date.now() - lastActivityFromDaemon < STALL_THRESHOLD_MS) return;
|
|
14248
14544
|
try {
|
|
14249
|
-
const auditMtime =
|
|
14545
|
+
const auditMtime = fs46.statSync(auditLog).mtimeMs;
|
|
14250
14546
|
if (Date.now() - auditMtime >= STALL_THRESHOLD_MS) return;
|
|
14251
14547
|
console.log("");
|
|
14252
14548
|
console.log(
|
|
@@ -14485,7 +14781,7 @@ __export(hud_exports, {
|
|
|
14485
14781
|
main: () => main,
|
|
14486
14782
|
renderEnvironmentLine: () => renderEnvironmentLine
|
|
14487
14783
|
});
|
|
14488
|
-
import
|
|
14784
|
+
import fs47 from "fs";
|
|
14489
14785
|
import path48 from "path";
|
|
14490
14786
|
import os42 from "os";
|
|
14491
14787
|
import http3 from "http";
|
|
@@ -14563,9 +14859,9 @@ function formatTimeLeft(resetsAt) {
|
|
|
14563
14859
|
return ` (${m}m left)`;
|
|
14564
14860
|
}
|
|
14565
14861
|
function safeReadJson(filePath) {
|
|
14566
|
-
if (!
|
|
14862
|
+
if (!fs47.existsSync(filePath)) return null;
|
|
14567
14863
|
try {
|
|
14568
|
-
return JSON.parse(
|
|
14864
|
+
return JSON.parse(fs47.readFileSync(filePath, "utf-8"));
|
|
14569
14865
|
} catch {
|
|
14570
14866
|
return null;
|
|
14571
14867
|
}
|
|
@@ -14586,10 +14882,10 @@ function countHooksInFile(filePath) {
|
|
|
14586
14882
|
return Object.keys(cfg.hooks).length;
|
|
14587
14883
|
}
|
|
14588
14884
|
function countRulesInDir(rulesDir) {
|
|
14589
|
-
if (!
|
|
14885
|
+
if (!fs47.existsSync(rulesDir)) return 0;
|
|
14590
14886
|
let count = 0;
|
|
14591
14887
|
try {
|
|
14592
|
-
for (const entry of
|
|
14888
|
+
for (const entry of fs47.readdirSync(rulesDir, { withFileTypes: true })) {
|
|
14593
14889
|
if (entry.isDirectory()) {
|
|
14594
14890
|
count += countRulesInDir(path48.join(rulesDir, entry.name));
|
|
14595
14891
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -14615,7 +14911,7 @@ function countConfigs(cwd) {
|
|
|
14615
14911
|
let hooksCount = 0;
|
|
14616
14912
|
const userMcpServers = /* @__PURE__ */ new Set();
|
|
14617
14913
|
const projectMcpServers = /* @__PURE__ */ new Set();
|
|
14618
|
-
if (
|
|
14914
|
+
if (fs47.existsSync(path48.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
14619
14915
|
rulesCount += countRulesInDir(path48.join(claudeDir, "rules"));
|
|
14620
14916
|
const userSettings = path48.join(claudeDir, "settings.json");
|
|
14621
14917
|
for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
|
|
@@ -14626,18 +14922,18 @@ function countConfigs(cwd) {
|
|
|
14626
14922
|
userMcpServers.delete(name);
|
|
14627
14923
|
}
|
|
14628
14924
|
if (cwd) {
|
|
14629
|
-
if (
|
|
14630
|
-
if (
|
|
14925
|
+
if (fs47.existsSync(path48.join(cwd, "CLAUDE.md"))) claudeMdCount++;
|
|
14926
|
+
if (fs47.existsSync(path48.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
|
|
14631
14927
|
const projectClaudeDir = path48.join(cwd, ".claude");
|
|
14632
14928
|
const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
|
|
14633
14929
|
if (!overlapsUserScope) {
|
|
14634
|
-
if (
|
|
14930
|
+
if (fs47.existsSync(path48.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
14635
14931
|
rulesCount += countRulesInDir(path48.join(projectClaudeDir, "rules"));
|
|
14636
14932
|
const projSettings = path48.join(projectClaudeDir, "settings.json");
|
|
14637
14933
|
for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
|
|
14638
14934
|
hooksCount += countHooksInFile(projSettings);
|
|
14639
14935
|
}
|
|
14640
|
-
if (
|
|
14936
|
+
if (fs47.existsSync(path48.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
|
|
14641
14937
|
const localSettings = path48.join(projectClaudeDir, "settings.local.json");
|
|
14642
14938
|
for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
|
|
14643
14939
|
hooksCount += countHooksInFile(localSettings);
|
|
@@ -14675,11 +14971,11 @@ function readActiveShieldsHud() {
|
|
|
14675
14971
|
}
|
|
14676
14972
|
try {
|
|
14677
14973
|
const shieldsPath = path48.join(os42.homedir(), ".node9", "shields.json");
|
|
14678
|
-
if (!
|
|
14974
|
+
if (!fs47.existsSync(shieldsPath)) {
|
|
14679
14975
|
shieldsCache = { value: [], ts: now };
|
|
14680
14976
|
return [];
|
|
14681
14977
|
}
|
|
14682
|
-
const parsed = JSON.parse(
|
|
14978
|
+
const parsed = JSON.parse(fs47.readFileSync(shieldsPath, "utf-8"));
|
|
14683
14979
|
if (!Array.isArray(parsed.active)) {
|
|
14684
14980
|
shieldsCache = { value: [], ts: now };
|
|
14685
14981
|
return [];
|
|
@@ -14781,17 +15077,17 @@ function renderContextLine(stdin) {
|
|
|
14781
15077
|
async function main() {
|
|
14782
15078
|
try {
|
|
14783
15079
|
const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
|
|
14784
|
-
if (
|
|
15080
|
+
if (fs47.existsSync(path48.join(os42.homedir(), ".node9", "hud-debug"))) {
|
|
14785
15081
|
try {
|
|
14786
15082
|
const logPath = path48.join(os42.homedir(), ".node9", "hud-debug.log");
|
|
14787
15083
|
const MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
14788
15084
|
let size = 0;
|
|
14789
15085
|
try {
|
|
14790
|
-
size =
|
|
15086
|
+
size = fs47.statSync(logPath).size;
|
|
14791
15087
|
} catch {
|
|
14792
15088
|
}
|
|
14793
15089
|
if (size < MAX_LOG_SIZE) {
|
|
14794
|
-
|
|
15090
|
+
fs47.appendFileSync(
|
|
14795
15091
|
logPath,
|
|
14796
15092
|
JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
|
|
14797
15093
|
);
|
|
@@ -14815,8 +15111,8 @@ async function main() {
|
|
|
14815
15111
|
path48.join(cwd, "node9.config.json"),
|
|
14816
15112
|
path48.join(os42.homedir(), ".node9", "config.json")
|
|
14817
15113
|
]) {
|
|
14818
|
-
if (!
|
|
14819
|
-
const cfg = JSON.parse(
|
|
15114
|
+
if (!fs47.existsSync(configPath)) continue;
|
|
15115
|
+
const cfg = JSON.parse(fs47.readFileSync(configPath, "utf-8"));
|
|
14820
15116
|
const hud = cfg.settings?.hud;
|
|
14821
15117
|
if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
|
|
14822
15118
|
}
|
|
@@ -14863,7 +15159,7 @@ init_setup();
|
|
|
14863
15159
|
init_daemon2();
|
|
14864
15160
|
import { Command } from "commander";
|
|
14865
15161
|
import chalk30 from "chalk";
|
|
14866
|
-
import
|
|
15162
|
+
import fs48 from "fs";
|
|
14867
15163
|
import path49 from "path";
|
|
14868
15164
|
import os43 from "os";
|
|
14869
15165
|
import { confirm as confirm2 } from "@inquirer/prompts";
|
|
@@ -15048,17 +15344,17 @@ async function runProxy(targetCommand) {
|
|
|
15048
15344
|
// src/cli/daemon-starter.ts
|
|
15049
15345
|
init_daemon();
|
|
15050
15346
|
import { spawn as spawn3 } from "child_process";
|
|
15051
|
-
import
|
|
15052
|
-
import
|
|
15347
|
+
import path30 from "path";
|
|
15348
|
+
import fs29 from "fs";
|
|
15053
15349
|
function isTestingMode() {
|
|
15054
15350
|
return /^(1|true|yes)$/i.test(process.env.NODE9_TESTING ?? "");
|
|
15055
15351
|
}
|
|
15056
15352
|
async function autoStartDaemonAndWait() {
|
|
15057
15353
|
if (isTestingMode()) return false;
|
|
15058
|
-
if (!
|
|
15354
|
+
if (!path30.isAbsolute(process.argv[1])) return false;
|
|
15059
15355
|
let resolvedArgv1;
|
|
15060
15356
|
try {
|
|
15061
|
-
resolvedArgv1 =
|
|
15357
|
+
resolvedArgv1 = fs29.realpathSync(process.argv[1]);
|
|
15062
15358
|
} catch {
|
|
15063
15359
|
return false;
|
|
15064
15360
|
}
|
|
@@ -15089,19 +15385,19 @@ init_daemon();
|
|
|
15089
15385
|
init_config();
|
|
15090
15386
|
init_policy();
|
|
15091
15387
|
import chalk9 from "chalk";
|
|
15092
|
-
import
|
|
15388
|
+
import fs32 from "fs";
|
|
15093
15389
|
import { spawn as spawn5 } from "child_process";
|
|
15094
|
-
import
|
|
15095
|
-
import
|
|
15390
|
+
import path33 from "path";
|
|
15391
|
+
import os28 from "os";
|
|
15096
15392
|
|
|
15097
15393
|
// src/undo.ts
|
|
15098
15394
|
import { spawnSync as spawnSync3, spawn as spawn4 } from "child_process";
|
|
15099
|
-
import
|
|
15100
|
-
import
|
|
15395
|
+
import crypto4 from "crypto";
|
|
15396
|
+
import fs30 from "fs";
|
|
15101
15397
|
import net3 from "net";
|
|
15102
|
-
import
|
|
15103
|
-
import
|
|
15104
|
-
var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" :
|
|
15398
|
+
import path31 from "path";
|
|
15399
|
+
import os26 from "os";
|
|
15400
|
+
var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path31.join(os26.tmpdir(), "node9-activity.sock");
|
|
15105
15401
|
function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
|
|
15106
15402
|
try {
|
|
15107
15403
|
const payload = JSON.stringify({
|
|
@@ -15121,22 +15417,22 @@ function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
|
|
|
15121
15417
|
} catch {
|
|
15122
15418
|
}
|
|
15123
15419
|
}
|
|
15124
|
-
var SNAPSHOT_STACK_PATH =
|
|
15125
|
-
var UNDO_LATEST_PATH =
|
|
15420
|
+
var SNAPSHOT_STACK_PATH = path31.join(os26.homedir(), ".node9", "snapshots.json");
|
|
15421
|
+
var UNDO_LATEST_PATH = path31.join(os26.homedir(), ".node9", "undo_latest.txt");
|
|
15126
15422
|
var MAX_SNAPSHOTS = 10;
|
|
15127
15423
|
var GIT_TIMEOUT = 15e3;
|
|
15128
15424
|
function readStack() {
|
|
15129
15425
|
try {
|
|
15130
|
-
if (
|
|
15131
|
-
return JSON.parse(
|
|
15426
|
+
if (fs30.existsSync(SNAPSHOT_STACK_PATH))
|
|
15427
|
+
return JSON.parse(fs30.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
|
|
15132
15428
|
} catch {
|
|
15133
15429
|
}
|
|
15134
15430
|
return [];
|
|
15135
15431
|
}
|
|
15136
15432
|
function writeStack(stack) {
|
|
15137
|
-
const dir =
|
|
15138
|
-
if (!
|
|
15139
|
-
|
|
15433
|
+
const dir = path31.dirname(SNAPSHOT_STACK_PATH);
|
|
15434
|
+
if (!fs30.existsSync(dir)) fs30.mkdirSync(dir, { recursive: true });
|
|
15435
|
+
fs30.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
|
|
15140
15436
|
}
|
|
15141
15437
|
function extractFilePath(args) {
|
|
15142
15438
|
if (!args || typeof args !== "object") return null;
|
|
@@ -15156,12 +15452,12 @@ function buildArgsSummary(tool, args) {
|
|
|
15156
15452
|
return "";
|
|
15157
15453
|
}
|
|
15158
15454
|
function findProjectRoot(filePath) {
|
|
15159
|
-
let dir =
|
|
15455
|
+
let dir = path31.dirname(filePath);
|
|
15160
15456
|
while (true) {
|
|
15161
|
-
if (
|
|
15457
|
+
if (fs30.existsSync(path31.join(dir, ".git")) || fs30.existsSync(path31.join(dir, "package.json"))) {
|
|
15162
15458
|
return dir;
|
|
15163
15459
|
}
|
|
15164
|
-
const parent =
|
|
15460
|
+
const parent = path31.dirname(dir);
|
|
15165
15461
|
if (parent === dir) return process.cwd();
|
|
15166
15462
|
dir = parent;
|
|
15167
15463
|
}
|
|
@@ -15169,7 +15465,7 @@ function findProjectRoot(filePath) {
|
|
|
15169
15465
|
function normalizeCwdForHash(cwd) {
|
|
15170
15466
|
let normalized;
|
|
15171
15467
|
try {
|
|
15172
|
-
normalized =
|
|
15468
|
+
normalized = fs30.realpathSync(cwd);
|
|
15173
15469
|
} catch {
|
|
15174
15470
|
normalized = cwd;
|
|
15175
15471
|
}
|
|
@@ -15178,17 +15474,17 @@ function normalizeCwdForHash(cwd) {
|
|
|
15178
15474
|
return normalized;
|
|
15179
15475
|
}
|
|
15180
15476
|
function getShadowRepoDir(cwd) {
|
|
15181
|
-
const hash =
|
|
15182
|
-
return
|
|
15477
|
+
const hash = crypto4.createHash("sha256").update(normalizeCwdForHash(cwd)).digest("hex").slice(0, 16);
|
|
15478
|
+
return path31.join(os26.homedir(), ".node9", "snapshots", hash);
|
|
15183
15479
|
}
|
|
15184
15480
|
function cleanOrphanedIndexFiles(shadowDir) {
|
|
15185
15481
|
try {
|
|
15186
15482
|
const cutoff = Date.now() - 6e4;
|
|
15187
|
-
for (const f of
|
|
15483
|
+
for (const f of fs30.readdirSync(shadowDir)) {
|
|
15188
15484
|
if (f.startsWith("index_")) {
|
|
15189
|
-
const fp =
|
|
15485
|
+
const fp = path31.join(shadowDir, f);
|
|
15190
15486
|
try {
|
|
15191
|
-
if (
|
|
15487
|
+
if (fs30.statSync(fp).mtimeMs < cutoff) fs30.unlinkSync(fp);
|
|
15192
15488
|
} catch {
|
|
15193
15489
|
}
|
|
15194
15490
|
}
|
|
@@ -15200,7 +15496,7 @@ function writeShadowExcludes(shadowDir, ignorePaths) {
|
|
|
15200
15496
|
const hardcoded = [".git", ".node9"];
|
|
15201
15497
|
const lines = [...hardcoded, ...ignorePaths].join("\n");
|
|
15202
15498
|
try {
|
|
15203
|
-
|
|
15499
|
+
fs30.writeFileSync(path31.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
|
|
15204
15500
|
} catch {
|
|
15205
15501
|
}
|
|
15206
15502
|
}
|
|
@@ -15213,25 +15509,25 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
15213
15509
|
timeout: 3e3
|
|
15214
15510
|
});
|
|
15215
15511
|
if (check.status === 0) {
|
|
15216
|
-
const ptPath =
|
|
15512
|
+
const ptPath = path31.join(shadowDir, "project-path.txt");
|
|
15217
15513
|
try {
|
|
15218
|
-
const stored =
|
|
15514
|
+
const stored = fs30.readFileSync(ptPath, "utf8").trim();
|
|
15219
15515
|
if (stored === normalizedCwd) return true;
|
|
15220
15516
|
if (process.env.NODE9_DEBUG === "1")
|
|
15221
15517
|
console.error(
|
|
15222
15518
|
`[Node9] Shadow repo path mismatch: stored="${stored}" expected="${normalizedCwd}" \u2014 reinitializing`
|
|
15223
15519
|
);
|
|
15224
|
-
|
|
15520
|
+
fs30.rmSync(shadowDir, { recursive: true, force: true });
|
|
15225
15521
|
} catch {
|
|
15226
15522
|
try {
|
|
15227
|
-
|
|
15523
|
+
fs30.writeFileSync(ptPath, normalizedCwd, "utf8");
|
|
15228
15524
|
} catch {
|
|
15229
15525
|
}
|
|
15230
15526
|
return true;
|
|
15231
15527
|
}
|
|
15232
15528
|
}
|
|
15233
15529
|
try {
|
|
15234
|
-
|
|
15530
|
+
fs30.mkdirSync(shadowDir, { recursive: true });
|
|
15235
15531
|
} catch {
|
|
15236
15532
|
}
|
|
15237
15533
|
const init = spawnSync3("git", ["init", "--bare", shadowDir], { timeout: 5e3 });
|
|
@@ -15240,7 +15536,7 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
15240
15536
|
if (process.env.NODE9_DEBUG === "1") console.error("[Node9] git init --bare failed:", reason);
|
|
15241
15537
|
return false;
|
|
15242
15538
|
}
|
|
15243
|
-
const configFile =
|
|
15539
|
+
const configFile = path31.join(shadowDir, "config");
|
|
15244
15540
|
spawnSync3("git", ["config", "--file", configFile, "core.untrackedCache", "true"], {
|
|
15245
15541
|
timeout: 3e3
|
|
15246
15542
|
});
|
|
@@ -15248,7 +15544,7 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
15248
15544
|
timeout: 3e3
|
|
15249
15545
|
});
|
|
15250
15546
|
try {
|
|
15251
|
-
|
|
15547
|
+
fs30.writeFileSync(path31.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
|
|
15252
15548
|
} catch {
|
|
15253
15549
|
}
|
|
15254
15550
|
return true;
|
|
@@ -15271,12 +15567,12 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
15271
15567
|
let indexFile = null;
|
|
15272
15568
|
try {
|
|
15273
15569
|
const rawFilePath = extractFilePath(args);
|
|
15274
|
-
const absFilePath = rawFilePath &&
|
|
15570
|
+
const absFilePath = rawFilePath && path31.isAbsolute(rawFilePath) ? rawFilePath : null;
|
|
15275
15571
|
const cwd = absFilePath ? findProjectRoot(absFilePath) : process.cwd();
|
|
15276
15572
|
const shadowDir = getShadowRepoDir(cwd);
|
|
15277
15573
|
if (!ensureShadowRepo(shadowDir, cwd)) return null;
|
|
15278
15574
|
writeShadowExcludes(shadowDir, ignorePaths);
|
|
15279
|
-
indexFile =
|
|
15575
|
+
indexFile = path31.join(shadowDir, `index_${process.pid}_${Date.now()}`);
|
|
15280
15576
|
const shadowEnv = {
|
|
15281
15577
|
...process.env,
|
|
15282
15578
|
GIT_DIR: shadowDir,
|
|
@@ -15348,7 +15644,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
15348
15644
|
writeStack(stack);
|
|
15349
15645
|
const entry = stack[stack.length - 1];
|
|
15350
15646
|
notifySnapshotTaken(commitHash.slice(0, 7), tool, entry.argsSummary, capturedFiles.length);
|
|
15351
|
-
|
|
15647
|
+
fs30.writeFileSync(UNDO_LATEST_PATH, commitHash);
|
|
15352
15648
|
if (shouldGc) {
|
|
15353
15649
|
spawn4("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
|
|
15354
15650
|
}
|
|
@@ -15359,7 +15655,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
15359
15655
|
} finally {
|
|
15360
15656
|
if (indexFile) {
|
|
15361
15657
|
try {
|
|
15362
|
-
|
|
15658
|
+
fs30.unlinkSync(indexFile);
|
|
15363
15659
|
} catch {
|
|
15364
15660
|
}
|
|
15365
15661
|
}
|
|
@@ -15435,9 +15731,9 @@ function applyUndo(hash, cwd) {
|
|
|
15435
15731
|
timeout: GIT_TIMEOUT
|
|
15436
15732
|
}).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
|
|
15437
15733
|
for (const file of [...tracked, ...untracked]) {
|
|
15438
|
-
const fullPath =
|
|
15439
|
-
if (!snapshotFiles.has(file) &&
|
|
15440
|
-
|
|
15734
|
+
const fullPath = path31.join(dir, file);
|
|
15735
|
+
if (!snapshotFiles.has(file) && fs30.existsSync(fullPath)) {
|
|
15736
|
+
fs30.unlinkSync(fullPath);
|
|
15441
15737
|
}
|
|
15442
15738
|
}
|
|
15443
15739
|
return true;
|
|
@@ -15447,17 +15743,17 @@ function applyUndo(hash, cwd) {
|
|
|
15447
15743
|
}
|
|
15448
15744
|
|
|
15449
15745
|
// src/skill-pin.ts
|
|
15450
|
-
import
|
|
15451
|
-
import
|
|
15452
|
-
import
|
|
15453
|
-
import
|
|
15454
|
-
function
|
|
15455
|
-
return
|
|
15746
|
+
import fs31 from "fs";
|
|
15747
|
+
import path32 from "path";
|
|
15748
|
+
import os27 from "os";
|
|
15749
|
+
import crypto5 from "crypto";
|
|
15750
|
+
function getPinsFilePath2() {
|
|
15751
|
+
return path32.join(os27.homedir(), ".node9", "skill-pins.json");
|
|
15456
15752
|
}
|
|
15457
15753
|
var MAX_FILES = 5e3;
|
|
15458
15754
|
var MAX_TOTAL_BYTES = 50 * 1024 * 1024;
|
|
15459
15755
|
function sha256Bytes(buf) {
|
|
15460
|
-
return
|
|
15756
|
+
return crypto5.createHash("sha256").update(buf).digest("hex");
|
|
15461
15757
|
}
|
|
15462
15758
|
function walkDir(root) {
|
|
15463
15759
|
const out = [];
|
|
@@ -15466,18 +15762,18 @@ function walkDir(root) {
|
|
|
15466
15762
|
if (out.length >= MAX_FILES) return;
|
|
15467
15763
|
let entries;
|
|
15468
15764
|
try {
|
|
15469
|
-
entries =
|
|
15765
|
+
entries = fs31.readdirSync(dir, { withFileTypes: true });
|
|
15470
15766
|
} catch {
|
|
15471
15767
|
return;
|
|
15472
15768
|
}
|
|
15473
15769
|
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
15474
15770
|
for (const entry of entries) {
|
|
15475
15771
|
if (out.length >= MAX_FILES) return;
|
|
15476
|
-
const full =
|
|
15477
|
-
const rel = relDir ?
|
|
15772
|
+
const full = path32.join(dir, entry.name);
|
|
15773
|
+
const rel = relDir ? path32.posix.join(relDir, entry.name) : entry.name;
|
|
15478
15774
|
let lst;
|
|
15479
15775
|
try {
|
|
15480
|
-
lst =
|
|
15776
|
+
lst = fs31.lstatSync(full);
|
|
15481
15777
|
} catch {
|
|
15482
15778
|
continue;
|
|
15483
15779
|
}
|
|
@@ -15489,7 +15785,7 @@ function walkDir(root) {
|
|
|
15489
15785
|
if (!lst.isFile()) continue;
|
|
15490
15786
|
if (totalBytes + lst.size > MAX_TOTAL_BYTES) continue;
|
|
15491
15787
|
try {
|
|
15492
|
-
const buf =
|
|
15788
|
+
const buf = fs31.readFileSync(full);
|
|
15493
15789
|
totalBytes += buf.length;
|
|
15494
15790
|
out.push({ rel, hash: sha256Bytes(buf) });
|
|
15495
15791
|
} catch {
|
|
@@ -15503,32 +15799,32 @@ function walkDir(root) {
|
|
|
15503
15799
|
function hashSkillRoot(absPath) {
|
|
15504
15800
|
let lst;
|
|
15505
15801
|
try {
|
|
15506
|
-
lst =
|
|
15802
|
+
lst = fs31.lstatSync(absPath);
|
|
15507
15803
|
} catch {
|
|
15508
15804
|
return { exists: false, contentHash: "", fileCount: 0 };
|
|
15509
15805
|
}
|
|
15510
15806
|
if (lst.isSymbolicLink()) return { exists: false, contentHash: "", fileCount: 0 };
|
|
15511
15807
|
if (lst.isFile()) {
|
|
15512
15808
|
try {
|
|
15513
|
-
return { exists: true, contentHash: sha256Bytes(
|
|
15809
|
+
return { exists: true, contentHash: sha256Bytes(fs31.readFileSync(absPath)), fileCount: 1 };
|
|
15514
15810
|
} catch {
|
|
15515
15811
|
return { exists: false, contentHash: "", fileCount: 0 };
|
|
15516
15812
|
}
|
|
15517
15813
|
}
|
|
15518
15814
|
if (lst.isDirectory()) {
|
|
15519
15815
|
const entries = walkDir(absPath);
|
|
15520
|
-
const contentHash =
|
|
15816
|
+
const contentHash = crypto5.createHash("sha256").update(entries.join("\n")).digest("hex");
|
|
15521
15817
|
return { exists: true, contentHash, fileCount: entries.length };
|
|
15522
15818
|
}
|
|
15523
15819
|
return { exists: false, contentHash: "", fileCount: 0 };
|
|
15524
15820
|
}
|
|
15525
15821
|
function getRootKey(absPath) {
|
|
15526
|
-
return
|
|
15822
|
+
return crypto5.createHash("sha256").update(absPath).digest("hex").slice(0, 16);
|
|
15527
15823
|
}
|
|
15528
15824
|
function readSkillPinsSafe() {
|
|
15529
|
-
const filePath =
|
|
15825
|
+
const filePath = getPinsFilePath2();
|
|
15530
15826
|
try {
|
|
15531
|
-
const raw =
|
|
15827
|
+
const raw = fs31.readFileSync(filePath, "utf-8");
|
|
15532
15828
|
if (!raw.trim()) return { ok: false, reason: "corrupt", detail: "empty file" };
|
|
15533
15829
|
const parsed = JSON.parse(raw);
|
|
15534
15830
|
if (!parsed.roots || typeof parsed.roots !== "object" || Array.isArray(parsed.roots)) {
|
|
@@ -15547,18 +15843,18 @@ function readSkillPins() {
|
|
|
15547
15843
|
throw new Error(`[node9] skill pin file is corrupt: ${result.detail}`);
|
|
15548
15844
|
}
|
|
15549
15845
|
function writeSkillPins(data) {
|
|
15550
|
-
const filePath =
|
|
15551
|
-
|
|
15552
|
-
const tmp = `${filePath}.${
|
|
15553
|
-
|
|
15554
|
-
|
|
15846
|
+
const filePath = getPinsFilePath2();
|
|
15847
|
+
fs31.mkdirSync(path32.dirname(filePath), { recursive: true });
|
|
15848
|
+
const tmp = `${filePath}.${crypto5.randomBytes(6).toString("hex")}.tmp`;
|
|
15849
|
+
fs31.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
15850
|
+
fs31.renameSync(tmp, filePath);
|
|
15555
15851
|
}
|
|
15556
|
-
function
|
|
15852
|
+
function removePin2(rootKey) {
|
|
15557
15853
|
const pins = readSkillPins();
|
|
15558
15854
|
delete pins.roots[rootKey];
|
|
15559
15855
|
writeSkillPins(pins);
|
|
15560
15856
|
}
|
|
15561
|
-
function
|
|
15857
|
+
function clearAllPins2() {
|
|
15562
15858
|
writeSkillPins({ roots: {} });
|
|
15563
15859
|
}
|
|
15564
15860
|
function verifyAndPinRoots(roots) {
|
|
@@ -15595,43 +15891,48 @@ function verifyAndPinRoots(roots) {
|
|
|
15595
15891
|
return { kind: "verified" };
|
|
15596
15892
|
}
|
|
15597
15893
|
function defaultSkillRoots(_cwd) {
|
|
15598
|
-
const marketplaces =
|
|
15894
|
+
const marketplaces = path32.join(os27.homedir(), ".claude", "plugins", "marketplaces");
|
|
15599
15895
|
const roots = [];
|
|
15600
15896
|
let registries;
|
|
15601
15897
|
try {
|
|
15602
|
-
registries =
|
|
15898
|
+
registries = fs31.readdirSync(marketplaces, { withFileTypes: true });
|
|
15603
15899
|
} catch {
|
|
15604
15900
|
return [];
|
|
15605
15901
|
}
|
|
15606
15902
|
for (const registry of registries) {
|
|
15607
15903
|
if (!registry.isDirectory()) continue;
|
|
15608
|
-
const pluginsDir =
|
|
15904
|
+
const pluginsDir = path32.join(marketplaces, registry.name, "plugins");
|
|
15609
15905
|
let plugins;
|
|
15610
15906
|
try {
|
|
15611
|
-
plugins =
|
|
15907
|
+
plugins = fs31.readdirSync(pluginsDir, { withFileTypes: true });
|
|
15612
15908
|
} catch {
|
|
15613
15909
|
continue;
|
|
15614
15910
|
}
|
|
15615
15911
|
for (const plugin of plugins) {
|
|
15616
15912
|
if (!plugin.isDirectory()) continue;
|
|
15617
|
-
roots.push(
|
|
15913
|
+
roots.push(path32.join(pluginsDir, plugin.name));
|
|
15618
15914
|
}
|
|
15619
15915
|
}
|
|
15620
15916
|
return roots;
|
|
15621
15917
|
}
|
|
15622
15918
|
function resolveUserSkillRoot(entry, cwd) {
|
|
15623
15919
|
if (!entry) return null;
|
|
15624
|
-
if (entry.startsWith("~/") || entry === "~") return
|
|
15625
|
-
if (
|
|
15626
|
-
if (!cwd || !
|
|
15627
|
-
return
|
|
15920
|
+
if (entry.startsWith("~/") || entry === "~") return path32.join(os27.homedir(), entry.slice(1));
|
|
15921
|
+
if (path32.isAbsolute(entry)) return entry;
|
|
15922
|
+
if (!cwd || !path32.isAbsolute(cwd)) return null;
|
|
15923
|
+
return path32.join(cwd, entry);
|
|
15628
15924
|
}
|
|
15629
15925
|
|
|
15630
15926
|
// src/cli/commands/check.ts
|
|
15927
|
+
init_dlp();
|
|
15928
|
+
init_audit();
|
|
15631
15929
|
function sanitize2(value) {
|
|
15632
15930
|
return value.replace(/[\x00-\x1F\x7F]/g, "");
|
|
15633
15931
|
}
|
|
15634
15932
|
function detectAiAgent(payload) {
|
|
15933
|
+
if (payload.turn_id !== void 0) {
|
|
15934
|
+
return "Codex";
|
|
15935
|
+
}
|
|
15635
15936
|
if (payload.hook_event_name === "PreToolUse" || payload.hook_event_name === "PostToolUse" || payload.tool_use_id !== void 0 || payload.permission_mode !== void 0) {
|
|
15636
15937
|
return "Claude Code";
|
|
15637
15938
|
}
|
|
@@ -15666,9 +15967,9 @@ function registerCheckCommand(program2) {
|
|
|
15666
15967
|
} catch (err2) {
|
|
15667
15968
|
const tempConfig = getConfig();
|
|
15668
15969
|
if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
|
|
15669
|
-
const logPath =
|
|
15970
|
+
const logPath = path33.join(os28.homedir(), ".node9", "hook-debug.log");
|
|
15670
15971
|
const errMsg = err2 instanceof Error ? err2.message : String(err2);
|
|
15671
|
-
|
|
15972
|
+
fs32.appendFileSync(
|
|
15672
15973
|
logPath,
|
|
15673
15974
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
|
|
15674
15975
|
RAW: ${raw}
|
|
@@ -15677,15 +15978,76 @@ RAW: ${raw}
|
|
|
15677
15978
|
}
|
|
15678
15979
|
process.exit(0);
|
|
15679
15980
|
}
|
|
15680
|
-
|
|
15981
|
+
if (payload.hook_event_name === "UserPromptSubmit") {
|
|
15982
|
+
const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
|
|
15983
|
+
if (process.env.NODE9_DEBUG === "1") {
|
|
15984
|
+
try {
|
|
15985
|
+
const logPath = path33.join(os28.homedir(), ".node9", "hook-debug.log");
|
|
15986
|
+
if (!fs32.existsSync(path33.dirname(logPath)))
|
|
15987
|
+
fs32.mkdirSync(path33.dirname(logPath), { recursive: true });
|
|
15988
|
+
const sanitized = JSON.stringify({
|
|
15989
|
+
...payload,
|
|
15990
|
+
prompt: `<redacted, ${prompt.length} bytes>`
|
|
15991
|
+
});
|
|
15992
|
+
fs32.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${sanitized}
|
|
15993
|
+
`);
|
|
15994
|
+
} catch {
|
|
15995
|
+
}
|
|
15996
|
+
}
|
|
15997
|
+
if (!prompt) process.exit(0);
|
|
15998
|
+
const dlpMatch = scanArgs({ prompt });
|
|
15999
|
+
if (!dlpMatch) process.exit(0);
|
|
16000
|
+
const agent2 = detectAiAgent(payload);
|
|
16001
|
+
const sessionId2 = typeof payload.session_id === "string" ? payload.session_id : void 0;
|
|
16002
|
+
appendLocalAudit(
|
|
16003
|
+
"UserPromptSubmit",
|
|
16004
|
+
{ prompt },
|
|
16005
|
+
"deny",
|
|
16006
|
+
"dlp-block",
|
|
16007
|
+
{ agent: agent2, sessionId: sessionId2 },
|
|
16008
|
+
true
|
|
16009
|
+
);
|
|
16010
|
+
const reason = `\u{1F6A8} Node9 DLP: ${dlpMatch.patternName} detected in prompt (${dlpMatch.redactedSample}). Prompt was not submitted \u2014 remove the credential and try again.`;
|
|
16011
|
+
try {
|
|
16012
|
+
const ttyFd = fs32.openSync("/dev/tty", "w");
|
|
16013
|
+
fs32.writeSync(
|
|
16014
|
+
ttyFd,
|
|
16015
|
+
chalk9.bgRed.white.bold(`
|
|
16016
|
+
\u{1F6A8} NODE9 DLP \u2014 PROMPT BLOCKED
|
|
16017
|
+
`) + chalk9.red(` ${dlpMatch.patternName} detected in your prompt.
|
|
16018
|
+
`) + chalk9.gray(` Match: ${dlpMatch.redactedSample}
|
|
16019
|
+
`) + chalk9.cyan(` Edit the prompt to remove the credential and resubmit.
|
|
16020
|
+
|
|
16021
|
+
`)
|
|
16022
|
+
);
|
|
16023
|
+
fs32.closeSync(ttyFd);
|
|
16024
|
+
} catch {
|
|
16025
|
+
}
|
|
16026
|
+
const isCodex = agent2 === "Codex";
|
|
16027
|
+
process.stdout.write(
|
|
16028
|
+
JSON.stringify({
|
|
16029
|
+
decision: "block",
|
|
16030
|
+
reason,
|
|
16031
|
+
systemMessage: reason,
|
|
16032
|
+
hookSpecificOutput: isCodex ? { hookEventName: "UserPromptSubmit" } : {
|
|
16033
|
+
hookEventName: "UserPromptSubmit",
|
|
16034
|
+
permissionDecision: "deny",
|
|
16035
|
+
permissionDecisionReason: reason
|
|
16036
|
+
}
|
|
16037
|
+
}) + "\n"
|
|
16038
|
+
);
|
|
16039
|
+
process.exit(2);
|
|
16040
|
+
}
|
|
16041
|
+
const safeCwdForConfig = typeof payload.cwd === "string" && path33.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
16042
|
+
const config = getConfig(safeCwdForConfig);
|
|
15681
16043
|
if (config.settings.autoStartDaemon && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON) {
|
|
15682
16044
|
try {
|
|
15683
16045
|
const scriptPath = process.argv[1];
|
|
15684
|
-
if (typeof scriptPath !== "string" || !
|
|
16046
|
+
if (typeof scriptPath !== "string" || !path33.isAbsolute(scriptPath))
|
|
15685
16047
|
throw new Error("node9: argv[1] is not an absolute path");
|
|
15686
|
-
const resolvedScript =
|
|
15687
|
-
const packageDist =
|
|
15688
|
-
if (!resolvedScript.startsWith(packageDist +
|
|
16048
|
+
const resolvedScript = fs32.realpathSync(scriptPath);
|
|
16049
|
+
const packageDist = fs32.realpathSync(path33.resolve(__dirname, "../.."));
|
|
16050
|
+
if (!resolvedScript.startsWith(packageDist + path33.sep) && resolvedScript !== packageDist)
|
|
15689
16051
|
throw new Error(
|
|
15690
16052
|
`node9: daemon spawn aborted \u2014 argv[1] (${resolvedScript}) is outside package dist (${packageDist})`
|
|
15691
16053
|
);
|
|
@@ -15707,10 +16069,10 @@ RAW: ${raw}
|
|
|
15707
16069
|
});
|
|
15708
16070
|
d.unref();
|
|
15709
16071
|
} catch (spawnErr) {
|
|
15710
|
-
const logPath =
|
|
16072
|
+
const logPath = path33.join(os28.homedir(), ".node9", "hook-debug.log");
|
|
15711
16073
|
const msg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);
|
|
15712
16074
|
try {
|
|
15713
|
-
|
|
16075
|
+
fs32.appendFileSync(
|
|
15714
16076
|
logPath,
|
|
15715
16077
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-autostart-failed: ${msg}
|
|
15716
16078
|
`
|
|
@@ -15720,10 +16082,10 @@ RAW: ${raw}
|
|
|
15720
16082
|
}
|
|
15721
16083
|
}
|
|
15722
16084
|
if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
|
|
15723
|
-
const logPath =
|
|
15724
|
-
if (!
|
|
15725
|
-
|
|
15726
|
-
|
|
16085
|
+
const logPath = path33.join(os28.homedir(), ".node9", "hook-debug.log");
|
|
16086
|
+
if (!fs32.existsSync(path33.dirname(logPath)))
|
|
16087
|
+
fs32.mkdirSync(path33.dirname(logPath), { recursive: true });
|
|
16088
|
+
fs32.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
|
|
15727
16089
|
`);
|
|
15728
16090
|
}
|
|
15729
16091
|
const toolName = sanitize2(payload.tool_name ?? payload.name ?? "");
|
|
@@ -15736,8 +16098,8 @@ RAW: ${raw}
|
|
|
15736
16098
|
const isHumanDecision = blockedByContext.toLowerCase().includes("user") || blockedByContext.toLowerCase().includes("daemon") || blockedByContext.toLowerCase().includes("decision");
|
|
15737
16099
|
let ttyFd = null;
|
|
15738
16100
|
try {
|
|
15739
|
-
ttyFd =
|
|
15740
|
-
const writeTty = (line) =>
|
|
16101
|
+
ttyFd = fs32.openSync("/dev/tty", "w");
|
|
16102
|
+
const writeTty = (line) => fs32.writeSync(ttyFd, line + "\n");
|
|
15741
16103
|
if (blockedByContext.includes("DLP") || blockedByContext.includes("Secret Detected") || blockedByContext.includes("Credential Review")) {
|
|
15742
16104
|
writeTty(chalk9.bgRed.white.bold(`
|
|
15743
16105
|
\u{1F6A8} NODE9 DLP ALERT \u2014 CREDENTIAL DETECTED `));
|
|
@@ -15756,7 +16118,7 @@ RAW: ${raw}
|
|
|
15756
16118
|
} finally {
|
|
15757
16119
|
if (ttyFd !== null)
|
|
15758
16120
|
try {
|
|
15759
|
-
|
|
16121
|
+
fs32.closeSync(ttyFd);
|
|
15760
16122
|
} catch {
|
|
15761
16123
|
}
|
|
15762
16124
|
}
|
|
@@ -15792,17 +16154,17 @@ RAW: ${raw}
|
|
|
15792
16154
|
const safeSessionId = /^[A-Za-z0-9_\-]{1,128}$/.test(rawSessionId) ? rawSessionId : "";
|
|
15793
16155
|
if (skillPinCfg.enabled && safeSessionId) {
|
|
15794
16156
|
try {
|
|
15795
|
-
const sessionsDir =
|
|
15796
|
-
const flagPath =
|
|
16157
|
+
const sessionsDir = path33.join(os28.homedir(), ".node9", "skill-sessions");
|
|
16158
|
+
const flagPath = path33.join(sessionsDir, `${safeSessionId}.json`);
|
|
15797
16159
|
let flag = null;
|
|
15798
16160
|
try {
|
|
15799
|
-
flag = JSON.parse(
|
|
16161
|
+
flag = JSON.parse(fs32.readFileSync(flagPath, "utf-8"));
|
|
15800
16162
|
} catch {
|
|
15801
16163
|
}
|
|
15802
16164
|
const writeFlag = (data2) => {
|
|
15803
16165
|
try {
|
|
15804
|
-
|
|
15805
|
-
|
|
16166
|
+
fs32.mkdirSync(sessionsDir, { recursive: true });
|
|
16167
|
+
fs32.writeFileSync(
|
|
15806
16168
|
flagPath,
|
|
15807
16169
|
JSON.stringify({ ...data2, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
|
|
15808
16170
|
{ mode: 384 }
|
|
@@ -15813,8 +16175,8 @@ RAW: ${raw}
|
|
|
15813
16175
|
const sendSkillWarn = (detail, recoveryCmd) => {
|
|
15814
16176
|
let ttyFd = null;
|
|
15815
16177
|
try {
|
|
15816
|
-
ttyFd =
|
|
15817
|
-
const w = (line) =>
|
|
16178
|
+
ttyFd = fs32.openSync("/dev/tty", "w");
|
|
16179
|
+
const w = (line) => fs32.writeSync(ttyFd, line + "\n");
|
|
15818
16180
|
w(chalk9.yellow(`
|
|
15819
16181
|
\u26A0\uFE0F Node9: installed skill drift detected`));
|
|
15820
16182
|
w(chalk9.gray(` ${detail}`));
|
|
@@ -15829,7 +16191,7 @@ RAW: ${raw}
|
|
|
15829
16191
|
} finally {
|
|
15830
16192
|
if (ttyFd !== null)
|
|
15831
16193
|
try {
|
|
15832
|
-
|
|
16194
|
+
fs32.closeSync(ttyFd);
|
|
15833
16195
|
} catch {
|
|
15834
16196
|
}
|
|
15835
16197
|
}
|
|
@@ -15845,7 +16207,7 @@ RAW: ${raw}
|
|
|
15845
16207
|
return;
|
|
15846
16208
|
}
|
|
15847
16209
|
if (!flag || flag.state !== "verified" && flag.state !== "warned") {
|
|
15848
|
-
const absoluteCwd = typeof payload.cwd === "string" &&
|
|
16210
|
+
const absoluteCwd = typeof payload.cwd === "string" && path33.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
15849
16211
|
const extraRoots = skillPinCfg.roots;
|
|
15850
16212
|
const resolvedExtra = extraRoots.map((r) => resolveUserSkillRoot(r, absoluteCwd)).filter((r) => typeof r === "string");
|
|
15851
16213
|
const roots = [...defaultSkillRoots(absoluteCwd), ...resolvedExtra];
|
|
@@ -15886,10 +16248,10 @@ RAW: ${raw}
|
|
|
15886
16248
|
}
|
|
15887
16249
|
try {
|
|
15888
16250
|
const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1e3;
|
|
15889
|
-
for (const name of
|
|
15890
|
-
const p =
|
|
16251
|
+
for (const name of fs32.readdirSync(sessionsDir)) {
|
|
16252
|
+
const p = path33.join(sessionsDir, name);
|
|
15891
16253
|
try {
|
|
15892
|
-
if (
|
|
16254
|
+
if (fs32.statSync(p).mtimeMs < cutoff) fs32.unlinkSync(p);
|
|
15893
16255
|
} catch {
|
|
15894
16256
|
}
|
|
15895
16257
|
}
|
|
@@ -15899,9 +16261,9 @@ RAW: ${raw}
|
|
|
15899
16261
|
} catch (err2) {
|
|
15900
16262
|
if (process.env.NODE9_DEBUG === "1") {
|
|
15901
16263
|
try {
|
|
15902
|
-
const dbg =
|
|
16264
|
+
const dbg = path33.join(os28.homedir(), ".node9", "hook-debug.log");
|
|
15903
16265
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
15904
|
-
|
|
16266
|
+
fs32.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
|
|
15905
16267
|
`);
|
|
15906
16268
|
} catch {
|
|
15907
16269
|
}
|
|
@@ -15911,7 +16273,7 @@ RAW: ${raw}
|
|
|
15911
16273
|
if (shouldSnapshot(toolName, toolInput, config)) {
|
|
15912
16274
|
await createShadowSnapshot(toolName, toolInput, config.policy.snapshot.ignorePaths);
|
|
15913
16275
|
}
|
|
15914
|
-
const safeCwdForAuth = typeof payload.cwd === "string" &&
|
|
16276
|
+
const safeCwdForAuth = typeof payload.cwd === "string" && path33.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
15915
16277
|
const result = await authorizeHeadless(toolName, toolInput, meta, {
|
|
15916
16278
|
cwd: safeCwdForAuth
|
|
15917
16279
|
});
|
|
@@ -15923,12 +16285,12 @@ RAW: ${raw}
|
|
|
15923
16285
|
}
|
|
15924
16286
|
if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && !process.stdout.isTTY && config.settings.autoStartDaemon) {
|
|
15925
16287
|
try {
|
|
15926
|
-
const tty =
|
|
15927
|
-
|
|
16288
|
+
const tty = fs32.openSync("/dev/tty", "w");
|
|
16289
|
+
fs32.writeSync(
|
|
15928
16290
|
tty,
|
|
15929
16291
|
chalk9.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically...\n")
|
|
15930
16292
|
);
|
|
15931
|
-
|
|
16293
|
+
fs32.closeSync(tty);
|
|
15932
16294
|
} catch {
|
|
15933
16295
|
}
|
|
15934
16296
|
const daemonReady = await autoStartDaemonAndWait();
|
|
@@ -15955,9 +16317,9 @@ RAW: ${raw}
|
|
|
15955
16317
|
});
|
|
15956
16318
|
} catch (err2) {
|
|
15957
16319
|
if (process.env.NODE9_DEBUG === "1") {
|
|
15958
|
-
const logPath =
|
|
16320
|
+
const logPath = path33.join(os28.homedir(), ".node9", "hook-debug.log");
|
|
15959
16321
|
const errMsg = err2 instanceof Error ? err2.message : String(err2);
|
|
15960
|
-
|
|
16322
|
+
fs32.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
|
|
15961
16323
|
`);
|
|
15962
16324
|
}
|
|
15963
16325
|
process.exit(0);
|
|
@@ -15993,9 +16355,9 @@ RAW: ${raw}
|
|
|
15993
16355
|
// src/cli/commands/log.ts
|
|
15994
16356
|
init_audit();
|
|
15995
16357
|
init_config();
|
|
15996
|
-
import
|
|
15997
|
-
import
|
|
15998
|
-
import
|
|
16358
|
+
import fs33 from "fs";
|
|
16359
|
+
import path34 from "path";
|
|
16360
|
+
import os29 from "os";
|
|
15999
16361
|
init_daemon();
|
|
16000
16362
|
|
|
16001
16363
|
// src/utils/cp-mv-parser.ts
|
|
@@ -16061,7 +16423,7 @@ function registerLogCommand(program2) {
|
|
|
16061
16423
|
const payload = JSON.parse(raw);
|
|
16062
16424
|
const tool = sanitize3(payload.tool_name ?? payload.name ?? "unknown");
|
|
16063
16425
|
const rawInput = payload.tool_input ?? payload.args ?? {};
|
|
16064
|
-
const agent = 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;
|
|
16426
|
+
const agent = 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;
|
|
16065
16427
|
const entry = {
|
|
16066
16428
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16067
16429
|
tool,
|
|
@@ -16071,10 +16433,10 @@ function registerLogCommand(program2) {
|
|
|
16071
16433
|
};
|
|
16072
16434
|
if (agent) entry.agent = agent;
|
|
16073
16435
|
if (payload.session_id) entry.sessionId = payload.session_id;
|
|
16074
|
-
const logPath =
|
|
16075
|
-
if (!
|
|
16076
|
-
|
|
16077
|
-
|
|
16436
|
+
const logPath = path34.join(os29.homedir(), ".node9", "audit.log");
|
|
16437
|
+
if (!fs33.existsSync(path34.dirname(logPath)))
|
|
16438
|
+
fs33.mkdirSync(path34.dirname(logPath), { recursive: true });
|
|
16439
|
+
fs33.appendFileSync(logPath, JSON.stringify(entry) + "\n");
|
|
16078
16440
|
if ((tool === "Bash" || tool === "bash") && isDaemonRunning()) {
|
|
16079
16441
|
const command = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
|
|
16080
16442
|
if (command) {
|
|
@@ -16107,7 +16469,7 @@ function registerLogCommand(program2) {
|
|
|
16107
16469
|
}
|
|
16108
16470
|
}
|
|
16109
16471
|
}
|
|
16110
|
-
const safeCwd = typeof payload.cwd === "string" &&
|
|
16472
|
+
const safeCwd = typeof payload.cwd === "string" && path34.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
16111
16473
|
const config = getConfig(safeCwd);
|
|
16112
16474
|
if ((tool === "Bash" || tool === "bash") && config.settings.enableUndo !== false) {
|
|
16113
16475
|
const bashCommand = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
|
|
@@ -16128,9 +16490,9 @@ function registerLogCommand(program2) {
|
|
|
16128
16490
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
16129
16491
|
process.stderr.write(`[Node9] audit log error: ${msg}
|
|
16130
16492
|
`);
|
|
16131
|
-
const debugPath =
|
|
16493
|
+
const debugPath = path34.join(os29.homedir(), ".node9", "hook-debug.log");
|
|
16132
16494
|
try {
|
|
16133
|
-
|
|
16495
|
+
fs33.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
|
|
16134
16496
|
`);
|
|
16135
16497
|
} catch {
|
|
16136
16498
|
}
|
|
@@ -16531,13 +16893,13 @@ function registerConfigShowCommand(program2) {
|
|
|
16531
16893
|
// src/cli/commands/doctor.ts
|
|
16532
16894
|
init_daemon();
|
|
16533
16895
|
import chalk11 from "chalk";
|
|
16534
|
-
import
|
|
16535
|
-
import
|
|
16536
|
-
import
|
|
16896
|
+
import fs34 from "fs";
|
|
16897
|
+
import path35 from "path";
|
|
16898
|
+
import os30 from "os";
|
|
16537
16899
|
import { execSync } from "child_process";
|
|
16538
16900
|
function registerDoctorCommand(program2, version2) {
|
|
16539
16901
|
program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(() => {
|
|
16540
|
-
const homeDir2 =
|
|
16902
|
+
const homeDir2 = os30.homedir();
|
|
16541
16903
|
let failures = 0;
|
|
16542
16904
|
function pass(msg) {
|
|
16543
16905
|
console.log(chalk11.green(" \u2705 ") + msg);
|
|
@@ -16586,10 +16948,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16586
16948
|
);
|
|
16587
16949
|
}
|
|
16588
16950
|
section("Configuration");
|
|
16589
|
-
const globalConfigPath =
|
|
16590
|
-
if (
|
|
16951
|
+
const globalConfigPath = path35.join(homeDir2, ".node9", "config.json");
|
|
16952
|
+
if (fs34.existsSync(globalConfigPath)) {
|
|
16591
16953
|
try {
|
|
16592
|
-
JSON.parse(
|
|
16954
|
+
JSON.parse(fs34.readFileSync(globalConfigPath, "utf-8"));
|
|
16593
16955
|
pass("~/.node9/config.json found and valid");
|
|
16594
16956
|
} catch {
|
|
16595
16957
|
fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
|
|
@@ -16597,10 +16959,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16597
16959
|
} else {
|
|
16598
16960
|
warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
|
|
16599
16961
|
}
|
|
16600
|
-
const projectConfigPath =
|
|
16601
|
-
if (
|
|
16962
|
+
const projectConfigPath = path35.join(process.cwd(), "node9.config.json");
|
|
16963
|
+
if (fs34.existsSync(projectConfigPath)) {
|
|
16602
16964
|
try {
|
|
16603
|
-
JSON.parse(
|
|
16965
|
+
JSON.parse(fs34.readFileSync(projectConfigPath, "utf-8"));
|
|
16604
16966
|
pass("node9.config.json found and valid (project)");
|
|
16605
16967
|
} catch {
|
|
16606
16968
|
fail(
|
|
@@ -16609,8 +16971,8 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16609
16971
|
);
|
|
16610
16972
|
}
|
|
16611
16973
|
}
|
|
16612
|
-
const credsPath =
|
|
16613
|
-
if (
|
|
16974
|
+
const credsPath = path35.join(homeDir2, ".node9", "credentials.json");
|
|
16975
|
+
if (fs34.existsSync(credsPath)) {
|
|
16614
16976
|
pass("Cloud credentials found (~/.node9/credentials.json)");
|
|
16615
16977
|
} else {
|
|
16616
16978
|
warn(
|
|
@@ -16619,10 +16981,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16619
16981
|
);
|
|
16620
16982
|
}
|
|
16621
16983
|
section("Agent Hooks");
|
|
16622
|
-
const claudeSettingsPath =
|
|
16623
|
-
if (
|
|
16984
|
+
const claudeSettingsPath = path35.join(homeDir2, ".claude", "settings.json");
|
|
16985
|
+
if (fs34.existsSync(claudeSettingsPath)) {
|
|
16624
16986
|
try {
|
|
16625
|
-
const cs = JSON.parse(
|
|
16987
|
+
const cs = JSON.parse(fs34.readFileSync(claudeSettingsPath, "utf-8"));
|
|
16626
16988
|
const hasHook = cs.hooks?.PreToolUse?.some(
|
|
16627
16989
|
(m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
|
|
16628
16990
|
);
|
|
@@ -16638,10 +17000,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16638
17000
|
} else {
|
|
16639
17001
|
warn("Claude Code \u2014 not configured", "Run: node9 setup claude");
|
|
16640
17002
|
}
|
|
16641
|
-
const geminiSettingsPath =
|
|
16642
|
-
if (
|
|
17003
|
+
const geminiSettingsPath = path35.join(homeDir2, ".gemini", "settings.json");
|
|
17004
|
+
if (fs34.existsSync(geminiSettingsPath)) {
|
|
16643
17005
|
try {
|
|
16644
|
-
const gs = JSON.parse(
|
|
17006
|
+
const gs = JSON.parse(fs34.readFileSync(geminiSettingsPath, "utf-8"));
|
|
16645
17007
|
const hasHook = gs.hooks?.BeforeTool?.some(
|
|
16646
17008
|
(m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
|
|
16647
17009
|
);
|
|
@@ -16657,10 +17019,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16657
17019
|
} else {
|
|
16658
17020
|
warn("Gemini CLI \u2014 not configured", "Run: node9 setup gemini (skip if not using Gemini)");
|
|
16659
17021
|
}
|
|
16660
|
-
const cursorHooksPath =
|
|
16661
|
-
if (
|
|
17022
|
+
const cursorHooksPath = path35.join(homeDir2, ".cursor", "hooks.json");
|
|
17023
|
+
if (fs34.existsSync(cursorHooksPath)) {
|
|
16662
17024
|
try {
|
|
16663
|
-
const cur = JSON.parse(
|
|
17025
|
+
const cur = JSON.parse(fs34.readFileSync(cursorHooksPath, "utf-8"));
|
|
16664
17026
|
const hasHook = cur.hooks?.preToolUse?.some(
|
|
16665
17027
|
(h) => h.command?.includes("node9") || h.command?.includes("cli.js")
|
|
16666
17028
|
);
|
|
@@ -16700,9 +17062,9 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16700
17062
|
|
|
16701
17063
|
// src/cli/commands/audit.ts
|
|
16702
17064
|
import chalk12 from "chalk";
|
|
16703
|
-
import
|
|
16704
|
-
import
|
|
16705
|
-
import
|
|
17065
|
+
import fs35 from "fs";
|
|
17066
|
+
import path36 from "path";
|
|
17067
|
+
import os31 from "os";
|
|
16706
17068
|
function formatRelativeTime(timestamp) {
|
|
16707
17069
|
const diff = Date.now() - new Date(timestamp).getTime();
|
|
16708
17070
|
const sec = Math.floor(diff / 1e3);
|
|
@@ -16715,14 +17077,14 @@ function formatRelativeTime(timestamp) {
|
|
|
16715
17077
|
}
|
|
16716
17078
|
function registerAuditCommand(program2) {
|
|
16717
17079
|
program2.command("audit").description("View local execution audit log").option("--tail <n>", "Number of entries to show", "20").option("--tool <pattern>", "Filter by tool name (substring match)").option("--deny", "Show only denied actions").option("--json", "Output raw JSON").action((options) => {
|
|
16718
|
-
const logPath =
|
|
16719
|
-
if (!
|
|
17080
|
+
const logPath = path36.join(os31.homedir(), ".node9", "audit.log");
|
|
17081
|
+
if (!fs35.existsSync(logPath)) {
|
|
16720
17082
|
console.log(
|
|
16721
17083
|
chalk12.yellow("No audit logs found. Run node9 with an agent to generate entries.")
|
|
16722
17084
|
);
|
|
16723
17085
|
return;
|
|
16724
17086
|
}
|
|
16725
|
-
const raw =
|
|
17087
|
+
const raw = fs35.readFileSync(logPath, "utf-8");
|
|
16726
17088
|
const lines = raw.split("\n").filter((l) => l.trim() !== "");
|
|
16727
17089
|
let entries = lines.flatMap((line) => {
|
|
16728
17090
|
try {
|
|
@@ -16780,9 +17142,9 @@ import chalk13 from "chalk";
|
|
|
16780
17142
|
// src/cli/aggregate/report-audit.ts
|
|
16781
17143
|
init_costSync();
|
|
16782
17144
|
init_litellm();
|
|
16783
|
-
import
|
|
16784
|
-
import
|
|
16785
|
-
import
|
|
17145
|
+
import fs36 from "fs";
|
|
17146
|
+
import os32 from "os";
|
|
17147
|
+
import path37 from "path";
|
|
16786
17148
|
var TEST_COMMAND_RE3 = /(?:^|\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;
|
|
16787
17149
|
function buildTestTimestamps(allEntries) {
|
|
16788
17150
|
const testTs = /* @__PURE__ */ new Set();
|
|
@@ -16862,8 +17224,8 @@ function getDateRange(period, now) {
|
|
|
16862
17224
|
}
|
|
16863
17225
|
}
|
|
16864
17226
|
function parseAuditLog(logPath) {
|
|
16865
|
-
if (!
|
|
16866
|
-
const raw =
|
|
17227
|
+
if (!fs36.existsSync(logPath)) return [];
|
|
17228
|
+
const raw = fs36.readFileSync(logPath, "utf-8");
|
|
16867
17229
|
return raw.split("\n").flatMap((line) => {
|
|
16868
17230
|
if (!line.trim()) return [];
|
|
16869
17231
|
try {
|
|
@@ -16923,25 +17285,25 @@ function freezeClaudeCost(acc) {
|
|
|
16923
17285
|
};
|
|
16924
17286
|
}
|
|
16925
17287
|
function processClaudeCostProject(proj, projectsDir, start, end, acc) {
|
|
16926
|
-
const projPath =
|
|
17288
|
+
const projPath = path37.join(projectsDir, proj);
|
|
16927
17289
|
let files;
|
|
16928
17290
|
try {
|
|
16929
|
-
const stat =
|
|
17291
|
+
const stat = fs36.statSync(projPath);
|
|
16930
17292
|
if (!stat.isDirectory()) return;
|
|
16931
|
-
files =
|
|
17293
|
+
files = fs36.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
16932
17294
|
} catch {
|
|
16933
17295
|
return;
|
|
16934
17296
|
}
|
|
16935
17297
|
const startMs = start.getTime();
|
|
16936
17298
|
for (const file of files) {
|
|
16937
|
-
const filePath =
|
|
17299
|
+
const filePath = path37.join(projPath, file);
|
|
16938
17300
|
try {
|
|
16939
|
-
if (
|
|
17301
|
+
if (fs36.statSync(filePath).mtimeMs < startMs) continue;
|
|
16940
17302
|
} catch {
|
|
16941
17303
|
continue;
|
|
16942
17304
|
}
|
|
16943
17305
|
try {
|
|
16944
|
-
const raw =
|
|
17306
|
+
const raw = fs36.readFileSync(filePath, "utf-8");
|
|
16945
17307
|
for (const line of raw.split("\n")) {
|
|
16946
17308
|
if (!line.trim()) continue;
|
|
16947
17309
|
let entry;
|
|
@@ -16991,10 +17353,10 @@ function processClaudeCostProject(proj, projectsDir, start, end, acc) {
|
|
|
16991
17353
|
}
|
|
16992
17354
|
function loadClaudeCost(start, end, projectsDir) {
|
|
16993
17355
|
const acc = emptyClaudeCostAccumulator();
|
|
16994
|
-
if (!
|
|
17356
|
+
if (!fs36.existsSync(projectsDir)) return freezeClaudeCost(acc);
|
|
16995
17357
|
let dirs;
|
|
16996
17358
|
try {
|
|
16997
|
-
dirs =
|
|
17359
|
+
dirs = fs36.readdirSync(projectsDir);
|
|
16998
17360
|
} catch {
|
|
16999
17361
|
return freezeClaudeCost(acc);
|
|
17000
17362
|
}
|
|
@@ -17006,7 +17368,7 @@ function loadClaudeCost(start, end, projectsDir) {
|
|
|
17006
17368
|
function processCodexCostFile(filePath, start, end, acc) {
|
|
17007
17369
|
let lines;
|
|
17008
17370
|
try {
|
|
17009
|
-
lines =
|
|
17371
|
+
lines = fs36.readFileSync(filePath, "utf-8").split("\n");
|
|
17010
17372
|
} catch {
|
|
17011
17373
|
return;
|
|
17012
17374
|
}
|
|
@@ -17051,31 +17413,31 @@ function processCodexCostFile(filePath, start, end, acc) {
|
|
|
17051
17413
|
}
|
|
17052
17414
|
function listCodexSessionFiles(sessionsBase) {
|
|
17053
17415
|
const jsonlFiles = [];
|
|
17054
|
-
if (!
|
|
17416
|
+
if (!fs36.existsSync(sessionsBase)) return jsonlFiles;
|
|
17055
17417
|
try {
|
|
17056
|
-
for (const year of
|
|
17057
|
-
const yearPath =
|
|
17418
|
+
for (const year of fs36.readdirSync(sessionsBase)) {
|
|
17419
|
+
const yearPath = path37.join(sessionsBase, year);
|
|
17058
17420
|
try {
|
|
17059
|
-
if (!
|
|
17421
|
+
if (!fs36.statSync(yearPath).isDirectory()) continue;
|
|
17060
17422
|
} catch {
|
|
17061
17423
|
continue;
|
|
17062
17424
|
}
|
|
17063
|
-
for (const month of
|
|
17064
|
-
const monthPath =
|
|
17425
|
+
for (const month of fs36.readdirSync(yearPath)) {
|
|
17426
|
+
const monthPath = path37.join(yearPath, month);
|
|
17065
17427
|
try {
|
|
17066
|
-
if (!
|
|
17428
|
+
if (!fs36.statSync(monthPath).isDirectory()) continue;
|
|
17067
17429
|
} catch {
|
|
17068
17430
|
continue;
|
|
17069
17431
|
}
|
|
17070
|
-
for (const day of
|
|
17071
|
-
const dayPath =
|
|
17432
|
+
for (const day of fs36.readdirSync(monthPath)) {
|
|
17433
|
+
const dayPath = path37.join(monthPath, day);
|
|
17072
17434
|
try {
|
|
17073
|
-
if (!
|
|
17435
|
+
if (!fs36.statSync(dayPath).isDirectory()) continue;
|
|
17074
17436
|
} catch {
|
|
17075
17437
|
continue;
|
|
17076
17438
|
}
|
|
17077
|
-
for (const file of
|
|
17078
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(
|
|
17439
|
+
for (const file of fs36.readdirSync(dayPath)) {
|
|
17440
|
+
if (file.endsWith(".jsonl")) jsonlFiles.push(path37.join(dayPath, file));
|
|
17079
17441
|
}
|
|
17080
17442
|
}
|
|
17081
17443
|
}
|
|
@@ -17128,13 +17490,13 @@ function freezeGeminiCost(acc) {
|
|
|
17128
17490
|
function processGeminiCostFile(filePath, projectKey, start, end, acc) {
|
|
17129
17491
|
const startMs = start.getTime();
|
|
17130
17492
|
try {
|
|
17131
|
-
if (
|
|
17493
|
+
if (fs36.statSync(filePath).mtimeMs < startMs) return;
|
|
17132
17494
|
} catch {
|
|
17133
17495
|
return;
|
|
17134
17496
|
}
|
|
17135
17497
|
let raw;
|
|
17136
17498
|
try {
|
|
17137
|
-
raw =
|
|
17499
|
+
raw = fs36.readFileSync(filePath, "utf-8");
|
|
17138
17500
|
} catch {
|
|
17139
17501
|
return;
|
|
17140
17502
|
}
|
|
@@ -17183,30 +17545,30 @@ function listGeminiSessionFiles(geminiTmpDir) {
|
|
|
17183
17545
|
const out = [];
|
|
17184
17546
|
let dirs;
|
|
17185
17547
|
try {
|
|
17186
|
-
if (!
|
|
17187
|
-
dirs =
|
|
17548
|
+
if (!fs36.statSync(geminiTmpDir).isDirectory()) return out;
|
|
17549
|
+
dirs = fs36.readdirSync(geminiTmpDir);
|
|
17188
17550
|
} catch {
|
|
17189
17551
|
return out;
|
|
17190
17552
|
}
|
|
17191
17553
|
for (const proj of dirs) {
|
|
17192
|
-
const chatsDir =
|
|
17554
|
+
const chatsDir = path37.join(geminiTmpDir, proj, "chats");
|
|
17193
17555
|
let files;
|
|
17194
17556
|
try {
|
|
17195
|
-
if (!
|
|
17196
|
-
files =
|
|
17557
|
+
if (!fs36.statSync(chatsDir).isDirectory()) continue;
|
|
17558
|
+
files = fs36.readdirSync(chatsDir);
|
|
17197
17559
|
} catch {
|
|
17198
17560
|
continue;
|
|
17199
17561
|
}
|
|
17200
17562
|
for (const f of files) {
|
|
17201
17563
|
if (!f.endsWith(".jsonl")) continue;
|
|
17202
|
-
out.push({ projectKey: proj, file:
|
|
17564
|
+
out.push({ projectKey: proj, file: path37.join(chatsDir, f) });
|
|
17203
17565
|
}
|
|
17204
17566
|
}
|
|
17205
17567
|
return out;
|
|
17206
17568
|
}
|
|
17207
17569
|
function loadGeminiCost(start, end, geminiTmpDir) {
|
|
17208
17570
|
const acc = emptyGeminiAccumulator();
|
|
17209
|
-
if (!
|
|
17571
|
+
if (!fs36.existsSync(geminiTmpDir)) return freezeGeminiCost(acc);
|
|
17210
17572
|
for (const { projectKey, file } of listGeminiSessionFiles(geminiTmpDir)) {
|
|
17211
17573
|
processGeminiCostFile(file, projectKey, start, end, acc);
|
|
17212
17574
|
}
|
|
@@ -17214,11 +17576,11 @@ function loadGeminiCost(start, end, geminiTmpDir) {
|
|
|
17214
17576
|
}
|
|
17215
17577
|
function aggregateReportFromAudit(period, opts = {}) {
|
|
17216
17578
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
17217
|
-
const auditLogPath = opts.auditLogPath ??
|
|
17218
|
-
const claudeProjectsDir = opts.claudeProjectsDir ??
|
|
17219
|
-
const codexSessionsDir = opts.codexSessionsDir ??
|
|
17220
|
-
const geminiTmpDir = opts.geminiTmpDir ??
|
|
17221
|
-
const hasAuditFile =
|
|
17579
|
+
const auditLogPath = opts.auditLogPath ?? path37.join(os32.homedir(), ".node9", "audit.log");
|
|
17580
|
+
const claudeProjectsDir = opts.claudeProjectsDir ?? path37.join(os32.homedir(), ".claude", "projects");
|
|
17581
|
+
const codexSessionsDir = opts.codexSessionsDir ?? path37.join(os32.homedir(), ".codex", "sessions");
|
|
17582
|
+
const geminiTmpDir = opts.geminiTmpDir ?? path37.join(os32.homedir(), ".gemini", "tmp");
|
|
17583
|
+
const hasAuditFile = fs36.existsSync(auditLogPath);
|
|
17222
17584
|
const allEntries = opts.preloadedAuditEntries ?? parseAuditLog(auditLogPath);
|
|
17223
17585
|
const unackedDlp = allEntries.filter((e) => e.source === "response-dlp");
|
|
17224
17586
|
const { start, end } = getDateRange(period, now);
|
|
@@ -17916,12 +18278,12 @@ function registerDaemonCommand(program2) {
|
|
|
17916
18278
|
init_core();
|
|
17917
18279
|
init_daemon();
|
|
17918
18280
|
import chalk15 from "chalk";
|
|
17919
|
-
import
|
|
17920
|
-
import
|
|
17921
|
-
import
|
|
18281
|
+
import fs37 from "fs";
|
|
18282
|
+
import path38 from "path";
|
|
18283
|
+
import os33 from "os";
|
|
17922
18284
|
function readJson2(filePath) {
|
|
17923
18285
|
try {
|
|
17924
|
-
if (
|
|
18286
|
+
if (fs37.existsSync(filePath)) return JSON.parse(fs37.readFileSync(filePath, "utf-8"));
|
|
17925
18287
|
} catch {
|
|
17926
18288
|
}
|
|
17927
18289
|
return null;
|
|
@@ -17986,28 +18348,28 @@ function registerStatusCommand(program2) {
|
|
|
17986
18348
|
console.log("");
|
|
17987
18349
|
const modeLabel = settings.mode === "audit" ? chalk15.blue("audit") : settings.mode === "strict" ? chalk15.red("strict") : chalk15.white("standard");
|
|
17988
18350
|
console.log(` Mode: ${modeLabel}`);
|
|
17989
|
-
const projectConfig =
|
|
17990
|
-
const globalConfig =
|
|
18351
|
+
const projectConfig = path38.join(process.cwd(), "node9.config.json");
|
|
18352
|
+
const globalConfig = path38.join(os33.homedir(), ".node9", "config.json");
|
|
17991
18353
|
console.log(
|
|
17992
|
-
` Local: ${
|
|
18354
|
+
` Local: ${fs37.existsSync(projectConfig) ? chalk15.green("Active (node9.config.json)") : chalk15.gray("Not present")}`
|
|
17993
18355
|
);
|
|
17994
18356
|
console.log(
|
|
17995
|
-
` Global: ${
|
|
18357
|
+
` Global: ${fs37.existsSync(globalConfig) ? chalk15.green("Active (~/.node9/config.json)") : chalk15.gray("Not present")}`
|
|
17996
18358
|
);
|
|
17997
18359
|
if (mergedConfig.policy.sandboxPaths.length > 0) {
|
|
17998
18360
|
console.log(
|
|
17999
18361
|
` Sandbox: ${chalk15.green(`${mergedConfig.policy.sandboxPaths.length} safe zones active`)}`
|
|
18000
18362
|
);
|
|
18001
18363
|
}
|
|
18002
|
-
const homeDir2 =
|
|
18364
|
+
const homeDir2 = os33.homedir();
|
|
18003
18365
|
const claudeSettings = readJson2(
|
|
18004
|
-
|
|
18366
|
+
path38.join(homeDir2, ".claude", "settings.json")
|
|
18005
18367
|
);
|
|
18006
|
-
const claudeConfig = readJson2(
|
|
18368
|
+
const claudeConfig = readJson2(path38.join(homeDir2, ".claude.json"));
|
|
18007
18369
|
const geminiSettings = readJson2(
|
|
18008
|
-
|
|
18370
|
+
path38.join(homeDir2, ".gemini", "settings.json")
|
|
18009
18371
|
);
|
|
18010
|
-
const cursorConfig = readJson2(
|
|
18372
|
+
const cursorConfig = readJson2(path38.join(homeDir2, ".cursor", "mcp.json"));
|
|
18011
18373
|
const agentFound = claudeSettings || claudeConfig || geminiSettings || cursorConfig;
|
|
18012
18374
|
if (agentFound) {
|
|
18013
18375
|
console.log("");
|
|
@@ -18070,9 +18432,9 @@ init_setup();
|
|
|
18070
18432
|
init_shields();
|
|
18071
18433
|
init_service();
|
|
18072
18434
|
import chalk16 from "chalk";
|
|
18073
|
-
import
|
|
18074
|
-
import
|
|
18075
|
-
import
|
|
18435
|
+
import fs38 from "fs";
|
|
18436
|
+
import path39 from "path";
|
|
18437
|
+
import os34 from "os";
|
|
18076
18438
|
import https4 from "https";
|
|
18077
18439
|
var DEFAULT_SHIELDS = ["bash-safe", "filesystem", "project-jail"];
|
|
18078
18440
|
function fireTelemetryPing(agents) {
|
|
@@ -18148,15 +18510,15 @@ function registerInitCommand(program2) {
|
|
|
18148
18510
|
}
|
|
18149
18511
|
console.log("");
|
|
18150
18512
|
}
|
|
18151
|
-
const configPath =
|
|
18152
|
-
if (
|
|
18513
|
+
const configPath = path39.join(os34.homedir(), ".node9", "config.json");
|
|
18514
|
+
if (fs38.existsSync(configPath) && !options.force) {
|
|
18153
18515
|
try {
|
|
18154
|
-
const existing = JSON.parse(
|
|
18516
|
+
const existing = JSON.parse(fs38.readFileSync(configPath, "utf-8"));
|
|
18155
18517
|
const settings = existing.settings ?? {};
|
|
18156
18518
|
if (settings.mode !== chosenMode) {
|
|
18157
18519
|
settings.mode = chosenMode;
|
|
18158
18520
|
existing.settings = settings;
|
|
18159
|
-
|
|
18521
|
+
fs38.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
|
|
18160
18522
|
console.log(chalk16.green(`\u2705 Mode updated: ${chosenMode}`));
|
|
18161
18523
|
} else {
|
|
18162
18524
|
console.log(chalk16.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
|
|
@@ -18169,9 +18531,9 @@ function registerInitCommand(program2) {
|
|
|
18169
18531
|
...DEFAULT_CONFIG,
|
|
18170
18532
|
settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
|
|
18171
18533
|
};
|
|
18172
|
-
const dir =
|
|
18173
|
-
if (!
|
|
18174
|
-
|
|
18534
|
+
const dir = path39.dirname(configPath);
|
|
18535
|
+
if (!fs38.existsSync(dir)) fs38.mkdirSync(dir, { recursive: true });
|
|
18536
|
+
fs38.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
|
|
18175
18537
|
console.log(chalk16.green(`\u2705 Config created: ${configPath}`));
|
|
18176
18538
|
console.log(chalk16.gray(` Mode: ${chosenMode}`));
|
|
18177
18539
|
}
|
|
@@ -18269,7 +18631,7 @@ function registerInitCommand(program2) {
|
|
|
18269
18631
|
}
|
|
18270
18632
|
|
|
18271
18633
|
// src/cli/commands/undo.ts
|
|
18272
|
-
import
|
|
18634
|
+
import path40 from "path";
|
|
18273
18635
|
import chalk18 from "chalk";
|
|
18274
18636
|
|
|
18275
18637
|
// src/tui/undo-navigator.ts
|
|
@@ -18428,7 +18790,7 @@ function findMatchingCwd(startDir, history) {
|
|
|
18428
18790
|
let dir = startDir;
|
|
18429
18791
|
while (true) {
|
|
18430
18792
|
if (cwds.has(dir)) return dir;
|
|
18431
|
-
const parent =
|
|
18793
|
+
const parent = path40.dirname(dir);
|
|
18432
18794
|
if (parent === dir) return null;
|
|
18433
18795
|
dir = parent;
|
|
18434
18796
|
}
|
|
@@ -18561,90 +18923,7 @@ import chalk19 from "chalk";
|
|
|
18561
18923
|
import { spawn as spawn7 } from "child_process";
|
|
18562
18924
|
import { execa as execa2 } from "execa";
|
|
18563
18925
|
init_provenance();
|
|
18564
|
-
|
|
18565
|
-
// src/mcp-pin.ts
|
|
18566
|
-
import fs38 from "fs";
|
|
18567
|
-
import path40 from "path";
|
|
18568
|
-
import os34 from "os";
|
|
18569
|
-
import crypto5 from "crypto";
|
|
18570
|
-
function getPinsFilePath2() {
|
|
18571
|
-
return path40.join(os34.homedir(), ".node9", "mcp-pins.json");
|
|
18572
|
-
}
|
|
18573
|
-
function hashToolDefinitions(tools) {
|
|
18574
|
-
const sorted = [...tools].sort((a, b) => {
|
|
18575
|
-
const nameA = a.name ?? "";
|
|
18576
|
-
const nameB = b.name ?? "";
|
|
18577
|
-
return nameA.localeCompare(nameB);
|
|
18578
|
-
});
|
|
18579
|
-
const canonical = JSON.stringify(sorted);
|
|
18580
|
-
return crypto5.createHash("sha256").update(canonical).digest("hex");
|
|
18581
|
-
}
|
|
18582
|
-
function getServerKey(upstreamCommand) {
|
|
18583
|
-
return crypto5.createHash("sha256").update(upstreamCommand).digest("hex").slice(0, 16);
|
|
18584
|
-
}
|
|
18585
|
-
function readMcpPinsSafe() {
|
|
18586
|
-
const filePath = getPinsFilePath2();
|
|
18587
|
-
try {
|
|
18588
|
-
const raw = fs38.readFileSync(filePath, "utf-8");
|
|
18589
|
-
if (!raw.trim()) {
|
|
18590
|
-
return { ok: false, reason: "corrupt", detail: "empty file" };
|
|
18591
|
-
}
|
|
18592
|
-
const parsed = JSON.parse(raw);
|
|
18593
|
-
if (!parsed.servers || typeof parsed.servers !== "object" || Array.isArray(parsed.servers)) {
|
|
18594
|
-
return { ok: false, reason: "corrupt", detail: "invalid structure: missing servers object" };
|
|
18595
|
-
}
|
|
18596
|
-
return { ok: true, pins: { servers: parsed.servers } };
|
|
18597
|
-
} catch (err2) {
|
|
18598
|
-
if (err2.code === "ENOENT") {
|
|
18599
|
-
return { ok: false, reason: "missing" };
|
|
18600
|
-
}
|
|
18601
|
-
return { ok: false, reason: "corrupt", detail: String(err2) };
|
|
18602
|
-
}
|
|
18603
|
-
}
|
|
18604
|
-
function readMcpPins() {
|
|
18605
|
-
const result = readMcpPinsSafe();
|
|
18606
|
-
if (result.ok) return result.pins;
|
|
18607
|
-
if (result.reason === "missing") return { servers: {} };
|
|
18608
|
-
throw new Error(`[node9] MCP pin file is corrupt: ${result.detail}`);
|
|
18609
|
-
}
|
|
18610
|
-
function writeMcpPins(data) {
|
|
18611
|
-
const filePath = getPinsFilePath2();
|
|
18612
|
-
fs38.mkdirSync(path40.dirname(filePath), { recursive: true });
|
|
18613
|
-
const tmp = `${filePath}.${crypto5.randomBytes(6).toString("hex")}.tmp`;
|
|
18614
|
-
fs38.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
18615
|
-
fs38.renameSync(tmp, filePath);
|
|
18616
|
-
}
|
|
18617
|
-
function checkPin(serverKey, currentHash) {
|
|
18618
|
-
const result = readMcpPinsSafe();
|
|
18619
|
-
if (!result.ok) {
|
|
18620
|
-
if (result.reason === "missing") return "new";
|
|
18621
|
-
return "corrupt";
|
|
18622
|
-
}
|
|
18623
|
-
const entry = result.pins.servers[serverKey];
|
|
18624
|
-
if (!entry) return "new";
|
|
18625
|
-
return entry.toolsHash === currentHash ? "match" : "mismatch";
|
|
18626
|
-
}
|
|
18627
|
-
function updatePin(serverKey, label, toolsHash, toolNames) {
|
|
18628
|
-
const pins = readMcpPins();
|
|
18629
|
-
pins.servers[serverKey] = {
|
|
18630
|
-
label,
|
|
18631
|
-
toolsHash,
|
|
18632
|
-
toolNames,
|
|
18633
|
-
toolCount: toolNames.length,
|
|
18634
|
-
pinnedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
18635
|
-
};
|
|
18636
|
-
writeMcpPins(pins);
|
|
18637
|
-
}
|
|
18638
|
-
function removePin2(serverKey) {
|
|
18639
|
-
const pins = readMcpPins();
|
|
18640
|
-
delete pins.servers[serverKey];
|
|
18641
|
-
writeMcpPins(pins);
|
|
18642
|
-
}
|
|
18643
|
-
function clearAllPins2() {
|
|
18644
|
-
writeMcpPins({ servers: {} });
|
|
18645
|
-
}
|
|
18646
|
-
|
|
18647
|
-
// src/mcp-gateway/index.ts
|
|
18926
|
+
init_mcp_pin();
|
|
18648
18927
|
init_mcp_tools();
|
|
18649
18928
|
init_daemon();
|
|
18650
18929
|
function sanitize4(value) {
|
|
@@ -18706,6 +18985,7 @@ function tokenize4(cmd) {
|
|
|
18706
18985
|
return tokens;
|
|
18707
18986
|
}
|
|
18708
18987
|
async function runMcpGateway(upstreamCommand) {
|
|
18988
|
+
const gatewayCwd = process.cwd();
|
|
18709
18989
|
const commandParts = tokenize4(upstreamCommand);
|
|
18710
18990
|
const cmd = commandParts[0];
|
|
18711
18991
|
const cmdArgs = commandParts.slice(1);
|
|
@@ -18928,7 +19208,7 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
18928
19208
|
if (parsed.result && Array.isArray(parsed.result.tools)) {
|
|
18929
19209
|
const tools = parsed.result.tools || [];
|
|
18930
19210
|
const currentHash = hashToolDefinitions(tools);
|
|
18931
|
-
const pinStatus = checkPin(serverKey, currentHash);
|
|
19211
|
+
const pinStatus = checkPin(serverKey, currentHash, gatewayCwd);
|
|
18932
19212
|
const token = getInternalToken();
|
|
18933
19213
|
if (isDaemonRunning() && process.env.NODE9_TESTING !== "1") {
|
|
18934
19214
|
const toolSummary = tools.map((t) => ({ name: t.name, description: t.description }));
|
|
@@ -19812,27 +20092,46 @@ function registerTrustCommand(program2) {
|
|
|
19812
20092
|
}
|
|
19813
20093
|
|
|
19814
20094
|
// src/cli/commands/mcp-pin.ts
|
|
20095
|
+
init_mcp_pin();
|
|
19815
20096
|
import chalk21 from "chalk";
|
|
20097
|
+
import fs40 from "fs";
|
|
19816
20098
|
function registerMcpPinCommand(program2) {
|
|
19817
20099
|
const pinCmd = program2.command("mcp").description("Manage MCP server tool definition pinning (rug pull defense)");
|
|
19818
20100
|
const pinSubCmd = pinCmd.command("pin").description("Manage pinned MCP server tool definitions");
|
|
19819
20101
|
pinSubCmd.command("list").description("Show all pinned MCP servers and their tool definition hashes").action(() => {
|
|
19820
|
-
const
|
|
19821
|
-
|
|
19822
|
-
|
|
19823
|
-
|
|
19824
|
-
|
|
19825
|
-
|
|
19826
|
-
);
|
|
19827
|
-
|
|
20102
|
+
const found = findPinsFilePath(process.cwd());
|
|
20103
|
+
const homeResult = readMcpPinsSafe();
|
|
20104
|
+
let repoEntries = {};
|
|
20105
|
+
let repoCorrupt = false;
|
|
20106
|
+
if (found.source === "repo") {
|
|
20107
|
+
try {
|
|
20108
|
+
const raw = fs40.readFileSync(found.path, "utf-8");
|
|
20109
|
+
const parsed = JSON.parse(raw);
|
|
20110
|
+
repoEntries = parsed.servers ?? {};
|
|
20111
|
+
} catch {
|
|
20112
|
+
repoCorrupt = true;
|
|
19828
20113
|
}
|
|
20114
|
+
}
|
|
20115
|
+
if (repoCorrupt) {
|
|
19829
20116
|
console.error(chalk21.red(`
|
|
19830
|
-
\u274C
|
|
20117
|
+
\u274C Repo pin file at ${found.path} is corrupt or unreadable.`));
|
|
20118
|
+
process.exit(1);
|
|
20119
|
+
}
|
|
20120
|
+
if (!homeResult.ok && homeResult.reason === "corrupt") {
|
|
20121
|
+
console.error(chalk21.red(`
|
|
20122
|
+
\u274C Home pin file is corrupt: ${homeResult.detail}`));
|
|
19831
20123
|
console.error(chalk21.yellow(" Run: node9 mcp pin reset\n"));
|
|
19832
20124
|
process.exit(1);
|
|
19833
20125
|
}
|
|
19834
|
-
const
|
|
19835
|
-
|
|
20126
|
+
const homeEntries = homeResult.ok ? homeResult.pins.servers : {};
|
|
20127
|
+
const merged = /* @__PURE__ */ new Map();
|
|
20128
|
+
for (const [key, entry] of Object.entries(homeEntries)) {
|
|
20129
|
+
merged.set(key, { entry, source: "home" });
|
|
20130
|
+
}
|
|
20131
|
+
for (const [key, entry] of Object.entries(repoEntries)) {
|
|
20132
|
+
merged.set(key, { entry, source: "repo" });
|
|
20133
|
+
}
|
|
20134
|
+
if (merged.size === 0) {
|
|
19836
20135
|
console.log(chalk21.gray("\nNo MCP servers are pinned yet."));
|
|
19837
20136
|
console.log(
|
|
19838
20137
|
chalk21.gray("Pins are created automatically when the MCP gateway first connects.\n")
|
|
@@ -19840,13 +20139,47 @@ function registerMcpPinCommand(program2) {
|
|
|
19840
20139
|
return;
|
|
19841
20140
|
}
|
|
19842
20141
|
console.log(chalk21.bold("\n\u{1F512} Pinned MCP Servers\n"));
|
|
19843
|
-
|
|
19844
|
-
|
|
20142
|
+
const showSource = found.source === "repo";
|
|
20143
|
+
for (const [key, { entry, source }] of merged) {
|
|
20144
|
+
const tag = showSource ? ` ${chalk21.yellow(`[${source}]`)}` : "";
|
|
20145
|
+
console.log(` ${chalk21.cyan(key)}${tag} ${chalk21.gray(entry.label)}`);
|
|
19845
20146
|
console.log(` Tools (${entry.toolCount}): ${chalk21.white(entry.toolNames.join(", "))}`);
|
|
19846
20147
|
console.log(` Hash: ${chalk21.gray(entry.toolsHash.slice(0, 16))}...`);
|
|
19847
20148
|
console.log(` Pinned: ${chalk21.gray(entry.pinnedAt)}`);
|
|
19848
20149
|
console.log("");
|
|
19849
20150
|
}
|
|
20151
|
+
if (showSource) {
|
|
20152
|
+
console.log(chalk21.gray(` [repo] entries come from ${found.path}`));
|
|
20153
|
+
console.log(chalk21.gray(" [home] entries come from ~/.node9/mcp-pins.json\n"));
|
|
20154
|
+
}
|
|
20155
|
+
});
|
|
20156
|
+
pinSubCmd.command("promote <serverKey>").description(
|
|
20157
|
+
"Copy a pin from ~/.node9/mcp-pins.json into <repo>/.node9/mcp-pins.json so teammates share the same vetted baseline"
|
|
20158
|
+
).action((serverKey) => {
|
|
20159
|
+
try {
|
|
20160
|
+
const { repoPath, created } = promotePin(serverKey, process.cwd());
|
|
20161
|
+
if (created) {
|
|
20162
|
+
console.log(
|
|
20163
|
+
chalk21.green(
|
|
20164
|
+
`
|
|
20165
|
+
\u2705 Created ${repoPath} with the promoted pin for ${chalk21.cyan(serverKey)}.`
|
|
20166
|
+
)
|
|
20167
|
+
);
|
|
20168
|
+
} else {
|
|
20169
|
+
console.log(chalk21.green(`
|
|
20170
|
+
\u2705 Promoted ${chalk21.cyan(serverKey)} into ${repoPath}.`));
|
|
20171
|
+
}
|
|
20172
|
+
console.log(chalk21.gray(" Review the change and commit it:"));
|
|
20173
|
+
console.log(chalk21.cyan(` git add ${repoPath}`));
|
|
20174
|
+
console.log(chalk21.cyan(` git commit -m "pin ${serverKey} (node9)"`));
|
|
20175
|
+
console.log("");
|
|
20176
|
+
} catch (err2) {
|
|
20177
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
20178
|
+
console.error(chalk21.red(`
|
|
20179
|
+
\u274C ${msg}
|
|
20180
|
+
`));
|
|
20181
|
+
process.exit(1);
|
|
20182
|
+
}
|
|
19850
20183
|
});
|
|
19851
20184
|
pinSubCmd.command("update <serverKey>").description(
|
|
19852
20185
|
"Remove a pin so the next gateway connection re-pins with current tool definitions"
|
|
@@ -19868,7 +20201,7 @@ function registerMcpPinCommand(program2) {
|
|
|
19868
20201
|
process.exit(1);
|
|
19869
20202
|
}
|
|
19870
20203
|
const label = pins.servers[serverKey].label;
|
|
19871
|
-
|
|
20204
|
+
removePin(serverKey);
|
|
19872
20205
|
console.log(chalk21.green(`
|
|
19873
20206
|
\u{1F513} Pin removed for ${chalk21.cyan(serverKey)}`));
|
|
19874
20207
|
console.log(chalk21.gray(` Server: ${label}`));
|
|
@@ -19881,7 +20214,7 @@ function registerMcpPinCommand(program2) {
|
|
|
19881
20214
|
return;
|
|
19882
20215
|
}
|
|
19883
20216
|
const count = result.ok ? Object.keys(result.pins.servers).length : "?";
|
|
19884
|
-
|
|
20217
|
+
clearAllPins();
|
|
19885
20218
|
console.log(chalk21.green(`
|
|
19886
20219
|
\u{1F513} Cleared ${count} MCP pin(s).`));
|
|
19887
20220
|
console.log(chalk21.gray(" Next connection to each server will re-pin.\n"));
|
|
@@ -20064,7 +20397,7 @@ init_scan();
|
|
|
20064
20397
|
|
|
20065
20398
|
// src/cli/commands/sessions.ts
|
|
20066
20399
|
import chalk24 from "chalk";
|
|
20067
|
-
import
|
|
20400
|
+
import fs41 from "fs";
|
|
20068
20401
|
import path42 from "path";
|
|
20069
20402
|
import os36 from "os";
|
|
20070
20403
|
var CLAUDE_PRICING3 = {
|
|
@@ -20182,7 +20515,7 @@ function loadAuditEntries(auditPath) {
|
|
|
20182
20515
|
const aPath = auditPath ?? path42.join(os36.homedir(), ".node9", "audit.log");
|
|
20183
20516
|
let raw;
|
|
20184
20517
|
try {
|
|
20185
|
-
raw =
|
|
20518
|
+
raw = fs41.readFileSync(aPath, "utf-8");
|
|
20186
20519
|
} catch {
|
|
20187
20520
|
return [];
|
|
20188
20521
|
}
|
|
@@ -20219,7 +20552,7 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
|
|
|
20219
20552
|
}
|
|
20220
20553
|
function buildGeminiSessions(days, allAuditEntries) {
|
|
20221
20554
|
const tmpDir = path42.join(os36.homedir(), ".gemini", "tmp");
|
|
20222
|
-
if (!
|
|
20555
|
+
if (!fs41.existsSync(tmpDir)) return [];
|
|
20223
20556
|
const cutoff = days !== null ? (() => {
|
|
20224
20557
|
const d = /* @__PURE__ */ new Date();
|
|
20225
20558
|
d.setDate(d.getDate() - days);
|
|
@@ -20228,7 +20561,7 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
20228
20561
|
})() : null;
|
|
20229
20562
|
let slugDirs;
|
|
20230
20563
|
try {
|
|
20231
|
-
slugDirs =
|
|
20564
|
+
slugDirs = fs41.readdirSync(tmpDir);
|
|
20232
20565
|
} catch {
|
|
20233
20566
|
return [];
|
|
20234
20567
|
}
|
|
@@ -20236,27 +20569,27 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
20236
20569
|
for (const slug of slugDirs) {
|
|
20237
20570
|
const slugPath = path42.join(tmpDir, slug);
|
|
20238
20571
|
try {
|
|
20239
|
-
if (!
|
|
20572
|
+
if (!fs41.statSync(slugPath).isDirectory()) continue;
|
|
20240
20573
|
} catch {
|
|
20241
20574
|
continue;
|
|
20242
20575
|
}
|
|
20243
20576
|
let projectRoot = path42.join(os36.homedir(), slug);
|
|
20244
20577
|
try {
|
|
20245
|
-
projectRoot =
|
|
20578
|
+
projectRoot = fs41.readFileSync(path42.join(slugPath, ".project_root"), "utf-8").trim();
|
|
20246
20579
|
} catch {
|
|
20247
20580
|
}
|
|
20248
20581
|
const chatsDir = path42.join(slugPath, "chats");
|
|
20249
|
-
if (!
|
|
20582
|
+
if (!fs41.existsSync(chatsDir)) continue;
|
|
20250
20583
|
let chatFiles;
|
|
20251
20584
|
try {
|
|
20252
|
-
chatFiles =
|
|
20585
|
+
chatFiles = fs41.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
|
|
20253
20586
|
} catch {
|
|
20254
20587
|
continue;
|
|
20255
20588
|
}
|
|
20256
20589
|
for (const chatFile of chatFiles) {
|
|
20257
20590
|
let raw;
|
|
20258
20591
|
try {
|
|
20259
|
-
raw =
|
|
20592
|
+
raw = fs41.readFileSync(path42.join(chatsDir, chatFile), "utf-8");
|
|
20260
20593
|
} catch {
|
|
20261
20594
|
continue;
|
|
20262
20595
|
}
|
|
@@ -20337,7 +20670,7 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
20337
20670
|
}
|
|
20338
20671
|
function buildCodexSessions(days, allAuditEntries) {
|
|
20339
20672
|
const sessionsBase = path42.join(os36.homedir(), ".codex", "sessions");
|
|
20340
|
-
if (!
|
|
20673
|
+
if (!fs41.existsSync(sessionsBase)) return [];
|
|
20341
20674
|
const cutoff = days !== null ? (() => {
|
|
20342
20675
|
const d = /* @__PURE__ */ new Date();
|
|
20343
20676
|
d.setDate(d.getDate() - days);
|
|
@@ -20346,28 +20679,28 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
20346
20679
|
})() : null;
|
|
20347
20680
|
const jsonlFiles = [];
|
|
20348
20681
|
try {
|
|
20349
|
-
for (const year of
|
|
20682
|
+
for (const year of fs41.readdirSync(sessionsBase)) {
|
|
20350
20683
|
const yearPath = path42.join(sessionsBase, year);
|
|
20351
20684
|
try {
|
|
20352
|
-
if (!
|
|
20685
|
+
if (!fs41.statSync(yearPath).isDirectory()) continue;
|
|
20353
20686
|
} catch {
|
|
20354
20687
|
continue;
|
|
20355
20688
|
}
|
|
20356
|
-
for (const month of
|
|
20689
|
+
for (const month of fs41.readdirSync(yearPath)) {
|
|
20357
20690
|
const monthPath = path42.join(yearPath, month);
|
|
20358
20691
|
try {
|
|
20359
|
-
if (!
|
|
20692
|
+
if (!fs41.statSync(monthPath).isDirectory()) continue;
|
|
20360
20693
|
} catch {
|
|
20361
20694
|
continue;
|
|
20362
20695
|
}
|
|
20363
|
-
for (const day of
|
|
20696
|
+
for (const day of fs41.readdirSync(monthPath)) {
|
|
20364
20697
|
const dayPath = path42.join(monthPath, day);
|
|
20365
20698
|
try {
|
|
20366
|
-
if (!
|
|
20699
|
+
if (!fs41.statSync(dayPath).isDirectory()) continue;
|
|
20367
20700
|
} catch {
|
|
20368
20701
|
continue;
|
|
20369
20702
|
}
|
|
20370
|
-
for (const file of
|
|
20703
|
+
for (const file of fs41.readdirSync(dayPath)) {
|
|
20371
20704
|
if (file.endsWith(".jsonl")) jsonlFiles.push(path42.join(dayPath, file));
|
|
20372
20705
|
}
|
|
20373
20706
|
}
|
|
@@ -20380,7 +20713,7 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
20380
20713
|
for (const filePath of jsonlFiles) {
|
|
20381
20714
|
let lines;
|
|
20382
20715
|
try {
|
|
20383
|
-
lines =
|
|
20716
|
+
lines = fs41.readFileSync(filePath, "utf-8").split("\n");
|
|
20384
20717
|
} catch {
|
|
20385
20718
|
continue;
|
|
20386
20719
|
}
|
|
@@ -20461,7 +20794,7 @@ function buildSessions(days, historyPath) {
|
|
|
20461
20794
|
const hPath = historyPath ?? path42.join(os36.homedir(), ".claude", "history.jsonl");
|
|
20462
20795
|
let historyRaw;
|
|
20463
20796
|
try {
|
|
20464
|
-
historyRaw =
|
|
20797
|
+
historyRaw = fs41.readFileSync(hPath, "utf-8");
|
|
20465
20798
|
} catch {
|
|
20466
20799
|
return [];
|
|
20467
20800
|
}
|
|
@@ -20486,7 +20819,7 @@ function buildSessions(days, historyPath) {
|
|
|
20486
20819
|
const jsonlFile = sessionJsonlPath(entry.project, entry.sessionId);
|
|
20487
20820
|
let sessionLines = [];
|
|
20488
20821
|
try {
|
|
20489
|
-
sessionLines =
|
|
20822
|
+
sessionLines = fs41.readFileSync(jsonlFile, "utf-8").split("\n");
|
|
20490
20823
|
} catch {
|
|
20491
20824
|
}
|
|
20492
20825
|
const { toolCalls, costUSD, hasSnapshot, modifiedFiles } = parseSessionLines(sessionLines);
|
|
@@ -20753,7 +21086,7 @@ function registerSessionsCommand(program2) {
|
|
|
20753
21086
|
console.log(chalk24.cyan.bold("\u{1F4CB} node9 sessions") + chalk24.dim(" \u2014 what your AI agent did"));
|
|
20754
21087
|
console.log("");
|
|
20755
21088
|
const historyPath = path42.join(os36.homedir(), ".claude", "history.jsonl");
|
|
20756
|
-
if (!
|
|
21089
|
+
if (!fs41.existsSync(historyPath)) {
|
|
20757
21090
|
console.log(chalk24.yellow(" No Claude session history found at ~/.claude/history.jsonl"));
|
|
20758
21091
|
console.log(chalk24.gray(" Install Claude Code, run a few sessions, then try again.\n"));
|
|
20759
21092
|
return;
|
|
@@ -20790,12 +21123,12 @@ function registerSessionsCommand(program2) {
|
|
|
20790
21123
|
|
|
20791
21124
|
// src/cli/commands/skill-pin.ts
|
|
20792
21125
|
import chalk25 from "chalk";
|
|
20793
|
-
import
|
|
21126
|
+
import fs42 from "fs";
|
|
20794
21127
|
import os37 from "os";
|
|
20795
21128
|
import path43 from "path";
|
|
20796
21129
|
function wipeSkillSessions() {
|
|
20797
21130
|
try {
|
|
20798
|
-
|
|
21131
|
+
fs42.rmSync(path43.join(os37.homedir(), ".node9", "skill-sessions"), {
|
|
20799
21132
|
recursive: true,
|
|
20800
21133
|
force: true
|
|
20801
21134
|
});
|
|
@@ -20853,7 +21186,7 @@ function registerSkillPinCommand(program2) {
|
|
|
20853
21186
|
process.exit(1);
|
|
20854
21187
|
}
|
|
20855
21188
|
const rootPath = pins.roots[rootKey].rootPath;
|
|
20856
|
-
|
|
21189
|
+
removePin2(rootKey);
|
|
20857
21190
|
wipeSkillSessions();
|
|
20858
21191
|
console.log(chalk25.green(`
|
|
20859
21192
|
\u{1F513} Pin removed for ${chalk25.cyan(rootKey)}`));
|
|
@@ -20868,7 +21201,7 @@ function registerSkillPinCommand(program2) {
|
|
|
20868
21201
|
return;
|
|
20869
21202
|
}
|
|
20870
21203
|
const count = result.ok ? Object.keys(result.pins.roots).length : "?";
|
|
20871
|
-
|
|
21204
|
+
clearAllPins2();
|
|
20872
21205
|
wipeSkillSessions();
|
|
20873
21206
|
console.log(chalk25.green(`
|
|
20874
21207
|
\u{1F513} Cleared ${count} skill pin(s).`));
|
|
@@ -20877,15 +21210,15 @@ function registerSkillPinCommand(program2) {
|
|
|
20877
21210
|
}
|
|
20878
21211
|
|
|
20879
21212
|
// src/cli/commands/decisions.ts
|
|
20880
|
-
import
|
|
21213
|
+
import fs43 from "fs";
|
|
20881
21214
|
import os38 from "os";
|
|
20882
21215
|
import path44 from "path";
|
|
20883
21216
|
import chalk26 from "chalk";
|
|
20884
21217
|
var DECISIONS_FILE2 = path44.join(os38.homedir(), ".node9", "decisions.json");
|
|
20885
21218
|
function readDecisions() {
|
|
20886
21219
|
try {
|
|
20887
|
-
if (!
|
|
20888
|
-
const raw =
|
|
21220
|
+
if (!fs43.existsSync(DECISIONS_FILE2)) return {};
|
|
21221
|
+
const raw = fs43.readFileSync(DECISIONS_FILE2, "utf-8");
|
|
20889
21222
|
const parsed = JSON.parse(raw);
|
|
20890
21223
|
const out = {};
|
|
20891
21224
|
for (const [k, v] of Object.entries(parsed)) {
|
|
@@ -20898,10 +21231,10 @@ function readDecisions() {
|
|
|
20898
21231
|
}
|
|
20899
21232
|
function writeDecisions(d) {
|
|
20900
21233
|
const dir = path44.dirname(DECISIONS_FILE2);
|
|
20901
|
-
if (!
|
|
21234
|
+
if (!fs43.existsSync(dir)) fs43.mkdirSync(dir, { recursive: true });
|
|
20902
21235
|
const tmp = `${DECISIONS_FILE2}.${process.pid}.tmp`;
|
|
20903
|
-
|
|
20904
|
-
|
|
21236
|
+
fs43.writeFileSync(tmp, JSON.stringify(d, null, 2));
|
|
21237
|
+
fs43.renameSync(tmp, DECISIONS_FILE2);
|
|
20905
21238
|
}
|
|
20906
21239
|
function registerDecisionsCommand(program2) {
|
|
20907
21240
|
const cmd = program2.command("decisions").description('Manage persistent "Always Allow" / "Always Deny" tool decisions');
|
|
@@ -20958,7 +21291,7 @@ Persistent decisions (${entries.length})
|
|
|
20958
21291
|
|
|
20959
21292
|
// src/cli/commands/dlp.ts
|
|
20960
21293
|
import chalk27 from "chalk";
|
|
20961
|
-
import
|
|
21294
|
+
import fs44 from "fs";
|
|
20962
21295
|
import path45 from "path";
|
|
20963
21296
|
import os39 from "os";
|
|
20964
21297
|
var AUDIT_LOG = path45.join(os39.homedir(), ".node9", "audit.log");
|
|
@@ -20969,7 +21302,7 @@ function stripAnsi(s) {
|
|
|
20969
21302
|
}
|
|
20970
21303
|
function loadResolved() {
|
|
20971
21304
|
try {
|
|
20972
|
-
const raw = JSON.parse(
|
|
21305
|
+
const raw = JSON.parse(fs44.readFileSync(RESOLVED_FILE, "utf-8"));
|
|
20973
21306
|
return new Set(raw);
|
|
20974
21307
|
} catch {
|
|
20975
21308
|
return /* @__PURE__ */ new Set();
|
|
@@ -20977,13 +21310,13 @@ function loadResolved() {
|
|
|
20977
21310
|
}
|
|
20978
21311
|
function saveResolved(resolved) {
|
|
20979
21312
|
try {
|
|
20980
|
-
|
|
21313
|
+
fs44.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
|
|
20981
21314
|
} catch {
|
|
20982
21315
|
}
|
|
20983
21316
|
}
|
|
20984
21317
|
function loadDlpFindings() {
|
|
20985
|
-
if (!
|
|
20986
|
-
return
|
|
21318
|
+
if (!fs44.existsSync(AUDIT_LOG)) return [];
|
|
21319
|
+
return fs44.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
|
|
20987
21320
|
if (!line.trim()) return [];
|
|
20988
21321
|
try {
|
|
20989
21322
|
const e = JSON.parse(line);
|
|
@@ -21082,13 +21415,13 @@ function registerDlpCommand(program2) {
|
|
|
21082
21415
|
// src/cli/commands/mask.ts
|
|
21083
21416
|
init_dlp();
|
|
21084
21417
|
import chalk28 from "chalk";
|
|
21085
|
-
import
|
|
21418
|
+
import fs45 from "fs";
|
|
21086
21419
|
import path46 from "path";
|
|
21087
21420
|
import os40 from "os";
|
|
21088
21421
|
function findJsonlFiles(dir) {
|
|
21089
21422
|
const results = [];
|
|
21090
|
-
if (!
|
|
21091
|
-
for (const entry of
|
|
21423
|
+
if (!fs45.existsSync(dir)) return results;
|
|
21424
|
+
for (const entry of fs45.readdirSync(dir, { withFileTypes: true })) {
|
|
21092
21425
|
const full = path46.join(dir, entry.name);
|
|
21093
21426
|
if (entry.isDirectory()) results.push(...findJsonlFiles(full));
|
|
21094
21427
|
else if (entry.isFile() && entry.name.endsWith(".jsonl")) results.push(full);
|
|
@@ -21132,7 +21465,7 @@ function redactJson(obj) {
|
|
|
21132
21465
|
function processFile(filePath, dryRun) {
|
|
21133
21466
|
let raw;
|
|
21134
21467
|
try {
|
|
21135
|
-
raw =
|
|
21468
|
+
raw = fs45.readFileSync(filePath, "utf-8");
|
|
21136
21469
|
} catch {
|
|
21137
21470
|
return { redactedLines: 0, patterns: [] };
|
|
21138
21471
|
}
|
|
@@ -21164,14 +21497,14 @@ function processFile(filePath, dryRun) {
|
|
|
21164
21497
|
}
|
|
21165
21498
|
}
|
|
21166
21499
|
if (!dryRun && redactedLines > 0) {
|
|
21167
|
-
|
|
21500
|
+
fs45.writeFileSync(filePath, newLines.join("\n"), "utf-8");
|
|
21168
21501
|
}
|
|
21169
21502
|
return { redactedLines, patterns };
|
|
21170
21503
|
}
|
|
21171
21504
|
function processJsonFile(filePath, dryRun) {
|
|
21172
21505
|
let raw;
|
|
21173
21506
|
try {
|
|
21174
|
-
raw =
|
|
21507
|
+
raw = fs45.readFileSync(filePath, "utf-8");
|
|
21175
21508
|
} catch {
|
|
21176
21509
|
return { redactedLines: 0, patterns: [] };
|
|
21177
21510
|
}
|
|
@@ -21184,14 +21517,14 @@ function processJsonFile(filePath, dryRun) {
|
|
|
21184
21517
|
const { value, modified, found } = redactJson(parsed);
|
|
21185
21518
|
if (!modified) return { redactedLines: 0, patterns: [] };
|
|
21186
21519
|
if (!dryRun) {
|
|
21187
|
-
|
|
21520
|
+
fs45.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
|
|
21188
21521
|
}
|
|
21189
21522
|
return { redactedLines: 1, patterns: found };
|
|
21190
21523
|
}
|
|
21191
21524
|
function findJsonFiles(dir) {
|
|
21192
21525
|
const results = [];
|
|
21193
|
-
if (!
|
|
21194
|
-
for (const entry of
|
|
21526
|
+
if (!fs45.existsSync(dir)) return results;
|
|
21527
|
+
for (const entry of fs45.readdirSync(dir, { withFileTypes: true })) {
|
|
21195
21528
|
const full = path46.join(dir, entry.name);
|
|
21196
21529
|
if (entry.isDirectory()) results.push(...findJsonFiles(full));
|
|
21197
21530
|
else if (entry.isFile() && entry.name.endsWith(".json")) results.push(full);
|
|
@@ -21211,7 +21544,7 @@ function registerMaskCommand(program2) {
|
|
|
21211
21544
|
const cutoff = options.all ? null : new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
|
|
21212
21545
|
const filtered = cutoff ? allFiles.filter((f) => {
|
|
21213
21546
|
try {
|
|
21214
|
-
return
|
|
21547
|
+
return fs45.statSync(f.path).mtime >= cutoff;
|
|
21215
21548
|
} catch {
|
|
21216
21549
|
return false;
|
|
21217
21550
|
}
|
|
@@ -21267,20 +21600,20 @@ function registerMaskCommand(program2) {
|
|
|
21267
21600
|
// src/cli.ts
|
|
21268
21601
|
init_blast();
|
|
21269
21602
|
var { version } = JSON.parse(
|
|
21270
|
-
|
|
21603
|
+
fs48.readFileSync(path49.join(__dirname, "../package.json"), "utf-8")
|
|
21271
21604
|
);
|
|
21272
21605
|
var program = new Command();
|
|
21273
21606
|
program.name("node9").description("The Sudo Command for AI Agents").version(version);
|
|
21274
21607
|
program.command("login").argument("<apiKey>").option("--local", "Save key for audit/logging only \u2014 local config still controls all decisions").option("--profile <name>", 'Save as a named profile (default: "default")').action((apiKey, options) => {
|
|
21275
21608
|
const DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept";
|
|
21276
21609
|
const credPath = path49.join(os43.homedir(), ".node9", "credentials.json");
|
|
21277
|
-
if (!
|
|
21278
|
-
|
|
21610
|
+
if (!fs48.existsSync(path49.dirname(credPath)))
|
|
21611
|
+
fs48.mkdirSync(path49.dirname(credPath), { recursive: true });
|
|
21279
21612
|
const profileName = options.profile || "default";
|
|
21280
21613
|
let existingCreds = {};
|
|
21281
21614
|
try {
|
|
21282
|
-
if (
|
|
21283
|
-
const raw = JSON.parse(
|
|
21615
|
+
if (fs48.existsSync(credPath)) {
|
|
21616
|
+
const raw = JSON.parse(fs48.readFileSync(credPath, "utf-8"));
|
|
21284
21617
|
if (raw.apiKey) {
|
|
21285
21618
|
existingCreds = {
|
|
21286
21619
|
default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL2 }
|
|
@@ -21292,13 +21625,13 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
21292
21625
|
} catch {
|
|
21293
21626
|
}
|
|
21294
21627
|
existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL2 };
|
|
21295
|
-
|
|
21628
|
+
fs48.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
|
|
21296
21629
|
if (profileName === "default") {
|
|
21297
21630
|
const configPath = path49.join(os43.homedir(), ".node9", "config.json");
|
|
21298
21631
|
let config = {};
|
|
21299
21632
|
try {
|
|
21300
|
-
if (
|
|
21301
|
-
config = JSON.parse(
|
|
21633
|
+
if (fs48.existsSync(configPath))
|
|
21634
|
+
config = JSON.parse(fs48.readFileSync(configPath, "utf-8"));
|
|
21302
21635
|
} catch {
|
|
21303
21636
|
}
|
|
21304
21637
|
if (!config.settings || typeof config.settings !== "object") config.settings = {};
|
|
@@ -21313,9 +21646,9 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
21313
21646
|
approvers.cloud = false;
|
|
21314
21647
|
}
|
|
21315
21648
|
s.approvers = approvers;
|
|
21316
|
-
if (!
|
|
21317
|
-
|
|
21318
|
-
|
|
21649
|
+
if (!fs48.existsSync(path49.dirname(configPath)))
|
|
21650
|
+
fs48.mkdirSync(path49.dirname(configPath), { recursive: true });
|
|
21651
|
+
fs48.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
21319
21652
|
}
|
|
21320
21653
|
if (options.profile && profileName !== "default") {
|
|
21321
21654
|
console.log(chalk30.green(`\u2705 Profile "${profileName}" saved`));
|
|
@@ -21453,14 +21786,14 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
21453
21786
|
}
|
|
21454
21787
|
if (options.purge) {
|
|
21455
21788
|
const node9Dir = path49.join(os43.homedir(), ".node9");
|
|
21456
|
-
if (
|
|
21789
|
+
if (fs48.existsSync(node9Dir)) {
|
|
21457
21790
|
const confirmed = await confirm2({
|
|
21458
21791
|
message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
|
|
21459
21792
|
default: false
|
|
21460
21793
|
});
|
|
21461
21794
|
if (confirmed) {
|
|
21462
|
-
|
|
21463
|
-
if (
|
|
21795
|
+
fs48.rmSync(node9Dir, { recursive: true });
|
|
21796
|
+
if (fs48.existsSync(node9Dir)) {
|
|
21464
21797
|
console.error(
|
|
21465
21798
|
chalk30.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
|
|
21466
21799
|
);
|
|
@@ -21615,12 +21948,12 @@ Run "node9 addto claude" to register it as the statusLine.`
|
|
|
21615
21948
|
if (subcommand === "debug") {
|
|
21616
21949
|
const flagFile = path49.join(os43.homedir(), ".node9", "hud-debug");
|
|
21617
21950
|
if (state === "on") {
|
|
21618
|
-
|
|
21619
|
-
|
|
21951
|
+
fs48.mkdirSync(path49.dirname(flagFile), { recursive: true });
|
|
21952
|
+
fs48.writeFileSync(flagFile, "");
|
|
21620
21953
|
console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
|
|
21621
21954
|
console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
|
|
21622
21955
|
} else if (state === "off") {
|
|
21623
|
-
if (
|
|
21956
|
+
if (fs48.existsSync(flagFile)) fs48.unlinkSync(flagFile);
|
|
21624
21957
|
console.log("HUD debug logging disabled.");
|
|
21625
21958
|
} else {
|
|
21626
21959
|
console.error("Usage: node9 hud debug on|off");
|
|
@@ -21736,7 +22069,7 @@ if (process.argv[2] !== "daemon") {
|
|
|
21736
22069
|
if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
|
|
21737
22070
|
const logPath = path49.join(os43.homedir(), ".node9", "hook-debug.log");
|
|
21738
22071
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
21739
|
-
|
|
22072
|
+
fs48.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
|
|
21740
22073
|
`);
|
|
21741
22074
|
}
|
|
21742
22075
|
process.exit(0);
|