@songsid/agend 2.3.1 → 2.3.2-beta.10
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/THIRD_PARTY_NOTICES.md +40 -0
- package/dist/agent-cli.js +21 -2
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.js +5 -0
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/antigravity-usage-scan.d.ts +58 -0
- package/dist/antigravity-usage-scan.js +257 -0
- package/dist/antigravity-usage-scan.js.map +1 -0
- package/dist/antigravity-usage-worker.d.ts +1 -0
- package/dist/antigravity-usage-worker.js +10 -0
- package/dist/antigravity-usage-worker.js.map +1 -0
- package/dist/auth-api.js +4 -0
- package/dist/auth-api.js.map +1 -1
- package/dist/authority/authority-state.d.ts +4 -0
- package/dist/authority/authority-state.js +16 -10
- package/dist/authority/authority-state.js.map +1 -1
- package/dist/authority/session-authorizations.d.ts +7 -1
- package/dist/authority/session-authorizations.js +6 -4
- package/dist/authority/session-authorizations.js.map +1 -1
- package/dist/authority/web-authority.d.ts +19 -6
- package/dist/authority/web-authority.js +22 -8
- package/dist/authority/web-authority.js.map +1 -1
- package/dist/backend/kiro-identity.d.ts +64 -0
- package/dist/backend/kiro-identity.js +101 -0
- package/dist/backend/kiro-identity.js.map +1 -1
- package/dist/backend/kiro-retired-model.d.ts +62 -0
- package/dist/backend/kiro-retired-model.js +160 -0
- package/dist/backend/kiro-retired-model.js.map +1 -0
- package/dist/backend/kiro.d.ts +71 -2
- package/dist/backend/kiro.js +352 -11
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/types.d.ts +59 -0
- package/dist/backend/types.js.map +1 -1
- package/dist/backend-signin-guide.d.ts +1 -0
- package/dist/backend-signin-guide.js +9 -0
- package/dist/backend-signin-guide.js.map +1 -0
- package/dist/backend-signin-pending.d.ts +10 -0
- package/dist/backend-signin-pending.js +44 -0
- package/dist/backend-signin-pending.js.map +1 -0
- package/dist/browser-backend-signins.d.ts +56 -0
- package/dist/browser-backend-signins.js +189 -0
- package/dist/browser-backend-signins.js.map +1 -0
- package/dist/cache-ledger.d.ts +38 -2
- package/dist/cache-ledger.js +133 -8
- package/dist/cache-ledger.js.map +1 -1
- package/dist/cache-prices.d.ts +2 -1
- package/dist/cache-prices.js +5 -2
- package/dist/cache-prices.js.map +1 -1
- package/dist/cache-service.d.ts +68 -0
- package/dist/cache-service.js +355 -13
- package/dist/cache-service.js.map +1 -1
- package/dist/channel/adapters/discord.d.ts +13 -0
- package/dist/channel/adapters/discord.js +103 -16
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/mcp-tools.js +34 -7
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/tool-router.js +2 -1
- package/dist/channel/tool-router.js.map +1 -1
- package/dist/channel/types.d.ts +11 -0
- package/dist/classic-channel-manager.d.ts +11 -0
- package/dist/classic-channel-manager.js +39 -1
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli.js +21 -2
- package/dist/cli.js.map +1 -1
- package/dist/config-validator.d.ts +7 -1
- package/dist/config-validator.js +92 -12
- package/dist/config-validator.js.map +1 -1
- package/dist/config.d.ts +19 -0
- package/dist/config.js +31 -1
- package/dist/config.js.map +1 -1
- package/dist/cpu-quota.d.ts +23 -0
- package/dist/cpu-quota.js +86 -0
- package/dist/cpu-quota.js.map +1 -0
- package/dist/daemon.d.ts +57 -3
- package/dist/daemon.js +330 -50
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard-permissions.d.ts +20 -6
- package/dist/dashboard-permissions.js +58 -9
- package/dist/dashboard-permissions.js.map +1 -1
- package/dist/delivery-outbox.d.ts +12 -1
- package/dist/delivery-outbox.js +67 -15
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/doctor.d.ts +3 -0
- package/dist/doctor.js +11 -1
- package/dist/doctor.js.map +1 -1
- package/dist/event-log.d.ts +5 -0
- package/dist/event-log.js +13 -0
- package/dist/event-log.js.map +1 -1
- package/dist/fleet-manager.d.ts +212 -0
- package/dist/fleet-manager.js +914 -111
- package/dist/fleet-manager.js.map +1 -1
- package/dist/fleet-process-handlers.d.ts +1 -0
- package/dist/fleet-process-handlers.js +9 -1
- package/dist/fleet-process-handlers.js.map +1 -1
- package/dist/forged-envelope-scan.d.ts +9 -0
- package/dist/forged-envelope-scan.js +30 -5
- package/dist/forged-envelope-scan.js.map +1 -1
- package/dist/general-knowledge/skills/credential-profiles/SKILL.md +5 -2
- package/dist/general-knowledge/skills/kiro-engine-migration/SKILL.md +14 -1
- package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +28 -5
- package/dist/general-knowledge/skills/web-appearance/SKILL.md +31 -8
- package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +138 -59
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +145 -61
- package/dist/guest-seen-people.d.ts +38 -0
- package/dist/guest-seen-people.js +78 -0
- package/dist/guest-seen-people.js.map +1 -0
- package/dist/instance-config-impact.js +14 -6
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +20 -0
- package/dist/instance-lifecycle.js +71 -13
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/kiro-db-reader.d.ts +5 -0
- package/dist/kiro-db-reader.js +18 -0
- package/dist/kiro-db-reader.js.map +1 -1
- package/dist/kiro-engine-status.d.ts +2 -0
- package/dist/kiro-engine-status.js +9 -0
- package/dist/kiro-engine-status.js.map +1 -1
- package/dist/kiro-migrate.d.ts +49 -0
- package/dist/kiro-migrate.js +105 -0
- package/dist/kiro-migrate.js.map +1 -0
- package/dist/kiro-transcript-lane.d.ts +2 -0
- package/dist/kiro-transcript-lane.js.map +1 -1
- package/dist/kiro-transcript-worker.js +1 -0
- package/dist/kiro-transcript-worker.js.map +1 -1
- package/dist/kiro-tui-forensic.d.ts +7 -0
- package/dist/kiro-tui-forensic.js +110 -0
- package/dist/kiro-tui-forensic.js.map +1 -0
- package/dist/kiro-usage-run.d.ts +7 -0
- package/dist/kiro-usage-run.js +39 -0
- package/dist/kiro-usage-run.js.map +1 -0
- package/dist/kiro-usage-scan.d.ts +49 -0
- package/dist/kiro-usage-scan.js +223 -0
- package/dist/kiro-usage-scan.js.map +1 -0
- package/dist/kiro-usage-worker.d.ts +1 -0
- package/dist/kiro-usage-worker.js +10 -0
- package/dist/kiro-usage-worker.js.map +1 -0
- package/dist/kiro-usage.d.ts +58 -0
- package/dist/kiro-usage.js +310 -0
- package/dist/kiro-usage.js.map +1 -0
- package/dist/locale.js +120 -12
- package/dist/locale.js.map +1 -1
- package/dist/log-service.js +1 -1
- package/dist/log-service.js.map +1 -1
- package/dist/login-url.d.ts +4 -0
- package/dist/login-url.js +22 -0
- package/dist/login-url.js.map +1 -0
- package/dist/mac-auto-restore.d.ts +117 -0
- package/dist/mac-auto-restore.js +294 -0
- package/dist/mac-auto-restore.js.map +1 -0
- package/dist/needs-you-hub.d.ts +2 -1
- package/dist/needs-you-hub.js +1 -1
- package/dist/needs-you-hub.js.map +1 -1
- package/dist/needs-you.d.ts +8 -2
- package/dist/needs-you.js +6 -0
- package/dist/needs-you.js.map +1 -1
- package/dist/notice-delivery.d.ts +16 -0
- package/dist/notice-delivery.js +35 -0
- package/dist/notice-delivery.js.map +1 -0
- package/dist/office-share-page.d.ts +16 -0
- package/dist/office-share-page.js +85 -0
- package/dist/office-share-page.js.map +1 -0
- package/dist/office-share-service.d.ts +91 -0
- package/dist/office-share-service.js +176 -0
- package/dist/office-share-service.js.map +1 -0
- package/dist/office-share.d.ts +258 -0
- package/dist/office-share.js +549 -0
- package/dist/office-share.js.map +1 -0
- package/dist/opencode-usage-scan.d.ts +28 -0
- package/dist/opencode-usage-scan.js +92 -0
- package/dist/opencode-usage-scan.js.map +1 -0
- package/dist/opencode-usage-worker.d.ts +1 -0
- package/dist/opencode-usage-worker.js +10 -0
- package/dist/opencode-usage-worker.js.map +1 -0
- package/dist/outbound-handlers.js +22 -2
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +28 -2
- package/dist/outbound-schemas.js +29 -2
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/public-web-gateway.js +16 -2
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/quickstart-api.js +13 -2
- package/dist/quickstart-api.js.map +1 -1
- package/dist/quickstart.js +2 -1
- package/dist/quickstart.js.map +1 -1
- package/dist/remote-card-answer.d.ts +83 -0
- package/dist/remote-card-answer.js +208 -0
- package/dist/remote-card-answer.js.map +1 -0
- package/dist/reply-triggers.d.ts +2 -0
- package/dist/reply-triggers.js +3 -0
- package/dist/reply-triggers.js.map +1 -0
- package/dist/settings-api.d.ts +28 -0
- package/dist/settings-api.js +148 -17
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +54 -17
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-effect.js +12 -3
- package/dist/settings-effect.js.map +1 -1
- package/dist/settings-instance-patch.js +4 -1
- package/dist/settings-instance-patch.js.map +1 -1
- package/dist/setup-form.d.ts +1 -1
- package/dist/setup-form.js +55 -6
- package/dist/setup-form.js.map +1 -1
- package/dist/setup-host.d.ts +9 -0
- package/dist/setup-host.js +127 -6
- package/dist/setup-host.js.map +1 -1
- package/dist/setup-signin-assets.d.ts +2 -0
- package/dist/setup-signin-assets.js +6 -0
- package/dist/setup-signin-assets.js.map +1 -0
- package/dist/setup-wizard.js +2 -0
- package/dist/setup-wizard.js.map +1 -1
- package/dist/start-attempt.d.ts +9 -5
- package/dist/start-attempt.js +27 -8
- package/dist/start-attempt.js.map +1 -1
- package/dist/start-failure-chat.d.ts +4 -0
- package/dist/start-failure-chat.js +23 -6
- package/dist/start-failure-chat.js.map +1 -1
- package/dist/start-failure-notice.d.ts +4 -0
- package/dist/start-failure-notice.js +8 -0
- package/dist/start-failure-notice.js.map +1 -1
- package/dist/start-failure.d.ts +25 -0
- package/dist/start-failure.js +75 -2
- package/dist/start-failure.js.map +1 -1
- package/dist/structured-signin-transport.d.ts +8 -0
- package/dist/structured-signin-transport.js +79 -0
- package/dist/structured-signin-transport.js.map +1 -0
- package/dist/structured-signin.d.ts +131 -0
- package/dist/structured-signin.js +388 -0
- package/dist/structured-signin.js.map +1 -0
- package/dist/tool-permissions.js +12 -0
- package/dist/tool-permissions.js.map +1 -1
- package/dist/topic-commands.js +6 -2
- package/dist/topic-commands.js.map +1 -1
- package/dist/transcript-sources.d.ts +26 -2
- package/dist/transcript-sources.js +103 -40
- package/dist/transcript-sources.js.map +1 -1
- package/dist/turn-reply-guard.d.ts +25 -0
- package/dist/turn-reply-guard.js +111 -12
- package/dist/turn-reply-guard.js.map +1 -1
- package/dist/types.d.ts +46 -9
- package/dist/ui/backend-signin.js +68 -12
- package/dist/ui/chat-ambient.js +1 -1
- package/dist/ui/chat-effects.js +4 -4
- package/dist/ui/chat-render.js +5 -2
- package/dist/ui/chat-strings.js +4 -4
- package/dist/ui/chat-thread.js +2 -2
- package/dist/ui/first-run.js +2 -2
- package/dist/ui/fleet-canvas-view.js +114 -0
- package/dist/ui/fleet-canvas.js +15 -77
- package/dist/ui/fleet-office-editor.js +94 -14
- package/dist/ui/fleet-office-layout.js +235 -0
- package/dist/ui/fleet-office-model.js +12 -6
- package/dist/ui/fleet-office-overview.js +167 -0
- package/dist/ui/fleet-office-pack.d.ts +44 -0
- package/dist/ui/fleet-office-pack.js +352 -37
- package/dist/ui/fleet-office-skin.js +220 -36
- package/dist/ui/fleet-office-store.js +112 -7
- package/dist/ui/fleet-office-view.js +155 -0
- package/dist/ui/fleet-office-zones.js +264 -42
- package/dist/ui/fleet-office.js +957 -160
- package/dist/ui/fleet-usage.js +26 -21
- package/dist/ui/office-share-dialog.js +145 -0
- package/dist/ui/office-share-page.js +164 -0
- package/dist/ui/panel-chat.js +9 -2
- package/dist/ui/panel-details.js +2 -2
- package/dist/ui/panel-fleet.js +11 -77
- package/dist/ui/panel-log.js +4 -4
- package/dist/ui/panel-needs.js +4 -4
- package/dist/ui/panel-settings.js +608 -167
- package/dist/ui/search-dialog.js +26 -7
- package/dist/ui/settings-appearance.js +82 -29
- package/dist/ui/settings-apply.js +42 -7
- package/dist/ui/settings-dialogs.js +76 -23
- package/dist/ui/settings-draft.js +51 -0
- package/dist/ui/settings-guests.js +255 -79
- package/dist/ui/settings-learn.js +2 -2
- package/dist/ui/settings-model.js +16 -6
- package/dist/ui/settings-registry.js +146 -0
- package/dist/ui/settings-strings.js +116 -44
- package/dist/ui/settings-web-authority.js +34 -23
- package/dist/ui/shared/app-i18n.js +18 -2
- package/dist/ui/shared/app-nav.js +10 -5
- package/dist/ui/shared/app-prefs.js +80 -2
- package/dist/ui/shared/app-route.js +39 -5
- package/dist/ui/shared/app-shell.js +73 -10
- package/dist/ui/shared/app-stream.js +3 -1
- package/dist/ui/shared/app.css +307 -17
- package/dist/ui/shared/app.js +3 -0
- package/dist/ui/shared/effect-stock-trust.js +1 -1
- package/dist/ui/shared/identity-color.js +4 -4
- package/dist/ui/shared/office/cozy.png +0 -0
- package/dist/ui/shared/side-panel.js +95 -0
- package/dist/ui/shared/signin.js +2 -2
- package/dist/ui/shared/ui-dialog.js +6 -1
- package/dist/ui/shared/ui-icons.js +2 -0
- package/dist/ui/shared/web-authorize.js +13 -12
- package/dist/ui/structured-signin.js +115 -0
- package/dist/usage-report.d.ts +20 -0
- package/dist/usage-report.js +54 -17
- package/dist/usage-report.js.map +1 -1
- package/dist/usage-rollup.d.ts +9 -1
- package/dist/usage-rollup.js +20 -4
- package/dist/usage-rollup.js.map +1 -1
- package/dist/web-api.d.ts +26 -2
- package/dist/web-api.js +361 -5
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.js +8 -2
- package/dist/web-auth.js.map +1 -1
- package/dist/web-backend-login.d.ts +1 -7
- package/dist/web-backend-login.js +2 -24
- package/dist/web-backend-login.js.map +1 -1
- package/dist/web-experimental.d.ts +1 -1
- package/dist/web-experimental.js +1 -1
- package/dist/web-experimental.js.map +1 -1
- package/dist/web-mod-agent.js +2 -2
- package/dist/web-mod-agent.js.map +1 -1
- package/dist/web-office-acts.d.ts +173 -0
- package/dist/web-office-acts.js +285 -0
- package/dist/web-office-acts.js.map +1 -0
- package/dist/web-office-skins.d.ts +109 -0
- package/dist/web-office-skins.js +299 -0
- package/dist/web-office-skins.js.map +1 -0
- package/dist/web-office-store.d.ts +8 -2
- package/dist/web-office-store.js +77 -14
- package/dist/web-office-store.js.map +1 -1
- package/dist/web-settings-patch.d.ts +8 -1
- package/dist/web-settings-patch.js +65 -5
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/web-shell-routes.d.ts +4 -2
- package/dist/web-shell-routes.js +17 -7
- package/dist/web-shell-routes.js.map +1 -1
- package/dist/working-recovery.d.ts +104 -0
- package/dist/working-recovery.js +298 -0
- package/dist/working-recovery.js.map +1 -0
- package/dist/working-state-journal.d.ts +5 -1
- package/dist/working-state-journal.js +31 -3
- package/dist/working-state-journal.js.map +1 -1
- package/package.json +3 -2
- package/templates/windows-shortcuts.ps1 +155 -140
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// #2585 (the user): settings-like editors docked on the right. On a screen wide enough for the usage rail (RAIL_MEDIA,
|
|
2
|
+
// #1673) an editor opens in the rail's place, beside the page — not modal: the page stays in view and in use (the
|
|
3
|
+
// Office re-fits beside it). While one is open the rail steps aside; it comes back as it was when the panel closes (its
|
|
4
|
+
// shown/hidden choice is never touched). Narrower, the same editor is the shared Dialog (centred; a phone's sheet).
|
|
5
|
+
//
|
|
6
|
+
// <${SidePanel} title=… onClose=… actions=… busy=…>…body…</${SidePanel}>: rendered = open, the owner unmounts it to
|
|
7
|
+
// close (like Dialog). Docked, the owner renders nothing itself: it publishes its title, body and actions to the shell's
|
|
8
|
+
// host (sidePanelStore) on every render, so the body keeps the owner's state and closures; the owner going (a close, a
|
|
9
|
+
// navigation) takes the panel with it. Esc in the panel and its × close it (not while `busy`); focus moves into it when
|
|
10
|
+
// it opens and goes back to what opened it when it closes. Docked, the body renders under the shell, not under its
|
|
11
|
+
// owner: a context provided above the owner does not reach it (pass what it needs as props, or read a store).
|
|
12
|
+
// `dirty`: the editor holds unsaved changes. Docked, the page beside it is in use (its links, search): leaving the page
|
|
13
|
+
// then closes the panel as Cancel does — and with changes, asks first (the app's leave guard; one at a time).
|
|
14
|
+
// `dock` false keeps it from docking (the Office in fullscreen, where the shell is out of view); `fallback`, when given,
|
|
15
|
+
// is what it is below the rail's width (or not docked) instead of the Dialog — Edit layout's bar over the canvas.
|
|
16
|
+
import { html, useEffect, useLayoutEffect, useRef } from "./app-html.js";
|
|
17
|
+
import { createStore, useStore } from "./app-store.js";
|
|
18
|
+
import { t } from "./app-i18n.js";
|
|
19
|
+
import { Icon } from "./ui-icons.js";
|
|
20
|
+
import { Dialog } from "./ui-dialog.js";
|
|
21
|
+
import { confirmDialog } from "./ui-confirm.js";
|
|
22
|
+
import { navigate, setLeaveGuard } from "./app-nav.js";
|
|
23
|
+
import { UsageRail, useRailWidth } from "./header-tools.js";
|
|
24
|
+
|
|
25
|
+
/** { panel: { id, title, label, body, actions, busy, onClose } | null } — the one docked panel. */
|
|
26
|
+
export const sidePanelStore = createStore({ panel: null });
|
|
27
|
+
let seq = 0;
|
|
28
|
+
const clear = (id) => { if (sidePanelStore.get().panel?.id === id) sidePanelStore.set({ panel: null }); };
|
|
29
|
+
|
|
30
|
+
/** An editor: docked on a wide screen, the shared Dialog (`wide`) elsewhere. */
|
|
31
|
+
export function SidePanel({ title, label, onClose, actions, busy = false, dirty = false, dock = true, fallback, children }) {
|
|
32
|
+
const docked = useRailWidth() && dock;
|
|
33
|
+
const id = useRef(0);
|
|
34
|
+
if (!id.current) id.current = ++seq;
|
|
35
|
+
const opener = useRef(undefined);
|
|
36
|
+
if (opener.current === undefined) opener.current = typeof document !== "undefined" ? document.activeElement : null;
|
|
37
|
+
useLayoutEffect(() => {
|
|
38
|
+
if (docked) sidePanelStore.set({ panel: { id: id.current, title, label, body: children, actions, busy, onClose } });
|
|
39
|
+
else clear(id.current);
|
|
40
|
+
});
|
|
41
|
+
useLayoutEffect(() => () => {
|
|
42
|
+
const host = typeof document !== "undefined" ? document.querySelector(".side-panel") : null;
|
|
43
|
+
const inside = !!host && (host.contains(document.activeElement) || document.activeElement === document.body);
|
|
44
|
+
clear(id.current);
|
|
45
|
+
// Focus back to what opened it — when it was in the panel (or nowhere): a person who moved on keeps their place.
|
|
46
|
+
const back = opener.current;
|
|
47
|
+
if ((inside || !host) && back && typeof back.focus === "function" && document.contains?.(back)) back.focus();
|
|
48
|
+
}, []);
|
|
49
|
+
// Leaving the page with changes in the docked panel: asked in the app's dialog; yes = Cancel, then on to there.
|
|
50
|
+
const live = useRef(null);
|
|
51
|
+
live.current = { title, onClose };
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
if (!docked || !dirty) return undefined;
|
|
54
|
+
let asking = false;
|
|
55
|
+
return setLeaveGuard((_to, path) => {
|
|
56
|
+
if (!asking) {
|
|
57
|
+
asking = true;
|
|
58
|
+
confirmDialog({ message: t("app.sidePanelLeave", live.current.title), confirmLabel: t("app.sidePanelDiscard"), danger: true }).then((yes) => {
|
|
59
|
+
asking = false;
|
|
60
|
+
if (!yes) return;
|
|
61
|
+
live.current.onClose();
|
|
62
|
+
navigate(path, { force: true });
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
});
|
|
67
|
+
}, [docked, dirty]);
|
|
68
|
+
if (docked) return null;
|
|
69
|
+
if (fallback !== undefined) return fallback;
|
|
70
|
+
return html`<${Dialog} title=${title} label=${label} onClose=${onClose} actions=${actions} busy=${busy} wide=${true}>${children}</${Dialog}>`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** The docked panel itself: a non-modal dialog in the rail's place. */
|
|
74
|
+
function SidePanelHost({ panel }) {
|
|
75
|
+
const box = useRef(null);
|
|
76
|
+
// Into the panel as it opens (its box: no ring; Tab moves on), once per panel.
|
|
77
|
+
useLayoutEffect(() => { if (box.current && typeof box.current.focus === "function") box.current.focus({ preventScroll: true }); }, [panel.id]);
|
|
78
|
+
const close = () => { if (!panel.busy) panel.onClose(); };
|
|
79
|
+
const onKeyDown = (e) => { if (e.key === "Escape" && !e.defaultPrevented) { e.preventDefault(); close(); } };
|
|
80
|
+
return html`<dialog open data-trust="dialog" class="dlg side-panel" aria-label=${panel.label || panel.title} onKeyDown=${onKeyDown}>
|
|
81
|
+
<div class="dlg-box" tabindex="-1" ref=${box}>
|
|
82
|
+
<div class="dlg-head"><h2 class="dlg-title">${panel.title}</h2>
|
|
83
|
+
<button type="button" class="icon-btn dlg-x" aria-label=${t("app.close")} title=${t("app.close")} onClick=${close}><${Icon} name="close" /></button></div>
|
|
84
|
+
<div class="dlg-body">${panel.body}</div>
|
|
85
|
+
${panel.actions ? html`<div class="dlg-actions">${panel.actions}</div>` : null}
|
|
86
|
+
</div>
|
|
87
|
+
</dialog>`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** The shell's right-hand column: a docked panel when one is open (wide screens), else the usage rail. */
|
|
91
|
+
export function RightRail({ route }) {
|
|
92
|
+
const { panel } = useStore(sidePanelStore);
|
|
93
|
+
const wide = useRailWidth();
|
|
94
|
+
return panel && wide ? html`<${SidePanelHost} panel=${panel} />` : html`<${UsageRail} route=${route} />`;
|
|
95
|
+
}
|
package/dist/ui/shared/signin.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
label: "One-time code", submit: "Sign in", working: "Signing in…",
|
|
16
16
|
hint: "No code? Send /web in General, then press a sign-in button to get one.",
|
|
17
17
|
nobot: "No chat bot?", nobot_body: "Ask the operator to run agend web --code on the AgEnD computer.",
|
|
18
|
-
safe: "Page looks wrong after signing in?", 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 →
|
|
18
|
+
safe: "Page looks wrong after signing in?", 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 → Look.",
|
|
19
19
|
refused: "The code is invalid or no longer works. Send /web in General, then press a sign-in button for a new code.",
|
|
20
20
|
unconfirmed: "Sign-in could not be confirmed. Get a new code and try again; if it keeps failing, contact the operator.",
|
|
21
21
|
paused: "Too many wrong codes — sign-in is paused for a few minutes.",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
label: "一次性登入碼", submit: "登入", working: "登入中…",
|
|
30
30
|
hint: "沒收到?在 General 傳 /web,再按登入按鈕取碼。",
|
|
31
31
|
nobot: "沒有聊天 bot?", nobot_body: "請管理員在 AgEnD 電腦執行 agend web --code。",
|
|
32
|
-
safe: "登入後畫面不對勁?", safe_body: "可能是你的自訂 CSS 把東西藏起來了。在網址最後加上 ?safe=1,就會不套用它地載入;再到「設定 →
|
|
32
|
+
safe: "登入後畫面不對勁?", safe_body: "可能是你的自訂 CSS 把東西藏起來了。在網址最後加上 ?safe=1,就會不套用它地載入;再到「設定 → 外觀」修正或關閉。",
|
|
33
33
|
refused: "登入碼無效或已失效。請在 General 傳 /web,再按登入按鈕取新碼。",
|
|
34
34
|
unconfirmed: "登入暫時無法確認。請重新取碼再試;若仍失敗,請聯絡管理員。",
|
|
35
35
|
paused: "錯誤次數過多,登入暫停幾分鐘。",
|
|
@@ -19,6 +19,11 @@ export function Dialog({ title, onClose, actions, children, busy = false, wide =
|
|
|
19
19
|
const d = ref.current;
|
|
20
20
|
opener.current = typeof document !== "undefined" ? document.activeElement : null;
|
|
21
21
|
if (d && typeof d.showModal === "function" && !d.open) d.showModal();
|
|
22
|
+
// The browser's own first stop is the × (WebKit paints its focus ring at once): the dialog's box takes that focus
|
|
23
|
+
// instead, so opening shows no ring; Tab then reaches the × and every field with the keyboard's ring. A field a
|
|
24
|
+
// dialog focuses itself (autofocus) keeps it.
|
|
25
|
+
const x = d && d.querySelector ? d.querySelector(".dlg-x") : null, box = d && d.querySelector ? d.querySelector(".dlg-box") : null;
|
|
26
|
+
if (box && x && typeof document !== "undefined" && document.activeElement === x && typeof box.focus === "function") box.focus({ preventScroll: true });
|
|
22
27
|
return () => {
|
|
23
28
|
if (d && d.open && typeof d.close === "function") d.close();
|
|
24
29
|
const back = opener.current;
|
|
@@ -37,7 +42,7 @@ export function Dialog({ title, onClose, actions, children, busy = false, wide =
|
|
|
37
42
|
return () => { d.removeEventListener("cancel", onCancel); d.removeEventListener("click", onClick); };
|
|
38
43
|
}, [onClose]);
|
|
39
44
|
return html`<dialog data-trust="dialog" ref=${ref} class=${`dlg${wide ? " dlg-wide" : ""}${drawer ? " dlg-drawer" : ""}`} aria-label=${label || title}>
|
|
40
|
-
<div class="dlg-box">
|
|
45
|
+
<div class="dlg-box" tabindex="-1">
|
|
41
46
|
<div class="dlg-head"><h2 class="dlg-title">${title}</h2>${tools ? html`<span class="dlg-tools">${tools}</span>` : null}
|
|
42
47
|
<button type="button" class="icon-btn dlg-x" aria-label=${t("app.close")} title=${t("app.close")} onClick=${() => { if (!busyRef.current) onClose(); }}><${Icon} name="close" /></button></div>
|
|
43
48
|
<div class="dlg-body">${children}</div>
|
|
@@ -9,6 +9,8 @@ const P = {
|
|
|
9
9
|
plus: () => html`<path d="M12 5v14M5 12h14"/>`,
|
|
10
10
|
minus: () => html`<path d="M5 12h14"/>`, // #2333: the lightbox's zoom out
|
|
11
11
|
fitwidth: () => html`<path d="M3 4v16M21 4v16M7 12h10M10 9l-3 3 3 3M14 9l3 3-3 3"/>`, // #2333: fit width
|
|
12
|
+
fullscreen: () => html`<path d="M4 9V4h5M15 4h5v5M20 15v5h-5M9 20H4v-5"/>`, // #2540: the Office's fullscreen
|
|
13
|
+
fullscreenexit: () => html`<path d="M9 4v5H4M20 9h-5V4M15 20v-5h5M4 15h5v5"/>`, // …and its way out
|
|
12
14
|
edit: () => html`<path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4z"/>`,
|
|
13
15
|
more: () => html`<circle cx="5" cy="12" r="1.5"/><circle cx="12" cy="12" r="1.5"/><circle cx="19" cy="12" r="1.5"/>`,
|
|
14
16
|
chat: () => html`<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>`,
|
|
@@ -12,7 +12,6 @@ register("webauth", {
|
|
|
12
12
|
authorize: "Authorize this browser",
|
|
13
13
|
waiting: "Waiting for your Confirm in Discord or Telegram ({0})",
|
|
14
14
|
sent: "Sent — confirm it in your DM within 5 minutes.",
|
|
15
|
-
publicLink: "Not on the temporary public link: open the dashboard directly to authorize.",
|
|
16
15
|
reason_disabled: "Web authority is turned off.",
|
|
17
16
|
reason_loopback_only: "Only this computer can authorize, and this browser is not on it.",
|
|
18
17
|
reason_no_account: "This browser was not signed in as an account (agend web): sign in with /web to authorize.",
|
|
@@ -20,7 +19,8 @@ register("webauth", {
|
|
|
20
19
|
reason_not_admin_session: "Guests can't authorize.",
|
|
21
20
|
reason_token_rotated: "The dashboard's token changed: sign in again.",
|
|
22
21
|
reason_public_link: "Not on the temporary public link.",
|
|
23
|
-
reason_named_link_off: "Over your named link: turn on \"Also over my named link\" in Settings →
|
|
22
|
+
reason_named_link_off: "Over your named link: turn on \"Also over my named link\" in Settings → Access & security on this computer.",
|
|
23
|
+
reason_quick_link_off: "Over the temporary public link: turn on \"Also over the temporary public link\" in Settings → Access & security on this computer.",
|
|
24
24
|
reason_not_session: "Sign in to authorize.",
|
|
25
25
|
reason_other: "This browser can't be authorized here.",
|
|
26
26
|
refusal_pending: "A request is already waiting in your DM.",
|
|
@@ -39,7 +39,6 @@ register("webauth", {
|
|
|
39
39
|
authorize: "授權這個瀏覽器",
|
|
40
40
|
waiting: "等你在 Discord 或 Telegram 按下確認({0})",
|
|
41
41
|
sent: "已送出——請在 5 分鐘內到私訊確認。",
|
|
42
|
-
publicLink: "臨時公開連結上無法授權:請直接開啟儀表板再授權。",
|
|
43
42
|
reason_disabled: "網頁授權已關閉。",
|
|
44
43
|
reason_loopback_only: "只有這台電腦可以授權,而這個瀏覽器不在這台電腦上。",
|
|
45
44
|
reason_no_account: "這個瀏覽器不是用帳號登入的(agend web):請用 /web 登入後再授權。",
|
|
@@ -47,7 +46,8 @@ register("webauth", {
|
|
|
47
46
|
reason_not_admin_session: "訪客無法授權。",
|
|
48
47
|
reason_token_rotated: "儀表板的 token 已變更:請重新登入。",
|
|
49
48
|
reason_public_link: "臨時公開連結上無法授權。",
|
|
50
|
-
reason_named_link_off: "透過固定網域:請在這台電腦的 設定 →
|
|
49
|
+
reason_named_link_off: "透過固定網域:請在這台電腦的 設定 → 存取與安全 開啟「也允許透過我的固定網域」。",
|
|
50
|
+
reason_quick_link_off: "透過臨時公開連結:請在這台電腦的 設定 → 存取與安全 開啟「也允許透過臨時公開連結」。",
|
|
51
51
|
reason_not_session: "登入後才能授權。",
|
|
52
52
|
reason_other: "這個瀏覽器在這裡無法授權。",
|
|
53
53
|
refusal_pending: "已經有一個請求在你的私訊中等待。",
|
|
@@ -62,7 +62,9 @@ register("webauth", {
|
|
|
62
62
|
},
|
|
63
63
|
});
|
|
64
64
|
const tn = (k, ...v) => t(`webauth.${k}`, ...v);
|
|
65
|
-
|
|
65
|
+
// The switch and "Only this computer" are changed in Settings on this computer — never over the public link (#2525 review).
|
|
66
|
+
const fixable = (reason) => (reason === "disabled" || reason === "loopback_only") && globalThis.document?.body?.dataset?.publicLink !== "1";
|
|
67
|
+
const REASONS = ["disabled", "loopback_only", "no_account", "not_admin", "not_admin_session", "token_rotated", "public_link", "named_link_off", "quick_link_off", "not_session"];
|
|
66
68
|
// Until when: the time, with the day when it isn't today (an authorization lasts up to 48 h).
|
|
67
69
|
const clock = (ms) => {
|
|
68
70
|
const d = new Date(ms), today = new Date().toDateString() === d.toDateString();
|
|
@@ -70,7 +72,6 @@ const clock = (ms) => {
|
|
|
70
72
|
: d.toLocaleString([], { weekday: "short", month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" });
|
|
71
73
|
};
|
|
72
74
|
const mmss = (ms) => { const s = Math.max(0, Math.ceil(ms / 1000)); return `${Math.floor(s / 60)}:${String(s % 60).padStart(2, "0")}`; };
|
|
73
|
-
const publicLink = () => globalThis.document?.body?.dataset?.publicLink === "1";
|
|
74
75
|
|
|
75
76
|
// One read per page load, shared by every line that shows it (the composer's, the session menu's): coming back to a chat
|
|
76
77
|
// reads nothing (#1374). It is read again only after an ask, and while a card is pending.
|
|
@@ -98,7 +99,9 @@ export function useWebAuthorization() {
|
|
|
98
99
|
const [deadline, setDeadline] = useState(0), [now, setNow] = useState(() => Date.now());
|
|
99
100
|
const alive = useRef(true);
|
|
100
101
|
const load = async () => {
|
|
101
|
-
|
|
102
|
+
// §0.2 / §0.3: over the public link too — the server says whether this browser may be authorized there (its link's
|
|
103
|
+
// option on) or why not; a guest never asks.
|
|
104
|
+
if (guestPage()) return;
|
|
102
105
|
const body = await readStatus();
|
|
103
106
|
if (!alive.current) return;
|
|
104
107
|
if (signedOut) { setSt(null); setNotice(null); return; }
|
|
@@ -144,7 +147,6 @@ export function resetWebAuthorization() { shared = null; answered = false; infli
|
|
|
144
147
|
export function AuthorizeChip() {
|
|
145
148
|
const a = useWebAuthorization();
|
|
146
149
|
if (guestPage()) return null;
|
|
147
|
-
if (publicLink()) return html`<p class="webauth-chip note" data-state="public">${tn("publicLink")}</p>`;
|
|
148
150
|
if (!a.st) return null;
|
|
149
151
|
return html`<div class="webauth-chip" data-state=${a.st.state}>${body(a)}${a.notice ? html` <span class="note" role="status">${a.notice}</span>` : null}</div>`;
|
|
150
152
|
}
|
|
@@ -163,7 +165,6 @@ export function ComposerAuth() {
|
|
|
163
165
|
const tip = useRef("wa-tip-" + Math.random().toString(36).slice(2, 8));
|
|
164
166
|
useEffect(() => { setOpen(false); }, [a.st && a.st.state]); // a new state closes the bubble
|
|
165
167
|
if (guestPage()) return null;
|
|
166
|
-
if (publicLink()) return html`<span class="webauth-row note" data-state="public" title=${tn("publicLink")}>${tn("publicLink")}</span>`;
|
|
167
168
|
if (!a.st) return null;
|
|
168
169
|
const st = a.st, id = tip.current;
|
|
169
170
|
const said = a.notice ? html`<span class="webauth-notice" role="status">${a.notice}</span>` : null;
|
|
@@ -184,8 +185,8 @@ export function ComposerAuth() {
|
|
|
184
185
|
title=${tn("authorizeWhy")} aria-label=${tn("authorizeWhy")} onClick=${a.ask}>${tn("authorizeShort")}</button>${said}</span>`;
|
|
185
186
|
}
|
|
186
187
|
const why = REASONS.includes(st.reason) ? tn(`reason_${st.reason}`) : tn("reason_other");
|
|
187
|
-
return html`<span class="webauth-row" data-state="none"><span class="note" title=${why}>${tn("notVerified")}<span class="sr-only">: ${why}</span></span>${st.reason
|
|
188
|
-
? html` · <a href="/settings/
|
|
188
|
+
return html`<span class="webauth-row" data-state="none"><span class="note" title=${why}>${tn("notVerified")}<span class="sr-only">: ${why}</span></span>${fixable(st.reason)
|
|
189
|
+
? html` · <a href="/settings/access" title=${why}>${tn("settings")}</a>` : null}</span>`;
|
|
189
190
|
}
|
|
190
191
|
|
|
191
192
|
function body(a) {
|
|
@@ -196,5 +197,5 @@ function body(a) {
|
|
|
196
197
|
return html`<span class="note">${tn("notVerified")} ·</span> <button type="button" class="btn btn-sm" onClick=${a.ask}>${tn("authorize")}</button>`;
|
|
197
198
|
const why = REASONS.includes(st.reason) ? tn(`reason_${st.reason}`) : tn("reason_other");
|
|
198
199
|
// The fix, where there is one the person can take here: the switch lives in Settings → Web.
|
|
199
|
-
return html`<span class="note">${tn("notVerified")}: ${why}</span>${st.reason
|
|
200
|
+
return html`<span class="note">${tn("notVerified")}: ${why}</span>${fixable(st.reason) ? html` <a href="/settings/access">${tn("settings")}</a>` : null}`;
|
|
200
201
|
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/* Shared browser controls. No terminal relay, provider output or secrets in diagnostics. */
|
|
2
|
+
const NAMES = { "kiro-cli": "Kiro", "claude-code": "Claude Code", codex: "Codex", antigravity: "Antigravity (console fallback)" };
|
|
3
|
+
const CHOICES = { builder_id: "Builder ID", google: "Google", github: "GitHub", organization: "Your organization", subscription: "Claude subscription", console: "Anthropic Console" };
|
|
4
|
+
const ERRORS = {
|
|
5
|
+
unsupported_prompt: "This CLI screen is not supported. Cancel and use the console fallback.",
|
|
6
|
+
cleanup_failed: "The login process could not be confirmed closed. Close it on the host and restart setup before retrying.",
|
|
7
|
+
signin_cleanup_failed: "The login process could not be confirmed closed. Close it on the host and restart setup before retrying.",
|
|
8
|
+
signin_action_stale: "That prompt changed or was already answered. Refresh its status; do not submit it again.",
|
|
9
|
+
signin_console_only: "This backend uses the explicit console fallback.",
|
|
10
|
+
backend_signin_local_only: "Backend sign-in is available only on this computer. Use the local setup page, or sign in later.",
|
|
11
|
+
signin_session_expired: "This setup session has ended. Reopen setup on the host.",
|
|
12
|
+
expired: "This login attempt expired. Start a new attempt.",
|
|
13
|
+
auth_unconfirmed: "The CLI exited, but credentials could not be verified. Retry or sign in later.",
|
|
14
|
+
provider_rejected: "The provider rejected this sign-in. Retry or sign in later.",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function mountStructuredSignIns(root, { endpoint, fetcher = fetch, changed = () => {} } = {}) {
|
|
18
|
+
const doc = root.ownerDocument;
|
|
19
|
+
let view = null, busy = false, disposed = false, timer = null, generation = 0, message = "", rendered = "";
|
|
20
|
+
const el = (tag, text, parent = root) => { const node = doc.createElement(tag); if (text) node.textContent = text; parent.append(node); return node; };
|
|
21
|
+
const errorText = code => ERRORS[code] || "Could not complete sign-in. Check status, retry, or sign in later.";
|
|
22
|
+
const button = (parent, label, action) => { const b = el("button", label, parent); b.type = "button"; b.disabled = busy; b.onclick = action; return b; };
|
|
23
|
+
const request = async data => {
|
|
24
|
+
const response = await fetcher(endpoint, { method: data ? "POST" : "GET", cache: "no-store", headers: { "Content-Type": "application/json" }, ...(data ? { body: JSON.stringify(data) } : {}) });
|
|
25
|
+
const body = await response.json().catch(() => null);
|
|
26
|
+
if (!response.ok) throw new Error(body?.error || "signin_failed");
|
|
27
|
+
return body;
|
|
28
|
+
};
|
|
29
|
+
const accept = next => { view = next; root.dataset.ready = String(view.ready); changed(view); };
|
|
30
|
+
async function perform(data) {
|
|
31
|
+
if (busy || disposed) return;
|
|
32
|
+
busy = true; generation++; message = ""; render();
|
|
33
|
+
try { const next = await request(data); if (!disposed) accept(next); }
|
|
34
|
+
catch (error) { if (!disposed) message = errorText(error?.message); }
|
|
35
|
+
finally { busy = false; if (!disposed) render(); }
|
|
36
|
+
}
|
|
37
|
+
function render() {
|
|
38
|
+
const signature = JSON.stringify([view, busy, message]);
|
|
39
|
+
if (disposed || rendered === signature) return;
|
|
40
|
+
rendered = signature; root.replaceChildren();
|
|
41
|
+
el("h2", "Sign in your AI backends");
|
|
42
|
+
el("p", "Use the controls here; no terminal typing is needed for Kiro, Claude Code or Codex. You can sign in later from the dashboard.");
|
|
43
|
+
if (message) { const alert = el("p", message); alert.setAttribute("role", "alert"); }
|
|
44
|
+
if (!view) { button(root, "Check sign-ins", () => perform({ operation: "refresh" })); return; }
|
|
45
|
+
for (const backend of view.selected) {
|
|
46
|
+
const card = el("section"); card.dataset.backend = backend;
|
|
47
|
+
el("h3", NAMES[backend] || backend, card);
|
|
48
|
+
const state = view.states[backend], pending = view.pending.includes(backend), skipped = view.skipped.includes(backend);
|
|
49
|
+
el("p", skipped ? "Sign in later · remains in Needs you" : state === "valid" ? "Signed in ✓" : state === "not_installed" ? "Not installed on this host" : state === "console_only" ? "Use the explicit console fallback, or sign in later." : "Sign-in needed", card);
|
|
50
|
+
const login = view.login?.backend === backend ? view.login : null;
|
|
51
|
+
const active = login && login.phase !== "done" && !(login.phase === "failed" && login.reason);
|
|
52
|
+
if (active) {
|
|
53
|
+
el("p", login.phase === "waiting" || login.phase === "starting" ? "Waiting for the CLI…" : "Login: " + login.phase.replaceAll("_", " "), card);
|
|
54
|
+
if (login.phase === "method" && login.canAct) for (const choice of login.choices || []) {
|
|
55
|
+
if (CHOICES[choice]) button(card, CHOICES[choice], () => perform({ operation: "input", id: login.id, revision: login.revision, action: { kind: "method", choice } }));
|
|
56
|
+
}
|
|
57
|
+
if (login.url) {
|
|
58
|
+
// The host already allowlists these; still never turn a non-HTTPS URL into a link.
|
|
59
|
+
let url; try { url = new URL(login.url); } catch {}
|
|
60
|
+
if (url?.protocol === "https:" && !url.username && !url.password) {
|
|
61
|
+
const link = el("a", "Open provider sign-in", card); link.href = url.href; link.target = "_blank"; link.rel = "noopener noreferrer";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (login.code) { el("p", "Device code:", card); el("code", login.code, card); }
|
|
65
|
+
if (login.canAct && ["code", "start_url", "region"].includes(login.phase)) {
|
|
66
|
+
const label = el("label", { code: "Paste the returned code", start_url: "Organization Start URL", region: "AWS region" }[login.phase], card);
|
|
67
|
+
const input = el("input", null, label); input.type = login.phase === "code" ? "password" : "text"; input.autocomplete = "off"; input.spellcheck = false;
|
|
68
|
+
input.setAttribute("aria-label", label.textContent); input.disabled = busy;
|
|
69
|
+
button(card, "Submit " + login.phase.replaceAll("_", " "), () => { const value = input.value; input.value = ""; void perform({ operation: "input", id: login.id, revision: login.revision, action: { kind: login.phase, value } }); });
|
|
70
|
+
}
|
|
71
|
+
if (["method", "code", "start_url", "region"].includes(login.phase) && !login.canAct) el("p", "Answer sent. Waiting for the next screen; it will not be sent twice.", card);
|
|
72
|
+
button(card, "Cancel sign-in", () => perform({ operation: "cancel" }));
|
|
73
|
+
} else {
|
|
74
|
+
if (login?.reason && login.reason !== "cancelled") el("p", errorText(login.reason), card);
|
|
75
|
+
const usable = !["antigravity"].includes(backend) && state !== "not_installed";
|
|
76
|
+
if (usable) button(card, state === "valid" ? "Sign in again" : "Sign in", () => {
|
|
77
|
+
const relogin = state === "valid";
|
|
78
|
+
if (relogin && !globalThis.confirm("Sign in again? Kiro will sign out its existing account first.")) return;
|
|
79
|
+
void perform({ operation: "start", backend, relogin });
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
if (pending && !skipped) button(card, "Sign in later", () => perform({ operation: "skip", backend }));
|
|
83
|
+
}
|
|
84
|
+
button(root, "Check sign-ins", () => perform({ operation: "refresh" }));
|
|
85
|
+
if (view.ready) el("p", "You can continue setup.");
|
|
86
|
+
}
|
|
87
|
+
async function poll() {
|
|
88
|
+
if (disposed) return;
|
|
89
|
+
const before = generation;
|
|
90
|
+
if (!busy) try { const next = await request(); if (!disposed && before === generation) { accept(next); render(); } }
|
|
91
|
+
catch (error) { if (!disposed && before === generation) { message = errorText(error?.message); render(); } }
|
|
92
|
+
if (!disposed) timer = setTimeout(poll, 1000);
|
|
93
|
+
}
|
|
94
|
+
render(); void poll();
|
|
95
|
+
return {
|
|
96
|
+
addBackends(backends) {
|
|
97
|
+
if (view && Array.isArray(backends) && backends.every(b => NAMES[b]) && backends.some(b => !view.selected.includes(b)))
|
|
98
|
+
return perform({ operation: "select", backends: [...new Set([...view.selected, ...backends])] });
|
|
99
|
+
},
|
|
100
|
+
addBackend(backend) { if (view && NAMES[backend] && !view.selected.includes(backend)) return perform({ operation: "select", backends: [...view.selected, backend] }); },
|
|
101
|
+
dispose() { disposed = true; generation++; clearTimeout(timer); root.replaceChildren(); },
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const setupRoot = typeof document === "undefined" ? null : document.getElementById("setupSignins");
|
|
106
|
+
if (setupRoot) {
|
|
107
|
+
const cards = mountStructuredSignIns(setupRoot, { endpoint: "setup/signins" });
|
|
108
|
+
const backend = document.getElementById("backend");
|
|
109
|
+
const select = () => cards.addBackend(backend?.value);
|
|
110
|
+
backend?.addEventListener("change", select);
|
|
111
|
+
const observer = new MutationObserver(select); if (backend) observer.observe(backend, { childList: true });
|
|
112
|
+
// Initial GET may finish after the environment list, so also observe our readiness result.
|
|
113
|
+
const ready = new MutationObserver(select); ready.observe(setupRoot, { attributes: true, attributeFilter: ["data-ready"] });
|
|
114
|
+
addEventListener("pagehide", () => { observer.disconnect(); ready.disconnect(); cards.dispose(); }, { once: true });
|
|
115
|
+
}
|
package/dist/usage-report.d.ts
CHANGED
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
* Honest by construction: an amount is "API-equivalent at list price (checked <date>) — not money spent"; an unpriced
|
|
8
8
|
* model's tokens are shown and its amount is not; Codex's cache writes are "not reported" (every CLI seen records 0),
|
|
9
9
|
* never priced as uncached; an instance whose backend AgEnD cannot measure is listed with its reason, never as $0.
|
|
10
|
+
* Kiro records no token counts: its rows are ESTIMATED from text size (kiro-usage.ts, design §10.2) and say so — on the
|
|
11
|
+
* row (`estimated`), on any total that includes one (`includes_estimates`, with the estimated part of the amount), in
|
|
12
|
+
* the API, on the page and in the CSV. An estimate never mixes silently into a measured number.
|
|
10
13
|
*/
|
|
11
14
|
import type { Ledger } from "./cache-ledger.js";
|
|
12
15
|
import { type UsageDays } from "./usage-rollup.js";
|
|
@@ -27,6 +30,12 @@ export declare const usageBasis: (checked?: string) => string;
|
|
|
27
30
|
* latest whole days and says from which date (`dailyFrom`); the rows and totals always cover the whole range.
|
|
28
31
|
*/
|
|
29
32
|
export declare const USAGE_DAILY_MAX = 20000;
|
|
33
|
+
/** The backends whose usage is estimated, not measured (#2122 §10.2: Kiro records no token counts). */
|
|
34
|
+
export declare const ESTIMATED_BACKENDS: ReadonlySet<string>;
|
|
35
|
+
/** The backends whose transcripts report reasoning tokens (#2122 §10.3); elsewhere a 0 means not reported. */
|
|
36
|
+
export declare const REPORTS_REASONING: ReadonlySet<string>;
|
|
37
|
+
/** The backends whose transcripts carry a cache-write field that stays 0: "not reported" until a real write is seen. */
|
|
38
|
+
export declare const WRITES_UNREPORTED: ReadonlySet<string>;
|
|
30
39
|
export interface UsageInstanceInput {
|
|
31
40
|
name: string;
|
|
32
41
|
/** Its backend now: what it is collected with from here on (each slice of its history carries its own). */
|
|
@@ -63,7 +72,10 @@ export interface UsageInput {
|
|
|
63
72
|
export interface UsageTotals {
|
|
64
73
|
/** Uncached input. */
|
|
65
74
|
input: number;
|
|
75
|
+
/** Output without its reasoning part — the units of tokscale's daily rows (design §10.3). */
|
|
66
76
|
output: number;
|
|
77
|
+
/** The part of the output a CLI reports as reasoning (Codex); 0 where none is reported (Claude). */
|
|
78
|
+
reasoning: number;
|
|
67
79
|
cacheRead: number;
|
|
68
80
|
cacheWrite5m: number;
|
|
69
81
|
cacheWrite1h: number;
|
|
@@ -73,6 +85,12 @@ export interface UsageTotals {
|
|
|
73
85
|
}
|
|
74
86
|
export interface UsageRow extends UsageTotals {
|
|
75
87
|
key: string;
|
|
88
|
+
/** Whether any of it comes from a CLI that reports reasoning (REPORTS_REASONING): false → its 0 is "not reported". */
|
|
89
|
+
reasoningReported: boolean;
|
|
90
|
+
/** Some of it is estimated (ESTIMATED_BACKENDS); flagged "estimated" (a row) or "includes_estimates" (the totals). */
|
|
91
|
+
estimated: boolean;
|
|
92
|
+
/** The estimated part of `amount` (null: nothing estimated is priced). */
|
|
93
|
+
amountEstimated: number | null;
|
|
76
94
|
total: number;
|
|
77
95
|
/** Delivered messages; null where a message has no such key (by model, by profile). */
|
|
78
96
|
messages: number | null;
|
|
@@ -92,6 +110,8 @@ export interface UsageRow extends UsageTotals {
|
|
|
92
110
|
}
|
|
93
111
|
export interface UsageDaily extends UsageTotals {
|
|
94
112
|
date: string;
|
|
113
|
+
reasoningReported: boolean;
|
|
114
|
+
estimated: boolean;
|
|
95
115
|
zone: string;
|
|
96
116
|
instance: string;
|
|
97
117
|
backend: string;
|
package/dist/usage-report.js
CHANGED
|
@@ -10,6 +10,12 @@ export const usageBasis = (checked = PRICES_CHECKED) => `API-equivalent at list
|
|
|
10
10
|
*/
|
|
11
11
|
export const USAGE_DAILY_MAX = 20_000;
|
|
12
12
|
const M = 1_000_000;
|
|
13
|
+
/** The backends whose usage is estimated, not measured (#2122 §10.2: Kiro records no token counts). */
|
|
14
|
+
export const ESTIMATED_BACKENDS = new Set(["kiro-cli"]);
|
|
15
|
+
/** The backends whose transcripts report reasoning tokens (#2122 §10.3); elsewhere a 0 means not reported. */
|
|
16
|
+
export const REPORTS_REASONING = new Set(["codex", "grok", "muse", "opencode", "antigravity"]);
|
|
17
|
+
/** The backends whose transcripts carry a cache-write field that stays 0: "not reported" until a real write is seen. */
|
|
18
|
+
export const WRITES_UNREPORTED = new Set(["codex", "muse"]);
|
|
13
19
|
const DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
14
20
|
/** A range as asked (query string or tool argument); anything else is the last 7 days. */
|
|
15
21
|
export function usageRange(kind, from, to) {
|
|
@@ -31,19 +37,23 @@ export function rangeDates(range, now, zone) {
|
|
|
31
37
|
case "custom": return { from: range.from, to: range.to };
|
|
32
38
|
}
|
|
33
39
|
}
|
|
34
|
-
const zero = () => ({ input: 0, output: 0, cacheRead: 0, cacheWrite5m: 0, cacheWrite1h: 0, cacheWrite: 0, requests: 0 });
|
|
35
|
-
|
|
40
|
+
const zero = () => ({ input: 0, output: 0, reasoning: 0, cacheRead: 0, cacheWrite5m: 0, cacheWrite1h: 0, cacheWrite: 0, requests: 0 });
|
|
41
|
+
/** One slice into a row: `reasoning` is the part of the slice's output reported as reasoning (shown beside the rest). */
|
|
42
|
+
const addTotals = (into, [n, uncached, read, w5, w1h, w, out], reasoning) => {
|
|
43
|
+
const r = Math.min(Math.max(0, reasoning), out);
|
|
36
44
|
into.requests += n;
|
|
37
45
|
into.input += uncached;
|
|
38
46
|
into.cacheRead += read;
|
|
39
47
|
into.cacheWrite5m += w5;
|
|
40
48
|
into.cacheWrite1h += w1h;
|
|
41
49
|
into.cacheWrite += w;
|
|
42
|
-
into.output += out;
|
|
50
|
+
into.output += out - r;
|
|
51
|
+
into.reasoning += r;
|
|
43
52
|
};
|
|
44
53
|
const splitKey = (mk) => { const bar = mk.lastIndexOf("|"); return { model: mk.slice(0, bar), long: mk.slice(bar + 1) === "1" }; };
|
|
45
54
|
/**
|
|
46
|
-
* The list-price amount of one slice, or null when its model is not in the table.
|
|
55
|
+
* The list-price amount of one slice, or null when its model is not in the table. Reasoning is a part of the slice's
|
|
56
|
+
* output, so the whole output is priced at the output rate once. Codex writes not reported are 0 in the
|
|
47
57
|
* ledger (it marks writesSeen on the first one above 0), so they add nothing and are never priced as uncached input.
|
|
48
58
|
*/
|
|
49
59
|
function amountOf(model, long, [, uncached, read, w5, w1h, w, out]) {
|
|
@@ -52,6 +62,7 @@ function amountOf(model, long, [, uncached, read, w5, w1h, w, out]) {
|
|
|
52
62
|
return null;
|
|
53
63
|
return (uncached * r.input + read * r.read + w5 * r.write5m + w1h * r.write1h + w * r.write + out * r.output) / M;
|
|
54
64
|
}
|
|
65
|
+
const estimatedSlice = (s) => ESTIMATED_BACKENDS.has(s.backend);
|
|
55
66
|
const unknownSource = (s) => s.backend === "unknown" || s.profile === "unknown";
|
|
56
67
|
export function buildUsageReport(input) {
|
|
57
68
|
const { from, to } = rangeDates(input.range, input.now, input.zone);
|
|
@@ -70,14 +81,14 @@ export function buildUsageReport(input) {
|
|
|
70
81
|
if (!i.ledger || !i.days)
|
|
71
82
|
continue;
|
|
72
83
|
coverage.push({ name: i.name, intervals: i.days.coverage.map(([a, b]) => [a, b]) });
|
|
73
|
-
// Codex: a write is reported only once a real one has been seen (every CLI so far records 0).
|
|
84
|
+
// Codex and Muse: a write is reported only once a real one has been seen (every CLI so far records 0).
|
|
74
85
|
const writesSeen = i.ledger.writesSeen === true;
|
|
75
86
|
const base = { instance: i.name, team: i.team };
|
|
76
87
|
for (const row of i.days.rows) {
|
|
77
88
|
if (!inRange(row.date))
|
|
78
89
|
continue;
|
|
79
90
|
for (const [mk, tot] of Object.entries(row.m))
|
|
80
|
-
slices.push({ ...base, backend: row.be, writesReported: row.be
|
|
91
|
+
slices.push({ ...base, backend: row.be, writesReported: !WRITES_UNREPORTED.has(row.be) || writesSeen, date: row.date, zone: row.zone, profile: row.pf, ...splitKey(mk), tot, reasoning: row.r?.[mk] ?? 0, cost: row.c?.[mk] ?? 0 });
|
|
81
92
|
}
|
|
82
93
|
for (const [hk, hour] of Object.entries(i.ledger.hours)) {
|
|
83
94
|
const h = Number(hk);
|
|
@@ -88,7 +99,7 @@ export function buildUsageReport(input) {
|
|
|
88
99
|
continue;
|
|
89
100
|
for (const src of hourSources(hour)) {
|
|
90
101
|
for (const [mk, tot] of Object.entries(src.m))
|
|
91
|
-
slices.push({ ...base, backend: src.backend, writesReported: src.backend
|
|
102
|
+
slices.push({ ...base, backend: src.backend, writesReported: !WRITES_UNREPORTED.has(src.backend) || writesSeen, date, zone: input.zone, profile: src.profile, ...splitKey(mk), tot, reasoning: src.r[mk] ?? 0, cost: src.c[mk] ?? 0 });
|
|
92
103
|
}
|
|
93
104
|
}
|
|
94
105
|
}
|
|
@@ -102,7 +113,7 @@ export function buildUsageReport(input) {
|
|
|
102
113
|
}
|
|
103
114
|
};
|
|
104
115
|
const rows = new Map();
|
|
105
|
-
const newRow = (key) => ({ key, ...zero(), total: 0, messages: null, amount: null, priced: "none", flags: [], unpriced: [], instances: [], backends: [], models: [], profiles: [], _amount: 0, _pricedAny: false, _unpricedAny: false });
|
|
116
|
+
const newRow = (key) => ({ key, ...zero(), reasoningReported: false, estimated: false, amountEstimated: null, _estAmount: 0, _estPriced: false, total: 0, messages: null, amount: null, priced: "none", flags: [], unpriced: [], instances: [], backends: [], models: [], profiles: [], _amount: 0, _pricedAny: false, _unpricedAny: false });
|
|
106
117
|
const uniq = (list, v) => { if (!list.includes(v))
|
|
107
118
|
list.push(v); };
|
|
108
119
|
// Daily rows: one per day, zone, agent, backend, model, tier and profile (an hour's slices summed into their day).
|
|
@@ -110,12 +121,18 @@ export function buildUsageReport(input) {
|
|
|
110
121
|
for (const s of slices) {
|
|
111
122
|
const key = keyOf(s);
|
|
112
123
|
const row = rows.get(key) ?? (rows.set(key, newRow(key)), rows.get(key));
|
|
113
|
-
addTotals(row, s.tot);
|
|
124
|
+
addTotals(row, s.tot, s.reasoning);
|
|
125
|
+
if (REPORTS_REASONING.has(s.backend))
|
|
126
|
+
row.reasoningReported = true;
|
|
114
127
|
uniq(row.instances, s.instance);
|
|
115
128
|
uniq(row.backends, s.backend);
|
|
116
129
|
uniq(row.models, s.model);
|
|
117
130
|
uniq(row.profiles, s.profile);
|
|
118
|
-
|
|
131
|
+
// List price first; a model the table lacks takes the cost its CLI reported (Grok), said so (design §10.2).
|
|
132
|
+
const listed = amountOf(s.model, s.long, s.tot), reported = listed === null && s.cost > 0;
|
|
133
|
+
const amount = listed ?? (reported ? s.cost : null);
|
|
134
|
+
if (reported)
|
|
135
|
+
uniq(row.flags, "cost_reported");
|
|
119
136
|
if (amount === null) {
|
|
120
137
|
row._unpricedAny = true;
|
|
121
138
|
uniq(row.unpriced, s.model);
|
|
@@ -124,6 +141,14 @@ export function buildUsageReport(input) {
|
|
|
124
141
|
row._pricedAny = true;
|
|
125
142
|
row._amount += amount;
|
|
126
143
|
}
|
|
144
|
+
if (estimatedSlice(s)) {
|
|
145
|
+
row.estimated = true;
|
|
146
|
+
uniq(row.flags, "estimated");
|
|
147
|
+
if (amount !== null) {
|
|
148
|
+
row._estPriced = true;
|
|
149
|
+
row._estAmount += amount;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
127
152
|
if (!s.writesReported)
|
|
128
153
|
uniq(row.flags, "cache_write_not_reported");
|
|
129
154
|
if (unknownSource(s))
|
|
@@ -132,11 +157,13 @@ export function buildUsageReport(input) {
|
|
|
132
157
|
const dk = [s.date, s.zone, s.instance, s.backend, s.model, tier, s.profile].join("\u0000");
|
|
133
158
|
let d = dailyBy.get(dk);
|
|
134
159
|
if (!d) {
|
|
135
|
-
d = { date: s.date, zone: s.zone, instance: s.instance, backend: s.backend, model: s.model, tier, profile: s.profile, ...zero(), amount: amount === null ? null : 0,
|
|
136
|
-
flags: [...(s.writesReported ? [] : ["cache_write_not_reported"]), ...(amount === null ? ["not_priced"] : []), ...(unknownSource(s) ? ["source_unknown"] : [])] };
|
|
160
|
+
d = { date: s.date, zone: s.zone, instance: s.instance, backend: s.backend, model: s.model, tier, profile: s.profile, ...zero(), reasoningReported: REPORTS_REASONING.has(s.backend), estimated: estimatedSlice(s), amount: amount === null ? null : 0,
|
|
161
|
+
flags: [...(estimatedSlice(s) ? ["estimated"] : []), ...(s.writesReported ? [] : ["cache_write_not_reported"]), ...(amount === null ? ["not_priced"] : []), ...(unknownSource(s) ? ["source_unknown"] : [])] };
|
|
137
162
|
dailyBy.set(dk, d);
|
|
138
163
|
}
|
|
139
|
-
addTotals(d, s.tot);
|
|
164
|
+
addTotals(d, s.tot, s.reasoning);
|
|
165
|
+
if (reported && !d.flags.includes("cost_reported"))
|
|
166
|
+
d.flags.push("cost_reported");
|
|
140
167
|
if (amount !== null && d.amount !== null)
|
|
141
168
|
d.amount += amount;
|
|
142
169
|
}
|
|
@@ -188,8 +215,9 @@ export function buildUsageReport(input) {
|
|
|
188
215
|
row.messages ??= 0;
|
|
189
216
|
}
|
|
190
217
|
const finish = (row) => {
|
|
191
|
-
const { _amount, _pricedAny, _unpricedAny, ...rest } = row;
|
|
192
|
-
rest.
|
|
218
|
+
const { _amount, _pricedAny, _unpricedAny, _estAmount, _estPriced, ...rest } = row;
|
|
219
|
+
rest.amountEstimated = _estPriced ? _estAmount : null;
|
|
220
|
+
rest.total = rest.input + rest.output + rest.reasoning + rest.cacheRead + rest.cacheWrite5m + rest.cacheWrite1h + rest.cacheWrite;
|
|
193
221
|
rest.amount = _pricedAny ? _amount : null;
|
|
194
222
|
rest.priced = _pricedAny ? (_unpricedAny ? "partial" : "all") : "none";
|
|
195
223
|
if (_unpricedAny)
|
|
@@ -200,15 +228,24 @@ export function buildUsageReport(input) {
|
|
|
200
228
|
.sort((a, b) => (b.amount ?? -1) - (a.amount ?? -1) || b.total - a.total || a.key.localeCompare(b.key));
|
|
201
229
|
const totals = finish(newRow("all"));
|
|
202
230
|
for (const r of out) {
|
|
203
|
-
for (const k of ["input", "output", "cacheRead", "cacheWrite5m", "cacheWrite1h", "cacheWrite", "requests"])
|
|
231
|
+
for (const k of ["input", "output", "reasoning", "cacheRead", "cacheWrite5m", "cacheWrite1h", "cacheWrite", "requests"])
|
|
204
232
|
totals[k] += r[k];
|
|
205
233
|
totals.total += r.total;
|
|
206
234
|
if (r.messages !== null)
|
|
207
235
|
totals.messages = (totals.messages ?? 0) + r.messages;
|
|
208
236
|
if (r.amount !== null)
|
|
209
237
|
totals.amount = (totals.amount ?? 0) + r.amount;
|
|
238
|
+
if (r.reasoningReported)
|
|
239
|
+
totals.reasoningReported = true;
|
|
240
|
+
if (r.estimated) {
|
|
241
|
+
totals.estimated = true;
|
|
242
|
+
uniq(totals.flags, "includes_estimates");
|
|
243
|
+
}
|
|
244
|
+
if (r.amountEstimated !== null)
|
|
245
|
+
totals.amountEstimated = (totals.amountEstimated ?? 0) + r.amountEstimated;
|
|
210
246
|
for (const f of r.flags)
|
|
211
|
-
|
|
247
|
+
if (f !== "estimated")
|
|
248
|
+
uniq(totals.flags, f);
|
|
212
249
|
for (const m of r.unpriced)
|
|
213
250
|
uniq(totals.unpriced, m);
|
|
214
251
|
}
|