gm-skill 2.0.1849 → 2.0.1851
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/bootstrap.js +18 -4
- package/gm-plugkit/package.json +1 -1
- package/gm-plugkit/plugkit-wasm-wrapper.js +89 -12
- package/gm-plugkit/supervisor.js +19 -6
- package/gm.json +1 -1
- package/package.json +1 -1
package/gm-plugkit/bootstrap.js
CHANGED
|
@@ -1047,13 +1047,12 @@ function probeUnsupervisedWatcher(spoolDir) {
|
|
|
1047
1047
|
}
|
|
1048
1048
|
|
|
1049
1049
|
function resolveNodeRuntime() {
|
|
1050
|
-
const isNodeExe = (p) => /(^|[\\/])node(\.exe)?$/i.test(String(p || ''));
|
|
1051
1050
|
const candidates = [];
|
|
1052
|
-
if (
|
|
1053
|
-
|
|
1051
|
+
if (process.env.PLUGKIT_RUNTIME) candidates.push(process.env.PLUGKIT_RUNTIME);
|
|
1052
|
+
candidates.push('bun');
|
|
1054
1053
|
try {
|
|
1055
1054
|
const which = process.platform === 'win32' ? 'where' : 'which';
|
|
1056
|
-
const out = require('child_process').spawnSync(which, ['
|
|
1055
|
+
const out = require('child_process').spawnSync(which, ['bun'], { encoding: 'utf8', windowsHide: true });
|
|
1057
1056
|
if (out && out.stdout) {
|
|
1058
1057
|
const first = out.stdout.split(/\r?\n/).map((s) => s.trim()).filter(Boolean)[0];
|
|
1059
1058
|
if (first) candidates.push(first);
|
|
@@ -1062,6 +1061,21 @@ function resolveNodeRuntime() {
|
|
|
1062
1061
|
for (const c of candidates) {
|
|
1063
1062
|
try { const r = require('child_process').spawnSync(c, ['--version'], { stdio: 'ignore', windowsHide: true }); if (r && r.status === 0) return c; } catch (_) {}
|
|
1064
1063
|
}
|
|
1064
|
+
const isNodeExe = (p) => /(^|[\\/])node(\.exe)?$/i.test(String(p || ''));
|
|
1065
|
+
const nodeCandidates = [];
|
|
1066
|
+
if (isNodeExe(process.env.GM_NODE_PATH)) nodeCandidates.push(process.env.GM_NODE_PATH);
|
|
1067
|
+
if (isNodeExe(process.execPath)) nodeCandidates.push(process.execPath);
|
|
1068
|
+
try {
|
|
1069
|
+
const which = process.platform === 'win32' ? 'where' : 'which';
|
|
1070
|
+
const out = require('child_process').spawnSync(which, ['node'], { encoding: 'utf8', windowsHide: true });
|
|
1071
|
+
if (out && out.stdout) {
|
|
1072
|
+
const first = out.stdout.split(/\r?\n/).map((s) => s.trim()).filter(Boolean)[0];
|
|
1073
|
+
if (first) nodeCandidates.push(first);
|
|
1074
|
+
}
|
|
1075
|
+
} catch (_) {}
|
|
1076
|
+
for (const c of nodeCandidates) {
|
|
1077
|
+
try { const r = require('child_process').spawnSync(c, ['--version'], { stdio: 'ignore', windowsHide: true }); if (r && r.status === 0) return c; } catch (_) {}
|
|
1078
|
+
}
|
|
1065
1079
|
return process.execPath;
|
|
1066
1080
|
}
|
|
1067
1081
|
|
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.1851",
|
|
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": {
|
|
@@ -117,12 +117,14 @@ function clearVerbActive() {
|
|
|
117
117
|
|
|
118
118
|
process.on('uncaughtException', (err) => {
|
|
119
119
|
try { console.error('[plugkit-wasm] uncaught:', err && err.stack || err); } catch (_) {}
|
|
120
|
+
try { killAllTasks('crash:uncaughtException'); } catch (_) {}
|
|
120
121
|
emitShutdownReason('uncaughtException', err);
|
|
121
122
|
process.exit(1);
|
|
122
123
|
});
|
|
123
124
|
|
|
124
125
|
process.on('unhandledRejection', (reason) => {
|
|
125
126
|
try { console.error('[plugkit-wasm] unhandled rejection:', reason && reason.stack || reason); } catch (_) {}
|
|
127
|
+
try { killAllTasks('crash:unhandledRejection'); } catch (_) {}
|
|
126
128
|
emitShutdownReason('unhandledRejection', reason instanceof Error ? reason : new Error(String(reason)));
|
|
127
129
|
process.exit(1);
|
|
128
130
|
});
|
|
@@ -744,24 +746,38 @@ function aggregateCpuProfile(profile, topN) {
|
|
|
744
746
|
|
|
745
747
|
const BROWSER_RUNNER_BIN = process.env.GM_BROWSER_RUNNER_BIN || 'playwriter';
|
|
746
748
|
|
|
749
|
+
function findCachedBunRunnerBin() {
|
|
750
|
+
try {
|
|
751
|
+
const cacheDir = path.join(os.homedir(), '.bun', 'install', 'cache');
|
|
752
|
+
const entries = fs.readdirSync(cacheDir).filter(n => n.startsWith(`${BROWSER_RUNNER_BIN}@`));
|
|
753
|
+
for (const name of entries) {
|
|
754
|
+
const binJs = path.join(cacheDir, name, 'bin.js');
|
|
755
|
+
if (fs.existsSync(binJs)) return binJs;
|
|
756
|
+
}
|
|
757
|
+
} catch (_) {}
|
|
758
|
+
return null;
|
|
759
|
+
}
|
|
760
|
+
|
|
747
761
|
function findBrowserRunner() {
|
|
748
|
-
const
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
762
|
+
const bunGlobalRoots = [
|
|
763
|
+
path.join(os.homedir(), '.bun', 'install', 'global', 'node_modules', BROWSER_RUNNER_BIN, 'bin.js'),
|
|
764
|
+
];
|
|
765
|
+
for (const binJs of bunGlobalRoots) {
|
|
752
766
|
if (fs.existsSync(binJs)) return { cmd: process.execPath, baseArgs: [binJs], shell: false };
|
|
753
767
|
}
|
|
768
|
+
const cachedBin = findCachedBunRunnerBin();
|
|
769
|
+
if (cachedBin) return { cmd: process.execPath, baseArgs: [cachedBin], shell: false };
|
|
754
770
|
const whichCmd = process.platform === 'win32' ? 'where' : 'which';
|
|
771
|
+
const bunR = spawnSync(whichCmd, ['bun'], { encoding: 'utf-8', shell: true });
|
|
772
|
+
if (bunR.status === 0 && bunR.stdout.trim()) {
|
|
773
|
+
return { cmd: 'bun', baseArgs: ['x', `${BROWSER_RUNNER_BIN}@latest`], shell: true };
|
|
774
|
+
}
|
|
755
775
|
const r = spawnSync(whichCmd, [BROWSER_RUNNER_BIN], { encoding: 'utf-8', shell: true });
|
|
756
776
|
if (r.status === 0 && r.stdout.trim()) {
|
|
757
777
|
const candidates = r.stdout.split(/\r?\n/).map(l => l.trim()).filter(Boolean);
|
|
758
778
|
const cmd = candidates.find(c => c.toLowerCase().endsWith('.cmd')) || candidates.find(c => !c.toLowerCase().endsWith('.ps1')) || candidates[0];
|
|
759
779
|
if (cmd) return { cmd, baseArgs: [], shell: process.platform === 'win32' };
|
|
760
780
|
}
|
|
761
|
-
const bunR = spawnSync(whichCmd, ['bun'], { encoding: 'utf-8', shell: true });
|
|
762
|
-
if (bunR.status === 0 && bunR.stdout.trim()) {
|
|
763
|
-
return { cmd: 'bun', baseArgs: ['x', `${BROWSER_RUNNER_BIN}@latest`], shell: true };
|
|
764
|
-
}
|
|
765
781
|
const npxR = spawnSync(whichCmd, ['npx'], { encoding: 'utf-8', shell: true });
|
|
766
782
|
if (npxR.status === 0 && npxR.stdout.trim()) {
|
|
767
783
|
return { cmd: 'npx', baseArgs: ['-y', BROWSER_RUNNER_BIN], shell: true };
|
|
@@ -827,8 +843,7 @@ function isProfileLocked(profileDir) {
|
|
|
827
843
|
}
|
|
828
844
|
|
|
829
845
|
function sessionProfileSlug(claudeSessionId) {
|
|
830
|
-
|
|
831
|
-
return s || 'default';
|
|
846
|
+
return 'default';
|
|
832
847
|
}
|
|
833
848
|
|
|
834
849
|
function sessionProfileDir(cwd, claudeSessionId) {
|
|
@@ -864,6 +879,7 @@ function cleanDeadProfileFragments(cwd) {
|
|
|
864
879
|
}
|
|
865
880
|
continue;
|
|
866
881
|
}
|
|
882
|
+
if (name === 'browser-profile-default') continue;
|
|
867
883
|
try {
|
|
868
884
|
if (fs.existsSync(dir) && !isProfileLocked(dir)) {
|
|
869
885
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
@@ -2116,8 +2132,25 @@ function nextTaskId(cwd) {
|
|
|
2116
2132
|
return `t${n}`;
|
|
2117
2133
|
}
|
|
2118
2134
|
|
|
2135
|
+
let _jsRuntimeCmd = null;
|
|
2136
|
+
function resolveJsRuntimeCmd() {
|
|
2137
|
+
if (_jsRuntimeCmd) return _jsRuntimeCmd;
|
|
2138
|
+
if (!/(^|[\\/])node(\.exe)?$/i.test(String(process.execPath || ''))) {
|
|
2139
|
+
_jsRuntimeCmd = process.execPath;
|
|
2140
|
+
return _jsRuntimeCmd;
|
|
2141
|
+
}
|
|
2142
|
+
try {
|
|
2143
|
+
const which = process.platform === 'win32' ? 'where' : 'which';
|
|
2144
|
+
const out = spawnSync(which, ['bun'], { encoding: 'utf-8', windowsHide: true });
|
|
2145
|
+
const first = (out && out.stdout || '').split(/\r?\n/).map((s) => s.trim()).filter(Boolean)[0];
|
|
2146
|
+
if (first) { _jsRuntimeCmd = first; return _jsRuntimeCmd; }
|
|
2147
|
+
} catch (_) {}
|
|
2148
|
+
_jsRuntimeCmd = process.execPath;
|
|
2149
|
+
return _jsRuntimeCmd;
|
|
2150
|
+
}
|
|
2151
|
+
|
|
2119
2152
|
function langToCmd(lang, code) {
|
|
2120
|
-
if (lang === 'nodejs' || lang === 'js' || lang === 'javascript' || lang === 'node') return { cmd:
|
|
2153
|
+
if (lang === 'nodejs' || lang === 'js' || lang === 'javascript' || lang === 'node') return { cmd: resolveJsRuntimeCmd(), args: ['-e', code], stdinCode: null };
|
|
2121
2154
|
if (lang === 'python' || lang === 'py') return { cmd: 'python', args: ['-c', code], stdinCode: null };
|
|
2122
2155
|
if (lang === 'bash' || lang === 'sh' || lang === 'shell' || lang === 'zsh') return { cmd: 'bash', args: ['-c', code], stdinCode: null };
|
|
2123
2156
|
if (lang === 'powershell' || lang === 'ps1') return { cmd: 'powershell', args: ['-NoProfile', '-NonInteractive', '-Command', code], stdinCode: null };
|
|
@@ -2125,10 +2158,15 @@ function langToCmd(lang, code) {
|
|
|
2125
2158
|
return null;
|
|
2126
2159
|
}
|
|
2127
2160
|
|
|
2161
|
+
const TASK_MAX_TIMEOUT_MS = 10 * 60 * 1000;
|
|
2162
|
+
|
|
2128
2163
|
function spawnTask({ cwd, lang, code, timeoutMs }) {
|
|
2129
2164
|
const id = nextTaskId(cwd);
|
|
2130
2165
|
const built = langToCmd(lang, code);
|
|
2131
2166
|
if (!built) return { ok: false, error: `unsupported lang: ${lang}` };
|
|
2167
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0 || timeoutMs > TASK_MAX_TIMEOUT_MS) {
|
|
2168
|
+
timeoutMs = TASK_MAX_TIMEOUT_MS;
|
|
2169
|
+
}
|
|
2132
2170
|
const outLog = taskOutPath(cwd, id, 'stdout');
|
|
2133
2171
|
const errLog = taskOutPath(cwd, id, 'stderr');
|
|
2134
2172
|
let outFd = null, errFd = null;
|
|
@@ -2261,6 +2299,43 @@ function killAllTasks(reason) {
|
|
|
2261
2299
|
return killed;
|
|
2262
2300
|
}
|
|
2263
2301
|
|
|
2302
|
+
function pidAliveLocal(pid) {
|
|
2303
|
+
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
2304
|
+
try { process.kill(pid, 0); return true; } catch (_) { return false; }
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
function sweepOrphanedTaskMetaOnBoot(cwd) {
|
|
2308
|
+
let swept = 0;
|
|
2309
|
+
try {
|
|
2310
|
+
const dir = tasksDir(cwd);
|
|
2311
|
+
const now = Date.now();
|
|
2312
|
+
for (const name of fs.readdirSync(dir)) {
|
|
2313
|
+
if (!/^t\d+\.json$/.test(name)) continue;
|
|
2314
|
+
const metaPath = path.join(dir, name);
|
|
2315
|
+
let meta = null;
|
|
2316
|
+
try { meta = JSON.parse(fs.readFileSync(metaPath, 'utf-8')); } catch (_) { continue; }
|
|
2317
|
+
if (!meta || meta.status !== 'running') continue;
|
|
2318
|
+
const stale = !pidAliveLocal(meta.pid) || (meta.deadline_ms && now > meta.deadline_ms);
|
|
2319
|
+
if (!stale) continue;
|
|
2320
|
+
if (pidAliveLocal(meta.pid)) {
|
|
2321
|
+
try {
|
|
2322
|
+
if (process.platform === 'win32') {
|
|
2323
|
+
spawnSync('taskkill', ['/F', '/T', '/PID', String(meta.pid)], { stdio: 'ignore', windowsHide: true, timeout: 3000 });
|
|
2324
|
+
} else {
|
|
2325
|
+
try { process.kill(-meta.pid, 'SIGKILL'); } catch (_) { try { process.kill(meta.pid, 'SIGKILL'); } catch (_) {} }
|
|
2326
|
+
}
|
|
2327
|
+
} catch (_) {}
|
|
2328
|
+
}
|
|
2329
|
+
meta.status = 'reaped-on-boot';
|
|
2330
|
+
meta.ended_ms = now;
|
|
2331
|
+
try { fs.writeFileSync(metaPath, JSON.stringify(meta, null, 2)); } catch (_) {}
|
|
2332
|
+
swept += 1;
|
|
2333
|
+
}
|
|
2334
|
+
} catch (_) {}
|
|
2335
|
+
if (swept > 0) logEvent('plugkit', 'task.bootSweepReaped', { cwd: cwd || process.cwd(), count: swept });
|
|
2336
|
+
return swept;
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2264
2339
|
function hostTaskProc(action, params) {
|
|
2265
2340
|
switch (action) {
|
|
2266
2341
|
case 'spawn': return spawnTask(params);
|
|
@@ -3394,6 +3469,8 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
3394
3469
|
process.exit(0);
|
|
3395
3470
|
}
|
|
3396
3471
|
|
|
3472
|
+
try { sweepOrphanedTaskMetaOnBoot(process.cwd()); } catch (_) {}
|
|
3473
|
+
|
|
3397
3474
|
setInterval(() => {
|
|
3398
3475
|
try { reapTimedOutTasks(); } catch (_) {}
|
|
3399
3476
|
}, 5000);
|
|
@@ -3744,7 +3821,7 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
3744
3821
|
}
|
|
3745
3822
|
}, 60_000);
|
|
3746
3823
|
|
|
3747
|
-
const BROWSER_IDLE_LIMIT_MS = parseInt(process.env.PLUGKIT_BROWSER_IDLE_LIMIT_MS, 10) ||
|
|
3824
|
+
const BROWSER_IDLE_LIMIT_MS = parseInt(process.env.PLUGKIT_BROWSER_IDLE_LIMIT_MS, 10) || 5 * 60 * 1000;
|
|
3748
3825
|
setInterval(() => {
|
|
3749
3826
|
try {
|
|
3750
3827
|
const portsFile = browserPortsFile(process.cwd());
|
package/gm-plugkit/supervisor.js
CHANGED
|
@@ -194,14 +194,13 @@ function spawnWatcher(bootReason) {
|
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
const isNodeExe = (p) => /(^|[\\/])node(\.exe)?$/i.test(String(p || ''));
|
|
197
|
-
const
|
|
197
|
+
const resolveRuntime = () => {
|
|
198
198
|
const candidates = [];
|
|
199
|
-
if (
|
|
200
|
-
|
|
201
|
-
if (process.env.GM_NODE_PATH) candidates.push(process.env.GM_NODE_PATH);
|
|
199
|
+
if (process.env.PLUGKIT_RUNTIME) candidates.push(process.env.PLUGKIT_RUNTIME);
|
|
200
|
+
candidates.push('bun');
|
|
202
201
|
try {
|
|
203
202
|
const which = process.platform === 'win32' ? 'where' : 'which';
|
|
204
|
-
const out = spawnSync(which, ['
|
|
203
|
+
const out = spawnSync(which, ['bun'], { encoding: 'utf8', windowsHide: true });
|
|
205
204
|
if (out && out.stdout) {
|
|
206
205
|
const first = out.stdout.split(/\r?\n/).map((s) => s.trim()).filter(Boolean)[0];
|
|
207
206
|
if (first) candidates.push(first);
|
|
@@ -210,9 +209,23 @@ function spawnWatcher(bootReason) {
|
|
|
210
209
|
for (const c of candidates) {
|
|
211
210
|
try { const r = spawnSync(c, ['--version'], { stdio: 'ignore', windowsHide: true }); if (r && r.status === 0) return c; } catch (_) {}
|
|
212
211
|
}
|
|
212
|
+
const nodeCandidates = [];
|
|
213
|
+
if (isNodeExe(process.execPath)) nodeCandidates.push(process.execPath);
|
|
214
|
+
if (process.env.GM_NODE_PATH) nodeCandidates.push(process.env.GM_NODE_PATH);
|
|
215
|
+
try {
|
|
216
|
+
const which = process.platform === 'win32' ? 'where' : 'which';
|
|
217
|
+
const out = spawnSync(which, ['node'], { encoding: 'utf8', windowsHide: true });
|
|
218
|
+
if (out && out.stdout) {
|
|
219
|
+
const first = out.stdout.split(/\r?\n/).map((s) => s.trim()).filter(Boolean)[0];
|
|
220
|
+
if (first) nodeCandidates.push(first);
|
|
221
|
+
}
|
|
222
|
+
} catch (_) {}
|
|
223
|
+
for (const c of nodeCandidates) {
|
|
224
|
+
try { const r = spawnSync(c, ['--version'], { stdio: 'ignore', windowsHide: true }); if (r && r.status === 0) return c; } catch (_) {}
|
|
225
|
+
}
|
|
213
226
|
return process.execPath;
|
|
214
227
|
};
|
|
215
|
-
let cmd =
|
|
228
|
+
let cmd = resolveRuntime();
|
|
216
229
|
let args = [wrapper, 'spool'];
|
|
217
230
|
|
|
218
231
|
let logFd = null;
|
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.1851",
|
|
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",
|