@node9/proxy 1.22.0 → 1.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +946 -809
- package/dist/cli.mjs +915 -779
- 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
|
}
|
|
@@ -10863,7 +11028,7 @@ function registerScanCommand(program2) {
|
|
|
10863
11028
|
if (!drillDown) {
|
|
10864
11029
|
const useInk2 = !options.classic;
|
|
10865
11030
|
if (useInk2) {
|
|
10866
|
-
const scanInkPath =
|
|
11031
|
+
const scanInkPath = path22.join(__dirname, "scan-ink.mjs");
|
|
10867
11032
|
const dynamicImport = new Function("id", "return import(id)");
|
|
10868
11033
|
const mod = await dynamicImport(`file://${scanInkPath}`);
|
|
10869
11034
|
const rangeLabel2 = options.all ? "all time" : `last ${options.days ?? 90} days`;
|
|
@@ -11281,8 +11446,8 @@ var init_suggestion_tracker = __esm({
|
|
|
11281
11446
|
});
|
|
11282
11447
|
|
|
11283
11448
|
// src/daemon/taint-store.ts
|
|
11284
|
-
import
|
|
11285
|
-
import
|
|
11449
|
+
import fs21 from "fs";
|
|
11450
|
+
import path23 from "path";
|
|
11286
11451
|
var DEFAULT_TTL_MS, TaintStore;
|
|
11287
11452
|
var init_taint_store = __esm({
|
|
11288
11453
|
"src/daemon/taint-store.ts"() {
|
|
@@ -11351,9 +11516,9 @@ var init_taint_store = __esm({
|
|
|
11351
11516
|
/** Resolve to absolute path, falling back to path.resolve if file doesn't exist yet. */
|
|
11352
11517
|
_resolve(filePath) {
|
|
11353
11518
|
try {
|
|
11354
|
-
return
|
|
11519
|
+
return fs21.realpathSync.native(path23.resolve(filePath));
|
|
11355
11520
|
} catch {
|
|
11356
|
-
return
|
|
11521
|
+
return path23.resolve(filePath);
|
|
11357
11522
|
}
|
|
11358
11523
|
}
|
|
11359
11524
|
};
|
|
@@ -11470,14 +11635,14 @@ var init_session_history = __esm({
|
|
|
11470
11635
|
|
|
11471
11636
|
// src/daemon/state.ts
|
|
11472
11637
|
import net2 from "net";
|
|
11473
|
-
import
|
|
11474
|
-
import
|
|
11475
|
-
import
|
|
11638
|
+
import fs22 from "fs";
|
|
11639
|
+
import path24 from "path";
|
|
11640
|
+
import os20 from "os";
|
|
11476
11641
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
11477
11642
|
function loadInsightCounts() {
|
|
11478
11643
|
try {
|
|
11479
|
-
if (!
|
|
11480
|
-
const data = JSON.parse(
|
|
11644
|
+
if (!fs22.existsSync(INSIGHT_COUNTS_FILE)) return;
|
|
11645
|
+
const data = JSON.parse(fs22.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
|
|
11481
11646
|
for (const [tool, count] of Object.entries(data)) {
|
|
11482
11647
|
if (typeof count === "number" && count > 0) insightCounts.set(tool, count);
|
|
11483
11648
|
}
|
|
@@ -11516,23 +11681,23 @@ function markRejectionHandlerRegistered() {
|
|
|
11516
11681
|
daemonRejectionHandlerRegistered = true;
|
|
11517
11682
|
}
|
|
11518
11683
|
function atomicWriteSync2(filePath, data, options) {
|
|
11519
|
-
const dir =
|
|
11520
|
-
if (!
|
|
11684
|
+
const dir = path24.dirname(filePath);
|
|
11685
|
+
if (!fs22.existsSync(dir)) fs22.mkdirSync(dir, { recursive: true });
|
|
11521
11686
|
const tmpPath = `${filePath}.${randomUUID3()}.tmp`;
|
|
11522
11687
|
try {
|
|
11523
|
-
|
|
11688
|
+
fs22.writeFileSync(tmpPath, data, options);
|
|
11524
11689
|
} catch (err2) {
|
|
11525
11690
|
try {
|
|
11526
|
-
|
|
11691
|
+
fs22.unlinkSync(tmpPath);
|
|
11527
11692
|
} catch {
|
|
11528
11693
|
}
|
|
11529
11694
|
throw err2;
|
|
11530
11695
|
}
|
|
11531
11696
|
try {
|
|
11532
|
-
|
|
11697
|
+
fs22.renameSync(tmpPath, filePath);
|
|
11533
11698
|
} catch (err2) {
|
|
11534
11699
|
try {
|
|
11535
|
-
|
|
11700
|
+
fs22.unlinkSync(tmpPath);
|
|
11536
11701
|
} catch {
|
|
11537
11702
|
}
|
|
11538
11703
|
throw err2;
|
|
@@ -11556,16 +11721,16 @@ function appendAuditLog(data) {
|
|
|
11556
11721
|
decision: data.decision,
|
|
11557
11722
|
source: "daemon"
|
|
11558
11723
|
};
|
|
11559
|
-
const dir =
|
|
11560
|
-
if (!
|
|
11561
|
-
|
|
11724
|
+
const dir = path24.dirname(AUDIT_LOG_FILE);
|
|
11725
|
+
if (!fs22.existsSync(dir)) fs22.mkdirSync(dir, { recursive: true });
|
|
11726
|
+
fs22.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
|
|
11562
11727
|
} catch {
|
|
11563
11728
|
}
|
|
11564
11729
|
}
|
|
11565
11730
|
function getAuditHistory(limit = 20) {
|
|
11566
11731
|
try {
|
|
11567
|
-
if (!
|
|
11568
|
-
const lines =
|
|
11732
|
+
if (!fs22.existsSync(AUDIT_LOG_FILE)) return [];
|
|
11733
|
+
const lines = fs22.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
|
|
11569
11734
|
if (lines.length === 1 && lines[0] === "") return [];
|
|
11570
11735
|
return lines.slice(-limit).map((l) => JSON.parse(l)).reverse();
|
|
11571
11736
|
} catch {
|
|
@@ -11574,7 +11739,7 @@ function getAuditHistory(limit = 20) {
|
|
|
11574
11739
|
}
|
|
11575
11740
|
function getOrgName() {
|
|
11576
11741
|
try {
|
|
11577
|
-
if (
|
|
11742
|
+
if (fs22.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
|
|
11578
11743
|
} catch {
|
|
11579
11744
|
}
|
|
11580
11745
|
return null;
|
|
@@ -11582,8 +11747,8 @@ function getOrgName() {
|
|
|
11582
11747
|
function writeGlobalSetting(key, value) {
|
|
11583
11748
|
let config = {};
|
|
11584
11749
|
try {
|
|
11585
|
-
if (
|
|
11586
|
-
config = JSON.parse(
|
|
11750
|
+
if (fs22.existsSync(GLOBAL_CONFIG_FILE)) {
|
|
11751
|
+
config = JSON.parse(fs22.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
|
|
11587
11752
|
}
|
|
11588
11753
|
} catch {
|
|
11589
11754
|
}
|
|
@@ -11595,8 +11760,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
|
|
|
11595
11760
|
try {
|
|
11596
11761
|
let trust = { entries: [] };
|
|
11597
11762
|
try {
|
|
11598
|
-
if (
|
|
11599
|
-
trust = JSON.parse(
|
|
11763
|
+
if (fs22.existsSync(TRUST_FILE2))
|
|
11764
|
+
trust = JSON.parse(fs22.readFileSync(TRUST_FILE2, "utf-8"));
|
|
11600
11765
|
} catch {
|
|
11601
11766
|
}
|
|
11602
11767
|
trust.entries = trust.entries.filter(
|
|
@@ -11613,8 +11778,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
|
|
|
11613
11778
|
}
|
|
11614
11779
|
function readPersistentDecisions() {
|
|
11615
11780
|
try {
|
|
11616
|
-
if (
|
|
11617
|
-
return JSON.parse(
|
|
11781
|
+
if (fs22.existsSync(DECISIONS_FILE)) {
|
|
11782
|
+
return JSON.parse(fs22.readFileSync(DECISIONS_FILE, "utf-8"));
|
|
11618
11783
|
}
|
|
11619
11784
|
} catch {
|
|
11620
11785
|
}
|
|
@@ -11642,7 +11807,7 @@ function estimateToolCost(tool, args) {
|
|
|
11642
11807
|
const filePath = a.file_path ?? a.path;
|
|
11643
11808
|
if (filePath) {
|
|
11644
11809
|
try {
|
|
11645
|
-
const bytes =
|
|
11810
|
+
const bytes = fs22.statSync(filePath).size;
|
|
11646
11811
|
return bytes / BYTES_PER_TOKEN / 1e6 * INPUT_PRICE_PER_1M;
|
|
11647
11812
|
} catch {
|
|
11648
11813
|
}
|
|
@@ -11713,7 +11878,7 @@ function abandonPending() {
|
|
|
11713
11878
|
});
|
|
11714
11879
|
if (autoStarted) {
|
|
11715
11880
|
try {
|
|
11716
|
-
|
|
11881
|
+
fs22.unlinkSync(DAEMON_PID_FILE);
|
|
11717
11882
|
} catch {
|
|
11718
11883
|
}
|
|
11719
11884
|
setTimeout(() => {
|
|
@@ -11724,8 +11889,8 @@ function abandonPending() {
|
|
|
11724
11889
|
}
|
|
11725
11890
|
function logActivitySocket(msg) {
|
|
11726
11891
|
try {
|
|
11727
|
-
|
|
11728
|
-
|
|
11892
|
+
fs22.appendFileSync(
|
|
11893
|
+
path24.join(homeDir, ".node9", "hook-debug.log"),
|
|
11729
11894
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] [activity-socket] ${msg}
|
|
11730
11895
|
`
|
|
11731
11896
|
);
|
|
@@ -11747,13 +11912,13 @@ function shouldRebind(now = Date.now()) {
|
|
|
11747
11912
|
function startActivitySocket() {
|
|
11748
11913
|
bindActivitySocket();
|
|
11749
11914
|
activityHealthInterval = setInterval(() => {
|
|
11750
|
-
if (!
|
|
11915
|
+
if (!fs22.existsSync(ACTIVITY_SOCKET_PATH2)) attemptRebind("health-probe");
|
|
11751
11916
|
}, ACTIVITY_HEALTH_PROBE_MS);
|
|
11752
11917
|
activityHealthInterval.unref();
|
|
11753
11918
|
process.on("exit", () => {
|
|
11754
11919
|
if (activityHealthInterval) clearInterval(activityHealthInterval);
|
|
11755
11920
|
try {
|
|
11756
|
-
|
|
11921
|
+
fs22.unlinkSync(ACTIVITY_SOCKET_PATH2);
|
|
11757
11922
|
} catch {
|
|
11758
11923
|
}
|
|
11759
11924
|
});
|
|
@@ -11781,7 +11946,7 @@ function attemptRebind(reason) {
|
|
|
11781
11946
|
}
|
|
11782
11947
|
function bindActivitySocket() {
|
|
11783
11948
|
try {
|
|
11784
|
-
|
|
11949
|
+
fs22.unlinkSync(ACTIVITY_SOCKET_PATH2);
|
|
11785
11950
|
} catch {
|
|
11786
11951
|
}
|
|
11787
11952
|
const ACTIVITY_MAX_BYTES = 1024 * 1024;
|
|
@@ -11894,14 +12059,14 @@ var init_state2 = __esm({
|
|
|
11894
12059
|
init_taint_store();
|
|
11895
12060
|
init_session_counters();
|
|
11896
12061
|
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 =
|
|
12062
|
+
homeDir = os20.homedir();
|
|
12063
|
+
DAEMON_PID_FILE = path24.join(homeDir, ".node9", "daemon.pid");
|
|
12064
|
+
DECISIONS_FILE = path24.join(homeDir, ".node9", "decisions.json");
|
|
12065
|
+
AUDIT_LOG_FILE = path24.join(homeDir, ".node9", "audit.log");
|
|
12066
|
+
TRUST_FILE2 = path24.join(homeDir, ".node9", "trust.json");
|
|
12067
|
+
GLOBAL_CONFIG_FILE = path24.join(homeDir, ".node9", "config.json");
|
|
12068
|
+
CREDENTIALS_FILE = path24.join(homeDir, ".node9", "credentials.json");
|
|
12069
|
+
INSIGHT_COUNTS_FILE = path24.join(homeDir, ".node9", "insight-counts.json");
|
|
11905
12070
|
pending = /* @__PURE__ */ new Map();
|
|
11906
12071
|
sseClients = /* @__PURE__ */ new Set();
|
|
11907
12072
|
suggestionTracker = new SuggestionTracker(3);
|
|
@@ -11918,7 +12083,7 @@ var init_state2 = __esm({
|
|
|
11918
12083
|
"2h": 2 * 60 * 6e4
|
|
11919
12084
|
};
|
|
11920
12085
|
autoStarted = process.env.NODE9_AUTO_STARTED === "1";
|
|
11921
|
-
ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" :
|
|
12086
|
+
ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path24.join(os20.tmpdir(), "node9-activity.sock");
|
|
11922
12087
|
ACTIVITY_RING_SIZE = 100;
|
|
11923
12088
|
activityRing = [];
|
|
11924
12089
|
LARGE_RESPONSE_RING_SIZE = 20;
|
|
@@ -11957,10 +12122,10 @@ var init_state2 = __esm({
|
|
|
11957
12122
|
});
|
|
11958
12123
|
|
|
11959
12124
|
// src/daemon/sync.ts
|
|
11960
|
-
import
|
|
12125
|
+
import fs23 from "fs";
|
|
11961
12126
|
import https2 from "https";
|
|
11962
|
-
import
|
|
11963
|
-
import
|
|
12127
|
+
import os21 from "os";
|
|
12128
|
+
import path25 from "path";
|
|
11964
12129
|
function emptySignals3() {
|
|
11965
12130
|
return {
|
|
11966
12131
|
dlpFindings: 0,
|
|
@@ -12000,8 +12165,8 @@ function readCredentials() {
|
|
|
12000
12165
|
};
|
|
12001
12166
|
}
|
|
12002
12167
|
try {
|
|
12003
|
-
const credPath =
|
|
12004
|
-
const creds = JSON.parse(
|
|
12168
|
+
const credPath = path25.join(os21.homedir(), ".node9", "credentials.json");
|
|
12169
|
+
const creds = JSON.parse(fs23.readFileSync(credPath, "utf-8"));
|
|
12005
12170
|
const profileName = process.env.NODE9_PROFILE ?? "default";
|
|
12006
12171
|
const profile = creds[profileName];
|
|
12007
12172
|
if (typeof profile?.apiKey === "string" && profile.apiKey.length > 0) {
|
|
@@ -12027,7 +12192,7 @@ function readCredentials() {
|
|
|
12027
12192
|
}
|
|
12028
12193
|
function readCachedEtag() {
|
|
12029
12194
|
try {
|
|
12030
|
-
const raw = JSON.parse(
|
|
12195
|
+
const raw = JSON.parse(fs23.readFileSync(rulesCacheFile(), "utf-8"));
|
|
12031
12196
|
return typeof raw.etag === "string" ? raw.etag : void 0;
|
|
12032
12197
|
} catch {
|
|
12033
12198
|
return void 0;
|
|
@@ -12088,9 +12253,9 @@ function extractRules(body) {
|
|
|
12088
12253
|
return [];
|
|
12089
12254
|
}
|
|
12090
12255
|
function writeCache2(cache) {
|
|
12091
|
-
const dir =
|
|
12092
|
-
if (!
|
|
12093
|
-
|
|
12256
|
+
const dir = path25.dirname(rulesCacheFile());
|
|
12257
|
+
if (!fs23.existsSync(dir)) fs23.mkdirSync(dir, { recursive: true });
|
|
12258
|
+
fs23.writeFileSync(rulesCacheFile(), JSON.stringify(cache, null, 2) + "\n", "utf-8");
|
|
12094
12259
|
}
|
|
12095
12260
|
async function syncOnce() {
|
|
12096
12261
|
const creds = readCredentials();
|
|
@@ -12247,7 +12412,7 @@ async function runCloudSync() {
|
|
|
12247
12412
|
}
|
|
12248
12413
|
function getCloudSyncStatus() {
|
|
12249
12414
|
try {
|
|
12250
|
-
const raw = JSON.parse(
|
|
12415
|
+
const raw = JSON.parse(fs23.readFileSync(rulesCacheFile(), "utf-8"));
|
|
12251
12416
|
if (!Array.isArray(raw.rules) || typeof raw.fetchedAt !== "string") return { cached: false };
|
|
12252
12417
|
return {
|
|
12253
12418
|
cached: true,
|
|
@@ -12264,7 +12429,7 @@ function getCloudSyncStatus() {
|
|
|
12264
12429
|
}
|
|
12265
12430
|
function getCloudRules() {
|
|
12266
12431
|
try {
|
|
12267
|
-
const raw = JSON.parse(
|
|
12432
|
+
const raw = JSON.parse(fs23.readFileSync(rulesCacheFile(), "utf-8"));
|
|
12268
12433
|
return Array.isArray(raw.rules) ? raw.rules : null;
|
|
12269
12434
|
} catch {
|
|
12270
12435
|
return null;
|
|
@@ -12320,7 +12485,7 @@ var init_sync = __esm({
|
|
|
12320
12485
|
loop: "loops",
|
|
12321
12486
|
"long-output-redacted": "longOutputRedactions"
|
|
12322
12487
|
};
|
|
12323
|
-
rulesCacheFile = () =>
|
|
12488
|
+
rulesCacheFile = () => path25.join(os21.homedir(), ".node9", "rules-cache.json");
|
|
12324
12489
|
DEFAULT_API_URL = "https://api.node9.ai/api/v1/intercept/policies/sync";
|
|
12325
12490
|
DEFAULT_INTERVAL_HOURS = 5;
|
|
12326
12491
|
MIN_INTERVAL_HOURS = 1;
|
|
@@ -12331,73 +12496,73 @@ var init_sync = __esm({
|
|
|
12331
12496
|
});
|
|
12332
12497
|
|
|
12333
12498
|
// src/daemon/dlp-scanner.ts
|
|
12334
|
-
import
|
|
12335
|
-
import
|
|
12336
|
-
import
|
|
12499
|
+
import fs24 from "fs";
|
|
12500
|
+
import path26 from "path";
|
|
12501
|
+
import os22 from "os";
|
|
12337
12502
|
function loadIndex() {
|
|
12338
12503
|
try {
|
|
12339
|
-
return JSON.parse(
|
|
12504
|
+
return JSON.parse(fs24.readFileSync(INDEX_FILE, "utf-8"));
|
|
12340
12505
|
} catch {
|
|
12341
12506
|
return {};
|
|
12342
12507
|
}
|
|
12343
12508
|
}
|
|
12344
12509
|
function saveIndex(index) {
|
|
12345
12510
|
try {
|
|
12346
|
-
|
|
12511
|
+
fs24.writeFileSync(INDEX_FILE, JSON.stringify(index), { encoding: "utf-8", mode: 384 });
|
|
12347
12512
|
} catch {
|
|
12348
12513
|
}
|
|
12349
12514
|
}
|
|
12350
12515
|
function appendAuditEntry(entry) {
|
|
12351
12516
|
try {
|
|
12352
|
-
|
|
12517
|
+
fs24.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
|
|
12353
12518
|
} catch {
|
|
12354
12519
|
}
|
|
12355
12520
|
}
|
|
12356
12521
|
function runDlpScan() {
|
|
12357
|
-
if (!
|
|
12522
|
+
if (!fs24.existsSync(PROJECTS_DIR2)) return;
|
|
12358
12523
|
const index = loadIndex();
|
|
12359
12524
|
let updated = false;
|
|
12360
12525
|
let projDirs;
|
|
12361
12526
|
try {
|
|
12362
|
-
projDirs =
|
|
12527
|
+
projDirs = fs24.readdirSync(PROJECTS_DIR2);
|
|
12363
12528
|
} catch {
|
|
12364
12529
|
return;
|
|
12365
12530
|
}
|
|
12366
12531
|
for (const proj of projDirs) {
|
|
12367
|
-
const projPath =
|
|
12532
|
+
const projPath = path26.join(PROJECTS_DIR2, proj);
|
|
12368
12533
|
try {
|
|
12369
|
-
if (!
|
|
12370
|
-
const real =
|
|
12371
|
-
if (!real.startsWith(PROJECTS_DIR2 +
|
|
12534
|
+
if (!fs24.lstatSync(projPath).isDirectory()) continue;
|
|
12535
|
+
const real = fs24.realpathSync(projPath);
|
|
12536
|
+
if (!real.startsWith(PROJECTS_DIR2 + path26.sep) && real !== PROJECTS_DIR2) continue;
|
|
12372
12537
|
} catch {
|
|
12373
12538
|
continue;
|
|
12374
12539
|
}
|
|
12375
12540
|
let files;
|
|
12376
12541
|
try {
|
|
12377
|
-
files =
|
|
12542
|
+
files = fs24.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
12378
12543
|
} catch {
|
|
12379
12544
|
continue;
|
|
12380
12545
|
}
|
|
12381
12546
|
for (const file of files) {
|
|
12382
|
-
const filePath =
|
|
12547
|
+
const filePath = path26.join(projPath, file);
|
|
12383
12548
|
const lastOffset = index[filePath] ?? 0;
|
|
12384
12549
|
let size;
|
|
12385
12550
|
try {
|
|
12386
|
-
size =
|
|
12551
|
+
size = fs24.statSync(filePath).size;
|
|
12387
12552
|
} catch {
|
|
12388
12553
|
continue;
|
|
12389
12554
|
}
|
|
12390
12555
|
if (size <= lastOffset) continue;
|
|
12391
12556
|
let fd;
|
|
12392
12557
|
try {
|
|
12393
|
-
fd =
|
|
12558
|
+
fd = fs24.openSync(filePath, "r");
|
|
12394
12559
|
} catch {
|
|
12395
12560
|
continue;
|
|
12396
12561
|
}
|
|
12397
12562
|
try {
|
|
12398
12563
|
const chunkSize = size - lastOffset;
|
|
12399
12564
|
const buf = Buffer.alloc(chunkSize);
|
|
12400
|
-
|
|
12565
|
+
fs24.readSync(fd, buf, 0, chunkSize, lastOffset);
|
|
12401
12566
|
const chunk = buf.toString("utf-8");
|
|
12402
12567
|
for (const line of chunk.split("\n")) {
|
|
12403
12568
|
if (!line.trim()) continue;
|
|
@@ -12417,7 +12582,7 @@ function runDlpScan() {
|
|
|
12417
12582
|
if (typeof text !== "string") continue;
|
|
12418
12583
|
const match = scanText(text);
|
|
12419
12584
|
if (!match) continue;
|
|
12420
|
-
const projLabel = decodeURIComponent(proj).replace(
|
|
12585
|
+
const projLabel = decodeURIComponent(proj).replace(os22.homedir(), "~").slice(0, 40);
|
|
12421
12586
|
const ts = entry.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
12422
12587
|
appendAuditEntry({
|
|
12423
12588
|
ts,
|
|
@@ -12442,7 +12607,7 @@ Run: node9 report --period 30d`
|
|
|
12442
12607
|
updated = true;
|
|
12443
12608
|
} finally {
|
|
12444
12609
|
try {
|
|
12445
|
-
|
|
12610
|
+
fs24.closeSync(fd);
|
|
12446
12611
|
} catch {
|
|
12447
12612
|
}
|
|
12448
12613
|
}
|
|
@@ -12475,23 +12640,23 @@ var init_dlp_scanner = __esm({
|
|
|
12475
12640
|
init_dlp();
|
|
12476
12641
|
init_native();
|
|
12477
12642
|
init_state2();
|
|
12478
|
-
INDEX_FILE =
|
|
12479
|
-
PROJECTS_DIR2 =
|
|
12643
|
+
INDEX_FILE = path26.join(os22.homedir(), ".node9", "dlp-index.json");
|
|
12644
|
+
PROJECTS_DIR2 = path26.join(os22.homedir(), ".claude", "projects");
|
|
12480
12645
|
}
|
|
12481
12646
|
});
|
|
12482
12647
|
|
|
12483
12648
|
// src/daemon/mcp-tools.ts
|
|
12484
|
-
import
|
|
12485
|
-
import
|
|
12486
|
-
import
|
|
12649
|
+
import fs25 from "fs";
|
|
12650
|
+
import path27 from "path";
|
|
12651
|
+
import os23 from "os";
|
|
12487
12652
|
function getMcpToolsFile() {
|
|
12488
|
-
return
|
|
12653
|
+
return path27.join(os23.homedir(), ".node9", "mcp-tools.json");
|
|
12489
12654
|
}
|
|
12490
12655
|
function readMcpToolsConfig() {
|
|
12491
12656
|
try {
|
|
12492
12657
|
const file = getMcpToolsFile();
|
|
12493
|
-
if (!
|
|
12494
|
-
const raw =
|
|
12658
|
+
if (!fs25.existsSync(file)) return {};
|
|
12659
|
+
const raw = fs25.readFileSync(file, "utf-8");
|
|
12495
12660
|
return JSON.parse(raw);
|
|
12496
12661
|
} catch {
|
|
12497
12662
|
return {};
|
|
@@ -12500,11 +12665,11 @@ function readMcpToolsConfig() {
|
|
|
12500
12665
|
function writeMcpToolsConfig(config) {
|
|
12501
12666
|
try {
|
|
12502
12667
|
const file = getMcpToolsFile();
|
|
12503
|
-
const dir =
|
|
12504
|
-
if (!
|
|
12505
|
-
const tmpPath = `${file}.${
|
|
12506
|
-
|
|
12507
|
-
|
|
12668
|
+
const dir = path27.dirname(file);
|
|
12669
|
+
if (!fs25.existsSync(dir)) fs25.mkdirSync(dir, { recursive: true });
|
|
12670
|
+
const tmpPath = `${file}.${os23.hostname()}.${process.pid}.tmp`;
|
|
12671
|
+
fs25.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
|
|
12672
|
+
fs25.renameSync(tmpPath, file);
|
|
12508
12673
|
} catch (e) {
|
|
12509
12674
|
console.error("Failed to write mcp-tools.json", e);
|
|
12510
12675
|
}
|
|
@@ -12551,9 +12716,9 @@ var init_mcp_tools = __esm({
|
|
|
12551
12716
|
|
|
12552
12717
|
// src/daemon/server.ts
|
|
12553
12718
|
import http from "http";
|
|
12554
|
-
import
|
|
12555
|
-
import
|
|
12556
|
-
import
|
|
12719
|
+
import fs26 from "fs";
|
|
12720
|
+
import path28 from "path";
|
|
12721
|
+
import os24 from "os";
|
|
12557
12722
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
12558
12723
|
import { spawnSync } from "child_process";
|
|
12559
12724
|
import chalk6 from "chalk";
|
|
@@ -12574,7 +12739,7 @@ function startDaemon() {
|
|
|
12574
12739
|
idleTimer = setTimeout(() => {
|
|
12575
12740
|
if (autoStarted) {
|
|
12576
12741
|
try {
|
|
12577
|
-
|
|
12742
|
+
fs26.unlinkSync(DAEMON_PID_FILE);
|
|
12578
12743
|
} catch {
|
|
12579
12744
|
}
|
|
12580
12745
|
}
|
|
@@ -12719,7 +12884,7 @@ data: ${JSON.stringify(item.data)}
|
|
|
12719
12884
|
mcpServer: entry.mcpServer
|
|
12720
12885
|
});
|
|
12721
12886
|
}
|
|
12722
|
-
const projectCwd = typeof cwd === "string" &&
|
|
12887
|
+
const projectCwd = typeof cwd === "string" && path28.isAbsolute(cwd) ? cwd : void 0;
|
|
12723
12888
|
const projectConfig = getConfig(projectCwd);
|
|
12724
12889
|
const browserEnabled = projectConfig.settings.approvers?.browser !== false;
|
|
12725
12890
|
const terminalEnabled = projectConfig.settings.approvers?.terminal !== false;
|
|
@@ -13011,8 +13176,8 @@ data: ${JSON.stringify(item.data)}
|
|
|
13011
13176
|
if (!validToken(req)) return res.writeHead(403).end();
|
|
13012
13177
|
const periodParam = reqUrl.searchParams.get("period") || "7d";
|
|
13013
13178
|
const period = ["today", "7d", "30d", "month"].includes(periodParam) ? periodParam : "7d";
|
|
13014
|
-
const logPath =
|
|
13015
|
-
if (!
|
|
13179
|
+
const logPath = path28.join(os24.homedir(), ".node9", "audit.log");
|
|
13180
|
+
if (!fs26.existsSync(logPath)) {
|
|
13016
13181
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
13017
13182
|
return res.end(
|
|
13018
13183
|
JSON.stringify({
|
|
@@ -13025,7 +13190,7 @@ data: ${JSON.stringify(item.data)}
|
|
|
13025
13190
|
);
|
|
13026
13191
|
}
|
|
13027
13192
|
try {
|
|
13028
|
-
const raw =
|
|
13193
|
+
const raw = fs26.readFileSync(logPath, "utf-8");
|
|
13029
13194
|
const allEntries = raw.split("\n").flatMap((line) => {
|
|
13030
13195
|
if (!line.trim()) return [];
|
|
13031
13196
|
try {
|
|
@@ -13348,14 +13513,14 @@ data: ${JSON.stringify(item.data)}
|
|
|
13348
13513
|
server.on("error", (e) => {
|
|
13349
13514
|
if (e.code === "EADDRINUSE") {
|
|
13350
13515
|
try {
|
|
13351
|
-
if (
|
|
13352
|
-
const { pid } = JSON.parse(
|
|
13516
|
+
if (fs26.existsSync(DAEMON_PID_FILE)) {
|
|
13517
|
+
const { pid } = JSON.parse(fs26.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
13353
13518
|
process.kill(pid, 0);
|
|
13354
13519
|
return process.exit(0);
|
|
13355
13520
|
}
|
|
13356
13521
|
} catch {
|
|
13357
13522
|
try {
|
|
13358
|
-
|
|
13523
|
+
fs26.unlinkSync(DAEMON_PID_FILE);
|
|
13359
13524
|
} catch {
|
|
13360
13525
|
}
|
|
13361
13526
|
server.listen(DAEMON_PORT, DAEMON_HOST);
|
|
@@ -13443,15 +13608,15 @@ var init_server = __esm({
|
|
|
13443
13608
|
});
|
|
13444
13609
|
|
|
13445
13610
|
// src/daemon/service.ts
|
|
13446
|
-
import
|
|
13447
|
-
import
|
|
13448
|
-
import
|
|
13611
|
+
import fs27 from "fs";
|
|
13612
|
+
import path29 from "path";
|
|
13613
|
+
import os25 from "os";
|
|
13449
13614
|
import { spawnSync as spawnSync2, execFileSync } from "child_process";
|
|
13450
13615
|
function resolveNode9Binary() {
|
|
13451
13616
|
try {
|
|
13452
13617
|
const script = process.argv[1];
|
|
13453
|
-
if (typeof script === "string" &&
|
|
13454
|
-
return
|
|
13618
|
+
if (typeof script === "string" && path29.isAbsolute(script) && fs27.existsSync(script)) {
|
|
13619
|
+
return fs27.realpathSync(script);
|
|
13455
13620
|
}
|
|
13456
13621
|
} catch {
|
|
13457
13622
|
}
|
|
@@ -13469,11 +13634,11 @@ function xmlEscape(s) {
|
|
|
13469
13634
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
13470
13635
|
}
|
|
13471
13636
|
function launchdPlist(binaryPath) {
|
|
13472
|
-
const logDir =
|
|
13637
|
+
const logDir = path29.join(os25.homedir(), ".node9");
|
|
13473
13638
|
const nodePath = xmlEscape(process.execPath);
|
|
13474
13639
|
const scriptPath = xmlEscape(binaryPath);
|
|
13475
|
-
const outLog = xmlEscape(
|
|
13476
|
-
const errLog = xmlEscape(
|
|
13640
|
+
const outLog = xmlEscape(path29.join(logDir, "daemon.log"));
|
|
13641
|
+
const errLog = xmlEscape(path29.join(logDir, "daemon-error.log"));
|
|
13477
13642
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
13478
13643
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
13479
13644
|
<plist version="1.0">
|
|
@@ -13506,9 +13671,9 @@ function launchdPlist(binaryPath) {
|
|
|
13506
13671
|
`;
|
|
13507
13672
|
}
|
|
13508
13673
|
function installLaunchd(binaryPath) {
|
|
13509
|
-
const dir =
|
|
13510
|
-
if (!
|
|
13511
|
-
|
|
13674
|
+
const dir = path29.dirname(LAUNCHD_PLIST);
|
|
13675
|
+
if (!fs27.existsSync(dir)) fs27.mkdirSync(dir, { recursive: true });
|
|
13676
|
+
fs27.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
|
|
13512
13677
|
spawnSync2("launchctl", ["unload", LAUNCHD_PLIST], { encoding: "utf8" });
|
|
13513
13678
|
const r = spawnSync2("launchctl", ["load", "-w", LAUNCHD_PLIST], {
|
|
13514
13679
|
encoding: "utf8",
|
|
@@ -13519,13 +13684,13 @@ function installLaunchd(binaryPath) {
|
|
|
13519
13684
|
}
|
|
13520
13685
|
}
|
|
13521
13686
|
function uninstallLaunchd() {
|
|
13522
|
-
if (
|
|
13687
|
+
if (fs27.existsSync(LAUNCHD_PLIST)) {
|
|
13523
13688
|
spawnSync2("launchctl", ["unload", "-w", LAUNCHD_PLIST], { encoding: "utf8", timeout: 5e3 });
|
|
13524
|
-
|
|
13689
|
+
fs27.unlinkSync(LAUNCHD_PLIST);
|
|
13525
13690
|
}
|
|
13526
13691
|
}
|
|
13527
13692
|
function isLaunchdInstalled() {
|
|
13528
|
-
return
|
|
13693
|
+
return fs27.existsSync(LAUNCHD_PLIST);
|
|
13529
13694
|
}
|
|
13530
13695
|
function systemdUnit(binaryPath) {
|
|
13531
13696
|
return `[Unit]
|
|
@@ -13544,12 +13709,12 @@ WantedBy=default.target
|
|
|
13544
13709
|
`;
|
|
13545
13710
|
}
|
|
13546
13711
|
function installSystemd(binaryPath) {
|
|
13547
|
-
if (!
|
|
13548
|
-
|
|
13712
|
+
if (!fs27.existsSync(SYSTEMD_UNIT_DIR)) {
|
|
13713
|
+
fs27.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
|
|
13549
13714
|
}
|
|
13550
|
-
|
|
13715
|
+
fs27.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
|
|
13551
13716
|
try {
|
|
13552
|
-
execFileSync("loginctl", ["enable-linger",
|
|
13717
|
+
execFileSync("loginctl", ["enable-linger", os25.userInfo().username], { timeout: 3e3 });
|
|
13553
13718
|
} catch {
|
|
13554
13719
|
}
|
|
13555
13720
|
const reload = spawnSync2("systemctl", ["--user", "daemon-reload"], {
|
|
@@ -13569,23 +13734,23 @@ function installSystemd(binaryPath) {
|
|
|
13569
13734
|
}
|
|
13570
13735
|
}
|
|
13571
13736
|
function uninstallSystemd() {
|
|
13572
|
-
if (
|
|
13737
|
+
if (fs27.existsSync(SYSTEMD_UNIT)) {
|
|
13573
13738
|
spawnSync2("systemctl", ["--user", "disable", "--now", "node9-daemon"], {
|
|
13574
13739
|
encoding: "utf8",
|
|
13575
13740
|
timeout: 5e3
|
|
13576
13741
|
});
|
|
13577
13742
|
spawnSync2("systemctl", ["--user", "daemon-reload"], { encoding: "utf8", timeout: 5e3 });
|
|
13578
|
-
|
|
13743
|
+
fs27.unlinkSync(SYSTEMD_UNIT);
|
|
13579
13744
|
}
|
|
13580
13745
|
}
|
|
13581
13746
|
function isSystemdInstalled() {
|
|
13582
|
-
return
|
|
13747
|
+
return fs27.existsSync(SYSTEMD_UNIT);
|
|
13583
13748
|
}
|
|
13584
13749
|
function stopRunningDaemon() {
|
|
13585
|
-
const pidFile =
|
|
13586
|
-
if (!
|
|
13750
|
+
const pidFile = path29.join(os25.homedir(), ".node9", "daemon.pid");
|
|
13751
|
+
if (!fs27.existsSync(pidFile)) return;
|
|
13587
13752
|
try {
|
|
13588
|
-
const data = JSON.parse(
|
|
13753
|
+
const data = JSON.parse(fs27.readFileSync(pidFile, "utf-8"));
|
|
13589
13754
|
const pid = data.pid;
|
|
13590
13755
|
const MAX_PID2 = 4194304;
|
|
13591
13756
|
if (typeof pid === "number" && Number.isInteger(pid) && pid > 0 && pid <= MAX_PID2) {
|
|
@@ -13605,7 +13770,7 @@ function stopRunningDaemon() {
|
|
|
13605
13770
|
}
|
|
13606
13771
|
}
|
|
13607
13772
|
try {
|
|
13608
|
-
|
|
13773
|
+
fs27.unlinkSync(pidFile);
|
|
13609
13774
|
} catch {
|
|
13610
13775
|
}
|
|
13611
13776
|
} catch {
|
|
@@ -13680,19 +13845,19 @@ var init_service = __esm({
|
|
|
13680
13845
|
"src/daemon/service.ts"() {
|
|
13681
13846
|
"use strict";
|
|
13682
13847
|
LAUNCHD_LABEL = "ai.node9.daemon";
|
|
13683
|
-
LAUNCHD_PLIST =
|
|
13684
|
-
SYSTEMD_UNIT_DIR =
|
|
13685
|
-
SYSTEMD_UNIT =
|
|
13848
|
+
LAUNCHD_PLIST = path29.join(os25.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
|
|
13849
|
+
SYSTEMD_UNIT_DIR = path29.join(os25.homedir(), ".config", "systemd", "user");
|
|
13850
|
+
SYSTEMD_UNIT = path29.join(SYSTEMD_UNIT_DIR, "node9-daemon.service");
|
|
13686
13851
|
}
|
|
13687
13852
|
});
|
|
13688
13853
|
|
|
13689
13854
|
// src/daemon/index.ts
|
|
13690
|
-
import
|
|
13855
|
+
import fs28 from "fs";
|
|
13691
13856
|
import chalk7 from "chalk";
|
|
13692
13857
|
function stopDaemon() {
|
|
13693
|
-
if (!
|
|
13858
|
+
if (!fs28.existsSync(DAEMON_PID_FILE)) return console.log(chalk7.yellow("Not running."));
|
|
13694
13859
|
try {
|
|
13695
|
-
const data = JSON.parse(
|
|
13860
|
+
const data = JSON.parse(fs28.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
13696
13861
|
const pid = data.pid;
|
|
13697
13862
|
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
|
|
13698
13863
|
console.log(chalk7.gray("Cleaned up invalid PID file."));
|
|
@@ -13704,7 +13869,7 @@ function stopDaemon() {
|
|
|
13704
13869
|
console.log(chalk7.gray("Cleaned up stale PID file."));
|
|
13705
13870
|
} finally {
|
|
13706
13871
|
try {
|
|
13707
|
-
|
|
13872
|
+
fs28.unlinkSync(DAEMON_PID_FILE);
|
|
13708
13873
|
} catch {
|
|
13709
13874
|
}
|
|
13710
13875
|
}
|
|
@@ -13713,9 +13878,9 @@ function daemonStatus() {
|
|
|
13713
13878
|
const serviceInstalled = isDaemonServiceInstalled();
|
|
13714
13879
|
const serviceLabel = serviceInstalled ? chalk7.green("installed (starts on login)") : chalk7.yellow("not installed \u2014 run: node9 daemon install");
|
|
13715
13880
|
let processStatus;
|
|
13716
|
-
if (
|
|
13881
|
+
if (fs28.existsSync(DAEMON_PID_FILE)) {
|
|
13717
13882
|
try {
|
|
13718
|
-
const data = JSON.parse(
|
|
13883
|
+
const data = JSON.parse(fs28.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
13719
13884
|
const pid = data.pid;
|
|
13720
13885
|
const port = data.port;
|
|
13721
13886
|
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
|
|
@@ -13760,7 +13925,7 @@ __export(tail_exports, {
|
|
|
13760
13925
|
});
|
|
13761
13926
|
import http2 from "http";
|
|
13762
13927
|
import chalk29 from "chalk";
|
|
13763
|
-
import
|
|
13928
|
+
import fs46 from "fs";
|
|
13764
13929
|
import os41 from "os";
|
|
13765
13930
|
import path47 from "path";
|
|
13766
13931
|
import readline6 from "readline";
|
|
@@ -13787,19 +13952,19 @@ function getModelContextLimit(model) {
|
|
|
13787
13952
|
}
|
|
13788
13953
|
function readSessionUsage() {
|
|
13789
13954
|
const projectsDir = path47.join(os41.homedir(), ".claude", "projects");
|
|
13790
|
-
if (!
|
|
13955
|
+
if (!fs46.existsSync(projectsDir)) return null;
|
|
13791
13956
|
let latestFile = null;
|
|
13792
13957
|
let latestMtime = 0;
|
|
13793
13958
|
try {
|
|
13794
|
-
for (const dir of
|
|
13959
|
+
for (const dir of fs46.readdirSync(projectsDir)) {
|
|
13795
13960
|
const dirPath = path47.join(projectsDir, dir);
|
|
13796
13961
|
try {
|
|
13797
|
-
if (!
|
|
13798
|
-
for (const file of
|
|
13962
|
+
if (!fs46.statSync(dirPath).isDirectory()) continue;
|
|
13963
|
+
for (const file of fs46.readdirSync(dirPath)) {
|
|
13799
13964
|
if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
|
|
13800
13965
|
const filePath = path47.join(dirPath, file);
|
|
13801
13966
|
try {
|
|
13802
|
-
const mtime =
|
|
13967
|
+
const mtime = fs46.statSync(filePath).mtimeMs;
|
|
13803
13968
|
if (mtime > latestMtime) {
|
|
13804
13969
|
latestMtime = mtime;
|
|
13805
13970
|
latestFile = filePath;
|
|
@@ -13814,7 +13979,7 @@ function readSessionUsage() {
|
|
|
13814
13979
|
}
|
|
13815
13980
|
if (!latestFile) return null;
|
|
13816
13981
|
try {
|
|
13817
|
-
const lines =
|
|
13982
|
+
const lines = fs46.readFileSync(latestFile, "utf-8").split("\n");
|
|
13818
13983
|
let lastModel = "";
|
|
13819
13984
|
let lastInput = 0;
|
|
13820
13985
|
let lastOutput = 0;
|
|
@@ -13914,9 +14079,9 @@ function renderPending(activity) {
|
|
|
13914
14079
|
}
|
|
13915
14080
|
async function ensureDaemon() {
|
|
13916
14081
|
let pidPort = null;
|
|
13917
|
-
if (
|
|
14082
|
+
if (fs46.existsSync(PID_FILE)) {
|
|
13918
14083
|
try {
|
|
13919
|
-
const { port } = JSON.parse(
|
|
14084
|
+
const { port } = JSON.parse(fs46.readFileSync(PID_FILE, "utf-8"));
|
|
13920
14085
|
pidPort = port;
|
|
13921
14086
|
} catch {
|
|
13922
14087
|
console.error(chalk29.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
|
|
@@ -14074,7 +14239,7 @@ function buildRecoveryCardLines(req) {
|
|
|
14074
14239
|
function readApproversFromDisk() {
|
|
14075
14240
|
const configPath = path47.join(os41.homedir(), ".node9", "config.json");
|
|
14076
14241
|
try {
|
|
14077
|
-
const raw = JSON.parse(
|
|
14242
|
+
const raw = JSON.parse(fs46.readFileSync(configPath, "utf-8"));
|
|
14078
14243
|
const settings = raw.settings ?? {};
|
|
14079
14244
|
return settings.approvers ?? {};
|
|
14080
14245
|
} catch {
|
|
@@ -14092,13 +14257,13 @@ function approverStatusLine() {
|
|
|
14092
14257
|
function toggleApprover(channel) {
|
|
14093
14258
|
const configPath = path47.join(os41.homedir(), ".node9", "config.json");
|
|
14094
14259
|
try {
|
|
14095
|
-
const raw = JSON.parse(
|
|
14260
|
+
const raw = JSON.parse(fs46.readFileSync(configPath, "utf-8"));
|
|
14096
14261
|
const settings = raw.settings ?? {};
|
|
14097
14262
|
const approvers = settings.approvers ?? {};
|
|
14098
14263
|
approvers[channel] = approvers[channel] === false;
|
|
14099
14264
|
settings.approvers = approvers;
|
|
14100
14265
|
raw.settings = settings;
|
|
14101
|
-
|
|
14266
|
+
fs46.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
|
|
14102
14267
|
} catch (err2) {
|
|
14103
14268
|
process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
|
|
14104
14269
|
`);
|
|
@@ -14270,7 +14435,7 @@ async function startTail(options = {}) {
|
|
|
14270
14435
|
}
|
|
14271
14436
|
postDecisionHttp(req2.id, httpDecision, authToken, port, httpOpts).catch((err2) => {
|
|
14272
14437
|
try {
|
|
14273
|
-
|
|
14438
|
+
fs46.appendFileSync(
|
|
14274
14439
|
path47.join(os41.homedir(), ".node9", "hook-debug.log"),
|
|
14275
14440
|
`[tail] POST /decision failed: ${String(err2)}
|
|
14276
14441
|
`
|
|
@@ -14337,7 +14502,7 @@ async function startTail(options = {}) {
|
|
|
14337
14502
|
}
|
|
14338
14503
|
const auditLog = path47.join(os41.homedir(), ".node9", "audit.log");
|
|
14339
14504
|
try {
|
|
14340
|
-
const unackedDlp =
|
|
14505
|
+
const unackedDlp = fs46.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
|
|
14341
14506
|
if (unackedDlp > 0) {
|
|
14342
14507
|
console.log("");
|
|
14343
14508
|
console.log(
|
|
@@ -14377,7 +14542,7 @@ async function startTail(options = {}) {
|
|
|
14377
14542
|
if (stallWarned) return;
|
|
14378
14543
|
if (Date.now() - lastActivityFromDaemon < STALL_THRESHOLD_MS) return;
|
|
14379
14544
|
try {
|
|
14380
|
-
const auditMtime =
|
|
14545
|
+
const auditMtime = fs46.statSync(auditLog).mtimeMs;
|
|
14381
14546
|
if (Date.now() - auditMtime >= STALL_THRESHOLD_MS) return;
|
|
14382
14547
|
console.log("");
|
|
14383
14548
|
console.log(
|
|
@@ -14616,7 +14781,7 @@ __export(hud_exports, {
|
|
|
14616
14781
|
main: () => main,
|
|
14617
14782
|
renderEnvironmentLine: () => renderEnvironmentLine
|
|
14618
14783
|
});
|
|
14619
|
-
import
|
|
14784
|
+
import fs47 from "fs";
|
|
14620
14785
|
import path48 from "path";
|
|
14621
14786
|
import os42 from "os";
|
|
14622
14787
|
import http3 from "http";
|
|
@@ -14694,9 +14859,9 @@ function formatTimeLeft(resetsAt) {
|
|
|
14694
14859
|
return ` (${m}m left)`;
|
|
14695
14860
|
}
|
|
14696
14861
|
function safeReadJson(filePath) {
|
|
14697
|
-
if (!
|
|
14862
|
+
if (!fs47.existsSync(filePath)) return null;
|
|
14698
14863
|
try {
|
|
14699
|
-
return JSON.parse(
|
|
14864
|
+
return JSON.parse(fs47.readFileSync(filePath, "utf-8"));
|
|
14700
14865
|
} catch {
|
|
14701
14866
|
return null;
|
|
14702
14867
|
}
|
|
@@ -14717,10 +14882,10 @@ function countHooksInFile(filePath) {
|
|
|
14717
14882
|
return Object.keys(cfg.hooks).length;
|
|
14718
14883
|
}
|
|
14719
14884
|
function countRulesInDir(rulesDir) {
|
|
14720
|
-
if (!
|
|
14885
|
+
if (!fs47.existsSync(rulesDir)) return 0;
|
|
14721
14886
|
let count = 0;
|
|
14722
14887
|
try {
|
|
14723
|
-
for (const entry of
|
|
14888
|
+
for (const entry of fs47.readdirSync(rulesDir, { withFileTypes: true })) {
|
|
14724
14889
|
if (entry.isDirectory()) {
|
|
14725
14890
|
count += countRulesInDir(path48.join(rulesDir, entry.name));
|
|
14726
14891
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -14746,7 +14911,7 @@ function countConfigs(cwd) {
|
|
|
14746
14911
|
let hooksCount = 0;
|
|
14747
14912
|
const userMcpServers = /* @__PURE__ */ new Set();
|
|
14748
14913
|
const projectMcpServers = /* @__PURE__ */ new Set();
|
|
14749
|
-
if (
|
|
14914
|
+
if (fs47.existsSync(path48.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
14750
14915
|
rulesCount += countRulesInDir(path48.join(claudeDir, "rules"));
|
|
14751
14916
|
const userSettings = path48.join(claudeDir, "settings.json");
|
|
14752
14917
|
for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
|
|
@@ -14757,18 +14922,18 @@ function countConfigs(cwd) {
|
|
|
14757
14922
|
userMcpServers.delete(name);
|
|
14758
14923
|
}
|
|
14759
14924
|
if (cwd) {
|
|
14760
|
-
if (
|
|
14761
|
-
if (
|
|
14925
|
+
if (fs47.existsSync(path48.join(cwd, "CLAUDE.md"))) claudeMdCount++;
|
|
14926
|
+
if (fs47.existsSync(path48.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
|
|
14762
14927
|
const projectClaudeDir = path48.join(cwd, ".claude");
|
|
14763
14928
|
const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
|
|
14764
14929
|
if (!overlapsUserScope) {
|
|
14765
|
-
if (
|
|
14930
|
+
if (fs47.existsSync(path48.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
14766
14931
|
rulesCount += countRulesInDir(path48.join(projectClaudeDir, "rules"));
|
|
14767
14932
|
const projSettings = path48.join(projectClaudeDir, "settings.json");
|
|
14768
14933
|
for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
|
|
14769
14934
|
hooksCount += countHooksInFile(projSettings);
|
|
14770
14935
|
}
|
|
14771
|
-
if (
|
|
14936
|
+
if (fs47.existsSync(path48.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
|
|
14772
14937
|
const localSettings = path48.join(projectClaudeDir, "settings.local.json");
|
|
14773
14938
|
for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
|
|
14774
14939
|
hooksCount += countHooksInFile(localSettings);
|
|
@@ -14806,11 +14971,11 @@ function readActiveShieldsHud() {
|
|
|
14806
14971
|
}
|
|
14807
14972
|
try {
|
|
14808
14973
|
const shieldsPath = path48.join(os42.homedir(), ".node9", "shields.json");
|
|
14809
|
-
if (!
|
|
14974
|
+
if (!fs47.existsSync(shieldsPath)) {
|
|
14810
14975
|
shieldsCache = { value: [], ts: now };
|
|
14811
14976
|
return [];
|
|
14812
14977
|
}
|
|
14813
|
-
const parsed = JSON.parse(
|
|
14978
|
+
const parsed = JSON.parse(fs47.readFileSync(shieldsPath, "utf-8"));
|
|
14814
14979
|
if (!Array.isArray(parsed.active)) {
|
|
14815
14980
|
shieldsCache = { value: [], ts: now };
|
|
14816
14981
|
return [];
|
|
@@ -14912,17 +15077,17 @@ function renderContextLine(stdin) {
|
|
|
14912
15077
|
async function main() {
|
|
14913
15078
|
try {
|
|
14914
15079
|
const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
|
|
14915
|
-
if (
|
|
15080
|
+
if (fs47.existsSync(path48.join(os42.homedir(), ".node9", "hud-debug"))) {
|
|
14916
15081
|
try {
|
|
14917
15082
|
const logPath = path48.join(os42.homedir(), ".node9", "hud-debug.log");
|
|
14918
15083
|
const MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
14919
15084
|
let size = 0;
|
|
14920
15085
|
try {
|
|
14921
|
-
size =
|
|
15086
|
+
size = fs47.statSync(logPath).size;
|
|
14922
15087
|
} catch {
|
|
14923
15088
|
}
|
|
14924
15089
|
if (size < MAX_LOG_SIZE) {
|
|
14925
|
-
|
|
15090
|
+
fs47.appendFileSync(
|
|
14926
15091
|
logPath,
|
|
14927
15092
|
JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
|
|
14928
15093
|
);
|
|
@@ -14946,8 +15111,8 @@ async function main() {
|
|
|
14946
15111
|
path48.join(cwd, "node9.config.json"),
|
|
14947
15112
|
path48.join(os42.homedir(), ".node9", "config.json")
|
|
14948
15113
|
]) {
|
|
14949
|
-
if (!
|
|
14950
|
-
const cfg = JSON.parse(
|
|
15114
|
+
if (!fs47.existsSync(configPath)) continue;
|
|
15115
|
+
const cfg = JSON.parse(fs47.readFileSync(configPath, "utf-8"));
|
|
14951
15116
|
const hud = cfg.settings?.hud;
|
|
14952
15117
|
if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
|
|
14953
15118
|
}
|
|
@@ -14994,7 +15159,7 @@ init_setup();
|
|
|
14994
15159
|
init_daemon2();
|
|
14995
15160
|
import { Command } from "commander";
|
|
14996
15161
|
import chalk30 from "chalk";
|
|
14997
|
-
import
|
|
15162
|
+
import fs48 from "fs";
|
|
14998
15163
|
import path49 from "path";
|
|
14999
15164
|
import os43 from "os";
|
|
15000
15165
|
import { confirm as confirm2 } from "@inquirer/prompts";
|
|
@@ -15179,17 +15344,17 @@ async function runProxy(targetCommand) {
|
|
|
15179
15344
|
// src/cli/daemon-starter.ts
|
|
15180
15345
|
init_daemon();
|
|
15181
15346
|
import { spawn as spawn3 } from "child_process";
|
|
15182
|
-
import
|
|
15183
|
-
import
|
|
15347
|
+
import path30 from "path";
|
|
15348
|
+
import fs29 from "fs";
|
|
15184
15349
|
function isTestingMode() {
|
|
15185
15350
|
return /^(1|true|yes)$/i.test(process.env.NODE9_TESTING ?? "");
|
|
15186
15351
|
}
|
|
15187
15352
|
async function autoStartDaemonAndWait() {
|
|
15188
15353
|
if (isTestingMode()) return false;
|
|
15189
|
-
if (!
|
|
15354
|
+
if (!path30.isAbsolute(process.argv[1])) return false;
|
|
15190
15355
|
let resolvedArgv1;
|
|
15191
15356
|
try {
|
|
15192
|
-
resolvedArgv1 =
|
|
15357
|
+
resolvedArgv1 = fs29.realpathSync(process.argv[1]);
|
|
15193
15358
|
} catch {
|
|
15194
15359
|
return false;
|
|
15195
15360
|
}
|
|
@@ -15220,19 +15385,19 @@ init_daemon();
|
|
|
15220
15385
|
init_config();
|
|
15221
15386
|
init_policy();
|
|
15222
15387
|
import chalk9 from "chalk";
|
|
15223
|
-
import
|
|
15388
|
+
import fs32 from "fs";
|
|
15224
15389
|
import { spawn as spawn5 } from "child_process";
|
|
15225
|
-
import
|
|
15226
|
-
import
|
|
15390
|
+
import path33 from "path";
|
|
15391
|
+
import os28 from "os";
|
|
15227
15392
|
|
|
15228
15393
|
// src/undo.ts
|
|
15229
15394
|
import { spawnSync as spawnSync3, spawn as spawn4 } from "child_process";
|
|
15230
|
-
import
|
|
15231
|
-
import
|
|
15395
|
+
import crypto4 from "crypto";
|
|
15396
|
+
import fs30 from "fs";
|
|
15232
15397
|
import net3 from "net";
|
|
15233
|
-
import
|
|
15234
|
-
import
|
|
15235
|
-
var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" :
|
|
15398
|
+
import path31 from "path";
|
|
15399
|
+
import os26 from "os";
|
|
15400
|
+
var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path31.join(os26.tmpdir(), "node9-activity.sock");
|
|
15236
15401
|
function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
|
|
15237
15402
|
try {
|
|
15238
15403
|
const payload = JSON.stringify({
|
|
@@ -15252,22 +15417,22 @@ function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
|
|
|
15252
15417
|
} catch {
|
|
15253
15418
|
}
|
|
15254
15419
|
}
|
|
15255
|
-
var SNAPSHOT_STACK_PATH =
|
|
15256
|
-
var UNDO_LATEST_PATH =
|
|
15420
|
+
var SNAPSHOT_STACK_PATH = path31.join(os26.homedir(), ".node9", "snapshots.json");
|
|
15421
|
+
var UNDO_LATEST_PATH = path31.join(os26.homedir(), ".node9", "undo_latest.txt");
|
|
15257
15422
|
var MAX_SNAPSHOTS = 10;
|
|
15258
15423
|
var GIT_TIMEOUT = 15e3;
|
|
15259
15424
|
function readStack() {
|
|
15260
15425
|
try {
|
|
15261
|
-
if (
|
|
15262
|
-
return JSON.parse(
|
|
15426
|
+
if (fs30.existsSync(SNAPSHOT_STACK_PATH))
|
|
15427
|
+
return JSON.parse(fs30.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
|
|
15263
15428
|
} catch {
|
|
15264
15429
|
}
|
|
15265
15430
|
return [];
|
|
15266
15431
|
}
|
|
15267
15432
|
function writeStack(stack) {
|
|
15268
|
-
const dir =
|
|
15269
|
-
if (!
|
|
15270
|
-
|
|
15433
|
+
const dir = path31.dirname(SNAPSHOT_STACK_PATH);
|
|
15434
|
+
if (!fs30.existsSync(dir)) fs30.mkdirSync(dir, { recursive: true });
|
|
15435
|
+
fs30.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
|
|
15271
15436
|
}
|
|
15272
15437
|
function extractFilePath(args) {
|
|
15273
15438
|
if (!args || typeof args !== "object") return null;
|
|
@@ -15287,12 +15452,12 @@ function buildArgsSummary(tool, args) {
|
|
|
15287
15452
|
return "";
|
|
15288
15453
|
}
|
|
15289
15454
|
function findProjectRoot(filePath) {
|
|
15290
|
-
let dir =
|
|
15455
|
+
let dir = path31.dirname(filePath);
|
|
15291
15456
|
while (true) {
|
|
15292
|
-
if (
|
|
15457
|
+
if (fs30.existsSync(path31.join(dir, ".git")) || fs30.existsSync(path31.join(dir, "package.json"))) {
|
|
15293
15458
|
return dir;
|
|
15294
15459
|
}
|
|
15295
|
-
const parent =
|
|
15460
|
+
const parent = path31.dirname(dir);
|
|
15296
15461
|
if (parent === dir) return process.cwd();
|
|
15297
15462
|
dir = parent;
|
|
15298
15463
|
}
|
|
@@ -15300,7 +15465,7 @@ function findProjectRoot(filePath) {
|
|
|
15300
15465
|
function normalizeCwdForHash(cwd) {
|
|
15301
15466
|
let normalized;
|
|
15302
15467
|
try {
|
|
15303
|
-
normalized =
|
|
15468
|
+
normalized = fs30.realpathSync(cwd);
|
|
15304
15469
|
} catch {
|
|
15305
15470
|
normalized = cwd;
|
|
15306
15471
|
}
|
|
@@ -15309,17 +15474,17 @@ function normalizeCwdForHash(cwd) {
|
|
|
15309
15474
|
return normalized;
|
|
15310
15475
|
}
|
|
15311
15476
|
function getShadowRepoDir(cwd) {
|
|
15312
|
-
const hash =
|
|
15313
|
-
return
|
|
15477
|
+
const hash = crypto4.createHash("sha256").update(normalizeCwdForHash(cwd)).digest("hex").slice(0, 16);
|
|
15478
|
+
return path31.join(os26.homedir(), ".node9", "snapshots", hash);
|
|
15314
15479
|
}
|
|
15315
15480
|
function cleanOrphanedIndexFiles(shadowDir) {
|
|
15316
15481
|
try {
|
|
15317
15482
|
const cutoff = Date.now() - 6e4;
|
|
15318
|
-
for (const f of
|
|
15483
|
+
for (const f of fs30.readdirSync(shadowDir)) {
|
|
15319
15484
|
if (f.startsWith("index_")) {
|
|
15320
|
-
const fp =
|
|
15485
|
+
const fp = path31.join(shadowDir, f);
|
|
15321
15486
|
try {
|
|
15322
|
-
if (
|
|
15487
|
+
if (fs30.statSync(fp).mtimeMs < cutoff) fs30.unlinkSync(fp);
|
|
15323
15488
|
} catch {
|
|
15324
15489
|
}
|
|
15325
15490
|
}
|
|
@@ -15331,7 +15496,7 @@ function writeShadowExcludes(shadowDir, ignorePaths) {
|
|
|
15331
15496
|
const hardcoded = [".git", ".node9"];
|
|
15332
15497
|
const lines = [...hardcoded, ...ignorePaths].join("\n");
|
|
15333
15498
|
try {
|
|
15334
|
-
|
|
15499
|
+
fs30.writeFileSync(path31.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
|
|
15335
15500
|
} catch {
|
|
15336
15501
|
}
|
|
15337
15502
|
}
|
|
@@ -15344,25 +15509,25 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
15344
15509
|
timeout: 3e3
|
|
15345
15510
|
});
|
|
15346
15511
|
if (check.status === 0) {
|
|
15347
|
-
const ptPath =
|
|
15512
|
+
const ptPath = path31.join(shadowDir, "project-path.txt");
|
|
15348
15513
|
try {
|
|
15349
|
-
const stored =
|
|
15514
|
+
const stored = fs30.readFileSync(ptPath, "utf8").trim();
|
|
15350
15515
|
if (stored === normalizedCwd) return true;
|
|
15351
15516
|
if (process.env.NODE9_DEBUG === "1")
|
|
15352
15517
|
console.error(
|
|
15353
15518
|
`[Node9] Shadow repo path mismatch: stored="${stored}" expected="${normalizedCwd}" \u2014 reinitializing`
|
|
15354
15519
|
);
|
|
15355
|
-
|
|
15520
|
+
fs30.rmSync(shadowDir, { recursive: true, force: true });
|
|
15356
15521
|
} catch {
|
|
15357
15522
|
try {
|
|
15358
|
-
|
|
15523
|
+
fs30.writeFileSync(ptPath, normalizedCwd, "utf8");
|
|
15359
15524
|
} catch {
|
|
15360
15525
|
}
|
|
15361
15526
|
return true;
|
|
15362
15527
|
}
|
|
15363
15528
|
}
|
|
15364
15529
|
try {
|
|
15365
|
-
|
|
15530
|
+
fs30.mkdirSync(shadowDir, { recursive: true });
|
|
15366
15531
|
} catch {
|
|
15367
15532
|
}
|
|
15368
15533
|
const init = spawnSync3("git", ["init", "--bare", shadowDir], { timeout: 5e3 });
|
|
@@ -15371,7 +15536,7 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
15371
15536
|
if (process.env.NODE9_DEBUG === "1") console.error("[Node9] git init --bare failed:", reason);
|
|
15372
15537
|
return false;
|
|
15373
15538
|
}
|
|
15374
|
-
const configFile =
|
|
15539
|
+
const configFile = path31.join(shadowDir, "config");
|
|
15375
15540
|
spawnSync3("git", ["config", "--file", configFile, "core.untrackedCache", "true"], {
|
|
15376
15541
|
timeout: 3e3
|
|
15377
15542
|
});
|
|
@@ -15379,7 +15544,7 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
15379
15544
|
timeout: 3e3
|
|
15380
15545
|
});
|
|
15381
15546
|
try {
|
|
15382
|
-
|
|
15547
|
+
fs30.writeFileSync(path31.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
|
|
15383
15548
|
} catch {
|
|
15384
15549
|
}
|
|
15385
15550
|
return true;
|
|
@@ -15402,12 +15567,12 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
15402
15567
|
let indexFile = null;
|
|
15403
15568
|
try {
|
|
15404
15569
|
const rawFilePath = extractFilePath(args);
|
|
15405
|
-
const absFilePath = rawFilePath &&
|
|
15570
|
+
const absFilePath = rawFilePath && path31.isAbsolute(rawFilePath) ? rawFilePath : null;
|
|
15406
15571
|
const cwd = absFilePath ? findProjectRoot(absFilePath) : process.cwd();
|
|
15407
15572
|
const shadowDir = getShadowRepoDir(cwd);
|
|
15408
15573
|
if (!ensureShadowRepo(shadowDir, cwd)) return null;
|
|
15409
15574
|
writeShadowExcludes(shadowDir, ignorePaths);
|
|
15410
|
-
indexFile =
|
|
15575
|
+
indexFile = path31.join(shadowDir, `index_${process.pid}_${Date.now()}`);
|
|
15411
15576
|
const shadowEnv = {
|
|
15412
15577
|
...process.env,
|
|
15413
15578
|
GIT_DIR: shadowDir,
|
|
@@ -15479,7 +15644,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
15479
15644
|
writeStack(stack);
|
|
15480
15645
|
const entry = stack[stack.length - 1];
|
|
15481
15646
|
notifySnapshotTaken(commitHash.slice(0, 7), tool, entry.argsSummary, capturedFiles.length);
|
|
15482
|
-
|
|
15647
|
+
fs30.writeFileSync(UNDO_LATEST_PATH, commitHash);
|
|
15483
15648
|
if (shouldGc) {
|
|
15484
15649
|
spawn4("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
|
|
15485
15650
|
}
|
|
@@ -15490,7 +15655,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
15490
15655
|
} finally {
|
|
15491
15656
|
if (indexFile) {
|
|
15492
15657
|
try {
|
|
15493
|
-
|
|
15658
|
+
fs30.unlinkSync(indexFile);
|
|
15494
15659
|
} catch {
|
|
15495
15660
|
}
|
|
15496
15661
|
}
|
|
@@ -15566,9 +15731,9 @@ function applyUndo(hash, cwd) {
|
|
|
15566
15731
|
timeout: GIT_TIMEOUT
|
|
15567
15732
|
}).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
|
|
15568
15733
|
for (const file of [...tracked, ...untracked]) {
|
|
15569
|
-
const fullPath =
|
|
15570
|
-
if (!snapshotFiles.has(file) &&
|
|
15571
|
-
|
|
15734
|
+
const fullPath = path31.join(dir, file);
|
|
15735
|
+
if (!snapshotFiles.has(file) && fs30.existsSync(fullPath)) {
|
|
15736
|
+
fs30.unlinkSync(fullPath);
|
|
15572
15737
|
}
|
|
15573
15738
|
}
|
|
15574
15739
|
return true;
|
|
@@ -15578,17 +15743,17 @@ function applyUndo(hash, cwd) {
|
|
|
15578
15743
|
}
|
|
15579
15744
|
|
|
15580
15745
|
// src/skill-pin.ts
|
|
15581
|
-
import
|
|
15582
|
-
import
|
|
15583
|
-
import
|
|
15584
|
-
import
|
|
15585
|
-
function
|
|
15586
|
-
return
|
|
15746
|
+
import fs31 from "fs";
|
|
15747
|
+
import path32 from "path";
|
|
15748
|
+
import os27 from "os";
|
|
15749
|
+
import crypto5 from "crypto";
|
|
15750
|
+
function getPinsFilePath2() {
|
|
15751
|
+
return path32.join(os27.homedir(), ".node9", "skill-pins.json");
|
|
15587
15752
|
}
|
|
15588
15753
|
var MAX_FILES = 5e3;
|
|
15589
15754
|
var MAX_TOTAL_BYTES = 50 * 1024 * 1024;
|
|
15590
15755
|
function sha256Bytes(buf) {
|
|
15591
|
-
return
|
|
15756
|
+
return crypto5.createHash("sha256").update(buf).digest("hex");
|
|
15592
15757
|
}
|
|
15593
15758
|
function walkDir(root) {
|
|
15594
15759
|
const out = [];
|
|
@@ -15597,18 +15762,18 @@ function walkDir(root) {
|
|
|
15597
15762
|
if (out.length >= MAX_FILES) return;
|
|
15598
15763
|
let entries;
|
|
15599
15764
|
try {
|
|
15600
|
-
entries =
|
|
15765
|
+
entries = fs31.readdirSync(dir, { withFileTypes: true });
|
|
15601
15766
|
} catch {
|
|
15602
15767
|
return;
|
|
15603
15768
|
}
|
|
15604
15769
|
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
15605
15770
|
for (const entry of entries) {
|
|
15606
15771
|
if (out.length >= MAX_FILES) return;
|
|
15607
|
-
const full =
|
|
15608
|
-
const rel = relDir ?
|
|
15772
|
+
const full = path32.join(dir, entry.name);
|
|
15773
|
+
const rel = relDir ? path32.posix.join(relDir, entry.name) : entry.name;
|
|
15609
15774
|
let lst;
|
|
15610
15775
|
try {
|
|
15611
|
-
lst =
|
|
15776
|
+
lst = fs31.lstatSync(full);
|
|
15612
15777
|
} catch {
|
|
15613
15778
|
continue;
|
|
15614
15779
|
}
|
|
@@ -15620,7 +15785,7 @@ function walkDir(root) {
|
|
|
15620
15785
|
if (!lst.isFile()) continue;
|
|
15621
15786
|
if (totalBytes + lst.size > MAX_TOTAL_BYTES) continue;
|
|
15622
15787
|
try {
|
|
15623
|
-
const buf =
|
|
15788
|
+
const buf = fs31.readFileSync(full);
|
|
15624
15789
|
totalBytes += buf.length;
|
|
15625
15790
|
out.push({ rel, hash: sha256Bytes(buf) });
|
|
15626
15791
|
} catch {
|
|
@@ -15634,32 +15799,32 @@ function walkDir(root) {
|
|
|
15634
15799
|
function hashSkillRoot(absPath) {
|
|
15635
15800
|
let lst;
|
|
15636
15801
|
try {
|
|
15637
|
-
lst =
|
|
15802
|
+
lst = fs31.lstatSync(absPath);
|
|
15638
15803
|
} catch {
|
|
15639
15804
|
return { exists: false, contentHash: "", fileCount: 0 };
|
|
15640
15805
|
}
|
|
15641
15806
|
if (lst.isSymbolicLink()) return { exists: false, contentHash: "", fileCount: 0 };
|
|
15642
15807
|
if (lst.isFile()) {
|
|
15643
15808
|
try {
|
|
15644
|
-
return { exists: true, contentHash: sha256Bytes(
|
|
15809
|
+
return { exists: true, contentHash: sha256Bytes(fs31.readFileSync(absPath)), fileCount: 1 };
|
|
15645
15810
|
} catch {
|
|
15646
15811
|
return { exists: false, contentHash: "", fileCount: 0 };
|
|
15647
15812
|
}
|
|
15648
15813
|
}
|
|
15649
15814
|
if (lst.isDirectory()) {
|
|
15650
15815
|
const entries = walkDir(absPath);
|
|
15651
|
-
const contentHash =
|
|
15816
|
+
const contentHash = crypto5.createHash("sha256").update(entries.join("\n")).digest("hex");
|
|
15652
15817
|
return { exists: true, contentHash, fileCount: entries.length };
|
|
15653
15818
|
}
|
|
15654
15819
|
return { exists: false, contentHash: "", fileCount: 0 };
|
|
15655
15820
|
}
|
|
15656
15821
|
function getRootKey(absPath) {
|
|
15657
|
-
return
|
|
15822
|
+
return crypto5.createHash("sha256").update(absPath).digest("hex").slice(0, 16);
|
|
15658
15823
|
}
|
|
15659
15824
|
function readSkillPinsSafe() {
|
|
15660
|
-
const filePath =
|
|
15825
|
+
const filePath = getPinsFilePath2();
|
|
15661
15826
|
try {
|
|
15662
|
-
const raw =
|
|
15827
|
+
const raw = fs31.readFileSync(filePath, "utf-8");
|
|
15663
15828
|
if (!raw.trim()) return { ok: false, reason: "corrupt", detail: "empty file" };
|
|
15664
15829
|
const parsed = JSON.parse(raw);
|
|
15665
15830
|
if (!parsed.roots || typeof parsed.roots !== "object" || Array.isArray(parsed.roots)) {
|
|
@@ -15678,18 +15843,18 @@ function readSkillPins() {
|
|
|
15678
15843
|
throw new Error(`[node9] skill pin file is corrupt: ${result.detail}`);
|
|
15679
15844
|
}
|
|
15680
15845
|
function writeSkillPins(data) {
|
|
15681
|
-
const filePath =
|
|
15682
|
-
|
|
15683
|
-
const tmp = `${filePath}.${
|
|
15684
|
-
|
|
15685
|
-
|
|
15846
|
+
const filePath = getPinsFilePath2();
|
|
15847
|
+
fs31.mkdirSync(path32.dirname(filePath), { recursive: true });
|
|
15848
|
+
const tmp = `${filePath}.${crypto5.randomBytes(6).toString("hex")}.tmp`;
|
|
15849
|
+
fs31.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
15850
|
+
fs31.renameSync(tmp, filePath);
|
|
15686
15851
|
}
|
|
15687
|
-
function
|
|
15852
|
+
function removePin2(rootKey) {
|
|
15688
15853
|
const pins = readSkillPins();
|
|
15689
15854
|
delete pins.roots[rootKey];
|
|
15690
15855
|
writeSkillPins(pins);
|
|
15691
15856
|
}
|
|
15692
|
-
function
|
|
15857
|
+
function clearAllPins2() {
|
|
15693
15858
|
writeSkillPins({ roots: {} });
|
|
15694
15859
|
}
|
|
15695
15860
|
function verifyAndPinRoots(roots) {
|
|
@@ -15726,36 +15891,36 @@ function verifyAndPinRoots(roots) {
|
|
|
15726
15891
|
return { kind: "verified" };
|
|
15727
15892
|
}
|
|
15728
15893
|
function defaultSkillRoots(_cwd) {
|
|
15729
|
-
const marketplaces =
|
|
15894
|
+
const marketplaces = path32.join(os27.homedir(), ".claude", "plugins", "marketplaces");
|
|
15730
15895
|
const roots = [];
|
|
15731
15896
|
let registries;
|
|
15732
15897
|
try {
|
|
15733
|
-
registries =
|
|
15898
|
+
registries = fs31.readdirSync(marketplaces, { withFileTypes: true });
|
|
15734
15899
|
} catch {
|
|
15735
15900
|
return [];
|
|
15736
15901
|
}
|
|
15737
15902
|
for (const registry of registries) {
|
|
15738
15903
|
if (!registry.isDirectory()) continue;
|
|
15739
|
-
const pluginsDir =
|
|
15904
|
+
const pluginsDir = path32.join(marketplaces, registry.name, "plugins");
|
|
15740
15905
|
let plugins;
|
|
15741
15906
|
try {
|
|
15742
|
-
plugins =
|
|
15907
|
+
plugins = fs31.readdirSync(pluginsDir, { withFileTypes: true });
|
|
15743
15908
|
} catch {
|
|
15744
15909
|
continue;
|
|
15745
15910
|
}
|
|
15746
15911
|
for (const plugin of plugins) {
|
|
15747
15912
|
if (!plugin.isDirectory()) continue;
|
|
15748
|
-
roots.push(
|
|
15913
|
+
roots.push(path32.join(pluginsDir, plugin.name));
|
|
15749
15914
|
}
|
|
15750
15915
|
}
|
|
15751
15916
|
return roots;
|
|
15752
15917
|
}
|
|
15753
15918
|
function resolveUserSkillRoot(entry, cwd) {
|
|
15754
15919
|
if (!entry) return null;
|
|
15755
|
-
if (entry.startsWith("~/") || entry === "~") return
|
|
15756
|
-
if (
|
|
15757
|
-
if (!cwd || !
|
|
15758
|
-
return
|
|
15920
|
+
if (entry.startsWith("~/") || entry === "~") return path32.join(os27.homedir(), entry.slice(1));
|
|
15921
|
+
if (path32.isAbsolute(entry)) return entry;
|
|
15922
|
+
if (!cwd || !path32.isAbsolute(cwd)) return null;
|
|
15923
|
+
return path32.join(cwd, entry);
|
|
15759
15924
|
}
|
|
15760
15925
|
|
|
15761
15926
|
// src/cli/commands/check.ts
|
|
@@ -15802,9 +15967,9 @@ function registerCheckCommand(program2) {
|
|
|
15802
15967
|
} catch (err2) {
|
|
15803
15968
|
const tempConfig = getConfig();
|
|
15804
15969
|
if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
|
|
15805
|
-
const logPath =
|
|
15970
|
+
const logPath = path33.join(os28.homedir(), ".node9", "hook-debug.log");
|
|
15806
15971
|
const errMsg = err2 instanceof Error ? err2.message : String(err2);
|
|
15807
|
-
|
|
15972
|
+
fs32.appendFileSync(
|
|
15808
15973
|
logPath,
|
|
15809
15974
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
|
|
15810
15975
|
RAW: ${raw}
|
|
@@ -15817,14 +15982,14 @@ RAW: ${raw}
|
|
|
15817
15982
|
const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
|
|
15818
15983
|
if (process.env.NODE9_DEBUG === "1") {
|
|
15819
15984
|
try {
|
|
15820
|
-
const logPath =
|
|
15821
|
-
if (!
|
|
15822
|
-
|
|
15985
|
+
const logPath = path33.join(os28.homedir(), ".node9", "hook-debug.log");
|
|
15986
|
+
if (!fs32.existsSync(path33.dirname(logPath)))
|
|
15987
|
+
fs32.mkdirSync(path33.dirname(logPath), { recursive: true });
|
|
15823
15988
|
const sanitized = JSON.stringify({
|
|
15824
15989
|
...payload,
|
|
15825
15990
|
prompt: `<redacted, ${prompt.length} bytes>`
|
|
15826
15991
|
});
|
|
15827
|
-
|
|
15992
|
+
fs32.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${sanitized}
|
|
15828
15993
|
`);
|
|
15829
15994
|
} catch {
|
|
15830
15995
|
}
|
|
@@ -15844,8 +16009,8 @@ RAW: ${raw}
|
|
|
15844
16009
|
);
|
|
15845
16010
|
const reason = `\u{1F6A8} Node9 DLP: ${dlpMatch.patternName} detected in prompt (${dlpMatch.redactedSample}). Prompt was not submitted \u2014 remove the credential and try again.`;
|
|
15846
16011
|
try {
|
|
15847
|
-
const ttyFd =
|
|
15848
|
-
|
|
16012
|
+
const ttyFd = fs32.openSync("/dev/tty", "w");
|
|
16013
|
+
fs32.writeSync(
|
|
15849
16014
|
ttyFd,
|
|
15850
16015
|
chalk9.bgRed.white.bold(`
|
|
15851
16016
|
\u{1F6A8} NODE9 DLP \u2014 PROMPT BLOCKED
|
|
@@ -15855,7 +16020,7 @@ RAW: ${raw}
|
|
|
15855
16020
|
|
|
15856
16021
|
`)
|
|
15857
16022
|
);
|
|
15858
|
-
|
|
16023
|
+
fs32.closeSync(ttyFd);
|
|
15859
16024
|
} catch {
|
|
15860
16025
|
}
|
|
15861
16026
|
const isCodex = agent2 === "Codex";
|
|
@@ -15873,16 +16038,16 @@ RAW: ${raw}
|
|
|
15873
16038
|
);
|
|
15874
16039
|
process.exit(2);
|
|
15875
16040
|
}
|
|
15876
|
-
const safeCwdForConfig = typeof payload.cwd === "string" &&
|
|
16041
|
+
const safeCwdForConfig = typeof payload.cwd === "string" && path33.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
15877
16042
|
const config = getConfig(safeCwdForConfig);
|
|
15878
16043
|
if (config.settings.autoStartDaemon && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON) {
|
|
15879
16044
|
try {
|
|
15880
16045
|
const scriptPath = process.argv[1];
|
|
15881
|
-
if (typeof scriptPath !== "string" || !
|
|
16046
|
+
if (typeof scriptPath !== "string" || !path33.isAbsolute(scriptPath))
|
|
15882
16047
|
throw new Error("node9: argv[1] is not an absolute path");
|
|
15883
|
-
const resolvedScript =
|
|
15884
|
-
const packageDist =
|
|
15885
|
-
if (!resolvedScript.startsWith(packageDist +
|
|
16048
|
+
const resolvedScript = fs32.realpathSync(scriptPath);
|
|
16049
|
+
const packageDist = fs32.realpathSync(path33.resolve(__dirname, "../.."));
|
|
16050
|
+
if (!resolvedScript.startsWith(packageDist + path33.sep) && resolvedScript !== packageDist)
|
|
15886
16051
|
throw new Error(
|
|
15887
16052
|
`node9: daemon spawn aborted \u2014 argv[1] (${resolvedScript}) is outside package dist (${packageDist})`
|
|
15888
16053
|
);
|
|
@@ -15904,10 +16069,10 @@ RAW: ${raw}
|
|
|
15904
16069
|
});
|
|
15905
16070
|
d.unref();
|
|
15906
16071
|
} catch (spawnErr) {
|
|
15907
|
-
const logPath =
|
|
16072
|
+
const logPath = path33.join(os28.homedir(), ".node9", "hook-debug.log");
|
|
15908
16073
|
const msg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);
|
|
15909
16074
|
try {
|
|
15910
|
-
|
|
16075
|
+
fs32.appendFileSync(
|
|
15911
16076
|
logPath,
|
|
15912
16077
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-autostart-failed: ${msg}
|
|
15913
16078
|
`
|
|
@@ -15917,10 +16082,10 @@ RAW: ${raw}
|
|
|
15917
16082
|
}
|
|
15918
16083
|
}
|
|
15919
16084
|
if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
|
|
15920
|
-
const logPath =
|
|
15921
|
-
if (!
|
|
15922
|
-
|
|
15923
|
-
|
|
16085
|
+
const logPath = path33.join(os28.homedir(), ".node9", "hook-debug.log");
|
|
16086
|
+
if (!fs32.existsSync(path33.dirname(logPath)))
|
|
16087
|
+
fs32.mkdirSync(path33.dirname(logPath), { recursive: true });
|
|
16088
|
+
fs32.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
|
|
15924
16089
|
`);
|
|
15925
16090
|
}
|
|
15926
16091
|
const toolName = sanitize2(payload.tool_name ?? payload.name ?? "");
|
|
@@ -15933,8 +16098,8 @@ RAW: ${raw}
|
|
|
15933
16098
|
const isHumanDecision = blockedByContext.toLowerCase().includes("user") || blockedByContext.toLowerCase().includes("daemon") || blockedByContext.toLowerCase().includes("decision");
|
|
15934
16099
|
let ttyFd = null;
|
|
15935
16100
|
try {
|
|
15936
|
-
ttyFd =
|
|
15937
|
-
const writeTty = (line) =>
|
|
16101
|
+
ttyFd = fs32.openSync("/dev/tty", "w");
|
|
16102
|
+
const writeTty = (line) => fs32.writeSync(ttyFd, line + "\n");
|
|
15938
16103
|
if (blockedByContext.includes("DLP") || blockedByContext.includes("Secret Detected") || blockedByContext.includes("Credential Review")) {
|
|
15939
16104
|
writeTty(chalk9.bgRed.white.bold(`
|
|
15940
16105
|
\u{1F6A8} NODE9 DLP ALERT \u2014 CREDENTIAL DETECTED `));
|
|
@@ -15953,7 +16118,7 @@ RAW: ${raw}
|
|
|
15953
16118
|
} finally {
|
|
15954
16119
|
if (ttyFd !== null)
|
|
15955
16120
|
try {
|
|
15956
|
-
|
|
16121
|
+
fs32.closeSync(ttyFd);
|
|
15957
16122
|
} catch {
|
|
15958
16123
|
}
|
|
15959
16124
|
}
|
|
@@ -15989,17 +16154,17 @@ RAW: ${raw}
|
|
|
15989
16154
|
const safeSessionId = /^[A-Za-z0-9_\-]{1,128}$/.test(rawSessionId) ? rawSessionId : "";
|
|
15990
16155
|
if (skillPinCfg.enabled && safeSessionId) {
|
|
15991
16156
|
try {
|
|
15992
|
-
const sessionsDir =
|
|
15993
|
-
const flagPath =
|
|
16157
|
+
const sessionsDir = path33.join(os28.homedir(), ".node9", "skill-sessions");
|
|
16158
|
+
const flagPath = path33.join(sessionsDir, `${safeSessionId}.json`);
|
|
15994
16159
|
let flag = null;
|
|
15995
16160
|
try {
|
|
15996
|
-
flag = JSON.parse(
|
|
16161
|
+
flag = JSON.parse(fs32.readFileSync(flagPath, "utf-8"));
|
|
15997
16162
|
} catch {
|
|
15998
16163
|
}
|
|
15999
16164
|
const writeFlag = (data2) => {
|
|
16000
16165
|
try {
|
|
16001
|
-
|
|
16002
|
-
|
|
16166
|
+
fs32.mkdirSync(sessionsDir, { recursive: true });
|
|
16167
|
+
fs32.writeFileSync(
|
|
16003
16168
|
flagPath,
|
|
16004
16169
|
JSON.stringify({ ...data2, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
|
|
16005
16170
|
{ mode: 384 }
|
|
@@ -16010,8 +16175,8 @@ RAW: ${raw}
|
|
|
16010
16175
|
const sendSkillWarn = (detail, recoveryCmd) => {
|
|
16011
16176
|
let ttyFd = null;
|
|
16012
16177
|
try {
|
|
16013
|
-
ttyFd =
|
|
16014
|
-
const w = (line) =>
|
|
16178
|
+
ttyFd = fs32.openSync("/dev/tty", "w");
|
|
16179
|
+
const w = (line) => fs32.writeSync(ttyFd, line + "\n");
|
|
16015
16180
|
w(chalk9.yellow(`
|
|
16016
16181
|
\u26A0\uFE0F Node9: installed skill drift detected`));
|
|
16017
16182
|
w(chalk9.gray(` ${detail}`));
|
|
@@ -16026,7 +16191,7 @@ RAW: ${raw}
|
|
|
16026
16191
|
} finally {
|
|
16027
16192
|
if (ttyFd !== null)
|
|
16028
16193
|
try {
|
|
16029
|
-
|
|
16194
|
+
fs32.closeSync(ttyFd);
|
|
16030
16195
|
} catch {
|
|
16031
16196
|
}
|
|
16032
16197
|
}
|
|
@@ -16042,7 +16207,7 @@ RAW: ${raw}
|
|
|
16042
16207
|
return;
|
|
16043
16208
|
}
|
|
16044
16209
|
if (!flag || flag.state !== "verified" && flag.state !== "warned") {
|
|
16045
|
-
const absoluteCwd = typeof payload.cwd === "string" &&
|
|
16210
|
+
const absoluteCwd = typeof payload.cwd === "string" && path33.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
16046
16211
|
const extraRoots = skillPinCfg.roots;
|
|
16047
16212
|
const resolvedExtra = extraRoots.map((r) => resolveUserSkillRoot(r, absoluteCwd)).filter((r) => typeof r === "string");
|
|
16048
16213
|
const roots = [...defaultSkillRoots(absoluteCwd), ...resolvedExtra];
|
|
@@ -16083,10 +16248,10 @@ RAW: ${raw}
|
|
|
16083
16248
|
}
|
|
16084
16249
|
try {
|
|
16085
16250
|
const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1e3;
|
|
16086
|
-
for (const name of
|
|
16087
|
-
const p =
|
|
16251
|
+
for (const name of fs32.readdirSync(sessionsDir)) {
|
|
16252
|
+
const p = path33.join(sessionsDir, name);
|
|
16088
16253
|
try {
|
|
16089
|
-
if (
|
|
16254
|
+
if (fs32.statSync(p).mtimeMs < cutoff) fs32.unlinkSync(p);
|
|
16090
16255
|
} catch {
|
|
16091
16256
|
}
|
|
16092
16257
|
}
|
|
@@ -16096,9 +16261,9 @@ RAW: ${raw}
|
|
|
16096
16261
|
} catch (err2) {
|
|
16097
16262
|
if (process.env.NODE9_DEBUG === "1") {
|
|
16098
16263
|
try {
|
|
16099
|
-
const dbg =
|
|
16264
|
+
const dbg = path33.join(os28.homedir(), ".node9", "hook-debug.log");
|
|
16100
16265
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
16101
|
-
|
|
16266
|
+
fs32.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
|
|
16102
16267
|
`);
|
|
16103
16268
|
} catch {
|
|
16104
16269
|
}
|
|
@@ -16108,7 +16273,7 @@ RAW: ${raw}
|
|
|
16108
16273
|
if (shouldSnapshot(toolName, toolInput, config)) {
|
|
16109
16274
|
await createShadowSnapshot(toolName, toolInput, config.policy.snapshot.ignorePaths);
|
|
16110
16275
|
}
|
|
16111
|
-
const safeCwdForAuth = typeof payload.cwd === "string" &&
|
|
16276
|
+
const safeCwdForAuth = typeof payload.cwd === "string" && path33.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
16112
16277
|
const result = await authorizeHeadless(toolName, toolInput, meta, {
|
|
16113
16278
|
cwd: safeCwdForAuth
|
|
16114
16279
|
});
|
|
@@ -16120,12 +16285,12 @@ RAW: ${raw}
|
|
|
16120
16285
|
}
|
|
16121
16286
|
if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && !process.stdout.isTTY && config.settings.autoStartDaemon) {
|
|
16122
16287
|
try {
|
|
16123
|
-
const tty =
|
|
16124
|
-
|
|
16288
|
+
const tty = fs32.openSync("/dev/tty", "w");
|
|
16289
|
+
fs32.writeSync(
|
|
16125
16290
|
tty,
|
|
16126
16291
|
chalk9.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically...\n")
|
|
16127
16292
|
);
|
|
16128
|
-
|
|
16293
|
+
fs32.closeSync(tty);
|
|
16129
16294
|
} catch {
|
|
16130
16295
|
}
|
|
16131
16296
|
const daemonReady = await autoStartDaemonAndWait();
|
|
@@ -16152,9 +16317,9 @@ RAW: ${raw}
|
|
|
16152
16317
|
});
|
|
16153
16318
|
} catch (err2) {
|
|
16154
16319
|
if (process.env.NODE9_DEBUG === "1") {
|
|
16155
|
-
const logPath =
|
|
16320
|
+
const logPath = path33.join(os28.homedir(), ".node9", "hook-debug.log");
|
|
16156
16321
|
const errMsg = err2 instanceof Error ? err2.message : String(err2);
|
|
16157
|
-
|
|
16322
|
+
fs32.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
|
|
16158
16323
|
`);
|
|
16159
16324
|
}
|
|
16160
16325
|
process.exit(0);
|
|
@@ -16190,9 +16355,9 @@ RAW: ${raw}
|
|
|
16190
16355
|
// src/cli/commands/log.ts
|
|
16191
16356
|
init_audit();
|
|
16192
16357
|
init_config();
|
|
16193
|
-
import
|
|
16194
|
-
import
|
|
16195
|
-
import
|
|
16358
|
+
import fs33 from "fs";
|
|
16359
|
+
import path34 from "path";
|
|
16360
|
+
import os29 from "os";
|
|
16196
16361
|
init_daemon();
|
|
16197
16362
|
|
|
16198
16363
|
// src/utils/cp-mv-parser.ts
|
|
@@ -16268,10 +16433,10 @@ function registerLogCommand(program2) {
|
|
|
16268
16433
|
};
|
|
16269
16434
|
if (agent) entry.agent = agent;
|
|
16270
16435
|
if (payload.session_id) entry.sessionId = payload.session_id;
|
|
16271
|
-
const logPath =
|
|
16272
|
-
if (!
|
|
16273
|
-
|
|
16274
|
-
|
|
16436
|
+
const logPath = path34.join(os29.homedir(), ".node9", "audit.log");
|
|
16437
|
+
if (!fs33.existsSync(path34.dirname(logPath)))
|
|
16438
|
+
fs33.mkdirSync(path34.dirname(logPath), { recursive: true });
|
|
16439
|
+
fs33.appendFileSync(logPath, JSON.stringify(entry) + "\n");
|
|
16275
16440
|
if ((tool === "Bash" || tool === "bash") && isDaemonRunning()) {
|
|
16276
16441
|
const command = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
|
|
16277
16442
|
if (command) {
|
|
@@ -16304,7 +16469,7 @@ function registerLogCommand(program2) {
|
|
|
16304
16469
|
}
|
|
16305
16470
|
}
|
|
16306
16471
|
}
|
|
16307
|
-
const safeCwd = typeof payload.cwd === "string" &&
|
|
16472
|
+
const safeCwd = typeof payload.cwd === "string" && path34.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
16308
16473
|
const config = getConfig(safeCwd);
|
|
16309
16474
|
if ((tool === "Bash" || tool === "bash") && config.settings.enableUndo !== false) {
|
|
16310
16475
|
const bashCommand = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
|
|
@@ -16325,9 +16490,9 @@ function registerLogCommand(program2) {
|
|
|
16325
16490
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
16326
16491
|
process.stderr.write(`[Node9] audit log error: ${msg}
|
|
16327
16492
|
`);
|
|
16328
|
-
const debugPath =
|
|
16493
|
+
const debugPath = path34.join(os29.homedir(), ".node9", "hook-debug.log");
|
|
16329
16494
|
try {
|
|
16330
|
-
|
|
16495
|
+
fs33.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
|
|
16331
16496
|
`);
|
|
16332
16497
|
} catch {
|
|
16333
16498
|
}
|
|
@@ -16728,13 +16893,13 @@ function registerConfigShowCommand(program2) {
|
|
|
16728
16893
|
// src/cli/commands/doctor.ts
|
|
16729
16894
|
init_daemon();
|
|
16730
16895
|
import chalk11 from "chalk";
|
|
16731
|
-
import
|
|
16732
|
-
import
|
|
16733
|
-
import
|
|
16896
|
+
import fs34 from "fs";
|
|
16897
|
+
import path35 from "path";
|
|
16898
|
+
import os30 from "os";
|
|
16734
16899
|
import { execSync } from "child_process";
|
|
16735
16900
|
function registerDoctorCommand(program2, version2) {
|
|
16736
16901
|
program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(() => {
|
|
16737
|
-
const homeDir2 =
|
|
16902
|
+
const homeDir2 = os30.homedir();
|
|
16738
16903
|
let failures = 0;
|
|
16739
16904
|
function pass(msg) {
|
|
16740
16905
|
console.log(chalk11.green(" \u2705 ") + msg);
|
|
@@ -16783,10 +16948,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16783
16948
|
);
|
|
16784
16949
|
}
|
|
16785
16950
|
section("Configuration");
|
|
16786
|
-
const globalConfigPath =
|
|
16787
|
-
if (
|
|
16951
|
+
const globalConfigPath = path35.join(homeDir2, ".node9", "config.json");
|
|
16952
|
+
if (fs34.existsSync(globalConfigPath)) {
|
|
16788
16953
|
try {
|
|
16789
|
-
JSON.parse(
|
|
16954
|
+
JSON.parse(fs34.readFileSync(globalConfigPath, "utf-8"));
|
|
16790
16955
|
pass("~/.node9/config.json found and valid");
|
|
16791
16956
|
} catch {
|
|
16792
16957
|
fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
|
|
@@ -16794,10 +16959,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16794
16959
|
} else {
|
|
16795
16960
|
warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
|
|
16796
16961
|
}
|
|
16797
|
-
const projectConfigPath =
|
|
16798
|
-
if (
|
|
16962
|
+
const projectConfigPath = path35.join(process.cwd(), "node9.config.json");
|
|
16963
|
+
if (fs34.existsSync(projectConfigPath)) {
|
|
16799
16964
|
try {
|
|
16800
|
-
JSON.parse(
|
|
16965
|
+
JSON.parse(fs34.readFileSync(projectConfigPath, "utf-8"));
|
|
16801
16966
|
pass("node9.config.json found and valid (project)");
|
|
16802
16967
|
} catch {
|
|
16803
16968
|
fail(
|
|
@@ -16806,8 +16971,8 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16806
16971
|
);
|
|
16807
16972
|
}
|
|
16808
16973
|
}
|
|
16809
|
-
const credsPath =
|
|
16810
|
-
if (
|
|
16974
|
+
const credsPath = path35.join(homeDir2, ".node9", "credentials.json");
|
|
16975
|
+
if (fs34.existsSync(credsPath)) {
|
|
16811
16976
|
pass("Cloud credentials found (~/.node9/credentials.json)");
|
|
16812
16977
|
} else {
|
|
16813
16978
|
warn(
|
|
@@ -16816,10 +16981,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16816
16981
|
);
|
|
16817
16982
|
}
|
|
16818
16983
|
section("Agent Hooks");
|
|
16819
|
-
const claudeSettingsPath =
|
|
16820
|
-
if (
|
|
16984
|
+
const claudeSettingsPath = path35.join(homeDir2, ".claude", "settings.json");
|
|
16985
|
+
if (fs34.existsSync(claudeSettingsPath)) {
|
|
16821
16986
|
try {
|
|
16822
|
-
const cs = JSON.parse(
|
|
16987
|
+
const cs = JSON.parse(fs34.readFileSync(claudeSettingsPath, "utf-8"));
|
|
16823
16988
|
const hasHook = cs.hooks?.PreToolUse?.some(
|
|
16824
16989
|
(m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
|
|
16825
16990
|
);
|
|
@@ -16835,10 +17000,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16835
17000
|
} else {
|
|
16836
17001
|
warn("Claude Code \u2014 not configured", "Run: node9 setup claude");
|
|
16837
17002
|
}
|
|
16838
|
-
const geminiSettingsPath =
|
|
16839
|
-
if (
|
|
17003
|
+
const geminiSettingsPath = path35.join(homeDir2, ".gemini", "settings.json");
|
|
17004
|
+
if (fs34.existsSync(geminiSettingsPath)) {
|
|
16840
17005
|
try {
|
|
16841
|
-
const gs = JSON.parse(
|
|
17006
|
+
const gs = JSON.parse(fs34.readFileSync(geminiSettingsPath, "utf-8"));
|
|
16842
17007
|
const hasHook = gs.hooks?.BeforeTool?.some(
|
|
16843
17008
|
(m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
|
|
16844
17009
|
);
|
|
@@ -16854,10 +17019,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16854
17019
|
} else {
|
|
16855
17020
|
warn("Gemini CLI \u2014 not configured", "Run: node9 setup gemini (skip if not using Gemini)");
|
|
16856
17021
|
}
|
|
16857
|
-
const cursorHooksPath =
|
|
16858
|
-
if (
|
|
17022
|
+
const cursorHooksPath = path35.join(homeDir2, ".cursor", "hooks.json");
|
|
17023
|
+
if (fs34.existsSync(cursorHooksPath)) {
|
|
16859
17024
|
try {
|
|
16860
|
-
const cur = JSON.parse(
|
|
17025
|
+
const cur = JSON.parse(fs34.readFileSync(cursorHooksPath, "utf-8"));
|
|
16861
17026
|
const hasHook = cur.hooks?.preToolUse?.some(
|
|
16862
17027
|
(h) => h.command?.includes("node9") || h.command?.includes("cli.js")
|
|
16863
17028
|
);
|
|
@@ -16897,9 +17062,9 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16897
17062
|
|
|
16898
17063
|
// src/cli/commands/audit.ts
|
|
16899
17064
|
import chalk12 from "chalk";
|
|
16900
|
-
import
|
|
16901
|
-
import
|
|
16902
|
-
import
|
|
17065
|
+
import fs35 from "fs";
|
|
17066
|
+
import path36 from "path";
|
|
17067
|
+
import os31 from "os";
|
|
16903
17068
|
function formatRelativeTime(timestamp) {
|
|
16904
17069
|
const diff = Date.now() - new Date(timestamp).getTime();
|
|
16905
17070
|
const sec = Math.floor(diff / 1e3);
|
|
@@ -16912,14 +17077,14 @@ function formatRelativeTime(timestamp) {
|
|
|
16912
17077
|
}
|
|
16913
17078
|
function registerAuditCommand(program2) {
|
|
16914
17079
|
program2.command("audit").description("View local execution audit log").option("--tail <n>", "Number of entries to show", "20").option("--tool <pattern>", "Filter by tool name (substring match)").option("--deny", "Show only denied actions").option("--json", "Output raw JSON").action((options) => {
|
|
16915
|
-
const logPath =
|
|
16916
|
-
if (!
|
|
17080
|
+
const logPath = path36.join(os31.homedir(), ".node9", "audit.log");
|
|
17081
|
+
if (!fs35.existsSync(logPath)) {
|
|
16917
17082
|
console.log(
|
|
16918
17083
|
chalk12.yellow("No audit logs found. Run node9 with an agent to generate entries.")
|
|
16919
17084
|
);
|
|
16920
17085
|
return;
|
|
16921
17086
|
}
|
|
16922
|
-
const raw =
|
|
17087
|
+
const raw = fs35.readFileSync(logPath, "utf-8");
|
|
16923
17088
|
const lines = raw.split("\n").filter((l) => l.trim() !== "");
|
|
16924
17089
|
let entries = lines.flatMap((line) => {
|
|
16925
17090
|
try {
|
|
@@ -16977,9 +17142,9 @@ import chalk13 from "chalk";
|
|
|
16977
17142
|
// src/cli/aggregate/report-audit.ts
|
|
16978
17143
|
init_costSync();
|
|
16979
17144
|
init_litellm();
|
|
16980
|
-
import
|
|
16981
|
-
import
|
|
16982
|
-
import
|
|
17145
|
+
import fs36 from "fs";
|
|
17146
|
+
import os32 from "os";
|
|
17147
|
+
import path37 from "path";
|
|
16983
17148
|
var TEST_COMMAND_RE3 = /(?:^|\s)(npm\s+(?:run\s+)?test|npx\s+(?:vitest|jest|mocha)|yarn\s+(?:run\s+)?test|pnpm\s+(?:run\s+)?test|vitest|jest|mocha|pytest|py\.test|cargo\s+test|go\s+test|bundle\s+exec\s+rspec|rspec|phpunit|dotnet\s+test)\b/i;
|
|
16984
17149
|
function buildTestTimestamps(allEntries) {
|
|
16985
17150
|
const testTs = /* @__PURE__ */ new Set();
|
|
@@ -17059,8 +17224,8 @@ function getDateRange(period, now) {
|
|
|
17059
17224
|
}
|
|
17060
17225
|
}
|
|
17061
17226
|
function parseAuditLog(logPath) {
|
|
17062
|
-
if (!
|
|
17063
|
-
const raw =
|
|
17227
|
+
if (!fs36.existsSync(logPath)) return [];
|
|
17228
|
+
const raw = fs36.readFileSync(logPath, "utf-8");
|
|
17064
17229
|
return raw.split("\n").flatMap((line) => {
|
|
17065
17230
|
if (!line.trim()) return [];
|
|
17066
17231
|
try {
|
|
@@ -17120,25 +17285,25 @@ function freezeClaudeCost(acc) {
|
|
|
17120
17285
|
};
|
|
17121
17286
|
}
|
|
17122
17287
|
function processClaudeCostProject(proj, projectsDir, start, end, acc) {
|
|
17123
|
-
const projPath =
|
|
17288
|
+
const projPath = path37.join(projectsDir, proj);
|
|
17124
17289
|
let files;
|
|
17125
17290
|
try {
|
|
17126
|
-
const stat =
|
|
17291
|
+
const stat = fs36.statSync(projPath);
|
|
17127
17292
|
if (!stat.isDirectory()) return;
|
|
17128
|
-
files =
|
|
17293
|
+
files = fs36.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
17129
17294
|
} catch {
|
|
17130
17295
|
return;
|
|
17131
17296
|
}
|
|
17132
17297
|
const startMs = start.getTime();
|
|
17133
17298
|
for (const file of files) {
|
|
17134
|
-
const filePath =
|
|
17299
|
+
const filePath = path37.join(projPath, file);
|
|
17135
17300
|
try {
|
|
17136
|
-
if (
|
|
17301
|
+
if (fs36.statSync(filePath).mtimeMs < startMs) continue;
|
|
17137
17302
|
} catch {
|
|
17138
17303
|
continue;
|
|
17139
17304
|
}
|
|
17140
17305
|
try {
|
|
17141
|
-
const raw =
|
|
17306
|
+
const raw = fs36.readFileSync(filePath, "utf-8");
|
|
17142
17307
|
for (const line of raw.split("\n")) {
|
|
17143
17308
|
if (!line.trim()) continue;
|
|
17144
17309
|
let entry;
|
|
@@ -17188,10 +17353,10 @@ function processClaudeCostProject(proj, projectsDir, start, end, acc) {
|
|
|
17188
17353
|
}
|
|
17189
17354
|
function loadClaudeCost(start, end, projectsDir) {
|
|
17190
17355
|
const acc = emptyClaudeCostAccumulator();
|
|
17191
|
-
if (!
|
|
17356
|
+
if (!fs36.existsSync(projectsDir)) return freezeClaudeCost(acc);
|
|
17192
17357
|
let dirs;
|
|
17193
17358
|
try {
|
|
17194
|
-
dirs =
|
|
17359
|
+
dirs = fs36.readdirSync(projectsDir);
|
|
17195
17360
|
} catch {
|
|
17196
17361
|
return freezeClaudeCost(acc);
|
|
17197
17362
|
}
|
|
@@ -17203,7 +17368,7 @@ function loadClaudeCost(start, end, projectsDir) {
|
|
|
17203
17368
|
function processCodexCostFile(filePath, start, end, acc) {
|
|
17204
17369
|
let lines;
|
|
17205
17370
|
try {
|
|
17206
|
-
lines =
|
|
17371
|
+
lines = fs36.readFileSync(filePath, "utf-8").split("\n");
|
|
17207
17372
|
} catch {
|
|
17208
17373
|
return;
|
|
17209
17374
|
}
|
|
@@ -17248,31 +17413,31 @@ function processCodexCostFile(filePath, start, end, acc) {
|
|
|
17248
17413
|
}
|
|
17249
17414
|
function listCodexSessionFiles(sessionsBase) {
|
|
17250
17415
|
const jsonlFiles = [];
|
|
17251
|
-
if (!
|
|
17416
|
+
if (!fs36.existsSync(sessionsBase)) return jsonlFiles;
|
|
17252
17417
|
try {
|
|
17253
|
-
for (const year of
|
|
17254
|
-
const yearPath =
|
|
17418
|
+
for (const year of fs36.readdirSync(sessionsBase)) {
|
|
17419
|
+
const yearPath = path37.join(sessionsBase, year);
|
|
17255
17420
|
try {
|
|
17256
|
-
if (!
|
|
17421
|
+
if (!fs36.statSync(yearPath).isDirectory()) continue;
|
|
17257
17422
|
} catch {
|
|
17258
17423
|
continue;
|
|
17259
17424
|
}
|
|
17260
|
-
for (const month of
|
|
17261
|
-
const monthPath =
|
|
17425
|
+
for (const month of fs36.readdirSync(yearPath)) {
|
|
17426
|
+
const monthPath = path37.join(yearPath, month);
|
|
17262
17427
|
try {
|
|
17263
|
-
if (!
|
|
17428
|
+
if (!fs36.statSync(monthPath).isDirectory()) continue;
|
|
17264
17429
|
} catch {
|
|
17265
17430
|
continue;
|
|
17266
17431
|
}
|
|
17267
|
-
for (const day of
|
|
17268
|
-
const dayPath =
|
|
17432
|
+
for (const day of fs36.readdirSync(monthPath)) {
|
|
17433
|
+
const dayPath = path37.join(monthPath, day);
|
|
17269
17434
|
try {
|
|
17270
|
-
if (!
|
|
17435
|
+
if (!fs36.statSync(dayPath).isDirectory()) continue;
|
|
17271
17436
|
} catch {
|
|
17272
17437
|
continue;
|
|
17273
17438
|
}
|
|
17274
|
-
for (const file of
|
|
17275
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(
|
|
17439
|
+
for (const file of fs36.readdirSync(dayPath)) {
|
|
17440
|
+
if (file.endsWith(".jsonl")) jsonlFiles.push(path37.join(dayPath, file));
|
|
17276
17441
|
}
|
|
17277
17442
|
}
|
|
17278
17443
|
}
|
|
@@ -17325,13 +17490,13 @@ function freezeGeminiCost(acc) {
|
|
|
17325
17490
|
function processGeminiCostFile(filePath, projectKey, start, end, acc) {
|
|
17326
17491
|
const startMs = start.getTime();
|
|
17327
17492
|
try {
|
|
17328
|
-
if (
|
|
17493
|
+
if (fs36.statSync(filePath).mtimeMs < startMs) return;
|
|
17329
17494
|
} catch {
|
|
17330
17495
|
return;
|
|
17331
17496
|
}
|
|
17332
17497
|
let raw;
|
|
17333
17498
|
try {
|
|
17334
|
-
raw =
|
|
17499
|
+
raw = fs36.readFileSync(filePath, "utf-8");
|
|
17335
17500
|
} catch {
|
|
17336
17501
|
return;
|
|
17337
17502
|
}
|
|
@@ -17380,30 +17545,30 @@ function listGeminiSessionFiles(geminiTmpDir) {
|
|
|
17380
17545
|
const out = [];
|
|
17381
17546
|
let dirs;
|
|
17382
17547
|
try {
|
|
17383
|
-
if (!
|
|
17384
|
-
dirs =
|
|
17548
|
+
if (!fs36.statSync(geminiTmpDir).isDirectory()) return out;
|
|
17549
|
+
dirs = fs36.readdirSync(geminiTmpDir);
|
|
17385
17550
|
} catch {
|
|
17386
17551
|
return out;
|
|
17387
17552
|
}
|
|
17388
17553
|
for (const proj of dirs) {
|
|
17389
|
-
const chatsDir =
|
|
17554
|
+
const chatsDir = path37.join(geminiTmpDir, proj, "chats");
|
|
17390
17555
|
let files;
|
|
17391
17556
|
try {
|
|
17392
|
-
if (!
|
|
17393
|
-
files =
|
|
17557
|
+
if (!fs36.statSync(chatsDir).isDirectory()) continue;
|
|
17558
|
+
files = fs36.readdirSync(chatsDir);
|
|
17394
17559
|
} catch {
|
|
17395
17560
|
continue;
|
|
17396
17561
|
}
|
|
17397
17562
|
for (const f of files) {
|
|
17398
17563
|
if (!f.endsWith(".jsonl")) continue;
|
|
17399
|
-
out.push({ projectKey: proj, file:
|
|
17564
|
+
out.push({ projectKey: proj, file: path37.join(chatsDir, f) });
|
|
17400
17565
|
}
|
|
17401
17566
|
}
|
|
17402
17567
|
return out;
|
|
17403
17568
|
}
|
|
17404
17569
|
function loadGeminiCost(start, end, geminiTmpDir) {
|
|
17405
17570
|
const acc = emptyGeminiAccumulator();
|
|
17406
|
-
if (!
|
|
17571
|
+
if (!fs36.existsSync(geminiTmpDir)) return freezeGeminiCost(acc);
|
|
17407
17572
|
for (const { projectKey, file } of listGeminiSessionFiles(geminiTmpDir)) {
|
|
17408
17573
|
processGeminiCostFile(file, projectKey, start, end, acc);
|
|
17409
17574
|
}
|
|
@@ -17411,11 +17576,11 @@ function loadGeminiCost(start, end, geminiTmpDir) {
|
|
|
17411
17576
|
}
|
|
17412
17577
|
function aggregateReportFromAudit(period, opts = {}) {
|
|
17413
17578
|
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 =
|
|
17579
|
+
const auditLogPath = opts.auditLogPath ?? path37.join(os32.homedir(), ".node9", "audit.log");
|
|
17580
|
+
const claudeProjectsDir = opts.claudeProjectsDir ?? path37.join(os32.homedir(), ".claude", "projects");
|
|
17581
|
+
const codexSessionsDir = opts.codexSessionsDir ?? path37.join(os32.homedir(), ".codex", "sessions");
|
|
17582
|
+
const geminiTmpDir = opts.geminiTmpDir ?? path37.join(os32.homedir(), ".gemini", "tmp");
|
|
17583
|
+
const hasAuditFile = fs36.existsSync(auditLogPath);
|
|
17419
17584
|
const allEntries = opts.preloadedAuditEntries ?? parseAuditLog(auditLogPath);
|
|
17420
17585
|
const unackedDlp = allEntries.filter((e) => e.source === "response-dlp");
|
|
17421
17586
|
const { start, end } = getDateRange(period, now);
|
|
@@ -18113,12 +18278,12 @@ function registerDaemonCommand(program2) {
|
|
|
18113
18278
|
init_core();
|
|
18114
18279
|
init_daemon();
|
|
18115
18280
|
import chalk15 from "chalk";
|
|
18116
|
-
import
|
|
18117
|
-
import
|
|
18118
|
-
import
|
|
18281
|
+
import fs37 from "fs";
|
|
18282
|
+
import path38 from "path";
|
|
18283
|
+
import os33 from "os";
|
|
18119
18284
|
function readJson2(filePath) {
|
|
18120
18285
|
try {
|
|
18121
|
-
if (
|
|
18286
|
+
if (fs37.existsSync(filePath)) return JSON.parse(fs37.readFileSync(filePath, "utf-8"));
|
|
18122
18287
|
} catch {
|
|
18123
18288
|
}
|
|
18124
18289
|
return null;
|
|
@@ -18183,28 +18348,28 @@ function registerStatusCommand(program2) {
|
|
|
18183
18348
|
console.log("");
|
|
18184
18349
|
const modeLabel = settings.mode === "audit" ? chalk15.blue("audit") : settings.mode === "strict" ? chalk15.red("strict") : chalk15.white("standard");
|
|
18185
18350
|
console.log(` Mode: ${modeLabel}`);
|
|
18186
|
-
const projectConfig =
|
|
18187
|
-
const globalConfig =
|
|
18351
|
+
const projectConfig = path38.join(process.cwd(), "node9.config.json");
|
|
18352
|
+
const globalConfig = path38.join(os33.homedir(), ".node9", "config.json");
|
|
18188
18353
|
console.log(
|
|
18189
|
-
` Local: ${
|
|
18354
|
+
` Local: ${fs37.existsSync(projectConfig) ? chalk15.green("Active (node9.config.json)") : chalk15.gray("Not present")}`
|
|
18190
18355
|
);
|
|
18191
18356
|
console.log(
|
|
18192
|
-
` Global: ${
|
|
18357
|
+
` Global: ${fs37.existsSync(globalConfig) ? chalk15.green("Active (~/.node9/config.json)") : chalk15.gray("Not present")}`
|
|
18193
18358
|
);
|
|
18194
18359
|
if (mergedConfig.policy.sandboxPaths.length > 0) {
|
|
18195
18360
|
console.log(
|
|
18196
18361
|
` Sandbox: ${chalk15.green(`${mergedConfig.policy.sandboxPaths.length} safe zones active`)}`
|
|
18197
18362
|
);
|
|
18198
18363
|
}
|
|
18199
|
-
const homeDir2 =
|
|
18364
|
+
const homeDir2 = os33.homedir();
|
|
18200
18365
|
const claudeSettings = readJson2(
|
|
18201
|
-
|
|
18366
|
+
path38.join(homeDir2, ".claude", "settings.json")
|
|
18202
18367
|
);
|
|
18203
|
-
const claudeConfig = readJson2(
|
|
18368
|
+
const claudeConfig = readJson2(path38.join(homeDir2, ".claude.json"));
|
|
18204
18369
|
const geminiSettings = readJson2(
|
|
18205
|
-
|
|
18370
|
+
path38.join(homeDir2, ".gemini", "settings.json")
|
|
18206
18371
|
);
|
|
18207
|
-
const cursorConfig = readJson2(
|
|
18372
|
+
const cursorConfig = readJson2(path38.join(homeDir2, ".cursor", "mcp.json"));
|
|
18208
18373
|
const agentFound = claudeSettings || claudeConfig || geminiSettings || cursorConfig;
|
|
18209
18374
|
if (agentFound) {
|
|
18210
18375
|
console.log("");
|
|
@@ -18267,9 +18432,9 @@ init_setup();
|
|
|
18267
18432
|
init_shields();
|
|
18268
18433
|
init_service();
|
|
18269
18434
|
import chalk16 from "chalk";
|
|
18270
|
-
import
|
|
18271
|
-
import
|
|
18272
|
-
import
|
|
18435
|
+
import fs38 from "fs";
|
|
18436
|
+
import path39 from "path";
|
|
18437
|
+
import os34 from "os";
|
|
18273
18438
|
import https4 from "https";
|
|
18274
18439
|
var DEFAULT_SHIELDS = ["bash-safe", "filesystem", "project-jail"];
|
|
18275
18440
|
function fireTelemetryPing(agents) {
|
|
@@ -18345,15 +18510,15 @@ function registerInitCommand(program2) {
|
|
|
18345
18510
|
}
|
|
18346
18511
|
console.log("");
|
|
18347
18512
|
}
|
|
18348
|
-
const configPath =
|
|
18349
|
-
if (
|
|
18513
|
+
const configPath = path39.join(os34.homedir(), ".node9", "config.json");
|
|
18514
|
+
if (fs38.existsSync(configPath) && !options.force) {
|
|
18350
18515
|
try {
|
|
18351
|
-
const existing = JSON.parse(
|
|
18516
|
+
const existing = JSON.parse(fs38.readFileSync(configPath, "utf-8"));
|
|
18352
18517
|
const settings = existing.settings ?? {};
|
|
18353
18518
|
if (settings.mode !== chosenMode) {
|
|
18354
18519
|
settings.mode = chosenMode;
|
|
18355
18520
|
existing.settings = settings;
|
|
18356
|
-
|
|
18521
|
+
fs38.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
|
|
18357
18522
|
console.log(chalk16.green(`\u2705 Mode updated: ${chosenMode}`));
|
|
18358
18523
|
} else {
|
|
18359
18524
|
console.log(chalk16.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
|
|
@@ -18366,9 +18531,9 @@ function registerInitCommand(program2) {
|
|
|
18366
18531
|
...DEFAULT_CONFIG,
|
|
18367
18532
|
settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
|
|
18368
18533
|
};
|
|
18369
|
-
const dir =
|
|
18370
|
-
if (!
|
|
18371
|
-
|
|
18534
|
+
const dir = path39.dirname(configPath);
|
|
18535
|
+
if (!fs38.existsSync(dir)) fs38.mkdirSync(dir, { recursive: true });
|
|
18536
|
+
fs38.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
|
|
18372
18537
|
console.log(chalk16.green(`\u2705 Config created: ${configPath}`));
|
|
18373
18538
|
console.log(chalk16.gray(` Mode: ${chosenMode}`));
|
|
18374
18539
|
}
|
|
@@ -18466,7 +18631,7 @@ function registerInitCommand(program2) {
|
|
|
18466
18631
|
}
|
|
18467
18632
|
|
|
18468
18633
|
// src/cli/commands/undo.ts
|
|
18469
|
-
import
|
|
18634
|
+
import path40 from "path";
|
|
18470
18635
|
import chalk18 from "chalk";
|
|
18471
18636
|
|
|
18472
18637
|
// src/tui/undo-navigator.ts
|
|
@@ -18625,7 +18790,7 @@ function findMatchingCwd(startDir, history) {
|
|
|
18625
18790
|
let dir = startDir;
|
|
18626
18791
|
while (true) {
|
|
18627
18792
|
if (cwds.has(dir)) return dir;
|
|
18628
|
-
const parent =
|
|
18793
|
+
const parent = path40.dirname(dir);
|
|
18629
18794
|
if (parent === dir) return null;
|
|
18630
18795
|
dir = parent;
|
|
18631
18796
|
}
|
|
@@ -18758,90 +18923,7 @@ import chalk19 from "chalk";
|
|
|
18758
18923
|
import { spawn as spawn7 } from "child_process";
|
|
18759
18924
|
import { execa as execa2 } from "execa";
|
|
18760
18925
|
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
|
|
18926
|
+
init_mcp_pin();
|
|
18845
18927
|
init_mcp_tools();
|
|
18846
18928
|
init_daemon();
|
|
18847
18929
|
function sanitize4(value) {
|
|
@@ -18903,6 +18985,7 @@ function tokenize4(cmd) {
|
|
|
18903
18985
|
return tokens;
|
|
18904
18986
|
}
|
|
18905
18987
|
async function runMcpGateway(upstreamCommand) {
|
|
18988
|
+
const gatewayCwd = process.cwd();
|
|
18906
18989
|
const commandParts = tokenize4(upstreamCommand);
|
|
18907
18990
|
const cmd = commandParts[0];
|
|
18908
18991
|
const cmdArgs = commandParts.slice(1);
|
|
@@ -19125,7 +19208,7 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
19125
19208
|
if (parsed.result && Array.isArray(parsed.result.tools)) {
|
|
19126
19209
|
const tools = parsed.result.tools || [];
|
|
19127
19210
|
const currentHash = hashToolDefinitions(tools);
|
|
19128
|
-
const pinStatus = checkPin(serverKey, currentHash);
|
|
19211
|
+
const pinStatus = checkPin(serverKey, currentHash, gatewayCwd);
|
|
19129
19212
|
const token = getInternalToken();
|
|
19130
19213
|
if (isDaemonRunning() && process.env.NODE9_TESTING !== "1") {
|
|
19131
19214
|
const toolSummary = tools.map((t) => ({ name: t.name, description: t.description }));
|
|
@@ -20009,27 +20092,46 @@ function registerTrustCommand(program2) {
|
|
|
20009
20092
|
}
|
|
20010
20093
|
|
|
20011
20094
|
// src/cli/commands/mcp-pin.ts
|
|
20095
|
+
init_mcp_pin();
|
|
20012
20096
|
import chalk21 from "chalk";
|
|
20097
|
+
import fs40 from "fs";
|
|
20013
20098
|
function registerMcpPinCommand(program2) {
|
|
20014
20099
|
const pinCmd = program2.command("mcp").description("Manage MCP server tool definition pinning (rug pull defense)");
|
|
20015
20100
|
const pinSubCmd = pinCmd.command("pin").description("Manage pinned MCP server tool definitions");
|
|
20016
20101
|
pinSubCmd.command("list").description("Show all pinned MCP servers and their tool definition hashes").action(() => {
|
|
20017
|
-
const
|
|
20018
|
-
|
|
20019
|
-
|
|
20020
|
-
|
|
20021
|
-
|
|
20022
|
-
|
|
20023
|
-
);
|
|
20024
|
-
|
|
20102
|
+
const found = findPinsFilePath(process.cwd());
|
|
20103
|
+
const homeResult = readMcpPinsSafe();
|
|
20104
|
+
let repoEntries = {};
|
|
20105
|
+
let repoCorrupt = false;
|
|
20106
|
+
if (found.source === "repo") {
|
|
20107
|
+
try {
|
|
20108
|
+
const raw = fs40.readFileSync(found.path, "utf-8");
|
|
20109
|
+
const parsed = JSON.parse(raw);
|
|
20110
|
+
repoEntries = parsed.servers ?? {};
|
|
20111
|
+
} catch {
|
|
20112
|
+
repoCorrupt = true;
|
|
20025
20113
|
}
|
|
20114
|
+
}
|
|
20115
|
+
if (repoCorrupt) {
|
|
20026
20116
|
console.error(chalk21.red(`
|
|
20027
|
-
\u274C
|
|
20117
|
+
\u274C Repo pin file at ${found.path} is corrupt or unreadable.`));
|
|
20118
|
+
process.exit(1);
|
|
20119
|
+
}
|
|
20120
|
+
if (!homeResult.ok && homeResult.reason === "corrupt") {
|
|
20121
|
+
console.error(chalk21.red(`
|
|
20122
|
+
\u274C Home pin file is corrupt: ${homeResult.detail}`));
|
|
20028
20123
|
console.error(chalk21.yellow(" Run: node9 mcp pin reset\n"));
|
|
20029
20124
|
process.exit(1);
|
|
20030
20125
|
}
|
|
20031
|
-
const
|
|
20032
|
-
|
|
20126
|
+
const homeEntries = homeResult.ok ? homeResult.pins.servers : {};
|
|
20127
|
+
const merged = /* @__PURE__ */ new Map();
|
|
20128
|
+
for (const [key, entry] of Object.entries(homeEntries)) {
|
|
20129
|
+
merged.set(key, { entry, source: "home" });
|
|
20130
|
+
}
|
|
20131
|
+
for (const [key, entry] of Object.entries(repoEntries)) {
|
|
20132
|
+
merged.set(key, { entry, source: "repo" });
|
|
20133
|
+
}
|
|
20134
|
+
if (merged.size === 0) {
|
|
20033
20135
|
console.log(chalk21.gray("\nNo MCP servers are pinned yet."));
|
|
20034
20136
|
console.log(
|
|
20035
20137
|
chalk21.gray("Pins are created automatically when the MCP gateway first connects.\n")
|
|
@@ -20037,13 +20139,47 @@ function registerMcpPinCommand(program2) {
|
|
|
20037
20139
|
return;
|
|
20038
20140
|
}
|
|
20039
20141
|
console.log(chalk21.bold("\n\u{1F512} Pinned MCP Servers\n"));
|
|
20040
|
-
|
|
20041
|
-
|
|
20142
|
+
const showSource = found.source === "repo";
|
|
20143
|
+
for (const [key, { entry, source }] of merged) {
|
|
20144
|
+
const tag = showSource ? ` ${chalk21.yellow(`[${source}]`)}` : "";
|
|
20145
|
+
console.log(` ${chalk21.cyan(key)}${tag} ${chalk21.gray(entry.label)}`);
|
|
20042
20146
|
console.log(` Tools (${entry.toolCount}): ${chalk21.white(entry.toolNames.join(", "))}`);
|
|
20043
20147
|
console.log(` Hash: ${chalk21.gray(entry.toolsHash.slice(0, 16))}...`);
|
|
20044
20148
|
console.log(` Pinned: ${chalk21.gray(entry.pinnedAt)}`);
|
|
20045
20149
|
console.log("");
|
|
20046
20150
|
}
|
|
20151
|
+
if (showSource) {
|
|
20152
|
+
console.log(chalk21.gray(` [repo] entries come from ${found.path}`));
|
|
20153
|
+
console.log(chalk21.gray(" [home] entries come from ~/.node9/mcp-pins.json\n"));
|
|
20154
|
+
}
|
|
20155
|
+
});
|
|
20156
|
+
pinSubCmd.command("promote <serverKey>").description(
|
|
20157
|
+
"Copy a pin from ~/.node9/mcp-pins.json into <repo>/.node9/mcp-pins.json so teammates share the same vetted baseline"
|
|
20158
|
+
).action((serverKey) => {
|
|
20159
|
+
try {
|
|
20160
|
+
const { repoPath, created } = promotePin(serverKey, process.cwd());
|
|
20161
|
+
if (created) {
|
|
20162
|
+
console.log(
|
|
20163
|
+
chalk21.green(
|
|
20164
|
+
`
|
|
20165
|
+
\u2705 Created ${repoPath} with the promoted pin for ${chalk21.cyan(serverKey)}.`
|
|
20166
|
+
)
|
|
20167
|
+
);
|
|
20168
|
+
} else {
|
|
20169
|
+
console.log(chalk21.green(`
|
|
20170
|
+
\u2705 Promoted ${chalk21.cyan(serverKey)} into ${repoPath}.`));
|
|
20171
|
+
}
|
|
20172
|
+
console.log(chalk21.gray(" Review the change and commit it:"));
|
|
20173
|
+
console.log(chalk21.cyan(` git add ${repoPath}`));
|
|
20174
|
+
console.log(chalk21.cyan(` git commit -m "pin ${serverKey} (node9)"`));
|
|
20175
|
+
console.log("");
|
|
20176
|
+
} catch (err2) {
|
|
20177
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
20178
|
+
console.error(chalk21.red(`
|
|
20179
|
+
\u274C ${msg}
|
|
20180
|
+
`));
|
|
20181
|
+
process.exit(1);
|
|
20182
|
+
}
|
|
20047
20183
|
});
|
|
20048
20184
|
pinSubCmd.command("update <serverKey>").description(
|
|
20049
20185
|
"Remove a pin so the next gateway connection re-pins with current tool definitions"
|
|
@@ -20065,7 +20201,7 @@ function registerMcpPinCommand(program2) {
|
|
|
20065
20201
|
process.exit(1);
|
|
20066
20202
|
}
|
|
20067
20203
|
const label = pins.servers[serverKey].label;
|
|
20068
|
-
|
|
20204
|
+
removePin(serverKey);
|
|
20069
20205
|
console.log(chalk21.green(`
|
|
20070
20206
|
\u{1F513} Pin removed for ${chalk21.cyan(serverKey)}`));
|
|
20071
20207
|
console.log(chalk21.gray(` Server: ${label}`));
|
|
@@ -20078,7 +20214,7 @@ function registerMcpPinCommand(program2) {
|
|
|
20078
20214
|
return;
|
|
20079
20215
|
}
|
|
20080
20216
|
const count = result.ok ? Object.keys(result.pins.servers).length : "?";
|
|
20081
|
-
|
|
20217
|
+
clearAllPins();
|
|
20082
20218
|
console.log(chalk21.green(`
|
|
20083
20219
|
\u{1F513} Cleared ${count} MCP pin(s).`));
|
|
20084
20220
|
console.log(chalk21.gray(" Next connection to each server will re-pin.\n"));
|
|
@@ -20261,7 +20397,7 @@ init_scan();
|
|
|
20261
20397
|
|
|
20262
20398
|
// src/cli/commands/sessions.ts
|
|
20263
20399
|
import chalk24 from "chalk";
|
|
20264
|
-
import
|
|
20400
|
+
import fs41 from "fs";
|
|
20265
20401
|
import path42 from "path";
|
|
20266
20402
|
import os36 from "os";
|
|
20267
20403
|
var CLAUDE_PRICING3 = {
|
|
@@ -20379,7 +20515,7 @@ function loadAuditEntries(auditPath) {
|
|
|
20379
20515
|
const aPath = auditPath ?? path42.join(os36.homedir(), ".node9", "audit.log");
|
|
20380
20516
|
let raw;
|
|
20381
20517
|
try {
|
|
20382
|
-
raw =
|
|
20518
|
+
raw = fs41.readFileSync(aPath, "utf-8");
|
|
20383
20519
|
} catch {
|
|
20384
20520
|
return [];
|
|
20385
20521
|
}
|
|
@@ -20416,7 +20552,7 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
|
|
|
20416
20552
|
}
|
|
20417
20553
|
function buildGeminiSessions(days, allAuditEntries) {
|
|
20418
20554
|
const tmpDir = path42.join(os36.homedir(), ".gemini", "tmp");
|
|
20419
|
-
if (!
|
|
20555
|
+
if (!fs41.existsSync(tmpDir)) return [];
|
|
20420
20556
|
const cutoff = days !== null ? (() => {
|
|
20421
20557
|
const d = /* @__PURE__ */ new Date();
|
|
20422
20558
|
d.setDate(d.getDate() - days);
|
|
@@ -20425,7 +20561,7 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
20425
20561
|
})() : null;
|
|
20426
20562
|
let slugDirs;
|
|
20427
20563
|
try {
|
|
20428
|
-
slugDirs =
|
|
20564
|
+
slugDirs = fs41.readdirSync(tmpDir);
|
|
20429
20565
|
} catch {
|
|
20430
20566
|
return [];
|
|
20431
20567
|
}
|
|
@@ -20433,27 +20569,27 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
20433
20569
|
for (const slug of slugDirs) {
|
|
20434
20570
|
const slugPath = path42.join(tmpDir, slug);
|
|
20435
20571
|
try {
|
|
20436
|
-
if (!
|
|
20572
|
+
if (!fs41.statSync(slugPath).isDirectory()) continue;
|
|
20437
20573
|
} catch {
|
|
20438
20574
|
continue;
|
|
20439
20575
|
}
|
|
20440
20576
|
let projectRoot = path42.join(os36.homedir(), slug);
|
|
20441
20577
|
try {
|
|
20442
|
-
projectRoot =
|
|
20578
|
+
projectRoot = fs41.readFileSync(path42.join(slugPath, ".project_root"), "utf-8").trim();
|
|
20443
20579
|
} catch {
|
|
20444
20580
|
}
|
|
20445
20581
|
const chatsDir = path42.join(slugPath, "chats");
|
|
20446
|
-
if (!
|
|
20582
|
+
if (!fs41.existsSync(chatsDir)) continue;
|
|
20447
20583
|
let chatFiles;
|
|
20448
20584
|
try {
|
|
20449
|
-
chatFiles =
|
|
20585
|
+
chatFiles = fs41.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
|
|
20450
20586
|
} catch {
|
|
20451
20587
|
continue;
|
|
20452
20588
|
}
|
|
20453
20589
|
for (const chatFile of chatFiles) {
|
|
20454
20590
|
let raw;
|
|
20455
20591
|
try {
|
|
20456
|
-
raw =
|
|
20592
|
+
raw = fs41.readFileSync(path42.join(chatsDir, chatFile), "utf-8");
|
|
20457
20593
|
} catch {
|
|
20458
20594
|
continue;
|
|
20459
20595
|
}
|
|
@@ -20534,7 +20670,7 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
20534
20670
|
}
|
|
20535
20671
|
function buildCodexSessions(days, allAuditEntries) {
|
|
20536
20672
|
const sessionsBase = path42.join(os36.homedir(), ".codex", "sessions");
|
|
20537
|
-
if (!
|
|
20673
|
+
if (!fs41.existsSync(sessionsBase)) return [];
|
|
20538
20674
|
const cutoff = days !== null ? (() => {
|
|
20539
20675
|
const d = /* @__PURE__ */ new Date();
|
|
20540
20676
|
d.setDate(d.getDate() - days);
|
|
@@ -20543,28 +20679,28 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
20543
20679
|
})() : null;
|
|
20544
20680
|
const jsonlFiles = [];
|
|
20545
20681
|
try {
|
|
20546
|
-
for (const year of
|
|
20682
|
+
for (const year of fs41.readdirSync(sessionsBase)) {
|
|
20547
20683
|
const yearPath = path42.join(sessionsBase, year);
|
|
20548
20684
|
try {
|
|
20549
|
-
if (!
|
|
20685
|
+
if (!fs41.statSync(yearPath).isDirectory()) continue;
|
|
20550
20686
|
} catch {
|
|
20551
20687
|
continue;
|
|
20552
20688
|
}
|
|
20553
|
-
for (const month of
|
|
20689
|
+
for (const month of fs41.readdirSync(yearPath)) {
|
|
20554
20690
|
const monthPath = path42.join(yearPath, month);
|
|
20555
20691
|
try {
|
|
20556
|
-
if (!
|
|
20692
|
+
if (!fs41.statSync(monthPath).isDirectory()) continue;
|
|
20557
20693
|
} catch {
|
|
20558
20694
|
continue;
|
|
20559
20695
|
}
|
|
20560
|
-
for (const day of
|
|
20696
|
+
for (const day of fs41.readdirSync(monthPath)) {
|
|
20561
20697
|
const dayPath = path42.join(monthPath, day);
|
|
20562
20698
|
try {
|
|
20563
|
-
if (!
|
|
20699
|
+
if (!fs41.statSync(dayPath).isDirectory()) continue;
|
|
20564
20700
|
} catch {
|
|
20565
20701
|
continue;
|
|
20566
20702
|
}
|
|
20567
|
-
for (const file of
|
|
20703
|
+
for (const file of fs41.readdirSync(dayPath)) {
|
|
20568
20704
|
if (file.endsWith(".jsonl")) jsonlFiles.push(path42.join(dayPath, file));
|
|
20569
20705
|
}
|
|
20570
20706
|
}
|
|
@@ -20577,7 +20713,7 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
20577
20713
|
for (const filePath of jsonlFiles) {
|
|
20578
20714
|
let lines;
|
|
20579
20715
|
try {
|
|
20580
|
-
lines =
|
|
20716
|
+
lines = fs41.readFileSync(filePath, "utf-8").split("\n");
|
|
20581
20717
|
} catch {
|
|
20582
20718
|
continue;
|
|
20583
20719
|
}
|
|
@@ -20658,7 +20794,7 @@ function buildSessions(days, historyPath) {
|
|
|
20658
20794
|
const hPath = historyPath ?? path42.join(os36.homedir(), ".claude", "history.jsonl");
|
|
20659
20795
|
let historyRaw;
|
|
20660
20796
|
try {
|
|
20661
|
-
historyRaw =
|
|
20797
|
+
historyRaw = fs41.readFileSync(hPath, "utf-8");
|
|
20662
20798
|
} catch {
|
|
20663
20799
|
return [];
|
|
20664
20800
|
}
|
|
@@ -20683,7 +20819,7 @@ function buildSessions(days, historyPath) {
|
|
|
20683
20819
|
const jsonlFile = sessionJsonlPath(entry.project, entry.sessionId);
|
|
20684
20820
|
let sessionLines = [];
|
|
20685
20821
|
try {
|
|
20686
|
-
sessionLines =
|
|
20822
|
+
sessionLines = fs41.readFileSync(jsonlFile, "utf-8").split("\n");
|
|
20687
20823
|
} catch {
|
|
20688
20824
|
}
|
|
20689
20825
|
const { toolCalls, costUSD, hasSnapshot, modifiedFiles } = parseSessionLines(sessionLines);
|
|
@@ -20950,7 +21086,7 @@ function registerSessionsCommand(program2) {
|
|
|
20950
21086
|
console.log(chalk24.cyan.bold("\u{1F4CB} node9 sessions") + chalk24.dim(" \u2014 what your AI agent did"));
|
|
20951
21087
|
console.log("");
|
|
20952
21088
|
const historyPath = path42.join(os36.homedir(), ".claude", "history.jsonl");
|
|
20953
|
-
if (!
|
|
21089
|
+
if (!fs41.existsSync(historyPath)) {
|
|
20954
21090
|
console.log(chalk24.yellow(" No Claude session history found at ~/.claude/history.jsonl"));
|
|
20955
21091
|
console.log(chalk24.gray(" Install Claude Code, run a few sessions, then try again.\n"));
|
|
20956
21092
|
return;
|
|
@@ -20987,12 +21123,12 @@ function registerSessionsCommand(program2) {
|
|
|
20987
21123
|
|
|
20988
21124
|
// src/cli/commands/skill-pin.ts
|
|
20989
21125
|
import chalk25 from "chalk";
|
|
20990
|
-
import
|
|
21126
|
+
import fs42 from "fs";
|
|
20991
21127
|
import os37 from "os";
|
|
20992
21128
|
import path43 from "path";
|
|
20993
21129
|
function wipeSkillSessions() {
|
|
20994
21130
|
try {
|
|
20995
|
-
|
|
21131
|
+
fs42.rmSync(path43.join(os37.homedir(), ".node9", "skill-sessions"), {
|
|
20996
21132
|
recursive: true,
|
|
20997
21133
|
force: true
|
|
20998
21134
|
});
|
|
@@ -21050,7 +21186,7 @@ function registerSkillPinCommand(program2) {
|
|
|
21050
21186
|
process.exit(1);
|
|
21051
21187
|
}
|
|
21052
21188
|
const rootPath = pins.roots[rootKey].rootPath;
|
|
21053
|
-
|
|
21189
|
+
removePin2(rootKey);
|
|
21054
21190
|
wipeSkillSessions();
|
|
21055
21191
|
console.log(chalk25.green(`
|
|
21056
21192
|
\u{1F513} Pin removed for ${chalk25.cyan(rootKey)}`));
|
|
@@ -21065,7 +21201,7 @@ function registerSkillPinCommand(program2) {
|
|
|
21065
21201
|
return;
|
|
21066
21202
|
}
|
|
21067
21203
|
const count = result.ok ? Object.keys(result.pins.roots).length : "?";
|
|
21068
|
-
|
|
21204
|
+
clearAllPins2();
|
|
21069
21205
|
wipeSkillSessions();
|
|
21070
21206
|
console.log(chalk25.green(`
|
|
21071
21207
|
\u{1F513} Cleared ${count} skill pin(s).`));
|
|
@@ -21074,15 +21210,15 @@ function registerSkillPinCommand(program2) {
|
|
|
21074
21210
|
}
|
|
21075
21211
|
|
|
21076
21212
|
// src/cli/commands/decisions.ts
|
|
21077
|
-
import
|
|
21213
|
+
import fs43 from "fs";
|
|
21078
21214
|
import os38 from "os";
|
|
21079
21215
|
import path44 from "path";
|
|
21080
21216
|
import chalk26 from "chalk";
|
|
21081
21217
|
var DECISIONS_FILE2 = path44.join(os38.homedir(), ".node9", "decisions.json");
|
|
21082
21218
|
function readDecisions() {
|
|
21083
21219
|
try {
|
|
21084
|
-
if (!
|
|
21085
|
-
const raw =
|
|
21220
|
+
if (!fs43.existsSync(DECISIONS_FILE2)) return {};
|
|
21221
|
+
const raw = fs43.readFileSync(DECISIONS_FILE2, "utf-8");
|
|
21086
21222
|
const parsed = JSON.parse(raw);
|
|
21087
21223
|
const out = {};
|
|
21088
21224
|
for (const [k, v] of Object.entries(parsed)) {
|
|
@@ -21095,10 +21231,10 @@ function readDecisions() {
|
|
|
21095
21231
|
}
|
|
21096
21232
|
function writeDecisions(d) {
|
|
21097
21233
|
const dir = path44.dirname(DECISIONS_FILE2);
|
|
21098
|
-
if (!
|
|
21234
|
+
if (!fs43.existsSync(dir)) fs43.mkdirSync(dir, { recursive: true });
|
|
21099
21235
|
const tmp = `${DECISIONS_FILE2}.${process.pid}.tmp`;
|
|
21100
|
-
|
|
21101
|
-
|
|
21236
|
+
fs43.writeFileSync(tmp, JSON.stringify(d, null, 2));
|
|
21237
|
+
fs43.renameSync(tmp, DECISIONS_FILE2);
|
|
21102
21238
|
}
|
|
21103
21239
|
function registerDecisionsCommand(program2) {
|
|
21104
21240
|
const cmd = program2.command("decisions").description('Manage persistent "Always Allow" / "Always Deny" tool decisions');
|
|
@@ -21155,7 +21291,7 @@ Persistent decisions (${entries.length})
|
|
|
21155
21291
|
|
|
21156
21292
|
// src/cli/commands/dlp.ts
|
|
21157
21293
|
import chalk27 from "chalk";
|
|
21158
|
-
import
|
|
21294
|
+
import fs44 from "fs";
|
|
21159
21295
|
import path45 from "path";
|
|
21160
21296
|
import os39 from "os";
|
|
21161
21297
|
var AUDIT_LOG = path45.join(os39.homedir(), ".node9", "audit.log");
|
|
@@ -21166,7 +21302,7 @@ function stripAnsi(s) {
|
|
|
21166
21302
|
}
|
|
21167
21303
|
function loadResolved() {
|
|
21168
21304
|
try {
|
|
21169
|
-
const raw = JSON.parse(
|
|
21305
|
+
const raw = JSON.parse(fs44.readFileSync(RESOLVED_FILE, "utf-8"));
|
|
21170
21306
|
return new Set(raw);
|
|
21171
21307
|
} catch {
|
|
21172
21308
|
return /* @__PURE__ */ new Set();
|
|
@@ -21174,13 +21310,13 @@ function loadResolved() {
|
|
|
21174
21310
|
}
|
|
21175
21311
|
function saveResolved(resolved) {
|
|
21176
21312
|
try {
|
|
21177
|
-
|
|
21313
|
+
fs44.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
|
|
21178
21314
|
} catch {
|
|
21179
21315
|
}
|
|
21180
21316
|
}
|
|
21181
21317
|
function loadDlpFindings() {
|
|
21182
|
-
if (!
|
|
21183
|
-
return
|
|
21318
|
+
if (!fs44.existsSync(AUDIT_LOG)) return [];
|
|
21319
|
+
return fs44.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
|
|
21184
21320
|
if (!line.trim()) return [];
|
|
21185
21321
|
try {
|
|
21186
21322
|
const e = JSON.parse(line);
|
|
@@ -21279,13 +21415,13 @@ function registerDlpCommand(program2) {
|
|
|
21279
21415
|
// src/cli/commands/mask.ts
|
|
21280
21416
|
init_dlp();
|
|
21281
21417
|
import chalk28 from "chalk";
|
|
21282
|
-
import
|
|
21418
|
+
import fs45 from "fs";
|
|
21283
21419
|
import path46 from "path";
|
|
21284
21420
|
import os40 from "os";
|
|
21285
21421
|
function findJsonlFiles(dir) {
|
|
21286
21422
|
const results = [];
|
|
21287
|
-
if (!
|
|
21288
|
-
for (const entry of
|
|
21423
|
+
if (!fs45.existsSync(dir)) return results;
|
|
21424
|
+
for (const entry of fs45.readdirSync(dir, { withFileTypes: true })) {
|
|
21289
21425
|
const full = path46.join(dir, entry.name);
|
|
21290
21426
|
if (entry.isDirectory()) results.push(...findJsonlFiles(full));
|
|
21291
21427
|
else if (entry.isFile() && entry.name.endsWith(".jsonl")) results.push(full);
|
|
@@ -21329,7 +21465,7 @@ function redactJson(obj) {
|
|
|
21329
21465
|
function processFile(filePath, dryRun) {
|
|
21330
21466
|
let raw;
|
|
21331
21467
|
try {
|
|
21332
|
-
raw =
|
|
21468
|
+
raw = fs45.readFileSync(filePath, "utf-8");
|
|
21333
21469
|
} catch {
|
|
21334
21470
|
return { redactedLines: 0, patterns: [] };
|
|
21335
21471
|
}
|
|
@@ -21361,14 +21497,14 @@ function processFile(filePath, dryRun) {
|
|
|
21361
21497
|
}
|
|
21362
21498
|
}
|
|
21363
21499
|
if (!dryRun && redactedLines > 0) {
|
|
21364
|
-
|
|
21500
|
+
fs45.writeFileSync(filePath, newLines.join("\n"), "utf-8");
|
|
21365
21501
|
}
|
|
21366
21502
|
return { redactedLines, patterns };
|
|
21367
21503
|
}
|
|
21368
21504
|
function processJsonFile(filePath, dryRun) {
|
|
21369
21505
|
let raw;
|
|
21370
21506
|
try {
|
|
21371
|
-
raw =
|
|
21507
|
+
raw = fs45.readFileSync(filePath, "utf-8");
|
|
21372
21508
|
} catch {
|
|
21373
21509
|
return { redactedLines: 0, patterns: [] };
|
|
21374
21510
|
}
|
|
@@ -21381,14 +21517,14 @@ function processJsonFile(filePath, dryRun) {
|
|
|
21381
21517
|
const { value, modified, found } = redactJson(parsed);
|
|
21382
21518
|
if (!modified) return { redactedLines: 0, patterns: [] };
|
|
21383
21519
|
if (!dryRun) {
|
|
21384
|
-
|
|
21520
|
+
fs45.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
|
|
21385
21521
|
}
|
|
21386
21522
|
return { redactedLines: 1, patterns: found };
|
|
21387
21523
|
}
|
|
21388
21524
|
function findJsonFiles(dir) {
|
|
21389
21525
|
const results = [];
|
|
21390
|
-
if (!
|
|
21391
|
-
for (const entry of
|
|
21526
|
+
if (!fs45.existsSync(dir)) return results;
|
|
21527
|
+
for (const entry of fs45.readdirSync(dir, { withFileTypes: true })) {
|
|
21392
21528
|
const full = path46.join(dir, entry.name);
|
|
21393
21529
|
if (entry.isDirectory()) results.push(...findJsonFiles(full));
|
|
21394
21530
|
else if (entry.isFile() && entry.name.endsWith(".json")) results.push(full);
|
|
@@ -21408,7 +21544,7 @@ function registerMaskCommand(program2) {
|
|
|
21408
21544
|
const cutoff = options.all ? null : new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
|
|
21409
21545
|
const filtered = cutoff ? allFiles.filter((f) => {
|
|
21410
21546
|
try {
|
|
21411
|
-
return
|
|
21547
|
+
return fs45.statSync(f.path).mtime >= cutoff;
|
|
21412
21548
|
} catch {
|
|
21413
21549
|
return false;
|
|
21414
21550
|
}
|
|
@@ -21464,20 +21600,20 @@ function registerMaskCommand(program2) {
|
|
|
21464
21600
|
// src/cli.ts
|
|
21465
21601
|
init_blast();
|
|
21466
21602
|
var { version } = JSON.parse(
|
|
21467
|
-
|
|
21603
|
+
fs48.readFileSync(path49.join(__dirname, "../package.json"), "utf-8")
|
|
21468
21604
|
);
|
|
21469
21605
|
var program = new Command();
|
|
21470
21606
|
program.name("node9").description("The Sudo Command for AI Agents").version(version);
|
|
21471
21607
|
program.command("login").argument("<apiKey>").option("--local", "Save key for audit/logging only \u2014 local config still controls all decisions").option("--profile <name>", 'Save as a named profile (default: "default")').action((apiKey, options) => {
|
|
21472
21608
|
const DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept";
|
|
21473
21609
|
const credPath = path49.join(os43.homedir(), ".node9", "credentials.json");
|
|
21474
|
-
if (!
|
|
21475
|
-
|
|
21610
|
+
if (!fs48.existsSync(path49.dirname(credPath)))
|
|
21611
|
+
fs48.mkdirSync(path49.dirname(credPath), { recursive: true });
|
|
21476
21612
|
const profileName = options.profile || "default";
|
|
21477
21613
|
let existingCreds = {};
|
|
21478
21614
|
try {
|
|
21479
|
-
if (
|
|
21480
|
-
const raw = JSON.parse(
|
|
21615
|
+
if (fs48.existsSync(credPath)) {
|
|
21616
|
+
const raw = JSON.parse(fs48.readFileSync(credPath, "utf-8"));
|
|
21481
21617
|
if (raw.apiKey) {
|
|
21482
21618
|
existingCreds = {
|
|
21483
21619
|
default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL2 }
|
|
@@ -21489,13 +21625,13 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
21489
21625
|
} catch {
|
|
21490
21626
|
}
|
|
21491
21627
|
existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL2 };
|
|
21492
|
-
|
|
21628
|
+
fs48.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
|
|
21493
21629
|
if (profileName === "default") {
|
|
21494
21630
|
const configPath = path49.join(os43.homedir(), ".node9", "config.json");
|
|
21495
21631
|
let config = {};
|
|
21496
21632
|
try {
|
|
21497
|
-
if (
|
|
21498
|
-
config = JSON.parse(
|
|
21633
|
+
if (fs48.existsSync(configPath))
|
|
21634
|
+
config = JSON.parse(fs48.readFileSync(configPath, "utf-8"));
|
|
21499
21635
|
} catch {
|
|
21500
21636
|
}
|
|
21501
21637
|
if (!config.settings || typeof config.settings !== "object") config.settings = {};
|
|
@@ -21510,9 +21646,9 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
21510
21646
|
approvers.cloud = false;
|
|
21511
21647
|
}
|
|
21512
21648
|
s.approvers = approvers;
|
|
21513
|
-
if (!
|
|
21514
|
-
|
|
21515
|
-
|
|
21649
|
+
if (!fs48.existsSync(path49.dirname(configPath)))
|
|
21650
|
+
fs48.mkdirSync(path49.dirname(configPath), { recursive: true });
|
|
21651
|
+
fs48.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
21516
21652
|
}
|
|
21517
21653
|
if (options.profile && profileName !== "default") {
|
|
21518
21654
|
console.log(chalk30.green(`\u2705 Profile "${profileName}" saved`));
|
|
@@ -21650,14 +21786,14 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
21650
21786
|
}
|
|
21651
21787
|
if (options.purge) {
|
|
21652
21788
|
const node9Dir = path49.join(os43.homedir(), ".node9");
|
|
21653
|
-
if (
|
|
21789
|
+
if (fs48.existsSync(node9Dir)) {
|
|
21654
21790
|
const confirmed = await confirm2({
|
|
21655
21791
|
message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
|
|
21656
21792
|
default: false
|
|
21657
21793
|
});
|
|
21658
21794
|
if (confirmed) {
|
|
21659
|
-
|
|
21660
|
-
if (
|
|
21795
|
+
fs48.rmSync(node9Dir, { recursive: true });
|
|
21796
|
+
if (fs48.existsSync(node9Dir)) {
|
|
21661
21797
|
console.error(
|
|
21662
21798
|
chalk30.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
|
|
21663
21799
|
);
|
|
@@ -21812,12 +21948,12 @@ Run "node9 addto claude" to register it as the statusLine.`
|
|
|
21812
21948
|
if (subcommand === "debug") {
|
|
21813
21949
|
const flagFile = path49.join(os43.homedir(), ".node9", "hud-debug");
|
|
21814
21950
|
if (state === "on") {
|
|
21815
|
-
|
|
21816
|
-
|
|
21951
|
+
fs48.mkdirSync(path49.dirname(flagFile), { recursive: true });
|
|
21952
|
+
fs48.writeFileSync(flagFile, "");
|
|
21817
21953
|
console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
|
|
21818
21954
|
console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
|
|
21819
21955
|
} else if (state === "off") {
|
|
21820
|
-
if (
|
|
21956
|
+
if (fs48.existsSync(flagFile)) fs48.unlinkSync(flagFile);
|
|
21821
21957
|
console.log("HUD debug logging disabled.");
|
|
21822
21958
|
} else {
|
|
21823
21959
|
console.error("Usage: node9 hud debug on|off");
|
|
@@ -21933,7 +22069,7 @@ if (process.argv[2] !== "daemon") {
|
|
|
21933
22069
|
if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
|
|
21934
22070
|
const logPath = path49.join(os43.homedir(), ".node9", "hook-debug.log");
|
|
21935
22071
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
21936
|
-
|
|
22072
|
+
fs48.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
|
|
21937
22073
|
`);
|
|
21938
22074
|
}
|
|
21939
22075
|
process.exit(0);
|