@node9/proxy 1.30.0 → 1.31.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 +1314 -764
- package/dist/cli.mjs +1299 -749
- package/dist/dashboard.mjs +20 -2
- package/dist/index.js +414 -6
- package/dist/index.mjs +414 -6
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -179,8 +179,8 @@ function sanitizeConfig(raw) {
|
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
181
|
const lines = result.error.issues.map((issue) => {
|
|
182
|
-
const
|
|
183
|
-
return ` \u2022 ${
|
|
182
|
+
const path52 = issue.path.length > 0 ? issue.path.join(".") : "root";
|
|
183
|
+
return ` \u2022 ${path52}: ${issue.message}`;
|
|
184
184
|
});
|
|
185
185
|
return {
|
|
186
186
|
sanitized,
|
|
@@ -286,7 +286,15 @@ var init_config_schema = __esm({
|
|
|
286
286
|
}).optional(),
|
|
287
287
|
dlp: z.object({
|
|
288
288
|
enabled: z.boolean().optional(),
|
|
289
|
-
scanIgnoredTools: z.boolean().optional()
|
|
289
|
+
scanIgnoredTools: z.boolean().optional(),
|
|
290
|
+
pii: z.enum(["off", "block"]).optional()
|
|
291
|
+
}).optional(),
|
|
292
|
+
egress: z.object({
|
|
293
|
+
enabled: z.boolean().optional(),
|
|
294
|
+
mode: z.enum(["off", "review", "block"]).optional(),
|
|
295
|
+
allow: z.array(z.string()).optional(),
|
|
296
|
+
deny: z.array(z.string()).optional(),
|
|
297
|
+
allowPrivate: z.boolean().optional()
|
|
290
298
|
}).optional(),
|
|
291
299
|
loopDetection: z.object({
|
|
292
300
|
enabled: z.boolean().optional(),
|
|
@@ -669,6 +677,116 @@ function extractLiteralArgs(callExpr) {
|
|
|
669
677
|
}
|
|
670
678
|
return { name, flags, paths };
|
|
671
679
|
}
|
|
680
|
+
function resolveWordLiteral(w) {
|
|
681
|
+
const parts = w?.Parts || [];
|
|
682
|
+
let s = "";
|
|
683
|
+
for (const p of parts) {
|
|
684
|
+
const t = syntax.NodeType(p);
|
|
685
|
+
if (t === "Lit") s += (p.Value ?? "").replace(/\\(.)/g, "$1");
|
|
686
|
+
else if (t === "SglQuoted") s += p.Value ?? "";
|
|
687
|
+
else if (t === "DblQuoted") {
|
|
688
|
+
const inner = p.Parts || [];
|
|
689
|
+
if (!inner.every((ip) => syntax.NodeType(ip) === "Lit")) return null;
|
|
690
|
+
s += inner.map((ip) => ip.Value ?? "").join("");
|
|
691
|
+
} else {
|
|
692
|
+
return null;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
return s;
|
|
696
|
+
}
|
|
697
|
+
function parseDestHost(token) {
|
|
698
|
+
if (!token) return null;
|
|
699
|
+
let t = token.trim();
|
|
700
|
+
if (!t || t.startsWith("-")) return null;
|
|
701
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(t)) {
|
|
702
|
+
try {
|
|
703
|
+
const h = new URL(t).hostname.toLowerCase();
|
|
704
|
+
return h || null;
|
|
705
|
+
} catch {
|
|
706
|
+
return null;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
const at = t.lastIndexOf("@");
|
|
710
|
+
if (at >= 0) t = t.slice(at + 1);
|
|
711
|
+
t = t.split("/")[0];
|
|
712
|
+
t = t.replace(/:\d+$/, "");
|
|
713
|
+
t = t.split(":")[0];
|
|
714
|
+
t = t.toLowerCase();
|
|
715
|
+
if (t.length > 253) return null;
|
|
716
|
+
if (t === "localhost") return t;
|
|
717
|
+
if (/^[a-z0-9.-]+\.[a-z0-9.-]+$/.test(t)) return t;
|
|
718
|
+
return null;
|
|
719
|
+
}
|
|
720
|
+
function destTokensForBinary(binary, args) {
|
|
721
|
+
const valueFlags = VALUE_FLAGS[binary] ?? /* @__PURE__ */ new Set();
|
|
722
|
+
const positionals = [];
|
|
723
|
+
const urlFlagValues = [];
|
|
724
|
+
for (let i = 0; i < args.length; i++) {
|
|
725
|
+
const tok = args[i];
|
|
726
|
+
if (tok === null) continue;
|
|
727
|
+
if (tok.startsWith("-")) {
|
|
728
|
+
if (tok.startsWith("--url=")) {
|
|
729
|
+
urlFlagValues.push(tok.slice("--url=".length));
|
|
730
|
+
continue;
|
|
731
|
+
}
|
|
732
|
+
if (tok === "--url") {
|
|
733
|
+
const next = args[i + 1];
|
|
734
|
+
if (typeof next === "string") urlFlagValues.push(next);
|
|
735
|
+
i++;
|
|
736
|
+
continue;
|
|
737
|
+
}
|
|
738
|
+
if (tok.includes("=")) continue;
|
|
739
|
+
if (valueFlags.has(tok)) i++;
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
742
|
+
positionals.push(tok);
|
|
743
|
+
}
|
|
744
|
+
switch (binary) {
|
|
745
|
+
case "curl":
|
|
746
|
+
case "wget":
|
|
747
|
+
return [...urlFlagValues, ...positionals];
|
|
748
|
+
case "ssh":
|
|
749
|
+
return positionals.slice(0, 1);
|
|
750
|
+
case "scp":
|
|
751
|
+
return positionals.filter((p) => p.includes(":") || p.includes("@"));
|
|
752
|
+
case "nc":
|
|
753
|
+
case "ncat":
|
|
754
|
+
case "netcat":
|
|
755
|
+
return positionals.slice(0, 1);
|
|
756
|
+
default:
|
|
757
|
+
return [];
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
function extractShellDestinations(command) {
|
|
761
|
+
const f = parseShared(command);
|
|
762
|
+
if (f === PARSE_FAIL) return [];
|
|
763
|
+
const out = [];
|
|
764
|
+
const seen = /* @__PURE__ */ new Set();
|
|
765
|
+
try {
|
|
766
|
+
syntax.Walk(f, (node) => {
|
|
767
|
+
if (!node) return false;
|
|
768
|
+
const n = node;
|
|
769
|
+
if (syntax.NodeType(n) !== "CallExpr") return true;
|
|
770
|
+
const callArgs = n.Args || [];
|
|
771
|
+
if (callArgs.length === 0) return true;
|
|
772
|
+
const name = (resolveWordLiteral(callArgs[0]) || "").toLowerCase();
|
|
773
|
+
if (!NET_BINARIES.has(name)) return true;
|
|
774
|
+
const rest = callArgs.slice(1).map((a) => resolveWordLiteral(a));
|
|
775
|
+
for (const raw of destTokensForBinary(name, rest)) {
|
|
776
|
+
const host = parseDestHost(raw);
|
|
777
|
+
if (!host) continue;
|
|
778
|
+
const key = `${name}:${host}`;
|
|
779
|
+
if (seen.has(key)) continue;
|
|
780
|
+
seen.add(key);
|
|
781
|
+
out.push({ host, binary: name, raw });
|
|
782
|
+
}
|
|
783
|
+
return true;
|
|
784
|
+
});
|
|
785
|
+
} catch {
|
|
786
|
+
return out;
|
|
787
|
+
}
|
|
788
|
+
return out;
|
|
789
|
+
}
|
|
672
790
|
function analyzeFsOperation(command) {
|
|
673
791
|
if (!FS_OP_PRESCREEN_RE.test(command)) return null;
|
|
674
792
|
if (fsOpCache.has(command)) {
|
|
@@ -796,6 +914,64 @@ function analyzeShellCommand(command) {
|
|
|
796
914
|
}
|
|
797
915
|
return { actions, paths, allTokens };
|
|
798
916
|
}
|
|
917
|
+
function hostMatches(host, pattern) {
|
|
918
|
+
const h = host.toLowerCase();
|
|
919
|
+
const p = pattern.toLowerCase().trim();
|
|
920
|
+
if (!p) return false;
|
|
921
|
+
if (p === "*") return true;
|
|
922
|
+
if (p.startsWith("*.")) {
|
|
923
|
+
const suffix = p.slice(2);
|
|
924
|
+
return h === suffix || h.endsWith("." + suffix);
|
|
925
|
+
}
|
|
926
|
+
return h === p;
|
|
927
|
+
}
|
|
928
|
+
function matchesAny(host, patterns) {
|
|
929
|
+
for (const p of patterns) if (hostMatches(host, p)) return true;
|
|
930
|
+
return false;
|
|
931
|
+
}
|
|
932
|
+
function isPrivateHost(host) {
|
|
933
|
+
const h = host.toLowerCase();
|
|
934
|
+
if (h === "localhost" || h === "0.0.0.0") return true;
|
|
935
|
+
if (h.endsWith(".local") || h.endsWith(".internal") || h.endsWith(".localhost")) return true;
|
|
936
|
+
if (/^127\./.test(h)) return true;
|
|
937
|
+
if (/^10\./.test(h)) return true;
|
|
938
|
+
if (/^192\.168\./.test(h)) return true;
|
|
939
|
+
if (/^172\.(1[6-9]|2\d|3[01])\./.test(h)) return true;
|
|
940
|
+
return false;
|
|
941
|
+
}
|
|
942
|
+
function evaluateEgress(dests, policy) {
|
|
943
|
+
if (!policy.enabled) return null;
|
|
944
|
+
let review = null;
|
|
945
|
+
for (const d of dests) {
|
|
946
|
+
if (matchesAny(d.host, policy.deny)) {
|
|
947
|
+
return {
|
|
948
|
+
verdict: "block",
|
|
949
|
+
host: d.host,
|
|
950
|
+
binary: d.binary,
|
|
951
|
+
reason: `Egress to ${d.host} is on the deny list.`
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
if (policy.allowPrivate && isPrivateHost(d.host)) continue;
|
|
955
|
+
if (matchesAny(d.host, policy.allow) || matchesAny(d.host, DEFAULT_EGRESS_ALLOWLIST)) continue;
|
|
956
|
+
if (policy.mode === "block") {
|
|
957
|
+
return {
|
|
958
|
+
verdict: "block",
|
|
959
|
+
host: d.host,
|
|
960
|
+
binary: d.binary,
|
|
961
|
+
reason: `Egress to unknown host ${d.host} is blocked (egress policy: block).`
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
if (policy.mode === "review" && !review) {
|
|
965
|
+
review = {
|
|
966
|
+
verdict: "review",
|
|
967
|
+
host: d.host,
|
|
968
|
+
binary: d.binary,
|
|
969
|
+
reason: `${d.binary} is sending data to an unrecognized host (${d.host}). Approve this destination?`
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
return review;
|
|
974
|
+
}
|
|
799
975
|
function isSensitivePath(p) {
|
|
800
976
|
return SENSITIVE_PATTERNS.some((re) => re.test(p));
|
|
801
977
|
}
|
|
@@ -1023,9 +1199,9 @@ function matchesPattern(text, patterns) {
|
|
|
1023
1199
|
const withoutDotSlash = text.replace(/^\.\//, "");
|
|
1024
1200
|
return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
|
|
1025
1201
|
}
|
|
1026
|
-
function getNestedValue(obj,
|
|
1202
|
+
function getNestedValue(obj, path52) {
|
|
1027
1203
|
if (!obj || typeof obj !== "object") return null;
|
|
1028
|
-
const segments =
|
|
1204
|
+
const segments = path52.split(".");
|
|
1029
1205
|
for (const seg of segments) {
|
|
1030
1206
|
if (FORBIDDEN_PATH_SEGMENTS.has(seg)) return null;
|
|
1031
1207
|
}
|
|
@@ -1228,6 +1404,22 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
|
|
|
1228
1404
|
}
|
|
1229
1405
|
const ptVerdict = pipeChainVerdict(shellCommand, isTrustedHost2);
|
|
1230
1406
|
if (ptVerdict) return ptVerdict;
|
|
1407
|
+
if (config.policy.egress?.enabled) {
|
|
1408
|
+
const dests = extractShellDestinations(shellCommand);
|
|
1409
|
+
if (dests.length > 0) {
|
|
1410
|
+
const eg = evaluateEgress(dests, config.policy.egress);
|
|
1411
|
+
if (eg) {
|
|
1412
|
+
return {
|
|
1413
|
+
decision: eg.verdict,
|
|
1414
|
+
blockedByLabel: eg.verdict === "block" ? "\u{1F310} Node9 Egress (Blocked)" : "\u{1F310} Node9 Egress (Review)",
|
|
1415
|
+
reason: eg.reason,
|
|
1416
|
+
ruleName: `egress:${eg.binary}:${eg.host}`,
|
|
1417
|
+
ruleDescription: eg.reason,
|
|
1418
|
+
tier: eg.verdict === "block" ? 3 : 4
|
|
1419
|
+
};
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1231
1423
|
const firstToken = analyzed.actions[0] ?? "";
|
|
1232
1424
|
if (["ssh", "scp", "rsync"].includes(firstToken)) {
|
|
1233
1425
|
const rawTokens = shellCommand.trim().split(/\s+/);
|
|
@@ -1557,6 +1749,18 @@ function detectPii(text) {
|
|
|
1557
1749
|
if (PII_CC_RE.test(text)) found.add("Credit Card");
|
|
1558
1750
|
return [...found];
|
|
1559
1751
|
}
|
|
1752
|
+
function detectArgsPii(args) {
|
|
1753
|
+
if (args === null || args === void 0) return [];
|
|
1754
|
+
let text;
|
|
1755
|
+
try {
|
|
1756
|
+
text = typeof args === "string" ? args : JSON.stringify(args);
|
|
1757
|
+
} catch {
|
|
1758
|
+
return [];
|
|
1759
|
+
}
|
|
1760
|
+
if (typeof text !== "string") return [];
|
|
1761
|
+
if (text.length > MAX_PII_SCAN_BYTES) text = text.slice(0, MAX_PII_SCAN_BYTES);
|
|
1762
|
+
return detectPii(text).filter((p) => REALTIME_PII_PATTERNS.includes(p));
|
|
1763
|
+
}
|
|
1560
1764
|
function extractCanonicalFindings(call, ctx) {
|
|
1561
1765
|
const out = [];
|
|
1562
1766
|
const ts = call.timestamp;
|
|
@@ -1869,7 +2073,7 @@ function* stringValues(obj, depth = 0) {
|
|
|
1869
2073
|
}
|
|
1870
2074
|
for (const v of Object.values(obj)) yield* stringValues(v, depth + 1);
|
|
1871
2075
|
}
|
|
1872
|
-
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, FS_OP_CACHE_MAX, fsOpCache, 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, LONG_OUTPUT_THRESHOLD_BYTES, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, TERMINAL_ESCAPE_RE;
|
|
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;
|
|
1873
2077
|
var init_dist = __esm({
|
|
1874
2078
|
"packages/policy-engine/dist/index.mjs"() {
|
|
1875
2079
|
"use strict";
|
|
@@ -2483,8 +2687,112 @@ var init_dist = __esm({
|
|
|
2483
2687
|
"shield:project-jail:block-read-env",
|
|
2484
2688
|
"shield:project-jail:review-read-credentials"
|
|
2485
2689
|
]);
|
|
2690
|
+
NET_BINARIES = /* @__PURE__ */ new Set(["curl", "wget", "scp", "ssh", "nc", "ncat", "netcat"]);
|
|
2691
|
+
VALUE_FLAGS = {
|
|
2692
|
+
curl: /* @__PURE__ */ new Set([
|
|
2693
|
+
"-d",
|
|
2694
|
+
"--data",
|
|
2695
|
+
"--data-ascii",
|
|
2696
|
+
"--data-binary",
|
|
2697
|
+
"--data-raw",
|
|
2698
|
+
"--data-urlencode",
|
|
2699
|
+
"-F",
|
|
2700
|
+
"--form",
|
|
2701
|
+
"-H",
|
|
2702
|
+
"--header",
|
|
2703
|
+
"-X",
|
|
2704
|
+
"--request",
|
|
2705
|
+
"-o",
|
|
2706
|
+
"--output",
|
|
2707
|
+
"-T",
|
|
2708
|
+
"--upload-file",
|
|
2709
|
+
"-u",
|
|
2710
|
+
"--user",
|
|
2711
|
+
"-e",
|
|
2712
|
+
"--referer",
|
|
2713
|
+
"-A",
|
|
2714
|
+
"--user-agent",
|
|
2715
|
+
"-b",
|
|
2716
|
+
"--cookie",
|
|
2717
|
+
"-c",
|
|
2718
|
+
"--cookie-jar",
|
|
2719
|
+
"--connect-to",
|
|
2720
|
+
"--resolve",
|
|
2721
|
+
"--cacert",
|
|
2722
|
+
"--cert",
|
|
2723
|
+
"--key",
|
|
2724
|
+
"-x",
|
|
2725
|
+
"--proxy",
|
|
2726
|
+
"-m",
|
|
2727
|
+
"--max-time",
|
|
2728
|
+
"--retry"
|
|
2729
|
+
]),
|
|
2730
|
+
wget: /* @__PURE__ */ new Set([
|
|
2731
|
+
"-O",
|
|
2732
|
+
"--output-document",
|
|
2733
|
+
"--post-data",
|
|
2734
|
+
"--post-file",
|
|
2735
|
+
"--header",
|
|
2736
|
+
"-U",
|
|
2737
|
+
"--user-agent",
|
|
2738
|
+
"--user",
|
|
2739
|
+
"--password",
|
|
2740
|
+
"-o",
|
|
2741
|
+
"--output-file",
|
|
2742
|
+
"-P",
|
|
2743
|
+
"--directory-prefix",
|
|
2744
|
+
"-t",
|
|
2745
|
+
"--tries",
|
|
2746
|
+
"-T",
|
|
2747
|
+
"--timeout"
|
|
2748
|
+
]),
|
|
2749
|
+
scp: /* @__PURE__ */ new Set(["-i", "-F", "-l", "-o", "-c", "-S", "-P", "-J", "-D", "-W"]),
|
|
2750
|
+
ssh: /* @__PURE__ */ new Set([
|
|
2751
|
+
"-i",
|
|
2752
|
+
"-p",
|
|
2753
|
+
"-o",
|
|
2754
|
+
"-l",
|
|
2755
|
+
"-F",
|
|
2756
|
+
"-c",
|
|
2757
|
+
"-L",
|
|
2758
|
+
"-R",
|
|
2759
|
+
"-D",
|
|
2760
|
+
"-W",
|
|
2761
|
+
"-b",
|
|
2762
|
+
"-e",
|
|
2763
|
+
"-m",
|
|
2764
|
+
"-O",
|
|
2765
|
+
"-Q",
|
|
2766
|
+
"-S",
|
|
2767
|
+
"-J",
|
|
2768
|
+
"-w",
|
|
2769
|
+
"-B",
|
|
2770
|
+
"-I",
|
|
2771
|
+
"-E"
|
|
2772
|
+
]),
|
|
2773
|
+
nc: /* @__PURE__ */ new Set(["-p", "-s", "-w", "-X", "-x", "-e", "-g", "-G", "-i", "-O", "-T", "-q", "-m"])
|
|
2774
|
+
};
|
|
2486
2775
|
FS_OP_CACHE_MAX = 5e3;
|
|
2487
2776
|
fsOpCache = /* @__PURE__ */ new Map();
|
|
2777
|
+
DEFAULT_EGRESS_ALLOWLIST = [
|
|
2778
|
+
"*.github.com",
|
|
2779
|
+
"*.githubusercontent.com",
|
|
2780
|
+
"*.npmjs.org",
|
|
2781
|
+
"pypi.org",
|
|
2782
|
+
"*.pythonhosted.org",
|
|
2783
|
+
"crates.io",
|
|
2784
|
+
"*.crates.io",
|
|
2785
|
+
"rubygems.org",
|
|
2786
|
+
"proxy.golang.org",
|
|
2787
|
+
"sum.golang.org",
|
|
2788
|
+
"*.anthropic.com",
|
|
2789
|
+
"*.openai.com",
|
|
2790
|
+
"*.googleapis.com",
|
|
2791
|
+
"*.docker.io",
|
|
2792
|
+
"*.docker.com",
|
|
2793
|
+
"deb.debian.org",
|
|
2794
|
+
"*.ubuntu.com"
|
|
2795
|
+
];
|
|
2488
2796
|
SOURCE_COMMANDS = /* @__PURE__ */ new Set([
|
|
2489
2797
|
"cat",
|
|
2490
2798
|
"head",
|
|
@@ -3414,6 +3722,8 @@ var init_dist = __esm({
|
|
|
3414
3722
|
PII_SSN_RE = /\b\d{3}-\d{2}-\d{4}\b/;
|
|
3415
3723
|
PII_PHONE_RE = /\b(?:\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]\d{3}[-.\s]\d{4}\b/;
|
|
3416
3724
|
PII_CC_RE = /\b(?:4\d{3}|5[1-5]\d{2}|3[47]\d{2}|6\d{3})[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/;
|
|
3725
|
+
REALTIME_PII_PATTERNS = ["SSN", "Credit Card"];
|
|
3726
|
+
MAX_PII_SCAN_BYTES = 1e5;
|
|
3417
3727
|
LONG_OUTPUT_THRESHOLD_BYTES = 100 * 1024;
|
|
3418
3728
|
CANONICAL_EXTRACTOR_VERSION = "canonical-v4";
|
|
3419
3729
|
DEDUPE_PREVIEW_LEN = 120;
|
|
@@ -3704,6 +4014,11 @@ function getConfig(cwd) {
|
|
|
3704
4014
|
ignorePaths: [...DEFAULT_CONFIG.policy.snapshot.ignorePaths]
|
|
3705
4015
|
},
|
|
3706
4016
|
dlp: { ...DEFAULT_CONFIG.policy.dlp },
|
|
4017
|
+
egress: {
|
|
4018
|
+
...DEFAULT_CONFIG.policy.egress,
|
|
4019
|
+
allow: [...DEFAULT_CONFIG.policy.egress.allow],
|
|
4020
|
+
deny: [...DEFAULT_CONFIG.policy.egress.deny]
|
|
4021
|
+
},
|
|
3707
4022
|
loopDetection: { ...DEFAULT_CONFIG.policy.loopDetection },
|
|
3708
4023
|
skillPinning: {
|
|
3709
4024
|
...DEFAULT_CONFIG.policy.skillPinning,
|
|
@@ -3754,6 +4069,15 @@ function getConfig(cwd) {
|
|
|
3754
4069
|
const d = p.dlp;
|
|
3755
4070
|
if (d.enabled !== void 0) mergedPolicy.dlp.enabled = d.enabled;
|
|
3756
4071
|
if (d.scanIgnoredTools !== void 0) mergedPolicy.dlp.scanIgnoredTools = d.scanIgnoredTools;
|
|
4072
|
+
if (d.pii !== void 0) mergedPolicy.dlp.pii = d.pii;
|
|
4073
|
+
}
|
|
4074
|
+
if (p.egress) {
|
|
4075
|
+
const e = p.egress;
|
|
4076
|
+
if (e.enabled !== void 0) mergedPolicy.egress.enabled = e.enabled;
|
|
4077
|
+
if (e.mode !== void 0) mergedPolicy.egress.mode = e.mode;
|
|
4078
|
+
if (Array.isArray(e.allow)) mergedPolicy.egress.allow.push(...e.allow);
|
|
4079
|
+
if (Array.isArray(e.deny)) mergedPolicy.egress.deny.push(...e.deny);
|
|
4080
|
+
if (e.allowPrivate !== void 0) mergedPolicy.egress.allowPrivate = e.allowPrivate;
|
|
3757
4081
|
}
|
|
3758
4082
|
if (p.loopDetection) {
|
|
3759
4083
|
const ld = p.loopDetection;
|
|
@@ -4104,7 +4428,8 @@ var init_config = __esm({
|
|
|
4104
4428
|
description: "The AI wants to download a script from the internet and run it immediately, without you seeing what it contains. This is one of the most common ways malware gets installed."
|
|
4105
4429
|
}
|
|
4106
4430
|
],
|
|
4107
|
-
dlp: { enabled: true, scanIgnoredTools: true },
|
|
4431
|
+
dlp: { enabled: true, scanIgnoredTools: true, pii: "off" },
|
|
4432
|
+
egress: { enabled: false, mode: "review", allow: [], deny: [], allowPrivate: true },
|
|
4108
4433
|
loopDetection: { enabled: true, threshold: 5, windowSeconds: 120 },
|
|
4109
4434
|
skillPinning: { enabled: false, mode: "warn", roots: [] }
|
|
4110
4435
|
},
|
|
@@ -5207,6 +5532,14 @@ var init_context_sniper = __esm({
|
|
|
5207
5532
|
// src/ui/native.ts
|
|
5208
5533
|
import { spawn } from "child_process";
|
|
5209
5534
|
import path11 from "path";
|
|
5535
|
+
function resolveNativeDecision(opts) {
|
|
5536
|
+
const { code, output, elapsedMs, locked } = opts;
|
|
5537
|
+
if (locked) return "deny";
|
|
5538
|
+
const tooFast = elapsedMs < MIN_INTERACTION_MS;
|
|
5539
|
+
if (output.includes("Always Allow")) return tooFast ? "deny" : "always_allow";
|
|
5540
|
+
if (code === 0) return tooFast ? "deny" : "allow";
|
|
5541
|
+
return "deny";
|
|
5542
|
+
}
|
|
5210
5543
|
function formatArgs(args, matchedField, matchedWord) {
|
|
5211
5544
|
if (args === null || args === void 0) return { message: "(none)", intent: "EXEC" };
|
|
5212
5545
|
let parsed = args;
|
|
@@ -5366,6 +5699,7 @@ async function askNativePopup(toolName, args, agent, explainableLabel, locked =
|
|
|
5366
5699
|
);
|
|
5367
5700
|
return new Promise((resolve) => {
|
|
5368
5701
|
let childProcess = null;
|
|
5702
|
+
const startedAt = Date.now();
|
|
5369
5703
|
const onAbort = () => {
|
|
5370
5704
|
if (childProcess && childProcess.pid) {
|
|
5371
5705
|
try {
|
|
@@ -5423,19 +5757,20 @@ end run`;
|
|
|
5423
5757
|
}
|
|
5424
5758
|
let output = "";
|
|
5425
5759
|
childProcess?.stdout?.on("data", (d) => output += d.toString());
|
|
5426
|
-
childProcess?.on("
|
|
5760
|
+
childProcess?.on("error", () => {
|
|
5427
5761
|
if (signal) signal.removeEventListener("abort", onAbort);
|
|
5428
|
-
if (locked) return resolve("deny");
|
|
5429
|
-
if (output.includes("Always Allow")) return resolve("always_allow");
|
|
5430
|
-
if (code === 0) return resolve("allow");
|
|
5431
5762
|
resolve("deny");
|
|
5432
5763
|
});
|
|
5764
|
+
childProcess?.on("close", (code) => {
|
|
5765
|
+
if (signal) signal.removeEventListener("abort", onAbort);
|
|
5766
|
+
resolve(resolveNativeDecision({ code, output, elapsedMs: Date.now() - startedAt, locked }));
|
|
5767
|
+
});
|
|
5433
5768
|
} catch {
|
|
5434
5769
|
resolve("deny");
|
|
5435
5770
|
}
|
|
5436
5771
|
});
|
|
5437
5772
|
}
|
|
5438
|
-
var isTestEnv;
|
|
5773
|
+
var isTestEnv, MIN_INTERACTION_MS;
|
|
5439
5774
|
var init_native = __esm({
|
|
5440
5775
|
"src/ui/native.ts"() {
|
|
5441
5776
|
"use strict";
|
|
@@ -5443,6 +5778,7 @@ var init_native = __esm({
|
|
|
5443
5778
|
isTestEnv = () => {
|
|
5444
5779
|
return process.env.NODE_ENV === "test" || process.env.VITEST === "true" || !!process.env.VITEST || process.env.CI === "true" || !!process.env.CI || process.env.NODE9_TESTING === "1";
|
|
5445
5780
|
};
|
|
5781
|
+
MIN_INTERACTION_MS = 400;
|
|
5446
5782
|
}
|
|
5447
5783
|
});
|
|
5448
5784
|
|
|
@@ -5755,6 +6091,37 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
5755
6091
|
if (taintResult.tainted && taintResult.record) {
|
|
5756
6092
|
const { path: taintedPath, source: taintSource } = taintResult.record;
|
|
5757
6093
|
taintWarning = `\u26A0\uFE0F ${taintedPath} was flagged by ${taintSource} \u2014 this file may contain sensitive data`;
|
|
6094
|
+
if (config.policy.egress?.enabled) {
|
|
6095
|
+
const a = args && typeof args === "object" && !Array.isArray(args) ? args : {};
|
|
6096
|
+
const cmd = typeof a.command === "string" ? a.command : typeof a.cmd === "string" ? a.cmd : "";
|
|
6097
|
+
const dests = cmd ? extractShellDestinations(cmd) : [];
|
|
6098
|
+
const eg = dests.length > 0 ? evaluateEgress(dests, config.policy.egress) : null;
|
|
6099
|
+
if (eg) {
|
|
6100
|
+
if (!isManual)
|
|
6101
|
+
appendLocalAudit(
|
|
6102
|
+
toolName,
|
|
6103
|
+
args,
|
|
6104
|
+
"deny",
|
|
6105
|
+
isObserveMode ? "observe-mode-taint-egress-would-block" : "taint-egress-block",
|
|
6106
|
+
{ ...meta, ruleName: `taint-egress:${eg.host}` },
|
|
6107
|
+
hashAuditArgs
|
|
6108
|
+
);
|
|
6109
|
+
if (isObserveMode) {
|
|
6110
|
+
return {
|
|
6111
|
+
approved: true,
|
|
6112
|
+
checkedBy: "audit",
|
|
6113
|
+
observeWouldBlock: true,
|
|
6114
|
+
blockedByLabel: "\u{1F534} Node9 Taint+Egress (Exfiltration)"
|
|
6115
|
+
};
|
|
6116
|
+
}
|
|
6117
|
+
return {
|
|
6118
|
+
approved: false,
|
|
6119
|
+
reason: `\u{1F534} EXFILTRATION BLOCKED: the tainted file "${taintedPath}" is being sent to untrusted host "${eg.host}". A flagged file leaving to an unrecognized destination is blocked outright.`,
|
|
6120
|
+
blockedBy: "local-config",
|
|
6121
|
+
blockedByLabel: "\u{1F534} Node9 Taint+Egress (Exfiltration Blocked)"
|
|
6122
|
+
};
|
|
6123
|
+
}
|
|
6124
|
+
}
|
|
5758
6125
|
} else if (taintResult.daemonUnavailable) {
|
|
5759
6126
|
taintWarning = `\u26A0\uFE0F Taint service unavailable \u2014 cannot verify if ${filePaths.join(", ")} is clean`;
|
|
5760
6127
|
}
|
|
@@ -5803,6 +6170,35 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
5803
6170
|
explainableLabel = "\u{1F6A8} Node9 DLP (Credential Review)";
|
|
5804
6171
|
}
|
|
5805
6172
|
}
|
|
6173
|
+
if (config.policy.dlp.pii === "block" && (!isIgnoredTool2(toolName) || config.policy.dlp.scanIgnoredTools)) {
|
|
6174
|
+
const piiFound = detectArgsPii(args);
|
|
6175
|
+
if (piiFound.length > 0) {
|
|
6176
|
+
const piiReason = `\u{1F512} PII DETECTED: ${piiFound.join(", ")} found in tool arguments. Remove or tokenize personal data before passing it to a tool.`;
|
|
6177
|
+
if (!isManual)
|
|
6178
|
+
appendLocalAudit(
|
|
6179
|
+
toolName,
|
|
6180
|
+
args,
|
|
6181
|
+
"deny",
|
|
6182
|
+
isObserveMode ? "observe-mode-pii-would-block" : "pii-block",
|
|
6183
|
+
{ ...meta, piiPatterns: piiFound.join(",") },
|
|
6184
|
+
true
|
|
6185
|
+
);
|
|
6186
|
+
if (isObserveMode) {
|
|
6187
|
+
return {
|
|
6188
|
+
approved: true,
|
|
6189
|
+
checkedBy: "audit",
|
|
6190
|
+
observeWouldBlock: true,
|
|
6191
|
+
blockedByLabel: "\u{1F512} Node9 PII (Detected)"
|
|
6192
|
+
};
|
|
6193
|
+
}
|
|
6194
|
+
return {
|
|
6195
|
+
approved: false,
|
|
6196
|
+
reason: piiReason,
|
|
6197
|
+
blockedBy: "local-config",
|
|
6198
|
+
blockedByLabel: "\u{1F512} Node9 PII (Detected)"
|
|
6199
|
+
};
|
|
6200
|
+
}
|
|
6201
|
+
}
|
|
5806
6202
|
if (isObserveMode) {
|
|
5807
6203
|
if (!isIgnoredTool2(toolName)) {
|
|
5808
6204
|
const policyResult = await evaluatePolicy2(toolName, args, meta?.agent, options?.cwd);
|
|
@@ -6251,6 +6647,7 @@ var init_orchestrator = __esm({
|
|
|
6251
6647
|
init_native();
|
|
6252
6648
|
init_context_sniper();
|
|
6253
6649
|
init_dlp();
|
|
6650
|
+
init_dist();
|
|
6254
6651
|
init_audit();
|
|
6255
6652
|
init_config();
|
|
6256
6653
|
init_policy();
|
|
@@ -9602,18 +9999,118 @@ var init_litellm = __esm({
|
|
|
9602
9999
|
}
|
|
9603
10000
|
});
|
|
9604
10001
|
|
|
9605
|
-
// src/
|
|
10002
|
+
// src/cost-codex.ts
|
|
9606
10003
|
import fs17 from "fs";
|
|
9607
|
-
import path19 from "path";
|
|
9608
10004
|
import os16 from "os";
|
|
10005
|
+
import path19 from "path";
|
|
10006
|
+
function codexLogPath() {
|
|
10007
|
+
return path19.join(os16.homedir(), ".codex", "log", "codex-tui.log");
|
|
10008
|
+
}
|
|
10009
|
+
function parseCodexUsageLine(line) {
|
|
10010
|
+
if (!line.includes("token_usage")) return null;
|
|
10011
|
+
const date = line.match(RE_DATE)?.[1];
|
|
10012
|
+
if (!date) return null;
|
|
10013
|
+
const model = line.match(RE_MODEL)?.[1];
|
|
10014
|
+
if (!model) return null;
|
|
10015
|
+
const num3 = (re) => {
|
|
10016
|
+
const m = line.match(re);
|
|
10017
|
+
return m ? Number(m[1]) : 0;
|
|
10018
|
+
};
|
|
10019
|
+
const totalInput = num3(RE_INPUT);
|
|
10020
|
+
const cached = num3(RE_CACHED);
|
|
10021
|
+
const nonCached = num3(RE_NON_CACHED);
|
|
10022
|
+
const output = num3(RE_OUTPUT);
|
|
10023
|
+
const inputTokens = nonCached || Math.max(0, totalInput - cached);
|
|
10024
|
+
if (inputTokens === 0 && output === 0 && cached === 0) return null;
|
|
10025
|
+
const runId = line.match(RE_THREAD)?.[1] ?? "";
|
|
10026
|
+
const norm = normalizeModel(model);
|
|
10027
|
+
const p = pricingFor(model);
|
|
10028
|
+
const costUSD = p ? inputTokens * p[0] + output * p[1] + cached * p[3] : 0;
|
|
10029
|
+
return {
|
|
10030
|
+
date,
|
|
10031
|
+
model: norm,
|
|
10032
|
+
workingDir: "",
|
|
10033
|
+
// Codex span carries no cwd — attribution is by runId (thread)
|
|
10034
|
+
runId,
|
|
10035
|
+
costUSD,
|
|
10036
|
+
inputTokens,
|
|
10037
|
+
outputTokens: output,
|
|
10038
|
+
cacheReadTokens: cached,
|
|
10039
|
+
cacheWriteTokens: 0
|
|
10040
|
+
};
|
|
10041
|
+
}
|
|
10042
|
+
var RE_INPUT, RE_CACHED, RE_NON_CACHED, RE_OUTPUT, RE_MODEL, RE_THREAD, RE_DATE, codexSource;
|
|
10043
|
+
var init_cost_codex = __esm({
|
|
10044
|
+
"src/cost-codex.ts"() {
|
|
10045
|
+
"use strict";
|
|
10046
|
+
init_litellm();
|
|
10047
|
+
RE_INPUT = /token_usage\.input_tokens=(\d+)/;
|
|
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/;
|
|
10054
|
+
codexSource = {
|
|
10055
|
+
id: "codex",
|
|
10056
|
+
available() {
|
|
10057
|
+
try {
|
|
10058
|
+
return fs17.existsSync(codexLogPath());
|
|
10059
|
+
} catch {
|
|
10060
|
+
return false;
|
|
10061
|
+
}
|
|
10062
|
+
},
|
|
10063
|
+
collect(sinceMs) {
|
|
10064
|
+
const file = codexLogPath();
|
|
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
|
+
}
|
|
10072
|
+
const combined = /* @__PURE__ */ new Map();
|
|
10073
|
+
for (const line of content.split("\n")) {
|
|
10074
|
+
if (!line.includes("token_usage")) continue;
|
|
10075
|
+
if (sinceMs !== void 0) {
|
|
10076
|
+
const tsFull = line.match(/^(\S+)\s/)?.[1];
|
|
10077
|
+
if (tsFull) {
|
|
10078
|
+
const t = Date.parse(tsFull);
|
|
10079
|
+
if (!Number.isNaN(t) && t < sinceMs) continue;
|
|
10080
|
+
}
|
|
10081
|
+
}
|
|
10082
|
+
const e = parseCodexUsageLine(line);
|
|
10083
|
+
if (!e) continue;
|
|
10084
|
+
const key = `${e.date}::${e.model}::${e.workingDir ?? ""}::${e.runId ?? ""}`;
|
|
10085
|
+
const prev = combined.get(key);
|
|
10086
|
+
if (prev) {
|
|
10087
|
+
prev.costUSD += e.costUSD;
|
|
10088
|
+
prev.inputTokens += e.inputTokens;
|
|
10089
|
+
prev.outputTokens += e.outputTokens;
|
|
10090
|
+
prev.cacheReadTokens += e.cacheReadTokens;
|
|
10091
|
+
prev.cacheWriteTokens += e.cacheWriteTokens;
|
|
10092
|
+
} else {
|
|
10093
|
+
combined.set(key, { ...e });
|
|
10094
|
+
}
|
|
10095
|
+
}
|
|
10096
|
+
return [...combined.values()];
|
|
10097
|
+
}
|
|
10098
|
+
};
|
|
10099
|
+
}
|
|
10100
|
+
});
|
|
10101
|
+
|
|
10102
|
+
// src/costSync.ts
|
|
10103
|
+
import fs18 from "fs";
|
|
10104
|
+
import path20 from "path";
|
|
10105
|
+
import os17 from "os";
|
|
9609
10106
|
function decodeProjectDirName(dirName) {
|
|
9610
10107
|
return dirName.replace(/-/g, "/");
|
|
9611
10108
|
}
|
|
9612
10109
|
function parseJSONLFile(filePath, fallbackWorkingDir) {
|
|
9613
|
-
const runId =
|
|
10110
|
+
const runId = path20.basename(filePath, ".jsonl");
|
|
9614
10111
|
let content;
|
|
9615
10112
|
try {
|
|
9616
|
-
content =
|
|
10113
|
+
content = fs18.readFileSync(filePath, "utf8");
|
|
9617
10114
|
} catch {
|
|
9618
10115
|
return /* @__PURE__ */ new Map();
|
|
9619
10116
|
}
|
|
@@ -9668,51 +10165,30 @@ function parseJSONLFile(filePath, fallbackWorkingDir) {
|
|
|
9668
10165
|
}
|
|
9669
10166
|
return daily;
|
|
9670
10167
|
}
|
|
10168
|
+
function entryKey(e) {
|
|
10169
|
+
return `${e.date}::${e.model}::${e.workingDir ?? ""}::${e.runId ?? ""}`;
|
|
10170
|
+
}
|
|
9671
10171
|
function collectEntries(sinceMs) {
|
|
9672
|
-
const projectsDir = path19.join(os16.homedir(), ".claude", "projects");
|
|
9673
|
-
if (!fs17.existsSync(projectsDir)) return [];
|
|
9674
10172
|
const combined = /* @__PURE__ */ new Map();
|
|
9675
|
-
|
|
9676
|
-
|
|
9677
|
-
dirs = fs17.readdirSync(projectsDir);
|
|
9678
|
-
} catch {
|
|
9679
|
-
return [];
|
|
9680
|
-
}
|
|
9681
|
-
for (const dir of dirs) {
|
|
9682
|
-
const dirPath = path19.join(projectsDir, dir);
|
|
9683
|
-
try {
|
|
9684
|
-
if (!fs17.statSync(dirPath).isDirectory()) continue;
|
|
9685
|
-
} catch {
|
|
9686
|
-
continue;
|
|
9687
|
-
}
|
|
9688
|
-
let files;
|
|
10173
|
+
for (const src of COST_SOURCES) {
|
|
10174
|
+
let rows;
|
|
9689
10175
|
try {
|
|
9690
|
-
|
|
10176
|
+
if (!src.available()) continue;
|
|
10177
|
+
rows = src.collect(sinceMs);
|
|
9691
10178
|
} catch {
|
|
9692
10179
|
continue;
|
|
9693
10180
|
}
|
|
9694
|
-
const
|
|
9695
|
-
|
|
9696
|
-
const
|
|
9697
|
-
if (
|
|
9698
|
-
|
|
9699
|
-
|
|
9700
|
-
|
|
9701
|
-
|
|
9702
|
-
|
|
9703
|
-
}
|
|
9704
|
-
|
|
9705
|
-
for (const [key, e] of entries) {
|
|
9706
|
-
const prev = combined.get(key);
|
|
9707
|
-
if (prev) {
|
|
9708
|
-
prev.costUSD += e.costUSD;
|
|
9709
|
-
prev.inputTokens += e.inputTokens;
|
|
9710
|
-
prev.outputTokens += e.outputTokens;
|
|
9711
|
-
prev.cacheWriteTokens += e.cacheWriteTokens;
|
|
9712
|
-
prev.cacheReadTokens += e.cacheReadTokens;
|
|
9713
|
-
} else {
|
|
9714
|
-
combined.set(key, { ...e });
|
|
9715
|
-
}
|
|
10181
|
+
for (const e of rows) {
|
|
10182
|
+
const key = entryKey(e);
|
|
10183
|
+
const prev = combined.get(key);
|
|
10184
|
+
if (prev) {
|
|
10185
|
+
prev.costUSD += e.costUSD;
|
|
10186
|
+
prev.inputTokens += e.inputTokens;
|
|
10187
|
+
prev.outputTokens += e.outputTokens;
|
|
10188
|
+
prev.cacheWriteTokens += e.cacheWriteTokens;
|
|
10189
|
+
prev.cacheReadTokens += e.cacheReadTokens;
|
|
10190
|
+
} else {
|
|
10191
|
+
combined.set(key, { ...e });
|
|
9716
10192
|
}
|
|
9717
10193
|
}
|
|
9718
10194
|
}
|
|
@@ -9726,10 +10202,10 @@ async function syncCost() {
|
|
|
9726
10202
|
if (entries.length === 0) return;
|
|
9727
10203
|
let username = "unknown";
|
|
9728
10204
|
try {
|
|
9729
|
-
username =
|
|
10205
|
+
username = os17.userInfo().username;
|
|
9730
10206
|
} catch {
|
|
9731
10207
|
}
|
|
9732
|
-
const machineId = `${
|
|
10208
|
+
const machineId = `${os17.hostname()}:${username}`;
|
|
9733
10209
|
try {
|
|
9734
10210
|
const res = await fetch(`${creds.apiUrl}/cost-sync`, {
|
|
9735
10211
|
method: "POST",
|
|
@@ -9738,11 +10214,11 @@ async function syncCost() {
|
|
|
9738
10214
|
signal: AbortSignal.timeout(15e3)
|
|
9739
10215
|
});
|
|
9740
10216
|
if (!res.ok) {
|
|
9741
|
-
|
|
10217
|
+
fs18.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
|
|
9742
10218
|
`);
|
|
9743
10219
|
}
|
|
9744
10220
|
} catch (err2) {
|
|
9745
|
-
|
|
10221
|
+
fs18.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
|
|
9746
10222
|
`);
|
|
9747
10223
|
}
|
|
9748
10224
|
}
|
|
@@ -9755,14 +10231,72 @@ function startCostSync() {
|
|
|
9755
10231
|
}, SYNC_INTERVAL_MS);
|
|
9756
10232
|
timer.unref();
|
|
9757
10233
|
}
|
|
9758
|
-
var SYNC_INTERVAL_MS;
|
|
10234
|
+
var SYNC_INTERVAL_MS, claudeSource, COST_SOURCES;
|
|
9759
10235
|
var init_costSync = __esm({
|
|
9760
10236
|
"src/costSync.ts"() {
|
|
9761
10237
|
"use strict";
|
|
9762
10238
|
init_config();
|
|
9763
10239
|
init_audit();
|
|
9764
10240
|
init_litellm();
|
|
10241
|
+
init_cost_codex();
|
|
9765
10242
|
SYNC_INTERVAL_MS = 10 * 60 * 1e3;
|
|
10243
|
+
claudeSource = {
|
|
10244
|
+
id: "claude",
|
|
10245
|
+
available() {
|
|
10246
|
+
return fs18.existsSync(path20.join(os17.homedir(), ".claude", "projects"));
|
|
10247
|
+
},
|
|
10248
|
+
collect(sinceMs) {
|
|
10249
|
+
const projectsDir = path20.join(os17.homedir(), ".claude", "projects");
|
|
10250
|
+
if (!fs18.existsSync(projectsDir)) return [];
|
|
10251
|
+
const combined = /* @__PURE__ */ new Map();
|
|
10252
|
+
let dirs;
|
|
10253
|
+
try {
|
|
10254
|
+
dirs = fs18.readdirSync(projectsDir);
|
|
10255
|
+
} catch {
|
|
10256
|
+
return [];
|
|
10257
|
+
}
|
|
10258
|
+
for (const dir of dirs) {
|
|
10259
|
+
const dirPath = path20.join(projectsDir, dir);
|
|
10260
|
+
try {
|
|
10261
|
+
if (!fs18.statSync(dirPath).isDirectory()) continue;
|
|
10262
|
+
} catch {
|
|
10263
|
+
continue;
|
|
10264
|
+
}
|
|
10265
|
+
let files;
|
|
10266
|
+
try {
|
|
10267
|
+
files = fs18.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
|
|
10268
|
+
} catch {
|
|
10269
|
+
continue;
|
|
10270
|
+
}
|
|
10271
|
+
const fallbackWorkingDir = decodeProjectDirName(dir);
|
|
10272
|
+
for (const file of files) {
|
|
10273
|
+
const filePath = path20.join(dirPath, file);
|
|
10274
|
+
if (sinceMs !== void 0) {
|
|
10275
|
+
try {
|
|
10276
|
+
if (fs18.statSync(filePath).mtimeMs < sinceMs) continue;
|
|
10277
|
+
} catch {
|
|
10278
|
+
continue;
|
|
10279
|
+
}
|
|
10280
|
+
}
|
|
10281
|
+
const entries = parseJSONLFile(filePath, fallbackWorkingDir);
|
|
10282
|
+
for (const [key, e] of entries) {
|
|
10283
|
+
const prev = combined.get(key);
|
|
10284
|
+
if (prev) {
|
|
10285
|
+
prev.costUSD += e.costUSD;
|
|
10286
|
+
prev.inputTokens += e.inputTokens;
|
|
10287
|
+
prev.outputTokens += e.outputTokens;
|
|
10288
|
+
prev.cacheWriteTokens += e.cacheWriteTokens;
|
|
10289
|
+
prev.cacheReadTokens += e.cacheReadTokens;
|
|
10290
|
+
} else {
|
|
10291
|
+
combined.set(key, { ...e });
|
|
10292
|
+
}
|
|
10293
|
+
}
|
|
10294
|
+
}
|
|
10295
|
+
}
|
|
10296
|
+
return [...combined.values()];
|
|
10297
|
+
}
|
|
10298
|
+
};
|
|
10299
|
+
COST_SOURCES = [claudeSource, codexSource];
|
|
9766
10300
|
}
|
|
9767
10301
|
});
|
|
9768
10302
|
|
|
@@ -9778,9 +10312,9 @@ __export(scan_watermark_exports, {
|
|
|
9778
10312
|
tickForensicBroadcast: () => tickForensicBroadcast,
|
|
9779
10313
|
tickScanWatcher: () => tickScanWatcher
|
|
9780
10314
|
});
|
|
9781
|
-
import
|
|
9782
|
-
import
|
|
9783
|
-
import
|
|
10315
|
+
import fs19 from "fs";
|
|
10316
|
+
import os18 from "os";
|
|
10317
|
+
import path21 from "path";
|
|
9784
10318
|
import readline from "readline";
|
|
9785
10319
|
function freshWatermark() {
|
|
9786
10320
|
return {
|
|
@@ -9793,7 +10327,7 @@ function freshWatermark() {
|
|
|
9793
10327
|
function loadWatermark() {
|
|
9794
10328
|
let raw;
|
|
9795
10329
|
try {
|
|
9796
|
-
raw =
|
|
10330
|
+
raw = fs19.readFileSync(WATERMARK_FILE(), "utf-8");
|
|
9797
10331
|
} catch {
|
|
9798
10332
|
return { status: "fresh", wm: freshWatermark() };
|
|
9799
10333
|
}
|
|
@@ -9845,28 +10379,28 @@ function loadWatermark() {
|
|
|
9845
10379
|
function saveWatermark(wm) {
|
|
9846
10380
|
if (wm.schemaVersion > WATERMARK_SCHEMA_VERSION) return;
|
|
9847
10381
|
const target = WATERMARK_FILE();
|
|
9848
|
-
const dir =
|
|
9849
|
-
if (!
|
|
10382
|
+
const dir = path21.dirname(target);
|
|
10383
|
+
if (!fs19.existsSync(dir)) fs19.mkdirSync(dir, { recursive: true });
|
|
9850
10384
|
const tmp = target + ".tmp";
|
|
9851
|
-
|
|
9852
|
-
|
|
10385
|
+
fs19.writeFileSync(tmp, JSON.stringify(wm, null, 2) + "\n", "utf-8");
|
|
10386
|
+
fs19.renameSync(tmp, target);
|
|
9853
10387
|
}
|
|
9854
10388
|
function listJsonlFiles() {
|
|
9855
10389
|
const root = PROJECTS_DIR();
|
|
9856
|
-
if (!
|
|
10390
|
+
if (!fs19.existsSync(root)) return [];
|
|
9857
10391
|
const out = [];
|
|
9858
|
-
for (const entry of
|
|
10392
|
+
for (const entry of fs19.readdirSync(root, { withFileTypes: true })) {
|
|
9859
10393
|
if (!entry.isDirectory()) continue;
|
|
9860
|
-
const projectDir =
|
|
10394
|
+
const projectDir = path21.join(root, entry.name);
|
|
9861
10395
|
let inner;
|
|
9862
10396
|
try {
|
|
9863
|
-
inner =
|
|
10397
|
+
inner = fs19.readdirSync(projectDir, { withFileTypes: true });
|
|
9864
10398
|
} catch {
|
|
9865
10399
|
continue;
|
|
9866
10400
|
}
|
|
9867
10401
|
for (const file of inner) {
|
|
9868
10402
|
if (file.isFile() && file.name.endsWith(".jsonl")) {
|
|
9869
|
-
out.push(
|
|
10403
|
+
out.push(path21.join(projectDir, file.name));
|
|
9870
10404
|
}
|
|
9871
10405
|
}
|
|
9872
10406
|
}
|
|
@@ -9874,7 +10408,7 @@ function listJsonlFiles() {
|
|
|
9874
10408
|
}
|
|
9875
10409
|
function fileSize(p) {
|
|
9876
10410
|
try {
|
|
9877
|
-
return
|
|
10411
|
+
return fs19.statSync(p).size;
|
|
9878
10412
|
} catch {
|
|
9879
10413
|
return 0;
|
|
9880
10414
|
}
|
|
@@ -9882,7 +10416,7 @@ function fileSize(p) {
|
|
|
9882
10416
|
async function scanDelta(filePath, fromByte, onLine) {
|
|
9883
10417
|
const size = fileSize(filePath);
|
|
9884
10418
|
if (size <= fromByte) return fromByte;
|
|
9885
|
-
const stream =
|
|
10419
|
+
const stream = fs19.createReadStream(filePath, {
|
|
9886
10420
|
start: fromByte,
|
|
9887
10421
|
end: size - 1,
|
|
9888
10422
|
highWaterMark: 64 * 1024
|
|
@@ -9994,7 +10528,7 @@ async function tickForensicBroadcast(offsets) {
|
|
|
9994
10528
|
continue;
|
|
9995
10529
|
}
|
|
9996
10530
|
if (size <= offset) continue;
|
|
9997
|
-
const sessionId =
|
|
10531
|
+
const sessionId = path21.basename(file, ".jsonl");
|
|
9998
10532
|
const newOffset = await scanDelta(file, offset, (obj, lineIndex) => {
|
|
9999
10533
|
out.push(...extractFindingsFromLine(obj, sessionId, lineIndex));
|
|
10000
10534
|
});
|
|
@@ -10053,7 +10587,7 @@ function emptyTick(uploadAs) {
|
|
|
10053
10587
|
function readRawWatermarkPreservingOffsets() {
|
|
10054
10588
|
let raw;
|
|
10055
10589
|
try {
|
|
10056
|
-
raw =
|
|
10590
|
+
raw = fs19.readFileSync(WATERMARK_FILE(), "utf-8");
|
|
10057
10591
|
} catch {
|
|
10058
10592
|
return null;
|
|
10059
10593
|
}
|
|
@@ -10087,13 +10621,13 @@ async function runActualTick(wm) {
|
|
|
10087
10621
|
if (!known) {
|
|
10088
10622
|
let mtimeMs = 0;
|
|
10089
10623
|
try {
|
|
10090
|
-
mtimeMs =
|
|
10624
|
+
mtimeMs = fs19.statSync(filePath).mtime.getTime();
|
|
10091
10625
|
} catch {
|
|
10092
10626
|
continue;
|
|
10093
10627
|
}
|
|
10094
10628
|
if (mtimeMs >= watermarkCreatedAt) {
|
|
10095
10629
|
filesNew++;
|
|
10096
|
-
const sessionId2 =
|
|
10630
|
+
const sessionId2 = path21.basename(filePath, ".jsonl");
|
|
10097
10631
|
const newScannedTo2 = await scanDelta(filePath, 0, (obj, lineIndex) => {
|
|
10098
10632
|
totalToolCalls++;
|
|
10099
10633
|
toolCallsBySession[sessionId2] = (toolCallsBySession[sessionId2] ?? 0) + 1;
|
|
@@ -10111,7 +10645,7 @@ async function runActualTick(wm) {
|
|
|
10111
10645
|
filesSkipped++;
|
|
10112
10646
|
continue;
|
|
10113
10647
|
}
|
|
10114
|
-
const sessionId =
|
|
10648
|
+
const sessionId = path21.basename(filePath, ".jsonl");
|
|
10115
10649
|
const newScannedTo = await scanDelta(filePath, known.scannedTo, (obj, lineIndex) => {
|
|
10116
10650
|
totalToolCalls++;
|
|
10117
10651
|
toolCallsBySession[sessionId] = (toolCallsBySession[sessionId] ?? 0) + 1;
|
|
@@ -10139,8 +10673,8 @@ var init_scan_watermark = __esm({
|
|
|
10139
10673
|
"use strict";
|
|
10140
10674
|
init_dlp();
|
|
10141
10675
|
init_dist();
|
|
10142
|
-
PROJECTS_DIR = () =>
|
|
10143
|
-
WATERMARK_FILE = () =>
|
|
10676
|
+
PROJECTS_DIR = () => path21.join(os18.homedir(), ".claude", "projects");
|
|
10677
|
+
WATERMARK_FILE = () => path21.join(os18.homedir(), ".node9", "scan-watermark.json");
|
|
10144
10678
|
MAX_LINE_BYTES = 2 * 1024 * 1024;
|
|
10145
10679
|
WATERMARK_SCHEMA_VERSION = 2;
|
|
10146
10680
|
LONG_OUTPUT_THRESHOLD_BYTES2 = LONG_OUTPUT_THRESHOLD_BYTES;
|
|
@@ -10151,14 +10685,15 @@ var init_scan_watermark = __esm({
|
|
|
10151
10685
|
var scan_upload_history_exports = {};
|
|
10152
10686
|
__export(scan_upload_history_exports, {
|
|
10153
10687
|
buildSessionTotals: () => buildSessionTotals,
|
|
10688
|
+
extractHistoricalLoops: () => extractHistoricalLoops,
|
|
10154
10689
|
iterateJsonlFiles: () => iterateJsonlFiles,
|
|
10155
10690
|
parseSinceCutoff: () => parseSinceCutoff,
|
|
10156
10691
|
runUploadHistory: () => runUploadHistory
|
|
10157
10692
|
});
|
|
10158
|
-
import
|
|
10693
|
+
import fs20 from "fs";
|
|
10159
10694
|
import https from "https";
|
|
10160
|
-
import
|
|
10161
|
-
import
|
|
10695
|
+
import os19 from "os";
|
|
10696
|
+
import path22 from "path";
|
|
10162
10697
|
import chalk4 from "chalk";
|
|
10163
10698
|
function emptySignals2() {
|
|
10164
10699
|
return {
|
|
@@ -10193,40 +10728,40 @@ function parseSinceCutoff(raw, now = /* @__PURE__ */ new Date()) {
|
|
|
10193
10728
|
return now.getTime() - 90 * 864e5;
|
|
10194
10729
|
}
|
|
10195
10730
|
function* iterateJsonlFiles(cutoffMs) {
|
|
10196
|
-
const projectsDir =
|
|
10731
|
+
const projectsDir = path22.join(os19.homedir(), ".claude", "projects");
|
|
10197
10732
|
let dirs;
|
|
10198
10733
|
try {
|
|
10199
|
-
dirs =
|
|
10734
|
+
dirs = fs20.readdirSync(projectsDir);
|
|
10200
10735
|
} catch {
|
|
10201
10736
|
return;
|
|
10202
10737
|
}
|
|
10203
10738
|
for (const dir of dirs) {
|
|
10204
|
-
const dirPath =
|
|
10739
|
+
const dirPath = path22.join(projectsDir, dir);
|
|
10205
10740
|
let stats;
|
|
10206
10741
|
try {
|
|
10207
|
-
stats =
|
|
10742
|
+
stats = fs20.statSync(dirPath);
|
|
10208
10743
|
} catch {
|
|
10209
10744
|
continue;
|
|
10210
10745
|
}
|
|
10211
10746
|
if (!stats.isDirectory()) continue;
|
|
10212
10747
|
let files;
|
|
10213
10748
|
try {
|
|
10214
|
-
files =
|
|
10749
|
+
files = fs20.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
|
|
10215
10750
|
} catch {
|
|
10216
10751
|
continue;
|
|
10217
10752
|
}
|
|
10218
10753
|
for (const file of files) {
|
|
10219
|
-
const filePath =
|
|
10754
|
+
const filePath = path22.join(dirPath, file);
|
|
10220
10755
|
let mtime = 0;
|
|
10221
10756
|
try {
|
|
10222
|
-
mtime =
|
|
10757
|
+
mtime = fs20.statSync(filePath).mtimeMs;
|
|
10223
10758
|
} catch {
|
|
10224
10759
|
continue;
|
|
10225
10760
|
}
|
|
10226
10761
|
if (mtime < cutoffMs) continue;
|
|
10227
10762
|
yield {
|
|
10228
10763
|
filePath,
|
|
10229
|
-
sessionId:
|
|
10764
|
+
sessionId: path22.basename(file, ".jsonl"),
|
|
10230
10765
|
projectDir: dir
|
|
10231
10766
|
};
|
|
10232
10767
|
}
|
|
@@ -10249,6 +10784,19 @@ function buildSessionTotals(findings, toolCallsBySession) {
|
|
|
10249
10784
|
signals
|
|
10250
10785
|
}));
|
|
10251
10786
|
}
|
|
10787
|
+
function extractHistoricalLoops(sessionCalls, ctx) {
|
|
10788
|
+
return extractSessionLevelFindings(sessionCalls, {
|
|
10789
|
+
sessionId: ctx.sessionId,
|
|
10790
|
+
project: ctx.project,
|
|
10791
|
+
agent: ctx.agent,
|
|
10792
|
+
loopDetection: {
|
|
10793
|
+
enabled: true,
|
|
10794
|
+
threshold: ctx.threshold,
|
|
10795
|
+
windowSeconds: 0
|
|
10796
|
+
// whole session — never the live blocker's window
|
|
10797
|
+
}
|
|
10798
|
+
});
|
|
10799
|
+
}
|
|
10252
10800
|
async function runUploadHistory(opts) {
|
|
10253
10801
|
const creds = getCredentials();
|
|
10254
10802
|
if (!creds?.apiKey) {
|
|
@@ -10279,7 +10827,7 @@ async function runUploadHistory(opts) {
|
|
|
10279
10827
|
filesScanned++;
|
|
10280
10828
|
let content;
|
|
10281
10829
|
try {
|
|
10282
|
-
content =
|
|
10830
|
+
content = fs20.readFileSync(filePath, "utf8");
|
|
10283
10831
|
} catch {
|
|
10284
10832
|
continue;
|
|
10285
10833
|
}
|
|
@@ -10318,15 +10866,11 @@ async function runUploadHistory(opts) {
|
|
|
10318
10866
|
lineIndex++;
|
|
10319
10867
|
}
|
|
10320
10868
|
if (loopCfg.enabled && sessionCalls.length > 0) {
|
|
10321
|
-
const loops =
|
|
10869
|
+
const loops = extractHistoricalLoops(sessionCalls, {
|
|
10322
10870
|
sessionId,
|
|
10323
10871
|
project: decodeProjectDirName(projectDir),
|
|
10324
10872
|
agent: "claude",
|
|
10325
|
-
|
|
10326
|
-
enabled: loopCfg.enabled,
|
|
10327
|
-
threshold: loopCfg.threshold,
|
|
10328
|
-
windowSeconds: loopCfg.windowSeconds
|
|
10329
|
-
}
|
|
10873
|
+
threshold: loopCfg.threshold
|
|
10330
10874
|
});
|
|
10331
10875
|
for (const cf of loops) {
|
|
10332
10876
|
const sf = toScanFinding(cf);
|
|
@@ -10365,10 +10909,10 @@ async function runUploadHistory(opts) {
|
|
|
10365
10909
|
const costUrl = creds.apiUrl.endsWith("/policies/sync") ? creds.apiUrl.replace(/\/policies\/sync$/, "/cost-sync") : `${creds.apiUrl.replace(/\/$/, "")}/cost-sync`;
|
|
10366
10910
|
let username = "unknown";
|
|
10367
10911
|
try {
|
|
10368
|
-
username =
|
|
10912
|
+
username = os19.userInfo().username;
|
|
10369
10913
|
} catch {
|
|
10370
10914
|
}
|
|
10371
|
-
const machineId = `${
|
|
10915
|
+
const machineId = `${os19.hostname()}:${username}`;
|
|
10372
10916
|
await postJson(costUrl, creds.apiKey, {
|
|
10373
10917
|
machineId,
|
|
10374
10918
|
entries: dailyEntries
|
|
@@ -10442,9 +10986,9 @@ var init_scan_upload_history = __esm({
|
|
|
10442
10986
|
|
|
10443
10987
|
// src/cli/commands/scan.ts
|
|
10444
10988
|
import chalk5 from "chalk";
|
|
10445
|
-
import
|
|
10446
|
-
import
|
|
10447
|
-
import
|
|
10989
|
+
import fs21 from "fs";
|
|
10990
|
+
import path23 from "path";
|
|
10991
|
+
import os20 from "os";
|
|
10448
10992
|
import stringWidth2 from "string-width";
|
|
10449
10993
|
function claudeModelPrice(model) {
|
|
10450
10994
|
const base = model.replace(/@.*$/, "").replace(/-\d{8}$/, "");
|
|
@@ -10671,14 +11215,14 @@ function buildRuleSources() {
|
|
|
10671
11215
|
}
|
|
10672
11216
|
function countScanFiles() {
|
|
10673
11217
|
let total = 0;
|
|
10674
|
-
const claudeDir =
|
|
10675
|
-
if (
|
|
11218
|
+
const claudeDir = path23.join(os20.homedir(), ".claude", "projects");
|
|
11219
|
+
if (fs21.existsSync(claudeDir)) {
|
|
10676
11220
|
try {
|
|
10677
|
-
for (const proj of
|
|
10678
|
-
const p =
|
|
11221
|
+
for (const proj of fs21.readdirSync(claudeDir)) {
|
|
11222
|
+
const p = path23.join(claudeDir, proj);
|
|
10679
11223
|
try {
|
|
10680
|
-
if (!
|
|
10681
|
-
total +=
|
|
11224
|
+
if (!fs21.statSync(p).isDirectory()) continue;
|
|
11225
|
+
total += fs21.readdirSync(p).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).length;
|
|
10682
11226
|
} catch {
|
|
10683
11227
|
continue;
|
|
10684
11228
|
}
|
|
@@ -10686,17 +11230,17 @@ function countScanFiles() {
|
|
|
10686
11230
|
} catch {
|
|
10687
11231
|
}
|
|
10688
11232
|
}
|
|
10689
|
-
const geminiDir =
|
|
10690
|
-
if (
|
|
11233
|
+
const geminiDir = path23.join(os20.homedir(), ".gemini", "tmp");
|
|
11234
|
+
if (fs21.existsSync(geminiDir)) {
|
|
10691
11235
|
try {
|
|
10692
|
-
for (const slug of
|
|
10693
|
-
const p =
|
|
11236
|
+
for (const slug of fs21.readdirSync(geminiDir)) {
|
|
11237
|
+
const p = path23.join(geminiDir, slug);
|
|
10694
11238
|
try {
|
|
10695
|
-
if (!
|
|
10696
|
-
const chatsDir =
|
|
10697
|
-
if (
|
|
11239
|
+
if (!fs21.statSync(p).isDirectory()) continue;
|
|
11240
|
+
const chatsDir = path23.join(p, "chats");
|
|
11241
|
+
if (fs21.existsSync(chatsDir)) {
|
|
10698
11242
|
try {
|
|
10699
|
-
total +=
|
|
11243
|
+
total += fs21.readdirSync(chatsDir).filter((f) => f.endsWith(".json")).length;
|
|
10700
11244
|
} catch {
|
|
10701
11245
|
}
|
|
10702
11246
|
}
|
|
@@ -10708,15 +11252,15 @@ function countScanFiles() {
|
|
|
10708
11252
|
}
|
|
10709
11253
|
}
|
|
10710
11254
|
for (const surface of ["antigravity-cli", "antigravity-ide"]) {
|
|
10711
|
-
const brainDir =
|
|
10712
|
-
if (!
|
|
11255
|
+
const brainDir = path23.join(os20.homedir(), ".gemini", surface, "brain");
|
|
11256
|
+
if (!fs21.existsSync(brainDir)) continue;
|
|
10713
11257
|
try {
|
|
10714
|
-
for (const conv of
|
|
10715
|
-
const convPath =
|
|
11258
|
+
for (const conv of fs21.readdirSync(brainDir)) {
|
|
11259
|
+
const convPath = path23.join(brainDir, conv);
|
|
10716
11260
|
try {
|
|
10717
|
-
if (!
|
|
10718
|
-
const logsDir =
|
|
10719
|
-
if (
|
|
11261
|
+
if (!fs21.statSync(convPath).isDirectory()) continue;
|
|
11262
|
+
const logsDir = path23.join(convPath, ".system_generated", "logs");
|
|
11263
|
+
if (fs21.existsSync(path23.join(logsDir, "transcript_full.jsonl")) || fs21.existsSync(path23.join(logsDir, "transcript.jsonl"))) {
|
|
10720
11264
|
total += 1;
|
|
10721
11265
|
}
|
|
10722
11266
|
} catch {
|
|
@@ -10726,31 +11270,31 @@ function countScanFiles() {
|
|
|
10726
11270
|
} catch {
|
|
10727
11271
|
}
|
|
10728
11272
|
}
|
|
10729
|
-
const copilotDir =
|
|
10730
|
-
if (
|
|
11273
|
+
const copilotDir = path23.join(os20.homedir(), ".copilot", "session-state");
|
|
11274
|
+
if (fs21.existsSync(copilotDir)) {
|
|
10731
11275
|
try {
|
|
10732
|
-
for (const sid of
|
|
10733
|
-
if (
|
|
11276
|
+
for (const sid of fs21.readdirSync(copilotDir)) {
|
|
11277
|
+
if (fs21.existsSync(path23.join(copilotDir, sid, "events.jsonl"))) total += 1;
|
|
10734
11278
|
}
|
|
10735
11279
|
} catch {
|
|
10736
11280
|
}
|
|
10737
11281
|
}
|
|
10738
|
-
const codexDir =
|
|
10739
|
-
if (
|
|
11282
|
+
const codexDir = path23.join(os20.homedir(), ".codex", "sessions");
|
|
11283
|
+
if (fs21.existsSync(codexDir)) {
|
|
10740
11284
|
try {
|
|
10741
|
-
for (const year of
|
|
10742
|
-
const yp =
|
|
11285
|
+
for (const year of fs21.readdirSync(codexDir)) {
|
|
11286
|
+
const yp = path23.join(codexDir, year);
|
|
10743
11287
|
try {
|
|
10744
|
-
if (!
|
|
10745
|
-
for (const month of
|
|
10746
|
-
const mp =
|
|
11288
|
+
if (!fs21.statSync(yp).isDirectory()) continue;
|
|
11289
|
+
for (const month of fs21.readdirSync(yp)) {
|
|
11290
|
+
const mp = path23.join(yp, month);
|
|
10747
11291
|
try {
|
|
10748
|
-
if (!
|
|
10749
|
-
for (const day of
|
|
10750
|
-
const dp =
|
|
11292
|
+
if (!fs21.statSync(mp).isDirectory()) continue;
|
|
11293
|
+
for (const day of fs21.readdirSync(mp)) {
|
|
11294
|
+
const dp = path23.join(mp, day);
|
|
10751
11295
|
try {
|
|
10752
|
-
if (!
|
|
10753
|
-
total +=
|
|
11296
|
+
if (!fs21.statSync(dp).isDirectory()) continue;
|
|
11297
|
+
total += fs21.readdirSync(dp).filter((f) => f.endsWith(".jsonl")).length;
|
|
10754
11298
|
} catch {
|
|
10755
11299
|
continue;
|
|
10756
11300
|
}
|
|
@@ -10786,7 +11330,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
10786
11330
|
const sessionId = file.replace(/\.jsonl$/, "");
|
|
10787
11331
|
let raw;
|
|
10788
11332
|
try {
|
|
10789
|
-
raw =
|
|
11333
|
+
raw = fs21.readFileSync(path23.join(projPath, file), "utf-8");
|
|
10790
11334
|
} catch {
|
|
10791
11335
|
return;
|
|
10792
11336
|
}
|
|
@@ -10838,7 +11382,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
10838
11382
|
if (block.type !== "tool_result") continue;
|
|
10839
11383
|
const filePath = block.tool_use_id ? toolUseFilePaths.get(block.tool_use_id) : void 0;
|
|
10840
11384
|
if (filePath) {
|
|
10841
|
-
const ext =
|
|
11385
|
+
const ext = path23.extname(filePath).toLowerCase();
|
|
10842
11386
|
if (CODE_EXTENSIONS.has(ext)) continue;
|
|
10843
11387
|
}
|
|
10844
11388
|
const resultText = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map((c) => c.text ?? "").join("\n") : null;
|
|
@@ -10895,7 +11439,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
10895
11439
|
const rawCmd = String(input.command ?? "").trimStart();
|
|
10896
11440
|
if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd)) continue;
|
|
10897
11441
|
const inputFilePath = typeof input.file_path === "string" ? input.file_path : "";
|
|
10898
|
-
const inputFileExt = inputFilePath ?
|
|
11442
|
+
const inputFileExt = inputFilePath ? path23.extname(inputFilePath).toLowerCase() : "";
|
|
10899
11443
|
if (CODE_EXTENSIONS.has(inputFileExt)) continue;
|
|
10900
11444
|
const dlpMatch = scanArgs(input);
|
|
10901
11445
|
if (dlpMatch) {
|
|
@@ -10992,19 +11536,19 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
10992
11536
|
}
|
|
10993
11537
|
}
|
|
10994
11538
|
function processClaudeProject(proj, projectsDir, ruleSources, startDate, result, dedup, onProgress, onLine) {
|
|
10995
|
-
const projPath =
|
|
11539
|
+
const projPath = path23.join(projectsDir, proj);
|
|
10996
11540
|
try {
|
|
10997
|
-
if (!
|
|
11541
|
+
if (!fs21.statSync(projPath).isDirectory()) return;
|
|
10998
11542
|
} catch {
|
|
10999
11543
|
return;
|
|
11000
11544
|
}
|
|
11001
|
-
const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(
|
|
11545
|
+
const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(os20.homedir(), "~")).slice(
|
|
11002
11546
|
0,
|
|
11003
11547
|
40
|
|
11004
11548
|
);
|
|
11005
11549
|
let files;
|
|
11006
11550
|
try {
|
|
11007
|
-
files =
|
|
11551
|
+
files = fs21.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
11008
11552
|
} catch {
|
|
11009
11553
|
return;
|
|
11010
11554
|
}
|
|
@@ -11038,12 +11582,12 @@ function emptyClaudeScan() {
|
|
|
11038
11582
|
};
|
|
11039
11583
|
}
|
|
11040
11584
|
function scanClaudeHistory(startDate, onProgress, onLine) {
|
|
11041
|
-
const projectsDir =
|
|
11585
|
+
const projectsDir = path23.join(os20.homedir(), ".claude", "projects");
|
|
11042
11586
|
const result = emptyClaudeScan();
|
|
11043
|
-
if (!
|
|
11587
|
+
if (!fs21.existsSync(projectsDir)) return result;
|
|
11044
11588
|
let projDirs;
|
|
11045
11589
|
try {
|
|
11046
|
-
projDirs =
|
|
11590
|
+
projDirs = fs21.readdirSync(projectsDir);
|
|
11047
11591
|
} catch {
|
|
11048
11592
|
return result;
|
|
11049
11593
|
}
|
|
@@ -11064,7 +11608,7 @@ function scanClaudeHistory(startDate, onProgress, onLine) {
|
|
|
11064
11608
|
return result;
|
|
11065
11609
|
}
|
|
11066
11610
|
function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
11067
|
-
const tmpDir =
|
|
11611
|
+
const tmpDir = path23.join(os20.homedir(), ".gemini", "tmp");
|
|
11068
11612
|
const result = {
|
|
11069
11613
|
filesScanned: 0,
|
|
11070
11614
|
sessions: 0,
|
|
@@ -11079,33 +11623,33 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
11079
11623
|
sessionsWithEarlySecrets: 0
|
|
11080
11624
|
};
|
|
11081
11625
|
const dedup = emptyScanDedup();
|
|
11082
|
-
if (!
|
|
11626
|
+
if (!fs21.existsSync(tmpDir)) return result;
|
|
11083
11627
|
let slugDirs;
|
|
11084
11628
|
try {
|
|
11085
|
-
slugDirs =
|
|
11629
|
+
slugDirs = fs21.readdirSync(tmpDir);
|
|
11086
11630
|
} catch {
|
|
11087
11631
|
return result;
|
|
11088
11632
|
}
|
|
11089
11633
|
const ruleSources = buildRuleSources();
|
|
11090
11634
|
for (const slug of slugDirs) {
|
|
11091
|
-
const slugPath =
|
|
11635
|
+
const slugPath = path23.join(tmpDir, slug);
|
|
11092
11636
|
try {
|
|
11093
|
-
if (!
|
|
11637
|
+
if (!fs21.statSync(slugPath).isDirectory()) continue;
|
|
11094
11638
|
} catch {
|
|
11095
11639
|
continue;
|
|
11096
11640
|
}
|
|
11097
11641
|
let projLabel = stripTerminalEscapes(slug).slice(0, 40);
|
|
11098
11642
|
try {
|
|
11099
11643
|
projLabel = stripTerminalEscapes(
|
|
11100
|
-
|
|
11101
|
-
).replace(
|
|
11644
|
+
fs21.readFileSync(path23.join(slugPath, ".project_root"), "utf-8").trim()
|
|
11645
|
+
).replace(os20.homedir(), "~").slice(0, 40);
|
|
11102
11646
|
} catch {
|
|
11103
11647
|
}
|
|
11104
|
-
const chatsDir =
|
|
11105
|
-
if (!
|
|
11648
|
+
const chatsDir = path23.join(slugPath, "chats");
|
|
11649
|
+
if (!fs21.existsSync(chatsDir)) continue;
|
|
11106
11650
|
let chatFiles;
|
|
11107
11651
|
try {
|
|
11108
|
-
chatFiles =
|
|
11652
|
+
chatFiles = fs21.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
|
|
11109
11653
|
} catch {
|
|
11110
11654
|
continue;
|
|
11111
11655
|
}
|
|
@@ -11115,7 +11659,7 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
11115
11659
|
const sessionId = chatFile.replace(/\.json$/, "");
|
|
11116
11660
|
let raw;
|
|
11117
11661
|
try {
|
|
11118
|
-
raw =
|
|
11662
|
+
raw = fs21.readFileSync(path23.join(chatsDir, chatFile), "utf-8");
|
|
11119
11663
|
} catch {
|
|
11120
11664
|
continue;
|
|
11121
11665
|
}
|
|
@@ -11277,13 +11821,13 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
11277
11821
|
return result;
|
|
11278
11822
|
}
|
|
11279
11823
|
function antigravityBrainDirs() {
|
|
11280
|
-
return ["antigravity-cli", "antigravity-ide"].map((surface) =>
|
|
11824
|
+
return ["antigravity-cli", "antigravity-ide"].map((surface) => path23.join(os20.homedir(), ".gemini", surface, "brain")).filter((p) => fs21.existsSync(p));
|
|
11281
11825
|
}
|
|
11282
11826
|
function antigravityTranscriptPath(convPath) {
|
|
11283
|
-
const logsDir =
|
|
11827
|
+
const logsDir = path23.join(convPath, ".system_generated", "logs");
|
|
11284
11828
|
for (const name of ["transcript_full.jsonl", "transcript.jsonl"]) {
|
|
11285
|
-
const p =
|
|
11286
|
-
if (
|
|
11829
|
+
const p = path23.join(logsDir, name);
|
|
11830
|
+
if (fs21.existsSync(p)) return p;
|
|
11287
11831
|
}
|
|
11288
11832
|
return null;
|
|
11289
11833
|
}
|
|
@@ -11309,14 +11853,14 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
11309
11853
|
for (const brainDir of brainDirs) {
|
|
11310
11854
|
let convDirs;
|
|
11311
11855
|
try {
|
|
11312
|
-
convDirs =
|
|
11856
|
+
convDirs = fs21.readdirSync(brainDir);
|
|
11313
11857
|
} catch {
|
|
11314
11858
|
continue;
|
|
11315
11859
|
}
|
|
11316
11860
|
for (const conv of convDirs) {
|
|
11317
|
-
const convPath =
|
|
11861
|
+
const convPath = path23.join(brainDir, conv);
|
|
11318
11862
|
try {
|
|
11319
|
-
if (!
|
|
11863
|
+
if (!fs21.statSync(convPath).isDirectory()) continue;
|
|
11320
11864
|
} catch {
|
|
11321
11865
|
continue;
|
|
11322
11866
|
}
|
|
@@ -11326,7 +11870,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
11326
11870
|
onProgress?.(result.filesScanned);
|
|
11327
11871
|
let raw;
|
|
11328
11872
|
try {
|
|
11329
|
-
raw =
|
|
11873
|
+
raw = fs21.readFileSync(transcriptFile, "utf-8");
|
|
11330
11874
|
} catch {
|
|
11331
11875
|
continue;
|
|
11332
11876
|
}
|
|
@@ -11383,7 +11927,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
11383
11927
|
result.bashCalls++;
|
|
11384
11928
|
const cwd = String(input.cwd ?? "");
|
|
11385
11929
|
if (cwd && projLabel === conv.slice(0, 8)) {
|
|
11386
|
-
projLabel = stripTerminalEscapes(cwd).replace(
|
|
11930
|
+
projLabel = stripTerminalEscapes(cwd).replace(os20.homedir(), "~").slice(0, 40);
|
|
11387
11931
|
}
|
|
11388
11932
|
}
|
|
11389
11933
|
const rawCmd = String(input.command ?? "").trimStart();
|
|
@@ -11483,7 +12027,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
11483
12027
|
return result;
|
|
11484
12028
|
}
|
|
11485
12029
|
function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
11486
|
-
const sessionDir =
|
|
12030
|
+
const sessionDir = path23.join(os20.homedir(), ".copilot", "session-state");
|
|
11487
12031
|
const result = {
|
|
11488
12032
|
filesScanned: 0,
|
|
11489
12033
|
sessions: 0,
|
|
@@ -11499,22 +12043,22 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
11499
12043
|
sessionsWithEarlySecrets: 0
|
|
11500
12044
|
};
|
|
11501
12045
|
const dedup = emptyScanDedup();
|
|
11502
|
-
if (!
|
|
12046
|
+
if (!fs21.existsSync(sessionDir)) return result;
|
|
11503
12047
|
let sessionIds;
|
|
11504
12048
|
try {
|
|
11505
|
-
sessionIds =
|
|
12049
|
+
sessionIds = fs21.readdirSync(sessionDir);
|
|
11506
12050
|
} catch {
|
|
11507
12051
|
return result;
|
|
11508
12052
|
}
|
|
11509
12053
|
const ruleSources = buildRuleSources();
|
|
11510
12054
|
for (const sessionId of sessionIds) {
|
|
11511
|
-
const eventsPath =
|
|
11512
|
-
if (!
|
|
12055
|
+
const eventsPath = path23.join(sessionDir, sessionId, "events.jsonl");
|
|
12056
|
+
if (!fs21.existsSync(eventsPath)) continue;
|
|
11513
12057
|
result.filesScanned++;
|
|
11514
12058
|
onProgress?.(result.filesScanned);
|
|
11515
12059
|
let raw;
|
|
11516
12060
|
try {
|
|
11517
|
-
raw =
|
|
12061
|
+
raw = fs21.readFileSync(eventsPath, "utf-8");
|
|
11518
12062
|
} catch {
|
|
11519
12063
|
continue;
|
|
11520
12064
|
}
|
|
@@ -11534,7 +12078,7 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
11534
12078
|
if (ev.type === "session.start") {
|
|
11535
12079
|
const cwd = ev.data?.context?.cwd;
|
|
11536
12080
|
if (typeof cwd === "string" && cwd) {
|
|
11537
|
-
projLabel = stripTerminalEscapes(cwd).replace(
|
|
12081
|
+
projLabel = stripTerminalEscapes(cwd).replace(os20.homedir(), "~").slice(0, 40);
|
|
11538
12082
|
}
|
|
11539
12083
|
continue;
|
|
11540
12084
|
}
|
|
@@ -11666,7 +12210,7 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
11666
12210
|
return result;
|
|
11667
12211
|
}
|
|
11668
12212
|
function scanCodexHistory(startDate, onProgress, onLine) {
|
|
11669
|
-
const sessionsBase =
|
|
12213
|
+
const sessionsBase = path23.join(os20.homedir(), ".codex", "sessions");
|
|
11670
12214
|
const result = {
|
|
11671
12215
|
filesScanned: 0,
|
|
11672
12216
|
sessions: 0,
|
|
@@ -11681,32 +12225,32 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
11681
12225
|
sessionsWithEarlySecrets: 0
|
|
11682
12226
|
};
|
|
11683
12227
|
const dedup = emptyScanDedup();
|
|
11684
|
-
if (!
|
|
12228
|
+
if (!fs21.existsSync(sessionsBase)) return result;
|
|
11685
12229
|
const jsonlFiles = [];
|
|
11686
12230
|
try {
|
|
11687
|
-
for (const year of
|
|
11688
|
-
const yearPath =
|
|
12231
|
+
for (const year of fs21.readdirSync(sessionsBase)) {
|
|
12232
|
+
const yearPath = path23.join(sessionsBase, year);
|
|
11689
12233
|
try {
|
|
11690
|
-
if (!
|
|
12234
|
+
if (!fs21.statSync(yearPath).isDirectory()) continue;
|
|
11691
12235
|
} catch {
|
|
11692
12236
|
continue;
|
|
11693
12237
|
}
|
|
11694
|
-
for (const month of
|
|
11695
|
-
const monthPath =
|
|
12238
|
+
for (const month of fs21.readdirSync(yearPath)) {
|
|
12239
|
+
const monthPath = path23.join(yearPath, month);
|
|
11696
12240
|
try {
|
|
11697
|
-
if (!
|
|
12241
|
+
if (!fs21.statSync(monthPath).isDirectory()) continue;
|
|
11698
12242
|
} catch {
|
|
11699
12243
|
continue;
|
|
11700
12244
|
}
|
|
11701
|
-
for (const day of
|
|
11702
|
-
const dayPath =
|
|
12245
|
+
for (const day of fs21.readdirSync(monthPath)) {
|
|
12246
|
+
const dayPath = path23.join(monthPath, day);
|
|
11703
12247
|
try {
|
|
11704
|
-
if (!
|
|
12248
|
+
if (!fs21.statSync(dayPath).isDirectory()) continue;
|
|
11705
12249
|
} catch {
|
|
11706
12250
|
continue;
|
|
11707
12251
|
}
|
|
11708
|
-
for (const file of
|
|
11709
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(
|
|
12252
|
+
for (const file of fs21.readdirSync(dayPath)) {
|
|
12253
|
+
if (file.endsWith(".jsonl")) jsonlFiles.push(path23.join(dayPath, file));
|
|
11710
12254
|
}
|
|
11711
12255
|
}
|
|
11712
12256
|
}
|
|
@@ -11720,7 +12264,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
11720
12264
|
onProgress?.(result.filesScanned);
|
|
11721
12265
|
let lines;
|
|
11722
12266
|
try {
|
|
11723
|
-
lines =
|
|
12267
|
+
lines = fs21.readFileSync(filePath, "utf-8").split("\n");
|
|
11724
12268
|
} catch {
|
|
11725
12269
|
continue;
|
|
11726
12270
|
}
|
|
@@ -11746,7 +12290,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
11746
12290
|
sessionId = String(payload["id"] ?? filePath);
|
|
11747
12291
|
startTime = String(payload["timestamp"] ?? "");
|
|
11748
12292
|
const cwd = String(payload["cwd"] ?? "");
|
|
11749
|
-
projLabel = stripTerminalEscapes(cwd.replace(
|
|
12293
|
+
projLabel = stripTerminalEscapes(cwd.replace(os20.homedir(), "~")).slice(0, 40);
|
|
11750
12294
|
continue;
|
|
11751
12295
|
}
|
|
11752
12296
|
if (entry.type === "event_msg" && payload["type"] === "token_count") {
|
|
@@ -11899,17 +12443,17 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
11899
12443
|
return result;
|
|
11900
12444
|
}
|
|
11901
12445
|
function scanShellConfig() {
|
|
11902
|
-
const home =
|
|
12446
|
+
const home = os20.homedir();
|
|
11903
12447
|
const configFiles = [".zshrc", ".bashrc", ".bash_profile", ".profile"].map(
|
|
11904
|
-
(f) =>
|
|
12448
|
+
(f) => path23.join(home, f)
|
|
11905
12449
|
);
|
|
11906
12450
|
const findings = [];
|
|
11907
12451
|
const seen = /* @__PURE__ */ new Set();
|
|
11908
12452
|
for (const filePath of configFiles) {
|
|
11909
|
-
if (!
|
|
12453
|
+
if (!fs21.existsSync(filePath)) continue;
|
|
11910
12454
|
let lines;
|
|
11911
12455
|
try {
|
|
11912
|
-
lines =
|
|
12456
|
+
lines = fs21.readFileSync(filePath, "utf-8").split("\n");
|
|
11913
12457
|
} catch {
|
|
11914
12458
|
continue;
|
|
11915
12459
|
}
|
|
@@ -12715,7 +13259,7 @@ function registerScanCommand(program2) {
|
|
|
12715
13259
|
if (!drillDown) {
|
|
12716
13260
|
const useInk2 = !options.classic;
|
|
12717
13261
|
if (useInk2) {
|
|
12718
|
-
const scanInkPath =
|
|
13262
|
+
const scanInkPath = path23.join(__dirname, "scan-ink.mjs");
|
|
12719
13263
|
const dynamicImport = new Function("id", "return import(id)");
|
|
12720
13264
|
const mod = await dynamicImport(`file://${scanInkPath}`);
|
|
12721
13265
|
const rangeLabel2 = options.all ? "all time" : `last ${options.days ?? 90} days`;
|
|
@@ -13136,8 +13680,8 @@ var init_suggestion_tracker = __esm({
|
|
|
13136
13680
|
});
|
|
13137
13681
|
|
|
13138
13682
|
// src/daemon/taint-store.ts
|
|
13139
|
-
import
|
|
13140
|
-
import
|
|
13683
|
+
import fs22 from "fs";
|
|
13684
|
+
import path24 from "path";
|
|
13141
13685
|
var DEFAULT_TTL_MS, TaintStore;
|
|
13142
13686
|
var init_taint_store = __esm({
|
|
13143
13687
|
"src/daemon/taint-store.ts"() {
|
|
@@ -13206,9 +13750,9 @@ var init_taint_store = __esm({
|
|
|
13206
13750
|
/** Resolve to absolute path, falling back to path.resolve if file doesn't exist yet. */
|
|
13207
13751
|
_resolve(filePath) {
|
|
13208
13752
|
try {
|
|
13209
|
-
return
|
|
13753
|
+
return fs22.realpathSync.native(path24.resolve(filePath));
|
|
13210
13754
|
} catch {
|
|
13211
|
-
return
|
|
13755
|
+
return path24.resolve(filePath);
|
|
13212
13756
|
}
|
|
13213
13757
|
}
|
|
13214
13758
|
};
|
|
@@ -13325,14 +13869,14 @@ var init_session_history = __esm({
|
|
|
13325
13869
|
|
|
13326
13870
|
// src/daemon/state.ts
|
|
13327
13871
|
import net2 from "net";
|
|
13328
|
-
import
|
|
13329
|
-
import
|
|
13330
|
-
import
|
|
13872
|
+
import fs23 from "fs";
|
|
13873
|
+
import path25 from "path";
|
|
13874
|
+
import os21 from "os";
|
|
13331
13875
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
13332
13876
|
function loadInsightCounts() {
|
|
13333
13877
|
try {
|
|
13334
|
-
if (!
|
|
13335
|
-
const data = JSON.parse(
|
|
13878
|
+
if (!fs23.existsSync(INSIGHT_COUNTS_FILE)) return;
|
|
13879
|
+
const data = JSON.parse(fs23.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
|
|
13336
13880
|
for (const [tool, count] of Object.entries(data)) {
|
|
13337
13881
|
if (typeof count === "number" && count > 0) insightCounts.set(tool, count);
|
|
13338
13882
|
}
|
|
@@ -13371,23 +13915,23 @@ function markRejectionHandlerRegistered() {
|
|
|
13371
13915
|
daemonRejectionHandlerRegistered = true;
|
|
13372
13916
|
}
|
|
13373
13917
|
function atomicWriteSync2(filePath, data, options) {
|
|
13374
|
-
const dir =
|
|
13375
|
-
if (!
|
|
13918
|
+
const dir = path25.dirname(filePath);
|
|
13919
|
+
if (!fs23.existsSync(dir)) fs23.mkdirSync(dir, { recursive: true });
|
|
13376
13920
|
const tmpPath = `${filePath}.${randomUUID3()}.tmp`;
|
|
13377
13921
|
try {
|
|
13378
|
-
|
|
13922
|
+
fs23.writeFileSync(tmpPath, data, options);
|
|
13379
13923
|
} catch (err2) {
|
|
13380
13924
|
try {
|
|
13381
|
-
|
|
13925
|
+
fs23.unlinkSync(tmpPath);
|
|
13382
13926
|
} catch {
|
|
13383
13927
|
}
|
|
13384
13928
|
throw err2;
|
|
13385
13929
|
}
|
|
13386
13930
|
try {
|
|
13387
|
-
|
|
13931
|
+
fs23.renameSync(tmpPath, filePath);
|
|
13388
13932
|
} catch (err2) {
|
|
13389
13933
|
try {
|
|
13390
|
-
|
|
13934
|
+
fs23.unlinkSync(tmpPath);
|
|
13391
13935
|
} catch {
|
|
13392
13936
|
}
|
|
13393
13937
|
throw err2;
|
|
@@ -13411,16 +13955,16 @@ function appendAuditLog(data) {
|
|
|
13411
13955
|
decision: data.decision,
|
|
13412
13956
|
source: "daemon"
|
|
13413
13957
|
};
|
|
13414
|
-
const dir =
|
|
13415
|
-
if (!
|
|
13416
|
-
|
|
13958
|
+
const dir = path25.dirname(AUDIT_LOG_FILE);
|
|
13959
|
+
if (!fs23.existsSync(dir)) fs23.mkdirSync(dir, { recursive: true });
|
|
13960
|
+
fs23.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
|
|
13417
13961
|
} catch {
|
|
13418
13962
|
}
|
|
13419
13963
|
}
|
|
13420
13964
|
function getAuditHistory(limit = 20) {
|
|
13421
13965
|
try {
|
|
13422
|
-
if (!
|
|
13423
|
-
const lines =
|
|
13966
|
+
if (!fs23.existsSync(AUDIT_LOG_FILE)) return [];
|
|
13967
|
+
const lines = fs23.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
|
|
13424
13968
|
if (lines.length === 1 && lines[0] === "") return [];
|
|
13425
13969
|
return lines.slice(-limit).map((l) => JSON.parse(l)).reverse();
|
|
13426
13970
|
} catch {
|
|
@@ -13429,7 +13973,7 @@ function getAuditHistory(limit = 20) {
|
|
|
13429
13973
|
}
|
|
13430
13974
|
function getOrgName() {
|
|
13431
13975
|
try {
|
|
13432
|
-
if (
|
|
13976
|
+
if (fs23.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
|
|
13433
13977
|
} catch {
|
|
13434
13978
|
}
|
|
13435
13979
|
return null;
|
|
@@ -13437,8 +13981,8 @@ function getOrgName() {
|
|
|
13437
13981
|
function writeGlobalSetting(key, value) {
|
|
13438
13982
|
let config = {};
|
|
13439
13983
|
try {
|
|
13440
|
-
if (
|
|
13441
|
-
config = JSON.parse(
|
|
13984
|
+
if (fs23.existsSync(GLOBAL_CONFIG_FILE)) {
|
|
13985
|
+
config = JSON.parse(fs23.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
|
|
13442
13986
|
}
|
|
13443
13987
|
} catch {
|
|
13444
13988
|
}
|
|
@@ -13450,8 +13994,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
|
|
|
13450
13994
|
try {
|
|
13451
13995
|
let trust = { entries: [] };
|
|
13452
13996
|
try {
|
|
13453
|
-
if (
|
|
13454
|
-
trust = JSON.parse(
|
|
13997
|
+
if (fs23.existsSync(TRUST_FILE2))
|
|
13998
|
+
trust = JSON.parse(fs23.readFileSync(TRUST_FILE2, "utf-8"));
|
|
13455
13999
|
} catch {
|
|
13456
14000
|
}
|
|
13457
14001
|
trust.entries = trust.entries.filter(
|
|
@@ -13468,8 +14012,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
|
|
|
13468
14012
|
}
|
|
13469
14013
|
function readPersistentDecisions() {
|
|
13470
14014
|
try {
|
|
13471
|
-
if (
|
|
13472
|
-
return JSON.parse(
|
|
14015
|
+
if (fs23.existsSync(DECISIONS_FILE)) {
|
|
14016
|
+
return JSON.parse(fs23.readFileSync(DECISIONS_FILE, "utf-8"));
|
|
13473
14017
|
}
|
|
13474
14018
|
} catch {
|
|
13475
14019
|
}
|
|
@@ -13497,7 +14041,7 @@ function estimateToolCost(tool, args) {
|
|
|
13497
14041
|
const filePath = a.file_path ?? a.path;
|
|
13498
14042
|
if (filePath) {
|
|
13499
14043
|
try {
|
|
13500
|
-
const bytes =
|
|
14044
|
+
const bytes = fs23.statSync(filePath).size;
|
|
13501
14045
|
return bytes / BYTES_PER_TOKEN / 1e6 * INPUT_PRICE_PER_1M;
|
|
13502
14046
|
} catch {
|
|
13503
14047
|
}
|
|
@@ -13568,7 +14112,7 @@ function abandonPending() {
|
|
|
13568
14112
|
});
|
|
13569
14113
|
if (autoStarted) {
|
|
13570
14114
|
try {
|
|
13571
|
-
|
|
14115
|
+
fs23.unlinkSync(DAEMON_PID_FILE);
|
|
13572
14116
|
} catch {
|
|
13573
14117
|
}
|
|
13574
14118
|
setTimeout(() => {
|
|
@@ -13579,8 +14123,8 @@ function abandonPending() {
|
|
|
13579
14123
|
}
|
|
13580
14124
|
function logActivitySocket(msg) {
|
|
13581
14125
|
try {
|
|
13582
|
-
|
|
13583
|
-
|
|
14126
|
+
fs23.appendFileSync(
|
|
14127
|
+
path25.join(homeDir, ".node9", "hook-debug.log"),
|
|
13584
14128
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] [activity-socket] ${msg}
|
|
13585
14129
|
`
|
|
13586
14130
|
);
|
|
@@ -13602,13 +14146,13 @@ function shouldRebind(now = Date.now()) {
|
|
|
13602
14146
|
function startActivitySocket() {
|
|
13603
14147
|
bindActivitySocket();
|
|
13604
14148
|
activityHealthInterval = setInterval(() => {
|
|
13605
|
-
if (!
|
|
14149
|
+
if (!fs23.existsSync(ACTIVITY_SOCKET_PATH2)) attemptRebind("health-probe");
|
|
13606
14150
|
}, ACTIVITY_HEALTH_PROBE_MS);
|
|
13607
14151
|
activityHealthInterval.unref();
|
|
13608
14152
|
process.on("exit", () => {
|
|
13609
14153
|
if (activityHealthInterval) clearInterval(activityHealthInterval);
|
|
13610
14154
|
try {
|
|
13611
|
-
|
|
14155
|
+
fs23.unlinkSync(ACTIVITY_SOCKET_PATH2);
|
|
13612
14156
|
} catch {
|
|
13613
14157
|
}
|
|
13614
14158
|
});
|
|
@@ -13636,7 +14180,7 @@ function attemptRebind(reason) {
|
|
|
13636
14180
|
}
|
|
13637
14181
|
function bindActivitySocket() {
|
|
13638
14182
|
try {
|
|
13639
|
-
|
|
14183
|
+
fs23.unlinkSync(ACTIVITY_SOCKET_PATH2);
|
|
13640
14184
|
} catch {
|
|
13641
14185
|
}
|
|
13642
14186
|
const ACTIVITY_MAX_BYTES = 1024 * 1024;
|
|
@@ -13749,14 +14293,14 @@ var init_state2 = __esm({
|
|
|
13749
14293
|
init_taint_store();
|
|
13750
14294
|
init_session_counters();
|
|
13751
14295
|
init_session_history();
|
|
13752
|
-
homeDir =
|
|
13753
|
-
DAEMON_PID_FILE =
|
|
13754
|
-
DECISIONS_FILE =
|
|
13755
|
-
AUDIT_LOG_FILE =
|
|
13756
|
-
TRUST_FILE2 =
|
|
13757
|
-
GLOBAL_CONFIG_FILE =
|
|
13758
|
-
CREDENTIALS_FILE =
|
|
13759
|
-
INSIGHT_COUNTS_FILE =
|
|
14296
|
+
homeDir = os21.homedir();
|
|
14297
|
+
DAEMON_PID_FILE = path25.join(homeDir, ".node9", "daemon.pid");
|
|
14298
|
+
DECISIONS_FILE = path25.join(homeDir, ".node9", "decisions.json");
|
|
14299
|
+
AUDIT_LOG_FILE = path25.join(homeDir, ".node9", "audit.log");
|
|
14300
|
+
TRUST_FILE2 = path25.join(homeDir, ".node9", "trust.json");
|
|
14301
|
+
GLOBAL_CONFIG_FILE = path25.join(homeDir, ".node9", "config.json");
|
|
14302
|
+
CREDENTIALS_FILE = path25.join(homeDir, ".node9", "credentials.json");
|
|
14303
|
+
INSIGHT_COUNTS_FILE = path25.join(homeDir, ".node9", "insight-counts.json");
|
|
13760
14304
|
pending = /* @__PURE__ */ new Map();
|
|
13761
14305
|
sseClients = /* @__PURE__ */ new Set();
|
|
13762
14306
|
suggestionTracker = new SuggestionTracker(3);
|
|
@@ -13773,7 +14317,7 @@ var init_state2 = __esm({
|
|
|
13773
14317
|
"2h": 2 * 60 * 6e4
|
|
13774
14318
|
};
|
|
13775
14319
|
autoStarted = process.env.NODE9_AUTO_STARTED === "1";
|
|
13776
|
-
ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" :
|
|
14320
|
+
ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path25.join(os21.tmpdir(), "node9-activity.sock");
|
|
13777
14321
|
ACTIVITY_RING_SIZE = 100;
|
|
13778
14322
|
activityRing = [];
|
|
13779
14323
|
LARGE_RESPONSE_RING_SIZE = 20;
|
|
@@ -13812,10 +14356,10 @@ var init_state2 = __esm({
|
|
|
13812
14356
|
});
|
|
13813
14357
|
|
|
13814
14358
|
// src/daemon/sync.ts
|
|
13815
|
-
import
|
|
14359
|
+
import fs24 from "fs";
|
|
13816
14360
|
import https2 from "https";
|
|
13817
|
-
import
|
|
13818
|
-
import
|
|
14361
|
+
import os22 from "os";
|
|
14362
|
+
import path26 from "path";
|
|
13819
14363
|
function emptySignals3() {
|
|
13820
14364
|
return {
|
|
13821
14365
|
dlpFindings: 0,
|
|
@@ -13855,8 +14399,8 @@ function readCredentials() {
|
|
|
13855
14399
|
};
|
|
13856
14400
|
}
|
|
13857
14401
|
try {
|
|
13858
|
-
const credPath =
|
|
13859
|
-
const creds = JSON.parse(
|
|
14402
|
+
const credPath = path26.join(os22.homedir(), ".node9", "credentials.json");
|
|
14403
|
+
const creds = JSON.parse(fs24.readFileSync(credPath, "utf-8"));
|
|
13860
14404
|
const profileName = process.env.NODE9_PROFILE ?? "default";
|
|
13861
14405
|
const profile = creds[profileName];
|
|
13862
14406
|
if (typeof profile?.apiKey === "string" && profile.apiKey.length > 0) {
|
|
@@ -13882,7 +14426,7 @@ function readCredentials() {
|
|
|
13882
14426
|
}
|
|
13883
14427
|
function readCachedEtag() {
|
|
13884
14428
|
try {
|
|
13885
|
-
const raw = JSON.parse(
|
|
14429
|
+
const raw = JSON.parse(fs24.readFileSync(rulesCacheFile(), "utf-8"));
|
|
13886
14430
|
return typeof raw.etag === "string" ? raw.etag : void 0;
|
|
13887
14431
|
} catch {
|
|
13888
14432
|
return void 0;
|
|
@@ -13943,9 +14487,9 @@ function extractRules(body) {
|
|
|
13943
14487
|
return [];
|
|
13944
14488
|
}
|
|
13945
14489
|
function writeCache2(cache) {
|
|
13946
|
-
const dir =
|
|
13947
|
-
if (!
|
|
13948
|
-
|
|
14490
|
+
const dir = path26.dirname(rulesCacheFile());
|
|
14491
|
+
if (!fs24.existsSync(dir)) fs24.mkdirSync(dir, { recursive: true });
|
|
14492
|
+
fs24.writeFileSync(rulesCacheFile(), JSON.stringify(cache, null, 2) + "\n", "utf-8");
|
|
13949
14493
|
}
|
|
13950
14494
|
async function syncOnce() {
|
|
13951
14495
|
const creds = readCredentials();
|
|
@@ -14102,7 +14646,7 @@ async function runCloudSync() {
|
|
|
14102
14646
|
}
|
|
14103
14647
|
function getCloudSyncStatus() {
|
|
14104
14648
|
try {
|
|
14105
|
-
const raw = JSON.parse(
|
|
14649
|
+
const raw = JSON.parse(fs24.readFileSync(rulesCacheFile(), "utf-8"));
|
|
14106
14650
|
if (!Array.isArray(raw.rules) || typeof raw.fetchedAt !== "string") return { cached: false };
|
|
14107
14651
|
return {
|
|
14108
14652
|
cached: true,
|
|
@@ -14119,7 +14663,7 @@ function getCloudSyncStatus() {
|
|
|
14119
14663
|
}
|
|
14120
14664
|
function getCloudRules() {
|
|
14121
14665
|
try {
|
|
14122
|
-
const raw = JSON.parse(
|
|
14666
|
+
const raw = JSON.parse(fs24.readFileSync(rulesCacheFile(), "utf-8"));
|
|
14123
14667
|
return Array.isArray(raw.rules) ? raw.rules : null;
|
|
14124
14668
|
} catch {
|
|
14125
14669
|
return null;
|
|
@@ -14175,7 +14719,7 @@ var init_sync = __esm({
|
|
|
14175
14719
|
loop: "loops",
|
|
14176
14720
|
"long-output-redacted": "longOutputRedactions"
|
|
14177
14721
|
};
|
|
14178
|
-
rulesCacheFile = () =>
|
|
14722
|
+
rulesCacheFile = () => path26.join(os22.homedir(), ".node9", "rules-cache.json");
|
|
14179
14723
|
DEFAULT_API_URL = "https://api.node9.ai/api/v1/intercept/policies/sync";
|
|
14180
14724
|
DEFAULT_INTERVAL_HOURS = 5;
|
|
14181
14725
|
MIN_INTERVAL_HOURS = 1;
|
|
@@ -14189,6 +14733,7 @@ var init_sync = __esm({
|
|
|
14189
14733
|
var audit_shipper_exports = {};
|
|
14190
14734
|
__export(audit_shipper_exports, {
|
|
14191
14735
|
AUDIT_SHIP_WATERMARK: () => AUDIT_SHIP_WATERMARK,
|
|
14736
|
+
buildBatchEndpoint: () => buildBatchEndpoint,
|
|
14192
14737
|
buildWireRows: () => buildWireRows,
|
|
14193
14738
|
fileSignature: () => fileSignature,
|
|
14194
14739
|
readWatermark: () => readWatermark,
|
|
@@ -14197,26 +14742,26 @@ __export(audit_shipper_exports, {
|
|
|
14197
14742
|
startAuditShipper: () => startAuditShipper,
|
|
14198
14743
|
writeWatermark: () => writeWatermark
|
|
14199
14744
|
});
|
|
14200
|
-
import
|
|
14201
|
-
import
|
|
14202
|
-
import
|
|
14745
|
+
import fs25 from "fs";
|
|
14746
|
+
import path27 from "path";
|
|
14747
|
+
import os23 from "os";
|
|
14203
14748
|
import crypto5 from "crypto";
|
|
14204
14749
|
function fileSignature(filePath) {
|
|
14205
|
-
const fd =
|
|
14750
|
+
const fd = fs25.openSync(filePath, "r");
|
|
14206
14751
|
try {
|
|
14207
14752
|
const buf = Buffer.alloc(512);
|
|
14208
|
-
const read =
|
|
14753
|
+
const read = fs25.readSync(fd, buf, 0, 512, 0);
|
|
14209
14754
|
const slice = buf.subarray(0, read);
|
|
14210
14755
|
const nl = slice.indexOf(10);
|
|
14211
14756
|
const firstLine = nl === -1 ? slice : slice.subarray(0, nl);
|
|
14212
14757
|
return crypto5.createHash("sha256").update(firstLine).digest("hex").slice(0, 16);
|
|
14213
14758
|
} finally {
|
|
14214
|
-
|
|
14759
|
+
fs25.closeSync(fd);
|
|
14215
14760
|
}
|
|
14216
14761
|
}
|
|
14217
14762
|
function readWatermark(watermarkPath) {
|
|
14218
14763
|
try {
|
|
14219
|
-
const raw = JSON.parse(
|
|
14764
|
+
const raw = JSON.parse(fs25.readFileSync(watermarkPath, "utf-8"));
|
|
14220
14765
|
if (typeof raw.fileSig === "string" && typeof raw.offset === "number" && raw.offset >= 0)
|
|
14221
14766
|
return raw;
|
|
14222
14767
|
} catch {
|
|
@@ -14225,8 +14770,8 @@ function readWatermark(watermarkPath) {
|
|
|
14225
14770
|
}
|
|
14226
14771
|
function writeWatermark(watermarkPath, wm) {
|
|
14227
14772
|
const tmp = `${watermarkPath}.tmp`;
|
|
14228
|
-
|
|
14229
|
-
|
|
14773
|
+
fs25.writeFileSync(tmp, JSON.stringify(wm));
|
|
14774
|
+
fs25.renameSync(tmp, watermarkPath);
|
|
14230
14775
|
}
|
|
14231
14776
|
function buildWireRows(chunk) {
|
|
14232
14777
|
const lastNl = chunk.lastIndexOf(10);
|
|
@@ -14270,6 +14815,12 @@ function buildWireRows(chunk) {
|
|
|
14270
14815
|
}
|
|
14271
14816
|
return { rows, consumed: lastNl + 1 };
|
|
14272
14817
|
}
|
|
14818
|
+
function buildBatchEndpoint(rawApiUrl) {
|
|
14819
|
+
const validated = validateApiUrl(rawApiUrl);
|
|
14820
|
+
if (!validated) return null;
|
|
14821
|
+
const base = validated.toString().replace(/\/$/, "").replace(/\/policies\/sync$/, "");
|
|
14822
|
+
return `${base}/audit/batch`;
|
|
14823
|
+
}
|
|
14273
14824
|
async function shipOnce(deps = {}) {
|
|
14274
14825
|
const auditLogPath = deps.auditLogPath ?? LOCAL_AUDIT_LOG;
|
|
14275
14826
|
const watermarkPath = deps.watermarkPath ?? AUDIT_SHIP_WATERMARK;
|
|
@@ -14286,14 +14837,13 @@ async function shipOnce(deps = {}) {
|
|
|
14286
14837
|
if (!cloudEnabled) return { status: "disabled", shipped: 0 };
|
|
14287
14838
|
const creds = deps.creds !== void 0 ? deps.creds : readCredentials();
|
|
14288
14839
|
if (!creds?.apiKey) return { status: "no-creds", shipped: 0 };
|
|
14289
|
-
const
|
|
14290
|
-
if (!
|
|
14291
|
-
|
|
14292
|
-
if (!fs24.existsSync(auditLogPath)) return { status: "idle", shipped: 0 };
|
|
14840
|
+
const endpoint = buildBatchEndpoint(creds.apiUrl);
|
|
14841
|
+
if (!endpoint) return { status: "no-creds", shipped: 0 };
|
|
14842
|
+
if (!fs25.existsSync(auditLogPath)) return { status: "idle", shipped: 0 };
|
|
14293
14843
|
let shipped = 0;
|
|
14294
14844
|
try {
|
|
14295
14845
|
for (let chunkN = 0; chunkN < MAX_CHUNKS_PER_TICK; chunkN++) {
|
|
14296
|
-
const size =
|
|
14846
|
+
const size = fs25.statSync(auditLogPath).size;
|
|
14297
14847
|
if (size === 0) break;
|
|
14298
14848
|
const sig = fileSignature(auditLogPath);
|
|
14299
14849
|
const wm = readWatermark(watermarkPath);
|
|
@@ -14301,12 +14851,12 @@ async function shipOnce(deps = {}) {
|
|
|
14301
14851
|
if (offset >= size) break;
|
|
14302
14852
|
const toRead = Math.min(size - offset, MAX_CHUNK_BYTES);
|
|
14303
14853
|
const buf = Buffer.alloc(toRead);
|
|
14304
|
-
const fd =
|
|
14854
|
+
const fd = fs25.openSync(auditLogPath, "r");
|
|
14305
14855
|
let read;
|
|
14306
14856
|
try {
|
|
14307
|
-
read =
|
|
14857
|
+
read = fs25.readSync(fd, buf, 0, toRead, offset);
|
|
14308
14858
|
} finally {
|
|
14309
|
-
|
|
14859
|
+
fs25.closeSync(fd);
|
|
14310
14860
|
}
|
|
14311
14861
|
const { rows, consumed } = buildWireRows(buf.subarray(0, read));
|
|
14312
14862
|
if (consumed === 0) break;
|
|
@@ -14353,8 +14903,8 @@ async function shipOnce(deps = {}) {
|
|
|
14353
14903
|
}
|
|
14354
14904
|
function shipLagBytes(auditLogPath = LOCAL_AUDIT_LOG, watermarkPath = AUDIT_SHIP_WATERMARK) {
|
|
14355
14905
|
try {
|
|
14356
|
-
if (!
|
|
14357
|
-
const size =
|
|
14906
|
+
if (!fs25.existsSync(auditLogPath)) return 0;
|
|
14907
|
+
const size = fs25.statSync(auditLogPath).size;
|
|
14358
14908
|
const wm = readWatermark(watermarkPath);
|
|
14359
14909
|
if (!wm) return size;
|
|
14360
14910
|
if (wm.fileSig !== fileSignature(auditLogPath)) return size;
|
|
@@ -14385,7 +14935,7 @@ var init_audit_shipper = __esm({
|
|
|
14385
14935
|
init_config();
|
|
14386
14936
|
init_sync();
|
|
14387
14937
|
init_cloud();
|
|
14388
|
-
AUDIT_SHIP_WATERMARK =
|
|
14938
|
+
AUDIT_SHIP_WATERMARK = path27.join(os23.homedir(), ".node9", "audit-ship.json");
|
|
14389
14939
|
DEFAULT_INTERVAL_MS = 2e4;
|
|
14390
14940
|
MAX_BATCH = 500;
|
|
14391
14941
|
MAX_CHUNK_BYTES = 4 * 1024 * 1024;
|
|
@@ -14397,73 +14947,73 @@ var init_audit_shipper = __esm({
|
|
|
14397
14947
|
});
|
|
14398
14948
|
|
|
14399
14949
|
// src/daemon/dlp-scanner.ts
|
|
14400
|
-
import
|
|
14401
|
-
import
|
|
14402
|
-
import
|
|
14950
|
+
import fs26 from "fs";
|
|
14951
|
+
import path28 from "path";
|
|
14952
|
+
import os24 from "os";
|
|
14403
14953
|
function loadIndex() {
|
|
14404
14954
|
try {
|
|
14405
|
-
return JSON.parse(
|
|
14955
|
+
return JSON.parse(fs26.readFileSync(INDEX_FILE, "utf-8"));
|
|
14406
14956
|
} catch {
|
|
14407
14957
|
return {};
|
|
14408
14958
|
}
|
|
14409
14959
|
}
|
|
14410
14960
|
function saveIndex(index) {
|
|
14411
14961
|
try {
|
|
14412
|
-
|
|
14962
|
+
fs26.writeFileSync(INDEX_FILE, JSON.stringify(index), { encoding: "utf-8", mode: 384 });
|
|
14413
14963
|
} catch {
|
|
14414
14964
|
}
|
|
14415
14965
|
}
|
|
14416
14966
|
function appendAuditEntry(entry) {
|
|
14417
14967
|
try {
|
|
14418
|
-
|
|
14968
|
+
fs26.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
|
|
14419
14969
|
} catch {
|
|
14420
14970
|
}
|
|
14421
14971
|
}
|
|
14422
14972
|
function runDlpScan() {
|
|
14423
|
-
if (!
|
|
14973
|
+
if (!fs26.existsSync(PROJECTS_DIR2)) return;
|
|
14424
14974
|
const index = loadIndex();
|
|
14425
14975
|
let updated = false;
|
|
14426
14976
|
let projDirs;
|
|
14427
14977
|
try {
|
|
14428
|
-
projDirs =
|
|
14978
|
+
projDirs = fs26.readdirSync(PROJECTS_DIR2);
|
|
14429
14979
|
} catch {
|
|
14430
14980
|
return;
|
|
14431
14981
|
}
|
|
14432
14982
|
for (const proj of projDirs) {
|
|
14433
|
-
const projPath =
|
|
14983
|
+
const projPath = path28.join(PROJECTS_DIR2, proj);
|
|
14434
14984
|
try {
|
|
14435
|
-
if (!
|
|
14436
|
-
const real =
|
|
14437
|
-
if (!real.startsWith(PROJECTS_DIR2 +
|
|
14985
|
+
if (!fs26.lstatSync(projPath).isDirectory()) continue;
|
|
14986
|
+
const real = fs26.realpathSync(projPath);
|
|
14987
|
+
if (!real.startsWith(PROJECTS_DIR2 + path28.sep) && real !== PROJECTS_DIR2) continue;
|
|
14438
14988
|
} catch {
|
|
14439
14989
|
continue;
|
|
14440
14990
|
}
|
|
14441
14991
|
let files;
|
|
14442
14992
|
try {
|
|
14443
|
-
files =
|
|
14993
|
+
files = fs26.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
14444
14994
|
} catch {
|
|
14445
14995
|
continue;
|
|
14446
14996
|
}
|
|
14447
14997
|
for (const file of files) {
|
|
14448
|
-
const filePath =
|
|
14998
|
+
const filePath = path28.join(projPath, file);
|
|
14449
14999
|
const lastOffset = index[filePath] ?? 0;
|
|
14450
15000
|
let size;
|
|
14451
15001
|
try {
|
|
14452
|
-
size =
|
|
15002
|
+
size = fs26.statSync(filePath).size;
|
|
14453
15003
|
} catch {
|
|
14454
15004
|
continue;
|
|
14455
15005
|
}
|
|
14456
15006
|
if (size <= lastOffset) continue;
|
|
14457
15007
|
let fd;
|
|
14458
15008
|
try {
|
|
14459
|
-
fd =
|
|
15009
|
+
fd = fs26.openSync(filePath, "r");
|
|
14460
15010
|
} catch {
|
|
14461
15011
|
continue;
|
|
14462
15012
|
}
|
|
14463
15013
|
try {
|
|
14464
15014
|
const chunkSize = size - lastOffset;
|
|
14465
15015
|
const buf = Buffer.alloc(chunkSize);
|
|
14466
|
-
|
|
15016
|
+
fs26.readSync(fd, buf, 0, chunkSize, lastOffset);
|
|
14467
15017
|
const chunk = buf.toString("utf-8");
|
|
14468
15018
|
for (const line of chunk.split("\n")) {
|
|
14469
15019
|
if (!line.trim()) continue;
|
|
@@ -14483,7 +15033,7 @@ function runDlpScan() {
|
|
|
14483
15033
|
if (typeof text !== "string") continue;
|
|
14484
15034
|
const match = scanText(text);
|
|
14485
15035
|
if (!match) continue;
|
|
14486
|
-
const projLabel = decodeURIComponent(proj).replace(
|
|
15036
|
+
const projLabel = decodeURIComponent(proj).replace(os24.homedir(), "~").slice(0, 40);
|
|
14487
15037
|
const ts = entry.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
14488
15038
|
appendAuditEntry({
|
|
14489
15039
|
ts,
|
|
@@ -14508,7 +15058,7 @@ Run: node9 report --period 30d`
|
|
|
14508
15058
|
updated = true;
|
|
14509
15059
|
} finally {
|
|
14510
15060
|
try {
|
|
14511
|
-
|
|
15061
|
+
fs26.closeSync(fd);
|
|
14512
15062
|
} catch {
|
|
14513
15063
|
}
|
|
14514
15064
|
}
|
|
@@ -14541,23 +15091,23 @@ var init_dlp_scanner = __esm({
|
|
|
14541
15091
|
init_dlp();
|
|
14542
15092
|
init_native();
|
|
14543
15093
|
init_state2();
|
|
14544
|
-
INDEX_FILE =
|
|
14545
|
-
PROJECTS_DIR2 =
|
|
15094
|
+
INDEX_FILE = path28.join(os24.homedir(), ".node9", "dlp-index.json");
|
|
15095
|
+
PROJECTS_DIR2 = path28.join(os24.homedir(), ".claude", "projects");
|
|
14546
15096
|
}
|
|
14547
15097
|
});
|
|
14548
15098
|
|
|
14549
15099
|
// src/daemon/mcp-tools.ts
|
|
14550
|
-
import
|
|
14551
|
-
import
|
|
14552
|
-
import
|
|
15100
|
+
import fs27 from "fs";
|
|
15101
|
+
import path29 from "path";
|
|
15102
|
+
import os25 from "os";
|
|
14553
15103
|
function getMcpToolsFile() {
|
|
14554
|
-
return
|
|
15104
|
+
return path29.join(os25.homedir(), ".node9", "mcp-tools.json");
|
|
14555
15105
|
}
|
|
14556
15106
|
function readMcpToolsConfig() {
|
|
14557
15107
|
try {
|
|
14558
15108
|
const file = getMcpToolsFile();
|
|
14559
|
-
if (!
|
|
14560
|
-
const raw =
|
|
15109
|
+
if (!fs27.existsSync(file)) return {};
|
|
15110
|
+
const raw = fs27.readFileSync(file, "utf-8");
|
|
14561
15111
|
return JSON.parse(raw);
|
|
14562
15112
|
} catch {
|
|
14563
15113
|
return {};
|
|
@@ -14566,11 +15116,11 @@ function readMcpToolsConfig() {
|
|
|
14566
15116
|
function writeMcpToolsConfig(config) {
|
|
14567
15117
|
try {
|
|
14568
15118
|
const file = getMcpToolsFile();
|
|
14569
|
-
const dir =
|
|
14570
|
-
if (!
|
|
14571
|
-
const tmpPath = `${file}.${
|
|
14572
|
-
|
|
14573
|
-
|
|
15119
|
+
const dir = path29.dirname(file);
|
|
15120
|
+
if (!fs27.existsSync(dir)) fs27.mkdirSync(dir, { recursive: true });
|
|
15121
|
+
const tmpPath = `${file}.${os25.hostname()}.${process.pid}.tmp`;
|
|
15122
|
+
fs27.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
|
|
15123
|
+
fs27.renameSync(tmpPath, file);
|
|
14574
15124
|
} catch (e) {
|
|
14575
15125
|
console.error("Failed to write mcp-tools.json", e);
|
|
14576
15126
|
}
|
|
@@ -14617,9 +15167,9 @@ var init_mcp_tools = __esm({
|
|
|
14617
15167
|
|
|
14618
15168
|
// src/daemon/server.ts
|
|
14619
15169
|
import http from "http";
|
|
14620
|
-
import
|
|
14621
|
-
import
|
|
14622
|
-
import
|
|
15170
|
+
import fs28 from "fs";
|
|
15171
|
+
import path30 from "path";
|
|
15172
|
+
import os26 from "os";
|
|
14623
15173
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
14624
15174
|
import { spawnSync } from "child_process";
|
|
14625
15175
|
import chalk6 from "chalk";
|
|
@@ -14641,7 +15191,7 @@ function startDaemon() {
|
|
|
14641
15191
|
idleTimer = setTimeout(() => {
|
|
14642
15192
|
if (autoStarted) {
|
|
14643
15193
|
try {
|
|
14644
|
-
|
|
15194
|
+
fs28.unlinkSync(DAEMON_PID_FILE);
|
|
14645
15195
|
} catch {
|
|
14646
15196
|
}
|
|
14647
15197
|
}
|
|
@@ -14786,7 +15336,7 @@ data: ${JSON.stringify(item.data)}
|
|
|
14786
15336
|
mcpServer: entry.mcpServer
|
|
14787
15337
|
});
|
|
14788
15338
|
}
|
|
14789
|
-
const projectCwd = typeof cwd === "string" &&
|
|
15339
|
+
const projectCwd = typeof cwd === "string" && path30.isAbsolute(cwd) ? cwd : void 0;
|
|
14790
15340
|
const projectConfig = getConfig(projectCwd);
|
|
14791
15341
|
const browserEnabled = projectConfig.settings.approvers?.browser !== false;
|
|
14792
15342
|
const terminalEnabled = projectConfig.settings.approvers?.terminal !== false;
|
|
@@ -15078,8 +15628,8 @@ data: ${JSON.stringify(item.data)}
|
|
|
15078
15628
|
if (!validToken(req)) return res.writeHead(403).end();
|
|
15079
15629
|
const periodParam = reqUrl.searchParams.get("period") || "7d";
|
|
15080
15630
|
const period = ["today", "7d", "30d", "month"].includes(periodParam) ? periodParam : "7d";
|
|
15081
|
-
const logPath =
|
|
15082
|
-
if (!
|
|
15631
|
+
const logPath = path30.join(os26.homedir(), ".node9", "audit.log");
|
|
15632
|
+
if (!fs28.existsSync(logPath)) {
|
|
15083
15633
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
15084
15634
|
return res.end(
|
|
15085
15635
|
JSON.stringify({
|
|
@@ -15092,7 +15642,7 @@ data: ${JSON.stringify(item.data)}
|
|
|
15092
15642
|
);
|
|
15093
15643
|
}
|
|
15094
15644
|
try {
|
|
15095
|
-
const raw =
|
|
15645
|
+
const raw = fs28.readFileSync(logPath, "utf-8");
|
|
15096
15646
|
const allEntries = raw.split("\n").flatMap((line) => {
|
|
15097
15647
|
if (!line.trim()) return [];
|
|
15098
15648
|
try {
|
|
@@ -15415,14 +15965,14 @@ data: ${JSON.stringify(item.data)}
|
|
|
15415
15965
|
server.on("error", (e) => {
|
|
15416
15966
|
if (e.code === "EADDRINUSE") {
|
|
15417
15967
|
try {
|
|
15418
|
-
if (
|
|
15419
|
-
const { pid } = JSON.parse(
|
|
15968
|
+
if (fs28.existsSync(DAEMON_PID_FILE)) {
|
|
15969
|
+
const { pid } = JSON.parse(fs28.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
15420
15970
|
process.kill(pid, 0);
|
|
15421
15971
|
return process.exit(0);
|
|
15422
15972
|
}
|
|
15423
15973
|
} catch {
|
|
15424
15974
|
try {
|
|
15425
|
-
|
|
15975
|
+
fs28.unlinkSync(DAEMON_PID_FILE);
|
|
15426
15976
|
} catch {
|
|
15427
15977
|
}
|
|
15428
15978
|
server.listen(DAEMON_PORT, DAEMON_HOST);
|
|
@@ -15511,15 +16061,15 @@ var init_server = __esm({
|
|
|
15511
16061
|
});
|
|
15512
16062
|
|
|
15513
16063
|
// src/daemon/service.ts
|
|
15514
|
-
import
|
|
15515
|
-
import
|
|
15516
|
-
import
|
|
16064
|
+
import fs29 from "fs";
|
|
16065
|
+
import path31 from "path";
|
|
16066
|
+
import os27 from "os";
|
|
15517
16067
|
import { spawnSync as spawnSync2, execFileSync } from "child_process";
|
|
15518
16068
|
function resolveNode9Binary() {
|
|
15519
16069
|
try {
|
|
15520
16070
|
const script = process.argv[1];
|
|
15521
|
-
if (typeof script === "string" &&
|
|
15522
|
-
return
|
|
16071
|
+
if (typeof script === "string" && path31.isAbsolute(script) && fs29.existsSync(script)) {
|
|
16072
|
+
return fs29.realpathSync(script);
|
|
15523
16073
|
}
|
|
15524
16074
|
} catch {
|
|
15525
16075
|
}
|
|
@@ -15537,11 +16087,11 @@ function xmlEscape(s) {
|
|
|
15537
16087
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
15538
16088
|
}
|
|
15539
16089
|
function launchdPlist(binaryPath) {
|
|
15540
|
-
const logDir =
|
|
16090
|
+
const logDir = path31.join(os27.homedir(), ".node9");
|
|
15541
16091
|
const nodePath = xmlEscape(process.execPath);
|
|
15542
16092
|
const scriptPath = xmlEscape(binaryPath);
|
|
15543
|
-
const outLog = xmlEscape(
|
|
15544
|
-
const errLog = xmlEscape(
|
|
16093
|
+
const outLog = xmlEscape(path31.join(logDir, "daemon.log"));
|
|
16094
|
+
const errLog = xmlEscape(path31.join(logDir, "daemon-error.log"));
|
|
15545
16095
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
15546
16096
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
15547
16097
|
<plist version="1.0">
|
|
@@ -15574,9 +16124,9 @@ function launchdPlist(binaryPath) {
|
|
|
15574
16124
|
`;
|
|
15575
16125
|
}
|
|
15576
16126
|
function installLaunchd(binaryPath) {
|
|
15577
|
-
const dir =
|
|
15578
|
-
if (!
|
|
15579
|
-
|
|
16127
|
+
const dir = path31.dirname(LAUNCHD_PLIST);
|
|
16128
|
+
if (!fs29.existsSync(dir)) fs29.mkdirSync(dir, { recursive: true });
|
|
16129
|
+
fs29.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
|
|
15580
16130
|
spawnSync2("launchctl", ["unload", LAUNCHD_PLIST], { encoding: "utf8" });
|
|
15581
16131
|
const r = spawnSync2("launchctl", ["load", "-w", LAUNCHD_PLIST], {
|
|
15582
16132
|
encoding: "utf8",
|
|
@@ -15587,13 +16137,13 @@ function installLaunchd(binaryPath) {
|
|
|
15587
16137
|
}
|
|
15588
16138
|
}
|
|
15589
16139
|
function uninstallLaunchd() {
|
|
15590
|
-
if (
|
|
16140
|
+
if (fs29.existsSync(LAUNCHD_PLIST)) {
|
|
15591
16141
|
spawnSync2("launchctl", ["unload", "-w", LAUNCHD_PLIST], { encoding: "utf8", timeout: 5e3 });
|
|
15592
|
-
|
|
16142
|
+
fs29.unlinkSync(LAUNCHD_PLIST);
|
|
15593
16143
|
}
|
|
15594
16144
|
}
|
|
15595
16145
|
function isLaunchdInstalled() {
|
|
15596
|
-
return
|
|
16146
|
+
return fs29.existsSync(LAUNCHD_PLIST);
|
|
15597
16147
|
}
|
|
15598
16148
|
function systemdUnit(binaryPath) {
|
|
15599
16149
|
return `[Unit]
|
|
@@ -15612,12 +16162,12 @@ WantedBy=default.target
|
|
|
15612
16162
|
`;
|
|
15613
16163
|
}
|
|
15614
16164
|
function installSystemd(binaryPath) {
|
|
15615
|
-
if (!
|
|
15616
|
-
|
|
16165
|
+
if (!fs29.existsSync(SYSTEMD_UNIT_DIR)) {
|
|
16166
|
+
fs29.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
|
|
15617
16167
|
}
|
|
15618
|
-
|
|
16168
|
+
fs29.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
|
|
15619
16169
|
try {
|
|
15620
|
-
execFileSync("loginctl", ["enable-linger",
|
|
16170
|
+
execFileSync("loginctl", ["enable-linger", os27.userInfo().username], { timeout: 3e3 });
|
|
15621
16171
|
} catch {
|
|
15622
16172
|
}
|
|
15623
16173
|
const reload = spawnSync2("systemctl", ["--user", "daemon-reload"], {
|
|
@@ -15637,23 +16187,23 @@ function installSystemd(binaryPath) {
|
|
|
15637
16187
|
}
|
|
15638
16188
|
}
|
|
15639
16189
|
function uninstallSystemd() {
|
|
15640
|
-
if (
|
|
16190
|
+
if (fs29.existsSync(SYSTEMD_UNIT)) {
|
|
15641
16191
|
spawnSync2("systemctl", ["--user", "disable", "--now", "node9-daemon"], {
|
|
15642
16192
|
encoding: "utf8",
|
|
15643
16193
|
timeout: 5e3
|
|
15644
16194
|
});
|
|
15645
16195
|
spawnSync2("systemctl", ["--user", "daemon-reload"], { encoding: "utf8", timeout: 5e3 });
|
|
15646
|
-
|
|
16196
|
+
fs29.unlinkSync(SYSTEMD_UNIT);
|
|
15647
16197
|
}
|
|
15648
16198
|
}
|
|
15649
16199
|
function isSystemdInstalled() {
|
|
15650
|
-
return
|
|
16200
|
+
return fs29.existsSync(SYSTEMD_UNIT);
|
|
15651
16201
|
}
|
|
15652
16202
|
function stopRunningDaemon() {
|
|
15653
|
-
const pidFile =
|
|
15654
|
-
if (!
|
|
16203
|
+
const pidFile = path31.join(os27.homedir(), ".node9", "daemon.pid");
|
|
16204
|
+
if (!fs29.existsSync(pidFile)) return;
|
|
15655
16205
|
try {
|
|
15656
|
-
const data = JSON.parse(
|
|
16206
|
+
const data = JSON.parse(fs29.readFileSync(pidFile, "utf-8"));
|
|
15657
16207
|
const pid = data.pid;
|
|
15658
16208
|
const MAX_PID2 = 4194304;
|
|
15659
16209
|
if (typeof pid === "number" && Number.isInteger(pid) && pid > 0 && pid <= MAX_PID2) {
|
|
@@ -15673,7 +16223,7 @@ function stopRunningDaemon() {
|
|
|
15673
16223
|
}
|
|
15674
16224
|
}
|
|
15675
16225
|
try {
|
|
15676
|
-
|
|
16226
|
+
fs29.unlinkSync(pidFile);
|
|
15677
16227
|
} catch {
|
|
15678
16228
|
}
|
|
15679
16229
|
} catch {
|
|
@@ -15748,19 +16298,19 @@ var init_service = __esm({
|
|
|
15748
16298
|
"src/daemon/service.ts"() {
|
|
15749
16299
|
"use strict";
|
|
15750
16300
|
LAUNCHD_LABEL = "ai.node9.daemon";
|
|
15751
|
-
LAUNCHD_PLIST =
|
|
15752
|
-
SYSTEMD_UNIT_DIR =
|
|
15753
|
-
SYSTEMD_UNIT =
|
|
16301
|
+
LAUNCHD_PLIST = path31.join(os27.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
|
|
16302
|
+
SYSTEMD_UNIT_DIR = path31.join(os27.homedir(), ".config", "systemd", "user");
|
|
16303
|
+
SYSTEMD_UNIT = path31.join(SYSTEMD_UNIT_DIR, "node9-daemon.service");
|
|
15754
16304
|
}
|
|
15755
16305
|
});
|
|
15756
16306
|
|
|
15757
16307
|
// src/daemon/index.ts
|
|
15758
|
-
import
|
|
16308
|
+
import fs30 from "fs";
|
|
15759
16309
|
import chalk7 from "chalk";
|
|
15760
16310
|
function stopDaemon() {
|
|
15761
|
-
if (!
|
|
16311
|
+
if (!fs30.existsSync(DAEMON_PID_FILE)) return console.log(chalk7.yellow("Not running."));
|
|
15762
16312
|
try {
|
|
15763
|
-
const data = JSON.parse(
|
|
16313
|
+
const data = JSON.parse(fs30.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
15764
16314
|
const pid = data.pid;
|
|
15765
16315
|
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
|
|
15766
16316
|
console.log(chalk7.gray("Cleaned up invalid PID file."));
|
|
@@ -15772,7 +16322,7 @@ function stopDaemon() {
|
|
|
15772
16322
|
console.log(chalk7.gray("Cleaned up stale PID file."));
|
|
15773
16323
|
} finally {
|
|
15774
16324
|
try {
|
|
15775
|
-
|
|
16325
|
+
fs30.unlinkSync(DAEMON_PID_FILE);
|
|
15776
16326
|
} catch {
|
|
15777
16327
|
}
|
|
15778
16328
|
}
|
|
@@ -15781,9 +16331,9 @@ function daemonStatus() {
|
|
|
15781
16331
|
const serviceInstalled = isDaemonServiceInstalled();
|
|
15782
16332
|
const serviceLabel = serviceInstalled ? chalk7.green("installed (starts on login)") : chalk7.yellow("not installed \u2014 run: node9 daemon install");
|
|
15783
16333
|
let processStatus;
|
|
15784
|
-
if (
|
|
16334
|
+
if (fs30.existsSync(DAEMON_PID_FILE)) {
|
|
15785
16335
|
try {
|
|
15786
|
-
const data = JSON.parse(
|
|
16336
|
+
const data = JSON.parse(fs30.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
15787
16337
|
const pid = data.pid;
|
|
15788
16338
|
const port = data.port;
|
|
15789
16339
|
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
|
|
@@ -15828,9 +16378,9 @@ __export(tail_exports, {
|
|
|
15828
16378
|
});
|
|
15829
16379
|
import http2 from "http";
|
|
15830
16380
|
import chalk29 from "chalk";
|
|
15831
|
-
import
|
|
15832
|
-
import
|
|
15833
|
-
import
|
|
16381
|
+
import fs48 from "fs";
|
|
16382
|
+
import os43 from "os";
|
|
16383
|
+
import path49 from "path";
|
|
15834
16384
|
import readline6 from "readline";
|
|
15835
16385
|
import { spawn as spawn8 } from "child_process";
|
|
15836
16386
|
function shortenPathSummary(s) {
|
|
@@ -15854,20 +16404,20 @@ function getModelContextLimit(model) {
|
|
|
15854
16404
|
return 2e5;
|
|
15855
16405
|
}
|
|
15856
16406
|
function readSessionUsage() {
|
|
15857
|
-
const projectsDir =
|
|
15858
|
-
if (!
|
|
16407
|
+
const projectsDir = path49.join(os43.homedir(), ".claude", "projects");
|
|
16408
|
+
if (!fs48.existsSync(projectsDir)) return null;
|
|
15859
16409
|
let latestFile = null;
|
|
15860
16410
|
let latestMtime = 0;
|
|
15861
16411
|
try {
|
|
15862
|
-
for (const dir of
|
|
15863
|
-
const dirPath =
|
|
16412
|
+
for (const dir of fs48.readdirSync(projectsDir)) {
|
|
16413
|
+
const dirPath = path49.join(projectsDir, dir);
|
|
15864
16414
|
try {
|
|
15865
|
-
if (!
|
|
15866
|
-
for (const file of
|
|
16415
|
+
if (!fs48.statSync(dirPath).isDirectory()) continue;
|
|
16416
|
+
for (const file of fs48.readdirSync(dirPath)) {
|
|
15867
16417
|
if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
|
|
15868
|
-
const filePath =
|
|
16418
|
+
const filePath = path49.join(dirPath, file);
|
|
15869
16419
|
try {
|
|
15870
|
-
const mtime =
|
|
16420
|
+
const mtime = fs48.statSync(filePath).mtimeMs;
|
|
15871
16421
|
if (mtime > latestMtime) {
|
|
15872
16422
|
latestMtime = mtime;
|
|
15873
16423
|
latestFile = filePath;
|
|
@@ -15882,7 +16432,7 @@ function readSessionUsage() {
|
|
|
15882
16432
|
}
|
|
15883
16433
|
if (!latestFile) return null;
|
|
15884
16434
|
try {
|
|
15885
|
-
const lines =
|
|
16435
|
+
const lines = fs48.readFileSync(latestFile, "utf-8").split("\n");
|
|
15886
16436
|
let lastModel = "";
|
|
15887
16437
|
let lastInput = 0;
|
|
15888
16438
|
let lastOutput = 0;
|
|
@@ -15943,7 +16493,7 @@ function formatBase(activity) {
|
|
|
15943
16493
|
const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
|
|
15944
16494
|
const icon = getIcon(activity.tool);
|
|
15945
16495
|
const toolName = activity.tool.slice(0, 16).padEnd(16);
|
|
15946
|
-
const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(
|
|
16496
|
+
const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(os43.homedir(), "~");
|
|
15947
16497
|
const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
|
|
15948
16498
|
return `${chalk29.gray(time)} ${icon} ${agentLabel(activity.agent, activity.mcpServer, activity.sessionId)}${chalk29.white.bold(toolName)} ${chalk29.dim(argsPreview)}`;
|
|
15949
16499
|
}
|
|
@@ -15982,9 +16532,9 @@ function renderPending(activity) {
|
|
|
15982
16532
|
}
|
|
15983
16533
|
async function ensureDaemon() {
|
|
15984
16534
|
let pidPort = null;
|
|
15985
|
-
if (
|
|
16535
|
+
if (fs48.existsSync(PID_FILE)) {
|
|
15986
16536
|
try {
|
|
15987
|
-
const { port } = JSON.parse(
|
|
16537
|
+
const { port } = JSON.parse(fs48.readFileSync(PID_FILE, "utf-8"));
|
|
15988
16538
|
pidPort = port;
|
|
15989
16539
|
} catch {
|
|
15990
16540
|
console.error(chalk29.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
|
|
@@ -16140,9 +16690,9 @@ function buildRecoveryCardLines(req) {
|
|
|
16140
16690
|
];
|
|
16141
16691
|
}
|
|
16142
16692
|
function readApproversFromDisk() {
|
|
16143
|
-
const configPath =
|
|
16693
|
+
const configPath = path49.join(os43.homedir(), ".node9", "config.json");
|
|
16144
16694
|
try {
|
|
16145
|
-
const raw = JSON.parse(
|
|
16695
|
+
const raw = JSON.parse(fs48.readFileSync(configPath, "utf-8"));
|
|
16146
16696
|
const settings = raw.settings ?? {};
|
|
16147
16697
|
return settings.approvers ?? {};
|
|
16148
16698
|
} catch {
|
|
@@ -16158,15 +16708,15 @@ function approverStatusLine() {
|
|
|
16158
16708
|
return `${fmt("native", "native")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
|
|
16159
16709
|
}
|
|
16160
16710
|
function toggleApprover(channel) {
|
|
16161
|
-
const configPath =
|
|
16711
|
+
const configPath = path49.join(os43.homedir(), ".node9", "config.json");
|
|
16162
16712
|
try {
|
|
16163
|
-
const raw = JSON.parse(
|
|
16713
|
+
const raw = JSON.parse(fs48.readFileSync(configPath, "utf-8"));
|
|
16164
16714
|
const settings = raw.settings ?? {};
|
|
16165
16715
|
const approvers = settings.approvers ?? {};
|
|
16166
16716
|
approvers[channel] = approvers[channel] === false;
|
|
16167
16717
|
settings.approvers = approvers;
|
|
16168
16718
|
raw.settings = settings;
|
|
16169
|
-
|
|
16719
|
+
fs48.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
|
|
16170
16720
|
} catch (err2) {
|
|
16171
16721
|
process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
|
|
16172
16722
|
`);
|
|
@@ -16338,8 +16888,8 @@ async function startTail(options = {}) {
|
|
|
16338
16888
|
}
|
|
16339
16889
|
postDecisionHttp(req2.id, httpDecision, authToken, port, httpOpts).catch((err2) => {
|
|
16340
16890
|
try {
|
|
16341
|
-
|
|
16342
|
-
|
|
16891
|
+
fs48.appendFileSync(
|
|
16892
|
+
path49.join(os43.homedir(), ".node9", "hook-debug.log"),
|
|
16343
16893
|
`[tail] POST /decision failed: ${String(err2)}
|
|
16344
16894
|
`
|
|
16345
16895
|
);
|
|
@@ -16403,9 +16953,9 @@ async function startTail(options = {}) {
|
|
|
16403
16953
|
};
|
|
16404
16954
|
process.stdin.on("keypress", onKeypress);
|
|
16405
16955
|
}
|
|
16406
|
-
const auditLog =
|
|
16956
|
+
const auditLog = path49.join(os43.homedir(), ".node9", "audit.log");
|
|
16407
16957
|
try {
|
|
16408
|
-
const unackedDlp =
|
|
16958
|
+
const unackedDlp = fs48.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
|
|
16409
16959
|
if (unackedDlp > 0) {
|
|
16410
16960
|
console.log("");
|
|
16411
16961
|
console.log(
|
|
@@ -16445,7 +16995,7 @@ async function startTail(options = {}) {
|
|
|
16445
16995
|
if (stallWarned) return;
|
|
16446
16996
|
if (Date.now() - lastActivityFromDaemon < STALL_THRESHOLD_MS) return;
|
|
16447
16997
|
try {
|
|
16448
|
-
const auditMtime =
|
|
16998
|
+
const auditMtime = fs48.statSync(auditLog).mtimeMs;
|
|
16449
16999
|
if (Date.now() - auditMtime >= STALL_THRESHOLD_MS) return;
|
|
16450
17000
|
console.log("");
|
|
16451
17001
|
console.log(
|
|
@@ -16636,7 +17186,7 @@ var init_tail = __esm({
|
|
|
16636
17186
|
"use strict";
|
|
16637
17187
|
init_daemon2();
|
|
16638
17188
|
init_daemon();
|
|
16639
|
-
PID_FILE =
|
|
17189
|
+
PID_FILE = path49.join(os43.homedir(), ".node9", "daemon.pid");
|
|
16640
17190
|
ICONS = {
|
|
16641
17191
|
bash: "\u{1F4BB}",
|
|
16642
17192
|
shell: "\u{1F4BB}",
|
|
@@ -16684,9 +17234,9 @@ __export(hud_exports, {
|
|
|
16684
17234
|
main: () => main,
|
|
16685
17235
|
renderEnvironmentLine: () => renderEnvironmentLine
|
|
16686
17236
|
});
|
|
16687
|
-
import
|
|
16688
|
-
import
|
|
16689
|
-
import
|
|
17237
|
+
import fs49 from "fs";
|
|
17238
|
+
import path50 from "path";
|
|
17239
|
+
import os44 from "os";
|
|
16690
17240
|
import http3 from "http";
|
|
16691
17241
|
async function readStdin() {
|
|
16692
17242
|
const chunks = [];
|
|
@@ -16762,9 +17312,9 @@ function formatTimeLeft(resetsAt) {
|
|
|
16762
17312
|
return ` (${m}m left)`;
|
|
16763
17313
|
}
|
|
16764
17314
|
function safeReadJson(filePath) {
|
|
16765
|
-
if (!
|
|
17315
|
+
if (!fs49.existsSync(filePath)) return null;
|
|
16766
17316
|
try {
|
|
16767
|
-
return JSON.parse(
|
|
17317
|
+
return JSON.parse(fs49.readFileSync(filePath, "utf-8"));
|
|
16768
17318
|
} catch {
|
|
16769
17319
|
return null;
|
|
16770
17320
|
}
|
|
@@ -16785,12 +17335,12 @@ function countHooksInFile(filePath) {
|
|
|
16785
17335
|
return Object.keys(cfg.hooks).length;
|
|
16786
17336
|
}
|
|
16787
17337
|
function countRulesInDir(rulesDir) {
|
|
16788
|
-
if (!
|
|
17338
|
+
if (!fs49.existsSync(rulesDir)) return 0;
|
|
16789
17339
|
let count = 0;
|
|
16790
17340
|
try {
|
|
16791
|
-
for (const entry of
|
|
17341
|
+
for (const entry of fs49.readdirSync(rulesDir, { withFileTypes: true })) {
|
|
16792
17342
|
if (entry.isDirectory()) {
|
|
16793
|
-
count += countRulesInDir(
|
|
17343
|
+
count += countRulesInDir(path50.join(rulesDir, entry.name));
|
|
16794
17344
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
16795
17345
|
count++;
|
|
16796
17346
|
}
|
|
@@ -16801,46 +17351,46 @@ function countRulesInDir(rulesDir) {
|
|
|
16801
17351
|
}
|
|
16802
17352
|
function isSamePath(a, b) {
|
|
16803
17353
|
try {
|
|
16804
|
-
return
|
|
17354
|
+
return path50.resolve(a) === path50.resolve(b);
|
|
16805
17355
|
} catch {
|
|
16806
17356
|
return false;
|
|
16807
17357
|
}
|
|
16808
17358
|
}
|
|
16809
17359
|
function countConfigs(cwd) {
|
|
16810
|
-
const homeDir2 =
|
|
16811
|
-
const claudeDir =
|
|
17360
|
+
const homeDir2 = os44.homedir();
|
|
17361
|
+
const claudeDir = path50.join(homeDir2, ".claude");
|
|
16812
17362
|
let claudeMdCount = 0;
|
|
16813
17363
|
let rulesCount = 0;
|
|
16814
17364
|
let hooksCount = 0;
|
|
16815
17365
|
const userMcpServers = /* @__PURE__ */ new Set();
|
|
16816
17366
|
const projectMcpServers = /* @__PURE__ */ new Set();
|
|
16817
|
-
if (
|
|
16818
|
-
rulesCount += countRulesInDir(
|
|
16819
|
-
const userSettings =
|
|
17367
|
+
if (fs49.existsSync(path50.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
17368
|
+
rulesCount += countRulesInDir(path50.join(claudeDir, "rules"));
|
|
17369
|
+
const userSettings = path50.join(claudeDir, "settings.json");
|
|
16820
17370
|
for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
|
|
16821
17371
|
hooksCount += countHooksInFile(userSettings);
|
|
16822
|
-
const userClaudeJson =
|
|
17372
|
+
const userClaudeJson = path50.join(homeDir2, ".claude.json");
|
|
16823
17373
|
for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
|
|
16824
17374
|
for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
|
|
16825
17375
|
userMcpServers.delete(name);
|
|
16826
17376
|
}
|
|
16827
17377
|
if (cwd) {
|
|
16828
|
-
if (
|
|
16829
|
-
if (
|
|
16830
|
-
const projectClaudeDir =
|
|
17378
|
+
if (fs49.existsSync(path50.join(cwd, "CLAUDE.md"))) claudeMdCount++;
|
|
17379
|
+
if (fs49.existsSync(path50.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
|
|
17380
|
+
const projectClaudeDir = path50.join(cwd, ".claude");
|
|
16831
17381
|
const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
|
|
16832
17382
|
if (!overlapsUserScope) {
|
|
16833
|
-
if (
|
|
16834
|
-
rulesCount += countRulesInDir(
|
|
16835
|
-
const projSettings =
|
|
17383
|
+
if (fs49.existsSync(path50.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
17384
|
+
rulesCount += countRulesInDir(path50.join(projectClaudeDir, "rules"));
|
|
17385
|
+
const projSettings = path50.join(projectClaudeDir, "settings.json");
|
|
16836
17386
|
for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
|
|
16837
17387
|
hooksCount += countHooksInFile(projSettings);
|
|
16838
17388
|
}
|
|
16839
|
-
if (
|
|
16840
|
-
const localSettings =
|
|
17389
|
+
if (fs49.existsSync(path50.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
|
|
17390
|
+
const localSettings = path50.join(projectClaudeDir, "settings.local.json");
|
|
16841
17391
|
for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
|
|
16842
17392
|
hooksCount += countHooksInFile(localSettings);
|
|
16843
|
-
const mcpJsonServers = getMcpServerNames(
|
|
17393
|
+
const mcpJsonServers = getMcpServerNames(path50.join(cwd, ".mcp.json"));
|
|
16844
17394
|
const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
|
|
16845
17395
|
for (const name of disabledMcpJson) mcpJsonServers.delete(name);
|
|
16846
17396
|
for (const name of mcpJsonServers) projectMcpServers.add(name);
|
|
@@ -16873,12 +17423,12 @@ function readActiveShieldsHud() {
|
|
|
16873
17423
|
return shieldsCache.value;
|
|
16874
17424
|
}
|
|
16875
17425
|
try {
|
|
16876
|
-
const shieldsPath =
|
|
16877
|
-
if (!
|
|
17426
|
+
const shieldsPath = path50.join(os44.homedir(), ".node9", "shields.json");
|
|
17427
|
+
if (!fs49.existsSync(shieldsPath)) {
|
|
16878
17428
|
shieldsCache = { value: [], ts: now };
|
|
16879
17429
|
return [];
|
|
16880
17430
|
}
|
|
16881
|
-
const parsed = JSON.parse(
|
|
17431
|
+
const parsed = JSON.parse(fs49.readFileSync(shieldsPath, "utf-8"));
|
|
16882
17432
|
if (!Array.isArray(parsed.active)) {
|
|
16883
17433
|
shieldsCache = { value: [], ts: now };
|
|
16884
17434
|
return [];
|
|
@@ -16980,17 +17530,17 @@ function renderContextLine(stdin) {
|
|
|
16980
17530
|
async function main() {
|
|
16981
17531
|
try {
|
|
16982
17532
|
const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
|
|
16983
|
-
if (
|
|
17533
|
+
if (fs49.existsSync(path50.join(os44.homedir(), ".node9", "hud-debug"))) {
|
|
16984
17534
|
try {
|
|
16985
|
-
const logPath =
|
|
17535
|
+
const logPath = path50.join(os44.homedir(), ".node9", "hud-debug.log");
|
|
16986
17536
|
const MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
16987
17537
|
let size = 0;
|
|
16988
17538
|
try {
|
|
16989
|
-
size =
|
|
17539
|
+
size = fs49.statSync(logPath).size;
|
|
16990
17540
|
} catch {
|
|
16991
17541
|
}
|
|
16992
17542
|
if (size < MAX_LOG_SIZE) {
|
|
16993
|
-
|
|
17543
|
+
fs49.appendFileSync(
|
|
16994
17544
|
logPath,
|
|
16995
17545
|
JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
|
|
16996
17546
|
);
|
|
@@ -17011,11 +17561,11 @@ async function main() {
|
|
|
17011
17561
|
try {
|
|
17012
17562
|
const cwd = stdin.cwd ?? process.cwd();
|
|
17013
17563
|
for (const configPath of [
|
|
17014
|
-
|
|
17015
|
-
|
|
17564
|
+
path50.join(cwd, "node9.config.json"),
|
|
17565
|
+
path50.join(os44.homedir(), ".node9", "config.json")
|
|
17016
17566
|
]) {
|
|
17017
|
-
if (!
|
|
17018
|
-
const cfg = JSON.parse(
|
|
17567
|
+
if (!fs49.existsSync(configPath)) continue;
|
|
17568
|
+
const cfg = JSON.parse(fs49.readFileSync(configPath, "utf-8"));
|
|
17019
17569
|
const hud = cfg.settings?.hud;
|
|
17020
17570
|
if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
|
|
17021
17571
|
}
|
|
@@ -17062,9 +17612,9 @@ init_setup();
|
|
|
17062
17612
|
init_daemon2();
|
|
17063
17613
|
import { Command } from "commander";
|
|
17064
17614
|
import chalk30 from "chalk";
|
|
17065
|
-
import
|
|
17066
|
-
import
|
|
17067
|
-
import
|
|
17615
|
+
import fs50 from "fs";
|
|
17616
|
+
import path51 from "path";
|
|
17617
|
+
import os45 from "os";
|
|
17068
17618
|
import { confirm as confirm2 } from "@inquirer/prompts";
|
|
17069
17619
|
|
|
17070
17620
|
// src/utils/duration.ts
|
|
@@ -17247,17 +17797,17 @@ async function runProxy(targetCommand) {
|
|
|
17247
17797
|
// src/cli/daemon-starter.ts
|
|
17248
17798
|
init_daemon();
|
|
17249
17799
|
import { spawn as spawn3 } from "child_process";
|
|
17250
|
-
import
|
|
17251
|
-
import
|
|
17800
|
+
import path32 from "path";
|
|
17801
|
+
import fs31 from "fs";
|
|
17252
17802
|
function isTestingMode() {
|
|
17253
17803
|
return /^(1|true|yes)$/i.test(process.env.NODE9_TESTING ?? "");
|
|
17254
17804
|
}
|
|
17255
17805
|
async function autoStartDaemonAndWait() {
|
|
17256
17806
|
if (isTestingMode()) return false;
|
|
17257
|
-
if (!
|
|
17807
|
+
if (!path32.isAbsolute(process.argv[1])) return false;
|
|
17258
17808
|
let resolvedArgv1;
|
|
17259
17809
|
try {
|
|
17260
|
-
resolvedArgv1 =
|
|
17810
|
+
resolvedArgv1 = fs31.realpathSync(process.argv[1]);
|
|
17261
17811
|
} catch {
|
|
17262
17812
|
return false;
|
|
17263
17813
|
}
|
|
@@ -17288,19 +17838,19 @@ init_daemon();
|
|
|
17288
17838
|
init_config();
|
|
17289
17839
|
init_policy();
|
|
17290
17840
|
import chalk9 from "chalk";
|
|
17291
|
-
import
|
|
17841
|
+
import fs34 from "fs";
|
|
17292
17842
|
import { spawn as spawn5 } from "child_process";
|
|
17293
|
-
import
|
|
17294
|
-
import
|
|
17843
|
+
import path35 from "path";
|
|
17844
|
+
import os30 from "os";
|
|
17295
17845
|
|
|
17296
17846
|
// src/undo.ts
|
|
17297
17847
|
import { spawnSync as spawnSync3, spawn as spawn4 } from "child_process";
|
|
17298
17848
|
import crypto6 from "crypto";
|
|
17299
|
-
import
|
|
17849
|
+
import fs32 from "fs";
|
|
17300
17850
|
import net3 from "net";
|
|
17301
|
-
import
|
|
17302
|
-
import
|
|
17303
|
-
var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" :
|
|
17851
|
+
import path33 from "path";
|
|
17852
|
+
import os28 from "os";
|
|
17853
|
+
var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path33.join(os28.tmpdir(), "node9-activity.sock");
|
|
17304
17854
|
function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
|
|
17305
17855
|
try {
|
|
17306
17856
|
const payload = JSON.stringify({
|
|
@@ -17320,22 +17870,22 @@ function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
|
|
|
17320
17870
|
} catch {
|
|
17321
17871
|
}
|
|
17322
17872
|
}
|
|
17323
|
-
var SNAPSHOT_STACK_PATH =
|
|
17324
|
-
var UNDO_LATEST_PATH =
|
|
17873
|
+
var SNAPSHOT_STACK_PATH = path33.join(os28.homedir(), ".node9", "snapshots.json");
|
|
17874
|
+
var UNDO_LATEST_PATH = path33.join(os28.homedir(), ".node9", "undo_latest.txt");
|
|
17325
17875
|
var MAX_SNAPSHOTS = 10;
|
|
17326
17876
|
var GIT_TIMEOUT = 15e3;
|
|
17327
17877
|
function readStack() {
|
|
17328
17878
|
try {
|
|
17329
|
-
if (
|
|
17330
|
-
return JSON.parse(
|
|
17879
|
+
if (fs32.existsSync(SNAPSHOT_STACK_PATH))
|
|
17880
|
+
return JSON.parse(fs32.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
|
|
17331
17881
|
} catch {
|
|
17332
17882
|
}
|
|
17333
17883
|
return [];
|
|
17334
17884
|
}
|
|
17335
17885
|
function writeStack(stack) {
|
|
17336
|
-
const dir =
|
|
17337
|
-
if (!
|
|
17338
|
-
|
|
17886
|
+
const dir = path33.dirname(SNAPSHOT_STACK_PATH);
|
|
17887
|
+
if (!fs32.existsSync(dir)) fs32.mkdirSync(dir, { recursive: true });
|
|
17888
|
+
fs32.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
|
|
17339
17889
|
}
|
|
17340
17890
|
function extractFilePath(args) {
|
|
17341
17891
|
if (!args || typeof args !== "object") return null;
|
|
@@ -17355,12 +17905,12 @@ function buildArgsSummary(tool, args) {
|
|
|
17355
17905
|
return "";
|
|
17356
17906
|
}
|
|
17357
17907
|
function findProjectRoot(filePath) {
|
|
17358
|
-
let dir =
|
|
17908
|
+
let dir = path33.dirname(filePath);
|
|
17359
17909
|
while (true) {
|
|
17360
|
-
if (
|
|
17910
|
+
if (fs32.existsSync(path33.join(dir, ".git")) || fs32.existsSync(path33.join(dir, "package.json"))) {
|
|
17361
17911
|
return dir;
|
|
17362
17912
|
}
|
|
17363
|
-
const parent =
|
|
17913
|
+
const parent = path33.dirname(dir);
|
|
17364
17914
|
if (parent === dir) return process.cwd();
|
|
17365
17915
|
dir = parent;
|
|
17366
17916
|
}
|
|
@@ -17368,7 +17918,7 @@ function findProjectRoot(filePath) {
|
|
|
17368
17918
|
function normalizeCwdForHash(cwd) {
|
|
17369
17919
|
let normalized;
|
|
17370
17920
|
try {
|
|
17371
|
-
normalized =
|
|
17921
|
+
normalized = fs32.realpathSync(cwd);
|
|
17372
17922
|
} catch {
|
|
17373
17923
|
normalized = cwd;
|
|
17374
17924
|
}
|
|
@@ -17378,16 +17928,16 @@ function normalizeCwdForHash(cwd) {
|
|
|
17378
17928
|
}
|
|
17379
17929
|
function getShadowRepoDir(cwd) {
|
|
17380
17930
|
const hash = crypto6.createHash("sha256").update(normalizeCwdForHash(cwd)).digest("hex").slice(0, 16);
|
|
17381
|
-
return
|
|
17931
|
+
return path33.join(os28.homedir(), ".node9", "snapshots", hash);
|
|
17382
17932
|
}
|
|
17383
17933
|
function cleanOrphanedIndexFiles(shadowDir) {
|
|
17384
17934
|
try {
|
|
17385
17935
|
const cutoff = Date.now() - 6e4;
|
|
17386
|
-
for (const f of
|
|
17936
|
+
for (const f of fs32.readdirSync(shadowDir)) {
|
|
17387
17937
|
if (f.startsWith("index_")) {
|
|
17388
|
-
const fp =
|
|
17938
|
+
const fp = path33.join(shadowDir, f);
|
|
17389
17939
|
try {
|
|
17390
|
-
if (
|
|
17940
|
+
if (fs32.statSync(fp).mtimeMs < cutoff) fs32.unlinkSync(fp);
|
|
17391
17941
|
} catch {
|
|
17392
17942
|
}
|
|
17393
17943
|
}
|
|
@@ -17399,7 +17949,7 @@ function writeShadowExcludes(shadowDir, ignorePaths) {
|
|
|
17399
17949
|
const hardcoded = [".git", ".node9"];
|
|
17400
17950
|
const lines = [...hardcoded, ...ignorePaths].join("\n");
|
|
17401
17951
|
try {
|
|
17402
|
-
|
|
17952
|
+
fs32.writeFileSync(path33.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
|
|
17403
17953
|
} catch {
|
|
17404
17954
|
}
|
|
17405
17955
|
}
|
|
@@ -17412,25 +17962,25 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
17412
17962
|
timeout: 3e3
|
|
17413
17963
|
});
|
|
17414
17964
|
if (check.status === 0) {
|
|
17415
|
-
const ptPath =
|
|
17965
|
+
const ptPath = path33.join(shadowDir, "project-path.txt");
|
|
17416
17966
|
try {
|
|
17417
|
-
const stored =
|
|
17967
|
+
const stored = fs32.readFileSync(ptPath, "utf8").trim();
|
|
17418
17968
|
if (stored === normalizedCwd) return true;
|
|
17419
17969
|
if (process.env.NODE9_DEBUG === "1")
|
|
17420
17970
|
console.error(
|
|
17421
17971
|
`[Node9] Shadow repo path mismatch: stored="${stored}" expected="${normalizedCwd}" \u2014 reinitializing`
|
|
17422
17972
|
);
|
|
17423
|
-
|
|
17973
|
+
fs32.rmSync(shadowDir, { recursive: true, force: true });
|
|
17424
17974
|
} catch {
|
|
17425
17975
|
try {
|
|
17426
|
-
|
|
17976
|
+
fs32.writeFileSync(ptPath, normalizedCwd, "utf8");
|
|
17427
17977
|
} catch {
|
|
17428
17978
|
}
|
|
17429
17979
|
return true;
|
|
17430
17980
|
}
|
|
17431
17981
|
}
|
|
17432
17982
|
try {
|
|
17433
|
-
|
|
17983
|
+
fs32.mkdirSync(shadowDir, { recursive: true });
|
|
17434
17984
|
} catch {
|
|
17435
17985
|
}
|
|
17436
17986
|
const init = spawnSync3("git", ["init", "--bare", shadowDir], { timeout: 5e3 });
|
|
@@ -17439,7 +17989,7 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
17439
17989
|
if (process.env.NODE9_DEBUG === "1") console.error("[Node9] git init --bare failed:", reason);
|
|
17440
17990
|
return false;
|
|
17441
17991
|
}
|
|
17442
|
-
const configFile =
|
|
17992
|
+
const configFile = path33.join(shadowDir, "config");
|
|
17443
17993
|
spawnSync3("git", ["config", "--file", configFile, "core.untrackedCache", "true"], {
|
|
17444
17994
|
timeout: 3e3
|
|
17445
17995
|
});
|
|
@@ -17447,7 +17997,7 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
17447
17997
|
timeout: 3e3
|
|
17448
17998
|
});
|
|
17449
17999
|
try {
|
|
17450
|
-
|
|
18000
|
+
fs32.writeFileSync(path33.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
|
|
17451
18001
|
} catch {
|
|
17452
18002
|
}
|
|
17453
18003
|
return true;
|
|
@@ -17470,12 +18020,12 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
17470
18020
|
let indexFile = null;
|
|
17471
18021
|
try {
|
|
17472
18022
|
const rawFilePath = extractFilePath(args);
|
|
17473
|
-
const absFilePath = rawFilePath &&
|
|
18023
|
+
const absFilePath = rawFilePath && path33.isAbsolute(rawFilePath) ? rawFilePath : null;
|
|
17474
18024
|
const cwd = absFilePath ? findProjectRoot(absFilePath) : process.cwd();
|
|
17475
18025
|
const shadowDir = getShadowRepoDir(cwd);
|
|
17476
18026
|
if (!ensureShadowRepo(shadowDir, cwd)) return null;
|
|
17477
18027
|
writeShadowExcludes(shadowDir, ignorePaths);
|
|
17478
|
-
indexFile =
|
|
18028
|
+
indexFile = path33.join(shadowDir, `index_${process.pid}_${Date.now()}`);
|
|
17479
18029
|
const shadowEnv = {
|
|
17480
18030
|
...process.env,
|
|
17481
18031
|
GIT_DIR: shadowDir,
|
|
@@ -17547,7 +18097,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
17547
18097
|
writeStack(stack);
|
|
17548
18098
|
const entry = stack[stack.length - 1];
|
|
17549
18099
|
notifySnapshotTaken(commitHash.slice(0, 7), tool, entry.argsSummary, capturedFiles.length);
|
|
17550
|
-
|
|
18100
|
+
fs32.writeFileSync(UNDO_LATEST_PATH, commitHash);
|
|
17551
18101
|
if (shouldGc) {
|
|
17552
18102
|
spawn4("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
|
|
17553
18103
|
}
|
|
@@ -17558,7 +18108,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
17558
18108
|
} finally {
|
|
17559
18109
|
if (indexFile) {
|
|
17560
18110
|
try {
|
|
17561
|
-
|
|
18111
|
+
fs32.unlinkSync(indexFile);
|
|
17562
18112
|
} catch {
|
|
17563
18113
|
}
|
|
17564
18114
|
}
|
|
@@ -17634,9 +18184,9 @@ function applyUndo(hash, cwd) {
|
|
|
17634
18184
|
timeout: GIT_TIMEOUT
|
|
17635
18185
|
}).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
|
|
17636
18186
|
for (const file of [...tracked, ...untracked]) {
|
|
17637
|
-
const fullPath =
|
|
17638
|
-
if (!snapshotFiles.has(file) &&
|
|
17639
|
-
|
|
18187
|
+
const fullPath = path33.join(dir, file);
|
|
18188
|
+
if (!snapshotFiles.has(file) && fs32.existsSync(fullPath)) {
|
|
18189
|
+
fs32.unlinkSync(fullPath);
|
|
17640
18190
|
}
|
|
17641
18191
|
}
|
|
17642
18192
|
return true;
|
|
@@ -17646,12 +18196,12 @@ function applyUndo(hash, cwd) {
|
|
|
17646
18196
|
}
|
|
17647
18197
|
|
|
17648
18198
|
// src/skill-pin.ts
|
|
17649
|
-
import
|
|
17650
|
-
import
|
|
17651
|
-
import
|
|
18199
|
+
import fs33 from "fs";
|
|
18200
|
+
import path34 from "path";
|
|
18201
|
+
import os29 from "os";
|
|
17652
18202
|
import crypto7 from "crypto";
|
|
17653
18203
|
function getPinsFilePath2() {
|
|
17654
|
-
return
|
|
18204
|
+
return path34.join(os29.homedir(), ".node9", "skill-pins.json");
|
|
17655
18205
|
}
|
|
17656
18206
|
var MAX_FILES = 5e3;
|
|
17657
18207
|
var MAX_TOTAL_BYTES = 50 * 1024 * 1024;
|
|
@@ -17665,18 +18215,18 @@ function walkDir(root) {
|
|
|
17665
18215
|
if (out.length >= MAX_FILES) return;
|
|
17666
18216
|
let entries;
|
|
17667
18217
|
try {
|
|
17668
|
-
entries =
|
|
18218
|
+
entries = fs33.readdirSync(dir, { withFileTypes: true });
|
|
17669
18219
|
} catch {
|
|
17670
18220
|
return;
|
|
17671
18221
|
}
|
|
17672
18222
|
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
17673
18223
|
for (const entry of entries) {
|
|
17674
18224
|
if (out.length >= MAX_FILES) return;
|
|
17675
|
-
const full =
|
|
17676
|
-
const rel = relDir ?
|
|
18225
|
+
const full = path34.join(dir, entry.name);
|
|
18226
|
+
const rel = relDir ? path34.posix.join(relDir, entry.name) : entry.name;
|
|
17677
18227
|
let lst;
|
|
17678
18228
|
try {
|
|
17679
|
-
lst =
|
|
18229
|
+
lst = fs33.lstatSync(full);
|
|
17680
18230
|
} catch {
|
|
17681
18231
|
continue;
|
|
17682
18232
|
}
|
|
@@ -17688,7 +18238,7 @@ function walkDir(root) {
|
|
|
17688
18238
|
if (!lst.isFile()) continue;
|
|
17689
18239
|
if (totalBytes + lst.size > MAX_TOTAL_BYTES) continue;
|
|
17690
18240
|
try {
|
|
17691
|
-
const buf =
|
|
18241
|
+
const buf = fs33.readFileSync(full);
|
|
17692
18242
|
totalBytes += buf.length;
|
|
17693
18243
|
out.push({ rel, hash: sha256Bytes(buf) });
|
|
17694
18244
|
} catch {
|
|
@@ -17702,14 +18252,14 @@ function walkDir(root) {
|
|
|
17702
18252
|
function hashSkillRoot(absPath) {
|
|
17703
18253
|
let lst;
|
|
17704
18254
|
try {
|
|
17705
|
-
lst =
|
|
18255
|
+
lst = fs33.lstatSync(absPath);
|
|
17706
18256
|
} catch {
|
|
17707
18257
|
return { exists: false, contentHash: "", fileCount: 0 };
|
|
17708
18258
|
}
|
|
17709
18259
|
if (lst.isSymbolicLink()) return { exists: false, contentHash: "", fileCount: 0 };
|
|
17710
18260
|
if (lst.isFile()) {
|
|
17711
18261
|
try {
|
|
17712
|
-
return { exists: true, contentHash: sha256Bytes(
|
|
18262
|
+
return { exists: true, contentHash: sha256Bytes(fs33.readFileSync(absPath)), fileCount: 1 };
|
|
17713
18263
|
} catch {
|
|
17714
18264
|
return { exists: false, contentHash: "", fileCount: 0 };
|
|
17715
18265
|
}
|
|
@@ -17727,7 +18277,7 @@ function getRootKey(absPath) {
|
|
|
17727
18277
|
function readSkillPinsSafe() {
|
|
17728
18278
|
const filePath = getPinsFilePath2();
|
|
17729
18279
|
try {
|
|
17730
|
-
const raw =
|
|
18280
|
+
const raw = fs33.readFileSync(filePath, "utf-8");
|
|
17731
18281
|
if (!raw.trim()) return { ok: false, reason: "corrupt", detail: "empty file" };
|
|
17732
18282
|
const parsed = JSON.parse(raw);
|
|
17733
18283
|
if (!parsed.roots || typeof parsed.roots !== "object" || Array.isArray(parsed.roots)) {
|
|
@@ -17747,10 +18297,10 @@ function readSkillPins() {
|
|
|
17747
18297
|
}
|
|
17748
18298
|
function writeSkillPins(data) {
|
|
17749
18299
|
const filePath = getPinsFilePath2();
|
|
17750
|
-
|
|
18300
|
+
fs33.mkdirSync(path34.dirname(filePath), { recursive: true });
|
|
17751
18301
|
const tmp = `${filePath}.${crypto7.randomBytes(6).toString("hex")}.tmp`;
|
|
17752
|
-
|
|
17753
|
-
|
|
18302
|
+
fs33.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
18303
|
+
fs33.renameSync(tmp, filePath);
|
|
17754
18304
|
}
|
|
17755
18305
|
function removePin2(rootKey) {
|
|
17756
18306
|
const pins = readSkillPins();
|
|
@@ -17794,36 +18344,36 @@ function verifyAndPinRoots(roots) {
|
|
|
17794
18344
|
return { kind: "verified" };
|
|
17795
18345
|
}
|
|
17796
18346
|
function defaultSkillRoots(_cwd) {
|
|
17797
|
-
const marketplaces =
|
|
18347
|
+
const marketplaces = path34.join(os29.homedir(), ".claude", "plugins", "marketplaces");
|
|
17798
18348
|
const roots = [];
|
|
17799
18349
|
let registries;
|
|
17800
18350
|
try {
|
|
17801
|
-
registries =
|
|
18351
|
+
registries = fs33.readdirSync(marketplaces, { withFileTypes: true });
|
|
17802
18352
|
} catch {
|
|
17803
18353
|
return [];
|
|
17804
18354
|
}
|
|
17805
18355
|
for (const registry of registries) {
|
|
17806
18356
|
if (!registry.isDirectory()) continue;
|
|
17807
|
-
const pluginsDir =
|
|
18357
|
+
const pluginsDir = path34.join(marketplaces, registry.name, "plugins");
|
|
17808
18358
|
let plugins;
|
|
17809
18359
|
try {
|
|
17810
|
-
plugins =
|
|
18360
|
+
plugins = fs33.readdirSync(pluginsDir, { withFileTypes: true });
|
|
17811
18361
|
} catch {
|
|
17812
18362
|
continue;
|
|
17813
18363
|
}
|
|
17814
18364
|
for (const plugin of plugins) {
|
|
17815
18365
|
if (!plugin.isDirectory()) continue;
|
|
17816
|
-
roots.push(
|
|
18366
|
+
roots.push(path34.join(pluginsDir, plugin.name));
|
|
17817
18367
|
}
|
|
17818
18368
|
}
|
|
17819
18369
|
return roots;
|
|
17820
18370
|
}
|
|
17821
18371
|
function resolveUserSkillRoot(entry, cwd) {
|
|
17822
18372
|
if (!entry) return null;
|
|
17823
|
-
if (entry.startsWith("~/") || entry === "~") return
|
|
17824
|
-
if (
|
|
17825
|
-
if (!cwd || !
|
|
17826
|
-
return
|
|
18373
|
+
if (entry.startsWith("~/") || entry === "~") return path34.join(os29.homedir(), entry.slice(1));
|
|
18374
|
+
if (path34.isAbsolute(entry)) return entry;
|
|
18375
|
+
if (!cwd || !path34.isAbsolute(cwd)) return null;
|
|
18376
|
+
return path34.join(cwd, entry);
|
|
17827
18377
|
}
|
|
17828
18378
|
|
|
17829
18379
|
// src/cli/commands/check.ts
|
|
@@ -17892,9 +18442,9 @@ function registerCheckCommand(program2) {
|
|
|
17892
18442
|
} catch (err2) {
|
|
17893
18443
|
const tempConfig = getConfig();
|
|
17894
18444
|
if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
|
|
17895
|
-
const logPath =
|
|
18445
|
+
const logPath = path35.join(os30.homedir(), ".node9", "hook-debug.log");
|
|
17896
18446
|
const errMsg = err2 instanceof Error ? err2.message : String(err2);
|
|
17897
|
-
|
|
18447
|
+
fs34.appendFileSync(
|
|
17898
18448
|
logPath,
|
|
17899
18449
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
|
|
17900
18450
|
RAW: ${raw}
|
|
@@ -17907,14 +18457,14 @@ RAW: ${raw}
|
|
|
17907
18457
|
const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
|
|
17908
18458
|
if (process.env.NODE9_DEBUG === "1") {
|
|
17909
18459
|
try {
|
|
17910
|
-
const logPath =
|
|
17911
|
-
if (!
|
|
17912
|
-
|
|
18460
|
+
const logPath = path35.join(os30.homedir(), ".node9", "hook-debug.log");
|
|
18461
|
+
if (!fs34.existsSync(path35.dirname(logPath)))
|
|
18462
|
+
fs34.mkdirSync(path35.dirname(logPath), { recursive: true });
|
|
17913
18463
|
const sanitized = JSON.stringify({
|
|
17914
18464
|
...payload,
|
|
17915
18465
|
prompt: `<redacted, ${prompt.length} bytes>`
|
|
17916
18466
|
});
|
|
17917
|
-
|
|
18467
|
+
fs34.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${sanitized}
|
|
17918
18468
|
`);
|
|
17919
18469
|
} catch {
|
|
17920
18470
|
}
|
|
@@ -17934,8 +18484,8 @@ RAW: ${raw}
|
|
|
17934
18484
|
);
|
|
17935
18485
|
const reason = `\u{1F6A8} Node9 DLP: ${dlpMatch.patternName} detected in prompt (${dlpMatch.redactedSample}). Prompt was not submitted \u2014 remove the credential and try again.`;
|
|
17936
18486
|
try {
|
|
17937
|
-
const ttyFd =
|
|
17938
|
-
|
|
18487
|
+
const ttyFd = fs34.openSync("/dev/tty", "w");
|
|
18488
|
+
fs34.writeSync(
|
|
17939
18489
|
ttyFd,
|
|
17940
18490
|
chalk9.bgRed.white.bold(`
|
|
17941
18491
|
\u{1F6A8} NODE9 DLP \u2014 PROMPT BLOCKED
|
|
@@ -17945,7 +18495,7 @@ RAW: ${raw}
|
|
|
17945
18495
|
|
|
17946
18496
|
`)
|
|
17947
18497
|
);
|
|
17948
|
-
|
|
18498
|
+
fs34.closeSync(ttyFd);
|
|
17949
18499
|
} catch {
|
|
17950
18500
|
}
|
|
17951
18501
|
const isCodex = agent2 === "Codex";
|
|
@@ -17964,16 +18514,16 @@ RAW: ${raw}
|
|
|
17964
18514
|
process.exit(2);
|
|
17965
18515
|
}
|
|
17966
18516
|
const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
|
|
17967
|
-
const safeCwdForConfig = typeof payloadCwd === "string" &&
|
|
18517
|
+
const safeCwdForConfig = typeof payloadCwd === "string" && path35.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
17968
18518
|
const config = getConfig(safeCwdForConfig);
|
|
17969
18519
|
if (config.settings.autoStartDaemon && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON) {
|
|
17970
18520
|
try {
|
|
17971
18521
|
const scriptPath = process.argv[1];
|
|
17972
|
-
if (typeof scriptPath !== "string" || !
|
|
18522
|
+
if (typeof scriptPath !== "string" || !path35.isAbsolute(scriptPath))
|
|
17973
18523
|
throw new Error("node9: argv[1] is not an absolute path");
|
|
17974
|
-
const resolvedScript =
|
|
17975
|
-
const packageDist =
|
|
17976
|
-
if (!resolvedScript.startsWith(packageDist +
|
|
18524
|
+
const resolvedScript = fs34.realpathSync(scriptPath);
|
|
18525
|
+
const packageDist = fs34.realpathSync(path35.resolve(__dirname, "../.."));
|
|
18526
|
+
if (!resolvedScript.startsWith(packageDist + path35.sep) && resolvedScript !== packageDist)
|
|
17977
18527
|
throw new Error(
|
|
17978
18528
|
`node9: daemon spawn aborted \u2014 argv[1] (${resolvedScript}) is outside package dist (${packageDist})`
|
|
17979
18529
|
);
|
|
@@ -17995,10 +18545,10 @@ RAW: ${raw}
|
|
|
17995
18545
|
});
|
|
17996
18546
|
d.unref();
|
|
17997
18547
|
} catch (spawnErr) {
|
|
17998
|
-
const logPath =
|
|
18548
|
+
const logPath = path35.join(os30.homedir(), ".node9", "hook-debug.log");
|
|
17999
18549
|
const msg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);
|
|
18000
18550
|
try {
|
|
18001
|
-
|
|
18551
|
+
fs34.appendFileSync(
|
|
18002
18552
|
logPath,
|
|
18003
18553
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-autostart-failed: ${msg}
|
|
18004
18554
|
`
|
|
@@ -18008,10 +18558,10 @@ RAW: ${raw}
|
|
|
18008
18558
|
}
|
|
18009
18559
|
}
|
|
18010
18560
|
if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
|
|
18011
|
-
const logPath =
|
|
18012
|
-
if (!
|
|
18013
|
-
|
|
18014
|
-
|
|
18561
|
+
const logPath = path35.join(os30.homedir(), ".node9", "hook-debug.log");
|
|
18562
|
+
if (!fs34.existsSync(path35.dirname(logPath)))
|
|
18563
|
+
fs34.mkdirSync(path35.dirname(logPath), { recursive: true });
|
|
18564
|
+
fs34.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
|
|
18015
18565
|
`);
|
|
18016
18566
|
}
|
|
18017
18567
|
const rawToolName = sanitize2(extractToolName(payload));
|
|
@@ -18025,8 +18575,8 @@ RAW: ${raw}
|
|
|
18025
18575
|
const isHumanDecision = blockedByContext.toLowerCase().includes("user") || blockedByContext.toLowerCase().includes("daemon") || blockedByContext.toLowerCase().includes("decision");
|
|
18026
18576
|
let ttyFd = null;
|
|
18027
18577
|
try {
|
|
18028
|
-
ttyFd =
|
|
18029
|
-
const writeTty = (line) =>
|
|
18578
|
+
ttyFd = fs34.openSync("/dev/tty", "w");
|
|
18579
|
+
const writeTty = (line) => fs34.writeSync(ttyFd, line + "\n");
|
|
18030
18580
|
if (blockedByContext.includes("DLP") || blockedByContext.includes("Secret Detected") || blockedByContext.includes("Credential Review")) {
|
|
18031
18581
|
writeTty(chalk9.bgRed.white.bold(`
|
|
18032
18582
|
\u{1F6A8} NODE9 DLP ALERT \u2014 CREDENTIAL DETECTED `));
|
|
@@ -18045,7 +18595,7 @@ RAW: ${raw}
|
|
|
18045
18595
|
} finally {
|
|
18046
18596
|
if (ttyFd !== null)
|
|
18047
18597
|
try {
|
|
18048
|
-
|
|
18598
|
+
fs34.closeSync(ttyFd);
|
|
18049
18599
|
} catch {
|
|
18050
18600
|
}
|
|
18051
18601
|
}
|
|
@@ -18096,17 +18646,17 @@ RAW: ${raw}
|
|
|
18096
18646
|
const safeSessionId = /^[A-Za-z0-9_\-]{1,128}$/.test(rawSessionId) ? rawSessionId : "";
|
|
18097
18647
|
if (skillPinCfg.enabled && safeSessionId) {
|
|
18098
18648
|
try {
|
|
18099
|
-
const sessionsDir =
|
|
18100
|
-
const flagPath =
|
|
18649
|
+
const sessionsDir = path35.join(os30.homedir(), ".node9", "skill-sessions");
|
|
18650
|
+
const flagPath = path35.join(sessionsDir, `${safeSessionId}.json`);
|
|
18101
18651
|
let flag = null;
|
|
18102
18652
|
try {
|
|
18103
|
-
flag = JSON.parse(
|
|
18653
|
+
flag = JSON.parse(fs34.readFileSync(flagPath, "utf-8"));
|
|
18104
18654
|
} catch {
|
|
18105
18655
|
}
|
|
18106
18656
|
const writeFlag = (data2) => {
|
|
18107
18657
|
try {
|
|
18108
|
-
|
|
18109
|
-
|
|
18658
|
+
fs34.mkdirSync(sessionsDir, { recursive: true });
|
|
18659
|
+
fs34.writeFileSync(
|
|
18110
18660
|
flagPath,
|
|
18111
18661
|
JSON.stringify({ ...data2, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
|
|
18112
18662
|
{ mode: 384 }
|
|
@@ -18117,8 +18667,8 @@ RAW: ${raw}
|
|
|
18117
18667
|
const sendSkillWarn = (detail, recoveryCmd) => {
|
|
18118
18668
|
let ttyFd = null;
|
|
18119
18669
|
try {
|
|
18120
|
-
ttyFd =
|
|
18121
|
-
const w = (line) =>
|
|
18670
|
+
ttyFd = fs34.openSync("/dev/tty", "w");
|
|
18671
|
+
const w = (line) => fs34.writeSync(ttyFd, line + "\n");
|
|
18122
18672
|
w(chalk9.yellow(`
|
|
18123
18673
|
\u26A0\uFE0F Node9: installed skill drift detected`));
|
|
18124
18674
|
w(chalk9.gray(` ${detail}`));
|
|
@@ -18133,7 +18683,7 @@ RAW: ${raw}
|
|
|
18133
18683
|
} finally {
|
|
18134
18684
|
if (ttyFd !== null)
|
|
18135
18685
|
try {
|
|
18136
|
-
|
|
18686
|
+
fs34.closeSync(ttyFd);
|
|
18137
18687
|
} catch {
|
|
18138
18688
|
}
|
|
18139
18689
|
}
|
|
@@ -18149,7 +18699,7 @@ RAW: ${raw}
|
|
|
18149
18699
|
return;
|
|
18150
18700
|
}
|
|
18151
18701
|
if (!flag || flag.state !== "verified" && flag.state !== "warned") {
|
|
18152
|
-
const absoluteCwd = typeof payloadCwd === "string" &&
|
|
18702
|
+
const absoluteCwd = typeof payloadCwd === "string" && path35.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
18153
18703
|
const extraRoots = skillPinCfg.roots;
|
|
18154
18704
|
const resolvedExtra = extraRoots.map((r) => resolveUserSkillRoot(r, absoluteCwd)).filter((r) => typeof r === "string");
|
|
18155
18705
|
const roots = [...defaultSkillRoots(absoluteCwd), ...resolvedExtra];
|
|
@@ -18190,10 +18740,10 @@ RAW: ${raw}
|
|
|
18190
18740
|
}
|
|
18191
18741
|
try {
|
|
18192
18742
|
const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1e3;
|
|
18193
|
-
for (const name of
|
|
18194
|
-
const p =
|
|
18743
|
+
for (const name of fs34.readdirSync(sessionsDir)) {
|
|
18744
|
+
const p = path35.join(sessionsDir, name);
|
|
18195
18745
|
try {
|
|
18196
|
-
if (
|
|
18746
|
+
if (fs34.statSync(p).mtimeMs < cutoff) fs34.unlinkSync(p);
|
|
18197
18747
|
} catch {
|
|
18198
18748
|
}
|
|
18199
18749
|
}
|
|
@@ -18203,9 +18753,9 @@ RAW: ${raw}
|
|
|
18203
18753
|
} catch (err2) {
|
|
18204
18754
|
if (process.env.NODE9_DEBUG === "1") {
|
|
18205
18755
|
try {
|
|
18206
|
-
const dbg =
|
|
18756
|
+
const dbg = path35.join(os30.homedir(), ".node9", "hook-debug.log");
|
|
18207
18757
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
18208
|
-
|
|
18758
|
+
fs34.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
|
|
18209
18759
|
`);
|
|
18210
18760
|
} catch {
|
|
18211
18761
|
}
|
|
@@ -18215,7 +18765,7 @@ RAW: ${raw}
|
|
|
18215
18765
|
if (shouldSnapshot(toolName, toolInput, config)) {
|
|
18216
18766
|
await createShadowSnapshot(toolName, toolInput, config.policy.snapshot.ignorePaths);
|
|
18217
18767
|
}
|
|
18218
|
-
const safeCwdForAuth = typeof payloadCwd === "string" &&
|
|
18768
|
+
const safeCwdForAuth = typeof payloadCwd === "string" && path35.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
18219
18769
|
const result = await authorizeHeadless(toolName, toolInput, meta, {
|
|
18220
18770
|
cwd: safeCwdForAuth
|
|
18221
18771
|
});
|
|
@@ -18227,12 +18777,12 @@ RAW: ${raw}
|
|
|
18227
18777
|
}
|
|
18228
18778
|
if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && !process.stdout.isTTY && config.settings.autoStartDaemon) {
|
|
18229
18779
|
try {
|
|
18230
|
-
const tty =
|
|
18231
|
-
|
|
18780
|
+
const tty = fs34.openSync("/dev/tty", "w");
|
|
18781
|
+
fs34.writeSync(
|
|
18232
18782
|
tty,
|
|
18233
18783
|
chalk9.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically...\n")
|
|
18234
18784
|
);
|
|
18235
|
-
|
|
18785
|
+
fs34.closeSync(tty);
|
|
18236
18786
|
} catch {
|
|
18237
18787
|
}
|
|
18238
18788
|
const daemonReady = await autoStartDaemonAndWait();
|
|
@@ -18259,9 +18809,9 @@ RAW: ${raw}
|
|
|
18259
18809
|
});
|
|
18260
18810
|
} catch (err2) {
|
|
18261
18811
|
if (process.env.NODE9_DEBUG === "1") {
|
|
18262
|
-
const logPath =
|
|
18812
|
+
const logPath = path35.join(os30.homedir(), ".node9", "hook-debug.log");
|
|
18263
18813
|
const errMsg = err2 instanceof Error ? err2.message : String(err2);
|
|
18264
|
-
|
|
18814
|
+
fs34.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
|
|
18265
18815
|
`);
|
|
18266
18816
|
}
|
|
18267
18817
|
process.exit(0);
|
|
@@ -18297,9 +18847,9 @@ RAW: ${raw}
|
|
|
18297
18847
|
// src/cli/commands/log.ts
|
|
18298
18848
|
init_audit();
|
|
18299
18849
|
init_config();
|
|
18300
|
-
import
|
|
18301
|
-
import
|
|
18302
|
-
import
|
|
18850
|
+
import fs35 from "fs";
|
|
18851
|
+
import path36 from "path";
|
|
18852
|
+
import os31 from "os";
|
|
18303
18853
|
init_daemon();
|
|
18304
18854
|
|
|
18305
18855
|
// src/utils/cp-mv-parser.ts
|
|
@@ -18392,10 +18942,10 @@ function registerLogCommand(program2) {
|
|
|
18392
18942
|
if (rawToolName !== tool) entry.agentToolName = rawToolName;
|
|
18393
18943
|
const payloadSessionId = payload.session_id ?? payload.conversationId;
|
|
18394
18944
|
if (payloadSessionId) entry.sessionId = payloadSessionId;
|
|
18395
|
-
const logPath =
|
|
18396
|
-
if (!
|
|
18397
|
-
|
|
18398
|
-
|
|
18945
|
+
const logPath = path36.join(os31.homedir(), ".node9", "audit.log");
|
|
18946
|
+
if (!fs35.existsSync(path36.dirname(logPath)))
|
|
18947
|
+
fs35.mkdirSync(path36.dirname(logPath), { recursive: true });
|
|
18948
|
+
fs35.appendFileSync(logPath, JSON.stringify(entry) + "\n");
|
|
18399
18949
|
if ((tool === "Bash" || tool === "bash") && isDaemonRunning()) {
|
|
18400
18950
|
const command = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
|
|
18401
18951
|
if (command) {
|
|
@@ -18429,7 +18979,7 @@ function registerLogCommand(program2) {
|
|
|
18429
18979
|
}
|
|
18430
18980
|
}
|
|
18431
18981
|
const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
|
|
18432
|
-
const safeCwd = typeof payloadCwd === "string" &&
|
|
18982
|
+
const safeCwd = typeof payloadCwd === "string" && path36.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
18433
18983
|
const config = getConfig(safeCwd);
|
|
18434
18984
|
if ((tool === "Bash" || tool === "bash") && config.settings.enableUndo !== false) {
|
|
18435
18985
|
const bashCommand = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
|
|
@@ -18450,9 +19000,9 @@ function registerLogCommand(program2) {
|
|
|
18450
19000
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
18451
19001
|
process.stderr.write(`[Node9] audit log error: ${msg}
|
|
18452
19002
|
`);
|
|
18453
|
-
const debugPath =
|
|
19003
|
+
const debugPath = path36.join(os31.homedir(), ".node9", "hook-debug.log");
|
|
18454
19004
|
try {
|
|
18455
|
-
|
|
19005
|
+
fs35.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
|
|
18456
19006
|
`);
|
|
18457
19007
|
} catch {
|
|
18458
19008
|
}
|
|
@@ -18854,13 +19404,13 @@ function registerConfigShowCommand(program2) {
|
|
|
18854
19404
|
init_daemon();
|
|
18855
19405
|
init_config();
|
|
18856
19406
|
import chalk11 from "chalk";
|
|
18857
|
-
import
|
|
18858
|
-
import
|
|
18859
|
-
import
|
|
19407
|
+
import fs36 from "fs";
|
|
19408
|
+
import path37 from "path";
|
|
19409
|
+
import os32 from "os";
|
|
18860
19410
|
import { execSync } from "child_process";
|
|
18861
19411
|
function registerDoctorCommand(program2, version2) {
|
|
18862
19412
|
program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(async () => {
|
|
18863
|
-
const homeDir2 =
|
|
19413
|
+
const homeDir2 = os32.homedir();
|
|
18864
19414
|
let failures = 0;
|
|
18865
19415
|
function pass(msg) {
|
|
18866
19416
|
console.log(chalk11.green(" \u2705 ") + msg);
|
|
@@ -18909,10 +19459,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
18909
19459
|
);
|
|
18910
19460
|
}
|
|
18911
19461
|
section("Configuration");
|
|
18912
|
-
const globalConfigPath =
|
|
18913
|
-
if (
|
|
19462
|
+
const globalConfigPath = path37.join(homeDir2, ".node9", "config.json");
|
|
19463
|
+
if (fs36.existsSync(globalConfigPath)) {
|
|
18914
19464
|
try {
|
|
18915
|
-
JSON.parse(
|
|
19465
|
+
JSON.parse(fs36.readFileSync(globalConfigPath, "utf-8"));
|
|
18916
19466
|
pass("~/.node9/config.json found and valid");
|
|
18917
19467
|
} catch {
|
|
18918
19468
|
fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
|
|
@@ -18920,10 +19470,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
18920
19470
|
} else {
|
|
18921
19471
|
warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
|
|
18922
19472
|
}
|
|
18923
|
-
const projectConfigPath =
|
|
18924
|
-
if (
|
|
19473
|
+
const projectConfigPath = path37.join(process.cwd(), "node9.config.json");
|
|
19474
|
+
if (fs36.existsSync(projectConfigPath)) {
|
|
18925
19475
|
try {
|
|
18926
|
-
JSON.parse(
|
|
19476
|
+
JSON.parse(fs36.readFileSync(projectConfigPath, "utf-8"));
|
|
18927
19477
|
pass("node9.config.json found and valid (project)");
|
|
18928
19478
|
} catch {
|
|
18929
19479
|
fail(
|
|
@@ -18932,8 +19482,8 @@ function registerDoctorCommand(program2, version2) {
|
|
|
18932
19482
|
);
|
|
18933
19483
|
}
|
|
18934
19484
|
}
|
|
18935
|
-
const credsPath =
|
|
18936
|
-
if (
|
|
19485
|
+
const credsPath = path37.join(homeDir2, ".node9", "credentials.json");
|
|
19486
|
+
if (fs36.existsSync(credsPath)) {
|
|
18937
19487
|
pass("Cloud credentials found (~/.node9/credentials.json)");
|
|
18938
19488
|
} else {
|
|
18939
19489
|
warn(
|
|
@@ -18942,10 +19492,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
18942
19492
|
);
|
|
18943
19493
|
}
|
|
18944
19494
|
section("Agent Hooks");
|
|
18945
|
-
const claudeSettingsPath =
|
|
18946
|
-
if (
|
|
19495
|
+
const claudeSettingsPath = path37.join(homeDir2, ".claude", "settings.json");
|
|
19496
|
+
if (fs36.existsSync(claudeSettingsPath)) {
|
|
18947
19497
|
try {
|
|
18948
|
-
const cs = JSON.parse(
|
|
19498
|
+
const cs = JSON.parse(fs36.readFileSync(claudeSettingsPath, "utf-8"));
|
|
18949
19499
|
const hasHook = cs.hooks?.PreToolUse?.some(
|
|
18950
19500
|
(m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
|
|
18951
19501
|
);
|
|
@@ -18961,10 +19511,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
18961
19511
|
} else {
|
|
18962
19512
|
warn("Claude Code \u2014 not configured", "Run: node9 setup claude");
|
|
18963
19513
|
}
|
|
18964
|
-
const geminiSettingsPath =
|
|
18965
|
-
if (
|
|
19514
|
+
const geminiSettingsPath = path37.join(homeDir2, ".gemini", "settings.json");
|
|
19515
|
+
if (fs36.existsSync(geminiSettingsPath)) {
|
|
18966
19516
|
try {
|
|
18967
|
-
const gs = JSON.parse(
|
|
19517
|
+
const gs = JSON.parse(fs36.readFileSync(geminiSettingsPath, "utf-8"));
|
|
18968
19518
|
const hasHook = gs.hooks?.BeforeTool?.some(
|
|
18969
19519
|
(m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
|
|
18970
19520
|
);
|
|
@@ -18980,10 +19530,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
18980
19530
|
} else {
|
|
18981
19531
|
warn("Gemini CLI \u2014 not configured", "Run: node9 setup gemini (skip if not using Gemini)");
|
|
18982
19532
|
}
|
|
18983
|
-
const cursorHooksPath =
|
|
18984
|
-
if (
|
|
19533
|
+
const cursorHooksPath = path37.join(homeDir2, ".cursor", "hooks.json");
|
|
19534
|
+
if (fs36.existsSync(cursorHooksPath)) {
|
|
18985
19535
|
try {
|
|
18986
|
-
const cur = JSON.parse(
|
|
19536
|
+
const cur = JSON.parse(fs36.readFileSync(cursorHooksPath, "utf-8"));
|
|
18987
19537
|
const hasHook = cur.hooks?.preToolUse?.some(
|
|
18988
19538
|
(h) => h.command?.includes("node9") || h.command?.includes("cli.js")
|
|
18989
19539
|
);
|
|
@@ -19014,7 +19564,7 @@ function registerDoctorCommand(program2, version2) {
|
|
|
19014
19564
|
try {
|
|
19015
19565
|
const { shipLagBytes: shipLagBytes2, readWatermark: readWatermark2, AUDIT_SHIP_WATERMARK: AUDIT_SHIP_WATERMARK2 } = await Promise.resolve().then(() => (init_audit_shipper(), audit_shipper_exports));
|
|
19016
19566
|
const cfg = getConfig();
|
|
19017
|
-
const creds =
|
|
19567
|
+
const creds = fs36.existsSync(path37.join(os32.homedir(), ".node9", "credentials.json"));
|
|
19018
19568
|
if (!creds) {
|
|
19019
19569
|
warn("Not logged in \u2014 audit rows stay local", "Run: node9 login <api-key>");
|
|
19020
19570
|
} else if (!cfg.settings.approvers.cloud) {
|
|
@@ -19064,9 +19614,9 @@ function registerDoctorCommand(program2, version2) {
|
|
|
19064
19614
|
|
|
19065
19615
|
// src/cli/commands/audit.ts
|
|
19066
19616
|
import chalk12 from "chalk";
|
|
19067
|
-
import
|
|
19068
|
-
import
|
|
19069
|
-
import
|
|
19617
|
+
import fs37 from "fs";
|
|
19618
|
+
import path38 from "path";
|
|
19619
|
+
import os33 from "os";
|
|
19070
19620
|
function formatRelativeTime(timestamp) {
|
|
19071
19621
|
const diff = Date.now() - new Date(timestamp).getTime();
|
|
19072
19622
|
const sec = Math.floor(diff / 1e3);
|
|
@@ -19079,14 +19629,14 @@ function formatRelativeTime(timestamp) {
|
|
|
19079
19629
|
}
|
|
19080
19630
|
function registerAuditCommand(program2) {
|
|
19081
19631
|
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) => {
|
|
19082
|
-
const logPath =
|
|
19083
|
-
if (!
|
|
19632
|
+
const logPath = path38.join(os33.homedir(), ".node9", "audit.log");
|
|
19633
|
+
if (!fs37.existsSync(logPath)) {
|
|
19084
19634
|
console.log(
|
|
19085
19635
|
chalk12.yellow("No audit logs found. Run node9 with an agent to generate entries.")
|
|
19086
19636
|
);
|
|
19087
19637
|
return;
|
|
19088
19638
|
}
|
|
19089
|
-
const raw =
|
|
19639
|
+
const raw = fs37.readFileSync(logPath, "utf-8");
|
|
19090
19640
|
const lines = raw.split("\n").filter((l) => l.trim() !== "");
|
|
19091
19641
|
let entries = lines.flatMap((line) => {
|
|
19092
19642
|
try {
|
|
@@ -19144,9 +19694,9 @@ import chalk13 from "chalk";
|
|
|
19144
19694
|
// src/cli/aggregate/report-audit.ts
|
|
19145
19695
|
init_costSync();
|
|
19146
19696
|
init_litellm();
|
|
19147
|
-
import
|
|
19148
|
-
import
|
|
19149
|
-
import
|
|
19697
|
+
import fs38 from "fs";
|
|
19698
|
+
import os34 from "os";
|
|
19699
|
+
import path39 from "path";
|
|
19150
19700
|
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;
|
|
19151
19701
|
function buildTestTimestamps(allEntries) {
|
|
19152
19702
|
const testTs = /* @__PURE__ */ new Set();
|
|
@@ -19226,8 +19776,8 @@ function getDateRange(period, now) {
|
|
|
19226
19776
|
}
|
|
19227
19777
|
}
|
|
19228
19778
|
function parseAuditLog(logPath) {
|
|
19229
|
-
if (!
|
|
19230
|
-
const raw =
|
|
19779
|
+
if (!fs38.existsSync(logPath)) return [];
|
|
19780
|
+
const raw = fs38.readFileSync(logPath, "utf-8");
|
|
19231
19781
|
return raw.split("\n").flatMap((line) => {
|
|
19232
19782
|
if (!line.trim()) return [];
|
|
19233
19783
|
try {
|
|
@@ -19287,25 +19837,25 @@ function freezeClaudeCost(acc) {
|
|
|
19287
19837
|
};
|
|
19288
19838
|
}
|
|
19289
19839
|
function processClaudeCostProject(proj, projectsDir, start, end, acc) {
|
|
19290
|
-
const projPath =
|
|
19840
|
+
const projPath = path39.join(projectsDir, proj);
|
|
19291
19841
|
let files;
|
|
19292
19842
|
try {
|
|
19293
|
-
const stat =
|
|
19843
|
+
const stat = fs38.statSync(projPath);
|
|
19294
19844
|
if (!stat.isDirectory()) return;
|
|
19295
|
-
files =
|
|
19845
|
+
files = fs38.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
19296
19846
|
} catch {
|
|
19297
19847
|
return;
|
|
19298
19848
|
}
|
|
19299
19849
|
const startMs = start.getTime();
|
|
19300
19850
|
for (const file of files) {
|
|
19301
|
-
const filePath =
|
|
19851
|
+
const filePath = path39.join(projPath, file);
|
|
19302
19852
|
try {
|
|
19303
|
-
if (
|
|
19853
|
+
if (fs38.statSync(filePath).mtimeMs < startMs) continue;
|
|
19304
19854
|
} catch {
|
|
19305
19855
|
continue;
|
|
19306
19856
|
}
|
|
19307
19857
|
try {
|
|
19308
|
-
const raw =
|
|
19858
|
+
const raw = fs38.readFileSync(filePath, "utf-8");
|
|
19309
19859
|
for (const line of raw.split("\n")) {
|
|
19310
19860
|
if (!line.trim()) continue;
|
|
19311
19861
|
let entry;
|
|
@@ -19355,10 +19905,10 @@ function processClaudeCostProject(proj, projectsDir, start, end, acc) {
|
|
|
19355
19905
|
}
|
|
19356
19906
|
function loadClaudeCost(start, end, projectsDir) {
|
|
19357
19907
|
const acc = emptyClaudeCostAccumulator();
|
|
19358
|
-
if (!
|
|
19908
|
+
if (!fs38.existsSync(projectsDir)) return freezeClaudeCost(acc);
|
|
19359
19909
|
let dirs;
|
|
19360
19910
|
try {
|
|
19361
|
-
dirs =
|
|
19911
|
+
dirs = fs38.readdirSync(projectsDir);
|
|
19362
19912
|
} catch {
|
|
19363
19913
|
return freezeClaudeCost(acc);
|
|
19364
19914
|
}
|
|
@@ -19370,7 +19920,7 @@ function loadClaudeCost(start, end, projectsDir) {
|
|
|
19370
19920
|
function processCodexCostFile(filePath, start, end, acc) {
|
|
19371
19921
|
let lines;
|
|
19372
19922
|
try {
|
|
19373
|
-
lines =
|
|
19923
|
+
lines = fs38.readFileSync(filePath, "utf-8").split("\n");
|
|
19374
19924
|
} catch {
|
|
19375
19925
|
return;
|
|
19376
19926
|
}
|
|
@@ -19415,31 +19965,31 @@ function processCodexCostFile(filePath, start, end, acc) {
|
|
|
19415
19965
|
}
|
|
19416
19966
|
function listCodexSessionFiles(sessionsBase) {
|
|
19417
19967
|
const jsonlFiles = [];
|
|
19418
|
-
if (!
|
|
19968
|
+
if (!fs38.existsSync(sessionsBase)) return jsonlFiles;
|
|
19419
19969
|
try {
|
|
19420
|
-
for (const year of
|
|
19421
|
-
const yearPath =
|
|
19970
|
+
for (const year of fs38.readdirSync(sessionsBase)) {
|
|
19971
|
+
const yearPath = path39.join(sessionsBase, year);
|
|
19422
19972
|
try {
|
|
19423
|
-
if (!
|
|
19973
|
+
if (!fs38.statSync(yearPath).isDirectory()) continue;
|
|
19424
19974
|
} catch {
|
|
19425
19975
|
continue;
|
|
19426
19976
|
}
|
|
19427
|
-
for (const month of
|
|
19428
|
-
const monthPath =
|
|
19977
|
+
for (const month of fs38.readdirSync(yearPath)) {
|
|
19978
|
+
const monthPath = path39.join(yearPath, month);
|
|
19429
19979
|
try {
|
|
19430
|
-
if (!
|
|
19980
|
+
if (!fs38.statSync(monthPath).isDirectory()) continue;
|
|
19431
19981
|
} catch {
|
|
19432
19982
|
continue;
|
|
19433
19983
|
}
|
|
19434
|
-
for (const day of
|
|
19435
|
-
const dayPath =
|
|
19984
|
+
for (const day of fs38.readdirSync(monthPath)) {
|
|
19985
|
+
const dayPath = path39.join(monthPath, day);
|
|
19436
19986
|
try {
|
|
19437
|
-
if (!
|
|
19987
|
+
if (!fs38.statSync(dayPath).isDirectory()) continue;
|
|
19438
19988
|
} catch {
|
|
19439
19989
|
continue;
|
|
19440
19990
|
}
|
|
19441
|
-
for (const file of
|
|
19442
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(
|
|
19991
|
+
for (const file of fs38.readdirSync(dayPath)) {
|
|
19992
|
+
if (file.endsWith(".jsonl")) jsonlFiles.push(path39.join(dayPath, file));
|
|
19443
19993
|
}
|
|
19444
19994
|
}
|
|
19445
19995
|
}
|
|
@@ -19492,13 +20042,13 @@ function freezeGeminiCost(acc) {
|
|
|
19492
20042
|
function processGeminiCostFile(filePath, projectKey, start, end, acc) {
|
|
19493
20043
|
const startMs = start.getTime();
|
|
19494
20044
|
try {
|
|
19495
|
-
if (
|
|
20045
|
+
if (fs38.statSync(filePath).mtimeMs < startMs) return;
|
|
19496
20046
|
} catch {
|
|
19497
20047
|
return;
|
|
19498
20048
|
}
|
|
19499
20049
|
let raw;
|
|
19500
20050
|
try {
|
|
19501
|
-
raw =
|
|
20051
|
+
raw = fs38.readFileSync(filePath, "utf-8");
|
|
19502
20052
|
} catch {
|
|
19503
20053
|
return;
|
|
19504
20054
|
}
|
|
@@ -19547,30 +20097,30 @@ function listGeminiSessionFiles(geminiTmpDir) {
|
|
|
19547
20097
|
const out = [];
|
|
19548
20098
|
let dirs;
|
|
19549
20099
|
try {
|
|
19550
|
-
if (!
|
|
19551
|
-
dirs =
|
|
20100
|
+
if (!fs38.statSync(geminiTmpDir).isDirectory()) return out;
|
|
20101
|
+
dirs = fs38.readdirSync(geminiTmpDir);
|
|
19552
20102
|
} catch {
|
|
19553
20103
|
return out;
|
|
19554
20104
|
}
|
|
19555
20105
|
for (const proj of dirs) {
|
|
19556
|
-
const chatsDir =
|
|
20106
|
+
const chatsDir = path39.join(geminiTmpDir, proj, "chats");
|
|
19557
20107
|
let files;
|
|
19558
20108
|
try {
|
|
19559
|
-
if (!
|
|
19560
|
-
files =
|
|
20109
|
+
if (!fs38.statSync(chatsDir).isDirectory()) continue;
|
|
20110
|
+
files = fs38.readdirSync(chatsDir);
|
|
19561
20111
|
} catch {
|
|
19562
20112
|
continue;
|
|
19563
20113
|
}
|
|
19564
20114
|
for (const f of files) {
|
|
19565
20115
|
if (!f.endsWith(".jsonl")) continue;
|
|
19566
|
-
out.push({ projectKey: proj, file:
|
|
20116
|
+
out.push({ projectKey: proj, file: path39.join(chatsDir, f) });
|
|
19567
20117
|
}
|
|
19568
20118
|
}
|
|
19569
20119
|
return out;
|
|
19570
20120
|
}
|
|
19571
20121
|
function loadGeminiCost(start, end, geminiTmpDir) {
|
|
19572
20122
|
const acc = emptyGeminiAccumulator();
|
|
19573
|
-
if (!
|
|
20123
|
+
if (!fs38.existsSync(geminiTmpDir)) return freezeGeminiCost(acc);
|
|
19574
20124
|
for (const { projectKey, file } of listGeminiSessionFiles(geminiTmpDir)) {
|
|
19575
20125
|
processGeminiCostFile(file, projectKey, start, end, acc);
|
|
19576
20126
|
}
|
|
@@ -19578,11 +20128,11 @@ function loadGeminiCost(start, end, geminiTmpDir) {
|
|
|
19578
20128
|
}
|
|
19579
20129
|
function aggregateReportFromAudit(period, opts = {}) {
|
|
19580
20130
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
19581
|
-
const auditLogPath = opts.auditLogPath ??
|
|
19582
|
-
const claudeProjectsDir = opts.claudeProjectsDir ??
|
|
19583
|
-
const codexSessionsDir = opts.codexSessionsDir ??
|
|
19584
|
-
const geminiTmpDir = opts.geminiTmpDir ??
|
|
19585
|
-
const hasAuditFile =
|
|
20131
|
+
const auditLogPath = opts.auditLogPath ?? path39.join(os34.homedir(), ".node9", "audit.log");
|
|
20132
|
+
const claudeProjectsDir = opts.claudeProjectsDir ?? path39.join(os34.homedir(), ".claude", "projects");
|
|
20133
|
+
const codexSessionsDir = opts.codexSessionsDir ?? path39.join(os34.homedir(), ".codex", "sessions");
|
|
20134
|
+
const geminiTmpDir = opts.geminiTmpDir ?? path39.join(os34.homedir(), ".gemini", "tmp");
|
|
20135
|
+
const hasAuditFile = fs38.existsSync(auditLogPath);
|
|
19586
20136
|
const allEntries = opts.preloadedAuditEntries ?? parseAuditLog(auditLogPath);
|
|
19587
20137
|
const unackedDlp = allEntries.filter((e) => e.source === "response-dlp");
|
|
19588
20138
|
const { start, end } = getDateRange(period, now);
|
|
@@ -20280,12 +20830,12 @@ function registerDaemonCommand(program2) {
|
|
|
20280
20830
|
init_core();
|
|
20281
20831
|
init_daemon();
|
|
20282
20832
|
import chalk15 from "chalk";
|
|
20283
|
-
import
|
|
20284
|
-
import
|
|
20285
|
-
import
|
|
20833
|
+
import fs39 from "fs";
|
|
20834
|
+
import path40 from "path";
|
|
20835
|
+
import os35 from "os";
|
|
20286
20836
|
function readJson2(filePath) {
|
|
20287
20837
|
try {
|
|
20288
|
-
if (
|
|
20838
|
+
if (fs39.existsSync(filePath)) return JSON.parse(fs39.readFileSync(filePath, "utf-8"));
|
|
20289
20839
|
} catch {
|
|
20290
20840
|
}
|
|
20291
20841
|
return null;
|
|
@@ -20350,28 +20900,28 @@ function registerStatusCommand(program2) {
|
|
|
20350
20900
|
console.log("");
|
|
20351
20901
|
const modeLabel = settings.mode === "audit" ? chalk15.blue("audit") : settings.mode === "strict" ? chalk15.red("strict") : chalk15.white("standard");
|
|
20352
20902
|
console.log(` Mode: ${modeLabel}`);
|
|
20353
|
-
const projectConfig =
|
|
20354
|
-
const globalConfig =
|
|
20903
|
+
const projectConfig = path40.join(process.cwd(), "node9.config.json");
|
|
20904
|
+
const globalConfig = path40.join(os35.homedir(), ".node9", "config.json");
|
|
20355
20905
|
console.log(
|
|
20356
|
-
` Local: ${
|
|
20906
|
+
` Local: ${fs39.existsSync(projectConfig) ? chalk15.green("Active (node9.config.json)") : chalk15.gray("Not present")}`
|
|
20357
20907
|
);
|
|
20358
20908
|
console.log(
|
|
20359
|
-
` Global: ${
|
|
20909
|
+
` Global: ${fs39.existsSync(globalConfig) ? chalk15.green("Active (~/.node9/config.json)") : chalk15.gray("Not present")}`
|
|
20360
20910
|
);
|
|
20361
20911
|
if (mergedConfig.policy.sandboxPaths.length > 0) {
|
|
20362
20912
|
console.log(
|
|
20363
20913
|
` Sandbox: ${chalk15.green(`${mergedConfig.policy.sandboxPaths.length} safe zones active`)}`
|
|
20364
20914
|
);
|
|
20365
20915
|
}
|
|
20366
|
-
const homeDir2 =
|
|
20916
|
+
const homeDir2 = os35.homedir();
|
|
20367
20917
|
const claudeSettings = readJson2(
|
|
20368
|
-
|
|
20918
|
+
path40.join(homeDir2, ".claude", "settings.json")
|
|
20369
20919
|
);
|
|
20370
|
-
const claudeConfig = readJson2(
|
|
20920
|
+
const claudeConfig = readJson2(path40.join(homeDir2, ".claude.json"));
|
|
20371
20921
|
const geminiSettings = readJson2(
|
|
20372
|
-
|
|
20922
|
+
path40.join(homeDir2, ".gemini", "settings.json")
|
|
20373
20923
|
);
|
|
20374
|
-
const cursorConfig = readJson2(
|
|
20924
|
+
const cursorConfig = readJson2(path40.join(homeDir2, ".cursor", "mcp.json"));
|
|
20375
20925
|
const agentFound = claudeSettings || claudeConfig || geminiSettings || cursorConfig;
|
|
20376
20926
|
if (agentFound) {
|
|
20377
20927
|
console.log("");
|
|
@@ -20434,9 +20984,9 @@ init_setup();
|
|
|
20434
20984
|
init_shields();
|
|
20435
20985
|
init_service();
|
|
20436
20986
|
import chalk16 from "chalk";
|
|
20437
|
-
import
|
|
20438
|
-
import
|
|
20439
|
-
import
|
|
20987
|
+
import fs40 from "fs";
|
|
20988
|
+
import path41 from "path";
|
|
20989
|
+
import os36 from "os";
|
|
20440
20990
|
import https4 from "https";
|
|
20441
20991
|
var DEFAULT_SHIELDS = ["bash-safe", "filesystem", "project-jail"];
|
|
20442
20992
|
function buildTelemetryPayload(agents, firstInstall) {
|
|
@@ -20522,16 +21072,16 @@ function registerInitCommand(program2) {
|
|
|
20522
21072
|
}
|
|
20523
21073
|
console.log("");
|
|
20524
21074
|
}
|
|
20525
|
-
const configPath =
|
|
20526
|
-
const isFirstInstall = !
|
|
20527
|
-
if (
|
|
21075
|
+
const configPath = path41.join(os36.homedir(), ".node9", "config.json");
|
|
21076
|
+
const isFirstInstall = !fs40.existsSync(configPath);
|
|
21077
|
+
if (fs40.existsSync(configPath) && !options.force) {
|
|
20528
21078
|
try {
|
|
20529
|
-
const existing = JSON.parse(
|
|
21079
|
+
const existing = JSON.parse(fs40.readFileSync(configPath, "utf-8"));
|
|
20530
21080
|
const settings = existing.settings ?? {};
|
|
20531
21081
|
if (settings.mode !== chosenMode) {
|
|
20532
21082
|
settings.mode = chosenMode;
|
|
20533
21083
|
existing.settings = settings;
|
|
20534
|
-
|
|
21084
|
+
fs40.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
|
|
20535
21085
|
console.log(chalk16.green(`\u2705 Mode updated: ${chosenMode}`));
|
|
20536
21086
|
} else {
|
|
20537
21087
|
console.log(chalk16.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
|
|
@@ -20544,9 +21094,9 @@ function registerInitCommand(program2) {
|
|
|
20544
21094
|
...DEFAULT_CONFIG,
|
|
20545
21095
|
settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
|
|
20546
21096
|
};
|
|
20547
|
-
const dir =
|
|
20548
|
-
if (!
|
|
20549
|
-
|
|
21097
|
+
const dir = path41.dirname(configPath);
|
|
21098
|
+
if (!fs40.existsSync(dir)) fs40.mkdirSync(dir, { recursive: true });
|
|
21099
|
+
fs40.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
|
|
20550
21100
|
console.log(chalk16.green(`\u2705 Config created: ${configPath}`));
|
|
20551
21101
|
console.log(chalk16.gray(` Mode: ${chosenMode}`));
|
|
20552
21102
|
}
|
|
@@ -20651,7 +21201,7 @@ function registerInitCommand(program2) {
|
|
|
20651
21201
|
}
|
|
20652
21202
|
|
|
20653
21203
|
// src/cli/commands/undo.ts
|
|
20654
|
-
import
|
|
21204
|
+
import path42 from "path";
|
|
20655
21205
|
import chalk18 from "chalk";
|
|
20656
21206
|
|
|
20657
21207
|
// src/tui/undo-navigator.ts
|
|
@@ -20810,7 +21360,7 @@ function findMatchingCwd(startDir, history) {
|
|
|
20810
21360
|
let dir = startDir;
|
|
20811
21361
|
while (true) {
|
|
20812
21362
|
if (cwds.has(dir)) return dir;
|
|
20813
|
-
const parent =
|
|
21363
|
+
const parent = path42.dirname(dir);
|
|
20814
21364
|
if (parent === dir) return null;
|
|
20815
21365
|
dir = parent;
|
|
20816
21366
|
}
|
|
@@ -21386,9 +21936,9 @@ function registerMcpGatewayCommand(program2) {
|
|
|
21386
21936
|
|
|
21387
21937
|
// src/mcp-server/index.ts
|
|
21388
21938
|
import readline5 from "readline";
|
|
21389
|
-
import
|
|
21390
|
-
import
|
|
21391
|
-
import
|
|
21939
|
+
import fs41 from "fs";
|
|
21940
|
+
import os37 from "os";
|
|
21941
|
+
import path43 from "path";
|
|
21392
21942
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
21393
21943
|
init_core();
|
|
21394
21944
|
init_daemon();
|
|
@@ -21639,13 +22189,13 @@ function handleStatus() {
|
|
|
21639
22189
|
lines.push(`Active shields: ${activeShields.length > 0 ? activeShields.join(", ") : "none"}`);
|
|
21640
22190
|
lines.push(`Smart rules: ${config.policy.smartRules.length} loaded`);
|
|
21641
22191
|
lines.push(`DLP: ${config.policy.dlp?.enabled !== false ? "enabled" : "disabled"}`);
|
|
21642
|
-
const projectConfig =
|
|
21643
|
-
const globalConfig =
|
|
22192
|
+
const projectConfig = path43.join(process.cwd(), "node9.config.json");
|
|
22193
|
+
const globalConfig = path43.join(os37.homedir(), ".node9", "config.json");
|
|
21644
22194
|
lines.push(
|
|
21645
|
-
`Project config (node9.config.json): ${
|
|
22195
|
+
`Project config (node9.config.json): ${fs41.existsSync(projectConfig) ? "present" : "not found"}`
|
|
21646
22196
|
);
|
|
21647
22197
|
lines.push(
|
|
21648
|
-
`Global config (~/.node9/config.json): ${
|
|
22198
|
+
`Global config (~/.node9/config.json): ${fs41.existsSync(globalConfig) ? "present" : "not found"}`
|
|
21649
22199
|
);
|
|
21650
22200
|
return lines.join("\n");
|
|
21651
22201
|
}
|
|
@@ -21719,21 +22269,21 @@ function handleShieldDisable(args) {
|
|
|
21719
22269
|
writeActiveShields(active.filter((s) => s !== name));
|
|
21720
22270
|
return `Shield "${name}" disabled.`;
|
|
21721
22271
|
}
|
|
21722
|
-
var GLOBAL_CONFIG_PATH =
|
|
22272
|
+
var GLOBAL_CONFIG_PATH = path43.join(os37.homedir(), ".node9", "config.json");
|
|
21723
22273
|
var APPROVER_CHANNELS = ["native", "browser", "cloud", "terminal"];
|
|
21724
22274
|
function readGlobalConfigRaw() {
|
|
21725
22275
|
try {
|
|
21726
|
-
if (
|
|
21727
|
-
return JSON.parse(
|
|
22276
|
+
if (fs41.existsSync(GLOBAL_CONFIG_PATH)) {
|
|
22277
|
+
return JSON.parse(fs41.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
|
|
21728
22278
|
}
|
|
21729
22279
|
} catch {
|
|
21730
22280
|
}
|
|
21731
22281
|
return {};
|
|
21732
22282
|
}
|
|
21733
22283
|
function writeGlobalConfigRaw(data) {
|
|
21734
|
-
const dir =
|
|
21735
|
-
if (!
|
|
21736
|
-
|
|
22284
|
+
const dir = path43.dirname(GLOBAL_CONFIG_PATH);
|
|
22285
|
+
if (!fs41.existsSync(dir)) fs41.mkdirSync(dir, { recursive: true });
|
|
22286
|
+
fs41.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(data, null, 2) + "\n");
|
|
21737
22287
|
}
|
|
21738
22288
|
function handleApproverList() {
|
|
21739
22289
|
const config = getConfig();
|
|
@@ -21777,9 +22327,9 @@ function handleApproverSet(args) {
|
|
|
21777
22327
|
function handleAuditGet(args) {
|
|
21778
22328
|
const limit = Math.min(typeof args.limit === "number" ? args.limit : 20, 100);
|
|
21779
22329
|
const filter = typeof args.filter === "string" && args.filter !== "all" ? args.filter : null;
|
|
21780
|
-
const auditPath =
|
|
21781
|
-
if (!
|
|
21782
|
-
const rawLines =
|
|
22330
|
+
const auditPath = path43.join(os37.homedir(), ".node9", "audit.log");
|
|
22331
|
+
if (!fs41.existsSync(auditPath)) return "No audit log found.";
|
|
22332
|
+
const rawLines = fs41.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
21783
22333
|
const parsed = [];
|
|
21784
22334
|
for (const line of rawLines) {
|
|
21785
22335
|
try {
|
|
@@ -22114,7 +22664,7 @@ function registerTrustCommand(program2) {
|
|
|
22114
22664
|
// src/cli/commands/mcp-pin.ts
|
|
22115
22665
|
init_mcp_pin();
|
|
22116
22666
|
import chalk21 from "chalk";
|
|
22117
|
-
import
|
|
22667
|
+
import fs42 from "fs";
|
|
22118
22668
|
function registerMcpPinCommand(program2) {
|
|
22119
22669
|
const pinCmd = program2.command("mcp").description("Manage MCP server tool definition pinning (rug pull defense)");
|
|
22120
22670
|
const pinSubCmd = pinCmd.command("pin").description("Manage pinned MCP server tool definitions");
|
|
@@ -22125,7 +22675,7 @@ function registerMcpPinCommand(program2) {
|
|
|
22125
22675
|
let repoCorrupt = false;
|
|
22126
22676
|
if (found.source === "repo") {
|
|
22127
22677
|
try {
|
|
22128
|
-
const raw =
|
|
22678
|
+
const raw = fs42.readFileSync(found.path, "utf-8");
|
|
22129
22679
|
const parsed = JSON.parse(raw);
|
|
22130
22680
|
repoEntries = parsed.servers ?? {};
|
|
22131
22681
|
} catch {
|
|
@@ -22439,9 +22989,9 @@ init_scan();
|
|
|
22439
22989
|
// src/cli/commands/sessions.ts
|
|
22440
22990
|
init_scan_summary();
|
|
22441
22991
|
import chalk24 from "chalk";
|
|
22442
|
-
import
|
|
22443
|
-
import
|
|
22444
|
-
import
|
|
22992
|
+
import fs43 from "fs";
|
|
22993
|
+
import path44 from "path";
|
|
22994
|
+
import os38 from "os";
|
|
22445
22995
|
var CLAUDE_PRICING3 = {
|
|
22446
22996
|
"claude-opus-4-6": { i: 5e-6, o: 25e-6, cw: 625e-8, cr: 5e-7 },
|
|
22447
22997
|
"claude-opus-4-5": { i: 5e-6, o: 25e-6, cw: 625e-8, cr: 5e-7 },
|
|
@@ -22482,10 +23032,10 @@ function encodeProjectPath(projectPath) {
|
|
|
22482
23032
|
}
|
|
22483
23033
|
function sessionJsonlPath(projectPath, sessionId) {
|
|
22484
23034
|
const encoded = encodeProjectPath(projectPath);
|
|
22485
|
-
return
|
|
23035
|
+
return path44.join(os38.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
|
|
22486
23036
|
}
|
|
22487
23037
|
function projectLabel(projectPath) {
|
|
22488
|
-
return projectPath.replace(
|
|
23038
|
+
return projectPath.replace(os38.homedir(), "~");
|
|
22489
23039
|
}
|
|
22490
23040
|
function parseHistoryLines(lines) {
|
|
22491
23041
|
const entries = [];
|
|
@@ -22554,10 +23104,10 @@ function parseSessionLines(lines) {
|
|
|
22554
23104
|
return { toolCalls, costUSD, hasSnapshot, modifiedFiles };
|
|
22555
23105
|
}
|
|
22556
23106
|
function loadAuditEntries(auditPath) {
|
|
22557
|
-
const aPath = auditPath ??
|
|
23107
|
+
const aPath = auditPath ?? path44.join(os38.homedir(), ".node9", "audit.log");
|
|
22558
23108
|
let raw;
|
|
22559
23109
|
try {
|
|
22560
|
-
raw =
|
|
23110
|
+
raw = fs43.readFileSync(aPath, "utf-8");
|
|
22561
23111
|
} catch {
|
|
22562
23112
|
return [];
|
|
22563
23113
|
}
|
|
@@ -22593,8 +23143,8 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
|
|
|
22593
23143
|
return result;
|
|
22594
23144
|
}
|
|
22595
23145
|
function buildGeminiSessions(days, allAuditEntries) {
|
|
22596
|
-
const tmpDir =
|
|
22597
|
-
if (!
|
|
23146
|
+
const tmpDir = path44.join(os38.homedir(), ".gemini", "tmp");
|
|
23147
|
+
if (!fs43.existsSync(tmpDir)) return [];
|
|
22598
23148
|
const cutoff = days !== null ? (() => {
|
|
22599
23149
|
const d = /* @__PURE__ */ new Date();
|
|
22600
23150
|
d.setDate(d.getDate() - days);
|
|
@@ -22603,35 +23153,35 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
22603
23153
|
})() : null;
|
|
22604
23154
|
let slugDirs;
|
|
22605
23155
|
try {
|
|
22606
|
-
slugDirs =
|
|
23156
|
+
slugDirs = fs43.readdirSync(tmpDir);
|
|
22607
23157
|
} catch {
|
|
22608
23158
|
return [];
|
|
22609
23159
|
}
|
|
22610
23160
|
const summaries = [];
|
|
22611
23161
|
for (const slug of slugDirs) {
|
|
22612
|
-
const slugPath =
|
|
23162
|
+
const slugPath = path44.join(tmpDir, slug);
|
|
22613
23163
|
try {
|
|
22614
|
-
if (!
|
|
23164
|
+
if (!fs43.statSync(slugPath).isDirectory()) continue;
|
|
22615
23165
|
} catch {
|
|
22616
23166
|
continue;
|
|
22617
23167
|
}
|
|
22618
|
-
let projectRoot =
|
|
23168
|
+
let projectRoot = path44.join(os38.homedir(), slug);
|
|
22619
23169
|
try {
|
|
22620
|
-
projectRoot =
|
|
23170
|
+
projectRoot = fs43.readFileSync(path44.join(slugPath, ".project_root"), "utf-8").trim();
|
|
22621
23171
|
} catch {
|
|
22622
23172
|
}
|
|
22623
|
-
const chatsDir =
|
|
22624
|
-
if (!
|
|
23173
|
+
const chatsDir = path44.join(slugPath, "chats");
|
|
23174
|
+
if (!fs43.existsSync(chatsDir)) continue;
|
|
22625
23175
|
let chatFiles;
|
|
22626
23176
|
try {
|
|
22627
|
-
chatFiles =
|
|
23177
|
+
chatFiles = fs43.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
|
|
22628
23178
|
} catch {
|
|
22629
23179
|
continue;
|
|
22630
23180
|
}
|
|
22631
23181
|
for (const chatFile of chatFiles) {
|
|
22632
23182
|
let raw;
|
|
22633
23183
|
try {
|
|
22634
|
-
raw =
|
|
23184
|
+
raw = fs43.readFileSync(path44.join(chatsDir, chatFile), "utf-8");
|
|
22635
23185
|
} catch {
|
|
22636
23186
|
continue;
|
|
22637
23187
|
}
|
|
@@ -22711,8 +23261,8 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
22711
23261
|
return summaries;
|
|
22712
23262
|
}
|
|
22713
23263
|
function buildCodexSessions(days, allAuditEntries) {
|
|
22714
|
-
const sessionsBase =
|
|
22715
|
-
if (!
|
|
23264
|
+
const sessionsBase = path44.join(os38.homedir(), ".codex", "sessions");
|
|
23265
|
+
if (!fs43.existsSync(sessionsBase)) return [];
|
|
22716
23266
|
const cutoff = days !== null ? (() => {
|
|
22717
23267
|
const d = /* @__PURE__ */ new Date();
|
|
22718
23268
|
d.setDate(d.getDate() - days);
|
|
@@ -22721,29 +23271,29 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
22721
23271
|
})() : null;
|
|
22722
23272
|
const jsonlFiles = [];
|
|
22723
23273
|
try {
|
|
22724
|
-
for (const year of
|
|
22725
|
-
const yearPath =
|
|
23274
|
+
for (const year of fs43.readdirSync(sessionsBase)) {
|
|
23275
|
+
const yearPath = path44.join(sessionsBase, year);
|
|
22726
23276
|
try {
|
|
22727
|
-
if (!
|
|
23277
|
+
if (!fs43.statSync(yearPath).isDirectory()) continue;
|
|
22728
23278
|
} catch {
|
|
22729
23279
|
continue;
|
|
22730
23280
|
}
|
|
22731
|
-
for (const month of
|
|
22732
|
-
const monthPath =
|
|
23281
|
+
for (const month of fs43.readdirSync(yearPath)) {
|
|
23282
|
+
const monthPath = path44.join(yearPath, month);
|
|
22733
23283
|
try {
|
|
22734
|
-
if (!
|
|
23284
|
+
if (!fs43.statSync(monthPath).isDirectory()) continue;
|
|
22735
23285
|
} catch {
|
|
22736
23286
|
continue;
|
|
22737
23287
|
}
|
|
22738
|
-
for (const day of
|
|
22739
|
-
const dayPath =
|
|
23288
|
+
for (const day of fs43.readdirSync(monthPath)) {
|
|
23289
|
+
const dayPath = path44.join(monthPath, day);
|
|
22740
23290
|
try {
|
|
22741
|
-
if (!
|
|
23291
|
+
if (!fs43.statSync(dayPath).isDirectory()) continue;
|
|
22742
23292
|
} catch {
|
|
22743
23293
|
continue;
|
|
22744
23294
|
}
|
|
22745
|
-
for (const file of
|
|
22746
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(
|
|
23295
|
+
for (const file of fs43.readdirSync(dayPath)) {
|
|
23296
|
+
if (file.endsWith(".jsonl")) jsonlFiles.push(path44.join(dayPath, file));
|
|
22747
23297
|
}
|
|
22748
23298
|
}
|
|
22749
23299
|
}
|
|
@@ -22755,7 +23305,7 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
22755
23305
|
for (const filePath of jsonlFiles) {
|
|
22756
23306
|
let lines;
|
|
22757
23307
|
try {
|
|
22758
|
-
lines =
|
|
23308
|
+
lines = fs43.readFileSync(filePath, "utf-8").split("\n");
|
|
22759
23309
|
} catch {
|
|
22760
23310
|
continue;
|
|
22761
23311
|
}
|
|
@@ -22833,10 +23383,10 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
22833
23383
|
return summaries;
|
|
22834
23384
|
}
|
|
22835
23385
|
function buildSessions(days, historyPath) {
|
|
22836
|
-
const hPath = historyPath ??
|
|
23386
|
+
const hPath = historyPath ?? path44.join(os38.homedir(), ".claude", "history.jsonl");
|
|
22837
23387
|
let historyRaw;
|
|
22838
23388
|
try {
|
|
22839
|
-
historyRaw =
|
|
23389
|
+
historyRaw = fs43.readFileSync(hPath, "utf-8");
|
|
22840
23390
|
} catch {
|
|
22841
23391
|
return [];
|
|
22842
23392
|
}
|
|
@@ -22861,7 +23411,7 @@ function buildSessions(days, historyPath) {
|
|
|
22861
23411
|
const jsonlFile = sessionJsonlPath(entry.project, entry.sessionId);
|
|
22862
23412
|
let sessionLines = [];
|
|
22863
23413
|
try {
|
|
22864
|
-
sessionLines =
|
|
23414
|
+
sessionLines = fs43.readFileSync(jsonlFile, "utf-8").split("\n");
|
|
22865
23415
|
} catch {
|
|
22866
23416
|
}
|
|
22867
23417
|
const { toolCalls, costUSD, hasSnapshot, modifiedFiles } = parseSessionLines(sessionLines);
|
|
@@ -23129,8 +23679,8 @@ function registerSessionsCommand(program2) {
|
|
|
23129
23679
|
console.log("");
|
|
23130
23680
|
console.log(chalk24.cyan.bold("\u{1F4CB} node9 sessions") + chalk24.dim(" \u2014 what your AI agent did"));
|
|
23131
23681
|
console.log("");
|
|
23132
|
-
const historyPath =
|
|
23133
|
-
if (!
|
|
23682
|
+
const historyPath = path44.join(os38.homedir(), ".claude", "history.jsonl");
|
|
23683
|
+
if (!fs43.existsSync(historyPath)) {
|
|
23134
23684
|
console.log(chalk24.yellow(" No Claude session history found at ~/.claude/history.jsonl"));
|
|
23135
23685
|
console.log(chalk24.gray(" Install Claude Code, run a few sessions, then try again.\n"));
|
|
23136
23686
|
return;
|
|
@@ -23167,12 +23717,12 @@ function registerSessionsCommand(program2) {
|
|
|
23167
23717
|
|
|
23168
23718
|
// src/cli/commands/skill-pin.ts
|
|
23169
23719
|
import chalk25 from "chalk";
|
|
23170
|
-
import
|
|
23171
|
-
import
|
|
23172
|
-
import
|
|
23720
|
+
import fs44 from "fs";
|
|
23721
|
+
import os39 from "os";
|
|
23722
|
+
import path45 from "path";
|
|
23173
23723
|
function wipeSkillSessions() {
|
|
23174
23724
|
try {
|
|
23175
|
-
|
|
23725
|
+
fs44.rmSync(path45.join(os39.homedir(), ".node9", "skill-sessions"), {
|
|
23176
23726
|
recursive: true,
|
|
23177
23727
|
force: true
|
|
23178
23728
|
});
|
|
@@ -23254,15 +23804,15 @@ function registerSkillPinCommand(program2) {
|
|
|
23254
23804
|
}
|
|
23255
23805
|
|
|
23256
23806
|
// src/cli/commands/decisions.ts
|
|
23257
|
-
import
|
|
23258
|
-
import
|
|
23259
|
-
import
|
|
23807
|
+
import fs45 from "fs";
|
|
23808
|
+
import os40 from "os";
|
|
23809
|
+
import path46 from "path";
|
|
23260
23810
|
import chalk26 from "chalk";
|
|
23261
|
-
var DECISIONS_FILE2 =
|
|
23811
|
+
var DECISIONS_FILE2 = path46.join(os40.homedir(), ".node9", "decisions.json");
|
|
23262
23812
|
function readDecisions() {
|
|
23263
23813
|
try {
|
|
23264
|
-
if (!
|
|
23265
|
-
const raw =
|
|
23814
|
+
if (!fs45.existsSync(DECISIONS_FILE2)) return {};
|
|
23815
|
+
const raw = fs45.readFileSync(DECISIONS_FILE2, "utf-8");
|
|
23266
23816
|
const parsed = JSON.parse(raw);
|
|
23267
23817
|
const out = {};
|
|
23268
23818
|
for (const [k, v] of Object.entries(parsed)) {
|
|
@@ -23274,11 +23824,11 @@ function readDecisions() {
|
|
|
23274
23824
|
}
|
|
23275
23825
|
}
|
|
23276
23826
|
function writeDecisions(d) {
|
|
23277
|
-
const dir =
|
|
23278
|
-
if (!
|
|
23827
|
+
const dir = path46.dirname(DECISIONS_FILE2);
|
|
23828
|
+
if (!fs45.existsSync(dir)) fs45.mkdirSync(dir, { recursive: true });
|
|
23279
23829
|
const tmp = `${DECISIONS_FILE2}.${process.pid}.tmp`;
|
|
23280
|
-
|
|
23281
|
-
|
|
23830
|
+
fs45.writeFileSync(tmp, JSON.stringify(d, null, 2));
|
|
23831
|
+
fs45.renameSync(tmp, DECISIONS_FILE2);
|
|
23282
23832
|
}
|
|
23283
23833
|
function registerDecisionsCommand(program2) {
|
|
23284
23834
|
const cmd = program2.command("decisions").description('Manage persistent "Always Allow" / "Always Deny" tool decisions');
|
|
@@ -23335,18 +23885,18 @@ Persistent decisions (${entries.length})
|
|
|
23335
23885
|
|
|
23336
23886
|
// src/cli/commands/dlp.ts
|
|
23337
23887
|
import chalk27 from "chalk";
|
|
23338
|
-
import
|
|
23339
|
-
import
|
|
23340
|
-
import
|
|
23341
|
-
var AUDIT_LOG =
|
|
23342
|
-
var RESOLVED_FILE =
|
|
23888
|
+
import fs46 from "fs";
|
|
23889
|
+
import path47 from "path";
|
|
23890
|
+
import os41 from "os";
|
|
23891
|
+
var AUDIT_LOG = path47.join(os41.homedir(), ".node9", "audit.log");
|
|
23892
|
+
var RESOLVED_FILE = path47.join(os41.homedir(), ".node9", "dlp-resolved.json");
|
|
23343
23893
|
var ANSI_RE = /\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g;
|
|
23344
23894
|
function stripAnsi(s) {
|
|
23345
23895
|
return s.replace(ANSI_RE, "");
|
|
23346
23896
|
}
|
|
23347
23897
|
function loadResolved() {
|
|
23348
23898
|
try {
|
|
23349
|
-
const raw = JSON.parse(
|
|
23899
|
+
const raw = JSON.parse(fs46.readFileSync(RESOLVED_FILE, "utf-8"));
|
|
23350
23900
|
return new Set(raw);
|
|
23351
23901
|
} catch {
|
|
23352
23902
|
return /* @__PURE__ */ new Set();
|
|
@@ -23354,13 +23904,13 @@ function loadResolved() {
|
|
|
23354
23904
|
}
|
|
23355
23905
|
function saveResolved(resolved) {
|
|
23356
23906
|
try {
|
|
23357
|
-
|
|
23907
|
+
fs46.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
|
|
23358
23908
|
} catch {
|
|
23359
23909
|
}
|
|
23360
23910
|
}
|
|
23361
23911
|
function loadDlpFindings() {
|
|
23362
|
-
if (!
|
|
23363
|
-
return
|
|
23912
|
+
if (!fs46.existsSync(AUDIT_LOG)) return [];
|
|
23913
|
+
return fs46.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
|
|
23364
23914
|
if (!line.trim()) return [];
|
|
23365
23915
|
try {
|
|
23366
23916
|
const e = JSON.parse(line);
|
|
@@ -23370,7 +23920,7 @@ function loadDlpFindings() {
|
|
|
23370
23920
|
}
|
|
23371
23921
|
});
|
|
23372
23922
|
}
|
|
23373
|
-
function
|
|
23923
|
+
function entryKey2(e) {
|
|
23374
23924
|
return `${e.ts}:${e.dlpPattern}:${e.dlpSample}`;
|
|
23375
23925
|
}
|
|
23376
23926
|
function fmtDate3(ts) {
|
|
@@ -23393,7 +23943,7 @@ function registerDlpCommand(program2) {
|
|
|
23393
23943
|
return;
|
|
23394
23944
|
}
|
|
23395
23945
|
const resolved = loadResolved();
|
|
23396
|
-
for (const e of findings) resolved.add(
|
|
23946
|
+
for (const e of findings) resolved.add(entryKey2(e));
|
|
23397
23947
|
saveResolved(resolved);
|
|
23398
23948
|
console.log(
|
|
23399
23949
|
chalk27.green(
|
|
@@ -23406,7 +23956,7 @@ function registerDlpCommand(program2) {
|
|
|
23406
23956
|
cmd.action(() => {
|
|
23407
23957
|
const findings = loadDlpFindings();
|
|
23408
23958
|
const resolved = loadResolved();
|
|
23409
|
-
const open = findings.filter((e) => !resolved.has(
|
|
23959
|
+
const open = findings.filter((e) => !resolved.has(entryKey2(e)));
|
|
23410
23960
|
const resolvedCount = findings.length - open.length;
|
|
23411
23961
|
console.log("");
|
|
23412
23962
|
console.log(
|
|
@@ -23459,14 +24009,14 @@ function registerDlpCommand(program2) {
|
|
|
23459
24009
|
// src/cli/commands/mask.ts
|
|
23460
24010
|
init_dlp();
|
|
23461
24011
|
import chalk28 from "chalk";
|
|
23462
|
-
import
|
|
23463
|
-
import
|
|
23464
|
-
import
|
|
24012
|
+
import fs47 from "fs";
|
|
24013
|
+
import path48 from "path";
|
|
24014
|
+
import os42 from "os";
|
|
23465
24015
|
function findJsonlFiles(dir) {
|
|
23466
24016
|
const results = [];
|
|
23467
|
-
if (!
|
|
23468
|
-
for (const entry of
|
|
23469
|
-
const full =
|
|
24017
|
+
if (!fs47.existsSync(dir)) return results;
|
|
24018
|
+
for (const entry of fs47.readdirSync(dir, { withFileTypes: true })) {
|
|
24019
|
+
const full = path48.join(dir, entry.name);
|
|
23470
24020
|
if (entry.isDirectory()) results.push(...findJsonlFiles(full));
|
|
23471
24021
|
else if (entry.isFile() && entry.name.endsWith(".jsonl")) results.push(full);
|
|
23472
24022
|
}
|
|
@@ -23509,7 +24059,7 @@ function redactJson(obj) {
|
|
|
23509
24059
|
function processFile(filePath, dryRun) {
|
|
23510
24060
|
let raw;
|
|
23511
24061
|
try {
|
|
23512
|
-
raw =
|
|
24062
|
+
raw = fs47.readFileSync(filePath, "utf-8");
|
|
23513
24063
|
} catch {
|
|
23514
24064
|
return { redactedLines: 0, patterns: [] };
|
|
23515
24065
|
}
|
|
@@ -23541,14 +24091,14 @@ function processFile(filePath, dryRun) {
|
|
|
23541
24091
|
}
|
|
23542
24092
|
}
|
|
23543
24093
|
if (!dryRun && redactedLines > 0) {
|
|
23544
|
-
|
|
24094
|
+
fs47.writeFileSync(filePath, newLines.join("\n"), "utf-8");
|
|
23545
24095
|
}
|
|
23546
24096
|
return { redactedLines, patterns };
|
|
23547
24097
|
}
|
|
23548
24098
|
function processJsonFile(filePath, dryRun) {
|
|
23549
24099
|
let raw;
|
|
23550
24100
|
try {
|
|
23551
|
-
raw =
|
|
24101
|
+
raw = fs47.readFileSync(filePath, "utf-8");
|
|
23552
24102
|
} catch {
|
|
23553
24103
|
return { redactedLines: 0, patterns: [] };
|
|
23554
24104
|
}
|
|
@@ -23561,15 +24111,15 @@ function processJsonFile(filePath, dryRun) {
|
|
|
23561
24111
|
const { value, modified, found } = redactJson(parsed);
|
|
23562
24112
|
if (!modified) return { redactedLines: 0, patterns: [] };
|
|
23563
24113
|
if (!dryRun) {
|
|
23564
|
-
|
|
24114
|
+
fs47.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
|
|
23565
24115
|
}
|
|
23566
24116
|
return { redactedLines: 1, patterns: found };
|
|
23567
24117
|
}
|
|
23568
24118
|
function findJsonFiles(dir) {
|
|
23569
24119
|
const results = [];
|
|
23570
|
-
if (!
|
|
23571
|
-
for (const entry of
|
|
23572
|
-
const full =
|
|
24120
|
+
if (!fs47.existsSync(dir)) return results;
|
|
24121
|
+
for (const entry of fs47.readdirSync(dir, { withFileTypes: true })) {
|
|
24122
|
+
const full = path48.join(dir, entry.name);
|
|
23573
24123
|
if (entry.isDirectory()) results.push(...findJsonFiles(full));
|
|
23574
24124
|
else if (entry.isFile() && entry.name.endsWith(".json")) results.push(full);
|
|
23575
24125
|
}
|
|
@@ -23578,9 +24128,9 @@ function findJsonFiles(dir) {
|
|
|
23578
24128
|
function registerMaskCommand(program2) {
|
|
23579
24129
|
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) => {
|
|
23580
24130
|
const dryRun = !!options.dryRun;
|
|
23581
|
-
const home =
|
|
23582
|
-
const claudeDir =
|
|
23583
|
-
const geminiDir =
|
|
24131
|
+
const home = os42.homedir();
|
|
24132
|
+
const claudeDir = path48.join(home, ".claude", "projects");
|
|
24133
|
+
const geminiDir = path48.join(home, ".gemini", "tmp");
|
|
23584
24134
|
const allFiles = [
|
|
23585
24135
|
...findJsonlFiles(claudeDir).map((p) => ({ path: p, type: "jsonl" })),
|
|
23586
24136
|
...findJsonFiles(geminiDir).map((p) => ({ path: p, type: "json" }))
|
|
@@ -23588,7 +24138,7 @@ function registerMaskCommand(program2) {
|
|
|
23588
24138
|
const cutoff = options.all ? null : new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
|
|
23589
24139
|
const filtered = cutoff ? allFiles.filter((f) => {
|
|
23590
24140
|
try {
|
|
23591
|
-
return
|
|
24141
|
+
return fs47.statSync(f.path).mtime >= cutoff;
|
|
23592
24142
|
} catch {
|
|
23593
24143
|
return false;
|
|
23594
24144
|
}
|
|
@@ -23644,20 +24194,20 @@ function registerMaskCommand(program2) {
|
|
|
23644
24194
|
// src/cli.ts
|
|
23645
24195
|
init_blast();
|
|
23646
24196
|
var { version } = JSON.parse(
|
|
23647
|
-
|
|
24197
|
+
fs50.readFileSync(path51.join(__dirname, "../package.json"), "utf-8")
|
|
23648
24198
|
);
|
|
23649
24199
|
var program = new Command();
|
|
23650
24200
|
program.name("node9").description("The Sudo Command for AI Agents").version(version);
|
|
23651
24201
|
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) => {
|
|
23652
24202
|
const DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept";
|
|
23653
|
-
const credPath =
|
|
23654
|
-
if (!
|
|
23655
|
-
|
|
24203
|
+
const credPath = path51.join(os45.homedir(), ".node9", "credentials.json");
|
|
24204
|
+
if (!fs50.existsSync(path51.dirname(credPath)))
|
|
24205
|
+
fs50.mkdirSync(path51.dirname(credPath), { recursive: true });
|
|
23656
24206
|
const profileName = options.profile || "default";
|
|
23657
24207
|
let existingCreds = {};
|
|
23658
24208
|
try {
|
|
23659
|
-
if (
|
|
23660
|
-
const raw = JSON.parse(
|
|
24209
|
+
if (fs50.existsSync(credPath)) {
|
|
24210
|
+
const raw = JSON.parse(fs50.readFileSync(credPath, "utf-8"));
|
|
23661
24211
|
if (raw.apiKey) {
|
|
23662
24212
|
existingCreds = {
|
|
23663
24213
|
default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL2 }
|
|
@@ -23669,14 +24219,14 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
23669
24219
|
} catch {
|
|
23670
24220
|
}
|
|
23671
24221
|
existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL2 };
|
|
23672
|
-
|
|
24222
|
+
fs50.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
|
|
23673
24223
|
let effectiveCloud = null;
|
|
23674
24224
|
if (profileName === "default") {
|
|
23675
|
-
const configPath =
|
|
24225
|
+
const configPath = path51.join(os45.homedir(), ".node9", "config.json");
|
|
23676
24226
|
let config = {};
|
|
23677
24227
|
try {
|
|
23678
|
-
if (
|
|
23679
|
-
config = JSON.parse(
|
|
24228
|
+
if (fs50.existsSync(configPath))
|
|
24229
|
+
config = JSON.parse(fs50.readFileSync(configPath, "utf-8"));
|
|
23680
24230
|
} catch {
|
|
23681
24231
|
}
|
|
23682
24232
|
if (!config.settings || typeof config.settings !== "object") config.settings = {};
|
|
@@ -23691,9 +24241,9 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
23691
24241
|
approvers.cloud = false;
|
|
23692
24242
|
}
|
|
23693
24243
|
s.approvers = approvers;
|
|
23694
|
-
if (!
|
|
23695
|
-
|
|
23696
|
-
|
|
24244
|
+
if (!fs50.existsSync(path51.dirname(configPath)))
|
|
24245
|
+
fs50.mkdirSync(path51.dirname(configPath), { recursive: true });
|
|
24246
|
+
fs50.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
23697
24247
|
effectiveCloud = approvers.cloud === true;
|
|
23698
24248
|
}
|
|
23699
24249
|
if (options.profile && profileName !== "default") {
|
|
@@ -23852,15 +24402,15 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
23852
24402
|
}
|
|
23853
24403
|
}
|
|
23854
24404
|
if (options.purge) {
|
|
23855
|
-
const node9Dir =
|
|
23856
|
-
if (
|
|
24405
|
+
const node9Dir = path51.join(os45.homedir(), ".node9");
|
|
24406
|
+
if (fs50.existsSync(node9Dir)) {
|
|
23857
24407
|
const confirmed = await confirm2({
|
|
23858
24408
|
message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
|
|
23859
24409
|
default: false
|
|
23860
24410
|
});
|
|
23861
24411
|
if (confirmed) {
|
|
23862
|
-
|
|
23863
|
-
if (
|
|
24412
|
+
fs50.rmSync(node9Dir, { recursive: true });
|
|
24413
|
+
if (fs50.existsSync(node9Dir)) {
|
|
23864
24414
|
console.error(
|
|
23865
24415
|
chalk30.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
|
|
23866
24416
|
);
|
|
@@ -23975,7 +24525,7 @@ program.command("tail").description("Stream live agent activity to the terminal"
|
|
|
23975
24525
|
});
|
|
23976
24526
|
program.command("monitor").description("Live interactive dashboard \u2014 activity feed, approvals, security signals").action(async () => {
|
|
23977
24527
|
try {
|
|
23978
|
-
const dashboardPath =
|
|
24528
|
+
const dashboardPath = path51.join(__dirname, "dashboard.mjs");
|
|
23979
24529
|
const dynamicImport = new Function("id", "return import(id)");
|
|
23980
24530
|
const mod = await dynamicImport(`file://${dashboardPath}`);
|
|
23981
24531
|
await mod.startMonitor();
|
|
@@ -24013,14 +24563,14 @@ Claude Code spawns this command every ~300ms and writes a JSON payload to stdin.
|
|
|
24013
24563
|
Run "node9 addto claude" to register it as the statusLine.`
|
|
24014
24564
|
).argument("[subcommand]", 'Optional: "debug on" / "debug off" to toggle stdin logging').argument("[state]", 'on|off \u2014 used with "debug" subcommand').action(async (subcommand, state) => {
|
|
24015
24565
|
if (subcommand === "debug") {
|
|
24016
|
-
const flagFile =
|
|
24566
|
+
const flagFile = path51.join(os45.homedir(), ".node9", "hud-debug");
|
|
24017
24567
|
if (state === "on") {
|
|
24018
|
-
|
|
24019
|
-
|
|
24568
|
+
fs50.mkdirSync(path51.dirname(flagFile), { recursive: true });
|
|
24569
|
+
fs50.writeFileSync(flagFile, "");
|
|
24020
24570
|
console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
|
|
24021
24571
|
console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
|
|
24022
24572
|
} else if (state === "off") {
|
|
24023
|
-
if (
|
|
24573
|
+
if (fs50.existsSync(flagFile)) fs50.unlinkSync(flagFile);
|
|
24024
24574
|
console.log("HUD debug logging disabled.");
|
|
24025
24575
|
} else {
|
|
24026
24576
|
console.error("Usage: node9 hud debug on|off");
|
|
@@ -24137,9 +24687,9 @@ if (process.argv[2] !== "daemon") {
|
|
|
24137
24687
|
const isCheckHook = process.argv[2] === "check";
|
|
24138
24688
|
if (isCheckHook) {
|
|
24139
24689
|
if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
|
|
24140
|
-
const logPath =
|
|
24690
|
+
const logPath = path51.join(os45.homedir(), ".node9", "hook-debug.log");
|
|
24141
24691
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
24142
|
-
|
|
24692
|
+
fs50.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
|
|
24143
24693
|
`);
|
|
24144
24694
|
}
|
|
24145
24695
|
process.exit(0);
|