@4pm/cli 1.10.2 → 1.12.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.
- package/dist/index.js +61 -25
- 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
|
|
28545
|
-
|
|
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 (
|
|
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 (
|
|
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 =
|
|
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 (
|
|
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),
|
|
@@ -37610,8 +37623,20 @@ async function writeTemplateMarker(target) {
|
|
|
37610
37623
|
const marker = { templateVersion: PROJECT_TEMPLATE.version, scaffoldedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
37611
37624
|
await writeFile(join6(dir2, ".4pm.json"), JSON.stringify(marker, null, 2) + "\n", "utf8");
|
|
37612
37625
|
}
|
|
37626
|
+
function readSpecField(spec, key) {
|
|
37627
|
+
const fields = spec.fields;
|
|
37628
|
+
if (Array.isArray(fields)) {
|
|
37629
|
+
const f2 = fields.find(
|
|
37630
|
+
(x) => !!x && typeof x.id === "string" && x.id === key
|
|
37631
|
+
);
|
|
37632
|
+
if (f2 && typeof f2.value === "string") return f2.value;
|
|
37633
|
+
}
|
|
37634
|
+
return typeof spec[key] === "string" ? spec[key] : "";
|
|
37635
|
+
}
|
|
37613
37636
|
async function aiInit(target, spec, emit) {
|
|
37614
|
-
|
|
37637
|
+
const guideFile = readSpecField(spec, "ai_guide_file") === "AGENT.md" ? "AGENT.md" : "CLAUDE.md";
|
|
37638
|
+
const guideInstructions = readSpecField(spec, "ai_guide_instructions").trim();
|
|
37639
|
+
emit("ai-init", `AI init: subagents, README, ${guideFile}\u2026`);
|
|
37615
37640
|
const subagents = Array.isArray(spec.subagents) ? spec.subagents : [];
|
|
37616
37641
|
if (subagents.length > 0) {
|
|
37617
37642
|
await mkdir(join6(target, ".claude", "agents"), { recursive: true });
|
|
@@ -37634,14 +37659,12 @@ ${(sa.description || "").trim()}
|
|
|
37634
37659
|
${specJson}`
|
|
37635
37660
|
);
|
|
37636
37661
|
await generateFile(
|
|
37637
|
-
join6(target,
|
|
37638
|
-
`Write a
|
|
37639
|
-
${specJson}`
|
|
37640
|
-
|
|
37641
|
-
|
|
37642
|
-
|
|
37643
|
-
`Write an AGENT.md (Markdown only, no preamble) with guidance/conventions for AI agents working in this project, derived from its spec JSON:
|
|
37644
|
-
${specJson}`
|
|
37662
|
+
join6(target, guideFile),
|
|
37663
|
+
`Write a ${guideFile} (Markdown only, no preamble) with guidance/conventions for AI agents working in this project, derived from its spec JSON:
|
|
37664
|
+
${specJson}` + (guideInstructions ? `
|
|
37665
|
+
Also incorporate these additional instructions/content:
|
|
37666
|
+
${guideInstructions}
|
|
37667
|
+
` : "")
|
|
37645
37668
|
);
|
|
37646
37669
|
}
|
|
37647
37670
|
async function generateFile(path, prompt) {
|
|
@@ -38282,6 +38305,12 @@ function sleep(ms2) {
|
|
|
38282
38305
|
t2.unref?.();
|
|
38283
38306
|
});
|
|
38284
38307
|
}
|
|
38308
|
+
function extractToolError(out) {
|
|
38309
|
+
const lines = out.split(/\r?\n/).map((l) => l.trim()).filter((l) => l && !/^npm warn\b/i.test(l));
|
|
38310
|
+
const errLines = lines.filter((l) => /npm error|npm err!|ERR_|ELIFECYCLE|unsupported engine|e404|etarget/i.test(l));
|
|
38311
|
+
const msg = (errLines.length ? errLines : lines).slice(-3).join(" \xB7 ");
|
|
38312
|
+
return msg.length > 500 ? `${msg.slice(0, 500)}\u2026` : msg;
|
|
38313
|
+
}
|
|
38285
38314
|
function classifyToolFailure(code, out) {
|
|
38286
38315
|
if (code === 124) return { reason: "timeout", retryable: true };
|
|
38287
38316
|
const t2 = out.toLowerCase();
|
|
@@ -38421,8 +38450,10 @@ async function runWorkerToolOp(op, name, manager, onLine, timeoutMs = TOOL_INSTA
|
|
|
38421
38450
|
return { ok: false, exitCode: 1, error: resolved.error ?? "Invalid package." };
|
|
38422
38451
|
}
|
|
38423
38452
|
onLine(`$ ${manager} ${opArgs(op, manager, resolved.pkg).join(" ")}`);
|
|
38424
|
-
const { code } = await run3(manager, opArgs(op, manager, resolved.pkg), onLine, timeoutMs);
|
|
38425
|
-
|
|
38453
|
+
const { code, out } = await run3(manager, opArgs(op, manager, resolved.pkg), onLine, timeoutMs);
|
|
38454
|
+
if (code === 0) return { ok: true, exitCode: code };
|
|
38455
|
+
const detail = extractToolError(out);
|
|
38456
|
+
return { ok: false, exitCode: code, error: detail ? `${manager} exited ${code}: ${detail}` : `${manager} exited ${code}` };
|
|
38426
38457
|
}
|
|
38427
38458
|
function setToolAutoUpdate(profileDir2, name, enabled) {
|
|
38428
38459
|
const resolved = resolvePackage(name, "update");
|
|
@@ -38456,7 +38487,8 @@ async function installPinned(name, version, manager, onLine, timeoutMs = TOOL_IN
|
|
|
38456
38487
|
if (code === 0) return { ok: true };
|
|
38457
38488
|
const { reason, retryable } = classifyToolFailure(code, out);
|
|
38458
38489
|
if (!retryable || attempt === TOOL_INSTALL_MAX_ATTEMPTS) {
|
|
38459
|
-
|
|
38490
|
+
const detail = extractToolError(out);
|
|
38491
|
+
return { ok: false, error: detail ? `${manager} exited ${code}: ${detail}` : `${manager} exited ${code}`, reason, retryable };
|
|
38460
38492
|
}
|
|
38461
38493
|
const base = TOOL_RETRY_BASE_MS * 3 ** (attempt - 1);
|
|
38462
38494
|
const wait = Math.round(base * (0.8 + Math.random() * 0.4));
|
|
@@ -38592,7 +38624,7 @@ var UpdateScheduler = class {
|
|
|
38592
38624
|
if (this.forcedPending) {
|
|
38593
38625
|
if (this.bus.busy !== null) return;
|
|
38594
38626
|
this.forcedPending = false;
|
|
38595
|
-
await this.runUpdate();
|
|
38627
|
+
await this.runUpdate(true);
|
|
38596
38628
|
return;
|
|
38597
38629
|
}
|
|
38598
38630
|
const p2 = this.policy;
|
|
@@ -38608,15 +38640,16 @@ var UpdateScheduler = class {
|
|
|
38608
38640
|
if (this.bus.busy !== null) return;
|
|
38609
38641
|
this.lastDoneDateKey = clock.dateKey;
|
|
38610
38642
|
this.pendingDateKey = null;
|
|
38611
|
-
await this.runUpdate();
|
|
38643
|
+
await this.runUpdate(false);
|
|
38612
38644
|
}
|
|
38613
|
-
/** Check + update, then re-exec the new binary on success (keeps `.cre`/profile).
|
|
38614
|
-
|
|
38645
|
+
/** Check + update, then re-exec the new binary on success (keeps `.cre`/profile). `force` installs the
|
|
38646
|
+
* server's latest unconditionally (an operator push) vs. the conditional daily-scheduled update. */
|
|
38647
|
+
async runUpdate(force) {
|
|
38615
38648
|
this.updating = true;
|
|
38616
38649
|
try {
|
|
38617
38650
|
await this.updateFlaggedTools();
|
|
38618
38651
|
this.bus.log(t("update.scheduledChecking"));
|
|
38619
|
-
const result = await updateToLatest(this.serverUrl);
|
|
38652
|
+
const result = await updateToLatest(this.serverUrl, force);
|
|
38620
38653
|
if (result.action === "already-latest") {
|
|
38621
38654
|
logger.info("update.scheduled.latest", { version: result.version });
|
|
38622
38655
|
return;
|
|
@@ -42201,7 +42234,10 @@ function handleToolsChannels(ctx, message, payload) {
|
|
|
42201
42234
|
ctx.send(WsChannels.TOOLS_DONE, {
|
|
42202
42235
|
opId,
|
|
42203
42236
|
ok: restoreFailed.length === 0,
|
|
42204
|
-
exitCode: restoreFailed.length === 0 ? 0 : 1
|
|
42237
|
+
exitCode: restoreFailed.length === 0 ? 0 : 1,
|
|
42238
|
+
// Name the tools that failed + their classified reason so the web card shows more than a
|
|
42239
|
+
// bare "failed" (the real per-tool npm/pnpm error already streamed in the log above).
|
|
42240
|
+
error: restoreFailed.length === 0 ? void 0 : restoreFailed.map((f2) => `${f2.name} (${f2.reason})`).join(", ")
|
|
42205
42241
|
});
|
|
42206
42242
|
await ctx.reportWorkerTools(restoreTrigger, restoreFailed);
|
|
42207
42243
|
}).catch(
|