archer-wizard 0.1.1 → 0.2.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 (53) hide show
  1. package/dist/daemon/client.d.ts +4 -0
  2. package/dist/daemon/client.d.ts.map +1 -0
  3. package/dist/daemon/client.js +44 -0
  4. package/dist/daemon/client.js.map +1 -0
  5. package/dist/daemon/lifecycle.d.ts +9 -0
  6. package/dist/daemon/lifecycle.d.ts.map +1 -0
  7. package/dist/daemon/lifecycle.js +118 -0
  8. package/dist/daemon/lifecycle.js.map +1 -0
  9. package/dist/daemon/process.d.ts +2 -0
  10. package/dist/daemon/process.d.ts.map +1 -0
  11. package/dist/daemon/process.js +171 -0
  12. package/dist/daemon/process.js.map +1 -0
  13. package/dist/daemon/run.d.ts +2 -0
  14. package/dist/daemon/run.d.ts.map +1 -0
  15. package/dist/daemon/run.js +5 -0
  16. package/dist/daemon/run.js.map +1 -0
  17. package/dist/daemon/store.d.ts +9 -0
  18. package/dist/daemon/store.d.ts.map +1 -0
  19. package/dist/daemon/store.js +53 -0
  20. package/dist/daemon/store.js.map +1 -0
  21. package/dist/daemon/types.d.ts +47 -0
  22. package/dist/daemon/types.d.ts.map +1 -0
  23. package/dist/daemon/types.js +10 -0
  24. package/dist/daemon/types.js.map +1 -0
  25. package/dist/index.js +105 -21
  26. package/dist/index.js.map +1 -1
  27. package/dist/lib/ascii.d.ts.map +1 -1
  28. package/dist/lib/ascii.js +9 -41
  29. package/dist/lib/ascii.js.map +1 -1
  30. package/dist/tools/unwatch.d.ts +7 -0
  31. package/dist/tools/unwatch.d.ts.map +1 -0
  32. package/dist/tools/unwatch.js +20 -0
  33. package/dist/tools/unwatch.js.map +1 -0
  34. package/dist/tools/watch.d.ts +14 -34
  35. package/dist/tools/watch.d.ts.map +1 -1
  36. package/dist/tools/watch.js +36 -170
  37. package/dist/tools/watch.js.map +1 -1
  38. package/dist/tools/watches.d.ts +2 -0
  39. package/dist/tools/watches.d.ts.map +1 -0
  40. package/dist/tools/watches.js +33 -0
  41. package/dist/tools/watches.js.map +1 -0
  42. package/package.json +2 -1
  43. package/src/daemon/client.ts +50 -0
  44. package/src/daemon/lifecycle.ts +126 -0
  45. package/src/daemon/process.ts +207 -0
  46. package/src/daemon/run.ts +6 -0
  47. package/src/daemon/store.ts +60 -0
  48. package/src/daemon/types.ts +41 -0
  49. package/src/index.ts +111 -22
  50. package/src/lib/ascii.ts +9 -44
  51. package/src/tools/unwatch.ts +26 -0
  52. package/src/tools/watch.ts +46 -212
  53. package/src/tools/watches.ts +37 -0
