@node9/proxy 1.11.12 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +142 -244
- package/dist/cli.js +775 -642
- package/dist/cli.mjs +762 -629
- 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 path45 = issue.path.length > 0 ? issue.path.join(".") : "root";
|
|
172
|
+
return ` \u2022 ${path45}: ${issue.message}`;
|
|
173
173
|
});
|
|
174
174
|
return {
|
|
175
175
|
sanitized,
|
|
@@ -2083,9 +2083,9 @@ function matchesPattern(text, patterns) {
|
|
|
2083
2083
|
const withoutDotSlash = text.replace(/^\.\//, "");
|
|
2084
2084
|
return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
|
|
2085
2085
|
}
|
|
2086
|
-
function getNestedValue(obj,
|
|
2086
|
+
function getNestedValue(obj, path45) {
|
|
2087
2087
|
if (!obj || typeof obj !== "object") return null;
|
|
2088
|
-
return
|
|
2088
|
+
return path45.split(".").reduce((prev, curr) => prev?.[curr], obj);
|
|
2089
2089
|
}
|
|
2090
2090
|
function normalizeCommandForPolicy(command) {
|
|
2091
2091
|
try {
|
|
@@ -5266,6 +5266,10 @@ var init_ui = __esm({
|
|
|
5266
5266
|
border-color: rgba(83, 155, 245, 0.35);
|
|
5267
5267
|
background: rgba(83, 155, 245, 0.04);
|
|
5268
5268
|
}
|
|
5269
|
+
.pending-card.discovery-card {
|
|
5270
|
+
border-color: rgba(120, 100, 255, 0.45);
|
|
5271
|
+
background: rgba(100, 80, 255, 0.06);
|
|
5272
|
+
}
|
|
5269
5273
|
.pending-card-header {
|
|
5270
5274
|
display: flex;
|
|
5271
5275
|
align-items: center;
|
|
@@ -5735,6 +5739,12 @@ var init_ui = __esm({
|
|
|
5735
5739
|
text-align: center;
|
|
5736
5740
|
}
|
|
5737
5741
|
/* results */
|
|
5742
|
+
.scan-date-range {
|
|
5743
|
+
text-align: center;
|
|
5744
|
+
font-size: 11px;
|
|
5745
|
+
color: var(--muted);
|
|
5746
|
+
padding: 6px 0 10px;
|
|
5747
|
+
}
|
|
5738
5748
|
.scan-summary-row {
|
|
5739
5749
|
display: grid;
|
|
5740
5750
|
grid-template-columns: repeat(4, 1fr);
|
|
@@ -7446,7 +7456,7 @@ var init_ui = __esm({
|
|
|
7446
7456
|
});
|
|
7447
7457
|
|
|
7448
7458
|
try {
|
|
7449
|
-
await fetch('/mcp/approve', {
|
|
7459
|
+
const res = await fetch('/mcp/approve', {
|
|
7450
7460
|
method: 'POST',
|
|
7451
7461
|
headers: {
|
|
7452
7462
|
'Content-Type': 'application/json',
|
|
@@ -7454,9 +7464,19 @@ var init_ui = __esm({
|
|
|
7454
7464
|
},
|
|
7455
7465
|
body: JSON.stringify({ id, serverKey, disabledTools }),
|
|
7456
7466
|
});
|
|
7467
|
+
if (!res.ok) {
|
|
7468
|
+
showToast(
|
|
7469
|
+
'\u26A0\uFE0F',
|
|
7470
|
+
'Approval failed',
|
|
7471
|
+
\`Server responded with \${res.status}\`,
|
|
7472
|
+
'toast-block'
|
|
7473
|
+
);
|
|
7474
|
+
return;
|
|
7475
|
+
}
|
|
7457
7476
|
closeMcpReview();
|
|
7458
7477
|
refreshMcpTools();
|
|
7459
7478
|
} catch (err) {
|
|
7479
|
+
showToast('\u26A0\uFE0F', 'Approval failed', 'Network error \u2014 check the daemon', 'toast-block');
|
|
7460
7480
|
console.error('Failed to approve MCP server:', err);
|
|
7461
7481
|
}
|
|
7462
7482
|
}
|
|
@@ -7627,13 +7647,10 @@ var init_ui = __esm({
|
|
|
7627
7647
|
const res = await fetch('/scan', { headers: { 'X-Node9-Token': CSRF_TOKEN } });
|
|
7628
7648
|
if (!res.ok) return;
|
|
7629
7649
|
const data = await res.json();
|
|
7630
|
-
if (data.status !== 'complete') return;
|
|
7650
|
+
if (data.status !== 'complete' || !data.summary) return;
|
|
7631
7651
|
_scanData = data;
|
|
7632
|
-
const
|
|
7633
|
-
const total =
|
|
7634
|
-
sources.reduce((n, s) => n + (s.findings || []).length, 0) +
|
|
7635
|
-
sources.reduce((n, s) => n + (s.leaks || []).length, 0) +
|
|
7636
|
-
sources.reduce((n, s) => n + (s.loops || []).length, 0);
|
|
7652
|
+
const v = data.summary.byVerdict || {};
|
|
7653
|
+
const total = (v.blocked || 0) + (v.supervised || 0) + (v.leaks || 0) + (v.loops || 0);
|
|
7637
7654
|
if (total > 0) {
|
|
7638
7655
|
const btn = document.getElementById('btn-scan-history');
|
|
7639
7656
|
if (btn && !btn.querySelector('.scan-badge')) {
|
|
@@ -7738,42 +7755,26 @@ var init_ui = __esm({
|
|
|
7738
7755
|
}
|
|
7739
7756
|
|
|
7740
7757
|
function renderScanResults(data) {
|
|
7741
|
-
if (!data || data.status !== 'complete') {
|
|
7758
|
+
if (!data || data.status !== 'complete' || !data.summary) {
|
|
7742
7759
|
return '<div style="text-align:center;color:var(--muted);padding:32px">No data returned.</div>';
|
|
7743
7760
|
}
|
|
7744
|
-
|
|
7745
|
-
|
|
7746
|
-
|
|
7747
|
-
|
|
7748
|
-
const
|
|
7749
|
-
const
|
|
7750
|
-
const
|
|
7751
|
-
|
|
7752
|
-
|
|
7753
|
-
const
|
|
7754
|
-
const
|
|
7755
|
-
(f) => f.verdict !== 'block' && f.ruleSource !== 'user'
|
|
7756
|
-
);
|
|
7757
|
-
const yourRules = allFindings.filter((f) => f.ruleSource === 'user');
|
|
7758
|
-
|
|
7759
|
-
// Loop cost estimate: each wasted iteration ~2K tokens at Sonnet pricing
|
|
7760
|
-
const LOOP_THRESHOLD = 3;
|
|
7761
|
-
const COST_PER_ITER = 0.006;
|
|
7762
|
-
const wastedIters = allLoops.reduce(
|
|
7763
|
-
(sum, l) => sum + Math.max(0, (l.count || 0) - LOOP_THRESHOLD),
|
|
7764
|
-
0
|
|
7765
|
-
);
|
|
7766
|
-
const loopWastedUSD = wastedIters * COST_PER_ITER;
|
|
7767
|
-
|
|
7768
|
-
const totalCost = s.totalCostUSD || 0;
|
|
7761
|
+
// Server-computed ScanSummary (see src/scan-summary.ts).
|
|
7762
|
+
// This renderer is a pure presentation layer \u2014 no categorization here.
|
|
7763
|
+
const summary = data.summary;
|
|
7764
|
+
const stats = summary.stats || {};
|
|
7765
|
+
const byVerdict = summary.byVerdict || { blocked: 0, supervised: 0, leaks: 0, loops: 0 };
|
|
7766
|
+
const byAgent = summary.byAgent || [];
|
|
7767
|
+
const sections = summary.sections || [];
|
|
7768
|
+
const leaks = summary.leaks || [];
|
|
7769
|
+
const loops = summary.loops || [];
|
|
7770
|
+
const loopWastedUSD = summary.loopWastedUSD || 0;
|
|
7771
|
+
const totalCost = stats.totalCostUSD || 0;
|
|
7769
7772
|
|
|
7770
7773
|
function fmtUSD(n) {
|
|
7771
7774
|
if (!n || n < 0.001) return null;
|
|
7772
7775
|
if (n < 1) return '$' + n.toFixed(3);
|
|
7773
7776
|
return '$' + n.toFixed(2);
|
|
7774
7777
|
}
|
|
7775
|
-
|
|
7776
|
-
// \u2500\u2500 Hero stats row \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
7777
7778
|
const costStr = fmtUSD(totalCost);
|
|
7778
7779
|
const savingsStr = fmtUSD(loopWastedUSD);
|
|
7779
7780
|
|
|
@@ -7795,10 +7796,46 @@ var init_ui = __esm({
|
|
|
7795
7796
|
);
|
|
7796
7797
|
}
|
|
7797
7798
|
|
|
7798
|
-
|
|
7799
|
+
// \u2500\u2500 Date range header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
7800
|
+
function fmtShort(iso) {
|
|
7801
|
+
if (!iso) return null;
|
|
7802
|
+
try {
|
|
7803
|
+
return new Date(iso).toLocaleDateString([], {
|
|
7804
|
+
month: 'short',
|
|
7805
|
+
day: 'numeric',
|
|
7806
|
+
year: 'numeric',
|
|
7807
|
+
});
|
|
7808
|
+
} catch {
|
|
7809
|
+
return null;
|
|
7810
|
+
}
|
|
7811
|
+
}
|
|
7812
|
+
const firstLabel = fmtShort(stats.firstDate);
|
|
7813
|
+
const lastLabel = fmtShort(stats.lastDate);
|
|
7814
|
+
let daySpan = '';
|
|
7815
|
+
if (stats.firstDate && stats.lastDate) {
|
|
7816
|
+
const days =
|
|
7817
|
+
Math.round(
|
|
7818
|
+
(new Date(stats.lastDate).getTime() - new Date(stats.firstDate).getTime()) /
|
|
7819
|
+
(1000 * 60 * 60 * 24)
|
|
7820
|
+
) + 1;
|
|
7821
|
+
if (days > 1) daySpan = ' \xB7 ' + days + ' day' + (days !== 1 ? 's' : '');
|
|
7822
|
+
}
|
|
7823
|
+
const dateRangeHtml =
|
|
7824
|
+
firstLabel && lastLabel
|
|
7825
|
+
? '<div class="scan-date-range">\u{1F4C5} ' +
|
|
7826
|
+
esc(firstLabel) +
|
|
7827
|
+
' \u2013 ' +
|
|
7828
|
+
esc(lastLabel) +
|
|
7829
|
+
esc(daySpan) +
|
|
7830
|
+
'</div>'
|
|
7831
|
+
: '';
|
|
7832
|
+
|
|
7833
|
+
// \u2500\u2500 Hero stats row (by VERDICT \u2014 matches terminal) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
7834
|
+
const statsHtml =
|
|
7835
|
+
dateRangeHtml +
|
|
7799
7836
|
'<div class="scan-summary-row">' +
|
|
7800
7837
|
'<div class="scan-stat">' +
|
|
7801
|
-
statVal(
|
|
7838
|
+
statVal(stats.sessions || 0, '') +
|
|
7802
7839
|
'<div class="scan-stat-label">Sessions</div></div>' +
|
|
7803
7840
|
(totalCost > 0
|
|
7804
7841
|
? '<div class="scan-stat">' +
|
|
@@ -7806,19 +7843,16 @@ var init_ui = __esm({
|
|
|
7806
7843
|
'<div class="scan-stat-label">AI Spend</div></div>'
|
|
7807
7844
|
: '') +
|
|
7808
7845
|
'<div class="scan-stat">' +
|
|
7809
|
-
statVal(blocked
|
|
7846
|
+
statVal(byVerdict.blocked || 0, byVerdict.blocked ? 'danger' : 'ok') +
|
|
7810
7847
|
'<div class="scan-stat-label">Blocked</div></div>' +
|
|
7811
7848
|
'<div class="scan-stat">' +
|
|
7812
|
-
statVal(supervised
|
|
7849
|
+
statVal(byVerdict.supervised || 0, byVerdict.supervised ? 'warning' : 'ok') +
|
|
7813
7850
|
'<div class="scan-stat-label">Supervised</div></div>' +
|
|
7814
7851
|
'<div class="scan-stat">' +
|
|
7815
|
-
statVal(
|
|
7816
|
-
'<div class="scan-stat-label">Your Rules</div></div>' +
|
|
7817
|
-
'<div class="scan-stat">' +
|
|
7818
|
-
statVal(allLeaks.length, allLeaks.length ? 'danger' : 'ok') +
|
|
7852
|
+
statVal(byVerdict.leaks || 0, byVerdict.leaks ? 'danger' : 'ok') +
|
|
7819
7853
|
'<div class="scan-stat-label">Leaks</div></div>' +
|
|
7820
7854
|
'<div class="scan-stat">' +
|
|
7821
|
-
statVal(
|
|
7855
|
+
statVal(byVerdict.loops || 0, byVerdict.loops ? 'warning' : 'ok') +
|
|
7822
7856
|
'<div class="scan-stat-label">Loops</div></div>' +
|
|
7823
7857
|
'</div>' +
|
|
7824
7858
|
(savingsStr
|
|
@@ -7827,56 +7861,103 @@ var init_ui = __esm({
|
|
|
7827
7861
|
'</span> in unnecessary LLM turns</div>'
|
|
7828
7862
|
: '');
|
|
7829
7863
|
|
|
7830
|
-
|
|
7864
|
+
const hasNothing = !sections.length && !leaks.length && !loops.length;
|
|
7865
|
+
if (hasNothing) {
|
|
7831
7866
|
return (
|
|
7832
|
-
|
|
7867
|
+
statsHtml +
|
|
7833
7868
|
'<div style="text-align:center;color:#57ab5a;padding:28px 0;font-size:13px;line-height:1.8">\u2705 <strong>No issues found</strong><br><span style="font-size:11px;color:var(--muted)">Scanned ' +
|
|
7834
|
-
(
|
|
7835
|
-
' sessions
|
|
7836
|
-
sources.length +
|
|
7837
|
-
' AI source(s)</span></div>'
|
|
7869
|
+
(stats.sessions || 0) +
|
|
7870
|
+
' sessions</span></div>'
|
|
7838
7871
|
);
|
|
7839
7872
|
}
|
|
7840
7873
|
|
|
7841
|
-
// \u2500\u2500
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
7845
|
-
|
|
7846
|
-
|
|
7847
|
-
|
|
7874
|
+
// \u2500\u2500 Per-agent breakdown (server-computed byAgent) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
7875
|
+
const agentBreakdown = byAgent.length
|
|
7876
|
+
? '<div class="scan-agent-group">' +
|
|
7877
|
+
byAgent
|
|
7878
|
+
.map(
|
|
7879
|
+
(a) =>
|
|
7880
|
+
'<div class="scan-agent-row">' +
|
|
7881
|
+
'<span class="scan-agent-name">' +
|
|
7882
|
+
esc(a.icon || '') +
|
|
7883
|
+
' ' +
|
|
7884
|
+
esc(a.label) +
|
|
7885
|
+
'</span>' +
|
|
7886
|
+
'<span class="scan-agent-stat">' +
|
|
7887
|
+
a.sessions +
|
|
7888
|
+
' session' +
|
|
7889
|
+
(a.sessions !== 1 ? 's' : '') +
|
|
7890
|
+
'</span>' +
|
|
7891
|
+
'<span class="scan-agent-sep">\xB7</span>' +
|
|
7892
|
+
'<span class="scan-agent-stat">' +
|
|
7893
|
+
a.findings +
|
|
7894
|
+
' finding' +
|
|
7895
|
+
(a.findings !== 1 ? 's' : '') +
|
|
7896
|
+
'</span>' +
|
|
7897
|
+
'</div>'
|
|
7898
|
+
)
|
|
7899
|
+
.join('') +
|
|
7900
|
+
'</div>'
|
|
7901
|
+
: '';
|
|
7902
|
+
|
|
7903
|
+
// \u2500\u2500 Rule row renderer \u2014 shared between rule sections + leaks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
7904
|
+
// Determines bar-max across all sections so relative counts stay honest.
|
|
7905
|
+
let maxBar = 1;
|
|
7906
|
+
for (const section of sections) {
|
|
7907
|
+
for (const rule of section.rules || []) {
|
|
7908
|
+
if (rule.findings.length > maxBar) maxBar = rule.findings.length;
|
|
7848
7909
|
}
|
|
7849
|
-
return [...map.entries()].sort((a, b) => b[1].length - a[1].length);
|
|
7850
7910
|
}
|
|
7911
|
+
// Group leaks by patternName for display
|
|
7912
|
+
const leaksByPattern = (function () {
|
|
7913
|
+
const m = new Map();
|
|
7914
|
+
for (const l of leaks) {
|
|
7915
|
+
const k = l.patternName || 'DLP';
|
|
7916
|
+
if (!m.has(k)) m.set(k, []);
|
|
7917
|
+
m.get(k).push(l);
|
|
7918
|
+
}
|
|
7919
|
+
for (const [, group] of m) if (group.length > maxBar) maxBar = group.length;
|
|
7920
|
+
return [...m.entries()].sort((a, b) => b[1].length - a[1].length);
|
|
7921
|
+
})();
|
|
7851
7922
|
|
|
7852
|
-
function findingRow(
|
|
7853
|
-
const date = new Date(
|
|
7923
|
+
function findingRow(timestamp, text) {
|
|
7924
|
+
const date = new Date(timestamp).toLocaleDateString([], {
|
|
7854
7925
|
month: 'short',
|
|
7855
7926
|
day: 'numeric',
|
|
7856
7927
|
});
|
|
7857
|
-
const cmdText =
|
|
7858
|
-
type === 'leak' ? esc(item.sample || '') : esc(truncate(item.command || '', 120));
|
|
7859
7928
|
return (
|
|
7860
7929
|
'<div class="scan-finding-row"><span class="scan-finding-ts">' +
|
|
7861
7930
|
date +
|
|
7862
7931
|
'</span><span class="scan-finding-cmd">' +
|
|
7863
|
-
|
|
7932
|
+
text +
|
|
7864
7933
|
'</span></div>'
|
|
7865
7934
|
);
|
|
7866
7935
|
}
|
|
7867
7936
|
|
|
7868
|
-
function
|
|
7869
|
-
|
|
7870
|
-
|
|
7871
|
-
|
|
7872
|
-
: (i) => (i.rule || 'unknown').replace(/^shield:[^:]+:/, '');
|
|
7873
|
-
const grouped = groupByKey(items, keyFn);
|
|
7874
|
-
return grouped
|
|
7875
|
-
.map(([rule, group]) => {
|
|
7876
|
-
const count = group.length;
|
|
7937
|
+
function ruleCardsForSection(section) {
|
|
7938
|
+
return (section.rules || [])
|
|
7939
|
+
.map((rule) => {
|
|
7940
|
+
const count = rule.findings.length;
|
|
7877
7941
|
const barPct = Math.round((count / maxBar) * 100);
|
|
7878
7942
|
const detailId = 'detail-' + Math.random().toString(36).slice(2);
|
|
7879
|
-
const
|
|
7943
|
+
const badgeClass =
|
|
7944
|
+
section.sourceType === 'user'
|
|
7945
|
+
? 'badge-user'
|
|
7946
|
+
: rule.verdict === 'block'
|
|
7947
|
+
? 'badge-block'
|
|
7948
|
+
: 'badge-review';
|
|
7949
|
+
const badgeLabel =
|
|
7950
|
+
section.sourceType === 'user'
|
|
7951
|
+
? rule.verdict === 'block'
|
|
7952
|
+
? 'YOUR BLOCK'
|
|
7953
|
+
: 'YOUR RULE'
|
|
7954
|
+
: rule.verdict === 'block'
|
|
7955
|
+
? 'BLOCK'
|
|
7956
|
+
: 'REVIEW';
|
|
7957
|
+
const barColor = section.sourceType === 'user' ? ';background:#388bfd33' : '';
|
|
7958
|
+
const rows = rule.findings
|
|
7959
|
+
.map((f) => findingRow(f.timestamp, esc(truncate(f.command || '', 120))))
|
|
7960
|
+
.join('');
|
|
7880
7961
|
return (
|
|
7881
7962
|
'<div class="scan-rule-row" onclick="var d=document.getElementById(\\'' +
|
|
7882
7963
|
detailId +
|
|
@@ -7887,12 +7968,12 @@ var init_ui = __esm({
|
|
|
7887
7968
|
badgeLabel +
|
|
7888
7969
|
'</span>' +
|
|
7889
7970
|
'<span class="scan-rule-name">' +
|
|
7890
|
-
esc(rule) +
|
|
7971
|
+
esc(rule.name) +
|
|
7891
7972
|
'</span>' +
|
|
7892
7973
|
'<span class="scan-rule-bar-wrap"><span class="scan-rule-bar" style="width:' +
|
|
7893
7974
|
barPct +
|
|
7894
7975
|
'%' +
|
|
7895
|
-
|
|
7976
|
+
barColor +
|
|
7896
7977
|
'"></span></span>' +
|
|
7897
7978
|
'<span class="scan-rule-count">' +
|
|
7898
7979
|
count +
|
|
@@ -7908,98 +7989,95 @@ var init_ui = __esm({
|
|
|
7908
7989
|
.join('');
|
|
7909
7990
|
}
|
|
7910
7991
|
|
|
7911
|
-
// \u2500\u2500
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
7915
|
-
|
|
7916
|
-
)
|
|
7917
|
-
|
|
7918
|
-
|
|
7919
|
-
|
|
7992
|
+
// \u2500\u2500 Section headings by sourceType / id \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
7993
|
+
function sectionHeading(section) {
|
|
7994
|
+
if (section.id === 'default') {
|
|
7995
|
+
return '\u{1F441} ' + esc(section.label) + ' \u2014 built-in, always on';
|
|
7996
|
+
}
|
|
7997
|
+
if (section.sourceType === 'shield') {
|
|
7998
|
+
return (
|
|
7999
|
+
'\u{1F6E1} ' +
|
|
8000
|
+
esc(section.label) +
|
|
8001
|
+
' shield' +
|
|
8002
|
+
(section.subtitle ? ' \u2014 ' + esc(section.subtitle) : '')
|
|
8003
|
+
);
|
|
8004
|
+
}
|
|
8005
|
+
if (section.id === 'cloud') {
|
|
8006
|
+
return '\u2601 Cloud Policy \u2014 synced from node9';
|
|
8007
|
+
}
|
|
8008
|
+
return '\u2705 Your Rules \u2014 working as configured';
|
|
8009
|
+
}
|
|
8010
|
+
function sectionColor(section) {
|
|
8011
|
+
if (section.blockedCount > 0) return '#e5534b';
|
|
8012
|
+
if (section.sourceType === 'user') return '#58a6ff';
|
|
8013
|
+
return 'var(--muted)';
|
|
8014
|
+
}
|
|
7920
8015
|
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
|
|
7926
|
-
|
|
7927
|
-
|
|
7928
|
-
|
|
7929
|
-
|
|
7930
|
-
|
|
7931
|
-
|
|
7932
|
-
const
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
const findingsTotal = f + l + lp;
|
|
8016
|
+
let html = statsHtml + agentBreakdown;
|
|
8017
|
+
|
|
8018
|
+
// \u2500\u2500 Leaks (shown first: credential exposure is highest-severity) \u2500\u2500\u2500\u2500\u2500
|
|
8019
|
+
if (leaksByPattern.length) {
|
|
8020
|
+
html +=
|
|
8021
|
+
'<div class="scan-rule-section-label" style="color:#e5534b">\u{1F511} Credential Leaks \u2014 secrets found in tool calls</div>';
|
|
8022
|
+
html += leaksByPattern
|
|
8023
|
+
.map(([pattern, group]) => {
|
|
8024
|
+
const count = group.length;
|
|
8025
|
+
const barPct = Math.round((count / maxBar) * 100);
|
|
8026
|
+
const detailId = 'detail-' + Math.random().toString(36).slice(2);
|
|
8027
|
+
const rows = group
|
|
8028
|
+
.map((l) => findingRow(l.timestamp, esc(l.redactedSample || '')))
|
|
8029
|
+
.join('');
|
|
7936
8030
|
return (
|
|
7937
|
-
'<div class="scan-
|
|
7938
|
-
|
|
7939
|
-
|
|
7940
|
-
' ' +
|
|
7941
|
-
|
|
7942
|
-
|
|
7943
|
-
'<span class="scan-agent-stat">' +
|
|
7944
|
-
(src.sessions || 0) +
|
|
7945
|
-
' session' +
|
|
7946
|
-
((src.sessions || 0) !== 1 ? 's' : '') +
|
|
8031
|
+
'<div class="scan-rule-row" onclick="var d=document.getElementById(\\'' +
|
|
8032
|
+
detailId +
|
|
8033
|
+
"');var a=this.querySelector('.scan-rule-arrow');d.style.display=d.style.display===''?'none':'';a.textContent=d.style.display===''?'\u25B2':'\u25BC';\\">" +
|
|
8034
|
+
'<span class="scan-verdict-badge badge-dlp">DLP</span>' +
|
|
8035
|
+
'<span class="scan-rule-name">' +
|
|
8036
|
+
esc(pattern) +
|
|
7947
8037
|
'</span>' +
|
|
7948
|
-
'<span class="scan-
|
|
7949
|
-
|
|
7950
|
-
|
|
7951
|
-
'
|
|
7952
|
-
|
|
8038
|
+
'<span class="scan-rule-bar-wrap"><span class="scan-rule-bar" style="width:' +
|
|
8039
|
+
barPct +
|
|
8040
|
+
'%"></span></span>' +
|
|
8041
|
+
'<span class="scan-rule-count">' +
|
|
8042
|
+
count +
|
|
7953
8043
|
'</span>' +
|
|
8044
|
+
'<span class="scan-rule-arrow">\u25BC</span>' +
|
|
8045
|
+
'</div><div id="' +
|
|
8046
|
+
detailId +
|
|
8047
|
+
'" style="display:none" class="scan-rule-detail">' +
|
|
8048
|
+
rows +
|
|
7954
8049
|
'</div>'
|
|
7955
8050
|
);
|
|
7956
|
-
})
|
|
7957
|
-
|
|
7958
|
-
return '<div class="scan-agent-group">' + rows.join('') + '</div>';
|
|
7959
|
-
})();
|
|
7960
|
-
|
|
7961
|
-
let html = stats + agentBreakdown;
|
|
7962
|
-
|
|
7963
|
-
if (allLeaks.length) {
|
|
7964
|
-
html +=
|
|
7965
|
-
'<div class="scan-rule-section-label" style="color:#e5534b">\u{1F511} Credential Leaks \u2014 secrets found in tool calls</div>';
|
|
7966
|
-
html += ruleSection(allLeaks, 'leak', 'badge-dlp', 'DLP', maxBar);
|
|
7967
|
-
}
|
|
7968
|
-
|
|
7969
|
-
if (blocked.length) {
|
|
7970
|
-
html +=
|
|
7971
|
-
'<div class="scan-rule-section-label" style="color:#e5534b">\u{1F6D1} Blocked \u2014 Node9 would have stopped these</div>';
|
|
7972
|
-
html += ruleSection(blocked, 'risk', 'badge-block', 'BLOCK', maxBar);
|
|
7973
|
-
}
|
|
7974
|
-
|
|
7975
|
-
if (supervised.length) {
|
|
7976
|
-
html +=
|
|
7977
|
-
'<div class="scan-rule-section-label">\u{1F441} Supervised \u2014 Node9 would have asked you first</div>';
|
|
7978
|
-
html += ruleSection(supervised, 'risk', 'badge-review', 'REVIEW', maxBar);
|
|
8051
|
+
})
|
|
8052
|
+
.join('');
|
|
7979
8053
|
}
|
|
7980
8054
|
|
|
7981
|
-
|
|
8055
|
+
// \u2500\u2500 Sections (pre-grouped by source on the server) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
8056
|
+
for (const section of sections) {
|
|
7982
8057
|
html +=
|
|
7983
|
-
'<div class="scan-rule-section-label" style="color
|
|
7984
|
-
|
|
8058
|
+
'<div class="scan-rule-section-label" style="color:' +
|
|
8059
|
+
sectionColor(section) +
|
|
8060
|
+
'">' +
|
|
8061
|
+
sectionHeading(section) +
|
|
8062
|
+
'</div>';
|
|
8063
|
+
html += ruleCardsForSection(section);
|
|
7985
8064
|
}
|
|
7986
8065
|
|
|
7987
|
-
// Loops: single collapsed summary card, expand to see all groups
|
|
7988
|
-
if (
|
|
7989
|
-
const totalReps =
|
|
8066
|
+
// \u2500\u2500 Loops: single collapsed summary card, expand to see all groups \u2500\u2500\u2500
|
|
8067
|
+
if (loops.length) {
|
|
8068
|
+
const totalReps = loops.reduce((sum, l) => sum + (l.count || 0), 0);
|
|
7990
8069
|
const loopsDetailId = 'loops-detail-' + Math.random().toString(36).slice(2);
|
|
7991
8070
|
const savingNote = savingsStr
|
|
7992
8071
|
? ' \xB7 <span style="color:var(--warning)">~' + savingsStr + ' wasted</span>'
|
|
7993
8072
|
: '';
|
|
7994
|
-
const
|
|
8073
|
+
const maxLoopCount = Math.max(...loops.map((l) => l.count || 0), 1);
|
|
8074
|
+
const loopGroupRows = loops
|
|
7995
8075
|
.map((item) => {
|
|
7996
8076
|
const date = new Date(item.timestamp).toLocaleDateString([], {
|
|
7997
8077
|
month: 'short',
|
|
7998
8078
|
day: 'numeric',
|
|
7999
8079
|
});
|
|
8000
|
-
const barPct = Math.round(
|
|
8001
|
-
(item.count / Math.max(...allLoops.map((l) => l.count), 1)) * 100
|
|
8002
|
-
);
|
|
8080
|
+
const barPct = Math.round((item.count / maxLoopCount) * 100);
|
|
8003
8081
|
const detailId = 'detail-' + Math.random().toString(36).slice(2);
|
|
8004
8082
|
return (
|
|
8005
8083
|
'<div class="scan-rule-row" onclick="event.stopPropagation();var d=document.getElementById(\\'' +
|
|
@@ -8037,9 +8115,9 @@ var init_ui = __esm({
|
|
|
8037
8115
|
"');var a=this.querySelector('.scan-rule-arrow');d.style.display=d.style.display===''?'none':'';a.textContent=d.style.display===''?'\u25B2':'\u25BC';\\">" +
|
|
8038
8116
|
'<span class="scan-verdict-badge badge-review">LOOP</span>' +
|
|
8039
8117
|
'<span class="scan-rule-name">' +
|
|
8040
|
-
|
|
8118
|
+
loops.length +
|
|
8041
8119
|
' pattern' +
|
|
8042
|
-
(
|
|
8120
|
+
(loops.length !== 1 ? 's' : '') +
|
|
8043
8121
|
' \xB7 ' +
|
|
8044
8122
|
totalReps +
|
|
8045
8123
|
' total repetitions' +
|
|
@@ -8113,10 +8191,20 @@ var init_ui = __esm({
|
|
|
8113
8191
|
const data = await res.json();
|
|
8114
8192
|
results.style.display = 'block';
|
|
8115
8193
|
|
|
8116
|
-
if (data.status === 'complete') {
|
|
8117
|
-
const
|
|
8118
|
-
const
|
|
8119
|
-
const
|
|
8194
|
+
if (data.status === 'complete' && data.summary) {
|
|
8195
|
+
const summary = data.summary;
|
|
8196
|
+
const byVerdict = summary.byVerdict || {};
|
|
8197
|
+
const sections = summary.sections || [];
|
|
8198
|
+
const leaks = summary.leaks || [];
|
|
8199
|
+
// Flatten rule findings across all sections so we can show a Top-N list
|
|
8200
|
+
const allRisks = [];
|
|
8201
|
+
for (const section of sections) {
|
|
8202
|
+
for (const rule of section.rules || []) {
|
|
8203
|
+
for (const f of rule.findings) {
|
|
8204
|
+
allRisks.push({ rule: rule.name, verdict: rule.verdict, ...f });
|
|
8205
|
+
}
|
|
8206
|
+
}
|
|
8207
|
+
}
|
|
8120
8208
|
|
|
8121
8209
|
let risksHtml = '';
|
|
8122
8210
|
if (allRisks.length > 0) {
|
|
@@ -8129,7 +8217,7 @@ var init_ui = __esm({
|
|
|
8129
8217
|
(r) => \`
|
|
8130
8218
|
<div class="finding-item">
|
|
8131
8219
|
<span class="finding-ts">\${new Date(r.timestamp).toLocaleDateString([], { month: 'short', day: 'numeric' })}</span>
|
|
8132
|
-
<span class="finding-rule">\${r.rule
|
|
8220
|
+
<span class="finding-rule">\${esc(r.rule)}</span>
|
|
8133
8221
|
<span class="finding-cmd"><span class="finding-badge \${r.verdict === 'block' ? 'badge-block' : ''}">\${r.verdict === 'block' ? '\u{1F6D1}' : '\u{1F441}\uFE0F'}</span>\${esc(truncate(r.command, 120))}</span>
|
|
8134
8222
|
</div>
|
|
8135
8223
|
\`
|
|
@@ -8140,18 +8228,18 @@ var init_ui = __esm({
|
|
|
8140
8228
|
}
|
|
8141
8229
|
|
|
8142
8230
|
let leaksHtml = '';
|
|
8143
|
-
if (
|
|
8231
|
+
if (leaks.length > 0) {
|
|
8144
8232
|
leaksHtml = \`
|
|
8145
8233
|
<div class="findings-list" style="border-color: rgba(201, 60, 55, 0.3);">
|
|
8146
8234
|
<div style="font-size: 10px; font-weight: 700; color: #f87171; margin-bottom: 8px; text-transform: uppercase;">Credential Leaks Identified</div>
|
|
8147
|
-
\${
|
|
8235
|
+
\${leaks
|
|
8148
8236
|
.slice(0, 5)
|
|
8149
8237
|
.map(
|
|
8150
8238
|
(l) => \`
|
|
8151
8239
|
<div class="finding-item">
|
|
8152
8240
|
<span class="finding-ts">\${new Date(l.timestamp).toLocaleDateString([], { month: 'short', day: 'numeric' })}</span>
|
|
8153
|
-
<span class="finding-rule">\${esc(l.
|
|
8154
|
-
<span class="finding-cmd"><span class="finding-badge badge-leak">\u{1F511}</span>\${esc(l.
|
|
8241
|
+
<span class="finding-rule">\${esc(l.patternName)}</span>
|
|
8242
|
+
<span class="finding-cmd"><span class="finding-badge badge-leak">\u{1F511}</span>\${esc(l.redactedSample)}</span>
|
|
8155
8243
|
</div>
|
|
8156
8244
|
\`
|
|
8157
8245
|
)
|
|
@@ -8160,10 +8248,11 @@ var init_ui = __esm({
|
|
|
8160
8248
|
\`;
|
|
8161
8249
|
}
|
|
8162
8250
|
|
|
8251
|
+
const riskCount = (byVerdict.blocked || 0) + (byVerdict.supervised || 0);
|
|
8163
8252
|
results.innerHTML = \`
|
|
8164
8253
|
<div class="insight-hint" style="color: #57ab5a; border-color: rgba(87, 171, 90, 0.4); background: rgba(87, 171, 90, 0.08);">
|
|
8165
8254
|
<strong>\u2705 History Audit Complete</strong><br>
|
|
8166
|
-
Scanned \${
|
|
8255
|
+
Scanned \${summary.stats?.sessions || 0} sessions. Found <strong>\${riskCount} risky operations</strong> and <strong>\${byVerdict.leaks || 0} secret leaks</strong>.
|
|
8167
8256
|
Node9 is now actively protecting you from these types of events.
|
|
8168
8257
|
</div>
|
|
8169
8258
|
\${leaksHtml}
|
|
@@ -8201,6 +8290,9 @@ var init_ui2 = __esm({
|
|
|
8201
8290
|
});
|
|
8202
8291
|
|
|
8203
8292
|
// src/cli/daemon-starter.ts
|
|
8293
|
+
function isTestingMode() {
|
|
8294
|
+
return /^(1|true|yes)$/i.test(process.env.NODE9_TESTING ?? "");
|
|
8295
|
+
}
|
|
8204
8296
|
function openBrowserLocal() {
|
|
8205
8297
|
const url = `http://${DAEMON_HOST}:${DAEMON_PORT}/`;
|
|
8206
8298
|
try {
|
|
@@ -8211,8 +8303,9 @@ function openBrowserLocal() {
|
|
|
8211
8303
|
} catch {
|
|
8212
8304
|
}
|
|
8213
8305
|
}
|
|
8214
|
-
async function autoStartDaemonAndWait() {
|
|
8215
|
-
if (
|
|
8306
|
+
async function autoStartDaemonAndWait(openBrowser2 = true) {
|
|
8307
|
+
if (isTestingMode()) return false;
|
|
8308
|
+
if (!import_path16.default.isAbsolute(process.argv[1])) return false;
|
|
8216
8309
|
try {
|
|
8217
8310
|
const child = (0, import_child_process3.spawn)(process.execPath, [process.argv[1], "daemon"], {
|
|
8218
8311
|
detached: true,
|
|
@@ -8230,7 +8323,9 @@ async function autoStartDaemonAndWait() {
|
|
|
8230
8323
|
signal: AbortSignal.timeout(500)
|
|
8231
8324
|
});
|
|
8232
8325
|
if (res.ok) {
|
|
8233
|
-
|
|
8326
|
+
if (openBrowser2) {
|
|
8327
|
+
openBrowserLocal();
|
|
8328
|
+
}
|
|
8234
8329
|
return true;
|
|
8235
8330
|
}
|
|
8236
8331
|
} catch {
|
|
@@ -8240,15 +8335,205 @@ async function autoStartDaemonAndWait() {
|
|
|
8240
8335
|
}
|
|
8241
8336
|
return false;
|
|
8242
8337
|
}
|
|
8243
|
-
var import_child_process3;
|
|
8338
|
+
var import_child_process3, import_path16;
|
|
8244
8339
|
var init_daemon_starter = __esm({
|
|
8245
8340
|
"src/cli/daemon-starter.ts"() {
|
|
8246
8341
|
"use strict";
|
|
8247
8342
|
import_child_process3 = require("child_process");
|
|
8343
|
+
import_path16 = __toESM(require("path"));
|
|
8248
8344
|
init_daemon();
|
|
8249
8345
|
}
|
|
8250
8346
|
});
|
|
8251
8347
|
|
|
8348
|
+
// src/scan-summary.ts
|
|
8349
|
+
function buildScanSummary(agents) {
|
|
8350
|
+
const stats = {
|
|
8351
|
+
sessions: 0,
|
|
8352
|
+
totalToolCalls: 0,
|
|
8353
|
+
bashCalls: 0,
|
|
8354
|
+
totalCostUSD: 0,
|
|
8355
|
+
firstDate: null,
|
|
8356
|
+
lastDate: null
|
|
8357
|
+
};
|
|
8358
|
+
for (const a of agents) {
|
|
8359
|
+
stats.sessions += a.scan.sessions;
|
|
8360
|
+
stats.totalToolCalls += a.scan.totalToolCalls;
|
|
8361
|
+
stats.bashCalls += a.scan.bashCalls;
|
|
8362
|
+
stats.totalCostUSD += a.scan.totalCostUSD;
|
|
8363
|
+
if (a.scan.firstDate && (!stats.firstDate || a.scan.firstDate < stats.firstDate)) {
|
|
8364
|
+
stats.firstDate = a.scan.firstDate;
|
|
8365
|
+
}
|
|
8366
|
+
if (a.scan.lastDate && (!stats.lastDate || a.scan.lastDate > stats.lastDate)) {
|
|
8367
|
+
stats.lastDate = a.scan.lastDate;
|
|
8368
|
+
}
|
|
8369
|
+
}
|
|
8370
|
+
const allFindings = agents.flatMap((a) => a.scan.findings);
|
|
8371
|
+
const allLeaks = agents.flatMap(
|
|
8372
|
+
(a) => a.scan.dlpFindings.map((f) => ({
|
|
8373
|
+
patternName: f.patternName,
|
|
8374
|
+
redactedSample: f.redactedSample,
|
|
8375
|
+
toolName: f.toolName,
|
|
8376
|
+
timestamp: f.timestamp,
|
|
8377
|
+
project: f.project,
|
|
8378
|
+
sessionId: f.sessionId,
|
|
8379
|
+
agent: f.agent
|
|
8380
|
+
}))
|
|
8381
|
+
);
|
|
8382
|
+
const allLoops = agents.flatMap(
|
|
8383
|
+
(a) => a.scan.loopFindings.map((f) => ({
|
|
8384
|
+
toolName: f.toolName,
|
|
8385
|
+
commandPreview: f.commandPreview,
|
|
8386
|
+
count: f.count,
|
|
8387
|
+
timestamp: f.timestamp,
|
|
8388
|
+
project: f.project,
|
|
8389
|
+
sessionId: f.sessionId,
|
|
8390
|
+
agent: f.agent
|
|
8391
|
+
}))
|
|
8392
|
+
);
|
|
8393
|
+
const byVerdict = {
|
|
8394
|
+
blocked: allFindings.filter((f) => f.source.rule.verdict === "block").length,
|
|
8395
|
+
supervised: allFindings.filter((f) => f.source.rule.verdict === "review").length,
|
|
8396
|
+
leaks: allLeaks.length,
|
|
8397
|
+
loops: allLoops.length
|
|
8398
|
+
};
|
|
8399
|
+
const byAgent = agents.map((a) => ({
|
|
8400
|
+
id: a.id,
|
|
8401
|
+
label: a.label,
|
|
8402
|
+
icon: a.icon,
|
|
8403
|
+
sessions: a.scan.sessions,
|
|
8404
|
+
findings: a.scan.findings.length + a.scan.dlpFindings.length + a.scan.loopFindings.length,
|
|
8405
|
+
costUSD: a.scan.totalCostUSD
|
|
8406
|
+
})).filter((s) => s.sessions > 0 || s.findings > 0);
|
|
8407
|
+
const sections = buildSections(allFindings);
|
|
8408
|
+
const wastedIters = allLoops.reduce(
|
|
8409
|
+
(sum, l) => sum + Math.max(0, l.count - LOOP_THRESHOLD_FOR_WASTE),
|
|
8410
|
+
0
|
|
8411
|
+
);
|
|
8412
|
+
const loopWastedUSD = wastedIters * COST_PER_LOOP_ITER_USD;
|
|
8413
|
+
return {
|
|
8414
|
+
stats,
|
|
8415
|
+
byVerdict,
|
|
8416
|
+
byAgent,
|
|
8417
|
+
sections,
|
|
8418
|
+
leaks: allLeaks,
|
|
8419
|
+
loops: allLoops,
|
|
8420
|
+
loopWastedUSD
|
|
8421
|
+
};
|
|
8422
|
+
}
|
|
8423
|
+
function buildSections(findings) {
|
|
8424
|
+
const sectionMap = /* @__PURE__ */ new Map();
|
|
8425
|
+
function ensureSection(id, label, subtitle, sourceType, shieldKey) {
|
|
8426
|
+
let s = sectionMap.get(id);
|
|
8427
|
+
if (!s) {
|
|
8428
|
+
s = {
|
|
8429
|
+
id,
|
|
8430
|
+
label,
|
|
8431
|
+
subtitle,
|
|
8432
|
+
sourceType,
|
|
8433
|
+
shieldKey,
|
|
8434
|
+
blockedCount: 0,
|
|
8435
|
+
reviewCount: 0,
|
|
8436
|
+
rules: []
|
|
8437
|
+
};
|
|
8438
|
+
sectionMap.set(id, s);
|
|
8439
|
+
}
|
|
8440
|
+
return s;
|
|
8441
|
+
}
|
|
8442
|
+
const ruleMap = /* @__PURE__ */ new Map();
|
|
8443
|
+
for (const f of findings) {
|
|
8444
|
+
const src = f.source;
|
|
8445
|
+
const sourceType = src.sourceType;
|
|
8446
|
+
const shieldName = src.shieldName;
|
|
8447
|
+
const verdict = src.rule.verdict === "block" ? "block" : "review";
|
|
8448
|
+
let sectionId;
|
|
8449
|
+
let sectionLabel;
|
|
8450
|
+
let sectionSubtitle;
|
|
8451
|
+
let shieldKey;
|
|
8452
|
+
if (sourceType === "default") {
|
|
8453
|
+
sectionId = "default";
|
|
8454
|
+
sectionLabel = "Default Rules";
|
|
8455
|
+
sectionSubtitle = "built-in, always on";
|
|
8456
|
+
} else if (sourceType === "shield") {
|
|
8457
|
+
sectionId = `shield:${shieldName}`;
|
|
8458
|
+
sectionLabel = shieldName;
|
|
8459
|
+
sectionSubtitle = SHIELDS[shieldName]?.description ?? "";
|
|
8460
|
+
shieldKey = shieldName;
|
|
8461
|
+
} else if (shieldName === "cloud") {
|
|
8462
|
+
sectionId = "cloud";
|
|
8463
|
+
sectionLabel = "Cloud Policy";
|
|
8464
|
+
sectionSubtitle = "synced from node9 cloud";
|
|
8465
|
+
} else {
|
|
8466
|
+
sectionId = "user";
|
|
8467
|
+
sectionLabel = "Your Rules";
|
|
8468
|
+
sectionSubtitle = "added in node9.config.json";
|
|
8469
|
+
}
|
|
8470
|
+
const section = ensureSection(sectionId, sectionLabel, sectionSubtitle, sourceType, shieldKey);
|
|
8471
|
+
const ruleDisplayName = (src.rule.name ?? "unnamed").replace(/^shield:[^:]+:/, "");
|
|
8472
|
+
const ruleKey = sectionId + "::" + ruleDisplayName;
|
|
8473
|
+
let rule = ruleMap.get(ruleKey);
|
|
8474
|
+
if (!rule) {
|
|
8475
|
+
rule = {
|
|
8476
|
+
name: ruleDisplayName,
|
|
8477
|
+
verdict,
|
|
8478
|
+
reason: src.rule.reason ?? "",
|
|
8479
|
+
findings: []
|
|
8480
|
+
};
|
|
8481
|
+
ruleMap.set(ruleKey, rule);
|
|
8482
|
+
section.rules.push(rule);
|
|
8483
|
+
}
|
|
8484
|
+
const cmdPreview = previewCommand(f.input, 120);
|
|
8485
|
+
const fullCmd = fullCommandOf(f.input);
|
|
8486
|
+
const isDupe = rule.findings.some((x) => x.project === f.project && x.command === cmdPreview);
|
|
8487
|
+
if (!isDupe) {
|
|
8488
|
+
rule.findings.push({
|
|
8489
|
+
timestamp: f.timestamp ?? "",
|
|
8490
|
+
command: cmdPreview,
|
|
8491
|
+
fullCommand: fullCmd,
|
|
8492
|
+
project: f.project,
|
|
8493
|
+
sessionId: f.sessionId,
|
|
8494
|
+
agent: f.agent,
|
|
8495
|
+
toolName: f.toolName
|
|
8496
|
+
});
|
|
8497
|
+
}
|
|
8498
|
+
if (verdict === "block") section.blockedCount++;
|
|
8499
|
+
else section.reviewCount++;
|
|
8500
|
+
}
|
|
8501
|
+
const sections = [...sectionMap.values()];
|
|
8502
|
+
sections.sort((a, b) => {
|
|
8503
|
+
const aTotal = a.blockedCount + a.reviewCount;
|
|
8504
|
+
const bTotal = b.blockedCount + b.reviewCount;
|
|
8505
|
+
if (b.blockedCount !== a.blockedCount) return b.blockedCount - a.blockedCount;
|
|
8506
|
+
return bTotal - aTotal;
|
|
8507
|
+
});
|
|
8508
|
+
for (const s of sections) {
|
|
8509
|
+
s.rules.sort((a, b) => {
|
|
8510
|
+
const aBlock = a.verdict === "block" ? 1 : 0;
|
|
8511
|
+
const bBlock = b.verdict === "block" ? 1 : 0;
|
|
8512
|
+
if (bBlock !== aBlock) return bBlock - aBlock;
|
|
8513
|
+
return b.findings.length - a.findings.length;
|
|
8514
|
+
});
|
|
8515
|
+
}
|
|
8516
|
+
return sections;
|
|
8517
|
+
}
|
|
8518
|
+
function previewCommand(input, max) {
|
|
8519
|
+
const raw = input.command ?? input.query ?? input.file_path ?? JSON.stringify(input);
|
|
8520
|
+
const s = String(raw).replace(/\s+/g, " ").trim();
|
|
8521
|
+
return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
|
|
8522
|
+
}
|
|
8523
|
+
function fullCommandOf(input) {
|
|
8524
|
+
const raw = input.command ?? input.query ?? input.file_path ?? JSON.stringify(input);
|
|
8525
|
+
return String(raw).replace(/\s+/g, " ").trim();
|
|
8526
|
+
}
|
|
8527
|
+
var LOOP_THRESHOLD_FOR_WASTE, COST_PER_LOOP_ITER_USD;
|
|
8528
|
+
var init_scan_summary = __esm({
|
|
8529
|
+
"src/scan-summary.ts"() {
|
|
8530
|
+
"use strict";
|
|
8531
|
+
init_shields();
|
|
8532
|
+
LOOP_THRESHOLD_FOR_WASTE = 3;
|
|
8533
|
+
COST_PER_LOOP_ITER_USD = 6e-3;
|
|
8534
|
+
}
|
|
8535
|
+
});
|
|
8536
|
+
|
|
8252
8537
|
// src/cli/commands/scan.ts
|
|
8253
8538
|
function claudeModelPrice(model) {
|
|
8254
8539
|
const base = model.replace(/@.*$/, "").replace(/-\d{8}$/, "");
|
|
@@ -8289,10 +8574,6 @@ function preview(input, max) {
|
|
|
8289
8574
|
const s = String(cmd).replace(/\s+/g, " ").trim();
|
|
8290
8575
|
return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
|
|
8291
8576
|
}
|
|
8292
|
-
function fullCommand(input) {
|
|
8293
|
-
const cmd = input.command ?? input.query ?? input.file_path ?? JSON.stringify(input);
|
|
8294
|
-
return String(cmd).replace(/\s+/g, " ").trim();
|
|
8295
|
-
}
|
|
8296
8577
|
function detectLoops(calls, project, sessionId, agent) {
|
|
8297
8578
|
const counts = /* @__PURE__ */ new Map();
|
|
8298
8579
|
for (const call of calls) {
|
|
@@ -8352,11 +8633,11 @@ function buildRuleSources() {
|
|
|
8352
8633
|
}
|
|
8353
8634
|
function countScanFiles() {
|
|
8354
8635
|
let total = 0;
|
|
8355
|
-
const claudeDir =
|
|
8636
|
+
const claudeDir = import_path17.default.join(import_os12.default.homedir(), ".claude", "projects");
|
|
8356
8637
|
if (import_fs13.default.existsSync(claudeDir)) {
|
|
8357
8638
|
try {
|
|
8358
8639
|
for (const proj of import_fs13.default.readdirSync(claudeDir)) {
|
|
8359
|
-
const p =
|
|
8640
|
+
const p = import_path17.default.join(claudeDir, proj);
|
|
8360
8641
|
try {
|
|
8361
8642
|
if (!import_fs13.default.statSync(p).isDirectory()) continue;
|
|
8362
8643
|
total += import_fs13.default.readdirSync(p).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).length;
|
|
@@ -8367,14 +8648,14 @@ function countScanFiles() {
|
|
|
8367
8648
|
} catch {
|
|
8368
8649
|
}
|
|
8369
8650
|
}
|
|
8370
|
-
const geminiDir =
|
|
8651
|
+
const geminiDir = import_path17.default.join(import_os12.default.homedir(), ".gemini", "tmp");
|
|
8371
8652
|
if (import_fs13.default.existsSync(geminiDir)) {
|
|
8372
8653
|
try {
|
|
8373
8654
|
for (const slug of import_fs13.default.readdirSync(geminiDir)) {
|
|
8374
|
-
const p =
|
|
8655
|
+
const p = import_path17.default.join(geminiDir, slug);
|
|
8375
8656
|
try {
|
|
8376
8657
|
if (!import_fs13.default.statSync(p).isDirectory()) continue;
|
|
8377
|
-
const chatsDir =
|
|
8658
|
+
const chatsDir = import_path17.default.join(p, "chats");
|
|
8378
8659
|
if (import_fs13.default.existsSync(chatsDir)) {
|
|
8379
8660
|
try {
|
|
8380
8661
|
total += import_fs13.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json")).length;
|
|
@@ -8388,19 +8669,19 @@ function countScanFiles() {
|
|
|
8388
8669
|
} catch {
|
|
8389
8670
|
}
|
|
8390
8671
|
}
|
|
8391
|
-
const codexDir =
|
|
8672
|
+
const codexDir = import_path17.default.join(import_os12.default.homedir(), ".codex", "sessions");
|
|
8392
8673
|
if (import_fs13.default.existsSync(codexDir)) {
|
|
8393
8674
|
try {
|
|
8394
8675
|
for (const year of import_fs13.default.readdirSync(codexDir)) {
|
|
8395
|
-
const yp =
|
|
8676
|
+
const yp = import_path17.default.join(codexDir, year);
|
|
8396
8677
|
try {
|
|
8397
8678
|
if (!import_fs13.default.statSync(yp).isDirectory()) continue;
|
|
8398
8679
|
for (const month of import_fs13.default.readdirSync(yp)) {
|
|
8399
|
-
const mp =
|
|
8680
|
+
const mp = import_path17.default.join(yp, month);
|
|
8400
8681
|
try {
|
|
8401
8682
|
if (!import_fs13.default.statSync(mp).isDirectory()) continue;
|
|
8402
8683
|
for (const day of import_fs13.default.readdirSync(mp)) {
|
|
8403
|
-
const dp =
|
|
8684
|
+
const dp = import_path17.default.join(mp, day);
|
|
8404
8685
|
try {
|
|
8405
8686
|
if (!import_fs13.default.statSync(dp).isDirectory()) continue;
|
|
8406
8687
|
total += import_fs13.default.readdirSync(dp).filter((f) => f.endsWith(".jsonl")).length;
|
|
@@ -8432,7 +8713,7 @@ function renderProgressBar(done, total) {
|
|
|
8432
8713
|
);
|
|
8433
8714
|
}
|
|
8434
8715
|
function scanClaudeHistory(startDate, onProgress) {
|
|
8435
|
-
const projectsDir =
|
|
8716
|
+
const projectsDir = import_path17.default.join(import_os12.default.homedir(), ".claude", "projects");
|
|
8436
8717
|
const result = {
|
|
8437
8718
|
filesScanned: 0,
|
|
8438
8719
|
sessions: 0,
|
|
@@ -8454,7 +8735,7 @@ function scanClaudeHistory(startDate, onProgress) {
|
|
|
8454
8735
|
}
|
|
8455
8736
|
const ruleSources = buildRuleSources();
|
|
8456
8737
|
for (const proj of projDirs) {
|
|
8457
|
-
const projPath =
|
|
8738
|
+
const projPath = import_path17.default.join(projectsDir, proj);
|
|
8458
8739
|
try {
|
|
8459
8740
|
if (!import_fs13.default.statSync(projPath).isDirectory()) continue;
|
|
8460
8741
|
} catch {
|
|
@@ -8474,7 +8755,7 @@ function scanClaudeHistory(startDate, onProgress) {
|
|
|
8474
8755
|
const sessionId = file.replace(/\.jsonl$/, "");
|
|
8475
8756
|
let raw;
|
|
8476
8757
|
try {
|
|
8477
|
-
raw = import_fs13.default.readFileSync(
|
|
8758
|
+
raw = import_fs13.default.readFileSync(import_path17.default.join(projPath, file), "utf-8");
|
|
8478
8759
|
} catch {
|
|
8479
8760
|
continue;
|
|
8480
8761
|
}
|
|
@@ -8627,7 +8908,7 @@ function scanClaudeHistory(startDate, onProgress) {
|
|
|
8627
8908
|
return result;
|
|
8628
8909
|
}
|
|
8629
8910
|
function scanGeminiHistory(startDate, onProgress) {
|
|
8630
|
-
const tmpDir =
|
|
8911
|
+
const tmpDir = import_path17.default.join(import_os12.default.homedir(), ".gemini", "tmp");
|
|
8631
8912
|
const result = {
|
|
8632
8913
|
filesScanned: 0,
|
|
8633
8914
|
sessions: 0,
|
|
@@ -8649,7 +8930,7 @@ function scanGeminiHistory(startDate, onProgress) {
|
|
|
8649
8930
|
}
|
|
8650
8931
|
const ruleSources = buildRuleSources();
|
|
8651
8932
|
for (const slug of slugDirs) {
|
|
8652
|
-
const slugPath =
|
|
8933
|
+
const slugPath = import_path17.default.join(tmpDir, slug);
|
|
8653
8934
|
try {
|
|
8654
8935
|
if (!import_fs13.default.statSync(slugPath).isDirectory()) continue;
|
|
8655
8936
|
} catch {
|
|
@@ -8657,10 +8938,10 @@ function scanGeminiHistory(startDate, onProgress) {
|
|
|
8657
8938
|
}
|
|
8658
8939
|
let projLabel = slug;
|
|
8659
8940
|
try {
|
|
8660
|
-
projLabel = import_fs13.default.readFileSync(
|
|
8941
|
+
projLabel = import_fs13.default.readFileSync(import_path17.default.join(slugPath, ".project_root"), "utf-8").trim().replace(import_os12.default.homedir(), "~").slice(0, 40);
|
|
8661
8942
|
} catch {
|
|
8662
8943
|
}
|
|
8663
|
-
const chatsDir =
|
|
8944
|
+
const chatsDir = import_path17.default.join(slugPath, "chats");
|
|
8664
8945
|
if (!import_fs13.default.existsSync(chatsDir)) continue;
|
|
8665
8946
|
let chatFiles;
|
|
8666
8947
|
try {
|
|
@@ -8674,7 +8955,7 @@ function scanGeminiHistory(startDate, onProgress) {
|
|
|
8674
8955
|
const sessionId = chatFile.replace(/\.json$/, "");
|
|
8675
8956
|
let raw;
|
|
8676
8957
|
try {
|
|
8677
|
-
raw = import_fs13.default.readFileSync(
|
|
8958
|
+
raw = import_fs13.default.readFileSync(import_path17.default.join(chatsDir, chatFile), "utf-8");
|
|
8678
8959
|
} catch {
|
|
8679
8960
|
continue;
|
|
8680
8961
|
}
|
|
@@ -8823,7 +9104,7 @@ function scanGeminiHistory(startDate, onProgress) {
|
|
|
8823
9104
|
return result;
|
|
8824
9105
|
}
|
|
8825
9106
|
function scanCodexHistory(startDate, onProgress) {
|
|
8826
|
-
const sessionsBase =
|
|
9107
|
+
const sessionsBase = import_path17.default.join(import_os12.default.homedir(), ".codex", "sessions");
|
|
8827
9108
|
const result = {
|
|
8828
9109
|
filesScanned: 0,
|
|
8829
9110
|
sessions: 0,
|
|
@@ -8840,28 +9121,28 @@ function scanCodexHistory(startDate, onProgress) {
|
|
|
8840
9121
|
const jsonlFiles = [];
|
|
8841
9122
|
try {
|
|
8842
9123
|
for (const year of import_fs13.default.readdirSync(sessionsBase)) {
|
|
8843
|
-
const yearPath =
|
|
9124
|
+
const yearPath = import_path17.default.join(sessionsBase, year);
|
|
8844
9125
|
try {
|
|
8845
9126
|
if (!import_fs13.default.statSync(yearPath).isDirectory()) continue;
|
|
8846
9127
|
} catch {
|
|
8847
9128
|
continue;
|
|
8848
9129
|
}
|
|
8849
9130
|
for (const month of import_fs13.default.readdirSync(yearPath)) {
|
|
8850
|
-
const monthPath =
|
|
9131
|
+
const monthPath = import_path17.default.join(yearPath, month);
|
|
8851
9132
|
try {
|
|
8852
9133
|
if (!import_fs13.default.statSync(monthPath).isDirectory()) continue;
|
|
8853
9134
|
} catch {
|
|
8854
9135
|
continue;
|
|
8855
9136
|
}
|
|
8856
9137
|
for (const day of import_fs13.default.readdirSync(monthPath)) {
|
|
8857
|
-
const dayPath =
|
|
9138
|
+
const dayPath = import_path17.default.join(monthPath, day);
|
|
8858
9139
|
try {
|
|
8859
9140
|
if (!import_fs13.default.statSync(dayPath).isDirectory()) continue;
|
|
8860
9141
|
} catch {
|
|
8861
9142
|
continue;
|
|
8862
9143
|
}
|
|
8863
9144
|
for (const file of import_fs13.default.readdirSync(dayPath)) {
|
|
8864
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(
|
|
9145
|
+
if (file.endsWith(".jsonl")) jsonlFiles.push(import_path17.default.join(dayPath, file));
|
|
8865
9146
|
}
|
|
8866
9147
|
}
|
|
8867
9148
|
}
|
|
@@ -9063,26 +9344,32 @@ function printFindingRow(f, drillDown, showSessionId, previewWidth) {
|
|
|
9063
9344
|
const ts = f.timestamp ? import_chalk2.default.dim(fmtTs(f.timestamp) + " ") : "";
|
|
9064
9345
|
const proj = import_chalk2.default.dim(f.project.slice(0, 22).padEnd(22) + " ");
|
|
9065
9346
|
const agentBadge = f.agent === "gemini" ? import_chalk2.default.blue("[Gemini] ") : f.agent === "codex" ? import_chalk2.default.magenta("[Codex] ") : import_chalk2.default.cyan("[Claude] ");
|
|
9066
|
-
|
|
9347
|
+
let cmdText;
|
|
9348
|
+
if (drillDown) {
|
|
9349
|
+
cmdText = f.fullCommand;
|
|
9350
|
+
} else {
|
|
9351
|
+
cmdText = f.command;
|
|
9352
|
+
if (cmdText.length > previewWidth) cmdText = cmdText.slice(0, previewWidth - 1) + "\u2026";
|
|
9353
|
+
}
|
|
9354
|
+
const cmd = import_chalk2.default.gray(cmdText);
|
|
9067
9355
|
const sessionSuffix = showSessionId && f.sessionId ? import_chalk2.default.dim(` \u2192 ${f.sessionId.slice(0, 8)}`) : "";
|
|
9068
9356
|
console.log(` ${ts}${proj}${agentBadge}${cmd}${sessionSuffix}`);
|
|
9069
9357
|
}
|
|
9070
|
-
function printRuleGroup(
|
|
9071
|
-
const
|
|
9072
|
-
const ruleCount =
|
|
9358
|
+
function printRuleGroup(rule, topN, drillDown, previewWidth) {
|
|
9359
|
+
const findings = rule.findings;
|
|
9360
|
+
const ruleCount = findings.length;
|
|
9073
9361
|
const countBadge = ruleCount > 1 ? import_chalk2.default.white(` \xD7${ruleCount}`) : "";
|
|
9074
|
-
const
|
|
9075
|
-
const icon = verdictIcon(rule.verdict ?? "review");
|
|
9362
|
+
const icon = verdictIcon(rule.verdict);
|
|
9076
9363
|
console.log(
|
|
9077
|
-
" " + icon + " " + import_chalk2.default.white(
|
|
9364
|
+
" " + icon + " " + import_chalk2.default.white(rule.name) + countBadge + (rule.reason ? import_chalk2.default.dim(` \u2014 ${rule.reason}`) : "")
|
|
9078
9365
|
);
|
|
9079
|
-
const shown = drillDown ?
|
|
9366
|
+
const shown = drillDown ? findings : findings.slice(0, topN);
|
|
9080
9367
|
for (const f of shown) {
|
|
9081
9368
|
printFindingRow(f, drillDown, drillDown, previewWidth);
|
|
9082
9369
|
}
|
|
9083
|
-
if (!drillDown &&
|
|
9370
|
+
if (!drillDown && findings.length > topN) {
|
|
9084
9371
|
console.log(
|
|
9085
|
-
import_chalk2.default.dim(` \u2026 and ${
|
|
9372
|
+
import_chalk2.default.dim(` \u2026 and ${findings.length - topN} more (--drill-down for full list)`)
|
|
9086
9373
|
);
|
|
9087
9374
|
}
|
|
9088
9375
|
}
|
|
@@ -9097,7 +9384,7 @@ function registerScanCommand(program2) {
|
|
|
9097
9384
|
d.setHours(0, 0, 0, 0);
|
|
9098
9385
|
return d;
|
|
9099
9386
|
})();
|
|
9100
|
-
const isInstalled = import_fs13.default.existsSync(
|
|
9387
|
+
const isInstalled = import_fs13.default.existsSync(import_path17.default.join(import_os12.default.homedir(), ".node9", "audit.log"));
|
|
9101
9388
|
console.log("");
|
|
9102
9389
|
if (!isInstalled) {
|
|
9103
9390
|
console.log(
|
|
@@ -9129,6 +9416,11 @@ function registerScanCommand(program2) {
|
|
|
9129
9416
|
(done) => onProgress(claudeScan.filesScanned + geminiScan.filesScanned + done)
|
|
9130
9417
|
);
|
|
9131
9418
|
const scan = mergeScans(mergeScans(claudeScan, geminiScan), codexScan);
|
|
9419
|
+
const summary = buildScanSummary([
|
|
9420
|
+
{ id: "claude", label: "Claude", icon: "\u{1F916}", scan: claudeScan },
|
|
9421
|
+
{ id: "gemini", label: "Gemini", icon: "\u264A", scan: geminiScan },
|
|
9422
|
+
{ id: "codex", label: "Codex", icon: "\u{1F52E}", scan: codexScan }
|
|
9423
|
+
]);
|
|
9132
9424
|
process.stdout.write("\r" + " ".repeat(60) + "\r");
|
|
9133
9425
|
if (scan.filesScanned === 0) {
|
|
9134
9426
|
console.log(import_chalk2.default.yellow(" No session history found."));
|
|
@@ -9191,76 +9483,27 @@ function registerScanCommand(program2) {
|
|
|
9191
9483
|
);
|
|
9192
9484
|
}
|
|
9193
9485
|
console.log("");
|
|
9194
|
-
const sections
|
|
9195
|
-
const defaultFindings = scan.findings.filter((f) => f.source.sourceType === "default");
|
|
9196
|
-
if (defaultFindings.length > 0) {
|
|
9197
|
-
sections.push({
|
|
9198
|
-
label: "Default Rules",
|
|
9199
|
-
subtitle: "built-in, always on",
|
|
9200
|
-
findings: defaultFindings
|
|
9201
|
-
});
|
|
9202
|
-
}
|
|
9203
|
-
const byShield = /* @__PURE__ */ new Map();
|
|
9204
|
-
for (const f of scan.findings.filter((f2) => f2.source.sourceType === "shield")) {
|
|
9205
|
-
const arr = byShield.get(f.source.shieldName) ?? [];
|
|
9206
|
-
arr.push(f);
|
|
9207
|
-
byShield.set(f.source.shieldName, arr);
|
|
9208
|
-
}
|
|
9209
|
-
const shieldsWithFindings = [...byShield.entries()].sort(
|
|
9210
|
-
(a, b) => b[1].length - a[1].length
|
|
9211
|
-
);
|
|
9212
|
-
for (const [shieldName, findings] of shieldsWithFindings) {
|
|
9213
|
-
const description = SHIELDS[shieldName]?.description ?? "";
|
|
9214
|
-
sections.push({
|
|
9215
|
-
label: shieldName,
|
|
9216
|
-
subtitle: description,
|
|
9217
|
-
shieldKey: shieldName,
|
|
9218
|
-
findings
|
|
9219
|
-
});
|
|
9220
|
-
}
|
|
9221
|
-
const userFindings = scan.findings.filter(
|
|
9222
|
-
(f) => f.source.sourceType === "user" || f.source.shieldName === "cloud"
|
|
9223
|
-
);
|
|
9224
|
-
if (userFindings.length > 0) {
|
|
9225
|
-
sections.push({
|
|
9226
|
-
label: "Your Rules",
|
|
9227
|
-
subtitle: "added in node9.config.json",
|
|
9228
|
-
findings: userFindings
|
|
9229
|
-
});
|
|
9230
|
-
}
|
|
9231
|
-
for (const section of sections) {
|
|
9232
|
-
const sectionBlocked = section.findings.filter(
|
|
9233
|
-
(f) => f.source.rule.verdict === "block"
|
|
9234
|
-
).length;
|
|
9235
|
-
const sectionReview = section.findings.length - sectionBlocked;
|
|
9486
|
+
for (const section of summary.sections) {
|
|
9236
9487
|
const countParts = [];
|
|
9237
|
-
if (
|
|
9238
|
-
|
|
9488
|
+
if (section.blockedCount > 0)
|
|
9489
|
+
countParts.push(import_chalk2.default.red(`${section.blockedCount} blocked`));
|
|
9490
|
+
if (section.reviewCount > 0)
|
|
9491
|
+
countParts.push(import_chalk2.default.yellow(`${section.reviewCount} review`));
|
|
9239
9492
|
const countStr = countParts.join(import_chalk2.default.dim(" \xB7 "));
|
|
9240
9493
|
const enableHint = section.shieldKey ? import_chalk2.default.dim(` \u2192 node9 shield enable ${section.shieldKey}`) : "";
|
|
9241
9494
|
console.log(" " + import_chalk2.default.dim("\u2500".repeat(70)));
|
|
9242
9495
|
console.log(
|
|
9243
9496
|
" " + import_chalk2.default.bold(section.label) + (section.subtitle ? import_chalk2.default.dim(` \xB7 ${section.subtitle}`) : "") + " " + countStr + enableHint
|
|
9244
9497
|
);
|
|
9245
|
-
const
|
|
9246
|
-
|
|
9247
|
-
const ruleKey = f.source.rule.name ?? "unnamed";
|
|
9248
|
-
const arr = byRule.get(ruleKey) ?? [];
|
|
9249
|
-
arr.push(f);
|
|
9250
|
-
byRule.set(ruleKey, arr);
|
|
9251
|
-
}
|
|
9252
|
-
const sortedRules = [...byRule.entries()].sort((a, b) => {
|
|
9253
|
-
const aBlock = a[1][0].source.rule.verdict === "block" ? 1 : 0;
|
|
9254
|
-
const bBlock = b[1][0].source.rule.verdict === "block" ? 1 : 0;
|
|
9255
|
-
if (bBlock !== aBlock) return bBlock - aBlock;
|
|
9256
|
-
return b[1].length - a[1].length;
|
|
9257
|
-
});
|
|
9258
|
-
for (const [, ruleFindings] of sortedRules) {
|
|
9259
|
-
printRuleGroup(ruleFindings, topN, drillDown, previewWidth);
|
|
9498
|
+
for (const rule of section.rules) {
|
|
9499
|
+
printRuleGroup(rule, topN, drillDown, previewWidth);
|
|
9260
9500
|
}
|
|
9261
9501
|
console.log("");
|
|
9262
9502
|
}
|
|
9263
|
-
const
|
|
9503
|
+
const activeShieldIds = new Set(
|
|
9504
|
+
summary.sections.filter((s) => s.sourceType === "shield" && s.shieldKey).map((s) => s.shieldKey)
|
|
9505
|
+
);
|
|
9506
|
+
const emptyShields = Object.keys(SHIELDS).filter((n) => !activeShieldIds.has(n)).sort();
|
|
9264
9507
|
if (emptyShields.length > 0) {
|
|
9265
9508
|
console.log(" " + import_chalk2.default.dim("\u2500".repeat(70)));
|
|
9266
9509
|
console.log(
|
|
@@ -9366,82 +9609,16 @@ function registerScanCommand(program2) {
|
|
|
9366
9609
|
console.log(" " + import_chalk2.default.dim("\u2192 ") + import_chalk2.default.underline("https://node9.ai"));
|
|
9367
9610
|
}
|
|
9368
9611
|
console.log("");
|
|
9369
|
-
if (
|
|
9612
|
+
if (!isTestingMode() && isDaemonRunning()) {
|
|
9370
9613
|
const internalToken = getInternalToken();
|
|
9371
9614
|
if (internalToken) {
|
|
9372
9615
|
try {
|
|
9373
|
-
const
|
|
9374
|
-
|
|
9375
|
-
|
|
9376
|
-
|
|
9377
|
-
|
|
9378
|
-
|
|
9379
|
-
verdict: f.source?.rule?.verdict ?? f.source?.rule?.action ?? "review",
|
|
9380
|
-
ruleSource: f.source?.sourceType ?? "default",
|
|
9381
|
-
source: src
|
|
9382
|
-
}))
|
|
9383
|
-
);
|
|
9384
|
-
const mapLeaks = (arr, src) => (
|
|
9385
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9386
|
-
arr.map((f) => ({
|
|
9387
|
-
timestamp: f.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
9388
|
-
pattern: f.patternName || "DLP",
|
|
9389
|
-
sample: f.redactedSample || "********",
|
|
9390
|
-
source: src
|
|
9391
|
-
}))
|
|
9392
|
-
);
|
|
9393
|
-
const mapLoops = (arr, src) => (
|
|
9394
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9395
|
-
arr.map((f) => ({
|
|
9396
|
-
timestamp: f.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
9397
|
-
toolName: f.toolName || "unknown",
|
|
9398
|
-
commandPreview: f.commandPreview || "",
|
|
9399
|
-
count: f.count || 0,
|
|
9400
|
-
source: src
|
|
9401
|
-
}))
|
|
9402
|
-
);
|
|
9403
|
-
const sources = [
|
|
9404
|
-
{
|
|
9405
|
-
id: "claude",
|
|
9406
|
-
label: "Claude",
|
|
9407
|
-
icon: "\u{1F916}",
|
|
9408
|
-
sessions: claudeScan.sessions,
|
|
9409
|
-
findings: mapFindings(claudeScan.findings, "claude"),
|
|
9410
|
-
leaks: mapLeaks(claudeScan.dlpFindings, "claude"),
|
|
9411
|
-
loops: mapLoops(claudeScan.loopFindings, "claude")
|
|
9412
|
-
},
|
|
9413
|
-
{
|
|
9414
|
-
id: "gemini",
|
|
9415
|
-
label: "Gemini",
|
|
9416
|
-
icon: "\u264A",
|
|
9417
|
-
sessions: geminiScan.sessions,
|
|
9418
|
-
findings: mapFindings(geminiScan.findings, "gemini"),
|
|
9419
|
-
leaks: mapLeaks(geminiScan.dlpFindings, "gemini"),
|
|
9420
|
-
loops: mapLoops(geminiScan.loopFindings, "gemini")
|
|
9421
|
-
},
|
|
9422
|
-
{
|
|
9423
|
-
id: "codex",
|
|
9424
|
-
label: "Codex",
|
|
9425
|
-
icon: "\u{1F52E}",
|
|
9426
|
-
sessions: codexScan.sessions,
|
|
9427
|
-
findings: mapFindings(codexScan.findings, "codex"),
|
|
9428
|
-
leaks: mapLeaks(codexScan.dlpFindings, "codex"),
|
|
9429
|
-
loops: mapLoops(codexScan.loopFindings, "codex")
|
|
9430
|
-
}
|
|
9431
|
-
].filter(
|
|
9432
|
-
(s) => s.sessions > 0 || s.findings.length > 0 || s.leaks.length > 0 || s.loops.length > 0
|
|
9433
|
-
);
|
|
9434
|
-
const payload = {
|
|
9435
|
-
status: "complete",
|
|
9436
|
-
summary: {
|
|
9437
|
-
sessions: scan.sessions,
|
|
9438
|
-
findings: scan.findings.length,
|
|
9439
|
-
dlp: scan.dlpFindings.length,
|
|
9440
|
-
loops: scan.loopFindings.length,
|
|
9441
|
-
totalCostUSD: scan.totalCostUSD
|
|
9442
|
-
},
|
|
9443
|
-
sources
|
|
9444
|
-
};
|
|
9616
|
+
const summary2 = buildScanSummary([
|
|
9617
|
+
{ id: "claude", label: "Claude", icon: "\u{1F916}", scan: claudeScan },
|
|
9618
|
+
{ id: "gemini", label: "Gemini", icon: "\u264A", scan: geminiScan },
|
|
9619
|
+
{ id: "codex", label: "Codex", icon: "\u{1F52E}", scan: codexScan }
|
|
9620
|
+
]);
|
|
9621
|
+
const payload = { status: "complete", summary: summary2 };
|
|
9445
9622
|
await fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/scan/push`, {
|
|
9446
9623
|
method: "POST",
|
|
9447
9624
|
headers: { "Content-Type": "application/json", "x-node9-internal": internalToken },
|
|
@@ -9458,13 +9635,13 @@ function registerScanCommand(program2) {
|
|
|
9458
9635
|
}
|
|
9459
9636
|
});
|
|
9460
9637
|
}
|
|
9461
|
-
var import_chalk2, import_fs13,
|
|
9638
|
+
var import_chalk2, import_fs13, import_path17, import_os12, CLAUDE_PRICING, GEMINI_PRICING, LOOP_TOOLS, LOOP_THRESHOLD, DEFAULT_RULE_NAMES;
|
|
9462
9639
|
var init_scan = __esm({
|
|
9463
9640
|
"src/cli/commands/scan.ts"() {
|
|
9464
9641
|
"use strict";
|
|
9465
9642
|
import_chalk2 = __toESM(require("chalk"));
|
|
9466
9643
|
import_fs13 = __toESM(require("fs"));
|
|
9467
|
-
|
|
9644
|
+
import_path17 = __toESM(require("path"));
|
|
9468
9645
|
import_os12 = __toESM(require("os"));
|
|
9469
9646
|
init_shields();
|
|
9470
9647
|
init_config();
|
|
@@ -9472,6 +9649,7 @@ var init_scan = __esm({
|
|
|
9472
9649
|
init_dlp();
|
|
9473
9650
|
init_daemon();
|
|
9474
9651
|
init_daemon_starter();
|
|
9652
|
+
init_scan_summary();
|
|
9475
9653
|
CLAUDE_PRICING = {
|
|
9476
9654
|
"claude-opus-4-6": { i: 5e-6, o: 25e-6, cw: 625e-8, cr: 5e-7 },
|
|
9477
9655
|
"claude-opus-4-5": { i: 5e-6, o: 25e-6, cw: 625e-8, cr: 5e-7 },
|
|
@@ -9600,12 +9778,12 @@ var init_suggestion_tracker = __esm({
|
|
|
9600
9778
|
});
|
|
9601
9779
|
|
|
9602
9780
|
// src/daemon/taint-store.ts
|
|
9603
|
-
var import_fs14,
|
|
9781
|
+
var import_fs14, import_path18, DEFAULT_TTL_MS, TaintStore;
|
|
9604
9782
|
var init_taint_store = __esm({
|
|
9605
9783
|
"src/daemon/taint-store.ts"() {
|
|
9606
9784
|
"use strict";
|
|
9607
9785
|
import_fs14 = __toESM(require("fs"));
|
|
9608
|
-
|
|
9786
|
+
import_path18 = __toESM(require("path"));
|
|
9609
9787
|
DEFAULT_TTL_MS = 60 * 60 * 1e3;
|
|
9610
9788
|
TaintStore = class {
|
|
9611
9789
|
records = /* @__PURE__ */ new Map();
|
|
@@ -9670,9 +9848,9 @@ var init_taint_store = __esm({
|
|
|
9670
9848
|
/** Resolve to absolute path, falling back to path.resolve if file doesn't exist yet. */
|
|
9671
9849
|
_resolve(filePath) {
|
|
9672
9850
|
try {
|
|
9673
|
-
return import_fs14.default.realpathSync.native(
|
|
9851
|
+
return import_fs14.default.realpathSync.native(import_path18.default.resolve(filePath));
|
|
9674
9852
|
} catch {
|
|
9675
|
-
return
|
|
9853
|
+
return import_path18.default.resolve(filePath);
|
|
9676
9854
|
}
|
|
9677
9855
|
}
|
|
9678
9856
|
};
|
|
@@ -9834,7 +10012,7 @@ function setCachedScanResult(result) {
|
|
|
9834
10012
|
cachedScanTs = Date.now();
|
|
9835
10013
|
}
|
|
9836
10014
|
function atomicWriteSync2(filePath, data, options) {
|
|
9837
|
-
const dir =
|
|
10015
|
+
const dir = import_path19.default.dirname(filePath);
|
|
9838
10016
|
if (!import_fs15.default.existsSync(dir)) import_fs15.default.mkdirSync(dir, { recursive: true });
|
|
9839
10017
|
const tmpPath = `${filePath}.${(0, import_crypto6.randomUUID)()}.tmp`;
|
|
9840
10018
|
try {
|
|
@@ -9874,7 +10052,7 @@ function appendAuditLog(data) {
|
|
|
9874
10052
|
decision: data.decision,
|
|
9875
10053
|
source: "daemon"
|
|
9876
10054
|
};
|
|
9877
|
-
const dir =
|
|
10055
|
+
const dir = import_path19.default.dirname(AUDIT_LOG_FILE);
|
|
9878
10056
|
if (!import_fs15.default.existsSync(dir)) import_fs15.default.mkdirSync(dir, { recursive: true });
|
|
9879
10057
|
import_fs15.default.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
|
|
9880
10058
|
} catch {
|
|
@@ -10128,13 +10306,13 @@ function startActivitySocket() {
|
|
|
10128
10306
|
}
|
|
10129
10307
|
});
|
|
10130
10308
|
}
|
|
10131
|
-
var import_net2, import_fs15,
|
|
10309
|
+
var import_net2, import_fs15, import_path19, import_os13, import_child_process4, import_crypto6, homeDir, DAEMON_PID_FILE, DECISIONS_FILE, AUDIT_LOG_FILE, TRUST_FILE2, GLOBAL_CONFIG_FILE, CREDENTIALS_FILE, INSIGHT_COUNTS_FILE, pending, sseClients, suggestionTracker, suggestions, taintStore, insightCounts, _abandonTimer, _hadBrowserClient, _daemonServer, daemonRejectionHandlerRegistered, AUTO_DENY_MS, TRUST_DURATIONS, autoStarted, ACTIVITY_SOCKET_PATH2, ACTIVITY_RING_SIZE, activityRing, LARGE_RESPONSE_RING_SIZE, largeResponseRing, cachedScanResult, cachedScanTs, SCAN_CACHE_TTL_MS, SECRET_KEY_RE, INPUT_PRICE_PER_1M, OUTPUT_PRICE_PER_1M, BYTES_PER_TOKEN, WRITE_TOOL_NAMES;
|
|
10132
10310
|
var init_state2 = __esm({
|
|
10133
10311
|
"src/daemon/state.ts"() {
|
|
10134
10312
|
"use strict";
|
|
10135
10313
|
import_net2 = __toESM(require("net"));
|
|
10136
10314
|
import_fs15 = __toESM(require("fs"));
|
|
10137
|
-
|
|
10315
|
+
import_path19 = __toESM(require("path"));
|
|
10138
10316
|
import_os13 = __toESM(require("os"));
|
|
10139
10317
|
import_child_process4 = require("child_process");
|
|
10140
10318
|
import_crypto6 = require("crypto");
|
|
@@ -10144,13 +10322,13 @@ var init_state2 = __esm({
|
|
|
10144
10322
|
init_session_counters();
|
|
10145
10323
|
init_session_history();
|
|
10146
10324
|
homeDir = import_os13.default.homedir();
|
|
10147
|
-
DAEMON_PID_FILE =
|
|
10148
|
-
DECISIONS_FILE =
|
|
10149
|
-
AUDIT_LOG_FILE =
|
|
10150
|
-
TRUST_FILE2 =
|
|
10151
|
-
GLOBAL_CONFIG_FILE =
|
|
10152
|
-
CREDENTIALS_FILE =
|
|
10153
|
-
INSIGHT_COUNTS_FILE =
|
|
10325
|
+
DAEMON_PID_FILE = import_path19.default.join(homeDir, ".node9", "daemon.pid");
|
|
10326
|
+
DECISIONS_FILE = import_path19.default.join(homeDir, ".node9", "decisions.json");
|
|
10327
|
+
AUDIT_LOG_FILE = import_path19.default.join(homeDir, ".node9", "audit.log");
|
|
10328
|
+
TRUST_FILE2 = import_path19.default.join(homeDir, ".node9", "trust.json");
|
|
10329
|
+
GLOBAL_CONFIG_FILE = import_path19.default.join(homeDir, ".node9", "config.json");
|
|
10330
|
+
CREDENTIALS_FILE = import_path19.default.join(homeDir, ".node9", "credentials.json");
|
|
10331
|
+
INSIGHT_COUNTS_FILE = import_path19.default.join(homeDir, ".node9", "insight-counts.json");
|
|
10154
10332
|
pending = /* @__PURE__ */ new Map();
|
|
10155
10333
|
sseClients = /* @__PURE__ */ new Set();
|
|
10156
10334
|
suggestionTracker = new SuggestionTracker(3);
|
|
@@ -10168,7 +10346,7 @@ var init_state2 = __esm({
|
|
|
10168
10346
|
"2h": 2 * 60 * 6e4
|
|
10169
10347
|
};
|
|
10170
10348
|
autoStarted = process.env.NODE9_AUTO_STARTED === "1";
|
|
10171
|
-
ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" :
|
|
10349
|
+
ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path19.default.join(import_os13.default.tmpdir(), "node9-activity.sock");
|
|
10172
10350
|
ACTIVITY_RING_SIZE = 100;
|
|
10173
10351
|
activityRing = [];
|
|
10174
10352
|
LARGE_RESPONSE_RING_SIZE = 20;
|
|
@@ -10218,7 +10396,7 @@ function patchConfig(configPath, patch) {
|
|
|
10218
10396
|
ignored.push(patch.toolName);
|
|
10219
10397
|
}
|
|
10220
10398
|
}
|
|
10221
|
-
const dir =
|
|
10399
|
+
const dir = import_path20.default.dirname(configPath);
|
|
10222
10400
|
import_fs16.default.mkdirSync(dir, { recursive: true });
|
|
10223
10401
|
const tmp = configPath + ".node9-tmp";
|
|
10224
10402
|
try {
|
|
@@ -10240,14 +10418,14 @@ function patchConfig(configPath, patch) {
|
|
|
10240
10418
|
throw err2;
|
|
10241
10419
|
}
|
|
10242
10420
|
}
|
|
10243
|
-
var import_fs16,
|
|
10421
|
+
var import_fs16, import_path20, import_os14, GLOBAL_CONFIG_PATH;
|
|
10244
10422
|
var init_patch = __esm({
|
|
10245
10423
|
"src/config/patch.ts"() {
|
|
10246
10424
|
"use strict";
|
|
10247
10425
|
import_fs16 = __toESM(require("fs"));
|
|
10248
|
-
|
|
10426
|
+
import_path20 = __toESM(require("path"));
|
|
10249
10427
|
import_os14 = __toESM(require("os"));
|
|
10250
|
-
GLOBAL_CONFIG_PATH =
|
|
10428
|
+
GLOBAL_CONFIG_PATH = import_path20.default.join(import_os14.default.homedir(), ".node9", "config.json");
|
|
10251
10429
|
}
|
|
10252
10430
|
});
|
|
10253
10431
|
|
|
@@ -10319,7 +10497,7 @@ function parseJSONLFile(filePath) {
|
|
|
10319
10497
|
return daily;
|
|
10320
10498
|
}
|
|
10321
10499
|
function collectEntries() {
|
|
10322
|
-
const projectsDir =
|
|
10500
|
+
const projectsDir = import_path21.default.join(import_os15.default.homedir(), ".claude", "projects");
|
|
10323
10501
|
if (!import_fs17.default.existsSync(projectsDir)) return [];
|
|
10324
10502
|
const combined = /* @__PURE__ */ new Map();
|
|
10325
10503
|
let dirs;
|
|
@@ -10329,7 +10507,7 @@ function collectEntries() {
|
|
|
10329
10507
|
return [];
|
|
10330
10508
|
}
|
|
10331
10509
|
for (const dir of dirs) {
|
|
10332
|
-
const dirPath =
|
|
10510
|
+
const dirPath = import_path21.default.join(projectsDir, dir);
|
|
10333
10511
|
try {
|
|
10334
10512
|
if (!import_fs17.default.statSync(dirPath).isDirectory()) continue;
|
|
10335
10513
|
} catch {
|
|
@@ -10342,7 +10520,7 @@ function collectEntries() {
|
|
|
10342
10520
|
continue;
|
|
10343
10521
|
}
|
|
10344
10522
|
for (const file of files) {
|
|
10345
|
-
const entries = parseJSONLFile(
|
|
10523
|
+
const entries = parseJSONLFile(import_path21.default.join(dirPath, file));
|
|
10346
10524
|
for (const [key, e] of entries) {
|
|
10347
10525
|
const prev = combined.get(key);
|
|
10348
10526
|
if (prev) {
|
|
@@ -10395,12 +10573,12 @@ function startCostSync() {
|
|
|
10395
10573
|
}, SYNC_INTERVAL_MS);
|
|
10396
10574
|
timer.unref();
|
|
10397
10575
|
}
|
|
10398
|
-
var import_fs17,
|
|
10576
|
+
var import_fs17, import_path21, import_os15, SYNC_INTERVAL_MS, PRICING;
|
|
10399
10577
|
var init_costSync = __esm({
|
|
10400
10578
|
"src/costSync.ts"() {
|
|
10401
10579
|
"use strict";
|
|
10402
10580
|
import_fs17 = __toESM(require("fs"));
|
|
10403
|
-
|
|
10581
|
+
import_path21 = __toESM(require("path"));
|
|
10404
10582
|
import_os15 = __toESM(require("os"));
|
|
10405
10583
|
init_config();
|
|
10406
10584
|
init_audit();
|
|
@@ -10426,7 +10604,7 @@ function readCredentials() {
|
|
|
10426
10604
|
};
|
|
10427
10605
|
}
|
|
10428
10606
|
try {
|
|
10429
|
-
const credPath =
|
|
10607
|
+
const credPath = import_path22.default.join(import_os16.default.homedir(), ".node9", "credentials.json");
|
|
10430
10608
|
const creds = JSON.parse(import_fs18.default.readFileSync(credPath, "utf-8"));
|
|
10431
10609
|
const profileName = process.env.NODE9_PROFILE ?? "default";
|
|
10432
10610
|
const profile = creds[profileName];
|
|
@@ -10489,7 +10667,7 @@ async function syncOnce() {
|
|
|
10489
10667
|
try {
|
|
10490
10668
|
const rules = await fetchCloudRules(creds.apiKey, creds.apiUrl);
|
|
10491
10669
|
const cache = { fetchedAt: (/* @__PURE__ */ new Date()).toISOString(), rules };
|
|
10492
|
-
const dir =
|
|
10670
|
+
const dir = import_path22.default.dirname(rulesCacheFile());
|
|
10493
10671
|
if (!import_fs18.default.existsSync(dir)) import_fs18.default.mkdirSync(dir, { recursive: true });
|
|
10494
10672
|
import_fs18.default.writeFileSync(rulesCacheFile(), JSON.stringify(cache, null, 2) + "\n", "utf-8");
|
|
10495
10673
|
} catch {
|
|
@@ -10503,7 +10681,7 @@ async function runCloudSync() {
|
|
|
10503
10681
|
try {
|
|
10504
10682
|
const rules = await fetchCloudRules(creds.apiKey, creds.apiUrl);
|
|
10505
10683
|
const cache = { fetchedAt: (/* @__PURE__ */ new Date()).toISOString(), rules };
|
|
10506
|
-
const dir =
|
|
10684
|
+
const dir = import_path22.default.dirname(rulesCacheFile());
|
|
10507
10685
|
if (!import_fs18.default.existsSync(dir)) import_fs18.default.mkdirSync(dir, { recursive: true });
|
|
10508
10686
|
import_fs18.default.writeFileSync(rulesCacheFile(), JSON.stringify(cache, null, 2) + "\n", "utf-8");
|
|
10509
10687
|
return { ok: true, rules: rules.length, fetchedAt: cache.fetchedAt };
|
|
@@ -10537,16 +10715,16 @@ function startCloudSync() {
|
|
|
10537
10715
|
const recurring = setInterval(() => void syncOnce(), intervalMs);
|
|
10538
10716
|
recurring.unref();
|
|
10539
10717
|
}
|
|
10540
|
-
var import_fs18, import_https, import_os16,
|
|
10718
|
+
var import_fs18, import_https, import_os16, import_path22, rulesCacheFile, DEFAULT_API_URL, DEFAULT_INTERVAL_HOURS, MIN_INTERVAL_HOURS;
|
|
10541
10719
|
var init_sync = __esm({
|
|
10542
10720
|
"src/daemon/sync.ts"() {
|
|
10543
10721
|
"use strict";
|
|
10544
10722
|
import_fs18 = __toESM(require("fs"));
|
|
10545
10723
|
import_https = __toESM(require("https"));
|
|
10546
10724
|
import_os16 = __toESM(require("os"));
|
|
10547
|
-
|
|
10725
|
+
import_path22 = __toESM(require("path"));
|
|
10548
10726
|
init_config();
|
|
10549
|
-
rulesCacheFile = () =>
|
|
10727
|
+
rulesCacheFile = () => import_path22.default.join(import_os16.default.homedir(), ".node9", "rules-cache.json");
|
|
10550
10728
|
DEFAULT_API_URL = "https://api.node9.ai/api/v1/policy";
|
|
10551
10729
|
DEFAULT_INTERVAL_HOURS = 5;
|
|
10552
10730
|
MIN_INTERVAL_HOURS = 1;
|
|
@@ -10584,11 +10762,11 @@ function runDlpScan() {
|
|
|
10584
10762
|
return;
|
|
10585
10763
|
}
|
|
10586
10764
|
for (const proj of projDirs) {
|
|
10587
|
-
const projPath =
|
|
10765
|
+
const projPath = import_path23.default.join(PROJECTS_DIR, proj);
|
|
10588
10766
|
try {
|
|
10589
10767
|
if (!import_fs19.default.lstatSync(projPath).isDirectory()) continue;
|
|
10590
10768
|
const real = import_fs19.default.realpathSync(projPath);
|
|
10591
|
-
if (!real.startsWith(PROJECTS_DIR +
|
|
10769
|
+
if (!real.startsWith(PROJECTS_DIR + import_path23.default.sep) && real !== PROJECTS_DIR) continue;
|
|
10592
10770
|
} catch {
|
|
10593
10771
|
continue;
|
|
10594
10772
|
}
|
|
@@ -10599,7 +10777,7 @@ function runDlpScan() {
|
|
|
10599
10777
|
continue;
|
|
10600
10778
|
}
|
|
10601
10779
|
for (const file of files) {
|
|
10602
|
-
const filePath =
|
|
10780
|
+
const filePath = import_path23.default.join(projPath, file);
|
|
10603
10781
|
const lastOffset = index[filePath] ?? 0;
|
|
10604
10782
|
let size;
|
|
10605
10783
|
try {
|
|
@@ -10688,24 +10866,24 @@ function startDlpScanner() {
|
|
|
10688
10866
|
);
|
|
10689
10867
|
timer.unref();
|
|
10690
10868
|
}
|
|
10691
|
-
var import_fs19,
|
|
10869
|
+
var import_fs19, import_path23, import_os17, INDEX_FILE, PROJECTS_DIR;
|
|
10692
10870
|
var init_dlp_scanner = __esm({
|
|
10693
10871
|
"src/daemon/dlp-scanner.ts"() {
|
|
10694
10872
|
"use strict";
|
|
10695
10873
|
import_fs19 = __toESM(require("fs"));
|
|
10696
|
-
|
|
10874
|
+
import_path23 = __toESM(require("path"));
|
|
10697
10875
|
import_os17 = __toESM(require("os"));
|
|
10698
10876
|
init_dlp();
|
|
10699
10877
|
init_native();
|
|
10700
10878
|
init_state2();
|
|
10701
|
-
INDEX_FILE =
|
|
10702
|
-
PROJECTS_DIR =
|
|
10879
|
+
INDEX_FILE = import_path23.default.join(import_os17.default.homedir(), ".node9", "dlp-index.json");
|
|
10880
|
+
PROJECTS_DIR = import_path23.default.join(import_os17.default.homedir(), ".claude", "projects");
|
|
10703
10881
|
}
|
|
10704
10882
|
});
|
|
10705
10883
|
|
|
10706
10884
|
// src/daemon/mcp-tools.ts
|
|
10707
10885
|
function getMcpToolsFile() {
|
|
10708
|
-
return
|
|
10886
|
+
return import_path24.default.join(import_os18.default.homedir(), ".node9", "mcp-tools.json");
|
|
10709
10887
|
}
|
|
10710
10888
|
function readMcpToolsConfig() {
|
|
10711
10889
|
try {
|
|
@@ -10720,7 +10898,7 @@ function readMcpToolsConfig() {
|
|
|
10720
10898
|
function writeMcpToolsConfig(config) {
|
|
10721
10899
|
try {
|
|
10722
10900
|
const file = getMcpToolsFile();
|
|
10723
|
-
const dir =
|
|
10901
|
+
const dir = import_path24.default.dirname(file);
|
|
10724
10902
|
if (!import_fs20.default.existsSync(dir)) import_fs20.default.mkdirSync(dir, { recursive: true });
|
|
10725
10903
|
const tmpPath = `${file}.${import_os18.default.hostname()}.${process.pid}.tmp`;
|
|
10726
10904
|
import_fs20.default.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
|
|
@@ -10763,12 +10941,12 @@ function approveServer(serverKey, disabledTools) {
|
|
|
10763
10941
|
writeMcpToolsConfig(config);
|
|
10764
10942
|
}
|
|
10765
10943
|
}
|
|
10766
|
-
var import_fs20,
|
|
10944
|
+
var import_fs20, import_path24, import_os18;
|
|
10767
10945
|
var init_mcp_tools = __esm({
|
|
10768
10946
|
"src/daemon/mcp-tools.ts"() {
|
|
10769
10947
|
"use strict";
|
|
10770
10948
|
import_fs20 = __toESM(require("fs"));
|
|
10771
|
-
|
|
10949
|
+
import_path24 = __toESM(require("path"));
|
|
10772
10950
|
import_os18 = __toESM(require("os"));
|
|
10773
10951
|
}
|
|
10774
10952
|
});
|
|
@@ -10964,7 +11142,7 @@ data: ${JSON.stringify(item.data)}
|
|
|
10964
11142
|
status: "pending"
|
|
10965
11143
|
});
|
|
10966
11144
|
}
|
|
10967
|
-
const projectCwd = typeof cwd === "string" &&
|
|
11145
|
+
const projectCwd = typeof cwd === "string" && import_path25.default.isAbsolute(cwd) ? cwd : void 0;
|
|
10968
11146
|
const projectConfig = getConfig(projectCwd);
|
|
10969
11147
|
const browserEnabled = projectConfig.settings.approvers?.browser !== false;
|
|
10970
11148
|
const terminalEnabled = projectConfig.settings.approvers?.terminal !== false;
|
|
@@ -11328,7 +11506,7 @@ data: ${JSON.stringify(item.data)}
|
|
|
11328
11506
|
if (!validToken(req)) return res.writeHead(403).end();
|
|
11329
11507
|
const periodParam = reqUrl.searchParams.get("period") || "7d";
|
|
11330
11508
|
const period = ["today", "7d", "30d", "month"].includes(periodParam) ? periodParam : "7d";
|
|
11331
|
-
const logPath =
|
|
11509
|
+
const logPath = import_path25.default.join(import_os19.default.homedir(), ".node9", "audit.log");
|
|
11332
11510
|
if (!import_fs21.default.existsSync(logPath)) {
|
|
11333
11511
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
11334
11512
|
return res.end(
|
|
@@ -11448,9 +11626,21 @@ data: ${JSON.stringify(item.data)}
|
|
|
11448
11626
|
const d = /* @__PURE__ */ new Date();
|
|
11449
11627
|
d.setDate(d.getDate() - 90);
|
|
11450
11628
|
d.setHours(0, 0, 0, 0);
|
|
11451
|
-
|
|
11452
|
-
|
|
11453
|
-
|
|
11629
|
+
const EMPTY_SCAN = {
|
|
11630
|
+
filesScanned: 0,
|
|
11631
|
+
sessions: 0,
|
|
11632
|
+
totalToolCalls: 0,
|
|
11633
|
+
bashCalls: 0,
|
|
11634
|
+
findings: [],
|
|
11635
|
+
dlpFindings: [],
|
|
11636
|
+
loopFindings: [],
|
|
11637
|
+
totalCostUSD: 0,
|
|
11638
|
+
firstDate: null,
|
|
11639
|
+
lastDate: null
|
|
11640
|
+
};
|
|
11641
|
+
let claude = EMPTY_SCAN;
|
|
11642
|
+
let gemini = EMPTY_SCAN;
|
|
11643
|
+
let codex = EMPTY_SCAN;
|
|
11454
11644
|
try {
|
|
11455
11645
|
claude = scanClaudeHistory(d);
|
|
11456
11646
|
} catch (e) {
|
|
@@ -11466,86 +11656,13 @@ data: ${JSON.stringify(item.data)}
|
|
|
11466
11656
|
} catch (e) {
|
|
11467
11657
|
console.error("Codex scan failed:", e);
|
|
11468
11658
|
}
|
|
11469
|
-
const
|
|
11470
|
-
|
|
11471
|
-
|
|
11472
|
-
|
|
11473
|
-
|
|
11474
|
-
command: f.input?.command ?? f.input?.cmd ?? f.input?.file_path ?? f.toolName ?? "unknown",
|
|
11475
|
-
verdict: f.source?.rule?.verdict ?? f.source?.rule?.action ?? "review",
|
|
11476
|
-
ruleSource: f.source?.sourceType ?? "default",
|
|
11477
|
-
source: src
|
|
11478
|
-
}))
|
|
11479
|
-
);
|
|
11480
|
-
const mapLeaks = (arr, src) => (
|
|
11481
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11482
|
-
arr.map((f) => ({
|
|
11483
|
-
timestamp: f.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
11484
|
-
pattern: f.patternName || "DLP",
|
|
11485
|
-
sample: f.redactedSample || "********",
|
|
11486
|
-
source: src
|
|
11487
|
-
}))
|
|
11488
|
-
);
|
|
11489
|
-
const mapLoops = (arr, src) => (
|
|
11490
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11491
|
-
arr.map((f) => ({
|
|
11492
|
-
timestamp: f.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
11493
|
-
toolName: f.toolName || "unknown",
|
|
11494
|
-
commandPreview: f.commandPreview || "",
|
|
11495
|
-
count: f.count || 0,
|
|
11496
|
-
source: src
|
|
11497
|
-
}))
|
|
11498
|
-
);
|
|
11499
|
-
const sources = [
|
|
11500
|
-
{
|
|
11501
|
-
id: "claude",
|
|
11502
|
-
label: "Claude",
|
|
11503
|
-
icon: "\u{1F916}",
|
|
11504
|
-
sessions: claude.sessions,
|
|
11505
|
-
findings: mapFindings(claude.findings, "claude"),
|
|
11506
|
-
leaks: mapLeaks(claude.dlpFindings, "claude"),
|
|
11507
|
-
loops: mapLoops(claude.loopFindings, "claude")
|
|
11508
|
-
},
|
|
11509
|
-
{
|
|
11510
|
-
id: "gemini",
|
|
11511
|
-
label: "Gemini",
|
|
11512
|
-
icon: "\u264A",
|
|
11513
|
-
sessions: gemini.sessions,
|
|
11514
|
-
findings: mapFindings(gemini.findings, "gemini"),
|
|
11515
|
-
leaks: mapLeaks(gemini.dlpFindings, "gemini"),
|
|
11516
|
-
loops: mapLoops(gemini.loopFindings, "gemini")
|
|
11517
|
-
},
|
|
11518
|
-
{
|
|
11519
|
-
id: "codex",
|
|
11520
|
-
label: "Codex",
|
|
11521
|
-
icon: "\u{1F52E}",
|
|
11522
|
-
sessions: codex.sessions,
|
|
11523
|
-
findings: mapFindings(codex.findings, "codex"),
|
|
11524
|
-
leaks: mapLeaks(codex.dlpFindings, "codex"),
|
|
11525
|
-
loops: mapLoops(codex.loopFindings, "codex")
|
|
11526
|
-
}
|
|
11527
|
-
].filter(
|
|
11528
|
-
(s) => s.sessions > 0 || s.findings.length > 0 || s.leaks.length > 0 || s.loops.length > 0
|
|
11529
|
-
);
|
|
11530
|
-
const totalSessions = claude.sessions + gemini.sessions + codex.sessions;
|
|
11531
|
-
const totalFindings = claude.findings.length + gemini.findings.length + codex.findings.length;
|
|
11532
|
-
const totalDlp = claude.dlpFindings.length + gemini.dlpFindings.length + codex.dlpFindings.length;
|
|
11533
|
-
const totalLoops = claude.loopFindings.length + gemini.loopFindings.length + codex.loopFindings.length;
|
|
11534
|
-
const totalCostUSD = (claude.totalCostUSD || 0) + (gemini.totalCostUSD || 0) + (codex.totalCostUSD || 0);
|
|
11659
|
+
const summary = buildScanSummary([
|
|
11660
|
+
{ id: "claude", label: "Claude", icon: "\u{1F916}", scan: claude },
|
|
11661
|
+
{ id: "gemini", label: "Gemini", icon: "\u264A", scan: gemini },
|
|
11662
|
+
{ id: "codex", label: "Codex", icon: "\u{1F52E}", scan: codex }
|
|
11663
|
+
]);
|
|
11535
11664
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
11536
|
-
return res.end(
|
|
11537
|
-
JSON.stringify({
|
|
11538
|
-
status: "complete",
|
|
11539
|
-
summary: {
|
|
11540
|
-
sessions: totalSessions,
|
|
11541
|
-
totalCostUSD,
|
|
11542
|
-
findings: totalFindings,
|
|
11543
|
-
dlp: totalDlp,
|
|
11544
|
-
loops: totalLoops
|
|
11545
|
-
},
|
|
11546
|
-
sources
|
|
11547
|
-
})
|
|
11548
|
-
);
|
|
11665
|
+
return res.end(JSON.stringify({ status: "complete", summary }));
|
|
11549
11666
|
} catch (err2) {
|
|
11550
11667
|
console.error("Scan failed:", err2);
|
|
11551
11668
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
@@ -11583,8 +11700,8 @@ data: ${JSON.stringify(item.data)}
|
|
|
11583
11700
|
const body = await readBody(req);
|
|
11584
11701
|
const data = body ? JSON.parse(body) : {};
|
|
11585
11702
|
const configPath = data.configPath ?? GLOBAL_CONFIG_PATH;
|
|
11586
|
-
const node9Dir =
|
|
11587
|
-
if (!
|
|
11703
|
+
const node9Dir = import_path25.default.dirname(GLOBAL_CONFIG_PATH);
|
|
11704
|
+
if (!import_path25.default.resolve(configPath).startsWith(node9Dir + import_path25.default.sep)) {
|
|
11588
11705
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
11589
11706
|
return res.end(
|
|
11590
11707
|
JSON.stringify({ error: "configPath must be within the node9 config directory" })
|
|
@@ -11703,6 +11820,11 @@ data: ${JSON.stringify(item.data)}
|
|
|
11703
11820
|
return;
|
|
11704
11821
|
}
|
|
11705
11822
|
approveServer(serverKey, disabledTools);
|
|
11823
|
+
appendAuditLog({
|
|
11824
|
+
toolName: `mcp-server:${serverKey}`,
|
|
11825
|
+
args: { disabledTools },
|
|
11826
|
+
decision: "allow"
|
|
11827
|
+
});
|
|
11706
11828
|
broadcast("mcp-tools-updated", { serverKey, disabledTools });
|
|
11707
11829
|
res.writeHead(200).end();
|
|
11708
11830
|
return;
|
|
@@ -11721,6 +11843,11 @@ data: ${JSON.stringify(item.data)}
|
|
|
11721
11843
|
}
|
|
11722
11844
|
const status = updateServerDiscovery(serverKey, tools);
|
|
11723
11845
|
if (status === "new" || status === "drift") {
|
|
11846
|
+
appendAuditLog({
|
|
11847
|
+
toolName: `mcp-server:${serverKey}`,
|
|
11848
|
+
args: { toolCount: tools.length, status },
|
|
11849
|
+
decision: "mcp-discovered"
|
|
11850
|
+
});
|
|
11724
11851
|
const id = (0, import_crypto7.randomUUID)();
|
|
11725
11852
|
const entry = {
|
|
11726
11853
|
id,
|
|
@@ -11789,6 +11916,11 @@ data: ${JSON.stringify(item.data)}
|
|
|
11789
11916
|
}
|
|
11790
11917
|
clearTimeout(entry.timer);
|
|
11791
11918
|
approveServer(serverKey, disabledTools);
|
|
11919
|
+
appendAuditLog({
|
|
11920
|
+
toolName: `mcp-server:${serverKey}`,
|
|
11921
|
+
args: { disabledTools },
|
|
11922
|
+
decision: "allow"
|
|
11923
|
+
});
|
|
11792
11924
|
pending.delete(id);
|
|
11793
11925
|
broadcast("remove", { id, decision: "allow" });
|
|
11794
11926
|
res.writeHead(200).end();
|
|
@@ -11877,13 +12009,13 @@ data: ${JSON.stringify(item.data)}
|
|
|
11877
12009
|
}
|
|
11878
12010
|
startActivitySocket();
|
|
11879
12011
|
}
|
|
11880
|
-
var import_http, import_fs21,
|
|
12012
|
+
var import_http, import_fs21, import_path25, import_os19, import_crypto7, import_child_process5, import_chalk3;
|
|
11881
12013
|
var init_server = __esm({
|
|
11882
12014
|
"src/daemon/server.ts"() {
|
|
11883
12015
|
"use strict";
|
|
11884
12016
|
import_http = __toESM(require("http"));
|
|
11885
12017
|
import_fs21 = __toESM(require("fs"));
|
|
11886
|
-
|
|
12018
|
+
import_path25 = __toESM(require("path"));
|
|
11887
12019
|
import_os19 = __toESM(require("os"));
|
|
11888
12020
|
import_crypto7 = require("crypto");
|
|
11889
12021
|
import_child_process5 = require("child_process");
|
|
@@ -11892,6 +12024,7 @@ var init_server = __esm({
|
|
|
11892
12024
|
init_shields();
|
|
11893
12025
|
init_ui2();
|
|
11894
12026
|
init_scan();
|
|
12027
|
+
init_scan_summary();
|
|
11895
12028
|
init_state2();
|
|
11896
12029
|
init_state();
|
|
11897
12030
|
init_patch();
|
|
@@ -11907,7 +12040,7 @@ var init_server = __esm({
|
|
|
11907
12040
|
function resolveNode9Binary() {
|
|
11908
12041
|
try {
|
|
11909
12042
|
const script = process.argv[1];
|
|
11910
|
-
if (typeof script === "string" &&
|
|
12043
|
+
if (typeof script === "string" && import_path26.default.isAbsolute(script) && import_fs22.default.existsSync(script)) {
|
|
11911
12044
|
return import_fs22.default.realpathSync(script);
|
|
11912
12045
|
}
|
|
11913
12046
|
} catch {
|
|
@@ -11926,11 +12059,11 @@ function xmlEscape(s) {
|
|
|
11926
12059
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
11927
12060
|
}
|
|
11928
12061
|
function launchdPlist(binaryPath) {
|
|
11929
|
-
const logDir =
|
|
12062
|
+
const logDir = import_path26.default.join(import_os20.default.homedir(), ".node9");
|
|
11930
12063
|
const nodePath = xmlEscape(process.execPath);
|
|
11931
12064
|
const scriptPath = xmlEscape(binaryPath);
|
|
11932
|
-
const outLog = xmlEscape(
|
|
11933
|
-
const errLog = xmlEscape(
|
|
12065
|
+
const outLog = xmlEscape(import_path26.default.join(logDir, "daemon.log"));
|
|
12066
|
+
const errLog = xmlEscape(import_path26.default.join(logDir, "daemon-error.log"));
|
|
11934
12067
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
11935
12068
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
11936
12069
|
<plist version="1.0">
|
|
@@ -11965,7 +12098,7 @@ function launchdPlist(binaryPath) {
|
|
|
11965
12098
|
`;
|
|
11966
12099
|
}
|
|
11967
12100
|
function installLaunchd(binaryPath) {
|
|
11968
|
-
const dir =
|
|
12101
|
+
const dir = import_path26.default.dirname(LAUNCHD_PLIST);
|
|
11969
12102
|
if (!import_fs22.default.existsSync(dir)) import_fs22.default.mkdirSync(dir, { recursive: true });
|
|
11970
12103
|
import_fs22.default.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
|
|
11971
12104
|
(0, import_child_process6.spawnSync)("launchctl", ["unload", LAUNCHD_PLIST], { encoding: "utf8" });
|
|
@@ -12042,7 +12175,7 @@ function isSystemdInstalled() {
|
|
|
12042
12175
|
return import_fs22.default.existsSync(SYSTEMD_UNIT);
|
|
12043
12176
|
}
|
|
12044
12177
|
function stopRunningDaemon() {
|
|
12045
|
-
const pidFile =
|
|
12178
|
+
const pidFile = import_path26.default.join(import_os20.default.homedir(), ".node9", "daemon.pid");
|
|
12046
12179
|
if (!import_fs22.default.existsSync(pidFile)) return;
|
|
12047
12180
|
try {
|
|
12048
12181
|
const data = JSON.parse(import_fs22.default.readFileSync(pidFile, "utf-8"));
|
|
@@ -12135,18 +12268,18 @@ function isDaemonServiceInstalled() {
|
|
|
12135
12268
|
if (process.platform === "linux") return isSystemdInstalled();
|
|
12136
12269
|
return false;
|
|
12137
12270
|
}
|
|
12138
|
-
var import_fs22,
|
|
12271
|
+
var import_fs22, import_path26, import_os20, import_child_process6, LAUNCHD_LABEL, LAUNCHD_PLIST, SYSTEMD_UNIT_DIR, SYSTEMD_UNIT;
|
|
12139
12272
|
var init_service = __esm({
|
|
12140
12273
|
"src/daemon/service.ts"() {
|
|
12141
12274
|
"use strict";
|
|
12142
12275
|
import_fs22 = __toESM(require("fs"));
|
|
12143
|
-
|
|
12276
|
+
import_path26 = __toESM(require("path"));
|
|
12144
12277
|
import_os20 = __toESM(require("os"));
|
|
12145
12278
|
import_child_process6 = require("child_process");
|
|
12146
12279
|
LAUNCHD_LABEL = "ai.node9.daemon";
|
|
12147
|
-
LAUNCHD_PLIST =
|
|
12148
|
-
SYSTEMD_UNIT_DIR =
|
|
12149
|
-
SYSTEMD_UNIT =
|
|
12280
|
+
LAUNCHD_PLIST = import_path26.default.join(import_os20.default.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
|
|
12281
|
+
SYSTEMD_UNIT_DIR = import_path26.default.join(import_os20.default.homedir(), ".config", "systemd", "user");
|
|
12282
|
+
SYSTEMD_UNIT = import_path26.default.join(SYSTEMD_UNIT_DIR, "node9-daemon.service");
|
|
12150
12283
|
}
|
|
12151
12284
|
});
|
|
12152
12285
|
|
|
@@ -12243,18 +12376,18 @@ function getModelContextLimit(model) {
|
|
|
12243
12376
|
return 2e5;
|
|
12244
12377
|
}
|
|
12245
12378
|
function readSessionUsage() {
|
|
12246
|
-
const projectsDir =
|
|
12379
|
+
const projectsDir = import_path42.default.join(import_os35.default.homedir(), ".claude", "projects");
|
|
12247
12380
|
if (!import_fs38.default.existsSync(projectsDir)) return null;
|
|
12248
12381
|
let latestFile = null;
|
|
12249
12382
|
let latestMtime = 0;
|
|
12250
12383
|
try {
|
|
12251
12384
|
for (const dir of import_fs38.default.readdirSync(projectsDir)) {
|
|
12252
|
-
const dirPath =
|
|
12385
|
+
const dirPath = import_path42.default.join(projectsDir, dir);
|
|
12253
12386
|
try {
|
|
12254
12387
|
if (!import_fs38.default.statSync(dirPath).isDirectory()) continue;
|
|
12255
12388
|
for (const file of import_fs38.default.readdirSync(dirPath)) {
|
|
12256
12389
|
if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
|
|
12257
|
-
const filePath =
|
|
12390
|
+
const filePath = import_path42.default.join(dirPath, file);
|
|
12258
12391
|
try {
|
|
12259
12392
|
const mtime = import_fs38.default.statSync(filePath).mtimeMs;
|
|
12260
12393
|
if (mtime > latestMtime) {
|
|
@@ -12515,7 +12648,7 @@ function buildRecoveryCardLines(req) {
|
|
|
12515
12648
|
];
|
|
12516
12649
|
}
|
|
12517
12650
|
function readApproversFromDisk() {
|
|
12518
|
-
const configPath =
|
|
12651
|
+
const configPath = import_path42.default.join(import_os35.default.homedir(), ".node9", "config.json");
|
|
12519
12652
|
try {
|
|
12520
12653
|
const raw = JSON.parse(import_fs38.default.readFileSync(configPath, "utf-8"));
|
|
12521
12654
|
const settings = raw.settings ?? {};
|
|
@@ -12533,7 +12666,7 @@ function approverStatusLine() {
|
|
|
12533
12666
|
return `${fmt("native", "native")} ${fmt("browser", "browser")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
|
|
12534
12667
|
}
|
|
12535
12668
|
function toggleApprover(channel) {
|
|
12536
|
-
const configPath =
|
|
12669
|
+
const configPath = import_path42.default.join(import_os35.default.homedir(), ".node9", "config.json");
|
|
12537
12670
|
try {
|
|
12538
12671
|
const raw = JSON.parse(import_fs38.default.readFileSync(configPath, "utf-8"));
|
|
12539
12672
|
const settings = raw.settings ?? {};
|
|
@@ -12712,7 +12845,7 @@ async function startTail(options = {}) {
|
|
|
12712
12845
|
postDecisionHttp(req2.id, httpDecision, csrfToken, port, httpOpts).catch((err2) => {
|
|
12713
12846
|
try {
|
|
12714
12847
|
import_fs38.default.appendFileSync(
|
|
12715
|
-
|
|
12848
|
+
import_path42.default.join(import_os35.default.homedir(), ".node9", "hook-debug.log"),
|
|
12716
12849
|
`[tail] POST /decision failed: ${String(err2)}
|
|
12717
12850
|
`
|
|
12718
12851
|
);
|
|
@@ -12793,7 +12926,7 @@ async function startTail(options = {}) {
|
|
|
12793
12926
|
}
|
|
12794
12927
|
} catch {
|
|
12795
12928
|
}
|
|
12796
|
-
const auditLog =
|
|
12929
|
+
const auditLog = import_path42.default.join(import_os35.default.homedir(), ".node9", "audit.log");
|
|
12797
12930
|
try {
|
|
12798
12931
|
const unackedDlp = import_fs38.default.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
|
|
12799
12932
|
if (unackedDlp > 0) {
|
|
@@ -12980,7 +13113,7 @@ async function startTail(options = {}) {
|
|
|
12980
13113
|
process.exit(1);
|
|
12981
13114
|
});
|
|
12982
13115
|
}
|
|
12983
|
-
var import_http2, import_chalk25, import_fs38, import_os35,
|
|
13116
|
+
var import_http2, import_chalk25, import_fs38, import_os35, import_path42, 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;
|
|
12984
13117
|
var init_tail = __esm({
|
|
12985
13118
|
"src/tui/tail.ts"() {
|
|
12986
13119
|
"use strict";
|
|
@@ -12988,13 +13121,13 @@ var init_tail = __esm({
|
|
|
12988
13121
|
import_chalk25 = __toESM(require("chalk"));
|
|
12989
13122
|
import_fs38 = __toESM(require("fs"));
|
|
12990
13123
|
import_os35 = __toESM(require("os"));
|
|
12991
|
-
|
|
13124
|
+
import_path42 = __toESM(require("path"));
|
|
12992
13125
|
import_readline5 = __toESM(require("readline"));
|
|
12993
13126
|
import_child_process16 = require("child_process");
|
|
12994
13127
|
init_daemon2();
|
|
12995
13128
|
init_daemon();
|
|
12996
13129
|
init_core();
|
|
12997
|
-
PID_FILE =
|
|
13130
|
+
PID_FILE = import_path42.default.join(import_os35.default.homedir(), ".node9", "daemon.pid");
|
|
12998
13131
|
ICONS = {
|
|
12999
13132
|
bash: "\u{1F4BB}",
|
|
13000
13133
|
shell: "\u{1F4BB}",
|
|
@@ -13144,7 +13277,7 @@ function countRulesInDir(rulesDir) {
|
|
|
13144
13277
|
try {
|
|
13145
13278
|
for (const entry of import_fs39.default.readdirSync(rulesDir, { withFileTypes: true })) {
|
|
13146
13279
|
if (entry.isDirectory()) {
|
|
13147
|
-
count += countRulesInDir(
|
|
13280
|
+
count += countRulesInDir(import_path43.default.join(rulesDir, entry.name));
|
|
13148
13281
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
13149
13282
|
count++;
|
|
13150
13283
|
}
|
|
@@ -13155,46 +13288,46 @@ function countRulesInDir(rulesDir) {
|
|
|
13155
13288
|
}
|
|
13156
13289
|
function isSamePath(a, b) {
|
|
13157
13290
|
try {
|
|
13158
|
-
return
|
|
13291
|
+
return import_path43.default.resolve(a) === import_path43.default.resolve(b);
|
|
13159
13292
|
} catch {
|
|
13160
13293
|
return false;
|
|
13161
13294
|
}
|
|
13162
13295
|
}
|
|
13163
13296
|
function countConfigs(cwd) {
|
|
13164
13297
|
const homeDir2 = import_os36.default.homedir();
|
|
13165
|
-
const claudeDir =
|
|
13298
|
+
const claudeDir = import_path43.default.join(homeDir2, ".claude");
|
|
13166
13299
|
let claudeMdCount = 0;
|
|
13167
13300
|
let rulesCount = 0;
|
|
13168
13301
|
let hooksCount = 0;
|
|
13169
13302
|
const userMcpServers = /* @__PURE__ */ new Set();
|
|
13170
13303
|
const projectMcpServers = /* @__PURE__ */ new Set();
|
|
13171
|
-
if (import_fs39.default.existsSync(
|
|
13172
|
-
rulesCount += countRulesInDir(
|
|
13173
|
-
const userSettings =
|
|
13304
|
+
if (import_fs39.default.existsSync(import_path43.default.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
13305
|
+
rulesCount += countRulesInDir(import_path43.default.join(claudeDir, "rules"));
|
|
13306
|
+
const userSettings = import_path43.default.join(claudeDir, "settings.json");
|
|
13174
13307
|
for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
|
|
13175
13308
|
hooksCount += countHooksInFile(userSettings);
|
|
13176
|
-
const userClaudeJson =
|
|
13309
|
+
const userClaudeJson = import_path43.default.join(homeDir2, ".claude.json");
|
|
13177
13310
|
for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
|
|
13178
13311
|
for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
|
|
13179
13312
|
userMcpServers.delete(name);
|
|
13180
13313
|
}
|
|
13181
13314
|
if (cwd) {
|
|
13182
|
-
if (import_fs39.default.existsSync(
|
|
13183
|
-
if (import_fs39.default.existsSync(
|
|
13184
|
-
const projectClaudeDir =
|
|
13315
|
+
if (import_fs39.default.existsSync(import_path43.default.join(cwd, "CLAUDE.md"))) claudeMdCount++;
|
|
13316
|
+
if (import_fs39.default.existsSync(import_path43.default.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
|
|
13317
|
+
const projectClaudeDir = import_path43.default.join(cwd, ".claude");
|
|
13185
13318
|
const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
|
|
13186
13319
|
if (!overlapsUserScope) {
|
|
13187
|
-
if (import_fs39.default.existsSync(
|
|
13188
|
-
rulesCount += countRulesInDir(
|
|
13189
|
-
const projSettings =
|
|
13320
|
+
if (import_fs39.default.existsSync(import_path43.default.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
|
|
13321
|
+
rulesCount += countRulesInDir(import_path43.default.join(projectClaudeDir, "rules"));
|
|
13322
|
+
const projSettings = import_path43.default.join(projectClaudeDir, "settings.json");
|
|
13190
13323
|
for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
|
|
13191
13324
|
hooksCount += countHooksInFile(projSettings);
|
|
13192
13325
|
}
|
|
13193
|
-
if (import_fs39.default.existsSync(
|
|
13194
|
-
const localSettings =
|
|
13326
|
+
if (import_fs39.default.existsSync(import_path43.default.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
|
|
13327
|
+
const localSettings = import_path43.default.join(projectClaudeDir, "settings.local.json");
|
|
13195
13328
|
for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
|
|
13196
13329
|
hooksCount += countHooksInFile(localSettings);
|
|
13197
|
-
const mcpJsonServers = getMcpServerNames(
|
|
13330
|
+
const mcpJsonServers = getMcpServerNames(import_path43.default.join(cwd, ".mcp.json"));
|
|
13198
13331
|
const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
|
|
13199
13332
|
for (const name of disabledMcpJson) mcpJsonServers.delete(name);
|
|
13200
13333
|
for (const name of mcpJsonServers) projectMcpServers.add(name);
|
|
@@ -13227,7 +13360,7 @@ function readActiveShieldsHud() {
|
|
|
13227
13360
|
return shieldsCache.value;
|
|
13228
13361
|
}
|
|
13229
13362
|
try {
|
|
13230
|
-
const shieldsPath =
|
|
13363
|
+
const shieldsPath = import_path43.default.join(import_os36.default.homedir(), ".node9", "shields.json");
|
|
13231
13364
|
if (!import_fs39.default.existsSync(shieldsPath)) {
|
|
13232
13365
|
shieldsCache = { value: [], ts: now };
|
|
13233
13366
|
return [];
|
|
@@ -13334,9 +13467,9 @@ function renderContextLine(stdin) {
|
|
|
13334
13467
|
async function main() {
|
|
13335
13468
|
try {
|
|
13336
13469
|
const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
|
|
13337
|
-
if (import_fs39.default.existsSync(
|
|
13470
|
+
if (import_fs39.default.existsSync(import_path43.default.join(import_os36.default.homedir(), ".node9", "hud-debug"))) {
|
|
13338
13471
|
try {
|
|
13339
|
-
const logPath =
|
|
13472
|
+
const logPath = import_path43.default.join(import_os36.default.homedir(), ".node9", "hud-debug.log");
|
|
13340
13473
|
const MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
13341
13474
|
let size = 0;
|
|
13342
13475
|
try {
|
|
@@ -13365,8 +13498,8 @@ async function main() {
|
|
|
13365
13498
|
try {
|
|
13366
13499
|
const cwd = stdin.cwd ?? process.cwd();
|
|
13367
13500
|
for (const configPath of [
|
|
13368
|
-
|
|
13369
|
-
|
|
13501
|
+
import_path43.default.join(cwd, "node9.config.json"),
|
|
13502
|
+
import_path43.default.join(import_os36.default.homedir(), ".node9", "config.json")
|
|
13370
13503
|
]) {
|
|
13371
13504
|
if (!import_fs39.default.existsSync(configPath)) continue;
|
|
13372
13505
|
const cfg = JSON.parse(import_fs39.default.readFileSync(configPath, "utf-8"));
|
|
@@ -13387,12 +13520,12 @@ async function main() {
|
|
|
13387
13520
|
renderOffline();
|
|
13388
13521
|
}
|
|
13389
13522
|
}
|
|
13390
|
-
var import_fs39,
|
|
13523
|
+
var import_fs39, import_path43, import_os36, import_http3, RESET3, BOLD3, DIM, RED2, GREEN2, YELLOW2, BLUE, MAGENTA, CYAN2, WHITE, BAR_FILLED, BAR_EMPTY, BAR_WIDTH, shieldsCache, SHIELDS_CACHE_TTL_MS;
|
|
13391
13524
|
var init_hud = __esm({
|
|
13392
13525
|
"src/cli/hud.ts"() {
|
|
13393
13526
|
"use strict";
|
|
13394
13527
|
import_fs39 = __toESM(require("fs"));
|
|
13395
|
-
|
|
13528
|
+
import_path43 = __toESM(require("path"));
|
|
13396
13529
|
import_os36 = __toESM(require("os"));
|
|
13397
13530
|
import_http3 = __toESM(require("http"));
|
|
13398
13531
|
init_daemon();
|
|
@@ -14442,7 +14575,7 @@ function getAgentsStatus(homeDir2 = import_os11.default.homedir()) {
|
|
|
14442
14575
|
init_daemon2();
|
|
14443
14576
|
var import_chalk26 = __toESM(require("chalk"));
|
|
14444
14577
|
var import_fs40 = __toESM(require("fs"));
|
|
14445
|
-
var
|
|
14578
|
+
var import_path44 = __toESM(require("path"));
|
|
14446
14579
|
var import_os37 = __toESM(require("os"));
|
|
14447
14580
|
var import_prompts2 = require("@inquirer/prompts");
|
|
14448
14581
|
|
|
@@ -14630,7 +14763,7 @@ init_daemon_starter();
|
|
|
14630
14763
|
var import_chalk6 = __toESM(require("chalk"));
|
|
14631
14764
|
var import_fs26 = __toESM(require("fs"));
|
|
14632
14765
|
var import_child_process10 = require("child_process");
|
|
14633
|
-
var
|
|
14766
|
+
var import_path29 = __toESM(require("path"));
|
|
14634
14767
|
var import_os23 = __toESM(require("os"));
|
|
14635
14768
|
init_orchestrator();
|
|
14636
14769
|
init_daemon();
|
|
@@ -14642,9 +14775,9 @@ var import_child_process9 = require("child_process");
|
|
|
14642
14775
|
var import_crypto8 = __toESM(require("crypto"));
|
|
14643
14776
|
var import_fs24 = __toESM(require("fs"));
|
|
14644
14777
|
var import_net3 = __toESM(require("net"));
|
|
14645
|
-
var
|
|
14778
|
+
var import_path27 = __toESM(require("path"));
|
|
14646
14779
|
var import_os21 = __toESM(require("os"));
|
|
14647
|
-
var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" :
|
|
14780
|
+
var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path27.default.join(import_os21.default.tmpdir(), "node9-activity.sock");
|
|
14648
14781
|
function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
|
|
14649
14782
|
try {
|
|
14650
14783
|
const payload = JSON.stringify({
|
|
@@ -14664,8 +14797,8 @@ function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
|
|
|
14664
14797
|
} catch {
|
|
14665
14798
|
}
|
|
14666
14799
|
}
|
|
14667
|
-
var SNAPSHOT_STACK_PATH =
|
|
14668
|
-
var UNDO_LATEST_PATH =
|
|
14800
|
+
var SNAPSHOT_STACK_PATH = import_path27.default.join(import_os21.default.homedir(), ".node9", "snapshots.json");
|
|
14801
|
+
var UNDO_LATEST_PATH = import_path27.default.join(import_os21.default.homedir(), ".node9", "undo_latest.txt");
|
|
14669
14802
|
var MAX_SNAPSHOTS = 10;
|
|
14670
14803
|
var GIT_TIMEOUT = 15e3;
|
|
14671
14804
|
function readStack() {
|
|
@@ -14677,7 +14810,7 @@ function readStack() {
|
|
|
14677
14810
|
return [];
|
|
14678
14811
|
}
|
|
14679
14812
|
function writeStack(stack) {
|
|
14680
|
-
const dir =
|
|
14813
|
+
const dir = import_path27.default.dirname(SNAPSHOT_STACK_PATH);
|
|
14681
14814
|
if (!import_fs24.default.existsSync(dir)) import_fs24.default.mkdirSync(dir, { recursive: true });
|
|
14682
14815
|
import_fs24.default.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
|
|
14683
14816
|
}
|
|
@@ -14699,12 +14832,12 @@ function buildArgsSummary(tool, args) {
|
|
|
14699
14832
|
return "";
|
|
14700
14833
|
}
|
|
14701
14834
|
function findProjectRoot(filePath) {
|
|
14702
|
-
let dir =
|
|
14835
|
+
let dir = import_path27.default.dirname(filePath);
|
|
14703
14836
|
while (true) {
|
|
14704
|
-
if (import_fs24.default.existsSync(
|
|
14837
|
+
if (import_fs24.default.existsSync(import_path27.default.join(dir, ".git")) || import_fs24.default.existsSync(import_path27.default.join(dir, "package.json"))) {
|
|
14705
14838
|
return dir;
|
|
14706
14839
|
}
|
|
14707
|
-
const parent =
|
|
14840
|
+
const parent = import_path27.default.dirname(dir);
|
|
14708
14841
|
if (parent === dir) return process.cwd();
|
|
14709
14842
|
dir = parent;
|
|
14710
14843
|
}
|
|
@@ -14722,14 +14855,14 @@ function normalizeCwdForHash(cwd) {
|
|
|
14722
14855
|
}
|
|
14723
14856
|
function getShadowRepoDir(cwd) {
|
|
14724
14857
|
const hash = import_crypto8.default.createHash("sha256").update(normalizeCwdForHash(cwd)).digest("hex").slice(0, 16);
|
|
14725
|
-
return
|
|
14858
|
+
return import_path27.default.join(import_os21.default.homedir(), ".node9", "snapshots", hash);
|
|
14726
14859
|
}
|
|
14727
14860
|
function cleanOrphanedIndexFiles(shadowDir) {
|
|
14728
14861
|
try {
|
|
14729
14862
|
const cutoff = Date.now() - 6e4;
|
|
14730
14863
|
for (const f of import_fs24.default.readdirSync(shadowDir)) {
|
|
14731
14864
|
if (f.startsWith("index_")) {
|
|
14732
|
-
const fp =
|
|
14865
|
+
const fp = import_path27.default.join(shadowDir, f);
|
|
14733
14866
|
try {
|
|
14734
14867
|
if (import_fs24.default.statSync(fp).mtimeMs < cutoff) import_fs24.default.unlinkSync(fp);
|
|
14735
14868
|
} catch {
|
|
@@ -14743,7 +14876,7 @@ function writeShadowExcludes(shadowDir, ignorePaths) {
|
|
|
14743
14876
|
const hardcoded = [".git", ".node9"];
|
|
14744
14877
|
const lines = [...hardcoded, ...ignorePaths].join("\n");
|
|
14745
14878
|
try {
|
|
14746
|
-
import_fs24.default.writeFileSync(
|
|
14879
|
+
import_fs24.default.writeFileSync(import_path27.default.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
|
|
14747
14880
|
} catch {
|
|
14748
14881
|
}
|
|
14749
14882
|
}
|
|
@@ -14756,7 +14889,7 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
14756
14889
|
timeout: 3e3
|
|
14757
14890
|
});
|
|
14758
14891
|
if (check.status === 0) {
|
|
14759
|
-
const ptPath =
|
|
14892
|
+
const ptPath = import_path27.default.join(shadowDir, "project-path.txt");
|
|
14760
14893
|
try {
|
|
14761
14894
|
const stored = import_fs24.default.readFileSync(ptPath, "utf8").trim();
|
|
14762
14895
|
if (stored === normalizedCwd) return true;
|
|
@@ -14783,7 +14916,7 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
14783
14916
|
if (process.env.NODE9_DEBUG === "1") console.error("[Node9] git init --bare failed:", reason);
|
|
14784
14917
|
return false;
|
|
14785
14918
|
}
|
|
14786
|
-
const configFile =
|
|
14919
|
+
const configFile = import_path27.default.join(shadowDir, "config");
|
|
14787
14920
|
(0, import_child_process9.spawnSync)("git", ["config", "--file", configFile, "core.untrackedCache", "true"], {
|
|
14788
14921
|
timeout: 3e3
|
|
14789
14922
|
});
|
|
@@ -14791,7 +14924,7 @@ function ensureShadowRepo(shadowDir, cwd) {
|
|
|
14791
14924
|
timeout: 3e3
|
|
14792
14925
|
});
|
|
14793
14926
|
try {
|
|
14794
|
-
import_fs24.default.writeFileSync(
|
|
14927
|
+
import_fs24.default.writeFileSync(import_path27.default.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
|
|
14795
14928
|
} catch {
|
|
14796
14929
|
}
|
|
14797
14930
|
return true;
|
|
@@ -14811,12 +14944,12 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
|
|
|
14811
14944
|
let indexFile = null;
|
|
14812
14945
|
try {
|
|
14813
14946
|
const rawFilePath = extractFilePath(args);
|
|
14814
|
-
const absFilePath = rawFilePath &&
|
|
14947
|
+
const absFilePath = rawFilePath && import_path27.default.isAbsolute(rawFilePath) ? rawFilePath : null;
|
|
14815
14948
|
const cwd = absFilePath ? findProjectRoot(absFilePath) : process.cwd();
|
|
14816
14949
|
const shadowDir = getShadowRepoDir(cwd);
|
|
14817
14950
|
if (!ensureShadowRepo(shadowDir, cwd)) return null;
|
|
14818
14951
|
writeShadowExcludes(shadowDir, ignorePaths);
|
|
14819
|
-
indexFile =
|
|
14952
|
+
indexFile = import_path27.default.join(shadowDir, `index_${process.pid}_${Date.now()}`);
|
|
14820
14953
|
const shadowEnv = {
|
|
14821
14954
|
...process.env,
|
|
14822
14955
|
GIT_DIR: shadowDir,
|
|
@@ -14975,7 +15108,7 @@ function applyUndo(hash, cwd) {
|
|
|
14975
15108
|
timeout: GIT_TIMEOUT
|
|
14976
15109
|
}).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
|
|
14977
15110
|
for (const file of [...tracked, ...untracked]) {
|
|
14978
|
-
const fullPath =
|
|
15111
|
+
const fullPath = import_path27.default.join(dir, file);
|
|
14979
15112
|
if (!snapshotFiles.has(file) && import_fs24.default.existsSync(fullPath)) {
|
|
14980
15113
|
import_fs24.default.unlinkSync(fullPath);
|
|
14981
15114
|
}
|
|
@@ -14991,11 +15124,11 @@ init_daemon_starter();
|
|
|
14991
15124
|
|
|
14992
15125
|
// src/skill-pin.ts
|
|
14993
15126
|
var import_fs25 = __toESM(require("fs"));
|
|
14994
|
-
var
|
|
15127
|
+
var import_path28 = __toESM(require("path"));
|
|
14995
15128
|
var import_os22 = __toESM(require("os"));
|
|
14996
15129
|
var import_crypto9 = __toESM(require("crypto"));
|
|
14997
15130
|
function getPinsFilePath() {
|
|
14998
|
-
return
|
|
15131
|
+
return import_path28.default.join(import_os22.default.homedir(), ".node9", "skill-pins.json");
|
|
14999
15132
|
}
|
|
15000
15133
|
var MAX_FILES = 5e3;
|
|
15001
15134
|
var MAX_TOTAL_BYTES = 50 * 1024 * 1024;
|
|
@@ -15016,8 +15149,8 @@ function walkDir(root) {
|
|
|
15016
15149
|
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
15017
15150
|
for (const entry of entries) {
|
|
15018
15151
|
if (out.length >= MAX_FILES) return;
|
|
15019
|
-
const full =
|
|
15020
|
-
const rel = relDir ?
|
|
15152
|
+
const full = import_path28.default.join(dir, entry.name);
|
|
15153
|
+
const rel = relDir ? import_path28.default.posix.join(relDir, entry.name) : entry.name;
|
|
15021
15154
|
let lst;
|
|
15022
15155
|
try {
|
|
15023
15156
|
lst = import_fs25.default.lstatSync(full);
|
|
@@ -15091,7 +15224,7 @@ function readSkillPins() {
|
|
|
15091
15224
|
}
|
|
15092
15225
|
function writeSkillPins(data) {
|
|
15093
15226
|
const filePath = getPinsFilePath();
|
|
15094
|
-
import_fs25.default.mkdirSync(
|
|
15227
|
+
import_fs25.default.mkdirSync(import_path28.default.dirname(filePath), { recursive: true });
|
|
15095
15228
|
const tmp = `${filePath}.${import_crypto9.default.randomBytes(6).toString("hex")}.tmp`;
|
|
15096
15229
|
import_fs25.default.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
15097
15230
|
import_fs25.default.renameSync(tmp, filePath);
|
|
@@ -15138,7 +15271,7 @@ function verifyAndPinRoots(roots) {
|
|
|
15138
15271
|
return { kind: "verified" };
|
|
15139
15272
|
}
|
|
15140
15273
|
function defaultSkillRoots(_cwd) {
|
|
15141
|
-
const marketplaces =
|
|
15274
|
+
const marketplaces = import_path28.default.join(import_os22.default.homedir(), ".claude", "plugins", "marketplaces");
|
|
15142
15275
|
const roots = [];
|
|
15143
15276
|
let registries;
|
|
15144
15277
|
try {
|
|
@@ -15148,7 +15281,7 @@ function defaultSkillRoots(_cwd) {
|
|
|
15148
15281
|
}
|
|
15149
15282
|
for (const registry of registries) {
|
|
15150
15283
|
if (!registry.isDirectory()) continue;
|
|
15151
|
-
const pluginsDir =
|
|
15284
|
+
const pluginsDir = import_path28.default.join(marketplaces, registry.name, "plugins");
|
|
15152
15285
|
let plugins;
|
|
15153
15286
|
try {
|
|
15154
15287
|
plugins = import_fs25.default.readdirSync(pluginsDir, { withFileTypes: true });
|
|
@@ -15157,17 +15290,17 @@ function defaultSkillRoots(_cwd) {
|
|
|
15157
15290
|
}
|
|
15158
15291
|
for (const plugin of plugins) {
|
|
15159
15292
|
if (!plugin.isDirectory()) continue;
|
|
15160
|
-
roots.push(
|
|
15293
|
+
roots.push(import_path28.default.join(pluginsDir, plugin.name));
|
|
15161
15294
|
}
|
|
15162
15295
|
}
|
|
15163
15296
|
return roots;
|
|
15164
15297
|
}
|
|
15165
15298
|
function resolveUserSkillRoot(entry, cwd) {
|
|
15166
15299
|
if (!entry) return null;
|
|
15167
|
-
if (entry.startsWith("~/") || entry === "~") return
|
|
15168
|
-
if (
|
|
15169
|
-
if (!cwd || !
|
|
15170
|
-
return
|
|
15300
|
+
if (entry.startsWith("~/") || entry === "~") return import_path28.default.join(import_os22.default.homedir(), entry.slice(1));
|
|
15301
|
+
if (import_path28.default.isAbsolute(entry)) return entry;
|
|
15302
|
+
if (!cwd || !import_path28.default.isAbsolute(cwd)) return null;
|
|
15303
|
+
return import_path28.default.join(cwd, entry);
|
|
15171
15304
|
}
|
|
15172
15305
|
|
|
15173
15306
|
// src/cli/commands/check.ts
|
|
@@ -15185,7 +15318,7 @@ function registerCheckCommand(program2) {
|
|
|
15185
15318
|
} catch (err2) {
|
|
15186
15319
|
const tempConfig = getConfig();
|
|
15187
15320
|
if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
|
|
15188
|
-
const logPath =
|
|
15321
|
+
const logPath = import_path29.default.join(import_os23.default.homedir(), ".node9", "hook-debug.log");
|
|
15189
15322
|
const errMsg = err2 instanceof Error ? err2.message : String(err2);
|
|
15190
15323
|
import_fs26.default.appendFileSync(
|
|
15191
15324
|
logPath,
|
|
@@ -15200,11 +15333,11 @@ RAW: ${raw}
|
|
|
15200
15333
|
if (config.settings.autoStartDaemon && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON) {
|
|
15201
15334
|
try {
|
|
15202
15335
|
const scriptPath = process.argv[1];
|
|
15203
|
-
if (typeof scriptPath !== "string" || !
|
|
15336
|
+
if (typeof scriptPath !== "string" || !import_path29.default.isAbsolute(scriptPath))
|
|
15204
15337
|
throw new Error("node9: argv[1] is not an absolute path");
|
|
15205
15338
|
const resolvedScript = import_fs26.default.realpathSync(scriptPath);
|
|
15206
|
-
const packageDist = import_fs26.default.realpathSync(
|
|
15207
|
-
if (!resolvedScript.startsWith(packageDist +
|
|
15339
|
+
const packageDist = import_fs26.default.realpathSync(import_path29.default.resolve(__dirname, "../.."));
|
|
15340
|
+
if (!resolvedScript.startsWith(packageDist + import_path29.default.sep) && resolvedScript !== packageDist)
|
|
15208
15341
|
throw new Error(
|
|
15209
15342
|
`node9: daemon spawn aborted \u2014 argv[1] (${resolvedScript}) is outside package dist (${packageDist})`
|
|
15210
15343
|
);
|
|
@@ -15226,7 +15359,7 @@ RAW: ${raw}
|
|
|
15226
15359
|
});
|
|
15227
15360
|
d.unref();
|
|
15228
15361
|
} catch (spawnErr) {
|
|
15229
|
-
const logPath =
|
|
15362
|
+
const logPath = import_path29.default.join(import_os23.default.homedir(), ".node9", "hook-debug.log");
|
|
15230
15363
|
const msg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);
|
|
15231
15364
|
try {
|
|
15232
15365
|
import_fs26.default.appendFileSync(
|
|
@@ -15239,9 +15372,9 @@ RAW: ${raw}
|
|
|
15239
15372
|
}
|
|
15240
15373
|
}
|
|
15241
15374
|
if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
|
|
15242
|
-
const logPath =
|
|
15243
|
-
if (!import_fs26.default.existsSync(
|
|
15244
|
-
import_fs26.default.mkdirSync(
|
|
15375
|
+
const logPath = import_path29.default.join(import_os23.default.homedir(), ".node9", "hook-debug.log");
|
|
15376
|
+
if (!import_fs26.default.existsSync(import_path29.default.dirname(logPath)))
|
|
15377
|
+
import_fs26.default.mkdirSync(import_path29.default.dirname(logPath), { recursive: true });
|
|
15245
15378
|
import_fs26.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
|
|
15246
15379
|
`);
|
|
15247
15380
|
}
|
|
@@ -15309,8 +15442,8 @@ RAW: ${raw}
|
|
|
15309
15442
|
const safeSessionId = /^[A-Za-z0-9_\-]{1,128}$/.test(rawSessionId) ? rawSessionId : "";
|
|
15310
15443
|
if (skillPinCfg.enabled && safeSessionId) {
|
|
15311
15444
|
try {
|
|
15312
|
-
const sessionsDir =
|
|
15313
|
-
const flagPath =
|
|
15445
|
+
const sessionsDir = import_path29.default.join(import_os23.default.homedir(), ".node9", "skill-sessions");
|
|
15446
|
+
const flagPath = import_path29.default.join(sessionsDir, `${safeSessionId}.json`);
|
|
15314
15447
|
let flag = null;
|
|
15315
15448
|
try {
|
|
15316
15449
|
flag = JSON.parse(import_fs26.default.readFileSync(flagPath, "utf-8"));
|
|
@@ -15362,7 +15495,7 @@ RAW: ${raw}
|
|
|
15362
15495
|
return;
|
|
15363
15496
|
}
|
|
15364
15497
|
if (!flag || flag.state !== "verified" && flag.state !== "warned") {
|
|
15365
|
-
const absoluteCwd = typeof payload.cwd === "string" &&
|
|
15498
|
+
const absoluteCwd = typeof payload.cwd === "string" && import_path29.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
15366
15499
|
const extraRoots = skillPinCfg.roots;
|
|
15367
15500
|
const resolvedExtra = extraRoots.map((r) => resolveUserSkillRoot(r, absoluteCwd)).filter((r) => typeof r === "string");
|
|
15368
15501
|
const roots = [...defaultSkillRoots(absoluteCwd), ...resolvedExtra];
|
|
@@ -15404,7 +15537,7 @@ RAW: ${raw}
|
|
|
15404
15537
|
try {
|
|
15405
15538
|
const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1e3;
|
|
15406
15539
|
for (const name of import_fs26.default.readdirSync(sessionsDir)) {
|
|
15407
|
-
const p =
|
|
15540
|
+
const p = import_path29.default.join(sessionsDir, name);
|
|
15408
15541
|
try {
|
|
15409
15542
|
if (import_fs26.default.statSync(p).mtimeMs < cutoff) import_fs26.default.unlinkSync(p);
|
|
15410
15543
|
} catch {
|
|
@@ -15416,7 +15549,7 @@ RAW: ${raw}
|
|
|
15416
15549
|
} catch (err2) {
|
|
15417
15550
|
if (process.env.NODE9_DEBUG === "1") {
|
|
15418
15551
|
try {
|
|
15419
|
-
const dbg =
|
|
15552
|
+
const dbg = import_path29.default.join(import_os23.default.homedir(), ".node9", "hook-debug.log");
|
|
15420
15553
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
15421
15554
|
import_fs26.default.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
|
|
15422
15555
|
`);
|
|
@@ -15428,7 +15561,7 @@ RAW: ${raw}
|
|
|
15428
15561
|
if (shouldSnapshot(toolName, toolInput, config)) {
|
|
15429
15562
|
await createShadowSnapshot(toolName, toolInput, config.policy.snapshot.ignorePaths);
|
|
15430
15563
|
}
|
|
15431
|
-
const safeCwdForAuth = typeof payload.cwd === "string" &&
|
|
15564
|
+
const safeCwdForAuth = typeof payload.cwd === "string" && import_path29.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
15432
15565
|
const result = await authorizeHeadless(toolName, toolInput, meta, {
|
|
15433
15566
|
cwd: safeCwdForAuth
|
|
15434
15567
|
});
|
|
@@ -15472,7 +15605,7 @@ RAW: ${raw}
|
|
|
15472
15605
|
});
|
|
15473
15606
|
} catch (err2) {
|
|
15474
15607
|
if (process.env.NODE9_DEBUG === "1") {
|
|
15475
|
-
const logPath =
|
|
15608
|
+
const logPath = import_path29.default.join(import_os23.default.homedir(), ".node9", "hook-debug.log");
|
|
15476
15609
|
const errMsg = err2 instanceof Error ? err2.message : String(err2);
|
|
15477
15610
|
import_fs26.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
|
|
15478
15611
|
`);
|
|
@@ -15509,7 +15642,7 @@ RAW: ${raw}
|
|
|
15509
15642
|
|
|
15510
15643
|
// src/cli/commands/log.ts
|
|
15511
15644
|
var import_fs27 = __toESM(require("fs"));
|
|
15512
|
-
var
|
|
15645
|
+
var import_path30 = __toESM(require("path"));
|
|
15513
15646
|
var import_os24 = __toESM(require("os"));
|
|
15514
15647
|
init_audit();
|
|
15515
15648
|
init_config();
|
|
@@ -15585,9 +15718,9 @@ function registerLogCommand(program2) {
|
|
|
15585
15718
|
decision: "allowed",
|
|
15586
15719
|
source: "post-hook"
|
|
15587
15720
|
};
|
|
15588
|
-
const logPath =
|
|
15589
|
-
if (!import_fs27.default.existsSync(
|
|
15590
|
-
import_fs27.default.mkdirSync(
|
|
15721
|
+
const logPath = import_path30.default.join(import_os24.default.homedir(), ".node9", "audit.log");
|
|
15722
|
+
if (!import_fs27.default.existsSync(import_path30.default.dirname(logPath)))
|
|
15723
|
+
import_fs27.default.mkdirSync(import_path30.default.dirname(logPath), { recursive: true });
|
|
15591
15724
|
import_fs27.default.appendFileSync(logPath, JSON.stringify(entry) + "\n");
|
|
15592
15725
|
if ((tool === "Bash" || tool === "bash") && isDaemonRunning()) {
|
|
15593
15726
|
const command = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
|
|
@@ -15621,7 +15754,7 @@ function registerLogCommand(program2) {
|
|
|
15621
15754
|
}
|
|
15622
15755
|
}
|
|
15623
15756
|
}
|
|
15624
|
-
const safeCwd = typeof payload.cwd === "string" &&
|
|
15757
|
+
const safeCwd = typeof payload.cwd === "string" && import_path30.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
15625
15758
|
const config = getConfig(safeCwd);
|
|
15626
15759
|
if ((tool === "Bash" || tool === "bash") && config.settings.enableUndo !== false) {
|
|
15627
15760
|
const bashCommand = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
|
|
@@ -15642,7 +15775,7 @@ function registerLogCommand(program2) {
|
|
|
15642
15775
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
15643
15776
|
process.stderr.write(`[Node9] audit log error: ${msg}
|
|
15644
15777
|
`);
|
|
15645
|
-
const debugPath =
|
|
15778
|
+
const debugPath = import_path30.default.join(import_os24.default.homedir(), ".node9", "hook-debug.log");
|
|
15646
15779
|
try {
|
|
15647
15780
|
import_fs27.default.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
|
|
15648
15781
|
`);
|
|
@@ -16045,7 +16178,7 @@ function registerConfigShowCommand(program2) {
|
|
|
16045
16178
|
// src/cli/commands/doctor.ts
|
|
16046
16179
|
var import_chalk8 = __toESM(require("chalk"));
|
|
16047
16180
|
var import_fs28 = __toESM(require("fs"));
|
|
16048
|
-
var
|
|
16181
|
+
var import_path31 = __toESM(require("path"));
|
|
16049
16182
|
var import_os25 = __toESM(require("os"));
|
|
16050
16183
|
var import_child_process11 = require("child_process");
|
|
16051
16184
|
init_daemon();
|
|
@@ -16100,7 +16233,7 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16100
16233
|
);
|
|
16101
16234
|
}
|
|
16102
16235
|
section("Configuration");
|
|
16103
|
-
const globalConfigPath =
|
|
16236
|
+
const globalConfigPath = import_path31.default.join(homeDir2, ".node9", "config.json");
|
|
16104
16237
|
if (import_fs28.default.existsSync(globalConfigPath)) {
|
|
16105
16238
|
try {
|
|
16106
16239
|
JSON.parse(import_fs28.default.readFileSync(globalConfigPath, "utf-8"));
|
|
@@ -16111,7 +16244,7 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16111
16244
|
} else {
|
|
16112
16245
|
warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
|
|
16113
16246
|
}
|
|
16114
|
-
const projectConfigPath =
|
|
16247
|
+
const projectConfigPath = import_path31.default.join(process.cwd(), "node9.config.json");
|
|
16115
16248
|
if (import_fs28.default.existsSync(projectConfigPath)) {
|
|
16116
16249
|
try {
|
|
16117
16250
|
JSON.parse(import_fs28.default.readFileSync(projectConfigPath, "utf-8"));
|
|
@@ -16123,7 +16256,7 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16123
16256
|
);
|
|
16124
16257
|
}
|
|
16125
16258
|
}
|
|
16126
|
-
const credsPath =
|
|
16259
|
+
const credsPath = import_path31.default.join(homeDir2, ".node9", "credentials.json");
|
|
16127
16260
|
if (import_fs28.default.existsSync(credsPath)) {
|
|
16128
16261
|
pass("Cloud credentials found (~/.node9/credentials.json)");
|
|
16129
16262
|
} else {
|
|
@@ -16133,7 +16266,7 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16133
16266
|
);
|
|
16134
16267
|
}
|
|
16135
16268
|
section("Agent Hooks");
|
|
16136
|
-
const claudeSettingsPath =
|
|
16269
|
+
const claudeSettingsPath = import_path31.default.join(homeDir2, ".claude", "settings.json");
|
|
16137
16270
|
if (import_fs28.default.existsSync(claudeSettingsPath)) {
|
|
16138
16271
|
try {
|
|
16139
16272
|
const cs = JSON.parse(import_fs28.default.readFileSync(claudeSettingsPath, "utf-8"));
|
|
@@ -16152,7 +16285,7 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16152
16285
|
} else {
|
|
16153
16286
|
warn("Claude Code \u2014 not configured", "Run: node9 setup claude");
|
|
16154
16287
|
}
|
|
16155
|
-
const geminiSettingsPath =
|
|
16288
|
+
const geminiSettingsPath = import_path31.default.join(homeDir2, ".gemini", "settings.json");
|
|
16156
16289
|
if (import_fs28.default.existsSync(geminiSettingsPath)) {
|
|
16157
16290
|
try {
|
|
16158
16291
|
const gs = JSON.parse(import_fs28.default.readFileSync(geminiSettingsPath, "utf-8"));
|
|
@@ -16171,7 +16304,7 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16171
16304
|
} else {
|
|
16172
16305
|
warn("Gemini CLI \u2014 not configured", "Run: node9 setup gemini (skip if not using Gemini)");
|
|
16173
16306
|
}
|
|
16174
|
-
const cursorHooksPath =
|
|
16307
|
+
const cursorHooksPath = import_path31.default.join(homeDir2, ".cursor", "hooks.json");
|
|
16175
16308
|
if (import_fs28.default.existsSync(cursorHooksPath)) {
|
|
16176
16309
|
try {
|
|
16177
16310
|
const cur = JSON.parse(import_fs28.default.readFileSync(cursorHooksPath, "utf-8"));
|
|
@@ -16213,7 +16346,7 @@ function registerDoctorCommand(program2, version2) {
|
|
|
16213
16346
|
// src/cli/commands/audit.ts
|
|
16214
16347
|
var import_chalk9 = __toESM(require("chalk"));
|
|
16215
16348
|
var import_fs29 = __toESM(require("fs"));
|
|
16216
|
-
var
|
|
16349
|
+
var import_path32 = __toESM(require("path"));
|
|
16217
16350
|
var import_os26 = __toESM(require("os"));
|
|
16218
16351
|
function formatRelativeTime(timestamp) {
|
|
16219
16352
|
const diff = Date.now() - new Date(timestamp).getTime();
|
|
@@ -16227,7 +16360,7 @@ function formatRelativeTime(timestamp) {
|
|
|
16227
16360
|
}
|
|
16228
16361
|
function registerAuditCommand(program2) {
|
|
16229
16362
|
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) => {
|
|
16230
|
-
const logPath =
|
|
16363
|
+
const logPath = import_path32.default.join(import_os26.default.homedir(), ".node9", "audit.log");
|
|
16231
16364
|
if (!import_fs29.default.existsSync(logPath)) {
|
|
16232
16365
|
console.log(
|
|
16233
16366
|
import_chalk9.default.yellow("No audit logs found. Run node9 with an agent to generate entries.")
|
|
@@ -16289,7 +16422,7 @@ function registerAuditCommand(program2) {
|
|
|
16289
16422
|
// src/cli/commands/report.ts
|
|
16290
16423
|
var import_chalk10 = __toESM(require("chalk"));
|
|
16291
16424
|
var import_fs30 = __toESM(require("fs"));
|
|
16292
|
-
var
|
|
16425
|
+
var import_path33 = __toESM(require("path"));
|
|
16293
16426
|
var import_os27 = __toESM(require("os"));
|
|
16294
16427
|
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;
|
|
16295
16428
|
function buildTestTimestamps(allEntries) {
|
|
@@ -16417,7 +16550,7 @@ function loadClaudeCost(start, end) {
|
|
|
16417
16550
|
cacheWriteTokens: 0,
|
|
16418
16551
|
cacheReadTokens: 0
|
|
16419
16552
|
};
|
|
16420
|
-
const projectsDir =
|
|
16553
|
+
const projectsDir = import_path33.default.join(import_os27.default.homedir(), ".claude", "projects");
|
|
16421
16554
|
if (!import_fs30.default.existsSync(projectsDir)) return empty;
|
|
16422
16555
|
let dirs;
|
|
16423
16556
|
try {
|
|
@@ -16433,7 +16566,7 @@ function loadClaudeCost(start, end) {
|
|
|
16433
16566
|
const byDay = /* @__PURE__ */ new Map();
|
|
16434
16567
|
const byModel = /* @__PURE__ */ new Map();
|
|
16435
16568
|
for (const proj of dirs) {
|
|
16436
|
-
const projPath =
|
|
16569
|
+
const projPath = import_path33.default.join(projectsDir, proj);
|
|
16437
16570
|
let files;
|
|
16438
16571
|
try {
|
|
16439
16572
|
const stat = import_fs30.default.statSync(projPath);
|
|
@@ -16444,7 +16577,7 @@ function loadClaudeCost(start, end) {
|
|
|
16444
16577
|
}
|
|
16445
16578
|
for (const file of files) {
|
|
16446
16579
|
try {
|
|
16447
|
-
const raw = import_fs30.default.readFileSync(
|
|
16580
|
+
const raw = import_fs30.default.readFileSync(import_path33.default.join(projPath, file), "utf-8");
|
|
16448
16581
|
for (const line of raw.split("\n")) {
|
|
16449
16582
|
if (!line.trim()) continue;
|
|
16450
16583
|
let entry;
|
|
@@ -16485,7 +16618,7 @@ function loadClaudeCost(start, end) {
|
|
|
16485
16618
|
return { total, byDay, byModel, inputTokens, outputTokens, cacheWriteTokens, cacheReadTokens };
|
|
16486
16619
|
}
|
|
16487
16620
|
function loadCodexCost(start, end) {
|
|
16488
|
-
const sessionsBase =
|
|
16621
|
+
const sessionsBase = import_path33.default.join(import_os27.default.homedir(), ".codex", "sessions");
|
|
16489
16622
|
const byDay = /* @__PURE__ */ new Map();
|
|
16490
16623
|
let total = 0;
|
|
16491
16624
|
let toolCalls = 0;
|
|
@@ -16493,28 +16626,28 @@ function loadCodexCost(start, end) {
|
|
|
16493
16626
|
const jsonlFiles = [];
|
|
16494
16627
|
try {
|
|
16495
16628
|
for (const year of import_fs30.default.readdirSync(sessionsBase)) {
|
|
16496
|
-
const yearPath =
|
|
16629
|
+
const yearPath = import_path33.default.join(sessionsBase, year);
|
|
16497
16630
|
try {
|
|
16498
16631
|
if (!import_fs30.default.statSync(yearPath).isDirectory()) continue;
|
|
16499
16632
|
} catch {
|
|
16500
16633
|
continue;
|
|
16501
16634
|
}
|
|
16502
16635
|
for (const month of import_fs30.default.readdirSync(yearPath)) {
|
|
16503
|
-
const monthPath =
|
|
16636
|
+
const monthPath = import_path33.default.join(yearPath, month);
|
|
16504
16637
|
try {
|
|
16505
16638
|
if (!import_fs30.default.statSync(monthPath).isDirectory()) continue;
|
|
16506
16639
|
} catch {
|
|
16507
16640
|
continue;
|
|
16508
16641
|
}
|
|
16509
16642
|
for (const day of import_fs30.default.readdirSync(monthPath)) {
|
|
16510
|
-
const dayPath =
|
|
16643
|
+
const dayPath = import_path33.default.join(monthPath, day);
|
|
16511
16644
|
try {
|
|
16512
16645
|
if (!import_fs30.default.statSync(dayPath).isDirectory()) continue;
|
|
16513
16646
|
} catch {
|
|
16514
16647
|
continue;
|
|
16515
16648
|
}
|
|
16516
16649
|
for (const file of import_fs30.default.readdirSync(dayPath)) {
|
|
16517
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(
|
|
16650
|
+
if (file.endsWith(".jsonl")) jsonlFiles.push(import_path33.default.join(dayPath, file));
|
|
16518
16651
|
}
|
|
16519
16652
|
}
|
|
16520
16653
|
}
|
|
@@ -16575,7 +16708,7 @@ function registerReportCommand(program2) {
|
|
|
16575
16708
|
const period = ["today", "7d", "30d", "month"].includes(
|
|
16576
16709
|
options.period
|
|
16577
16710
|
) ? options.period : "7d";
|
|
16578
|
-
const logPath =
|
|
16711
|
+
const logPath = import_path33.default.join(import_os27.default.homedir(), ".node9", "audit.log");
|
|
16579
16712
|
const allEntries = parseAuditLog(logPath);
|
|
16580
16713
|
const unackedDlp = allEntries.filter((e) => e.source === "response-dlp");
|
|
16581
16714
|
if (unackedDlp.length > 0) {
|
|
@@ -17081,7 +17214,7 @@ function registerDaemonCommand(program2) {
|
|
|
17081
17214
|
// src/cli/commands/status.ts
|
|
17082
17215
|
var import_chalk12 = __toESM(require("chalk"));
|
|
17083
17216
|
var import_fs31 = __toESM(require("fs"));
|
|
17084
|
-
var
|
|
17217
|
+
var import_path34 = __toESM(require("path"));
|
|
17085
17218
|
var import_os28 = __toESM(require("os"));
|
|
17086
17219
|
init_core();
|
|
17087
17220
|
init_daemon();
|
|
@@ -17152,8 +17285,8 @@ function registerStatusCommand(program2) {
|
|
|
17152
17285
|
console.log("");
|
|
17153
17286
|
const modeLabel = settings.mode === "audit" ? import_chalk12.default.blue("audit") : settings.mode === "strict" ? import_chalk12.default.red("strict") : import_chalk12.default.white("standard");
|
|
17154
17287
|
console.log(` Mode: ${modeLabel}`);
|
|
17155
|
-
const projectConfig =
|
|
17156
|
-
const globalConfig =
|
|
17288
|
+
const projectConfig = import_path34.default.join(process.cwd(), "node9.config.json");
|
|
17289
|
+
const globalConfig = import_path34.default.join(import_os28.default.homedir(), ".node9", "config.json");
|
|
17157
17290
|
console.log(
|
|
17158
17291
|
` Local: ${import_fs31.default.existsSync(projectConfig) ? import_chalk12.default.green("Active (node9.config.json)") : import_chalk12.default.gray("Not present")}`
|
|
17159
17292
|
);
|
|
@@ -17167,13 +17300,13 @@ function registerStatusCommand(program2) {
|
|
|
17167
17300
|
}
|
|
17168
17301
|
const homeDir2 = import_os28.default.homedir();
|
|
17169
17302
|
const claudeSettings = readJson2(
|
|
17170
|
-
|
|
17303
|
+
import_path34.default.join(homeDir2, ".claude", "settings.json")
|
|
17171
17304
|
);
|
|
17172
|
-
const claudeConfig = readJson2(
|
|
17305
|
+
const claudeConfig = readJson2(import_path34.default.join(homeDir2, ".claude.json"));
|
|
17173
17306
|
const geminiSettings = readJson2(
|
|
17174
|
-
|
|
17307
|
+
import_path34.default.join(homeDir2, ".gemini", "settings.json")
|
|
17175
17308
|
);
|
|
17176
|
-
const cursorConfig = readJson2(
|
|
17309
|
+
const cursorConfig = readJson2(import_path34.default.join(homeDir2, ".cursor", "mcp.json"));
|
|
17177
17310
|
const agentFound = claudeSettings || claudeConfig || geminiSettings || cursorConfig;
|
|
17178
17311
|
if (agentFound) {
|
|
17179
17312
|
console.log("");
|
|
@@ -17233,7 +17366,7 @@ function registerStatusCommand(program2) {
|
|
|
17233
17366
|
// src/cli/commands/init.ts
|
|
17234
17367
|
var import_chalk13 = __toESM(require("chalk"));
|
|
17235
17368
|
var import_fs32 = __toESM(require("fs"));
|
|
17236
|
-
var
|
|
17369
|
+
var import_path35 = __toESM(require("path"));
|
|
17237
17370
|
var import_os29 = __toESM(require("os"));
|
|
17238
17371
|
var import_https3 = __toESM(require("https"));
|
|
17239
17372
|
init_core();
|
|
@@ -17295,7 +17428,7 @@ function registerInitCommand(program2) {
|
|
|
17295
17428
|
}
|
|
17296
17429
|
console.log("");
|
|
17297
17430
|
}
|
|
17298
|
-
const configPath =
|
|
17431
|
+
const configPath = import_path35.default.join(import_os29.default.homedir(), ".node9", "config.json");
|
|
17299
17432
|
if (import_fs32.default.existsSync(configPath) && !options.force) {
|
|
17300
17433
|
try {
|
|
17301
17434
|
const existing = JSON.parse(import_fs32.default.readFileSync(configPath, "utf-8"));
|
|
@@ -17316,7 +17449,7 @@ function registerInitCommand(program2) {
|
|
|
17316
17449
|
...DEFAULT_CONFIG,
|
|
17317
17450
|
settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
|
|
17318
17451
|
};
|
|
17319
|
-
const dir =
|
|
17452
|
+
const dir = import_path35.default.dirname(configPath);
|
|
17320
17453
|
if (!import_fs32.default.existsSync(dir)) import_fs32.default.mkdirSync(dir, { recursive: true });
|
|
17321
17454
|
import_fs32.default.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
|
|
17322
17455
|
console.log(import_chalk13.default.green(`\u2705 Config created: ${configPath}`));
|
|
@@ -17403,7 +17536,7 @@ function registerInitCommand(program2) {
|
|
|
17403
17536
|
}
|
|
17404
17537
|
|
|
17405
17538
|
// src/cli/commands/undo.ts
|
|
17406
|
-
var
|
|
17539
|
+
var import_path36 = __toESM(require("path"));
|
|
17407
17540
|
var import_chalk15 = __toESM(require("chalk"));
|
|
17408
17541
|
|
|
17409
17542
|
// src/tui/undo-navigator.ts
|
|
@@ -17562,7 +17695,7 @@ function findMatchingCwd(startDir, history) {
|
|
|
17562
17695
|
let dir = startDir;
|
|
17563
17696
|
while (true) {
|
|
17564
17697
|
if (cwds.has(dir)) return dir;
|
|
17565
|
-
const parent =
|
|
17698
|
+
const parent = import_path36.default.dirname(dir);
|
|
17566
17699
|
if (parent === dir) return null;
|
|
17567
17700
|
dir = parent;
|
|
17568
17701
|
}
|
|
@@ -17759,11 +17892,11 @@ init_provenance();
|
|
|
17759
17892
|
|
|
17760
17893
|
// src/mcp-pin.ts
|
|
17761
17894
|
var import_fs33 = __toESM(require("fs"));
|
|
17762
|
-
var
|
|
17895
|
+
var import_path37 = __toESM(require("path"));
|
|
17763
17896
|
var import_os30 = __toESM(require("os"));
|
|
17764
17897
|
var import_crypto10 = __toESM(require("crypto"));
|
|
17765
17898
|
function getPinsFilePath2() {
|
|
17766
|
-
return
|
|
17899
|
+
return import_path37.default.join(import_os30.default.homedir(), ".node9", "mcp-pins.json");
|
|
17767
17900
|
}
|
|
17768
17901
|
function hashToolDefinitions(tools) {
|
|
17769
17902
|
const sorted = [...tools].sort((a, b) => {
|
|
@@ -17804,7 +17937,7 @@ function readMcpPins() {
|
|
|
17804
17937
|
}
|
|
17805
17938
|
function writeMcpPins(data) {
|
|
17806
17939
|
const filePath = getPinsFilePath2();
|
|
17807
|
-
import_fs33.default.mkdirSync(
|
|
17940
|
+
import_fs33.default.mkdirSync(import_path37.default.dirname(filePath), { recursive: true });
|
|
17808
17941
|
const tmp = `${filePath}.${import_crypto10.default.randomBytes(6).toString("hex")}.tmp`;
|
|
17809
17942
|
import_fs33.default.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
17810
17943
|
import_fs33.default.renameSync(tmp, filePath);
|
|
@@ -18257,7 +18390,7 @@ function registerMcpGatewayCommand(program2) {
|
|
|
18257
18390
|
var import_readline4 = __toESM(require("readline"));
|
|
18258
18391
|
var import_fs34 = __toESM(require("fs"));
|
|
18259
18392
|
var import_os31 = __toESM(require("os"));
|
|
18260
|
-
var
|
|
18393
|
+
var import_path38 = __toESM(require("path"));
|
|
18261
18394
|
var import_child_process15 = require("child_process");
|
|
18262
18395
|
init_core();
|
|
18263
18396
|
init_daemon();
|
|
@@ -18508,8 +18641,8 @@ function handleStatus() {
|
|
|
18508
18641
|
lines.push(`Active shields: ${activeShields.length > 0 ? activeShields.join(", ") : "none"}`);
|
|
18509
18642
|
lines.push(`Smart rules: ${config.policy.smartRules.length} loaded`);
|
|
18510
18643
|
lines.push(`DLP: ${config.policy.dlp?.enabled !== false ? "enabled" : "disabled"}`);
|
|
18511
|
-
const projectConfig =
|
|
18512
|
-
const globalConfig =
|
|
18644
|
+
const projectConfig = import_path38.default.join(process.cwd(), "node9.config.json");
|
|
18645
|
+
const globalConfig = import_path38.default.join(import_os31.default.homedir(), ".node9", "config.json");
|
|
18513
18646
|
lines.push(
|
|
18514
18647
|
`Project config (node9.config.json): ${import_fs34.default.existsSync(projectConfig) ? "present" : "not found"}`
|
|
18515
18648
|
);
|
|
@@ -18588,7 +18721,7 @@ function handleShieldDisable(args) {
|
|
|
18588
18721
|
writeActiveShields(active.filter((s) => s !== name));
|
|
18589
18722
|
return `Shield "${name}" disabled.`;
|
|
18590
18723
|
}
|
|
18591
|
-
var GLOBAL_CONFIG_PATH2 =
|
|
18724
|
+
var GLOBAL_CONFIG_PATH2 = import_path38.default.join(import_os31.default.homedir(), ".node9", "config.json");
|
|
18592
18725
|
var APPROVER_CHANNELS = ["native", "browser", "cloud", "terminal"];
|
|
18593
18726
|
function readGlobalConfigRaw() {
|
|
18594
18727
|
try {
|
|
@@ -18600,7 +18733,7 @@ function readGlobalConfigRaw() {
|
|
|
18600
18733
|
return {};
|
|
18601
18734
|
}
|
|
18602
18735
|
function writeGlobalConfigRaw(data) {
|
|
18603
|
-
const dir =
|
|
18736
|
+
const dir = import_path38.default.dirname(GLOBAL_CONFIG_PATH2);
|
|
18604
18737
|
if (!import_fs34.default.existsSync(dir)) import_fs34.default.mkdirSync(dir, { recursive: true });
|
|
18605
18738
|
import_fs34.default.writeFileSync(GLOBAL_CONFIG_PATH2, JSON.stringify(data, null, 2) + "\n");
|
|
18606
18739
|
}
|
|
@@ -18646,7 +18779,7 @@ function handleApproverSet(args) {
|
|
|
18646
18779
|
function handleAuditGet(args) {
|
|
18647
18780
|
const limit = Math.min(typeof args.limit === "number" ? args.limit : 20, 100);
|
|
18648
18781
|
const filter = typeof args.filter === "string" && args.filter !== "all" ? args.filter : null;
|
|
18649
|
-
const auditPath =
|
|
18782
|
+
const auditPath = import_path38.default.join(import_os31.default.homedir(), ".node9", "audit.log");
|
|
18650
18783
|
if (!import_fs34.default.existsSync(auditPath)) return "No audit log found.";
|
|
18651
18784
|
const rawLines = import_fs34.default.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
18652
18785
|
const parsed = [];
|
|
@@ -19203,7 +19336,7 @@ init_scan();
|
|
|
19203
19336
|
// src/cli/commands/sessions.ts
|
|
19204
19337
|
var import_chalk22 = __toESM(require("chalk"));
|
|
19205
19338
|
var import_fs35 = __toESM(require("fs"));
|
|
19206
|
-
var
|
|
19339
|
+
var import_path39 = __toESM(require("path"));
|
|
19207
19340
|
var import_os32 = __toESM(require("os"));
|
|
19208
19341
|
var CLAUDE_PRICING3 = {
|
|
19209
19342
|
"claude-opus-4-6": { i: 5e-6, o: 25e-6, cw: 625e-8, cr: 5e-7 },
|
|
@@ -19245,7 +19378,7 @@ function encodeProjectPath(projectPath) {
|
|
|
19245
19378
|
}
|
|
19246
19379
|
function sessionJsonlPath(projectPath, sessionId) {
|
|
19247
19380
|
const encoded = encodeProjectPath(projectPath);
|
|
19248
|
-
return
|
|
19381
|
+
return import_path39.default.join(import_os32.default.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
|
|
19249
19382
|
}
|
|
19250
19383
|
function projectLabel(projectPath) {
|
|
19251
19384
|
return projectPath.replace(import_os32.default.homedir(), "~");
|
|
@@ -19317,7 +19450,7 @@ function parseSessionLines(lines) {
|
|
|
19317
19450
|
return { toolCalls, costUSD, hasSnapshot, modifiedFiles };
|
|
19318
19451
|
}
|
|
19319
19452
|
function loadAuditEntries(auditPath) {
|
|
19320
|
-
const aPath = auditPath ??
|
|
19453
|
+
const aPath = auditPath ?? import_path39.default.join(import_os32.default.homedir(), ".node9", "audit.log");
|
|
19321
19454
|
let raw;
|
|
19322
19455
|
try {
|
|
19323
19456
|
raw = import_fs35.default.readFileSync(aPath, "utf-8");
|
|
@@ -19356,7 +19489,7 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
|
|
|
19356
19489
|
return result;
|
|
19357
19490
|
}
|
|
19358
19491
|
function buildGeminiSessions(days, allAuditEntries) {
|
|
19359
|
-
const tmpDir =
|
|
19492
|
+
const tmpDir = import_path39.default.join(import_os32.default.homedir(), ".gemini", "tmp");
|
|
19360
19493
|
if (!import_fs35.default.existsSync(tmpDir)) return [];
|
|
19361
19494
|
const cutoff = days !== null ? (() => {
|
|
19362
19495
|
const d = /* @__PURE__ */ new Date();
|
|
@@ -19372,18 +19505,18 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
19372
19505
|
}
|
|
19373
19506
|
const summaries = [];
|
|
19374
19507
|
for (const slug of slugDirs) {
|
|
19375
|
-
const slugPath =
|
|
19508
|
+
const slugPath = import_path39.default.join(tmpDir, slug);
|
|
19376
19509
|
try {
|
|
19377
19510
|
if (!import_fs35.default.statSync(slugPath).isDirectory()) continue;
|
|
19378
19511
|
} catch {
|
|
19379
19512
|
continue;
|
|
19380
19513
|
}
|
|
19381
|
-
let projectRoot =
|
|
19514
|
+
let projectRoot = import_path39.default.join(import_os32.default.homedir(), slug);
|
|
19382
19515
|
try {
|
|
19383
|
-
projectRoot = import_fs35.default.readFileSync(
|
|
19516
|
+
projectRoot = import_fs35.default.readFileSync(import_path39.default.join(slugPath, ".project_root"), "utf-8").trim();
|
|
19384
19517
|
} catch {
|
|
19385
19518
|
}
|
|
19386
|
-
const chatsDir =
|
|
19519
|
+
const chatsDir = import_path39.default.join(slugPath, "chats");
|
|
19387
19520
|
if (!import_fs35.default.existsSync(chatsDir)) continue;
|
|
19388
19521
|
let chatFiles;
|
|
19389
19522
|
try {
|
|
@@ -19394,7 +19527,7 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
19394
19527
|
for (const chatFile of chatFiles) {
|
|
19395
19528
|
let raw;
|
|
19396
19529
|
try {
|
|
19397
|
-
raw = import_fs35.default.readFileSync(
|
|
19530
|
+
raw = import_fs35.default.readFileSync(import_path39.default.join(chatsDir, chatFile), "utf-8");
|
|
19398
19531
|
} catch {
|
|
19399
19532
|
continue;
|
|
19400
19533
|
}
|
|
@@ -19474,7 +19607,7 @@ function buildGeminiSessions(days, allAuditEntries) {
|
|
|
19474
19607
|
return summaries;
|
|
19475
19608
|
}
|
|
19476
19609
|
function buildCodexSessions(days, allAuditEntries) {
|
|
19477
|
-
const sessionsBase =
|
|
19610
|
+
const sessionsBase = import_path39.default.join(import_os32.default.homedir(), ".codex", "sessions");
|
|
19478
19611
|
if (!import_fs35.default.existsSync(sessionsBase)) return [];
|
|
19479
19612
|
const cutoff = days !== null ? (() => {
|
|
19480
19613
|
const d = /* @__PURE__ */ new Date();
|
|
@@ -19485,28 +19618,28 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
19485
19618
|
const jsonlFiles = [];
|
|
19486
19619
|
try {
|
|
19487
19620
|
for (const year of import_fs35.default.readdirSync(sessionsBase)) {
|
|
19488
|
-
const yearPath =
|
|
19621
|
+
const yearPath = import_path39.default.join(sessionsBase, year);
|
|
19489
19622
|
try {
|
|
19490
19623
|
if (!import_fs35.default.statSync(yearPath).isDirectory()) continue;
|
|
19491
19624
|
} catch {
|
|
19492
19625
|
continue;
|
|
19493
19626
|
}
|
|
19494
19627
|
for (const month of import_fs35.default.readdirSync(yearPath)) {
|
|
19495
|
-
const monthPath =
|
|
19628
|
+
const monthPath = import_path39.default.join(yearPath, month);
|
|
19496
19629
|
try {
|
|
19497
19630
|
if (!import_fs35.default.statSync(monthPath).isDirectory()) continue;
|
|
19498
19631
|
} catch {
|
|
19499
19632
|
continue;
|
|
19500
19633
|
}
|
|
19501
19634
|
for (const day of import_fs35.default.readdirSync(monthPath)) {
|
|
19502
|
-
const dayPath =
|
|
19635
|
+
const dayPath = import_path39.default.join(monthPath, day);
|
|
19503
19636
|
try {
|
|
19504
19637
|
if (!import_fs35.default.statSync(dayPath).isDirectory()) continue;
|
|
19505
19638
|
} catch {
|
|
19506
19639
|
continue;
|
|
19507
19640
|
}
|
|
19508
19641
|
for (const file of import_fs35.default.readdirSync(dayPath)) {
|
|
19509
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(
|
|
19642
|
+
if (file.endsWith(".jsonl")) jsonlFiles.push(import_path39.default.join(dayPath, file));
|
|
19510
19643
|
}
|
|
19511
19644
|
}
|
|
19512
19645
|
}
|
|
@@ -19596,7 +19729,7 @@ function buildCodexSessions(days, allAuditEntries) {
|
|
|
19596
19729
|
return summaries;
|
|
19597
19730
|
}
|
|
19598
19731
|
function buildSessions(days, historyPath) {
|
|
19599
|
-
const hPath = historyPath ??
|
|
19732
|
+
const hPath = historyPath ?? import_path39.default.join(import_os32.default.homedir(), ".claude", "history.jsonl");
|
|
19600
19733
|
let historyRaw;
|
|
19601
19734
|
try {
|
|
19602
19735
|
historyRaw = import_fs35.default.readFileSync(hPath, "utf-8");
|
|
@@ -19890,7 +20023,7 @@ function registerSessionsCommand(program2) {
|
|
|
19890
20023
|
console.log("");
|
|
19891
20024
|
console.log(import_chalk22.default.cyan.bold("\u{1F4CB} node9 sessions") + import_chalk22.default.dim(" \u2014 what your AI agent did"));
|
|
19892
20025
|
console.log("");
|
|
19893
|
-
const historyPath =
|
|
20026
|
+
const historyPath = import_path39.default.join(import_os32.default.homedir(), ".claude", "history.jsonl");
|
|
19894
20027
|
if (!import_fs35.default.existsSync(historyPath)) {
|
|
19895
20028
|
console.log(import_chalk22.default.yellow(" No Claude session history found at ~/.claude/history.jsonl"));
|
|
19896
20029
|
console.log(import_chalk22.default.gray(" Install Claude Code, run a few sessions, then try again.\n"));
|
|
@@ -19930,10 +20063,10 @@ function registerSessionsCommand(program2) {
|
|
|
19930
20063
|
var import_chalk23 = __toESM(require("chalk"));
|
|
19931
20064
|
var import_fs36 = __toESM(require("fs"));
|
|
19932
20065
|
var import_os33 = __toESM(require("os"));
|
|
19933
|
-
var
|
|
20066
|
+
var import_path40 = __toESM(require("path"));
|
|
19934
20067
|
function wipeSkillSessions() {
|
|
19935
20068
|
try {
|
|
19936
|
-
import_fs36.default.rmSync(
|
|
20069
|
+
import_fs36.default.rmSync(import_path40.default.join(import_os33.default.homedir(), ".node9", "skill-sessions"), {
|
|
19937
20070
|
recursive: true,
|
|
19938
20071
|
force: true
|
|
19939
20072
|
});
|
|
@@ -20017,10 +20150,10 @@ function registerSkillPinCommand(program2) {
|
|
|
20017
20150
|
// src/cli/commands/dlp.ts
|
|
20018
20151
|
var import_chalk24 = __toESM(require("chalk"));
|
|
20019
20152
|
var import_fs37 = __toESM(require("fs"));
|
|
20020
|
-
var
|
|
20153
|
+
var import_path41 = __toESM(require("path"));
|
|
20021
20154
|
var import_os34 = __toESM(require("os"));
|
|
20022
|
-
var AUDIT_LOG =
|
|
20023
|
-
var RESOLVED_FILE =
|
|
20155
|
+
var AUDIT_LOG = import_path41.default.join(import_os34.default.homedir(), ".node9", "audit.log");
|
|
20156
|
+
var RESOLVED_FILE = import_path41.default.join(import_os34.default.homedir(), ".node9", "dlp-resolved.json");
|
|
20024
20157
|
var ANSI_RE = /\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g;
|
|
20025
20158
|
function stripAnsi(s) {
|
|
20026
20159
|
return s.replace(ANSI_RE, "");
|
|
@@ -20139,15 +20272,15 @@ function registerDlpCommand(program2) {
|
|
|
20139
20272
|
|
|
20140
20273
|
// src/cli.ts
|
|
20141
20274
|
var { version } = JSON.parse(
|
|
20142
|
-
import_fs40.default.readFileSync(
|
|
20275
|
+
import_fs40.default.readFileSync(import_path44.default.join(__dirname, "../package.json"), "utf-8")
|
|
20143
20276
|
);
|
|
20144
20277
|
var program = new import_commander.Command();
|
|
20145
20278
|
program.name("node9").description("The Sudo Command for AI Agents").version(version);
|
|
20146
20279
|
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) => {
|
|
20147
20280
|
const DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept";
|
|
20148
|
-
const credPath =
|
|
20149
|
-
if (!import_fs40.default.existsSync(
|
|
20150
|
-
import_fs40.default.mkdirSync(
|
|
20281
|
+
const credPath = import_path44.default.join(import_os37.default.homedir(), ".node9", "credentials.json");
|
|
20282
|
+
if (!import_fs40.default.existsSync(import_path44.default.dirname(credPath)))
|
|
20283
|
+
import_fs40.default.mkdirSync(import_path44.default.dirname(credPath), { recursive: true });
|
|
20151
20284
|
const profileName = options.profile || "default";
|
|
20152
20285
|
let existingCreds = {};
|
|
20153
20286
|
try {
|
|
@@ -20166,7 +20299,7 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
20166
20299
|
existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL2 };
|
|
20167
20300
|
import_fs40.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
|
|
20168
20301
|
if (profileName === "default") {
|
|
20169
|
-
const configPath =
|
|
20302
|
+
const configPath = import_path44.default.join(import_os37.default.homedir(), ".node9", "config.json");
|
|
20170
20303
|
let config = {};
|
|
20171
20304
|
try {
|
|
20172
20305
|
if (import_fs40.default.existsSync(configPath))
|
|
@@ -20185,8 +20318,8 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
|
|
|
20185
20318
|
approvers.cloud = false;
|
|
20186
20319
|
}
|
|
20187
20320
|
s.approvers = approvers;
|
|
20188
|
-
if (!import_fs40.default.existsSync(
|
|
20189
|
-
import_fs40.default.mkdirSync(
|
|
20321
|
+
if (!import_fs40.default.existsSync(import_path44.default.dirname(configPath)))
|
|
20322
|
+
import_fs40.default.mkdirSync(import_path44.default.dirname(configPath), { recursive: true });
|
|
20190
20323
|
import_fs40.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
20191
20324
|
}
|
|
20192
20325
|
if (options.profile && profileName !== "default") {
|
|
@@ -20324,7 +20457,7 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
|
|
|
20324
20457
|
}
|
|
20325
20458
|
}
|
|
20326
20459
|
if (options.purge) {
|
|
20327
|
-
const node9Dir =
|
|
20460
|
+
const node9Dir = import_path44.default.join(import_os37.default.homedir(), ".node9");
|
|
20328
20461
|
if (import_fs40.default.existsSync(node9Dir)) {
|
|
20329
20462
|
const confirmed = await (0, import_prompts2.confirm)({
|
|
20330
20463
|
message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
|
|
@@ -20474,9 +20607,9 @@ Claude Code spawns this command every ~300ms and writes a JSON payload to stdin.
|
|
|
20474
20607
|
Run "node9 addto claude" to register it as the statusLine.`
|
|
20475
20608
|
).argument("[subcommand]", 'Optional: "debug on" / "debug off" to toggle stdin logging').argument("[state]", 'on|off \u2014 used with "debug" subcommand').action(async (subcommand, state) => {
|
|
20476
20609
|
if (subcommand === "debug") {
|
|
20477
|
-
const flagFile =
|
|
20610
|
+
const flagFile = import_path44.default.join(import_os37.default.homedir(), ".node9", "hud-debug");
|
|
20478
20611
|
if (state === "on") {
|
|
20479
|
-
import_fs40.default.mkdirSync(
|
|
20612
|
+
import_fs40.default.mkdirSync(import_path44.default.dirname(flagFile), { recursive: true });
|
|
20480
20613
|
import_fs40.default.writeFileSync(flagFile, "");
|
|
20481
20614
|
console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
|
|
20482
20615
|
console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
|
|
@@ -20546,10 +20679,10 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
20546
20679
|
program.help();
|
|
20547
20680
|
return;
|
|
20548
20681
|
}
|
|
20549
|
-
const
|
|
20682
|
+
const fullCommand = runArgs.join(" ");
|
|
20550
20683
|
let result = await authorizeHeadless(
|
|
20551
20684
|
"shell",
|
|
20552
|
-
{ command:
|
|
20685
|
+
{ command: fullCommand },
|
|
20553
20686
|
{
|
|
20554
20687
|
agent: "Terminal"
|
|
20555
20688
|
}
|
|
@@ -20557,11 +20690,11 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
20557
20690
|
if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && getConfig().settings.autoStartDaemon) {
|
|
20558
20691
|
console.error(import_chalk26.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically..."));
|
|
20559
20692
|
const daemonReady = await autoStartDaemonAndWait();
|
|
20560
|
-
if (daemonReady) result = await authorizeHeadless("shell", { command:
|
|
20693
|
+
if (daemonReady) result = await authorizeHeadless("shell", { command: fullCommand });
|
|
20561
20694
|
}
|
|
20562
20695
|
if (result.noApprovalMechanism && process.stdout.isTTY) {
|
|
20563
20696
|
const approved = await (0, import_prompts2.confirm)({
|
|
20564
|
-
message: `\u{1F6E1}\uFE0F Node9: Allow "${
|
|
20697
|
+
message: `\u{1F6E1}\uFE0F Node9: Allow "${fullCommand}"?`,
|
|
20565
20698
|
default: false
|
|
20566
20699
|
});
|
|
20567
20700
|
result = { approved, reason: approved ? void 0 : "Denied by user at terminal." };
|
|
@@ -20574,7 +20707,7 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
|
|
|
20574
20707
|
process.exit(1);
|
|
20575
20708
|
}
|
|
20576
20709
|
console.error(import_chalk26.default.green("\n\u2705 Approved \u2014 running command...\n"));
|
|
20577
|
-
await runProxy(
|
|
20710
|
+
await runProxy(fullCommand);
|
|
20578
20711
|
} else {
|
|
20579
20712
|
program.help();
|
|
20580
20713
|
}
|
|
@@ -20593,7 +20726,7 @@ if (process.argv[2] !== "daemon") {
|
|
|
20593
20726
|
const isCheckHook = process.argv[2] === "check";
|
|
20594
20727
|
if (isCheckHook) {
|
|
20595
20728
|
if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
|
|
20596
|
-
const logPath =
|
|
20729
|
+
const logPath = import_path44.default.join(import_os37.default.homedir(), ".node9", "hook-debug.log");
|
|
20597
20730
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
20598
20731
|
import_fs40.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
|
|
20599
20732
|
`);
|