@two7722/sentinel-guard 1.1.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.
Files changed (88) hide show
  1. package/dist/__tests__/rules-engine.test.d.ts +1 -0
  2. package/dist/__tests__/rules-engine.test.js +69 -0
  3. package/dist/__tests__/rules-engine.test.js.map +1 -0
  4. package/dist/__tests__/transport-encryption.test.d.ts +1 -0
  5. package/dist/__tests__/transport-encryption.test.js +95 -0
  6. package/dist/__tests__/transport-encryption.test.js.map +1 -0
  7. package/dist/api/client.d.ts +27 -0
  8. package/dist/api/client.js +91 -0
  9. package/dist/api/client.js.map +1 -0
  10. package/dist/cli/bootstrap.d.ts +12 -0
  11. package/dist/cli/bootstrap.js +132 -0
  12. package/dist/cli/bootstrap.js.map +1 -0
  13. package/dist/cli/index.d.ts +2 -0
  14. package/dist/cli/index.js +534 -0
  15. package/dist/cli/index.js.map +1 -0
  16. package/dist/crypto/keys.d.ts +40 -0
  17. package/dist/crypto/keys.js +125 -0
  18. package/dist/crypto/keys.js.map +1 -0
  19. package/dist/crypto/transport-encryption.d.ts +13 -0
  20. package/dist/crypto/transport-encryption.js +62 -0
  21. package/dist/crypto/transport-encryption.js.map +1 -0
  22. package/dist/install/setup.d.ts +2 -0
  23. package/dist/install/setup.js +80 -0
  24. package/dist/install/setup.js.map +1 -0
  25. package/dist/lib/budget.d.ts +14 -0
  26. package/dist/lib/budget.js +93 -0
  27. package/dist/lib/budget.js.map +1 -0
  28. package/dist/lib/claude-process.d.ts +16 -0
  29. package/dist/lib/claude-process.js +98 -0
  30. package/dist/lib/claude-process.js.map +1 -0
  31. package/dist/lib/daemon.d.ts +6 -0
  32. package/dist/lib/daemon.js +218 -0
  33. package/dist/lib/daemon.js.map +1 -0
  34. package/dist/lib/diff.d.ts +5 -0
  35. package/dist/lib/diff.js +85 -0
  36. package/dist/lib/diff.js.map +1 -0
  37. package/dist/lib/doctor.d.ts +1 -0
  38. package/dist/lib/doctor.js +108 -0
  39. package/dist/lib/doctor.js.map +1 -0
  40. package/dist/lib/history.d.ts +22 -0
  41. package/dist/lib/history.js +62 -0
  42. package/dist/lib/history.js.map +1 -0
  43. package/dist/lib/logger.d.ts +11 -0
  44. package/dist/lib/logger.js +62 -0
  45. package/dist/lib/logger.js.map +1 -0
  46. package/dist/lib/modes.d.ts +22 -0
  47. package/dist/lib/modes.js +58 -0
  48. package/dist/lib/modes.js.map +1 -0
  49. package/dist/lib/overrides.d.ts +13 -0
  50. package/dist/lib/overrides.js +74 -0
  51. package/dist/lib/overrides.js.map +1 -0
  52. package/dist/lib/session.d.ts +32 -0
  53. package/dist/lib/session.js +68 -0
  54. package/dist/lib/session.js.map +1 -0
  55. package/dist/lib/summarizer.d.ts +5 -0
  56. package/dist/lib/summarizer.js +46 -0
  57. package/dist/lib/summarizer.js.map +1 -0
  58. package/dist/lib/tunnel.d.ts +2 -0
  59. package/dist/lib/tunnel.js +48 -0
  60. package/dist/lib/tunnel.js.map +1 -0
  61. package/dist/relay/pending.d.ts +27 -0
  62. package/dist/relay/pending.js +65 -0
  63. package/dist/relay/pending.js.map +1 -0
  64. package/dist/rules/engine.d.ts +31 -0
  65. package/dist/rules/engine.js +203 -0
  66. package/dist/rules/engine.js.map +1 -0
  67. package/dist/server/http.d.ts +8 -0
  68. package/dist/server/http.js +359 -0
  69. package/dist/server/http.js.map +1 -0
  70. package/dist/socket/client.d.ts +16 -0
  71. package/dist/socket/client.js +81 -0
  72. package/dist/socket/client.js.map +1 -0
  73. package/dist/transport/cloudkit.d.ts +30 -0
  74. package/dist/transport/cloudkit.js +162 -0
  75. package/dist/transport/cloudkit.js.map +1 -0
  76. package/dist/transport/factory.d.ts +6 -0
  77. package/dist/transport/factory.js +20 -0
  78. package/dist/transport/factory.js.map +1 -0
  79. package/dist/transport/interface.d.ts +25 -0
  80. package/dist/transport/interface.js +13 -0
  81. package/dist/transport/interface.js.map +1 -0
  82. package/dist/transport/local.d.ts +46 -0
  83. package/dist/transport/local.js +320 -0
  84. package/dist/transport/local.js.map +1 -0
  85. package/dist/transport/remote.d.ts +17 -0
  86. package/dist/transport/remote.js +83 -0
  87. package/dist/transport/remote.js.map +1 -0
  88. package/package.json +44 -0
