gm-skill 2.0.1607 → 2.0.1608

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.
@@ -10,6 +10,18 @@ const { pathToFileURL } = require('url');
10
10
  const ROOT = process.cwd();
11
11
  const WITNESS_DIR = path.join(ROOT, '.gm', 'witness');
12
12
 
13
+ function freePort() {
14
+ const net = require('net');
15
+ return new Promise((resolve, reject) => {
16
+ const srv = net.createServer();
17
+ srv.once('error', reject);
18
+ srv.listen(0, '127.0.0.1', () => {
19
+ const p = srv.address().port;
20
+ srv.close(() => resolve(p));
21
+ });
22
+ });
23
+ }
24
+
13
25
  function sleep(ms) { return new Promise((r) => setTimeout(r, ms)); }
14
26
  function rmrf(p) { try { fs.rmSync(p, { recursive: true, force: true }); } catch (_) {} }
15
27
  function write(file, text) { fs.mkdirSync(path.dirname(file), { recursive: true }); fs.writeFileSync(file, text); }
@@ -26,7 +38,7 @@ function which(cmds) {
26
38
  async function renderPreview() {
27
39
  const preview = fs.mkdtempSync(path.join(os.tmpdir(), 'gm-shell-preview-'));
28
40
  fs.mkdirSync(path.join(preview, 'vendor'), { recursive: true });
29
- cp.execSync(`powershell.exe -NoProfile -NonInteractive -Command "Copy-Item -Recurse -Force '${path.join(ROOT, 'site', 'vendor', '*')}' '${path.join(preview, 'vendor')}'"`, { stdio: 'ignore', windowsHide: true });
41
+ fs.cpSync(path.join(ROOT, 'site', 'vendor'), path.join(preview, 'vendor'), { recursive: true });
30
42
 
31
43
  const renderScript = `
32
44
  import { writeFileSync } from 'fs';
@@ -44,17 +56,18 @@ async function renderPreview() {
44
56
  }
45
57
  };
46
58
  const out = await mod.default.render(ctx);
47
- writeFileSync(resolve('${preview.replace(/\\/g, '\\\\')}', 'index.html'), out[0].html);
59
+ writeFileSync(resolve(process.env.GM_SHELL_PREVIEW, 'index.html'), out[0].html);
48
60
  `;
49
61
  const tmp = path.join(os.tmpdir(), `gm-shell-render-${Date.now()}.mjs`);
50
62
  fs.writeFileSync(tmp, renderScript);
51
- cp.execFileSync('node', [tmp], { stdio: 'inherit', windowsHide: true });
63
+ cp.execFileSync('node', [tmp], { stdio: 'inherit', windowsHide: true, env: { ...process.env, GM_SHELL_PREVIEW: preview } });
52
64
  try { fs.unlinkSync(tmp); } catch (_) {}
53
65
 
54
- const server = cp.spawn('python', ['-m', 'http.server', '4210', '--directory', preview], { cwd: ROOT, detached: true, stdio: 'ignore', windowsHide: true });
66
+ const port = await freePort();
67
+ const server = cp.spawn('python', ['-m', 'http.server', String(port), '--directory', preview], { cwd: ROOT, detached: true, stdio: 'ignore', windowsHide: true });
55
68
  server.unref();
56
69
  await sleep(1500);
57
- return { preview, port: 4210, serverPid: server.pid };
70
+ return { preview, port, serverPid: server.pid };
58
71
  }
59
72
 
60
73
  function killServer(pid) {
@@ -96,7 +109,7 @@ return JSON.stringify(result);
96
109
  const response = await fetch(relayUrl, {
97
110
  method: 'POST',
98
111
  headers: { 'Content-Type': 'application/json' },
99
- body: JSON.stringify({ sessionId: '9', code: script, timeout: 60000, cwd: ROOT }),
112
+ body: JSON.stringify({ sessionId: `gm-shell-${process.pid}-${Date.now()}`, code: script, timeout: 60000, cwd: ROOT }),
100
113
  });
101
114
  const result = await response.json();
102
115
  const out = result.text || '';
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-plugkit",
3
- "version": "2.0.1607",
3
+ "version": "2.0.1608",
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": {
@@ -1 +1 @@
1
- 0.1.683
1
+ 0.1.684
package/gm.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.1607",
3
+ "version": "2.0.1608",
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/lib/spool.js CHANGED
@@ -55,7 +55,13 @@ function writeSpool(body, lang = 'nodejs', options = {}) {
55
55
  fs.mkdirSync(inDir, { recursive: true });
56
56
 
57
57
  const sessionId = options.sessionId || process.env.CLAUDE_SESSION_ID;
58
- const code = sessionId ? `const SESSION_ID = ${JSON.stringify(sessionId)};\n${body}` : body;
58
+ let code = body;
59
+ if (sessionId) {
60
+ const prelude = validLang === 'bash'
61
+ ? `SESSION_ID=${JSON.stringify(String(sessionId))}\n`
62
+ : `const SESSION_ID = ${JSON.stringify(sessionId)};\n`;
63
+ code = prelude + body;
64
+ }
59
65
 
60
66
  fs.writeFileSync(inFile, code, 'utf8');
61
67
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-skill",
3
- "version": "2.0.1607",
3
+ "version": "2.0.1608",
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",
@@ -38,7 +38,18 @@
38
38
  "bin/plugkit.sha256",
39
39
  "bin/plugkit.version",
40
40
  "bin/plugkit.wasm.sha256",
41
- "gm-plugkit/",
41
+ "gm-plugkit/bootstrap.js",
42
+ "gm-plugkit/browser-idle.js",
43
+ "gm-plugkit/cli.js",
44
+ "gm-plugkit/index.js",
45
+ "gm-plugkit/lang-host-runner.js",
46
+ "gm-plugkit/package.json",
47
+ "gm-plugkit/plugkit-wasm-wrapper.js",
48
+ "gm-plugkit/supervisor.js",
49
+ "gm-plugkit/plugkit.version",
50
+ "gm-plugkit/plugkit.sha256",
51
+ "gm-plugkit/instructions/",
52
+ "gm-plugkit/scripts/",
42
53
  "AGENTS.md",
43
54
  "README.md",
44
55
  "gm.json"
@@ -1,46 +0,0 @@
1
- const assert = require('assert');
2
- const { selectIdleBrowserSessions } = require('./browser-idle.js');
3
-
4
- const NOW = 1_000_000;
5
- const LIMIT = 10 * 60 * 1000;
6
-
7
- (function onlyPastLimitSelected() {
8
- const ports = {
9
- active: { pid: 1, lastUse: NOW - 1000 },
10
- idle: { pid: 2, lastUse: NOW - (LIMIT + 5000) },
11
- };
12
- const idle = selectIdleBrowserSessions(ports, NOW, LIMIT);
13
- assert.strictEqual(idle.length, 1, 'exactly one idle session selected');
14
- assert.strictEqual(idle[0].sid, 'idle', 'the idle session is selected, active untouched');
15
- })();
16
-
17
- (function boundaryIsInclusive() {
18
- const ports = { edge: { pid: 1, lastUse: NOW - LIMIT } };
19
- const idle = selectIdleBrowserSessions(ports, NOW, LIMIT);
20
- assert.strictEqual(idle.length, 1, 'idleMs == limit closes (>=)');
21
- })();
22
-
23
- (function missingLastUseReapedAsStale() {
24
- const ports = { orphan: { pid: 1 } };
25
- const idle = selectIdleBrowserSessions(ports, NOW, LIMIT);
26
- assert.strictEqual(idle.length, 1, 'entry with no lastUse is treated as stale (epoch 0) and reaped');
27
- assert.strictEqual(idle[0].sid, 'orphan');
28
- })();
29
-
30
- (function concurrentIsolation() {
31
- const ports = {
32
- sessA: { pid: 1, lastUse: NOW - 2000 },
33
- sessB: { pid: 2, lastUse: NOW - (LIMIT + 1) },
34
- sessC: { pid: 3, lastUse: NOW - (LIMIT + 999999) },
35
- };
36
- const idle = selectIdleBrowserSessions(ports, NOW, LIMIT).map(x => x.sid).sort();
37
- assert.deepStrictEqual(idle, ['sessB', 'sessC'], 'only the idle sessions, active sessA preserved');
38
- })();
39
-
40
- (function emptyAndMalformed() {
41
- assert.deepStrictEqual(selectIdleBrowserSessions({}, NOW, LIMIT), [], 'empty ports');
42
- assert.deepStrictEqual(selectIdleBrowserSessions(null, NOW, LIMIT), [], 'null ports');
43
- assert.deepStrictEqual(selectIdleBrowserSessions({ bad: null, str: 'x' }, NOW, LIMIT), [], 'malformed entries skipped');
44
- })();
45
-
46
- console.log('browser-idle.test.js: all assertions passed');
@@ -1,25 +0,0 @@
1
- const assert = require('assert');
2
- const fs = require('fs');
3
- const path = require('path');
4
-
5
- const wrapper = fs.readFileSync(path.join(__dirname, 'plugkit-wasm-wrapper.js'), 'utf-8');
6
-
7
- const aliasInChildScript = /\bspawnSync\s*\(\s*process\.execPath\s*,\s*\[\s*['"]-e['"]\s*,\s*`[^`]*\b_(?:net|http|https|crypto|childProcess)Module\b[^`]*`/;
8
-
9
- (function noParentAliasInChildEvalTemplates() {
10
- assert.strictEqual(
11
- aliasInChildScript.test(wrapper),
12
- false,
13
- 'no spawnSync(process.execPath, ["-e", `...`]) child-script template may reference a parent-scope _*Module alias; the spawned child has no such binding (use require() inside the template). This regression broke findFreePortSync/isPort*/fetchJsonSync and surfaced only as "could not allocate free port" at browser-spawn time.'
14
- );
15
- })();
16
-
17
- (function childTemplatesUseRequire() {
18
- const childEvalBlocks = wrapper.match(/spawnSync\s*\(\s*process\.execPath\s*,\s*\[\s*['"]-e['"]\s*,\s*`[^`]*`/g) || [];
19
- for (const block of childEvalBlocks) {
20
- if (/\brequire\s*\(\s*['"](?:net|http|https)['"]\s*\)/.test(block) || !/\b(?:net|http|https)\b/.test(block)) continue;
21
- assert.fail(`child -e template uses a node builtin without require(): ${block.slice(0, 80)}...`);
22
- }
23
- })();
24
-
25
- console.log('child-script-alias.test.js: all assertions passed');