@node9/proxy 1.29.0 → 1.30.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 +845 -650
- package/dist/cli.mjs +817 -623
- package/dist/dashboard.mjs +9 -2
- package/dist/index.js +64 -149
- package/dist/index.mjs +61 -146
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -52,11 +52,17 @@ __export(audit_exports, {
|
|
|
52
52
|
appendHookDebug: () => appendHookDebug,
|
|
53
53
|
appendLocalAudit: () => appendLocalAudit,
|
|
54
54
|
appendToLog: () => appendToLog,
|
|
55
|
+
buildArgsPreview: () => buildArgsPreview,
|
|
56
|
+
generateEventId: () => generateEventId,
|
|
55
57
|
redactSecrets: () => redactSecrets
|
|
56
58
|
});
|
|
57
59
|
import fs from "fs";
|
|
58
60
|
import path from "path";
|
|
59
61
|
import os from "os";
|
|
62
|
+
import crypto from "crypto";
|
|
63
|
+
function generateEventId() {
|
|
64
|
+
return `${Date.now().toString(36)}-${crypto.randomBytes(6).toString("hex")}`;
|
|
65
|
+
}
|
|
60
66
|
function isTestCall(toolName, args) {
|
|
61
67
|
if (toolName !== "Bash" && toolName !== "bash") return false;
|
|
62
68
|
const cmd = args?.command;
|
|
@@ -75,6 +81,17 @@ function redactSecrets(text) {
|
|
|
75
81
|
);
|
|
76
82
|
return redacted;
|
|
77
83
|
}
|
|
84
|
+
function buildArgsPreview(args) {
|
|
85
|
+
try {
|
|
86
|
+
const o = args && typeof args === "object" ? args : null;
|
|
87
|
+
const primary = o && (o.command ?? o.file_path ?? o.path ?? o.url ?? o.query);
|
|
88
|
+
const text = typeof primary === "string" ? primary : args ? JSON.stringify(args) : "";
|
|
89
|
+
if (!text) return void 0;
|
|
90
|
+
return redactSecrets(text).slice(0, 120);
|
|
91
|
+
} catch {
|
|
92
|
+
return void 0;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
78
95
|
function appendToLog(logPath, entry) {
|
|
79
96
|
try {
|
|
80
97
|
const dir = path.dirname(logPath);
|
|
@@ -97,11 +114,18 @@ function appendHookDebug(toolName, args, meta, auditHashArgsEnabled) {
|
|
|
97
114
|
});
|
|
98
115
|
}
|
|
99
116
|
function appendLocalAudit(toolName, args, decision, checkedBy, meta, auditHashArgsEnabled) {
|
|
100
|
-
const
|
|
117
|
+
const isDlpRow = checkedBy.toLowerCase().includes("dlp") || Boolean(meta?.dlpPattern);
|
|
118
|
+
const preview2 = auditHashArgsEnabled && !isDlpRow ? buildArgsPreview(args) : void 0;
|
|
119
|
+
const argsField = auditHashArgsEnabled ? { argsHash: hashArgs(args), ...preview2 ? { argsPreview: preview2 } : {} } : { args: args ? JSON.parse(redactSecrets(JSON.stringify(args))) : {} };
|
|
101
120
|
const testRun = isTestCall(toolName, args) || process.env.NODE9_TESTING === "1" ? { testRun: true } : {};
|
|
102
121
|
const ruleNameField = meta?.ruleName ? { ruleName: meta.ruleName } : {};
|
|
103
122
|
const agentToolNameField = meta?.agentToolName ? { agentToolName: meta.agentToolName } : {};
|
|
123
|
+
const dlpFields = meta?.dlpPattern ? { dlpPattern: meta.dlpPattern, dlpSample: meta.dlpSample } : {};
|
|
124
|
+
const cloudLinkField = meta?.cloudRequestId ? { cloudRequestId: meta.cloudRequestId } : {};
|
|
104
125
|
appendToLog(LOCAL_AUDIT_LOG, {
|
|
126
|
+
// eid first: the outbox shipper dedups on it, and a fixed leading field
|
|
127
|
+
// makes the JSONL easy to eyeball.
|
|
128
|
+
eid: generateEventId(),
|
|
105
129
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
106
130
|
tool: toolName,
|
|
107
131
|
...agentToolNameField,
|
|
@@ -109,6 +133,8 @@ function appendLocalAudit(toolName, args, decision, checkedBy, meta, auditHashAr
|
|
|
109
133
|
decision,
|
|
110
134
|
checkedBy,
|
|
111
135
|
...ruleNameField,
|
|
136
|
+
...dlpFields,
|
|
137
|
+
...cloudLinkField,
|
|
112
138
|
...testRun,
|
|
113
139
|
agent: meta?.agent,
|
|
114
140
|
mcpServer: meta?.mcpServer,
|
|
@@ -153,8 +179,8 @@ function sanitizeConfig(raw) {
|
|
|
153
179
|
}
|
|
154
180
|
}
|
|
155
181
|
const lines = result.error.issues.map((issue) => {
|
|
156
|
-
const
|
|
157
|
-
return ` \u2022 ${
|
|
182
|
+
const path51 = issue.path.length > 0 ? issue.path.join(".") : "root";
|
|
183
|
+
return ` \u2022 ${path51}: ${issue.message}`;
|
|
158
184
|
});
|
|
159
185
|
return {
|
|
160
186
|
sanitized,
|
|
@@ -239,7 +265,13 @@ var init_config_schema = __esm({
|
|
|
239
265
|
allowGlobalPause: z.boolean().optional(),
|
|
240
266
|
auditHashArgs: z.boolean().optional(),
|
|
241
267
|
agentPolicy: z.enum(["require_approval", "block_on_rules"]).optional(),
|
|
242
|
-
cloudSyncIntervalHours: z.number().positive().optional()
|
|
268
|
+
cloudSyncIntervalHours: z.number().positive().optional(),
|
|
269
|
+
// Outbox shipper (audit.log → SaaS batch ingest). enabled defaults
|
|
270
|
+
// to true; set false to fall back to local-only auditing.
|
|
271
|
+
shipper: z.object({
|
|
272
|
+
enabled: z.boolean().optional(),
|
|
273
|
+
intervalSeconds: z.number().min(5).optional()
|
|
274
|
+
}).optional()
|
|
243
275
|
}).optional(),
|
|
244
276
|
policy: z.object({
|
|
245
277
|
sandboxPaths: z.array(z.string()).optional(),
|
|
@@ -278,7 +310,7 @@ import mvdanSh from "mvdan-sh";
|
|
|
278
310
|
import pm from "picomatch";
|
|
279
311
|
import safeRegex2 from "safe-regex2";
|
|
280
312
|
import safeRegex3 from "safe-regex2";
|
|
281
|
-
import
|
|
313
|
+
import crypto2 from "crypto";
|
|
282
314
|
function isAssignmentContext(text) {
|
|
283
315
|
return ASSIGNMENT_CONTEXT_RE.test(text);
|
|
284
316
|
}
|
|
@@ -991,9 +1023,9 @@ function matchesPattern(text, patterns) {
|
|
|
991
1023
|
const withoutDotSlash = text.replace(/^\.\//, "");
|
|
992
1024
|
return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
|
|
993
1025
|
}
|
|
994
|
-
function getNestedValue(obj,
|
|
1026
|
+
function getNestedValue(obj, path51) {
|
|
995
1027
|
if (!obj || typeof obj !== "object") return null;
|
|
996
|
-
const segments =
|
|
1028
|
+
const segments = path51.split(".");
|
|
997
1029
|
for (const seg of segments) {
|
|
998
1030
|
if (FORBIDDEN_PATH_SEGMENTS.has(seg)) return null;
|
|
999
1031
|
}
|
|
@@ -1355,7 +1387,7 @@ function assertBuiltinShieldRegexesAreSafe() {
|
|
|
1355
1387
|
}
|
|
1356
1388
|
function computeArgsHash(args) {
|
|
1357
1389
|
const str = JSON.stringify(args ?? "");
|
|
1358
|
-
return
|
|
1390
|
+
return crypto2.createHash("sha256").update(str).digest("hex").slice(0, 16);
|
|
1359
1391
|
}
|
|
1360
1392
|
function evaluateLoopWindow(records, tool, args, threshold, windowMs, now) {
|
|
1361
1393
|
const hash = computeArgsHash(args);
|
|
@@ -3394,7 +3426,7 @@ var init_dist = __esm({
|
|
|
3394
3426
|
import fs2 from "fs";
|
|
3395
3427
|
import path2 from "path";
|
|
3396
3428
|
import os2 from "os";
|
|
3397
|
-
import
|
|
3429
|
+
import crypto3 from "crypto";
|
|
3398
3430
|
function loadUserShields() {
|
|
3399
3431
|
const result = {};
|
|
3400
3432
|
let entries;
|
|
@@ -3469,7 +3501,7 @@ function readShieldsFile() {
|
|
|
3469
3501
|
}
|
|
3470
3502
|
function writeShieldsFile(data) {
|
|
3471
3503
|
fs2.mkdirSync(path2.dirname(SHIELDS_STATE_FILE), { recursive: true });
|
|
3472
|
-
const tmp = `${SHIELDS_STATE_FILE}.${
|
|
3504
|
+
const tmp = `${SHIELDS_STATE_FILE}.${crypto3.randomBytes(6).toString("hex")}.tmp`;
|
|
3473
3505
|
const toWrite = { active: data.active };
|
|
3474
3506
|
if (data.overrides && Object.keys(data.overrides).length > 0) toWrite.overrides = data.overrides;
|
|
3475
3507
|
fs2.writeFileSync(tmp, JSON.stringify(toWrite, null, 2), { mode: 384 });
|
|
@@ -3559,7 +3591,7 @@ function installShield(name, shieldJson) {
|
|
|
3559
3591
|
}
|
|
3560
3592
|
fs2.mkdirSync(USER_SHIELDS_DIR, { recursive: true });
|
|
3561
3593
|
const filePath = path2.join(USER_SHIELDS_DIR, `${name}.json`);
|
|
3562
|
-
const tmp = `${filePath}.${
|
|
3594
|
+
const tmp = `${filePath}.${crypto3.randomBytes(6).toString("hex")}.tmp`;
|
|
3563
3595
|
fs2.writeFileSync(tmp, JSON.stringify(shieldJson, null, 2), { mode: 384 });
|
|
3564
3596
|
fs2.renameSync(tmp, filePath);
|
|
3565
3597
|
}
|
|
@@ -3657,7 +3689,8 @@ function getConfig(cwd) {
|
|
|
3657
3689
|
const projectConfig = tryLoadConfig(projectPath);
|
|
3658
3690
|
const mergedSettings = {
|
|
3659
3691
|
...DEFAULT_CONFIG.settings,
|
|
3660
|
-
approvers: { ...DEFAULT_CONFIG.settings.approvers }
|
|
3692
|
+
approvers: { ...DEFAULT_CONFIG.settings.approvers },
|
|
3693
|
+
shipper: { ...DEFAULT_CONFIG.settings.shipper }
|
|
3661
3694
|
};
|
|
3662
3695
|
const mergedPolicy = {
|
|
3663
3696
|
sandboxPaths: [...DEFAULT_CONFIG.policy.sandboxPaths],
|
|
@@ -3688,6 +3721,7 @@ function getConfig(cwd) {
|
|
|
3688
3721
|
if (s.enableHookLogDebug !== void 0)
|
|
3689
3722
|
mergedSettings.enableHookLogDebug = s.enableHookLogDebug;
|
|
3690
3723
|
if (s.approvers) mergedSettings.approvers = { ...mergedSettings.approvers, ...s.approvers };
|
|
3724
|
+
if (s.shipper) mergedSettings.shipper = { ...mergedSettings.shipper, ...s.shipper };
|
|
3691
3725
|
if (s.approvalTimeoutMs !== void 0) mergedSettings.approvalTimeoutMs = s.approvalTimeoutMs;
|
|
3692
3726
|
if (s.approvalTimeoutSeconds !== void 0 && s.approvalTimeoutMs === void 0)
|
|
3693
3727
|
mergedSettings.approvalTimeoutMs = s.approvalTimeoutSeconds * 1e3;
|
|
@@ -3885,7 +3919,8 @@ var init_config = __esm({
|
|
|
3885
3919
|
flightRecorder: true,
|
|
3886
3920
|
auditHashArgs: true,
|
|
3887
3921
|
approvers: { native: true, browser: false, cloud: false, terminal: true },
|
|
3888
|
-
cloudSyncIntervalHours: 5
|
|
3922
|
+
cloudSyncIntervalHours: 5,
|
|
3923
|
+
shipper: { enabled: true, intervalSeconds: 20 }
|
|
3889
3924
|
},
|
|
3890
3925
|
policy: {
|
|
3891
3926
|
sandboxPaths: ["/tmp/**", "**/sandbox/**", "**/test-results/**"],
|
|
@@ -5430,55 +5465,6 @@ function validateApiUrl(raw) {
|
|
|
5430
5465
|
}
|
|
5431
5466
|
return null;
|
|
5432
5467
|
}
|
|
5433
|
-
function auditLocalAllow(toolName, args, checkedBy, creds, meta, dlpInfo, containsSensitiveArgs = false, riskMetadata) {
|
|
5434
|
-
const validated = validateApiUrl(creds.apiUrl);
|
|
5435
|
-
if (!validated) {
|
|
5436
|
-
try {
|
|
5437
|
-
fs10.appendFileSync(
|
|
5438
|
-
HOOK_DEBUG_LOG,
|
|
5439
|
-
`[audit] refused to send: invalid apiUrl scheme/host (got "${String(creds.apiUrl).slice(0, 200)}")
|
|
5440
|
-
`
|
|
5441
|
-
);
|
|
5442
|
-
} catch {
|
|
5443
|
-
}
|
|
5444
|
-
return Promise.resolve();
|
|
5445
|
-
}
|
|
5446
|
-
const safeArgs = containsSensitiveArgs ? { tool: toolName, redacted: true } : args;
|
|
5447
|
-
const dlpSample = dlpInfo && typeof dlpInfo.redactedSample === "string" ? dlpInfo.redactedSample.slice(0, DLP_SAMPLE_MAX_LEN) : void 0;
|
|
5448
|
-
const dlpPattern = dlpInfo && typeof dlpInfo.pattern === "string" ? dlpInfo.pattern.slice(0, DLP_PATTERN_MAX_LEN) : void 0;
|
|
5449
|
-
const safeCheckedBy = KNOWN_CHECKED_BY.has(checkedBy) ? checkedBy : "unknown";
|
|
5450
|
-
const cleanedRiskMetadata = riskMetadata ? Object.fromEntries(
|
|
5451
|
-
Object.entries(riskMetadata).filter(([, v]) => typeof v === "string" && v.length > 0)
|
|
5452
|
-
) : void 0;
|
|
5453
|
-
const hasRiskMetadata = cleanedRiskMetadata && Object.keys(cleanedRiskMetadata).length > 0;
|
|
5454
|
-
return fetch(`${validated.toString().replace(/\/$/, "")}/audit`, {
|
|
5455
|
-
method: "POST",
|
|
5456
|
-
headers: { "Content-Type": "application/json", Authorization: `Bearer ${creds.apiKey}` },
|
|
5457
|
-
body: JSON.stringify({
|
|
5458
|
-
toolName,
|
|
5459
|
-
args: safeArgs,
|
|
5460
|
-
checkedBy: safeCheckedBy,
|
|
5461
|
-
...dlpInfo && { dlpPattern, dlpSample },
|
|
5462
|
-
...hasRiskMetadata && { riskMetadata: cleanedRiskMetadata },
|
|
5463
|
-
// session_id (Claude Code + Gemini CLI) groups all audit rows from one
|
|
5464
|
-
// agent run; transcript_path is the authoritative pointer to the
|
|
5465
|
-
// session log (survives Gemini resume drift). Both optional —
|
|
5466
|
-
// unsupported agents (MCP-mediated) leave them undefined.
|
|
5467
|
-
...meta?.sessionId && { runId: meta.sessionId },
|
|
5468
|
-
...meta?.transcriptPath && { transcriptPath: meta.transcriptPath },
|
|
5469
|
-
context: {
|
|
5470
|
-
agent: meta?.agent,
|
|
5471
|
-
mcpServer: meta?.mcpServer,
|
|
5472
|
-
hostname: os9.hostname(),
|
|
5473
|
-
cwd: process.cwd(),
|
|
5474
|
-
platform: os9.platform()
|
|
5475
|
-
}
|
|
5476
|
-
}),
|
|
5477
|
-
signal: AbortSignal.timeout(5e3)
|
|
5478
|
-
}).then(() => {
|
|
5479
|
-
}).catch(() => {
|
|
5480
|
-
});
|
|
5481
|
-
}
|
|
5482
5468
|
async function initNode9SaaS(toolName, args, creds, meta, riskMetadata, agentPolicy, forceReview) {
|
|
5483
5469
|
const controller = new AbortController();
|
|
5484
5470
|
const timeout = setTimeout(() => controller.abort(), 1e4);
|
|
@@ -5602,32 +5588,10 @@ async function resolveNode9SaaS(requestId, creds, approved, decidedBy) {
|
|
|
5602
5588
|
);
|
|
5603
5589
|
}
|
|
5604
5590
|
}
|
|
5605
|
-
var DLP_SAMPLE_MAX_LEN, DLP_PATTERN_MAX_LEN, KNOWN_CHECKED_BY;
|
|
5606
5591
|
var init_cloud = __esm({
|
|
5607
5592
|
"src/auth/cloud.ts"() {
|
|
5608
5593
|
"use strict";
|
|
5609
5594
|
init_audit();
|
|
5610
|
-
DLP_SAMPLE_MAX_LEN = 200;
|
|
5611
|
-
DLP_PATTERN_MAX_LEN = 100;
|
|
5612
|
-
KNOWN_CHECKED_BY = /* @__PURE__ */ new Set([
|
|
5613
|
-
"dlp-block",
|
|
5614
|
-
"observe-mode-dlp-would-block",
|
|
5615
|
-
"dlp-review-flagged",
|
|
5616
|
-
"loop-detected",
|
|
5617
|
-
"audit-mode",
|
|
5618
|
-
"local-policy",
|
|
5619
|
-
"smart-rule-block",
|
|
5620
|
-
// Smart-rule block was downgraded to review because the daemon was
|
|
5621
|
-
// running and we're not in CI. The block attempt is still recorded;
|
|
5622
|
-
// the user got a popup. Distinct from 'smart-rule-block' so the
|
|
5623
|
-
// dashboard can show "block rule overridden" separately from a hard
|
|
5624
|
-
// block that fired with no human in the loop.
|
|
5625
|
-
"smart-rule-block-override",
|
|
5626
|
-
"persistent",
|
|
5627
|
-
"trust",
|
|
5628
|
-
"observe-mode",
|
|
5629
|
-
"observe-mode-would-block"
|
|
5630
|
-
]);
|
|
5631
5595
|
}
|
|
5632
5596
|
});
|
|
5633
5597
|
|
|
@@ -5809,17 +5773,11 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
5809
5773
|
args,
|
|
5810
5774
|
"deny",
|
|
5811
5775
|
isObserveMode ? "observe-mode-dlp-would-block" : "dlp-block",
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
toolName,
|
|
5818
|
-
args,
|
|
5819
|
-
isObserveMode ? "observe-mode-dlp-would-block" : "dlp-block",
|
|
5820
|
-
creds,
|
|
5821
|
-
meta,
|
|
5822
|
-
{ pattern: dlpMatch.patternName, redactedSample: dlpMatch.redactedSample },
|
|
5776
|
+
{
|
|
5777
|
+
...meta,
|
|
5778
|
+
dlpPattern: dlpMatch.patternName,
|
|
5779
|
+
dlpSample: dlpMatch.redactedSample
|
|
5780
|
+
},
|
|
5823
5781
|
true
|
|
5824
5782
|
);
|
|
5825
5783
|
if (isWriteTool(toolName) && filePath) {
|
|
@@ -5875,9 +5833,6 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
5875
5833
|
const policyResult = await evaluatePolicy2(toolName, args, meta?.agent, options?.cwd);
|
|
5876
5834
|
if (policyResult.decision === "review") {
|
|
5877
5835
|
appendLocalAudit(toolName, args, "allow", "audit-mode", meta, hashAuditArgs);
|
|
5878
|
-
if (approvers.cloud && creds?.apiKey) {
|
|
5879
|
-
await auditLocalAllow(toolName, args, "audit-mode", creds, meta);
|
|
5880
|
-
}
|
|
5881
5836
|
}
|
|
5882
5837
|
}
|
|
5883
5838
|
return { approved: true, checkedBy: "audit" };
|
|
@@ -5890,8 +5845,6 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
5890
5845
|
const reason = `It looks like you've called "${toolName}" ${loopResult.count} times with identical arguments in the last ${ld.windowSeconds}s. Are you stuck? Step back and reconsider your approach \u2014 what are you actually trying to accomplish, and is there a different way to get there?`;
|
|
5891
5846
|
if (!isManual)
|
|
5892
5847
|
appendLocalAudit(toolName, args, "deny", "loop-detected", meta, hashAuditArgs);
|
|
5893
|
-
if (approvers.cloud && creds?.apiKey)
|
|
5894
|
-
auditLocalAllow(toolName, args, "loop-detected", creds, meta, void 0, true);
|
|
5895
5848
|
return {
|
|
5896
5849
|
approved: false,
|
|
5897
5850
|
reason,
|
|
@@ -5910,15 +5863,15 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
5910
5863
|
};
|
|
5911
5864
|
}
|
|
5912
5865
|
if (policyResult.decision === "allow") {
|
|
5913
|
-
if (
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5866
|
+
if (!isManual)
|
|
5867
|
+
appendLocalAudit(
|
|
5868
|
+
toolName,
|
|
5869
|
+
args,
|
|
5870
|
+
"allow",
|
|
5871
|
+
"local-policy",
|
|
5872
|
+
{ ...meta, ruleName: policyResult.ruleName },
|
|
5873
|
+
hashAuditArgs
|
|
5874
|
+
);
|
|
5922
5875
|
return { approved: true, checkedBy: "local-policy" };
|
|
5923
5876
|
}
|
|
5924
5877
|
if (policyResult.decision === "block") {
|
|
@@ -5951,23 +5904,6 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
5951
5904
|
{ ...meta, ruleName: policyResult.ruleName },
|
|
5952
5905
|
hashAuditArgs
|
|
5953
5906
|
);
|
|
5954
|
-
if (approvers.cloud && creds?.apiKey)
|
|
5955
|
-
auditLocalAllow(
|
|
5956
|
-
toolName,
|
|
5957
|
-
args,
|
|
5958
|
-
"smart-rule-block-override",
|
|
5959
|
-
creds,
|
|
5960
|
-
meta,
|
|
5961
|
-
void 0,
|
|
5962
|
-
false,
|
|
5963
|
-
{
|
|
5964
|
-
ruleName: policyResult.ruleName,
|
|
5965
|
-
ruleDescription: policyResult.ruleDescription,
|
|
5966
|
-
blockedByLabel: policyResult.blockedByLabel,
|
|
5967
|
-
matchedField: policyResult.matchedField,
|
|
5968
|
-
matchedWord: policyResult.matchedWord
|
|
5969
|
-
}
|
|
5970
|
-
);
|
|
5971
5907
|
const baseLabel = policyResult.blockedByLabel || "Smart Rule";
|
|
5972
5908
|
const OVERRIDE_PREFIX = "\u26A0\uFE0F Override block rule: ";
|
|
5973
5909
|
if (!baseLabel.startsWith(OVERRIDE_PREFIX)) {
|
|
@@ -5992,14 +5928,6 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
5992
5928
|
{ ...meta, ruleName: policyResult.ruleName },
|
|
5993
5929
|
hashAuditArgs
|
|
5994
5930
|
);
|
|
5995
|
-
if (approvers.cloud && creds?.apiKey)
|
|
5996
|
-
auditLocalAllow(toolName, args, "smart-rule-block", creds, meta, void 0, false, {
|
|
5997
|
-
ruleName: policyResult.ruleName,
|
|
5998
|
-
ruleDescription: policyResult.ruleDescription,
|
|
5999
|
-
blockedByLabel: policyResult.blockedByLabel,
|
|
6000
|
-
matchedField: policyResult.matchedField,
|
|
6001
|
-
matchedWord: policyResult.matchedWord
|
|
6002
|
-
});
|
|
6003
5931
|
return {
|
|
6004
5932
|
approved: false,
|
|
6005
5933
|
reason: policyResult.reason ?? "Action explicitly blocked by Smart Policy.",
|
|
@@ -6028,8 +5956,6 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
6028
5956
|
if (policyRuleDescription) riskMetadata.ruleDescription = policyRuleDescription.slice(0, 200);
|
|
6029
5957
|
const persistent = policyResult.ruleName ? null : getPersistentDecision(toolName);
|
|
6030
5958
|
if (persistent === "allow") {
|
|
6031
|
-
if (approvers.cloud && creds?.apiKey)
|
|
6032
|
-
await auditLocalAllow(toolName, args, "persistent", creds, meta);
|
|
6033
5959
|
if (!isManual) appendLocalAudit(toolName, args, "allow", "persistent", meta, hashAuditArgs);
|
|
6034
5960
|
return { approved: true, checkedBy: "persistent" };
|
|
6035
5961
|
}
|
|
@@ -6062,8 +5988,6 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
6062
5988
|
}
|
|
6063
5989
|
}
|
|
6064
5990
|
if (!taintWarning && getActiveTrustSession(toolName, args)) {
|
|
6065
|
-
if (approvers.cloud && creds?.apiKey)
|
|
6066
|
-
await auditLocalAllow(toolName, args, "trust", creds, meta);
|
|
6067
5991
|
if (!isManual) appendLocalAudit(toolName, args, "allow", "trust", meta, hashAuditArgs);
|
|
6068
5992
|
return { approved: true, checkedBy: "trust" };
|
|
6069
5993
|
}
|
|
@@ -6308,7 +6232,12 @@ REASON: Action blocked because no approval channels are available. (Native/Brows
|
|
|
6308
6232
|
args,
|
|
6309
6233
|
finalResult.approved ? "allow" : "deny",
|
|
6310
6234
|
finalResult.checkedBy || finalResult.blockedBy || "unknown",
|
|
6311
|
-
|
|
6235
|
+
// cloudRequestId links this row to the BE-origin AuditLog row the
|
|
6236
|
+
// /intercept handshake created — the shipper hands it to the SaaS so
|
|
6237
|
+
// the BE enriches that row instead of inserting a duplicate. Matters
|
|
6238
|
+
// for EVERY racer outcome, not just cloud wins: a native-popup
|
|
6239
|
+
// decision on a cloud-pending request would otherwise count twice.
|
|
6240
|
+
cloudRequestId ? { ...meta, cloudRequestId } : meta,
|
|
6312
6241
|
hashAuditArgs
|
|
6313
6242
|
);
|
|
6314
6243
|
}
|
|
@@ -6362,7 +6291,7 @@ var init_core = __esm({
|
|
|
6362
6291
|
import fs12 from "fs";
|
|
6363
6292
|
import path14 from "path";
|
|
6364
6293
|
import os11 from "os";
|
|
6365
|
-
import
|
|
6294
|
+
import crypto4 from "crypto";
|
|
6366
6295
|
function getHomePinsFilePath() {
|
|
6367
6296
|
return path14.join(os11.homedir(), ".node9", "mcp-pins.json");
|
|
6368
6297
|
}
|
|
@@ -6392,10 +6321,10 @@ function hashToolDefinitions(tools) {
|
|
|
6392
6321
|
return nameA.localeCompare(nameB);
|
|
6393
6322
|
});
|
|
6394
6323
|
const canonical = JSON.stringify(sorted);
|
|
6395
|
-
return
|
|
6324
|
+
return crypto4.createHash("sha256").update(canonical).digest("hex");
|
|
6396
6325
|
}
|
|
6397
6326
|
function getServerKey(upstreamCommand) {
|
|
6398
|
-
return
|
|
6327
|
+
return crypto4.createHash("sha256").update(upstreamCommand).digest("hex").slice(0, 16);
|
|
6399
6328
|
}
|
|
6400
6329
|
function readPinsFile(filePath) {
|
|
6401
6330
|
try {
|
|
@@ -6426,7 +6355,7 @@ function readMcpPins() {
|
|
|
6426
6355
|
}
|
|
6427
6356
|
function writePinsFile(filePath, data) {
|
|
6428
6357
|
fs12.mkdirSync(path14.dirname(filePath), { recursive: true });
|
|
6429
|
-
const tmp = `${filePath}.${
|
|
6358
|
+
const tmp = `${filePath}.${crypto4.randomBytes(6).toString("hex")}.tmp`;
|
|
6430
6359
|
const isHome = filePath === getHomePinsFilePath();
|
|
6431
6360
|
fs12.writeFileSync(tmp, JSON.stringify(data, null, 2), isHome ? { mode: 384 } : {});
|
|
6432
6361
|
fs12.renameSync(tmp, filePath);
|
|
@@ -14256,74 +14185,285 @@ var init_sync = __esm({
|
|
|
14256
14185
|
}
|
|
14257
14186
|
});
|
|
14258
14187
|
|
|
14259
|
-
// src/daemon/
|
|
14188
|
+
// src/daemon/audit-shipper.ts
|
|
14189
|
+
var audit_shipper_exports = {};
|
|
14190
|
+
__export(audit_shipper_exports, {
|
|
14191
|
+
AUDIT_SHIP_WATERMARK: () => AUDIT_SHIP_WATERMARK,
|
|
14192
|
+
buildWireRows: () => buildWireRows,
|
|
14193
|
+
fileSignature: () => fileSignature,
|
|
14194
|
+
readWatermark: () => readWatermark,
|
|
14195
|
+
shipLagBytes: () => shipLagBytes,
|
|
14196
|
+
shipOnce: () => shipOnce,
|
|
14197
|
+
startAuditShipper: () => startAuditShipper,
|
|
14198
|
+
writeWatermark: () => writeWatermark
|
|
14199
|
+
});
|
|
14260
14200
|
import fs24 from "fs";
|
|
14261
14201
|
import path26 from "path";
|
|
14262
14202
|
import os22 from "os";
|
|
14203
|
+
import crypto5 from "crypto";
|
|
14204
|
+
function fileSignature(filePath) {
|
|
14205
|
+
const fd = fs24.openSync(filePath, "r");
|
|
14206
|
+
try {
|
|
14207
|
+
const buf = Buffer.alloc(512);
|
|
14208
|
+
const read = fs24.readSync(fd, buf, 0, 512, 0);
|
|
14209
|
+
const slice = buf.subarray(0, read);
|
|
14210
|
+
const nl = slice.indexOf(10);
|
|
14211
|
+
const firstLine = nl === -1 ? slice : slice.subarray(0, nl);
|
|
14212
|
+
return crypto5.createHash("sha256").update(firstLine).digest("hex").slice(0, 16);
|
|
14213
|
+
} finally {
|
|
14214
|
+
fs24.closeSync(fd);
|
|
14215
|
+
}
|
|
14216
|
+
}
|
|
14217
|
+
function readWatermark(watermarkPath) {
|
|
14218
|
+
try {
|
|
14219
|
+
const raw = JSON.parse(fs24.readFileSync(watermarkPath, "utf-8"));
|
|
14220
|
+
if (typeof raw.fileSig === "string" && typeof raw.offset === "number" && raw.offset >= 0)
|
|
14221
|
+
return raw;
|
|
14222
|
+
} catch {
|
|
14223
|
+
}
|
|
14224
|
+
return null;
|
|
14225
|
+
}
|
|
14226
|
+
function writeWatermark(watermarkPath, wm) {
|
|
14227
|
+
const tmp = `${watermarkPath}.tmp`;
|
|
14228
|
+
fs24.writeFileSync(tmp, JSON.stringify(wm));
|
|
14229
|
+
fs24.renameSync(tmp, watermarkPath);
|
|
14230
|
+
}
|
|
14231
|
+
function buildWireRows(chunk) {
|
|
14232
|
+
const lastNl = chunk.lastIndexOf(10);
|
|
14233
|
+
if (lastNl === -1) return { rows: [], consumed: 0 };
|
|
14234
|
+
const complete = chunk.subarray(0, lastNl + 1);
|
|
14235
|
+
const rows = [];
|
|
14236
|
+
for (const line of complete.toString("utf-8").split("\n")) {
|
|
14237
|
+
if (!line.trim()) continue;
|
|
14238
|
+
let parsed;
|
|
14239
|
+
try {
|
|
14240
|
+
parsed = JSON.parse(line);
|
|
14241
|
+
} catch {
|
|
14242
|
+
continue;
|
|
14243
|
+
}
|
|
14244
|
+
if (typeof parsed.eid !== "string" || parsed.eid.length < 8) continue;
|
|
14245
|
+
if (typeof parsed.tool !== "string" || !parsed.tool) continue;
|
|
14246
|
+
if (parsed.decision !== "allow" && parsed.decision !== "deny") continue;
|
|
14247
|
+
if (typeof parsed.ts !== "string") continue;
|
|
14248
|
+
if (parsed.testRun === true) continue;
|
|
14249
|
+
const checkedBy = typeof parsed.checkedBy === "string" ? parsed.checkedBy : void 0;
|
|
14250
|
+
if (checkedBy && SKIP_CHECKED_BY.has(checkedBy)) continue;
|
|
14251
|
+
const cloudRequestId = typeof parsed.cloudRequestId === "string" ? parsed.cloudRequestId : void 0;
|
|
14252
|
+
if (checkedBy === "cloud" && !cloudRequestId) continue;
|
|
14253
|
+
rows.push({
|
|
14254
|
+
eid: parsed.eid,
|
|
14255
|
+
ts: parsed.ts,
|
|
14256
|
+
tool: parsed.tool,
|
|
14257
|
+
...parsed.args && typeof parsed.args === "object" ? { args: parsed.args } : {},
|
|
14258
|
+
...typeof parsed.argsHash === "string" ? { argsHash: parsed.argsHash } : {},
|
|
14259
|
+
...typeof parsed.argsPreview === "string" ? { argsPreview: parsed.argsPreview } : {},
|
|
14260
|
+
decision: parsed.decision,
|
|
14261
|
+
...checkedBy ? { checkedBy } : {},
|
|
14262
|
+
...typeof parsed.ruleName === "string" ? { ruleName: parsed.ruleName } : {},
|
|
14263
|
+
...typeof parsed.agent === "string" ? { agent: parsed.agent } : {},
|
|
14264
|
+
...typeof parsed.mcpServer === "string" ? { mcpServer: parsed.mcpServer } : {},
|
|
14265
|
+
...typeof parsed.sessionId === "string" ? { sessionId: parsed.sessionId } : {},
|
|
14266
|
+
...typeof parsed.dlpPattern === "string" ? { dlpPattern: parsed.dlpPattern } : {},
|
|
14267
|
+
...typeof parsed.dlpSample === "string" ? { dlpSample: parsed.dlpSample } : {},
|
|
14268
|
+
...cloudRequestId ? { cloudRequestId } : {}
|
|
14269
|
+
});
|
|
14270
|
+
}
|
|
14271
|
+
return { rows, consumed: lastNl + 1 };
|
|
14272
|
+
}
|
|
14273
|
+
async function shipOnce(deps = {}) {
|
|
14274
|
+
const auditLogPath = deps.auditLogPath ?? LOCAL_AUDIT_LOG;
|
|
14275
|
+
const watermarkPath = deps.watermarkPath ?? AUDIT_SHIP_WATERMARK;
|
|
14276
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
14277
|
+
let cloudEnabled = deps.cloudEnabled;
|
|
14278
|
+
if (cloudEnabled === void 0) {
|
|
14279
|
+
try {
|
|
14280
|
+
const settings = getConfig().settings;
|
|
14281
|
+
cloudEnabled = settings.shipper.enabled !== false && settings.approvers.cloud;
|
|
14282
|
+
} catch {
|
|
14283
|
+
cloudEnabled = false;
|
|
14284
|
+
}
|
|
14285
|
+
}
|
|
14286
|
+
if (!cloudEnabled) return { status: "disabled", shipped: 0 };
|
|
14287
|
+
const creds = deps.creds !== void 0 ? deps.creds : readCredentials();
|
|
14288
|
+
if (!creds?.apiKey) return { status: "no-creds", shipped: 0 };
|
|
14289
|
+
const validated = validateApiUrl(creds.apiUrl);
|
|
14290
|
+
if (!validated) return { status: "no-creds", shipped: 0 };
|
|
14291
|
+
const endpoint = `${validated.toString().replace(/\/$/, "")}/audit/batch`;
|
|
14292
|
+
if (!fs24.existsSync(auditLogPath)) return { status: "idle", shipped: 0 };
|
|
14293
|
+
let shipped = 0;
|
|
14294
|
+
try {
|
|
14295
|
+
for (let chunkN = 0; chunkN < MAX_CHUNKS_PER_TICK; chunkN++) {
|
|
14296
|
+
const size = fs24.statSync(auditLogPath).size;
|
|
14297
|
+
if (size === 0) break;
|
|
14298
|
+
const sig = fileSignature(auditLogPath);
|
|
14299
|
+
const wm = readWatermark(watermarkPath);
|
|
14300
|
+
const offset = wm && wm.fileSig === sig && wm.offset <= size ? wm.offset : 0;
|
|
14301
|
+
if (offset >= size) break;
|
|
14302
|
+
const toRead = Math.min(size - offset, MAX_CHUNK_BYTES);
|
|
14303
|
+
const buf = Buffer.alloc(toRead);
|
|
14304
|
+
const fd = fs24.openSync(auditLogPath, "r");
|
|
14305
|
+
let read;
|
|
14306
|
+
try {
|
|
14307
|
+
read = fs24.readSync(fd, buf, 0, toRead, offset);
|
|
14308
|
+
} finally {
|
|
14309
|
+
fs24.closeSync(fd);
|
|
14310
|
+
}
|
|
14311
|
+
const { rows, consumed } = buildWireRows(buf.subarray(0, read));
|
|
14312
|
+
if (consumed === 0) break;
|
|
14313
|
+
for (let i = 0; i < rows.length; i += MAX_BATCH) {
|
|
14314
|
+
const batch = rows.slice(i, i + MAX_BATCH);
|
|
14315
|
+
const controller = new AbortController();
|
|
14316
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
14317
|
+
try {
|
|
14318
|
+
const res = await fetchImpl(endpoint, {
|
|
14319
|
+
method: "POST",
|
|
14320
|
+
headers: {
|
|
14321
|
+
"Content-Type": "application/json",
|
|
14322
|
+
Authorization: `Bearer ${creds.apiKey}`
|
|
14323
|
+
},
|
|
14324
|
+
body: JSON.stringify({ rows: batch }),
|
|
14325
|
+
signal: controller.signal
|
|
14326
|
+
});
|
|
14327
|
+
if (!res.ok) throw new Error(`audit/batch HTTP ${res.status}`);
|
|
14328
|
+
} finally {
|
|
14329
|
+
clearTimeout(timer);
|
|
14330
|
+
}
|
|
14331
|
+
shipped += batch.length;
|
|
14332
|
+
}
|
|
14333
|
+
writeWatermark(watermarkPath, {
|
|
14334
|
+
fileSig: sig,
|
|
14335
|
+
offset: offset + consumed,
|
|
14336
|
+
lastEid: rows.length > 0 ? rows[rows.length - 1].eid : wm?.lastEid,
|
|
14337
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
14338
|
+
});
|
|
14339
|
+
if (consumed < toRead) break;
|
|
14340
|
+
}
|
|
14341
|
+
} catch (err2) {
|
|
14342
|
+
try {
|
|
14343
|
+
appendToLog(HOOK_DEBUG_LOG, {
|
|
14344
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14345
|
+
shipper: "error",
|
|
14346
|
+
message: err2.message
|
|
14347
|
+
});
|
|
14348
|
+
} catch {
|
|
14349
|
+
}
|
|
14350
|
+
return { status: "error", shipped };
|
|
14351
|
+
}
|
|
14352
|
+
return { status: shipped > 0 ? "shipped" : "idle", shipped };
|
|
14353
|
+
}
|
|
14354
|
+
function shipLagBytes(auditLogPath = LOCAL_AUDIT_LOG, watermarkPath = AUDIT_SHIP_WATERMARK) {
|
|
14355
|
+
try {
|
|
14356
|
+
if (!fs24.existsSync(auditLogPath)) return 0;
|
|
14357
|
+
const size = fs24.statSync(auditLogPath).size;
|
|
14358
|
+
const wm = readWatermark(watermarkPath);
|
|
14359
|
+
if (!wm) return size;
|
|
14360
|
+
if (wm.fileSig !== fileSignature(auditLogPath)) return size;
|
|
14361
|
+
return Math.max(0, size - wm.offset);
|
|
14362
|
+
} catch {
|
|
14363
|
+
return null;
|
|
14364
|
+
}
|
|
14365
|
+
}
|
|
14366
|
+
function startAuditShipper() {
|
|
14367
|
+
if (shipperStarted) return;
|
|
14368
|
+
shipperStarted = true;
|
|
14369
|
+
const intervalMs = (() => {
|
|
14370
|
+
try {
|
|
14371
|
+
const sec = getConfig().settings.shipper.intervalSeconds;
|
|
14372
|
+
return sec >= 5 ? sec * 1e3 : DEFAULT_INTERVAL_MS;
|
|
14373
|
+
} catch {
|
|
14374
|
+
return DEFAULT_INTERVAL_MS;
|
|
14375
|
+
}
|
|
14376
|
+
})();
|
|
14377
|
+
setTimeout(() => void shipOnce(), 3e3);
|
|
14378
|
+
setInterval(() => void shipOnce(), intervalMs);
|
|
14379
|
+
}
|
|
14380
|
+
var AUDIT_SHIP_WATERMARK, DEFAULT_INTERVAL_MS, MAX_BATCH, MAX_CHUNK_BYTES, MAX_CHUNKS_PER_TICK, FETCH_TIMEOUT_MS, SKIP_CHECKED_BY, shipperStarted;
|
|
14381
|
+
var init_audit_shipper = __esm({
|
|
14382
|
+
"src/daemon/audit-shipper.ts"() {
|
|
14383
|
+
"use strict";
|
|
14384
|
+
init_audit();
|
|
14385
|
+
init_config();
|
|
14386
|
+
init_sync();
|
|
14387
|
+
init_cloud();
|
|
14388
|
+
AUDIT_SHIP_WATERMARK = path26.join(os22.homedir(), ".node9", "audit-ship.json");
|
|
14389
|
+
DEFAULT_INTERVAL_MS = 2e4;
|
|
14390
|
+
MAX_BATCH = 500;
|
|
14391
|
+
MAX_CHUNK_BYTES = 4 * 1024 * 1024;
|
|
14392
|
+
MAX_CHUNKS_PER_TICK = 10;
|
|
14393
|
+
FETCH_TIMEOUT_MS = 1e4;
|
|
14394
|
+
SKIP_CHECKED_BY = /* @__PURE__ */ new Set(["ignored"]);
|
|
14395
|
+
shipperStarted = false;
|
|
14396
|
+
}
|
|
14397
|
+
});
|
|
14398
|
+
|
|
14399
|
+
// src/daemon/dlp-scanner.ts
|
|
14400
|
+
import fs25 from "fs";
|
|
14401
|
+
import path27 from "path";
|
|
14402
|
+
import os23 from "os";
|
|
14263
14403
|
function loadIndex() {
|
|
14264
14404
|
try {
|
|
14265
|
-
return JSON.parse(
|
|
14405
|
+
return JSON.parse(fs25.readFileSync(INDEX_FILE, "utf-8"));
|
|
14266
14406
|
} catch {
|
|
14267
14407
|
return {};
|
|
14268
14408
|
}
|
|
14269
14409
|
}
|
|
14270
14410
|
function saveIndex(index) {
|
|
14271
14411
|
try {
|
|
14272
|
-
|
|
14412
|
+
fs25.writeFileSync(INDEX_FILE, JSON.stringify(index), { encoding: "utf-8", mode: 384 });
|
|
14273
14413
|
} catch {
|
|
14274
14414
|
}
|
|
14275
14415
|
}
|
|
14276
14416
|
function appendAuditEntry(entry) {
|
|
14277
14417
|
try {
|
|
14278
|
-
|
|
14418
|
+
fs25.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
|
|
14279
14419
|
} catch {
|
|
14280
14420
|
}
|
|
14281
14421
|
}
|
|
14282
14422
|
function runDlpScan() {
|
|
14283
|
-
if (!
|
|
14423
|
+
if (!fs25.existsSync(PROJECTS_DIR2)) return;
|
|
14284
14424
|
const index = loadIndex();
|
|
14285
14425
|
let updated = false;
|
|
14286
14426
|
let projDirs;
|
|
14287
14427
|
try {
|
|
14288
|
-
projDirs =
|
|
14428
|
+
projDirs = fs25.readdirSync(PROJECTS_DIR2);
|
|
14289
14429
|
} catch {
|
|
14290
14430
|
return;
|
|
14291
14431
|
}
|
|
14292
14432
|
for (const proj of projDirs) {
|
|
14293
|
-
const projPath =
|
|
14433
|
+
const projPath = path27.join(PROJECTS_DIR2, proj);
|
|
14294
14434
|
try {
|
|
14295
|
-
if (!
|
|
14296
|
-
const real =
|
|
14297
|
-
if (!real.startsWith(PROJECTS_DIR2 +
|
|
14435
|
+
if (!fs25.lstatSync(projPath).isDirectory()) continue;
|
|
14436
|
+
const real = fs25.realpathSync(projPath);
|
|
14437
|
+
if (!real.startsWith(PROJECTS_DIR2 + path27.sep) && real !== PROJECTS_DIR2) continue;
|
|
14298
14438
|
} catch {
|
|
14299
14439
|
continue;
|
|
14300
14440
|
}
|
|
14301
14441
|
let files;
|
|
14302
14442
|
try {
|
|
14303
|
-
files =
|
|
14443
|
+
files = fs25.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
14304
14444
|
} catch {
|
|
14305
14445
|
continue;
|
|
14306
14446
|
}
|
|
14307
14447
|
for (const file of files) {
|
|
14308
|
-
const filePath =
|
|
14448
|
+
const filePath = path27.join(projPath, file);
|
|
14309
14449
|
const lastOffset = index[filePath] ?? 0;
|
|
14310
14450
|
let size;
|
|
14311
14451
|
try {
|
|
14312
|
-
size =
|
|
14452
|
+
size = fs25.statSync(filePath).size;
|
|
14313
14453
|
} catch {
|
|
14314
14454
|
continue;
|
|
14315
14455
|
}
|
|
14316
14456
|
if (size <= lastOffset) continue;
|
|
14317
14457
|
let fd;
|
|
14318
14458
|
try {
|
|
14319
|
-
fd =
|
|
14459
|
+
fd = fs25.openSync(filePath, "r");
|
|
14320
14460
|
} catch {
|
|
14321
14461
|
continue;
|
|
14322
14462
|
}
|
|
14323
14463
|
try {
|
|
14324
14464
|
const chunkSize = size - lastOffset;
|
|
14325
14465
|
const buf = Buffer.alloc(chunkSize);
|
|
14326
|
-
|
|
14466
|
+
fs25.readSync(fd, buf, 0, chunkSize, lastOffset);
|
|
14327
14467
|
const chunk = buf.toString("utf-8");
|
|
14328
14468
|
for (const line of chunk.split("\n")) {
|
|
14329
14469
|
if (!line.trim()) continue;
|
|
@@ -14343,7 +14483,7 @@ function runDlpScan() {
|
|
|
14343
14483
|
if (typeof text !== "string") continue;
|
|
14344
14484
|
const match = scanText(text);
|
|
14345
14485
|
if (!match) continue;
|
|
14346
|
-
const projLabel = decodeURIComponent(proj).replace(
|
|
14486
|
+
const projLabel = decodeURIComponent(proj).replace(os23.homedir(), "~").slice(0, 40);
|
|
14347
14487
|
const ts = entry.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
14348
14488
|
appendAuditEntry({
|
|
14349
14489
|
ts,
|
|
@@ -14368,7 +14508,7 @@ Run: node9 report --period 30d`
|
|
|
14368
14508
|
updated = true;
|
|
14369
14509
|
} finally {
|
|
14370
14510
|
try {
|
|
14371
|
-
|
|
14511
|
+
fs25.closeSync(fd);
|
|
14372
14512
|
} catch {
|
|
14373
14513
|
}
|
|
14374
14514
|
}
|
|
@@ -14401,23 +14541,23 @@ var init_dlp_scanner = __esm({
|
|
|
14401
14541
|
init_dlp();
|
|
14402
14542
|
init_native();
|
|
14403
14543
|
init_state2();
|
|
14404
|
-
INDEX_FILE =
|
|
14405
|
-
PROJECTS_DIR2 =
|
|
14544
|
+
INDEX_FILE = path27.join(os23.homedir(), ".node9", "dlp-index.json");
|
|
14545
|
+
PROJECTS_DIR2 = path27.join(os23.homedir(), ".claude", "projects");
|
|
14406
14546
|
}
|
|
14407
14547
|
});
|
|
14408
14548
|
|
|
14409
14549
|
// src/daemon/mcp-tools.ts
|
|
14410
|
-
import
|
|
14411
|
-
import
|
|
14412
|
-
import
|
|
14550
|
+
import fs26 from "fs";
|
|
14551
|
+
import path28 from "path";
|
|
14552
|
+
import os24 from "os";
|
|
14413
14553
|
function getMcpToolsFile() {
|
|
14414
|
-
return
|
|
14554
|
+
return path28.join(os24.homedir(), ".node9", "mcp-tools.json");
|
|
14415
14555
|
}
|
|
14416
14556
|
function readMcpToolsConfig() {
|
|
14417
14557
|
try {
|
|
14418
14558
|
const file = getMcpToolsFile();
|
|
14419
|
-
if (!
|
|
14420
|
-
const raw =
|
|
14559
|
+
if (!fs26.existsSync(file)) return {};
|
|
14560
|
+
const raw = fs26.readFileSync(file, "utf-8");
|
|
14421
14561
|
return JSON.parse(raw);
|
|
14422
14562
|
} catch {
|
|
14423
14563
|
return {};
|
|
@@ -14426,11 +14566,11 @@ function readMcpToolsConfig() {
|
|
|
14426
14566
|
function writeMcpToolsConfig(config) {
|
|
14427
14567
|
try {
|
|
14428
14568
|
const file = getMcpToolsFile();
|
|
14429
|
-
const dir =
|
|
14430
|
-
if (!
|
|
14431
|
-
const tmpPath = `${file}.${
|
|
14432
|
-
|
|
14433
|
-
|
|
14569
|
+
const dir = path28.dirname(file);
|
|
14570
|
+
if (!fs26.existsSync(dir)) fs26.mkdirSync(dir, { recursive: true });
|
|
14571
|
+
const tmpPath = `${file}.${os24.hostname()}.${process.pid}.tmp`;
|
|
14572
|
+
fs26.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
|
|
14573
|
+
fs26.renameSync(tmpPath, file);
|
|
14434
14574
|
} catch (e) {
|
|
14435
14575
|
console.error("Failed to write mcp-tools.json", e);
|
|
14436
14576
|
}
|
|
@@ -14477,9 +14617,9 @@ var init_mcp_tools = __esm({
|
|
|
14477
14617
|
|
|
14478
14618
|
// src/daemon/server.ts
|
|
14479
14619
|
import http from "http";
|
|
14480
|
-
import
|
|
14481
|
-
import
|
|
14482
|
-
import
|
|
14620
|
+
import fs27 from "fs";
|
|
14621
|
+
import path29 from "path";
|
|
14622
|
+
import os25 from "os";
|
|
14483
14623
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
14484
14624
|
import { spawnSync } from "child_process";
|
|
14485
14625
|
import chalk6 from "chalk";
|
|
@@ -14487,6 +14627,7 @@ function startDaemon() {
|
|
|
14487
14627
|
startCostSync();
|
|
14488
14628
|
startCloudSync();
|
|
14489
14629
|
startForensicBroadcast();
|
|
14630
|
+
startAuditShipper();
|
|
14490
14631
|
startDlpScanner();
|
|
14491
14632
|
loadInsightCounts();
|
|
14492
14633
|
const internalToken = randomUUID4();
|
|
@@ -14500,7 +14641,7 @@ function startDaemon() {
|
|
|
14500
14641
|
idleTimer = setTimeout(() => {
|
|
14501
14642
|
if (autoStarted) {
|
|
14502
14643
|
try {
|
|
14503
|
-
|
|
14644
|
+
fs27.unlinkSync(DAEMON_PID_FILE);
|
|
14504
14645
|
} catch {
|
|
14505
14646
|
}
|
|
14506
14647
|
}
|
|
@@ -14645,7 +14786,7 @@ data: ${JSON.stringify(item.data)}
|
|
|
14645
14786
|
mcpServer: entry.mcpServer
|
|
14646
14787
|
});
|
|
14647
14788
|
}
|
|
14648
|
-
const projectCwd = typeof cwd === "string" &&
|
|
14789
|
+
const projectCwd = typeof cwd === "string" && path29.isAbsolute(cwd) ? cwd : void 0;
|
|
14649
14790
|
const projectConfig = getConfig(projectCwd);
|
|
14650
14791
|
const browserEnabled = projectConfig.settings.approvers?.browser !== false;
|
|
14651
14792
|
const terminalEnabled = projectConfig.settings.approvers?.terminal !== false;
|
|
@@ -14937,8 +15078,8 @@ data: ${JSON.stringify(item.data)}
|
|
|
14937
15078
|
if (!validToken(req)) return res.writeHead(403).end();
|
|
14938
15079
|
const periodParam = reqUrl.searchParams.get("period") || "7d";
|
|
14939
15080
|
const period = ["today", "7d", "30d", "month"].includes(periodParam) ? periodParam : "7d";
|
|
14940
|
-
const logPath =
|
|
14941
|
-
if (!
|
|
15081
|
+
const logPath = path29.join(os25.homedir(), ".node9", "audit.log");
|
|
15082
|
+
if (!fs27.existsSync(logPath)) {
|
|
14942
15083
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
14943
15084
|
return res.end(
|
|
14944
15085
|
JSON.stringify({
|
|
@@ -14951,7 +15092,7 @@ data: ${JSON.stringify(item.data)}
|
|
|
14951
15092
|
);
|
|
14952
15093
|
}
|
|
14953
15094
|
try {
|
|
14954
|
-
const raw =
|
|
15095
|
+
const raw = fs27.readFileSync(logPath, "utf-8");
|
|
14955
15096
|
const allEntries = raw.split("\n").flatMap((line) => {
|
|
14956
15097
|
if (!line.trim()) return [];
|
|
14957
15098
|
try {
|
|
@@ -15274,14 +15415,14 @@ data: ${JSON.stringify(item.data)}
|
|
|
15274
15415
|
server.on("error", (e) => {
|
|
15275
15416
|
if (e.code === "EADDRINUSE") {
|
|
15276
15417
|
try {
|
|
15277
|
-
if (
|
|
15278
|
-
const { pid } = JSON.parse(
|
|
15418
|
+
if (fs27.existsSync(DAEMON_PID_FILE)) {
|
|
15419
|
+
const { pid } = JSON.parse(fs27.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
15279
15420
|
process.kill(pid, 0);
|
|
15280
15421
|
return process.exit(0);
|
|
15281
15422
|
}
|
|
15282
15423
|
} catch {
|
|
15283
15424
|
try {
|
|
15284
|
-
|
|
15425
|
+
fs27.unlinkSync(DAEMON_PID_FILE);
|
|
15285
15426
|
} catch {
|
|
15286
15427
|
}
|
|
15287
15428
|
server.listen(DAEMON_PORT, DAEMON_HOST);
|
|
@@ -15363,21 +15504,22 @@ var init_server = __esm({
|
|
|
15363
15504
|
init_state();
|
|
15364
15505
|
init_costSync();
|
|
15365
15506
|
init_sync();
|
|
15507
|
+
init_audit_shipper();
|
|
15366
15508
|
init_dlp_scanner();
|
|
15367
15509
|
init_mcp_tools();
|
|
15368
15510
|
}
|
|
15369
15511
|
});
|
|
15370
15512
|
|
|
15371
15513
|
// src/daemon/service.ts
|
|
15372
|
-
import
|
|
15373
|
-
import
|
|
15374
|
-
import
|
|
15514
|
+
import fs28 from "fs";
|
|
15515
|
+
import path30 from "path";
|
|
15516
|
+
import os26 from "os";
|
|
15375
15517
|
import { spawnSync as spawnSync2, execFileSync } from "child_process";
|
|
15376
15518
|
function resolveNode9Binary() {
|
|
15377
15519
|
try {
|
|
15378
15520
|
const script = process.argv[1];
|
|
15379
|
-
if (typeof script === "string" &&
|
|
15380
|
-
return
|
|
15521
|
+
if (typeof script === "string" && path30.isAbsolute(script) && fs28.existsSync(script)) {
|
|
15522
|
+
return fs28.realpathSync(script);
|
|
15381
15523
|
}
|
|
15382
15524
|
} catch {
|
|
15383
15525
|
}
|
|
@@ -15395,11 +15537,11 @@ function xmlEscape(s) {
|
|
|
15395
15537
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
15396
15538
|
}
|
|
15397
15539
|
function launchdPlist(binaryPath) {
|
|
15398
|
-
const logDir =
|
|
15540
|
+
const logDir = path30.join(os26.homedir(), ".node9");
|
|
15399
15541
|
const nodePath = xmlEscape(process.execPath);
|
|
15400
15542
|
const scriptPath = xmlEscape(binaryPath);
|
|
15401
|
-
const outLog = xmlEscape(
|
|
15402
|
-
const errLog = xmlEscape(
|
|
15543
|
+
const outLog = xmlEscape(path30.join(logDir, "daemon.log"));
|
|
15544
|
+
const errLog = xmlEscape(path30.join(logDir, "daemon-error.log"));
|
|
15403
15545
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
15404
15546
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
15405
15547
|
<plist version="1.0">
|
|
@@ -15432,9 +15574,9 @@ function launchdPlist(binaryPath) {
|
|
|
15432
15574
|
`;
|
|
15433
15575
|
}
|
|
15434
15576
|
function installLaunchd(binaryPath) {
|
|
15435
|
-
const dir =
|
|
15436
|
-
if (!
|
|
15437
|
-
|
|
15577
|
+
const dir = path30.dirname(LAUNCHD_PLIST);
|
|
15578
|
+
if (!fs28.existsSync(dir)) fs28.mkdirSync(dir, { recursive: true });
|
|
15579
|
+
fs28.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
|
|
15438
15580
|
spawnSync2("launchctl", ["unload", LAUNCHD_PLIST], { encoding: "utf8" });
|
|
15439
15581
|
const r = spawnSync2("launchctl", ["load", "-w", LAUNCHD_PLIST], {
|
|
15440
15582
|
encoding: "utf8",
|
|
@@ -15445,13 +15587,13 @@ function installLaunchd(binaryPath) {
|
|
|
15445
15587
|
}
|
|
15446
15588
|
}
|
|
15447
15589
|
function uninstallLaunchd() {
|
|
15448
|
-
if (
|
|
15590
|
+
if (fs28.existsSync(LAUNCHD_PLIST)) {
|
|
15449
15591
|
spawnSync2("launchctl", ["unload", "-w", LAUNCHD_PLIST], { encoding: "utf8", timeout: 5e3 });
|
|
15450
|
-
|
|
15592
|
+
fs28.unlinkSync(LAUNCHD_PLIST);
|
|
15451
15593
|
}
|
|
15452
15594
|
}
|
|
15453
15595
|
function isLaunchdInstalled() {
|
|
15454
|
-
return
|
|
15596
|
+
return fs28.existsSync(LAUNCHD_PLIST);
|
|
15455
15597
|
}
|
|
15456
15598
|
function systemdUnit(binaryPath) {
|
|
15457
15599
|
return `[Unit]
|
|
@@ -15470,12 +15612,12 @@ WantedBy=default.target
|
|
|
15470
15612
|
`;
|
|
15471
15613
|
}
|
|
15472
15614
|
function installSystemd(binaryPath) {
|
|
15473
|
-
if (!
|
|
15474
|
-
|
|
15615
|
+
if (!fs28.existsSync(SYSTEMD_UNIT_DIR)) {
|
|
15616
|
+
fs28.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
|
|
15475
15617
|
}
|
|
15476
|
-
|
|
15618
|
+
fs28.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
|
|
15477
15619
|
try {
|
|
15478
|
-
execFileSync("loginctl", ["enable-linger",
|
|
15620
|
+
execFileSync("loginctl", ["enable-linger", os26.userInfo().username], { timeout: 3e3 });
|
|
15479
15621
|
} catch {
|
|
15480
15622
|
}
|
|
15481
15623
|
const reload = spawnSync2("systemctl", ["--user", "daemon-reload"], {
|
|
@@ -15495,23 +15637,23 @@ function installSystemd(binaryPath) {
|
|
|
15495
15637
|
}
|
|
15496
15638
|
}
|
|
15497
15639
|
function uninstallSystemd() {
|
|
15498
|
-
if (
|
|
15640
|
+
if (fs28.existsSync(SYSTEMD_UNIT)) {
|
|
15499
15641
|
spawnSync2("systemctl", ["--user", "disable", "--now", "node9-daemon"], {
|
|
15500
15642
|
encoding: "utf8",
|
|
15501
15643
|
timeout: 5e3
|
|
15502
15644
|
});
|
|
15503
15645
|
spawnSync2("systemctl", ["--user", "daemon-reload"], { encoding: "utf8", timeout: 5e3 });
|
|
15504
|
-
|
|
15646
|
+
fs28.unlinkSync(SYSTEMD_UNIT);
|
|
15505
15647
|
}
|
|
15506
15648
|
}
|
|
15507
15649
|
function isSystemdInstalled() {
|
|
15508
|
-
return
|
|
15650
|
+
return fs28.existsSync(SYSTEMD_UNIT);
|
|
15509
15651
|
}
|
|
15510
15652
|
function stopRunningDaemon() {
|
|
15511
|
-
const pidFile =
|
|
15512
|
-
if (!
|
|
15653
|
+
const pidFile = path30.join(os26.homedir(), ".node9", "daemon.pid");
|
|
15654
|
+
if (!fs28.existsSync(pidFile)) return;
|
|
15513
15655
|
try {
|
|
15514
|
-
const data = JSON.parse(
|
|
15656
|
+
const data = JSON.parse(fs28.readFileSync(pidFile, "utf-8"));
|
|
15515
15657
|
const pid = data.pid;
|
|
15516
15658
|
const MAX_PID2 = 4194304;
|
|
15517
15659
|
if (typeof pid === "number" && Number.isInteger(pid) && pid > 0 && pid <= MAX_PID2) {
|
|
@@ -15531,7 +15673,7 @@ function stopRunningDaemon() {
|
|
|
15531
15673
|
}
|
|
15532
15674
|
}
|
|
15533
15675
|
try {
|
|
15534
|
-
|
|
15676
|
+
fs28.unlinkSync(pidFile);
|
|
15535
15677
|
} catch {
|
|
15536
15678
|
}
|
|
15537
15679
|
} catch {
|
|
@@ -15606,19 +15748,19 @@ var init_service = __esm({
|
|
|
15606
15748
|
"src/daemon/service.ts"() {
|
|
15607
15749
|
"use strict";
|
|
15608
15750
|
LAUNCHD_LABEL = "ai.node9.daemon";
|
|
15609
|
-
LAUNCHD_PLIST =
|
|
15610
|
-
SYSTEMD_UNIT_DIR =
|
|
15611
|
-
SYSTEMD_UNIT =
|
|
15751
|
+
LAUNCHD_PLIST = path30.join(os26.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
|
|
15752
|
+
SYSTEMD_UNIT_DIR = path30.join(os26.homedir(), ".config", "systemd", "user");
|
|
15753
|
+
SYSTEMD_UNIT = path30.join(SYSTEMD_UNIT_DIR, "node9-daemon.service");
|
|
15612
15754
|
}
|
|
15613
15755
|
});
|
|
15614
15756
|
|
|
15615
15757
|
// src/daemon/index.ts
|
|
15616
|
-
import
|
|
15758
|
+
import fs29 from "fs";
|
|
15617
15759
|
import chalk7 from "chalk";
|
|
15618
15760
|
function stopDaemon() {
|
|
15619
|
-
if (!
|
|
15761
|
+
if (!fs29.existsSync(DAEMON_PID_FILE)) return console.log(chalk7.yellow("Not running."));
|
|
15620
15762
|
try {
|
|
15621
|
-
const data = JSON.parse(
|
|
15763
|
+
const data = JSON.parse(fs29.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
15622
15764
|
const pid = data.pid;
|
|
15623
15765
|
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
|
|
15624
15766
|
console.log(chalk7.gray("Cleaned up invalid PID file."));
|
|
@@ -15630,7 +15772,7 @@ function stopDaemon() {
|
|
|
15630
15772
|
console.log(chalk7.gray("Cleaned up stale PID file."));
|
|
15631
15773
|
} finally {
|
|
15632
15774
|
try {
|
|
15633
|
-
|
|
15775
|
+
fs29.unlinkSync(DAEMON_PID_FILE);
|
|
15634
15776
|
} catch {
|
|
15635
15777
|
}
|
|
15636
15778
|
}
|
|
@@ -15639,9 +15781,9 @@ function daemonStatus() {
|
|
|
15639
15781
|
const serviceInstalled = isDaemonServiceInstalled();
|
|
15640
15782
|
const serviceLabel = serviceInstalled ? chalk7.green("installed (starts on login)") : chalk7.yellow("not installed \u2014 run: node9 daemon install");
|
|
15641
15783
|
let processStatus;
|
|
15642
|
-
if (
|
|
15784
|
+
if (fs29.existsSync(DAEMON_PID_FILE)) {
|
|
15643
15785
|
try {
|
|
15644
|
-
const data = JSON.parse(
|
|
15786
|
+
const data = JSON.parse(fs29.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
15645
15787
|
const pid = data.pid;
|
|
15646
15788
|
const port = data.port;
|
|
15647
15789
|
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
|
|
@@ -15686,9 +15828,9 @@ __export(tail_exports, {
|
|
|
15686
15828
|
});
|
|
15687
15829
|
import http2 from "http";
|
|
15688
15830
|
import chalk29 from "chalk";
|
|
15689
|
-
import
|
|
15690
|
-
import
|
|
15691
|
-
import
|
|
15831
|
+
import fs47 from "fs";
|
|
15832
|
+
import os42 from "os";
|
|
15833
|
+
import path48 from "path";
|
|
15692
15834
|
import readline6 from "readline";
|
|
15693
15835
|
import { spawn as spawn8 } from "child_process";
|
|
15694
15836
|
function shortenPathSummary(s) {
|
|
@@ -15712,20 +15854,20 @@ function getModelContextLimit(model) {
|
|
|
15712
15854
|
return 2e5;
|
|
15713
15855
|
}
|
|
15714
15856
|
function readSessionUsage() {
|
|
15715
|
-
const projectsDir =
|
|
15716
|
-
if (!
|
|
15857
|
+
const projectsDir = path48.join(os42.homedir(), ".claude", "projects");
|
|
15858
|
+
if (!fs47.existsSync(projectsDir)) return null;
|
|
15717
15859
|
let latestFile = null;
|
|
15718
15860
|
let latestMtime = 0;
|
|
15719
15861
|
try {
|
|
15720
|
-
for (const dir of
|
|
15721
|
-
const dirPath =
|
|
15862
|
+
for (const dir of fs47.readdirSync(projectsDir)) {
|
|
15863
|
+
const dirPath = path48.join(projectsDir, dir);
|
|
15722
15864
|
try {
|
|
15723
|
-
if (!
|
|
15724
|
-
for (const file of
|
|
15865
|
+
if (!fs47.statSync(dirPath).isDirectory()) continue;
|
|
15866
|
+
for (const file of fs47.readdirSync(dirPath)) {
|
|
15725
15867
|
if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
|
|
15726
|
-
const filePath =
|
|
15868
|
+
const filePath = path48.join(dirPath, file);
|
|
15727
15869
|
try {
|
|
15728
|
-
const mtime =
|
|
15870
|
+
const mtime = fs47.statSync(filePath).mtimeMs;
|
|
15729
15871
|
if (mtime > latestMtime) {
|
|
15730
15872
|
latestMtime = mtime;
|
|
15731
15873
|
latestFile = filePath;
|
|
@@ -15740,7 +15882,7 @@ function readSessionUsage() {
|
|
|
15740
15882
|
}
|
|
15741
15883
|
if (!latestFile) return null;
|
|
15742
15884
|
try {
|
|
15743
|
-
const lines =
|
|
15885
|
+
const lines = fs47.readFileSync(latestFile, "utf-8").split("\n");
|
|
15744
15886
|
let lastModel = "";
|
|
15745
15887
|
let lastInput = 0;
|
|
15746
15888
|
let lastOutput = 0;
|
|
@@ -15801,7 +15943,7 @@ function formatBase(activity) {
|
|
|
15801
15943
|
const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
|
|
15802
15944
|
const icon = getIcon(activity.tool);
|
|
15803
15945
|
const toolName = activity.tool.slice(0, 16).padEnd(16);
|
|
15804
|
-
const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(
|
|
15946
|
+
const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(os42.homedir(), "~");
|
|
15805
15947
|
const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
|
|
15806
15948
|
return `${chalk29.gray(time)} ${icon} ${agentLabel(activity.agent, activity.mcpServer, activity.sessionId)}${chalk29.white.bold(toolName)} ${chalk29.dim(argsPreview)}`;
|
|
15807
15949
|
}
|
|
@@ -15840,9 +15982,9 @@ function renderPending(activity) {
|
|
|
15840
15982
|
}
|
|
15841
15983
|
async function ensureDaemon() {
|
|
15842
15984
|
let pidPort = null;
|
|
15843
|
-
if (
|
|
15985
|
+
if (fs47.existsSync(PID_FILE)) {
|
|
15844
15986
|
try {
|
|
15845
|
-
const { port } = JSON.parse(
|
|
15987
|
+
const { port } = JSON.parse(fs47.readFileSync(PID_FILE, "utf-8"));
|
|
15846
15988
|
pidPort = port;
|
|
15847
15989
|
} catch {
|
|
15848
15990
|
console.error(chalk29.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
|
|
@@ -15998,9 +16140,9 @@ function buildRecoveryCardLines(req) {
|
|
|
15998
16140
|
];
|
|
15999
16141
|
}
|
|
16000
16142
|
function readApproversFromDisk() {
|
|
16001
|
-
const configPath =
|
|
16143
|
+
const configPath = path48.join(os42.homedir(), ".node9", "config.json");
|
|
16002
16144
|
try {
|
|
16003
|
-
const raw = JSON.parse(
|
|
16145
|
+
const raw = JSON.parse(fs47.readFileSync(configPath, "utf-8"));
|
|
16004
16146
|
const settings = raw.settings ?? {};
|
|
16005
16147
|
return settings.approvers ?? {};
|
|
16006
16148
|
} catch {
|
|
@@ -16016,15 +16158,15 @@ function approverStatusLine() {
|
|
|
16016
16158
|
return `${fmt("native", "native")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
|
|
16017
16159
|
}
|
|
16018
16160
|
function toggleApprover(channel) {
|
|
16019
|
-
const configPath =
|
|
16161
|
+
const configPath = path48.join(os42.homedir(), ".node9", "config.json");
|
|
16020
16162
|
try {
|
|
16021
|
-
const raw = JSON.parse(
|
|
16163
|
+
const raw = JSON.parse(fs47.readFileSync(configPath, "utf-8"));
|
|
16022
16164
|
const settings = raw.settings ?? {};
|
|
16023
16165
|
const approvers = settings.approvers ?? {};
|
|
16024
16166
|
approvers[channel] = approvers[channel] === false;
|
|
16025
16167
|
settings.approvers = approvers;
|
|
16026
16168
|
raw.settings = settings;
|
|
16027
|
-
|
|
16169
|
+
fs47.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
|
|
16028
16170
|
} catch (err2) {
|
|
16029
16171
|
process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
|
|
16030
16172
|
`);
|
|
@@ -16196,8 +16338,8 @@ async function startTail(options = {}) {
|
|
|
16196
16338
|
}
|
|
16197
16339
|
postDecisionHttp(req2.id, httpDecision, authToken, port, httpOpts).catch((err2) => {
|
|
16198
16340
|
try {
|
|
16199
|
-
|
|
16200
|
-
|
|
16341
|
+
fs47.appendFileSync(
|
|
16342
|
+
path48.join(os42.homedir(), ".node9", "hook-debug.log"),
|
|
16201
16343
|
`[tail] POST /decision failed: ${String(err2)}
|
|
16202
16344
|
`
|
|
16203
16345
|
);
|
|
@@ -16261,9 +16403,9 @@ async function startTail(options = {}) {
|
|
|
16261
16403
|
};
|
|
16262
16404
|
process.stdin.on("keypress", onKeypress);
|
|
16263
16405
|
}
|
|
16264
|
-
const auditLog =
|
|
16406
|
+
const auditLog = path48.join(os42.homedir(), ".node9", "audit.log");
|
|
16265
16407
|
try {
|
|
16266
|
-
const unackedDlp =
|
|
16408
|
+
const unackedDlp = fs47.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
|
|
16267
16409
|
if (unackedDlp > 0) {
|
|
16268
16410
|
console.log("");
|
|
16269
16411
|
console.log(
|
|
@@ -16303,7 +16445,7 @@ async function startTail(options = {}) {
|
|
|
16303
16445
|
if (stallWarned) return;
|
|
16304
16446
|
if (Date.now() - lastActivityFromDaemon < STALL_THRESHOLD_MS) return;
|
|
16305
16447
|
try {
|
|
16306
|
-
const auditMtime =
|
|
16448
|
+
const auditMtime = fs47.statSync(auditLog).mtimeMs;
|
|
16307
16449
|
if (Date.now() - auditMtime >= STALL_THRESHOLD_MS) return;
|
|
16308
16450
|
console.log("");
|
|
16309
16451
|
console.log(
|
|
@@ -16494,7 +16636,7 @@ var init_tail = __esm({
|
|
|
16494
16636
|
"use strict";
|
|
16495
16637
|
init_daemon2();
|
|
16496
16638
|
init_daemon();
|
|
16497
|
-
PID_FILE =
|
|
16639
|
+
PID_FILE = path48.join(os42.homedir(), ".node9", "daemon.pid");
|
|
16498
16640
|
ICONS = {
|
|
16499
16641
|
bash: "\u{1F4BB}",
|
|
16500
16642
|
shell: "\u{1F4BB}",
|
|
@@ -16542,9 +16684,9 @@ __export(hud_exports, {
|
|
|
16542
16684
|
main: () => main,
|
|
16543
16685
|
renderEnvironmentLine: () => renderEnvironmentLine
|
|
16544
16686
|
});
|
|
16545
|
-
import
|
|
16546
|
-
import
|
|
16547
|
-
import
|
|
16687
|
+
import fs48 from "fs";
|
|
16688
|
+
import path49 from "path";
|
|
16689
|
+
import os43 from "os";
|
|
16548
16690
|
import http3 from "http";
|
|
16549
16691
|
async function readStdin() {
|
|
16550
16692
|
const chunks = [];
|
|
@@ -16620,9 +16762,9 @@ function formatTimeLeft(resetsAt) {
|
|
|
16620
16762
|
return ` (${m}m left)`;
|
|
16621
16763
|
}
|
|
16622
16764
|
function safeReadJson(filePath) {
|
|
16623
|
-
if (!
|
|
16765
|
+
if (!fs48.existsSync(filePath)) return null;
|
|
16624
16766
|
try {
|
|
16625
|
-
return JSON.parse(
|
|
16767
|
+
return JSON.parse(fs48.readFileSync(filePath, "utf-8"));
|
|
16626
16768
|
} catch {
|
|
16627
16769
|
return null;
|
|
16628
16770
|
}
|
|
@@ -16643,12 +16785,12 @@ function countHooksInFile(filePath) {
|
|
|
16643
16785
|
return Object.keys(cfg.hooks).length;
|
|
16644
16786
|
}
|
|
16645
16787
|
function countRulesInDir(rulesDir) {
|
|
16646
|
-
if (!
|
|
16788
|
+
if (!fs48.existsSync(rulesDir)) return 0;
|
|
16647
16789
|
let count = 0;
|
|
16648
16790
|
try {
|
|
16649
|
-
for (const entry of
|
|
16791
|
+
for (const entry of fs48.readdirSync(rulesDir, { withFileTypes: true })) {
|
|
16650
16792
|
if (entry.isDirectory()) {
|
|
16651
|
-
count += countRulesInDir(
|
|
16793
|
+
count += countRulesInDir(path49.join(rulesDir, entry.name));
|
|
16652
16794
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
16653
16795
|
count++;
|
|
16654
16796
|
}
|
|
@@ -16659,46 +16801,46 @@ function countRulesInDir(rulesDir) {
|
|
|
16659
16801
|
}
|
|
16660
16802
|
function isSamePath(a, b) {
|
|
16661
16803
|
try {
|
|
16662
|
-
return
|
|
16804
|
+
return path49.resolve(a) === path49.resolve(b);
|
|
16663
16805
|
} catch {
|
|
16664
16806
|
return false;
|
|
16665
16807
|
}
|
|
16666
16808
|
}
|
|
16667
16809
|
function countConfigs(cwd) {
|
|
16668
|
-
const homeDir2 =
|
|
16669
|
-
const claudeDir =
|
|
16810
|
+
const homeDir2 = os43.homedir();
|
|
16811
|
+
const claudeDir = path49.join(homeDir2, ".claude");
|
|
16670
16812
|
let claudeMdCount = 0;
|
|
16671
16813
|
let rulesCount = 0;
|
|
16672
16814
|
let hooksCount = 0;
|
|
16673
16815
|
const userMcpServers = /* @__PURE__ */ new Set();
|
|
16674
16816
|
const projectMcpServers = /* @__PURE__ */ new Set();
|
|
16675
|
-
if (
|
|
16676
|
-
rulesCount += countRulesInDir(
|
|
16677
|
-
const userSettings =
|
|
16817
|
+
if (fs48.existsSync(path49.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
16818
|
+
rulesCount += countRulesInDir(path49.join(claudeDir, "rules"));
|
|
16819
|
+
const userSettings = path49.join(claudeDir, "settings.json");
|
|
16678
16820
|
for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
|
|
16679
16821
|
hooksCount += countHooksInFile(userSettings);
|
|
16680
|
-
const userClaudeJson =
|
|
16822
|
+
const userClaudeJson = path49.join(homeDir2, ".claude.json");
|
|
16681
16823
|
for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
|
|
16682
16824
|
for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
|
|
16683
16825
|
userMcpServers.delete(name);
|
|
16684
16826
|
}
|
|
16685
16827
|
if (cwd) {
|
|
16686
|
-
if (
|
|
16687
|
-
if (
|
|
16688
|
-
const projectClaudeDir =
|
|
16828
|
+
if (fs48.existsSync(path49.join(cwd, "CLAUDE.md"))) claudeMdCount++;
|
|
16829
|
+
if (fs48.existsSync(path49.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
|
|
16830
|
+
const projectClaudeDir = path49.join(cwd, ".claude");
|
|
16689
16831
|
const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
|
|
16690
16832
|
if (!overlapsUserScope) {
|
|
16691
|
-
if (
|
|
16692
|
-
rulesCount += countRulesInDir(
|
|
16693
|
-
const projSettings =
|
|
16833
|
+
if (fs48.existsSync(path49.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
16834
|
+
rulesCount += countRulesInDir(path49.join(projectClaudeDir, "rules"));
|
|
16835
|
+
const projSettings = path49.join(projectClaudeDir, "settings.json");
|
|
16694
16836
|
for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
|
|
16695
16837
|
hooksCount += countHooksInFile(projSettings);
|
|
16696
16838
|
}
|
|
16697
|
-
if (
|
|
16698
|
-
const localSettings =
|
|
16839
|
+
if (fs48.existsSync(path49.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
|
|
16840
|
+
const localSettings = path49.join(projectClaudeDir, "settings.local.json");
|
|
16699
16841
|
for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
|
|
16700
16842
|
hooksCount += countHooksInFile(localSettings);
|
|
16701
|
-
const mcpJsonServers = getMcpServerNames(
|
|
16843
|
+
const mcpJsonServers = getMcpServerNames(path49.join(cwd, ".mcp.json"));
|
|
16702
16844
|
const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
|
|
16703
16845
|
for (const name of disabledMcpJson) mcpJsonServers.delete(name);
|
|
16704
16846
|
for (const name of mcpJsonServers) projectMcpServers.add(name);
|
|
@@ -16731,12 +16873,12 @@ function readActiveShieldsHud() {
|
|
|
16731
16873
|
return shieldsCache.value;
|
|
16732
16874
|
}
|
|
16733
16875
|
try {
|
|
16734
|
-
const shieldsPath =
|
|
16735
|
-
if (!
|
|
16876
|
+
const shieldsPath = path49.join(os43.homedir(), ".node9", "shields.json");
|
|
16877
|
+
if (!fs48.existsSync(shieldsPath)) {
|
|
16736
16878
|
shieldsCache = { value: [], ts: now };
|
|
16737
16879
|
return [];
|
|
16738
16880
|
}
|
|
16739
|
-
const parsed = JSON.parse(
|
|
16881
|
+
const parsed = JSON.parse(fs48.readFileSync(shieldsPath, "utf-8"));
|
|
16740
16882
|
if (!Array.isArray(parsed.active)) {
|
|
16741
16883
|
shieldsCache = { value: [], ts: now };
|
|
16742
16884
|
return [];
|
|
@@ -16838,17 +16980,17 @@ function renderContextLine(stdin) {
|
|
|
16838
16980
|
async function main() {
|
|
16839
16981
|
try {
|
|
16840
16982
|
const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
|
|
16841
|
-
if (
|
|
16983
|
+
if (fs48.existsSync(path49.join(os43.homedir(), ".node9", "hud-debug"))) {
|
|
16842
16984
|
try {
|
|
16843
|
-
const logPath =
|
|
16985
|
+
const logPath = path49.join(os43.homedir(), ".node9", "hud-debug.log");
|
|
16844
16986
|
const MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
16845
16987
|
let size = 0;
|
|
16846
16988
|
try {
|
|
16847
|
-
size =
|
|
16989
|
+
size = fs48.statSync(logPath).size;
|
|
16848
16990
|
} catch {
|
|
16849
16991
|
}
|
|
16850
16992
|
if (size < MAX_LOG_SIZE) {
|
|
16851
|
-
|
|
16993
|
+
fs48.appendFileSync(
|
|
16852
16994
|
logPath,
|
|
16853
16995
|
JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
|
|
16854
16996
|
);
|
|
@@ -16869,11 +17011,11 @@ async function main() {
|
|
|
16869
17011
|
try {
|
|
16870
17012
|
const cwd = stdin.cwd ?? process.cwd();
|
|
16871
17013
|
for (const configPath of [
|
|
16872
|
-
|
|
16873
|
-
|
|
17014
|
+
path49.join(cwd, "node9.config.json"),
|
|
17015
|
+
path49.join(os43.homedir(), ".node9", "config.json")
|
|
16874
17016
|
]) {
|
|
16875
|
-
if (!
|
|
16876
|
-
const cfg = JSON.parse(
|
|
17017
|
+
if (!fs48.existsSync(configPath)) continue;
|
|
17018
|
+
const cfg = JSON.parse(fs48.readFileSync(configPath, "utf-8"));
|
|
16877
17019
|
const hud = cfg.settings?.hud;
|
|
16878
17020
|
if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
|
|
16879
17021
|
}
|
|
@@ -16920,9 +17062,9 @@ init_setup();
|
|
|
16920
17062
|
init_daemon2();
|
|
16921
17063
|
import { Command } from "commander";
|
|
16922
17064
|
import chalk30 from "chalk";
|
|
16923
|
-
import
|
|
16924
|
-
import
|
|
16925
|
-
import
|
|
17065
|
+
import fs49 from "fs";
|
|
17066
|
+
import path50 from "path";
|
|
17067
|
+
import os44 from "os";
|
|
16926
17068
|
import { confirm as confirm2 } from "@inquirer/prompts";
|
|
16927
17069
|
|
|
16928
17070
|
// src/utils/duration.ts
|
|
@@ -17105,17 +17247,17 @@ async function runProxy(targetCommand) {
|
|
|
17105
17247
|
// src/cli/daemon-starter.ts
|
|
17106
17248
|
init_daemon();
|
|
17107
17249
|
import { spawn as spawn3 } from "child_process";
|
|
17108
|
-
import
|
|
17109
|
-
import
|
|
17250
|
+
import path31 from "path";
|
|
17251
|
+
import fs30 from "fs";
|
|
17110
17252
|
function isTestingMode() {
|
|
17111
17253
|
return /^(1|true|yes)$/i.test(process.env.NODE9_TESTING ?? "");
|
|
17112
17254
|
}
|
|
17113
17255
|
async function autoStartDaemonAndWait() {
|
|
17114
17256
|
if (isTestingMode()) return false;
|
|
17115
|
-
if (!
|
|
17257
|
+
if (!path31.isAbsolute(process.argv[1])) return false;
|
|
17116
17258
|
let resolvedArgv1;
|
|
17117
17259
|
try {
|
|
17118
|
-
resolvedArgv1 =
|
|
17260
|
+
resolvedArgv1 = fs30.realpathSync(process.argv[1]);
|
|
17119
17261
|
} catch {
|
|
17120
17262
|
return false;
|
|
17121
17263
|
}
|
|
@@ -17146,19 +17288,19 @@ init_daemon();
|
|
|
17146
17288
|
init_config();
|
|
17147
17289
|
init_policy();
|
|
17148
17290
|
import chalk9 from "chalk";
|
|
17149
|
-
import
|
|
17291
|
+
import fs33 from "fs";
|
|
17150
17292
|
import { spawn as spawn5 } from "child_process";
|
|
17151
|
-
import
|
|
17152
|
-
import
|
|
17293
|
+
import path34 from "path";
|
|
17294
|
+
import os29 from "os";
|
|
17153
17295
|
|
|
17154
17296
|
// src/undo.ts
|
|
17155
17297
|
import { spawnSync as spawnSync3, spawn as spawn4 } from "child_process";
|
|
17156
|
-
import
|
|
17157
|
-
import
|
|
17298
|
+
import crypto6 from "crypto";
|
|
17299
|
+
import fs31 from "fs";
|
|
17158
17300
|
import net3 from "net";
|
|
17159
|
-
import
|
|
17160
|
-
import
|
|
17161
|
-
var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" :
|
|
17301
|
+
import path32 from "path";
|
|
17302
|
+
import os27 from "os";
|
|
17303
|
+
var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path32.join(os27.tmpdir(), "node9-activity.sock");
|
|
17162
17304
|
function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
|
|
17163
17305
|
try {
|
|
17164
17306
|
const payload = JSON.stringify({
|
|
@@ -17178,22 +17320,22 @@ function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
|
|
|
17178
17320
|
} catch {
|
|
17179
17321
|
}
|
|
17180
17322
|
}
|
|
17181
|
-
var SNAPSHOT_STACK_PATH =
|
|
17182
|
-
var UNDO_LATEST_PATH =
|
|
17323
|
+
var SNAPSHOT_STACK_PATH = path32.join(os27.homedir(), ".node9", "snapshots.json");
|
|
17324
|
+
var UNDO_LATEST_PATH = path32.join(os27.homedir(), ".node9", "undo_latest.txt");
|
|
17183
17325
|
var MAX_SNAPSHOTS = 10;
|
|
17184
17326
|
var GIT_TIMEOUT = 15e3;
|
|
17185
17327
|
function readStack() {
|
|
17186
17328
|
try {
|
|
17187
|
-
if (
|
|
17188
|
-
return JSON.parse(
|
|
17329
|
+
if (fs31.existsSync(SNAPSHOT_STACK_PATH))
|
|
17330
|
+
return JSON.parse(fs31.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
|
|
17189
17331
|
} catch {
|
|
17190
17332
|
}
|
|
17191
17333
|
return [];
|
|
17192
17334
|
}
|
|
17193
17335
|
function writeStack(stack) {
|
|
17194
|
-
const dir =
|
|
17195
|
-
if (!
|
|
17196
|
-
|
|
17336
|
+
const dir = path32.dirname(SNAPSHOT_STACK_PATH);
|
|
17337
|
+
if (!fs31.existsSync(dir)) fs31.mkdirSync(dir, { recursive: true });
|
|
17338
|
+
fs31.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
|
|
17197
17339
|
}
|
|
17198
17340
|
function extractFilePath(args) {
|
|
17199
17341
|
if (!args || typeof args !== "object") return null;
|
|
@@ -17213,12 +17355,12 @@ function buildArgsSummary(tool, args) {
|
|
|
17213
17355
|
return "";
|
|
17214
17356
|
}
|
|
17215
17357
|
function findProjectRoot(filePath) {
|
|
17216
|
-
let dir =
|
|
17358
|
+
let dir = path32.dirname(filePath);
|
|
17217
17359
|
while (true) {
|
|
17218
|
-
if (
|
|
17360
|
+
if (fs31.existsSync(path32.join(dir, ".git")) || fs31.existsSync(path32.join(dir, "package.json"))) {
|
|
17219
17361
|
return dir;
|
|
17220
17362
|
}
|
|
17221
|
-
const parent =
|
|
17363
|
+
const parent = path32.dirname(dir);
|
|
17222
17364
|
if (parent === dir) return process.cwd();
|
|
17223
17365
|
dir = parent;
|
|
17224
17366
|
}
|
|
@@ -17226,7 +17368,7 @@ function findProjectRoot(filePath) {
|
|
|
17226
17368
|
function normalizeCwdForHash(cwd) {
|
|
17227
17369
|
let normalized;
|
|
17228
17370
|
try {
|
|
17229
|
-
normalized =
|
|
17371
|
+
normalized = fs31.realpathSync(cwd);
|
|
17230
17372
|
} catch {
|
|
17231
17373
|
normalized = cwd;
|
|
17232
17374
|
}
|
|
@@ -17235,17 +17377,17 @@ function normalizeCwdForHash(cwd) {
|
|
|
17235
17377
|
return normalized;
|
|
17236
17378
|
}
|
|
17237
17379
|
function getShadowRepoDir(cwd) {
|
|
17238
|
-
const hash =
|
|
17239
|
-
return
|
|
17380
|
+
const hash = crypto6.createHash("sha256").update(normalizeCwdForHash(cwd)).digest("hex").slice(0, 16);
|
|
17381
|
+
return path32.join(os27.homedir(), ".node9", "snapshots", hash);
|
|
17240
17382
|
}
|
|
17241
17383
|
function cleanOrphanedIndexFiles(shadowDir) {
|
|
17242
17384
|
try {
|
|
17243
17385
|
const cutoff = Date.now() - 6e4;
|
|
17244
|
-
for (const f of
|
|
17386
|
+
for (const f of fs31.readdirSync(shadowDir)) {
|
|
17245
17387
|
if (f.startsWith("index_")) {
|
|
17246
|
-
const fp =
|
|
17388
|
+
const fp = path32.join(shadowDir, f);
|
|
17247
17389
|
try {
|
|
17248
|
-
if (
|
|
17390
|
+
if (fs31.statSync(fp).mtimeMs < cutoff) fs31.unlinkSync(fp);
|
|
17249
17391
|
} catch {
|
|
17250
17392
|
}
|
|
17251
17393
|
}
|
|
@@ -17257,7 +17399,7 @@ function writeShadowExcludes(shadowDir, ignorePaths) {
|
|
|
17257
17399
|
const hardcoded = [".git", ".node9"];
|
|
17258
17400
|
const lines = [...hardcoded, ...ignorePaths].join("\n");
|
|
17259
17401
|
try {
|
|
17260
|
-
|
|
17402
|
+
fs31.writeFileSync(path32.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
|
|
17261
17403
|
} catch {
|
|
17262
17404
|
}
|
|
17263
17405
|
}
|
|
@@ -17270,25 +17412,25 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
17270
17412
|
timeout: 3e3
|
|
17271
17413
|
});
|
|
17272
17414
|
if (check.status === 0) {
|
|
17273
|
-
const ptPath =
|
|
17415
|
+
const ptPath = path32.join(shadowDir, "project-path.txt");
|
|
17274
17416
|
try {
|
|
17275
|
-
const stored =
|
|
17417
|
+
const stored = fs31.readFileSync(ptPath, "utf8").trim();
|
|
17276
17418
|
if (stored === normalizedCwd) return true;
|
|
17277
17419
|
if (process.env.NODE9_DEBUG === "1")
|
|
17278
17420
|
console.error(
|
|
17279
17421
|
`[Node9] Shadow repo path mismatch: stored="${stored}" expected="${normalizedCwd}" \u2014 reinitializing`
|
|
17280
17422
|
);
|
|
17281
|
-
|
|
17423
|
+
fs31.rmSync(shadowDir, { recursive: true, force: true });
|
|
17282
17424
|
} catch {
|
|
17283
17425
|
try {
|
|
17284
|
-
|
|
17426
|
+
fs31.writeFileSync(ptPath, normalizedCwd, "utf8");
|
|
17285
17427
|
} catch {
|
|
17286
17428
|
}
|
|
17287
17429
|
return true;
|
|
17288
17430
|
}
|
|
17289
17431
|
}
|
|
17290
17432
|
try {
|
|
17291
|
-
|
|
17433
|
+
fs31.mkdirSync(shadowDir, { recursive: true });
|
|
17292
17434
|
} catch {
|
|
17293
17435
|
}
|
|
17294
17436
|
const init = spawnSync3("git", ["init", "--bare", shadowDir], { timeout: 5e3 });
|
|
@@ -17297,7 +17439,7 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
17297
17439
|
if (process.env.NODE9_DEBUG === "1") console.error("[Node9] git init --bare failed:", reason);
|
|
17298
17440
|
return false;
|
|
17299
17441
|
}
|
|
17300
|
-
const configFile =
|
|
17442
|
+
const configFile = path32.join(shadowDir, "config");
|
|
17301
17443
|
spawnSync3("git", ["config", "--file", configFile, "core.untrackedCache", "true"], {
|
|
17302
17444
|
timeout: 3e3
|
|
17303
17445
|
});
|
|
@@ -17305,7 +17447,7 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
17305
17447
|
timeout: 3e3
|
|
17306
17448
|
});
|
|
17307
17449
|
try {
|
|
17308
|
-
|
|
17450
|
+
fs31.writeFileSync(path32.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
|
|
17309
17451
|
} catch {
|
|
17310
17452
|
}
|
|
17311
17453
|
return true;
|
|
@@ -17328,12 +17470,12 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
17328
17470
|
let indexFile = null;
|
|
17329
17471
|
try {
|
|
17330
17472
|
const rawFilePath = extractFilePath(args);
|
|
17331
|
-
const absFilePath = rawFilePath &&
|
|
17473
|
+
const absFilePath = rawFilePath && path32.isAbsolute(rawFilePath) ? rawFilePath : null;
|
|
17332
17474
|
const cwd = absFilePath ? findProjectRoot(absFilePath) : process.cwd();
|
|
17333
17475
|
const shadowDir = getShadowRepoDir(cwd);
|
|
17334
17476
|
if (!ensureShadowRepo(shadowDir, cwd)) return null;
|
|
17335
17477
|
writeShadowExcludes(shadowDir, ignorePaths);
|
|
17336
|
-
indexFile =
|
|
17478
|
+
indexFile = path32.join(shadowDir, `index_${process.pid}_${Date.now()}`);
|
|
17337
17479
|
const shadowEnv = {
|
|
17338
17480
|
...process.env,
|
|
17339
17481
|
GIT_DIR: shadowDir,
|
|
@@ -17405,7 +17547,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
17405
17547
|
writeStack(stack);
|
|
17406
17548
|
const entry = stack[stack.length - 1];
|
|
17407
17549
|
notifySnapshotTaken(commitHash.slice(0, 7), tool, entry.argsSummary, capturedFiles.length);
|
|
17408
|
-
|
|
17550
|
+
fs31.writeFileSync(UNDO_LATEST_PATH, commitHash);
|
|
17409
17551
|
if (shouldGc) {
|
|
17410
17552
|
spawn4("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
|
|
17411
17553
|
}
|
|
@@ -17416,7 +17558,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
17416
17558
|
} finally {
|
|
17417
17559
|
if (indexFile) {
|
|
17418
17560
|
try {
|
|
17419
|
-
|
|
17561
|
+
fs31.unlinkSync(indexFile);
|
|
17420
17562
|
} catch {
|
|
17421
17563
|
}
|
|
17422
17564
|
}
|
|
@@ -17492,9 +17634,9 @@ function applyUndo(hash, cwd) {
|
|
|
17492
17634
|
timeout: GIT_TIMEOUT
|
|
17493
17635
|
}).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
|
|
17494
17636
|
for (const file of [...tracked, ...untracked]) {
|
|
17495
|
-
const fullPath =
|
|
17496
|
-
if (!snapshotFiles.has(file) &&
|
|
17497
|
-
|
|
17637
|
+
const fullPath = path32.join(dir, file);
|
|
17638
|
+
if (!snapshotFiles.has(file) && fs31.existsSync(fullPath)) {
|
|
17639
|
+
fs31.unlinkSync(fullPath);
|
|
17498
17640
|
}
|
|
17499
17641
|
}
|
|
17500
17642
|
return true;
|
|
@@ -17504,17 +17646,17 @@ function applyUndo(hash, cwd) {
|
|
|
17504
17646
|
}
|
|
17505
17647
|
|
|
17506
17648
|
// src/skill-pin.ts
|
|
17507
|
-
import
|
|
17508
|
-
import
|
|
17509
|
-
import
|
|
17510
|
-
import
|
|
17649
|
+
import fs32 from "fs";
|
|
17650
|
+
import path33 from "path";
|
|
17651
|
+
import os28 from "os";
|
|
17652
|
+
import crypto7 from "crypto";
|
|
17511
17653
|
function getPinsFilePath2() {
|
|
17512
|
-
return
|
|
17654
|
+
return path33.join(os28.homedir(), ".node9", "skill-pins.json");
|
|
17513
17655
|
}
|
|
17514
17656
|
var MAX_FILES = 5e3;
|
|
17515
17657
|
var MAX_TOTAL_BYTES = 50 * 1024 * 1024;
|
|
17516
17658
|
function sha256Bytes(buf) {
|
|
17517
|
-
return
|
|
17659
|
+
return crypto7.createHash("sha256").update(buf).digest("hex");
|
|
17518
17660
|
}
|
|
17519
17661
|
function walkDir(root) {
|
|
17520
17662
|
const out = [];
|
|
@@ -17523,18 +17665,18 @@ function walkDir(root) {
|
|
|
17523
17665
|
if (out.length >= MAX_FILES) return;
|
|
17524
17666
|
let entries;
|
|
17525
17667
|
try {
|
|
17526
|
-
entries =
|
|
17668
|
+
entries = fs32.readdirSync(dir, { withFileTypes: true });
|
|
17527
17669
|
} catch {
|
|
17528
17670
|
return;
|
|
17529
17671
|
}
|
|
17530
17672
|
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
17531
17673
|
for (const entry of entries) {
|
|
17532
17674
|
if (out.length >= MAX_FILES) return;
|
|
17533
|
-
const full =
|
|
17534
|
-
const rel = relDir ?
|
|
17675
|
+
const full = path33.join(dir, entry.name);
|
|
17676
|
+
const rel = relDir ? path33.posix.join(relDir, entry.name) : entry.name;
|
|
17535
17677
|
let lst;
|
|
17536
17678
|
try {
|
|
17537
|
-
lst =
|
|
17679
|
+
lst = fs32.lstatSync(full);
|
|
17538
17680
|
} catch {
|
|
17539
17681
|
continue;
|
|
17540
17682
|
}
|
|
@@ -17546,7 +17688,7 @@ function walkDir(root) {
|
|
|
17546
17688
|
if (!lst.isFile()) continue;
|
|
17547
17689
|
if (totalBytes + lst.size > MAX_TOTAL_BYTES) continue;
|
|
17548
17690
|
try {
|
|
17549
|
-
const buf =
|
|
17691
|
+
const buf = fs32.readFileSync(full);
|
|
17550
17692
|
totalBytes += buf.length;
|
|
17551
17693
|
out.push({ rel, hash: sha256Bytes(buf) });
|
|
17552
17694
|
} catch {
|
|
@@ -17560,32 +17702,32 @@ function walkDir(root) {
|
|
|
17560
17702
|
function hashSkillRoot(absPath) {
|
|
17561
17703
|
let lst;
|
|
17562
17704
|
try {
|
|
17563
|
-
lst =
|
|
17705
|
+
lst = fs32.lstatSync(absPath);
|
|
17564
17706
|
} catch {
|
|
17565
17707
|
return { exists: false, contentHash: "", fileCount: 0 };
|
|
17566
17708
|
}
|
|
17567
17709
|
if (lst.isSymbolicLink()) return { exists: false, contentHash: "", fileCount: 0 };
|
|
17568
17710
|
if (lst.isFile()) {
|
|
17569
17711
|
try {
|
|
17570
|
-
return { exists: true, contentHash: sha256Bytes(
|
|
17712
|
+
return { exists: true, contentHash: sha256Bytes(fs32.readFileSync(absPath)), fileCount: 1 };
|
|
17571
17713
|
} catch {
|
|
17572
17714
|
return { exists: false, contentHash: "", fileCount: 0 };
|
|
17573
17715
|
}
|
|
17574
17716
|
}
|
|
17575
17717
|
if (lst.isDirectory()) {
|
|
17576
17718
|
const entries = walkDir(absPath);
|
|
17577
|
-
const contentHash =
|
|
17719
|
+
const contentHash = crypto7.createHash("sha256").update(entries.join("\n")).digest("hex");
|
|
17578
17720
|
return { exists: true, contentHash, fileCount: entries.length };
|
|
17579
17721
|
}
|
|
17580
17722
|
return { exists: false, contentHash: "", fileCount: 0 };
|
|
17581
17723
|
}
|
|
17582
17724
|
function getRootKey(absPath) {
|
|
17583
|
-
return
|
|
17725
|
+
return crypto7.createHash("sha256").update(absPath).digest("hex").slice(0, 16);
|
|
17584
17726
|
}
|
|
17585
17727
|
function readSkillPinsSafe() {
|
|
17586
17728
|
const filePath = getPinsFilePath2();
|
|
17587
17729
|
try {
|
|
17588
|
-
const raw =
|
|
17730
|
+
const raw = fs32.readFileSync(filePath, "utf-8");
|
|
17589
17731
|
if (!raw.trim()) return { ok: false, reason: "corrupt", detail: "empty file" };
|
|
17590
17732
|
const parsed = JSON.parse(raw);
|
|
17591
17733
|
if (!parsed.roots || typeof parsed.roots !== "object" || Array.isArray(parsed.roots)) {
|
|
@@ -17605,10 +17747,10 @@ function readSkillPins() {
|
|
|
17605
17747
|
}
|
|
17606
17748
|
function writeSkillPins(data) {
|
|
17607
17749
|
const filePath = getPinsFilePath2();
|
|
17608
|
-
|
|
17609
|
-
const tmp = `${filePath}.${
|
|
17610
|
-
|
|
17611
|
-
|
|
17750
|
+
fs32.mkdirSync(path33.dirname(filePath), { recursive: true });
|
|
17751
|
+
const tmp = `${filePath}.${crypto7.randomBytes(6).toString("hex")}.tmp`;
|
|
17752
|
+
fs32.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
17753
|
+
fs32.renameSync(tmp, filePath);
|
|
17612
17754
|
}
|
|
17613
17755
|
function removePin2(rootKey) {
|
|
17614
17756
|
const pins = readSkillPins();
|
|
@@ -17652,36 +17794,36 @@ function verifyAndPinRoots(roots) {
|
|
|
17652
17794
|
return { kind: "verified" };
|
|
17653
17795
|
}
|
|
17654
17796
|
function defaultSkillRoots(_cwd) {
|
|
17655
|
-
const marketplaces =
|
|
17797
|
+
const marketplaces = path33.join(os28.homedir(), ".claude", "plugins", "marketplaces");
|
|
17656
17798
|
const roots = [];
|
|
17657
17799
|
let registries;
|
|
17658
17800
|
try {
|
|
17659
|
-
registries =
|
|
17801
|
+
registries = fs32.readdirSync(marketplaces, { withFileTypes: true });
|
|
17660
17802
|
} catch {
|
|
17661
17803
|
return [];
|
|
17662
17804
|
}
|
|
17663
17805
|
for (const registry of registries) {
|
|
17664
17806
|
if (!registry.isDirectory()) continue;
|
|
17665
|
-
const pluginsDir =
|
|
17807
|
+
const pluginsDir = path33.join(marketplaces, registry.name, "plugins");
|
|
17666
17808
|
let plugins;
|
|
17667
17809
|
try {
|
|
17668
|
-
plugins =
|
|
17810
|
+
plugins = fs32.readdirSync(pluginsDir, { withFileTypes: true });
|
|
17669
17811
|
} catch {
|
|
17670
17812
|
continue;
|
|
17671
17813
|
}
|
|
17672
17814
|
for (const plugin of plugins) {
|
|
17673
17815
|
if (!plugin.isDirectory()) continue;
|
|
17674
|
-
roots.push(
|
|
17816
|
+
roots.push(path33.join(pluginsDir, plugin.name));
|
|
17675
17817
|
}
|
|
17676
17818
|
}
|
|
17677
17819
|
return roots;
|
|
17678
17820
|
}
|
|
17679
17821
|
function resolveUserSkillRoot(entry, cwd) {
|
|
17680
17822
|
if (!entry) return null;
|
|
17681
|
-
if (entry.startsWith("~/") || entry === "~") return
|
|
17682
|
-
if (
|
|
17683
|
-
if (!cwd || !
|
|
17684
|
-
return
|
|
17823
|
+
if (entry.startsWith("~/") || entry === "~") return path33.join(os28.homedir(), entry.slice(1));
|
|
17824
|
+
if (path33.isAbsolute(entry)) return entry;
|
|
17825
|
+
if (!cwd || !path33.isAbsolute(cwd)) return null;
|
|
17826
|
+
return path33.join(cwd, entry);
|
|
17685
17827
|
}
|
|
17686
17828
|
|
|
17687
17829
|
// src/cli/commands/check.ts
|
|
@@ -17750,9 +17892,9 @@ function registerCheckCommand(program2) {
|
|
|
17750
17892
|
} catch (err2) {
|
|
17751
17893
|
const tempConfig = getConfig();
|
|
17752
17894
|
if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
|
|
17753
|
-
const logPath =
|
|
17895
|
+
const logPath = path34.join(os29.homedir(), ".node9", "hook-debug.log");
|
|
17754
17896
|
const errMsg = err2 instanceof Error ? err2.message : String(err2);
|
|
17755
|
-
|
|
17897
|
+
fs33.appendFileSync(
|
|
17756
17898
|
logPath,
|
|
17757
17899
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
|
|
17758
17900
|
RAW: ${raw}
|
|
@@ -17765,14 +17907,14 @@ RAW: ${raw}
|
|
|
17765
17907
|
const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
|
|
17766
17908
|
if (process.env.NODE9_DEBUG === "1") {
|
|
17767
17909
|
try {
|
|
17768
|
-
const logPath =
|
|
17769
|
-
if (!
|
|
17770
|
-
|
|
17910
|
+
const logPath = path34.join(os29.homedir(), ".node9", "hook-debug.log");
|
|
17911
|
+
if (!fs33.existsSync(path34.dirname(logPath)))
|
|
17912
|
+
fs33.mkdirSync(path34.dirname(logPath), { recursive: true });
|
|
17771
17913
|
const sanitized = JSON.stringify({
|
|
17772
17914
|
...payload,
|
|
17773
17915
|
prompt: `<redacted, ${prompt.length} bytes>`
|
|
17774
17916
|
});
|
|
17775
|
-
|
|
17917
|
+
fs33.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${sanitized}
|
|
17776
17918
|
`);
|
|
17777
17919
|
} catch {
|
|
17778
17920
|
}
|
|
@@ -17792,8 +17934,8 @@ RAW: ${raw}
|
|
|
17792
17934
|
);
|
|
17793
17935
|
const reason = `\u{1F6A8} Node9 DLP: ${dlpMatch.patternName} detected in prompt (${dlpMatch.redactedSample}). Prompt was not submitted \u2014 remove the credential and try again.`;
|
|
17794
17936
|
try {
|
|
17795
|
-
const ttyFd =
|
|
17796
|
-
|
|
17937
|
+
const ttyFd = fs33.openSync("/dev/tty", "w");
|
|
17938
|
+
fs33.writeSync(
|
|
17797
17939
|
ttyFd,
|
|
17798
17940
|
chalk9.bgRed.white.bold(`
|
|
17799
17941
|
\u{1F6A8} NODE9 DLP \u2014 PROMPT BLOCKED
|
|
@@ -17803,7 +17945,7 @@ RAW: ${raw}
|
|
|
17803
17945
|
|
|
17804
17946
|
`)
|
|
17805
17947
|
);
|
|
17806
|
-
|
|
17948
|
+
fs33.closeSync(ttyFd);
|
|
17807
17949
|
} catch {
|
|
17808
17950
|
}
|
|
17809
17951
|
const isCodex = agent2 === "Codex";
|
|
@@ -17822,16 +17964,16 @@ RAW: ${raw}
|
|
|
17822
17964
|
process.exit(2);
|
|
17823
17965
|
}
|
|
17824
17966
|
const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
|
|
17825
|
-
const safeCwdForConfig = typeof payloadCwd === "string" &&
|
|
17967
|
+
const safeCwdForConfig = typeof payloadCwd === "string" && path34.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
17826
17968
|
const config = getConfig(safeCwdForConfig);
|
|
17827
17969
|
if (config.settings.autoStartDaemon && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON) {
|
|
17828
17970
|
try {
|
|
17829
17971
|
const scriptPath = process.argv[1];
|
|
17830
|
-
if (typeof scriptPath !== "string" || !
|
|
17972
|
+
if (typeof scriptPath !== "string" || !path34.isAbsolute(scriptPath))
|
|
17831
17973
|
throw new Error("node9: argv[1] is not an absolute path");
|
|
17832
|
-
const resolvedScript =
|
|
17833
|
-
const packageDist =
|
|
17834
|
-
if (!resolvedScript.startsWith(packageDist +
|
|
17974
|
+
const resolvedScript = fs33.realpathSync(scriptPath);
|
|
17975
|
+
const packageDist = fs33.realpathSync(path34.resolve(__dirname, "../.."));
|
|
17976
|
+
if (!resolvedScript.startsWith(packageDist + path34.sep) && resolvedScript !== packageDist)
|
|
17835
17977
|
throw new Error(
|
|
17836
17978
|
`node9: daemon spawn aborted \u2014 argv[1] (${resolvedScript}) is outside package dist (${packageDist})`
|
|
17837
17979
|
);
|
|
@@ -17853,10 +17995,10 @@ RAW: ${raw}
|
|
|
17853
17995
|
});
|
|
17854
17996
|
d.unref();
|
|
17855
17997
|
} catch (spawnErr) {
|
|
17856
|
-
const logPath =
|
|
17998
|
+
const logPath = path34.join(os29.homedir(), ".node9", "hook-debug.log");
|
|
17857
17999
|
const msg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);
|
|
17858
18000
|
try {
|
|
17859
|
-
|
|
18001
|
+
fs33.appendFileSync(
|
|
17860
18002
|
logPath,
|
|
17861
18003
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-autostart-failed: ${msg}
|
|
17862
18004
|
`
|
|
@@ -17866,10 +18008,10 @@ RAW: ${raw}
|
|
|
17866
18008
|
}
|
|
17867
18009
|
}
|
|
17868
18010
|
if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
|
|
17869
|
-
const logPath =
|
|
17870
|
-
if (!
|
|
17871
|
-
|
|
17872
|
-
|
|
18011
|
+
const logPath = path34.join(os29.homedir(), ".node9", "hook-debug.log");
|
|
18012
|
+
if (!fs33.existsSync(path34.dirname(logPath)))
|
|
18013
|
+
fs33.mkdirSync(path34.dirname(logPath), { recursive: true });
|
|
18014
|
+
fs33.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
|
|
17873
18015
|
`);
|
|
17874
18016
|
}
|
|
17875
18017
|
const rawToolName = sanitize2(extractToolName(payload));
|
|
@@ -17883,8 +18025,8 @@ RAW: ${raw}
|
|
|
17883
18025
|
const isHumanDecision = blockedByContext.toLowerCase().includes("user") || blockedByContext.toLowerCase().includes("daemon") || blockedByContext.toLowerCase().includes("decision");
|
|
17884
18026
|
let ttyFd = null;
|
|
17885
18027
|
try {
|
|
17886
|
-
ttyFd =
|
|
17887
|
-
const writeTty = (line) =>
|
|
18028
|
+
ttyFd = fs33.openSync("/dev/tty", "w");
|
|
18029
|
+
const writeTty = (line) => fs33.writeSync(ttyFd, line + "\n");
|
|
17888
18030
|
if (blockedByContext.includes("DLP") || blockedByContext.includes("Secret Detected") || blockedByContext.includes("Credential Review")) {
|
|
17889
18031
|
writeTty(chalk9.bgRed.white.bold(`
|
|
17890
18032
|
\u{1F6A8} NODE9 DLP ALERT \u2014 CREDENTIAL DETECTED `));
|
|
@@ -17903,7 +18045,7 @@ RAW: ${raw}
|
|
|
17903
18045
|
} finally {
|
|
17904
18046
|
if (ttyFd !== null)
|
|
17905
18047
|
try {
|
|
17906
|
-
|
|
18048
|
+
fs33.closeSync(ttyFd);
|
|
17907
18049
|
} catch {
|
|
17908
18050
|
}
|
|
17909
18051
|
}
|
|
@@ -17954,17 +18096,17 @@ RAW: ${raw}
|
|
|
17954
18096
|
const safeSessionId = /^[A-Za-z0-9_\-]{1,128}$/.test(rawSessionId) ? rawSessionId : "";
|
|
17955
18097
|
if (skillPinCfg.enabled && safeSessionId) {
|
|
17956
18098
|
try {
|
|
17957
|
-
const sessionsDir =
|
|
17958
|
-
const flagPath =
|
|
18099
|
+
const sessionsDir = path34.join(os29.homedir(), ".node9", "skill-sessions");
|
|
18100
|
+
const flagPath = path34.join(sessionsDir, `${safeSessionId}.json`);
|
|
17959
18101
|
let flag = null;
|
|
17960
18102
|
try {
|
|
17961
|
-
flag = JSON.parse(
|
|
18103
|
+
flag = JSON.parse(fs33.readFileSync(flagPath, "utf-8"));
|
|
17962
18104
|
} catch {
|
|
17963
18105
|
}
|
|
17964
18106
|
const writeFlag = (data2) => {
|
|
17965
18107
|
try {
|
|
17966
|
-
|
|
17967
|
-
|
|
18108
|
+
fs33.mkdirSync(sessionsDir, { recursive: true });
|
|
18109
|
+
fs33.writeFileSync(
|
|
17968
18110
|
flagPath,
|
|
17969
18111
|
JSON.stringify({ ...data2, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
|
|
17970
18112
|
{ mode: 384 }
|
|
@@ -17975,8 +18117,8 @@ RAW: ${raw}
|
|
|
17975
18117
|
const sendSkillWarn = (detail, recoveryCmd) => {
|
|
17976
18118
|
let ttyFd = null;
|
|
17977
18119
|
try {
|
|
17978
|
-
ttyFd =
|
|
17979
|
-
const w = (line) =>
|
|
18120
|
+
ttyFd = fs33.openSync("/dev/tty", "w");
|
|
18121
|
+
const w = (line) => fs33.writeSync(ttyFd, line + "\n");
|
|
17980
18122
|
w(chalk9.yellow(`
|
|
17981
18123
|
\u26A0\uFE0F Node9: installed skill drift detected`));
|
|
17982
18124
|
w(chalk9.gray(` ${detail}`));
|
|
@@ -17991,7 +18133,7 @@ RAW: ${raw}
|
|
|
17991
18133
|
} finally {
|
|
17992
18134
|
if (ttyFd !== null)
|
|
17993
18135
|
try {
|
|
17994
|
-
|
|
18136
|
+
fs33.closeSync(ttyFd);
|
|
17995
18137
|
} catch {
|
|
17996
18138
|
}
|
|
17997
18139
|
}
|
|
@@ -18007,7 +18149,7 @@ RAW: ${raw}
|
|
|
18007
18149
|
return;
|
|
18008
18150
|
}
|
|
18009
18151
|
if (!flag || flag.state !== "verified" && flag.state !== "warned") {
|
|
18010
|
-
const absoluteCwd = typeof payloadCwd === "string" &&
|
|
18152
|
+
const absoluteCwd = typeof payloadCwd === "string" && path34.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
18011
18153
|
const extraRoots = skillPinCfg.roots;
|
|
18012
18154
|
const resolvedExtra = extraRoots.map((r) => resolveUserSkillRoot(r, absoluteCwd)).filter((r) => typeof r === "string");
|
|
18013
18155
|
const roots = [...defaultSkillRoots(absoluteCwd), ...resolvedExtra];
|
|
@@ -18048,10 +18190,10 @@ RAW: ${raw}
|
|
|
18048
18190
|
}
|
|
18049
18191
|
try {
|
|
18050
18192
|
const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1e3;
|
|
18051
|
-
for (const name of
|
|
18052
|
-
const p =
|
|
18193
|
+
for (const name of fs33.readdirSync(sessionsDir)) {
|
|
18194
|
+
const p = path34.join(sessionsDir, name);
|
|
18053
18195
|
try {
|
|
18054
|
-
if (
|
|
18196
|
+
if (fs33.statSync(p).mtimeMs < cutoff) fs33.unlinkSync(p);
|
|
18055
18197
|
} catch {
|
|
18056
18198
|
}
|
|
18057
18199
|
}
|
|
@@ -18061,9 +18203,9 @@ RAW: ${raw}
|
|
|
18061
18203
|
} catch (err2) {
|
|
18062
18204
|
if (process.env.NODE9_DEBUG === "1") {
|
|
18063
18205
|
try {
|
|
18064
|
-
const dbg =
|
|
18206
|
+
const dbg = path34.join(os29.homedir(), ".node9", "hook-debug.log");
|
|
18065
18207
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
18066
|
-
|
|
18208
|
+
fs33.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
|
|
18067
18209
|
`);
|
|
18068
18210
|
} catch {
|
|
18069
18211
|
}
|
|
@@ -18073,7 +18215,7 @@ RAW: ${raw}
|
|
|
18073
18215
|
if (shouldSnapshot(toolName, toolInput, config)) {
|
|
18074
18216
|
await createShadowSnapshot(toolName, toolInput, config.policy.snapshot.ignorePaths);
|
|
18075
18217
|
}
|
|
18076
|
-
const safeCwdForAuth = typeof payloadCwd === "string" &&
|
|
18218
|
+
const safeCwdForAuth = typeof payloadCwd === "string" && path34.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
18077
18219
|
const result = await authorizeHeadless(toolName, toolInput, meta, {
|
|
18078
18220
|
cwd: safeCwdForAuth
|
|
18079
18221
|
});
|
|
@@ -18085,12 +18227,12 @@ RAW: ${raw}
|
|
|
18085
18227
|
}
|
|
18086
18228
|
if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && !process.stdout.isTTY && config.settings.autoStartDaemon) {
|
|
18087
18229
|
try {
|
|
18088
|
-
const tty =
|
|
18089
|
-
|
|
18230
|
+
const tty = fs33.openSync("/dev/tty", "w");
|
|
18231
|
+
fs33.writeSync(
|
|
18090
18232
|
tty,
|
|
18091
18233
|
chalk9.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically...\n")
|
|
18092
18234
|
);
|
|
18093
|
-
|
|
18235
|
+
fs33.closeSync(tty);
|
|
18094
18236
|
} catch {
|
|
18095
18237
|
}
|
|
18096
18238
|
const daemonReady = await autoStartDaemonAndWait();
|
|
@@ -18117,9 +18259,9 @@ RAW: ${raw}
|
|
|
18117
18259
|
});
|
|
18118
18260
|
} catch (err2) {
|
|
18119
18261
|
if (process.env.NODE9_DEBUG === "1") {
|
|
18120
|
-
const logPath =
|
|
18262
|
+
const logPath = path34.join(os29.homedir(), ".node9", "hook-debug.log");
|
|
18121
18263
|
const errMsg = err2 instanceof Error ? err2.message : String(err2);
|
|
18122
|
-
|
|
18264
|
+
fs33.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
|
|
18123
18265
|
`);
|
|
18124
18266
|
}
|
|
18125
18267
|
process.exit(0);
|
|
@@ -18155,9 +18297,9 @@ RAW: ${raw}
|
|
|
18155
18297
|
// src/cli/commands/log.ts
|
|
18156
18298
|
init_audit();
|
|
18157
18299
|
init_config();
|
|
18158
|
-
import
|
|
18159
|
-
import
|
|
18160
|
-
import
|
|
18300
|
+
import fs34 from "fs";
|
|
18301
|
+
import path35 from "path";
|
|
18302
|
+
import os30 from "os";
|
|
18161
18303
|
init_daemon();
|
|
18162
18304
|
|
|
18163
18305
|
// src/utils/cp-mv-parser.ts
|
|
@@ -18250,10 +18392,10 @@ function registerLogCommand(program2) {
|
|
|
18250
18392
|
if (rawToolName !== tool) entry.agentToolName = rawToolName;
|
|
18251
18393
|
const payloadSessionId = payload.session_id ?? payload.conversationId;
|
|
18252
18394
|
if (payloadSessionId) entry.sessionId = payloadSessionId;
|
|
18253
|
-
const logPath =
|
|
18254
|
-
if (!
|
|
18255
|
-
|
|
18256
|
-
|
|
18395
|
+
const logPath = path35.join(os30.homedir(), ".node9", "audit.log");
|
|
18396
|
+
if (!fs34.existsSync(path35.dirname(logPath)))
|
|
18397
|
+
fs34.mkdirSync(path35.dirname(logPath), { recursive: true });
|
|
18398
|
+
fs34.appendFileSync(logPath, JSON.stringify(entry) + "\n");
|
|
18257
18399
|
if ((tool === "Bash" || tool === "bash") && isDaemonRunning()) {
|
|
18258
18400
|
const command = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
|
|
18259
18401
|
if (command) {
|
|
@@ -18287,7 +18429,7 @@ function registerLogCommand(program2) {
|
|
|
18287
18429
|
}
|
|
18288
18430
|
}
|
|
18289
18431
|
const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
|
|
18290
|
-
const safeCwd = typeof payloadCwd === "string" &&
|
|
18432
|
+
const safeCwd = typeof payloadCwd === "string" && path35.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
18291
18433
|
const config = getConfig(safeCwd);
|
|
18292
18434
|
if ((tool === "Bash" || tool === "bash") && config.settings.enableUndo !== false) {
|
|
18293
18435
|
const bashCommand = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
|
|
@@ -18308,9 +18450,9 @@ function registerLogCommand(program2) {
|
|
|
18308
18450
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
18309
18451
|
process.stderr.write(`[Node9] audit log error: ${msg}
|
|
18310
18452
|
`);
|
|
18311
|
-
const debugPath =
|
|
18453
|
+
const debugPath = path35.join(os30.homedir(), ".node9", "hook-debug.log");
|
|
18312
18454
|
try {
|
|
18313
|
-
|
|
18455
|
+
fs34.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
|
|
18314
18456
|
`);
|
|
18315
18457
|
} catch {
|
|
18316
18458
|
}
|
|
@@ -18710,14 +18852,15 @@ function registerConfigShowCommand(program2) {
|
|
|
18710
18852
|
|
|
18711
18853
|
// src/cli/commands/doctor.ts
|
|
18712
18854
|
init_daemon();
|
|
18855
|
+
init_config();
|
|
18713
18856
|
import chalk11 from "chalk";
|
|
18714
|
-
import
|
|
18715
|
-
import
|
|
18716
|
-
import
|
|
18857
|
+
import fs35 from "fs";
|
|
18858
|
+
import path36 from "path";
|
|
18859
|
+
import os31 from "os";
|
|
18717
18860
|
import { execSync } from "child_process";
|
|
18718
18861
|
function registerDoctorCommand(program2, version2) {
|
|
18719
|
-
program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(() => {
|
|
18720
|
-
const homeDir2 =
|
|
18862
|
+
program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(async () => {
|
|
18863
|
+
const homeDir2 = os31.homedir();
|
|
18721
18864
|
let failures = 0;
|
|
18722
18865
|
function pass(msg) {
|
|
18723
18866
|
console.log(chalk11.green(" \u2705 ") + msg);
|
|
@@ -18766,10 +18909,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
18766
18909
|
);
|
|
18767
18910
|
}
|
|
18768
18911
|
section("Configuration");
|
|
18769
|
-
const globalConfigPath =
|
|
18770
|
-
if (
|
|
18912
|
+
const globalConfigPath = path36.join(homeDir2, ".node9", "config.json");
|
|
18913
|
+
if (fs35.existsSync(globalConfigPath)) {
|
|
18771
18914
|
try {
|
|
18772
|
-
JSON.parse(
|
|
18915
|
+
JSON.parse(fs35.readFileSync(globalConfigPath, "utf-8"));
|
|
18773
18916
|
pass("~/.node9/config.json found and valid");
|
|
18774
18917
|
} catch {
|
|
18775
18918
|
fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
|
|
@@ -18777,10 +18920,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
18777
18920
|
} else {
|
|
18778
18921
|
warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
|
|
18779
18922
|
}
|
|
18780
|
-
const projectConfigPath =
|
|
18781
|
-
if (
|
|
18923
|
+
const projectConfigPath = path36.join(process.cwd(), "node9.config.json");
|
|
18924
|
+
if (fs35.existsSync(projectConfigPath)) {
|
|
18782
18925
|
try {
|
|
18783
|
-
JSON.parse(
|
|
18926
|
+
JSON.parse(fs35.readFileSync(projectConfigPath, "utf-8"));
|
|
18784
18927
|
pass("node9.config.json found and valid (project)");
|
|
18785
18928
|
} catch {
|
|
18786
18929
|
fail(
|
|
@@ -18789,8 +18932,8 @@ function registerDoctorCommand(program2, version2) {
|
|
|
18789
18932
|
);
|
|
18790
18933
|
}
|
|
18791
18934
|
}
|
|
18792
|
-
const credsPath =
|
|
18793
|
-
if (
|
|
18935
|
+
const credsPath = path36.join(homeDir2, ".node9", "credentials.json");
|
|
18936
|
+
if (fs35.existsSync(credsPath)) {
|
|
18794
18937
|
pass("Cloud credentials found (~/.node9/credentials.json)");
|
|
18795
18938
|
} else {
|
|
18796
18939
|
warn(
|
|
@@ -18799,10 +18942,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
18799
18942
|
);
|
|
18800
18943
|
}
|
|
18801
18944
|
section("Agent Hooks");
|
|
18802
|
-
const claudeSettingsPath =
|
|
18803
|
-
if (
|
|
18945
|
+
const claudeSettingsPath = path36.join(homeDir2, ".claude", "settings.json");
|
|
18946
|
+
if (fs35.existsSync(claudeSettingsPath)) {
|
|
18804
18947
|
try {
|
|
18805
|
-
const cs = JSON.parse(
|
|
18948
|
+
const cs = JSON.parse(fs35.readFileSync(claudeSettingsPath, "utf-8"));
|
|
18806
18949
|
const hasHook = cs.hooks?.PreToolUse?.some(
|
|
18807
18950
|
(m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
|
|
18808
18951
|
);
|
|
@@ -18818,10 +18961,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
18818
18961
|
} else {
|
|
18819
18962
|
warn("Claude Code \u2014 not configured", "Run: node9 setup claude");
|
|
18820
18963
|
}
|
|
18821
|
-
const geminiSettingsPath =
|
|
18822
|
-
if (
|
|
18964
|
+
const geminiSettingsPath = path36.join(homeDir2, ".gemini", "settings.json");
|
|
18965
|
+
if (fs35.existsSync(geminiSettingsPath)) {
|
|
18823
18966
|
try {
|
|
18824
|
-
const gs = JSON.parse(
|
|
18967
|
+
const gs = JSON.parse(fs35.readFileSync(geminiSettingsPath, "utf-8"));
|
|
18825
18968
|
const hasHook = gs.hooks?.BeforeTool?.some(
|
|
18826
18969
|
(m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
|
|
18827
18970
|
);
|
|
@@ -18837,10 +18980,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
18837
18980
|
} else {
|
|
18838
18981
|
warn("Gemini CLI \u2014 not configured", "Run: node9 setup gemini (skip if not using Gemini)");
|
|
18839
18982
|
}
|
|
18840
|
-
const cursorHooksPath =
|
|
18841
|
-
if (
|
|
18983
|
+
const cursorHooksPath = path36.join(homeDir2, ".cursor", "hooks.json");
|
|
18984
|
+
if (fs35.existsSync(cursorHooksPath)) {
|
|
18842
18985
|
try {
|
|
18843
|
-
const cur = JSON.parse(
|
|
18986
|
+
const cur = JSON.parse(fs35.readFileSync(cursorHooksPath, "utf-8"));
|
|
18844
18987
|
const hasHook = cur.hooks?.preToolUse?.some(
|
|
18845
18988
|
(h) => h.command?.includes("node9") || h.command?.includes("cli.js")
|
|
18846
18989
|
);
|
|
@@ -18867,6 +19010,47 @@ function registerDoctorCommand(program2, version2) {
|
|
|
18867
19010
|
"Run: node9 daemon --background"
|
|
18868
19011
|
);
|
|
18869
19012
|
}
|
|
19013
|
+
section("Cloud audit shipping");
|
|
19014
|
+
try {
|
|
19015
|
+
const { shipLagBytes: shipLagBytes2, readWatermark: readWatermark2, AUDIT_SHIP_WATERMARK: AUDIT_SHIP_WATERMARK2 } = await Promise.resolve().then(() => (init_audit_shipper(), audit_shipper_exports));
|
|
19016
|
+
const cfg = getConfig();
|
|
19017
|
+
const creds = fs35.existsSync(path36.join(os31.homedir(), ".node9", "credentials.json"));
|
|
19018
|
+
if (!creds) {
|
|
19019
|
+
warn("Not logged in \u2014 audit rows stay local", "Run: node9 login <api-key>");
|
|
19020
|
+
} else if (!cfg.settings.approvers.cloud) {
|
|
19021
|
+
warn(
|
|
19022
|
+
"Cloud approvals OFF (settings.approvers.cloud=false) \u2014 nothing syncs to the dashboard",
|
|
19023
|
+
"Privacy mode is a valid choice; set approvers.cloud=true to sync."
|
|
19024
|
+
);
|
|
19025
|
+
} else if (cfg.settings.shipper.enabled === false) {
|
|
19026
|
+
warn("Shipper disabled (settings.shipper.enabled=false) \u2014 audit rows stay local");
|
|
19027
|
+
} else {
|
|
19028
|
+
const lag = shipLagBytes2();
|
|
19029
|
+
const wm = readWatermark2(AUDIT_SHIP_WATERMARK2);
|
|
19030
|
+
if (lag === 0) {
|
|
19031
|
+
pass("Audit shipping caught up \u2014 dashboard matches the local log");
|
|
19032
|
+
} else if (wm && lag !== null) {
|
|
19033
|
+
const ageMin = Math.round((Date.now() - new Date(wm.updatedAt).getTime()) / 6e4);
|
|
19034
|
+
if (ageMin > 5 && !isDaemonRunning()) {
|
|
19035
|
+
warn(
|
|
19036
|
+
`${Math.round(lag / 1024)} KB of audit rows not shipped (last ship ${ageMin}m ago)`,
|
|
19037
|
+
"The daemon ships every ~20s \u2014 start it: node9 daemon --background"
|
|
19038
|
+
);
|
|
19039
|
+
} else {
|
|
19040
|
+
pass(
|
|
19041
|
+
`Shipping in progress \u2014 ${Math.round(lag / 1024)} KB queued, last ship ${ageMin}m ago`
|
|
19042
|
+
);
|
|
19043
|
+
}
|
|
19044
|
+
} else {
|
|
19045
|
+
warn(
|
|
19046
|
+
"Shipper has never run on this machine \u2014 dashboard may lag the local log",
|
|
19047
|
+
"Start the daemon: node9 daemon --background"
|
|
19048
|
+
);
|
|
19049
|
+
}
|
|
19050
|
+
}
|
|
19051
|
+
} catch (err2) {
|
|
19052
|
+
warn(`Shipping status unavailable: ${err2.message}`);
|
|
19053
|
+
}
|
|
18870
19054
|
console.log("");
|
|
18871
19055
|
if (failures === 0) {
|
|
18872
19056
|
console.log(chalk11.green.bold(" All checks passed. Node9 is ready.\n"));
|
|
@@ -18880,9 +19064,9 @@ function registerDoctorCommand(program2, version2) {
|
|
|
18880
19064
|
|
|
18881
19065
|
// src/cli/commands/audit.ts
|
|
18882
19066
|
import chalk12 from "chalk";
|
|
18883
|
-
import
|
|
18884
|
-
import
|
|
18885
|
-
import
|
|
19067
|
+
import fs36 from "fs";
|
|
19068
|
+
import path37 from "path";
|
|
19069
|
+
import os32 from "os";
|
|
18886
19070
|
function formatRelativeTime(timestamp) {
|
|
18887
19071
|
const diff = Date.now() - new Date(timestamp).getTime();
|
|
18888
19072
|
const sec = Math.floor(diff / 1e3);
|
|
@@ -18895,14 +19079,14 @@ function formatRelativeTime(timestamp) {
|
|
|
18895
19079
|
}
|
|
18896
19080
|
function registerAuditCommand(program2) {
|
|
18897
19081
|
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) => {
|
|
18898
|
-
const logPath =
|
|
18899
|
-
if (!
|
|
19082
|
+
const logPath = path37.join(os32.homedir(), ".node9", "audit.log");
|
|
19083
|
+
if (!fs36.existsSync(logPath)) {
|
|
18900
19084
|
console.log(
|
|
18901
19085
|
chalk12.yellow("No audit logs found. Run node9 with an agent to generate entries.")
|
|
18902
19086
|
);
|
|
18903
19087
|
return;
|
|
18904
19088
|
}
|
|
18905
|
-
const raw =
|
|
19089
|
+
const raw = fs36.readFileSync(logPath, "utf-8");
|
|
18906
19090
|
const lines = raw.split("\n").filter((l) => l.trim() !== "");
|
|
18907
19091
|
let entries = lines.flatMap((line) => {
|
|
18908
19092
|
try {
|
|
@@ -18960,9 +19144,9 @@ import chalk13 from "chalk";
|
|
|
18960
19144
|
// src/cli/aggregate/report-audit.ts
|
|
18961
19145
|
init_costSync();
|
|
18962
19146
|
init_litellm();
|
|
18963
|
-
import
|
|
18964
|
-
import
|
|
18965
|
-
import
|
|
19147
|
+
import fs37 from "fs";
|
|
19148
|
+
import os33 from "os";
|
|
19149
|
+
import path38 from "path";
|
|
18966
19150
|
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;
|
|
18967
19151
|
function buildTestTimestamps(allEntries) {
|
|
18968
19152
|
const testTs = /* @__PURE__ */ new Set();
|
|
@@ -19042,8 +19226,8 @@ function getDateRange(period, now) {
|
|
|
19042
19226
|
}
|
|
19043
19227
|
}
|
|
19044
19228
|
function parseAuditLog(logPath) {
|
|
19045
|
-
if (!
|
|
19046
|
-
const raw =
|
|
19229
|
+
if (!fs37.existsSync(logPath)) return [];
|
|
19230
|
+
const raw = fs37.readFileSync(logPath, "utf-8");
|
|
19047
19231
|
return raw.split("\n").flatMap((line) => {
|
|
19048
19232
|
if (!line.trim()) return [];
|
|
19049
19233
|
try {
|
|
@@ -19103,25 +19287,25 @@ function freezeClaudeCost(acc) {
|
|
|
19103
19287
|
};
|
|
19104
19288
|
}
|
|
19105
19289
|
function processClaudeCostProject(proj, projectsDir, start, end, acc) {
|
|
19106
|
-
const projPath =
|
|
19290
|
+
const projPath = path38.join(projectsDir, proj);
|
|
19107
19291
|
let files;
|
|
19108
19292
|
try {
|
|
19109
|
-
const stat =
|
|
19293
|
+
const stat = fs37.statSync(projPath);
|
|
19110
19294
|
if (!stat.isDirectory()) return;
|
|
19111
|
-
files =
|
|
19295
|
+
files = fs37.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
19112
19296
|
} catch {
|
|
19113
19297
|
return;
|
|
19114
19298
|
}
|
|
19115
19299
|
const startMs = start.getTime();
|
|
19116
19300
|
for (const file of files) {
|
|
19117
|
-
const filePath =
|
|
19301
|
+
const filePath = path38.join(projPath, file);
|
|
19118
19302
|
try {
|
|
19119
|
-
if (
|
|
19303
|
+
if (fs37.statSync(filePath).mtimeMs < startMs) continue;
|
|
19120
19304
|
} catch {
|
|
19121
19305
|
continue;
|
|
19122
19306
|
}
|
|
19123
19307
|
try {
|
|
19124
|
-
const raw =
|
|
19308
|
+
const raw = fs37.readFileSync(filePath, "utf-8");
|
|
19125
19309
|
for (const line of raw.split("\n")) {
|
|
19126
19310
|
if (!line.trim()) continue;
|
|
19127
19311
|
let entry;
|
|
@@ -19171,10 +19355,10 @@ function processClaudeCostProject(proj, projectsDir, start, end, acc) {
|
|
|
19171
19355
|
}
|
|
19172
19356
|
function loadClaudeCost(start, end, projectsDir) {
|
|
19173
19357
|
const acc = emptyClaudeCostAccumulator();
|
|
19174
|
-
if (!
|
|
19358
|
+
if (!fs37.existsSync(projectsDir)) return freezeClaudeCost(acc);
|
|
19175
19359
|
let dirs;
|
|
19176
19360
|
try {
|
|
19177
|
-
dirs =
|
|
19361
|
+
dirs = fs37.readdirSync(projectsDir);
|
|
19178
19362
|
} catch {
|
|
19179
19363
|
return freezeClaudeCost(acc);
|
|
19180
19364
|
}
|
|
@@ -19186,7 +19370,7 @@ function loadClaudeCost(start, end, projectsDir) {
|
|
|
19186
19370
|
function processCodexCostFile(filePath, start, end, acc) {
|
|
19187
19371
|
let lines;
|
|
19188
19372
|
try {
|
|
19189
|
-
lines =
|
|
19373
|
+
lines = fs37.readFileSync(filePath, "utf-8").split("\n");
|
|
19190
19374
|
} catch {
|
|
19191
19375
|
return;
|
|
19192
19376
|
}
|
|
@@ -19231,31 +19415,31 @@ function processCodexCostFile(filePath, start, end, acc) {
|
|
|
19231
19415
|
}
|
|
19232
19416
|
function listCodexSessionFiles(sessionsBase) {
|
|
19233
19417
|
const jsonlFiles = [];
|
|
19234
|
-
if (!
|
|
19418
|
+
if (!fs37.existsSync(sessionsBase)) return jsonlFiles;
|
|
19235
19419
|
try {
|
|
19236
|
-
for (const year of
|
|
19237
|
-
const yearPath =
|
|
19420
|
+
for (const year of fs37.readdirSync(sessionsBase)) {
|
|
19421
|
+
const yearPath = path38.join(sessionsBase, year);
|
|
19238
19422
|
try {
|
|
19239
|
-
if (!
|
|
19423
|
+
if (!fs37.statSync(yearPath).isDirectory()) continue;
|
|
19240
19424
|
} catch {
|
|
19241
19425
|
continue;
|
|
19242
19426
|
}
|
|
19243
|
-
for (const month of
|
|
19244
|
-
const monthPath =
|
|
19427
|
+
for (const month of fs37.readdirSync(yearPath)) {
|
|
19428
|
+
const monthPath = path38.join(yearPath, month);
|
|
19245
19429
|
try {
|
|
19246
|
-
if (!
|
|
19430
|
+
if (!fs37.statSync(monthPath).isDirectory()) continue;
|
|
19247
19431
|
} catch {
|
|
19248
19432
|
continue;
|
|
19249
19433
|
}
|
|
19250
|
-
for (const day of
|
|
19251
|
-
const dayPath =
|
|
19434
|
+
for (const day of fs37.readdirSync(monthPath)) {
|
|
19435
|
+
const dayPath = path38.join(monthPath, day);
|
|
19252
19436
|
try {
|
|
19253
|
-
if (!
|
|
19437
|
+
if (!fs37.statSync(dayPath).isDirectory()) continue;
|
|
19254
19438
|
} catch {
|
|
19255
19439
|
continue;
|
|
19256
19440
|
}
|
|
19257
|
-
for (const file of
|
|
19258
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(
|
|
19441
|
+
for (const file of fs37.readdirSync(dayPath)) {
|
|
19442
|
+
if (file.endsWith(".jsonl")) jsonlFiles.push(path38.join(dayPath, file));
|
|
19259
19443
|
}
|
|
19260
19444
|
}
|
|
19261
19445
|
}
|
|
@@ -19308,13 +19492,13 @@ function freezeGeminiCost(acc) {
|
|
|
19308
19492
|
function processGeminiCostFile(filePath, projectKey, start, end, acc) {
|
|
19309
19493
|
const startMs = start.getTime();
|
|
19310
19494
|
try {
|
|
19311
|
-
if (
|
|
19495
|
+
if (fs37.statSync(filePath).mtimeMs < startMs) return;
|
|
19312
19496
|
} catch {
|
|
19313
19497
|
return;
|
|
19314
19498
|
}
|
|
19315
19499
|
let raw;
|
|
19316
19500
|
try {
|
|
19317
|
-
raw =
|
|
19501
|
+
raw = fs37.readFileSync(filePath, "utf-8");
|
|
19318
19502
|
} catch {
|
|
19319
19503
|
return;
|
|
19320
19504
|
}
|
|
@@ -19363,30 +19547,30 @@ function listGeminiSessionFiles(geminiTmpDir) {
|
|
|
19363
19547
|
const out = [];
|
|
19364
19548
|
let dirs;
|
|
19365
19549
|
try {
|
|
19366
|
-
if (!
|
|
19367
|
-
dirs =
|
|
19550
|
+
if (!fs37.statSync(geminiTmpDir).isDirectory()) return out;
|
|
19551
|
+
dirs = fs37.readdirSync(geminiTmpDir);
|
|
19368
19552
|
} catch {
|
|
19369
19553
|
return out;
|
|
19370
19554
|
}
|
|
19371
19555
|
for (const proj of dirs) {
|
|
19372
|
-
const chatsDir =
|
|
19556
|
+
const chatsDir = path38.join(geminiTmpDir, proj, "chats");
|
|
19373
19557
|
let files;
|
|
19374
19558
|
try {
|
|
19375
|
-
if (!
|
|
19376
|
-
files =
|
|
19559
|
+
if (!fs37.statSync(chatsDir).isDirectory()) continue;
|
|
19560
|
+
files = fs37.readdirSync(chatsDir);
|
|
19377
19561
|
} catch {
|
|
19378
19562
|
continue;
|
|
19379
19563
|
}
|
|
19380
19564
|
for (const f of files) {
|
|
19381
19565
|
if (!f.endsWith(".jsonl")) continue;
|
|
19382
|
-
out.push({ projectKey: proj, file:
|
|
19566
|
+
out.push({ projectKey: proj, file: path38.join(chatsDir, f) });
|
|
19383
19567
|
}
|
|
19384
19568
|
}
|
|
19385
19569
|
return out;
|
|
19386
19570
|
}
|
|
19387
19571
|
function loadGeminiCost(start, end, geminiTmpDir) {
|
|
19388
19572
|
const acc = emptyGeminiAccumulator();
|
|
19389
|
-
if (!
|
|
19573
|
+
if (!fs37.existsSync(geminiTmpDir)) return freezeGeminiCost(acc);
|
|
19390
19574
|
for (const { projectKey, file } of listGeminiSessionFiles(geminiTmpDir)) {
|
|
19391
19575
|
processGeminiCostFile(file, projectKey, start, end, acc);
|
|
19392
19576
|
}
|
|
@@ -19394,11 +19578,11 @@ function loadGeminiCost(start, end, geminiTmpDir) {
|
|
|
19394
19578
|
}
|
|
19395
19579
|
function aggregateReportFromAudit(period, opts = {}) {
|
|
19396
19580
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
19397
|
-
const auditLogPath = opts.auditLogPath ??
|
|
19398
|
-
const claudeProjectsDir = opts.claudeProjectsDir ??
|
|
19399
|
-
const codexSessionsDir = opts.codexSessionsDir ??
|
|
19400
|
-
const geminiTmpDir = opts.geminiTmpDir ??
|
|
19401
|
-
const hasAuditFile =
|
|
19581
|
+
const auditLogPath = opts.auditLogPath ?? path38.join(os33.homedir(), ".node9", "audit.log");
|
|
19582
|
+
const claudeProjectsDir = opts.claudeProjectsDir ?? path38.join(os33.homedir(), ".claude", "projects");
|
|
19583
|
+
const codexSessionsDir = opts.codexSessionsDir ?? path38.join(os33.homedir(), ".codex", "sessions");
|
|
19584
|
+
const geminiTmpDir = opts.geminiTmpDir ?? path38.join(os33.homedir(), ".gemini", "tmp");
|
|
19585
|
+
const hasAuditFile = fs37.existsSync(auditLogPath);
|
|
19402
19586
|
const allEntries = opts.preloadedAuditEntries ?? parseAuditLog(auditLogPath);
|
|
19403
19587
|
const unackedDlp = allEntries.filter((e) => e.source === "response-dlp");
|
|
19404
19588
|
const { start, end } = getDateRange(period, now);
|
|
@@ -20096,12 +20280,12 @@ function registerDaemonCommand(program2) {
|
|
|
20096
20280
|
init_core();
|
|
20097
20281
|
init_daemon();
|
|
20098
20282
|
import chalk15 from "chalk";
|
|
20099
|
-
import
|
|
20100
|
-
import
|
|
20101
|
-
import
|
|
20283
|
+
import fs38 from "fs";
|
|
20284
|
+
import path39 from "path";
|
|
20285
|
+
import os34 from "os";
|
|
20102
20286
|
function readJson2(filePath) {
|
|
20103
20287
|
try {
|
|
20104
|
-
if (
|
|
20288
|
+
if (fs38.existsSync(filePath)) return JSON.parse(fs38.readFileSync(filePath, "utf-8"));
|
|
20105
20289
|
} catch {
|
|
20106
20290
|
}
|
|
20107
20291
|
return null;
|
|
@@ -20166,28 +20350,28 @@ function registerStatusCommand(program2) {
|
|
|
20166
20350
|
console.log("");
|
|
20167
20351
|
const modeLabel = settings.mode === "audit" ? chalk15.blue("audit") : settings.mode === "strict" ? chalk15.red("strict") : chalk15.white("standard");
|
|
20168
20352
|
console.log(` Mode: ${modeLabel}`);
|
|
20169
|
-
const projectConfig =
|
|
20170
|
-
const globalConfig =
|
|
20353
|
+
const projectConfig = path39.join(process.cwd(), "node9.config.json");
|
|
20354
|
+
const globalConfig = path39.join(os34.homedir(), ".node9", "config.json");
|
|
20171
20355
|
console.log(
|
|
20172
|
-
` Local: ${
|
|
20356
|
+
` Local: ${fs38.existsSync(projectConfig) ? chalk15.green("Active (node9.config.json)") : chalk15.gray("Not present")}`
|
|
20173
20357
|
);
|
|
20174
20358
|
console.log(
|
|
20175
|
-
` Global: ${
|
|
20359
|
+
` Global: ${fs38.existsSync(globalConfig) ? chalk15.green("Active (~/.node9/config.json)") : chalk15.gray("Not present")}`
|
|
20176
20360
|
);
|
|
20177
20361
|
if (mergedConfig.policy.sandboxPaths.length > 0) {
|
|
20178
20362
|
console.log(
|
|
20179
20363
|
` Sandbox: ${chalk15.green(`${mergedConfig.policy.sandboxPaths.length} safe zones active`)}`
|
|
20180
20364
|
);
|
|
20181
20365
|
}
|
|
20182
|
-
const homeDir2 =
|
|
20366
|
+
const homeDir2 = os34.homedir();
|
|
20183
20367
|
const claudeSettings = readJson2(
|
|
20184
|
-
|
|
20368
|
+
path39.join(homeDir2, ".claude", "settings.json")
|
|
20185
20369
|
);
|
|
20186
|
-
const claudeConfig = readJson2(
|
|
20370
|
+
const claudeConfig = readJson2(path39.join(homeDir2, ".claude.json"));
|
|
20187
20371
|
const geminiSettings = readJson2(
|
|
20188
|
-
|
|
20372
|
+
path39.join(homeDir2, ".gemini", "settings.json")
|
|
20189
20373
|
);
|
|
20190
|
-
const cursorConfig = readJson2(
|
|
20374
|
+
const cursorConfig = readJson2(path39.join(homeDir2, ".cursor", "mcp.json"));
|
|
20191
20375
|
const agentFound = claudeSettings || claudeConfig || geminiSettings || cursorConfig;
|
|
20192
20376
|
if (agentFound) {
|
|
20193
20377
|
console.log("");
|
|
@@ -20250,9 +20434,9 @@ init_setup();
|
|
|
20250
20434
|
init_shields();
|
|
20251
20435
|
init_service();
|
|
20252
20436
|
import chalk16 from "chalk";
|
|
20253
|
-
import
|
|
20254
|
-
import
|
|
20255
|
-
import
|
|
20437
|
+
import fs39 from "fs";
|
|
20438
|
+
import path40 from "path";
|
|
20439
|
+
import os35 from "os";
|
|
20256
20440
|
import https4 from "https";
|
|
20257
20441
|
var DEFAULT_SHIELDS = ["bash-safe", "filesystem", "project-jail"];
|
|
20258
20442
|
function buildTelemetryPayload(agents, firstInstall) {
|
|
@@ -20338,16 +20522,16 @@ function registerInitCommand(program2) {
|
|
|
20338
20522
|
}
|
|
20339
20523
|
console.log("");
|
|
20340
20524
|
}
|
|
20341
|
-
const configPath =
|
|
20342
|
-
const isFirstInstall = !
|
|
20343
|
-
if (
|
|
20525
|
+
const configPath = path40.join(os35.homedir(), ".node9", "config.json");
|
|
20526
|
+
const isFirstInstall = !fs39.existsSync(configPath);
|
|
20527
|
+
if (fs39.existsSync(configPath) && !options.force) {
|
|
20344
20528
|
try {
|
|
20345
|
-
const existing = JSON.parse(
|
|
20529
|
+
const existing = JSON.parse(fs39.readFileSync(configPath, "utf-8"));
|
|
20346
20530
|
const settings = existing.settings ?? {};
|
|
20347
20531
|
if (settings.mode !== chosenMode) {
|
|
20348
20532
|
settings.mode = chosenMode;
|
|
20349
20533
|
existing.settings = settings;
|
|
20350
|
-
|
|
20534
|
+
fs39.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
|
|
20351
20535
|
console.log(chalk16.green(`\u2705 Mode updated: ${chosenMode}`));
|
|
20352
20536
|
} else {
|
|
20353
20537
|
console.log(chalk16.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
|
|
@@ -20360,9 +20544,9 @@ function registerInitCommand(program2) {
|
|
|
20360
20544
|
...DEFAULT_CONFIG,
|
|
20361
20545
|
settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
|
|
20362
20546
|
};
|
|
20363
|
-
const dir =
|
|
20364
|
-
if (!
|
|
20365
|
-
|
|
20547
|
+
const dir = path40.dirname(configPath);
|
|
20548
|
+
if (!fs39.existsSync(dir)) fs39.mkdirSync(dir, { recursive: true });
|
|
20549
|
+
fs39.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
|
|
20366
20550
|
console.log(chalk16.green(`\u2705 Config created: ${configPath}`));
|
|
20367
20551
|
console.log(chalk16.gray(` Mode: ${chosenMode}`));
|
|
20368
20552
|
}
|
|
@@ -20467,7 +20651,7 @@ function registerInitCommand(program2) {
|
|
|
20467
20651
|
}
|
|
20468
20652
|
|
|
20469
20653
|
// src/cli/commands/undo.ts
|
|
20470
|
-
import
|
|
20654
|
+
import path41 from "path";
|
|
20471
20655
|
import chalk18 from "chalk";
|
|
20472
20656
|
|
|
20473
20657
|
// src/tui/undo-navigator.ts
|
|
@@ -20626,7 +20810,7 @@ function findMatchingCwd(startDir, history) {
|
|
|
20626
20810
|
let dir = startDir;
|
|
20627
20811
|
while (true) {
|
|
20628
20812
|
if (cwds.has(dir)) return dir;
|
|
20629
|
-
const parent =
|
|
20813
|
+
const parent = path41.dirname(dir);
|
|
20630
20814
|
if (parent === dir) return null;
|
|
20631
20815
|
dir = parent;
|
|
20632
20816
|
}
|
|
@@ -21202,9 +21386,9 @@ function registerMcpGatewayCommand(program2) {
|
|
|
21202
21386
|
|
|
21203
21387
|
// src/mcp-server/index.ts
|
|
21204
21388
|
import readline5 from "readline";
|
|
21205
|
-
import
|
|
21206
|
-
import
|
|
21207
|
-
import
|
|
21389
|
+
import fs40 from "fs";
|
|
21390
|
+
import os36 from "os";
|
|
21391
|
+
import path42 from "path";
|
|
21208
21392
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
21209
21393
|
init_core();
|
|
21210
21394
|
init_daemon();
|
|
@@ -21455,13 +21639,13 @@ function handleStatus() {
|
|
|
21455
21639
|
lines.push(`Active shields: ${activeShields.length > 0 ? activeShields.join(", ") : "none"}`);
|
|
21456
21640
|
lines.push(`Smart rules: ${config.policy.smartRules.length} loaded`);
|
|
21457
21641
|
lines.push(`DLP: ${config.policy.dlp?.enabled !== false ? "enabled" : "disabled"}`);
|
|
21458
|
-
const projectConfig =
|
|
21459
|
-
const globalConfig =
|
|
21642
|
+
const projectConfig = path42.join(process.cwd(), "node9.config.json");
|
|
21643
|
+
const globalConfig = path42.join(os36.homedir(), ".node9", "config.json");
|
|
21460
21644
|
lines.push(
|
|
21461
|
-
`Project config (node9.config.json): ${
|
|
21645
|
+
`Project config (node9.config.json): ${fs40.existsSync(projectConfig) ? "present" : "not found"}`
|
|
21462
21646
|
);
|
|
21463
21647
|
lines.push(
|
|
21464
|
-
`Global config (~/.node9/config.json): ${
|
|
21648
|
+
`Global config (~/.node9/config.json): ${fs40.existsSync(globalConfig) ? "present" : "not found"}`
|
|
21465
21649
|
);
|
|
21466
21650
|
return lines.join("\n");
|
|
21467
21651
|
}
|
|
@@ -21535,21 +21719,21 @@ function handleShieldDisable(args) {
|
|
|
21535
21719
|
writeActiveShields(active.filter((s) => s !== name));
|
|
21536
21720
|
return `Shield "${name}" disabled.`;
|
|
21537
21721
|
}
|
|
21538
|
-
var GLOBAL_CONFIG_PATH =
|
|
21722
|
+
var GLOBAL_CONFIG_PATH = path42.join(os36.homedir(), ".node9", "config.json");
|
|
21539
21723
|
var APPROVER_CHANNELS = ["native", "browser", "cloud", "terminal"];
|
|
21540
21724
|
function readGlobalConfigRaw() {
|
|
21541
21725
|
try {
|
|
21542
|
-
if (
|
|
21543
|
-
return JSON.parse(
|
|
21726
|
+
if (fs40.existsSync(GLOBAL_CONFIG_PATH)) {
|
|
21727
|
+
return JSON.parse(fs40.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
|
|
21544
21728
|
}
|
|
21545
21729
|
} catch {
|
|
21546
21730
|
}
|
|
21547
21731
|
return {};
|
|
21548
21732
|
}
|
|
21549
21733
|
function writeGlobalConfigRaw(data) {
|
|
21550
|
-
const dir =
|
|
21551
|
-
if (!
|
|
21552
|
-
|
|
21734
|
+
const dir = path42.dirname(GLOBAL_CONFIG_PATH);
|
|
21735
|
+
if (!fs40.existsSync(dir)) fs40.mkdirSync(dir, { recursive: true });
|
|
21736
|
+
fs40.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(data, null, 2) + "\n");
|
|
21553
21737
|
}
|
|
21554
21738
|
function handleApproverList() {
|
|
21555
21739
|
const config = getConfig();
|
|
@@ -21593,9 +21777,9 @@ function handleApproverSet(args) {
|
|
|
21593
21777
|
function handleAuditGet(args) {
|
|
21594
21778
|
const limit = Math.min(typeof args.limit === "number" ? args.limit : 20, 100);
|
|
21595
21779
|
const filter = typeof args.filter === "string" && args.filter !== "all" ? args.filter : null;
|
|
21596
|
-
const auditPath =
|
|
21597
|
-
if (!
|
|
21598
|
-
const rawLines =
|
|
21780
|
+
const auditPath = path42.join(os36.homedir(), ".node9", "audit.log");
|
|
21781
|
+
if (!fs40.existsSync(auditPath)) return "No audit log found.";
|
|
21782
|
+
const rawLines = fs40.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
21599
21783
|
const parsed = [];
|
|
21600
21784
|
for (const line of rawLines) {
|
|
21601
21785
|
try {
|
|
@@ -21930,7 +22114,7 @@ function registerTrustCommand(program2) {
|
|
|
21930
22114
|
// src/cli/commands/mcp-pin.ts
|
|
21931
22115
|
init_mcp_pin();
|
|
21932
22116
|
import chalk21 from "chalk";
|
|
21933
|
-
import
|
|
22117
|
+
import fs41 from "fs";
|
|
21934
22118
|
function registerMcpPinCommand(program2) {
|
|
21935
22119
|
const pinCmd = program2.command("mcp").description("Manage MCP server tool definition pinning (rug pull defense)");
|
|
21936
22120
|
const pinSubCmd = pinCmd.command("pin").description("Manage pinned MCP server tool definitions");
|
|
@@ -21941,7 +22125,7 @@ function registerMcpPinCommand(program2) {
|
|
|
21941
22125
|
let repoCorrupt = false;
|
|
21942
22126
|
if (found.source === "repo") {
|
|
21943
22127
|
try {
|
|
21944
|
-
const raw =
|
|
22128
|
+
const raw = fs41.readFileSync(found.path, "utf-8");
|
|
21945
22129
|
const parsed = JSON.parse(raw);
|
|
21946
22130
|
repoEntries = parsed.servers ?? {};
|
|
21947
22131
|
} catch {
|
|
@@ -22255,9 +22439,9 @@ init_scan();
|
|
|
22255
22439
|
// src/cli/commands/sessions.ts
|
|
22256
22440
|
init_scan_summary();
|
|
22257
22441
|
import chalk24 from "chalk";
|
|
22258
|
-
import
|
|
22259
|
-
import
|
|
22260
|
-
import
|
|
22442
|
+
import fs42 from "fs";
|
|
22443
|
+
import path43 from "path";
|
|
22444
|
+
import os37 from "os";
|
|
22261
22445
|
var CLAUDE_PRICING3 = {
|
|
22262
22446
|
"claude-opus-4-6": { i: 5e-6, o: 25e-6, cw: 625e-8, cr: 5e-7 },
|
|
22263
22447
|
"claude-opus-4-5": { i: 5e-6, o: 25e-6, cw: 625e-8, cr: 5e-7 },
|
|
@@ -22298,10 +22482,10 @@ function encodeProjectPath(projectPath) {
|
|
|
22298
22482
|
}
|
|
22299
22483
|
function sessionJsonlPath(projectPath, sessionId) {
|
|
22300
22484
|
const encoded = encodeProjectPath(projectPath);
|
|
22301
|
-
return
|
|
22485
|
+
return path43.join(os37.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
|
|
22302
22486
|
}
|
|
22303
22487
|
function projectLabel(projectPath) {
|
|
22304
|
-
return projectPath.replace(
|
|
22488
|
+
return projectPath.replace(os37.homedir(), "~");
|
|
22305
22489
|
}
|
|
22306
22490
|
function parseHistoryLines(lines) {
|
|
22307
22491
|
const entries = [];
|
|
@@ -22370,10 +22554,10 @@ function parseSessionLines(lines) {
|
|
|
22370
22554
|
return { toolCalls, costUSD, hasSnapshot, modifiedFiles };
|
|
22371
22555
|
}
|
|
22372
22556
|
function loadAuditEntries(auditPath) {
|
|
22373
|
-
const aPath = auditPath ??
|
|
22557
|
+
const aPath = auditPath ?? path43.join(os37.homedir(), ".node9", "audit.log");
|
|
22374
22558
|
let raw;
|
|
22375
22559
|
try {
|
|
22376
|
-
raw =
|
|
22560
|
+
raw = fs42.readFileSync(aPath, "utf-8");
|
|
22377
22561
|
} catch {
|
|
22378
22562
|
return [];
|
|
22379
22563
|
}
|
|
@@ -22409,8 +22593,8 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
|
|
|
22409
22593
|
return result;
|
|
22410
22594
|
}
|
|
22411
22595
|
function buildGeminiSessions(days, allAuditEntries) {
|
|
22412
|
-
const tmpDir =
|
|
22413
|
-
if (!
|
|
22596
|
+
const tmpDir = path43.join(os37.homedir(), ".gemini", "tmp");
|
|
22597
|
+
if (!fs42.existsSync(tmpDir)) return [];
|
|
22414
22598
|
const cutoff = days !== null ? (() => {
|
|
22415
22599
|
const d = /* @__PURE__ */ new Date();
|
|
22416
22600
|
d.setDate(d.getDate() - days);
|
|
@@ -22419,35 +22603,35 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
22419
22603
|
})() : null;
|
|
22420
22604
|
let slugDirs;
|
|
22421
22605
|
try {
|
|
22422
|
-
slugDirs =
|
|
22606
|
+
slugDirs = fs42.readdirSync(tmpDir);
|
|
22423
22607
|
} catch {
|
|
22424
22608
|
return [];
|
|
22425
22609
|
}
|
|
22426
22610
|
const summaries = [];
|
|
22427
22611
|
for (const slug of slugDirs) {
|
|
22428
|
-
const slugPath =
|
|
22612
|
+
const slugPath = path43.join(tmpDir, slug);
|
|
22429
22613
|
try {
|
|
22430
|
-
if (!
|
|
22614
|
+
if (!fs42.statSync(slugPath).isDirectory()) continue;
|
|
22431
22615
|
} catch {
|
|
22432
22616
|
continue;
|
|
22433
22617
|
}
|
|
22434
|
-
let projectRoot =
|
|
22618
|
+
let projectRoot = path43.join(os37.homedir(), slug);
|
|
22435
22619
|
try {
|
|
22436
|
-
projectRoot =
|
|
22620
|
+
projectRoot = fs42.readFileSync(path43.join(slugPath, ".project_root"), "utf-8").trim();
|
|
22437
22621
|
} catch {
|
|
22438
22622
|
}
|
|
22439
|
-
const chatsDir =
|
|
22440
|
-
if (!
|
|
22623
|
+
const chatsDir = path43.join(slugPath, "chats");
|
|
22624
|
+
if (!fs42.existsSync(chatsDir)) continue;
|
|
22441
22625
|
let chatFiles;
|
|
22442
22626
|
try {
|
|
22443
|
-
chatFiles =
|
|
22627
|
+
chatFiles = fs42.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
|
|
22444
22628
|
} catch {
|
|
22445
22629
|
continue;
|
|
22446
22630
|
}
|
|
22447
22631
|
for (const chatFile of chatFiles) {
|
|
22448
22632
|
let raw;
|
|
22449
22633
|
try {
|
|
22450
|
-
raw =
|
|
22634
|
+
raw = fs42.readFileSync(path43.join(chatsDir, chatFile), "utf-8");
|
|
22451
22635
|
} catch {
|
|
22452
22636
|
continue;
|
|
22453
22637
|
}
|
|
@@ -22527,8 +22711,8 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
22527
22711
|
return summaries;
|
|
22528
22712
|
}
|
|
22529
22713
|
function buildCodexSessions(days, allAuditEntries) {
|
|
22530
|
-
const sessionsBase =
|
|
22531
|
-
if (!
|
|
22714
|
+
const sessionsBase = path43.join(os37.homedir(), ".codex", "sessions");
|
|
22715
|
+
if (!fs42.existsSync(sessionsBase)) return [];
|
|
22532
22716
|
const cutoff = days !== null ? (() => {
|
|
22533
22717
|
const d = /* @__PURE__ */ new Date();
|
|
22534
22718
|
d.setDate(d.getDate() - days);
|
|
@@ -22537,29 +22721,29 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
22537
22721
|
})() : null;
|
|
22538
22722
|
const jsonlFiles = [];
|
|
22539
22723
|
try {
|
|
22540
|
-
for (const year of
|
|
22541
|
-
const yearPath =
|
|
22724
|
+
for (const year of fs42.readdirSync(sessionsBase)) {
|
|
22725
|
+
const yearPath = path43.join(sessionsBase, year);
|
|
22542
22726
|
try {
|
|
22543
|
-
if (!
|
|
22727
|
+
if (!fs42.statSync(yearPath).isDirectory()) continue;
|
|
22544
22728
|
} catch {
|
|
22545
22729
|
continue;
|
|
22546
22730
|
}
|
|
22547
|
-
for (const month of
|
|
22548
|
-
const monthPath =
|
|
22731
|
+
for (const month of fs42.readdirSync(yearPath)) {
|
|
22732
|
+
const monthPath = path43.join(yearPath, month);
|
|
22549
22733
|
try {
|
|
22550
|
-
if (!
|
|
22734
|
+
if (!fs42.statSync(monthPath).isDirectory()) continue;
|
|
22551
22735
|
} catch {
|
|
22552
22736
|
continue;
|
|
22553
22737
|
}
|
|
22554
|
-
for (const day of
|
|
22555
|
-
const dayPath =
|
|
22738
|
+
for (const day of fs42.readdirSync(monthPath)) {
|
|
22739
|
+
const dayPath = path43.join(monthPath, day);
|
|
22556
22740
|
try {
|
|
22557
|
-
if (!
|
|
22741
|
+
if (!fs42.statSync(dayPath).isDirectory()) continue;
|
|
22558
22742
|
} catch {
|
|
22559
22743
|
continue;
|
|
22560
22744
|
}
|
|
22561
|
-
for (const file of
|
|
22562
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(
|
|
22745
|
+
for (const file of fs42.readdirSync(dayPath)) {
|
|
22746
|
+
if (file.endsWith(".jsonl")) jsonlFiles.push(path43.join(dayPath, file));
|
|
22563
22747
|
}
|
|
22564
22748
|
}
|
|
22565
22749
|
}
|
|
@@ -22571,7 +22755,7 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
22571
22755
|
for (const filePath of jsonlFiles) {
|
|
22572
22756
|
let lines;
|
|
22573
22757
|
try {
|
|
22574
|
-
lines =
|
|
22758
|
+
lines = fs42.readFileSync(filePath, "utf-8").split("\n");
|
|
22575
22759
|
} catch {
|
|
22576
22760
|
continue;
|
|
22577
22761
|
}
|
|
@@ -22649,10 +22833,10 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
22649
22833
|
return summaries;
|
|
22650
22834
|
}
|
|
22651
22835
|
function buildSessions(days, historyPath) {
|
|
22652
|
-
const hPath = historyPath ??
|
|
22836
|
+
const hPath = historyPath ?? path43.join(os37.homedir(), ".claude", "history.jsonl");
|
|
22653
22837
|
let historyRaw;
|
|
22654
22838
|
try {
|
|
22655
|
-
historyRaw =
|
|
22839
|
+
historyRaw = fs42.readFileSync(hPath, "utf-8");
|
|
22656
22840
|
} catch {
|
|
22657
22841
|
return [];
|
|
22658
22842
|
}
|
|
@@ -22677,7 +22861,7 @@ function buildSessions(days, historyPath) {
|
|
|
22677
22861
|
const jsonlFile = sessionJsonlPath(entry.project, entry.sessionId);
|
|
22678
22862
|
let sessionLines = [];
|
|
22679
22863
|
try {
|
|
22680
|
-
sessionLines =
|
|
22864
|
+
sessionLines = fs42.readFileSync(jsonlFile, "utf-8").split("\n");
|
|
22681
22865
|
} catch {
|
|
22682
22866
|
}
|
|
22683
22867
|
const { toolCalls, costUSD, hasSnapshot, modifiedFiles } = parseSessionLines(sessionLines);
|
|
@@ -22945,8 +23129,8 @@ function registerSessionsCommand(program2) {
|
|
|
22945
23129
|
console.log("");
|
|
22946
23130
|
console.log(chalk24.cyan.bold("\u{1F4CB} node9 sessions") + chalk24.dim(" \u2014 what your AI agent did"));
|
|
22947
23131
|
console.log("");
|
|
22948
|
-
const historyPath =
|
|
22949
|
-
if (!
|
|
23132
|
+
const historyPath = path43.join(os37.homedir(), ".claude", "history.jsonl");
|
|
23133
|
+
if (!fs42.existsSync(historyPath)) {
|
|
22950
23134
|
console.log(chalk24.yellow(" No Claude session history found at ~/.claude/history.jsonl"));
|
|
22951
23135
|
console.log(chalk24.gray(" Install Claude Code, run a few sessions, then try again.\n"));
|
|
22952
23136
|
return;
|
|
@@ -22983,12 +23167,12 @@ function registerSessionsCommand(program2) {
|
|
|
22983
23167
|
|
|
22984
23168
|
// src/cli/commands/skill-pin.ts
|
|
22985
23169
|
import chalk25 from "chalk";
|
|
22986
|
-
import
|
|
22987
|
-
import
|
|
22988
|
-
import
|
|
23170
|
+
import fs43 from "fs";
|
|
23171
|
+
import os38 from "os";
|
|
23172
|
+
import path44 from "path";
|
|
22989
23173
|
function wipeSkillSessions() {
|
|
22990
23174
|
try {
|
|
22991
|
-
|
|
23175
|
+
fs43.rmSync(path44.join(os38.homedir(), ".node9", "skill-sessions"), {
|
|
22992
23176
|
recursive: true,
|
|
22993
23177
|
force: true
|
|
22994
23178
|
});
|
|
@@ -23070,15 +23254,15 @@ function registerSkillPinCommand(program2) {
|
|
|
23070
23254
|
}
|
|
23071
23255
|
|
|
23072
23256
|
// src/cli/commands/decisions.ts
|
|
23073
|
-
import
|
|
23074
|
-
import
|
|
23075
|
-
import
|
|
23257
|
+
import fs44 from "fs";
|
|
23258
|
+
import os39 from "os";
|
|
23259
|
+
import path45 from "path";
|
|
23076
23260
|
import chalk26 from "chalk";
|
|
23077
|
-
var DECISIONS_FILE2 =
|
|
23261
|
+
var DECISIONS_FILE2 = path45.join(os39.homedir(), ".node9", "decisions.json");
|
|
23078
23262
|
function readDecisions() {
|
|
23079
23263
|
try {
|
|
23080
|
-
if (!
|
|
23081
|
-
const raw =
|
|
23264
|
+
if (!fs44.existsSync(DECISIONS_FILE2)) return {};
|
|
23265
|
+
const raw = fs44.readFileSync(DECISIONS_FILE2, "utf-8");
|
|
23082
23266
|
const parsed = JSON.parse(raw);
|
|
23083
23267
|
const out = {};
|
|
23084
23268
|
for (const [k, v] of Object.entries(parsed)) {
|
|
@@ -23090,11 +23274,11 @@ function readDecisions() {
|
|
|
23090
23274
|
}
|
|
23091
23275
|
}
|
|
23092
23276
|
function writeDecisions(d) {
|
|
23093
|
-
const dir =
|
|
23094
|
-
if (!
|
|
23277
|
+
const dir = path45.dirname(DECISIONS_FILE2);
|
|
23278
|
+
if (!fs44.existsSync(dir)) fs44.mkdirSync(dir, { recursive: true });
|
|
23095
23279
|
const tmp = `${DECISIONS_FILE2}.${process.pid}.tmp`;
|
|
23096
|
-
|
|
23097
|
-
|
|
23280
|
+
fs44.writeFileSync(tmp, JSON.stringify(d, null, 2));
|
|
23281
|
+
fs44.renameSync(tmp, DECISIONS_FILE2);
|
|
23098
23282
|
}
|
|
23099
23283
|
function registerDecisionsCommand(program2) {
|
|
23100
23284
|
const cmd = program2.command("decisions").description('Manage persistent "Always Allow" / "Always Deny" tool decisions');
|
|
@@ -23151,18 +23335,18 @@ Persistent decisions (${entries.length})
|
|
|
23151
23335
|
|
|
23152
23336
|
// src/cli/commands/dlp.ts
|
|
23153
23337
|
import chalk27 from "chalk";
|
|
23154
|
-
import
|
|
23155
|
-
import
|
|
23156
|
-
import
|
|
23157
|
-
var AUDIT_LOG =
|
|
23158
|
-
var RESOLVED_FILE =
|
|
23338
|
+
import fs45 from "fs";
|
|
23339
|
+
import path46 from "path";
|
|
23340
|
+
import os40 from "os";
|
|
23341
|
+
var AUDIT_LOG = path46.join(os40.homedir(), ".node9", "audit.log");
|
|
23342
|
+
var RESOLVED_FILE = path46.join(os40.homedir(), ".node9", "dlp-resolved.json");
|
|
23159
23343
|
var ANSI_RE = /\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g;
|
|
23160
23344
|
function stripAnsi(s) {
|
|
23161
23345
|
return s.replace(ANSI_RE, "");
|
|
23162
23346
|
}
|
|
23163
23347
|
function loadResolved() {
|
|
23164
23348
|
try {
|
|
23165
|
-
const raw = JSON.parse(
|
|
23349
|
+
const raw = JSON.parse(fs45.readFileSync(RESOLVED_FILE, "utf-8"));
|
|
23166
23350
|
return new Set(raw);
|
|
23167
23351
|
} catch {
|
|
23168
23352
|
return /* @__PURE__ */ new Set();
|
|
@@ -23170,13 +23354,13 @@ function loadResolved() {
|
|
|
23170
23354
|
}
|
|
23171
23355
|
function saveResolved(resolved) {
|
|
23172
23356
|
try {
|
|
23173
|
-
|
|
23357
|
+
fs45.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
|
|
23174
23358
|
} catch {
|
|
23175
23359
|
}
|
|
23176
23360
|
}
|
|
23177
23361
|
function loadDlpFindings() {
|
|
23178
|
-
if (!
|
|
23179
|
-
return
|
|
23362
|
+
if (!fs45.existsSync(AUDIT_LOG)) return [];
|
|
23363
|
+
return fs45.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
|
|
23180
23364
|
if (!line.trim()) return [];
|
|
23181
23365
|
try {
|
|
23182
23366
|
const e = JSON.parse(line);
|
|
@@ -23275,14 +23459,14 @@ function registerDlpCommand(program2) {
|
|
|
23275
23459
|
// src/cli/commands/mask.ts
|
|
23276
23460
|
init_dlp();
|
|
23277
23461
|
import chalk28 from "chalk";
|
|
23278
|
-
import
|
|
23279
|
-
import
|
|
23280
|
-
import
|
|
23462
|
+
import fs46 from "fs";
|
|
23463
|
+
import path47 from "path";
|
|
23464
|
+
import os41 from "os";
|
|
23281
23465
|
function findJsonlFiles(dir) {
|
|
23282
23466
|
const results = [];
|
|
23283
|
-
if (!
|
|
23284
|
-
for (const entry of
|
|
23285
|
-
const full =
|
|
23467
|
+
if (!fs46.existsSync(dir)) return results;
|
|
23468
|
+
for (const entry of fs46.readdirSync(dir, { withFileTypes: true })) {
|
|
23469
|
+
const full = path47.join(dir, entry.name);
|
|
23286
23470
|
if (entry.isDirectory()) results.push(...findJsonlFiles(full));
|
|
23287
23471
|
else if (entry.isFile() && entry.name.endsWith(".jsonl")) results.push(full);
|
|
23288
23472
|
}
|
|
@@ -23325,7 +23509,7 @@ function redactJson(obj) {
|
|
|
23325
23509
|
function processFile(filePath, dryRun) {
|
|
23326
23510
|
let raw;
|
|
23327
23511
|
try {
|
|
23328
|
-
raw =
|
|
23512
|
+
raw = fs46.readFileSync(filePath, "utf-8");
|
|
23329
23513
|
} catch {
|
|
23330
23514
|
return { redactedLines: 0, patterns: [] };
|
|
23331
23515
|
}
|
|
@@ -23357,14 +23541,14 @@ function processFile(filePath, dryRun) {
|
|
|
23357
23541
|
}
|
|
23358
23542
|
}
|
|
23359
23543
|
if (!dryRun && redactedLines > 0) {
|
|
23360
|
-
|
|
23544
|
+
fs46.writeFileSync(filePath, newLines.join("\n"), "utf-8");
|
|
23361
23545
|
}
|
|
23362
23546
|
return { redactedLines, patterns };
|
|
23363
23547
|
}
|
|
23364
23548
|
function processJsonFile(filePath, dryRun) {
|
|
23365
23549
|
let raw;
|
|
23366
23550
|
try {
|
|
23367
|
-
raw =
|
|
23551
|
+
raw = fs46.readFileSync(filePath, "utf-8");
|
|
23368
23552
|
} catch {
|
|
23369
23553
|
return { redactedLines: 0, patterns: [] };
|
|
23370
23554
|
}
|
|
@@ -23377,15 +23561,15 @@ function processJsonFile(filePath, dryRun) {
|
|
|
23377
23561
|
const { value, modified, found } = redactJson(parsed);
|
|
23378
23562
|
if (!modified) return { redactedLines: 0, patterns: [] };
|
|
23379
23563
|
if (!dryRun) {
|
|
23380
|
-
|
|
23564
|
+
fs46.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
|
|
23381
23565
|
}
|
|
23382
23566
|
return { redactedLines: 1, patterns: found };
|
|
23383
23567
|
}
|
|
23384
23568
|
function findJsonFiles(dir) {
|
|
23385
23569
|
const results = [];
|
|
23386
|
-
if (!
|
|
23387
|
-
for (const entry of
|
|
23388
|
-
const full =
|
|
23570
|
+
if (!fs46.existsSync(dir)) return results;
|
|
23571
|
+
for (const entry of fs46.readdirSync(dir, { withFileTypes: true })) {
|
|
23572
|
+
const full = path47.join(dir, entry.name);
|
|
23389
23573
|
if (entry.isDirectory()) results.push(...findJsonFiles(full));
|
|
23390
23574
|
else if (entry.isFile() && entry.name.endsWith(".json")) results.push(full);
|
|
23391
23575
|
}
|
|
@@ -23394,9 +23578,9 @@ function findJsonFiles(dir) {
|
|
|
23394
23578
|
function registerMaskCommand(program2) {
|
|
23395
23579
|
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) => {
|
|
23396
23580
|
const dryRun = !!options.dryRun;
|
|
23397
|
-
const home =
|
|
23398
|
-
const claudeDir =
|
|
23399
|
-
const geminiDir =
|
|
23581
|
+
const home = os41.homedir();
|
|
23582
|
+
const claudeDir = path47.join(home, ".claude", "projects");
|
|
23583
|
+
const geminiDir = path47.join(home, ".gemini", "tmp");
|
|
23400
23584
|
const allFiles = [
|
|
23401
23585
|
...findJsonlFiles(claudeDir).map((p) => ({ path: p, type: "jsonl" })),
|
|
23402
23586
|
...findJsonFiles(geminiDir).map((p) => ({ path: p, type: "json" }))
|
|
@@ -23404,7 +23588,7 @@ function registerMaskCommand(program2) {
|
|
|
23404
23588
|
const cutoff = options.all ? null : new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
|
|
23405
23589
|
const filtered = cutoff ? allFiles.filter((f) => {
|
|
23406
23590
|
try {
|
|
23407
|
-
return
|
|
23591
|
+
return fs46.statSync(f.path).mtime >= cutoff;
|
|
23408
23592
|
} catch {
|
|
23409
23593
|
return false;
|
|
23410
23594
|
}
|
|
@@ -23460,20 +23644,20 @@ function registerMaskCommand(program2) {
|
|
|
23460
23644
|
// src/cli.ts
|
|
23461
23645
|
init_blast();
|
|
23462
23646
|
var { version } = JSON.parse(
|
|
23463
|
-
|
|
23647
|
+
fs49.readFileSync(path50.join(__dirname, "../package.json"), "utf-8")
|
|
23464
23648
|
);
|
|
23465
23649
|
var program = new Command();
|
|
23466
23650
|
program.name("node9").description("The Sudo Command for AI Agents").version(version);
|
|
23467
23651
|
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) => {
|
|
23468
23652
|
const DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept";
|
|
23469
|
-
const credPath =
|
|
23470
|
-
if (!
|
|
23471
|
-
|
|
23653
|
+
const credPath = path50.join(os44.homedir(), ".node9", "credentials.json");
|
|
23654
|
+
if (!fs49.existsSync(path50.dirname(credPath)))
|
|
23655
|
+
fs49.mkdirSync(path50.dirname(credPath), { recursive: true });
|
|
23472
23656
|
const profileName = options.profile || "default";
|
|
23473
23657
|
let existingCreds = {};
|
|
23474
23658
|
try {
|
|
23475
|
-
if (
|
|
23476
|
-
const raw = JSON.parse(
|
|
23659
|
+
if (fs49.existsSync(credPath)) {
|
|
23660
|
+
const raw = JSON.parse(fs49.readFileSync(credPath, "utf-8"));
|
|
23477
23661
|
if (raw.apiKey) {
|
|
23478
23662
|
existingCreds = {
|
|
23479
23663
|
default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL2 }
|
|
@@ -23485,13 +23669,14 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
23485
23669
|
} catch {
|
|
23486
23670
|
}
|
|
23487
23671
|
existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL2 };
|
|
23488
|
-
|
|
23672
|
+
fs49.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
|
|
23673
|
+
let effectiveCloud = null;
|
|
23489
23674
|
if (profileName === "default") {
|
|
23490
|
-
const configPath =
|
|
23675
|
+
const configPath = path50.join(os44.homedir(), ".node9", "config.json");
|
|
23491
23676
|
let config = {};
|
|
23492
23677
|
try {
|
|
23493
|
-
if (
|
|
23494
|
-
config = JSON.parse(
|
|
23678
|
+
if (fs49.existsSync(configPath))
|
|
23679
|
+
config = JSON.parse(fs49.readFileSync(configPath, "utf-8"));
|
|
23495
23680
|
} catch {
|
|
23496
23681
|
}
|
|
23497
23682
|
if (!config.settings || typeof config.settings !== "object") config.settings = {};
|
|
@@ -23506,16 +23691,25 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
23506
23691
|
approvers.cloud = false;
|
|
23507
23692
|
}
|
|
23508
23693
|
s.approvers = approvers;
|
|
23509
|
-
if (!
|
|
23510
|
-
|
|
23511
|
-
|
|
23694
|
+
if (!fs49.existsSync(path50.dirname(configPath)))
|
|
23695
|
+
fs49.mkdirSync(path50.dirname(configPath), { recursive: true });
|
|
23696
|
+
fs49.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
23697
|
+
effectiveCloud = approvers.cloud === true;
|
|
23512
23698
|
}
|
|
23513
23699
|
if (options.profile && profileName !== "default") {
|
|
23514
23700
|
console.log(chalk30.green(`\u2705 Profile "${profileName}" saved`));
|
|
23515
23701
|
console.log(chalk30.gray(` Switch to it per-session: NODE9_PROFILE=${profileName} claude`));
|
|
23516
|
-
} else if (options.local) {
|
|
23517
|
-
console.log(chalk30.green(`\u2705 Privacy mode \u{1F6E1}\uFE0F`));
|
|
23518
|
-
console.log(chalk30.gray(` All decisions stay on this machine.`));
|
|
23702
|
+
} else if (options.local || effectiveCloud === false) {
|
|
23703
|
+
console.log(chalk30.green(`\u2705 Key saved \u2014 Privacy mode \u{1F6E1}\uFE0F`));
|
|
23704
|
+
console.log(chalk30.gray(` All decisions stay on this machine. Nothing syncs to the cloud.`));
|
|
23705
|
+
if (!options.local) {
|
|
23706
|
+
console.log(
|
|
23707
|
+
chalk30.yellow(` Your config has cloud approvals OFF (settings.approvers.cloud).`)
|
|
23708
|
+
);
|
|
23709
|
+
console.log(
|
|
23710
|
+
chalk30.gray(` To enable team policy + dashboard sync: set it to true, or re-init.`)
|
|
23711
|
+
);
|
|
23712
|
+
}
|
|
23519
23713
|
} else {
|
|
23520
23714
|
console.log(chalk30.green(`\u2705 Logged in \u2014 agent mode`));
|
|
23521
23715
|
console.log(chalk30.gray(` Team policy enforced for all calls via Node9 cloud.`));
|
|
@@ -23658,15 +23852,15 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
23658
23852
|
}
|
|
23659
23853
|
}
|
|
23660
23854
|
if (options.purge) {
|
|
23661
|
-
const node9Dir =
|
|
23662
|
-
if (
|
|
23855
|
+
const node9Dir = path50.join(os44.homedir(), ".node9");
|
|
23856
|
+
if (fs49.existsSync(node9Dir)) {
|
|
23663
23857
|
const confirmed = await confirm2({
|
|
23664
23858
|
message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
|
|
23665
23859
|
default: false
|
|
23666
23860
|
});
|
|
23667
23861
|
if (confirmed) {
|
|
23668
|
-
|
|
23669
|
-
if (
|
|
23862
|
+
fs49.rmSync(node9Dir, { recursive: true });
|
|
23863
|
+
if (fs49.existsSync(node9Dir)) {
|
|
23670
23864
|
console.error(
|
|
23671
23865
|
chalk30.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
|
|
23672
23866
|
);
|
|
@@ -23781,7 +23975,7 @@ program.command("tail").description("Stream live agent activity to the terminal"
|
|
|
23781
23975
|
});
|
|
23782
23976
|
program.command("monitor").description("Live interactive dashboard \u2014 activity feed, approvals, security signals").action(async () => {
|
|
23783
23977
|
try {
|
|
23784
|
-
const dashboardPath =
|
|
23978
|
+
const dashboardPath = path50.join(__dirname, "dashboard.mjs");
|
|
23785
23979
|
const dynamicImport = new Function("id", "return import(id)");
|
|
23786
23980
|
const mod = await dynamicImport(`file://${dashboardPath}`);
|
|
23787
23981
|
await mod.startMonitor();
|
|
@@ -23819,14 +24013,14 @@ Claude Code spawns this command every ~300ms and writes a JSON payload to stdin.
|
|
|
23819
24013
|
Run "node9 addto claude" to register it as the statusLine.`
|
|
23820
24014
|
).argument("[subcommand]", 'Optional: "debug on" / "debug off" to toggle stdin logging').argument("[state]", 'on|off \u2014 used with "debug" subcommand').action(async (subcommand, state) => {
|
|
23821
24015
|
if (subcommand === "debug") {
|
|
23822
|
-
const flagFile =
|
|
24016
|
+
const flagFile = path50.join(os44.homedir(), ".node9", "hud-debug");
|
|
23823
24017
|
if (state === "on") {
|
|
23824
|
-
|
|
23825
|
-
|
|
24018
|
+
fs49.mkdirSync(path50.dirname(flagFile), { recursive: true });
|
|
24019
|
+
fs49.writeFileSync(flagFile, "");
|
|
23826
24020
|
console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
|
|
23827
24021
|
console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
|
|
23828
24022
|
} else if (state === "off") {
|
|
23829
|
-
if (
|
|
24023
|
+
if (fs49.existsSync(flagFile)) fs49.unlinkSync(flagFile);
|
|
23830
24024
|
console.log("HUD debug logging disabled.");
|
|
23831
24025
|
} else {
|
|
23832
24026
|
console.error("Usage: node9 hud debug on|off");
|
|
@@ -23943,9 +24137,9 @@ if (process.argv[2] !== "daemon") {
|
|
|
23943
24137
|
const isCheckHook = process.argv[2] === "check";
|
|
23944
24138
|
if (isCheckHook) {
|
|
23945
24139
|
if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
|
|
23946
|
-
const logPath =
|
|
24140
|
+
const logPath = path50.join(os44.homedir(), ".node9", "hook-debug.log");
|
|
23947
24141
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
23948
|
-
|
|
24142
|
+
fs49.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
|
|
23949
24143
|
`);
|
|
23950
24144
|
}
|
|
23951
24145
|
process.exit(0);
|