gm-plugkit 2.0.2208 → 2.0.2209

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/bootstrap.js CHANGED
@@ -6,8 +6,9 @@ const path = require('path');
6
6
  const os = require('os');
7
7
  const crypto = require('crypto');
8
8
  const { spawn, spawnSync } = require('child_process');
9
- const { pidAlive, sha256OfFile, sha256OfFileSync } = require('./gm-process');
9
+ const { sha256OfFile, sha256OfFileSync } = require('./gm-process');
10
10
  const shared = require('./bootstrap-shared');
11
+ const core = require('./bootstrap-wasm-core');
11
12
  const {
12
13
  obsEvent,
13
14
  cacheRoot,
@@ -16,49 +17,40 @@ const {
16
17
  ensureDir,
17
18
  acquireLock,
18
19
  releaseLock,
19
- isLockStale,
20
20
  pruneOldVersions,
21
21
  healIfShaMatches,
22
22
  daemonVersionSentinel,
23
23
  readDaemonVersion,
24
24
  writeDaemonVersion,
25
- pidCommandLineForKillGuard,
26
- pidIsPlugkitProcess,
27
- writeKillAttribution,
28
- killPid,
29
25
  killSpoolWatcherInCwd,
30
26
  proactiveKillForNewInstall,
31
27
  ensureNextStepWiring: ensureNextStepWiringShared,
32
- resolveWindowsExe,
33
- resolveNpmCliJs,
34
28
  } = shared;
29
+ const {
30
+ resolveProjectRoot,
31
+ writeBootstrapError,
32
+ clearBootstrapError,
33
+ resolveInstalledWasmPath,
34
+ } = core;
35
35
 
36
- const NPM_PACKAGE = 'plugkit-wasm';
37
- const ATTEMPT_TIMEOUT_MS = 10 * 60 * 1000;
38
- const MAX_ATTEMPTS = 3;
39
- const BACKOFF_MS = [5000, 15000];
40
36
  const LOCK_STALE_MS = 30 * 60 * 1000;
41
37
 
42
38
  const wrapperDir = __dirname;
43
39
 
44
- function log(msg) {
45
- try { process.stderr.write(`[gm-plugkit] ${msg}\n`); } catch (_) {}
46
- }
40
+ const log = core.makeLogger('gm-plugkit');
47
41
 
48
- function writeBootstrapError(spec) {
42
+ function copyWasmToGmTools(wasmPath, version) {
43
+ core.copyWasmToGmTools(wasmPath, version);
49
44
  try {
50
- const projectDir = resolveProjectRoot(process.env.CLAUDE_PROJECT_DIR || process.cwd());
51
- const spoolDir = path.join(projectDir, '.gm', 'exec-spool');
52
- fs.mkdirSync(spoolDir, { recursive: true });
53
- fs.writeFileSync(path.join(spoolDir, '.bootstrap-error.json'), JSON.stringify({ ts: new Date().toISOString(), ...spec }, null, 2));
45
+ const ownPkg = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf-8'));
46
+ if (ownPkg && ownPkg.version) {
47
+ fs.writeFileSync(path.join(gmToolsDir(), 'gm-plugkit.version'), ownPkg.version);
48
+ }
54
49
  } catch (_) {}
55
50
  }
56
51
 
57
- function clearBootstrapError() {
58
- try {
59
- const projectDir = resolveProjectRoot(process.env.CLAUDE_PROJECT_DIR || process.cwd());
60
- fs.unlinkSync(path.join(projectDir, '.gm', 'exec-spool', '.bootstrap-error.json'));
61
- } catch (_) {}
52
+ function extractNpmPackageWithRetry(destPath, version) {
53
+ return core.extractNpmPackageWithRetry(destPath, version, { log });
62
54
  }
63
55
 
64
56
  function sha256Hex(buf) {
@@ -179,45 +171,12 @@ function hasNativeEmbedRunner() {
179
171
  return names.some(n => { try { return fs.existsSync(path.join(dir, n)); } catch (_) { return false; } });
180
172
  }
181
173
 
182
- function resolveProjectRoot(start) {
183
- const resolved = path.resolve(start);
184
- try {
185
- const r = spawnSync('git', ['rev-parse', '--git-common-dir'], { cwd: resolved, encoding: 'utf-8', windowsHide: true, timeout: 1500 });
186
- if (r.status === 0 && r.stdout && r.stdout.trim()) {
187
- let commonDir = r.stdout.trim();
188
- if (!path.isAbsolute(commonDir)) commonDir = path.resolve(resolved, commonDir);
189
- if (/(^|[\\/])\.git$/.test(commonDir)) return path.dirname(commonDir);
190
- }
191
- } catch (_) {}
192
- return resolved;
193
- }
194
-
195
174
  function readVersionFile() {
196
- const p = path.join(wrapperDir, 'plugkit.version');
197
- if (!fs.existsSync(p)) throw new Error(`plugkit.version not found at ${p}`);
198
- return fs.readFileSync(p, 'utf8').trim();
175
+ return core.readVersionFile(wrapperDir);
199
176
  }
200
177
 
201
178
  function readShaManifest() {
202
- const p = path.join(wrapperDir, 'plugkit.sha256');
203
- if (!fs.existsSync(p)) return null;
204
- const raw = fs.readFileSync(p, 'utf8');
205
- try {
206
- const parsed = JSON.parse(raw);
207
- if (parsed && typeof parsed === 'object') {
208
- const out = {};
209
- for (const [name, sha] of Object.entries(parsed)) {
210
- if (typeof sha === 'string') out[name] = sha.toLowerCase();
211
- }
212
- return out;
213
- }
214
- } catch (_) {}
215
- const out = {};
216
- for (const line of raw.split(/\r?\n/)) {
217
- const m = line.match(/^([0-9a-f]{64})\s+(\S+)\s*$/i);
218
- if (m) out[m[2]] = m[1].toLowerCase();
219
- }
220
- return out;
179
+ return core.readShaManifest(wrapperDir);
221
180
  }
222
181
 
223
182
  async function fetchRemoteSha(version, artifactName) {
@@ -231,54 +190,6 @@ async function fetchRemoteSha(version, artifactName) {
231
190
  }
232
191
  }
233
192
 
234
- async function extractNpmPackageWasm(destPath, version) {
235
- const tempDir = path.join(path.dirname(destPath), '.npm-extract-' + Date.now());
236
- try {
237
- ensureDir(tempDir);
238
- const startMs = Date.now();
239
- log(`extracting npm package ${NPM_PACKAGE}@${version} to ${tempDir}`);
240
- obsEvent('bootstrap', 'npm.extract.start', { package: NPM_PACKAGE, version });
241
-
242
- fs.writeFileSync(path.join(tempDir, 'package.json'), JSON.stringify({ name: 'plugkit-extract', version: '0.0.0', private: true }));
243
-
244
- const cmd = resolveWindowsExe('npm');
245
- const installArgs = ['install', '--no-audit', '--no-fund', '--no-save', NPM_PACKAGE + '@' + version];
246
- const isCmdShim = process.platform === 'win32' && /\.(cmd|bat)$/i.test(cmd);
247
- const npmCliJs = isCmdShim ? resolveNpmCliJs(cmd) : null;
248
-
249
- const spawnCmd = npmCliJs ? process.execPath : (isCmdShim ? `"${cmd}"` : cmd);
250
- const rawArgs = npmCliJs ? [npmCliJs, ...installArgs] : installArgs;
251
- const spawnArgs = (isCmdShim && !npmCliJs) ? rawArgs.map(a => /[\s"]/.test(a) ? `"${a.replace(/"/g, '\\"')}"` : a) : rawArgs;
252
-
253
- const result = spawnSync(spawnCmd, spawnArgs, {
254
- cwd: tempDir,
255
- stdio: ['ignore', 'pipe', 'pipe'],
256
- timeout: ATTEMPT_TIMEOUT_MS,
257
- encoding: 'utf8',
258
- windowsHide: true,
259
- ...((isCmdShim && !npmCliJs) ? { shell: true } : {}),
260
- });
261
-
262
- if (result.error) throw result.error;
263
- if (result.status !== 0) {
264
- const detail = (result.stderr || result.stdout || '').trim().split(/\r?\n/).slice(-5).join(' | ');
265
- const sig = result.signal ? ` signal=${result.signal}` : '';
266
- throw new Error(`npm install failed status=${result.status}${sig}: ${detail || 'no stderr/stdout captured'}`);
267
- }
268
-
269
- const nodeModulesPath = path.join(tempDir, 'node_modules', NPM_PACKAGE, 'plugkit.wasm');
270
- if (!fs.existsSync(nodeModulesPath)) {
271
- throw new Error(`plugkit.wasm not found in extracted npm package at ${nodeModulesPath}`);
272
- }
273
-
274
- fs.copyFileSync(nodeModulesPath, destPath);
275
- log(`extracted ${nodeModulesPath} -> ${destPath}`);
276
- obsEvent('bootstrap', 'npm.extract.end', { dur_ms: Date.now() - startMs, ok: true });
277
- } finally {
278
- try { fs.rmSync(tempDir, { recursive: true, force: true, maxRetries: 1, retryDelay: 50 }); } catch (_) {}
279
- }
280
- }
281
-
282
193
  function httpGetBuffer(url, timeoutMs) {
283
194
  const https = require('https');
284
195
  const idleTimeoutMs = timeoutMs || 30000;
@@ -355,35 +266,6 @@ async function downloadFromGithubReleases(destPath, version, artifactName) {
355
266
  log(`gh-releases wrote ${buf.length} bytes to ${destPath} (artifact=${name})`);
356
267
  }
357
268
 
358
- async function extractNpmPackageWithRetry(destPath, version) {
359
- let lastErr;
360
- for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
361
- try {
362
- log(`npm extract attempt ${attempt}/${MAX_ATTEMPTS}: ${NPM_PACKAGE}@${version}`);
363
- await extractNpmPackageWasm(destPath, version);
364
- return;
365
- } catch (err) {
366
- lastErr = err;
367
- log(`attempt ${attempt} failed: ${err.message}`);
368
- obsEvent('bootstrap', 'npm.extract.attempt_failed', { package: NPM_PACKAGE, attempt, max: MAX_ATTEMPTS, err: String(err.message || err) });
369
- if (err && (err.code === 'ENOENT' || /ENOENT/.test(String(err.message || '')))) {
370
- log(`npm binary unresolvable (ENOENT); skipping retries, falling back`);
371
- throw err;
372
- }
373
- if (err && (err.code === 'EINVAL' || /EINVAL/.test(String(err.message || '')))) {
374
- log(`spawn EINVAL on npm shim; skipping retries, falling back`);
375
- throw err;
376
- }
377
- if (attempt < MAX_ATTEMPTS) {
378
- const wait = BACKOFF_MS[attempt - 1] || 120000;
379
- log(`backing off ${wait}ms`);
380
- await new Promise(r => setTimeout(r, wait));
381
- }
382
- }
383
- }
384
- throw lastErr;
385
- }
386
-
387
269
  function killStaleDaemonIfVersionChanged() {
388
270
  let currentVersion;
389
271
  try { currentVersion = readVersionFile(); }
@@ -391,9 +273,9 @@ function killStaleDaemonIfVersionChanged() {
391
273
  obsEvent('bootstrap', 'kill-stale-daemon.version-read-failed', { error: e.message });
392
274
  return;
393
275
  }
394
- const cached = resolveCachedBinary({ version: currentVersion });
395
- if (cached) {
396
- proactiveKillForNewInstall(currentVersion, cached);
276
+ const cached = resolveInstalledWasmPath();
277
+ if (cached && fs.existsSync(cached)) {
278
+ proactiveKillForNewInstall(currentVersion);
397
279
  return;
398
280
  }
399
281
  const recorded = readDaemonVersion();
@@ -553,51 +435,8 @@ async function bootstrap(opts) {
553
435
  }
554
436
  }
555
437
 
556
- function copyWasmToGmTools(wasmPath, version) {
557
- const dst = gmToolsDir();
558
- fs.mkdirSync(dst, { recursive: true });
559
- const target = path.join(dst, 'plugkit.wasm');
560
-
561
- let wasmFresh = false;
562
- if (fs.existsSync(target)) {
563
- try {
564
- const cur = sha256OfFileSync(target);
565
- const src = sha256OfFileSync(wasmPath);
566
- if (cur === src) wasmFresh = true;
567
- } catch (_) {}
568
- }
569
- if (!wasmFresh) {
570
- const tmp = `${target}.partial-${process.pid}`;
571
- fs.copyFileSync(wasmPath, tmp);
572
- try { fs.renameSync(tmp, target); }
573
- catch (err) {
574
- if (err.code === 'EEXIST' || err.code === 'EPERM') {
575
- try { fs.unlinkSync(target); } catch (_) {}
576
- fs.renameSync(tmp, target);
577
- } else {
578
- try { fs.unlinkSync(tmp); } catch (_) {}
579
- throw err;
580
- }
581
- }
582
- }
583
- fs.writeFileSync(path.join(dst, 'plugkit.version'), version);
584
-
585
- try {
586
- const ownPkg = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf-8'));
587
- if (ownPkg && ownPkg.version) {
588
- fs.writeFileSync(path.join(dst, 'gm-plugkit.version'), ownPkg.version);
589
- }
590
- } catch (_) {}
591
-
592
- }
593
-
594
438
  function getWasmPath() {
595
- const home = process.env.USERPROFILE || process.env.HOME || os.homedir();
596
- const primary = path.join(home, '.gm-tools', 'plugkit.wasm');
597
- const fallback = path.join(home, '.claude', 'gm-tools', 'plugkit.wasm');
598
- if (fs.existsSync(primary)) return primary;
599
- if (fs.existsSync(fallback)) return fallback;
600
- return primary;
439
+ return resolveInstalledWasmPath();
601
440
  }
602
441
 
603
442
  function isReady() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-plugkit",
3
- "version": "2.0.2208",
3
+ "version": "2.0.2209",
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": {
package/plugkit.version CHANGED
@@ -1 +1 @@
1
- 0.1.1048
1
+ 0.1.1049