archer-wizard 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/daemon/client.d.ts +4 -0
- package/dist/daemon/client.d.ts.map +1 -0
- package/dist/daemon/client.js +44 -0
- package/dist/daemon/client.js.map +1 -0
- package/dist/daemon/lifecycle.d.ts +9 -0
- package/dist/daemon/lifecycle.d.ts.map +1 -0
- package/dist/daemon/lifecycle.js +118 -0
- package/dist/daemon/lifecycle.js.map +1 -0
- package/dist/daemon/process.d.ts +2 -0
- package/dist/daemon/process.d.ts.map +1 -0
- package/dist/daemon/process.js +171 -0
- package/dist/daemon/process.js.map +1 -0
- package/dist/daemon/run.d.ts +2 -0
- package/dist/daemon/run.d.ts.map +1 -0
- package/dist/daemon/run.js +5 -0
- package/dist/daemon/run.js.map +1 -0
- package/dist/daemon/store.d.ts +9 -0
- package/dist/daemon/store.d.ts.map +1 -0
- package/dist/daemon/store.js +53 -0
- package/dist/daemon/store.js.map +1 -0
- package/dist/daemon/types.d.ts +47 -0
- package/dist/daemon/types.d.ts.map +1 -0
- package/dist/daemon/types.js +10 -0
- package/dist/daemon/types.js.map +1 -0
- package/dist/index.js +105 -21
- package/dist/index.js.map +1 -1
- package/dist/lib/ascii.d.ts.map +1 -1
- package/dist/lib/ascii.js +9 -41
- package/dist/lib/ascii.js.map +1 -1
- package/dist/tools/unwatch.d.ts +7 -0
- package/dist/tools/unwatch.d.ts.map +1 -0
- package/dist/tools/unwatch.js +20 -0
- package/dist/tools/unwatch.js.map +1 -0
- package/dist/tools/watch.d.ts +14 -34
- package/dist/tools/watch.d.ts.map +1 -1
- package/dist/tools/watch.js +36 -170
- package/dist/tools/watch.js.map +1 -1
- package/dist/tools/watches.d.ts +2 -0
- package/dist/tools/watches.d.ts.map +1 -0
- package/dist/tools/watches.js +33 -0
- package/dist/tools/watches.js.map +1 -0
- package/dist/wizard/index.d.ts.map +1 -1
- package/dist/wizard/index.js +57 -23
- package/dist/wizard/index.js.map +1 -1
- package/dist/wizard/scanner.d.ts.map +1 -1
- package/dist/wizard/scanner.js +0 -23
- package/dist/wizard/scanner.js.map +1 -1
- package/package.json +2 -1
- package/src/daemon/client.ts +50 -0
- package/src/daemon/lifecycle.ts +126 -0
- package/src/daemon/process.ts +207 -0
- package/src/daemon/run.ts +6 -0
- package/src/daemon/store.ts +60 -0
- package/src/daemon/types.ts +41 -0
- package/src/index.ts +111 -22
- package/src/lib/ascii.ts +9 -44
- package/src/tools/unwatch.ts +26 -0
- package/src/tools/watch.ts +46 -212
- package/src/tools/watches.ts +37 -0
- package/src/wizard/index.ts +54 -24
- package/src/wizard/scanner.ts +0 -23
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { sendCommand } from '../daemon/client.js';
|
|
2
|
+
// ─── Execute List Watches (via daemon IPC) ──────────────────
|
|
3
|
+
export async function executeListWatches() {
|
|
4
|
+
try {
|
|
5
|
+
const res = await sendCommand({ type: 'list_watches' });
|
|
6
|
+
if (!res.ok) {
|
|
7
|
+
return `❌ Failed to list watches: ${res.error}`;
|
|
8
|
+
}
|
|
9
|
+
if (res.type !== 'watch_list') {
|
|
10
|
+
return `❌ Unexpected response type: ${res.type}`;
|
|
11
|
+
}
|
|
12
|
+
if (res.watches.length === 0) {
|
|
13
|
+
return '📭 No active watches.';
|
|
14
|
+
}
|
|
15
|
+
const lines = [`📋 Active watches (${res.watches.length}):\n`];
|
|
16
|
+
for (const w of res.watches) {
|
|
17
|
+
lines.push(` 🔹 ${w.id}`);
|
|
18
|
+
lines.push(` table: ${w.table}`);
|
|
19
|
+
lines.push(` event: ${w.event}`);
|
|
20
|
+
if (w.filter)
|
|
21
|
+
lines.push(` filter: ${w.filter}`);
|
|
22
|
+
if (w.webhookUrl)
|
|
23
|
+
lines.push(` webhook: ${w.webhookUrl}`);
|
|
24
|
+
lines.push(` created: ${w.createdAt}`);
|
|
25
|
+
lines.push('');
|
|
26
|
+
}
|
|
27
|
+
return lines.join('\n');
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
return `❌ Daemon error: ${err instanceof Error ? err.message : String(err)}\n\nIs the archer daemon running?`;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=watches.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watches.js","sourceRoot":"","sources":["../../src/tools/watches.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAElD,+DAA+D;AAE/D,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;QAExD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,OAAO,6BAA6B,GAAG,CAAC,KAAK,EAAE,CAAC;QAClD,CAAC;QAED,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YAC9B,OAAO,+BAA+B,GAAG,CAAC,IAAI,EAAE,CAAC;QACnD,CAAC;QAED,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,OAAO,uBAAuB,CAAC;QACjC,CAAC;QAED,MAAM,KAAK,GAAG,CAAC,sBAAsB,GAAG,CAAC,OAAO,CAAC,MAAM,MAAM,CAAC,CAAC;QAE/D,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;YAC5B,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;YACrC,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;YACrC,IAAI,CAAC,CAAC,MAAM;gBAAE,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;YACrD,IAAI,CAAC,CAAC,UAAU;gBAAE,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;YAC9D,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;YAC3C,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjB,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,mBAAmB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,mCAAmC,CAAC;IAChH,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/wizard/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/wizard/index.ts"],"names":[],"mappings":"AAcA,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAoF/C"}
|
package/dist/wizard/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import * as clack from '@clack/prompts';
|
|
2
2
|
import pc from 'picocolors';
|
|
3
|
-
import { showAsciiArt,
|
|
3
|
+
import { showAsciiArt, showSuccessBox } from '../lib/ascii.js';
|
|
4
4
|
import { scanProject, promptForMissing } from './scanner.js';
|
|
5
5
|
import { detectAgents } from './detector.js';
|
|
6
6
|
import { injectIntoAgents } from './injector.js';
|
|
7
7
|
import { injectRules } from './rules.js';
|
|
8
|
+
// ─── Helpers ─────────────────────────────────────────────────
|
|
9
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
8
10
|
// ─── Wizard Sequence ────────────────────────────────────────
|
|
9
11
|
export async function runWizard() {
|
|
10
12
|
const cwd = process.cwd();
|
|
@@ -12,38 +14,70 @@ export async function runWizard() {
|
|
|
12
14
|
showAsciiArt();
|
|
13
15
|
// ─ Step 2: Start clack ─
|
|
14
16
|
clack.intro(pc.bgGreen(pc.black(' archer setup ')));
|
|
15
|
-
// ─ Step 3:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
// ─ Step 3: Scan for Supabase credentials ─
|
|
18
|
+
const s = clack.spinner();
|
|
19
|
+
s.start('scanning project for supabase credentials');
|
|
20
|
+
await sleep(600);
|
|
19
21
|
const scan = await scanProject(cwd);
|
|
20
|
-
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
await sleep(300);
|
|
23
|
+
// Build scan summary lines
|
|
24
|
+
const scanNotes = [];
|
|
25
|
+
if (scan.supabaseUrl)
|
|
26
|
+
scanNotes.push('SUPABASE_URL');
|
|
27
|
+
if (scan.serviceRoleKey)
|
|
28
|
+
scanNotes.push('SERVICE_ROLE_KEY');
|
|
29
|
+
if (scan.anonKey)
|
|
30
|
+
scanNotes.push('ANON_KEY');
|
|
31
|
+
if (scan.foundInFile)
|
|
32
|
+
scanNotes.push(`from ${scan.foundInFile}`);
|
|
33
|
+
if (scanNotes.length > 0) {
|
|
34
|
+
s.stop(`found ${pc.green(scanNotes.join(pc.dim(' · ')))}`);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
s.stop(pc.yellow('no credentials found — will prompt'));
|
|
24
38
|
}
|
|
25
|
-
|
|
26
|
-
|
|
39
|
+
// ─ Step 4: Framework detection ─
|
|
40
|
+
if (scan.framework !== 'unknown' || scan.hasSupabaseInstalled) {
|
|
41
|
+
const s2 = clack.spinner();
|
|
42
|
+
s2.start('detecting project framework');
|
|
43
|
+
await sleep(400);
|
|
44
|
+
const parts = [];
|
|
45
|
+
if (scan.framework !== 'unknown')
|
|
46
|
+
parts.push(pc.bold(scan.framework));
|
|
47
|
+
if (scan.hasSupabaseInstalled)
|
|
48
|
+
parts.push(pc.dim('@supabase/supabase-js'));
|
|
49
|
+
s2.stop(`detected ${parts.join(pc.dim(' + '))}`);
|
|
27
50
|
}
|
|
28
|
-
// ─ Step
|
|
51
|
+
// ─ Step 5: Prompt for missing credentials ─
|
|
29
52
|
const credentials = await promptForMissing(scan);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
53
|
+
// ─ Step 6: Detect agents ─
|
|
54
|
+
const s3 = clack.spinner();
|
|
55
|
+
s3.start('scanning for AI agents');
|
|
56
|
+
await sleep(500);
|
|
33
57
|
const agents = detectAgents();
|
|
34
|
-
|
|
35
|
-
|
|
58
|
+
await sleep(200);
|
|
59
|
+
if (agents.length > 0) {
|
|
60
|
+
const names = agents.map((a) => pc.bold(a.name)).join(pc.dim(', '));
|
|
61
|
+
s3.stop(`found ${pc.green(String(agents.length))} agent${agents.length !== 1 ? 's' : ''} — ${names}`);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
s3.stop(pc.yellow('no agents found'));
|
|
65
|
+
}
|
|
66
|
+
// ─ Step 7: Inject into agents ─
|
|
36
67
|
const injectionResults = await injectIntoAgents(agents, credentials.supabaseUrl, credentials.serviceRoleKey);
|
|
37
|
-
|
|
38
|
-
// ─ Step 12: Filter successful injections ─
|
|
68
|
+
// ─ Step 8: Filter successful injections ─
|
|
39
69
|
const successfulAgents = agents.filter((a) => injectionResults.some((r) => r.agent === a.name && r.success));
|
|
40
|
-
// ─ Step
|
|
70
|
+
// ─ Step 9: Inject rules ─
|
|
41
71
|
if (successfulAgents.length > 0) {
|
|
42
|
-
|
|
72
|
+
const s4 = clack.spinner();
|
|
73
|
+
s4.start('writing agent rules');
|
|
74
|
+
await sleep(400);
|
|
43
75
|
injectRules(successfulAgents);
|
|
44
|
-
|
|
76
|
+
await sleep(200);
|
|
77
|
+
s4.stop(`rules injected into ${pc.green(String(successfulAgents.length))} agent${successfulAgents.length !== 1 ? 's' : ''}`);
|
|
45
78
|
}
|
|
46
|
-
// ─ Step
|
|
79
|
+
// ─ Step 10: Success ─
|
|
80
|
+
console.log();
|
|
47
81
|
showSuccessBox(successfulAgents.length);
|
|
48
82
|
clack.outro(pc.dim('docs → github.com/archer-mcp'));
|
|
49
83
|
}
|
package/dist/wizard/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/wizard/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/wizard/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/wizard/scanner.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/wizard/scanner.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAa,MAAM,mBAAmB,CAAC;AAgL/D,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAkElE;AAID,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,UAAU,GACf,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAAC,CAgD1D"}
|
package/dist/wizard/scanner.js
CHANGED
|
@@ -2,7 +2,6 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import * as clack from '@clack/prompts';
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
-
import { logSuccess, logError, maskCredential } from '../lib/ascii.js';
|
|
6
5
|
// Helper to search for keys in files
|
|
7
6
|
function searchInFiles(cwd, patterns) {
|
|
8
7
|
const results = new Map();
|
|
@@ -195,28 +194,6 @@ export async function scanProject(cwd) {
|
|
|
195
194
|
const serviceRoleKey = findByAliases(mergedEnv, SERVICE_ROLE_KEY_ALIASES);
|
|
196
195
|
const anonKey = findByAliases(mergedEnv, ANON_KEY_ALIASES);
|
|
197
196
|
const { framework, hasSupabaseInstalled } = detectFramework(cwd);
|
|
198
|
-
// Log found credentials
|
|
199
|
-
if (supabaseUrl) {
|
|
200
|
-
logSuccess(`SUPABASE_URL = ${maskCredential(supabaseUrl)}`);
|
|
201
|
-
}
|
|
202
|
-
else {
|
|
203
|
-
logError('missing SUPABASE_URL');
|
|
204
|
-
}
|
|
205
|
-
if (serviceRoleKey) {
|
|
206
|
-
logSuccess(`SUPABASE_SERVICE_ROLE_KEY = ${maskCredential(serviceRoleKey)}`);
|
|
207
|
-
}
|
|
208
|
-
else {
|
|
209
|
-
logError('missing SUPABASE_SERVICE_ROLE_KEY');
|
|
210
|
-
}
|
|
211
|
-
if (anonKey) {
|
|
212
|
-
logSuccess(`SUPABASE_ANON_KEY = ${maskCredential(anonKey)}`);
|
|
213
|
-
}
|
|
214
|
-
if (foundInFile) {
|
|
215
|
-
logSuccess(`found credentials in ${foundInFile}`);
|
|
216
|
-
}
|
|
217
|
-
else if (codebaseResults.size > 0) {
|
|
218
|
-
logSuccess(`found credentials in codebase`);
|
|
219
|
-
}
|
|
220
197
|
return {
|
|
221
198
|
supabaseUrl,
|
|
222
199
|
serviceRoleKey,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../../src/wizard/scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../../src/wizard/scanner.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "archer-wizard",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "event intelligence layer for AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"build": "tsc",
|
|
12
12
|
"dev": "tsx src/index.ts",
|
|
13
13
|
"dev:mcp": "tsx src/index.ts --mcp",
|
|
14
|
+
"dev:daemon": "tsx src/index.ts --daemon",
|
|
14
15
|
"prepublishOnly": "npm run build"
|
|
15
16
|
},
|
|
16
17
|
"dependencies": {
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import net from 'node:net';
|
|
2
|
+
import { DAEMON_PORT, DAEMON_HOST } from './types.js';
|
|
3
|
+
import type { IpcRequest, IpcResponse } from './types.js';
|
|
4
|
+
|
|
5
|
+
// ─── Send Command to Daemon ─────────────────────────────────
|
|
6
|
+
|
|
7
|
+
export function sendCommand(req: IpcRequest): Promise<IpcResponse> {
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
const socket = net.createConnection({ host: DAEMON_HOST, port: DAEMON_PORT }, () => {
|
|
10
|
+
socket.write(JSON.stringify(req) + '\n');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
let buffer = '';
|
|
14
|
+
|
|
15
|
+
socket.on('data', (data) => {
|
|
16
|
+
buffer += data.toString();
|
|
17
|
+
const newlineIdx = buffer.indexOf('\n');
|
|
18
|
+
if (newlineIdx !== -1) {
|
|
19
|
+
const line = buffer.slice(0, newlineIdx).trim();
|
|
20
|
+
socket.end();
|
|
21
|
+
try {
|
|
22
|
+
resolve(JSON.parse(line) as IpcResponse);
|
|
23
|
+
} catch (err) {
|
|
24
|
+
reject(new Error(`invalid daemon response: ${line}`));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
socket.on('error', (err) => {
|
|
30
|
+
reject(new Error(`daemon connection failed: ${err.message}`));
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Timeout after 5 seconds
|
|
34
|
+
socket.setTimeout(5_000, () => {
|
|
35
|
+
socket.destroy();
|
|
36
|
+
reject(new Error('daemon response timeout'));
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ─── Check if Daemon is Running ─────────────────────────────
|
|
42
|
+
|
|
43
|
+
export async function isDaemonRunning(): Promise<boolean> {
|
|
44
|
+
try {
|
|
45
|
+
const res = await sendCommand({ type: 'ping' });
|
|
46
|
+
return res.ok && res.type === 'pong';
|
|
47
|
+
} catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
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
|
+
|
|
8
|
+
// ─── Start Daemon ───────────────────────────────────────────
|
|
9
|
+
|
|
10
|
+
export async function startDaemon(): Promise<{ pid: number; alreadyRunning: boolean }> {
|
|
11
|
+
// Check if already running
|
|
12
|
+
if (await isDaemonRunning()) {
|
|
13
|
+
const pid = readPid();
|
|
14
|
+
return { pid: pid ?? 0, alreadyRunning: true };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Ensure directory exists
|
|
18
|
+
if (!fs.existsSync(ARCHER_DIR)) {
|
|
19
|
+
fs.mkdirSync(ARCHER_DIR, { recursive: true });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Resolve the daemon entry script
|
|
23
|
+
// In compiled mode this is dist/daemon/process.js
|
|
24
|
+
// In dev mode with tsx this is src/daemon/process.ts
|
|
25
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
26
|
+
const thisDir = path.dirname(thisFile);
|
|
27
|
+
|
|
28
|
+
// Look for the daemon runner script
|
|
29
|
+
const daemonRunner = path.join(thisDir, 'run.js');
|
|
30
|
+
const daemonRunnerTs = path.join(thisDir, 'run.ts');
|
|
31
|
+
|
|
32
|
+
let cmd: string;
|
|
33
|
+
let args: string[];
|
|
34
|
+
|
|
35
|
+
if (fs.existsSync(daemonRunner)) {
|
|
36
|
+
// Compiled mode
|
|
37
|
+
cmd = process.execPath; // node
|
|
38
|
+
args = [daemonRunner];
|
|
39
|
+
} else if (fs.existsSync(daemonRunnerTs)) {
|
|
40
|
+
// Dev mode — use tsx
|
|
41
|
+
cmd = 'npx';
|
|
42
|
+
args = ['tsx', daemonRunnerTs];
|
|
43
|
+
} else {
|
|
44
|
+
// Fallback: assume we're in dist/ and run process.js directly
|
|
45
|
+
const processJs = path.join(thisDir, 'process.js');
|
|
46
|
+
cmd = process.execPath;
|
|
47
|
+
args = [processJs, '--run-daemon'];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Open log file for daemon output
|
|
51
|
+
const logFd = fs.openSync(LOG_FILE, 'a');
|
|
52
|
+
|
|
53
|
+
const child = spawn(cmd, args, {
|
|
54
|
+
detached: true,
|
|
55
|
+
stdio: ['ignore', logFd, logFd],
|
|
56
|
+
env: { ...process.env },
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
child.unref();
|
|
60
|
+
fs.closeSync(logFd);
|
|
61
|
+
|
|
62
|
+
const pid = child.pid ?? 0;
|
|
63
|
+
|
|
64
|
+
// Wait briefly for daemon to start, then verify
|
|
65
|
+
await sleep(500);
|
|
66
|
+
|
|
67
|
+
const running = await isDaemonRunning();
|
|
68
|
+
if (!running) {
|
|
69
|
+
// Give it another moment
|
|
70
|
+
await sleep(1000);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return { pid, alreadyRunning: false };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ─── Stop Daemon ────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
export async function stopDaemon(): Promise<boolean> {
|
|
79
|
+
const pid = readPid();
|
|
80
|
+
if (pid === null) return false;
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
process.kill(pid, 'SIGTERM');
|
|
84
|
+
// Wait for it to die
|
|
85
|
+
await sleep(500);
|
|
86
|
+
return true;
|
|
87
|
+
} catch {
|
|
88
|
+
// Process already dead — clean up PID file
|
|
89
|
+
try { fs.unlinkSync(PID_FILE); } catch {}
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ─── Ensure Daemon is Running ───────────────────────────────
|
|
95
|
+
|
|
96
|
+
export async function ensureDaemon(): Promise<{ pid: number }> {
|
|
97
|
+
const result = await startDaemon();
|
|
98
|
+
return { pid: result.pid };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ─── Helpers ────────────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
function readPid(): number | null {
|
|
104
|
+
try {
|
|
105
|
+
if (!fs.existsSync(PID_FILE)) return null;
|
|
106
|
+
const raw = fs.readFileSync(PID_FILE, 'utf-8').trim();
|
|
107
|
+
const pid = parseInt(raw, 10);
|
|
108
|
+
if (isNaN(pid)) return null;
|
|
109
|
+
|
|
110
|
+
// Check if process is alive
|
|
111
|
+
try {
|
|
112
|
+
process.kill(pid, 0); // Signal 0 = existence check
|
|
113
|
+
return pid;
|
|
114
|
+
} catch {
|
|
115
|
+
// Process is dead — clean up stale PID
|
|
116
|
+
try { fs.unlinkSync(PID_FILE); } catch {}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
} catch {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function sleep(ms: number): Promise<void> {
|
|
125
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
126
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import net from 'node:net';
|
|
2
|
+
import { createClient, type RealtimeChannel, type SupabaseClient } from '@supabase/supabase-js';
|
|
3
|
+
import { DAEMON_PORT, DAEMON_HOST, PID_FILE, ARCHER_DIR, LOG_FILE } from './types.js';
|
|
4
|
+
import type { IpcRequest, IpcResponse, WatchConfig } from './types.js';
|
|
5
|
+
import { loadWatches, addWatch, removeWatch } from './store.js';
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
|
|
8
|
+
// ─── Simple webhook delivery for daemon context ─────────────
|
|
9
|
+
|
|
10
|
+
async function deliverWebhook(url: string, payload: unknown): Promise<void> {
|
|
11
|
+
const res = await fetch(url, {
|
|
12
|
+
method: 'POST',
|
|
13
|
+
headers: {
|
|
14
|
+
'Content-Type': 'application/json',
|
|
15
|
+
'User-Agent': 'Archer/0.2.0',
|
|
16
|
+
},
|
|
17
|
+
body: JSON.stringify(payload),
|
|
18
|
+
});
|
|
19
|
+
if (!res.ok) {
|
|
20
|
+
throw new Error(`webhook ${res.status} ${res.statusText}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ─── Active Channel Registry ────────────────────────────────
|
|
25
|
+
|
|
26
|
+
const channels = new Map<string, { client: SupabaseClient; channel: RealtimeChannel }>();
|
|
27
|
+
|
|
28
|
+
// ─── Subscribe to a Watch ───────────────────────────────────
|
|
29
|
+
|
|
30
|
+
function subscribe(watch: WatchConfig): void {
|
|
31
|
+
// Skip if already subscribed
|
|
32
|
+
if (channels.has(watch.id)) return;
|
|
33
|
+
|
|
34
|
+
const client = createClient(watch.supabaseUrl, watch.supabaseKey);
|
|
35
|
+
|
|
36
|
+
const channelName = `daemon-${watch.table}-${watch.id}`;
|
|
37
|
+
const channel = client
|
|
38
|
+
.channel(channelName)
|
|
39
|
+
.on(
|
|
40
|
+
'postgres_changes',
|
|
41
|
+
{
|
|
42
|
+
event: watch.event,
|
|
43
|
+
schema: 'public',
|
|
44
|
+
table: watch.table,
|
|
45
|
+
...(watch.filter ? { filter: watch.filter } : {}),
|
|
46
|
+
},
|
|
47
|
+
async (payload) => {
|
|
48
|
+
log(`[event] ${watch.table} ${payload.eventType} → watch ${watch.id}`);
|
|
49
|
+
|
|
50
|
+
if (watch.webhookUrl) {
|
|
51
|
+
try {
|
|
52
|
+
await deliverWebhook(watch.webhookUrl, payload);
|
|
53
|
+
log(`[webhook] delivered to ${watch.webhookUrl}`);
|
|
54
|
+
} catch (err) {
|
|
55
|
+
log(`[webhook] failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
)
|
|
60
|
+
.subscribe((status) => {
|
|
61
|
+
log(`[channel] ${channelName} → ${status}`);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
channels.set(watch.id, { client, channel });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ─── Unsubscribe from a Watch ───────────────────────────────
|
|
68
|
+
|
|
69
|
+
async function unsubscribe(watchId: string): Promise<void> {
|
|
70
|
+
const entry = channels.get(watchId);
|
|
71
|
+
if (!entry) return;
|
|
72
|
+
|
|
73
|
+
await entry.client.removeChannel(entry.channel);
|
|
74
|
+
channels.delete(watchId);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ─── Handle IPC Command ─────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
async function handleCommand(req: IpcRequest): Promise<IpcResponse> {
|
|
80
|
+
switch (req.type) {
|
|
81
|
+
case 'add_watch': {
|
|
82
|
+
const watches = addWatch(req.watch);
|
|
83
|
+
subscribe(req.watch);
|
|
84
|
+
return { ok: true, type: 'watch_added', watchId: req.watch.id };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
case 'remove_watch': {
|
|
88
|
+
const { watches, removed } = removeWatch(req.watchId);
|
|
89
|
+
if (!removed) {
|
|
90
|
+
return { ok: false, error: `watch ${req.watchId} not found` };
|
|
91
|
+
}
|
|
92
|
+
await unsubscribe(req.watchId);
|
|
93
|
+
return { ok: true, type: 'watch_removed', watchId: req.watchId };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
case 'list_watches': {
|
|
97
|
+
const watches = loadWatches();
|
|
98
|
+
return { ok: true, type: 'watch_list', watches };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
case 'ping': {
|
|
102
|
+
return { ok: true, type: 'pong' };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
default:
|
|
106
|
+
return { ok: false, error: 'unknown command' };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ─── Logging ────────────────────────────────────────────────
|
|
111
|
+
|
|
112
|
+
function log(message: string): void {
|
|
113
|
+
const ts = new Date().toISOString();
|
|
114
|
+
const line = `[${ts}] ${message}\n`;
|
|
115
|
+
try {
|
|
116
|
+
fs.appendFileSync(LOG_FILE, line);
|
|
117
|
+
} catch {
|
|
118
|
+
// If we can't write to log, just continue
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ─── Main: Start TCP Server ─────────────────────────────────
|
|
123
|
+
|
|
124
|
+
export function startDaemonProcess(): void {
|
|
125
|
+
// Ensure directory exists
|
|
126
|
+
if (!fs.existsSync(ARCHER_DIR)) {
|
|
127
|
+
fs.mkdirSync(ARCHER_DIR, { recursive: true });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Write PID file
|
|
131
|
+
fs.writeFileSync(PID_FILE, String(process.pid), 'utf-8');
|
|
132
|
+
|
|
133
|
+
log('daemon starting');
|
|
134
|
+
|
|
135
|
+
// Re-subscribe all persisted watches
|
|
136
|
+
const watches = loadWatches();
|
|
137
|
+
for (const watch of watches) {
|
|
138
|
+
subscribe(watch);
|
|
139
|
+
log(`[restore] re-subscribed watch ${watch.id} on ${watch.table}`);
|
|
140
|
+
}
|
|
141
|
+
log(`restored ${watches.length} watch(es)`);
|
|
142
|
+
|
|
143
|
+
// Create TCP server
|
|
144
|
+
const server = net.createServer((socket) => {
|
|
145
|
+
let buffer = '';
|
|
146
|
+
|
|
147
|
+
socket.on('data', async (data) => {
|
|
148
|
+
buffer += data.toString();
|
|
149
|
+
|
|
150
|
+
// Process complete JSON lines
|
|
151
|
+
let newlineIdx: number;
|
|
152
|
+
while ((newlineIdx = buffer.indexOf('\n')) !== -1) {
|
|
153
|
+
const line = buffer.slice(0, newlineIdx).trim();
|
|
154
|
+
buffer = buffer.slice(newlineIdx + 1);
|
|
155
|
+
|
|
156
|
+
if (!line) continue;
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
const req = JSON.parse(line) as IpcRequest;
|
|
160
|
+
const res = await handleCommand(req);
|
|
161
|
+
socket.write(JSON.stringify(res) + '\n');
|
|
162
|
+
} catch (err) {
|
|
163
|
+
const errRes: IpcResponse = {
|
|
164
|
+
ok: false,
|
|
165
|
+
error: `parse error: ${err instanceof Error ? err.message : String(err)}`,
|
|
166
|
+
};
|
|
167
|
+
socket.write(JSON.stringify(errRes) + '\n');
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
socket.on('error', (err) => {
|
|
173
|
+
log(`[socket] error: ${err.message}`);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
server.listen(DAEMON_PORT, DAEMON_HOST, () => {
|
|
178
|
+
log(`daemon listening on ${DAEMON_HOST}:${DAEMON_PORT} (pid ${process.pid})`);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
server.on('error', (err) => {
|
|
182
|
+
log(`[server] error: ${err.message}`);
|
|
183
|
+
process.exit(1);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Clean shutdown
|
|
187
|
+
const shutdown = () => {
|
|
188
|
+
log('daemon shutting down');
|
|
189
|
+
server.close();
|
|
190
|
+
|
|
191
|
+
// Unsubscribe all channels
|
|
192
|
+
for (const [id, entry] of channels) {
|
|
193
|
+
entry.client.removeChannel(entry.channel).catch(() => {});
|
|
194
|
+
}
|
|
195
|
+
channels.clear();
|
|
196
|
+
|
|
197
|
+
// Remove PID file
|
|
198
|
+
try {
|
|
199
|
+
fs.unlinkSync(PID_FILE);
|
|
200
|
+
} catch {}
|
|
201
|
+
|
|
202
|
+
process.exit(0);
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
process.on('SIGTERM', shutdown);
|
|
206
|
+
process.on('SIGINT', shutdown);
|
|
207
|
+
}
|