claude-synapse 1.0.1 → 1.0.2
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/bin/synapse.mjs +116 -40
- package/package.json +1 -1
package/bin/synapse.mjs
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { execSync, spawn } from 'node:child_process';
|
|
4
|
-
import { existsSync } from 'node:fs';
|
|
4
|
+
import { existsSync, writeFileSync, readFileSync, unlinkSync } from 'node:fs';
|
|
5
5
|
import { resolve, dirname } from 'node:path';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { tmpdir } from 'node:os';
|
|
7
8
|
|
|
8
9
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
10
|
const ROOT = resolve(__dirname, '..');
|
|
10
11
|
const PORT = process.env.PORT || 4800;
|
|
12
|
+
const PID_FILE = resolve(tmpdir(), `claude-synapse-${PORT}.pid`);
|
|
11
13
|
|
|
12
14
|
// Detect whether we're running from the npm-published bundle or from source
|
|
13
15
|
const bundledCollector = resolve(ROOT, 'dist', 'collector.mjs');
|
|
@@ -24,6 +26,16 @@ if (command === 'init') {
|
|
|
24
26
|
process.exit(0);
|
|
25
27
|
}
|
|
26
28
|
|
|
29
|
+
if (command === 'stop') {
|
|
30
|
+
stop();
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (command === 'status') {
|
|
35
|
+
status();
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
38
|
+
|
|
27
39
|
function isDevBuilt() {
|
|
28
40
|
return (
|
|
29
41
|
existsSync(resolve(ROOT, 'packages/protocol/dist/index.js')) &&
|
|
@@ -38,26 +50,68 @@ function build() {
|
|
|
38
50
|
console.log('[synapse] Build complete.');
|
|
39
51
|
}
|
|
40
52
|
|
|
41
|
-
function
|
|
42
|
-
let entryPoint;
|
|
43
|
-
let dashboardDir;
|
|
44
|
-
|
|
53
|
+
function getEntryAndDashboard() {
|
|
45
54
|
if (isBundled) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
55
|
+
return { entryPoint: bundledCollector, dashboardDir: bundledDashboard };
|
|
56
|
+
}
|
|
57
|
+
if (!isDevBuilt()) build();
|
|
58
|
+
return {
|
|
59
|
+
entryPoint: resolve(ROOT, 'apps/collector/dist/index.js'),
|
|
60
|
+
dashboardDir: resolve(ROOT, 'apps/dashboard/dist'),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function isRunning() {
|
|
65
|
+
if (!existsSync(PID_FILE)) return false;
|
|
66
|
+
const pid = parseInt(readFileSync(PID_FILE, 'utf-8').trim(), 10);
|
|
67
|
+
try {
|
|
68
|
+
process.kill(pid, 0); // signal 0 = check if alive
|
|
69
|
+
return pid;
|
|
70
|
+
} catch {
|
|
71
|
+
// Stale pid file
|
|
72
|
+
unlinkSync(PID_FILE);
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function stop() {
|
|
78
|
+
const pid = isRunning();
|
|
79
|
+
if (!pid) {
|
|
80
|
+
console.log(`[synapse] Not running on port ${PORT}.`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
process.kill(pid, 'SIGTERM');
|
|
85
|
+
unlinkSync(PID_FILE);
|
|
86
|
+
console.log(`[synapse] Stopped (pid ${pid}).`);
|
|
87
|
+
} catch (err) {
|
|
88
|
+
console.error(`[synapse] Failed to stop pid ${pid}:`, err.message);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function status() {
|
|
93
|
+
const pid = isRunning();
|
|
94
|
+
if (pid) {
|
|
95
|
+
console.log(`[synapse] Running on http://localhost:${PORT} (pid ${pid})`);
|
|
49
96
|
} else {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
97
|
+
console.log(`[synapse] Not running.`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function start() {
|
|
102
|
+
const runningPid = isRunning();
|
|
103
|
+
if (runningPid) {
|
|
104
|
+
console.log(`[synapse] Already running on http://localhost:${PORT} (pid ${runningPid})`);
|
|
105
|
+
process.exit(0);
|
|
54
106
|
}
|
|
55
107
|
|
|
56
|
-
|
|
108
|
+
const { entryPoint, dashboardDir } = getEntryAndDashboard();
|
|
109
|
+
const isDaemon = args.includes('--daemon') || args.includes('-d');
|
|
57
110
|
|
|
58
111
|
const collector = spawn('node', [entryPoint], {
|
|
59
112
|
cwd: ROOT,
|
|
60
|
-
stdio: 'inherit',
|
|
113
|
+
stdio: isDaemon ? 'ignore' : 'inherit',
|
|
114
|
+
detached: isDaemon,
|
|
61
115
|
env: {
|
|
62
116
|
...process.env,
|
|
63
117
|
PORT: String(PORT),
|
|
@@ -65,32 +119,54 @@ function start() {
|
|
|
65
119
|
},
|
|
66
120
|
});
|
|
67
121
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
122
|
+
if (isDaemon) {
|
|
123
|
+
// Write PID file and detach
|
|
124
|
+
writeFileSync(PID_FILE, String(collector.pid));
|
|
125
|
+
collector.unref();
|
|
126
|
+
console.log(`[synapse] Started in background on http://localhost:${PORT} (pid ${collector.pid})`);
|
|
127
|
+
console.log(`[synapse] Stop with: npx claude-synapse stop`);
|
|
128
|
+
|
|
129
|
+
// Open browser
|
|
130
|
+
setTimeout(() => {
|
|
131
|
+
const url = `http://localhost:${PORT}`;
|
|
132
|
+
try {
|
|
133
|
+
if (process.platform === 'darwin') execSync(`open ${url}`);
|
|
134
|
+
else if (process.platform === 'linux') execSync(`xdg-open ${url}`);
|
|
135
|
+
else if (process.platform === 'win32') execSync(`start ${url}`);
|
|
136
|
+
} catch {}
|
|
137
|
+
process.exit(0);
|
|
138
|
+
}, 1500);
|
|
139
|
+
} else {
|
|
140
|
+
// Foreground mode
|
|
141
|
+
console.log(`[synapse] Starting on http://localhost:${PORT}`);
|
|
142
|
+
writeFileSync(PID_FILE, String(collector.pid));
|
|
143
|
+
|
|
144
|
+
collector.on('close', (code) => {
|
|
145
|
+
try { unlinkSync(PID_FILE); } catch {}
|
|
146
|
+
process.exit(code ?? 0);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
setTimeout(() => {
|
|
150
|
+
const url = `http://localhost:${PORT}`;
|
|
151
|
+
try {
|
|
152
|
+
if (process.platform === 'darwin') execSync(`open ${url}`);
|
|
153
|
+
else if (process.platform === 'linux') execSync(`xdg-open ${url}`);
|
|
154
|
+
else if (process.platform === 'win32') execSync(`start ${url}`);
|
|
155
|
+
} catch {}
|
|
156
|
+
}, 1500);
|
|
157
|
+
|
|
158
|
+
process.on('SIGINT', () => {
|
|
159
|
+
collector.kill('SIGINT');
|
|
160
|
+
try { unlinkSync(PID_FILE); } catch {}
|
|
161
|
+
process.exit(0);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
process.on('SIGTERM', () => {
|
|
165
|
+
collector.kill('SIGTERM');
|
|
166
|
+
try { unlinkSync(PID_FILE); } catch {}
|
|
167
|
+
process.exit(0);
|
|
168
|
+
});
|
|
169
|
+
}
|
|
94
170
|
}
|
|
95
171
|
|
|
96
172
|
start();
|