@node9/proxy 1.45.0 → 1.47.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 +996 -795
- package/dist/cli.mjs +971 -771
- package/dist/dashboard.mjs +5 -0
- package/dist/index.js +20 -1
- package/dist/index.mjs +20 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -219,8 +219,8 @@ function sanitizeConfig(raw) {
|
|
|
219
219
|
}
|
|
220
220
|
}
|
|
221
221
|
const lines = result.error.issues.map((issue) => {
|
|
222
|
-
const
|
|
223
|
-
return ` \u2022 ${
|
|
222
|
+
const path65 = issue.path.length > 0 ? issue.path.join(".") : "root";
|
|
223
|
+
return ` \u2022 ${path65}: ${issue.message}`;
|
|
224
224
|
});
|
|
225
225
|
return {
|
|
226
226
|
sanitized,
|
|
@@ -315,6 +315,11 @@ var init_config_schema = __esm({
|
|
|
315
315
|
// must run them from the CLI. node9's threat model is the agent itself.
|
|
316
316
|
mcpAllowWeakening: z.boolean().optional(),
|
|
317
317
|
cloudSyncIntervalHours: z.number().positive().optional(),
|
|
318
|
+
// Seconds-granular override for the cloud policy sync cadence. Wins over
|
|
319
|
+
// cloudSyncIntervalHours when set. Lets you opt into fast apply (e.g. 20)
|
|
320
|
+
// for an incident; clamped to a 15s floor so it can't hammer the API.
|
|
321
|
+
// Unset → falls back to hours, then the 5h default.
|
|
322
|
+
cloudSyncIntervalSeconds: z.number().positive().optional(),
|
|
318
323
|
// Outbox shipper (audit.log → SaaS batch ingest). enabled defaults
|
|
319
324
|
// to true; set false to fall back to local-only auditing.
|
|
320
325
|
shipper: z.object({
|
|
@@ -1341,9 +1346,9 @@ function matchesPattern(text, patterns) {
|
|
|
1341
1346
|
const withoutDotSlash = text.replace(/^\.\//, "");
|
|
1342
1347
|
return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
|
|
1343
1348
|
}
|
|
1344
|
-
function getNestedValue(obj,
|
|
1349
|
+
function getNestedValue(obj, path65) {
|
|
1345
1350
|
if (!obj || typeof obj !== "object") return null;
|
|
1346
|
-
const segments =
|
|
1351
|
+
const segments = path65.split(".");
|
|
1347
1352
|
for (const seg of segments) {
|
|
1348
1353
|
if (FORBIDDEN_PATH_SEGMENTS.has(seg)) return null;
|
|
1349
1354
|
}
|
|
@@ -1396,6 +1401,14 @@ function evaluateSmartConditions(args, rule) {
|
|
|
1396
1401
|
});
|
|
1397
1402
|
return mode === "any" ? results.some((r) => r) : results.every((r) => r);
|
|
1398
1403
|
}
|
|
1404
|
+
function resolvePinned(matches) {
|
|
1405
|
+
if (matches.length === 0) return void 0;
|
|
1406
|
+
const pinned = matches.filter((r) => r.pinned);
|
|
1407
|
+
if (pinned.length === 0) return matches[0];
|
|
1408
|
+
return pinned.reduce(
|
|
1409
|
+
(best, r) => VERDICT_RANK[r.verdict] > VERDICT_RANK[best.verdict] ? r : best
|
|
1410
|
+
);
|
|
1411
|
+
}
|
|
1399
1412
|
function tokenize2(toolName) {
|
|
1400
1413
|
return toolName.toLowerCase().split(/[_.\-\s]+/).filter(Boolean);
|
|
1401
1414
|
}
|
|
@@ -1507,9 +1520,10 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
|
|
|
1507
1520
|
}
|
|
1508
1521
|
}
|
|
1509
1522
|
if (config.policy.smartRules.length > 0) {
|
|
1510
|
-
const
|
|
1523
|
+
const matches = config.policy.smartRules.filter(
|
|
1511
1524
|
(rule) => matchesPattern(toolName, rule.tool) && !(bashCommand !== null && rule.name && AST_FS_REGEX_RULES.has(rule.name)) && evaluateSmartConditions(args, rule)
|
|
1512
1525
|
);
|
|
1526
|
+
const matchedRule = resolvePinned(matches);
|
|
1513
1527
|
if (matchedRule) {
|
|
1514
1528
|
if (matchedRule.verdict === "allow")
|
|
1515
1529
|
return { decision: "allow", ruleName: matchedRule.name ?? matchedRule.tool };
|
|
@@ -2248,7 +2262,7 @@ function* stringValues(obj, depth = 0) {
|
|
|
2248
2262
|
}
|
|
2249
2263
|
for (const v of Object.values(obj)) yield* stringValues(v, depth + 1);
|
|
2250
2264
|
}
|
|
2251
|
-
var MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, 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;
|
|
2265
|
+
var MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, 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, VERDICT_RANK, 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, ENGINE_VERSION;
|
|
2252
2266
|
var init_dist = __esm({
|
|
2253
2267
|
"packages/policy-engine/dist/index.mjs"() {
|
|
2254
2268
|
"use strict";
|
|
@@ -3173,6 +3187,11 @@ var init_dist = __esm({
|
|
|
3173
3187
|
REGEX_CACHE_MAX = 500;
|
|
3174
3188
|
regexCache = /* @__PURE__ */ new Map();
|
|
3175
3189
|
FORBIDDEN_PATH_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
3190
|
+
VERDICT_RANK = {
|
|
3191
|
+
allow: 0,
|
|
3192
|
+
review: 1,
|
|
3193
|
+
block: 2
|
|
3194
|
+
};
|
|
3176
3195
|
SQL_DML_KEYWORDS = /* @__PURE__ */ new Set(["select", "insert", "update", "delete", "merge", "upsert"]);
|
|
3177
3196
|
aws_default = {
|
|
3178
3197
|
name: "aws",
|
|
@@ -3977,6 +3996,7 @@ var init_dist = __esm({
|
|
|
3977
3996
|
DEDUPE_PREVIEW_LEN = 120;
|
|
3978
3997
|
TERMINAL_ESCAPE_RE = // eslint-disable-next-line no-control-regex
|
|
3979
3998
|
/\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-_]|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
|
|
3999
|
+
ENGINE_VERSION = "1.4.0";
|
|
3980
4000
|
}
|
|
3981
4001
|
});
|
|
3982
4002
|
|
|
@@ -9700,16 +9720,237 @@ var init_setup = __esm({
|
|
|
9700
9720
|
}
|
|
9701
9721
|
});
|
|
9702
9722
|
|
|
9703
|
-
// src/
|
|
9723
|
+
// src/agent-wiring.ts
|
|
9704
9724
|
import fs14 from "fs";
|
|
9705
9725
|
import path16 from "path";
|
|
9706
9726
|
import os13 from "os";
|
|
9727
|
+
import * as yaml2 from "yaml";
|
|
9728
|
+
import { parse as parseToml2 } from "smol-toml";
|
|
9729
|
+
function readJson2(filePath) {
|
|
9730
|
+
if (!fs14.existsSync(filePath)) return null;
|
|
9731
|
+
try {
|
|
9732
|
+
return JSON.parse(fs14.readFileSync(filePath, "utf-8"));
|
|
9733
|
+
} catch {
|
|
9734
|
+
return "invalid";
|
|
9735
|
+
}
|
|
9736
|
+
}
|
|
9737
|
+
function matchersHaveNode9Hook(matchers) {
|
|
9738
|
+
return (matchers ?? []).some((m) => (m.hooks ?? []).some((h) => isNode9Hook(h.command)));
|
|
9739
|
+
}
|
|
9740
|
+
function flatHaveNode9Hook(entries) {
|
|
9741
|
+
return (Array.isArray(entries) ? entries : []).some((h) => isNode9Hook(h.command));
|
|
9742
|
+
}
|
|
9743
|
+
function readHookRoot(filePath, format) {
|
|
9744
|
+
if (!fs14.existsSync(filePath)) return "absent";
|
|
9745
|
+
let raw;
|
|
9746
|
+
try {
|
|
9747
|
+
raw = fs14.readFileSync(filePath, "utf-8");
|
|
9748
|
+
} catch {
|
|
9749
|
+
return "absent";
|
|
9750
|
+
}
|
|
9751
|
+
try {
|
|
9752
|
+
const parsed = format === "yaml" ? yaml2.parse(raw) : JSON.parse(raw);
|
|
9753
|
+
return parsed?.hooks ?? {};
|
|
9754
|
+
} catch {
|
|
9755
|
+
return "invalid";
|
|
9756
|
+
}
|
|
9757
|
+
}
|
|
9758
|
+
function eventWired(root, ev, format) {
|
|
9759
|
+
const arr = root[ev.key];
|
|
9760
|
+
if (format === "matcher") return matchersHaveNode9Hook(arr);
|
|
9761
|
+
return flatHaveNode9Hook(arr);
|
|
9762
|
+
}
|
|
9763
|
+
function detectMcp(servers) {
|
|
9764
|
+
const entries = Object.entries(servers ?? {});
|
|
9765
|
+
const present = entries.some(([, s]) => s?.command === "node9");
|
|
9766
|
+
const wrapped = entries.filter(([, s]) => s?.command === "node9" && Array.isArray(s.args) && s.args.length > 0).map(([name, s]) => `${name} \u2192 ${s.args.join(" ")}`);
|
|
9767
|
+
return { wrapped, present };
|
|
9768
|
+
}
|
|
9769
|
+
function readMcp(filePath, format) {
|
|
9770
|
+
if (!fs14.existsSync(filePath)) return { wrapped: [], present: false };
|
|
9771
|
+
try {
|
|
9772
|
+
if (format === "toml") {
|
|
9773
|
+
const parsed2 = parseToml2(fs14.readFileSync(filePath, "utf-8"));
|
|
9774
|
+
return detectMcp(parsed2?.mcp_servers);
|
|
9775
|
+
}
|
|
9776
|
+
const parsed = readJson2(filePath);
|
|
9777
|
+
if (parsed === null || parsed === "invalid") return { wrapped: [], present: false };
|
|
9778
|
+
return detectMcp(parsed.mcpServers);
|
|
9779
|
+
} catch {
|
|
9780
|
+
return { wrapped: [], present: false };
|
|
9781
|
+
}
|
|
9782
|
+
}
|
|
9783
|
+
function getAgentWiring(home = os13.homedir()) {
|
|
9784
|
+
const detected = detectAgents(home);
|
|
9785
|
+
return AGENT_SPECS.map((spec) => {
|
|
9786
|
+
const present = spec.present(home);
|
|
9787
|
+
const pad = spec.labelPad ?? DEFAULT_LABEL_PAD;
|
|
9788
|
+
let hooks;
|
|
9789
|
+
let wireState;
|
|
9790
|
+
let hookLabel;
|
|
9791
|
+
let settingsPath;
|
|
9792
|
+
if (spec.shimFile) {
|
|
9793
|
+
const shimWired = exists(spec.shimFile(home));
|
|
9794
|
+
hooks = [{ label: "node9 plugin (node9 check)", wired: shimWired }];
|
|
9795
|
+
wireState = shimWired ? "wired" : present ? "unwired" : "absent";
|
|
9796
|
+
hookLabel = "node9 plugin";
|
|
9797
|
+
settingsPath = spec.shimFile(home);
|
|
9798
|
+
} else {
|
|
9799
|
+
const root = spec.hookFile ? readHookRoot(spec.hookFile(home), spec.hookFormat) : "absent";
|
|
9800
|
+
const primary = spec.hookEvents[0];
|
|
9801
|
+
const rootPresent = root !== "absent" && root !== "invalid";
|
|
9802
|
+
hooks = spec.hookEvents.map((ev) => ({
|
|
9803
|
+
label: hookLabelOf(ev, pad),
|
|
9804
|
+
wired: rootPresent && eventWired(root, ev, spec.hookFormat)
|
|
9805
|
+
}));
|
|
9806
|
+
if (root === "absent") wireState = "absent";
|
|
9807
|
+
else if (root === "invalid") wireState = "invalid";
|
|
9808
|
+
else wireState = primary && eventWired(root, primary, spec.hookFormat) ? "wired" : "unwired";
|
|
9809
|
+
hookLabel = primary ? `${primary.key} hook` : "MCP proxy";
|
|
9810
|
+
settingsPath = spec.hookFile ? spec.hookFile(home) : spec.mcpFile ? spec.mcpFile(home) : "";
|
|
9811
|
+
}
|
|
9812
|
+
const mcp = spec.mcpFile ? readMcp(spec.mcpFile(home), spec.mcpFormat ?? "json") : null;
|
|
9813
|
+
const anyHookWired = hooks.some((h) => h.wired);
|
|
9814
|
+
return {
|
|
9815
|
+
id: spec.id,
|
|
9816
|
+
label: spec.label,
|
|
9817
|
+
setupCommand: spec.setupCommand,
|
|
9818
|
+
installed: detected[spec.id],
|
|
9819
|
+
present,
|
|
9820
|
+
hooks,
|
|
9821
|
+
wireState,
|
|
9822
|
+
hookLabel,
|
|
9823
|
+
settingsPath,
|
|
9824
|
+
configFormat: spec.hookFormat === "yaml" ? "YAML" : "JSON",
|
|
9825
|
+
mcpServers: mcp ? mcp.wrapped : null,
|
|
9826
|
+
mcpProtected: mcp ? mcp.present : false,
|
|
9827
|
+
isProtected: anyHookWired || (mcp?.present ?? false)
|
|
9828
|
+
};
|
|
9829
|
+
});
|
|
9830
|
+
}
|
|
9831
|
+
var exists, ck, lg, DEFAULT_LABEL_PAD, hookLabelOf, AGENT_SPECS;
|
|
9832
|
+
var init_agent_wiring = __esm({
|
|
9833
|
+
"src/agent-wiring.ts"() {
|
|
9834
|
+
"use strict";
|
|
9835
|
+
init_setup();
|
|
9836
|
+
exists = (p) => {
|
|
9837
|
+
try {
|
|
9838
|
+
return fs14.existsSync(p);
|
|
9839
|
+
} catch {
|
|
9840
|
+
return false;
|
|
9841
|
+
}
|
|
9842
|
+
};
|
|
9843
|
+
ck = (key) => ({ key, kind: "check" });
|
|
9844
|
+
lg = (key) => ({ key, kind: "log" });
|
|
9845
|
+
DEFAULT_LABEL_PAD = 11;
|
|
9846
|
+
hookLabelOf = (ev, pad) => `${ev.key.padEnd(pad)} (node9 ${ev.kind})`;
|
|
9847
|
+
AGENT_SPECS = [
|
|
9848
|
+
{
|
|
9849
|
+
id: "claude",
|
|
9850
|
+
label: "Claude Code",
|
|
9851
|
+
setupCommand: "node9 agents add claude",
|
|
9852
|
+
hookFile: (h) => path16.join(h, ".claude", "settings.json"),
|
|
9853
|
+
hookFormat: "matcher",
|
|
9854
|
+
hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
|
|
9855
|
+
mcpFile: (h) => path16.join(h, ".claude.json"),
|
|
9856
|
+
present: (h) => exists(path16.join(h, ".claude", "settings.json")) || exists(path16.join(h, ".claude.json"))
|
|
9857
|
+
},
|
|
9858
|
+
{
|
|
9859
|
+
id: "gemini",
|
|
9860
|
+
label: "Gemini CLI",
|
|
9861
|
+
setupCommand: "node9 agents add gemini",
|
|
9862
|
+
hookFile: (h) => path16.join(h, ".gemini", "settings.json"),
|
|
9863
|
+
hookFormat: "matcher",
|
|
9864
|
+
hookEvents: [ck("BeforeTool"), lg("AfterTool")],
|
|
9865
|
+
mcpFile: (h) => path16.join(h, ".gemini", "settings.json"),
|
|
9866
|
+
present: (h) => exists(path16.join(h, ".gemini", "settings.json"))
|
|
9867
|
+
},
|
|
9868
|
+
{
|
|
9869
|
+
id: "codex",
|
|
9870
|
+
label: "Codex",
|
|
9871
|
+
setupCommand: "node9 agents add codex",
|
|
9872
|
+
hookFile: (h) => path16.join(h, ".codex", "hooks.json"),
|
|
9873
|
+
hookFormat: "matcher",
|
|
9874
|
+
hookEvents: [ck("PreToolUse"), ck("UserPromptSubmit")],
|
|
9875
|
+
mcpFile: (h) => path16.join(h, ".codex", "config.toml"),
|
|
9876
|
+
mcpFormat: "toml",
|
|
9877
|
+
present: (h) => exists(path16.join(h, ".codex"))
|
|
9878
|
+
},
|
|
9879
|
+
{
|
|
9880
|
+
id: "antigravity",
|
|
9881
|
+
label: "Antigravity",
|
|
9882
|
+
setupCommand: "node9 agents add antigravity",
|
|
9883
|
+
hookFile: (h) => path16.join(h, ".gemini", "config", "hooks.json"),
|
|
9884
|
+
hookFormat: "matcher",
|
|
9885
|
+
hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
|
|
9886
|
+
mcpFile: (h) => path16.join(h, ".gemini", "config", "mcp_config.json"),
|
|
9887
|
+
present: (h) => exists(path16.join(h, ".gemini", "config", "hooks.json")) || exists(path16.join(h, ".gemini", "antigravity-cli")) || exists(path16.join(h, ".gemini", "antigravity-ide"))
|
|
9888
|
+
},
|
|
9889
|
+
{
|
|
9890
|
+
id: "copilot",
|
|
9891
|
+
label: "GitHub Copilot",
|
|
9892
|
+
setupCommand: "node9 agents add copilot",
|
|
9893
|
+
hookFile: (h) => path16.join(h, ".copilot", "hooks", "node9.json"),
|
|
9894
|
+
hookFormat: "flat",
|
|
9895
|
+
hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
|
|
9896
|
+
mcpFile: (h) => path16.join(h, ".copilot", "mcp-config.json"),
|
|
9897
|
+
present: (h) => exists(path16.join(h, ".copilot"))
|
|
9898
|
+
},
|
|
9899
|
+
{
|
|
9900
|
+
id: "cursor",
|
|
9901
|
+
label: "Cursor",
|
|
9902
|
+
setupCommand: "node9 agents add cursor",
|
|
9903
|
+
// MCP-only — no hook file (see note above).
|
|
9904
|
+
hookFormat: "flat",
|
|
9905
|
+
hookEvents: [],
|
|
9906
|
+
mcpFile: (h) => path16.join(h, ".cursor", "mcp.json"),
|
|
9907
|
+
present: (h) => exists(path16.join(h, ".cursor", "mcp.json"))
|
|
9908
|
+
},
|
|
9909
|
+
{
|
|
9910
|
+
id: "hermes",
|
|
9911
|
+
label: "Hermes Agent",
|
|
9912
|
+
setupCommand: "node9 agents add hermes",
|
|
9913
|
+
hookFile: (h) => hermesConfigPath(h),
|
|
9914
|
+
hookFormat: "yaml",
|
|
9915
|
+
hookEvents: [ck("pre_tool_call"), lg("post_tool_call")],
|
|
9916
|
+
labelPad: 14,
|
|
9917
|
+
// 'post_tool_call' is wider than the default
|
|
9918
|
+
present: (h) => exists(hermesConfigPath(h))
|
|
9919
|
+
},
|
|
9920
|
+
{
|
|
9921
|
+
// Plugin-shim agents — protected by a node9-authored plugin/extension file
|
|
9922
|
+
// (no hooks, no MCP). hookFormat is unused for these (shimFile drives it).
|
|
9923
|
+
id: "opencode",
|
|
9924
|
+
label: "OpenCode",
|
|
9925
|
+
setupCommand: "node9 agents add opencode",
|
|
9926
|
+
hookFormat: "flat",
|
|
9927
|
+
hookEvents: [],
|
|
9928
|
+
shimFile: (h) => path16.join(h, ".config", "opencode", "plugins", "node9.js"),
|
|
9929
|
+
present: (h) => exists(path16.join(h, ".config", "opencode")) || exists(path16.join(h, ".config", "opencode", "plugins", "node9.js"))
|
|
9930
|
+
},
|
|
9931
|
+
{
|
|
9932
|
+
id: "pi",
|
|
9933
|
+
label: "Pi",
|
|
9934
|
+
setupCommand: "node9 agents add pi",
|
|
9935
|
+
hookFormat: "flat",
|
|
9936
|
+
hookEvents: [],
|
|
9937
|
+
shimFile: (h) => path16.join(h, ".pi", "agent", "extensions", "node9.js"),
|
|
9938
|
+
present: (h) => exists(path16.join(h, ".pi", "agent")) || exists(path16.join(h, ".pi", "agent", "extensions", "node9.js"))
|
|
9939
|
+
}
|
|
9940
|
+
];
|
|
9941
|
+
}
|
|
9942
|
+
});
|
|
9943
|
+
|
|
9944
|
+
// src/pricing/litellm.ts
|
|
9945
|
+
import fs15 from "fs";
|
|
9946
|
+
import path17 from "path";
|
|
9947
|
+
import os14 from "os";
|
|
9707
9948
|
function normalizeModel(raw) {
|
|
9708
9949
|
return raw.replace(/-\d{8}$/, "").toLowerCase();
|
|
9709
9950
|
}
|
|
9710
9951
|
function readCache() {
|
|
9711
9952
|
try {
|
|
9712
|
-
const raw = JSON.parse(
|
|
9953
|
+
const raw = JSON.parse(fs15.readFileSync(CACHE_FILE(), "utf-8"));
|
|
9713
9954
|
if (typeof raw.fetchedAt !== "string" || typeof raw.prices !== "object" || raw.prices === null) {
|
|
9714
9955
|
return null;
|
|
9715
9956
|
}
|
|
@@ -9723,18 +9964,18 @@ function readCache() {
|
|
|
9723
9964
|
function writeCache(prices) {
|
|
9724
9965
|
try {
|
|
9725
9966
|
const target = CACHE_FILE();
|
|
9726
|
-
const dir =
|
|
9727
|
-
if (!
|
|
9967
|
+
const dir = path17.dirname(target);
|
|
9968
|
+
if (!fs15.existsSync(dir)) fs15.mkdirSync(dir, { recursive: true });
|
|
9728
9969
|
const tmp = target + ".tmp";
|
|
9729
9970
|
const body = {
|
|
9730
9971
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9731
9972
|
prices
|
|
9732
9973
|
};
|
|
9733
|
-
|
|
9734
|
-
|
|
9974
|
+
fs15.writeFileSync(tmp, JSON.stringify(body) + "\n", "utf-8");
|
|
9975
|
+
fs15.renameSync(tmp, target);
|
|
9735
9976
|
} catch (err2) {
|
|
9736
9977
|
try {
|
|
9737
|
-
|
|
9978
|
+
fs15.appendFileSync(
|
|
9738
9979
|
HOOK_DEBUG_LOG,
|
|
9739
9980
|
`[pricing] cache write failed: ${err2.message}
|
|
9740
9981
|
`
|
|
@@ -9878,7 +10119,7 @@ var init_litellm = __esm({
|
|
|
9878
10119
|
"gemini-2.0-flash": [75e-9, 3e-7, 0, 0],
|
|
9879
10120
|
"gemini-1.5-pro": [125e-8, 5e-6, 0, 0]
|
|
9880
10121
|
};
|
|
9881
|
-
CACHE_FILE = () =>
|
|
10122
|
+
CACHE_FILE = () => path17.join(os14.homedir(), ".node9", "model-pricing.json");
|
|
9882
10123
|
TTL_MS = 24 * 60 * 60 * 1e3;
|
|
9883
10124
|
memCache = null;
|
|
9884
10125
|
memCacheAt = 0;
|
|
@@ -9888,11 +10129,11 @@ var init_litellm = __esm({
|
|
|
9888
10129
|
});
|
|
9889
10130
|
|
|
9890
10131
|
// src/cost-gemini.ts
|
|
9891
|
-
import
|
|
9892
|
-
import
|
|
9893
|
-
import
|
|
10132
|
+
import fs16 from "fs";
|
|
10133
|
+
import os15 from "os";
|
|
10134
|
+
import path18 from "path";
|
|
9894
10135
|
function geminiTmpDir() {
|
|
9895
|
-
return
|
|
10136
|
+
return path18.join(os15.homedir(), ".gemini", "tmp");
|
|
9896
10137
|
}
|
|
9897
10138
|
function geminiPriceFor(model) {
|
|
9898
10139
|
let tuple = pricingFor(model);
|
|
@@ -9907,14 +10148,14 @@ function geminiPriceFor(model) {
|
|
|
9907
10148
|
}
|
|
9908
10149
|
function safeReaddir(dir) {
|
|
9909
10150
|
try {
|
|
9910
|
-
return
|
|
10151
|
+
return fs16.readdirSync(dir);
|
|
9911
10152
|
} catch {
|
|
9912
10153
|
return [];
|
|
9913
10154
|
}
|
|
9914
10155
|
}
|
|
9915
10156
|
function isDir(p) {
|
|
9916
10157
|
try {
|
|
9917
|
-
return
|
|
10158
|
+
return fs16.statSync(p).isDirectory();
|
|
9918
10159
|
} catch {
|
|
9919
10160
|
return false;
|
|
9920
10161
|
}
|
|
@@ -9922,11 +10163,11 @@ function isDir(p) {
|
|
|
9922
10163
|
function listGeminiSessionFiles(base) {
|
|
9923
10164
|
const out = [];
|
|
9924
10165
|
for (const project of safeReaddir(base)) {
|
|
9925
|
-
const chats =
|
|
10166
|
+
const chats = path18.join(base, project, "chats");
|
|
9926
10167
|
if (!isDir(chats)) continue;
|
|
9927
10168
|
for (const f of safeReaddir(chats)) {
|
|
9928
10169
|
if (f.startsWith("session-") && f.endsWith(".jsonl")) {
|
|
9929
|
-
out.push({ file:
|
|
10170
|
+
out.push({ file: path18.join(chats, f), project });
|
|
9930
10171
|
}
|
|
9931
10172
|
}
|
|
9932
10173
|
}
|
|
@@ -9993,7 +10234,7 @@ var init_cost_gemini = __esm({
|
|
|
9993
10234
|
id: "gemini",
|
|
9994
10235
|
available() {
|
|
9995
10236
|
try {
|
|
9996
|
-
return
|
|
10237
|
+
return fs16.existsSync(geminiTmpDir());
|
|
9997
10238
|
} catch {
|
|
9998
10239
|
return false;
|
|
9999
10240
|
}
|
|
@@ -10002,13 +10243,13 @@ var init_cost_gemini = __esm({
|
|
|
10002
10243
|
const combined = /* @__PURE__ */ new Map();
|
|
10003
10244
|
for (const { file, project } of listGeminiSessionFiles(geminiTmpDir())) {
|
|
10004
10245
|
try {
|
|
10005
|
-
if (sinceMs !== void 0 &&
|
|
10246
|
+
if (sinceMs !== void 0 && fs16.statSync(file).mtimeMs < sinceMs) continue;
|
|
10006
10247
|
} catch {
|
|
10007
10248
|
continue;
|
|
10008
10249
|
}
|
|
10009
10250
|
let content;
|
|
10010
10251
|
try {
|
|
10011
|
-
content =
|
|
10252
|
+
content = fs16.readFileSync(file, "utf8");
|
|
10012
10253
|
} catch {
|
|
10013
10254
|
continue;
|
|
10014
10255
|
}
|
|
@@ -10033,11 +10274,11 @@ var init_cost_gemini = __esm({
|
|
|
10033
10274
|
});
|
|
10034
10275
|
|
|
10035
10276
|
// src/cost-codex.ts
|
|
10036
|
-
import
|
|
10037
|
-
import
|
|
10038
|
-
import
|
|
10277
|
+
import fs17 from "fs";
|
|
10278
|
+
import os16 from "os";
|
|
10279
|
+
import path19 from "path";
|
|
10039
10280
|
function codexSessionsDir() {
|
|
10040
|
-
return
|
|
10281
|
+
return path19.join(os16.homedir(), ".codex", "sessions");
|
|
10041
10282
|
}
|
|
10042
10283
|
function codexPriceFor(model) {
|
|
10043
10284
|
return pricingFor(model) ?? CODEX_FALLBACK;
|
|
@@ -10050,16 +10291,16 @@ function codexSessionCost(model, tokens) {
|
|
|
10050
10291
|
function listCodexSessionFiles(base) {
|
|
10051
10292
|
const out = [];
|
|
10052
10293
|
for (const y of safeReaddir2(base)) {
|
|
10053
|
-
const yp =
|
|
10294
|
+
const yp = path19.join(base, y);
|
|
10054
10295
|
if (!isDir2(yp)) continue;
|
|
10055
10296
|
for (const m of safeReaddir2(yp)) {
|
|
10056
|
-
const mp =
|
|
10297
|
+
const mp = path19.join(yp, m);
|
|
10057
10298
|
if (!isDir2(mp)) continue;
|
|
10058
10299
|
for (const d of safeReaddir2(mp)) {
|
|
10059
|
-
const dp =
|
|
10300
|
+
const dp = path19.join(mp, d);
|
|
10060
10301
|
if (!isDir2(dp)) continue;
|
|
10061
10302
|
for (const f of safeReaddir2(dp)) {
|
|
10062
|
-
if (f.endsWith(".jsonl")) out.push(
|
|
10303
|
+
if (f.endsWith(".jsonl")) out.push(path19.join(dp, f));
|
|
10063
10304
|
}
|
|
10064
10305
|
}
|
|
10065
10306
|
}
|
|
@@ -10068,14 +10309,14 @@ function listCodexSessionFiles(base) {
|
|
|
10068
10309
|
}
|
|
10069
10310
|
function safeReaddir2(dir) {
|
|
10070
10311
|
try {
|
|
10071
|
-
return
|
|
10312
|
+
return fs17.readdirSync(dir);
|
|
10072
10313
|
} catch {
|
|
10073
10314
|
return [];
|
|
10074
10315
|
}
|
|
10075
10316
|
}
|
|
10076
10317
|
function isDir2(p) {
|
|
10077
10318
|
try {
|
|
10078
|
-
return
|
|
10319
|
+
return fs17.statSync(p).isDirectory();
|
|
10079
10320
|
} catch {
|
|
10080
10321
|
return false;
|
|
10081
10322
|
}
|
|
@@ -10145,7 +10386,7 @@ var init_cost_codex = __esm({
|
|
|
10145
10386
|
id: "codex",
|
|
10146
10387
|
available() {
|
|
10147
10388
|
try {
|
|
10148
|
-
return
|
|
10389
|
+
return fs17.existsSync(codexSessionsDir());
|
|
10149
10390
|
} catch {
|
|
10150
10391
|
return false;
|
|
10151
10392
|
}
|
|
@@ -10155,13 +10396,13 @@ var init_cost_codex = __esm({
|
|
|
10155
10396
|
const combined = /* @__PURE__ */ new Map();
|
|
10156
10397
|
for (const file of listCodexSessionFiles(base)) {
|
|
10157
10398
|
try {
|
|
10158
|
-
if (sinceMs !== void 0 &&
|
|
10399
|
+
if (sinceMs !== void 0 && fs17.statSync(file).mtimeMs < sinceMs) continue;
|
|
10159
10400
|
} catch {
|
|
10160
10401
|
continue;
|
|
10161
10402
|
}
|
|
10162
10403
|
let content;
|
|
10163
10404
|
try {
|
|
10164
|
-
content =
|
|
10405
|
+
content = fs17.readFileSync(file, "utf8");
|
|
10165
10406
|
} catch {
|
|
10166
10407
|
continue;
|
|
10167
10408
|
}
|
|
@@ -10467,85 +10708,85 @@ var init_scan_summary = __esm({
|
|
|
10467
10708
|
|
|
10468
10709
|
// src/cli/commands/blast.ts
|
|
10469
10710
|
import chalk2 from "chalk";
|
|
10470
|
-
import
|
|
10471
|
-
import
|
|
10472
|
-
import
|
|
10711
|
+
import fs18 from "fs";
|
|
10712
|
+
import path20 from "path";
|
|
10713
|
+
import os17 from "os";
|
|
10473
10714
|
function buildSensitivePaths(home, cwd) {
|
|
10474
10715
|
return [
|
|
10475
10716
|
{
|
|
10476
|
-
full:
|
|
10717
|
+
full: path20.join(home, ".ssh", "id_rsa"),
|
|
10477
10718
|
label: "~/.ssh/id_rsa",
|
|
10478
10719
|
description: "RSA private key \u2014 grants SSH access to your servers",
|
|
10479
10720
|
score: 20
|
|
10480
10721
|
},
|
|
10481
10722
|
{
|
|
10482
|
-
full:
|
|
10723
|
+
full: path20.join(home, ".ssh", "id_ed25519"),
|
|
10483
10724
|
label: "~/.ssh/id_ed25519",
|
|
10484
10725
|
description: "Ed25519 private key \u2014 grants SSH access to your servers",
|
|
10485
10726
|
score: 20
|
|
10486
10727
|
},
|
|
10487
10728
|
{
|
|
10488
|
-
full:
|
|
10729
|
+
full: path20.join(home, ".ssh", "id_ecdsa"),
|
|
10489
10730
|
label: "~/.ssh/id_ecdsa",
|
|
10490
10731
|
description: "ECDSA private key \u2014 grants SSH access to your servers",
|
|
10491
10732
|
score: 20
|
|
10492
10733
|
},
|
|
10493
10734
|
{
|
|
10494
|
-
full:
|
|
10735
|
+
full: path20.join(home, ".aws", "credentials"),
|
|
10495
10736
|
label: "~/.aws/credentials",
|
|
10496
10737
|
description: "AWS access keys \u2014 full cloud account access",
|
|
10497
10738
|
score: 20
|
|
10498
10739
|
},
|
|
10499
10740
|
{
|
|
10500
|
-
full:
|
|
10741
|
+
full: path20.join(home, ".aws", "config"),
|
|
10501
10742
|
label: "~/.aws/config",
|
|
10502
10743
|
description: "AWS configuration \u2014 account and region settings",
|
|
10503
10744
|
score: 5
|
|
10504
10745
|
},
|
|
10505
10746
|
{
|
|
10506
|
-
full:
|
|
10747
|
+
full: path20.join(home, ".config", "gcloud", "credentials.db"),
|
|
10507
10748
|
label: "~/.config/gcloud/credentials.db",
|
|
10508
10749
|
description: "Google Cloud credentials",
|
|
10509
10750
|
score: 15
|
|
10510
10751
|
},
|
|
10511
10752
|
{
|
|
10512
|
-
full:
|
|
10753
|
+
full: path20.join(home, ".docker", "config.json"),
|
|
10513
10754
|
label: "~/.docker/config.json",
|
|
10514
10755
|
description: "Docker registry auth tokens",
|
|
10515
10756
|
score: 10
|
|
10516
10757
|
},
|
|
10517
10758
|
{
|
|
10518
|
-
full:
|
|
10759
|
+
full: path20.join(home, ".netrc"),
|
|
10519
10760
|
label: "~/.netrc",
|
|
10520
10761
|
description: "FTP/HTTP credentials in plain text",
|
|
10521
10762
|
score: 15
|
|
10522
10763
|
},
|
|
10523
10764
|
{
|
|
10524
|
-
full:
|
|
10765
|
+
full: path20.join(home, ".npmrc"),
|
|
10525
10766
|
label: "~/.npmrc",
|
|
10526
10767
|
description: "npm auth token \u2014 can publish packages as you",
|
|
10527
10768
|
score: 10
|
|
10528
10769
|
},
|
|
10529
10770
|
{
|
|
10530
|
-
full:
|
|
10771
|
+
full: path20.join(home, ".node9", "credentials.json"),
|
|
10531
10772
|
label: "~/.node9/credentials.json",
|
|
10532
10773
|
description: "Node9 cloud API key",
|
|
10533
10774
|
score: 10
|
|
10534
10775
|
},
|
|
10535
10776
|
{
|
|
10536
|
-
full:
|
|
10777
|
+
full: path20.join(cwd, ".env"),
|
|
10537
10778
|
label: ".env (current folder)",
|
|
10538
10779
|
description: "App secrets \u2014 database passwords, API keys",
|
|
10539
10780
|
score: 20
|
|
10540
10781
|
},
|
|
10541
10782
|
{
|
|
10542
|
-
full:
|
|
10783
|
+
full: path20.join(cwd, ".env.local"),
|
|
10543
10784
|
label: ".env.local (current folder)",
|
|
10544
10785
|
description: "Local overrides \u2014 often contains real credentials",
|
|
10545
10786
|
score: 15
|
|
10546
10787
|
},
|
|
10547
10788
|
{
|
|
10548
|
-
full:
|
|
10789
|
+
full: path20.join(cwd, ".env.production"),
|
|
10549
10790
|
label: ".env.production (current folder)",
|
|
10550
10791
|
description: "Production secrets",
|
|
10551
10792
|
score: 20
|
|
@@ -10554,7 +10795,7 @@ function buildSensitivePaths(home, cwd) {
|
|
|
10554
10795
|
}
|
|
10555
10796
|
function isReadable(filePath) {
|
|
10556
10797
|
try {
|
|
10557
|
-
|
|
10798
|
+
fs18.accessSync(filePath, fs18.constants.R_OK);
|
|
10558
10799
|
return true;
|
|
10559
10800
|
} catch {
|
|
10560
10801
|
return false;
|
|
@@ -10567,13 +10808,13 @@ function scoreLabel(score) {
|
|
|
10567
10808
|
return chalk2.red.bold(`${score}/100 Critical`);
|
|
10568
10809
|
}
|
|
10569
10810
|
function runBlast() {
|
|
10570
|
-
const home =
|
|
10811
|
+
const home = os17.homedir();
|
|
10571
10812
|
const cwd = process.cwd();
|
|
10572
10813
|
const paths = buildSensitivePaths(home, cwd);
|
|
10573
10814
|
let scoreDeduction = 0;
|
|
10574
10815
|
const reachable = [];
|
|
10575
10816
|
for (const p of paths) {
|
|
10576
|
-
if (
|
|
10817
|
+
if (fs18.existsSync(p.full) && isReadable(p.full)) {
|
|
10577
10818
|
reachable.push(p);
|
|
10578
10819
|
scoreDeduction += p.score;
|
|
10579
10820
|
}
|
|
@@ -10591,7 +10832,7 @@ function runBlast() {
|
|
|
10591
10832
|
}
|
|
10592
10833
|
function registerBlastCommand(program2) {
|
|
10593
10834
|
program2.command("blast").description("Map what an AI agent can currently reach on this machine").action(() => {
|
|
10594
|
-
const home =
|
|
10835
|
+
const home = os17.homedir();
|
|
10595
10836
|
const cwd = process.cwd();
|
|
10596
10837
|
const { reachable, envFindings, score } = runBlast();
|
|
10597
10838
|
console.log("");
|
|
@@ -10767,17 +11008,17 @@ var init_scan_json = __esm({
|
|
|
10767
11008
|
});
|
|
10768
11009
|
|
|
10769
11010
|
// src/cli/render/scan-history.ts
|
|
10770
|
-
import
|
|
10771
|
-
import
|
|
10772
|
-
import
|
|
11011
|
+
import fs19 from "fs";
|
|
11012
|
+
import path21 from "path";
|
|
11013
|
+
import os18 from "os";
|
|
10773
11014
|
function defaultHistoryPath() {
|
|
10774
|
-
return
|
|
11015
|
+
return path21.join(os18.homedir(), ".node9", "scan-history.json");
|
|
10775
11016
|
}
|
|
10776
11017
|
function readPreviousScan(opts = {}) {
|
|
10777
11018
|
const filePath = opts.path ?? defaultHistoryPath();
|
|
10778
11019
|
try {
|
|
10779
|
-
if (!
|
|
10780
|
-
const raw =
|
|
11020
|
+
if (!fs19.existsSync(filePath)) return null;
|
|
11021
|
+
const raw = fs19.readFileSync(filePath, "utf8");
|
|
10781
11022
|
const parsed = JSON.parse(raw);
|
|
10782
11023
|
if (!Array.isArray(parsed) || parsed.length === 0) return null;
|
|
10783
11024
|
const last = parsed[parsed.length - 1];
|
|
@@ -10791,11 +11032,11 @@ function appendScanHistory(record, opts = {}) {
|
|
|
10791
11032
|
const filePath = opts.path ?? defaultHistoryPath();
|
|
10792
11033
|
const cap = opts.cap ?? SCAN_HISTORY_CAP;
|
|
10793
11034
|
try {
|
|
10794
|
-
|
|
11035
|
+
fs19.mkdirSync(path21.dirname(filePath), { recursive: true });
|
|
10795
11036
|
let history = [];
|
|
10796
|
-
if (
|
|
11037
|
+
if (fs19.existsSync(filePath)) {
|
|
10797
11038
|
try {
|
|
10798
|
-
const parsed = JSON.parse(
|
|
11039
|
+
const parsed = JSON.parse(fs19.readFileSync(filePath, "utf8"));
|
|
10799
11040
|
if (Array.isArray(parsed)) {
|
|
10800
11041
|
history = parsed.filter(isValidRecord);
|
|
10801
11042
|
}
|
|
@@ -10806,7 +11047,7 @@ function appendScanHistory(record, opts = {}) {
|
|
|
10806
11047
|
if (history.length > cap) {
|
|
10807
11048
|
history = history.slice(history.length - cap);
|
|
10808
11049
|
}
|
|
10809
|
-
|
|
11050
|
+
fs19.writeFileSync(filePath, JSON.stringify(history, null, 2));
|
|
10810
11051
|
} catch (err2) {
|
|
10811
11052
|
process.stderr.write(
|
|
10812
11053
|
`[node9] Warning: could not write scan-history.json: ${err2.message}
|
|
@@ -10837,15 +11078,15 @@ var init_scan_history = __esm({
|
|
|
10837
11078
|
});
|
|
10838
11079
|
|
|
10839
11080
|
// src/cost-copilot.ts
|
|
10840
|
-
import
|
|
10841
|
-
import
|
|
10842
|
-
import
|
|
11081
|
+
import fs20 from "fs";
|
|
11082
|
+
import os19 from "os";
|
|
11083
|
+
import path22 from "path";
|
|
10843
11084
|
function copilotSessionsDir() {
|
|
10844
|
-
return
|
|
11085
|
+
return path22.join(os19.homedir(), ".copilot", "session-state");
|
|
10845
11086
|
}
|
|
10846
11087
|
function safeReaddir3(dir) {
|
|
10847
11088
|
try {
|
|
10848
|
-
return
|
|
11089
|
+
return fs20.readdirSync(dir);
|
|
10849
11090
|
} catch {
|
|
10850
11091
|
return [];
|
|
10851
11092
|
}
|
|
@@ -10921,7 +11162,7 @@ var init_cost_copilot = __esm({
|
|
|
10921
11162
|
id: "copilot",
|
|
10922
11163
|
available() {
|
|
10923
11164
|
try {
|
|
10924
|
-
return
|
|
11165
|
+
return fs20.existsSync(copilotSessionsDir());
|
|
10925
11166
|
} catch {
|
|
10926
11167
|
return false;
|
|
10927
11168
|
}
|
|
@@ -10930,15 +11171,15 @@ var init_cost_copilot = __esm({
|
|
|
10930
11171
|
const base = copilotSessionsDir();
|
|
10931
11172
|
const combined = /* @__PURE__ */ new Map();
|
|
10932
11173
|
for (const sid of safeReaddir3(base)) {
|
|
10933
|
-
const file =
|
|
11174
|
+
const file = path22.join(base, sid, "events.jsonl");
|
|
10934
11175
|
try {
|
|
10935
|
-
if (sinceMs !== void 0 &&
|
|
11176
|
+
if (sinceMs !== void 0 && fs20.statSync(file).mtimeMs < sinceMs) continue;
|
|
10936
11177
|
} catch {
|
|
10937
11178
|
continue;
|
|
10938
11179
|
}
|
|
10939
11180
|
let content;
|
|
10940
11181
|
try {
|
|
10941
|
-
content =
|
|
11182
|
+
content = fs20.readFileSync(file, "utf8");
|
|
10942
11183
|
} catch {
|
|
10943
11184
|
continue;
|
|
10944
11185
|
}
|
|
@@ -10963,17 +11204,17 @@ var init_cost_copilot = __esm({
|
|
|
10963
11204
|
});
|
|
10964
11205
|
|
|
10965
11206
|
// src/costSync.ts
|
|
10966
|
-
import
|
|
10967
|
-
import
|
|
10968
|
-
import
|
|
11207
|
+
import fs21 from "fs";
|
|
11208
|
+
import path23 from "path";
|
|
11209
|
+
import os20 from "os";
|
|
10969
11210
|
function decodeProjectDirName(dirName) {
|
|
10970
11211
|
return dirName.replace(/-/g, "/");
|
|
10971
11212
|
}
|
|
10972
11213
|
function parseJSONLFile(filePath, fallbackWorkingDir) {
|
|
10973
|
-
const runId =
|
|
11214
|
+
const runId = path23.basename(filePath, ".jsonl");
|
|
10974
11215
|
let content;
|
|
10975
11216
|
try {
|
|
10976
|
-
content =
|
|
11217
|
+
content = fs21.readFileSync(filePath, "utf8");
|
|
10977
11218
|
} catch {
|
|
10978
11219
|
return /* @__PURE__ */ new Map();
|
|
10979
11220
|
}
|
|
@@ -11073,7 +11314,7 @@ async function postCostBatches(apiUrl, apiKey, machineId, entries) {
|
|
|
11073
11314
|
signal: AbortSignal.timeout(15e3)
|
|
11074
11315
|
});
|
|
11075
11316
|
if (!res.ok) {
|
|
11076
|
-
|
|
11317
|
+
fs21.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
|
|
11077
11318
|
`);
|
|
11078
11319
|
} else {
|
|
11079
11320
|
let stored;
|
|
@@ -11083,7 +11324,7 @@ async function postCostBatches(apiUrl, apiKey, machineId, entries) {
|
|
|
11083
11324
|
} catch {
|
|
11084
11325
|
}
|
|
11085
11326
|
if (typeof stored === "number" && stored < batch.length) {
|
|
11086
|
-
|
|
11327
|
+
fs21.appendFileSync(
|
|
11087
11328
|
HOOK_DEBUG_LOG,
|
|
11088
11329
|
`[cost-sync] dropped ${batch.length - stored} of ${batch.length} rows
|
|
11089
11330
|
`
|
|
@@ -11091,7 +11332,7 @@ async function postCostBatches(apiUrl, apiKey, machineId, entries) {
|
|
|
11091
11332
|
}
|
|
11092
11333
|
}
|
|
11093
11334
|
} catch (err2) {
|
|
11094
|
-
|
|
11335
|
+
fs21.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
|
|
11095
11336
|
`);
|
|
11096
11337
|
}
|
|
11097
11338
|
}
|
|
@@ -11104,10 +11345,10 @@ async function syncCost() {
|
|
|
11104
11345
|
if (entries.length === 0) return;
|
|
11105
11346
|
let username = "unknown";
|
|
11106
11347
|
try {
|
|
11107
|
-
username =
|
|
11348
|
+
username = os20.userInfo().username;
|
|
11108
11349
|
} catch {
|
|
11109
11350
|
}
|
|
11110
|
-
const machineId = `${
|
|
11351
|
+
const machineId = `${os20.hostname()}:${username}`;
|
|
11111
11352
|
await postCostBatches(creds.apiUrl, creds.apiKey, machineId, entries);
|
|
11112
11353
|
}
|
|
11113
11354
|
function startCostSync() {
|
|
@@ -11133,37 +11374,37 @@ var init_costSync = __esm({
|
|
|
11133
11374
|
claudeSource = {
|
|
11134
11375
|
id: "claude",
|
|
11135
11376
|
available() {
|
|
11136
|
-
return
|
|
11377
|
+
return fs21.existsSync(path23.join(os20.homedir(), ".claude", "projects"));
|
|
11137
11378
|
},
|
|
11138
11379
|
collect(sinceMs) {
|
|
11139
|
-
const projectsDir =
|
|
11140
|
-
if (!
|
|
11380
|
+
const projectsDir = path23.join(os20.homedir(), ".claude", "projects");
|
|
11381
|
+
if (!fs21.existsSync(projectsDir)) return [];
|
|
11141
11382
|
const combined = /* @__PURE__ */ new Map();
|
|
11142
11383
|
let dirs;
|
|
11143
11384
|
try {
|
|
11144
|
-
dirs =
|
|
11385
|
+
dirs = fs21.readdirSync(projectsDir);
|
|
11145
11386
|
} catch {
|
|
11146
11387
|
return [];
|
|
11147
11388
|
}
|
|
11148
11389
|
for (const dir of dirs) {
|
|
11149
|
-
const dirPath =
|
|
11390
|
+
const dirPath = path23.join(projectsDir, dir);
|
|
11150
11391
|
try {
|
|
11151
|
-
if (!
|
|
11392
|
+
if (!fs21.statSync(dirPath).isDirectory()) continue;
|
|
11152
11393
|
} catch {
|
|
11153
11394
|
continue;
|
|
11154
11395
|
}
|
|
11155
11396
|
let files;
|
|
11156
11397
|
try {
|
|
11157
|
-
files =
|
|
11398
|
+
files = fs21.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
|
|
11158
11399
|
} catch {
|
|
11159
11400
|
continue;
|
|
11160
11401
|
}
|
|
11161
11402
|
const fallbackWorkingDir = decodeProjectDirName(dir);
|
|
11162
11403
|
for (const file of files) {
|
|
11163
|
-
const filePath =
|
|
11404
|
+
const filePath = path23.join(dirPath, file);
|
|
11164
11405
|
if (sinceMs !== void 0) {
|
|
11165
11406
|
try {
|
|
11166
|
-
if (
|
|
11407
|
+
if (fs21.statSync(filePath).mtimeMs < sinceMs) continue;
|
|
11167
11408
|
} catch {
|
|
11168
11409
|
continue;
|
|
11169
11410
|
}
|
|
@@ -11203,9 +11444,9 @@ __export(scan_watermark_exports, {
|
|
|
11203
11444
|
tickForensicBroadcast: () => tickForensicBroadcast,
|
|
11204
11445
|
tickScanWatcher: () => tickScanWatcher
|
|
11205
11446
|
});
|
|
11206
|
-
import
|
|
11207
|
-
import
|
|
11208
|
-
import
|
|
11447
|
+
import fs22 from "fs";
|
|
11448
|
+
import os21 from "os";
|
|
11449
|
+
import path24 from "path";
|
|
11209
11450
|
import readline from "readline";
|
|
11210
11451
|
function freshWatermark() {
|
|
11211
11452
|
return {
|
|
@@ -11218,7 +11459,7 @@ function freshWatermark() {
|
|
|
11218
11459
|
function loadWatermark() {
|
|
11219
11460
|
let raw;
|
|
11220
11461
|
try {
|
|
11221
|
-
raw =
|
|
11462
|
+
raw = fs22.readFileSync(WATERMARK_FILE(), "utf-8");
|
|
11222
11463
|
} catch {
|
|
11223
11464
|
return { status: "fresh", wm: freshWatermark() };
|
|
11224
11465
|
}
|
|
@@ -11270,28 +11511,28 @@ function loadWatermark() {
|
|
|
11270
11511
|
function saveWatermark(wm) {
|
|
11271
11512
|
if (wm.schemaVersion > WATERMARK_SCHEMA_VERSION) return;
|
|
11272
11513
|
const target = WATERMARK_FILE();
|
|
11273
|
-
const dir =
|
|
11274
|
-
if (!
|
|
11514
|
+
const dir = path24.dirname(target);
|
|
11515
|
+
if (!fs22.existsSync(dir)) fs22.mkdirSync(dir, { recursive: true });
|
|
11275
11516
|
const tmp = target + ".tmp";
|
|
11276
|
-
|
|
11277
|
-
|
|
11517
|
+
fs22.writeFileSync(tmp, JSON.stringify(wm, null, 2) + "\n", "utf-8");
|
|
11518
|
+
fs22.renameSync(tmp, target);
|
|
11278
11519
|
}
|
|
11279
11520
|
function listJsonlFiles() {
|
|
11280
11521
|
const root = PROJECTS_DIR();
|
|
11281
|
-
if (!
|
|
11522
|
+
if (!fs22.existsSync(root)) return [];
|
|
11282
11523
|
const out = [];
|
|
11283
|
-
for (const entry of
|
|
11524
|
+
for (const entry of fs22.readdirSync(root, { withFileTypes: true })) {
|
|
11284
11525
|
if (!entry.isDirectory()) continue;
|
|
11285
|
-
const projectDir =
|
|
11526
|
+
const projectDir = path24.join(root, entry.name);
|
|
11286
11527
|
let inner;
|
|
11287
11528
|
try {
|
|
11288
|
-
inner =
|
|
11529
|
+
inner = fs22.readdirSync(projectDir, { withFileTypes: true });
|
|
11289
11530
|
} catch {
|
|
11290
11531
|
continue;
|
|
11291
11532
|
}
|
|
11292
11533
|
for (const file of inner) {
|
|
11293
11534
|
if (file.isFile() && file.name.endsWith(".jsonl")) {
|
|
11294
|
-
out.push(
|
|
11535
|
+
out.push(path24.join(projectDir, file.name));
|
|
11295
11536
|
}
|
|
11296
11537
|
}
|
|
11297
11538
|
}
|
|
@@ -11299,7 +11540,7 @@ function listJsonlFiles() {
|
|
|
11299
11540
|
}
|
|
11300
11541
|
function fileSize(p) {
|
|
11301
11542
|
try {
|
|
11302
|
-
return
|
|
11543
|
+
return fs22.statSync(p).size;
|
|
11303
11544
|
} catch {
|
|
11304
11545
|
return 0;
|
|
11305
11546
|
}
|
|
@@ -11307,7 +11548,7 @@ function fileSize(p) {
|
|
|
11307
11548
|
async function scanDelta(filePath, fromByte, onLine) {
|
|
11308
11549
|
const size = fileSize(filePath);
|
|
11309
11550
|
if (size <= fromByte) return fromByte;
|
|
11310
|
-
const stream =
|
|
11551
|
+
const stream = fs22.createReadStream(filePath, {
|
|
11311
11552
|
start: fromByte,
|
|
11312
11553
|
end: size - 1,
|
|
11313
11554
|
highWaterMark: 64 * 1024
|
|
@@ -11419,7 +11660,7 @@ async function tickForensicBroadcast(offsets) {
|
|
|
11419
11660
|
continue;
|
|
11420
11661
|
}
|
|
11421
11662
|
if (size <= offset) continue;
|
|
11422
|
-
const sessionId =
|
|
11663
|
+
const sessionId = path24.basename(file, ".jsonl");
|
|
11423
11664
|
const newOffset = await scanDelta(file, offset, (obj, lineIndex) => {
|
|
11424
11665
|
out.push(...extractFindingsFromLine(obj, sessionId, lineIndex));
|
|
11425
11666
|
});
|
|
@@ -11478,7 +11719,7 @@ function emptyTick(uploadAs) {
|
|
|
11478
11719
|
function readRawWatermarkPreservingOffsets() {
|
|
11479
11720
|
let raw;
|
|
11480
11721
|
try {
|
|
11481
|
-
raw =
|
|
11722
|
+
raw = fs22.readFileSync(WATERMARK_FILE(), "utf-8");
|
|
11482
11723
|
} catch {
|
|
11483
11724
|
return null;
|
|
11484
11725
|
}
|
|
@@ -11512,13 +11753,13 @@ async function runActualTick(wm) {
|
|
|
11512
11753
|
if (!known) {
|
|
11513
11754
|
let mtimeMs = 0;
|
|
11514
11755
|
try {
|
|
11515
|
-
mtimeMs =
|
|
11756
|
+
mtimeMs = fs22.statSync(filePath).mtime.getTime();
|
|
11516
11757
|
} catch {
|
|
11517
11758
|
continue;
|
|
11518
11759
|
}
|
|
11519
11760
|
if (mtimeMs >= watermarkCreatedAt) {
|
|
11520
11761
|
filesNew++;
|
|
11521
|
-
const sessionId2 =
|
|
11762
|
+
const sessionId2 = path24.basename(filePath, ".jsonl");
|
|
11522
11763
|
const newScannedTo2 = await scanDelta(filePath, 0, (obj, lineIndex) => {
|
|
11523
11764
|
totalToolCalls++;
|
|
11524
11765
|
toolCallsBySession[sessionId2] = (toolCallsBySession[sessionId2] ?? 0) + 1;
|
|
@@ -11536,7 +11777,7 @@ async function runActualTick(wm) {
|
|
|
11536
11777
|
filesSkipped++;
|
|
11537
11778
|
continue;
|
|
11538
11779
|
}
|
|
11539
|
-
const sessionId =
|
|
11780
|
+
const sessionId = path24.basename(filePath, ".jsonl");
|
|
11540
11781
|
const newScannedTo = await scanDelta(filePath, known.scannedTo, (obj, lineIndex) => {
|
|
11541
11782
|
totalToolCalls++;
|
|
11542
11783
|
toolCallsBySession[sessionId] = (toolCallsBySession[sessionId] ?? 0) + 1;
|
|
@@ -11564,8 +11805,8 @@ var init_scan_watermark = __esm({
|
|
|
11564
11805
|
"use strict";
|
|
11565
11806
|
init_dlp();
|
|
11566
11807
|
init_dist();
|
|
11567
|
-
PROJECTS_DIR = () =>
|
|
11568
|
-
WATERMARK_FILE = () =>
|
|
11808
|
+
PROJECTS_DIR = () => path24.join(os21.homedir(), ".claude", "projects");
|
|
11809
|
+
WATERMARK_FILE = () => path24.join(os21.homedir(), ".node9", "scan-watermark.json");
|
|
11569
11810
|
MAX_LINE_BYTES = 2 * 1024 * 1024;
|
|
11570
11811
|
WATERMARK_SCHEMA_VERSION = 2;
|
|
11571
11812
|
LONG_OUTPUT_THRESHOLD_BYTES2 = LONG_OUTPUT_THRESHOLD_BYTES;
|
|
@@ -11581,10 +11822,10 @@ __export(scan_upload_history_exports, {
|
|
|
11581
11822
|
parseSinceCutoff: () => parseSinceCutoff,
|
|
11582
11823
|
runUploadHistory: () => runUploadHistory
|
|
11583
11824
|
});
|
|
11584
|
-
import
|
|
11825
|
+
import fs23 from "fs";
|
|
11585
11826
|
import https from "https";
|
|
11586
|
-
import
|
|
11587
|
-
import
|
|
11827
|
+
import os22 from "os";
|
|
11828
|
+
import path25 from "path";
|
|
11588
11829
|
import chalk4 from "chalk";
|
|
11589
11830
|
function emptySignals2() {
|
|
11590
11831
|
return {
|
|
@@ -11619,40 +11860,40 @@ function parseSinceCutoff(raw, now = /* @__PURE__ */ new Date()) {
|
|
|
11619
11860
|
return now.getTime() - 90 * 864e5;
|
|
11620
11861
|
}
|
|
11621
11862
|
function* iterateJsonlFiles(cutoffMs) {
|
|
11622
|
-
const projectsDir =
|
|
11863
|
+
const projectsDir = path25.join(os22.homedir(), ".claude", "projects");
|
|
11623
11864
|
let dirs;
|
|
11624
11865
|
try {
|
|
11625
|
-
dirs =
|
|
11866
|
+
dirs = fs23.readdirSync(projectsDir);
|
|
11626
11867
|
} catch {
|
|
11627
11868
|
return;
|
|
11628
11869
|
}
|
|
11629
11870
|
for (const dir of dirs) {
|
|
11630
|
-
const dirPath =
|
|
11871
|
+
const dirPath = path25.join(projectsDir, dir);
|
|
11631
11872
|
let stats;
|
|
11632
11873
|
try {
|
|
11633
|
-
stats =
|
|
11874
|
+
stats = fs23.statSync(dirPath);
|
|
11634
11875
|
} catch {
|
|
11635
11876
|
continue;
|
|
11636
11877
|
}
|
|
11637
11878
|
if (!stats.isDirectory()) continue;
|
|
11638
11879
|
let files;
|
|
11639
11880
|
try {
|
|
11640
|
-
files =
|
|
11881
|
+
files = fs23.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
|
|
11641
11882
|
} catch {
|
|
11642
11883
|
continue;
|
|
11643
11884
|
}
|
|
11644
11885
|
for (const file of files) {
|
|
11645
|
-
const filePath =
|
|
11886
|
+
const filePath = path25.join(dirPath, file);
|
|
11646
11887
|
let mtime = 0;
|
|
11647
11888
|
try {
|
|
11648
|
-
mtime =
|
|
11889
|
+
mtime = fs23.statSync(filePath).mtimeMs;
|
|
11649
11890
|
} catch {
|
|
11650
11891
|
continue;
|
|
11651
11892
|
}
|
|
11652
11893
|
if (mtime < cutoffMs) continue;
|
|
11653
11894
|
yield {
|
|
11654
11895
|
filePath,
|
|
11655
|
-
sessionId:
|
|
11896
|
+
sessionId: path25.basename(file, ".jsonl"),
|
|
11656
11897
|
projectDir: dir
|
|
11657
11898
|
};
|
|
11658
11899
|
}
|
|
@@ -11720,7 +11961,7 @@ async function runUploadHistory(opts) {
|
|
|
11720
11961
|
filesScanned++;
|
|
11721
11962
|
let content;
|
|
11722
11963
|
try {
|
|
11723
|
-
content =
|
|
11964
|
+
content = fs23.readFileSync(filePath, "utf8");
|
|
11724
11965
|
} catch {
|
|
11725
11966
|
continue;
|
|
11726
11967
|
}
|
|
@@ -11794,10 +12035,10 @@ async function runUploadHistory(opts) {
|
|
|
11794
12035
|
const costUrl = creds.apiUrl.endsWith("/policies/sync") ? creds.apiUrl.replace(/\/policies\/sync$/, "/cost-sync") : `${creds.apiUrl.replace(/\/$/, "")}/cost-sync`;
|
|
11795
12036
|
let username = "unknown";
|
|
11796
12037
|
try {
|
|
11797
|
-
username =
|
|
12038
|
+
username = os22.userInfo().username;
|
|
11798
12039
|
} catch {
|
|
11799
12040
|
}
|
|
11800
|
-
const machineId = `${
|
|
12041
|
+
const machineId = `${os22.hostname()}:${username}`;
|
|
11801
12042
|
await postJson(costUrl, creds.apiKey, {
|
|
11802
12043
|
machineId,
|
|
11803
12044
|
entries: dailyEntries
|
|
@@ -11871,9 +12112,9 @@ var init_scan_upload_history = __esm({
|
|
|
11871
12112
|
|
|
11872
12113
|
// src/cli/commands/scan.ts
|
|
11873
12114
|
import chalk5 from "chalk";
|
|
11874
|
-
import
|
|
11875
|
-
import
|
|
11876
|
-
import
|
|
12115
|
+
import fs24 from "fs";
|
|
12116
|
+
import path26 from "path";
|
|
12117
|
+
import os23 from "os";
|
|
11877
12118
|
import stringWidth2 from "string-width";
|
|
11878
12119
|
function claudeModelPrice(model) {
|
|
11879
12120
|
const t = pricingFor(model);
|
|
@@ -12096,14 +12337,14 @@ function buildRuleSources() {
|
|
|
12096
12337
|
}
|
|
12097
12338
|
function countScanFiles() {
|
|
12098
12339
|
let total = 0;
|
|
12099
|
-
const claudeDir =
|
|
12100
|
-
if (
|
|
12340
|
+
const claudeDir = path26.join(os23.homedir(), ".claude", "projects");
|
|
12341
|
+
if (fs24.existsSync(claudeDir)) {
|
|
12101
12342
|
try {
|
|
12102
|
-
for (const proj of
|
|
12103
|
-
const p =
|
|
12343
|
+
for (const proj of fs24.readdirSync(claudeDir)) {
|
|
12344
|
+
const p = path26.join(claudeDir, proj);
|
|
12104
12345
|
try {
|
|
12105
|
-
if (!
|
|
12106
|
-
total +=
|
|
12346
|
+
if (!fs24.statSync(p).isDirectory()) continue;
|
|
12347
|
+
total += fs24.readdirSync(p).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).length;
|
|
12107
12348
|
} catch {
|
|
12108
12349
|
continue;
|
|
12109
12350
|
}
|
|
@@ -12111,17 +12352,17 @@ function countScanFiles() {
|
|
|
12111
12352
|
} catch {
|
|
12112
12353
|
}
|
|
12113
12354
|
}
|
|
12114
|
-
const geminiDir =
|
|
12115
|
-
if (
|
|
12355
|
+
const geminiDir = path26.join(os23.homedir(), ".gemini", "tmp");
|
|
12356
|
+
if (fs24.existsSync(geminiDir)) {
|
|
12116
12357
|
try {
|
|
12117
|
-
for (const slug2 of
|
|
12118
|
-
const p =
|
|
12358
|
+
for (const slug2 of fs24.readdirSync(geminiDir)) {
|
|
12359
|
+
const p = path26.join(geminiDir, slug2);
|
|
12119
12360
|
try {
|
|
12120
|
-
if (!
|
|
12121
|
-
const chatsDir =
|
|
12122
|
-
if (
|
|
12361
|
+
if (!fs24.statSync(p).isDirectory()) continue;
|
|
12362
|
+
const chatsDir = path26.join(p, "chats");
|
|
12363
|
+
if (fs24.existsSync(chatsDir)) {
|
|
12123
12364
|
try {
|
|
12124
|
-
total +=
|
|
12365
|
+
total += fs24.readdirSync(chatsDir).filter((f) => f.endsWith(".json")).length;
|
|
12125
12366
|
} catch {
|
|
12126
12367
|
}
|
|
12127
12368
|
}
|
|
@@ -12133,15 +12374,15 @@ function countScanFiles() {
|
|
|
12133
12374
|
}
|
|
12134
12375
|
}
|
|
12135
12376
|
for (const surface of ["antigravity-cli", "antigravity-ide"]) {
|
|
12136
|
-
const brainDir =
|
|
12137
|
-
if (!
|
|
12377
|
+
const brainDir = path26.join(os23.homedir(), ".gemini", surface, "brain");
|
|
12378
|
+
if (!fs24.existsSync(brainDir)) continue;
|
|
12138
12379
|
try {
|
|
12139
|
-
for (const conv of
|
|
12140
|
-
const convPath =
|
|
12380
|
+
for (const conv of fs24.readdirSync(brainDir)) {
|
|
12381
|
+
const convPath = path26.join(brainDir, conv);
|
|
12141
12382
|
try {
|
|
12142
|
-
if (!
|
|
12143
|
-
const logsDir =
|
|
12144
|
-
if (
|
|
12383
|
+
if (!fs24.statSync(convPath).isDirectory()) continue;
|
|
12384
|
+
const logsDir = path26.join(convPath, ".system_generated", "logs");
|
|
12385
|
+
if (fs24.existsSync(path26.join(logsDir, "transcript_full.jsonl")) || fs24.existsSync(path26.join(logsDir, "transcript.jsonl"))) {
|
|
12145
12386
|
total += 1;
|
|
12146
12387
|
}
|
|
12147
12388
|
} catch {
|
|
@@ -12151,31 +12392,31 @@ function countScanFiles() {
|
|
|
12151
12392
|
} catch {
|
|
12152
12393
|
}
|
|
12153
12394
|
}
|
|
12154
|
-
const copilotDir =
|
|
12155
|
-
if (
|
|
12395
|
+
const copilotDir = path26.join(os23.homedir(), ".copilot", "session-state");
|
|
12396
|
+
if (fs24.existsSync(copilotDir)) {
|
|
12156
12397
|
try {
|
|
12157
|
-
for (const sid of
|
|
12158
|
-
if (
|
|
12398
|
+
for (const sid of fs24.readdirSync(copilotDir)) {
|
|
12399
|
+
if (fs24.existsSync(path26.join(copilotDir, sid, "events.jsonl"))) total += 1;
|
|
12159
12400
|
}
|
|
12160
12401
|
} catch {
|
|
12161
12402
|
}
|
|
12162
12403
|
}
|
|
12163
|
-
const codexDir =
|
|
12164
|
-
if (
|
|
12404
|
+
const codexDir = path26.join(os23.homedir(), ".codex", "sessions");
|
|
12405
|
+
if (fs24.existsSync(codexDir)) {
|
|
12165
12406
|
try {
|
|
12166
|
-
for (const year of
|
|
12167
|
-
const yp =
|
|
12407
|
+
for (const year of fs24.readdirSync(codexDir)) {
|
|
12408
|
+
const yp = path26.join(codexDir, year);
|
|
12168
12409
|
try {
|
|
12169
|
-
if (!
|
|
12170
|
-
for (const month of
|
|
12171
|
-
const mp =
|
|
12410
|
+
if (!fs24.statSync(yp).isDirectory()) continue;
|
|
12411
|
+
for (const month of fs24.readdirSync(yp)) {
|
|
12412
|
+
const mp = path26.join(yp, month);
|
|
12172
12413
|
try {
|
|
12173
|
-
if (!
|
|
12174
|
-
for (const day of
|
|
12175
|
-
const dp =
|
|
12414
|
+
if (!fs24.statSync(mp).isDirectory()) continue;
|
|
12415
|
+
for (const day of fs24.readdirSync(mp)) {
|
|
12416
|
+
const dp = path26.join(mp, day);
|
|
12176
12417
|
try {
|
|
12177
|
-
if (!
|
|
12178
|
-
total +=
|
|
12418
|
+
if (!fs24.statSync(dp).isDirectory()) continue;
|
|
12419
|
+
total += fs24.readdirSync(dp).filter((f) => f.endsWith(".jsonl")).length;
|
|
12179
12420
|
} catch {
|
|
12180
12421
|
continue;
|
|
12181
12422
|
}
|
|
@@ -12211,7 +12452,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
12211
12452
|
const sessionId = file.replace(/\.jsonl$/, "");
|
|
12212
12453
|
let raw;
|
|
12213
12454
|
try {
|
|
12214
|
-
raw =
|
|
12455
|
+
raw = fs24.readFileSync(path26.join(projPath, file), "utf-8");
|
|
12215
12456
|
} catch {
|
|
12216
12457
|
return;
|
|
12217
12458
|
}
|
|
@@ -12263,7 +12504,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
12263
12504
|
if (block.type !== "tool_result") continue;
|
|
12264
12505
|
const filePath = block.tool_use_id ? toolUseFilePaths.get(block.tool_use_id) : void 0;
|
|
12265
12506
|
if (filePath) {
|
|
12266
|
-
const ext =
|
|
12507
|
+
const ext = path26.extname(filePath).toLowerCase();
|
|
12267
12508
|
if (CODE_EXTENSIONS.has(ext)) continue;
|
|
12268
12509
|
}
|
|
12269
12510
|
const resultText = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map((c) => c.text ?? "").join("\n") : null;
|
|
@@ -12320,7 +12561,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
12320
12561
|
const rawCmd = String(input.command ?? "").trimStart();
|
|
12321
12562
|
if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd)) continue;
|
|
12322
12563
|
const inputFilePath = typeof input.file_path === "string" ? input.file_path : "";
|
|
12323
|
-
const inputFileExt = inputFilePath ?
|
|
12564
|
+
const inputFileExt = inputFilePath ? path26.extname(inputFilePath).toLowerCase() : "";
|
|
12324
12565
|
if (CODE_EXTENSIONS.has(inputFileExt)) continue;
|
|
12325
12566
|
const dlpMatch = scanArgs(input);
|
|
12326
12567
|
if (dlpMatch) {
|
|
@@ -12417,19 +12658,19 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
12417
12658
|
}
|
|
12418
12659
|
}
|
|
12419
12660
|
function processClaudeProject(proj, projectsDir, ruleSources, startDate, result, dedup, onProgress, onLine) {
|
|
12420
|
-
const projPath =
|
|
12661
|
+
const projPath = path26.join(projectsDir, proj);
|
|
12421
12662
|
try {
|
|
12422
|
-
if (!
|
|
12663
|
+
if (!fs24.statSync(projPath).isDirectory()) return;
|
|
12423
12664
|
} catch {
|
|
12424
12665
|
return;
|
|
12425
12666
|
}
|
|
12426
|
-
const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(
|
|
12667
|
+
const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(os23.homedir(), "~")).slice(
|
|
12427
12668
|
0,
|
|
12428
12669
|
40
|
|
12429
12670
|
);
|
|
12430
12671
|
let files;
|
|
12431
12672
|
try {
|
|
12432
|
-
files =
|
|
12673
|
+
files = fs24.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
12433
12674
|
} catch {
|
|
12434
12675
|
return;
|
|
12435
12676
|
}
|
|
@@ -12463,12 +12704,12 @@ function emptyClaudeScan() {
|
|
|
12463
12704
|
};
|
|
12464
12705
|
}
|
|
12465
12706
|
function scanClaudeHistory(startDate, onProgress, onLine) {
|
|
12466
|
-
const projectsDir =
|
|
12707
|
+
const projectsDir = path26.join(os23.homedir(), ".claude", "projects");
|
|
12467
12708
|
const result = emptyClaudeScan();
|
|
12468
|
-
if (!
|
|
12709
|
+
if (!fs24.existsSync(projectsDir)) return result;
|
|
12469
12710
|
let projDirs;
|
|
12470
12711
|
try {
|
|
12471
|
-
projDirs =
|
|
12712
|
+
projDirs = fs24.readdirSync(projectsDir);
|
|
12472
12713
|
} catch {
|
|
12473
12714
|
return result;
|
|
12474
12715
|
}
|
|
@@ -12489,7 +12730,7 @@ function scanClaudeHistory(startDate, onProgress, onLine) {
|
|
|
12489
12730
|
return result;
|
|
12490
12731
|
}
|
|
12491
12732
|
function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
12492
|
-
const tmpDir =
|
|
12733
|
+
const tmpDir = path26.join(os23.homedir(), ".gemini", "tmp");
|
|
12493
12734
|
const result = {
|
|
12494
12735
|
filesScanned: 0,
|
|
12495
12736
|
sessions: 0,
|
|
@@ -12504,33 +12745,33 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
12504
12745
|
sessionsWithEarlySecrets: 0
|
|
12505
12746
|
};
|
|
12506
12747
|
const dedup = emptyScanDedup();
|
|
12507
|
-
if (!
|
|
12748
|
+
if (!fs24.existsSync(tmpDir)) return result;
|
|
12508
12749
|
let slugDirs;
|
|
12509
12750
|
try {
|
|
12510
|
-
slugDirs =
|
|
12751
|
+
slugDirs = fs24.readdirSync(tmpDir);
|
|
12511
12752
|
} catch {
|
|
12512
12753
|
return result;
|
|
12513
12754
|
}
|
|
12514
12755
|
const ruleSources = buildRuleSources();
|
|
12515
12756
|
for (const slug2 of slugDirs) {
|
|
12516
|
-
const slugPath =
|
|
12757
|
+
const slugPath = path26.join(tmpDir, slug2);
|
|
12517
12758
|
try {
|
|
12518
|
-
if (!
|
|
12759
|
+
if (!fs24.statSync(slugPath).isDirectory()) continue;
|
|
12519
12760
|
} catch {
|
|
12520
12761
|
continue;
|
|
12521
12762
|
}
|
|
12522
12763
|
let projLabel = stripTerminalEscapes(slug2).slice(0, 40);
|
|
12523
12764
|
try {
|
|
12524
12765
|
projLabel = stripTerminalEscapes(
|
|
12525
|
-
|
|
12526
|
-
).replace(
|
|
12766
|
+
fs24.readFileSync(path26.join(slugPath, ".project_root"), "utf-8").trim()
|
|
12767
|
+
).replace(os23.homedir(), "~").slice(0, 40);
|
|
12527
12768
|
} catch {
|
|
12528
12769
|
}
|
|
12529
|
-
const chatsDir =
|
|
12530
|
-
if (!
|
|
12770
|
+
const chatsDir = path26.join(slugPath, "chats");
|
|
12771
|
+
if (!fs24.existsSync(chatsDir)) continue;
|
|
12531
12772
|
let chatFiles;
|
|
12532
12773
|
try {
|
|
12533
|
-
chatFiles =
|
|
12774
|
+
chatFiles = fs24.readdirSync(chatsDir).filter((f) => f.endsWith(".json") || f.endsWith(".jsonl")).sort((a, b) => Number(b.endsWith(".jsonl")) - Number(a.endsWith(".jsonl")));
|
|
12534
12775
|
} catch {
|
|
12535
12776
|
continue;
|
|
12536
12777
|
}
|
|
@@ -12543,7 +12784,7 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
12543
12784
|
onProgress?.(result.filesScanned);
|
|
12544
12785
|
let raw;
|
|
12545
12786
|
try {
|
|
12546
|
-
raw =
|
|
12787
|
+
raw = fs24.readFileSync(path26.join(chatsDir, chatFile), "utf-8");
|
|
12547
12788
|
} catch {
|
|
12548
12789
|
continue;
|
|
12549
12790
|
}
|
|
@@ -12716,13 +12957,13 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
12716
12957
|
return result;
|
|
12717
12958
|
}
|
|
12718
12959
|
function antigravityBrainDirs() {
|
|
12719
|
-
return ["antigravity-cli", "antigravity-ide"].map((surface) =>
|
|
12960
|
+
return ["antigravity-cli", "antigravity-ide"].map((surface) => path26.join(os23.homedir(), ".gemini", surface, "brain")).filter((p) => fs24.existsSync(p));
|
|
12720
12961
|
}
|
|
12721
12962
|
function antigravityTranscriptPath(convPath) {
|
|
12722
|
-
const logsDir =
|
|
12963
|
+
const logsDir = path26.join(convPath, ".system_generated", "logs");
|
|
12723
12964
|
for (const name of ["transcript_full.jsonl", "transcript.jsonl"]) {
|
|
12724
|
-
const p =
|
|
12725
|
-
if (
|
|
12965
|
+
const p = path26.join(logsDir, name);
|
|
12966
|
+
if (fs24.existsSync(p)) return p;
|
|
12726
12967
|
}
|
|
12727
12968
|
return null;
|
|
12728
12969
|
}
|
|
@@ -12748,14 +12989,14 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
12748
12989
|
for (const brainDir of brainDirs) {
|
|
12749
12990
|
let convDirs;
|
|
12750
12991
|
try {
|
|
12751
|
-
convDirs =
|
|
12992
|
+
convDirs = fs24.readdirSync(brainDir);
|
|
12752
12993
|
} catch {
|
|
12753
12994
|
continue;
|
|
12754
12995
|
}
|
|
12755
12996
|
for (const conv of convDirs) {
|
|
12756
|
-
const convPath =
|
|
12997
|
+
const convPath = path26.join(brainDir, conv);
|
|
12757
12998
|
try {
|
|
12758
|
-
if (!
|
|
12999
|
+
if (!fs24.statSync(convPath).isDirectory()) continue;
|
|
12759
13000
|
} catch {
|
|
12760
13001
|
continue;
|
|
12761
13002
|
}
|
|
@@ -12765,7 +13006,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
12765
13006
|
onProgress?.(result.filesScanned);
|
|
12766
13007
|
let raw;
|
|
12767
13008
|
try {
|
|
12768
|
-
raw =
|
|
13009
|
+
raw = fs24.readFileSync(transcriptFile, "utf-8");
|
|
12769
13010
|
} catch {
|
|
12770
13011
|
continue;
|
|
12771
13012
|
}
|
|
@@ -12822,7 +13063,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
12822
13063
|
result.bashCalls++;
|
|
12823
13064
|
const cwd = String(input.cwd ?? "");
|
|
12824
13065
|
if (cwd && projLabel === conv.slice(0, 8)) {
|
|
12825
|
-
projLabel = stripTerminalEscapes(cwd).replace(
|
|
13066
|
+
projLabel = stripTerminalEscapes(cwd).replace(os23.homedir(), "~").slice(0, 40);
|
|
12826
13067
|
}
|
|
12827
13068
|
}
|
|
12828
13069
|
const rawCmd = String(input.command ?? "").trimStart();
|
|
@@ -12922,7 +13163,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
12922
13163
|
return result;
|
|
12923
13164
|
}
|
|
12924
13165
|
function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
12925
|
-
const sessionDir =
|
|
13166
|
+
const sessionDir = path26.join(os23.homedir(), ".copilot", "session-state");
|
|
12926
13167
|
const result = {
|
|
12927
13168
|
filesScanned: 0,
|
|
12928
13169
|
sessions: 0,
|
|
@@ -12938,22 +13179,22 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
12938
13179
|
sessionsWithEarlySecrets: 0
|
|
12939
13180
|
};
|
|
12940
13181
|
const dedup = emptyScanDedup();
|
|
12941
|
-
if (!
|
|
13182
|
+
if (!fs24.existsSync(sessionDir)) return result;
|
|
12942
13183
|
let sessionIds;
|
|
12943
13184
|
try {
|
|
12944
|
-
sessionIds =
|
|
13185
|
+
sessionIds = fs24.readdirSync(sessionDir);
|
|
12945
13186
|
} catch {
|
|
12946
13187
|
return result;
|
|
12947
13188
|
}
|
|
12948
13189
|
const ruleSources = buildRuleSources();
|
|
12949
13190
|
for (const sessionId of sessionIds) {
|
|
12950
|
-
const eventsPath =
|
|
12951
|
-
if (!
|
|
13191
|
+
const eventsPath = path26.join(sessionDir, sessionId, "events.jsonl");
|
|
13192
|
+
if (!fs24.existsSync(eventsPath)) continue;
|
|
12952
13193
|
result.filesScanned++;
|
|
12953
13194
|
onProgress?.(result.filesScanned);
|
|
12954
13195
|
let raw;
|
|
12955
13196
|
try {
|
|
12956
|
-
raw =
|
|
13197
|
+
raw = fs24.readFileSync(eventsPath, "utf-8");
|
|
12957
13198
|
} catch {
|
|
12958
13199
|
continue;
|
|
12959
13200
|
}
|
|
@@ -12973,7 +13214,7 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
12973
13214
|
if (ev.type === "session.start") {
|
|
12974
13215
|
const cwd = ev.data?.context?.cwd;
|
|
12975
13216
|
if (typeof cwd === "string" && cwd) {
|
|
12976
|
-
projLabel = stripTerminalEscapes(cwd).replace(
|
|
13217
|
+
projLabel = stripTerminalEscapes(cwd).replace(os23.homedir(), "~").slice(0, 40);
|
|
12977
13218
|
}
|
|
12978
13219
|
continue;
|
|
12979
13220
|
}
|
|
@@ -13105,7 +13346,7 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
13105
13346
|
return result;
|
|
13106
13347
|
}
|
|
13107
13348
|
function scanCodexHistory(startDate, onProgress, onLine) {
|
|
13108
|
-
const sessionsBase =
|
|
13349
|
+
const sessionsBase = path26.join(os23.homedir(), ".codex", "sessions");
|
|
13109
13350
|
const result = {
|
|
13110
13351
|
filesScanned: 0,
|
|
13111
13352
|
sessions: 0,
|
|
@@ -13120,32 +13361,32 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
13120
13361
|
sessionsWithEarlySecrets: 0
|
|
13121
13362
|
};
|
|
13122
13363
|
const dedup = emptyScanDedup();
|
|
13123
|
-
if (!
|
|
13364
|
+
if (!fs24.existsSync(sessionsBase)) return result;
|
|
13124
13365
|
const jsonlFiles = [];
|
|
13125
13366
|
try {
|
|
13126
|
-
for (const year of
|
|
13127
|
-
const yearPath =
|
|
13367
|
+
for (const year of fs24.readdirSync(sessionsBase)) {
|
|
13368
|
+
const yearPath = path26.join(sessionsBase, year);
|
|
13128
13369
|
try {
|
|
13129
|
-
if (!
|
|
13370
|
+
if (!fs24.statSync(yearPath).isDirectory()) continue;
|
|
13130
13371
|
} catch {
|
|
13131
13372
|
continue;
|
|
13132
13373
|
}
|
|
13133
|
-
for (const month of
|
|
13134
|
-
const monthPath =
|
|
13374
|
+
for (const month of fs24.readdirSync(yearPath)) {
|
|
13375
|
+
const monthPath = path26.join(yearPath, month);
|
|
13135
13376
|
try {
|
|
13136
|
-
if (!
|
|
13377
|
+
if (!fs24.statSync(monthPath).isDirectory()) continue;
|
|
13137
13378
|
} catch {
|
|
13138
13379
|
continue;
|
|
13139
13380
|
}
|
|
13140
|
-
for (const day of
|
|
13141
|
-
const dayPath =
|
|
13381
|
+
for (const day of fs24.readdirSync(monthPath)) {
|
|
13382
|
+
const dayPath = path26.join(monthPath, day);
|
|
13142
13383
|
try {
|
|
13143
|
-
if (!
|
|
13384
|
+
if (!fs24.statSync(dayPath).isDirectory()) continue;
|
|
13144
13385
|
} catch {
|
|
13145
13386
|
continue;
|
|
13146
13387
|
}
|
|
13147
|
-
for (const file of
|
|
13148
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(
|
|
13388
|
+
for (const file of fs24.readdirSync(dayPath)) {
|
|
13389
|
+
if (file.endsWith(".jsonl")) jsonlFiles.push(path26.join(dayPath, file));
|
|
13149
13390
|
}
|
|
13150
13391
|
}
|
|
13151
13392
|
}
|
|
@@ -13159,7 +13400,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
13159
13400
|
onProgress?.(result.filesScanned);
|
|
13160
13401
|
let lines;
|
|
13161
13402
|
try {
|
|
13162
|
-
lines =
|
|
13403
|
+
lines = fs24.readFileSync(filePath, "utf-8").split("\n");
|
|
13163
13404
|
} catch {
|
|
13164
13405
|
continue;
|
|
13165
13406
|
}
|
|
@@ -13186,7 +13427,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
13186
13427
|
sessionId = String(payload["id"] ?? filePath);
|
|
13187
13428
|
startTime = String(payload["timestamp"] ?? "");
|
|
13188
13429
|
const cwd = String(payload["cwd"] ?? "");
|
|
13189
|
-
projLabel = stripTerminalEscapes(cwd.replace(
|
|
13430
|
+
projLabel = stripTerminalEscapes(cwd.replace(os23.homedir(), "~")).slice(0, 40);
|
|
13190
13431
|
continue;
|
|
13191
13432
|
}
|
|
13192
13433
|
if (entry.type === "turn_context" && typeof payload["model"] === "string") {
|
|
@@ -13346,17 +13587,17 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
13346
13587
|
return result;
|
|
13347
13588
|
}
|
|
13348
13589
|
function scanShellConfig() {
|
|
13349
|
-
const home =
|
|
13590
|
+
const home = os23.homedir();
|
|
13350
13591
|
const configFiles = [".zshrc", ".bashrc", ".bash_profile", ".profile"].map(
|
|
13351
|
-
(f) =>
|
|
13592
|
+
(f) => path26.join(home, f)
|
|
13352
13593
|
);
|
|
13353
13594
|
const findings = [];
|
|
13354
13595
|
const seen = /* @__PURE__ */ new Set();
|
|
13355
13596
|
for (const filePath of configFiles) {
|
|
13356
|
-
if (!
|
|
13597
|
+
if (!fs24.existsSync(filePath)) continue;
|
|
13357
13598
|
let lines;
|
|
13358
13599
|
try {
|
|
13359
|
-
lines =
|
|
13600
|
+
lines = fs24.readFileSync(filePath, "utf-8").split("\n");
|
|
13360
13601
|
} catch {
|
|
13361
13602
|
continue;
|
|
13362
13603
|
}
|
|
@@ -14162,7 +14403,7 @@ function registerScanCommand(program2) {
|
|
|
14162
14403
|
if (!drillDown) {
|
|
14163
14404
|
const useInk2 = !options.classic;
|
|
14164
14405
|
if (useInk2) {
|
|
14165
|
-
const scanInkPath =
|
|
14406
|
+
const scanInkPath = path26.join(__dirname, "scan-ink.mjs");
|
|
14166
14407
|
const dynamicImport = new Function("id", "return import(id)");
|
|
14167
14408
|
const mod = await dynamicImport(`file://${scanInkPath}`);
|
|
14168
14409
|
const rangeLabel2 = options.all ? "all time" : `last ${options.days ?? 90} days`;
|
|
@@ -14573,8 +14814,8 @@ var init_suggestion_tracker = __esm({
|
|
|
14573
14814
|
});
|
|
14574
14815
|
|
|
14575
14816
|
// src/daemon/taint-store.ts
|
|
14576
|
-
import
|
|
14577
|
-
import
|
|
14817
|
+
import fs25 from "fs";
|
|
14818
|
+
import path27 from "path";
|
|
14578
14819
|
var DEFAULT_TTL_MS, TaintStore, SESSION_TAINT_TTL_MS, SessionTaintStore;
|
|
14579
14820
|
var init_taint_store = __esm({
|
|
14580
14821
|
"src/daemon/taint-store.ts"() {
|
|
@@ -14644,9 +14885,9 @@ var init_taint_store = __esm({
|
|
|
14644
14885
|
/** Resolve to absolute path, falling back to path.resolve if file doesn't exist yet. */
|
|
14645
14886
|
_resolve(filePath) {
|
|
14646
14887
|
try {
|
|
14647
|
-
return
|
|
14888
|
+
return fs25.realpathSync.native(path27.resolve(filePath));
|
|
14648
14889
|
} catch {
|
|
14649
|
-
return
|
|
14890
|
+
return path27.resolve(filePath);
|
|
14650
14891
|
}
|
|
14651
14892
|
}
|
|
14652
14893
|
};
|
|
@@ -14811,14 +15052,14 @@ var init_session_history = __esm({
|
|
|
14811
15052
|
|
|
14812
15053
|
// src/daemon/state.ts
|
|
14813
15054
|
import net2 from "net";
|
|
14814
|
-
import
|
|
14815
|
-
import
|
|
14816
|
-
import
|
|
15055
|
+
import fs26 from "fs";
|
|
15056
|
+
import path28 from "path";
|
|
15057
|
+
import os24 from "os";
|
|
14817
15058
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
14818
15059
|
function loadInsightCounts() {
|
|
14819
15060
|
try {
|
|
14820
|
-
if (!
|
|
14821
|
-
const data = JSON.parse(
|
|
15061
|
+
if (!fs26.existsSync(INSIGHT_COUNTS_FILE)) return;
|
|
15062
|
+
const data = JSON.parse(fs26.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
|
|
14822
15063
|
for (const [tool, count] of Object.entries(data)) {
|
|
14823
15064
|
if (typeof count === "number" && count > 0) insightCounts.set(tool, count);
|
|
14824
15065
|
}
|
|
@@ -14857,23 +15098,23 @@ function markRejectionHandlerRegistered() {
|
|
|
14857
15098
|
daemonRejectionHandlerRegistered = true;
|
|
14858
15099
|
}
|
|
14859
15100
|
function atomicWriteSync2(filePath, data, options) {
|
|
14860
|
-
const dir =
|
|
14861
|
-
if (!
|
|
15101
|
+
const dir = path28.dirname(filePath);
|
|
15102
|
+
if (!fs26.existsSync(dir)) fs26.mkdirSync(dir, { recursive: true });
|
|
14862
15103
|
const tmpPath = `${filePath}.${randomUUID3()}.tmp`;
|
|
14863
15104
|
try {
|
|
14864
|
-
|
|
15105
|
+
fs26.writeFileSync(tmpPath, data, options);
|
|
14865
15106
|
} catch (err2) {
|
|
14866
15107
|
try {
|
|
14867
|
-
|
|
15108
|
+
fs26.unlinkSync(tmpPath);
|
|
14868
15109
|
} catch {
|
|
14869
15110
|
}
|
|
14870
15111
|
throw err2;
|
|
14871
15112
|
}
|
|
14872
15113
|
try {
|
|
14873
|
-
|
|
15114
|
+
fs26.renameSync(tmpPath, filePath);
|
|
14874
15115
|
} catch (err2) {
|
|
14875
15116
|
try {
|
|
14876
|
-
|
|
15117
|
+
fs26.unlinkSync(tmpPath);
|
|
14877
15118
|
} catch {
|
|
14878
15119
|
}
|
|
14879
15120
|
throw err2;
|
|
@@ -14897,16 +15138,16 @@ function appendAuditLog(data) {
|
|
|
14897
15138
|
decision: data.decision,
|
|
14898
15139
|
source: "daemon"
|
|
14899
15140
|
};
|
|
14900
|
-
const dir =
|
|
14901
|
-
if (!
|
|
14902
|
-
|
|
15141
|
+
const dir = path28.dirname(AUDIT_LOG_FILE);
|
|
15142
|
+
if (!fs26.existsSync(dir)) fs26.mkdirSync(dir, { recursive: true });
|
|
15143
|
+
fs26.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
|
|
14903
15144
|
} catch {
|
|
14904
15145
|
}
|
|
14905
15146
|
}
|
|
14906
15147
|
function getAuditHistory(limit = 20) {
|
|
14907
15148
|
try {
|
|
14908
|
-
if (!
|
|
14909
|
-
const lines =
|
|
15149
|
+
if (!fs26.existsSync(AUDIT_LOG_FILE)) return [];
|
|
15150
|
+
const lines = fs26.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
|
|
14910
15151
|
if (lines.length === 1 && lines[0] === "") return [];
|
|
14911
15152
|
return lines.slice(-limit).map((l) => JSON.parse(l)).reverse();
|
|
14912
15153
|
} catch {
|
|
@@ -14915,7 +15156,7 @@ function getAuditHistory(limit = 20) {
|
|
|
14915
15156
|
}
|
|
14916
15157
|
function getOrgName() {
|
|
14917
15158
|
try {
|
|
14918
|
-
if (
|
|
15159
|
+
if (fs26.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
|
|
14919
15160
|
} catch {
|
|
14920
15161
|
}
|
|
14921
15162
|
return null;
|
|
@@ -14923,8 +15164,8 @@ function getOrgName() {
|
|
|
14923
15164
|
function writeGlobalSetting(key, value) {
|
|
14924
15165
|
let config = {};
|
|
14925
15166
|
try {
|
|
14926
|
-
if (
|
|
14927
|
-
config = JSON.parse(
|
|
15167
|
+
if (fs26.existsSync(GLOBAL_CONFIG_FILE)) {
|
|
15168
|
+
config = JSON.parse(fs26.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
|
|
14928
15169
|
}
|
|
14929
15170
|
} catch {
|
|
14930
15171
|
}
|
|
@@ -14936,8 +15177,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
|
|
|
14936
15177
|
try {
|
|
14937
15178
|
let trust = { entries: [] };
|
|
14938
15179
|
try {
|
|
14939
|
-
if (
|
|
14940
|
-
trust = JSON.parse(
|
|
15180
|
+
if (fs26.existsSync(TRUST_FILE2))
|
|
15181
|
+
trust = JSON.parse(fs26.readFileSync(TRUST_FILE2, "utf-8"));
|
|
14941
15182
|
} catch {
|
|
14942
15183
|
}
|
|
14943
15184
|
trust.entries = trust.entries.filter(
|
|
@@ -14954,8 +15195,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
|
|
|
14954
15195
|
}
|
|
14955
15196
|
function readPersistentDecisions() {
|
|
14956
15197
|
try {
|
|
14957
|
-
if (
|
|
14958
|
-
return JSON.parse(
|
|
15198
|
+
if (fs26.existsSync(DECISIONS_FILE)) {
|
|
15199
|
+
return JSON.parse(fs26.readFileSync(DECISIONS_FILE, "utf-8"));
|
|
14959
15200
|
}
|
|
14960
15201
|
} catch {
|
|
14961
15202
|
}
|
|
@@ -14983,7 +15224,7 @@ function estimateToolCost(tool, args) {
|
|
|
14983
15224
|
const filePath = a.file_path ?? a.path;
|
|
14984
15225
|
if (filePath) {
|
|
14985
15226
|
try {
|
|
14986
|
-
const bytes =
|
|
15227
|
+
const bytes = fs26.statSync(filePath).size;
|
|
14987
15228
|
return bytes / BYTES_PER_TOKEN / 1e6 * INPUT_PRICE_PER_1M;
|
|
14988
15229
|
} catch {
|
|
14989
15230
|
}
|
|
@@ -15054,7 +15295,7 @@ function abandonPending() {
|
|
|
15054
15295
|
});
|
|
15055
15296
|
if (autoStarted) {
|
|
15056
15297
|
try {
|
|
15057
|
-
|
|
15298
|
+
fs26.unlinkSync(DAEMON_PID_FILE);
|
|
15058
15299
|
} catch {
|
|
15059
15300
|
}
|
|
15060
15301
|
setTimeout(() => {
|
|
@@ -15065,8 +15306,8 @@ function abandonPending() {
|
|
|
15065
15306
|
}
|
|
15066
15307
|
function logActivitySocket(msg) {
|
|
15067
15308
|
try {
|
|
15068
|
-
|
|
15069
|
-
|
|
15309
|
+
fs26.appendFileSync(
|
|
15310
|
+
path28.join(homeDir, ".node9", "hook-debug.log"),
|
|
15070
15311
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] [activity-socket] ${msg}
|
|
15071
15312
|
`
|
|
15072
15313
|
);
|
|
@@ -15088,13 +15329,13 @@ function shouldRebind(now = Date.now()) {
|
|
|
15088
15329
|
function startActivitySocket() {
|
|
15089
15330
|
bindActivitySocket();
|
|
15090
15331
|
activityHealthInterval = setInterval(() => {
|
|
15091
|
-
if (!
|
|
15332
|
+
if (!fs26.existsSync(ACTIVITY_SOCKET_PATH2)) attemptRebind("health-probe");
|
|
15092
15333
|
}, ACTIVITY_HEALTH_PROBE_MS);
|
|
15093
15334
|
activityHealthInterval.unref();
|
|
15094
15335
|
process.on("exit", () => {
|
|
15095
15336
|
if (activityHealthInterval) clearInterval(activityHealthInterval);
|
|
15096
15337
|
try {
|
|
15097
|
-
|
|
15338
|
+
fs26.unlinkSync(ACTIVITY_SOCKET_PATH2);
|
|
15098
15339
|
} catch {
|
|
15099
15340
|
}
|
|
15100
15341
|
});
|
|
@@ -15122,7 +15363,7 @@ function attemptRebind(reason) {
|
|
|
15122
15363
|
}
|
|
15123
15364
|
function bindActivitySocket() {
|
|
15124
15365
|
try {
|
|
15125
|
-
|
|
15366
|
+
fs26.unlinkSync(ACTIVITY_SOCKET_PATH2);
|
|
15126
15367
|
} catch {
|
|
15127
15368
|
}
|
|
15128
15369
|
const ACTIVITY_MAX_BYTES = 1024 * 1024;
|
|
@@ -15218,304 +15459,83 @@ function bindActivitySocket() {
|
|
|
15218
15459
|
socket.on("error", () => {
|
|
15219
15460
|
});
|
|
15220
15461
|
});
|
|
15221
|
-
unixServer.on("error", (err2) => {
|
|
15222
|
-
logActivitySocket(`server error: ${err2.message}`);
|
|
15223
|
-
});
|
|
15224
|
-
unixServer.listen(ACTIVITY_SOCKET_PATH2, () => {
|
|
15225
|
-
logActivitySocket(`bound to ${ACTIVITY_SOCKET_PATH2}`);
|
|
15226
|
-
});
|
|
15227
|
-
activitySocketServer = unixServer;
|
|
15228
|
-
}
|
|
15229
|
-
var homeDir, DAEMON_PID_FILE, DECISIONS_FILE, AUDIT_LOG_FILE, TRUST_FILE2, GLOBAL_CONFIG_FILE, CREDENTIALS_FILE, INSIGHT_COUNTS_FILE, pending, sseClients, suggestionTracker, taintStore, sessionTaintStore, insightCounts, _abandonTimer, _hadBrowserClient, _daemonServer, daemonRejectionHandlerRegistered, AUTO_DENY_MS, TRUST_DURATIONS, autoStarted, ACTIVITY_SOCKET_PATH2, ACTIVITY_RING_SIZE, activityRing, LARGE_RESPONSE_RING_SIZE, largeResponseRing, cachedScanResult, cachedScanTs, SCAN_CACHE_TTL_MS, SECRET_KEY_RE, INPUT_PRICE_PER_1M, OUTPUT_PRICE_PER_1M, BYTES_PER_TOKEN, CRITICAL_FORENSIC_CATEGORIES, WRITE_TOOL_NAMES, ACTIVITY_REBIND_MAX_ATTEMPTS, ACTIVITY_REBIND_WINDOW_MS, ACTIVITY_HEALTH_PROBE_MS, activitySocketServer, activityHealthInterval, activityRebindAttempts, activityCircuitTripped;
|
|
15230
|
-
var init_state2 = __esm({
|
|
15231
|
-
"src/daemon/state.ts"() {
|
|
15232
|
-
"use strict";
|
|
15233
|
-
init_daemon();
|
|
15234
|
-
init_suggestion_tracker();
|
|
15235
|
-
init_taint_store();
|
|
15236
|
-
init_session_counters();
|
|
15237
|
-
init_session_history();
|
|
15238
|
-
homeDir =
|
|
15239
|
-
DAEMON_PID_FILE =
|
|
15240
|
-
DECISIONS_FILE =
|
|
15241
|
-
AUDIT_LOG_FILE =
|
|
15242
|
-
TRUST_FILE2 =
|
|
15243
|
-
GLOBAL_CONFIG_FILE =
|
|
15244
|
-
CREDENTIALS_FILE =
|
|
15245
|
-
INSIGHT_COUNTS_FILE =
|
|
15246
|
-
pending = /* @__PURE__ */ new Map();
|
|
15247
|
-
sseClients = /* @__PURE__ */ new Set();
|
|
15248
|
-
suggestionTracker = new SuggestionTracker(3);
|
|
15249
|
-
taintStore = new TaintStore();
|
|
15250
|
-
sessionTaintStore = new SessionTaintStore();
|
|
15251
|
-
insightCounts = /* @__PURE__ */ new Map();
|
|
15252
|
-
_abandonTimer = null;
|
|
15253
|
-
_hadBrowserClient = false;
|
|
15254
|
-
_daemonServer = null;
|
|
15255
|
-
daemonRejectionHandlerRegistered = false;
|
|
15256
|
-
AUTO_DENY_MS = 12e4;
|
|
15257
|
-
TRUST_DURATIONS = {
|
|
15258
|
-
"30m": 30 * 6e4,
|
|
15259
|
-
"1h": 60 * 6e4,
|
|
15260
|
-
"2h": 2 * 60 * 6e4
|
|
15261
|
-
};
|
|
15262
|
-
autoStarted = process.env.NODE9_AUTO_STARTED === "1";
|
|
15263
|
-
ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" :
|
|
15264
|
-
ACTIVITY_RING_SIZE = 100;
|
|
15265
|
-
activityRing = [];
|
|
15266
|
-
LARGE_RESPONSE_RING_SIZE = 20;
|
|
15267
|
-
largeResponseRing = [];
|
|
15268
|
-
cachedScanResult = null;
|
|
15269
|
-
cachedScanTs = 0;
|
|
15270
|
-
SCAN_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
15271
|
-
SECRET_KEY_RE = /password|secret|token|key|apikey|credential|auth/i;
|
|
15272
|
-
INPUT_PRICE_PER_1M = 3;
|
|
15273
|
-
OUTPUT_PRICE_PER_1M = 15;
|
|
15274
|
-
BYTES_PER_TOKEN = 4;
|
|
15275
|
-
CRITICAL_FORENSIC_CATEGORIES = /* @__PURE__ */ new Set([
|
|
15276
|
-
"privilege-escalation",
|
|
15277
|
-
"destructive-op",
|
|
15278
|
-
"eval-of-remote"
|
|
15279
|
-
]);
|
|
15280
|
-
WRITE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
15281
|
-
"write",
|
|
15282
|
-
"write_file",
|
|
15283
|
-
"create_file",
|
|
15284
|
-
"edit",
|
|
15285
|
-
"multiedit",
|
|
15286
|
-
"str_replace_based_edit_tool",
|
|
15287
|
-
"replace",
|
|
15288
|
-
"notebook_edit",
|
|
15289
|
-
"notebookedit"
|
|
15290
|
-
]);
|
|
15291
|
-
ACTIVITY_REBIND_MAX_ATTEMPTS = 5;
|
|
15292
|
-
ACTIVITY_REBIND_WINDOW_MS = 6e4;
|
|
15293
|
-
ACTIVITY_HEALTH_PROBE_MS = 2e3;
|
|
15294
|
-
activitySocketServer = null;
|
|
15295
|
-
activityHealthInterval = null;
|
|
15296
|
-
activityRebindAttempts = [];
|
|
15297
|
-
activityCircuitTripped = false;
|
|
15298
|
-
}
|
|
15299
|
-
});
|
|
15300
|
-
|
|
15301
|
-
// src/agent-wiring.ts
|
|
15302
|
-
import fs26 from "fs";
|
|
15303
|
-
import path28 from "path";
|
|
15304
|
-
import os24 from "os";
|
|
15305
|
-
import * as yaml2 from "yaml";
|
|
15306
|
-
import { parse as parseToml2 } from "smol-toml";
|
|
15307
|
-
function readJson2(filePath) {
|
|
15308
|
-
if (!fs26.existsSync(filePath)) return null;
|
|
15309
|
-
try {
|
|
15310
|
-
return JSON.parse(fs26.readFileSync(filePath, "utf-8"));
|
|
15311
|
-
} catch {
|
|
15312
|
-
return "invalid";
|
|
15313
|
-
}
|
|
15314
|
-
}
|
|
15315
|
-
function matchersHaveNode9Hook(matchers) {
|
|
15316
|
-
return (matchers ?? []).some((m) => (m.hooks ?? []).some((h) => isNode9Hook(h.command)));
|
|
15317
|
-
}
|
|
15318
|
-
function flatHaveNode9Hook(entries) {
|
|
15319
|
-
return (Array.isArray(entries) ? entries : []).some((h) => isNode9Hook(h.command));
|
|
15320
|
-
}
|
|
15321
|
-
function readHookRoot(filePath, format) {
|
|
15322
|
-
if (!fs26.existsSync(filePath)) return "absent";
|
|
15323
|
-
let raw;
|
|
15324
|
-
try {
|
|
15325
|
-
raw = fs26.readFileSync(filePath, "utf-8");
|
|
15326
|
-
} catch {
|
|
15327
|
-
return "absent";
|
|
15328
|
-
}
|
|
15329
|
-
try {
|
|
15330
|
-
const parsed = format === "yaml" ? yaml2.parse(raw) : JSON.parse(raw);
|
|
15331
|
-
return parsed?.hooks ?? {};
|
|
15332
|
-
} catch {
|
|
15333
|
-
return "invalid";
|
|
15334
|
-
}
|
|
15335
|
-
}
|
|
15336
|
-
function eventWired(root, ev, format) {
|
|
15337
|
-
const arr = root[ev.key];
|
|
15338
|
-
if (format === "matcher") return matchersHaveNode9Hook(arr);
|
|
15339
|
-
return flatHaveNode9Hook(arr);
|
|
15340
|
-
}
|
|
15341
|
-
function detectMcp(servers) {
|
|
15342
|
-
const entries = Object.entries(servers ?? {});
|
|
15343
|
-
const present = entries.some(([, s]) => s?.command === "node9");
|
|
15344
|
-
const wrapped = entries.filter(([, s]) => s?.command === "node9" && Array.isArray(s.args) && s.args.length > 0).map(([name, s]) => `${name} \u2192 ${s.args.join(" ")}`);
|
|
15345
|
-
return { wrapped, present };
|
|
15346
|
-
}
|
|
15347
|
-
function readMcp(filePath, format) {
|
|
15348
|
-
if (!fs26.existsSync(filePath)) return { wrapped: [], present: false };
|
|
15349
|
-
try {
|
|
15350
|
-
if (format === "toml") {
|
|
15351
|
-
const parsed2 = parseToml2(fs26.readFileSync(filePath, "utf-8"));
|
|
15352
|
-
return detectMcp(parsed2?.mcp_servers);
|
|
15353
|
-
}
|
|
15354
|
-
const parsed = readJson2(filePath);
|
|
15355
|
-
if (parsed === null || parsed === "invalid") return { wrapped: [], present: false };
|
|
15356
|
-
return detectMcp(parsed.mcpServers);
|
|
15357
|
-
} catch {
|
|
15358
|
-
return { wrapped: [], present: false };
|
|
15359
|
-
}
|
|
15360
|
-
}
|
|
15361
|
-
function getAgentWiring(home = os24.homedir()) {
|
|
15362
|
-
const detected = detectAgents(home);
|
|
15363
|
-
return AGENT_SPECS.map((spec) => {
|
|
15364
|
-
const present = spec.present(home);
|
|
15365
|
-
const pad = spec.labelPad ?? DEFAULT_LABEL_PAD;
|
|
15366
|
-
let hooks;
|
|
15367
|
-
let wireState;
|
|
15368
|
-
let hookLabel;
|
|
15369
|
-
let settingsPath;
|
|
15370
|
-
if (spec.shimFile) {
|
|
15371
|
-
const shimWired = exists(spec.shimFile(home));
|
|
15372
|
-
hooks = [{ label: "node9 plugin (node9 check)", wired: shimWired }];
|
|
15373
|
-
wireState = shimWired ? "wired" : present ? "unwired" : "absent";
|
|
15374
|
-
hookLabel = "node9 plugin";
|
|
15375
|
-
settingsPath = spec.shimFile(home);
|
|
15376
|
-
} else {
|
|
15377
|
-
const root = spec.hookFile ? readHookRoot(spec.hookFile(home), spec.hookFormat) : "absent";
|
|
15378
|
-
const primary = spec.hookEvents[0];
|
|
15379
|
-
const rootPresent = root !== "absent" && root !== "invalid";
|
|
15380
|
-
hooks = spec.hookEvents.map((ev) => ({
|
|
15381
|
-
label: hookLabelOf(ev, pad),
|
|
15382
|
-
wired: rootPresent && eventWired(root, ev, spec.hookFormat)
|
|
15383
|
-
}));
|
|
15384
|
-
if (root === "absent") wireState = "absent";
|
|
15385
|
-
else if (root === "invalid") wireState = "invalid";
|
|
15386
|
-
else wireState = primary && eventWired(root, primary, spec.hookFormat) ? "wired" : "unwired";
|
|
15387
|
-
hookLabel = primary ? `${primary.key} hook` : "MCP proxy";
|
|
15388
|
-
settingsPath = spec.hookFile ? spec.hookFile(home) : spec.mcpFile ? spec.mcpFile(home) : "";
|
|
15389
|
-
}
|
|
15390
|
-
const mcp = spec.mcpFile ? readMcp(spec.mcpFile(home), spec.mcpFormat ?? "json") : null;
|
|
15391
|
-
const anyHookWired = hooks.some((h) => h.wired);
|
|
15392
|
-
return {
|
|
15393
|
-
id: spec.id,
|
|
15394
|
-
label: spec.label,
|
|
15395
|
-
setupCommand: spec.setupCommand,
|
|
15396
|
-
installed: detected[spec.id],
|
|
15397
|
-
present,
|
|
15398
|
-
hooks,
|
|
15399
|
-
wireState,
|
|
15400
|
-
hookLabel,
|
|
15401
|
-
settingsPath,
|
|
15402
|
-
configFormat: spec.hookFormat === "yaml" ? "YAML" : "JSON",
|
|
15403
|
-
mcpServers: mcp ? mcp.wrapped : null,
|
|
15404
|
-
mcpProtected: mcp ? mcp.present : false,
|
|
15405
|
-
isProtected: anyHookWired || (mcp?.present ?? false)
|
|
15406
|
-
};
|
|
15407
|
-
});
|
|
15408
|
-
}
|
|
15409
|
-
var exists, ck, lg, DEFAULT_LABEL_PAD, hookLabelOf, AGENT_SPECS;
|
|
15410
|
-
var init_agent_wiring = __esm({
|
|
15411
|
-
"src/agent-wiring.ts"() {
|
|
15412
|
-
"use strict";
|
|
15413
|
-
init_setup();
|
|
15414
|
-
exists = (p) => {
|
|
15415
|
-
try {
|
|
15416
|
-
return fs26.existsSync(p);
|
|
15417
|
-
} catch {
|
|
15418
|
-
return false;
|
|
15419
|
-
}
|
|
15420
|
-
};
|
|
15421
|
-
ck = (key) => ({ key, kind: "check" });
|
|
15422
|
-
lg = (key) => ({ key, kind: "log" });
|
|
15423
|
-
DEFAULT_LABEL_PAD = 11;
|
|
15424
|
-
hookLabelOf = (ev, pad) => `${ev.key.padEnd(pad)} (node9 ${ev.kind})`;
|
|
15425
|
-
AGENT_SPECS = [
|
|
15426
|
-
{
|
|
15427
|
-
id: "claude",
|
|
15428
|
-
label: "Claude Code",
|
|
15429
|
-
setupCommand: "node9 agents add claude",
|
|
15430
|
-
hookFile: (h) => path28.join(h, ".claude", "settings.json"),
|
|
15431
|
-
hookFormat: "matcher",
|
|
15432
|
-
hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
|
|
15433
|
-
mcpFile: (h) => path28.join(h, ".claude.json"),
|
|
15434
|
-
present: (h) => exists(path28.join(h, ".claude", "settings.json")) || exists(path28.join(h, ".claude.json"))
|
|
15435
|
-
},
|
|
15436
|
-
{
|
|
15437
|
-
id: "gemini",
|
|
15438
|
-
label: "Gemini CLI",
|
|
15439
|
-
setupCommand: "node9 agents add gemini",
|
|
15440
|
-
hookFile: (h) => path28.join(h, ".gemini", "settings.json"),
|
|
15441
|
-
hookFormat: "matcher",
|
|
15442
|
-
hookEvents: [ck("BeforeTool"), lg("AfterTool")],
|
|
15443
|
-
mcpFile: (h) => path28.join(h, ".gemini", "settings.json"),
|
|
15444
|
-
present: (h) => exists(path28.join(h, ".gemini", "settings.json"))
|
|
15445
|
-
},
|
|
15446
|
-
{
|
|
15447
|
-
id: "codex",
|
|
15448
|
-
label: "Codex",
|
|
15449
|
-
setupCommand: "node9 agents add codex",
|
|
15450
|
-
hookFile: (h) => path28.join(h, ".codex", "hooks.json"),
|
|
15451
|
-
hookFormat: "matcher",
|
|
15452
|
-
hookEvents: [ck("PreToolUse"), ck("UserPromptSubmit")],
|
|
15453
|
-
mcpFile: (h) => path28.join(h, ".codex", "config.toml"),
|
|
15454
|
-
mcpFormat: "toml",
|
|
15455
|
-
present: (h) => exists(path28.join(h, ".codex"))
|
|
15456
|
-
},
|
|
15457
|
-
{
|
|
15458
|
-
id: "antigravity",
|
|
15459
|
-
label: "Antigravity",
|
|
15460
|
-
setupCommand: "node9 agents add antigravity",
|
|
15461
|
-
hookFile: (h) => path28.join(h, ".gemini", "config", "hooks.json"),
|
|
15462
|
-
hookFormat: "matcher",
|
|
15463
|
-
hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
|
|
15464
|
-
mcpFile: (h) => path28.join(h, ".gemini", "config", "mcp_config.json"),
|
|
15465
|
-
present: (h) => exists(path28.join(h, ".gemini", "config", "hooks.json")) || exists(path28.join(h, ".gemini", "antigravity-cli")) || exists(path28.join(h, ".gemini", "antigravity-ide"))
|
|
15466
|
-
},
|
|
15467
|
-
{
|
|
15468
|
-
id: "copilot",
|
|
15469
|
-
label: "GitHub Copilot",
|
|
15470
|
-
setupCommand: "node9 agents add copilot",
|
|
15471
|
-
hookFile: (h) => path28.join(h, ".copilot", "hooks", "node9.json"),
|
|
15472
|
-
hookFormat: "flat",
|
|
15473
|
-
hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
|
|
15474
|
-
mcpFile: (h) => path28.join(h, ".copilot", "mcp-config.json"),
|
|
15475
|
-
present: (h) => exists(path28.join(h, ".copilot"))
|
|
15476
|
-
},
|
|
15477
|
-
{
|
|
15478
|
-
id: "cursor",
|
|
15479
|
-
label: "Cursor",
|
|
15480
|
-
setupCommand: "node9 agents add cursor",
|
|
15481
|
-
// MCP-only — no hook file (see note above).
|
|
15482
|
-
hookFormat: "flat",
|
|
15483
|
-
hookEvents: [],
|
|
15484
|
-
mcpFile: (h) => path28.join(h, ".cursor", "mcp.json"),
|
|
15485
|
-
present: (h) => exists(path28.join(h, ".cursor", "mcp.json"))
|
|
15486
|
-
},
|
|
15487
|
-
{
|
|
15488
|
-
id: "hermes",
|
|
15489
|
-
label: "Hermes Agent",
|
|
15490
|
-
setupCommand: "node9 agents add hermes",
|
|
15491
|
-
hookFile: (h) => hermesConfigPath(h),
|
|
15492
|
-
hookFormat: "yaml",
|
|
15493
|
-
hookEvents: [ck("pre_tool_call"), lg("post_tool_call")],
|
|
15494
|
-
labelPad: 14,
|
|
15495
|
-
// 'post_tool_call' is wider than the default
|
|
15496
|
-
present: (h) => exists(hermesConfigPath(h))
|
|
15497
|
-
},
|
|
15498
|
-
{
|
|
15499
|
-
// Plugin-shim agents — protected by a node9-authored plugin/extension file
|
|
15500
|
-
// (no hooks, no MCP). hookFormat is unused for these (shimFile drives it).
|
|
15501
|
-
id: "opencode",
|
|
15502
|
-
label: "OpenCode",
|
|
15503
|
-
setupCommand: "node9 agents add opencode",
|
|
15504
|
-
hookFormat: "flat",
|
|
15505
|
-
hookEvents: [],
|
|
15506
|
-
shimFile: (h) => path28.join(h, ".config", "opencode", "plugins", "node9.js"),
|
|
15507
|
-
present: (h) => exists(path28.join(h, ".config", "opencode")) || exists(path28.join(h, ".config", "opencode", "plugins", "node9.js"))
|
|
15508
|
-
},
|
|
15509
|
-
{
|
|
15510
|
-
id: "pi",
|
|
15511
|
-
label: "Pi",
|
|
15512
|
-
setupCommand: "node9 agents add pi",
|
|
15513
|
-
hookFormat: "flat",
|
|
15514
|
-
hookEvents: [],
|
|
15515
|
-
shimFile: (h) => path28.join(h, ".pi", "agent", "extensions", "node9.js"),
|
|
15516
|
-
present: (h) => exists(path28.join(h, ".pi", "agent")) || exists(path28.join(h, ".pi", "agent", "extensions", "node9.js"))
|
|
15517
|
-
}
|
|
15518
|
-
];
|
|
15462
|
+
unixServer.on("error", (err2) => {
|
|
15463
|
+
logActivitySocket(`server error: ${err2.message}`);
|
|
15464
|
+
});
|
|
15465
|
+
unixServer.listen(ACTIVITY_SOCKET_PATH2, () => {
|
|
15466
|
+
logActivitySocket(`bound to ${ACTIVITY_SOCKET_PATH2}`);
|
|
15467
|
+
});
|
|
15468
|
+
activitySocketServer = unixServer;
|
|
15469
|
+
}
|
|
15470
|
+
var homeDir, DAEMON_PID_FILE, DECISIONS_FILE, AUDIT_LOG_FILE, TRUST_FILE2, GLOBAL_CONFIG_FILE, CREDENTIALS_FILE, INSIGHT_COUNTS_FILE, pending, sseClients, suggestionTracker, taintStore, sessionTaintStore, insightCounts, _abandonTimer, _hadBrowserClient, _daemonServer, daemonRejectionHandlerRegistered, AUTO_DENY_MS, TRUST_DURATIONS, autoStarted, ACTIVITY_SOCKET_PATH2, ACTIVITY_RING_SIZE, activityRing, LARGE_RESPONSE_RING_SIZE, largeResponseRing, cachedScanResult, cachedScanTs, SCAN_CACHE_TTL_MS, SECRET_KEY_RE, INPUT_PRICE_PER_1M, OUTPUT_PRICE_PER_1M, BYTES_PER_TOKEN, CRITICAL_FORENSIC_CATEGORIES, WRITE_TOOL_NAMES, ACTIVITY_REBIND_MAX_ATTEMPTS, ACTIVITY_REBIND_WINDOW_MS, ACTIVITY_HEALTH_PROBE_MS, activitySocketServer, activityHealthInterval, activityRebindAttempts, activityCircuitTripped;
|
|
15471
|
+
var init_state2 = __esm({
|
|
15472
|
+
"src/daemon/state.ts"() {
|
|
15473
|
+
"use strict";
|
|
15474
|
+
init_daemon();
|
|
15475
|
+
init_suggestion_tracker();
|
|
15476
|
+
init_taint_store();
|
|
15477
|
+
init_session_counters();
|
|
15478
|
+
init_session_history();
|
|
15479
|
+
homeDir = os24.homedir();
|
|
15480
|
+
DAEMON_PID_FILE = path28.join(homeDir, ".node9", "daemon.pid");
|
|
15481
|
+
DECISIONS_FILE = path28.join(homeDir, ".node9", "decisions.json");
|
|
15482
|
+
AUDIT_LOG_FILE = path28.join(homeDir, ".node9", "audit.log");
|
|
15483
|
+
TRUST_FILE2 = path28.join(homeDir, ".node9", "trust.json");
|
|
15484
|
+
GLOBAL_CONFIG_FILE = path28.join(homeDir, ".node9", "config.json");
|
|
15485
|
+
CREDENTIALS_FILE = path28.join(homeDir, ".node9", "credentials.json");
|
|
15486
|
+
INSIGHT_COUNTS_FILE = path28.join(homeDir, ".node9", "insight-counts.json");
|
|
15487
|
+
pending = /* @__PURE__ */ new Map();
|
|
15488
|
+
sseClients = /* @__PURE__ */ new Set();
|
|
15489
|
+
suggestionTracker = new SuggestionTracker(3);
|
|
15490
|
+
taintStore = new TaintStore();
|
|
15491
|
+
sessionTaintStore = new SessionTaintStore();
|
|
15492
|
+
insightCounts = /* @__PURE__ */ new Map();
|
|
15493
|
+
_abandonTimer = null;
|
|
15494
|
+
_hadBrowserClient = false;
|
|
15495
|
+
_daemonServer = null;
|
|
15496
|
+
daemonRejectionHandlerRegistered = false;
|
|
15497
|
+
AUTO_DENY_MS = 12e4;
|
|
15498
|
+
TRUST_DURATIONS = {
|
|
15499
|
+
"30m": 30 * 6e4,
|
|
15500
|
+
"1h": 60 * 6e4,
|
|
15501
|
+
"2h": 2 * 60 * 6e4
|
|
15502
|
+
};
|
|
15503
|
+
autoStarted = process.env.NODE9_AUTO_STARTED === "1";
|
|
15504
|
+
ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path28.join(os24.tmpdir(), "node9-activity.sock");
|
|
15505
|
+
ACTIVITY_RING_SIZE = 100;
|
|
15506
|
+
activityRing = [];
|
|
15507
|
+
LARGE_RESPONSE_RING_SIZE = 20;
|
|
15508
|
+
largeResponseRing = [];
|
|
15509
|
+
cachedScanResult = null;
|
|
15510
|
+
cachedScanTs = 0;
|
|
15511
|
+
SCAN_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
15512
|
+
SECRET_KEY_RE = /password|secret|token|key|apikey|credential|auth/i;
|
|
15513
|
+
INPUT_PRICE_PER_1M = 3;
|
|
15514
|
+
OUTPUT_PRICE_PER_1M = 15;
|
|
15515
|
+
BYTES_PER_TOKEN = 4;
|
|
15516
|
+
CRITICAL_FORENSIC_CATEGORIES = /* @__PURE__ */ new Set([
|
|
15517
|
+
"privilege-escalation",
|
|
15518
|
+
"destructive-op",
|
|
15519
|
+
"eval-of-remote"
|
|
15520
|
+
]);
|
|
15521
|
+
WRITE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
15522
|
+
"write",
|
|
15523
|
+
"write_file",
|
|
15524
|
+
"create_file",
|
|
15525
|
+
"edit",
|
|
15526
|
+
"multiedit",
|
|
15527
|
+
"str_replace_based_edit_tool",
|
|
15528
|
+
"replace",
|
|
15529
|
+
"notebook_edit",
|
|
15530
|
+
"notebookedit"
|
|
15531
|
+
]);
|
|
15532
|
+
ACTIVITY_REBIND_MAX_ATTEMPTS = 5;
|
|
15533
|
+
ACTIVITY_REBIND_WINDOW_MS = 6e4;
|
|
15534
|
+
ACTIVITY_HEALTH_PROBE_MS = 2e3;
|
|
15535
|
+
activitySocketServer = null;
|
|
15536
|
+
activityHealthInterval = null;
|
|
15537
|
+
activityRebindAttempts = [];
|
|
15538
|
+
activityCircuitTripped = false;
|
|
15519
15539
|
}
|
|
15520
15540
|
});
|
|
15521
15541
|
|
|
@@ -16614,9 +16634,95 @@ var init_ship = __esm({
|
|
|
16614
16634
|
}
|
|
16615
16635
|
});
|
|
16616
16636
|
|
|
16637
|
+
// src/policy-snapshot/build.ts
|
|
16638
|
+
function buildPolicySnapshot(config, activeShields, overrides) {
|
|
16639
|
+
const p = config.policy;
|
|
16640
|
+
return {
|
|
16641
|
+
mode: config.settings.mode,
|
|
16642
|
+
panicMode: config.settings.panicMode === true,
|
|
16643
|
+
// The proxy expresses shadow/observe as mode === 'observe' (cloud shadowMode
|
|
16644
|
+
// forces it); there's no separate settings flag.
|
|
16645
|
+
shadowMode: config.settings.mode === "observe",
|
|
16646
|
+
activeShields,
|
|
16647
|
+
shieldOverrides: overrides,
|
|
16648
|
+
smartRuleCount: p.smartRules.length,
|
|
16649
|
+
smartRules: p.smartRules.slice(0, MAX_RULES).map((r) => ({
|
|
16650
|
+
name: r.name,
|
|
16651
|
+
tool: r.tool,
|
|
16652
|
+
verdict: r.verdict,
|
|
16653
|
+
reason: r.reason
|
|
16654
|
+
})),
|
|
16655
|
+
egress: {
|
|
16656
|
+
enabled: p.egress.enabled,
|
|
16657
|
+
mode: p.egress.mode,
|
|
16658
|
+
allow: p.egress.allow.slice(0, MAX_EGRESS)
|
|
16659
|
+
},
|
|
16660
|
+
dlpEnabled: p.dlp.enabled,
|
|
16661
|
+
engineVersion: ENGINE_VERSION
|
|
16662
|
+
};
|
|
16663
|
+
}
|
|
16664
|
+
var MAX_RULES, MAX_EGRESS;
|
|
16665
|
+
var init_build = __esm({
|
|
16666
|
+
"src/policy-snapshot/build.ts"() {
|
|
16667
|
+
"use strict";
|
|
16668
|
+
init_dist();
|
|
16669
|
+
MAX_RULES = 500;
|
|
16670
|
+
MAX_EGRESS = 200;
|
|
16671
|
+
}
|
|
16672
|
+
});
|
|
16673
|
+
|
|
16674
|
+
// src/policy-snapshot/ship.ts
|
|
16675
|
+
import http2 from "http";
|
|
16676
|
+
import https3 from "https";
|
|
16677
|
+
import { URL as URL3 } from "url";
|
|
16678
|
+
function policySnapshotUrlFrom(apiUrl) {
|
|
16679
|
+
return apiUrl.endsWith("/policies/sync") ? apiUrl.replace(/\/policies\/sync$/, "/policy/snapshot") : null;
|
|
16680
|
+
}
|
|
16681
|
+
async function shipPolicySnapshot(body, creds) {
|
|
16682
|
+
const url = policySnapshotUrlFrom(creds.apiUrl);
|
|
16683
|
+
if (!url) return false;
|
|
16684
|
+
const payload = JSON.stringify(body);
|
|
16685
|
+
const parsed = new URL3(url);
|
|
16686
|
+
const transport = parsed.protocol === "http:" ? http2 : https3;
|
|
16687
|
+
return new Promise((resolve) => {
|
|
16688
|
+
const req = transport.request(
|
|
16689
|
+
{
|
|
16690
|
+
hostname: parsed.hostname,
|
|
16691
|
+
port: parsed.port ? parseInt(parsed.port, 10) : void 0,
|
|
16692
|
+
path: parsed.pathname + parsed.search,
|
|
16693
|
+
method: "POST",
|
|
16694
|
+
headers: {
|
|
16695
|
+
"Content-Type": "application/json",
|
|
16696
|
+
"Content-Length": Buffer.byteLength(payload),
|
|
16697
|
+
Authorization: `Bearer ${creds.apiKey}`
|
|
16698
|
+
},
|
|
16699
|
+
timeout: 1e4
|
|
16700
|
+
},
|
|
16701
|
+
(res) => {
|
|
16702
|
+
const ok2 = !!res.statusCode && res.statusCode >= 200 && res.statusCode < 300;
|
|
16703
|
+
res.resume();
|
|
16704
|
+
res.on("end", () => resolve(ok2));
|
|
16705
|
+
res.on("error", () => resolve(false));
|
|
16706
|
+
}
|
|
16707
|
+
);
|
|
16708
|
+
req.on("error", () => resolve(false));
|
|
16709
|
+
req.on("timeout", () => {
|
|
16710
|
+
req.destroy();
|
|
16711
|
+
resolve(false);
|
|
16712
|
+
});
|
|
16713
|
+
req.write(payload);
|
|
16714
|
+
req.end();
|
|
16715
|
+
});
|
|
16716
|
+
}
|
|
16717
|
+
var init_ship2 = __esm({
|
|
16718
|
+
"src/policy-snapshot/ship.ts"() {
|
|
16719
|
+
"use strict";
|
|
16720
|
+
}
|
|
16721
|
+
});
|
|
16722
|
+
|
|
16617
16723
|
// src/daemon/sync.ts
|
|
16618
16724
|
import fs32 from "fs";
|
|
16619
|
-
import
|
|
16725
|
+
import https4 from "https";
|
|
16620
16726
|
import os29 from "os";
|
|
16621
16727
|
import path31 from "path";
|
|
16622
16728
|
function emptySignals3() {
|
|
@@ -16650,6 +16756,11 @@ function buildSessionDeltas(findings, toolCallsBySession) {
|
|
|
16650
16756
|
signals
|
|
16651
16757
|
}));
|
|
16652
16758
|
}
|
|
16759
|
+
function resolveSyncIntervalMs(settings) {
|
|
16760
|
+
const rawSeconds = settings.cloudSyncIntervalSeconds ?? (settings.cloudSyncIntervalHours ?? DEFAULT_INTERVAL_HOURS) * 3600;
|
|
16761
|
+
const clamped = Math.min(Math.max(rawSeconds, MIN_INTERVAL_SECONDS), MAX_INTERVAL_SECONDS);
|
|
16762
|
+
return clamped * 1e3;
|
|
16763
|
+
}
|
|
16653
16764
|
function readCredentials() {
|
|
16654
16765
|
if (process.env.NODE9_API_KEY) {
|
|
16655
16766
|
return {
|
|
@@ -16699,7 +16810,7 @@ function fetchCloudPolicy(apiKey, apiUrl, ifNoneMatch) {
|
|
|
16699
16810
|
};
|
|
16700
16811
|
if (ifNoneMatch) headers["If-None-Match"] = `"${ifNoneMatch}"`;
|
|
16701
16812
|
return new Promise((resolve, reject) => {
|
|
16702
|
-
const req =
|
|
16813
|
+
const req = https4.request(
|
|
16703
16814
|
{
|
|
16704
16815
|
hostname: parsed.hostname,
|
|
16705
16816
|
port: parsed.port ? parseInt(parsed.port, 10) : void 0,
|
|
@@ -16779,6 +16890,9 @@ async function syncOnce() {
|
|
|
16779
16890
|
if (process.env.NODE9_POSTURE_DISABLE !== "1") {
|
|
16780
16891
|
void pushPostureSnapshot(creds);
|
|
16781
16892
|
}
|
|
16893
|
+
if (process.env.NODE9_POLICY_MIRROR_DISABLE !== "1") {
|
|
16894
|
+
void pushPolicySnapshot(creds);
|
|
16895
|
+
}
|
|
16782
16896
|
}
|
|
16783
16897
|
async function pushBlastSnapshot(creds) {
|
|
16784
16898
|
try {
|
|
@@ -16788,7 +16902,7 @@ async function pushBlastSnapshot(creds) {
|
|
|
16788
16902
|
if (!blastUrl) return;
|
|
16789
16903
|
const parsed = new URL(blastUrl);
|
|
16790
16904
|
await new Promise((resolve) => {
|
|
16791
|
-
const req =
|
|
16905
|
+
const req = https4.request(
|
|
16792
16906
|
{
|
|
16793
16907
|
hostname: parsed.hostname,
|
|
16794
16908
|
port: parsed.port ? parseInt(parsed.port, 10) : void 0,
|
|
@@ -16825,6 +16939,29 @@ async function pushPostureSnapshot(creds) {
|
|
|
16825
16939
|
} catch {
|
|
16826
16940
|
}
|
|
16827
16941
|
}
|
|
16942
|
+
async function pushPolicySnapshot(creds) {
|
|
16943
|
+
try {
|
|
16944
|
+
const body = buildPolicySnapshot(getConfig(), readActiveShields(), readShieldOverrides());
|
|
16945
|
+
await shipPolicySnapshot(body, creds);
|
|
16946
|
+
} catch {
|
|
16947
|
+
}
|
|
16948
|
+
}
|
|
16949
|
+
async function runPolicyPush() {
|
|
16950
|
+
const creds = readCredentials();
|
|
16951
|
+
if (!creds) {
|
|
16952
|
+
return {
|
|
16953
|
+
ok: false,
|
|
16954
|
+
reason: "No API key configured. Add credentials with: node9 login"
|
|
16955
|
+
};
|
|
16956
|
+
}
|
|
16957
|
+
try {
|
|
16958
|
+
const body = buildPolicySnapshot(getConfig(), readActiveShields(), readShieldOverrides());
|
|
16959
|
+
const sent = await shipPolicySnapshot(body, creds);
|
|
16960
|
+
return sent ? { ok: true } : { ok: false, reason: "Push failed (network or server error)" };
|
|
16961
|
+
} catch (e) {
|
|
16962
|
+
return { ok: false, reason: e instanceof Error ? e.message : String(e) };
|
|
16963
|
+
}
|
|
16964
|
+
}
|
|
16828
16965
|
async function pushScanSnapshot(creds) {
|
|
16829
16966
|
try {
|
|
16830
16967
|
const tick = await tickScanWatcher();
|
|
@@ -16842,7 +16979,7 @@ async function pushScanSnapshot(creds) {
|
|
|
16842
16979
|
const parsed = new URL(scanUrl);
|
|
16843
16980
|
let posted = false;
|
|
16844
16981
|
await new Promise((resolve) => {
|
|
16845
|
-
const req =
|
|
16982
|
+
const req = https4.request(
|
|
16846
16983
|
{
|
|
16847
16984
|
hostname: parsed.hostname,
|
|
16848
16985
|
port: parsed.port ? parseInt(parsed.port, 10) : void 0,
|
|
@@ -16892,6 +17029,9 @@ async function runCloudSync() {
|
|
|
16892
17029
|
if (process.env.NODE9_POSTURE_DISABLE !== "1") {
|
|
16893
17030
|
void pushPostureSnapshot(creds);
|
|
16894
17031
|
}
|
|
17032
|
+
if (process.env.NODE9_POLICY_MIRROR_DISABLE !== "1") {
|
|
17033
|
+
void pushPolicySnapshot(creds);
|
|
17034
|
+
}
|
|
16895
17035
|
};
|
|
16896
17036
|
try {
|
|
16897
17037
|
const result = await fetchCloudPolicy(creds.apiKey, creds.apiUrl, readCachedEtag());
|
|
@@ -16943,9 +17083,7 @@ function getCloudRules() {
|
|
|
16943
17083
|
}
|
|
16944
17084
|
}
|
|
16945
17085
|
function startCloudSync() {
|
|
16946
|
-
const
|
|
16947
|
-
const intervalHours = Math.max(rawHours, MIN_INTERVAL_HOURS);
|
|
16948
|
-
const intervalMs = intervalHours * 60 * 60 * 1e3;
|
|
17086
|
+
const intervalMs = resolveSyncIntervalMs(getConfig().settings);
|
|
16949
17087
|
const initial = setTimeout(() => void syncOnce(), 3e4);
|
|
16950
17088
|
initial.unref();
|
|
16951
17089
|
const recurring = setInterval(() => void syncOnce(), intervalMs);
|
|
@@ -16970,7 +17108,7 @@ function startForensicBroadcast() {
|
|
|
16970
17108
|
const recurring = setInterval(() => void tick(), FORENSIC_BROADCAST_INTERVAL_MS);
|
|
16971
17109
|
recurring.unref();
|
|
16972
17110
|
}
|
|
16973
|
-
var FINDING_TO_SIGNAL3, rulesCacheFile, DEFAULT_API_URL, DEFAULT_INTERVAL_HOURS,
|
|
17111
|
+
var FINDING_TO_SIGNAL3, rulesCacheFile, DEFAULT_API_URL, DEFAULT_INTERVAL_HOURS, MIN_INTERVAL_SECONDS, MAX_INTERVAL_SECONDS, FORENSIC_BROADCAST_INTERVAL_MS, FORENSIC_INITIAL_DELAY_MS, forensicBroadcastOffsets;
|
|
16974
17112
|
var init_sync = __esm({
|
|
16975
17113
|
"src/daemon/sync.ts"() {
|
|
16976
17114
|
"use strict";
|
|
@@ -16978,6 +17116,9 @@ var init_sync = __esm({
|
|
|
16978
17116
|
init_blast();
|
|
16979
17117
|
init_posture();
|
|
16980
17118
|
init_ship();
|
|
17119
|
+
init_build();
|
|
17120
|
+
init_ship2();
|
|
17121
|
+
init_shields();
|
|
16981
17122
|
init_dist();
|
|
16982
17123
|
init_scan_watermark();
|
|
16983
17124
|
init_state2();
|
|
@@ -16997,7 +17138,8 @@ var init_sync = __esm({
|
|
|
16997
17138
|
rulesCacheFile = () => path31.join(os29.homedir(), ".node9", "rules-cache.json");
|
|
16998
17139
|
DEFAULT_API_URL = "https://api.node9.ai/api/v1/intercept/policies/sync";
|
|
16999
17140
|
DEFAULT_INTERVAL_HOURS = 5;
|
|
17000
|
-
|
|
17141
|
+
MIN_INTERVAL_SECONDS = 15;
|
|
17142
|
+
MAX_INTERVAL_SECONDS = 24 * 60 * 60;
|
|
17001
17143
|
FORENSIC_BROADCAST_INTERVAL_MS = 3e4;
|
|
17002
17144
|
FORENSIC_INITIAL_DELAY_MS = 5e3;
|
|
17003
17145
|
forensicBroadcastOffsets = /* @__PURE__ */ new Map();
|
|
@@ -17451,7 +17593,7 @@ var init_mcp_tools = __esm({
|
|
|
17451
17593
|
});
|
|
17452
17594
|
|
|
17453
17595
|
// src/daemon/server.ts
|
|
17454
|
-
import
|
|
17596
|
+
import http3 from "http";
|
|
17455
17597
|
import fs36 from "fs";
|
|
17456
17598
|
import path35 from "path";
|
|
17457
17599
|
import os33 from "os";
|
|
@@ -17486,7 +17628,7 @@ function startDaemon() {
|
|
|
17486
17628
|
}
|
|
17487
17629
|
resetIdleTimer();
|
|
17488
17630
|
const allowedHosts = /* @__PURE__ */ new Set([`127.0.0.1:${DAEMON_PORT}`, `localhost:${DAEMON_PORT}`]);
|
|
17489
|
-
const server =
|
|
17631
|
+
const server = http3.createServer(async (req, res) => {
|
|
17490
17632
|
const host = req.headers.host ?? "";
|
|
17491
17633
|
if (!allowedHosts.has(host)) {
|
|
17492
17634
|
res.writeHead(421, { "Content-Type": "text/plain" });
|
|
@@ -18720,11 +18862,11 @@ __export(tail_exports, {
|
|
|
18720
18862
|
shortenPathSummary: () => shortenPathSummary,
|
|
18721
18863
|
startTail: () => startTail
|
|
18722
18864
|
});
|
|
18723
|
-
import
|
|
18865
|
+
import http4 from "http";
|
|
18724
18866
|
import chalk35 from "chalk";
|
|
18725
18867
|
import fs64 from "fs";
|
|
18726
|
-
import
|
|
18727
|
-
import
|
|
18868
|
+
import os55 from "os";
|
|
18869
|
+
import path62 from "path";
|
|
18728
18870
|
import readline6 from "readline";
|
|
18729
18871
|
import { spawn as spawn8 } from "child_process";
|
|
18730
18872
|
function shortenPathSummary(s) {
|
|
@@ -18748,18 +18890,18 @@ function getModelContextLimit(model) {
|
|
|
18748
18890
|
return 2e5;
|
|
18749
18891
|
}
|
|
18750
18892
|
function readSessionUsage() {
|
|
18751
|
-
const projectsDir =
|
|
18893
|
+
const projectsDir = path62.join(os55.homedir(), ".claude", "projects");
|
|
18752
18894
|
if (!fs64.existsSync(projectsDir)) return null;
|
|
18753
18895
|
let latestFile = null;
|
|
18754
18896
|
let latestMtime = 0;
|
|
18755
18897
|
try {
|
|
18756
18898
|
for (const dir of fs64.readdirSync(projectsDir)) {
|
|
18757
|
-
const dirPath =
|
|
18899
|
+
const dirPath = path62.join(projectsDir, dir);
|
|
18758
18900
|
try {
|
|
18759
18901
|
if (!fs64.statSync(dirPath).isDirectory()) continue;
|
|
18760
18902
|
for (const file of fs64.readdirSync(dirPath)) {
|
|
18761
18903
|
if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
|
|
18762
|
-
const filePath =
|
|
18904
|
+
const filePath = path62.join(dirPath, file);
|
|
18763
18905
|
try {
|
|
18764
18906
|
const mtime = fs64.statSync(filePath).mtimeMs;
|
|
18765
18907
|
if (mtime > latestMtime) {
|
|
@@ -18837,7 +18979,7 @@ function formatBase(activity) {
|
|
|
18837
18979
|
const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
|
|
18838
18980
|
const icon = getIcon(activity.tool);
|
|
18839
18981
|
const toolName = activity.tool.slice(0, 16).padEnd(16);
|
|
18840
|
-
const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(
|
|
18982
|
+
const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(os55.homedir(), "~");
|
|
18841
18983
|
const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
|
|
18842
18984
|
return `${chalk35.gray(time)} ${icon} ${agentLabel(activity.agent, activity.mcpServer, activity.sessionId)}${chalk35.white.bold(toolName)} ${chalk35.dim(argsPreview)}`;
|
|
18843
18985
|
}
|
|
@@ -18919,7 +19061,7 @@ function postDecisionHttp(id, decision, authToken, port, opts) {
|
|
|
18919
19061
|
if (opts?.trustDuration) bodyObj.trustDuration = opts.trustDuration;
|
|
18920
19062
|
if (opts?.reason) bodyObj.reason = opts.reason;
|
|
18921
19063
|
const body = JSON.stringify(bodyObj);
|
|
18922
|
-
const req =
|
|
19064
|
+
const req = http4.request(
|
|
18923
19065
|
{
|
|
18924
19066
|
hostname: "127.0.0.1",
|
|
18925
19067
|
port,
|
|
@@ -19034,7 +19176,7 @@ function buildRecoveryCardLines(req) {
|
|
|
19034
19176
|
];
|
|
19035
19177
|
}
|
|
19036
19178
|
function readApproversFromDisk() {
|
|
19037
|
-
const configPath =
|
|
19179
|
+
const configPath = path62.join(os55.homedir(), ".node9", "config.json");
|
|
19038
19180
|
try {
|
|
19039
19181
|
const raw = JSON.parse(fs64.readFileSync(configPath, "utf-8"));
|
|
19040
19182
|
const settings = raw.settings ?? {};
|
|
@@ -19052,7 +19194,7 @@ function approverStatusLine() {
|
|
|
19052
19194
|
return `${fmt("native", "native")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
|
|
19053
19195
|
}
|
|
19054
19196
|
function toggleApprover(channel) {
|
|
19055
|
-
const configPath =
|
|
19197
|
+
const configPath = path62.join(os55.homedir(), ".node9", "config.json");
|
|
19056
19198
|
try {
|
|
19057
19199
|
const raw = JSON.parse(fs64.readFileSync(configPath, "utf-8"));
|
|
19058
19200
|
const settings = raw.settings ?? {};
|
|
@@ -19070,7 +19212,7 @@ async function startTail(options = {}) {
|
|
|
19070
19212
|
const port = await ensureDaemon();
|
|
19071
19213
|
if (options.clear) {
|
|
19072
19214
|
const result = await new Promise((resolve) => {
|
|
19073
|
-
const req2 =
|
|
19215
|
+
const req2 = http4.request(
|
|
19074
19216
|
{ method: "POST", hostname: "127.0.0.1", port, path: "/events/clear" },
|
|
19075
19217
|
(res) => {
|
|
19076
19218
|
const status = res.statusCode ?? 0;
|
|
@@ -19233,7 +19375,7 @@ async function startTail(options = {}) {
|
|
|
19233
19375
|
postDecisionHttp(req2.id, httpDecision, authToken, port, httpOpts).catch((err2) => {
|
|
19234
19376
|
try {
|
|
19235
19377
|
fs64.appendFileSync(
|
|
19236
|
-
|
|
19378
|
+
path62.join(os55.homedir(), ".node9", "hook-debug.log"),
|
|
19237
19379
|
`[tail] POST /decision failed: ${String(err2)}
|
|
19238
19380
|
`
|
|
19239
19381
|
);
|
|
@@ -19297,7 +19439,7 @@ async function startTail(options = {}) {
|
|
|
19297
19439
|
};
|
|
19298
19440
|
process.stdin.on("keypress", onKeypress);
|
|
19299
19441
|
}
|
|
19300
|
-
const auditLog =
|
|
19442
|
+
const auditLog = path62.join(os55.homedir(), ".node9", "audit.log");
|
|
19301
19443
|
try {
|
|
19302
19444
|
const unackedDlp = fs64.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
|
|
19303
19445
|
if (unackedDlp > 0) {
|
|
@@ -19353,7 +19495,7 @@ async function startTail(options = {}) {
|
|
|
19353
19495
|
}, STALL_THRESHOLD_MS / 2);
|
|
19354
19496
|
stallWatchdog.unref();
|
|
19355
19497
|
const sseUrl = `http://127.0.0.1:${port}/events?capabilities=input`;
|
|
19356
|
-
const req =
|
|
19498
|
+
const req = http4.get(
|
|
19357
19499
|
sseUrl,
|
|
19358
19500
|
{
|
|
19359
19501
|
headers: authToken ? { "X-Node9-Internal": authToken } : {}
|
|
@@ -19530,7 +19672,7 @@ var init_tail = __esm({
|
|
|
19530
19672
|
"use strict";
|
|
19531
19673
|
init_daemon2();
|
|
19532
19674
|
init_daemon();
|
|
19533
|
-
PID_FILE =
|
|
19675
|
+
PID_FILE = path62.join(os55.homedir(), ".node9", "daemon.pid");
|
|
19534
19676
|
ICONS = {
|
|
19535
19677
|
bash: "\u{1F4BB}",
|
|
19536
19678
|
shell: "\u{1F4BB}",
|
|
@@ -19579,9 +19721,9 @@ __export(hud_exports, {
|
|
|
19579
19721
|
renderEnvironmentLine: () => renderEnvironmentLine
|
|
19580
19722
|
});
|
|
19581
19723
|
import fs65 from "fs";
|
|
19582
|
-
import
|
|
19583
|
-
import
|
|
19584
|
-
import
|
|
19724
|
+
import path63 from "path";
|
|
19725
|
+
import os56 from "os";
|
|
19726
|
+
import http5 from "http";
|
|
19585
19727
|
async function readStdin() {
|
|
19586
19728
|
const chunks = [];
|
|
19587
19729
|
for await (const chunk2 of process.stdin) {
|
|
@@ -19599,7 +19741,7 @@ function queryDaemon() {
|
|
|
19599
19741
|
return new Promise((resolve) => {
|
|
19600
19742
|
const timeout = setTimeout(() => resolve(null), 50);
|
|
19601
19743
|
try {
|
|
19602
|
-
const req =
|
|
19744
|
+
const req = http5.get(
|
|
19603
19745
|
`http://${DAEMON_HOST}:${DAEMON_PORT}/status`,
|
|
19604
19746
|
{ timeout: 50 },
|
|
19605
19747
|
(res) => {
|
|
@@ -19684,7 +19826,7 @@ function countRulesInDir(rulesDir) {
|
|
|
19684
19826
|
try {
|
|
19685
19827
|
for (const entry of fs65.readdirSync(rulesDir, { withFileTypes: true })) {
|
|
19686
19828
|
if (entry.isDirectory()) {
|
|
19687
|
-
count += countRulesInDir(
|
|
19829
|
+
count += countRulesInDir(path63.join(rulesDir, entry.name));
|
|
19688
19830
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
19689
19831
|
count++;
|
|
19690
19832
|
}
|
|
@@ -19695,46 +19837,46 @@ function countRulesInDir(rulesDir) {
|
|
|
19695
19837
|
}
|
|
19696
19838
|
function isSamePath(a, b) {
|
|
19697
19839
|
try {
|
|
19698
|
-
return
|
|
19840
|
+
return path63.resolve(a) === path63.resolve(b);
|
|
19699
19841
|
} catch {
|
|
19700
19842
|
return false;
|
|
19701
19843
|
}
|
|
19702
19844
|
}
|
|
19703
19845
|
function countConfigs(cwd) {
|
|
19704
|
-
const homeDir2 =
|
|
19705
|
-
const claudeDir =
|
|
19846
|
+
const homeDir2 = os56.homedir();
|
|
19847
|
+
const claudeDir = path63.join(homeDir2, ".claude");
|
|
19706
19848
|
let claudeMdCount = 0;
|
|
19707
19849
|
let rulesCount = 0;
|
|
19708
19850
|
let hooksCount = 0;
|
|
19709
19851
|
const userMcpServers = /* @__PURE__ */ new Set();
|
|
19710
19852
|
const projectMcpServers = /* @__PURE__ */ new Set();
|
|
19711
|
-
if (fs65.existsSync(
|
|
19712
|
-
rulesCount += countRulesInDir(
|
|
19713
|
-
const userSettings =
|
|
19853
|
+
if (fs65.existsSync(path63.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
19854
|
+
rulesCount += countRulesInDir(path63.join(claudeDir, "rules"));
|
|
19855
|
+
const userSettings = path63.join(claudeDir, "settings.json");
|
|
19714
19856
|
for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
|
|
19715
19857
|
hooksCount += countHooksInFile(userSettings);
|
|
19716
|
-
const userClaudeJson =
|
|
19858
|
+
const userClaudeJson = path63.join(homeDir2, ".claude.json");
|
|
19717
19859
|
for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
|
|
19718
19860
|
for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
|
|
19719
19861
|
userMcpServers.delete(name);
|
|
19720
19862
|
}
|
|
19721
19863
|
if (cwd) {
|
|
19722
|
-
if (fs65.existsSync(
|
|
19723
|
-
if (fs65.existsSync(
|
|
19724
|
-
const projectClaudeDir =
|
|
19864
|
+
if (fs65.existsSync(path63.join(cwd, "CLAUDE.md"))) claudeMdCount++;
|
|
19865
|
+
if (fs65.existsSync(path63.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
|
|
19866
|
+
const projectClaudeDir = path63.join(cwd, ".claude");
|
|
19725
19867
|
const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
|
|
19726
19868
|
if (!overlapsUserScope) {
|
|
19727
|
-
if (fs65.existsSync(
|
|
19728
|
-
rulesCount += countRulesInDir(
|
|
19729
|
-
const projSettings =
|
|
19869
|
+
if (fs65.existsSync(path63.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
19870
|
+
rulesCount += countRulesInDir(path63.join(projectClaudeDir, "rules"));
|
|
19871
|
+
const projSettings = path63.join(projectClaudeDir, "settings.json");
|
|
19730
19872
|
for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
|
|
19731
19873
|
hooksCount += countHooksInFile(projSettings);
|
|
19732
19874
|
}
|
|
19733
|
-
if (fs65.existsSync(
|
|
19734
|
-
const localSettings =
|
|
19875
|
+
if (fs65.existsSync(path63.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
|
|
19876
|
+
const localSettings = path63.join(projectClaudeDir, "settings.local.json");
|
|
19735
19877
|
for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
|
|
19736
19878
|
hooksCount += countHooksInFile(localSettings);
|
|
19737
|
-
const mcpJsonServers = getMcpServerNames(
|
|
19879
|
+
const mcpJsonServers = getMcpServerNames(path63.join(cwd, ".mcp.json"));
|
|
19738
19880
|
const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
|
|
19739
19881
|
for (const name of disabledMcpJson) mcpJsonServers.delete(name);
|
|
19740
19882
|
for (const name of mcpJsonServers) projectMcpServers.add(name);
|
|
@@ -19767,7 +19909,7 @@ function readActiveShieldsHud() {
|
|
|
19767
19909
|
return shieldsCache.value;
|
|
19768
19910
|
}
|
|
19769
19911
|
try {
|
|
19770
|
-
const shieldsPath =
|
|
19912
|
+
const shieldsPath = path63.join(os56.homedir(), ".node9", "shields.json");
|
|
19771
19913
|
if (!fs65.existsSync(shieldsPath)) {
|
|
19772
19914
|
shieldsCache = { value: [], ts: now };
|
|
19773
19915
|
return [];
|
|
@@ -19874,9 +20016,9 @@ function renderContextLine(stdin) {
|
|
|
19874
20016
|
async function main() {
|
|
19875
20017
|
try {
|
|
19876
20018
|
const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
|
|
19877
|
-
if (fs65.existsSync(
|
|
20019
|
+
if (fs65.existsSync(path63.join(os56.homedir(), ".node9", "hud-debug"))) {
|
|
19878
20020
|
try {
|
|
19879
|
-
const logPath =
|
|
20021
|
+
const logPath = path63.join(os56.homedir(), ".node9", "hud-debug.log");
|
|
19880
20022
|
const MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
19881
20023
|
let size = 0;
|
|
19882
20024
|
try {
|
|
@@ -19905,8 +20047,8 @@ async function main() {
|
|
|
19905
20047
|
try {
|
|
19906
20048
|
const cwd = stdin.cwd ?? process.cwd();
|
|
19907
20049
|
for (const configPath of [
|
|
19908
|
-
|
|
19909
|
-
|
|
20050
|
+
path63.join(cwd, "node9.config.json"),
|
|
20051
|
+
path63.join(os56.homedir(), ".node9", "config.json")
|
|
19910
20052
|
]) {
|
|
19911
20053
|
if (!fs65.existsSync(configPath)) continue;
|
|
19912
20054
|
const cfg = JSON.parse(fs65.readFileSync(configPath, "utf-8"));
|
|
@@ -19953,12 +20095,45 @@ var init_hud = __esm({
|
|
|
19953
20095
|
// src/cli.ts
|
|
19954
20096
|
init_core();
|
|
19955
20097
|
init_setup();
|
|
19956
|
-
init_daemon2();
|
|
19957
20098
|
import { Command } from "commander";
|
|
20099
|
+
|
|
20100
|
+
// src/agent-teardowns.ts
|
|
20101
|
+
init_setup();
|
|
20102
|
+
var AGENT_TEARDOWNS = [
|
|
20103
|
+
{ id: "claude", label: "Claude", fn: teardownClaude },
|
|
20104
|
+
{ id: "gemini", label: "Gemini", fn: teardownGemini },
|
|
20105
|
+
{ id: "codex", label: "Codex", fn: teardownCodex },
|
|
20106
|
+
{ id: "cursor", label: "Cursor", fn: teardownCursor },
|
|
20107
|
+
{ id: "windsurf", label: "Windsurf", fn: teardownWindsurf },
|
|
20108
|
+
{ id: "vscode", label: "VSCode", fn: teardownVSCode },
|
|
20109
|
+
{ id: "hermes", label: "Hermes", fn: teardownHermes },
|
|
20110
|
+
{ id: "antigravity", label: "Antigravity", fn: teardownAntigravity, aliases: ["agy"] },
|
|
20111
|
+
{ id: "copilot", label: "Copilot", fn: teardownCopilot },
|
|
20112
|
+
{ id: "hud", label: "HUD", fn: teardownHud },
|
|
20113
|
+
{
|
|
20114
|
+
id: "claudedesktop",
|
|
20115
|
+
label: "Claude Desktop",
|
|
20116
|
+
fn: teardownClaudeDesktop,
|
|
20117
|
+
aliases: ["claude-desktop"]
|
|
20118
|
+
},
|
|
20119
|
+
{ id: "opencode", label: "OpenCode", fn: teardownOpencode },
|
|
20120
|
+
{ id: "pi", label: "Pi", fn: teardownPi }
|
|
20121
|
+
];
|
|
20122
|
+
function resolveAgentTeardown(target) {
|
|
20123
|
+
const t = target.trim().toLowerCase();
|
|
20124
|
+
return AGENT_TEARDOWNS.find((a) => a.id === t || a.aliases?.includes(t));
|
|
20125
|
+
}
|
|
20126
|
+
function agentTeardownTargets() {
|
|
20127
|
+
return AGENT_TEARDOWNS.flatMap((a) => [a.id, ...a.aliases ?? []]);
|
|
20128
|
+
}
|
|
20129
|
+
|
|
20130
|
+
// src/cli.ts
|
|
20131
|
+
init_agent_wiring();
|
|
20132
|
+
init_daemon2();
|
|
19958
20133
|
import chalk36 from "chalk";
|
|
19959
20134
|
import fs66 from "fs";
|
|
19960
|
-
import
|
|
19961
|
-
import
|
|
20135
|
+
import path64 from "path";
|
|
20136
|
+
import os57 from "os";
|
|
19962
20137
|
import { spawn as spawn9 } from "child_process";
|
|
19963
20138
|
import { confirm as confirm2 } from "@inquirer/prompts";
|
|
19964
20139
|
|
|
@@ -21589,6 +21764,8 @@ function registerLogCommand(program2) {
|
|
|
21589
21764
|
init_shields();
|
|
21590
21765
|
import chalk10 from "chalk";
|
|
21591
21766
|
import fs46 from "fs";
|
|
21767
|
+
import path44 from "path";
|
|
21768
|
+
import os40 from "os";
|
|
21592
21769
|
|
|
21593
21770
|
// src/shields/build.ts
|
|
21594
21771
|
function escapeRegex(s) {
|
|
@@ -21718,10 +21895,10 @@ init_audit();
|
|
|
21718
21895
|
init_config();
|
|
21719
21896
|
|
|
21720
21897
|
// src/utils/https-fetch.ts
|
|
21721
|
-
import
|
|
21898
|
+
import https5 from "https";
|
|
21722
21899
|
function httpsFetch(url) {
|
|
21723
21900
|
return new Promise((resolve, reject) => {
|
|
21724
|
-
|
|
21901
|
+
https5.get(url, (res) => {
|
|
21725
21902
|
if (res.statusCode !== 200) {
|
|
21726
21903
|
reject(new Error(`HTTP ${String(res.statusCode)} for ${url}`));
|
|
21727
21904
|
res.resume();
|
|
@@ -21737,6 +21914,22 @@ function httpsFetch(url) {
|
|
|
21737
21914
|
|
|
21738
21915
|
// src/cli/commands/shield.ts
|
|
21739
21916
|
var COMMUNITY_INDEX_URL = "https://raw.githubusercontent.com/node9ai/node9-proxy/main/shields/community/index.json";
|
|
21917
|
+
function readCloudShields() {
|
|
21918
|
+
const out = /* @__PURE__ */ new Set();
|
|
21919
|
+
try {
|
|
21920
|
+
const file = path44.join(os40.homedir(), ".node9", "rules-cache.json");
|
|
21921
|
+
const raw = JSON.parse(fs46.readFileSync(file, "utf-8"));
|
|
21922
|
+
for (const r of raw.rules ?? []) {
|
|
21923
|
+
const rule = r;
|
|
21924
|
+
const fromSource = rule.source?.startsWith("SHIELD:") ? rule.source.slice("SHIELD:".length).toLowerCase() : void 0;
|
|
21925
|
+
const fromDesc = !fromSource && rule.description ? /\b([a-z0-9-]+)\s+shield/i.exec(rule.description)?.[1]?.toLowerCase() : void 0;
|
|
21926
|
+
const name = fromSource ?? fromDesc;
|
|
21927
|
+
if (name) out.add(name);
|
|
21928
|
+
}
|
|
21929
|
+
} catch {
|
|
21930
|
+
}
|
|
21931
|
+
return out;
|
|
21932
|
+
}
|
|
21740
21933
|
function registerShieldCommand(program2) {
|
|
21741
21934
|
const shieldCmd = program2.command("shield").description("Manage pre-packaged security shield templates");
|
|
21742
21935
|
shieldCmd.command("enable <service>").description("Enable a security shield for a specific service").action((service) => {
|
|
@@ -21823,10 +22016,17 @@ function registerShieldCommand(program2) {
|
|
|
21823
22016
|
return;
|
|
21824
22017
|
}
|
|
21825
22018
|
const active = new Set(readActiveShields());
|
|
22019
|
+
const cloud = readCloudShields();
|
|
21826
22020
|
console.log(chalk10.bold("\n\u{1F6E1}\uFE0F Available Shields\n"));
|
|
22021
|
+
console.log(chalk10.gray(" \u25CF local \xB7 \u2601 cloud\n"));
|
|
21827
22022
|
for (const shield of listShields()) {
|
|
21828
|
-
const
|
|
21829
|
-
|
|
22023
|
+
const isLocal = active.has(shield.name);
|
|
22024
|
+
const isCloud = cloud.has(shield.name);
|
|
22025
|
+
const status = isLocal && isCloud ? chalk10.green("\u25CF\u2601 enabled ") : isLocal ? chalk10.green("\u25CF enabled ") : isCloud ? chalk10.cyan("\u2601 cloud ") : chalk10.gray("\u25CB disabled");
|
|
22026
|
+
const via = isCloud && !isLocal ? chalk10.gray(" (via dashboard)") : "";
|
|
22027
|
+
console.log(
|
|
22028
|
+
` ${status} ${chalk10.cyan(shield.name.padEnd(12))} ${shield.description}${via}`
|
|
22029
|
+
);
|
|
21830
22030
|
if (shield.aliases.length > 0)
|
|
21831
22031
|
console.log(chalk10.gray(` aliases: ${shield.aliases.join(", ")}`));
|
|
21832
22032
|
}
|
|
@@ -22159,12 +22359,12 @@ init_config();
|
|
|
22159
22359
|
init_agent_wiring();
|
|
22160
22360
|
import chalk11 from "chalk";
|
|
22161
22361
|
import fs47 from "fs";
|
|
22162
|
-
import
|
|
22163
|
-
import
|
|
22362
|
+
import path45 from "path";
|
|
22363
|
+
import os41 from "os";
|
|
22164
22364
|
import { execSync } from "child_process";
|
|
22165
22365
|
function registerDoctorCommand(program2, version2) {
|
|
22166
22366
|
program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(async () => {
|
|
22167
|
-
const homeDir2 =
|
|
22367
|
+
const homeDir2 = os41.homedir();
|
|
22168
22368
|
let failures = 0;
|
|
22169
22369
|
function pass(msg) {
|
|
22170
22370
|
console.log(chalk11.green(" \u2705 ") + msg);
|
|
@@ -22210,7 +22410,7 @@ function registerDoctorCommand(program2, version2) {
|
|
|
22210
22410
|
);
|
|
22211
22411
|
}
|
|
22212
22412
|
section("Configuration");
|
|
22213
|
-
const globalConfigPath =
|
|
22413
|
+
const globalConfigPath = path45.join(homeDir2, ".node9", "config.json");
|
|
22214
22414
|
if (fs47.existsSync(globalConfigPath)) {
|
|
22215
22415
|
try {
|
|
22216
22416
|
JSON.parse(fs47.readFileSync(globalConfigPath, "utf-8"));
|
|
@@ -22221,7 +22421,7 @@ function registerDoctorCommand(program2, version2) {
|
|
|
22221
22421
|
} else {
|
|
22222
22422
|
warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
|
|
22223
22423
|
}
|
|
22224
|
-
const projectConfigPath =
|
|
22424
|
+
const projectConfigPath = path45.join(process.cwd(), "node9.config.json");
|
|
22225
22425
|
if (fs47.existsSync(projectConfigPath)) {
|
|
22226
22426
|
try {
|
|
22227
22427
|
JSON.parse(fs47.readFileSync(projectConfigPath, "utf-8"));
|
|
@@ -22233,7 +22433,7 @@ function registerDoctorCommand(program2, version2) {
|
|
|
22233
22433
|
);
|
|
22234
22434
|
}
|
|
22235
22435
|
}
|
|
22236
|
-
const credsPath =
|
|
22436
|
+
const credsPath = path45.join(homeDir2, ".node9", "credentials.json");
|
|
22237
22437
|
if (fs47.existsSync(credsPath)) {
|
|
22238
22438
|
pass("Cloud credentials found (~/.node9/credentials.json)");
|
|
22239
22439
|
} else {
|
|
@@ -22278,7 +22478,7 @@ function registerDoctorCommand(program2, version2) {
|
|
|
22278
22478
|
try {
|
|
22279
22479
|
const { shipLagBytes: shipLagBytes2, readWatermark: readWatermark2, AUDIT_SHIP_WATERMARK: AUDIT_SHIP_WATERMARK2 } = await Promise.resolve().then(() => (init_audit_shipper(), audit_shipper_exports));
|
|
22280
22480
|
const cfg = getConfig();
|
|
22281
|
-
const creds = fs47.existsSync(
|
|
22481
|
+
const creds = fs47.existsSync(path45.join(os41.homedir(), ".node9", "credentials.json"));
|
|
22282
22482
|
if (!creds) {
|
|
22283
22483
|
warn("Not logged in \u2014 audit rows stay local", "Run: node9 login <api-key>");
|
|
22284
22484
|
} else if (!cfg.settings.approvers.cloud) {
|
|
@@ -22329,8 +22529,8 @@ function registerDoctorCommand(program2, version2) {
|
|
|
22329
22529
|
// src/cli/commands/audit.ts
|
|
22330
22530
|
import chalk12 from "chalk";
|
|
22331
22531
|
import fs48 from "fs";
|
|
22332
|
-
import
|
|
22333
|
-
import
|
|
22532
|
+
import path46 from "path";
|
|
22533
|
+
import os42 from "os";
|
|
22334
22534
|
function formatRelativeTime(timestamp) {
|
|
22335
22535
|
const diff = Date.now() - new Date(timestamp).getTime();
|
|
22336
22536
|
const sec = Math.floor(diff / 1e3);
|
|
@@ -22343,7 +22543,7 @@ function formatRelativeTime(timestamp) {
|
|
|
22343
22543
|
}
|
|
22344
22544
|
function registerAuditCommand(program2) {
|
|
22345
22545
|
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) => {
|
|
22346
|
-
const logPath =
|
|
22546
|
+
const logPath = path46.join(os42.homedir(), ".node9", "audit.log");
|
|
22347
22547
|
if (!fs48.existsSync(logPath)) {
|
|
22348
22548
|
console.log(
|
|
22349
22549
|
chalk12.yellow("No audit logs found. Run node9 with an agent to generate entries.")
|
|
@@ -22410,8 +22610,8 @@ init_costSync();
|
|
|
22410
22610
|
init_litellm();
|
|
22411
22611
|
init_cost_codex();
|
|
22412
22612
|
import fs49 from "fs";
|
|
22413
|
-
import
|
|
22414
|
-
import
|
|
22613
|
+
import os43 from "os";
|
|
22614
|
+
import path47 from "path";
|
|
22415
22615
|
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;
|
|
22416
22616
|
function buildTestTimestamps(allEntries) {
|
|
22417
22617
|
const testTs = /* @__PURE__ */ new Set();
|
|
@@ -22539,7 +22739,7 @@ function freezeClaudeCost(acc) {
|
|
|
22539
22739
|
};
|
|
22540
22740
|
}
|
|
22541
22741
|
function processClaudeCostProject(proj, projectsDir, start, end, acc) {
|
|
22542
|
-
const projPath =
|
|
22742
|
+
const projPath = path47.join(projectsDir, proj);
|
|
22543
22743
|
let files;
|
|
22544
22744
|
try {
|
|
22545
22745
|
const stat = fs49.statSync(projPath);
|
|
@@ -22550,7 +22750,7 @@ function processClaudeCostProject(proj, projectsDir, start, end, acc) {
|
|
|
22550
22750
|
}
|
|
22551
22751
|
const startMs = start.getTime();
|
|
22552
22752
|
for (const file of files) {
|
|
22553
|
-
const filePath =
|
|
22753
|
+
const filePath = path47.join(projPath, file);
|
|
22554
22754
|
try {
|
|
22555
22755
|
if (fs49.statSync(filePath).mtimeMs < startMs) continue;
|
|
22556
22756
|
} catch {
|
|
@@ -22680,28 +22880,28 @@ function listCodexSessionFiles2(sessionsBase) {
|
|
|
22680
22880
|
if (!fs49.existsSync(sessionsBase)) return jsonlFiles;
|
|
22681
22881
|
try {
|
|
22682
22882
|
for (const year of fs49.readdirSync(sessionsBase)) {
|
|
22683
|
-
const yearPath =
|
|
22883
|
+
const yearPath = path47.join(sessionsBase, year);
|
|
22684
22884
|
try {
|
|
22685
22885
|
if (!fs49.statSync(yearPath).isDirectory()) continue;
|
|
22686
22886
|
} catch {
|
|
22687
22887
|
continue;
|
|
22688
22888
|
}
|
|
22689
22889
|
for (const month of fs49.readdirSync(yearPath)) {
|
|
22690
|
-
const monthPath =
|
|
22890
|
+
const monthPath = path47.join(yearPath, month);
|
|
22691
22891
|
try {
|
|
22692
22892
|
if (!fs49.statSync(monthPath).isDirectory()) continue;
|
|
22693
22893
|
} catch {
|
|
22694
22894
|
continue;
|
|
22695
22895
|
}
|
|
22696
22896
|
for (const day of fs49.readdirSync(monthPath)) {
|
|
22697
|
-
const dayPath =
|
|
22897
|
+
const dayPath = path47.join(monthPath, day);
|
|
22698
22898
|
try {
|
|
22699
22899
|
if (!fs49.statSync(dayPath).isDirectory()) continue;
|
|
22700
22900
|
} catch {
|
|
22701
22901
|
continue;
|
|
22702
22902
|
}
|
|
22703
22903
|
for (const file of fs49.readdirSync(dayPath)) {
|
|
22704
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(
|
|
22904
|
+
if (file.endsWith(".jsonl")) jsonlFiles.push(path47.join(dayPath, file));
|
|
22705
22905
|
}
|
|
22706
22906
|
}
|
|
22707
22907
|
}
|
|
@@ -22827,7 +23027,7 @@ function listGeminiSessionFiles2(geminiTmpDir2) {
|
|
|
22827
23027
|
return out;
|
|
22828
23028
|
}
|
|
22829
23029
|
for (const proj of dirs) {
|
|
22830
|
-
const chatsDir =
|
|
23030
|
+
const chatsDir = path47.join(geminiTmpDir2, proj, "chats");
|
|
22831
23031
|
let files;
|
|
22832
23032
|
try {
|
|
22833
23033
|
if (!fs49.statSync(chatsDir).isDirectory()) continue;
|
|
@@ -22837,7 +23037,7 @@ function listGeminiSessionFiles2(geminiTmpDir2) {
|
|
|
22837
23037
|
}
|
|
22838
23038
|
for (const f of files) {
|
|
22839
23039
|
if (!f.endsWith(".jsonl")) continue;
|
|
22840
|
-
out.push({ projectKey: proj, file:
|
|
23040
|
+
out.push({ projectKey: proj, file: path47.join(chatsDir, f) });
|
|
22841
23041
|
}
|
|
22842
23042
|
}
|
|
22843
23043
|
return out;
|
|
@@ -22852,10 +23052,10 @@ function loadGeminiCost(start, end, geminiTmpDir2) {
|
|
|
22852
23052
|
}
|
|
22853
23053
|
function aggregateReportFromAudit(period, opts = {}) {
|
|
22854
23054
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
22855
|
-
const auditLogPath = opts.auditLogPath ??
|
|
22856
|
-
const claudeProjectsDir = opts.claudeProjectsDir ??
|
|
22857
|
-
const codexSessionsDir2 = opts.codexSessionsDir ??
|
|
22858
|
-
const geminiTmpDir2 = opts.geminiTmpDir ??
|
|
23055
|
+
const auditLogPath = opts.auditLogPath ?? path47.join(os43.homedir(), ".node9", "audit.log");
|
|
23056
|
+
const claudeProjectsDir = opts.claudeProjectsDir ?? path47.join(os43.homedir(), ".claude", "projects");
|
|
23057
|
+
const codexSessionsDir2 = opts.codexSessionsDir ?? path47.join(os43.homedir(), ".codex", "sessions");
|
|
23058
|
+
const geminiTmpDir2 = opts.geminiTmpDir ?? path47.join(os43.homedir(), ".gemini", "tmp");
|
|
22859
23059
|
const hasAuditFile = fs49.existsSync(auditLogPath);
|
|
22860
23060
|
const allEntries = opts.preloadedAuditEntries ?? parseAuditLog(auditLogPath);
|
|
22861
23061
|
const unackedDlp = allEntries.filter((e) => e.source === "response-dlp");
|
|
@@ -23556,8 +23756,8 @@ init_daemon();
|
|
|
23556
23756
|
init_agent_wiring();
|
|
23557
23757
|
import chalk15 from "chalk";
|
|
23558
23758
|
import fs50 from "fs";
|
|
23559
|
-
import
|
|
23560
|
-
import
|
|
23759
|
+
import path48 from "path";
|
|
23760
|
+
import os44 from "os";
|
|
23561
23761
|
function printAgentSection(label2, hookPairs, wrapped) {
|
|
23562
23762
|
console.log(chalk15.bold(` ${label2}`));
|
|
23563
23763
|
for (const { name, present } of hookPairs) {
|
|
@@ -23611,8 +23811,8 @@ function registerStatusCommand(program2) {
|
|
|
23611
23811
|
console.log("");
|
|
23612
23812
|
const modeLabel = settings.mode === "audit" ? chalk15.blue("audit") : settings.mode === "strict" ? chalk15.red("strict") : chalk15.white("standard");
|
|
23613
23813
|
console.log(` Mode: ${modeLabel}`);
|
|
23614
|
-
const projectConfig =
|
|
23615
|
-
const globalConfig =
|
|
23814
|
+
const projectConfig = path48.join(process.cwd(), "node9.config.json");
|
|
23815
|
+
const globalConfig = path48.join(os44.homedir(), ".node9", "config.json");
|
|
23616
23816
|
console.log(
|
|
23617
23817
|
` Local: ${fs50.existsSync(projectConfig) ? chalk15.green("Active (node9.config.json)") : chalk15.gray("Not present")}`
|
|
23618
23818
|
);
|
|
@@ -23624,7 +23824,7 @@ function registerStatusCommand(program2) {
|
|
|
23624
23824
|
` Sandbox: ${chalk15.green(`${mergedConfig.policy.sandboxPaths.length} safe zones active`)}`
|
|
23625
23825
|
);
|
|
23626
23826
|
}
|
|
23627
|
-
const wiring = getAgentWiring(
|
|
23827
|
+
const wiring = getAgentWiring(os44.homedir()).filter((a) => a.present);
|
|
23628
23828
|
if (wiring.length > 0) {
|
|
23629
23829
|
console.log("");
|
|
23630
23830
|
console.log(chalk15.bold(" Agent Wiring:"));
|
|
@@ -23664,9 +23864,9 @@ init_shields();
|
|
|
23664
23864
|
init_service();
|
|
23665
23865
|
import chalk16 from "chalk";
|
|
23666
23866
|
import fs51 from "fs";
|
|
23667
|
-
import
|
|
23668
|
-
import
|
|
23669
|
-
import
|
|
23867
|
+
import path49 from "path";
|
|
23868
|
+
import os45 from "os";
|
|
23869
|
+
import https6 from "https";
|
|
23670
23870
|
var DEFAULT_SHIELDS = ["bash-safe", "filesystem", "project-jail"];
|
|
23671
23871
|
function buildTelemetryPayload(agents, firstInstall) {
|
|
23672
23872
|
return {
|
|
@@ -23680,7 +23880,7 @@ function buildTelemetryPayload(agents, firstInstall) {
|
|
|
23680
23880
|
function fireTelemetryPing(agents, firstInstall) {
|
|
23681
23881
|
try {
|
|
23682
23882
|
const body = JSON.stringify(buildTelemetryPayload(agents, firstInstall));
|
|
23683
|
-
const req =
|
|
23883
|
+
const req = https6.request(
|
|
23684
23884
|
{
|
|
23685
23885
|
hostname: "api.node9.ai",
|
|
23686
23886
|
path: "/api/v1/telemetry",
|
|
@@ -23751,7 +23951,7 @@ function registerInitCommand(program2) {
|
|
|
23751
23951
|
}
|
|
23752
23952
|
console.log("");
|
|
23753
23953
|
}
|
|
23754
|
-
const configPath =
|
|
23954
|
+
const configPath = path49.join(os45.homedir(), ".node9", "config.json");
|
|
23755
23955
|
const isFirstInstall = !fs51.existsSync(configPath);
|
|
23756
23956
|
if (fs51.existsSync(configPath) && !options.force) {
|
|
23757
23957
|
try {
|
|
@@ -23773,7 +23973,7 @@ function registerInitCommand(program2) {
|
|
|
23773
23973
|
...DEFAULT_CONFIG,
|
|
23774
23974
|
settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
|
|
23775
23975
|
};
|
|
23776
|
-
const dir =
|
|
23976
|
+
const dir = path49.dirname(configPath);
|
|
23777
23977
|
if (!fs51.existsSync(dir)) fs51.mkdirSync(dir, { recursive: true });
|
|
23778
23978
|
fs51.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
|
|
23779
23979
|
console.log(chalk16.green(`\u2705 Config created: ${configPath}`));
|
|
@@ -23880,7 +24080,7 @@ function registerInitCommand(program2) {
|
|
|
23880
24080
|
}
|
|
23881
24081
|
|
|
23882
24082
|
// src/cli/commands/undo.ts
|
|
23883
|
-
import
|
|
24083
|
+
import path50 from "path";
|
|
23884
24084
|
import chalk18 from "chalk";
|
|
23885
24085
|
|
|
23886
24086
|
// src/tui/undo-navigator.ts
|
|
@@ -24039,7 +24239,7 @@ function findMatchingCwd(startDir, history) {
|
|
|
24039
24239
|
let dir = startDir;
|
|
24040
24240
|
while (true) {
|
|
24041
24241
|
if (cwds.has(dir)) return dir;
|
|
24042
|
-
const parent =
|
|
24242
|
+
const parent = path50.dirname(dir);
|
|
24043
24243
|
if (parent === dir) return null;
|
|
24044
24244
|
dir = parent;
|
|
24045
24245
|
}
|
|
@@ -24675,8 +24875,8 @@ function registerMcpGatewayCommand(program2) {
|
|
|
24675
24875
|
// src/mcp-server/index.ts
|
|
24676
24876
|
import readline5 from "readline";
|
|
24677
24877
|
import fs53 from "fs";
|
|
24678
|
-
import
|
|
24679
|
-
import
|
|
24878
|
+
import os47 from "os";
|
|
24879
|
+
import path52 from "path";
|
|
24680
24880
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
24681
24881
|
init_core();
|
|
24682
24882
|
init_daemon();
|
|
@@ -24684,8 +24884,8 @@ init_shields();
|
|
|
24684
24884
|
|
|
24685
24885
|
// src/auth/egress-config.ts
|
|
24686
24886
|
import fs52 from "fs";
|
|
24687
|
-
import
|
|
24688
|
-
import
|
|
24887
|
+
import os46 from "os";
|
|
24888
|
+
import path51 from "path";
|
|
24689
24889
|
var DEFAULT_EGRESS = {
|
|
24690
24890
|
enabled: false,
|
|
24691
24891
|
mode: "review",
|
|
@@ -24694,7 +24894,7 @@ var DEFAULT_EGRESS = {
|
|
|
24694
24894
|
allowPrivate: true
|
|
24695
24895
|
};
|
|
24696
24896
|
function egressConfigPath() {
|
|
24697
|
-
return
|
|
24897
|
+
return path51.join(os46.homedir(), ".node9", "config.json");
|
|
24698
24898
|
}
|
|
24699
24899
|
function readEgressRawConfig() {
|
|
24700
24900
|
let text;
|
|
@@ -24714,7 +24914,7 @@ function readEgressRawConfig() {
|
|
|
24714
24914
|
}
|
|
24715
24915
|
function writeEgressRawConfig(config) {
|
|
24716
24916
|
const p = egressConfigPath();
|
|
24717
|
-
fs52.mkdirSync(
|
|
24917
|
+
fs52.mkdirSync(path51.dirname(p), { recursive: true });
|
|
24718
24918
|
fs52.writeFileSync(p, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
|
|
24719
24919
|
}
|
|
24720
24920
|
function applyEgress(config, change) {
|
|
@@ -25100,8 +25300,8 @@ function handleStatus() {
|
|
|
25100
25300
|
lines.push(`Active shields: ${activeShields.length > 0 ? activeShields.join(", ") : "none"}`);
|
|
25101
25301
|
lines.push(`Smart rules: ${config.policy.smartRules.length} loaded`);
|
|
25102
25302
|
lines.push(`DLP: ${config.policy.dlp?.enabled !== false ? "enabled" : "disabled"}`);
|
|
25103
|
-
const projectConfig =
|
|
25104
|
-
const globalConfig =
|
|
25303
|
+
const projectConfig = path52.join(process.cwd(), "node9.config.json");
|
|
25304
|
+
const globalConfig = path52.join(os47.homedir(), ".node9", "config.json");
|
|
25105
25305
|
lines.push(
|
|
25106
25306
|
`Project config (node9.config.json): ${fs53.existsSync(projectConfig) ? "present" : "not found"}`
|
|
25107
25307
|
);
|
|
@@ -25212,7 +25412,7 @@ function handleEgressDeny(args) {
|
|
|
25212
25412
|
addEgressHost("deny", host);
|
|
25213
25413
|
return `Denied egress to ${host} (deny always wins over allow).`;
|
|
25214
25414
|
}
|
|
25215
|
-
var GLOBAL_CONFIG_PATH =
|
|
25415
|
+
var GLOBAL_CONFIG_PATH = path52.join(os47.homedir(), ".node9", "config.json");
|
|
25216
25416
|
var APPROVER_CHANNELS = ["native", "browser", "cloud", "terminal"];
|
|
25217
25417
|
function readGlobalConfigRaw() {
|
|
25218
25418
|
try {
|
|
@@ -25224,7 +25424,7 @@ function readGlobalConfigRaw() {
|
|
|
25224
25424
|
return {};
|
|
25225
25425
|
}
|
|
25226
25426
|
function writeGlobalConfigRaw(data) {
|
|
25227
|
-
const dir =
|
|
25427
|
+
const dir = path52.dirname(GLOBAL_CONFIG_PATH);
|
|
25228
25428
|
if (!fs53.existsSync(dir)) fs53.mkdirSync(dir, { recursive: true });
|
|
25229
25429
|
fs53.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(data, null, 2) + "\n");
|
|
25230
25430
|
}
|
|
@@ -25270,7 +25470,7 @@ function handleApproverSet(args) {
|
|
|
25270
25470
|
function handleAuditGet(args) {
|
|
25271
25471
|
const limit = Math.min(typeof args.limit === "number" ? args.limit : 20, 100);
|
|
25272
25472
|
const filter = typeof args.filter === "string" && args.filter !== "all" ? args.filter : null;
|
|
25273
|
-
const auditPath =
|
|
25473
|
+
const auditPath = path52.join(os47.homedir(), ".node9", "audit.log");
|
|
25274
25474
|
if (!fs53.existsSync(auditPath)) return "No audit log found.";
|
|
25275
25475
|
const rawLines = fs53.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
25276
25476
|
const parsed = [];
|
|
@@ -25778,6 +25978,17 @@ init_sync();
|
|
|
25778
25978
|
import chalk22 from "chalk";
|
|
25779
25979
|
function registerSyncCommand(program2) {
|
|
25780
25980
|
const policy = program2.command("policy").description("Manage cloud policy rules");
|
|
25981
|
+
policy.command("push").description("Push this machine's effective policy to the node9 dashboard").action(async () => {
|
|
25982
|
+
process.stdout.write(chalk22.cyan("Pushing policy to the dashboard\u2026"));
|
|
25983
|
+
const result = await runPolicyPush();
|
|
25984
|
+
process.stdout.write("\n");
|
|
25985
|
+
if (!result.ok) {
|
|
25986
|
+
console.error(chalk22.red(`\u2717 ${result.reason}`));
|
|
25987
|
+
process.exit(1);
|
|
25988
|
+
}
|
|
25989
|
+
console.log(chalk22.green("\u2713 Policy mirrored to the dashboard"));
|
|
25990
|
+
console.log(chalk22.gray(" See it under Security Policy \u2192 Machines"));
|
|
25991
|
+
});
|
|
25781
25992
|
policy.command("sync").description("Sync cloud policy rules to local cache (~/.node9/rules-cache.json)").action(async () => {
|
|
25782
25993
|
process.stdout.write(chalk22.cyan("Syncing cloud policy rules\u2026"));
|
|
25783
25994
|
const result = await runCloudSync();
|
|
@@ -26226,12 +26437,12 @@ import chalk27 from "chalk";
|
|
|
26226
26437
|
|
|
26227
26438
|
// src/shields/jail.ts
|
|
26228
26439
|
import fs55 from "fs";
|
|
26229
|
-
import
|
|
26230
|
-
import
|
|
26440
|
+
import os48 from "os";
|
|
26441
|
+
import path53 from "path";
|
|
26231
26442
|
init_shields();
|
|
26232
26443
|
var USER_JAIL_SHIELD = "user-jail";
|
|
26233
26444
|
function jailStorePath() {
|
|
26234
|
-
return
|
|
26445
|
+
return path53.join(os48.homedir(), ".node9", "jail-paths.json");
|
|
26235
26446
|
}
|
|
26236
26447
|
function readJailPaths() {
|
|
26237
26448
|
let text;
|
|
@@ -26254,7 +26465,7 @@ function readJailPaths() {
|
|
|
26254
26465
|
}
|
|
26255
26466
|
function writeJailPaths(paths) {
|
|
26256
26467
|
const p = jailStorePath();
|
|
26257
|
-
fs55.mkdirSync(
|
|
26468
|
+
fs55.mkdirSync(path53.dirname(p), { recursive: true });
|
|
26258
26469
|
fs55.writeFileSync(p, JSON.stringify({ paths }, null, 2) + "\n", { mode: 384 });
|
|
26259
26470
|
}
|
|
26260
26471
|
function addJailPath(rawPath, verdict) {
|
|
@@ -26277,7 +26488,7 @@ function removeJailPath(rawPath) {
|
|
|
26277
26488
|
return { removed, paths: after };
|
|
26278
26489
|
}
|
|
26279
26490
|
function regenerateUserJail(paths) {
|
|
26280
|
-
const file =
|
|
26491
|
+
const file = path53.join(USER_SHIELDS_DIR_PATH, `${USER_JAIL_SHIELD}.json`);
|
|
26281
26492
|
if (paths.length === 0) {
|
|
26282
26493
|
const active2 = readActiveShields();
|
|
26283
26494
|
if (active2.includes(USER_JAIL_SHIELD)) {
|
|
@@ -26400,12 +26611,12 @@ function registerJailCommand(program2) {
|
|
|
26400
26611
|
init_config();
|
|
26401
26612
|
import chalk28 from "chalk";
|
|
26402
26613
|
import fs58 from "fs";
|
|
26403
|
-
import
|
|
26614
|
+
import path56 from "path";
|
|
26404
26615
|
import { spawnSync as spawnSync6 } from "child_process";
|
|
26405
26616
|
|
|
26406
26617
|
// src/sandbox/config.ts
|
|
26407
26618
|
import fs56 from "fs";
|
|
26408
|
-
import
|
|
26619
|
+
import path54 from "path";
|
|
26409
26620
|
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
26410
26621
|
var SANDBOX_CONFIG_FILE = "node9.sandbox.yaml";
|
|
26411
26622
|
var FORBIDDEN_ENV = /* @__PURE__ */ new Set(["NODE9_API_KEY", "NODE9_API_URL"]);
|
|
@@ -26478,7 +26689,7 @@ function scaffoldSandboxYaml(agent) {
|
|
|
26478
26689
|
return header + stringifyYaml(defaultSandboxConfig(agent));
|
|
26479
26690
|
}
|
|
26480
26691
|
function sandboxConfigPath(cwd = process.cwd()) {
|
|
26481
|
-
return
|
|
26692
|
+
return path54.join(cwd, SANDBOX_CONFIG_FILE);
|
|
26482
26693
|
}
|
|
26483
26694
|
function loadSandboxConfig(cwd = process.cwd(), fallbackAgent = "claude") {
|
|
26484
26695
|
const p = sandboxConfigPath(cwd);
|
|
@@ -26547,12 +26758,12 @@ init_templates();
|
|
|
26547
26758
|
// src/sandbox/runtime.ts
|
|
26548
26759
|
init_templates();
|
|
26549
26760
|
import fs57 from "fs";
|
|
26550
|
-
import
|
|
26551
|
-
import
|
|
26761
|
+
import os49 from "os";
|
|
26762
|
+
import path55 from "path";
|
|
26552
26763
|
import crypto8 from "crypto";
|
|
26553
26764
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
26554
26765
|
function sandboxDataDir(cwd = process.cwd()) {
|
|
26555
|
-
return
|
|
26766
|
+
return path55.join(cwd, ".node9", "sandbox", "data");
|
|
26556
26767
|
}
|
|
26557
26768
|
function detectEngine(engine) {
|
|
26558
26769
|
const r = spawnSync5(engine, ["--version"], { encoding: "utf-8" });
|
|
@@ -26563,7 +26774,7 @@ function detectEngine(engine) {
|
|
|
26563
26774
|
}
|
|
26564
26775
|
function agentCredentialsMount(agent) {
|
|
26565
26776
|
const rel = agent === "codex" ? ".codex/auth.json" : ".claude/.credentials.json";
|
|
26566
|
-
return { hostPath:
|
|
26777
|
+
return { hostPath: path55.join(os49.homedir(), rel), target: `/home/${RUN_AS_USER}/${rel}` };
|
|
26567
26778
|
}
|
|
26568
26779
|
function buildRunArgs(opts) {
|
|
26569
26780
|
const { config, workspaceHostPath, dataHostPath, allowlistHostPath, agentArgs } = opts;
|
|
@@ -26591,30 +26802,30 @@ function imageContentHash(dockerfile, entrypoint) {
|
|
|
26591
26802
|
return crypto8.createHash("sha256").update(dockerfile).update("\0").update(entrypoint).digest("hex").slice(0, 16);
|
|
26592
26803
|
}
|
|
26593
26804
|
function sandboxBuildDir(cwd = process.cwd()) {
|
|
26594
|
-
return
|
|
26805
|
+
return path55.join(cwd, ".node9", "sandbox", "build");
|
|
26595
26806
|
}
|
|
26596
26807
|
function writeBuildContext(cwd, dockerfile, entrypoint) {
|
|
26597
26808
|
const dir = sandboxBuildDir(cwd);
|
|
26598
26809
|
fs57.mkdirSync(dir, { recursive: true });
|
|
26599
|
-
fs57.writeFileSync(
|
|
26600
|
-
fs57.writeFileSync(
|
|
26810
|
+
fs57.writeFileSync(path55.join(dir, "Dockerfile"), dockerfile);
|
|
26811
|
+
fs57.writeFileSync(path55.join(dir, "entrypoint.sh"), entrypoint);
|
|
26601
26812
|
return dir;
|
|
26602
26813
|
}
|
|
26603
26814
|
function writeAllowlist(cwd, hosts) {
|
|
26604
|
-
const dir =
|
|
26815
|
+
const dir = path55.join(cwd, ".node9", "sandbox");
|
|
26605
26816
|
fs57.mkdirSync(dir, { recursive: true });
|
|
26606
|
-
const p =
|
|
26817
|
+
const p = path55.join(dir, "allowed-domains.txt");
|
|
26607
26818
|
fs57.writeFileSync(p, hosts.join("\n") + "\n");
|
|
26608
26819
|
return p;
|
|
26609
26820
|
}
|
|
26610
26821
|
function resolveHomePath(p) {
|
|
26611
|
-
return p.startsWith("~") ?
|
|
26822
|
+
return p.startsWith("~") ? path55.join(os49.homedir(), p.slice(1)) : path55.resolve(p);
|
|
26612
26823
|
}
|
|
26613
26824
|
|
|
26614
26825
|
// src/cli/commands/sandbox.ts
|
|
26615
26826
|
function seedDataDirConfig(dataDir, sandbox) {
|
|
26616
26827
|
fs58.mkdirSync(dataDir, { recursive: true });
|
|
26617
|
-
const configPath =
|
|
26828
|
+
const configPath = path56.join(dataDir, "config.json");
|
|
26618
26829
|
const seed = {
|
|
26619
26830
|
settings: {
|
|
26620
26831
|
approvers: {
|
|
@@ -26679,7 +26890,7 @@ function registerSandboxCommand(program2, version2) {
|
|
|
26679
26890
|
const buildDir = writeBuildContext(cwd, dockerfile, entrypoint);
|
|
26680
26891
|
const hash = imageContentHash(dockerfile, entrypoint);
|
|
26681
26892
|
const image = sandbox.runtime.image;
|
|
26682
|
-
const hashFile =
|
|
26893
|
+
const hashFile = path56.join(sandboxBuildDir(cwd), ".image-hash");
|
|
26683
26894
|
const lastHash = fs58.existsSync(hashFile) ? fs58.readFileSync(hashFile, "utf-8").trim() : "";
|
|
26684
26895
|
const imageExists = spawnSync6(sandbox.runtime.engine, ["image", "inspect", image], { stdio: "ignore" }).status === 0;
|
|
26685
26896
|
const needBuild = sandbox.runtime.rebuild === "always" || !imageExists || sandbox.runtime.rebuild !== "never" && lastHash !== hash;
|
|
@@ -26722,7 +26933,7 @@ function registerSandboxCommand(program2, version2) {
|
|
|
26722
26933
|
process.exit(r.status ?? 0);
|
|
26723
26934
|
});
|
|
26724
26935
|
cmd.command("tail").description("Stream the sandbox's audit log (host-side)").action(() => {
|
|
26725
|
-
const auditPath =
|
|
26936
|
+
const auditPath = path56.join(sandboxDataDir(), "audit.log");
|
|
26726
26937
|
if (!fs58.existsSync(auditPath)) {
|
|
26727
26938
|
console.log(chalk28.dim(" no sandbox audit yet."));
|
|
26728
26939
|
return;
|
|
@@ -26730,7 +26941,7 @@ function registerSandboxCommand(program2, version2) {
|
|
|
26730
26941
|
spawnSync6("tail", ["-f", auditPath], { stdio: "inherit" });
|
|
26731
26942
|
});
|
|
26732
26943
|
cmd.command("logs").description("Dump the sandbox's audit log").action(() => {
|
|
26733
|
-
const auditPath =
|
|
26944
|
+
const auditPath = path56.join(sandboxDataDir(), "audit.log");
|
|
26734
26945
|
if (!fs58.existsSync(auditPath)) {
|
|
26735
26946
|
console.log(chalk28.dim(" no sandbox audit yet."));
|
|
26736
26947
|
return;
|
|
@@ -26749,7 +26960,7 @@ function registerSandboxCommand(program2, version2) {
|
|
|
26749
26960
|
stdio: "ignore"
|
|
26750
26961
|
});
|
|
26751
26962
|
}
|
|
26752
|
-
fs58.rmSync(
|
|
26963
|
+
fs58.rmSync(path56.join(cwd, ".node9", "sandbox"), { recursive: true, force: true });
|
|
26753
26964
|
console.log(chalk28.green(" \u2713 sandbox image + build + data removed."));
|
|
26754
26965
|
});
|
|
26755
26966
|
}
|
|
@@ -26761,8 +26972,8 @@ init_cost_gemini();
|
|
|
26761
26972
|
init_cost_codex();
|
|
26762
26973
|
import chalk29 from "chalk";
|
|
26763
26974
|
import fs59 from "fs";
|
|
26764
|
-
import
|
|
26765
|
-
import
|
|
26975
|
+
import path57 from "path";
|
|
26976
|
+
import os50 from "os";
|
|
26766
26977
|
function modelPrice(model) {
|
|
26767
26978
|
const t = pricingFor(model);
|
|
26768
26979
|
if (!t) return null;
|
|
@@ -26779,10 +26990,10 @@ function encodeProjectPath(projectPath) {
|
|
|
26779
26990
|
}
|
|
26780
26991
|
function sessionJsonlPath(projectPath, sessionId) {
|
|
26781
26992
|
const encoded = encodeProjectPath(projectPath);
|
|
26782
|
-
return
|
|
26993
|
+
return path57.join(os50.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
|
|
26783
26994
|
}
|
|
26784
26995
|
function projectLabel(projectPath) {
|
|
26785
|
-
return projectPath.replace(
|
|
26996
|
+
return projectPath.replace(os50.homedir(), "~");
|
|
26786
26997
|
}
|
|
26787
26998
|
function parseHistoryLines(lines) {
|
|
26788
26999
|
const entries = [];
|
|
@@ -26851,7 +27062,7 @@ function parseSessionLines(lines) {
|
|
|
26851
27062
|
return { toolCalls, costUSD, hasSnapshot, modifiedFiles };
|
|
26852
27063
|
}
|
|
26853
27064
|
function loadAuditEntries(auditPath) {
|
|
26854
|
-
const aPath = auditPath ??
|
|
27065
|
+
const aPath = auditPath ?? path57.join(os50.homedir(), ".node9", "audit.log");
|
|
26855
27066
|
let raw;
|
|
26856
27067
|
try {
|
|
26857
27068
|
raw = fs59.readFileSync(aPath, "utf-8");
|
|
@@ -26890,7 +27101,7 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
|
|
|
26890
27101
|
return result;
|
|
26891
27102
|
}
|
|
26892
27103
|
function buildGeminiSessions(days, allAuditEntries) {
|
|
26893
|
-
const tmpDir =
|
|
27104
|
+
const tmpDir = path57.join(os50.homedir(), ".gemini", "tmp");
|
|
26894
27105
|
if (!fs59.existsSync(tmpDir)) return [];
|
|
26895
27106
|
const cutoff = days !== null ? (() => {
|
|
26896
27107
|
const d = /* @__PURE__ */ new Date();
|
|
@@ -26906,18 +27117,18 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
26906
27117
|
}
|
|
26907
27118
|
const summaries = [];
|
|
26908
27119
|
for (const slug2 of slugDirs) {
|
|
26909
|
-
const slugPath =
|
|
27120
|
+
const slugPath = path57.join(tmpDir, slug2);
|
|
26910
27121
|
try {
|
|
26911
27122
|
if (!fs59.statSync(slugPath).isDirectory()) continue;
|
|
26912
27123
|
} catch {
|
|
26913
27124
|
continue;
|
|
26914
27125
|
}
|
|
26915
|
-
let projectRoot =
|
|
27126
|
+
let projectRoot = path57.join(os50.homedir(), slug2);
|
|
26916
27127
|
try {
|
|
26917
|
-
projectRoot = fs59.readFileSync(
|
|
27128
|
+
projectRoot = fs59.readFileSync(path57.join(slugPath, ".project_root"), "utf-8").trim();
|
|
26918
27129
|
} catch {
|
|
26919
27130
|
}
|
|
26920
|
-
const chatsDir =
|
|
27131
|
+
const chatsDir = path57.join(slugPath, "chats");
|
|
26921
27132
|
if (!fs59.existsSync(chatsDir)) continue;
|
|
26922
27133
|
let chatFiles;
|
|
26923
27134
|
try {
|
|
@@ -26928,7 +27139,7 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
26928
27139
|
for (const chatFile of chatFiles) {
|
|
26929
27140
|
let raw;
|
|
26930
27141
|
try {
|
|
26931
|
-
raw = fs59.readFileSync(
|
|
27142
|
+
raw = fs59.readFileSync(path57.join(chatsDir, chatFile), "utf-8");
|
|
26932
27143
|
} catch {
|
|
26933
27144
|
continue;
|
|
26934
27145
|
}
|
|
@@ -27008,7 +27219,7 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
27008
27219
|
return summaries;
|
|
27009
27220
|
}
|
|
27010
27221
|
function buildCodexSessions(days, allAuditEntries) {
|
|
27011
|
-
const sessionsBase =
|
|
27222
|
+
const sessionsBase = path57.join(os50.homedir(), ".codex", "sessions");
|
|
27012
27223
|
if (!fs59.existsSync(sessionsBase)) return [];
|
|
27013
27224
|
const cutoff = days !== null ? (() => {
|
|
27014
27225
|
const d = /* @__PURE__ */ new Date();
|
|
@@ -27019,28 +27230,28 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
27019
27230
|
const jsonlFiles = [];
|
|
27020
27231
|
try {
|
|
27021
27232
|
for (const year of fs59.readdirSync(sessionsBase)) {
|
|
27022
|
-
const yearPath =
|
|
27233
|
+
const yearPath = path57.join(sessionsBase, year);
|
|
27023
27234
|
try {
|
|
27024
27235
|
if (!fs59.statSync(yearPath).isDirectory()) continue;
|
|
27025
27236
|
} catch {
|
|
27026
27237
|
continue;
|
|
27027
27238
|
}
|
|
27028
27239
|
for (const month of fs59.readdirSync(yearPath)) {
|
|
27029
|
-
const monthPath =
|
|
27240
|
+
const monthPath = path57.join(yearPath, month);
|
|
27030
27241
|
try {
|
|
27031
27242
|
if (!fs59.statSync(monthPath).isDirectory()) continue;
|
|
27032
27243
|
} catch {
|
|
27033
27244
|
continue;
|
|
27034
27245
|
}
|
|
27035
27246
|
for (const day of fs59.readdirSync(monthPath)) {
|
|
27036
|
-
const dayPath =
|
|
27247
|
+
const dayPath = path57.join(monthPath, day);
|
|
27037
27248
|
try {
|
|
27038
27249
|
if (!fs59.statSync(dayPath).isDirectory()) continue;
|
|
27039
27250
|
} catch {
|
|
27040
27251
|
continue;
|
|
27041
27252
|
}
|
|
27042
27253
|
for (const file of fs59.readdirSync(dayPath)) {
|
|
27043
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(
|
|
27254
|
+
if (file.endsWith(".jsonl")) jsonlFiles.push(path57.join(dayPath, file));
|
|
27044
27255
|
}
|
|
27045
27256
|
}
|
|
27046
27257
|
}
|
|
@@ -27138,7 +27349,7 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
27138
27349
|
return summaries;
|
|
27139
27350
|
}
|
|
27140
27351
|
function buildSessions(days, historyPath) {
|
|
27141
|
-
const hPath = historyPath ??
|
|
27352
|
+
const hPath = historyPath ?? path57.join(os50.homedir(), ".claude", "history.jsonl");
|
|
27142
27353
|
let historyRaw = "";
|
|
27143
27354
|
try {
|
|
27144
27355
|
historyRaw = fs59.readFileSync(hPath, "utf-8");
|
|
@@ -27560,11 +27771,11 @@ function registerSessionTaintCommand(program2) {
|
|
|
27560
27771
|
// src/cli/commands/skill-pin.ts
|
|
27561
27772
|
import chalk31 from "chalk";
|
|
27562
27773
|
import fs60 from "fs";
|
|
27563
|
-
import
|
|
27564
|
-
import
|
|
27774
|
+
import os51 from "os";
|
|
27775
|
+
import path58 from "path";
|
|
27565
27776
|
function wipeSkillSessions() {
|
|
27566
27777
|
try {
|
|
27567
|
-
fs60.rmSync(
|
|
27778
|
+
fs60.rmSync(path58.join(os51.homedir(), ".node9", "skill-sessions"), {
|
|
27568
27779
|
recursive: true,
|
|
27569
27780
|
force: true
|
|
27570
27781
|
});
|
|
@@ -27647,10 +27858,10 @@ function registerSkillPinCommand(program2) {
|
|
|
27647
27858
|
|
|
27648
27859
|
// src/cli/commands/decisions.ts
|
|
27649
27860
|
import fs61 from "fs";
|
|
27650
|
-
import
|
|
27651
|
-
import
|
|
27861
|
+
import os52 from "os";
|
|
27862
|
+
import path59 from "path";
|
|
27652
27863
|
import chalk32 from "chalk";
|
|
27653
|
-
var DECISIONS_FILE2 =
|
|
27864
|
+
var DECISIONS_FILE2 = path59.join(os52.homedir(), ".node9", "decisions.json");
|
|
27654
27865
|
function readDecisions() {
|
|
27655
27866
|
try {
|
|
27656
27867
|
if (!fs61.existsSync(DECISIONS_FILE2)) return {};
|
|
@@ -27666,7 +27877,7 @@ function readDecisions() {
|
|
|
27666
27877
|
}
|
|
27667
27878
|
}
|
|
27668
27879
|
function writeDecisions(d) {
|
|
27669
|
-
const dir =
|
|
27880
|
+
const dir = path59.dirname(DECISIONS_FILE2);
|
|
27670
27881
|
if (!fs61.existsSync(dir)) fs61.mkdirSync(dir, { recursive: true });
|
|
27671
27882
|
const tmp = `${DECISIONS_FILE2}.${process.pid}.tmp`;
|
|
27672
27883
|
fs61.writeFileSync(tmp, JSON.stringify(d, null, 2));
|
|
@@ -27728,10 +27939,10 @@ Persistent decisions (${entries.length})
|
|
|
27728
27939
|
// src/cli/commands/dlp.ts
|
|
27729
27940
|
import chalk33 from "chalk";
|
|
27730
27941
|
import fs62 from "fs";
|
|
27731
|
-
import
|
|
27732
|
-
import
|
|
27733
|
-
var AUDIT_LOG =
|
|
27734
|
-
var RESOLVED_FILE =
|
|
27942
|
+
import path60 from "path";
|
|
27943
|
+
import os53 from "os";
|
|
27944
|
+
var AUDIT_LOG = path60.join(os53.homedir(), ".node9", "audit.log");
|
|
27945
|
+
var RESOLVED_FILE = path60.join(os53.homedir(), ".node9", "dlp-resolved.json");
|
|
27735
27946
|
var ANSI_RE = /\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g;
|
|
27736
27947
|
function stripAnsi(s) {
|
|
27737
27948
|
return s.replace(ANSI_RE, "");
|
|
@@ -27852,13 +28063,13 @@ function registerDlpCommand(program2) {
|
|
|
27852
28063
|
init_dlp();
|
|
27853
28064
|
import chalk34 from "chalk";
|
|
27854
28065
|
import fs63 from "fs";
|
|
27855
|
-
import
|
|
27856
|
-
import
|
|
28066
|
+
import path61 from "path";
|
|
28067
|
+
import os54 from "os";
|
|
27857
28068
|
function findJsonlFiles(dir) {
|
|
27858
28069
|
const results = [];
|
|
27859
28070
|
if (!fs63.existsSync(dir)) return results;
|
|
27860
28071
|
for (const entry of fs63.readdirSync(dir, { withFileTypes: true })) {
|
|
27861
|
-
const full =
|
|
28072
|
+
const full = path61.join(dir, entry.name);
|
|
27862
28073
|
if (entry.isDirectory()) results.push(...findJsonlFiles(full));
|
|
27863
28074
|
else if (entry.isFile() && entry.name.endsWith(".jsonl")) results.push(full);
|
|
27864
28075
|
}
|
|
@@ -27961,7 +28172,7 @@ function findJsonFiles(dir) {
|
|
|
27961
28172
|
const results = [];
|
|
27962
28173
|
if (!fs63.existsSync(dir)) return results;
|
|
27963
28174
|
for (const entry of fs63.readdirSync(dir, { withFileTypes: true })) {
|
|
27964
|
-
const full =
|
|
28175
|
+
const full = path61.join(dir, entry.name);
|
|
27965
28176
|
if (entry.isDirectory()) results.push(...findJsonFiles(full));
|
|
27966
28177
|
else if (entry.isFile() && entry.name.endsWith(".json")) results.push(full);
|
|
27967
28178
|
}
|
|
@@ -27970,9 +28181,9 @@ function findJsonFiles(dir) {
|
|
|
27970
28181
|
function registerMaskCommand(program2) {
|
|
27971
28182
|
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) => {
|
|
27972
28183
|
const dryRun = !!options.dryRun;
|
|
27973
|
-
const home =
|
|
27974
|
-
const claudeDir =
|
|
27975
|
-
const geminiDir =
|
|
28184
|
+
const home = os54.homedir();
|
|
28185
|
+
const claudeDir = path61.join(home, ".claude", "projects");
|
|
28186
|
+
const geminiDir = path61.join(home, ".gemini", "tmp");
|
|
27976
28187
|
const allFiles = [
|
|
27977
28188
|
...findJsonlFiles(claudeDir).map((p) => ({ path: p, type: "jsonl" })),
|
|
27978
28189
|
...findJsonFiles(geminiDir).map((p) => ({ path: p, type: "json" }))
|
|
@@ -28036,15 +28247,15 @@ function registerMaskCommand(program2) {
|
|
|
28036
28247
|
// src/cli.ts
|
|
28037
28248
|
init_blast();
|
|
28038
28249
|
var { version } = JSON.parse(
|
|
28039
|
-
fs66.readFileSync(
|
|
28250
|
+
fs66.readFileSync(path64.join(__dirname, "../package.json"), "utf-8")
|
|
28040
28251
|
);
|
|
28041
28252
|
var program = new Command();
|
|
28042
28253
|
program.name("node9").description("The Sudo Command for AI Agents").version(version);
|
|
28043
28254
|
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) => {
|
|
28044
28255
|
const DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept";
|
|
28045
|
-
const credPath =
|
|
28046
|
-
if (!fs66.existsSync(
|
|
28047
|
-
fs66.mkdirSync(
|
|
28256
|
+
const credPath = path64.join(os57.homedir(), ".node9", "credentials.json");
|
|
28257
|
+
if (!fs66.existsSync(path64.dirname(credPath)))
|
|
28258
|
+
fs66.mkdirSync(path64.dirname(credPath), { recursive: true });
|
|
28048
28259
|
const profileName = options.profile || "default";
|
|
28049
28260
|
let existingCreds = {};
|
|
28050
28261
|
try {
|
|
@@ -28064,7 +28275,7 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
28064
28275
|
fs66.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
|
|
28065
28276
|
let effectiveCloud = null;
|
|
28066
28277
|
if (profileName === "default") {
|
|
28067
|
-
const configPath =
|
|
28278
|
+
const configPath = path64.join(os57.homedir(), ".node9", "config.json");
|
|
28068
28279
|
let config = {};
|
|
28069
28280
|
try {
|
|
28070
28281
|
if (fs66.existsSync(configPath))
|
|
@@ -28083,8 +28294,8 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
28083
28294
|
approvers.cloud = false;
|
|
28084
28295
|
}
|
|
28085
28296
|
s.approvers = approvers;
|
|
28086
|
-
if (!fs66.existsSync(
|
|
28087
|
-
fs66.mkdirSync(
|
|
28297
|
+
if (!fs66.existsSync(path64.dirname(configPath)))
|
|
28298
|
+
fs66.mkdirSync(path64.dirname(configPath), { recursive: true });
|
|
28088
28299
|
fs66.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
28089
28300
|
effectiveCloud = approvers.cloud === true;
|
|
28090
28301
|
}
|
|
@@ -28194,37 +28405,21 @@ program.command("setup", { hidden: true }).description('Alias for "addto" \u2014
|
|
|
28194
28405
|
);
|
|
28195
28406
|
process.exit(1);
|
|
28196
28407
|
});
|
|
28197
|
-
program.command("removefrom", { hidden: true }).description("Remove Node9 hooks from an AI agent configuration").addHelpText(
|
|
28198
|
-
"
|
|
28199
|
-
|
|
28200
|
-
)
|
|
28201
|
-
"<target>",
|
|
28202
|
-
"The agent to remove from: claude | antigravity | copilot | gemini | cursor | codex | windsurf | vscode | hud"
|
|
28203
|
-
).action((target) => {
|
|
28204
|
-
let fn;
|
|
28205
|
-
if (target === "claude") fn = teardownClaude;
|
|
28206
|
-
else if (target === "gemini") fn = teardownGemini;
|
|
28207
|
-
else if (target === "antigravity" || target === "agy") fn = teardownAntigravity;
|
|
28208
|
-
else if (target === "copilot") fn = teardownCopilot;
|
|
28209
|
-
else if (target === "cursor") fn = teardownCursor;
|
|
28210
|
-
else if (target === "codex") fn = teardownCodex;
|
|
28211
|
-
else if (target === "windsurf") fn = teardownWindsurf;
|
|
28212
|
-
else if (target === "vscode") fn = teardownVSCode;
|
|
28213
|
-
else if (target === "hermes") fn = teardownHermes;
|
|
28214
|
-
else if (target === "hud") fn = teardownHud;
|
|
28215
|
-
else {
|
|
28408
|
+
program.command("removefrom", { hidden: true }).description("Remove Node9 hooks from an AI agent configuration").addHelpText("after", `
|
|
28409
|
+
Supported targets: ${agentTeardownTargets().join(" ")}`).argument("<target>", `The agent to remove from: ${agentTeardownTargets().join(" | ")}`).action((target) => {
|
|
28410
|
+
const agent = resolveAgentTeardown(target);
|
|
28411
|
+
if (!agent) {
|
|
28216
28412
|
console.error(
|
|
28217
|
-
chalk36.red(
|
|
28218
|
-
`Unknown target: "${target}". Supported: claude, antigravity, copilot, gemini, cursor, codex, windsurf, vscode, hermes, hud`
|
|
28219
|
-
)
|
|
28413
|
+
chalk36.red(`Unknown target: "${target}". Supported: ${agentTeardownTargets().join(", ")}`)
|
|
28220
28414
|
);
|
|
28221
28415
|
process.exit(1);
|
|
28416
|
+
return;
|
|
28222
28417
|
}
|
|
28223
28418
|
console.log(chalk36.cyan(`
|
|
28224
|
-
\u{1F6E1}\uFE0F Node9: removing hooks from ${
|
|
28419
|
+
\u{1F6E1}\uFE0F Node9: removing hooks from ${agent.label}...
|
|
28225
28420
|
`));
|
|
28226
28421
|
try {
|
|
28227
|
-
fn();
|
|
28422
|
+
agent.fn();
|
|
28228
28423
|
} catch (err2) {
|
|
28229
28424
|
console.error(chalk36.red(` \u26A0\uFE0F Failed: ${err2 instanceof Error ? err2.message : String(err2)}`));
|
|
28230
28425
|
process.exit(1);
|
|
@@ -28242,15 +28437,7 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
28242
28437
|
}
|
|
28243
28438
|
console.log(chalk36.bold("\nRemoving hooks..."));
|
|
28244
28439
|
let teardownFailed = false;
|
|
28245
|
-
for (const
|
|
28246
|
-
["Claude", teardownClaude],
|
|
28247
|
-
["Gemini", teardownGemini],
|
|
28248
|
-
["Cursor", teardownCursor],
|
|
28249
|
-
["Codex", teardownCodex],
|
|
28250
|
-
["Windsurf", teardownWindsurf],
|
|
28251
|
-
["VSCode", teardownVSCode],
|
|
28252
|
-
["Hermes", teardownHermes]
|
|
28253
|
-
]) {
|
|
28440
|
+
for (const { label: label2, fn } of AGENT_TEARDOWNS) {
|
|
28254
28441
|
try {
|
|
28255
28442
|
fn();
|
|
28256
28443
|
} catch (err2) {
|
|
@@ -28262,8 +28449,21 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
28262
28449
|
);
|
|
28263
28450
|
}
|
|
28264
28451
|
}
|
|
28452
|
+
try {
|
|
28453
|
+
const residual = getAgentWiring().filter((a) => a.wireState === "wired");
|
|
28454
|
+
if (residual.length === 0) {
|
|
28455
|
+
console.log(chalk36.green(" \u2705 Verified \u2014 no node9 hooks or plugin shims remain"));
|
|
28456
|
+
} else {
|
|
28457
|
+
teardownFailed = true;
|
|
28458
|
+
console.error(chalk36.red(" \u26A0\uFE0F Still wired after teardown:"));
|
|
28459
|
+
for (const a of residual) {
|
|
28460
|
+
console.error(chalk36.red(` \u2022 ${a.label} \u2014 ${a.settingsPath}`));
|
|
28461
|
+
}
|
|
28462
|
+
}
|
|
28463
|
+
} catch {
|
|
28464
|
+
}
|
|
28265
28465
|
if (options.purge) {
|
|
28266
|
-
const node9Dir =
|
|
28466
|
+
const node9Dir = path64.join(os57.homedir(), ".node9");
|
|
28267
28467
|
if (fs66.existsSync(node9Dir)) {
|
|
28268
28468
|
const confirmed = await confirm2({
|
|
28269
28469
|
message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
|
|
@@ -28394,7 +28594,7 @@ program.command("tail").description("Stream live agent activity to the terminal"
|
|
|
28394
28594
|
});
|
|
28395
28595
|
program.command("monitor").description("Live interactive dashboard \u2014 activity feed, approvals, security signals").action(async () => {
|
|
28396
28596
|
try {
|
|
28397
|
-
const dashboardPath =
|
|
28597
|
+
const dashboardPath = path64.join(__dirname, "dashboard.mjs");
|
|
28398
28598
|
const dynamicImport = new Function("id", "return import(id)");
|
|
28399
28599
|
const mod = await dynamicImport(`file://${dashboardPath}`);
|
|
28400
28600
|
await mod.startMonitor();
|
|
@@ -28432,9 +28632,9 @@ Claude Code spawns this command every ~300ms and writes a JSON payload to stdin.
|
|
|
28432
28632
|
Run "node9 addto claude" to register it as the statusLine.`
|
|
28433
28633
|
).argument("[subcommand]", 'Optional: "debug on" / "debug off" to toggle stdin logging').argument("[state]", 'on|off \u2014 used with "debug" subcommand').action(async (subcommand, state) => {
|
|
28434
28634
|
if (subcommand === "debug") {
|
|
28435
|
-
const flagFile =
|
|
28635
|
+
const flagFile = path64.join(os57.homedir(), ".node9", "hud-debug");
|
|
28436
28636
|
if (state === "on") {
|
|
28437
|
-
fs66.mkdirSync(
|
|
28637
|
+
fs66.mkdirSync(path64.dirname(flagFile), { recursive: true });
|
|
28438
28638
|
fs66.writeFileSync(flagFile, "");
|
|
28439
28639
|
console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
|
|
28440
28640
|
console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
|
|
@@ -28561,7 +28761,7 @@ if (process.argv[2] !== "daemon") {
|
|
|
28561
28761
|
const isCheckHook = process.argv[2] === "check";
|
|
28562
28762
|
if (isCheckHook) {
|
|
28563
28763
|
if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
|
|
28564
|
-
const logPath =
|
|
28764
|
+
const logPath = path64.join(os57.homedir(), ".node9", "hook-debug.log");
|
|
28565
28765
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
28566
28766
|
fs66.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
|
|
28567
28767
|
`);
|