gm-skill 2.0.1514 → 2.0.1516
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 +2 -0
- package/gm-plugkit/package.json +1 -1
- package/gm-plugkit/plugkit-wasm-wrapper.js +23 -19
- package/gm.json +1 -1
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -30,6 +30,8 @@ The plugkit stack runs as a wasm cdylib loaded by `plugkit-wasm-wrapper.js` unde
|
|
|
30
30
|
|
|
31
31
|
**Every wasm host-import `extern "C"` block carries `#[link(wasm_import_module = "env")]`.** The host provides every possible host fn (host_kv_get/put/query, host_vec_search, host_git, host_log, host_now_ms, host_fs_*, host_env_get, host_exec_js, host_random_fill, …) under the `env` import module (`plugkit-wasm-wrapper.js` `importObject.env`). A bare `extern "C"` block links only because lenient linkers tolerate the unresolved module; the strict Linux release `rust-lld` in CI errors `undefined symbol: host_*` and Build-WASM fails. This holds in rs-plugkit AND every dep crate linked into the cdylib (rs-learn) AND any sibling that builds wasm (rs-exec, rs-search). The trap: `cargo check` and even `cargo build --release` on a non-Linux host both pass while CI fails — the linker differs by host, so the only reproduction is a Linux release link; the CI job log is admin-gated, so Build-WASM echoes `::error::` annotations to surface the lld error publicly. Add a host import anywhere and the block carries the attribute or the cascade goes dark. Full incident in rs-learn (`recall: cascade outage wasm import module link`).
|
|
32
32
|
|
|
33
|
+
**`plugkit-wasm-wrapper.js` is ESM; never use inline `require()` for a node builtin — import it at module scope.** The wrapper runs under both node and bun, and the supervisor's `resolveRuntime()` prefers bun. Under bun's ESM, `require` is not a global, so an inline `const x = require('crypto'|'net'|'http'|'https'|'child_process')` throws `require is not defined` — and because those calls sit in `catch(_){}` blocks, the failure is silent: it broke `_ownWrapperSha12` (status.wrapper_sha stayed null, leaving the supervisor wrapper-sha-drift recycle inert), `_wrapperShaAtBoot` and its self-drift-restart, the synthetic-session cwd-hash, and the file-index sha — all only under the bun watcher, which is why it hid for so long (node-run watchers have `require` via CJS interop). Every node builtin is imported once at the top (`import crypto from 'crypto'`, etc.); inline `require` of a builtin is forbidden. Full incident in rs-learn (`recall: wrapper require not defined under bun`).
|
|
34
|
+
|
|
33
35
|
## Spool dispatch ABI
|
|
34
36
|
|
|
35
37
|
Agents dispatch verbs by writing to `.gm/exec-spool/in/<verb>/<N>.txt` (request body) and reading the response from `.gm/exec-spool/out/<verb>-<N>.json` (nested verbs) or `.gm/exec-spool/out/<N>.json` (root verbs). The wasm orchestrator services every possible verb; the harness never executes side effects directly.
|
package/gm-plugkit/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-plugkit",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1516",
|
|
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": {
|
|
@@ -3,9 +3,14 @@ import path from 'path';
|
|
|
3
3
|
import os from 'os';
|
|
4
4
|
import crypto from 'crypto';
|
|
5
5
|
import https from 'https';
|
|
6
|
+
import http from 'http';
|
|
6
7
|
import { watch } from 'fs';
|
|
8
|
+
import * as _childProcess from 'child_process';
|
|
7
9
|
import { spawn as _rawSpawn, spawnSync as _rawSpawnSync } from 'child_process';
|
|
8
10
|
import net from 'net';
|
|
11
|
+
const _netModule = net;
|
|
12
|
+
const _httpModule = http;
|
|
13
|
+
const _httpsModule = https;
|
|
9
14
|
import { fileURLToPath } from 'url';
|
|
10
15
|
|
|
11
16
|
// Set by the spool watcher's writeStatus closure once it is live. Lets long synchronous verbs
|
|
@@ -437,7 +442,7 @@ function readCurrentSess() {
|
|
|
437
442
|
let resolved = found || process.env.CLAUDE_SESSION_ID || process.env.GM_SESSION_ID || '';
|
|
438
443
|
if (!resolved) {
|
|
439
444
|
if (!__sessCache.syntheticSess) {
|
|
440
|
-
const cwdHash =
|
|
445
|
+
const cwdHash = crypto.createHash('sha1').update(process.cwd()).digest('hex').slice(0, 8);
|
|
441
446
|
__sessCache.syntheticSess = `cwd-${cwdHash}-pid${process.pid}`;
|
|
442
447
|
}
|
|
443
448
|
resolved = __sessCache.syntheticSess;
|
|
@@ -488,7 +493,7 @@ function autoRecordBrowserEditsFromBody(body, cwd, taskBase, verb) {
|
|
|
488
493
|
try { st = fs.statSync(abs); } catch (_) { continue; }
|
|
489
494
|
if (!st.isFile()) continue;
|
|
490
495
|
let hash = '';
|
|
491
|
-
try { hash =
|
|
496
|
+
try { hash = crypto.createHash('sha256').update(fs.readFileSync(abs)).digest('hex').slice(0, 12); } catch (_) {}
|
|
492
497
|
const idx = list.findIndex(e => e && e.file === rel);
|
|
493
498
|
const entry = { file: rel, ts: Date.now(), hash, source_verb: verb, source_task: taskBase };
|
|
494
499
|
if (idx === -1) { list.push(entry); added++; } else { list[idx] = entry; }
|
|
@@ -823,7 +828,7 @@ function resolveWindowsExeLocal(cmd) {
|
|
|
823
828
|
|
|
824
829
|
function isPortReachableSync(host, port, timeoutMs) {
|
|
825
830
|
const r = spawnSync(process.execPath, ['-e', `
|
|
826
|
-
const net =
|
|
831
|
+
const net = _netModule;
|
|
827
832
|
const s = net.connect({ port: ${port}, host: ${JSON.stringify(host)} });
|
|
828
833
|
let done = false;
|
|
829
834
|
s.on('connect', () => { done = true; s.destroy(); process.exit(0); });
|
|
@@ -835,7 +840,7 @@ function isPortReachableSync(host, port, timeoutMs) {
|
|
|
835
840
|
|
|
836
841
|
function findFreePortSync() {
|
|
837
842
|
const r = spawnSync(process.execPath, ['-e', `
|
|
838
|
-
const net =
|
|
843
|
+
const net = _netModule;
|
|
839
844
|
const srv = net.createServer();
|
|
840
845
|
srv.listen(0, '127.0.0.1', () => { const p = srv.address().port; srv.close(() => { process.stdout.write(String(p)); }); });
|
|
841
846
|
srv.on('error', e => { process.stderr.write(e.message); process.exit(1); });
|
|
@@ -846,7 +851,7 @@ function findFreePortSync() {
|
|
|
846
851
|
|
|
847
852
|
function isPortAliveSync(port) {
|
|
848
853
|
const r = spawnSync(process.execPath, ['-e', `
|
|
849
|
-
const net =
|
|
854
|
+
const net = _netModule;
|
|
850
855
|
const s = net.connect({ port: ${port}, host: '127.0.0.1' });
|
|
851
856
|
s.on('connect', () => { s.destroy(); process.exit(0); });
|
|
852
857
|
s.on('error', () => process.exit(1));
|
|
@@ -944,7 +949,7 @@ function findInstalledChromiumBinary() {
|
|
|
944
949
|
|
|
945
950
|
function fetchJsonSync(url, timeoutMs) {
|
|
946
951
|
const r = spawnSync(process.execPath, ['-e', `
|
|
947
|
-
const http =
|
|
952
|
+
const http = _httpModule;
|
|
948
953
|
const req = http.get(${JSON.stringify(url)}, (res) => {
|
|
949
954
|
let buf = '';
|
|
950
955
|
res.on('data', d => buf += d);
|
|
@@ -1052,7 +1057,7 @@ function gracefulCloseBrowser(entry, reason) {
|
|
|
1052
1057
|
const info = fetchJsonSync(`http://127.0.0.1:${port}/json/version`, 600);
|
|
1053
1058
|
if (info && info.webSocketDebuggerUrl) {
|
|
1054
1059
|
spawnSync(process.execPath, ['-e', `
|
|
1055
|
-
const http =
|
|
1060
|
+
const http = _httpModule;
|
|
1056
1061
|
const req = http.request({host:'127.0.0.1',port:${port},path:'/json/close/browser',method:'GET',timeout:1500},
|
|
1057
1062
|
res => { res.resume(); res.on('end', () => process.exit(0)); });
|
|
1058
1063
|
req.on('error', () => process.exit(1));
|
|
@@ -2083,10 +2088,11 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
2083
2088
|
|
|
2084
2089
|
const LOCK_PATH = path.join(spoolDir, '.watcher.lock');
|
|
2085
2090
|
try {
|
|
2086
|
-
const _crypto = require('crypto');
|
|
2087
2091
|
const _wp = path.join(GM_TOOLS_ROOT, 'plugkit-wasm-wrapper.js');
|
|
2088
|
-
_ownWrapperSha12 =
|
|
2089
|
-
} catch (
|
|
2092
|
+
_ownWrapperSha12 = crypto.createHash('sha256').update(fs.readFileSync(_wp)).digest('hex').slice(0, 12);
|
|
2093
|
+
} catch (e) {
|
|
2094
|
+
try { logEvent('plugkit', 'watcher.own-wrapper-sha-failed', { error: String(e && e.message || e), gm_tools_root: GM_TOOLS_ROOT }); } catch (_) {}
|
|
2095
|
+
}
|
|
2090
2096
|
function lockBody() { return `${process.pid}|${Date.now()}|${_ownWrapperSha12}`; }
|
|
2091
2097
|
function acquireLock() {
|
|
2092
2098
|
try {
|
|
@@ -2347,7 +2353,7 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
2347
2353
|
try { own = JSON.parse(fs.readFileSync(ownPkgJsonFile, 'utf-8')).version; } catch (_) {}
|
|
2348
2354
|
}
|
|
2349
2355
|
if (!own) return;
|
|
2350
|
-
const https =
|
|
2356
|
+
const https = _httpsModule;
|
|
2351
2357
|
let _probeErrored = false;
|
|
2352
2358
|
const req = https.get('https://registry.npmjs.org/gm-plugkit/latest', { timeout: 10000, headers: { 'user-agent': 'plugkit-watcher' } }, (res) => {
|
|
2353
2359
|
let body = '';
|
|
@@ -2381,7 +2387,7 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
2381
2387
|
try { logEvent('plugkit', 'gm-plugkit.self-stale', { running_version: own, latest_version: latest, detected_by: 'watcher-periodic-probe' }); } catch (_) {}
|
|
2382
2388
|
console.error(`[plugkit-wasm] gm-plugkit self-stale: running ${own}, latest npm ${latest} -> spawning replacement via bun x gm-plugkit@latest spool and exiting`);
|
|
2383
2389
|
try {
|
|
2384
|
-
const cp =
|
|
2390
|
+
const cp = _childProcess;
|
|
2385
2391
|
const bunPath = process.env.GM_BUN_PATH || 'bun';
|
|
2386
2392
|
const child = cp.spawn(bunPath, ['x', `gm-plugkit@${latest}`, 'spool'], {
|
|
2387
2393
|
cwd: process.cwd(),
|
|
@@ -2472,7 +2478,7 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
2472
2478
|
console.error(`[plugkit-wasm] version drift detected: instance=${instV} file=${fileV} — spawning replacement via bun x gm-plugkit@latest spool, waiting for its heartbeat before exiting`);
|
|
2473
2479
|
let spawnOk = false;
|
|
2474
2480
|
try {
|
|
2475
|
-
const cp =
|
|
2481
|
+
const cp = _childProcess;
|
|
2476
2482
|
const bunPath = process.env.GM_BUN_PATH || 'bun';
|
|
2477
2483
|
const child = cp.spawn(bunPath, ['x', 'gm-plugkit@latest', 'spool'], {
|
|
2478
2484
|
cwd: process.cwd(),
|
|
@@ -2547,15 +2553,13 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
2547
2553
|
const _wrapperPathInstalled = path.join(GM_TOOLS_ROOT, 'plugkit-wasm-wrapper.js');
|
|
2548
2554
|
let _wrapperShaAtBoot = '';
|
|
2549
2555
|
try {
|
|
2550
|
-
|
|
2551
|
-
_wrapperShaAtBoot = _crypto.createHash('sha256').update(fs.readFileSync(_wrapperPathInstalled)).digest('hex');
|
|
2556
|
+
_wrapperShaAtBoot = crypto.createHash('sha256').update(fs.readFileSync(_wrapperPathInstalled)).digest('hex');
|
|
2552
2557
|
} catch (_) {}
|
|
2553
2558
|
let _wrapperDriftLoggedOnce = false;
|
|
2554
2559
|
setInterval(() => {
|
|
2555
2560
|
try {
|
|
2556
2561
|
if (!_wrapperShaAtBoot) return;
|
|
2557
|
-
const
|
|
2558
|
-
const cur = _crypto.createHash('sha256').update(fs.readFileSync(_wrapperPathInstalled)).digest('hex');
|
|
2562
|
+
const cur = crypto.createHash('sha256').update(fs.readFileSync(_wrapperPathInstalled)).digest('hex');
|
|
2559
2563
|
if (cur === _wrapperShaAtBoot) return;
|
|
2560
2564
|
const bootReason = process.env.PLUGKIT_BOOT_REASON || 'unknown';
|
|
2561
2565
|
const unsupervised = bootReason === 'direct-no-supervisor';
|
|
@@ -2570,7 +2574,7 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
2570
2574
|
});
|
|
2571
2575
|
console.error(`[plugkit-wasm] wrapper.js drift detected — spawning replacement directly from installed wrapper then exiting`);
|
|
2572
2576
|
try {
|
|
2573
|
-
const cp =
|
|
2577
|
+
const cp = _childProcess;
|
|
2574
2578
|
const child = cp.spawn(process.execPath, [_wrapperPathInstalled, 'spool'], {
|
|
2575
2579
|
cwd: process.cwd(),
|
|
2576
2580
|
detached: true,
|
|
@@ -2582,7 +2586,7 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
2582
2586
|
} catch (e) {
|
|
2583
2587
|
console.error(`[plugkit-wasm] direct node spawn failed: ${e.message}; falling back to bun x`);
|
|
2584
2588
|
try {
|
|
2585
|
-
const cp =
|
|
2589
|
+
const cp = _childProcess;
|
|
2586
2590
|
const bunPath = process.env.GM_BUN_PATH || 'bun';
|
|
2587
2591
|
const child = cp.spawn(bunPath, ['x', 'gm-plugkit@latest', 'spool'], {
|
|
2588
2592
|
cwd: process.cwd(),
|
package/gm.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-skill",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1516",
|
|
4
4
|
"description": "Canonical universal harness — AI-native software engineering via skill-driven orchestration; bootstraps plugkit for task execution and session isolation. Install in any AI coding agent host.",
|
|
5
5
|
"author": "AnEntrypoint",
|
|
6
6
|
"license": "MIT",
|