@node9/proxy 1.10.0 → 1.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +507 -273
- package/dist/cli.mjs +503 -269
- package/dist/index.js +31 -15
- package/dist/index.mjs +31 -15
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -168,8 +168,8 @@ function sanitizeConfig(raw) {
|
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
const lines = result.error.issues.map((issue) => {
|
|
171
|
-
const
|
|
172
|
-
return ` \u2022 ${
|
|
171
|
+
const path35 = issue.path.length > 0 ? issue.path.join(".") : "root";
|
|
172
|
+
return ` \u2022 ${path35}: ${issue.message}`;
|
|
173
173
|
});
|
|
174
174
|
return {
|
|
175
175
|
sanitized,
|
|
@@ -253,7 +253,8 @@ var init_config_schema = __esm({
|
|
|
253
253
|
slackEnabled: import_zod.z.boolean().optional(),
|
|
254
254
|
enableTrustSessions: import_zod.z.boolean().optional(),
|
|
255
255
|
allowGlobalPause: import_zod.z.boolean().optional(),
|
|
256
|
-
auditHashArgs: import_zod.z.boolean().optional()
|
|
256
|
+
auditHashArgs: import_zod.z.boolean().optional(),
|
|
257
|
+
agentPolicy: import_zod.z.enum(["require_approval", "block_on_rules"]).optional()
|
|
257
258
|
}).optional(),
|
|
258
259
|
policy: import_zod.z.object({
|
|
259
260
|
sandboxPaths: import_zod.z.array(import_zod.z.string()).optional(),
|
|
@@ -1726,9 +1727,9 @@ function matchesPattern(text, patterns) {
|
|
|
1726
1727
|
const withoutDotSlash = text.replace(/^\.\//, "");
|
|
1727
1728
|
return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
|
|
1728
1729
|
}
|
|
1729
|
-
function getNestedValue(obj,
|
|
1730
|
+
function getNestedValue(obj, path35) {
|
|
1730
1731
|
if (!obj || typeof obj !== "object") return null;
|
|
1731
|
-
return
|
|
1732
|
+
return path35.split(".").reduce((prev, curr) => prev?.[curr], obj);
|
|
1732
1733
|
}
|
|
1733
1734
|
function shouldSnapshot(toolName, args, config) {
|
|
1734
1735
|
if (!config.settings.enableUndo) return false;
|
|
@@ -2811,11 +2812,12 @@ ${smartTruncate(str, 500)}`
|
|
|
2811
2812
|
function escapePango(text) {
|
|
2812
2813
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
2813
2814
|
}
|
|
2814
|
-
function buildPlainMessage(toolName, formattedArgs, agent, explainableLabel, locked, allowCount = 1) {
|
|
2815
|
+
function buildPlainMessage(toolName, formattedArgs, agent, explainableLabel, locked, allowCount = 1, ruleDescription) {
|
|
2815
2816
|
const lines = [];
|
|
2816
2817
|
if (locked) lines.push("\u26A0\uFE0F LOCKED BY ADMIN POLICY\n");
|
|
2817
2818
|
lines.push(`\u{1F916} ${agent || "AI Agent"} | \u{1F527} ${toolName}`);
|
|
2818
2819
|
lines.push(`\u{1F6E1}\uFE0F ${explainableLabel || "Security Policy"}`);
|
|
2820
|
+
if (ruleDescription) lines.push(`\u2139 ${ruleDescription}`);
|
|
2819
2821
|
lines.push("");
|
|
2820
2822
|
lines.push(formattedArgs);
|
|
2821
2823
|
if (allowCount >= 3) {
|
|
@@ -2828,7 +2830,7 @@ function buildPlainMessage(toolName, formattedArgs, agent, explainableLabel, loc
|
|
|
2828
2830
|
}
|
|
2829
2831
|
return lines.join("\n");
|
|
2830
2832
|
}
|
|
2831
|
-
function buildPangoMessage(toolName, formattedArgs, agent, explainableLabel, locked, allowCount = 1) {
|
|
2833
|
+
function buildPangoMessage(toolName, formattedArgs, agent, explainableLabel, locked, allowCount = 1, ruleDescription) {
|
|
2832
2834
|
const lines = [];
|
|
2833
2835
|
if (locked) {
|
|
2834
2836
|
lines.push('<span foreground="red" weight="bold">\u26A0\uFE0F LOCKED BY ADMIN POLICY</span>');
|
|
@@ -2838,6 +2840,7 @@ function buildPangoMessage(toolName, formattedArgs, agent, explainableLabel, loc
|
|
|
2838
2840
|
`<b>\u{1F916} ${escapePango(agent || "AI Agent")}</b> | <b>\u{1F527} <tt>${escapePango(toolName)}</tt></b>`
|
|
2839
2841
|
);
|
|
2840
2842
|
lines.push(`<i>\u{1F6E1}\uFE0F ${escapePango(explainableLabel || "Security Policy")}</i>`);
|
|
2843
|
+
if (ruleDescription) lines.push(`<i>\u2139 ${escapePango(ruleDescription)}</i>`);
|
|
2841
2844
|
lines.push("");
|
|
2842
2845
|
lines.push(`<tt>${escapePango(formattedArgs)}</tt>`);
|
|
2843
2846
|
if (allowCount >= 3) {
|
|
@@ -2854,7 +2857,7 @@ function buildPangoMessage(toolName, formattedArgs, agent, explainableLabel, loc
|
|
|
2854
2857
|
}
|
|
2855
2858
|
return lines.join("\n");
|
|
2856
2859
|
}
|
|
2857
|
-
async function askNativePopup(toolName, args, agent, explainableLabel, locked = false, signal, matchedField, matchedWord, allowCount = 1) {
|
|
2860
|
+
async function askNativePopup(toolName, args, agent, explainableLabel, locked = false, signal, matchedField, matchedWord, allowCount = 1, ruleDescription) {
|
|
2858
2861
|
if (isTestEnv()) return "deny";
|
|
2859
2862
|
const { message: formattedArgs, intent } = formatArgs(args, matchedField, matchedWord);
|
|
2860
2863
|
const intentLabel = intent === "EDIT" ? "Code Edit" : "Action Approval";
|
|
@@ -2865,7 +2868,8 @@ async function askNativePopup(toolName, args, agent, explainableLabel, locked =
|
|
|
2865
2868
|
agent,
|
|
2866
2869
|
explainableLabel,
|
|
2867
2870
|
locked,
|
|
2868
|
-
allowCount
|
|
2871
|
+
allowCount,
|
|
2872
|
+
ruleDescription
|
|
2869
2873
|
);
|
|
2870
2874
|
return new Promise((resolve) => {
|
|
2871
2875
|
let childProcess = null;
|
|
@@ -2899,7 +2903,8 @@ end run`;
|
|
|
2899
2903
|
agent,
|
|
2900
2904
|
explainableLabel,
|
|
2901
2905
|
locked,
|
|
2902
|
-
allowCount
|
|
2906
|
+
allowCount,
|
|
2907
|
+
ruleDescription
|
|
2903
2908
|
);
|
|
2904
2909
|
const argsList = [
|
|
2905
2910
|
locked ? "--info" : "--question",
|
|
@@ -2972,7 +2977,7 @@ function auditLocalAllow(toolName, args, checkedBy, creds, meta) {
|
|
|
2972
2977
|
}).catch(() => {
|
|
2973
2978
|
});
|
|
2974
2979
|
}
|
|
2975
|
-
async function initNode9SaaS(toolName, args, creds, meta, riskMetadata) {
|
|
2980
|
+
async function initNode9SaaS(toolName, args, creds, meta, riskMetadata, agentPolicy, forceReview) {
|
|
2976
2981
|
const controller = new AbortController();
|
|
2977
2982
|
const timeout = setTimeout(() => controller.abort(), 1e4);
|
|
2978
2983
|
if (!creds.apiKey) throw new Error("Node9 API Key is missing");
|
|
@@ -3017,7 +3022,9 @@ async function initNode9SaaS(toolName, args, creds, meta, riskMetadata) {
|
|
|
3017
3022
|
platform: import_os9.default.platform()
|
|
3018
3023
|
},
|
|
3019
3024
|
...riskMetadata && { riskMetadata },
|
|
3020
|
-
...ciContext && { ciContext }
|
|
3025
|
+
...ciContext && { ciContext },
|
|
3026
|
+
...agentPolicy && { policy: agentPolicy },
|
|
3027
|
+
...forceReview && { forceReview: true }
|
|
3021
3028
|
}),
|
|
3022
3029
|
signal: controller.signal
|
|
3023
3030
|
});
|
|
@@ -3414,6 +3421,7 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
3414
3421
|
policyMatchedWord,
|
|
3415
3422
|
policyResult.ruleName
|
|
3416
3423
|
);
|
|
3424
|
+
if (policyRuleDescription) riskMetadata.ruleDescription = policyRuleDescription.slice(0, 200);
|
|
3417
3425
|
const persistent = policyResult.ruleName ? null : getPersistentDecision(toolName);
|
|
3418
3426
|
if (persistent === "allow") {
|
|
3419
3427
|
if (approvers.cloud && creds?.apiKey)
|
|
@@ -3448,9 +3456,18 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
3448
3456
|
}
|
|
3449
3457
|
let cloudRequestId = null;
|
|
3450
3458
|
const cloudEnforced = approvers.cloud && !!creds?.apiKey;
|
|
3451
|
-
|
|
3459
|
+
const forceReview = localSmartRuleMatched === true || options?.localSmartRuleMatched === true || void 0;
|
|
3460
|
+
if (cloudEnforced) {
|
|
3452
3461
|
try {
|
|
3453
|
-
const initResult = await initNode9SaaS(
|
|
3462
|
+
const initResult = await initNode9SaaS(
|
|
3463
|
+
toolName,
|
|
3464
|
+
args,
|
|
3465
|
+
creds,
|
|
3466
|
+
meta,
|
|
3467
|
+
riskMetadata,
|
|
3468
|
+
config.settings.agentPolicy,
|
|
3469
|
+
forceReview
|
|
3470
|
+
);
|
|
3454
3471
|
if (!initResult.pending) {
|
|
3455
3472
|
if (initResult.shadowMode) {
|
|
3456
3473
|
return { approved: true, checkedBy: "cloud" };
|
|
@@ -3465,9 +3482,7 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
3465
3482
|
};
|
|
3466
3483
|
}
|
|
3467
3484
|
}
|
|
3468
|
-
if (
|
|
3469
|
-
cloudRequestId = initResult.requestId || null;
|
|
3470
|
-
}
|
|
3485
|
+
if (initResult.pending) cloudRequestId = initResult.requestId || null;
|
|
3471
3486
|
if (!taintWarning) explainableLabel = "Organization Policy (SaaS)";
|
|
3472
3487
|
} catch {
|
|
3473
3488
|
}
|
|
@@ -3524,7 +3539,7 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
3524
3539
|
}
|
|
3525
3540
|
}
|
|
3526
3541
|
}
|
|
3527
|
-
if (cloudEnforced && cloudRequestId
|
|
3542
|
+
if (cloudEnforced && cloudRequestId) {
|
|
3528
3543
|
racePromises.push(
|
|
3529
3544
|
(async () => {
|
|
3530
3545
|
try {
|
|
@@ -3556,7 +3571,8 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
3556
3571
|
signal,
|
|
3557
3572
|
policyMatchedField,
|
|
3558
3573
|
policyMatchedWord,
|
|
3559
|
-
daemonAllowCount
|
|
3574
|
+
daemonAllowCount,
|
|
3575
|
+
riskMetadata?.ruleDescription
|
|
3560
3576
|
);
|
|
3561
3577
|
if (decision === "always_allow") {
|
|
3562
3578
|
writeTrustSession(toolName, 36e5);
|
|
@@ -6059,8 +6075,175 @@ var init_patch = __esm({
|
|
|
6059
6075
|
}
|
|
6060
6076
|
});
|
|
6061
6077
|
|
|
6078
|
+
// src/costSync.ts
|
|
6079
|
+
function normalizeModel(raw) {
|
|
6080
|
+
return raw.replace(/-\d{8}$/, "");
|
|
6081
|
+
}
|
|
6082
|
+
function pricingFor(model) {
|
|
6083
|
+
const norm = normalizeModel(model);
|
|
6084
|
+
if (PRICING[norm]) return PRICING[norm];
|
|
6085
|
+
let best = null;
|
|
6086
|
+
for (const key of Object.keys(PRICING)) {
|
|
6087
|
+
if (norm.startsWith(key) && (best === null || key.length > best.length)) best = key;
|
|
6088
|
+
}
|
|
6089
|
+
return best ? PRICING[best] : null;
|
|
6090
|
+
}
|
|
6091
|
+
function parseJSONLFile(filePath) {
|
|
6092
|
+
let content;
|
|
6093
|
+
try {
|
|
6094
|
+
content = import_fs16.default.readFileSync(filePath, "utf8");
|
|
6095
|
+
} catch {
|
|
6096
|
+
return /* @__PURE__ */ new Map();
|
|
6097
|
+
}
|
|
6098
|
+
const daily = /* @__PURE__ */ new Map();
|
|
6099
|
+
for (const line of content.split("\n")) {
|
|
6100
|
+
if (!line.trim()) continue;
|
|
6101
|
+
let row;
|
|
6102
|
+
try {
|
|
6103
|
+
row = JSON.parse(line);
|
|
6104
|
+
} catch {
|
|
6105
|
+
continue;
|
|
6106
|
+
}
|
|
6107
|
+
if (row["type"] !== "assistant") continue;
|
|
6108
|
+
const msg = row["message"];
|
|
6109
|
+
if (!msg?.["usage"] || typeof msg["model"] !== "string") continue;
|
|
6110
|
+
const usage = msg["usage"];
|
|
6111
|
+
const model = msg["model"];
|
|
6112
|
+
const timestamp = row["timestamp"];
|
|
6113
|
+
if (typeof timestamp !== "string" || timestamp.length < 10) continue;
|
|
6114
|
+
const date = timestamp.slice(0, 10);
|
|
6115
|
+
const p = pricingFor(model);
|
|
6116
|
+
if (!p) continue;
|
|
6117
|
+
const inp = Number(usage["input_tokens"] ?? 0);
|
|
6118
|
+
const out = Number(usage["output_tokens"] ?? 0);
|
|
6119
|
+
const cw = Number(usage["cache_creation_input_tokens"] ?? 0);
|
|
6120
|
+
const cr = Number(usage["cache_read_input_tokens"] ?? 0);
|
|
6121
|
+
const cost = inp * p[0] + out * p[1] + cw * p[2] + cr * p[3];
|
|
6122
|
+
const norm = normalizeModel(model);
|
|
6123
|
+
const key = `${date}::${norm}`;
|
|
6124
|
+
const prev = daily.get(key);
|
|
6125
|
+
if (prev) {
|
|
6126
|
+
prev.costUSD += cost;
|
|
6127
|
+
prev.inputTokens += inp;
|
|
6128
|
+
prev.outputTokens += out;
|
|
6129
|
+
prev.cacheWriteTokens += cw;
|
|
6130
|
+
prev.cacheReadTokens += cr;
|
|
6131
|
+
} else {
|
|
6132
|
+
daily.set(key, {
|
|
6133
|
+
date,
|
|
6134
|
+
model: norm,
|
|
6135
|
+
costUSD: cost,
|
|
6136
|
+
inputTokens: inp,
|
|
6137
|
+
outputTokens: out,
|
|
6138
|
+
cacheWriteTokens: cw,
|
|
6139
|
+
cacheReadTokens: cr
|
|
6140
|
+
});
|
|
6141
|
+
}
|
|
6142
|
+
}
|
|
6143
|
+
return daily;
|
|
6144
|
+
}
|
|
6145
|
+
function collectEntries() {
|
|
6146
|
+
const projectsDir = import_path19.default.join(import_os14.default.homedir(), ".claude", "projects");
|
|
6147
|
+
if (!import_fs16.default.existsSync(projectsDir)) return [];
|
|
6148
|
+
const combined = /* @__PURE__ */ new Map();
|
|
6149
|
+
let dirs;
|
|
6150
|
+
try {
|
|
6151
|
+
dirs = import_fs16.default.readdirSync(projectsDir);
|
|
6152
|
+
} catch {
|
|
6153
|
+
return [];
|
|
6154
|
+
}
|
|
6155
|
+
for (const dir of dirs) {
|
|
6156
|
+
const dirPath = import_path19.default.join(projectsDir, dir);
|
|
6157
|
+
try {
|
|
6158
|
+
if (!import_fs16.default.statSync(dirPath).isDirectory()) continue;
|
|
6159
|
+
} catch {
|
|
6160
|
+
continue;
|
|
6161
|
+
}
|
|
6162
|
+
let files;
|
|
6163
|
+
try {
|
|
6164
|
+
files = import_fs16.default.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
|
|
6165
|
+
} catch {
|
|
6166
|
+
continue;
|
|
6167
|
+
}
|
|
6168
|
+
for (const file of files) {
|
|
6169
|
+
const entries = parseJSONLFile(import_path19.default.join(dirPath, file));
|
|
6170
|
+
for (const [key, e] of entries) {
|
|
6171
|
+
const prev = combined.get(key);
|
|
6172
|
+
if (prev) {
|
|
6173
|
+
prev.costUSD += e.costUSD;
|
|
6174
|
+
prev.inputTokens += e.inputTokens;
|
|
6175
|
+
prev.outputTokens += e.outputTokens;
|
|
6176
|
+
prev.cacheWriteTokens += e.cacheWriteTokens;
|
|
6177
|
+
prev.cacheReadTokens += e.cacheReadTokens;
|
|
6178
|
+
} else {
|
|
6179
|
+
combined.set(key, { ...e });
|
|
6180
|
+
}
|
|
6181
|
+
}
|
|
6182
|
+
}
|
|
6183
|
+
}
|
|
6184
|
+
return [...combined.values()];
|
|
6185
|
+
}
|
|
6186
|
+
async function syncCost() {
|
|
6187
|
+
const creds = getCredentials();
|
|
6188
|
+
if (!creds?.apiKey || !creds?.apiUrl) return;
|
|
6189
|
+
const entries = collectEntries();
|
|
6190
|
+
if (entries.length === 0) return;
|
|
6191
|
+
let username = "unknown";
|
|
6192
|
+
try {
|
|
6193
|
+
username = import_os14.default.userInfo().username;
|
|
6194
|
+
} catch {
|
|
6195
|
+
}
|
|
6196
|
+
const machineId = `${import_os14.default.hostname()}:${username}`;
|
|
6197
|
+
try {
|
|
6198
|
+
const res = await fetch(`${creds.apiUrl}/cost-sync`, {
|
|
6199
|
+
method: "POST",
|
|
6200
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${creds.apiKey}` },
|
|
6201
|
+
body: JSON.stringify({ machineId, entries }),
|
|
6202
|
+
signal: AbortSignal.timeout(15e3)
|
|
6203
|
+
});
|
|
6204
|
+
if (!res.ok) {
|
|
6205
|
+
import_fs16.default.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
|
|
6206
|
+
`);
|
|
6207
|
+
}
|
|
6208
|
+
} catch (err2) {
|
|
6209
|
+
import_fs16.default.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
|
|
6210
|
+
`);
|
|
6211
|
+
}
|
|
6212
|
+
}
|
|
6213
|
+
function startCostSync() {
|
|
6214
|
+
syncCost().catch(() => {
|
|
6215
|
+
});
|
|
6216
|
+
const timer = setInterval(() => {
|
|
6217
|
+
syncCost().catch(() => {
|
|
6218
|
+
});
|
|
6219
|
+
}, SYNC_INTERVAL_MS);
|
|
6220
|
+
timer.unref();
|
|
6221
|
+
}
|
|
6222
|
+
var import_fs16, import_path19, import_os14, SYNC_INTERVAL_MS, PRICING;
|
|
6223
|
+
var init_costSync = __esm({
|
|
6224
|
+
"src/costSync.ts"() {
|
|
6225
|
+
"use strict";
|
|
6226
|
+
import_fs16 = __toESM(require("fs"));
|
|
6227
|
+
import_path19 = __toESM(require("path"));
|
|
6228
|
+
import_os14 = __toESM(require("os"));
|
|
6229
|
+
init_config();
|
|
6230
|
+
init_audit();
|
|
6231
|
+
SYNC_INTERVAL_MS = 10 * 60 * 1e3;
|
|
6232
|
+
PRICING = {
|
|
6233
|
+
"claude-opus-4": [5e-6, 25e-6, 625e-8, 5e-7],
|
|
6234
|
+
"claude-sonnet-4": [3e-6, 15e-6, 375e-8, 3e-7],
|
|
6235
|
+
"claude-haiku-4": [8e-7, 4e-6, 1e-6, 8e-8],
|
|
6236
|
+
"claude-3-7-sonnet": [3e-6, 15e-6, 375e-8, 3e-7],
|
|
6237
|
+
"claude-3-5-sonnet": [3e-6, 15e-6, 375e-8, 3e-7],
|
|
6238
|
+
"claude-3-5-haiku": [8e-7, 4e-6, 1e-6, 8e-8],
|
|
6239
|
+
"claude-3-haiku": [25e-8, 125e-8, 3e-7, 3e-8]
|
|
6240
|
+
};
|
|
6241
|
+
}
|
|
6242
|
+
});
|
|
6243
|
+
|
|
6062
6244
|
// src/daemon/server.ts
|
|
6063
6245
|
function startDaemon() {
|
|
6246
|
+
startCostSync();
|
|
6064
6247
|
loadInsightCounts();
|
|
6065
6248
|
const csrfToken = (0, import_crypto7.randomUUID)();
|
|
6066
6249
|
const internalToken = (0, import_crypto7.randomUUID)();
|
|
@@ -6076,7 +6259,7 @@ function startDaemon() {
|
|
|
6076
6259
|
idleTimer = setTimeout(() => {
|
|
6077
6260
|
if (autoStarted) {
|
|
6078
6261
|
try {
|
|
6079
|
-
|
|
6262
|
+
import_fs17.default.unlinkSync(DAEMON_PID_FILE);
|
|
6080
6263
|
} catch {
|
|
6081
6264
|
}
|
|
6082
6265
|
}
|
|
@@ -6239,7 +6422,7 @@ data: ${JSON.stringify(item.data)}
|
|
|
6239
6422
|
status: "pending"
|
|
6240
6423
|
});
|
|
6241
6424
|
}
|
|
6242
|
-
const projectCwd = typeof cwd === "string" &&
|
|
6425
|
+
const projectCwd = typeof cwd === "string" && import_path20.default.isAbsolute(cwd) ? cwd : void 0;
|
|
6243
6426
|
const projectConfig = getConfig(projectCwd);
|
|
6244
6427
|
const browserEnabled = projectConfig.settings.approvers?.browser !== false;
|
|
6245
6428
|
const terminalEnabled = projectConfig.settings.approvers?.terminal !== false;
|
|
@@ -6629,8 +6812,8 @@ data: ${JSON.stringify(item.data)}
|
|
|
6629
6812
|
const body = await readBody(req);
|
|
6630
6813
|
const data = body ? JSON.parse(body) : {};
|
|
6631
6814
|
const configPath = data.configPath ?? GLOBAL_CONFIG_PATH;
|
|
6632
|
-
const node9Dir =
|
|
6633
|
-
if (!
|
|
6815
|
+
const node9Dir = import_path20.default.dirname(GLOBAL_CONFIG_PATH);
|
|
6816
|
+
if (!import_path20.default.resolve(configPath).startsWith(node9Dir + import_path20.default.sep)) {
|
|
6634
6817
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
6635
6818
|
return res.end(
|
|
6636
6819
|
JSON.stringify({ error: "configPath must be within the node9 config directory" })
|
|
@@ -6741,14 +6924,14 @@ data: ${JSON.stringify(item.data)}
|
|
|
6741
6924
|
server.on("error", (e) => {
|
|
6742
6925
|
if (e.code === "EADDRINUSE") {
|
|
6743
6926
|
try {
|
|
6744
|
-
if (
|
|
6745
|
-
const { pid } = JSON.parse(
|
|
6927
|
+
if (import_fs17.default.existsSync(DAEMON_PID_FILE)) {
|
|
6928
|
+
const { pid } = JSON.parse(import_fs17.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
6746
6929
|
process.kill(pid, 0);
|
|
6747
6930
|
return process.exit(0);
|
|
6748
6931
|
}
|
|
6749
6932
|
} catch {
|
|
6750
6933
|
try {
|
|
6751
|
-
|
|
6934
|
+
import_fs17.default.unlinkSync(DAEMON_PID_FILE);
|
|
6752
6935
|
} catch {
|
|
6753
6936
|
}
|
|
6754
6937
|
server.listen(DAEMON_PORT, DAEMON_HOST);
|
|
@@ -6807,13 +6990,13 @@ data: ${JSON.stringify(item.data)}
|
|
|
6807
6990
|
}
|
|
6808
6991
|
startActivitySocket();
|
|
6809
6992
|
}
|
|
6810
|
-
var import_http,
|
|
6993
|
+
var import_http, import_fs17, import_path20, import_crypto7, import_child_process4, import_chalk2;
|
|
6811
6994
|
var init_server = __esm({
|
|
6812
6995
|
"src/daemon/server.ts"() {
|
|
6813
6996
|
"use strict";
|
|
6814
6997
|
import_http = __toESM(require("http"));
|
|
6815
|
-
|
|
6816
|
-
|
|
6998
|
+
import_fs17 = __toESM(require("fs"));
|
|
6999
|
+
import_path20 = __toESM(require("path"));
|
|
6817
7000
|
import_crypto7 = require("crypto");
|
|
6818
7001
|
import_child_process4 = require("child_process");
|
|
6819
7002
|
import_chalk2 = __toESM(require("chalk"));
|
|
@@ -6823,29 +7006,30 @@ var init_server = __esm({
|
|
|
6823
7006
|
init_state2();
|
|
6824
7007
|
init_patch();
|
|
6825
7008
|
init_config_schema();
|
|
7009
|
+
init_costSync();
|
|
6826
7010
|
}
|
|
6827
7011
|
});
|
|
6828
7012
|
|
|
6829
7013
|
// src/daemon/index.ts
|
|
6830
7014
|
function stopDaemon() {
|
|
6831
|
-
if (!
|
|
7015
|
+
if (!import_fs18.default.existsSync(DAEMON_PID_FILE)) return console.log(import_chalk3.default.yellow("Not running."));
|
|
6832
7016
|
try {
|
|
6833
|
-
const { pid } = JSON.parse(
|
|
7017
|
+
const { pid } = JSON.parse(import_fs18.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
6834
7018
|
process.kill(pid, "SIGTERM");
|
|
6835
7019
|
console.log(import_chalk3.default.green("\u2705 Stopped."));
|
|
6836
7020
|
} catch {
|
|
6837
7021
|
console.log(import_chalk3.default.gray("Cleaned up stale PID file."));
|
|
6838
7022
|
} finally {
|
|
6839
7023
|
try {
|
|
6840
|
-
|
|
7024
|
+
import_fs18.default.unlinkSync(DAEMON_PID_FILE);
|
|
6841
7025
|
} catch {
|
|
6842
7026
|
}
|
|
6843
7027
|
}
|
|
6844
7028
|
}
|
|
6845
7029
|
function daemonStatus() {
|
|
6846
|
-
if (
|
|
7030
|
+
if (import_fs18.default.existsSync(DAEMON_PID_FILE)) {
|
|
6847
7031
|
try {
|
|
6848
|
-
const { pid } = JSON.parse(
|
|
7032
|
+
const { pid } = JSON.parse(import_fs18.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
6849
7033
|
process.kill(pid, 0);
|
|
6850
7034
|
console.log(import_chalk3.default.green("Node9 daemon: running"));
|
|
6851
7035
|
return;
|
|
@@ -6864,11 +7048,11 @@ function daemonStatus() {
|
|
|
6864
7048
|
console.log(import_chalk3.default.yellow("Node9 daemon: not running"));
|
|
6865
7049
|
}
|
|
6866
7050
|
}
|
|
6867
|
-
var
|
|
7051
|
+
var import_fs18, import_chalk3, import_child_process5;
|
|
6868
7052
|
var init_daemon2 = __esm({
|
|
6869
7053
|
"src/daemon/index.ts"() {
|
|
6870
7054
|
"use strict";
|
|
6871
|
-
|
|
7055
|
+
import_fs18 = __toESM(require("fs"));
|
|
6872
7056
|
import_chalk3 = __toESM(require("chalk"));
|
|
6873
7057
|
import_child_process5 = require("child_process");
|
|
6874
7058
|
init_server();
|
|
@@ -6902,7 +7086,7 @@ function formatBase(activity) {
|
|
|
6902
7086
|
const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
|
|
6903
7087
|
const icon = getIcon(activity.tool);
|
|
6904
7088
|
const toolName = activity.tool.slice(0, 16).padEnd(16);
|
|
6905
|
-
const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(
|
|
7089
|
+
const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(import_os25.default.homedir(), "~");
|
|
6906
7090
|
const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
|
|
6907
7091
|
return `${import_chalk19.default.gray(time)} ${icon} ${import_chalk19.default.white.bold(toolName)} ${import_chalk19.default.dim(argsPreview)}`;
|
|
6908
7092
|
}
|
|
@@ -6941,9 +7125,9 @@ function renderPending(activity) {
|
|
|
6941
7125
|
}
|
|
6942
7126
|
async function ensureDaemon() {
|
|
6943
7127
|
let pidPort = null;
|
|
6944
|
-
if (
|
|
7128
|
+
if (import_fs29.default.existsSync(PID_FILE)) {
|
|
6945
7129
|
try {
|
|
6946
|
-
const { port } = JSON.parse(
|
|
7130
|
+
const { port } = JSON.parse(import_fs29.default.readFileSync(PID_FILE, "utf-8"));
|
|
6947
7131
|
pidPort = port;
|
|
6948
7132
|
} catch {
|
|
6949
7133
|
console.error(import_chalk19.default.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
|
|
@@ -7020,6 +7204,9 @@ function buildCardLines(req, localCount = 0) {
|
|
|
7020
7204
|
`${CYAN}\u2551${RESET2} Tool: ${BOLD2}${req.toolName}${RESET2}`,
|
|
7021
7205
|
`${CYAN}\u2551${RESET2} Reason: ${tierLabel} \u2014 ${blockedBy}${RESET2}`
|
|
7022
7206
|
];
|
|
7207
|
+
if (req.riskMetadata?.ruleDescription) {
|
|
7208
|
+
lines.push(`${CYAN}\u2551${RESET2} ${YELLOW}\u2139 ${req.riskMetadata.ruleDescription}${RESET2}`);
|
|
7209
|
+
}
|
|
7023
7210
|
if (req.riskMetadata?.ruleName && blockedBy.includes("Taint")) {
|
|
7024
7211
|
lines.push(`${CYAN}\u2551${RESET2} ${YELLOW}\u26A0 ${req.riskMetadata.ruleName}${RESET2}`);
|
|
7025
7212
|
}
|
|
@@ -7063,9 +7250,9 @@ function buildRecoveryCardLines(req) {
|
|
|
7063
7250
|
];
|
|
7064
7251
|
}
|
|
7065
7252
|
function readApproversFromDisk() {
|
|
7066
|
-
const configPath =
|
|
7253
|
+
const configPath = import_path32.default.join(import_os25.default.homedir(), ".node9", "config.json");
|
|
7067
7254
|
try {
|
|
7068
|
-
const raw = JSON.parse(
|
|
7255
|
+
const raw = JSON.parse(import_fs29.default.readFileSync(configPath, "utf-8"));
|
|
7069
7256
|
const settings = raw.settings ?? {};
|
|
7070
7257
|
return settings.approvers ?? {};
|
|
7071
7258
|
} catch {
|
|
@@ -7081,15 +7268,15 @@ function approverStatusLine() {
|
|
|
7081
7268
|
return `${fmt("native", "native")} ${fmt("browser", "browser")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
|
|
7082
7269
|
}
|
|
7083
7270
|
function toggleApprover(channel) {
|
|
7084
|
-
const configPath =
|
|
7271
|
+
const configPath = import_path32.default.join(import_os25.default.homedir(), ".node9", "config.json");
|
|
7085
7272
|
try {
|
|
7086
|
-
const raw = JSON.parse(
|
|
7273
|
+
const raw = JSON.parse(import_fs29.default.readFileSync(configPath, "utf-8"));
|
|
7087
7274
|
const settings = raw.settings ?? {};
|
|
7088
7275
|
const approvers = settings.approvers ?? {};
|
|
7089
7276
|
approvers[channel] = approvers[channel] === false;
|
|
7090
7277
|
settings.approvers = approvers;
|
|
7091
7278
|
raw.settings = settings;
|
|
7092
|
-
|
|
7279
|
+
import_fs29.default.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
|
|
7093
7280
|
} catch (err2) {
|
|
7094
7281
|
process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
|
|
7095
7282
|
`);
|
|
@@ -7259,8 +7446,8 @@ async function startTail(options = {}) {
|
|
|
7259
7446
|
}
|
|
7260
7447
|
postDecisionHttp(req2.id, httpDecision, csrfToken, port, httpOpts).catch((err2) => {
|
|
7261
7448
|
try {
|
|
7262
|
-
|
|
7263
|
-
|
|
7449
|
+
import_fs29.default.appendFileSync(
|
|
7450
|
+
import_path32.default.join(import_os25.default.homedir(), ".node9", "hook-debug.log"),
|
|
7264
7451
|
`[tail] POST /decision failed: ${String(err2)}
|
|
7265
7452
|
`
|
|
7266
7453
|
);
|
|
@@ -7513,21 +7700,21 @@ async function startTail(options = {}) {
|
|
|
7513
7700
|
process.exit(1);
|
|
7514
7701
|
});
|
|
7515
7702
|
}
|
|
7516
|
-
var import_http2, import_chalk19,
|
|
7703
|
+
var import_http2, import_chalk19, import_fs29, import_os25, import_path32, import_readline5, import_child_process14, PID_FILE, ICONS, RESET2, BOLD2, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, pendingShownForId, pendingWrappedLines, DIVIDER;
|
|
7517
7704
|
var init_tail = __esm({
|
|
7518
7705
|
"src/tui/tail.ts"() {
|
|
7519
7706
|
"use strict";
|
|
7520
7707
|
import_http2 = __toESM(require("http"));
|
|
7521
7708
|
import_chalk19 = __toESM(require("chalk"));
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7709
|
+
import_fs29 = __toESM(require("fs"));
|
|
7710
|
+
import_os25 = __toESM(require("os"));
|
|
7711
|
+
import_path32 = __toESM(require("path"));
|
|
7525
7712
|
import_readline5 = __toESM(require("readline"));
|
|
7526
7713
|
import_child_process14 = require("child_process");
|
|
7527
7714
|
init_daemon2();
|
|
7528
7715
|
init_daemon();
|
|
7529
7716
|
init_core();
|
|
7530
|
-
PID_FILE =
|
|
7717
|
+
PID_FILE = import_path32.default.join(import_os25.default.homedir(), ".node9", "daemon.pid");
|
|
7531
7718
|
ICONS = {
|
|
7532
7719
|
bash: "\u{1F4BB}",
|
|
7533
7720
|
shell: "\u{1F4BB}",
|
|
@@ -7642,9 +7829,9 @@ function formatTimeLeft(resetsAt) {
|
|
|
7642
7829
|
return ` (${m}m left)`;
|
|
7643
7830
|
}
|
|
7644
7831
|
function safeReadJson(filePath) {
|
|
7645
|
-
if (!
|
|
7832
|
+
if (!import_fs30.default.existsSync(filePath)) return null;
|
|
7646
7833
|
try {
|
|
7647
|
-
return JSON.parse(
|
|
7834
|
+
return JSON.parse(import_fs30.default.readFileSync(filePath, "utf-8"));
|
|
7648
7835
|
} catch {
|
|
7649
7836
|
return null;
|
|
7650
7837
|
}
|
|
@@ -7665,12 +7852,12 @@ function countHooksInFile(filePath) {
|
|
|
7665
7852
|
return Object.keys(cfg.hooks).length;
|
|
7666
7853
|
}
|
|
7667
7854
|
function countRulesInDir(rulesDir) {
|
|
7668
|
-
if (!
|
|
7855
|
+
if (!import_fs30.default.existsSync(rulesDir)) return 0;
|
|
7669
7856
|
let count = 0;
|
|
7670
7857
|
try {
|
|
7671
|
-
for (const entry of
|
|
7858
|
+
for (const entry of import_fs30.default.readdirSync(rulesDir, { withFileTypes: true })) {
|
|
7672
7859
|
if (entry.isDirectory()) {
|
|
7673
|
-
count += countRulesInDir(
|
|
7860
|
+
count += countRulesInDir(import_path33.default.join(rulesDir, entry.name));
|
|
7674
7861
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
7675
7862
|
count++;
|
|
7676
7863
|
}
|
|
@@ -7681,46 +7868,46 @@ function countRulesInDir(rulesDir) {
|
|
|
7681
7868
|
}
|
|
7682
7869
|
function isSamePath(a, b) {
|
|
7683
7870
|
try {
|
|
7684
|
-
return
|
|
7871
|
+
return import_path33.default.resolve(a) === import_path33.default.resolve(b);
|
|
7685
7872
|
} catch {
|
|
7686
7873
|
return false;
|
|
7687
7874
|
}
|
|
7688
7875
|
}
|
|
7689
7876
|
function countConfigs(cwd) {
|
|
7690
|
-
const homeDir2 =
|
|
7691
|
-
const claudeDir =
|
|
7877
|
+
const homeDir2 = import_os26.default.homedir();
|
|
7878
|
+
const claudeDir = import_path33.default.join(homeDir2, ".claude");
|
|
7692
7879
|
let claudeMdCount = 0;
|
|
7693
7880
|
let rulesCount = 0;
|
|
7694
7881
|
let hooksCount = 0;
|
|
7695
7882
|
const userMcpServers = /* @__PURE__ */ new Set();
|
|
7696
7883
|
const projectMcpServers = /* @__PURE__ */ new Set();
|
|
7697
|
-
if (
|
|
7698
|
-
rulesCount += countRulesInDir(
|
|
7699
|
-
const userSettings =
|
|
7884
|
+
if (import_fs30.default.existsSync(import_path33.default.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
7885
|
+
rulesCount += countRulesInDir(import_path33.default.join(claudeDir, "rules"));
|
|
7886
|
+
const userSettings = import_path33.default.join(claudeDir, "settings.json");
|
|
7700
7887
|
for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
|
|
7701
7888
|
hooksCount += countHooksInFile(userSettings);
|
|
7702
|
-
const userClaudeJson =
|
|
7889
|
+
const userClaudeJson = import_path33.default.join(homeDir2, ".claude.json");
|
|
7703
7890
|
for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
|
|
7704
7891
|
for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
|
|
7705
7892
|
userMcpServers.delete(name);
|
|
7706
7893
|
}
|
|
7707
7894
|
if (cwd) {
|
|
7708
|
-
if (
|
|
7709
|
-
if (
|
|
7710
|
-
const projectClaudeDir =
|
|
7895
|
+
if (import_fs30.default.existsSync(import_path33.default.join(cwd, "CLAUDE.md"))) claudeMdCount++;
|
|
7896
|
+
if (import_fs30.default.existsSync(import_path33.default.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
|
|
7897
|
+
const projectClaudeDir = import_path33.default.join(cwd, ".claude");
|
|
7711
7898
|
const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
|
|
7712
7899
|
if (!overlapsUserScope) {
|
|
7713
|
-
if (
|
|
7714
|
-
rulesCount += countRulesInDir(
|
|
7715
|
-
const projSettings =
|
|
7900
|
+
if (import_fs30.default.existsSync(import_path33.default.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
7901
|
+
rulesCount += countRulesInDir(import_path33.default.join(projectClaudeDir, "rules"));
|
|
7902
|
+
const projSettings = import_path33.default.join(projectClaudeDir, "settings.json");
|
|
7716
7903
|
for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
|
|
7717
7904
|
hooksCount += countHooksInFile(projSettings);
|
|
7718
7905
|
}
|
|
7719
|
-
if (
|
|
7720
|
-
const localSettings =
|
|
7906
|
+
if (import_fs30.default.existsSync(import_path33.default.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
|
|
7907
|
+
const localSettings = import_path33.default.join(projectClaudeDir, "settings.local.json");
|
|
7721
7908
|
for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
|
|
7722
7909
|
hooksCount += countHooksInFile(localSettings);
|
|
7723
|
-
const mcpJsonServers = getMcpServerNames(
|
|
7910
|
+
const mcpJsonServers = getMcpServerNames(import_path33.default.join(cwd, ".mcp.json"));
|
|
7724
7911
|
const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
|
|
7725
7912
|
for (const name of disabledMcpJson) mcpJsonServers.delete(name);
|
|
7726
7913
|
for (const name of mcpJsonServers) projectMcpServers.add(name);
|
|
@@ -7753,12 +7940,12 @@ function readActiveShieldsHud() {
|
|
|
7753
7940
|
return shieldsCache.value;
|
|
7754
7941
|
}
|
|
7755
7942
|
try {
|
|
7756
|
-
const shieldsPath =
|
|
7757
|
-
if (!
|
|
7943
|
+
const shieldsPath = import_path33.default.join(import_os26.default.homedir(), ".node9", "shields.json");
|
|
7944
|
+
if (!import_fs30.default.existsSync(shieldsPath)) {
|
|
7758
7945
|
shieldsCache = { value: [], ts: now };
|
|
7759
7946
|
return [];
|
|
7760
7947
|
}
|
|
7761
|
-
const parsed = JSON.parse(
|
|
7948
|
+
const parsed = JSON.parse(import_fs30.default.readFileSync(shieldsPath, "utf-8"));
|
|
7762
7949
|
if (!Array.isArray(parsed.active)) {
|
|
7763
7950
|
shieldsCache = { value: [], ts: now };
|
|
7764
7951
|
return [];
|
|
@@ -7860,17 +8047,17 @@ function renderContextLine(stdin) {
|
|
|
7860
8047
|
async function main() {
|
|
7861
8048
|
try {
|
|
7862
8049
|
const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
|
|
7863
|
-
if (
|
|
8050
|
+
if (import_fs30.default.existsSync(import_path33.default.join(import_os26.default.homedir(), ".node9", "hud-debug"))) {
|
|
7864
8051
|
try {
|
|
7865
|
-
const logPath =
|
|
8052
|
+
const logPath = import_path33.default.join(import_os26.default.homedir(), ".node9", "hud-debug.log");
|
|
7866
8053
|
const MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
7867
8054
|
let size = 0;
|
|
7868
8055
|
try {
|
|
7869
|
-
size =
|
|
8056
|
+
size = import_fs30.default.statSync(logPath).size;
|
|
7870
8057
|
} catch {
|
|
7871
8058
|
}
|
|
7872
8059
|
if (size < MAX_LOG_SIZE) {
|
|
7873
|
-
|
|
8060
|
+
import_fs30.default.appendFileSync(
|
|
7874
8061
|
logPath,
|
|
7875
8062
|
JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
|
|
7876
8063
|
);
|
|
@@ -7891,11 +8078,11 @@ async function main() {
|
|
|
7891
8078
|
try {
|
|
7892
8079
|
const cwd = stdin.cwd ?? process.cwd();
|
|
7893
8080
|
for (const configPath of [
|
|
7894
|
-
|
|
7895
|
-
|
|
8081
|
+
import_path33.default.join(cwd, "node9.config.json"),
|
|
8082
|
+
import_path33.default.join(import_os26.default.homedir(), ".node9", "config.json")
|
|
7896
8083
|
]) {
|
|
7897
|
-
if (!
|
|
7898
|
-
const cfg = JSON.parse(
|
|
8084
|
+
if (!import_fs30.default.existsSync(configPath)) continue;
|
|
8085
|
+
const cfg = JSON.parse(import_fs30.default.readFileSync(configPath, "utf-8"));
|
|
7899
8086
|
const hud = cfg.settings?.hud;
|
|
7900
8087
|
if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
|
|
7901
8088
|
}
|
|
@@ -7913,13 +8100,13 @@ async function main() {
|
|
|
7913
8100
|
renderOffline();
|
|
7914
8101
|
}
|
|
7915
8102
|
}
|
|
7916
|
-
var
|
|
8103
|
+
var import_fs30, import_path33, import_os26, import_http3, RESET3, BOLD3, DIM, RED2, GREEN2, YELLOW2, BLUE, MAGENTA, CYAN2, WHITE, BAR_FILLED, BAR_EMPTY, BAR_WIDTH, shieldsCache, SHIELDS_CACHE_TTL_MS;
|
|
7917
8104
|
var init_hud = __esm({
|
|
7918
8105
|
"src/cli/hud.ts"() {
|
|
7919
8106
|
"use strict";
|
|
7920
|
-
|
|
7921
|
-
|
|
7922
|
-
|
|
8107
|
+
import_fs30 = __toESM(require("fs"));
|
|
8108
|
+
import_path33 = __toESM(require("path"));
|
|
8109
|
+
import_os26 = __toESM(require("os"));
|
|
7923
8110
|
import_http3 = __toESM(require("http"));
|
|
7924
8111
|
init_daemon();
|
|
7925
8112
|
RESET3 = "\x1B[0m";
|
|
@@ -8529,9 +8716,9 @@ function teardownHud() {
|
|
|
8529
8716
|
// src/cli.ts
|
|
8530
8717
|
init_daemon2();
|
|
8531
8718
|
var import_chalk20 = __toESM(require("chalk"));
|
|
8532
|
-
var
|
|
8533
|
-
var
|
|
8534
|
-
var
|
|
8719
|
+
var import_fs31 = __toESM(require("fs"));
|
|
8720
|
+
var import_path34 = __toESM(require("path"));
|
|
8721
|
+
var import_os27 = __toESM(require("os"));
|
|
8535
8722
|
var import_prompts2 = require("@inquirer/prompts");
|
|
8536
8723
|
|
|
8537
8724
|
// src/utils/duration.ts
|
|
@@ -8756,10 +8943,10 @@ async function autoStartDaemonAndWait() {
|
|
|
8756
8943
|
|
|
8757
8944
|
// src/cli/commands/check.ts
|
|
8758
8945
|
var import_chalk5 = __toESM(require("chalk"));
|
|
8759
|
-
var
|
|
8946
|
+
var import_fs20 = __toESM(require("fs"));
|
|
8760
8947
|
var import_child_process9 = require("child_process");
|
|
8761
|
-
var
|
|
8762
|
-
var
|
|
8948
|
+
var import_path22 = __toESM(require("path"));
|
|
8949
|
+
var import_os16 = __toESM(require("os"));
|
|
8763
8950
|
init_orchestrator();
|
|
8764
8951
|
init_daemon();
|
|
8765
8952
|
init_config();
|
|
@@ -8768,11 +8955,11 @@ init_policy();
|
|
|
8768
8955
|
// src/undo.ts
|
|
8769
8956
|
var import_child_process8 = require("child_process");
|
|
8770
8957
|
var import_crypto8 = __toESM(require("crypto"));
|
|
8771
|
-
var
|
|
8958
|
+
var import_fs19 = __toESM(require("fs"));
|
|
8772
8959
|
var import_net3 = __toESM(require("net"));
|
|
8773
|
-
var
|
|
8774
|
-
var
|
|
8775
|
-
var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" :
|
|
8960
|
+
var import_path21 = __toESM(require("path"));
|
|
8961
|
+
var import_os15 = __toESM(require("os"));
|
|
8962
|
+
var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path21.default.join(import_os15.default.tmpdir(), "node9-activity.sock");
|
|
8776
8963
|
function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
|
|
8777
8964
|
try {
|
|
8778
8965
|
const payload = JSON.stringify({
|
|
@@ -8792,22 +8979,22 @@ function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
|
|
|
8792
8979
|
} catch {
|
|
8793
8980
|
}
|
|
8794
8981
|
}
|
|
8795
|
-
var SNAPSHOT_STACK_PATH =
|
|
8796
|
-
var UNDO_LATEST_PATH =
|
|
8982
|
+
var SNAPSHOT_STACK_PATH = import_path21.default.join(import_os15.default.homedir(), ".node9", "snapshots.json");
|
|
8983
|
+
var UNDO_LATEST_PATH = import_path21.default.join(import_os15.default.homedir(), ".node9", "undo_latest.txt");
|
|
8797
8984
|
var MAX_SNAPSHOTS = 10;
|
|
8798
8985
|
var GIT_TIMEOUT = 15e3;
|
|
8799
8986
|
function readStack() {
|
|
8800
8987
|
try {
|
|
8801
|
-
if (
|
|
8802
|
-
return JSON.parse(
|
|
8988
|
+
if (import_fs19.default.existsSync(SNAPSHOT_STACK_PATH))
|
|
8989
|
+
return JSON.parse(import_fs19.default.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
|
|
8803
8990
|
} catch {
|
|
8804
8991
|
}
|
|
8805
8992
|
return [];
|
|
8806
8993
|
}
|
|
8807
8994
|
function writeStack(stack) {
|
|
8808
|
-
const dir =
|
|
8809
|
-
if (!
|
|
8810
|
-
|
|
8995
|
+
const dir = import_path21.default.dirname(SNAPSHOT_STACK_PATH);
|
|
8996
|
+
if (!import_fs19.default.existsSync(dir)) import_fs19.default.mkdirSync(dir, { recursive: true });
|
|
8997
|
+
import_fs19.default.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
|
|
8811
8998
|
}
|
|
8812
8999
|
function extractFilePath(args) {
|
|
8813
9000
|
if (!args || typeof args !== "object") return null;
|
|
@@ -8827,12 +9014,12 @@ function buildArgsSummary(tool, args) {
|
|
|
8827
9014
|
return "";
|
|
8828
9015
|
}
|
|
8829
9016
|
function findProjectRoot(filePath) {
|
|
8830
|
-
let dir =
|
|
9017
|
+
let dir = import_path21.default.dirname(filePath);
|
|
8831
9018
|
while (true) {
|
|
8832
|
-
if (
|
|
9019
|
+
if (import_fs19.default.existsSync(import_path21.default.join(dir, ".git")) || import_fs19.default.existsSync(import_path21.default.join(dir, "package.json"))) {
|
|
8833
9020
|
return dir;
|
|
8834
9021
|
}
|
|
8835
|
-
const parent =
|
|
9022
|
+
const parent = import_path21.default.dirname(dir);
|
|
8836
9023
|
if (parent === dir) return process.cwd();
|
|
8837
9024
|
dir = parent;
|
|
8838
9025
|
}
|
|
@@ -8840,7 +9027,7 @@ function findProjectRoot(filePath) {
|
|
|
8840
9027
|
function normalizeCwdForHash(cwd) {
|
|
8841
9028
|
let normalized;
|
|
8842
9029
|
try {
|
|
8843
|
-
normalized =
|
|
9030
|
+
normalized = import_fs19.default.realpathSync(cwd);
|
|
8844
9031
|
} catch {
|
|
8845
9032
|
normalized = cwd;
|
|
8846
9033
|
}
|
|
@@ -8850,16 +9037,16 @@ function normalizeCwdForHash(cwd) {
|
|
|
8850
9037
|
}
|
|
8851
9038
|
function getShadowRepoDir(cwd) {
|
|
8852
9039
|
const hash = import_crypto8.default.createHash("sha256").update(normalizeCwdForHash(cwd)).digest("hex").slice(0, 16);
|
|
8853
|
-
return
|
|
9040
|
+
return import_path21.default.join(import_os15.default.homedir(), ".node9", "snapshots", hash);
|
|
8854
9041
|
}
|
|
8855
9042
|
function cleanOrphanedIndexFiles(shadowDir) {
|
|
8856
9043
|
try {
|
|
8857
9044
|
const cutoff = Date.now() - 6e4;
|
|
8858
|
-
for (const f of
|
|
9045
|
+
for (const f of import_fs19.default.readdirSync(shadowDir)) {
|
|
8859
9046
|
if (f.startsWith("index_")) {
|
|
8860
|
-
const fp =
|
|
9047
|
+
const fp = import_path21.default.join(shadowDir, f);
|
|
8861
9048
|
try {
|
|
8862
|
-
if (
|
|
9049
|
+
if (import_fs19.default.statSync(fp).mtimeMs < cutoff) import_fs19.default.unlinkSync(fp);
|
|
8863
9050
|
} catch {
|
|
8864
9051
|
}
|
|
8865
9052
|
}
|
|
@@ -8871,7 +9058,7 @@ function writeShadowExcludes(shadowDir, ignorePaths) {
|
|
|
8871
9058
|
const hardcoded = [".git", ".node9"];
|
|
8872
9059
|
const lines = [...hardcoded, ...ignorePaths].join("\n");
|
|
8873
9060
|
try {
|
|
8874
|
-
|
|
9061
|
+
import_fs19.default.writeFileSync(import_path21.default.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
|
|
8875
9062
|
} catch {
|
|
8876
9063
|
}
|
|
8877
9064
|
}
|
|
@@ -8884,25 +9071,25 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
8884
9071
|
timeout: 3e3
|
|
8885
9072
|
});
|
|
8886
9073
|
if (check.status === 0) {
|
|
8887
|
-
const ptPath =
|
|
9074
|
+
const ptPath = import_path21.default.join(shadowDir, "project-path.txt");
|
|
8888
9075
|
try {
|
|
8889
|
-
const stored =
|
|
9076
|
+
const stored = import_fs19.default.readFileSync(ptPath, "utf8").trim();
|
|
8890
9077
|
if (stored === normalizedCwd) return true;
|
|
8891
9078
|
if (process.env.NODE9_DEBUG === "1")
|
|
8892
9079
|
console.error(
|
|
8893
9080
|
`[Node9] Shadow repo path mismatch: stored="${stored}" expected="${normalizedCwd}" \u2014 reinitializing`
|
|
8894
9081
|
);
|
|
8895
|
-
|
|
9082
|
+
import_fs19.default.rmSync(shadowDir, { recursive: true, force: true });
|
|
8896
9083
|
} catch {
|
|
8897
9084
|
try {
|
|
8898
|
-
|
|
9085
|
+
import_fs19.default.writeFileSync(ptPath, normalizedCwd, "utf8");
|
|
8899
9086
|
} catch {
|
|
8900
9087
|
}
|
|
8901
9088
|
return true;
|
|
8902
9089
|
}
|
|
8903
9090
|
}
|
|
8904
9091
|
try {
|
|
8905
|
-
|
|
9092
|
+
import_fs19.default.mkdirSync(shadowDir, { recursive: true });
|
|
8906
9093
|
} catch {
|
|
8907
9094
|
}
|
|
8908
9095
|
const init = (0, import_child_process8.spawnSync)("git", ["init", "--bare", shadowDir], { timeout: 5e3 });
|
|
@@ -8911,7 +9098,7 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
8911
9098
|
if (process.env.NODE9_DEBUG === "1") console.error("[Node9] git init --bare failed:", reason);
|
|
8912
9099
|
return false;
|
|
8913
9100
|
}
|
|
8914
|
-
const configFile =
|
|
9101
|
+
const configFile = import_path21.default.join(shadowDir, "config");
|
|
8915
9102
|
(0, import_child_process8.spawnSync)("git", ["config", "--file", configFile, "core.untrackedCache", "true"], {
|
|
8916
9103
|
timeout: 3e3
|
|
8917
9104
|
});
|
|
@@ -8919,7 +9106,7 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
8919
9106
|
timeout: 3e3
|
|
8920
9107
|
});
|
|
8921
9108
|
try {
|
|
8922
|
-
|
|
9109
|
+
import_fs19.default.writeFileSync(import_path21.default.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
|
|
8923
9110
|
} catch {
|
|
8924
9111
|
}
|
|
8925
9112
|
return true;
|
|
@@ -8939,12 +9126,12 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
8939
9126
|
let indexFile = null;
|
|
8940
9127
|
try {
|
|
8941
9128
|
const rawFilePath = extractFilePath(args);
|
|
8942
|
-
const absFilePath = rawFilePath &&
|
|
9129
|
+
const absFilePath = rawFilePath && import_path21.default.isAbsolute(rawFilePath) ? rawFilePath : null;
|
|
8943
9130
|
const cwd = absFilePath ? findProjectRoot(absFilePath) : process.cwd();
|
|
8944
9131
|
const shadowDir = getShadowRepoDir(cwd);
|
|
8945
9132
|
if (!ensureShadowRepo(shadowDir, cwd)) return null;
|
|
8946
9133
|
writeShadowExcludes(shadowDir, ignorePaths);
|
|
8947
|
-
indexFile =
|
|
9134
|
+
indexFile = import_path21.default.join(shadowDir, `index_${process.pid}_${Date.now()}`);
|
|
8948
9135
|
const shadowEnv = {
|
|
8949
9136
|
...process.env,
|
|
8950
9137
|
GIT_DIR: shadowDir,
|
|
@@ -9016,7 +9203,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
9016
9203
|
writeStack(stack);
|
|
9017
9204
|
const entry = stack[stack.length - 1];
|
|
9018
9205
|
notifySnapshotTaken(commitHash.slice(0, 7), tool, entry.argsSummary, capturedFiles.length);
|
|
9019
|
-
|
|
9206
|
+
import_fs19.default.writeFileSync(UNDO_LATEST_PATH, commitHash);
|
|
9020
9207
|
if (shouldGc) {
|
|
9021
9208
|
(0, import_child_process8.spawn)("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
|
|
9022
9209
|
}
|
|
@@ -9027,7 +9214,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
9027
9214
|
} finally {
|
|
9028
9215
|
if (indexFile) {
|
|
9029
9216
|
try {
|
|
9030
|
-
|
|
9217
|
+
import_fs19.default.unlinkSync(indexFile);
|
|
9031
9218
|
} catch {
|
|
9032
9219
|
}
|
|
9033
9220
|
}
|
|
@@ -9103,9 +9290,9 @@ function applyUndo(hash, cwd) {
|
|
|
9103
9290
|
timeout: GIT_TIMEOUT
|
|
9104
9291
|
}).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
|
|
9105
9292
|
for (const file of [...tracked, ...untracked]) {
|
|
9106
|
-
const fullPath =
|
|
9107
|
-
if (!snapshotFiles.has(file) &&
|
|
9108
|
-
|
|
9293
|
+
const fullPath = import_path21.default.join(dir, file);
|
|
9294
|
+
if (!snapshotFiles.has(file) && import_fs19.default.existsSync(fullPath)) {
|
|
9295
|
+
import_fs19.default.unlinkSync(fullPath);
|
|
9109
9296
|
}
|
|
9110
9297
|
}
|
|
9111
9298
|
return true;
|
|
@@ -9129,9 +9316,9 @@ function registerCheckCommand(program2) {
|
|
|
9129
9316
|
} catch (err2) {
|
|
9130
9317
|
const tempConfig = getConfig();
|
|
9131
9318
|
if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
|
|
9132
|
-
const logPath =
|
|
9319
|
+
const logPath = import_path22.default.join(import_os16.default.homedir(), ".node9", "hook-debug.log");
|
|
9133
9320
|
const errMsg = err2 instanceof Error ? err2.message : String(err2);
|
|
9134
|
-
|
|
9321
|
+
import_fs20.default.appendFileSync(
|
|
9135
9322
|
logPath,
|
|
9136
9323
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
|
|
9137
9324
|
RAW: ${raw}
|
|
@@ -9144,10 +9331,10 @@ RAW: ${raw}
|
|
|
9144
9331
|
if (config.settings.autoStartDaemon && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON) {
|
|
9145
9332
|
try {
|
|
9146
9333
|
const scriptPath = process.argv[1];
|
|
9147
|
-
if (typeof scriptPath !== "string" || !
|
|
9334
|
+
if (typeof scriptPath !== "string" || !import_path22.default.isAbsolute(scriptPath))
|
|
9148
9335
|
throw new Error("node9: argv[1] is not an absolute path");
|
|
9149
|
-
const resolvedScript =
|
|
9150
|
-
const expectedCli =
|
|
9336
|
+
const resolvedScript = import_fs20.default.realpathSync(scriptPath);
|
|
9337
|
+
const expectedCli = import_fs20.default.realpathSync(import_path22.default.resolve(__dirname, "../../cli.js"));
|
|
9151
9338
|
if (resolvedScript !== expectedCli)
|
|
9152
9339
|
throw new Error(
|
|
9153
9340
|
"node9: daemon spawn aborted \u2014 argv[1] does not resolve to the node9 CLI"
|
|
@@ -9173,10 +9360,10 @@ RAW: ${raw}
|
|
|
9173
9360
|
}
|
|
9174
9361
|
}
|
|
9175
9362
|
if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
|
|
9176
|
-
const logPath =
|
|
9177
|
-
if (!
|
|
9178
|
-
|
|
9179
|
-
|
|
9363
|
+
const logPath = import_path22.default.join(import_os16.default.homedir(), ".node9", "hook-debug.log");
|
|
9364
|
+
if (!import_fs20.default.existsSync(import_path22.default.dirname(logPath)))
|
|
9365
|
+
import_fs20.default.mkdirSync(import_path22.default.dirname(logPath), { recursive: true });
|
|
9366
|
+
import_fs20.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
|
|
9180
9367
|
`);
|
|
9181
9368
|
}
|
|
9182
9369
|
const toolName = sanitize2(payload.tool_name ?? payload.name ?? "");
|
|
@@ -9189,8 +9376,8 @@ RAW: ${raw}
|
|
|
9189
9376
|
const isHumanDecision = blockedByContext.toLowerCase().includes("user") || blockedByContext.toLowerCase().includes("daemon") || blockedByContext.toLowerCase().includes("decision");
|
|
9190
9377
|
let ttyFd = null;
|
|
9191
9378
|
try {
|
|
9192
|
-
ttyFd =
|
|
9193
|
-
const writeTty = (line) =>
|
|
9379
|
+
ttyFd = import_fs20.default.openSync("/dev/tty", "w");
|
|
9380
|
+
const writeTty = (line) => import_fs20.default.writeSync(ttyFd, line + "\n");
|
|
9194
9381
|
if (blockedByContext.includes("DLP") || blockedByContext.includes("Secret Detected") || blockedByContext.includes("Credential Review")) {
|
|
9195
9382
|
writeTty(import_chalk5.default.bgRed.white.bold(`
|
|
9196
9383
|
\u{1F6A8} NODE9 DLP ALERT \u2014 CREDENTIAL DETECTED `));
|
|
@@ -9209,7 +9396,7 @@ RAW: ${raw}
|
|
|
9209
9396
|
} finally {
|
|
9210
9397
|
if (ttyFd !== null)
|
|
9211
9398
|
try {
|
|
9212
|
-
|
|
9399
|
+
import_fs20.default.closeSync(ttyFd);
|
|
9213
9400
|
} catch {
|
|
9214
9401
|
}
|
|
9215
9402
|
}
|
|
@@ -9241,7 +9428,7 @@ RAW: ${raw}
|
|
|
9241
9428
|
if (shouldSnapshot(toolName, toolInput, config)) {
|
|
9242
9429
|
await createShadowSnapshot(toolName, toolInput, config.policy.snapshot.ignorePaths);
|
|
9243
9430
|
}
|
|
9244
|
-
const safeCwdForAuth = typeof payload.cwd === "string" &&
|
|
9431
|
+
const safeCwdForAuth = typeof payload.cwd === "string" && import_path22.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
9245
9432
|
const result = await authorizeHeadless(toolName, toolInput, meta, {
|
|
9246
9433
|
cwd: safeCwdForAuth
|
|
9247
9434
|
});
|
|
@@ -9253,12 +9440,12 @@ RAW: ${raw}
|
|
|
9253
9440
|
}
|
|
9254
9441
|
if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && !process.stdout.isTTY && config.settings.autoStartDaemon) {
|
|
9255
9442
|
try {
|
|
9256
|
-
const tty =
|
|
9257
|
-
|
|
9443
|
+
const tty = import_fs20.default.openSync("/dev/tty", "w");
|
|
9444
|
+
import_fs20.default.writeSync(
|
|
9258
9445
|
tty,
|
|
9259
9446
|
import_chalk5.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically...\n")
|
|
9260
9447
|
);
|
|
9261
|
-
|
|
9448
|
+
import_fs20.default.closeSync(tty);
|
|
9262
9449
|
} catch {
|
|
9263
9450
|
}
|
|
9264
9451
|
const daemonReady = await autoStartDaemonAndWait();
|
|
@@ -9285,9 +9472,9 @@ RAW: ${raw}
|
|
|
9285
9472
|
});
|
|
9286
9473
|
} catch (err2) {
|
|
9287
9474
|
if (process.env.NODE9_DEBUG === "1") {
|
|
9288
|
-
const logPath =
|
|
9475
|
+
const logPath = import_path22.default.join(import_os16.default.homedir(), ".node9", "hook-debug.log");
|
|
9289
9476
|
const errMsg = err2 instanceof Error ? err2.message : String(err2);
|
|
9290
|
-
|
|
9477
|
+
import_fs20.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
|
|
9291
9478
|
`);
|
|
9292
9479
|
}
|
|
9293
9480
|
process.exit(0);
|
|
@@ -9321,9 +9508,9 @@ RAW: ${raw}
|
|
|
9321
9508
|
}
|
|
9322
9509
|
|
|
9323
9510
|
// src/cli/commands/log.ts
|
|
9324
|
-
var
|
|
9325
|
-
var
|
|
9326
|
-
var
|
|
9511
|
+
var import_fs21 = __toESM(require("fs"));
|
|
9512
|
+
var import_path23 = __toESM(require("path"));
|
|
9513
|
+
var import_os17 = __toESM(require("os"));
|
|
9327
9514
|
init_audit();
|
|
9328
9515
|
init_config();
|
|
9329
9516
|
init_policy();
|
|
@@ -9399,10 +9586,10 @@ function registerLogCommand(program2) {
|
|
|
9399
9586
|
decision: "allowed",
|
|
9400
9587
|
source: "post-hook"
|
|
9401
9588
|
};
|
|
9402
|
-
const logPath =
|
|
9403
|
-
if (!
|
|
9404
|
-
|
|
9405
|
-
|
|
9589
|
+
const logPath = import_path23.default.join(import_os17.default.homedir(), ".node9", "audit.log");
|
|
9590
|
+
if (!import_fs21.default.existsSync(import_path23.default.dirname(logPath)))
|
|
9591
|
+
import_fs21.default.mkdirSync(import_path23.default.dirname(logPath), { recursive: true });
|
|
9592
|
+
import_fs21.default.appendFileSync(logPath, JSON.stringify(entry) + "\n");
|
|
9406
9593
|
if ((tool === "Bash" || tool === "bash") && isDaemonRunning()) {
|
|
9407
9594
|
const command = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
|
|
9408
9595
|
if (command) {
|
|
@@ -9435,7 +9622,7 @@ function registerLogCommand(program2) {
|
|
|
9435
9622
|
}
|
|
9436
9623
|
}
|
|
9437
9624
|
}
|
|
9438
|
-
const safeCwd = typeof payload.cwd === "string" &&
|
|
9625
|
+
const safeCwd = typeof payload.cwd === "string" && import_path23.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
9439
9626
|
const config = getConfig(safeCwd);
|
|
9440
9627
|
if (shouldSnapshot(tool, {}, config)) {
|
|
9441
9628
|
await createShadowSnapshot("unknown", {}, config.policy.snapshot.ignorePaths);
|
|
@@ -9444,9 +9631,9 @@ function registerLogCommand(program2) {
|
|
|
9444
9631
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
9445
9632
|
process.stderr.write(`[Node9] audit log error: ${msg}
|
|
9446
9633
|
`);
|
|
9447
|
-
const debugPath =
|
|
9634
|
+
const debugPath = import_path23.default.join(import_os17.default.homedir(), ".node9", "hook-debug.log");
|
|
9448
9635
|
try {
|
|
9449
|
-
|
|
9636
|
+
import_fs21.default.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
|
|
9450
9637
|
`);
|
|
9451
9638
|
} catch {
|
|
9452
9639
|
}
|
|
@@ -9846,14 +10033,14 @@ function registerConfigShowCommand(program2) {
|
|
|
9846
10033
|
|
|
9847
10034
|
// src/cli/commands/doctor.ts
|
|
9848
10035
|
var import_chalk7 = __toESM(require("chalk"));
|
|
9849
|
-
var
|
|
9850
|
-
var
|
|
9851
|
-
var
|
|
10036
|
+
var import_fs22 = __toESM(require("fs"));
|
|
10037
|
+
var import_path24 = __toESM(require("path"));
|
|
10038
|
+
var import_os18 = __toESM(require("os"));
|
|
9852
10039
|
var import_child_process10 = require("child_process");
|
|
9853
10040
|
init_daemon();
|
|
9854
10041
|
function registerDoctorCommand(program2, version2) {
|
|
9855
10042
|
program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(() => {
|
|
9856
|
-
const homeDir2 =
|
|
10043
|
+
const homeDir2 = import_os18.default.homedir();
|
|
9857
10044
|
let failures = 0;
|
|
9858
10045
|
function pass(msg) {
|
|
9859
10046
|
console.log(import_chalk7.default.green(" \u2705 ") + msg);
|
|
@@ -9902,10 +10089,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
9902
10089
|
);
|
|
9903
10090
|
}
|
|
9904
10091
|
section("Configuration");
|
|
9905
|
-
const globalConfigPath =
|
|
9906
|
-
if (
|
|
10092
|
+
const globalConfigPath = import_path24.default.join(homeDir2, ".node9", "config.json");
|
|
10093
|
+
if (import_fs22.default.existsSync(globalConfigPath)) {
|
|
9907
10094
|
try {
|
|
9908
|
-
JSON.parse(
|
|
10095
|
+
JSON.parse(import_fs22.default.readFileSync(globalConfigPath, "utf-8"));
|
|
9909
10096
|
pass("~/.node9/config.json found and valid");
|
|
9910
10097
|
} catch {
|
|
9911
10098
|
fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
|
|
@@ -9913,10 +10100,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
9913
10100
|
} else {
|
|
9914
10101
|
warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
|
|
9915
10102
|
}
|
|
9916
|
-
const projectConfigPath =
|
|
9917
|
-
if (
|
|
10103
|
+
const projectConfigPath = import_path24.default.join(process.cwd(), "node9.config.json");
|
|
10104
|
+
if (import_fs22.default.existsSync(projectConfigPath)) {
|
|
9918
10105
|
try {
|
|
9919
|
-
JSON.parse(
|
|
10106
|
+
JSON.parse(import_fs22.default.readFileSync(projectConfigPath, "utf-8"));
|
|
9920
10107
|
pass("node9.config.json found and valid (project)");
|
|
9921
10108
|
} catch {
|
|
9922
10109
|
fail(
|
|
@@ -9925,8 +10112,8 @@ function registerDoctorCommand(program2, version2) {
|
|
|
9925
10112
|
);
|
|
9926
10113
|
}
|
|
9927
10114
|
}
|
|
9928
|
-
const credsPath =
|
|
9929
|
-
if (
|
|
10115
|
+
const credsPath = import_path24.default.join(homeDir2, ".node9", "credentials.json");
|
|
10116
|
+
if (import_fs22.default.existsSync(credsPath)) {
|
|
9930
10117
|
pass("Cloud credentials found (~/.node9/credentials.json)");
|
|
9931
10118
|
} else {
|
|
9932
10119
|
warn(
|
|
@@ -9935,10 +10122,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
9935
10122
|
);
|
|
9936
10123
|
}
|
|
9937
10124
|
section("Agent Hooks");
|
|
9938
|
-
const claudeSettingsPath =
|
|
9939
|
-
if (
|
|
10125
|
+
const claudeSettingsPath = import_path24.default.join(homeDir2, ".claude", "settings.json");
|
|
10126
|
+
if (import_fs22.default.existsSync(claudeSettingsPath)) {
|
|
9940
10127
|
try {
|
|
9941
|
-
const cs = JSON.parse(
|
|
10128
|
+
const cs = JSON.parse(import_fs22.default.readFileSync(claudeSettingsPath, "utf-8"));
|
|
9942
10129
|
const hasHook = cs.hooks?.PreToolUse?.some(
|
|
9943
10130
|
(m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
|
|
9944
10131
|
);
|
|
@@ -9954,10 +10141,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
9954
10141
|
} else {
|
|
9955
10142
|
warn("Claude Code \u2014 not configured", "Run: node9 setup claude");
|
|
9956
10143
|
}
|
|
9957
|
-
const geminiSettingsPath =
|
|
9958
|
-
if (
|
|
10144
|
+
const geminiSettingsPath = import_path24.default.join(homeDir2, ".gemini", "settings.json");
|
|
10145
|
+
if (import_fs22.default.existsSync(geminiSettingsPath)) {
|
|
9959
10146
|
try {
|
|
9960
|
-
const gs = JSON.parse(
|
|
10147
|
+
const gs = JSON.parse(import_fs22.default.readFileSync(geminiSettingsPath, "utf-8"));
|
|
9961
10148
|
const hasHook = gs.hooks?.BeforeTool?.some(
|
|
9962
10149
|
(m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
|
|
9963
10150
|
);
|
|
@@ -9973,10 +10160,10 @@ function registerDoctorCommand(program2, version2) {
|
|
|
9973
10160
|
} else {
|
|
9974
10161
|
warn("Gemini CLI \u2014 not configured", "Run: node9 setup gemini (skip if not using Gemini)");
|
|
9975
10162
|
}
|
|
9976
|
-
const cursorHooksPath =
|
|
9977
|
-
if (
|
|
10163
|
+
const cursorHooksPath = import_path24.default.join(homeDir2, ".cursor", "hooks.json");
|
|
10164
|
+
if (import_fs22.default.existsSync(cursorHooksPath)) {
|
|
9978
10165
|
try {
|
|
9979
|
-
const cur = JSON.parse(
|
|
10166
|
+
const cur = JSON.parse(import_fs22.default.readFileSync(cursorHooksPath, "utf-8"));
|
|
9980
10167
|
const hasHook = cur.hooks?.preToolUse?.some(
|
|
9981
10168
|
(h) => h.command?.includes("node9") || h.command?.includes("cli.js")
|
|
9982
10169
|
);
|
|
@@ -10014,9 +10201,9 @@ function registerDoctorCommand(program2, version2) {
|
|
|
10014
10201
|
|
|
10015
10202
|
// src/cli/commands/audit.ts
|
|
10016
10203
|
var import_chalk8 = __toESM(require("chalk"));
|
|
10017
|
-
var
|
|
10018
|
-
var
|
|
10019
|
-
var
|
|
10204
|
+
var import_fs23 = __toESM(require("fs"));
|
|
10205
|
+
var import_path25 = __toESM(require("path"));
|
|
10206
|
+
var import_os19 = __toESM(require("os"));
|
|
10020
10207
|
function formatRelativeTime(timestamp) {
|
|
10021
10208
|
const diff = Date.now() - new Date(timestamp).getTime();
|
|
10022
10209
|
const sec = Math.floor(diff / 1e3);
|
|
@@ -10029,14 +10216,14 @@ function formatRelativeTime(timestamp) {
|
|
|
10029
10216
|
}
|
|
10030
10217
|
function registerAuditCommand(program2) {
|
|
10031
10218
|
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) => {
|
|
10032
|
-
const logPath =
|
|
10033
|
-
if (!
|
|
10219
|
+
const logPath = import_path25.default.join(import_os19.default.homedir(), ".node9", "audit.log");
|
|
10220
|
+
if (!import_fs23.default.existsSync(logPath)) {
|
|
10034
10221
|
console.log(
|
|
10035
10222
|
import_chalk8.default.yellow("No audit logs found. Run node9 with an agent to generate entries.")
|
|
10036
10223
|
);
|
|
10037
10224
|
return;
|
|
10038
10225
|
}
|
|
10039
|
-
const raw =
|
|
10226
|
+
const raw = import_fs23.default.readFileSync(logPath, "utf-8");
|
|
10040
10227
|
const lines = raw.split("\n").filter((l) => l.trim() !== "");
|
|
10041
10228
|
let entries = lines.flatMap((line) => {
|
|
10042
10229
|
try {
|
|
@@ -10090,9 +10277,9 @@ function registerAuditCommand(program2) {
|
|
|
10090
10277
|
|
|
10091
10278
|
// src/cli/commands/report.ts
|
|
10092
10279
|
var import_chalk9 = __toESM(require("chalk"));
|
|
10093
|
-
var
|
|
10094
|
-
var
|
|
10095
|
-
var
|
|
10280
|
+
var import_fs24 = __toESM(require("fs"));
|
|
10281
|
+
var import_path26 = __toESM(require("path"));
|
|
10282
|
+
var import_os20 = __toESM(require("os"));
|
|
10096
10283
|
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;
|
|
10097
10284
|
function buildTestTimestamps(allEntries) {
|
|
10098
10285
|
const testTs = /* @__PURE__ */ new Set();
|
|
@@ -10139,8 +10326,8 @@ function getDateRange(period) {
|
|
|
10139
10326
|
}
|
|
10140
10327
|
}
|
|
10141
10328
|
function parseAuditLog(logPath) {
|
|
10142
|
-
if (!
|
|
10143
|
-
const raw =
|
|
10329
|
+
if (!import_fs24.default.existsSync(logPath)) return [];
|
|
10330
|
+
const raw = import_fs24.default.readFileSync(logPath, "utf-8");
|
|
10144
10331
|
return raw.split("\n").flatMap((line) => {
|
|
10145
10332
|
if (!line.trim()) return [];
|
|
10146
10333
|
try {
|
|
@@ -10202,29 +10389,39 @@ function claudeModelPrice(model) {
|
|
|
10202
10389
|
return null;
|
|
10203
10390
|
}
|
|
10204
10391
|
function loadClaudeCost(start, end) {
|
|
10205
|
-
const
|
|
10206
|
-
|
|
10392
|
+
const empty = {
|
|
10393
|
+
total: 0,
|
|
10394
|
+
byDay: /* @__PURE__ */ new Map(),
|
|
10395
|
+
byModel: /* @__PURE__ */ new Map(),
|
|
10396
|
+
inputTokens: 0,
|
|
10397
|
+
cacheReadTokens: 0
|
|
10398
|
+
};
|
|
10399
|
+
const projectsDir = import_path26.default.join(import_os20.default.homedir(), ".claude", "projects");
|
|
10400
|
+
if (!import_fs24.default.existsSync(projectsDir)) return empty;
|
|
10207
10401
|
let dirs;
|
|
10208
10402
|
try {
|
|
10209
|
-
dirs =
|
|
10403
|
+
dirs = import_fs24.default.readdirSync(projectsDir);
|
|
10210
10404
|
} catch {
|
|
10211
|
-
return
|
|
10405
|
+
return empty;
|
|
10212
10406
|
}
|
|
10213
10407
|
let total = 0;
|
|
10408
|
+
let inputTokens = 0;
|
|
10409
|
+
let cacheReadTokens = 0;
|
|
10214
10410
|
const byDay = /* @__PURE__ */ new Map();
|
|
10411
|
+
const byModel = /* @__PURE__ */ new Map();
|
|
10215
10412
|
for (const proj of dirs) {
|
|
10216
|
-
const projPath =
|
|
10413
|
+
const projPath = import_path26.default.join(projectsDir, proj);
|
|
10217
10414
|
let files;
|
|
10218
10415
|
try {
|
|
10219
|
-
const stat =
|
|
10416
|
+
const stat = import_fs24.default.statSync(projPath);
|
|
10220
10417
|
if (!stat.isDirectory()) continue;
|
|
10221
|
-
files =
|
|
10418
|
+
files = import_fs24.default.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
10222
10419
|
} catch {
|
|
10223
10420
|
continue;
|
|
10224
10421
|
}
|
|
10225
10422
|
for (const file of files) {
|
|
10226
10423
|
try {
|
|
10227
|
-
const raw =
|
|
10424
|
+
const raw = import_fs24.default.readFileSync(import_path26.default.join(projPath, file), "utf-8");
|
|
10228
10425
|
for (const line of raw.split("\n")) {
|
|
10229
10426
|
if (!line.trim()) continue;
|
|
10230
10427
|
let entry;
|
|
@@ -10242,24 +10439,32 @@ function loadClaudeCost(start, end) {
|
|
|
10242
10439
|
if (!usage || !model) continue;
|
|
10243
10440
|
const p = claudeModelPrice(model);
|
|
10244
10441
|
if (!p) continue;
|
|
10245
|
-
const
|
|
10442
|
+
const inp = usage.input_tokens ?? 0;
|
|
10443
|
+
const out = usage.output_tokens ?? 0;
|
|
10444
|
+
const cw = usage.cache_creation_input_tokens ?? 0;
|
|
10445
|
+
const cr = usage.cache_read_input_tokens ?? 0;
|
|
10446
|
+
const cost = inp * p.i + out * p.o + cw * p.cw + cr * p.cr;
|
|
10246
10447
|
total += cost;
|
|
10448
|
+
inputTokens += inp;
|
|
10449
|
+
cacheReadTokens += cr;
|
|
10247
10450
|
const dateKey = entry.timestamp.slice(0, 10);
|
|
10248
10451
|
byDay.set(dateKey, (byDay.get(dateKey) ?? 0) + cost);
|
|
10452
|
+
const normModel = model.replace(/@.*$/, "").replace(/-\d{8}$/, "");
|
|
10453
|
+
byModel.set(normModel, (byModel.get(normModel) ?? 0) + cost);
|
|
10249
10454
|
}
|
|
10250
10455
|
} catch {
|
|
10251
10456
|
continue;
|
|
10252
10457
|
}
|
|
10253
10458
|
}
|
|
10254
10459
|
}
|
|
10255
|
-
return { total, byDay };
|
|
10460
|
+
return { total, byDay, byModel, inputTokens, cacheReadTokens };
|
|
10256
10461
|
}
|
|
10257
10462
|
function registerReportCommand(program2) {
|
|
10258
10463
|
program2.command("report").description("Activity and security report \u2014 what Claude did, what was blocked").option("--period <period>", "today | 7d | 30d | month", "7d").option("--no-tests", "exclude test runner calls (npm test, vitest, pytest\u2026) from stats").action((options) => {
|
|
10259
10464
|
const period = ["today", "7d", "30d", "month"].includes(
|
|
10260
10465
|
options.period
|
|
10261
10466
|
) ? options.period : "7d";
|
|
10262
|
-
const logPath =
|
|
10467
|
+
const logPath = import_path26.default.join(import_os20.default.homedir(), ".node9", "audit.log");
|
|
10263
10468
|
const allEntries = parseAuditLog(logPath);
|
|
10264
10469
|
if (allEntries.length === 0) {
|
|
10265
10470
|
console.log(
|
|
@@ -10268,7 +10473,13 @@ function registerReportCommand(program2) {
|
|
|
10268
10473
|
return;
|
|
10269
10474
|
}
|
|
10270
10475
|
const { start, end } = getDateRange(period);
|
|
10271
|
-
const {
|
|
10476
|
+
const {
|
|
10477
|
+
total: costUSD,
|
|
10478
|
+
byDay: costByDay,
|
|
10479
|
+
byModel: costByModel,
|
|
10480
|
+
inputTokens: costInputTokens,
|
|
10481
|
+
cacheReadTokens: costCacheRead
|
|
10482
|
+
} = loadClaudeCost(start, end);
|
|
10272
10483
|
const periodMs = end.getTime() - start.getTime();
|
|
10273
10484
|
const priorEnd = new Date(start.getTime() - 1);
|
|
10274
10485
|
const priorStart = new Date(start.getTime() - periodMs);
|
|
@@ -10370,7 +10581,6 @@ function registerReportCommand(program2) {
|
|
|
10370
10581
|
const blockLabel = blocked > 0 ? import_chalk9.default.red(`\u{1F6D1} ${num(blocked)} blocked`) : import_chalk9.default.dim("\u{1F6D1} 0 blocked");
|
|
10371
10582
|
const dlpLabel = dlpHits > 0 ? import_chalk9.default.yellow(`\u{1F6A8} ${dlpHits} DLP hits`) : import_chalk9.default.dim("\u{1F6A8} 0 DLP hits");
|
|
10372
10583
|
const loopLabel = loopHits > 0 ? import_chalk9.default.yellow(`\u{1F504} ${loopHits} loops`) : import_chalk9.default.dim("\u{1F504} 0 loops");
|
|
10373
|
-
const costLabel = costUSD > 0 ? import_chalk9.default.magenta(`\u{1F4B0} ${fmtCost(costUSD)}`) : import_chalk9.default.dim("\u{1F4B0} \u2013");
|
|
10374
10584
|
const currentRate = total > 0 ? blocked / total : 0;
|
|
10375
10585
|
const trendLabel = (() => {
|
|
10376
10586
|
if (priorBlockRate === null) return import_chalk9.default.dim(`${pct(blocked, total)} block rate`);
|
|
@@ -10383,7 +10593,7 @@ function registerReportCommand(program2) {
|
|
|
10383
10593
|
const ratioLabel = reads > 0 ? import_chalk9.default.dim(`edit/read ${(edits / reads).toFixed(1)}`) : import_chalk9.default.dim("edit/read \u2013");
|
|
10384
10594
|
const testLabel = testPasses + testFails > 0 ? import_chalk9.default.dim("tests ") + import_chalk9.default.green(`${testPasses}\u2713`) + (testFails > 0 ? " " + import_chalk9.default.red(`${testFails}\u2717`) : "") : import_chalk9.default.dim("tests \u2013");
|
|
10385
10595
|
console.log(
|
|
10386
|
-
" " + import_chalk9.default.green(`\u2705 ${num(allowed)} allowed`) + " " + blockLabel + " " + dlpLabel + " " + loopLabel + " " + trendLabel
|
|
10596
|
+
" " + import_chalk9.default.green(`\u2705 ${num(allowed)} allowed`) + " " + blockLabel + " " + dlpLabel + " " + loopLabel + " " + trendLabel
|
|
10387
10597
|
);
|
|
10388
10598
|
console.log(" " + ratioLabel + " " + testLabel);
|
|
10389
10599
|
console.log("");
|
|
@@ -10468,6 +10678,30 @@ function registerReportCommand(program2) {
|
|
|
10468
10678
|
);
|
|
10469
10679
|
}
|
|
10470
10680
|
}
|
|
10681
|
+
if (costUSD > 0) {
|
|
10682
|
+
const periodDays = Math.max(1, Math.ceil((end.getTime() - start.getTime()) / 864e5));
|
|
10683
|
+
const avgPerDay = costUSD / periodDays;
|
|
10684
|
+
const cacheHitPct = costInputTokens + costCacheRead > 0 ? Math.round(costCacheRead / (costInputTokens + costCacheRead) * 100) : 0;
|
|
10685
|
+
const costHeaderRight = [
|
|
10686
|
+
import_chalk9.default.yellow(fmtCost(costUSD)),
|
|
10687
|
+
import_chalk9.default.dim(`avg ${fmtCost(avgPerDay)}/day`),
|
|
10688
|
+
cacheHitPct > 0 ? import_chalk9.default.dim(`${cacheHitPct}% cache hit`) : null
|
|
10689
|
+
].filter(Boolean).join(import_chalk9.default.dim(" \xB7 "));
|
|
10690
|
+
console.log("");
|
|
10691
|
+
console.log(" " + import_chalk9.default.bold("Cost") + " " + costHeaderRight);
|
|
10692
|
+
console.log(" " + import_chalk9.default.dim("\u2500".repeat(Math.min(50, W - 4))));
|
|
10693
|
+
const modelList = [...costByModel.entries()].sort((a, b) => b[1] - a[1]);
|
|
10694
|
+
const maxModelCost = Math.max(...modelList.map(([, v]) => v), 1e-9);
|
|
10695
|
+
const MODEL_LABEL = 22;
|
|
10696
|
+
const MODEL_BAR = Math.max(6, Math.min(20, W - MODEL_LABEL - 12));
|
|
10697
|
+
for (const [model, cost] of modelList) {
|
|
10698
|
+
const label = model.length > MODEL_LABEL - 1 ? model.slice(0, MODEL_LABEL - 2) + "\u2026" : model;
|
|
10699
|
+
const b = colorBar(cost, maxModelCost, MODEL_BAR);
|
|
10700
|
+
console.log(
|
|
10701
|
+
" " + import_chalk9.default.white(label.padEnd(MODEL_LABEL)) + b + " " + import_chalk9.default.yellow(fmtCost(cost))
|
|
10702
|
+
);
|
|
10703
|
+
}
|
|
10704
|
+
}
|
|
10471
10705
|
console.log("");
|
|
10472
10706
|
console.log(
|
|
10473
10707
|
" " + import_chalk9.default.dim("node9 audit --deny") + import_chalk9.default.dim(" \xB7 ") + import_chalk9.default.dim("node9 report --period today|7d|30d|month --no-tests")
|
|
@@ -10542,14 +10776,14 @@ function registerDaemonCommand(program2) {
|
|
|
10542
10776
|
|
|
10543
10777
|
// src/cli/commands/status.ts
|
|
10544
10778
|
var import_chalk11 = __toESM(require("chalk"));
|
|
10545
|
-
var
|
|
10546
|
-
var
|
|
10547
|
-
var
|
|
10779
|
+
var import_fs25 = __toESM(require("fs"));
|
|
10780
|
+
var import_path27 = __toESM(require("path"));
|
|
10781
|
+
var import_os21 = __toESM(require("os"));
|
|
10548
10782
|
init_core();
|
|
10549
10783
|
init_daemon();
|
|
10550
10784
|
function readJson2(filePath) {
|
|
10551
10785
|
try {
|
|
10552
|
-
if (
|
|
10786
|
+
if (import_fs25.default.existsSync(filePath)) return JSON.parse(import_fs25.default.readFileSync(filePath, "utf-8"));
|
|
10553
10787
|
} catch {
|
|
10554
10788
|
}
|
|
10555
10789
|
return null;
|
|
@@ -10614,28 +10848,28 @@ function registerStatusCommand(program2) {
|
|
|
10614
10848
|
console.log("");
|
|
10615
10849
|
const modeLabel = settings.mode === "audit" ? import_chalk11.default.blue("audit") : settings.mode === "strict" ? import_chalk11.default.red("strict") : import_chalk11.default.white("standard");
|
|
10616
10850
|
console.log(` Mode: ${modeLabel}`);
|
|
10617
|
-
const projectConfig =
|
|
10618
|
-
const globalConfig =
|
|
10851
|
+
const projectConfig = import_path27.default.join(process.cwd(), "node9.config.json");
|
|
10852
|
+
const globalConfig = import_path27.default.join(import_os21.default.homedir(), ".node9", "config.json");
|
|
10619
10853
|
console.log(
|
|
10620
|
-
` Local: ${
|
|
10854
|
+
` Local: ${import_fs25.default.existsSync(projectConfig) ? import_chalk11.default.green("Active (node9.config.json)") : import_chalk11.default.gray("Not present")}`
|
|
10621
10855
|
);
|
|
10622
10856
|
console.log(
|
|
10623
|
-
` Global: ${
|
|
10857
|
+
` Global: ${import_fs25.default.existsSync(globalConfig) ? import_chalk11.default.green("Active (~/.node9/config.json)") : import_chalk11.default.gray("Not present")}`
|
|
10624
10858
|
);
|
|
10625
10859
|
if (mergedConfig.policy.sandboxPaths.length > 0) {
|
|
10626
10860
|
console.log(
|
|
10627
10861
|
` Sandbox: ${import_chalk11.default.green(`${mergedConfig.policy.sandboxPaths.length} safe zones active`)}`
|
|
10628
10862
|
);
|
|
10629
10863
|
}
|
|
10630
|
-
const homeDir2 =
|
|
10864
|
+
const homeDir2 = import_os21.default.homedir();
|
|
10631
10865
|
const claudeSettings = readJson2(
|
|
10632
|
-
|
|
10866
|
+
import_path27.default.join(homeDir2, ".claude", "settings.json")
|
|
10633
10867
|
);
|
|
10634
|
-
const claudeConfig = readJson2(
|
|
10868
|
+
const claudeConfig = readJson2(import_path27.default.join(homeDir2, ".claude.json"));
|
|
10635
10869
|
const geminiSettings = readJson2(
|
|
10636
|
-
|
|
10870
|
+
import_path27.default.join(homeDir2, ".gemini", "settings.json")
|
|
10637
10871
|
);
|
|
10638
|
-
const cursorConfig = readJson2(
|
|
10872
|
+
const cursorConfig = readJson2(import_path27.default.join(homeDir2, ".cursor", "mcp.json"));
|
|
10639
10873
|
const agentFound = claudeSettings || claudeConfig || geminiSettings || cursorConfig;
|
|
10640
10874
|
if (agentFound) {
|
|
10641
10875
|
console.log("");
|
|
@@ -10694,9 +10928,9 @@ function registerStatusCommand(program2) {
|
|
|
10694
10928
|
|
|
10695
10929
|
// src/cli/commands/init.ts
|
|
10696
10930
|
var import_chalk12 = __toESM(require("chalk"));
|
|
10697
|
-
var
|
|
10698
|
-
var
|
|
10699
|
-
var
|
|
10931
|
+
var import_fs26 = __toESM(require("fs"));
|
|
10932
|
+
var import_path28 = __toESM(require("path"));
|
|
10933
|
+
var import_os22 = __toESM(require("os"));
|
|
10700
10934
|
var import_https2 = __toESM(require("https"));
|
|
10701
10935
|
init_core();
|
|
10702
10936
|
init_shields();
|
|
@@ -10756,15 +10990,15 @@ function registerInitCommand(program2) {
|
|
|
10756
10990
|
}
|
|
10757
10991
|
console.log("");
|
|
10758
10992
|
}
|
|
10759
|
-
const configPath =
|
|
10760
|
-
if (
|
|
10993
|
+
const configPath = import_path28.default.join(import_os22.default.homedir(), ".node9", "config.json");
|
|
10994
|
+
if (import_fs26.default.existsSync(configPath) && !options.force) {
|
|
10761
10995
|
try {
|
|
10762
|
-
const existing = JSON.parse(
|
|
10996
|
+
const existing = JSON.parse(import_fs26.default.readFileSync(configPath, "utf-8"));
|
|
10763
10997
|
const settings = existing.settings ?? {};
|
|
10764
10998
|
if (settings.mode !== chosenMode) {
|
|
10765
10999
|
settings.mode = chosenMode;
|
|
10766
11000
|
existing.settings = settings;
|
|
10767
|
-
|
|
11001
|
+
import_fs26.default.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
|
|
10768
11002
|
console.log(import_chalk12.default.green(`\u2705 Mode updated: ${chosenMode}`));
|
|
10769
11003
|
} else {
|
|
10770
11004
|
console.log(import_chalk12.default.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
|
|
@@ -10777,9 +11011,9 @@ function registerInitCommand(program2) {
|
|
|
10777
11011
|
...DEFAULT_CONFIG,
|
|
10778
11012
|
settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
|
|
10779
11013
|
};
|
|
10780
|
-
const dir =
|
|
10781
|
-
if (!
|
|
10782
|
-
|
|
11014
|
+
const dir = import_path28.default.dirname(configPath);
|
|
11015
|
+
if (!import_fs26.default.existsSync(dir)) import_fs26.default.mkdirSync(dir, { recursive: true });
|
|
11016
|
+
import_fs26.default.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
|
|
10783
11017
|
console.log(import_chalk12.default.green(`\u2705 Config created: ${configPath}`));
|
|
10784
11018
|
console.log(import_chalk12.default.gray(` Mode: ${chosenMode}`));
|
|
10785
11019
|
}
|
|
@@ -10833,7 +11067,7 @@ function registerInitCommand(program2) {
|
|
|
10833
11067
|
}
|
|
10834
11068
|
|
|
10835
11069
|
// src/cli/commands/undo.ts
|
|
10836
|
-
var
|
|
11070
|
+
var import_path29 = __toESM(require("path"));
|
|
10837
11071
|
var import_chalk14 = __toESM(require("chalk"));
|
|
10838
11072
|
|
|
10839
11073
|
// src/tui/undo-navigator.ts
|
|
@@ -10992,7 +11226,7 @@ function findMatchingCwd(startDir, history) {
|
|
|
10992
11226
|
let dir = startDir;
|
|
10993
11227
|
while (true) {
|
|
10994
11228
|
if (cwds.has(dir)) return dir;
|
|
10995
|
-
const parent =
|
|
11229
|
+
const parent = import_path29.default.dirname(dir);
|
|
10996
11230
|
if (parent === dir) return null;
|
|
10997
11231
|
dir = parent;
|
|
10998
11232
|
}
|
|
@@ -11188,12 +11422,12 @@ init_orchestrator();
|
|
|
11188
11422
|
init_provenance();
|
|
11189
11423
|
|
|
11190
11424
|
// src/mcp-pin.ts
|
|
11191
|
-
var
|
|
11192
|
-
var
|
|
11193
|
-
var
|
|
11425
|
+
var import_fs27 = __toESM(require("fs"));
|
|
11426
|
+
var import_path30 = __toESM(require("path"));
|
|
11427
|
+
var import_os23 = __toESM(require("os"));
|
|
11194
11428
|
var import_crypto9 = __toESM(require("crypto"));
|
|
11195
11429
|
function getPinsFilePath() {
|
|
11196
|
-
return
|
|
11430
|
+
return import_path30.default.join(import_os23.default.homedir(), ".node9", "mcp-pins.json");
|
|
11197
11431
|
}
|
|
11198
11432
|
function hashToolDefinitions(tools) {
|
|
11199
11433
|
const sorted = [...tools].sort((a, b) => {
|
|
@@ -11210,7 +11444,7 @@ function getServerKey(upstreamCommand) {
|
|
|
11210
11444
|
function readMcpPinsSafe() {
|
|
11211
11445
|
const filePath = getPinsFilePath();
|
|
11212
11446
|
try {
|
|
11213
|
-
const raw =
|
|
11447
|
+
const raw = import_fs27.default.readFileSync(filePath, "utf-8");
|
|
11214
11448
|
if (!raw.trim()) {
|
|
11215
11449
|
return { ok: false, reason: "corrupt", detail: "empty file" };
|
|
11216
11450
|
}
|
|
@@ -11234,10 +11468,10 @@ function readMcpPins() {
|
|
|
11234
11468
|
}
|
|
11235
11469
|
function writeMcpPins(data) {
|
|
11236
11470
|
const filePath = getPinsFilePath();
|
|
11237
|
-
|
|
11471
|
+
import_fs27.default.mkdirSync(import_path30.default.dirname(filePath), { recursive: true });
|
|
11238
11472
|
const tmp = `${filePath}.${import_crypto9.default.randomBytes(6).toString("hex")}.tmp`;
|
|
11239
|
-
|
|
11240
|
-
|
|
11473
|
+
import_fs27.default.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
11474
|
+
import_fs27.default.renameSync(tmp, filePath);
|
|
11241
11475
|
}
|
|
11242
11476
|
function checkPin(serverKey, currentHash) {
|
|
11243
11477
|
const result = readMcpPinsSafe();
|
|
@@ -11609,9 +11843,9 @@ function registerMcpGatewayCommand(program2) {
|
|
|
11609
11843
|
|
|
11610
11844
|
// src/mcp-server/index.ts
|
|
11611
11845
|
var import_readline4 = __toESM(require("readline"));
|
|
11612
|
-
var
|
|
11613
|
-
var
|
|
11614
|
-
var
|
|
11846
|
+
var import_fs28 = __toESM(require("fs"));
|
|
11847
|
+
var import_os24 = __toESM(require("os"));
|
|
11848
|
+
var import_path31 = __toESM(require("path"));
|
|
11615
11849
|
init_core();
|
|
11616
11850
|
init_daemon();
|
|
11617
11851
|
init_shields();
|
|
@@ -11786,13 +12020,13 @@ function handleStatus() {
|
|
|
11786
12020
|
lines.push(`Active shields: ${activeShields.length > 0 ? activeShields.join(", ") : "none"}`);
|
|
11787
12021
|
lines.push(`Smart rules: ${config.policy.smartRules.length} loaded`);
|
|
11788
12022
|
lines.push(`DLP: ${config.policy.dlp?.enabled !== false ? "enabled" : "disabled"}`);
|
|
11789
|
-
const projectConfig =
|
|
11790
|
-
const globalConfig =
|
|
12023
|
+
const projectConfig = import_path31.default.join(process.cwd(), "node9.config.json");
|
|
12024
|
+
const globalConfig = import_path31.default.join(import_os24.default.homedir(), ".node9", "config.json");
|
|
11791
12025
|
lines.push(
|
|
11792
|
-
`Project config (node9.config.json): ${
|
|
12026
|
+
`Project config (node9.config.json): ${import_fs28.default.existsSync(projectConfig) ? "present" : "not found"}`
|
|
11793
12027
|
);
|
|
11794
12028
|
lines.push(
|
|
11795
|
-
`Global config (~/.node9/config.json): ${
|
|
12029
|
+
`Global config (~/.node9/config.json): ${import_fs28.default.existsSync(globalConfig) ? "present" : "not found"}`
|
|
11796
12030
|
);
|
|
11797
12031
|
return lines.join("\n");
|
|
11798
12032
|
}
|
|
@@ -11866,21 +12100,21 @@ function handleShieldDisable(args) {
|
|
|
11866
12100
|
writeActiveShields(active.filter((s) => s !== name));
|
|
11867
12101
|
return `Shield "${name}" disabled.`;
|
|
11868
12102
|
}
|
|
11869
|
-
var GLOBAL_CONFIG_PATH2 =
|
|
12103
|
+
var GLOBAL_CONFIG_PATH2 = import_path31.default.join(import_os24.default.homedir(), ".node9", "config.json");
|
|
11870
12104
|
var APPROVER_CHANNELS = ["native", "browser", "cloud", "terminal"];
|
|
11871
12105
|
function readGlobalConfigRaw() {
|
|
11872
12106
|
try {
|
|
11873
|
-
if (
|
|
11874
|
-
return JSON.parse(
|
|
12107
|
+
if (import_fs28.default.existsSync(GLOBAL_CONFIG_PATH2)) {
|
|
12108
|
+
return JSON.parse(import_fs28.default.readFileSync(GLOBAL_CONFIG_PATH2, "utf-8"));
|
|
11875
12109
|
}
|
|
11876
12110
|
} catch {
|
|
11877
12111
|
}
|
|
11878
12112
|
return {};
|
|
11879
12113
|
}
|
|
11880
12114
|
function writeGlobalConfigRaw(data) {
|
|
11881
|
-
const dir =
|
|
11882
|
-
if (!
|
|
11883
|
-
|
|
12115
|
+
const dir = import_path31.default.dirname(GLOBAL_CONFIG_PATH2);
|
|
12116
|
+
if (!import_fs28.default.existsSync(dir)) import_fs28.default.mkdirSync(dir, { recursive: true });
|
|
12117
|
+
import_fs28.default.writeFileSync(GLOBAL_CONFIG_PATH2, JSON.stringify(data, null, 2) + "\n");
|
|
11884
12118
|
}
|
|
11885
12119
|
function handleApproverList() {
|
|
11886
12120
|
const config = getConfig();
|
|
@@ -11923,9 +12157,9 @@ function handleApproverSet(args) {
|
|
|
11923
12157
|
}
|
|
11924
12158
|
function handleAuditGet(args) {
|
|
11925
12159
|
const limit = Math.min(typeof args.limit === "number" ? args.limit : 20, 100);
|
|
11926
|
-
const auditPath =
|
|
11927
|
-
if (!
|
|
11928
|
-
const lines =
|
|
12160
|
+
const auditPath = import_path31.default.join(import_os24.default.homedir(), ".node9", "audit.log");
|
|
12161
|
+
if (!import_fs28.default.existsSync(auditPath)) return "No audit log found.";
|
|
12162
|
+
const lines = import_fs28.default.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
11929
12163
|
const recent = lines.slice(-limit);
|
|
11930
12164
|
const entries = recent.map((line) => {
|
|
11931
12165
|
try {
|
|
@@ -12245,20 +12479,20 @@ function registerMcpPinCommand(program2) {
|
|
|
12245
12479
|
|
|
12246
12480
|
// src/cli.ts
|
|
12247
12481
|
var { version } = JSON.parse(
|
|
12248
|
-
|
|
12482
|
+
import_fs31.default.readFileSync(import_path34.default.join(__dirname, "../package.json"), "utf-8")
|
|
12249
12483
|
);
|
|
12250
12484
|
var program = new import_commander.Command();
|
|
12251
12485
|
program.name("node9").description("The Sudo Command for AI Agents").version(version);
|
|
12252
12486
|
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) => {
|
|
12253
12487
|
const DEFAULT_API_URL = "https://api.node9.ai/api/v1/intercept";
|
|
12254
|
-
const credPath =
|
|
12255
|
-
if (!
|
|
12256
|
-
|
|
12488
|
+
const credPath = import_path34.default.join(import_os27.default.homedir(), ".node9", "credentials.json");
|
|
12489
|
+
if (!import_fs31.default.existsSync(import_path34.default.dirname(credPath)))
|
|
12490
|
+
import_fs31.default.mkdirSync(import_path34.default.dirname(credPath), { recursive: true });
|
|
12257
12491
|
const profileName = options.profile || "default";
|
|
12258
12492
|
let existingCreds = {};
|
|
12259
12493
|
try {
|
|
12260
|
-
if (
|
|
12261
|
-
const raw = JSON.parse(
|
|
12494
|
+
if (import_fs31.default.existsSync(credPath)) {
|
|
12495
|
+
const raw = JSON.parse(import_fs31.default.readFileSync(credPath, "utf-8"));
|
|
12262
12496
|
if (raw.apiKey) {
|
|
12263
12497
|
existingCreds = {
|
|
12264
12498
|
default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL }
|
|
@@ -12270,13 +12504,13 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
12270
12504
|
} catch {
|
|
12271
12505
|
}
|
|
12272
12506
|
existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL };
|
|
12273
|
-
|
|
12507
|
+
import_fs31.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
|
|
12274
12508
|
if (profileName === "default") {
|
|
12275
|
-
const configPath =
|
|
12509
|
+
const configPath = import_path34.default.join(import_os27.default.homedir(), ".node9", "config.json");
|
|
12276
12510
|
let config = {};
|
|
12277
12511
|
try {
|
|
12278
|
-
if (
|
|
12279
|
-
config = JSON.parse(
|
|
12512
|
+
if (import_fs31.default.existsSync(configPath))
|
|
12513
|
+
config = JSON.parse(import_fs31.default.readFileSync(configPath, "utf-8"));
|
|
12280
12514
|
} catch {
|
|
12281
12515
|
}
|
|
12282
12516
|
if (!config.settings || typeof config.settings !== "object") config.settings = {};
|
|
@@ -12291,9 +12525,9 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
12291
12525
|
approvers.cloud = false;
|
|
12292
12526
|
}
|
|
12293
12527
|
s.approvers = approvers;
|
|
12294
|
-
if (!
|
|
12295
|
-
|
|
12296
|
-
|
|
12528
|
+
if (!import_fs31.default.existsSync(import_path34.default.dirname(configPath)))
|
|
12529
|
+
import_fs31.default.mkdirSync(import_path34.default.dirname(configPath), { recursive: true });
|
|
12530
|
+
import_fs31.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
12297
12531
|
}
|
|
12298
12532
|
if (options.profile && profileName !== "default") {
|
|
12299
12533
|
console.log(import_chalk20.default.green(`\u2705 Profile "${profileName}" saved`));
|
|
@@ -12387,15 +12621,15 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
12387
12621
|
}
|
|
12388
12622
|
}
|
|
12389
12623
|
if (options.purge) {
|
|
12390
|
-
const node9Dir =
|
|
12391
|
-
if (
|
|
12624
|
+
const node9Dir = import_path34.default.join(import_os27.default.homedir(), ".node9");
|
|
12625
|
+
if (import_fs31.default.existsSync(node9Dir)) {
|
|
12392
12626
|
const confirmed = await (0, import_prompts2.confirm)({
|
|
12393
12627
|
message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
|
|
12394
12628
|
default: false
|
|
12395
12629
|
});
|
|
12396
12630
|
if (confirmed) {
|
|
12397
|
-
|
|
12398
|
-
if (
|
|
12631
|
+
import_fs31.default.rmSync(node9Dir, { recursive: true });
|
|
12632
|
+
if (import_fs31.default.existsSync(node9Dir)) {
|
|
12399
12633
|
console.error(
|
|
12400
12634
|
import_chalk20.default.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
|
|
12401
12635
|
);
|
|
@@ -12536,14 +12770,14 @@ Claude Code spawns this command every ~300ms and writes a JSON payload to stdin.
|
|
|
12536
12770
|
Run "node9 addto claude" to register it as the statusLine.`
|
|
12537
12771
|
).argument("[subcommand]", 'Optional: "debug on" / "debug off" to toggle stdin logging').argument("[state]", 'on|off \u2014 used with "debug" subcommand').action(async (subcommand, state) => {
|
|
12538
12772
|
if (subcommand === "debug") {
|
|
12539
|
-
const flagFile =
|
|
12773
|
+
const flagFile = import_path34.default.join(import_os27.default.homedir(), ".node9", "hud-debug");
|
|
12540
12774
|
if (state === "on") {
|
|
12541
|
-
|
|
12542
|
-
|
|
12775
|
+
import_fs31.default.mkdirSync(import_path34.default.dirname(flagFile), { recursive: true });
|
|
12776
|
+
import_fs31.default.writeFileSync(flagFile, "");
|
|
12543
12777
|
console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
|
|
12544
12778
|
console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
|
|
12545
12779
|
} else if (state === "off") {
|
|
12546
|
-
if (
|
|
12780
|
+
if (import_fs31.default.existsSync(flagFile)) import_fs31.default.unlinkSync(flagFile);
|
|
12547
12781
|
console.log("HUD debug logging disabled.");
|
|
12548
12782
|
} else {
|
|
12549
12783
|
console.error("Usage: node9 hud debug on|off");
|
|
@@ -12650,9 +12884,9 @@ if (process.argv[2] !== "daemon") {
|
|
|
12650
12884
|
const isCheckHook = process.argv[2] === "check";
|
|
12651
12885
|
if (isCheckHook) {
|
|
12652
12886
|
if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
|
|
12653
|
-
const logPath =
|
|
12887
|
+
const logPath = import_path34.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
|
|
12654
12888
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
12655
|
-
|
|
12889
|
+
import_fs31.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
|
|
12656
12890
|
`);
|
|
12657
12891
|
}
|
|
12658
12892
|
process.exit(0);
|