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 +23 -184
- package/package.json +1 -1
- package/plugkit.version +1 -1
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 {
|
|
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
|
-
|
|
45
|
-
try { process.stderr.write(`[gm-plugkit] ${msg}\n`); } catch (_) {}
|
|
46
|
-
}
|
|
40
|
+
const log = core.makeLogger('gm-plugkit');
|
|
47
41
|
|
|
48
|
-
function
|
|
42
|
+
function copyWasmToGmTools(wasmPath, version) {
|
|
43
|
+
core.copyWasmToGmTools(wasmPath, version);
|
|
49
44
|
try {
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
|
58
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
395
|
-
if (cached) {
|
|
396
|
-
proactiveKillForNewInstall(currentVersion
|
|
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
|
-
|
|
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.
|
|
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.
|
|
1
|
+
0.1.1049
|