@node9/proxy 1.22.0 → 1.24.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 +948 -810
- package/dist/cli.mjs +917 -780
- 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,32 +6469,32 @@ 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) {
|
|
@@ -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 ?? {};
|
|
@@ -6613,8 +6771,9 @@ async function setupClaude() {
|
|
|
6613
6771
|
}
|
|
6614
6772
|
}
|
|
6615
6773
|
async function setupGemini() {
|
|
6616
|
-
|
|
6617
|
-
const
|
|
6774
|
+
seedMcpPinsIfMissing();
|
|
6775
|
+
const homeDir2 = os12.homedir();
|
|
6776
|
+
const settingsPath = path15.join(homeDir2, ".gemini", "settings.json");
|
|
6618
6777
|
const settings = readJson(settingsPath) ?? {};
|
|
6619
6778
|
const servers = settings.mcpServers ?? {};
|
|
6620
6779
|
let hooksChanged = false;
|
|
@@ -6709,9 +6868,9 @@ async function setupGemini() {
|
|
|
6709
6868
|
printDaemonTip();
|
|
6710
6869
|
}
|
|
6711
6870
|
}
|
|
6712
|
-
function claudeDesktopConfigPath(homeDir2 =
|
|
6871
|
+
function claudeDesktopConfigPath(homeDir2 = os12.homedir()) {
|
|
6713
6872
|
if (process.platform === "darwin") {
|
|
6714
|
-
return
|
|
6873
|
+
return path15.join(
|
|
6715
6874
|
homeDir2,
|
|
6716
6875
|
"Library",
|
|
6717
6876
|
"Application Support",
|
|
@@ -6720,18 +6879,18 @@ function claudeDesktopConfigPath(homeDir2 = os11.homedir()) {
|
|
|
6720
6879
|
);
|
|
6721
6880
|
}
|
|
6722
6881
|
if (process.platform === "linux") {
|
|
6723
|
-
return
|
|
6882
|
+
return path15.join(homeDir2, ".config", "Claude", "claude_desktop_config.json");
|
|
6724
6883
|
}
|
|
6725
6884
|
if (process.platform === "win32") {
|
|
6726
|
-
const appData = process.env.APPDATA ??
|
|
6727
|
-
return
|
|
6885
|
+
const appData = process.env.APPDATA ?? path15.join(homeDir2, "AppData", "Roaming");
|
|
6886
|
+
return path15.join(appData, "Claude", "claude_desktop_config.json");
|
|
6728
6887
|
}
|
|
6729
6888
|
return null;
|
|
6730
6889
|
}
|
|
6731
|
-
function detectAgents(homeDir2 =
|
|
6890
|
+
function detectAgents(homeDir2 = os12.homedir()) {
|
|
6732
6891
|
const exists = (p) => {
|
|
6733
6892
|
try {
|
|
6734
|
-
return
|
|
6893
|
+
return fs13.existsSync(p);
|
|
6735
6894
|
} catch (err2) {
|
|
6736
6895
|
const code = err2.code;
|
|
6737
6896
|
if (code !== "ENOENT") {
|
|
@@ -6743,18 +6902,19 @@ function detectAgents(homeDir2 = os11.homedir()) {
|
|
|
6743
6902
|
};
|
|
6744
6903
|
const desktopPath = claudeDesktopConfigPath(homeDir2);
|
|
6745
6904
|
return {
|
|
6746
|
-
claude: exists(
|
|
6747
|
-
gemini: exists(
|
|
6748
|
-
cursor: exists(
|
|
6749
|
-
codex: exists(
|
|
6750
|
-
windsurf: exists(
|
|
6751
|
-
vscode: exists(
|
|
6752
|
-
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))
|
|
6753
6912
|
};
|
|
6754
6913
|
}
|
|
6755
6914
|
async function setupCursor() {
|
|
6756
|
-
|
|
6757
|
-
const
|
|
6915
|
+
seedMcpPinsIfMissing();
|
|
6916
|
+
const homeDir2 = os12.homedir();
|
|
6917
|
+
const mcpPath = path15.join(homeDir2, ".cursor", "mcp.json");
|
|
6758
6918
|
const mcpConfig = readJson(mcpPath) ?? {};
|
|
6759
6919
|
const servers = mcpConfig.mcpServers ?? {};
|
|
6760
6920
|
let anythingChanged = false;
|
|
@@ -6820,22 +6980,23 @@ async function setupCursor() {
|
|
|
6820
6980
|
}
|
|
6821
6981
|
function readToml(filePath) {
|
|
6822
6982
|
try {
|
|
6823
|
-
if (
|
|
6824
|
-
return parseToml(
|
|
6983
|
+
if (fs13.existsSync(filePath)) {
|
|
6984
|
+
return parseToml(fs13.readFileSync(filePath, "utf-8"));
|
|
6825
6985
|
}
|
|
6826
6986
|
} catch {
|
|
6827
6987
|
}
|
|
6828
6988
|
return null;
|
|
6829
6989
|
}
|
|
6830
6990
|
function writeToml(filePath, data) {
|
|
6831
|
-
const dir =
|
|
6832
|
-
if (!
|
|
6833
|
-
|
|
6991
|
+
const dir = path15.dirname(filePath);
|
|
6992
|
+
if (!fs13.existsSync(dir)) fs13.mkdirSync(dir, { recursive: true });
|
|
6993
|
+
fs13.writeFileSync(filePath, stringifyToml(data));
|
|
6834
6994
|
}
|
|
6835
6995
|
async function setupCodex() {
|
|
6836
|
-
|
|
6837
|
-
const
|
|
6838
|
-
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");
|
|
6839
7000
|
const config = readToml(configPath) ?? {};
|
|
6840
7001
|
const servers = config.mcp_servers ?? {};
|
|
6841
7002
|
let anythingChanged = false;
|
|
@@ -6985,9 +7146,9 @@ async function setupCodex() {
|
|
|
6985
7146
|
}
|
|
6986
7147
|
}
|
|
6987
7148
|
function teardownCodex() {
|
|
6988
|
-
const homeDir2 =
|
|
6989
|
-
const configPath =
|
|
6990
|
-
const hooksPath =
|
|
7149
|
+
const homeDir2 = os12.homedir();
|
|
7150
|
+
const configPath = path15.join(homeDir2, ".codex", "config.toml");
|
|
7151
|
+
const hooksPath = path15.join(homeDir2, ".codex", "hooks.json");
|
|
6991
7152
|
const hooksFile = readJson(hooksPath);
|
|
6992
7153
|
if (hooksFile?.hooks) {
|
|
6993
7154
|
let hooksChanged = false;
|
|
@@ -7034,8 +7195,8 @@ function teardownCodex() {
|
|
|
7034
7195
|
}
|
|
7035
7196
|
}
|
|
7036
7197
|
function setupHud() {
|
|
7037
|
-
const homeDir2 =
|
|
7038
|
-
const hooksPath =
|
|
7198
|
+
const homeDir2 = os12.homedir();
|
|
7199
|
+
const hooksPath = path15.join(homeDir2, ".claude", "settings.json");
|
|
7039
7200
|
const settings = readJson(hooksPath) ?? {};
|
|
7040
7201
|
const hudCommand = fullPathCommand("hud");
|
|
7041
7202
|
const statusLineObj = { type: "command", command: hudCommand };
|
|
@@ -7061,8 +7222,8 @@ function setupHud() {
|
|
|
7061
7222
|
console.log(chalk.gray(" Restart Claude Code to activate."));
|
|
7062
7223
|
}
|
|
7063
7224
|
function teardownHud() {
|
|
7064
|
-
const homeDir2 =
|
|
7065
|
-
const hooksPath =
|
|
7225
|
+
const homeDir2 = os12.homedir();
|
|
7226
|
+
const hooksPath = path15.join(homeDir2, ".claude", "settings.json");
|
|
7066
7227
|
const settings = readJson(hooksPath);
|
|
7067
7228
|
if (!settings) {
|
|
7068
7229
|
console.log(chalk.blue(" \u2139\uFE0F ~/.claude/settings.json not found \u2014 nothing to remove"));
|
|
@@ -7080,8 +7241,9 @@ function teardownHud() {
|
|
|
7080
7241
|
console.log(chalk.gray(" Restart Claude Code for changes to take effect."));
|
|
7081
7242
|
}
|
|
7082
7243
|
async function setupWindsurf() {
|
|
7083
|
-
|
|
7084
|
-
const
|
|
7244
|
+
seedMcpPinsIfMissing();
|
|
7245
|
+
const homeDir2 = os12.homedir();
|
|
7246
|
+
const mcpPath = path15.join(homeDir2, ".codeium", "windsurf", "mcp_config.json");
|
|
7085
7247
|
const mcpConfig = readJson(mcpPath) ?? {};
|
|
7086
7248
|
const servers = mcpConfig.mcpServers ?? {};
|
|
7087
7249
|
let anythingChanged = false;
|
|
@@ -7141,8 +7303,8 @@ async function setupWindsurf() {
|
|
|
7141
7303
|
}
|
|
7142
7304
|
}
|
|
7143
7305
|
function teardownWindsurf() {
|
|
7144
|
-
const homeDir2 =
|
|
7145
|
-
const mcpPath =
|
|
7306
|
+
const homeDir2 = os12.homedir();
|
|
7307
|
+
const mcpPath = path15.join(homeDir2, ".codeium", "windsurf", "mcp_config.json");
|
|
7146
7308
|
const mcpConfig = readJson(mcpPath);
|
|
7147
7309
|
if (!mcpConfig?.mcpServers) {
|
|
7148
7310
|
console.log(
|
|
@@ -7183,8 +7345,9 @@ function hasNode9McpServerVSCode(servers) {
|
|
|
7183
7345
|
return !!entry && entry.command === "node9" && Array.isArray(entry.args) && entry.args[0] === "mcp-server";
|
|
7184
7346
|
}
|
|
7185
7347
|
async function setupVSCode() {
|
|
7186
|
-
|
|
7187
|
-
const
|
|
7348
|
+
seedMcpPinsIfMissing();
|
|
7349
|
+
const homeDir2 = os12.homedir();
|
|
7350
|
+
const mcpPath = path15.join(homeDir2, ".vscode", "mcp.json");
|
|
7188
7351
|
const mcpConfig = readJson(mcpPath) ?? {};
|
|
7189
7352
|
const servers = mcpConfig.servers ?? {};
|
|
7190
7353
|
let anythingChanged = false;
|
|
@@ -7245,8 +7408,8 @@ async function setupVSCode() {
|
|
|
7245
7408
|
}
|
|
7246
7409
|
}
|
|
7247
7410
|
function teardownVSCode() {
|
|
7248
|
-
const homeDir2 =
|
|
7249
|
-
const mcpPath =
|
|
7411
|
+
const homeDir2 = os12.homedir();
|
|
7412
|
+
const mcpPath = path15.join(homeDir2, ".vscode", "mcp.json");
|
|
7250
7413
|
const mcpConfig = readJson(mcpPath);
|
|
7251
7414
|
if (!mcpConfig?.servers) {
|
|
7252
7415
|
console.log(chalk.blue(" \u2139\uFE0F ~/.vscode/mcp.json not found \u2014 nothing to remove"));
|
|
@@ -7279,6 +7442,7 @@ function teardownVSCode() {
|
|
|
7279
7442
|
}
|
|
7280
7443
|
}
|
|
7281
7444
|
async function setupClaudeDesktop() {
|
|
7445
|
+
seedMcpPinsIfMissing();
|
|
7282
7446
|
const configPath = claudeDesktopConfigPath();
|
|
7283
7447
|
if (!configPath) {
|
|
7284
7448
|
console.log(chalk.yellow(" \u26A0\uFE0F Claude Desktop is not supported on this platform."));
|
|
@@ -7377,32 +7541,32 @@ function teardownClaudeDesktop() {
|
|
|
7377
7541
|
console.log(chalk.blue(" \u2139\uFE0F No Node9-wrapped MCP servers found in Claude Desktop config"));
|
|
7378
7542
|
}
|
|
7379
7543
|
}
|
|
7380
|
-
function getAgentsStatus(homeDir2 =
|
|
7544
|
+
function getAgentsStatus(homeDir2 = os12.homedir()) {
|
|
7381
7545
|
const detected = detectAgents(homeDir2);
|
|
7382
7546
|
const claudeWired = (() => {
|
|
7383
|
-
const settings = readJson(
|
|
7547
|
+
const settings = readJson(path15.join(homeDir2, ".claude", "settings.json"));
|
|
7384
7548
|
return !!settings?.hooks?.PreToolUse?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)));
|
|
7385
7549
|
})();
|
|
7386
7550
|
const geminiWired = (() => {
|
|
7387
|
-
const settings = readJson(
|
|
7551
|
+
const settings = readJson(path15.join(homeDir2, ".gemini", "settings.json"));
|
|
7388
7552
|
return !!settings?.hooks?.BeforeTool?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)));
|
|
7389
7553
|
})();
|
|
7390
7554
|
const cursorWired = (() => {
|
|
7391
|
-
const cfg = readJson(
|
|
7555
|
+
const cfg = readJson(path15.join(homeDir2, ".cursor", "mcp.json"));
|
|
7392
7556
|
return !!(cfg?.mcpServers && hasNode9McpServer(cfg.mcpServers));
|
|
7393
7557
|
})();
|
|
7394
7558
|
const codexWired = (() => {
|
|
7395
|
-
const cfg = readToml(
|
|
7559
|
+
const cfg = readToml(path15.join(homeDir2, ".codex", "config.toml"));
|
|
7396
7560
|
return !!(cfg?.mcp_servers && hasNode9McpServer(cfg.mcp_servers));
|
|
7397
7561
|
})();
|
|
7398
7562
|
const windsurfWired = (() => {
|
|
7399
7563
|
const cfg = readJson(
|
|
7400
|
-
|
|
7564
|
+
path15.join(homeDir2, ".codeium", "windsurf", "mcp_config.json")
|
|
7401
7565
|
);
|
|
7402
7566
|
return !!(cfg?.mcpServers && hasNode9McpServer(cfg.mcpServers));
|
|
7403
7567
|
})();
|
|
7404
7568
|
const vscodeWired = (() => {
|
|
7405
|
-
const cfg = readJson(
|
|
7569
|
+
const cfg = readJson(path15.join(homeDir2, ".vscode", "mcp.json"));
|
|
7406
7570
|
return !!(cfg?.servers && hasNode9McpServerVSCode(cfg.servers));
|
|
7407
7571
|
})();
|
|
7408
7572
|
return [
|
|
@@ -7466,6 +7630,7 @@ var NODE9_MCP_SERVER_ENTRY, CODEX_PRE_TOOL_MATCHERS;
|
|
|
7466
7630
|
var init_setup = __esm({
|
|
7467
7631
|
"src/setup.ts"() {
|
|
7468
7632
|
"use strict";
|
|
7633
|
+
init_mcp_pin();
|
|
7469
7634
|
NODE9_MCP_SERVER_ENTRY = { command: "node9", args: ["mcp-server"] };
|
|
7470
7635
|
CODEX_PRE_TOOL_MATCHERS = ["^Bash$", "^apply_patch$", "^mcp__.*"];
|
|
7471
7636
|
}
|
|
@@ -7659,85 +7824,85 @@ var init_scan_summary = __esm({
|
|
|
7659
7824
|
|
|
7660
7825
|
// src/cli/commands/blast.ts
|
|
7661
7826
|
import chalk2 from "chalk";
|
|
7662
|
-
import
|
|
7663
|
-
import
|
|
7664
|
-
import
|
|
7827
|
+
import fs14 from "fs";
|
|
7828
|
+
import path16 from "path";
|
|
7829
|
+
import os13 from "os";
|
|
7665
7830
|
function buildSensitivePaths(home, cwd) {
|
|
7666
7831
|
return [
|
|
7667
7832
|
{
|
|
7668
|
-
full:
|
|
7833
|
+
full: path16.join(home, ".ssh", "id_rsa"),
|
|
7669
7834
|
label: "~/.ssh/id_rsa",
|
|
7670
7835
|
description: "RSA private key \u2014 grants SSH access to your servers",
|
|
7671
7836
|
score: 20
|
|
7672
7837
|
},
|
|
7673
7838
|
{
|
|
7674
|
-
full:
|
|
7839
|
+
full: path16.join(home, ".ssh", "id_ed25519"),
|
|
7675
7840
|
label: "~/.ssh/id_ed25519",
|
|
7676
7841
|
description: "Ed25519 private key \u2014 grants SSH access to your servers",
|
|
7677
7842
|
score: 20
|
|
7678
7843
|
},
|
|
7679
7844
|
{
|
|
7680
|
-
full:
|
|
7845
|
+
full: path16.join(home, ".ssh", "id_ecdsa"),
|
|
7681
7846
|
label: "~/.ssh/id_ecdsa",
|
|
7682
7847
|
description: "ECDSA private key \u2014 grants SSH access to your servers",
|
|
7683
7848
|
score: 20
|
|
7684
7849
|
},
|
|
7685
7850
|
{
|
|
7686
|
-
full:
|
|
7851
|
+
full: path16.join(home, ".aws", "credentials"),
|
|
7687
7852
|
label: "~/.aws/credentials",
|
|
7688
7853
|
description: "AWS access keys \u2014 full cloud account access",
|
|
7689
7854
|
score: 20
|
|
7690
7855
|
},
|
|
7691
7856
|
{
|
|
7692
|
-
full:
|
|
7857
|
+
full: path16.join(home, ".aws", "config"),
|
|
7693
7858
|
label: "~/.aws/config",
|
|
7694
7859
|
description: "AWS configuration \u2014 account and region settings",
|
|
7695
7860
|
score: 5
|
|
7696
7861
|
},
|
|
7697
7862
|
{
|
|
7698
|
-
full:
|
|
7863
|
+
full: path16.join(home, ".config", "gcloud", "credentials.db"),
|
|
7699
7864
|
label: "~/.config/gcloud/credentials.db",
|
|
7700
7865
|
description: "Google Cloud credentials",
|
|
7701
7866
|
score: 15
|
|
7702
7867
|
},
|
|
7703
7868
|
{
|
|
7704
|
-
full:
|
|
7869
|
+
full: path16.join(home, ".docker", "config.json"),
|
|
7705
7870
|
label: "~/.docker/config.json",
|
|
7706
7871
|
description: "Docker registry auth tokens",
|
|
7707
7872
|
score: 10
|
|
7708
7873
|
},
|
|
7709
7874
|
{
|
|
7710
|
-
full:
|
|
7875
|
+
full: path16.join(home, ".netrc"),
|
|
7711
7876
|
label: "~/.netrc",
|
|
7712
7877
|
description: "FTP/HTTP credentials in plain text",
|
|
7713
7878
|
score: 15
|
|
7714
7879
|
},
|
|
7715
7880
|
{
|
|
7716
|
-
full:
|
|
7881
|
+
full: path16.join(home, ".npmrc"),
|
|
7717
7882
|
label: "~/.npmrc",
|
|
7718
7883
|
description: "npm auth token \u2014 can publish packages as you",
|
|
7719
7884
|
score: 10
|
|
7720
7885
|
},
|
|
7721
7886
|
{
|
|
7722
|
-
full:
|
|
7887
|
+
full: path16.join(home, ".node9", "credentials.json"),
|
|
7723
7888
|
label: "~/.node9/credentials.json",
|
|
7724
7889
|
description: "Node9 cloud API key",
|
|
7725
7890
|
score: 10
|
|
7726
7891
|
},
|
|
7727
7892
|
{
|
|
7728
|
-
full:
|
|
7893
|
+
full: path16.join(cwd, ".env"),
|
|
7729
7894
|
label: ".env (current folder)",
|
|
7730
7895
|
description: "App secrets \u2014 database passwords, API keys",
|
|
7731
7896
|
score: 20
|
|
7732
7897
|
},
|
|
7733
7898
|
{
|
|
7734
|
-
full:
|
|
7899
|
+
full: path16.join(cwd, ".env.local"),
|
|
7735
7900
|
label: ".env.local (current folder)",
|
|
7736
7901
|
description: "Local overrides \u2014 often contains real credentials",
|
|
7737
7902
|
score: 15
|
|
7738
7903
|
},
|
|
7739
7904
|
{
|
|
7740
|
-
full:
|
|
7905
|
+
full: path16.join(cwd, ".env.production"),
|
|
7741
7906
|
label: ".env.production (current folder)",
|
|
7742
7907
|
description: "Production secrets",
|
|
7743
7908
|
score: 20
|
|
@@ -7746,7 +7911,7 @@ function buildSensitivePaths(home, cwd) {
|
|
|
7746
7911
|
}
|
|
7747
7912
|
function isReadable(filePath) {
|
|
7748
7913
|
try {
|
|
7749
|
-
|
|
7914
|
+
fs14.accessSync(filePath, fs14.constants.R_OK);
|
|
7750
7915
|
return true;
|
|
7751
7916
|
} catch {
|
|
7752
7917
|
return false;
|
|
@@ -7759,13 +7924,13 @@ function scoreLabel(score) {
|
|
|
7759
7924
|
return chalk2.red.bold(`${score}/100 Critical`);
|
|
7760
7925
|
}
|
|
7761
7926
|
function runBlast() {
|
|
7762
|
-
const home =
|
|
7927
|
+
const home = os13.homedir();
|
|
7763
7928
|
const cwd = process.cwd();
|
|
7764
7929
|
const paths = buildSensitivePaths(home, cwd);
|
|
7765
7930
|
let scoreDeduction = 0;
|
|
7766
7931
|
const reachable = [];
|
|
7767
7932
|
for (const p of paths) {
|
|
7768
|
-
if (
|
|
7933
|
+
if (fs14.existsSync(p.full) && isReadable(p.full)) {
|
|
7769
7934
|
reachable.push(p);
|
|
7770
7935
|
scoreDeduction += p.score;
|
|
7771
7936
|
}
|
|
@@ -7783,7 +7948,7 @@ function runBlast() {
|
|
|
7783
7948
|
}
|
|
7784
7949
|
function registerBlastCommand(program2) {
|
|
7785
7950
|
program2.command("blast").description("Map what an AI agent can currently reach on this machine").action(() => {
|
|
7786
|
-
const home =
|
|
7951
|
+
const home = os13.homedir();
|
|
7787
7952
|
const cwd = process.cwd();
|
|
7788
7953
|
const { reachable, envFindings, score } = runBlast();
|
|
7789
7954
|
console.log("");
|
|
@@ -7959,17 +8124,17 @@ var init_scan_json = __esm({
|
|
|
7959
8124
|
});
|
|
7960
8125
|
|
|
7961
8126
|
// src/cli/render/scan-history.ts
|
|
7962
|
-
import
|
|
7963
|
-
import
|
|
7964
|
-
import
|
|
8127
|
+
import fs15 from "fs";
|
|
8128
|
+
import path17 from "path";
|
|
8129
|
+
import os14 from "os";
|
|
7965
8130
|
function defaultHistoryPath() {
|
|
7966
|
-
return
|
|
8131
|
+
return path17.join(os14.homedir(), ".node9", "scan-history.json");
|
|
7967
8132
|
}
|
|
7968
8133
|
function readPreviousScan(opts = {}) {
|
|
7969
8134
|
const filePath = opts.path ?? defaultHistoryPath();
|
|
7970
8135
|
try {
|
|
7971
|
-
if (!
|
|
7972
|
-
const raw =
|
|
8136
|
+
if (!fs15.existsSync(filePath)) return null;
|
|
8137
|
+
const raw = fs15.readFileSync(filePath, "utf8");
|
|
7973
8138
|
const parsed = JSON.parse(raw);
|
|
7974
8139
|
if (!Array.isArray(parsed) || parsed.length === 0) return null;
|
|
7975
8140
|
const last = parsed[parsed.length - 1];
|
|
@@ -7983,11 +8148,11 @@ function appendScanHistory(record, opts = {}) {
|
|
|
7983
8148
|
const filePath = opts.path ?? defaultHistoryPath();
|
|
7984
8149
|
const cap = opts.cap ?? SCAN_HISTORY_CAP;
|
|
7985
8150
|
try {
|
|
7986
|
-
|
|
8151
|
+
fs15.mkdirSync(path17.dirname(filePath), { recursive: true });
|
|
7987
8152
|
let history = [];
|
|
7988
|
-
if (
|
|
8153
|
+
if (fs15.existsSync(filePath)) {
|
|
7989
8154
|
try {
|
|
7990
|
-
const parsed = JSON.parse(
|
|
8155
|
+
const parsed = JSON.parse(fs15.readFileSync(filePath, "utf8"));
|
|
7991
8156
|
if (Array.isArray(parsed)) {
|
|
7992
8157
|
history = parsed.filter(isValidRecord);
|
|
7993
8158
|
}
|
|
@@ -7998,7 +8163,7 @@ function appendScanHistory(record, opts = {}) {
|
|
|
7998
8163
|
if (history.length > cap) {
|
|
7999
8164
|
history = history.slice(history.length - cap);
|
|
8000
8165
|
}
|
|
8001
|
-
|
|
8166
|
+
fs15.writeFileSync(filePath, JSON.stringify(history, null, 2));
|
|
8002
8167
|
} catch (err2) {
|
|
8003
8168
|
process.stderr.write(
|
|
8004
8169
|
`[node9] Warning: could not write scan-history.json: ${err2.message}
|
|
@@ -8029,15 +8194,15 @@ var init_scan_history = __esm({
|
|
|
8029
8194
|
});
|
|
8030
8195
|
|
|
8031
8196
|
// src/pricing/litellm.ts
|
|
8032
|
-
import
|
|
8033
|
-
import
|
|
8034
|
-
import
|
|
8197
|
+
import fs16 from "fs";
|
|
8198
|
+
import path18 from "path";
|
|
8199
|
+
import os15 from "os";
|
|
8035
8200
|
function normalizeModel(raw) {
|
|
8036
8201
|
return raw.replace(/-\d{8}$/, "").toLowerCase();
|
|
8037
8202
|
}
|
|
8038
8203
|
function readCache() {
|
|
8039
8204
|
try {
|
|
8040
|
-
const raw = JSON.parse(
|
|
8205
|
+
const raw = JSON.parse(fs16.readFileSync(CACHE_FILE(), "utf-8"));
|
|
8041
8206
|
if (typeof raw.fetchedAt !== "string" || typeof raw.prices !== "object" || raw.prices === null) {
|
|
8042
8207
|
return null;
|
|
8043
8208
|
}
|
|
@@ -8051,18 +8216,18 @@ function readCache() {
|
|
|
8051
8216
|
function writeCache(prices) {
|
|
8052
8217
|
try {
|
|
8053
8218
|
const target = CACHE_FILE();
|
|
8054
|
-
const dir =
|
|
8055
|
-
if (!
|
|
8219
|
+
const dir = path18.dirname(target);
|
|
8220
|
+
if (!fs16.existsSync(dir)) fs16.mkdirSync(dir, { recursive: true });
|
|
8056
8221
|
const tmp = target + ".tmp";
|
|
8057
8222
|
const body = {
|
|
8058
8223
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8059
8224
|
prices
|
|
8060
8225
|
};
|
|
8061
|
-
|
|
8062
|
-
|
|
8226
|
+
fs16.writeFileSync(tmp, JSON.stringify(body) + "\n", "utf-8");
|
|
8227
|
+
fs16.renameSync(tmp, target);
|
|
8063
8228
|
} catch (err2) {
|
|
8064
8229
|
try {
|
|
8065
|
-
|
|
8230
|
+
fs16.appendFileSync(
|
|
8066
8231
|
HOOK_DEBUG_LOG,
|
|
8067
8232
|
`[pricing] cache write failed: ${err2.message}
|
|
8068
8233
|
`
|
|
@@ -8185,7 +8350,7 @@ var init_litellm = __esm({
|
|
|
8185
8350
|
"gemini-2.0-flash": [75e-9, 3e-7, 0, 0],
|
|
8186
8351
|
"gemini-1.5-pro": [125e-8, 5e-6, 0, 0]
|
|
8187
8352
|
};
|
|
8188
|
-
CACHE_FILE = () =>
|
|
8353
|
+
CACHE_FILE = () => path18.join(os15.homedir(), ".node9", "model-pricing.json");
|
|
8189
8354
|
TTL_MS = 24 * 60 * 60 * 1e3;
|
|
8190
8355
|
memCache = null;
|
|
8191
8356
|
memCacheAt = 0;
|
|
@@ -8194,17 +8359,17 @@ var init_litellm = __esm({
|
|
|
8194
8359
|
});
|
|
8195
8360
|
|
|
8196
8361
|
// src/costSync.ts
|
|
8197
|
-
import
|
|
8198
|
-
import
|
|
8199
|
-
import
|
|
8362
|
+
import fs17 from "fs";
|
|
8363
|
+
import path19 from "path";
|
|
8364
|
+
import os16 from "os";
|
|
8200
8365
|
function decodeProjectDirName(dirName) {
|
|
8201
8366
|
return dirName.replace(/-/g, "/");
|
|
8202
8367
|
}
|
|
8203
8368
|
function parseJSONLFile(filePath, fallbackWorkingDir) {
|
|
8204
|
-
const runId =
|
|
8369
|
+
const runId = path19.basename(filePath, ".jsonl");
|
|
8205
8370
|
let content;
|
|
8206
8371
|
try {
|
|
8207
|
-
content =
|
|
8372
|
+
content = fs17.readFileSync(filePath, "utf8");
|
|
8208
8373
|
} catch {
|
|
8209
8374
|
return /* @__PURE__ */ new Map();
|
|
8210
8375
|
}
|
|
@@ -8260,34 +8425,34 @@ function parseJSONLFile(filePath, fallbackWorkingDir) {
|
|
|
8260
8425
|
return daily;
|
|
8261
8426
|
}
|
|
8262
8427
|
function collectEntries(sinceMs) {
|
|
8263
|
-
const projectsDir =
|
|
8264
|
-
if (!
|
|
8428
|
+
const projectsDir = path19.join(os16.homedir(), ".claude", "projects");
|
|
8429
|
+
if (!fs17.existsSync(projectsDir)) return [];
|
|
8265
8430
|
const combined = /* @__PURE__ */ new Map();
|
|
8266
8431
|
let dirs;
|
|
8267
8432
|
try {
|
|
8268
|
-
dirs =
|
|
8433
|
+
dirs = fs17.readdirSync(projectsDir);
|
|
8269
8434
|
} catch {
|
|
8270
8435
|
return [];
|
|
8271
8436
|
}
|
|
8272
8437
|
for (const dir of dirs) {
|
|
8273
|
-
const dirPath =
|
|
8438
|
+
const dirPath = path19.join(projectsDir, dir);
|
|
8274
8439
|
try {
|
|
8275
|
-
if (!
|
|
8440
|
+
if (!fs17.statSync(dirPath).isDirectory()) continue;
|
|
8276
8441
|
} catch {
|
|
8277
8442
|
continue;
|
|
8278
8443
|
}
|
|
8279
8444
|
let files;
|
|
8280
8445
|
try {
|
|
8281
|
-
files =
|
|
8446
|
+
files = fs17.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
|
|
8282
8447
|
} catch {
|
|
8283
8448
|
continue;
|
|
8284
8449
|
}
|
|
8285
8450
|
const fallbackWorkingDir = decodeProjectDirName(dir);
|
|
8286
8451
|
for (const file of files) {
|
|
8287
|
-
const filePath =
|
|
8452
|
+
const filePath = path19.join(dirPath, file);
|
|
8288
8453
|
if (sinceMs !== void 0) {
|
|
8289
8454
|
try {
|
|
8290
|
-
if (
|
|
8455
|
+
if (fs17.statSync(filePath).mtimeMs < sinceMs) continue;
|
|
8291
8456
|
} catch {
|
|
8292
8457
|
continue;
|
|
8293
8458
|
}
|
|
@@ -8317,10 +8482,10 @@ async function syncCost() {
|
|
|
8317
8482
|
if (entries.length === 0) return;
|
|
8318
8483
|
let username = "unknown";
|
|
8319
8484
|
try {
|
|
8320
|
-
username =
|
|
8485
|
+
username = os16.userInfo().username;
|
|
8321
8486
|
} catch {
|
|
8322
8487
|
}
|
|
8323
|
-
const machineId = `${
|
|
8488
|
+
const machineId = `${os16.hostname()}:${username}`;
|
|
8324
8489
|
try {
|
|
8325
8490
|
const res = await fetch(`${creds.apiUrl}/cost-sync`, {
|
|
8326
8491
|
method: "POST",
|
|
@@ -8329,11 +8494,11 @@ async function syncCost() {
|
|
|
8329
8494
|
signal: AbortSignal.timeout(15e3)
|
|
8330
8495
|
});
|
|
8331
8496
|
if (!res.ok) {
|
|
8332
|
-
|
|
8497
|
+
fs17.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
|
|
8333
8498
|
`);
|
|
8334
8499
|
}
|
|
8335
8500
|
} catch (err2) {
|
|
8336
|
-
|
|
8501
|
+
fs17.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
|
|
8337
8502
|
`);
|
|
8338
8503
|
}
|
|
8339
8504
|
}
|
|
@@ -8369,9 +8534,9 @@ __export(scan_watermark_exports, {
|
|
|
8369
8534
|
tickForensicBroadcast: () => tickForensicBroadcast,
|
|
8370
8535
|
tickScanWatcher: () => tickScanWatcher
|
|
8371
8536
|
});
|
|
8372
|
-
import
|
|
8373
|
-
import
|
|
8374
|
-
import
|
|
8537
|
+
import fs18 from "fs";
|
|
8538
|
+
import os17 from "os";
|
|
8539
|
+
import path20 from "path";
|
|
8375
8540
|
import readline from "readline";
|
|
8376
8541
|
function freshWatermark() {
|
|
8377
8542
|
return {
|
|
@@ -8384,7 +8549,7 @@ function freshWatermark() {
|
|
|
8384
8549
|
function loadWatermark() {
|
|
8385
8550
|
let raw;
|
|
8386
8551
|
try {
|
|
8387
|
-
raw =
|
|
8552
|
+
raw = fs18.readFileSync(WATERMARK_FILE(), "utf-8");
|
|
8388
8553
|
} catch {
|
|
8389
8554
|
return { status: "fresh", wm: freshWatermark() };
|
|
8390
8555
|
}
|
|
@@ -8436,28 +8601,28 @@ function loadWatermark() {
|
|
|
8436
8601
|
function saveWatermark(wm) {
|
|
8437
8602
|
if (wm.schemaVersion > WATERMARK_SCHEMA_VERSION) return;
|
|
8438
8603
|
const target = WATERMARK_FILE();
|
|
8439
|
-
const dir =
|
|
8440
|
-
if (!
|
|
8604
|
+
const dir = path20.dirname(target);
|
|
8605
|
+
if (!fs18.existsSync(dir)) fs18.mkdirSync(dir, { recursive: true });
|
|
8441
8606
|
const tmp = target + ".tmp";
|
|
8442
|
-
|
|
8443
|
-
|
|
8607
|
+
fs18.writeFileSync(tmp, JSON.stringify(wm, null, 2) + "\n", "utf-8");
|
|
8608
|
+
fs18.renameSync(tmp, target);
|
|
8444
8609
|
}
|
|
8445
8610
|
function listJsonlFiles() {
|
|
8446
8611
|
const root = PROJECTS_DIR();
|
|
8447
|
-
if (!
|
|
8612
|
+
if (!fs18.existsSync(root)) return [];
|
|
8448
8613
|
const out = [];
|
|
8449
|
-
for (const entry of
|
|
8614
|
+
for (const entry of fs18.readdirSync(root, { withFileTypes: true })) {
|
|
8450
8615
|
if (!entry.isDirectory()) continue;
|
|
8451
|
-
const projectDir =
|
|
8616
|
+
const projectDir = path20.join(root, entry.name);
|
|
8452
8617
|
let inner;
|
|
8453
8618
|
try {
|
|
8454
|
-
inner =
|
|
8619
|
+
inner = fs18.readdirSync(projectDir, { withFileTypes: true });
|
|
8455
8620
|
} catch {
|
|
8456
8621
|
continue;
|
|
8457
8622
|
}
|
|
8458
8623
|
for (const file of inner) {
|
|
8459
8624
|
if (file.isFile() && file.name.endsWith(".jsonl")) {
|
|
8460
|
-
out.push(
|
|
8625
|
+
out.push(path20.join(projectDir, file.name));
|
|
8461
8626
|
}
|
|
8462
8627
|
}
|
|
8463
8628
|
}
|
|
@@ -8465,7 +8630,7 @@ function listJsonlFiles() {
|
|
|
8465
8630
|
}
|
|
8466
8631
|
function fileSize(p) {
|
|
8467
8632
|
try {
|
|
8468
|
-
return
|
|
8633
|
+
return fs18.statSync(p).size;
|
|
8469
8634
|
} catch {
|
|
8470
8635
|
return 0;
|
|
8471
8636
|
}
|
|
@@ -8473,7 +8638,7 @@ function fileSize(p) {
|
|
|
8473
8638
|
async function scanDelta(filePath, fromByte, onLine) {
|
|
8474
8639
|
const size = fileSize(filePath);
|
|
8475
8640
|
if (size <= fromByte) return fromByte;
|
|
8476
|
-
const stream =
|
|
8641
|
+
const stream = fs18.createReadStream(filePath, {
|
|
8477
8642
|
start: fromByte,
|
|
8478
8643
|
end: size - 1,
|
|
8479
8644
|
highWaterMark: 64 * 1024
|
|
@@ -8585,7 +8750,7 @@ async function tickForensicBroadcast(offsets) {
|
|
|
8585
8750
|
continue;
|
|
8586
8751
|
}
|
|
8587
8752
|
if (size <= offset) continue;
|
|
8588
|
-
const sessionId =
|
|
8753
|
+
const sessionId = path20.basename(file, ".jsonl");
|
|
8589
8754
|
const newOffset = await scanDelta(file, offset, (obj, lineIndex) => {
|
|
8590
8755
|
out.push(...extractFindingsFromLine(obj, sessionId, lineIndex));
|
|
8591
8756
|
});
|
|
@@ -8644,7 +8809,7 @@ function emptyTick(uploadAs) {
|
|
|
8644
8809
|
function readRawWatermarkPreservingOffsets() {
|
|
8645
8810
|
let raw;
|
|
8646
8811
|
try {
|
|
8647
|
-
raw =
|
|
8812
|
+
raw = fs18.readFileSync(WATERMARK_FILE(), "utf-8");
|
|
8648
8813
|
} catch {
|
|
8649
8814
|
return null;
|
|
8650
8815
|
}
|
|
@@ -8678,13 +8843,13 @@ async function runActualTick(wm) {
|
|
|
8678
8843
|
if (!known) {
|
|
8679
8844
|
let mtimeMs = 0;
|
|
8680
8845
|
try {
|
|
8681
|
-
mtimeMs =
|
|
8846
|
+
mtimeMs = fs18.statSync(filePath).mtime.getTime();
|
|
8682
8847
|
} catch {
|
|
8683
8848
|
continue;
|
|
8684
8849
|
}
|
|
8685
8850
|
if (mtimeMs >= watermarkCreatedAt) {
|
|
8686
8851
|
filesNew++;
|
|
8687
|
-
const sessionId2 =
|
|
8852
|
+
const sessionId2 = path20.basename(filePath, ".jsonl");
|
|
8688
8853
|
const newScannedTo2 = await scanDelta(filePath, 0, (obj, lineIndex) => {
|
|
8689
8854
|
totalToolCalls++;
|
|
8690
8855
|
toolCallsBySession[sessionId2] = (toolCallsBySession[sessionId2] ?? 0) + 1;
|
|
@@ -8702,7 +8867,7 @@ async function runActualTick(wm) {
|
|
|
8702
8867
|
filesSkipped++;
|
|
8703
8868
|
continue;
|
|
8704
8869
|
}
|
|
8705
|
-
const sessionId =
|
|
8870
|
+
const sessionId = path20.basename(filePath, ".jsonl");
|
|
8706
8871
|
const newScannedTo = await scanDelta(filePath, known.scannedTo, (obj, lineIndex) => {
|
|
8707
8872
|
totalToolCalls++;
|
|
8708
8873
|
toolCallsBySession[sessionId] = (toolCallsBySession[sessionId] ?? 0) + 1;
|
|
@@ -8730,8 +8895,8 @@ var init_scan_watermark = __esm({
|
|
|
8730
8895
|
"use strict";
|
|
8731
8896
|
init_dlp();
|
|
8732
8897
|
init_dist();
|
|
8733
|
-
PROJECTS_DIR = () =>
|
|
8734
|
-
WATERMARK_FILE = () =>
|
|
8898
|
+
PROJECTS_DIR = () => path20.join(os17.homedir(), ".claude", "projects");
|
|
8899
|
+
WATERMARK_FILE = () => path20.join(os17.homedir(), ".node9", "scan-watermark.json");
|
|
8735
8900
|
MAX_LINE_BYTES = 2 * 1024 * 1024;
|
|
8736
8901
|
WATERMARK_SCHEMA_VERSION = 2;
|
|
8737
8902
|
LONG_OUTPUT_THRESHOLD_BYTES2 = LONG_OUTPUT_THRESHOLD_BYTES;
|
|
@@ -8746,10 +8911,10 @@ __export(scan_upload_history_exports, {
|
|
|
8746
8911
|
parseSinceCutoff: () => parseSinceCutoff,
|
|
8747
8912
|
runUploadHistory: () => runUploadHistory
|
|
8748
8913
|
});
|
|
8749
|
-
import
|
|
8914
|
+
import fs19 from "fs";
|
|
8750
8915
|
import https from "https";
|
|
8751
|
-
import
|
|
8752
|
-
import
|
|
8916
|
+
import os18 from "os";
|
|
8917
|
+
import path21 from "path";
|
|
8753
8918
|
import chalk4 from "chalk";
|
|
8754
8919
|
function emptySignals2() {
|
|
8755
8920
|
return {
|
|
@@ -8784,40 +8949,40 @@ function parseSinceCutoff(raw, now = /* @__PURE__ */ new Date()) {
|
|
|
8784
8949
|
return now.getTime() - 90 * 864e5;
|
|
8785
8950
|
}
|
|
8786
8951
|
function* iterateJsonlFiles(cutoffMs) {
|
|
8787
|
-
const projectsDir =
|
|
8952
|
+
const projectsDir = path21.join(os18.homedir(), ".claude", "projects");
|
|
8788
8953
|
let dirs;
|
|
8789
8954
|
try {
|
|
8790
|
-
dirs =
|
|
8955
|
+
dirs = fs19.readdirSync(projectsDir);
|
|
8791
8956
|
} catch {
|
|
8792
8957
|
return;
|
|
8793
8958
|
}
|
|
8794
8959
|
for (const dir of dirs) {
|
|
8795
|
-
const dirPath =
|
|
8960
|
+
const dirPath = path21.join(projectsDir, dir);
|
|
8796
8961
|
let stats;
|
|
8797
8962
|
try {
|
|
8798
|
-
stats =
|
|
8963
|
+
stats = fs19.statSync(dirPath);
|
|
8799
8964
|
} catch {
|
|
8800
8965
|
continue;
|
|
8801
8966
|
}
|
|
8802
8967
|
if (!stats.isDirectory()) continue;
|
|
8803
8968
|
let files;
|
|
8804
8969
|
try {
|
|
8805
|
-
files =
|
|
8970
|
+
files = fs19.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
|
|
8806
8971
|
} catch {
|
|
8807
8972
|
continue;
|
|
8808
8973
|
}
|
|
8809
8974
|
for (const file of files) {
|
|
8810
|
-
const filePath =
|
|
8975
|
+
const filePath = path21.join(dirPath, file);
|
|
8811
8976
|
let mtime = 0;
|
|
8812
8977
|
try {
|
|
8813
|
-
mtime =
|
|
8978
|
+
mtime = fs19.statSync(filePath).mtimeMs;
|
|
8814
8979
|
} catch {
|
|
8815
8980
|
continue;
|
|
8816
8981
|
}
|
|
8817
8982
|
if (mtime < cutoffMs) continue;
|
|
8818
8983
|
yield {
|
|
8819
8984
|
filePath,
|
|
8820
|
-
sessionId:
|
|
8985
|
+
sessionId: path21.basename(file, ".jsonl"),
|
|
8821
8986
|
projectDir: dir
|
|
8822
8987
|
};
|
|
8823
8988
|
}
|
|
@@ -8870,7 +9035,7 @@ async function runUploadHistory(opts) {
|
|
|
8870
9035
|
filesScanned++;
|
|
8871
9036
|
let content;
|
|
8872
9037
|
try {
|
|
8873
|
-
content =
|
|
9038
|
+
content = fs19.readFileSync(filePath, "utf8");
|
|
8874
9039
|
} catch {
|
|
8875
9040
|
continue;
|
|
8876
9041
|
}
|
|
@@ -8956,10 +9121,10 @@ async function runUploadHistory(opts) {
|
|
|
8956
9121
|
const costUrl = creds.apiUrl.endsWith("/policies/sync") ? creds.apiUrl.replace(/\/policies\/sync$/, "/cost-sync") : `${creds.apiUrl.replace(/\/$/, "")}/cost-sync`;
|
|
8957
9122
|
let username = "unknown";
|
|
8958
9123
|
try {
|
|
8959
|
-
username =
|
|
9124
|
+
username = os18.userInfo().username;
|
|
8960
9125
|
} catch {
|
|
8961
9126
|
}
|
|
8962
|
-
const machineId = `${
|
|
9127
|
+
const machineId = `${os18.hostname()}:${username}`;
|
|
8963
9128
|
await postJson(costUrl, creds.apiKey, {
|
|
8964
9129
|
machineId,
|
|
8965
9130
|
entries: dailyEntries
|
|
@@ -9033,9 +9198,9 @@ var init_scan_upload_history = __esm({
|
|
|
9033
9198
|
|
|
9034
9199
|
// src/cli/commands/scan.ts
|
|
9035
9200
|
import chalk5 from "chalk";
|
|
9036
|
-
import
|
|
9037
|
-
import
|
|
9038
|
-
import
|
|
9201
|
+
import fs20 from "fs";
|
|
9202
|
+
import path22 from "path";
|
|
9203
|
+
import os19 from "os";
|
|
9039
9204
|
import stringWidth2 from "string-width";
|
|
9040
9205
|
function claudeModelPrice(model) {
|
|
9041
9206
|
const base = model.replace(/@.*$/, "").replace(/-\d{8}$/, "");
|
|
@@ -9262,14 +9427,14 @@ function buildRuleSources() {
|
|
|
9262
9427
|
}
|
|
9263
9428
|
function countScanFiles() {
|
|
9264
9429
|
let total = 0;
|
|
9265
|
-
const claudeDir =
|
|
9266
|
-
if (
|
|
9430
|
+
const claudeDir = path22.join(os19.homedir(), ".claude", "projects");
|
|
9431
|
+
if (fs20.existsSync(claudeDir)) {
|
|
9267
9432
|
try {
|
|
9268
|
-
for (const proj of
|
|
9269
|
-
const p =
|
|
9433
|
+
for (const proj of fs20.readdirSync(claudeDir)) {
|
|
9434
|
+
const p = path22.join(claudeDir, proj);
|
|
9270
9435
|
try {
|
|
9271
|
-
if (!
|
|
9272
|
-
total +=
|
|
9436
|
+
if (!fs20.statSync(p).isDirectory()) continue;
|
|
9437
|
+
total += fs20.readdirSync(p).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).length;
|
|
9273
9438
|
} catch {
|
|
9274
9439
|
continue;
|
|
9275
9440
|
}
|
|
@@ -9277,17 +9442,17 @@ function countScanFiles() {
|
|
|
9277
9442
|
} catch {
|
|
9278
9443
|
}
|
|
9279
9444
|
}
|
|
9280
|
-
const geminiDir =
|
|
9281
|
-
if (
|
|
9445
|
+
const geminiDir = path22.join(os19.homedir(), ".gemini", "tmp");
|
|
9446
|
+
if (fs20.existsSync(geminiDir)) {
|
|
9282
9447
|
try {
|
|
9283
|
-
for (const slug of
|
|
9284
|
-
const p =
|
|
9448
|
+
for (const slug of fs20.readdirSync(geminiDir)) {
|
|
9449
|
+
const p = path22.join(geminiDir, slug);
|
|
9285
9450
|
try {
|
|
9286
|
-
if (!
|
|
9287
|
-
const chatsDir =
|
|
9288
|
-
if (
|
|
9451
|
+
if (!fs20.statSync(p).isDirectory()) continue;
|
|
9452
|
+
const chatsDir = path22.join(p, "chats");
|
|
9453
|
+
if (fs20.existsSync(chatsDir)) {
|
|
9289
9454
|
try {
|
|
9290
|
-
total +=
|
|
9455
|
+
total += fs20.readdirSync(chatsDir).filter((f) => f.endsWith(".json")).length;
|
|
9291
9456
|
} catch {
|
|
9292
9457
|
}
|
|
9293
9458
|
}
|
|
@@ -9298,22 +9463,22 @@ function countScanFiles() {
|
|
|
9298
9463
|
} catch {
|
|
9299
9464
|
}
|
|
9300
9465
|
}
|
|
9301
|
-
const codexDir =
|
|
9302
|
-
if (
|
|
9466
|
+
const codexDir = path22.join(os19.homedir(), ".codex", "sessions");
|
|
9467
|
+
if (fs20.existsSync(codexDir)) {
|
|
9303
9468
|
try {
|
|
9304
|
-
for (const year of
|
|
9305
|
-
const yp =
|
|
9469
|
+
for (const year of fs20.readdirSync(codexDir)) {
|
|
9470
|
+
const yp = path22.join(codexDir, year);
|
|
9306
9471
|
try {
|
|
9307
|
-
if (!
|
|
9308
|
-
for (const month of
|
|
9309
|
-
const mp =
|
|
9472
|
+
if (!fs20.statSync(yp).isDirectory()) continue;
|
|
9473
|
+
for (const month of fs20.readdirSync(yp)) {
|
|
9474
|
+
const mp = path22.join(yp, month);
|
|
9310
9475
|
try {
|
|
9311
|
-
if (!
|
|
9312
|
-
for (const day of
|
|
9313
|
-
const dp =
|
|
9476
|
+
if (!fs20.statSync(mp).isDirectory()) continue;
|
|
9477
|
+
for (const day of fs20.readdirSync(mp)) {
|
|
9478
|
+
const dp = path22.join(mp, day);
|
|
9314
9479
|
try {
|
|
9315
|
-
if (!
|
|
9316
|
-
total +=
|
|
9480
|
+
if (!fs20.statSync(dp).isDirectory()) continue;
|
|
9481
|
+
total += fs20.readdirSync(dp).filter((f) => f.endsWith(".jsonl")).length;
|
|
9317
9482
|
} catch {
|
|
9318
9483
|
continue;
|
|
9319
9484
|
}
|
|
@@ -9349,7 +9514,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
9349
9514
|
const sessionId = file.replace(/\.jsonl$/, "");
|
|
9350
9515
|
let raw;
|
|
9351
9516
|
try {
|
|
9352
|
-
raw =
|
|
9517
|
+
raw = fs20.readFileSync(path22.join(projPath, file), "utf-8");
|
|
9353
9518
|
} catch {
|
|
9354
9519
|
return;
|
|
9355
9520
|
}
|
|
@@ -9401,7 +9566,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
9401
9566
|
if (block.type !== "tool_result") continue;
|
|
9402
9567
|
const filePath = block.tool_use_id ? toolUseFilePaths.get(block.tool_use_id) : void 0;
|
|
9403
9568
|
if (filePath) {
|
|
9404
|
-
const ext =
|
|
9569
|
+
const ext = path22.extname(filePath).toLowerCase();
|
|
9405
9570
|
if (CODE_EXTENSIONS.has(ext)) continue;
|
|
9406
9571
|
}
|
|
9407
9572
|
const resultText = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map((c) => c.text ?? "").join("\n") : null;
|
|
@@ -9458,7 +9623,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
9458
9623
|
const rawCmd = String(input.command ?? "").trimStart();
|
|
9459
9624
|
if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd)) continue;
|
|
9460
9625
|
const inputFilePath = typeof input.file_path === "string" ? input.file_path : "";
|
|
9461
|
-
const inputFileExt = inputFilePath ?
|
|
9626
|
+
const inputFileExt = inputFilePath ? path22.extname(inputFilePath).toLowerCase() : "";
|
|
9462
9627
|
if (CODE_EXTENSIONS.has(inputFileExt)) continue;
|
|
9463
9628
|
const dlpMatch = scanArgs(input);
|
|
9464
9629
|
if (dlpMatch) {
|
|
@@ -9555,19 +9720,19 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
9555
9720
|
}
|
|
9556
9721
|
}
|
|
9557
9722
|
function processClaudeProject(proj, projectsDir, ruleSources, startDate, result, dedup, onProgress, onLine) {
|
|
9558
|
-
const projPath =
|
|
9723
|
+
const projPath = path22.join(projectsDir, proj);
|
|
9559
9724
|
try {
|
|
9560
|
-
if (!
|
|
9725
|
+
if (!fs20.statSync(projPath).isDirectory()) return;
|
|
9561
9726
|
} catch {
|
|
9562
9727
|
return;
|
|
9563
9728
|
}
|
|
9564
|
-
const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(
|
|
9729
|
+
const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(os19.homedir(), "~")).slice(
|
|
9565
9730
|
0,
|
|
9566
9731
|
40
|
|
9567
9732
|
);
|
|
9568
9733
|
let files;
|
|
9569
9734
|
try {
|
|
9570
|
-
files =
|
|
9735
|
+
files = fs20.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
9571
9736
|
} catch {
|
|
9572
9737
|
return;
|
|
9573
9738
|
}
|
|
@@ -9601,12 +9766,12 @@ function emptyClaudeScan() {
|
|
|
9601
9766
|
};
|
|
9602
9767
|
}
|
|
9603
9768
|
function scanClaudeHistory(startDate, onProgress, onLine) {
|
|
9604
|
-
const projectsDir =
|
|
9769
|
+
const projectsDir = path22.join(os19.homedir(), ".claude", "projects");
|
|
9605
9770
|
const result = emptyClaudeScan();
|
|
9606
|
-
if (!
|
|
9771
|
+
if (!fs20.existsSync(projectsDir)) return result;
|
|
9607
9772
|
let projDirs;
|
|
9608
9773
|
try {
|
|
9609
|
-
projDirs =
|
|
9774
|
+
projDirs = fs20.readdirSync(projectsDir);
|
|
9610
9775
|
} catch {
|
|
9611
9776
|
return result;
|
|
9612
9777
|
}
|
|
@@ -9627,7 +9792,7 @@ function scanClaudeHistory(startDate, onProgress, onLine) {
|
|
|
9627
9792
|
return result;
|
|
9628
9793
|
}
|
|
9629
9794
|
function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
9630
|
-
const tmpDir =
|
|
9795
|
+
const tmpDir = path22.join(os19.homedir(), ".gemini", "tmp");
|
|
9631
9796
|
const result = {
|
|
9632
9797
|
filesScanned: 0,
|
|
9633
9798
|
sessions: 0,
|
|
@@ -9642,33 +9807,33 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
9642
9807
|
sessionsWithEarlySecrets: 0
|
|
9643
9808
|
};
|
|
9644
9809
|
const dedup = emptyScanDedup();
|
|
9645
|
-
if (!
|
|
9810
|
+
if (!fs20.existsSync(tmpDir)) return result;
|
|
9646
9811
|
let slugDirs;
|
|
9647
9812
|
try {
|
|
9648
|
-
slugDirs =
|
|
9813
|
+
slugDirs = fs20.readdirSync(tmpDir);
|
|
9649
9814
|
} catch {
|
|
9650
9815
|
return result;
|
|
9651
9816
|
}
|
|
9652
9817
|
const ruleSources = buildRuleSources();
|
|
9653
9818
|
for (const slug of slugDirs) {
|
|
9654
|
-
const slugPath =
|
|
9819
|
+
const slugPath = path22.join(tmpDir, slug);
|
|
9655
9820
|
try {
|
|
9656
|
-
if (!
|
|
9821
|
+
if (!fs20.statSync(slugPath).isDirectory()) continue;
|
|
9657
9822
|
} catch {
|
|
9658
9823
|
continue;
|
|
9659
9824
|
}
|
|
9660
9825
|
let projLabel = stripTerminalEscapes(slug).slice(0, 40);
|
|
9661
9826
|
try {
|
|
9662
9827
|
projLabel = stripTerminalEscapes(
|
|
9663
|
-
|
|
9664
|
-
).replace(
|
|
9828
|
+
fs20.readFileSync(path22.join(slugPath, ".project_root"), "utf-8").trim()
|
|
9829
|
+
).replace(os19.homedir(), "~").slice(0, 40);
|
|
9665
9830
|
} catch {
|
|
9666
9831
|
}
|
|
9667
|
-
const chatsDir =
|
|
9668
|
-
if (!
|
|
9832
|
+
const chatsDir = path22.join(slugPath, "chats");
|
|
9833
|
+
if (!fs20.existsSync(chatsDir)) continue;
|
|
9669
9834
|
let chatFiles;
|
|
9670
9835
|
try {
|
|
9671
|
-
chatFiles =
|
|
9836
|
+
chatFiles = fs20.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
|
|
9672
9837
|
} catch {
|
|
9673
9838
|
continue;
|
|
9674
9839
|
}
|
|
@@ -9678,7 +9843,7 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
9678
9843
|
const sessionId = chatFile.replace(/\.json$/, "");
|
|
9679
9844
|
let raw;
|
|
9680
9845
|
try {
|
|
9681
|
-
raw =
|
|
9846
|
+
raw = fs20.readFileSync(path22.join(chatsDir, chatFile), "utf-8");
|
|
9682
9847
|
} catch {
|
|
9683
9848
|
continue;
|
|
9684
9849
|
}
|
|
@@ -9840,7 +10005,7 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
9840
10005
|
return result;
|
|
9841
10006
|
}
|
|
9842
10007
|
function scanCodexHistory(startDate, onProgress, onLine) {
|
|
9843
|
-
const sessionsBase =
|
|
10008
|
+
const sessionsBase = path22.join(os19.homedir(), ".codex", "sessions");
|
|
9844
10009
|
const result = {
|
|
9845
10010
|
filesScanned: 0,
|
|
9846
10011
|
sessions: 0,
|
|
@@ -9855,32 +10020,32 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
9855
10020
|
sessionsWithEarlySecrets: 0
|
|
9856
10021
|
};
|
|
9857
10022
|
const dedup = emptyScanDedup();
|
|
9858
|
-
if (!
|
|
10023
|
+
if (!fs20.existsSync(sessionsBase)) return result;
|
|
9859
10024
|
const jsonlFiles = [];
|
|
9860
10025
|
try {
|
|
9861
|
-
for (const year of
|
|
9862
|
-
const yearPath =
|
|
10026
|
+
for (const year of fs20.readdirSync(sessionsBase)) {
|
|
10027
|
+
const yearPath = path22.join(sessionsBase, year);
|
|
9863
10028
|
try {
|
|
9864
|
-
if (!
|
|
10029
|
+
if (!fs20.statSync(yearPath).isDirectory()) continue;
|
|
9865
10030
|
} catch {
|
|
9866
10031
|
continue;
|
|
9867
10032
|
}
|
|
9868
|
-
for (const month of
|
|
9869
|
-
const monthPath =
|
|
10033
|
+
for (const month of fs20.readdirSync(yearPath)) {
|
|
10034
|
+
const monthPath = path22.join(yearPath, month);
|
|
9870
10035
|
try {
|
|
9871
|
-
if (!
|
|
10036
|
+
if (!fs20.statSync(monthPath).isDirectory()) continue;
|
|
9872
10037
|
} catch {
|
|
9873
10038
|
continue;
|
|
9874
10039
|
}
|
|
9875
|
-
for (const day of
|
|
9876
|
-
const dayPath =
|
|
10040
|
+
for (const day of fs20.readdirSync(monthPath)) {
|
|
10041
|
+
const dayPath = path22.join(monthPath, day);
|
|
9877
10042
|
try {
|
|
9878
|
-
if (!
|
|
10043
|
+
if (!fs20.statSync(dayPath).isDirectory()) continue;
|
|
9879
10044
|
} catch {
|
|
9880
10045
|
continue;
|
|
9881
10046
|
}
|
|
9882
|
-
for (const file of
|
|
9883
|
-
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));
|
|
9884
10049
|
}
|
|
9885
10050
|
}
|
|
9886
10051
|
}
|
|
@@ -9894,7 +10059,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
9894
10059
|
onProgress?.(result.filesScanned);
|
|
9895
10060
|
let lines;
|
|
9896
10061
|
try {
|
|
9897
|
-
lines =
|
|
10062
|
+
lines = fs20.readFileSync(filePath, "utf-8").split("\n");
|
|
9898
10063
|
} catch {
|
|
9899
10064
|
continue;
|
|
9900
10065
|
}
|
|
@@ -9920,7 +10085,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
9920
10085
|
sessionId = String(payload["id"] ?? filePath);
|
|
9921
10086
|
startTime = String(payload["timestamp"] ?? "");
|
|
9922
10087
|
const cwd = String(payload["cwd"] ?? "");
|
|
9923
|
-
projLabel = stripTerminalEscapes(cwd.replace(
|
|
10088
|
+
projLabel = stripTerminalEscapes(cwd.replace(os19.homedir(), "~")).slice(0, 40);
|
|
9924
10089
|
continue;
|
|
9925
10090
|
}
|
|
9926
10091
|
if (entry.type === "event_msg" && payload["type"] === "token_count") {
|
|
@@ -10073,17 +10238,17 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
10073
10238
|
return result;
|
|
10074
10239
|
}
|
|
10075
10240
|
function scanShellConfig() {
|
|
10076
|
-
const home =
|
|
10241
|
+
const home = os19.homedir();
|
|
10077
10242
|
const configFiles = [".zshrc", ".bashrc", ".bash_profile", ".profile"].map(
|
|
10078
|
-
(f) =>
|
|
10243
|
+
(f) => path22.join(home, f)
|
|
10079
10244
|
);
|
|
10080
10245
|
const findings = [];
|
|
10081
10246
|
const seen = /* @__PURE__ */ new Set();
|
|
10082
10247
|
for (const filePath of configFiles) {
|
|
10083
|
-
if (!
|
|
10248
|
+
if (!fs20.existsSync(filePath)) continue;
|
|
10084
10249
|
let lines;
|
|
10085
10250
|
try {
|
|
10086
|
-
lines =
|
|
10251
|
+
lines = fs20.readFileSync(filePath, "utf-8").split("\n");
|
|
10087
10252
|
} catch {
|
|
10088
10253
|
continue;
|
|
10089
10254
|
}
|
|
@@ -10797,7 +10962,8 @@ function registerScanCommand(program2) {
|
|
|
10797
10962
|
" node9 is still worth running \u2014 it monitors every tool call in real time.\n"
|
|
10798
10963
|
)
|
|
10799
10964
|
);
|
|
10800
|
-
}
|
|
10965
|
+
}
|
|
10966
|
+
{
|
|
10801
10967
|
const totalRisky = totalFindings + scan.dlpFindings.length;
|
|
10802
10968
|
const score = classifyScore(blast.score);
|
|
10803
10969
|
const severityDisplay = score.band === "critical" ? chalk5.red.bold(score.label) : score.color(score.label);
|
|
@@ -10863,7 +11029,7 @@ function registerScanCommand(program2) {
|
|
|
10863
11029
|
if (!drillDown) {
|
|
10864
11030
|
const useInk2 = !options.classic;
|
|
10865
11031
|
if (useInk2) {
|
|
10866
|
-
const scanInkPath =
|
|
11032
|
+
const scanInkPath = path22.join(__dirname, "scan-ink.mjs");
|
|
10867
11033
|
const dynamicImport = new Function("id", "return import(id)");
|
|
10868
11034
|
const mod = await dynamicImport(`file://${scanInkPath}`);
|
|
10869
11035
|
const rangeLabel2 = options.all ? "all time" : `last ${options.days ?? 90} days`;
|
|
@@ -11281,8 +11447,8 @@ var init_suggestion_tracker = __esm({
|
|
|
11281
11447
|
});
|
|
11282
11448
|
|
|
11283
11449
|
// src/daemon/taint-store.ts
|
|
11284
|
-
import
|
|
11285
|
-
import
|
|
11450
|
+
import fs21 from "fs";
|
|
11451
|
+
import path23 from "path";
|
|
11286
11452
|
var DEFAULT_TTL_MS, TaintStore;
|
|
11287
11453
|
var init_taint_store = __esm({
|
|
11288
11454
|
"src/daemon/taint-store.ts"() {
|
|
@@ -11351,9 +11517,9 @@ var init_taint_store = __esm({
|
|
|
11351
11517
|
/** Resolve to absolute path, falling back to path.resolve if file doesn't exist yet. */
|
|
11352
11518
|
_resolve(filePath) {
|
|
11353
11519
|
try {
|
|
11354
|
-
return
|
|
11520
|
+
return fs21.realpathSync.native(path23.resolve(filePath));
|
|
11355
11521
|
} catch {
|
|
11356
|
-
return
|
|
11522
|
+
return path23.resolve(filePath);
|
|
11357
11523
|
}
|
|
11358
11524
|
}
|
|
11359
11525
|
};
|
|
@@ -11470,14 +11636,14 @@ var init_session_history = __esm({
|
|
|
11470
11636
|
|
|
11471
11637
|
// src/daemon/state.ts
|
|
11472
11638
|
import net2 from "net";
|
|
11473
|
-
import
|
|
11474
|
-
import
|
|
11475
|
-
import
|
|
11639
|
+
import fs22 from "fs";
|
|
11640
|
+
import path24 from "path";
|
|
11641
|
+
import os20 from "os";
|
|
11476
11642
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
11477
11643
|
function loadInsightCounts() {
|
|
11478
11644
|
try {
|
|
11479
|
-
if (!
|
|
11480
|
-
const data = JSON.parse(
|
|
11645
|
+
if (!fs22.existsSync(INSIGHT_COUNTS_FILE)) return;
|
|
11646
|
+
const data = JSON.parse(fs22.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
|
|
11481
11647
|
for (const [tool, count] of Object.entries(data)) {
|
|
11482
11648
|
if (typeof count === "number" && count > 0) insightCounts.set(tool, count);
|
|
11483
11649
|
}
|
|
@@ -11516,23 +11682,23 @@ function markRejectionHandlerRegistered() {
|
|
|
11516
11682
|
daemonRejectionHandlerRegistered = true;
|
|
11517
11683
|
}
|
|
11518
11684
|
function atomicWriteSync2(filePath, data, options) {
|
|
11519
|
-
const dir =
|
|
11520
|
-
if (!
|
|
11685
|
+
const dir = path24.dirname(filePath);
|
|
11686
|
+
if (!fs22.existsSync(dir)) fs22.mkdirSync(dir, { recursive: true });
|
|
11521
11687
|
const tmpPath = `${filePath}.${randomUUID3()}.tmp`;
|
|
11522
11688
|
try {
|
|
11523
|
-
|
|
11689
|
+
fs22.writeFileSync(tmpPath, data, options);
|
|
11524
11690
|
} catch (err2) {
|
|
11525
11691
|
try {
|
|
11526
|
-
|
|
11692
|
+
fs22.unlinkSync(tmpPath);
|
|
11527
11693
|
} catch {
|
|
11528
11694
|
}
|
|
11529
11695
|
throw err2;
|
|
11530
11696
|
}
|
|
11531
11697
|
try {
|
|
11532
|
-
|
|
11698
|
+
fs22.renameSync(tmpPath, filePath);
|
|
11533
11699
|
} catch (err2) {
|
|
11534
11700
|
try {
|
|
11535
|
-
|
|
11701
|
+
fs22.unlinkSync(tmpPath);
|
|
11536
11702
|
} catch {
|
|
11537
11703
|
}
|
|
11538
11704
|
throw err2;
|
|
@@ -11556,16 +11722,16 @@ function appendAuditLog(data) {
|
|
|
11556
11722
|
decision: data.decision,
|
|
11557
11723
|
source: "daemon"
|
|
11558
11724
|
};
|
|
11559
|
-
const dir =
|
|
11560
|
-
if (!
|
|
11561
|
-
|
|
11725
|
+
const dir = path24.dirname(AUDIT_LOG_FILE);
|
|
11726
|
+
if (!fs22.existsSync(dir)) fs22.mkdirSync(dir, { recursive: true });
|
|
11727
|
+
fs22.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
|
|
11562
11728
|
} catch {
|
|
11563
11729
|
}
|
|
11564
11730
|
}
|
|
11565
11731
|
function getAuditHistory(limit = 20) {
|
|
11566
11732
|
try {
|
|
11567
|
-
if (!
|
|
11568
|
-
const lines =
|
|
11733
|
+
if (!fs22.existsSync(AUDIT_LOG_FILE)) return [];
|
|
11734
|
+
const lines = fs22.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
|
|
11569
11735
|
if (lines.length === 1 && lines[0] === "") return [];
|
|
11570
11736
|
return lines.slice(-limit).map((l) => JSON.parse(l)).reverse();
|
|
11571
11737
|
} catch {
|
|
@@ -11574,7 +11740,7 @@ function getAuditHistory(limit = 20) {
|
|
|
11574
11740
|
}
|
|
11575
11741
|
function getOrgName() {
|
|
11576
11742
|
try {
|
|
11577
|
-
if (
|
|
11743
|
+
if (fs22.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
|
|
11578
11744
|
} catch {
|
|
11579
11745
|
}
|
|
11580
11746
|
return null;
|
|
@@ -11582,8 +11748,8 @@ function getOrgName() {
|
|
|
11582
11748
|
function writeGlobalSetting(key, value) {
|
|
11583
11749
|
let config = {};
|
|
11584
11750
|
try {
|
|
11585
|
-
if (
|
|
11586
|
-
config = JSON.parse(
|
|
11751
|
+
if (fs22.existsSync(GLOBAL_CONFIG_FILE)) {
|
|
11752
|
+
config = JSON.parse(fs22.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
|
|
11587
11753
|
}
|
|
11588
11754
|
} catch {
|
|
11589
11755
|
}
|
|
@@ -11595,8 +11761,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
|
|
|
11595
11761
|
try {
|
|
11596
11762
|
let trust = { entries: [] };
|
|
11597
11763
|
try {
|
|
11598
|
-
if (
|
|
11599
|
-
trust = JSON.parse(
|
|
11764
|
+
if (fs22.existsSync(TRUST_FILE2))
|
|
11765
|
+
trust = JSON.parse(fs22.readFileSync(TRUST_FILE2, "utf-8"));
|
|
11600
11766
|
} catch {
|
|
11601
11767
|
}
|
|
11602
11768
|
trust.entries = trust.entries.filter(
|
|
@@ -11613,8 +11779,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
|
|
|
11613
11779
|
}
|
|
11614
11780
|
function readPersistentDecisions() {
|
|
11615
11781
|
try {
|
|
11616
|
-
if (
|
|
11617
|
-
return JSON.parse(
|
|
11782
|
+
if (fs22.existsSync(DECISIONS_FILE)) {
|
|
11783
|
+
return JSON.parse(fs22.readFileSync(DECISIONS_FILE, "utf-8"));
|
|
11618
11784
|
}
|
|
11619
11785
|
} catch {
|
|
11620
11786
|
}
|
|
@@ -11642,7 +11808,7 @@ function estimateToolCost(tool, args) {
|
|
|
11642
11808
|
const filePath = a.file_path ?? a.path;
|
|
11643
11809
|
if (filePath) {
|
|
11644
11810
|
try {
|
|
11645
|
-
const bytes =
|
|
11811
|
+
const bytes = fs22.statSync(filePath).size;
|
|
11646
11812
|
return bytes / BYTES_PER_TOKEN / 1e6 * INPUT_PRICE_PER_1M;
|
|
11647
11813
|
} catch {
|
|
11648
11814
|
}
|
|
@@ -11713,7 +11879,7 @@ function abandonPending() {
|
|
|
11713
11879
|
});
|
|
11714
11880
|
if (autoStarted) {
|
|
11715
11881
|
try {
|
|
11716
|
-
|
|
11882
|
+
fs22.unlinkSync(DAEMON_PID_FILE);
|
|
11717
11883
|
} catch {
|
|
11718
11884
|
}
|
|
11719
11885
|
setTimeout(() => {
|
|
@@ -11724,8 +11890,8 @@ function abandonPending() {
|
|
|
11724
11890
|
}
|
|
11725
11891
|
function logActivitySocket(msg) {
|
|
11726
11892
|
try {
|
|
11727
|
-
|
|
11728
|
-
|
|
11893
|
+
fs22.appendFileSync(
|
|
11894
|
+
path24.join(homeDir, ".node9", "hook-debug.log"),
|
|
11729
11895
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] [activity-socket] ${msg}
|
|
11730
11896
|
`
|
|
11731
11897
|
);
|
|
@@ -11747,13 +11913,13 @@ function shouldRebind(now = Date.now()) {
|
|
|
11747
11913
|
function startActivitySocket() {
|
|
11748
11914
|
bindActivitySocket();
|
|
11749
11915
|
activityHealthInterval = setInterval(() => {
|
|
11750
|
-
if (!
|
|
11916
|
+
if (!fs22.existsSync(ACTIVITY_SOCKET_PATH2)) attemptRebind("health-probe");
|
|
11751
11917
|
}, ACTIVITY_HEALTH_PROBE_MS);
|
|
11752
11918
|
activityHealthInterval.unref();
|
|
11753
11919
|
process.on("exit", () => {
|
|
11754
11920
|
if (activityHealthInterval) clearInterval(activityHealthInterval);
|
|
11755
11921
|
try {
|
|
11756
|
-
|
|
11922
|
+
fs22.unlinkSync(ACTIVITY_SOCKET_PATH2);
|
|
11757
11923
|
} catch {
|
|
11758
11924
|
}
|
|
11759
11925
|
});
|
|
@@ -11781,7 +11947,7 @@ function attemptRebind(reason) {
|
|
|
11781
11947
|
}
|
|
11782
11948
|
function bindActivitySocket() {
|
|
11783
11949
|
try {
|
|
11784
|
-
|
|
11950
|
+
fs22.unlinkSync(ACTIVITY_SOCKET_PATH2);
|
|
11785
11951
|
} catch {
|
|
11786
11952
|
}
|
|
11787
11953
|
const ACTIVITY_MAX_BYTES = 1024 * 1024;
|
|
@@ -11894,14 +12060,14 @@ var init_state2 = __esm({
|
|
|
11894
12060
|
init_taint_store();
|
|
11895
12061
|
init_session_counters();
|
|
11896
12062
|
init_session_history();
|
|
11897
|
-
homeDir =
|
|
11898
|
-
DAEMON_PID_FILE =
|
|
11899
|
-
DECISIONS_FILE =
|
|
11900
|
-
AUDIT_LOG_FILE =
|
|
11901
|
-
TRUST_FILE2 =
|
|
11902
|
-
GLOBAL_CONFIG_FILE =
|
|
11903
|
-
CREDENTIALS_FILE =
|
|
11904
|
-
INSIGHT_COUNTS_FILE =
|
|
12063
|
+
homeDir = os20.homedir();
|
|
12064
|
+
DAEMON_PID_FILE = path24.join(homeDir, ".node9", "daemon.pid");
|
|
12065
|
+
DECISIONS_FILE = path24.join(homeDir, ".node9", "decisions.json");
|
|
12066
|
+
AUDIT_LOG_FILE = path24.join(homeDir, ".node9", "audit.log");
|
|
12067
|
+
TRUST_FILE2 = path24.join(homeDir, ".node9", "trust.json");
|
|
12068
|
+
GLOBAL_CONFIG_FILE = path24.join(homeDir, ".node9", "config.json");
|
|
12069
|
+
CREDENTIALS_FILE = path24.join(homeDir, ".node9", "credentials.json");
|
|
12070
|
+
INSIGHT_COUNTS_FILE = path24.join(homeDir, ".node9", "insight-counts.json");
|
|
11905
12071
|
pending = /* @__PURE__ */ new Map();
|
|
11906
12072
|
sseClients = /* @__PURE__ */ new Set();
|
|
11907
12073
|
suggestionTracker = new SuggestionTracker(3);
|
|
@@ -11918,7 +12084,7 @@ var init_state2 = __esm({
|
|
|
11918
12084
|
"2h": 2 * 60 * 6e4
|
|
11919
12085
|
};
|
|
11920
12086
|
autoStarted = process.env.NODE9_AUTO_STARTED === "1";
|
|
11921
|
-
ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" :
|
|
12087
|
+
ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path24.join(os20.tmpdir(), "node9-activity.sock");
|
|
11922
12088
|
ACTIVITY_RING_SIZE = 100;
|
|
11923
12089
|
activityRing = [];
|
|
11924
12090
|
LARGE_RESPONSE_RING_SIZE = 20;
|
|
@@ -11957,10 +12123,10 @@ var init_state2 = __esm({
|
|
|
11957
12123
|
});
|
|
11958
12124
|
|
|
11959
12125
|
// src/daemon/sync.ts
|
|
11960
|
-
import
|
|
12126
|
+
import fs23 from "fs";
|
|
11961
12127
|
import https2 from "https";
|
|
11962
|
-
import
|
|
11963
|
-
import
|
|
12128
|
+
import os21 from "os";
|
|
12129
|
+
import path25 from "path";
|
|
11964
12130
|
function emptySignals3() {
|
|
11965
12131
|
return {
|
|
11966
12132
|
dlpFindings: 0,
|
|
@@ -12000,8 +12166,8 @@ function readCredentials() {
|
|
|
12000
12166
|
};
|
|
12001
12167
|
}
|
|
12002
12168
|
try {
|
|
12003
|
-
const credPath =
|
|
12004
|
-
const creds = JSON.parse(
|
|
12169
|
+
const credPath = path25.join(os21.homedir(), ".node9", "credentials.json");
|
|
12170
|
+
const creds = JSON.parse(fs23.readFileSync(credPath, "utf-8"));
|
|
12005
12171
|
const profileName = process.env.NODE9_PROFILE ?? "default";
|
|
12006
12172
|
const profile = creds[profileName];
|
|
12007
12173
|
if (typeof profile?.apiKey === "string" && profile.apiKey.length > 0) {
|
|
@@ -12027,7 +12193,7 @@ function readCredentials() {
|
|
|
12027
12193
|
}
|
|
12028
12194
|
function readCachedEtag() {
|
|
12029
12195
|
try {
|
|
12030
|
-
const raw = JSON.parse(
|
|
12196
|
+
const raw = JSON.parse(fs23.readFileSync(rulesCacheFile(), "utf-8"));
|
|
12031
12197
|
return typeof raw.etag === "string" ? raw.etag : void 0;
|
|
12032
12198
|
} catch {
|
|
12033
12199
|
return void 0;
|
|
@@ -12088,9 +12254,9 @@ function extractRules(body) {
|
|
|
12088
12254
|
return [];
|
|
12089
12255
|
}
|
|
12090
12256
|
function writeCache2(cache) {
|
|
12091
|
-
const dir =
|
|
12092
|
-
if (!
|
|
12093
|
-
|
|
12257
|
+
const dir = path25.dirname(rulesCacheFile());
|
|
12258
|
+
if (!fs23.existsSync(dir)) fs23.mkdirSync(dir, { recursive: true });
|
|
12259
|
+
fs23.writeFileSync(rulesCacheFile(), JSON.stringify(cache, null, 2) + "\n", "utf-8");
|
|
12094
12260
|
}
|
|
12095
12261
|
async function syncOnce() {
|
|
12096
12262
|
const creds = readCredentials();
|
|
@@ -12247,7 +12413,7 @@ async function runCloudSync() {
|
|
|
12247
12413
|
}
|
|
12248
12414
|
function getCloudSyncStatus() {
|
|
12249
12415
|
try {
|
|
12250
|
-
const raw = JSON.parse(
|
|
12416
|
+
const raw = JSON.parse(fs23.readFileSync(rulesCacheFile(), "utf-8"));
|
|
12251
12417
|
if (!Array.isArray(raw.rules) || typeof raw.fetchedAt !== "string") return { cached: false };
|
|
12252
12418
|
return {
|
|
12253
12419
|
cached: true,
|
|
@@ -12264,7 +12430,7 @@ function getCloudSyncStatus() {
|
|
|
12264
12430
|
}
|
|
12265
12431
|
function getCloudRules() {
|
|
12266
12432
|
try {
|
|
12267
|
-
const raw = JSON.parse(
|
|
12433
|
+
const raw = JSON.parse(fs23.readFileSync(rulesCacheFile(), "utf-8"));
|
|
12268
12434
|
return Array.isArray(raw.rules) ? raw.rules : null;
|
|
12269
12435
|
} catch {
|
|
12270
12436
|
return null;
|
|
@@ -12320,7 +12486,7 @@ var init_sync = __esm({
|
|
|
12320
12486
|
loop: "loops",
|
|
12321
12487
|
"long-output-redacted": "longOutputRedactions"
|
|
12322
12488
|
};
|
|
12323
|
-
rulesCacheFile = () =>
|
|
12489
|
+
rulesCacheFile = () => path25.join(os21.homedir(), ".node9", "rules-cache.json");
|
|
12324
12490
|
DEFAULT_API_URL = "https://api.node9.ai/api/v1/intercept/policies/sync";
|
|
12325
12491
|
DEFAULT_INTERVAL_HOURS = 5;
|
|
12326
12492
|
MIN_INTERVAL_HOURS = 1;
|
|
@@ -12331,73 +12497,73 @@ var init_sync = __esm({
|
|
|
12331
12497
|
});
|
|
12332
12498
|
|
|
12333
12499
|
// src/daemon/dlp-scanner.ts
|
|
12334
|
-
import
|
|
12335
|
-
import
|
|
12336
|
-
import
|
|
12500
|
+
import fs24 from "fs";
|
|
12501
|
+
import path26 from "path";
|
|
12502
|
+
import os22 from "os";
|
|
12337
12503
|
function loadIndex() {
|
|
12338
12504
|
try {
|
|
12339
|
-
return JSON.parse(
|
|
12505
|
+
return JSON.parse(fs24.readFileSync(INDEX_FILE, "utf-8"));
|
|
12340
12506
|
} catch {
|
|
12341
12507
|
return {};
|
|
12342
12508
|
}
|
|
12343
12509
|
}
|
|
12344
12510
|
function saveIndex(index) {
|
|
12345
12511
|
try {
|
|
12346
|
-
|
|
12512
|
+
fs24.writeFileSync(INDEX_FILE, JSON.stringify(index), { encoding: "utf-8", mode: 384 });
|
|
12347
12513
|
} catch {
|
|
12348
12514
|
}
|
|
12349
12515
|
}
|
|
12350
12516
|
function appendAuditEntry(entry) {
|
|
12351
12517
|
try {
|
|
12352
|
-
|
|
12518
|
+
fs24.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
|
|
12353
12519
|
} catch {
|
|
12354
12520
|
}
|
|
12355
12521
|
}
|
|
12356
12522
|
function runDlpScan() {
|
|
12357
|
-
if (!
|
|
12523
|
+
if (!fs24.existsSync(PROJECTS_DIR2)) return;
|
|
12358
12524
|
const index = loadIndex();
|
|
12359
12525
|
let updated = false;
|
|
12360
12526
|
let projDirs;
|
|
12361
12527
|
try {
|
|
12362
|
-
projDirs =
|
|
12528
|
+
projDirs = fs24.readdirSync(PROJECTS_DIR2);
|
|
12363
12529
|
} catch {
|
|
12364
12530
|
return;
|
|
12365
12531
|
}
|
|
12366
12532
|
for (const proj of projDirs) {
|
|
12367
|
-
const projPath =
|
|
12533
|
+
const projPath = path26.join(PROJECTS_DIR2, proj);
|
|
12368
12534
|
try {
|
|
12369
|
-
if (!
|
|
12370
|
-
const real =
|
|
12371
|
-
if (!real.startsWith(PROJECTS_DIR2 +
|
|
12535
|
+
if (!fs24.lstatSync(projPath).isDirectory()) continue;
|
|
12536
|
+
const real = fs24.realpathSync(projPath);
|
|
12537
|
+
if (!real.startsWith(PROJECTS_DIR2 + path26.sep) && real !== PROJECTS_DIR2) continue;
|
|
12372
12538
|
} catch {
|
|
12373
12539
|
continue;
|
|
12374
12540
|
}
|
|
12375
12541
|
let files;
|
|
12376
12542
|
try {
|
|
12377
|
-
files =
|
|
12543
|
+
files = fs24.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
12378
12544
|
} catch {
|
|
12379
12545
|
continue;
|
|
12380
12546
|
}
|
|
12381
12547
|
for (const file of files) {
|
|
12382
|
-
const filePath =
|
|
12548
|
+
const filePath = path26.join(projPath, file);
|
|
12383
12549
|
const lastOffset = index[filePath] ?? 0;
|
|
12384
12550
|
let size;
|
|
12385
12551
|
try {
|
|
12386
|
-
size =
|
|
12552
|
+
size = fs24.statSync(filePath).size;
|
|
12387
12553
|
} catch {
|
|
12388
12554
|
continue;
|
|
12389
12555
|
}
|
|
12390
12556
|
if (size <= lastOffset) continue;
|
|
12391
12557
|
let fd;
|
|
12392
12558
|
try {
|
|
12393
|
-
fd =
|
|
12559
|
+
fd = fs24.openSync(filePath, "r");
|
|
12394
12560
|
} catch {
|
|
12395
12561
|
continue;
|
|
12396
12562
|
}
|
|
12397
12563
|
try {
|
|
12398
12564
|
const chunkSize = size - lastOffset;
|
|
12399
12565
|
const buf = Buffer.alloc(chunkSize);
|
|
12400
|
-
|
|
12566
|
+
fs24.readSync(fd, buf, 0, chunkSize, lastOffset);
|
|
12401
12567
|
const chunk = buf.toString("utf-8");
|
|
12402
12568
|
for (const line of chunk.split("\n")) {
|
|
12403
12569
|
if (!line.trim()) continue;
|
|
@@ -12417,7 +12583,7 @@ function runDlpScan() {
|
|
|
12417
12583
|
if (typeof text !== "string") continue;
|
|
12418
12584
|
const match = scanText(text);
|
|
12419
12585
|
if (!match) continue;
|
|
12420
|
-
const projLabel = decodeURIComponent(proj).replace(
|
|
12586
|
+
const projLabel = decodeURIComponent(proj).replace(os22.homedir(), "~").slice(0, 40);
|
|
12421
12587
|
const ts = entry.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
12422
12588
|
appendAuditEntry({
|
|
12423
12589
|
ts,
|
|
@@ -12442,7 +12608,7 @@ Run: node9 report --period 30d`
|
|
|
12442
12608
|
updated = true;
|
|
12443
12609
|
} finally {
|
|
12444
12610
|
try {
|
|
12445
|
-
|
|
12611
|
+
fs24.closeSync(fd);
|
|
12446
12612
|
} catch {
|
|
12447
12613
|
}
|
|
12448
12614
|
}
|
|
@@ -12475,23 +12641,23 @@ var init_dlp_scanner = __esm({
|
|
|
12475
12641
|
init_dlp();
|
|
12476
12642
|
init_native();
|
|
12477
12643
|
init_state2();
|
|
12478
|
-
INDEX_FILE =
|
|
12479
|
-
PROJECTS_DIR2 =
|
|
12644
|
+
INDEX_FILE = path26.join(os22.homedir(), ".node9", "dlp-index.json");
|
|
12645
|
+
PROJECTS_DIR2 = path26.join(os22.homedir(), ".claude", "projects");
|
|
12480
12646
|
}
|
|
12481
12647
|
});
|
|
12482
12648
|
|
|
12483
12649
|
// src/daemon/mcp-tools.ts
|
|
12484
|
-
import
|
|
12485
|
-
import
|
|
12486
|
-
import
|
|
12650
|
+
import fs25 from "fs";
|
|
12651
|
+
import path27 from "path";
|
|
12652
|
+
import os23 from "os";
|
|
12487
12653
|
function getMcpToolsFile() {
|
|
12488
|
-
return
|
|
12654
|
+
return path27.join(os23.homedir(), ".node9", "mcp-tools.json");
|
|
12489
12655
|
}
|
|
12490
12656
|
function readMcpToolsConfig() {
|
|
12491
12657
|
try {
|
|
12492
12658
|
const file = getMcpToolsFile();
|
|
12493
|
-
if (!
|
|
12494
|
-
const raw =
|
|
12659
|
+
if (!fs25.existsSync(file)) return {};
|
|
12660
|
+
const raw = fs25.readFileSync(file, "utf-8");
|
|
12495
12661
|
return JSON.parse(raw);
|
|
12496
12662
|
} catch {
|
|
12497
12663
|
return {};
|
|
@@ -12500,11 +12666,11 @@ function readMcpToolsConfig() {
|
|
|
12500
12666
|
function writeMcpToolsConfig(config) {
|
|
12501
12667
|
try {
|
|
12502
12668
|
const file = getMcpToolsFile();
|
|
12503
|
-
const dir =
|
|
12504
|
-
if (!
|
|
12505
|
-
const tmpPath = `${file}.${
|
|
12506
|
-
|
|
12507
|
-
|
|
12669
|
+
const dir = path27.dirname(file);
|
|
12670
|
+
if (!fs25.existsSync(dir)) fs25.mkdirSync(dir, { recursive: true });
|
|
12671
|
+
const tmpPath = `${file}.${os23.hostname()}.${process.pid}.tmp`;
|
|
12672
|
+
fs25.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
|
|
12673
|
+
fs25.renameSync(tmpPath, file);
|
|
12508
12674
|
} catch (e) {
|
|
12509
12675
|
console.error("Failed to write mcp-tools.json", e);
|
|
12510
12676
|
}
|
|
@@ -12551,9 +12717,9 @@ var init_mcp_tools = __esm({
|
|
|
12551
12717
|
|
|
12552
12718
|
// src/daemon/server.ts
|
|
12553
12719
|
import http from "http";
|
|
12554
|
-
import
|
|
12555
|
-
import
|
|
12556
|
-
import
|
|
12720
|
+
import fs26 from "fs";
|
|
12721
|
+
import path28 from "path";
|
|
12722
|
+
import os24 from "os";
|
|
12557
12723
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
12558
12724
|
import { spawnSync } from "child_process";
|
|
12559
12725
|
import chalk6 from "chalk";
|
|
@@ -12574,7 +12740,7 @@ function startDaemon() {
|
|
|
12574
12740
|
idleTimer = setTimeout(() => {
|
|
12575
12741
|
if (autoStarted) {
|
|
12576
12742
|
try {
|
|
12577
|
-
|
|
12743
|
+
fs26.unlinkSync(DAEMON_PID_FILE);
|
|
12578
12744
|
} catch {
|
|
12579
12745
|
}
|
|
12580
12746
|
}
|
|
@@ -12719,7 +12885,7 @@ data: ${JSON.stringify(item.data)}
|
|
|
12719
12885
|
mcpServer: entry.mcpServer
|
|
12720
12886
|
});
|
|
12721
12887
|
}
|
|
12722
|
-
const projectCwd = typeof cwd === "string" &&
|
|
12888
|
+
const projectCwd = typeof cwd === "string" && path28.isAbsolute(cwd) ? cwd : void 0;
|
|
12723
12889
|
const projectConfig = getConfig(projectCwd);
|
|
12724
12890
|
const browserEnabled = projectConfig.settings.approvers?.browser !== false;
|
|
12725
12891
|
const terminalEnabled = projectConfig.settings.approvers?.terminal !== false;
|
|
@@ -13011,8 +13177,8 @@ data: ${JSON.stringify(item.data)}
|
|
|
13011
13177
|
if (!validToken(req)) return res.writeHead(403).end();
|
|
13012
13178
|
const periodParam = reqUrl.searchParams.get("period") || "7d";
|
|
13013
13179
|
const period = ["today", "7d", "30d", "month"].includes(periodParam) ? periodParam : "7d";
|
|
13014
|
-
const logPath =
|
|
13015
|
-
if (!
|
|
13180
|
+
const logPath = path28.join(os24.homedir(), ".node9", "audit.log");
|
|
13181
|
+
if (!fs26.existsSync(logPath)) {
|
|
13016
13182
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
13017
13183
|
return res.end(
|
|
13018
13184
|
JSON.stringify({
|
|
@@ -13025,7 +13191,7 @@ data: ${JSON.stringify(item.data)}
|
|
|
13025
13191
|
);
|
|
13026
13192
|
}
|
|
13027
13193
|
try {
|
|
13028
|
-
const raw =
|
|
13194
|
+
const raw = fs26.readFileSync(logPath, "utf-8");
|
|
13029
13195
|
const allEntries = raw.split("\n").flatMap((line) => {
|
|
13030
13196
|
if (!line.trim()) return [];
|
|
13031
13197
|
try {
|
|
@@ -13348,14 +13514,14 @@ data: ${JSON.stringify(item.data)}
|
|
|
13348
13514
|
server.on("error", (e) => {
|
|
13349
13515
|
if (e.code === "EADDRINUSE") {
|
|
13350
13516
|
try {
|
|
13351
|
-
if (
|
|
13352
|
-
const { pid } = JSON.parse(
|
|
13517
|
+
if (fs26.existsSync(DAEMON_PID_FILE)) {
|
|
13518
|
+
const { pid } = JSON.parse(fs26.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
13353
13519
|
process.kill(pid, 0);
|
|
13354
13520
|
return process.exit(0);
|
|
13355
13521
|
}
|
|
13356
13522
|
} catch {
|
|
13357
13523
|
try {
|
|
13358
|
-
|
|
13524
|
+
fs26.unlinkSync(DAEMON_PID_FILE);
|
|
13359
13525
|
} catch {
|
|
13360
13526
|
}
|
|
13361
13527
|
server.listen(DAEMON_PORT, DAEMON_HOST);
|
|
@@ -13443,15 +13609,15 @@ var init_server = __esm({
|
|
|
13443
13609
|
});
|
|
13444
13610
|
|
|
13445
13611
|
// src/daemon/service.ts
|
|
13446
|
-
import
|
|
13447
|
-
import
|
|
13448
|
-
import
|
|
13612
|
+
import fs27 from "fs";
|
|
13613
|
+
import path29 from "path";
|
|
13614
|
+
import os25 from "os";
|
|
13449
13615
|
import { spawnSync as spawnSync2, execFileSync } from "child_process";
|
|
13450
13616
|
function resolveNode9Binary() {
|
|
13451
13617
|
try {
|
|
13452
13618
|
const script = process.argv[1];
|
|
13453
|
-
if (typeof script === "string" &&
|
|
13454
|
-
return
|
|
13619
|
+
if (typeof script === "string" && path29.isAbsolute(script) && fs27.existsSync(script)) {
|
|
13620
|
+
return fs27.realpathSync(script);
|
|
13455
13621
|
}
|
|
13456
13622
|
} catch {
|
|
13457
13623
|
}
|
|
@@ -13469,11 +13635,11 @@ function xmlEscape(s) {
|
|
|
13469
13635
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
13470
13636
|
}
|
|
13471
13637
|
function launchdPlist(binaryPath) {
|
|
13472
|
-
const logDir =
|
|
13638
|
+
const logDir = path29.join(os25.homedir(), ".node9");
|
|
13473
13639
|
const nodePath = xmlEscape(process.execPath);
|
|
13474
13640
|
const scriptPath = xmlEscape(binaryPath);
|
|
13475
|
-
const outLog = xmlEscape(
|
|
13476
|
-
const errLog = xmlEscape(
|
|
13641
|
+
const outLog = xmlEscape(path29.join(logDir, "daemon.log"));
|
|
13642
|
+
const errLog = xmlEscape(path29.join(logDir, "daemon-error.log"));
|
|
13477
13643
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
13478
13644
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
13479
13645
|
<plist version="1.0">
|
|
@@ -13506,9 +13672,9 @@ function launchdPlist(binaryPath) {
|
|
|
13506
13672
|
`;
|
|
13507
13673
|
}
|
|
13508
13674
|
function installLaunchd(binaryPath) {
|
|
13509
|
-
const dir =
|
|
13510
|
-
if (!
|
|
13511
|
-
|
|
13675
|
+
const dir = path29.dirname(LAUNCHD_PLIST);
|
|
13676
|
+
if (!fs27.existsSync(dir)) fs27.mkdirSync(dir, { recursive: true });
|
|
13677
|
+
fs27.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
|
|
13512
13678
|
spawnSync2("launchctl", ["unload", LAUNCHD_PLIST], { encoding: "utf8" });
|
|
13513
13679
|
const r = spawnSync2("launchctl", ["load", "-w", LAUNCHD_PLIST], {
|
|
13514
13680
|
encoding: "utf8",
|
|
@@ -13519,13 +13685,13 @@ function installLaunchd(binaryPath) {
|
|
|
13519
13685
|
}
|
|
13520
13686
|
}
|
|
13521
13687
|
function uninstallLaunchd() {
|
|
13522
|
-
if (
|
|
13688
|
+
if (fs27.existsSync(LAUNCHD_PLIST)) {
|
|
13523
13689
|
spawnSync2("launchctl", ["unload", "-w", LAUNCHD_PLIST], { encoding: "utf8", timeout: 5e3 });
|
|
13524
|
-
|
|
13690
|
+
fs27.unlinkSync(LAUNCHD_PLIST);
|
|
13525
13691
|
}
|
|
13526
13692
|
}
|
|
13527
13693
|
function isLaunchdInstalled() {
|
|
13528
|
-
return
|
|
13694
|
+
return fs27.existsSync(LAUNCHD_PLIST);
|
|
13529
13695
|
}
|
|
13530
13696
|
function systemdUnit(binaryPath) {
|
|
13531
13697
|
return `[Unit]
|
|
@@ -13544,12 +13710,12 @@ WantedBy=default.target
|
|
|
13544
13710
|
`;
|
|
13545
13711
|
}
|
|
13546
13712
|
function installSystemd(binaryPath) {
|
|
13547
|
-
if (!
|
|
13548
|
-
|
|
13713
|
+
if (!fs27.existsSync(SYSTEMD_UNIT_DIR)) {
|
|
13714
|
+
fs27.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
|
|
13549
13715
|
}
|
|
13550
|
-
|
|
13716
|
+
fs27.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
|
|
13551
13717
|
try {
|
|
13552
|
-
execFileSync("loginctl", ["enable-linger",
|
|
13718
|
+
execFileSync("loginctl", ["enable-linger", os25.userInfo().username], { timeout: 3e3 });
|
|
13553
13719
|
} catch {
|
|
13554
13720
|
}
|
|
13555
13721
|
const reload = spawnSync2("systemctl", ["--user", "daemon-reload"], {
|
|
@@ -13569,23 +13735,23 @@ function installSystemd(binaryPath) {
|
|
|
13569
13735
|
}
|
|
13570
13736
|
}
|
|
13571
13737
|
function uninstallSystemd() {
|
|
13572
|
-
if (
|
|
13738
|
+
if (fs27.existsSync(SYSTEMD_UNIT)) {
|
|
13573
13739
|
spawnSync2("systemctl", ["--user", "disable", "--now", "node9-daemon"], {
|
|
13574
13740
|
encoding: "utf8",
|
|
13575
13741
|
timeout: 5e3
|
|
13576
13742
|
});
|
|
13577
13743
|
spawnSync2("systemctl", ["--user", "daemon-reload"], { encoding: "utf8", timeout: 5e3 });
|
|
13578
|
-
|
|
13744
|
+
fs27.unlinkSync(SYSTEMD_UNIT);
|
|
13579
13745
|
}
|
|
13580
13746
|
}
|
|
13581
13747
|
function isSystemdInstalled() {
|
|
13582
|
-
return
|
|
13748
|
+
return fs27.existsSync(SYSTEMD_UNIT);
|
|
13583
13749
|
}
|
|
13584
13750
|
function stopRunningDaemon() {
|
|
13585
|
-
const pidFile =
|
|
13586
|
-
if (!
|
|
13751
|
+
const pidFile = path29.join(os25.homedir(), ".node9", "daemon.pid");
|
|
13752
|
+
if (!fs27.existsSync(pidFile)) return;
|
|
13587
13753
|
try {
|
|
13588
|
-
const data = JSON.parse(
|
|
13754
|
+
const data = JSON.parse(fs27.readFileSync(pidFile, "utf-8"));
|
|
13589
13755
|
const pid = data.pid;
|
|
13590
13756
|
const MAX_PID2 = 4194304;
|
|
13591
13757
|
if (typeof pid === "number" && Number.isInteger(pid) && pid > 0 && pid <= MAX_PID2) {
|
|
@@ -13605,7 +13771,7 @@ function stopRunningDaemon() {
|
|
|
13605
13771
|
}
|
|
13606
13772
|
}
|
|
13607
13773
|
try {
|
|
13608
|
-
|
|
13774
|
+
fs27.unlinkSync(pidFile);
|
|
13609
13775
|
} catch {
|
|
13610
13776
|
}
|
|
13611
13777
|
} catch {
|
|
@@ -13680,19 +13846,19 @@ var init_service = __esm({
|
|
|
13680
13846
|
"src/daemon/service.ts"() {
|
|
13681
13847
|
"use strict";
|
|
13682
13848
|
LAUNCHD_LABEL = "ai.node9.daemon";
|
|
13683
|
-
LAUNCHD_PLIST =
|
|
13684
|
-
SYSTEMD_UNIT_DIR =
|
|
13685
|
-
SYSTEMD_UNIT =
|
|
13849
|
+
LAUNCHD_PLIST = path29.join(os25.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
|
|
13850
|
+
SYSTEMD_UNIT_DIR = path29.join(os25.homedir(), ".config", "systemd", "user");
|
|
13851
|
+
SYSTEMD_UNIT = path29.join(SYSTEMD_UNIT_DIR, "node9-daemon.service");
|
|
13686
13852
|
}
|
|
13687
13853
|
});
|
|
13688
13854
|
|
|
13689
13855
|
// src/daemon/index.ts
|
|
13690
|
-
import
|
|
13856
|
+
import fs28 from "fs";
|
|
13691
13857
|
import chalk7 from "chalk";
|
|
13692
13858
|
function stopDaemon() {
|
|
13693
|
-
if (!
|
|
13859
|
+
if (!fs28.existsSync(DAEMON_PID_FILE)) return console.log(chalk7.yellow("Not running."));
|
|
13694
13860
|
try {
|
|
13695
|
-
const data = JSON.parse(
|
|
13861
|
+
const data = JSON.parse(fs28.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
13696
13862
|
const pid = data.pid;
|
|
13697
13863
|
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
|
|
13698
13864
|
console.log(chalk7.gray("Cleaned up invalid PID file."));
|
|
@@ -13704,7 +13870,7 @@ function stopDaemon() {
|
|
|
13704
13870
|
console.log(chalk7.gray("Cleaned up stale PID file."));
|
|
13705
13871
|
} finally {
|
|
13706
13872
|
try {
|
|
13707
|
-
|
|
13873
|
+
fs28.unlinkSync(DAEMON_PID_FILE);
|
|
13708
13874
|
} catch {
|
|
13709
13875
|
}
|
|
13710
13876
|
}
|
|
@@ -13713,9 +13879,9 @@ function daemonStatus() {
|
|
|
13713
13879
|
const serviceInstalled = isDaemonServiceInstalled();
|
|
13714
13880
|
const serviceLabel = serviceInstalled ? chalk7.green("installed (starts on login)") : chalk7.yellow("not installed \u2014 run: node9 daemon install");
|
|
13715
13881
|
let processStatus;
|
|
13716
|
-
if (
|
|
13882
|
+
if (fs28.existsSync(DAEMON_PID_FILE)) {
|
|
13717
13883
|
try {
|
|
13718
|
-
const data = JSON.parse(
|
|
13884
|
+
const data = JSON.parse(fs28.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
13719
13885
|
const pid = data.pid;
|
|
13720
13886
|
const port = data.port;
|
|
13721
13887
|
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
|
|
@@ -13760,7 +13926,7 @@ __export(tail_exports, {
|
|
|
13760
13926
|
});
|
|
13761
13927
|
import http2 from "http";
|
|
13762
13928
|
import chalk29 from "chalk";
|
|
13763
|
-
import
|
|
13929
|
+
import fs46 from "fs";
|
|
13764
13930
|
import os41 from "os";
|
|
13765
13931
|
import path47 from "path";
|
|
13766
13932
|
import readline6 from "readline";
|
|
@@ -13787,19 +13953,19 @@ function getModelContextLimit(model) {
|
|
|
13787
13953
|
}
|
|
13788
13954
|
function readSessionUsage() {
|
|
13789
13955
|
const projectsDir = path47.join(os41.homedir(), ".claude", "projects");
|
|
13790
|
-
if (!
|
|
13956
|
+
if (!fs46.existsSync(projectsDir)) return null;
|
|
13791
13957
|
let latestFile = null;
|
|
13792
13958
|
let latestMtime = 0;
|
|
13793
13959
|
try {
|
|
13794
|
-
for (const dir of
|
|
13960
|
+
for (const dir of fs46.readdirSync(projectsDir)) {
|
|
13795
13961
|
const dirPath = path47.join(projectsDir, dir);
|
|
13796
13962
|
try {
|
|
13797
|
-
if (!
|
|
13798
|
-
for (const file of
|
|
13963
|
+
if (!fs46.statSync(dirPath).isDirectory()) continue;
|
|
13964
|
+
for (const file of fs46.readdirSync(dirPath)) {
|
|
13799
13965
|
if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
|
|
13800
13966
|
const filePath = path47.join(dirPath, file);
|
|
13801
13967
|
try {
|
|
13802
|
-
const mtime =
|
|
13968
|
+
const mtime = fs46.statSync(filePath).mtimeMs;
|
|
13803
13969
|
if (mtime > latestMtime) {
|
|
13804
13970
|
latestMtime = mtime;
|
|
13805
13971
|
latestFile = filePath;
|
|
@@ -13814,7 +13980,7 @@ function readSessionUsage() {
|
|
|
13814
13980
|
}
|
|
13815
13981
|
if (!latestFile) return null;
|
|
13816
13982
|
try {
|
|
13817
|
-
const lines =
|
|
13983
|
+
const lines = fs46.readFileSync(latestFile, "utf-8").split("\n");
|
|
13818
13984
|
let lastModel = "";
|
|
13819
13985
|
let lastInput = 0;
|
|
13820
13986
|
let lastOutput = 0;
|
|
@@ -13914,9 +14080,9 @@ function renderPending(activity) {
|
|
|
13914
14080
|
}
|
|
13915
14081
|
async function ensureDaemon() {
|
|
13916
14082
|
let pidPort = null;
|
|
13917
|
-
if (
|
|
14083
|
+
if (fs46.existsSync(PID_FILE)) {
|
|
13918
14084
|
try {
|
|
13919
|
-
const { port } = JSON.parse(
|
|
14085
|
+
const { port } = JSON.parse(fs46.readFileSync(PID_FILE, "utf-8"));
|
|
13920
14086
|
pidPort = port;
|
|
13921
14087
|
} catch {
|
|
13922
14088
|
console.error(chalk29.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
|
|
@@ -14074,7 +14240,7 @@ function buildRecoveryCardLines(req) {
|
|
|
14074
14240
|
function readApproversFromDisk() {
|
|
14075
14241
|
const configPath = path47.join(os41.homedir(), ".node9", "config.json");
|
|
14076
14242
|
try {
|
|
14077
|
-
const raw = JSON.parse(
|
|
14243
|
+
const raw = JSON.parse(fs46.readFileSync(configPath, "utf-8"));
|
|
14078
14244
|
const settings = raw.settings ?? {};
|
|
14079
14245
|
return settings.approvers ?? {};
|
|
14080
14246
|
} catch {
|
|
@@ -14092,13 +14258,13 @@ function approverStatusLine() {
|
|
|
14092
14258
|
function toggleApprover(channel) {
|
|
14093
14259
|
const configPath = path47.join(os41.homedir(), ".node9", "config.json");
|
|
14094
14260
|
try {
|
|
14095
|
-
const raw = JSON.parse(
|
|
14261
|
+
const raw = JSON.parse(fs46.readFileSync(configPath, "utf-8"));
|
|
14096
14262
|
const settings = raw.settings ?? {};
|
|
14097
14263
|
const approvers = settings.approvers ?? {};
|
|
14098
14264
|
approvers[channel] = approvers[channel] === false;
|
|
14099
14265
|
settings.approvers = approvers;
|
|
14100
14266
|
raw.settings = settings;
|
|
14101
|
-
|
|
14267
|
+
fs46.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
|
|
14102
14268
|
} catch (err2) {
|
|
14103
14269
|
process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
|
|
14104
14270
|
`);
|
|
@@ -14270,7 +14436,7 @@ async function startTail(options = {}) {
|
|
|
14270
14436
|
}
|
|
14271
14437
|
postDecisionHttp(req2.id, httpDecision, authToken, port, httpOpts).catch((err2) => {
|
|
14272
14438
|
try {
|
|
14273
|
-
|
|
14439
|
+
fs46.appendFileSync(
|
|
14274
14440
|
path47.join(os41.homedir(), ".node9", "hook-debug.log"),
|
|
14275
14441
|
`[tail] POST /decision failed: ${String(err2)}
|
|
14276
14442
|
`
|
|
@@ -14337,7 +14503,7 @@ async function startTail(options = {}) {
|
|
|
14337
14503
|
}
|
|
14338
14504
|
const auditLog = path47.join(os41.homedir(), ".node9", "audit.log");
|
|
14339
14505
|
try {
|
|
14340
|
-
const unackedDlp =
|
|
14506
|
+
const unackedDlp = fs46.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
|
|
14341
14507
|
if (unackedDlp > 0) {
|
|
14342
14508
|
console.log("");
|
|
14343
14509
|
console.log(
|
|
@@ -14377,7 +14543,7 @@ async function startTail(options = {}) {
|
|
|
14377
14543
|
if (stallWarned) return;
|
|
14378
14544
|
if (Date.now() - lastActivityFromDaemon < STALL_THRESHOLD_MS) return;
|
|
14379
14545
|
try {
|
|
14380
|
-
const auditMtime =
|
|
14546
|
+
const auditMtime = fs46.statSync(auditLog).mtimeMs;
|
|
14381
14547
|
if (Date.now() - auditMtime >= STALL_THRESHOLD_MS) return;
|
|
14382
14548
|
console.log("");
|
|
14383
14549
|
console.log(
|
|
@@ -14616,7 +14782,7 @@ __export(hud_exports, {
|
|
|
14616
14782
|
main: () => main,
|
|
14617
14783
|
renderEnvironmentLine: () => renderEnvironmentLine
|
|
14618
14784
|
});
|
|
14619
|
-
import
|
|
14785
|
+
import fs47 from "fs";
|
|
14620
14786
|
import path48 from "path";
|
|
14621
14787
|
import os42 from "os";
|
|
14622
14788
|
import http3 from "http";
|
|
@@ -14694,9 +14860,9 @@ function formatTimeLeft(resetsAt) {
|
|
|
14694
14860
|
return ` (${m}m left)`;
|
|
14695
14861
|
}
|
|
14696
14862
|
function safeReadJson(filePath) {
|
|
14697
|
-
if (!
|
|
14863
|
+
if (!fs47.existsSync(filePath)) return null;
|
|
14698
14864
|
try {
|
|
14699
|
-
return JSON.parse(
|
|
14865
|
+
return JSON.parse(fs47.readFileSync(filePath, "utf-8"));
|
|
14700
14866
|
} catch {
|
|
14701
14867
|
return null;
|
|
14702
14868
|
}
|
|
@@ -14717,10 +14883,10 @@ function countHooksInFile(filePath) {
|
|
|
14717
14883
|
return Object.keys(cfg.hooks).length;
|
|
14718
14884
|
}
|
|
14719
14885
|
function countRulesInDir(rulesDir) {
|
|
14720
|
-
if (!
|
|
14886
|
+
if (!fs47.existsSync(rulesDir)) return 0;
|
|
14721
14887
|
let count = 0;
|
|
14722
14888
|
try {
|
|
14723
|
-
for (const entry of
|
|
14889
|
+
for (const entry of fs47.readdirSync(rulesDir, { withFileTypes: true })) {
|
|
14724
14890
|
if (entry.isDirectory()) {
|
|
14725
14891
|
count += countRulesInDir(path48.join(rulesDir, entry.name));
|
|
14726
14892
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -14746,7 +14912,7 @@ function countConfigs(cwd) {
|
|
|
14746
14912
|
let hooksCount = 0;
|
|
14747
14913
|
const userMcpServers = /* @__PURE__ */ new Set();
|
|
14748
14914
|
const projectMcpServers = /* @__PURE__ */ new Set();
|
|
14749
|
-
if (
|
|
14915
|
+
if (fs47.existsSync(path48.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
14750
14916
|
rulesCount += countRulesInDir(path48.join(claudeDir, "rules"));
|
|
14751
14917
|
const userSettings = path48.join(claudeDir, "settings.json");
|
|
14752
14918
|
for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
|
|
@@ -14757,18 +14923,18 @@ function countConfigs(cwd) {
|
|
|
14757
14923
|
userMcpServers.delete(name);
|
|
14758
14924
|
}
|
|
14759
14925
|
if (cwd) {
|
|
14760
|
-
if (
|
|
14761
|
-
if (
|
|
14926
|
+
if (fs47.existsSync(path48.join(cwd, "CLAUDE.md"))) claudeMdCount++;
|
|
14927
|
+
if (fs47.existsSync(path48.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
|
|
14762
14928
|
const projectClaudeDir = path48.join(cwd, ".claude");
|
|
14763
14929
|
const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
|
|
14764
14930
|
if (!overlapsUserScope) {
|
|
14765
|
-
if (
|
|
14931
|
+
if (fs47.existsSync(path48.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
14766
14932
|
rulesCount += countRulesInDir(path48.join(projectClaudeDir, "rules"));
|
|
14767
14933
|
const projSettings = path48.join(projectClaudeDir, "settings.json");
|
|
14768
14934
|
for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
|
|
14769
14935
|
hooksCount += countHooksInFile(projSettings);
|
|
14770
14936
|
}
|
|
14771
|
-
if (
|
|
14937
|
+
if (fs47.existsSync(path48.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
|
|
14772
14938
|
const localSettings = path48.join(projectClaudeDir, "settings.local.json");
|
|
14773
14939
|
for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
|
|
14774
14940
|
hooksCount += countHooksInFile(localSettings);
|
|
@@ -14806,11 +14972,11 @@ function readActiveShieldsHud() {
|
|
|
14806
14972
|
}
|
|
14807
14973
|
try {
|
|
14808
14974
|
const shieldsPath = path48.join(os42.homedir(), ".node9", "shields.json");
|
|
14809
|
-
if (!
|
|
14975
|
+
if (!fs47.existsSync(shieldsPath)) {
|
|
14810
14976
|
shieldsCache = { value: [], ts: now };
|
|
14811
14977
|
return [];
|
|
14812
14978
|
}
|
|
14813
|
-
const parsed = JSON.parse(
|
|
14979
|
+
const parsed = JSON.parse(fs47.readFileSync(shieldsPath, "utf-8"));
|
|
14814
14980
|
if (!Array.isArray(parsed.active)) {
|
|
14815
14981
|
shieldsCache = { value: [], ts: now };
|
|
14816
14982
|
return [];
|
|
@@ -14912,17 +15078,17 @@ function renderContextLine(stdin) {
|
|
|
14912
15078
|
async function main() {
|
|
14913
15079
|
try {
|
|
14914
15080
|
const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
|
|
14915
|
-
if (
|
|
15081
|
+
if (fs47.existsSync(path48.join(os42.homedir(), ".node9", "hud-debug"))) {
|
|
14916
15082
|
try {
|
|
14917
15083
|
const logPath = path48.join(os42.homedir(), ".node9", "hud-debug.log");
|
|
14918
15084
|
const MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
14919
15085
|
let size = 0;
|
|
14920
15086
|
try {
|
|
14921
|
-
size =
|
|
15087
|
+
size = fs47.statSync(logPath).size;
|
|
14922
15088
|
} catch {
|
|
14923
15089
|
}
|
|
14924
15090
|
if (size < MAX_LOG_SIZE) {
|
|
14925
|
-
|
|
15091
|
+
fs47.appendFileSync(
|
|
14926
15092
|
logPath,
|
|
14927
15093
|
JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
|
|
14928
15094
|
);
|
|
@@ -14946,8 +15112,8 @@ async function main() {
|
|
|
14946
15112
|
path48.join(cwd, "node9.config.json"),
|
|
14947
15113
|
path48.join(os42.homedir(), ".node9", "config.json")
|
|
14948
15114
|
]) {
|
|
14949
|
-
if (!
|
|
14950
|
-
const cfg = JSON.parse(
|
|
15115
|
+
if (!fs47.existsSync(configPath)) continue;
|
|
15116
|
+
const cfg = JSON.parse(fs47.readFileSync(configPath, "utf-8"));
|
|
14951
15117
|
const hud = cfg.settings?.hud;
|
|
14952
15118
|
if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
|
|
14953
15119
|
}
|
|
@@ -14994,7 +15160,7 @@ init_setup();
|
|
|
14994
15160
|
init_daemon2();
|
|
14995
15161
|
import { Command } from "commander";
|
|
14996
15162
|
import chalk30 from "chalk";
|
|
14997
|
-
import
|
|
15163
|
+
import fs48 from "fs";
|
|
14998
15164
|
import path49 from "path";
|
|
14999
15165
|
import os43 from "os";
|
|
15000
15166
|
import { confirm as confirm2 } from "@inquirer/prompts";
|
|
@@ -15179,17 +15345,17 @@ async function runProxy(targetCommand) {
|
|
|
15179
15345
|
// src/cli/daemon-starter.ts
|
|
15180
15346
|
init_daemon();
|
|
15181
15347
|
import { spawn as spawn3 } from "child_process";
|
|
15182
|
-
import
|
|
15183
|
-
import
|
|
15348
|
+
import path30 from "path";
|
|
15349
|
+
import fs29 from "fs";
|
|
15184
15350
|
function isTestingMode() {
|
|
15185
15351
|
return /^(1|true|yes)$/i.test(process.env.NODE9_TESTING ?? "");
|
|
15186
15352
|
}
|
|
15187
15353
|
async function autoStartDaemonAndWait() {
|
|
15188
15354
|
if (isTestingMode()) return false;
|
|
15189
|
-
if (!
|
|
15355
|
+
if (!path30.isAbsolute(process.argv[1])) return false;
|
|
15190
15356
|
let resolvedArgv1;
|
|
15191
15357
|
try {
|
|
15192
|
-
resolvedArgv1 =
|
|
15358
|
+
resolvedArgv1 = fs29.realpathSync(process.argv[1]);
|
|
15193
15359
|
} catch {
|
|
15194
15360
|
return false;
|
|
15195
15361
|
}
|
|
@@ -15220,19 +15386,19 @@ init_daemon();
|
|
|
15220
15386
|
init_config();
|
|
15221
15387
|
init_policy();
|
|
15222
15388
|
import chalk9 from "chalk";
|
|
15223
|
-
import
|
|
15389
|
+
import fs32 from "fs";
|
|
15224
15390
|
import { spawn as spawn5 } from "child_process";
|
|
15225
|
-
import
|
|
15226
|
-
import
|
|
15391
|
+
import path33 from "path";
|
|
15392
|
+
import os28 from "os";
|
|
15227
15393
|
|
|
15228
15394
|
// src/undo.ts
|
|
15229
15395
|
import { spawnSync as spawnSync3, spawn as spawn4 } from "child_process";
|
|
15230
|
-
import
|
|
15231
|
-
import
|
|
15396
|
+
import crypto4 from "crypto";
|
|
15397
|
+
import fs30 from "fs";
|
|
15232
15398
|
import net3 from "net";
|
|
15233
|
-
import
|
|
15234
|
-
import
|
|
15235
|
-
var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" :
|
|
15399
|
+
import path31 from "path";
|
|
15400
|
+
import os26 from "os";
|
|
15401
|
+
var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path31.join(os26.tmpdir(), "node9-activity.sock");
|
|
15236
15402
|
function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
|
|
15237
15403
|
try {
|
|
15238
15404
|
const payload = JSON.stringify({
|
|
@@ -15252,22 +15418,22 @@ function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
|
|
|
15252
15418
|
} catch {
|
|
15253
15419
|
}
|
|
15254
15420
|
}
|
|
15255
|
-
var SNAPSHOT_STACK_PATH =
|
|
15256
|
-
var UNDO_LATEST_PATH =
|
|
15421
|
+
var SNAPSHOT_STACK_PATH = path31.join(os26.homedir(), ".node9", "snapshots.json");
|
|
15422
|
+
var UNDO_LATEST_PATH = path31.join(os26.homedir(), ".node9", "undo_latest.txt");
|
|
15257
15423
|
var MAX_SNAPSHOTS = 10;
|
|
15258
15424
|
var GIT_TIMEOUT = 15e3;
|
|
15259
15425
|
function readStack() {
|
|
15260
15426
|
try {
|
|
15261
|
-
if (
|
|
15262
|
-
return JSON.parse(
|
|
15427
|
+
if (fs30.existsSync(SNAPSHOT_STACK_PATH))
|
|
15428
|
+
return JSON.parse(fs30.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
|
|
15263
15429
|
} catch {
|
|
15264
15430
|
}
|
|
15265
15431
|
return [];
|
|
15266
15432
|
}
|
|
15267
15433
|
function writeStack(stack) {
|
|
15268
|
-
const dir =
|
|
15269
|
-
if (!
|
|
15270
|
-
|
|
15434
|
+
const dir = path31.dirname(SNAPSHOT_STACK_PATH);
|
|
15435
|
+
if (!fs30.existsSync(dir)) fs30.mkdirSync(dir, { recursive: true });
|
|
15436
|
+
fs30.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
|
|
15271
15437
|
}
|
|
15272
15438
|
function extractFilePath(args) {
|
|
15273
15439
|
if (!args || typeof args !== "object") return null;
|
|
@@ -15287,12 +15453,12 @@ function buildArgsSummary(tool, args) {
|
|
|
15287
15453
|
return "";
|
|
15288
15454
|
}
|
|
15289
15455
|
function findProjectRoot(filePath) {
|
|
15290
|
-
let dir =
|
|
15456
|
+
let dir = path31.dirname(filePath);
|
|
15291
15457
|
while (true) {
|
|
15292
|
-
if (
|
|
15458
|
+
if (fs30.existsSync(path31.join(dir, ".git")) || fs30.existsSync(path31.join(dir, "package.json"))) {
|
|
15293
15459
|
return dir;
|
|
15294
15460
|
}
|
|
15295
|
-
const parent =
|
|
15461
|
+
const parent = path31.dirname(dir);
|
|
15296
15462
|
if (parent === dir) return process.cwd();
|
|
15297
15463
|
dir = parent;
|
|
15298
15464
|
}
|
|
@@ -15300,7 +15466,7 @@ function findProjectRoot(filePath) {
|
|
|
15300
15466
|
function normalizeCwdForHash(cwd) {
|
|
15301
15467
|
let normalized;
|
|
15302
15468
|
try {
|
|
15303
|
-
normalized =
|
|
15469
|
+
normalized = fs30.realpathSync(cwd);
|
|
15304
15470
|
} catch {
|
|
15305
15471
|
normalized = cwd;
|
|
15306
15472
|
}
|
|
@@ -15309,17 +15475,17 @@ function normalizeCwdForHash(cwd) {
|
|
|
15309
15475
|
return normalized;
|
|
15310
15476
|
}
|
|
15311
15477
|
function getShadowRepoDir(cwd) {
|
|
15312
|
-
const hash =
|
|
15313
|
-
return
|
|
15478
|
+
const hash = crypto4.createHash("sha256").update(normalizeCwdForHash(cwd)).digest("hex").slice(0, 16);
|
|
15479
|
+
return path31.join(os26.homedir(), ".node9", "snapshots", hash);
|
|
15314
15480
|
}
|
|
15315
15481
|
function cleanOrphanedIndexFiles(shadowDir) {
|
|
15316
15482
|
try {
|
|
15317
15483
|
const cutoff = Date.now() - 6e4;
|
|
15318
|
-
for (const f of
|
|
15484
|
+
for (const f of fs30.readdirSync(shadowDir)) {
|
|
15319
15485
|
if (f.startsWith("index_")) {
|
|
15320
|
-
const fp =
|
|
15486
|
+
const fp = path31.join(shadowDir, f);
|
|
15321
15487
|
try {
|
|
15322
|
-
if (
|
|
15488
|
+
if (fs30.statSync(fp).mtimeMs < cutoff) fs30.unlinkSync(fp);
|
|
15323
15489
|
} catch {
|
|
15324
15490
|
}
|
|
15325
15491
|
}
|
|
@@ -15331,7 +15497,7 @@ function writeShadowExcludes(shadowDir, ignorePaths) {
|
|
|
15331
15497
|
const hardcoded = [".git", ".node9"];
|
|
15332
15498
|
const lines = [...hardcoded, ...ignorePaths].join("\n");
|
|
15333
15499
|
try {
|
|
15334
|
-
|
|
15500
|
+
fs30.writeFileSync(path31.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
|
|
15335
15501
|
} catch {
|
|
15336
15502
|
}
|
|
15337
15503
|
}
|
|
@@ -15344,25 +15510,25 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
15344
15510
|
timeout: 3e3
|
|
15345
15511
|
});
|
|
15346
15512
|
if (check.status === 0) {
|
|
15347
|
-
const ptPath =
|
|
15513
|
+
const ptPath = path31.join(shadowDir, "project-path.txt");
|
|
15348
15514
|
try {
|
|
15349
|
-
const stored =
|
|
15515
|
+
const stored = fs30.readFileSync(ptPath, "utf8").trim();
|
|
15350
15516
|
if (stored === normalizedCwd) return true;
|
|
15351
15517
|
if (process.env.NODE9_DEBUG === "1")
|
|
15352
15518
|
console.error(
|
|
15353
15519
|
`[Node9] Shadow repo path mismatch: stored="${stored}" expected="${normalizedCwd}" \u2014 reinitializing`
|
|
15354
15520
|
);
|
|
15355
|
-
|
|
15521
|
+
fs30.rmSync(shadowDir, { recursive: true, force: true });
|
|
15356
15522
|
} catch {
|
|
15357
15523
|
try {
|
|
15358
|
-
|
|
15524
|
+
fs30.writeFileSync(ptPath, normalizedCwd, "utf8");
|
|
15359
15525
|
} catch {
|
|
15360
15526
|
}
|
|
15361
15527
|
return true;
|
|
15362
15528
|
}
|
|
15363
15529
|
}
|
|
15364
15530
|
try {
|
|
15365
|
-
|
|
15531
|
+
fs30.mkdirSync(shadowDir, { recursive: true });
|
|
15366
15532
|
} catch {
|
|
15367
15533
|
}
|
|
15368
15534
|
const init = spawnSync3("git", ["init", "--bare", shadowDir], { timeout: 5e3 });
|
|
@@ -15371,7 +15537,7 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
15371
15537
|
if (process.env.NODE9_DEBUG === "1") console.error("[Node9] git init --bare failed:", reason);
|
|
15372
15538
|
return false;
|
|
15373
15539
|
}
|
|
15374
|
-
const configFile =
|
|
15540
|
+
const configFile = path31.join(shadowDir, "config");
|
|
15375
15541
|
spawnSync3("git", ["config", "--file", configFile, "core.untrackedCache", "true"], {
|
|
15376
15542
|
timeout: 3e3
|
|
15377
15543
|
});
|
|
@@ -15379,7 +15545,7 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
15379
15545
|
timeout: 3e3
|
|
15380
15546
|
});
|
|
15381
15547
|
try {
|
|
15382
|
-
|
|
15548
|
+
fs30.writeFileSync(path31.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
|
|
15383
15549
|
} catch {
|
|
15384
15550
|
}
|
|
15385
15551
|
return true;
|
|
@@ -15402,12 +15568,12 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
15402
15568
|
let indexFile = null;
|
|
15403
15569
|
try {
|
|
15404
15570
|
const rawFilePath = extractFilePath(args);
|
|
15405
|
-
const absFilePath = rawFilePath &&
|
|
15571
|
+
const absFilePath = rawFilePath && path31.isAbsolute(rawFilePath) ? rawFilePath : null;
|
|
15406
15572
|
const cwd = absFilePath ? findProjectRoot(absFilePath) : process.cwd();
|
|
15407
15573
|
const shadowDir = getShadowRepoDir(cwd);
|
|
15408
15574
|
if (!ensureShadowRepo(shadowDir, cwd)) return null;
|
|
15409
15575
|
writeShadowExcludes(shadowDir, ignorePaths);
|
|
15410
|
-
indexFile =
|
|
15576
|
+
indexFile = path31.join(shadowDir, `index_${process.pid}_${Date.now()}`);
|
|
15411
15577
|
const shadowEnv = {
|
|
15412
15578
|
...process.env,
|
|
15413
15579
|
GIT_DIR: shadowDir,
|
|
@@ -15479,7 +15645,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
15479
15645
|
writeStack(stack);
|
|
15480
15646
|
const entry = stack[stack.length - 1];
|
|
15481
15647
|
notifySnapshotTaken(commitHash.slice(0, 7), tool, entry.argsSummary, capturedFiles.length);
|
|
15482
|
-
|
|
15648
|
+
fs30.writeFileSync(UNDO_LATEST_PATH, commitHash);
|
|
15483
15649
|
if (shouldGc) {
|
|
15484
15650
|
spawn4("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
|
|
15485
15651
|
}
|
|
@@ -15490,7 +15656,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
15490
15656
|
} finally {
|
|
15491
15657
|
if (indexFile) {
|
|
15492
15658
|
try {
|
|
15493
|
-
|
|
15659
|
+
fs30.unlinkSync(indexFile);
|
|
15494
15660
|
} catch {
|
|
15495
15661
|
}
|
|
15496
15662
|
}
|
|
@@ -15566,9 +15732,9 @@ function applyUndo(hash, cwd) {
|
|
|
15566
15732
|
timeout: GIT_TIMEOUT
|
|
15567
15733
|
}).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
|
|
15568
15734
|
for (const file of [...tracked, ...untracked]) {
|
|
15569
|
-
const fullPath =
|
|
15570
|
-
if (!snapshotFiles.has(file) &&
|
|
15571
|
-
|
|
15735
|
+
const fullPath = path31.join(dir, file);
|
|
15736
|
+
if (!snapshotFiles.has(file) && fs30.existsSync(fullPath)) {
|
|
15737
|
+
fs30.unlinkSync(fullPath);
|
|
15572
15738
|
}
|
|
15573
15739
|
}
|
|
15574
15740
|
return true;
|
|
@@ -15578,17 +15744,17 @@ function applyUndo(hash, cwd) {
|
|
|
15578
15744
|
}
|
|
15579
15745
|
|
|
15580
15746
|
// src/skill-pin.ts
|
|
15581
|
-
import
|
|
15582
|
-
import
|
|
15583
|
-
import
|
|
15584
|
-
import
|
|
15585
|
-
function
|
|
15586
|
-
return
|
|
15747
|
+
import fs31 from "fs";
|
|
15748
|
+
import path32 from "path";
|
|
15749
|
+
import os27 from "os";
|
|
15750
|
+
import crypto5 from "crypto";
|
|
15751
|
+
function getPinsFilePath2() {
|
|
15752
|
+
return path32.join(os27.homedir(), ".node9", "skill-pins.json");
|
|
15587
15753
|
}
|
|
15588
15754
|
var MAX_FILES = 5e3;
|
|
15589
15755
|
var MAX_TOTAL_BYTES = 50 * 1024 * 1024;
|
|
15590
15756
|
function sha256Bytes(buf) {
|
|
15591
|
-
return
|
|
15757
|
+
return crypto5.createHash("sha256").update(buf).digest("hex");
|
|
15592
15758
|
}
|
|
15593
15759
|
function walkDir(root) {
|
|
15594
15760
|
const out = [];
|
|
@@ -15597,18 +15763,18 @@ function walkDir(root) {
|
|
|
15597
15763
|
if (out.length >= MAX_FILES) return;
|
|
15598
15764
|
let entries;
|
|
15599
15765
|
try {
|
|
15600
|
-
entries =
|
|
15766
|
+
entries = fs31.readdirSync(dir, { withFileTypes: true });
|
|
15601
15767
|
} catch {
|
|
15602
15768
|
return;
|
|
15603
15769
|
}
|
|
15604
15770
|
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
15605
15771
|
for (const entry of entries) {
|
|
15606
15772
|
if (out.length >= MAX_FILES) return;
|
|
15607
|
-
const full =
|
|
15608
|
-
const rel = relDir ?
|
|
15773
|
+
const full = path32.join(dir, entry.name);
|
|
15774
|
+
const rel = relDir ? path32.posix.join(relDir, entry.name) : entry.name;
|
|
15609
15775
|
let lst;
|
|
15610
15776
|
try {
|
|
15611
|
-
lst =
|
|
15777
|
+
lst = fs31.lstatSync(full);
|
|
15612
15778
|
} catch {
|
|
15613
15779
|
continue;
|
|
15614
15780
|
}
|
|
@@ -15620,7 +15786,7 @@ function walkDir(root) {
|
|
|
15620
15786
|
if (!lst.isFile()) continue;
|
|
15621
15787
|
if (totalBytes + lst.size > MAX_TOTAL_BYTES) continue;
|
|
15622
15788
|
try {
|
|
15623
|
-
const buf =
|
|
15789
|
+
const buf = fs31.readFileSync(full);
|
|
15624
15790
|
totalBytes += buf.length;
|
|
15625
15791
|
out.push({ rel, hash: sha256Bytes(buf) });
|
|
15626
15792
|
} catch {
|
|
@@ -15634,32 +15800,32 @@ function walkDir(root) {
|
|
|
15634
15800
|
function hashSkillRoot(absPath) {
|
|
15635
15801
|
let lst;
|
|
15636
15802
|
try {
|
|
15637
|
-
lst =
|
|
15803
|
+
lst = fs31.lstatSync(absPath);
|
|
15638
15804
|
} catch {
|
|
15639
15805
|
return { exists: false, contentHash: "", fileCount: 0 };
|
|
15640
15806
|
}
|
|
15641
15807
|
if (lst.isSymbolicLink()) return { exists: false, contentHash: "", fileCount: 0 };
|
|
15642
15808
|
if (lst.isFile()) {
|
|
15643
15809
|
try {
|
|
15644
|
-
return { exists: true, contentHash: sha256Bytes(
|
|
15810
|
+
return { exists: true, contentHash: sha256Bytes(fs31.readFileSync(absPath)), fileCount: 1 };
|
|
15645
15811
|
} catch {
|
|
15646
15812
|
return { exists: false, contentHash: "", fileCount: 0 };
|
|
15647
15813
|
}
|
|
15648
15814
|
}
|
|
15649
15815
|
if (lst.isDirectory()) {
|
|
15650
15816
|
const entries = walkDir(absPath);
|
|
15651
|
-
const contentHash =
|
|
15817
|
+
const contentHash = crypto5.createHash("sha256").update(entries.join("\n")).digest("hex");
|
|
15652
15818
|
return { exists: true, contentHash, fileCount: entries.length };
|
|
15653
15819
|
}
|
|
15654
15820
|
return { exists: false, contentHash: "", fileCount: 0 };
|
|
15655
15821
|
}
|
|
15656
15822
|
function getRootKey(absPath) {
|
|
15657
|
-
return
|
|
15823
|
+
return crypto5.createHash("sha256").update(absPath).digest("hex").slice(0, 16);
|
|
15658
15824
|
}
|
|
15659
15825
|
function readSkillPinsSafe() {
|
|
15660
|
-
const filePath =
|
|
15826
|
+
const filePath = getPinsFilePath2();
|
|
15661
15827
|
try {
|
|
15662
|
-
const raw =
|
|
15828
|
+
const raw = fs31.readFileSync(filePath, "utf-8");
|
|
15663
15829
|
if (!raw.trim()) return { ok: false, reason: "corrupt", detail: "empty file" };
|
|
15664
15830
|
const parsed = JSON.parse(raw);
|
|
15665
15831
|
if (!parsed.roots || typeof parsed.roots !== "object" || Array.isArray(parsed.roots)) {
|
|
@@ -15678,18 +15844,18 @@ function readSkillPins() {
|
|
|
15678
15844
|
throw new Error(`[node9] skill pin file is corrupt: ${result.detail}`);
|
|
15679
15845
|
}
|
|
15680
15846
|
function writeSkillPins(data) {
|
|
15681
|
-
const filePath =
|
|
15682
|
-
|
|
15683
|
-
const tmp = `${filePath}.${
|
|
15684
|
-
|
|
15685
|
-
|
|
15847
|
+
const filePath = getPinsFilePath2();
|
|
15848
|
+
fs31.mkdirSync(path32.dirname(filePath), { recursive: true });
|
|
15849
|
+
const tmp = `${filePath}.${crypto5.randomBytes(6).toString("hex")}.tmp`;
|
|
15850
|
+
fs31.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
15851
|
+
fs31.renameSync(tmp, filePath);
|
|
15686
15852
|
}
|
|
15687
|
-
function
|
|
15853
|
+
function removePin2(rootKey) {
|
|
15688
15854
|
const pins = readSkillPins();
|
|
15689
15855
|
delete pins.roots[rootKey];
|
|
15690
15856
|
writeSkillPins(pins);
|
|
15691
15857
|
}
|
|
15692
|
-
function
|
|
15858
|
+
function clearAllPins2() {
|
|
15693
15859
|
writeSkillPins({ roots: {} });
|
|
15694
15860
|
}
|
|
15695
15861
|
function verifyAndPinRoots(roots) {
|
|
@@ -15726,36 +15892,36 @@ function verifyAndPinRoots(roots) {
|
|
|
15726
15892
|
return { kind: "verified" };
|
|
15727
15893
|
}
|
|
15728
15894
|
function defaultSkillRoots(_cwd) {
|
|
15729
|
-
const marketplaces =
|
|
15895
|
+
const marketplaces = path32.join(os27.homedir(), ".claude", "plugins", "marketplaces");
|
|
15730
15896
|
const roots = [];
|
|
15731
15897
|
let registries;
|
|
15732
15898
|
try {
|
|
15733
|
-
registries =
|
|
15899
|
+
registries = fs31.readdirSync(marketplaces, { withFileTypes: true });
|
|
15734
15900
|
} catch {
|
|
15735
15901
|
return [];
|
|
15736
15902
|
}
|
|
15737
15903
|
for (const registry of registries) {
|
|
15738
15904
|
if (!registry.isDirectory()) continue;
|
|
15739
|
-
const pluginsDir =
|
|
15905
|
+
const pluginsDir = path32.join(marketplaces, registry.name, "plugins");
|
|
15740
15906
|
let plugins;
|
|
15741
15907
|
try {
|
|
15742
|
-
plugins =
|
|
15908
|
+
plugins = fs31.readdirSync(pluginsDir, { withFileTypes: true });
|
|
15743
15909
|
} catch {
|
|
15744
15910
|
continue;
|
|
15745
15911
|
}
|
|
15746
15912
|
for (const plugin of plugins) {
|
|
15747
15913
|
if (!plugin.isDirectory()) continue;
|
|
15748
|
-
roots.push(
|
|
15914
|
+
roots.push(path32.join(pluginsDir, plugin.name));
|
|
15749
15915
|
}
|
|
15750
15916
|
}
|
|
15751
15917
|
return roots;
|
|
15752
15918
|
}
|
|
15753
15919
|
function resolveUserSkillRoot(entry, cwd) {
|
|
15754
15920
|
if (!entry) return null;
|
|
15755
|
-
if (entry.startsWith("~/") || entry === "~") return
|
|
15756
|
-
if (
|
|
15757
|
-
if (!cwd || !
|
|
15758
|
-
return
|
|
15921
|
+
if (entry.startsWith("~/") || entry === "~") return path32.join(os27.homedir(), entry.slice(1));
|
|
15922
|
+
if (path32.isAbsolute(entry)) return entry;
|
|
15923
|
+
if (!cwd || !path32.isAbsolute(cwd)) return null;
|
|
15924
|
+
return path32.join(cwd, entry);
|
|
15759
15925
|
}
|
|
15760
15926
|
|
|
15761
15927
|
// src/cli/commands/check.ts
|
|
@@ -15802,9 +15968,9 @@ function registerCheckCommand(program2) {
|
|
|
15802
15968
|
} catch (err2) {
|
|
15803
15969
|
const tempConfig = getConfig();
|
|
15804
15970
|
if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
|
|
15805
|
-
const logPath =
|
|
15971
|
+
const logPath = path33.join(os28.homedir(), ".node9", "hook-debug.log");
|
|
15806
15972
|
const errMsg = err2 instanceof Error ? err2.message : String(err2);
|
|
15807
|
-
|
|
15973
|
+
fs32.appendFileSync(
|
|
15808
15974
|
logPath,
|
|
15809
15975
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
|
|
15810
15976
|
RAW: ${raw}
|
|
@@ -15817,14 +15983,14 @@ RAW: ${raw}
|
|
|
15817
15983
|
const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
|
|
15818
15984
|
if (process.env.NODE9_DEBUG === "1") {
|
|
15819
15985
|
try {
|
|
15820
|
-
const logPath =
|
|
15821
|
-
if (!
|
|
15822
|
-
|
|
15986
|
+
const logPath = path33.join(os28.homedir(), ".node9", "hook-debug.log");
|
|
15987
|
+
if (!fs32.existsSync(path33.dirname(logPath)))
|
|
15988
|
+
fs32.mkdirSync(path33.dirname(logPath), { recursive: true });
|
|
15823
15989
|
const sanitized = JSON.stringify({
|
|
15824
15990
|
...payload,
|
|
15825
15991
|
prompt: `<redacted, ${prompt.length} bytes>`
|
|
15826
15992
|
});
|
|
15827
|
-
|
|
15993
|
+
fs32.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${sanitized}
|
|
15828
15994
|
`);
|
|
15829
15995
|
} catch {
|
|
15830
15996
|
}
|
|
@@ -15844,8 +16010,8 @@ RAW: ${raw}
|
|
|
15844
16010
|
);
|
|
15845
16011
|
const reason = `\u{1F6A8} Node9 DLP: ${dlpMatch.patternName} detected in prompt (${dlpMatch.redactedSample}). Prompt was not submitted \u2014 remove the credential and try again.`;
|
|
15846
16012
|
try {
|
|
15847
|
-
const ttyFd =
|
|
15848
|
-
|
|
16013
|
+
const ttyFd = fs32.openSync("/dev/tty", "w");
|
|
16014
|
+
fs32.writeSync(
|
|
15849
16015
|
ttyFd,
|
|
15850
16016
|
chalk9.bgRed.white.bold(`
|
|
15851
16017
|
\u{1F6A8} NODE9 DLP \u2014 PROMPT BLOCKED
|
|
@@ -15855,7 +16021,7 @@ RAW: ${raw}
|
|
|
15855
16021
|
|
|
15856
16022
|
`)
|
|
15857
16023
|
);
|
|
15858
|
-
|
|
16024
|
+
fs32.closeSync(ttyFd);
|
|
15859
16025
|
} catch {
|
|
15860
16026
|
}
|
|
15861
16027
|
const isCodex = agent2 === "Codex";
|
|
@@ -15873,16 +16039,16 @@ RAW: ${raw}
|
|
|
15873
16039
|
);
|
|
15874
16040
|
process.exit(2);
|
|
15875
16041
|
}
|
|
15876
|
-
const safeCwdForConfig = typeof payload.cwd === "string" &&
|
|
16042
|
+
const safeCwdForConfig = typeof payload.cwd === "string" && path33.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
15877
16043
|
const config = getConfig(safeCwdForConfig);
|
|
15878
16044
|
if (config.settings.autoStartDaemon && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON) {
|
|
15879
16045
|
try {
|
|
15880
16046
|
const scriptPath = process.argv[1];
|
|
15881
|
-
if (typeof scriptPath !== "string" || !
|
|
16047
|
+
if (typeof scriptPath !== "string" || !path33.isAbsolute(scriptPath))
|
|
15882
16048
|
throw new Error("node9: argv[1] is not an absolute path");
|
|
15883
|
-
const resolvedScript =
|
|
15884
|
-
const packageDist =
|
|
15885
|
-
if (!resolvedScript.startsWith(packageDist +
|
|
16049
|
+
const resolvedScript = fs32.realpathSync(scriptPath);
|
|
16050
|
+
const packageDist = fs32.realpathSync(path33.resolve(__dirname, "../.."));
|
|
16051
|
+
if (!resolvedScript.startsWith(packageDist + path33.sep) && resolvedScript !== packageDist)
|
|
15886
16052
|
throw new Error(
|
|
15887
16053
|
`node9: daemon spawn aborted \u2014 argv[1] (${resolvedScript}) is outside package dist (${packageDist})`
|
|
15888
16054
|
);
|
|
@@ -15904,10 +16070,10 @@ RAW: ${raw}
|
|
|
15904
16070
|
});
|
|
15905
16071
|
d.unref();
|
|
15906
16072
|
} catch (spawnErr) {
|
|
15907
|
-
const logPath =
|
|
16073
|
+
const logPath = path33.join(os28.homedir(), ".node9", "hook-debug.log");
|
|
15908
16074
|
const msg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);
|
|
15909
16075
|
try {
|
|
15910
|
-
|
|
16076
|
+
fs32.appendFileSync(
|
|
15911
16077
|
logPath,
|
|
15912
16078
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-autostart-failed: ${msg}
|
|
15913
16079
|
`
|
|
@@ -15917,10 +16083,10 @@ RAW: ${raw}
|
|
|
15917
16083
|
}
|
|
15918
16084
|
}
|
|
15919
16085
|
if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
|
|
15920
|
-
const logPath =
|
|
15921
|
-
if (!
|
|
15922
|
-
|
|
15923
|
-
|
|
16086
|
+
const logPath = path33.join(os28.homedir(), ".node9", "hook-debug.log");
|
|
16087
|
+
if (!fs32.existsSync(path33.dirname(logPath)))
|
|
16088
|
+
fs32.mkdirSync(path33.dirname(logPath), { recursive: true });
|
|
16089
|
+
fs32.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
|
|
15924
16090
|
`);
|
|
15925
16091
|
}
|
|
15926
16092
|
const toolName = sanitize2(payload.tool_name ?? payload.name ?? "");
|
|
@@ -15933,8 +16099,8 @@ RAW: ${raw}
|
|
|
15933
16099
|
const isHumanDecision = blockedByContext.toLowerCase().includes("user") || blockedByContext.toLowerCase().includes("daemon") || blockedByContext.toLowerCase().includes("decision");
|
|
15934
16100
|
let ttyFd = null;
|
|
15935
16101
|
try {
|
|
15936
|
-
ttyFd =
|
|
15937
|
-
const writeTty = (line) =>
|
|
16102
|
+
ttyFd = fs32.openSync("/dev/tty", "w");
|
|
16103
|
+
const writeTty = (line) => fs32.writeSync(ttyFd, line + "\n");
|
|
15938
16104
|
if (blockedByContext.includes("DLP") || blockedByContext.includes("Secret Detected") || blockedByContext.includes("Credential Review")) {
|
|
15939
16105
|
writeTty(chalk9.bgRed.white.bold(`
|
|
15940
16106
|
\u{1F6A8} NODE9 DLP ALERT \u2014 CREDENTIAL DETECTED `));
|
|
@@ -15953,7 +16119,7 @@ RAW: ${raw}
|
|
|
15953
16119
|
} finally {
|
|
15954
16120
|
if (ttyFd !== null)
|
|
15955
16121
|
try {
|
|
15956
|
-
|
|
16122
|
+
fs32.closeSync(ttyFd);
|
|
15957
16123
|
} catch {
|
|
15958
16124
|
}
|
|
15959
16125
|
}
|
|
@@ -15989,17 +16155,17 @@ RAW: ${raw}
|
|
|
15989
16155
|
const safeSessionId = /^[A-Za-z0-9_\-]{1,128}$/.test(rawSessionId) ? rawSessionId : "";
|
|
15990
16156
|
if (skillPinCfg.enabled && safeSessionId) {
|
|
15991
16157
|
try {
|
|
15992
|
-
const sessionsDir =
|
|
15993
|
-
const flagPath =
|
|
16158
|
+
const sessionsDir = path33.join(os28.homedir(), ".node9", "skill-sessions");
|
|
16159
|
+
const flagPath = path33.join(sessionsDir, `${safeSessionId}.json`);
|
|
15994
16160
|
let flag = null;
|
|
15995
16161
|
try {
|
|
15996
|
-
flag = JSON.parse(
|
|
16162
|
+
flag = JSON.parse(fs32.readFileSync(flagPath, "utf-8"));
|
|
15997
16163
|
} catch {
|
|
15998
16164
|
}
|
|
15999
16165
|
const writeFlag = (data2) => {
|
|
16000
16166
|
try {
|
|
16001
|
-
|
|
16002
|
-
|
|
16167
|
+
fs32.mkdirSync(sessionsDir, { recursive: true });
|
|
16168
|
+
fs32.writeFileSync(
|
|
16003
16169
|
flagPath,
|
|
16004
16170
|
JSON.stringify({ ...data2, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
|
|
16005
16171
|
{ mode: 384 }
|
|
@@ -16010,8 +16176,8 @@ RAW: ${raw}
|
|
|
16010
16176
|
const sendSkillWarn = (detail, recoveryCmd) => {
|
|
16011
16177
|
let ttyFd = null;
|
|
16012
16178
|
try {
|
|
16013
|
-
ttyFd =
|
|
16014
|
-
const w = (line) =>
|
|
16179
|
+
ttyFd = fs32.openSync("/dev/tty", "w");
|
|
16180
|
+
const w = (line) => fs32.writeSync(ttyFd, line + "\n");
|
|
16015
16181
|
w(chalk9.yellow(`
|
|
16016
16182
|
\u26A0\uFE0F Node9: installed skill drift detected`));
|
|
16017
16183
|
w(chalk9.gray(` ${detail}`));
|
|
@@ -16026,7 +16192,7 @@ RAW: ${raw}
|
|
|
16026
16192
|
} finally {
|
|
16027
16193
|
if (ttyFd !== null)
|
|
16028
16194
|
try {
|
|
16029
|
-
|
|
16195
|
+
fs32.closeSync(ttyFd);
|
|
16030
16196
|
} catch {
|
|
16031
16197
|
}
|
|
16032
16198
|
}
|
|
@@ -16042,7 +16208,7 @@ RAW: ${raw}
|
|
|
16042
16208
|
return;
|
|
16043
16209
|
}
|
|
16044
16210
|
if (!flag || flag.state !== "verified" && flag.state !== "warned") {
|
|
16045
|
-
const absoluteCwd = typeof payload.cwd === "string" &&
|
|
16211
|
+
const absoluteCwd = typeof payload.cwd === "string" && path33.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
16046
16212
|
const extraRoots = skillPinCfg.roots;
|
|
16047
16213
|
const resolvedExtra = extraRoots.map((r) => resolveUserSkillRoot(r, absoluteCwd)).filter((r) => typeof r === "string");
|
|
16048
16214
|
const roots = [...defaultSkillRoots(absoluteCwd), ...resolvedExtra];
|
|
@@ -16083,10 +16249,10 @@ RAW: ${raw}
|
|
|
16083
16249
|
}
|
|
16084
16250
|
try {
|
|
16085
16251
|
const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1e3;
|
|
16086
|
-
for (const name of
|
|
16087
|
-
const p =
|
|
16252
|
+
for (const name of fs32.readdirSync(sessionsDir)) {
|
|
16253
|
+
const p = path33.join(sessionsDir, name);
|
|
16088
16254
|
try {
|
|
16089
|
-
if (
|
|
16255
|
+
if (fs32.statSync(p).mtimeMs < cutoff) fs32.unlinkSync(p);
|
|
16090
16256
|
} catch {
|
|
16091
16257
|
}
|
|
16092
16258
|
}
|
|
@@ -16096,9 +16262,9 @@ RAW: ${raw}
|
|
|
16096
16262
|
} catch (err2) {
|
|
16097
16263
|
if (process.env.NODE9_DEBUG === "1") {
|
|
16098
16264
|
try {
|
|
16099
|
-
const dbg =
|
|
16265
|
+
const dbg = path33.join(os28.homedir(), ".node9", "hook-debug.log");
|
|
16100
16266
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
16101
|
-
|
|
16267
|
+
fs32.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
|
|
16102
16268
|
`);
|
|
16103
16269
|
} catch {
|
|
16104
16270
|
}
|
|
@@ -16108,7 +16274,7 @@ RAW: ${raw}
|
|
|
16108
16274
|
if (shouldSnapshot(toolName, toolInput, config)) {
|
|
16109
16275
|
await createShadowSnapshot(toolName, toolInput, config.policy.snapshot.ignorePaths);
|
|
16110
16276
|
}
|
|
16111
|
-
const safeCwdForAuth = typeof payload.cwd === "string" &&
|
|
16277
|
+
const safeCwdForAuth = typeof payload.cwd === "string" && path33.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
16112
16278
|
const result = await authorizeHeadless(toolName, toolInput, meta, {
|
|
16113
16279
|
cwd: safeCwdForAuth
|
|
16114
16280
|
});
|
|
@@ -16120,12 +16286,12 @@ RAW: ${raw}
|
|
|
16120
16286
|
}
|
|
16121
16287
|
if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && !process.stdout.isTTY && config.settings.autoStartDaemon) {
|
|
16122
16288
|
try {
|
|
16123
|
-
const tty =
|
|
16124
|
-
|
|
16289
|
+
const tty = fs32.openSync("/dev/tty", "w");
|
|
16290
|
+
fs32.writeSync(
|
|
16125
16291
|
tty,
|
|
16126
16292
|
chalk9.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically...\n")
|
|
16127
16293
|
);
|
|
16128
|
-
|
|
16294
|
+
fs32.closeSync(tty);
|
|
16129
16295
|
} catch {
|
|
16130
16296
|
}
|
|
16131
16297
|
const daemonReady = await autoStartDaemonAndWait();
|
|
@@ -16152,9 +16318,9 @@ RAW: ${raw}
|
|
|
16152
16318
|
});
|
|
16153
16319
|
} catch (err2) {
|
|
16154
16320
|
if (process.env.NODE9_DEBUG === "1") {
|
|
16155
|
-
const logPath =
|
|
16321
|
+
const logPath = path33.join(os28.homedir(), ".node9", "hook-debug.log");
|
|
16156
16322
|
const errMsg = err2 instanceof Error ? err2.message : String(err2);
|
|
16157
|
-
|
|
16323
|
+
fs32.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
|
|
16158
16324
|
`);
|
|
16159
16325
|
}
|
|
16160
16326
|
process.exit(0);
|
|
@@ -16190,9 +16356,9 @@ RAW: ${raw}
|
|
|
16190
16356
|
// src/cli/commands/log.ts
|
|
16191
16357
|
init_audit();
|
|
16192
16358
|
init_config();
|
|
16193
|
-
import
|
|
16194
|
-
import
|
|
16195
|
-
import
|
|
16359
|
+
import fs33 from "fs";
|
|
16360
|
+
import path34 from "path";
|
|
16361
|
+
import os29 from "os";
|
|
16196
16362
|
init_daemon();
|
|
16197
16363
|
|
|
16198
16364
|
// src/utils/cp-mv-parser.ts
|
|
@@ -16268,10 +16434,10 @@ function registerLogCommand(program2) {
|
|
|
16268
16434
|
};
|
|
16269
16435
|
if (agent) entry.agent = agent;
|
|
16270
16436
|
if (payload.session_id) entry.sessionId = payload.session_id;
|
|
16271
|
-
const logPath =
|
|
16272
|
-
if (!
|
|
16273
|
-
|
|
16274
|
-
|
|
16437
|
+
const logPath = path34.join(os29.homedir(), ".node9", "audit.log");
|
|
16438
|
+
if (!fs33.existsSync(path34.dirname(logPath)))
|
|
16439
|
+
fs33.mkdirSync(path34.dirname(logPath), { recursive: true });
|
|
16440
|
+
fs33.appendFileSync(logPath, JSON.stringify(entry) + "\n");
|
|
16275
16441
|
if ((tool === "Bash" || tool === "bash") && isDaemonRunning()) {
|
|
16276
16442
|
const command = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
|
|
16277
16443
|
if (command) {
|
|
@@ -16304,7 +16470,7 @@ function registerLogCommand(program2) {
|
|
|
16304
16470
|
}
|
|
16305
16471
|
}
|
|
16306
16472
|
}
|
|
16307
|
-
const safeCwd = typeof payload.cwd === "string" &&
|
|
16473
|
+
const safeCwd = typeof payload.cwd === "string" && path34.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
16308
16474
|
const config = getConfig(safeCwd);
|
|
16309
16475
|
if ((tool === "Bash" || tool === "bash") && config.settings.enableUndo !== false) {
|
|
16310
16476
|
const bashCommand = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
|
|
@@ -16325,9 +16491,9 @@ function registerLogCommand(program2) {
|
|
|
16325
16491
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
16326
16492
|
process.stderr.write(`[Node9] audit log error: ${msg}
|
|
16327
16493
|
`);
|
|
16328
|
-
const debugPath =
|
|
16494
|
+
const debugPath = path34.join(os29.homedir(), ".node9", "hook-debug.log");
|
|
16329
16495
|
try {
|
|
16330
|
-
|
|
16496
|
+
fs33.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
|
|
16331
16497
|
`);
|
|
16332
16498
|
} catch {
|
|
16333
16499
|
}
|
|
@@ -16728,13 +16894,13 @@ function registerConfigShowCommand(program2) {
|
|
|
16728
16894
|
// src/cli/commands/doctor.ts
|
|
16729
16895
|
init_daemon();
|
|
16730
16896
|
import chalk11 from "chalk";
|
|
16731
|
-
import
|
|
16732
|
-
import
|
|
16733
|
-
import
|
|
16897
|
+
import fs34 from "fs";
|
|
16898
|
+
import path35 from "path";
|
|
16899
|
+
import os30 from "os";
|
|
16734
16900
|
import { execSync } from "child_process";
|
|
16735
16901
|
function registerDoctorCommand(program2, version2) {
|
|
16736
16902
|
program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(() => {
|
|
16737
|
-
const homeDir2 =
|
|
16903
|
+
const homeDir2 = os30.homedir();
|
|
16738
16904
|
let failures = 0;
|
|
16739
16905
|
function pass(msg) {
|
|
16740
16906
|
console.log(chalk11.green(" \u2705 ") + msg);
|
|
@@ -16783,10 +16949,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16783
16949
|
);
|
|
16784
16950
|
}
|
|
16785
16951
|
section("Configuration");
|
|
16786
|
-
const globalConfigPath =
|
|
16787
|
-
if (
|
|
16952
|
+
const globalConfigPath = path35.join(homeDir2, ".node9", "config.json");
|
|
16953
|
+
if (fs34.existsSync(globalConfigPath)) {
|
|
16788
16954
|
try {
|
|
16789
|
-
JSON.parse(
|
|
16955
|
+
JSON.parse(fs34.readFileSync(globalConfigPath, "utf-8"));
|
|
16790
16956
|
pass("~/.node9/config.json found and valid");
|
|
16791
16957
|
} catch {
|
|
16792
16958
|
fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
|
|
@@ -16794,10 +16960,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16794
16960
|
} else {
|
|
16795
16961
|
warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
|
|
16796
16962
|
}
|
|
16797
|
-
const projectConfigPath =
|
|
16798
|
-
if (
|
|
16963
|
+
const projectConfigPath = path35.join(process.cwd(), "node9.config.json");
|
|
16964
|
+
if (fs34.existsSync(projectConfigPath)) {
|
|
16799
16965
|
try {
|
|
16800
|
-
JSON.parse(
|
|
16966
|
+
JSON.parse(fs34.readFileSync(projectConfigPath, "utf-8"));
|
|
16801
16967
|
pass("node9.config.json found and valid (project)");
|
|
16802
16968
|
} catch {
|
|
16803
16969
|
fail(
|
|
@@ -16806,8 +16972,8 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16806
16972
|
);
|
|
16807
16973
|
}
|
|
16808
16974
|
}
|
|
16809
|
-
const credsPath =
|
|
16810
|
-
if (
|
|
16975
|
+
const credsPath = path35.join(homeDir2, ".node9", "credentials.json");
|
|
16976
|
+
if (fs34.existsSync(credsPath)) {
|
|
16811
16977
|
pass("Cloud credentials found (~/.node9/credentials.json)");
|
|
16812
16978
|
} else {
|
|
16813
16979
|
warn(
|
|
@@ -16816,10 +16982,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16816
16982
|
);
|
|
16817
16983
|
}
|
|
16818
16984
|
section("Agent Hooks");
|
|
16819
|
-
const claudeSettingsPath =
|
|
16820
|
-
if (
|
|
16985
|
+
const claudeSettingsPath = path35.join(homeDir2, ".claude", "settings.json");
|
|
16986
|
+
if (fs34.existsSync(claudeSettingsPath)) {
|
|
16821
16987
|
try {
|
|
16822
|
-
const cs = JSON.parse(
|
|
16988
|
+
const cs = JSON.parse(fs34.readFileSync(claudeSettingsPath, "utf-8"));
|
|
16823
16989
|
const hasHook = cs.hooks?.PreToolUse?.some(
|
|
16824
16990
|
(m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
|
|
16825
16991
|
);
|
|
@@ -16835,10 +17001,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16835
17001
|
} else {
|
|
16836
17002
|
warn("Claude Code \u2014 not configured", "Run: node9 setup claude");
|
|
16837
17003
|
}
|
|
16838
|
-
const geminiSettingsPath =
|
|
16839
|
-
if (
|
|
17004
|
+
const geminiSettingsPath = path35.join(homeDir2, ".gemini", "settings.json");
|
|
17005
|
+
if (fs34.existsSync(geminiSettingsPath)) {
|
|
16840
17006
|
try {
|
|
16841
|
-
const gs = JSON.parse(
|
|
17007
|
+
const gs = JSON.parse(fs34.readFileSync(geminiSettingsPath, "utf-8"));
|
|
16842
17008
|
const hasHook = gs.hooks?.BeforeTool?.some(
|
|
16843
17009
|
(m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
|
|
16844
17010
|
);
|
|
@@ -16854,10 +17020,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16854
17020
|
} else {
|
|
16855
17021
|
warn("Gemini CLI \u2014 not configured", "Run: node9 setup gemini (skip if not using Gemini)");
|
|
16856
17022
|
}
|
|
16857
|
-
const cursorHooksPath =
|
|
16858
|
-
if (
|
|
17023
|
+
const cursorHooksPath = path35.join(homeDir2, ".cursor", "hooks.json");
|
|
17024
|
+
if (fs34.existsSync(cursorHooksPath)) {
|
|
16859
17025
|
try {
|
|
16860
|
-
const cur = JSON.parse(
|
|
17026
|
+
const cur = JSON.parse(fs34.readFileSync(cursorHooksPath, "utf-8"));
|
|
16861
17027
|
const hasHook = cur.hooks?.preToolUse?.some(
|
|
16862
17028
|
(h) => h.command?.includes("node9") || h.command?.includes("cli.js")
|
|
16863
17029
|
);
|
|
@@ -16897,9 +17063,9 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16897
17063
|
|
|
16898
17064
|
// src/cli/commands/audit.ts
|
|
16899
17065
|
import chalk12 from "chalk";
|
|
16900
|
-
import
|
|
16901
|
-
import
|
|
16902
|
-
import
|
|
17066
|
+
import fs35 from "fs";
|
|
17067
|
+
import path36 from "path";
|
|
17068
|
+
import os31 from "os";
|
|
16903
17069
|
function formatRelativeTime(timestamp) {
|
|
16904
17070
|
const diff = Date.now() - new Date(timestamp).getTime();
|
|
16905
17071
|
const sec = Math.floor(diff / 1e3);
|
|
@@ -16912,14 +17078,14 @@ function formatRelativeTime(timestamp) {
|
|
|
16912
17078
|
}
|
|
16913
17079
|
function registerAuditCommand(program2) {
|
|
16914
17080
|
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) => {
|
|
16915
|
-
const logPath =
|
|
16916
|
-
if (!
|
|
17081
|
+
const logPath = path36.join(os31.homedir(), ".node9", "audit.log");
|
|
17082
|
+
if (!fs35.existsSync(logPath)) {
|
|
16917
17083
|
console.log(
|
|
16918
17084
|
chalk12.yellow("No audit logs found. Run node9 with an agent to generate entries.")
|
|
16919
17085
|
);
|
|
16920
17086
|
return;
|
|
16921
17087
|
}
|
|
16922
|
-
const raw =
|
|
17088
|
+
const raw = fs35.readFileSync(logPath, "utf-8");
|
|
16923
17089
|
const lines = raw.split("\n").filter((l) => l.trim() !== "");
|
|
16924
17090
|
let entries = lines.flatMap((line) => {
|
|
16925
17091
|
try {
|
|
@@ -16977,9 +17143,9 @@ import chalk13 from "chalk";
|
|
|
16977
17143
|
// src/cli/aggregate/report-audit.ts
|
|
16978
17144
|
init_costSync();
|
|
16979
17145
|
init_litellm();
|
|
16980
|
-
import
|
|
16981
|
-
import
|
|
16982
|
-
import
|
|
17146
|
+
import fs36 from "fs";
|
|
17147
|
+
import os32 from "os";
|
|
17148
|
+
import path37 from "path";
|
|
16983
17149
|
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;
|
|
16984
17150
|
function buildTestTimestamps(allEntries) {
|
|
16985
17151
|
const testTs = /* @__PURE__ */ new Set();
|
|
@@ -17059,8 +17225,8 @@ function getDateRange(period, now) {
|
|
|
17059
17225
|
}
|
|
17060
17226
|
}
|
|
17061
17227
|
function parseAuditLog(logPath) {
|
|
17062
|
-
if (!
|
|
17063
|
-
const raw =
|
|
17228
|
+
if (!fs36.existsSync(logPath)) return [];
|
|
17229
|
+
const raw = fs36.readFileSync(logPath, "utf-8");
|
|
17064
17230
|
return raw.split("\n").flatMap((line) => {
|
|
17065
17231
|
if (!line.trim()) return [];
|
|
17066
17232
|
try {
|
|
@@ -17120,25 +17286,25 @@ function freezeClaudeCost(acc) {
|
|
|
17120
17286
|
};
|
|
17121
17287
|
}
|
|
17122
17288
|
function processClaudeCostProject(proj, projectsDir, start, end, acc) {
|
|
17123
|
-
const projPath =
|
|
17289
|
+
const projPath = path37.join(projectsDir, proj);
|
|
17124
17290
|
let files;
|
|
17125
17291
|
try {
|
|
17126
|
-
const stat =
|
|
17292
|
+
const stat = fs36.statSync(projPath);
|
|
17127
17293
|
if (!stat.isDirectory()) return;
|
|
17128
|
-
files =
|
|
17294
|
+
files = fs36.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
17129
17295
|
} catch {
|
|
17130
17296
|
return;
|
|
17131
17297
|
}
|
|
17132
17298
|
const startMs = start.getTime();
|
|
17133
17299
|
for (const file of files) {
|
|
17134
|
-
const filePath =
|
|
17300
|
+
const filePath = path37.join(projPath, file);
|
|
17135
17301
|
try {
|
|
17136
|
-
if (
|
|
17302
|
+
if (fs36.statSync(filePath).mtimeMs < startMs) continue;
|
|
17137
17303
|
} catch {
|
|
17138
17304
|
continue;
|
|
17139
17305
|
}
|
|
17140
17306
|
try {
|
|
17141
|
-
const raw =
|
|
17307
|
+
const raw = fs36.readFileSync(filePath, "utf-8");
|
|
17142
17308
|
for (const line of raw.split("\n")) {
|
|
17143
17309
|
if (!line.trim()) continue;
|
|
17144
17310
|
let entry;
|
|
@@ -17188,10 +17354,10 @@ function processClaudeCostProject(proj, projectsDir, start, end, acc) {
|
|
|
17188
17354
|
}
|
|
17189
17355
|
function loadClaudeCost(start, end, projectsDir) {
|
|
17190
17356
|
const acc = emptyClaudeCostAccumulator();
|
|
17191
|
-
if (!
|
|
17357
|
+
if (!fs36.existsSync(projectsDir)) return freezeClaudeCost(acc);
|
|
17192
17358
|
let dirs;
|
|
17193
17359
|
try {
|
|
17194
|
-
dirs =
|
|
17360
|
+
dirs = fs36.readdirSync(projectsDir);
|
|
17195
17361
|
} catch {
|
|
17196
17362
|
return freezeClaudeCost(acc);
|
|
17197
17363
|
}
|
|
@@ -17203,7 +17369,7 @@ function loadClaudeCost(start, end, projectsDir) {
|
|
|
17203
17369
|
function processCodexCostFile(filePath, start, end, acc) {
|
|
17204
17370
|
let lines;
|
|
17205
17371
|
try {
|
|
17206
|
-
lines =
|
|
17372
|
+
lines = fs36.readFileSync(filePath, "utf-8").split("\n");
|
|
17207
17373
|
} catch {
|
|
17208
17374
|
return;
|
|
17209
17375
|
}
|
|
@@ -17248,31 +17414,31 @@ function processCodexCostFile(filePath, start, end, acc) {
|
|
|
17248
17414
|
}
|
|
17249
17415
|
function listCodexSessionFiles(sessionsBase) {
|
|
17250
17416
|
const jsonlFiles = [];
|
|
17251
|
-
if (!
|
|
17417
|
+
if (!fs36.existsSync(sessionsBase)) return jsonlFiles;
|
|
17252
17418
|
try {
|
|
17253
|
-
for (const year of
|
|
17254
|
-
const yearPath =
|
|
17419
|
+
for (const year of fs36.readdirSync(sessionsBase)) {
|
|
17420
|
+
const yearPath = path37.join(sessionsBase, year);
|
|
17255
17421
|
try {
|
|
17256
|
-
if (!
|
|
17422
|
+
if (!fs36.statSync(yearPath).isDirectory()) continue;
|
|
17257
17423
|
} catch {
|
|
17258
17424
|
continue;
|
|
17259
17425
|
}
|
|
17260
|
-
for (const month of
|
|
17261
|
-
const monthPath =
|
|
17426
|
+
for (const month of fs36.readdirSync(yearPath)) {
|
|
17427
|
+
const monthPath = path37.join(yearPath, month);
|
|
17262
17428
|
try {
|
|
17263
|
-
if (!
|
|
17429
|
+
if (!fs36.statSync(monthPath).isDirectory()) continue;
|
|
17264
17430
|
} catch {
|
|
17265
17431
|
continue;
|
|
17266
17432
|
}
|
|
17267
|
-
for (const day of
|
|
17268
|
-
const dayPath =
|
|
17433
|
+
for (const day of fs36.readdirSync(monthPath)) {
|
|
17434
|
+
const dayPath = path37.join(monthPath, day);
|
|
17269
17435
|
try {
|
|
17270
|
-
if (!
|
|
17436
|
+
if (!fs36.statSync(dayPath).isDirectory()) continue;
|
|
17271
17437
|
} catch {
|
|
17272
17438
|
continue;
|
|
17273
17439
|
}
|
|
17274
|
-
for (const file of
|
|
17275
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(
|
|
17440
|
+
for (const file of fs36.readdirSync(dayPath)) {
|
|
17441
|
+
if (file.endsWith(".jsonl")) jsonlFiles.push(path37.join(dayPath, file));
|
|
17276
17442
|
}
|
|
17277
17443
|
}
|
|
17278
17444
|
}
|
|
@@ -17325,13 +17491,13 @@ function freezeGeminiCost(acc) {
|
|
|
17325
17491
|
function processGeminiCostFile(filePath, projectKey, start, end, acc) {
|
|
17326
17492
|
const startMs = start.getTime();
|
|
17327
17493
|
try {
|
|
17328
|
-
if (
|
|
17494
|
+
if (fs36.statSync(filePath).mtimeMs < startMs) return;
|
|
17329
17495
|
} catch {
|
|
17330
17496
|
return;
|
|
17331
17497
|
}
|
|
17332
17498
|
let raw;
|
|
17333
17499
|
try {
|
|
17334
|
-
raw =
|
|
17500
|
+
raw = fs36.readFileSync(filePath, "utf-8");
|
|
17335
17501
|
} catch {
|
|
17336
17502
|
return;
|
|
17337
17503
|
}
|
|
@@ -17380,30 +17546,30 @@ function listGeminiSessionFiles(geminiTmpDir) {
|
|
|
17380
17546
|
const out = [];
|
|
17381
17547
|
let dirs;
|
|
17382
17548
|
try {
|
|
17383
|
-
if (!
|
|
17384
|
-
dirs =
|
|
17549
|
+
if (!fs36.statSync(geminiTmpDir).isDirectory()) return out;
|
|
17550
|
+
dirs = fs36.readdirSync(geminiTmpDir);
|
|
17385
17551
|
} catch {
|
|
17386
17552
|
return out;
|
|
17387
17553
|
}
|
|
17388
17554
|
for (const proj of dirs) {
|
|
17389
|
-
const chatsDir =
|
|
17555
|
+
const chatsDir = path37.join(geminiTmpDir, proj, "chats");
|
|
17390
17556
|
let files;
|
|
17391
17557
|
try {
|
|
17392
|
-
if (!
|
|
17393
|
-
files =
|
|
17558
|
+
if (!fs36.statSync(chatsDir).isDirectory()) continue;
|
|
17559
|
+
files = fs36.readdirSync(chatsDir);
|
|
17394
17560
|
} catch {
|
|
17395
17561
|
continue;
|
|
17396
17562
|
}
|
|
17397
17563
|
for (const f of files) {
|
|
17398
17564
|
if (!f.endsWith(".jsonl")) continue;
|
|
17399
|
-
out.push({ projectKey: proj, file:
|
|
17565
|
+
out.push({ projectKey: proj, file: path37.join(chatsDir, f) });
|
|
17400
17566
|
}
|
|
17401
17567
|
}
|
|
17402
17568
|
return out;
|
|
17403
17569
|
}
|
|
17404
17570
|
function loadGeminiCost(start, end, geminiTmpDir) {
|
|
17405
17571
|
const acc = emptyGeminiAccumulator();
|
|
17406
|
-
if (!
|
|
17572
|
+
if (!fs36.existsSync(geminiTmpDir)) return freezeGeminiCost(acc);
|
|
17407
17573
|
for (const { projectKey, file } of listGeminiSessionFiles(geminiTmpDir)) {
|
|
17408
17574
|
processGeminiCostFile(file, projectKey, start, end, acc);
|
|
17409
17575
|
}
|
|
@@ -17411,11 +17577,11 @@ function loadGeminiCost(start, end, geminiTmpDir) {
|
|
|
17411
17577
|
}
|
|
17412
17578
|
function aggregateReportFromAudit(period, opts = {}) {
|
|
17413
17579
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
17414
|
-
const auditLogPath = opts.auditLogPath ??
|
|
17415
|
-
const claudeProjectsDir = opts.claudeProjectsDir ??
|
|
17416
|
-
const codexSessionsDir = opts.codexSessionsDir ??
|
|
17417
|
-
const geminiTmpDir = opts.geminiTmpDir ??
|
|
17418
|
-
const hasAuditFile =
|
|
17580
|
+
const auditLogPath = opts.auditLogPath ?? path37.join(os32.homedir(), ".node9", "audit.log");
|
|
17581
|
+
const claudeProjectsDir = opts.claudeProjectsDir ?? path37.join(os32.homedir(), ".claude", "projects");
|
|
17582
|
+
const codexSessionsDir = opts.codexSessionsDir ?? path37.join(os32.homedir(), ".codex", "sessions");
|
|
17583
|
+
const geminiTmpDir = opts.geminiTmpDir ?? path37.join(os32.homedir(), ".gemini", "tmp");
|
|
17584
|
+
const hasAuditFile = fs36.existsSync(auditLogPath);
|
|
17419
17585
|
const allEntries = opts.preloadedAuditEntries ?? parseAuditLog(auditLogPath);
|
|
17420
17586
|
const unackedDlp = allEntries.filter((e) => e.source === "response-dlp");
|
|
17421
17587
|
const { start, end } = getDateRange(period, now);
|
|
@@ -18113,12 +18279,12 @@ function registerDaemonCommand(program2) {
|
|
|
18113
18279
|
init_core();
|
|
18114
18280
|
init_daemon();
|
|
18115
18281
|
import chalk15 from "chalk";
|
|
18116
|
-
import
|
|
18117
|
-
import
|
|
18118
|
-
import
|
|
18282
|
+
import fs37 from "fs";
|
|
18283
|
+
import path38 from "path";
|
|
18284
|
+
import os33 from "os";
|
|
18119
18285
|
function readJson2(filePath) {
|
|
18120
18286
|
try {
|
|
18121
|
-
if (
|
|
18287
|
+
if (fs37.existsSync(filePath)) return JSON.parse(fs37.readFileSync(filePath, "utf-8"));
|
|
18122
18288
|
} catch {
|
|
18123
18289
|
}
|
|
18124
18290
|
return null;
|
|
@@ -18183,28 +18349,28 @@ function registerStatusCommand(program2) {
|
|
|
18183
18349
|
console.log("");
|
|
18184
18350
|
const modeLabel = settings.mode === "audit" ? chalk15.blue("audit") : settings.mode === "strict" ? chalk15.red("strict") : chalk15.white("standard");
|
|
18185
18351
|
console.log(` Mode: ${modeLabel}`);
|
|
18186
|
-
const projectConfig =
|
|
18187
|
-
const globalConfig =
|
|
18352
|
+
const projectConfig = path38.join(process.cwd(), "node9.config.json");
|
|
18353
|
+
const globalConfig = path38.join(os33.homedir(), ".node9", "config.json");
|
|
18188
18354
|
console.log(
|
|
18189
|
-
` Local: ${
|
|
18355
|
+
` Local: ${fs37.existsSync(projectConfig) ? chalk15.green("Active (node9.config.json)") : chalk15.gray("Not present")}`
|
|
18190
18356
|
);
|
|
18191
18357
|
console.log(
|
|
18192
|
-
` Global: ${
|
|
18358
|
+
` Global: ${fs37.existsSync(globalConfig) ? chalk15.green("Active (~/.node9/config.json)") : chalk15.gray("Not present")}`
|
|
18193
18359
|
);
|
|
18194
18360
|
if (mergedConfig.policy.sandboxPaths.length > 0) {
|
|
18195
18361
|
console.log(
|
|
18196
18362
|
` Sandbox: ${chalk15.green(`${mergedConfig.policy.sandboxPaths.length} safe zones active`)}`
|
|
18197
18363
|
);
|
|
18198
18364
|
}
|
|
18199
|
-
const homeDir2 =
|
|
18365
|
+
const homeDir2 = os33.homedir();
|
|
18200
18366
|
const claudeSettings = readJson2(
|
|
18201
|
-
|
|
18367
|
+
path38.join(homeDir2, ".claude", "settings.json")
|
|
18202
18368
|
);
|
|
18203
|
-
const claudeConfig = readJson2(
|
|
18369
|
+
const claudeConfig = readJson2(path38.join(homeDir2, ".claude.json"));
|
|
18204
18370
|
const geminiSettings = readJson2(
|
|
18205
|
-
|
|
18371
|
+
path38.join(homeDir2, ".gemini", "settings.json")
|
|
18206
18372
|
);
|
|
18207
|
-
const cursorConfig = readJson2(
|
|
18373
|
+
const cursorConfig = readJson2(path38.join(homeDir2, ".cursor", "mcp.json"));
|
|
18208
18374
|
const agentFound = claudeSettings || claudeConfig || geminiSettings || cursorConfig;
|
|
18209
18375
|
if (agentFound) {
|
|
18210
18376
|
console.log("");
|
|
@@ -18267,9 +18433,9 @@ init_setup();
|
|
|
18267
18433
|
init_shields();
|
|
18268
18434
|
init_service();
|
|
18269
18435
|
import chalk16 from "chalk";
|
|
18270
|
-
import
|
|
18271
|
-
import
|
|
18272
|
-
import
|
|
18436
|
+
import fs38 from "fs";
|
|
18437
|
+
import path39 from "path";
|
|
18438
|
+
import os34 from "os";
|
|
18273
18439
|
import https4 from "https";
|
|
18274
18440
|
var DEFAULT_SHIELDS = ["bash-safe", "filesystem", "project-jail"];
|
|
18275
18441
|
function fireTelemetryPing(agents) {
|
|
@@ -18345,15 +18511,15 @@ function registerInitCommand(program2) {
|
|
|
18345
18511
|
}
|
|
18346
18512
|
console.log("");
|
|
18347
18513
|
}
|
|
18348
|
-
const configPath =
|
|
18349
|
-
if (
|
|
18514
|
+
const configPath = path39.join(os34.homedir(), ".node9", "config.json");
|
|
18515
|
+
if (fs38.existsSync(configPath) && !options.force) {
|
|
18350
18516
|
try {
|
|
18351
|
-
const existing = JSON.parse(
|
|
18517
|
+
const existing = JSON.parse(fs38.readFileSync(configPath, "utf-8"));
|
|
18352
18518
|
const settings = existing.settings ?? {};
|
|
18353
18519
|
if (settings.mode !== chosenMode) {
|
|
18354
18520
|
settings.mode = chosenMode;
|
|
18355
18521
|
existing.settings = settings;
|
|
18356
|
-
|
|
18522
|
+
fs38.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
|
|
18357
18523
|
console.log(chalk16.green(`\u2705 Mode updated: ${chosenMode}`));
|
|
18358
18524
|
} else {
|
|
18359
18525
|
console.log(chalk16.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
|
|
@@ -18366,9 +18532,9 @@ function registerInitCommand(program2) {
|
|
|
18366
18532
|
...DEFAULT_CONFIG,
|
|
18367
18533
|
settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
|
|
18368
18534
|
};
|
|
18369
|
-
const dir =
|
|
18370
|
-
if (!
|
|
18371
|
-
|
|
18535
|
+
const dir = path39.dirname(configPath);
|
|
18536
|
+
if (!fs38.existsSync(dir)) fs38.mkdirSync(dir, { recursive: true });
|
|
18537
|
+
fs38.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
|
|
18372
18538
|
console.log(chalk16.green(`\u2705 Config created: ${configPath}`));
|
|
18373
18539
|
console.log(chalk16.gray(` Mode: ${chosenMode}`));
|
|
18374
18540
|
}
|
|
@@ -18466,7 +18632,7 @@ function registerInitCommand(program2) {
|
|
|
18466
18632
|
}
|
|
18467
18633
|
|
|
18468
18634
|
// src/cli/commands/undo.ts
|
|
18469
|
-
import
|
|
18635
|
+
import path40 from "path";
|
|
18470
18636
|
import chalk18 from "chalk";
|
|
18471
18637
|
|
|
18472
18638
|
// src/tui/undo-navigator.ts
|
|
@@ -18625,7 +18791,7 @@ function findMatchingCwd(startDir, history) {
|
|
|
18625
18791
|
let dir = startDir;
|
|
18626
18792
|
while (true) {
|
|
18627
18793
|
if (cwds.has(dir)) return dir;
|
|
18628
|
-
const parent =
|
|
18794
|
+
const parent = path40.dirname(dir);
|
|
18629
18795
|
if (parent === dir) return null;
|
|
18630
18796
|
dir = parent;
|
|
18631
18797
|
}
|
|
@@ -18758,90 +18924,7 @@ import chalk19 from "chalk";
|
|
|
18758
18924
|
import { spawn as spawn7 } from "child_process";
|
|
18759
18925
|
import { execa as execa2 } from "execa";
|
|
18760
18926
|
init_provenance();
|
|
18761
|
-
|
|
18762
|
-
// src/mcp-pin.ts
|
|
18763
|
-
import fs38 from "fs";
|
|
18764
|
-
import path40 from "path";
|
|
18765
|
-
import os34 from "os";
|
|
18766
|
-
import crypto5 from "crypto";
|
|
18767
|
-
function getPinsFilePath2() {
|
|
18768
|
-
return path40.join(os34.homedir(), ".node9", "mcp-pins.json");
|
|
18769
|
-
}
|
|
18770
|
-
function hashToolDefinitions(tools) {
|
|
18771
|
-
const sorted = [...tools].sort((a, b) => {
|
|
18772
|
-
const nameA = a.name ?? "";
|
|
18773
|
-
const nameB = b.name ?? "";
|
|
18774
|
-
return nameA.localeCompare(nameB);
|
|
18775
|
-
});
|
|
18776
|
-
const canonical = JSON.stringify(sorted);
|
|
18777
|
-
return crypto5.createHash("sha256").update(canonical).digest("hex");
|
|
18778
|
-
}
|
|
18779
|
-
function getServerKey(upstreamCommand) {
|
|
18780
|
-
return crypto5.createHash("sha256").update(upstreamCommand).digest("hex").slice(0, 16);
|
|
18781
|
-
}
|
|
18782
|
-
function readMcpPinsSafe() {
|
|
18783
|
-
const filePath = getPinsFilePath2();
|
|
18784
|
-
try {
|
|
18785
|
-
const raw = fs38.readFileSync(filePath, "utf-8");
|
|
18786
|
-
if (!raw.trim()) {
|
|
18787
|
-
return { ok: false, reason: "corrupt", detail: "empty file" };
|
|
18788
|
-
}
|
|
18789
|
-
const parsed = JSON.parse(raw);
|
|
18790
|
-
if (!parsed.servers || typeof parsed.servers !== "object" || Array.isArray(parsed.servers)) {
|
|
18791
|
-
return { ok: false, reason: "corrupt", detail: "invalid structure: missing servers object" };
|
|
18792
|
-
}
|
|
18793
|
-
return { ok: true, pins: { servers: parsed.servers } };
|
|
18794
|
-
} catch (err2) {
|
|
18795
|
-
if (err2.code === "ENOENT") {
|
|
18796
|
-
return { ok: false, reason: "missing" };
|
|
18797
|
-
}
|
|
18798
|
-
return { ok: false, reason: "corrupt", detail: String(err2) };
|
|
18799
|
-
}
|
|
18800
|
-
}
|
|
18801
|
-
function readMcpPins() {
|
|
18802
|
-
const result = readMcpPinsSafe();
|
|
18803
|
-
if (result.ok) return result.pins;
|
|
18804
|
-
if (result.reason === "missing") return { servers: {} };
|
|
18805
|
-
throw new Error(`[node9] MCP pin file is corrupt: ${result.detail}`);
|
|
18806
|
-
}
|
|
18807
|
-
function writeMcpPins(data) {
|
|
18808
|
-
const filePath = getPinsFilePath2();
|
|
18809
|
-
fs38.mkdirSync(path40.dirname(filePath), { recursive: true });
|
|
18810
|
-
const tmp = `${filePath}.${crypto5.randomBytes(6).toString("hex")}.tmp`;
|
|
18811
|
-
fs38.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
18812
|
-
fs38.renameSync(tmp, filePath);
|
|
18813
|
-
}
|
|
18814
|
-
function checkPin(serverKey, currentHash) {
|
|
18815
|
-
const result = readMcpPinsSafe();
|
|
18816
|
-
if (!result.ok) {
|
|
18817
|
-
if (result.reason === "missing") return "new";
|
|
18818
|
-
return "corrupt";
|
|
18819
|
-
}
|
|
18820
|
-
const entry = result.pins.servers[serverKey];
|
|
18821
|
-
if (!entry) return "new";
|
|
18822
|
-
return entry.toolsHash === currentHash ? "match" : "mismatch";
|
|
18823
|
-
}
|
|
18824
|
-
function updatePin(serverKey, label, toolsHash, toolNames) {
|
|
18825
|
-
const pins = readMcpPins();
|
|
18826
|
-
pins.servers[serverKey] = {
|
|
18827
|
-
label,
|
|
18828
|
-
toolsHash,
|
|
18829
|
-
toolNames,
|
|
18830
|
-
toolCount: toolNames.length,
|
|
18831
|
-
pinnedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
18832
|
-
};
|
|
18833
|
-
writeMcpPins(pins);
|
|
18834
|
-
}
|
|
18835
|
-
function removePin2(serverKey) {
|
|
18836
|
-
const pins = readMcpPins();
|
|
18837
|
-
delete pins.servers[serverKey];
|
|
18838
|
-
writeMcpPins(pins);
|
|
18839
|
-
}
|
|
18840
|
-
function clearAllPins2() {
|
|
18841
|
-
writeMcpPins({ servers: {} });
|
|
18842
|
-
}
|
|
18843
|
-
|
|
18844
|
-
// src/mcp-gateway/index.ts
|
|
18927
|
+
init_mcp_pin();
|
|
18845
18928
|
init_mcp_tools();
|
|
18846
18929
|
init_daemon();
|
|
18847
18930
|
function sanitize4(value) {
|
|
@@ -18903,6 +18986,7 @@ function tokenize4(cmd) {
|
|
|
18903
18986
|
return tokens;
|
|
18904
18987
|
}
|
|
18905
18988
|
async function runMcpGateway(upstreamCommand) {
|
|
18989
|
+
const gatewayCwd = process.cwd();
|
|
18906
18990
|
const commandParts = tokenize4(upstreamCommand);
|
|
18907
18991
|
const cmd = commandParts[0];
|
|
18908
18992
|
const cmdArgs = commandParts.slice(1);
|
|
@@ -19125,7 +19209,7 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
19125
19209
|
if (parsed.result && Array.isArray(parsed.result.tools)) {
|
|
19126
19210
|
const tools = parsed.result.tools || [];
|
|
19127
19211
|
const currentHash = hashToolDefinitions(tools);
|
|
19128
|
-
const pinStatus = checkPin(serverKey, currentHash);
|
|
19212
|
+
const pinStatus = checkPin(serverKey, currentHash, gatewayCwd);
|
|
19129
19213
|
const token = getInternalToken();
|
|
19130
19214
|
if (isDaemonRunning() && process.env.NODE9_TESTING !== "1") {
|
|
19131
19215
|
const toolSummary = tools.map((t) => ({ name: t.name, description: t.description }));
|
|
@@ -20009,27 +20093,46 @@ function registerTrustCommand(program2) {
|
|
|
20009
20093
|
}
|
|
20010
20094
|
|
|
20011
20095
|
// src/cli/commands/mcp-pin.ts
|
|
20096
|
+
init_mcp_pin();
|
|
20012
20097
|
import chalk21 from "chalk";
|
|
20098
|
+
import fs40 from "fs";
|
|
20013
20099
|
function registerMcpPinCommand(program2) {
|
|
20014
20100
|
const pinCmd = program2.command("mcp").description("Manage MCP server tool definition pinning (rug pull defense)");
|
|
20015
20101
|
const pinSubCmd = pinCmd.command("pin").description("Manage pinned MCP server tool definitions");
|
|
20016
20102
|
pinSubCmd.command("list").description("Show all pinned MCP servers and their tool definition hashes").action(() => {
|
|
20017
|
-
const
|
|
20018
|
-
|
|
20019
|
-
|
|
20020
|
-
|
|
20021
|
-
|
|
20022
|
-
|
|
20023
|
-
);
|
|
20024
|
-
|
|
20103
|
+
const found = findPinsFilePath(process.cwd());
|
|
20104
|
+
const homeResult = readMcpPinsSafe();
|
|
20105
|
+
let repoEntries = {};
|
|
20106
|
+
let repoCorrupt = false;
|
|
20107
|
+
if (found.source === "repo") {
|
|
20108
|
+
try {
|
|
20109
|
+
const raw = fs40.readFileSync(found.path, "utf-8");
|
|
20110
|
+
const parsed = JSON.parse(raw);
|
|
20111
|
+
repoEntries = parsed.servers ?? {};
|
|
20112
|
+
} catch {
|
|
20113
|
+
repoCorrupt = true;
|
|
20025
20114
|
}
|
|
20115
|
+
}
|
|
20116
|
+
if (repoCorrupt) {
|
|
20026
20117
|
console.error(chalk21.red(`
|
|
20027
|
-
\u274C
|
|
20118
|
+
\u274C Repo pin file at ${found.path} is corrupt or unreadable.`));
|
|
20119
|
+
process.exit(1);
|
|
20120
|
+
}
|
|
20121
|
+
if (!homeResult.ok && homeResult.reason === "corrupt") {
|
|
20122
|
+
console.error(chalk21.red(`
|
|
20123
|
+
\u274C Home pin file is corrupt: ${homeResult.detail}`));
|
|
20028
20124
|
console.error(chalk21.yellow(" Run: node9 mcp pin reset\n"));
|
|
20029
20125
|
process.exit(1);
|
|
20030
20126
|
}
|
|
20031
|
-
const
|
|
20032
|
-
|
|
20127
|
+
const homeEntries = homeResult.ok ? homeResult.pins.servers : {};
|
|
20128
|
+
const merged = /* @__PURE__ */ new Map();
|
|
20129
|
+
for (const [key, entry] of Object.entries(homeEntries)) {
|
|
20130
|
+
merged.set(key, { entry, source: "home" });
|
|
20131
|
+
}
|
|
20132
|
+
for (const [key, entry] of Object.entries(repoEntries)) {
|
|
20133
|
+
merged.set(key, { entry, source: "repo" });
|
|
20134
|
+
}
|
|
20135
|
+
if (merged.size === 0) {
|
|
20033
20136
|
console.log(chalk21.gray("\nNo MCP servers are pinned yet."));
|
|
20034
20137
|
console.log(
|
|
20035
20138
|
chalk21.gray("Pins are created automatically when the MCP gateway first connects.\n")
|
|
@@ -20037,13 +20140,47 @@ function registerMcpPinCommand(program2) {
|
|
|
20037
20140
|
return;
|
|
20038
20141
|
}
|
|
20039
20142
|
console.log(chalk21.bold("\n\u{1F512} Pinned MCP Servers\n"));
|
|
20040
|
-
|
|
20041
|
-
|
|
20143
|
+
const showSource = found.source === "repo";
|
|
20144
|
+
for (const [key, { entry, source }] of merged) {
|
|
20145
|
+
const tag = showSource ? ` ${chalk21.yellow(`[${source}]`)}` : "";
|
|
20146
|
+
console.log(` ${chalk21.cyan(key)}${tag} ${chalk21.gray(entry.label)}`);
|
|
20042
20147
|
console.log(` Tools (${entry.toolCount}): ${chalk21.white(entry.toolNames.join(", "))}`);
|
|
20043
20148
|
console.log(` Hash: ${chalk21.gray(entry.toolsHash.slice(0, 16))}...`);
|
|
20044
20149
|
console.log(` Pinned: ${chalk21.gray(entry.pinnedAt)}`);
|
|
20045
20150
|
console.log("");
|
|
20046
20151
|
}
|
|
20152
|
+
if (showSource) {
|
|
20153
|
+
console.log(chalk21.gray(` [repo] entries come from ${found.path}`));
|
|
20154
|
+
console.log(chalk21.gray(" [home] entries come from ~/.node9/mcp-pins.json\n"));
|
|
20155
|
+
}
|
|
20156
|
+
});
|
|
20157
|
+
pinSubCmd.command("promote <serverKey>").description(
|
|
20158
|
+
"Copy a pin from ~/.node9/mcp-pins.json into <repo>/.node9/mcp-pins.json so teammates share the same vetted baseline"
|
|
20159
|
+
).action((serverKey) => {
|
|
20160
|
+
try {
|
|
20161
|
+
const { repoPath, created } = promotePin(serverKey, process.cwd());
|
|
20162
|
+
if (created) {
|
|
20163
|
+
console.log(
|
|
20164
|
+
chalk21.green(
|
|
20165
|
+
`
|
|
20166
|
+
\u2705 Created ${repoPath} with the promoted pin for ${chalk21.cyan(serverKey)}.`
|
|
20167
|
+
)
|
|
20168
|
+
);
|
|
20169
|
+
} else {
|
|
20170
|
+
console.log(chalk21.green(`
|
|
20171
|
+
\u2705 Promoted ${chalk21.cyan(serverKey)} into ${repoPath}.`));
|
|
20172
|
+
}
|
|
20173
|
+
console.log(chalk21.gray(" Review the change and commit it:"));
|
|
20174
|
+
console.log(chalk21.cyan(` git add ${repoPath}`));
|
|
20175
|
+
console.log(chalk21.cyan(` git commit -m "pin ${serverKey} (node9)"`));
|
|
20176
|
+
console.log("");
|
|
20177
|
+
} catch (err2) {
|
|
20178
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
20179
|
+
console.error(chalk21.red(`
|
|
20180
|
+
\u274C ${msg}
|
|
20181
|
+
`));
|
|
20182
|
+
process.exit(1);
|
|
20183
|
+
}
|
|
20047
20184
|
});
|
|
20048
20185
|
pinSubCmd.command("update <serverKey>").description(
|
|
20049
20186
|
"Remove a pin so the next gateway connection re-pins with current tool definitions"
|
|
@@ -20065,7 +20202,7 @@ function registerMcpPinCommand(program2) {
|
|
|
20065
20202
|
process.exit(1);
|
|
20066
20203
|
}
|
|
20067
20204
|
const label = pins.servers[serverKey].label;
|
|
20068
|
-
|
|
20205
|
+
removePin(serverKey);
|
|
20069
20206
|
console.log(chalk21.green(`
|
|
20070
20207
|
\u{1F513} Pin removed for ${chalk21.cyan(serverKey)}`));
|
|
20071
20208
|
console.log(chalk21.gray(` Server: ${label}`));
|
|
@@ -20078,7 +20215,7 @@ function registerMcpPinCommand(program2) {
|
|
|
20078
20215
|
return;
|
|
20079
20216
|
}
|
|
20080
20217
|
const count = result.ok ? Object.keys(result.pins.servers).length : "?";
|
|
20081
|
-
|
|
20218
|
+
clearAllPins();
|
|
20082
20219
|
console.log(chalk21.green(`
|
|
20083
20220
|
\u{1F513} Cleared ${count} MCP pin(s).`));
|
|
20084
20221
|
console.log(chalk21.gray(" Next connection to each server will re-pin.\n"));
|
|
@@ -20261,7 +20398,7 @@ init_scan();
|
|
|
20261
20398
|
|
|
20262
20399
|
// src/cli/commands/sessions.ts
|
|
20263
20400
|
import chalk24 from "chalk";
|
|
20264
|
-
import
|
|
20401
|
+
import fs41 from "fs";
|
|
20265
20402
|
import path42 from "path";
|
|
20266
20403
|
import os36 from "os";
|
|
20267
20404
|
var CLAUDE_PRICING3 = {
|
|
@@ -20379,7 +20516,7 @@ function loadAuditEntries(auditPath) {
|
|
|
20379
20516
|
const aPath = auditPath ?? path42.join(os36.homedir(), ".node9", "audit.log");
|
|
20380
20517
|
let raw;
|
|
20381
20518
|
try {
|
|
20382
|
-
raw =
|
|
20519
|
+
raw = fs41.readFileSync(aPath, "utf-8");
|
|
20383
20520
|
} catch {
|
|
20384
20521
|
return [];
|
|
20385
20522
|
}
|
|
@@ -20416,7 +20553,7 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
|
|
|
20416
20553
|
}
|
|
20417
20554
|
function buildGeminiSessions(days, allAuditEntries) {
|
|
20418
20555
|
const tmpDir = path42.join(os36.homedir(), ".gemini", "tmp");
|
|
20419
|
-
if (!
|
|
20556
|
+
if (!fs41.existsSync(tmpDir)) return [];
|
|
20420
20557
|
const cutoff = days !== null ? (() => {
|
|
20421
20558
|
const d = /* @__PURE__ */ new Date();
|
|
20422
20559
|
d.setDate(d.getDate() - days);
|
|
@@ -20425,7 +20562,7 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
20425
20562
|
})() : null;
|
|
20426
20563
|
let slugDirs;
|
|
20427
20564
|
try {
|
|
20428
|
-
slugDirs =
|
|
20565
|
+
slugDirs = fs41.readdirSync(tmpDir);
|
|
20429
20566
|
} catch {
|
|
20430
20567
|
return [];
|
|
20431
20568
|
}
|
|
@@ -20433,27 +20570,27 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
20433
20570
|
for (const slug of slugDirs) {
|
|
20434
20571
|
const slugPath = path42.join(tmpDir, slug);
|
|
20435
20572
|
try {
|
|
20436
|
-
if (!
|
|
20573
|
+
if (!fs41.statSync(slugPath).isDirectory()) continue;
|
|
20437
20574
|
} catch {
|
|
20438
20575
|
continue;
|
|
20439
20576
|
}
|
|
20440
20577
|
let projectRoot = path42.join(os36.homedir(), slug);
|
|
20441
20578
|
try {
|
|
20442
|
-
projectRoot =
|
|
20579
|
+
projectRoot = fs41.readFileSync(path42.join(slugPath, ".project_root"), "utf-8").trim();
|
|
20443
20580
|
} catch {
|
|
20444
20581
|
}
|
|
20445
20582
|
const chatsDir = path42.join(slugPath, "chats");
|
|
20446
|
-
if (!
|
|
20583
|
+
if (!fs41.existsSync(chatsDir)) continue;
|
|
20447
20584
|
let chatFiles;
|
|
20448
20585
|
try {
|
|
20449
|
-
chatFiles =
|
|
20586
|
+
chatFiles = fs41.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
|
|
20450
20587
|
} catch {
|
|
20451
20588
|
continue;
|
|
20452
20589
|
}
|
|
20453
20590
|
for (const chatFile of chatFiles) {
|
|
20454
20591
|
let raw;
|
|
20455
20592
|
try {
|
|
20456
|
-
raw =
|
|
20593
|
+
raw = fs41.readFileSync(path42.join(chatsDir, chatFile), "utf-8");
|
|
20457
20594
|
} catch {
|
|
20458
20595
|
continue;
|
|
20459
20596
|
}
|
|
@@ -20534,7 +20671,7 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
20534
20671
|
}
|
|
20535
20672
|
function buildCodexSessions(days, allAuditEntries) {
|
|
20536
20673
|
const sessionsBase = path42.join(os36.homedir(), ".codex", "sessions");
|
|
20537
|
-
if (!
|
|
20674
|
+
if (!fs41.existsSync(sessionsBase)) return [];
|
|
20538
20675
|
const cutoff = days !== null ? (() => {
|
|
20539
20676
|
const d = /* @__PURE__ */ new Date();
|
|
20540
20677
|
d.setDate(d.getDate() - days);
|
|
@@ -20543,28 +20680,28 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
20543
20680
|
})() : null;
|
|
20544
20681
|
const jsonlFiles = [];
|
|
20545
20682
|
try {
|
|
20546
|
-
for (const year of
|
|
20683
|
+
for (const year of fs41.readdirSync(sessionsBase)) {
|
|
20547
20684
|
const yearPath = path42.join(sessionsBase, year);
|
|
20548
20685
|
try {
|
|
20549
|
-
if (!
|
|
20686
|
+
if (!fs41.statSync(yearPath).isDirectory()) continue;
|
|
20550
20687
|
} catch {
|
|
20551
20688
|
continue;
|
|
20552
20689
|
}
|
|
20553
|
-
for (const month of
|
|
20690
|
+
for (const month of fs41.readdirSync(yearPath)) {
|
|
20554
20691
|
const monthPath = path42.join(yearPath, month);
|
|
20555
20692
|
try {
|
|
20556
|
-
if (!
|
|
20693
|
+
if (!fs41.statSync(monthPath).isDirectory()) continue;
|
|
20557
20694
|
} catch {
|
|
20558
20695
|
continue;
|
|
20559
20696
|
}
|
|
20560
|
-
for (const day of
|
|
20697
|
+
for (const day of fs41.readdirSync(monthPath)) {
|
|
20561
20698
|
const dayPath = path42.join(monthPath, day);
|
|
20562
20699
|
try {
|
|
20563
|
-
if (!
|
|
20700
|
+
if (!fs41.statSync(dayPath).isDirectory()) continue;
|
|
20564
20701
|
} catch {
|
|
20565
20702
|
continue;
|
|
20566
20703
|
}
|
|
20567
|
-
for (const file of
|
|
20704
|
+
for (const file of fs41.readdirSync(dayPath)) {
|
|
20568
20705
|
if (file.endsWith(".jsonl")) jsonlFiles.push(path42.join(dayPath, file));
|
|
20569
20706
|
}
|
|
20570
20707
|
}
|
|
@@ -20577,7 +20714,7 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
20577
20714
|
for (const filePath of jsonlFiles) {
|
|
20578
20715
|
let lines;
|
|
20579
20716
|
try {
|
|
20580
|
-
lines =
|
|
20717
|
+
lines = fs41.readFileSync(filePath, "utf-8").split("\n");
|
|
20581
20718
|
} catch {
|
|
20582
20719
|
continue;
|
|
20583
20720
|
}
|
|
@@ -20658,7 +20795,7 @@ function buildSessions(days, historyPath) {
|
|
|
20658
20795
|
const hPath = historyPath ?? path42.join(os36.homedir(), ".claude", "history.jsonl");
|
|
20659
20796
|
let historyRaw;
|
|
20660
20797
|
try {
|
|
20661
|
-
historyRaw =
|
|
20798
|
+
historyRaw = fs41.readFileSync(hPath, "utf-8");
|
|
20662
20799
|
} catch {
|
|
20663
20800
|
return [];
|
|
20664
20801
|
}
|
|
@@ -20683,7 +20820,7 @@ function buildSessions(days, historyPath) {
|
|
|
20683
20820
|
const jsonlFile = sessionJsonlPath(entry.project, entry.sessionId);
|
|
20684
20821
|
let sessionLines = [];
|
|
20685
20822
|
try {
|
|
20686
|
-
sessionLines =
|
|
20823
|
+
sessionLines = fs41.readFileSync(jsonlFile, "utf-8").split("\n");
|
|
20687
20824
|
} catch {
|
|
20688
20825
|
}
|
|
20689
20826
|
const { toolCalls, costUSD, hasSnapshot, modifiedFiles } = parseSessionLines(sessionLines);
|
|
@@ -20950,7 +21087,7 @@ function registerSessionsCommand(program2) {
|
|
|
20950
21087
|
console.log(chalk24.cyan.bold("\u{1F4CB} node9 sessions") + chalk24.dim(" \u2014 what your AI agent did"));
|
|
20951
21088
|
console.log("");
|
|
20952
21089
|
const historyPath = path42.join(os36.homedir(), ".claude", "history.jsonl");
|
|
20953
|
-
if (!
|
|
21090
|
+
if (!fs41.existsSync(historyPath)) {
|
|
20954
21091
|
console.log(chalk24.yellow(" No Claude session history found at ~/.claude/history.jsonl"));
|
|
20955
21092
|
console.log(chalk24.gray(" Install Claude Code, run a few sessions, then try again.\n"));
|
|
20956
21093
|
return;
|
|
@@ -20987,12 +21124,12 @@ function registerSessionsCommand(program2) {
|
|
|
20987
21124
|
|
|
20988
21125
|
// src/cli/commands/skill-pin.ts
|
|
20989
21126
|
import chalk25 from "chalk";
|
|
20990
|
-
import
|
|
21127
|
+
import fs42 from "fs";
|
|
20991
21128
|
import os37 from "os";
|
|
20992
21129
|
import path43 from "path";
|
|
20993
21130
|
function wipeSkillSessions() {
|
|
20994
21131
|
try {
|
|
20995
|
-
|
|
21132
|
+
fs42.rmSync(path43.join(os37.homedir(), ".node9", "skill-sessions"), {
|
|
20996
21133
|
recursive: true,
|
|
20997
21134
|
force: true
|
|
20998
21135
|
});
|
|
@@ -21050,7 +21187,7 @@ function registerSkillPinCommand(program2) {
|
|
|
21050
21187
|
process.exit(1);
|
|
21051
21188
|
}
|
|
21052
21189
|
const rootPath = pins.roots[rootKey].rootPath;
|
|
21053
|
-
|
|
21190
|
+
removePin2(rootKey);
|
|
21054
21191
|
wipeSkillSessions();
|
|
21055
21192
|
console.log(chalk25.green(`
|
|
21056
21193
|
\u{1F513} Pin removed for ${chalk25.cyan(rootKey)}`));
|
|
@@ -21065,7 +21202,7 @@ function registerSkillPinCommand(program2) {
|
|
|
21065
21202
|
return;
|
|
21066
21203
|
}
|
|
21067
21204
|
const count = result.ok ? Object.keys(result.pins.roots).length : "?";
|
|
21068
|
-
|
|
21205
|
+
clearAllPins2();
|
|
21069
21206
|
wipeSkillSessions();
|
|
21070
21207
|
console.log(chalk25.green(`
|
|
21071
21208
|
\u{1F513} Cleared ${count} skill pin(s).`));
|
|
@@ -21074,15 +21211,15 @@ function registerSkillPinCommand(program2) {
|
|
|
21074
21211
|
}
|
|
21075
21212
|
|
|
21076
21213
|
// src/cli/commands/decisions.ts
|
|
21077
|
-
import
|
|
21214
|
+
import fs43 from "fs";
|
|
21078
21215
|
import os38 from "os";
|
|
21079
21216
|
import path44 from "path";
|
|
21080
21217
|
import chalk26 from "chalk";
|
|
21081
21218
|
var DECISIONS_FILE2 = path44.join(os38.homedir(), ".node9", "decisions.json");
|
|
21082
21219
|
function readDecisions() {
|
|
21083
21220
|
try {
|
|
21084
|
-
if (!
|
|
21085
|
-
const raw =
|
|
21221
|
+
if (!fs43.existsSync(DECISIONS_FILE2)) return {};
|
|
21222
|
+
const raw = fs43.readFileSync(DECISIONS_FILE2, "utf-8");
|
|
21086
21223
|
const parsed = JSON.parse(raw);
|
|
21087
21224
|
const out = {};
|
|
21088
21225
|
for (const [k, v] of Object.entries(parsed)) {
|
|
@@ -21095,10 +21232,10 @@ function readDecisions() {
|
|
|
21095
21232
|
}
|
|
21096
21233
|
function writeDecisions(d) {
|
|
21097
21234
|
const dir = path44.dirname(DECISIONS_FILE2);
|
|
21098
|
-
if (!
|
|
21235
|
+
if (!fs43.existsSync(dir)) fs43.mkdirSync(dir, { recursive: true });
|
|
21099
21236
|
const tmp = `${DECISIONS_FILE2}.${process.pid}.tmp`;
|
|
21100
|
-
|
|
21101
|
-
|
|
21237
|
+
fs43.writeFileSync(tmp, JSON.stringify(d, null, 2));
|
|
21238
|
+
fs43.renameSync(tmp, DECISIONS_FILE2);
|
|
21102
21239
|
}
|
|
21103
21240
|
function registerDecisionsCommand(program2) {
|
|
21104
21241
|
const cmd = program2.command("decisions").description('Manage persistent "Always Allow" / "Always Deny" tool decisions');
|
|
@@ -21155,7 +21292,7 @@ Persistent decisions (${entries.length})
|
|
|
21155
21292
|
|
|
21156
21293
|
// src/cli/commands/dlp.ts
|
|
21157
21294
|
import chalk27 from "chalk";
|
|
21158
|
-
import
|
|
21295
|
+
import fs44 from "fs";
|
|
21159
21296
|
import path45 from "path";
|
|
21160
21297
|
import os39 from "os";
|
|
21161
21298
|
var AUDIT_LOG = path45.join(os39.homedir(), ".node9", "audit.log");
|
|
@@ -21166,7 +21303,7 @@ function stripAnsi(s) {
|
|
|
21166
21303
|
}
|
|
21167
21304
|
function loadResolved() {
|
|
21168
21305
|
try {
|
|
21169
|
-
const raw = JSON.parse(
|
|
21306
|
+
const raw = JSON.parse(fs44.readFileSync(RESOLVED_FILE, "utf-8"));
|
|
21170
21307
|
return new Set(raw);
|
|
21171
21308
|
} catch {
|
|
21172
21309
|
return /* @__PURE__ */ new Set();
|
|
@@ -21174,13 +21311,13 @@ function loadResolved() {
|
|
|
21174
21311
|
}
|
|
21175
21312
|
function saveResolved(resolved) {
|
|
21176
21313
|
try {
|
|
21177
|
-
|
|
21314
|
+
fs44.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
|
|
21178
21315
|
} catch {
|
|
21179
21316
|
}
|
|
21180
21317
|
}
|
|
21181
21318
|
function loadDlpFindings() {
|
|
21182
|
-
if (!
|
|
21183
|
-
return
|
|
21319
|
+
if (!fs44.existsSync(AUDIT_LOG)) return [];
|
|
21320
|
+
return fs44.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
|
|
21184
21321
|
if (!line.trim()) return [];
|
|
21185
21322
|
try {
|
|
21186
21323
|
const e = JSON.parse(line);
|
|
@@ -21279,13 +21416,13 @@ function registerDlpCommand(program2) {
|
|
|
21279
21416
|
// src/cli/commands/mask.ts
|
|
21280
21417
|
init_dlp();
|
|
21281
21418
|
import chalk28 from "chalk";
|
|
21282
|
-
import
|
|
21419
|
+
import fs45 from "fs";
|
|
21283
21420
|
import path46 from "path";
|
|
21284
21421
|
import os40 from "os";
|
|
21285
21422
|
function findJsonlFiles(dir) {
|
|
21286
21423
|
const results = [];
|
|
21287
|
-
if (!
|
|
21288
|
-
for (const entry of
|
|
21424
|
+
if (!fs45.existsSync(dir)) return results;
|
|
21425
|
+
for (const entry of fs45.readdirSync(dir, { withFileTypes: true })) {
|
|
21289
21426
|
const full = path46.join(dir, entry.name);
|
|
21290
21427
|
if (entry.isDirectory()) results.push(...findJsonlFiles(full));
|
|
21291
21428
|
else if (entry.isFile() && entry.name.endsWith(".jsonl")) results.push(full);
|
|
@@ -21329,7 +21466,7 @@ function redactJson(obj) {
|
|
|
21329
21466
|
function processFile(filePath, dryRun) {
|
|
21330
21467
|
let raw;
|
|
21331
21468
|
try {
|
|
21332
|
-
raw =
|
|
21469
|
+
raw = fs45.readFileSync(filePath, "utf-8");
|
|
21333
21470
|
} catch {
|
|
21334
21471
|
return { redactedLines: 0, patterns: [] };
|
|
21335
21472
|
}
|
|
@@ -21361,14 +21498,14 @@ function processFile(filePath, dryRun) {
|
|
|
21361
21498
|
}
|
|
21362
21499
|
}
|
|
21363
21500
|
if (!dryRun && redactedLines > 0) {
|
|
21364
|
-
|
|
21501
|
+
fs45.writeFileSync(filePath, newLines.join("\n"), "utf-8");
|
|
21365
21502
|
}
|
|
21366
21503
|
return { redactedLines, patterns };
|
|
21367
21504
|
}
|
|
21368
21505
|
function processJsonFile(filePath, dryRun) {
|
|
21369
21506
|
let raw;
|
|
21370
21507
|
try {
|
|
21371
|
-
raw =
|
|
21508
|
+
raw = fs45.readFileSync(filePath, "utf-8");
|
|
21372
21509
|
} catch {
|
|
21373
21510
|
return { redactedLines: 0, patterns: [] };
|
|
21374
21511
|
}
|
|
@@ -21381,14 +21518,14 @@ function processJsonFile(filePath, dryRun) {
|
|
|
21381
21518
|
const { value, modified, found } = redactJson(parsed);
|
|
21382
21519
|
if (!modified) return { redactedLines: 0, patterns: [] };
|
|
21383
21520
|
if (!dryRun) {
|
|
21384
|
-
|
|
21521
|
+
fs45.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
|
|
21385
21522
|
}
|
|
21386
21523
|
return { redactedLines: 1, patterns: found };
|
|
21387
21524
|
}
|
|
21388
21525
|
function findJsonFiles(dir) {
|
|
21389
21526
|
const results = [];
|
|
21390
|
-
if (!
|
|
21391
|
-
for (const entry of
|
|
21527
|
+
if (!fs45.existsSync(dir)) return results;
|
|
21528
|
+
for (const entry of fs45.readdirSync(dir, { withFileTypes: true })) {
|
|
21392
21529
|
const full = path46.join(dir, entry.name);
|
|
21393
21530
|
if (entry.isDirectory()) results.push(...findJsonFiles(full));
|
|
21394
21531
|
else if (entry.isFile() && entry.name.endsWith(".json")) results.push(full);
|
|
@@ -21408,7 +21545,7 @@ function registerMaskCommand(program2) {
|
|
|
21408
21545
|
const cutoff = options.all ? null : new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
|
|
21409
21546
|
const filtered = cutoff ? allFiles.filter((f) => {
|
|
21410
21547
|
try {
|
|
21411
|
-
return
|
|
21548
|
+
return fs45.statSync(f.path).mtime >= cutoff;
|
|
21412
21549
|
} catch {
|
|
21413
21550
|
return false;
|
|
21414
21551
|
}
|
|
@@ -21464,20 +21601,20 @@ function registerMaskCommand(program2) {
|
|
|
21464
21601
|
// src/cli.ts
|
|
21465
21602
|
init_blast();
|
|
21466
21603
|
var { version } = JSON.parse(
|
|
21467
|
-
|
|
21604
|
+
fs48.readFileSync(path49.join(__dirname, "../package.json"), "utf-8")
|
|
21468
21605
|
);
|
|
21469
21606
|
var program = new Command();
|
|
21470
21607
|
program.name("node9").description("The Sudo Command for AI Agents").version(version);
|
|
21471
21608
|
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) => {
|
|
21472
21609
|
const DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept";
|
|
21473
21610
|
const credPath = path49.join(os43.homedir(), ".node9", "credentials.json");
|
|
21474
|
-
if (!
|
|
21475
|
-
|
|
21611
|
+
if (!fs48.existsSync(path49.dirname(credPath)))
|
|
21612
|
+
fs48.mkdirSync(path49.dirname(credPath), { recursive: true });
|
|
21476
21613
|
const profileName = options.profile || "default";
|
|
21477
21614
|
let existingCreds = {};
|
|
21478
21615
|
try {
|
|
21479
|
-
if (
|
|
21480
|
-
const raw = JSON.parse(
|
|
21616
|
+
if (fs48.existsSync(credPath)) {
|
|
21617
|
+
const raw = JSON.parse(fs48.readFileSync(credPath, "utf-8"));
|
|
21481
21618
|
if (raw.apiKey) {
|
|
21482
21619
|
existingCreds = {
|
|
21483
21620
|
default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL2 }
|
|
@@ -21489,13 +21626,13 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
21489
21626
|
} catch {
|
|
21490
21627
|
}
|
|
21491
21628
|
existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL2 };
|
|
21492
|
-
|
|
21629
|
+
fs48.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
|
|
21493
21630
|
if (profileName === "default") {
|
|
21494
21631
|
const configPath = path49.join(os43.homedir(), ".node9", "config.json");
|
|
21495
21632
|
let config = {};
|
|
21496
21633
|
try {
|
|
21497
|
-
if (
|
|
21498
|
-
config = JSON.parse(
|
|
21634
|
+
if (fs48.existsSync(configPath))
|
|
21635
|
+
config = JSON.parse(fs48.readFileSync(configPath, "utf-8"));
|
|
21499
21636
|
} catch {
|
|
21500
21637
|
}
|
|
21501
21638
|
if (!config.settings || typeof config.settings !== "object") config.settings = {};
|
|
@@ -21510,9 +21647,9 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
21510
21647
|
approvers.cloud = false;
|
|
21511
21648
|
}
|
|
21512
21649
|
s.approvers = approvers;
|
|
21513
|
-
if (!
|
|
21514
|
-
|
|
21515
|
-
|
|
21650
|
+
if (!fs48.existsSync(path49.dirname(configPath)))
|
|
21651
|
+
fs48.mkdirSync(path49.dirname(configPath), { recursive: true });
|
|
21652
|
+
fs48.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
21516
21653
|
}
|
|
21517
21654
|
if (options.profile && profileName !== "default") {
|
|
21518
21655
|
console.log(chalk30.green(`\u2705 Profile "${profileName}" saved`));
|
|
@@ -21650,14 +21787,14 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
21650
21787
|
}
|
|
21651
21788
|
if (options.purge) {
|
|
21652
21789
|
const node9Dir = path49.join(os43.homedir(), ".node9");
|
|
21653
|
-
if (
|
|
21790
|
+
if (fs48.existsSync(node9Dir)) {
|
|
21654
21791
|
const confirmed = await confirm2({
|
|
21655
21792
|
message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
|
|
21656
21793
|
default: false
|
|
21657
21794
|
});
|
|
21658
21795
|
if (confirmed) {
|
|
21659
|
-
|
|
21660
|
-
if (
|
|
21796
|
+
fs48.rmSync(node9Dir, { recursive: true });
|
|
21797
|
+
if (fs48.existsSync(node9Dir)) {
|
|
21661
21798
|
console.error(
|
|
21662
21799
|
chalk30.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
|
|
21663
21800
|
);
|
|
@@ -21812,12 +21949,12 @@ Run "node9 addto claude" to register it as the statusLine.`
|
|
|
21812
21949
|
if (subcommand === "debug") {
|
|
21813
21950
|
const flagFile = path49.join(os43.homedir(), ".node9", "hud-debug");
|
|
21814
21951
|
if (state === "on") {
|
|
21815
|
-
|
|
21816
|
-
|
|
21952
|
+
fs48.mkdirSync(path49.dirname(flagFile), { recursive: true });
|
|
21953
|
+
fs48.writeFileSync(flagFile, "");
|
|
21817
21954
|
console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
|
|
21818
21955
|
console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
|
|
21819
21956
|
} else if (state === "off") {
|
|
21820
|
-
if (
|
|
21957
|
+
if (fs48.existsSync(flagFile)) fs48.unlinkSync(flagFile);
|
|
21821
21958
|
console.log("HUD debug logging disabled.");
|
|
21822
21959
|
} else {
|
|
21823
21960
|
console.error("Usage: node9 hud debug on|off");
|
|
@@ -21933,7 +22070,7 @@ if (process.argv[2] !== "daemon") {
|
|
|
21933
22070
|
if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
|
|
21934
22071
|
const logPath = path49.join(os43.homedir(), ".node9", "hook-debug.log");
|
|
21935
22072
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
21936
|
-
|
|
22073
|
+
fs48.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
|
|
21937
22074
|
`);
|
|
21938
22075
|
}
|
|
21939
22076
|
process.exit(0);
|