@4pm/cli 1.10.2 → 1.11.0

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.
Files changed (2) hide show
  1. package/dist/index.js +42 -16
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -28541,8 +28541,14 @@ function selfUnlink(serverUrl, hashcode3) {
28541
28541
  return post(serverUrl, "/machine-links/self-unlink", { hashcode3 });
28542
28542
  }
28543
28543
  async function fetchCliVersion(serverUrl) {
28544
- const res = await fetch(`${serverUrl}/meta/cli-version`);
28545
- return unwrap(res);
28544
+ const ctrl = new AbortController();
28545
+ const timer = setTimeout(() => ctrl.abort(), 2e4);
28546
+ try {
28547
+ const res = await fetch(`${serverUrl}/meta/cli-version`, { signal: ctrl.signal });
28548
+ return unwrap(res);
28549
+ } finally {
28550
+ clearTimeout(timer);
28551
+ }
28546
28552
  }
28547
28553
 
28548
28554
  // vendor/@4pm/constants/preferences.ts
@@ -32520,6 +32526,9 @@ function compareSemver(a, b2) {
32520
32526
  }
32521
32527
  return 0;
32522
32528
  }
32529
+ function shouldUpdateTo(current2, latest) {
32530
+ return current2 !== latest && compareSemver(current2, latest) <= 0;
32531
+ }
32523
32532
  function runningInstallRoot() {
32524
32533
  return dirname2(dirname2(fileURLToPath2(import.meta.url)));
32525
32534
  }
@@ -32572,10 +32581,10 @@ async function updateViaDownload(tarballUrl, checksum, signature) {
32572
32581
  throw new Error(`Failed to extract the update tarball into ${installRoot}: ${String(err)}`);
32573
32582
  }
32574
32583
  }
