@node9/proxy 1.12.0 → 1.12.1
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 +435 -391
- package/dist/cli.mjs +421 -377
- package/dist/index.js +9 -1
- package/dist/index.mjs +9 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -2327,7 +2327,12 @@ async function evaluatePolicy(toolName, args, agent, cwd) {
|
|
|
2327
2327
|
pathTokens = analyzed.paths;
|
|
2328
2328
|
const INLINE_EXEC_PATTERN = /^(python3?|bash|sh|zsh|perl|ruby|node|php|lua)\s+(-c|-e|-eval)\s/i;
|
|
2329
2329
|
if (INLINE_EXEC_PATTERN.test(shellCommand.trim())) {
|
|
2330
|
-
return {
|
|
2330
|
+
return {
|
|
2331
|
+
decision: "review",
|
|
2332
|
+
blockedByLabel: "Node9 Standard (Inline Execution)",
|
|
2333
|
+
ruleDescription: "The AI is running code directly from the command line. Review the full script below before allowing it to execute.",
|
|
2334
|
+
tier: 3
|
|
2335
|
+
};
|
|
2331
2336
|
}
|
|
2332
2337
|
const evalVerdict = detectDangerousShellExec(shellCommand);
|
|
2333
2338
|
if (evalVerdict === "block") {
|
|
@@ -2335,6 +2340,7 @@ async function evaluatePolicy(toolName, args, agent, cwd) {
|
|
|
2335
2340
|
decision: "block",
|
|
2336
2341
|
blockedByLabel: "Node9: Eval Remote Execution",
|
|
2337
2342
|
reason: "eval of remote download (curl/wget) is a near-certain supply-chain attack",
|
|
2343
|
+
ruleDescription: "The AI is downloading a script from the internet and running it immediately without inspection. This is a common way malware gets installed.",
|
|
2338
2344
|
tier: 3
|
|
2339
2345
|
};
|
|
2340
2346
|
}
|
|
@@ -2343,6 +2349,7 @@ async function evaluatePolicy(toolName, args, agent, cwd) {
|
|
|
2343
2349
|
decision: "review",
|
|
2344
2350
|
blockedByLabel: "Node9: Eval Dynamic Content",
|
|
2345
2351
|
reason: "eval of dynamic content (variable or subshell expansion) requires approval",
|
|
2352
|
+
ruleDescription: "The AI is running a command that includes a variable or subshell expansion. The actual command executed at runtime may differ from what is shown here.",
|
|
2346
2353
|
tier: 3
|
|
2347
2354
|
};
|
|
2348
2355
|
}
|
|
@@ -2472,6 +2479,7 @@ async function evaluatePolicy(toolName, args, agent, cwd) {
|
|
|
2472
2479
|
blockedByLabel: `Project/Global Config \u2014 dangerous word: "${matchedDangerousWord}"`,
|
|
2473
2480
|
matchedWord: matchedDangerousWord,
|
|
2474
2481
|
matchedField,
|
|
2482
|
+
ruleDescription: `This command contains a flagged keyword ("${matchedDangerousWord}") from your node9 config. Review it before allowing.`,
|
|
2475
2483
|
tier: 6
|
|
2476
2484
|
};
|
|
2477
2485
|
}
|
|
@@ -2624,7 +2632,8 @@ async function explainPolicy(toolName, args) {
|
|
|
2624
2632
|
waterfall,
|
|
2625
2633
|
steps,
|
|
2626
2634
|
decision: "review",
|
|
2627
|
-
blockedByLabel: "Node9 Standard (Inline Execution)"
|
|
2635
|
+
blockedByLabel: "Node9 Standard (Inline Execution)",
|
|
2636
|
+
ruleDescription: "The AI is running code directly from the command line. Review the full script below before allowing it to execute."
|
|
2628
2637
|
};
|
|
2629
2638
|
}
|
|
2630
2639
|
steps.push({
|
|
@@ -2730,7 +2739,8 @@ async function explainPolicy(toolName, args) {
|
|
|
2730
2739
|
steps,
|
|
2731
2740
|
decision: "review",
|
|
2732
2741
|
blockedByLabel: `Project/Global Config \u2014 dangerous word: "${matchedDangerousWord}"`,
|
|
2733
|
-
matchedToken: matchedDangerousWord
|
|
2742
|
+
matchedToken: matchedDangerousWord,
|
|
2743
|
+
ruleDescription: `This command contains a flagged keyword ("${matchedDangerousWord}") from your node9 config. Review it before allowing.`
|
|
2734
2744
|
};
|
|
2735
2745
|
}
|
|
2736
2746
|
steps.push({
|
|
@@ -8269,6 +8279,7 @@ var init_ui2 = __esm({
|
|
|
8269
8279
|
// src/cli/daemon-starter.ts
|
|
8270
8280
|
import { spawn as spawn2, execSync } from "child_process";
|
|
8271
8281
|
import path16 from "path";
|
|
8282
|
+
import fs13 from "fs";
|
|
8272
8283
|
function isTestingMode() {
|
|
8273
8284
|
return /^(1|true|yes)$/i.test(process.env.NODE9_TESTING ?? "");
|
|
8274
8285
|
}
|
|
@@ -8285,20 +8296,32 @@ function openBrowserLocal() {
|
|
|
8285
8296
|
async function autoStartDaemonAndWait(openBrowser2 = true) {
|
|
8286
8297
|
if (isTestingMode()) return false;
|
|
8287
8298
|
if (!path16.isAbsolute(process.argv[1])) return false;
|
|
8299
|
+
let resolvedArgv1;
|
|
8288
8300
|
try {
|
|
8289
|
-
|
|
8301
|
+
resolvedArgv1 = fs13.realpathSync(process.argv[1]);
|
|
8302
|
+
} catch {
|
|
8303
|
+
return false;
|
|
8304
|
+
}
|
|
8305
|
+
if (!resolvedArgv1.endsWith(".js")) return false;
|
|
8306
|
+
try {
|
|
8307
|
+
const child = spawn2(process.execPath, [resolvedArgv1, "daemon"], {
|
|
8290
8308
|
detached: true,
|
|
8291
8309
|
stdio: "ignore",
|
|
8292
8310
|
// NODE9_BROWSER_OPENED=1 tells the daemon we will open the browser ourselves
|
|
8293
8311
|
// (openBrowserLocal below), so it must not open a duplicate tab on first approval.
|
|
8294
|
-
|
|
8312
|
+
// Only set NODE9_BROWSER_OPENED when we actually intend to open it here.
|
|
8313
|
+
env: {
|
|
8314
|
+
...process.env,
|
|
8315
|
+
NODE9_AUTO_STARTED: "1",
|
|
8316
|
+
...openBrowser2 && { NODE9_BROWSER_OPENED: "1" }
|
|
8317
|
+
}
|
|
8295
8318
|
});
|
|
8296
8319
|
child.unref();
|
|
8297
8320
|
for (let i = 0; i < 20; i++) {
|
|
8298
8321
|
await new Promise((r) => setTimeout(r, 250));
|
|
8299
8322
|
if (!isDaemonRunning()) continue;
|
|
8300
8323
|
try {
|
|
8301
|
-
const res = await fetch(
|
|
8324
|
+
const res = await fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/settings`, {
|
|
8302
8325
|
signal: AbortSignal.timeout(500)
|
|
8303
8326
|
});
|
|
8304
8327
|
if (res.ok) {
|
|
@@ -8512,7 +8535,7 @@ var init_scan_summary = __esm({
|
|
|
8512
8535
|
|
|
8513
8536
|
// src/cli/commands/scan.ts
|
|
8514
8537
|
import chalk2 from "chalk";
|
|
8515
|
-
import
|
|
8538
|
+
import fs14 from "fs";
|
|
8516
8539
|
import path17 from "path";
|
|
8517
8540
|
import os12 from "os";
|
|
8518
8541
|
function claudeModelPrice(model) {
|
|
@@ -8614,13 +8637,13 @@ function buildRuleSources() {
|
|
|
8614
8637
|
function countScanFiles() {
|
|
8615
8638
|
let total = 0;
|
|
8616
8639
|
const claudeDir = path17.join(os12.homedir(), ".claude", "projects");
|
|
8617
|
-
if (
|
|
8640
|
+
if (fs14.existsSync(claudeDir)) {
|
|
8618
8641
|
try {
|
|
8619
|
-
for (const proj of
|
|
8642
|
+
for (const proj of fs14.readdirSync(claudeDir)) {
|
|
8620
8643
|
const p = path17.join(claudeDir, proj);
|
|
8621
8644
|
try {
|
|
8622
|
-
if (!
|
|
8623
|
-
total +=
|
|
8645
|
+
if (!fs14.statSync(p).isDirectory()) continue;
|
|
8646
|
+
total += fs14.readdirSync(p).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).length;
|
|
8624
8647
|
} catch {
|
|
8625
8648
|
continue;
|
|
8626
8649
|
}
|
|
@@ -8629,16 +8652,16 @@ function countScanFiles() {
|
|
|
8629
8652
|
}
|
|
8630
8653
|
}
|
|
8631
8654
|
const geminiDir = path17.join(os12.homedir(), ".gemini", "tmp");
|
|
8632
|
-
if (
|
|
8655
|
+
if (fs14.existsSync(geminiDir)) {
|
|
8633
8656
|
try {
|
|
8634
|
-
for (const slug of
|
|
8657
|
+
for (const slug of fs14.readdirSync(geminiDir)) {
|
|
8635
8658
|
const p = path17.join(geminiDir, slug);
|
|
8636
8659
|
try {
|
|
8637
|
-
if (!
|
|
8660
|
+
if (!fs14.statSync(p).isDirectory()) continue;
|
|
8638
8661
|
const chatsDir = path17.join(p, "chats");
|
|
8639
|
-
if (
|
|
8662
|
+
if (fs14.existsSync(chatsDir)) {
|
|
8640
8663
|
try {
|
|
8641
|
-
total +=
|
|
8664
|
+
total += fs14.readdirSync(chatsDir).filter((f) => f.endsWith(".json")).length;
|
|
8642
8665
|
} catch {
|
|
8643
8666
|
}
|
|
8644
8667
|
}
|
|
@@ -8650,21 +8673,21 @@ function countScanFiles() {
|
|
|
8650
8673
|
}
|
|
8651
8674
|
}
|
|
8652
8675
|
const codexDir = path17.join(os12.homedir(), ".codex", "sessions");
|
|
8653
|
-
if (
|
|
8676
|
+
if (fs14.existsSync(codexDir)) {
|
|
8654
8677
|
try {
|
|
8655
|
-
for (const year of
|
|
8678
|
+
for (const year of fs14.readdirSync(codexDir)) {
|
|
8656
8679
|
const yp = path17.join(codexDir, year);
|
|
8657
8680
|
try {
|
|
8658
|
-
if (!
|
|
8659
|
-
for (const month of
|
|
8681
|
+
if (!fs14.statSync(yp).isDirectory()) continue;
|
|
8682
|
+
for (const month of fs14.readdirSync(yp)) {
|
|
8660
8683
|
const mp = path17.join(yp, month);
|
|
8661
8684
|
try {
|
|
8662
|
-
if (!
|
|
8663
|
-
for (const day of
|
|
8685
|
+
if (!fs14.statSync(mp).isDirectory()) continue;
|
|
8686
|
+
for (const day of fs14.readdirSync(mp)) {
|
|
8664
8687
|
const dp = path17.join(mp, day);
|
|
8665
8688
|
try {
|
|
8666
|
-
if (!
|
|
8667
|
-
total +=
|
|
8689
|
+
if (!fs14.statSync(dp).isDirectory()) continue;
|
|
8690
|
+
total += fs14.readdirSync(dp).filter((f) => f.endsWith(".jsonl")).length;
|
|
8668
8691
|
} catch {
|
|
8669
8692
|
continue;
|
|
8670
8693
|
}
|
|
@@ -8682,17 +8705,18 @@ function countScanFiles() {
|
|
|
8682
8705
|
}
|
|
8683
8706
|
return total;
|
|
8684
8707
|
}
|
|
8685
|
-
function renderProgressBar(done, total) {
|
|
8708
|
+
function renderProgressBar(done, total, lines) {
|
|
8686
8709
|
const width = 28;
|
|
8687
8710
|
const pct = total > 0 ? done / total : 0;
|
|
8688
8711
|
const filled = Math.min(width, Math.round(pct * width));
|
|
8689
8712
|
const bar = "\u2588".repeat(filled) + "\u2591".repeat(width - filled);
|
|
8690
|
-
const
|
|
8713
|
+
const fileLabel = total > 0 ? `${done}/${total} files` : `${done} files`;
|
|
8714
|
+
const lineLabel = lines > 0 ? chalk2.dim(` ${lines.toLocaleString()} lines`) : "";
|
|
8691
8715
|
process.stdout.write(
|
|
8692
|
-
`\r ${chalk2.cyan("Scanning")} [${chalk2.cyan(bar)}] ${chalk2.dim(
|
|
8716
|
+
`\r ${chalk2.cyan("Scanning")} [${chalk2.cyan(bar)}] ${chalk2.dim(fileLabel)}${lineLabel} `
|
|
8693
8717
|
);
|
|
8694
8718
|
}
|
|
8695
|
-
function scanClaudeHistory(startDate, onProgress) {
|
|
8719
|
+
function scanClaudeHistory(startDate, onProgress, onLine) {
|
|
8696
8720
|
const projectsDir = path17.join(os12.homedir(), ".claude", "projects");
|
|
8697
8721
|
const result = {
|
|
8698
8722
|
filesScanned: 0,
|
|
@@ -8706,10 +8730,10 @@ function scanClaudeHistory(startDate, onProgress) {
|
|
|
8706
8730
|
firstDate: null,
|
|
8707
8731
|
lastDate: null
|
|
8708
8732
|
};
|
|
8709
|
-
if (!
|
|
8733
|
+
if (!fs14.existsSync(projectsDir)) return result;
|
|
8710
8734
|
let projDirs;
|
|
8711
8735
|
try {
|
|
8712
|
-
projDirs =
|
|
8736
|
+
projDirs = fs14.readdirSync(projectsDir);
|
|
8713
8737
|
} catch {
|
|
8714
8738
|
return result;
|
|
8715
8739
|
}
|
|
@@ -8717,14 +8741,14 @@ function scanClaudeHistory(startDate, onProgress) {
|
|
|
8717
8741
|
for (const proj of projDirs) {
|
|
8718
8742
|
const projPath = path17.join(projectsDir, proj);
|
|
8719
8743
|
try {
|
|
8720
|
-
if (!
|
|
8744
|
+
if (!fs14.statSync(projPath).isDirectory()) continue;
|
|
8721
8745
|
} catch {
|
|
8722
8746
|
continue;
|
|
8723
8747
|
}
|
|
8724
8748
|
const projLabel = decodeURIComponent(proj).replace(os12.homedir(), "~").slice(0, 40);
|
|
8725
8749
|
let files;
|
|
8726
8750
|
try {
|
|
8727
|
-
files =
|
|
8751
|
+
files = fs14.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
8728
8752
|
} catch {
|
|
8729
8753
|
continue;
|
|
8730
8754
|
}
|
|
@@ -8735,13 +8759,14 @@ function scanClaudeHistory(startDate, onProgress) {
|
|
|
8735
8759
|
const sessionId = file.replace(/\.jsonl$/, "");
|
|
8736
8760
|
let raw;
|
|
8737
8761
|
try {
|
|
8738
|
-
raw =
|
|
8762
|
+
raw = fs14.readFileSync(path17.join(projPath, file), "utf-8");
|
|
8739
8763
|
} catch {
|
|
8740
8764
|
continue;
|
|
8741
8765
|
}
|
|
8742
8766
|
const sessionCalls = [];
|
|
8743
8767
|
for (const line of raw.split("\n")) {
|
|
8744
8768
|
if (!line.trim()) continue;
|
|
8769
|
+
onLine?.();
|
|
8745
8770
|
let entry;
|
|
8746
8771
|
try {
|
|
8747
8772
|
entry = JSON.parse(line);
|
|
@@ -8887,7 +8912,7 @@ function scanClaudeHistory(startDate, onProgress) {
|
|
|
8887
8912
|
}
|
|
8888
8913
|
return result;
|
|
8889
8914
|
}
|
|
8890
|
-
function scanGeminiHistory(startDate, onProgress) {
|
|
8915
|
+
function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
8891
8916
|
const tmpDir = path17.join(os12.homedir(), ".gemini", "tmp");
|
|
8892
8917
|
const result = {
|
|
8893
8918
|
filesScanned: 0,
|
|
@@ -8901,10 +8926,10 @@ function scanGeminiHistory(startDate, onProgress) {
|
|
|
8901
8926
|
firstDate: null,
|
|
8902
8927
|
lastDate: null
|
|
8903
8928
|
};
|
|
8904
|
-
if (!
|
|
8929
|
+
if (!fs14.existsSync(tmpDir)) return result;
|
|
8905
8930
|
let slugDirs;
|
|
8906
8931
|
try {
|
|
8907
|
-
slugDirs =
|
|
8932
|
+
slugDirs = fs14.readdirSync(tmpDir);
|
|
8908
8933
|
} catch {
|
|
8909
8934
|
return result;
|
|
8910
8935
|
}
|
|
@@ -8912,20 +8937,20 @@ function scanGeminiHistory(startDate, onProgress) {
|
|
|
8912
8937
|
for (const slug of slugDirs) {
|
|
8913
8938
|
const slugPath = path17.join(tmpDir, slug);
|
|
8914
8939
|
try {
|
|
8915
|
-
if (!
|
|
8940
|
+
if (!fs14.statSync(slugPath).isDirectory()) continue;
|
|
8916
8941
|
} catch {
|
|
8917
8942
|
continue;
|
|
8918
8943
|
}
|
|
8919
8944
|
let projLabel = slug;
|
|
8920
8945
|
try {
|
|
8921
|
-
projLabel =
|
|
8946
|
+
projLabel = fs14.readFileSync(path17.join(slugPath, ".project_root"), "utf-8").trim().replace(os12.homedir(), "~").slice(0, 40);
|
|
8922
8947
|
} catch {
|
|
8923
8948
|
}
|
|
8924
8949
|
const chatsDir = path17.join(slugPath, "chats");
|
|
8925
|
-
if (!
|
|
8950
|
+
if (!fs14.existsSync(chatsDir)) continue;
|
|
8926
8951
|
let chatFiles;
|
|
8927
8952
|
try {
|
|
8928
|
-
chatFiles =
|
|
8953
|
+
chatFiles = fs14.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
|
|
8929
8954
|
} catch {
|
|
8930
8955
|
continue;
|
|
8931
8956
|
}
|
|
@@ -8935,7 +8960,7 @@ function scanGeminiHistory(startDate, onProgress) {
|
|
|
8935
8960
|
const sessionId = chatFile.replace(/\.json$/, "");
|
|
8936
8961
|
let raw;
|
|
8937
8962
|
try {
|
|
8938
|
-
raw =
|
|
8963
|
+
raw = fs14.readFileSync(path17.join(chatsDir, chatFile), "utf-8");
|
|
8939
8964
|
} catch {
|
|
8940
8965
|
continue;
|
|
8941
8966
|
}
|
|
@@ -8948,6 +8973,7 @@ function scanGeminiHistory(startDate, onProgress) {
|
|
|
8948
8973
|
}
|
|
8949
8974
|
result.sessions++;
|
|
8950
8975
|
for (const msg of session.messages ?? []) {
|
|
8976
|
+
onLine?.();
|
|
8951
8977
|
if (msg.type === "user") {
|
|
8952
8978
|
const content = msg.content;
|
|
8953
8979
|
const text = Array.isArray(content) ? content.map((c) => c.text ?? "").join("\n") : typeof content === "string" ? content : "";
|
|
@@ -9083,7 +9109,7 @@ function scanGeminiHistory(startDate, onProgress) {
|
|
|
9083
9109
|
}
|
|
9084
9110
|
return result;
|
|
9085
9111
|
}
|
|
9086
|
-
function scanCodexHistory(startDate, onProgress) {
|
|
9112
|
+
function scanCodexHistory(startDate, onProgress, onLine) {
|
|
9087
9113
|
const sessionsBase = path17.join(os12.homedir(), ".codex", "sessions");
|
|
9088
9114
|
const result = {
|
|
9089
9115
|
filesScanned: 0,
|
|
@@ -9097,31 +9123,31 @@ function scanCodexHistory(startDate, onProgress) {
|
|
|
9097
9123
|
firstDate: null,
|
|
9098
9124
|
lastDate: null
|
|
9099
9125
|
};
|
|
9100
|
-
if (!
|
|
9126
|
+
if (!fs14.existsSync(sessionsBase)) return result;
|
|
9101
9127
|
const jsonlFiles = [];
|
|
9102
9128
|
try {
|
|
9103
|
-
for (const year of
|
|
9129
|
+
for (const year of fs14.readdirSync(sessionsBase)) {
|
|
9104
9130
|
const yearPath = path17.join(sessionsBase, year);
|
|
9105
9131
|
try {
|
|
9106
|
-
if (!
|
|
9132
|
+
if (!fs14.statSync(yearPath).isDirectory()) continue;
|
|
9107
9133
|
} catch {
|
|
9108
9134
|
continue;
|
|
9109
9135
|
}
|
|
9110
|
-
for (const month of
|
|
9136
|
+
for (const month of fs14.readdirSync(yearPath)) {
|
|
9111
9137
|
const monthPath = path17.join(yearPath, month);
|
|
9112
9138
|
try {
|
|
9113
|
-
if (!
|
|
9139
|
+
if (!fs14.statSync(monthPath).isDirectory()) continue;
|
|
9114
9140
|
} catch {
|
|
9115
9141
|
continue;
|
|
9116
9142
|
}
|
|
9117
|
-
for (const day of
|
|
9143
|
+
for (const day of fs14.readdirSync(monthPath)) {
|
|
9118
9144
|
const dayPath = path17.join(monthPath, day);
|
|
9119
9145
|
try {
|
|
9120
|
-
if (!
|
|
9146
|
+
if (!fs14.statSync(dayPath).isDirectory()) continue;
|
|
9121
9147
|
} catch {
|
|
9122
9148
|
continue;
|
|
9123
9149
|
}
|
|
9124
|
-
for (const file of
|
|
9150
|
+
for (const file of fs14.readdirSync(dayPath)) {
|
|
9125
9151
|
if (file.endsWith(".jsonl")) jsonlFiles.push(path17.join(dayPath, file));
|
|
9126
9152
|
}
|
|
9127
9153
|
}
|
|
@@ -9136,7 +9162,7 @@ function scanCodexHistory(startDate, onProgress) {
|
|
|
9136
9162
|
onProgress?.(result.filesScanned);
|
|
9137
9163
|
let lines;
|
|
9138
9164
|
try {
|
|
9139
|
-
lines =
|
|
9165
|
+
lines = fs14.readFileSync(filePath, "utf-8").split("\n");
|
|
9140
9166
|
} catch {
|
|
9141
9167
|
continue;
|
|
9142
9168
|
}
|
|
@@ -9150,6 +9176,7 @@ function scanCodexHistory(startDate, onProgress) {
|
|
|
9150
9176
|
let lastTotalOutput = 0;
|
|
9151
9177
|
for (const line of lines) {
|
|
9152
9178
|
if (!line.trim()) continue;
|
|
9179
|
+
onLine?.();
|
|
9153
9180
|
let entry;
|
|
9154
9181
|
try {
|
|
9155
9182
|
entry = JSON.parse(line);
|
|
@@ -9354,17 +9381,17 @@ function printRuleGroup(rule, topN, drillDown, previewWidth) {
|
|
|
9354
9381
|
}
|
|
9355
9382
|
}
|
|
9356
9383
|
function registerScanCommand(program2) {
|
|
9357
|
-
program2.command("scan").description("Forecast: scan agent history and show what node9 would catch if installed").option("--all", "Scan all history (default: last
|
|
9384
|
+
program2.command("scan").description("Forecast: scan agent history and show what node9 would catch if installed").option("--all", "Scan all history (default: last 30 days)").option("--days <n>", "Scan last N days of history", "30").option("--top <n>", "Max findings to show per rule (default: 5)", "5").option("--drill-down", "Show all findings with full commands and session IDs").action(async (options) => {
|
|
9358
9385
|
const drillDown = options.drillDown ?? false;
|
|
9359
9386
|
const topN = drillDown ? Infinity : Math.max(1, parseInt(options.top, 10) || 5);
|
|
9360
9387
|
const previewWidth = 70;
|
|
9361
9388
|
const startDate = options.all ? null : (() => {
|
|
9362
9389
|
const d = /* @__PURE__ */ new Date();
|
|
9363
|
-
d.setDate(d.getDate() - (parseInt(options.days, 10) ||
|
|
9390
|
+
d.setDate(d.getDate() - (parseInt(options.days, 10) || 30));
|
|
9364
9391
|
d.setHours(0, 0, 0, 0);
|
|
9365
9392
|
return d;
|
|
9366
9393
|
})();
|
|
9367
|
-
const isInstalled =
|
|
9394
|
+
const isInstalled = fs14.existsSync(path17.join(os12.homedir(), ".node9", "audit.log"));
|
|
9368
9395
|
console.log("");
|
|
9369
9396
|
if (!isInstalled) {
|
|
9370
9397
|
console.log(
|
|
@@ -9381,19 +9408,32 @@ function registerScanCommand(program2) {
|
|
|
9381
9408
|
console.log("");
|
|
9382
9409
|
const totalFiles = countScanFiles();
|
|
9383
9410
|
let filesScanned = 0;
|
|
9411
|
+
let linesScanned = 0;
|
|
9412
|
+
let lastRender = 0;
|
|
9384
9413
|
const onProgress = (done) => {
|
|
9385
9414
|
filesScanned = done;
|
|
9386
|
-
renderProgressBar(filesScanned, totalFiles);
|
|
9415
|
+
renderProgressBar(filesScanned, totalFiles, linesScanned);
|
|
9416
|
+
lastRender = Date.now();
|
|
9417
|
+
};
|
|
9418
|
+
const onLine = () => {
|
|
9419
|
+
linesScanned++;
|
|
9420
|
+
const now = Date.now();
|
|
9421
|
+
if (now - lastRender >= 80) {
|
|
9422
|
+
lastRender = now;
|
|
9423
|
+
renderProgressBar(filesScanned, totalFiles, linesScanned);
|
|
9424
|
+
}
|
|
9387
9425
|
};
|
|
9388
|
-
renderProgressBar(0, totalFiles);
|
|
9389
|
-
const claudeScan = scanClaudeHistory(startDate, onProgress);
|
|
9426
|
+
renderProgressBar(0, totalFiles, 0);
|
|
9427
|
+
const claudeScan = scanClaudeHistory(startDate, onProgress, onLine);
|
|
9390
9428
|
const geminiScan = scanGeminiHistory(
|
|
9391
9429
|
startDate,
|
|
9392
|
-
(done) => onProgress(claudeScan.filesScanned + done)
|
|
9430
|
+
(done) => onProgress(claudeScan.filesScanned + done),
|
|
9431
|
+
onLine
|
|
9393
9432
|
);
|
|
9394
9433
|
const codexScan = scanCodexHistory(
|
|
9395
9434
|
startDate,
|
|
9396
|
-
(done) => onProgress(claudeScan.filesScanned + geminiScan.filesScanned + done)
|
|
9435
|
+
(done) => onProgress(claudeScan.filesScanned + geminiScan.filesScanned + done),
|
|
9436
|
+
onLine
|
|
9397
9437
|
);
|
|
9398
9438
|
const scan = mergeScans(mergeScans(claudeScan, geminiScan), codexScan);
|
|
9399
9439
|
const summary = buildScanSummary([
|
|
@@ -9411,7 +9451,7 @@ function registerScanCommand(program2) {
|
|
|
9411
9451
|
);
|
|
9412
9452
|
return;
|
|
9413
9453
|
}
|
|
9414
|
-
const rangeLabel = options.all ? chalk2.dim("all time") : chalk2.dim(`last ${options.days ??
|
|
9454
|
+
const rangeLabel = options.all ? chalk2.dim("all time") : chalk2.dim(`last ${options.days ?? 30} days`);
|
|
9415
9455
|
const dateRange = scan.firstDate && scan.lastDate ? chalk2.dim(` ${fmtTs(scan.firstDate)} \u2013 ${fmtTs(scan.lastDate)}`) : "";
|
|
9416
9456
|
const breakdownParts = [];
|
|
9417
9457
|
if (claudeScan.sessions > 0)
|
|
@@ -9589,29 +9629,33 @@ function registerScanCommand(program2) {
|
|
|
9589
9629
|
console.log(" " + chalk2.dim("\u2192 ") + chalk2.underline("https://node9.ai"));
|
|
9590
9630
|
}
|
|
9591
9631
|
console.log("");
|
|
9592
|
-
|
|
9593
|
-
|
|
9594
|
-
if (
|
|
9595
|
-
|
|
9596
|
-
|
|
9597
|
-
|
|
9598
|
-
|
|
9599
|
-
|
|
9600
|
-
|
|
9601
|
-
|
|
9602
|
-
|
|
9603
|
-
|
|
9604
|
-
|
|
9605
|
-
|
|
9606
|
-
|
|
9607
|
-
|
|
9608
|
-
|
|
9609
|
-
|
|
9610
|
-
|
|
9611
|
-
|
|
9612
|
-
} catch {
|
|
9632
|
+
const url = `http://${DAEMON_HOST}:${DAEMON_PORT}/`;
|
|
9633
|
+
if (!isTestingMode()) {
|
|
9634
|
+
if (isDaemonRunning()) {
|
|
9635
|
+
const internalToken = getInternalToken();
|
|
9636
|
+
if (internalToken) {
|
|
9637
|
+
try {
|
|
9638
|
+
const summary2 = buildScanSummary([
|
|
9639
|
+
{ id: "claude", label: "Claude", icon: "\u{1F916}", scan: claudeScan },
|
|
9640
|
+
{ id: "gemini", label: "Gemini", icon: "\u264A", scan: geminiScan },
|
|
9641
|
+
{ id: "codex", label: "Codex", icon: "\u{1F52E}", scan: codexScan }
|
|
9642
|
+
]);
|
|
9643
|
+
await fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/scan/push`, {
|
|
9644
|
+
method: "POST",
|
|
9645
|
+
headers: { "Content-Type": "application/json", "x-node9-internal": internalToken },
|
|
9646
|
+
body: JSON.stringify({ status: "complete", summary: summary2 }),
|
|
9647
|
+
signal: AbortSignal.timeout(3e3)
|
|
9648
|
+
});
|
|
9649
|
+
openBrowserLocal();
|
|
9650
|
+
} catch {
|
|
9651
|
+
}
|
|
9613
9652
|
}
|
|
9614
9653
|
}
|
|
9654
|
+
console.log(" " + chalk2.cyan("\u{1F310} View in browser:") + " " + chalk2.underline(url));
|
|
9655
|
+
if (!isDaemonRunning()) {
|
|
9656
|
+
console.log(" " + chalk2.dim(" run node9 daemon --background to activate"));
|
|
9657
|
+
}
|
|
9658
|
+
console.log("");
|
|
9615
9659
|
}
|
|
9616
9660
|
});
|
|
9617
9661
|
}
|
|
@@ -9754,7 +9798,7 @@ var init_suggestion_tracker = __esm({
|
|
|
9754
9798
|
});
|
|
9755
9799
|
|
|
9756
9800
|
// src/daemon/taint-store.ts
|
|
9757
|
-
import
|
|
9801
|
+
import fs15 from "fs";
|
|
9758
9802
|
import path18 from "path";
|
|
9759
9803
|
var DEFAULT_TTL_MS, TaintStore;
|
|
9760
9804
|
var init_taint_store = __esm({
|
|
@@ -9824,7 +9868,7 @@ var init_taint_store = __esm({
|
|
|
9824
9868
|
/** Resolve to absolute path, falling back to path.resolve if file doesn't exist yet. */
|
|
9825
9869
|
_resolve(filePath) {
|
|
9826
9870
|
try {
|
|
9827
|
-
return
|
|
9871
|
+
return fs15.realpathSync.native(path18.resolve(filePath));
|
|
9828
9872
|
} catch {
|
|
9829
9873
|
return path18.resolve(filePath);
|
|
9830
9874
|
}
|
|
@@ -9943,15 +9987,15 @@ var init_session_history = __esm({
|
|
|
9943
9987
|
|
|
9944
9988
|
// src/daemon/state.ts
|
|
9945
9989
|
import net2 from "net";
|
|
9946
|
-
import
|
|
9990
|
+
import fs16 from "fs";
|
|
9947
9991
|
import path19 from "path";
|
|
9948
9992
|
import os13 from "os";
|
|
9949
9993
|
import { spawn as spawn3 } from "child_process";
|
|
9950
9994
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
9951
9995
|
function loadInsightCounts() {
|
|
9952
9996
|
try {
|
|
9953
|
-
if (!
|
|
9954
|
-
const data = JSON.parse(
|
|
9997
|
+
if (!fs16.existsSync(INSIGHT_COUNTS_FILE)) return;
|
|
9998
|
+
const data = JSON.parse(fs16.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
|
|
9955
9999
|
for (const [tool, count] of Object.entries(data)) {
|
|
9956
10000
|
if (typeof count === "number" && count > 0) insightCounts.set(tool, count);
|
|
9957
10001
|
}
|
|
@@ -9995,22 +10039,22 @@ function setCachedScanResult(result) {
|
|
|
9995
10039
|
}
|
|
9996
10040
|
function atomicWriteSync2(filePath, data, options) {
|
|
9997
10041
|
const dir = path19.dirname(filePath);
|
|
9998
|
-
if (!
|
|
10042
|
+
if (!fs16.existsSync(dir)) fs16.mkdirSync(dir, { recursive: true });
|
|
9999
10043
|
const tmpPath = `${filePath}.${randomUUID3()}.tmp`;
|
|
10000
10044
|
try {
|
|
10001
|
-
|
|
10045
|
+
fs16.writeFileSync(tmpPath, data, options);
|
|
10002
10046
|
} catch (err2) {
|
|
10003
10047
|
try {
|
|
10004
|
-
|
|
10048
|
+
fs16.unlinkSync(tmpPath);
|
|
10005
10049
|
} catch {
|
|
10006
10050
|
}
|
|
10007
10051
|
throw err2;
|
|
10008
10052
|
}
|
|
10009
10053
|
try {
|
|
10010
|
-
|
|
10054
|
+
fs16.renameSync(tmpPath, filePath);
|
|
10011
10055
|
} catch (err2) {
|
|
10012
10056
|
try {
|
|
10013
|
-
|
|
10057
|
+
fs16.unlinkSync(tmpPath);
|
|
10014
10058
|
} catch {
|
|
10015
10059
|
}
|
|
10016
10060
|
throw err2;
|
|
@@ -10035,15 +10079,15 @@ function appendAuditLog(data) {
|
|
|
10035
10079
|
source: "daemon"
|
|
10036
10080
|
};
|
|
10037
10081
|
const dir = path19.dirname(AUDIT_LOG_FILE);
|
|
10038
|
-
if (!
|
|
10039
|
-
|
|
10082
|
+
if (!fs16.existsSync(dir)) fs16.mkdirSync(dir, { recursive: true });
|
|
10083
|
+
fs16.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
|
|
10040
10084
|
} catch {
|
|
10041
10085
|
}
|
|
10042
10086
|
}
|
|
10043
10087
|
function getAuditHistory(limit = 20) {
|
|
10044
10088
|
try {
|
|
10045
|
-
if (!
|
|
10046
|
-
const lines =
|
|
10089
|
+
if (!fs16.existsSync(AUDIT_LOG_FILE)) return [];
|
|
10090
|
+
const lines = fs16.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
|
|
10047
10091
|
if (lines.length === 1 && lines[0] === "") return [];
|
|
10048
10092
|
return lines.slice(-limit).map((l) => JSON.parse(l)).reverse();
|
|
10049
10093
|
} catch {
|
|
@@ -10052,19 +10096,19 @@ function getAuditHistory(limit = 20) {
|
|
|
10052
10096
|
}
|
|
10053
10097
|
function getOrgName() {
|
|
10054
10098
|
try {
|
|
10055
|
-
if (
|
|
10099
|
+
if (fs16.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
|
|
10056
10100
|
} catch {
|
|
10057
10101
|
}
|
|
10058
10102
|
return null;
|
|
10059
10103
|
}
|
|
10060
10104
|
function hasStoredSlackKey() {
|
|
10061
|
-
return
|
|
10105
|
+
return fs16.existsSync(CREDENTIALS_FILE);
|
|
10062
10106
|
}
|
|
10063
10107
|
function writeGlobalSetting(key, value) {
|
|
10064
10108
|
let config = {};
|
|
10065
10109
|
try {
|
|
10066
|
-
if (
|
|
10067
|
-
config = JSON.parse(
|
|
10110
|
+
if (fs16.existsSync(GLOBAL_CONFIG_FILE)) {
|
|
10111
|
+
config = JSON.parse(fs16.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
|
|
10068
10112
|
}
|
|
10069
10113
|
} catch {
|
|
10070
10114
|
}
|
|
@@ -10076,8 +10120,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
|
|
|
10076
10120
|
try {
|
|
10077
10121
|
let trust = { entries: [] };
|
|
10078
10122
|
try {
|
|
10079
|
-
if (
|
|
10080
|
-
trust = JSON.parse(
|
|
10123
|
+
if (fs16.existsSync(TRUST_FILE2))
|
|
10124
|
+
trust = JSON.parse(fs16.readFileSync(TRUST_FILE2, "utf-8"));
|
|
10081
10125
|
} catch {
|
|
10082
10126
|
}
|
|
10083
10127
|
trust.entries = trust.entries.filter(
|
|
@@ -10094,8 +10138,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
|
|
|
10094
10138
|
}
|
|
10095
10139
|
function readPersistentDecisions() {
|
|
10096
10140
|
try {
|
|
10097
|
-
if (
|
|
10098
|
-
return JSON.parse(
|
|
10141
|
+
if (fs16.existsSync(DECISIONS_FILE)) {
|
|
10142
|
+
return JSON.parse(fs16.readFileSync(DECISIONS_FILE, "utf-8"));
|
|
10099
10143
|
}
|
|
10100
10144
|
} catch {
|
|
10101
10145
|
}
|
|
@@ -10132,7 +10176,7 @@ function estimateToolCost(tool, args) {
|
|
|
10132
10176
|
const filePath = a.file_path ?? a.path;
|
|
10133
10177
|
if (filePath) {
|
|
10134
10178
|
try {
|
|
10135
|
-
const bytes =
|
|
10179
|
+
const bytes = fs16.statSync(filePath).size;
|
|
10136
10180
|
return bytes / BYTES_PER_TOKEN / 1e6 * INPUT_PRICE_PER_1M;
|
|
10137
10181
|
} catch {
|
|
10138
10182
|
}
|
|
@@ -10190,7 +10234,7 @@ function abandonPending() {
|
|
|
10190
10234
|
});
|
|
10191
10235
|
if (autoStarted) {
|
|
10192
10236
|
try {
|
|
10193
|
-
|
|
10237
|
+
fs16.unlinkSync(DAEMON_PID_FILE);
|
|
10194
10238
|
} catch {
|
|
10195
10239
|
}
|
|
10196
10240
|
setTimeout(() => {
|
|
@@ -10201,7 +10245,7 @@ function abandonPending() {
|
|
|
10201
10245
|
}
|
|
10202
10246
|
function startActivitySocket() {
|
|
10203
10247
|
try {
|
|
10204
|
-
|
|
10248
|
+
fs16.unlinkSync(ACTIVITY_SOCKET_PATH2);
|
|
10205
10249
|
} catch {
|
|
10206
10250
|
}
|
|
10207
10251
|
const ACTIVITY_MAX_BYTES = 1024 * 1024;
|
|
@@ -10283,7 +10327,7 @@ function startActivitySocket() {
|
|
|
10283
10327
|
unixServer.listen(ACTIVITY_SOCKET_PATH2);
|
|
10284
10328
|
process.on("exit", () => {
|
|
10285
10329
|
try {
|
|
10286
|
-
|
|
10330
|
+
fs16.unlinkSync(ACTIVITY_SOCKET_PATH2);
|
|
10287
10331
|
} catch {
|
|
10288
10332
|
}
|
|
10289
10333
|
});
|
|
@@ -10349,14 +10393,14 @@ var init_state2 = __esm({
|
|
|
10349
10393
|
});
|
|
10350
10394
|
|
|
10351
10395
|
// src/config/patch.ts
|
|
10352
|
-
import
|
|
10396
|
+
import fs17 from "fs";
|
|
10353
10397
|
import path20 from "path";
|
|
10354
10398
|
import os14 from "os";
|
|
10355
10399
|
function patchConfig(configPath, patch) {
|
|
10356
10400
|
let config = {};
|
|
10357
10401
|
try {
|
|
10358
|
-
if (
|
|
10359
|
-
config = JSON.parse(
|
|
10402
|
+
if (fs17.existsSync(configPath)) {
|
|
10403
|
+
config = JSON.parse(fs17.readFileSync(configPath, "utf8"));
|
|
10360
10404
|
}
|
|
10361
10405
|
} catch {
|
|
10362
10406
|
throw new Error(`Cannot read config at ${configPath} \u2014 file may be corrupted`);
|
|
@@ -10376,22 +10420,22 @@ function patchConfig(configPath, patch) {
|
|
|
10376
10420
|
}
|
|
10377
10421
|
}
|
|
10378
10422
|
const dir = path20.dirname(configPath);
|
|
10379
|
-
|
|
10423
|
+
fs17.mkdirSync(dir, { recursive: true });
|
|
10380
10424
|
const tmp = configPath + ".node9-tmp";
|
|
10381
10425
|
try {
|
|
10382
|
-
|
|
10426
|
+
fs17.writeFileSync(tmp, JSON.stringify(config, null, 2), { mode: 384 });
|
|
10383
10427
|
} catch (err2) {
|
|
10384
10428
|
try {
|
|
10385
|
-
|
|
10429
|
+
fs17.unlinkSync(tmp);
|
|
10386
10430
|
} catch {
|
|
10387
10431
|
}
|
|
10388
10432
|
throw err2;
|
|
10389
10433
|
}
|
|
10390
10434
|
try {
|
|
10391
|
-
|
|
10435
|
+
fs17.renameSync(tmp, configPath);
|
|
10392
10436
|
} catch (err2) {
|
|
10393
10437
|
try {
|
|
10394
|
-
|
|
10438
|
+
fs17.unlinkSync(tmp);
|
|
10395
10439
|
} catch {
|
|
10396
10440
|
}
|
|
10397
10441
|
throw err2;
|
|
@@ -10406,7 +10450,7 @@ var init_patch = __esm({
|
|
|
10406
10450
|
});
|
|
10407
10451
|
|
|
10408
10452
|
// src/costSync.ts
|
|
10409
|
-
import
|
|
10453
|
+
import fs18 from "fs";
|
|
10410
10454
|
import path21 from "path";
|
|
10411
10455
|
import os15 from "os";
|
|
10412
10456
|
function normalizeModel(raw) {
|
|
@@ -10424,7 +10468,7 @@ function pricingFor(model) {
|
|
|
10424
10468
|
function parseJSONLFile(filePath) {
|
|
10425
10469
|
let content;
|
|
10426
10470
|
try {
|
|
10427
|
-
content =
|
|
10471
|
+
content = fs18.readFileSync(filePath, "utf8");
|
|
10428
10472
|
} catch {
|
|
10429
10473
|
return /* @__PURE__ */ new Map();
|
|
10430
10474
|
}
|
|
@@ -10477,24 +10521,24 @@ function parseJSONLFile(filePath) {
|
|
|
10477
10521
|
}
|
|
10478
10522
|
function collectEntries() {
|
|
10479
10523
|
const projectsDir = path21.join(os15.homedir(), ".claude", "projects");
|
|
10480
|
-
if (!
|
|
10524
|
+
if (!fs18.existsSync(projectsDir)) return [];
|
|
10481
10525
|
const combined = /* @__PURE__ */ new Map();
|
|
10482
10526
|
let dirs;
|
|
10483
10527
|
try {
|
|
10484
|
-
dirs =
|
|
10528
|
+
dirs = fs18.readdirSync(projectsDir);
|
|
10485
10529
|
} catch {
|
|
10486
10530
|
return [];
|
|
10487
10531
|
}
|
|
10488
10532
|
for (const dir of dirs) {
|
|
10489
10533
|
const dirPath = path21.join(projectsDir, dir);
|
|
10490
10534
|
try {
|
|
10491
|
-
if (!
|
|
10535
|
+
if (!fs18.statSync(dirPath).isDirectory()) continue;
|
|
10492
10536
|
} catch {
|
|
10493
10537
|
continue;
|
|
10494
10538
|
}
|
|
10495
10539
|
let files;
|
|
10496
10540
|
try {
|
|
10497
|
-
files =
|
|
10541
|
+
files = fs18.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
|
|
10498
10542
|
} catch {
|
|
10499
10543
|
continue;
|
|
10500
10544
|
}
|
|
@@ -10535,11 +10579,11 @@ async function syncCost() {
|
|
|
10535
10579
|
signal: AbortSignal.timeout(15e3)
|
|
10536
10580
|
});
|
|
10537
10581
|
if (!res.ok) {
|
|
10538
|
-
|
|
10582
|
+
fs18.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
|
|
10539
10583
|
`);
|
|
10540
10584
|
}
|
|
10541
10585
|
} catch (err2) {
|
|
10542
|
-
|
|
10586
|
+
fs18.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
|
|
10543
10587
|
`);
|
|
10544
10588
|
}
|
|
10545
10589
|
}
|
|
@@ -10572,7 +10616,7 @@ var init_costSync = __esm({
|
|
|
10572
10616
|
});
|
|
10573
10617
|
|
|
10574
10618
|
// src/daemon/sync.ts
|
|
10575
|
-
import
|
|
10619
|
+
import fs19 from "fs";
|
|
10576
10620
|
import https from "https";
|
|
10577
10621
|
import os16 from "os";
|
|
10578
10622
|
import path22 from "path";
|
|
@@ -10585,7 +10629,7 @@ function readCredentials() {
|
|
|
10585
10629
|
}
|
|
10586
10630
|
try {
|
|
10587
10631
|
const credPath = path22.join(os16.homedir(), ".node9", "credentials.json");
|
|
10588
|
-
const creds = JSON.parse(
|
|
10632
|
+
const creds = JSON.parse(fs19.readFileSync(credPath, "utf-8"));
|
|
10589
10633
|
const profileName = process.env.NODE9_PROFILE ?? "default";
|
|
10590
10634
|
const profile = creds[profileName];
|
|
10591
10635
|
if (typeof profile?.apiKey === "string" && profile.apiKey.length > 0) {
|
|
@@ -10648,8 +10692,8 @@ async function syncOnce() {
|
|
|
10648
10692
|
const rules = await fetchCloudRules(creds.apiKey, creds.apiUrl);
|
|
10649
10693
|
const cache = { fetchedAt: (/* @__PURE__ */ new Date()).toISOString(), rules };
|
|
10650
10694
|
const dir = path22.dirname(rulesCacheFile());
|
|
10651
|
-
if (!
|
|
10652
|
-
|
|
10695
|
+
if (!fs19.existsSync(dir)) fs19.mkdirSync(dir, { recursive: true });
|
|
10696
|
+
fs19.writeFileSync(rulesCacheFile(), JSON.stringify(cache, null, 2) + "\n", "utf-8");
|
|
10653
10697
|
} catch {
|
|
10654
10698
|
}
|
|
10655
10699
|
}
|
|
@@ -10662,8 +10706,8 @@ async function runCloudSync() {
|
|
|
10662
10706
|
const rules = await fetchCloudRules(creds.apiKey, creds.apiUrl);
|
|
10663
10707
|
const cache = { fetchedAt: (/* @__PURE__ */ new Date()).toISOString(), rules };
|
|
10664
10708
|
const dir = path22.dirname(rulesCacheFile());
|
|
10665
|
-
if (!
|
|
10666
|
-
|
|
10709
|
+
if (!fs19.existsSync(dir)) fs19.mkdirSync(dir, { recursive: true });
|
|
10710
|
+
fs19.writeFileSync(rulesCacheFile(), JSON.stringify(cache, null, 2) + "\n", "utf-8");
|
|
10667
10711
|
return { ok: true, rules: rules.length, fetchedAt: cache.fetchedAt };
|
|
10668
10712
|
} catch (err2) {
|
|
10669
10713
|
return { ok: false, reason: err2 instanceof Error ? err2.message : String(err2) };
|
|
@@ -10671,7 +10715,7 @@ async function runCloudSync() {
|
|
|
10671
10715
|
}
|
|
10672
10716
|
function getCloudSyncStatus() {
|
|
10673
10717
|
try {
|
|
10674
|
-
const raw = JSON.parse(
|
|
10718
|
+
const raw = JSON.parse(fs19.readFileSync(rulesCacheFile(), "utf-8"));
|
|
10675
10719
|
if (!Array.isArray(raw.rules) || typeof raw.fetchedAt !== "string") return { cached: false };
|
|
10676
10720
|
return { cached: true, rules: raw.rules.length, fetchedAt: raw.fetchedAt };
|
|
10677
10721
|
} catch {
|
|
@@ -10680,7 +10724,7 @@ function getCloudSyncStatus() {
|
|
|
10680
10724
|
}
|
|
10681
10725
|
function getCloudRules() {
|
|
10682
10726
|
try {
|
|
10683
|
-
const raw = JSON.parse(
|
|
10727
|
+
const raw = JSON.parse(fs19.readFileSync(rulesCacheFile(), "utf-8"));
|
|
10684
10728
|
return Array.isArray(raw.rules) ? raw.rules : null;
|
|
10685
10729
|
} catch {
|
|
10686
10730
|
return null;
|
|
@@ -10708,50 +10752,50 @@ var init_sync = __esm({
|
|
|
10708
10752
|
});
|
|
10709
10753
|
|
|
10710
10754
|
// src/daemon/dlp-scanner.ts
|
|
10711
|
-
import
|
|
10755
|
+
import fs20 from "fs";
|
|
10712
10756
|
import path23 from "path";
|
|
10713
10757
|
import os17 from "os";
|
|
10714
10758
|
function loadIndex() {
|
|
10715
10759
|
try {
|
|
10716
|
-
return JSON.parse(
|
|
10760
|
+
return JSON.parse(fs20.readFileSync(INDEX_FILE, "utf-8"));
|
|
10717
10761
|
} catch {
|
|
10718
10762
|
return {};
|
|
10719
10763
|
}
|
|
10720
10764
|
}
|
|
10721
10765
|
function saveIndex(index) {
|
|
10722
10766
|
try {
|
|
10723
|
-
|
|
10767
|
+
fs20.writeFileSync(INDEX_FILE, JSON.stringify(index), { encoding: "utf-8", mode: 384 });
|
|
10724
10768
|
} catch {
|
|
10725
10769
|
}
|
|
10726
10770
|
}
|
|
10727
10771
|
function appendAuditEntry(entry) {
|
|
10728
10772
|
try {
|
|
10729
|
-
|
|
10773
|
+
fs20.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
|
|
10730
10774
|
} catch {
|
|
10731
10775
|
}
|
|
10732
10776
|
}
|
|
10733
10777
|
function runDlpScan() {
|
|
10734
|
-
if (!
|
|
10778
|
+
if (!fs20.existsSync(PROJECTS_DIR)) return;
|
|
10735
10779
|
const index = loadIndex();
|
|
10736
10780
|
let updated = false;
|
|
10737
10781
|
let projDirs;
|
|
10738
10782
|
try {
|
|
10739
|
-
projDirs =
|
|
10783
|
+
projDirs = fs20.readdirSync(PROJECTS_DIR);
|
|
10740
10784
|
} catch {
|
|
10741
10785
|
return;
|
|
10742
10786
|
}
|
|
10743
10787
|
for (const proj of projDirs) {
|
|
10744
10788
|
const projPath = path23.join(PROJECTS_DIR, proj);
|
|
10745
10789
|
try {
|
|
10746
|
-
if (!
|
|
10747
|
-
const real =
|
|
10790
|
+
if (!fs20.lstatSync(projPath).isDirectory()) continue;
|
|
10791
|
+
const real = fs20.realpathSync(projPath);
|
|
10748
10792
|
if (!real.startsWith(PROJECTS_DIR + path23.sep) && real !== PROJECTS_DIR) continue;
|
|
10749
10793
|
} catch {
|
|
10750
10794
|
continue;
|
|
10751
10795
|
}
|
|
10752
10796
|
let files;
|
|
10753
10797
|
try {
|
|
10754
|
-
files =
|
|
10798
|
+
files = fs20.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
10755
10799
|
} catch {
|
|
10756
10800
|
continue;
|
|
10757
10801
|
}
|
|
@@ -10760,21 +10804,21 @@ function runDlpScan() {
|
|
|
10760
10804
|
const lastOffset = index[filePath] ?? 0;
|
|
10761
10805
|
let size;
|
|
10762
10806
|
try {
|
|
10763
|
-
size =
|
|
10807
|
+
size = fs20.statSync(filePath).size;
|
|
10764
10808
|
} catch {
|
|
10765
10809
|
continue;
|
|
10766
10810
|
}
|
|
10767
10811
|
if (size <= lastOffset) continue;
|
|
10768
10812
|
let fd;
|
|
10769
10813
|
try {
|
|
10770
|
-
fd =
|
|
10814
|
+
fd = fs20.openSync(filePath, "r");
|
|
10771
10815
|
} catch {
|
|
10772
10816
|
continue;
|
|
10773
10817
|
}
|
|
10774
10818
|
try {
|
|
10775
10819
|
const chunkSize = size - lastOffset;
|
|
10776
10820
|
const buf = Buffer.alloc(chunkSize);
|
|
10777
|
-
|
|
10821
|
+
fs20.readSync(fd, buf, 0, chunkSize, lastOffset);
|
|
10778
10822
|
const chunk = buf.toString("utf-8");
|
|
10779
10823
|
for (const line of chunk.split("\n")) {
|
|
10780
10824
|
if (!line.trim()) continue;
|
|
@@ -10819,7 +10863,7 @@ Run: node9 report --period 30d`
|
|
|
10819
10863
|
updated = true;
|
|
10820
10864
|
} finally {
|
|
10821
10865
|
try {
|
|
10822
|
-
|
|
10866
|
+
fs20.closeSync(fd);
|
|
10823
10867
|
} catch {
|
|
10824
10868
|
}
|
|
10825
10869
|
}
|
|
@@ -10858,7 +10902,7 @@ var init_dlp_scanner = __esm({
|
|
|
10858
10902
|
});
|
|
10859
10903
|
|
|
10860
10904
|
// src/daemon/mcp-tools.ts
|
|
10861
|
-
import
|
|
10905
|
+
import fs21 from "fs";
|
|
10862
10906
|
import path24 from "path";
|
|
10863
10907
|
import os18 from "os";
|
|
10864
10908
|
function getMcpToolsFile() {
|
|
@@ -10867,8 +10911,8 @@ function getMcpToolsFile() {
|
|
|
10867
10911
|
function readMcpToolsConfig() {
|
|
10868
10912
|
try {
|
|
10869
10913
|
const file = getMcpToolsFile();
|
|
10870
|
-
if (!
|
|
10871
|
-
const raw =
|
|
10914
|
+
if (!fs21.existsSync(file)) return {};
|
|
10915
|
+
const raw = fs21.readFileSync(file, "utf-8");
|
|
10872
10916
|
return JSON.parse(raw);
|
|
10873
10917
|
} catch {
|
|
10874
10918
|
return {};
|
|
@@ -10878,10 +10922,10 @@ function writeMcpToolsConfig(config) {
|
|
|
10878
10922
|
try {
|
|
10879
10923
|
const file = getMcpToolsFile();
|
|
10880
10924
|
const dir = path24.dirname(file);
|
|
10881
|
-
if (!
|
|
10925
|
+
if (!fs21.existsSync(dir)) fs21.mkdirSync(dir, { recursive: true });
|
|
10882
10926
|
const tmpPath = `${file}.${os18.hostname()}.${process.pid}.tmp`;
|
|
10883
|
-
|
|
10884
|
-
|
|
10927
|
+
fs21.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
|
|
10928
|
+
fs21.renameSync(tmpPath, file);
|
|
10885
10929
|
} catch (e) {
|
|
10886
10930
|
console.error("Failed to write mcp-tools.json", e);
|
|
10887
10931
|
}
|
|
@@ -10928,7 +10972,7 @@ var init_mcp_tools = __esm({
|
|
|
10928
10972
|
|
|
10929
10973
|
// src/daemon/server.ts
|
|
10930
10974
|
import http from "http";
|
|
10931
|
-
import
|
|
10975
|
+
import fs22 from "fs";
|
|
10932
10976
|
import path25 from "path";
|
|
10933
10977
|
import os19 from "os";
|
|
10934
10978
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
@@ -10953,7 +10997,7 @@ function startDaemon() {
|
|
|
10953
10997
|
idleTimer = setTimeout(() => {
|
|
10954
10998
|
if (autoStarted) {
|
|
10955
10999
|
try {
|
|
10956
|
-
|
|
11000
|
+
fs22.unlinkSync(DAEMON_PID_FILE);
|
|
10957
11001
|
} catch {
|
|
10958
11002
|
}
|
|
10959
11003
|
}
|
|
@@ -11489,7 +11533,7 @@ data: ${JSON.stringify(item.data)}
|
|
|
11489
11533
|
const periodParam = reqUrl.searchParams.get("period") || "7d";
|
|
11490
11534
|
const period = ["today", "7d", "30d", "month"].includes(periodParam) ? periodParam : "7d";
|
|
11491
11535
|
const logPath = path25.join(os19.homedir(), ".node9", "audit.log");
|
|
11492
|
-
if (!
|
|
11536
|
+
if (!fs22.existsSync(logPath)) {
|
|
11493
11537
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
11494
11538
|
return res.end(
|
|
11495
11539
|
JSON.stringify({
|
|
@@ -11502,7 +11546,7 @@ data: ${JSON.stringify(item.data)}
|
|
|
11502
11546
|
);
|
|
11503
11547
|
}
|
|
11504
11548
|
try {
|
|
11505
|
-
const raw =
|
|
11549
|
+
const raw = fs22.readFileSync(logPath, "utf-8");
|
|
11506
11550
|
const allEntries = raw.split("\n").flatMap((line) => {
|
|
11507
11551
|
if (!line.trim()) return [];
|
|
11508
11552
|
try {
|
|
@@ -11606,7 +11650,7 @@ data: ${JSON.stringify(item.data)}
|
|
|
11606
11650
|
}
|
|
11607
11651
|
try {
|
|
11608
11652
|
const d = /* @__PURE__ */ new Date();
|
|
11609
|
-
d.setDate(d.getDate() -
|
|
11653
|
+
d.setDate(d.getDate() - 30);
|
|
11610
11654
|
d.setHours(0, 0, 0, 0);
|
|
11611
11655
|
const EMPTY_SCAN = {
|
|
11612
11656
|
filesScanned: 0,
|
|
@@ -11797,7 +11841,7 @@ data: ${JSON.stringify(item.data)}
|
|
|
11797
11841
|
if (!validToken(req)) return res.writeHead(403).end();
|
|
11798
11842
|
try {
|
|
11799
11843
|
const { serverKey, disabledTools } = JSON.parse(await readBody(req));
|
|
11800
|
-
if (typeof serverKey !== "string" || !Array.isArray(disabledTools)) {
|
|
11844
|
+
if (typeof serverKey !== "string" || serverKey.length < 1 || serverKey.length > 256 || !/^[\w.-]+$/.test(serverKey) || !Array.isArray(disabledTools)) {
|
|
11801
11845
|
res.writeHead(400).end();
|
|
11802
11846
|
return;
|
|
11803
11847
|
}
|
|
@@ -11819,7 +11863,7 @@ data: ${JSON.stringify(item.data)}
|
|
|
11819
11863
|
if (req.headers["x-node9-internal"] !== internalToken) return res.writeHead(403).end();
|
|
11820
11864
|
try {
|
|
11821
11865
|
const { serverKey, tools } = JSON.parse(await readBody(req));
|
|
11822
|
-
if (typeof serverKey !== "string" || !Array.isArray(tools)) {
|
|
11866
|
+
if (typeof serverKey !== "string" || serverKey.length < 1 || serverKey.length > 256 || !/^[\w.-]+$/.test(serverKey) || !Array.isArray(tools)) {
|
|
11823
11867
|
res.writeHead(400).end();
|
|
11824
11868
|
return;
|
|
11825
11869
|
}
|
|
@@ -11925,14 +11969,14 @@ data: ${JSON.stringify(item.data)}
|
|
|
11925
11969
|
server.on("error", (e) => {
|
|
11926
11970
|
if (e.code === "EADDRINUSE") {
|
|
11927
11971
|
try {
|
|
11928
|
-
if (
|
|
11929
|
-
const { pid } = JSON.parse(
|
|
11972
|
+
if (fs22.existsSync(DAEMON_PID_FILE)) {
|
|
11973
|
+
const { pid } = JSON.parse(fs22.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
11930
11974
|
process.kill(pid, 0);
|
|
11931
11975
|
return process.exit(0);
|
|
11932
11976
|
}
|
|
11933
11977
|
} catch {
|
|
11934
11978
|
try {
|
|
11935
|
-
|
|
11979
|
+
fs22.unlinkSync(DAEMON_PID_FILE);
|
|
11936
11980
|
} catch {
|
|
11937
11981
|
}
|
|
11938
11982
|
server.listen(DAEMON_PORT, DAEMON_HOST);
|
|
@@ -12011,15 +12055,15 @@ var init_server = __esm({
|
|
|
12011
12055
|
});
|
|
12012
12056
|
|
|
12013
12057
|
// src/daemon/service.ts
|
|
12014
|
-
import
|
|
12058
|
+
import fs23 from "fs";
|
|
12015
12059
|
import path26 from "path";
|
|
12016
12060
|
import os20 from "os";
|
|
12017
12061
|
import { spawnSync as spawnSync3, execFileSync } from "child_process";
|
|
12018
12062
|
function resolveNode9Binary() {
|
|
12019
12063
|
try {
|
|
12020
12064
|
const script = process.argv[1];
|
|
12021
|
-
if (typeof script === "string" && path26.isAbsolute(script) &&
|
|
12022
|
-
return
|
|
12065
|
+
if (typeof script === "string" && path26.isAbsolute(script) && fs23.existsSync(script)) {
|
|
12066
|
+
return fs23.realpathSync(script);
|
|
12023
12067
|
}
|
|
12024
12068
|
} catch {
|
|
12025
12069
|
}
|
|
@@ -12077,8 +12121,8 @@ function launchdPlist(binaryPath) {
|
|
|
12077
12121
|
}
|
|
12078
12122
|
function installLaunchd(binaryPath) {
|
|
12079
12123
|
const dir = path26.dirname(LAUNCHD_PLIST);
|
|
12080
|
-
if (!
|
|
12081
|
-
|
|
12124
|
+
if (!fs23.existsSync(dir)) fs23.mkdirSync(dir, { recursive: true });
|
|
12125
|
+
fs23.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
|
|
12082
12126
|
spawnSync3("launchctl", ["unload", LAUNCHD_PLIST], { encoding: "utf8" });
|
|
12083
12127
|
const r = spawnSync3("launchctl", ["load", "-w", LAUNCHD_PLIST], {
|
|
12084
12128
|
encoding: "utf8",
|
|
@@ -12089,13 +12133,13 @@ function installLaunchd(binaryPath) {
|
|
|
12089
12133
|
}
|
|
12090
12134
|
}
|
|
12091
12135
|
function uninstallLaunchd() {
|
|
12092
|
-
if (
|
|
12136
|
+
if (fs23.existsSync(LAUNCHD_PLIST)) {
|
|
12093
12137
|
spawnSync3("launchctl", ["unload", "-w", LAUNCHD_PLIST], { encoding: "utf8", timeout: 5e3 });
|
|
12094
|
-
|
|
12138
|
+
fs23.unlinkSync(LAUNCHD_PLIST);
|
|
12095
12139
|
}
|
|
12096
12140
|
}
|
|
12097
12141
|
function isLaunchdInstalled() {
|
|
12098
|
-
return
|
|
12142
|
+
return fs23.existsSync(LAUNCHD_PLIST);
|
|
12099
12143
|
}
|
|
12100
12144
|
function systemdUnit(binaryPath) {
|
|
12101
12145
|
return `[Unit]
|
|
@@ -12115,10 +12159,10 @@ WantedBy=default.target
|
|
|
12115
12159
|
`;
|
|
12116
12160
|
}
|
|
12117
12161
|
function installSystemd(binaryPath) {
|
|
12118
|
-
if (!
|
|
12119
|
-
|
|
12162
|
+
if (!fs23.existsSync(SYSTEMD_UNIT_DIR)) {
|
|
12163
|
+
fs23.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
|
|
12120
12164
|
}
|
|
12121
|
-
|
|
12165
|
+
fs23.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
|
|
12122
12166
|
try {
|
|
12123
12167
|
execFileSync("loginctl", ["enable-linger", os20.userInfo().username], { timeout: 3e3 });
|
|
12124
12168
|
} catch {
|
|
@@ -12140,23 +12184,23 @@ function installSystemd(binaryPath) {
|
|
|
12140
12184
|
}
|
|
12141
12185
|
}
|
|
12142
12186
|
function uninstallSystemd() {
|
|
12143
|
-
if (
|
|
12187
|
+
if (fs23.existsSync(SYSTEMD_UNIT)) {
|
|
12144
12188
|
spawnSync3("systemctl", ["--user", "disable", "--now", "node9-daemon"], {
|
|
12145
12189
|
encoding: "utf8",
|
|
12146
12190
|
timeout: 5e3
|
|
12147
12191
|
});
|
|
12148
12192
|
spawnSync3("systemctl", ["--user", "daemon-reload"], { encoding: "utf8", timeout: 5e3 });
|
|
12149
|
-
|
|
12193
|
+
fs23.unlinkSync(SYSTEMD_UNIT);
|
|
12150
12194
|
}
|
|
12151
12195
|
}
|
|
12152
12196
|
function isSystemdInstalled() {
|
|
12153
|
-
return
|
|
12197
|
+
return fs23.existsSync(SYSTEMD_UNIT);
|
|
12154
12198
|
}
|
|
12155
12199
|
function stopRunningDaemon() {
|
|
12156
12200
|
const pidFile = path26.join(os20.homedir(), ".node9", "daemon.pid");
|
|
12157
|
-
if (!
|
|
12201
|
+
if (!fs23.existsSync(pidFile)) return;
|
|
12158
12202
|
try {
|
|
12159
|
-
const data = JSON.parse(
|
|
12203
|
+
const data = JSON.parse(fs23.readFileSync(pidFile, "utf-8"));
|
|
12160
12204
|
const pid = data.pid;
|
|
12161
12205
|
const MAX_PID2 = 4194304;
|
|
12162
12206
|
if (typeof pid === "number" && Number.isInteger(pid) && pid > 0 && pid <= MAX_PID2) {
|
|
@@ -12176,7 +12220,7 @@ function stopRunningDaemon() {
|
|
|
12176
12220
|
}
|
|
12177
12221
|
}
|
|
12178
12222
|
try {
|
|
12179
|
-
|
|
12223
|
+
fs23.unlinkSync(pidFile);
|
|
12180
12224
|
} catch {
|
|
12181
12225
|
}
|
|
12182
12226
|
} catch {
|
|
@@ -12258,13 +12302,13 @@ var init_service = __esm({
|
|
|
12258
12302
|
});
|
|
12259
12303
|
|
|
12260
12304
|
// src/daemon/index.ts
|
|
12261
|
-
import
|
|
12305
|
+
import fs24 from "fs";
|
|
12262
12306
|
import chalk4 from "chalk";
|
|
12263
12307
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
12264
12308
|
function stopDaemon() {
|
|
12265
|
-
if (!
|
|
12309
|
+
if (!fs24.existsSync(DAEMON_PID_FILE)) return console.log(chalk4.yellow("Not running."));
|
|
12266
12310
|
try {
|
|
12267
|
-
const data = JSON.parse(
|
|
12311
|
+
const data = JSON.parse(fs24.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
12268
12312
|
const pid = data.pid;
|
|
12269
12313
|
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
|
|
12270
12314
|
console.log(chalk4.gray("Cleaned up invalid PID file."));
|
|
@@ -12276,7 +12320,7 @@ function stopDaemon() {
|
|
|
12276
12320
|
console.log(chalk4.gray("Cleaned up stale PID file."));
|
|
12277
12321
|
} finally {
|
|
12278
12322
|
try {
|
|
12279
|
-
|
|
12323
|
+
fs24.unlinkSync(DAEMON_PID_FILE);
|
|
12280
12324
|
} catch {
|
|
12281
12325
|
}
|
|
12282
12326
|
}
|
|
@@ -12285,9 +12329,9 @@ function daemonStatus() {
|
|
|
12285
12329
|
const serviceInstalled = isDaemonServiceInstalled();
|
|
12286
12330
|
const serviceLabel = serviceInstalled ? chalk4.green("installed (starts on login)") : chalk4.yellow("not installed \u2014 run: node9 daemon install");
|
|
12287
12331
|
let processStatus;
|
|
12288
|
-
if (
|
|
12332
|
+
if (fs24.existsSync(DAEMON_PID_FILE)) {
|
|
12289
12333
|
try {
|
|
12290
|
-
const data = JSON.parse(
|
|
12334
|
+
const data = JSON.parse(fs24.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
12291
12335
|
const pid = data.pid;
|
|
12292
12336
|
const port = data.port;
|
|
12293
12337
|
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
|
|
@@ -12337,7 +12381,7 @@ __export(tail_exports, {
|
|
|
12337
12381
|
});
|
|
12338
12382
|
import http2 from "http";
|
|
12339
12383
|
import chalk25 from "chalk";
|
|
12340
|
-
import
|
|
12384
|
+
import fs39 from "fs";
|
|
12341
12385
|
import os35 from "os";
|
|
12342
12386
|
import path42 from "path";
|
|
12343
12387
|
import readline5 from "readline";
|
|
@@ -12358,19 +12402,19 @@ function getModelContextLimit(model) {
|
|
|
12358
12402
|
}
|
|
12359
12403
|
function readSessionUsage() {
|
|
12360
12404
|
const projectsDir = path42.join(os35.homedir(), ".claude", "projects");
|
|
12361
|
-
if (!
|
|
12405
|
+
if (!fs39.existsSync(projectsDir)) return null;
|
|
12362
12406
|
let latestFile = null;
|
|
12363
12407
|
let latestMtime = 0;
|
|
12364
12408
|
try {
|
|
12365
|
-
for (const dir of
|
|
12409
|
+
for (const dir of fs39.readdirSync(projectsDir)) {
|
|
12366
12410
|
const dirPath = path42.join(projectsDir, dir);
|
|
12367
12411
|
try {
|
|
12368
|
-
if (!
|
|
12369
|
-
for (const file of
|
|
12412
|
+
if (!fs39.statSync(dirPath).isDirectory()) continue;
|
|
12413
|
+
for (const file of fs39.readdirSync(dirPath)) {
|
|
12370
12414
|
if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
|
|
12371
12415
|
const filePath = path42.join(dirPath, file);
|
|
12372
12416
|
try {
|
|
12373
|
-
const mtime =
|
|
12417
|
+
const mtime = fs39.statSync(filePath).mtimeMs;
|
|
12374
12418
|
if (mtime > latestMtime) {
|
|
12375
12419
|
latestMtime = mtime;
|
|
12376
12420
|
latestFile = filePath;
|
|
@@ -12385,7 +12429,7 @@ function readSessionUsage() {
|
|
|
12385
12429
|
}
|
|
12386
12430
|
if (!latestFile) return null;
|
|
12387
12431
|
try {
|
|
12388
|
-
const lines =
|
|
12432
|
+
const lines = fs39.readFileSync(latestFile, "utf-8").split("\n");
|
|
12389
12433
|
let lastModel = "";
|
|
12390
12434
|
let lastInput = 0;
|
|
12391
12435
|
let lastOutput = 0;
|
|
@@ -12474,9 +12518,9 @@ function renderPending(activity) {
|
|
|
12474
12518
|
}
|
|
12475
12519
|
async function ensureDaemon() {
|
|
12476
12520
|
let pidPort = null;
|
|
12477
|
-
if (
|
|
12521
|
+
if (fs39.existsSync(PID_FILE)) {
|
|
12478
12522
|
try {
|
|
12479
|
-
const { port } = JSON.parse(
|
|
12523
|
+
const { port } = JSON.parse(fs39.readFileSync(PID_FILE, "utf-8"));
|
|
12480
12524
|
pidPort = port;
|
|
12481
12525
|
} catch {
|
|
12482
12526
|
console.error(chalk25.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
|
|
@@ -12631,7 +12675,7 @@ function buildRecoveryCardLines(req) {
|
|
|
12631
12675
|
function readApproversFromDisk() {
|
|
12632
12676
|
const configPath = path42.join(os35.homedir(), ".node9", "config.json");
|
|
12633
12677
|
try {
|
|
12634
|
-
const raw = JSON.parse(
|
|
12678
|
+
const raw = JSON.parse(fs39.readFileSync(configPath, "utf-8"));
|
|
12635
12679
|
const settings = raw.settings ?? {};
|
|
12636
12680
|
return settings.approvers ?? {};
|
|
12637
12681
|
} catch {
|
|
@@ -12649,13 +12693,13 @@ function approverStatusLine() {
|
|
|
12649
12693
|
function toggleApprover(channel) {
|
|
12650
12694
|
const configPath = path42.join(os35.homedir(), ".node9", "config.json");
|
|
12651
12695
|
try {
|
|
12652
|
-
const raw = JSON.parse(
|
|
12696
|
+
const raw = JSON.parse(fs39.readFileSync(configPath, "utf-8"));
|
|
12653
12697
|
const settings = raw.settings ?? {};
|
|
12654
12698
|
const approvers = settings.approvers ?? {};
|
|
12655
12699
|
approvers[channel] = approvers[channel] === false;
|
|
12656
12700
|
settings.approvers = approvers;
|
|
12657
12701
|
raw.settings = settings;
|
|
12658
|
-
|
|
12702
|
+
fs39.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
|
|
12659
12703
|
} catch (err2) {
|
|
12660
12704
|
process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
|
|
12661
12705
|
`);
|
|
@@ -12825,7 +12869,7 @@ async function startTail(options = {}) {
|
|
|
12825
12869
|
}
|
|
12826
12870
|
postDecisionHttp(req2.id, httpDecision, csrfToken, port, httpOpts).catch((err2) => {
|
|
12827
12871
|
try {
|
|
12828
|
-
|
|
12872
|
+
fs39.appendFileSync(
|
|
12829
12873
|
path42.join(os35.homedir(), ".node9", "hook-debug.log"),
|
|
12830
12874
|
`[tail] POST /decision failed: ${String(err2)}
|
|
12831
12875
|
`
|
|
@@ -12909,7 +12953,7 @@ async function startTail(options = {}) {
|
|
|
12909
12953
|
}
|
|
12910
12954
|
const auditLog = path42.join(os35.homedir(), ".node9", "audit.log");
|
|
12911
12955
|
try {
|
|
12912
|
-
const unackedDlp =
|
|
12956
|
+
const unackedDlp = fs39.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
|
|
12913
12957
|
if (unackedDlp > 0) {
|
|
12914
12958
|
console.log("");
|
|
12915
12959
|
console.log(
|
|
@@ -13149,7 +13193,7 @@ __export(hud_exports, {
|
|
|
13149
13193
|
main: () => main,
|
|
13150
13194
|
renderEnvironmentLine: () => renderEnvironmentLine
|
|
13151
13195
|
});
|
|
13152
|
-
import
|
|
13196
|
+
import fs40 from "fs";
|
|
13153
13197
|
import path43 from "path";
|
|
13154
13198
|
import os36 from "os";
|
|
13155
13199
|
import http3 from "http";
|
|
@@ -13227,9 +13271,9 @@ function formatTimeLeft(resetsAt) {
|
|
|
13227
13271
|
return ` (${m}m left)`;
|
|
13228
13272
|
}
|
|
13229
13273
|
function safeReadJson(filePath) {
|
|
13230
|
-
if (!
|
|
13274
|
+
if (!fs40.existsSync(filePath)) return null;
|
|
13231
13275
|
try {
|
|
13232
|
-
return JSON.parse(
|
|
13276
|
+
return JSON.parse(fs40.readFileSync(filePath, "utf-8"));
|
|
13233
13277
|
} catch {
|
|
13234
13278
|
return null;
|
|
13235
13279
|
}
|
|
@@ -13250,10 +13294,10 @@ function countHooksInFile(filePath) {
|
|
|
13250
13294
|
return Object.keys(cfg.hooks).length;
|
|
13251
13295
|
}
|
|
13252
13296
|
function countRulesInDir(rulesDir) {
|
|
13253
|
-
if (!
|
|
13297
|
+
if (!fs40.existsSync(rulesDir)) return 0;
|
|
13254
13298
|
let count = 0;
|
|
13255
13299
|
try {
|
|
13256
|
-
for (const entry of
|
|
13300
|
+
for (const entry of fs40.readdirSync(rulesDir, { withFileTypes: true })) {
|
|
13257
13301
|
if (entry.isDirectory()) {
|
|
13258
13302
|
count += countRulesInDir(path43.join(rulesDir, entry.name));
|
|
13259
13303
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -13279,7 +13323,7 @@ function countConfigs(cwd) {
|
|
|
13279
13323
|
let hooksCount = 0;
|
|
13280
13324
|
const userMcpServers = /* @__PURE__ */ new Set();
|
|
13281
13325
|
const projectMcpServers = /* @__PURE__ */ new Set();
|
|
13282
|
-
if (
|
|
13326
|
+
if (fs40.existsSync(path43.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
13283
13327
|
rulesCount += countRulesInDir(path43.join(claudeDir, "rules"));
|
|
13284
13328
|
const userSettings = path43.join(claudeDir, "settings.json");
|
|
13285
13329
|
for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
|
|
@@ -13290,18 +13334,18 @@ function countConfigs(cwd) {
|
|
|
13290
13334
|
userMcpServers.delete(name);
|
|
13291
13335
|
}
|
|
13292
13336
|
if (cwd) {
|
|
13293
|
-
if (
|
|
13294
|
-
if (
|
|
13337
|
+
if (fs40.existsSync(path43.join(cwd, "CLAUDE.md"))) claudeMdCount++;
|
|
13338
|
+
if (fs40.existsSync(path43.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
|
|
13295
13339
|
const projectClaudeDir = path43.join(cwd, ".claude");
|
|
13296
13340
|
const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
|
|
13297
13341
|
if (!overlapsUserScope) {
|
|
13298
|
-
if (
|
|
13342
|
+
if (fs40.existsSync(path43.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
13299
13343
|
rulesCount += countRulesInDir(path43.join(projectClaudeDir, "rules"));
|
|
13300
13344
|
const projSettings = path43.join(projectClaudeDir, "settings.json");
|
|
13301
13345
|
for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
|
|
13302
13346
|
hooksCount += countHooksInFile(projSettings);
|
|
13303
13347
|
}
|
|
13304
|
-
if (
|
|
13348
|
+
if (fs40.existsSync(path43.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
|
|
13305
13349
|
const localSettings = path43.join(projectClaudeDir, "settings.local.json");
|
|
13306
13350
|
for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
|
|
13307
13351
|
hooksCount += countHooksInFile(localSettings);
|
|
@@ -13339,11 +13383,11 @@ function readActiveShieldsHud() {
|
|
|
13339
13383
|
}
|
|
13340
13384
|
try {
|
|
13341
13385
|
const shieldsPath = path43.join(os36.homedir(), ".node9", "shields.json");
|
|
13342
|
-
if (!
|
|
13386
|
+
if (!fs40.existsSync(shieldsPath)) {
|
|
13343
13387
|
shieldsCache = { value: [], ts: now };
|
|
13344
13388
|
return [];
|
|
13345
13389
|
}
|
|
13346
|
-
const parsed = JSON.parse(
|
|
13390
|
+
const parsed = JSON.parse(fs40.readFileSync(shieldsPath, "utf-8"));
|
|
13347
13391
|
if (!Array.isArray(parsed.active)) {
|
|
13348
13392
|
shieldsCache = { value: [], ts: now };
|
|
13349
13393
|
return [];
|
|
@@ -13445,17 +13489,17 @@ function renderContextLine(stdin) {
|
|
|
13445
13489
|
async function main() {
|
|
13446
13490
|
try {
|
|
13447
13491
|
const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
|
|
13448
|
-
if (
|
|
13492
|
+
if (fs40.existsSync(path43.join(os36.homedir(), ".node9", "hud-debug"))) {
|
|
13449
13493
|
try {
|
|
13450
13494
|
const logPath = path43.join(os36.homedir(), ".node9", "hud-debug.log");
|
|
13451
13495
|
const MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
13452
13496
|
let size = 0;
|
|
13453
13497
|
try {
|
|
13454
|
-
size =
|
|
13498
|
+
size = fs40.statSync(logPath).size;
|
|
13455
13499
|
} catch {
|
|
13456
13500
|
}
|
|
13457
13501
|
if (size < MAX_LOG_SIZE) {
|
|
13458
|
-
|
|
13502
|
+
fs40.appendFileSync(
|
|
13459
13503
|
logPath,
|
|
13460
13504
|
JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
|
|
13461
13505
|
);
|
|
@@ -13479,8 +13523,8 @@ async function main() {
|
|
|
13479
13523
|
path43.join(cwd, "node9.config.json"),
|
|
13480
13524
|
path43.join(os36.homedir(), ".node9", "config.json")
|
|
13481
13525
|
]) {
|
|
13482
|
-
if (!
|
|
13483
|
-
const cfg = JSON.parse(
|
|
13526
|
+
if (!fs40.existsSync(configPath)) continue;
|
|
13527
|
+
const cfg = JSON.parse(fs40.readFileSync(configPath, "utf-8"));
|
|
13484
13528
|
const hud = cfg.settings?.hud;
|
|
13485
13529
|
if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
|
|
13486
13530
|
}
|
|
@@ -14548,7 +14592,7 @@ function getAgentsStatus(homeDir2 = os11.homedir()) {
|
|
|
14548
14592
|
// src/cli.ts
|
|
14549
14593
|
init_daemon2();
|
|
14550
14594
|
import chalk26 from "chalk";
|
|
14551
|
-
import
|
|
14595
|
+
import fs41 from "fs";
|
|
14552
14596
|
import path44 from "path";
|
|
14553
14597
|
import os37 from "os";
|
|
14554
14598
|
import { confirm as confirm2 } from "@inquirer/prompts";
|
|
@@ -14739,7 +14783,7 @@ init_daemon();
|
|
|
14739
14783
|
init_config();
|
|
14740
14784
|
init_policy();
|
|
14741
14785
|
import chalk6 from "chalk";
|
|
14742
|
-
import
|
|
14786
|
+
import fs27 from "fs";
|
|
14743
14787
|
import { spawn as spawn6 } from "child_process";
|
|
14744
14788
|
import path29 from "path";
|
|
14745
14789
|
import os23 from "os";
|
|
@@ -14747,7 +14791,7 @@ import os23 from "os";
|
|
|
14747
14791
|
// src/undo.ts
|
|
14748
14792
|
import { spawnSync as spawnSync5, spawn as spawn5 } from "child_process";
|
|
14749
14793
|
import crypto3 from "crypto";
|
|
14750
|
-
import
|
|
14794
|
+
import fs25 from "fs";
|
|
14751
14795
|
import net3 from "net";
|
|
14752
14796
|
import path27 from "path";
|
|
14753
14797
|
import os21 from "os";
|
|
@@ -14777,16 +14821,16 @@ var MAX_SNAPSHOTS = 10;
|
|
|
14777
14821
|
var GIT_TIMEOUT = 15e3;
|
|
14778
14822
|
function readStack() {
|
|
14779
14823
|
try {
|
|
14780
|
-
if (
|
|
14781
|
-
return JSON.parse(
|
|
14824
|
+
if (fs25.existsSync(SNAPSHOT_STACK_PATH))
|
|
14825
|
+
return JSON.parse(fs25.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
|
|
14782
14826
|
} catch {
|
|
14783
14827
|
}
|
|
14784
14828
|
return [];
|
|
14785
14829
|
}
|
|
14786
14830
|
function writeStack(stack) {
|
|
14787
14831
|
const dir = path27.dirname(SNAPSHOT_STACK_PATH);
|
|
14788
|
-
if (!
|
|
14789
|
-
|
|
14832
|
+
if (!fs25.existsSync(dir)) fs25.mkdirSync(dir, { recursive: true });
|
|
14833
|
+
fs25.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
|
|
14790
14834
|
}
|
|
14791
14835
|
function extractFilePath(args) {
|
|
14792
14836
|
if (!args || typeof args !== "object") return null;
|
|
@@ -14808,7 +14852,7 @@ function buildArgsSummary(tool, args) {
|
|
|
14808
14852
|
function findProjectRoot(filePath) {
|
|
14809
14853
|
let dir = path27.dirname(filePath);
|
|
14810
14854
|
while (true) {
|
|
14811
|
-
if (
|
|
14855
|
+
if (fs25.existsSync(path27.join(dir, ".git")) || fs25.existsSync(path27.join(dir, "package.json"))) {
|
|
14812
14856
|
return dir;
|
|
14813
14857
|
}
|
|
14814
14858
|
const parent = path27.dirname(dir);
|
|
@@ -14819,7 +14863,7 @@ function findProjectRoot(filePath) {
|
|
|
14819
14863
|
function normalizeCwdForHash(cwd) {
|
|
14820
14864
|
let normalized;
|
|
14821
14865
|
try {
|
|
14822
|
-
normalized =
|
|
14866
|
+
normalized = fs25.realpathSync(cwd);
|
|
14823
14867
|
} catch {
|
|
14824
14868
|
normalized = cwd;
|
|
14825
14869
|
}
|
|
@@ -14834,11 +14878,11 @@ function getShadowRepoDir(cwd) {
|
|
|
14834
14878
|
function cleanOrphanedIndexFiles(shadowDir) {
|
|
14835
14879
|
try {
|
|
14836
14880
|
const cutoff = Date.now() - 6e4;
|
|
14837
|
-
for (const f of
|
|
14881
|
+
for (const f of fs25.readdirSync(shadowDir)) {
|
|
14838
14882
|
if (f.startsWith("index_")) {
|
|
14839
14883
|
const fp = path27.join(shadowDir, f);
|
|
14840
14884
|
try {
|
|
14841
|
-
if (
|
|
14885
|
+
if (fs25.statSync(fp).mtimeMs < cutoff) fs25.unlinkSync(fp);
|
|
14842
14886
|
} catch {
|
|
14843
14887
|
}
|
|
14844
14888
|
}
|
|
@@ -14850,7 +14894,7 @@ function writeShadowExcludes(shadowDir, ignorePaths) {
|
|
|
14850
14894
|
const hardcoded = [".git", ".node9"];
|
|
14851
14895
|
const lines = [...hardcoded, ...ignorePaths].join("\n");
|
|
14852
14896
|
try {
|
|
14853
|
-
|
|
14897
|
+
fs25.writeFileSync(path27.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
|
|
14854
14898
|
} catch {
|
|
14855
14899
|
}
|
|
14856
14900
|
}
|
|
@@ -14865,23 +14909,23 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
14865
14909
|
if (check.status === 0) {
|
|
14866
14910
|
const ptPath = path27.join(shadowDir, "project-path.txt");
|
|
14867
14911
|
try {
|
|
14868
|
-
const stored =
|
|
14912
|
+
const stored = fs25.readFileSync(ptPath, "utf8").trim();
|
|
14869
14913
|
if (stored === normalizedCwd) return true;
|
|
14870
14914
|
if (process.env.NODE9_DEBUG === "1")
|
|
14871
14915
|
console.error(
|
|
14872
14916
|
`[Node9] Shadow repo path mismatch: stored="${stored}" expected="${normalizedCwd}" \u2014 reinitializing`
|
|
14873
14917
|
);
|
|
14874
|
-
|
|
14918
|
+
fs25.rmSync(shadowDir, { recursive: true, force: true });
|
|
14875
14919
|
} catch {
|
|
14876
14920
|
try {
|
|
14877
|
-
|
|
14921
|
+
fs25.writeFileSync(ptPath, normalizedCwd, "utf8");
|
|
14878
14922
|
} catch {
|
|
14879
14923
|
}
|
|
14880
14924
|
return true;
|
|
14881
14925
|
}
|
|
14882
14926
|
}
|
|
14883
14927
|
try {
|
|
14884
|
-
|
|
14928
|
+
fs25.mkdirSync(shadowDir, { recursive: true });
|
|
14885
14929
|
} catch {
|
|
14886
14930
|
}
|
|
14887
14931
|
const init = spawnSync5("git", ["init", "--bare", shadowDir], { timeout: 5e3 });
|
|
@@ -14898,7 +14942,7 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
14898
14942
|
timeout: 3e3
|
|
14899
14943
|
});
|
|
14900
14944
|
try {
|
|
14901
|
-
|
|
14945
|
+
fs25.writeFileSync(path27.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
|
|
14902
14946
|
} catch {
|
|
14903
14947
|
}
|
|
14904
14948
|
return true;
|
|
@@ -14995,7 +15039,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
14995
15039
|
writeStack(stack);
|
|
14996
15040
|
const entry = stack[stack.length - 1];
|
|
14997
15041
|
notifySnapshotTaken(commitHash.slice(0, 7), tool, entry.argsSummary, capturedFiles.length);
|
|
14998
|
-
|
|
15042
|
+
fs25.writeFileSync(UNDO_LATEST_PATH, commitHash);
|
|
14999
15043
|
if (shouldGc) {
|
|
15000
15044
|
spawn5("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
|
|
15001
15045
|
}
|
|
@@ -15006,7 +15050,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
15006
15050
|
} finally {
|
|
15007
15051
|
if (indexFile) {
|
|
15008
15052
|
try {
|
|
15009
|
-
|
|
15053
|
+
fs25.unlinkSync(indexFile);
|
|
15010
15054
|
} catch {
|
|
15011
15055
|
}
|
|
15012
15056
|
}
|
|
@@ -15083,8 +15127,8 @@ function applyUndo(hash, cwd) {
|
|
|
15083
15127
|
}).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
|
|
15084
15128
|
for (const file of [...tracked, ...untracked]) {
|
|
15085
15129
|
const fullPath = path27.join(dir, file);
|
|
15086
|
-
if (!snapshotFiles.has(file) &&
|
|
15087
|
-
|
|
15130
|
+
if (!snapshotFiles.has(file) && fs25.existsSync(fullPath)) {
|
|
15131
|
+
fs25.unlinkSync(fullPath);
|
|
15088
15132
|
}
|
|
15089
15133
|
}
|
|
15090
15134
|
return true;
|
|
@@ -15097,7 +15141,7 @@ function applyUndo(hash, cwd) {
|
|
|
15097
15141
|
init_daemon_starter();
|
|
15098
15142
|
|
|
15099
15143
|
// src/skill-pin.ts
|
|
15100
|
-
import
|
|
15144
|
+
import fs26 from "fs";
|
|
15101
15145
|
import path28 from "path";
|
|
15102
15146
|
import os22 from "os";
|
|
15103
15147
|
import crypto4 from "crypto";
|
|
@@ -15116,7 +15160,7 @@ function walkDir(root) {
|
|
|
15116
15160
|
if (out.length >= MAX_FILES) return;
|
|
15117
15161
|
let entries;
|
|
15118
15162
|
try {
|
|
15119
|
-
entries =
|
|
15163
|
+
entries = fs26.readdirSync(dir, { withFileTypes: true });
|
|
15120
15164
|
} catch {
|
|
15121
15165
|
return;
|
|
15122
15166
|
}
|
|
@@ -15127,7 +15171,7 @@ function walkDir(root) {
|
|
|
15127
15171
|
const rel = relDir ? path28.posix.join(relDir, entry.name) : entry.name;
|
|
15128
15172
|
let lst;
|
|
15129
15173
|
try {
|
|
15130
|
-
lst =
|
|
15174
|
+
lst = fs26.lstatSync(full);
|
|
15131
15175
|
} catch {
|
|
15132
15176
|
continue;
|
|
15133
15177
|
}
|
|
@@ -15139,7 +15183,7 @@ function walkDir(root) {
|
|
|
15139
15183
|
if (!lst.isFile()) continue;
|
|
15140
15184
|
if (totalBytes + lst.size > MAX_TOTAL_BYTES) continue;
|
|
15141
15185
|
try {
|
|
15142
|
-
const buf =
|
|
15186
|
+
const buf = fs26.readFileSync(full);
|
|
15143
15187
|
totalBytes += buf.length;
|
|
15144
15188
|
out.push({ rel, hash: sha256Bytes(buf) });
|
|
15145
15189
|
} catch {
|
|
@@ -15153,14 +15197,14 @@ function walkDir(root) {
|
|
|
15153
15197
|
function hashSkillRoot(absPath) {
|
|
15154
15198
|
let lst;
|
|
15155
15199
|
try {
|
|
15156
|
-
lst =
|
|
15200
|
+
lst = fs26.lstatSync(absPath);
|
|
15157
15201
|
} catch {
|
|
15158
15202
|
return { exists: false, contentHash: "", fileCount: 0 };
|
|
15159
15203
|
}
|
|
15160
15204
|
if (lst.isSymbolicLink()) return { exists: false, contentHash: "", fileCount: 0 };
|
|
15161
15205
|
if (lst.isFile()) {
|
|
15162
15206
|
try {
|
|
15163
|
-
return { exists: true, contentHash: sha256Bytes(
|
|
15207
|
+
return { exists: true, contentHash: sha256Bytes(fs26.readFileSync(absPath)), fileCount: 1 };
|
|
15164
15208
|
} catch {
|
|
15165
15209
|
return { exists: false, contentHash: "", fileCount: 0 };
|
|
15166
15210
|
}
|
|
@@ -15178,7 +15222,7 @@ function getRootKey(absPath) {
|
|
|
15178
15222
|
function readSkillPinsSafe() {
|
|
15179
15223
|
const filePath = getPinsFilePath();
|
|
15180
15224
|
try {
|
|
15181
|
-
const raw =
|
|
15225
|
+
const raw = fs26.readFileSync(filePath, "utf-8");
|
|
15182
15226
|
if (!raw.trim()) return { ok: false, reason: "corrupt", detail: "empty file" };
|
|
15183
15227
|
const parsed = JSON.parse(raw);
|
|
15184
15228
|
if (!parsed.roots || typeof parsed.roots !== "object" || Array.isArray(parsed.roots)) {
|
|
@@ -15198,10 +15242,10 @@ function readSkillPins() {
|
|
|
15198
15242
|
}
|
|
15199
15243
|
function writeSkillPins(data) {
|
|
15200
15244
|
const filePath = getPinsFilePath();
|
|
15201
|
-
|
|
15245
|
+
fs26.mkdirSync(path28.dirname(filePath), { recursive: true });
|
|
15202
15246
|
const tmp = `${filePath}.${crypto4.randomBytes(6).toString("hex")}.tmp`;
|
|
15203
|
-
|
|
15204
|
-
|
|
15247
|
+
fs26.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
15248
|
+
fs26.renameSync(tmp, filePath);
|
|
15205
15249
|
}
|
|
15206
15250
|
function removePin(rootKey) {
|
|
15207
15251
|
const pins = readSkillPins();
|
|
@@ -15249,7 +15293,7 @@ function defaultSkillRoots(_cwd) {
|
|
|
15249
15293
|
const roots = [];
|
|
15250
15294
|
let registries;
|
|
15251
15295
|
try {
|
|
15252
|
-
registries =
|
|
15296
|
+
registries = fs26.readdirSync(marketplaces, { withFileTypes: true });
|
|
15253
15297
|
} catch {
|
|
15254
15298
|
return [];
|
|
15255
15299
|
}
|
|
@@ -15258,7 +15302,7 @@ function defaultSkillRoots(_cwd) {
|
|
|
15258
15302
|
const pluginsDir = path28.join(marketplaces, registry.name, "plugins");
|
|
15259
15303
|
let plugins;
|
|
15260
15304
|
try {
|
|
15261
|
-
plugins =
|
|
15305
|
+
plugins = fs26.readdirSync(pluginsDir, { withFileTypes: true });
|
|
15262
15306
|
} catch {
|
|
15263
15307
|
continue;
|
|
15264
15308
|
}
|
|
@@ -15294,7 +15338,7 @@ function registerCheckCommand(program2) {
|
|
|
15294
15338
|
if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
|
|
15295
15339
|
const logPath = path29.join(os23.homedir(), ".node9", "hook-debug.log");
|
|
15296
15340
|
const errMsg = err2 instanceof Error ? err2.message : String(err2);
|
|
15297
|
-
|
|
15341
|
+
fs27.appendFileSync(
|
|
15298
15342
|
logPath,
|
|
15299
15343
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
|
|
15300
15344
|
RAW: ${raw}
|
|
@@ -15309,8 +15353,8 @@ RAW: ${raw}
|
|
|
15309
15353
|
const scriptPath = process.argv[1];
|
|
15310
15354
|
if (typeof scriptPath !== "string" || !path29.isAbsolute(scriptPath))
|
|
15311
15355
|
throw new Error("node9: argv[1] is not an absolute path");
|
|
15312
|
-
const resolvedScript =
|
|
15313
|
-
const packageDist =
|
|
15356
|
+
const resolvedScript = fs27.realpathSync(scriptPath);
|
|
15357
|
+
const packageDist = fs27.realpathSync(path29.resolve(__dirname, "../.."));
|
|
15314
15358
|
if (!resolvedScript.startsWith(packageDist + path29.sep) && resolvedScript !== packageDist)
|
|
15315
15359
|
throw new Error(
|
|
15316
15360
|
`node9: daemon spawn aborted \u2014 argv[1] (${resolvedScript}) is outside package dist (${packageDist})`
|
|
@@ -15336,7 +15380,7 @@ RAW: ${raw}
|
|
|
15336
15380
|
const logPath = path29.join(os23.homedir(), ".node9", "hook-debug.log");
|
|
15337
15381
|
const msg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);
|
|
15338
15382
|
try {
|
|
15339
|
-
|
|
15383
|
+
fs27.appendFileSync(
|
|
15340
15384
|
logPath,
|
|
15341
15385
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-autostart-failed: ${msg}
|
|
15342
15386
|
`
|
|
@@ -15347,9 +15391,9 @@ RAW: ${raw}
|
|
|
15347
15391
|
}
|
|
15348
15392
|
if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
|
|
15349
15393
|
const logPath = path29.join(os23.homedir(), ".node9", "hook-debug.log");
|
|
15350
|
-
if (!
|
|
15351
|
-
|
|
15352
|
-
|
|
15394
|
+
if (!fs27.existsSync(path29.dirname(logPath)))
|
|
15395
|
+
fs27.mkdirSync(path29.dirname(logPath), { recursive: true });
|
|
15396
|
+
fs27.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
|
|
15353
15397
|
`);
|
|
15354
15398
|
}
|
|
15355
15399
|
const toolName = sanitize2(payload.tool_name ?? payload.name ?? "");
|
|
@@ -15362,8 +15406,8 @@ RAW: ${raw}
|
|
|
15362
15406
|
const isHumanDecision = blockedByContext.toLowerCase().includes("user") || blockedByContext.toLowerCase().includes("daemon") || blockedByContext.toLowerCase().includes("decision");
|
|
15363
15407
|
let ttyFd = null;
|
|
15364
15408
|
try {
|
|
15365
|
-
ttyFd =
|
|
15366
|
-
const writeTty = (line) =>
|
|
15409
|
+
ttyFd = fs27.openSync("/dev/tty", "w");
|
|
15410
|
+
const writeTty = (line) => fs27.writeSync(ttyFd, line + "\n");
|
|
15367
15411
|
if (blockedByContext.includes("DLP") || blockedByContext.includes("Secret Detected") || blockedByContext.includes("Credential Review")) {
|
|
15368
15412
|
writeTty(chalk6.bgRed.white.bold(`
|
|
15369
15413
|
\u{1F6A8} NODE9 DLP ALERT \u2014 CREDENTIAL DETECTED `));
|
|
@@ -15382,7 +15426,7 @@ RAW: ${raw}
|
|
|
15382
15426
|
} finally {
|
|
15383
15427
|
if (ttyFd !== null)
|
|
15384
15428
|
try {
|
|
15385
|
-
|
|
15429
|
+
fs27.closeSync(ttyFd);
|
|
15386
15430
|
} catch {
|
|
15387
15431
|
}
|
|
15388
15432
|
}
|
|
@@ -15420,13 +15464,13 @@ RAW: ${raw}
|
|
|
15420
15464
|
const flagPath = path29.join(sessionsDir, `${safeSessionId}.json`);
|
|
15421
15465
|
let flag = null;
|
|
15422
15466
|
try {
|
|
15423
|
-
flag = JSON.parse(
|
|
15467
|
+
flag = JSON.parse(fs27.readFileSync(flagPath, "utf-8"));
|
|
15424
15468
|
} catch {
|
|
15425
15469
|
}
|
|
15426
15470
|
const writeFlag = (data2) => {
|
|
15427
15471
|
try {
|
|
15428
|
-
|
|
15429
|
-
|
|
15472
|
+
fs27.mkdirSync(sessionsDir, { recursive: true });
|
|
15473
|
+
fs27.writeFileSync(
|
|
15430
15474
|
flagPath,
|
|
15431
15475
|
JSON.stringify({ ...data2, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
|
|
15432
15476
|
{ mode: 384 }
|
|
@@ -15437,8 +15481,8 @@ RAW: ${raw}
|
|
|
15437
15481
|
const sendSkillWarn = (detail, recoveryCmd) => {
|
|
15438
15482
|
let ttyFd = null;
|
|
15439
15483
|
try {
|
|
15440
|
-
ttyFd =
|
|
15441
|
-
const w = (line) =>
|
|
15484
|
+
ttyFd = fs27.openSync("/dev/tty", "w");
|
|
15485
|
+
const w = (line) => fs27.writeSync(ttyFd, line + "\n");
|
|
15442
15486
|
w(chalk6.yellow(`
|
|
15443
15487
|
\u26A0\uFE0F Node9: installed skill drift detected`));
|
|
15444
15488
|
w(chalk6.gray(` ${detail}`));
|
|
@@ -15453,7 +15497,7 @@ RAW: ${raw}
|
|
|
15453
15497
|
} finally {
|
|
15454
15498
|
if (ttyFd !== null)
|
|
15455
15499
|
try {
|
|
15456
|
-
|
|
15500
|
+
fs27.closeSync(ttyFd);
|
|
15457
15501
|
} catch {
|
|
15458
15502
|
}
|
|
15459
15503
|
}
|
|
@@ -15510,10 +15554,10 @@ RAW: ${raw}
|
|
|
15510
15554
|
}
|
|
15511
15555
|
try {
|
|
15512
15556
|
const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1e3;
|
|
15513
|
-
for (const name of
|
|
15557
|
+
for (const name of fs27.readdirSync(sessionsDir)) {
|
|
15514
15558
|
const p = path29.join(sessionsDir, name);
|
|
15515
15559
|
try {
|
|
15516
|
-
if (
|
|
15560
|
+
if (fs27.statSync(p).mtimeMs < cutoff) fs27.unlinkSync(p);
|
|
15517
15561
|
} catch {
|
|
15518
15562
|
}
|
|
15519
15563
|
}
|
|
@@ -15525,7 +15569,7 @@ RAW: ${raw}
|
|
|
15525
15569
|
try {
|
|
15526
15570
|
const dbg = path29.join(os23.homedir(), ".node9", "hook-debug.log");
|
|
15527
15571
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
15528
|
-
|
|
15572
|
+
fs27.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
|
|
15529
15573
|
`);
|
|
15530
15574
|
} catch {
|
|
15531
15575
|
}
|
|
@@ -15547,12 +15591,12 @@ RAW: ${raw}
|
|
|
15547
15591
|
}
|
|
15548
15592
|
if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && !process.stdout.isTTY && config.settings.autoStartDaemon) {
|
|
15549
15593
|
try {
|
|
15550
|
-
const tty =
|
|
15551
|
-
|
|
15594
|
+
const tty = fs27.openSync("/dev/tty", "w");
|
|
15595
|
+
fs27.writeSync(
|
|
15552
15596
|
tty,
|
|
15553
15597
|
chalk6.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically...\n")
|
|
15554
15598
|
);
|
|
15555
|
-
|
|
15599
|
+
fs27.closeSync(tty);
|
|
15556
15600
|
} catch {
|
|
15557
15601
|
}
|
|
15558
15602
|
const daemonReady = await autoStartDaemonAndWait();
|
|
@@ -15581,7 +15625,7 @@ RAW: ${raw}
|
|
|
15581
15625
|
if (process.env.NODE9_DEBUG === "1") {
|
|
15582
15626
|
const logPath = path29.join(os23.homedir(), ".node9", "hook-debug.log");
|
|
15583
15627
|
const errMsg = err2 instanceof Error ? err2.message : String(err2);
|
|
15584
|
-
|
|
15628
|
+
fs27.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
|
|
15585
15629
|
`);
|
|
15586
15630
|
}
|
|
15587
15631
|
process.exit(0);
|
|
@@ -15617,7 +15661,7 @@ RAW: ${raw}
|
|
|
15617
15661
|
// src/cli/commands/log.ts
|
|
15618
15662
|
init_audit();
|
|
15619
15663
|
init_config();
|
|
15620
|
-
import
|
|
15664
|
+
import fs28 from "fs";
|
|
15621
15665
|
import path30 from "path";
|
|
15622
15666
|
import os24 from "os";
|
|
15623
15667
|
init_daemon();
|
|
@@ -15693,9 +15737,9 @@ function registerLogCommand(program2) {
|
|
|
15693
15737
|
source: "post-hook"
|
|
15694
15738
|
};
|
|
15695
15739
|
const logPath = path30.join(os24.homedir(), ".node9", "audit.log");
|
|
15696
|
-
if (!
|
|
15697
|
-
|
|
15698
|
-
|
|
15740
|
+
if (!fs28.existsSync(path30.dirname(logPath)))
|
|
15741
|
+
fs28.mkdirSync(path30.dirname(logPath), { recursive: true });
|
|
15742
|
+
fs28.appendFileSync(logPath, JSON.stringify(entry) + "\n");
|
|
15699
15743
|
if ((tool === "Bash" || tool === "bash") && isDaemonRunning()) {
|
|
15700
15744
|
const command = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
|
|
15701
15745
|
if (command) {
|
|
@@ -15751,7 +15795,7 @@ function registerLogCommand(program2) {
|
|
|
15751
15795
|
`);
|
|
15752
15796
|
const debugPath = path30.join(os24.homedir(), ".node9", "hook-debug.log");
|
|
15753
15797
|
try {
|
|
15754
|
-
|
|
15798
|
+
fs28.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
|
|
15755
15799
|
`);
|
|
15756
15800
|
} catch {
|
|
15757
15801
|
}
|
|
@@ -16152,7 +16196,7 @@ function registerConfigShowCommand(program2) {
|
|
|
16152
16196
|
// src/cli/commands/doctor.ts
|
|
16153
16197
|
init_daemon();
|
|
16154
16198
|
import chalk8 from "chalk";
|
|
16155
|
-
import
|
|
16199
|
+
import fs29 from "fs";
|
|
16156
16200
|
import path31 from "path";
|
|
16157
16201
|
import os25 from "os";
|
|
16158
16202
|
import { execSync as execSync2 } from "child_process";
|
|
@@ -16208,9 +16252,9 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16208
16252
|
}
|
|
16209
16253
|
section("Configuration");
|
|
16210
16254
|
const globalConfigPath = path31.join(homeDir2, ".node9", "config.json");
|
|
16211
|
-
if (
|
|
16255
|
+
if (fs29.existsSync(globalConfigPath)) {
|
|
16212
16256
|
try {
|
|
16213
|
-
JSON.parse(
|
|
16257
|
+
JSON.parse(fs29.readFileSync(globalConfigPath, "utf-8"));
|
|
16214
16258
|
pass("~/.node9/config.json found and valid");
|
|
16215
16259
|
} catch {
|
|
16216
16260
|
fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
|
|
@@ -16219,9 +16263,9 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16219
16263
|
warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
|
|
16220
16264
|
}
|
|
16221
16265
|
const projectConfigPath = path31.join(process.cwd(), "node9.config.json");
|
|
16222
|
-
if (
|
|
16266
|
+
if (fs29.existsSync(projectConfigPath)) {
|
|
16223
16267
|
try {
|
|
16224
|
-
JSON.parse(
|
|
16268
|
+
JSON.parse(fs29.readFileSync(projectConfigPath, "utf-8"));
|
|
16225
16269
|
pass("node9.config.json found and valid (project)");
|
|
16226
16270
|
} catch {
|
|
16227
16271
|
fail(
|
|
@@ -16231,7 +16275,7 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16231
16275
|
}
|
|
16232
16276
|
}
|
|
16233
16277
|
const credsPath = path31.join(homeDir2, ".node9", "credentials.json");
|
|
16234
|
-
if (
|
|
16278
|
+
if (fs29.existsSync(credsPath)) {
|
|
16235
16279
|
pass("Cloud credentials found (~/.node9/credentials.json)");
|
|
16236
16280
|
} else {
|
|
16237
16281
|
warn(
|
|
@@ -16241,9 +16285,9 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16241
16285
|
}
|
|
16242
16286
|
section("Agent Hooks");
|
|
16243
16287
|
const claudeSettingsPath = path31.join(homeDir2, ".claude", "settings.json");
|
|
16244
|
-
if (
|
|
16288
|
+
if (fs29.existsSync(claudeSettingsPath)) {
|
|
16245
16289
|
try {
|
|
16246
|
-
const cs = JSON.parse(
|
|
16290
|
+
const cs = JSON.parse(fs29.readFileSync(claudeSettingsPath, "utf-8"));
|
|
16247
16291
|
const hasHook = cs.hooks?.PreToolUse?.some(
|
|
16248
16292
|
(m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
|
|
16249
16293
|
);
|
|
@@ -16260,9 +16304,9 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16260
16304
|
warn("Claude Code \u2014 not configured", "Run: node9 setup claude");
|
|
16261
16305
|
}
|
|
16262
16306
|
const geminiSettingsPath = path31.join(homeDir2, ".gemini", "settings.json");
|
|
16263
|
-
if (
|
|
16307
|
+
if (fs29.existsSync(geminiSettingsPath)) {
|
|
16264
16308
|
try {
|
|
16265
|
-
const gs = JSON.parse(
|
|
16309
|
+
const gs = JSON.parse(fs29.readFileSync(geminiSettingsPath, "utf-8"));
|
|
16266
16310
|
const hasHook = gs.hooks?.BeforeTool?.some(
|
|
16267
16311
|
(m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
|
|
16268
16312
|
);
|
|
@@ -16279,9 +16323,9 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16279
16323
|
warn("Gemini CLI \u2014 not configured", "Run: node9 setup gemini (skip if not using Gemini)");
|
|
16280
16324
|
}
|
|
16281
16325
|
const cursorHooksPath = path31.join(homeDir2, ".cursor", "hooks.json");
|
|
16282
|
-
if (
|
|
16326
|
+
if (fs29.existsSync(cursorHooksPath)) {
|
|
16283
16327
|
try {
|
|
16284
|
-
const cur = JSON.parse(
|
|
16328
|
+
const cur = JSON.parse(fs29.readFileSync(cursorHooksPath, "utf-8"));
|
|
16285
16329
|
const hasHook = cur.hooks?.preToolUse?.some(
|
|
16286
16330
|
(h) => h.command?.includes("node9") || h.command?.includes("cli.js")
|
|
16287
16331
|
);
|
|
@@ -16319,7 +16363,7 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16319
16363
|
|
|
16320
16364
|
// src/cli/commands/audit.ts
|
|
16321
16365
|
import chalk9 from "chalk";
|
|
16322
|
-
import
|
|
16366
|
+
import fs30 from "fs";
|
|
16323
16367
|
import path32 from "path";
|
|
16324
16368
|
import os26 from "os";
|
|
16325
16369
|
function formatRelativeTime(timestamp) {
|
|
@@ -16335,13 +16379,13 @@ function formatRelativeTime(timestamp) {
|
|
|
16335
16379
|
function registerAuditCommand(program2) {
|
|
16336
16380
|
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) => {
|
|
16337
16381
|
const logPath = path32.join(os26.homedir(), ".node9", "audit.log");
|
|
16338
|
-
if (!
|
|
16382
|
+
if (!fs30.existsSync(logPath)) {
|
|
16339
16383
|
console.log(
|
|
16340
16384
|
chalk9.yellow("No audit logs found. Run node9 with an agent to generate entries.")
|
|
16341
16385
|
);
|
|
16342
16386
|
return;
|
|
16343
16387
|
}
|
|
16344
|
-
const raw =
|
|
16388
|
+
const raw = fs30.readFileSync(logPath, "utf-8");
|
|
16345
16389
|
const lines = raw.split("\n").filter((l) => l.trim() !== "");
|
|
16346
16390
|
let entries = lines.flatMap((line) => {
|
|
16347
16391
|
try {
|
|
@@ -16395,7 +16439,7 @@ function registerAuditCommand(program2) {
|
|
|
16395
16439
|
|
|
16396
16440
|
// src/cli/commands/report.ts
|
|
16397
16441
|
import chalk10 from "chalk";
|
|
16398
|
-
import
|
|
16442
|
+
import fs31 from "fs";
|
|
16399
16443
|
import path33 from "path";
|
|
16400
16444
|
import os27 from "os";
|
|
16401
16445
|
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;
|
|
@@ -16444,8 +16488,8 @@ function getDateRange(period) {
|
|
|
16444
16488
|
}
|
|
16445
16489
|
}
|
|
16446
16490
|
function parseAuditLog(logPath) {
|
|
16447
|
-
if (!
|
|
16448
|
-
const raw =
|
|
16491
|
+
if (!fs31.existsSync(logPath)) return [];
|
|
16492
|
+
const raw = fs31.readFileSync(logPath, "utf-8");
|
|
16449
16493
|
return raw.split("\n").flatMap((line) => {
|
|
16450
16494
|
if (!line.trim()) return [];
|
|
16451
16495
|
try {
|
|
@@ -16525,10 +16569,10 @@ function loadClaudeCost(start, end) {
|
|
|
16525
16569
|
cacheReadTokens: 0
|
|
16526
16570
|
};
|
|
16527
16571
|
const projectsDir = path33.join(os27.homedir(), ".claude", "projects");
|
|
16528
|
-
if (!
|
|
16572
|
+
if (!fs31.existsSync(projectsDir)) return empty;
|
|
16529
16573
|
let dirs;
|
|
16530
16574
|
try {
|
|
16531
|
-
dirs =
|
|
16575
|
+
dirs = fs31.readdirSync(projectsDir);
|
|
16532
16576
|
} catch {
|
|
16533
16577
|
return empty;
|
|
16534
16578
|
}
|
|
@@ -16543,15 +16587,15 @@ function loadClaudeCost(start, end) {
|
|
|
16543
16587
|
const projPath = path33.join(projectsDir, proj);
|
|
16544
16588
|
let files;
|
|
16545
16589
|
try {
|
|
16546
|
-
const stat =
|
|
16590
|
+
const stat = fs31.statSync(projPath);
|
|
16547
16591
|
if (!stat.isDirectory()) continue;
|
|
16548
|
-
files =
|
|
16592
|
+
files = fs31.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
16549
16593
|
} catch {
|
|
16550
16594
|
continue;
|
|
16551
16595
|
}
|
|
16552
16596
|
for (const file of files) {
|
|
16553
16597
|
try {
|
|
16554
|
-
const raw =
|
|
16598
|
+
const raw = fs31.readFileSync(path33.join(projPath, file), "utf-8");
|
|
16555
16599
|
for (const line of raw.split("\n")) {
|
|
16556
16600
|
if (!line.trim()) continue;
|
|
16557
16601
|
let entry;
|
|
@@ -16596,31 +16640,31 @@ function loadCodexCost(start, end) {
|
|
|
16596
16640
|
const byDay = /* @__PURE__ */ new Map();
|
|
16597
16641
|
let total = 0;
|
|
16598
16642
|
let toolCalls = 0;
|
|
16599
|
-
if (!
|
|
16643
|
+
if (!fs31.existsSync(sessionsBase)) return { total, byDay, toolCalls };
|
|
16600
16644
|
const jsonlFiles = [];
|
|
16601
16645
|
try {
|
|
16602
|
-
for (const year of
|
|
16646
|
+
for (const year of fs31.readdirSync(sessionsBase)) {
|
|
16603
16647
|
const yearPath = path33.join(sessionsBase, year);
|
|
16604
16648
|
try {
|
|
16605
|
-
if (!
|
|
16649
|
+
if (!fs31.statSync(yearPath).isDirectory()) continue;
|
|
16606
16650
|
} catch {
|
|
16607
16651
|
continue;
|
|
16608
16652
|
}
|
|
16609
|
-
for (const month of
|
|
16653
|
+
for (const month of fs31.readdirSync(yearPath)) {
|
|
16610
16654
|
const monthPath = path33.join(yearPath, month);
|
|
16611
16655
|
try {
|
|
16612
|
-
if (!
|
|
16656
|
+
if (!fs31.statSync(monthPath).isDirectory()) continue;
|
|
16613
16657
|
} catch {
|
|
16614
16658
|
continue;
|
|
16615
16659
|
}
|
|
16616
|
-
for (const day of
|
|
16660
|
+
for (const day of fs31.readdirSync(monthPath)) {
|
|
16617
16661
|
const dayPath = path33.join(monthPath, day);
|
|
16618
16662
|
try {
|
|
16619
|
-
if (!
|
|
16663
|
+
if (!fs31.statSync(dayPath).isDirectory()) continue;
|
|
16620
16664
|
} catch {
|
|
16621
16665
|
continue;
|
|
16622
16666
|
}
|
|
16623
|
-
for (const file of
|
|
16667
|
+
for (const file of fs31.readdirSync(dayPath)) {
|
|
16624
16668
|
if (file.endsWith(".jsonl")) jsonlFiles.push(path33.join(dayPath, file));
|
|
16625
16669
|
}
|
|
16626
16670
|
}
|
|
@@ -16632,7 +16676,7 @@ function loadCodexCost(start, end) {
|
|
|
16632
16676
|
for (const filePath of jsonlFiles) {
|
|
16633
16677
|
let lines;
|
|
16634
16678
|
try {
|
|
16635
|
-
lines =
|
|
16679
|
+
lines = fs31.readFileSync(filePath, "utf-8").split("\n");
|
|
16636
16680
|
} catch {
|
|
16637
16681
|
continue;
|
|
16638
16682
|
}
|
|
@@ -17189,12 +17233,12 @@ function registerDaemonCommand(program2) {
|
|
|
17189
17233
|
init_core();
|
|
17190
17234
|
init_daemon();
|
|
17191
17235
|
import chalk12 from "chalk";
|
|
17192
|
-
import
|
|
17236
|
+
import fs32 from "fs";
|
|
17193
17237
|
import path34 from "path";
|
|
17194
17238
|
import os28 from "os";
|
|
17195
17239
|
function readJson2(filePath) {
|
|
17196
17240
|
try {
|
|
17197
|
-
if (
|
|
17241
|
+
if (fs32.existsSync(filePath)) return JSON.parse(fs32.readFileSync(filePath, "utf-8"));
|
|
17198
17242
|
} catch {
|
|
17199
17243
|
}
|
|
17200
17244
|
return null;
|
|
@@ -17262,10 +17306,10 @@ function registerStatusCommand(program2) {
|
|
|
17262
17306
|
const projectConfig = path34.join(process.cwd(), "node9.config.json");
|
|
17263
17307
|
const globalConfig = path34.join(os28.homedir(), ".node9", "config.json");
|
|
17264
17308
|
console.log(
|
|
17265
|
-
` Local: ${
|
|
17309
|
+
` Local: ${fs32.existsSync(projectConfig) ? chalk12.green("Active (node9.config.json)") : chalk12.gray("Not present")}`
|
|
17266
17310
|
);
|
|
17267
17311
|
console.log(
|
|
17268
|
-
` Global: ${
|
|
17312
|
+
` Global: ${fs32.existsSync(globalConfig) ? chalk12.green("Active (~/.node9/config.json)") : chalk12.gray("Not present")}`
|
|
17269
17313
|
);
|
|
17270
17314
|
if (mergedConfig.policy.sandboxPaths.length > 0) {
|
|
17271
17315
|
console.log(
|
|
@@ -17340,7 +17384,7 @@ function registerStatusCommand(program2) {
|
|
|
17340
17384
|
// src/cli/commands/init.ts
|
|
17341
17385
|
init_core();
|
|
17342
17386
|
import chalk13 from "chalk";
|
|
17343
|
-
import
|
|
17387
|
+
import fs33 from "fs";
|
|
17344
17388
|
import path35 from "path";
|
|
17345
17389
|
import os29 from "os";
|
|
17346
17390
|
import https3 from "https";
|
|
@@ -17403,14 +17447,14 @@ function registerInitCommand(program2) {
|
|
|
17403
17447
|
console.log("");
|
|
17404
17448
|
}
|
|
17405
17449
|
const configPath = path35.join(os29.homedir(), ".node9", "config.json");
|
|
17406
|
-
if (
|
|
17450
|
+
if (fs33.existsSync(configPath) && !options.force) {
|
|
17407
17451
|
try {
|
|
17408
|
-
const existing = JSON.parse(
|
|
17452
|
+
const existing = JSON.parse(fs33.readFileSync(configPath, "utf-8"));
|
|
17409
17453
|
const settings = existing.settings ?? {};
|
|
17410
17454
|
if (settings.mode !== chosenMode) {
|
|
17411
17455
|
settings.mode = chosenMode;
|
|
17412
17456
|
existing.settings = settings;
|
|
17413
|
-
|
|
17457
|
+
fs33.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
|
|
17414
17458
|
console.log(chalk13.green(`\u2705 Mode updated: ${chosenMode}`));
|
|
17415
17459
|
} else {
|
|
17416
17460
|
console.log(chalk13.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
|
|
@@ -17424,8 +17468,8 @@ function registerInitCommand(program2) {
|
|
|
17424
17468
|
settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
|
|
17425
17469
|
};
|
|
17426
17470
|
const dir = path35.dirname(configPath);
|
|
17427
|
-
if (!
|
|
17428
|
-
|
|
17471
|
+
if (!fs33.existsSync(dir)) fs33.mkdirSync(dir, { recursive: true });
|
|
17472
|
+
fs33.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
|
|
17429
17473
|
console.log(chalk13.green(`\u2705 Config created: ${configPath}`));
|
|
17430
17474
|
console.log(chalk13.gray(` Mode: ${chosenMode}`));
|
|
17431
17475
|
}
|
|
@@ -17865,7 +17909,7 @@ import { execa as execa2 } from "execa";
|
|
|
17865
17909
|
init_provenance();
|
|
17866
17910
|
|
|
17867
17911
|
// src/mcp-pin.ts
|
|
17868
|
-
import
|
|
17912
|
+
import fs34 from "fs";
|
|
17869
17913
|
import path37 from "path";
|
|
17870
17914
|
import os30 from "os";
|
|
17871
17915
|
import crypto5 from "crypto";
|
|
@@ -17887,7 +17931,7 @@ function getServerKey(upstreamCommand) {
|
|
|
17887
17931
|
function readMcpPinsSafe() {
|
|
17888
17932
|
const filePath = getPinsFilePath2();
|
|
17889
17933
|
try {
|
|
17890
|
-
const raw =
|
|
17934
|
+
const raw = fs34.readFileSync(filePath, "utf-8");
|
|
17891
17935
|
if (!raw.trim()) {
|
|
17892
17936
|
return { ok: false, reason: "corrupt", detail: "empty file" };
|
|
17893
17937
|
}
|
|
@@ -17911,10 +17955,10 @@ function readMcpPins() {
|
|
|
17911
17955
|
}
|
|
17912
17956
|
function writeMcpPins(data) {
|
|
17913
17957
|
const filePath = getPinsFilePath2();
|
|
17914
|
-
|
|
17958
|
+
fs34.mkdirSync(path37.dirname(filePath), { recursive: true });
|
|
17915
17959
|
const tmp = `${filePath}.${crypto5.randomBytes(6).toString("hex")}.tmp`;
|
|
17916
|
-
|
|
17917
|
-
|
|
17960
|
+
fs34.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
17961
|
+
fs34.renameSync(tmp, filePath);
|
|
17918
17962
|
}
|
|
17919
17963
|
function checkPin(serverKey, currentHash) {
|
|
17920
17964
|
const result = readMcpPinsSafe();
|
|
@@ -18362,7 +18406,7 @@ function registerMcpGatewayCommand(program2) {
|
|
|
18362
18406
|
|
|
18363
18407
|
// src/mcp-server/index.ts
|
|
18364
18408
|
import readline4 from "readline";
|
|
18365
|
-
import
|
|
18409
|
+
import fs35 from "fs";
|
|
18366
18410
|
import os31 from "os";
|
|
18367
18411
|
import path38 from "path";
|
|
18368
18412
|
import { spawnSync as spawnSync7 } from "child_process";
|
|
@@ -18618,10 +18662,10 @@ function handleStatus() {
|
|
|
18618
18662
|
const projectConfig = path38.join(process.cwd(), "node9.config.json");
|
|
18619
18663
|
const globalConfig = path38.join(os31.homedir(), ".node9", "config.json");
|
|
18620
18664
|
lines.push(
|
|
18621
|
-
`Project config (node9.config.json): ${
|
|
18665
|
+
`Project config (node9.config.json): ${fs35.existsSync(projectConfig) ? "present" : "not found"}`
|
|
18622
18666
|
);
|
|
18623
18667
|
lines.push(
|
|
18624
|
-
`Global config (~/.node9/config.json): ${
|
|
18668
|
+
`Global config (~/.node9/config.json): ${fs35.existsSync(globalConfig) ? "present" : "not found"}`
|
|
18625
18669
|
);
|
|
18626
18670
|
return lines.join("\n");
|
|
18627
18671
|
}
|
|
@@ -18699,8 +18743,8 @@ var GLOBAL_CONFIG_PATH2 = path38.join(os31.homedir(), ".node9", "config.json");
|
|
|
18699
18743
|
var APPROVER_CHANNELS = ["native", "browser", "cloud", "terminal"];
|
|
18700
18744
|
function readGlobalConfigRaw() {
|
|
18701
18745
|
try {
|
|
18702
|
-
if (
|
|
18703
|
-
return JSON.parse(
|
|
18746
|
+
if (fs35.existsSync(GLOBAL_CONFIG_PATH2)) {
|
|
18747
|
+
return JSON.parse(fs35.readFileSync(GLOBAL_CONFIG_PATH2, "utf-8"));
|
|
18704
18748
|
}
|
|
18705
18749
|
} catch {
|
|
18706
18750
|
}
|
|
@@ -18708,8 +18752,8 @@ function readGlobalConfigRaw() {
|
|
|
18708
18752
|
}
|
|
18709
18753
|
function writeGlobalConfigRaw(data) {
|
|
18710
18754
|
const dir = path38.dirname(GLOBAL_CONFIG_PATH2);
|
|
18711
|
-
if (!
|
|
18712
|
-
|
|
18755
|
+
if (!fs35.existsSync(dir)) fs35.mkdirSync(dir, { recursive: true });
|
|
18756
|
+
fs35.writeFileSync(GLOBAL_CONFIG_PATH2, JSON.stringify(data, null, 2) + "\n");
|
|
18713
18757
|
}
|
|
18714
18758
|
function handleApproverList() {
|
|
18715
18759
|
const config = getConfig();
|
|
@@ -18754,8 +18798,8 @@ function handleAuditGet(args) {
|
|
|
18754
18798
|
const limit = Math.min(typeof args.limit === "number" ? args.limit : 20, 100);
|
|
18755
18799
|
const filter = typeof args.filter === "string" && args.filter !== "all" ? args.filter : null;
|
|
18756
18800
|
const auditPath = path38.join(os31.homedir(), ".node9", "audit.log");
|
|
18757
|
-
if (!
|
|
18758
|
-
const rawLines =
|
|
18801
|
+
if (!fs35.existsSync(auditPath)) return "No audit log found.";
|
|
18802
|
+
const rawLines = fs35.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
18759
18803
|
const parsed = [];
|
|
18760
18804
|
for (const line of rawLines) {
|
|
18761
18805
|
try {
|
|
@@ -19309,7 +19353,7 @@ init_scan();
|
|
|
19309
19353
|
|
|
19310
19354
|
// src/cli/commands/sessions.ts
|
|
19311
19355
|
import chalk22 from "chalk";
|
|
19312
|
-
import
|
|
19356
|
+
import fs36 from "fs";
|
|
19313
19357
|
import path39 from "path";
|
|
19314
19358
|
import os32 from "os";
|
|
19315
19359
|
var CLAUDE_PRICING3 = {
|
|
@@ -19427,7 +19471,7 @@ function loadAuditEntries(auditPath) {
|
|
|
19427
19471
|
const aPath = auditPath ?? path39.join(os32.homedir(), ".node9", "audit.log");
|
|
19428
19472
|
let raw;
|
|
19429
19473
|
try {
|
|
19430
|
-
raw =
|
|
19474
|
+
raw = fs36.readFileSync(aPath, "utf-8");
|
|
19431
19475
|
} catch {
|
|
19432
19476
|
return [];
|
|
19433
19477
|
}
|
|
@@ -19464,7 +19508,7 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
|
|
|
19464
19508
|
}
|
|
19465
19509
|
function buildGeminiSessions(days, allAuditEntries) {
|
|
19466
19510
|
const tmpDir = path39.join(os32.homedir(), ".gemini", "tmp");
|
|
19467
|
-
if (!
|
|
19511
|
+
if (!fs36.existsSync(tmpDir)) return [];
|
|
19468
19512
|
const cutoff = days !== null ? (() => {
|
|
19469
19513
|
const d = /* @__PURE__ */ new Date();
|
|
19470
19514
|
d.setDate(d.getDate() - days);
|
|
@@ -19473,7 +19517,7 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
19473
19517
|
})() : null;
|
|
19474
19518
|
let slugDirs;
|
|
19475
19519
|
try {
|
|
19476
|
-
slugDirs =
|
|
19520
|
+
slugDirs = fs36.readdirSync(tmpDir);
|
|
19477
19521
|
} catch {
|
|
19478
19522
|
return [];
|
|
19479
19523
|
}
|
|
@@ -19481,27 +19525,27 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
19481
19525
|
for (const slug of slugDirs) {
|
|
19482
19526
|
const slugPath = path39.join(tmpDir, slug);
|
|
19483
19527
|
try {
|
|
19484
|
-
if (!
|
|
19528
|
+
if (!fs36.statSync(slugPath).isDirectory()) continue;
|
|
19485
19529
|
} catch {
|
|
19486
19530
|
continue;
|
|
19487
19531
|
}
|
|
19488
19532
|
let projectRoot = path39.join(os32.homedir(), slug);
|
|
19489
19533
|
try {
|
|
19490
|
-
projectRoot =
|
|
19534
|
+
projectRoot = fs36.readFileSync(path39.join(slugPath, ".project_root"), "utf-8").trim();
|
|
19491
19535
|
} catch {
|
|
19492
19536
|
}
|
|
19493
19537
|
const chatsDir = path39.join(slugPath, "chats");
|
|
19494
|
-
if (!
|
|
19538
|
+
if (!fs36.existsSync(chatsDir)) continue;
|
|
19495
19539
|
let chatFiles;
|
|
19496
19540
|
try {
|
|
19497
|
-
chatFiles =
|
|
19541
|
+
chatFiles = fs36.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
|
|
19498
19542
|
} catch {
|
|
19499
19543
|
continue;
|
|
19500
19544
|
}
|
|
19501
19545
|
for (const chatFile of chatFiles) {
|
|
19502
19546
|
let raw;
|
|
19503
19547
|
try {
|
|
19504
|
-
raw =
|
|
19548
|
+
raw = fs36.readFileSync(path39.join(chatsDir, chatFile), "utf-8");
|
|
19505
19549
|
} catch {
|
|
19506
19550
|
continue;
|
|
19507
19551
|
}
|
|
@@ -19582,7 +19626,7 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
19582
19626
|
}
|
|
19583
19627
|
function buildCodexSessions(days, allAuditEntries) {
|
|
19584
19628
|
const sessionsBase = path39.join(os32.homedir(), ".codex", "sessions");
|
|
19585
|
-
if (!
|
|
19629
|
+
if (!fs36.existsSync(sessionsBase)) return [];
|
|
19586
19630
|
const cutoff = days !== null ? (() => {
|
|
19587
19631
|
const d = /* @__PURE__ */ new Date();
|
|
19588
19632
|
d.setDate(d.getDate() - days);
|
|
@@ -19591,28 +19635,28 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
19591
19635
|
})() : null;
|
|
19592
19636
|
const jsonlFiles = [];
|
|
19593
19637
|
try {
|
|
19594
|
-
for (const year of
|
|
19638
|
+
for (const year of fs36.readdirSync(sessionsBase)) {
|
|
19595
19639
|
const yearPath = path39.join(sessionsBase, year);
|
|
19596
19640
|
try {
|
|
19597
|
-
if (!
|
|
19641
|
+
if (!fs36.statSync(yearPath).isDirectory()) continue;
|
|
19598
19642
|
} catch {
|
|
19599
19643
|
continue;
|
|
19600
19644
|
}
|
|
19601
|
-
for (const month of
|
|
19645
|
+
for (const month of fs36.readdirSync(yearPath)) {
|
|
19602
19646
|
const monthPath = path39.join(yearPath, month);
|
|
19603
19647
|
try {
|
|
19604
|
-
if (!
|
|
19648
|
+
if (!fs36.statSync(monthPath).isDirectory()) continue;
|
|
19605
19649
|
} catch {
|
|
19606
19650
|
continue;
|
|
19607
19651
|
}
|
|
19608
|
-
for (const day of
|
|
19652
|
+
for (const day of fs36.readdirSync(monthPath)) {
|
|
19609
19653
|
const dayPath = path39.join(monthPath, day);
|
|
19610
19654
|
try {
|
|
19611
|
-
if (!
|
|
19655
|
+
if (!fs36.statSync(dayPath).isDirectory()) continue;
|
|
19612
19656
|
} catch {
|
|
19613
19657
|
continue;
|
|
19614
19658
|
}
|
|
19615
|
-
for (const file of
|
|
19659
|
+
for (const file of fs36.readdirSync(dayPath)) {
|
|
19616
19660
|
if (file.endsWith(".jsonl")) jsonlFiles.push(path39.join(dayPath, file));
|
|
19617
19661
|
}
|
|
19618
19662
|
}
|
|
@@ -19625,7 +19669,7 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
19625
19669
|
for (const filePath of jsonlFiles) {
|
|
19626
19670
|
let lines;
|
|
19627
19671
|
try {
|
|
19628
|
-
lines =
|
|
19672
|
+
lines = fs36.readFileSync(filePath, "utf-8").split("\n");
|
|
19629
19673
|
} catch {
|
|
19630
19674
|
continue;
|
|
19631
19675
|
}
|
|
@@ -19706,7 +19750,7 @@ function buildSessions(days, historyPath) {
|
|
|
19706
19750
|
const hPath = historyPath ?? path39.join(os32.homedir(), ".claude", "history.jsonl");
|
|
19707
19751
|
let historyRaw;
|
|
19708
19752
|
try {
|
|
19709
|
-
historyRaw =
|
|
19753
|
+
historyRaw = fs36.readFileSync(hPath, "utf-8");
|
|
19710
19754
|
} catch {
|
|
19711
19755
|
return [];
|
|
19712
19756
|
}
|
|
@@ -19731,7 +19775,7 @@ function buildSessions(days, historyPath) {
|
|
|
19731
19775
|
const jsonlFile = sessionJsonlPath(entry.project, entry.sessionId);
|
|
19732
19776
|
let sessionLines = [];
|
|
19733
19777
|
try {
|
|
19734
|
-
sessionLines =
|
|
19778
|
+
sessionLines = fs36.readFileSync(jsonlFile, "utf-8").split("\n");
|
|
19735
19779
|
} catch {
|
|
19736
19780
|
}
|
|
19737
19781
|
const { toolCalls, costUSD, hasSnapshot, modifiedFiles } = parseSessionLines(sessionLines);
|
|
@@ -19998,7 +20042,7 @@ function registerSessionsCommand(program2) {
|
|
|
19998
20042
|
console.log(chalk22.cyan.bold("\u{1F4CB} node9 sessions") + chalk22.dim(" \u2014 what your AI agent did"));
|
|
19999
20043
|
console.log("");
|
|
20000
20044
|
const historyPath = path39.join(os32.homedir(), ".claude", "history.jsonl");
|
|
20001
|
-
if (!
|
|
20045
|
+
if (!fs36.existsSync(historyPath)) {
|
|
20002
20046
|
console.log(chalk22.yellow(" No Claude session history found at ~/.claude/history.jsonl"));
|
|
20003
20047
|
console.log(chalk22.gray(" Install Claude Code, run a few sessions, then try again.\n"));
|
|
20004
20048
|
return;
|
|
@@ -20035,12 +20079,12 @@ function registerSessionsCommand(program2) {
|
|
|
20035
20079
|
|
|
20036
20080
|
// src/cli/commands/skill-pin.ts
|
|
20037
20081
|
import chalk23 from "chalk";
|
|
20038
|
-
import
|
|
20082
|
+
import fs37 from "fs";
|
|
20039
20083
|
import os33 from "os";
|
|
20040
20084
|
import path40 from "path";
|
|
20041
20085
|
function wipeSkillSessions() {
|
|
20042
20086
|
try {
|
|
20043
|
-
|
|
20087
|
+
fs37.rmSync(path40.join(os33.homedir(), ".node9", "skill-sessions"), {
|
|
20044
20088
|
recursive: true,
|
|
20045
20089
|
force: true
|
|
20046
20090
|
});
|
|
@@ -20123,7 +20167,7 @@ function registerSkillPinCommand(program2) {
|
|
|
20123
20167
|
|
|
20124
20168
|
// src/cli/commands/dlp.ts
|
|
20125
20169
|
import chalk24 from "chalk";
|
|
20126
|
-
import
|
|
20170
|
+
import fs38 from "fs";
|
|
20127
20171
|
import path41 from "path";
|
|
20128
20172
|
import os34 from "os";
|
|
20129
20173
|
var AUDIT_LOG = path41.join(os34.homedir(), ".node9", "audit.log");
|
|
@@ -20134,7 +20178,7 @@ function stripAnsi(s) {
|
|
|
20134
20178
|
}
|
|
20135
20179
|
function loadResolved() {
|
|
20136
20180
|
try {
|
|
20137
|
-
const raw = JSON.parse(
|
|
20181
|
+
const raw = JSON.parse(fs38.readFileSync(RESOLVED_FILE, "utf-8"));
|
|
20138
20182
|
return new Set(raw);
|
|
20139
20183
|
} catch {
|
|
20140
20184
|
return /* @__PURE__ */ new Set();
|
|
@@ -20142,13 +20186,13 @@ function loadResolved() {
|
|
|
20142
20186
|
}
|
|
20143
20187
|
function saveResolved(resolved) {
|
|
20144
20188
|
try {
|
|
20145
|
-
|
|
20189
|
+
fs38.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
|
|
20146
20190
|
} catch {
|
|
20147
20191
|
}
|
|
20148
20192
|
}
|
|
20149
20193
|
function loadDlpFindings() {
|
|
20150
|
-
if (!
|
|
20151
|
-
return
|
|
20194
|
+
if (!fs38.existsSync(AUDIT_LOG)) return [];
|
|
20195
|
+
return fs38.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
|
|
20152
20196
|
if (!line.trim()) return [];
|
|
20153
20197
|
try {
|
|
20154
20198
|
const e = JSON.parse(line);
|
|
@@ -20246,20 +20290,20 @@ function registerDlpCommand(program2) {
|
|
|
20246
20290
|
|
|
20247
20291
|
// src/cli.ts
|
|
20248
20292
|
var { version } = JSON.parse(
|
|
20249
|
-
|
|
20293
|
+
fs41.readFileSync(path44.join(__dirname, "../package.json"), "utf-8")
|
|
20250
20294
|
);
|
|
20251
20295
|
var program = new Command();
|
|
20252
20296
|
program.name("node9").description("The Sudo Command for AI Agents").version(version);
|
|
20253
20297
|
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) => {
|
|
20254
20298
|
const DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept";
|
|
20255
20299
|
const credPath = path44.join(os37.homedir(), ".node9", "credentials.json");
|
|
20256
|
-
if (!
|
|
20257
|
-
|
|
20300
|
+
if (!fs41.existsSync(path44.dirname(credPath)))
|
|
20301
|
+
fs41.mkdirSync(path44.dirname(credPath), { recursive: true });
|
|
20258
20302
|
const profileName = options.profile || "default";
|
|
20259
20303
|
let existingCreds = {};
|
|
20260
20304
|
try {
|
|
20261
|
-
if (
|
|
20262
|
-
const raw = JSON.parse(
|
|
20305
|
+
if (fs41.existsSync(credPath)) {
|
|
20306
|
+
const raw = JSON.parse(fs41.readFileSync(credPath, "utf-8"));
|
|
20263
20307
|
if (raw.apiKey) {
|
|
20264
20308
|
existingCreds = {
|
|
20265
20309
|
default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL2 }
|
|
@@ -20271,13 +20315,13 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
20271
20315
|
} catch {
|
|
20272
20316
|
}
|
|
20273
20317
|
existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL2 };
|
|
20274
|
-
|
|
20318
|
+
fs41.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
|
|
20275
20319
|
if (profileName === "default") {
|
|
20276
20320
|
const configPath = path44.join(os37.homedir(), ".node9", "config.json");
|
|
20277
20321
|
let config = {};
|
|
20278
20322
|
try {
|
|
20279
|
-
if (
|
|
20280
|
-
config = JSON.parse(
|
|
20323
|
+
if (fs41.existsSync(configPath))
|
|
20324
|
+
config = JSON.parse(fs41.readFileSync(configPath, "utf-8"));
|
|
20281
20325
|
} catch {
|
|
20282
20326
|
}
|
|
20283
20327
|
if (!config.settings || typeof config.settings !== "object") config.settings = {};
|
|
@@ -20292,9 +20336,9 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
20292
20336
|
approvers.cloud = false;
|
|
20293
20337
|
}
|
|
20294
20338
|
s.approvers = approvers;
|
|
20295
|
-
if (!
|
|
20296
|
-
|
|
20297
|
-
|
|
20339
|
+
if (!fs41.existsSync(path44.dirname(configPath)))
|
|
20340
|
+
fs41.mkdirSync(path44.dirname(configPath), { recursive: true });
|
|
20341
|
+
fs41.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
20298
20342
|
}
|
|
20299
20343
|
if (options.profile && profileName !== "default") {
|
|
20300
20344
|
console.log(chalk26.green(`\u2705 Profile "${profileName}" saved`));
|
|
@@ -20432,14 +20476,14 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
20432
20476
|
}
|
|
20433
20477
|
if (options.purge) {
|
|
20434
20478
|
const node9Dir = path44.join(os37.homedir(), ".node9");
|
|
20435
|
-
if (
|
|
20479
|
+
if (fs41.existsSync(node9Dir)) {
|
|
20436
20480
|
const confirmed = await confirm2({
|
|
20437
20481
|
message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
|
|
20438
20482
|
default: false
|
|
20439
20483
|
});
|
|
20440
20484
|
if (confirmed) {
|
|
20441
|
-
|
|
20442
|
-
if (
|
|
20485
|
+
fs41.rmSync(node9Dir, { recursive: true });
|
|
20486
|
+
if (fs41.existsSync(node9Dir)) {
|
|
20443
20487
|
console.error(
|
|
20444
20488
|
chalk26.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
|
|
20445
20489
|
);
|
|
@@ -20583,12 +20627,12 @@ Run "node9 addto claude" to register it as the statusLine.`
|
|
|
20583
20627
|
if (subcommand === "debug") {
|
|
20584
20628
|
const flagFile = path44.join(os37.homedir(), ".node9", "hud-debug");
|
|
20585
20629
|
if (state === "on") {
|
|
20586
|
-
|
|
20587
|
-
|
|
20630
|
+
fs41.mkdirSync(path44.dirname(flagFile), { recursive: true });
|
|
20631
|
+
fs41.writeFileSync(flagFile, "");
|
|
20588
20632
|
console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
|
|
20589
20633
|
console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
|
|
20590
20634
|
} else if (state === "off") {
|
|
20591
|
-
if (
|
|
20635
|
+
if (fs41.existsSync(flagFile)) fs41.unlinkSync(flagFile);
|
|
20592
20636
|
console.log("HUD debug logging disabled.");
|
|
20593
20637
|
} else {
|
|
20594
20638
|
console.error("Usage: node9 hud debug on|off");
|
|
@@ -20702,7 +20746,7 @@ if (process.argv[2] !== "daemon") {
|
|
|
20702
20746
|
if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
|
|
20703
20747
|
const logPath = path44.join(os37.homedir(), ".node9", "hook-debug.log");
|
|
20704
20748
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
20705
|
-
|
|
20749
|
+
fs41.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
|
|
20706
20750
|
`);
|
|
20707
20751
|
}
|
|
20708
20752
|
process.exit(0);
|