@node9/proxy 1.31.0 → 1.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1344 -855
- package/dist/cli.mjs +1329 -840
- package/dist/dashboard.mjs +78 -15
- package/dist/index.js +86 -17
- package/dist/index.mjs +86 -17
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -122,6 +122,9 @@ function appendLocalAudit(toolName, args, decision, checkedBy, meta, auditHashAr
|
|
|
122
122
|
const agentToolNameField = meta?.agentToolName ? { agentToolName: meta.agentToolName } : {};
|
|
123
123
|
const dlpFields = meta?.dlpPattern ? { dlpPattern: meta.dlpPattern, dlpSample: meta.dlpSample } : {};
|
|
124
124
|
const cloudLinkField = meta?.cloudRequestId ? { cloudRequestId: meta.cloudRequestId } : {};
|
|
125
|
+
const workingDirField = meta?.workingDir ? { workingDir: meta.workingDir } : {};
|
|
126
|
+
const shell = process.env.SHELL ? path.basename(process.env.SHELL) : void 0;
|
|
127
|
+
const shellTypeField = shell ? { shellType: shell } : {};
|
|
125
128
|
appendToLog(LOCAL_AUDIT_LOG, {
|
|
126
129
|
// eid first: the outbox shipper dedups on it, and a fixed leading field
|
|
127
130
|
// makes the JSONL easy to eyeball.
|
|
@@ -135,11 +138,14 @@ function appendLocalAudit(toolName, args, decision, checkedBy, meta, auditHashAr
|
|
|
135
138
|
...ruleNameField,
|
|
136
139
|
...dlpFields,
|
|
137
140
|
...cloudLinkField,
|
|
141
|
+
...workingDirField,
|
|
142
|
+
...shellTypeField,
|
|
138
143
|
...testRun,
|
|
139
144
|
agent: meta?.agent,
|
|
140
145
|
mcpServer: meta?.mcpServer,
|
|
141
146
|
sessionId: meta?.sessionId,
|
|
142
|
-
hostname: os.hostname()
|
|
147
|
+
hostname: os.hostname(),
|
|
148
|
+
platform: os.platform()
|
|
143
149
|
});
|
|
144
150
|
}
|
|
145
151
|
function appendConfigAudit(entry) {
|
|
@@ -179,8 +185,8 @@ function sanitizeConfig(raw) {
|
|
|
179
185
|
}
|
|
180
186
|
}
|
|
181
187
|
const lines = result.error.issues.map((issue) => {
|
|
182
|
-
const
|
|
183
|
-
return ` \u2022 ${
|
|
188
|
+
const path54 = issue.path.length > 0 ? issue.path.join(".") : "root";
|
|
189
|
+
return ` \u2022 ${path54}: ${issue.message}`;
|
|
184
190
|
});
|
|
185
191
|
return {
|
|
186
192
|
sanitized,
|
|
@@ -532,6 +538,8 @@ function normalizeCommandForPolicyImpl(command) {
|
|
|
532
538
|
if (f === PARSE_FAIL) return command;
|
|
533
539
|
try {
|
|
534
540
|
const strips = [];
|
|
541
|
+
const rewrites = [];
|
|
542
|
+
const msgSpans = /* @__PURE__ */ new Set();
|
|
535
543
|
syntax.Walk(f, (node) => {
|
|
536
544
|
if (!node) return false;
|
|
537
545
|
const n = node;
|
|
@@ -547,25 +555,46 @@ function normalizeCommandForPolicyImpl(command) {
|
|
|
547
555
|
if (nextParts.length !== 1) continue;
|
|
548
556
|
const quotedNode = nextParts[0];
|
|
549
557
|
const nt = syntax.NodeType(quotedNode);
|
|
558
|
+
const markStrip = () => {
|
|
559
|
+
const s = next.Pos().Offset();
|
|
560
|
+
const e = next.End().Offset();
|
|
561
|
+
strips.push([s, e]);
|
|
562
|
+
msgSpans.add(`${s}:${e}`);
|
|
563
|
+
};
|
|
550
564
|
if (nt === "SglQuoted") {
|
|
551
|
-
|
|
565
|
+
markStrip();
|
|
552
566
|
} else if (nt === "DblQuoted") {
|
|
553
567
|
const innerParts = quotedNode.Parts || [];
|
|
554
568
|
const allLit = innerParts.length === 0 || innerParts.every((p) => syntax.NodeType(p) === "Lit");
|
|
555
569
|
if (allLit) {
|
|
556
|
-
|
|
570
|
+
markStrip();
|
|
557
571
|
} else if (innerParts.every((p) => isCatHeredocOrLit(p))) {
|
|
558
|
-
|
|
572
|
+
markStrip();
|
|
559
573
|
}
|
|
560
574
|
}
|
|
561
575
|
}
|
|
576
|
+
for (const arg of args) {
|
|
577
|
+
const s = arg.Pos().Offset();
|
|
578
|
+
const e = arg.End().Offset();
|
|
579
|
+
if (msgSpans.has(`${s}:${e}`)) continue;
|
|
580
|
+
const resolved = resolveWordLiteral(arg);
|
|
581
|
+
if (resolved === null) continue;
|
|
582
|
+
const source = command.slice(s, e);
|
|
583
|
+
if (resolved === source) continue;
|
|
584
|
+
if (resolved === "" || /\s/.test(resolved)) continue;
|
|
585
|
+
rewrites.push([s, e, resolved]);
|
|
586
|
+
}
|
|
562
587
|
return true;
|
|
563
588
|
});
|
|
564
|
-
|
|
565
|
-
|
|
589
|
+
const edits = [
|
|
590
|
+
...strips.map(([s, e]) => [s, e, '""']),
|
|
591
|
+
...rewrites
|
|
592
|
+
];
|
|
593
|
+
if (edits.length === 0) return command;
|
|
594
|
+
edits.sort((a, b) => b[0] - a[0]);
|
|
566
595
|
let result = command;
|
|
567
|
-
for (const [
|
|
568
|
-
result = result.slice(0,
|
|
596
|
+
for (const [s, e, rep] of edits) {
|
|
597
|
+
result = result.slice(0, s) + rep + result.slice(e);
|
|
569
598
|
}
|
|
570
599
|
return result;
|
|
571
600
|
} catch {
|
|
@@ -627,6 +656,17 @@ function detectDangerousShellExec(command) {
|
|
|
627
656
|
function isBashTool(toolName) {
|
|
628
657
|
return BASH_TOOL_NAMES.has(toolName.toLowerCase());
|
|
629
658
|
}
|
|
659
|
+
function analyzeSqlDestructive(command) {
|
|
660
|
+
if (!SQL_DDL_RE.test(command)) return null;
|
|
661
|
+
const { actions } = analyzeShellCommand(command);
|
|
662
|
+
if (!actions.some((a) => SQL_DB_CLIS.has(a))) return null;
|
|
663
|
+
return {
|
|
664
|
+
ruleName: "review-drop-truncate-shell",
|
|
665
|
+
verdict: "review",
|
|
666
|
+
reason: "SQL DDL destructive statement inside a shell command",
|
|
667
|
+
description: "The AI wants to drop or truncate a database table via the shell. This permanently deletes the table structure or all its data."
|
|
668
|
+
};
|
|
669
|
+
}
|
|
630
670
|
function isProtectedHomePath(rawPath) {
|
|
631
671
|
let p = rawPath.replace(/^\$HOME[\\/]?|^\$\{HOME\}[\\/]?/, "~/");
|
|
632
672
|
let underHome = false;
|
|
@@ -788,19 +828,20 @@ function extractShellDestinations(command) {
|
|
|
788
828
|
return out;
|
|
789
829
|
}
|
|
790
830
|
function analyzeFsOperation(command) {
|
|
791
|
-
|
|
792
|
-
if (
|
|
793
|
-
|
|
794
|
-
fsOpCache.
|
|
795
|
-
fsOpCache.
|
|
831
|
+
const normalized = normalizeCommandForPolicy(command);
|
|
832
|
+
if (!FS_OP_PRESCREEN_RE.test(normalized)) return null;
|
|
833
|
+
if (fsOpCache.has(normalized)) {
|
|
834
|
+
const hit = fsOpCache.get(normalized) ?? null;
|
|
835
|
+
fsOpCache.delete(normalized);
|
|
836
|
+
fsOpCache.set(normalized, hit);
|
|
796
837
|
return hit;
|
|
797
838
|
}
|
|
798
|
-
const computed = analyzeFsOperationImpl(
|
|
839
|
+
const computed = analyzeFsOperationImpl(normalized);
|
|
799
840
|
if (fsOpCache.size >= FS_OP_CACHE_MAX) {
|
|
800
841
|
const oldest = fsOpCache.keys().next().value;
|
|
801
842
|
if (oldest !== void 0) fsOpCache.delete(oldest);
|
|
802
843
|
}
|
|
803
|
-
fsOpCache.set(
|
|
844
|
+
fsOpCache.set(normalized, computed);
|
|
804
845
|
return computed;
|
|
805
846
|
}
|
|
806
847
|
function analyzeFsOperationImpl(command) {
|
|
@@ -1199,9 +1240,9 @@ function matchesPattern(text, patterns) {
|
|
|
1199
1240
|
const withoutDotSlash = text.replace(/^\.\//, "");
|
|
1200
1241
|
return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
|
|
1201
1242
|
}
|
|
1202
|
-
function getNestedValue(obj,
|
|
1243
|
+
function getNestedValue(obj, path54) {
|
|
1203
1244
|
if (!obj || typeof obj !== "object") return null;
|
|
1204
|
-
const segments =
|
|
1245
|
+
const segments = path54.split(".");
|
|
1205
1246
|
for (const seg of segments) {
|
|
1206
1247
|
if (FORBIDDEN_PATH_SEGMENTS.has(seg)) return null;
|
|
1207
1248
|
}
|
|
@@ -1341,6 +1382,17 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
|
|
|
1341
1382
|
ruleDescription: fsVerdict.reason
|
|
1342
1383
|
};
|
|
1343
1384
|
}
|
|
1385
|
+
const sqlVerdict = analyzeSqlDestructive(bashCommand);
|
|
1386
|
+
if (sqlVerdict) {
|
|
1387
|
+
return {
|
|
1388
|
+
decision: sqlVerdict.verdict,
|
|
1389
|
+
blockedByLabel: `Node9 (AST): ${sqlVerdict.ruleName}`,
|
|
1390
|
+
reason: sqlVerdict.reason,
|
|
1391
|
+
tier: 2,
|
|
1392
|
+
ruleName: sqlVerdict.ruleName,
|
|
1393
|
+
ruleDescription: sqlVerdict.description
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1344
1396
|
}
|
|
1345
1397
|
if (config.policy.smartRules.length > 0) {
|
|
1346
1398
|
const matchedRule = config.policy.smartRules.find(
|
|
@@ -1921,7 +1973,7 @@ function extractCanonicalFindings(call, ctx) {
|
|
|
1921
1973
|
})
|
|
1922
1974
|
);
|
|
1923
1975
|
}
|
|
1924
|
-
if (DESTRUCTIVE_OP_RE.test(command)) {
|
|
1976
|
+
if (command !== null && DESTRUCTIVE_OP_RE.test(normalizeCommandForPolicy(command))) {
|
|
1925
1977
|
out.push(
|
|
1926
1978
|
makeFinding({
|
|
1927
1979
|
type: "destructive-op",
|
|
@@ -2073,7 +2125,7 @@ function* stringValues(obj, depth = 0) {
|
|
|
2073
2125
|
}
|
|
2074
2126
|
for (const v of Object.values(obj)) yield* stringValues(v, depth + 1);
|
|
2075
2127
|
}
|
|
2076
|
-
var ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, NET_BINARIES, VALUE_FLAGS, FS_OP_CACHE_MAX, fsOpCache, DEFAULT_EGRESS_ALLOWLIST, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, SQL_DML_KEYWORDS, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, COST_PER_LOOP_ITER_USD, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, LONG_OUTPUT_THRESHOLD_BYTES, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, TERMINAL_ESCAPE_RE;
|
|
2128
|
+
var ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, NET_BINARIES, VALUE_FLAGS, FS_OP_CACHE_MAX, fsOpCache, DEFAULT_EGRESS_ALLOWLIST, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, SQL_DML_KEYWORDS, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, COST_PER_LOOP_ITER_USD, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, LONG_OUTPUT_THRESHOLD_BYTES, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, TERMINAL_ESCAPE_RE;
|
|
2077
2129
|
var init_dist = __esm({
|
|
2078
2130
|
"packages/policy-engine/dist/index.mjs"() {
|
|
2079
2131
|
"use strict";
|
|
@@ -2685,8 +2737,24 @@ var init_dist = __esm({
|
|
|
2685
2737
|
"shield:project-jail:block-read-ssh",
|
|
2686
2738
|
"shield:project-jail:block-read-aws",
|
|
2687
2739
|
"shield:project-jail:block-read-env",
|
|
2688
|
-
"shield:project-jail:review-read-credentials"
|
|
2740
|
+
"shield:project-jail:review-read-credentials",
|
|
2741
|
+
// SQL-DDL is now owned by the AST detector (analyzeSqlDestructive) so the
|
|
2742
|
+
// raw-regex smart rule is suppressed for bash — its cond1 read a grep
|
|
2743
|
+
// alternation's `|` as a shell pipe (`grep "…|mysql…"` → false positive).
|
|
2744
|
+
"review-drop-truncate-shell"
|
|
2689
2745
|
]);
|
|
2746
|
+
SQL_DB_CLIS = /* @__PURE__ */ new Set([
|
|
2747
|
+
"psql",
|
|
2748
|
+
"mysql",
|
|
2749
|
+
"mariadb",
|
|
2750
|
+
"sqlite3",
|
|
2751
|
+
"sqlplus",
|
|
2752
|
+
"cockroach",
|
|
2753
|
+
"clickhouse-client",
|
|
2754
|
+
"mongo",
|
|
2755
|
+
"mongosh"
|
|
2756
|
+
]);
|
|
2757
|
+
SQL_DDL_RE = /\b(DROP|TRUNCATE)\s+(TABLE|DATABASE|SCHEMA|INDEX)\b/i;
|
|
2690
2758
|
NET_BINARIES = /* @__PURE__ */ new Set(["curl", "wget", "scp", "ssh", "nc", "ncat", "netcat"]);
|
|
2691
2759
|
VALUE_FLAGS = {
|
|
2692
2760
|
curl: /* @__PURE__ */ new Set([
|
|
@@ -3725,7 +3793,7 @@ var init_dist = __esm({
|
|
|
3725
3793
|
REALTIME_PII_PATTERNS = ["SSN", "Credit Card"];
|
|
3726
3794
|
MAX_PII_SCAN_BYTES = 1e5;
|
|
3727
3795
|
LONG_OUTPUT_THRESHOLD_BYTES = 100 * 1024;
|
|
3728
|
-
CANONICAL_EXTRACTOR_VERSION = "canonical-
|
|
3796
|
+
CANONICAL_EXTRACTOR_VERSION = "canonical-v6";
|
|
3729
3797
|
DEDUPE_PREVIEW_LEN = 120;
|
|
3730
3798
|
TERMINAL_ESCAPE_RE = // eslint-disable-next-line no-control-regex
|
|
3731
3799
|
/\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-_]|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
|
|
@@ -6055,7 +6123,8 @@ async function authorizeHeadless(toolName, args, meta, options) {
|
|
|
6055
6123
|
}
|
|
6056
6124
|
return _authorizeHeadlessCore(toolName, args, meta, options);
|
|
6057
6125
|
}
|
|
6058
|
-
async function _authorizeHeadlessCore(toolName, args,
|
|
6126
|
+
async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
6127
|
+
const meta = options?.cwd && !metaArg?.workingDir ? { ...metaArg, workingDir: options.cwd } : metaArg;
|
|
6059
6128
|
if (process.env.NODE9_PAUSED === "1") return { approved: true, checkedBy: "paused" };
|
|
6060
6129
|
const pauseState = checkPause();
|
|
6061
6130
|
if (pauseState.paused) return { approved: true, checkedBy: "paused" };
|
|
@@ -7216,7 +7285,7 @@ function writeJson(filePath, data) {
|
|
|
7216
7285
|
}
|
|
7217
7286
|
function isNode9Hook(cmd) {
|
|
7218
7287
|
if (!cmd) return false;
|
|
7219
|
-
return /(?:^|[\s/\\"])node9 (?:check|log)/.test(cmd) || /(?:^|[\s/\\
|
|
7288
|
+
return /(?:^|[\s/\\"])node9"? (?:check|log)/.test(cmd) || /(?:^|[\s/\\])cli\.js"? (?:check|log)/.test(cmd);
|
|
7220
7289
|
}
|
|
7221
7290
|
function teardownClaude() {
|
|
7222
7291
|
const homeDir2 = os12.homedir();
|
|
@@ -10003,83 +10072,132 @@ var init_litellm = __esm({
|
|
|
10003
10072
|
import fs17 from "fs";
|
|
10004
10073
|
import os16 from "os";
|
|
10005
10074
|
import path19 from "path";
|
|
10006
|
-
function
|
|
10007
|
-
return path19.join(os16.homedir(), ".codex", "
|
|
10008
|
-
}
|
|
10009
|
-
function
|
|
10010
|
-
|
|
10011
|
-
|
|
10012
|
-
|
|
10013
|
-
const
|
|
10014
|
-
|
|
10015
|
-
|
|
10016
|
-
|
|
10017
|
-
|
|
10018
|
-
|
|
10019
|
-
|
|
10020
|
-
|
|
10021
|
-
|
|
10022
|
-
|
|
10023
|
-
|
|
10024
|
-
|
|
10025
|
-
|
|
10026
|
-
|
|
10027
|
-
|
|
10028
|
-
|
|
10075
|
+
function codexSessionsDir() {
|
|
10076
|
+
return path19.join(os16.homedir(), ".codex", "sessions");
|
|
10077
|
+
}
|
|
10078
|
+
function codexPriceFor(model) {
|
|
10079
|
+
return pricingFor(model) ?? CODEX_FALLBACK;
|
|
10080
|
+
}
|
|
10081
|
+
function listCodexSessionFiles(base) {
|
|
10082
|
+
const out = [];
|
|
10083
|
+
for (const y of safeReaddir(base)) {
|
|
10084
|
+
const yp = path19.join(base, y);
|
|
10085
|
+
if (!isDir(yp)) continue;
|
|
10086
|
+
for (const m of safeReaddir(yp)) {
|
|
10087
|
+
const mp = path19.join(yp, m);
|
|
10088
|
+
if (!isDir(mp)) continue;
|
|
10089
|
+
for (const d of safeReaddir(mp)) {
|
|
10090
|
+
const dp = path19.join(mp, d);
|
|
10091
|
+
if (!isDir(dp)) continue;
|
|
10092
|
+
for (const f of safeReaddir(dp)) {
|
|
10093
|
+
if (f.endsWith(".jsonl")) out.push(path19.join(dp, f));
|
|
10094
|
+
}
|
|
10095
|
+
}
|
|
10096
|
+
}
|
|
10097
|
+
}
|
|
10098
|
+
return out;
|
|
10099
|
+
}
|
|
10100
|
+
function safeReaddir(dir) {
|
|
10101
|
+
try {
|
|
10102
|
+
return fs17.readdirSync(dir);
|
|
10103
|
+
} catch {
|
|
10104
|
+
return [];
|
|
10105
|
+
}
|
|
10106
|
+
}
|
|
10107
|
+
function isDir(p) {
|
|
10108
|
+
try {
|
|
10109
|
+
return fs17.statSync(p).isDirectory();
|
|
10110
|
+
} catch {
|
|
10111
|
+
return false;
|
|
10112
|
+
}
|
|
10113
|
+
}
|
|
10114
|
+
function parseCodexSession(lines) {
|
|
10115
|
+
let sessionStart2 = "";
|
|
10116
|
+
let runId = "";
|
|
10117
|
+
let cwd = "";
|
|
10118
|
+
let model = "";
|
|
10119
|
+
let input = 0;
|
|
10120
|
+
let cached = 0;
|
|
10121
|
+
let output = 0;
|
|
10122
|
+
let sawUsage = false;
|
|
10123
|
+
for (const raw of lines) {
|
|
10124
|
+
if (!raw.trim()) continue;
|
|
10125
|
+
let entry;
|
|
10126
|
+
try {
|
|
10127
|
+
entry = JSON.parse(raw);
|
|
10128
|
+
} catch {
|
|
10129
|
+
continue;
|
|
10130
|
+
}
|
|
10131
|
+
const p = entry.payload ?? {};
|
|
10132
|
+
if (entry.type === "session_meta") {
|
|
10133
|
+
if (!sessionStart2 && typeof p["timestamp"] === "string") sessionStart2 = p["timestamp"];
|
|
10134
|
+
if (!runId && typeof p["id"] === "string") runId = p["id"];
|
|
10135
|
+
if (!cwd && typeof p["cwd"] === "string") cwd = p["cwd"];
|
|
10136
|
+
continue;
|
|
10137
|
+
}
|
|
10138
|
+
if (entry.type === "turn_context") {
|
|
10139
|
+
if (typeof p["model"] === "string") model = p["model"];
|
|
10140
|
+
if (!cwd && typeof p["cwd"] === "string") cwd = p["cwd"];
|
|
10141
|
+
continue;
|
|
10142
|
+
}
|
|
10143
|
+
if (entry.type === "event_msg" && p["type"] === "token_count") {
|
|
10144
|
+
const info = p["info"] ?? {};
|
|
10145
|
+
const usage = info["total_token_usage"] ?? {};
|
|
10146
|
+
if (typeof usage["input_tokens"] === "number") input = usage["input_tokens"];
|
|
10147
|
+
if (typeof usage["cached_input_tokens"] === "number") cached = usage["cached_input_tokens"];
|
|
10148
|
+
if (typeof usage["output_tokens"] === "number") output = usage["output_tokens"];
|
|
10149
|
+
sawUsage = true;
|
|
10150
|
+
}
|
|
10151
|
+
}
|
|
10152
|
+
if (!sessionStart2 || !sawUsage) return null;
|
|
10153
|
+
const nonCached = Math.max(0, input - cached);
|
|
10154
|
+
if (nonCached === 0 && output === 0 && cached === 0) return null;
|
|
10155
|
+
const norm = normalizeModel(model || "gpt-5");
|
|
10156
|
+
const [pin, pout, , pcr] = codexPriceFor(model || "gpt-5");
|
|
10157
|
+
const costUSD = nonCached * pin + output * pout + cached * pcr;
|
|
10029
10158
|
return {
|
|
10030
|
-
date,
|
|
10159
|
+
date: sessionStart2.slice(0, 10),
|
|
10031
10160
|
model: norm,
|
|
10032
|
-
workingDir:
|
|
10033
|
-
// Codex span carries no cwd — attribution is by runId (thread)
|
|
10161
|
+
workingDir: cwd,
|
|
10034
10162
|
runId,
|
|
10035
10163
|
costUSD,
|
|
10036
|
-
inputTokens,
|
|
10164
|
+
inputTokens: nonCached,
|
|
10037
10165
|
outputTokens: output,
|
|
10038
10166
|
cacheReadTokens: cached,
|
|
10039
10167
|
cacheWriteTokens: 0
|
|
10040
10168
|
};
|
|
10041
10169
|
}
|
|
10042
|
-
var
|
|
10170
|
+
var CODEX_FALLBACK, codexSource;
|
|
10043
10171
|
var init_cost_codex = __esm({
|
|
10044
10172
|
"src/cost-codex.ts"() {
|
|
10045
10173
|
"use strict";
|
|
10046
10174
|
init_litellm();
|
|
10047
|
-
|
|
10048
|
-
RE_CACHED = /token_usage\.cached_input_tokens=(\d+)/;
|
|
10049
|
-
RE_NON_CACHED = /token_usage\.non_cached_input_tokens=(\d+)/;
|
|
10050
|
-
RE_OUTPUT = /token_usage\.output_tokens=(\d+)/;
|
|
10051
|
-
RE_MODEL = /\bmodel=([^\s}]+)/;
|
|
10052
|
-
RE_THREAD = /\bthread\.id=([0-9a-fA-F-]+)/;
|
|
10053
|
-
RE_DATE = /^(\d{4}-\d{2}-\d{2})T/;
|
|
10175
|
+
CODEX_FALLBACK = [5e-6, 15e-6, 0, 25e-7];
|
|
10054
10176
|
codexSource = {
|
|
10055
10177
|
id: "codex",
|
|
10056
10178
|
available() {
|
|
10057
10179
|
try {
|
|
10058
|
-
return fs17.existsSync(
|
|
10180
|
+
return fs17.existsSync(codexSessionsDir());
|
|
10059
10181
|
} catch {
|
|
10060
10182
|
return false;
|
|
10061
10183
|
}
|
|
10062
10184
|
},
|
|
10063
10185
|
collect(sinceMs) {
|
|
10064
|
-
const
|
|
10065
|
-
let content;
|
|
10066
|
-
try {
|
|
10067
|
-
if (sinceMs !== void 0 && fs17.statSync(file).mtimeMs < sinceMs) return [];
|
|
10068
|
-
content = fs17.readFileSync(file, "utf8");
|
|
10069
|
-
} catch {
|
|
10070
|
-
return [];
|
|
10071
|
-
}
|
|
10186
|
+
const base = codexSessionsDir();
|
|
10072
10187
|
const combined = /* @__PURE__ */ new Map();
|
|
10073
|
-
for (const
|
|
10074
|
-
|
|
10075
|
-
|
|
10076
|
-
|
|
10077
|
-
|
|
10078
|
-
const t = Date.parse(tsFull);
|
|
10079
|
-
if (!Number.isNaN(t) && t < sinceMs) continue;
|
|
10080
|
-
}
|
|
10188
|
+
for (const file of listCodexSessionFiles(base)) {
|
|
10189
|
+
try {
|
|
10190
|
+
if (sinceMs !== void 0 && fs17.statSync(file).mtimeMs < sinceMs) continue;
|
|
10191
|
+
} catch {
|
|
10192
|
+
continue;
|
|
10081
10193
|
}
|
|
10082
|
-
|
|
10194
|
+
let content;
|
|
10195
|
+
try {
|
|
10196
|
+
content = fs17.readFileSync(file, "utf8");
|
|
10197
|
+
} catch {
|
|
10198
|
+
continue;
|
|
10199
|
+
}
|
|
10200
|
+
const e = parseCodexSession(content.split("\n"));
|
|
10083
10201
|
if (!e) continue;
|
|
10084
10202
|
const key = `${e.date}::${e.model}::${e.workingDir ?? ""}::${e.runId ?? ""}`;
|
|
10085
10203
|
const prev = combined.get(key);
|
|
@@ -10099,18 +10217,289 @@ var init_cost_codex = __esm({
|
|
|
10099
10217
|
}
|
|
10100
10218
|
});
|
|
10101
10219
|
|
|
10102
|
-
// src/
|
|
10220
|
+
// src/cost-gemini.ts
|
|
10103
10221
|
import fs18 from "fs";
|
|
10104
|
-
import path20 from "path";
|
|
10105
10222
|
import os17 from "os";
|
|
10223
|
+
import path20 from "path";
|
|
10224
|
+
function geminiTmpDir() {
|
|
10225
|
+
return path20.join(os17.homedir(), ".gemini", "tmp");
|
|
10226
|
+
}
|
|
10227
|
+
function geminiPriceFor(model) {
|
|
10228
|
+
let tuple = pricingFor(model);
|
|
10229
|
+
if (!tuple && /^gemini-/i.test(model)) {
|
|
10230
|
+
for (const proxy of GEMINI_FALLBACK_MODELS) {
|
|
10231
|
+
tuple = pricingFor(proxy);
|
|
10232
|
+
if (tuple) break;
|
|
10233
|
+
}
|
|
10234
|
+
}
|
|
10235
|
+
if (!tuple) return null;
|
|
10236
|
+
return { input: tuple[0], output: tuple[1], cacheRead: tuple[3] || tuple[0] };
|
|
10237
|
+
}
|
|
10238
|
+
function safeReaddir2(dir) {
|
|
10239
|
+
try {
|
|
10240
|
+
return fs18.readdirSync(dir);
|
|
10241
|
+
} catch {
|
|
10242
|
+
return [];
|
|
10243
|
+
}
|
|
10244
|
+
}
|
|
10245
|
+
function isDir2(p) {
|
|
10246
|
+
try {
|
|
10247
|
+
return fs18.statSync(p).isDirectory();
|
|
10248
|
+
} catch {
|
|
10249
|
+
return false;
|
|
10250
|
+
}
|
|
10251
|
+
}
|
|
10252
|
+
function listGeminiSessionFiles(base) {
|
|
10253
|
+
const out = [];
|
|
10254
|
+
for (const project of safeReaddir2(base)) {
|
|
10255
|
+
const chats = path20.join(base, project, "chats");
|
|
10256
|
+
if (!isDir2(chats)) continue;
|
|
10257
|
+
for (const f of safeReaddir2(chats)) {
|
|
10258
|
+
if (f.startsWith("session-") && f.endsWith(".jsonl")) {
|
|
10259
|
+
out.push({ file: path20.join(chats, f), project });
|
|
10260
|
+
}
|
|
10261
|
+
}
|
|
10262
|
+
}
|
|
10263
|
+
return out;
|
|
10264
|
+
}
|
|
10265
|
+
function parseGeminiSession(lines, project) {
|
|
10266
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
10267
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
10268
|
+
let runId = "";
|
|
10269
|
+
for (const raw of lines) {
|
|
10270
|
+
if (!raw.trim()) continue;
|
|
10271
|
+
let obj;
|
|
10272
|
+
try {
|
|
10273
|
+
obj = JSON.parse(raw);
|
|
10274
|
+
} catch {
|
|
10275
|
+
continue;
|
|
10276
|
+
}
|
|
10277
|
+
if (typeof obj.sessionId === "string" && !runId) runId = obj.sessionId;
|
|
10278
|
+
if (!obj.tokens || !obj.model || !obj.timestamp) continue;
|
|
10279
|
+
if (obj.id) {
|
|
10280
|
+
if (seenIds.has(obj.id)) continue;
|
|
10281
|
+
seenIds.add(obj.id);
|
|
10282
|
+
}
|
|
10283
|
+
const price = geminiPriceFor(obj.model);
|
|
10284
|
+
if (!price) continue;
|
|
10285
|
+
const inp = obj.tokens.input ?? 0;
|
|
10286
|
+
const out = obj.tokens.output ?? 0;
|
|
10287
|
+
const cached = Math.min(obj.tokens.cached ?? 0, inp);
|
|
10288
|
+
const fresh = Math.max(0, inp - cached);
|
|
10289
|
+
const cost = fresh * price.input + cached * price.cacheRead + out * price.output;
|
|
10290
|
+
const date = obj.timestamp.slice(0, 10);
|
|
10291
|
+
const model = normalizeModel(obj.model);
|
|
10292
|
+
const key = `${date}::${model}`;
|
|
10293
|
+
const prev = byKey.get(key);
|
|
10294
|
+
if (prev) {
|
|
10295
|
+
prev.costUSD += cost;
|
|
10296
|
+
prev.inputTokens += fresh;
|
|
10297
|
+
prev.outputTokens += out;
|
|
10298
|
+
prev.cacheReadTokens += cached;
|
|
10299
|
+
} else {
|
|
10300
|
+
byKey.set(key, {
|
|
10301
|
+
date,
|
|
10302
|
+
model,
|
|
10303
|
+
workingDir: project,
|
|
10304
|
+
runId,
|
|
10305
|
+
costUSD: cost,
|
|
10306
|
+
inputTokens: fresh,
|
|
10307
|
+
outputTokens: out,
|
|
10308
|
+
cacheReadTokens: cached,
|
|
10309
|
+
cacheWriteTokens: 0
|
|
10310
|
+
});
|
|
10311
|
+
}
|
|
10312
|
+
}
|
|
10313
|
+
if (runId) for (const e of byKey.values()) e.runId = runId;
|
|
10314
|
+
return [...byKey.values()];
|
|
10315
|
+
}
|
|
10316
|
+
var GEMINI_FALLBACK_MODELS, geminiSource;
|
|
10317
|
+
var init_cost_gemini = __esm({
|
|
10318
|
+
"src/cost-gemini.ts"() {
|
|
10319
|
+
"use strict";
|
|
10320
|
+
init_litellm();
|
|
10321
|
+
GEMINI_FALLBACK_MODELS = ["gemini-2.5-flash", "gemini-2.0-flash"];
|
|
10322
|
+
geminiSource = {
|
|
10323
|
+
id: "gemini",
|
|
10324
|
+
available() {
|
|
10325
|
+
try {
|
|
10326
|
+
return fs18.existsSync(geminiTmpDir());
|
|
10327
|
+
} catch {
|
|
10328
|
+
return false;
|
|
10329
|
+
}
|
|
10330
|
+
},
|
|
10331
|
+
collect(sinceMs) {
|
|
10332
|
+
const combined = /* @__PURE__ */ new Map();
|
|
10333
|
+
for (const { file, project } of listGeminiSessionFiles(geminiTmpDir())) {
|
|
10334
|
+
try {
|
|
10335
|
+
if (sinceMs !== void 0 && fs18.statSync(file).mtimeMs < sinceMs) continue;
|
|
10336
|
+
} catch {
|
|
10337
|
+
continue;
|
|
10338
|
+
}
|
|
10339
|
+
let content;
|
|
10340
|
+
try {
|
|
10341
|
+
content = fs18.readFileSync(file, "utf8");
|
|
10342
|
+
} catch {
|
|
10343
|
+
continue;
|
|
10344
|
+
}
|
|
10345
|
+
for (const e of parseGeminiSession(content.split("\n"), project)) {
|
|
10346
|
+
const key = `${e.date}::${e.model}::${e.workingDir ?? ""}::${e.runId ?? ""}`;
|
|
10347
|
+
const prev = combined.get(key);
|
|
10348
|
+
if (prev) {
|
|
10349
|
+
prev.costUSD += e.costUSD;
|
|
10350
|
+
prev.inputTokens += e.inputTokens;
|
|
10351
|
+
prev.outputTokens += e.outputTokens;
|
|
10352
|
+
prev.cacheReadTokens += e.cacheReadTokens;
|
|
10353
|
+
prev.cacheWriteTokens += e.cacheWriteTokens;
|
|
10354
|
+
} else {
|
|
10355
|
+
combined.set(key, { ...e });
|
|
10356
|
+
}
|
|
10357
|
+
}
|
|
10358
|
+
}
|
|
10359
|
+
return [...combined.values()];
|
|
10360
|
+
}
|
|
10361
|
+
};
|
|
10362
|
+
}
|
|
10363
|
+
});
|
|
10364
|
+
|
|
10365
|
+
// src/cost-copilot.ts
|
|
10366
|
+
import fs19 from "fs";
|
|
10367
|
+
import os18 from "os";
|
|
10368
|
+
import path21 from "path";
|
|
10369
|
+
function copilotSessionsDir() {
|
|
10370
|
+
return path21.join(os18.homedir(), ".copilot", "session-state");
|
|
10371
|
+
}
|
|
10372
|
+
function safeReaddir3(dir) {
|
|
10373
|
+
try {
|
|
10374
|
+
return fs19.readdirSync(dir);
|
|
10375
|
+
} catch {
|
|
10376
|
+
return [];
|
|
10377
|
+
}
|
|
10378
|
+
}
|
|
10379
|
+
function priceTokens(model, u) {
|
|
10380
|
+
const tuple = pricingFor(model);
|
|
10381
|
+
if (!tuple || !u) return 0;
|
|
10382
|
+
const [pin, pout, pcw, pcr] = tuple;
|
|
10383
|
+
return (u.inputTokens ?? 0) * pin + (u.outputTokens ?? 0) * pout + (u.cacheWriteTokens ?? 0) * pcw + (u.cacheReadTokens ?? 0) * pcr;
|
|
10384
|
+
}
|
|
10385
|
+
function parseCopilotSession(lines) {
|
|
10386
|
+
let sessionId = "";
|
|
10387
|
+
let cwd = "";
|
|
10388
|
+
let startDate = "";
|
|
10389
|
+
let shutdownDate = "";
|
|
10390
|
+
let modelMetrics = null;
|
|
10391
|
+
for (const raw of lines) {
|
|
10392
|
+
if (!raw.trim()) continue;
|
|
10393
|
+
let o;
|
|
10394
|
+
try {
|
|
10395
|
+
o = JSON.parse(raw);
|
|
10396
|
+
} catch {
|
|
10397
|
+
continue;
|
|
10398
|
+
}
|
|
10399
|
+
const d = o.data ?? {};
|
|
10400
|
+
if (o.type === "session.start") {
|
|
10401
|
+
if (typeof d["sessionId"] === "string") sessionId = d["sessionId"];
|
|
10402
|
+
if (typeof d["startTime"] === "string") startDate = d["startTime"];
|
|
10403
|
+
const ctx = d["context"] ?? {};
|
|
10404
|
+
if (typeof ctx["cwd"] === "string") cwd = ctx["cwd"];
|
|
10405
|
+
} else if (o.type === "session.shutdown") {
|
|
10406
|
+
if (d["modelMetrics"] && typeof d["modelMetrics"] === "object") {
|
|
10407
|
+
modelMetrics = d["modelMetrics"];
|
|
10408
|
+
}
|
|
10409
|
+
if (typeof o.timestamp === "string") shutdownDate = o.timestamp;
|
|
10410
|
+
}
|
|
10411
|
+
}
|
|
10412
|
+
if (!modelMetrics) return [];
|
|
10413
|
+
const date = (startDate || shutdownDate).slice(0, 10);
|
|
10414
|
+
if (!date) return [];
|
|
10415
|
+
const rows = [];
|
|
10416
|
+
for (const [rawModel, m] of Object.entries(modelMetrics)) {
|
|
10417
|
+
const u = m.usage ?? {};
|
|
10418
|
+
const inputTokens = u.inputTokens ?? 0;
|
|
10419
|
+
const outputTokens = u.outputTokens ?? 0;
|
|
10420
|
+
const cacheReadTokens = u.cacheReadTokens ?? 0;
|
|
10421
|
+
const cacheWriteTokens = u.cacheWriteTokens ?? 0;
|
|
10422
|
+
if (inputTokens === 0 && outputTokens === 0 && cacheReadTokens === 0 && cacheWriteTokens === 0) {
|
|
10423
|
+
continue;
|
|
10424
|
+
}
|
|
10425
|
+
const model = normalizeModel(rawModel);
|
|
10426
|
+
const costUSD = typeof m.requests?.cost === "number" && m.requests.cost > 0 ? m.requests.cost : priceTokens(rawModel, u);
|
|
10427
|
+
rows.push({
|
|
10428
|
+
date,
|
|
10429
|
+
model,
|
|
10430
|
+
workingDir: cwd,
|
|
10431
|
+
runId: sessionId,
|
|
10432
|
+
costUSD,
|
|
10433
|
+
inputTokens,
|
|
10434
|
+
outputTokens,
|
|
10435
|
+
cacheReadTokens,
|
|
10436
|
+
cacheWriteTokens
|
|
10437
|
+
});
|
|
10438
|
+
}
|
|
10439
|
+
return rows;
|
|
10440
|
+
}
|
|
10441
|
+
var copilotSource;
|
|
10442
|
+
var init_cost_copilot = __esm({
|
|
10443
|
+
"src/cost-copilot.ts"() {
|
|
10444
|
+
"use strict";
|
|
10445
|
+
init_litellm();
|
|
10446
|
+
copilotSource = {
|
|
10447
|
+
id: "copilot",
|
|
10448
|
+
available() {
|
|
10449
|
+
try {
|
|
10450
|
+
return fs19.existsSync(copilotSessionsDir());
|
|
10451
|
+
} catch {
|
|
10452
|
+
return false;
|
|
10453
|
+
}
|
|
10454
|
+
},
|
|
10455
|
+
collect(sinceMs) {
|
|
10456
|
+
const base = copilotSessionsDir();
|
|
10457
|
+
const combined = /* @__PURE__ */ new Map();
|
|
10458
|
+
for (const sid of safeReaddir3(base)) {
|
|
10459
|
+
const file = path21.join(base, sid, "events.jsonl");
|
|
10460
|
+
try {
|
|
10461
|
+
if (sinceMs !== void 0 && fs19.statSync(file).mtimeMs < sinceMs) continue;
|
|
10462
|
+
} catch {
|
|
10463
|
+
continue;
|
|
10464
|
+
}
|
|
10465
|
+
let content;
|
|
10466
|
+
try {
|
|
10467
|
+
content = fs19.readFileSync(file, "utf8");
|
|
10468
|
+
} catch {
|
|
10469
|
+
continue;
|
|
10470
|
+
}
|
|
10471
|
+
for (const e of parseCopilotSession(content.split("\n"))) {
|
|
10472
|
+
const key = `${e.date}::${e.model}::${e.workingDir ?? ""}::${e.runId ?? ""}`;
|
|
10473
|
+
const prev = combined.get(key);
|
|
10474
|
+
if (prev) {
|
|
10475
|
+
prev.costUSD += e.costUSD;
|
|
10476
|
+
prev.inputTokens += e.inputTokens;
|
|
10477
|
+
prev.outputTokens += e.outputTokens;
|
|
10478
|
+
prev.cacheReadTokens += e.cacheReadTokens;
|
|
10479
|
+
prev.cacheWriteTokens += e.cacheWriteTokens;
|
|
10480
|
+
} else {
|
|
10481
|
+
combined.set(key, { ...e });
|
|
10482
|
+
}
|
|
10483
|
+
}
|
|
10484
|
+
}
|
|
10485
|
+
return [...combined.values()];
|
|
10486
|
+
}
|
|
10487
|
+
};
|
|
10488
|
+
}
|
|
10489
|
+
});
|
|
10490
|
+
|
|
10491
|
+
// src/costSync.ts
|
|
10492
|
+
import fs20 from "fs";
|
|
10493
|
+
import path22 from "path";
|
|
10494
|
+
import os19 from "os";
|
|
10106
10495
|
function decodeProjectDirName(dirName) {
|
|
10107
10496
|
return dirName.replace(/-/g, "/");
|
|
10108
10497
|
}
|
|
10109
10498
|
function parseJSONLFile(filePath, fallbackWorkingDir) {
|
|
10110
|
-
const runId =
|
|
10499
|
+
const runId = path22.basename(filePath, ".jsonl");
|
|
10111
10500
|
let content;
|
|
10112
10501
|
try {
|
|
10113
|
-
content =
|
|
10502
|
+
content = fs20.readFileSync(filePath, "utf8");
|
|
10114
10503
|
} catch {
|
|
10115
10504
|
return /* @__PURE__ */ new Map();
|
|
10116
10505
|
}
|
|
@@ -10194,6 +10583,45 @@ function collectEntries(sinceMs) {
|
|
|
10194
10583
|
}
|
|
10195
10584
|
return [...combined.values()];
|
|
10196
10585
|
}
|
|
10586
|
+
function chunk(arr, size) {
|
|
10587
|
+
if (size <= 0) return arr.length ? [arr] : [];
|
|
10588
|
+
const out = [];
|
|
10589
|
+
for (let i = 0; i < arr.length; i += size) out.push(arr.slice(i, i + size));
|
|
10590
|
+
return out;
|
|
10591
|
+
}
|
|
10592
|
+
async function postCostBatches(apiUrl, apiKey, machineId, entries) {
|
|
10593
|
+
for (const batch of chunk(entries, COST_BATCH_SIZE)) {
|
|
10594
|
+
try {
|
|
10595
|
+
const res = await fetch(`${apiUrl}/cost-sync`, {
|
|
10596
|
+
method: "POST",
|
|
10597
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
|
|
10598
|
+
body: JSON.stringify({ machineId, entries: batch }),
|
|
10599
|
+
signal: AbortSignal.timeout(15e3)
|
|
10600
|
+
});
|
|
10601
|
+
if (!res.ok) {
|
|
10602
|
+
fs20.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
|
|
10603
|
+
`);
|
|
10604
|
+
} else {
|
|
10605
|
+
let stored;
|
|
10606
|
+
try {
|
|
10607
|
+
const respBody = typeof res.json === "function" ? await res.json() : null;
|
|
10608
|
+
stored = respBody?.stored;
|
|
10609
|
+
} catch {
|
|
10610
|
+
}
|
|
10611
|
+
if (typeof stored === "number" && stored < batch.length) {
|
|
10612
|
+
fs20.appendFileSync(
|
|
10613
|
+
HOOK_DEBUG_LOG,
|
|
10614
|
+
`[cost-sync] dropped ${batch.length - stored} of ${batch.length} rows
|
|
10615
|
+
`
|
|
10616
|
+
);
|
|
10617
|
+
}
|
|
10618
|
+
}
|
|
10619
|
+
} catch (err2) {
|
|
10620
|
+
fs20.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
|
|
10621
|
+
`);
|
|
10622
|
+
}
|
|
10623
|
+
}
|
|
10624
|
+
}
|
|
10197
10625
|
async function syncCost() {
|
|
10198
10626
|
const creds = getCredentials();
|
|
10199
10627
|
if (!creds?.apiKey || !creds?.apiUrl) return;
|
|
@@ -10202,25 +10630,11 @@ async function syncCost() {
|
|
|
10202
10630
|
if (entries.length === 0) return;
|
|
10203
10631
|
let username = "unknown";
|
|
10204
10632
|
try {
|
|
10205
|
-
username =
|
|
10633
|
+
username = os19.userInfo().username;
|
|
10206
10634
|
} catch {
|
|
10207
10635
|
}
|
|
10208
|
-
const machineId = `${
|
|
10209
|
-
|
|
10210
|
-
const res = await fetch(`${creds.apiUrl}/cost-sync`, {
|
|
10211
|
-
method: "POST",
|
|
10212
|
-
headers: { "Content-Type": "application/json", Authorization: `Bearer ${creds.apiKey}` },
|
|
10213
|
-
body: JSON.stringify({ machineId, entries }),
|
|
10214
|
-
signal: AbortSignal.timeout(15e3)
|
|
10215
|
-
});
|
|
10216
|
-
if (!res.ok) {
|
|
10217
|
-
fs18.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
|
|
10218
|
-
`);
|
|
10219
|
-
}
|
|
10220
|
-
} catch (err2) {
|
|
10221
|
-
fs18.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
|
|
10222
|
-
`);
|
|
10223
|
-
}
|
|
10636
|
+
const machineId = `${os19.hostname()}:${username}`;
|
|
10637
|
+
await postCostBatches(creds.apiUrl, creds.apiKey, machineId, entries);
|
|
10224
10638
|
}
|
|
10225
10639
|
function startCostSync() {
|
|
10226
10640
|
syncCost().catch(() => {
|
|
@@ -10231,7 +10645,7 @@ function startCostSync() {
|
|
|
10231
10645
|
}, SYNC_INTERVAL_MS);
|
|
10232
10646
|
timer.unref();
|
|
10233
10647
|
}
|
|
10234
|
-
var SYNC_INTERVAL_MS, claudeSource, COST_SOURCES;
|
|
10648
|
+
var SYNC_INTERVAL_MS, claudeSource, COST_SOURCES, COST_BATCH_SIZE;
|
|
10235
10649
|
var init_costSync = __esm({
|
|
10236
10650
|
"src/costSync.ts"() {
|
|
10237
10651
|
"use strict";
|
|
@@ -10239,41 +10653,43 @@ var init_costSync = __esm({
|
|
|
10239
10653
|
init_audit();
|
|
10240
10654
|
init_litellm();
|
|
10241
10655
|
init_cost_codex();
|
|
10656
|
+
init_cost_gemini();
|
|
10657
|
+
init_cost_copilot();
|
|
10242
10658
|
SYNC_INTERVAL_MS = 10 * 60 * 1e3;
|
|
10243
10659
|
claudeSource = {
|
|
10244
10660
|
id: "claude",
|
|
10245
10661
|
available() {
|
|
10246
|
-
return
|
|
10662
|
+
return fs20.existsSync(path22.join(os19.homedir(), ".claude", "projects"));
|
|
10247
10663
|
},
|
|
10248
10664
|
collect(sinceMs) {
|
|
10249
|
-
const projectsDir =
|
|
10250
|
-
if (!
|
|
10665
|
+
const projectsDir = path22.join(os19.homedir(), ".claude", "projects");
|
|
10666
|
+
if (!fs20.existsSync(projectsDir)) return [];
|
|
10251
10667
|
const combined = /* @__PURE__ */ new Map();
|
|
10252
10668
|
let dirs;
|
|
10253
10669
|
try {
|
|
10254
|
-
dirs =
|
|
10670
|
+
dirs = fs20.readdirSync(projectsDir);
|
|
10255
10671
|
} catch {
|
|
10256
10672
|
return [];
|
|
10257
10673
|
}
|
|
10258
10674
|
for (const dir of dirs) {
|
|
10259
|
-
const dirPath =
|
|
10675
|
+
const dirPath = path22.join(projectsDir, dir);
|
|
10260
10676
|
try {
|
|
10261
|
-
if (!
|
|
10677
|
+
if (!fs20.statSync(dirPath).isDirectory()) continue;
|
|
10262
10678
|
} catch {
|
|
10263
10679
|
continue;
|
|
10264
10680
|
}
|
|
10265
10681
|
let files;
|
|
10266
10682
|
try {
|
|
10267
|
-
files =
|
|
10683
|
+
files = fs20.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
|
|
10268
10684
|
} catch {
|
|
10269
10685
|
continue;
|
|
10270
10686
|
}
|
|
10271
10687
|
const fallbackWorkingDir = decodeProjectDirName(dir);
|
|
10272
10688
|
for (const file of files) {
|
|
10273
|
-
const filePath =
|
|
10689
|
+
const filePath = path22.join(dirPath, file);
|
|
10274
10690
|
if (sinceMs !== void 0) {
|
|
10275
10691
|
try {
|
|
10276
|
-
if (
|
|
10692
|
+
if (fs20.statSync(filePath).mtimeMs < sinceMs) continue;
|
|
10277
10693
|
} catch {
|
|
10278
10694
|
continue;
|
|
10279
10695
|
}
|
|
@@ -10296,7 +10712,8 @@ var init_costSync = __esm({
|
|
|
10296
10712
|
return [...combined.values()];
|
|
10297
10713
|
}
|
|
10298
10714
|
};
|
|
10299
|
-
COST_SOURCES = [claudeSource, codexSource];
|
|
10715
|
+
COST_SOURCES = [claudeSource, codexSource, geminiSource, copilotSource];
|
|
10716
|
+
COST_BATCH_SIZE = 200;
|
|
10300
10717
|
}
|
|
10301
10718
|
});
|
|
10302
10719
|
|
|
@@ -10312,9 +10729,9 @@ __export(scan_watermark_exports, {
|
|
|
10312
10729
|
tickForensicBroadcast: () => tickForensicBroadcast,
|
|
10313
10730
|
tickScanWatcher: () => tickScanWatcher
|
|
10314
10731
|
});
|
|
10315
|
-
import
|
|
10316
|
-
import
|
|
10317
|
-
import
|
|
10732
|
+
import fs21 from "fs";
|
|
10733
|
+
import os20 from "os";
|
|
10734
|
+
import path23 from "path";
|
|
10318
10735
|
import readline from "readline";
|
|
10319
10736
|
function freshWatermark() {
|
|
10320
10737
|
return {
|
|
@@ -10327,7 +10744,7 @@ function freshWatermark() {
|
|
|
10327
10744
|
function loadWatermark() {
|
|
10328
10745
|
let raw;
|
|
10329
10746
|
try {
|
|
10330
|
-
raw =
|
|
10747
|
+
raw = fs21.readFileSync(WATERMARK_FILE(), "utf-8");
|
|
10331
10748
|
} catch {
|
|
10332
10749
|
return { status: "fresh", wm: freshWatermark() };
|
|
10333
10750
|
}
|
|
@@ -10379,28 +10796,28 @@ function loadWatermark() {
|
|
|
10379
10796
|
function saveWatermark(wm) {
|
|
10380
10797
|
if (wm.schemaVersion > WATERMARK_SCHEMA_VERSION) return;
|
|
10381
10798
|
const target = WATERMARK_FILE();
|
|
10382
|
-
const dir =
|
|
10383
|
-
if (!
|
|
10799
|
+
const dir = path23.dirname(target);
|
|
10800
|
+
if (!fs21.existsSync(dir)) fs21.mkdirSync(dir, { recursive: true });
|
|
10384
10801
|
const tmp = target + ".tmp";
|
|
10385
|
-
|
|
10386
|
-
|
|
10802
|
+
fs21.writeFileSync(tmp, JSON.stringify(wm, null, 2) + "\n", "utf-8");
|
|
10803
|
+
fs21.renameSync(tmp, target);
|
|
10387
10804
|
}
|
|
10388
10805
|
function listJsonlFiles() {
|
|
10389
10806
|
const root = PROJECTS_DIR();
|
|
10390
|
-
if (!
|
|
10807
|
+
if (!fs21.existsSync(root)) return [];
|
|
10391
10808
|
const out = [];
|
|
10392
|
-
for (const entry of
|
|
10809
|
+
for (const entry of fs21.readdirSync(root, { withFileTypes: true })) {
|
|
10393
10810
|
if (!entry.isDirectory()) continue;
|
|
10394
|
-
const projectDir =
|
|
10811
|
+
const projectDir = path23.join(root, entry.name);
|
|
10395
10812
|
let inner;
|
|
10396
10813
|
try {
|
|
10397
|
-
inner =
|
|
10814
|
+
inner = fs21.readdirSync(projectDir, { withFileTypes: true });
|
|
10398
10815
|
} catch {
|
|
10399
10816
|
continue;
|
|
10400
10817
|
}
|
|
10401
10818
|
for (const file of inner) {
|
|
10402
10819
|
if (file.isFile() && file.name.endsWith(".jsonl")) {
|
|
10403
|
-
out.push(
|
|
10820
|
+
out.push(path23.join(projectDir, file.name));
|
|
10404
10821
|
}
|
|
10405
10822
|
}
|
|
10406
10823
|
}
|
|
@@ -10408,7 +10825,7 @@ function listJsonlFiles() {
|
|
|
10408
10825
|
}
|
|
10409
10826
|
function fileSize(p) {
|
|
10410
10827
|
try {
|
|
10411
|
-
return
|
|
10828
|
+
return fs21.statSync(p).size;
|
|
10412
10829
|
} catch {
|
|
10413
10830
|
return 0;
|
|
10414
10831
|
}
|
|
@@ -10416,7 +10833,7 @@ function fileSize(p) {
|
|
|
10416
10833
|
async function scanDelta(filePath, fromByte, onLine) {
|
|
10417
10834
|
const size = fileSize(filePath);
|
|
10418
10835
|
if (size <= fromByte) return fromByte;
|
|
10419
|
-
const stream =
|
|
10836
|
+
const stream = fs21.createReadStream(filePath, {
|
|
10420
10837
|
start: fromByte,
|
|
10421
10838
|
end: size - 1,
|
|
10422
10839
|
highWaterMark: 64 * 1024
|
|
@@ -10528,7 +10945,7 @@ async function tickForensicBroadcast(offsets) {
|
|
|
10528
10945
|
continue;
|
|
10529
10946
|
}
|
|
10530
10947
|
if (size <= offset) continue;
|
|
10531
|
-
const sessionId =
|
|
10948
|
+
const sessionId = path23.basename(file, ".jsonl");
|
|
10532
10949
|
const newOffset = await scanDelta(file, offset, (obj, lineIndex) => {
|
|
10533
10950
|
out.push(...extractFindingsFromLine(obj, sessionId, lineIndex));
|
|
10534
10951
|
});
|
|
@@ -10587,7 +11004,7 @@ function emptyTick(uploadAs) {
|
|
|
10587
11004
|
function readRawWatermarkPreservingOffsets() {
|
|
10588
11005
|
let raw;
|
|
10589
11006
|
try {
|
|
10590
|
-
raw =
|
|
11007
|
+
raw = fs21.readFileSync(WATERMARK_FILE(), "utf-8");
|
|
10591
11008
|
} catch {
|
|
10592
11009
|
return null;
|
|
10593
11010
|
}
|
|
@@ -10621,13 +11038,13 @@ async function runActualTick(wm) {
|
|
|
10621
11038
|
if (!known) {
|
|
10622
11039
|
let mtimeMs = 0;
|
|
10623
11040
|
try {
|
|
10624
|
-
mtimeMs =
|
|
11041
|
+
mtimeMs = fs21.statSync(filePath).mtime.getTime();
|
|
10625
11042
|
} catch {
|
|
10626
11043
|
continue;
|
|
10627
11044
|
}
|
|
10628
11045
|
if (mtimeMs >= watermarkCreatedAt) {
|
|
10629
11046
|
filesNew++;
|
|
10630
|
-
const sessionId2 =
|
|
11047
|
+
const sessionId2 = path23.basename(filePath, ".jsonl");
|
|
10631
11048
|
const newScannedTo2 = await scanDelta(filePath, 0, (obj, lineIndex) => {
|
|
10632
11049
|
totalToolCalls++;
|
|
10633
11050
|
toolCallsBySession[sessionId2] = (toolCallsBySession[sessionId2] ?? 0) + 1;
|
|
@@ -10645,7 +11062,7 @@ async function runActualTick(wm) {
|
|
|
10645
11062
|
filesSkipped++;
|
|
10646
11063
|
continue;
|
|
10647
11064
|
}
|
|
10648
|
-
const sessionId =
|
|
11065
|
+
const sessionId = path23.basename(filePath, ".jsonl");
|
|
10649
11066
|
const newScannedTo = await scanDelta(filePath, known.scannedTo, (obj, lineIndex) => {
|
|
10650
11067
|
totalToolCalls++;
|
|
10651
11068
|
toolCallsBySession[sessionId] = (toolCallsBySession[sessionId] ?? 0) + 1;
|
|
@@ -10673,8 +11090,8 @@ var init_scan_watermark = __esm({
|
|
|
10673
11090
|
"use strict";
|
|
10674
11091
|
init_dlp();
|
|
10675
11092
|
init_dist();
|
|
10676
|
-
PROJECTS_DIR = () =>
|
|
10677
|
-
WATERMARK_FILE = () =>
|
|
11093
|
+
PROJECTS_DIR = () => path23.join(os20.homedir(), ".claude", "projects");
|
|
11094
|
+
WATERMARK_FILE = () => path23.join(os20.homedir(), ".node9", "scan-watermark.json");
|
|
10678
11095
|
MAX_LINE_BYTES = 2 * 1024 * 1024;
|
|
10679
11096
|
WATERMARK_SCHEMA_VERSION = 2;
|
|
10680
11097
|
LONG_OUTPUT_THRESHOLD_BYTES2 = LONG_OUTPUT_THRESHOLD_BYTES;
|
|
@@ -10690,10 +11107,10 @@ __export(scan_upload_history_exports, {
|
|
|
10690
11107
|
parseSinceCutoff: () => parseSinceCutoff,
|
|
10691
11108
|
runUploadHistory: () => runUploadHistory
|
|
10692
11109
|
});
|
|
10693
|
-
import
|
|
11110
|
+
import fs22 from "fs";
|
|
10694
11111
|
import https from "https";
|
|
10695
|
-
import
|
|
10696
|
-
import
|
|
11112
|
+
import os21 from "os";
|
|
11113
|
+
import path24 from "path";
|
|
10697
11114
|
import chalk4 from "chalk";
|
|
10698
11115
|
function emptySignals2() {
|
|
10699
11116
|
return {
|
|
@@ -10728,40 +11145,40 @@ function parseSinceCutoff(raw, now = /* @__PURE__ */ new Date()) {
|
|
|
10728
11145
|
return now.getTime() - 90 * 864e5;
|
|
10729
11146
|
}
|
|
10730
11147
|
function* iterateJsonlFiles(cutoffMs) {
|
|
10731
|
-
const projectsDir =
|
|
11148
|
+
const projectsDir = path24.join(os21.homedir(), ".claude", "projects");
|
|
10732
11149
|
let dirs;
|
|
10733
11150
|
try {
|
|
10734
|
-
dirs =
|
|
11151
|
+
dirs = fs22.readdirSync(projectsDir);
|
|
10735
11152
|
} catch {
|
|
10736
11153
|
return;
|
|
10737
11154
|
}
|
|
10738
11155
|
for (const dir of dirs) {
|
|
10739
|
-
const dirPath =
|
|
11156
|
+
const dirPath = path24.join(projectsDir, dir);
|
|
10740
11157
|
let stats;
|
|
10741
11158
|
try {
|
|
10742
|
-
stats =
|
|
11159
|
+
stats = fs22.statSync(dirPath);
|
|
10743
11160
|
} catch {
|
|
10744
11161
|
continue;
|
|
10745
11162
|
}
|
|
10746
11163
|
if (!stats.isDirectory()) continue;
|
|
10747
11164
|
let files;
|
|
10748
11165
|
try {
|
|
10749
|
-
files =
|
|
11166
|
+
files = fs22.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
|
|
10750
11167
|
} catch {
|
|
10751
11168
|
continue;
|
|
10752
11169
|
}
|
|
10753
11170
|
for (const file of files) {
|
|
10754
|
-
const filePath =
|
|
11171
|
+
const filePath = path24.join(dirPath, file);
|
|
10755
11172
|
let mtime = 0;
|
|
10756
11173
|
try {
|
|
10757
|
-
mtime =
|
|
11174
|
+
mtime = fs22.statSync(filePath).mtimeMs;
|
|
10758
11175
|
} catch {
|
|
10759
11176
|
continue;
|
|
10760
11177
|
}
|
|
10761
11178
|
if (mtime < cutoffMs) continue;
|
|
10762
11179
|
yield {
|
|
10763
11180
|
filePath,
|
|
10764
|
-
sessionId:
|
|
11181
|
+
sessionId: path24.basename(file, ".jsonl"),
|
|
10765
11182
|
projectDir: dir
|
|
10766
11183
|
};
|
|
10767
11184
|
}
|
|
@@ -10815,7 +11232,9 @@ async function runUploadHistory(opts) {
|
|
|
10815
11232
|
let filesScanned = 0;
|
|
10816
11233
|
let linesParsed = 0;
|
|
10817
11234
|
let linesSkipped = 0;
|
|
10818
|
-
const dailyEntries =
|
|
11235
|
+
const dailyEntries = collectEntries(cutoffMs).filter(
|
|
11236
|
+
(e) => cutoffMs === 0 || Date.parse(e.date + "T00:00:00Z") >= cutoffMs
|
|
11237
|
+
);
|
|
10819
11238
|
const liveLoopCfg = getConfig().policy.loopDetection;
|
|
10820
11239
|
const loopCfg = {
|
|
10821
11240
|
enabled: liveLoopCfg.enabled,
|
|
@@ -10827,7 +11246,7 @@ async function runUploadHistory(opts) {
|
|
|
10827
11246
|
filesScanned++;
|
|
10828
11247
|
let content;
|
|
10829
11248
|
try {
|
|
10830
|
-
content =
|
|
11249
|
+
content = fs22.readFileSync(filePath, "utf8");
|
|
10831
11250
|
} catch {
|
|
10832
11251
|
continue;
|
|
10833
11252
|
}
|
|
@@ -10877,14 +11296,6 @@ async function runUploadHistory(opts) {
|
|
|
10877
11296
|
if (sf) findings.push(sf);
|
|
10878
11297
|
}
|
|
10879
11298
|
}
|
|
10880
|
-
const fallbackWorkingDir = decodeProjectDirName(projectDir);
|
|
10881
|
-
const dailyMap = parseJSONLFile(filePath, fallbackWorkingDir);
|
|
10882
|
-
for (const entry of dailyMap.values()) {
|
|
10883
|
-
if (cutoffMs > 0 && Date.parse(entry.date + "T00:00:00Z") < cutoffMs) {
|
|
10884
|
-
continue;
|
|
10885
|
-
}
|
|
10886
|
-
dailyEntries.push(entry);
|
|
10887
|
-
}
|
|
10888
11299
|
}
|
|
10889
11300
|
if (filesScanned === 0) {
|
|
10890
11301
|
console.log(chalk4.yellow(" No JSONL files found in window. Nothing to upload."));
|
|
@@ -10909,10 +11320,10 @@ async function runUploadHistory(opts) {
|
|
|
10909
11320
|
const costUrl = creds.apiUrl.endsWith("/policies/sync") ? creds.apiUrl.replace(/\/policies\/sync$/, "/cost-sync") : `${creds.apiUrl.replace(/\/$/, "")}/cost-sync`;
|
|
10910
11321
|
let username = "unknown";
|
|
10911
11322
|
try {
|
|
10912
|
-
username =
|
|
11323
|
+
username = os21.userInfo().username;
|
|
10913
11324
|
} catch {
|
|
10914
11325
|
}
|
|
10915
|
-
const machineId = `${
|
|
11326
|
+
const machineId = `${os21.hostname()}:${username}`;
|
|
10916
11327
|
await postJson(costUrl, creds.apiKey, {
|
|
10917
11328
|
machineId,
|
|
10918
11329
|
entries: dailyEntries
|
|
@@ -10986,9 +11397,9 @@ var init_scan_upload_history = __esm({
|
|
|
10986
11397
|
|
|
10987
11398
|
// src/cli/commands/scan.ts
|
|
10988
11399
|
import chalk5 from "chalk";
|
|
10989
|
-
import
|
|
10990
|
-
import
|
|
10991
|
-
import
|
|
11400
|
+
import fs23 from "fs";
|
|
11401
|
+
import path25 from "path";
|
|
11402
|
+
import os22 from "os";
|
|
10992
11403
|
import stringWidth2 from "string-width";
|
|
10993
11404
|
function claudeModelPrice(model) {
|
|
10994
11405
|
const base = model.replace(/@.*$/, "").replace(/-\d{8}$/, "");
|
|
@@ -11215,14 +11626,14 @@ function buildRuleSources() {
|
|
|
11215
11626
|
}
|
|
11216
11627
|
function countScanFiles() {
|
|
11217
11628
|
let total = 0;
|
|
11218
|
-
const claudeDir =
|
|
11219
|
-
if (
|
|
11629
|
+
const claudeDir = path25.join(os22.homedir(), ".claude", "projects");
|
|
11630
|
+
if (fs23.existsSync(claudeDir)) {
|
|
11220
11631
|
try {
|
|
11221
|
-
for (const proj of
|
|
11222
|
-
const p =
|
|
11632
|
+
for (const proj of fs23.readdirSync(claudeDir)) {
|
|
11633
|
+
const p = path25.join(claudeDir, proj);
|
|
11223
11634
|
try {
|
|
11224
|
-
if (!
|
|
11225
|
-
total +=
|
|
11635
|
+
if (!fs23.statSync(p).isDirectory()) continue;
|
|
11636
|
+
total += fs23.readdirSync(p).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).length;
|
|
11226
11637
|
} catch {
|
|
11227
11638
|
continue;
|
|
11228
11639
|
}
|
|
@@ -11230,17 +11641,17 @@ function countScanFiles() {
|
|
|
11230
11641
|
} catch {
|
|
11231
11642
|
}
|
|
11232
11643
|
}
|
|
11233
|
-
const geminiDir =
|
|
11234
|
-
if (
|
|
11644
|
+
const geminiDir = path25.join(os22.homedir(), ".gemini", "tmp");
|
|
11645
|
+
if (fs23.existsSync(geminiDir)) {
|
|
11235
11646
|
try {
|
|
11236
|
-
for (const slug of
|
|
11237
|
-
const p =
|
|
11647
|
+
for (const slug of fs23.readdirSync(geminiDir)) {
|
|
11648
|
+
const p = path25.join(geminiDir, slug);
|
|
11238
11649
|
try {
|
|
11239
|
-
if (!
|
|
11240
|
-
const chatsDir =
|
|
11241
|
-
if (
|
|
11650
|
+
if (!fs23.statSync(p).isDirectory()) continue;
|
|
11651
|
+
const chatsDir = path25.join(p, "chats");
|
|
11652
|
+
if (fs23.existsSync(chatsDir)) {
|
|
11242
11653
|
try {
|
|
11243
|
-
total +=
|
|
11654
|
+
total += fs23.readdirSync(chatsDir).filter((f) => f.endsWith(".json")).length;
|
|
11244
11655
|
} catch {
|
|
11245
11656
|
}
|
|
11246
11657
|
}
|
|
@@ -11252,15 +11663,15 @@ function countScanFiles() {
|
|
|
11252
11663
|
}
|
|
11253
11664
|
}
|
|
11254
11665
|
for (const surface of ["antigravity-cli", "antigravity-ide"]) {
|
|
11255
|
-
const brainDir =
|
|
11256
|
-
if (!
|
|
11666
|
+
const brainDir = path25.join(os22.homedir(), ".gemini", surface, "brain");
|
|
11667
|
+
if (!fs23.existsSync(brainDir)) continue;
|
|
11257
11668
|
try {
|
|
11258
|
-
for (const conv of
|
|
11259
|
-
const convPath =
|
|
11669
|
+
for (const conv of fs23.readdirSync(brainDir)) {
|
|
11670
|
+
const convPath = path25.join(brainDir, conv);
|
|
11260
11671
|
try {
|
|
11261
|
-
if (!
|
|
11262
|
-
const logsDir =
|
|
11263
|
-
if (
|
|
11672
|
+
if (!fs23.statSync(convPath).isDirectory()) continue;
|
|
11673
|
+
const logsDir = path25.join(convPath, ".system_generated", "logs");
|
|
11674
|
+
if (fs23.existsSync(path25.join(logsDir, "transcript_full.jsonl")) || fs23.existsSync(path25.join(logsDir, "transcript.jsonl"))) {
|
|
11264
11675
|
total += 1;
|
|
11265
11676
|
}
|
|
11266
11677
|
} catch {
|
|
@@ -11270,31 +11681,31 @@ function countScanFiles() {
|
|
|
11270
11681
|
} catch {
|
|
11271
11682
|
}
|
|
11272
11683
|
}
|
|
11273
|
-
const copilotDir =
|
|
11274
|
-
if (
|
|
11684
|
+
const copilotDir = path25.join(os22.homedir(), ".copilot", "session-state");
|
|
11685
|
+
if (fs23.existsSync(copilotDir)) {
|
|
11275
11686
|
try {
|
|
11276
|
-
for (const sid of
|
|
11277
|
-
if (
|
|
11687
|
+
for (const sid of fs23.readdirSync(copilotDir)) {
|
|
11688
|
+
if (fs23.existsSync(path25.join(copilotDir, sid, "events.jsonl"))) total += 1;
|
|
11278
11689
|
}
|
|
11279
11690
|
} catch {
|
|
11280
11691
|
}
|
|
11281
11692
|
}
|
|
11282
|
-
const codexDir =
|
|
11283
|
-
if (
|
|
11693
|
+
const codexDir = path25.join(os22.homedir(), ".codex", "sessions");
|
|
11694
|
+
if (fs23.existsSync(codexDir)) {
|
|
11284
11695
|
try {
|
|
11285
|
-
for (const year of
|
|
11286
|
-
const yp =
|
|
11696
|
+
for (const year of fs23.readdirSync(codexDir)) {
|
|
11697
|
+
const yp = path25.join(codexDir, year);
|
|
11287
11698
|
try {
|
|
11288
|
-
if (!
|
|
11289
|
-
for (const month of
|
|
11290
|
-
const mp =
|
|
11699
|
+
if (!fs23.statSync(yp).isDirectory()) continue;
|
|
11700
|
+
for (const month of fs23.readdirSync(yp)) {
|
|
11701
|
+
const mp = path25.join(yp, month);
|
|
11291
11702
|
try {
|
|
11292
|
-
if (!
|
|
11293
|
-
for (const day of
|
|
11294
|
-
const dp =
|
|
11703
|
+
if (!fs23.statSync(mp).isDirectory()) continue;
|
|
11704
|
+
for (const day of fs23.readdirSync(mp)) {
|
|
11705
|
+
const dp = path25.join(mp, day);
|
|
11295
11706
|
try {
|
|
11296
|
-
if (!
|
|
11297
|
-
total +=
|
|
11707
|
+
if (!fs23.statSync(dp).isDirectory()) continue;
|
|
11708
|
+
total += fs23.readdirSync(dp).filter((f) => f.endsWith(".jsonl")).length;
|
|
11298
11709
|
} catch {
|
|
11299
11710
|
continue;
|
|
11300
11711
|
}
|
|
@@ -11330,7 +11741,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
11330
11741
|
const sessionId = file.replace(/\.jsonl$/, "");
|
|
11331
11742
|
let raw;
|
|
11332
11743
|
try {
|
|
11333
|
-
raw =
|
|
11744
|
+
raw = fs23.readFileSync(path25.join(projPath, file), "utf-8");
|
|
11334
11745
|
} catch {
|
|
11335
11746
|
return;
|
|
11336
11747
|
}
|
|
@@ -11382,7 +11793,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
11382
11793
|
if (block.type !== "tool_result") continue;
|
|
11383
11794
|
const filePath = block.tool_use_id ? toolUseFilePaths.get(block.tool_use_id) : void 0;
|
|
11384
11795
|
if (filePath) {
|
|
11385
|
-
const ext =
|
|
11796
|
+
const ext = path25.extname(filePath).toLowerCase();
|
|
11386
11797
|
if (CODE_EXTENSIONS.has(ext)) continue;
|
|
11387
11798
|
}
|
|
11388
11799
|
const resultText = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map((c) => c.text ?? "").join("\n") : null;
|
|
@@ -11439,7 +11850,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
11439
11850
|
const rawCmd = String(input.command ?? "").trimStart();
|
|
11440
11851
|
if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd)) continue;
|
|
11441
11852
|
const inputFilePath = typeof input.file_path === "string" ? input.file_path : "";
|
|
11442
|
-
const inputFileExt = inputFilePath ?
|
|
11853
|
+
const inputFileExt = inputFilePath ? path25.extname(inputFilePath).toLowerCase() : "";
|
|
11443
11854
|
if (CODE_EXTENSIONS.has(inputFileExt)) continue;
|
|
11444
11855
|
const dlpMatch = scanArgs(input);
|
|
11445
11856
|
if (dlpMatch) {
|
|
@@ -11536,19 +11947,19 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
11536
11947
|
}
|
|
11537
11948
|
}
|
|
11538
11949
|
function processClaudeProject(proj, projectsDir, ruleSources, startDate, result, dedup, onProgress, onLine) {
|
|
11539
|
-
const projPath =
|
|
11950
|
+
const projPath = path25.join(projectsDir, proj);
|
|
11540
11951
|
try {
|
|
11541
|
-
if (!
|
|
11952
|
+
if (!fs23.statSync(projPath).isDirectory()) return;
|
|
11542
11953
|
} catch {
|
|
11543
11954
|
return;
|
|
11544
11955
|
}
|
|
11545
|
-
const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(
|
|
11956
|
+
const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(os22.homedir(), "~")).slice(
|
|
11546
11957
|
0,
|
|
11547
11958
|
40
|
|
11548
11959
|
);
|
|
11549
11960
|
let files;
|
|
11550
11961
|
try {
|
|
11551
|
-
files =
|
|
11962
|
+
files = fs23.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
11552
11963
|
} catch {
|
|
11553
11964
|
return;
|
|
11554
11965
|
}
|
|
@@ -11582,12 +11993,12 @@ function emptyClaudeScan() {
|
|
|
11582
11993
|
};
|
|
11583
11994
|
}
|
|
11584
11995
|
function scanClaudeHistory(startDate, onProgress, onLine) {
|
|
11585
|
-
const projectsDir =
|
|
11996
|
+
const projectsDir = path25.join(os22.homedir(), ".claude", "projects");
|
|
11586
11997
|
const result = emptyClaudeScan();
|
|
11587
|
-
if (!
|
|
11998
|
+
if (!fs23.existsSync(projectsDir)) return result;
|
|
11588
11999
|
let projDirs;
|
|
11589
12000
|
try {
|
|
11590
|
-
projDirs =
|
|
12001
|
+
projDirs = fs23.readdirSync(projectsDir);
|
|
11591
12002
|
} catch {
|
|
11592
12003
|
return result;
|
|
11593
12004
|
}
|
|
@@ -11608,7 +12019,7 @@ function scanClaudeHistory(startDate, onProgress, onLine) {
|
|
|
11608
12019
|
return result;
|
|
11609
12020
|
}
|
|
11610
12021
|
function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
11611
|
-
const tmpDir =
|
|
12022
|
+
const tmpDir = path25.join(os22.homedir(), ".gemini", "tmp");
|
|
11612
12023
|
const result = {
|
|
11613
12024
|
filesScanned: 0,
|
|
11614
12025
|
sessions: 0,
|
|
@@ -11623,33 +12034,33 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
11623
12034
|
sessionsWithEarlySecrets: 0
|
|
11624
12035
|
};
|
|
11625
12036
|
const dedup = emptyScanDedup();
|
|
11626
|
-
if (!
|
|
12037
|
+
if (!fs23.existsSync(tmpDir)) return result;
|
|
11627
12038
|
let slugDirs;
|
|
11628
12039
|
try {
|
|
11629
|
-
slugDirs =
|
|
12040
|
+
slugDirs = fs23.readdirSync(tmpDir);
|
|
11630
12041
|
} catch {
|
|
11631
12042
|
return result;
|
|
11632
12043
|
}
|
|
11633
12044
|
const ruleSources = buildRuleSources();
|
|
11634
12045
|
for (const slug of slugDirs) {
|
|
11635
|
-
const slugPath =
|
|
12046
|
+
const slugPath = path25.join(tmpDir, slug);
|
|
11636
12047
|
try {
|
|
11637
|
-
if (!
|
|
12048
|
+
if (!fs23.statSync(slugPath).isDirectory()) continue;
|
|
11638
12049
|
} catch {
|
|
11639
12050
|
continue;
|
|
11640
12051
|
}
|
|
11641
12052
|
let projLabel = stripTerminalEscapes(slug).slice(0, 40);
|
|
11642
12053
|
try {
|
|
11643
12054
|
projLabel = stripTerminalEscapes(
|
|
11644
|
-
|
|
11645
|
-
).replace(
|
|
12055
|
+
fs23.readFileSync(path25.join(slugPath, ".project_root"), "utf-8").trim()
|
|
12056
|
+
).replace(os22.homedir(), "~").slice(0, 40);
|
|
11646
12057
|
} catch {
|
|
11647
12058
|
}
|
|
11648
|
-
const chatsDir =
|
|
11649
|
-
if (!
|
|
12059
|
+
const chatsDir = path25.join(slugPath, "chats");
|
|
12060
|
+
if (!fs23.existsSync(chatsDir)) continue;
|
|
11650
12061
|
let chatFiles;
|
|
11651
12062
|
try {
|
|
11652
|
-
chatFiles =
|
|
12063
|
+
chatFiles = fs23.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
|
|
11653
12064
|
} catch {
|
|
11654
12065
|
continue;
|
|
11655
12066
|
}
|
|
@@ -11659,7 +12070,7 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
11659
12070
|
const sessionId = chatFile.replace(/\.json$/, "");
|
|
11660
12071
|
let raw;
|
|
11661
12072
|
try {
|
|
11662
|
-
raw =
|
|
12073
|
+
raw = fs23.readFileSync(path25.join(chatsDir, chatFile), "utf-8");
|
|
11663
12074
|
} catch {
|
|
11664
12075
|
continue;
|
|
11665
12076
|
}
|
|
@@ -11821,13 +12232,13 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
11821
12232
|
return result;
|
|
11822
12233
|
}
|
|
11823
12234
|
function antigravityBrainDirs() {
|
|
11824
|
-
return ["antigravity-cli", "antigravity-ide"].map((surface) =>
|
|
12235
|
+
return ["antigravity-cli", "antigravity-ide"].map((surface) => path25.join(os22.homedir(), ".gemini", surface, "brain")).filter((p) => fs23.existsSync(p));
|
|
11825
12236
|
}
|
|
11826
12237
|
function antigravityTranscriptPath(convPath) {
|
|
11827
|
-
const logsDir =
|
|
12238
|
+
const logsDir = path25.join(convPath, ".system_generated", "logs");
|
|
11828
12239
|
for (const name of ["transcript_full.jsonl", "transcript.jsonl"]) {
|
|
11829
|
-
const p =
|
|
11830
|
-
if (
|
|
12240
|
+
const p = path25.join(logsDir, name);
|
|
12241
|
+
if (fs23.existsSync(p)) return p;
|
|
11831
12242
|
}
|
|
11832
12243
|
return null;
|
|
11833
12244
|
}
|
|
@@ -11853,14 +12264,14 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
11853
12264
|
for (const brainDir of brainDirs) {
|
|
11854
12265
|
let convDirs;
|
|
11855
12266
|
try {
|
|
11856
|
-
convDirs =
|
|
12267
|
+
convDirs = fs23.readdirSync(brainDir);
|
|
11857
12268
|
} catch {
|
|
11858
12269
|
continue;
|
|
11859
12270
|
}
|
|
11860
12271
|
for (const conv of convDirs) {
|
|
11861
|
-
const convPath =
|
|
12272
|
+
const convPath = path25.join(brainDir, conv);
|
|
11862
12273
|
try {
|
|
11863
|
-
if (!
|
|
12274
|
+
if (!fs23.statSync(convPath).isDirectory()) continue;
|
|
11864
12275
|
} catch {
|
|
11865
12276
|
continue;
|
|
11866
12277
|
}
|
|
@@ -11870,7 +12281,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
11870
12281
|
onProgress?.(result.filesScanned);
|
|
11871
12282
|
let raw;
|
|
11872
12283
|
try {
|
|
11873
|
-
raw =
|
|
12284
|
+
raw = fs23.readFileSync(transcriptFile, "utf-8");
|
|
11874
12285
|
} catch {
|
|
11875
12286
|
continue;
|
|
11876
12287
|
}
|
|
@@ -11927,7 +12338,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
11927
12338
|
result.bashCalls++;
|
|
11928
12339
|
const cwd = String(input.cwd ?? "");
|
|
11929
12340
|
if (cwd && projLabel === conv.slice(0, 8)) {
|
|
11930
|
-
projLabel = stripTerminalEscapes(cwd).replace(
|
|
12341
|
+
projLabel = stripTerminalEscapes(cwd).replace(os22.homedir(), "~").slice(0, 40);
|
|
11931
12342
|
}
|
|
11932
12343
|
}
|
|
11933
12344
|
const rawCmd = String(input.command ?? "").trimStart();
|
|
@@ -12027,7 +12438,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
12027
12438
|
return result;
|
|
12028
12439
|
}
|
|
12029
12440
|
function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
12030
|
-
const sessionDir =
|
|
12441
|
+
const sessionDir = path25.join(os22.homedir(), ".copilot", "session-state");
|
|
12031
12442
|
const result = {
|
|
12032
12443
|
filesScanned: 0,
|
|
12033
12444
|
sessions: 0,
|
|
@@ -12043,22 +12454,22 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
12043
12454
|
sessionsWithEarlySecrets: 0
|
|
12044
12455
|
};
|
|
12045
12456
|
const dedup = emptyScanDedup();
|
|
12046
|
-
if (!
|
|
12457
|
+
if (!fs23.existsSync(sessionDir)) return result;
|
|
12047
12458
|
let sessionIds;
|
|
12048
12459
|
try {
|
|
12049
|
-
sessionIds =
|
|
12460
|
+
sessionIds = fs23.readdirSync(sessionDir);
|
|
12050
12461
|
} catch {
|
|
12051
12462
|
return result;
|
|
12052
12463
|
}
|
|
12053
12464
|
const ruleSources = buildRuleSources();
|
|
12054
12465
|
for (const sessionId of sessionIds) {
|
|
12055
|
-
const eventsPath =
|
|
12056
|
-
if (!
|
|
12466
|
+
const eventsPath = path25.join(sessionDir, sessionId, "events.jsonl");
|
|
12467
|
+
if (!fs23.existsSync(eventsPath)) continue;
|
|
12057
12468
|
result.filesScanned++;
|
|
12058
12469
|
onProgress?.(result.filesScanned);
|
|
12059
12470
|
let raw;
|
|
12060
12471
|
try {
|
|
12061
|
-
raw =
|
|
12472
|
+
raw = fs23.readFileSync(eventsPath, "utf-8");
|
|
12062
12473
|
} catch {
|
|
12063
12474
|
continue;
|
|
12064
12475
|
}
|
|
@@ -12078,7 +12489,7 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
12078
12489
|
if (ev.type === "session.start") {
|
|
12079
12490
|
const cwd = ev.data?.context?.cwd;
|
|
12080
12491
|
if (typeof cwd === "string" && cwd) {
|
|
12081
|
-
projLabel = stripTerminalEscapes(cwd).replace(
|
|
12492
|
+
projLabel = stripTerminalEscapes(cwd).replace(os22.homedir(), "~").slice(0, 40);
|
|
12082
12493
|
}
|
|
12083
12494
|
continue;
|
|
12084
12495
|
}
|
|
@@ -12210,7 +12621,7 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
12210
12621
|
return result;
|
|
12211
12622
|
}
|
|
12212
12623
|
function scanCodexHistory(startDate, onProgress, onLine) {
|
|
12213
|
-
const sessionsBase =
|
|
12624
|
+
const sessionsBase = path25.join(os22.homedir(), ".codex", "sessions");
|
|
12214
12625
|
const result = {
|
|
12215
12626
|
filesScanned: 0,
|
|
12216
12627
|
sessions: 0,
|
|
@@ -12225,32 +12636,32 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
12225
12636
|
sessionsWithEarlySecrets: 0
|
|
12226
12637
|
};
|
|
12227
12638
|
const dedup = emptyScanDedup();
|
|
12228
|
-
if (!
|
|
12639
|
+
if (!fs23.existsSync(sessionsBase)) return result;
|
|
12229
12640
|
const jsonlFiles = [];
|
|
12230
12641
|
try {
|
|
12231
|
-
for (const year of
|
|
12232
|
-
const yearPath =
|
|
12642
|
+
for (const year of fs23.readdirSync(sessionsBase)) {
|
|
12643
|
+
const yearPath = path25.join(sessionsBase, year);
|
|
12233
12644
|
try {
|
|
12234
|
-
if (!
|
|
12645
|
+
if (!fs23.statSync(yearPath).isDirectory()) continue;
|
|
12235
12646
|
} catch {
|
|
12236
12647
|
continue;
|
|
12237
12648
|
}
|
|
12238
|
-
for (const month of
|
|
12239
|
-
const monthPath =
|
|
12649
|
+
for (const month of fs23.readdirSync(yearPath)) {
|
|
12650
|
+
const monthPath = path25.join(yearPath, month);
|
|
12240
12651
|
try {
|
|
12241
|
-
if (!
|
|
12652
|
+
if (!fs23.statSync(monthPath).isDirectory()) continue;
|
|
12242
12653
|
} catch {
|
|
12243
12654
|
continue;
|
|
12244
12655
|
}
|
|
12245
|
-
for (const day of
|
|
12246
|
-
const dayPath =
|
|
12656
|
+
for (const day of fs23.readdirSync(monthPath)) {
|
|
12657
|
+
const dayPath = path25.join(monthPath, day);
|
|
12247
12658
|
try {
|
|
12248
|
-
if (!
|
|
12659
|
+
if (!fs23.statSync(dayPath).isDirectory()) continue;
|
|
12249
12660
|
} catch {
|
|
12250
12661
|
continue;
|
|
12251
12662
|
}
|
|
12252
|
-
for (const file of
|
|
12253
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(
|
|
12663
|
+
for (const file of fs23.readdirSync(dayPath)) {
|
|
12664
|
+
if (file.endsWith(".jsonl")) jsonlFiles.push(path25.join(dayPath, file));
|
|
12254
12665
|
}
|
|
12255
12666
|
}
|
|
12256
12667
|
}
|
|
@@ -12264,7 +12675,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
12264
12675
|
onProgress?.(result.filesScanned);
|
|
12265
12676
|
let lines;
|
|
12266
12677
|
try {
|
|
12267
|
-
lines =
|
|
12678
|
+
lines = fs23.readFileSync(filePath, "utf-8").split("\n");
|
|
12268
12679
|
} catch {
|
|
12269
12680
|
continue;
|
|
12270
12681
|
}
|
|
@@ -12290,7 +12701,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
12290
12701
|
sessionId = String(payload["id"] ?? filePath);
|
|
12291
12702
|
startTime = String(payload["timestamp"] ?? "");
|
|
12292
12703
|
const cwd = String(payload["cwd"] ?? "");
|
|
12293
|
-
projLabel = stripTerminalEscapes(cwd.replace(
|
|
12704
|
+
projLabel = stripTerminalEscapes(cwd.replace(os22.homedir(), "~")).slice(0, 40);
|
|
12294
12705
|
continue;
|
|
12295
12706
|
}
|
|
12296
12707
|
if (entry.type === "event_msg" && payload["type"] === "token_count") {
|
|
@@ -12443,17 +12854,17 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
12443
12854
|
return result;
|
|
12444
12855
|
}
|
|
12445
12856
|
function scanShellConfig() {
|
|
12446
|
-
const home =
|
|
12857
|
+
const home = os22.homedir();
|
|
12447
12858
|
const configFiles = [".zshrc", ".bashrc", ".bash_profile", ".profile"].map(
|
|
12448
|
-
(f) =>
|
|
12859
|
+
(f) => path25.join(home, f)
|
|
12449
12860
|
);
|
|
12450
12861
|
const findings = [];
|
|
12451
12862
|
const seen = /* @__PURE__ */ new Set();
|
|
12452
12863
|
for (const filePath of configFiles) {
|
|
12453
|
-
if (!
|
|
12864
|
+
if (!fs23.existsSync(filePath)) continue;
|
|
12454
12865
|
let lines;
|
|
12455
12866
|
try {
|
|
12456
|
-
lines =
|
|
12867
|
+
lines = fs23.readFileSync(filePath, "utf-8").split("\n");
|
|
12457
12868
|
} catch {
|
|
12458
12869
|
continue;
|
|
12459
12870
|
}
|
|
@@ -13259,7 +13670,7 @@ function registerScanCommand(program2) {
|
|
|
13259
13670
|
if (!drillDown) {
|
|
13260
13671
|
const useInk2 = !options.classic;
|
|
13261
13672
|
if (useInk2) {
|
|
13262
|
-
const scanInkPath =
|
|
13673
|
+
const scanInkPath = path25.join(__dirname, "scan-ink.mjs");
|
|
13263
13674
|
const dynamicImport = new Function("id", "return import(id)");
|
|
13264
13675
|
const mod = await dynamicImport(`file://${scanInkPath}`);
|
|
13265
13676
|
const rangeLabel2 = options.all ? "all time" : `last ${options.days ?? 90} days`;
|
|
@@ -13680,8 +14091,8 @@ var init_suggestion_tracker = __esm({
|
|
|
13680
14091
|
});
|
|
13681
14092
|
|
|
13682
14093
|
// src/daemon/taint-store.ts
|
|
13683
|
-
import
|
|
13684
|
-
import
|
|
14094
|
+
import fs24 from "fs";
|
|
14095
|
+
import path26 from "path";
|
|
13685
14096
|
var DEFAULT_TTL_MS, TaintStore;
|
|
13686
14097
|
var init_taint_store = __esm({
|
|
13687
14098
|
"src/daemon/taint-store.ts"() {
|
|
@@ -13750,9 +14161,9 @@ var init_taint_store = __esm({
|
|
|
13750
14161
|
/** Resolve to absolute path, falling back to path.resolve if file doesn't exist yet. */
|
|
13751
14162
|
_resolve(filePath) {
|
|
13752
14163
|
try {
|
|
13753
|
-
return
|
|
14164
|
+
return fs24.realpathSync.native(path26.resolve(filePath));
|
|
13754
14165
|
} catch {
|
|
13755
|
-
return
|
|
14166
|
+
return path26.resolve(filePath);
|
|
13756
14167
|
}
|
|
13757
14168
|
}
|
|
13758
14169
|
};
|
|
@@ -13869,14 +14280,14 @@ var init_session_history = __esm({
|
|
|
13869
14280
|
|
|
13870
14281
|
// src/daemon/state.ts
|
|
13871
14282
|
import net2 from "net";
|
|
13872
|
-
import
|
|
13873
|
-
import
|
|
13874
|
-
import
|
|
14283
|
+
import fs25 from "fs";
|
|
14284
|
+
import path27 from "path";
|
|
14285
|
+
import os23 from "os";
|
|
13875
14286
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
13876
14287
|
function loadInsightCounts() {
|
|
13877
14288
|
try {
|
|
13878
|
-
if (!
|
|
13879
|
-
const data = JSON.parse(
|
|
14289
|
+
if (!fs25.existsSync(INSIGHT_COUNTS_FILE)) return;
|
|
14290
|
+
const data = JSON.parse(fs25.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
|
|
13880
14291
|
for (const [tool, count] of Object.entries(data)) {
|
|
13881
14292
|
if (typeof count === "number" && count > 0) insightCounts.set(tool, count);
|
|
13882
14293
|
}
|
|
@@ -13915,23 +14326,23 @@ function markRejectionHandlerRegistered() {
|
|
|
13915
14326
|
daemonRejectionHandlerRegistered = true;
|
|
13916
14327
|
}
|
|
13917
14328
|
function atomicWriteSync2(filePath, data, options) {
|
|
13918
|
-
const dir =
|
|
13919
|
-
if (!
|
|
14329
|
+
const dir = path27.dirname(filePath);
|
|
14330
|
+
if (!fs25.existsSync(dir)) fs25.mkdirSync(dir, { recursive: true });
|
|
13920
14331
|
const tmpPath = `${filePath}.${randomUUID3()}.tmp`;
|
|
13921
14332
|
try {
|
|
13922
|
-
|
|
14333
|
+
fs25.writeFileSync(tmpPath, data, options);
|
|
13923
14334
|
} catch (err2) {
|
|
13924
14335
|
try {
|
|
13925
|
-
|
|
14336
|
+
fs25.unlinkSync(tmpPath);
|
|
13926
14337
|
} catch {
|
|
13927
14338
|
}
|
|
13928
14339
|
throw err2;
|
|
13929
14340
|
}
|
|
13930
14341
|
try {
|
|
13931
|
-
|
|
14342
|
+
fs25.renameSync(tmpPath, filePath);
|
|
13932
14343
|
} catch (err2) {
|
|
13933
14344
|
try {
|
|
13934
|
-
|
|
14345
|
+
fs25.unlinkSync(tmpPath);
|
|
13935
14346
|
} catch {
|
|
13936
14347
|
}
|
|
13937
14348
|
throw err2;
|
|
@@ -13955,16 +14366,16 @@ function appendAuditLog(data) {
|
|
|
13955
14366
|
decision: data.decision,
|
|
13956
14367
|
source: "daemon"
|
|
13957
14368
|
};
|
|
13958
|
-
const dir =
|
|
13959
|
-
if (!
|
|
13960
|
-
|
|
14369
|
+
const dir = path27.dirname(AUDIT_LOG_FILE);
|
|
14370
|
+
if (!fs25.existsSync(dir)) fs25.mkdirSync(dir, { recursive: true });
|
|
14371
|
+
fs25.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
|
|
13961
14372
|
} catch {
|
|
13962
14373
|
}
|
|
13963
14374
|
}
|
|
13964
14375
|
function getAuditHistory(limit = 20) {
|
|
13965
14376
|
try {
|
|
13966
|
-
if (!
|
|
13967
|
-
const lines =
|
|
14377
|
+
if (!fs25.existsSync(AUDIT_LOG_FILE)) return [];
|
|
14378
|
+
const lines = fs25.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
|
|
13968
14379
|
if (lines.length === 1 && lines[0] === "") return [];
|
|
13969
14380
|
return lines.slice(-limit).map((l) => JSON.parse(l)).reverse();
|
|
13970
14381
|
} catch {
|
|
@@ -13973,7 +14384,7 @@ function getAuditHistory(limit = 20) {
|
|
|
13973
14384
|
}
|
|
13974
14385
|
function getOrgName() {
|
|
13975
14386
|
try {
|
|
13976
|
-
if (
|
|
14387
|
+
if (fs25.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
|
|
13977
14388
|
} catch {
|
|
13978
14389
|
}
|
|
13979
14390
|
return null;
|
|
@@ -13981,8 +14392,8 @@ function getOrgName() {
|
|
|
13981
14392
|
function writeGlobalSetting(key, value) {
|
|
13982
14393
|
let config = {};
|
|
13983
14394
|
try {
|
|
13984
|
-
if (
|
|
13985
|
-
config = JSON.parse(
|
|
14395
|
+
if (fs25.existsSync(GLOBAL_CONFIG_FILE)) {
|
|
14396
|
+
config = JSON.parse(fs25.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
|
|
13986
14397
|
}
|
|
13987
14398
|
} catch {
|
|
13988
14399
|
}
|
|
@@ -13994,8 +14405,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
|
|
|
13994
14405
|
try {
|
|
13995
14406
|
let trust = { entries: [] };
|
|
13996
14407
|
try {
|
|
13997
|
-
if (
|
|
13998
|
-
trust = JSON.parse(
|
|
14408
|
+
if (fs25.existsSync(TRUST_FILE2))
|
|
14409
|
+
trust = JSON.parse(fs25.readFileSync(TRUST_FILE2, "utf-8"));
|
|
13999
14410
|
} catch {
|
|
14000
14411
|
}
|
|
14001
14412
|
trust.entries = trust.entries.filter(
|
|
@@ -14012,8 +14423,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
|
|
|
14012
14423
|
}
|
|
14013
14424
|
function readPersistentDecisions() {
|
|
14014
14425
|
try {
|
|
14015
|
-
if (
|
|
14016
|
-
return JSON.parse(
|
|
14426
|
+
if (fs25.existsSync(DECISIONS_FILE)) {
|
|
14427
|
+
return JSON.parse(fs25.readFileSync(DECISIONS_FILE, "utf-8"));
|
|
14017
14428
|
}
|
|
14018
14429
|
} catch {
|
|
14019
14430
|
}
|
|
@@ -14030,7 +14441,7 @@ function writePersistentDecision(toolName, decision) {
|
|
|
14030
14441
|
function readBody(req) {
|
|
14031
14442
|
return new Promise((resolve) => {
|
|
14032
14443
|
let body = "";
|
|
14033
|
-
req.on("data", (
|
|
14444
|
+
req.on("data", (chunk2) => body += chunk2);
|
|
14034
14445
|
req.on("end", () => resolve(body));
|
|
14035
14446
|
});
|
|
14036
14447
|
}
|
|
@@ -14041,7 +14452,7 @@ function estimateToolCost(tool, args) {
|
|
|
14041
14452
|
const filePath = a.file_path ?? a.path;
|
|
14042
14453
|
if (filePath) {
|
|
14043
14454
|
try {
|
|
14044
|
-
const bytes =
|
|
14455
|
+
const bytes = fs25.statSync(filePath).size;
|
|
14045
14456
|
return bytes / BYTES_PER_TOKEN / 1e6 * INPUT_PRICE_PER_1M;
|
|
14046
14457
|
} catch {
|
|
14047
14458
|
}
|
|
@@ -14112,7 +14523,7 @@ function abandonPending() {
|
|
|
14112
14523
|
});
|
|
14113
14524
|
if (autoStarted) {
|
|
14114
14525
|
try {
|
|
14115
|
-
|
|
14526
|
+
fs25.unlinkSync(DAEMON_PID_FILE);
|
|
14116
14527
|
} catch {
|
|
14117
14528
|
}
|
|
14118
14529
|
setTimeout(() => {
|
|
@@ -14123,8 +14534,8 @@ function abandonPending() {
|
|
|
14123
14534
|
}
|
|
14124
14535
|
function logActivitySocket(msg) {
|
|
14125
14536
|
try {
|
|
14126
|
-
|
|
14127
|
-
|
|
14537
|
+
fs25.appendFileSync(
|
|
14538
|
+
path27.join(homeDir, ".node9", "hook-debug.log"),
|
|
14128
14539
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] [activity-socket] ${msg}
|
|
14129
14540
|
`
|
|
14130
14541
|
);
|
|
@@ -14146,13 +14557,13 @@ function shouldRebind(now = Date.now()) {
|
|
|
14146
14557
|
function startActivitySocket() {
|
|
14147
14558
|
bindActivitySocket();
|
|
14148
14559
|
activityHealthInterval = setInterval(() => {
|
|
14149
|
-
if (!
|
|
14560
|
+
if (!fs25.existsSync(ACTIVITY_SOCKET_PATH2)) attemptRebind("health-probe");
|
|
14150
14561
|
}, ACTIVITY_HEALTH_PROBE_MS);
|
|
14151
14562
|
activityHealthInterval.unref();
|
|
14152
14563
|
process.on("exit", () => {
|
|
14153
14564
|
if (activityHealthInterval) clearInterval(activityHealthInterval);
|
|
14154
14565
|
try {
|
|
14155
|
-
|
|
14566
|
+
fs25.unlinkSync(ACTIVITY_SOCKET_PATH2);
|
|
14156
14567
|
} catch {
|
|
14157
14568
|
}
|
|
14158
14569
|
});
|
|
@@ -14180,20 +14591,20 @@ function attemptRebind(reason) {
|
|
|
14180
14591
|
}
|
|
14181
14592
|
function bindActivitySocket() {
|
|
14182
14593
|
try {
|
|
14183
|
-
|
|
14594
|
+
fs25.unlinkSync(ACTIVITY_SOCKET_PATH2);
|
|
14184
14595
|
} catch {
|
|
14185
14596
|
}
|
|
14186
14597
|
const ACTIVITY_MAX_BYTES = 1024 * 1024;
|
|
14187
14598
|
const unixServer = net2.createServer((socket) => {
|
|
14188
14599
|
const chunks = [];
|
|
14189
14600
|
let bytesReceived = 0;
|
|
14190
|
-
socket.on("data", (
|
|
14191
|
-
bytesReceived +=
|
|
14601
|
+
socket.on("data", (chunk2) => {
|
|
14602
|
+
bytesReceived += chunk2.length;
|
|
14192
14603
|
if (bytesReceived > ACTIVITY_MAX_BYTES) {
|
|
14193
14604
|
socket.destroy();
|
|
14194
14605
|
return;
|
|
14195
14606
|
}
|
|
14196
|
-
chunks.push(
|
|
14607
|
+
chunks.push(chunk2);
|
|
14197
14608
|
});
|
|
14198
14609
|
socket.on("end", () => {
|
|
14199
14610
|
try {
|
|
@@ -14293,14 +14704,14 @@ var init_state2 = __esm({
|
|
|
14293
14704
|
init_taint_store();
|
|
14294
14705
|
init_session_counters();
|
|
14295
14706
|
init_session_history();
|
|
14296
|
-
homeDir =
|
|
14297
|
-
DAEMON_PID_FILE =
|
|
14298
|
-
DECISIONS_FILE =
|
|
14299
|
-
AUDIT_LOG_FILE =
|
|
14300
|
-
TRUST_FILE2 =
|
|
14301
|
-
GLOBAL_CONFIG_FILE =
|
|
14302
|
-
CREDENTIALS_FILE =
|
|
14303
|
-
INSIGHT_COUNTS_FILE =
|
|
14707
|
+
homeDir = os23.homedir();
|
|
14708
|
+
DAEMON_PID_FILE = path27.join(homeDir, ".node9", "daemon.pid");
|
|
14709
|
+
DECISIONS_FILE = path27.join(homeDir, ".node9", "decisions.json");
|
|
14710
|
+
AUDIT_LOG_FILE = path27.join(homeDir, ".node9", "audit.log");
|
|
14711
|
+
TRUST_FILE2 = path27.join(homeDir, ".node9", "trust.json");
|
|
14712
|
+
GLOBAL_CONFIG_FILE = path27.join(homeDir, ".node9", "config.json");
|
|
14713
|
+
CREDENTIALS_FILE = path27.join(homeDir, ".node9", "credentials.json");
|
|
14714
|
+
INSIGHT_COUNTS_FILE = path27.join(homeDir, ".node9", "insight-counts.json");
|
|
14304
14715
|
pending = /* @__PURE__ */ new Map();
|
|
14305
14716
|
sseClients = /* @__PURE__ */ new Set();
|
|
14306
14717
|
suggestionTracker = new SuggestionTracker(3);
|
|
@@ -14317,7 +14728,7 @@ var init_state2 = __esm({
|
|
|
14317
14728
|
"2h": 2 * 60 * 6e4
|
|
14318
14729
|
};
|
|
14319
14730
|
autoStarted = process.env.NODE9_AUTO_STARTED === "1";
|
|
14320
|
-
ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" :
|
|
14731
|
+
ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path27.join(os23.tmpdir(), "node9-activity.sock");
|
|
14321
14732
|
ACTIVITY_RING_SIZE = 100;
|
|
14322
14733
|
activityRing = [];
|
|
14323
14734
|
LARGE_RESPONSE_RING_SIZE = 20;
|
|
@@ -14356,10 +14767,10 @@ var init_state2 = __esm({
|
|
|
14356
14767
|
});
|
|
14357
14768
|
|
|
14358
14769
|
// src/daemon/sync.ts
|
|
14359
|
-
import
|
|
14770
|
+
import fs26 from "fs";
|
|
14360
14771
|
import https2 from "https";
|
|
14361
|
-
import
|
|
14362
|
-
import
|
|
14772
|
+
import os24 from "os";
|
|
14773
|
+
import path28 from "path";
|
|
14363
14774
|
function emptySignals3() {
|
|
14364
14775
|
return {
|
|
14365
14776
|
dlpFindings: 0,
|
|
@@ -14399,8 +14810,8 @@ function readCredentials() {
|
|
|
14399
14810
|
};
|
|
14400
14811
|
}
|
|
14401
14812
|
try {
|
|
14402
|
-
const credPath =
|
|
14403
|
-
const creds = JSON.parse(
|
|
14813
|
+
const credPath = path28.join(os24.homedir(), ".node9", "credentials.json");
|
|
14814
|
+
const creds = JSON.parse(fs26.readFileSync(credPath, "utf-8"));
|
|
14404
14815
|
const profileName = process.env.NODE9_PROFILE ?? "default";
|
|
14405
14816
|
const profile = creds[profileName];
|
|
14406
14817
|
if (typeof profile?.apiKey === "string" && profile.apiKey.length > 0) {
|
|
@@ -14426,7 +14837,7 @@ function readCredentials() {
|
|
|
14426
14837
|
}
|
|
14427
14838
|
function readCachedEtag() {
|
|
14428
14839
|
try {
|
|
14429
|
-
const raw = JSON.parse(
|
|
14840
|
+
const raw = JSON.parse(fs26.readFileSync(rulesCacheFile(), "utf-8"));
|
|
14430
14841
|
return typeof raw.etag === "string" ? raw.etag : void 0;
|
|
14431
14842
|
} catch {
|
|
14432
14843
|
return void 0;
|
|
@@ -14456,7 +14867,7 @@ function fetchCloudPolicy(apiKey, apiUrl, ifNoneMatch) {
|
|
|
14456
14867
|
return;
|
|
14457
14868
|
}
|
|
14458
14869
|
const chunks = [];
|
|
14459
|
-
res.on("data", (
|
|
14870
|
+
res.on("data", (chunk2) => chunks.push(chunk2));
|
|
14460
14871
|
res.on("end", () => {
|
|
14461
14872
|
if (res.statusCode !== 200) {
|
|
14462
14873
|
reject(new Error(`API returned ${res.statusCode ?? "unknown"}`));
|
|
@@ -14487,9 +14898,9 @@ function extractRules(body) {
|
|
|
14487
14898
|
return [];
|
|
14488
14899
|
}
|
|
14489
14900
|
function writeCache2(cache) {
|
|
14490
|
-
const dir =
|
|
14491
|
-
if (!
|
|
14492
|
-
|
|
14901
|
+
const dir = path28.dirname(rulesCacheFile());
|
|
14902
|
+
if (!fs26.existsSync(dir)) fs26.mkdirSync(dir, { recursive: true });
|
|
14903
|
+
fs26.writeFileSync(rulesCacheFile(), JSON.stringify(cache, null, 2) + "\n", "utf-8");
|
|
14493
14904
|
}
|
|
14494
14905
|
async function syncOnce() {
|
|
14495
14906
|
const creds = readCredentials();
|
|
@@ -14646,7 +15057,7 @@ async function runCloudSync() {
|
|
|
14646
15057
|
}
|
|
14647
15058
|
function getCloudSyncStatus() {
|
|
14648
15059
|
try {
|
|
14649
|
-
const raw = JSON.parse(
|
|
15060
|
+
const raw = JSON.parse(fs26.readFileSync(rulesCacheFile(), "utf-8"));
|
|
14650
15061
|
if (!Array.isArray(raw.rules) || typeof raw.fetchedAt !== "string") return { cached: false };
|
|
14651
15062
|
return {
|
|
14652
15063
|
cached: true,
|
|
@@ -14663,7 +15074,7 @@ function getCloudSyncStatus() {
|
|
|
14663
15074
|
}
|
|
14664
15075
|
function getCloudRules() {
|
|
14665
15076
|
try {
|
|
14666
|
-
const raw = JSON.parse(
|
|
15077
|
+
const raw = JSON.parse(fs26.readFileSync(rulesCacheFile(), "utf-8"));
|
|
14667
15078
|
return Array.isArray(raw.rules) ? raw.rules : null;
|
|
14668
15079
|
} catch {
|
|
14669
15080
|
return null;
|
|
@@ -14719,7 +15130,7 @@ var init_sync = __esm({
|
|
|
14719
15130
|
loop: "loops",
|
|
14720
15131
|
"long-output-redacted": "longOutputRedactions"
|
|
14721
15132
|
};
|
|
14722
|
-
rulesCacheFile = () =>
|
|
15133
|
+
rulesCacheFile = () => path28.join(os24.homedir(), ".node9", "rules-cache.json");
|
|
14723
15134
|
DEFAULT_API_URL = "https://api.node9.ai/api/v1/intercept/policies/sync";
|
|
14724
15135
|
DEFAULT_INTERVAL_HOURS = 5;
|
|
14725
15136
|
MIN_INTERVAL_HOURS = 1;
|
|
@@ -14742,26 +15153,26 @@ __export(audit_shipper_exports, {
|
|
|
14742
15153
|
startAuditShipper: () => startAuditShipper,
|
|
14743
15154
|
writeWatermark: () => writeWatermark
|
|
14744
15155
|
});
|
|
14745
|
-
import
|
|
14746
|
-
import
|
|
14747
|
-
import
|
|
15156
|
+
import fs27 from "fs";
|
|
15157
|
+
import path29 from "path";
|
|
15158
|
+
import os25 from "os";
|
|
14748
15159
|
import crypto5 from "crypto";
|
|
14749
15160
|
function fileSignature(filePath) {
|
|
14750
|
-
const fd =
|
|
15161
|
+
const fd = fs27.openSync(filePath, "r");
|
|
14751
15162
|
try {
|
|
14752
15163
|
const buf = Buffer.alloc(512);
|
|
14753
|
-
const read =
|
|
15164
|
+
const read = fs27.readSync(fd, buf, 0, 512, 0);
|
|
14754
15165
|
const slice = buf.subarray(0, read);
|
|
14755
15166
|
const nl = slice.indexOf(10);
|
|
14756
15167
|
const firstLine = nl === -1 ? slice : slice.subarray(0, nl);
|
|
14757
15168
|
return crypto5.createHash("sha256").update(firstLine).digest("hex").slice(0, 16);
|
|
14758
15169
|
} finally {
|
|
14759
|
-
|
|
15170
|
+
fs27.closeSync(fd);
|
|
14760
15171
|
}
|
|
14761
15172
|
}
|
|
14762
15173
|
function readWatermark(watermarkPath) {
|
|
14763
15174
|
try {
|
|
14764
|
-
const raw = JSON.parse(
|
|
15175
|
+
const raw = JSON.parse(fs27.readFileSync(watermarkPath, "utf-8"));
|
|
14765
15176
|
if (typeof raw.fileSig === "string" && typeof raw.offset === "number" && raw.offset >= 0)
|
|
14766
15177
|
return raw;
|
|
14767
15178
|
} catch {
|
|
@@ -14770,13 +15181,13 @@ function readWatermark(watermarkPath) {
|
|
|
14770
15181
|
}
|
|
14771
15182
|
function writeWatermark(watermarkPath, wm) {
|
|
14772
15183
|
const tmp = `${watermarkPath}.tmp`;
|
|
14773
|
-
|
|
14774
|
-
|
|
15184
|
+
fs27.writeFileSync(tmp, JSON.stringify(wm));
|
|
15185
|
+
fs27.renameSync(tmp, watermarkPath);
|
|
14775
15186
|
}
|
|
14776
|
-
function buildWireRows(
|
|
14777
|
-
const lastNl =
|
|
15187
|
+
function buildWireRows(chunk2) {
|
|
15188
|
+
const lastNl = chunk2.lastIndexOf(10);
|
|
14778
15189
|
if (lastNl === -1) return { rows: [], consumed: 0 };
|
|
14779
|
-
const complete =
|
|
15190
|
+
const complete = chunk2.subarray(0, lastNl + 1);
|
|
14780
15191
|
const rows = [];
|
|
14781
15192
|
for (const line of complete.toString("utf-8").split("\n")) {
|
|
14782
15193
|
if (!line.trim()) continue;
|
|
@@ -14810,7 +15221,10 @@ function buildWireRows(chunk) {
|
|
|
14810
15221
|
...typeof parsed.sessionId === "string" ? { sessionId: parsed.sessionId } : {},
|
|
14811
15222
|
...typeof parsed.dlpPattern === "string" ? { dlpPattern: parsed.dlpPattern } : {},
|
|
14812
15223
|
...typeof parsed.dlpSample === "string" ? { dlpSample: parsed.dlpSample } : {},
|
|
14813
|
-
...cloudRequestId ? { cloudRequestId } : {}
|
|
15224
|
+
...cloudRequestId ? { cloudRequestId } : {},
|
|
15225
|
+
...typeof parsed.workingDir === "string" ? { workingDir: parsed.workingDir } : {},
|
|
15226
|
+
...typeof parsed.platform === "string" ? { platform: parsed.platform } : {},
|
|
15227
|
+
...typeof parsed.shellType === "string" ? { shellType: parsed.shellType } : {}
|
|
14814
15228
|
});
|
|
14815
15229
|
}
|
|
14816
15230
|
return { rows, consumed: lastNl + 1 };
|
|
@@ -14839,11 +15253,11 @@ async function shipOnce(deps = {}) {
|
|
|
14839
15253
|
if (!creds?.apiKey) return { status: "no-creds", shipped: 0 };
|
|
14840
15254
|
const endpoint = buildBatchEndpoint(creds.apiUrl);
|
|
14841
15255
|
if (!endpoint) return { status: "no-creds", shipped: 0 };
|
|
14842
|
-
if (!
|
|
15256
|
+
if (!fs27.existsSync(auditLogPath)) return { status: "idle", shipped: 0 };
|
|
14843
15257
|
let shipped = 0;
|
|
14844
15258
|
try {
|
|
14845
15259
|
for (let chunkN = 0; chunkN < MAX_CHUNKS_PER_TICK; chunkN++) {
|
|
14846
|
-
const size =
|
|
15260
|
+
const size = fs27.statSync(auditLogPath).size;
|
|
14847
15261
|
if (size === 0) break;
|
|
14848
15262
|
const sig = fileSignature(auditLogPath);
|
|
14849
15263
|
const wm = readWatermark(watermarkPath);
|
|
@@ -14851,12 +15265,12 @@ async function shipOnce(deps = {}) {
|
|
|
14851
15265
|
if (offset >= size) break;
|
|
14852
15266
|
const toRead = Math.min(size - offset, MAX_CHUNK_BYTES);
|
|
14853
15267
|
const buf = Buffer.alloc(toRead);
|
|
14854
|
-
const fd =
|
|
15268
|
+
const fd = fs27.openSync(auditLogPath, "r");
|
|
14855
15269
|
let read;
|
|
14856
15270
|
try {
|
|
14857
|
-
read =
|
|
15271
|
+
read = fs27.readSync(fd, buf, 0, toRead, offset);
|
|
14858
15272
|
} finally {
|
|
14859
|
-
|
|
15273
|
+
fs27.closeSync(fd);
|
|
14860
15274
|
}
|
|
14861
15275
|
const { rows, consumed } = buildWireRows(buf.subarray(0, read));
|
|
14862
15276
|
if (consumed === 0) break;
|
|
@@ -14903,8 +15317,8 @@ async function shipOnce(deps = {}) {
|
|
|
14903
15317
|
}
|
|
14904
15318
|
function shipLagBytes(auditLogPath = LOCAL_AUDIT_LOG, watermarkPath = AUDIT_SHIP_WATERMARK) {
|
|
14905
15319
|
try {
|
|
14906
|
-
if (!
|
|
14907
|
-
const size =
|
|
15320
|
+
if (!fs27.existsSync(auditLogPath)) return 0;
|
|
15321
|
+
const size = fs27.statSync(auditLogPath).size;
|
|
14908
15322
|
const wm = readWatermark(watermarkPath);
|
|
14909
15323
|
if (!wm) return size;
|
|
14910
15324
|
if (wm.fileSig !== fileSignature(auditLogPath)) return size;
|
|
@@ -14935,7 +15349,7 @@ var init_audit_shipper = __esm({
|
|
|
14935
15349
|
init_config();
|
|
14936
15350
|
init_sync();
|
|
14937
15351
|
init_cloud();
|
|
14938
|
-
AUDIT_SHIP_WATERMARK =
|
|
15352
|
+
AUDIT_SHIP_WATERMARK = path29.join(os25.homedir(), ".node9", "audit-ship.json");
|
|
14939
15353
|
DEFAULT_INTERVAL_MS = 2e4;
|
|
14940
15354
|
MAX_BATCH = 500;
|
|
14941
15355
|
MAX_CHUNK_BYTES = 4 * 1024 * 1024;
|
|
@@ -14947,75 +15361,75 @@ var init_audit_shipper = __esm({
|
|
|
14947
15361
|
});
|
|
14948
15362
|
|
|
14949
15363
|
// src/daemon/dlp-scanner.ts
|
|
14950
|
-
import
|
|
14951
|
-
import
|
|
14952
|
-
import
|
|
15364
|
+
import fs28 from "fs";
|
|
15365
|
+
import path30 from "path";
|
|
15366
|
+
import os26 from "os";
|
|
14953
15367
|
function loadIndex() {
|
|
14954
15368
|
try {
|
|
14955
|
-
return JSON.parse(
|
|
15369
|
+
return JSON.parse(fs28.readFileSync(INDEX_FILE, "utf-8"));
|
|
14956
15370
|
} catch {
|
|
14957
15371
|
return {};
|
|
14958
15372
|
}
|
|
14959
15373
|
}
|
|
14960
15374
|
function saveIndex(index) {
|
|
14961
15375
|
try {
|
|
14962
|
-
|
|
15376
|
+
fs28.writeFileSync(INDEX_FILE, JSON.stringify(index), { encoding: "utf-8", mode: 384 });
|
|
14963
15377
|
} catch {
|
|
14964
15378
|
}
|
|
14965
15379
|
}
|
|
14966
15380
|
function appendAuditEntry(entry) {
|
|
14967
15381
|
try {
|
|
14968
|
-
|
|
15382
|
+
fs28.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
|
|
14969
15383
|
} catch {
|
|
14970
15384
|
}
|
|
14971
15385
|
}
|
|
14972
15386
|
function runDlpScan() {
|
|
14973
|
-
if (!
|
|
15387
|
+
if (!fs28.existsSync(PROJECTS_DIR2)) return;
|
|
14974
15388
|
const index = loadIndex();
|
|
14975
15389
|
let updated = false;
|
|
14976
15390
|
let projDirs;
|
|
14977
15391
|
try {
|
|
14978
|
-
projDirs =
|
|
15392
|
+
projDirs = fs28.readdirSync(PROJECTS_DIR2);
|
|
14979
15393
|
} catch {
|
|
14980
15394
|
return;
|
|
14981
15395
|
}
|
|
14982
15396
|
for (const proj of projDirs) {
|
|
14983
|
-
const projPath =
|
|
15397
|
+
const projPath = path30.join(PROJECTS_DIR2, proj);
|
|
14984
15398
|
try {
|
|
14985
|
-
if (!
|
|
14986
|
-
const real =
|
|
14987
|
-
if (!real.startsWith(PROJECTS_DIR2 +
|
|
15399
|
+
if (!fs28.lstatSync(projPath).isDirectory()) continue;
|
|
15400
|
+
const real = fs28.realpathSync(projPath);
|
|
15401
|
+
if (!real.startsWith(PROJECTS_DIR2 + path30.sep) && real !== PROJECTS_DIR2) continue;
|
|
14988
15402
|
} catch {
|
|
14989
15403
|
continue;
|
|
14990
15404
|
}
|
|
14991
15405
|
let files;
|
|
14992
15406
|
try {
|
|
14993
|
-
files =
|
|
15407
|
+
files = fs28.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
14994
15408
|
} catch {
|
|
14995
15409
|
continue;
|
|
14996
15410
|
}
|
|
14997
15411
|
for (const file of files) {
|
|
14998
|
-
const filePath =
|
|
15412
|
+
const filePath = path30.join(projPath, file);
|
|
14999
15413
|
const lastOffset = index[filePath] ?? 0;
|
|
15000
15414
|
let size;
|
|
15001
15415
|
try {
|
|
15002
|
-
size =
|
|
15416
|
+
size = fs28.statSync(filePath).size;
|
|
15003
15417
|
} catch {
|
|
15004
15418
|
continue;
|
|
15005
15419
|
}
|
|
15006
15420
|
if (size <= lastOffset) continue;
|
|
15007
15421
|
let fd;
|
|
15008
15422
|
try {
|
|
15009
|
-
fd =
|
|
15423
|
+
fd = fs28.openSync(filePath, "r");
|
|
15010
15424
|
} catch {
|
|
15011
15425
|
continue;
|
|
15012
15426
|
}
|
|
15013
15427
|
try {
|
|
15014
15428
|
const chunkSize = size - lastOffset;
|
|
15015
15429
|
const buf = Buffer.alloc(chunkSize);
|
|
15016
|
-
|
|
15017
|
-
const
|
|
15018
|
-
for (const line of
|
|
15430
|
+
fs28.readSync(fd, buf, 0, chunkSize, lastOffset);
|
|
15431
|
+
const chunk2 = buf.toString("utf-8");
|
|
15432
|
+
for (const line of chunk2.split("\n")) {
|
|
15019
15433
|
if (!line.trim()) continue;
|
|
15020
15434
|
let entry;
|
|
15021
15435
|
try {
|
|
@@ -15033,7 +15447,7 @@ function runDlpScan() {
|
|
|
15033
15447
|
if (typeof text !== "string") continue;
|
|
15034
15448
|
const match = scanText(text);
|
|
15035
15449
|
if (!match) continue;
|
|
15036
|
-
const projLabel = decodeURIComponent(proj).replace(
|
|
15450
|
+
const projLabel = decodeURIComponent(proj).replace(os26.homedir(), "~").slice(0, 40);
|
|
15037
15451
|
const ts = entry.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
15038
15452
|
appendAuditEntry({
|
|
15039
15453
|
ts,
|
|
@@ -15058,7 +15472,7 @@ Run: node9 report --period 30d`
|
|
|
15058
15472
|
updated = true;
|
|
15059
15473
|
} finally {
|
|
15060
15474
|
try {
|
|
15061
|
-
|
|
15475
|
+
fs28.closeSync(fd);
|
|
15062
15476
|
} catch {
|
|
15063
15477
|
}
|
|
15064
15478
|
}
|
|
@@ -15091,23 +15505,23 @@ var init_dlp_scanner = __esm({
|
|
|
15091
15505
|
init_dlp();
|
|
15092
15506
|
init_native();
|
|
15093
15507
|
init_state2();
|
|
15094
|
-
INDEX_FILE =
|
|
15095
|
-
PROJECTS_DIR2 =
|
|
15508
|
+
INDEX_FILE = path30.join(os26.homedir(), ".node9", "dlp-index.json");
|
|
15509
|
+
PROJECTS_DIR2 = path30.join(os26.homedir(), ".claude", "projects");
|
|
15096
15510
|
}
|
|
15097
15511
|
});
|
|
15098
15512
|
|
|
15099
15513
|
// src/daemon/mcp-tools.ts
|
|
15100
|
-
import
|
|
15101
|
-
import
|
|
15102
|
-
import
|
|
15514
|
+
import fs29 from "fs";
|
|
15515
|
+
import path31 from "path";
|
|
15516
|
+
import os27 from "os";
|
|
15103
15517
|
function getMcpToolsFile() {
|
|
15104
|
-
return
|
|
15518
|
+
return path31.join(os27.homedir(), ".node9", "mcp-tools.json");
|
|
15105
15519
|
}
|
|
15106
15520
|
function readMcpToolsConfig() {
|
|
15107
15521
|
try {
|
|
15108
15522
|
const file = getMcpToolsFile();
|
|
15109
|
-
if (!
|
|
15110
|
-
const raw =
|
|
15523
|
+
if (!fs29.existsSync(file)) return {};
|
|
15524
|
+
const raw = fs29.readFileSync(file, "utf-8");
|
|
15111
15525
|
return JSON.parse(raw);
|
|
15112
15526
|
} catch {
|
|
15113
15527
|
return {};
|
|
@@ -15116,11 +15530,11 @@ function readMcpToolsConfig() {
|
|
|
15116
15530
|
function writeMcpToolsConfig(config) {
|
|
15117
15531
|
try {
|
|
15118
15532
|
const file = getMcpToolsFile();
|
|
15119
|
-
const dir =
|
|
15120
|
-
if (!
|
|
15121
|
-
const tmpPath = `${file}.${
|
|
15122
|
-
|
|
15123
|
-
|
|
15533
|
+
const dir = path31.dirname(file);
|
|
15534
|
+
if (!fs29.existsSync(dir)) fs29.mkdirSync(dir, { recursive: true });
|
|
15535
|
+
const tmpPath = `${file}.${os27.hostname()}.${process.pid}.tmp`;
|
|
15536
|
+
fs29.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
|
|
15537
|
+
fs29.renameSync(tmpPath, file);
|
|
15124
15538
|
} catch (e) {
|
|
15125
15539
|
console.error("Failed to write mcp-tools.json", e);
|
|
15126
15540
|
}
|
|
@@ -15167,9 +15581,9 @@ var init_mcp_tools = __esm({
|
|
|
15167
15581
|
|
|
15168
15582
|
// src/daemon/server.ts
|
|
15169
15583
|
import http from "http";
|
|
15170
|
-
import
|
|
15171
|
-
import
|
|
15172
|
-
import
|
|
15584
|
+
import fs30 from "fs";
|
|
15585
|
+
import path32 from "path";
|
|
15586
|
+
import os28 from "os";
|
|
15173
15587
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
15174
15588
|
import { spawnSync } from "child_process";
|
|
15175
15589
|
import chalk6 from "chalk";
|
|
@@ -15191,7 +15605,7 @@ function startDaemon() {
|
|
|
15191
15605
|
idleTimer = setTimeout(() => {
|
|
15192
15606
|
if (autoStarted) {
|
|
15193
15607
|
try {
|
|
15194
|
-
|
|
15608
|
+
fs30.unlinkSync(DAEMON_PID_FILE);
|
|
15195
15609
|
} catch {
|
|
15196
15610
|
}
|
|
15197
15611
|
}
|
|
@@ -15336,7 +15750,7 @@ data: ${JSON.stringify(item.data)}
|
|
|
15336
15750
|
mcpServer: entry.mcpServer
|
|
15337
15751
|
});
|
|
15338
15752
|
}
|
|
15339
|
-
const projectCwd = typeof cwd === "string" &&
|
|
15753
|
+
const projectCwd = typeof cwd === "string" && path32.isAbsolute(cwd) ? cwd : void 0;
|
|
15340
15754
|
const projectConfig = getConfig(projectCwd);
|
|
15341
15755
|
const browserEnabled = projectConfig.settings.approvers?.browser !== false;
|
|
15342
15756
|
const terminalEnabled = projectConfig.settings.approvers?.terminal !== false;
|
|
@@ -15628,8 +16042,8 @@ data: ${JSON.stringify(item.data)}
|
|
|
15628
16042
|
if (!validToken(req)) return res.writeHead(403).end();
|
|
15629
16043
|
const periodParam = reqUrl.searchParams.get("period") || "7d";
|
|
15630
16044
|
const period = ["today", "7d", "30d", "month"].includes(periodParam) ? periodParam : "7d";
|
|
15631
|
-
const logPath =
|
|
15632
|
-
if (!
|
|
16045
|
+
const logPath = path32.join(os28.homedir(), ".node9", "audit.log");
|
|
16046
|
+
if (!fs30.existsSync(logPath)) {
|
|
15633
16047
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
15634
16048
|
return res.end(
|
|
15635
16049
|
JSON.stringify({
|
|
@@ -15642,7 +16056,7 @@ data: ${JSON.stringify(item.data)}
|
|
|
15642
16056
|
);
|
|
15643
16057
|
}
|
|
15644
16058
|
try {
|
|
15645
|
-
const raw =
|
|
16059
|
+
const raw = fs30.readFileSync(logPath, "utf-8");
|
|
15646
16060
|
const allEntries = raw.split("\n").flatMap((line) => {
|
|
15647
16061
|
if (!line.trim()) return [];
|
|
15648
16062
|
try {
|
|
@@ -15965,14 +16379,14 @@ data: ${JSON.stringify(item.data)}
|
|
|
15965
16379
|
server.on("error", (e) => {
|
|
15966
16380
|
if (e.code === "EADDRINUSE") {
|
|
15967
16381
|
try {
|
|
15968
|
-
if (
|
|
15969
|
-
const { pid } = JSON.parse(
|
|
16382
|
+
if (fs30.existsSync(DAEMON_PID_FILE)) {
|
|
16383
|
+
const { pid } = JSON.parse(fs30.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
15970
16384
|
process.kill(pid, 0);
|
|
15971
16385
|
return process.exit(0);
|
|
15972
16386
|
}
|
|
15973
16387
|
} catch {
|
|
15974
16388
|
try {
|
|
15975
|
-
|
|
16389
|
+
fs30.unlinkSync(DAEMON_PID_FILE);
|
|
15976
16390
|
} catch {
|
|
15977
16391
|
}
|
|
15978
16392
|
server.listen(DAEMON_PORT, DAEMON_HOST);
|
|
@@ -16061,15 +16475,15 @@ var init_server = __esm({
|
|
|
16061
16475
|
});
|
|
16062
16476
|
|
|
16063
16477
|
// src/daemon/service.ts
|
|
16064
|
-
import
|
|
16065
|
-
import
|
|
16066
|
-
import
|
|
16478
|
+
import fs31 from "fs";
|
|
16479
|
+
import path33 from "path";
|
|
16480
|
+
import os29 from "os";
|
|
16067
16481
|
import { spawnSync as spawnSync2, execFileSync } from "child_process";
|
|
16068
16482
|
function resolveNode9Binary() {
|
|
16069
16483
|
try {
|
|
16070
16484
|
const script = process.argv[1];
|
|
16071
|
-
if (typeof script === "string" &&
|
|
16072
|
-
return
|
|
16485
|
+
if (typeof script === "string" && path33.isAbsolute(script) && fs31.existsSync(script)) {
|
|
16486
|
+
return fs31.realpathSync(script);
|
|
16073
16487
|
}
|
|
16074
16488
|
} catch {
|
|
16075
16489
|
}
|
|
@@ -16087,11 +16501,11 @@ function xmlEscape(s) {
|
|
|
16087
16501
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
16088
16502
|
}
|
|
16089
16503
|
function launchdPlist(binaryPath) {
|
|
16090
|
-
const logDir =
|
|
16504
|
+
const logDir = path33.join(os29.homedir(), ".node9");
|
|
16091
16505
|
const nodePath = xmlEscape(process.execPath);
|
|
16092
16506
|
const scriptPath = xmlEscape(binaryPath);
|
|
16093
|
-
const outLog = xmlEscape(
|
|
16094
|
-
const errLog = xmlEscape(
|
|
16507
|
+
const outLog = xmlEscape(path33.join(logDir, "daemon.log"));
|
|
16508
|
+
const errLog = xmlEscape(path33.join(logDir, "daemon-error.log"));
|
|
16095
16509
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
16096
16510
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
16097
16511
|
<plist version="1.0">
|
|
@@ -16124,9 +16538,9 @@ function launchdPlist(binaryPath) {
|
|
|
16124
16538
|
`;
|
|
16125
16539
|
}
|
|
16126
16540
|
function installLaunchd(binaryPath) {
|
|
16127
|
-
const dir =
|
|
16128
|
-
if (!
|
|
16129
|
-
|
|
16541
|
+
const dir = path33.dirname(LAUNCHD_PLIST);
|
|
16542
|
+
if (!fs31.existsSync(dir)) fs31.mkdirSync(dir, { recursive: true });
|
|
16543
|
+
fs31.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
|
|
16130
16544
|
spawnSync2("launchctl", ["unload", LAUNCHD_PLIST], { encoding: "utf8" });
|
|
16131
16545
|
const r = spawnSync2("launchctl", ["load", "-w", LAUNCHD_PLIST], {
|
|
16132
16546
|
encoding: "utf8",
|
|
@@ -16137,13 +16551,13 @@ function installLaunchd(binaryPath) {
|
|
|
16137
16551
|
}
|
|
16138
16552
|
}
|
|
16139
16553
|
function uninstallLaunchd() {
|
|
16140
|
-
if (
|
|
16554
|
+
if (fs31.existsSync(LAUNCHD_PLIST)) {
|
|
16141
16555
|
spawnSync2("launchctl", ["unload", "-w", LAUNCHD_PLIST], { encoding: "utf8", timeout: 5e3 });
|
|
16142
|
-
|
|
16556
|
+
fs31.unlinkSync(LAUNCHD_PLIST);
|
|
16143
16557
|
}
|
|
16144
16558
|
}
|
|
16145
16559
|
function isLaunchdInstalled() {
|
|
16146
|
-
return
|
|
16560
|
+
return fs31.existsSync(LAUNCHD_PLIST);
|
|
16147
16561
|
}
|
|
16148
16562
|
function systemdUnit(binaryPath) {
|
|
16149
16563
|
return `[Unit]
|
|
@@ -16162,12 +16576,12 @@ WantedBy=default.target
|
|
|
16162
16576
|
`;
|
|
16163
16577
|
}
|
|
16164
16578
|
function installSystemd(binaryPath) {
|
|
16165
|
-
if (!
|
|
16166
|
-
|
|
16579
|
+
if (!fs31.existsSync(SYSTEMD_UNIT_DIR)) {
|
|
16580
|
+
fs31.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
|
|
16167
16581
|
}
|
|
16168
|
-
|
|
16582
|
+
fs31.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
|
|
16169
16583
|
try {
|
|
16170
|
-
execFileSync("loginctl", ["enable-linger",
|
|
16584
|
+
execFileSync("loginctl", ["enable-linger", os29.userInfo().username], { timeout: 3e3 });
|
|
16171
16585
|
} catch {
|
|
16172
16586
|
}
|
|
16173
16587
|
const reload = spawnSync2("systemctl", ["--user", "daemon-reload"], {
|
|
@@ -16187,23 +16601,23 @@ function installSystemd(binaryPath) {
|
|
|
16187
16601
|
}
|
|
16188
16602
|
}
|
|
16189
16603
|
function uninstallSystemd() {
|
|
16190
|
-
if (
|
|
16604
|
+
if (fs31.existsSync(SYSTEMD_UNIT)) {
|
|
16191
16605
|
spawnSync2("systemctl", ["--user", "disable", "--now", "node9-daemon"], {
|
|
16192
16606
|
encoding: "utf8",
|
|
16193
16607
|
timeout: 5e3
|
|
16194
16608
|
});
|
|
16195
16609
|
spawnSync2("systemctl", ["--user", "daemon-reload"], { encoding: "utf8", timeout: 5e3 });
|
|
16196
|
-
|
|
16610
|
+
fs31.unlinkSync(SYSTEMD_UNIT);
|
|
16197
16611
|
}
|
|
16198
16612
|
}
|
|
16199
16613
|
function isSystemdInstalled() {
|
|
16200
|
-
return
|
|
16614
|
+
return fs31.existsSync(SYSTEMD_UNIT);
|
|
16201
16615
|
}
|
|
16202
16616
|
function stopRunningDaemon() {
|
|
16203
|
-
const pidFile =
|
|
16204
|
-
if (!
|
|
16617
|
+
const pidFile = path33.join(os29.homedir(), ".node9", "daemon.pid");
|
|
16618
|
+
if (!fs31.existsSync(pidFile)) return;
|
|
16205
16619
|
try {
|
|
16206
|
-
const data = JSON.parse(
|
|
16620
|
+
const data = JSON.parse(fs31.readFileSync(pidFile, "utf-8"));
|
|
16207
16621
|
const pid = data.pid;
|
|
16208
16622
|
const MAX_PID2 = 4194304;
|
|
16209
16623
|
if (typeof pid === "number" && Number.isInteger(pid) && pid > 0 && pid <= MAX_PID2) {
|
|
@@ -16223,7 +16637,7 @@ function stopRunningDaemon() {
|
|
|
16223
16637
|
}
|
|
16224
16638
|
}
|
|
16225
16639
|
try {
|
|
16226
|
-
|
|
16640
|
+
fs31.unlinkSync(pidFile);
|
|
16227
16641
|
} catch {
|
|
16228
16642
|
}
|
|
16229
16643
|
} catch {
|
|
@@ -16298,19 +16712,19 @@ var init_service = __esm({
|
|
|
16298
16712
|
"src/daemon/service.ts"() {
|
|
16299
16713
|
"use strict";
|
|
16300
16714
|
LAUNCHD_LABEL = "ai.node9.daemon";
|
|
16301
|
-
LAUNCHD_PLIST =
|
|
16302
|
-
SYSTEMD_UNIT_DIR =
|
|
16303
|
-
SYSTEMD_UNIT =
|
|
16715
|
+
LAUNCHD_PLIST = path33.join(os29.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
|
|
16716
|
+
SYSTEMD_UNIT_DIR = path33.join(os29.homedir(), ".config", "systemd", "user");
|
|
16717
|
+
SYSTEMD_UNIT = path33.join(SYSTEMD_UNIT_DIR, "node9-daemon.service");
|
|
16304
16718
|
}
|
|
16305
16719
|
});
|
|
16306
16720
|
|
|
16307
16721
|
// src/daemon/index.ts
|
|
16308
|
-
import
|
|
16722
|
+
import fs32 from "fs";
|
|
16309
16723
|
import chalk7 from "chalk";
|
|
16310
16724
|
function stopDaemon() {
|
|
16311
|
-
if (!
|
|
16725
|
+
if (!fs32.existsSync(DAEMON_PID_FILE)) return console.log(chalk7.yellow("Not running."));
|
|
16312
16726
|
try {
|
|
16313
|
-
const data = JSON.parse(
|
|
16727
|
+
const data = JSON.parse(fs32.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
16314
16728
|
const pid = data.pid;
|
|
16315
16729
|
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
|
|
16316
16730
|
console.log(chalk7.gray("Cleaned up invalid PID file."));
|
|
@@ -16322,7 +16736,7 @@ function stopDaemon() {
|
|
|
16322
16736
|
console.log(chalk7.gray("Cleaned up stale PID file."));
|
|
16323
16737
|
} finally {
|
|
16324
16738
|
try {
|
|
16325
|
-
|
|
16739
|
+
fs32.unlinkSync(DAEMON_PID_FILE);
|
|
16326
16740
|
} catch {
|
|
16327
16741
|
}
|
|
16328
16742
|
}
|
|
@@ -16331,9 +16745,9 @@ function daemonStatus() {
|
|
|
16331
16745
|
const serviceInstalled = isDaemonServiceInstalled();
|
|
16332
16746
|
const serviceLabel = serviceInstalled ? chalk7.green("installed (starts on login)") : chalk7.yellow("not installed \u2014 run: node9 daemon install");
|
|
16333
16747
|
let processStatus;
|
|
16334
|
-
if (
|
|
16748
|
+
if (fs32.existsSync(DAEMON_PID_FILE)) {
|
|
16335
16749
|
try {
|
|
16336
|
-
const data = JSON.parse(
|
|
16750
|
+
const data = JSON.parse(fs32.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
16337
16751
|
const pid = data.pid;
|
|
16338
16752
|
const port = data.port;
|
|
16339
16753
|
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
|
|
@@ -16378,9 +16792,9 @@ __export(tail_exports, {
|
|
|
16378
16792
|
});
|
|
16379
16793
|
import http2 from "http";
|
|
16380
16794
|
import chalk29 from "chalk";
|
|
16381
|
-
import
|
|
16382
|
-
import
|
|
16383
|
-
import
|
|
16795
|
+
import fs50 from "fs";
|
|
16796
|
+
import os45 from "os";
|
|
16797
|
+
import path51 from "path";
|
|
16384
16798
|
import readline6 from "readline";
|
|
16385
16799
|
import { spawn as spawn8 } from "child_process";
|
|
16386
16800
|
function shortenPathSummary(s) {
|
|
@@ -16404,20 +16818,20 @@ function getModelContextLimit(model) {
|
|
|
16404
16818
|
return 2e5;
|
|
16405
16819
|
}
|
|
16406
16820
|
function readSessionUsage() {
|
|
16407
|
-
const projectsDir =
|
|
16408
|
-
if (!
|
|
16821
|
+
const projectsDir = path51.join(os45.homedir(), ".claude", "projects");
|
|
16822
|
+
if (!fs50.existsSync(projectsDir)) return null;
|
|
16409
16823
|
let latestFile = null;
|
|
16410
16824
|
let latestMtime = 0;
|
|
16411
16825
|
try {
|
|
16412
|
-
for (const dir of
|
|
16413
|
-
const dirPath =
|
|
16826
|
+
for (const dir of fs50.readdirSync(projectsDir)) {
|
|
16827
|
+
const dirPath = path51.join(projectsDir, dir);
|
|
16414
16828
|
try {
|
|
16415
|
-
if (!
|
|
16416
|
-
for (const file of
|
|
16829
|
+
if (!fs50.statSync(dirPath).isDirectory()) continue;
|
|
16830
|
+
for (const file of fs50.readdirSync(dirPath)) {
|
|
16417
16831
|
if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
|
|
16418
|
-
const filePath =
|
|
16832
|
+
const filePath = path51.join(dirPath, file);
|
|
16419
16833
|
try {
|
|
16420
|
-
const mtime =
|
|
16834
|
+
const mtime = fs50.statSync(filePath).mtimeMs;
|
|
16421
16835
|
if (mtime > latestMtime) {
|
|
16422
16836
|
latestMtime = mtime;
|
|
16423
16837
|
latestFile = filePath;
|
|
@@ -16432,7 +16846,7 @@ function readSessionUsage() {
|
|
|
16432
16846
|
}
|
|
16433
16847
|
if (!latestFile) return null;
|
|
16434
16848
|
try {
|
|
16435
|
-
const lines =
|
|
16849
|
+
const lines = fs50.readFileSync(latestFile, "utf-8").split("\n");
|
|
16436
16850
|
let lastModel = "";
|
|
16437
16851
|
let lastInput = 0;
|
|
16438
16852
|
let lastOutput = 0;
|
|
@@ -16493,7 +16907,7 @@ function formatBase(activity) {
|
|
|
16493
16907
|
const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
|
|
16494
16908
|
const icon = getIcon(activity.tool);
|
|
16495
16909
|
const toolName = activity.tool.slice(0, 16).padEnd(16);
|
|
16496
|
-
const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(
|
|
16910
|
+
const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(os45.homedir(), "~");
|
|
16497
16911
|
const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
|
|
16498
16912
|
return `${chalk29.gray(time)} ${icon} ${agentLabel(activity.agent, activity.mcpServer, activity.sessionId)}${chalk29.white.bold(toolName)} ${chalk29.dim(argsPreview)}`;
|
|
16499
16913
|
}
|
|
@@ -16532,9 +16946,9 @@ function renderPending(activity) {
|
|
|
16532
16946
|
}
|
|
16533
16947
|
async function ensureDaemon() {
|
|
16534
16948
|
let pidPort = null;
|
|
16535
|
-
if (
|
|
16949
|
+
if (fs50.existsSync(PID_FILE)) {
|
|
16536
16950
|
try {
|
|
16537
|
-
const { port } = JSON.parse(
|
|
16951
|
+
const { port } = JSON.parse(fs50.readFileSync(PID_FILE, "utf-8"));
|
|
16538
16952
|
pidPort = port;
|
|
16539
16953
|
} catch {
|
|
16540
16954
|
console.error(chalk29.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
|
|
@@ -16690,9 +17104,9 @@ function buildRecoveryCardLines(req) {
|
|
|
16690
17104
|
];
|
|
16691
17105
|
}
|
|
16692
17106
|
function readApproversFromDisk() {
|
|
16693
|
-
const configPath =
|
|
17107
|
+
const configPath = path51.join(os45.homedir(), ".node9", "config.json");
|
|
16694
17108
|
try {
|
|
16695
|
-
const raw = JSON.parse(
|
|
17109
|
+
const raw = JSON.parse(fs50.readFileSync(configPath, "utf-8"));
|
|
16696
17110
|
const settings = raw.settings ?? {};
|
|
16697
17111
|
return settings.approvers ?? {};
|
|
16698
17112
|
} catch {
|
|
@@ -16708,15 +17122,15 @@ function approverStatusLine() {
|
|
|
16708
17122
|
return `${fmt("native", "native")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
|
|
16709
17123
|
}
|
|
16710
17124
|
function toggleApprover(channel) {
|
|
16711
|
-
const configPath =
|
|
17125
|
+
const configPath = path51.join(os45.homedir(), ".node9", "config.json");
|
|
16712
17126
|
try {
|
|
16713
|
-
const raw = JSON.parse(
|
|
17127
|
+
const raw = JSON.parse(fs50.readFileSync(configPath, "utf-8"));
|
|
16714
17128
|
const settings = raw.settings ?? {};
|
|
16715
17129
|
const approvers = settings.approvers ?? {};
|
|
16716
17130
|
approvers[channel] = approvers[channel] === false;
|
|
16717
17131
|
settings.approvers = approvers;
|
|
16718
17132
|
raw.settings = settings;
|
|
16719
|
-
|
|
17133
|
+
fs50.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
|
|
16720
17134
|
} catch (err2) {
|
|
16721
17135
|
process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
|
|
16722
17136
|
`);
|
|
@@ -16888,8 +17302,8 @@ async function startTail(options = {}) {
|
|
|
16888
17302
|
}
|
|
16889
17303
|
postDecisionHttp(req2.id, httpDecision, authToken, port, httpOpts).catch((err2) => {
|
|
16890
17304
|
try {
|
|
16891
|
-
|
|
16892
|
-
|
|
17305
|
+
fs50.appendFileSync(
|
|
17306
|
+
path51.join(os45.homedir(), ".node9", "hook-debug.log"),
|
|
16893
17307
|
`[tail] POST /decision failed: ${String(err2)}
|
|
16894
17308
|
`
|
|
16895
17309
|
);
|
|
@@ -16953,9 +17367,9 @@ async function startTail(options = {}) {
|
|
|
16953
17367
|
};
|
|
16954
17368
|
process.stdin.on("keypress", onKeypress);
|
|
16955
17369
|
}
|
|
16956
|
-
const auditLog =
|
|
17370
|
+
const auditLog = path51.join(os45.homedir(), ".node9", "audit.log");
|
|
16957
17371
|
try {
|
|
16958
|
-
const unackedDlp =
|
|
17372
|
+
const unackedDlp = fs50.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
|
|
16959
17373
|
if (unackedDlp > 0) {
|
|
16960
17374
|
console.log("");
|
|
16961
17375
|
console.log(
|
|
@@ -16995,7 +17409,7 @@ async function startTail(options = {}) {
|
|
|
16995
17409
|
if (stallWarned) return;
|
|
16996
17410
|
if (Date.now() - lastActivityFromDaemon < STALL_THRESHOLD_MS) return;
|
|
16997
17411
|
try {
|
|
16998
|
-
const auditMtime =
|
|
17412
|
+
const auditMtime = fs50.statSync(auditLog).mtimeMs;
|
|
16999
17413
|
if (Date.now() - auditMtime >= STALL_THRESHOLD_MS) return;
|
|
17000
17414
|
console.log("");
|
|
17001
17415
|
console.log(
|
|
@@ -17186,7 +17600,7 @@ var init_tail = __esm({
|
|
|
17186
17600
|
"use strict";
|
|
17187
17601
|
init_daemon2();
|
|
17188
17602
|
init_daemon();
|
|
17189
|
-
PID_FILE =
|
|
17603
|
+
PID_FILE = path51.join(os45.homedir(), ".node9", "daemon.pid");
|
|
17190
17604
|
ICONS = {
|
|
17191
17605
|
bash: "\u{1F4BB}",
|
|
17192
17606
|
shell: "\u{1F4BB}",
|
|
@@ -17234,14 +17648,14 @@ __export(hud_exports, {
|
|
|
17234
17648
|
main: () => main,
|
|
17235
17649
|
renderEnvironmentLine: () => renderEnvironmentLine
|
|
17236
17650
|
});
|
|
17237
|
-
import
|
|
17238
|
-
import
|
|
17239
|
-
import
|
|
17651
|
+
import fs51 from "fs";
|
|
17652
|
+
import path52 from "path";
|
|
17653
|
+
import os46 from "os";
|
|
17240
17654
|
import http3 from "http";
|
|
17241
17655
|
async function readStdin() {
|
|
17242
17656
|
const chunks = [];
|
|
17243
|
-
for await (const
|
|
17244
|
-
chunks.push(
|
|
17657
|
+
for await (const chunk2 of process.stdin) {
|
|
17658
|
+
chunks.push(chunk2);
|
|
17245
17659
|
}
|
|
17246
17660
|
const raw = Buffer.concat(chunks).toString("utf-8").trim();
|
|
17247
17661
|
if (!raw) return {};
|
|
@@ -17312,9 +17726,9 @@ function formatTimeLeft(resetsAt) {
|
|
|
17312
17726
|
return ` (${m}m left)`;
|
|
17313
17727
|
}
|
|
17314
17728
|
function safeReadJson(filePath) {
|
|
17315
|
-
if (!
|
|
17729
|
+
if (!fs51.existsSync(filePath)) return null;
|
|
17316
17730
|
try {
|
|
17317
|
-
return JSON.parse(
|
|
17731
|
+
return JSON.parse(fs51.readFileSync(filePath, "utf-8"));
|
|
17318
17732
|
} catch {
|
|
17319
17733
|
return null;
|
|
17320
17734
|
}
|
|
@@ -17335,12 +17749,12 @@ function countHooksInFile(filePath) {
|
|
|
17335
17749
|
return Object.keys(cfg.hooks).length;
|
|
17336
17750
|
}
|
|
17337
17751
|
function countRulesInDir(rulesDir) {
|
|
17338
|
-
if (!
|
|
17752
|
+
if (!fs51.existsSync(rulesDir)) return 0;
|
|
17339
17753
|
let count = 0;
|
|
17340
17754
|
try {
|
|
17341
|
-
for (const entry of
|
|
17755
|
+
for (const entry of fs51.readdirSync(rulesDir, { withFileTypes: true })) {
|
|
17342
17756
|
if (entry.isDirectory()) {
|
|
17343
|
-
count += countRulesInDir(
|
|
17757
|
+
count += countRulesInDir(path52.join(rulesDir, entry.name));
|
|
17344
17758
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
17345
17759
|
count++;
|
|
17346
17760
|
}
|
|
@@ -17351,46 +17765,46 @@ function countRulesInDir(rulesDir) {
|
|
|
17351
17765
|
}
|
|
17352
17766
|
function isSamePath(a, b) {
|
|
17353
17767
|
try {
|
|
17354
|
-
return
|
|
17768
|
+
return path52.resolve(a) === path52.resolve(b);
|
|
17355
17769
|
} catch {
|
|
17356
17770
|
return false;
|
|
17357
17771
|
}
|
|
17358
17772
|
}
|
|
17359
17773
|
function countConfigs(cwd) {
|
|
17360
|
-
const homeDir2 =
|
|
17361
|
-
const claudeDir =
|
|
17774
|
+
const homeDir2 = os46.homedir();
|
|
17775
|
+
const claudeDir = path52.join(homeDir2, ".claude");
|
|
17362
17776
|
let claudeMdCount = 0;
|
|
17363
17777
|
let rulesCount = 0;
|
|
17364
17778
|
let hooksCount = 0;
|
|
17365
17779
|
const userMcpServers = /* @__PURE__ */ new Set();
|
|
17366
17780
|
const projectMcpServers = /* @__PURE__ */ new Set();
|
|
17367
|
-
if (
|
|
17368
|
-
rulesCount += countRulesInDir(
|
|
17369
|
-
const userSettings =
|
|
17781
|
+
if (fs51.existsSync(path52.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
17782
|
+
rulesCount += countRulesInDir(path52.join(claudeDir, "rules"));
|
|
17783
|
+
const userSettings = path52.join(claudeDir, "settings.json");
|
|
17370
17784
|
for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
|
|
17371
17785
|
hooksCount += countHooksInFile(userSettings);
|
|
17372
|
-
const userClaudeJson =
|
|
17786
|
+
const userClaudeJson = path52.join(homeDir2, ".claude.json");
|
|
17373
17787
|
for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
|
|
17374
17788
|
for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
|
|
17375
17789
|
userMcpServers.delete(name);
|
|
17376
17790
|
}
|
|
17377
17791
|
if (cwd) {
|
|
17378
|
-
if (
|
|
17379
|
-
if (
|
|
17380
|
-
const projectClaudeDir =
|
|
17792
|
+
if (fs51.existsSync(path52.join(cwd, "CLAUDE.md"))) claudeMdCount++;
|
|
17793
|
+
if (fs51.existsSync(path52.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
|
|
17794
|
+
const projectClaudeDir = path52.join(cwd, ".claude");
|
|
17381
17795
|
const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
|
|
17382
17796
|
if (!overlapsUserScope) {
|
|
17383
|
-
if (
|
|
17384
|
-
rulesCount += countRulesInDir(
|
|
17385
|
-
const projSettings =
|
|
17797
|
+
if (fs51.existsSync(path52.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
17798
|
+
rulesCount += countRulesInDir(path52.join(projectClaudeDir, "rules"));
|
|
17799
|
+
const projSettings = path52.join(projectClaudeDir, "settings.json");
|
|
17386
17800
|
for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
|
|
17387
17801
|
hooksCount += countHooksInFile(projSettings);
|
|
17388
17802
|
}
|
|
17389
|
-
if (
|
|
17390
|
-
const localSettings =
|
|
17803
|
+
if (fs51.existsSync(path52.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
|
|
17804
|
+
const localSettings = path52.join(projectClaudeDir, "settings.local.json");
|
|
17391
17805
|
for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
|
|
17392
17806
|
hooksCount += countHooksInFile(localSettings);
|
|
17393
|
-
const mcpJsonServers = getMcpServerNames(
|
|
17807
|
+
const mcpJsonServers = getMcpServerNames(path52.join(cwd, ".mcp.json"));
|
|
17394
17808
|
const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
|
|
17395
17809
|
for (const name of disabledMcpJson) mcpJsonServers.delete(name);
|
|
17396
17810
|
for (const name of mcpJsonServers) projectMcpServers.add(name);
|
|
@@ -17423,12 +17837,12 @@ function readActiveShieldsHud() {
|
|
|
17423
17837
|
return shieldsCache.value;
|
|
17424
17838
|
}
|
|
17425
17839
|
try {
|
|
17426
|
-
const shieldsPath =
|
|
17427
|
-
if (!
|
|
17840
|
+
const shieldsPath = path52.join(os46.homedir(), ".node9", "shields.json");
|
|
17841
|
+
if (!fs51.existsSync(shieldsPath)) {
|
|
17428
17842
|
shieldsCache = { value: [], ts: now };
|
|
17429
17843
|
return [];
|
|
17430
17844
|
}
|
|
17431
|
-
const parsed = JSON.parse(
|
|
17845
|
+
const parsed = JSON.parse(fs51.readFileSync(shieldsPath, "utf-8"));
|
|
17432
17846
|
if (!Array.isArray(parsed.active)) {
|
|
17433
17847
|
shieldsCache = { value: [], ts: now };
|
|
17434
17848
|
return [];
|
|
@@ -17530,17 +17944,17 @@ function renderContextLine(stdin) {
|
|
|
17530
17944
|
async function main() {
|
|
17531
17945
|
try {
|
|
17532
17946
|
const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
|
|
17533
|
-
if (
|
|
17947
|
+
if (fs51.existsSync(path52.join(os46.homedir(), ".node9", "hud-debug"))) {
|
|
17534
17948
|
try {
|
|
17535
|
-
const logPath =
|
|
17949
|
+
const logPath = path52.join(os46.homedir(), ".node9", "hud-debug.log");
|
|
17536
17950
|
const MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
17537
17951
|
let size = 0;
|
|
17538
17952
|
try {
|
|
17539
|
-
size =
|
|
17953
|
+
size = fs51.statSync(logPath).size;
|
|
17540
17954
|
} catch {
|
|
17541
17955
|
}
|
|
17542
17956
|
if (size < MAX_LOG_SIZE) {
|
|
17543
|
-
|
|
17957
|
+
fs51.appendFileSync(
|
|
17544
17958
|
logPath,
|
|
17545
17959
|
JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
|
|
17546
17960
|
);
|
|
@@ -17561,11 +17975,11 @@ async function main() {
|
|
|
17561
17975
|
try {
|
|
17562
17976
|
const cwd = stdin.cwd ?? process.cwd();
|
|
17563
17977
|
for (const configPath of [
|
|
17564
|
-
|
|
17565
|
-
|
|
17978
|
+
path52.join(cwd, "node9.config.json"),
|
|
17979
|
+
path52.join(os46.homedir(), ".node9", "config.json")
|
|
17566
17980
|
]) {
|
|
17567
|
-
if (!
|
|
17568
|
-
const cfg = JSON.parse(
|
|
17981
|
+
if (!fs51.existsSync(configPath)) continue;
|
|
17982
|
+
const cfg = JSON.parse(fs51.readFileSync(configPath, "utf-8"));
|
|
17569
17983
|
const hud = cfg.settings?.hud;
|
|
17570
17984
|
if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
|
|
17571
17985
|
}
|
|
@@ -17612,9 +18026,9 @@ init_setup();
|
|
|
17612
18026
|
init_daemon2();
|
|
17613
18027
|
import { Command } from "commander";
|
|
17614
18028
|
import chalk30 from "chalk";
|
|
17615
|
-
import
|
|
17616
|
-
import
|
|
17617
|
-
import
|
|
18029
|
+
import fs52 from "fs";
|
|
18030
|
+
import path53 from "path";
|
|
18031
|
+
import os47 from "os";
|
|
17618
18032
|
import { confirm as confirm2 } from "@inquirer/prompts";
|
|
17619
18033
|
|
|
17620
18034
|
// src/utils/duration.ts
|
|
@@ -17797,17 +18211,17 @@ async function runProxy(targetCommand) {
|
|
|
17797
18211
|
// src/cli/daemon-starter.ts
|
|
17798
18212
|
init_daemon();
|
|
17799
18213
|
import { spawn as spawn3 } from "child_process";
|
|
17800
|
-
import
|
|
17801
|
-
import
|
|
18214
|
+
import path34 from "path";
|
|
18215
|
+
import fs33 from "fs";
|
|
17802
18216
|
function isTestingMode() {
|
|
17803
18217
|
return /^(1|true|yes)$/i.test(process.env.NODE9_TESTING ?? "");
|
|
17804
18218
|
}
|
|
17805
18219
|
async function autoStartDaemonAndWait() {
|
|
17806
18220
|
if (isTestingMode()) return false;
|
|
17807
|
-
if (!
|
|
18221
|
+
if (!path34.isAbsolute(process.argv[1])) return false;
|
|
17808
18222
|
let resolvedArgv1;
|
|
17809
18223
|
try {
|
|
17810
|
-
resolvedArgv1 =
|
|
18224
|
+
resolvedArgv1 = fs33.realpathSync(process.argv[1]);
|
|
17811
18225
|
} catch {
|
|
17812
18226
|
return false;
|
|
17813
18227
|
}
|
|
@@ -17838,19 +18252,19 @@ init_daemon();
|
|
|
17838
18252
|
init_config();
|
|
17839
18253
|
init_policy();
|
|
17840
18254
|
import chalk9 from "chalk";
|
|
17841
|
-
import
|
|
18255
|
+
import fs36 from "fs";
|
|
17842
18256
|
import { spawn as spawn5 } from "child_process";
|
|
17843
|
-
import
|
|
17844
|
-
import
|
|
18257
|
+
import path37 from "path";
|
|
18258
|
+
import os32 from "os";
|
|
17845
18259
|
|
|
17846
18260
|
// src/undo.ts
|
|
17847
18261
|
import { spawnSync as spawnSync3, spawn as spawn4 } from "child_process";
|
|
17848
18262
|
import crypto6 from "crypto";
|
|
17849
|
-
import
|
|
18263
|
+
import fs34 from "fs";
|
|
17850
18264
|
import net3 from "net";
|
|
17851
|
-
import
|
|
17852
|
-
import
|
|
17853
|
-
var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" :
|
|
18265
|
+
import path35 from "path";
|
|
18266
|
+
import os30 from "os";
|
|
18267
|
+
var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path35.join(os30.tmpdir(), "node9-activity.sock");
|
|
17854
18268
|
function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
|
|
17855
18269
|
try {
|
|
17856
18270
|
const payload = JSON.stringify({
|
|
@@ -17870,22 +18284,22 @@ function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
|
|
|
17870
18284
|
} catch {
|
|
17871
18285
|
}
|
|
17872
18286
|
}
|
|
17873
|
-
var SNAPSHOT_STACK_PATH =
|
|
17874
|
-
var UNDO_LATEST_PATH =
|
|
18287
|
+
var SNAPSHOT_STACK_PATH = path35.join(os30.homedir(), ".node9", "snapshots.json");
|
|
18288
|
+
var UNDO_LATEST_PATH = path35.join(os30.homedir(), ".node9", "undo_latest.txt");
|
|
17875
18289
|
var MAX_SNAPSHOTS = 10;
|
|
17876
18290
|
var GIT_TIMEOUT = 15e3;
|
|
17877
18291
|
function readStack() {
|
|
17878
18292
|
try {
|
|
17879
|
-
if (
|
|
17880
|
-
return JSON.parse(
|
|
18293
|
+
if (fs34.existsSync(SNAPSHOT_STACK_PATH))
|
|
18294
|
+
return JSON.parse(fs34.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
|
|
17881
18295
|
} catch {
|
|
17882
18296
|
}
|
|
17883
18297
|
return [];
|
|
17884
18298
|
}
|
|
17885
18299
|
function writeStack(stack) {
|
|
17886
|
-
const dir =
|
|
17887
|
-
if (!
|
|
17888
|
-
|
|
18300
|
+
const dir = path35.dirname(SNAPSHOT_STACK_PATH);
|
|
18301
|
+
if (!fs34.existsSync(dir)) fs34.mkdirSync(dir, { recursive: true });
|
|
18302
|
+
fs34.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
|
|
17889
18303
|
}
|
|
17890
18304
|
function extractFilePath(args) {
|
|
17891
18305
|
if (!args || typeof args !== "object") return null;
|
|
@@ -17905,12 +18319,12 @@ function buildArgsSummary(tool, args) {
|
|
|
17905
18319
|
return "";
|
|
17906
18320
|
}
|
|
17907
18321
|
function findProjectRoot(filePath) {
|
|
17908
|
-
let dir =
|
|
18322
|
+
let dir = path35.dirname(filePath);
|
|
17909
18323
|
while (true) {
|
|
17910
|
-
if (
|
|
18324
|
+
if (fs34.existsSync(path35.join(dir, ".git")) || fs34.existsSync(path35.join(dir, "package.json"))) {
|
|
17911
18325
|
return dir;
|
|
17912
18326
|
}
|
|
17913
|
-
const parent =
|
|
18327
|
+
const parent = path35.dirname(dir);
|
|
17914
18328
|
if (parent === dir) return process.cwd();
|
|
17915
18329
|
dir = parent;
|
|
17916
18330
|
}
|
|
@@ -17918,7 +18332,7 @@ function findProjectRoot(filePath) {
|
|
|
17918
18332
|
function normalizeCwdForHash(cwd) {
|
|
17919
18333
|
let normalized;
|
|
17920
18334
|
try {
|
|
17921
|
-
normalized =
|
|
18335
|
+
normalized = fs34.realpathSync(cwd);
|
|
17922
18336
|
} catch {
|
|
17923
18337
|
normalized = cwd;
|
|
17924
18338
|
}
|
|
@@ -17928,16 +18342,16 @@ function normalizeCwdForHash(cwd) {
|
|
|
17928
18342
|
}
|
|
17929
18343
|
function getShadowRepoDir(cwd) {
|
|
17930
18344
|
const hash = crypto6.createHash("sha256").update(normalizeCwdForHash(cwd)).digest("hex").slice(0, 16);
|
|
17931
|
-
return
|
|
18345
|
+
return path35.join(os30.homedir(), ".node9", "snapshots", hash);
|
|
17932
18346
|
}
|
|
17933
18347
|
function cleanOrphanedIndexFiles(shadowDir) {
|
|
17934
18348
|
try {
|
|
17935
18349
|
const cutoff = Date.now() - 6e4;
|
|
17936
|
-
for (const f of
|
|
18350
|
+
for (const f of fs34.readdirSync(shadowDir)) {
|
|
17937
18351
|
if (f.startsWith("index_")) {
|
|
17938
|
-
const fp =
|
|
18352
|
+
const fp = path35.join(shadowDir, f);
|
|
17939
18353
|
try {
|
|
17940
|
-
if (
|
|
18354
|
+
if (fs34.statSync(fp).mtimeMs < cutoff) fs34.unlinkSync(fp);
|
|
17941
18355
|
} catch {
|
|
17942
18356
|
}
|
|
17943
18357
|
}
|
|
@@ -17949,7 +18363,7 @@ function writeShadowExcludes(shadowDir, ignorePaths) {
|
|
|
17949
18363
|
const hardcoded = [".git", ".node9"];
|
|
17950
18364
|
const lines = [...hardcoded, ...ignorePaths].join("\n");
|
|
17951
18365
|
try {
|
|
17952
|
-
|
|
18366
|
+
fs34.writeFileSync(path35.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
|
|
17953
18367
|
} catch {
|
|
17954
18368
|
}
|
|
17955
18369
|
}
|
|
@@ -17962,25 +18376,25 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
17962
18376
|
timeout: 3e3
|
|
17963
18377
|
});
|
|
17964
18378
|
if (check.status === 0) {
|
|
17965
|
-
const ptPath =
|
|
18379
|
+
const ptPath = path35.join(shadowDir, "project-path.txt");
|
|
17966
18380
|
try {
|
|
17967
|
-
const stored =
|
|
18381
|
+
const stored = fs34.readFileSync(ptPath, "utf8").trim();
|
|
17968
18382
|
if (stored === normalizedCwd) return true;
|
|
17969
18383
|
if (process.env.NODE9_DEBUG === "1")
|
|
17970
18384
|
console.error(
|
|
17971
18385
|
`[Node9] Shadow repo path mismatch: stored="${stored}" expected="${normalizedCwd}" \u2014 reinitializing`
|
|
17972
18386
|
);
|
|
17973
|
-
|
|
18387
|
+
fs34.rmSync(shadowDir, { recursive: true, force: true });
|
|
17974
18388
|
} catch {
|
|
17975
18389
|
try {
|
|
17976
|
-
|
|
18390
|
+
fs34.writeFileSync(ptPath, normalizedCwd, "utf8");
|
|
17977
18391
|
} catch {
|
|
17978
18392
|
}
|
|
17979
18393
|
return true;
|
|
17980
18394
|
}
|
|
17981
18395
|
}
|
|
17982
18396
|
try {
|
|
17983
|
-
|
|
18397
|
+
fs34.mkdirSync(shadowDir, { recursive: true });
|
|
17984
18398
|
} catch {
|
|
17985
18399
|
}
|
|
17986
18400
|
const init = spawnSync3("git", ["init", "--bare", shadowDir], { timeout: 5e3 });
|
|
@@ -17989,7 +18403,7 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
17989
18403
|
if (process.env.NODE9_DEBUG === "1") console.error("[Node9] git init --bare failed:", reason);
|
|
17990
18404
|
return false;
|
|
17991
18405
|
}
|
|
17992
|
-
const configFile =
|
|
18406
|
+
const configFile = path35.join(shadowDir, "config");
|
|
17993
18407
|
spawnSync3("git", ["config", "--file", configFile, "core.untrackedCache", "true"], {
|
|
17994
18408
|
timeout: 3e3
|
|
17995
18409
|
});
|
|
@@ -17997,7 +18411,7 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
17997
18411
|
timeout: 3e3
|
|
17998
18412
|
});
|
|
17999
18413
|
try {
|
|
18000
|
-
|
|
18414
|
+
fs34.writeFileSync(path35.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
|
|
18001
18415
|
} catch {
|
|
18002
18416
|
}
|
|
18003
18417
|
return true;
|
|
@@ -18020,12 +18434,12 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
18020
18434
|
let indexFile = null;
|
|
18021
18435
|
try {
|
|
18022
18436
|
const rawFilePath = extractFilePath(args);
|
|
18023
|
-
const absFilePath = rawFilePath &&
|
|
18437
|
+
const absFilePath = rawFilePath && path35.isAbsolute(rawFilePath) ? rawFilePath : null;
|
|
18024
18438
|
const cwd = absFilePath ? findProjectRoot(absFilePath) : process.cwd();
|
|
18025
18439
|
const shadowDir = getShadowRepoDir(cwd);
|
|
18026
18440
|
if (!ensureShadowRepo(shadowDir, cwd)) return null;
|
|
18027
18441
|
writeShadowExcludes(shadowDir, ignorePaths);
|
|
18028
|
-
indexFile =
|
|
18442
|
+
indexFile = path35.join(shadowDir, `index_${process.pid}_${Date.now()}`);
|
|
18029
18443
|
const shadowEnv = {
|
|
18030
18444
|
...process.env,
|
|
18031
18445
|
GIT_DIR: shadowDir,
|
|
@@ -18097,7 +18511,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
18097
18511
|
writeStack(stack);
|
|
18098
18512
|
const entry = stack[stack.length - 1];
|
|
18099
18513
|
notifySnapshotTaken(commitHash.slice(0, 7), tool, entry.argsSummary, capturedFiles.length);
|
|
18100
|
-
|
|
18514
|
+
fs34.writeFileSync(UNDO_LATEST_PATH, commitHash);
|
|
18101
18515
|
if (shouldGc) {
|
|
18102
18516
|
spawn4("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
|
|
18103
18517
|
}
|
|
@@ -18108,7 +18522,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
18108
18522
|
} finally {
|
|
18109
18523
|
if (indexFile) {
|
|
18110
18524
|
try {
|
|
18111
|
-
|
|
18525
|
+
fs34.unlinkSync(indexFile);
|
|
18112
18526
|
} catch {
|
|
18113
18527
|
}
|
|
18114
18528
|
}
|
|
@@ -18184,9 +18598,9 @@ function applyUndo(hash, cwd) {
|
|
|
18184
18598
|
timeout: GIT_TIMEOUT
|
|
18185
18599
|
}).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
|
|
18186
18600
|
for (const file of [...tracked, ...untracked]) {
|
|
18187
|
-
const fullPath =
|
|
18188
|
-
if (!snapshotFiles.has(file) &&
|
|
18189
|
-
|
|
18601
|
+
const fullPath = path35.join(dir, file);
|
|
18602
|
+
if (!snapshotFiles.has(file) && fs34.existsSync(fullPath)) {
|
|
18603
|
+
fs34.unlinkSync(fullPath);
|
|
18190
18604
|
}
|
|
18191
18605
|
}
|
|
18192
18606
|
return true;
|
|
@@ -18196,12 +18610,12 @@ function applyUndo(hash, cwd) {
|
|
|
18196
18610
|
}
|
|
18197
18611
|
|
|
18198
18612
|
// src/skill-pin.ts
|
|
18199
|
-
import
|
|
18200
|
-
import
|
|
18201
|
-
import
|
|
18613
|
+
import fs35 from "fs";
|
|
18614
|
+
import path36 from "path";
|
|
18615
|
+
import os31 from "os";
|
|
18202
18616
|
import crypto7 from "crypto";
|
|
18203
18617
|
function getPinsFilePath2() {
|
|
18204
|
-
return
|
|
18618
|
+
return path36.join(os31.homedir(), ".node9", "skill-pins.json");
|
|
18205
18619
|
}
|
|
18206
18620
|
var MAX_FILES = 5e3;
|
|
18207
18621
|
var MAX_TOTAL_BYTES = 50 * 1024 * 1024;
|
|
@@ -18215,18 +18629,18 @@ function walkDir(root) {
|
|
|
18215
18629
|
if (out.length >= MAX_FILES) return;
|
|
18216
18630
|
let entries;
|
|
18217
18631
|
try {
|
|
18218
|
-
entries =
|
|
18632
|
+
entries = fs35.readdirSync(dir, { withFileTypes: true });
|
|
18219
18633
|
} catch {
|
|
18220
18634
|
return;
|
|
18221
18635
|
}
|
|
18222
18636
|
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
18223
18637
|
for (const entry of entries) {
|
|
18224
18638
|
if (out.length >= MAX_FILES) return;
|
|
18225
|
-
const full =
|
|
18226
|
-
const rel = relDir ?
|
|
18639
|
+
const full = path36.join(dir, entry.name);
|
|
18640
|
+
const rel = relDir ? path36.posix.join(relDir, entry.name) : entry.name;
|
|
18227
18641
|
let lst;
|
|
18228
18642
|
try {
|
|
18229
|
-
lst =
|
|
18643
|
+
lst = fs35.lstatSync(full);
|
|
18230
18644
|
} catch {
|
|
18231
18645
|
continue;
|
|
18232
18646
|
}
|
|
@@ -18238,7 +18652,7 @@ function walkDir(root) {
|
|
|
18238
18652
|
if (!lst.isFile()) continue;
|
|
18239
18653
|
if (totalBytes + lst.size > MAX_TOTAL_BYTES) continue;
|
|
18240
18654
|
try {
|
|
18241
|
-
const buf =
|
|
18655
|
+
const buf = fs35.readFileSync(full);
|
|
18242
18656
|
totalBytes += buf.length;
|
|
18243
18657
|
out.push({ rel, hash: sha256Bytes(buf) });
|
|
18244
18658
|
} catch {
|
|
@@ -18252,14 +18666,14 @@ function walkDir(root) {
|
|
|
18252
18666
|
function hashSkillRoot(absPath) {
|
|
18253
18667
|
let lst;
|
|
18254
18668
|
try {
|
|
18255
|
-
lst =
|
|
18669
|
+
lst = fs35.lstatSync(absPath);
|
|
18256
18670
|
} catch {
|
|
18257
18671
|
return { exists: false, contentHash: "", fileCount: 0 };
|
|
18258
18672
|
}
|
|
18259
18673
|
if (lst.isSymbolicLink()) return { exists: false, contentHash: "", fileCount: 0 };
|
|
18260
18674
|
if (lst.isFile()) {
|
|
18261
18675
|
try {
|
|
18262
|
-
return { exists: true, contentHash: sha256Bytes(
|
|
18676
|
+
return { exists: true, contentHash: sha256Bytes(fs35.readFileSync(absPath)), fileCount: 1 };
|
|
18263
18677
|
} catch {
|
|
18264
18678
|
return { exists: false, contentHash: "", fileCount: 0 };
|
|
18265
18679
|
}
|
|
@@ -18277,7 +18691,7 @@ function getRootKey(absPath) {
|
|
|
18277
18691
|
function readSkillPinsSafe() {
|
|
18278
18692
|
const filePath = getPinsFilePath2();
|
|
18279
18693
|
try {
|
|
18280
|
-
const raw =
|
|
18694
|
+
const raw = fs35.readFileSync(filePath, "utf-8");
|
|
18281
18695
|
if (!raw.trim()) return { ok: false, reason: "corrupt", detail: "empty file" };
|
|
18282
18696
|
const parsed = JSON.parse(raw);
|
|
18283
18697
|
if (!parsed.roots || typeof parsed.roots !== "object" || Array.isArray(parsed.roots)) {
|
|
@@ -18297,10 +18711,10 @@ function readSkillPins() {
|
|
|
18297
18711
|
}
|
|
18298
18712
|
function writeSkillPins(data) {
|
|
18299
18713
|
const filePath = getPinsFilePath2();
|
|
18300
|
-
|
|
18714
|
+
fs35.mkdirSync(path36.dirname(filePath), { recursive: true });
|
|
18301
18715
|
const tmp = `${filePath}.${crypto7.randomBytes(6).toString("hex")}.tmp`;
|
|
18302
|
-
|
|
18303
|
-
|
|
18716
|
+
fs35.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
18717
|
+
fs35.renameSync(tmp, filePath);
|
|
18304
18718
|
}
|
|
18305
18719
|
function removePin2(rootKey) {
|
|
18306
18720
|
const pins = readSkillPins();
|
|
@@ -18344,36 +18758,36 @@ function verifyAndPinRoots(roots) {
|
|
|
18344
18758
|
return { kind: "verified" };
|
|
18345
18759
|
}
|
|
18346
18760
|
function defaultSkillRoots(_cwd) {
|
|
18347
|
-
const marketplaces =
|
|
18761
|
+
const marketplaces = path36.join(os31.homedir(), ".claude", "plugins", "marketplaces");
|
|
18348
18762
|
const roots = [];
|
|
18349
18763
|
let registries;
|
|
18350
18764
|
try {
|
|
18351
|
-
registries =
|
|
18765
|
+
registries = fs35.readdirSync(marketplaces, { withFileTypes: true });
|
|
18352
18766
|
} catch {
|
|
18353
18767
|
return [];
|
|
18354
18768
|
}
|
|
18355
18769
|
for (const registry of registries) {
|
|
18356
18770
|
if (!registry.isDirectory()) continue;
|
|
18357
|
-
const pluginsDir =
|
|
18771
|
+
const pluginsDir = path36.join(marketplaces, registry.name, "plugins");
|
|
18358
18772
|
let plugins;
|
|
18359
18773
|
try {
|
|
18360
|
-
plugins =
|
|
18774
|
+
plugins = fs35.readdirSync(pluginsDir, { withFileTypes: true });
|
|
18361
18775
|
} catch {
|
|
18362
18776
|
continue;
|
|
18363
18777
|
}
|
|
18364
18778
|
for (const plugin of plugins) {
|
|
18365
18779
|
if (!plugin.isDirectory()) continue;
|
|
18366
|
-
roots.push(
|
|
18780
|
+
roots.push(path36.join(pluginsDir, plugin.name));
|
|
18367
18781
|
}
|
|
18368
18782
|
}
|
|
18369
18783
|
return roots;
|
|
18370
18784
|
}
|
|
18371
18785
|
function resolveUserSkillRoot(entry, cwd) {
|
|
18372
18786
|
if (!entry) return null;
|
|
18373
|
-
if (entry.startsWith("~/") || entry === "~") return
|
|
18374
|
-
if (
|
|
18375
|
-
if (!cwd || !
|
|
18376
|
-
return
|
|
18787
|
+
if (entry.startsWith("~/") || entry === "~") return path36.join(os31.homedir(), entry.slice(1));
|
|
18788
|
+
if (path36.isAbsolute(entry)) return entry;
|
|
18789
|
+
if (!cwd || !path36.isAbsolute(cwd)) return null;
|
|
18790
|
+
return path36.join(cwd, entry);
|
|
18377
18791
|
}
|
|
18378
18792
|
|
|
18379
18793
|
// src/cli/commands/check.ts
|
|
@@ -18442,9 +18856,9 @@ function registerCheckCommand(program2) {
|
|
|
18442
18856
|
} catch (err2) {
|
|
18443
18857
|
const tempConfig = getConfig();
|
|
18444
18858
|
if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
|
|
18445
|
-
const logPath =
|
|
18859
|
+
const logPath = path37.join(os32.homedir(), ".node9", "hook-debug.log");
|
|
18446
18860
|
const errMsg = err2 instanceof Error ? err2.message : String(err2);
|
|
18447
|
-
|
|
18861
|
+
fs36.appendFileSync(
|
|
18448
18862
|
logPath,
|
|
18449
18863
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
|
|
18450
18864
|
RAW: ${raw}
|
|
@@ -18457,14 +18871,14 @@ RAW: ${raw}
|
|
|
18457
18871
|
const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
|
|
18458
18872
|
if (process.env.NODE9_DEBUG === "1") {
|
|
18459
18873
|
try {
|
|
18460
|
-
const logPath =
|
|
18461
|
-
if (!
|
|
18462
|
-
|
|
18874
|
+
const logPath = path37.join(os32.homedir(), ".node9", "hook-debug.log");
|
|
18875
|
+
if (!fs36.existsSync(path37.dirname(logPath)))
|
|
18876
|
+
fs36.mkdirSync(path37.dirname(logPath), { recursive: true });
|
|
18463
18877
|
const sanitized = JSON.stringify({
|
|
18464
18878
|
...payload,
|
|
18465
18879
|
prompt: `<redacted, ${prompt.length} bytes>`
|
|
18466
18880
|
});
|
|
18467
|
-
|
|
18881
|
+
fs36.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${sanitized}
|
|
18468
18882
|
`);
|
|
18469
18883
|
} catch {
|
|
18470
18884
|
}
|
|
@@ -18484,8 +18898,8 @@ RAW: ${raw}
|
|
|
18484
18898
|
);
|
|
18485
18899
|
const reason = `\u{1F6A8} Node9 DLP: ${dlpMatch.patternName} detected in prompt (${dlpMatch.redactedSample}). Prompt was not submitted \u2014 remove the credential and try again.`;
|
|
18486
18900
|
try {
|
|
18487
|
-
const ttyFd =
|
|
18488
|
-
|
|
18901
|
+
const ttyFd = fs36.openSync("/dev/tty", "w");
|
|
18902
|
+
fs36.writeSync(
|
|
18489
18903
|
ttyFd,
|
|
18490
18904
|
chalk9.bgRed.white.bold(`
|
|
18491
18905
|
\u{1F6A8} NODE9 DLP \u2014 PROMPT BLOCKED
|
|
@@ -18495,7 +18909,7 @@ RAW: ${raw}
|
|
|
18495
18909
|
|
|
18496
18910
|
`)
|
|
18497
18911
|
);
|
|
18498
|
-
|
|
18912
|
+
fs36.closeSync(ttyFd);
|
|
18499
18913
|
} catch {
|
|
18500
18914
|
}
|
|
18501
18915
|
const isCodex = agent2 === "Codex";
|
|
@@ -18514,16 +18928,16 @@ RAW: ${raw}
|
|
|
18514
18928
|
process.exit(2);
|
|
18515
18929
|
}
|
|
18516
18930
|
const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
|
|
18517
|
-
const safeCwdForConfig = typeof payloadCwd === "string" &&
|
|
18931
|
+
const safeCwdForConfig = typeof payloadCwd === "string" && path37.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
18518
18932
|
const config = getConfig(safeCwdForConfig);
|
|
18519
18933
|
if (config.settings.autoStartDaemon && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON) {
|
|
18520
18934
|
try {
|
|
18521
18935
|
const scriptPath = process.argv[1];
|
|
18522
|
-
if (typeof scriptPath !== "string" || !
|
|
18936
|
+
if (typeof scriptPath !== "string" || !path37.isAbsolute(scriptPath))
|
|
18523
18937
|
throw new Error("node9: argv[1] is not an absolute path");
|
|
18524
|
-
const resolvedScript =
|
|
18525
|
-
const packageDist =
|
|
18526
|
-
if (!resolvedScript.startsWith(packageDist +
|
|
18938
|
+
const resolvedScript = fs36.realpathSync(scriptPath);
|
|
18939
|
+
const packageDist = fs36.realpathSync(path37.resolve(__dirname, "../.."));
|
|
18940
|
+
if (!resolvedScript.startsWith(packageDist + path37.sep) && resolvedScript !== packageDist)
|
|
18527
18941
|
throw new Error(
|
|
18528
18942
|
`node9: daemon spawn aborted \u2014 argv[1] (${resolvedScript}) is outside package dist (${packageDist})`
|
|
18529
18943
|
);
|
|
@@ -18545,10 +18959,10 @@ RAW: ${raw}
|
|
|
18545
18959
|
});
|
|
18546
18960
|
d.unref();
|
|
18547
18961
|
} catch (spawnErr) {
|
|
18548
|
-
const logPath =
|
|
18962
|
+
const logPath = path37.join(os32.homedir(), ".node9", "hook-debug.log");
|
|
18549
18963
|
const msg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);
|
|
18550
18964
|
try {
|
|
18551
|
-
|
|
18965
|
+
fs36.appendFileSync(
|
|
18552
18966
|
logPath,
|
|
18553
18967
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-autostart-failed: ${msg}
|
|
18554
18968
|
`
|
|
@@ -18558,10 +18972,10 @@ RAW: ${raw}
|
|
|
18558
18972
|
}
|
|
18559
18973
|
}
|
|
18560
18974
|
if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
|
|
18561
|
-
const logPath =
|
|
18562
|
-
if (!
|
|
18563
|
-
|
|
18564
|
-
|
|
18975
|
+
const logPath = path37.join(os32.homedir(), ".node9", "hook-debug.log");
|
|
18976
|
+
if (!fs36.existsSync(path37.dirname(logPath)))
|
|
18977
|
+
fs36.mkdirSync(path37.dirname(logPath), { recursive: true });
|
|
18978
|
+
fs36.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
|
|
18565
18979
|
`);
|
|
18566
18980
|
}
|
|
18567
18981
|
const rawToolName = sanitize2(extractToolName(payload));
|
|
@@ -18575,8 +18989,8 @@ RAW: ${raw}
|
|
|
18575
18989
|
const isHumanDecision = blockedByContext.toLowerCase().includes("user") || blockedByContext.toLowerCase().includes("daemon") || blockedByContext.toLowerCase().includes("decision");
|
|
18576
18990
|
let ttyFd = null;
|
|
18577
18991
|
try {
|
|
18578
|
-
ttyFd =
|
|
18579
|
-
const writeTty = (line) =>
|
|
18992
|
+
ttyFd = fs36.openSync("/dev/tty", "w");
|
|
18993
|
+
const writeTty = (line) => fs36.writeSync(ttyFd, line + "\n");
|
|
18580
18994
|
if (blockedByContext.includes("DLP") || blockedByContext.includes("Secret Detected") || blockedByContext.includes("Credential Review")) {
|
|
18581
18995
|
writeTty(chalk9.bgRed.white.bold(`
|
|
18582
18996
|
\u{1F6A8} NODE9 DLP ALERT \u2014 CREDENTIAL DETECTED `));
|
|
@@ -18595,7 +19009,7 @@ RAW: ${raw}
|
|
|
18595
19009
|
} finally {
|
|
18596
19010
|
if (ttyFd !== null)
|
|
18597
19011
|
try {
|
|
18598
|
-
|
|
19012
|
+
fs36.closeSync(ttyFd);
|
|
18599
19013
|
} catch {
|
|
18600
19014
|
}
|
|
18601
19015
|
}
|
|
@@ -18646,17 +19060,17 @@ RAW: ${raw}
|
|
|
18646
19060
|
const safeSessionId = /^[A-Za-z0-9_\-]{1,128}$/.test(rawSessionId) ? rawSessionId : "";
|
|
18647
19061
|
if (skillPinCfg.enabled && safeSessionId) {
|
|
18648
19062
|
try {
|
|
18649
|
-
const sessionsDir =
|
|
18650
|
-
const flagPath =
|
|
19063
|
+
const sessionsDir = path37.join(os32.homedir(), ".node9", "skill-sessions");
|
|
19064
|
+
const flagPath = path37.join(sessionsDir, `${safeSessionId}.json`);
|
|
18651
19065
|
let flag = null;
|
|
18652
19066
|
try {
|
|
18653
|
-
flag = JSON.parse(
|
|
19067
|
+
flag = JSON.parse(fs36.readFileSync(flagPath, "utf-8"));
|
|
18654
19068
|
} catch {
|
|
18655
19069
|
}
|
|
18656
19070
|
const writeFlag = (data2) => {
|
|
18657
19071
|
try {
|
|
18658
|
-
|
|
18659
|
-
|
|
19072
|
+
fs36.mkdirSync(sessionsDir, { recursive: true });
|
|
19073
|
+
fs36.writeFileSync(
|
|
18660
19074
|
flagPath,
|
|
18661
19075
|
JSON.stringify({ ...data2, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
|
|
18662
19076
|
{ mode: 384 }
|
|
@@ -18667,8 +19081,8 @@ RAW: ${raw}
|
|
|
18667
19081
|
const sendSkillWarn = (detail, recoveryCmd) => {
|
|
18668
19082
|
let ttyFd = null;
|
|
18669
19083
|
try {
|
|
18670
|
-
ttyFd =
|
|
18671
|
-
const w = (line) =>
|
|
19084
|
+
ttyFd = fs36.openSync("/dev/tty", "w");
|
|
19085
|
+
const w = (line) => fs36.writeSync(ttyFd, line + "\n");
|
|
18672
19086
|
w(chalk9.yellow(`
|
|
18673
19087
|
\u26A0\uFE0F Node9: installed skill drift detected`));
|
|
18674
19088
|
w(chalk9.gray(` ${detail}`));
|
|
@@ -18683,7 +19097,7 @@ RAW: ${raw}
|
|
|
18683
19097
|
} finally {
|
|
18684
19098
|
if (ttyFd !== null)
|
|
18685
19099
|
try {
|
|
18686
|
-
|
|
19100
|
+
fs36.closeSync(ttyFd);
|
|
18687
19101
|
} catch {
|
|
18688
19102
|
}
|
|
18689
19103
|
}
|
|
@@ -18699,7 +19113,7 @@ RAW: ${raw}
|
|
|
18699
19113
|
return;
|
|
18700
19114
|
}
|
|
18701
19115
|
if (!flag || flag.state !== "verified" && flag.state !== "warned") {
|
|
18702
|
-
const absoluteCwd = typeof payloadCwd === "string" &&
|
|
19116
|
+
const absoluteCwd = typeof payloadCwd === "string" && path37.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
18703
19117
|
const extraRoots = skillPinCfg.roots;
|
|
18704
19118
|
const resolvedExtra = extraRoots.map((r) => resolveUserSkillRoot(r, absoluteCwd)).filter((r) => typeof r === "string");
|
|
18705
19119
|
const roots = [...defaultSkillRoots(absoluteCwd), ...resolvedExtra];
|
|
@@ -18740,10 +19154,10 @@ RAW: ${raw}
|
|
|
18740
19154
|
}
|
|
18741
19155
|
try {
|
|
18742
19156
|
const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1e3;
|
|
18743
|
-
for (const name of
|
|
18744
|
-
const p =
|
|
19157
|
+
for (const name of fs36.readdirSync(sessionsDir)) {
|
|
19158
|
+
const p = path37.join(sessionsDir, name);
|
|
18745
19159
|
try {
|
|
18746
|
-
if (
|
|
19160
|
+
if (fs36.statSync(p).mtimeMs < cutoff) fs36.unlinkSync(p);
|
|
18747
19161
|
} catch {
|
|
18748
19162
|
}
|
|
18749
19163
|
}
|
|
@@ -18753,9 +19167,9 @@ RAW: ${raw}
|
|
|
18753
19167
|
} catch (err2) {
|
|
18754
19168
|
if (process.env.NODE9_DEBUG === "1") {
|
|
18755
19169
|
try {
|
|
18756
|
-
const dbg =
|
|
19170
|
+
const dbg = path37.join(os32.homedir(), ".node9", "hook-debug.log");
|
|
18757
19171
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
18758
|
-
|
|
19172
|
+
fs36.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
|
|
18759
19173
|
`);
|
|
18760
19174
|
} catch {
|
|
18761
19175
|
}
|
|
@@ -18765,7 +19179,7 @@ RAW: ${raw}
|
|
|
18765
19179
|
if (shouldSnapshot(toolName, toolInput, config)) {
|
|
18766
19180
|
await createShadowSnapshot(toolName, toolInput, config.policy.snapshot.ignorePaths);
|
|
18767
19181
|
}
|
|
18768
|
-
const safeCwdForAuth = typeof payloadCwd === "string" &&
|
|
19182
|
+
const safeCwdForAuth = typeof payloadCwd === "string" && path37.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
18769
19183
|
const result = await authorizeHeadless(toolName, toolInput, meta, {
|
|
18770
19184
|
cwd: safeCwdForAuth
|
|
18771
19185
|
});
|
|
@@ -18777,12 +19191,12 @@ RAW: ${raw}
|
|
|
18777
19191
|
}
|
|
18778
19192
|
if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && !process.stdout.isTTY && config.settings.autoStartDaemon) {
|
|
18779
19193
|
try {
|
|
18780
|
-
const tty =
|
|
18781
|
-
|
|
19194
|
+
const tty = fs36.openSync("/dev/tty", "w");
|
|
19195
|
+
fs36.writeSync(
|
|
18782
19196
|
tty,
|
|
18783
19197
|
chalk9.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically...\n")
|
|
18784
19198
|
);
|
|
18785
|
-
|
|
19199
|
+
fs36.closeSync(tty);
|
|
18786
19200
|
} catch {
|
|
18787
19201
|
}
|
|
18788
19202
|
const daemonReady = await autoStartDaemonAndWait();
|
|
@@ -18809,9 +19223,9 @@ RAW: ${raw}
|
|
|
18809
19223
|
});
|
|
18810
19224
|
} catch (err2) {
|
|
18811
19225
|
if (process.env.NODE9_DEBUG === "1") {
|
|
18812
|
-
const logPath =
|
|
19226
|
+
const logPath = path37.join(os32.homedir(), ".node9", "hook-debug.log");
|
|
18813
19227
|
const errMsg = err2 instanceof Error ? err2.message : String(err2);
|
|
18814
|
-
|
|
19228
|
+
fs36.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
|
|
18815
19229
|
`);
|
|
18816
19230
|
}
|
|
18817
19231
|
process.exit(0);
|
|
@@ -18831,8 +19245,8 @@ RAW: ${raw}
|
|
|
18831
19245
|
await processPayload(raw);
|
|
18832
19246
|
};
|
|
18833
19247
|
process.stdin.setEncoding("utf-8");
|
|
18834
|
-
process.stdin.on("data", (
|
|
18835
|
-
raw +=
|
|
19248
|
+
process.stdin.on("data", (chunk2) => {
|
|
19249
|
+
raw += chunk2;
|
|
18836
19250
|
if (inactivityTimer) clearTimeout(inactivityTimer);
|
|
18837
19251
|
inactivityTimer = setTimeout(() => void done(), 2e3);
|
|
18838
19252
|
});
|
|
@@ -18847,9 +19261,9 @@ RAW: ${raw}
|
|
|
18847
19261
|
// src/cli/commands/log.ts
|
|
18848
19262
|
init_audit();
|
|
18849
19263
|
init_config();
|
|
18850
|
-
import
|
|
18851
|
-
import
|
|
18852
|
-
import
|
|
19264
|
+
import fs37 from "fs";
|
|
19265
|
+
import path38 from "path";
|
|
19266
|
+
import os33 from "os";
|
|
18853
19267
|
init_daemon();
|
|
18854
19268
|
|
|
18855
19269
|
// src/utils/cp-mv-parser.ts
|
|
@@ -18942,10 +19356,10 @@ function registerLogCommand(program2) {
|
|
|
18942
19356
|
if (rawToolName !== tool) entry.agentToolName = rawToolName;
|
|
18943
19357
|
const payloadSessionId = payload.session_id ?? payload.conversationId;
|
|
18944
19358
|
if (payloadSessionId) entry.sessionId = payloadSessionId;
|
|
18945
|
-
const logPath =
|
|
18946
|
-
if (!
|
|
18947
|
-
|
|
18948
|
-
|
|
19359
|
+
const logPath = path38.join(os33.homedir(), ".node9", "audit.log");
|
|
19360
|
+
if (!fs37.existsSync(path38.dirname(logPath)))
|
|
19361
|
+
fs37.mkdirSync(path38.dirname(logPath), { recursive: true });
|
|
19362
|
+
fs37.appendFileSync(logPath, JSON.stringify(entry) + "\n");
|
|
18949
19363
|
if ((tool === "Bash" || tool === "bash") && isDaemonRunning()) {
|
|
18950
19364
|
const command = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
|
|
18951
19365
|
if (command) {
|
|
@@ -18979,7 +19393,7 @@ function registerLogCommand(program2) {
|
|
|
18979
19393
|
}
|
|
18980
19394
|
}
|
|
18981
19395
|
const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
|
|
18982
|
-
const safeCwd = typeof payloadCwd === "string" &&
|
|
19396
|
+
const safeCwd = typeof payloadCwd === "string" && path38.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
18983
19397
|
const config = getConfig(safeCwd);
|
|
18984
19398
|
if ((tool === "Bash" || tool === "bash") && config.settings.enableUndo !== false) {
|
|
18985
19399
|
const bashCommand = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
|
|
@@ -19000,9 +19414,9 @@ function registerLogCommand(program2) {
|
|
|
19000
19414
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
19001
19415
|
process.stderr.write(`[Node9] audit log error: ${msg}
|
|
19002
19416
|
`);
|
|
19003
|
-
const debugPath =
|
|
19417
|
+
const debugPath = path38.join(os33.homedir(), ".node9", "hook-debug.log");
|
|
19004
19418
|
try {
|
|
19005
|
-
|
|
19419
|
+
fs37.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
|
|
19006
19420
|
`);
|
|
19007
19421
|
} catch {
|
|
19008
19422
|
}
|
|
@@ -19014,7 +19428,7 @@ function registerLogCommand(program2) {
|
|
|
19014
19428
|
} else {
|
|
19015
19429
|
let raw = "";
|
|
19016
19430
|
process.stdin.setEncoding("utf-8");
|
|
19017
|
-
process.stdin.on("data", (
|
|
19431
|
+
process.stdin.on("data", (chunk2) => raw += chunk2);
|
|
19018
19432
|
process.stdin.on("end", () => {
|
|
19019
19433
|
void logPayload(raw);
|
|
19020
19434
|
});
|
|
@@ -19042,7 +19456,7 @@ function httpsFetch(url) {
|
|
|
19042
19456
|
return;
|
|
19043
19457
|
}
|
|
19044
19458
|
const chunks = [];
|
|
19045
|
-
res.on("data", (
|
|
19459
|
+
res.on("data", (chunk2) => chunks.push(chunk2));
|
|
19046
19460
|
res.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
19047
19461
|
res.on("error", reject);
|
|
19048
19462
|
}).on("error", reject);
|
|
@@ -19404,13 +19818,13 @@ function registerConfigShowCommand(program2) {
|
|
|
19404
19818
|
init_daemon();
|
|
19405
19819
|
init_config();
|
|
19406
19820
|
import chalk11 from "chalk";
|
|
19407
|
-
import
|
|
19408
|
-
import
|
|
19409
|
-
import
|
|
19821
|
+
import fs38 from "fs";
|
|
19822
|
+
import path39 from "path";
|
|
19823
|
+
import os34 from "os";
|
|
19410
19824
|
import { execSync } from "child_process";
|
|
19411
19825
|
function registerDoctorCommand(program2, version2) {
|
|
19412
19826
|
program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(async () => {
|
|
19413
|
-
const homeDir2 =
|
|
19827
|
+
const homeDir2 = os34.homedir();
|
|
19414
19828
|
let failures = 0;
|
|
19415
19829
|
function pass(msg) {
|
|
19416
19830
|
console.log(chalk11.green(" \u2705 ") + msg);
|
|
@@ -19459,10 +19873,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
19459
19873
|
);
|
|
19460
19874
|
}
|
|
19461
19875
|
section("Configuration");
|
|
19462
|
-
const globalConfigPath =
|
|
19463
|
-
if (
|
|
19876
|
+
const globalConfigPath = path39.join(homeDir2, ".node9", "config.json");
|
|
19877
|
+
if (fs38.existsSync(globalConfigPath)) {
|
|
19464
19878
|
try {
|
|
19465
|
-
JSON.parse(
|
|
19879
|
+
JSON.parse(fs38.readFileSync(globalConfigPath, "utf-8"));
|
|
19466
19880
|
pass("~/.node9/config.json found and valid");
|
|
19467
19881
|
} catch {
|
|
19468
19882
|
fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
|
|
@@ -19470,10 +19884,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
19470
19884
|
} else {
|
|
19471
19885
|
warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
|
|
19472
19886
|
}
|
|
19473
|
-
const projectConfigPath =
|
|
19474
|
-
if (
|
|
19887
|
+
const projectConfigPath = path39.join(process.cwd(), "node9.config.json");
|
|
19888
|
+
if (fs38.existsSync(projectConfigPath)) {
|
|
19475
19889
|
try {
|
|
19476
|
-
JSON.parse(
|
|
19890
|
+
JSON.parse(fs38.readFileSync(projectConfigPath, "utf-8"));
|
|
19477
19891
|
pass("node9.config.json found and valid (project)");
|
|
19478
19892
|
} catch {
|
|
19479
19893
|
fail(
|
|
@@ -19482,8 +19896,8 @@ function registerDoctorCommand(program2, version2) {
|
|
|
19482
19896
|
);
|
|
19483
19897
|
}
|
|
19484
19898
|
}
|
|
19485
|
-
const credsPath =
|
|
19486
|
-
if (
|
|
19899
|
+
const credsPath = path39.join(homeDir2, ".node9", "credentials.json");
|
|
19900
|
+
if (fs38.existsSync(credsPath)) {
|
|
19487
19901
|
pass("Cloud credentials found (~/.node9/credentials.json)");
|
|
19488
19902
|
} else {
|
|
19489
19903
|
warn(
|
|
@@ -19492,10 +19906,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
19492
19906
|
);
|
|
19493
19907
|
}
|
|
19494
19908
|
section("Agent Hooks");
|
|
19495
|
-
const claudeSettingsPath =
|
|
19496
|
-
if (
|
|
19909
|
+
const claudeSettingsPath = path39.join(homeDir2, ".claude", "settings.json");
|
|
19910
|
+
if (fs38.existsSync(claudeSettingsPath)) {
|
|
19497
19911
|
try {
|
|
19498
|
-
const cs = JSON.parse(
|
|
19912
|
+
const cs = JSON.parse(fs38.readFileSync(claudeSettingsPath, "utf-8"));
|
|
19499
19913
|
const hasHook = cs.hooks?.PreToolUse?.some(
|
|
19500
19914
|
(m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
|
|
19501
19915
|
);
|
|
@@ -19511,10 +19925,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
19511
19925
|
} else {
|
|
19512
19926
|
warn("Claude Code \u2014 not configured", "Run: node9 setup claude");
|
|
19513
19927
|
}
|
|
19514
|
-
const geminiSettingsPath =
|
|
19515
|
-
if (
|
|
19928
|
+
const geminiSettingsPath = path39.join(homeDir2, ".gemini", "settings.json");
|
|
19929
|
+
if (fs38.existsSync(geminiSettingsPath)) {
|
|
19516
19930
|
try {
|
|
19517
|
-
const gs = JSON.parse(
|
|
19931
|
+
const gs = JSON.parse(fs38.readFileSync(geminiSettingsPath, "utf-8"));
|
|
19518
19932
|
const hasHook = gs.hooks?.BeforeTool?.some(
|
|
19519
19933
|
(m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
|
|
19520
19934
|
);
|
|
@@ -19530,10 +19944,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
19530
19944
|
} else {
|
|
19531
19945
|
warn("Gemini CLI \u2014 not configured", "Run: node9 setup gemini (skip if not using Gemini)");
|
|
19532
19946
|
}
|
|
19533
|
-
const cursorHooksPath =
|
|
19534
|
-
if (
|
|
19947
|
+
const cursorHooksPath = path39.join(homeDir2, ".cursor", "hooks.json");
|
|
19948
|
+
if (fs38.existsSync(cursorHooksPath)) {
|
|
19535
19949
|
try {
|
|
19536
|
-
const cur = JSON.parse(
|
|
19950
|
+
const cur = JSON.parse(fs38.readFileSync(cursorHooksPath, "utf-8"));
|
|
19537
19951
|
const hasHook = cur.hooks?.preToolUse?.some(
|
|
19538
19952
|
(h) => h.command?.includes("node9") || h.command?.includes("cli.js")
|
|
19539
19953
|
);
|
|
@@ -19564,7 +19978,7 @@ function registerDoctorCommand(program2, version2) {
|
|
|
19564
19978
|
try {
|
|
19565
19979
|
const { shipLagBytes: shipLagBytes2, readWatermark: readWatermark2, AUDIT_SHIP_WATERMARK: AUDIT_SHIP_WATERMARK2 } = await Promise.resolve().then(() => (init_audit_shipper(), audit_shipper_exports));
|
|
19566
19980
|
const cfg = getConfig();
|
|
19567
|
-
const creds =
|
|
19981
|
+
const creds = fs38.existsSync(path39.join(os34.homedir(), ".node9", "credentials.json"));
|
|
19568
19982
|
if (!creds) {
|
|
19569
19983
|
warn("Not logged in \u2014 audit rows stay local", "Run: node9 login <api-key>");
|
|
19570
19984
|
} else if (!cfg.settings.approvers.cloud) {
|
|
@@ -19614,9 +20028,9 @@ function registerDoctorCommand(program2, version2) {
|
|
|
19614
20028
|
|
|
19615
20029
|
// src/cli/commands/audit.ts
|
|
19616
20030
|
import chalk12 from "chalk";
|
|
19617
|
-
import
|
|
19618
|
-
import
|
|
19619
|
-
import
|
|
20031
|
+
import fs39 from "fs";
|
|
20032
|
+
import path40 from "path";
|
|
20033
|
+
import os35 from "os";
|
|
19620
20034
|
function formatRelativeTime(timestamp) {
|
|
19621
20035
|
const diff = Date.now() - new Date(timestamp).getTime();
|
|
19622
20036
|
const sec = Math.floor(diff / 1e3);
|
|
@@ -19629,14 +20043,14 @@ function formatRelativeTime(timestamp) {
|
|
|
19629
20043
|
}
|
|
19630
20044
|
function registerAuditCommand(program2) {
|
|
19631
20045
|
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) => {
|
|
19632
|
-
const logPath =
|
|
19633
|
-
if (!
|
|
20046
|
+
const logPath = path40.join(os35.homedir(), ".node9", "audit.log");
|
|
20047
|
+
if (!fs39.existsSync(logPath)) {
|
|
19634
20048
|
console.log(
|
|
19635
20049
|
chalk12.yellow("No audit logs found. Run node9 with an agent to generate entries.")
|
|
19636
20050
|
);
|
|
19637
20051
|
return;
|
|
19638
20052
|
}
|
|
19639
|
-
const raw =
|
|
20053
|
+
const raw = fs39.readFileSync(logPath, "utf-8");
|
|
19640
20054
|
const lines = raw.split("\n").filter((l) => l.trim() !== "");
|
|
19641
20055
|
let entries = lines.flatMap((line) => {
|
|
19642
20056
|
try {
|
|
@@ -19694,9 +20108,9 @@ import chalk13 from "chalk";
|
|
|
19694
20108
|
// src/cli/aggregate/report-audit.ts
|
|
19695
20109
|
init_costSync();
|
|
19696
20110
|
init_litellm();
|
|
19697
|
-
import
|
|
19698
|
-
import
|
|
19699
|
-
import
|
|
20111
|
+
import fs40 from "fs";
|
|
20112
|
+
import os36 from "os";
|
|
20113
|
+
import path41 from "path";
|
|
19700
20114
|
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;
|
|
19701
20115
|
function buildTestTimestamps(allEntries) {
|
|
19702
20116
|
const testTs = /* @__PURE__ */ new Set();
|
|
@@ -19776,8 +20190,8 @@ function getDateRange(period, now) {
|
|
|
19776
20190
|
}
|
|
19777
20191
|
}
|
|
19778
20192
|
function parseAuditLog(logPath) {
|
|
19779
|
-
if (!
|
|
19780
|
-
const raw =
|
|
20193
|
+
if (!fs40.existsSync(logPath)) return [];
|
|
20194
|
+
const raw = fs40.readFileSync(logPath, "utf-8");
|
|
19781
20195
|
return raw.split("\n").flatMap((line) => {
|
|
19782
20196
|
if (!line.trim()) return [];
|
|
19783
20197
|
try {
|
|
@@ -19837,25 +20251,25 @@ function freezeClaudeCost(acc) {
|
|
|
19837
20251
|
};
|
|
19838
20252
|
}
|
|
19839
20253
|
function processClaudeCostProject(proj, projectsDir, start, end, acc) {
|
|
19840
|
-
const projPath =
|
|
20254
|
+
const projPath = path41.join(projectsDir, proj);
|
|
19841
20255
|
let files;
|
|
19842
20256
|
try {
|
|
19843
|
-
const stat =
|
|
20257
|
+
const stat = fs40.statSync(projPath);
|
|
19844
20258
|
if (!stat.isDirectory()) return;
|
|
19845
|
-
files =
|
|
20259
|
+
files = fs40.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
19846
20260
|
} catch {
|
|
19847
20261
|
return;
|
|
19848
20262
|
}
|
|
19849
20263
|
const startMs = start.getTime();
|
|
19850
20264
|
for (const file of files) {
|
|
19851
|
-
const filePath =
|
|
20265
|
+
const filePath = path41.join(projPath, file);
|
|
19852
20266
|
try {
|
|
19853
|
-
if (
|
|
20267
|
+
if (fs40.statSync(filePath).mtimeMs < startMs) continue;
|
|
19854
20268
|
} catch {
|
|
19855
20269
|
continue;
|
|
19856
20270
|
}
|
|
19857
20271
|
try {
|
|
19858
|
-
const raw =
|
|
20272
|
+
const raw = fs40.readFileSync(filePath, "utf-8");
|
|
19859
20273
|
for (const line of raw.split("\n")) {
|
|
19860
20274
|
if (!line.trim()) continue;
|
|
19861
20275
|
let entry;
|
|
@@ -19905,10 +20319,10 @@ function processClaudeCostProject(proj, projectsDir, start, end, acc) {
|
|
|
19905
20319
|
}
|
|
19906
20320
|
function loadClaudeCost(start, end, projectsDir) {
|
|
19907
20321
|
const acc = emptyClaudeCostAccumulator();
|
|
19908
|
-
if (!
|
|
20322
|
+
if (!fs40.existsSync(projectsDir)) return freezeClaudeCost(acc);
|
|
19909
20323
|
let dirs;
|
|
19910
20324
|
try {
|
|
19911
|
-
dirs =
|
|
20325
|
+
dirs = fs40.readdirSync(projectsDir);
|
|
19912
20326
|
} catch {
|
|
19913
20327
|
return freezeClaudeCost(acc);
|
|
19914
20328
|
}
|
|
@@ -19920,7 +20334,7 @@ function loadClaudeCost(start, end, projectsDir) {
|
|
|
19920
20334
|
function processCodexCostFile(filePath, start, end, acc) {
|
|
19921
20335
|
let lines;
|
|
19922
20336
|
try {
|
|
19923
|
-
lines =
|
|
20337
|
+
lines = fs40.readFileSync(filePath, "utf-8").split("\n");
|
|
19924
20338
|
} catch {
|
|
19925
20339
|
return;
|
|
19926
20340
|
}
|
|
@@ -19963,33 +20377,33 @@ function processCodexCostFile(filePath, start, end, acc) {
|
|
|
19963
20377
|
const dateKey = sessionStart2.slice(0, 10);
|
|
19964
20378
|
acc.byDay.set(dateKey, (acc.byDay.get(dateKey) ?? 0) + cost);
|
|
19965
20379
|
}
|
|
19966
|
-
function
|
|
20380
|
+
function listCodexSessionFiles2(sessionsBase) {
|
|
19967
20381
|
const jsonlFiles = [];
|
|
19968
|
-
if (!
|
|
20382
|
+
if (!fs40.existsSync(sessionsBase)) return jsonlFiles;
|
|
19969
20383
|
try {
|
|
19970
|
-
for (const year of
|
|
19971
|
-
const yearPath =
|
|
20384
|
+
for (const year of fs40.readdirSync(sessionsBase)) {
|
|
20385
|
+
const yearPath = path41.join(sessionsBase, year);
|
|
19972
20386
|
try {
|
|
19973
|
-
if (!
|
|
20387
|
+
if (!fs40.statSync(yearPath).isDirectory()) continue;
|
|
19974
20388
|
} catch {
|
|
19975
20389
|
continue;
|
|
19976
20390
|
}
|
|
19977
|
-
for (const month of
|
|
19978
|
-
const monthPath =
|
|
20391
|
+
for (const month of fs40.readdirSync(yearPath)) {
|
|
20392
|
+
const monthPath = path41.join(yearPath, month);
|
|
19979
20393
|
try {
|
|
19980
|
-
if (!
|
|
20394
|
+
if (!fs40.statSync(monthPath).isDirectory()) continue;
|
|
19981
20395
|
} catch {
|
|
19982
20396
|
continue;
|
|
19983
20397
|
}
|
|
19984
|
-
for (const day of
|
|
19985
|
-
const dayPath =
|
|
20398
|
+
for (const day of fs40.readdirSync(monthPath)) {
|
|
20399
|
+
const dayPath = path41.join(monthPath, day);
|
|
19986
20400
|
try {
|
|
19987
|
-
if (!
|
|
20401
|
+
if (!fs40.statSync(dayPath).isDirectory()) continue;
|
|
19988
20402
|
} catch {
|
|
19989
20403
|
continue;
|
|
19990
20404
|
}
|
|
19991
|
-
for (const file of
|
|
19992
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(
|
|
20405
|
+
for (const file of fs40.readdirSync(dayPath)) {
|
|
20406
|
+
if (file.endsWith(".jsonl")) jsonlFiles.push(path41.join(dayPath, file));
|
|
19993
20407
|
}
|
|
19994
20408
|
}
|
|
19995
20409
|
}
|
|
@@ -20001,17 +20415,17 @@ function listCodexSessionFiles(sessionsBase) {
|
|
|
20001
20415
|
}
|
|
20002
20416
|
function loadCodexCost(start, end, sessionsBase) {
|
|
20003
20417
|
const acc = { total: 0, toolCalls: 0, byDay: /* @__PURE__ */ new Map() };
|
|
20004
|
-
const files =
|
|
20418
|
+
const files = listCodexSessionFiles2(sessionsBase);
|
|
20005
20419
|
for (const filePath of files) {
|
|
20006
20420
|
processCodexCostFile(filePath, start, end, acc);
|
|
20007
20421
|
}
|
|
20008
20422
|
return { total: acc.total, byDay: acc.byDay, toolCalls: acc.toolCalls };
|
|
20009
20423
|
}
|
|
20010
|
-
var
|
|
20011
|
-
function
|
|
20424
|
+
var GEMINI_FALLBACK_MODELS2 = ["gemini-2.5-flash", "gemini-2.0-flash"];
|
|
20425
|
+
function geminiPriceFor2(model) {
|
|
20012
20426
|
let tuple = pricingFor(model);
|
|
20013
20427
|
if (!tuple && /^gemini-/i.test(model)) {
|
|
20014
|
-
for (const proxy of
|
|
20428
|
+
for (const proxy of GEMINI_FALLBACK_MODELS2) {
|
|
20015
20429
|
tuple = pricingFor(proxy);
|
|
20016
20430
|
if (tuple) break;
|
|
20017
20431
|
}
|
|
@@ -20042,13 +20456,13 @@ function freezeGeminiCost(acc) {
|
|
|
20042
20456
|
function processGeminiCostFile(filePath, projectKey, start, end, acc) {
|
|
20043
20457
|
const startMs = start.getTime();
|
|
20044
20458
|
try {
|
|
20045
|
-
if (
|
|
20459
|
+
if (fs40.statSync(filePath).mtimeMs < startMs) return;
|
|
20046
20460
|
} catch {
|
|
20047
20461
|
return;
|
|
20048
20462
|
}
|
|
20049
20463
|
let raw;
|
|
20050
20464
|
try {
|
|
20051
|
-
raw =
|
|
20465
|
+
raw = fs40.readFileSync(filePath, "utf-8");
|
|
20052
20466
|
} catch {
|
|
20053
20467
|
return;
|
|
20054
20468
|
}
|
|
@@ -20069,7 +20483,7 @@ function processGeminiCostFile(filePath, projectKey, start, end, acc) {
|
|
|
20069
20483
|
}
|
|
20070
20484
|
const ts = new Date(entry.timestamp);
|
|
20071
20485
|
if (ts < start || ts > end) continue;
|
|
20072
|
-
const price =
|
|
20486
|
+
const price = geminiPriceFor2(entry.model);
|
|
20073
20487
|
if (!price) continue;
|
|
20074
20488
|
const inp = entry.tokens.input ?? 0;
|
|
20075
20489
|
const out = entry.tokens.output ?? 0;
|
|
@@ -20093,46 +20507,46 @@ function processGeminiCostFile(filePath, projectKey, start, end, acc) {
|
|
|
20093
20507
|
acc.byProject.set(projectKey, rollup);
|
|
20094
20508
|
}
|
|
20095
20509
|
}
|
|
20096
|
-
function
|
|
20510
|
+
function listGeminiSessionFiles2(geminiTmpDir2) {
|
|
20097
20511
|
const out = [];
|
|
20098
20512
|
let dirs;
|
|
20099
20513
|
try {
|
|
20100
|
-
if (!
|
|
20101
|
-
dirs =
|
|
20514
|
+
if (!fs40.statSync(geminiTmpDir2).isDirectory()) return out;
|
|
20515
|
+
dirs = fs40.readdirSync(geminiTmpDir2);
|
|
20102
20516
|
} catch {
|
|
20103
20517
|
return out;
|
|
20104
20518
|
}
|
|
20105
20519
|
for (const proj of dirs) {
|
|
20106
|
-
const chatsDir =
|
|
20520
|
+
const chatsDir = path41.join(geminiTmpDir2, proj, "chats");
|
|
20107
20521
|
let files;
|
|
20108
20522
|
try {
|
|
20109
|
-
if (!
|
|
20110
|
-
files =
|
|
20523
|
+
if (!fs40.statSync(chatsDir).isDirectory()) continue;
|
|
20524
|
+
files = fs40.readdirSync(chatsDir);
|
|
20111
20525
|
} catch {
|
|
20112
20526
|
continue;
|
|
20113
20527
|
}
|
|
20114
20528
|
for (const f of files) {
|
|
20115
20529
|
if (!f.endsWith(".jsonl")) continue;
|
|
20116
|
-
out.push({ projectKey: proj, file:
|
|
20530
|
+
out.push({ projectKey: proj, file: path41.join(chatsDir, f) });
|
|
20117
20531
|
}
|
|
20118
20532
|
}
|
|
20119
20533
|
return out;
|
|
20120
20534
|
}
|
|
20121
|
-
function loadGeminiCost(start, end,
|
|
20535
|
+
function loadGeminiCost(start, end, geminiTmpDir2) {
|
|
20122
20536
|
const acc = emptyGeminiAccumulator();
|
|
20123
|
-
if (!
|
|
20124
|
-
for (const { projectKey, file } of
|
|
20537
|
+
if (!fs40.existsSync(geminiTmpDir2)) return freezeGeminiCost(acc);
|
|
20538
|
+
for (const { projectKey, file } of listGeminiSessionFiles2(geminiTmpDir2)) {
|
|
20125
20539
|
processGeminiCostFile(file, projectKey, start, end, acc);
|
|
20126
20540
|
}
|
|
20127
20541
|
return freezeGeminiCost(acc);
|
|
20128
20542
|
}
|
|
20129
20543
|
function aggregateReportFromAudit(period, opts = {}) {
|
|
20130
20544
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
20131
|
-
const auditLogPath = opts.auditLogPath ??
|
|
20132
|
-
const claudeProjectsDir = opts.claudeProjectsDir ??
|
|
20133
|
-
const
|
|
20134
|
-
const
|
|
20135
|
-
const hasAuditFile =
|
|
20545
|
+
const auditLogPath = opts.auditLogPath ?? path41.join(os36.homedir(), ".node9", "audit.log");
|
|
20546
|
+
const claudeProjectsDir = opts.claudeProjectsDir ?? path41.join(os36.homedir(), ".claude", "projects");
|
|
20547
|
+
const codexSessionsDir2 = opts.codexSessionsDir ?? path41.join(os36.homedir(), ".codex", "sessions");
|
|
20548
|
+
const geminiTmpDir2 = opts.geminiTmpDir ?? path41.join(os36.homedir(), ".gemini", "tmp");
|
|
20549
|
+
const hasAuditFile = fs40.existsSync(auditLogPath);
|
|
20136
20550
|
const allEntries = opts.preloadedAuditEntries ?? parseAuditLog(auditLogPath);
|
|
20137
20551
|
const unackedDlp = allEntries.filter((e) => e.source === "response-dlp");
|
|
20138
20552
|
const { start, end } = getDateRange(period, now);
|
|
@@ -20149,8 +20563,8 @@ function aggregateReportFromAudit(period, opts = {}) {
|
|
|
20149
20563
|
};
|
|
20150
20564
|
});
|
|
20151
20565
|
const claudeCost = opts.preloadedClaudeCost ?? loadClaudeCost(start, end, claudeProjectsDir);
|
|
20152
|
-
const codexCost = opts.preloadedCodexCost ?? loadCodexCost(start, end,
|
|
20153
|
-
const geminiCost = opts.preloadedGeminiCost ?? loadGeminiCost(start, end,
|
|
20566
|
+
const codexCost = opts.preloadedCodexCost ?? loadCodexCost(start, end, codexSessionsDir2);
|
|
20567
|
+
const geminiCost = opts.preloadedGeminiCost ?? loadGeminiCost(start, end, geminiTmpDir2);
|
|
20154
20568
|
for (const [day, c] of codexCost.byDay) {
|
|
20155
20569
|
claudeCost.byDay.set(day, (claudeCost.byDay.get(day) ?? 0) + c);
|
|
20156
20570
|
}
|
|
@@ -20829,20 +21243,47 @@ function registerDaemonCommand(program2) {
|
|
|
20829
21243
|
// src/cli/commands/status.ts
|
|
20830
21244
|
init_core();
|
|
20831
21245
|
init_daemon();
|
|
21246
|
+
init_setup();
|
|
20832
21247
|
import chalk15 from "chalk";
|
|
20833
|
-
import
|
|
20834
|
-
import
|
|
20835
|
-
import
|
|
21248
|
+
import fs41 from "fs";
|
|
21249
|
+
import path42 from "path";
|
|
21250
|
+
import os37 from "os";
|
|
21251
|
+
import * as yaml2 from "yaml";
|
|
21252
|
+
function readHermesHooks(configPath) {
|
|
21253
|
+
if (!fs41.existsSync(configPath)) return null;
|
|
21254
|
+
let raw;
|
|
21255
|
+
try {
|
|
21256
|
+
raw = fs41.readFileSync(configPath, "utf-8");
|
|
21257
|
+
} catch {
|
|
21258
|
+
return null;
|
|
21259
|
+
}
|
|
21260
|
+
try {
|
|
21261
|
+
const cfg = yaml2.parse(raw);
|
|
21262
|
+
const has = (event) => (cfg?.hooks?.[event] ?? []).some(
|
|
21263
|
+
(e) => typeof e?.command === "string" && isNode9Hook(e.command)
|
|
21264
|
+
);
|
|
21265
|
+
return { pre: has("pre_tool_call"), post: has("post_tool_call") };
|
|
21266
|
+
} catch {
|
|
21267
|
+
console.error(
|
|
21268
|
+
chalk15.yellow(
|
|
21269
|
+
` \u26A0\uFE0F Hermes config.yaml at ${configPath} is not valid YAML \u2014 showing as unwired.`
|
|
21270
|
+
)
|
|
21271
|
+
);
|
|
21272
|
+
return { pre: false, post: false };
|
|
21273
|
+
}
|
|
21274
|
+
}
|
|
20836
21275
|
function readJson2(filePath) {
|
|
20837
21276
|
try {
|
|
20838
|
-
if (
|
|
21277
|
+
if (fs41.existsSync(filePath)) return JSON.parse(fs41.readFileSync(filePath, "utf-8"));
|
|
20839
21278
|
} catch {
|
|
20840
21279
|
}
|
|
20841
21280
|
return null;
|
|
20842
21281
|
}
|
|
20843
|
-
function
|
|
20844
|
-
|
|
20845
|
-
|
|
21282
|
+
function matchersHaveNode9Hook(matchers) {
|
|
21283
|
+
return (matchers ?? []).some((m) => (m.hooks ?? []).some((h) => isNode9Hook(h.command)));
|
|
21284
|
+
}
|
|
21285
|
+
function flatHaveNode9Hook(entries) {
|
|
21286
|
+
return (Array.isArray(entries) ? entries : []).some((h) => isNode9Hook(h.command));
|
|
20846
21287
|
}
|
|
20847
21288
|
function wrappedMcpServers(servers) {
|
|
20848
21289
|
if (!servers) return [];
|
|
@@ -20857,6 +21298,7 @@ function printAgentSection(label, hookPairs, wrapped) {
|
|
|
20857
21298
|
console.log(chalk15.red(` \u2717 ${name}`) + chalk15.gray(" (not wired)"));
|
|
20858
21299
|
}
|
|
20859
21300
|
}
|
|
21301
|
+
if (wrapped === null) return;
|
|
20860
21302
|
if (wrapped.length > 0) {
|
|
20861
21303
|
console.log(chalk15.cyan(` MCP proxied:`));
|
|
20862
21304
|
for (const entry of wrapped) {
|
|
@@ -20900,40 +21342,51 @@ function registerStatusCommand(program2) {
|
|
|
20900
21342
|
console.log("");
|
|
20901
21343
|
const modeLabel = settings.mode === "audit" ? chalk15.blue("audit") : settings.mode === "strict" ? chalk15.red("strict") : chalk15.white("standard");
|
|
20902
21344
|
console.log(` Mode: ${modeLabel}`);
|
|
20903
|
-
const projectConfig =
|
|
20904
|
-
const globalConfig =
|
|
21345
|
+
const projectConfig = path42.join(process.cwd(), "node9.config.json");
|
|
21346
|
+
const globalConfig = path42.join(os37.homedir(), ".node9", "config.json");
|
|
20905
21347
|
console.log(
|
|
20906
|
-
` Local: ${
|
|
21348
|
+
` Local: ${fs41.existsSync(projectConfig) ? chalk15.green("Active (node9.config.json)") : chalk15.gray("Not present")}`
|
|
20907
21349
|
);
|
|
20908
21350
|
console.log(
|
|
20909
|
-
` Global: ${
|
|
21351
|
+
` Global: ${fs41.existsSync(globalConfig) ? chalk15.green("Active (~/.node9/config.json)") : chalk15.gray("Not present")}`
|
|
20910
21352
|
);
|
|
20911
21353
|
if (mergedConfig.policy.sandboxPaths.length > 0) {
|
|
20912
21354
|
console.log(
|
|
20913
21355
|
` Sandbox: ${chalk15.green(`${mergedConfig.policy.sandboxPaths.length} safe zones active`)}`
|
|
20914
21356
|
);
|
|
20915
21357
|
}
|
|
20916
|
-
const homeDir2 =
|
|
21358
|
+
const homeDir2 = os37.homedir();
|
|
20917
21359
|
const claudeSettings = readJson2(
|
|
20918
|
-
|
|
21360
|
+
path42.join(homeDir2, ".claude", "settings.json")
|
|
20919
21361
|
);
|
|
20920
|
-
const claudeConfig = readJson2(
|
|
21362
|
+
const claudeConfig = readJson2(path42.join(homeDir2, ".claude.json"));
|
|
20921
21363
|
const geminiSettings = readJson2(
|
|
20922
|
-
|
|
21364
|
+
path42.join(homeDir2, ".gemini", "settings.json")
|
|
21365
|
+
);
|
|
21366
|
+
const cursorConfig = readJson2(path42.join(homeDir2, ".cursor", "mcp.json"));
|
|
21367
|
+
const antigravityHooks = readJson2(
|
|
21368
|
+
path42.join(homeDir2, ".gemini", "config", "hooks.json")
|
|
20923
21369
|
);
|
|
20924
|
-
const
|
|
20925
|
-
|
|
21370
|
+
const antigravityMcp = readJson2(
|
|
21371
|
+
path42.join(homeDir2, ".gemini", "config", "mcp_config.json")
|
|
21372
|
+
);
|
|
21373
|
+
const antigravityPresent = antigravityHooks !== null || fs41.existsSync(path42.join(homeDir2, ".gemini", "antigravity-cli")) || fs41.existsSync(path42.join(homeDir2, ".gemini", "antigravity-ide"));
|
|
21374
|
+
const copilotHooks = readJson2(
|
|
21375
|
+
path42.join(homeDir2, ".copilot", "hooks", "node9.json")
|
|
21376
|
+
);
|
|
21377
|
+
const copilotMcp = readJson2(
|
|
21378
|
+
path42.join(homeDir2, ".copilot", "mcp-config.json")
|
|
21379
|
+
);
|
|
21380
|
+
const copilotPresent = fs41.existsSync(path42.join(homeDir2, ".copilot"));
|
|
21381
|
+
const hermesHooks = readHermesHooks(hermesConfigPath(homeDir2));
|
|
21382
|
+
const agentFound = claudeSettings || claudeConfig || geminiSettings || cursorConfig || antigravityPresent || copilotPresent || hermesHooks;
|
|
20926
21383
|
if (agentFound) {
|
|
20927
21384
|
console.log("");
|
|
20928
21385
|
console.log(chalk15.bold(" Agent Wiring:"));
|
|
20929
21386
|
console.log("");
|
|
20930
21387
|
if (claudeSettings || claudeConfig) {
|
|
20931
|
-
const preHook = claudeSettings?.hooks?.PreToolUse
|
|
20932
|
-
|
|
20933
|
-
) ?? false;
|
|
20934
|
-
const postHook = claudeSettings?.hooks?.PostToolUse?.some(
|
|
20935
|
-
(m) => m.hooks.some((h) => isNode9Hook2(h.command))
|
|
20936
|
-
) ?? false;
|
|
21388
|
+
const preHook = matchersHaveNode9Hook(claudeSettings?.hooks?.PreToolUse);
|
|
21389
|
+
const postHook = matchersHaveNode9Hook(claudeSettings?.hooks?.PostToolUse);
|
|
20937
21390
|
printAgentSection(
|
|
20938
21391
|
"Claude Code",
|
|
20939
21392
|
[
|
|
@@ -20945,12 +21398,8 @@ function registerStatusCommand(program2) {
|
|
|
20945
21398
|
console.log("");
|
|
20946
21399
|
}
|
|
20947
21400
|
if (geminiSettings) {
|
|
20948
|
-
const beforeHook = geminiSettings.hooks?.BeforeTool
|
|
20949
|
-
|
|
20950
|
-
) ?? false;
|
|
20951
|
-
const afterHook = geminiSettings.hooks?.AfterTool?.some(
|
|
20952
|
-
(m) => m.hooks.some((h) => isNode9Hook2(h.command))
|
|
20953
|
-
) ?? false;
|
|
21401
|
+
const beforeHook = matchersHaveNode9Hook(geminiSettings.hooks?.BeforeTool);
|
|
21402
|
+
const afterHook = matchersHaveNode9Hook(geminiSettings.hooks?.AfterTool);
|
|
20954
21403
|
printAgentSection(
|
|
20955
21404
|
"Gemini CLI",
|
|
20956
21405
|
[
|
|
@@ -20961,10 +21410,50 @@ function registerStatusCommand(program2) {
|
|
|
20961
21410
|
);
|
|
20962
21411
|
console.log("");
|
|
20963
21412
|
}
|
|
21413
|
+
if (antigravityPresent) {
|
|
21414
|
+
const preHook = matchersHaveNode9Hook(antigravityHooks?.hooks?.PreToolUse);
|
|
21415
|
+
const postHook = matchersHaveNode9Hook(antigravityHooks?.hooks?.PostToolUse);
|
|
21416
|
+
printAgentSection(
|
|
21417
|
+
"Antigravity",
|
|
21418
|
+
[
|
|
21419
|
+
{ name: "PreToolUse (node9 check)", present: preHook },
|
|
21420
|
+
{ name: "PostToolUse (node9 log)", present: postHook }
|
|
21421
|
+
],
|
|
21422
|
+
wrappedMcpServers(antigravityMcp?.mcpServers)
|
|
21423
|
+
);
|
|
21424
|
+
console.log("");
|
|
21425
|
+
}
|
|
21426
|
+
if (copilotPresent) {
|
|
21427
|
+
const preHook = flatHaveNode9Hook(copilotHooks?.hooks?.PreToolUse);
|
|
21428
|
+
const postHook = flatHaveNode9Hook(copilotHooks?.hooks?.PostToolUse);
|
|
21429
|
+
const promptHook = flatHaveNode9Hook(copilotHooks?.hooks?.UserPromptSubmit);
|
|
21430
|
+
printAgentSection(
|
|
21431
|
+
"GitHub Copilot",
|
|
21432
|
+
[
|
|
21433
|
+
{ name: "PreToolUse (node9 check)", present: preHook },
|
|
21434
|
+
{ name: "PostToolUse (node9 log)", present: postHook },
|
|
21435
|
+
{ name: "UserPromptSubmit (node9 check)", present: promptHook }
|
|
21436
|
+
],
|
|
21437
|
+
wrappedMcpServers(copilotMcp?.mcpServers)
|
|
21438
|
+
);
|
|
21439
|
+
console.log("");
|
|
21440
|
+
}
|
|
20964
21441
|
if (cursorConfig) {
|
|
20965
21442
|
printAgentSection("Cursor", [], wrappedMcpServers(cursorConfig.mcpServers));
|
|
20966
21443
|
console.log("");
|
|
20967
21444
|
}
|
|
21445
|
+
if (hermesHooks) {
|
|
21446
|
+
printAgentSection(
|
|
21447
|
+
"Hermes Agent",
|
|
21448
|
+
[
|
|
21449
|
+
{ name: "pre_tool_call (node9 check)", present: hermesHooks.pre },
|
|
21450
|
+
{ name: "post_tool_call (node9 log)", present: hermesHooks.post }
|
|
21451
|
+
],
|
|
21452
|
+
null
|
|
21453
|
+
// Hermes has no MCP surface
|
|
21454
|
+
);
|
|
21455
|
+
console.log("");
|
|
21456
|
+
}
|
|
20968
21457
|
}
|
|
20969
21458
|
const pauseState = checkPause();
|
|
20970
21459
|
if (pauseState.paused) {
|
|
@@ -20984,9 +21473,9 @@ init_setup();
|
|
|
20984
21473
|
init_shields();
|
|
20985
21474
|
init_service();
|
|
20986
21475
|
import chalk16 from "chalk";
|
|
20987
|
-
import
|
|
20988
|
-
import
|
|
20989
|
-
import
|
|
21476
|
+
import fs42 from "fs";
|
|
21477
|
+
import path43 from "path";
|
|
21478
|
+
import os38 from "os";
|
|
20990
21479
|
import https4 from "https";
|
|
20991
21480
|
var DEFAULT_SHIELDS = ["bash-safe", "filesystem", "project-jail"];
|
|
20992
21481
|
function buildTelemetryPayload(agents, firstInstall) {
|
|
@@ -21072,16 +21561,16 @@ function registerInitCommand(program2) {
|
|
|
21072
21561
|
}
|
|
21073
21562
|
console.log("");
|
|
21074
21563
|
}
|
|
21075
|
-
const configPath =
|
|
21076
|
-
const isFirstInstall = !
|
|
21077
|
-
if (
|
|
21564
|
+
const configPath = path43.join(os38.homedir(), ".node9", "config.json");
|
|
21565
|
+
const isFirstInstall = !fs42.existsSync(configPath);
|
|
21566
|
+
if (fs42.existsSync(configPath) && !options.force) {
|
|
21078
21567
|
try {
|
|
21079
|
-
const existing = JSON.parse(
|
|
21568
|
+
const existing = JSON.parse(fs42.readFileSync(configPath, "utf-8"));
|
|
21080
21569
|
const settings = existing.settings ?? {};
|
|
21081
21570
|
if (settings.mode !== chosenMode) {
|
|
21082
21571
|
settings.mode = chosenMode;
|
|
21083
21572
|
existing.settings = settings;
|
|
21084
|
-
|
|
21573
|
+
fs42.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
|
|
21085
21574
|
console.log(chalk16.green(`\u2705 Mode updated: ${chosenMode}`));
|
|
21086
21575
|
} else {
|
|
21087
21576
|
console.log(chalk16.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
|
|
@@ -21094,9 +21583,9 @@ function registerInitCommand(program2) {
|
|
|
21094
21583
|
...DEFAULT_CONFIG,
|
|
21095
21584
|
settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
|
|
21096
21585
|
};
|
|
21097
|
-
const dir =
|
|
21098
|
-
if (!
|
|
21099
|
-
|
|
21586
|
+
const dir = path43.dirname(configPath);
|
|
21587
|
+
if (!fs42.existsSync(dir)) fs42.mkdirSync(dir, { recursive: true });
|
|
21588
|
+
fs42.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
|
|
21100
21589
|
console.log(chalk16.green(`\u2705 Config created: ${configPath}`));
|
|
21101
21590
|
console.log(chalk16.gray(` Mode: ${chosenMode}`));
|
|
21102
21591
|
}
|
|
@@ -21201,7 +21690,7 @@ function registerInitCommand(program2) {
|
|
|
21201
21690
|
}
|
|
21202
21691
|
|
|
21203
21692
|
// src/cli/commands/undo.ts
|
|
21204
|
-
import
|
|
21693
|
+
import path44 from "path";
|
|
21205
21694
|
import chalk18 from "chalk";
|
|
21206
21695
|
|
|
21207
21696
|
// src/tui/undo-navigator.ts
|
|
@@ -21360,7 +21849,7 @@ function findMatchingCwd(startDir, history) {
|
|
|
21360
21849
|
let dir = startDir;
|
|
21361
21850
|
while (true) {
|
|
21362
21851
|
if (cwds.has(dir)) return dir;
|
|
21363
|
-
const parent =
|
|
21852
|
+
const parent = path44.dirname(dir);
|
|
21364
21853
|
if (parent === dir) return null;
|
|
21365
21854
|
dir = parent;
|
|
21366
21855
|
}
|
|
@@ -21936,9 +22425,9 @@ function registerMcpGatewayCommand(program2) {
|
|
|
21936
22425
|
|
|
21937
22426
|
// src/mcp-server/index.ts
|
|
21938
22427
|
import readline5 from "readline";
|
|
21939
|
-
import
|
|
21940
|
-
import
|
|
21941
|
-
import
|
|
22428
|
+
import fs43 from "fs";
|
|
22429
|
+
import os39 from "os";
|
|
22430
|
+
import path45 from "path";
|
|
21942
22431
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
21943
22432
|
init_core();
|
|
21944
22433
|
init_daemon();
|
|
@@ -22189,13 +22678,13 @@ function handleStatus() {
|
|
|
22189
22678
|
lines.push(`Active shields: ${activeShields.length > 0 ? activeShields.join(", ") : "none"}`);
|
|
22190
22679
|
lines.push(`Smart rules: ${config.policy.smartRules.length} loaded`);
|
|
22191
22680
|
lines.push(`DLP: ${config.policy.dlp?.enabled !== false ? "enabled" : "disabled"}`);
|
|
22192
|
-
const projectConfig =
|
|
22193
|
-
const globalConfig =
|
|
22681
|
+
const projectConfig = path45.join(process.cwd(), "node9.config.json");
|
|
22682
|
+
const globalConfig = path45.join(os39.homedir(), ".node9", "config.json");
|
|
22194
22683
|
lines.push(
|
|
22195
|
-
`Project config (node9.config.json): ${
|
|
22684
|
+
`Project config (node9.config.json): ${fs43.existsSync(projectConfig) ? "present" : "not found"}`
|
|
22196
22685
|
);
|
|
22197
22686
|
lines.push(
|
|
22198
|
-
`Global config (~/.node9/config.json): ${
|
|
22687
|
+
`Global config (~/.node9/config.json): ${fs43.existsSync(globalConfig) ? "present" : "not found"}`
|
|
22199
22688
|
);
|
|
22200
22689
|
return lines.join("\n");
|
|
22201
22690
|
}
|
|
@@ -22269,21 +22758,21 @@ function handleShieldDisable(args) {
|
|
|
22269
22758
|
writeActiveShields(active.filter((s) => s !== name));
|
|
22270
22759
|
return `Shield "${name}" disabled.`;
|
|
22271
22760
|
}
|
|
22272
|
-
var GLOBAL_CONFIG_PATH =
|
|
22761
|
+
var GLOBAL_CONFIG_PATH = path45.join(os39.homedir(), ".node9", "config.json");
|
|
22273
22762
|
var APPROVER_CHANNELS = ["native", "browser", "cloud", "terminal"];
|
|
22274
22763
|
function readGlobalConfigRaw() {
|
|
22275
22764
|
try {
|
|
22276
|
-
if (
|
|
22277
|
-
return JSON.parse(
|
|
22765
|
+
if (fs43.existsSync(GLOBAL_CONFIG_PATH)) {
|
|
22766
|
+
return JSON.parse(fs43.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
|
|
22278
22767
|
}
|
|
22279
22768
|
} catch {
|
|
22280
22769
|
}
|
|
22281
22770
|
return {};
|
|
22282
22771
|
}
|
|
22283
22772
|
function writeGlobalConfigRaw(data) {
|
|
22284
|
-
const dir =
|
|
22285
|
-
if (!
|
|
22286
|
-
|
|
22773
|
+
const dir = path45.dirname(GLOBAL_CONFIG_PATH);
|
|
22774
|
+
if (!fs43.existsSync(dir)) fs43.mkdirSync(dir, { recursive: true });
|
|
22775
|
+
fs43.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(data, null, 2) + "\n");
|
|
22287
22776
|
}
|
|
22288
22777
|
function handleApproverList() {
|
|
22289
22778
|
const config = getConfig();
|
|
@@ -22327,9 +22816,9 @@ function handleApproverSet(args) {
|
|
|
22327
22816
|
function handleAuditGet(args) {
|
|
22328
22817
|
const limit = Math.min(typeof args.limit === "number" ? args.limit : 20, 100);
|
|
22329
22818
|
const filter = typeof args.filter === "string" && args.filter !== "all" ? args.filter : null;
|
|
22330
|
-
const auditPath =
|
|
22331
|
-
if (!
|
|
22332
|
-
const rawLines =
|
|
22819
|
+
const auditPath = path45.join(os39.homedir(), ".node9", "audit.log");
|
|
22820
|
+
if (!fs43.existsSync(auditPath)) return "No audit log found.";
|
|
22821
|
+
const rawLines = fs43.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
22333
22822
|
const parsed = [];
|
|
22334
22823
|
for (const line of rawLines) {
|
|
22335
22824
|
try {
|
|
@@ -22664,7 +23153,7 @@ function registerTrustCommand(program2) {
|
|
|
22664
23153
|
// src/cli/commands/mcp-pin.ts
|
|
22665
23154
|
init_mcp_pin();
|
|
22666
23155
|
import chalk21 from "chalk";
|
|
22667
|
-
import
|
|
23156
|
+
import fs44 from "fs";
|
|
22668
23157
|
function registerMcpPinCommand(program2) {
|
|
22669
23158
|
const pinCmd = program2.command("mcp").description("Manage MCP server tool definition pinning (rug pull defense)");
|
|
22670
23159
|
const pinSubCmd = pinCmd.command("pin").description("Manage pinned MCP server tool definitions");
|
|
@@ -22675,7 +23164,7 @@ function registerMcpPinCommand(program2) {
|
|
|
22675
23164
|
let repoCorrupt = false;
|
|
22676
23165
|
if (found.source === "repo") {
|
|
22677
23166
|
try {
|
|
22678
|
-
const raw =
|
|
23167
|
+
const raw = fs44.readFileSync(found.path, "utf-8");
|
|
22679
23168
|
const parsed = JSON.parse(raw);
|
|
22680
23169
|
repoEntries = parsed.servers ?? {};
|
|
22681
23170
|
} catch {
|
|
@@ -22989,9 +23478,9 @@ init_scan();
|
|
|
22989
23478
|
// src/cli/commands/sessions.ts
|
|
22990
23479
|
init_scan_summary();
|
|
22991
23480
|
import chalk24 from "chalk";
|
|
22992
|
-
import
|
|
22993
|
-
import
|
|
22994
|
-
import
|
|
23481
|
+
import fs45 from "fs";
|
|
23482
|
+
import path46 from "path";
|
|
23483
|
+
import os40 from "os";
|
|
22995
23484
|
var CLAUDE_PRICING3 = {
|
|
22996
23485
|
"claude-opus-4-6": { i: 5e-6, o: 25e-6, cw: 625e-8, cr: 5e-7 },
|
|
22997
23486
|
"claude-opus-4-5": { i: 5e-6, o: 25e-6, cw: 625e-8, cr: 5e-7 },
|
|
@@ -23032,10 +23521,10 @@ function encodeProjectPath(projectPath) {
|
|
|
23032
23521
|
}
|
|
23033
23522
|
function sessionJsonlPath(projectPath, sessionId) {
|
|
23034
23523
|
const encoded = encodeProjectPath(projectPath);
|
|
23035
|
-
return
|
|
23524
|
+
return path46.join(os40.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
|
|
23036
23525
|
}
|
|
23037
23526
|
function projectLabel(projectPath) {
|
|
23038
|
-
return projectPath.replace(
|
|
23527
|
+
return projectPath.replace(os40.homedir(), "~");
|
|
23039
23528
|
}
|
|
23040
23529
|
function parseHistoryLines(lines) {
|
|
23041
23530
|
const entries = [];
|
|
@@ -23104,10 +23593,10 @@ function parseSessionLines(lines) {
|
|
|
23104
23593
|
return { toolCalls, costUSD, hasSnapshot, modifiedFiles };
|
|
23105
23594
|
}
|
|
23106
23595
|
function loadAuditEntries(auditPath) {
|
|
23107
|
-
const aPath = auditPath ??
|
|
23596
|
+
const aPath = auditPath ?? path46.join(os40.homedir(), ".node9", "audit.log");
|
|
23108
23597
|
let raw;
|
|
23109
23598
|
try {
|
|
23110
|
-
raw =
|
|
23599
|
+
raw = fs45.readFileSync(aPath, "utf-8");
|
|
23111
23600
|
} catch {
|
|
23112
23601
|
return [];
|
|
23113
23602
|
}
|
|
@@ -23143,8 +23632,8 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
|
|
|
23143
23632
|
return result;
|
|
23144
23633
|
}
|
|
23145
23634
|
function buildGeminiSessions(days, allAuditEntries) {
|
|
23146
|
-
const tmpDir =
|
|
23147
|
-
if (!
|
|
23635
|
+
const tmpDir = path46.join(os40.homedir(), ".gemini", "tmp");
|
|
23636
|
+
if (!fs45.existsSync(tmpDir)) return [];
|
|
23148
23637
|
const cutoff = days !== null ? (() => {
|
|
23149
23638
|
const d = /* @__PURE__ */ new Date();
|
|
23150
23639
|
d.setDate(d.getDate() - days);
|
|
@@ -23153,35 +23642,35 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
23153
23642
|
})() : null;
|
|
23154
23643
|
let slugDirs;
|
|
23155
23644
|
try {
|
|
23156
|
-
slugDirs =
|
|
23645
|
+
slugDirs = fs45.readdirSync(tmpDir);
|
|
23157
23646
|
} catch {
|
|
23158
23647
|
return [];
|
|
23159
23648
|
}
|
|
23160
23649
|
const summaries = [];
|
|
23161
23650
|
for (const slug of slugDirs) {
|
|
23162
|
-
const slugPath =
|
|
23651
|
+
const slugPath = path46.join(tmpDir, slug);
|
|
23163
23652
|
try {
|
|
23164
|
-
if (!
|
|
23653
|
+
if (!fs45.statSync(slugPath).isDirectory()) continue;
|
|
23165
23654
|
} catch {
|
|
23166
23655
|
continue;
|
|
23167
23656
|
}
|
|
23168
|
-
let projectRoot =
|
|
23657
|
+
let projectRoot = path46.join(os40.homedir(), slug);
|
|
23169
23658
|
try {
|
|
23170
|
-
projectRoot =
|
|
23659
|
+
projectRoot = fs45.readFileSync(path46.join(slugPath, ".project_root"), "utf-8").trim();
|
|
23171
23660
|
} catch {
|
|
23172
23661
|
}
|
|
23173
|
-
const chatsDir =
|
|
23174
|
-
if (!
|
|
23662
|
+
const chatsDir = path46.join(slugPath, "chats");
|
|
23663
|
+
if (!fs45.existsSync(chatsDir)) continue;
|
|
23175
23664
|
let chatFiles;
|
|
23176
23665
|
try {
|
|
23177
|
-
chatFiles =
|
|
23666
|
+
chatFiles = fs45.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
|
|
23178
23667
|
} catch {
|
|
23179
23668
|
continue;
|
|
23180
23669
|
}
|
|
23181
23670
|
for (const chatFile of chatFiles) {
|
|
23182
23671
|
let raw;
|
|
23183
23672
|
try {
|
|
23184
|
-
raw =
|
|
23673
|
+
raw = fs45.readFileSync(path46.join(chatsDir, chatFile), "utf-8");
|
|
23185
23674
|
} catch {
|
|
23186
23675
|
continue;
|
|
23187
23676
|
}
|
|
@@ -23261,8 +23750,8 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
23261
23750
|
return summaries;
|
|
23262
23751
|
}
|
|
23263
23752
|
function buildCodexSessions(days, allAuditEntries) {
|
|
23264
|
-
const sessionsBase =
|
|
23265
|
-
if (!
|
|
23753
|
+
const sessionsBase = path46.join(os40.homedir(), ".codex", "sessions");
|
|
23754
|
+
if (!fs45.existsSync(sessionsBase)) return [];
|
|
23266
23755
|
const cutoff = days !== null ? (() => {
|
|
23267
23756
|
const d = /* @__PURE__ */ new Date();
|
|
23268
23757
|
d.setDate(d.getDate() - days);
|
|
@@ -23271,29 +23760,29 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
23271
23760
|
})() : null;
|
|
23272
23761
|
const jsonlFiles = [];
|
|
23273
23762
|
try {
|
|
23274
|
-
for (const year of
|
|
23275
|
-
const yearPath =
|
|
23763
|
+
for (const year of fs45.readdirSync(sessionsBase)) {
|
|
23764
|
+
const yearPath = path46.join(sessionsBase, year);
|
|
23276
23765
|
try {
|
|
23277
|
-
if (!
|
|
23766
|
+
if (!fs45.statSync(yearPath).isDirectory()) continue;
|
|
23278
23767
|
} catch {
|
|
23279
23768
|
continue;
|
|
23280
23769
|
}
|
|
23281
|
-
for (const month of
|
|
23282
|
-
const monthPath =
|
|
23770
|
+
for (const month of fs45.readdirSync(yearPath)) {
|
|
23771
|
+
const monthPath = path46.join(yearPath, month);
|
|
23283
23772
|
try {
|
|
23284
|
-
if (!
|
|
23773
|
+
if (!fs45.statSync(monthPath).isDirectory()) continue;
|
|
23285
23774
|
} catch {
|
|
23286
23775
|
continue;
|
|
23287
23776
|
}
|
|
23288
|
-
for (const day of
|
|
23289
|
-
const dayPath =
|
|
23777
|
+
for (const day of fs45.readdirSync(monthPath)) {
|
|
23778
|
+
const dayPath = path46.join(monthPath, day);
|
|
23290
23779
|
try {
|
|
23291
|
-
if (!
|
|
23780
|
+
if (!fs45.statSync(dayPath).isDirectory()) continue;
|
|
23292
23781
|
} catch {
|
|
23293
23782
|
continue;
|
|
23294
23783
|
}
|
|
23295
|
-
for (const file of
|
|
23296
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(
|
|
23784
|
+
for (const file of fs45.readdirSync(dayPath)) {
|
|
23785
|
+
if (file.endsWith(".jsonl")) jsonlFiles.push(path46.join(dayPath, file));
|
|
23297
23786
|
}
|
|
23298
23787
|
}
|
|
23299
23788
|
}
|
|
@@ -23305,7 +23794,7 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
23305
23794
|
for (const filePath of jsonlFiles) {
|
|
23306
23795
|
let lines;
|
|
23307
23796
|
try {
|
|
23308
|
-
lines =
|
|
23797
|
+
lines = fs45.readFileSync(filePath, "utf-8").split("\n");
|
|
23309
23798
|
} catch {
|
|
23310
23799
|
continue;
|
|
23311
23800
|
}
|
|
@@ -23383,10 +23872,10 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
23383
23872
|
return summaries;
|
|
23384
23873
|
}
|
|
23385
23874
|
function buildSessions(days, historyPath) {
|
|
23386
|
-
const hPath = historyPath ??
|
|
23875
|
+
const hPath = historyPath ?? path46.join(os40.homedir(), ".claude", "history.jsonl");
|
|
23387
23876
|
let historyRaw;
|
|
23388
23877
|
try {
|
|
23389
|
-
historyRaw =
|
|
23878
|
+
historyRaw = fs45.readFileSync(hPath, "utf-8");
|
|
23390
23879
|
} catch {
|
|
23391
23880
|
return [];
|
|
23392
23881
|
}
|
|
@@ -23411,7 +23900,7 @@ function buildSessions(days, historyPath) {
|
|
|
23411
23900
|
const jsonlFile = sessionJsonlPath(entry.project, entry.sessionId);
|
|
23412
23901
|
let sessionLines = [];
|
|
23413
23902
|
try {
|
|
23414
|
-
sessionLines =
|
|
23903
|
+
sessionLines = fs45.readFileSync(jsonlFile, "utf-8").split("\n");
|
|
23415
23904
|
} catch {
|
|
23416
23905
|
}
|
|
23417
23906
|
const { toolCalls, costUSD, hasSnapshot, modifiedFiles } = parseSessionLines(sessionLines);
|
|
@@ -23679,8 +24168,8 @@ function registerSessionsCommand(program2) {
|
|
|
23679
24168
|
console.log("");
|
|
23680
24169
|
console.log(chalk24.cyan.bold("\u{1F4CB} node9 sessions") + chalk24.dim(" \u2014 what your AI agent did"));
|
|
23681
24170
|
console.log("");
|
|
23682
|
-
const historyPath =
|
|
23683
|
-
if (!
|
|
24171
|
+
const historyPath = path46.join(os40.homedir(), ".claude", "history.jsonl");
|
|
24172
|
+
if (!fs45.existsSync(historyPath)) {
|
|
23684
24173
|
console.log(chalk24.yellow(" No Claude session history found at ~/.claude/history.jsonl"));
|
|
23685
24174
|
console.log(chalk24.gray(" Install Claude Code, run a few sessions, then try again.\n"));
|
|
23686
24175
|
return;
|
|
@@ -23717,12 +24206,12 @@ function registerSessionsCommand(program2) {
|
|
|
23717
24206
|
|
|
23718
24207
|
// src/cli/commands/skill-pin.ts
|
|
23719
24208
|
import chalk25 from "chalk";
|
|
23720
|
-
import
|
|
23721
|
-
import
|
|
23722
|
-
import
|
|
24209
|
+
import fs46 from "fs";
|
|
24210
|
+
import os41 from "os";
|
|
24211
|
+
import path47 from "path";
|
|
23723
24212
|
function wipeSkillSessions() {
|
|
23724
24213
|
try {
|
|
23725
|
-
|
|
24214
|
+
fs46.rmSync(path47.join(os41.homedir(), ".node9", "skill-sessions"), {
|
|
23726
24215
|
recursive: true,
|
|
23727
24216
|
force: true
|
|
23728
24217
|
});
|
|
@@ -23804,15 +24293,15 @@ function registerSkillPinCommand(program2) {
|
|
|
23804
24293
|
}
|
|
23805
24294
|
|
|
23806
24295
|
// src/cli/commands/decisions.ts
|
|
23807
|
-
import
|
|
23808
|
-
import
|
|
23809
|
-
import
|
|
24296
|
+
import fs47 from "fs";
|
|
24297
|
+
import os42 from "os";
|
|
24298
|
+
import path48 from "path";
|
|
23810
24299
|
import chalk26 from "chalk";
|
|
23811
|
-
var DECISIONS_FILE2 =
|
|
24300
|
+
var DECISIONS_FILE2 = path48.join(os42.homedir(), ".node9", "decisions.json");
|
|
23812
24301
|
function readDecisions() {
|
|
23813
24302
|
try {
|
|
23814
|
-
if (!
|
|
23815
|
-
const raw =
|
|
24303
|
+
if (!fs47.existsSync(DECISIONS_FILE2)) return {};
|
|
24304
|
+
const raw = fs47.readFileSync(DECISIONS_FILE2, "utf-8");
|
|
23816
24305
|
const parsed = JSON.parse(raw);
|
|
23817
24306
|
const out = {};
|
|
23818
24307
|
for (const [k, v] of Object.entries(parsed)) {
|
|
@@ -23824,11 +24313,11 @@ function readDecisions() {
|
|
|
23824
24313
|
}
|
|
23825
24314
|
}
|
|
23826
24315
|
function writeDecisions(d) {
|
|
23827
|
-
const dir =
|
|
23828
|
-
if (!
|
|
24316
|
+
const dir = path48.dirname(DECISIONS_FILE2);
|
|
24317
|
+
if (!fs47.existsSync(dir)) fs47.mkdirSync(dir, { recursive: true });
|
|
23829
24318
|
const tmp = `${DECISIONS_FILE2}.${process.pid}.tmp`;
|
|
23830
|
-
|
|
23831
|
-
|
|
24319
|
+
fs47.writeFileSync(tmp, JSON.stringify(d, null, 2));
|
|
24320
|
+
fs47.renameSync(tmp, DECISIONS_FILE2);
|
|
23832
24321
|
}
|
|
23833
24322
|
function registerDecisionsCommand(program2) {
|
|
23834
24323
|
const cmd = program2.command("decisions").description('Manage persistent "Always Allow" / "Always Deny" tool decisions');
|
|
@@ -23885,18 +24374,18 @@ Persistent decisions (${entries.length})
|
|
|
23885
24374
|
|
|
23886
24375
|
// src/cli/commands/dlp.ts
|
|
23887
24376
|
import chalk27 from "chalk";
|
|
23888
|
-
import
|
|
23889
|
-
import
|
|
23890
|
-
import
|
|
23891
|
-
var AUDIT_LOG =
|
|
23892
|
-
var RESOLVED_FILE =
|
|
24377
|
+
import fs48 from "fs";
|
|
24378
|
+
import path49 from "path";
|
|
24379
|
+
import os43 from "os";
|
|
24380
|
+
var AUDIT_LOG = path49.join(os43.homedir(), ".node9", "audit.log");
|
|
24381
|
+
var RESOLVED_FILE = path49.join(os43.homedir(), ".node9", "dlp-resolved.json");
|
|
23893
24382
|
var ANSI_RE = /\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g;
|
|
23894
24383
|
function stripAnsi(s) {
|
|
23895
24384
|
return s.replace(ANSI_RE, "");
|
|
23896
24385
|
}
|
|
23897
24386
|
function loadResolved() {
|
|
23898
24387
|
try {
|
|
23899
|
-
const raw = JSON.parse(
|
|
24388
|
+
const raw = JSON.parse(fs48.readFileSync(RESOLVED_FILE, "utf-8"));
|
|
23900
24389
|
return new Set(raw);
|
|
23901
24390
|
} catch {
|
|
23902
24391
|
return /* @__PURE__ */ new Set();
|
|
@@ -23904,13 +24393,13 @@ function loadResolved() {
|
|
|
23904
24393
|
}
|
|
23905
24394
|
function saveResolved(resolved) {
|
|
23906
24395
|
try {
|
|
23907
|
-
|
|
24396
|
+
fs48.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
|
|
23908
24397
|
} catch {
|
|
23909
24398
|
}
|
|
23910
24399
|
}
|
|
23911
24400
|
function loadDlpFindings() {
|
|
23912
|
-
if (!
|
|
23913
|
-
return
|
|
24401
|
+
if (!fs48.existsSync(AUDIT_LOG)) return [];
|
|
24402
|
+
return fs48.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
|
|
23914
24403
|
if (!line.trim()) return [];
|
|
23915
24404
|
try {
|
|
23916
24405
|
const e = JSON.parse(line);
|
|
@@ -24009,14 +24498,14 @@ function registerDlpCommand(program2) {
|
|
|
24009
24498
|
// src/cli/commands/mask.ts
|
|
24010
24499
|
init_dlp();
|
|
24011
24500
|
import chalk28 from "chalk";
|
|
24012
|
-
import
|
|
24013
|
-
import
|
|
24014
|
-
import
|
|
24501
|
+
import fs49 from "fs";
|
|
24502
|
+
import path50 from "path";
|
|
24503
|
+
import os44 from "os";
|
|
24015
24504
|
function findJsonlFiles(dir) {
|
|
24016
24505
|
const results = [];
|
|
24017
|
-
if (!
|
|
24018
|
-
for (const entry of
|
|
24019
|
-
const full =
|
|
24506
|
+
if (!fs49.existsSync(dir)) return results;
|
|
24507
|
+
for (const entry of fs49.readdirSync(dir, { withFileTypes: true })) {
|
|
24508
|
+
const full = path50.join(dir, entry.name);
|
|
24020
24509
|
if (entry.isDirectory()) results.push(...findJsonlFiles(full));
|
|
24021
24510
|
else if (entry.isFile() && entry.name.endsWith(".jsonl")) results.push(full);
|
|
24022
24511
|
}
|
|
@@ -24059,7 +24548,7 @@ function redactJson(obj) {
|
|
|
24059
24548
|
function processFile(filePath, dryRun) {
|
|
24060
24549
|
let raw;
|
|
24061
24550
|
try {
|
|
24062
|
-
raw =
|
|
24551
|
+
raw = fs49.readFileSync(filePath, "utf-8");
|
|
24063
24552
|
} catch {
|
|
24064
24553
|
return { redactedLines: 0, patterns: [] };
|
|
24065
24554
|
}
|
|
@@ -24091,14 +24580,14 @@ function processFile(filePath, dryRun) {
|
|
|
24091
24580
|
}
|
|
24092
24581
|
}
|
|
24093
24582
|
if (!dryRun && redactedLines > 0) {
|
|
24094
|
-
|
|
24583
|
+
fs49.writeFileSync(filePath, newLines.join("\n"), "utf-8");
|
|
24095
24584
|
}
|
|
24096
24585
|
return { redactedLines, patterns };
|
|
24097
24586
|
}
|
|
24098
24587
|
function processJsonFile(filePath, dryRun) {
|
|
24099
24588
|
let raw;
|
|
24100
24589
|
try {
|
|
24101
|
-
raw =
|
|
24590
|
+
raw = fs49.readFileSync(filePath, "utf-8");
|
|
24102
24591
|
} catch {
|
|
24103
24592
|
return { redactedLines: 0, patterns: [] };
|
|
24104
24593
|
}
|
|
@@ -24111,15 +24600,15 @@ function processJsonFile(filePath, dryRun) {
|
|
|
24111
24600
|
const { value, modified, found } = redactJson(parsed);
|
|
24112
24601
|
if (!modified) return { redactedLines: 0, patterns: [] };
|
|
24113
24602
|
if (!dryRun) {
|
|
24114
|
-
|
|
24603
|
+
fs49.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
|
|
24115
24604
|
}
|
|
24116
24605
|
return { redactedLines: 1, patterns: found };
|
|
24117
24606
|
}
|
|
24118
24607
|
function findJsonFiles(dir) {
|
|
24119
24608
|
const results = [];
|
|
24120
|
-
if (!
|
|
24121
|
-
for (const entry of
|
|
24122
|
-
const full =
|
|
24609
|
+
if (!fs49.existsSync(dir)) return results;
|
|
24610
|
+
for (const entry of fs49.readdirSync(dir, { withFileTypes: true })) {
|
|
24611
|
+
const full = path50.join(dir, entry.name);
|
|
24123
24612
|
if (entry.isDirectory()) results.push(...findJsonFiles(full));
|
|
24124
24613
|
else if (entry.isFile() && entry.name.endsWith(".json")) results.push(full);
|
|
24125
24614
|
}
|
|
@@ -24128,9 +24617,9 @@ function findJsonFiles(dir) {
|
|
|
24128
24617
|
function registerMaskCommand(program2) {
|
|
24129
24618
|
program2.command("mask").description("Redact plaintext secrets from local AI session history files").option("--dry-run", "show what would be redacted without making changes").option("--all", "scan all history (default: last 30 days)").action(async (options) => {
|
|
24130
24619
|
const dryRun = !!options.dryRun;
|
|
24131
|
-
const home =
|
|
24132
|
-
const claudeDir =
|
|
24133
|
-
const geminiDir =
|
|
24620
|
+
const home = os44.homedir();
|
|
24621
|
+
const claudeDir = path50.join(home, ".claude", "projects");
|
|
24622
|
+
const geminiDir = path50.join(home, ".gemini", "tmp");
|
|
24134
24623
|
const allFiles = [
|
|
24135
24624
|
...findJsonlFiles(claudeDir).map((p) => ({ path: p, type: "jsonl" })),
|
|
24136
24625
|
...findJsonFiles(geminiDir).map((p) => ({ path: p, type: "json" }))
|
|
@@ -24138,7 +24627,7 @@ function registerMaskCommand(program2) {
|
|
|
24138
24627
|
const cutoff = options.all ? null : new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
|
|
24139
24628
|
const filtered = cutoff ? allFiles.filter((f) => {
|
|
24140
24629
|
try {
|
|
24141
|
-
return
|
|
24630
|
+
return fs49.statSync(f.path).mtime >= cutoff;
|
|
24142
24631
|
} catch {
|
|
24143
24632
|
return false;
|
|
24144
24633
|
}
|
|
@@ -24194,20 +24683,20 @@ function registerMaskCommand(program2) {
|
|
|
24194
24683
|
// src/cli.ts
|
|
24195
24684
|
init_blast();
|
|
24196
24685
|
var { version } = JSON.parse(
|
|
24197
|
-
|
|
24686
|
+
fs52.readFileSync(path53.join(__dirname, "../package.json"), "utf-8")
|
|
24198
24687
|
);
|
|
24199
24688
|
var program = new Command();
|
|
24200
24689
|
program.name("node9").description("The Sudo Command for AI Agents").version(version);
|
|
24201
24690
|
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) => {
|
|
24202
24691
|
const DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept";
|
|
24203
|
-
const credPath =
|
|
24204
|
-
if (!
|
|
24205
|
-
|
|
24692
|
+
const credPath = path53.join(os47.homedir(), ".node9", "credentials.json");
|
|
24693
|
+
if (!fs52.existsSync(path53.dirname(credPath)))
|
|
24694
|
+
fs52.mkdirSync(path53.dirname(credPath), { recursive: true });
|
|
24206
24695
|
const profileName = options.profile || "default";
|
|
24207
24696
|
let existingCreds = {};
|
|
24208
24697
|
try {
|
|
24209
|
-
if (
|
|
24210
|
-
const raw = JSON.parse(
|
|
24698
|
+
if (fs52.existsSync(credPath)) {
|
|
24699
|
+
const raw = JSON.parse(fs52.readFileSync(credPath, "utf-8"));
|
|
24211
24700
|
if (raw.apiKey) {
|
|
24212
24701
|
existingCreds = {
|
|
24213
24702
|
default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL2 }
|
|
@@ -24219,14 +24708,14 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
24219
24708
|
} catch {
|
|
24220
24709
|
}
|
|
24221
24710
|
existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL2 };
|
|
24222
|
-
|
|
24711
|
+
fs52.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
|
|
24223
24712
|
let effectiveCloud = null;
|
|
24224
24713
|
if (profileName === "default") {
|
|
24225
|
-
const configPath =
|
|
24714
|
+
const configPath = path53.join(os47.homedir(), ".node9", "config.json");
|
|
24226
24715
|
let config = {};
|
|
24227
24716
|
try {
|
|
24228
|
-
if (
|
|
24229
|
-
config = JSON.parse(
|
|
24717
|
+
if (fs52.existsSync(configPath))
|
|
24718
|
+
config = JSON.parse(fs52.readFileSync(configPath, "utf-8"));
|
|
24230
24719
|
} catch {
|
|
24231
24720
|
}
|
|
24232
24721
|
if (!config.settings || typeof config.settings !== "object") config.settings = {};
|
|
@@ -24241,9 +24730,9 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
24241
24730
|
approvers.cloud = false;
|
|
24242
24731
|
}
|
|
24243
24732
|
s.approvers = approvers;
|
|
24244
|
-
if (!
|
|
24245
|
-
|
|
24246
|
-
|
|
24733
|
+
if (!fs52.existsSync(path53.dirname(configPath)))
|
|
24734
|
+
fs52.mkdirSync(path53.dirname(configPath), { recursive: true });
|
|
24735
|
+
fs52.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
24247
24736
|
effectiveCloud = approvers.cloud === true;
|
|
24248
24737
|
}
|
|
24249
24738
|
if (options.profile && profileName !== "default") {
|
|
@@ -24402,15 +24891,15 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
24402
24891
|
}
|
|
24403
24892
|
}
|
|
24404
24893
|
if (options.purge) {
|
|
24405
|
-
const node9Dir =
|
|
24406
|
-
if (
|
|
24894
|
+
const node9Dir = path53.join(os47.homedir(), ".node9");
|
|
24895
|
+
if (fs52.existsSync(node9Dir)) {
|
|
24407
24896
|
const confirmed = await confirm2({
|
|
24408
24897
|
message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
|
|
24409
24898
|
default: false
|
|
24410
24899
|
});
|
|
24411
24900
|
if (confirmed) {
|
|
24412
|
-
|
|
24413
|
-
if (
|
|
24901
|
+
fs52.rmSync(node9Dir, { recursive: true });
|
|
24902
|
+
if (fs52.existsSync(node9Dir)) {
|
|
24414
24903
|
console.error(
|
|
24415
24904
|
chalk30.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
|
|
24416
24905
|
);
|
|
@@ -24525,7 +25014,7 @@ program.command("tail").description("Stream live agent activity to the terminal"
|
|
|
24525
25014
|
});
|
|
24526
25015
|
program.command("monitor").description("Live interactive dashboard \u2014 activity feed, approvals, security signals").action(async () => {
|
|
24527
25016
|
try {
|
|
24528
|
-
const dashboardPath =
|
|
25017
|
+
const dashboardPath = path53.join(__dirname, "dashboard.mjs");
|
|
24529
25018
|
const dynamicImport = new Function("id", "return import(id)");
|
|
24530
25019
|
const mod = await dynamicImport(`file://${dashboardPath}`);
|
|
24531
25020
|
await mod.startMonitor();
|
|
@@ -24563,14 +25052,14 @@ Claude Code spawns this command every ~300ms and writes a JSON payload to stdin.
|
|
|
24563
25052
|
Run "node9 addto claude" to register it as the statusLine.`
|
|
24564
25053
|
).argument("[subcommand]", 'Optional: "debug on" / "debug off" to toggle stdin logging').argument("[state]", 'on|off \u2014 used with "debug" subcommand').action(async (subcommand, state) => {
|
|
24565
25054
|
if (subcommand === "debug") {
|
|
24566
|
-
const flagFile =
|
|
25055
|
+
const flagFile = path53.join(os47.homedir(), ".node9", "hud-debug");
|
|
24567
25056
|
if (state === "on") {
|
|
24568
|
-
|
|
24569
|
-
|
|
25057
|
+
fs52.mkdirSync(path53.dirname(flagFile), { recursive: true });
|
|
25058
|
+
fs52.writeFileSync(flagFile, "");
|
|
24570
25059
|
console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
|
|
24571
25060
|
console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
|
|
24572
25061
|
} else if (state === "off") {
|
|
24573
|
-
if (
|
|
25062
|
+
if (fs52.existsSync(flagFile)) fs52.unlinkSync(flagFile);
|
|
24574
25063
|
console.log("HUD debug logging disabled.");
|
|
24575
25064
|
} else {
|
|
24576
25065
|
console.error("Usage: node9 hud debug on|off");
|
|
@@ -24687,9 +25176,9 @@ if (process.argv[2] !== "daemon") {
|
|
|
24687
25176
|
const isCheckHook = process.argv[2] === "check";
|
|
24688
25177
|
if (isCheckHook) {
|
|
24689
25178
|
if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
|
|
24690
|
-
const logPath =
|
|
25179
|
+
const logPath = path53.join(os47.homedir(), ".node9", "hook-debug.log");
|
|
24691
25180
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
24692
|
-
|
|
25181
|
+
fs52.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
|
|
24693
25182
|
`);
|
|
24694
25183
|
}
|
|
24695
25184
|
process.exit(0);
|