agents: an update says each agent's version from and to (37FlowAI on X: "更新后 显示一次各 Agent 的版本变更列表: 例如 Pi 从 v1.0x 更新到 v1.10")
Update all's line, when the run is done, and its toast now list every
agent it updated with the versions it went from and to ("2 agents
updated: Codex 0.155.1 → 0.159.0, Crush 0.3.0 → 0.4.0"); with some
failed, the ones that went well are listed before the ones that didn't.
The line stays one line (cut short when narrow, so the list under it
doesn't move), and its tooltip has each change and error on its own
line. A row's own pill says "Codex updated: 0.155.1 → 0.159.0".
Three new strings, in zh, zh-TW, ja and de.
Run: agent-update-all and cli-update in Chromium and WebKit (14 pass in
each); the new checks fail on origin/main's app.js (5 fail). A new
case updates two agents in en, zh, ja and de, and checks the line at
420 wide keeps its height with no sideways scroll. gui-de, gui-ja,
i18n-keys, cli-behind, click-scroll and agent-cli-missing pass in both
engines (22 each). Not tried in the real window with real CLIs behind.
This commit is contained in:
1 parent
1734d2b33d
commit
9f5812f3cf
4 files changed
+66
-12
No files matched your search
@@ -1678,9 +1678,11 @@ async function loadCLIs(again = 0) {
|
||||
}
|
||||
|
||||
// updateCLI updates one agent's CLI; "" when it went well, else why not.
|
||||
// Quiet (Update all) leaves the saying to the caller.
|
||||
// Quiet (Update all) leaves the saying to the caller. Each version it went
|
||||
// from and to is said once (37FlowAI on X: "Pi 从 v1.0x 更新到 v1.10").
|
||||
async function updateCLI(a, btn, quiet = false) {
|
||||
if (cliBusy.has(a.id)) return t("{agent} is already being updated", { agent: a.name });
|
||||
const from = cliInfo[a.id]?.version;
|
||||
cliBusy.add(a.id);
|
||||
if (btn) paintCLIButton(btn, cliInfo[a.id] || {}, true); // in place: what was clicked stays
|
||||
else paintCLI(a.id);
|
||||
@@ -1688,7 +1690,9 @@ async function updateCLI(a, btn, quiet = false) {
|
||||
try {
|
||||
const c = await api("agents/cli/" + encodeURIComponent(a.id), {});
|
||||
cliInfo[a.id] = c;
|
||||
if (!quiet) status(t("{agent} updated to {v}", { agent: a.name, v: c.version }), "ok");
|
||||
if (cliAll && quiet) cliAll.changed.push(versionChange(a.name, from, c.version));
|
||||
if (!quiet) status(from && from !== c.version ? t("{agent} updated: {from} → {v}", { agent: a.name, from, v: c.version })
|
||||
: t("{agent} updated to {v}", { agent: a.name, v: c.version }), "ok");
|
||||
return "";
|
||||
} catch (e) {
|
||||
if (!quiet) status(e.message, "err", 12000);
|
||||
@@ -1702,6 +1706,10 @@ async function updateCLI(a, btn, quiet = false) {
|
||||
}
|
||||
}
|
||||
|
||||
// "Pi 1.0.9 → 1.1.0", or the name and the version now when the one before
|
||||
// isn't known or is the same
|
||||
const versionChange = (name, from, to) => from && to && from !== to ? `${name} ${from} → ${to}` : [name, to].filter(Boolean).join(" ");
|
||||
|
||||
// ---------- Update all (#727) ----------
|
||||
// With two or more agents' CLIs behind, a line above the list updates them
|
||||
// all, one after another (two global npm installs at once can trip over
|
||||
@@ -1710,7 +1718,7 @@ async function updateCLI(a, btn, quiet = false) {
|
||||
// window coming back), saying how the run went or what is left, so the
|
||||
// list under it never jumps for a click — Update all's or a row's pill's.
|
||||
|
||||
let cliAll = null; // { n, done, now, failed: [{ name, msg }] } once Update all is clicked
|
||||
let cliAll = null; // { n, done, now, failed: [{ name, msg }], changed: ["Pi 1.0.9 → 1.1.0"] } once Update all is clicked
|
||||
|
||||
const updatable = () => (state?.agents || []).filter((a) => cliInfo[a.id]?.update && !cliBusy.has(a.id));
|
||||
|
||||
@@ -1739,10 +1747,9 @@ function paintUpdateAll(fresh = false) {
|
||||
b.append(svg(CLI_SPIN, 11, 1.8), el("span", "", t("Updating…")));
|
||||
parts.push(b);
|
||||
} else if (cliAll) {
|
||||
const ok = cliAll.n - cliAll.failed.length;
|
||||
say.textContent = !cliAll.failed.length ? t("{n} agents updated", { n: ok })
|
||||
: t("{ok} of {n} agents updated · {failed} didn't: {names}", { ok, n: cliAll.n, failed: cliAll.failed.length, names: cliAll.failed.map((f) => f.name).join(", ") });
|
||||
say.title = cliAll.failed.map((f) => f.msg).join("\n");
|
||||
say.textContent = updatedSay();
|
||||
// one line, cut short when narrow: every change and error in full here
|
||||
say.title = [...cliAll.changed, ...cliAll.failed.map((f) => f.msg)].join("\n");
|
||||
} else {
|
||||
// what is behind now, the ones being updated by their own pill too
|
||||
const behind = (state?.agents || []).filter((a) => cliInfo[a.id]?.update || cliBusy.has(a.id));
|
||||
@@ -1765,7 +1772,7 @@ async function updateAllCLIs() {
|
||||
if (cliAll && cliAll.done < cliAll.n) return;
|
||||
const ups = updatable();
|
||||
if (!ups.length) return;
|
||||
cliAll = { n: ups.length, done: 0, now: ups[0].name, failed: [] };
|
||||
cliAll = { n: ups.length, done: 0, now: ups[0].name, failed: [], changed: [] };
|
||||
for (const a of ups) {
|
||||
cliAll.now = a.name;
|
||||
paintUpdateAll();
|
||||
@@ -1774,10 +1781,19 @@ async function updateAllCLIs() {
|
||||
cliAll.done++;
|
||||
}
|
||||
paintUpdateAll();
|
||||
if (!cliAll.failed.length) status(t("{n} agents updated", { n: cliAll.n }), "ok");
|
||||
if (!cliAll.failed.length) status(updatedSay(), "ok");
|
||||
else status(cliAll.failed.map((f) => f.msg).join(" · "), "err", 12000);
|
||||
}
|
||||
|
||||
// how an Update all went, with each agent's versions from and to
|
||||
function updatedSay() {
|
||||
const ok = cliAll.n - cliAll.failed.length, list = cliAll.changed.join(", ");
|
||||
if (!cliAll.failed.length) return t("{n} agents updated: {list}", { n: ok, list });
|
||||
const names = cliAll.failed.map((f) => f.name).join(", ");
|
||||
return ok ? t("{ok} of {n} agents updated: {list} · {failed} didn't: {names}", { ok, n: cliAll.n, list, failed: cliAll.failed.length, names })
|
||||
: t("{ok} of {n} agents updated · {failed} didn't: {names}", { ok, n: cliAll.n, failed: cliAll.failed.length, names });
|
||||
}
|
||||
|
||||
// ---------- installing the agents not here (#727) ----------
|
||||
// Under the list, the agents magpie knows that aren't on this machine, each
|
||||
// with its vendor's install commands to copy into a terminal: the vendor's
|
||||
|
||||
@@ -481,6 +481,9 @@ const I18N = {
|
||||
"{agent} has an update": "{agent} 有新版本",
|
||||
"Every agent is up to date": "所有 Agent 都已是最新",
|
||||
"{ok} of {n} agents updated · {failed} didn't: {names}": "{n} 个 Agent 中已更新 {ok} 个 · {failed} 个未成功:{names}",
|
||||
"{agent} updated: {from} → {v}": "{agent} 已更新:{from} → {v}",
|
||||
"{n} agents updated: {list}": "已更新 {n} 个 Agent:{list}",
|
||||
"{ok} of {n} agents updated: {list} · {failed} didn't: {names}": "{n} 个 Agent 中已更新 {ok} 个:{list} · {failed} 个未成功:{names}",
|
||||
"Install another agent ({n})": "安装其他 Agent({n})",
|
||||
"CLI not found": "未找到 CLI",
|
||||
"{agent}'s settings are still here ({path}), but its command-line program isn't found: it may have been uninstalled. Install another agent, below the list, has its install command.": "{agent} 的配置还在({path}),但找不到它的命令行程序,可能已被卸载。列表下方的「安装其他 Agent」里有它的安装命令。",
|
||||
@@ -4279,6 +4282,9 @@ const I18N = {
|
||||
"{agent} has an update": "{agent} 有新版本",
|
||||
"Every agent is up to date": "所有 Agent 都已是最新",
|
||||
"{ok} of {n} agents updated · {failed} didn't: {names}": "{n} 個 Agent 中已更新 {ok} 個 · {failed} 個未成功:{names}",
|
||||
"{agent} updated: {from} → {v}": "{agent} 已更新:{from} → {v}",
|
||||
"{n} agents updated: {list}": "已更新 {n} 個 Agent:{list}",
|
||||
"{ok} of {n} agents updated: {list} · {failed} didn't: {names}": "{n} 個 Agent 中已更新 {ok} 個:{list} · {failed} 個未成功:{names}",
|
||||
"Install another agent ({n})": "安裝其他 Agent({n})",
|
||||
"CLI not found": "未找到 CLI",
|
||||
"{agent}'s settings are still here ({path}), but its command-line program isn't found: it may have been uninstalled. Install another agent, below the list, has its install command.": "{agent} 的設定還在({path}),但找不到它的命令列程式,可能已被解除安裝。列表下方的「安裝其他 Agent」裡有它的安裝命令。",
|
||||
@@ -8042,6 +8048,9 @@ const I18N = {
|
||||
"{agent} has an update": "{agent} にアップデートがあります",
|
||||
"Every agent is up to date": "すべてのエージェントが最新です",
|
||||
"{ok} of {n} agents updated · {failed} didn't: {names}": "{n} 個中 {ok} 個のエージェントをアップデートしました · {failed} 個は失敗:{names}",
|
||||
"{agent} updated: {from} → {v}": "{agent} をアップデートしました:{from} → {v}",
|
||||
"{n} agents updated: {list}": "{n} 個のエージェントをアップデートしました:{list}",
|
||||
"{ok} of {n} agents updated: {list} · {failed} didn't: {names}": "{n} 個中 {ok} 個のエージェントをアップデートしました:{list} · {failed} 個は失敗:{names}",
|
||||
"Install another agent ({n})": "他のエージェントをインストール({n})",
|
||||
"CLI not found": "CLI が見つかりません",
|
||||
"{agent}'s settings are still here ({path}), but its command-line program isn't found: it may have been uninstalled. Install another agent, below the list, has its install command.": "{agent} の設定は残っています({path})が、コマンドラインプログラムが見つかりません。アンインストールされた可能性があります。リストの下の「他のエージェントをインストール」にインストールコマンドがあります。",
|
||||
@@ -11823,6 +11832,9 @@ const I18N = {
|
||||
"{agent} has an update": "Für {agent} gibt es ein Update",
|
||||
"Every agent is up to date": "Alle Agenten sind aktuell",
|
||||
"{ok} of {n} agents updated · {failed} didn't: {names}": "{ok} von {n} Agenten aktualisiert · {failed} fehlgeschlagen: {names}",
|
||||
"{agent} updated: {from} → {v}": "{agent} aktualisiert: {from} → {v}",
|
||||
"{n} agents updated: {list}": "{n} Agenten aktualisiert: {list}",
|
||||
"{ok} of {n} agents updated: {list} · {failed} didn't: {names}": "{ok} von {n} Agenten aktualisiert: {list} · {failed} fehlgeschlagen: {names}",
|
||||
"Install another agent ({n})": "Weiteren Agenten installieren ({n})",
|
||||
"CLI not found": "CLI nicht gefunden",
|
||||
"{agent}'s settings are still here ({path}), but its command-line program isn't found: it may have been uninstalled. Install another agent, below the list, has its install command.": "Die Einstellungen von {agent} sind noch da ({path}), aber sein Kommandozeilenprogramm wird nicht gefunden: Es wurde vielleicht deinstalliert. Unter „Weiteren Agenten installieren“ unterhalb der Liste steht sein Installationsbefehl.",
|
||||
|
||||
@@ -129,8 +129,9 @@ for (const engine of (process.env.BROWSER ? [process.env.BROWSER] : ["chromium",
|
||||
await page.waitForFunction(() => /didn't/.test(document.querySelector("#agentsUpdates .ag-updates-say")?.textContent || ""), null, { timeout: 4000 });
|
||||
assert.deepEqual(log.posts, ["codex", "gemini", "crush"]);
|
||||
assert.equal(log.most, 1, "two updates ran at once");
|
||||
assert.equal(await bar.locator(".ag-updates-say").textContent(), "2 of 3 agents updated · 1 didn't: Gemini CLI");
|
||||
assert.match(await bar.locator(".ag-updates-say").getAttribute("title"), /EACCES/);
|
||||
// each one updated with the versions it went from and to (37FlowAI on X)
|
||||
assert.equal(await bar.locator(".ag-updates-say").textContent(), "2 of 3 agents updated: Codex 0.155.1 → 0.159.0, Crush 0.3.0 → 0.4.0 · 1 didn't: Gemini CLI");
|
||||
assert.match(await bar.locator(".ag-updates-say").getAttribute("title"), /^Codex 0\.155\.1 → 0\.159\.0\nCrush 0\.3\.0 → 0\.4\.0\n.*EACCES/);
|
||||
assert.match(await page.locator("#status").textContent(), /EACCES/);
|
||||
// updated ones lose their pill; the failed one keeps it to try again
|
||||
assert.equal(await page.locator(`${row("codex")} .ag-ver`).textContent(), "0.159.0");
|
||||
@@ -142,6 +143,30 @@ for (const engine of (process.env.BROWSER ? [process.env.BROWSER] : ["chromium",
|
||||
assert.equal(await view.evaluate((v) => v.scrollTop), top, "the run moved the page");
|
||||
});
|
||||
|
||||
await t.test("all updated: the line lists each one's versions, in one line", async () => {
|
||||
for (const [lang, want] of [
|
||||
["en", "2 agents updated: Codex 0.155.1 → 0.159.0, Crush 0.3.0 → 0.4.0"],
|
||||
["zh", "已更新 2 个 Agent:Codex 0.155.1 → 0.159.0, Crush 0.3.0 → 0.4.0"],
|
||||
["ja", "2 個のエージェントをアップデートしました:Codex 0.155.1 → 0.159.0, Crush 0.3.0 → 0.4.0"],
|
||||
["de", "2 Agenten aktualisiert: Codex 0.155.1 → 0.159.0, Crush 0.3.0 → 0.4.0"],
|
||||
]) {
|
||||
const page = await open(lang, { clis: { codex: CLIS.codex, crush: CLIS.crush } });
|
||||
const bar = page.locator("#agentsUpdates");
|
||||
await bar.waitFor();
|
||||
const h = await bar.evaluate((b) => b.getBoundingClientRect().height);
|
||||
await bar.locator("button").click();
|
||||
await page.waitForFunction(() => !document.querySelector("#agentsUpdates button"), null, { timeout: 4000 });
|
||||
assert.equal(await bar.locator(".ag-updates-say").textContent(), want);
|
||||
assert.equal(await page.locator("#status").textContent(), want);
|
||||
assert.equal(await bar.locator(".ag-updates-say").getAttribute("title"), "Codex 0.155.1 → 0.159.0\nCrush 0.3.0 → 0.4.0");
|
||||
// narrow: still one line, cut short, the list under it not pushed down
|
||||
await page.setViewportSize({ width: 420, height: 520 });
|
||||
await page.waitForTimeout(200);
|
||||
assert.equal(await bar.evaluate((b) => b.getBoundingClientRect().height), h, "the line grew");
|
||||
assert.equal(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth), true, "the page scrolls sideways");
|
||||
}
|
||||
});
|
||||
|
||||
await t.test("a row's own pill leaves the line where it is", async () => {
|
||||
const page = await open("en", { clis: { codex: CLIS.codex, crush: CLIS.crush } });
|
||||
const bar = page.locator("#agentsUpdates");
|
||||
|
||||
@@ -139,7 +139,8 @@ for (const engine of (process.env.BROWSER ? [process.env.BROWSER] : ["chromium",
|
||||
assert.deepEqual(posts, ["codex"]);
|
||||
assert.equal(await page.locator(`${row("codex")} .ag-ver`).textContent(), "0.159.0");
|
||||
assert.equal(await page.locator(`${row("codex")} .ag-up`).count(), 0);
|
||||
assert.match(await page.locator("#status").textContent(), /Codex updated to 0\.159\.0/);
|
||||
// the version it went from too (37FlowAI on X)
|
||||
assert.equal(await page.locator("#status").textContent(), "Codex updated: 0.155.1 → 0.159.0");
|
||||
assert.equal(await view.evaluate((v) => v.scrollTop), top, "the update moved the page");
|
||||
const now = await page.locator(`${row("codex")} .ag-ver`).evaluate((b) => b.getBoundingClientRect().top);
|
||||
assert(Math.abs(now - was) <= 3, `the row moved ${now - was}px`);
|
||||
|
||||
Reference in new issue
Block a user