@node9/proxy 1.34.0 → 1.35.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +596 -355
- package/dist/cli.mjs +592 -350
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -185,8 +185,8 @@ function sanitizeConfig(raw) {
|
|
|
185
185
|
}
|
|
186
186
|
}
|
|
187
187
|
const lines = result.error.issues.map((issue) => {
|
|
188
|
-
const
|
|
189
|
-
return ` \u2022 ${
|
|
188
|
+
const path55 = issue.path.length > 0 ? issue.path.join(".") : "root";
|
|
189
|
+
return ` \u2022 ${path55}: ${issue.message}`;
|
|
190
190
|
});
|
|
191
191
|
return {
|
|
192
192
|
sanitized,
|
|
@@ -1240,9 +1240,9 @@ function matchesPattern(text, patterns) {
|
|
|
1240
1240
|
const withoutDotSlash = text.replace(/^\.\//, "");
|
|
1241
1241
|
return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
|
|
1242
1242
|
}
|
|
1243
|
-
function getNestedValue(obj,
|
|
1243
|
+
function getNestedValue(obj, path55) {
|
|
1244
1244
|
if (!obj || typeof obj !== "object") return null;
|
|
1245
|
-
const segments =
|
|
1245
|
+
const segments = path55.split(".");
|
|
1246
1246
|
for (const seg of segments) {
|
|
1247
1247
|
if (FORBIDDEN_PATH_SEGMENTS.has(seg)) return null;
|
|
1248
1248
|
}
|
|
@@ -5869,6 +5869,57 @@ function validateApiUrl(raw) {
|
|
|
5869
5869
|
}
|
|
5870
5870
|
return null;
|
|
5871
5871
|
}
|
|
5872
|
+
function auditLocalAllow(toolName, args, checkedBy, creds, meta, dlpInfo, containsSensitiveArgs = false, riskMetadata) {
|
|
5873
|
+
const validated = validateApiUrl(creds.apiUrl);
|
|
5874
|
+
if (!validated) {
|
|
5875
|
+
try {
|
|
5876
|
+
fs10.appendFileSync(
|
|
5877
|
+
HOOK_DEBUG_LOG,
|
|
5878
|
+
`[audit] refused to send: invalid apiUrl scheme/host (got "${String(creds.apiUrl).slice(0, 200)}")
|
|
5879
|
+
`
|
|
5880
|
+
);
|
|
5881
|
+
} catch {
|
|
5882
|
+
}
|
|
5883
|
+
return Promise.resolve();
|
|
5884
|
+
}
|
|
5885
|
+
const safeArgs = containsSensitiveArgs ? { tool: toolName, redacted: true } : args;
|
|
5886
|
+
const dlpSample = dlpInfo && typeof dlpInfo.redactedSample === "string" ? dlpInfo.redactedSample.slice(0, DLP_SAMPLE_MAX_LEN) : void 0;
|
|
5887
|
+
const dlpPattern = dlpInfo && typeof dlpInfo.pattern === "string" ? dlpInfo.pattern.slice(0, DLP_PATTERN_MAX_LEN) : void 0;
|
|
5888
|
+
const safeCheckedBy = KNOWN_CHECKED_BY.has(checkedBy) ? checkedBy : "unknown";
|
|
5889
|
+
const cleanedRiskMetadata = riskMetadata ? Object.fromEntries(
|
|
5890
|
+
Object.entries(riskMetadata).filter(
|
|
5891
|
+
([, v]) => typeof v === "string" && v.length > 0 || typeof v === "number" && Number.isFinite(v)
|
|
5892
|
+
)
|
|
5893
|
+
) : void 0;
|
|
5894
|
+
const hasRiskMetadata = cleanedRiskMetadata && Object.keys(cleanedRiskMetadata).length > 0;
|
|
5895
|
+
return fetch(`${validated.toString().replace(/\/$/, "")}/audit`, {
|
|
5896
|
+
method: "POST",
|
|
5897
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${creds.apiKey}` },
|
|
5898
|
+
body: JSON.stringify({
|
|
5899
|
+
toolName,
|
|
5900
|
+
args: safeArgs,
|
|
5901
|
+
checkedBy: safeCheckedBy,
|
|
5902
|
+
...dlpInfo && { dlpPattern, dlpSample },
|
|
5903
|
+
...hasRiskMetadata && { riskMetadata: cleanedRiskMetadata },
|
|
5904
|
+
// session_id (Claude Code + Gemini CLI) groups all audit rows from one
|
|
5905
|
+
// agent run; transcript_path is the authoritative pointer to the
|
|
5906
|
+
// session log (survives Gemini resume drift). Both optional —
|
|
5907
|
+
// unsupported agents (MCP-mediated) leave them undefined.
|
|
5908
|
+
...meta?.sessionId && { runId: meta.sessionId },
|
|
5909
|
+
...meta?.transcriptPath && { transcriptPath: meta.transcriptPath },
|
|
5910
|
+
context: {
|
|
5911
|
+
agent: meta?.agent,
|
|
5912
|
+
mcpServer: meta?.mcpServer,
|
|
5913
|
+
hostname: os9.hostname(),
|
|
5914
|
+
cwd: process.cwd(),
|
|
5915
|
+
platform: os9.platform()
|
|
5916
|
+
}
|
|
5917
|
+
}),
|
|
5918
|
+
signal: AbortSignal.timeout(5e3)
|
|
5919
|
+
}).then(() => {
|
|
5920
|
+
}).catch(() => {
|
|
5921
|
+
});
|
|
5922
|
+
}
|
|
5872
5923
|
async function initNode9SaaS(toolName, args, creds, meta, riskMetadata, agentPolicy, forceReview) {
|
|
5873
5924
|
const controller = new AbortController();
|
|
5874
5925
|
const timeout = setTimeout(() => controller.abort(), 1e4);
|
|
@@ -5992,10 +6043,44 @@ async function resolveNode9SaaS(requestId, creds, approved, decidedBy) {
|
|
|
5992
6043
|
);
|
|
5993
6044
|
}
|
|
5994
6045
|
}
|
|
6046
|
+
var DLP_SAMPLE_MAX_LEN, DLP_PATTERN_MAX_LEN, KNOWN_CHECKED_BY;
|
|
5995
6047
|
var init_cloud = __esm({
|
|
5996
6048
|
"src/auth/cloud.ts"() {
|
|
5997
6049
|
"use strict";
|
|
5998
6050
|
init_audit();
|
|
6051
|
+
DLP_SAMPLE_MAX_LEN = 200;
|
|
6052
|
+
DLP_PATTERN_MAX_LEN = 100;
|
|
6053
|
+
KNOWN_CHECKED_BY = /* @__PURE__ */ new Set([
|
|
6054
|
+
"dlp-block",
|
|
6055
|
+
"observe-mode-dlp-would-block",
|
|
6056
|
+
"dlp-review-flagged",
|
|
6057
|
+
"loop-detected",
|
|
6058
|
+
"audit-mode",
|
|
6059
|
+
"local-policy",
|
|
6060
|
+
"smart-rule-block",
|
|
6061
|
+
// Smart-rule block was downgraded to review because the daemon was
|
|
6062
|
+
// running and we're not in CI. The block attempt is still recorded;
|
|
6063
|
+
// the user got a popup. Distinct from 'smart-rule-block' so the
|
|
6064
|
+
// dashboard can show "block rule overridden" separately from a hard
|
|
6065
|
+
// block that fired with no human in the loop.
|
|
6066
|
+
"smart-rule-block-override",
|
|
6067
|
+
"persistent",
|
|
6068
|
+
"trust",
|
|
6069
|
+
"observe-mode",
|
|
6070
|
+
"observe-mode-would-block",
|
|
6071
|
+
// MCP supply-chain: the gateway pinned a server's tool definitions and they
|
|
6072
|
+
// changed since (possible tool poisoning / rug pull). Emitted as a synthetic
|
|
6073
|
+
// audit row so the SaaS surfaces it as a blocked event. The firewall maps
|
|
6074
|
+
// this checkedBy to AUTO_BLOCKED. See doc/roadmap/active/saas-value-first.md
|
|
6075
|
+
// (workstream B-Tier2).
|
|
6076
|
+
"mcp-pin-mismatch",
|
|
6077
|
+
// MCP visibility (B-Tier2, informational — NOT blocks): the gateway
|
|
6078
|
+
// discovered a server's tool inventory (mcp-discovered) or saw an oversized
|
|
6079
|
+
// tool response that bloats the context window (mcp-large-response). Stored
|
|
6080
|
+
// AUTO_ALLOWED; carries mcpToolCount / mcpResponseBytes in riskMetadata.
|
|
6081
|
+
"mcp-discovered",
|
|
6082
|
+
"mcp-large-response"
|
|
6083
|
+
]);
|
|
5999
6084
|
}
|
|
6000
6085
|
});
|
|
6001
6086
|
|
|
@@ -16819,9 +16904,9 @@ __export(tail_exports, {
|
|
|
16819
16904
|
});
|
|
16820
16905
|
import http2 from "http";
|
|
16821
16906
|
import chalk29 from "chalk";
|
|
16822
|
-
import
|
|
16823
|
-
import
|
|
16824
|
-
import
|
|
16907
|
+
import fs51 from "fs";
|
|
16908
|
+
import os46 from "os";
|
|
16909
|
+
import path52 from "path";
|
|
16825
16910
|
import readline6 from "readline";
|
|
16826
16911
|
import { spawn as spawn8 } from "child_process";
|
|
16827
16912
|
function shortenPathSummary(s) {
|
|
@@ -16845,20 +16930,20 @@ function getModelContextLimit(model) {
|
|
|
16845
16930
|
return 2e5;
|
|
16846
16931
|
}
|
|
16847
16932
|
function readSessionUsage() {
|
|
16848
|
-
const projectsDir =
|
|
16849
|
-
if (!
|
|
16933
|
+
const projectsDir = path52.join(os46.homedir(), ".claude", "projects");
|
|
16934
|
+
if (!fs51.existsSync(projectsDir)) return null;
|
|
16850
16935
|
let latestFile = null;
|
|
16851
16936
|
let latestMtime = 0;
|
|
16852
16937
|
try {
|
|
16853
|
-
for (const dir of
|
|
16854
|
-
const dirPath =
|
|
16938
|
+
for (const dir of fs51.readdirSync(projectsDir)) {
|
|
16939
|
+
const dirPath = path52.join(projectsDir, dir);
|
|
16855
16940
|
try {
|
|
16856
|
-
if (!
|
|
16857
|
-
for (const file of
|
|
16941
|
+
if (!fs51.statSync(dirPath).isDirectory()) continue;
|
|
16942
|
+
for (const file of fs51.readdirSync(dirPath)) {
|
|
16858
16943
|
if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
|
|
16859
|
-
const filePath =
|
|
16944
|
+
const filePath = path52.join(dirPath, file);
|
|
16860
16945
|
try {
|
|
16861
|
-
const mtime =
|
|
16946
|
+
const mtime = fs51.statSync(filePath).mtimeMs;
|
|
16862
16947
|
if (mtime > latestMtime) {
|
|
16863
16948
|
latestMtime = mtime;
|
|
16864
16949
|
latestFile = filePath;
|
|
@@ -16873,7 +16958,7 @@ function readSessionUsage() {
|
|
|
16873
16958
|
}
|
|
16874
16959
|
if (!latestFile) return null;
|
|
16875
16960
|
try {
|
|
16876
|
-
const lines =
|
|
16961
|
+
const lines = fs51.readFileSync(latestFile, "utf-8").split("\n");
|
|
16877
16962
|
let lastModel = "";
|
|
16878
16963
|
let lastInput = 0;
|
|
16879
16964
|
let lastOutput = 0;
|
|
@@ -16934,7 +17019,7 @@ function formatBase(activity) {
|
|
|
16934
17019
|
const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
|
|
16935
17020
|
const icon = getIcon(activity.tool);
|
|
16936
17021
|
const toolName = activity.tool.slice(0, 16).padEnd(16);
|
|
16937
|
-
const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(
|
|
17022
|
+
const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(os46.homedir(), "~");
|
|
16938
17023
|
const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
|
|
16939
17024
|
return `${chalk29.gray(time)} ${icon} ${agentLabel(activity.agent, activity.mcpServer, activity.sessionId)}${chalk29.white.bold(toolName)} ${chalk29.dim(argsPreview)}`;
|
|
16940
17025
|
}
|
|
@@ -16973,9 +17058,9 @@ function renderPending(activity) {
|
|
|
16973
17058
|
}
|
|
16974
17059
|
async function ensureDaemon() {
|
|
16975
17060
|
let pidPort = null;
|
|
16976
|
-
if (
|
|
17061
|
+
if (fs51.existsSync(PID_FILE)) {
|
|
16977
17062
|
try {
|
|
16978
|
-
const { port } = JSON.parse(
|
|
17063
|
+
const { port } = JSON.parse(fs51.readFileSync(PID_FILE, "utf-8"));
|
|
16979
17064
|
pidPort = port;
|
|
16980
17065
|
} catch {
|
|
16981
17066
|
console.error(chalk29.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
|
|
@@ -17131,9 +17216,9 @@ function buildRecoveryCardLines(req) {
|
|
|
17131
17216
|
];
|
|
17132
17217
|
}
|
|
17133
17218
|
function readApproversFromDisk() {
|
|
17134
|
-
const configPath =
|
|
17219
|
+
const configPath = path52.join(os46.homedir(), ".node9", "config.json");
|
|
17135
17220
|
try {
|
|
17136
|
-
const raw = JSON.parse(
|
|
17221
|
+
const raw = JSON.parse(fs51.readFileSync(configPath, "utf-8"));
|
|
17137
17222
|
const settings = raw.settings ?? {};
|
|
17138
17223
|
return settings.approvers ?? {};
|
|
17139
17224
|
} catch {
|
|
@@ -17149,15 +17234,15 @@ function approverStatusLine() {
|
|
|
17149
17234
|
return `${fmt("native", "native")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
|
|
17150
17235
|
}
|
|
17151
17236
|
function toggleApprover(channel) {
|
|
17152
|
-
const configPath =
|
|
17237
|
+
const configPath = path52.join(os46.homedir(), ".node9", "config.json");
|
|
17153
17238
|
try {
|
|
17154
|
-
const raw = JSON.parse(
|
|
17239
|
+
const raw = JSON.parse(fs51.readFileSync(configPath, "utf-8"));
|
|
17155
17240
|
const settings = raw.settings ?? {};
|
|
17156
17241
|
const approvers = settings.approvers ?? {};
|
|
17157
17242
|
approvers[channel] = approvers[channel] === false;
|
|
17158
17243
|
settings.approvers = approvers;
|
|
17159
17244
|
raw.settings = settings;
|
|
17160
|
-
|
|
17245
|
+
fs51.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
|
|
17161
17246
|
} catch (err2) {
|
|
17162
17247
|
process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
|
|
17163
17248
|
`);
|
|
@@ -17329,8 +17414,8 @@ async function startTail(options = {}) {
|
|
|
17329
17414
|
}
|
|
17330
17415
|
postDecisionHttp(req2.id, httpDecision, authToken, port, httpOpts).catch((err2) => {
|
|
17331
17416
|
try {
|
|
17332
|
-
|
|
17333
|
-
|
|
17417
|
+
fs51.appendFileSync(
|
|
17418
|
+
path52.join(os46.homedir(), ".node9", "hook-debug.log"),
|
|
17334
17419
|
`[tail] POST /decision failed: ${String(err2)}
|
|
17335
17420
|
`
|
|
17336
17421
|
);
|
|
@@ -17394,9 +17479,9 @@ async function startTail(options = {}) {
|
|
|
17394
17479
|
};
|
|
17395
17480
|
process.stdin.on("keypress", onKeypress);
|
|
17396
17481
|
}
|
|
17397
|
-
const auditLog =
|
|
17482
|
+
const auditLog = path52.join(os46.homedir(), ".node9", "audit.log");
|
|
17398
17483
|
try {
|
|
17399
|
-
const unackedDlp =
|
|
17484
|
+
const unackedDlp = fs51.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
|
|
17400
17485
|
if (unackedDlp > 0) {
|
|
17401
17486
|
console.log("");
|
|
17402
17487
|
console.log(
|
|
@@ -17436,7 +17521,7 @@ async function startTail(options = {}) {
|
|
|
17436
17521
|
if (stallWarned) return;
|
|
17437
17522
|
if (Date.now() - lastActivityFromDaemon < STALL_THRESHOLD_MS) return;
|
|
17438
17523
|
try {
|
|
17439
|
-
const auditMtime =
|
|
17524
|
+
const auditMtime = fs51.statSync(auditLog).mtimeMs;
|
|
17440
17525
|
if (Date.now() - auditMtime >= STALL_THRESHOLD_MS) return;
|
|
17441
17526
|
console.log("");
|
|
17442
17527
|
console.log(
|
|
@@ -17627,7 +17712,7 @@ var init_tail = __esm({
|
|
|
17627
17712
|
"use strict";
|
|
17628
17713
|
init_daemon2();
|
|
17629
17714
|
init_daemon();
|
|
17630
|
-
PID_FILE =
|
|
17715
|
+
PID_FILE = path52.join(os46.homedir(), ".node9", "daemon.pid");
|
|
17631
17716
|
ICONS = {
|
|
17632
17717
|
bash: "\u{1F4BB}",
|
|
17633
17718
|
shell: "\u{1F4BB}",
|
|
@@ -17675,9 +17760,9 @@ __export(hud_exports, {
|
|
|
17675
17760
|
main: () => main,
|
|
17676
17761
|
renderEnvironmentLine: () => renderEnvironmentLine
|
|
17677
17762
|
});
|
|
17678
|
-
import
|
|
17679
|
-
import
|
|
17680
|
-
import
|
|
17763
|
+
import fs52 from "fs";
|
|
17764
|
+
import path53 from "path";
|
|
17765
|
+
import os47 from "os";
|
|
17681
17766
|
import http3 from "http";
|
|
17682
17767
|
async function readStdin() {
|
|
17683
17768
|
const chunks = [];
|
|
@@ -17753,9 +17838,9 @@ function formatTimeLeft(resetsAt) {
|
|
|
17753
17838
|
return ` (${m}m left)`;
|
|
17754
17839
|
}
|
|
17755
17840
|
function safeReadJson(filePath) {
|
|
17756
|
-
if (!
|
|
17841
|
+
if (!fs52.existsSync(filePath)) return null;
|
|
17757
17842
|
try {
|
|
17758
|
-
return JSON.parse(
|
|
17843
|
+
return JSON.parse(fs52.readFileSync(filePath, "utf-8"));
|
|
17759
17844
|
} catch {
|
|
17760
17845
|
return null;
|
|
17761
17846
|
}
|
|
@@ -17776,12 +17861,12 @@ function countHooksInFile(filePath) {
|
|
|
17776
17861
|
return Object.keys(cfg.hooks).length;
|
|
17777
17862
|
}
|
|
17778
17863
|
function countRulesInDir(rulesDir) {
|
|
17779
|
-
if (!
|
|
17864
|
+
if (!fs52.existsSync(rulesDir)) return 0;
|
|
17780
17865
|
let count = 0;
|
|
17781
17866
|
try {
|
|
17782
|
-
for (const entry of
|
|
17867
|
+
for (const entry of fs52.readdirSync(rulesDir, { withFileTypes: true })) {
|
|
17783
17868
|
if (entry.isDirectory()) {
|
|
17784
|
-
count += countRulesInDir(
|
|
17869
|
+
count += countRulesInDir(path53.join(rulesDir, entry.name));
|
|
17785
17870
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
17786
17871
|
count++;
|
|
17787
17872
|
}
|
|
@@ -17792,46 +17877,46 @@ function countRulesInDir(rulesDir) {
|
|
|
17792
17877
|
}
|
|
17793
17878
|
function isSamePath(a, b) {
|
|
17794
17879
|
try {
|
|
17795
|
-
return
|
|
17880
|
+
return path53.resolve(a) === path53.resolve(b);
|
|
17796
17881
|
} catch {
|
|
17797
17882
|
return false;
|
|
17798
17883
|
}
|
|
17799
17884
|
}
|
|
17800
17885
|
function countConfigs(cwd) {
|
|
17801
|
-
const homeDir2 =
|
|
17802
|
-
const claudeDir =
|
|
17886
|
+
const homeDir2 = os47.homedir();
|
|
17887
|
+
const claudeDir = path53.join(homeDir2, ".claude");
|
|
17803
17888
|
let claudeMdCount = 0;
|
|
17804
17889
|
let rulesCount = 0;
|
|
17805
17890
|
let hooksCount = 0;
|
|
17806
17891
|
const userMcpServers = /* @__PURE__ */ new Set();
|
|
17807
17892
|
const projectMcpServers = /* @__PURE__ */ new Set();
|
|
17808
|
-
if (
|
|
17809
|
-
rulesCount += countRulesInDir(
|
|
17810
|
-
const userSettings =
|
|
17893
|
+
if (fs52.existsSync(path53.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
17894
|
+
rulesCount += countRulesInDir(path53.join(claudeDir, "rules"));
|
|
17895
|
+
const userSettings = path53.join(claudeDir, "settings.json");
|
|
17811
17896
|
for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
|
|
17812
17897
|
hooksCount += countHooksInFile(userSettings);
|
|
17813
|
-
const userClaudeJson =
|
|
17898
|
+
const userClaudeJson = path53.join(homeDir2, ".claude.json");
|
|
17814
17899
|
for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
|
|
17815
17900
|
for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
|
|
17816
17901
|
userMcpServers.delete(name);
|
|
17817
17902
|
}
|
|
17818
17903
|
if (cwd) {
|
|
17819
|
-
if (
|
|
17820
|
-
if (
|
|
17821
|
-
const projectClaudeDir =
|
|
17904
|
+
if (fs52.existsSync(path53.join(cwd, "CLAUDE.md"))) claudeMdCount++;
|
|
17905
|
+
if (fs52.existsSync(path53.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
|
|
17906
|
+
const projectClaudeDir = path53.join(cwd, ".claude");
|
|
17822
17907
|
const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
|
|
17823
17908
|
if (!overlapsUserScope) {
|
|
17824
|
-
if (
|
|
17825
|
-
rulesCount += countRulesInDir(
|
|
17826
|
-
const projSettings =
|
|
17909
|
+
if (fs52.existsSync(path53.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
17910
|
+
rulesCount += countRulesInDir(path53.join(projectClaudeDir, "rules"));
|
|
17911
|
+
const projSettings = path53.join(projectClaudeDir, "settings.json");
|
|
17827
17912
|
for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
|
|
17828
17913
|
hooksCount += countHooksInFile(projSettings);
|
|
17829
17914
|
}
|
|
17830
|
-
if (
|
|
17831
|
-
const localSettings =
|
|
17915
|
+
if (fs52.existsSync(path53.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
|
|
17916
|
+
const localSettings = path53.join(projectClaudeDir, "settings.local.json");
|
|
17832
17917
|
for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
|
|
17833
17918
|
hooksCount += countHooksInFile(localSettings);
|
|
17834
|
-
const mcpJsonServers = getMcpServerNames(
|
|
17919
|
+
const mcpJsonServers = getMcpServerNames(path53.join(cwd, ".mcp.json"));
|
|
17835
17920
|
const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
|
|
17836
17921
|
for (const name of disabledMcpJson) mcpJsonServers.delete(name);
|
|
17837
17922
|
for (const name of mcpJsonServers) projectMcpServers.add(name);
|
|
@@ -17864,12 +17949,12 @@ function readActiveShieldsHud() {
|
|
|
17864
17949
|
return shieldsCache.value;
|
|
17865
17950
|
}
|
|
17866
17951
|
try {
|
|
17867
|
-
const shieldsPath =
|
|
17868
|
-
if (!
|
|
17952
|
+
const shieldsPath = path53.join(os47.homedir(), ".node9", "shields.json");
|
|
17953
|
+
if (!fs52.existsSync(shieldsPath)) {
|
|
17869
17954
|
shieldsCache = { value: [], ts: now };
|
|
17870
17955
|
return [];
|
|
17871
17956
|
}
|
|
17872
|
-
const parsed = JSON.parse(
|
|
17957
|
+
const parsed = JSON.parse(fs52.readFileSync(shieldsPath, "utf-8"));
|
|
17873
17958
|
if (!Array.isArray(parsed.active)) {
|
|
17874
17959
|
shieldsCache = { value: [], ts: now };
|
|
17875
17960
|
return [];
|
|
@@ -17971,17 +18056,17 @@ function renderContextLine(stdin) {
|
|
|
17971
18056
|
async function main() {
|
|
17972
18057
|
try {
|
|
17973
18058
|
const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
|
|
17974
|
-
if (
|
|
18059
|
+
if (fs52.existsSync(path53.join(os47.homedir(), ".node9", "hud-debug"))) {
|
|
17975
18060
|
try {
|
|
17976
|
-
const logPath =
|
|
18061
|
+
const logPath = path53.join(os47.homedir(), ".node9", "hud-debug.log");
|
|
17977
18062
|
const MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
17978
18063
|
let size = 0;
|
|
17979
18064
|
try {
|
|
17980
|
-
size =
|
|
18065
|
+
size = fs52.statSync(logPath).size;
|
|
17981
18066
|
} catch {
|
|
17982
18067
|
}
|
|
17983
18068
|
if (size < MAX_LOG_SIZE) {
|
|
17984
|
-
|
|
18069
|
+
fs52.appendFileSync(
|
|
17985
18070
|
logPath,
|
|
17986
18071
|
JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
|
|
17987
18072
|
);
|
|
@@ -18002,11 +18087,11 @@ async function main() {
|
|
|
18002
18087
|
try {
|
|
18003
18088
|
const cwd = stdin.cwd ?? process.cwd();
|
|
18004
18089
|
for (const configPath of [
|
|
18005
|
-
|
|
18006
|
-
|
|
18090
|
+
path53.join(cwd, "node9.config.json"),
|
|
18091
|
+
path53.join(os47.homedir(), ".node9", "config.json")
|
|
18007
18092
|
]) {
|
|
18008
|
-
if (!
|
|
18009
|
-
const cfg = JSON.parse(
|
|
18093
|
+
if (!fs52.existsSync(configPath)) continue;
|
|
18094
|
+
const cfg = JSON.parse(fs52.readFileSync(configPath, "utf-8"));
|
|
18010
18095
|
const hud = cfg.settings?.hud;
|
|
18011
18096
|
if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
|
|
18012
18097
|
}
|
|
@@ -18053,9 +18138,9 @@ init_setup();
|
|
|
18053
18138
|
init_daemon2();
|
|
18054
18139
|
import { Command } from "commander";
|
|
18055
18140
|
import chalk30 from "chalk";
|
|
18056
|
-
import
|
|
18057
|
-
import
|
|
18058
|
-
import
|
|
18141
|
+
import fs53 from "fs";
|
|
18142
|
+
import path54 from "path";
|
|
18143
|
+
import os48 from "os";
|
|
18059
18144
|
import { confirm as confirm2 } from "@inquirer/prompts";
|
|
18060
18145
|
|
|
18061
18146
|
// src/utils/duration.ts
|
|
@@ -19845,13 +19930,149 @@ function registerConfigShowCommand(program2) {
|
|
|
19845
19930
|
init_daemon();
|
|
19846
19931
|
init_config();
|
|
19847
19932
|
import chalk11 from "chalk";
|
|
19933
|
+
import fs39 from "fs";
|
|
19934
|
+
import path40 from "path";
|
|
19935
|
+
import os35 from "os";
|
|
19936
|
+
import { execSync } from "child_process";
|
|
19937
|
+
|
|
19938
|
+
// src/agent-wiring.ts
|
|
19939
|
+
init_setup();
|
|
19848
19940
|
import fs38 from "fs";
|
|
19849
19941
|
import path39 from "path";
|
|
19850
19942
|
import os34 from "os";
|
|
19851
|
-
import
|
|
19943
|
+
import * as yaml2 from "yaml";
|
|
19944
|
+
function readJson2(filePath) {
|
|
19945
|
+
if (!fs38.existsSync(filePath)) return null;
|
|
19946
|
+
try {
|
|
19947
|
+
return JSON.parse(fs38.readFileSync(filePath, "utf-8"));
|
|
19948
|
+
} catch {
|
|
19949
|
+
return "invalid";
|
|
19950
|
+
}
|
|
19951
|
+
}
|
|
19952
|
+
function matchersHaveNode9Hook(matchers) {
|
|
19953
|
+
return (matchers ?? []).some((m) => (m.hooks ?? []).some((h) => isNode9Hook(h.command)));
|
|
19954
|
+
}
|
|
19955
|
+
function flatHaveNode9Hook(entries) {
|
|
19956
|
+
return (Array.isArray(entries) ? entries : []).some((h) => isNode9Hook(h.command));
|
|
19957
|
+
}
|
|
19958
|
+
function jsonWire(filePath, read) {
|
|
19959
|
+
const parsed = readJson2(filePath);
|
|
19960
|
+
if (parsed === null) return "absent";
|
|
19961
|
+
if (parsed === "invalid") return "invalid";
|
|
19962
|
+
return read(parsed) ? "wired" : "unwired";
|
|
19963
|
+
}
|
|
19964
|
+
function hermesWire(home) {
|
|
19965
|
+
const configPath = hermesConfigPath(home);
|
|
19966
|
+
if (!fs38.existsSync(configPath)) return "absent";
|
|
19967
|
+
let raw;
|
|
19968
|
+
try {
|
|
19969
|
+
raw = fs38.readFileSync(configPath, "utf-8");
|
|
19970
|
+
} catch {
|
|
19971
|
+
return "absent";
|
|
19972
|
+
}
|
|
19973
|
+
try {
|
|
19974
|
+
const cfg = yaml2.parse(raw);
|
|
19975
|
+
const pre = (cfg?.hooks?.pre_tool_call ?? []).some(
|
|
19976
|
+
(e) => typeof e?.command === "string" && isNode9Hook(e.command)
|
|
19977
|
+
);
|
|
19978
|
+
return pre ? "wired" : "unwired";
|
|
19979
|
+
} catch {
|
|
19980
|
+
return "invalid";
|
|
19981
|
+
}
|
|
19982
|
+
}
|
|
19983
|
+
var AGENT_SPECS = [
|
|
19984
|
+
{
|
|
19985
|
+
id: "claude",
|
|
19986
|
+
label: "Claude Code",
|
|
19987
|
+
hookLabel: "PreToolUse hook",
|
|
19988
|
+
setupCommand: "node9 setup claude",
|
|
19989
|
+
settingsPath: (h) => path39.join(h, ".claude", "settings.json"),
|
|
19990
|
+
wireState: (h) => jsonWire(
|
|
19991
|
+
path39.join(h, ".claude", "settings.json"),
|
|
19992
|
+
(p) => matchersHaveNode9Hook(p.hooks?.PreToolUse)
|
|
19993
|
+
)
|
|
19994
|
+
},
|
|
19995
|
+
{
|
|
19996
|
+
id: "gemini",
|
|
19997
|
+
label: "Gemini CLI",
|
|
19998
|
+
hookLabel: "BeforeTool hook",
|
|
19999
|
+
setupCommand: "node9 setup gemini",
|
|
20000
|
+
settingsPath: (h) => path39.join(h, ".gemini", "settings.json"),
|
|
20001
|
+
wireState: (h) => jsonWire(
|
|
20002
|
+
path39.join(h, ".gemini", "settings.json"),
|
|
20003
|
+
(p) => matchersHaveNode9Hook(p.hooks?.BeforeTool)
|
|
20004
|
+
)
|
|
20005
|
+
},
|
|
20006
|
+
{
|
|
20007
|
+
id: "codex",
|
|
20008
|
+
label: "Codex",
|
|
20009
|
+
hookLabel: "PreToolUse hook",
|
|
20010
|
+
setupCommand: "node9 setup codex",
|
|
20011
|
+
settingsPath: (h) => path39.join(h, ".codex", "hooks.json"),
|
|
20012
|
+
wireState: (h) => jsonWire(
|
|
20013
|
+
path39.join(h, ".codex", "hooks.json"),
|
|
20014
|
+
(p) => matchersHaveNode9Hook(p.hooks?.PreToolUse)
|
|
20015
|
+
)
|
|
20016
|
+
},
|
|
20017
|
+
{
|
|
20018
|
+
id: "antigravity",
|
|
20019
|
+
label: "Antigravity",
|
|
20020
|
+
hookLabel: "PreToolUse hook",
|
|
20021
|
+
setupCommand: "node9 setup antigravity",
|
|
20022
|
+
settingsPath: (h) => path39.join(h, ".gemini", "config", "hooks.json"),
|
|
20023
|
+
wireState: (h) => jsonWire(
|
|
20024
|
+
path39.join(h, ".gemini", "config", "hooks.json"),
|
|
20025
|
+
(p) => matchersHaveNode9Hook(p.hooks?.PreToolUse)
|
|
20026
|
+
)
|
|
20027
|
+
},
|
|
20028
|
+
{
|
|
20029
|
+
id: "copilot",
|
|
20030
|
+
label: "GitHub Copilot",
|
|
20031
|
+
hookLabel: "PreToolUse hook",
|
|
20032
|
+
setupCommand: "node9 setup copilot",
|
|
20033
|
+
settingsPath: (h) => path39.join(h, ".copilot", "hooks", "node9.json"),
|
|
20034
|
+
wireState: (h) => jsonWire(
|
|
20035
|
+
path39.join(h, ".copilot", "hooks", "node9.json"),
|
|
20036
|
+
(p) => flatHaveNode9Hook(p.hooks?.PreToolUse)
|
|
20037
|
+
)
|
|
20038
|
+
},
|
|
20039
|
+
{
|
|
20040
|
+
id: "cursor",
|
|
20041
|
+
label: "Cursor",
|
|
20042
|
+
hookLabel: "preToolUse hook",
|
|
20043
|
+
setupCommand: "node9 setup cursor",
|
|
20044
|
+
settingsPath: (h) => path39.join(h, ".cursor", "hooks.json"),
|
|
20045
|
+
wireState: (h) => jsonWire(
|
|
20046
|
+
path39.join(h, ".cursor", "hooks.json"),
|
|
20047
|
+
(p) => flatHaveNode9Hook(p.hooks?.preToolUse)
|
|
20048
|
+
)
|
|
20049
|
+
},
|
|
20050
|
+
{
|
|
20051
|
+
id: "hermes",
|
|
20052
|
+
label: "Hermes Agent",
|
|
20053
|
+
hookLabel: "pre_tool_call hook",
|
|
20054
|
+
setupCommand: "node9 setup hermes",
|
|
20055
|
+
settingsPath: (h) => hermesConfigPath(h),
|
|
20056
|
+
wireState: (h) => hermesWire(h)
|
|
20057
|
+
}
|
|
20058
|
+
];
|
|
20059
|
+
function getAgentWiring(home = os34.homedir()) {
|
|
20060
|
+
const detected = detectAgents(home);
|
|
20061
|
+
return AGENT_SPECS.map((spec) => ({
|
|
20062
|
+
id: spec.id,
|
|
20063
|
+
label: spec.label,
|
|
20064
|
+
hookLabel: spec.hookLabel,
|
|
20065
|
+
setupCommand: spec.setupCommand,
|
|
20066
|
+
settingsPath: spec.settingsPath(home),
|
|
20067
|
+
installed: detected[spec.id],
|
|
20068
|
+
wireState: spec.wireState(home)
|
|
20069
|
+
}));
|
|
20070
|
+
}
|
|
20071
|
+
|
|
20072
|
+
// src/cli/commands/doctor.ts
|
|
19852
20073
|
function registerDoctorCommand(program2, version2) {
|
|
19853
20074
|
program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(async () => {
|
|
19854
|
-
const homeDir2 =
|
|
20075
|
+
const homeDir2 = os35.homedir();
|
|
19855
20076
|
let failures = 0;
|
|
19856
20077
|
function pass(msg) {
|
|
19857
20078
|
console.log(chalk11.green(" \u2705 ") + msg);
|
|
@@ -19900,10 +20121,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
19900
20121
|
);
|
|
19901
20122
|
}
|
|
19902
20123
|
section("Configuration");
|
|
19903
|
-
const globalConfigPath =
|
|
19904
|
-
if (
|
|
20124
|
+
const globalConfigPath = path40.join(homeDir2, ".node9", "config.json");
|
|
20125
|
+
if (fs39.existsSync(globalConfigPath)) {
|
|
19905
20126
|
try {
|
|
19906
|
-
JSON.parse(
|
|
20127
|
+
JSON.parse(fs39.readFileSync(globalConfigPath, "utf-8"));
|
|
19907
20128
|
pass("~/.node9/config.json found and valid");
|
|
19908
20129
|
} catch {
|
|
19909
20130
|
fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
|
|
@@ -19911,10 +20132,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
19911
20132
|
} else {
|
|
19912
20133
|
warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
|
|
19913
20134
|
}
|
|
19914
|
-
const projectConfigPath =
|
|
19915
|
-
if (
|
|
20135
|
+
const projectConfigPath = path40.join(process.cwd(), "node9.config.json");
|
|
20136
|
+
if (fs39.existsSync(projectConfigPath)) {
|
|
19916
20137
|
try {
|
|
19917
|
-
JSON.parse(
|
|
20138
|
+
JSON.parse(fs39.readFileSync(projectConfigPath, "utf-8"));
|
|
19918
20139
|
pass("node9.config.json found and valid (project)");
|
|
19919
20140
|
} catch {
|
|
19920
20141
|
fail(
|
|
@@ -19923,8 +20144,8 @@ function registerDoctorCommand(program2, version2) {
|
|
|
19923
20144
|
);
|
|
19924
20145
|
}
|
|
19925
20146
|
}
|
|
19926
|
-
const credsPath =
|
|
19927
|
-
if (
|
|
20147
|
+
const credsPath = path40.join(homeDir2, ".node9", "credentials.json");
|
|
20148
|
+
if (fs39.existsSync(credsPath)) {
|
|
19928
20149
|
pass("Cloud credentials found (~/.node9/credentials.json)");
|
|
19929
20150
|
} else {
|
|
19930
20151
|
warn(
|
|
@@ -19933,62 +20154,24 @@ function registerDoctorCommand(program2, version2) {
|
|
|
19933
20154
|
);
|
|
19934
20155
|
}
|
|
19935
20156
|
section("Agent Hooks");
|
|
19936
|
-
const
|
|
19937
|
-
|
|
19938
|
-
|
|
19939
|
-
|
|
19940
|
-
|
|
19941
|
-
|
|
19942
|
-
|
|
19943
|
-
|
|
19944
|
-
|
|
19945
|
-
|
|
19946
|
-
"Claude Code \u2014 hooks file found but node9 hook missing",
|
|
19947
|
-
"Run: node9 setup claude"
|
|
19948
|
-
);
|
|
19949
|
-
} catch {
|
|
19950
|
-
fail("Claude Code \u2014 ~/.claude/settings.json is invalid JSON");
|
|
19951
|
-
}
|
|
19952
|
-
} else {
|
|
19953
|
-
warn("Claude Code \u2014 not configured", "Run: node9 setup claude");
|
|
19954
|
-
}
|
|
19955
|
-
const geminiSettingsPath = path39.join(homeDir2, ".gemini", "settings.json");
|
|
19956
|
-
if (fs38.existsSync(geminiSettingsPath)) {
|
|
19957
|
-
try {
|
|
19958
|
-
const gs = JSON.parse(fs38.readFileSync(geminiSettingsPath, "utf-8"));
|
|
19959
|
-
const hasHook = gs.hooks?.BeforeTool?.some(
|
|
19960
|
-
(m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
|
|
19961
|
-
);
|
|
19962
|
-
if (hasHook) pass("Gemini CLI \u2014 BeforeTool hook active");
|
|
19963
|
-
else
|
|
19964
|
-
fail(
|
|
19965
|
-
"Gemini CLI \u2014 hooks file found but node9 hook missing",
|
|
19966
|
-
"Run: node9 setup gemini"
|
|
19967
|
-
);
|
|
19968
|
-
} catch {
|
|
19969
|
-
fail("Gemini CLI \u2014 ~/.gemini/settings.json is invalid JSON");
|
|
20157
|
+
const notConfigured = [];
|
|
20158
|
+
for (const a of getAgentWiring(homeDir2)) {
|
|
20159
|
+
if (a.wireState === "wired") {
|
|
20160
|
+
pass(`${a.label} \u2014 ${a.hookLabel} active`);
|
|
20161
|
+
} else if (a.wireState === "unwired") {
|
|
20162
|
+
fail(`${a.label} \u2014 settings found but node9 hook missing`, `Run: ${a.setupCommand}`);
|
|
20163
|
+
} else if (a.wireState === "invalid") {
|
|
20164
|
+
fail(`${a.label} \u2014 settings file is invalid JSON`, a.settingsPath);
|
|
20165
|
+
} else {
|
|
20166
|
+
notConfigured.push(a.label);
|
|
19970
20167
|
}
|
|
19971
|
-
} else {
|
|
19972
|
-
warn("Gemini CLI \u2014 not configured", "Run: node9 setup gemini (skip if not using Gemini)");
|
|
19973
20168
|
}
|
|
19974
|
-
|
|
19975
|
-
|
|
19976
|
-
|
|
19977
|
-
|
|
19978
|
-
|
|
19979
|
-
|
|
19980
|
-
);
|
|
19981
|
-
if (hasHook) pass("Cursor \u2014 preToolUse hook active");
|
|
19982
|
-
else
|
|
19983
|
-
fail(
|
|
19984
|
-
"Cursor \u2014 hooks file found but node9 hook missing",
|
|
19985
|
-
"Run: node9 setup cursor"
|
|
19986
|
-
);
|
|
19987
|
-
} catch {
|
|
19988
|
-
fail("Cursor \u2014 ~/.cursor/hooks.json is invalid JSON");
|
|
19989
|
-
}
|
|
19990
|
-
} else {
|
|
19991
|
-
warn("Cursor \u2014 not configured", "Run: node9 setup cursor (skip if not using Cursor)");
|
|
20169
|
+
if (notConfigured.length > 0) {
|
|
20170
|
+
console.log(
|
|
20171
|
+
chalk11.gray(
|
|
20172
|
+
` \xB7 Not configured: ${notConfigured.join(", ")} \u2014 run \`node9 setup <agent>\` if you use one`
|
|
20173
|
+
)
|
|
20174
|
+
);
|
|
19992
20175
|
}
|
|
19993
20176
|
section("Daemon (optional)");
|
|
19994
20177
|
if (isDaemonRunning()) {
|
|
@@ -20005,7 +20188,7 @@ function registerDoctorCommand(program2, version2) {
|
|
|
20005
20188
|
try {
|
|
20006
20189
|
const { shipLagBytes: shipLagBytes2, readWatermark: readWatermark2, AUDIT_SHIP_WATERMARK: AUDIT_SHIP_WATERMARK2 } = await Promise.resolve().then(() => (init_audit_shipper(), audit_shipper_exports));
|
|
20007
20190
|
const cfg = getConfig();
|
|
20008
|
-
const creds =
|
|
20191
|
+
const creds = fs39.existsSync(path40.join(os35.homedir(), ".node9", "credentials.json"));
|
|
20009
20192
|
if (!creds) {
|
|
20010
20193
|
warn("Not logged in \u2014 audit rows stay local", "Run: node9 login <api-key>");
|
|
20011
20194
|
} else if (!cfg.settings.approvers.cloud) {
|
|
@@ -20055,9 +20238,9 @@ function registerDoctorCommand(program2, version2) {
|
|
|
20055
20238
|
|
|
20056
20239
|
// src/cli/commands/audit.ts
|
|
20057
20240
|
import chalk12 from "chalk";
|
|
20058
|
-
import
|
|
20059
|
-
import
|
|
20060
|
-
import
|
|
20241
|
+
import fs40 from "fs";
|
|
20242
|
+
import path41 from "path";
|
|
20243
|
+
import os36 from "os";
|
|
20061
20244
|
function formatRelativeTime(timestamp) {
|
|
20062
20245
|
const diff = Date.now() - new Date(timestamp).getTime();
|
|
20063
20246
|
const sec = Math.floor(diff / 1e3);
|
|
@@ -20070,14 +20253,14 @@ function formatRelativeTime(timestamp) {
|
|
|
20070
20253
|
}
|
|
20071
20254
|
function registerAuditCommand(program2) {
|
|
20072
20255
|
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) => {
|
|
20073
|
-
const logPath =
|
|
20074
|
-
if (!
|
|
20256
|
+
const logPath = path41.join(os36.homedir(), ".node9", "audit.log");
|
|
20257
|
+
if (!fs40.existsSync(logPath)) {
|
|
20075
20258
|
console.log(
|
|
20076
20259
|
chalk12.yellow("No audit logs found. Run node9 with an agent to generate entries.")
|
|
20077
20260
|
);
|
|
20078
20261
|
return;
|
|
20079
20262
|
}
|
|
20080
|
-
const raw =
|
|
20263
|
+
const raw = fs40.readFileSync(logPath, "utf-8");
|
|
20081
20264
|
const lines = raw.split("\n").filter((l) => l.trim() !== "");
|
|
20082
20265
|
let entries = lines.flatMap((line) => {
|
|
20083
20266
|
try {
|
|
@@ -20136,9 +20319,9 @@ import chalk13 from "chalk";
|
|
|
20136
20319
|
init_costSync();
|
|
20137
20320
|
init_litellm();
|
|
20138
20321
|
init_cost_codex();
|
|
20139
|
-
import
|
|
20140
|
-
import
|
|
20141
|
-
import
|
|
20322
|
+
import fs41 from "fs";
|
|
20323
|
+
import os37 from "os";
|
|
20324
|
+
import path42 from "path";
|
|
20142
20325
|
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;
|
|
20143
20326
|
function buildTestTimestamps(allEntries) {
|
|
20144
20327
|
const testTs = /* @__PURE__ */ new Set();
|
|
@@ -20218,8 +20401,8 @@ function getDateRange(period, now) {
|
|
|
20218
20401
|
}
|
|
20219
20402
|
}
|
|
20220
20403
|
function parseAuditLog(logPath) {
|
|
20221
|
-
if (!
|
|
20222
|
-
const raw =
|
|
20404
|
+
if (!fs41.existsSync(logPath)) return [];
|
|
20405
|
+
const raw = fs41.readFileSync(logPath, "utf-8");
|
|
20223
20406
|
return raw.split("\n").flatMap((line) => {
|
|
20224
20407
|
if (!line.trim()) return [];
|
|
20225
20408
|
try {
|
|
@@ -20266,25 +20449,25 @@ function freezeClaudeCost(acc) {
|
|
|
20266
20449
|
};
|
|
20267
20450
|
}
|
|
20268
20451
|
function processClaudeCostProject(proj, projectsDir, start, end, acc) {
|
|
20269
|
-
const projPath =
|
|
20452
|
+
const projPath = path42.join(projectsDir, proj);
|
|
20270
20453
|
let files;
|
|
20271
20454
|
try {
|
|
20272
|
-
const stat =
|
|
20455
|
+
const stat = fs41.statSync(projPath);
|
|
20273
20456
|
if (!stat.isDirectory()) return;
|
|
20274
|
-
files =
|
|
20457
|
+
files = fs41.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
20275
20458
|
} catch {
|
|
20276
20459
|
return;
|
|
20277
20460
|
}
|
|
20278
20461
|
const startMs = start.getTime();
|
|
20279
20462
|
for (const file of files) {
|
|
20280
|
-
const filePath =
|
|
20463
|
+
const filePath = path42.join(projPath, file);
|
|
20281
20464
|
try {
|
|
20282
|
-
if (
|
|
20465
|
+
if (fs41.statSync(filePath).mtimeMs < startMs) continue;
|
|
20283
20466
|
} catch {
|
|
20284
20467
|
continue;
|
|
20285
20468
|
}
|
|
20286
20469
|
try {
|
|
20287
|
-
const raw =
|
|
20470
|
+
const raw = fs41.readFileSync(filePath, "utf-8");
|
|
20288
20471
|
for (const line of raw.split("\n")) {
|
|
20289
20472
|
if (!line.trim()) continue;
|
|
20290
20473
|
let entry;
|
|
@@ -20334,10 +20517,10 @@ function processClaudeCostProject(proj, projectsDir, start, end, acc) {
|
|
|
20334
20517
|
}
|
|
20335
20518
|
function loadClaudeCost(start, end, projectsDir) {
|
|
20336
20519
|
const acc = emptyClaudeCostAccumulator();
|
|
20337
|
-
if (!
|
|
20520
|
+
if (!fs41.existsSync(projectsDir)) return freezeClaudeCost(acc);
|
|
20338
20521
|
let dirs;
|
|
20339
20522
|
try {
|
|
20340
|
-
dirs =
|
|
20523
|
+
dirs = fs41.readdirSync(projectsDir);
|
|
20341
20524
|
} catch {
|
|
20342
20525
|
return freezeClaudeCost(acc);
|
|
20343
20526
|
}
|
|
@@ -20349,7 +20532,7 @@ function loadClaudeCost(start, end, projectsDir) {
|
|
|
20349
20532
|
function processCodexCostFile(filePath, start, end, acc) {
|
|
20350
20533
|
let lines;
|
|
20351
20534
|
try {
|
|
20352
|
-
lines =
|
|
20535
|
+
lines = fs41.readFileSync(filePath, "utf-8").split("\n");
|
|
20353
20536
|
} catch {
|
|
20354
20537
|
return;
|
|
20355
20538
|
}
|
|
@@ -20404,31 +20587,31 @@ function processCodexCostFile(filePath, start, end, acc) {
|
|
|
20404
20587
|
}
|
|
20405
20588
|
function listCodexSessionFiles2(sessionsBase) {
|
|
20406
20589
|
const jsonlFiles = [];
|
|
20407
|
-
if (!
|
|
20590
|
+
if (!fs41.existsSync(sessionsBase)) return jsonlFiles;
|
|
20408
20591
|
try {
|
|
20409
|
-
for (const year of
|
|
20410
|
-
const yearPath =
|
|
20592
|
+
for (const year of fs41.readdirSync(sessionsBase)) {
|
|
20593
|
+
const yearPath = path42.join(sessionsBase, year);
|
|
20411
20594
|
try {
|
|
20412
|
-
if (!
|
|
20595
|
+
if (!fs41.statSync(yearPath).isDirectory()) continue;
|
|
20413
20596
|
} catch {
|
|
20414
20597
|
continue;
|
|
20415
20598
|
}
|
|
20416
|
-
for (const month of
|
|
20417
|
-
const monthPath =
|
|
20599
|
+
for (const month of fs41.readdirSync(yearPath)) {
|
|
20600
|
+
const monthPath = path42.join(yearPath, month);
|
|
20418
20601
|
try {
|
|
20419
|
-
if (!
|
|
20602
|
+
if (!fs41.statSync(monthPath).isDirectory()) continue;
|
|
20420
20603
|
} catch {
|
|
20421
20604
|
continue;
|
|
20422
20605
|
}
|
|
20423
|
-
for (const day of
|
|
20424
|
-
const dayPath =
|
|
20606
|
+
for (const day of fs41.readdirSync(monthPath)) {
|
|
20607
|
+
const dayPath = path42.join(monthPath, day);
|
|
20425
20608
|
try {
|
|
20426
|
-
if (!
|
|
20609
|
+
if (!fs41.statSync(dayPath).isDirectory()) continue;
|
|
20427
20610
|
} catch {
|
|
20428
20611
|
continue;
|
|
20429
20612
|
}
|
|
20430
|
-
for (const file of
|
|
20431
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(
|
|
20613
|
+
for (const file of fs41.readdirSync(dayPath)) {
|
|
20614
|
+
if (file.endsWith(".jsonl")) jsonlFiles.push(path42.join(dayPath, file));
|
|
20432
20615
|
}
|
|
20433
20616
|
}
|
|
20434
20617
|
}
|
|
@@ -20493,13 +20676,13 @@ function freezeGeminiCost(acc) {
|
|
|
20493
20676
|
function processGeminiCostFile(filePath, projectKey, start, end, acc) {
|
|
20494
20677
|
const startMs = start.getTime();
|
|
20495
20678
|
try {
|
|
20496
|
-
if (
|
|
20679
|
+
if (fs41.statSync(filePath).mtimeMs < startMs) return;
|
|
20497
20680
|
} catch {
|
|
20498
20681
|
return;
|
|
20499
20682
|
}
|
|
20500
20683
|
let raw;
|
|
20501
20684
|
try {
|
|
20502
|
-
raw =
|
|
20685
|
+
raw = fs41.readFileSync(filePath, "utf-8");
|
|
20503
20686
|
} catch {
|
|
20504
20687
|
return;
|
|
20505
20688
|
}
|
|
@@ -20548,30 +20731,30 @@ function listGeminiSessionFiles2(geminiTmpDir2) {
|
|
|
20548
20731
|
const out = [];
|
|
20549
20732
|
let dirs;
|
|
20550
20733
|
try {
|
|
20551
|
-
if (!
|
|
20552
|
-
dirs =
|
|
20734
|
+
if (!fs41.statSync(geminiTmpDir2).isDirectory()) return out;
|
|
20735
|
+
dirs = fs41.readdirSync(geminiTmpDir2);
|
|
20553
20736
|
} catch {
|
|
20554
20737
|
return out;
|
|
20555
20738
|
}
|
|
20556
20739
|
for (const proj of dirs) {
|
|
20557
|
-
const chatsDir =
|
|
20740
|
+
const chatsDir = path42.join(geminiTmpDir2, proj, "chats");
|
|
20558
20741
|
let files;
|
|
20559
20742
|
try {
|
|
20560
|
-
if (!
|
|
20561
|
-
files =
|
|
20743
|
+
if (!fs41.statSync(chatsDir).isDirectory()) continue;
|
|
20744
|
+
files = fs41.readdirSync(chatsDir);
|
|
20562
20745
|
} catch {
|
|
20563
20746
|
continue;
|
|
20564
20747
|
}
|
|
20565
20748
|
for (const f of files) {
|
|
20566
20749
|
if (!f.endsWith(".jsonl")) continue;
|
|
20567
|
-
out.push({ projectKey: proj, file:
|
|
20750
|
+
out.push({ projectKey: proj, file: path42.join(chatsDir, f) });
|
|
20568
20751
|
}
|
|
20569
20752
|
}
|
|
20570
20753
|
return out;
|
|
20571
20754
|
}
|
|
20572
20755
|
function loadGeminiCost(start, end, geminiTmpDir2) {
|
|
20573
20756
|
const acc = emptyGeminiAccumulator();
|
|
20574
|
-
if (!
|
|
20757
|
+
if (!fs41.existsSync(geminiTmpDir2)) return freezeGeminiCost(acc);
|
|
20575
20758
|
for (const { projectKey, file } of listGeminiSessionFiles2(geminiTmpDir2)) {
|
|
20576
20759
|
processGeminiCostFile(file, projectKey, start, end, acc);
|
|
20577
20760
|
}
|
|
@@ -20579,11 +20762,11 @@ function loadGeminiCost(start, end, geminiTmpDir2) {
|
|
|
20579
20762
|
}
|
|
20580
20763
|
function aggregateReportFromAudit(period, opts = {}) {
|
|
20581
20764
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
20582
|
-
const auditLogPath = opts.auditLogPath ??
|
|
20583
|
-
const claudeProjectsDir = opts.claudeProjectsDir ??
|
|
20584
|
-
const codexSessionsDir2 = opts.codexSessionsDir ??
|
|
20585
|
-
const geminiTmpDir2 = opts.geminiTmpDir ??
|
|
20586
|
-
const hasAuditFile =
|
|
20765
|
+
const auditLogPath = opts.auditLogPath ?? path42.join(os37.homedir(), ".node9", "audit.log");
|
|
20766
|
+
const claudeProjectsDir = opts.claudeProjectsDir ?? path42.join(os37.homedir(), ".claude", "projects");
|
|
20767
|
+
const codexSessionsDir2 = opts.codexSessionsDir ?? path42.join(os37.homedir(), ".codex", "sessions");
|
|
20768
|
+
const geminiTmpDir2 = opts.geminiTmpDir ?? path42.join(os37.homedir(), ".gemini", "tmp");
|
|
20769
|
+
const hasAuditFile = fs41.existsSync(auditLogPath);
|
|
20587
20770
|
const allEntries = opts.preloadedAuditEntries ?? parseAuditLog(auditLogPath);
|
|
20588
20771
|
const unackedDlp = allEntries.filter((e) => e.source === "response-dlp");
|
|
20589
20772
|
const { start, end } = getDateRange(period, now);
|
|
@@ -21282,20 +21465,20 @@ init_core();
|
|
|
21282
21465
|
init_daemon();
|
|
21283
21466
|
init_setup();
|
|
21284
21467
|
import chalk15 from "chalk";
|
|
21285
|
-
import
|
|
21286
|
-
import
|
|
21287
|
-
import
|
|
21288
|
-
import * as
|
|
21468
|
+
import fs42 from "fs";
|
|
21469
|
+
import path43 from "path";
|
|
21470
|
+
import os38 from "os";
|
|
21471
|
+
import * as yaml3 from "yaml";
|
|
21289
21472
|
function readHermesHooks(configPath) {
|
|
21290
|
-
if (!
|
|
21473
|
+
if (!fs42.existsSync(configPath)) return null;
|
|
21291
21474
|
let raw;
|
|
21292
21475
|
try {
|
|
21293
|
-
raw =
|
|
21476
|
+
raw = fs42.readFileSync(configPath, "utf-8");
|
|
21294
21477
|
} catch {
|
|
21295
21478
|
return null;
|
|
21296
21479
|
}
|
|
21297
21480
|
try {
|
|
21298
|
-
const cfg =
|
|
21481
|
+
const cfg = yaml3.parse(raw);
|
|
21299
21482
|
const has = (event) => (cfg?.hooks?.[event] ?? []).some(
|
|
21300
21483
|
(e) => typeof e?.command === "string" && isNode9Hook(e.command)
|
|
21301
21484
|
);
|
|
@@ -21309,17 +21492,17 @@ function readHermesHooks(configPath) {
|
|
|
21309
21492
|
return { pre: false, post: false };
|
|
21310
21493
|
}
|
|
21311
21494
|
}
|
|
21312
|
-
function
|
|
21495
|
+
function readJson3(filePath) {
|
|
21313
21496
|
try {
|
|
21314
|
-
if (
|
|
21497
|
+
if (fs42.existsSync(filePath)) return JSON.parse(fs42.readFileSync(filePath, "utf-8"));
|
|
21315
21498
|
} catch {
|
|
21316
21499
|
}
|
|
21317
21500
|
return null;
|
|
21318
21501
|
}
|
|
21319
|
-
function
|
|
21502
|
+
function matchersHaveNode9Hook2(matchers) {
|
|
21320
21503
|
return (matchers ?? []).some((m) => (m.hooks ?? []).some((h) => isNode9Hook(h.command)));
|
|
21321
21504
|
}
|
|
21322
|
-
function
|
|
21505
|
+
function flatHaveNode9Hook2(entries) {
|
|
21323
21506
|
return (Array.isArray(entries) ? entries : []).some((h) => isNode9Hook(h.command));
|
|
21324
21507
|
}
|
|
21325
21508
|
function wrappedMcpServers(servers) {
|
|
@@ -21379,42 +21562,42 @@ function registerStatusCommand(program2) {
|
|
|
21379
21562
|
console.log("");
|
|
21380
21563
|
const modeLabel = settings.mode === "audit" ? chalk15.blue("audit") : settings.mode === "strict" ? chalk15.red("strict") : chalk15.white("standard");
|
|
21381
21564
|
console.log(` Mode: ${modeLabel}`);
|
|
21382
|
-
const projectConfig =
|
|
21383
|
-
const globalConfig =
|
|
21565
|
+
const projectConfig = path43.join(process.cwd(), "node9.config.json");
|
|
21566
|
+
const globalConfig = path43.join(os38.homedir(), ".node9", "config.json");
|
|
21384
21567
|
console.log(
|
|
21385
|
-
` Local: ${
|
|
21568
|
+
` Local: ${fs42.existsSync(projectConfig) ? chalk15.green("Active (node9.config.json)") : chalk15.gray("Not present")}`
|
|
21386
21569
|
);
|
|
21387
21570
|
console.log(
|
|
21388
|
-
` Global: ${
|
|
21571
|
+
` Global: ${fs42.existsSync(globalConfig) ? chalk15.green("Active (~/.node9/config.json)") : chalk15.gray("Not present")}`
|
|
21389
21572
|
);
|
|
21390
21573
|
if (mergedConfig.policy.sandboxPaths.length > 0) {
|
|
21391
21574
|
console.log(
|
|
21392
21575
|
` Sandbox: ${chalk15.green(`${mergedConfig.policy.sandboxPaths.length} safe zones active`)}`
|
|
21393
21576
|
);
|
|
21394
21577
|
}
|
|
21395
|
-
const homeDir2 =
|
|
21396
|
-
const claudeSettings =
|
|
21397
|
-
|
|
21578
|
+
const homeDir2 = os38.homedir();
|
|
21579
|
+
const claudeSettings = readJson3(
|
|
21580
|
+
path43.join(homeDir2, ".claude", "settings.json")
|
|
21398
21581
|
);
|
|
21399
|
-
const claudeConfig =
|
|
21400
|
-
const geminiSettings =
|
|
21401
|
-
|
|
21582
|
+
const claudeConfig = readJson3(path43.join(homeDir2, ".claude.json"));
|
|
21583
|
+
const geminiSettings = readJson3(
|
|
21584
|
+
path43.join(homeDir2, ".gemini", "settings.json")
|
|
21402
21585
|
);
|
|
21403
|
-
const cursorConfig =
|
|
21404
|
-
const antigravityHooks =
|
|
21405
|
-
|
|
21586
|
+
const cursorConfig = readJson3(path43.join(homeDir2, ".cursor", "mcp.json"));
|
|
21587
|
+
const antigravityHooks = readJson3(
|
|
21588
|
+
path43.join(homeDir2, ".gemini", "config", "hooks.json")
|
|
21406
21589
|
);
|
|
21407
|
-
const antigravityMcp =
|
|
21408
|
-
|
|
21590
|
+
const antigravityMcp = readJson3(
|
|
21591
|
+
path43.join(homeDir2, ".gemini", "config", "mcp_config.json")
|
|
21409
21592
|
);
|
|
21410
|
-
const antigravityPresent = antigravityHooks !== null ||
|
|
21411
|
-
const copilotHooks =
|
|
21412
|
-
|
|
21593
|
+
const antigravityPresent = antigravityHooks !== null || fs42.existsSync(path43.join(homeDir2, ".gemini", "antigravity-cli")) || fs42.existsSync(path43.join(homeDir2, ".gemini", "antigravity-ide"));
|
|
21594
|
+
const copilotHooks = readJson3(
|
|
21595
|
+
path43.join(homeDir2, ".copilot", "hooks", "node9.json")
|
|
21413
21596
|
);
|
|
21414
|
-
const copilotMcp =
|
|
21415
|
-
|
|
21597
|
+
const copilotMcp = readJson3(
|
|
21598
|
+
path43.join(homeDir2, ".copilot", "mcp-config.json")
|
|
21416
21599
|
);
|
|
21417
|
-
const copilotPresent =
|
|
21600
|
+
const copilotPresent = fs42.existsSync(path43.join(homeDir2, ".copilot"));
|
|
21418
21601
|
const hermesHooks = readHermesHooks(hermesConfigPath(homeDir2));
|
|
21419
21602
|
const agentFound = claudeSettings || claudeConfig || geminiSettings || cursorConfig || antigravityPresent || copilotPresent || hermesHooks;
|
|
21420
21603
|
if (agentFound) {
|
|
@@ -21422,8 +21605,8 @@ function registerStatusCommand(program2) {
|
|
|
21422
21605
|
console.log(chalk15.bold(" Agent Wiring:"));
|
|
21423
21606
|
console.log("");
|
|
21424
21607
|
if (claudeSettings || claudeConfig) {
|
|
21425
|
-
const preHook =
|
|
21426
|
-
const postHook =
|
|
21608
|
+
const preHook = matchersHaveNode9Hook2(claudeSettings?.hooks?.PreToolUse);
|
|
21609
|
+
const postHook = matchersHaveNode9Hook2(claudeSettings?.hooks?.PostToolUse);
|
|
21427
21610
|
printAgentSection(
|
|
21428
21611
|
"Claude Code",
|
|
21429
21612
|
[
|
|
@@ -21435,8 +21618,8 @@ function registerStatusCommand(program2) {
|
|
|
21435
21618
|
console.log("");
|
|
21436
21619
|
}
|
|
21437
21620
|
if (geminiSettings) {
|
|
21438
|
-
const beforeHook =
|
|
21439
|
-
const afterHook =
|
|
21621
|
+
const beforeHook = matchersHaveNode9Hook2(geminiSettings.hooks?.BeforeTool);
|
|
21622
|
+
const afterHook = matchersHaveNode9Hook2(geminiSettings.hooks?.AfterTool);
|
|
21440
21623
|
printAgentSection(
|
|
21441
21624
|
"Gemini CLI",
|
|
21442
21625
|
[
|
|
@@ -21448,8 +21631,8 @@ function registerStatusCommand(program2) {
|
|
|
21448
21631
|
console.log("");
|
|
21449
21632
|
}
|
|
21450
21633
|
if (antigravityPresent) {
|
|
21451
|
-
const preHook =
|
|
21452
|
-
const postHook =
|
|
21634
|
+
const preHook = matchersHaveNode9Hook2(antigravityHooks?.hooks?.PreToolUse);
|
|
21635
|
+
const postHook = matchersHaveNode9Hook2(antigravityHooks?.hooks?.PostToolUse);
|
|
21453
21636
|
printAgentSection(
|
|
21454
21637
|
"Antigravity",
|
|
21455
21638
|
[
|
|
@@ -21461,9 +21644,9 @@ function registerStatusCommand(program2) {
|
|
|
21461
21644
|
console.log("");
|
|
21462
21645
|
}
|
|
21463
21646
|
if (copilotPresent) {
|
|
21464
|
-
const preHook =
|
|
21465
|
-
const postHook =
|
|
21466
|
-
const promptHook =
|
|
21647
|
+
const preHook = flatHaveNode9Hook2(copilotHooks?.hooks?.PreToolUse);
|
|
21648
|
+
const postHook = flatHaveNode9Hook2(copilotHooks?.hooks?.PostToolUse);
|
|
21649
|
+
const promptHook = flatHaveNode9Hook2(copilotHooks?.hooks?.UserPromptSubmit);
|
|
21467
21650
|
printAgentSection(
|
|
21468
21651
|
"GitHub Copilot",
|
|
21469
21652
|
[
|
|
@@ -21510,9 +21693,9 @@ init_setup();
|
|
|
21510
21693
|
init_shields();
|
|
21511
21694
|
init_service();
|
|
21512
21695
|
import chalk16 from "chalk";
|
|
21513
|
-
import
|
|
21514
|
-
import
|
|
21515
|
-
import
|
|
21696
|
+
import fs43 from "fs";
|
|
21697
|
+
import path44 from "path";
|
|
21698
|
+
import os39 from "os";
|
|
21516
21699
|
import https4 from "https";
|
|
21517
21700
|
var DEFAULT_SHIELDS = ["bash-safe", "filesystem", "project-jail"];
|
|
21518
21701
|
function buildTelemetryPayload(agents, firstInstall) {
|
|
@@ -21598,16 +21781,16 @@ function registerInitCommand(program2) {
|
|
|
21598
21781
|
}
|
|
21599
21782
|
console.log("");
|
|
21600
21783
|
}
|
|
21601
|
-
const configPath =
|
|
21602
|
-
const isFirstInstall = !
|
|
21603
|
-
if (
|
|
21784
|
+
const configPath = path44.join(os39.homedir(), ".node9", "config.json");
|
|
21785
|
+
const isFirstInstall = !fs43.existsSync(configPath);
|
|
21786
|
+
if (fs43.existsSync(configPath) && !options.force) {
|
|
21604
21787
|
try {
|
|
21605
|
-
const existing = JSON.parse(
|
|
21788
|
+
const existing = JSON.parse(fs43.readFileSync(configPath, "utf-8"));
|
|
21606
21789
|
const settings = existing.settings ?? {};
|
|
21607
21790
|
if (settings.mode !== chosenMode) {
|
|
21608
21791
|
settings.mode = chosenMode;
|
|
21609
21792
|
existing.settings = settings;
|
|
21610
|
-
|
|
21793
|
+
fs43.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
|
|
21611
21794
|
console.log(chalk16.green(`\u2705 Mode updated: ${chosenMode}`));
|
|
21612
21795
|
} else {
|
|
21613
21796
|
console.log(chalk16.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
|
|
@@ -21620,9 +21803,9 @@ function registerInitCommand(program2) {
|
|
|
21620
21803
|
...DEFAULT_CONFIG,
|
|
21621
21804
|
settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
|
|
21622
21805
|
};
|
|
21623
|
-
const dir =
|
|
21624
|
-
if (!
|
|
21625
|
-
|
|
21806
|
+
const dir = path44.dirname(configPath);
|
|
21807
|
+
if (!fs43.existsSync(dir)) fs43.mkdirSync(dir, { recursive: true });
|
|
21808
|
+
fs43.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
|
|
21626
21809
|
console.log(chalk16.green(`\u2705 Config created: ${configPath}`));
|
|
21627
21810
|
console.log(chalk16.gray(` Mode: ${chosenMode}`));
|
|
21628
21811
|
}
|
|
@@ -21727,7 +21910,7 @@ function registerInitCommand(program2) {
|
|
|
21727
21910
|
}
|
|
21728
21911
|
|
|
21729
21912
|
// src/cli/commands/undo.ts
|
|
21730
|
-
import
|
|
21913
|
+
import path45 from "path";
|
|
21731
21914
|
import chalk18 from "chalk";
|
|
21732
21915
|
|
|
21733
21916
|
// src/tui/undo-navigator.ts
|
|
@@ -21886,7 +22069,7 @@ function findMatchingCwd(startDir, history) {
|
|
|
21886
22069
|
let dir = startDir;
|
|
21887
22070
|
while (true) {
|
|
21888
22071
|
if (cwds.has(dir)) return dir;
|
|
21889
|
-
const parent =
|
|
22072
|
+
const parent = path45.dirname(dir);
|
|
21890
22073
|
if (parent === dir) return null;
|
|
21891
22074
|
dir = parent;
|
|
21892
22075
|
}
|
|
@@ -22014,6 +22197,8 @@ function registerUndoCommand(program2) {
|
|
|
22014
22197
|
|
|
22015
22198
|
// src/mcp-gateway/index.ts
|
|
22016
22199
|
init_orchestrator();
|
|
22200
|
+
init_cloud();
|
|
22201
|
+
init_config();
|
|
22017
22202
|
import readline4 from "readline";
|
|
22018
22203
|
import chalk19 from "chalk";
|
|
22019
22204
|
import { spawn as spawn7 } from "child_process";
|
|
@@ -22046,6 +22231,60 @@ function normalizeClientName(name) {
|
|
|
22046
22231
|
const sanitized = sanitize4(name).slice(0, 40);
|
|
22047
22232
|
return sanitized.length > 0 ? sanitized : void 0;
|
|
22048
22233
|
}
|
|
22234
|
+
function reportPinMismatchToCloud(serverKey, agent) {
|
|
22235
|
+
try {
|
|
22236
|
+
const creds = getCredentials();
|
|
22237
|
+
if (!creds) return;
|
|
22238
|
+
void auditLocalAllow(
|
|
22239
|
+
`mcp-server:${serverKey}`,
|
|
22240
|
+
{ serverKey, reason: "tool-pin-mismatch" },
|
|
22241
|
+
"mcp-pin-mismatch",
|
|
22242
|
+
creds,
|
|
22243
|
+
{ mcpServer: serverKey, agent },
|
|
22244
|
+
void 0,
|
|
22245
|
+
false,
|
|
22246
|
+
{
|
|
22247
|
+
ruleName: "MCP tool definitions changed (possible rug pull)",
|
|
22248
|
+
ruleDescription: `The MCP server "${serverKey}" changed its tool definitions since they were pinned. This can indicate a supply-chain attack (tool poisoning). The session was quarantined. Review with: node9 mcp pin update ${serverKey}`
|
|
22249
|
+
}
|
|
22250
|
+
);
|
|
22251
|
+
} catch {
|
|
22252
|
+
}
|
|
22253
|
+
}
|
|
22254
|
+
function reportInventoryToCloud(serverKey, toolCount, agent) {
|
|
22255
|
+
try {
|
|
22256
|
+
const creds = getCredentials();
|
|
22257
|
+
if (!creds) return;
|
|
22258
|
+
void auditLocalAllow(
|
|
22259
|
+
`mcp-server:${serverKey}`,
|
|
22260
|
+
{ serverKey, toolCount },
|
|
22261
|
+
"mcp-discovered",
|
|
22262
|
+
creds,
|
|
22263
|
+
{ mcpServer: serverKey, agent },
|
|
22264
|
+
void 0,
|
|
22265
|
+
false,
|
|
22266
|
+
{ mcpToolCount: toolCount }
|
|
22267
|
+
);
|
|
22268
|
+
} catch {
|
|
22269
|
+
}
|
|
22270
|
+
}
|
|
22271
|
+
function reportLargeResponseToCloud(serverKey, responseBytes, agent) {
|
|
22272
|
+
try {
|
|
22273
|
+
const creds = getCredentials();
|
|
22274
|
+
if (!creds) return;
|
|
22275
|
+
void auditLocalAllow(
|
|
22276
|
+
`mcp-server:${serverKey}`,
|
|
22277
|
+
{ serverKey, responseBytes },
|
|
22278
|
+
"mcp-large-response",
|
|
22279
|
+
creds,
|
|
22280
|
+
{ mcpServer: serverKey, agent },
|
|
22281
|
+
void 0,
|
|
22282
|
+
false,
|
|
22283
|
+
{ mcpResponseBytes: responseBytes }
|
|
22284
|
+
);
|
|
22285
|
+
} catch {
|
|
22286
|
+
}
|
|
22287
|
+
}
|
|
22049
22288
|
function tokenize4(cmd) {
|
|
22050
22289
|
const tokens = [];
|
|
22051
22290
|
let current = "";
|
|
@@ -22306,6 +22545,7 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
22306
22545
|
const currentHash = hashToolDefinitions(tools);
|
|
22307
22546
|
const pinStatus = checkPin(serverKey, currentHash, gatewayCwd);
|
|
22308
22547
|
const token = getInternalToken();
|
|
22548
|
+
reportInventoryToCloud(serverKey, tools.length, clientName);
|
|
22309
22549
|
if (isDaemonRunning() && process.env.NODE9_TESTING !== "1") {
|
|
22310
22550
|
const toolSummary = tools.map((t) => ({ name: t.name, description: t.description }));
|
|
22311
22551
|
fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/mcp/discovered`, {
|
|
@@ -22370,6 +22610,7 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
22370
22610
|
console.error(chalk19.red(" Session quarantined \u2014 all tool calls blocked."));
|
|
22371
22611
|
console.error(chalk19.yellow(` Run: node9 mcp pin update ${serverKey}
|
|
22372
22612
|
`));
|
|
22613
|
+
reportPinMismatchToCloud(serverKey, clientName);
|
|
22373
22614
|
const errorResponse = {
|
|
22374
22615
|
jsonrpc: "2.0",
|
|
22375
22616
|
id: parsed.id,
|
|
@@ -22415,6 +22656,7 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
22415
22656
|
`\u26A1 Node9: Large MCP response from '${toolName}' (${(line.length / 1024).toFixed(0)}KB) \u2014 context window enlarged`
|
|
22416
22657
|
)
|
|
22417
22658
|
);
|
|
22659
|
+
reportLargeResponseToCloud(serverKey, line.length, clientName);
|
|
22418
22660
|
if (isDaemonRunning() && process.env.NODE9_TESTING !== "1") {
|
|
22419
22661
|
const token = getInternalToken();
|
|
22420
22662
|
fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/mcp/large-response`, {
|
|
@@ -22462,9 +22704,9 @@ function registerMcpGatewayCommand(program2) {
|
|
|
22462
22704
|
|
|
22463
22705
|
// src/mcp-server/index.ts
|
|
22464
22706
|
import readline5 from "readline";
|
|
22465
|
-
import
|
|
22466
|
-
import
|
|
22467
|
-
import
|
|
22707
|
+
import fs44 from "fs";
|
|
22708
|
+
import os40 from "os";
|
|
22709
|
+
import path46 from "path";
|
|
22468
22710
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
22469
22711
|
init_core();
|
|
22470
22712
|
init_daemon();
|
|
@@ -22715,13 +22957,13 @@ function handleStatus() {
|
|
|
22715
22957
|
lines.push(`Active shields: ${activeShields.length > 0 ? activeShields.join(", ") : "none"}`);
|
|
22716
22958
|
lines.push(`Smart rules: ${config.policy.smartRules.length} loaded`);
|
|
22717
22959
|
lines.push(`DLP: ${config.policy.dlp?.enabled !== false ? "enabled" : "disabled"}`);
|
|
22718
|
-
const projectConfig =
|
|
22719
|
-
const globalConfig =
|
|
22960
|
+
const projectConfig = path46.join(process.cwd(), "node9.config.json");
|
|
22961
|
+
const globalConfig = path46.join(os40.homedir(), ".node9", "config.json");
|
|
22720
22962
|
lines.push(
|
|
22721
|
-
`Project config (node9.config.json): ${
|
|
22963
|
+
`Project config (node9.config.json): ${fs44.existsSync(projectConfig) ? "present" : "not found"}`
|
|
22722
22964
|
);
|
|
22723
22965
|
lines.push(
|
|
22724
|
-
`Global config (~/.node9/config.json): ${
|
|
22966
|
+
`Global config (~/.node9/config.json): ${fs44.existsSync(globalConfig) ? "present" : "not found"}`
|
|
22725
22967
|
);
|
|
22726
22968
|
return lines.join("\n");
|
|
22727
22969
|
}
|
|
@@ -22795,21 +23037,21 @@ function handleShieldDisable(args) {
|
|
|
22795
23037
|
writeActiveShields(active.filter((s) => s !== name));
|
|
22796
23038
|
return `Shield "${name}" disabled.`;
|
|
22797
23039
|
}
|
|
22798
|
-
var GLOBAL_CONFIG_PATH =
|
|
23040
|
+
var GLOBAL_CONFIG_PATH = path46.join(os40.homedir(), ".node9", "config.json");
|
|
22799
23041
|
var APPROVER_CHANNELS = ["native", "browser", "cloud", "terminal"];
|
|
22800
23042
|
function readGlobalConfigRaw() {
|
|
22801
23043
|
try {
|
|
22802
|
-
if (
|
|
22803
|
-
return JSON.parse(
|
|
23044
|
+
if (fs44.existsSync(GLOBAL_CONFIG_PATH)) {
|
|
23045
|
+
return JSON.parse(fs44.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
|
|
22804
23046
|
}
|
|
22805
23047
|
} catch {
|
|
22806
23048
|
}
|
|
22807
23049
|
return {};
|
|
22808
23050
|
}
|
|
22809
23051
|
function writeGlobalConfigRaw(data) {
|
|
22810
|
-
const dir =
|
|
22811
|
-
if (!
|
|
22812
|
-
|
|
23052
|
+
const dir = path46.dirname(GLOBAL_CONFIG_PATH);
|
|
23053
|
+
if (!fs44.existsSync(dir)) fs44.mkdirSync(dir, { recursive: true });
|
|
23054
|
+
fs44.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(data, null, 2) + "\n");
|
|
22813
23055
|
}
|
|
22814
23056
|
function handleApproverList() {
|
|
22815
23057
|
const config = getConfig();
|
|
@@ -22853,9 +23095,9 @@ function handleApproverSet(args) {
|
|
|
22853
23095
|
function handleAuditGet(args) {
|
|
22854
23096
|
const limit = Math.min(typeof args.limit === "number" ? args.limit : 20, 100);
|
|
22855
23097
|
const filter = typeof args.filter === "string" && args.filter !== "all" ? args.filter : null;
|
|
22856
|
-
const auditPath =
|
|
22857
|
-
if (!
|
|
22858
|
-
const rawLines =
|
|
23098
|
+
const auditPath = path46.join(os40.homedir(), ".node9", "audit.log");
|
|
23099
|
+
if (!fs44.existsSync(auditPath)) return "No audit log found.";
|
|
23100
|
+
const rawLines = fs44.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
22859
23101
|
const parsed = [];
|
|
22860
23102
|
for (const line of rawLines) {
|
|
22861
23103
|
try {
|
|
@@ -23190,7 +23432,7 @@ function registerTrustCommand(program2) {
|
|
|
23190
23432
|
// src/cli/commands/mcp-pin.ts
|
|
23191
23433
|
init_mcp_pin();
|
|
23192
23434
|
import chalk21 from "chalk";
|
|
23193
|
-
import
|
|
23435
|
+
import fs45 from "fs";
|
|
23194
23436
|
function registerMcpPinCommand(program2) {
|
|
23195
23437
|
const pinCmd = program2.command("mcp").description("Manage MCP server tool definition pinning (rug pull defense)");
|
|
23196
23438
|
const pinSubCmd = pinCmd.command("pin").description("Manage pinned MCP server tool definitions");
|
|
@@ -23201,7 +23443,7 @@ function registerMcpPinCommand(program2) {
|
|
|
23201
23443
|
let repoCorrupt = false;
|
|
23202
23444
|
if (found.source === "repo") {
|
|
23203
23445
|
try {
|
|
23204
|
-
const raw =
|
|
23446
|
+
const raw = fs45.readFileSync(found.path, "utf-8");
|
|
23205
23447
|
const parsed = JSON.parse(raw);
|
|
23206
23448
|
repoEntries = parsed.servers ?? {};
|
|
23207
23449
|
} catch {
|
|
@@ -23518,9 +23760,9 @@ init_litellm();
|
|
|
23518
23760
|
init_cost_gemini();
|
|
23519
23761
|
init_cost_codex();
|
|
23520
23762
|
import chalk24 from "chalk";
|
|
23521
|
-
import
|
|
23522
|
-
import
|
|
23523
|
-
import
|
|
23763
|
+
import fs46 from "fs";
|
|
23764
|
+
import path47 from "path";
|
|
23765
|
+
import os41 from "os";
|
|
23524
23766
|
function modelPrice(model) {
|
|
23525
23767
|
const t = pricingFor(model);
|
|
23526
23768
|
if (!t) return null;
|
|
@@ -23537,10 +23779,10 @@ function encodeProjectPath(projectPath) {
|
|
|
23537
23779
|
}
|
|
23538
23780
|
function sessionJsonlPath(projectPath, sessionId) {
|
|
23539
23781
|
const encoded = encodeProjectPath(projectPath);
|
|
23540
|
-
return
|
|
23782
|
+
return path47.join(os41.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
|
|
23541
23783
|
}
|
|
23542
23784
|
function projectLabel(projectPath) {
|
|
23543
|
-
return projectPath.replace(
|
|
23785
|
+
return projectPath.replace(os41.homedir(), "~");
|
|
23544
23786
|
}
|
|
23545
23787
|
function parseHistoryLines(lines) {
|
|
23546
23788
|
const entries = [];
|
|
@@ -23609,10 +23851,10 @@ function parseSessionLines(lines) {
|
|
|
23609
23851
|
return { toolCalls, costUSD, hasSnapshot, modifiedFiles };
|
|
23610
23852
|
}
|
|
23611
23853
|
function loadAuditEntries(auditPath) {
|
|
23612
|
-
const aPath = auditPath ??
|
|
23854
|
+
const aPath = auditPath ?? path47.join(os41.homedir(), ".node9", "audit.log");
|
|
23613
23855
|
let raw;
|
|
23614
23856
|
try {
|
|
23615
|
-
raw =
|
|
23857
|
+
raw = fs46.readFileSync(aPath, "utf-8");
|
|
23616
23858
|
} catch {
|
|
23617
23859
|
return [];
|
|
23618
23860
|
}
|
|
@@ -23648,8 +23890,8 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
|
|
|
23648
23890
|
return result;
|
|
23649
23891
|
}
|
|
23650
23892
|
function buildGeminiSessions(days, allAuditEntries) {
|
|
23651
|
-
const tmpDir =
|
|
23652
|
-
if (!
|
|
23893
|
+
const tmpDir = path47.join(os41.homedir(), ".gemini", "tmp");
|
|
23894
|
+
if (!fs46.existsSync(tmpDir)) return [];
|
|
23653
23895
|
const cutoff = days !== null ? (() => {
|
|
23654
23896
|
const d = /* @__PURE__ */ new Date();
|
|
23655
23897
|
d.setDate(d.getDate() - days);
|
|
@@ -23658,35 +23900,35 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
23658
23900
|
})() : null;
|
|
23659
23901
|
let slugDirs;
|
|
23660
23902
|
try {
|
|
23661
|
-
slugDirs =
|
|
23903
|
+
slugDirs = fs46.readdirSync(tmpDir);
|
|
23662
23904
|
} catch {
|
|
23663
23905
|
return [];
|
|
23664
23906
|
}
|
|
23665
23907
|
const summaries = [];
|
|
23666
23908
|
for (const slug of slugDirs) {
|
|
23667
|
-
const slugPath =
|
|
23909
|
+
const slugPath = path47.join(tmpDir, slug);
|
|
23668
23910
|
try {
|
|
23669
|
-
if (!
|
|
23911
|
+
if (!fs46.statSync(slugPath).isDirectory()) continue;
|
|
23670
23912
|
} catch {
|
|
23671
23913
|
continue;
|
|
23672
23914
|
}
|
|
23673
|
-
let projectRoot =
|
|
23915
|
+
let projectRoot = path47.join(os41.homedir(), slug);
|
|
23674
23916
|
try {
|
|
23675
|
-
projectRoot =
|
|
23917
|
+
projectRoot = fs46.readFileSync(path47.join(slugPath, ".project_root"), "utf-8").trim();
|
|
23676
23918
|
} catch {
|
|
23677
23919
|
}
|
|
23678
|
-
const chatsDir =
|
|
23679
|
-
if (!
|
|
23920
|
+
const chatsDir = path47.join(slugPath, "chats");
|
|
23921
|
+
if (!fs46.existsSync(chatsDir)) continue;
|
|
23680
23922
|
let chatFiles;
|
|
23681
23923
|
try {
|
|
23682
|
-
chatFiles =
|
|
23924
|
+
chatFiles = fs46.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
|
|
23683
23925
|
} catch {
|
|
23684
23926
|
continue;
|
|
23685
23927
|
}
|
|
23686
23928
|
for (const chatFile of chatFiles) {
|
|
23687
23929
|
let raw;
|
|
23688
23930
|
try {
|
|
23689
|
-
raw =
|
|
23931
|
+
raw = fs46.readFileSync(path47.join(chatsDir, chatFile), "utf-8");
|
|
23690
23932
|
} catch {
|
|
23691
23933
|
continue;
|
|
23692
23934
|
}
|
|
@@ -23766,8 +24008,8 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
23766
24008
|
return summaries;
|
|
23767
24009
|
}
|
|
23768
24010
|
function buildCodexSessions(days, allAuditEntries) {
|
|
23769
|
-
const sessionsBase =
|
|
23770
|
-
if (!
|
|
24011
|
+
const sessionsBase = path47.join(os41.homedir(), ".codex", "sessions");
|
|
24012
|
+
if (!fs46.existsSync(sessionsBase)) return [];
|
|
23771
24013
|
const cutoff = days !== null ? (() => {
|
|
23772
24014
|
const d = /* @__PURE__ */ new Date();
|
|
23773
24015
|
d.setDate(d.getDate() - days);
|
|
@@ -23776,29 +24018,29 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
23776
24018
|
})() : null;
|
|
23777
24019
|
const jsonlFiles = [];
|
|
23778
24020
|
try {
|
|
23779
|
-
for (const year of
|
|
23780
|
-
const yearPath =
|
|
24021
|
+
for (const year of fs46.readdirSync(sessionsBase)) {
|
|
24022
|
+
const yearPath = path47.join(sessionsBase, year);
|
|
23781
24023
|
try {
|
|
23782
|
-
if (!
|
|
24024
|
+
if (!fs46.statSync(yearPath).isDirectory()) continue;
|
|
23783
24025
|
} catch {
|
|
23784
24026
|
continue;
|
|
23785
24027
|
}
|
|
23786
|
-
for (const month of
|
|
23787
|
-
const monthPath =
|
|
24028
|
+
for (const month of fs46.readdirSync(yearPath)) {
|
|
24029
|
+
const monthPath = path47.join(yearPath, month);
|
|
23788
24030
|
try {
|
|
23789
|
-
if (!
|
|
24031
|
+
if (!fs46.statSync(monthPath).isDirectory()) continue;
|
|
23790
24032
|
} catch {
|
|
23791
24033
|
continue;
|
|
23792
24034
|
}
|
|
23793
|
-
for (const day of
|
|
23794
|
-
const dayPath =
|
|
24035
|
+
for (const day of fs46.readdirSync(monthPath)) {
|
|
24036
|
+
const dayPath = path47.join(monthPath, day);
|
|
23795
24037
|
try {
|
|
23796
|
-
if (!
|
|
24038
|
+
if (!fs46.statSync(dayPath).isDirectory()) continue;
|
|
23797
24039
|
} catch {
|
|
23798
24040
|
continue;
|
|
23799
24041
|
}
|
|
23800
|
-
for (const file of
|
|
23801
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(
|
|
24042
|
+
for (const file of fs46.readdirSync(dayPath)) {
|
|
24043
|
+
if (file.endsWith(".jsonl")) jsonlFiles.push(path47.join(dayPath, file));
|
|
23802
24044
|
}
|
|
23803
24045
|
}
|
|
23804
24046
|
}
|
|
@@ -23810,7 +24052,7 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
23810
24052
|
for (const filePath of jsonlFiles) {
|
|
23811
24053
|
let lines;
|
|
23812
24054
|
try {
|
|
23813
|
-
lines =
|
|
24055
|
+
lines = fs46.readFileSync(filePath, "utf-8").split("\n");
|
|
23814
24056
|
} catch {
|
|
23815
24057
|
continue;
|
|
23816
24058
|
}
|
|
@@ -23896,10 +24138,10 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
23896
24138
|
return summaries;
|
|
23897
24139
|
}
|
|
23898
24140
|
function buildSessions(days, historyPath) {
|
|
23899
|
-
const hPath = historyPath ??
|
|
24141
|
+
const hPath = historyPath ?? path47.join(os41.homedir(), ".claude", "history.jsonl");
|
|
23900
24142
|
let historyRaw = "";
|
|
23901
24143
|
try {
|
|
23902
|
-
historyRaw =
|
|
24144
|
+
historyRaw = fs46.readFileSync(hPath, "utf-8");
|
|
23903
24145
|
} catch {
|
|
23904
24146
|
}
|
|
23905
24147
|
const cutoff = days !== null ? (() => {
|
|
@@ -23923,7 +24165,7 @@ function buildSessions(days, historyPath) {
|
|
|
23923
24165
|
const jsonlFile = sessionJsonlPath(entry.project, entry.sessionId);
|
|
23924
24166
|
let sessionLines = [];
|
|
23925
24167
|
try {
|
|
23926
|
-
sessionLines =
|
|
24168
|
+
sessionLines = fs46.readFileSync(jsonlFile, "utf-8").split("\n");
|
|
23927
24169
|
} catch {
|
|
23928
24170
|
}
|
|
23929
24171
|
const { toolCalls, costUSD, hasSnapshot, modifiedFiles } = parseSessionLines(sessionLines);
|
|
@@ -24223,12 +24465,12 @@ function registerSessionsCommand(program2) {
|
|
|
24223
24465
|
|
|
24224
24466
|
// src/cli/commands/skill-pin.ts
|
|
24225
24467
|
import chalk25 from "chalk";
|
|
24226
|
-
import
|
|
24227
|
-
import
|
|
24228
|
-
import
|
|
24468
|
+
import fs47 from "fs";
|
|
24469
|
+
import os42 from "os";
|
|
24470
|
+
import path48 from "path";
|
|
24229
24471
|
function wipeSkillSessions() {
|
|
24230
24472
|
try {
|
|
24231
|
-
|
|
24473
|
+
fs47.rmSync(path48.join(os42.homedir(), ".node9", "skill-sessions"), {
|
|
24232
24474
|
recursive: true,
|
|
24233
24475
|
force: true
|
|
24234
24476
|
});
|
|
@@ -24310,15 +24552,15 @@ function registerSkillPinCommand(program2) {
|
|
|
24310
24552
|
}
|
|
24311
24553
|
|
|
24312
24554
|
// src/cli/commands/decisions.ts
|
|
24313
|
-
import
|
|
24314
|
-
import
|
|
24315
|
-
import
|
|
24555
|
+
import fs48 from "fs";
|
|
24556
|
+
import os43 from "os";
|
|
24557
|
+
import path49 from "path";
|
|
24316
24558
|
import chalk26 from "chalk";
|
|
24317
|
-
var DECISIONS_FILE2 =
|
|
24559
|
+
var DECISIONS_FILE2 = path49.join(os43.homedir(), ".node9", "decisions.json");
|
|
24318
24560
|
function readDecisions() {
|
|
24319
24561
|
try {
|
|
24320
|
-
if (!
|
|
24321
|
-
const raw =
|
|
24562
|
+
if (!fs48.existsSync(DECISIONS_FILE2)) return {};
|
|
24563
|
+
const raw = fs48.readFileSync(DECISIONS_FILE2, "utf-8");
|
|
24322
24564
|
const parsed = JSON.parse(raw);
|
|
24323
24565
|
const out = {};
|
|
24324
24566
|
for (const [k, v] of Object.entries(parsed)) {
|
|
@@ -24330,11 +24572,11 @@ function readDecisions() {
|
|
|
24330
24572
|
}
|
|
24331
24573
|
}
|
|
24332
24574
|
function writeDecisions(d) {
|
|
24333
|
-
const dir =
|
|
24334
|
-
if (!
|
|
24575
|
+
const dir = path49.dirname(DECISIONS_FILE2);
|
|
24576
|
+
if (!fs48.existsSync(dir)) fs48.mkdirSync(dir, { recursive: true });
|
|
24335
24577
|
const tmp = `${DECISIONS_FILE2}.${process.pid}.tmp`;
|
|
24336
|
-
|
|
24337
|
-
|
|
24578
|
+
fs48.writeFileSync(tmp, JSON.stringify(d, null, 2));
|
|
24579
|
+
fs48.renameSync(tmp, DECISIONS_FILE2);
|
|
24338
24580
|
}
|
|
24339
24581
|
function registerDecisionsCommand(program2) {
|
|
24340
24582
|
const cmd = program2.command("decisions").description('Manage persistent "Always Allow" / "Always Deny" tool decisions');
|
|
@@ -24391,18 +24633,18 @@ Persistent decisions (${entries.length})
|
|
|
24391
24633
|
|
|
24392
24634
|
// src/cli/commands/dlp.ts
|
|
24393
24635
|
import chalk27 from "chalk";
|
|
24394
|
-
import
|
|
24395
|
-
import
|
|
24396
|
-
import
|
|
24397
|
-
var AUDIT_LOG =
|
|
24398
|
-
var RESOLVED_FILE =
|
|
24636
|
+
import fs49 from "fs";
|
|
24637
|
+
import path50 from "path";
|
|
24638
|
+
import os44 from "os";
|
|
24639
|
+
var AUDIT_LOG = path50.join(os44.homedir(), ".node9", "audit.log");
|
|
24640
|
+
var RESOLVED_FILE = path50.join(os44.homedir(), ".node9", "dlp-resolved.json");
|
|
24399
24641
|
var ANSI_RE = /\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g;
|
|
24400
24642
|
function stripAnsi(s) {
|
|
24401
24643
|
return s.replace(ANSI_RE, "");
|
|
24402
24644
|
}
|
|
24403
24645
|
function loadResolved() {
|
|
24404
24646
|
try {
|
|
24405
|
-
const raw = JSON.parse(
|
|
24647
|
+
const raw = JSON.parse(fs49.readFileSync(RESOLVED_FILE, "utf-8"));
|
|
24406
24648
|
return new Set(raw);
|
|
24407
24649
|
} catch {
|
|
24408
24650
|
return /* @__PURE__ */ new Set();
|
|
@@ -24410,13 +24652,13 @@ function loadResolved() {
|
|
|
24410
24652
|
}
|
|
24411
24653
|
function saveResolved(resolved) {
|
|
24412
24654
|
try {
|
|
24413
|
-
|
|
24655
|
+
fs49.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
|
|
24414
24656
|
} catch {
|
|
24415
24657
|
}
|
|
24416
24658
|
}
|
|
24417
24659
|
function loadDlpFindings() {
|
|
24418
|
-
if (!
|
|
24419
|
-
return
|
|
24660
|
+
if (!fs49.existsSync(AUDIT_LOG)) return [];
|
|
24661
|
+
return fs49.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
|
|
24420
24662
|
if (!line.trim()) return [];
|
|
24421
24663
|
try {
|
|
24422
24664
|
const e = JSON.parse(line);
|
|
@@ -24515,14 +24757,14 @@ function registerDlpCommand(program2) {
|
|
|
24515
24757
|
// src/cli/commands/mask.ts
|
|
24516
24758
|
init_dlp();
|
|
24517
24759
|
import chalk28 from "chalk";
|
|
24518
|
-
import
|
|
24519
|
-
import
|
|
24520
|
-
import
|
|
24760
|
+
import fs50 from "fs";
|
|
24761
|
+
import path51 from "path";
|
|
24762
|
+
import os45 from "os";
|
|
24521
24763
|
function findJsonlFiles(dir) {
|
|
24522
24764
|
const results = [];
|
|
24523
|
-
if (!
|
|
24524
|
-
for (const entry of
|
|
24525
|
-
const full =
|
|
24765
|
+
if (!fs50.existsSync(dir)) return results;
|
|
24766
|
+
for (const entry of fs50.readdirSync(dir, { withFileTypes: true })) {
|
|
24767
|
+
const full = path51.join(dir, entry.name);
|
|
24526
24768
|
if (entry.isDirectory()) results.push(...findJsonlFiles(full));
|
|
24527
24769
|
else if (entry.isFile() && entry.name.endsWith(".jsonl")) results.push(full);
|
|
24528
24770
|
}
|
|
@@ -24565,7 +24807,7 @@ function redactJson(obj) {
|
|
|
24565
24807
|
function processFile(filePath, dryRun) {
|
|
24566
24808
|
let raw;
|
|
24567
24809
|
try {
|
|
24568
|
-
raw =
|
|
24810
|
+
raw = fs50.readFileSync(filePath, "utf-8");
|
|
24569
24811
|
} catch {
|
|
24570
24812
|
return { redactedLines: 0, patterns: [] };
|
|
24571
24813
|
}
|
|
@@ -24597,14 +24839,14 @@ function processFile(filePath, dryRun) {
|
|
|
24597
24839
|
}
|
|
24598
24840
|
}
|
|
24599
24841
|
if (!dryRun && redactedLines > 0) {
|
|
24600
|
-
|
|
24842
|
+
fs50.writeFileSync(filePath, newLines.join("\n"), "utf-8");
|
|
24601
24843
|
}
|
|
24602
24844
|
return { redactedLines, patterns };
|
|
24603
24845
|
}
|
|
24604
24846
|
function processJsonFile(filePath, dryRun) {
|
|
24605
24847
|
let raw;
|
|
24606
24848
|
try {
|
|
24607
|
-
raw =
|
|
24849
|
+
raw = fs50.readFileSync(filePath, "utf-8");
|
|
24608
24850
|
} catch {
|
|
24609
24851
|
return { redactedLines: 0, patterns: [] };
|
|
24610
24852
|
}
|
|
@@ -24617,15 +24859,15 @@ function processJsonFile(filePath, dryRun) {
|
|
|
24617
24859
|
const { value, modified, found } = redactJson(parsed);
|
|
24618
24860
|
if (!modified) return { redactedLines: 0, patterns: [] };
|
|
24619
24861
|
if (!dryRun) {
|
|
24620
|
-
|
|
24862
|
+
fs50.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
|
|
24621
24863
|
}
|
|
24622
24864
|
return { redactedLines: 1, patterns: found };
|
|
24623
24865
|
}
|
|
24624
24866
|
function findJsonFiles(dir) {
|
|
24625
24867
|
const results = [];
|
|
24626
|
-
if (!
|
|
24627
|
-
for (const entry of
|
|
24628
|
-
const full =
|
|
24868
|
+
if (!fs50.existsSync(dir)) return results;
|
|
24869
|
+
for (const entry of fs50.readdirSync(dir, { withFileTypes: true })) {
|
|
24870
|
+
const full = path51.join(dir, entry.name);
|
|
24629
24871
|
if (entry.isDirectory()) results.push(...findJsonFiles(full));
|
|
24630
24872
|
else if (entry.isFile() && entry.name.endsWith(".json")) results.push(full);
|
|
24631
24873
|
}
|
|
@@ -24634,9 +24876,9 @@ function findJsonFiles(dir) {
|
|
|
24634
24876
|
function registerMaskCommand(program2) {
|
|
24635
24877
|
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) => {
|
|
24636
24878
|
const dryRun = !!options.dryRun;
|
|
24637
|
-
const home =
|
|
24638
|
-
const claudeDir =
|
|
24639
|
-
const geminiDir =
|
|
24879
|
+
const home = os45.homedir();
|
|
24880
|
+
const claudeDir = path51.join(home, ".claude", "projects");
|
|
24881
|
+
const geminiDir = path51.join(home, ".gemini", "tmp");
|
|
24640
24882
|
const allFiles = [
|
|
24641
24883
|
...findJsonlFiles(claudeDir).map((p) => ({ path: p, type: "jsonl" })),
|
|
24642
24884
|
...findJsonFiles(geminiDir).map((p) => ({ path: p, type: "json" }))
|
|
@@ -24644,7 +24886,7 @@ function registerMaskCommand(program2) {
|
|
|
24644
24886
|
const cutoff = options.all ? null : new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
|
|
24645
24887
|
const filtered = cutoff ? allFiles.filter((f) => {
|
|
24646
24888
|
try {
|
|
24647
|
-
return
|
|
24889
|
+
return fs50.statSync(f.path).mtime >= cutoff;
|
|
24648
24890
|
} catch {
|
|
24649
24891
|
return false;
|
|
24650
24892
|
}
|
|
@@ -24700,20 +24942,20 @@ function registerMaskCommand(program2) {
|
|
|
24700
24942
|
// src/cli.ts
|
|
24701
24943
|
init_blast();
|
|
24702
24944
|
var { version } = JSON.parse(
|
|
24703
|
-
|
|
24945
|
+
fs53.readFileSync(path54.join(__dirname, "../package.json"), "utf-8")
|
|
24704
24946
|
);
|
|
24705
24947
|
var program = new Command();
|
|
24706
24948
|
program.name("node9").description("The Sudo Command for AI Agents").version(version);
|
|
24707
24949
|
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) => {
|
|
24708
24950
|
const DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept";
|
|
24709
|
-
const credPath =
|
|
24710
|
-
if (!
|
|
24711
|
-
|
|
24951
|
+
const credPath = path54.join(os48.homedir(), ".node9", "credentials.json");
|
|
24952
|
+
if (!fs53.existsSync(path54.dirname(credPath)))
|
|
24953
|
+
fs53.mkdirSync(path54.dirname(credPath), { recursive: true });
|
|
24712
24954
|
const profileName = options.profile || "default";
|
|
24713
24955
|
let existingCreds = {};
|
|
24714
24956
|
try {
|
|
24715
|
-
if (
|
|
24716
|
-
const raw = JSON.parse(
|
|
24957
|
+
if (fs53.existsSync(credPath)) {
|
|
24958
|
+
const raw = JSON.parse(fs53.readFileSync(credPath, "utf-8"));
|
|
24717
24959
|
if (raw.apiKey) {
|
|
24718
24960
|
existingCreds = {
|
|
24719
24961
|
default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL2 }
|
|
@@ -24725,14 +24967,14 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
24725
24967
|
} catch {
|
|
24726
24968
|
}
|
|
24727
24969
|
existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL2 };
|
|
24728
|
-
|
|
24970
|
+
fs53.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
|
|
24729
24971
|
let effectiveCloud = null;
|
|
24730
24972
|
if (profileName === "default") {
|
|
24731
|
-
const configPath =
|
|
24973
|
+
const configPath = path54.join(os48.homedir(), ".node9", "config.json");
|
|
24732
24974
|
let config = {};
|
|
24733
24975
|
try {
|
|
24734
|
-
if (
|
|
24735
|
-
config = JSON.parse(
|
|
24976
|
+
if (fs53.existsSync(configPath))
|
|
24977
|
+
config = JSON.parse(fs53.readFileSync(configPath, "utf-8"));
|
|
24736
24978
|
} catch {
|
|
24737
24979
|
}
|
|
24738
24980
|
if (!config.settings || typeof config.settings !== "object") config.settings = {};
|
|
@@ -24747,9 +24989,9 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
24747
24989
|
approvers.cloud = false;
|
|
24748
24990
|
}
|
|
24749
24991
|
s.approvers = approvers;
|
|
24750
|
-
if (!
|
|
24751
|
-
|
|
24752
|
-
|
|
24992
|
+
if (!fs53.existsSync(path54.dirname(configPath)))
|
|
24993
|
+
fs53.mkdirSync(path54.dirname(configPath), { recursive: true });
|
|
24994
|
+
fs53.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
24753
24995
|
effectiveCloud = approvers.cloud === true;
|
|
24754
24996
|
}
|
|
24755
24997
|
if (options.profile && profileName !== "default") {
|
|
@@ -24908,15 +25150,15 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
24908
25150
|
}
|
|
24909
25151
|
}
|
|
24910
25152
|
if (options.purge) {
|
|
24911
|
-
const node9Dir =
|
|
24912
|
-
if (
|
|
25153
|
+
const node9Dir = path54.join(os48.homedir(), ".node9");
|
|
25154
|
+
if (fs53.existsSync(node9Dir)) {
|
|
24913
25155
|
const confirmed = await confirm2({
|
|
24914
25156
|
message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
|
|
24915
25157
|
default: false
|
|
24916
25158
|
});
|
|
24917
25159
|
if (confirmed) {
|
|
24918
|
-
|
|
24919
|
-
if (
|
|
25160
|
+
fs53.rmSync(node9Dir, { recursive: true });
|
|
25161
|
+
if (fs53.existsSync(node9Dir)) {
|
|
24920
25162
|
console.error(
|
|
24921
25163
|
chalk30.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
|
|
24922
25164
|
);
|
|
@@ -25031,7 +25273,7 @@ program.command("tail").description("Stream live agent activity to the terminal"
|
|
|
25031
25273
|
});
|
|
25032
25274
|
program.command("monitor").description("Live interactive dashboard \u2014 activity feed, approvals, security signals").action(async () => {
|
|
25033
25275
|
try {
|
|
25034
|
-
const dashboardPath =
|
|
25276
|
+
const dashboardPath = path54.join(__dirname, "dashboard.mjs");
|
|
25035
25277
|
const dynamicImport = new Function("id", "return import(id)");
|
|
25036
25278
|
const mod = await dynamicImport(`file://${dashboardPath}`);
|
|
25037
25279
|
await mod.startMonitor();
|
|
@@ -25069,14 +25311,14 @@ Claude Code spawns this command every ~300ms and writes a JSON payload to stdin.
|
|
|
25069
25311
|
Run "node9 addto claude" to register it as the statusLine.`
|
|
25070
25312
|
).argument("[subcommand]", 'Optional: "debug on" / "debug off" to toggle stdin logging').argument("[state]", 'on|off \u2014 used with "debug" subcommand').action(async (subcommand, state) => {
|
|
25071
25313
|
if (subcommand === "debug") {
|
|
25072
|
-
const flagFile =
|
|
25314
|
+
const flagFile = path54.join(os48.homedir(), ".node9", "hud-debug");
|
|
25073
25315
|
if (state === "on") {
|
|
25074
|
-
|
|
25075
|
-
|
|
25316
|
+
fs53.mkdirSync(path54.dirname(flagFile), { recursive: true });
|
|
25317
|
+
fs53.writeFileSync(flagFile, "");
|
|
25076
25318
|
console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
|
|
25077
25319
|
console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
|
|
25078
25320
|
} else if (state === "off") {
|
|
25079
|
-
if (
|
|
25321
|
+
if (fs53.existsSync(flagFile)) fs53.unlinkSync(flagFile);
|
|
25080
25322
|
console.log("HUD debug logging disabled.");
|
|
25081
25323
|
} else {
|
|
25082
25324
|
console.error("Usage: node9 hud debug on|off");
|
|
@@ -25193,9 +25435,9 @@ if (process.argv[2] !== "daemon") {
|
|
|
25193
25435
|
const isCheckHook = process.argv[2] === "check";
|
|
25194
25436
|
if (isCheckHook) {
|
|
25195
25437
|
if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
|
|
25196
|
-
const logPath =
|
|
25438
|
+
const logPath = path54.join(os48.homedir(), ".node9", "hook-debug.log");
|
|
25197
25439
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
25198
|
-
|
|
25440
|
+
fs53.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
|
|
25199
25441
|
`);
|
|
25200
25442
|
}
|
|
25201
25443
|
process.exit(0);
|