@songsid/agend 2.3.0-beta.2 → 2.3.0-beta.21
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/README.md +7 -1
- package/README.zh-TW.md +7 -1
- package/dist/agent-cli-instructions.md +9 -2
- package/dist/agent-cli.js +63 -2
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.js +4 -0
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/agent-file-read.d.ts +4 -1
- package/dist/agent-file-read.js +189 -16
- package/dist/agent-file-read.js.map +1 -1
- package/dist/agent-whats-new.d.ts +11 -0
- package/dist/agent-whats-new.js +29 -5
- package/dist/agent-whats-new.js.map +1 -1
- package/dist/auth-api.d.ts +30 -1
- package/dist/auth-api.js +45 -13
- package/dist/auth-api.js.map +1 -1
- package/dist/authority/authority-state.d.ts +38 -0
- package/dist/authority/authority-state.js +114 -0
- package/dist/authority/authority-state.js.map +1 -0
- package/dist/authority/session-authorizations.d.ts +43 -0
- package/dist/authority/session-authorizations.js +102 -0
- package/dist/authority/session-authorizations.js.map +1 -0
- package/dist/authority/web-authority.d.ts +120 -0
- package/dist/authority/web-authority.js +157 -0
- package/dist/authority/web-authority.js.map +1 -0
- package/dist/backend/claude-code.d.ts +2 -2
- package/dist/backend/claude-code.js +18 -8
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex-metadata.d.ts +8 -0
- package/dist/backend/codex-metadata.js +74 -17
- package/dist/backend/codex-metadata.js.map +1 -1
- package/dist/backend/codex.js +15 -2
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/credential-profile.d.ts +2 -0
- package/dist/backend/credential-profile.js +2 -0
- package/dist/backend/credential-profile.js.map +1 -1
- package/dist/backend/types.d.ts +6 -0
- package/dist/backend/types.js +15 -0
- package/dist/backend/types.js.map +1 -1
- package/dist/backend-availability.d.ts +120 -0
- package/dist/backend-availability.js +396 -0
- package/dist/backend-availability.js.map +1 -0
- package/dist/backend-command.d.ts +23 -3
- package/dist/backend-command.js +25 -4
- package/dist/backend-command.js.map +1 -1
- package/dist/bug-report-cli.d.ts +30 -0
- package/dist/bug-report-cli.js +138 -0
- package/dist/bug-report-cli.js.map +1 -0
- package/dist/bug-report.d.ts +47 -0
- package/dist/bug-report.js +136 -0
- package/dist/bug-report.js.map +1 -0
- package/dist/bundled-skills.d.ts +14 -0
- package/dist/bundled-skills.js +29 -0
- package/dist/bundled-skills.js.map +1 -0
- package/dist/cache-ledger.d.ts +16 -3
- package/dist/cache-ledger.js +33 -13
- package/dist/cache-ledger.js.map +1 -1
- package/dist/cache-service.d.ts +55 -3
- package/dist/cache-service.js +152 -16
- package/dist/cache-service.js.map +1 -1
- package/dist/channel/adapters/discord-client-retirement.d.ts +5 -0
- package/dist/channel/adapters/discord-client-retirement.js +57 -0
- package/dist/channel/adapters/discord-client-retirement.js.map +1 -0
- package/dist/channel/adapters/discord.d.ts +9 -0
- package/dist/channel/adapters/discord.js +49 -4
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/mcp-tools.js +22 -11
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/types.d.ts +6 -0
- package/dist/classic-channel-manager.d.ts +10 -5
- package/dist/classic-channel-manager.js +12 -9
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli.js +224 -111
- package/dist/cli.js.map +1 -1
- package/dist/config-validator.d.ts +6 -0
- package/dist/config-validator.js +129 -3
- package/dist/config-validator.js.map +1 -1
- package/dist/config.d.ts +13 -1
- package/dist/config.js +14 -5
- package/dist/config.js.map +1 -1
- package/dist/cross-instance-notice.d.ts +7 -0
- package/dist/cross-instance-notice.js +10 -0
- package/dist/cross-instance-notice.js.map +1 -1
- package/dist/daemon.d.ts +24 -1
- package/dist/daemon.js +151 -19
- package/dist/daemon.js.map +1 -1
- package/dist/decision-list.d.ts +25 -0
- package/dist/decision-list.js +30 -0
- package/dist/decision-list.js.map +1 -0
- package/dist/delivery-outbox.d.ts +10 -0
- package/dist/delivery-outbox.js +17 -1
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/diagnostic-redaction.d.ts +10 -0
- package/dist/diagnostic-redaction.js +167 -0
- package/dist/diagnostic-redaction.js.map +1 -0
- package/dist/fleet-context.d.ts +10 -0
- package/dist/fleet-manager.d.ts +505 -15
- package/dist/fleet-manager.js +1870 -204
- package/dist/fleet-manager.js.map +1 -1
- package/dist/fleet-process-handlers.d.ts +18 -0
- package/dist/fleet-process-handlers.js +69 -0
- package/dist/fleet-process-handlers.js.map +1 -0
- package/dist/fleet-traffic.d.ts +74 -0
- package/dist/fleet-traffic.js +196 -0
- package/dist/fleet-traffic.js.map +1 -0
- package/dist/general-knowledge/skills/bug-report/SKILL.md +31 -0
- package/dist/general-knowledge/skills/credential-profiles/SKILL.md +17 -6
- package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +6 -1
- package/dist/general-knowledge/skills/fleet-config/SKILL.md +2 -0
- package/dist/general-knowledge/skills/multi-channel/SKILL.md +3 -2
- package/dist/general-knowledge/skills/persona-emoji/SKILL.md +1 -1
- package/dist/general-knowledge/skills/scheduling/SKILL.md +4 -0
- package/dist/general-knowledge/skills/web-appearance/SKILL.md +190 -47
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +545 -0
- package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +1 -1
- package/dist/human-delivery-receipts.d.ts +24 -1
- package/dist/human-delivery-receipts.js +64 -11
- package/dist/human-delivery-receipts.js.map +1 -1
- package/dist/instance-config-impact.js +7 -1
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +10 -1
- package/dist/instance-lifecycle.js +79 -18
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/instance-names.d.ts +4 -0
- package/dist/instance-names.js +12 -0
- package/dist/instance-names.js.map +1 -0
- package/dist/instructions.d.ts +12 -1
- package/dist/instructions.js +13 -2
- package/dist/instructions.js.map +1 -1
- package/dist/locale.js +173 -12
- package/dist/locale.js.map +1 -1
- package/dist/log-steps.d.ts +1 -0
- package/dist/log-steps.js +15 -0
- package/dist/log-steps.js.map +1 -1
- package/dist/login-controller.d.ts +45 -0
- package/dist/login-controller.js +101 -5
- package/dist/login-controller.js.map +1 -1
- package/dist/ls-attach-resolve.d.ts +96 -0
- package/dist/ls-attach-resolve.js +180 -0
- package/dist/ls-attach-resolve.js.map +1 -0
- package/dist/ls-rows.d.ts +12 -0
- package/dist/ls-rows.js +82 -3
- package/dist/ls-rows.js.map +1 -1
- package/dist/memory-hogs.d.ts +27 -0
- package/dist/memory-hogs.js +124 -0
- package/dist/memory-hogs.js.map +1 -0
- package/dist/needs-you.d.ts +10 -0
- package/dist/needs-you.js +2 -1
- package/dist/needs-you.js.map +1 -1
- package/dist/outbound-handlers.d.ts +11 -0
- package/dist/outbound-handlers.js +139 -11
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +200 -4
- package/dist/outbound-schemas.js +124 -11
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/public-web-gateway.js +17 -4
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/public-web-link.d.ts +9 -0
- package/dist/public-web-link.js +8 -0
- package/dist/public-web-link.js.map +1 -1
- package/dist/quickstart-api.d.ts +19 -1
- package/dist/quickstart-api.js +94 -4
- package/dist/quickstart-api.js.map +1 -1
- package/dist/reply-buttons-controller.d.ts +4 -0
- package/dist/reply-buttons-controller.js +2 -2
- package/dist/reply-buttons-controller.js.map +1 -1
- package/dist/restart-progress.d.ts +80 -1
- package/dist/restart-progress.js +306 -21
- package/dist/restart-progress.js.map +1 -1
- package/dist/rollout-index.d.ts +5 -0
- package/dist/rollout-index.js +26 -5
- package/dist/rollout-index.js.map +1 -1
- package/dist/scheduler/db.d.ts +40 -13
- package/dist/scheduler/db.js +123 -89
- package/dist/scheduler/db.js.map +1 -1
- package/dist/scheduler/task-board-text.d.ts +26 -0
- package/dist/scheduler/task-board-text.js +135 -0
- package/dist/scheduler/task-board-text.js.map +1 -0
- package/dist/scheduler/task-board.d.ts +650 -0
- package/dist/scheduler/task-board.js +1938 -0
- package/dist/scheduler/task-board.js.map +1 -0
- package/dist/scheduler/types.d.ts +6 -1
- package/dist/search/search-docs.d.ts +58 -0
- package/dist/search/search-docs.js +63 -0
- package/dist/search/search-docs.js.map +1 -0
- package/dist/search/search-index.d.ts +141 -0
- package/dist/search/search-index.js +440 -0
- package/dist/search/search-index.js.map +1 -0
- package/dist/search/search-store.d.ts +122 -0
- package/dist/search/search-store.js +236 -0
- package/dist/search/search-store.js.map +1 -0
- package/dist/search/search-worker.d.ts +1 -0
- package/dist/search/search-worker.js +163 -0
- package/dist/search/search-worker.js.map +1 -0
- package/dist/service-installer.js +12 -0
- package/dist/service-installer.js.map +1 -1
- package/dist/settings-api.d.ts +23 -0
- package/dist/settings-api.js +164 -3
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +104 -0
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-confirmation.d.ts +3 -1
- package/dist/settings-confirmation.js +2 -0
- package/dist/settings-confirmation.js.map +1 -1
- package/dist/settings-effect.js +34 -3
- package/dist/settings-effect.js.map +1 -1
- package/dist/settings-http-confirmation.d.ts +11 -0
- package/dist/settings-http-confirmation.js +10 -0
- package/dist/settings-http-confirmation.js.map +1 -1
- package/dist/setup-form.d.ts +1 -1
- package/dist/setup-form.js +23 -7
- package/dist/setup-form.js.map +1 -1
- package/dist/spawn-gate.d.ts +1 -0
- package/dist/spawn-gate.js +13 -3
- package/dist/spawn-gate.js.map +1 -1
- package/dist/task-board-tool.d.ts +25 -0
- package/dist/task-board-tool.js +154 -0
- package/dist/task-board-tool.js.map +1 -0
- package/dist/task-board-waker.d.ts +44 -0
- package/dist/task-board-waker.js +128 -0
- package/dist/task-board-waker.js.map +1 -0
- package/dist/tmux-exec-helper.d.ts +94 -0
- package/dist/tmux-exec-helper.js +407 -0
- package/dist/tmux-exec-helper.js.map +1 -0
- package/dist/tmux-manager.d.ts +5 -0
- package/dist/tmux-manager.js +42 -11
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tool-permissions.d.ts +0 -7
- package/dist/tool-permissions.js +44 -5
- package/dist/tool-permissions.js.map +1 -1
- package/dist/topic-commands.d.ts +17 -1
- package/dist/topic-commands.js +45 -19
- package/dist/topic-commands.js.map +1 -1
- package/dist/tunnel/cloudflared.js +37 -2
- package/dist/tunnel/cloudflared.js.map +1 -1
- package/dist/tunnel/types.d.ts +3 -1
- package/dist/tunnel/types.js.map +1 -1
- package/dist/types.d.ts +63 -1
- package/dist/ui/app-effects.js +90 -32
- package/dist/ui/backend-signin.js +297 -0
- package/dist/ui/chat-ambient.js +307 -0
- package/dist/ui/chat-effect-posters.js +8 -8
- package/dist/ui/chat-effects.js +110 -15
- package/dist/ui/chat-msg-actions.js +176 -0
- package/dist/ui/chat-render.js +21 -3
- package/dist/ui/chat-store.js +64 -12
- package/dist/ui/chat-strings.js +85 -8
- package/dist/ui/chat-thread.js +204 -21
- package/dist/ui/effect-trial-controller.js +13 -8
- package/dist/ui/effect-trial-panel.js +19 -16
- package/dist/ui/effects-waker.js +70 -0
- package/dist/ui/fleet-canvas-layout.js +117 -0
- package/dist/ui/fleet-canvas.js +555 -0
- package/dist/ui/fleet-org.js +27 -12
- package/dist/ui/fleet-route-scene.js +207 -0
- package/dist/ui/fleet-route-worker.js +21 -0
- package/dist/ui/fleet-route.js +88 -0
- package/dist/ui/fleet-task-card.js +436 -0
- package/dist/ui/fleet-task-common.js +448 -0
- package/dist/ui/fleet-tasks.js +535 -0
- package/dist/ui/fleet-traffic.js +359 -0
- package/dist/ui/fleet-usage.js +435 -0
- package/dist/ui/panel-chat.js +264 -66
- package/dist/ui/panel-details.js +2 -1
- package/dist/ui/panel-fleet.js +65 -83
- package/dist/ui/panel-needs.js +4 -2
- package/dist/ui/panel-settings.js +56 -11
- package/dist/ui/preview.js +3 -2
- package/dist/ui/search-dialog.js +349 -0
- package/dist/ui/settings-appearance.js +44 -19
- package/dist/ui/settings-apply.js +3 -3
- package/dist/ui/settings-catalog.js +92 -0
- package/dist/ui/settings-confirm.js +7 -2
- package/dist/ui/settings-dialogs.js +40 -12
- package/dist/ui/settings-effects.js +4 -3
- package/dist/ui/settings-guests.js +117 -0
- package/dist/ui/settings-learn.js +25 -0
- package/dist/ui/settings-model.js +15 -6
- package/dist/ui/settings-mods.js +23 -9
- package/dist/ui/settings-owner-effects.js +15 -13
- package/dist/ui/settings-strings.js +122 -42
- package/dist/ui/settings-web-authority.js +91 -0
- package/dist/ui/shared/agend-auth.js +1 -1
- package/dist/ui/shared/app-i18n.js +11 -2
- package/dist/ui/shared/app-prefs.js +17 -4
- package/dist/ui/shared/app-route.js +19 -1
- package/dist/ui/shared/app-session.js +2 -0
- package/dist/ui/shared/app-shell.js +174 -30
- package/dist/ui/shared/app-store.js +26 -1
- package/dist/ui/shared/app-stream.js +5 -1
- package/dist/ui/shared/app-themes.js +140 -7
- package/dist/ui/shared/app.css +705 -20
- package/dist/ui/shared/app.js +26 -15
- package/dist/ui/shared/card-strings.js +8 -0
- package/dist/ui/shared/effect-app-runtime.js +68 -19
- package/dist/ui/shared/effect-chat-runtime.js +16 -3
- package/dist/ui/shared/effect-frame.js +26 -7
- package/dist/ui/shared/effect-geometry.js +24 -9
- package/dist/ui/shared/effect-owner-source.js +8 -1
- package/dist/ui/shared/effect-policy.js +21 -7
- package/dist/ui/shared/effect-profiler.js +31 -9
- package/dist/ui/shared/effect-registry.js +97 -50
- package/dist/ui/shared/effect-stock-snapshot.js +9 -0
- package/dist/ui/shared/effect-stock-trust.js +62 -10
- package/dist/ui/shared/effect-trial-shadow.js +62 -0
- package/dist/ui/shared/effect-trial-source.js +1 -1
- package/dist/ui/shared/effect-trial-stage.js +54 -31
- package/dist/ui/shared/effect-trial.css +40 -0
- package/dist/ui/shared/fx-motion.js +67 -0
- package/dist/ui/shared/identity-card.js +100 -11
- package/dist/ui/shared/identity-color.js +14 -10
- package/dist/ui/shared/instance-nav.js +179 -23
- package/dist/ui/shared/owner-css.js +30 -1
- package/dist/ui/shared/panel-view.js +24 -4
- package/dist/ui/shared/tokens.css +9 -3
- package/dist/ui/shared/ui-icons.js +2 -0
- package/dist/ui/shared/ui-menu.js +20 -7
- package/dist/ui/shared/ui-toast.js +16 -3
- package/dist/ui/shared/usage-dialog.js +34 -3
- package/dist/ui/shared/view-strings.js +72 -0
- package/dist/ui/shared/web-authorize.js +198 -0
- package/dist/ui/web-terminal/terminal.css +4 -4
- package/dist/ui/web-terminal/terminal.html +1 -1
- package/dist/update-launch.d.ts +9 -0
- package/dist/update-launch.js +38 -12
- package/dist/update-launch.js.map +1 -1
- package/dist/usage/format-rich.js +6 -3
- package/dist/usage/format-rich.js.map +1 -1
- package/dist/usage/i18n-keys.d.ts +1 -1
- package/dist/usage/i18n-keys.js +2 -1
- package/dist/usage/i18n-keys.js.map +1 -1
- package/dist/usage/i18n.d.ts +13 -1
- package/dist/usage/i18n.js +29 -0
- package/dist/usage/i18n.js.map +1 -1
- package/dist/usage/providers.d.ts +81 -2
- package/dist/usage/providers.js +240 -38
- package/dist/usage/providers.js.map +1 -1
- package/dist/usage/usage-api.d.ts +5 -1
- package/dist/usage/usage-api.js +184 -58
- package/dist/usage/usage-api.js.map +1 -1
- package/dist/usage-report.d.ts +158 -0
- package/dist/usage-report.js +226 -0
- package/dist/usage-report.js.map +1 -0
- package/dist/usage-rollup.d.ts +51 -0
- package/dist/usage-rollup.js +168 -0
- package/dist/usage-rollup.js.map +1 -0
- package/dist/user-systemd.d.ts +24 -0
- package/dist/user-systemd.js +75 -0
- package/dist/user-systemd.js.map +1 -0
- package/dist/view-api.d.ts +39 -0
- package/dist/view-api.js +69 -6
- package/dist/view-api.js.map +1 -1
- package/dist/web-ambient.d.ts +72 -0
- package/dist/web-ambient.js +161 -0
- package/dist/web-ambient.js.map +1 -0
- package/dist/web-api.d.ts +98 -14
- package/dist/web-api.js +1027 -83
- package/dist/web-api.js.map +1 -1
- package/dist/web-appearance-policy.d.ts +4 -6
- package/dist/web-appearance-policy.js +5 -12
- package/dist/web-appearance-policy.js.map +1 -1
- package/dist/web-appearance-tools.d.ts +3 -0
- package/dist/web-appearance-tools.js +22 -3
- package/dist/web-appearance-tools.js.map +1 -1
- package/dist/web-appearance-view.d.ts +27 -2
- package/dist/web-appearance-view.js +129 -14
- package/dist/web-appearance-view.js.map +1 -1
- package/dist/web-appearance.d.ts +17 -3
- package/dist/web-appearance.js +85 -8
- package/dist/web-appearance.js.map +1 -1
- package/dist/web-asset-parts.d.ts +2 -2
- package/dist/web-asset-parts.js +3 -3
- package/dist/web-asset-parts.js.map +1 -1
- package/dist/web-auth.js +3 -1
- package/dist/web-auth.js.map +1 -1
- package/dist/web-backend-login.d.ts +130 -0
- package/dist/web-backend-login.js +225 -0
- package/dist/web-backend-login.js.map +1 -0
- package/dist/web-card.d.ts +14 -0
- package/dist/web-card.js +40 -6
- package/dist/web-card.js.map +1 -1
- package/dist/web-channel-echo.d.ts +7 -1
- package/dist/web-channel-echo.js +6 -2
- package/dist/web-channel-echo.js.map +1 -1
- package/dist/web-chat-history.d.ts +83 -0
- package/dist/web-chat-history.js +137 -5
- package/dist/web-chat-history.js.map +1 -1
- package/dist/web-commands.d.ts +6 -1
- package/dist/web-commands.js +1 -1
- package/dist/web-commands.js.map +1 -1
- package/dist/web-effect-bootstrap.d.ts +12 -0
- package/dist/web-effect-bootstrap.js +49 -0
- package/dist/web-effect-bootstrap.js.map +1 -0
- package/dist/web-effect-bundle.js +46 -1
- package/dist/web-effect-bundle.js.map +1 -1
- package/dist/web-effect-playback.d.ts +30 -0
- package/dist/web-effect-playback.js +71 -0
- package/dist/web-effect-playback.js.map +1 -0
- package/dist/web-effect-profiles.d.ts +4 -4
- package/dist/web-effect-service.d.ts +11 -4
- package/dist/web-effect-service.js +25 -4
- package/dist/web-effect-service.js.map +1 -1
- package/dist/web-effect-tools.d.ts +1 -0
- package/dist/web-effect-tools.js +4 -4
- package/dist/web-effect-tools.js.map +1 -1
- package/dist/web-effect-trials.d.ts +2 -2
- package/dist/web-effect-trials.js +3 -3
- package/dist/web-effect-trials.js.map +1 -1
- package/dist/web-effect-upload.js +1 -1
- package/dist/web-effect-upload.js.map +1 -1
- package/dist/web-effect-worker.d.ts +1 -1
- package/dist/web-effect-worker.js +2 -2
- package/dist/web-effect-worker.js.map +1 -1
- package/dist/web-effect.d.ts +1 -1
- package/dist/web-guest-access.d.ts +126 -0
- package/dist/web-guest-access.js +238 -0
- package/dist/web-guest-access.js.map +1 -0
- package/dist/web-guest-rooms.d.ts +82 -0
- package/dist/web-guest-rooms.js +135 -0
- package/dist/web-guest-rooms.js.map +1 -0
- package/dist/web-host-guard.d.ts +11 -0
- package/dist/web-host-guard.js +11 -0
- package/dist/web-host-guard.js.map +1 -1
- package/dist/web-identity-color.d.ts +1 -1
- package/dist/web-identity-color.js +2 -2
- package/dist/web-identity-color.js.map +1 -1
- package/dist/web-identity-palette.d.ts +1 -1
- package/dist/web-identity-palette.js +2 -2
- package/dist/web-identity-palette.js.map +1 -1
- package/dist/web-image-meta.d.ts +13 -0
- package/dist/web-image-meta.js +62 -0
- package/dist/web-image-meta.js.map +1 -1
- package/dist/web-libavoid.d.ts +14 -0
- package/dist/web-libavoid.js +46 -0
- package/dist/web-libavoid.js.map +1 -0
- package/dist/web-login.d.ts +47 -0
- package/dist/web-login.js +115 -0
- package/dist/web-login.js.map +1 -1
- package/dist/web-mod-agent.d.ts +1 -1
- package/dist/web-mod-agent.js +7 -5
- package/dist/web-mod-agent.js.map +1 -1
- package/dist/web-mod-bundle.d.ts +9 -1
- package/dist/web-mod-bundle.js +62 -3
- package/dist/web-mod-bundle.js.map +1 -1
- package/dist/web-mod-export.d.ts +9 -0
- package/dist/web-mod-export.js +21 -0
- package/dist/web-mod-export.js.map +1 -1
- package/dist/web-mod-store.d.ts +7 -1
- package/dist/web-mod-store.js +19 -6
- package/dist/web-mod-store.js.map +1 -1
- package/dist/web-owner-css-store.d.ts +44 -1
- package/dist/web-owner-css-store.js +70 -9
- package/dist/web-owner-css-store.js.map +1 -1
- package/dist/web-prefs.d.ts +14 -0
- package/dist/web-prefs.js +37 -4
- package/dist/web-prefs.js.map +1 -1
- package/dist/web-preview.d.ts +6 -2
- package/dist/web-preview.js +4 -2
- package/dist/web-preview.js.map +1 -1
- package/dist/web-returning-device.d.ts +41 -0
- package/dist/web-returning-device.js +57 -5
- package/dist/web-returning-device.js.map +1 -1
- package/dist/web-session-authorization.d.ts +132 -0
- package/dist/web-session-authorization.js +193 -0
- package/dist/web-session-authorization.js.map +1 -0
- package/dist/web-session.d.ts +70 -10
- package/dist/web-session.js +137 -22
- package/dist/web-session.js.map +1 -1
- package/dist/web-settings-patch.d.ts +49 -2
- package/dist/web-settings-patch.js +145 -1
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/web-settings-tools.d.ts +94 -0
- package/dist/web-settings-tools.js +301 -0
- package/dist/web-settings-tools.js.map +1 -0
- package/dist/web-shell-routes.d.ts +1 -1
- package/dist/web-shell-routes.js +1 -1
- package/dist/web-shell-routes.js.map +1 -1
- package/dist/web-terminal-http.js +15 -2
- package/dist/web-terminal-http.js.map +1 -1
- package/dist/web-theme-store.d.ts +39 -0
- package/dist/web-theme-store.js +122 -11
- package/dist/web-theme-store.js.map +1 -1
- package/dist/web-theme.d.ts +31 -0
- package/dist/web-theme.js +99 -2
- package/dist/web-theme.js.map +1 -1
- package/dist/web-upload.d.ts +13 -0
- package/dist/web-upload.js +39 -3
- package/dist/web-upload.js.map +1 -1
- package/dist/windows-shortcuts.d.ts +78 -0
- package/dist/windows-shortcuts.js +203 -0
- package/dist/windows-shortcuts.js.map +1 -0
- package/dist/workflow-templates/default.md +4 -1
- package/dist/yaml-shared-nodes.d.ts +25 -0
- package/dist/yaml-shared-nodes.js +185 -0
- package/dist/yaml-shared-nodes.js.map +1 -0
- package/launcher/agend-install.cjs +553 -0
- package/launcher/install-admission.cjs +4 -39
- package/launcher/postinstall.cjs +14 -12
- package/package.json +4 -2
- package/scripts/preinstall-guard.cjs +2 -5
- package/templates/windows-shortcuts.ps1 +181 -0
- package/launcher/old-updater-note.cjs +0 -48
package/dist/types.d.ts
CHANGED
|
@@ -172,6 +172,12 @@ export interface InstanceConfig {
|
|
|
172
172
|
* Mirror Topic are unaffected. Unset inherits `defaults.cross_instance_visibility`, else "full".
|
|
173
173
|
*/
|
|
174
174
|
cross_instance_visibility?: "full" | "summary" | "hidden";
|
|
175
|
+
/**
|
|
176
|
+
* #2303: how many task board cards handed to this instance it may have in progress at once (claiming another at the
|
|
177
|
+
* limit is refused). A whole number of at least 1; unset is 1. Cards it opened for itself never count. The fleet
|
|
178
|
+
* reads it at each claim; the daemon only keeps it current.
|
|
179
|
+
*/
|
|
180
|
+
board_cards_at_once?: number;
|
|
175
181
|
/**
|
|
176
182
|
* Detect a human-channel turn that ended without a delivered reply and ask
|
|
177
183
|
* the agent to send one bounded recovery reply. Only capable backends use
|
|
@@ -345,6 +351,12 @@ export interface WebConfig {
|
|
|
345
351
|
echo_to_channel?: boolean;
|
|
346
352
|
/** Show the AI subscription usage panel on /view and serve /api/ai-usage (default true). */
|
|
347
353
|
usage_panel?: boolean;
|
|
354
|
+
/**
|
|
355
|
+
* #2122: serve the usage report (Fleet → Usage, GET /ui/usage-report) over the temporary public link too (default
|
|
356
|
+
* false). It is up to 400 days of per-agent, per-model, per-profile activity and amounts — more than the live quota
|
|
357
|
+
* rail or the cache report the link already shows. Guests never get it.
|
|
358
|
+
*/
|
|
359
|
+
usage_report_on_public_link?: boolean;
|
|
348
360
|
/**
|
|
349
361
|
* #1937: the largest file the web chat takes, in MB (whole number, 1–500; default 50). Never more than
|
|
350
362
|
* upload_max_total_mb; over the public link a file is at most 95 MB whatever this says (the quick tunnel's limit).
|
|
@@ -359,6 +371,15 @@ export interface WebConfig {
|
|
|
359
371
|
* admin's change, confirmed like any access change (Settings → Web).
|
|
360
372
|
*/
|
|
361
373
|
log_tab?: boolean;
|
|
374
|
+
/**
|
|
375
|
+
* #2293: the dashboard's global search (Ctrl+K) over chat messages, cross-instance messages and Task Board tasks — an
|
|
376
|
+
* index of its own (search.db, kept `retention_days`, default 90; search/search-index.ts). `enabled: false`: no index
|
|
377
|
+
* from the next fleet start (the file is left as it is), and searches are refused at once.
|
|
378
|
+
*/
|
|
379
|
+
search?: {
|
|
380
|
+
enabled?: boolean;
|
|
381
|
+
retention_days?: number;
|
|
382
|
+
};
|
|
362
383
|
/** Expose provider API-key verify/apply endpoints (default false until dark-shipped). */
|
|
363
384
|
provider_secrets?: boolean;
|
|
364
385
|
/**
|
|
@@ -378,7 +399,7 @@ export interface WebConfig {
|
|
|
378
399
|
/**
|
|
379
400
|
* #1620: how long a web session lasts. Only the LOCAL surface (the dashboard reached directly — loopback, a LAN, a
|
|
380
401
|
* tailnet) is configurable; a public-link session's limits stay fixed. Hours, 1 ≤ idle_hours ≤ absolute_hours ≤ 720;
|
|
381
|
-
* defaults
|
|
402
|
+
* defaults 48 (absolute, from sign-in) and 48 (idle, since the last use; #1670). Shortening applies to existing sessions.
|
|
382
403
|
*/
|
|
383
404
|
session?: {
|
|
384
405
|
local?: {
|
|
@@ -417,6 +438,47 @@ export interface WebConfig {
|
|
|
417
438
|
* (and warned about); a change applies to new uploads only.
|
|
418
439
|
*/
|
|
419
440
|
appearance?: import("./web-appearance-limits.js").AppearanceLimitsConfig;
|
|
441
|
+
/**
|
|
442
|
+
* #1920: guest web sign-in — a non-admin signed in as their own Discord or Telegram account, seeing only the instances
|
|
443
|
+
* they already use in chat (derived on every request). Default off.
|
|
444
|
+
*/
|
|
445
|
+
guests?: WebGuestsConfig;
|
|
446
|
+
/**
|
|
447
|
+
* #1939: when a signed-in web session's messages carry authority (docs/design/1939-verified-web-sessions.md §3).
|
|
448
|
+
* S1 records it in receipts only; no agent acts on it yet.
|
|
449
|
+
*/
|
|
450
|
+
verified_authority?: WebVerifiedAuthorityConfig;
|
|
451
|
+
}
|
|
452
|
+
/** #1939: fleet.yaml `web.verified_authority` (docs/design/1939-verified-web-sessions.md §0, §3, §10). */
|
|
453
|
+
export interface WebVerifiedAuthorityConfig {
|
|
454
|
+
/**
|
|
455
|
+
* The server switch (§10): web messages carry authority only while it is on (default true, §0.1). Turning it off makes
|
|
456
|
+
* every web authority read as none at once; turning it back on never brings back authority given before.
|
|
457
|
+
*/
|
|
458
|
+
enabled?: boolean;
|
|
459
|
+
/** "Only this computer can authorize": only loopback requests qualify (default false: LAN and tailnet too). */
|
|
460
|
+
loopback_only?: boolean;
|
|
461
|
+
/** A session's authorization lasts this many hours after it was given, 1–48 (default 48). */
|
|
462
|
+
max_session_hours?: number;
|
|
463
|
+
}
|
|
464
|
+
/** #1920: fleet.yaml `web.guests`. */
|
|
465
|
+
export interface WebGuestsConfig {
|
|
466
|
+
/** Guest sign-in at all (default false). Turning it off ends every guest session. */
|
|
467
|
+
enabled?: boolean;
|
|
468
|
+
/** Guests may chat (default true); false: read-only. */
|
|
469
|
+
chat?: boolean;
|
|
470
|
+
/**
|
|
471
|
+
* An `open` connection's guests reach nothing until an admin names instances here, per connection id (the user's
|
|
472
|
+
* decision: least privilege, since an open group has no list of people).
|
|
473
|
+
*/
|
|
474
|
+
open_instances?: Record<string, string[]>;
|
|
475
|
+
/** An admin's narrowing, per `<connection id>:<user id>`: intersected with the derived scope, never widening it. */
|
|
476
|
+
narrow?: Record<string, string[]>;
|
|
477
|
+
/** A guest session's limits: absolute 1–24 h (default 12), idle 15–720 min (default 120). */
|
|
478
|
+
session?: {
|
|
479
|
+
absolute_hours?: number;
|
|
480
|
+
idle_minutes?: number;
|
|
481
|
+
};
|
|
420
482
|
}
|
|
421
483
|
/** `login:` section — remote /login behaviour. */
|
|
422
484
|
export interface LoginConfig {
|
package/dist/ui/app-effects.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
/** #1629: one session-only app effect owner. Importing this module starts nothing. */
|
|
2
2
|
import { appStore } from "/assets/app-store.js";
|
|
3
|
+
import { motionBlocked, onMotionChange } from "/assets/fx-motion.js";
|
|
3
4
|
import { navStore } from "/assets/app-nav.js";
|
|
4
|
-
import { onPrefsChanged
|
|
5
|
-
import { resolvedMode, themesStore } from "/assets/app-themes.js";
|
|
5
|
+
import { onPrefsChanged } from "/assets/app-prefs.js";
|
|
6
|
+
import { resolvedMode, themesStore, themesShown } from "/assets/app-themes.js";
|
|
6
7
|
import { ownerCssLifecycle, safeMode } from "/assets/owner-css.js";
|
|
7
8
|
import { constrainedDevice } from "/assets/effect-policy.js";
|
|
8
9
|
import { createAppEffectRuntime } from "/assets/effect-app-runtime.js";
|
|
10
|
+
import { effectBootstrapReady } from "/assets/effect-frame.js";
|
|
9
11
|
import { createEffectCursor } from "/assets/effect-cursor.js";
|
|
10
12
|
import { createOwnerEffectRegions } from "/assets/effect-owner-regions.js";
|
|
11
|
-
import { chatEffectIdentity } from "/assets/effect-chat-runtime.js";
|
|
13
|
+
import { chatEffectIdentity, effectPlaybackIdentity } from "/assets/effect-chat-runtime.js";
|
|
12
14
|
import { createEffectTrialController } from "./effect-trial-controller.js";
|
|
13
15
|
import { effectTrialMessage } from "./effect-trial-offer.js";
|
|
14
16
|
import { claimPanel, panelVersion, panelStore, openTrialPanel, closePanel } from "./preview-panel.js";
|
|
@@ -19,8 +21,9 @@ export function getAppEffects(host = window) { const value = owners.get(host); r
|
|
|
19
21
|
export function startAppEffects({ store, window: host = window, factories = {} } = {}) {
|
|
20
22
|
const existing = owners.get(host);
|
|
21
23
|
if (existing) { if (!existing.dead) existing.bindStore(store); return getAppEffects(host); }
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
const playbackIdentity = effectPlaybackIdentity(host), ownerIdentity = chatEffectIdentity(host);
|
|
25
|
+
if (!playbackIdentity || typeof host.AgendAuth?.onSessionEnd !== "function" || typeof host.AgendAuth?.sessionEnded !== "function") return null;
|
|
26
|
+
const doc = host.document, releases = [], ended = new Set(), updates = new Set(), trialUpdates = new Set(); let replyOff = null, replyStore = null, regions = null, cursor = null, cursorStamp = null, trialControl = null, trialIntent = null, trialHint = ownerIdentity ? null : false, trialSerial = 0, trialStamp = null, chatBinding = null;
|
|
24
27
|
const safe = fn => { try { return fn(); } catch { return null; } };
|
|
25
28
|
const hold = off => { if (entry.dead) safe(() => off?.()); else releases.push(off); };
|
|
26
29
|
const entry = { dead: false, api: null,
|
|
@@ -38,11 +41,23 @@ export function startAppEffects({ store, window: host = window, factories = {} }
|
|
|
38
41
|
if (entry.dead || replyStore !== value) safe(() => off?.()); else replyOff = off;
|
|
39
42
|
}
|
|
40
43
|
},
|
|
41
|
-
|
|
44
|
+
trialStatus(message) {
|
|
42
45
|
const offer = safe(() => effectTrialMessage(message)), nav = navStore.get();
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
const now = safe(() => host.Date.now());
|
|
47
|
+
if (!offer || !Number.isFinite(now) || now >= offer.expiresAt) return "expired";
|
|
48
|
+
if (trialHint === null && !entry.dead) return "checking";
|
|
49
|
+
if (entry.dead || !trialHint || trialControl?.ok !== true) return "localAdmin";
|
|
50
|
+
if (!chatBinding || safe(() => chatBinding.current()) !== true || message.instance !== nav.route?.instance || nav.route?.panel !== "chat") return "chatChanged";
|
|
51
|
+
const state = trialControl.snapshot();
|
|
52
|
+
if (state.phase !== "idle" || state.busy) return "busy";
|
|
53
|
+
return trialPlaybackStatus();
|
|
45
54
|
},
|
|
55
|
+
trialFailure(message) {
|
|
56
|
+
const offer = safe(() => effectTrialMessage(message)), state = trialControl?.snapshot();
|
|
57
|
+
return offer && state?.phase === "idle" && state.offer === offer.offer && !["stopped", "replaced"].includes(state.reason) ? state.reason : null;
|
|
58
|
+
},
|
|
59
|
+
canTry(message) { return entry.trialStatus(message) === "ready"; },
|
|
60
|
+
stopTrial() { trialControl?.stop(); },
|
|
46
61
|
startTrial(message) {
|
|
47
62
|
if (!entry.canTry(message)) return Promise.resolve(false);
|
|
48
63
|
const nav = navStore.get(); trialIntent = { name: message.instance, seq: nav.seq, claim: claimPanel(), handle: null, chat: chatBinding };
|
|
@@ -67,25 +82,69 @@ export function startAppEffects({ store, window: host = window, factories = {} }
|
|
|
67
82
|
},
|
|
68
83
|
};
|
|
69
84
|
owners.set(host, entry); // Reentrant setup never constructs another owner.
|
|
70
|
-
let media, coarse
|
|
85
|
+
let media, coarse;
|
|
86
|
+
function ownerCssForPlayback() {
|
|
87
|
+
if (doc.body.dataset.tier !== "guest") return ownerCssLifecycle.get().state;
|
|
88
|
+
// Guests never install owner CSS. Prove its absence rather than borrowing the uninstalled
|
|
89
|
+
// lifecycle's initial unknown, and refuse unexpected styles instead of trusting a missing id.
|
|
90
|
+
try {
|
|
91
|
+
if (doc.body.dataset.ownerCss || doc.querySelectorAll("#owner-css").length) return "unknown";
|
|
92
|
+
// A CSP-refused or disabled STYLE has no paint. Recheck on every proof so enabling
|
|
93
|
+
// it never borrows an earlier absence verdict (WebKit capture inserts a refused no-op).
|
|
94
|
+
for (const style of doc.querySelectorAll("style")) if (style.sheet && style.sheet.disabled !== true) return "unknown";
|
|
95
|
+
const allowed = new Set(["/assets/tokens.css", "/assets/app.css"]);
|
|
96
|
+
for (const link of doc.querySelectorAll('link[rel~="stylesheet"]')) {
|
|
97
|
+
const url = new URL(link.getAttribute("href"), host.location.origin);
|
|
98
|
+
if (url.origin !== host.location.origin || !allowed.has(url.pathname) || url.search || url.hash) return "unknown";
|
|
99
|
+
}
|
|
100
|
+
return "off";
|
|
101
|
+
} catch { return "unknown"; }
|
|
102
|
+
}
|
|
103
|
+
function guestChatVersion(value) {
|
|
104
|
+
if (doc.body.dataset.tier !== "guest") return null;
|
|
105
|
+
const name = navStore.get().route?.instance, v = value.instances?.find(i => i.name === name)?.effect_chat_v;
|
|
106
|
+
return typeof v === "string" && /^[a-f0-9]{32}$/.test(v) ? v : null;
|
|
107
|
+
}
|
|
108
|
+
// #2188: whether a dialog, menu or popover is open — a whole-document query when none is (the usual case), asked
|
|
109
|
+
// several times per sync. Read once per task: the cached value is dropped at the next microtask, and whenever the
|
|
110
|
+
// document changes (the MutationObserver below drops it before it reads).
|
|
111
|
+
let modalCache = null;
|
|
112
|
+
function modalOpen() {
|
|
113
|
+
if (modalCache === null) {
|
|
114
|
+
modalCache = !!doc.querySelector("dialog[open],.menu-list,.pop,[role=alertdialog],.oc-keep,.shell.sb-open");
|
|
115
|
+
Promise.resolve().then(() => { modalCache = null; });
|
|
116
|
+
}
|
|
117
|
+
return modalCache;
|
|
118
|
+
}
|
|
71
119
|
function context() {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
show: prefInForce("fx.show")?.value !== false, previewReady: ready,
|
|
76
|
-
optIn: prefInForce("preview.optIn")?.value === true && preview?.optedIn() === true && preview?.never() !== true,
|
|
120
|
+
return { safe: safeMode(), publicLink: doc.body.dataset.publicLink === "1", reducedMotion: motionBlocked(host), saveData: host.navigator?.connection?.saveData === true,
|
|
121
|
+
hidden: doc.visibilityState !== "visible", enabled: entry.api?.playbackAvailable !== false && typeof host.OffscreenCanvas === "function" && typeof host.Worker === "function" && typeof host.MutationObserver === "function" && typeof host.ResizeObserver === "function",
|
|
122
|
+
show: themesShown(), transportReady: effectBootstrapReady(host, host.location?.origin, doc.body.dataset.effectBoot),
|
|
77
123
|
constrained: constrainedDevice({ memory: host.navigator?.deviceMemory, cores: host.navigator?.hardwareConcurrency, coarse: coarse?.matches === true, width: host.innerWidth }),
|
|
78
|
-
modal:
|
|
124
|
+
modal: modalOpen(), openInstance: null };
|
|
79
125
|
}
|
|
80
126
|
function trialCurrent(name) {
|
|
81
127
|
const intent = trialIntent, nav = navStore.get();
|
|
82
128
|
return !entry.dead && host.document === doc && !!intent && !!intent.chat && chatBinding === intent.chat && intent.chat.current() === true && intent.name === name && nav.seq === intent.seq && nav.route?.panel === "chat" && nav.route.instance === name
|
|
83
129
|
&& (panelVersion() === intent.claim || intent.handle !== null && panelStore.get().open?.handle === intent.handle);
|
|
84
130
|
}
|
|
131
|
+
// One priority order feeds both presentation and redraw identity: false→different-false must redraw.
|
|
132
|
+
function trialPlaybackStatus() {
|
|
133
|
+
const c = context();
|
|
134
|
+
if (c.safe) return "safe";
|
|
135
|
+
if (!c.show) return "effectsOff";
|
|
136
|
+
if (c.reducedMotion) return "reducedMotion";
|
|
137
|
+
if (!c.enabled) return "unsupported";
|
|
138
|
+
if (!c.transportReady) return "transport";
|
|
139
|
+
if (c.publicLink) return "localAdmin";
|
|
140
|
+
if (c.hidden || doc.hasFocus?.() !== true) return "inactive";
|
|
141
|
+
if (c.modal) return "dialog";
|
|
142
|
+
return "ready";
|
|
143
|
+
}
|
|
85
144
|
function trialAllowed() {
|
|
86
145
|
const c = context();
|
|
87
|
-
return !entry.dead && doc.visibilityState === "visible" && doc.hasFocus?.() === true
|
|
88
|
-
&& c.enabled && c.show && c.
|
|
146
|
+
return !entry.dead && doc.visibilityState === "visible" && doc.hasFocus?.() === true
|
|
147
|
+
&& c.enabled && c.show && c.transportReady && !c.safe && !c.publicLink && !c.reducedMotion && !c.hidden && !c.modal;
|
|
89
148
|
}
|
|
90
149
|
function mountTrial(handle) {
|
|
91
150
|
const intent = trialIntent; if (!intent || !trialCurrent(handle.instance) || !handle.current() || !Number.isSafeInteger(++trialSerial)) throw Error("trial_unavailable");
|
|
@@ -97,7 +156,7 @@ export function startAppEffects({ store, window: host = window, factories = {} }
|
|
|
97
156
|
if (panelStore.get().open?.handle === handle) closePanel();
|
|
98
157
|
};
|
|
99
158
|
try {
|
|
100
|
-
openTrialPanel({ key: binding.key, instance: handle.instance, binding, handle,
|
|
159
|
+
openTrialPanel({ key: binding.key, instance: handle.instance, binding, handle,
|
|
101
160
|
onStage(value) {
|
|
102
161
|
stage = value;
|
|
103
162
|
if (!handle.current()) { cleanup(); handle.stop(); return; }
|
|
@@ -111,7 +170,7 @@ export function startAppEffects({ store, window: host = window, factories = {} }
|
|
|
111
170
|
}
|
|
112
171
|
function notifyTrial() {
|
|
113
172
|
if (entry.dead) return;
|
|
114
|
-
const nav = navStore.get(), next = JSON.stringify([trialHint, safe(() => chatBinding?.current()) === true,
|
|
173
|
+
const nav = navStore.get(), next = JSON.stringify([trialHint, safe(() => chatBinding?.current()) === true, trialPlaybackStatus(), nav.route?.panel, nav.route?.instance, trialControl?.snapshot()]);
|
|
115
174
|
if (next === trialStamp) return; trialStamp = next;
|
|
116
175
|
for (const fn of [...trialUpdates]) safe(fn);
|
|
117
176
|
}
|
|
@@ -120,47 +179,45 @@ export function startAppEffects({ store, window: host = window, factories = {} }
|
|
|
120
179
|
function syncCursor() {
|
|
121
180
|
if (entry.dead || !cursor) return;
|
|
122
181
|
const c = context();
|
|
123
|
-
cursor.update(entry.api?.cursorSelection?.() ?? null, c.enabled && c.show && c.
|
|
182
|
+
cursor.update(entry.api?.cursorSelection?.() ?? null, c.enabled && c.show && c.transportReady && !c.safe && !c.publicLink && !c.reducedMotion && !c.hidden && !c.modal && !cursorDragging());
|
|
124
183
|
}
|
|
125
184
|
try {
|
|
126
185
|
if (appStore.get().connection === "ended") { entry.dispose(); return null; }
|
|
127
|
-
preview = host.AgendPreview;
|
|
128
186
|
media = host.matchMedia?.("(prefers-reduced-motion: reduce)") ?? { matches: true };
|
|
129
187
|
coarse = host.matchMedia?.("(pointer: coarse)") ?? { matches: false };
|
|
130
188
|
entry.api = (factories.runtime ?? createAppEffectRuntime)({ window: host, current: () => !entry.dead && host.document === doc,
|
|
131
|
-
observe: () => ({ context: context(), ownerCss:
|
|
189
|
+
observe: () => ({ context: context(), ownerCss: ownerCssForPlayback(), mode: resolvedMode() }), onDispose: () => entry.dispose(),
|
|
132
190
|
resolveOwnerTarget: (slot, manifest, proof) => !entry.dead ? regions?.resolve?.(slot, manifest.box, proof) ?? null : null,
|
|
133
191
|
onChange: () => { syncCursor(); notify(); } });
|
|
134
192
|
if (entry.dead || !entry.api.ok) { entry.dispose(); return null; }
|
|
135
|
-
trialControl = (factories.trialController ?? createEffectTrialController)({ window: host, current: trialCurrent, allowed: trialAllowed, onStart: mountTrial, onChange: notify });
|
|
193
|
+
if (ownerIdentity) trialControl = (factories.trialController ?? createEffectTrialController)({ window: host, current: trialCurrent, allowed: trialAllowed, onStart: mountTrial, onChange: notify });
|
|
136
194
|
hold(() => trialControl?.dispose());
|
|
137
|
-
if (typeof host.AgendAuth.trialSession === "function") Promise.resolve().then(() => host.AgendAuth.trialSession()).then(value => {
|
|
195
|
+
if (ownerIdentity && typeof host.AgendAuth.trialSession === "function") Promise.resolve().then(() => host.AgendAuth.trialSession()).then(value => {
|
|
138
196
|
if (entry.dead) return; trialHint = value?.localAdmin === true && value.epoch === chatEffectIdentity(host)?.epoch; notify();
|
|
139
|
-
}).catch(() => {});
|
|
197
|
+
}).catch(() => { if (!entry.dead) { trialHint = false; notify(); } });
|
|
140
198
|
hold(panelStore.subscribe(() => { trialControl?.sync(); notifyTrial(); }));
|
|
141
|
-
const ownRegions = (factories.regions ?? createOwnerEffectRegions)({ window: host, current: () => !entry.dead && host.document === doc });
|
|
199
|
+
const ownRegions = ownerIdentity && (factories.regions ?? createOwnerEffectRegions)({ window: host, current: () => !entry.dead && host.document === doc });
|
|
142
200
|
regions = ownRegions?.ok ? ownRegions : null; hold(() => ownRegions?.dispose?.());
|
|
143
201
|
if (entry.dead) return null;
|
|
144
|
-
const ownCursor = safe(() => (factories.cursor ?? createEffectCursor)({ window: host, current: () => !entry.dead && host.document === doc,
|
|
202
|
+
const ownCursor = ownerIdentity && safe(() => (factories.cursor ?? createEffectCursor)({ window: host, current: () => !entry.dead && host.document === doc,
|
|
145
203
|
readImage: (selection, signal) => entry.api?.cursorImageSettled?.(selection, signal) ?? Promise.resolve(null),
|
|
146
204
|
onChange(value) { const next = JSON.stringify(value); if (next !== cursorStamp) { cursorStamp = next; notify(); } },
|
|
147
205
|
}));
|
|
148
206
|
cursor = ownCursor?.ok ? ownCursor : null; hold(() => ownCursor?.dispose?.());
|
|
149
207
|
if (entry.dead) return null;
|
|
150
208
|
const sync = () => { if (!entry.dead) { trialControl?.sync(); entry.api.sync(); syncCursor(); notifyTrial(); } };
|
|
151
|
-
let revision = appStore.get().themesRev, prefsRevision = appStore.get().prefsRev, navSeq = navStore.get().seq;
|
|
209
|
+
let revision = appStore.get().themesRev, prefsRevision = appStore.get().prefsRev, guestRevision = guestChatVersion(appStore.get()), navSeq = navStore.get().seq;
|
|
152
210
|
hold(navStore.subscribe(value => { if (entry.dead || value.seq === navSeq) return; navSeq = value.seq; trialControl?.stop(); chatBinding = null; entry.api.detachChat(); syncCursor(); }));
|
|
153
211
|
hold(appStore.subscribe(value => {
|
|
154
212
|
if (entry.dead) return;
|
|
155
213
|
if (value.connection === "ended") { entry.dispose(); return; }
|
|
156
|
-
const chatChanged = value.themesRev !== revision, ownerChanged = value.prefsRev !== prefsRevision;
|
|
157
|
-
revision = value.themesRev; prefsRevision = value.prefsRev;
|
|
214
|
+
const guestNext = guestChatVersion(value), chatChanged = value.themesRev !== revision || guestNext !== guestRevision, ownerChanged = value.prefsRev !== prefsRevision;
|
|
215
|
+
revision = value.themesRev; prefsRevision = value.prefsRev; guestRevision = guestNext;
|
|
158
216
|
if (chatChanged) entry.api.changedChat();
|
|
159
217
|
if (ownerChanged) entry.api.changedOwner();
|
|
160
218
|
if (!chatChanged && !ownerChanged) sync();
|
|
161
219
|
}));
|
|
162
|
-
hold(ownerCssLifecycle.subscribe(sync)); hold(themesStore.subscribe(sync)); hold(onPrefsChanged(sync));
|
|
163
|
-
if (typeof preview?.onChange === "function") hold(preview.onChange(sync));
|
|
220
|
+
hold(ownerCssLifecycle.subscribe(sync)); hold(themesStore.subscribe(sync)); hold(onPrefsChanged(sync)); hold(onMotionChange(sync));
|
|
164
221
|
if (typeof host.AgendTheme?.onChange === "function") hold(host.AgendTheme.onChange(sync));
|
|
165
222
|
for (const [target, type] of [[media, "change"], [coarse, "change"], [doc, "visibilitychange"], [host, "resize"], [host.navigator?.connection, "change"]]) {
|
|
166
223
|
if (target?.addEventListener) { target.addEventListener(type, sync); hold(() => target.removeEventListener(type, sync)); }
|
|
@@ -175,6 +232,7 @@ export function startAppEffects({ store, window: host = window, factories = {} }
|
|
|
175
232
|
};
|
|
176
233
|
const barriers = typeof host.MutationObserver === "function" ? new host.MutationObserver(records => {
|
|
177
234
|
if (entry.dead) return;
|
|
235
|
+
modalCache = null; // the document changed: a dialog may have opened or closed (read it again)
|
|
178
236
|
const value = context().modal, drag = cursorDragging(); if (value !== modal || drag !== dragging) { modal = value; dragging = drag; sync(); }
|
|
179
237
|
if (Array.isArray(records) && records.some(r => r.target?.closest?.("#app") || r.target === doc.getElementById?.("app"))) surfaceChanged();
|
|
180
238
|
}) : null;
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
// #2233: signing a backend in from the dashboard — the dialog, the line every admin page shows while a sign-in runs,
|
|
2
|
+
// and the list Settings → Agents shows. Behind the session gate (/ui/js/); a guest's page never loads it.
|
|
3
|
+
//
|
|
4
|
+
// What the page is told comes from GET /ui/backend-login, as this session may see it (web-backend-login.ts): the
|
|
5
|
+
// session that started a sign-in gets its terminal link, access code and device code; every other admin page only that
|
|
6
|
+
// one runs, on which device, and may cancel it. The stream's `backend_login` event carries nothing — it only says to
|
|
7
|
+
// read again. Starting is two steps, as in chat: check (the server keeps what it found) and start (with its id).
|
|
8
|
+
import { html, useEffect, useState } from "/assets/app-html.js";
|
|
9
|
+
import { t, register } from "/assets/app-i18n.js";
|
|
10
|
+
import { appStore, createStore, useStore } from "/assets/app-store.js";
|
|
11
|
+
import { closeDialog, shellStore, showDialog } from "/assets/app-shell.js";
|
|
12
|
+
import { Dialog } from "/assets/ui-dialog.js";
|
|
13
|
+
import { toast } from "/assets/ui-toast.js";
|
|
14
|
+
|
|
15
|
+
register("signin", {
|
|
16
|
+
en: {
|
|
17
|
+
title: "Sign in to {0}",
|
|
18
|
+
checking: "Checking whether {0} is signed in…",
|
|
19
|
+
confirmDevice: "AgEnD runs {0}'s own sign-in on this machine. You get a link and a one-time code here, to finish in your browser.",
|
|
20
|
+
confirmTerminal: "AgEnD runs {0}'s own sign-in on this machine and opens it as a terminal in your browser (only that sign-in, no shell). You get the link and a one-time access code here.",
|
|
21
|
+
stillSignedIn: "It is still signed in: signing in again replaces that login.",
|
|
22
|
+
logoutFirst: "It signs out first ({0}), then in.",
|
|
23
|
+
publicNote: "Not at this machine? A temporary public link opens the terminal from anywhere, for this sign-in only.",
|
|
24
|
+
go: "Sign in",
|
|
25
|
+
goPublic: "Use a temporary public link",
|
|
26
|
+
cancel: "Cancel",
|
|
27
|
+
close: "Close",
|
|
28
|
+
starting: "Starting…",
|
|
29
|
+
deviceStep: "Open this page and enter the code:",
|
|
30
|
+
linkStep: "Open this page to sign in; if it gives you a code, paste it into the sign-in terminal:",
|
|
31
|
+
terminalStep: "Open the sign-in terminal and enter this access code there. It works once, for {0} minutes.",
|
|
32
|
+
openTerminal: "Open the sign-in terminal",
|
|
33
|
+
accessCode: "Access code",
|
|
34
|
+
code: "Code",
|
|
35
|
+
copy: "Copy",
|
|
36
|
+
copied: "Copied",
|
|
37
|
+
publicLink: "Public link: anyone with the link and the code could use it — keep both to yourself.",
|
|
38
|
+
loopback: "This terminal listens on the machine's own address only. Open it on that machine, or set web_terminal.bind for your network.",
|
|
39
|
+
waitingHint: "Waiting for the sign-in to show its link…",
|
|
40
|
+
signedIn: "{0} is signed in.",
|
|
41
|
+
ended: "The sign-in ended.",
|
|
42
|
+
cancelSignIn: "Cancel sign-in",
|
|
43
|
+
cancelled: "Sign-in cancelled.",
|
|
44
|
+
running: "Signing in to {0} (started on {1}).",
|
|
45
|
+
open: "Open",
|
|
46
|
+
expired: "That took too long — start again.",
|
|
47
|
+
notHere: "Signing in is not available over the public link. Open the dashboard on the machine running AgEnD, or use /login in your chat.",
|
|
48
|
+
notAdmin: "Only a fleet admin signed in on this machine or its network can sign a backend in.",
|
|
49
|
+
notOffered: "A public link is not offered for this sign-in.",
|
|
50
|
+
failed: "That did not work.",
|
|
51
|
+
listTitle: "Sign-ins",
|
|
52
|
+
listHint: "Each backend's login on this machine, and each credential profile an agent uses.",
|
|
53
|
+
stUsable: "signed in",
|
|
54
|
+
stNotSignedIn: "not signed in",
|
|
55
|
+
stNotInstalled: "not installed",
|
|
56
|
+
stTooOld: "too old",
|
|
57
|
+
stUnknown: "not checked",
|
|
58
|
+
signIn: "Sign in",
|
|
59
|
+
signInAgain: "Sign in again",
|
|
60
|
+
onHost: "Sign in on the machine: {0}",
|
|
61
|
+
install: "Install on the machine: {0}",
|
|
62
|
+
update: "Update on the machine: {0}",
|
|
63
|
+
profile: "{0} · profile {1}",
|
|
64
|
+
},
|
|
65
|
+
"zh-TW": {
|
|
66
|
+
title: "登入 {0}",
|
|
67
|
+
checking: "正在確認 {0} 是否已登入…",
|
|
68
|
+
confirmDevice: "AgEnD 會在這台機器上執行 {0} 自己的登入。這裡會給你一個連結和一次性代碼,在瀏覽器完成登入。",
|
|
69
|
+
confirmTerminal: "AgEnD 會在這台機器上執行 {0} 自己的登入,並在瀏覽器以終端機開啟(只有這個登入,沒有 shell)。這裡會給你連結和一次性存取碼。",
|
|
70
|
+
stillSignedIn: "它目前仍是登入狀態:重新登入會取代原本的登入。",
|
|
71
|
+
logoutFirst: "會先登出({0}),再登入。",
|
|
72
|
+
publicNote: "不在這台機器前?臨時公開連結可以從任何地方開啟終端機,只用於這次登入。",
|
|
73
|
+
go: "登入",
|
|
74
|
+
goPublic: "使用臨時公開連結",
|
|
75
|
+
cancel: "取消",
|
|
76
|
+
close: "關閉",
|
|
77
|
+
starting: "啟動中…",
|
|
78
|
+
deviceStep: "開啟這個頁面並輸入代碼:",
|
|
79
|
+
linkStep: "開啟這個頁面登入;如果它給你一組代碼,請貼到登入終端機:",
|
|
80
|
+
terminalStep: "開啟登入終端機,在那裡輸入這個存取碼。只能用一次,{0} 分鐘內有效。",
|
|
81
|
+
openTerminal: "開啟登入終端機",
|
|
82
|
+
accessCode: "存取碼",
|
|
83
|
+
code: "代碼",
|
|
84
|
+
copy: "複製",
|
|
85
|
+
copied: "已複製",
|
|
86
|
+
publicLink: "公開連結:拿到連結和代碼的人都能使用——兩者都只留給自己。",
|
|
87
|
+
loopback: "這個終端機只在本機位址上接聽。請在那台機器上開啟,或為你的網路設定 web_terminal.bind。",
|
|
88
|
+
waitingHint: "等待登入顯示連結…",
|
|
89
|
+
signedIn: "{0} 已登入。",
|
|
90
|
+
ended: "登入已結束。",
|
|
91
|
+
cancelSignIn: "取消登入",
|
|
92
|
+
cancelled: "已取消登入。",
|
|
93
|
+
running: "正在登入 {0}(從 {1} 啟動)。",
|
|
94
|
+
open: "開啟",
|
|
95
|
+
expired: "等太久了——請重新開始。",
|
|
96
|
+
notHere: "公開連結上不能登入。請在執行 AgEnD 的機器上開啟儀表板,或在聊天裡用 /login。",
|
|
97
|
+
notAdmin: "只有在這台機器或它的網路上登入的 fleet 管理員,才能讓後端登入。",
|
|
98
|
+
notOffered: "這次登入不提供公開連結。",
|
|
99
|
+
failed: "沒有成功。",
|
|
100
|
+
listTitle: "登入",
|
|
101
|
+
listHint: "這台機器上每個 backend 的登入,以及代理使用的每個憑證設定檔。",
|
|
102
|
+
stUsable: "已登入",
|
|
103
|
+
stNotSignedIn: "未登入",
|
|
104
|
+
stNotInstalled: "未安裝",
|
|
105
|
+
stTooOld: "版本太舊",
|
|
106
|
+
stUnknown: "未檢查",
|
|
107
|
+
signIn: "登入",
|
|
108
|
+
signInAgain: "重新登入",
|
|
109
|
+
onHost: "在機器上登入:{0}",
|
|
110
|
+
install: "在機器上安裝:{0}",
|
|
111
|
+
update: "在機器上更新:{0}",
|
|
112
|
+
profile: "{0} · 設定檔 {1}",
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
const tn = (k, ...v) => t(`signin.${k}`, ...v);
|
|
116
|
+
const shownOf = (backend, profile) => (profile ? tn("profile", backend, profile) : backend);
|
|
117
|
+
|
|
118
|
+
/** What this page knows: the current sign-in as this session may see it, and Settings' list. */
|
|
119
|
+
export const signInStore = createStore({ loaded: false, login: null, targets: [] });
|
|
120
|
+
|
|
121
|
+
let reading = null, again = false;
|
|
122
|
+
/**
|
|
123
|
+
* Read GET /ui/backend-login (one read at a time). Asked again while one is on its way (an event that came after it was
|
|
124
|
+
* sent): one more read follows it, so the last change is never missed (#2262 review).
|
|
125
|
+
*/
|
|
126
|
+
export function refreshSignIn() {
|
|
127
|
+
if (reading) { again = true; return reading; }
|
|
128
|
+
reading = fetch("/ui/backend-login", { cache: "no-store" })
|
|
129
|
+
.then((r) => (r.ok ? r.json() : null))
|
|
130
|
+
.then((b) => { if (b) signInStore.set({ loaded: true, login: b.login || null, targets: Array.isArray(b.targets) ? b.targets : [] }); return b; })
|
|
131
|
+
.catch(() => null)
|
|
132
|
+
.finally(() => { reading = null; if (again) { again = false; void refreshSignIn(); } });
|
|
133
|
+
return reading;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
let installed = false;
|
|
137
|
+
/** Once per admin page: listen for `backend_login`, read once, and show the line while a sign-in runs. */
|
|
138
|
+
export function installSignIn() {
|
|
139
|
+
if (installed || !signInOffered()) return;
|
|
140
|
+
installed = true;
|
|
141
|
+
const on = appStore.get().streamOn;
|
|
142
|
+
if (typeof on === "function") on("backend_login", () => { void refreshSignIn(); });
|
|
143
|
+
void refreshSignIn();
|
|
144
|
+
shellStore.set({ banner: SignInLine });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Open the dialog for one login (a backend's default one, or a credential profile). */
|
|
148
|
+
export function openSignIn({ backend, profile }) {
|
|
149
|
+
showDialog(SignInDialog, { backend, ...(profile ? { profile } : {}) });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Open the dialog on the running sign-in (its starter's view, or the cancel-only one). */
|
|
153
|
+
function openRunning(login) { showDialog(SignInDialog, { backend: login.backend, ...(login.profile ? { profile: login.profile } : {}), resume: true }); }
|
|
154
|
+
|
|
155
|
+
async function post(path, body) {
|
|
156
|
+
try {
|
|
157
|
+
const res = await fetch(path, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) });
|
|
158
|
+
let json = null; try { json = await res.json(); } catch { /* none */ }
|
|
159
|
+
return { ok: res.ok, status: res.status, body: json };
|
|
160
|
+
} catch { return { ok: false, status: 0, body: null }; }
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const errorText = (code) => (code === "confirm_expired" ? tn("expired") : code === "tunnel_not_offered" ? tn("notOffered")
|
|
164
|
+
: code === "sign_in_not_over_public_link" ? tn("notHere") : code === "sign_in_needs_a_local_admin" ? tn("notAdmin") : tn("failed"));
|
|
165
|
+
/** The public link's page (#2233: Sign in is refused there — never offered). */
|
|
166
|
+
export const signInOffered = () => !(typeof document !== "undefined" && document.body && document.body.dataset.publicLink === "1");
|
|
167
|
+
const loopbackHost = (h) => h === "localhost" || h === "::1" || h === "[::1]" || /^127\./.test(h);
|
|
168
|
+
|
|
169
|
+
function CopyField({ label, value }) {
|
|
170
|
+
const [copied, setCopied] = useState(false);
|
|
171
|
+
const copy = async () => { try { await navigator.clipboard.writeText(value); setCopied(true); } catch { toast(value); } };
|
|
172
|
+
return html`<div class="signin-field"><span class="signin-label">${label}</span><code class="signin-value">${value}</code>
|
|
173
|
+
<button type="button" class="btn btn-sm" onClick=${copy}>${copied ? tn("copied") : tn("copy")}</button></div>`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** The starter's view of a running sign-in: the device link and code, or the terminal and its access code. */
|
|
177
|
+
function Running({ login }) {
|
|
178
|
+
const { hint, terminal, accessCode } = login;
|
|
179
|
+
const away = terminal && terminal.loopback && typeof location !== "undefined" && !loopbackHost(location.hostname);
|
|
180
|
+
return html`<div class="signin-run">
|
|
181
|
+
${hint ? html`<p>${hint.code ? tn("deviceStep") : tn("linkStep")}</p>
|
|
182
|
+
${hint.linkable ? html`<p><a data-trust="navigation" class="btn btn-primary" href=${hint.url} target="_blank" rel="noopener noreferrer">${tn("open")}</a> <span class="signin-url">${hint.url}</span></p>`
|
|
183
|
+
: html`<${CopyField} label="URL" value=${hint.url} />`}
|
|
184
|
+
${hint.code ? html`<${CopyField} label=${tn("code")} value=${hint.code} />` : null}` : null}
|
|
185
|
+
${terminal ? html`<p>${tn("terminalStep", terminal.ttlMinutes)}</p>
|
|
186
|
+
<p><a data-trust="navigation" class="btn btn-primary" href=${terminal.url} target="_blank" rel="noopener noreferrer">${tn("openTerminal")}</a></p>
|
|
187
|
+
${accessCode ? html`<${CopyField} label=${tn("accessCode")} value=${accessCode} />` : null}
|
|
188
|
+
${terminal.public ? html`<p class="signin-note">${tn("publicLink")}</p>` : null}
|
|
189
|
+
${away ? html`<p class="signin-note">${tn("loopback")}</p>` : null}` : null}
|
|
190
|
+
${!hint && !terminal ? html`<p class="signin-note">${tn("waitingHint")}</p>` : null}
|
|
191
|
+
</div>`;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function SignInDialog({ backend, profile, resume = false, onClose }) {
|
|
195
|
+
const s = useStore(signInStore);
|
|
196
|
+
const [step, setStep] = useState(resume ? { kind: "login" } : { kind: "checking" });
|
|
197
|
+
const [busy, setBusy] = useState(false);
|
|
198
|
+
const shown = shownOf(backend, profile);
|
|
199
|
+
const close = onClose || closeDialog;
|
|
200
|
+
useEffect(() => {
|
|
201
|
+
if (resume) return undefined;
|
|
202
|
+
let gone = false;
|
|
203
|
+
post("/ui/backend-login/check", { backend, ...(profile ? { profile } : {}) }).then((r) => {
|
|
204
|
+
if (gone) return;
|
|
205
|
+
if (r.body && r.body.confirm) setStep({ kind: "confirm", confirm: r.body.confirm });
|
|
206
|
+
else setStep({ kind: "message", text: (r.body && (r.body.message || (r.body.error && errorText(r.body.error)))) || tn("failed") });
|
|
207
|
+
});
|
|
208
|
+
return () => { gone = true; };
|
|
209
|
+
}, []);
|
|
210
|
+
const login = s.login;
|
|
211
|
+
const live = step.kind === "login" && login && login.phase === "running";
|
|
212
|
+
// While a running sign-in is on screen, read it every 2 s too (a page without the stream still sees it move).
|
|
213
|
+
useEffect(() => {
|
|
214
|
+
if (!live) return undefined;
|
|
215
|
+
const h = setInterval(() => { void refreshSignIn(); }, 2000);
|
|
216
|
+
return () => clearInterval(h);
|
|
217
|
+
}, [live]);
|
|
218
|
+
const start = async (tunnel) => {
|
|
219
|
+
setBusy(true); setStep({ kind: "starting" });
|
|
220
|
+
const r = await post("/ui/backend-login/start", { confirm: step.confirm.id, ...(tunnel ? { tunnel: true } : {}) });
|
|
221
|
+
setBusy(false);
|
|
222
|
+
if (r.body && r.body.login) { signInStore.set({ login: r.body.login }); setStep({ kind: "login" }); return; }
|
|
223
|
+
setStep({ kind: "message", text: (r.body && (r.body.message || (r.body.error && errorText(r.body.error)))) || tn("failed") });
|
|
224
|
+
};
|
|
225
|
+
const cancelRun = async () => {
|
|
226
|
+
setBusy(true);
|
|
227
|
+
const r = await post("/ui/backend-login/cancel", {});
|
|
228
|
+
setBusy(false);
|
|
229
|
+
toast((r.body && r.body.message) || tn("cancelled"));
|
|
230
|
+
void refreshSignIn();
|
|
231
|
+
};
|
|
232
|
+
let body, actions;
|
|
233
|
+
if (step.kind === "checking" || step.kind === "starting") {
|
|
234
|
+
body = html`<p role="status">${step.kind === "checking" ? tn("checking", shown) : tn("starting")}</p>`;
|
|
235
|
+
actions = html`<button type="button" class="btn" disabled=${busy} onClick=${close}>${tn("cancel")}</button>`;
|
|
236
|
+
} else if (step.kind === "confirm") {
|
|
237
|
+
const c = step.confirm;
|
|
238
|
+
body = html`<p>${c.deviceAuth ? tn("confirmDevice", shown) : tn("confirmTerminal", shown)}</p>
|
|
239
|
+
${c.tokenPresent ? html`<p>${tn("stillSignedIn")}</p>` : null}
|
|
240
|
+
${c.logoutFirst ? html`<p>${tn("logoutFirst", c.logoutFirst)}</p>` : null}
|
|
241
|
+
${c.publicLink ? html`<p class="signin-note">${tn("publicNote")}</p>` : null}`;
|
|
242
|
+
actions = html`<button type="button" class="btn" onClick=${close}>${tn("cancel")}</button>
|
|
243
|
+
${c.publicLink ? html`<button type="button" class="btn" onClick=${() => start(true)}>${tn("goPublic")}</button>` : null}
|
|
244
|
+
<button type="button" class="btn btn-primary" onClick=${() => start(false)}>${tn("go")}</button>`;
|
|
245
|
+
} else if (step.kind === "message") {
|
|
246
|
+
body = html`<p class="signin-message">${step.text}</p>`;
|
|
247
|
+
actions = html`<button type="button" class="btn btn-primary" onClick=${close}>${tn("close")}</button>`;
|
|
248
|
+
} else if (!login) {
|
|
249
|
+
body = html`<p class="signin-note">${tn("ended")}</p>`;
|
|
250
|
+
actions = html`<button type="button" class="btn btn-primary" onClick=${close}>${tn("close")}</button>`;
|
|
251
|
+
} else if (!login.mine) {
|
|
252
|
+
body = html`<p>${login.phase === "running" ? tn("running", shownOf(login.backend, login.profile), login.startedOn) : tn("ended")}</p>`;
|
|
253
|
+
actions = html`<button type="button" class="btn" onClick=${close}>${tn("close")}</button>
|
|
254
|
+
${login.phase === "running" ? html`<button type="button" class="btn btn-danger" disabled=${busy} onClick=${cancelRun}>${tn("cancelSignIn")}</button>` : null}`;
|
|
255
|
+
} else {
|
|
256
|
+
const lines = Array.isArray(login.lines) ? login.lines : [];
|
|
257
|
+
body = html`${login.phase === "running" ? html`<${Running} login=${login} />`
|
|
258
|
+
: html`<p role="status" class=${login.ok ? "signin-ok" : "signin-message"}>${login.ok ? tn("signedIn", shownOf(login.backend, login.profile)) : tn("ended")}</p>`}
|
|
259
|
+
${lines.length ? html`<ul class="signin-lines">${lines.map((l, i) => html`<li key=${i}>${l}</li>`)}</ul>` : null}`;
|
|
260
|
+
actions = html`<button type="button" class="btn" onClick=${close}>${tn("close")}</button>
|
|
261
|
+
${login.phase === "running" ? html`<button type="button" class="btn btn-danger" disabled=${busy} onClick=${cancelRun}>${tn("cancelSignIn")}</button>` : null}`;
|
|
262
|
+
}
|
|
263
|
+
return html`<${Dialog} title=${tn("title", shown)} onClose=${close} busy=${busy} actions=${actions}>${body}</${Dialog}>`;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/** The line every admin page shows while a sign-in runs (the shell's banner slot). */
|
|
267
|
+
export function SignInLine() {
|
|
268
|
+
const { login } = useStore(signInStore);
|
|
269
|
+
if (!login || login.phase !== "running") return null;
|
|
270
|
+
return html`<div data-trust="status" class="conn op-line signin-line" role="status">
|
|
271
|
+
<span>${tn("running", shownOf(login.backend, login.profile), login.startedOn)}</span>
|
|
272
|
+
<button type="button" class="btn btn-sm" onClick=${() => openRunning(login)}>${tn("open")}</button></div>`;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const STATE_KEY = { usable: "stUsable", not_signed_in: "stNotSignedIn", not_installed: "stNotInstalled", too_old: "stTooOld", unknown: "stUnknown" };
|
|
276
|
+
|
|
277
|
+
/** Settings → Agents: each login on this machine, with Sign in where the dashboard can do it. */
|
|
278
|
+
export function SignInList() {
|
|
279
|
+
const { targets, loaded, login } = useStore(signInStore);
|
|
280
|
+
useEffect(() => { void refreshSignIn(); }, []);
|
|
281
|
+
if (!loaded || !targets.length) return null;
|
|
282
|
+
const running = login && login.phase === "running";
|
|
283
|
+
return html`<section class="s-group signin-list" aria-label=${tn("listTitle")}>
|
|
284
|
+
<div class="list-head"><span>${tn("listTitle")}</span></div>
|
|
285
|
+
<p class="sub">${tn("listHint")}</p>
|
|
286
|
+
${targets.map((x) => html`<div key=${`${x.backend}:${x.profile || ""}`} class="s-row">
|
|
287
|
+
<span class="s-name">${shownOf(x.backend, x.profile)}</span>
|
|
288
|
+
<span class="tag">${tn(STATE_KEY[x.state] || "stUnknown")}</span>
|
|
289
|
+
<span class="s-br" aria-hidden="true"></span>
|
|
290
|
+
${x.install ? html`<span class="s-meta"><code>${tn("install", x.install)}</code></span>` : null}
|
|
291
|
+
${x.update ? html`<span class="s-meta"><code>${tn("update", x.update)}</code></span>` : null}
|
|
292
|
+
${x.hostCommand ? html`<span class="s-meta"><code>${tn("onHost", x.hostCommand)}</code></span>` : null}
|
|
293
|
+
<span class="s-actions">${x.signIn ? html`<button type="button" class=${x.state === "not_signed_in" ? "btn btn-sm btn-primary" : "btn btn-sm"} disabled=${!!running}
|
|
294
|
+
onClick=${() => openSignIn(x)}>${x.state === "usable" ? tn("signInAgain") : tn("signIn")}</button>` : null}</span>
|
|
295
|
+
</div>`)}
|
|
296
|
+
</section>`;
|
|
297
|
+
}
|