32575
- async function updateToLatest(serverUrl) {
32584
+ async function updateToLatest(serverUrl, force = false) {
32576
32585
  if (CLI_VERSION.startsWith("0.0.0")) return { action: "dev-build", version: CLI_VERSION };
32577
32586
  const meta = await fetchCliVersion(serverUrl);
32578
- if (compareSemver(CLI_VERSION, meta.latest) >= 0) {
32587
+ if (!force && !shouldUpdateTo(CLI_VERSION, meta.latest)) {
32579
32588
  return { action: "already-latest", version: CLI_VERSION };
32580
32589
  }
32581
32590
  console.log(t("update.updating", { from: CLI_VERSION, to: meta.latest }));
@@ -32593,7 +32602,7 @@ async function updateToLatest(serverUrl) {
32593
32602
  return { action: "failed", error: String(err), toVersion: meta.latest };
32594
32603
  }
32595
32604
  const installed = readInstalledVersion();
32596
- if (compareSemver(installed, meta.latest) < 0) {
32605
+ if (shouldUpdateTo(installed, meta.latest)) {
32597
32606
  return {
32598
32607
  action: "failed",
32599
32608
  error: `update ran but the installed version is still ${installed} (expected ${meta.latest}) \u2014 it did not apply to the running install at ${runningInstallRoot()}.`,
@@ -32612,7 +32621,7 @@ async function checkAndUpdate(serverUrl, autoUpdate) {
32612
32621
  return { action: "none" };
32613
32622
  }
32614
32623
  const mandatory = compareSemver(CLI_VERSION, meta.minSupported) < 0;
32615
- const outdated = compareSemver(CLI_VERSION, meta.latest) < 0;
32624
+ const outdated = shouldUpdateTo(CLI_VERSION, meta.latest);
32616
32625
  if (!outdated) return { action: "none" };
32617
32626
  if (!mandatory && !autoUpdate) {
32618
32627
  console.warn(t("update.newVersionAutoUpdateOff", { latest: meta.latest, current: CLI_VERSION }));
@@ -32644,7 +32653,7 @@ async function checkAndUpdate(serverUrl, autoUpdate) {
32644
32653
  return { action: "none" };
32645
32654
  }
32646
32655
  const installed = readInstalledVersion();
32647
- if (compareSemver(installed, meta.latest) < 0) {
32656
+ if (shouldUpdateTo(installed, meta.latest)) {
32648
32657
  const msg = t("update.notApplied", { installed, expected: meta.latest });
32649
32658
  if (mandatory) {
32650
32659
  console.error(msg);
@@ -37003,6 +37012,10 @@ var workerToolCopySchema = external_exports.object({
37003
37012
  /** Target machine-link ids to queue the manifest for (pushed on their next connect). */
37004
37013
  targetLinkIds: external_exports.array(external_exports.string().uuid()).min(1).max(200)
37005
37014
  });
37015
+ var adminPoolToolsCopySchema = external_exports.object({
37016
+ sourceUserId: external_exports.string().uuid(),
37017
+ targetUserIds: external_exports.array(external_exports.string().uuid()).min(1).max(200)
37018
+ });
37006
37019
  var workerToolInstallSchema = external_exports.object({
37007
37020
  /** Catalog id or npm package name (validated npm-name shape on the cli). */
37008
37021
  name: external_exports.string().trim().min(1).max(214),
@@ -38282,6 +38295,12 @@ function sleep(ms2) {
38282
38295
  t2.unref?.();
38283
38296
  });
38284
38297
  }
38298
+ function extractToolError(out) {
38299
+ const lines = out.split(/\r?\n/).map((l) => l.trim()).filter((l) => l && !/^npm warn\b/i.test(l));
38300
+ const errLines = lines.filter((l) => /npm error|npm err!|ERR_|ELIFECYCLE|unsupported engine|e404|etarget/i.test(l));
38301
+ const msg = (errLines.length ? errLines : lines).slice(-3).join(" \xB7 ");
38302
+ return msg.length > 500 ? `${msg.slice(0, 500)}\u2026` : msg;
38303
+ }
38285
38304
  function classifyToolFailure(code, out) {
38286
38305
  if (code === 124) return { reason: "timeout", retryable: true };
38287
38306
  const t2 = out.toLowerCase();
@@ -38421,8 +38440,10 @@ async function runWorkerToolOp(op, name, manager, onLine, timeoutMs = TOOL_INSTA
38421
38440
  return { ok: false, exitCode: 1, error: resolved.error ?? "Invalid package." };
38422
38441
  }
38423
38442
  onLine(`$ ${manager} ${opArgs(op, manager, resolved.pkg).join(" ")}`);
38424
- const { code } = await run3(manager, opArgs(op, manager, resolved.pkg), onLine, timeoutMs);
38425
- return { ok: code === 0, exitCode: code, error: code === 0 ? void 0 : `${manager} exited ${code}` };
38443
+ const { code, out } = await run3(manager, opArgs(op, manager, resolved.pkg), onLine, timeoutMs);
38444
+ if (code === 0) return { ok: true, exitCode: code };
38445
+ const detail = extractToolError(out);
38446
+ return { ok: false, exitCode: code, error: detail ? `${manager} exited ${code}: ${detail}` : `${manager} exited ${code}` };
38426
38447
  }
38427
38448
  function setToolAutoUpdate(profileDir2, name, enabled) {
38428
38449
  const resolved = resolvePackage(name, "update");
@@ -38456,7 +38477,8 @@ async function installPinned(name, version, manager, onLine, timeoutMs = TOOL_IN
38456
38477
  if (code === 0) return { ok: true };
38457
38478
  const { reason, retryable } = classifyToolFailure(code, out);
38458
38479
  if (!retryable || attempt === TOOL_INSTALL_MAX_ATTEMPTS) {
38459
- return { ok: false, error: `${manager} exited ${code}`, reason, retryable };
38480
+ const detail = extractToolError(out);
38481
+ return { ok: false, error: detail ? `${manager} exited ${code}: ${detail}` : `${manager} exited ${code}`, reason, retryable };
38460
38482
  }
38461
38483
  const base = TOOL_RETRY_BASE_MS * 3 ** (attempt - 1);
38462
38484
  const wait = Math.round(base * (0.8 + Math.random() * 0.4));
@@ -38592,7 +38614,7 @@ var UpdateScheduler = class {
38592
38614
  if (this.forcedPending) {
38593
38615
  if (this.bus.busy !== null) return;
38594
38616
  this.forcedPending = false;
38595
- await this.runUpdate();
38617
+ await this.runUpdate(true);
38596
38618
  return;
38597
38619
  }
38598
38620
  const p2 = this.policy;
@@ -38608,15 +38630,16 @@ var UpdateScheduler = class {
38608
38630
  if (this.bus.busy !== null) return;
38609
38631
  this.lastDoneDateKey = clock.dateKey;
38610
38632
  this.pendingDateKey = null;
38611
- await this.runUpdate();
38633
+ await this.runUpdate(false);
38612
38634
  }
38613
- /** Check + update, then re-exec the new binary on success (keeps `.cre`/profile). */
38614
- async runUpdate() {
38635
+ /** Check + update, then re-exec the new binary on success (keeps `.cre`/profile). `force` installs the
38636
+ * server's latest unconditionally (an operator push) vs. the conditional daily-scheduled update. */
38637
+ async runUpdate(force) {
38615
38638
  this.updating = true;
38616
38639
  try {
38617
38640
  await this.updateFlaggedTools();
38618
38641
  this.bus.log(t("update.scheduledChecking"));
38619
- const result = await updateToLatest(this.serverUrl);
38642
+ const result = await updateToLatest(this.serverUrl, force);
38620
38643
  if (result.action === "already-latest") {
38621
38644
  logger.info("update.scheduled.latest", { version: result.version });
38622
38645
  return;
@@ -42201,7 +42224,10 @@ function handleToolsChannels(ctx, message, payload) {
42201
42224
  ctx.send(WsChannels.TOOLS_DONE, {
42202
42225
  opId,
42203
42226
  ok: restoreFailed.length === 0,
42204
- exitCode: restoreFailed.length === 0 ? 0 : 1
42227
+ exitCode: restoreFailed.length === 0 ? 0 : 1,
42228
+ // Name the tools that failed + their classified reason so the web card shows more than a
42229
+ // bare "failed" (the real per-tool npm/pnpm error already streamed in the log above).
42230
+ error: restoreFailed.length === 0 ? void 0 : restoreFailed.map((f2) => `${f2.name} (${f2.reason})`).join(", ")
42205
42231
  });
42206
42232
  await ctx.reportWorkerTools(restoreTrigger, restoreFailed);
42207
42233
  }).catch(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@4pm/cli",
3
- "version": "1.10.2",
3
+ "version": "1.11.0",
4
4
  "private": false,
5
5
  "description": "4PM CLI — drives Claude/Codex CLIs on AI worker machines (Node + TypeScript)",
6
6
  "license": "LicenseRef-4PM-Source-Available",