gm-plugkit 2.0.1627 → 2.0.1629
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/SKILL.md +3 -3
- package/cli.js +36 -2
- package/package.json +1 -1
package/SKILL.md
CHANGED
|
@@ -34,15 +34,15 @@ Every turn: dispatch `instruction`, read it, follow the imperative, dispatch the
|
|
|
34
34
|
cat .gm/exec-spool/.status.json 2>/dev/null; echo ---; cat .gm/exec-spool/.turn-summary.json 2>/dev/null; echo ---; date +%s%3N
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
`.turn-summary.json` carries `phase`, `last_skill`, `prd_pending`, `last_instruction_ts`, `last_instruction_age_ms`, `long_gap_threshold_ms`, `browser_sessions_alive`, `update_available`, `deviations_30m`, `watcher_uptime_ms`. Age over threshold: your next non-orienting verb is gated, dispatch `instruction` first. `update_available` non-null: eager-upgrade with `bun x gm-plugkit@latest --kill-stale-watchers; bun x gm-plugkit@latest spool
|
|
37
|
+
`.turn-summary.json` carries `phase`, `last_skill`, `prd_pending`, `last_instruction_ts`, `last_instruction_age_ms`, `long_gap_threshold_ms`, `browser_sessions_alive`, `update_available`, `deviations_30m`, `watcher_uptime_ms`. Age over threshold: your next non-orienting verb is gated, dispatch `instruction` first. `update_available` non-null: eager-upgrade with `bun x gm-plugkit@latest --kill-stale-watchers; bun x gm-plugkit@latest spool` (the spool call blocks until serving), then read `.status.json` to confirm `version`. `deviations_30m` non-zero indicates active drift to investigate before continuing.
|
|
38
38
|
|
|
39
39
|
Compare `.status.json` `ts` to the printed epoch: gap > 15000 = dead, boot it. Exception: a future `busy_until` means a long verb (browser/chromium spawn blocks the heartbeat ~15-18s) -- wait, do not boot a second watcher.
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
-
bun x gm-plugkit@latest spool
|
|
42
|
+
bun x gm-plugkit@latest spool
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
(`npx -y gm-plugkit@latest spool
|
|
45
|
+
(`npx -y gm-plugkit@latest spool` if `bun` missing.) This call is atomic: it daemonizes the watcher and blocks until `.status.json` reports a fresh heartbeat, returning only once the spool is serving (exit 0) or failing loud on timeout. No `&`, no `sleep`, no re-`cat` -- when it returns you write to `instruction/` directly. (An already-alive watcher makes it return at once.)
|
|
46
46
|
|
|
47
47
|
**Dispatch shape: Write request + Read response in the SAME tool-call block.** The shape is `Write .gm/exec-spool/in/instruction/<N>.txt` AND `Read .gm/exec-spool/out/instruction-<N>.json` (or `out/<N>.json` for nested verbs) in one block. A first-read "file does not exist" while plugkit is mid-verb is normal -- re-Read next message. Do not proceed, narrate readiness, or begin work before reading the response and following its `instruction` field. Never poll with `sleep && ls`: plugkit is synchronous, so a missing response means dead watcher (re-check `ts`) or slow verb (check `.gm/exec-spool/.watcher.log`), not "still processing."
|
|
48
48
|
|
package/cli.js
CHANGED
|
@@ -132,6 +132,31 @@ function spoolDir() {
|
|
|
132
132
|
return path.join(projectDir, '.gm', 'exec-spool');
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
function readStatus(dir) {
|
|
136
|
+
try { return JSON.parse(fs.readFileSync(path.join(dir, '.status.json'), 'utf-8')); } catch (_) { return null; }
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function statusServing(st, freshMs) {
|
|
140
|
+
if (!st || !st.pid) return false;
|
|
141
|
+
const now = Date.now();
|
|
142
|
+
if (Number.isFinite(st.busy_until) && st.busy_until > now) return true;
|
|
143
|
+
return Number.isFinite(st.ts) && (now - st.ts) < freshMs;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function sleepSync(ms) {
|
|
147
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function waitForWatcherHeartbeat(dir, deadlineMs, freshMs) {
|
|
151
|
+
const t0 = Date.now();
|
|
152
|
+
while (Date.now() - t0 < deadlineMs) {
|
|
153
|
+
const st = readStatus(dir);
|
|
154
|
+
if (statusServing(st, freshMs)) return st;
|
|
155
|
+
sleepSync(300);
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
135
160
|
function ensureSpoolDir() {
|
|
136
161
|
try { fs.mkdirSync(spoolDir(), { recursive: true }); } catch (_) {}
|
|
137
162
|
}
|
|
@@ -207,13 +232,22 @@ function writeCliError(phase, err) {
|
|
|
207
232
|
process.exit(1);
|
|
208
233
|
}
|
|
209
234
|
|
|
210
|
-
|
|
235
|
+
const serving = waitForWatcherHeartbeat(spoolDir(), 30000, 12000);
|
|
236
|
+
if (!serving) {
|
|
237
|
+
const errMsg = `watcher spawned (pid=${daemon.pid}) but .status.json heartbeat not fresh within 30s`;
|
|
238
|
+
writeCliError('watcher-heartbeat-timeout', new Error(errMsg));
|
|
239
|
+
console.error('Daemon start failed:', errMsg);
|
|
240
|
+
process.exit(1);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
writeCliStatus({ phase: 'ready', version: bootstrapResult.version, daemon_pid: daemon.pid, watcher_pid: serving.pid, log: daemon.logPath });
|
|
211
244
|
|
|
212
245
|
console.log(JSON.stringify({
|
|
213
246
|
ok: true,
|
|
214
247
|
binary: bootstrapResult.binaryPath,
|
|
215
248
|
daemon,
|
|
216
|
-
|
|
249
|
+
watcher_pid: serving.pid,
|
|
250
|
+
message: 'plugkit ready, spool watcher serving'
|
|
217
251
|
}));
|
|
218
252
|
process.exit(0);
|
|
219
253
|
})().catch((err) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-plugkit",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1629",
|
|
4
4
|
"description": "Bootstrap and daemon-spawn tool for gm plugkit binary. Downloads the correct platform binary, verifies SHA256, and starts the spool watcher daemon. Includes plugkit-wasm-wrapper for WASM-based spool watching.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|