gm-codex 2.0.948 → 2.0.950

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-codex",
3
- "version": "2.0.948",
3
+ "version": "2.0.950",
4
4
  "description": "State machine agent with hooks, skills, and automated git enforcement",
5
5
  "author": {
6
6
  "name": "AnEntrypoint",
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
- if (!e.startsWith('v')) continue;
256
- if (e === `v${keepVersion}`) continue;
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
- const tmpPath = `${finalPath}.partial`;
302
- if (fs.existsSync(tmpPath)) {
357
+ if (fs.existsSync(partialPath)) {
303
358
  try {
304
- const st = fs.statSync(tmpPath);
359
+ const st = fs.statSync(partialPath);
305
360
  if (Date.now() - st.mtimeMs > LOCK_STALE_MS) {
306
- fs.unlinkSync(tmpPath);
307
- log(`cleared stale partial: ${tmpPath}`);
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, tmpPath);
367
+ await downloadWithRetry(url, partialPath);
313
368
 
314
369
  if (expectedSha) {
315
- const got = await sha256OfFile(tmpPath);
370
+ const got = await sha256OfFile(partialPath);
316
371
  if (got !== expectedSha) {
317
- try { fs.unlinkSync(tmpPath); } catch (_) {}
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(tmpPath, finalPath); }
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(tmpPath, finalPath);
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
- // Best-effort rtk fetch: failures here never block plugkit usage
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
- async function bootstrapRtk(verDir, version, wrapperDir, silent) {
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 rtkPath = path.join(verDir, rtkName);
354
- const rtkOk = path.join(verDir, '.rtk-ok');
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
- const url = `https://github.com/${RELEASE_REPO}/releases/download/v${version}/${rtkName}`;
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, version, kind: 'rtk' });
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 verDir = path.join(root, `v${version}`);
394
- const rtkPath = path.join(verDir, rtkBinaryName());
395
- const rtkOk = path.join(verDir, '.rtk-ok');
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
  }
@@ -1,6 +1,6 @@
1
- 29c07cfbe1536e683a8892a2bfa459a879bd48bf0652adf606dea9a39e4d2fa7 plugkit-win32-x64.exe
2
- 5523ce4c5167cfb01f2dc5a865daf8dfedde321393f8a02743f224cd0b6bf610 plugkit-win32-arm64.exe
3
- 710fdb8a301c7b13b86677bbea64e2a54a092bb91c5fef0c8ec391795ab23202 plugkit-darwin-x64
4
- 082d56484e02458bbc97ba63b89010fd155265e74cb17c7d227ed57dbcef96c3 plugkit-darwin-arm64
5
- b9d1f97cf048a9ab6345cd68177762a8603cd0f52e79e29c611cacb22adf02f7 plugkit-linux-x64
6
- 6c64c385204b903d661002daf2e6b287082a02e579ce9c5533a24828765e46ab plugkit-linux-arm64
1
+ bbc4d0161be4088b66eea6cc3f7e463a43ab136f42da72d65953a68928fc5995 plugkit-win32-x64.exe
2
+ 291dafaab31076f25dd2e2a05a1a58db941465ea48762b7b029e4b309af1d63c plugkit-win32-arm64.exe
3
+ 25cd1a010844e9a3652b9da382088500740b4773c11eea51a871b0d62d436327 plugkit-darwin-x64
4
+ 1d38873d0c3cb3317f1b8cab4b12f50631546af4edad3e88fecd870e6d344009 plugkit-darwin-arm64
5
+ d5a94da58384f623aa05da0f77d3dd90d6d65a1d0de0ee718ceae522d9eef458 plugkit-linux-x64
6
+ 01d5b0f1f7476ae8dccf13e7fdcc7ec925f27456e76a54e886576d962f433f77 plugkit-linux-arm64
@@ -1 +1 @@
1
- 0.1.321
1
+ 0.1.322
package/bin/rtk.sha256 CHANGED
@@ -1,5 +1,5 @@
1
- 67e7f3aa39ab54376f90d040820ae471adc47374b0b9fa2527a09de9be73216b rtk-win32-x64.exe
2
- 7f0ce6ff2ae933fd82c5d309df3e06f2ba12b2a47b495ed54a8eacb096ef5264 rtk-win32-arm64.exe
1
+ 15d0b8abcf02d4b5c246370ad63f4948ed38855696e429d752edea1edb819dae rtk-win32-x64.exe
2
+ 9a035ec6e0a49c6986defb7495731785b9adfe3cb4a5d2180231bb418de266c7 rtk-win32-arm64.exe
3
3
  1b1e792767ed0e1e6ca0e2f0a8de02e77b06dea2f5ae667278b94baf239fcdc3 rtk-darwin-x64
4
4
  9717978d9d6216ea50c94444e00e359479b6315a17bd48c16064b267c8b0b60d rtk-darwin-arm64
5
5
  a100d3defac54194144e5723aec57e6f286b42298c67145c8428815246c9ee56 rtk-linux-x64
package/gm.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.948",
3
+ "version": "2.0.950",
4
4
  "description": "State machine agent with hooks, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",
@@ -23,5 +23,5 @@
23
23
  "publishConfig": {
24
24
  "access": "public"
25
25
  },
26
- "plugkitVersion": "0.1.321"
26
+ "plugkitVersion": "0.1.322"
27
27
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-codex",
3
- "version": "2.0.948",
3
+ "version": "2.0.950",
4
4
  "description": "State machine agent with hooks, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",
package/plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.948",
3
+ "version": "2.0.950",
4
4
  "description": "State machine agent with hooks, skills, and automated git enforcement",
5
5
  "author": {
6
6
  "name": "AnEntrypoint",