@songsid/agend 2.3.2-beta.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/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 +11 -0
- package/dist/channel/adapters/discord.js +71 -14
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/mcp-tools.js +29 -2
- 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/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 +53 -10
- 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 +23 -0
- package/dist/daemon.js +106 -7
- 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/doctor.js +2 -1
- package/dist/doctor.js.map +1 -1
- package/dist/fleet-manager.d.ts +72 -0
- package/dist/fleet-manager.js +507 -89
- package/dist/fleet-manager.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 +24 -1
- package/dist/general-knowledge/skills/web-appearance/SKILL.md +24 -1
- package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +37 -9
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +44 -11
- package/dist/instance-config-impact.js +10 -5
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +19 -0
- package/dist/instance-lifecycle.js +55 -12
- 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 +88 -8
- 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/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 +26 -2
- package/dist/outbound-schemas.js +27 -2
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/public-web-gateway.js +7 -1
- 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 +2 -0
- package/dist/settings-api.js +41 -16
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +17 -14
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-effect.js +7 -1
- 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/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 +1 -1
- package/dist/turn-reply-guard.js +1 -1
- package/dist/turn-reply-guard.js.map +1 -1
- package/dist/types.d.ts +28 -8
- package/dist/ui/backend-signin.js +63 -10
- package/dist/ui/chat-strings.js +2 -2
- package/dist/ui/fleet-office-editor.js +14 -9
- package/dist/ui/fleet-office-layout.js +70 -32
- package/dist/ui/fleet-office-model.js +6 -3
- package/dist/ui/fleet-office-overview.js +167 -0
- package/dist/ui/fleet-office-pack.d.ts +7 -2
- package/dist/ui/fleet-office-pack.js +119 -15
- package/dist/ui/fleet-office-skin.js +98 -23
- package/dist/ui/fleet-office-store.js +39 -5
- package/dist/ui/fleet-office-view.js +5 -4
- package/dist/ui/fleet-office-zones.js +172 -12
- package/dist/ui/fleet-office.js +547 -186
- package/dist/ui/fleet-usage.js +19 -15
- package/dist/ui/office-share-dialog.js +145 -0
- package/dist/ui/office-share-page.js +164 -0
- package/dist/ui/panel-chat.js +8 -1
- package/dist/ui/panel-log.js +2 -2
- package/dist/ui/panel-needs.js +4 -4
- package/dist/ui/panel-settings.js +37 -11
- package/dist/ui/settings-dialogs.js +8 -1
- package/dist/ui/settings-guests.js +17 -7
- package/dist/ui/settings-model.js +14 -6
- package/dist/ui/settings-registry.js +4 -0
- package/dist/ui/settings-strings.js +16 -2
- package/dist/ui/shared/app-i18n.js +4 -2
- package/dist/ui/shared/app-shell.js +14 -6
- package/dist/ui/shared/app-stream.js +2 -1
- package/dist/ui/shared/app.css +67 -2
- package/dist/ui/shared/office/cozy.png +0 -0
- package/dist/ui/shared/side-panel.js +95 -0
- 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 +18 -0
- package/dist/web-api.js +201 -0
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.js +1 -1
- 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-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 +3 -2
- package/dist/web-office-skins.js +3 -2
- package/dist/web-office-skins.js.map +1 -1
- package/dist/web-office-store.js +40 -7
- package/dist/web-office-store.js.map +1 -1
- package/dist/web-settings-patch.d.ts +2 -1
- package/dist/web-settings-patch.js +4 -1
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/working-recovery.d.ts +2 -2
- package/dist/working-recovery.js +43 -8
- package/dist/working-recovery.js.map +1 -1
- package/dist/working-state-journal.d.ts +2 -0
- package/dist/working-state-journal.js +16 -1
- package/dist/working-state-journal.js.map +1 -1
- package/package.json +3 -2
- package/templates/windows-shortcuts.ps1 +155 -140
package/dist/ui/shared/app.css
CHANGED
|
@@ -573,6 +573,9 @@ html:has(.shell), html:has(.shell) body { overflow: hidden; }
|
|
|
573
573
|
the message's overflow-wrap: anywhere that is one character wide (the #1672 status line's time, one glyph per line). */
|
|
574
574
|
.msg .time { font-family: var(--font-mono); white-space: nowrap; flex: none; }
|
|
575
575
|
.msg.user { align-self: flex-end; max-width: min(80%, 620px); }
|
|
576
|
+
/* Full width (the header's ⋯ menu, this browser's choice): the thread and the composer take the panel, less their own
|
|
577
|
+
padding, for wide tables and code; a person's bubble keeps its cap (above). A phone's column is the screen already. */
|
|
578
|
+
@media (min-width: 900px) { .p-chat[data-width="full"] .thread, .p-chat[data-width="full"] .dock-col { max-width: none; } }
|
|
576
579
|
.msg.user .meta { justify-content: flex-end; }
|
|
577
580
|
/* #1773: the bubble hugs its text. The message is as wide as its widest child — usually the meta row (sender, a time
|
|
578
581
|
such as 「昨天 下午03:58」, ticks) — and a block body would stretch to it; fit-content keeps a short message short, on
|
|
@@ -928,6 +931,8 @@ img.amb-p { object-fit: contain; }
|
|
|
928
931
|
.s-guests .guest-names { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 2px var(--s-3); max-height: 220px; overflow: auto;
|
|
929
932
|
padding: var(--s-1) 0; border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); }
|
|
930
933
|
.s-guests .guest-name span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
934
|
+
/* #2667: the ClassicBot rooms follow the fleet's instances under their own heading, across the grid. */
|
|
935
|
+
.s-guests .guest-names-group { grid-column: 1 / -1; margin-top: var(--s-2); }
|
|
931
936
|
.s-guests .guest-list, .s-guests .g-pick { list-style: none; margin: 0 0 var(--s-3); padding: 0; display: flex; flex-direction: column; border-top: 1px solid var(--border); }
|
|
932
937
|
.s-guests .guest-row, .s-guests .g-cand { padding: var(--s-3) 0; border-bottom: 1px solid var(--border); min-width: 0; }
|
|
933
938
|
.s-guests .guest-row.s-dirty { border-radius: 0; margin-left: 0; align-self: stretch; padding-left: calc(var(--s-2) + 3px); padding-right: var(--s-2); }
|
|
@@ -1345,6 +1350,18 @@ body.side-resizing, body.side-resizing * { cursor: col-resize !important; user-s
|
|
|
1345
1350
|
@media (prefers-reduced-motion: no-preference) { .u-crow { transition: background-color 0.15s var(--ease); } }
|
|
1346
1351
|
.u-rail-body { flex: 1; min-height: 0; overflow-y: auto; padding: 0 var(--s-3) var(--s-4); display: flex; flex-direction: column; gap: var(--s-3); }
|
|
1347
1352
|
@media (max-width: 1199px) { .u-rail { display: none; } }
|
|
1353
|
+
/* #2585: the docked side panel (side-panel.js), in the usage rail's place beside the page — a non-modal dialog, so the
|
|
1354
|
+
shared dialog's head, body and actions, laid out as a full-height column. Below the rail's width it is never drawn
|
|
1355
|
+
(the editor is the centred Dialog there). */
|
|
1356
|
+
.dlg.side-panel { position: static; inset: auto; margin: 0; width: 400px; max-width: 400px; height: auto; max-height: none; flex-shrink: 0;
|
|
1357
|
+
display: flex; flex-direction: column; min-height: 0; border-radius: 0; box-shadow: none; border-left: 1px solid var(--border); background: var(--bg); color: var(--text); }
|
|
1358
|
+
.side-panel .dlg-box { flex: 1; min-height: 0; max-height: none; }
|
|
1359
|
+
.side-panel .dlg-head { padding: var(--s-3) var(--s-3) var(--s-3) var(--s-5); border-bottom: 1px solid var(--border); }
|
|
1360
|
+
.side-panel .dlg-body { flex: 1; min-height: 0; padding-top: var(--s-4); }
|
|
1361
|
+
.side-panel .dlg-actions { padding: var(--s-3) var(--s-5); border-top: 1px solid var(--border); }
|
|
1362
|
+
@media (max-width: 1199px) { .dlg.side-panel { display: none; } }
|
|
1363
|
+
/* A toast (Offices saved.) beside a docked panel, not over its Cancel and Save. */
|
|
1364
|
+
@media (min-width: 1200px) { .shell:has(> .side-panel) .toasts { right: calc(400px + var(--s-6)); } }
|
|
1348
1365
|
@media (max-width: 899px) {
|
|
1349
1366
|
.hide-narrow { display: none; }
|
|
1350
1367
|
.v-id { padding-left: var(--s-3); padding-right: var(--s-3); }
|
|
@@ -1728,7 +1745,7 @@ strong.org-name { font-size: var(--fs-sm); color: var(--text); }
|
|
|
1728
1745
|
.oc-office > .oc-bar { margin: 0; padding: var(--s-3) var(--s-5) var(--s-2); }
|
|
1729
1746
|
.oc-office > .oc-legend { margin: 0 var(--s-5) var(--s-2); }
|
|
1730
1747
|
.oo { flex: 1 0 auto; display: flex; flex-direction: column; gap: var(--s-2); min-width: 0; width: 100%; }
|
|
1731
|
-
.oo > :not(.oo-stage) { margin-left: var(--s-5); margin-right: var(--s-5); }
|
|
1748
|
+
.oo > :not(.oo-stage):not(dialog) { margin-left: var(--s-5); margin-right: var(--s-5); } /* not the editors' dialogs: they centre themselves (#2585) */
|
|
1732
1749
|
.oo-bar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); }
|
|
1733
1750
|
.oo-pick { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-xs); color: var(--text-2); }
|
|
1734
1751
|
.oo-pick .tb-select { max-width: 200px; }
|
|
@@ -1762,6 +1779,8 @@ strong.org-name { font-size: var(--fs-sm); color: var(--text); }
|
|
|
1762
1779
|
.oo-people-list label { display: flex; align-items: center; gap: 8px; padding: 3px 2px; font-size: var(--fs-xs); color: var(--text); cursor: pointer; overflow-wrap: anywhere; }
|
|
1763
1780
|
.oo-note { font-size: var(--fs-xs); }
|
|
1764
1781
|
.oo-stage { position: relative; flex: 1 1 auto; min-height: 340px; overflow: hidden; background: var(--bg); touch-action: none; user-select: none; -webkit-user-select: none; }
|
|
1782
|
+
/* #2554: clip, not a scroll container — nothing past the edge (a tile's label) can scroll the stage and its controls. */
|
|
1783
|
+
.oo-stage { overflow: clip; }
|
|
1765
1784
|
.oo-canvas { position: absolute; left: 0; top: 0; display: block; image-rendering: pixelated; cursor: grab; touch-action: none; }
|
|
1766
1785
|
.oo-canvas:active { cursor: grabbing; }
|
|
1767
1786
|
.oo-canvas:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
|
|
@@ -1785,6 +1804,16 @@ strong.org-name { font-size: var(--fs-sm); color: var(--text); }
|
|
|
1785
1804
|
.oo-mini-dot.match { fill: var(--focus); }
|
|
1786
1805
|
.oo-tip { position: absolute; left: 0; top: 0; z-index: 5; max-width: 240px; pointer-events: none; padding: 6px 10px; font-size: var(--fs-xs); line-height: 1.4;
|
|
1787
1806
|
color: var(--text); background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-md); box-shadow: var(--shadow-2); overflow-wrap: anywhere; }
|
|
1807
|
+
/* #2554: All offices — each tile's label (office name and head count) just above the tile, placed by the page (left, top,
|
|
1808
|
+
max-width through the CSSOM); a button that opens the office, the lobby's a plain chip. */
|
|
1809
|
+
.oo-tile-label { position: absolute; left: 0; top: 0; z-index: 3; transform: translateY(calc(-100% - 4px)); max-width: 240px; overflow: hidden; text-overflow: ellipsis;
|
|
1810
|
+
white-space: nowrap; padding: 2px 8px; font: inherit; font-size: var(--fs-xs); font-weight: var(--fw-semibold); line-height: 18px; color: var(--text);
|
|
1811
|
+
background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-sm); }
|
|
1812
|
+
button.oo-tile-label { cursor: pointer; }
|
|
1813
|
+
button.oo-tile-label:hover { border-color: var(--accent); }
|
|
1814
|
+
button.oo-tile-label:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
|
|
1815
|
+
.oo-tile-label.lobby { color: var(--text-2); }
|
|
1816
|
+
.oo-lobby-tile { font-size: var(--fs-xs); color: var(--text-2); }
|
|
1788
1817
|
/* The desks as a list: for a screen reader, and shown (over the stage's corner) while the keyboard is in it. */
|
|
1789
1818
|
.oo-desks { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; list-style: none; }
|
|
1790
1819
|
.oo-desks:focus-within { position: absolute; z-index: 7; left: var(--s-2); bottom: var(--s-2); width: auto; height: auto; max-height: 40%; margin: 0; padding: var(--s-1); clip: auto; overflow-y: auto;
|
|
@@ -1819,18 +1848,25 @@ html.oo-immersive .p-fleet > .panel-body { z-index: 50; }
|
|
|
1819
1848
|
.ol-seat > circle { fill: var(--ok); }
|
|
1820
1849
|
.ol-furniture > rect { fill: var(--warn); }
|
|
1821
1850
|
.ol-door > rect { fill: var(--danger); }
|
|
1851
|
+
/* #2665: an item's night place — a moon on a faint disc, a thin dashed line to its day place. */
|
|
1852
|
+
.ol-night > circle { fill: var(--surface); fill-opacity: 0.85; }
|
|
1853
|
+
.ol-night > .ol-moon { fill: var(--warn); stroke: none; pointer-events: none; }
|
|
1854
|
+
.ol-night-line { stroke: var(--warn); stroke-width: 1.5; stroke-dasharray: 4 3; stroke-opacity: 0.8; pointer-events: none; }
|
|
1822
1855
|
.ol-waypoint > circle { fill: var(--focus); }
|
|
1823
1856
|
.ol-handle.on > circle, .ol-handle.on > rect, .ol-handle:focus-visible > circle, .ol-handle:focus-visible > rect { stroke: var(--focus); stroke-width: 4; }
|
|
1824
1857
|
.ol-bar { position: absolute; left: 0; right: 0; bottom: 0; z-index: 8; display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); padding: var(--s-2);
|
|
1825
1858
|
background: var(--surface); border-top: 1px solid var(--border); }
|
|
1826
1859
|
.ol-actions { display: inline-flex; gap: var(--s-2); margin-left: auto; }
|
|
1860
|
+
/* #2585: Edit layout's controls docked in the side panel (a wide desk): one column, the waypoint's own row wrapping. */
|
|
1861
|
+
.ol-side { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-3); }
|
|
1862
|
+
.ol-side .ol-corridors { display: flex; flex-wrap: wrap; gap: var(--s-2); }
|
|
1827
1863
|
.ol-corridors { display: inline-flex; flex-wrap: wrap; gap: 4px; }
|
|
1828
1864
|
.ol-msg { flex-basis: 100%; margin: 0; }
|
|
1829
1865
|
@media (max-width: 640px) {
|
|
1830
1866
|
/* A phone: the bars at the column's narrower padding; the stage at least most of a screen; no overview (as the canvas). */
|
|
1831
1867
|
.oc-office > .oc-bar { padding: var(--s-2) var(--s-3); }
|
|
1832
1868
|
.oc-office > .oc-legend { margin: 0 var(--s-3) var(--s-2); }
|
|
1833
|
-
.oo > :not(.oo-stage) { margin-left: var(--s-3); margin-right: var(--s-3); }
|
|
1869
|
+
.oo > :not(.oo-stage):not(dialog) { margin-left: var(--s-3); margin-right: var(--s-3); }
|
|
1834
1870
|
.oo-stage { min-height: max(300px, calc(100dvh - 300px)); }
|
|
1835
1871
|
.oo-pick .tb-select { max-width: 150px; }
|
|
1836
1872
|
.oo-mini { display: none; }
|
|
@@ -1906,6 +1942,7 @@ html.oo-immersive .p-fleet > .panel-body { z-index: 50; }
|
|
|
1906
1942
|
.us-bar { display: block; width: 100%; height: 6px; margin: 4px 0; }
|
|
1907
1943
|
.us-bar-bg { fill: var(--surface-2); } .us-bar-fg { fill: var(--us-0); }
|
|
1908
1944
|
.us-meta { display: block; font-size: var(--fs-xs); color: var(--text-3); overflow-wrap: anywhere; }
|
|
1945
|
+
.us-est { display: block; font-size: var(--fs-xs); font-weight: var(--fw-regular); color: var(--text-3); }
|
|
1909
1946
|
.us-flags { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }
|
|
1910
1947
|
.us-trend, .us-heat { display: flex; flex-direction: column; gap: var(--s-2); min-width: 0; }
|
|
1911
1948
|
.us-trend h3, .us-heat h3, .us-rest h3 { font-size: var(--fs-sm); font-weight: var(--fw-semibold); }
|
|
@@ -2473,3 +2510,31 @@ html.oo-immersive .p-fleet > .panel-body { z-index: 50; }
|
|
|
2473
2510
|
.msg.user .msg-reactions { justify-content: flex-end; }
|
|
2474
2511
|
.msg-reactions .reaction { display: inline-flex; align-items: center; justify-content: center; min-width: 28px; height: 24px; padding: 0 6px;
|
|
2475
2512
|
border: 1px solid var(--border); border-radius: var(--r-full); background: var(--surface-2); font-family: var(--font-emoji); font-size: 15px; line-height: 1; }
|
|
2513
|
+
|
|
2514
|
+
/* #2636: an Office share link's page (office-share-page.js) — the Office alone, filling the window under a one-line head. */
|
|
2515
|
+
.share-page { display: flex; flex-direction: column; min-height: 100dvh; background: var(--bg); color: var(--text); }
|
|
2516
|
+
.share-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-1) var(--s-3); padding: var(--s-3) var(--s-4); border-bottom: 1px solid var(--border); }
|
|
2517
|
+
.share-head .share-note { margin-left: auto; }
|
|
2518
|
+
.share-stage { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; padding: var(--s-2); }
|
|
2519
|
+
.share-loading, .share-page > .note { padding: var(--s-4); }
|
|
2520
|
+
.share-ended { align-items: center; justify-content: center; padding: var(--s-6); text-align: center; }
|
|
2521
|
+
@media (max-width: 640px) { .share-head .share-note { margin-left: 0; flex-basis: 100%; } }
|
|
2522
|
+
|
|
2523
|
+
/* #2636: Office share links — the Share… dialog and the open links (office-share-dialog.js; Settings → Access & security). */
|
|
2524
|
+
.os { display: flex; flex-direction: column; gap: var(--s-3); }
|
|
2525
|
+
.os > p { margin: 0; }
|
|
2526
|
+
.os-form { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-3); align-items: end; }
|
|
2527
|
+
.os-form .check, .os-form .note { grid-column: 1 / -1; margin: 0; }
|
|
2528
|
+
.os-made { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); }
|
|
2529
|
+
.os-url { flex: 1 1 260px; min-width: 0; min-height: 40px; padding: var(--s-2) var(--s-3); border: 1px solid var(--border-strong); border-radius: var(--r-sm);
|
|
2530
|
+
background: var(--bg); color: var(--text); font-size: var(--fs-sm); outline: none; }
|
|
2531
|
+
.os-url:focus { border-color: var(--focus); box-shadow: 0 0 0 3px var(--accent-dim); }
|
|
2532
|
+
.os-made .note { flex-basis: 100%; margin: 0; }
|
|
2533
|
+
.os-head { margin: var(--s-2) 0 0; font-size: var(--fs-sm); font-weight: var(--fw-medium); color: var(--text-2); }
|
|
2534
|
+
.os-rows { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-2); }
|
|
2535
|
+
.os-row { display: flex; align-items: center; gap: var(--s-3); padding: var(--s-2) var(--s-3); border: 1px solid var(--border); border-radius: var(--r-sm); }
|
|
2536
|
+
.os-what { flex: 1; min-width: 0; overflow-wrap: anywhere; font-size: var(--fs-sm); }
|
|
2537
|
+
.os-all { margin-top: var(--s-2); }
|
|
2538
|
+
.os-none { margin: 0; }
|
|
2539
|
+
.os-form .os-err { grid-column: 1 / -1; margin: 0; padding: var(--s-2) var(--s-3); border: 1px solid var(--danger); border-radius: var(--r-sm); }
|
|
2540
|
+
@media (max-width: 480px) { .os-form { grid-template-columns: 1fr; } }
|
|
Binary file
|
|
@@ -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
|
+
}
|
|
@@ -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
|
}
|