@@ -0,0 +1,218 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.daemonStart = daemonStart;
4
+ exports.daemonStop = daemonStop;
5
+ exports.daemonStatus = daemonStatus;
6
+ exports.daemonRestart = daemonRestart;
7
+ exports.daemonInstallLaunchd = daemonInstallLaunchd;
8
+ const child_process_1 = require("child_process");
9
+ const fs_1 = require("fs");
10
+ const path_1 = require("path");
11
+ const os_1 = require("os");
12
+ const keys_1 = require("../crypto/keys");
13
+ const logger_1 = require("./logger");
14
+ const PID_PATH = (0, path_1.join)((0, keys_1.getSentinelDir)(), 'daemon.pid');
15
+ const LOCK_PATH = (0, path_1.join)((0, keys_1.getSentinelDir)(), 'daemon.lock');
16
+ const LOG_PATH = (0, path_1.join)((0, keys_1.getSentinelDir)(), 'daemon.log');
17
+ function savePid(pid) {
18
+ (0, fs_1.writeFileSync)(PID_PATH, String(pid));
19
+ }
20
+ function readPid() {
21
+ if (!(0, fs_1.existsSync)(PID_PATH))
22
+ return null;
23
+ try {
24
+ const pid = parseInt((0, fs_1.readFileSync)(PID_PATH, 'utf-8').trim(), 10);
25
+ return isNaN(pid) ? null : pid;
26
+ }
27
+ catch {
28
+ return null;
29
+ }
30
+ }
31
+ function isProcessRunning(pid) {
32
+ try {
33
+ process.kill(pid, 0);
34
+ return true;
35
+ }
36
+ catch {
37
+ return false;
38
+ }
39
+ }
40
+ function acquireLock() {
41
+ if ((0, fs_1.existsSync)(LOCK_PATH)) {
42
+ const lockPid = parseInt((0, fs_1.readFileSync)(LOCK_PATH, 'utf-8').trim(), 10);
43
+ if (!isNaN(lockPid) && isProcessRunning(lockPid))
44
+ return false;
45
+ // Stale lock
46
+ try {
47
+ (0, fs_1.unlinkSync)(LOCK_PATH);
48
+ }
49
+ catch { }
50
+ }
51
+ (0, fs_1.writeFileSync)(LOCK_PATH, String(process.pid));
52
+ return true;
53
+ }
54
+ function releaseLock() {
55
+ try {
56
+ (0, fs_1.unlinkSync)(LOCK_PATH);
57
+ }
58
+ catch { }
59
+ }
60
+ /** Start caffeinate to prevent Mac sleep while daemon runs */
61
+ function startCaffeinate(daemonPid) {
62
+ try {
63
+ const caf = (0, child_process_1.spawn)('caffeinate', ['-i', '-w', String(daemonPid)], {
64
+ detached: true,
65
+ stdio: 'ignore',
66
+ });
67
+ caf.unref();
68
+ logger_1.log.dim(` caffeinate: preventing sleep (PID: ${caf.pid})`);
69
+ }
70
+ catch {
71
+ // caffeinate not available (Linux)
72
+ }
73
+ }
74
+ function daemonStart(mode, port, serverUrl) {
75
+ const existingPid = readPid();
76
+ if (existingPid && isProcessRunning(existingPid)) {
77
+ logger_1.log.warn(`Daemon already running (PID: ${existingPid})`);
78
+ return;
79
+ }
80
+ // Lock file check
81
+ if (!acquireLock()) {
82
+ logger_1.log.error('Another daemon instance is starting. Remove ~/.sentinel/daemon.lock if stale.');
83
+ return;
84
+ }
85
+ const args = ['start', '--mode', mode, '--port', String(port)];
86
+ if (serverUrl)
87
+ args.push('--server', serverUrl);
88
+ const script = (0, path_1.join)(__dirname, 'index.js');
89
+ const out = require('fs').openSync(LOG_PATH, 'a');
90
+ const child = (0, child_process_1.spawn)('node', [script, ...args], {
91
+ detached: true,
92
+ stdio: ['ignore', out, out],
93
+ env: { ...process.env, SENTINEL_DAEMON: '1' },
94
+ });
95
+ child.unref();
96
+ releaseLock();
97
+ if (child.pid) {
98
+ savePid(child.pid);
99
+ startCaffeinate(child.pid);
100
+ logger_1.log.success(`Sentinel daemon started (PID: ${child.pid})`);
101
+ logger_1.log.dim(` Hook server: localhost:${port}`);
102
+ logger_1.log.dim(` Mode: ${mode}`);
103
+ logger_1.log.dim(` Log: ${LOG_PATH}`);
104
+ logger_1.log.dim(` PID file: ${PID_PATH}`);
105
+ }
106
+ else {
107
+ logger_1.log.error('Failed to start daemon');
108
+ }
109
+ }
110
+ function daemonStop() {
111
+ const pid = readPid();
112
+ if (!pid) {
113
+ logger_1.log.warn('No daemon PID file found');
114
+ return;
115
+ }
116
+ if (!isProcessRunning(pid)) {
117
+ logger_1.log.warn(`Daemon not running (stale PID: ${pid})`);
118
+ try {
119
+ (0, fs_1.unlinkSync)(PID_PATH);
120
+ }
121
+ catch { }
122
+ return;
123
+ }
124
+ try {
125
+ process.kill(pid, 'SIGTERM');
126
+ // Wait briefly for graceful shutdown using busy-wait with Atomics
127
+ const start = Date.now();
128
+ while (Date.now() - start < 2000 && isProcessRunning(pid)) {
129
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 200);
130
+ }
131
+ if (isProcessRunning(pid))
132
+ process.kill(pid, 'SIGKILL');
133
+ logger_1.log.success(`Daemon stopped (PID: ${pid})`);
134
+ try {
135
+ (0, fs_1.unlinkSync)(PID_PATH);
136
+ }
137
+ catch { }
138
+ releaseLock();
139
+ }
140
+ catch (err) {
141
+ logger_1.log.error(`Failed to stop daemon: ${err.message}`);
142
+ }
143
+ }
144
+ async function daemonStatus() {
145
+ const pid = readPid();
146
+ if (!pid) {
147
+ logger_1.log.warn('No daemon running (no PID file)');
148
+ return;
149
+ }
150
+ const running = isProcessRunning(pid);
151
+ if (!running) {
152
+ logger_1.log.warn(`Daemon not running (stale PID: ${pid})`);
153
+ try {
154
+ (0, fs_1.unlinkSync)(PID_PATH);
155
+ }
156
+ catch { }
157
+ return;
158
+ }
159
+ logger_1.log.success(`Daemon running (PID: ${pid})`);
160
+ try {
161
+ const res = await fetch('http://localhost:7749/status');
162
+ const data = (await res.json());
163
+ logger_1.log.info(` Mode: ${data.mode}`);
164
+ logger_1.log.info(` Connected: ${data.connected}`);
165
+ logger_1.log.info(` Pending: ${data.pendingRequests}`);
166
+ logger_1.log.info(` Uptime: ${Math.round(data.uptime)}s`);
167
+ }
168
+ catch {
169
+ logger_1.log.warn(' Hook server not responding');
170
+ }
171
+ // Show log tail
172
+ if ((0, fs_1.existsSync)(LOG_PATH)) {
173
+ logger_1.log.dim(` Log: ${LOG_PATH}`);
174
+ }
175
+ }
176
+ function daemonRestart(mode, port, serverUrl) {
177
+ daemonStop();
178
+ setTimeout(() => daemonStart(mode, port, serverUrl), 800);
179
+ }
180
+ /** Generate macOS launchd plist for auto-start */
181
+ function daemonInstallLaunchd(mode, port) {
182
+ const plistName = 'com.sentinel.daemon';
183
+ const plistDir = (0, path_1.join)((0, os_1.homedir)(), 'Library', 'LaunchAgents');
184
+ const plistPath = (0, path_1.join)(plistDir, `${plistName}.plist`);
185
+ const nodePath = process.execPath;
186
+ const scriptPath = (0, path_1.join)(__dirname, 'index.js');
187
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
188
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
189
+ <plist version="1.0">
190
+ <dict>
191
+ <key>Label</key><string>${plistName}</string>
192
+ <key>ProgramArguments</key>
193
+ <array>
194
+ <string>${nodePath}</string>
195
+ <string>${scriptPath}</string>
196
+ <string>start</string>
197
+ <string>--mode</string>
198
+ <string>${mode}</string>
199
+ <string>--port</string>
200
+ <string>${port}</string>
201
+ </array>
202
+ <key>RunAtLoad</key><true/>
203
+ <key>KeepAlive</key><true/>
204
+ <key>StandardOutPath</key><string>${LOG_PATH}</string>
205
+ <key>StandardErrorPath</key><string>${LOG_PATH}</string>
206
+ <key>EnvironmentVariables</key>
207
+ <dict>
208
+ <key>SENTINEL_DAEMON</key><string>1</string>
209
+ </dict>
210
+ </dict>
211
+ </plist>`;
212
+ (0, fs_1.writeFileSync)(plistPath, plist);
213
+ logger_1.log.success(`LaunchAgent installed: ${plistPath}`);
214
+ logger_1.log.dim(` Will auto-start on login.`);
215
+ logger_1.log.dim(` Load now: launchctl load ${plistPath}`);
216
+ logger_1.log.dim(` Unload: launchctl unload ${plistPath}`);
217
+ }
218
+ //# sourceMappingURL=daemon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/lib/daemon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * 为 Write/Edit 工具生成简化 diff。
3
+ * 对比现有文件内容和新内容,输出 Git-style unified diff。
4
+ */
5
+ export declare function generateDiff(toolName: string, toolInput: Record<string, unknown>): string | undefined;
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateDiff = generateDiff;
4
+ const fs_1 = require("fs");
5
+ /**
6
+ * 为 Write/Edit 工具生成简化 diff。
7
+ * 对比现有文件内容和新内容,输出 Git-style unified diff。
8
+ */
9
+ function generateDiff(toolName, toolInput) {
10
+ const name = toolName.toLowerCase();
11
+ if (name === 'write') {
12
+ const filePath = (toolInput.file_path ?? toolInput.path);
13
+ const newContent = toolInput.content;
14
+ if (!filePath || !newContent)
15
+ return undefined;
16
+ // 读取现有文件
17
+ let oldContent = '';
18
+ if ((0, fs_1.existsSync)(filePath)) {
19
+ try {
20
+ oldContent = (0, fs_1.readFileSync)(filePath, 'utf-8');
21
+ }
22
+ catch { /* new file */ }
23
+ }
24
+ return unifiedDiff(filePath, oldContent, newContent);
25
+ }
26
+ if (name === 'edit') {
27
+ const filePath = (toolInput.file_path ?? toolInput.path);
28
+ const oldStr = toolInput.old_string;
29
+ const newStr = toolInput.new_string;
30
+ if (!filePath || !oldStr || !newStr)
31
+ return undefined;
32
+ const lines = [
33
+ `--- a/${filePath}`,
34
+ `+++ b/${filePath}`,
35
+ '@@ edit @@',
36
+ ];
37
+ for (const line of oldStr.split('\n'))
38
+ lines.push(`-${line}`);
39
+ for (const line of newStr.split('\n'))
40
+ lines.push(`+${line}`);
41
+ return lines.join('\n');
42
+ }
43
+ return undefined;
44
+ }
45
+ /** 简化的 unified diff 生成(逐行对比) */
46
+ function unifiedDiff(path, oldText, newText) {
47
+ const oldLines = oldText.split('\n');
48
+ const newLines = newText.split('\n');
49
+ const lines = [
50
+ `--- a/${path}`,
51
+ `+++ b/${path}`,
52
+ ];
53
+ if (oldText === '') {
54
+ // 新文件
55
+ lines.push(`@@ -0,0 +1,${newLines.length} @@ (new file)`);
56
+ for (const l of newLines)
57
+ lines.push(`+${l}`);
58
+ return lines.join('\n');
59
+ }
60
+ // 简单逐行 diff(适合短文件,长文件截断)
61
+ const maxLines = 50;
62
+ lines.push(`@@ -1,${Math.min(oldLines.length, maxLines)} +1,${Math.min(newLines.length, maxLines)} @@`);
63
+ const maxI = Math.max(oldLines.length, newLines.length);
64
+ let shown = 0;
65
+ for (let i = 0; i < maxI && shown < maxLines; i++) {
66
+ const old = i < oldLines.length ? oldLines[i] : null;
67
+ const nw = i < newLines.length ? newLines[i] : null;
68
+ if (old === nw) {
69
+ lines.push(` ${old}`);
70
+ }
71
+ else {
72
+ if (old != null)
73
+ lines.push(`-${old}`);
74
+ if (nw != null)
75
+ lines.push(`+${nw}`);
76
+ }
77
+ shown++;
78
+ }
79
+ if (maxI > maxLines) {
80
+ const remaining = maxI - maxLines;
81
+ lines.push(`\n⚠ Diff truncated: ${remaining} more line${remaining > 1 ? 's' : ''} not shown. Review the full file before approving.`);
82
+ }
83
+ return lines.join('\n');
84
+ }
85
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/lib/diff.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function runDoctor(): Promise<void>;
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.runDoctor = runDoctor;
7
+ const fs_1 = require("fs");
8
+ const path_1 = require("path");
9
+ const os_1 = require("os");
10
+ const chalk_1 = __importDefault(require("chalk"));
11
+ const keys_1 = require("../crypto/keys");
12
+ const engine_1 = require("../rules/engine");
13
+ const budget_1 = require("./budget");
14
+ async function runDoctor() {
15
+ const checks = [];
16
+ // 1. Claude Code hook
17
+ const claudeSettings = (0, path_1.join)((0, os_1.homedir)(), '.claude', 'settings.json');
18
+ if ((0, fs_1.existsSync)(claudeSettings)) {
19
+ try {
20
+ const raw = (0, fs_1.readFileSync)(claudeSettings, 'utf-8');
21
+ const hookInstalled = raw.includes('localhost') && raw.includes('7749');
22
+ checks.push({
23
+ label: 'Claude Code hook',
24
+ ok: hookInstalled,
25
+ message: hookInstalled ? 'Hook installed' : 'Hook not installed',
26
+ fix: hookInstalled ? undefined : 'sentinel install',
27
+ });
28
+ }
29
+ catch {
30
+ checks.push({ label: 'Claude Code hook', ok: false, message: 'Cannot read settings.json', fix: 'sentinel install' });
31
+ }
32
+ }
33
+ else {
34
+ checks.push({ label: 'Claude Code hook', ok: false, message: 'No ~/.claude/settings.json', fix: 'sentinel install' });
35
+ }
36
+ // 2. Hook server
37
+ try {
38
+ const res = await fetch('http://localhost:7749/status');
39
+ const data = (await res.json());
40
+ checks.push({ label: 'Hook server', ok: true, message: `Running on port 7749 (mode: ${data.mode})` });
41
+ // 3. iOS connected (from server status)
42
+ checks.push({
43
+ label: 'iOS connection',
44
+ ok: data.connected,
45
+ message: data.connected ? `Connected (${data.mode} mode)` : 'Not connected',
46
+ fix: data.connected ? undefined : 'Connect iOS via Settings → 手动连接',
47
+ });
48
+ }
49
+ catch {
50
+ checks.push({ label: 'Hook server', ok: false, message: 'Not running', fix: 'sentinel start' });
51
+ checks.push({ label: 'iOS connection', ok: false, message: 'Server not running', fix: 'sentinel start' });
52
+ }
53
+ // 4. Rules
54
+ try {
55
+ const rules = (0, engine_1.getRules)();
56
+ const rulesPath = (0, path_1.join)((0, keys_1.getSentinelDir)(), 'rules.json');
57
+ const hasCustom = (0, fs_1.existsSync)(rulesPath);
58
+ checks.push({
59
+ label: 'Rules',
60
+ ok: true,
61
+ message: `${rules.length} rules loaded${hasCustom ? '' : ' (defaults only)'}`,
62
+ });
63
+ }
64
+ catch {
65
+ checks.push({ label: 'Rules', ok: false, message: 'Failed to load rules' });
66
+ }
67
+ // 5. Identity
68
+ const identityPath = (0, path_1.join)((0, keys_1.getSentinelDir)(), 'identity.json');
69
+ if ((0, fs_1.existsSync)(identityPath)) {
70
+ const pubKey = (0, keys_1.getPublicKeyBase64)().slice(0, 12);
71
+ checks.push({ label: 'Identity', ok: true, message: `${pubKey}...` });
72
+ }
73
+ else {
74
+ checks.push({ label: 'Identity', ok: false, message: 'No identity file', fix: 'Created on first start' });
75
+ }
76
+ // 6. Budget
77
+ const b = (0, budget_1.getBudgetStatus)();
78
+ if (b.limit > 0) {
79
+ checks.push({
80
+ label: 'Budget',
81
+ ok: !b.overBudget,
82
+ message: `$${b.spent.toFixed(4)} / $${b.limit.toFixed(2)} today${b.overBudget ? ' ⚠ OVER' : ''}`,
83
+ });
84
+ }
85
+ else {
86
+ checks.push({ label: 'Budget', ok: true, message: 'No limit set' });
87
+ }
88
+ // Output
89
+ console.log(chalk_1.default.bold('\n 🩺 Sentinel Doctor\n'));
90
+ let issues = 0;
91
+ for (const c of checks) {
92
+ const icon = c.ok ? chalk_1.default.green('✓') : chalk_1.default.red('✗');
93
+ console.log(` ${icon} ${chalk_1.default.bold(c.label.padEnd(18))} ${c.message}`);
94
+ if (!c.ok && c.fix) {
95
+ console.log(` ${chalk_1.default.dim(`→ ${c.fix}`)}`);
96
+ issues++;
97
+ }
98
+ }
99
+ console.log('');
100
+ if (issues === 0) {
101
+ log(chalk_1.default.green.bold(' All checks passed. Sentinel is ready. ✨\n'));
102
+ }
103
+ else {
104
+ log(chalk_1.default.yellow.bold(` ${issues} issue${issues > 1 ? 's' : ''} found. Run suggested commands to fix.\n`));
105
+ }
106
+ }
107
+ function log(msg) { console.log(msg); }
108
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/lib/doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ export interface LogEntry {
2
+ id: string;
3
+ toolName: string;
4
+ filePath: string | null;
5
+ riskLevel: string;
6
+ decision: string;
7
+ timestamp: string;
8
+ result?: string;
9
+ summary?: string;
10
+ }
11
+ export declare function appendLog(entry: LogEntry): void;
12
+ export declare function getHistory(): LogEntry[];
13
+ export interface DayStats {
14
+ allowed: number;
15
+ blocked: number;
16
+ timeout: number;
17
+ autoAllow: number;
18
+ offline: number;
19
+ total: number;
20
+ lastRequestTime: string | null;
21
+ }
22
+ export declare function getTodayStats(): DayStats;
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.appendLog = appendLog;
4
+ exports.getHistory = getHistory;
5
+ exports.getTodayStats = getTodayStats;
6
+ const fs_1 = require("fs");
7
+ const path_1 = require("path");
8
+ const keys_1 = require("../crypto/keys");
9
+ const MAX_ENTRIES = 100;
10
+ const HISTORY_PATH = (0, path_1.join)((0, keys_1.getSentinelDir)(), 'logs.json');
11
+ function loadHistory() {
12
+ if (!(0, fs_1.existsSync)(HISTORY_PATH))
13
+ return [];
14
+ try {
15
+ return JSON.parse((0, fs_1.readFileSync)(HISTORY_PATH, 'utf-8'));
16
+ }
17
+ catch {
18
+ return [];
19
+ }
20
+ }
21
+ function saveHistory(entries) {
22
+ (0, fs_1.writeFileSync)(HISTORY_PATH, JSON.stringify(entries, null, 2), { mode: 0o600 });
23
+ }
24
+ function appendLog(entry) {
25
+ const history = loadHistory();
26
+ history.unshift(entry); // newest first
27
+ if (history.length > MAX_ENTRIES)
28
+ history.length = MAX_ENTRIES;
29
+ saveHistory(history);
30
+ }
31
+ function getHistory() {
32
+ return loadHistory();
33
+ }
34
+ function getTodayStats() {
35
+ const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD
36
+ const entries = loadHistory().filter((e) => e.timestamp.startsWith(today));
37
+ const stats = {
38
+ allowed: 0, blocked: 0, timeout: 0, autoAllow: 0, offline: 0, total: entries.length,
39
+ lastRequestTime: entries[0]?.timestamp ?? null,
40
+ };
41
+ for (const e of entries) {
42
+ switch (e.decision) {
43
+ case 'allowed':
44
+ stats.allowed++;
45
+ break;
46
+ case 'blocked':
47
+ stats.blocked++;
48
+ break;
49
+ case 'timeout':
50
+ stats.timeout++;
51
+ break;
52
+ case 'auto_allow':
53
+ stats.autoAllow++;
54
+ break;
55
+ case 'offline':
56
+ stats.offline++;
57
+ break;
58
+ }
59
+ }
60
+ return stats;
61
+ }
62
+ //# sourceMappingURL=history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/lib/history.ts"],"names":[],"mappings":";;AA+BA,8BAKC;AAED,gCAEC;AAYD,sCAmBC;AAvED,2BAA6D;AAC7D,+BAA4B;AAC5B,yCAAgD;AAEhD,MAAM,WAAW,GAAG,GAAG,CAAC;AACxB,MAAM,YAAY,GAAG,IAAA,WAAI,EAAC,IAAA,qBAAc,GAAE,EAAE,WAAW,CAAC,CAAC;AAazD,SAAS,WAAW;IAClB,IAAI,CAAC,IAAA,eAAU,EAAC,YAAY,CAAC;QAAE,OAAO,EAAE,CAAC;IACzC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAA,iBAAY,EAAC,YAAY,EAAE,OAAO,CAAC,CAAe,CAAC;IACvE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,OAAmB;IACtC,IAAA,kBAAa,EAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACjF,CAAC;AAED,SAAgB,SAAS,CAAC,KAAe;IACvC,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;IAC9B,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,eAAe;IACvC,IAAI,OAAO,CAAC,MAAM,GAAG,WAAW;QAAE,OAAO,CAAC,MAAM,GAAG,WAAW,CAAC;IAC/D,WAAW,CAAC,OAAO,CAAC,CAAC;AACvB,CAAC;AAED,SAAgB,UAAU;IACxB,OAAO,WAAW,EAAE,CAAC;AACvB,CAAC;AAYD,SAAgB,aAAa;IAC3B,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa;IAClE,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;IAE3E,MAAM,KAAK,GAAa;QACtB,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM;QACnF,eAAe,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,SAAS,IAAI,IAAI;KAC/C,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC;YACnB,KAAK,SAAS;gBAAE,KAAK,CAAC,OAAO,EAAE,CAAC;gBAAC,MAAM;YACvC,KAAK,SAAS;gBAAE,KAAK,CAAC,OAAO,EAAE,CAAC;gBAAC,MAAM;YACvC,KAAK,SAAS;gBAAE,KAAK,CAAC,OAAO,EAAE,CAAC;gBAAC,MAAM;YACvC,KAAK,YAAY;gBAAE,KAAK,CAAC,SAAS,EAAE,CAAC;gBAAC,MAAM;YAC5C,KAAK,SAAS;gBAAE,KAAK,CAAC,OAAO,EAAE,CAAC;gBAAC,MAAM;QACzC,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,11 @@
1
+ /** Silence terminal output and redirect logs to ~/.sentinel/run.log */
2
+ export declare function silenceForClaude(): void;
3
+ export declare function unsilence(): void;
4
+ export declare const log: {
5
+ info: (msg: string) => void;
6
+ success: (msg: string) => void;
7
+ warn: (msg: string) => void;
8
+ error: (msg: string) => void;
9
+ debug: (msg: string) => void;
10
+ dim: (msg: string) => void;
11
+ };
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.log = void 0;
7
+ exports.silenceForClaude = silenceForClaude;
8
+ exports.unsilence = unsilence;
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const fs_1 = __importDefault(require("fs"));
11
+ const path_1 = __importDefault(require("path"));
12
+ const os_1 = __importDefault(require("os"));
13
+ // Log file for sentinel output when Claude TUI is active
14
+ const LOG_DIR = path_1.default.join(os_1.default.homedir(), '.sentinel');
15
+ const LOG_FILE = path_1.default.join(LOG_DIR, 'run.log');
16
+ let silenced = false;
17
+ let logStream = null;
18
+ /** Silence terminal output and redirect logs to ~/.sentinel/run.log */
19
+ function silenceForClaude() {
20
+ if (silenced)
21
+ return;
22
+ silenced = true;
23
+ try {
24
+ fs_1.default.mkdirSync(LOG_DIR, { recursive: true });
25
+ logStream = fs_1.default.createWriteStream(LOG_FILE, { flags: 'a' });
26
+ }
27
+ catch {
28
+ // ignore
29
+ }
30
+ }
31
+ function unsilence() {
32
+ silenced = false;
33
+ logStream?.end();
34
+ logStream = null;
35
+ }
36
+ function write(prefix, msg) {
37
+ const line = `${prefix} ${msg}`;
38
+ if (silenced) {
39
+ const ts = new Date().toISOString().slice(11, 19);
40
+ logStream?.write(`${ts} ${line}\n`);
41
+ }
42
+ else {
43
+ console.log(line);
44
+ }
45
+ }
46
+ exports.log = {
47
+ info: (msg) => write(chalk_1.default.blue('ℹ'), msg),
48
+ success: (msg) => write(chalk_1.default.green('✓'), msg),
49
+ warn: (msg) => write(chalk_1.default.yellow('⚠'), msg),
50
+ error: (msg) => {
51
+ // Always print errors, even when silenced
52
+ const line = `${chalk_1.default.red('✗')} ${msg}`;
53
+ console.error(line);
54
+ logStream?.write(`${new Date().toISOString().slice(11, 19)} ${line}\n`);
55
+ },
56
+ debug: (msg) => {
57
+ if (process.env.DEBUG)
58
+ write(chalk_1.default.gray('⋯'), msg);
59
+ },
60
+ dim: (msg) => write(chalk_1.default.dim(msg), ''),
61
+ };
62
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/lib/logger.ts"],"names":[],"mappings":";;;;;;AAaA,4CASC;AAED,8BAIC;AA5BD,kDAA0B;AAC1B,4CAAoB;AACpB,gDAAwB;AACxB,4CAAoB;AAEpB,yDAAyD;AACzD,MAAM,OAAO,GAAG,cAAI,CAAC,IAAI,CAAC,YAAE,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;AACrD,MAAM,QAAQ,GAAG,cAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;AAE/C,IAAI,QAAQ,GAAG,KAAK,CAAC;AACrB,IAAI,SAAS,GAA0B,IAAI,CAAC;AAE5C,uEAAuE;AACvE,SAAgB,gBAAgB;IAC9B,IAAI,QAAQ;QAAE,OAAO;IACrB,QAAQ,GAAG,IAAI,CAAC;IAChB,IAAI,CAAC;QACH,YAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3C,SAAS,GAAG,YAAE,CAAC,iBAAiB,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;AACH,CAAC;AAED,SAAgB,SAAS;IACvB,QAAQ,GAAG,KAAK,CAAC;IACjB,SAAS,EAAE,GAAG,EAAE,CAAC;IACjB,SAAS,GAAG,IAAI,CAAC;AACnB,CAAC;AAED,SAAS,KAAK,CAAC,MAAc,EAAE,GAAW;IACxC,MAAM,IAAI,GAAG,GAAG,MAAM,IAAI,GAAG,EAAE,CAAC;IAChC,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QAClD,SAAS,EAAE,KAAK,CAAC,GAAG,EAAE,IAAI,IAAI,IAAI,CAAC,CAAC;IACtC,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC;AACH,CAAC;AAEY,QAAA,GAAG,GAAG;IACjB,IAAI,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,KAAK,CAAC,eAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC;IAClD,OAAO,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,KAAK,CAAC,eAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC;IACtD,IAAI,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,KAAK,CAAC,eAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC;IACpD,KAAK,EAAE,CAAC,GAAW,EAAE,EAAE;QACrB,0CAA0C;QAC1C,MAAM,IAAI,GAAG,GAAG,eAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC;QACxC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACpB,SAAS,EAAE,KAAK,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;IAC1E,CAAC;IACD,KAAK,EAAE,CAAC,GAAW,EAAE,EAAE;QACrB,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK;YAAE,KAAK,CAAC,eAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;IACrD,CAAC;IACD,GAAG,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,KAAK,CAAC,eAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;CAChD,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Permission modes (better than Happy's 4 modes):
3
+ *
4
+ * strict — Every non-read tool needs approval (default)
5
+ * relaxed — Only Bash and destructive ops need approval
6
+ * yolo — All auto-approve, but everything is logged
7
+ * plan — Like strict, but groups related calls
8
+ * lockdown — Block everything (same as sentinel block on)
9
+ */
10
+ export type PermissionMode = 'strict' | 'relaxed' | 'yolo' | 'plan' | 'lockdown';
11
+ interface ModeConfig {
12
+ mode: PermissionMode;
13
+ changedAt: string;
14
+ }
15
+ export declare function getMode(): PermissionMode;
16
+ export declare function setMode(mode: PermissionMode): void;
17
+ export declare function getModeInfo(): ModeConfig;
18
+ /** Check if a tool should be auto-allowed based on current mode */
19
+ export declare function shouldAutoAllow(toolName: string): 'auto_allow' | 'require' | 'block';
20
+ export declare const MODE_DESCRIPTIONS: Record<PermissionMode, string>;
21
+ export declare const ALL_MODES: PermissionMode[];
22
+ export {};