@songsid/agend 2.2.1 → 2.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-cli-instructions.md +5 -2
- package/dist/agent-cli.js +284 -2
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.d.ts +2 -0
- package/dist/agent-endpoint.js +29 -0
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/agent-file-read.d.ts +38 -0
- package/dist/agent-file-read.js +317 -0
- package/dist/agent-file-read.js.map +1 -0
- package/dist/agent-whats-new.d.ts +11 -0
- package/dist/agent-whats-new.js +34 -2
- package/dist/agent-whats-new.js.map +1 -1
- package/dist/atomic-write.js +9 -1
- package/dist/atomic-write.js.map +1 -1
- package/dist/auth-api.d.ts +30 -1
- package/dist/auth-api.js +61 -12
- 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/antigravity.js +3 -3
- package/dist/backend/antigravity.js.map +1 -1
- package/dist/backend/claude-code.d.ts +2 -2
- package/dist/backend/claude-code.js +10 -5
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/launch-effort.js +1 -1
- package/dist/backend/launch-effort.js.map +1 -1
- package/dist/backend/muse.js +10 -2
- package/dist/backend/muse.js.map +1 -1
- package/dist/backend/types.d.ts +14 -0
- package/dist/backend/types.js +25 -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 +21 -3
- package/dist/backend-command.js +23 -4
- package/dist/backend-command.js.map +1 -1
- 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-analysis.d.ts +47 -1
- package/dist/cache-analysis.js +68 -8
- package/dist/cache-analysis.js.map +1 -1
- 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-report-tool.d.ts +17 -0
- package/dist/cache-report-tool.js +40 -0
- package/dist/cache-report-tool.js.map +1 -0
- package/dist/cache-service.d.ts +70 -3
- package/dist/cache-service.js +178 -17
- 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 +11 -0
- package/dist/channel/adapters/discord.js +66 -6
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +8 -1
- package/dist/channel/adapters/telegram.js +74 -4
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/mcp-tools.js +48 -2
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/types.d.ts +21 -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 +142 -110
- package/dist/cli.js.map +1 -1
- package/dist/config-validator.js +119 -4
- package/dist/config-validator.js.map +1 -1
- package/dist/daemon.d.ts +8 -0
- package/dist/daemon.js +298 -22
- 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 +12 -1
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/fleet-context.d.ts +15 -0
- package/dist/fleet-manager.d.ts +514 -12
- package/dist/fleet-manager.js +1885 -163
- 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/credential-profiles/SKILL.md +20 -5
- package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +6 -1
- 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/web-appearance/SKILL.md +492 -0
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +482 -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-avatar.js +3 -0
- package/dist/instance-avatar.js.map +1 -1
- package/dist/instance-config-impact.js +6 -1
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +2 -0
- package/dist/instance-lifecycle.js +20 -10
- 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 +118 -10
- package/dist/locale.js.map +1 -1
- package/dist/login-controller.js +35 -2
- package/dist/login-controller.js.map +1 -1
- package/dist/ls-attach-resolve.d.ts +95 -0
- package/dist/ls-attach-resolve.js +174 -0
- package/dist/ls-attach-resolve.js.map +1 -0
- 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-hub.d.ts +4 -0
- package/dist/needs-you-hub.js +31 -6
- package/dist/needs-you-hub.js.map +1 -1
- package/dist/needs-you.d.ts +1 -13
- package/dist/needs-you.js +3 -37
- package/dist/needs-you.js.map +1 -1
- package/dist/outbound-handlers.d.ts +15 -0
- package/dist/outbound-handlers.js +152 -11
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +228 -4
- package/dist/outbound-schemas.js +147 -1
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/public-web-gateway.js +21 -1
- 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 +10 -1
- package/dist/quickstart-api.js +12 -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/repo-worktree-guard.d.ts +36 -0
- package/dist/repo-worktree-guard.js +143 -0
- package/dist/repo-worktree-guard.js.map +1 -0
- package/dist/restart-progress.d.ts +65 -1
- package/dist/restart-progress.js +236 -10
- 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/scheduler.d.ts +70 -1
- package/dist/scheduler/scheduler.js +256 -17
- package/dist/scheduler/scheduler.js.map +1 -1
- package/dist/service-installer.d.ts +2 -1
- package/dist/service-installer.js +18 -3
- package/dist/service-installer.js.map +1 -1
- package/dist/settings-api.d.ts +23 -0
- package/dist/settings-api.js +233 -2
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +112 -0
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-effect.js +32 -2
- package/dist/settings-effect.js.map +1 -1
- package/dist/settings-http-confirmation.js +5 -0
- package/dist/settings-http-confirmation.js.map +1 -1
- package/dist/tmux-manager.d.ts +5 -0
- package/dist/tmux-manager.js +16 -0
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tool-permissions.d.ts +4 -6
- package/dist/tool-permissions.js +100 -2
- package/dist/tool-permissions.js.map +1 -1
- package/dist/topic-commands.d.ts +17 -7
- package/dist/topic-commands.js +39 -21
- package/dist/topic-commands.js.map +1 -1
- package/dist/topic-link.d.ts +128 -0
- package/dist/topic-link.js +269 -0
- package/dist/topic-link.js.map +1 -0
- 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 +61 -1
- package/dist/ui/app-effects.js +245 -0
- package/dist/ui/chat-ambient.js +307 -0
- package/dist/ui/chat-effect-posters.js +42 -0
- package/dist/ui/chat-effects.js +160 -0
- package/dist/ui/chat-live-replies.js +58 -0
- package/dist/ui/chat-render.js +25 -7
- package/dist/ui/chat-store.js +41 -13
- package/dist/ui/chat-strings.js +86 -2
- package/dist/ui/chat-thread.js +204 -37
- package/dist/ui/chat-tour.js +1 -1
- package/dist/ui/effect-trial-controller.js +124 -0
- package/dist/ui/effect-trial-offer.js +31 -0
- package/dist/ui/effect-trial-panel.js +40 -0
- package/dist/ui/fleet-cache.js +23 -1
- package/dist/ui/fleet-canvas-layout.js +111 -0
- package/dist/ui/fleet-canvas.js +525 -0
- package/dist/ui/fleet-org.js +27 -12
- package/dist/ui/fleet-traffic.js +343 -0
- package/dist/ui/fleet-usage.js +401 -0
- package/dist/ui/image-lightbox.js +1 -1
- package/dist/ui/panel-chat.js +252 -58
- package/dist/ui/panel-details.js +20 -6
- package/dist/ui/panel-fleet.js +45 -19
- package/dist/ui/panel-log.js +6 -6
- package/dist/ui/panel-needs.js +3 -3
- package/dist/ui/panel-settings.js +58 -13
- package/dist/ui/preview-panel.js +25 -9
- package/dist/ui/preview.js +3 -2
- package/dist/ui/settings-appearance.js +349 -0
- package/dist/ui/settings-catalog.js +92 -0
- package/dist/ui/settings-dialogs.js +40 -12
- package/dist/ui/settings-effects.js +202 -0
- 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 +271 -0
- package/dist/ui/settings-owner-effects.js +80 -0
- package/dist/ui/settings-strings.js +335 -10
- package/dist/ui/settings-web-authority.js +91 -0
- package/dist/ui/shared/agend-auth.js +28 -4
- package/dist/ui/shared/agent-widget.js +248 -0
- package/dist/ui/shared/app-i18n.js +6 -2
- package/dist/ui/shared/app-prefs.js +34 -13
- package/dist/ui/shared/app-route.js +42 -2
- package/dist/ui/shared/app-session.js +3 -1
- package/dist/ui/shared/app-shell.js +119 -41
- package/dist/ui/shared/app-store.js +28 -1
- package/dist/ui/shared/app-stream.js +14 -7
- package/dist/ui/shared/app-themes.js +206 -0
- package/dist/ui/shared/app.css +566 -5
- package/dist/ui/shared/app.js +23 -10
- package/dist/ui/shared/card-strings.js +32 -0
- package/dist/ui/shared/effect-app-runtime.js +297 -0
- package/dist/ui/shared/effect-baseline.js +64 -0
- package/dist/ui/shared/effect-chat-runtime.js +167 -0
- package/dist/ui/shared/effect-cursor.js +125 -0
- package/dist/ui/shared/effect-frame.js +146 -0
- package/dist/ui/shared/effect-geometry.js +137 -0
- package/dist/ui/shared/effect-host-style.js +10 -0
- package/dist/ui/shared/effect-owner-regions.js +74 -0
- package/dist/ui/shared/effect-owner-source.js +122 -0
- package/dist/ui/shared/effect-policy.js +151 -0
- package/dist/ui/shared/effect-poster-reader.js +98 -0
- package/dist/ui/shared/effect-posters.js +117 -0
- package/dist/ui/shared/effect-profiler.js +184 -0
- package/dist/ui/shared/effect-profiles.js +65 -0
- package/dist/ui/shared/effect-registry.js +543 -0
- package/dist/ui/shared/effect-runtime-loader.js +234 -0
- package/dist/ui/shared/effect-safety.js +153 -0
- package/dist/ui/shared/effect-stock-snapshot.js +43 -0
- package/dist/ui/shared/effect-stock-trust.js +217 -0
- package/dist/ui/shared/effect-trial-shadow.js +62 -0
- package/dist/ui/shared/effect-trial-source.js +103 -0
- package/dist/ui/shared/effect-trial-stage.js +170 -0
- package/dist/ui/shared/effect-trial.css +40 -0
- package/dist/ui/shared/fx-motion.js +67 -0
- package/dist/ui/shared/header-tools.js +1 -1
- package/dist/ui/shared/identity-card.js +273 -0
- package/dist/ui/shared/identity-color.js +261 -0
- package/dist/ui/shared/instance-nav.js +215 -41
- package/dist/ui/shared/owner-css.js +250 -0
- package/dist/ui/shared/owner-effect-prefs.js +117 -0
- package/dist/ui/shared/panel-view.js +103 -10
- package/dist/ui/shared/signin.js +2 -0
- package/dist/ui/shared/tokens.css +9 -3
- package/dist/ui/shared/ui-dialog.js +1 -1
- package/dist/ui/shared/ui-icons.js +3 -0
- package/dist/ui/shared/ui-menu.js +1 -1
- package/dist/ui/shared/ui-toast.js +1 -1
- package/dist/ui/shared/usage-dialog.js +22 -2
- package/dist/ui/shared/view-strings.js +98 -2
- package/dist/ui/shared/web-authorize.js +151 -0
- package/dist/ui/signin.html +1 -0
- package/dist/ui/web-terminal/terminal.css +4 -4
- package/dist/usage/providers.d.ts +28 -0
- package/dist/usage/providers.js +126 -36
- package/dist/usage/providers.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/view-api.d.ts +68 -0
- package/dist/view-api.js +249 -8
- 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 +117 -7
- package/dist/web-api.js +1283 -70
- package/dist/web-api.js.map +1 -1
- package/dist/web-appearance-limits.d.ts +82 -0
- package/dist/web-appearance-limits.js +83 -0
- package/dist/web-appearance-limits.js.map +1 -0
- package/dist/web-appearance-policy.d.ts +64 -0
- package/dist/web-appearance-policy.js +87 -0
- package/dist/web-appearance-policy.js.map +1 -0
- package/dist/web-appearance-tools.d.ts +53 -0
- package/dist/web-appearance-tools.js +124 -0
- package/dist/web-appearance-tools.js.map +1 -0
- package/dist/web-appearance-view.d.ts +124 -0
- package/dist/web-appearance-view.js +278 -0
- package/dist/web-appearance-view.js.map +1 -0
- package/dist/web-appearance.d.ts +93 -0
- package/dist/web-appearance.js +373 -0
- package/dist/web-appearance.js.map +1 -0
- package/dist/web-asset-parts.d.ts +93 -0
- package/dist/web-asset-parts.js +133 -0
- package/dist/web-asset-parts.js.map +1 -0
- package/dist/web-auth.js +20 -2
- package/dist/web-auth.js.map +1 -1
- package/dist/web-card.d.ts +110 -0
- package/dist/web-card.js +266 -0
- package/dist/web-card.js.map +1 -0
- 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 +63 -0
- package/dist/web-chat-history.js +109 -6
- package/dist/web-chat-history.js.map +1 -1
- package/dist/web-commands.d.ts +1 -0
- 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.d.ts +28 -0
- package/dist/web-effect-bundle.js +171 -0
- package/dist/web-effect-bundle.js.map +1 -0
- package/dist/web-effect-canonical.d.ts +8 -0
- package/dist/web-effect-canonical.js +27 -0
- package/dist/web-effect-canonical.js.map +1 -0
- package/dist/web-effect-library.d.ts +114 -0
- package/dist/web-effect-library.js +530 -0
- package/dist/web-effect-library.js.map +1 -0
- 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-poster.d.ts +8 -0
- package/dist/web-effect-poster.js +52 -0
- package/dist/web-effect-poster.js.map +1 -0
- package/dist/web-effect-profiles.d.ts +118 -0
- package/dist/web-effect-profiles.js +180 -0
- package/dist/web-effect-profiles.js.map +1 -0
- package/dist/web-effect-service.d.ts +93 -0
- package/dist/web-effect-service.js +335 -0
- package/dist/web-effect-service.js.map +1 -0
- package/dist/web-effect-tools.d.ts +48 -0
- package/dist/web-effect-tools.js +129 -0
- package/dist/web-effect-tools.js.map +1 -0
- package/dist/web-effect-trials.d.ts +86 -0
- package/dist/web-effect-trials.js +204 -0
- package/dist/web-effect-trials.js.map +1 -0
- package/dist/web-effect-upload.d.ts +27 -0
- package/dist/web-effect-upload.js +185 -0
- package/dist/web-effect-upload.js.map +1 -0
- package/dist/web-effect-worker.d.ts +8 -0
- package/dist/web-effect-worker.js +170 -0
- package/dist/web-effect-worker.js.map +1 -0
- package/dist/web-effect.d.ts +103 -0
- package/dist/web-effect.js +153 -0
- package/dist/web-effect.js.map +1 -0
- package/dist/web-guest-access.d.ts +125 -0
- package/dist/web-guest-access.js +234 -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-identity-color.d.ts +87 -0
- package/dist/web-identity-color.js +209 -0
- package/dist/web-identity-color.js.map +1 -0
- package/dist/web-identity-palette.d.ts +113 -0
- package/dist/web-identity-palette.js +84 -0
- package/dist/web-identity-palette.js.map +1 -0
- package/dist/web-image-meta.d.ts +38 -0
- package/dist/web-image-meta.js +262 -0
- package/dist/web-image-meta.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 +50 -0
- package/dist/web-mod-agent.js +207 -0
- package/dist/web-mod-agent.js.map +1 -0
- package/dist/web-mod-bundle.d.ts +157 -0
- package/dist/web-mod-bundle.js +529 -0
- package/dist/web-mod-bundle.js.map +1 -0
- package/dist/web-mod-export.d.ts +68 -0
- package/dist/web-mod-export.js +100 -0
- package/dist/web-mod-export.js.map +1 -0
- package/dist/web-mod-store.d.ts +289 -0
- package/dist/web-mod-store.js +638 -0
- package/dist/web-mod-store.js.map +1 -0
- package/dist/web-org.d.ts +3 -3
- package/dist/web-org.js +11 -17
- package/dist/web-org.js.map +1 -1
- package/dist/web-owner-css-store.d.ts +222 -0
- package/dist/web-owner-css-store.js +440 -0
- package/dist/web-owner-css-store.js.map +1 -0
- package/dist/web-owner-css.d.ts +58 -0
- package/dist/web-owner-css.js +552 -0
- package/dist/web-owner-css.js.map +1 -0
- package/dist/web-prefs.d.ts +62 -0
- package/dist/web-prefs.js +232 -36
- package/dist/web-prefs.js.map +1 -1
- package/dist/web-preview.d.ts +15 -3
- package/dist/web-preview.js +28 -9
- 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 +67 -2
- package/dist/web-settings-patch.js +196 -1
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/web-settings-tools.d.ts +89 -0
- package/dist/web-settings-tools.js +273 -0
- package/dist/web-settings-tools.js.map +1 -0
- package/dist/web-shell-routes.d.ts +2 -2
- package/dist/web-shell-routes.js +2 -2
- 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 +257 -0
- package/dist/web-theme-store.js +627 -0
- package/dist/web-theme-store.js.map +1 -0
- package/dist/web-theme.d.ts +123 -0
- package/dist/web-theme.js +416 -0
- package/dist/web-theme.js.map +1 -0
- 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/web-widget.d.ts +72 -0
- package/dist/web-widget.js +245 -0
- package/dist/web-widget.js.map +1 -0
- package/dist/workflow-templates/default.md +1 -1
- package/package.json +1 -1
- package/templates/systemd.service.ejs +8 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
// #1629 r4 §3: Settings → Appearance — the person's own custom CSS (owner CSS): the editor, keep-or-revert, earlier
|
|
2
|
+
// versions, the pictures and fonts it may name, and this device's off switch. Settings itself is always drawn stock
|
|
3
|
+
// (owner-css.js removes the link here), so a stylesheet that hides things can always be fixed from this page.
|
|
4
|
+
import { html, useEffect, useRef, useState } from "/assets/app-html.js";
|
|
5
|
+
import { t } from "/assets/app-i18n.js";
|
|
6
|
+
import { appStore, useStore } from "/assets/app-store.js";
|
|
7
|
+
import { ModsCard } from "./settings-mods.js";
|
|
8
|
+
import { EffectsCard } from "./settings-effects.js";
|
|
9
|
+
import { Icon } from "/assets/ui-icons.js";
|
|
10
|
+
import { toast } from "/assets/ui-toast.js";
|
|
11
|
+
import { ownerCssOffHere, ownerCssTrial, safeMode, setOwnerCssActive, setOwnerCssOffHere, startOwnerCssTrial } from "/assets/owner-css.js";
|
|
12
|
+
import { setThemesShown, themesStore } from "/assets/app-themes.js";
|
|
13
|
+
import { followsReducedMotion, onMotionChange, setFollowsReducedMotion } from "/assets/fx-motion.js";
|
|
14
|
+
import { CARD_SIZES, cardSizeDefault, cardSizeStore, setCardSizeDefault } from "/assets/identity-card.js";
|
|
15
|
+
import { AgentWidget, checkWidget, fleetBinds, setSidebarWidget, sidebarWidgetRaw, sidebarWidgetStore, widgetProblem } from "/assets/agent-widget.js";
|
|
16
|
+
import { confirmedWrite } from "./settings-confirm.js";
|
|
17
|
+
import { savePref } from "/assets/app-prefs.js";
|
|
18
|
+
import { confirmDialog } from "/assets/ui-confirm.js";
|
|
19
|
+
import { colorsOff, defaultPlaces, identityClasses, identityOf, PLACEMENTS, paintIdentity, setColorsOff, setDefaultPlaces, useIdentityColors } from "/assets/identity-color.js";
|
|
20
|
+
import { CardTitle } from "./settings-learn.js";
|
|
21
|
+
import "./settings-strings.js";
|
|
22
|
+
|
|
23
|
+
const tn = (k, ...v) => t(`settings.${k}`, ...v);
|
|
24
|
+
const short = (sha) => (sha ? sha.slice(0, 8) : "");
|
|
25
|
+
const kib = (n) => `${Math.max(1, Math.round(n / 1024))} KiB`;
|
|
26
|
+
|
|
27
|
+
async function call(path, init) {
|
|
28
|
+
let res;
|
|
29
|
+
try { res = await fetch(path, { credentials: "same-origin", ...init }); } catch { return { ok: false, status: 0, body: null }; }
|
|
30
|
+
let body = null;
|
|
31
|
+
try { body = await res.json(); } catch { /* not JSON */ }
|
|
32
|
+
return { ok: res.ok, status: res.status, body };
|
|
33
|
+
}
|
|
34
|
+
const postJson = (path, body) => call(path, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) });
|
|
35
|
+
|
|
36
|
+
/** Why the host refused a text, in the person's words. */
|
|
37
|
+
function refusal(body) {
|
|
38
|
+
const problem = body && body.error, detail = body && body.detail;
|
|
39
|
+
const known = ["at_rule", "url", "unknown_asset", "function", "property", "script_scheme", "too_large", "malformed", "nul", "unserialisable",
|
|
40
|
+
"asset_type", "asset_unreadable", "asset_quota", "asset_in_use", "owner_css_read_only", "owner_css_needs_a_signed_in_person"];
|
|
41
|
+
return known.includes(problem) ? tn(`cssError_${problem}`, detail || "") : tn("cssError_other", problem || "?");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function AppearanceSection() {
|
|
45
|
+
const publicLink = typeof document !== "undefined" && document.body && document.body.dataset.publicLink === "1";
|
|
46
|
+
if (publicLink) return html`<section class="card s-appearance"><h3>${tn("cssTitle")}</h3><p class="sub">${tn("cssPublicLink")}</p></section>`;
|
|
47
|
+
const group = (key) => html`<h2 class="s-ap-group">${tn(key)}</h2>`;
|
|
48
|
+
return html`${group("groupLook")}<${ThemesCard} /><${IdentityColorCard} /><${CardSizeCard} />
|
|
49
|
+
${group("groupLayout")}<${SidebarWidgetCard} />
|
|
50
|
+
${group("groupCss")}<${OwnerCssCard} /><${DeviceCard} />
|
|
51
|
+
${group("groupMods")}<${ModsCard} /><${EffectsCard} />
|
|
52
|
+
${group("groupAdvanced")}<${CardLimitsCard} /><${RestoreDefaultsCard} />`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** #2056 B: a sample sidebar row in the host's default colour places, so the choice is seen rather than described. */
|
|
56
|
+
const SAMPLE = { color: { person: "preset:teal" } };
|
|
57
|
+
function ColorSample({ places, off }) {
|
|
58
|
+
const id = identityOf(SAMPLE, { hostDefault: places, off });
|
|
59
|
+
return html`<div class="idc-preview s-colors-sample" aria-hidden="true"><span class=${`inst v-inst ${identityClasses(id)}`} ref=${(el) => paintIdentity(el, id)}>
|
|
60
|
+
<span class="av"><span class="av-text">A</span></span><span class="inst-text"><span class="inst-name">${tn("colorSample")}</span></span></span></div>`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* #1958: identity colours on this host — where they show for an instance nobody chose for (#2056: name, bar and
|
|
65
|
+
* ring unless changed), and a switch that turns them off everywhere while every pick is kept.
|
|
66
|
+
*/
|
|
67
|
+
function IdentityColorCard() {
|
|
68
|
+
useIdentityColors();
|
|
69
|
+
const places = defaultPlaces(), off = colorsOff();
|
|
70
|
+
const toggle = (p) => setDefaultPlaces(places.includes(p) ? places.filter((x) => x !== p) : [...places, p]);
|
|
71
|
+
return html`<section class="card s-appearance s-synced s-colors"><${CardTitle} title=${tn("colorTitle")} topic="colors" />
|
|
72
|
+
<p class="sub">${tn("colorLead")}</p>
|
|
73
|
+
<div class="s-colors-row">
|
|
74
|
+
<fieldset class="idc-places" disabled=${off}><legend>${tn("colorPlaces")}</legend>
|
|
75
|
+
${PLACEMENTS.map((p) => html`<label key=${p} class="check"><input type="checkbox" checked=${places.includes(p)} onChange=${() => toggle(p)} /> ${t(`color.place_${p}`)}</label>`)}
|
|
76
|
+
</fieldset>
|
|
77
|
+
<${ColorSample} places=${places} off=${off} />
|
|
78
|
+
</div>
|
|
79
|
+
<label class="check"><input type="checkbox" class="s-colors-off" checked=${off} onChange=${(e) => setColorsOff(e.target.checked)} /> <span>${tn("colorOff")}</span></label>
|
|
80
|
+
<p class="sub">${tn("colorOffLead")}</p>
|
|
81
|
+
</section>`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* #1958 decision 5: Restore defaults — the host's appearance settings back to their defaults after a confirm that says
|
|
86
|
+
* what it resets; this person's custom CSS turned off (its versions stay). Not settings, so never touched: the colours
|
|
87
|
+
* people picked per instance, installed mods, uploaded effects and the fleet's card limits.
|
|
88
|
+
*/
|
|
89
|
+
export async function restoreAppearanceDefaults() {
|
|
90
|
+
setThemesShown(true);
|
|
91
|
+
setFollowsReducedMotion(false);
|
|
92
|
+
setCardSizeDefault("m");
|
|
93
|
+
savePref("card.sizeByInstance", null);
|
|
94
|
+
setSidebarWidget(null);
|
|
95
|
+
setDefaultPlaces(null);
|
|
96
|
+
setColorsOff(false);
|
|
97
|
+
setOwnerCssOffHere(false);
|
|
98
|
+
const r = await postJson("/ui/owner-css/restore", { sha: null });
|
|
99
|
+
return r && r.error ? { ok: false, error: r.error } : { ok: true };
|
|
100
|
+
}
|
|
101
|
+
function RestoreDefaultsCard() {
|
|
102
|
+
const [busy, setBusy] = useState(false);
|
|
103
|
+
async function go() {
|
|
104
|
+
if (!(await confirmDialog({ title: tn("restoreTitle"), message: tn("restoreConfirm"), confirmLabel: tn("restoreGo"), danger: true }))) return;
|
|
105
|
+
setBusy(true);
|
|
106
|
+
const r = await restoreAppearanceDefaults();
|
|
107
|
+
setBusy(false);
|
|
108
|
+
if (r.ok) toast(tn("restoreDone")); else toast(tn("restoreCssFailed", r.error), false);
|
|
109
|
+
}
|
|
110
|
+
return html`<section class="card s-appearance s-restore"><${CardTitle} title=${tn("restoreTitle")} topic="restore" />
|
|
111
|
+
<p class="sub">${tn("restoreLead")}</p>
|
|
112
|
+
<button type="button" class="btn" disabled=${busy} onClick=${go}>${tn("restoreTitle")}</button>
|
|
113
|
+
</section>`;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** #1629 r4.1 Q3 (a): every identity card's size on this person's screens (a card's own control sets it apart). */
|
|
117
|
+
function CardSizeCard() {
|
|
118
|
+
useStore(cardSizeStore);
|
|
119
|
+
const size = cardSizeDefault();
|
|
120
|
+
return html`<section class="card s-appearance s-synced"><${CardTitle} title=${tn("cardSizeTitle")} topic="cards" />
|
|
121
|
+
<div class="card-size" role="group" aria-label=${tn("cardSizeTitle")}>
|
|
122
|
+
${CARD_SIZES.map((s) => html`<button key=${s} type="button" class=${`seg-item${s === size ? " active" : ""}`} aria-pressed=${s === size ? "true" : "false"}
|
|
123
|
+
onClick=${() => setCardSizeDefault(s)}>${t(`card.size${s.toUpperCase()}Name`)}</button>`)}
|
|
124
|
+
</div>
|
|
125
|
+
<p class="sub">${tn("cardSizeLead")}</p>
|
|
126
|
+
</section>`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const LIMIT_FIELDS = [["portrait", "max_width"], ["portrait", "max_height"], ["portrait", "max_kib"], ["banner", "max_width"], ["banner", "max_height"], ["banner", "max_kib"],
|
|
130
|
+
["instance_quota", "files"], ["instance_quota", "mib"]];
|
|
131
|
+
/**
|
|
132
|
+
* #1629 r4.1 Q3 (b): the identity card's upload limits (fleet.yaml web.appearance). Each field shows what is in force
|
|
133
|
+
* when it differs from what is written (a value beyond its bound is clamped). A lower limit applies at once; a higher
|
|
134
|
+
* one waits for a fleet admin's confirmation (the server decides). Stored pictures are never touched.
|
|
135
|
+
*/
|
|
136
|
+
function CardLimitsCard() {
|
|
137
|
+
const [view, setView] = useState(null);
|
|
138
|
+
const [draft, setDraft] = useState({});
|
|
139
|
+
const [busy, setBusy] = useState(false);
|
|
140
|
+
const [msg, setMsg] = useState(null);
|
|
141
|
+
const load = async () => {
|
|
142
|
+
const r = await call("/api/settings/fleet/appearance");
|
|
143
|
+
// Only an answer of the expected shape is drawn: every group's in-force values, defaults and bounds.
|
|
144
|
+
const ok = r.ok && r.body && ["portrait", "banner", "instance_quota"].every((g) => r.body.effective && r.body.effective[g] && r.body.defaults && r.body.defaults[g] && r.body.bounds && r.body.bounds[g]);
|
|
145
|
+
if (!ok) { setView({ error: true }); return; }
|
|
146
|
+
setView(r.body);
|
|
147
|
+
const d = {};
|
|
148
|
+
for (const [g, k] of LIMIT_FIELDS) { const v = r.body.configured && r.body.configured[g] && r.body.configured[g][k]; d[`${g}.${k}`] = typeof v === "number" ? String(v) : ""; }
|
|
149
|
+
setDraft(d);
|
|
150
|
+
};
|
|
151
|
+
useEffect(() => { load(); }, []);
|
|
152
|
+
if (!view) return null;
|
|
153
|
+
if (view.error) return html`<section class="card s-appearance"><${CardTitle} title=${tn("cardLimitsTitle")} topic="limits" /><p class="sub">${tn("cardLimitsUnavailable")}</p></section>`;
|
|
154
|
+
const configured = (g, k) => (view.configured && view.configured[g] && typeof view.configured[g][k] === "number" ? view.configured[g][k] : null);
|
|
155
|
+
const save = async () => {
|
|
156
|
+
const body = {};
|
|
157
|
+
for (const [g, k] of LIMIT_FIELDS) {
|
|
158
|
+
const raw = (draft[`${g}.${k}`] || "").trim();
|
|
159
|
+
const next = raw === "" ? null : Number(raw);
|
|
160
|
+
if (next !== null && !(Number.isInteger(next) && next > 0)) { setMsg(tn("cardLimitsWhole")); return; }
|
|
161
|
+
if (next !== configured(g, k)) (body[g] ??= {})[k] = next;
|
|
162
|
+
}
|
|
163
|
+
if (!Object.keys(body).length) { setMsg(tn("cardLimitsNoChange")); return; }
|
|
164
|
+
setBusy(true); setMsg(null);
|
|
165
|
+
const r = await confirmedWrite("/api/settings/fleet/appearance", { method: "PUT", body, label: tn("cardLimitsTitle") });
|
|
166
|
+
setBusy(false);
|
|
167
|
+
if (r.ok) { setMsg(tn("cardLimitsSaved")); load(); } else setMsg(tn("cardLimitsFailed", (r.body && r.body.error) || r.status));
|
|
168
|
+
};
|
|
169
|
+
const warnings = Array.isArray(view.warnings) ? view.warnings : [];
|
|
170
|
+
return html`<section class="card s-appearance"><${CardTitle} title=${tn("cardLimitsTitle")} topic="limits" />
|
|
171
|
+
<p class="sub">${tn("cardLimitsLead")}</p>
|
|
172
|
+
<fieldset class="form" disabled=${busy}>
|
|
173
|
+
${["portrait", "banner", "instance_quota"].map((g) => html`<div key=${g} class="grid3">
|
|
174
|
+
${LIMIT_FIELDS.filter(([gg]) => gg === g).map(([, k]) => {
|
|
175
|
+
const id = `cardLimit-${g}-${k}`, eff = view.effective[g][k], conf = configured(g, k);
|
|
176
|
+
return html`<div key=${k} class="field"><label for=${id}>${tn(`cardLimit_${g}_${k}`)}</label>
|
|
177
|
+
<input id=${id} type="number" min="1" step="1" placeholder=${String(view.defaults[g][k])} value=${draft[`${g}.${k}`] || ""}
|
|
178
|
+
onInput=${(e) => setDraft({ ...draft, [`${g}.${k}`]: e.target.value })} />
|
|
179
|
+
${conf !== null && conf !== eff ? html`<span class="note">${tn("cardLimitInForce", eff, view.bounds[g][k][0], view.bounds[g][k][1])}</span>` : null}</div>`;
|
|
180
|
+
})}
|
|
181
|
+
</div>`)}
|
|
182
|
+
</fieldset>
|
|
183
|
+
${warnings.length ? html`<ul class="note s-limit-warnings">${warnings.map((w, i) => html`<li key=${i}><code>${w.path}</code>: ${w.message}</li>`)}</ul>` : null}
|
|
184
|
+
<div class="save-row"><button type="button" class="btn btn-primary" disabled=${busy} onClick=${save}>${tn("cardLimitsSave")}</button>
|
|
185
|
+
${msg ? html`<span class="note" role="status">${msg}</span>` : null}</div>
|
|
186
|
+
</section>`;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** #1629 r2 (d): whether this person's screens show agents' themes (and, as they come, effects) — synced. */
|
|
190
|
+
function ThemesCard() {
|
|
191
|
+
const s = useStore(themesStore);
|
|
192
|
+
// #2117: this device's own switch (not synced): effects play unless it follows the system's reduced-motion setting.
|
|
193
|
+
const [follow, setFollow] = useState(followsReducedMotion);
|
|
194
|
+
useEffect(() => onMotionChange(() => setFollow(followsReducedMotion())), []);
|
|
195
|
+
return html`<section class="card s-appearance s-synced"><${CardTitle} title=${tn("themesTitle")} topic="themes" />
|
|
196
|
+
<label class="check"><input type="checkbox" checked=${s.shown} disabled=${safeMode()} onChange=${(e) => setThemesShown(e.target.checked)} />
|
|
197
|
+
<span>${tn("themesShow")}</span></label>
|
|
198
|
+
<p class="sub">${safeMode() ? tn("themesSafeMode") : tn("themesShowLead")}</p>
|
|
199
|
+
<label class="check"><input type="checkbox" id="fx-follow-reduced-motion" checked=${follow} onChange=${(e) => setFollowsReducedMotion(e.target.checked)} />
|
|
200
|
+
<span>${tn("themesFollowMotion")}</span></label>
|
|
201
|
+
<p class="sub">${tn("themesFollowMotionLead")}</p>
|
|
202
|
+
</section>`;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** An example to start from: the fleet's counts, as a person's sidebar widget may show them. */
|
|
206
|
+
export const SIDEBAR_WIDGET_EXAMPLE = {
|
|
207
|
+
spec: { v: 1, title: "Fleet", items: [
|
|
208
|
+
{ type: "stat", label: "Working", bind: "fleet.working" },
|
|
209
|
+
{ type: "stat", label: "Needs you", bind: "fleet.needs_you" },
|
|
210
|
+
{ type: "stat", label: "Idle", bind: "fleet.idle" },
|
|
211
|
+
{ type: "stat", label: "Stopped", bind: "fleet.stopped" },
|
|
212
|
+
] },
|
|
213
|
+
data: {},
|
|
214
|
+
};
|
|
215
|
+
/**
|
|
216
|
+
* #1629 r4-4: the person's own sidebar widget — a declarative spec and its values, as JSON; drawn under the instance
|
|
217
|
+
* list by the page's own components, with the fleet's counts as the only live values. Synced like the other choices.
|
|
218
|
+
*/
|
|
219
|
+
function SidebarWidgetCard() {
|
|
220
|
+
useStore(sidebarWidgetStore);
|
|
221
|
+
const app = useStore(appStore);
|
|
222
|
+
const saved = sidebarWidgetRaw();
|
|
223
|
+
const [text, setText] = useState(() => (saved ? JSON.stringify(saved, null, 2) : ""));
|
|
224
|
+
const [error, setError] = useState("");
|
|
225
|
+
let parsed = null, problem = "";
|
|
226
|
+
if (text.trim()) {
|
|
227
|
+
try { parsed = JSON.parse(text); problem = widgetProblem("sidebar", parsed) || ""; } catch { problem = tn("widgetNotJson"); }
|
|
228
|
+
}
|
|
229
|
+
const preview = parsed && !problem ? checkWidget("sidebar", parsed) : null;
|
|
230
|
+
const save = () => {
|
|
231
|
+
if (!parsed || problem) { setError(problem || tn("widgetNotJson")); return; }
|
|
232
|
+
setError("");
|
|
233
|
+
setSidebarWidget(parsed);
|
|
234
|
+
toast(tn("widgetSaved"));
|
|
235
|
+
};
|
|
236
|
+
const remove = () => { setSidebarWidget(null); setText(""); setError(""); toast(tn("widgetRemoved")); };
|
|
237
|
+
return html`<section class="card s-appearance s-synced s-widget"><${CardTitle} title=${tn("widgetTitle")} topic="widgets" />
|
|
238
|
+
<p class="sub">${tn("widgetLead")}</p>
|
|
239
|
+
<label class="field"><span class="sr-only">${tn("widgetTitle")}</span>
|
|
240
|
+
<textarea class="s-widget-text" rows="10" spellcheck=${false} autocapitalize="off" autocomplete="off" value=${text}
|
|
241
|
+
onInput=${(e) => { setText(e.target.value); setError(""); }}></textarea></label>
|
|
242
|
+
${error || problem ? html`<p class="err" role="alert">${error || problem}</p>` : null}
|
|
243
|
+
${preview ? html`<div class="s-widget-preview"><${AgentWidget} slot="sidebar" widget=${preview} binds=${fleetBinds(app.instances, app.exec, app.awaiting)} /></div>` : null}
|
|
244
|
+
<div class="s-widget-actions">
|
|
245
|
+
<button type="button" class="btn btn-primary" disabled=${!parsed || !!problem} onClick=${save}><${Icon} name="check" size=${16} />${tn("widgetSave")}</button>
|
|
246
|
+
${!text.trim() ? html`<button type="button" class="btn" onClick=${() => setText(JSON.stringify(SIDEBAR_WIDGET_EXAMPLE, null, 2))}>${tn("widgetExample")}</button>` : null}
|
|
247
|
+
${saved ? html`<button type="button" class="btn" onClick=${remove}>${tn("widgetRemove")}</button>` : null}
|
|
248
|
+
</div>
|
|
249
|
+
</section>`;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function OwnerCssCard() {
|
|
253
|
+
const [state, setState] = useState(null);
|
|
254
|
+
const [text, setText] = useState("");
|
|
255
|
+
const [busy, setBusy] = useState(false);
|
|
256
|
+
const [error, setError] = useState("");
|
|
257
|
+
const trial = useStore(ownerCssTrial);
|
|
258
|
+
const file = useRef(null);
|
|
259
|
+
const load = async (opts = {}) => {
|
|
260
|
+
const r = await call("/ui/owner-css/state");
|
|
261
|
+
if (!r.ok || !r.body) { setState({ failed: true }); return; }
|
|
262
|
+
setState(r.body);
|
|
263
|
+
if (opts.text !== false) setText(r.body.text || "");
|
|
264
|
+
};
|
|
265
|
+
useEffect(() => { load(); }, []);
|
|
266
|
+
// A trial answered (kept or reverted) changes what is in force: read it again, keeping the editor's text.
|
|
267
|
+
useEffect(() => { if (state) load({ text: false }); }, [trial && trial.sha]);
|
|
268
|
+
if (!state) return html`<section class="card s-appearance"><${CardTitle} title=${tn("cssTitle")} topic="css" /><p class="sub">…</p></section>`;
|
|
269
|
+
if (state.failed) return html`<section class="card s-appearance"><${CardTitle} title=${tn("cssTitle")} topic="css" /><p class="err">${tn("cssLoadFailed")}</p></section>`;
|
|
270
|
+
|
|
271
|
+
const save = async () => {
|
|
272
|
+
setBusy(true); setError("");
|
|
273
|
+
const r = await postJson("/ui/owner-css", { text });
|
|
274
|
+
setBusy(false);
|
|
275
|
+
if (!r.ok) { setError(refusal(r.body)); return; }
|
|
276
|
+
startOwnerCssTrial(r.body.trial, r.body.sha, r.body.previous);
|
|
277
|
+
toast(tn("cssTrying"));
|
|
278
|
+
load({ text: false });
|
|
279
|
+
};
|
|
280
|
+
const restore = async (sha) => {
|
|
281
|
+
setBusy(true); setError("");
|
|
282
|
+
const r = await postJson("/ui/owner-css/restore", { sha });
|
|
283
|
+
setBusy(false);
|
|
284
|
+
if (!r.ok) { setError(refusal(r.body)); return; }
|
|
285
|
+
setOwnerCssActive(r.body.active);
|
|
286
|
+
load();
|
|
287
|
+
};
|
|
288
|
+
const addAsset = async () => {
|
|
289
|
+
const f = file.current && file.current.files && file.current.files[0];
|
|
290
|
+
if (!f) return;
|
|
291
|
+
setBusy(true); setError("");
|
|
292
|
+
const r = await call("/ui/owner-css/asset", { method: "POST", headers: { "Content-Type": "application/octet-stream" }, body: f });
|
|
293
|
+
setBusy(false);
|
|
294
|
+
if (file.current) file.current.value = "";
|
|
295
|
+
if (!r.ok) { setError(refusal(r.body)); return; }
|
|
296
|
+
load({ text: false });
|
|
297
|
+
};
|
|
298
|
+
const removeAsset = async (id) => {
|
|
299
|
+
setBusy(true); setError("");
|
|
300
|
+
const r = await call(`/ui/owner-css/asset/${id}`, { method: "DELETE" });
|
|
301
|
+
setBusy(false);
|
|
302
|
+
if (!r.ok) { setError(refusal(r.body)); return; }
|
|
303
|
+
load({ text: false });
|
|
304
|
+
};
|
|
305
|
+
const copyRef = (id) => {
|
|
306
|
+
const ref = `url("asset:${id}")`;
|
|
307
|
+
try { navigator.clipboard.writeText(ref).then(() => toast(tn("cssCopied", ref)), () => toast(ref)); } catch { toast(ref); }
|
|
308
|
+
};
|
|
309
|
+
const inForce = state.active
|
|
310
|
+
? (state.pending ? tn("cssOnTrial", short(state.active)) : tn("cssInForce", short(state.active)))
|
|
311
|
+
: tn("cssNone");
|
|
312
|
+
const assetBytes = state.assets.reduce((n, a) => n + a.bytes, 0);
|
|
313
|
+
return html`<section class="card s-appearance s-owner-css"><${CardTitle} title=${tn("cssTitle")} topic="css" />
|
|
314
|
+
<p class="sub">${tn("cssLead")}</p>
|
|
315
|
+
<p class="s-css-status" role="status">${inForce}</p>
|
|
316
|
+
<label class="field"><span class="sr-only">${tn("cssTitle")}</span>
|
|
317
|
+
<textarea class="s-css-text" rows="14" spellcheck=${false} autocapitalize="off" autocomplete="off" value=${text} disabled=${busy}
|
|
318
|
+
onInput=${(e) => setText(e.target.value)}></textarea></label>
|
|
319
|
+
${error ? html`<p class="err" role="alert">${error}</p>` : null}
|
|
320
|
+
<div class="s-css-actions">
|
|
321
|
+
<button type="button" class="btn btn-primary" disabled=${busy || !text.trim()} onClick=${save}><${Icon} name="check" size=${16} />${tn("cssSave")}</button>
|
|
322
|
+
${state.active ? html`<button type="button" class="btn" disabled=${busy} onClick=${() => restore(null)}>${tn("cssOff")}</button>` : null}
|
|
323
|
+
</div>
|
|
324
|
+
${state.versions.length ? html`<details class="s-css-versions"><summary>${tn("cssVersions", state.versions.length)}</summary>
|
|
325
|
+
<ul class="s-css-list">${state.versions.map((v) => html`<li key=${v.sha}>
|
|
326
|
+
<code>${short(v.sha)}</code><span class="muted">${new Date(v.at).toLocaleString()} · ${kib(v.bytes)}</span>
|
|
327
|
+
${v.sha === state.active ? html`<span class="pill">${tn("cssCurrent")}</span>`
|
|
328
|
+
: html`<button type="button" class="btn btn-sm" disabled=${busy} onClick=${() => restore(v.sha)}>${tn("cssRestore")}</button>`}
|
|
329
|
+
</li>`)}</ul></details>` : null}
|
|
330
|
+
<h4>${tn("cssAssets")}</h4>
|
|
331
|
+
<p class="sub">${tn("cssAssetsLead", state.assets.length, state.limits.maxAssets, kib(assetBytes))}</p>
|
|
332
|
+
${state.assets.length ? html`<ul class="s-css-list">${state.assets.map((a) => html`<li key=${a.id}>
|
|
333
|
+
<code>${a.id}</code><span class="muted">${a.kind} · ${kib(a.bytes)}</span>
|
|
334
|
+
<button type="button" class="btn btn-sm" onClick=${() => copyRef(a.id)}>${tn("cssAssetCopy")}</button>
|
|
335
|
+
<button type="button" class="btn btn-sm btn-ghost" disabled=${busy} onClick=${() => removeAsset(a.id)}>${tn("cssAssetRemove")}</button>
|
|
336
|
+
</li>`)}</ul>` : null}
|
|
337
|
+
<label class="field"><span>${tn("cssAssetAdd")}</span>
|
|
338
|
+
<input ref=${file} type="file" accept="image/png,image/webp,image/jpeg,image/gif,font/woff2,.woff2" disabled=${busy} onChange=${addAsset} /></label>
|
|
339
|
+
</section>`;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function DeviceCard() {
|
|
343
|
+
const [off, setOff] = useState(ownerCssOffHere());
|
|
344
|
+
return html`<section class="card s-appearance s-local"><h3>${tn("cssDeviceTitle")}</h3>
|
|
345
|
+
<label class="check"><input type="checkbox" checked=${off} onChange=${(e) => { setOwnerCssOffHere(e.target.checked); setOff(ownerCssOffHere()); }} />
|
|
346
|
+
<span>${tn("cssDeviceOff")}</span></label>
|
|
347
|
+
<p class="sub">${tn("cssSafe")}</p>
|
|
348
|
+
</section>`;
|
|
349
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// #2072 part 3: what a backend or model field in Settings offers — the host's published availability (installed,
|
|
2
|
+
// signed in, new enough: /ui/backends) and the model catalog of that backend for that instance's account (/ui/models).
|
|
3
|
+
// Both answers are the server's cached ones; nothing here makes the server probe. A page asks once and reuses the answer
|
|
4
|
+
// for a short while, so opening several dialogs costs one request each.
|
|
5
|
+
import { useEffect, useState } from "/assets/app-html.js";
|
|
6
|
+
|
|
7
|
+
const TTL_MS = 30_000;
|
|
8
|
+
const cache = new Map(); // url → { at, value, pending }
|
|
9
|
+
|
|
10
|
+
async function getJson(url) {
|
|
11
|
+
const res = await fetch(url, { credentials: "same-origin" });
|
|
12
|
+
if (!res.ok) throw new Error(String(res.status));
|
|
13
|
+
return res.json();
|
|
14
|
+
}
|
|
15
|
+
/** The answer for `url`, shared by every field on the page while it is fresh; null when it cannot be read. */
|
|
16
|
+
function cached(url) {
|
|
17
|
+
const hit = cache.get(url), now = performance.now();
|
|
18
|
+
if (hit && (hit.pending || now - hit.at < TTL_MS)) return hit.pending ?? Promise.resolve(hit.value);
|
|
19
|
+
const pending = getJson(url).then((value) => { cache.set(url, { at: performance.now(), value, pending: null }); return value; })
|
|
20
|
+
.catch(() => { cache.delete(url); return null; });
|
|
21
|
+
cache.set(url, { at: now, value: null, pending });
|
|
22
|
+
return pending;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Drop every remembered answer. Only tests call it today: a page keeps an answer TTL_MS, so a sign-in or a newly
|
|
26
|
+
* cached catalog shows within that time, or at once on the next page load.
|
|
27
|
+
*/
|
|
28
|
+
export function forgetCatalogs() { cache.clear(); }
|
|
29
|
+
|
|
30
|
+
function useAnswer(url) {
|
|
31
|
+
const [value, setValue] = useState(undefined); // undefined: loading; null: unavailable
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (!url) { setValue(null); return undefined; }
|
|
34
|
+
let live = true;
|
|
35
|
+
setValue(undefined);
|
|
36
|
+
cached(url).then((v) => { if (live) setValue(v); });
|
|
37
|
+
return () => { live = false; };
|
|
38
|
+
}, [url]);
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** The host's backends with their state ({ name, state, minVersion, … }), or null while loading or unavailable. */
|
|
43
|
+
export function useBackendRows() {
|
|
44
|
+
const answer = useAnswer("/ui/backends");
|
|
45
|
+
return answer && Array.isArray(answer.backends) ? answer.backends : answer === undefined ? undefined : null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** The model catalog of `backend` for `instance` (its account) — [] when none is cached; undefined while loading. */
|
|
49
|
+
export function useModelCatalog(backend, instance) {
|
|
50
|
+
const url = backend ? `/ui/models?backend=${encodeURIComponent(backend)}${instance ? `&instance=${encodeURIComponent(instance)}` : ""}` : null;
|
|
51
|
+
const answer = useAnswer(url);
|
|
52
|
+
if (answer === undefined) return undefined;
|
|
53
|
+
return answer && Array.isArray(answer.models) ? answer.models : [];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** A state that keeps a backend from being chosen (shown, disabled, with why); "unknown" never does. */
|
|
57
|
+
export const BLOCKING_STATES = ["not_installed", "not_signed_in", "too_old"];
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The backend select's options: every backend the page knows, in its order, each with what the host said about it.
|
|
61
|
+
* The current value is always selectable — a probe never disables what an agent already runs — and one the page does
|
|
62
|
+
* not know (a removed backend) stays first, as before.
|
|
63
|
+
*/
|
|
64
|
+
export function backendChoices(known, rows, value, words) {
|
|
65
|
+
const byName = new Map((rows || []).map((r) => [r.name, r]));
|
|
66
|
+
const choice = (name) => {
|
|
67
|
+
const row = byName.get(name), state = row ? row.state ?? (row.installed ? "usable" : row.unknown ? "unknown" : "not_installed") : null;
|
|
68
|
+
const blocked = !!state && BLOCKING_STATES.includes(state) && name !== value;
|
|
69
|
+
const note = state === "not_installed" ? words.notInstalled : state === "not_signed_in" ? words.notSignedIn
|
|
70
|
+
: state === "too_old" ? words.tooOld(row.minVersion ?? "?") : state === "unknown" && name !== value ? words.unchecked : "";
|
|
71
|
+
return { value: name, label: note ? `${name} — ${note}` : name, disabled: blocked };
|
|
72
|
+
};
|
|
73
|
+
const list = known.map(choice);
|
|
74
|
+
return known.includes(value) || !value ? list : [{ value, label: words.removed(value), disabled: false }, ...list];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** The id of the "type another model" choice (never a model name: it has a space). */
|
|
78
|
+
export const OTHER_MODEL = "\u0000other";
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The model select's options: the inherited choice (empty value), the current model first when the catalog does not
|
|
82
|
+
* list it (marked), the catalog, and "Other…" for a name the CLI does not list.
|
|
83
|
+
*/
|
|
84
|
+
export function modelChoices(catalog, value, words, inherit) {
|
|
85
|
+
const list = [];
|
|
86
|
+
if (inherit) list.push({ value: "", label: inherit });
|
|
87
|
+
const ids = new Set((catalog || []).map((m) => m.id));
|
|
88
|
+
if (value && !ids.has(value)) list.push({ value, label: `${value} — ${words.notListed}` });
|
|
89
|
+
for (const m of catalog || []) list.push({ value: m.id, label: m.description ? `${m.label || m.id} — ${m.description}` : (m.label || m.id) });
|
|
90
|
+
list.push({ value: OTHER_MODEL, label: words.other });
|
|
91
|
+
return list;
|
|
92
|
+
}
|
|
@@ -13,11 +13,13 @@ import { api, confirmedWrite, newKey } from "./settings-confirm.js";
|
|
|
13
13
|
import { adminUserIdError, adminUserIdPattern, botHandle, TokenEnvNote, TokenField, verifyBotToken } from "./settings-token.js";
|
|
14
14
|
import { DiscordServerPicker } from "./settings-discord.js";
|
|
15
15
|
import { startOperation } from "./settings-apply.js";
|
|
16
|
+
import "./settings-strings.js"; // its words, wherever a picker of this file is used (#2072: Fleet's create dialog too)
|
|
17
|
+
import { backendChoices, modelChoices, OTHER_MODEL, useBackendRows, useModelCatalog } from "./settings-catalog.js";
|
|
16
18
|
import {
|
|
17
19
|
ACCESS_MODES, AGENT_FIELDS, AGENT_MODES, BOT_FIELDS, CH_TYPES, CLASSIC_FIELDS, HANG_TIMEOUT_DEFAULT_MINUTES, LOG_LEVELS, TOOL_PROGRESS, TOOL_SETS,
|
|
18
20
|
VISIBILITY_MODES, accessConfirmations, agentForm, agentPatch, backendOptions, changedFields, channelId, channelsOf,
|
|
19
21
|
chLabel, hasOwn, impactOf, nonNegative, nonNegativeInteger, positive, replyGuardSupported, sameValue, visibilityDefault,
|
|
20
|
-
|
|
22
|
+
classicWebEchoEffective, worstImpact,
|
|
21
23
|
} from "./settings-model.js";
|
|
22
24
|
|
|
23
25
|
const tn = (k, ...v) => t(`settings.${k}`, ...v);
|
|
@@ -32,9 +34,36 @@ export function Select({ value, options, onChange, disabled, id, label }) {
|
|
|
32
34
|
return html`<select id=${id} value=${value} disabled=${disabled} aria-label=${label} onChange=${(e) => onChange(e.target.value)}>
|
|
33
35
|
${opts.map((o) => html`<option key=${o.value} value=${o.value}>${o.label}</option>`)}</select>`;
|
|
34
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* #2072: a backend field shows every backend with what this host says about it — one it cannot run is disabled with
|
|
39
|
+
* why (not installed, not signed in, too old); one not checked yet stays selectable; the current value always can be.
|
|
40
|
+
*/
|
|
35
41
|
export function BackendSelect({ value, onChange, disabled, id }) {
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
const rows = useBackendRows();
|
|
43
|
+
const known = backendOptions(value).filter((o) => !o.unavailable).map((o) => o.value);
|
|
44
|
+
const opts = backendChoices(known, rows || [], value, {
|
|
45
|
+
notInstalled: tn("backendNotInstalled"), notSignedIn: tn("backendNotSignedIn"), tooOld: (v) => tn("backendTooOld", v),
|
|
46
|
+
unchecked: tn("backendUnchecked"), removed: (v) => tn("backendUnavailable", v),
|
|
47
|
+
});
|
|
48
|
+
return html`<select id=${id} value=${value} disabled=${disabled} onChange=${(e) => onChange(e.target.value)}>
|
|
49
|
+
${opts.map((o) => html`<option key=${o.value} value=${o.value} disabled=${o.disabled}>${o.label}</option>`)}</select>`;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* #2072: a model field is a list — that backend's models for this account (cached; never a probe), the current one
|
|
53
|
+
* first and marked when the CLI does not list it, and "Other…" for a name it does not list (a text box opens with
|
|
54
|
+
* the current value in it: choosing "Other…" alone changes nothing — emptying the box is what inherits, #2098 review).
|
|
55
|
+
* `inherit`: the label of the empty choice (inherit / none), or omit it when the field has none.
|
|
56
|
+
*/
|
|
57
|
+
export function ModelSelect({ value, onChange, disabled, id, backend, instance, inherit }) {
|
|
58
|
+
const catalog = useModelCatalog(backend, instance);
|
|
59
|
+
const [typing, setTyping] = useState(false);
|
|
60
|
+
const opts = modelChoices(catalog || [], value, { notListed: tn("modelNotListed"), other: tn("modelOther") }, inherit);
|
|
61
|
+
return html`<div class="model-pick">
|
|
62
|
+
<select id=${id} value=${typing ? OTHER_MODEL : value} disabled=${disabled}
|
|
63
|
+
onChange=${(e) => { const v = e.target.value; if (v === OTHER_MODEL) setTyping(true); else { setTyping(false); onChange(v); } }}>
|
|
64
|
+
${opts.map((o) => html`<option key=${o.value} value=${o.value}>${o.label}</option>`)}</select>
|
|
65
|
+
${typing ? html`<input type="text" aria-label=${tn("modelOtherLabel")} placeholder=${tn("modelOtherPlaceholder")} disabled=${disabled}
|
|
66
|
+
value=${value} onInput=${(e) => onChange(e.target.value)} />` : null}</div>`;
|
|
38
67
|
}
|
|
39
68
|
export function Impact({ schema, field }) { return html`<span class="impact">${impactText(impactOf(schema, field))}</span>`; }
|
|
40
69
|
export function Label({ text, schema, field, htmlFor }) {
|
|
@@ -228,7 +257,8 @@ export function AgentDialog({ name, inst, ctx, onClose }) {
|
|
|
228
257
|
<input id="ag-dn" type="text" placeholder=${name} disabled=${f.inherit.display_name} value=${shown("display_name", f.display_name, defaults.display_name || "")} onInput=${(e) => set("display_name")(e.target.value)} /></${Override}></div>
|
|
229
258
|
<div class="field">${L(tn("description"), "instance.description", "ag-desc")}<input id="ag-desc" type="text" value=${f.description} onInput=${(e) => set("description")(e.target.value)} /></div>
|
|
230
259
|
<div class="field">${L(tn("backend"), "instance.backend", "ag-be")}<${BackendSelect} id="ag-be" value=${f.backend} onChange=${set("backend")} /></div>
|
|
231
|
-
<div class="field">${L(tn("model"), "instance.model", "ag-model")}
|
|
260
|
+
<div class="field">${L(tn("model"), "instance.model", "ag-model")}<${ModelSelect} id="ag-model" value=${f.model} onChange=${set("model")}
|
|
261
|
+
backend=${f.backend || defaults.backend || "claude-code"} instance=${name} inherit=${tn("inheritPlaceholder")} /></div>
|
|
232
262
|
</div>
|
|
233
263
|
<div class="field">${L(tn("workingDir"), "instance.working_directory", "ag-wd")}<input id="ag-wd" type="text" value=${f.working_directory} onInput=${(e) => set("working_directory")(e.target.value)} /></div>
|
|
234
264
|
<div class="field">${L(`${tn("autoPause")} (${tn("minutes")})`, "instance.auto_pause_after", "ag-auto")}
|
|
@@ -264,8 +294,8 @@ export function AgentDialog({ name, inst, ctx, onClose }) {
|
|
|
264
294
|
onChange=${(e) => set("lightweight")(e.target.checked)} /><span>${tn("lightweight")}</span></label></${Override}></div>
|
|
265
295
|
<div class="field">${L(tn("modelFailover"), "instance.model_failover", "ag-fo")}
|
|
266
296
|
<${Override} inherit=${f.inherit.model_failover} onInherit=${setInherit("model_failover")}>
|
|
267
|
-
|
|
268
|
-
value=${shown("model_failover", f.model_failover, (defaults.model_failover && defaults.model_failover[0]) || "")}
|
|
297
|
+
<${ModelSelect} id="ag-fo" disabled=${f.inherit.model_failover} backend=${f.backend || defaults.backend || "claude-code"} instance=${name} inherit=${tn("modelNone")}
|
|
298
|
+
value=${shown("model_failover", f.model_failover, (defaults.model_failover && defaults.model_failover[0]) || "")} onChange=${set("model_failover")} /></${Override}>
|
|
269
299
|
<${Feedback} warning=${failoverWarn} /></div>
|
|
270
300
|
</div>
|
|
271
301
|
<div class="field">${L(tn("systemPrompt"), "instance.systemPrompt", "ag-sys")}<input id="ag-sys" type="text" placeholder="e.g. file:prompts/dev.md" value=${f.systemPrompt} onInput=${(e) => set("systemPrompt")(e.target.value)} /></div>
|
|
@@ -557,7 +587,7 @@ export function ClassicDialog({ room, ctx, onClose }) {
|
|
|
557
587
|
backend: c.backend || inherited.backend, model: c.model || "",
|
|
558
588
|
auto_pause_after: String(c.auto_pause_after ?? inherited.auto_pause_after), tool_progress: c.tool_progress ?? inherited.tool_progress,
|
|
559
589
|
reply_completion_guard: c.reply_completion_guard ?? inherited.reply_completion_guard,
|
|
560
|
-
collab: !!c.collab, web_echo:
|
|
590
|
+
collab: !!c.collab, web_echo: classicWebEchoEffective(c, fleet), context_lines: String(c.context_lines ?? cd.context_lines ?? 5),
|
|
561
591
|
inherit: { auto_pause_after: !hasOwn(c, "auto_pause_after"), tool_progress: !hasOwn(c, "tool_progress"), reply_completion_guard: !hasOwn(c, "reply_completion_guard") },
|
|
562
592
|
}));
|
|
563
593
|
const set = (k) => (v) => setF((x) => ({ ...x, [k]: v }));
|
|
@@ -580,12 +610,9 @@ export function ClassicDialog({ room, ctx, onClose }) {
|
|
|
580
610
|
auto_pause_after: hasOwn(c, "auto_pause_after") ? c.auto_pause_after : null,
|
|
581
611
|
tool_progress: hasOwn(c, "tool_progress") ? c.tool_progress : null,
|
|
582
612
|
reply_completion_guard: hasOwn(c, "reply_completion_guard") ? c.reply_completion_guard : null,
|
|
583
|
-
collab: !!c.collab, context_lines: c.context_lines ?? cd.context_lines ?? 5, web_echo:
|
|
613
|
+
collab: !!c.collab, context_lines: c.context_lines ?? cd.context_lines ?? 5, web_echo: classicWebEchoEffective(c, fleet),
|
|
584
614
|
});
|
|
585
615
|
if (!Object.keys(patch).length) { onClose(); return; }
|
|
586
|
-
// #1320: turning web echo on for a group posts every web message where all its members can read it.
|
|
587
|
-
if (patch.web_echo === true && !c.web_echo && webEchoIsGroup(c.channelId)
|
|
588
|
-
&& !(await ask(tn("webEchoGroupConfirm"), { confirmLabel: tn("webEchoEnable") }))) return;
|
|
589
616
|
if (!lease.current()) return;
|
|
590
617
|
ctx.stageClassic(c, patch);
|
|
591
618
|
onClose();
|
|
@@ -594,7 +621,8 @@ export function ClassicDialog({ room, ctx, onClose }) {
|
|
|
594
621
|
return html`<${EditDialog} title=${c.name || c.instanceName} subtitle=${`ID: ${c.instanceName} · ${tn(`exec_${runtime.execution}`)}`} onClose=${onClose} onStage=${stage} ready=${ready} impacts=${CLASSIC_FIELDS} schema=${schema}>
|
|
595
622
|
<div class="grid2">
|
|
596
623
|
<div class="field">${L(tn("backend"), "classic.backend", "cl-be")}<${BackendSelect} id="cl-be" value=${f.backend} onChange=${set("backend")} /></div>
|
|
597
|
-
<div class="field">${L(tn("model"), "classic.model", "cl-model")}
|
|
624
|
+
<div class="field">${L(tn("model"), "classic.model", "cl-model")}<${ModelSelect} id="cl-model" value=${f.model} onChange=${set("model")}
|
|
625
|
+
backend=${f.backend} instance=${c.instanceName} inherit=${inherited.model ? tn("inheritValue", inherited.model) : tn("inheritPlaceholder")} /></div>
|
|
598
626
|
<div class="field">${L(`${tn("autoPause")} (${tn("minutes")})`, "classic.auto_pause_after", "cl-auto")}
|
|
599
627
|
<${Override} inherit=${f.inherit.auto_pause_after} onInherit=${setInherit("auto_pause_after")}>
|
|
600
628
|
<input id="cl-auto" type="number" min="0" step="1" class=${errors.auto ? "invalid" : ""} disabled=${f.inherit.auto_pause_after}
|