@@ -0,0 +1,4 @@
1
+ import type { IpcRequest, IpcResponse } from './types.js';
2
+ export declare function sendCommand(req: IpcRequest): Promise<IpcResponse>;
3
+ export declare function isDaemonRunning(): Promise<boolean>;
4
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/daemon/client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAI1D,wBAAgB,WAAW,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAgCjE;AAID,wBAAsB,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,CAOxD"}
@@ -0,0 +1,44 @@
1
+ import net from 'node:net';
2
+ import { DAEMON_PORT, DAEMON_HOST } from './types.js';
3
+ // ─── Send Command to Daemon ─────────────────────────────────
4
+ export function sendCommand(req) {
5
+ return new Promise((resolve, reject) => {
6
+ const socket = net.createConnection({ host: DAEMON_HOST, port: DAEMON_PORT }, () => {
7
+ socket.write(JSON.stringify(req) + '\n');
8
+ });
9
+ let buffer = '';
10
+ socket.on('data', (data) => {
11
+ buffer += data.toString();
12
+ const newlineIdx = buffer.indexOf('\n');
13
+ if (newlineIdx !== -1) {
14
+ const line = buffer.slice(0, newlineIdx).trim();
15
+ socket.end();
16
+ try {
17
+ resolve(JSON.parse(line));
18
+ }
19
+ catch (err) {
20
+ reject(new Error(`invalid daemon response: ${line}`));
21
+ }
22
+ }
23
+ });
24
+ socket.on('error', (err) => {
25
+ reject(new Error(`daemon connection failed: ${err.message}`));
26
+ });
27
+ // Timeout after 5 seconds
28
+ socket.setTimeout(5_000, () => {
29
+ socket.destroy();
30
+ reject(new Error('daemon response timeout'));
31
+ });
32
+ });
33
+ }
34
+ // ─── Check if Daemon is Running ─────────────────────────────
35
+ export async function isDaemonRunning() {
36
+ try {
37
+ const res = await sendCommand({ type: 'ping' });
38
+ return res.ok && res.type === 'pong';
39
+ }
40
+ catch {
41
+ return false;
42
+ }
43
+ }
44
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/daemon/client.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,UAAU,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAGtD,+DAA+D;AAE/D,MAAM,UAAU,WAAW,CAAC,GAAe;IACzC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,GAAG,CAAC,gBAAgB,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,GAAG,EAAE;YACjF,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;QAEH,IAAI,MAAM,GAAG,EAAE,CAAC;QAEhB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YACzB,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC1B,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACxC,IAAI,UAAU,KAAK,CAAC,CAAC,EAAE,CAAC;gBACtB,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,IAAI,EAAE,CAAC;gBAChD,MAAM,CAAC,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC;oBACH,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAgB,CAAC,CAAC;gBAC3C,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,CAAC,IAAI,KAAK,CAAC,4BAA4B,IAAI,EAAE,CAAC,CAAC,CAAC;gBACxD,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACzB,MAAM,CAAC,IAAI,KAAK,CAAC,6BAA6B,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAChE,CAAC,CAAC,CAAC;QAEH,0BAA0B;QAC1B,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,GAAG,EAAE;YAC5B,MAAM,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,CAAC,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,+DAA+D;AAE/D,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QAChD,OAAO,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -0,0 +1,9 @@
1
+ export declare function startDaemon(): Promise<{
2
+ pid: number;
3
+ alreadyRunning: boolean;
4
+ }>;
5
+ export declare function stopDaemon(): Promise<boolean>;
6
+ export declare function ensureDaemon(): Promise<{
7
+ pid: number;
8
+ }>;
9
+ //# sourceMappingURL=lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../src/daemon/lifecycle.ts"],"names":[],"mappings":"AASA,wBAAsB,WAAW,IAAI,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,OAAO,CAAA;CAAE,CAAC,CAgErF;AAID,wBAAsB,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC,CAcnD;AAID,wBAAsB,YAAY,IAAI,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAG7D"}
@@ -0,0 +1,118 @@
1
+ import { spawn } from 'node:child_process';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { PID_FILE, ARCHER_DIR, LOG_FILE } from './types.js';
6
+ import { isDaemonRunning } from './client.js';
7
+ // ─── Start Daemon ───────────────────────────────────────────
8
+ export async function startDaemon() {
9
+ // Check if already running
10
+ if (await isDaemonRunning()) {
11
+ const pid = readPid();
12
+ return { pid: pid ?? 0, alreadyRunning: true };
13
+ }
14
+ // Ensure directory exists
15
+ if (!fs.existsSync(ARCHER_DIR)) {
16
+ fs.mkdirSync(ARCHER_DIR, { recursive: true });
17
+ }
18
+ // Resolve the daemon entry script
19
+ // In compiled mode this is dist/daemon/process.js
20
+ // In dev mode with tsx this is src/daemon/process.ts
21
+ const thisFile = fileURLToPath(import.meta.url);
22
+ const thisDir = path.dirname(thisFile);
23
+ // Look for the daemon runner script
24
+ const daemonRunner = path.join(thisDir, 'run.js');
25
+ const daemonRunnerTs = path.join(thisDir, 'run.ts');
26
+ let cmd;
27
+ let args;
28
+ if (fs.existsSync(daemonRunner)) {
29
+ // Compiled mode
30
+ cmd = process.execPath; // node
31
+ args = [daemonRunner];
32
+ }
33
+ else if (fs.existsSync(daemonRunnerTs)) {
34
+ // Dev mode — use tsx
35
+ cmd = 'npx';
36
+ args = ['tsx', daemonRunnerTs];
37
+ }
38
+ else {
39
+ // Fallback: assume we're in dist/ and run process.js directly
40
+ const processJs = path.join(thisDir, 'process.js');
41
+ cmd = process.execPath;
42
+ args = [processJs, '--run-daemon'];
43
+ }
44
+ // Open log file for daemon output
45
+ const logFd = fs.openSync(LOG_FILE, 'a');
46
+ const child = spawn(cmd, args, {
47
+ detached: true,
48
+ stdio: ['ignore', logFd, logFd],
49
+ env: { ...process.env },
50
+ });
51
+ child.unref();
52
+ fs.closeSync(logFd);
53
+ const pid = child.pid ?? 0;
54
+ // Wait briefly for daemon to start, then verify
55
+ await sleep(500);
56
+ const running = await isDaemonRunning();
57
+ if (!running) {
58
+ // Give it another moment
59
+ await sleep(1000);
60
+ }
61
+ return { pid, alreadyRunning: false };
62
+ }
63
+ // ─── Stop Daemon ────────────────────────────────────────────
64
+ export async function stopDaemon() {
65
+ const pid = readPid();
66
+ if (pid === null)
67
+ return false;
68
+ try {
69
+ process.kill(pid, 'SIGTERM');
70
+ // Wait for it to die
71
+ await sleep(500);
72
+ return true;
73
+ }
74
+ catch {
75
+ // Process already dead — clean up PID file
76
+ try {
77
+ fs.unlinkSync(PID_FILE);
78
+ }
79
+ catch { }
80
+ return false;
81
+ }
82
+ }
83
+ // ─── Ensure Daemon is Running ───────────────────────────────
84
+ export async function ensureDaemon() {
85
+ const result = await startDaemon();
86
+ return { pid: result.pid };
87
+ }
88
+ // ─── Helpers ────────────────────────────────────────────────
89
+ function readPid() {
90
+ try {
91
+ if (!fs.existsSync(PID_FILE))
92
+ return null;
93
+ const raw = fs.readFileSync(PID_FILE, 'utf-8').trim();
94
+ const pid = parseInt(raw, 10);
95
+ if (isNaN(pid))
96
+ return null;
97
+ // Check if process is alive
98
+ try {
99
+ process.kill(pid, 0); // Signal 0 = existence check
100
+ return pid;
101
+ }
102
+ catch {
103
+ // Process is dead — clean up stale PID
104
+ try {
105
+ fs.unlinkSync(PID_FILE);
106
+ }
107
+ catch { }
108
+ return null;
109
+ }
110
+ }
111
+ catch {
112
+ return null;
113
+ }
114
+ }
115
+ function sleep(ms) {
116
+ return new Promise((resolve) => setTimeout(resolve, ms));
117
+ }
118
+ //# sourceMappingURL=lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../src/daemon/lifecycle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function startDaemonProcess(): void;
2
+ //# sourceMappingURL=process.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../src/daemon/process.ts"],"names":[],"mappings":"AA2HA,wBAAgB,kBAAkB,IAAI,IAAI,CAmFzC"}
@@ -0,0 +1,171 @@
1
+ import net from 'node:net';
2
+ import { createClient } from '@supabase/supabase-js';
3
+ import { DAEMON_PORT, DAEMON_HOST, PID_FILE, ARCHER_DIR, LOG_FILE } from './types.js';
4
+ import { loadWatches, addWatch, removeWatch } from './store.js';
5
+ import fs from 'node:fs';
6
+ // ─── Simple webhook delivery for daemon context ─────────────
7
+ async function deliverWebhook(url, payload) {
8
+ const res = await fetch(url, {
9
+ method: 'POST',
10
+ headers: {
11
+ 'Content-Type': 'application/json',
12
+ 'User-Agent': 'Archer/0.2.0',
13
+ },
14
+ body: JSON.stringify(payload),
15
+ });
16
+ if (!res.ok) {
17
+ throw new Error(`webhook ${res.status} ${res.statusText}`);
18
+ }
19
+ }
20
+ // ─── Active Channel Registry ────────────────────────────────
21
+ const channels = new Map();
22
+ // ─── Subscribe to a Watch ───────────────────────────────────
23
+ function subscribe(watch) {
24
+ // Skip if already subscribed
25
+ if (channels.has(watch.id))
26
+ return;
27
+ const client = createClient(watch.supabaseUrl, watch.supabaseKey);
28
+ const channelName = `daemon-${watch.table}-${watch.id}`;
29
+ const channel = client
30
+ .channel(channelName)
31
+ .on('postgres_changes', {
32
+ event: watch.event,
33
+ schema: 'public',
34
+ table: watch.table,
35
+ ...(watch.filter ? { filter: watch.filter } : {}),
36
+ }, async (payload) => {
37
+ log(`[event] ${watch.table} ${payload.eventType} → watch ${watch.id}`);
38
+ if (watch.webhookUrl) {
39
+ try {
40
+ await deliverWebhook(watch.webhookUrl, payload);
41
+ log(`[webhook] delivered to ${watch.webhookUrl}`);
42
+ }
43
+ catch (err) {
44
+ log(`[webhook] failed: ${err instanceof Error ? err.message : String(err)}`);
45
+ }
46
+ }
47
+ })
48
+ .subscribe((status) => {
49
+ log(`[channel] ${channelName} → ${status}`);
50
+ });
51
+ channels.set(watch.id, { client, channel });
52
+ }
53
+ // ─── Unsubscribe from a Watch ───────────────────────────────
54
+ async function unsubscribe(watchId) {
55
+ const entry = channels.get(watchId);
56
+ if (!entry)
57
+ return;
58
+ await entry.client.removeChannel(entry.channel);
59
+ channels.delete(watchId);
60
+ }
61
+ // ─── Handle IPC Command ─────────────────────────────────────
62
+ async function handleCommand(req) {
63
+ switch (req.type) {
64
+ case 'add_watch': {
65
+ const watches = addWatch(req.watch);
66
+ subscribe(req.watch);
67
+ return { ok: true, type: 'watch_added', watchId: req.watch.id };
68
+ }
69
+ case 'remove_watch': {
70
+ const { watches, removed } = removeWatch(req.watchId);
71
+ if (!removed) {
72
+ return { ok: false, error: `watch ${req.watchId} not found` };
73
+ }
74
+ await unsubscribe(req.watchId);
75
+ return { ok: true, type: 'watch_removed', watchId: req.watchId };
76
+ }
77
+ case 'list_watches': {
78
+ const watches = loadWatches();
79
+ return { ok: true, type: 'watch_list', watches };
80
+ }
81
+ case 'ping': {
82
+ return { ok: true, type: 'pong' };
83
+ }
84
+ default:
85
+ return { ok: false, error: 'unknown command' };
86
+ }
87
+ }
88
+ // ─── Logging ────────────────────────────────────────────────
89
+ function log(message) {
90
+ const ts = new Date().toISOString();
91
+ const line = `[${ts}] ${message}\n`;
92
+ try {
93
+ fs.appendFileSync(LOG_FILE, line);
94
+ }
95
+ catch {
96
+ // If we can't write to log, just continue
97
+ }
98
+ }
99
+ // ─── Main: Start TCP Server ─────────────────────────────────
100
+ export function startDaemonProcess() {
101
+ // Ensure directory exists
102
+ if (!fs.existsSync(ARCHER_DIR)) {
103
+ fs.mkdirSync(ARCHER_DIR, { recursive: true });
104
+ }
105
+ // Write PID file
106
+ fs.writeFileSync(PID_FILE, String(process.pid), 'utf-8');
107
+ log('daemon starting');
108
+ // Re-subscribe all persisted watches
109
+ const watches = loadWatches();
110
+ for (const watch of watches) {
111
+ subscribe(watch);
112
+ log(`[restore] re-subscribed watch ${watch.id} on ${watch.table}`);
113
+ }
114
+ log(`restored ${watches.length} watch(es)`);
115
+ // Create TCP server
116
+ const server = net.createServer((socket) => {
117
+ let buffer = '';
118
+ socket.on('data', async (data) => {
119
+ buffer += data.toString();
120
+ // Process complete JSON lines
121
+ let newlineIdx;
122
+ while ((newlineIdx = buffer.indexOf('\n')) !== -1) {
123
+ const line = buffer.slice(0, newlineIdx).trim();
124
+ buffer = buffer.slice(newlineIdx + 1);
125
+ if (!line)
126
+ continue;
127
+ try {
128
+ const req = JSON.parse(line);
129
+ const res = await handleCommand(req);
130
+ socket.write(JSON.stringify(res) + '\n');
131
+ }
132
+ catch (err) {
133
+ const errRes = {
134
+ ok: false,
135
+ error: `parse error: ${err instanceof Error ? err.message : String(err)}`,
136
+ };
137
+ socket.write(JSON.stringify(errRes) + '\n');
138
+ }
139
+ }
140
+ });
141
+ socket.on('error', (err) => {
142
+ log(`[socket] error: ${err.message}`);
143
+ });
144
+ });
145
+ server.listen(DAEMON_PORT, DAEMON_HOST, () => {
146
+ log(`daemon listening on ${DAEMON_HOST}:${DAEMON_PORT} (pid ${process.pid})`);
147
+ });
148
+ server.on('error', (err) => {
149
+ log(`[server] error: ${err.message}`);
150
+ process.exit(1);
151
+ });
152
+ // Clean shutdown
153
+ const shutdown = () => {
154
+ log('daemon shutting down');
155
+ server.close();
156
+ // Unsubscribe all channels
157
+ for (const [id, entry] of channels) {
158
+ entry.client.removeChannel(entry.channel).catch(() => { });
159
+ }
160
+ channels.clear();
161
+ // Remove PID file
162
+ try {
163
+ fs.unlinkSync(PID_FILE);
164
+ }
165
+ catch { }
166
+ process.exit(0);
167
+ };
168
+ process.on('SIGTERM', shutdown);
169
+ process.on('SIGINT', shutdown);
170
+ }
171
+ //# sourceMappingURL=process.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process.js","sourceRoot":"","sources":["../../src/daemon/process.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/daemon/run.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ // Daemon runner — this file is the entry point for the detached daemon process.
2
+ // It gets spawned by lifecycle.ts and runs startDaemonProcess().
3
+ import { startDaemonProcess } from './process.js';
4
+ startDaemonProcess();
5
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/daemon/run.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,iEAAiE;AAEjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD,kBAAkB,EAAE,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { WatchConfig } from './types.js';
2
+ export declare function loadWatches(): WatchConfig[];
3
+ export declare function saveWatches(watches: WatchConfig[]): void;
4
+ export declare function addWatch(watch: WatchConfig): WatchConfig[];
5
+ export declare function removeWatch(watchId: string): {
6
+ watches: WatchConfig[];
7
+ removed: boolean;
8
+ };
9
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/daemon/store.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAY9C,wBAAgB,WAAW,IAAI,WAAW,EAAE,CAU3C;AAID,wBAAgB,WAAW,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,IAAI,CAKxD;AAID,wBAAgB,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,WAAW,EAAE,CAW1D;AAID,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAMzF"}
@@ -0,0 +1,53 @@
1
+ import fs from 'node:fs';
2
+ import { ARCHER_DIR, STATE_FILE } from './types.js';
3
+ // ─── Ensure Directory ───────────────────────────────────────
4
+ function ensureDir() {
5
+ if (!fs.existsSync(ARCHER_DIR)) {
6
+ fs.mkdirSync(ARCHER_DIR, { recursive: true });
7
+ }
8
+ }
9
+ // ─── Load Watches ───────────────────────────────────────────
10
+ export function loadWatches() {
11
+ try {
12
+ if (!fs.existsSync(STATE_FILE))
13
+ return [];
14
+ const raw = fs.readFileSync(STATE_FILE, 'utf-8');
15
+ const parsed = JSON.parse(raw);
16
+ if (!Array.isArray(parsed))
17
+ return [];
18
+ return parsed;
19
+ }
20
+ catch {
21
+ return [];
22
+ }
23
+ }
24
+ // ─── Save Watches (atomic write) ────────────────────────────
25
+ export function saveWatches(watches) {
26
+ ensureDir();
27
+ const tmp = STATE_FILE + '.tmp';
28
+ fs.writeFileSync(tmp, JSON.stringify(watches, null, 2) + '\n', 'utf-8');
29
+ fs.renameSync(tmp, STATE_FILE);
30
+ }
31
+ // ─── Add Watch ──────────────────────────────────────────────
32
+ export function addWatch(watch) {
33
+ const watches = loadWatches();
34
+ // Replace if same ID exists
35
+ const idx = watches.findIndex((w) => w.id === watch.id);
36
+ if (idx >= 0) {
37
+ watches[idx] = watch;
38
+ }
39
+ else {
40
+ watches.push(watch);
41
+ }
42
+ saveWatches(watches);
43
+ return watches;
44
+ }
45
+ // ─── Remove Watch ───────────────────────────────────────────
46
+ export function removeWatch(watchId) {
47
+ const watches = loadWatches();
48
+ const before = watches.length;
49
+ const filtered = watches.filter((w) => w.id !== watchId);
50
+ saveWatches(filtered);
51
+ return { watches: filtered, removed: filtered.length < before };
52
+ }
53
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/daemon/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAGpD,+DAA+D;AAE/D,SAAS,SAAS;IAChB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC;AACH,CAAC;AAED,+DAA+D;AAE/D,MAAM,UAAU,WAAW;IACzB,IAAI,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC;YAAE,OAAO,EAAE,CAAC;QAC1C,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACjD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,OAAO,EAAE,CAAC;QACtC,OAAO,MAAuB,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,+DAA+D;AAE/D,MAAM,UAAU,WAAW,CAAC,OAAsB;IAChD,SAAS,EAAE,CAAC;IACZ,MAAM,GAAG,GAAG,UAAU,GAAG,MAAM,CAAC;IAChC,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;IACxE,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;AACjC,CAAC;AAED,+DAA+D;AAE/D,MAAM,UAAU,QAAQ,CAAC,KAAkB;IACzC,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;IAC9B,4BAA4B;IAC5B,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,CAAC,CAAC;IACxD,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC;QACb,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACvB,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;IACD,WAAW,CAAC,OAAO,CAAC,CAAC;IACrB,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,+DAA+D;AAE/D,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;IAC9B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAC9B,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,CAAC;IACzD,WAAW,CAAC,QAAQ,CAAC,CAAC;IACtB,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,MAAM,GAAG,MAAM,EAAE,CAAC;AAClE,CAAC"}
@@ -0,0 +1,47 @@
1
+ export declare const DAEMON_PORT = 7481;
2
+ export declare const DAEMON_HOST = "127.0.0.1";
3
+ export declare const ARCHER_DIR: string;
4
+ export declare const STATE_FILE: string;
5
+ export declare const PID_FILE: string;
6
+ export declare const LOG_FILE: string;
7
+ export interface WatchConfig {
8
+ id: string;
9
+ table: string;
10
+ event: 'INSERT' | 'UPDATE' | 'DELETE' | '*';
11
+ filter?: string;
12
+ webhookUrl?: string;
13
+ createdAt: string;
14
+ supabaseUrl: string;
15
+ supabaseKey: string;
16
+ }
17
+ export type IpcRequest = {
18
+ type: 'add_watch';
19
+ watch: WatchConfig;
20
+ } | {
21
+ type: 'remove_watch';
22
+ watchId: string;
23
+ } | {
24
+ type: 'list_watches';
25
+ } | {
26
+ type: 'ping';
27
+ };
28
+ export type IpcResponse = {
29
+ ok: true;
30
+ type: 'watch_added';
31
+ watchId: string;
32
+ } | {
33
+ ok: true;
34
+ type: 'watch_removed';
35
+ watchId: string;
36
+ } | {
37
+ ok: true;
38
+ type: 'watch_list';
39
+ watches: WatchConfig[];
40
+ } | {
41
+ ok: true;
42
+ type: 'pong';
43
+ } | {
44
+ ok: false;
45
+ error: string;
46
+ };
47
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/daemon/types.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,WAAW,OAAO,CAAC;AAChC,eAAO,MAAM,WAAW,cAAc,CAAC;AACvC,eAAO,MAAM,UAAU,QAAqC,CAAC;AAC7D,eAAO,MAAM,UAAU,QAAsC,CAAC;AAC9D,eAAO,MAAM,QAAQ,QAAsC,CAAC;AAC5D,eAAO,MAAM,QAAQ,QAAsC,CAAC;AAI5D,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,GAAG,CAAC;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAMD,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErB,MAAM,MAAM,WAAW,GACnB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACpD;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,WAAW,EAAE,CAAA;CAAE,GACxD;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC1B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC"}
@@ -0,0 +1,10 @@
1
+ // ─── Daemon Configuration ───────────────────────────────────
2
+ import path from 'node:path';
3
+ import os from 'node:os';
4
+ export const DAEMON_PORT = 7481;
5
+ export const DAEMON_HOST = '127.0.0.1';
6
+ export const ARCHER_DIR = path.join(os.homedir(), '.archer');
7
+ export const STATE_FILE = path.join(ARCHER_DIR, 'state.json');
8
+ export const PID_FILE = path.join(ARCHER_DIR, 'daemon.pid');
9
+ export const LOG_FILE = path.join(ARCHER_DIR, 'daemon.log');
10
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/daemon/types.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAE/D,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC;AAChC,MAAM,CAAC,MAAM,WAAW,GAAG,WAAW,CAAC;AACvC,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;AAC7D,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;AAC9D,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;AAC5D,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC"}