@node9/proxy 1.29.0 → 1.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -74,8 +74,13 @@ __export(audit_exports, {
74
74
  appendHookDebug: () => appendHookDebug,
75
75
  appendLocalAudit: () => appendLocalAudit,
76
76
  appendToLog: () => appendToLog,
77
+ buildArgsPreview: () => buildArgsPreview,
78
+ generateEventId: () => generateEventId,
77
79
  redactSecrets: () => redactSecrets
78
80
  });
81
+ function generateEventId() {
82
+ return `${Date.now().toString(36)}-${import_crypto2.default.randomBytes(6).toString("hex")}`;
83
+ }
79
84
  function isTestCall(toolName, args) {
80
85
  if (toolName !== "Bash" && toolName !== "bash") return false;
81
86
  const cmd = args?.command;
@@ -94,6 +99,17 @@ function redactSecrets(text) {
94
99
  );
95
100
  return redacted;
96
101
  }
102
+ function buildArgsPreview(args) {
103
+ try {
104
+ const o = args && typeof args === "object" ? args : null;
105
+ const primary = o && (o.command ?? o.file_path ?? o.path ?? o.url ?? o.query);
106
+ const text = typeof primary === "string" ? primary : args ? JSON.stringify(args) : "";
107
+ if (!text) return void 0;
108
+ return redactSecrets(text).slice(0, 120);
109
+ } catch {
110
+ return void 0;
111
+ }
112
+ }
97
113
  function appendToLog(logPath, entry) {
98
114
  try {
99
115
  const dir = import_path.default.dirname(logPath);
@@ -116,11 +132,18 @@ function appendHookDebug(toolName, args, meta, auditHashArgsEnabled) {
116
132
  });
117
133
  }
