@songsid/agend 2.3.0-beta.1 → 2.3.0-beta.3
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/agent-cli.js +5 -5
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-file-read.d.ts +11 -6
- package/dist/agent-file-read.js +32 -17
- package/dist/agent-file-read.js.map +1 -1
- package/dist/agent-whats-new.js +10 -13
- package/dist/agent-whats-new.js.map +1 -1
- package/dist/auth-api.js +10 -5
- package/dist/auth-api.js.map +1 -1
- package/dist/backend/claude-code.d.ts +2 -2
- package/dist/backend/claude-code.js +10 -5
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/types.d.ts +13 -0
- package/dist/backend/types.js +10 -0
- package/dist/backend/types.js.map +1 -1
- package/dist/cache-analysis.d.ts +47 -1
- package/dist/cache-analysis.js +68 -8
- package/dist/cache-analysis.js.map +1 -1
- package/dist/cache-report-tool.d.ts +17 -0
- package/dist/cache-report-tool.js +40 -0
- package/dist/cache-report-tool.js.map +1 -0
- package/dist/cache-service.d.ts +15 -0
- package/dist/cache-service.js +26 -1
- package/dist/cache-service.js.map +1 -1
- package/dist/channel/mcp-tools.js +9 -3
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/classic-channel-manager.d.ts +8 -3
- package/dist/classic-channel-manager.js +10 -3
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli.js +7 -3
- package/dist/cli.js.map +1 -1
- package/dist/config-validator.js +5 -1
- package/dist/config-validator.js.map +1 -1
- package/dist/daemon.js +5 -5
- package/dist/daemon.js.map +1 -1
- package/dist/delivery-outbox.js +5 -0
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/fleet-manager.d.ts +71 -3
- package/dist/fleet-manager.js +256 -45
- package/dist/fleet-manager.js.map +1 -1
- package/dist/fleet-traffic.d.ts +38 -0
- package/dist/fleet-traffic.js +96 -0
- package/dist/fleet-traffic.js.map +1 -0
- package/dist/general-knowledge/skills/credential-profiles/SKILL.md +20 -5
- package/dist/general-knowledge/skills/multi-channel/SKILL.md +1 -1
- package/dist/general-knowledge/skills/web-appearance/SKILL.md +149 -5
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +398 -0
- package/dist/locale.js +6 -4
- package/dist/locale.js.map +1 -1
- package/dist/memory-hogs.d.ts +27 -0
- package/dist/memory-hogs.js +124 -0
- package/dist/memory-hogs.js.map +1 -0
- package/dist/outbound-handlers.d.ts +4 -0
- package/dist/outbound-handlers.js +74 -1
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +98 -0
- package/dist/outbound-schemas.js +56 -0
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/public-web-gateway.js +1 -1
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/scheduler/scheduler.d.ts +12 -0
- package/dist/scheduler/scheduler.js +83 -10
- package/dist/scheduler/scheduler.js.map +1 -1
- package/dist/service-installer.d.ts +2 -1
- package/dist/service-installer.js +18 -3
- package/dist/service-installer.js.map +1 -1
- package/dist/tmux-manager.d.ts +5 -0
- package/dist/tmux-manager.js +16 -0
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tool-permissions.d.ts +5 -0
- package/dist/tool-permissions.js +15 -2
- package/dist/tool-permissions.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/ui/app-effects.js +33 -9
- package/dist/ui/chat-effects.js +62 -4
- package/dist/ui/chat-strings.js +40 -4
- package/dist/ui/chat-thread.js +13 -5
- package/dist/ui/effect-trial-panel.js +19 -16
- package/dist/ui/fleet-cache.js +23 -1
- package/dist/ui/fleet-canvas-layout.js +111 -0
- package/dist/ui/fleet-canvas.js +489 -0
- package/dist/ui/fleet-org.js +27 -12
- package/dist/ui/fleet-traffic.js +224 -0
- package/dist/ui/panel-chat.js +8 -2
- package/dist/ui/panel-details.js +2 -1
- package/dist/ui/panel-fleet.js +17 -5
- package/dist/ui/panel-settings.js +3 -2
- package/dist/ui/preview.js +3 -2
- package/dist/ui/settings-appearance.js +34 -18
- package/dist/ui/settings-dialogs.js +3 -6
- package/dist/ui/settings-effects.js +4 -3
- package/dist/ui/settings-learn.js +25 -0
- package/dist/ui/settings-model.js +6 -2
- package/dist/ui/settings-mods.js +4 -3
- package/dist/ui/settings-owner-effects.js +8 -8
- package/dist/ui/settings-strings.js +34 -38
- package/dist/ui/shared/app-i18n.js +2 -2
- package/dist/ui/shared/app-prefs.js +1 -1
- package/dist/ui/shared/app-shell.js +23 -1
- package/dist/ui/shared/app-store.js +12 -1
- package/dist/ui/shared/app-stream.js +2 -1
- package/dist/ui/shared/app.css +163 -12
- package/dist/ui/shared/effect-app-runtime.js +1 -1
- package/dist/ui/shared/effect-frame.js +10 -5
- package/dist/ui/shared/effect-geometry.js +24 -9
- package/dist/ui/shared/effect-policy.js +2 -1
- package/dist/ui/shared/effect-registry.js +71 -17
- package/dist/ui/shared/effect-trial-shadow.js +62 -0
- package/dist/ui/shared/effect-trial-stage.js +15 -9
- package/dist/ui/shared/effect-trial.css +40 -0
- package/dist/ui/shared/identity-card.js +5 -2
- package/dist/ui/shared/identity-color.js +3 -3
- package/dist/ui/shared/instance-nav.js +35 -9
- package/dist/ui/shared/panel-view.js +2 -2
- package/dist/ui/shared/usage-dialog.js +14 -2
- package/dist/ui/shared/view-strings.js +22 -2
- package/dist/ui/signin.html +1 -1
- package/dist/usage/providers.d.ts +3 -0
- package/dist/usage/providers.js +4 -3
- package/dist/usage/providers.js.map +1 -1
- package/dist/view-api.d.ts +21 -0
- package/dist/view-api.js +28 -0
- package/dist/view-api.js.map +1 -1
- package/dist/web-api.d.ts +5 -6
- package/dist/web-api.js +60 -11
- package/dist/web-api.js.map +1 -1
- package/dist/web-appearance-view.d.ts +10 -0
- package/dist/web-appearance-view.js +77 -8
- package/dist/web-appearance-view.js.map +1 -1
- package/dist/web-asset-parts.d.ts +3 -8
- package/dist/web-asset-parts.js.map +1 -1
- package/dist/web-auth.js +1 -1
- package/dist/web-auth.js.map +1 -1
- package/dist/web-effect-service.js +3 -1
- package/dist/web-effect-service.js.map +1 -1
- package/dist/web-effect-tools.js +4 -1
- package/dist/web-effect-tools.js.map +1 -1
- package/dist/web-effect-trials.d.ts +4 -4
- package/dist/web-effect-trials.js +6 -6
- package/dist/web-effect-trials.js.map +1 -1
- package/dist/web-effect-upload.d.ts +2 -4
- package/dist/web-effect-upload.js +3 -8
- package/dist/web-effect-upload.js.map +1 -1
- package/dist/web-effect.d.ts +14 -0
- package/dist/web-effect.js +21 -0
- package/dist/web-effect.js.map +1 -1
- package/dist/web-guest-access.d.ts +67 -0
- package/dist/web-guest-access.js +126 -0
- package/dist/web-guest-access.js.map +1 -0
- package/dist/web-identity-palette.d.ts +1 -1
- package/dist/web-identity-palette.js +2 -2
- package/dist/web-identity-palette.js.map +1 -1
- package/dist/web-mod-agent.js +5 -3
- package/dist/web-mod-agent.js.map +1 -1
- package/dist/web-mod-bundle.d.ts +5 -0
- package/dist/web-mod-bundle.js +6 -0
- package/dist/web-mod-bundle.js.map +1 -1
- package/dist/web-mod-store.d.ts +5 -0
- package/dist/web-mod-store.js +8 -1
- package/dist/web-mod-store.js.map +1 -1
- package/dist/web-prefs.d.ts +8 -0
- package/dist/web-prefs.js +9 -0
- package/dist/web-prefs.js.map +1 -1
- package/dist/web-preview.d.ts +6 -2
- package/dist/web-preview.js +4 -2
- package/dist/web-preview.js.map +1 -1
- package/dist/web-session.d.ts +15 -7
- package/dist/web-session.js +25 -8
- package/dist/web-session.js.map +1 -1
- package/dist/web-settings-tools.d.ts +69 -0
- package/dist/web-settings-tools.js +247 -0
- package/dist/web-settings-tools.js.map +1 -0
- package/package.json +1 -1
- package/templates/systemd.service.ejs +8 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/** #2039: fixed host-only closed shadow shell. Owner CSS has no exported parts or slots into it. */
|
|
2
|
+
const roots = new WeakMap();
|
|
3
|
+
const clear = root => { while (root.firstChild) root.removeChild(root.firstChild); };
|
|
4
|
+
function sheetOf(host, record) {
|
|
5
|
+
try {
|
|
6
|
+
const link = record.link, sheet = link?.sheet;
|
|
7
|
+
return record.ready && !record.dead && roots.get(record.panel)?.current === record && record.root.mode === "closed" && record.root.host === record.panel
|
|
8
|
+
&& link.getRootNode() === record.root && link.isConnected && link.disabled === false && sheet?.ownerNode === link && sheet.disabled === false
|
|
9
|
+
&& sheet.href === new URL("/assets/effect-trial.css", host.location.origin).href && typeof sheet.insertRule === "function" && typeof sheet.deleteRule === "function" && sheet.cssRules ? sheet : null;
|
|
10
|
+
} catch { return null; }
|
|
11
|
+
}
|
|
12
|
+
/** Reflect only a shell issued here, after its fixed same-origin stylesheet loaded. Never a serialized capability. */
|
|
13
|
+
export function effectTrialShadow(host, panel) {
|
|
14
|
+
const record = roots.get(panel)?.current;
|
|
15
|
+
return record && sheetOf(host, record) === record.proof?.sheet ? record.proof : null;
|
|
16
|
+
}
|
|
17
|
+
export function createEffectTrialShadow({ window: host, panel, title, labels, onStop, onClose, onReady, onError } = {}) {
|
|
18
|
+
if (!host?.document || panel?.ownerDocument !== host.document || typeof panel.attachShadow !== "function" || typeof onReady !== "function" || typeof onError !== "function") return null;
|
|
19
|
+
let slot = roots.get(panel), cleanup = null;
|
|
20
|
+
const clock = () => (host.performance ?? globalThis.performance).now(), later = host.setTimeout?.bind(host) ?? globalThis.setTimeout, cancel = host.clearTimeout?.bind(host) ?? globalThis.clearTimeout;
|
|
21
|
+
try {
|
|
22
|
+
if (slot?.current && !slot.current.dead) return null;
|
|
23
|
+
if (!slot) { slot = { root: panel.attachShadow({ mode: "closed" }), current: null }; roots.set(panel, slot); }
|
|
24
|
+
const root = slot.root; clear(root);
|
|
25
|
+
const doc = host.document, element = (tag, cls) => { const el = doc.createElement(tag); el.className = cls; return el; };
|
|
26
|
+
const link = element("link", "effect-trial-style"); link.setAttribute("rel", "stylesheet"); link.setAttribute("href", "/assets/effect-trial.css");
|
|
27
|
+
const head = element("header", "effect-trial-head"), label = element("strong", "effect-trial-label"), name = element("span", "effect-trial-title"), close = element("button", "effect-trial-close"), controls = element("div", "effect-trial-controls"), stop = element("button", "effect-trial-stop"), body = element("div", "effect-trial-body"), stage = element("div", "effect-trial-stage"), note = element("p", "effect-trial-note");
|
|
28
|
+
close.setAttribute("type", "button"); stop.setAttribute("type", "button"); stage.setAttribute("aria-hidden", "true");
|
|
29
|
+
head.append(label, name, close); controls.append(stop); body.append(stage);
|
|
30
|
+
const nodes = [head, controls, body, note]; for (const el of nodes) el.setAttribute("hidden", "");
|
|
31
|
+
const began = clock(); if (!Number.isFinite(began) || began < 0) return null;
|
|
32
|
+
let timer = null;
|
|
33
|
+
const record = { panel, root, link, stage, head, controls, body, note, close, stop, ready: false, dead: false, proof: null }; slot.current = record;
|
|
34
|
+
const text = (nextTitle, nextLabels) => {
|
|
35
|
+
if (record.dead || !nextLabels) return;
|
|
36
|
+
label.textContent = nextLabels.label; name.textContent = typeof nextTitle === "string" ? nextTitle : "";
|
|
37
|
+
close.textContent = nextLabels.close; stop.textContent = nextLabels.stop; note.textContent = nextLabels.note;
|
|
38
|
+
panel.setAttribute("data-effect-trial-theme", doc.documentElement.dataset.theme === "dark" ? "dark" : "light");
|
|
39
|
+
};
|
|
40
|
+
const failed = () => { if (!record.dead) { dispose(); try { onError("stage_unavailable"); } catch { /* cleanup already owns failure */ } } };
|
|
41
|
+
const loaded = () => {
|
|
42
|
+
if (record.dead || record.ready || slot.current !== record) return;
|
|
43
|
+
const at = clock(); if (!Number.isFinite(at) || at < began || at - began >= 10000) { failed(); return; }
|
|
44
|
+
record.ready = true; const sheet = sheetOf(host, record);
|
|
45
|
+
if (!sheet) { failed(); return; }
|
|
46
|
+
cancel(timer); timer = null;
|
|
47
|
+
record.proof = Object.freeze({ panel, root, link, sheet, stage, head, controls, body, note, close, stop });
|
|
48
|
+
for (const el of nodes) el.removeAttribute("hidden");
|
|
49
|
+
try { onReady(record.proof); } catch { failed(); }
|
|
50
|
+
};
|
|
51
|
+
const stopClick = () => { if (!record.dead) onStop?.(); }, closeClick = () => { if (!record.dead) onClose?.(); };
|
|
52
|
+
function dispose() {
|
|
53
|
+
if (record.dead) return; record.dead = true; cancel(timer); timer = null;
|
|
54
|
+
link.removeEventListener("load", loaded); link.removeEventListener("error", failed); stop.removeEventListener("click", stopClick); close.removeEventListener("click", closeClick);
|
|
55
|
+
if (slot.current === record) { slot.current = null; clear(root); }
|
|
56
|
+
}
|
|
57
|
+
cleanup = dispose;
|
|
58
|
+
link.addEventListener("load", loaded); link.addEventListener("error", failed); stop.addEventListener("click", stopClick); close.addEventListener("click", closeClick);
|
|
59
|
+
text(title, labels); timer = later(failed, 10000); root.append(link, ...nodes);
|
|
60
|
+
return Object.freeze({ dispose, text });
|
|
61
|
+
} catch { cleanup?.(); return null; }
|
|
62
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/** #1629: host-issued LOCAL trial paint receipt. No authority, loader, frame factory or pool is created here. */
|
|
2
|
+
import { effectTrialShadow } from "./effect-trial-shadow.js";
|
|
2
3
|
import { effectHostStyle } from "./effect-host-style.js";
|
|
3
4
|
import { effectStockSnapshot } from "./effect-stock-snapshot.js";
|
|
4
5
|
import { STOCK_EFFECT_BARRIERS } from "./effect-stock-trust.js";
|
|
@@ -58,10 +59,11 @@ export function effectTrialStage(receipt, expected, stock) {
|
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
/** Only the fixed host TrialPanel supplies these elements and a current server-run lease. Not an agent manifest. */
|
|
61
|
-
export function createEffectTrialStage({ window: host, panel, stage, binding: raw, current,
|
|
62
|
+
export function createEffectTrialStage({ window: host, panel, stage, binding: raw, current, onStop = () => {} } = {}) {
|
|
62
63
|
const own = binding(raw);
|
|
63
|
-
if (!own || typeof current !== "function" || typeof
|
|
64
|
-
|
|
64
|
+
if (!own || typeof current !== "function" || typeof onStop !== "function" || !host?.document || !panel || !stage || owners.has(stage)) return null;
|
|
65
|
+
const shell = effectTrialShadow(host, panel), hostSheet = effectHostStyle(host);
|
|
66
|
+
let sheet; try { sheet = shell?.sheet; if (!shell || shell.stage !== stage || !hostSheet || !sheet || host.CSS?.supports("contain", "paint") !== true || host.CSS.supports("overflow", "clip") !== true) return null; } catch { return null; }
|
|
65
67
|
const receipt = Object.freeze(Object.create(null)), frames = new Set(), listeners = new Set(); let dead = false, constraint = null;
|
|
66
68
|
const identity = Object.freeze({ key: own.key, id: own.id, instance: own.instance, generation: own.generation, serial: ++serial });
|
|
67
69
|
const invalidate = reason => {
|
|
@@ -76,23 +78,27 @@ export function createEffectTrialStage({ window: host, panel, stage, binding: ra
|
|
|
76
78
|
function check(stock) {
|
|
77
79
|
try {
|
|
78
80
|
// This callback may reenter disposal or replace the owner. Recheck identity after it returns.
|
|
79
|
-
if (dead || current(own) !== true || constraint !== null && constraint() !== true || dead || owners.get(stage) !== receipt ||
|
|
81
|
+
if (dead || current(own) !== true || constraint !== null && constraint() !== true || dead || owners.get(stage) !== receipt || dead) return null;
|
|
80
82
|
const proof = effectStockSnapshot(stock), doc = host.document;
|
|
81
|
-
if (effectHostStyle(host) !==
|
|
83
|
+
if (effectHostStyle(host) !== hostSheet || effectTrialShadow(host, panel) !== shell) return null;
|
|
82
84
|
// The own panel MUST still suppress ordinary foreground effects. Never synthesize front:true.
|
|
83
85
|
if (!proof || proof.front !== false || doc.visibilityState !== "visible" || doc.hasFocus?.() !== true || doc.fullscreenElement || doc.pictureInPictureElement) return null;
|
|
84
86
|
const split = doc.querySelector(".main > .p-chat > .chat-split");
|
|
85
87
|
if (!split || doc.querySelectorAll(".main > .p-chat").length !== 1 || doc.querySelectorAll(".chat-split > .pv-panel").length !== 1
|
|
86
88
|
|| panel.parentElement !== split || panel.ownerDocument !== doc || panel.isConnected !== true || panel.className !== "pv-panel effect-trial-panel" || panel.getAttribute("data-trust") !== "preview") return null;
|
|
87
|
-
const children = [...
|
|
88
|
-
if (children.length !== names.length || children.some((el, i) => el.className !== names[i]) || children[
|
|
89
|
+
const children = [...shell.root.children], names = ["effect-trial-style", "effect-trial-head", "effect-trial-controls", "effect-trial-body", "effect-trial-note"];
|
|
90
|
+
if (panel.children.length !== 0 || children.length !== names.length || children.some((el, i) => el.className !== names[i]) || children[3].children.length !== 1 || children[3].firstElementChild !== stage
|
|
89
91
|
|| stage.className !== "effect-trial-stage" || stage.ownerDocument !== doc || stage.isConnected !== true || stage.hasAttribute("tabindex") || stage.hasAttribute("contenteditable")) return null;
|
|
92
|
+
for (const control of [shell.head, shell.controls, shell.close, shell.stop, shell.note]) {
|
|
93
|
+
const style = host.getComputedStyle(control);
|
|
94
|
+
if (style.display === "none" || style.visibility !== "visible" || style.opacity !== "1" || !rect(control)) return null;
|
|
95
|
+
}
|
|
90
96
|
if ([...doc.querySelectorAll(STOCK_EFFECT_BARRIERS)].some(el => el !== panel && visible(host, el))
|
|
91
97
|
|| [...doc.querySelectorAll(".pv-panel")].some(el => el !== panel && visible(host, el))) return null;
|
|
92
98
|
const active = doc.activeElement;
|
|
93
99
|
if (active && active !== doc.body && active !== doc.documentElement && (active.matches("input,select,textarea,[contenteditable]") || active.isContentEditable)) return null;
|
|
94
100
|
let el = stage, depth = 0;
|
|
95
|
-
while (el) { const s = host.getComputedStyle(el); if (++depth > 128 || el.ownerDocument !== doc || !neutral(s) || s.opacity !== "1" || s.visibility !== "visible" || s.display === "none" || s.contain !== (el === stage ? "paint" : "none")) return null; if (el === doc.documentElement) break; el = el.parentElement; }
|
|
101
|
+
while (el) { const s = host.getComputedStyle(el); if (++depth > 128 || el.ownerDocument !== doc || !neutral(s) || s.opacity !== "1" || s.visibility !== "visible" || s.display === "none" || s.contain !== (el === stage ? "paint" : "none")) return null; if (el === doc.documentElement) break; el = el.parentElement ?? (el.getRootNode?.() === shell.root ? panel : null); }
|
|
96
102
|
if (el !== doc.documentElement) return null;
|
|
97
103
|
const s = host.getComputedStyle(stage), r = rect(stage), p = rect(panel);
|
|
98
104
|
if (!r || !p || s.position !== "relative" || s.zIndex !== "0" || s.overflowX !== "clip" || s.overflowY !== "clip"
|
|
@@ -103,7 +109,7 @@ export function createEffectTrialStage({ window: host, panel, stage, binding: ra
|
|
|
103
109
|
if ([...stage.children].some(child => ![...frames].some(f => f.element === child))) return null;
|
|
104
110
|
const width = Math.floor(r.width), height = Math.floor(r.height);
|
|
105
111
|
if (width < 1 || height < 1 || width > 4096 || height > 4096 || width * height > 8388608 || dead || owners.get(stage) !== receipt) return null;
|
|
106
|
-
if (current(own) !== true || constraint !== null && constraint() !== true || dead || owners.get(stage) !== receipt ||
|
|
112
|
+
if (current(own) !== true || constraint !== null && constraint() !== true || dead || owners.get(stage) !== receipt || dead || stage.isConnected !== true || panel.parentElement !== split) return null;
|
|
107
113
|
return { x: r.x, y: r.y, width, height };
|
|
108
114
|
} catch { return null; }
|
|
109
115
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/* #2039: fixed closed-shadow Trial chrome; no inherited owner-CSS typography, parts or slots. */
|
|
2
|
+
:host {
|
|
3
|
+
all: initial !important;
|
|
4
|
+
box-sizing: border-box !important;
|
|
5
|
+
display: flex !important;
|
|
6
|
+
flex: 0 0 45% !important;
|
|
7
|
+
flex-direction: column !important;
|
|
8
|
+
align-self: stretch !important;
|
|
9
|
+
width: 45% !important;
|
|
10
|
+
min-width: 320px !important;
|
|
11
|
+
max-width: calc(100% - 368px) !important;
|
|
12
|
+
min-height: 0 !important;
|
|
13
|
+
height: auto !important;
|
|
14
|
+
position: relative !important;
|
|
15
|
+
z-index: 1 !important;
|
|
16
|
+
overflow: clip !important;
|
|
17
|
+
background: #ffffff !important;
|
|
18
|
+
color: #1b1b1f !important;
|
|
19
|
+
font: 400 16px/1.4 system-ui, sans-serif !important;
|
|
20
|
+
direction: ltr !important;
|
|
21
|
+
unicode-bidi: isolate !important;
|
|
22
|
+
text-align: start !important;
|
|
23
|
+
visibility: visible !important;
|
|
24
|
+
opacity: 1 !important;
|
|
25
|
+
}
|
|
26
|
+
:host([data-effect-trial-theme="dark"]) { background: #202024 !important; color: #f0f0f4 !important; }
|
|
27
|
+
:host::before, :host::after { all: initial !important; content: none !important; display: none !important; }
|
|
28
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
29
|
+
[hidden] { display: none !important; }
|
|
30
|
+
.effect-trial-head, .effect-trial-controls, .effect-trial-note { position: relative; z-index: 1; padding: 12px; margin: 0; }
|
|
31
|
+
.effect-trial-head { display: flex; align-items: center; gap: 8px; border-bottom: 1px solid currentColor; }
|
|
32
|
+
.effect-trial-title { flex: 1; min-width: 0; overflow-wrap: anywhere; }
|
|
33
|
+
button { appearance: auto; display: inline-block; font: inherit; color: inherit; background: transparent; border: 1px solid currentColor; border-radius: 6px; padding: 8px 12px; cursor: pointer; }
|
|
34
|
+
button:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
|
|
35
|
+
.effect-trial-body { flex: 1; min-height: 0; display: flex; padding: 12px; }
|
|
36
|
+
.effect-trial-stage { position: relative; z-index: 0; flex: 1; min-width: 0; min-height: 0; contain: paint; overflow: clip; padding: 0; border: 0; outline: none; box-shadow: none; }
|
|
37
|
+
.effect-trial-note { font-size: 14px; }
|
|
38
|
+
@media (max-width: 899px) {
|
|
39
|
+
:host { position: fixed !important; inset: 0 !important; z-index: 37 !important; width: auto !important; min-width: 0 !important; max-width: none !important; padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left) !important; }
|
|
40
|
+
}
|
|
@@ -135,7 +135,8 @@ function SizeControl({ name, size }) {
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
/**
|
|
138
|
-
* The card. `compact`: the chat header's own small size (no size control
|
|
138
|
+
* The card. `compact`: the chat header's own small size (no size control; the banner still follows the size choice,
|
|
139
|
+
* #2038). Nothing when the card is empty. The
|
|
139
140
|
* tagline and bio say whose they are (title), since a person's field wins over the agent's.
|
|
140
141
|
*/
|
|
141
142
|
export function IdentityCard({ name, card, compact = false, sizeControl = !compact }) {
|
|
@@ -143,10 +144,12 @@ export function IdentityCard({ name, card, compact = false, sizeControl = !compa
|
|
|
143
144
|
const [broken, setBroken] = useState({});
|
|
144
145
|
if (!card) return null;
|
|
145
146
|
const size = compact ? "c" : cardSizeFor(name);
|
|
147
|
+
// #2038: the chat header's card is its own small size, but its banner follows the person's size choice too.
|
|
148
|
+
const bannerSize = compact ? ` bs-${cardSizeFor(name)}` : "";
|
|
146
149
|
const whose = (f) => t(f.by === "human" ? "card.byHuman" : "card.byAgent");
|
|
147
150
|
const pic = (slot, cls, alt) => card[slot] && broken[slot] !== card[slot].src
|
|
148
151
|
? html`<img class=${cls} src=${card[slot].src} alt=${alt} title=${whose(card[slot])} decoding="async" onError=${() => setBroken({ ...broken, [slot]: card[slot].src })} />` : null;
|
|
149
|
-
return html`<section class=${`id-card card-${size}`} aria-label=${t("card.label", name)}>
|
|
152
|
+
return html`<section class=${`id-card card-${size}${bannerSize}`} aria-label=${t("card.label", name)}>
|
|
150
153
|
${pic("banner", "id-banner", "")}
|
|
151
154
|
<div class="id-row">
|
|
152
155
|
${pic("portrait", "id-portrait", t("card.portraitAlt", name))}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// (`color`, `color_show`); this module resolves them as src/web-identity-color.ts does (a test pins the two equal),
|
|
4
4
|
// paints the sidebar row through the CSSOM (no style attribute, #1300), and holds the Color dialog.
|
|
5
5
|
// colour: a person's pick > the agent's (themes shown) > its own bot's role colour > automatic (a few soft colours)
|
|
6
|
-
// places: a person's choice > the agent's (themes shown) > this host's default (color.show) >
|
|
6
|
+
// places: a person's choice > the agent's (themes shown) > this host's default (color.show) > all three (#2056)
|
|
7
7
|
// Settings → Web appearance can turn colours off for the host (color.off): nothing is painted, every pick is kept.
|
|
8
8
|
import { html, useEffect, useState } from "./app-html.js";
|
|
9
9
|
import { createStore, useStore } from "./app-store.js";
|
|
@@ -30,7 +30,7 @@ export const AUTO_PALETTE = [
|
|
|
30
30
|
{ id: "moss", light: "#5b7340", dark: "#a9c48d" }, { id: "slate", light: "#5f6b78", dark: "#aeb8c4" },
|
|
31
31
|
];
|
|
32
32
|
export const PLACEMENTS = ["name", "bar", "ring"];
|
|
33
|
-
export const DEFAULT_PLACEMENTS = ["bar", "ring"];
|
|
33
|
+
export const DEFAULT_PLACEMENTS = ["name", "bar", "ring"]; // #2056: all three where nobody chose (was ring + bar)
|
|
34
34
|
const HEX = /^#[0-9a-f]{6}$/;
|
|
35
35
|
const presetOf = (c) => (typeof c === "string" && c.startsWith("preset:") ? IDENTITY_PALETTE.find((p) => p.id === c.slice(7)) : undefined);
|
|
36
36
|
const autoOf = (c) => (typeof c === "string" && c.startsWith("auto:") ? AUTO_PALETTE.find((p) => p.id === c.slice(5)) : undefined);
|
|
@@ -115,7 +115,7 @@ export function colorsOff() {
|
|
|
115
115
|
const f = prefInForce("color.off");
|
|
116
116
|
return f ? f.value === true : readLocal("agend_color_off") === "on";
|
|
117
117
|
}
|
|
118
|
-
/** The default places (null: back to
|
|
118
|
+
/** The default places (null: back to all three, #2056). */
|
|
119
119
|
export function setDefaultPlaces(list) {
|
|
120
120
|
const v = list === null ? null : placesOk(list);
|
|
121
121
|
const same = v && v.length === DEFAULT_PLACEMENTS.length && v.every((p, i) => p === DEFAULT_PLACEMENTS[i]);
|
|
@@ -36,6 +36,22 @@ export function groupOf(it) {
|
|
|
36
36
|
if (tags.length) return tags[0];
|
|
37
37
|
return "Other";
|
|
38
38
|
}
|
|
39
|
+
/** A group's heading: the fallback group ("Other") is a key, shown in the page's language; a tag is shown as it is. */
|
|
40
|
+
export function groupLabel(g) { return g === "Other" ? tn("groupOther") : g; }
|
|
41
|
+
/**
|
|
42
|
+
* #2050: the model as a person reads it. The server's resolver writes English for a model it has not resolved yet
|
|
43
|
+
* ("default (detected when it starts)") and for a CLI's own default ("x (default)"); those are shown in the page's language.
|
|
44
|
+
*/
|
|
45
|
+
export function modelText(it, raw = it && it.model) {
|
|
46
|
+
if (!raw || typeof raw !== "string") return raw;
|
|
47
|
+
if (it.model_source === "unresolved") {
|
|
48
|
+
const why = /^default \((.*)\)$/.exec(raw)?.[1];
|
|
49
|
+
const reason = why === "detected when it starts" ? tn("modelWhyStart") : why === "this CLI does not report a default" ? tn("modelWhyCli") : why;
|
|
50
|
+
return reason ? tn("modelDefaultWhy", { reason }) : raw === "default" ? tn("modelDefault") : raw;
|
|
51
|
+
}
|
|
52
|
+
if (it.model_source === "cli-default") { const m = /^(.*) \(default\)$/.exec(raw); if (m) return tn("modelCliDefault", { model: m[1] }); }
|
|
53
|
+
return raw;
|
|
54
|
+
}
|
|
39
55
|
const BACKEND_LABELS = { "claude-code": "Claude Code", "kiro-cli": "Kiro CLI", codex: "Codex", grok: "Grok Build", antigravity: "Antigravity", opencode: "OpenCode" };
|
|
40
56
|
export function backendLabel(backend) { return BACKEND_LABELS[backend] || backend || "Unknown"; }
|
|
41
57
|
export const KNOWN_BACKENDS = new Set(Object.keys(BACKEND_LABELS));
|
|
@@ -93,8 +109,8 @@ export function instanceTooltip(it) {
|
|
|
93
109
|
if (it.display_name && it.display_name !== name) lines.push(it.display_name, `(${name})`);
|
|
94
110
|
else lines.push(name);
|
|
95
111
|
lines.push(line("tooltipBackend", backendLabel(it.backend)));
|
|
96
|
-
const src = (s) => (s === "instance" || s === "classic" ? "
|
|
97
|
-
lines.push(line("tooltipModel", (it
|
|
112
|
+
const src = (s) => (s === "instance" || s === "classic" ? tn("srcConfigured") : s === "fleet-default" ? tn("srcFleetDefault") : "");
|
|
113
|
+
lines.push(line("tooltipModel", (modelText(it) || tn("tooltipUnavailable")) + src(it.model_source)));
|
|
98
114
|
lines.push(line("tooltipStatus", status));
|
|
99
115
|
lines.push(line("tooltipContext", context));
|
|
100
116
|
if (it.effort) lines.push(line("tooltipEffort", it.effort + src(it.effort_source)));
|
|
@@ -224,6 +240,8 @@ function writeFacetsOpen(open) { try { localStorage.setItem(FACETS_KEY, JSON.str
|
|
|
224
240
|
|
|
225
241
|
/** The Favorites group's key among the tag groups: no tag can be it (a group name is never a control character). */
|
|
226
242
|
const FAV_GROUP = "\u0000favorites";
|
|
243
|
+
/** #1936: the open instance, pinned on top while a filter is on. */
|
|
244
|
+
const PINNED_GROUP = "\u0000pinned";
|
|
227
245
|
function persistOrder(groupNames, instByGroup) {
|
|
228
246
|
const rows = [];
|
|
229
247
|
groupNames.forEach((g, i) => rows.push({ item_type: "group", item_name: g, sort_index: i, group_name: null }));
|
|
@@ -356,13 +374,12 @@ export function InstanceNav({ route, items, loaded = true, exec = {}, awaiting =
|
|
|
356
374
|
// #1664: "☆ Edit" — the stars on every row (on a phone the only way to them) and ↑ ↓ on the favorites — only on the
|
|
357
375
|
// full list, as drag: in a filtered one the row above may not be the one above in the list (#1677 review).
|
|
358
376
|
const [editing, setEditing] = useState(false);
|
|
359
|
-
// Where a row goes: the view the page is in (#1523 §3.1). View → View; Details,
|
|
360
|
-
//
|
|
377
|
+
// Where a row goes: the view the page is in (#1523 §3.1). View → View; Details → Details; everywhere else, a Fleet tab
|
|
378
|
+
// included (#1917: the user's call, replacing Q4 = A), → Chat. The anonymous reader only ever has View.
|
|
361
379
|
const rowPath = viewOnly || (route && route.panel === "view") ? viewPath
|
|
362
380
|
: route && route.panel === "log" ? logPath // #1218: Log → Log
|
|
363
|
-
: route &&
|
|
381
|
+
: route && route.panel === "details" ? detailsPath : chatPath;
|
|
364
382
|
const active = route && (route.panel === "chat" || route.panel === "view" || route.panel === "log" || route.panel === "details") ? route.instance : null;
|
|
365
|
-
useLayoutEffect(() => { keepActiveInView(list.current); }, [active, items.length, loaded]);
|
|
366
383
|
const own = (map, name) => Object.prototype.hasOwnProperty.call(map, name);
|
|
367
384
|
const byName = new Map(items.map((it) => [nameOf(it), it]));
|
|
368
385
|
const facetOf = (name) => statusFacet(byName.get(name), own(exec, name) ? exec[name] : null, own(awaiting, name) ? awaiting[name] : null);
|
|
@@ -379,6 +396,12 @@ export function InstanceNav({ route, items, loaded = true, exec = {}, awaiting =
|
|
|
379
396
|
if (favs.length) shownInsts.set(FAV_GROUP, favs);
|
|
380
397
|
const f = nav.filter;
|
|
381
398
|
const shown = filterSidebar(shownGroups, shownInsts, byName, f.q, backendLabel, { status: f.status, cli: f.cli, statusOf: facetOf });
|
|
399
|
+
// #1936: while a filter is on, the instance you have open stays on top, pinned and marked "Current", whether or not
|
|
400
|
+
// it matches; a match is not repeated below. "N / M" still counts the filter's matches (the pinned row counts only
|
|
401
|
+
// when it is one). Clearing the filter puts it back in its place, and the list scrolls to it again.
|
|
402
|
+
const pinned = shown.active && active && byName.has(active) ? active : null;
|
|
403
|
+
const listed = pinned ? shown.groups.map((x) => ({ ...x, names: x.names.filter((n) => n !== pinned) })).filter((x) => x.names.length) : shown.groups;
|
|
404
|
+
useLayoutEffect(() => { keepActiveInView(list.current); }, [active, items.length, loaded, shown.active]);
|
|
382
405
|
const backends = [...new Set(items.map((it) => it.backend).filter((b) => typeof b === "string" && b))].sort((a, b) => backendLabel(a).localeCompare(backendLabel(b)));
|
|
383
406
|
const toggle = (g) => { const c = new Set(navStore.get().collapsed); if (c.has(g)) c.delete(g); else c.add(g); setCollapsed(c); };
|
|
384
407
|
// From the store, not this render's copy: two clicks before the next render must both count.
|
|
@@ -463,7 +486,10 @@ export function InstanceNav({ route, items, loaded = true, exec = {}, awaiting =
|
|
|
463
486
|
${canStar && items.length ? html`<button type="button" class="chip-btn fav-edit" aria-pressed=${editing ? "true" : "false"} onClick=${() => setEditing(!editing)}>
|
|
464
487
|
${editing ? tn("favDone") : html`<${Icon} name="star" size=${12} />${tn("favEdit")}`}</button>` : null}</div>
|
|
465
488
|
${!loaded ? html`<${Skeleton} lines=${4} />` : !items.length ? html`<p class="side-empty">${t("app.noInstances")}</p>` : null}
|
|
466
|
-
${
|
|
489
|
+
${pinned ? html`<div key=${PINNED_GROUP} class="v-group v-pinned" role="group" aria-label=${tn("pinnedLabel")}>
|
|
490
|
+
<div class="v-group-head pinned-head"><span class="grow">${tn("pinnedCurrent")}</span></div>
|
|
491
|
+
<ul class="inst-list">${row(pinned, PINNED_GROUP, 0, 1)}</ul></div>` : null}
|
|
492
|
+
${listed.map(({ group: g, names }) => {
|
|
467
493
|
// #1664: Favorites — always open, not dragged (↑ ↓ in Edit), named in the page's language.
|
|
468
494
|
if (g === FAV_GROUP) return html`<div key=${g} class="v-group v-fav" role="group" aria-label=${tn("favorites")}>
|
|
469
495
|
<div class="v-group-head fav-head"><${Icon} name="star" size=${14} cls="fav-mark" /><span class="grow">${tn("favorites")}</span><span class="count">${names.length}</span></div>
|
|
@@ -471,8 +497,8 @@ export function InstanceNav({ route, items, loaded = true, exec = {}, awaiting =
|
|
|
471
497
|
const folded = nav.collapsed.has(g);
|
|
472
498
|
return html`<div key=${g} class="v-group">
|
|
473
499
|
<button type="button" class="v-group-head" aria-expanded=${folded ? "false" : "true"} onClick=${() => toggle(g)}
|
|
474
|
-
title=${tn(folded ? "expandGroup" : "collapseGroup", { group: g })} ...${dragProps({ type: "group", name: g })}>
|
|
475
|
-
<${Icon} name="chevron" size=${14} cls=${folded ? "caret folded" : "caret"} /><span class="grow">${g}</span><span class="count">${names.length}</span></button>
|
|
500
|
+
title=${tn(folded ? "expandGroup" : "collapseGroup", { group: groupLabel(g) })} ...${dragProps({ type: "group", name: g })}>
|
|
501
|
+
<${Icon} name="chevron" size=${14} cls=${folded ? "caret folded" : "caret"} /><span class="grow">${groupLabel(g)}</span><span class="count">${names.length}</span></button>
|
|
476
502
|
${folded ? null : html`<ul class="inst-list">${names.map((name, i) => row(name, g, i, names.length))}</ul>`}
|
|
477
503
|
</div>`;
|
|
478
504
|
})}
|
|
@@ -16,7 +16,7 @@ import { HeaderTools, VIEW_SCALE, textSizeStore } from "./header-tools.js";
|
|
|
16
16
|
import { readStream } from "./read-stream.js";
|
|
17
17
|
// The usage dialog's metric row lives with the dialog now (usage-dialog.js); re-exported for what imported it here.
|
|
18
18
|
export { UsageMetric } from "./usage-dialog.js";
|
|
19
|
-
import { backendLabel, STATUS_KEYS } from "./instance-nav.js";
|
|
19
|
+
import { backendLabel, modelText, STATUS_KEYS } from "./instance-nav.js";
|
|
20
20
|
import { viewStore } from "./view-roster-store.js";
|
|
21
21
|
|
|
22
22
|
/** #1580: the app's stream says the fleet is not answering (a restart): the View's polls skip their turn until it is. */
|
|
@@ -305,7 +305,7 @@ function Card({ it, viewOnly, onEdit }) {
|
|
|
305
305
|
<div class="v-avatar">${it.has_avatar ? html`<img ref=${avatar} alt="" />` : html`<span class="v-initial" aria-hidden="true">${initial}</span>`}</div>
|
|
306
306
|
<div class="v-card-body">
|
|
307
307
|
<div class="v-card-line"><strong>${it.display_name || it.instance_name}</strong>${it.role ? html`<span class="v-role">${it.role}</span>` : null}</div>
|
|
308
|
-
<div class="v-card-meta">${it.instance_name} · ${backendLabel(it.backend)}${it.model ? ` · ${it
|
|
308
|
+
<div class="v-card-meta">${it.instance_name} · ${backendLabel(it.backend)}${it.model ? ` · ${modelText(it)}` : ""}${ctx}</div>
|
|
309
309
|
${open && it.description ? html`<p class="v-card-desc">${it.description}</p>` : null}
|
|
310
310
|
</div>
|
|
311
311
|
<button type="button" class="icon-btn" onClick=${toggle} aria-expanded=${open ? "true" : "false"} aria-label=${tn(open ? "collapseProfile" : "expandProfile")} title=${tn(open ? "collapseProfile" : "expandProfile")}>
|
|
@@ -66,7 +66,17 @@ export function UsageMetric({ m }) {
|
|
|
66
66
|
const sub = [note, expiryText(m.expiresAt)].filter(Boolean).join(" · ");
|
|
67
67
|
return html`<div class="u-metric"><div class="u-row"><span class="u-label">${label}</span><span class="u-val">${val}</span></div>${sub ? html`<div class="u-sub">${sub}</div>` : null}</div>`;
|
|
68
68
|
}
|
|
69
|
-
/**
|
|
69
|
+
/**
|
|
70
|
+
* #2014: a row's title. A credential profile's row (`profile`; its API `name` is "Claude (<profile>)") shows the
|
|
71
|
+
* provider name on the title line beside the plan badge, and the profile on its own full-width muted line under it;
|
|
72
|
+
* the full name is the tooltip, and the dialog shows the profile unclipped. Any other row: its name.
|
|
73
|
+
*/
|
|
74
|
+
export function providerTitle(p) {
|
|
75
|
+
const name = String((p && p.name) || ""), profile = p && typeof p.profile === "string" && p.profile ? p.profile : "";
|
|
76
|
+
const suffix = ` (${profile})`;
|
|
77
|
+
return profile && name.endsWith(suffix) ? { base: name.slice(0, -suffix.length), profile } : { base: name, profile };
|
|
78
|
+
}
|
|
79
|
+
|
|
70
80
|
/**
|
|
71
81
|
* #1749: a plan as its badge reads — one short line. "Name [Tier] (N×)" (Claude's "Team Premium (6.25x)", "Max (20x)")
|
|
72
82
|
* becomes "Name ×N"; anything else (Pro, Codex's and muse's labels) as it is. The full name stays on the badge's
|
|
@@ -76,6 +86,7 @@ export function planBadge(plan) {
|
|
|
76
86
|
const m = /^(\S+)(?: [^()]+)? \((\d+(?:\.\d+)?)x\)$/i.exec(String(plan || "").trim());
|
|
77
87
|
return m ? `${m[1]} ×${m[2]}` : String(plan || "");
|
|
78
88
|
}
|
|
89
|
+
/** "Updated 3 min ago" (#1585): how old the numbers are — the panel may show the last snapshot while a fresh one comes. */
|
|
79
90
|
export function updatedAgo(fetchedAt, now = Date.now()) {
|
|
80
91
|
const ms = now - new Date(fetchedAt).getTime();
|
|
81
92
|
if (!Number.isFinite(ms) || ms < 60_000) return tn("usageUpdatedJustNow");
|
|
@@ -134,11 +145,12 @@ export function UsageBody({ owner }) {
|
|
|
134
145
|
const providers = ordered(data.providers);
|
|
135
146
|
content = html`${!providers.length ? html`<p class="note">${tn("usage.empty_active")}</p>` : null}
|
|
136
147
|
${providers.map((p, i) => html`<section key=${key(p)} class="u-provider">
|
|
137
|
-
<div class="u-head"><strong title=${p.name}>${p.
|
|
148
|
+
<div class="u-head"><strong title=${p.name}>${providerTitle(p).base}</strong>${p.plan ? html`<span class="pill u-plan" title=${p.plan}><span aria-hidden="true">${planBadge(p.plan)}</span><span class="sr-only">${p.plan}</span></span>` : null}
|
|
138
149
|
<span class="u-order">
|
|
139
150
|
<button type="button" class="icon-btn" disabled=${i === 0} aria-label=${tn("usageMoveUp")} title=${tn("usageMoveUp")} onClick=${() => move(key(p), -1, data.providers)}><${Icon} name="up" size=${14} /></button>
|
|
140
151
|
<button type="button" class="icon-btn" disabled=${i === providers.length - 1} aria-label=${tn("usageMoveDown")} title=${tn("usageMoveDown")} onClick=${() => move(key(p), 1, data.providers)}><${Icon} name="down" size=${14} /></button>
|
|
141
152
|
</span></div>
|
|
153
|
+
${providerTitle(p).profile ? html`<div class="u-profile" title=${p.name}>${providerTitle(p).profile}</div>` : null}
|
|
142
154
|
${p.stale ? html`<p class="note">${usageText(p.hint || "", p.hintI18n)}</p>` : null}
|
|
143
155
|
${p.status === "error" && !p.transient ? html`<p class="u-err">${usageText(p.error || tn("usage.error_fallback"), p.errorI18n)}</p>`
|
|
144
156
|
: p.status === "error" && p.transient ? html`<p class="note">${usageText(p.error || tn("usage.error_fallback"), p.errorI18n)}</p>`
|
|
@@ -51,6 +51,14 @@ register("view", {
|
|
|
51
51
|
"tooltipEffort": "Effort: {value}",
|
|
52
52
|
"tooltipCost": "Cost: {value}",
|
|
53
53
|
"tooltipUnavailable": "Unavailable",
|
|
54
|
+
"srcConfigured": " (configured)",
|
|
55
|
+
"srcFleetDefault": " (fleet default)",
|
|
56
|
+
"modelDefault": "default",
|
|
57
|
+
"modelDefaultWhy": "default ({reason})",
|
|
58
|
+
"modelCliDefault": "{model} (default)",
|
|
59
|
+
"modelWhyStart": "detected when it starts",
|
|
60
|
+
"modelWhyCli": "this CLI does not report a default",
|
|
61
|
+
"groupOther": "Other",
|
|
54
62
|
"statusRunning": "Running",
|
|
55
63
|
"statusPaused": "Paused",
|
|
56
64
|
"statusStopped": "Stopped",
|
|
@@ -197,7 +205,9 @@ register("view", {
|
|
|
197
205
|
"favUp": "Move {name} up",
|
|
198
206
|
"favDown": "Move {name} down",
|
|
199
207
|
"favFull": "Favorites are full ({max})",
|
|
200
|
-
"favFiltered": "Clear the filter to reorder favorites"
|
|
208
|
+
"favFiltered": "Clear the filter to reorder favorites",
|
|
209
|
+
"pinnedCurrent": "Current",
|
|
210
|
+
"pinnedLabel": "The instance you have open"
|
|
201
211
|
},
|
|
202
212
|
"zh-TW": {
|
|
203
213
|
"editProfile": "編輯個人資料",
|
|
@@ -246,6 +256,14 @@ register("view", {
|
|
|
246
256
|
"tooltipEffort": "推理強度:{value}",
|
|
247
257
|
"tooltipCost": "費用:{value}",
|
|
248
258
|
"tooltipUnavailable": "無資料",
|
|
259
|
+
"srcConfigured": "(設定值)",
|
|
260
|
+
"srcFleetDefault": "(fleet 預設)",
|
|
261
|
+
"modelDefault": "預設",
|
|
262
|
+
"modelDefaultWhy": "預設({reason})",
|
|
263
|
+
"modelCliDefault": "{model}(預設)",
|
|
264
|
+
"modelWhyStart": "啟動時偵測",
|
|
265
|
+
"modelWhyCli": "這個 CLI 不提供預設值",
|
|
266
|
+
"groupOther": "其他",
|
|
249
267
|
"statusRunning": "運行中",
|
|
250
268
|
"statusPaused": "已暫停",
|
|
251
269
|
"statusStopped": "已停止",
|
|
@@ -392,6 +410,8 @@ register("view", {
|
|
|
392
410
|
"favUp": "把 {name} 上移",
|
|
393
411
|
"favDown": "把 {name} 下移",
|
|
394
412
|
"favFull": "最愛已滿({max} 個)",
|
|
395
|
-
"favFiltered": "清除篩選後才能調整最愛順序"
|
|
413
|
+
"favFiltered": "清除篩選後才能調整最愛順序",
|
|
414
|
+
"pinnedCurrent": "目前",
|
|
415
|
+
"pinnedLabel": "你目前開著的 instance"
|
|
396
416
|
}
|
|
397
417
|
});
|
package/dist/ui/signin.html
CHANGED
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
<div id="msg" role="alert" aria-live="polite"></div>
|
|
56
56
|
<div class="hint" data-i18n="hint">No code? Send /web in General, then press a sign-in button to get one.</div>
|
|
57
57
|
<details class="help"><summary data-i18n="nobot">No chat bot?</summary><p data-i18n="nobot_body">Ask the operator to run <code>agend web --code</code> on the AgEnD computer.</p></details>
|
|
58
|
-
<details class="help"><summary data-i18n="safe">Page looks wrong after signing in?</summary><p data-i18n="safe_body">Your custom CSS may be hiding something. Add ?safe=1 to the end of the address to load the page without it, then fix it or turn it off in Settings →
|
|
58
|
+
<details class="help"><summary data-i18n="safe">Page looks wrong after signing in?</summary><p data-i18n="safe_body">Your custom CSS may be hiding something. Add ?safe=1 to the end of the address to load the page without it, then fix it or turn it off in Settings → Web appearance.</p></details>
|
|
59
59
|
</form>
|
|
60
60
|
<noscript>This page needs JavaScript to sign you in.</noscript>
|
|
61
61
|
</main>
|
|
@@ -34,6 +34,8 @@ export interface UsageMetric {
|
|
|
34
34
|
export interface ProviderUsage {
|
|
35
35
|
id: string;
|
|
36
36
|
name: string;
|
|
37
|
+
/** #2014: the credential profile this row reads (its `name` ends in ` (<profile>)`); the page shows it on its own line. */
|
|
38
|
+
profile?: string;
|
|
37
39
|
status: "ok" | "error" | "no-credentials";
|
|
38
40
|
/** A non-metered subscription whose entitlement is unlimited (not 0/100%). */
|
|
39
41
|
unlimited?: boolean;
|
|
@@ -221,6 +223,7 @@ export declare function fetchKiroUsage(storeHome?: string): Promise<Omit<Provide
|
|
|
221
223
|
type UsageProvider = {
|
|
222
224
|
id: string;
|
|
223
225
|
name: string;
|
|
226
|
+
profile?: string;
|
|
224
227
|
fetch: (storeHome?: string, scope?: ClaudeUsageScope) => Promise<Omit<ProviderUsage, "id" | "name">>;
|
|
225
228
|
};
|
|
226
229
|
type UsageFleetConfig = Parameters<typeof listConfiguredProfiles>[0];
|
package/dist/usage/providers.js
CHANGED
|
@@ -1458,6 +1458,7 @@ export function providersForConfig(config, base = DEFAULT_PROVIDERS) {
|
|
|
1458
1458
|
rows.push(...profiles.map(profile => ({
|
|
1459
1459
|
id: `${provider.id}:${profile}`,
|
|
1460
1460
|
name: `${provider.name} (${profile})`,
|
|
1461
|
+
profile,
|
|
1461
1462
|
fetch: () => provider.fetch(credentialProfileStoreHome(getAgendHome(), backendName, profile), { profile }),
|
|
1462
1463
|
})));
|
|
1463
1464
|
return rows;
|
|
@@ -1644,16 +1645,16 @@ export async function fetchAllUsage(config) {
|
|
|
1644
1645
|
// Fail soft: this row says it could not be read, the rest still answer,
|
|
1645
1646
|
// and the snapshot returns in bounded time.
|
|
1646
1647
|
return {
|
|
1647
|
-
id: p.id, name: p.name, status: "error",
|
|
1648
|
+
id: p.id, name: p.name, ...(p.profile ? { profile: p.profile } : {}), status: "error",
|
|
1648
1649
|
error: `Could not reach ${p.name}.`,
|
|
1649
1650
|
errorI18n: i18n("usage.error.unreachable", p.name),
|
|
1650
1651
|
metrics: [],
|
|
1651
1652
|
};
|
|
1652
1653
|
}
|
|
1653
|
-
return { id: p.id, name: p.name, ...outcome };
|
|
1654
|
+
return { id: p.id, name: p.name, ...(p.profile ? { profile: p.profile } : {}), ...outcome };
|
|
1654
1655
|
}
|
|
1655
1656
|
catch (err) {
|
|
1656
|
-
return { id: p.id, name: p.name, status: "error", error: String(err?.message ?? err), metrics: [] };
|
|
1657
|
+
return { id: p.id, name: p.name, ...(p.profile ? { profile: p.profile } : {}), status: "error", error: String(err?.message ?? err), metrics: [] };
|
|
1657
1658
|
}
|
|
1658
1659
|
}));
|
|
1659
1660
|
return {
|