gm-skill 2.0.1849 → 2.0.1850

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.
@@ -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 (isNodeExe(process.env.GM_NODE_PATH)) candidates.push(process.env.GM_NODE_PATH);
1053
- if (isNodeExe(process.execPath)) candidates.push(process.execPath);
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, ['node'], { encoding: 'utf8', windowsHide: true });
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
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-plugkit",
3
- "version": "2.0.1849",
3
+ "version": "2.0.1850",
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
  });
@@ -745,23 +747,23 @@ function aggregateCpuProfile(profile, topN) {
745
747
  const BROWSER_RUNNER_BIN = process.env.GM_BROWSER_RUNNER_BIN || 'playwriter';
746
748
 
747
749
  function findBrowserRunner() {
748
- const npmR = spawnSync('npm', ['root', '-g'], { encoding: 'utf-8', shell: true });
749
- if (npmR.status === 0 && npmR.stdout.trim()) {
750
- const root = npmR.stdout.trim().split(/\r?\n/).pop();
751
- const binJs = path.join(root, BROWSER_RUNNER_BIN, 'bin.js');
750
+ const bunGlobalRoots = [
751
+ path.join(os.homedir(), '.bun', 'install', 'global', 'node_modules', BROWSER_RUNNER_BIN, 'bin.js'),
752
+ ];
753
+ for (const binJs of bunGlobalRoots) {
752
754
  if (fs.existsSync(binJs)) return { cmd: process.execPath, baseArgs: [binJs], shell: false };
753
755
  }
754
756
  const whichCmd = process.platform === 'win32' ? 'where' : 'which';
757
+ const bunR = spawnSync(whichCmd, ['bun'], { encoding: 'utf-8', shell: true });
758
+ if (bunR.status === 0 && bunR.stdout.trim()) {
759
+ return { cmd: 'bun', baseArgs: ['x', `${BROWSER_RUNNER_BIN}@latest`], shell: true };
760
+ }
755
761
  const r = spawnSync(whichCmd, [BROWSER_RUNNER_BIN], { encoding: 'utf-8', shell: true });
756
762
  if (r.status === 0 && r.stdout.trim()) {
757
763
  const candidates = r.stdout.split(/\r?\n/).map(l => l.trim()).filter(Boolean);
758
764
  const cmd = candidates.find(c => c.toLowerCase().endsWith('.cmd')) || candidates.find(c => !c.toLowerCase().endsWith('.ps1')) || candidates[0];
759
765
  if (cmd) return { cmd, baseArgs: [], shell: process.platform === 'win32' };
760
766
  }
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
767
  const npxR = spawnSync(whichCmd, ['npx'], { encoding: 'utf-8', shell: true });
766
768
  if (npxR.status === 0 && npxR.stdout.trim()) {
767
769
  return { cmd: 'npx', baseArgs: ['-y', BROWSER_RUNNER_BIN], shell: true };
@@ -2116,8 +2118,25 @@ function nextTaskId(cwd) {
2116
2118
  return `t${n}`;
2117
2119
  }
2118
2120
 
2121
+ let _jsRuntimeCmd = null;
2122
+ function resolveJsRuntimeCmd() {
2123
+ if (_jsRuntimeCmd) return _jsRuntimeCmd;
2124
+ if (!/(^|[\\/])node(\.exe)?$/i.test(String(process.execPath || ''))) {
2125
+ _jsRuntimeCmd = process.execPath;
2126
+ return _jsRuntimeCmd;
2127
+ }
2128
+ try {
2129
+ const which = process.platform === 'win32' ? 'where' : 'which';
2130
+ const out = spawnSync(which, ['bun'], { encoding: 'utf-8', windowsHide: true });
2131
+ const first = (out && out.stdout || '').split(/\r?\n/).map((s) => s.trim()).filter(Boolean)[0];
2132
+ if (first) { _jsRuntimeCmd = first; return _jsRuntimeCmd; }
2133
+ } catch (_) {}
2134
+ _jsRuntimeCmd = process.execPath;
2135
+ return _jsRuntimeCmd;
2136
+ }
2137
+
2119
2138
  function langToCmd(lang, code) {
2120
- if (lang === 'nodejs' || lang === 'js' || lang === 'javascript' || lang === 'node') return { cmd: process.execPath, args: ['-e', code], stdinCode: null };
2139
+ if (lang === 'nodejs' || lang === 'js' || lang === 'javascript' || lang === 'node') return { cmd: resolveJsRuntimeCmd(), args: ['-e', code], stdinCode: null };
2121
2140
  if (lang === 'python' || lang === 'py') return { cmd: 'python', args: ['-c', code], stdinCode: null };
2122
2141
  if (lang === 'bash' || lang === 'sh' || lang === 'shell' || lang === 'zsh') return { cmd: 'bash', args: ['-c', code], stdinCode: null };
2123
2142
  if (lang === 'powershell' || lang === 'ps1') return { cmd: 'powershell', args: ['-NoProfile', '-NonInteractive', '-Command', code], stdinCode: null };
@@ -2125,10 +2144,15 @@ function langToCmd(lang, code) {
2125
2144
  return null;
2126
2145
  }
2127
2146
 
2147
+ const TASK_MAX_TIMEOUT_MS = 10 * 60 * 1000;
2148
+
2128
2149
  function spawnTask({ cwd, lang, code, timeoutMs }) {
2129
2150
  const id = nextTaskId(cwd);
2130
2151
  const built = langToCmd(lang, code);
2131
2152
  if (!built) return { ok: false, error: `unsupported lang: ${lang}` };
2153
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0 || timeoutMs > TASK_MAX_TIMEOUT_MS) {
2154
+ timeoutMs = TASK_MAX_TIMEOUT_MS;
2155
+ }
2132
2156
  const outLog = taskOutPath(cwd, id, 'stdout');
2133
2157
  const errLog = taskOutPath(cwd, id, 'stderr');
2134
2158
  let outFd = null, errFd = null;
@@ -2261,6 +2285,43 @@ function killAllTasks(reason) {
2261
2285
  return killed;
2262
2286
  }
2263
2287
 
2288
+ function pidAliveLocal(pid) {
2289
+ if (!Number.isFinite(pid) || pid <= 0) return false;
2290
+ try { process.kill(pid, 0); return true; } catch (_) { return false; }
2291
+ }
2292
+
2293
+ function sweepOrphanedTaskMetaOnBoot(cwd) {
2294
+ let swept = 0;
2295
+ try {
2296
+ const dir = tasksDir(cwd);
2297
+ const now = Date.now();
2298
+ for (const name of fs.readdirSync(dir)) {
2299
+ if (!/^t\d+\.json$/.test(name)) continue;
2300
+ const metaPath = path.join(dir, name);
2301
+ let meta = null;
2302
+ try { meta = JSON.parse(fs.readFileSync(metaPath, 'utf-8')); } catch (_) { continue; }
2303
+ if (!meta || meta.status !== 'running') continue;
2304
+ const stale = !pidAliveLocal(meta.pid) || (meta.deadline_ms && now > meta.deadline_ms);
2305
+ if (!stale) continue;
2306
+ if (pidAliveLocal(meta.pid)) {
2307
+ try {
2308
+ if (process.platform === 'win32') {
2309
+ spawnSync('taskkill', ['/F', '/T', '/PID', String(meta.pid)], { stdio: 'ignore', windowsHide: true, timeout: 3000 });
2310
+ } else {
2311
+ try { process.kill(-meta.pid, 'SIGKILL'); } catch (_) { try { process.kill(meta.pid, 'SIGKILL'); } catch (_) {} }
2312
+ }
2313
+ } catch (_) {}
2314
+ }
2315
+ meta.status = 'reaped-on-boot';
2316
+ meta.ended_ms = now;
2317
+ try { fs.writeFileSync(metaPath, JSON.stringify(meta, null, 2)); } catch (_) {}
2318
+ swept += 1;
2319
+ }
2320
+ } catch (_) {}
2321
+ if (swept > 0) logEvent('plugkit', 'task.bootSweepReaped', { cwd: cwd || process.cwd(), count: swept });
2322
+ return swept;
2323
+ }
2324
+
2264
2325
  function hostTaskProc(action, params) {
2265
2326
  switch (action) {
2266
2327
  case 'spawn': return spawnTask(params);
@@ -3394,6 +3455,8 @@ async function runSpoolWatcher(instance, spoolDir) {
3394
3455
  process.exit(0);
3395
3456
  }
3396
3457
 
3458
+ try { sweepOrphanedTaskMetaOnBoot(process.cwd()); } catch (_) {}
3459
+
3397
3460
  setInterval(() => {
3398
3461
  try { reapTimedOutTasks(); } catch (_) {}
3399
3462
  }, 5000);
@@ -194,14 +194,13 @@ function spawnWatcher(bootReason) {
194
194
  }
195
195
 
196
196
  const isNodeExe = (p) => /(^|[\\/])node(\.exe)?$/i.test(String(p || ''));
197
- const resolveNode = () => {
197
+ const resolveRuntime = () => {
198
198
  const candidates = [];
199
- if (isNodeExe(process.env.PLUGKIT_RUNTIME)) candidates.push(process.env.PLUGKIT_RUNTIME);
200
- if (isNodeExe(process.execPath)) candidates.push(process.execPath);
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, ['node'], { encoding: 'utf8', windowsHide: true });
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 = resolveNode();
228
+ let cmd = resolveRuntime();
216
229
  let args = [wrapper, 'spool'];
217
230
 
218
231
  let logFd = null;
package/gm.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.1849",
3
+ "version": "2.0.1850",
4
4
  "description": "Spool-dispatch orchestration engine with unified state machine, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-skill",
3
- "version": "2.0.1849",
3
+ "version": "2.0.1850",
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",