agentvigil 1.0.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 (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +76 -0
  3. package/dist/commands/autostart.d.ts +3 -0
  4. package/dist/commands/autostart.d.ts.map +1 -0
  5. package/dist/commands/autostart.js +57 -0
  6. package/dist/commands/autostart.js.map +1 -0
  7. package/dist/commands/daemon.d.ts +31 -0
  8. package/dist/commands/daemon.d.ts.map +1 -0
  9. package/dist/commands/daemon.js +131 -0
  10. package/dist/commands/daemon.js.map +1 -0
  11. package/dist/commands/setup.d.ts +5 -0
  12. package/dist/commands/setup.d.ts.map +1 -0
  13. package/dist/commands/setup.js +158 -0
  14. package/dist/commands/setup.js.map +1 -0
  15. package/dist/commands/start.d.ts +2 -0
  16. package/dist/commands/start.d.ts.map +1 -0
  17. package/dist/commands/start.js +89 -0
  18. package/dist/commands/start.js.map +1 -0
  19. package/dist/commands/uninstall.d.ts +7 -0
  20. package/dist/commands/uninstall.d.ts.map +1 -0
  21. package/dist/commands/uninstall.js +13 -0
  22. package/dist/commands/uninstall.js.map +1 -0
  23. package/dist/crypto/encryption.d.ts +11 -0
  24. package/dist/crypto/encryption.d.ts.map +1 -0
  25. package/dist/crypto/encryption.js +57 -0
  26. package/dist/crypto/encryption.js.map +1 -0
  27. package/dist/hooks/hook-handler.d.ts +18 -0
  28. package/dist/hooks/hook-handler.d.ts.map +1 -0
  29. package/dist/hooks/hook-handler.js +256 -0
  30. package/dist/hooks/hook-handler.js.map +1 -0
  31. package/dist/hooks/hook-manager.d.ts +22 -0
  32. package/dist/hooks/hook-manager.d.ts.map +1 -0
  33. package/dist/hooks/hook-manager.js +133 -0
  34. package/dist/hooks/hook-manager.js.map +1 -0
  35. package/dist/index.d.ts +3 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +88 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/notifications/fcm-client.d.ts +15 -0
  40. package/dist/notifications/fcm-client.d.ts.map +1 -0
  41. package/dist/notifications/fcm-client.js +72 -0
  42. package/dist/notifications/fcm-client.js.map +1 -0
  43. package/dist/notifications/ntfy-client.d.ts +6 -0
  44. package/dist/notifications/ntfy-client.d.ts.map +1 -0
  45. package/dist/notifications/ntfy-client.js +51 -0
  46. package/dist/notifications/ntfy-client.js.map +1 -0
  47. package/dist/relay/relay-handler.d.ts +51 -0
  48. package/dist/relay/relay-handler.d.ts.map +1 -0
  49. package/dist/relay/relay-handler.js +325 -0
  50. package/dist/relay/relay-handler.js.map +1 -0
  51. package/dist/sessions/keystroke-injector.d.ts +4 -0
  52. package/dist/sessions/keystroke-injector.d.ts.map +1 -0
  53. package/dist/sessions/keystroke-injector.js +216 -0
  54. package/dist/sessions/keystroke-injector.js.map +1 -0
  55. package/dist/sessions/process-detector.d.ts +27 -0
  56. package/dist/sessions/process-detector.d.ts.map +1 -0
  57. package/dist/sessions/process-detector.js +180 -0
  58. package/dist/sessions/process-detector.js.map +1 -0
  59. package/dist/sessions/session-manager.d.ts +37 -0
  60. package/dist/sessions/session-manager.d.ts.map +1 -0
  61. package/dist/sessions/session-manager.js +90 -0
  62. package/dist/sessions/session-manager.js.map +1 -0
  63. package/dist/sessions/session-poller.d.ts +18 -0
  64. package/dist/sessions/session-poller.d.ts.map +1 -0
  65. package/dist/sessions/session-poller.js +73 -0
  66. package/dist/sessions/session-poller.js.map +1 -0
  67. package/dist/sessions/session-watcher.d.ts +20 -0
  68. package/dist/sessions/session-watcher.d.ts.map +1 -0
  69. package/dist/sessions/session-watcher.js +71 -0
  70. package/dist/sessions/session-watcher.js.map +1 -0
  71. package/dist/sessions/tmux-bridge.d.ts +11 -0
  72. package/dist/sessions/tmux-bridge.d.ts.map +1 -0
  73. package/dist/sessions/tmux-bridge.js +67 -0
  74. package/dist/sessions/tmux-bridge.js.map +1 -0
  75. package/dist/tunnel/tunnel-manager.d.ts +8 -0
  76. package/dist/tunnel/tunnel-manager.d.ts.map +1 -0
  77. package/dist/tunnel/tunnel-manager.js +57 -0
  78. package/dist/tunnel/tunnel-manager.js.map +1 -0
  79. package/dist/tunnel/websocket-server.d.ts +48 -0
  80. package/dist/tunnel/websocket-server.d.ts.map +1 -0
  81. package/dist/tunnel/websocket-server.js +173 -0
  82. package/dist/tunnel/websocket-server.js.map +1 -0
  83. package/dist/types.d.ts +37 -0
  84. package/dist/types.d.ts.map +1 -0
  85. package/dist/types.js +2 -0
  86. package/dist/types.js.map +1 -0
  87. package/dist/utils/config.d.ts +23 -0
  88. package/dist/utils/config.d.ts.map +1 -0
  89. package/dist/utils/config.js +54 -0
  90. package/dist/utils/config.js.map +1 -0
  91. package/dist/utils/logger.d.ts +9 -0
  92. package/dist/utils/logger.d.ts.map +1 -0
  93. package/dist/utils/logger.js +11 -0
  94. package/dist/utils/logger.js.map +1 -0
  95. package/dist/utils/qr.d.ts +19 -0
  96. package/dist/utils/qr.d.ts.map +1 -0
  97. package/dist/utils/qr.js +17 -0
  98. package/dist/utils/qr.js.map +1 -0
  99. package/package.json +67 -0
@@ -0,0 +1,73 @@
1
+ import { logger } from '../utils/logger.js';
2
+ import { deleteSession, getAllSessions, getSession, updateSession, } from './session-manager.js';
3
+ import { detectAgentProcesses, findSessionFileForCwd, isPidAlive } from './process-detector.js';
4
+ export const PROCESS_POLL_INTERVAL_MS = 10_000;
5
+ /**
6
+ * Reconciles the OS process table with the in-memory session store every
7
+ * PROCESS_POLL_INTERVAL_MS milliseconds.
8
+ *
9
+ * - Sessions whose process has died are removed immediately.
10
+ * - Running agent processes not yet in the store are added.
11
+ * - done/error sessions are left for cleanup() to expire.
12
+ *
13
+ * Runs the first poll synchronously (awaited) before returning so the store
14
+ * is fully populated when the caller proceeds.
15
+ */
16
+ export async function startSessionPolling(onSessionAdded, onSessionRemoved, intervalMs = PROCESS_POLL_INTERVAL_MS) {
17
+ async function poll() {
18
+ try {
19
+ const liveProcs = await detectAgentProcesses();
20
+ // ── Remove dead sessions ──────────────────────────────────────────
21
+ for (const session of getAllSessions()) {
22
+ // done/error sessions are managed by cleanup() — leave them alone
23
+ if (session.state === 'done' || session.state === 'error')
24
+ continue;
25
+ if (session.pid && !isPidAlive(session.pid)) {
26
+ logger.info(`Session terminated (pid ${session.pid} dead): ${session.project_name}`);
27
+ deleteSession(session.session_id);
28
+ onSessionRemoved(session.session_id);
29
+ }
30
+ }
31
+ // ── Add new sessions ──────────────────────────────────────────────
32
+ for (const proc of liveProcs) {
33
+ // Already tracked by pid?
34
+ const byPid = getAllSessions().find(s => s.pid === proc.pid);
35
+ if (byPid)
36
+ continue;
37
+ const fileInfo = await findSessionFileForCwd(proc.cwd);
38
+ if (!fileInfo) {
39
+ // JSONL not written yet — session is just starting; next poll will catch it.
40
+ continue;
41
+ }
42
+ // Already tracked by session_id (hook may have created it first)?
43
+ if (getSession(fileInfo.sessionId)) {
44
+ const existing = getSession(fileInfo.sessionId);
45
+ if (!existing.pid) {
46
+ // Attach pid so future dead-process checks work.
47
+ updateSession(fileInfo.sessionId, { pid: proc.pid });
48
+ }
49
+ continue;
50
+ }
51
+ const session = updateSession(fileInfo.sessionId, {
52
+ cwd: proc.cwd,
53
+ agent: proc.agentType,
54
+ state: 'working',
55
+ last_message: fileInfo.lastMessage,
56
+ last_activity: new Date(),
57
+ pid: proc.pid,
58
+ });
59
+ logger.info(`New session detected: ${session.project_name} (pid: ${proc.pid})`);
60
+ onSessionAdded(session);
61
+ }
62
+ }
63
+ catch (err) {
64
+ logger.warn('Session poll error', err);
65
+ }
66
+ }
67
+ await poll(); // first run awaited — store is populated when we return
68
+ const activeSessions = getAllSessions().filter(s => s.state === 'working' || s.state === 'blocked' || s.state === 'idle');
69
+ logger.info(`Session poll ready: ${activeSessions.length} active session(s)`);
70
+ const timer = setInterval(() => void poll(), intervalMs);
71
+ return { stop: () => clearInterval(timer) };
72
+ }
73
+ //# sourceMappingURL=session-poller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-poller.js","sourceRoot":"","sources":["../../src/sessions/session-poller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import { type FSWatcher } from 'chokidar';
2
+ export declare const SESSION_BLOCKLIST: string[];
3
+ /** Returns true when the basename of `cwd` exactly matches a blocklisted tool/meta directory. */
4
+ export declare function isBlocklisted(cwd: string): boolean;
5
+ export interface SessionUpdate {
6
+ session_id: string;
7
+ cwd: string;
8
+ last_message?: string;
9
+ last_activity: Date;
10
+ }
11
+ export declare function readLastLine(filePath: string): Promise<string | undefined>;
12
+ export declare function extractCwdFromPath(filePath: string): string;
13
+ export declare function processTranscriptFile(filePath: string, onUpdate: (update: SessionUpdate) => void): Promise<void>;
14
+ /**
15
+ * Watches JSONL transcripts for live last_message updates on existing sessions.
16
+ * Session creation and deletion is owned by the process poller — this watcher
17
+ * only fires on 'change' events (ignoreInitial) to keep last_message current.
18
+ */
19
+ export declare function watchSessions(onUpdate: (update: SessionUpdate) => void): FSWatcher;
20
+ //# sourceMappingURL=session-watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-watcher.d.ts","sourceRoot":"","sources":["../../src/sessions/session-watcher.ts"],"names":[],"mappings":"AAGA,OAAiB,EAAE,KAAK,SAAS,EAAE,MAAM,UAAU,CAAC;AAEpD,eAAO,MAAM,iBAAiB,UAK7B,CAAC;AAEF,iGAAiG;AACjG,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAGlD;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,IAAI,CAAC;CACrB;AAED,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAIhF;AAKD,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG3D;AAED,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,GACxC,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,GACxC,SAAS,CAaX"}
@@ -0,0 +1,71 @@
1
+ import fs from 'node:fs/promises';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import chokidar from 'chokidar';
5
+ export const SESSION_BLOCKLIST = [
6
+ 'agentvigil-mac', // Mac companion — never a monitored session
7
+ 'node_modules',
8
+ '.claude',
9
+ '.agentvigil',
10
+ ];
11
+ /** Returns true when the basename of `cwd` exactly matches a blocklisted tool/meta directory. */
12
+ export function isBlocklisted(cwd) {
13
+ const basename = path.basename(cwd).toLowerCase();
14
+ return SESSION_BLOCKLIST.includes(basename);
15
+ }
16
+ export async function readLastLine(filePath) {
17
+ const content = await fs.readFile(filePath, 'utf8');
18
+ const lines = content.split('\n').map((line) => line.trim()).filter(Boolean);
19
+ return lines.at(-1);
20
+ }
21
+ // Claude Code project directory names are the cwd with '/' replaced by '-'.
22
+ // That's lossy for paths containing dashes, so this is only a fallback for
23
+ // when a transcript entry doesn't carry its own `cwd` field.
24
+ export function extractCwdFromPath(filePath) {
25
+ const projectDir = path.basename(path.dirname(filePath));
26
+ return projectDir.replace(/-/g, '/');
27
+ }
28
+ export async function processTranscriptFile(filePath, onUpdate) {
29
+ try {
30
+ const lastLine = await readLastLine(filePath);
31
+ if (!lastLine)
32
+ return;
33
+ const entry = JSON.parse(lastLine);
34
+ // Skip sessions that have already ended — they show up in the initial
35
+ // chokidar scan but should not appear as active sessions on startup.
36
+ const hookEvent = typeof entry.hook_event_name === 'string' ? entry.hook_event_name : '';
37
+ if (hookEvent === 'Stop' || hookEvent === 'SubagentStop')
38
+ return;
39
+ if (typeof entry.state === 'string' && entry.state === 'done')
40
+ return;
41
+ const cwd = typeof entry.cwd === 'string' ? entry.cwd : extractCwdFromPath(filePath);
42
+ if (isBlocklisted(cwd))
43
+ return;
44
+ onUpdate({
45
+ session_id: path.basename(filePath, '.jsonl'),
46
+ cwd,
47
+ last_message: typeof entry.message === 'string' ? entry.message : undefined,
48
+ last_activity: new Date(),
49
+ });
50
+ }
51
+ catch {
52
+ // Malformed line or unreadable file — skip it, the watcher keeps running.
53
+ }
54
+ }
55
+ /**
56
+ * Watches JSONL transcripts for live last_message updates on existing sessions.
57
+ * Session creation and deletion is owned by the process poller — this watcher
58
+ * only fires on 'change' events (ignoreInitial) to keep last_message current.
59
+ */
60
+ export function watchSessions(onUpdate) {
61
+ const projectsDir = path.join(os.homedir(), '.claude', 'projects');
62
+ const watcher = chokidar.watch(`${projectsDir}/**/*.jsonl`, {
63
+ ignoreInitial: true, // poller owns detection; watcher is supplementary only
64
+ awaitWriteFinish: { stabilityThreshold: 100, pollInterval: 50 },
65
+ });
66
+ watcher.on('change', (filePath) => {
67
+ void processTranscriptFile(filePath, onUpdate);
68
+ });
69
+ return watcher;
70
+ }
71
+ //# sourceMappingURL=session-watcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-watcher.js","sourceRoot":"","sources":["../../src/sessions/session-watcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export interface TmuxPane {
2
+ pane_id: string;
3
+ pid: string;
4
+ command: string;
5
+ cwd: string;
6
+ }
7
+ export declare function isPidAlive(pid: string): boolean;
8
+ export declare function enumerateTmuxSessions(): Promise<TmuxPane[]>;
9
+ export declare function findTmuxPaneForSession(cwd: string, panes: TmuxPane[]): TmuxPane | undefined;
10
+ export declare function sendMacNotification(title: string, message: string): Promise<void>;
11
+ //# sourceMappingURL=tmux-bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tmux-bridge.d.ts","sourceRoot":"","sources":["../../src/sessions/tmux-bridge.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAO/C;AAWD,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC,CAwBjE;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,QAAQ,GAAG,SAAS,CAK3F;AAMD,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOvF"}
@@ -0,0 +1,67 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { logger } from '../utils/logger.js';
3
+ import { isBlocklisted } from './session-watcher.js';
4
+ const AGENT_COMMANDS = ['claude', 'codex', 'amp'];
5
+ export function isPidAlive(pid) {
6
+ try {
7
+ process.kill(parseInt(pid, 10), 0);
8
+ return true;
9
+ }
10
+ catch {
11
+ return false;
12
+ }
13
+ }
14
+ function runCommand(command, args) {
15
+ return new Promise((resolve, reject) => {
16
+ execFile(command, args, (error, stdout) => {
17
+ if (error)
18
+ reject(error);
19
+ else
20
+ resolve(stdout.toString());
21
+ });
22
+ });
23
+ }
24
+ export async function enumerateTmuxSessions() {
25
+ try {
26
+ const stdout = await runCommand('tmux', [
27
+ 'list-panes',
28
+ '-a',
29
+ '-F',
30
+ '#{pane_id}|#{pane_pid}|#{pane_current_command}|#{pane_current_path}',
31
+ ]);
32
+ return stdout
33
+ .trim()
34
+ .split('\n')
35
+ .filter(Boolean)
36
+ .map((line) => {
37
+ const [pane_id, pid, command, cwd] = line.split('|');
38
+ return { pane_id, pid, command, cwd };
39
+ })
40
+ .filter((pane) => AGENT_COMMANDS.some((agent) => pane.command.includes(agent)))
41
+ .filter((pane) => isPidAlive(pane.pid))
42
+ .filter((pane) => !isBlocklisted(pane.cwd));
43
+ }
44
+ catch {
45
+ // tmux not running or not installed
46
+ return [];
47
+ }
48
+ }
49
+ export function findTmuxPaneForSession(cwd, panes) {
50
+ // Prefer the most specific match so a nested-directory session binds to its
51
+ // own pane rather than a parent directory's (e.g. monorepo sub-packages).
52
+ const candidates = panes.filter((pane) => pane.cwd === cwd || cwd.startsWith(`${pane.cwd}/`));
53
+ return candidates.sort((a, b) => b.cwd.length - a.cwd.length)[0];
54
+ }
55
+ function appleScriptString(value) {
56
+ return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
57
+ }
58
+ export async function sendMacNotification(title, message) {
59
+ const script = `display notification ${appleScriptString(message)} with title ${appleScriptString(title)} sound name "Funk"`;
60
+ try {
61
+ await runCommand('osascript', ['-e', script]);
62
+ }
63
+ catch (err) {
64
+ logger.warn('Failed to display Mac notification', err);
65
+ }
66
+ }
67
+ //# sourceMappingURL=tmux-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tmux-bridge.js","sourceRoot":"","sources":["../../src/sessions/tmux-bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export declare class TunnelManager {
2
+ private process;
3
+ private tunnelUrl;
4
+ start(port: number): Promise<string>;
5
+ getTunnelUrl(): string | null;
6
+ stop(): void;
7
+ }
8
+ //# sourceMappingURL=tunnel-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tunnel-manager.d.ts","sourceRoot":"","sources":["../../src/tunnel/tunnel-manager.ts"],"names":[],"mappings":"AAMA,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,SAAS,CAAuB;IAElC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA8C1C,YAAY,IAAI,MAAM,GAAG,IAAI;IAI7B,IAAI,IAAI,IAAI;CAKb"}
@@ -0,0 +1,57 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { logger } from '../utils/logger.js';
3
+ const TUNNEL_URL_PATTERN = /https:\/\/[a-z0-9-]+\.trycloudflare\.com/;
4
+ const TUNNEL_URL_TIMEOUT_MS = 30_000;
5
+ export class TunnelManager {
6
+ process = null;
7
+ tunnelUrl = null;
8
+ async start(port) {
9
+ return new Promise((resolve, reject) => {
10
+ let settled = false;
11
+ let timeoutHandle;
12
+ const settle = (action) => {
13
+ if (settled)
14
+ return;
15
+ settled = true;
16
+ clearTimeout(timeoutHandle);
17
+ action();
18
+ };
19
+ this.process = spawn('cloudflared', [
20
+ 'tunnel',
21
+ '--url',
22
+ `http://localhost:${port}`,
23
+ '--no-autoupdate',
24
+ '--logfile',
25
+ '/dev/null',
26
+ ]);
27
+ this.process.stderr?.on('data', (data) => {
28
+ const match = data.toString().match(TUNNEL_URL_PATTERN);
29
+ if (match && !this.tunnelUrl) {
30
+ this.tunnelUrl = match[0].replace('https://', 'wss://');
31
+ logger.success(`Tunnel established: ${this.tunnelUrl}`);
32
+ settle(() => resolve(this.tunnelUrl));
33
+ }
34
+ });
35
+ this.process.on('exit', (code) => {
36
+ if (!this.tunnelUrl) {
37
+ settle(() => reject(new Error(`cloudflared exited with code ${code}`)));
38
+ }
39
+ });
40
+ this.process.on('error', (err) => {
41
+ settle(() => reject(err));
42
+ });
43
+ timeoutHandle = setTimeout(() => {
44
+ settle(() => reject(new Error('Tunnel URL not received within 30s')));
45
+ }, TUNNEL_URL_TIMEOUT_MS);
46
+ });
47
+ }
48
+ getTunnelUrl() {
49
+ return this.tunnelUrl;
50
+ }
51
+ stop() {
52
+ this.process?.kill();
53
+ this.process = null;
54
+ this.tunnelUrl = null;
55
+ }
56
+ }
57
+ //# sourceMappingURL=tunnel-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tunnel-manager.js","sourceRoot":"","sources":["../../src/tunnel/tunnel-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,kBAAkB,GAAG,0CAA0C,CAAC;AACtE,MAAM,qBAAqB,GAAG,MAAM,CAAC;AAErC,MAAM,OAAO,aAAa;IAChB,OAAO,GAAwB,IAAI,CAAC;IACpC,SAAS,GAAkB,IAAI,CAAC;IAExC,KAAK,CAAC,KAAK,CAAC,IAAY;QACtB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,OAAO,GAAG,KAAK,CAAC;YACpB,IAAI,aAA4C,CAAC;YAEjD,MAAM,MAAM,GAAG,CAAC,MAAkB,EAAE,EAAE;gBACpC,IAAI,OAAO;oBAAE,OAAO;gBACpB,OAAO,GAAG,IAAI,CAAC;gBACf,YAAY,CAAC,aAAa,CAAC,CAAC;gBAC5B,MAAM,EAAE,CAAC;YACX,CAAC,CAAC;YAEF,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,aAAa,EAAE;gBAClC,QAAQ;gBACR,OAAO;gBACP,oBAAoB,IAAI,EAAE;gBAC1B,iBAAiB;gBACjB,WAAW;gBACX,WAAW;aACZ,CAAC,CAAC;YAEH,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE;gBAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;gBACxD,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;oBAC7B,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;oBACxD,MAAM,CAAC,OAAO,CAAC,uBAAuB,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;oBACxD,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,SAAU,CAAC,CAAC,CAAC;gBACzC,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;gBAC/B,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;oBACpB,MAAM,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,gCAAgC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;gBAC1E,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBAC/B,MAAM,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAC5B,CAAC,CAAC,CAAC;YAEH,aAAa,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC9B,MAAM,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC,CAAC,CAAC;YACxE,CAAC,EAAE,qBAAqB,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,YAAY;QACV,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,IAAI;QACF,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;IACxB,CAAC;CACF"}
@@ -0,0 +1,48 @@
1
+ import { WebSocket } from 'ws';
2
+ import type { AgentEvent, PhoneCommand } from '../types.js';
3
+ export interface PairRequest {
4
+ type: 'pair';
5
+ pub_key: string;
6
+ device_name: string;
7
+ }
8
+ export interface AgentVigilWsServerOptions {
9
+ /** The phone's first message after scanning the QR arrives unencrypted as this. */
10
+ onPairRequest?: (request: PairRequest, socket: WebSocket) => void;
11
+ /** Decrypted phone commands (approve/deny/send_prompt), once a shared secret is known. */
12
+ onCommand?: (command: PhoneCommand) => void;
13
+ /** Supplies the current session list for the full_sync sent on connect. */
14
+ getSessions?: () => AgentEvent[];
15
+ /**
16
+ * AgentEvents forwarded in-process by short-lived `agentvigil hook` CLI
17
+ * invocations (see hook-handler.ts) — the only way they can reach the live
18
+ * phone connection the daemon is holding. Requires a matching `localToken`.
19
+ */
20
+ onHookEvent?: (event: AgentEvent) => void;
21
+ /** Shared secret only the Mac's own processes know (its X25519 secret key) — authenticates `hook_event` messages so the tunnel-exposed phone can't forge them. */
22
+ localToken?: string;
23
+ }
24
+ export declare class AgentVigilWsServer {
25
+ private readonly port;
26
+ private readonly wss;
27
+ private readonly getSessions;
28
+ private readonly onPairRequest?;
29
+ private readonly onCommand?;
30
+ private readonly onHookEvent?;
31
+ private readonly localToken?;
32
+ private phoneSocket;
33
+ private sharedSecret;
34
+ private heartbeatTimer;
35
+ constructor(port?: number, options?: AgentVigilWsServerOptions);
36
+ start(): void;
37
+ /** Records the shared secret derived for the currently-paired device (or clears it). */
38
+ setSharedSecret(secret: string | undefined): void;
39
+ /** The NaCl shared secret for the paired phone, or undefined if not yet paired. */
40
+ getSharedSecret(): string | undefined;
41
+ onMessage(raw: string): void;
42
+ sendEvent(event: AgentEvent): void;
43
+ sendFullSync(): void;
44
+ get isPhoneConnected(): boolean;
45
+ stop(): void;
46
+ private sendHeartbeat;
47
+ }
48
+ //# sourceMappingURL=websocket-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"websocket-server.d.ts","sourceRoot":"","sources":["../../src/tunnel/websocket-server.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,SAAS,EAAgB,MAAM,IAAI,CAAC;AAG9D,OAAO,KAAK,EAAE,UAAU,EAAiB,YAAY,EAAE,MAAM,aAAa,CAAC;AAI3E,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,yBAAyB;IACxC,mFAAmF;IACnF,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,KAAK,IAAI,CAAC;IAClE,0FAA0F;IAC1F,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC;IAC5C,2EAA2E;IAC3E,WAAW,CAAC,EAAE,MAAM,UAAU,EAAE,CAAC;IACjC;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IAC1C,kKAAkK;IAClK,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,kBAAkB;IAYjB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAXjC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkB;IACtC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IACjD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAoD;IACnF,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAkC;IAC7D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAA8B;IAC3D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAS;IAErC,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,cAAc,CAA+C;gBAExC,IAAI,GAAE,MAAa,EAAE,OAAO,GAAE,yBAA8B;IASzF,KAAK,IAAI,IAAI;IAyCb,wFAAwF;IACxF,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAIjD,mFAAmF;IACnF,eAAe,IAAI,MAAM,GAAG,SAAS;IAIrC,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IA2D5B,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAMlC,YAAY,IAAI,IAAI;IAepB,IAAI,gBAAgB,IAAI,OAAO,CAE9B;IAED,IAAI,IAAI,IAAI;IAUZ,OAAO,CAAC,aAAa;CAYtB"}
@@ -0,0 +1,173 @@
1
+ import { WebSocketServer, WebSocket } from 'ws';
2
+ import { logger } from '../utils/logger.js';
3
+ import { decrypt, encrypt } from '../crypto/encryption.js';
4
+ const HEARTBEAT_INTERVAL_MS = 30_000;
5
+ export class AgentVigilWsServer {
6
+ port;
7
+ wss;
8
+ getSessions;
9
+ onPairRequest;
10
+ onCommand;
11
+ onHookEvent;
12
+ localToken;
13
+ phoneSocket = null;
14
+ sharedSecret;
15
+ heartbeatTimer = null;
16
+ constructor(port = 3847, options = {}) {
17
+ this.port = port;
18
+ this.wss = new WebSocketServer({ port });
19
+ this.getSessions = options.getSessions ?? (() => []);
20
+ this.onPairRequest = options.onPairRequest;
21
+ this.onCommand = options.onCommand;
22
+ this.onHookEvent = options.onHookEvent;
23
+ this.localToken = options.localToken;
24
+ }
25
+ start() {
26
+ this.wss.on('connection', (ws, request) => {
27
+ // Short-lived connections forwarded by `agentvigil hook` CLI invocations
28
+ // (see hook-handler.ts) hit a dedicated path so they're never mistaken
29
+ // for the phone — otherwise they'd overwrite `phoneSocket` and the real
30
+ // phone connection would be silently orphaned when the hook's socket closes.
31
+ if (request?.url?.startsWith('/hook')) {
32
+ ws.on('message', (data) => {
33
+ try {
34
+ this.onMessage(data.toString());
35
+ }
36
+ catch (err) {
37
+ logger.error('Error handling hook_event message', err);
38
+ }
39
+ });
40
+ ws.on('error', (err) => logger.error('Hook connection WS error', err));
41
+ return;
42
+ }
43
+ logger.info('Phone connected via tunnel');
44
+ this.phoneSocket = ws;
45
+ ws.on('message', (data) => {
46
+ try {
47
+ this.onMessage(data.toString());
48
+ }
49
+ catch (err) {
50
+ logger.error('Error handling phone message — keeping socket open', err);
51
+ }
52
+ });
53
+ ws.on('close', () => {
54
+ logger.warn('Phone disconnected');
55
+ if (this.phoneSocket === ws)
56
+ this.phoneSocket = null;
57
+ });
58
+ ws.on('error', (err) => logger.error('WS error', err));
59
+ this.sendFullSync();
60
+ });
61
+ this.heartbeatTimer = setInterval(() => this.sendHeartbeat(), HEARTBEAT_INTERVAL_MS);
62
+ logger.success(`WS server listening on port ${this.port}`);
63
+ }
64
+ /** Records the shared secret derived for the currently-paired device (or clears it). */
65
+ setSharedSecret(secret) {
66
+ this.sharedSecret = secret;
67
+ }
68
+ /** The NaCl shared secret for the paired phone, or undefined if not yet paired. */
69
+ getSharedSecret() {
70
+ return this.sharedSecret;
71
+ }
72
+ onMessage(raw) {
73
+ let parsed;
74
+ try {
75
+ parsed = JSON.parse(raw);
76
+ }
77
+ catch {
78
+ logger.warn('Received a malformed WS message — ignoring');
79
+ return;
80
+ }
81
+ // Forwarded by a short-lived `agentvigil hook` CLI process — authenticated
82
+ // with a local-only token (the Mac's own secret key) rather than the
83
+ // phone's shared secret, since the hook process isn't "paired".
84
+ if (parsed?.type === 'hook_event') {
85
+ if (this.localToken && parsed.token === this.localToken) {
86
+ this.onHookEvent?.(parsed.event);
87
+ }
88
+ else {
89
+ logger.warn('Rejected hook_event message with an invalid local token');
90
+ }
91
+ return;
92
+ }
93
+ // The pairing handshake is the one message that travels unencrypted.
94
+ if (parsed?.type === 'pair') {
95
+ if (this.phoneSocket) {
96
+ try {
97
+ this.onPairRequest?.({ type: 'pair', pub_key: parsed.pub_key, device_name: parsed.device_name }, this.phoneSocket);
98
+ }
99
+ catch (err) {
100
+ logger.error('Pairing handshake failed', err);
101
+ this.phoneSocket.send(JSON.stringify({ type: 'pairing_error', message: String(err) }));
102
+ // DO NOT close the socket — let the phone retry
103
+ }
104
+ }
105
+ return;
106
+ }
107
+ if (!this.sharedSecret) {
108
+ logger.warn('Received a message before pairing was established — ignoring');
109
+ return;
110
+ }
111
+ try {
112
+ const decrypted = decrypt(parsed.payload, this.sharedSecret);
113
+ const command = JSON.parse(decrypted);
114
+ // full_sync_request is a WS-layer concern — respond immediately with
115
+ // the current session snapshot rather than forwarding to the relay.
116
+ if (command.type === 'full_sync_request') {
117
+ logger.dim('full_sync_request from phone — sending snapshot');
118
+ this.sendFullSync();
119
+ return;
120
+ }
121
+ this.onCommand?.(command);
122
+ }
123
+ catch (err) {
124
+ logger.error('Failed to decrypt phone message', err);
125
+ }
126
+ }
127
+ sendEvent(event) {
128
+ if (!this.isPhoneConnected || !this.sharedSecret)
129
+ return;
130
+ const encrypted = encrypt(JSON.stringify(event), this.sharedSecret);
131
+ this.phoneSocket.send(JSON.stringify({ payload: encrypted }));
132
+ }
133
+ sendFullSync() {
134
+ const sessions = this.getSessions();
135
+ const event = {
136
+ type: 'full_sync',
137
+ session_id: 'full_sync',
138
+ project_name: 'AgentVigil',
139
+ cwd: '',
140
+ agent: 'claude-code',
141
+ message: `${sessions.length} active session(s)`,
142
+ timestamp: new Date().toISOString(),
143
+ sessions,
144
+ };
145
+ this.sendEvent(event);
146
+ }
147
+ get isPhoneConnected() {
148
+ return this.phoneSocket?.readyState === WebSocket.OPEN;
149
+ }
150
+ stop() {
151
+ if (this.heartbeatTimer) {
152
+ clearInterval(this.heartbeatTimer);
153
+ this.heartbeatTimer = null;
154
+ }
155
+ this.phoneSocket?.close();
156
+ this.phoneSocket = null;
157
+ this.wss.close();
158
+ }
159
+ sendHeartbeat() {
160
+ if (!this.isPhoneConnected)
161
+ return;
162
+ this.sendEvent({
163
+ type: 'heartbeat',
164
+ session_id: 'heartbeat',
165
+ project_name: 'AgentVigil',
166
+ cwd: '',
167
+ agent: 'claude-code',
168
+ message: 'heartbeat',
169
+ timestamp: new Date().toISOString(),
170
+ });
171
+ }
172
+ }
173
+ //# sourceMappingURL=websocket-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"websocket-server.js","sourceRoot":"","sources":["../../src/tunnel/websocket-server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,SAAS,EAAgB,MAAM,IAAI,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAG3D,MAAM,qBAAqB,GAAG,MAAM,CAAC;AAyBrC,MAAM,OAAO,kBAAkB;IAYA;IAXZ,GAAG,CAAkB;IACrB,WAAW,CAAqB;IAChC,aAAa,CAAqD;IAClE,SAAS,CAAmC;IAC5C,WAAW,CAA+B;IAC1C,UAAU,CAAU;IAE7B,WAAW,GAAqB,IAAI,CAAC;IACrC,YAAY,CAAqB;IACjC,cAAc,GAA0C,IAAI,CAAC;IAErE,YAA6B,OAAe,IAAI,EAAE,UAAqC,EAAE;QAA5D,SAAI,GAAJ,IAAI,CAAe;QAC9C,IAAI,CAAC,GAAG,GAAG,IAAI,eAAe,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QACzC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QACrD,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;QAC3C,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QACnC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;QACvC,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IACvC,CAAC;IAED,KAAK;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,EAAa,EAAE,OAAyB,EAAE,EAAE;YACrE,yEAAyE;YACzE,uEAAuE;YACvE,wEAAwE;YACxE,6EAA6E;YAC7E,IAAI,OAAO,EAAE,GAAG,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;gBACtC,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAa,EAAE,EAAE;oBACjC,IAAI,CAAC;wBACH,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;oBAClC,CAAC;oBAAC,OAAO,GAAG,EAAE,CAAC;wBACb,MAAM,CAAC,KAAK,CAAC,mCAAmC,EAAE,GAAG,CAAC,CAAC;oBACzD,CAAC;gBACH,CAAC,CAAC,CAAC;gBACH,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,0BAA0B,EAAE,GAAG,CAAC,CAAC,CAAC;gBACvE,OAAO;YACT,CAAC;YAED,MAAM,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;YAC1C,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC;YAEtB,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAa,EAAE,EAAE;gBACjC,IAAI,CAAC;oBACH,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAClC,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,CAAC,KAAK,CAAC,oDAAoD,EAAE,GAAG,CAAC,CAAC;gBAC1E,CAAC;YACH,CAAC,CAAC,CAAC;YACH,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;gBAClB,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;gBAClC,IAAI,IAAI,CAAC,WAAW,KAAK,EAAE;oBAAE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;YACvD,CAAC,CAAC,CAAC;YACH,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC;YAEvD,IAAI,CAAC,YAAY,EAAE,CAAC;QACtB,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,cAAc,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,qBAAqB,CAAC,CAAC;QACrF,MAAM,CAAC,OAAO,CAAC,+BAA+B,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,wFAAwF;IACxF,eAAe,CAAC,MAA0B;QACxC,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC;IAC7B,CAAC;IAED,mFAAmF;IACnF,eAAe;QACb,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED,SAAS,CAAC,GAAW;QACnB,IAAI,MAAW,CAAC;QAChB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;YAC1D,OAAO;QACT,CAAC;QAED,2EAA2E;QAC3E,qEAAqE;QACrE,gEAAgE;QAChE,IAAI,MAAM,EAAE,IAAI,KAAK,YAAY,EAAE,CAAC;YAClC,IAAI,IAAI,CAAC,UAAU,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC,UAAU,EAAE,CAAC;gBACxD,IAAI,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,KAAmB,CAAC,CAAC;YACjD,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC;YACzE,CAAC;YACD,OAAO;QACT,CAAC;QAED,qEAAqE;QACrE,IAAI,MAAM,EAAE,IAAI,KAAK,MAAM,EAAE,CAAC;YAC5B,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;gBACrB,IAAI,CAAC;oBACH,IAAI,CAAC,aAAa,EAAE,CAClB,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,EAC1E,IAAI,CAAC,WAAW,CACjB,CAAC;gBACJ,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,CAAC,KAAK,CAAC,0BAA0B,EAAE,GAAG,CAAC,CAAC;oBAC9C,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;oBACvF,gDAAgD;gBAClD,CAAC;YACH,CAAC;YACD,OAAO;QACT,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YACvB,MAAM,CAAC,IAAI,CAAC,8DAA8D,CAAC,CAAC;YAC5E,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;YAC7D,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAA2D,CAAC;YAChG,qEAAqE;YACrE,oEAAoE;YACpE,IAAI,OAAO,CAAC,IAAI,KAAK,mBAAmB,EAAE,CAAC;gBACzC,MAAM,CAAC,GAAG,CAAC,iDAAiD,CAAC,CAAC;gBAC9D,IAAI,CAAC,YAAY,EAAE,CAAC;gBACpB,OAAO;YACT,CAAC;YACD,IAAI,CAAC,SAAS,EAAE,CAAC,OAAuB,CAAC,CAAC;QAC5C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,KAAK,CAAC,iCAAiC,EAAE,GAAG,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAED,SAAS,CAAC,KAAiB;QACzB,IAAI,CAAC,IAAI,CAAC,gBAAgB,IAAI,CAAC,IAAI,CAAC,YAAY;YAAE,OAAO;QACzD,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QACpE,IAAI,CAAC,WAAY,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;IACjE,CAAC;IAED,YAAY;QACV,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QACpC,MAAM,KAAK,GAAkB;YAC3B,IAAI,EAAE,WAAW;YACjB,UAAU,EAAE,WAAW;YACvB,YAAY,EAAE,YAAY;YAC1B,GAAG,EAAE,EAAE;YACP,KAAK,EAAE,aAAa;YACpB,OAAO,EAAE,GAAG,QAAQ,CAAC,MAAM,oBAAoB;YAC/C,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,QAAQ;SACT,CAAC;QACF,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACxB,CAAC;IAED,IAAI,gBAAgB;QAClB,OAAO,IAAI,CAAC,WAAW,EAAE,UAAU,KAAK,SAAS,CAAC,IAAI,CAAC;IACzD,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACxB,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YACnC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC7B,CAAC;QACD,IAAI,CAAC,WAAW,EAAE,KAAK,EAAE,CAAC;QAC1B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC;IAEO,aAAa;QACnB,IAAI,CAAC,IAAI,CAAC,gBAAgB;YAAE,OAAO;QACnC,IAAI,CAAC,SAAS,CAAC;YACb,IAAI,EAAE,WAAW;YACjB,UAAU,EAAE,WAAW;YACvB,YAAY,EAAE,YAAY;YAC1B,GAAG,EAAE,EAAE;YACP,KAAK,EAAE,aAAa;YACpB,OAAO,EAAE,WAAW;YACpB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,37 @@
1
+ export interface HookPayload {
2
+ session_id: string;
3
+ transcript_path: string;
4
+ cwd: string;
5
+ hook_event_name: string;
6
+ notification_type?: string;
7
+ message?: string;
8
+ }
9
+ export type AgentEventType = 'permission_prompt' | 'task_complete' | 'session_error' | 'idle_waiting' | 'session_started' | 'session_ended' | 'session_updated' | 'heartbeat' | 'full_sync';
10
+ export interface AgentEvent {
11
+ type: AgentEventType;
12
+ session_id: string;
13
+ project_name: string;
14
+ cwd: string;
15
+ agent: 'claude-code' | 'codex' | 'amp';
16
+ message: string;
17
+ permission_command?: string;
18
+ tool_name?: string;
19
+ tool_input?: string;
20
+ timestamp: string;
21
+ pid?: string;
22
+ }
23
+ export interface FullSyncEvent extends AgentEvent {
24
+ type: 'full_sync';
25
+ sessions: AgentEvent[];
26
+ }
27
+ export type PhoneCommandType = 'approve' | 'deny' | 'send_prompt' | 'heartbeat' | 'register_fcm_token';
28
+ export interface PhoneCommand {
29
+ type: PhoneCommandType;
30
+ session_id: string;
31
+ payload?: string;
32
+ /** FCM registration token — present on 'register_fcm_token' commands. */
33
+ token?: string;
34
+ /** Human-readable phone name — present on 'register_fcm_token' commands. */
35
+ device_name?: string;
36
+ }
37
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,cAAc,GACtB,mBAAmB,GACnB,eAAe,GACf,eAAe,GACf,cAAc,GACd,iBAAiB,GACjB,eAAe,GACf,iBAAiB,GACjB,WAAW,GACX,WAAW,CAAC;AAEhB,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,cAAc,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,aAAa,GAAG,OAAO,GAAG,KAAK,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAc,SAAQ,UAAU;IAC/C,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,EAAE,UAAU,EAAE,CAAC;CACxB;AAED,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,MAAM,GAAG,aAAa,GAAG,WAAW,GAAG,oBAAoB,CAAC;AAEvG,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,gBAAgB,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}