@node9/proxy 1.13.0 → 1.13.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/README.md +22 -19
- package/dist/cli.js +430 -200
- package/dist/cli.mjs +428 -198
- package/dist/index.js +9 -0
- package/dist/index.mjs +9 -0
- 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 path46 = issue.path.length > 0 ? issue.path.join(".") : "root";
|
|
172
|
+
return ` \u2022 ${path46}: ${issue.message}`;
|
|
173
173
|
});
|
|
174
174
|
return {
|
|
175
175
|
sanitized,
|
|
@@ -1220,7 +1220,26 @@ function scanText(text) {
|
|
|
1220
1220
|
}
|
|
1221
1221
|
return null;
|
|
1222
1222
|
}
|
|
1223
|
-
|
|
1223
|
+
function redactText(text) {
|
|
1224
|
+
const t = text.length > MAX_STRING_BYTES ? text.slice(0, MAX_STRING_BYTES) : text;
|
|
1225
|
+
let result = t;
|
|
1226
|
+
const found = [];
|
|
1227
|
+
const lower = t.toLowerCase();
|
|
1228
|
+
for (const { pattern, globalRegex } of DLP_PATTERNS_GLOBAL) {
|
|
1229
|
+
if (pattern.keywords && !pattern.keywords.some((kw) => lower.includes(kw.toLowerCase()))) {
|
|
1230
|
+
continue;
|
|
1231
|
+
}
|
|
1232
|
+
result = result.replace(globalRegex, (match) => {
|
|
1233
|
+
if (DLP_STOPWORDS.some((sw) => match.toLowerCase().includes(sw))) return match;
|
|
1234
|
+
if (pattern.minEntropy !== void 0 && shannonEntropy(match) < pattern.minEntropy)
|
|
1235
|
+
return match;
|
|
1236
|
+
if (!found.includes(pattern.name)) found.push(pattern.name);
|
|
1237
|
+
return `[node9-redacted:${pattern.name}]`;
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
1240
|
+
return { result, found };
|
|
1241
|
+
}
|
|
1242
|
+
var import_fs4, import_path4, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES;
|
|
1224
1243
|
var init_dlp = __esm({
|
|
1225
1244
|
"src/dlp.ts"() {
|
|
1226
1245
|
"use strict";
|
|
@@ -1640,6 +1659,15 @@ var init_dlp = __esm({
|
|
|
1640
1659
|
keywords: ["age-secret-key-"]
|
|
1641
1660
|
}
|
|
1642
1661
|
];
|
|
1662
|
+
DLP_PATTERNS_GLOBAL = DLP_PATTERNS.map(
|
|
1663
|
+
(p) => ({
|
|
1664
|
+
pattern: p,
|
|
1665
|
+
globalRegex: new RegExp(
|
|
1666
|
+
p.regex.source,
|
|
1667
|
+
p.regex.flags.includes("g") ? p.regex.flags : p.regex.flags + "g"
|
|
1668
|
+
)
|
|
1669
|
+
})
|
|
1670
|
+
);
|
|
1643
1671
|
SENSITIVE_PATH_PATTERNS = [
|
|
1644
1672
|
/[/\\]\.ssh[/\\]/i,
|
|
1645
1673
|
/[/\\]\.aws[/\\]/i,
|
|
@@ -2201,9 +2229,9 @@ function matchesPattern(text, patterns) {
|
|
|
2201
2229
|
const withoutDotSlash = text.replace(/^\.\//, "");
|
|
2202
2230
|
return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
|
|
2203
2231
|
}
|
|
2204
|
-
function getNestedValue(obj,
|
|
2232
|
+
function getNestedValue(obj, path46) {
|
|
2205
2233
|
if (!obj || typeof obj !== "object") return null;
|
|
2206
|
-
return
|
|
2234
|
+
return path46.split(".").reduce((prev, curr) => prev?.[curr], obj);
|
|
2207
2235
|
}
|
|
2208
2236
|
function normalizeCommandForPolicy(command) {
|
|
2209
2237
|
try {
|
|
@@ -9176,14 +9204,30 @@ var init_ui = __esm({
|
|
|
9176
9204
|
// \u2500\u2500 Leaks (shown first: credential exposure is highest-severity) \u2500\u2500\u2500\u2500\u2500
|
|
9177
9205
|
if (leaksByPattern.length) {
|
|
9178
9206
|
html +=
|
|
9179
|
-
'<div class="scan-rule-section-label" style="color:#e5534b">\u{1F511} Credential Leaks \u2014 secrets found in
|
|
9207
|
+
'<div class="scan-rule-section-label" style="color:#e5534b">\u{1F511} Credential Leaks \u2014 secrets found in history or shell config</div>';
|
|
9180
9208
|
html += leaksByPattern
|
|
9181
9209
|
.map(([pattern, group]) => {
|
|
9182
9210
|
const count = group.length;
|
|
9183
9211
|
const barPct = Math.round((count / maxBar) * 100);
|
|
9184
9212
|
const detailId = 'detail-' + Math.random().toString(36).slice(2);
|
|
9185
9213
|
const rows = group
|
|
9186
|
-
.map((l) =>
|
|
9214
|
+
.map((l) => {
|
|
9215
|
+
const agentBadge =
|
|
9216
|
+
l.agent === 'gemini'
|
|
9217
|
+
? '[Gemini]'
|
|
9218
|
+
: l.agent === 'codex'
|
|
9219
|
+
? '[Codex]'
|
|
9220
|
+
: l.agent === 'shell'
|
|
9221
|
+
? '[Shell]'
|
|
9222
|
+
: '[Claude]';
|
|
9223
|
+
return findingRow(
|
|
9224
|
+
l.timestamp,
|
|
9225
|
+
'<span style="opacity:0.6;margin-right:6px">' +
|
|
9226
|
+
esc(agentBadge) +
|
|
9227
|
+
'</span>' +
|
|
9228
|
+
esc(l.redactedSample || '')
|
|
9229
|
+
);
|
|
9230
|
+
})
|
|
9187
9231
|
.join('');
|
|
9188
9232
|
return (
|
|
9189
9233
|
'<div class="scan-rule-row" onclick="var d=document.getElementById(\\'' +
|
|
@@ -13695,20 +13739,20 @@ function getModelContextLimit(model) {
|
|
|
13695
13739
|
return 2e5;
|
|
13696
13740
|
}
|
|
13697
13741
|
function readSessionUsage() {
|
|
13698
|
-
const projectsDir =
|
|
13699
|
-
if (!
|
|
13742
|
+
const projectsDir = import_path43.default.join(import_os36.default.homedir(), ".claude", "projects");
|
|
13743
|
+
if (!import_fs40.default.existsSync(projectsDir)) return null;
|
|
13700
13744
|
let latestFile = null;
|
|
13701
13745
|
let latestMtime = 0;
|
|
13702
13746
|
try {
|
|
13703
|
-
for (const dir of
|
|
13704
|
-
const dirPath =
|
|
13747
|
+
for (const dir of import_fs40.default.readdirSync(projectsDir)) {
|
|
13748
|
+
const dirPath = import_path43.default.join(projectsDir, dir);
|
|
13705
13749
|
try {
|
|
13706
|
-
if (!
|
|
13707
|
-
for (const file of
|
|
13750
|
+
if (!import_fs40.default.statSync(dirPath).isDirectory()) continue;
|
|
13751
|
+
for (const file of import_fs40.default.readdirSync(dirPath)) {
|
|
13708
13752
|
if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
|
|
13709
|
-
const filePath =
|
|
13753
|
+
const filePath = import_path43.default.join(dirPath, file);
|
|
13710
13754
|
try {
|
|
13711
|
-
const mtime =
|
|
13755
|
+
const mtime = import_fs40.default.statSync(filePath).mtimeMs;
|
|
13712
13756
|
if (mtime > latestMtime) {
|
|
13713
13757
|
latestMtime = mtime;
|
|
13714
13758
|
latestFile = filePath;
|
|
@@ -13723,7 +13767,7 @@ function readSessionUsage() {
|
|
|
13723
13767
|
}
|
|
13724
13768
|
if (!latestFile) return null;
|
|
13725
13769
|
try {
|
|
13726
|
-
const lines =
|
|
13770
|
+
const lines = import_fs40.default.readFileSync(latestFile, "utf-8").split("\n");
|
|
13727
13771
|
let lastModel = "";
|
|
13728
13772
|
let lastInput = 0;
|
|
13729
13773
|
let lastOutput = 0;
|
|
@@ -13748,10 +13792,10 @@ function readSessionUsage() {
|
|
|
13748
13792
|
}
|
|
13749
13793
|
}
|
|
13750
13794
|
function formatContextStat(stat) {
|
|
13751
|
-
const pctColor = stat.fillPct >= 80 ?
|
|
13795
|
+
const pctColor = stat.fillPct >= 80 ? import_chalk26.default.red : stat.fillPct >= 50 ? import_chalk26.default.yellow : import_chalk26.default.cyan;
|
|
13752
13796
|
const k = (n) => `${Math.round(n / 1e3)}k`;
|
|
13753
13797
|
const modelShort = stat.model.replace(/@.*$/, "").replace(/-\d{8}$/, "").replace(/^claude-/, "");
|
|
13754
|
-
return
|
|
13798
|
+
return import_chalk26.default.dim("ctx: ") + pctColor(`${stat.fillPct}%`) + import_chalk26.default.dim(
|
|
13755
13799
|
` (${k(stat.inputTokens)}/${k(getModelContextLimit(stat.model))} out ${k(stat.outputTokens)} \xB7 ${modelShort})`
|
|
13756
13800
|
);
|
|
13757
13801
|
}
|
|
@@ -13767,28 +13811,28 @@ function wrappedLineCount(text) {
|
|
|
13767
13811
|
function agentLabel(agent) {
|
|
13768
13812
|
if (!agent || agent === "Terminal") return "";
|
|
13769
13813
|
const short = agent === "Claude Code" ? "Claude" : agent === "Gemini CLI" ? "Gemini" : agent === "Unknown Agent" ? "" : agent.split(" ")[0];
|
|
13770
|
-
return short ?
|
|
13814
|
+
return short ? import_chalk26.default.dim(`[${short}] `) : "";
|
|
13771
13815
|
}
|
|
13772
13816
|
function formatBase(activity) {
|
|
13773
13817
|
const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
|
|
13774
13818
|
const icon = getIcon(activity.tool);
|
|
13775
13819
|
const toolName = activity.tool.slice(0, 16).padEnd(16);
|
|
13776
|
-
const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(
|
|
13820
|
+
const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(import_os36.default.homedir(), "~");
|
|
13777
13821
|
const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
|
|
13778
|
-
return `${
|
|
13822
|
+
return `${import_chalk26.default.gray(time)} ${icon} ${agentLabel(activity.agent)}${import_chalk26.default.white.bold(toolName)} ${import_chalk26.default.dim(argsPreview)}`;
|
|
13779
13823
|
}
|
|
13780
13824
|
function renderResult(activity, result) {
|
|
13781
13825
|
const base = formatBase(activity);
|
|
13782
13826
|
let status;
|
|
13783
13827
|
if (result.status === "allow") {
|
|
13784
|
-
status =
|
|
13828
|
+
status = import_chalk26.default.green("\u2713 ALLOW");
|
|
13785
13829
|
} else if (result.status === "dlp") {
|
|
13786
|
-
status =
|
|
13830
|
+
status = import_chalk26.default.bgRed.white.bold(" \u{1F6E1}\uFE0F DLP ");
|
|
13787
13831
|
} else {
|
|
13788
|
-
status =
|
|
13832
|
+
status = import_chalk26.default.red("\u2717 BLOCK");
|
|
13789
13833
|
}
|
|
13790
13834
|
const cost = result.costEstimate ?? activity.costEstimate;
|
|
13791
|
-
const costSuffix = cost == null ? "" :
|
|
13835
|
+
const costSuffix = cost == null ? "" : import_chalk26.default.dim(` ~$${cost >= 1e-3 ? cost.toFixed(3) : "0.000"}`);
|
|
13792
13836
|
if (process.stdout.isTTY) {
|
|
13793
13837
|
if (pendingShownForId === activity.id && pendingWrappedLines > 1) {
|
|
13794
13838
|
import_readline5.default.moveCursor(process.stdout, 0, -(pendingWrappedLines - 1));
|
|
@@ -13805,19 +13849,19 @@ function renderResult(activity, result) {
|
|
|
13805
13849
|
}
|
|
13806
13850
|
function renderPending(activity) {
|
|
13807
13851
|
if (!process.stdout.isTTY) return;
|
|
13808
|
-
const line = `${formatBase(activity)} ${
|
|
13852
|
+
const line = `${formatBase(activity)} ${import_chalk26.default.yellow("\u25CF \u2026")}`;
|
|
13809
13853
|
pendingShownForId = activity.id;
|
|
13810
13854
|
pendingWrappedLines = wrappedLineCount(line);
|
|
13811
13855
|
process.stdout.write(`${line}\r`);
|
|
13812
13856
|
}
|
|
13813
13857
|
async function ensureDaemon() {
|
|
13814
13858
|
let pidPort = null;
|
|
13815
|
-
if (
|
|
13859
|
+
if (import_fs40.default.existsSync(PID_FILE)) {
|
|
13816
13860
|
try {
|
|
13817
|
-
const { port } = JSON.parse(
|
|
13861
|
+
const { port } = JSON.parse(import_fs40.default.readFileSync(PID_FILE, "utf-8"));
|
|
13818
13862
|
pidPort = port;
|
|
13819
13863
|
} catch {
|
|
13820
|
-
console.error(
|
|
13864
|
+
console.error(import_chalk26.default.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
|
|
13821
13865
|
}
|
|
13822
13866
|
}
|
|
13823
13867
|
const checkPort = pidPort ?? DAEMON_PORT;
|
|
@@ -13828,7 +13872,7 @@ async function ensureDaemon() {
|
|
|
13828
13872
|
if (res.ok) return checkPort;
|
|
13829
13873
|
} catch {
|
|
13830
13874
|
}
|
|
13831
|
-
console.log(
|
|
13875
|
+
console.log(import_chalk26.default.dim("\u{1F6E1}\uFE0F Starting Node9 daemon..."));
|
|
13832
13876
|
const child = (0, import_child_process16.spawn)(process.execPath, [process.argv[1], "daemon"], {
|
|
13833
13877
|
detached: true,
|
|
13834
13878
|
stdio: "ignore",
|
|
@@ -13845,7 +13889,7 @@ async function ensureDaemon() {
|
|
|
13845
13889
|
} catch {
|
|
13846
13890
|
}
|
|
13847
13891
|
}
|
|
13848
|
-
console.error(
|
|
13892
|
+
console.error(import_chalk26.default.red("\u274C Daemon failed to start. Try: node9 daemon start"));
|
|
13849
13893
|
process.exit(1);
|
|
13850
13894
|
}
|
|
13851
13895
|
function postDecisionHttp(id, decision, csrfToken, port, opts) {
|
|
@@ -13911,7 +13955,7 @@ function buildCardLines(req, localCount = 0) {
|
|
|
13911
13955
|
const severityIcon = isBlock ? `${RED}\u{1F6D1}` : `${YELLOW}\u26A0 `;
|
|
13912
13956
|
const rawDesc = req.riskMetadata?.ruleDescription ?? "";
|
|
13913
13957
|
const description = rawDesc ? cleanReason(rawDesc) : "";
|
|
13914
|
-
const agentSuffix = req.agent && req.agent !== "Terminal" ? ` ${RESET2}${
|
|
13958
|
+
const agentSuffix = req.agent && req.agent !== "Terminal" ? ` ${RESET2}${import_chalk26.default.dim(`(${req.agent})`)}` : "";
|
|
13915
13959
|
const lines = [
|
|
13916
13960
|
``,
|
|
13917
13961
|
`${BOLD2}${CYAN}\u2554\u2550\u2550 Node9 Approval Required \u2550\u2550\u2557${RESET2}`,
|
|
@@ -13967,9 +14011,9 @@ function buildRecoveryCardLines(req) {
|
|
|
13967
14011
|
];
|
|
13968
14012
|
}
|
|
13969
14013
|
function readApproversFromDisk() {
|
|
13970
|
-
const configPath =
|
|
14014
|
+
const configPath = import_path43.default.join(import_os36.default.homedir(), ".node9", "config.json");
|
|
13971
14015
|
try {
|
|
13972
|
-
const raw = JSON.parse(
|
|
14016
|
+
const raw = JSON.parse(import_fs40.default.readFileSync(configPath, "utf-8"));
|
|
13973
14017
|
const settings = raw.settings ?? {};
|
|
13974
14018
|
return settings.approvers ?? {};
|
|
13975
14019
|
} catch {
|
|
@@ -13980,20 +14024,20 @@ function approverStatusLine() {
|
|
|
13980
14024
|
const a = readApproversFromDisk();
|
|
13981
14025
|
const fmt = (label, key) => {
|
|
13982
14026
|
const on = a[key] !== false;
|
|
13983
|
-
return `[${key[0]}]${label.slice(1)} ${on ?
|
|
14027
|
+
return `[${key[0]}]${label.slice(1)} ${on ? import_chalk26.default.green("\u2713") : import_chalk26.default.dim("\u2717")}`;
|
|
13984
14028
|
};
|
|
13985
14029
|
return `${fmt("native", "native")} ${fmt("browser", "browser")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
|
|
13986
14030
|
}
|
|
13987
14031
|
function toggleApprover(channel) {
|
|
13988
|
-
const configPath =
|
|
14032
|
+
const configPath = import_path43.default.join(import_os36.default.homedir(), ".node9", "config.json");
|
|
13989
14033
|
try {
|
|
13990
|
-
const raw = JSON.parse(
|
|
14034
|
+
const raw = JSON.parse(import_fs40.default.readFileSync(configPath, "utf-8"));
|
|
13991
14035
|
const settings = raw.settings ?? {};
|
|
13992
14036
|
const approvers = settings.approvers ?? {};
|
|
13993
14037
|
approvers[channel] = approvers[channel] === false;
|
|
13994
14038
|
settings.approvers = approvers;
|
|
13995
14039
|
raw.settings = settings;
|
|
13996
|
-
|
|
14040
|
+
import_fs40.default.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
|
|
13997
14041
|
} catch (err2) {
|
|
13998
14042
|
process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
|
|
13999
14043
|
`);
|
|
@@ -14025,7 +14069,7 @@ async function startTail(options = {}) {
|
|
|
14025
14069
|
req2.end();
|
|
14026
14070
|
});
|
|
14027
14071
|
if (result.ok) {
|
|
14028
|
-
console.log(
|
|
14072
|
+
console.log(import_chalk26.default.green("\u2713 Flight Recorder buffer cleared."));
|
|
14029
14073
|
} else if (result.code === "ECONNREFUSED") {
|
|
14030
14074
|
throw new Error("Daemon is not running. Start it with: node9 daemon start");
|
|
14031
14075
|
} else if (result.code === "ETIMEDOUT") {
|
|
@@ -14069,7 +14113,7 @@ async function startTail(options = {}) {
|
|
|
14069
14113
|
const channel = name === "n" ? "native" : name === "b" ? "browser" : name === "c" ? "cloud" : name === "t" ? "terminal" : null;
|
|
14070
14114
|
if (channel) {
|
|
14071
14115
|
toggleApprover(channel);
|
|
14072
|
-
console.log(
|
|
14116
|
+
console.log(import_chalk26.default.dim(` Approvers: ${approverStatusLine()}`));
|
|
14073
14117
|
}
|
|
14074
14118
|
};
|
|
14075
14119
|
process.stdin.on("keypress", idleKeypressHandler);
|
|
@@ -14135,7 +14179,7 @@ async function startTail(options = {}) {
|
|
|
14135
14179
|
localAllowCounts.get(req2.toolName) ?? 0
|
|
14136
14180
|
)
|
|
14137
14181
|
);
|
|
14138
|
-
const decisionStamp = action === "always-allow" ?
|
|
14182
|
+
const decisionStamp = action === "always-allow" ? import_chalk26.default.yellow("\u2605 ALWAYS ALLOW") : action === "trust" ? import_chalk26.default.cyan("\u23F1 TRUST 30m") : action === "allow" ? import_chalk26.default.green("\u2713 ALLOWED") : action === "redirect" ? import_chalk26.default.yellow("\u21A9 REDIRECT AI") : import_chalk26.default.red("\u2717 DENIED");
|
|
14139
14183
|
stampedLines.push(` ${BOLD2}\u2192${RESET2} ${decisionStamp} ${GRAY}(terminal)${RESET2}`, ``);
|
|
14140
14184
|
for (const line of stampedLines) process.stdout.write(line + "\n");
|
|
14141
14185
|
process.stdout.write(SHOW_CURSOR);
|
|
@@ -14163,8 +14207,8 @@ async function startTail(options = {}) {
|
|
|
14163
14207
|
}
|
|
14164
14208
|
postDecisionHttp(req2.id, httpDecision, csrfToken, port, httpOpts).catch((err2) => {
|
|
14165
14209
|
try {
|
|
14166
|
-
|
|
14167
|
-
|
|
14210
|
+
import_fs40.default.appendFileSync(
|
|
14211
|
+
import_path43.default.join(import_os36.default.homedir(), ".node9", "hook-debug.log"),
|
|
14168
14212
|
`[tail] POST /decision failed: ${String(err2)}
|
|
14169
14213
|
`
|
|
14170
14214
|
);
|
|
@@ -14186,7 +14230,7 @@ async function startTail(options = {}) {
|
|
|
14186
14230
|
);
|
|
14187
14231
|
const stampedLines = buildCardLines(req2, priorCount);
|
|
14188
14232
|
if (externalDecision) {
|
|
14189
|
-
const source = externalDecision === "allow" ?
|
|
14233
|
+
const source = externalDecision === "allow" ? import_chalk26.default.green("\u2713 ALLOWED") : import_chalk26.default.red("\u2717 DENIED");
|
|
14190
14234
|
stampedLines.push(` ${BOLD2}\u2192${RESET2} ${source} ${GRAY}(external)${RESET2}`, ``);
|
|
14191
14235
|
}
|
|
14192
14236
|
for (const line of stampedLines) process.stdout.write(line + "\n");
|
|
@@ -14245,31 +14289,31 @@ async function startTail(options = {}) {
|
|
|
14245
14289
|
}
|
|
14246
14290
|
} catch {
|
|
14247
14291
|
}
|
|
14248
|
-
const auditLog =
|
|
14292
|
+
const auditLog = import_path43.default.join(import_os36.default.homedir(), ".node9", "audit.log");
|
|
14249
14293
|
try {
|
|
14250
|
-
const unackedDlp =
|
|
14294
|
+
const unackedDlp = import_fs40.default.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
|
|
14251
14295
|
if (unackedDlp > 0) {
|
|
14252
14296
|
console.log("");
|
|
14253
14297
|
console.log(
|
|
14254
|
-
|
|
14298
|
+
import_chalk26.default.bgRed.white.bold(
|
|
14255
14299
|
` \u26A0\uFE0F DLP ALERT: ${unackedDlp} secret${unackedDlp !== 1 ? "s" : ""} found in Claude response text \u2014 run: node9 dlp `
|
|
14256
14300
|
)
|
|
14257
14301
|
);
|
|
14258
14302
|
}
|
|
14259
14303
|
} catch {
|
|
14260
14304
|
}
|
|
14261
|
-
console.log(
|
|
14262
|
-
\u{1F6F0}\uFE0F Node9 tail `) +
|
|
14305
|
+
console.log(import_chalk26.default.cyan.bold(`
|
|
14306
|
+
\u{1F6F0}\uFE0F Node9 tail `) + import_chalk26.default.dim(`\u2192 ${dashboardUrl}`));
|
|
14263
14307
|
if (canApprove) {
|
|
14264
|
-
console.log(
|
|
14265
|
-
console.log(
|
|
14308
|
+
console.log(import_chalk26.default.dim("Card: [\u21B5/y] Allow [n] Deny [a] Always [t] Trust 30m"));
|
|
14309
|
+
console.log(import_chalk26.default.dim(`Approvers (toggle): ${approverStatusLine()} [q] quit`));
|
|
14266
14310
|
}
|
|
14267
14311
|
const ctxStat = readSessionUsage();
|
|
14268
14312
|
if (ctxStat) console.log(" " + formatContextStat(ctxStat));
|
|
14269
14313
|
if (options.history) {
|
|
14270
|
-
console.log(
|
|
14314
|
+
console.log(import_chalk26.default.dim("Showing history + live events.\n"));
|
|
14271
14315
|
} else {
|
|
14272
|
-
console.log(
|
|
14316
|
+
console.log(import_chalk26.default.dim("Showing live events only. Use --history to include past.\n"));
|
|
14273
14317
|
}
|
|
14274
14318
|
process.on("SIGINT", () => {
|
|
14275
14319
|
exitIdleMode();
|
|
@@ -14279,13 +14323,13 @@ async function startTail(options = {}) {
|
|
|
14279
14323
|
import_readline5.default.clearLine(process.stdout, 0);
|
|
14280
14324
|
import_readline5.default.cursorTo(process.stdout, 0);
|
|
14281
14325
|
}
|
|
14282
|
-
console.log(
|
|
14326
|
+
console.log(import_chalk26.default.dim("\n\u{1F6F0}\uFE0F Disconnected."));
|
|
14283
14327
|
process.exit(0);
|
|
14284
14328
|
});
|
|
14285
14329
|
const sseUrl = `http://127.0.0.1:${port}/events?capabilities=input`;
|
|
14286
14330
|
const req = import_http2.default.get(sseUrl, (res) => {
|
|
14287
14331
|
if (res.statusCode !== 200) {
|
|
14288
|
-
console.error(
|
|
14332
|
+
console.error(import_chalk26.default.red(`Failed to connect: HTTP ${res.statusCode}`));
|
|
14289
14333
|
process.exit(1);
|
|
14290
14334
|
}
|
|
14291
14335
|
if (canApprove) enterIdleMode();
|
|
@@ -14316,7 +14360,7 @@ async function startTail(options = {}) {
|
|
|
14316
14360
|
import_readline5.default.clearLine(process.stdout, 0);
|
|
14317
14361
|
import_readline5.default.cursorTo(process.stdout, 0);
|
|
14318
14362
|
}
|
|
14319
|
-
console.log(
|
|
14363
|
+
console.log(import_chalk26.default.red("\n\u274C Daemon disconnected."));
|
|
14320
14364
|
process.exit(1);
|
|
14321
14365
|
});
|
|
14322
14366
|
});
|
|
@@ -14408,9 +14452,9 @@ async function startTail(options = {}) {
|
|
|
14408
14452
|
const hash = data.hash ?? "";
|
|
14409
14453
|
const summary = data.argsSummary ?? data.tool;
|
|
14410
14454
|
const fileCount = data.fileCount ?? 0;
|
|
14411
|
-
const files = fileCount > 0 ?
|
|
14455
|
+
const files = fileCount > 0 ? import_chalk26.default.dim(` \xB7 ${fileCount} file${fileCount === 1 ? "" : "s"}`) : "";
|
|
14412
14456
|
process.stdout.write(
|
|
14413
|
-
`${
|
|
14457
|
+
`${import_chalk26.default.dim(time)} ${import_chalk26.default.cyan("\u{1F4F8} snapshot")} ${import_chalk26.default.dim(hash)} ${summary}${files}
|
|
14414
14458
|
`
|
|
14415
14459
|
);
|
|
14416
14460
|
return;
|
|
@@ -14427,26 +14471,26 @@ async function startTail(options = {}) {
|
|
|
14427
14471
|
}
|
|
14428
14472
|
req.on("error", (err2) => {
|
|
14429
14473
|
const msg = err2.code === "ECONNREFUSED" ? "Daemon is not running. Start it with: node9 daemon start" : err2.message;
|
|
14430
|
-
console.error(
|
|
14474
|
+
console.error(import_chalk26.default.red(`
|
|
14431
14475
|
\u274C ${msg}`));
|
|
14432
14476
|
process.exit(1);
|
|
14433
14477
|
});
|
|
14434
14478
|
}
|
|
14435
|
-
var import_http2,
|
|
14479
|
+
var import_http2, import_chalk26, import_fs40, import_os36, import_path43, import_readline5, import_child_process16, PID_FILE, ICONS, MODEL_CONTEXT_LIMITS, RESET2, BOLD2, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, pendingShownForId, pendingWrappedLines, DIVIDER;
|
|
14436
14480
|
var init_tail = __esm({
|
|
14437
14481
|
"src/tui/tail.ts"() {
|
|
14438
14482
|
"use strict";
|
|
14439
14483
|
import_http2 = __toESM(require("http"));
|
|
14440
|
-
|
|
14441
|
-
|
|
14442
|
-
|
|
14443
|
-
|
|
14484
|
+
import_chalk26 = __toESM(require("chalk"));
|
|
14485
|
+
import_fs40 = __toESM(require("fs"));
|
|
14486
|
+
import_os36 = __toESM(require("os"));
|
|
14487
|
+
import_path43 = __toESM(require("path"));
|
|
14444
14488
|
import_readline5 = __toESM(require("readline"));
|
|
14445
14489
|
import_child_process16 = require("child_process");
|
|
14446
14490
|
init_daemon2();
|
|
14447
14491
|
init_daemon();
|
|
14448
14492
|
init_core();
|
|
14449
|
-
PID_FILE =
|
|
14493
|
+
PID_FILE = import_path43.default.join(import_os36.default.homedir(), ".node9", "daemon.pid");
|
|
14450
14494
|
ICONS = {
|
|
14451
14495
|
bash: "\u{1F4BB}",
|
|
14452
14496
|
shell: "\u{1F4BB}",
|
|
@@ -14568,9 +14612,9 @@ function formatTimeLeft(resetsAt) {
|
|
|
14568
14612
|
return ` (${m}m left)`;
|
|
14569
14613
|
}
|
|
14570
14614
|
function safeReadJson(filePath) {
|
|
14571
|
-
if (!
|
|
14615
|
+
if (!import_fs41.default.existsSync(filePath)) return null;
|
|
14572
14616
|
try {
|
|
14573
|
-
return JSON.parse(
|
|
14617
|
+
return JSON.parse(import_fs41.default.readFileSync(filePath, "utf-8"));
|
|
14574
14618
|
} catch {
|
|
14575
14619
|
return null;
|
|
14576
14620
|
}
|
|
@@ -14591,12 +14635,12 @@ function countHooksInFile(filePath) {
|
|
|
14591
14635
|
return Object.keys(cfg.hooks).length;
|
|
14592
14636
|
}
|
|
14593
14637
|
function countRulesInDir(rulesDir) {
|
|
14594
|
-
if (!
|
|
14638
|
+
if (!import_fs41.default.existsSync(rulesDir)) return 0;
|
|
14595
14639
|
let count = 0;
|
|
14596
14640
|
try {
|
|
14597
|
-
for (const entry of
|
|
14641
|
+
for (const entry of import_fs41.default.readdirSync(rulesDir, { withFileTypes: true })) {
|
|
14598
14642
|
if (entry.isDirectory()) {
|
|
14599
|
-
count += countRulesInDir(
|
|
14643
|
+
count += countRulesInDir(import_path44.default.join(rulesDir, entry.name));
|
|
14600
14644
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
14601
14645
|
count++;
|
|
14602
14646
|
}
|
|
@@ -14607,46 +14651,46 @@ function countRulesInDir(rulesDir) {
|
|
|
14607
14651
|
}
|
|
14608
14652
|
function isSamePath(a, b) {
|
|
14609
14653
|
try {
|
|
14610
|
-
return
|
|
14654
|
+
return import_path44.default.resolve(a) === import_path44.default.resolve(b);
|
|
14611
14655
|
} catch {
|
|
14612
14656
|
return false;
|
|
14613
14657
|
}
|
|
14614
14658
|
}
|
|
14615
14659
|
function countConfigs(cwd) {
|
|
14616
|
-
const homeDir2 =
|
|
14617
|
-
const claudeDir =
|
|
14660
|
+
const homeDir2 = import_os37.default.homedir();
|
|
14661
|
+
const claudeDir = import_path44.default.join(homeDir2, ".claude");
|
|
14618
14662
|
let claudeMdCount = 0;
|
|
14619
14663
|
let rulesCount = 0;
|
|
14620
14664
|
let hooksCount = 0;
|
|
14621
14665
|
const userMcpServers = /* @__PURE__ */ new Set();
|
|
14622
14666
|
const projectMcpServers = /* @__PURE__ */ new Set();
|
|
14623
|
-
if (
|
|
14624
|
-
rulesCount += countRulesInDir(
|
|
14625
|
-
const userSettings =
|
|
14667
|
+
if (import_fs41.default.existsSync(import_path44.default.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
14668
|
+
rulesCount += countRulesInDir(import_path44.default.join(claudeDir, "rules"));
|
|
14669
|
+
const userSettings = import_path44.default.join(claudeDir, "settings.json");
|
|
14626
14670
|
for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
|
|
14627
14671
|
hooksCount += countHooksInFile(userSettings);
|
|
14628
|
-
const userClaudeJson =
|
|
14672
|
+
const userClaudeJson = import_path44.default.join(homeDir2, ".claude.json");
|
|
14629
14673
|
for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
|
|
14630
14674
|
for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
|
|
14631
14675
|
userMcpServers.delete(name);
|
|
14632
14676
|
}
|
|
14633
14677
|
if (cwd) {
|
|
14634
|
-
if (
|
|
14635
|
-
if (
|
|
14636
|
-
const projectClaudeDir =
|
|
14678
|
+
if (import_fs41.default.existsSync(import_path44.default.join(cwd, "CLAUDE.md"))) claudeMdCount++;
|
|
14679
|
+
if (import_fs41.default.existsSync(import_path44.default.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
|
|
14680
|
+
const projectClaudeDir = import_path44.default.join(cwd, ".claude");
|
|
14637
14681
|
const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
|
|
14638
14682
|
if (!overlapsUserScope) {
|
|
14639
|
-
if (
|
|
14640
|
-
rulesCount += countRulesInDir(
|
|
14641
|
-
const projSettings =
|
|
14683
|
+
if (import_fs41.default.existsSync(import_path44.default.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
14684
|
+
rulesCount += countRulesInDir(import_path44.default.join(projectClaudeDir, "rules"));
|
|
14685
|
+
const projSettings = import_path44.default.join(projectClaudeDir, "settings.json");
|
|
14642
14686
|
for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
|
|
14643
14687
|
hooksCount += countHooksInFile(projSettings);
|
|
14644
14688
|
}
|
|
14645
|
-
if (
|
|
14646
|
-
const localSettings =
|
|
14689
|
+
if (import_fs41.default.existsSync(import_path44.default.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
|
|
14690
|
+
const localSettings = import_path44.default.join(projectClaudeDir, "settings.local.json");
|
|
14647
14691
|
for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
|
|
14648
14692
|
hooksCount += countHooksInFile(localSettings);
|
|
14649
|
-
const mcpJsonServers = getMcpServerNames(
|
|
14693
|
+
const mcpJsonServers = getMcpServerNames(import_path44.default.join(cwd, ".mcp.json"));
|
|
14650
14694
|
const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
|
|
14651
14695
|
for (const name of disabledMcpJson) mcpJsonServers.delete(name);
|
|
14652
14696
|
for (const name of mcpJsonServers) projectMcpServers.add(name);
|
|
@@ -14679,12 +14723,12 @@ function readActiveShieldsHud() {
|
|
|
14679
14723
|
return shieldsCache.value;
|
|
14680
14724
|
}
|
|
14681
14725
|
try {
|
|
14682
|
-
const shieldsPath =
|
|
14683
|
-
if (!
|
|
14726
|
+
const shieldsPath = import_path44.default.join(import_os37.default.homedir(), ".node9", "shields.json");
|
|
14727
|
+
if (!import_fs41.default.existsSync(shieldsPath)) {
|
|
14684
14728
|
shieldsCache = { value: [], ts: now };
|
|
14685
14729
|
return [];
|
|
14686
14730
|
}
|
|
14687
|
-
const parsed = JSON.parse(
|
|
14731
|
+
const parsed = JSON.parse(import_fs41.default.readFileSync(shieldsPath, "utf-8"));
|
|
14688
14732
|
if (!Array.isArray(parsed.active)) {
|
|
14689
14733
|
shieldsCache = { value: [], ts: now };
|
|
14690
14734
|
return [];
|
|
@@ -14786,17 +14830,17 @@ function renderContextLine(stdin) {
|
|
|
14786
14830
|
async function main() {
|
|
14787
14831
|
try {
|
|
14788
14832
|
const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
|
|
14789
|
-
if (
|
|
14833
|
+
if (import_fs41.default.existsSync(import_path44.default.join(import_os37.default.homedir(), ".node9", "hud-debug"))) {
|
|
14790
14834
|
try {
|
|
14791
|
-
const logPath =
|
|
14835
|
+
const logPath = import_path44.default.join(import_os37.default.homedir(), ".node9", "hud-debug.log");
|
|
14792
14836
|
const MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
14793
14837
|
let size = 0;
|
|
14794
14838
|
try {
|
|
14795
|
-
size =
|
|
14839
|
+
size = import_fs41.default.statSync(logPath).size;
|
|
14796
14840
|
} catch {
|
|
14797
14841
|
}
|
|
14798
14842
|
if (size < MAX_LOG_SIZE) {
|
|
14799
|
-
|
|
14843
|
+
import_fs41.default.appendFileSync(
|
|
14800
14844
|
logPath,
|
|
14801
14845
|
JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
|
|
14802
14846
|
);
|
|
@@ -14817,11 +14861,11 @@ async function main() {
|
|
|
14817
14861
|
try {
|
|
14818
14862
|
const cwd = stdin.cwd ?? process.cwd();
|
|
14819
14863
|
for (const configPath of [
|
|
14820
|
-
|
|
14821
|
-
|
|
14864
|
+
import_path44.default.join(cwd, "node9.config.json"),
|
|
14865
|
+
import_path44.default.join(import_os37.default.homedir(), ".node9", "config.json")
|
|
14822
14866
|
]) {
|
|
14823
|
-
if (!
|
|
14824
|
-
const cfg = JSON.parse(
|
|
14867
|
+
if (!import_fs41.default.existsSync(configPath)) continue;
|
|
14868
|
+
const cfg = JSON.parse(import_fs41.default.readFileSync(configPath, "utf-8"));
|
|
14825
14869
|
const hud = cfg.settings?.hud;
|
|
14826
14870
|
if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
|
|
14827
14871
|
}
|
|
@@ -14839,13 +14883,13 @@ async function main() {
|
|
|
14839
14883
|
renderOffline();
|
|
14840
14884
|
}
|
|
14841
14885
|
}
|
|
14842
|
-
var
|
|
14886
|
+
var import_fs41, import_path44, import_os37, import_http3, RESET3, BOLD3, DIM, RED2, GREEN2, YELLOW2, BLUE, MAGENTA, CYAN2, WHITE, BAR_FILLED, BAR_EMPTY, BAR_WIDTH, shieldsCache, SHIELDS_CACHE_TTL_MS;
|
|
14843
14887
|
var init_hud = __esm({
|
|
14844
14888
|
"src/cli/hud.ts"() {
|
|
14845
14889
|
"use strict";
|
|
14846
|
-
|
|
14847
|
-
|
|
14848
|
-
|
|
14890
|
+
import_fs41 = __toESM(require("fs"));
|
|
14891
|
+
import_path44 = __toESM(require("path"));
|
|
14892
|
+
import_os37 = __toESM(require("os"));
|
|
14849
14893
|
import_http3 = __toESM(require("http"));
|
|
14850
14894
|
init_daemon();
|
|
14851
14895
|
RESET3 = "\x1B[0m";
|
|
@@ -14871,10 +14915,10 @@ var import_commander = require("commander");
|
|
|
14871
14915
|
init_core();
|
|
14872
14916
|
init_setup();
|
|
14873
14917
|
init_daemon2();
|
|
14874
|
-
var
|
|
14875
|
-
var
|
|
14876
|
-
var
|
|
14877
|
-
var
|
|
14918
|
+
var import_chalk27 = __toESM(require("chalk"));
|
|
14919
|
+
var import_fs42 = __toESM(require("fs"));
|
|
14920
|
+
var import_path45 = __toESM(require("path"));
|
|
14921
|
+
var import_os38 = __toESM(require("os"));
|
|
14878
14922
|
var import_prompts2 = require("@inquirer/prompts");
|
|
14879
14923
|
|
|
14880
14924
|
// src/utils/duration.ts
|
|
@@ -20584,22 +20628,207 @@ function registerDlpCommand(program2) {
|
|
|
20584
20628
|
});
|
|
20585
20629
|
}
|
|
20586
20630
|
|
|
20631
|
+
// src/cli/commands/mask.ts
|
|
20632
|
+
var import_chalk25 = __toESM(require("chalk"));
|
|
20633
|
+
var import_fs39 = __toESM(require("fs"));
|
|
20634
|
+
var import_path42 = __toESM(require("path"));
|
|
20635
|
+
var import_os35 = __toESM(require("os"));
|
|
20636
|
+
init_dlp();
|
|
20637
|
+
function findJsonlFiles(dir) {
|
|
20638
|
+
const results = [];
|
|
20639
|
+
if (!import_fs39.default.existsSync(dir)) return results;
|
|
20640
|
+
for (const entry of import_fs39.default.readdirSync(dir, { withFileTypes: true })) {
|
|
20641
|
+
const full = import_path42.default.join(dir, entry.name);
|
|
20642
|
+
if (entry.isDirectory()) results.push(...findJsonlFiles(full));
|
|
20643
|
+
else if (entry.isFile() && entry.name.endsWith(".jsonl")) results.push(full);
|
|
20644
|
+
}
|
|
20645
|
+
return results;
|
|
20646
|
+
}
|
|
20647
|
+
function redactJson(obj) {
|
|
20648
|
+
if (typeof obj === "string") {
|
|
20649
|
+
const { result, found } = redactText(obj);
|
|
20650
|
+
return { value: result, modified: result !== obj, found };
|
|
20651
|
+
}
|
|
20652
|
+
if (Array.isArray(obj)) {
|
|
20653
|
+
let modified = false;
|
|
20654
|
+
const found = [];
|
|
20655
|
+
const value = obj.map((item) => {
|
|
20656
|
+
const r = redactJson(item);
|
|
20657
|
+
if (r.modified) modified = true;
|
|
20658
|
+
r.found.forEach((f) => {
|
|
20659
|
+
if (!found.includes(f)) found.push(f);
|
|
20660
|
+
});
|
|
20661
|
+
return r.value;
|
|
20662
|
+
});
|
|
20663
|
+
return { value, modified, found };
|
|
20664
|
+
}
|
|
20665
|
+
if (obj !== null && typeof obj === "object") {
|
|
20666
|
+
let modified = false;
|
|
20667
|
+
const found = [];
|
|
20668
|
+
const value = {};
|
|
20669
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
20670
|
+
const r = redactJson(v);
|
|
20671
|
+
value[k] = r.value;
|
|
20672
|
+
if (r.modified) modified = true;
|
|
20673
|
+
r.found.forEach((f) => {
|
|
20674
|
+
if (!found.includes(f)) found.push(f);
|
|
20675
|
+
});
|
|
20676
|
+
}
|
|
20677
|
+
return { value, modified, found };
|
|
20678
|
+
}
|
|
20679
|
+
return { value: obj, modified: false, found: [] };
|
|
20680
|
+
}
|
|
20681
|
+
function processFile(filePath, dryRun) {
|
|
20682
|
+
let raw;
|
|
20683
|
+
try {
|
|
20684
|
+
raw = import_fs39.default.readFileSync(filePath, "utf-8");
|
|
20685
|
+
} catch {
|
|
20686
|
+
return { redactedLines: 0, patterns: [] };
|
|
20687
|
+
}
|
|
20688
|
+
const lines = raw.split("\n");
|
|
20689
|
+
let redactedLines = 0;
|
|
20690
|
+
const patterns = [];
|
|
20691
|
+
const newLines = [];
|
|
20692
|
+
for (const line of lines) {
|
|
20693
|
+
if (!line.trim()) {
|
|
20694
|
+
newLines.push(line);
|
|
20695
|
+
continue;
|
|
20696
|
+
}
|
|
20697
|
+
let parsed;
|
|
20698
|
+
try {
|
|
20699
|
+
parsed = JSON.parse(line);
|
|
20700
|
+
} catch {
|
|
20701
|
+
newLines.push(line);
|
|
20702
|
+
continue;
|
|
20703
|
+
}
|
|
20704
|
+
const { value, modified, found } = redactJson(parsed);
|
|
20705
|
+
if (modified) {
|
|
20706
|
+
redactedLines++;
|
|
20707
|
+
found.forEach((f) => {
|
|
20708
|
+
if (!patterns.includes(f)) patterns.push(f);
|
|
20709
|
+
});
|
|
20710
|
+
newLines.push(JSON.stringify(value));
|
|
20711
|
+
} else {
|
|
20712
|
+
newLines.push(line);
|
|
20713
|
+
}
|
|
20714
|
+
}
|
|
20715
|
+
if (!dryRun && redactedLines > 0) {
|
|
20716
|
+
import_fs39.default.writeFileSync(filePath, newLines.join("\n"), "utf-8");
|
|
20717
|
+
}
|
|
20718
|
+
return { redactedLines, patterns };
|
|
20719
|
+
}
|
|
20720
|
+
function processJsonFile(filePath, dryRun) {
|
|
20721
|
+
let raw;
|
|
20722
|
+
try {
|
|
20723
|
+
raw = import_fs39.default.readFileSync(filePath, "utf-8");
|
|
20724
|
+
} catch {
|
|
20725
|
+
return { redactedLines: 0, patterns: [] };
|
|
20726
|
+
}
|
|
20727
|
+
let parsed;
|
|
20728
|
+
try {
|
|
20729
|
+
parsed = JSON.parse(raw);
|
|
20730
|
+
} catch {
|
|
20731
|
+
return { redactedLines: 0, patterns: [] };
|
|
20732
|
+
}
|
|
20733
|
+
const { value, modified, found } = redactJson(parsed);
|
|
20734
|
+
if (!modified) return { redactedLines: 0, patterns: [] };
|
|
20735
|
+
if (!dryRun) {
|
|
20736
|
+
import_fs39.default.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
|
|
20737
|
+
}
|
|
20738
|
+
return { redactedLines: 1, patterns: found };
|
|
20739
|
+
}
|
|
20740
|
+
function findJsonFiles(dir) {
|
|
20741
|
+
const results = [];
|
|
20742
|
+
if (!import_fs39.default.existsSync(dir)) return results;
|
|
20743
|
+
for (const entry of import_fs39.default.readdirSync(dir, { withFileTypes: true })) {
|
|
20744
|
+
const full = import_path42.default.join(dir, entry.name);
|
|
20745
|
+
if (entry.isDirectory()) results.push(...findJsonFiles(full));
|
|
20746
|
+
else if (entry.isFile() && entry.name.endsWith(".json")) results.push(full);
|
|
20747
|
+
}
|
|
20748
|
+
return results;
|
|
20749
|
+
}
|
|
20750
|
+
function registerMaskCommand(program2) {
|
|
20751
|
+
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) => {
|
|
20752
|
+
const dryRun = !!options.dryRun;
|
|
20753
|
+
const home = import_os35.default.homedir();
|
|
20754
|
+
const claudeDir = import_path42.default.join(home, ".claude", "projects");
|
|
20755
|
+
const geminiDir = import_path42.default.join(home, ".gemini", "tmp");
|
|
20756
|
+
const allFiles = [
|
|
20757
|
+
...findJsonlFiles(claudeDir).map((p) => ({ path: p, type: "jsonl" })),
|
|
20758
|
+
...findJsonFiles(geminiDir).map((p) => ({ path: p, type: "json" }))
|
|
20759
|
+
];
|
|
20760
|
+
const cutoff = options.all ? null : new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
|
|
20761
|
+
const filtered = cutoff ? allFiles.filter((f) => {
|
|
20762
|
+
try {
|
|
20763
|
+
return import_fs39.default.statSync(f.path).mtime >= cutoff;
|
|
20764
|
+
} catch {
|
|
20765
|
+
return false;
|
|
20766
|
+
}
|
|
20767
|
+
}) : allFiles;
|
|
20768
|
+
if (filtered.length === 0) {
|
|
20769
|
+
console.log(import_chalk25.default.yellow(" No session files found."));
|
|
20770
|
+
return;
|
|
20771
|
+
}
|
|
20772
|
+
console.log("");
|
|
20773
|
+
if (dryRun) {
|
|
20774
|
+
console.log(import_chalk25.default.dim(" Dry run \u2014 no files will be modified.\n"));
|
|
20775
|
+
}
|
|
20776
|
+
let totalFiles = 0;
|
|
20777
|
+
let totalLines = 0;
|
|
20778
|
+
const totalPatterns = [];
|
|
20779
|
+
for (const file of filtered) {
|
|
20780
|
+
const shortPath = file.path.replace(home, "~");
|
|
20781
|
+
const { redactedLines, patterns } = file.type === "jsonl" ? processFile(file.path, dryRun) : processJsonFile(file.path, dryRun);
|
|
20782
|
+
if (redactedLines > 0) {
|
|
20783
|
+
totalFiles++;
|
|
20784
|
+
totalLines += redactedLines;
|
|
20785
|
+
patterns.forEach((p) => {
|
|
20786
|
+
if (!totalPatterns.includes(p)) totalPatterns.push(p);
|
|
20787
|
+
});
|
|
20788
|
+
const verb = dryRun ? "Would redact" : "Redacted";
|
|
20789
|
+
console.log(
|
|
20790
|
+
" " + import_chalk25.default.dim(shortPath.slice(0, 60).padEnd(62)) + import_chalk25.default.red(`${verb}: `) + import_chalk25.default.yellow(patterns.join(", ")) + import_chalk25.default.dim(` (${redactedLines} line${redactedLines !== 1 ? "s" : ""})`)
|
|
20791
|
+
);
|
|
20792
|
+
}
|
|
20793
|
+
}
|
|
20794
|
+
console.log("");
|
|
20795
|
+
if (totalFiles === 0) {
|
|
20796
|
+
console.log(import_chalk25.default.green(" No secrets found in session history."));
|
|
20797
|
+
} else {
|
|
20798
|
+
const verb = dryRun ? "would be modified" : "modified";
|
|
20799
|
+
console.log(
|
|
20800
|
+
import_chalk25.default.bold(` ${totalFiles} file${totalFiles !== 1 ? "s" : ""} ${verb}`) + import_chalk25.default.dim(`, ${totalLines} line${totalLines !== 1 ? "s" : ""} redacted`)
|
|
20801
|
+
);
|
|
20802
|
+
console.log(" Patterns: " + import_chalk25.default.yellow(totalPatterns.join(", ")));
|
|
20803
|
+
if (!dryRun) {
|
|
20804
|
+
console.log("");
|
|
20805
|
+
console.log(
|
|
20806
|
+
import_chalk25.default.dim(
|
|
20807
|
+
" Note: secrets were already sent to the AI provider during the active session.\n This cleans your local disk only. Rotate any exposed keys."
|
|
20808
|
+
)
|
|
20809
|
+
);
|
|
20810
|
+
}
|
|
20811
|
+
}
|
|
20812
|
+
console.log("");
|
|
20813
|
+
});
|
|
20814
|
+
}
|
|
20815
|
+
|
|
20587
20816
|
// src/cli.ts
|
|
20588
20817
|
var { version } = JSON.parse(
|
|
20589
|
-
|
|
20818
|
+
import_fs42.default.readFileSync(import_path45.default.join(__dirname, "../package.json"), "utf-8")
|
|
20590
20819
|
);
|
|
20591
20820
|
var program = new import_commander.Command();
|
|
20592
20821
|
program.name("node9").description("The Sudo Command for AI Agents").version(version);
|
|
20593
20822
|
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) => {
|
|
20594
20823
|
const DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept";
|
|
20595
|
-
const credPath =
|
|
20596
|
-
if (!
|
|
20597
|
-
|
|
20824
|
+
const credPath = import_path45.default.join(import_os38.default.homedir(), ".node9", "credentials.json");
|
|
20825
|
+
if (!import_fs42.default.existsSync(import_path45.default.dirname(credPath)))
|
|
20826
|
+
import_fs42.default.mkdirSync(import_path45.default.dirname(credPath), { recursive: true });
|
|
20598
20827
|
const profileName = options.profile || "default";
|
|
20599
20828
|
let existingCreds = {};
|
|
20600
20829
|
try {
|
|
20601
|
-
if (
|
|
20602
|
-
const raw = JSON.parse(
|
|
20830
|
+
if (import_fs42.default.existsSync(credPath)) {
|
|
20831
|
+
const raw = JSON.parse(import_fs42.default.readFileSync(credPath, "utf-8"));
|
|
20603
20832
|
if (raw.apiKey) {
|
|
20604
20833
|
existingCreds = {
|
|
20605
20834
|
default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL2 }
|
|
@@ -20611,13 +20840,13 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
20611
20840
|
} catch {
|
|
20612
20841
|
}
|
|
20613
20842
|
existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL2 };
|
|
20614
|
-
|
|
20843
|
+
import_fs42.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
|
|
20615
20844
|
if (profileName === "default") {
|
|
20616
|
-
const configPath =
|
|
20845
|
+
const configPath = import_path45.default.join(import_os38.default.homedir(), ".node9", "config.json");
|
|
20617
20846
|
let config = {};
|
|
20618
20847
|
try {
|
|
20619
|
-
if (
|
|
20620
|
-
config = JSON.parse(
|
|
20848
|
+
if (import_fs42.default.existsSync(configPath))
|
|
20849
|
+
config = JSON.parse(import_fs42.default.readFileSync(configPath, "utf-8"));
|
|
20621
20850
|
} catch {
|
|
20622
20851
|
}
|
|
20623
20852
|
if (!config.settings || typeof config.settings !== "object") config.settings = {};
|
|
@@ -20632,19 +20861,19 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
20632
20861
|
approvers.cloud = false;
|
|
20633
20862
|
}
|
|
20634
20863
|
s.approvers = approvers;
|
|
20635
|
-
if (!
|
|
20636
|
-
|
|
20637
|
-
|
|
20864
|
+
if (!import_fs42.default.existsSync(import_path45.default.dirname(configPath)))
|
|
20865
|
+
import_fs42.default.mkdirSync(import_path45.default.dirname(configPath), { recursive: true });
|
|
20866
|
+
import_fs42.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
20638
20867
|
}
|
|
20639
20868
|
if (options.profile && profileName !== "default") {
|
|
20640
|
-
console.log(
|
|
20641
|
-
console.log(
|
|
20869
|
+
console.log(import_chalk27.default.green(`\u2705 Profile "${profileName}" saved`));
|
|
20870
|
+
console.log(import_chalk27.default.gray(` Switch to it per-session: NODE9_PROFILE=${profileName} claude`));
|
|
20642
20871
|
} else if (options.local) {
|
|
20643
|
-
console.log(
|
|
20644
|
-
console.log(
|
|
20872
|
+
console.log(import_chalk27.default.green(`\u2705 Privacy mode \u{1F6E1}\uFE0F`));
|
|
20873
|
+
console.log(import_chalk27.default.gray(` All decisions stay on this machine.`));
|
|
20645
20874
|
} else {
|
|
20646
|
-
console.log(
|
|
20647
|
-
console.log(
|
|
20875
|
+
console.log(import_chalk27.default.green(`\u2705 Logged in \u2014 agent mode`));
|
|
20876
|
+
console.log(import_chalk27.default.gray(` Team policy enforced for all calls via Node9 cloud.`));
|
|
20648
20877
|
}
|
|
20649
20878
|
});
|
|
20650
20879
|
program.command("addto").description("Integrate Node9 with an AI agent").addHelpText(
|
|
@@ -20662,7 +20891,7 @@ program.command("addto").description("Integrate Node9 with an AI agent").addHelp
|
|
|
20662
20891
|
if (target === "vscode") return await setupVSCode();
|
|
20663
20892
|
if (target === "hud") return setupHud();
|
|
20664
20893
|
console.error(
|
|
20665
|
-
|
|
20894
|
+
import_chalk27.default.red(
|
|
20666
20895
|
`Unknown target: "${target}". Supported: claude, gemini, cursor, codex, windsurf, vscode, hud`
|
|
20667
20896
|
)
|
|
20668
20897
|
);
|
|
@@ -20676,17 +20905,17 @@ program.command("setup").description('Alias for "addto" \u2014 integrate Node9 w
|
|
|
20676
20905
|
"The agent to protect: claude | gemini | cursor | codex | windsurf | vscode | hud"
|
|
20677
20906
|
).action(async (target) => {
|
|
20678
20907
|
if (!target) {
|
|
20679
|
-
console.log(
|
|
20680
|
-
console.log(" Usage: " +
|
|
20908
|
+
console.log(import_chalk27.default.cyan("\n\u{1F6E1}\uFE0F Node9 Setup \u2014 integrate with your AI agent\n"));
|
|
20909
|
+
console.log(" Usage: " + import_chalk27.default.white("node9 setup <target>") + "\n");
|
|
20681
20910
|
console.log(" Targets:");
|
|
20682
|
-
console.log(" " +
|
|
20683
|
-
console.log(" " +
|
|
20684
|
-
console.log(" " +
|
|
20685
|
-
console.log(" " +
|
|
20686
|
-
console.log(" " +
|
|
20687
|
-
console.log(" " +
|
|
20911
|
+
console.log(" " + import_chalk27.default.green("claude") + " \u2014 Claude Code (hook mode)");
|
|
20912
|
+
console.log(" " + import_chalk27.default.green("gemini") + " \u2014 Gemini CLI (hook mode)");
|
|
20913
|
+
console.log(" " + import_chalk27.default.green("cursor") + " \u2014 Cursor (MCP proxy)");
|
|
20914
|
+
console.log(" " + import_chalk27.default.green("codex") + " \u2014 OpenAI Codex CLI (MCP proxy)");
|
|
20915
|
+
console.log(" " + import_chalk27.default.green("windsurf") + " \u2014 Windsurf (MCP proxy)");
|
|
20916
|
+
console.log(" " + import_chalk27.default.green("vscode") + " \u2014 VSCode / Copilot (MCP proxy)");
|
|
20688
20917
|
process.stdout.write(
|
|
20689
|
-
" " +
|
|
20918
|
+
" " + import_chalk27.default.green("hud") + " \u2014 Claude Code security statusline\n"
|
|
20690
20919
|
);
|
|
20691
20920
|
console.log("");
|
|
20692
20921
|
return;
|
|
@@ -20700,7 +20929,7 @@ program.command("setup").description('Alias for "addto" \u2014 integrate Node9 w
|
|
|
20700
20929
|
if (t === "vscode") return await setupVSCode();
|
|
20701
20930
|
if (t === "hud") return setupHud();
|
|
20702
20931
|
console.error(
|
|
20703
|
-
|
|
20932
|
+
import_chalk27.default.red(
|
|
20704
20933
|
`Unknown target: "${target}". Supported: claude, gemini, cursor, codex, windsurf, vscode, hud`
|
|
20705
20934
|
)
|
|
20706
20935
|
);
|
|
@@ -20723,33 +20952,33 @@ program.command("removefrom").description("Remove Node9 hooks from an AI agent c
|
|
|
20723
20952
|
else if (target === "hud") fn = teardownHud;
|
|
20724
20953
|
else {
|
|
20725
20954
|
console.error(
|
|
20726
|
-
|
|
20955
|
+
import_chalk27.default.red(
|
|
20727
20956
|
`Unknown target: "${target}". Supported: claude, gemini, cursor, codex, windsurf, vscode, hud`
|
|
20728
20957
|
)
|
|
20729
20958
|
);
|
|
20730
20959
|
process.exit(1);
|
|
20731
20960
|
}
|
|
20732
|
-
console.log(
|
|
20961
|
+
console.log(import_chalk27.default.cyan(`
|
|
20733
20962
|
\u{1F6E1}\uFE0F Node9: removing hooks from ${target}...
|
|
20734
20963
|
`));
|
|
20735
20964
|
try {
|
|
20736
20965
|
fn();
|
|
20737
20966
|
} catch (err2) {
|
|
20738
|
-
console.error(
|
|
20967
|
+
console.error(import_chalk27.default.red(` \u26A0\uFE0F Failed: ${err2 instanceof Error ? err2.message : String(err2)}`));
|
|
20739
20968
|
process.exit(1);
|
|
20740
20969
|
}
|
|
20741
|
-
console.log(
|
|
20970
|
+
console.log(import_chalk27.default.gray("\n Restart the agent for changes to take effect."));
|
|
20742
20971
|
});
|
|
20743
20972
|
program.command("uninstall").description("Remove all Node9 hooks and optionally delete config files").option("--purge", "Also delete ~/.node9/ directory (config, audit log, credentials)").action(async (options) => {
|
|
20744
|
-
console.log(
|
|
20745
|
-
console.log(
|
|
20973
|
+
console.log(import_chalk27.default.cyan("\n\u{1F6E1}\uFE0F Node9 Uninstall\n"));
|
|
20974
|
+
console.log(import_chalk27.default.bold("Stopping daemon..."));
|
|
20746
20975
|
try {
|
|
20747
20976
|
stopDaemon();
|
|
20748
|
-
console.log(
|
|
20977
|
+
console.log(import_chalk27.default.green(" \u2705 Daemon stopped"));
|
|
20749
20978
|
} catch {
|
|
20750
|
-
console.log(
|
|
20979
|
+
console.log(import_chalk27.default.blue(" \u2139\uFE0F Daemon was not running"));
|
|
20751
20980
|
}
|
|
20752
|
-
console.log(
|
|
20981
|
+
console.log(import_chalk27.default.bold("\nRemoving hooks..."));
|
|
20753
20982
|
let teardownFailed = false;
|
|
20754
20983
|
for (const [label, fn] of [
|
|
20755
20984
|
["Claude", teardownClaude],
|
|
@@ -20764,45 +20993,45 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
20764
20993
|
} catch (err2) {
|
|
20765
20994
|
teardownFailed = true;
|
|
20766
20995
|
console.error(
|
|
20767
|
-
|
|
20996
|
+
import_chalk27.default.red(
|
|
20768
20997
|
` \u26A0\uFE0F Failed to remove ${label} hooks: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
20769
20998
|
)
|
|
20770
20999
|
);
|
|
20771
21000
|
}
|
|
20772
21001
|
}
|
|
20773
21002
|
if (options.purge) {
|
|
20774
|
-
const node9Dir =
|
|
20775
|
-
if (
|
|
21003
|
+
const node9Dir = import_path45.default.join(import_os38.default.homedir(), ".node9");
|
|
21004
|
+
if (import_fs42.default.existsSync(node9Dir)) {
|
|
20776
21005
|
const confirmed = await (0, import_prompts2.confirm)({
|
|
20777
21006
|
message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
|
|
20778
21007
|
default: false
|
|
20779
21008
|
});
|
|
20780
21009
|
if (confirmed) {
|
|
20781
|
-
|
|
20782
|
-
if (
|
|
21010
|
+
import_fs42.default.rmSync(node9Dir, { recursive: true });
|
|
21011
|
+
if (import_fs42.default.existsSync(node9Dir)) {
|
|
20783
21012
|
console.error(
|
|
20784
|
-
|
|
21013
|
+
import_chalk27.default.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
|
|
20785
21014
|
);
|
|
20786
21015
|
} else {
|
|
20787
|
-
console.log(
|
|
21016
|
+
console.log(import_chalk27.default.green("\n \u2705 Deleted ~/.node9/ (config, audit log, credentials)"));
|
|
20788
21017
|
}
|
|
20789
21018
|
} else {
|
|
20790
|
-
console.log(
|
|
21019
|
+
console.log(import_chalk27.default.yellow("\n Skipped \u2014 ~/.node9/ was not deleted."));
|
|
20791
21020
|
}
|
|
20792
21021
|
} else {
|
|
20793
|
-
console.log(
|
|
21022
|
+
console.log(import_chalk27.default.blue("\n \u2139\uFE0F ~/.node9/ not found \u2014 nothing to delete"));
|
|
20794
21023
|
}
|
|
20795
21024
|
} else {
|
|
20796
21025
|
console.log(
|
|
20797
|
-
|
|
21026
|
+
import_chalk27.default.gray("\n ~/.node9/ kept \u2014 run with --purge to delete config and audit log")
|
|
20798
21027
|
);
|
|
20799
21028
|
}
|
|
20800
21029
|
if (teardownFailed) {
|
|
20801
|
-
console.error(
|
|
21030
|
+
console.error(import_chalk27.default.red("\n \u26A0\uFE0F Some hooks could not be removed \u2014 see errors above."));
|
|
20802
21031
|
process.exit(1);
|
|
20803
21032
|
}
|
|
20804
|
-
console.log(
|
|
20805
|
-
console.log(
|
|
21033
|
+
console.log(import_chalk27.default.green.bold("\n\u{1F6E1}\uFE0F Node9 removed. Run: npm uninstall -g @node9/proxy"));
|
|
21034
|
+
console.log(import_chalk27.default.gray(" Restart any open AI agent sessions for changes to take effect.\n"));
|
|
20806
21035
|
});
|
|
20807
21036
|
registerDoctorCommand(program, version);
|
|
20808
21037
|
program.command("explain").description(
|
|
@@ -20815,7 +21044,7 @@ program.command("explain").description(
|
|
|
20815
21044
|
try {
|
|
20816
21045
|
args = JSON.parse(trimmed);
|
|
20817
21046
|
} catch {
|
|
20818
|
-
console.error(
|
|
21047
|
+
console.error(import_chalk27.default.red(`
|
|
20819
21048
|
\u274C Invalid JSON: ${trimmed}
|
|
20820
21049
|
`));
|
|
20821
21050
|
process.exit(1);
|
|
@@ -20826,54 +21055,54 @@ program.command("explain").description(
|
|
|
20826
21055
|
}
|
|
20827
21056
|
const result = await explainPolicy(tool, args);
|
|
20828
21057
|
console.log("");
|
|
20829
|
-
console.log(
|
|
21058
|
+
console.log(import_chalk27.default.cyan.bold("\u{1F6E1}\uFE0F Node9 Explain"));
|
|
20830
21059
|
console.log("");
|
|
20831
|
-
console.log(` ${
|
|
21060
|
+
console.log(` ${import_chalk27.default.bold("Tool:")} ${import_chalk27.default.white(result.tool)}`);
|
|
20832
21061
|
if (argsRaw) {
|
|
20833
21062
|
const preview2 = argsRaw.length > 80 ? argsRaw.slice(0, 77) + "\u2026" : argsRaw;
|
|
20834
|
-
console.log(` ${
|
|
21063
|
+
console.log(` ${import_chalk27.default.bold("Input:")} ${import_chalk27.default.gray(preview2)}`);
|
|
20835
21064
|
}
|
|
20836
21065
|
console.log("");
|
|
20837
|
-
console.log(
|
|
21066
|
+
console.log(import_chalk27.default.bold("Config Sources (Waterfall):"));
|
|
20838
21067
|
for (const tier of result.waterfall) {
|
|
20839
|
-
const num3 =
|
|
21068
|
+
const num3 = import_chalk27.default.gray(` ${tier.tier}.`);
|
|
20840
21069
|
const label = tier.label.padEnd(16);
|
|
20841
21070
|
let statusStr;
|
|
20842
21071
|
if (tier.tier === 1) {
|
|
20843
|
-
statusStr =
|
|
21072
|
+
statusStr = import_chalk27.default.gray(tier.note ?? "");
|
|
20844
21073
|
} else if (tier.status === "active") {
|
|
20845
|
-
const loc = tier.path ?
|
|
20846
|
-
const note = tier.note ?
|
|
20847
|
-
statusStr =
|
|
21074
|
+
const loc = tier.path ? import_chalk27.default.gray(tier.path) : "";
|
|
21075
|
+
const note = tier.note ? import_chalk27.default.gray(`(${tier.note})`) : "";
|
|
21076
|
+
statusStr = import_chalk27.default.green("\u2713 active") + (loc ? " " + loc : "") + (note ? " " + note : "");
|
|
20848
21077
|
} else {
|
|
20849
|
-
statusStr =
|
|
21078
|
+
statusStr = import_chalk27.default.gray("\u25CB " + (tier.note ?? "not found"));
|
|
20850
21079
|
}
|
|
20851
|
-
console.log(`${num3} ${
|
|
21080
|
+
console.log(`${num3} ${import_chalk27.default.white(label)} ${statusStr}`);
|
|
20852
21081
|
}
|
|
20853
21082
|
console.log("");
|
|
20854
|
-
console.log(
|
|
21083
|
+
console.log(import_chalk27.default.bold("Policy Evaluation:"));
|
|
20855
21084
|
for (const step of result.steps) {
|
|
20856
21085
|
const isFinal = step.isFinal;
|
|
20857
21086
|
let icon;
|
|
20858
|
-
if (step.outcome === "allow") icon =
|
|
20859
|
-
else if (step.outcome === "review") icon =
|
|
20860
|
-
else if (step.outcome === "skip") icon =
|
|
20861
|
-
else icon =
|
|
21087
|
+
if (step.outcome === "allow") icon = import_chalk27.default.green(" \u2705");
|
|
21088
|
+
else if (step.outcome === "review") icon = import_chalk27.default.red(" \u{1F534}");
|
|
21089
|
+
else if (step.outcome === "skip") icon = import_chalk27.default.gray(" \u2500 ");
|
|
21090
|
+
else icon = import_chalk27.default.gray(" \u25CB ");
|
|
20862
21091
|
const name = step.name.padEnd(18);
|
|
20863
|
-
const nameStr = isFinal ?
|
|
20864
|
-
const detail = isFinal ?
|
|
20865
|
-
const arrow = isFinal ?
|
|
21092
|
+
const nameStr = isFinal ? import_chalk27.default.white.bold(name) : import_chalk27.default.white(name);
|
|
21093
|
+
const detail = isFinal ? import_chalk27.default.white(step.detail) : import_chalk27.default.gray(step.detail);
|
|
21094
|
+
const arrow = isFinal ? import_chalk27.default.yellow(" \u2190 STOP") : "";
|
|
20866
21095
|
console.log(`${icon} ${nameStr} ${detail}${arrow}`);
|
|
20867
21096
|
}
|
|
20868
21097
|
console.log("");
|
|
20869
21098
|
if (result.decision === "allow") {
|
|
20870
|
-
console.log(
|
|
21099
|
+
console.log(import_chalk27.default.green.bold(" Decision: \u2705 ALLOW") + import_chalk27.default.gray(" \u2014 no approval needed"));
|
|
20871
21100
|
} else {
|
|
20872
21101
|
console.log(
|
|
20873
|
-
|
|
21102
|
+
import_chalk27.default.red.bold(" Decision: \u{1F534} REVIEW") + import_chalk27.default.gray(" \u2014 human approval required")
|
|
20874
21103
|
);
|
|
20875
21104
|
if (result.blockedByLabel) {
|
|
20876
|
-
console.log(
|
|
21105
|
+
console.log(import_chalk27.default.gray(` Reason: ${result.blockedByLabel}`));
|
|
20877
21106
|
}
|
|
20878
21107
|
}
|
|
20879
21108
|
console.log("");
|
|
@@ -20888,7 +21117,7 @@ program.command("tail").description("Stream live agent activity to the terminal"
|
|
|
20888
21117
|
try {
|
|
20889
21118
|
await startTail2(options);
|
|
20890
21119
|
} catch (err2) {
|
|
20891
|
-
console.error(
|
|
21120
|
+
console.error(import_chalk27.default.red(`\u274C ${err2 instanceof Error ? err2.message : String(err2)}`));
|
|
20892
21121
|
process.exit(1);
|
|
20893
21122
|
}
|
|
20894
21123
|
});
|
|
@@ -20921,14 +21150,14 @@ Claude Code spawns this command every ~300ms and writes a JSON payload to stdin.
|
|
|
20921
21150
|
Run "node9 addto claude" to register it as the statusLine.`
|
|
20922
21151
|
).argument("[subcommand]", 'Optional: "debug on" / "debug off" to toggle stdin logging').argument("[state]", 'on|off \u2014 used with "debug" subcommand').action(async (subcommand, state) => {
|
|
20923
21152
|
if (subcommand === "debug") {
|
|
20924
|
-
const flagFile =
|
|
21153
|
+
const flagFile = import_path45.default.join(import_os38.default.homedir(), ".node9", "hud-debug");
|
|
20925
21154
|
if (state === "on") {
|
|
20926
|
-
|
|
20927
|
-
|
|
21155
|
+
import_fs42.default.mkdirSync(import_path45.default.dirname(flagFile), { recursive: true });
|
|
21156
|
+
import_fs42.default.writeFileSync(flagFile, "");
|
|
20928
21157
|
console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
|
|
20929
21158
|
console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
|
|
20930
21159
|
} else if (state === "off") {
|
|
20931
|
-
if (
|
|
21160
|
+
if (import_fs42.default.existsSync(flagFile)) import_fs42.default.unlinkSync(flagFile);
|
|
20932
21161
|
console.log("HUD debug logging disabled.");
|
|
20933
21162
|
} else {
|
|
20934
21163
|
console.error("Usage: node9 hud debug on|off");
|
|
@@ -20943,7 +21172,7 @@ program.command("pause").description("Temporarily disable Node9 protection for a
|
|
|
20943
21172
|
const ms = parseDuration(options.duration);
|
|
20944
21173
|
if (ms === null) {
|
|
20945
21174
|
console.error(
|
|
20946
|
-
|
|
21175
|
+
import_chalk27.default.red(`
|
|
20947
21176
|
\u274C Invalid duration: "${options.duration}". Use format like 15m, 1h, 30s.
|
|
20948
21177
|
`)
|
|
20949
21178
|
);
|
|
@@ -20951,20 +21180,20 @@ program.command("pause").description("Temporarily disable Node9 protection for a
|
|
|
20951
21180
|
}
|
|
20952
21181
|
pauseNode9(ms, options.duration);
|
|
20953
21182
|
const expiresAt = new Date(Date.now() + ms).toLocaleTimeString();
|
|
20954
|
-
console.log(
|
|
21183
|
+
console.log(import_chalk27.default.yellow(`
|
|
20955
21184
|
\u23F8 Node9 paused until ${expiresAt}`));
|
|
20956
|
-
console.log(
|
|
20957
|
-
console.log(
|
|
21185
|
+
console.log(import_chalk27.default.gray(` All tool calls will be allowed without review.`));
|
|
21186
|
+
console.log(import_chalk27.default.gray(` Run "node9 resume" to re-enable early.
|
|
20958
21187
|
`));
|
|
20959
21188
|
});
|
|
20960
21189
|
program.command("resume").description("Re-enable Node9 protection immediately").action(() => {
|
|
20961
21190
|
const { paused } = checkPause();
|
|
20962
21191
|
if (!paused) {
|
|
20963
|
-
console.log(
|
|
21192
|
+
console.log(import_chalk27.default.gray("\nNode9 is already active \u2014 nothing to resume.\n"));
|
|
20964
21193
|
return;
|
|
20965
21194
|
}
|
|
20966
21195
|
resumeNode9();
|
|
20967
|
-
console.log(
|
|
21196
|
+
console.log(import_chalk27.default.green("\n\u25B6 Node9 resumed \u2014 protection is active.\n"));
|
|
20968
21197
|
});
|
|
20969
21198
|
var HOOK_BASED_AGENTS = {
|
|
20970
21199
|
claude: "claude",
|
|
@@ -20977,15 +21206,15 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
20977
21206
|
if (HOOK_BASED_AGENTS[firstArg2] !== void 0) {
|
|
20978
21207
|
const target = HOOK_BASED_AGENTS[firstArg2];
|
|
20979
21208
|
console.error(
|
|
20980
|
-
|
|
21209
|
+
import_chalk27.default.yellow(`
|
|
20981
21210
|
\u26A0\uFE0F Node9 proxy mode does not support "${target}" directly.`)
|
|
20982
21211
|
);
|
|
20983
|
-
console.error(
|
|
21212
|
+
console.error(import_chalk27.default.white(`
|
|
20984
21213
|
"${target}" uses its own hook system. Use:`));
|
|
20985
21214
|
console.error(
|
|
20986
|
-
|
|
21215
|
+
import_chalk27.default.green(` node9 addto ${target} `) + import_chalk27.default.gray("# one-time setup")
|
|
20987
21216
|
);
|
|
20988
|
-
console.error(
|
|
21217
|
+
console.error(import_chalk27.default.green(` ${target} `) + import_chalk27.default.gray("# run normally"));
|
|
20989
21218
|
process.exit(1);
|
|
20990
21219
|
}
|
|
20991
21220
|
const runArgs = firstArg2 === "shell" ? commandArgs.slice(1) : commandArgs;
|
|
@@ -21002,7 +21231,7 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
21002
21231
|
}
|
|
21003
21232
|
);
|
|
21004
21233
|
if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && getConfig().settings.autoStartDaemon) {
|
|
21005
|
-
console.error(
|
|
21234
|
+
console.error(import_chalk27.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically..."));
|
|
21006
21235
|
const daemonReady = await autoStartDaemonAndWait();
|
|
21007
21236
|
if (daemonReady) result = await authorizeHeadless("shell", { command: fullCommand });
|
|
21008
21237
|
}
|
|
@@ -21015,12 +21244,12 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
21015
21244
|
}
|
|
21016
21245
|
if (!result.approved) {
|
|
21017
21246
|
console.error(
|
|
21018
|
-
|
|
21247
|
+
import_chalk27.default.red(`
|
|
21019
21248
|
\u274C Node9 Blocked: ${result.reason || "Dangerous command detected."}`)
|
|
21020
21249
|
);
|
|
21021
21250
|
process.exit(1);
|
|
21022
21251
|
}
|
|
21023
|
-
console.error(
|
|
21252
|
+
console.error(import_chalk27.default.green("\n\u2705 Approved \u2014 running command...\n"));
|
|
21024
21253
|
await runProxy(fullCommand);
|
|
21025
21254
|
} else {
|
|
21026
21255
|
program.help();
|
|
@@ -21035,14 +21264,15 @@ registerAgentsCommand(program);
|
|
|
21035
21264
|
registerScanCommand(program);
|
|
21036
21265
|
registerSessionsCommand(program);
|
|
21037
21266
|
registerDlpCommand(program);
|
|
21267
|
+
registerMaskCommand(program);
|
|
21038
21268
|
if (process.argv[2] !== "daemon") {
|
|
21039
21269
|
process.on("unhandledRejection", (reason) => {
|
|
21040
21270
|
const isCheckHook = process.argv[2] === "check";
|
|
21041
21271
|
if (isCheckHook) {
|
|
21042
21272
|
if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
|
|
21043
|
-
const logPath =
|
|
21273
|
+
const logPath = import_path45.default.join(import_os38.default.homedir(), ".node9", "hook-debug.log");
|
|
21044
21274
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
21045
|
-
|
|
21275
|
+
import_fs42.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
|
|
21046
21276
|
`);
|
|
21047
21277
|
}
|
|
21048
21278
|
process.exit(0);
|