gm-plugkit 2.0.2467 → 2.0.2468

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.
Files changed (3) hide show
  1. package/bootstrap.js +91 -0
  2. package/cli.js +19 -4
  3. package/package.json +1 -1
package/bootstrap.js CHANGED
@@ -792,6 +792,95 @@ function getBinaryPath() {
792
792
  return getWasmPath();
793
793
  }
794
794
 
795
+ function agentplugRunnerAssetName() {
796
+ const plat = process.platform;
797
+ const arch = process.arch;
798
+ if (plat === 'win32') {
799
+ if (arch === 'x64') return 'agentplug-runner-windows-x64.exe';
800
+ if (arch === 'arm64') return 'agentplug-runner-windows-arm64.exe';
801
+ return null;
802
+ }
803
+ if (plat === 'darwin') {
804
+ if (arch === 'x64') return 'agentplug-runner-macos-x64';
805
+ if (arch === 'arm64') return 'agentplug-runner-macos-arm64';
806
+ return null;
807
+ }
808
+ if (plat === 'linux') {
809
+ if (arch === 'x64') return 'agentplug-runner-linux-x64';
810
+ if (arch === 'arm64') return 'agentplug-runner-linux-arm64';
811
+ return null;
812
+ }
813
+ return null;
814
+ }
815
+
816
+ function compareDottedSemverAscending(a, b) {
817
+ const pa = a.replace(/^v/, '').split('.').map(Number);
818
+ const pb = b.replace(/^v/, '').split('.').map(Number);
819
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
820
+ const d = (pa[i] || 0) - (pb[i] || 0);
821
+ if (d !== 0) return d;
822
+ }
823
+ return 0;
824
+ }
825
+
826
+ function latestAgentplugRunnerTagViaGitLsRemote() {
827
+ const { execFileSync } = require('child_process');
828
+ const out = execFileSync('git', ['ls-remote', '--tags', '--refs', 'https://github.com/AnEntrypoint/agentplug-bin.git'], {
829
+ encoding: 'utf8',
830
+ stdio: ['ignore', 'pipe', 'ignore'],
831
+ });
832
+ const tags = out.split('\n')
833
+ .map(line => line.match(/refs\/tags\/(.+)$/))
834
+ .filter(Boolean)
835
+ .map(m => m[1]);
836
+ if (tags.length === 0) return null;
837
+ tags.sort(compareDottedSemverAscending);
838
+ return tags[tags.length - 1];
839
+ }
840
+
841
+ // The sole loader has to exist before startSpoolDaemon can do anything --
842
+ // a project that never ran `gm-skill install` (or whose install predates
843
+ // agentplug-runner) hit a hard "not installed" failure here even though
844
+ // the sha256-verified download this needs is the same one bin/install.js
845
+ // already performs. Attempting it inline means a bare `spool` boot recovers
846
+ // on its own instead of requiring a separate manual install step first.
847
+ async function ensureAgentplugRunnerInstalled(destPath) {
848
+ const assetName = agentplugRunnerAssetName();
849
+ if (!assetName) return false;
850
+ const destDir = gmToolsDir();
851
+ try {
852
+ let tag;
853
+ try {
854
+ const releaseInfo = JSON.parse((await httpGetBuffer('https://api.github.com/repos/AnEntrypoint/agentplug-bin/releases/latest', 15000)).toString('utf8'));
855
+ tag = releaseInfo && releaseInfo.tag_name;
856
+ } catch (apiErr) {
857
+ try {
858
+ tag = latestAgentplugRunnerTagViaGitLsRemote();
859
+ } catch (_) {
860
+ return false;
861
+ }
862
+ }
863
+ if (!tag) return false;
864
+ const base = `https://github.com/AnEntrypoint/agentplug-bin/releases/download/${tag}`;
865
+ const [binBuf, shaBuf] = await Promise.all([
866
+ httpGetBuffer(`${base}/${assetName}`, 60000),
867
+ httpGetBuffer(`${base}/${assetName}.sha256`, 15000),
868
+ ]);
869
+ const expectedSha = shaBuf.toString('utf8').trim().split(/\s+/)[0];
870
+ const actualSha = sha256Hex(binBuf);
871
+ if (!expectedSha || actualSha.toLowerCase() !== expectedSha.toLowerCase()) return false;
872
+ fs.mkdirSync(destDir, { recursive: true });
873
+ const tmp = destPath + '.tmp' + process.pid;
874
+ fs.writeFileSync(tmp, binBuf);
875
+ if (process.platform !== 'win32') { try { fs.chmodSync(tmp, 0o755); } catch (_) {} }
876
+ fs.renameSync(tmp, destPath);
877
+ fs.writeFileSync(path.join(destDir, 'agentplug-runner.version'), tag);
878
+ return true;
879
+ } catch (_) {
880
+ return false;
881
+ }
882
+ }
883
+
795
884
  function startSpoolDaemon() {
796
885
  try {
797
886
  const runnerName = process.platform === 'win32' ? 'agentplug-runner.exe' : 'agentplug-runner';
@@ -805,6 +894,7 @@ function startSpoolDaemon() {
805
894
  `which downloads the sha256-verified native runner from AnEntrypoint/agentplug-bin for this platform ` +
806
895
  `(${process.platform}/${process.arch}). If no binary is published for this platform yet, there is no ` +
807
896
  `loader available -- file an issue at https://github.com/AnEntrypoint/agentplug-bin so a binary is built for it.`,
897
+ needsRunnerInstall: true,
808
898
  };
809
899
  }
810
900
  const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
@@ -846,6 +936,7 @@ module.exports = {
846
936
  getWasmPath,
847
937
  getBinaryPath,
848
938
  startSpoolDaemon,
939
+ ensureAgentplugRunnerInstalled,
849
940
  isReady,
850
941
  cacheRoot,
851
942
  obsEvent,
package/cli.js CHANGED
@@ -5,7 +5,7 @@ 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, isReady, getWasmPath, readPinnedGmPlugkitVersion, spawnPinnedBoot, resolveProjectRoot } = require('./bootstrap');
8
+ const { ensureReady, startSpoolDaemon, ensureAgentplugRunnerInstalled, gmToolsDir, readVersionFile, ensureGmPlugkitVersionFresh, ensureSkillMdFresh, isReady, getWasmPath, readPinnedGmPlugkitVersion, spawnPinnedBoot, resolveProjectRoot } = require('./bootstrap');
9
9
  const { pidAliveSync, waitForPidDeath } = require('./gm-process');
10
10
 
11
11
  function getWasmPathSafe() {
@@ -24,7 +24,7 @@ function spawnBackgroundFreshnessCheck(reason) {
24
24
  } catch (_) {}
25
25
  }
26
26
 
27
- function spawnDaemonOrExit(version, binaryPath, message) {
27
+ async function spawnDaemonOrExit(version, binaryPath, message) {
28
28
  let daemon;
29
29
  try {
30
30
  daemon = startSpoolDaemon();
@@ -33,6 +33,21 @@ function spawnDaemonOrExit(version, binaryPath, message) {
33
33
  console.error('Daemon start failed:', err.message);
34
34
  process.exit(1);
35
35
  }
36
+ if (daemon && !daemon.ok && daemon.needsRunnerInstall) {
37
+ const runnerName = process.platform === 'win32' ? 'agentplug-runner.exe' : 'agentplug-runner';
38
+ const runnerPath = path.join(gmToolsDir(), runnerName);
39
+ console.error('agentplug-runner not found -- attempting sha256-verified download from AnEntrypoint/agentplug-bin before failing...');
40
+ const installed = await ensureAgentplugRunnerInstalled(runnerPath);
41
+ if (installed) {
42
+ try {
43
+ daemon = startSpoolDaemon();
44
+ } catch (err) {
45
+ writeCliError('start-daemon', err);
46
+ console.error('Daemon start failed:', err.message);
47
+ process.exit(1);
48
+ }
49
+ }
50
+ }
36
51
  if (!daemon || !daemon.ok) {
37
52
  const errMsg = (daemon && daemon.error) || 'startSpoolDaemon returned non-ok';
38
53
  writeCliError('start-daemon', new Error(errMsg));
@@ -207,7 +222,7 @@ function tryDelegateToRunner(args) {
207
222
  try { installedVersion = readVersionFile(); } catch (_) { installedVersion = null; }
208
223
  writeCliStatus({ phase: 'bootstrapped', version: installedVersion, binary: getWasmPathSafe() });
209
224
  spawnBackgroundFreshnessCheck(versionDrifted ? 'version-drift-respawn' : 'fast-path-spawn');
210
- spawnDaemonOrExit(
225
+ await spawnDaemonOrExit(
211
226
  installedVersion,
212
227
  getWasmPathSafe(),
213
228
  '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'
@@ -231,7 +246,7 @@ function tryDelegateToRunner(args) {
231
246
  }
232
247
 
233
248
  writeCliStatus({ phase: 'bootstrapped', version: bootstrapResult.version, binary: bootstrapResult.binaryPath });
234
- spawnDaemonOrExit(
249
+ await spawnDaemonOrExit(
235
250
  bootstrapResult.version,
236
251
  bootstrapResult.binaryPath,
237
252
  'plugkit daemon spawned, not yet confirmed serving -- check .gm/exec-spool/.status.json for heartbeat freshness'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-plugkit",
3
- "version": "2.0.2467",
3
+ "version": "2.0.2468",
4
4
  "description": "Bootstrap and daemon-spawn tool for gm plugkit binary. Downloads the correct platform wasm, verifies SHA256, and launches agentplug-runner (the native wasm host) as the spool watcher daemon.",
5
5
  "main": "index.js",
6
6
  "bin": {