gm-skill 2.0.1915 → 2.0.1917
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/gm-plugkit/cli.js +55 -35
- package/gm-plugkit/package.json +1 -1
- package/gm-plugkit/plugkit-wasm-wrapper.js +46 -25
- package/gm.json +1 -1
- package/package.json +1 -1
package/gm-plugkit/cli.js
CHANGED
|
@@ -5,7 +5,43 @@ const fs = require('fs');
|
|
|
5
5
|
const os = require('os');
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const cp = require('child_process');
|
|
8
|
-
const { ensureReady, startSpoolDaemon, gmToolsDir, readVersionFile, ensureGmPlugkitVersionFresh, ensureSkillMdFresh, ensureWrapperFresh } = require('./bootstrap');
|
|
8
|
+
const { ensureReady, startSpoolDaemon, gmToolsDir, readVersionFile, ensureGmPlugkitVersionFresh, ensureSkillMdFresh, ensureWrapperFresh, isReady, getWasmPath } = require('./bootstrap');
|
|
9
|
+
|
|
10
|
+
function getWasmPathSafe() {
|
|
11
|
+
try { return getWasmPath(); } catch (_) { return null; }
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function spawnBackgroundFreshnessCheck(reason) {
|
|
15
|
+
try {
|
|
16
|
+
const child = cp.spawn(process.execPath, [__filename.replace(/cli\.js$/, 'bootstrap.js')], {
|
|
17
|
+
detached: true,
|
|
18
|
+
stdio: 'ignore',
|
|
19
|
+
windowsHide: true,
|
|
20
|
+
env: { ...process.env, GM_PLUGKIT_BACKGROUND_REFRESH: reason },
|
|
21
|
+
});
|
|
22
|
+
child.unref();
|
|
23
|
+
} catch (_) {}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function spawnDaemonOrExit(version, binaryPath, message) {
|
|
27
|
+
let daemon;
|
|
28
|
+
try {
|
|
29
|
+
daemon = startSpoolDaemon();
|
|
30
|
+
} catch (err) {
|
|
31
|
+
writeCliError('start-daemon', err);
|
|
32
|
+
console.error('Daemon start failed:', err.message);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
if (!daemon || !daemon.ok) {
|
|
36
|
+
const errMsg = (daemon && daemon.error) || 'startSpoolDaemon returned non-ok';
|
|
37
|
+
writeCliError('start-daemon', new Error(errMsg));
|
|
38
|
+
console.error('Daemon start failed:', errMsg);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
writeCliStatus({ phase: 'daemon-spawned', version, daemon_pid: daemon.pid, log: daemon.logPath });
|
|
42
|
+
console.log(JSON.stringify({ ok: true, binary: binaryPath, daemon, message }));
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
9
45
|
|
|
10
46
|
function readUpdateAvailableMarker(dir) {
|
|
11
47
|
try {
|
|
@@ -237,6 +273,19 @@ function writeCliError(phase, err) {
|
|
|
237
273
|
} catch (_) {}
|
|
238
274
|
}
|
|
239
275
|
|
|
276
|
+
const wrapperPath = path.join(gmToolsDir(), 'plugkit-wasm-wrapper.js');
|
|
277
|
+
if (isReady() && fs.existsSync(wrapperPath)) {
|
|
278
|
+
let installedVersion = null;
|
|
279
|
+
try { installedVersion = readVersionFile(); } catch (_) { installedVersion = null; }
|
|
280
|
+
writeCliStatus({ phase: 'bootstrapped', version: installedVersion, binary: getWasmPathSafe() });
|
|
281
|
+
spawnBackgroundFreshnessCheck(versionDrifted ? 'version-drift-respawn' : 'fast-path-spawn');
|
|
282
|
+
spawnDaemonOrExit(
|
|
283
|
+
installedVersion,
|
|
284
|
+
getWasmPathSafe(),
|
|
285
|
+
'plugkit daemon spawned from existing local install, not yet confirmed serving -- check .gm/exec-spool/.status.json for heartbeat freshness; remote freshness check running in background'
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
|
|
240
289
|
let bootstrapResult;
|
|
241
290
|
try {
|
|
242
291
|
bootstrapResult = await ensureReady();
|
|
@@ -254,40 +303,11 @@ function writeCliError(phase, err) {
|
|
|
254
303
|
}
|
|
255
304
|
|
|
256
305
|
writeCliStatus({ phase: 'bootstrapped', version: bootstrapResult.version, binary: bootstrapResult.binaryPath });
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
daemon
|
|
261
|
-
|
|
262
|
-
writeCliError('start-daemon', err);
|
|
263
|
-
console.error('Daemon start failed:', err.message);
|
|
264
|
-
process.exit(1);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
if (!daemon || !daemon.ok) {
|
|
268
|
-
const errMsg = (daemon && daemon.error) || 'startSpoolDaemon returned non-ok';
|
|
269
|
-
writeCliError('start-daemon', new Error(errMsg));
|
|
270
|
-
console.error('Daemon start failed:', errMsg);
|
|
271
|
-
process.exit(1);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// Fire-and-forget: the daemon child is already detached+unref'd by
|
|
275
|
-
// startSpoolDaemon(), so the CLI process itself has nothing left to do.
|
|
276
|
-
// Waiting here for a heartbeat confirmation (removed) used to block the
|
|
277
|
-
// calling shell for the full bootstrap+first-heartbeat duration -- up to
|
|
278
|
-
// 30s on a healthy boot, worse on a slow/degraded one -- holding up every
|
|
279
|
-
// caller even though the daemon spawn itself is near-instant. A caller
|
|
280
|
-
// that needs serving-confirmation reads .gm/exec-spool/.status.json
|
|
281
|
-
// directly (ts freshness) rather than blocking this call on it.
|
|
282
|
-
writeCliStatus({ phase: 'daemon-spawned', version: bootstrapResult.version, daemon_pid: daemon.pid, log: daemon.logPath });
|
|
283
|
-
|
|
284
|
-
console.log(JSON.stringify({
|
|
285
|
-
ok: true,
|
|
286
|
-
binary: bootstrapResult.binaryPath,
|
|
287
|
-
daemon,
|
|
288
|
-
message: 'plugkit daemon spawned, not yet confirmed serving -- check .gm/exec-spool/.status.json for heartbeat freshness'
|
|
289
|
-
}));
|
|
290
|
-
process.exit(0);
|
|
306
|
+
spawnDaemonOrExit(
|
|
307
|
+
bootstrapResult.version,
|
|
308
|
+
bootstrapResult.binaryPath,
|
|
309
|
+
'plugkit daemon spawned, not yet confirmed serving -- check .gm/exec-spool/.status.json for heartbeat freshness'
|
|
310
|
+
);
|
|
291
311
|
})().catch((err) => {
|
|
292
312
|
writeCliError('uncaught', err);
|
|
293
313
|
console.error('gm-plugkit failed:', err && err.message ? err.message : err);
|
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.1917",
|
|
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": {
|
|
@@ -790,29 +790,40 @@ function findCachedBunRunnerVersion() {
|
|
|
790
790
|
const _patchedPlaywriterDistDirs = new Set();
|
|
791
791
|
function patchPlaywriterTimeoutBug(binJs) {
|
|
792
792
|
try {
|
|
793
|
-
const
|
|
794
|
-
if (_patchedPlaywriterDistDirs.has(
|
|
795
|
-
_patchedPlaywriterDistDirs.add(
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
793
|
+
const pkgDir = path.dirname(binJs);
|
|
794
|
+
if (_patchedPlaywriterDistDirs.has(pkgDir)) return;
|
|
795
|
+
_patchedPlaywriterDistDirs.add(pkgDir);
|
|
796
|
+
// executor.js/cli.js live under <pkg-dir>/dist/ in the real published package layout, not
|
|
797
|
+
// directly alongside bin.js -- the prior version of this function joined path.dirname(binJs)
|
|
798
|
+
// straight onto 'executor.js'/'cli.js', which never matched any real file (fs.existsSync was
|
|
799
|
+
// always false), so the timeout-coercion patch silently never applied and every managed
|
|
800
|
+
// browser session with an explicit timeout= prefix threw
|
|
801
|
+
// `TypeError [ERR_INVALID_ARG_TYPE]: The "options.timeout" property must be of type number`.
|
|
802
|
+
// Check both the dist/ subfolder (the real layout) and the flat pkg-dir (defensive fallback
|
|
803
|
+
// for a differently-laid-out future version) so this keeps working either way.
|
|
804
|
+
const candidateDirs = [path.join(pkgDir, 'dist'), pkgDir];
|
|
805
|
+
for (const distDir of candidateDirs) {
|
|
806
|
+
const executorPath = path.join(distDir, 'executor.js');
|
|
807
|
+
if (fs.existsSync(executorPath)) {
|
|
808
|
+
const src = fs.readFileSync(executorPath, 'utf-8');
|
|
809
|
+
if (/async execute\(code, timeout = 10000\) \{(?!\s*timeout = Number)/.test(src)) {
|
|
810
|
+
const patched = src.replace(
|
|
811
|
+
/(async execute\(code, timeout = 10000\) \{)/,
|
|
812
|
+
'$1\n timeout = Number(timeout) || 10000;'
|
|
813
|
+
);
|
|
814
|
+
fs.writeFileSync(executorPath, patched);
|
|
815
|
+
}
|
|
805
816
|
}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
817
|
+
const cliPath = path.join(distDir, 'cli.js');
|
|
818
|
+
if (fs.existsSync(cliPath)) {
|
|
819
|
+
const src = fs.readFileSync(cliPath, 'utf-8');
|
|
820
|
+
if (src.includes('timeout: options.timeout || 10000') && !src.includes('timeout: Number(options.timeout)')) {
|
|
821
|
+
const patched = src.replace(
|
|
822
|
+
'timeout: options.timeout || 10000',
|
|
823
|
+
'timeout: Number(options.timeout) || 10000'
|
|
824
|
+
);
|
|
825
|
+
fs.writeFileSync(cliPath, patched);
|
|
826
|
+
}
|
|
816
827
|
}
|
|
817
828
|
}
|
|
818
829
|
} catch (_) {}
|
|
@@ -3116,15 +3127,25 @@ function makeHostFunctions(instanceRef) {
|
|
|
3116
3127
|
const __topNM = modeOpts.match(/topN=(\d+)/);
|
|
3117
3128
|
const sampleIntervalUs = __intervalM && parseInt(__intervalM[1], 10) > 0 ? parseInt(__intervalM[1], 10) : 100;
|
|
3118
3129
|
const profileTopNBrowser = __topNM && parseInt(__topNM[1], 10) > 0 ? parseInt(__topNM[1], 10) : 20;
|
|
3130
|
+
// Real, pre-existing bug fixed here: this block previously called `page.evaluateOnNewDocument`,
|
|
3131
|
+
// which is a PUPPETEER method name -- playwriter's `page` object is a real Playwright Page,
|
|
3132
|
+
// whose equivalent is `page.addInitScript`. evaluateOnNewDocument does not exist on a
|
|
3133
|
+
// Playwright page, so both calls threw `TypeError: page.evaluateOnNewDocument is not a
|
|
3134
|
+
// function` on every single dispatch, silently swallowed by the enclosing try/catch -- meaning
|
|
3135
|
+
// window.__gmErrors (window.onerror/onunhandledrejection capture) was NEVER actually installed,
|
|
3136
|
+
// for as long as this code has existed, independent of the new GL-instrumentation block added
|
|
3137
|
+
// alongside it (which inherited the same wrong method name by copying the existing pattern).
|
|
3138
|
+
// Verified live: window.HTMLCanvasElement.prototype.getContext.toString() did not contain the
|
|
3139
|
+
// patch marker before this fix, confirming the pre-navigation init script never ran.
|
|
3119
3140
|
const debugSetup = `const __logs=[],__errs=[],__net=[];\n`
|
|
3120
3141
|
+ `try{page.on('console',m=>{try{__logs.push({type:m.type(),text:m.text()});}catch(_){}});`
|
|
3121
3142
|
+ `page.on('pageerror',e=>{try{__errs.push({type:'pageerror',msg:String(e&&e.message||e)});}catch(_){}});`
|
|
3122
3143
|
+ `page.on('error',e=>{try{__errs.push({type:'uncaught',msg:String(e&&e.message||e),stack:String(e&&e.stack||'')});}catch(_){}});`
|
|
3123
3144
|
+ `page.on('requestfinished',r=>{try{const t=r.timing();let __st=0,__sz=0;try{__st=(r.response()&&r.response().status())||0;}catch(_){}__net.push({url:String(r.url()).slice(0,120),method:r.method(),status:__st,dur_ms:Math.round(t.responseEnd),ttfb_ms:Math.round(t.responseStart)});}catch(_){}});`
|
|
3124
3145
|
+ `page.on('requestfailed',r=>{try{const err=r.failure();__errs.push({type:'fetch',msg:String(err&&err.errorText||'request failed'),url:String(r.url()).slice(0,120)});}catch(_){}});`
|
|
3125
|
-
+ `page.
|
|
3146
|
+
+ `await page.addInitScript(()=>{window.__gmErrors=[];window.onerror=(msg,src,line,col,err)=>{try{window.__gmErrors.push({type:'error',msg:String(msg),src:String(src).slice(0,80),line,col,stack:String(err&&err.stack||'')});}catch(_){};return false;};window.onunhandledrejection=(e)=>{try{window.__gmErrors.push({type:'unhandledRejection',msg:String(e.reason&&e.reason.message||e.reason),stack:String(e.reason&&e.reason.stack||'')});}catch(_){}};});`
|
|
3126
3147
|
// Auto-instrument every canvas's WebGL/WebGL2 context: patch getContext once (idempotent,
|
|
3127
|
-
// pre-navigation via
|
|
3148
|
+
// pre-navigation via page.addInitScript so it is in place before the page's own first
|
|
3128
3149
|
// getContext call) to wrap every draw call (drawArrays/drawElements and their -Instanced
|
|
3129
3150
|
// variants) with a post-call gl.getError() drain. This is exactly the manual
|
|
3130
3151
|
// gl.*=function(){...gl.getError()...} monkeypatch pattern used ad hoc to root-cause GPU
|
|
@@ -3134,7 +3155,7 @@ function makeHostFunctions(instanceRef) {
|
|
|
3134
3155
|
// window.__gmGlErrors accumulates {fn,mode,count,offset,type,error,errorName,ctxLabel} for
|
|
3135
3156
|
// up to 40 distinct GL errors per page load (capped to bound memory on a runaway-error page);
|
|
3136
3157
|
// window.__gmGlDrawCalls is a running total per draw-fn name for volume context.
|
|
3137
|
-
+ `page.
|
|
3158
|
+
+ `await page.addInitScript(()=>{`
|
|
3138
3159
|
+ `window.__gmGlErrors=[];window.__gmGlDrawCalls={};`
|
|
3139
3160
|
+ `const __glErrName=(gl,code)=>{for(const k of ['NO_ERROR','INVALID_ENUM','INVALID_VALUE','INVALID_OPERATION','INVALID_FRAMEBUFFER_OPERATION','OUT_OF_MEMORY','CONTEXT_LOST_WEBGL']){try{if(gl[k]===code)return k;}catch(_){}}return 'UNKNOWN_'+code;};`
|
|
3140
3161
|
+ `const __wrapDraw=(gl,ctxLabel)=>{`
|
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.1917",
|
|
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",
|