118
134
  function appendLocalAudit(toolName, args, decision, checkedBy, meta, auditHashArgsEnabled) {
119
- const argsField = auditHashArgsEnabled ? { argsHash: hashArgs(args) } : { args: args ? JSON.parse(redactSecrets(JSON.stringify(args))) : {} };
135
+ const isDlpRow = checkedBy.toLowerCase().includes("dlp") || Boolean(meta?.dlpPattern);
136
+ const preview2 = auditHashArgsEnabled && !isDlpRow ? buildArgsPreview(args) : void 0;
137
+ const argsField = auditHashArgsEnabled ? { argsHash: hashArgs(args), ...preview2 ? { argsPreview: preview2 } : {} } : { args: args ? JSON.parse(redactSecrets(JSON.stringify(args))) : {} };
120
138
  const testRun = isTestCall(toolName, args) || process.env.NODE9_TESTING === "1" ? { testRun: true } : {};
121
139
  const ruleNameField = meta?.ruleName ? { ruleName: meta.ruleName } : {};
122
140
  const agentToolNameField = meta?.agentToolName ? { agentToolName: meta.agentToolName } : {};
141
+ const dlpFields = meta?.dlpPattern ? { dlpPattern: meta.dlpPattern, dlpSample: meta.dlpSample } : {};
142
+ const cloudLinkField = meta?.cloudRequestId ? { cloudRequestId: meta.cloudRequestId } : {};
123
143
  appendToLog(LOCAL_AUDIT_LOG, {
144
+ // eid first: the outbox shipper dedups on it, and a fixed leading field
145
+ // makes the JSONL easy to eyeball.
146
+ eid: generateEventId(),
124
147
  ts: (/* @__PURE__ */ new Date()).toISOString(),
125
148
  tool: toolName,
126
149
  ...agentToolNameField,
@@ -128,6 +151,8 @@ function appendLocalAudit(toolName, args, decision, checkedBy, meta, auditHashAr
128
151
  decision,
129
152
  checkedBy,
130
153
  ...ruleNameField,
154
+ ...dlpFields,
155
+ ...cloudLinkField,
131
156
  ...testRun,
132
157
  agent: meta?.agent,
133
158
  mcpServer: meta?.mcpServer,
@@ -142,13 +167,14 @@ function appendConfigAudit(entry) {
142
167
  hostname: import_os.default.hostname()
143
168
  });
144
169
  }
145
- var import_fs, import_path, import_os, LOCAL_AUDIT_LOG, HOOK_DEBUG_LOG, TEST_COMMAND_RE;
170
+ var import_fs, import_path, import_os, import_crypto2, LOCAL_AUDIT_LOG, HOOK_DEBUG_LOG, TEST_COMMAND_RE;
146
171
  var init_audit = __esm({
147
172
  "src/audit/index.ts"() {
148
173
  "use strict";
149
174
  import_fs = __toESM(require("fs"));
150
175
  import_path = __toESM(require("path"));
151
176
  import_os = __toESM(require("os"));
177
+ import_crypto2 = __toESM(require("crypto"));
152
178
  init_hasher();
153
179
  LOCAL_AUDIT_LOG = import_path.default.join(import_os.default.homedir(), ".node9", "audit.log");
154
180
  HOOK_DEBUG_LOG = import_path.default.join(import_os.default.homedir(), ".node9", "hook-debug.log");
@@ -174,8 +200,8 @@ function sanitizeConfig(raw) {
174
200
  }
175
201
  }
176
202
  const lines = result.error.issues.map((issue) => {
177
- const path50 = issue.path.length > 0 ? issue.path.join(".") : "root";
178
- return ` \u2022 ${path50}: ${issue.message}`;
203
+ const path51 = issue.path.length > 0 ? issue.path.join(".") : "root";
204
+ return ` \u2022 ${path51}: ${issue.message}`;
179
205
  });
180
206
  return {
181
207
  sanitized,
@@ -261,7 +287,13 @@ var init_config_schema = __esm({
261
287
  allowGlobalPause: import_zod.z.boolean().optional(),
262
288
  auditHashArgs: import_zod.z.boolean().optional(),
263
289
  agentPolicy: import_zod.z.enum(["require_approval", "block_on_rules"]).optional(),
264
- cloudSyncIntervalHours: import_zod.z.number().positive().optional()
290
+ cloudSyncIntervalHours: import_zod.z.number().positive().optional(),
291
+ // Outbox shipper (audit.log → SaaS batch ingest). enabled defaults
292
+ // to true; set false to fall back to local-only auditing.
293
+ shipper: import_zod.z.object({
294
+ enabled: import_zod.z.boolean().optional(),
295
+ intervalSeconds: import_zod.z.number().min(5).optional()
296
+ }).optional()
265
297
  }).optional(),
266
298
  policy: import_zod.z.object({
267
299
  sandboxPaths: import_zod.z.array(import_zod.z.string()).optional(),
@@ -1007,9 +1039,9 @@ function matchesPattern(text, patterns) {
1007
1039
  const withoutDotSlash = text.replace(/^\.\//, "");
1008
1040
  return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
1009
1041
  }
1010
- function getNestedValue(obj, path50) {
1042
+ function getNestedValue(obj, path51) {
1011
1043
  if (!obj || typeof obj !== "object") return null;
1012
- const segments = path50.split(".");
1044
+ const segments = path51.split(".");
1013
1045
  for (const seg of segments) {
1014
1046
  if (FORBIDDEN_PATH_SEGMENTS.has(seg)) return null;
1015
1047
  }
@@ -1371,7 +1403,7 @@ function assertBuiltinShieldRegexesAreSafe() {
1371
1403
  }
1372
1404
  function computeArgsHash(args) {
1373
1405
  const str = JSON.stringify(args ?? "");
1374
- return import_crypto2.default.createHash("sha256").update(str).digest("hex").slice(0, 16);
1406
+ return import_crypto3.default.createHash("sha256").update(str).digest("hex").slice(0, 16);
1375
1407
  }
1376
1408
  function evaluateLoopWindow(records, tool, args, threshold, windowMs, now) {
1377
1409
  const hash = computeArgsHash(args);
@@ -1853,7 +1885,7 @@ function* stringValues(obj, depth = 0) {
1853
1885
  }
1854
1886
  for (const v of Object.values(obj)) yield* stringValues(v, depth + 1);
1855
1887
  }
1856
- var import_safe_regex2, import_mvdan_sh, import_picomatch, import_safe_regex22, import_safe_regex23, import_crypto2, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, FS_OP_CACHE_MAX, fsOpCache, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, SQL_DML_KEYWORDS, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, COST_PER_LOOP_ITER_USD, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC_RE, LONG_OUTPUT_THRESHOLD_BYTES, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, TERMINAL_ESCAPE_RE;
1888
+ var import_safe_regex2, import_mvdan_sh, import_picomatch, import_safe_regex22, import_safe_regex23, import_crypto3, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, FS_OP_CACHE_MAX, fsOpCache, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, SQL_DML_KEYWORDS, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, COST_PER_LOOP_ITER_USD, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC_RE, LONG_OUTPUT_THRESHOLD_BYTES, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, TERMINAL_ESCAPE_RE;
1857
1889
  var init_dist = __esm({
1858
1890
  "packages/policy-engine/dist/index.mjs"() {
1859
1891
  "use strict";
@@ -1862,7 +1894,7 @@ var init_dist = __esm({
1862
1894
  import_picomatch = __toESM(require("picomatch"), 1);
1863
1895
  import_safe_regex22 = __toESM(require("safe-regex2"), 1);
1864
1896
  import_safe_regex23 = __toESM(require("safe-regex2"), 1);
1865
- import_crypto2 = __toESM(require("crypto"), 1);
1897
+ import_crypto3 = __toESM(require("crypto"), 1);
1866
1898
  ASSIGNMENT_CONTEXT_RE = /\b(?:password|passwd|secret|token|api[_-]?key|auth(?:_key|_token)?|credential|private[_-]?key|access[_-]?key|client[_-]?secret)\s*[=:]\s*/i;
1867
1899
  DLP_STOPWORDS = [
1868
1900
  "example",
@@ -3487,7 +3519,7 @@ function readShieldsFile() {
3487
3519
  }
3488
3520
  function writeShieldsFile(data) {
3489
3521
  import_fs2.default.mkdirSync(import_path2.default.dirname(SHIELDS_STATE_FILE), { recursive: true });
3490
- const tmp = `${SHIELDS_STATE_FILE}.${import_crypto3.default.randomBytes(6).toString("hex")}.tmp`;
3522
+ const tmp = `${SHIELDS_STATE_FILE}.${import_crypto4.default.randomBytes(6).toString("hex")}.tmp`;
3491
3523
  const toWrite = { active: data.active };
3492
3524
  if (data.overrides && Object.keys(data.overrides).length > 0) toWrite.overrides = data.overrides;
3493
3525
  import_fs2.default.writeFileSync(tmp, JSON.stringify(toWrite, null, 2), { mode: 384 });
@@ -3577,18 +3609,18 @@ function installShield(name, shieldJson) {
3577
3609
  }
3578
3610
  import_fs2.default.mkdirSync(USER_SHIELDS_DIR, { recursive: true });
3579
3611
  const filePath = import_path2.default.join(USER_SHIELDS_DIR, `${name}.json`);
3580
- const tmp = `${filePath}.${import_crypto3.default.randomBytes(6).toString("hex")}.tmp`;
3612
+ const tmp = `${filePath}.${import_crypto4.default.randomBytes(6).toString("hex")}.tmp`;
3581
3613
  import_fs2.default.writeFileSync(tmp, JSON.stringify(shieldJson, null, 2), { mode: 384 });
3582
3614
  import_fs2.default.renameSync(tmp, filePath);
3583
3615
  }
3584
- var import_fs2, import_path2, import_os2, import_crypto3, USER_SHIELDS_DIR, SHIELDS, SHIELDS_STATE_FILE, RULE_KEY_MIGRATIONS;
3616
+ var import_fs2, import_path2, import_os2, import_crypto4, USER_SHIELDS_DIR, SHIELDS, SHIELDS_STATE_FILE, RULE_KEY_MIGRATIONS;
3585
3617
  var init_shields = __esm({
3586
3618
  "src/shields.ts"() {
3587
3619
  "use strict";
3588
3620
  import_fs2 = __toESM(require("fs"));
3589
3621
  import_path2 = __toESM(require("path"));
3590
3622
  import_os2 = __toESM(require("os"));
3591
- import_crypto3 = __toESM(require("crypto"));
3623
+ import_crypto4 = __toESM(require("crypto"));
3592
3624
  init_dist();
3593
3625
  init_dist();
3594
3626
  USER_SHIELDS_DIR = import_path2.default.join(import_os2.default.homedir(), ".node9", "shields");
@@ -3676,7 +3708,8 @@ function getConfig(cwd) {
3676
3708
  const projectConfig = tryLoadConfig(projectPath);
3677
3709
  const mergedSettings = {
3678
3710
  ...DEFAULT_CONFIG.settings,
3679
- approvers: { ...DEFAULT_CONFIG.settings.approvers }
3711
+ approvers: { ...DEFAULT_CONFIG.settings.approvers },
3712
+ shipper: { ...DEFAULT_CONFIG.settings.shipper }
3680
3713
  };
3681
3714
  const mergedPolicy = {
3682
3715
  sandboxPaths: [...DEFAULT_CONFIG.policy.sandboxPaths],
@@ -3707,6 +3740,7 @@ function getConfig(cwd) {
3707
3740
  if (s.enableHookLogDebug !== void 0)
3708
3741
  mergedSettings.enableHookLogDebug = s.enableHookLogDebug;
3709
3742
  if (s.approvers) mergedSettings.approvers = { ...mergedSettings.approvers, ...s.approvers };
3743
+ if (s.shipper) mergedSettings.shipper = { ...mergedSettings.shipper, ...s.shipper };
3710
3744
  if (s.approvalTimeoutMs !== void 0) mergedSettings.approvalTimeoutMs = s.approvalTimeoutMs;
3711
3745
  if (s.approvalTimeoutSeconds !== void 0 && s.approvalTimeoutMs === void 0)
3712
3746
  mergedSettings.approvalTimeoutMs = s.approvalTimeoutSeconds * 1e3;
@@ -3907,7 +3941,8 @@ var init_config = __esm({
3907
3941
  flightRecorder: true,
3908
3942
  auditHashArgs: true,
3909
3943
  approvers: { native: true, browser: false, cloud: false, terminal: true },
3910
- cloudSyncIntervalHours: 5
3944
+ cloudSyncIntervalHours: 5,
3945
+ shipper: { enabled: true, intervalSeconds: 20 }
3911
3946
  },
3912
3947
  policy: {
3913
3948
  sandboxPaths: ["/tmp/**", "**/sandbox/**", "**/test-results/**"],
@@ -5450,55 +5485,6 @@ function validateApiUrl(raw) {
5450
5485
  }
5451
5486
  return null;
5452
5487
  }
5453
- function auditLocalAllow(toolName, args, checkedBy, creds, meta, dlpInfo, containsSensitiveArgs = false, riskMetadata) {
5454
- const validated = validateApiUrl(creds.apiUrl);
5455
- if (!validated) {
5456
- try {
5457
- import_fs10.default.appendFileSync(
5458
- HOOK_DEBUG_LOG,
5459
- `[audit] refused to send: invalid apiUrl scheme/host (got "${String(creds.apiUrl).slice(0, 200)}")
5460
- `
5461
- );
5462
- } catch {
5463
- }
5464
- return Promise.resolve();
5465
- }
5466
- const safeArgs = containsSensitiveArgs ? { tool: toolName, redacted: true } : args;
5467
- const dlpSample = dlpInfo && typeof dlpInfo.redactedSample === "string" ? dlpInfo.redactedSample.slice(0, DLP_SAMPLE_MAX_LEN) : void 0;
5468
- const dlpPattern = dlpInfo && typeof dlpInfo.pattern === "string" ? dlpInfo.pattern.slice(0, DLP_PATTERN_MAX_LEN) : void 0;
5469
- const safeCheckedBy = KNOWN_CHECKED_BY.has(checkedBy) ? checkedBy : "unknown";
5470
- const cleanedRiskMetadata = riskMetadata ? Object.fromEntries(
5471
- Object.entries(riskMetadata).filter(([, v]) => typeof v === "string" && v.length > 0)
5472
- ) : void 0;
5473
- const hasRiskMetadata = cleanedRiskMetadata && Object.keys(cleanedRiskMetadata).length > 0;
5474
- return fetch(`${validated.toString().replace(/\/$/, "")}/audit`, {
5475
- method: "POST",
5476
- headers: { "Content-Type": "application/json", Authorization: `Bearer ${creds.apiKey}` },
5477
- body: JSON.stringify({
5478
- toolName,
5479
- args: safeArgs,
5480
- checkedBy: safeCheckedBy,
5481
- ...dlpInfo && { dlpPattern, dlpSample },
5482
- ...hasRiskMetadata && { riskMetadata: cleanedRiskMetadata },
5483
- // session_id (Claude Code + Gemini CLI) groups all audit rows from one
5484
- // agent run; transcript_path is the authoritative pointer to the
5485
- // session log (survives Gemini resume drift). Both optional —
5486
- // unsupported agents (MCP-mediated) leave them undefined.
5487
- ...meta?.sessionId && { runId: meta.sessionId },
5488
- ...meta?.transcriptPath && { transcriptPath: meta.transcriptPath },
5489
- context: {
5490
- agent: meta?.agent,
5491
- mcpServer: meta?.mcpServer,
5492
- hostname: import_os9.default.hostname(),
5493
- cwd: process.cwd(),
5494
- platform: import_os9.default.platform()
5495
- }
5496
- }),
5497
- signal: AbortSignal.timeout(5e3)
5498
- }).then(() => {
5499
- }).catch(() => {
5500
- });
5501
- }
5502
5488
  async function initNode9SaaS(toolName, args, creds, meta, riskMetadata, agentPolicy, forceReview) {
5503
5489
  const controller = new AbortController();
5504
5490
  const timeout = setTimeout(() => controller.abort(), 1e4);
@@ -5622,7 +5608,7 @@ async function resolveNode9SaaS(requestId, creds, approved, decidedBy) {
5622
5608
  );
5623
5609
  }
5624
5610
  }
5625
- var import_fs10, import_os9, import_path12, DLP_SAMPLE_MAX_LEN, DLP_PATTERN_MAX_LEN, KNOWN_CHECKED_BY;
5611
+ var import_fs10, import_os9, import_path12;
5626
5612
  var init_cloud = __esm({
5627
5613
  "src/auth/cloud.ts"() {
5628
5614
  "use strict";
@@ -5630,27 +5616,6 @@ var init_cloud = __esm({
5630
5616
  import_os9 = __toESM(require("os"));
5631
5617
  import_path12 = __toESM(require("path"));
5632
5618
  init_audit();
5633
- DLP_SAMPLE_MAX_LEN = 200;
5634
- DLP_PATTERN_MAX_LEN = 100;
5635
- KNOWN_CHECKED_BY = /* @__PURE__ */ new Set([
5636
- "dlp-block",
5637
- "observe-mode-dlp-would-block",
5638
- "dlp-review-flagged",
5639
- "loop-detected",
5640
- "audit-mode",
5641
- "local-policy",
5642
- "smart-rule-block",
5643
- // Smart-rule block was downgraded to review because the daemon was
5644
- // running and we're not in CI. The block attempt is still recorded;
5645
- // the user got a popup. Distinct from 'smart-rule-block' so the
5646
- // dashboard can show "block rule overridden" separately from a hard
5647
- // block that fired with no human in the loop.
5648
- "smart-rule-block-override",
5649
- "persistent",
5650
- "trust",
5651
- "observe-mode",
5652
- "observe-mode-would-block"
5653
- ]);
5654
5619
  }
5655
5620
  });
5656
5621
 
@@ -5737,7 +5702,7 @@ function notifyActivity(data) {
5737
5702
  }
5738
5703
  async function authorizeHeadless(toolName, args, meta, options) {
5739
5704
  if (!options?.calledFromDaemon) {
5740
- const actId = (0, import_crypto4.randomUUID)();
5705
+ const actId = (0, import_crypto5.randomUUID)();
5741
5706
  const actTs = Date.now();
5742
5707
  const stripAnsi2 = (s) => s.replace(/\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g, "");
5743
5708
  const sanitizedAgent = meta?.agent ? stripAnsi2(meta.agent).slice(0, 80) : void 0;
@@ -5832,17 +5797,11 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
5832
5797
  args,
5833
5798
  "deny",
5834
5799
  isObserveMode ? "observe-mode-dlp-would-block" : "dlp-block",
5835
- meta,
5836
- true
5837
- );
5838
- if (approvers.cloud && creds?.apiKey)
5839
- auditLocalAllow(
5840
- toolName,
5841
- args,
5842
- isObserveMode ? "observe-mode-dlp-would-block" : "dlp-block",
5843
- creds,
5844
- meta,
5845
- { pattern: dlpMatch.patternName, redactedSample: dlpMatch.redactedSample },
5800
+ {
5801
+ ...meta,
5802
+ dlpPattern: dlpMatch.patternName,
5803
+ dlpSample: dlpMatch.redactedSample
5804
+ },
5846
5805
  true
5847
5806
  );
5848
5807
  if (isWriteTool(toolName) && filePath) {
@@ -5898,9 +5857,6 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
5898
5857
  const policyResult = await evaluatePolicy2(toolName, args, meta?.agent, options?.cwd);
5899
5858
  if (policyResult.decision === "review") {
5900
5859
  appendLocalAudit(toolName, args, "allow", "audit-mode", meta, hashAuditArgs);
5901
- if (approvers.cloud && creds?.apiKey) {
5902
- await auditLocalAllow(toolName, args, "audit-mode", creds, meta);
5903
- }
5904
5860
  }
5905
5861
  }
5906
5862
  return { approved: true, checkedBy: "audit" };
@@ -5913,8 +5869,6 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
5913
5869
  const reason = `It looks like you've called "${toolName}" ${loopResult.count} times with identical arguments in the last ${ld.windowSeconds}s. Are you stuck? Step back and reconsider your approach \u2014 what are you actually trying to accomplish, and is there a different way to get there?`;
5914
5870
  if (!isManual)
5915
5871
  appendLocalAudit(toolName, args, "deny", "loop-detected", meta, hashAuditArgs);
5916
- if (approvers.cloud && creds?.apiKey)
5917
- auditLocalAllow(toolName, args, "loop-detected", creds, meta, void 0, true);
5918
5872
  return {
5919
5873
  approved: false,
5920
5874
  reason,
@@ -5933,15 +5887,15 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
5933
5887
  };
5934
5888
  }
5935
5889
  if (policyResult.decision === "allow") {
5936
- if (approvers.cloud && creds?.apiKey)
5937
- await auditLocalAllow(toolName, args, "local-policy", creds, meta, void 0, false, {
5938
- ruleName: policyResult.ruleName,
5939
- ruleDescription: policyResult.ruleDescription,
5940
- blockedByLabel: policyResult.blockedByLabel,
5941
- matchedField: policyResult.matchedField,
5942
- matchedWord: policyResult.matchedWord
5943
- });
5944
- if (!isManual) appendLocalAudit(toolName, args, "allow", "local-policy", meta, hashAuditArgs);
5890
+ if (!isManual)
5891
+ appendLocalAudit(
5892
+ toolName,
5893
+ args,
5894
+ "allow",
5895
+ "local-policy",
5896
+ { ...meta, ruleName: policyResult.ruleName },
5897
+ hashAuditArgs
5898
+ );
5945
5899
  return { approved: true, checkedBy: "local-policy" };
5946
5900
  }
5947
5901
  if (policyResult.decision === "block") {
@@ -5974,23 +5928,6 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
5974
5928
  { ...meta, ruleName: policyResult.ruleName },
5975
5929
  hashAuditArgs
5976
5930
  );
5977
- if (approvers.cloud && creds?.apiKey)
5978
- auditLocalAllow(
5979
- toolName,
5980
- args,
5981
- "smart-rule-block-override",
5982
- creds,
5983
- meta,
5984
- void 0,
5985
- false,
5986
- {
5987
- ruleName: policyResult.ruleName,
5988
- ruleDescription: policyResult.ruleDescription,
5989
- blockedByLabel: policyResult.blockedByLabel,
5990
- matchedField: policyResult.matchedField,
5991
- matchedWord: policyResult.matchedWord
5992
- }
5993
- );
5994
5931
  const baseLabel = policyResult.blockedByLabel || "Smart Rule";
5995
5932
  const OVERRIDE_PREFIX = "\u26A0\uFE0F Override block rule: ";
5996
5933
  if (!baseLabel.startsWith(OVERRIDE_PREFIX)) {
@@ -6015,14 +5952,6 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
6015
5952
  { ...meta, ruleName: policyResult.ruleName },
6016
5953
  hashAuditArgs
6017
5954
  );
6018
- if (approvers.cloud && creds?.apiKey)
6019
- auditLocalAllow(toolName, args, "smart-rule-block", creds, meta, void 0, false, {
6020
- ruleName: policyResult.ruleName,
6021
- ruleDescription: policyResult.ruleDescription,
6022
- blockedByLabel: policyResult.blockedByLabel,
6023
- matchedField: policyResult.matchedField,
6024
- matchedWord: policyResult.matchedWord
6025
- });
6026
5955
  return {
6027
5956
  approved: false,
6028
5957
  reason: policyResult.reason ?? "Action explicitly blocked by Smart Policy.",
@@ -6051,8 +5980,6 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
6051
5980
  if (policyRuleDescription) riskMetadata.ruleDescription = policyRuleDescription.slice(0, 200);
6052
5981
  const persistent = policyResult.ruleName ? null : getPersistentDecision(toolName);
6053
5982
  if (persistent === "allow") {
6054
- if (approvers.cloud && creds?.apiKey)
6055
- await auditLocalAllow(toolName, args, "persistent", creds, meta);
6056
5983
  if (!isManual) appendLocalAudit(toolName, args, "allow", "persistent", meta, hashAuditArgs);
6057
5984
  return { approved: true, checkedBy: "persistent" };
6058
5985
  }
@@ -6085,8 +6012,6 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
6085
6012
  }
6086
6013
  }
6087
6014
  if (!taintWarning && getActiveTrustSession(toolName, args)) {
6088
- if (approvers.cloud && creds?.apiKey)
6089
- await auditLocalAllow(toolName, args, "trust", creds, meta);
6090
6015
  if (!isManual) appendLocalAudit(toolName, args, "allow", "trust", meta, hashAuditArgs);
6091
6016
  return { approved: true, checkedBy: "trust" };
6092
6017
  }
@@ -6331,18 +6256,23 @@ REASON: Action blocked because no approval channels are available. (Native/Brows
6331
6256
  args,
6332
6257
  finalResult.approved ? "allow" : "deny",
6333
6258
  finalResult.checkedBy || finalResult.blockedBy || "unknown",
6334
- meta,
6259
+ // cloudRequestId links this row to the BE-origin AuditLog row the
6260
+ // /intercept handshake created — the shipper hands it to the SaaS so
6261
+ // the BE enriches that row instead of inserting a duplicate. Matters
6262
+ // for EVERY racer outcome, not just cloud wins: a native-popup
6263
+ // decision on a cloud-pending request would otherwise count twice.
6264
+ cloudRequestId ? { ...meta, cloudRequestId } : meta,
6335
6265
  hashAuditArgs
6336
6266
  );
6337
6267
  }
6338
6268
  const enrichedResult = !finalResult.approved && policyRuleDescription && !finalResult.ruleDescription ? { ...finalResult, ruleDescription: policyRuleDescription } : finalResult;
6339
6269
  return enrichedResult;
6340
6270
  }
6341
- var import_crypto4, WRITE_TOOLS;
6271
+ var import_crypto5, WRITE_TOOLS;
6342
6272
  var init_orchestrator = __esm({
6343
6273
  "src/auth/orchestrator.ts"() {
6344
6274
  "use strict";
6345
- import_crypto4 = require("crypto");
6275
+ import_crypto5 = require("crypto");
6346
6276
  init_native();
6347
6277
  init_context_sniper();
6348
6278
  init_dlp();
@@ -6412,10 +6342,10 @@ function hashToolDefinitions(tools) {
6412
6342
  return nameA.localeCompare(nameB);
6413
6343
  });
6414
6344
  const canonical = JSON.stringify(sorted);
6415
- return import_crypto5.default.createHash("sha256").update(canonical).digest("hex");
6345
+ return import_crypto6.default.createHash("sha256").update(canonical).digest("hex");
6416
6346
  }
6417
6347
  function getServerKey(upstreamCommand) {
6418
- return import_crypto5.default.createHash("sha256").update(upstreamCommand).digest("hex").slice(0, 16);
6348
+ return import_crypto6.default.createHash("sha256").update(upstreamCommand).digest("hex").slice(0, 16);
6419
6349
  }
6420
6350
  function readPinsFile(filePath) {
6421
6351
  try {
@@ -6446,7 +6376,7 @@ function readMcpPins() {
6446
6376
  }
6447
6377
  function writePinsFile(filePath, data) {
6448
6378
  import_fs12.default.mkdirSync(import_path14.default.dirname(filePath), { recursive: true });
6449
- const tmp = `${filePath}.${import_crypto5.default.randomBytes(6).toString("hex")}.tmp`;
6379
+ const tmp = `${filePath}.${import_crypto6.default.randomBytes(6).toString("hex")}.tmp`;
6450
6380
  const isHome = filePath === getHomePinsFilePath();
6451
6381
  import_fs12.default.writeFileSync(tmp, JSON.stringify(data, null, 2), isHome ? { mode: 384 } : {});
6452
6382
  import_fs12.default.renameSync(tmp, filePath);
@@ -6529,14 +6459,14 @@ function promotePin(serverKey, cwd) {
6529
6459
  writePinsFile(repoPath, repoPins);
6530
6460
  return { repoPath, created };
6531
6461
  }
6532
- var import_fs12, import_path14, import_os11, import_crypto5;
6462
+ var import_fs12, import_path14, import_os11, import_crypto6;
6533
6463
  var init_mcp_pin = __esm({
6534
6464
  "src/mcp-pin.ts"() {
6535
6465
  "use strict";
6536
6466
  import_fs12 = __toESM(require("fs"));
6537
6467
  import_path14 = __toESM(require("path"));
6538
6468
  import_os11 = __toESM(require("os"));
6539
- import_crypto5 = __toESM(require("crypto"));
6469
+ import_crypto6 = __toESM(require("crypto"));
6540
6470
  }
6541
6471
  });
6542
6472
 
@@ -13169,11 +13099,11 @@ function commonPathPrefix(paths) {
13169
13099
  const prefix = common.join("/").replace(/\/?$/, "/");
13170
13100
  return prefix.length > 1 ? prefix : null;
13171
13101
  }
13172
- var import_crypto6, SuggestionTracker;
13102
+ var import_crypto7, SuggestionTracker;
13173
13103
  var init_suggestion_tracker = __esm({
13174
13104
  "src/daemon/suggestion-tracker.ts"() {
13175
13105
  "use strict";
13176
- import_crypto6 = require("crypto");
13106
+ import_crypto7 = require("crypto");
13177
13107
  SuggestionTracker = class {
13178
13108
  events = /* @__PURE__ */ new Map();
13179
13109
  threshold;
@@ -13219,7 +13149,7 @@ var init_suggestion_tracker = __esm({
13219
13149
  }
13220
13150
  } : { type: "ignoredTool", toolName };
13221
13151
  return {
13222
- id: (0, import_crypto6.randomUUID)(),
13152
+ id: (0, import_crypto7.randomUUID)(),
13223
13153
  toolName,
13224
13154
  allowCount: events.length,
13225
13155
  suggestedRule,
@@ -13465,7 +13395,7 @@ function markRejectionHandlerRegistered() {
13465
13395
  function atomicWriteSync2(filePath, data, options) {
13466
13396
  const dir = import_path24.default.dirname(filePath);
13467
13397
  if (!import_fs22.default.existsSync(dir)) import_fs22.default.mkdirSync(dir, { recursive: true });
13468
- const tmpPath = `${filePath}.${(0, import_crypto7.randomUUID)()}.tmp`;
13398
+ const tmpPath = `${filePath}.${(0, import_crypto8.randomUUID)()}.tmp`;
13469
13399
  try {
13470
13400
  import_fs22.default.writeFileSync(tmpPath, data, options);
13471
13401
  } catch (err2) {
@@ -13640,7 +13570,7 @@ function broadcastForensic(finding) {
13640
13570
  const severity = CRITICAL_FORENSIC_CATEGORIES.has(finding.type) ? "critical" : "warning";
13641
13571
  const event = {
13642
13572
  type: "forensic",
13643
- id: `fnd_${(0, import_crypto7.randomUUID)()}`,
13573
+ id: `fnd_${(0, import_crypto8.randomUUID)()}`,
13644
13574
  ts: Date.now(),
13645
13575
  sessionId: finding.sessionId,
13646
13576
  category: finding.type,
@@ -13832,7 +13762,7 @@ function bindActivitySocket() {
13832
13762
  });
13833
13763
  activitySocketServer = unixServer;
13834
13764
  }
13835
- var import_net2, import_fs22, import_path24, import_os20, import_crypto7, homeDir, DAEMON_PID_FILE, DECISIONS_FILE, AUDIT_LOG_FILE, TRUST_FILE2, GLOBAL_CONFIG_FILE, CREDENTIALS_FILE, INSIGHT_COUNTS_FILE, pending, sseClients, suggestionTracker, 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, CRITICAL_FORENSIC_CATEGORIES, WRITE_TOOL_NAMES, ACTIVITY_REBIND_MAX_ATTEMPTS, ACTIVITY_REBIND_WINDOW_MS, ACTIVITY_HEALTH_PROBE_MS, activitySocketServer, activityHealthInterval, activityRebindAttempts, activityCircuitTripped;
13765
+ var import_net2, import_fs22, import_path24, import_os20, import_crypto8, homeDir, DAEMON_PID_FILE, DECISIONS_FILE, AUDIT_LOG_FILE, TRUST_FILE2, GLOBAL_CONFIG_FILE, CREDENTIALS_FILE, INSIGHT_COUNTS_FILE, pending, sseClients, suggestionTracker, 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, CRITICAL_FORENSIC_CATEGORIES, WRITE_TOOL_NAMES, ACTIVITY_REBIND_MAX_ATTEMPTS, ACTIVITY_REBIND_WINDOW_MS, ACTIVITY_HEALTH_PROBE_MS, activitySocketServer, activityHealthInterval, activityRebindAttempts, activityCircuitTripped;
13836
13766
  var init_state2 = __esm({
13837
13767
  "src/daemon/state.ts"() {
13838
13768
  "use strict";
@@ -13840,7 +13770,7 @@ var init_state2 = __esm({
13840
13770
  import_fs22 = __toESM(require("fs"));
13841
13771
  import_path24 = __toESM(require("path"));
13842
13772
  import_os20 = __toESM(require("os"));
13843
- import_crypto7 = require("crypto");
13773
+ import_crypto8 = require("crypto");
13844
13774
  init_daemon();
13845
13775
  init_suggestion_tracker();
13846
13776
  init_taint_store();
@@ -14282,71 +14212,282 @@ var init_sync = __esm({
14282
14212
  }
14283
14213
  });
14284
14214
 
14215
+ // src/daemon/audit-shipper.ts
14216
+ var audit_shipper_exports = {};
14217
+ __export(audit_shipper_exports, {
14218
+ AUDIT_SHIP_WATERMARK: () => AUDIT_SHIP_WATERMARK,
14219
+ buildWireRows: () => buildWireRows,
14220
+ fileSignature: () => fileSignature,
14221
+ readWatermark: () => readWatermark,
14222
+ shipLagBytes: () => shipLagBytes,
14223
+ shipOnce: () => shipOnce,
14224
+ startAuditShipper: () => startAuditShipper,
14225
+ writeWatermark: () => writeWatermark
14226
+ });
14227
+ function fileSignature(filePath) {
14228
+ const fd = import_fs24.default.openSync(filePath, "r");
14229
+ try {
14230
+ const buf = Buffer.alloc(512);
14231
+ const read = import_fs24.default.readSync(fd, buf, 0, 512, 0);
14232
+ const slice = buf.subarray(0, read);
14233
+ const nl = slice.indexOf(10);
14234
+ const firstLine = nl === -1 ? slice : slice.subarray(0, nl);
14235
+ return import_crypto9.default.createHash("sha256").update(firstLine).digest("hex").slice(0, 16);
14236
+ } finally {
14237
+ import_fs24.default.closeSync(fd);
14238
+ }
14239
+ }
14240
+ function readWatermark(watermarkPath) {
14241
+ try {
14242
+ const raw = JSON.parse(import_fs24.default.readFileSync(watermarkPath, "utf-8"));
14243
+ if (typeof raw.fileSig === "string" && typeof raw.offset === "number" && raw.offset >= 0)
14244
+ return raw;
14245
+ } catch {
14246
+ }
14247
+ return null;
14248
+ }
14249
+ function writeWatermark(watermarkPath, wm) {
14250
+ const tmp = `${watermarkPath}.tmp`;
14251
+ import_fs24.default.writeFileSync(tmp, JSON.stringify(wm));
14252
+ import_fs24.default.renameSync(tmp, watermarkPath);
14253
+ }
14254
+ function buildWireRows(chunk) {
14255
+ const lastNl = chunk.lastIndexOf(10);
14256
+ if (lastNl === -1) return { rows: [], consumed: 0 };
14257
+ const complete = chunk.subarray(0, lastNl + 1);
14258
+ const rows = [];
14259
+ for (const line of complete.toString("utf-8").split("\n")) {
14260
+ if (!line.trim()) continue;
14261
+ let parsed;
14262
+ try {
14263
+ parsed = JSON.parse(line);
14264
+ } catch {
14265
+ continue;
14266
+ }
14267
+ if (typeof parsed.eid !== "string" || parsed.eid.length < 8) continue;
14268
+ if (typeof parsed.tool !== "string" || !parsed.tool) continue;
14269
+ if (parsed.decision !== "allow" && parsed.decision !== "deny") continue;
14270
+ if (typeof parsed.ts !== "string") continue;
14271
+ if (parsed.testRun === true) continue;
14272
+ const checkedBy = typeof parsed.checkedBy === "string" ? parsed.checkedBy : void 0;
14273
+ if (checkedBy && SKIP_CHECKED_BY.has(checkedBy)) continue;
14274
+ const cloudRequestId = typeof parsed.cloudRequestId === "string" ? parsed.cloudRequestId : void 0;
14275
+ if (checkedBy === "cloud" && !cloudRequestId) continue;
14276
+ rows.push({
14277
+ eid: parsed.eid,
14278
+ ts: parsed.ts,
14279
+ tool: parsed.tool,
14280
+ ...parsed.args && typeof parsed.args === "object" ? { args: parsed.args } : {},
14281
+ ...typeof parsed.argsHash === "string" ? { argsHash: parsed.argsHash } : {},
14282
+ ...typeof parsed.argsPreview === "string" ? { argsPreview: parsed.argsPreview } : {},
14283
+ decision: parsed.decision,
14284
+ ...checkedBy ? { checkedBy } : {},
14285
+ ...typeof parsed.ruleName === "string" ? { ruleName: parsed.ruleName } : {},
14286
+ ...typeof parsed.agent === "string" ? { agent: parsed.agent } : {},
14287
+ ...typeof parsed.mcpServer === "string" ? { mcpServer: parsed.mcpServer } : {},
14288
+ ...typeof parsed.sessionId === "string" ? { sessionId: parsed.sessionId } : {},
14289
+ ...typeof parsed.dlpPattern === "string" ? { dlpPattern: parsed.dlpPattern } : {},
14290
+ ...typeof parsed.dlpSample === "string" ? { dlpSample: parsed.dlpSample } : {},
14291
+ ...cloudRequestId ? { cloudRequestId } : {}
14292
+ });
14293
+ }
14294
+ return { rows, consumed: lastNl + 1 };
14295
+ }
14296
+ async function shipOnce(deps = {}) {
14297
+ const auditLogPath = deps.auditLogPath ?? LOCAL_AUDIT_LOG;
14298
+ const watermarkPath = deps.watermarkPath ?? AUDIT_SHIP_WATERMARK;
14299
+ const fetchImpl = deps.fetchImpl ?? fetch;
14300
+ let cloudEnabled = deps.cloudEnabled;
14301
+ if (cloudEnabled === void 0) {
14302
+ try {
14303
+ const settings = getConfig().settings;
14304
+ cloudEnabled = settings.shipper.enabled !== false && settings.approvers.cloud;
14305
+ } catch {
14306
+ cloudEnabled = false;
14307
+ }
14308
+ }
14309
+ if (!cloudEnabled) return { status: "disabled", shipped: 0 };
14310
+ const creds = deps.creds !== void 0 ? deps.creds : readCredentials();
14311
+ if (!creds?.apiKey) return { status: "no-creds", shipped: 0 };
14312
+ const validated = validateApiUrl(creds.apiUrl);
14313
+ if (!validated) return { status: "no-creds", shipped: 0 };
14314
+ const endpoint = `${validated.toString().replace(/\/$/, "")}/audit/batch`;
14315
+ if (!import_fs24.default.existsSync(auditLogPath)) return { status: "idle", shipped: 0 };
14316
+ let shipped = 0;
14317
+ try {
14318
+ for (let chunkN = 0; chunkN < MAX_CHUNKS_PER_TICK; chunkN++) {
14319
+ const size = import_fs24.default.statSync(auditLogPath).size;
14320
+ if (size === 0) break;
14321
+ const sig = fileSignature(auditLogPath);
14322
+ const wm = readWatermark(watermarkPath);
14323
+ const offset = wm && wm.fileSig === sig && wm.offset <= size ? wm.offset : 0;
14324
+ if (offset >= size) break;
14325
+ const toRead = Math.min(size - offset, MAX_CHUNK_BYTES);
14326
+ const buf = Buffer.alloc(toRead);
14327
+ const fd = import_fs24.default.openSync(auditLogPath, "r");
14328
+ let read;
14329
+ try {
14330
+ read = import_fs24.default.readSync(fd, buf, 0, toRead, offset);
14331
+ } finally {
14332
+ import_fs24.default.closeSync(fd);
14333
+ }
14334
+ const { rows, consumed } = buildWireRows(buf.subarray(0, read));
14335
+ if (consumed === 0) break;
14336
+ for (let i = 0; i < rows.length; i += MAX_BATCH) {
14337
+ const batch = rows.slice(i, i + MAX_BATCH);
14338
+ const controller = new AbortController();
14339
+ const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
14340
+ try {
14341
+ const res = await fetchImpl(endpoint, {
14342
+ method: "POST",
14343
+ headers: {
14344
+ "Content-Type": "application/json",
14345
+ Authorization: `Bearer ${creds.apiKey}`
14346
+ },
14347
+ body: JSON.stringify({ rows: batch }),
14348
+ signal: controller.signal
14349
+ });
14350
+ if (!res.ok) throw new Error(`audit/batch HTTP ${res.status}`);
14351
+ } finally {
14352
+ clearTimeout(timer);
14353
+ }
14354
+ shipped += batch.length;
14355
+ }
14356
+ writeWatermark(watermarkPath, {
14357
+ fileSig: sig,
14358
+ offset: offset + consumed,
14359
+ lastEid: rows.length > 0 ? rows[rows.length - 1].eid : wm?.lastEid,
14360
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
14361
+ });
14362
+ if (consumed < toRead) break;
14363
+ }
14364
+ } catch (err2) {
14365
+ try {
14366
+ appendToLog(HOOK_DEBUG_LOG, {
14367
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
14368
+ shipper: "error",
14369
+ message: err2.message
14370
+ });
14371
+ } catch {
14372
+ }
14373
+ return { status: "error", shipped };
14374
+ }
14375
+ return { status: shipped > 0 ? "shipped" : "idle", shipped };
14376
+ }
14377
+ function shipLagBytes(auditLogPath = LOCAL_AUDIT_LOG, watermarkPath = AUDIT_SHIP_WATERMARK) {
14378
+ try {
14379
+ if (!import_fs24.default.existsSync(auditLogPath)) return 0;
14380
+ const size = import_fs24.default.statSync(auditLogPath).size;
14381
+ const wm = readWatermark(watermarkPath);
14382
+ if (!wm) return size;
14383
+ if (wm.fileSig !== fileSignature(auditLogPath)) return size;
14384
+ return Math.max(0, size - wm.offset);
14385
+ } catch {
14386
+ return null;
14387
+ }
14388
+ }
14389
+ function startAuditShipper() {
14390
+ if (shipperStarted) return;
14391
+ shipperStarted = true;
14392
+ const intervalMs = (() => {
14393
+ try {
14394
+ const sec = getConfig().settings.shipper.intervalSeconds;
14395
+ return sec >= 5 ? sec * 1e3 : DEFAULT_INTERVAL_MS;
14396
+ } catch {
14397
+ return DEFAULT_INTERVAL_MS;
14398
+ }
14399
+ })();
14400
+ setTimeout(() => void shipOnce(), 3e3);
14401
+ setInterval(() => void shipOnce(), intervalMs);
14402
+ }
14403
+ var import_fs24, import_path26, import_os22, import_crypto9, AUDIT_SHIP_WATERMARK, DEFAULT_INTERVAL_MS, MAX_BATCH, MAX_CHUNK_BYTES, MAX_CHUNKS_PER_TICK, FETCH_TIMEOUT_MS, SKIP_CHECKED_BY, shipperStarted;
14404
+ var init_audit_shipper = __esm({
14405
+ "src/daemon/audit-shipper.ts"() {
14406
+ "use strict";
14407
+ import_fs24 = __toESM(require("fs"));
14408
+ import_path26 = __toESM(require("path"));
14409
+ import_os22 = __toESM(require("os"));
14410
+ import_crypto9 = __toESM(require("crypto"));
14411
+ init_audit();
14412
+ init_config();
14413
+ init_sync();
14414
+ init_cloud();
14415
+ AUDIT_SHIP_WATERMARK = import_path26.default.join(import_os22.default.homedir(), ".node9", "audit-ship.json");
14416
+ DEFAULT_INTERVAL_MS = 2e4;
14417
+ MAX_BATCH = 500;
14418
+ MAX_CHUNK_BYTES = 4 * 1024 * 1024;
14419
+ MAX_CHUNKS_PER_TICK = 10;
14420
+ FETCH_TIMEOUT_MS = 1e4;
14421
+ SKIP_CHECKED_BY = /* @__PURE__ */ new Set(["ignored"]);
14422
+ shipperStarted = false;
14423
+ }
14424
+ });
14425
+
14285
14426
  // src/daemon/dlp-scanner.ts
14286
14427
  function loadIndex() {
14287
14428
  try {
14288
- return JSON.parse(import_fs24.default.readFileSync(INDEX_FILE, "utf-8"));
14429
+ return JSON.parse(import_fs25.default.readFileSync(INDEX_FILE, "utf-8"));
14289
14430
  } catch {
14290
14431
  return {};
14291
14432
  }
14292
14433
  }
14293
14434
  function saveIndex(index) {
14294
14435
  try {
14295
- import_fs24.default.writeFileSync(INDEX_FILE, JSON.stringify(index), { encoding: "utf-8", mode: 384 });
14436
+ import_fs25.default.writeFileSync(INDEX_FILE, JSON.stringify(index), { encoding: "utf-8", mode: 384 });
14296
14437
  } catch {
14297
14438
  }
14298
14439
  }
14299
14440
  function appendAuditEntry(entry) {
14300
14441
  try {
14301
- import_fs24.default.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
14442
+ import_fs25.default.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
14302
14443
  } catch {
14303
14444
  }
14304
14445
  }
14305
14446
  function runDlpScan() {
14306
- if (!import_fs24.default.existsSync(PROJECTS_DIR2)) return;
14447
+ if (!import_fs25.default.existsSync(PROJECTS_DIR2)) return;
14307
14448
  const index = loadIndex();
14308
14449
  let updated = false;
14309
14450
  let projDirs;
14310
14451
  try {
14311
- projDirs = import_fs24.default.readdirSync(PROJECTS_DIR2);
14452
+ projDirs = import_fs25.default.readdirSync(PROJECTS_DIR2);
14312
14453
  } catch {
14313
14454
  return;
14314
14455
  }
14315
14456
  for (const proj of projDirs) {
14316
- const projPath = import_path26.default.join(PROJECTS_DIR2, proj);
14457
+ const projPath = import_path27.default.join(PROJECTS_DIR2, proj);
14317
14458
  try {
14318
- if (!import_fs24.default.lstatSync(projPath).isDirectory()) continue;
14319
- const real = import_fs24.default.realpathSync(projPath);
14320
- if (!real.startsWith(PROJECTS_DIR2 + import_path26.default.sep) && real !== PROJECTS_DIR2) continue;
14459
+ if (!import_fs25.default.lstatSync(projPath).isDirectory()) continue;
14460
+ const real = import_fs25.default.realpathSync(projPath);
14461
+ if (!real.startsWith(PROJECTS_DIR2 + import_path27.default.sep) && real !== PROJECTS_DIR2) continue;
14321
14462
  } catch {
14322
14463
  continue;
14323
14464
  }
14324
14465
  let files;
14325
14466
  try {
14326
- files = import_fs24.default.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
14467
+ files = import_fs25.default.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
14327
14468
  } catch {
14328
14469
  continue;
14329
14470
  }
14330
14471
  for (const file of files) {
14331
- const filePath = import_path26.default.join(projPath, file);
14472
+ const filePath = import_path27.default.join(projPath, file);
14332
14473
  const lastOffset = index[filePath] ?? 0;
14333
14474
  let size;
14334
14475
  try {
14335
- size = import_fs24.default.statSync(filePath).size;
14476
+ size = import_fs25.default.statSync(filePath).size;
14336
14477
  } catch {
14337
14478
  continue;
14338
14479
  }
14339
14480
  if (size <= lastOffset) continue;
14340
14481
  let fd;
14341
14482
  try {
14342
- fd = import_fs24.default.openSync(filePath, "r");
14483
+ fd = import_fs25.default.openSync(filePath, "r");
14343
14484
  } catch {
14344
14485
  continue;
14345
14486
  }
14346
14487
  try {
14347
14488
  const chunkSize = size - lastOffset;
14348
14489
  const buf = Buffer.alloc(chunkSize);
14349
- import_fs24.default.readSync(fd, buf, 0, chunkSize, lastOffset);
14490
+ import_fs25.default.readSync(fd, buf, 0, chunkSize, lastOffset);
14350
14491
  const chunk = buf.toString("utf-8");
14351
14492
  for (const line of chunk.split("\n")) {
14352
14493
  if (!line.trim()) continue;
@@ -14366,7 +14507,7 @@ function runDlpScan() {
14366
14507
  if (typeof text !== "string") continue;
14367
14508
  const match = scanText(text);
14368
14509
  if (!match) continue;
14369
- const projLabel = decodeURIComponent(proj).replace(import_os22.default.homedir(), "~").slice(0, 40);
14510
+ const projLabel = decodeURIComponent(proj).replace(import_os23.default.homedir(), "~").slice(0, 40);
14370
14511
  const ts = entry.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
14371
14512
  appendAuditEntry({
14372
14513
  ts,
@@ -14391,7 +14532,7 @@ Run: node9 report --period 30d`
14391
14532
  updated = true;
14392
14533
  } finally {
14393
14534
  try {
14394
- import_fs24.default.closeSync(fd);
14535
+ import_fs25.default.closeSync(fd);
14395
14536
  } catch {
14396
14537
  }
14397
14538
  }
@@ -14417,30 +14558,30 @@ function startDlpScanner() {
14417
14558
  );
14418
14559
  timer.unref();
14419
14560
  }
14420
- var import_fs24, import_path26, import_os22, INDEX_FILE, PROJECTS_DIR2;
14561
+ var import_fs25, import_path27, import_os23, INDEX_FILE, PROJECTS_DIR2;
14421
14562
  var init_dlp_scanner = __esm({
14422
14563
  "src/daemon/dlp-scanner.ts"() {
14423
14564
  "use strict";
14424
- import_fs24 = __toESM(require("fs"));
14425
- import_path26 = __toESM(require("path"));
14426
- import_os22 = __toESM(require("os"));
14565
+ import_fs25 = __toESM(require("fs"));
14566
+ import_path27 = __toESM(require("path"));
14567
+ import_os23 = __toESM(require("os"));
14427
14568
  init_dlp();
14428
14569
  init_native();
14429
14570
  init_state2();
14430
- INDEX_FILE = import_path26.default.join(import_os22.default.homedir(), ".node9", "dlp-index.json");
14431
- PROJECTS_DIR2 = import_path26.default.join(import_os22.default.homedir(), ".claude", "projects");
14571
+ INDEX_FILE = import_path27.default.join(import_os23.default.homedir(), ".node9", "dlp-index.json");
14572
+ PROJECTS_DIR2 = import_path27.default.join(import_os23.default.homedir(), ".claude", "projects");
14432
14573
  }
14433
14574
  });
14434
14575
 
14435
14576
  // src/daemon/mcp-tools.ts
14436
14577
  function getMcpToolsFile() {
14437
- return import_path27.default.join(import_os23.default.homedir(), ".node9", "mcp-tools.json");
14578
+ return import_path28.default.join(import_os24.default.homedir(), ".node9", "mcp-tools.json");
14438
14579
  }
14439
14580
  function readMcpToolsConfig() {
14440
14581
  try {
14441
14582
  const file = getMcpToolsFile();
14442
- if (!import_fs25.default.existsSync(file)) return {};
14443
- const raw = import_fs25.default.readFileSync(file, "utf-8");
14583
+ if (!import_fs26.default.existsSync(file)) return {};
14584
+ const raw = import_fs26.default.readFileSync(file, "utf-8");
14444
14585
  return JSON.parse(raw);
14445
14586
  } catch {
14446
14587
  return {};
@@ -14449,11 +14590,11 @@ function readMcpToolsConfig() {
14449
14590
  function writeMcpToolsConfig(config) {
14450
14591
  try {
14451
14592
  const file = getMcpToolsFile();
14452
- const dir = import_path27.default.dirname(file);
14453
- if (!import_fs25.default.existsSync(dir)) import_fs25.default.mkdirSync(dir, { recursive: true });
14454
- const tmpPath = `${file}.${import_os23.default.hostname()}.${process.pid}.tmp`;
14455
- import_fs25.default.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
14456
- import_fs25.default.renameSync(tmpPath, file);
14593
+ const dir = import_path28.default.dirname(file);
14594
+ if (!import_fs26.default.existsSync(dir)) import_fs26.default.mkdirSync(dir, { recursive: true });
14595
+ const tmpPath = `${file}.${import_os24.default.hostname()}.${process.pid}.tmp`;
14596
+ import_fs26.default.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
14597
+ import_fs26.default.renameSync(tmpPath, file);
14457
14598
  } catch (e) {
14458
14599
  console.error("Failed to write mcp-tools.json", e);
14459
14600
  }
@@ -14492,13 +14633,13 @@ function approveServer(serverKey, disabledTools) {
14492
14633
  writeMcpToolsConfig(config);
14493
14634
  }
14494
14635
  }
14495
- var import_fs25, import_path27, import_os23;
14636
+ var import_fs26, import_path28, import_os24;
14496
14637
  var init_mcp_tools = __esm({
14497
14638
  "src/daemon/mcp-tools.ts"() {
14498
14639
  "use strict";
14499
- import_fs25 = __toESM(require("fs"));
14500
- import_path27 = __toESM(require("path"));
14501
- import_os23 = __toESM(require("os"));
14640
+ import_fs26 = __toESM(require("fs"));
14641
+ import_path28 = __toESM(require("path"));
14642
+ import_os24 = __toESM(require("os"));
14502
14643
  }
14503
14644
  });
14504
14645
 
@@ -14507,9 +14648,10 @@ function startDaemon() {
14507
14648
  startCostSync();
14508
14649
  startCloudSync();
14509
14650
  startForensicBroadcast();
14651
+ startAuditShipper();
14510
14652
  startDlpScanner();
14511
14653
  loadInsightCounts();
14512
- const internalToken = (0, import_crypto8.randomUUID)();
14654
+ const internalToken = (0, import_crypto10.randomUUID)();
14513
14655
  const validToken = (req) => req.headers["x-node9-internal"] === internalToken || req.headers["x-node9-token"] === internalToken;
14514
14656
  const IDLE_TIMEOUT_MS = 12 * 60 * 60 * 1e3;
14515
14657
  const watchMode = process.env.NODE9_WATCH_MODE === "1";
@@ -14520,7 +14662,7 @@ function startDaemon() {
14520
14662
  idleTimer = setTimeout(() => {
14521
14663
  if (autoStarted) {
14522
14664
  try {
14523
- import_fs26.default.unlinkSync(DAEMON_PID_FILE);
14665
+ import_fs27.default.unlinkSync(DAEMON_PID_FILE);
14524
14666
  } catch {
14525
14667
  }
14526
14668
  }
@@ -14621,7 +14763,7 @@ data: ${JSON.stringify(item.data)}
14621
14763
  cwd,
14622
14764
  localSmartRuleMatched = false
14623
14765
  } = JSON.parse(body);
14624
- const id = fromCLI && typeof activityId === "string" && activityId || (0, import_crypto8.randomUUID)();
14766
+ const id = fromCLI && typeof activityId === "string" && activityId || (0, import_crypto10.randomUUID)();
14625
14767
  const entry = {
14626
14768
  id,
14627
14769
  toolName,
@@ -14665,7 +14807,7 @@ data: ${JSON.stringify(item.data)}
14665
14807
  mcpServer: entry.mcpServer
14666
14808
  });
14667
14809
  }
14668
- const projectCwd = typeof cwd === "string" && import_path28.default.isAbsolute(cwd) ? cwd : void 0;
14810
+ const projectCwd = typeof cwd === "string" && import_path29.default.isAbsolute(cwd) ? cwd : void 0;
14669
14811
  const projectConfig = getConfig(projectCwd);
14670
14812
  const browserEnabled = projectConfig.settings.approvers?.browser !== false;
14671
14813
  const terminalEnabled = projectConfig.settings.approvers?.terminal !== false;
@@ -14957,8 +15099,8 @@ data: ${JSON.stringify(item.data)}
14957
15099
  if (!validToken(req)) return res.writeHead(403).end();
14958
15100
  const periodParam = reqUrl.searchParams.get("period") || "7d";
14959
15101
  const period = ["today", "7d", "30d", "month"].includes(periodParam) ? periodParam : "7d";
14960
- const logPath = import_path28.default.join(import_os24.default.homedir(), ".node9", "audit.log");
14961
- if (!import_fs26.default.existsSync(logPath)) {
15102
+ const logPath = import_path29.default.join(import_os25.default.homedir(), ".node9", "audit.log");
15103
+ if (!import_fs27.default.existsSync(logPath)) {
14962
15104
  res.writeHead(200, { "Content-Type": "application/json" });
14963
15105
  return res.end(
14964
15106
  JSON.stringify({
@@ -14971,7 +15113,7 @@ data: ${JSON.stringify(item.data)}
14971
15113
  );
14972
15114
  }
14973
15115
  try {
14974
- const raw = import_fs26.default.readFileSync(logPath, "utf-8");
15116
+ const raw = import_fs27.default.readFileSync(logPath, "utf-8");
14975
15117
  const allEntries = raw.split("\n").flatMap((line) => {
14976
15118
  if (!line.trim()) return [];
14977
15119
  try {
@@ -15206,7 +15348,7 @@ data: ${JSON.stringify(item.data)}
15206
15348
  args: { toolCount: tools.length, status },
15207
15349
  decision: "mcp-discovered"
15208
15350
  });
15209
- const id = (0, import_crypto8.randomUUID)();
15351
+ const id = (0, import_crypto10.randomUUID)();
15210
15352
  const entry = {
15211
15353
  id,
15212
15354
  type: "mcp-discovery",
@@ -15294,14 +15436,14 @@ data: ${JSON.stringify(item.data)}
15294
15436
  server.on("error", (e) => {
15295
15437
  if (e.code === "EADDRINUSE") {
15296
15438
  try {
15297
- if (import_fs26.default.existsSync(DAEMON_PID_FILE)) {
15298
- const { pid } = JSON.parse(import_fs26.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
15439
+ if (import_fs27.default.existsSync(DAEMON_PID_FILE)) {
15440
+ const { pid } = JSON.parse(import_fs27.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
15299
15441
  process.kill(pid, 0);
15300
15442
  return process.exit(0);
15301
15443
  }
15302
15444
  } catch {
15303
15445
  try {
15304
- import_fs26.default.unlinkSync(DAEMON_PID_FILE);
15446
+ import_fs27.default.unlinkSync(DAEMON_PID_FILE);
15305
15447
  } catch {
15306
15448
  }
15307
15449
  server.listen(DAEMON_PORT, DAEMON_HOST);
@@ -15373,15 +15515,15 @@ data: ${JSON.stringify(item.data)}
15373
15515
  }
15374
15516
  startActivitySocket();
15375
15517
  }
15376
- var import_http, import_fs26, import_path28, import_os24, import_crypto8, import_child_process2, import_chalk6;
15518
+ var import_http, import_fs27, import_path29, import_os25, import_crypto10, import_child_process2, import_chalk6;
15377
15519
  var init_server = __esm({
15378
15520
  "src/daemon/server.ts"() {
15379
15521
  "use strict";
15380
15522
  import_http = __toESM(require("http"));
15381
- import_fs26 = __toESM(require("fs"));
15382
- import_path28 = __toESM(require("path"));
15383
- import_os24 = __toESM(require("os"));
15384
- import_crypto8 = require("crypto");
15523
+ import_fs27 = __toESM(require("fs"));
15524
+ import_path29 = __toESM(require("path"));
15525
+ import_os25 = __toESM(require("os"));
15526
+ import_crypto10 = require("crypto");
15385
15527
  import_child_process2 = require("child_process");
15386
15528
  import_chalk6 = __toESM(require("chalk"));
15387
15529
  init_core();
@@ -15391,6 +15533,7 @@ var init_server = __esm({
15391
15533
  init_state();
15392
15534
  init_costSync();
15393
15535
  init_sync();
15536
+ init_audit_shipper();
15394
15537
  init_dlp_scanner();
15395
15538
  init_mcp_tools();
15396
15539
  }
@@ -15400,8 +15543,8 @@ var init_server = __esm({
15400
15543
  function resolveNode9Binary() {
15401
15544
  try {
15402
15545
  const script = process.argv[1];
15403
- if (typeof script === "string" && import_path29.default.isAbsolute(script) && import_fs27.default.existsSync(script)) {
15404
- return import_fs27.default.realpathSync(script);
15546
+ if (typeof script === "string" && import_path30.default.isAbsolute(script) && import_fs28.default.existsSync(script)) {
15547
+ return import_fs28.default.realpathSync(script);
15405
15548
  }
15406
15549
  } catch {
15407
15550
  }
@@ -15419,11 +15562,11 @@ function xmlEscape(s) {
15419
15562
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
15420
15563
  }
15421
15564
  function launchdPlist(binaryPath) {
15422
- const logDir = import_path29.default.join(import_os25.default.homedir(), ".node9");
15565
+ const logDir = import_path30.default.join(import_os26.default.homedir(), ".node9");
15423
15566
  const nodePath = xmlEscape(process.execPath);
15424
15567
  const scriptPath = xmlEscape(binaryPath);
15425
- const outLog = xmlEscape(import_path29.default.join(logDir, "daemon.log"));
15426
- const errLog = xmlEscape(import_path29.default.join(logDir, "daemon-error.log"));
15568
+ const outLog = xmlEscape(import_path30.default.join(logDir, "daemon.log"));
15569
+ const errLog = xmlEscape(import_path30.default.join(logDir, "daemon-error.log"));
15427
15570
  return `<?xml version="1.0" encoding="UTF-8"?>
15428
15571
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
15429
15572
  <plist version="1.0">
@@ -15456,9 +15599,9 @@ function launchdPlist(binaryPath) {
15456
15599
  `;
15457
15600
  }
15458
15601
  function installLaunchd(binaryPath) {
15459
- const dir = import_path29.default.dirname(LAUNCHD_PLIST);
15460
- if (!import_fs27.default.existsSync(dir)) import_fs27.default.mkdirSync(dir, { recursive: true });
15461
- import_fs27.default.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
15602
+ const dir = import_path30.default.dirname(LAUNCHD_PLIST);
15603
+ if (!import_fs28.default.existsSync(dir)) import_fs28.default.mkdirSync(dir, { recursive: true });
15604
+ import_fs28.default.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
15462
15605
  (0, import_child_process3.spawnSync)("launchctl", ["unload", LAUNCHD_PLIST], { encoding: "utf8" });
15463
15606
  const r = (0, import_child_process3.spawnSync)("launchctl", ["load", "-w", LAUNCHD_PLIST], {
15464
15607
  encoding: "utf8",
@@ -15469,13 +15612,13 @@ function installLaunchd(binaryPath) {
15469
15612
  }
15470
15613
  }
15471
15614
  function uninstallLaunchd() {
15472
- if (import_fs27.default.existsSync(LAUNCHD_PLIST)) {
15615
+ if (import_fs28.default.existsSync(LAUNCHD_PLIST)) {
15473
15616
  (0, import_child_process3.spawnSync)("launchctl", ["unload", "-w", LAUNCHD_PLIST], { encoding: "utf8", timeout: 5e3 });
15474
- import_fs27.default.unlinkSync(LAUNCHD_PLIST);
15617
+ import_fs28.default.unlinkSync(LAUNCHD_PLIST);
15475
15618
  }
15476
15619
  }
15477
15620
  function isLaunchdInstalled() {
15478
- return import_fs27.default.existsSync(LAUNCHD_PLIST);
15621
+ return import_fs28.default.existsSync(LAUNCHD_PLIST);
15479
15622
  }
15480
15623
  function systemdUnit(binaryPath) {
15481
15624
  return `[Unit]
@@ -15494,12 +15637,12 @@ WantedBy=default.target
15494
15637
  `;
15495
15638
  }
15496
15639
  function installSystemd(binaryPath) {
15497
- if (!import_fs27.default.existsSync(SYSTEMD_UNIT_DIR)) {
15498
- import_fs27.default.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
15640
+ if (!import_fs28.default.existsSync(SYSTEMD_UNIT_DIR)) {
15641
+ import_fs28.default.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
15499
15642
  }
15500
- import_fs27.default.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
15643
+ import_fs28.default.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
15501
15644
  try {
15502
- (0, import_child_process3.execFileSync)("loginctl", ["enable-linger", import_os25.default.userInfo().username], { timeout: 3e3 });
15645
+ (0, import_child_process3.execFileSync)("loginctl", ["enable-linger", import_os26.default.userInfo().username], { timeout: 3e3 });
15503
15646
  } catch {
15504
15647
  }
15505
15648
  const reload = (0, import_child_process3.spawnSync)("systemctl", ["--user", "daemon-reload"], {
@@ -15519,23 +15662,23 @@ function installSystemd(binaryPath) {
15519
15662
  }
15520
15663
  }
15521
15664
  function uninstallSystemd() {
15522
- if (import_fs27.default.existsSync(SYSTEMD_UNIT)) {
15665
+ if (import_fs28.default.existsSync(SYSTEMD_UNIT)) {
15523
15666
  (0, import_child_process3.spawnSync)("systemctl", ["--user", "disable", "--now", "node9-daemon"], {
15524
15667
  encoding: "utf8",
15525
15668
  timeout: 5e3
15526
15669
  });
15527
15670
  (0, import_child_process3.spawnSync)("systemctl", ["--user", "daemon-reload"], { encoding: "utf8", timeout: 5e3 });
15528
- import_fs27.default.unlinkSync(SYSTEMD_UNIT);
15671
+ import_fs28.default.unlinkSync(SYSTEMD_UNIT);
15529
15672
  }
15530
15673
  }
15531
15674
  function isSystemdInstalled() {
15532
- return import_fs27.default.existsSync(SYSTEMD_UNIT);
15675
+ return import_fs28.default.existsSync(SYSTEMD_UNIT);
15533
15676
  }
15534
15677
  function stopRunningDaemon() {
15535
- const pidFile = import_path29.default.join(import_os25.default.homedir(), ".node9", "daemon.pid");
15536
- if (!import_fs27.default.existsSync(pidFile)) return;
15678
+ const pidFile = import_path30.default.join(import_os26.default.homedir(), ".node9", "daemon.pid");
15679
+ if (!import_fs28.default.existsSync(pidFile)) return;
15537
15680
  try {
15538
- const data = JSON.parse(import_fs27.default.readFileSync(pidFile, "utf-8"));
15681
+ const data = JSON.parse(import_fs28.default.readFileSync(pidFile, "utf-8"));
15539
15682
  const pid = data.pid;
15540
15683
  const MAX_PID2 = 4194304;
15541
15684
  if (typeof pid === "number" && Number.isInteger(pid) && pid > 0 && pid <= MAX_PID2) {
@@ -15555,7 +15698,7 @@ function stopRunningDaemon() {
15555
15698
  }
15556
15699
  }
15557
15700
  try {
15558
- import_fs27.default.unlinkSync(pidFile);
15701
+ import_fs28.default.unlinkSync(pidFile);
15559
15702
  } catch {
15560
15703
  }
15561
15704
  } catch {
@@ -15625,26 +15768,26 @@ function isDaemonServiceInstalled() {
15625
15768
  if (process.platform === "linux") return isSystemdInstalled();
15626
15769
  return false;
15627
15770
  }
15628
- var import_fs27, import_path29, import_os25, import_child_process3, LAUNCHD_LABEL, LAUNCHD_PLIST, SYSTEMD_UNIT_DIR, SYSTEMD_UNIT;
15771
+ var import_fs28, import_path30, import_os26, import_child_process3, LAUNCHD_LABEL, LAUNCHD_PLIST, SYSTEMD_UNIT_DIR, SYSTEMD_UNIT;
15629
15772
  var init_service = __esm({
15630
15773
  "src/daemon/service.ts"() {
15631
15774
  "use strict";
15632
- import_fs27 = __toESM(require("fs"));
15633
- import_path29 = __toESM(require("path"));
15634
- import_os25 = __toESM(require("os"));
15775
+ import_fs28 = __toESM(require("fs"));
15776
+ import_path30 = __toESM(require("path"));
15777
+ import_os26 = __toESM(require("os"));
15635
15778
  import_child_process3 = require("child_process");
15636
15779
  LAUNCHD_LABEL = "ai.node9.daemon";
15637
- LAUNCHD_PLIST = import_path29.default.join(import_os25.default.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
15638
- SYSTEMD_UNIT_DIR = import_path29.default.join(import_os25.default.homedir(), ".config", "systemd", "user");
15639
- SYSTEMD_UNIT = import_path29.default.join(SYSTEMD_UNIT_DIR, "node9-daemon.service");
15780
+ LAUNCHD_PLIST = import_path30.default.join(import_os26.default.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
15781
+ SYSTEMD_UNIT_DIR = import_path30.default.join(import_os26.default.homedir(), ".config", "systemd", "user");
15782
+ SYSTEMD_UNIT = import_path30.default.join(SYSTEMD_UNIT_DIR, "node9-daemon.service");
15640
15783
  }
15641
15784
  });
15642
15785
 
15643
15786
  // src/daemon/index.ts
15644
15787
  function stopDaemon() {
15645
- if (!import_fs28.default.existsSync(DAEMON_PID_FILE)) return console.log(import_chalk7.default.yellow("Not running."));
15788
+ if (!import_fs29.default.existsSync(DAEMON_PID_FILE)) return console.log(import_chalk7.default.yellow("Not running."));
15646
15789
  try {
15647
- const data = JSON.parse(import_fs28.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
15790
+ const data = JSON.parse(import_fs29.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
15648
15791
  const pid = data.pid;
15649
15792
  if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
15650
15793
  console.log(import_chalk7.default.gray("Cleaned up invalid PID file."));
@@ -15656,7 +15799,7 @@ function stopDaemon() {
15656
15799
  console.log(import_chalk7.default.gray("Cleaned up stale PID file."));
15657
15800
  } finally {
15658
15801
  try {
15659
- import_fs28.default.unlinkSync(DAEMON_PID_FILE);
15802
+ import_fs29.default.unlinkSync(DAEMON_PID_FILE);
15660
15803
  } catch {
15661
15804
  }
15662
15805
  }
@@ -15665,9 +15808,9 @@ function daemonStatus() {
15665
15808
  const serviceInstalled = isDaemonServiceInstalled();
15666
15809
  const serviceLabel = serviceInstalled ? import_chalk7.default.green("installed (starts on login)") : import_chalk7.default.yellow("not installed \u2014 run: node9 daemon install");
15667
15810
  let processStatus;
15668
- if (import_fs28.default.existsSync(DAEMON_PID_FILE)) {
15811
+ if (import_fs29.default.existsSync(DAEMON_PID_FILE)) {
15669
15812
  try {
15670
- const data = JSON.parse(import_fs28.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
15813
+ const data = JSON.parse(import_fs29.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
15671
15814
  const pid = data.pid;
15672
15815
  const port = data.port;
15673
15816
  if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
@@ -15689,11 +15832,11 @@ function daemonStatus() {
15689
15832
  console.log(` Service : ${serviceLabel}
15690
15833
  `);
15691
15834
  }
15692
- var import_fs28, import_chalk7, MAX_PID;
15835
+ var import_fs29, import_chalk7, MAX_PID;
15693
15836
  var init_daemon2 = __esm({
15694
15837
  "src/daemon/index.ts"() {
15695
15838
  "use strict";
15696
- import_fs28 = __toESM(require("fs"));
15839
+ import_fs29 = __toESM(require("fs"));
15697
15840
  import_chalk7 = __toESM(require("chalk"));
15698
15841
  init_server();
15699
15842
  init_state2();
@@ -15733,20 +15876,20 @@ function getModelContextLimit(model) {
15733
15876
  return 2e5;
15734
15877
  }
15735
15878
  function readSessionUsage() {
15736
- const projectsDir = import_path47.default.join(import_os41.default.homedir(), ".claude", "projects");
15737
- if (!import_fs46.default.existsSync(projectsDir)) return null;
15879
+ const projectsDir = import_path48.default.join(import_os42.default.homedir(), ".claude", "projects");
15880
+ if (!import_fs47.default.existsSync(projectsDir)) return null;
15738
15881
  let latestFile = null;
15739
15882
  let latestMtime = 0;
15740
15883
  try {
15741
- for (const dir of import_fs46.default.readdirSync(projectsDir)) {
15742
- const dirPath = import_path47.default.join(projectsDir, dir);
15884
+ for (const dir of import_fs47.default.readdirSync(projectsDir)) {
15885
+ const dirPath = import_path48.default.join(projectsDir, dir);
15743
15886
  try {
15744
- if (!import_fs46.default.statSync(dirPath).isDirectory()) continue;
15745
- for (const file of import_fs46.default.readdirSync(dirPath)) {
15887
+ if (!import_fs47.default.statSync(dirPath).isDirectory()) continue;
15888
+ for (const file of import_fs47.default.readdirSync(dirPath)) {
15746
15889
  if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
15747
- const filePath = import_path47.default.join(dirPath, file);
15890
+ const filePath = import_path48.default.join(dirPath, file);
15748
15891
  try {
15749
- const mtime = import_fs46.default.statSync(filePath).mtimeMs;
15892
+ const mtime = import_fs47.default.statSync(filePath).mtimeMs;
15750
15893
  if (mtime > latestMtime) {
15751
15894
  latestMtime = mtime;
15752
15895
  latestFile = filePath;
@@ -15761,7 +15904,7 @@ function readSessionUsage() {
15761
15904
  }
15762
15905
  if (!latestFile) return null;
15763
15906
  try {
15764
- const lines = import_fs46.default.readFileSync(latestFile, "utf-8").split("\n");
15907
+ const lines = import_fs47.default.readFileSync(latestFile, "utf-8").split("\n");
15765
15908
  let lastModel = "";
15766
15909
  let lastInput = 0;
15767
15910
  let lastOutput = 0;
@@ -15822,7 +15965,7 @@ function formatBase(activity) {
15822
15965
  const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
15823
15966
  const icon = getIcon(activity.tool);
15824
15967
  const toolName = activity.tool.slice(0, 16).padEnd(16);
15825
- const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(import_os41.default.homedir(), "~");
15968
+ const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(import_os42.default.homedir(), "~");
15826
15969
  const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
15827
15970
  return `${import_chalk29.default.gray(time)} ${icon} ${agentLabel(activity.agent, activity.mcpServer, activity.sessionId)}${import_chalk29.default.white.bold(toolName)} ${import_chalk29.default.dim(argsPreview)}`;
15828
15971
  }
@@ -15861,9 +16004,9 @@ function renderPending(activity) {
15861
16004
  }
15862
16005
  async function ensureDaemon() {
15863
16006
  let pidPort = null;
15864
- if (import_fs46.default.existsSync(PID_FILE)) {
16007
+ if (import_fs47.default.existsSync(PID_FILE)) {
15865
16008
  try {
15866
- const { port } = JSON.parse(import_fs46.default.readFileSync(PID_FILE, "utf-8"));
16009
+ const { port } = JSON.parse(import_fs47.default.readFileSync(PID_FILE, "utf-8"));
15867
16010
  pidPort = port;
15868
16011
  } catch {
15869
16012
  console.error(import_chalk29.default.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
@@ -16019,9 +16162,9 @@ function buildRecoveryCardLines(req) {
16019
16162
  ];
16020
16163
  }
16021
16164
  function readApproversFromDisk() {
16022
- const configPath = import_path47.default.join(import_os41.default.homedir(), ".node9", "config.json");
16165
+ const configPath = import_path48.default.join(import_os42.default.homedir(), ".node9", "config.json");
16023
16166
  try {
16024
- const raw = JSON.parse(import_fs46.default.readFileSync(configPath, "utf-8"));
16167
+ const raw = JSON.parse(import_fs47.default.readFileSync(configPath, "utf-8"));
16025
16168
  const settings = raw.settings ?? {};
16026
16169
  return settings.approvers ?? {};
16027
16170
  } catch {
@@ -16037,15 +16180,15 @@ function approverStatusLine() {
16037
16180
  return `${fmt("native", "native")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
16038
16181
  }
16039
16182
  function toggleApprover(channel) {
16040
- const configPath = import_path47.default.join(import_os41.default.homedir(), ".node9", "config.json");
16183
+ const configPath = import_path48.default.join(import_os42.default.homedir(), ".node9", "config.json");
16041
16184
  try {
16042
- const raw = JSON.parse(import_fs46.default.readFileSync(configPath, "utf-8"));
16185
+ const raw = JSON.parse(import_fs47.default.readFileSync(configPath, "utf-8"));
16043
16186
  const settings = raw.settings ?? {};
16044
16187
  const approvers = settings.approvers ?? {};
16045
16188
  approvers[channel] = approvers[channel] === false;
16046
16189
  settings.approvers = approvers;
16047
16190
  raw.settings = settings;
16048
- import_fs46.default.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
16191
+ import_fs47.default.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
16049
16192
  } catch (err2) {
16050
16193
  process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
16051
16194
  `);
@@ -16217,8 +16360,8 @@ async function startTail(options = {}) {
16217
16360
  }
16218
16361
  postDecisionHttp(req2.id, httpDecision, authToken, port, httpOpts).catch((err2) => {
16219
16362
  try {
16220
- import_fs46.default.appendFileSync(
16221
- import_path47.default.join(import_os41.default.homedir(), ".node9", "hook-debug.log"),
16363
+ import_fs47.default.appendFileSync(
16364
+ import_path48.default.join(import_os42.default.homedir(), ".node9", "hook-debug.log"),
16222
16365
  `[tail] POST /decision failed: ${String(err2)}
16223
16366
  `
16224
16367
  );
@@ -16282,9 +16425,9 @@ async function startTail(options = {}) {
16282
16425
  };
16283
16426
  process.stdin.on("keypress", onKeypress);
16284
16427
  }
16285
- const auditLog = import_path47.default.join(import_os41.default.homedir(), ".node9", "audit.log");
16428
+ const auditLog = import_path48.default.join(import_os42.default.homedir(), ".node9", "audit.log");
16286
16429
  try {
16287
- const unackedDlp = import_fs46.default.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
16430
+ const unackedDlp = import_fs47.default.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
16288
16431
  if (unackedDlp > 0) {
16289
16432
  console.log("");
16290
16433
  console.log(
@@ -16324,7 +16467,7 @@ async function startTail(options = {}) {
16324
16467
  if (stallWarned) return;
16325
16468
  if (Date.now() - lastActivityFromDaemon < STALL_THRESHOLD_MS) return;
16326
16469
  try {
16327
- const auditMtime = import_fs46.default.statSync(auditLog).mtimeMs;
16470
+ const auditMtime = import_fs47.default.statSync(auditLog).mtimeMs;
16328
16471
  if (Date.now() - auditMtime >= STALL_THRESHOLD_MS) return;
16329
16472
  console.log("");
16330
16473
  console.log(
@@ -16509,20 +16652,20 @@ async function startTail(options = {}) {
16509
16652
  process.exit(1);
16510
16653
  });
16511
16654
  }
16512
- var import_http2, import_chalk29, import_fs46, import_os41, import_path47, import_readline6, import_child_process12, PID_FILE, ICONS, MODEL_CONTEXT_LIMITS, RESET2, BOLD2, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, pendingShownForId, pendingWrappedLines, DIVIDER;
16655
+ var import_http2, import_chalk29, import_fs47, import_os42, import_path48, import_readline6, import_child_process12, PID_FILE, ICONS, MODEL_CONTEXT_LIMITS, RESET2, BOLD2, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, pendingShownForId, pendingWrappedLines, DIVIDER;
16513
16656
  var init_tail = __esm({
16514
16657
  "src/tui/tail.ts"() {
16515
16658
  "use strict";
16516
16659
  import_http2 = __toESM(require("http"));
16517
16660
  import_chalk29 = __toESM(require("chalk"));
16518
- import_fs46 = __toESM(require("fs"));
16519
- import_os41 = __toESM(require("os"));
16520
- import_path47 = __toESM(require("path"));
16661
+ import_fs47 = __toESM(require("fs"));
16662
+ import_os42 = __toESM(require("os"));
16663
+ import_path48 = __toESM(require("path"));
16521
16664
  import_readline6 = __toESM(require("readline"));
16522
16665
  import_child_process12 = require("child_process");
16523
16666
  init_daemon2();
16524
16667
  init_daemon();
16525
- PID_FILE = import_path47.default.join(import_os41.default.homedir(), ".node9", "daemon.pid");
16668
+ PID_FILE = import_path48.default.join(import_os42.default.homedir(), ".node9", "daemon.pid");
16526
16669
  ICONS = {
16527
16670
  bash: "\u{1F4BB}",
16528
16671
  shell: "\u{1F4BB}",
@@ -16644,9 +16787,9 @@ function formatTimeLeft(resetsAt) {
16644
16787
  return ` (${m}m left)`;
16645
16788
  }
16646
16789
  function safeReadJson(filePath) {
16647
- if (!import_fs47.default.existsSync(filePath)) return null;
16790
+ if (!import_fs48.default.existsSync(filePath)) return null;
16648
16791
  try {
16649
- return JSON.parse(import_fs47.default.readFileSync(filePath, "utf-8"));
16792
+ return JSON.parse(import_fs48.default.readFileSync(filePath, "utf-8"));
16650
16793
  } catch {
16651
16794
  return null;
16652
16795
  }
@@ -16667,12 +16810,12 @@ function countHooksInFile(filePath) {
16667
16810
  return Object.keys(cfg.hooks).length;
16668
16811
  }
16669
16812
  function countRulesInDir(rulesDir) {
16670
- if (!import_fs47.default.existsSync(rulesDir)) return 0;
16813
+ if (!import_fs48.default.existsSync(rulesDir)) return 0;
16671
16814
  let count = 0;
16672
16815
  try {
16673
- for (const entry of import_fs47.default.readdirSync(rulesDir, { withFileTypes: true })) {
16816
+ for (const entry of import_fs48.default.readdirSync(rulesDir, { withFileTypes: true })) {
16674
16817
  if (entry.isDirectory()) {
16675
- count += countRulesInDir(import_path48.default.join(rulesDir, entry.name));
16818
+ count += countRulesInDir(import_path49.default.join(rulesDir, entry.name));
16676
16819
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
16677
16820
  count++;
16678
16821
  }
@@ -16683,46 +16826,46 @@ function countRulesInDir(rulesDir) {
16683
16826
  }
16684
16827
  function isSamePath(a, b) {
16685
16828
  try {
16686
- return import_path48.default.resolve(a) === import_path48.default.resolve(b);
16829
+ return import_path49.default.resolve(a) === import_path49.default.resolve(b);
16687
16830
  } catch {
16688
16831
  return false;
16689
16832
  }
16690
16833
  }
16691
16834
  function countConfigs(cwd) {
16692
- const homeDir2 = import_os42.default.homedir();
16693
- const claudeDir = import_path48.default.join(homeDir2, ".claude");
16835
+ const homeDir2 = import_os43.default.homedir();
16836
+ const claudeDir = import_path49.default.join(homeDir2, ".claude");
16694
16837
  let claudeMdCount = 0;
16695
16838
  let rulesCount = 0;
16696
16839
  let hooksCount = 0;
16697
16840
  const userMcpServers = /* @__PURE__ */ new Set();
16698
16841
  const projectMcpServers = /* @__PURE__ */ new Set();
16699
- if (import_fs47.default.existsSync(import_path48.default.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
16700
- rulesCount += countRulesInDir(import_path48.default.join(claudeDir, "rules"));
16701
- const userSettings = import_path48.default.join(claudeDir, "settings.json");
16842
+ if (import_fs48.default.existsSync(import_path49.default.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
16843
+ rulesCount += countRulesInDir(import_path49.default.join(claudeDir, "rules"));
16844
+ const userSettings = import_path49.default.join(claudeDir, "settings.json");
16702
16845
  for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
16703
16846
  hooksCount += countHooksInFile(userSettings);
16704
- const userClaudeJson = import_path48.default.join(homeDir2, ".claude.json");
16847
+ const userClaudeJson = import_path49.default.join(homeDir2, ".claude.json");
16705
16848
  for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
16706
16849
  for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
16707
16850
  userMcpServers.delete(name);
16708
16851
  }
16709
16852
  if (cwd) {
16710
- if (import_fs47.default.existsSync(import_path48.default.join(cwd, "CLAUDE.md"))) claudeMdCount++;
16711
- if (import_fs47.default.existsSync(import_path48.default.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
16712
- const projectClaudeDir = import_path48.default.join(cwd, ".claude");
16853
+ if (import_fs48.default.existsSync(import_path49.default.join(cwd, "CLAUDE.md"))) claudeMdCount++;
16854
+ if (import_fs48.default.existsSync(import_path49.default.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
16855
+ const projectClaudeDir = import_path49.default.join(cwd, ".claude");
16713
16856
  const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
16714
16857
  if (!overlapsUserScope) {
16715
- if (import_fs47.default.existsSync(import_path48.default.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
16716
- rulesCount += countRulesInDir(import_path48.default.join(projectClaudeDir, "rules"));
16717
- const projSettings = import_path48.default.join(projectClaudeDir, "settings.json");
16858
+ if (import_fs48.default.existsSync(import_path49.default.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
16859
+ rulesCount += countRulesInDir(import_path49.default.join(projectClaudeDir, "rules"));
16860
+ const projSettings = import_path49.default.join(projectClaudeDir, "settings.json");
16718
16861
  for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
16719
16862
  hooksCount += countHooksInFile(projSettings);
16720
16863
  }
16721
- if (import_fs47.default.existsSync(import_path48.default.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
16722
- const localSettings = import_path48.default.join(projectClaudeDir, "settings.local.json");
16864
+ if (import_fs48.default.existsSync(import_path49.default.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
16865
+ const localSettings = import_path49.default.join(projectClaudeDir, "settings.local.json");
16723
16866
  for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
16724
16867
  hooksCount += countHooksInFile(localSettings);
16725
- const mcpJsonServers = getMcpServerNames(import_path48.default.join(cwd, ".mcp.json"));
16868
+ const mcpJsonServers = getMcpServerNames(import_path49.default.join(cwd, ".mcp.json"));
16726
16869
  const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
16727
16870
  for (const name of disabledMcpJson) mcpJsonServers.delete(name);
16728
16871
  for (const name of mcpJsonServers) projectMcpServers.add(name);
@@ -16755,12 +16898,12 @@ function readActiveShieldsHud() {
16755
16898
  return shieldsCache.value;
16756
16899
  }
16757
16900
  try {
16758
- const shieldsPath = import_path48.default.join(import_os42.default.homedir(), ".node9", "shields.json");
16759
- if (!import_fs47.default.existsSync(shieldsPath)) {
16901
+ const shieldsPath = import_path49.default.join(import_os43.default.homedir(), ".node9", "shields.json");
16902
+ if (!import_fs48.default.existsSync(shieldsPath)) {
16760
16903
  shieldsCache = { value: [], ts: now };
16761
16904
  return [];
16762
16905
  }
16763
- const parsed = JSON.parse(import_fs47.default.readFileSync(shieldsPath, "utf-8"));
16906
+ const parsed = JSON.parse(import_fs48.default.readFileSync(shieldsPath, "utf-8"));
16764
16907
  if (!Array.isArray(parsed.active)) {
16765
16908
  shieldsCache = { value: [], ts: now };
16766
16909
  return [];
@@ -16862,17 +17005,17 @@ function renderContextLine(stdin) {
16862
17005
  async function main() {
16863
17006
  try {
16864
17007
  const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
16865
- if (import_fs47.default.existsSync(import_path48.default.join(import_os42.default.homedir(), ".node9", "hud-debug"))) {
17008
+ if (import_fs48.default.existsSync(import_path49.default.join(import_os43.default.homedir(), ".node9", "hud-debug"))) {
16866
17009
  try {
16867
- const logPath = import_path48.default.join(import_os42.default.homedir(), ".node9", "hud-debug.log");
17010
+ const logPath = import_path49.default.join(import_os43.default.homedir(), ".node9", "hud-debug.log");
16868
17011
  const MAX_LOG_SIZE = 10 * 1024 * 1024;
16869
17012
  let size = 0;
16870
17013
  try {
16871
- size = import_fs47.default.statSync(logPath).size;
17014
+ size = import_fs48.default.statSync(logPath).size;
16872
17015
  } catch {
16873
17016
  }
16874
17017
  if (size < MAX_LOG_SIZE) {
16875
- import_fs47.default.appendFileSync(
17018
+ import_fs48.default.appendFileSync(
16876
17019
  logPath,
16877
17020
  JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
16878
17021
  );
@@ -16893,11 +17036,11 @@ async function main() {
16893
17036
  try {
16894
17037
  const cwd = stdin.cwd ?? process.cwd();
16895
17038
  for (const configPath of [
16896
- import_path48.default.join(cwd, "node9.config.json"),
16897
- import_path48.default.join(import_os42.default.homedir(), ".node9", "config.json")
17039
+ import_path49.default.join(cwd, "node9.config.json"),
17040
+ import_path49.default.join(import_os43.default.homedir(), ".node9", "config.json")
16898
17041
  ]) {
16899
- if (!import_fs47.default.existsSync(configPath)) continue;
16900
- const cfg = JSON.parse(import_fs47.default.readFileSync(configPath, "utf-8"));
17042
+ if (!import_fs48.default.existsSync(configPath)) continue;
17043
+ const cfg = JSON.parse(import_fs48.default.readFileSync(configPath, "utf-8"));
16901
17044
  const hud = cfg.settings?.hud;
16902
17045
  if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
16903
17046
  }
@@ -16915,13 +17058,13 @@ async function main() {
16915
17058
  renderOffline();
16916
17059
  }
16917
17060
  }
16918
- var import_fs47, import_path48, import_os42, import_http3, RESET3, BOLD3, DIM, RED2, GREEN2, YELLOW2, BLUE, MAGENTA, CYAN2, WHITE, BAR_FILLED, BAR_EMPTY, BAR_WIDTH, shieldsCache, SHIELDS_CACHE_TTL_MS;
17061
+ var import_fs48, import_path49, import_os43, import_http3, RESET3, BOLD3, DIM, RED2, GREEN2, YELLOW2, BLUE, MAGENTA, CYAN2, WHITE, BAR_FILLED, BAR_EMPTY, BAR_WIDTH, shieldsCache, SHIELDS_CACHE_TTL_MS;
16919
17062
  var init_hud = __esm({
16920
17063
  "src/cli/hud.ts"() {
16921
17064
  "use strict";
16922
- import_fs47 = __toESM(require("fs"));
16923
- import_path48 = __toESM(require("path"));
16924
- import_os42 = __toESM(require("os"));
17065
+ import_fs48 = __toESM(require("fs"));
17066
+ import_path49 = __toESM(require("path"));
17067
+ import_os43 = __toESM(require("os"));
16925
17068
  import_http3 = __toESM(require("http"));
16926
17069
  init_daemon();
16927
17070
  RESET3 = "\x1B[0m";
@@ -16948,9 +17091,9 @@ init_core();
16948
17091
  init_setup();
16949
17092
  init_daemon2();
16950
17093
  var import_chalk30 = __toESM(require("chalk"));
16951
- var import_fs48 = __toESM(require("fs"));
16952
- var import_path49 = __toESM(require("path"));
16953
- var import_os43 = __toESM(require("os"));
17094
+ var import_fs49 = __toESM(require("fs"));
17095
+ var import_path50 = __toESM(require("path"));
17096
+ var import_os44 = __toESM(require("os"));
16954
17097
  var import_prompts2 = require("@inquirer/prompts");
16955
17098
 
16956
17099
  // src/utils/duration.ts
@@ -17132,18 +17275,18 @@ async function runProxy(targetCommand) {
17132
17275
 
17133
17276
  // src/cli/daemon-starter.ts
17134
17277
  var import_child_process5 = require("child_process");
17135
- var import_path30 = __toESM(require("path"));
17136
- var import_fs29 = __toESM(require("fs"));
17278
+ var import_path31 = __toESM(require("path"));
17279
+ var import_fs30 = __toESM(require("fs"));
17137
17280
  init_daemon();
17138
17281
  function isTestingMode() {
17139
17282
  return /^(1|true|yes)$/i.test(process.env.NODE9_TESTING ?? "");
17140
17283
  }
17141
17284
  async function autoStartDaemonAndWait() {
17142
17285
  if (isTestingMode()) return false;
17143
- if (!import_path30.default.isAbsolute(process.argv[1])) return false;
17286
+ if (!import_path31.default.isAbsolute(process.argv[1])) return false;
17144
17287
  let resolvedArgv1;
17145
17288
  try {
17146
- resolvedArgv1 = import_fs29.default.realpathSync(process.argv[1]);
17289
+ resolvedArgv1 = import_fs30.default.realpathSync(process.argv[1]);
17147
17290
  } catch {
17148
17291
  return false;
17149
17292
  }
@@ -17170,10 +17313,10 @@ async function autoStartDaemonAndWait() {
17170
17313
 
17171
17314
  // src/cli/commands/check.ts
17172
17315
  var import_chalk9 = __toESM(require("chalk"));
17173
- var import_fs32 = __toESM(require("fs"));
17316
+ var import_fs33 = __toESM(require("fs"));
17174
17317
  var import_child_process7 = require("child_process");
17175
- var import_path33 = __toESM(require("path"));
17176
- var import_os28 = __toESM(require("os"));
17318
+ var import_path34 = __toESM(require("path"));
17319
+ var import_os29 = __toESM(require("os"));
17177
17320
  init_orchestrator();
17178
17321
  init_daemon();
17179
17322
  init_config();
@@ -17181,12 +17324,12 @@ init_policy();
17181
17324
 
17182
17325
  // src/undo.ts
17183
17326
  var import_child_process6 = require("child_process");
17184
- var import_crypto9 = __toESM(require("crypto"));
17185
- var import_fs30 = __toESM(require("fs"));
17327
+ var import_crypto11 = __toESM(require("crypto"));
17328
+ var import_fs31 = __toESM(require("fs"));
17186
17329
  var import_net3 = __toESM(require("net"));
17187
- var import_path31 = __toESM(require("path"));
17188
- var import_os26 = __toESM(require("os"));
17189
- var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path31.default.join(import_os26.default.tmpdir(), "node9-activity.sock");
17330
+ var import_path32 = __toESM(require("path"));
17331
+ var import_os27 = __toESM(require("os"));
17332
+ var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path32.default.join(import_os27.default.tmpdir(), "node9-activity.sock");
17190
17333
  function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
17191
17334
  try {
17192
17335
  const payload = JSON.stringify({
@@ -17206,22 +17349,22 @@ function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
17206
17349
  } catch {
17207
17350
  }
17208
17351
  }
17209
- var SNAPSHOT_STACK_PATH = import_path31.default.join(import_os26.default.homedir(), ".node9", "snapshots.json");
17210
- var UNDO_LATEST_PATH = import_path31.default.join(import_os26.default.homedir(), ".node9", "undo_latest.txt");
17352
+ var SNAPSHOT_STACK_PATH = import_path32.default.join(import_os27.default.homedir(), ".node9", "snapshots.json");
17353
+ var UNDO_LATEST_PATH = import_path32.default.join(import_os27.default.homedir(), ".node9", "undo_latest.txt");
17211
17354
  var MAX_SNAPSHOTS = 10;
17212
17355
  var GIT_TIMEOUT = 15e3;
17213
17356
  function readStack() {
17214
17357
  try {
17215
- if (import_fs30.default.existsSync(SNAPSHOT_STACK_PATH))
17216
- return JSON.parse(import_fs30.default.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
17358
+ if (import_fs31.default.existsSync(SNAPSHOT_STACK_PATH))
17359
+ return JSON.parse(import_fs31.default.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
17217
17360
  } catch {
17218
17361
  }
17219
17362
  return [];
17220
17363
  }
17221
17364
  function writeStack(stack) {
17222
- const dir = import_path31.default.dirname(SNAPSHOT_STACK_PATH);
17223
- if (!import_fs30.default.existsSync(dir)) import_fs30.default.mkdirSync(dir, { recursive: true });
17224
- import_fs30.default.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
17365
+ const dir = import_path32.default.dirname(SNAPSHOT_STACK_PATH);
17366
+ if (!import_fs31.default.existsSync(dir)) import_fs31.default.mkdirSync(dir, { recursive: true });
17367
+ import_fs31.default.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
17225
17368
  }
17226
17369
  function extractFilePath(args) {
17227
17370
  if (!args || typeof args !== "object") return null;
@@ -17241,12 +17384,12 @@ function buildArgsSummary(tool, args) {
17241
17384
  return "";
17242
17385
  }
17243
17386
  function findProjectRoot(filePath) {
17244
- let dir = import_path31.default.dirname(filePath);
17387
+ let dir = import_path32.default.dirname(filePath);
17245
17388
  while (true) {
17246
- if (import_fs30.default.existsSync(import_path31.default.join(dir, ".git")) || import_fs30.default.existsSync(import_path31.default.join(dir, "package.json"))) {
17389
+ if (import_fs31.default.existsSync(import_path32.default.join(dir, ".git")) || import_fs31.default.existsSync(import_path32.default.join(dir, "package.json"))) {
17247
17390
  return dir;
17248
17391
  }
17249
- const parent = import_path31.default.dirname(dir);
17392
+ const parent = import_path32.default.dirname(dir);
17250
17393
  if (parent === dir) return process.cwd();
17251
17394
  dir = parent;
17252
17395
  }
@@ -17254,7 +17397,7 @@ function findProjectRoot(filePath) {
17254
17397
  function normalizeCwdForHash(cwd) {
17255
17398
  let normalized;
17256
17399
  try {
17257
- normalized = import_fs30.default.realpathSync(cwd);
17400
+ normalized = import_fs31.default.realpathSync(cwd);
17258
17401
  } catch {
17259
17402
  normalized = cwd;
17260
17403
  }
@@ -17263,17 +17406,17 @@ function normalizeCwdForHash(cwd) {
17263
17406
  return normalized;
17264
17407
  }
17265
17408
  function getShadowRepoDir(cwd) {
17266
- const hash = import_crypto9.default.createHash("sha256").update(normalizeCwdForHash(cwd)).digest("hex").slice(0, 16);
17267
- return import_path31.default.join(import_os26.default.homedir(), ".node9", "snapshots", hash);
17409
+ const hash = import_crypto11.default.createHash("sha256").update(normalizeCwdForHash(cwd)).digest("hex").slice(0, 16);
17410
+ return import_path32.default.join(import_os27.default.homedir(), ".node9", "snapshots", hash);
17268
17411
  }
17269
17412
  function cleanOrphanedIndexFiles(shadowDir) {
17270
17413
  try {
17271
17414
  const cutoff = Date.now() - 6e4;
17272
- for (const f of import_fs30.default.readdirSync(shadowDir)) {
17415
+ for (const f of import_fs31.default.readdirSync(shadowDir)) {
17273
17416
  if (f.startsWith("index_")) {
17274
- const fp = import_path31.default.join(shadowDir, f);
17417
+ const fp = import_path32.default.join(shadowDir, f);
17275
17418
  try {
17276
- if (import_fs30.default.statSync(fp).mtimeMs < cutoff) import_fs30.default.unlinkSync(fp);
17419
+ if (import_fs31.default.statSync(fp).mtimeMs < cutoff) import_fs31.default.unlinkSync(fp);
17277
17420
  } catch {
17278
17421
  }
17279
17422
  }
@@ -17285,7 +17428,7 @@ function writeShadowExcludes(shadowDir, ignorePaths) {
17285
17428
  const hardcoded = [".git", ".node9"];
17286
17429
  const lines = [...hardcoded, ...ignorePaths].join("\n");
17287
17430
  try {
17288
- import_fs30.default.writeFileSync(import_path31.default.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
17431
+ import_fs31.default.writeFileSync(import_path32.default.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
17289
17432
  } catch {
17290
17433
  }
17291
17434
  }
@@ -17298,25 +17441,25 @@ function ensureShadowRepo(shadowDir, cwd) {
17298
17441
  timeout: 3e3
17299
17442
  });
17300
17443
  if (check.status === 0) {
17301
- const ptPath = import_path31.default.join(shadowDir, "project-path.txt");
17444
+ const ptPath = import_path32.default.join(shadowDir, "project-path.txt");
17302
17445
  try {
17303
- const stored = import_fs30.default.readFileSync(ptPath, "utf8").trim();
17446
+ const stored = import_fs31.default.readFileSync(ptPath, "utf8").trim();
17304
17447
  if (stored === normalizedCwd) return true;
17305
17448
  if (process.env.NODE9_DEBUG === "1")
17306
17449
  console.error(
17307
17450
  `[Node9] Shadow repo path mismatch: stored="${stored}" expected="${normalizedCwd}" \u2014 reinitializing`
17308
17451
  );
17309
- import_fs30.default.rmSync(shadowDir, { recursive: true, force: true });
17452
+ import_fs31.default.rmSync(shadowDir, { recursive: true, force: true });
17310
17453
  } catch {
17311
17454
  try {
17312
- import_fs30.default.writeFileSync(ptPath, normalizedCwd, "utf8");
17455
+ import_fs31.default.writeFileSync(ptPath, normalizedCwd, "utf8");
17313
17456
  } catch {
17314
17457
  }
17315
17458
  return true;
17316
17459
  }
17317
17460
  }
17318
17461
  try {
17319
- import_fs30.default.mkdirSync(shadowDir, { recursive: true });
17462
+ import_fs31.default.mkdirSync(shadowDir, { recursive: true });
17320
17463
  } catch {
17321
17464
  }
17322
17465
  const init = (0, import_child_process6.spawnSync)("git", ["init", "--bare", shadowDir], { timeout: 5e3 });
@@ -17325,7 +17468,7 @@ function ensureShadowRepo(shadowDir, cwd) {
17325
17468
  if (process.env.NODE9_DEBUG === "1") console.error("[Node9] git init --bare failed:", reason);
17326
17469
  return false;
17327
17470
  }
17328
- const configFile = import_path31.default.join(shadowDir, "config");
17471
+ const configFile = import_path32.default.join(shadowDir, "config");
17329
17472
  (0, import_child_process6.spawnSync)("git", ["config", "--file", configFile, "core.untrackedCache", "true"], {
17330
17473
  timeout: 3e3
17331
17474
  });
@@ -17333,7 +17476,7 @@ function ensureShadowRepo(shadowDir, cwd) {
17333
17476
  timeout: 3e3
17334
17477
  });
17335
17478
  try {
17336
- import_fs30.default.writeFileSync(import_path31.default.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
17479
+ import_fs31.default.writeFileSync(import_path32.default.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
17337
17480
  } catch {
17338
17481
  }
17339
17482
  return true;
@@ -17356,12 +17499,12 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
17356
17499
  let indexFile = null;
17357
17500
  try {
17358
17501
  const rawFilePath = extractFilePath(args);
17359
- const absFilePath = rawFilePath && import_path31.default.isAbsolute(rawFilePath) ? rawFilePath : null;
17502
+ const absFilePath = rawFilePath && import_path32.default.isAbsolute(rawFilePath) ? rawFilePath : null;
17360
17503
  const cwd = absFilePath ? findProjectRoot(absFilePath) : process.cwd();
17361
17504
  const shadowDir = getShadowRepoDir(cwd);
17362
17505
  if (!ensureShadowRepo(shadowDir, cwd)) return null;
17363
17506
  writeShadowExcludes(shadowDir, ignorePaths);
17364
- indexFile = import_path31.default.join(shadowDir, `index_${process.pid}_${Date.now()}`);
17507
+ indexFile = import_path32.default.join(shadowDir, `index_${process.pid}_${Date.now()}`);
17365
17508
  const shadowEnv = {
17366
17509
  ...process.env,
17367
17510
  GIT_DIR: shadowDir,
@@ -17433,7 +17576,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
17433
17576
  writeStack(stack);
17434
17577
  const entry = stack[stack.length - 1];
17435
17578
  notifySnapshotTaken(commitHash.slice(0, 7), tool, entry.argsSummary, capturedFiles.length);
17436
- import_fs30.default.writeFileSync(UNDO_LATEST_PATH, commitHash);
17579
+ import_fs31.default.writeFileSync(UNDO_LATEST_PATH, commitHash);
17437
17580
  if (shouldGc) {
17438
17581
  (0, import_child_process6.spawn)("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
17439
17582
  }
@@ -17444,7 +17587,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
17444
17587
  } finally {
17445
17588
  if (indexFile) {
17446
17589
  try {
17447
- import_fs30.default.unlinkSync(indexFile);
17590
+ import_fs31.default.unlinkSync(indexFile);
17448
17591
  } catch {
17449
17592
  }
17450
17593
  }
@@ -17520,9 +17663,9 @@ function applyUndo(hash, cwd) {
17520
17663
  timeout: GIT_TIMEOUT
17521
17664
  }).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
17522
17665
  for (const file of [...tracked, ...untracked]) {
17523
- const fullPath = import_path31.default.join(dir, file);
17524
- if (!snapshotFiles.has(file) && import_fs30.default.existsSync(fullPath)) {
17525
- import_fs30.default.unlinkSync(fullPath);
17666
+ const fullPath = import_path32.default.join(dir, file);
17667
+ if (!snapshotFiles.has(file) && import_fs31.default.existsSync(fullPath)) {
17668
+ import_fs31.default.unlinkSync(fullPath);
17526
17669
  }
17527
17670
  }
17528
17671
  return true;
@@ -17532,17 +17675,17 @@ function applyUndo(hash, cwd) {
17532
17675
  }
17533
17676
 
17534
17677
  // src/skill-pin.ts
17535
- var import_fs31 = __toESM(require("fs"));
17536
- var import_path32 = __toESM(require("path"));
17537
- var import_os27 = __toESM(require("os"));
17538
- var import_crypto10 = __toESM(require("crypto"));
17678
+ var import_fs32 = __toESM(require("fs"));
17679
+ var import_path33 = __toESM(require("path"));
17680
+ var import_os28 = __toESM(require("os"));
17681
+ var import_crypto12 = __toESM(require("crypto"));
17539
17682
  function getPinsFilePath2() {
17540
- return import_path32.default.join(import_os27.default.homedir(), ".node9", "skill-pins.json");
17683
+ return import_path33.default.join(import_os28.default.homedir(), ".node9", "skill-pins.json");
17541
17684
  }
17542
17685
  var MAX_FILES = 5e3;
17543
17686
  var MAX_TOTAL_BYTES = 50 * 1024 * 1024;
17544
17687
  function sha256Bytes(buf) {
17545
- return import_crypto10.default.createHash("sha256").update(buf).digest("hex");
17688
+ return import_crypto12.default.createHash("sha256").update(buf).digest("hex");
17546
17689
  }
17547
17690
  function walkDir(root) {
17548
17691
  const out = [];
@@ -17551,18 +17694,18 @@ function walkDir(root) {
17551
17694
  if (out.length >= MAX_FILES) return;
17552
17695
  let entries;
17553
17696
  try {
17554
- entries = import_fs31.default.readdirSync(dir, { withFileTypes: true });
17697
+ entries = import_fs32.default.readdirSync(dir, { withFileTypes: true });
17555
17698
  } catch {
17556
17699
  return;
17557
17700
  }
17558
17701
  entries.sort((a, b) => a.name.localeCompare(b.name));
17559
17702
  for (const entry of entries) {
17560
17703
  if (out.length >= MAX_FILES) return;
17561
- const full = import_path32.default.join(dir, entry.name);
17562
- const rel = relDir ? import_path32.default.posix.join(relDir, entry.name) : entry.name;
17704
+ const full = import_path33.default.join(dir, entry.name);
17705
+ const rel = relDir ? import_path33.default.posix.join(relDir, entry.name) : entry.name;
17563
17706
  let lst;
17564
17707
  try {
17565
- lst = import_fs31.default.lstatSync(full);
17708
+ lst = import_fs32.default.lstatSync(full);
17566
17709
  } catch {
17567
17710
  continue;
17568
17711
  }
@@ -17574,7 +17717,7 @@ function walkDir(root) {
17574
17717
  if (!lst.isFile()) continue;
17575
17718
  if (totalBytes + lst.size > MAX_TOTAL_BYTES) continue;
17576
17719
  try {
17577
- const buf = import_fs31.default.readFileSync(full);
17720
+ const buf = import_fs32.default.readFileSync(full);
17578
17721
  totalBytes += buf.length;
17579
17722
  out.push({ rel, hash: sha256Bytes(buf) });
17580
17723
  } catch {
@@ -17588,32 +17731,32 @@ function walkDir(root) {
17588
17731
  function hashSkillRoot(absPath) {
17589
17732
  let lst;
17590
17733
  try {
17591
- lst = import_fs31.default.lstatSync(absPath);
17734
+ lst = import_fs32.default.lstatSync(absPath);
17592
17735
  } catch {
17593
17736
  return { exists: false, contentHash: "", fileCount: 0 };
17594
17737
  }
17595
17738
  if (lst.isSymbolicLink()) return { exists: false, contentHash: "", fileCount: 0 };
17596
17739
  if (lst.isFile()) {
17597
17740
  try {
17598
- return { exists: true, contentHash: sha256Bytes(import_fs31.default.readFileSync(absPath)), fileCount: 1 };
17741
+ return { exists: true, contentHash: sha256Bytes(import_fs32.default.readFileSync(absPath)), fileCount: 1 };
17599
17742
  } catch {
17600
17743
  return { exists: false, contentHash: "", fileCount: 0 };
17601
17744
  }
17602
17745
  }
17603
17746
  if (lst.isDirectory()) {
17604
17747
  const entries = walkDir(absPath);
17605
- const contentHash = import_crypto10.default.createHash("sha256").update(entries.join("\n")).digest("hex");
17748
+ const contentHash = import_crypto12.default.createHash("sha256").update(entries.join("\n")).digest("hex");
17606
17749
  return { exists: true, contentHash, fileCount: entries.length };
17607
17750
  }
17608
17751
  return { exists: false, contentHash: "", fileCount: 0 };
17609
17752
  }
17610
17753
  function getRootKey(absPath) {
17611
- return import_crypto10.default.createHash("sha256").update(absPath).digest("hex").slice(0, 16);
17754
+ return import_crypto12.default.createHash("sha256").update(absPath).digest("hex").slice(0, 16);
17612
17755
  }
17613
17756
  function readSkillPinsSafe() {
17614
17757
  const filePath = getPinsFilePath2();
17615
17758
  try {
17616
- const raw = import_fs31.default.readFileSync(filePath, "utf-8");
17759
+ const raw = import_fs32.default.readFileSync(filePath, "utf-8");
17617
17760
  if (!raw.trim()) return { ok: false, reason: "corrupt", detail: "empty file" };
17618
17761
  const parsed = JSON.parse(raw);
17619
17762
  if (!parsed.roots || typeof parsed.roots !== "object" || Array.isArray(parsed.roots)) {
@@ -17633,10 +17776,10 @@ function readSkillPins() {
17633
17776
  }
17634
17777
  function writeSkillPins(data) {
17635
17778
  const filePath = getPinsFilePath2();
17636
- import_fs31.default.mkdirSync(import_path32.default.dirname(filePath), { recursive: true });
17637
- const tmp = `${filePath}.${import_crypto10.default.randomBytes(6).toString("hex")}.tmp`;
17638
- import_fs31.default.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
17639
- import_fs31.default.renameSync(tmp, filePath);
17779
+ import_fs32.default.mkdirSync(import_path33.default.dirname(filePath), { recursive: true });
17780
+ const tmp = `${filePath}.${import_crypto12.default.randomBytes(6).toString("hex")}.tmp`;
17781
+ import_fs32.default.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
17782
+ import_fs32.default.renameSync(tmp, filePath);
17640
17783
  }
17641
17784
  function removePin2(rootKey) {
17642
17785
  const pins = readSkillPins();
@@ -17680,36 +17823,36 @@ function verifyAndPinRoots(roots) {
17680
17823
  return { kind: "verified" };
17681
17824
  }
17682
17825
  function defaultSkillRoots(_cwd) {
17683
- const marketplaces = import_path32.default.join(import_os27.default.homedir(), ".claude", "plugins", "marketplaces");
17826
+ const marketplaces = import_path33.default.join(import_os28.default.homedir(), ".claude", "plugins", "marketplaces");
17684
17827
  const roots = [];
17685
17828
  let registries;
17686
17829
  try {
17687
- registries = import_fs31.default.readdirSync(marketplaces, { withFileTypes: true });
17830
+ registries = import_fs32.default.readdirSync(marketplaces, { withFileTypes: true });
17688
17831
  } catch {
17689
17832
  return [];
17690
17833
  }
17691
17834
  for (const registry of registries) {
17692
17835
  if (!registry.isDirectory()) continue;
17693
- const pluginsDir = import_path32.default.join(marketplaces, registry.name, "plugins");
17836
+ const pluginsDir = import_path33.default.join(marketplaces, registry.name, "plugins");
17694
17837
  let plugins;
17695
17838
  try {
17696
- plugins = import_fs31.default.readdirSync(pluginsDir, { withFileTypes: true });
17839
+ plugins = import_fs32.default.readdirSync(pluginsDir, { withFileTypes: true });
17697
17840
  } catch {
17698
17841
  continue;
17699
17842
  }
17700
17843
  for (const plugin of plugins) {
17701
17844
  if (!plugin.isDirectory()) continue;
17702
- roots.push(import_path32.default.join(pluginsDir, plugin.name));
17845
+ roots.push(import_path33.default.join(pluginsDir, plugin.name));
17703
17846
  }
17704
17847
  }
17705
17848
  return roots;
17706
17849
  }
17707
17850
  function resolveUserSkillRoot(entry, cwd) {
17708
17851
  if (!entry) return null;
17709
- if (entry.startsWith("~/") || entry === "~") return import_path32.default.join(import_os27.default.homedir(), entry.slice(1));
17710
- if (import_path32.default.isAbsolute(entry)) return entry;
17711
- if (!cwd || !import_path32.default.isAbsolute(cwd)) return null;
17712
- return import_path32.default.join(cwd, entry);
17852
+ if (entry.startsWith("~/") || entry === "~") return import_path33.default.join(import_os28.default.homedir(), entry.slice(1));
17853
+ if (import_path33.default.isAbsolute(entry)) return entry;
17854
+ if (!cwd || !import_path33.default.isAbsolute(cwd)) return null;
17855
+ return import_path33.default.join(cwd, entry);
17713
17856
  }
17714
17857
 
17715
17858
  // src/cli/commands/check.ts
@@ -17778,9 +17921,9 @@ function registerCheckCommand(program2) {
17778
17921
  } catch (err2) {
17779
17922
  const tempConfig = getConfig();
17780
17923
  if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
17781
- const logPath = import_path33.default.join(import_os28.default.homedir(), ".node9", "hook-debug.log");
17924
+ const logPath = import_path34.default.join(import_os29.default.homedir(), ".node9", "hook-debug.log");
17782
17925
  const errMsg = err2 instanceof Error ? err2.message : String(err2);
17783
- import_fs32.default.appendFileSync(
17926
+ import_fs33.default.appendFileSync(
17784
17927
  logPath,
17785
17928
  `[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
17786
17929
  RAW: ${raw}
@@ -17793,14 +17936,14 @@ RAW: ${raw}
17793
17936
  const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
17794
17937
  if (process.env.NODE9_DEBUG === "1") {
17795
17938
  try {
17796
- const logPath = import_path33.default.join(import_os28.default.homedir(), ".node9", "hook-debug.log");
17797
- if (!import_fs32.default.existsSync(import_path33.default.dirname(logPath)))
17798
- import_fs32.default.mkdirSync(import_path33.default.dirname(logPath), { recursive: true });
17939
+ const logPath = import_path34.default.join(import_os29.default.homedir(), ".node9", "hook-debug.log");
17940
+ if (!import_fs33.default.existsSync(import_path34.default.dirname(logPath)))
17941
+ import_fs33.default.mkdirSync(import_path34.default.dirname(logPath), { recursive: true });
17799
17942
  const sanitized = JSON.stringify({
17800
17943
  ...payload,
17801
17944
  prompt: `<redacted, ${prompt.length} bytes>`
17802
17945
  });
17803
- import_fs32.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${sanitized}
17946
+ import_fs33.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${sanitized}
17804
17947
  `);
17805
17948
  } catch {
17806
17949
  }
@@ -17820,8 +17963,8 @@ RAW: ${raw}
17820
17963
  );
17821
17964
  const reason = `\u{1F6A8} Node9 DLP: ${dlpMatch.patternName} detected in prompt (${dlpMatch.redactedSample}). Prompt was not submitted \u2014 remove the credential and try again.`;
17822
17965
  try {
17823
- const ttyFd = import_fs32.default.openSync("/dev/tty", "w");
17824
- import_fs32.default.writeSync(
17966
+ const ttyFd = import_fs33.default.openSync("/dev/tty", "w");
17967
+ import_fs33.default.writeSync(
17825
17968
  ttyFd,
17826
17969
  import_chalk9.default.bgRed.white.bold(`
17827
17970
  \u{1F6A8} NODE9 DLP \u2014 PROMPT BLOCKED
@@ -17831,7 +17974,7 @@ RAW: ${raw}
17831
17974
 
17832
17975
  `)
17833
17976
  );
17834
- import_fs32.default.closeSync(ttyFd);
17977
+ import_fs33.default.closeSync(ttyFd);
17835
17978
  } catch {
17836
17979
  }
17837
17980
  const isCodex = agent2 === "Codex";
@@ -17850,16 +17993,16 @@ RAW: ${raw}
17850
17993
  process.exit(2);
17851
17994
  }
17852
17995
  const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
17853
- const safeCwdForConfig = typeof payloadCwd === "string" && import_path33.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
17996
+ const safeCwdForConfig = typeof payloadCwd === "string" && import_path34.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
17854
17997
  const config = getConfig(safeCwdForConfig);
17855
17998
  if (config.settings.autoStartDaemon && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON) {
17856
17999
  try {
17857
18000
  const scriptPath = process.argv[1];
17858
- if (typeof scriptPath !== "string" || !import_path33.default.isAbsolute(scriptPath))
18001
+ if (typeof scriptPath !== "string" || !import_path34.default.isAbsolute(scriptPath))
17859
18002
  throw new Error("node9: argv[1] is not an absolute path");
17860
- const resolvedScript = import_fs32.default.realpathSync(scriptPath);
17861
- const packageDist = import_fs32.default.realpathSync(import_path33.default.resolve(__dirname, "../.."));
17862
- if (!resolvedScript.startsWith(packageDist + import_path33.default.sep) && resolvedScript !== packageDist)
18003
+ const resolvedScript = import_fs33.default.realpathSync(scriptPath);
18004
+ const packageDist = import_fs33.default.realpathSync(import_path34.default.resolve(__dirname, "../.."));
18005
+ if (!resolvedScript.startsWith(packageDist + import_path34.default.sep) && resolvedScript !== packageDist)
17863
18006
  throw new Error(
17864
18007
  `node9: daemon spawn aborted \u2014 argv[1] (${resolvedScript}) is outside package dist (${packageDist})`
17865
18008
  );
@@ -17881,10 +18024,10 @@ RAW: ${raw}
17881
18024
  });
17882
18025
  d.unref();
17883
18026
  } catch (spawnErr) {
17884
- const logPath = import_path33.default.join(import_os28.default.homedir(), ".node9", "hook-debug.log");
18027
+ const logPath = import_path34.default.join(import_os29.default.homedir(), ".node9", "hook-debug.log");
17885
18028
  const msg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);
17886
18029
  try {
17887
- import_fs32.default.appendFileSync(
18030
+ import_fs33.default.appendFileSync(
17888
18031
  logPath,
17889
18032
  `[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-autostart-failed: ${msg}
17890
18033
  `
@@ -17894,10 +18037,10 @@ RAW: ${raw}
17894
18037
  }
17895
18038
  }
17896
18039
  if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
17897
- const logPath = import_path33.default.join(import_os28.default.homedir(), ".node9", "hook-debug.log");
17898
- if (!import_fs32.default.existsSync(import_path33.default.dirname(logPath)))
17899
- import_fs32.default.mkdirSync(import_path33.default.dirname(logPath), { recursive: true });
17900
- import_fs32.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
18040
+ const logPath = import_path34.default.join(import_os29.default.homedir(), ".node9", "hook-debug.log");
18041
+ if (!import_fs33.default.existsSync(import_path34.default.dirname(logPath)))
18042
+ import_fs33.default.mkdirSync(import_path34.default.dirname(logPath), { recursive: true });
18043
+ import_fs33.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
17901
18044
  `);
17902
18045
  }
17903
18046
  const rawToolName = sanitize2(extractToolName(payload));
@@ -17911,8 +18054,8 @@ RAW: ${raw}
17911
18054
  const isHumanDecision = blockedByContext.toLowerCase().includes("user") || blockedByContext.toLowerCase().includes("daemon") || blockedByContext.toLowerCase().includes("decision");
17912
18055
  let ttyFd = null;
17913
18056
  try {
17914
- ttyFd = import_fs32.default.openSync("/dev/tty", "w");
17915
- const writeTty = (line) => import_fs32.default.writeSync(ttyFd, line + "\n");
18057
+ ttyFd = import_fs33.default.openSync("/dev/tty", "w");
18058
+ const writeTty = (line) => import_fs33.default.writeSync(ttyFd, line + "\n");
17916
18059
  if (blockedByContext.includes("DLP") || blockedByContext.includes("Secret Detected") || blockedByContext.includes("Credential Review")) {
17917
18060
  writeTty(import_chalk9.default.bgRed.white.bold(`
17918
18061
  \u{1F6A8} NODE9 DLP ALERT \u2014 CREDENTIAL DETECTED `));
@@ -17931,7 +18074,7 @@ RAW: ${raw}
17931
18074
  } finally {
17932
18075
  if (ttyFd !== null)
17933
18076
  try {
17934
- import_fs32.default.closeSync(ttyFd);
18077
+ import_fs33.default.closeSync(ttyFd);
17935
18078
  } catch {
17936
18079
  }
17937
18080
  }
@@ -17982,17 +18125,17 @@ RAW: ${raw}
17982
18125
  const safeSessionId = /^[A-Za-z0-9_\-]{1,128}$/.test(rawSessionId) ? rawSessionId : "";
17983
18126
  if (skillPinCfg.enabled && safeSessionId) {
17984
18127
  try {
17985
- const sessionsDir = import_path33.default.join(import_os28.default.homedir(), ".node9", "skill-sessions");
17986
- const flagPath = import_path33.default.join(sessionsDir, `${safeSessionId}.json`);
18128
+ const sessionsDir = import_path34.default.join(import_os29.default.homedir(), ".node9", "skill-sessions");
18129
+ const flagPath = import_path34.default.join(sessionsDir, `${safeSessionId}.json`);
17987
18130
  let flag = null;
17988
18131
  try {
17989
- flag = JSON.parse(import_fs32.default.readFileSync(flagPath, "utf-8"));
18132
+ flag = JSON.parse(import_fs33.default.readFileSync(flagPath, "utf-8"));
17990
18133
  } catch {
17991
18134
  }
17992
18135
  const writeFlag = (data2) => {
17993
18136
  try {
17994
- import_fs32.default.mkdirSync(sessionsDir, { recursive: true });
17995
- import_fs32.default.writeFileSync(
18137
+ import_fs33.default.mkdirSync(sessionsDir, { recursive: true });
18138
+ import_fs33.default.writeFileSync(
17996
18139
  flagPath,
17997
18140
  JSON.stringify({ ...data2, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
17998
18141
  { mode: 384 }
@@ -18003,8 +18146,8 @@ RAW: ${raw}
18003
18146
  const sendSkillWarn = (detail, recoveryCmd) => {
18004
18147
  let ttyFd = null;
18005
18148
  try {
18006
- ttyFd = import_fs32.default.openSync("/dev/tty", "w");
18007
- const w = (line) => import_fs32.default.writeSync(ttyFd, line + "\n");
18149
+ ttyFd = import_fs33.default.openSync("/dev/tty", "w");
18150
+ const w = (line) => import_fs33.default.writeSync(ttyFd, line + "\n");
18008
18151
  w(import_chalk9.default.yellow(`
18009
18152
  \u26A0\uFE0F Node9: installed skill drift detected`));
18010
18153
  w(import_chalk9.default.gray(` ${detail}`));
@@ -18019,7 +18162,7 @@ RAW: ${raw}
18019
18162
  } finally {
18020
18163
  if (ttyFd !== null)
18021
18164
  try {
18022
- import_fs32.default.closeSync(ttyFd);
18165
+ import_fs33.default.closeSync(ttyFd);
18023
18166
  } catch {
18024
18167
  }
18025
18168
  }
@@ -18035,7 +18178,7 @@ RAW: ${raw}
18035
18178
  return;
18036
18179
  }
18037
18180
  if (!flag || flag.state !== "verified" && flag.state !== "warned") {
18038
- const absoluteCwd = typeof payloadCwd === "string" && import_path33.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
18181
+ const absoluteCwd = typeof payloadCwd === "string" && import_path34.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
18039
18182
  const extraRoots = skillPinCfg.roots;
18040
18183
  const resolvedExtra = extraRoots.map((r) => resolveUserSkillRoot(r, absoluteCwd)).filter((r) => typeof r === "string");
18041
18184
  const roots = [...defaultSkillRoots(absoluteCwd), ...resolvedExtra];
@@ -18076,10 +18219,10 @@ RAW: ${raw}
18076
18219
  }
18077
18220
  try {
18078
18221
  const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1e3;
18079
- for (const name of import_fs32.default.readdirSync(sessionsDir)) {
18080
- const p = import_path33.default.join(sessionsDir, name);
18222
+ for (const name of import_fs33.default.readdirSync(sessionsDir)) {
18223
+ const p = import_path34.default.join(sessionsDir, name);
18081
18224
  try {
18082
- if (import_fs32.default.statSync(p).mtimeMs < cutoff) import_fs32.default.unlinkSync(p);
18225
+ if (import_fs33.default.statSync(p).mtimeMs < cutoff) import_fs33.default.unlinkSync(p);
18083
18226
  } catch {
18084
18227
  }
18085
18228
  }
@@ -18089,9 +18232,9 @@ RAW: ${raw}
18089
18232
  } catch (err2) {
18090
18233
  if (process.env.NODE9_DEBUG === "1") {
18091
18234
  try {
18092
- const dbg = import_path33.default.join(import_os28.default.homedir(), ".node9", "hook-debug.log");
18235
+ const dbg = import_path34.default.join(import_os29.default.homedir(), ".node9", "hook-debug.log");
18093
18236
  const msg = err2 instanceof Error ? err2.message : String(err2);
18094
- import_fs32.default.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
18237
+ import_fs33.default.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
18095
18238
  `);
18096
18239
  } catch {
18097
18240
  }
@@ -18101,7 +18244,7 @@ RAW: ${raw}
18101
18244
  if (shouldSnapshot(toolName, toolInput, config)) {
18102
18245
  await createShadowSnapshot(toolName, toolInput, config.policy.snapshot.ignorePaths);
18103
18246
  }
18104
- const safeCwdForAuth = typeof payloadCwd === "string" && import_path33.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
18247
+ const safeCwdForAuth = typeof payloadCwd === "string" && import_path34.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
18105
18248
  const result = await authorizeHeadless(toolName, toolInput, meta, {
18106
18249
  cwd: safeCwdForAuth
18107
18250
  });
@@ -18113,12 +18256,12 @@ RAW: ${raw}
18113
18256
  }
18114
18257
  if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && !process.stdout.isTTY && config.settings.autoStartDaemon) {
18115
18258
  try {
18116
- const tty = import_fs32.default.openSync("/dev/tty", "w");
18117
- import_fs32.default.writeSync(
18259
+ const tty = import_fs33.default.openSync("/dev/tty", "w");
18260
+ import_fs33.default.writeSync(
18118
18261
  tty,
18119
18262
  import_chalk9.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically...\n")
18120
18263
  );
18121
- import_fs32.default.closeSync(tty);
18264
+ import_fs33.default.closeSync(tty);
18122
18265
  } catch {
18123
18266
  }
18124
18267
  const daemonReady = await autoStartDaemonAndWait();
@@ -18145,9 +18288,9 @@ RAW: ${raw}
18145
18288
  });
18146
18289
  } catch (err2) {
18147
18290
  if (process.env.NODE9_DEBUG === "1") {
18148
- const logPath = import_path33.default.join(import_os28.default.homedir(), ".node9", "hook-debug.log");
18291
+ const logPath = import_path34.default.join(import_os29.default.homedir(), ".node9", "hook-debug.log");
18149
18292
  const errMsg = err2 instanceof Error ? err2.message : String(err2);
18150
- import_fs32.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
18293
+ import_fs33.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
18151
18294
  `);
18152
18295
  }
18153
18296
  process.exit(0);
@@ -18181,9 +18324,9 @@ RAW: ${raw}
18181
18324
  }
18182
18325
 
18183
18326
  // src/cli/commands/log.ts
18184
- var import_fs33 = __toESM(require("fs"));
18185
- var import_path34 = __toESM(require("path"));
18186
- var import_os29 = __toESM(require("os"));
18327
+ var import_fs34 = __toESM(require("fs"));
18328
+ var import_path35 = __toESM(require("path"));
18329
+ var import_os30 = __toESM(require("os"));
18187
18330
  init_audit();
18188
18331
  init_config();
18189
18332
  init_daemon();
@@ -18278,10 +18421,10 @@ function registerLogCommand(program2) {
18278
18421
  if (rawToolName !== tool) entry.agentToolName = rawToolName;
18279
18422
  const payloadSessionId = payload.session_id ?? payload.conversationId;
18280
18423
  if (payloadSessionId) entry.sessionId = payloadSessionId;
18281
- const logPath = import_path34.default.join(import_os29.default.homedir(), ".node9", "audit.log");
18282
- if (!import_fs33.default.existsSync(import_path34.default.dirname(logPath)))
18283
- import_fs33.default.mkdirSync(import_path34.default.dirname(logPath), { recursive: true });
18284
- import_fs33.default.appendFileSync(logPath, JSON.stringify(entry) + "\n");
18424
+ const logPath = import_path35.default.join(import_os30.default.homedir(), ".node9", "audit.log");
18425
+ if (!import_fs34.default.existsSync(import_path35.default.dirname(logPath)))
18426
+ import_fs34.default.mkdirSync(import_path35.default.dirname(logPath), { recursive: true });
18427
+ import_fs34.default.appendFileSync(logPath, JSON.stringify(entry) + "\n");
18285
18428
  if ((tool === "Bash" || tool === "bash") && isDaemonRunning()) {
18286
18429
  const command = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
18287
18430
  if (command) {
@@ -18315,7 +18458,7 @@ function registerLogCommand(program2) {
18315
18458
  }
18316
18459
  }
18317
18460
  const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
18318
- const safeCwd = typeof payloadCwd === "string" && import_path34.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
18461
+ const safeCwd = typeof payloadCwd === "string" && import_path35.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
18319
18462
  const config = getConfig(safeCwd);
18320
18463
  if ((tool === "Bash" || tool === "bash") && config.settings.enableUndo !== false) {
18321
18464
  const bashCommand = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
@@ -18336,9 +18479,9 @@ function registerLogCommand(program2) {
18336
18479
  const msg = err2 instanceof Error ? err2.message : String(err2);
18337
18480
  process.stderr.write(`[Node9] audit log error: ${msg}
18338
18481
  `);
18339
- const debugPath = import_path34.default.join(import_os29.default.homedir(), ".node9", "hook-debug.log");
18482
+ const debugPath = import_path35.default.join(import_os30.default.homedir(), ".node9", "hook-debug.log");
18340
18483
  try {
18341
- import_fs33.default.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
18484
+ import_fs34.default.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
18342
18485
  `);
18343
18486
  } catch {
18344
18487
  }
@@ -18738,14 +18881,15 @@ function registerConfigShowCommand(program2) {
18738
18881
 
18739
18882
  // src/cli/commands/doctor.ts
18740
18883
  var import_chalk11 = __toESM(require("chalk"));
18741
- var import_fs34 = __toESM(require("fs"));
18742
- var import_path35 = __toESM(require("path"));
18743
- var import_os30 = __toESM(require("os"));
18884
+ var import_fs35 = __toESM(require("fs"));
18885
+ var import_path36 = __toESM(require("path"));
18886
+ var import_os31 = __toESM(require("os"));
18744
18887
  var import_child_process8 = require("child_process");
18745
18888
  init_daemon();
18889
+ init_config();
18746
18890
  function registerDoctorCommand(program2, version2) {
18747
- program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(() => {
18748
- const homeDir2 = import_os30.default.homedir();
18891
+ program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(async () => {
18892
+ const homeDir2 = import_os31.default.homedir();
18749
18893
  let failures = 0;
18750
18894
  function pass(msg) {
18751
18895
  console.log(import_chalk11.default.green(" \u2705 ") + msg);
@@ -18794,10 +18938,10 @@ function registerDoctorCommand(program2, version2) {
18794
18938
  );
18795
18939
  }
18796
18940
  section("Configuration");
18797
- const globalConfigPath = import_path35.default.join(homeDir2, ".node9", "config.json");
18798
- if (import_fs34.default.existsSync(globalConfigPath)) {
18941
+ const globalConfigPath = import_path36.default.join(homeDir2, ".node9", "config.json");
18942
+ if (import_fs35.default.existsSync(globalConfigPath)) {
18799
18943
  try {
18800
- JSON.parse(import_fs34.default.readFileSync(globalConfigPath, "utf-8"));
18944
+ JSON.parse(import_fs35.default.readFileSync(globalConfigPath, "utf-8"));
18801
18945
  pass("~/.node9/config.json found and valid");
18802
18946
  } catch {
18803
18947
  fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
@@ -18805,10 +18949,10 @@ function registerDoctorCommand(program2, version2) {
18805
18949
  } else {
18806
18950
  warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
18807
18951
  }
18808
- const projectConfigPath = import_path35.default.join(process.cwd(), "node9.config.json");
18809
- if (import_fs34.default.existsSync(projectConfigPath)) {
18952
+ const projectConfigPath = import_path36.default.join(process.cwd(), "node9.config.json");
18953
+ if (import_fs35.default.existsSync(projectConfigPath)) {
18810
18954
  try {
18811
- JSON.parse(import_fs34.default.readFileSync(projectConfigPath, "utf-8"));
18955
+ JSON.parse(import_fs35.default.readFileSync(projectConfigPath, "utf-8"));
18812
18956
  pass("node9.config.json found and valid (project)");
18813
18957
  } catch {
18814
18958
  fail(
@@ -18817,8 +18961,8 @@ function registerDoctorCommand(program2, version2) {
18817
18961
  );
18818
18962
  }
18819
18963
  }
18820
- const credsPath = import_path35.default.join(homeDir2, ".node9", "credentials.json");
18821
- if (import_fs34.default.existsSync(credsPath)) {
18964
+ const credsPath = import_path36.default.join(homeDir2, ".node9", "credentials.json");
18965
+ if (import_fs35.default.existsSync(credsPath)) {
18822
18966
  pass("Cloud credentials found (~/.node9/credentials.json)");
18823
18967
  } else {
18824
18968
  warn(
@@ -18827,10 +18971,10 @@ function registerDoctorCommand(program2, version2) {
18827
18971
  );
18828
18972
  }
18829
18973
  section("Agent Hooks");
18830
- const claudeSettingsPath = import_path35.default.join(homeDir2, ".claude", "settings.json");
18831
- if (import_fs34.default.existsSync(claudeSettingsPath)) {
18974
+ const claudeSettingsPath = import_path36.default.join(homeDir2, ".claude", "settings.json");
18975
+ if (import_fs35.default.existsSync(claudeSettingsPath)) {
18832
18976
  try {
18833
- const cs = JSON.parse(import_fs34.default.readFileSync(claudeSettingsPath, "utf-8"));
18977
+ const cs = JSON.parse(import_fs35.default.readFileSync(claudeSettingsPath, "utf-8"));
18834
18978
  const hasHook = cs.hooks?.PreToolUse?.some(
18835
18979
  (m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
18836
18980
  );
@@ -18846,10 +18990,10 @@ function registerDoctorCommand(program2, version2) {
18846
18990
  } else {
18847
18991
  warn("Claude Code \u2014 not configured", "Run: node9 setup claude");
18848
18992
  }
18849
- const geminiSettingsPath = import_path35.default.join(homeDir2, ".gemini", "settings.json");
18850
- if (import_fs34.default.existsSync(geminiSettingsPath)) {
18993
+ const geminiSettingsPath = import_path36.default.join(homeDir2, ".gemini", "settings.json");
18994
+ if (import_fs35.default.existsSync(geminiSettingsPath)) {
18851
18995
  try {
18852
- const gs = JSON.parse(import_fs34.default.readFileSync(geminiSettingsPath, "utf-8"));
18996
+ const gs = JSON.parse(import_fs35.default.readFileSync(geminiSettingsPath, "utf-8"));
18853
18997
  const hasHook = gs.hooks?.BeforeTool?.some(
18854
18998
  (m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
18855
18999
  );
@@ -18865,10 +19009,10 @@ function registerDoctorCommand(program2, version2) {
18865
19009
  } else {
18866
19010
  warn("Gemini CLI \u2014 not configured", "Run: node9 setup gemini (skip if not using Gemini)");
18867
19011
  }
18868
- const cursorHooksPath = import_path35.default.join(homeDir2, ".cursor", "hooks.json");
18869
- if (import_fs34.default.existsSync(cursorHooksPath)) {
19012
+ const cursorHooksPath = import_path36.default.join(homeDir2, ".cursor", "hooks.json");
19013
+ if (import_fs35.default.existsSync(cursorHooksPath)) {
18870
19014
  try {
18871
- const cur = JSON.parse(import_fs34.default.readFileSync(cursorHooksPath, "utf-8"));
19015
+ const cur = JSON.parse(import_fs35.default.readFileSync(cursorHooksPath, "utf-8"));
18872
19016
  const hasHook = cur.hooks?.preToolUse?.some(
18873
19017
  (h) => h.command?.includes("node9") || h.command?.includes("cli.js")
18874
19018
  );
@@ -18895,6 +19039,47 @@ function registerDoctorCommand(program2, version2) {
18895
19039
  "Run: node9 daemon --background"
18896
19040
  );
18897
19041
  }
19042
+ section("Cloud audit shipping");
19043
+ try {
19044
+ const { shipLagBytes: shipLagBytes2, readWatermark: readWatermark2, AUDIT_SHIP_WATERMARK: AUDIT_SHIP_WATERMARK2 } = await Promise.resolve().then(() => (init_audit_shipper(), audit_shipper_exports));
19045
+ const cfg = getConfig();
19046
+ const creds = import_fs35.default.existsSync(import_path36.default.join(import_os31.default.homedir(), ".node9", "credentials.json"));
19047
+ if (!creds) {
19048
+ warn("Not logged in \u2014 audit rows stay local", "Run: node9 login <api-key>");
19049
+ } else if (!cfg.settings.approvers.cloud) {
19050
+ warn(
19051
+ "Cloud approvals OFF (settings.approvers.cloud=false) \u2014 nothing syncs to the dashboard",
19052
+ "Privacy mode is a valid choice; set approvers.cloud=true to sync."
19053
+ );
19054
+ } else if (cfg.settings.shipper.enabled === false) {
19055
+ warn("Shipper disabled (settings.shipper.enabled=false) \u2014 audit rows stay local");
19056
+ } else {
19057
+ const lag = shipLagBytes2();
19058
+ const wm = readWatermark2(AUDIT_SHIP_WATERMARK2);
19059
+ if (lag === 0) {
19060
+ pass("Audit shipping caught up \u2014 dashboard matches the local log");
19061
+ } else if (wm && lag !== null) {
19062
+ const ageMin = Math.round((Date.now() - new Date(wm.updatedAt).getTime()) / 6e4);
19063
+ if (ageMin > 5 && !isDaemonRunning()) {
19064
+ warn(
19065
+ `${Math.round(lag / 1024)} KB of audit rows not shipped (last ship ${ageMin}m ago)`,
19066
+ "The daemon ships every ~20s \u2014 start it: node9 daemon --background"
19067
+ );
19068
+ } else {
19069
+ pass(
19070
+ `Shipping in progress \u2014 ${Math.round(lag / 1024)} KB queued, last ship ${ageMin}m ago`
19071
+ );
19072
+ }
19073
+ } else {
19074
+ warn(
19075
+ "Shipper has never run on this machine \u2014 dashboard may lag the local log",
19076
+ "Start the daemon: node9 daemon --background"
19077
+ );
19078
+ }
19079
+ }
19080
+ } catch (err2) {
19081
+ warn(`Shipping status unavailable: ${err2.message}`);
19082
+ }
18898
19083
  console.log("");
18899
19084
  if (failures === 0) {
18900
19085
  console.log(import_chalk11.default.green.bold(" All checks passed. Node9 is ready.\n"));
@@ -18908,9 +19093,9 @@ function registerDoctorCommand(program2, version2) {
18908
19093
 
18909
19094
  // src/cli/commands/audit.ts
18910
19095
  var import_chalk12 = __toESM(require("chalk"));
18911
- var import_fs35 = __toESM(require("fs"));
18912
- var import_path36 = __toESM(require("path"));
18913
- var import_os31 = __toESM(require("os"));
19096
+ var import_fs36 = __toESM(require("fs"));
19097
+ var import_path37 = __toESM(require("path"));
19098
+ var import_os32 = __toESM(require("os"));
18914
19099
  function formatRelativeTime(timestamp) {
18915
19100
  const diff = Date.now() - new Date(timestamp).getTime();
18916
19101
  const sec = Math.floor(diff / 1e3);
@@ -18923,14 +19108,14 @@ function formatRelativeTime(timestamp) {
18923
19108
  }
18924
19109
  function registerAuditCommand(program2) {
18925
19110
  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) => {
18926
- const logPath = import_path36.default.join(import_os31.default.homedir(), ".node9", "audit.log");
18927
- if (!import_fs35.default.existsSync(logPath)) {
19111
+ const logPath = import_path37.default.join(import_os32.default.homedir(), ".node9", "audit.log");
19112
+ if (!import_fs36.default.existsSync(logPath)) {
18928
19113
  console.log(
18929
19114
  import_chalk12.default.yellow("No audit logs found. Run node9 with an agent to generate entries.")
18930
19115
  );
18931
19116
  return;
18932
19117
  }
18933
- const raw = import_fs35.default.readFileSync(logPath, "utf-8");
19118
+ const raw = import_fs36.default.readFileSync(logPath, "utf-8");
18934
19119
  const lines = raw.split("\n").filter((l) => l.trim() !== "");
18935
19120
  let entries = lines.flatMap((line) => {
18936
19121
  try {
@@ -18986,9 +19171,9 @@ function registerAuditCommand(program2) {
18986
19171
  var import_chalk13 = __toESM(require("chalk"));
18987
19172
 
18988
19173
  // src/cli/aggregate/report-audit.ts
18989
- var import_fs36 = __toESM(require("fs"));
18990
- var import_os32 = __toESM(require("os"));
18991
- var import_path37 = __toESM(require("path"));
19174
+ var import_fs37 = __toESM(require("fs"));
19175
+ var import_os33 = __toESM(require("os"));
19176
+ var import_path38 = __toESM(require("path"));
18992
19177
  init_costSync();
18993
19178
  init_litellm();
18994
19179
  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;
@@ -19070,8 +19255,8 @@ function getDateRange(period, now) {
19070
19255
  }
19071
19256
  }
19072
19257
  function parseAuditLog(logPath) {
19073
- if (!import_fs36.default.existsSync(logPath)) return [];
19074
- const raw = import_fs36.default.readFileSync(logPath, "utf-8");
19258
+ if (!import_fs37.default.existsSync(logPath)) return [];
19259
+ const raw = import_fs37.default.readFileSync(logPath, "utf-8");
19075
19260
  return raw.split("\n").flatMap((line) => {
19076
19261
  if (!line.trim()) return [];
19077
19262
  try {
@@ -19131,25 +19316,25 @@ function freezeClaudeCost(acc) {
19131
19316
  };
19132
19317
  }
19133
19318
  function processClaudeCostProject(proj, projectsDir, start, end, acc) {
19134
- const projPath = import_path37.default.join(projectsDir, proj);
19319
+ const projPath = import_path38.default.join(projectsDir, proj);
19135
19320
  let files;
19136
19321
  try {
19137
- const stat = import_fs36.default.statSync(projPath);
19322
+ const stat = import_fs37.default.statSync(projPath);
19138
19323
  if (!stat.isDirectory()) return;
19139
- files = import_fs36.default.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
19324
+ files = import_fs37.default.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
19140
19325
  } catch {
19141
19326
  return;
19142
19327
  }
19143
19328
  const startMs = start.getTime();
19144
19329
  for (const file of files) {
19145
- const filePath = import_path37.default.join(projPath, file);
19330
+ const filePath = import_path38.default.join(projPath, file);
19146
19331
  try {
19147
- if (import_fs36.default.statSync(filePath).mtimeMs < startMs) continue;
19332
+ if (import_fs37.default.statSync(filePath).mtimeMs < startMs) continue;
19148
19333
  } catch {
19149
19334
  continue;
19150
19335
  }
19151
19336
  try {
19152
- const raw = import_fs36.default.readFileSync(filePath, "utf-8");
19337
+ const raw = import_fs37.default.readFileSync(filePath, "utf-8");
19153
19338
  for (const line of raw.split("\n")) {
19154
19339
  if (!line.trim()) continue;
19155
19340
  let entry;
@@ -19199,10 +19384,10 @@ function processClaudeCostProject(proj, projectsDir, start, end, acc) {
19199
19384
  }
19200
19385
  function loadClaudeCost(start, end, projectsDir) {
19201
19386
  const acc = emptyClaudeCostAccumulator();
19202
- if (!import_fs36.default.existsSync(projectsDir)) return freezeClaudeCost(acc);
19387
+ if (!import_fs37.default.existsSync(projectsDir)) return freezeClaudeCost(acc);
19203
19388
  let dirs;
19204
19389
  try {
19205
- dirs = import_fs36.default.readdirSync(projectsDir);
19390
+ dirs = import_fs37.default.readdirSync(projectsDir);
19206
19391
  } catch {
19207
19392
  return freezeClaudeCost(acc);
19208
19393
  }
@@ -19214,7 +19399,7 @@ function loadClaudeCost(start, end, projectsDir) {
19214
19399
  function processCodexCostFile(filePath, start, end, acc) {
19215
19400
  let lines;
19216
19401
  try {
19217
- lines = import_fs36.default.readFileSync(filePath, "utf-8").split("\n");
19402
+ lines = import_fs37.default.readFileSync(filePath, "utf-8").split("\n");
19218
19403
  } catch {
19219
19404
  return;
19220
19405
  }
@@ -19259,31 +19444,31 @@ function processCodexCostFile(filePath, start, end, acc) {
19259
19444
  }
19260
19445
  function listCodexSessionFiles(sessionsBase) {
19261
19446
  const jsonlFiles = [];
19262
- if (!import_fs36.default.existsSync(sessionsBase)) return jsonlFiles;
19447
+ if (!import_fs37.default.existsSync(sessionsBase)) return jsonlFiles;
19263
19448
  try {
19264
- for (const year of import_fs36.default.readdirSync(sessionsBase)) {
19265
- const yearPath = import_path37.default.join(sessionsBase, year);
19449
+ for (const year of import_fs37.default.readdirSync(sessionsBase)) {
19450
+ const yearPath = import_path38.default.join(sessionsBase, year);
19266
19451
  try {
19267
- if (!import_fs36.default.statSync(yearPath).isDirectory()) continue;
19452
+ if (!import_fs37.default.statSync(yearPath).isDirectory()) continue;
19268
19453
  } catch {
19269
19454
  continue;
19270
19455
  }
19271
- for (const month of import_fs36.default.readdirSync(yearPath)) {
19272
- const monthPath = import_path37.default.join(yearPath, month);
19456
+ for (const month of import_fs37.default.readdirSync(yearPath)) {
19457
+ const monthPath = import_path38.default.join(yearPath, month);
19273
19458
  try {
19274
- if (!import_fs36.default.statSync(monthPath).isDirectory()) continue;
19459
+ if (!import_fs37.default.statSync(monthPath).isDirectory()) continue;
19275
19460
  } catch {
19276
19461
  continue;
19277
19462
  }
19278
- for (const day of import_fs36.default.readdirSync(monthPath)) {
19279
- const dayPath = import_path37.default.join(monthPath, day);
19463
+ for (const day of import_fs37.default.readdirSync(monthPath)) {
19464
+ const dayPath = import_path38.default.join(monthPath, day);
19280
19465
  try {
19281
- if (!import_fs36.default.statSync(dayPath).isDirectory()) continue;
19466
+ if (!import_fs37.default.statSync(dayPath).isDirectory()) continue;
19282
19467
  } catch {
19283
19468
  continue;
19284
19469
  }
19285
- for (const file of import_fs36.default.readdirSync(dayPath)) {
19286
- if (file.endsWith(".jsonl")) jsonlFiles.push(import_path37.default.join(dayPath, file));
19470
+ for (const file of import_fs37.default.readdirSync(dayPath)) {
19471
+ if (file.endsWith(".jsonl")) jsonlFiles.push(import_path38.default.join(dayPath, file));
19287
19472
  }
19288
19473
  }
19289
19474
  }
@@ -19336,13 +19521,13 @@ function freezeGeminiCost(acc) {
19336
19521
  function processGeminiCostFile(filePath, projectKey, start, end, acc) {
19337
19522
  const startMs = start.getTime();
19338
19523
  try {
19339
- if (import_fs36.default.statSync(filePath).mtimeMs < startMs) return;
19524
+ if (import_fs37.default.statSync(filePath).mtimeMs < startMs) return;
19340
19525
  } catch {
19341
19526
  return;
19342
19527
  }
19343
19528
  let raw;
19344
19529
  try {
19345
- raw = import_fs36.default.readFileSync(filePath, "utf-8");
19530
+ raw = import_fs37.default.readFileSync(filePath, "utf-8");
19346
19531
  } catch {
19347
19532
  return;
19348
19533
  }
@@ -19391,30 +19576,30 @@ function listGeminiSessionFiles(geminiTmpDir) {
19391
19576
  const out = [];
19392
19577
  let dirs;
19393
19578
  try {
19394
- if (!import_fs36.default.statSync(geminiTmpDir).isDirectory()) return out;
19395
- dirs = import_fs36.default.readdirSync(geminiTmpDir);
19579
+ if (!import_fs37.default.statSync(geminiTmpDir).isDirectory()) return out;
19580
+ dirs = import_fs37.default.readdirSync(geminiTmpDir);
19396
19581
  } catch {
19397
19582
  return out;
19398
19583
  }
19399
19584
  for (const proj of dirs) {
19400
- const chatsDir = import_path37.default.join(geminiTmpDir, proj, "chats");
19585
+ const chatsDir = import_path38.default.join(geminiTmpDir, proj, "chats");
19401
19586
  let files;
19402
19587
  try {
19403
- if (!import_fs36.default.statSync(chatsDir).isDirectory()) continue;
19404
- files = import_fs36.default.readdirSync(chatsDir);
19588
+ if (!import_fs37.default.statSync(chatsDir).isDirectory()) continue;
19589
+ files = import_fs37.default.readdirSync(chatsDir);
19405
19590
  } catch {
19406
19591
  continue;
19407
19592
  }
19408
19593
  for (const f of files) {
19409
19594
  if (!f.endsWith(".jsonl")) continue;
19410
- out.push({ projectKey: proj, file: import_path37.default.join(chatsDir, f) });
19595
+ out.push({ projectKey: proj, file: import_path38.default.join(chatsDir, f) });
19411
19596
  }
19412
19597
  }
19413
19598
  return out;
19414
19599
  }
19415
19600
  function loadGeminiCost(start, end, geminiTmpDir) {
19416
19601
  const acc = emptyGeminiAccumulator();
19417
- if (!import_fs36.default.existsSync(geminiTmpDir)) return freezeGeminiCost(acc);
19602
+ if (!import_fs37.default.existsSync(geminiTmpDir)) return freezeGeminiCost(acc);
19418
19603
  for (const { projectKey, file } of listGeminiSessionFiles(geminiTmpDir)) {
19419
19604
  processGeminiCostFile(file, projectKey, start, end, acc);
19420
19605
  }
@@ -19422,11 +19607,11 @@ function loadGeminiCost(start, end, geminiTmpDir) {
19422
19607
  }
19423
19608
  function aggregateReportFromAudit(period, opts = {}) {
19424
19609
  const now = opts.now ?? /* @__PURE__ */ new Date();
19425
- const auditLogPath = opts.auditLogPath ?? import_path37.default.join(import_os32.default.homedir(), ".node9", "audit.log");
19426
- const claudeProjectsDir = opts.claudeProjectsDir ?? import_path37.default.join(import_os32.default.homedir(), ".claude", "projects");
19427
- const codexSessionsDir = opts.codexSessionsDir ?? import_path37.default.join(import_os32.default.homedir(), ".codex", "sessions");
19428
- const geminiTmpDir = opts.geminiTmpDir ?? import_path37.default.join(import_os32.default.homedir(), ".gemini", "tmp");
19429
- const hasAuditFile = import_fs36.default.existsSync(auditLogPath);
19610
+ const auditLogPath = opts.auditLogPath ?? import_path38.default.join(import_os33.default.homedir(), ".node9", "audit.log");
19611
+ const claudeProjectsDir = opts.claudeProjectsDir ?? import_path38.default.join(import_os33.default.homedir(), ".claude", "projects");
19612
+ const codexSessionsDir = opts.codexSessionsDir ?? import_path38.default.join(import_os33.default.homedir(), ".codex", "sessions");
19613
+ const geminiTmpDir = opts.geminiTmpDir ?? import_path38.default.join(import_os33.default.homedir(), ".gemini", "tmp");
19614
+ const hasAuditFile = import_fs37.default.existsSync(auditLogPath);
19430
19615
  const allEntries = opts.preloadedAuditEntries ?? parseAuditLog(auditLogPath);
19431
19616
  const unackedDlp = allEntries.filter((e) => e.source === "response-dlp");
19432
19617
  const { start, end } = getDateRange(period, now);
@@ -20122,14 +20307,14 @@ function registerDaemonCommand(program2) {
20122
20307
 
20123
20308
  // src/cli/commands/status.ts
20124
20309
  var import_chalk15 = __toESM(require("chalk"));
20125
- var import_fs37 = __toESM(require("fs"));
20126
- var import_path38 = __toESM(require("path"));
20127
- var import_os33 = __toESM(require("os"));
20310
+ var import_fs38 = __toESM(require("fs"));
20311
+ var import_path39 = __toESM(require("path"));
20312
+ var import_os34 = __toESM(require("os"));
20128
20313
  init_core();
20129
20314
  init_daemon();
20130
20315
  function readJson2(filePath) {
20131
20316
  try {
20132
- if (import_fs37.default.existsSync(filePath)) return JSON.parse(import_fs37.default.readFileSync(filePath, "utf-8"));
20317
+ if (import_fs38.default.existsSync(filePath)) return JSON.parse(import_fs38.default.readFileSync(filePath, "utf-8"));
20133
20318
  } catch {
20134
20319
  }
20135
20320
  return null;
@@ -20194,28 +20379,28 @@ function registerStatusCommand(program2) {
20194
20379
  console.log("");
20195
20380
  const modeLabel = settings.mode === "audit" ? import_chalk15.default.blue("audit") : settings.mode === "strict" ? import_chalk15.default.red("strict") : import_chalk15.default.white("standard");
20196
20381
  console.log(` Mode: ${modeLabel}`);
20197
- const projectConfig = import_path38.default.join(process.cwd(), "node9.config.json");
20198
- const globalConfig = import_path38.default.join(import_os33.default.homedir(), ".node9", "config.json");
20382
+ const projectConfig = import_path39.default.join(process.cwd(), "node9.config.json");
20383
+ const globalConfig = import_path39.default.join(import_os34.default.homedir(), ".node9", "config.json");
20199
20384
  console.log(
20200
- ` Local: ${import_fs37.default.existsSync(projectConfig) ? import_chalk15.default.green("Active (node9.config.json)") : import_chalk15.default.gray("Not present")}`
20385
+ ` Local: ${import_fs38.default.existsSync(projectConfig) ? import_chalk15.default.green("Active (node9.config.json)") : import_chalk15.default.gray("Not present")}`
20201
20386
  );
20202
20387
  console.log(
20203
- ` Global: ${import_fs37.default.existsSync(globalConfig) ? import_chalk15.default.green("Active (~/.node9/config.json)") : import_chalk15.default.gray("Not present")}`
20388
+ ` Global: ${import_fs38.default.existsSync(globalConfig) ? import_chalk15.default.green("Active (~/.node9/config.json)") : import_chalk15.default.gray("Not present")}`
20204
20389
  );
20205
20390
  if (mergedConfig.policy.sandboxPaths.length > 0) {
20206
20391
  console.log(
20207
20392
  ` Sandbox: ${import_chalk15.default.green(`${mergedConfig.policy.sandboxPaths.length} safe zones active`)}`
20208
20393
  );
20209
20394
  }
20210
- const homeDir2 = import_os33.default.homedir();
20395
+ const homeDir2 = import_os34.default.homedir();
20211
20396
  const claudeSettings = readJson2(
20212
- import_path38.default.join(homeDir2, ".claude", "settings.json")
20397
+ import_path39.default.join(homeDir2, ".claude", "settings.json")
20213
20398
  );
20214
- const claudeConfig = readJson2(import_path38.default.join(homeDir2, ".claude.json"));
20399
+ const claudeConfig = readJson2(import_path39.default.join(homeDir2, ".claude.json"));
20215
20400
  const geminiSettings = readJson2(
20216
- import_path38.default.join(homeDir2, ".gemini", "settings.json")
20401
+ import_path39.default.join(homeDir2, ".gemini", "settings.json")
20217
20402
  );
20218
- const cursorConfig = readJson2(import_path38.default.join(homeDir2, ".cursor", "mcp.json"));
20403
+ const cursorConfig = readJson2(import_path39.default.join(homeDir2, ".cursor", "mcp.json"));
20219
20404
  const agentFound = claudeSettings || claudeConfig || geminiSettings || cursorConfig;
20220
20405
  if (agentFound) {
20221
20406
  console.log("");
@@ -20274,9 +20459,9 @@ function registerStatusCommand(program2) {
20274
20459
 
20275
20460
  // src/cli/commands/init.ts
20276
20461
  var import_chalk16 = __toESM(require("chalk"));
20277
- var import_fs38 = __toESM(require("fs"));
20278
- var import_path39 = __toESM(require("path"));
20279
- var import_os34 = __toESM(require("os"));
20462
+ var import_fs39 = __toESM(require("fs"));
20463
+ var import_path40 = __toESM(require("path"));
20464
+ var import_os35 = __toESM(require("os"));
20280
20465
  var import_https4 = __toESM(require("https"));
20281
20466
  init_core();
20282
20467
  init_setup();
@@ -20366,16 +20551,16 @@ function registerInitCommand(program2) {
20366
20551
  }
20367
20552
  console.log("");
20368
20553
  }
20369
- const configPath = import_path39.default.join(import_os34.default.homedir(), ".node9", "config.json");
20370
- const isFirstInstall = !import_fs38.default.existsSync(configPath);
20371
- if (import_fs38.default.existsSync(configPath) && !options.force) {
20554
+ const configPath = import_path40.default.join(import_os35.default.homedir(), ".node9", "config.json");
20555
+ const isFirstInstall = !import_fs39.default.existsSync(configPath);
20556
+ if (import_fs39.default.existsSync(configPath) && !options.force) {
20372
20557
  try {
20373
- const existing = JSON.parse(import_fs38.default.readFileSync(configPath, "utf-8"));
20558
+ const existing = JSON.parse(import_fs39.default.readFileSync(configPath, "utf-8"));
20374
20559
  const settings = existing.settings ?? {};
20375
20560
  if (settings.mode !== chosenMode) {
20376
20561
  settings.mode = chosenMode;
20377
20562
  existing.settings = settings;
20378
- import_fs38.default.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
20563
+ import_fs39.default.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
20379
20564
  console.log(import_chalk16.default.green(`\u2705 Mode updated: ${chosenMode}`));
20380
20565
  } else {
20381
20566
  console.log(import_chalk16.default.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
@@ -20388,9 +20573,9 @@ function registerInitCommand(program2) {
20388
20573
  ...DEFAULT_CONFIG,
20389
20574
  settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
20390
20575
  };
20391
- const dir = import_path39.default.dirname(configPath);
20392
- if (!import_fs38.default.existsSync(dir)) import_fs38.default.mkdirSync(dir, { recursive: true });
20393
- import_fs38.default.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
20576
+ const dir = import_path40.default.dirname(configPath);
20577
+ if (!import_fs39.default.existsSync(dir)) import_fs39.default.mkdirSync(dir, { recursive: true });
20578
+ import_fs39.default.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
20394
20579
  console.log(import_chalk16.default.green(`\u2705 Config created: ${configPath}`));
20395
20580
  console.log(import_chalk16.default.gray(` Mode: ${chosenMode}`));
20396
20581
  }
@@ -20495,7 +20680,7 @@ function registerInitCommand(program2) {
20495
20680
  }
20496
20681
 
20497
20682
  // src/cli/commands/undo.ts
20498
- var import_path40 = __toESM(require("path"));
20683
+ var import_path41 = __toESM(require("path"));
20499
20684
  var import_chalk18 = __toESM(require("chalk"));
20500
20685
 
20501
20686
  // src/tui/undo-navigator.ts
@@ -20654,7 +20839,7 @@ function findMatchingCwd(startDir, history) {
20654
20839
  let dir = startDir;
20655
20840
  while (true) {
20656
20841
  if (cwds.has(dir)) return dir;
20657
- const parent = import_path40.default.dirname(dir);
20842
+ const parent = import_path41.default.dirname(dir);
20658
20843
  if (parent === dir) return null;
20659
20844
  dir = parent;
20660
20845
  }
@@ -21230,9 +21415,9 @@ function registerMcpGatewayCommand(program2) {
21230
21415
 
21231
21416
  // src/mcp-server/index.ts
21232
21417
  var import_readline5 = __toESM(require("readline"));
21233
- var import_fs39 = __toESM(require("fs"));
21234
- var import_os35 = __toESM(require("os"));
21235
- var import_path41 = __toESM(require("path"));
21418
+ var import_fs40 = __toESM(require("fs"));
21419
+ var import_os36 = __toESM(require("os"));
21420
+ var import_path42 = __toESM(require("path"));
21236
21421
  var import_child_process11 = require("child_process");
21237
21422
  init_core();
21238
21423
  init_daemon();
@@ -21483,13 +21668,13 @@ function handleStatus() {
21483
21668
  lines.push(`Active shields: ${activeShields.length > 0 ? activeShields.join(", ") : "none"}`);
21484
21669
  lines.push(`Smart rules: ${config.policy.smartRules.length} loaded`);
21485
21670
  lines.push(`DLP: ${config.policy.dlp?.enabled !== false ? "enabled" : "disabled"}`);
21486
- const projectConfig = import_path41.default.join(process.cwd(), "node9.config.json");
21487
- const globalConfig = import_path41.default.join(import_os35.default.homedir(), ".node9", "config.json");
21671
+ const projectConfig = import_path42.default.join(process.cwd(), "node9.config.json");
21672
+ const globalConfig = import_path42.default.join(import_os36.default.homedir(), ".node9", "config.json");
21488
21673
  lines.push(
21489
- `Project config (node9.config.json): ${import_fs39.default.existsSync(projectConfig) ? "present" : "not found"}`
21674
+ `Project config (node9.config.json): ${import_fs40.default.existsSync(projectConfig) ? "present" : "not found"}`
21490
21675
  );
21491
21676
  lines.push(
21492
- `Global config (~/.node9/config.json): ${import_fs39.default.existsSync(globalConfig) ? "present" : "not found"}`
21677
+ `Global config (~/.node9/config.json): ${import_fs40.default.existsSync(globalConfig) ? "present" : "not found"}`
21493
21678
  );
21494
21679
  return lines.join("\n");
21495
21680
  }
@@ -21563,21 +21748,21 @@ function handleShieldDisable(args) {
21563
21748
  writeActiveShields(active.filter((s) => s !== name));
21564
21749
  return `Shield "${name}" disabled.`;
21565
21750
  }
21566
- var GLOBAL_CONFIG_PATH = import_path41.default.join(import_os35.default.homedir(), ".node9", "config.json");
21751
+ var GLOBAL_CONFIG_PATH = import_path42.default.join(import_os36.default.homedir(), ".node9", "config.json");
21567
21752
  var APPROVER_CHANNELS = ["native", "browser", "cloud", "terminal"];
21568
21753
  function readGlobalConfigRaw() {
21569
21754
  try {
21570
- if (import_fs39.default.existsSync(GLOBAL_CONFIG_PATH)) {
21571
- return JSON.parse(import_fs39.default.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
21755
+ if (import_fs40.default.existsSync(GLOBAL_CONFIG_PATH)) {
21756
+ return JSON.parse(import_fs40.default.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
21572
21757
  }
21573
21758
  } catch {
21574
21759
  }
21575
21760
  return {};
21576
21761
  }
21577
21762
  function writeGlobalConfigRaw(data) {
21578
- const dir = import_path41.default.dirname(GLOBAL_CONFIG_PATH);
21579
- if (!import_fs39.default.existsSync(dir)) import_fs39.default.mkdirSync(dir, { recursive: true });
21580
- import_fs39.default.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(data, null, 2) + "\n");
21763
+ const dir = import_path42.default.dirname(GLOBAL_CONFIG_PATH);
21764
+ if (!import_fs40.default.existsSync(dir)) import_fs40.default.mkdirSync(dir, { recursive: true });
21765
+ import_fs40.default.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(data, null, 2) + "\n");
21581
21766
  }
21582
21767
  function handleApproverList() {
21583
21768
  const config = getConfig();
@@ -21621,9 +21806,9 @@ function handleApproverSet(args) {
21621
21806
  function handleAuditGet(args) {
21622
21807
  const limit = Math.min(typeof args.limit === "number" ? args.limit : 20, 100);
21623
21808
  const filter = typeof args.filter === "string" && args.filter !== "all" ? args.filter : null;
21624
- const auditPath = import_path41.default.join(import_os35.default.homedir(), ".node9", "audit.log");
21625
- if (!import_fs39.default.existsSync(auditPath)) return "No audit log found.";
21626
- const rawLines = import_fs39.default.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
21809
+ const auditPath = import_path42.default.join(import_os36.default.homedir(), ".node9", "audit.log");
21810
+ if (!import_fs40.default.existsSync(auditPath)) return "No audit log found.";
21811
+ const rawLines = import_fs40.default.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
21627
21812
  const parsed = [];
21628
21813
  for (const line of rawLines) {
21629
21814
  try {
@@ -21958,7 +22143,7 @@ function registerTrustCommand(program2) {
21958
22143
  // src/cli/commands/mcp-pin.ts
21959
22144
  var import_chalk21 = __toESM(require("chalk"));
21960
22145
  init_mcp_pin();
21961
- var import_fs40 = __toESM(require("fs"));
22146
+ var import_fs41 = __toESM(require("fs"));
21962
22147
  function registerMcpPinCommand(program2) {
21963
22148
  const pinCmd = program2.command("mcp").description("Manage MCP server tool definition pinning (rug pull defense)");
21964
22149
  const pinSubCmd = pinCmd.command("pin").description("Manage pinned MCP server tool definitions");
@@ -21969,7 +22154,7 @@ function registerMcpPinCommand(program2) {
21969
22154
  let repoCorrupt = false;
21970
22155
  if (found.source === "repo") {
21971
22156
  try {
21972
- const raw = import_fs40.default.readFileSync(found.path, "utf-8");
22157
+ const raw = import_fs41.default.readFileSync(found.path, "utf-8");
21973
22158
  const parsed = JSON.parse(raw);
21974
22159
  repoEntries = parsed.servers ?? {};
21975
22160
  } catch {
@@ -22282,9 +22467,9 @@ init_scan();
22282
22467
 
22283
22468
  // src/cli/commands/sessions.ts
22284
22469
  var import_chalk24 = __toESM(require("chalk"));
22285
- var import_fs41 = __toESM(require("fs"));
22286
- var import_path42 = __toESM(require("path"));
22287
- var import_os36 = __toESM(require("os"));
22470
+ var import_fs42 = __toESM(require("fs"));
22471
+ var import_path43 = __toESM(require("path"));
22472
+ var import_os37 = __toESM(require("os"));
22288
22473
  init_scan_summary();
22289
22474
  var CLAUDE_PRICING3 = {
22290
22475
  "claude-opus-4-6": { i: 5e-6, o: 25e-6, cw: 625e-8, cr: 5e-7 },
@@ -22326,10 +22511,10 @@ function encodeProjectPath(projectPath) {
22326
22511
  }
22327
22512
  function sessionJsonlPath(projectPath, sessionId) {
22328
22513
  const encoded = encodeProjectPath(projectPath);
22329
- return import_path42.default.join(import_os36.default.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
22514
+ return import_path43.default.join(import_os37.default.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
22330
22515
  }
22331
22516
  function projectLabel(projectPath) {
22332
- return projectPath.replace(import_os36.default.homedir(), "~");
22517
+ return projectPath.replace(import_os37.default.homedir(), "~");
22333
22518
  }
22334
22519
  function parseHistoryLines(lines) {
22335
22520
  const entries = [];
@@ -22398,10 +22583,10 @@ function parseSessionLines(lines) {
22398
22583
  return { toolCalls, costUSD, hasSnapshot, modifiedFiles };
22399
22584
  }
22400
22585
  function loadAuditEntries(auditPath) {
22401
- const aPath = auditPath ?? import_path42.default.join(import_os36.default.homedir(), ".node9", "audit.log");
22586
+ const aPath = auditPath ?? import_path43.default.join(import_os37.default.homedir(), ".node9", "audit.log");
22402
22587
  let raw;
22403
22588
  try {
22404
- raw = import_fs41.default.readFileSync(aPath, "utf-8");
22589
+ raw = import_fs42.default.readFileSync(aPath, "utf-8");
22405
22590
  } catch {
22406
22591
  return [];
22407
22592
  }
@@ -22437,8 +22622,8 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
22437
22622
  return result;
22438
22623
  }
22439
22624
  function buildGeminiSessions(days, allAuditEntries) {
22440
- const tmpDir = import_path42.default.join(import_os36.default.homedir(), ".gemini", "tmp");
22441
- if (!import_fs41.default.existsSync(tmpDir)) return [];
22625
+ const tmpDir = import_path43.default.join(import_os37.default.homedir(), ".gemini", "tmp");
22626
+ if (!import_fs42.default.existsSync(tmpDir)) return [];
22442
22627
  const cutoff = days !== null ? (() => {
22443
22628
  const d = /* @__PURE__ */ new Date();
22444
22629
  d.setDate(d.getDate() - days);
@@ -22447,35 +22632,35 @@ function buildGeminiSessions(days, allAuditEntries) {
22447
22632
  })() : null;
22448
22633
  let slugDirs;
22449
22634
  try {
22450
- slugDirs = import_fs41.default.readdirSync(tmpDir);
22635
+ slugDirs = import_fs42.default.readdirSync(tmpDir);
22451
22636
  } catch {
22452
22637
  return [];
22453
22638
  }
22454
22639
  const summaries = [];
22455
22640
  for (const slug of slugDirs) {
22456
- const slugPath = import_path42.default.join(tmpDir, slug);
22641
+ const slugPath = import_path43.default.join(tmpDir, slug);
22457
22642
  try {
22458
- if (!import_fs41.default.statSync(slugPath).isDirectory()) continue;
22643
+ if (!import_fs42.default.statSync(slugPath).isDirectory()) continue;
22459
22644
  } catch {
22460
22645
  continue;
22461
22646
  }
22462
- let projectRoot = import_path42.default.join(import_os36.default.homedir(), slug);
22647
+ let projectRoot = import_path43.default.join(import_os37.default.homedir(), slug);
22463
22648
  try {
22464
- projectRoot = import_fs41.default.readFileSync(import_path42.default.join(slugPath, ".project_root"), "utf-8").trim();
22649
+ projectRoot = import_fs42.default.readFileSync(import_path43.default.join(slugPath, ".project_root"), "utf-8").trim();
22465
22650
  } catch {
22466
22651
  }
22467
- const chatsDir = import_path42.default.join(slugPath, "chats");
22468
- if (!import_fs41.default.existsSync(chatsDir)) continue;
22652
+ const chatsDir = import_path43.default.join(slugPath, "chats");
22653
+ if (!import_fs42.default.existsSync(chatsDir)) continue;
22469
22654
  let chatFiles;
22470
22655
  try {
22471
- chatFiles = import_fs41.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
22656
+ chatFiles = import_fs42.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
22472
22657
  } catch {
22473
22658
  continue;
22474
22659
  }
22475
22660
  for (const chatFile of chatFiles) {
22476
22661
  let raw;
22477
22662
  try {
22478
- raw = import_fs41.default.readFileSync(import_path42.default.join(chatsDir, chatFile), "utf-8");
22663
+ raw = import_fs42.default.readFileSync(import_path43.default.join(chatsDir, chatFile), "utf-8");
22479
22664
  } catch {
22480
22665
  continue;
22481
22666
  }
@@ -22555,8 +22740,8 @@ function buildGeminiSessions(days, allAuditEntries) {
22555
22740
  return summaries;
22556
22741
  }
22557
22742
  function buildCodexSessions(days, allAuditEntries) {
22558
- const sessionsBase = import_path42.default.join(import_os36.default.homedir(), ".codex", "sessions");
22559
- if (!import_fs41.default.existsSync(sessionsBase)) return [];
22743
+ const sessionsBase = import_path43.default.join(import_os37.default.homedir(), ".codex", "sessions");
22744
+ if (!import_fs42.default.existsSync(sessionsBase)) return [];
22560
22745
  const cutoff = days !== null ? (() => {
22561
22746
  const d = /* @__PURE__ */ new Date();
22562
22747
  d.setDate(d.getDate() - days);
@@ -22565,29 +22750,29 @@ function buildCodexSessions(days, allAuditEntries) {
22565
22750
  })() : null;
22566
22751
  const jsonlFiles = [];
22567
22752
  try {
22568
- for (const year of import_fs41.default.readdirSync(sessionsBase)) {
22569
- const yearPath = import_path42.default.join(sessionsBase, year);
22753
+ for (const year of import_fs42.default.readdirSync(sessionsBase)) {
22754
+ const yearPath = import_path43.default.join(sessionsBase, year);
22570
22755
  try {
22571
- if (!import_fs41.default.statSync(yearPath).isDirectory()) continue;
22756
+ if (!import_fs42.default.statSync(yearPath).isDirectory()) continue;
22572
22757
  } catch {
22573
22758
  continue;
22574
22759
  }
22575
- for (const month of import_fs41.default.readdirSync(yearPath)) {
22576
- const monthPath = import_path42.default.join(yearPath, month);
22760
+ for (const month of import_fs42.default.readdirSync(yearPath)) {
22761
+ const monthPath = import_path43.default.join(yearPath, month);
22577
22762
  try {
22578
- if (!import_fs41.default.statSync(monthPath).isDirectory()) continue;
22763
+ if (!import_fs42.default.statSync(monthPath).isDirectory()) continue;
22579
22764
  } catch {
22580
22765
  continue;
22581
22766
  }
22582
- for (const day of import_fs41.default.readdirSync(monthPath)) {
22583
- const dayPath = import_path42.default.join(monthPath, day);
22767
+ for (const day of import_fs42.default.readdirSync(monthPath)) {
22768
+ const dayPath = import_path43.default.join(monthPath, day);
22584
22769
  try {
22585
- if (!import_fs41.default.statSync(dayPath).isDirectory()) continue;
22770
+ if (!import_fs42.default.statSync(dayPath).isDirectory()) continue;
22586
22771
  } catch {
22587
22772
  continue;
22588
22773
  }
22589
- for (const file of import_fs41.default.readdirSync(dayPath)) {
22590
- if (file.endsWith(".jsonl")) jsonlFiles.push(import_path42.default.join(dayPath, file));
22774
+ for (const file of import_fs42.default.readdirSync(dayPath)) {
22775
+ if (file.endsWith(".jsonl")) jsonlFiles.push(import_path43.default.join(dayPath, file));
22591
22776
  }
22592
22777
  }
22593
22778
  }
@@ -22599,7 +22784,7 @@ function buildCodexSessions(days, allAuditEntries) {
22599
22784
  for (const filePath of jsonlFiles) {
22600
22785
  let lines;
22601
22786
  try {
22602
- lines = import_fs41.default.readFileSync(filePath, "utf-8").split("\n");
22787
+ lines = import_fs42.default.readFileSync(filePath, "utf-8").split("\n");
22603
22788
  } catch {
22604
22789
  continue;
22605
22790
  }
@@ -22677,10 +22862,10 @@ function buildCodexSessions(days, allAuditEntries) {
22677
22862
  return summaries;
22678
22863
  }
22679
22864
  function buildSessions(days, historyPath) {
22680
- const hPath = historyPath ?? import_path42.default.join(import_os36.default.homedir(), ".claude", "history.jsonl");
22865
+ const hPath = historyPath ?? import_path43.default.join(import_os37.default.homedir(), ".claude", "history.jsonl");
22681
22866
  let historyRaw;
22682
22867
  try {
22683
- historyRaw = import_fs41.default.readFileSync(hPath, "utf-8");
22868
+ historyRaw = import_fs42.default.readFileSync(hPath, "utf-8");
22684
22869
  } catch {
22685
22870
  return [];
22686
22871
  }
@@ -22705,7 +22890,7 @@ function buildSessions(days, historyPath) {
22705
22890
  const jsonlFile = sessionJsonlPath(entry.project, entry.sessionId);
22706
22891
  let sessionLines = [];
22707
22892
  try {
22708
- sessionLines = import_fs41.default.readFileSync(jsonlFile, "utf-8").split("\n");
22893
+ sessionLines = import_fs42.default.readFileSync(jsonlFile, "utf-8").split("\n");
22709
22894
  } catch {
22710
22895
  }
22711
22896
  const { toolCalls, costUSD, hasSnapshot, modifiedFiles } = parseSessionLines(sessionLines);
@@ -22973,8 +23158,8 @@ function registerSessionsCommand(program2) {
22973
23158
  console.log("");
22974
23159
  console.log(import_chalk24.default.cyan.bold("\u{1F4CB} node9 sessions") + import_chalk24.default.dim(" \u2014 what your AI agent did"));
22975
23160
  console.log("");
22976
- const historyPath = import_path42.default.join(import_os36.default.homedir(), ".claude", "history.jsonl");
22977
- if (!import_fs41.default.existsSync(historyPath)) {
23161
+ const historyPath = import_path43.default.join(import_os37.default.homedir(), ".claude", "history.jsonl");
23162
+ if (!import_fs42.default.existsSync(historyPath)) {
22978
23163
  console.log(import_chalk24.default.yellow(" No Claude session history found at ~/.claude/history.jsonl"));
22979
23164
  console.log(import_chalk24.default.gray(" Install Claude Code, run a few sessions, then try again.\n"));
22980
23165
  return;
@@ -23011,12 +23196,12 @@ function registerSessionsCommand(program2) {
23011
23196
 
23012
23197
  // src/cli/commands/skill-pin.ts
23013
23198
  var import_chalk25 = __toESM(require("chalk"));
23014
- var import_fs42 = __toESM(require("fs"));
23015
- var import_os37 = __toESM(require("os"));
23016
- var import_path43 = __toESM(require("path"));
23199
+ var import_fs43 = __toESM(require("fs"));
23200
+ var import_os38 = __toESM(require("os"));
23201
+ var import_path44 = __toESM(require("path"));
23017
23202
  function wipeSkillSessions() {
23018
23203
  try {
23019
- import_fs42.default.rmSync(import_path43.default.join(import_os37.default.homedir(), ".node9", "skill-sessions"), {
23204
+ import_fs43.default.rmSync(import_path44.default.join(import_os38.default.homedir(), ".node9", "skill-sessions"), {
23020
23205
  recursive: true,
23021
23206
  force: true
23022
23207
  });
@@ -23098,15 +23283,15 @@ function registerSkillPinCommand(program2) {
23098
23283
  }
23099
23284
 
23100
23285
  // src/cli/commands/decisions.ts
23101
- var import_fs43 = __toESM(require("fs"));
23102
- var import_os38 = __toESM(require("os"));
23103
- var import_path44 = __toESM(require("path"));
23286
+ var import_fs44 = __toESM(require("fs"));
23287
+ var import_os39 = __toESM(require("os"));
23288
+ var import_path45 = __toESM(require("path"));
23104
23289
  var import_chalk26 = __toESM(require("chalk"));
23105
- var DECISIONS_FILE2 = import_path44.default.join(import_os38.default.homedir(), ".node9", "decisions.json");
23290
+ var DECISIONS_FILE2 = import_path45.default.join(import_os39.default.homedir(), ".node9", "decisions.json");
23106
23291
  function readDecisions() {
23107
23292
  try {
23108
- if (!import_fs43.default.existsSync(DECISIONS_FILE2)) return {};
23109
- const raw = import_fs43.default.readFileSync(DECISIONS_FILE2, "utf-8");
23293
+ if (!import_fs44.default.existsSync(DECISIONS_FILE2)) return {};
23294
+ const raw = import_fs44.default.readFileSync(DECISIONS_FILE2, "utf-8");
23110
23295
  const parsed = JSON.parse(raw);
23111
23296
  const out = {};
23112
23297
  for (const [k, v] of Object.entries(parsed)) {
@@ -23118,11 +23303,11 @@ function readDecisions() {
23118
23303
  }
23119
23304
  }
23120
23305
  function writeDecisions(d) {
23121
- const dir = import_path44.default.dirname(DECISIONS_FILE2);
23122
- if (!import_fs43.default.existsSync(dir)) import_fs43.default.mkdirSync(dir, { recursive: true });
23306
+ const dir = import_path45.default.dirname(DECISIONS_FILE2);
23307
+ if (!import_fs44.default.existsSync(dir)) import_fs44.default.mkdirSync(dir, { recursive: true });
23123
23308
  const tmp = `${DECISIONS_FILE2}.${process.pid}.tmp`;
23124
- import_fs43.default.writeFileSync(tmp, JSON.stringify(d, null, 2));
23125
- import_fs43.default.renameSync(tmp, DECISIONS_FILE2);
23309
+ import_fs44.default.writeFileSync(tmp, JSON.stringify(d, null, 2));
23310
+ import_fs44.default.renameSync(tmp, DECISIONS_FILE2);
23126
23311
  }
23127
23312
  function registerDecisionsCommand(program2) {
23128
23313
  const cmd = program2.command("decisions").description('Manage persistent "Always Allow" / "Always Deny" tool decisions');
@@ -23179,18 +23364,18 @@ Persistent decisions (${entries.length})
23179
23364
 
23180
23365
  // src/cli/commands/dlp.ts
23181
23366
  var import_chalk27 = __toESM(require("chalk"));
23182
- var import_fs44 = __toESM(require("fs"));
23183
- var import_path45 = __toESM(require("path"));
23184
- var import_os39 = __toESM(require("os"));
23185
- var AUDIT_LOG = import_path45.default.join(import_os39.default.homedir(), ".node9", "audit.log");
23186
- var RESOLVED_FILE = import_path45.default.join(import_os39.default.homedir(), ".node9", "dlp-resolved.json");
23367
+ var import_fs45 = __toESM(require("fs"));
23368
+ var import_path46 = __toESM(require("path"));
23369
+ var import_os40 = __toESM(require("os"));
23370
+ var AUDIT_LOG = import_path46.default.join(import_os40.default.homedir(), ".node9", "audit.log");
23371
+ var RESOLVED_FILE = import_path46.default.join(import_os40.default.homedir(), ".node9", "dlp-resolved.json");
23187
23372
  var ANSI_RE = /\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g;
23188
23373
  function stripAnsi(s) {
23189
23374
  return s.replace(ANSI_RE, "");
23190
23375
  }
23191
23376
  function loadResolved() {
23192
23377
  try {
23193
- const raw = JSON.parse(import_fs44.default.readFileSync(RESOLVED_FILE, "utf-8"));
23378
+ const raw = JSON.parse(import_fs45.default.readFileSync(RESOLVED_FILE, "utf-8"));
23194
23379
  return new Set(raw);
23195
23380
  } catch {
23196
23381
  return /* @__PURE__ */ new Set();
@@ -23198,13 +23383,13 @@ function loadResolved() {
23198
23383
  }
23199
23384
  function saveResolved(resolved) {
23200
23385
  try {
23201
- import_fs44.default.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
23386
+ import_fs45.default.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
23202
23387
  } catch {
23203
23388
  }
23204
23389
  }
23205
23390
  function loadDlpFindings() {
23206
- if (!import_fs44.default.existsSync(AUDIT_LOG)) return [];
23207
- return import_fs44.default.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
23391
+ if (!import_fs45.default.existsSync(AUDIT_LOG)) return [];
23392
+ return import_fs45.default.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
23208
23393
  if (!line.trim()) return [];
23209
23394
  try {
23210
23395
  const e = JSON.parse(line);
@@ -23302,15 +23487,15 @@ function registerDlpCommand(program2) {
23302
23487
 
23303
23488
  // src/cli/commands/mask.ts
23304
23489
  var import_chalk28 = __toESM(require("chalk"));
23305
- var import_fs45 = __toESM(require("fs"));
23306
- var import_path46 = __toESM(require("path"));
23307
- var import_os40 = __toESM(require("os"));
23490
+ var import_fs46 = __toESM(require("fs"));
23491
+ var import_path47 = __toESM(require("path"));
23492
+ var import_os41 = __toESM(require("os"));
23308
23493
  init_dlp();
23309
23494
  function findJsonlFiles(dir) {
23310
23495
  const results = [];
23311
- if (!import_fs45.default.existsSync(dir)) return results;
23312
- for (const entry of import_fs45.default.readdirSync(dir, { withFileTypes: true })) {
23313
- const full = import_path46.default.join(dir, entry.name);
23496
+ if (!import_fs46.default.existsSync(dir)) return results;
23497
+ for (const entry of import_fs46.default.readdirSync(dir, { withFileTypes: true })) {
23498
+ const full = import_path47.default.join(dir, entry.name);
23314
23499
  if (entry.isDirectory()) results.push(...findJsonlFiles(full));
23315
23500
  else if (entry.isFile() && entry.name.endsWith(".jsonl")) results.push(full);
23316
23501
  }
@@ -23353,7 +23538,7 @@ function redactJson(obj) {
23353
23538
  function processFile(filePath, dryRun) {
23354
23539
  let raw;
23355
23540
  try {
23356
- raw = import_fs45.default.readFileSync(filePath, "utf-8");
23541
+ raw = import_fs46.default.readFileSync(filePath, "utf-8");
23357
23542
  } catch {
23358
23543
  return { redactedLines: 0, patterns: [] };
23359
23544
  }
@@ -23385,14 +23570,14 @@ function processFile(filePath, dryRun) {
23385
23570
  }
23386
23571
  }
23387
23572
  if (!dryRun && redactedLines > 0) {
23388
- import_fs45.default.writeFileSync(filePath, newLines.join("\n"), "utf-8");
23573
+ import_fs46.default.writeFileSync(filePath, newLines.join("\n"), "utf-8");
23389
23574
  }
23390
23575
  return { redactedLines, patterns };
23391
23576
  }
23392
23577
  function processJsonFile(filePath, dryRun) {
23393
23578
  let raw;
23394
23579
  try {
23395
- raw = import_fs45.default.readFileSync(filePath, "utf-8");
23580
+ raw = import_fs46.default.readFileSync(filePath, "utf-8");
23396
23581
  } catch {
23397
23582
  return { redactedLines: 0, patterns: [] };
23398
23583
  }
@@ -23405,15 +23590,15 @@ function processJsonFile(filePath, dryRun) {
23405
23590
  const { value, modified, found } = redactJson(parsed);
23406
23591
  if (!modified) return { redactedLines: 0, patterns: [] };
23407
23592
  if (!dryRun) {
23408
- import_fs45.default.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
23593
+ import_fs46.default.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
23409
23594
  }
23410
23595
  return { redactedLines: 1, patterns: found };
23411
23596
  }
23412
23597
  function findJsonFiles(dir) {
23413
23598
  const results = [];
23414
- if (!import_fs45.default.existsSync(dir)) return results;
23415
- for (const entry of import_fs45.default.readdirSync(dir, { withFileTypes: true })) {
23416
- const full = import_path46.default.join(dir, entry.name);
23599
+ if (!import_fs46.default.existsSync(dir)) return results;
23600
+ for (const entry of import_fs46.default.readdirSync(dir, { withFileTypes: true })) {
23601
+ const full = import_path47.default.join(dir, entry.name);
23417
23602
  if (entry.isDirectory()) results.push(...findJsonFiles(full));
23418
23603
  else if (entry.isFile() && entry.name.endsWith(".json")) results.push(full);
23419
23604
  }
@@ -23422,9 +23607,9 @@ function findJsonFiles(dir) {
23422
23607
  function registerMaskCommand(program2) {
23423
23608
  program2.command("mask").description("Redact plaintext secrets from local AI session history files").option("--dry-run", "show what would be redacted without making changes").option("--all", "scan all history (default: last 30 days)").action(async (options) => {
23424
23609
  const dryRun = !!options.dryRun;
23425
- const home = import_os40.default.homedir();
23426
- const claudeDir = import_path46.default.join(home, ".claude", "projects");
23427
- const geminiDir = import_path46.default.join(home, ".gemini", "tmp");
23610
+ const home = import_os41.default.homedir();
23611
+ const claudeDir = import_path47.default.join(home, ".claude", "projects");
23612
+ const geminiDir = import_path47.default.join(home, ".gemini", "tmp");
23428
23613
  const allFiles = [
23429
23614
  ...findJsonlFiles(claudeDir).map((p) => ({ path: p, type: "jsonl" })),
23430
23615
  ...findJsonFiles(geminiDir).map((p) => ({ path: p, type: "json" }))
@@ -23432,7 +23617,7 @@ function registerMaskCommand(program2) {
23432
23617
  const cutoff = options.all ? null : new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
23433
23618
  const filtered = cutoff ? allFiles.filter((f) => {
23434
23619
  try {
23435
- return import_fs45.default.statSync(f.path).mtime >= cutoff;
23620
+ return import_fs46.default.statSync(f.path).mtime >= cutoff;
23436
23621
  } catch {
23437
23622
  return false;
23438
23623
  }
@@ -23488,20 +23673,20 @@ function registerMaskCommand(program2) {
23488
23673
  // src/cli.ts
23489
23674
  init_blast();
23490
23675
  var { version } = JSON.parse(
23491
- import_fs48.default.readFileSync(import_path49.default.join(__dirname, "../package.json"), "utf-8")
23676
+ import_fs49.default.readFileSync(import_path50.default.join(__dirname, "../package.json"), "utf-8")
23492
23677
  );
23493
23678
  var program = new import_commander.Command();
23494
23679
  program.name("node9").description("The Sudo Command for AI Agents").version(version);
23495
23680
  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) => {
23496
23681
  const DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept";
23497
- const credPath = import_path49.default.join(import_os43.default.homedir(), ".node9", "credentials.json");
23498
- if (!import_fs48.default.existsSync(import_path49.default.dirname(credPath)))
23499
- import_fs48.default.mkdirSync(import_path49.default.dirname(credPath), { recursive: true });
23682
+ const credPath = import_path50.default.join(import_os44.default.homedir(), ".node9", "credentials.json");
23683
+ if (!import_fs49.default.existsSync(import_path50.default.dirname(credPath)))
23684
+ import_fs49.default.mkdirSync(import_path50.default.dirname(credPath), { recursive: true });
23500
23685
  const profileName = options.profile || "default";
23501
23686
  let existingCreds = {};
23502
23687
  try {
23503
- if (import_fs48.default.existsSync(credPath)) {
23504
- const raw = JSON.parse(import_fs48.default.readFileSync(credPath, "utf-8"));
23688
+ if (import_fs49.default.existsSync(credPath)) {
23689
+ const raw = JSON.parse(import_fs49.default.readFileSync(credPath, "utf-8"));
23505
23690
  if (raw.apiKey) {
23506
23691
  existingCreds = {
23507
23692
  default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL2 }
@@ -23513,13 +23698,14 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
23513
23698
  } catch {
23514
23699
  }
23515
23700
  existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL2 };
23516
- import_fs48.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
23701
+ import_fs49.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
23702
+ let effectiveCloud = null;
23517
23703
  if (profileName === "default") {
23518
- const configPath = import_path49.default.join(import_os43.default.homedir(), ".node9", "config.json");
23704
+ const configPath = import_path50.default.join(import_os44.default.homedir(), ".node9", "config.json");
23519
23705
  let config = {};
23520
23706
  try {
23521
- if (import_fs48.default.existsSync(configPath))
23522
- config = JSON.parse(import_fs48.default.readFileSync(configPath, "utf-8"));
23707
+ if (import_fs49.default.existsSync(configPath))
23708
+ config = JSON.parse(import_fs49.default.readFileSync(configPath, "utf-8"));
23523
23709
  } catch {
23524
23710
  }
23525
23711
  if (!config.settings || typeof config.settings !== "object") config.settings = {};
@@ -23534,16 +23720,25 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
23534
23720
  approvers.cloud = false;
23535
23721
  }
23536
23722
  s.approvers = approvers;
23537
- if (!import_fs48.default.existsSync(import_path49.default.dirname(configPath)))
23538
- import_fs48.default.mkdirSync(import_path49.default.dirname(configPath), { recursive: true });
23539
- import_fs48.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
23723
+ if (!import_fs49.default.existsSync(import_path50.default.dirname(configPath)))
23724
+ import_fs49.default.mkdirSync(import_path50.default.dirname(configPath), { recursive: true });
23725
+ import_fs49.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
23726
+ effectiveCloud = approvers.cloud === true;
23540
23727
  }
23541
23728
  if (options.profile && profileName !== "default") {
23542
23729
  console.log(import_chalk30.default.green(`\u2705 Profile "${profileName}" saved`));
23543
23730
  console.log(import_chalk30.default.gray(` Switch to it per-session: NODE9_PROFILE=${profileName} claude`));
23544
- } else if (options.local) {
23545
- console.log(import_chalk30.default.green(`\u2705 Privacy mode \u{1F6E1}\uFE0F`));
23546
- console.log(import_chalk30.default.gray(` All decisions stay on this machine.`));
23731
+ } else if (options.local || effectiveCloud === false) {
23732
+ console.log(import_chalk30.default.green(`\u2705 Key saved \u2014 Privacy mode \u{1F6E1}\uFE0F`));
23733
+ console.log(import_chalk30.default.gray(` All decisions stay on this machine. Nothing syncs to the cloud.`));
23734
+ if (!options.local) {
23735
+ console.log(
23736
+ import_chalk30.default.yellow(` Your config has cloud approvals OFF (settings.approvers.cloud).`)
23737
+ );
23738
+ console.log(
23739
+ import_chalk30.default.gray(` To enable team policy + dashboard sync: set it to true, or re-init.`)
23740
+ );
23741
+ }
23547
23742
  } else {
23548
23743
  console.log(import_chalk30.default.green(`\u2705 Logged in \u2014 agent mode`));
23549
23744
  console.log(import_chalk30.default.gray(` Team policy enforced for all calls via Node9 cloud.`));
@@ -23686,15 +23881,15 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
23686
23881
  }
23687
23882
  }
23688
23883
  if (options.purge) {
23689
- const node9Dir = import_path49.default.join(import_os43.default.homedir(), ".node9");
23690
- if (import_fs48.default.existsSync(node9Dir)) {
23884
+ const node9Dir = import_path50.default.join(import_os44.default.homedir(), ".node9");
23885
+ if (import_fs49.default.existsSync(node9Dir)) {
23691
23886
  const confirmed = await (0, import_prompts2.confirm)({
23692
23887
  message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
23693
23888
  default: false
23694
23889
  });
23695
23890
  if (confirmed) {
23696
- import_fs48.default.rmSync(node9Dir, { recursive: true });
23697
- if (import_fs48.default.existsSync(node9Dir)) {
23891
+ import_fs49.default.rmSync(node9Dir, { recursive: true });
23892
+ if (import_fs49.default.existsSync(node9Dir)) {
23698
23893
  console.error(
23699
23894
  import_chalk30.default.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
23700
23895
  );
@@ -23809,7 +24004,7 @@ program.command("tail").description("Stream live agent activity to the terminal"
23809
24004
  });
23810
24005
  program.command("monitor").description("Live interactive dashboard \u2014 activity feed, approvals, security signals").action(async () => {
23811
24006
  try {
23812
- const dashboardPath = import_path49.default.join(__dirname, "dashboard.mjs");
24007
+ const dashboardPath = import_path50.default.join(__dirname, "dashboard.mjs");
23813
24008
  const dynamicImport = new Function("id", "return import(id)");
23814
24009
  const mod = await dynamicImport(`file://${dashboardPath}`);
23815
24010
  await mod.startMonitor();
@@ -23847,14 +24042,14 @@ Claude Code spawns this command every ~300ms and writes a JSON payload to stdin.
23847
24042
  Run "node9 addto claude" to register it as the statusLine.`
23848
24043
  ).argument("[subcommand]", 'Optional: "debug on" / "debug off" to toggle stdin logging').argument("[state]", 'on|off \u2014 used with "debug" subcommand').action(async (subcommand, state) => {
23849
24044
  if (subcommand === "debug") {
23850
- const flagFile = import_path49.default.join(import_os43.default.homedir(), ".node9", "hud-debug");
24045
+ const flagFile = import_path50.default.join(import_os44.default.homedir(), ".node9", "hud-debug");
23851
24046
  if (state === "on") {
23852
- import_fs48.default.mkdirSync(import_path49.default.dirname(flagFile), { recursive: true });
23853
- import_fs48.default.writeFileSync(flagFile, "");
24047
+ import_fs49.default.mkdirSync(import_path50.default.dirname(flagFile), { recursive: true });
24048
+ import_fs49.default.writeFileSync(flagFile, "");
23854
24049
  console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
23855
24050
  console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
23856
24051
  } else if (state === "off") {
23857
- if (import_fs48.default.existsSync(flagFile)) import_fs48.default.unlinkSync(flagFile);
24052
+ if (import_fs49.default.existsSync(flagFile)) import_fs49.default.unlinkSync(flagFile);
23858
24053
  console.log("HUD debug logging disabled.");
23859
24054
  } else {
23860
24055
  console.error("Usage: node9 hud debug on|off");
@@ -23971,9 +24166,9 @@ if (process.argv[2] !== "daemon") {
23971
24166
  const isCheckHook = process.argv[2] === "check";
23972
24167
  if (isCheckHook) {
23973
24168
  if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
23974
- const logPath = import_path49.default.join(import_os43.default.homedir(), ".node9", "hook-debug.log");
24169
+ const logPath = import_path50.default.join(import_os44.default.homedir(), ".node9", "hook-debug.log");
23975
24170
  const msg = reason instanceof Error ? reason.message : String(reason);
23976
- import_fs48.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
24171
+ import_fs49.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
23977
24172
  `);
23978
24173
  }
23979
24174
  process.exit(0);