gm-codex 2.0.948 → 2.0.949
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/.codex-plugin/plugin.json +1 -1
- package/bin/bootstrap.js +94 -26
- package/gm.json +1 -1
- package/package.json +1 -1
- package/plugin.json +1 -1
package/bin/bootstrap.js
CHANGED
|
@@ -88,6 +88,43 @@ function readVersionFile(wrapperDir) {
|
|
|
88
88
|
return fs.readFileSync(p, 'utf8').trim();
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
function readRtkVersion(wrapperDir) {
|
|
92
|
+
const p = path.join(wrapperDir, 'rtk.version');
|
|
93
|
+
if (!fs.existsSync(p)) return null;
|
|
94
|
+
const v = fs.readFileSync(p, 'utf8').trim();
|
|
95
|
+
return v || null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function sha256OfFileSync(filePath) {
|
|
99
|
+
const h = crypto.createHash('sha256');
|
|
100
|
+
const fd = fs.openSync(filePath, 'r');
|
|
101
|
+
try {
|
|
102
|
+
const buf = Buffer.alloc(1024 * 1024);
|
|
103
|
+
for (;;) {
|
|
104
|
+
const n = fs.readSync(fd, buf, 0, buf.length, null);
|
|
105
|
+
if (n <= 0) break;
|
|
106
|
+
h.update(buf.subarray(0, n));
|
|
107
|
+
}
|
|
108
|
+
} finally { try { fs.closeSync(fd); } catch (_) {} }
|
|
109
|
+
return h.digest('hex');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function healIfShaMatches(binPath, expectedSha, sentinelPath, partialPath, kind) {
|
|
113
|
+
if (!fs.existsSync(binPath)) return false;
|
|
114
|
+
if (partialPath) { try { if (fs.existsSync(partialPath)) fs.unlinkSync(partialPath); } catch (_) {} }
|
|
115
|
+
if (!expectedSha) return false;
|
|
116
|
+
let got;
|
|
117
|
+
try { got = sha256OfFileSync(binPath); }
|
|
118
|
+
catch (_) { return false; }
|
|
119
|
+
if (got !== expectedSha) {
|
|
120
|
+
try { fs.unlinkSync(binPath); } catch (_) {}
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
try { fs.writeFileSync(sentinelPath, new Date().toISOString()); } catch (_) { return false; }
|
|
124
|
+
obsEvent('bootstrap', 'cache.heal', { path: binPath, kind });
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
|
|
91
128
|
function readShaManifest(wrapperDir, manifestName) {
|
|
92
129
|
const p = path.join(wrapperDir, manifestName || 'plugkit.sha256');
|
|
93
130
|
if (!fs.existsSync(p)) return null;
|
|
@@ -248,12 +285,15 @@ function isLockStale(lockPath) {
|
|
|
248
285
|
return false;
|
|
249
286
|
}
|
|
250
287
|
|
|
251
|
-
function pruneOldVersions(root, keepVersion) {
|
|
288
|
+
function pruneOldVersions(root, keepVersion, keepRtkVersion) {
|
|
252
289
|
try {
|
|
253
290
|
const entries = fs.readdirSync(root);
|
|
254
291
|
for (const e of entries) {
|
|
255
|
-
|
|
256
|
-
|
|
292
|
+
const isPlugkit = e.startsWith('v') && !e.startsWith('rtk-');
|
|
293
|
+
const isRtk = e.startsWith('rtk-v');
|
|
294
|
+
if (!isPlugkit && !isRtk) continue;
|
|
295
|
+
if (isPlugkit && e === `v${keepVersion}`) continue;
|
|
296
|
+
if (isRtk && keepRtkVersion && e === `rtk-v${keepRtkVersion}`) continue;
|
|
257
297
|
const dir = path.join(root, e);
|
|
258
298
|
const lock = path.join(dir, '.lock');
|
|
259
299
|
if (fs.existsSync(lock) && !isLockStale(lock)) continue;
|
|
@@ -283,10 +323,19 @@ async function bootstrap(opts) {
|
|
|
283
323
|
|
|
284
324
|
const finalPath = path.join(verDir, binName);
|
|
285
325
|
const okSentinel = path.join(verDir, '.ok');
|
|
326
|
+
const partialPath = `${finalPath}.partial`;
|
|
286
327
|
|
|
287
328
|
if (fs.existsSync(finalPath) && fs.existsSync(okSentinel)) {
|
|
288
329
|
if (!opts.silent) log(`cache hit: ${finalPath}`);
|
|
289
|
-
pruneOldVersions(root, version);
|
|
330
|
+
pruneOldVersions(root, version, readRtkVersion(wrapperDir));
|
|
331
|
+
return finalPath;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (healIfShaMatches(finalPath, expectedSha, okSentinel, partialPath, 'plugkit')) {
|
|
335
|
+
if (!opts.silent) log(`cache heal (sha match): ${finalPath}`);
|
|
336
|
+
pruneOldVersions(root, version, readRtkVersion(wrapperDir));
|
|
337
|
+
try { await bootstrapRtk(verDir, version, wrapperDir, opts.silent, root); }
|
|
338
|
+
catch (err) { log(`rtk fetch skipped: ${err.message}`); }
|
|
290
339
|
return finalPath;
|
|
291
340
|
}
|
|
292
341
|
|
|
@@ -294,27 +343,33 @@ async function bootstrap(opts) {
|
|
|
294
343
|
acquireLock(lockPath);
|
|
295
344
|
try {
|
|
296
345
|
if (fs.existsSync(finalPath) && fs.existsSync(okSentinel)) {
|
|
297
|
-
pruneOldVersions(root, version);
|
|
346
|
+
pruneOldVersions(root, version, readRtkVersion(wrapperDir));
|
|
347
|
+
return finalPath;
|
|
348
|
+
}
|
|
349
|
+
if (healIfShaMatches(finalPath, expectedSha, okSentinel, partialPath, 'plugkit')) {
|
|
350
|
+
log(`cache heal (sha match) under lock: ${finalPath}`);
|
|
351
|
+
pruneOldVersions(root, version, readRtkVersion(wrapperDir));
|
|
352
|
+
try { await bootstrapRtk(verDir, version, wrapperDir, opts.silent, root); }
|
|
353
|
+
catch (err) { log(`rtk fetch skipped: ${err.message}`); }
|
|
298
354
|
return finalPath;
|
|
299
355
|
}
|
|
300
356
|
|
|
301
|
-
|
|
302
|
-
if (fs.existsSync(tmpPath)) {
|
|
357
|
+
if (fs.existsSync(partialPath)) {
|
|
303
358
|
try {
|
|
304
|
-
const st = fs.statSync(
|
|
359
|
+
const st = fs.statSync(partialPath);
|
|
305
360
|
if (Date.now() - st.mtimeMs > LOCK_STALE_MS) {
|
|
306
|
-
fs.unlinkSync(
|
|
307
|
-
log(`cleared stale partial: ${
|
|
361
|
+
fs.unlinkSync(partialPath);
|
|
362
|
+
log(`cleared stale partial: ${partialPath}`);
|
|
308
363
|
}
|
|
309
364
|
} catch (_) {}
|
|
310
365
|
}
|
|
311
366
|
const url = `https://github.com/${RELEASE_REPO}/releases/download/v${version}/${binName}`;
|
|
312
|
-
await downloadWithRetry(url,
|
|
367
|
+
await downloadWithRetry(url, partialPath);
|
|
313
368
|
|
|
314
369
|
if (expectedSha) {
|
|
315
|
-
const got = await sha256OfFile(
|
|
370
|
+
const got = await sha256OfFile(partialPath);
|
|
316
371
|
if (got !== expectedSha) {
|
|
317
|
-
try { fs.unlinkSync(
|
|
372
|
+
try { fs.unlinkSync(partialPath); } catch (_) {}
|
|
318
373
|
throw new Error(`sha256 mismatch for ${binName}: expected ${expectedSha}, got ${got}`);
|
|
319
374
|
}
|
|
320
375
|
log('sha256 verified');
|
|
@@ -322,11 +377,11 @@ async function bootstrap(opts) {
|
|
|
322
377
|
log('no sha256 manifest — skipping verify');
|
|
323
378
|
}
|
|
324
379
|
|
|
325
|
-
try { fs.renameSync(
|
|
380
|
+
try { fs.renameSync(partialPath, finalPath); }
|
|
326
381
|
catch (err) {
|
|
327
382
|
if (err.code === 'EEXIST' || err.code === 'EPERM') {
|
|
328
383
|
try { fs.unlinkSync(finalPath); } catch (_) {}
|
|
329
|
-
fs.renameSync(
|
|
384
|
+
fs.renameSync(partialPath, finalPath);
|
|
330
385
|
} else throw err;
|
|
331
386
|
}
|
|
332
387
|
|
|
@@ -337,10 +392,9 @@ async function bootstrap(opts) {
|
|
|
337
392
|
fs.writeFileSync(okSentinel, new Date().toISOString());
|
|
338
393
|
log(`installed ${finalPath}`);
|
|
339
394
|
obsEvent('bootstrap', 'install.done', { path: finalPath, version, kind: 'plugkit' });
|
|
340
|
-
pruneOldVersions(root, version);
|
|
395
|
+
pruneOldVersions(root, version, readRtkVersion(wrapperDir));
|
|
341
396
|
proactiveKillForNewInstall(version);
|
|
342
|
-
|
|
343
|
-
try { await bootstrapRtk(verDir, version, wrapperDir, opts.silent); }
|
|
397
|
+
try { await bootstrapRtk(verDir, version, wrapperDir, opts.silent, root); }
|
|
344
398
|
catch (err) { log(`rtk fetch skipped: ${err.message}`); }
|
|
345
399
|
return finalPath;
|
|
346
400
|
} finally {
|
|
@@ -348,10 +402,19 @@ async function bootstrap(opts) {
|
|
|
348
402
|
}
|
|
349
403
|
}
|
|
350
404
|
|
|
351
|
-
|
|
405
|
+
function rtkCacheDir(root, wrapperDir, plugkitVerDir) {
|
|
406
|
+
const rtkVer = readRtkVersion(wrapperDir);
|
|
407
|
+
if (!rtkVer) return plugkitVerDir;
|
|
408
|
+
const dir = path.join(root, `rtk-v${rtkVer}`);
|
|
409
|
+
ensureDir(dir);
|
|
410
|
+
return dir;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
async function bootstrapRtk(plugkitVerDir, plugkitVersion, wrapperDir, silent, root) {
|
|
352
414
|
const rtkName = rtkBinaryName();
|
|
353
|
-
const
|
|
354
|
-
const
|
|
415
|
+
const cacheDir = rtkCacheDir(root || cacheRoot(), wrapperDir, plugkitVerDir);
|
|
416
|
+
const rtkPath = path.join(cacheDir, rtkName);
|
|
417
|
+
const rtkOk = path.join(cacheDir, '.rtk-ok');
|
|
355
418
|
if (fs.existsSync(rtkPath) && fs.existsSync(rtkOk)) {
|
|
356
419
|
if (!silent) log(`rtk cache hit: ${rtkPath}`);
|
|
357
420
|
return rtkPath;
|
|
@@ -359,7 +422,11 @@ async function bootstrapRtk(verDir, version, wrapperDir, silent) {
|
|
|
359
422
|
const rtkSha = readShaManifest(wrapperDir, 'rtk.sha256');
|
|
360
423
|
const expected = rtkSha ? rtkSha[rtkName] : null;
|
|
361
424
|
const tmp = `${rtkPath}.partial`;
|
|
362
|
-
|
|
425
|
+
if (healIfShaMatches(rtkPath, expected, rtkOk, tmp, 'rtk')) {
|
|
426
|
+
if (!silent) log(`rtk cache heal (sha match): ${rtkPath}`);
|
|
427
|
+
return rtkPath;
|
|
428
|
+
}
|
|
429
|
+
const url = `https://github.com/${RELEASE_REPO}/releases/download/v${plugkitVersion}/${rtkName}`;
|
|
363
430
|
await downloadWithRetry(url, tmp);
|
|
364
431
|
if (expected) {
|
|
365
432
|
const got = await sha256OfFile(tmp);
|
|
@@ -378,7 +445,7 @@ async function bootstrapRtk(verDir, version, wrapperDir, silent) {
|
|
|
378
445
|
if (os.platform() !== 'win32') { try { fs.chmodSync(rtkPath, 0o755); } catch (_) {} }
|
|
379
446
|
fs.writeFileSync(rtkOk, new Date().toISOString());
|
|
380
447
|
log(`installed ${rtkPath}`);
|
|
381
|
-
obsEvent('bootstrap', 'install.done', { path: rtkPath,
|
|
448
|
+
obsEvent('bootstrap', 'install.done', { path: rtkPath, plugkit_version: plugkitVersion, rtk_version: readRtkVersion(wrapperDir) || plugkitVersion, kind: 'rtk' });
|
|
382
449
|
return rtkPath;
|
|
383
450
|
}
|
|
384
451
|
|
|
@@ -390,9 +457,10 @@ function resolveCachedRtk(opts) {
|
|
|
390
457
|
try { const r = cacheRoot(); ensureDir(r); return r; }
|
|
391
458
|
catch (_) { const r = fallbackCacheRoot(); ensureDir(r); return r; }
|
|
392
459
|
})();
|
|
393
|
-
const
|
|
394
|
-
const
|
|
395
|
-
const
|
|
460
|
+
const plugkitVerDir = path.join(root, `v${version}`);
|
|
461
|
+
const cacheDir = rtkCacheDir(root, wrapperDir, plugkitVerDir);
|
|
462
|
+
const rtkPath = path.join(cacheDir, rtkBinaryName());
|
|
463
|
+
const rtkOk = path.join(cacheDir, '.rtk-ok');
|
|
396
464
|
if (fs.existsSync(rtkPath) && fs.existsSync(rtkOk)) return rtkPath;
|
|
397
465
|
return null;
|
|
398
466
|
}
|
package/gm.json
CHANGED
package/package.json
CHANGED