gm-skill 0.1.2 → 2.0.1081
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/AGENTS.md +1 -0
- package/LICENSE +21 -0
- package/README.md +20 -84
- package/agents/gm.md +22 -0
- package/agents/memorize.md +100 -0
- package/agents/research-worker.md +36 -0
- package/agents/textprocessing.md +47 -0
- package/bin/bootstrap.js +702 -0
- package/bin/plugkit.js +136 -0
- package/bin/plugkit.sha256 +7 -0
- package/bin/plugkit.version +1 -0
- package/bin/plugkit.wasm +0 -0
- package/bin/plugkit.wasm.sha256 +1 -0
- package/bin/rtk.sha256 +6 -0
- package/bin/rtk.version +1 -0
- package/gm-plugkit/bootstrap.js +694 -0
- package/gm-plugkit/cli.js +48 -0
- package/gm-plugkit/index.js +12 -0
- package/gm-plugkit/package.json +26 -0
- package/gm-plugkit/plugkit-wasm-wrapper.js +190 -0
- package/gm-plugkit/plugkit.sha256 +6 -0
- package/gm-plugkit/plugkit.version +1 -0
- package/gm.json +27 -0
- package/lang/browser.js +45 -0
- package/lang/ssh.js +166 -0
- package/lib/browser-spool-handler.js +130 -0
- package/lib/browser.js +131 -0
- package/lib/codeinsight.js +109 -0
- package/lib/daemon-bootstrap.js +253 -132
- package/lib/git.js +0 -1
- package/lib/learning.js +169 -0
- package/lib/skill-bootstrap.js +406 -0
- package/lib/spool-dispatch.js +100 -0
- package/lib/spool.js +87 -49
- package/lib/wasm-host.js +241 -0
- package/package.json +38 -20
- package/prompts/bash-deny.txt +22 -0
- package/prompts/pre-compact.txt +21 -0
- package/prompts/prompt-submit.txt +83 -0
- package/prompts/session-start.txt +15 -0
- package/scripts/run-hook.sh +7 -0
- package/scripts/watch-cascade.js +166 -0
- package/skills/browser/SKILL.md +80 -0
- package/skills/code-search/SKILL.md +48 -0
- package/skills/create-lang-plugin/SKILL.md +121 -0
- package/skills/gm/SKILL.md +10 -49
- package/skills/gm-complete/SKILL.md +16 -87
- package/skills/gm-emit/SKILL.md +17 -50
- package/skills/gm-execute/SKILL.md +18 -69
- package/skills/gm-skill/SKILL.md +43 -0
- package/skills/gm-skill/index.js +21 -0
- package/skills/governance/SKILL.md +97 -0
- package/skills/pages/SKILL.md +208 -0
- package/skills/planning/SKILL.md +21 -97
- package/skills/research/SKILL.md +43 -0
- package/skills/ssh/SKILL.md +71 -0
- package/skills/textprocessing/SKILL.md +40 -0
- package/skills/update-docs/SKILL.md +24 -43
- package/gm-complete.SKILL.md +0 -106
- package/gm-emit.SKILL.md +0 -70
- package/gm-execute.SKILL.md +0 -88
- package/gm.SKILL.md +0 -63
- package/index.js +0 -1
- package/lib/index.js +0 -37
- package/lib/loader.js +0 -66
- package/lib/manifest.js +0 -99
- package/lib/prepare.js +0 -14
- package/planning.SKILL.md +0 -118
- package/skills/gm/index.js +0 -113
- package/skills/gm-complete/index.js +0 -118
- package/skills/gm-complete.SKILL.md +0 -106
- package/skills/gm-emit/index.js +0 -90
- package/skills/gm-emit.SKILL.md +0 -70
- package/skills/gm-execute/index.js +0 -91
- package/skills/gm-execute.SKILL.md +0 -88
- package/skills/gm.SKILL.md +0 -63
- package/skills/planning/index.js +0 -107
- package/skills/planning.SKILL.md +0 -118
- package/skills/update-docs/index.js +0 -108
- package/skills/update-docs.SKILL.md +0 -66
- package/test-build.js +0 -29
- package/test-e2e.js +0 -117
- package/test-unified.js +0 -24
- package/test.js +0 -89
- package/update-docs.SKILL.md +0 -66
package/lib/daemon-bootstrap.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const net = require('net');
|
|
4
3
|
const crypto = require('crypto');
|
|
5
4
|
const { spawn, execSync } = require('child_process');
|
|
6
5
|
const os = require('os');
|
|
6
|
+
const spool = require('./spool.js');
|
|
7
7
|
|
|
8
8
|
const LOG_DIR = path.join(os.homedir(), '.claude', 'gm-log');
|
|
9
9
|
const GM_STATE_DIR = path.join(os.homedir(), '.gm');
|
|
10
10
|
|
|
11
|
-
function
|
|
11
|
+
function emitDaemonEvent(daemon, severity, message, details) {
|
|
12
12
|
try {
|
|
13
13
|
const date = new Date().toISOString().split('T')[0];
|
|
14
14
|
const logDir = path.join(LOG_DIR, date);
|
|
@@ -25,7 +25,7 @@ function emitEvent(daemon, severity, message, details = {}) {
|
|
|
25
25
|
};
|
|
26
26
|
fs.appendFileSync(logFile, JSON.stringify(entry) + '\n');
|
|
27
27
|
} catch (e) {
|
|
28
|
-
console.error(`[daemon-bootstrap] emit
|
|
28
|
+
console.error(`[daemon-bootstrap] Failed to emit event: ${e.message}`);
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -65,92 +65,202 @@ function isDaemonRunning(daemonName) {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
function checkPortReachable(host, port, timeoutMs = 500) {
|
|
68
|
-
return
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
resolve(false);
|
|
73
|
-
}, timeoutMs);
|
|
74
|
-
|
|
75
|
-
socket.connect(port, host, () => {
|
|
76
|
-
clearTimeout(timeoutHandle);
|
|
77
|
-
socket.destroy();
|
|
78
|
-
resolve(true);
|
|
79
|
-
});
|
|
68
|
+
return spool.execSpool('health', 'health', { timeoutMs, sessionId: getSessionId() })
|
|
69
|
+
.then((r) => !!(r && r.ok))
|
|
70
|
+
.catch(() => false);
|
|
71
|
+
}
|
|
80
72
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
73
|
+
function computeIndexDigest(cwd = process.cwd()) {
|
|
74
|
+
try {
|
|
75
|
+
let mtimeSum = 0;
|
|
76
|
+
const walkDir = (dir) => {
|
|
77
|
+
try {
|
|
78
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
79
|
+
for (const entry of entries) {
|
|
80
|
+
if (entry.isDirectory()) {
|
|
81
|
+
if (!entry.name.startsWith('.') && entry.name !== 'node_modules') {
|
|
82
|
+
walkDir(path.join(dir, entry.name));
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
const fullPath = path.join(dir, entry.name);
|
|
86
|
+
const stat = fs.statSync(fullPath);
|
|
87
|
+
mtimeSum += stat.mtimeMs;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
} catch (e) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
walkDir(cwd);
|
|
96
|
+
|
|
97
|
+
let gitHead = '';
|
|
98
|
+
try {
|
|
99
|
+
gitHead = execSync('git rev-parse HEAD', { cwd, encoding: 'utf8' }).trim();
|
|
100
|
+
} catch {
|
|
101
|
+
gitHead = 'unknown';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let dirtyStatus = 'clean';
|
|
105
|
+
try {
|
|
106
|
+
const porcelain = execSync('git status --porcelain', { cwd, encoding: 'utf8' }).trim();
|
|
107
|
+
if (porcelain.length > 0) {
|
|
108
|
+
dirtyStatus = 'dirty';
|
|
109
|
+
}
|
|
110
|
+
} catch {
|
|
111
|
+
dirtyStatus = 'unknown';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const digestInput = `${mtimeSum}:${gitHead}:${dirtyStatus}`;
|
|
115
|
+
const digest = crypto.createHash('sha256').update(digestInput).digest('hex');
|
|
116
|
+
return `v1:${digest}:files=${mtimeSum}`;
|
|
117
|
+
} catch (e) {
|
|
118
|
+
emitDaemonEvent('digest', 'error', 'Failed to compute digest', { error: e.message });
|
|
119
|
+
return '';
|
|
120
|
+
}
|
|
86
121
|
}
|
|
87
122
|
|
|
88
|
-
function writeStatusFile(daemonName, status,
|
|
123
|
+
function writeStatusFile(daemonName, status, sessionId) {
|
|
89
124
|
try {
|
|
90
125
|
fs.mkdirSync(GM_STATE_DIR, { recursive: true });
|
|
91
126
|
const statusFile = path.join(GM_STATE_DIR, `${daemonName}-status.json`);
|
|
92
127
|
const payload = {
|
|
93
128
|
daemon: daemonName,
|
|
94
129
|
status,
|
|
95
|
-
sessionId
|
|
130
|
+
sessionId,
|
|
96
131
|
timestamp: new Date().toISOString(),
|
|
97
132
|
pid: process.pid,
|
|
98
|
-
...details,
|
|
99
133
|
};
|
|
100
134
|
fs.writeFileSync(statusFile, JSON.stringify(payload, null, 2));
|
|
101
|
-
|
|
135
|
+
emitDaemonEvent(daemonName, 'info', 'Status written', { file: statusFile });
|
|
102
136
|
} catch (e) {
|
|
103
|
-
|
|
137
|
+
emitDaemonEvent(daemonName, 'warn', 'Failed to write status file', { error: e.message });
|
|
104
138
|
}
|
|
105
139
|
}
|
|
106
140
|
|
|
107
|
-
async function
|
|
141
|
+
async function ensureRsLearningDaemonRunning() {
|
|
142
|
+
const daemonName = 'rs-learn';
|
|
108
143
|
const sessionId = getSessionId();
|
|
109
144
|
const startTime = Date.now();
|
|
110
145
|
|
|
111
146
|
try {
|
|
112
|
-
|
|
147
|
+
emitDaemonEvent(daemonName, 'info', 'Daemon startup check initiated', { sessionId });
|
|
113
148
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
return { ok: true, running: false, durationMs: Date.now() - startTime };
|
|
149
|
+
if (isDaemonRunning(daemonName)) {
|
|
150
|
+
emitDaemonEvent(daemonName, 'info', 'Daemon already running', { sessionId });
|
|
151
|
+
writeStatusFile(daemonName, 'running', sessionId);
|
|
152
|
+
return { ok: true, already_running: true };
|
|
119
153
|
}
|
|
120
154
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
155
|
+
emitDaemonEvent(daemonName, 'info', 'Spawning daemon', { sessionId });
|
|
156
|
+
|
|
157
|
+
const env = Object.assign({}, process.env, {
|
|
158
|
+
CLAUDE_SESSION_ID: sessionId,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const proc = spawn('bun', ['x', 'rs-learn@latest'], {
|
|
162
|
+
detached: true,
|
|
163
|
+
stdio: 'ignore',
|
|
164
|
+
windowsHide: true,
|
|
165
|
+
env,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const pid = proc.pid;
|
|
169
|
+
proc.unref();
|
|
170
|
+
|
|
171
|
+
emitDaemonEvent(daemonName, 'info', 'Daemon spawned successfully', { pid, sessionId });
|
|
172
|
+
writeStatusFile(daemonName, 'started', sessionId);
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
ok: true,
|
|
176
|
+
pid,
|
|
177
|
+
sessionId,
|
|
178
|
+
durationMs: Date.now() - startTime,
|
|
179
|
+
};
|
|
124
180
|
} catch (e) {
|
|
125
|
-
|
|
181
|
+
emitDaemonEvent(daemonName, 'error', 'Daemon spawn failed', {
|
|
126
182
|
error: e.message,
|
|
127
183
|
sessionId,
|
|
128
184
|
durationMs: Date.now() - startTime,
|
|
129
185
|
});
|
|
130
|
-
|
|
186
|
+
writeStatusFile(daemonName, 'error', sessionId);
|
|
187
|
+
throw e;
|
|
131
188
|
}
|
|
132
189
|
}
|
|
133
190
|
|
|
134
|
-
async function
|
|
191
|
+
async function ensureAcptoapiRunning() {
|
|
192
|
+
const sessionId = getSessionId();
|
|
193
|
+
const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
194
|
+
const statusPath = path.join(projectDir, '.gm', 'acptoapi-status.json');
|
|
195
|
+
|
|
196
|
+
const host = '127.0.0.1';
|
|
197
|
+
const port = 4800;
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
const reachable = await checkPortReachable(host, port);
|
|
201
|
+
|
|
202
|
+
if (reachable) {
|
|
203
|
+
emitDaemonEvent('acptoapi', 'info', 'Already running', { port, sessionId });
|
|
204
|
+
writeStatusFile('acptoapi', 'running', sessionId);
|
|
205
|
+
return { ok: true, message: 'acptoapi already running' };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
emitDaemonEvent('acptoapi', 'info', 'Spawning daemon', { port, sessionId });
|
|
209
|
+
|
|
210
|
+
const env = Object.assign({}, process.env, {
|
|
211
|
+
CLAUDE_SESSION_ID: sessionId,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
const child = spawn('bun', ['x', 'acptoapi@latest'], {
|
|
216
|
+
detached: true,
|
|
217
|
+
stdio: 'ignore',
|
|
218
|
+
windowsHide: true,
|
|
219
|
+
env,
|
|
220
|
+
});
|
|
221
|
+
child.unref();
|
|
222
|
+
emitDaemonEvent('acptoapi', 'info', 'Daemon spawned', { pid: child.pid, port, sessionId });
|
|
223
|
+
writeStatusFile('acptoapi', 'spawned', sessionId);
|
|
224
|
+
return { ok: true, message: 'acptoapi spawned', pid: child.pid };
|
|
225
|
+
} catch (spawnErr) {
|
|
226
|
+
emitDaemonEvent('acptoapi', 'warn', 'Spawn failed, fallback to SDK', {
|
|
227
|
+
error: spawnErr.message,
|
|
228
|
+
sessionId,
|
|
229
|
+
});
|
|
230
|
+
writeStatusFile('acptoapi', 'spawn_failed', sessionId);
|
|
231
|
+
return { ok: false, message: 'acptoapi spawn failed, fallback to Anthropic SDK', error: spawnErr.message };
|
|
232
|
+
}
|
|
233
|
+
} catch (err) {
|
|
234
|
+
emitDaemonEvent('acptoapi', 'error', 'Check failed', {
|
|
235
|
+
error: err.message,
|
|
236
|
+
sessionId,
|
|
237
|
+
});
|
|
238
|
+
writeStatusFile('acptoapi', 'check_error', sessionId);
|
|
239
|
+
return { ok: false, message: 'acptoapi check failed, fallback to Anthropic SDK', error: err.message };
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async function ensureRsCodeinsightDaemonRunning() {
|
|
244
|
+
const daemonName = 'rs-codeinsight';
|
|
135
245
|
const sessionId = getSessionId();
|
|
136
246
|
const startTime = Date.now();
|
|
137
247
|
|
|
138
248
|
try {
|
|
139
|
-
|
|
249
|
+
emitDaemonEvent(daemonName, 'info', 'Daemon startup check initiated', { sessionId });
|
|
140
250
|
|
|
141
251
|
if (isDaemonRunning(daemonName)) {
|
|
142
|
-
|
|
143
|
-
writeStatusFile(daemonName, 'running',
|
|
144
|
-
return { ok: true, already_running: true
|
|
252
|
+
emitDaemonEvent(daemonName, 'info', 'Daemon already running', { sessionId });
|
|
253
|
+
writeStatusFile(daemonName, 'running', sessionId);
|
|
254
|
+
return { ok: true, already_running: true };
|
|
145
255
|
}
|
|
146
256
|
|
|
147
|
-
|
|
257
|
+
emitDaemonEvent(daemonName, 'info', 'Spawning daemon', { sessionId });
|
|
148
258
|
|
|
149
259
|
const env = Object.assign({}, process.env, {
|
|
150
260
|
CLAUDE_SESSION_ID: sessionId,
|
|
151
261
|
});
|
|
152
262
|
|
|
153
|
-
const proc = spawn('bun', ['x',
|
|
263
|
+
const proc = spawn('bun', ['x', 'rs-codeinsight@latest'], {
|
|
154
264
|
detached: true,
|
|
155
265
|
stdio: 'ignore',
|
|
156
266
|
windowsHide: true,
|
|
@@ -160,155 +270,166 @@ async function spawnDaemon(daemonName, cmd) {
|
|
|
160
270
|
const pid = proc.pid;
|
|
161
271
|
proc.unref();
|
|
162
272
|
|
|
163
|
-
|
|
164
|
-
writeStatusFile(daemonName, '
|
|
273
|
+
emitDaemonEvent(daemonName, 'info', 'Daemon spawned successfully', { pid, sessionId });
|
|
274
|
+
writeStatusFile(daemonName, 'started', sessionId);
|
|
165
275
|
|
|
166
|
-
return {
|
|
167
|
-
ok: true,
|
|
168
|
-
pid,
|
|
169
|
-
cmd,
|
|
170
|
-
sessionId,
|
|
171
|
-
durationMs: Date.now() - startTime,
|
|
172
|
-
};
|
|
276
|
+
return { ok: true, pid, sessionId, durationMs: Date.now() - startTime };
|
|
173
277
|
} catch (e) {
|
|
174
|
-
|
|
278
|
+
emitDaemonEvent(daemonName, 'error', 'Daemon spawn failed', {
|
|
175
279
|
error: e.message,
|
|
176
|
-
cmd,
|
|
177
280
|
sessionId,
|
|
178
281
|
durationMs: Date.now() - startTime,
|
|
179
282
|
});
|
|
180
|
-
writeStatusFile(daemonName, '
|
|
181
|
-
|
|
283
|
+
writeStatusFile(daemonName, 'error', sessionId);
|
|
284
|
+
throw e;
|
|
182
285
|
}
|
|
183
286
|
}
|
|
184
287
|
|
|
185
|
-
async function
|
|
288
|
+
async function ensureRsSearchDaemonRunning() {
|
|
289
|
+
const daemonName = 'rs-search';
|
|
186
290
|
const sessionId = getSessionId();
|
|
187
291
|
const startTime = Date.now();
|
|
188
292
|
|
|
189
293
|
try {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
294
|
+
emitDaemonEvent(daemonName, 'info', 'Daemon startup check initiated', { sessionId });
|
|
295
|
+
|
|
296
|
+
if (isDaemonRunning(daemonName)) {
|
|
297
|
+
emitDaemonEvent(daemonName, 'info', 'Daemon already running', { sessionId });
|
|
298
|
+
writeStatusFile(daemonName, 'running', sessionId);
|
|
299
|
+
return { ok: true, already_running: true };
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
emitDaemonEvent(daemonName, 'info', 'Spawning daemon', { sessionId });
|
|
303
|
+
|
|
304
|
+
const env = Object.assign({}, process.env, {
|
|
305
|
+
CLAUDE_SESSION_ID: sessionId,
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
const proc = spawn('bun', ['x', 'rs-search@latest'], {
|
|
309
|
+
detached: true,
|
|
310
|
+
stdio: 'ignore',
|
|
311
|
+
windowsHide: true,
|
|
312
|
+
env,
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
const pid = proc.pid;
|
|
316
|
+
proc.unref();
|
|
317
|
+
|
|
318
|
+
emitDaemonEvent(daemonName, 'info', 'Daemon spawned successfully', { pid, sessionId });
|
|
319
|
+
writeStatusFile(daemonName, 'started', sessionId);
|
|
320
|
+
|
|
321
|
+
return { ok: true, pid, sessionId, durationMs: Date.now() - startTime };
|
|
322
|
+
} catch (e) {
|
|
323
|
+
emitDaemonEvent(daemonName, 'error', 'Daemon spawn failed', {
|
|
324
|
+
error: e.message,
|
|
194
325
|
sessionId,
|
|
326
|
+
durationMs: Date.now() - startTime,
|
|
195
327
|
});
|
|
328
|
+
writeStatusFile(daemonName, 'error', sessionId);
|
|
329
|
+
throw e;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
async function ensureRsCodeinsightReady(sessionId = getSessionId()) {
|
|
334
|
+
const startTime = Date.now();
|
|
335
|
+
const daemonName = 'rs-codeinsight';
|
|
336
|
+
const host = '127.0.0.1';
|
|
337
|
+
const port = 4802;
|
|
196
338
|
|
|
197
|
-
|
|
339
|
+
try {
|
|
340
|
+
emitDaemonEvent(daemonName, 'info', 'Ensuring daemon readiness', { sessionId, host, port });
|
|
341
|
+
|
|
342
|
+
await ensureRsCodeinsightDaemonRunning();
|
|
343
|
+
|
|
344
|
+
const maxWaitMs = 30000;
|
|
198
345
|
const pollIntervalMs = 500;
|
|
346
|
+
const deadline = Date.now() + maxWaitMs;
|
|
199
347
|
|
|
200
348
|
while (Date.now() < deadline) {
|
|
201
349
|
const reachable = await checkPortReachable(host, port, 1000);
|
|
202
350
|
if (reachable) {
|
|
203
|
-
|
|
351
|
+
emitDaemonEvent(daemonName, 'info', 'Daemon ready', {
|
|
204
352
|
host,
|
|
205
353
|
port,
|
|
206
354
|
elapsedMs: Date.now() - startTime,
|
|
207
355
|
sessionId,
|
|
208
356
|
});
|
|
209
|
-
writeStatusFile(daemonName, 'ready',
|
|
210
|
-
return { ok: true, host, port,
|
|
357
|
+
writeStatusFile(daemonName, 'ready', sessionId);
|
|
358
|
+
return { ok: true, host, port, sessionId, durationMs: Date.now() - startTime };
|
|
211
359
|
}
|
|
212
360
|
|
|
213
361
|
await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
|
|
214
362
|
}
|
|
215
363
|
|
|
216
|
-
|
|
364
|
+
emitDaemonEvent(daemonName, 'warn', 'Timeout waiting for readiness', {
|
|
217
365
|
host,
|
|
218
366
|
port,
|
|
219
|
-
|
|
367
|
+
maxWaitMs,
|
|
220
368
|
sessionId,
|
|
221
369
|
elapsedMs: Date.now() - startTime,
|
|
222
370
|
});
|
|
223
|
-
writeStatusFile(daemonName, 'timeout',
|
|
224
|
-
return { ok: false, error: 'Timeout',
|
|
371
|
+
writeStatusFile(daemonName, 'timeout', sessionId);
|
|
372
|
+
return { ok: false, error: 'Timeout waiting for codeinsight daemon', durationMs: Date.now() - startTime };
|
|
225
373
|
} catch (e) {
|
|
226
|
-
|
|
374
|
+
emitDaemonEvent(daemonName, 'error', 'Failed to ensure readiness', {
|
|
227
375
|
error: e.message,
|
|
228
|
-
host,
|
|
229
|
-
port,
|
|
230
376
|
sessionId,
|
|
231
|
-
|
|
232
|
-
});
|
|
233
|
-
return { ok: false, error: e.message, elapsedMs: Date.now() - startTime };
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
async function getSocket(daemonName) {
|
|
238
|
-
try {
|
|
239
|
-
emitEvent(daemonName, 'info', 'getSocket initiated', { sessionId: getSessionId() });
|
|
240
|
-
|
|
241
|
-
const statusFile = path.join(GM_STATE_DIR, `${daemonName}-status.json`);
|
|
242
|
-
if (!fs.existsSync(statusFile)) {
|
|
243
|
-
emitEvent(daemonName, 'warn', 'No status file found', { statusFile });
|
|
244
|
-
return { ok: false, error: 'No status file found' };
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const status = JSON.parse(fs.readFileSync(statusFile, 'utf8'));
|
|
248
|
-
const socket = `${status.host || '127.0.0.1'}:${status.port || 'unknown'}`;
|
|
249
|
-
|
|
250
|
-
emitEvent(daemonName, 'info', 'Socket retrieved', { socket });
|
|
251
|
-
return { ok: true, socket, ...status };
|
|
252
|
-
} catch (e) {
|
|
253
|
-
emitEvent(daemonName, 'error', 'getSocket failed', {
|
|
254
|
-
error: e.message,
|
|
255
|
-
sessionId: getSessionId(),
|
|
377
|
+
durationMs: Date.now() - startTime,
|
|
256
378
|
});
|
|
257
|
-
|
|
379
|
+
writeStatusFile(daemonName, 'error', sessionId);
|
|
380
|
+
return { ok: false, error: e.message, durationMs: Date.now() - startTime };
|
|
258
381
|
}
|
|
259
382
|
}
|
|
260
383
|
|
|
261
|
-
async function
|
|
262
|
-
const sessionId = getSessionId();
|
|
384
|
+
async function ensureBrowserReady(sessionId = getSessionId()) {
|
|
263
385
|
const startTime = Date.now();
|
|
386
|
+
const host = '127.0.0.1';
|
|
387
|
+
const port = 5000;
|
|
264
388
|
|
|
265
389
|
try {
|
|
266
|
-
|
|
390
|
+
emitDaemonEvent('browser', 'info', 'Checking browser readiness', { sessionId, host, port });
|
|
267
391
|
|
|
268
|
-
const
|
|
269
|
-
|
|
392
|
+
const maxWaitMs = 10000;
|
|
393
|
+
const pollIntervalMs = 250;
|
|
394
|
+
const deadline = Date.now() + maxWaitMs;
|
|
270
395
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
killed = true;
|
|
282
|
-
} catch {
|
|
283
|
-
killed = false;
|
|
396
|
+
while (Date.now() < deadline) {
|
|
397
|
+
const reachable = await checkPortReachable(host, port, 1000);
|
|
398
|
+
if (reachable) {
|
|
399
|
+
emitDaemonEvent('browser', 'info', 'Browser ready', {
|
|
400
|
+
host,
|
|
401
|
+
port,
|
|
402
|
+
elapsedMs: Date.now() - startTime,
|
|
403
|
+
sessionId,
|
|
404
|
+
});
|
|
405
|
+
return { ok: true, host, port, sessionId, durationMs: Date.now() - startTime };
|
|
284
406
|
}
|
|
407
|
+
|
|
408
|
+
await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
|
|
285
409
|
}
|
|
286
410
|
|
|
287
|
-
|
|
288
|
-
|
|
411
|
+
emitDaemonEvent('browser', 'warn', 'Browser not available', {
|
|
412
|
+
host,
|
|
413
|
+
port,
|
|
414
|
+
maxWaitMs,
|
|
289
415
|
sessionId,
|
|
290
|
-
durationMs: Date.now() - startTime,
|
|
291
416
|
});
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
return { ok: true, killed, durationMs: Date.now() - startTime };
|
|
417
|
+
return { ok: false, error: 'Browser API not available at 127.0.0.1:5000', durationMs: Date.now() - startTime };
|
|
295
418
|
} catch (e) {
|
|
296
|
-
|
|
419
|
+
emitDaemonEvent('browser', 'error', 'Failed to check browser', {
|
|
297
420
|
error: e.message,
|
|
298
421
|
sessionId,
|
|
299
|
-
durationMs: Date.now() - startTime,
|
|
300
422
|
});
|
|
301
423
|
return { ok: false, error: e.message, durationMs: Date.now() - startTime };
|
|
302
424
|
}
|
|
303
425
|
}
|
|
304
426
|
|
|
305
427
|
module.exports = {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
isDaemonRunning,
|
|
428
|
+
ensureAcptoapiRunning,
|
|
429
|
+
ensureRsCodeinsightDaemonRunning,
|
|
430
|
+
ensureRsCodeinsightReady,
|
|
431
|
+
ensureRsSearchDaemonRunning,
|
|
432
|
+
ensureRsLearningDaemonRunning,
|
|
433
|
+
ensureBrowserReady,
|
|
313
434
|
checkPortReachable,
|
|
314
435
|
};
|
package/lib/git.js
CHANGED