@phnx-labs/agents-cli 1.22.0 → 1.22.1

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.
@@ -1267,34 +1267,23 @@ export function isVersionIsolated(agent, version) {
1267
1267
  return fs.existsSync(getIsolatedMarkerPath(agent, version));
1268
1268
  }
1269
1269
  /**
1270
- * Grok's official installer writes into ~/.grok/downloads, which (because we
1271
- * symlink ~/.grok to the active version home) resolves to the PREVIOUS default
1272
- * home during `agents add grok@<new>`. Move the freshly-downloaded binary and
1273
- * its generic platform copy into the target version's isolated home so
1274
- * `listInstalledVersions` and the shim resolve the right binary.
1270
+ * Move any `grok-<installedVersion>-...` binary (and its generic platform
1271
+ * copy) found directly under `sourceDownloads` into `targetDownloads`.
1272
+ * Returns true if the versioned binary was moved.
1275
1273
  */
1276
- function relocateGrokBinaryToVersionHome(installedVersion) {
1277
- const hostGrokLink = path.join(getHomeDir(), agentConfigDirName('grok'));
1278
- let sourceDownloads;
1279
- try {
1280
- sourceDownloads = path.join(fs.readlinkSync(hostGrokLink), 'downloads');
1281
- }
1282
- catch {
1283
- sourceDownloads = path.join(hostGrokLink, 'downloads');
1284
- }
1285
- const targetDownloads = path.join(getVersionHomePath('grok', installedVersion), agentConfigDirName('grok'), 'downloads');
1274
+ function tryMoveGrokDownloads(sourceDownloads, targetDownloads, installedVersion) {
1286
1275
  if (!fs.existsSync(sourceDownloads))
1287
- return;
1276
+ return false;
1288
1277
  if (path.resolve(sourceDownloads) === path.resolve(targetDownloads))
1289
- return;
1278
+ return false;
1290
1279
  const entries = fs.readdirSync(sourceDownloads).filter((e) => e.startsWith('grok-'));
1291
1280
  if (entries.length === 0)
1292
- return;
1293
- fs.mkdirSync(targetDownloads, { recursive: true });
1281
+ return false;
1294
1282
  const escapedVersion = installedVersion.replace(/\./g, '\\.');
1295
1283
  const versionedPattern = new RegExp(`^grok-${escapedVersion}-`);
1296
1284
  const movedPaths = [];
1297
- // Move the versioned binary first.
1285
+ // Move the versioned binary first — only create the target dir once we
1286
+ // know there is something to move into it.
1298
1287
  for (const entry of entries) {
1299
1288
  if (!versionedPattern.test(entry))
1300
1289
  continue;
@@ -1303,6 +1292,7 @@ function relocateGrokBinaryToVersionHome(installedVersion) {
1303
1292
  if (fs.existsSync(dst))
1304
1293
  continue;
1305
1294
  try {
1295
+ fs.mkdirSync(targetDownloads, { recursive: true });
1306
1296
  fs.renameSync(src, dst);
1307
1297
  movedPaths.push(dst);
1308
1298
  }
@@ -1311,7 +1301,7 @@ function relocateGrokBinaryToVersionHome(installedVersion) {
1311
1301
  }
1312
1302
  }
1313
1303
  if (movedPaths.length === 0)
1314
- return;
1304
+ return false;
1315
1305
  // The installer also creates a generic platform binary (e.g. grok-macos-aarch64)
1316
1306
  // that is a copy of the versioned binary. Move it too if its size matches.
1317
1307
  const movedSize = fs.statSync(movedPaths[0]).size;
@@ -1331,6 +1321,42 @@ function relocateGrokBinaryToVersionHome(installedVersion) {
1331
1321
  /* ignore per-file failures */
1332
1322
  }
1333
1323
  }
1324
+ return true;
1325
+ }
1326
+ /**
1327
+ * Grok's official installer writes into ~/.grok/downloads, which (because we
1328
+ * symlink ~/.grok to the active version home) resolves to the PREVIOUS default
1329
+ * home during `agents add grok@<new>`. Move the freshly-downloaded binary and
1330
+ * its generic platform copy into the target version's isolated home so
1331
+ * `listInstalledVersions` and the shim resolve the right binary.
1332
+ *
1333
+ * Self-healing: if `~/.grok`'s current target has nothing matching (e.g. a
1334
+ * PAST install mislabeled `installedVersion` as the literal string 'latest',
1335
+ * whose regex never matched the real filename, stranding the binary in
1336
+ * whatever version was default at the time), sweep every other installed
1337
+ * grok version home for the missing file before giving up.
1338
+ */
1339
+ function relocateGrokBinaryToVersionHome(installedVersion) {
1340
+ const hostGrokLink = path.join(getHomeDir(), agentConfigDirName('grok'));
1341
+ let sourceDownloads;
1342
+ try {
1343
+ sourceDownloads = path.join(fs.readlinkSync(hostGrokLink), 'downloads');
1344
+ }
1345
+ catch {
1346
+ sourceDownloads = path.join(hostGrokLink, 'downloads');
1347
+ }
1348
+ const targetDownloads = path.join(getVersionHomePath('grok', installedVersion), agentConfigDirName('grok'), 'downloads');
1349
+ if (tryMoveGrokDownloads(sourceDownloads, targetDownloads, installedVersion))
1350
+ return;
1351
+ for (const version of listInstalledVersions('grok')) {
1352
+ if (version === installedVersion)
1353
+ continue;
1354
+ const candidate = path.join(getVersionHomePath('grok', version), agentConfigDirName('grok'), 'downloads');
1355
+ if (path.resolve(candidate) === path.resolve(sourceDownloads))
1356
+ continue; // already tried
1357
+ if (tryMoveGrokDownloads(candidate, targetDownloads, installedVersion))
1358
+ return;
1359
+ }
1334
1360
  }
1335
1361
  /**
1336
1362
  * Install a specific version of an agent.
@@ -1376,7 +1402,28 @@ export async function installVersion(agent, version, onProgress, opts) {
1376
1402
  await execAsync(script, { timeout: 120000 });
1377
1403
  }
1378
1404
  if (version === 'latest') {
1379
- installedVersion = await getCliVersionFromPath(agent) || version;
1405
+ // A freshly-installed self-updating binary (grok, …) can take a
1406
+ // moment to become resolvable on PATH right after the installer
1407
+ // exits — retry briefly rather than silently falling back to the
1408
+ // literal string 'latest'. That fallback used to corrupt version
1409
+ // bookkeeping downstream: it creates a bogus `versions/<agent>/latest/`
1410
+ // dir, and for grok specifically defeats relocateGrokBinaryToVersionHome
1411
+ // (its exact-filename regex can never match `grok-latest-...`), which
1412
+ // permanently strands the real downloaded binary in the PREVIOUS
1413
+ // default's downloads dir.
1414
+ let probed = await getCliVersionFromPath(agent);
1415
+ for (let attempt = 0; !probed && attempt < 2; attempt++) {
1416
+ await new Promise((resolve) => setTimeout(resolve, 1000));
1417
+ probed = await getCliVersionFromPath(agent);
1418
+ }
1419
+ if (!probed) {
1420
+ return {
1421
+ success: false,
1422
+ installedVersion: version,
1423
+ error: `${agentConfig.name} installed but its version could not be determined after several attempts.`,
1424
+ };
1425
+ }
1426
+ installedVersion = probed;
1380
1427
  // Fold any stale literal `latest` dir from an earlier probe-failed
1381
1428
  // install into the real version so it stops shadowing `agents view`.
1382
1429
  await reconcileStaleLatestDir(agent, installedVersion);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phnx-labs/agents-cli",
3
- "version": "1.22.0",
3
+ "version": "1.22.1",
4
4
  "description": "One CLI for all your AI coding agents - versions, config, cloud dispatch, sessions, and teams (now with first-class Grok Build CLI support)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",