@songsid/agend 2.3.0-beta.25 → 2.3.0-beta.27
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 -5
- package/README.zh-TW.md +7 -5
- package/dist/adapter-world.js.map +1 -1
- package/dist/agend-home.d.ts +5 -0
- package/dist/agend-home.js +7 -0
- package/dist/agend-home.js.map +1 -0
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.js +8 -0
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/agent-file-read.js +1 -1
- package/dist/agent-file-read.js.map +1 -1
- package/dist/agent-whats-new.js +12 -2
- package/dist/agent-whats-new.js.map +1 -1
- package/dist/apply-job.js.map +1 -1
- package/dist/atomic-write.js.map +1 -1
- package/dist/auth-api.d.ts +5 -0
- package/dist/auth-api.js +21 -0
- package/dist/auth-api.js.map +1 -1
- package/dist/authority/authority-state.d.ts +4 -0
- package/dist/authority/authority-state.js +17 -8
- package/dist/authority/authority-state.js.map +1 -1
- package/dist/authority/session-authorizations.js.map +1 -1
- package/dist/authority/web-authority.d.ts +14 -6
- package/dist/authority/web-authority.js +19 -5
- package/dist/authority/web-authority.js.map +1 -1
- package/dist/backend/antigravity.d.ts +2 -2
- package/dist/backend/antigravity.js +2 -1
- package/dist/backend/antigravity.js.map +1 -1
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex-package.js.map +1 -1
- package/dist/backend/codex-session-lookup.js.map +1 -1
- package/dist/backend/codex.js +1 -1
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/effort-metadata.js.map +1 -1
- package/dist/backend/grok.js.map +1 -1
- package/dist/backend/kiro-engine-ledger.js.map +1 -1
- package/dist/backend/kiro-kas-store.js.map +1 -1
- package/dist/backend/kiro-v2-store.js.map +1 -1
- package/dist/backend/kiro-v3-identity.js.map +1 -1
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/launch-effort.js.map +1 -1
- package/dist/backend/mock.js.map +1 -1
- package/dist/backend/muse.js.map +1 -1
- package/dist/backend/opencode-permission.js.map +1 -1
- package/dist/backend/opencode.js.map +1 -1
- package/dist/backend/session-signals.js.map +1 -1
- package/dist/backend-availability.js.map +1 -1
- package/dist/backend-command.js.map +1 -1
- package/dist/backend-outage.js.map +1 -1
- package/dist/binary-probe.js.map +1 -1
- package/dist/bug-report.js.map +1 -1
- package/dist/bundled-skills.js.map +1 -1
- package/dist/cache-service.js +1 -1
- package/dist/cache-service.js.map +1 -1
- package/dist/channel/adapters/discord.d.ts +2 -0
- package/dist/channel/adapters/discord.js +40 -5
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +6 -0
- package/dist/channel/adapters/telegram.js +54 -14
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/mcp-server.js +10 -1
- package/dist/channel/mcp-server.js.map +1 -1
- package/dist/channel/mcp-tools.js +2 -2
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/message-queue.js.map +1 -1
- package/dist/channel/sticker-download.js.map +1 -1
- package/dist/channel/tool-router.js.map +1 -1
- package/dist/classic-channel-manager.js +3 -2
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli-entry.js.map +1 -1
- package/dist/cli-flags.js.map +1 -1
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +44 -9
- package/dist/cli.js.map +1 -1
- package/dist/command-table.js.map +1 -1
- package/dist/compact-watch.js.map +1 -1
- package/dist/completion-install.js.map +1 -1
- package/dist/completion.js.map +1 -1
- package/dist/config-validator.js +22 -6
- package/dist/config-validator.js.map +1 -1
- package/dist/config.js +8 -2
- package/dist/config.js.map +1 -1
- package/dist/connection-secrets.js.map +1 -1
- package/dist/context-guardian.js.map +1 -1
- package/dist/coredump-filter.js.map +1 -1
- package/dist/cpu-profile.js.map +1 -1
- package/dist/cross-instance-envelope.js.map +1 -1
- package/dist/daemon.d.ts +3 -0
- package/dist/daemon.js +76 -4
- package/dist/daemon.js.map +1 -1
- package/dist/daily-summary.js.map +1 -1
- package/dist/darwin-memory.js.map +1 -1
- package/dist/dashboard-live.d.ts +8 -0
- package/dist/dashboard-live.js +35 -0
- package/dist/dashboard-live.js.map +1 -0
- package/dist/dashboard-permissions.d.ts +69 -0
- package/dist/dashboard-permissions.js +138 -0
- package/dist/dashboard-permissions.js.map +1 -0
- package/dist/decision-list.js.map +1 -1
- package/dist/delivery-consumed-watch.js.map +1 -1
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/delivery-outbox.test.js.map +1 -1
- package/dist/delivery-reconciliation.test.js.map +1 -1
- package/dist/diagnostic-redaction.js.map +1 -1
- package/dist/doctor.d.ts +3 -0
- package/dist/doctor.js +10 -0
- package/dist/doctor.js.map +1 -1
- package/dist/e2e-isolation.js.map +1 -1
- package/dist/event-log.js.map +1 -1
- package/dist/export-import.js +3 -2
- package/dist/export-import.js.map +1 -1
- package/dist/fleet-control-audit.js.map +1 -1
- package/dist/fleet-label.js.map +1 -1
- package/dist/fleet-lock.js +3 -2
- package/dist/fleet-lock.js.map +1 -1
- package/dist/fleet-manager.d.ts +84 -7
- package/dist/fleet-manager.js +562 -158
- package/dist/fleet-manager.js.map +1 -1
- package/dist/fleet-process-handlers.js +3 -2
- package/dist/fleet-process-handlers.js.map +1 -1
- package/dist/fleet-startup.d.ts +14 -0
- package/dist/fleet-startup.js +30 -0
- package/dist/fleet-startup.js.map +1 -0
- package/dist/fleet-traffic.js.map +1 -1
- package/dist/forged-envelope-scan.d.ts +0 -20
- package/dist/forged-envelope-scan.js +3 -2
- package/dist/forged-envelope-scan.js.map +1 -1
- package/dist/full-restart.js.map +1 -1
- package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +2 -0
- package/dist/general-knowledge/skills/web-appearance/SKILL.md +2 -0
- package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +43 -21
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +52 -23
- package/dist/host-memory.js.map +1 -1
- package/dist/idle-gate-log.d.ts +19 -0
- package/dist/idle-gate-log.js +15 -0
- package/dist/idle-gate-log.js.map +1 -0
- package/dist/instance-config-impact.js +6 -0
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.js +1 -1
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/instructions.d.ts +2 -0
- package/dist/instructions.js +6 -2
- package/dist/instructions.js.map +1 -1
- package/dist/kiro-compact-pane.js.map +1 -1
- package/dist/kiro-db-reader.js.map +1 -1
- package/dist/kiro-engine-status.js.map +1 -1
- package/dist/kiro-log-gate.js.map +1 -1
- package/dist/kiro-transcript-lane.js.map +1 -1
- package/dist/locale.js +40 -6
- package/dist/locale.js.map +1 -1
- package/dist/log-service.js.map +1 -1
- package/dist/log-steps.js.map +1 -1
- package/dist/logger.js +3 -2
- package/dist/logger.js.map +1 -1
- package/dist/login-controller.js.map +1 -1
- package/dist/login-manager.js.map +1 -1
- package/dist/login-shell.js.map +1 -1
- package/dist/memory-pressure.js.map +1 -1
- package/dist/muse-usage-relay.js.map +1 -1
- package/dist/needs-you-hub.js.map +1 -1
- package/dist/needs-you-live.js.map +1 -1
- package/dist/network-family.js.map +1 -1
- package/dist/operator-control-paths.js +3 -4
- package/dist/operator-control-paths.js.map +1 -1
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +68 -68
- package/dist/outbound-schemas.js +1 -1
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/outbox-open-error.js.map +1 -1
- package/dist/pane-context-cache.js.map +1 -1
- package/dist/paths.js.map +1 -1
- package/dist/private-dir.js.map +1 -1
- package/dist/probe-worker-pool.js.map +1 -1
- package/dist/profile-control.js.map +1 -1
- package/dist/prompt-file-ref.js.map +1 -1
- package/dist/provider-probe.js.map +1 -1
- package/dist/provider-secret-registry.js.map +1 -1
- package/dist/public-link-auto-open.d.ts +32 -0
- package/dist/public-link-auto-open.js +86 -0
- package/dist/public-link-auto-open.js.map +1 -0
- package/dist/public-link-owner.d.ts +10 -0
- package/dist/public-link-owner.js +36 -0
- package/dist/public-link-owner.js.map +1 -0
- package/dist/public-link-progress.js.map +1 -1
- package/dist/public-preview.js.map +1 -1
- package/dist/public-web-gateway.js +7 -2
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/public-web-link.d.ts +35 -1
- package/dist/public-web-link.js +142 -43
- package/dist/public-web-link.js.map +1 -1
- package/dist/quickstart-api.js.map +1 -1
- package/dist/quickstart.d.ts +5 -0
- package/dist/quickstart.js +25 -16
- package/dist/quickstart.js.map +1 -1
- package/dist/reply-buttons-controller.js.map +1 -1
- package/dist/reply-dedup.js.map +1 -1
- package/dist/resource-report.js.map +1 -1
- package/dist/restart-guard.js.map +1 -1
- package/dist/restart-progress.js.map +1 -1
- package/dist/rollout-index.js.map +1 -1
- package/dist/runtime-cpu-profile.js.map +1 -1
- package/dist/scheduler/db.js.map +1 -1
- package/dist/scheduler/scheduler.js.map +1 -1
- package/dist/scheduler/task-board-text.js +16 -11
- package/dist/scheduler/task-board-text.js.map +1 -1
- package/dist/scheduler/task-board.d.ts +39 -11
- package/dist/scheduler/task-board.js +123 -45
- package/dist/scheduler/task-board.js.map +1 -1
- package/dist/scheduler/types.d.ts +1 -1
- package/dist/sd-notify.js.map +1 -1
- package/dist/search/search-index.js.map +1 -1
- package/dist/secret-file.js.map +1 -1
- package/dist/secret-store.js.map +1 -1
- package/dist/service-installer.d.ts +7 -1
- package/dist/service-installer.js +13 -1
- package/dist/service-installer.js.map +1 -1
- package/dist/service-plan.d.ts +33 -4
- package/dist/service-plan.js +177 -8
- package/dist/service-plan.js.map +1 -1
- package/dist/session-oom.d.ts +21 -0
- package/dist/session-oom.js +139 -0
- package/dist/session-oom.js.map +1 -0
- package/dist/settings-api.js +3 -1
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-baseline.js.map +1 -1
- package/dist/settings-change.d.ts +5 -0
- package/dist/settings-change.js +71 -1
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-confirmation.d.ts +4 -0
- package/dist/settings-confirmation.js +2 -0
- package/dist/settings-confirmation.js.map +1 -1
- package/dist/settings-control.js.map +1 -1
- package/dist/settings-effect.js +4 -1
- package/dist/settings-effect.js.map +1 -1
- package/dist/settings-http-confirmation.js +1 -0
- package/dist/settings-http-confirmation.js.map +1 -1
- package/dist/settings-instance-patch.js +13 -1
- package/dist/settings-instance-patch.js.map +1 -1
- package/dist/settings-transaction.js.map +1 -1
- package/dist/setup-auth.js.map +1 -1
- package/dist/setup-host.d.ts +5 -1
- package/dist/setup-host.js +8 -1
- package/dist/setup-host.js.map +1 -1
- package/dist/setup-wizard.js +5 -2
- package/dist/setup-wizard.js.map +1 -1
- package/dist/spawn-gate.d.ts +4 -0
- package/dist/spawn-gate.js +15 -5
- package/dist/spawn-gate.js.map +1 -1
- package/dist/start-attempt.d.ts +1 -0
- package/dist/start-attempt.js +18 -0
- package/dist/start-attempt.js.map +1 -0
- package/dist/start-failure-notice.d.ts +4 -0
- package/dist/start-failure-notice.js +20 -0
- package/dist/start-failure-notice.js.map +1 -0
- package/dist/start-failure.d.ts +147 -0
- package/dist/start-failure.js +281 -0
- package/dist/start-failure.js.map +1 -0
- package/dist/startup-memory-deadline.d.ts +23 -0
- package/dist/startup-memory-deadline.js +55 -0
- package/dist/startup-memory-deadline.js.map +1 -0
- package/dist/statusline-watcher.js.map +1 -1
- package/dist/storm-window.js.map +1 -1
- package/dist/systemd-peer-bus.js.map +1 -1
- package/dist/target-queue-worker.js.map +1 -1
- package/dist/task-board-summary.js.map +1 -1
- package/dist/task-board-tool.js +3 -2
- package/dist/task-board-tool.js.map +1 -1
- package/dist/task-board-waker.js +8 -4
- package/dist/task-board-waker.js.map +1 -1
- package/dist/tips.js.map +1 -1
- package/dist/tmux-control.js.map +1 -1
- package/dist/tmux-exec-helper.js.map +1 -1
- package/dist/tmux-manager.d.ts +1 -0
- package/dist/tmux-manager.js +2 -1
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tmux-read.js +1 -1
- package/dist/tmux-read.js.map +1 -1
- package/dist/tool-permissions.js.map +1 -1
- package/dist/tool-progress.js.map +1 -1
- package/dist/topic-archiver.js.map +1 -1
- package/dist/topic-commands.js.map +1 -1
- package/dist/topic-link.js.map +1 -1
- package/dist/transcript-delta-reader.js.map +1 -1
- package/dist/transcript-jsonl.js.map +1 -1
- package/dist/transcript-monitor.js.map +1 -1
- package/dist/transcript-sources.js.map +1 -1
- package/dist/tunnel/cloudflared-install.js.map +1 -1
- package/dist/tunnel/cloudflared.js.map +1 -1
- package/dist/tunnel/lease.js +3 -2
- package/dist/tunnel/lease.js.map +1 -1
- package/dist/tunnel/manager.js.map +1 -1
- package/dist/tunnel/public-fetch.js.map +1 -1
- package/dist/tunnel/purpose-lane.js.map +1 -1
- package/dist/tunnel/types.js.map +1 -1
- package/dist/types.d.ts +22 -1
- package/dist/ui/chat-render.js +7 -17
- package/dist/ui/chat-store.js +171 -61
- package/dist/ui/chat-strings.js +9 -4
- package/dist/ui/chat-thread.js +48 -1
- package/dist/ui/fleet-canvas-layout.js +3 -0
- package/dist/ui/fleet-canvas.js +23 -20
- package/dist/ui/fleet-office-editor.js +149 -0
- package/dist/ui/fleet-office-model.js +358 -0
- package/dist/ui/fleet-office-pack.js +199 -0
- package/dist/ui/fleet-office-skin.js +474 -0
- package/dist/ui/fleet-office-store.js +49 -0
- package/dist/ui/fleet-office-zones.js +177 -0
- package/dist/ui/fleet-office.js +434 -0
- package/dist/ui/fleet-org.js +18 -5
- package/dist/ui/guest-refused.html +41 -0
- package/dist/ui/image-lightbox.js +9 -3
- package/dist/ui/panel-chat.js +35 -22
- package/dist/ui/panel-settings.js +24 -7
- package/dist/ui/settings-dialogs.js +11 -2
- package/dist/ui/settings-model.js +20 -0
- package/dist/ui/settings-strings.js +36 -0
- package/dist/ui/settings-web-authority.js +7 -2
- package/dist/ui/shared/agend-auth.js +2 -0
- package/dist/ui/shared/app-i18n.js +2 -2
- package/dist/ui/shared/app-prefs.js +2 -0
- package/dist/ui/shared/app-shell.js +62 -9
- package/dist/ui/shared/app-store.js +1 -0
- package/dist/ui/shared/app-stream.js +2 -1
- package/dist/ui/shared/app.css +164 -15
- package/dist/ui/shared/guest-refused.js +35 -0
- package/dist/ui/shared/header-tools.js +2 -2
- package/dist/ui/shared/instance-nav.js +25 -8
- package/dist/ui/shared/tokens.css +4 -0
- package/dist/ui/shared/ui-dialog.js +2 -2
- package/dist/ui/shared/usage-dialog.js +152 -6
- package/dist/ui/shared/view-strings.js +20 -0
- package/dist/ui/shared/web-authorize.js +3 -1
- package/dist/update-check.js.map +1 -1
- package/dist/update-dispatch.js.map +1 -1
- package/dist/update-install.js.map +1 -1
- package/dist/update-launch.js.map +1 -1
- package/dist/update-marker.js.map +1 -1
- package/dist/update-preflight.js.map +1 -1
- package/dist/update-progress.d.ts +3 -1
- package/dist/update-progress.js +3 -1
- package/dist/update-progress.js.map +1 -1
- package/dist/usage/format-rich.js.map +1 -1
- package/dist/usage/providers.d.ts +8 -0
- package/dist/usage/providers.js +6 -2
- package/dist/usage/providers.js.map +1 -1
- package/dist/usage/statusline-usage.js.map +1 -1
- package/dist/usage/usage-api.js +2 -1
- package/dist/usage/usage-api.js.map +1 -1
- package/dist/usage-report.js.map +1 -1
- package/dist/usage-rollup.js +1 -1
- package/dist/usage-rollup.js.map +1 -1
- package/dist/user-systemd.js.map +1 -1
- package/dist/view-api.js +1 -1
- package/dist/view-api.js.map +1 -1
- package/dist/wake-coordinator.js.map +1 -1
- package/dist/web-ambient.js.map +1 -1
- package/dist/web-api.d.ts +19 -2
- package/dist/web-api.js +180 -28
- package/dist/web-api.js.map +1 -1
- package/dist/web-app-icon.js.map +1 -1
- package/dist/web-appearance-policy.js.map +1 -1
- package/dist/web-appearance-tools.js.map +1 -1
- package/dist/web-appearance-view.js.map +1 -1
- package/dist/web-appearance.js.map +1 -1
- package/dist/web-auth.js +5 -4
- package/dist/web-auth.js.map +1 -1
- package/dist/web-backend-login.js.map +1 -1
- package/dist/web-card.js.map +1 -1
- package/dist/web-channel-echo.js.map +1 -1
- package/dist/web-chat-history.d.ts +41 -11
- package/dist/web-chat-history.js +93 -22
- package/dist/web-chat-history.js.map +1 -1
- package/dist/web-commands.js.map +1 -1
- package/dist/web-effect-bundle.d.ts +2 -2
- package/dist/web-effect-library.js.map +1 -1
- package/dist/web-effect-playback.js.map +1 -1
- package/dist/web-effect-profiles.d.ts +16 -16
- package/dist/web-effect-profiles.js +3 -2
- package/dist/web-effect-profiles.js.map +1 -1
- package/dist/web-effect-service.js.map +1 -1
- package/dist/web-effect-tools.js.map +1 -1
- package/dist/web-effect-trials.js.map +1 -1
- package/dist/web-effect-upload.js.map +1 -1
- package/dist/web-effect.d.ts +7 -7
- package/dist/web-effect.js.map +1 -1
- package/dist/web-experimental.js.map +1 -1
- package/dist/web-guest-access.js +1 -1
- package/dist/web-guest-access.js.map +1 -1
- package/dist/web-guest-rooms.js.map +1 -1
- package/dist/web-host-guard.js.map +1 -1
- package/dist/web-icons.js.map +1 -1
- package/dist/web-identity-color.js.map +1 -1
- package/dist/web-images.js.map +1 -1
- package/dist/web-libavoid.js.map +1 -1
- package/dist/web-login.js.map +1 -1
- package/dist/web-mentions.js.map +1 -1
- package/dist/web-mod-agent.js.map +1 -1
- package/dist/web-mod-store.js.map +1 -1
- package/dist/web-office-store.d.ts +104 -0
- package/dist/web-office-store.js +236 -0
- package/dist/web-office-store.js.map +1 -0
- package/dist/web-owner-css-store.js.map +1 -1
- package/dist/web-owner-css.js.map +1 -1
- package/dist/web-png.js.map +1 -1
- package/dist/web-prefs.js +2 -0
- package/dist/web-prefs.js.map +1 -1
- package/dist/web-profile.js.map +1 -1
- package/dist/web-reactions.d.ts +2 -1
- package/dist/web-request-context.d.ts +5 -0
- package/dist/web-request-context.js.map +1 -1
- package/dist/web-returning-device.js +1 -1
- package/dist/web-returning-device.js.map +1 -1
- package/dist/web-schedules.js.map +1 -1
- package/dist/web-send-dedupe.d.ts +36 -0
- package/dist/web-send-dedupe.js +69 -0
- package/dist/web-send-dedupe.js.map +1 -0
- package/dist/web-session-authorization.js.map +1 -1
- package/dist/web-session.js +1 -1
- package/dist/web-session.js.map +1 -1
- package/dist/web-settings-patch.d.ts +1 -0
- package/dist/web-settings-patch.js +6 -4
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/web-settings-tools.js.map +1 -1
- package/dist/web-terminal-http.js.map +1 -1
- package/dist/web-terminal.js.map +1 -1
- package/dist/web-theme-store.js.map +1 -1
- package/dist/web-upload.d.ts +5 -1
- package/dist/web-upload.js +6 -4
- package/dist/web-upload.js.map +1 -1
- package/dist/web-widget.js.map +1 -1
- package/dist/webhook-emitter.js.map +1 -1
- package/dist/windows-shortcuts.js +1 -1
- package/dist/windows-shortcuts.js.map +1 -1
- package/dist/workflow-templates/default.md +1 -1
- package/dist/ws-server.js.map +1 -1
- package/dist/yaml-shared-nodes.js.map +1 -1
- package/launcher/agend-install.cjs +281 -6
- package/launcher/private-home.cjs +43 -0
- package/package.json +3 -2
- package/templates/windows-shortcuts.ps1 +1 -1
package/dist/ui/fleet-org.js
CHANGED
|
@@ -11,11 +11,11 @@ import { t, register } from "/assets/app-i18n.js";
|
|
|
11
11
|
import { chatPath } from "/assets/app-route.js";
|
|
12
12
|
import { Empty, Skeleton } from "/assets/ui-states.js";
|
|
13
13
|
import { Icon } from "/assets/ui-icons.js";
|
|
14
|
-
import { modelText } from "/assets/instance-nav.js";
|
|
14
|
+
import { backendLabel, modelText, namesOf } from "/assets/instance-nav.js";
|
|
15
15
|
|
|
16
16
|
register("org", {
|
|
17
17
|
en: {
|
|
18
|
-
title: "Org chart", general: "General", unteamed: "Not in a team", members: "{0} members", member: "1 member", instances: "{0} instances", instance: "1 instance",
|
|
18
|
+
starting: "Starting", queued: "Queued", title: "Org chart", general: "General", unteamed: "Not in a team", members: "{0} members", member: "1 member", instances: "{0} instances", instance: "1 instance",
|
|
19
19
|
needs: "Needs you", working: "Working", idle: "Idle", stuck: "Looks stuck", paused: "Paused", stopped: "Stopped", crashed: "Crashed",
|
|
20
20
|
missing: "Not in the fleet", thread_discord: "Discord thread", thread_telegram: "Telegram topic", openChat: "Open chat",
|
|
21
21
|
classic: "ClassicBot room", empty: "No instances yet", emptyHint: "Instances and teams show here once the fleet has them.",
|
|
@@ -23,7 +23,7 @@ register("org", {
|
|
|
23
23
|
sending: "Sending:", queuedNote: "(queued)", workingNote: "(in progress)",
|
|
24
24
|
},
|
|
25
25
|
"zh-TW": {
|
|
26
|
-
title: "組織圖", general: "General", unteamed: "未加入 team", members: "{0} 名成員", member: "1 名成員", instances: "{0} 個 instance", instance: "1 個 instance",
|
|
26
|
+
starting: "啟動中", queued: "等待啟動", title: "組織圖", general: "General", unteamed: "未加入 team", members: "{0} 名成員", member: "1 名成員", instances: "{0} 個 instance", instance: "1 個 instance",
|
|
27
27
|
needs: "等你回應", working: "工作中", idle: "閒置", stuck: "似乎卡住", paused: "已暫停", stopped: "已停止", crashed: "已當掉",
|
|
28
28
|
missing: "不在 fleet 中", thread_discord: "Discord 討論串", thread_telegram: "Telegram topic", openChat: "開啟聊天",
|
|
29
29
|
classic: "ClassicBot 頻道", empty: "還沒有 instance", emptyHint: "fleet 有 instance 和 team 後會顯示在這裡。",
|
|
@@ -35,8 +35,8 @@ const to = (k, ...v) => t(`org.${k}`, ...v);
|
|
|
35
35
|
const count = (one, many, n) => (n === 1 ? to(one) : to(many, n));
|
|
36
36
|
|
|
37
37
|
/** The states a node can show, most pressing first (a team's summary lists them in this order). */
|
|
38
|
-
export const ORG_STATES = ["needs", "crashed", "stuck", "working", "idle", "paused", "stopped", "missing"];
|
|
39
|
-
const DOT = { needs: "warn", crashed: "bad", stuck: "warn", working: "busy", idle: "ok", paused: "off", stopped: "off", missing: "off" };
|
|
38
|
+
export const ORG_STATES = ["needs", "crashed", "stuck", "starting", "queued", "working", "idle", "paused", "stopped", "missing"];
|
|
39
|
+
const DOT = { starting: "busy", queued: "off", needs: "warn", crashed: "bad", stuck: "warn", working: "busy", idle: "ok", paused: "off", stopped: "off", missing: "off" };
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
42
|
* One instance's live state, from the app store: `i` its status entry (null: the fleet does not list it), `exec` the
|
|
@@ -46,6 +46,8 @@ const DOT = { needs: "warn", crashed: "bad", stuck: "warn", working: "busy", idl
|
|
|
46
46
|
export function orgState(i, exec, awaiting, needs) {
|
|
47
47
|
if (!i) return "missing";
|
|
48
48
|
if (awaiting != null || needs > 0) return "needs";
|
|
49
|
+
if (i.startup_phase === "queued") return "queued";
|
|
50
|
+
if (i.startup_phase === "starting") return "starting";
|
|
49
51
|
if (i.status === "crashed") return "crashed";
|
|
50
52
|
if (i.status === "paused") return "paused";
|
|
51
53
|
if (i.status !== "running") return "stopped";
|
|
@@ -54,6 +56,17 @@ export function orgState(i, exec, awaiting, needs) {
|
|
|
54
56
|
return "idle";
|
|
55
57
|
}
|
|
56
58
|
|
|
59
|
+
/**
|
|
60
|
+
* What a node's card says (fleet-canvas.js; the Office view's desk too, fleet-office.js): its names, its state and
|
|
61
|
+
* backend (`sub`), and all of it in one line (`full`, the card's title and label).
|
|
62
|
+
*/
|
|
63
|
+
export function nodeText(n) {
|
|
64
|
+
const it = n.i;
|
|
65
|
+
const names = it ? namesOf(it) : { primary: n.name, secondary: "" };
|
|
66
|
+
const sub = it ? `${to(n.state)} · ${backendLabel(it.backend)}` : to(n.state);
|
|
67
|
+
return { primary: names.primary, secondary: names.secondary, sub, full: `${names.primary}${names.secondary ? ` (${names.secondary})` : ""} — ${sub}` };
|
|
68
|
+
}
|
|
69
|
+
|
|
57
70
|
/**
|
|
58
71
|
* The chart: the structure from /ui/org, the nodes and their states from the app store. General on top; each team with
|
|
59
72
|
* its members in fleet.yaml's order (an instance in two teams shows in both: that is its place in each); then the
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<meta name="referrer" content="no-referrer">
|
|
7
|
+
<meta name="robots" content="noindex">
|
|
8
|
+
<title>AgEnD</title>
|
|
9
|
+
<!--app-icon-->
|
|
10
|
+
<link rel="icon" href="/favicon.ico" sizes="16x16 32x32 64x64">
|
|
11
|
+
<link rel="icon" href="/favicon.svg" type="image/svg+xml" sizes="any">
|
|
12
|
+
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180">
|
|
13
|
+
<!--/app-icon-->
|
|
14
|
+
<script src="/assets/theme.js"></script>
|
|
15
|
+
<link rel="stylesheet" href="/assets/tokens.css">
|
|
16
|
+
<style>
|
|
17
|
+
/* What a guest's browser gets for a page it cannot open (#2399 smoke: it was the gate's bare JSON). The sign-in
|
|
18
|
+
page's look: the app's tokens, light and dark (theme.js before paint). */
|
|
19
|
+
* { box-sizing: border-box; }
|
|
20
|
+
body { margin: 0; min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 16px;
|
|
21
|
+
background: var(--bg); color: var(--text); font: var(--fw-regular) var(--fs-md)/var(--lh-md) var(--font-ui); -webkit-font-smoothing: antialiased; }
|
|
22
|
+
main { width: 100%; max-width: 380px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg); padding: 28px 24px; }
|
|
23
|
+
.brand { margin: 0 0 18px; font-size: var(--fs-lg); font-weight: var(--fw-bold); }
|
|
24
|
+
h1 { margin: 0 0 6px; font-size: var(--fs-xl); font-weight: var(--fw-semibold); }
|
|
25
|
+
p { margin: 0 0 18px; color: var(--text-2); }
|
|
26
|
+
a.go { display: block; width: 100%; padding: 12px; text-align: center; text-decoration: none; font: var(--fw-semibold) var(--fs-md) var(--font-ui);
|
|
27
|
+
color: var(--on-accent); background: var(--accent-fill); border-radius: var(--r-full); }
|
|
28
|
+
a.go:hover { opacity: .88; }
|
|
29
|
+
a.go:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-dim); }
|
|
30
|
+
</style>
|
|
31
|
+
</head>
|
|
32
|
+
<body>
|
|
33
|
+
<main id="refused" data-why="__WHY__">
|
|
34
|
+
<div class="brand">AgEnD</div>
|
|
35
|
+
<h1 id="title">This page is not open to guests</h1>
|
|
36
|
+
<p id="lead">This page is for the fleet's members. As a guest you can use the chats shared with you.</p>
|
|
37
|
+
<a id="go" class="go" href="/ui">Open your chats</a>
|
|
38
|
+
</main>
|
|
39
|
+
<script src="/assets/guest-refused.js"></script>
|
|
40
|
+
</body>
|
|
41
|
+
</html>
|
|
@@ -83,12 +83,18 @@ function ImageLightbox({ items, index, opener, onClose }) {
|
|
|
83
83
|
zoom.current = z;
|
|
84
84
|
return () => { z.destroy(); zoom.current = null; };
|
|
85
85
|
}, []);
|
|
86
|
-
// Another image: hidden until it has loaded and been fitted (its load resets the zoom).
|
|
86
|
+
// Another image: hidden until it has loaded and been fitted (its load resets the zoom). A layout effect — in the same
|
|
87
|
+
// commit that set the new src, before any load can arrive: a picture already in the cache (every thumbnail above it
|
|
88
|
+
// has loaded) fires its load before a passive effect would run, and such an effect then hid it for good with the
|
|
89
|
+
// level at "…" (the report on beta.24: Next "flashes once, then the next image is not visible"). A picture that is
|
|
90
|
+
// already complete here (the same file again: no load comes) is fitted at once.
|
|
87
91
|
const first = useRef(true);
|
|
88
|
-
|
|
92
|
+
useLayoutEffect(() => {
|
|
89
93
|
if (first.current) { first.current = false; return; }
|
|
90
|
-
|
|
94
|
+
const el = img.current;
|
|
95
|
+
if (el) el.classList.remove("lb-ready");
|
|
91
96
|
setLevel(null);
|
|
97
|
+
if (el && el.complete && el.naturalWidth > 0 && zoom.current) zoom.current.reset();
|
|
92
98
|
}, [at]);
|
|
93
99
|
useEffect(() => {
|
|
94
100
|
const d = ref.current;
|
package/dist/ui/panel-chat.js
CHANGED
|
@@ -122,7 +122,9 @@ export async function setPreviewOptIn(on, commit = (apply) => apply()) {
|
|
|
122
122
|
export function boot({ stream, boot: bootData, deps = {} }) {
|
|
123
123
|
if (store) return store;
|
|
124
124
|
store = createChatStore({
|
|
125
|
-
fetch: (...a) => fetch(...a), toast, announce, t, setTimeout: (fn, ms) => setTimeout(fn, ms), confirm: confirmDialog,
|
|
125
|
+
fetch: (...a) => fetch(...a), toast, announce, t, setTimeout: (fn, ms) => setTimeout(fn, ms), confirm: confirmDialog,
|
|
126
|
+
XMLHttpRequest: typeof XMLHttpRequest === "function" ? XMLHttpRequest : undefined, // an upload's progress (chat-store.js)
|
|
127
|
+
...deps,
|
|
126
128
|
});
|
|
127
129
|
emojiIndex = createEmojiIndex((url) => (deps.fetch || fetch)(url, { credentials: "same-origin" }));
|
|
128
130
|
mentionIndex = createMentionIndex((url) => (deps.fetch || fetch)(url, { credentials: "same-origin" }));
|
|
@@ -187,6 +189,10 @@ function useChatTick(name, kinds) {
|
|
|
187
189
|
|
|
188
190
|
/** #1722: the chat's title — the name that leads, the other on hover. */
|
|
189
191
|
const headerName = ({ primary, secondary }) => (secondary ? html`<span title=${secondary}>${primary}</span>` : primary);
|
|
192
|
+
/** #2478: who the composer writes to — the display name, else the instance's name (a long id made the box two lines). */
|
|
193
|
+
const composerLabel = (inst, name) => (inst && typeof inst.display_name === "string" && inst.display_name.trim()) || name;
|
|
194
|
+
/** #2478: a touch screen (a coarse pointer) — Return is a new line there and only the Send button sends. Asked each time. */
|
|
195
|
+
const touchScreen = () => typeof matchMedia === "function" && matchMedia("(pointer: coarse)").matches;
|
|
190
196
|
|
|
191
197
|
export function ChatPanel({ route, navKey }) {
|
|
192
198
|
const lease = useLease(navKey);
|
|
@@ -425,6 +431,9 @@ function threadEntry(name, doc) {
|
|
|
425
431
|
canTryEffect: x => getAppEffects()?.canTry(x) === true,
|
|
426
432
|
startEffectTrial: x => { const seq = navStore.get().seq; void getAppEffects()?.startTrial(x).then(ok => { if (!ok && e.sink && navStore.get().seq === seq && document.hasFocus?.() === true && document.visibilityState === "visible") toast(t("chat.effectTrialUnavailable")); }); },
|
|
427
433
|
clickReplyButton: (id, index) => store.clickReplyButton(name, id, index), // #1266
|
|
434
|
+
retryOutgoing: (clientId) => store.retryOutgoing(name, clientId), // a failed send's Retry and Delete
|
|
435
|
+
deleteOutgoing: (clientId) => store.deleteOutgoing(name, clientId),
|
|
436
|
+
putBackOutgoing: (clientId) => { recordEdit(composerField()); store.putBackOutgoing(name, clientId); }, // undoable, as typing
|
|
428
437
|
// #2272: reply to a message (the bar above the composer), and show the one a reply quotes.
|
|
429
438
|
startReply: (x) => { store.startReply(name, x); doc.getElementById("msgIn")?.focus(); },
|
|
430
439
|
findQuoted: (key) => store.findQuoted(name, key),
|
|
@@ -446,12 +455,13 @@ function follow(name, thread) {
|
|
|
446
455
|
// #1682: the same for mention names — asked for the ids the messages carry; when names arrive, drawn again.
|
|
447
456
|
const wantEmojis = (msgs) => { if (emojiIndex && emojiIndex.wants(msgs)) emojiIndex.ensure(); if (mentionIndex) mentionIndex.ensure(msgs); };
|
|
448
457
|
const offs = [];
|
|
449
|
-
if (emojiIndex) offs.push(emojiIndex.subscribe(() => thread.render(store.
|
|
450
|
-
if (mentionIndex) offs.push(mentionIndex.subscribe(() => thread.render(store.
|
|
458
|
+
if (emojiIndex) offs.push(emojiIndex.subscribe(() => thread.render(store.threadRows(name))));
|
|
459
|
+
if (mentionIndex) offs.push(mentionIndex.subscribe(() => thread.render(store.threadRows(name))));
|
|
451
460
|
wantEmojis(store.state.msgs[name]);
|
|
452
461
|
offs.push(store.subscribe((instance, kind) => {
|
|
453
462
|
if (instance !== name) return;
|
|
454
|
-
if (kind === "msgs") { thread.render(store.
|
|
463
|
+
if (kind === "msgs") { thread.render(store.threadRows(name)); wantEmojis(store.state.msgs[name]); }
|
|
464
|
+
else if (kind === "progress") thread.progress(); // an upload moved: its bar, in place
|
|
455
465
|
else if (kind === "sent") thread.jumpLatest();
|
|
456
466
|
}));
|
|
457
467
|
offs.push(P() ? P().onChange(() => thread.refreshCards()) : () => {});
|
|
@@ -461,10 +471,10 @@ function follow(name, thread) {
|
|
|
461
471
|
const next = getAppEffects();
|
|
462
472
|
if (next === trialOwner) return;
|
|
463
473
|
trialOff?.(); trialOff = null; trialOwner = next;
|
|
464
|
-
trialOff = next?.subscribeTrial(() => thread.render(store.
|
|
474
|
+
trialOff = next?.subscribeTrial(() => thread.render(store.threadRows(name))) ?? null;
|
|
465
475
|
};
|
|
466
476
|
watchTrial();
|
|
467
|
-
offs.push(onEffectsAwake(() => { watchTrial(); thread.render(store.
|
|
477
|
+
offs.push(onEffectsAwake(() => { watchTrial(); thread.render(store.threadRows(name)); }));
|
|
468
478
|
offs.push(() => { trialOff?.(); trialOff = null; trialOwner = null; });
|
|
469
479
|
offs.push(panelStore.subscribe(() => thread.refreshCards())); // a card shows whether the panel has its block
|
|
470
480
|
return () => { for (const off of offs) off(); };
|
|
@@ -493,7 +503,7 @@ function Thread({ name, th }) {
|
|
|
493
503
|
thread.attach(sc);
|
|
494
504
|
th.current = thread;
|
|
495
505
|
// Up to date, at the reader's place (the bottom unless they had scrolled up); a kept one's cards say what they can do.
|
|
496
|
-
thread.render(store.
|
|
506
|
+
thread.render(store.threadRows(name), { restore: store.state.scrollMemo[name] ?? null });
|
|
497
507
|
if (kept) thread.refreshCards();
|
|
498
508
|
const unfollow = follow(name, thread);
|
|
499
509
|
const onScroll = () => { store.state.scrollMemo[name] = thread.onScroll(); };
|
|
@@ -546,15 +556,10 @@ function Dock({ name, inst, lease, exec, awaiting, workingLabel }) {
|
|
|
546
556
|
<${WorkBar} name=${name} lease=${lease} exec=${exec} awaiting=${awaiting} workingLabel=${workingLabel} />
|
|
547
557
|
<${CommandCard} name=${name} lease=${lease} />
|
|
548
558
|
<${QuickActions} name=${name} inst=${inst} lease=${lease} />
|
|
549
|
-
${s.failedSends[name] ? html`<div class="failed-send" role="alert"><span>${t("chat.notSent")}</span><span class="txt">${s.failedSends[name]}</span>
|
|
550
|
-
<button type="button" class="btn btn-primary btn-sm" onClick=${() => { recordEdit(composerField()); store.putBack(name); }}>${t("chat.putBack")}</button>
|
|
551
|
-
<button type="button" class="btn btn-sm" onClick=${() => store.discardFailed(name)}>${t("chat.discard")}</button></div>` : null}
|
|
552
559
|
${s.waking[name] ? html`<p class="send-waking" role="status">${t("chat.waking", name)}</p>` : null}
|
|
553
|
-
|
|
554
|
-
<button type="button" class="btn btn-sm" disabled=${!store.canRetrySend(name)} onClick=${() => store.retrySend(name)}>${t("chat.retrySend")}</button></div>` : null}
|
|
555
|
-
<${Composer} name=${name} lease=${lease} busy=${R().isBusy(exec)} />
|
|
560
|
+
<${Composer} name=${name} label=${composerLabel(inst, name)} lease=${lease} busy=${R().isBusy(exec)} />
|
|
556
561
|
<div class="composer-row">${guestPage() ? html`<span class="guest-note" title=${t("chat.guestNote")}>${t("chat.guestNote")}</span>` : html`<${ComposerAuth} />`}<span
|
|
557
|
-
class="hint" title=${t(guestPage() ? "chat.guestMore" : "chat.composerMore")}>${t("chat.composerHint")}</span></div>
|
|
562
|
+
class="hint" title=${t(guestPage() ? "chat.guestMore" : "chat.composerMore")}>${t(touchScreen() ? "chat.composerHintTouch" : "chat.composerHint")}</span></div>
|
|
558
563
|
</div></div>`;
|
|
559
564
|
}
|
|
560
565
|
|
|
@@ -637,7 +642,7 @@ function replySnippet(r) {
|
|
|
637
642
|
return text || (r.media === "photo" ? t("chat.replyPhoto") : r.media === "document" ? t("chat.replyFile") : "");
|
|
638
643
|
}
|
|
639
644
|
|
|
640
|
-
function Composer({ name, lease, busy }) {
|
|
645
|
+
function Composer({ name, label = name, lease, busy }) {
|
|
641
646
|
const s = store.state;
|
|
642
647
|
const input = useRef(null), fileIn = useRef(null);
|
|
643
648
|
const [, tick] = useState(0);
|
|
@@ -652,8 +657,13 @@ function Composer({ name, lease, busy }) {
|
|
|
652
657
|
const guest = guestPage();
|
|
653
658
|
const palette = guest || files.length || closedFor === value ? null : paletteFor(value);
|
|
654
659
|
const pick = palette && palette.length ? palette[Math.min(sel, palette.length - 1)] : null;
|
|
655
|
-
// Grow with the text, up to a limit: set through the CSSOM, never a style attribute (#1300).
|
|
656
|
-
|
|
660
|
+
// Grow with the text, up to a limit: set through the CSSOM, never a style attribute (#1300). Empty, it is one line
|
|
661
|
+
// (#2478 — a long name's placeholder made the box two lines tall).
|
|
662
|
+
useLayoutEffect(() => {
|
|
663
|
+
const el = input.current; if (!el) return;
|
|
664
|
+
el.style.height = "auto";
|
|
665
|
+
if (value) el.style.height = `${Math.min(el.scrollHeight, 240)}px`;
|
|
666
|
+
}, [value]);
|
|
657
667
|
useEffect(() => { if (input.current) input.current.focus(); }, [name]);
|
|
658
668
|
useEffect(() => { refreshTourSpot(); });
|
|
659
669
|
const setValue = (v) => { store.setDraft(name, v); setSel(0); tick(n => n + 1); };
|
|
@@ -688,7 +698,7 @@ function Composer({ name, lease, busy }) {
|
|
|
688
698
|
if (pick && e.key === "Tab" && !e.shiftKey) { e.preventDefault(); complete(pick); return; }
|
|
689
699
|
if (palette && e.key === "Escape" && !e.isComposing) { e.preventDefault(); setClosedFor(value); return; }
|
|
690
700
|
if (reply && e.key === "Escape" && !e.isComposing) { e.preventDefault(); store.clearReply(name); return; } // #2272
|
|
691
|
-
if (R().composerKey(e) === "send") { e.preventDefault(); submit(); }
|
|
701
|
+
if (R().composerKey(e, { touch: touchScreen() }) === "send") { e.preventDefault(); submit(); }
|
|
692
702
|
};
|
|
693
703
|
const onInput = (e) => { setValue(e.target.value); };
|
|
694
704
|
// Files in a paste are attached; a very long text paste goes as a text file — only once it is really attached.
|
|
@@ -716,12 +726,15 @@ function Composer({ name, lease, busy }) {
|
|
|
716
726
|
<button id="attachBtn" type="button" class="icon-btn" title=${t("chat.attachTitle")} aria-label=${t("chat.attachTitle")} onClick=${() => fileIn.current && fileIn.current.click()}><${Icon} name="attach" /></button>
|
|
717
727
|
<input ref=${fileIn} type="file" multiple hidden
|
|
718
728
|
onChange=${(e) => { store.addFiles(name, e.target.files); e.target.value = ""; }} />
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
729
|
+
${/* #2478: the placeholder is drawn beside the box, one line ending in "…" — a textarea's own placeholder is clipped,
|
|
730
|
+
never ellipsized, and a long name wrapped it to two lines. The box keeps the words as its accessible name. */ ""}
|
|
731
|
+
<span class="composer-field"><textarea id="msgIn" ref=${input} rows="1" value=${value} aria-label=${t("chat.composerPlaceholder", label)}
|
|
732
|
+
enterkeyhint=${touchScreen() ? "enter" : undefined} autocomplete="off" aria-controls=${palette ? "cmdPalette" : undefined} aria-expanded=${palette ? "true" : "false"}
|
|
733
|
+
aria-activedescendant=${pick ? optId(pick) : undefined} onInput=${onInput} onKeyDown=${onKeyDown} onPaste=${onPaste}></textarea>${value ? null
|
|
734
|
+
: html`<span class="composer-ph" aria-hidden="true">${t("chat.composerPlaceholder", label)}</span>`}</span>
|
|
722
735
|
<button id="stopBtn" type="button" class="btn btn-stop" hidden=${!busy} title=${t("chat.stopReplyTitle")}
|
|
723
736
|
disabled=${!!s.cancelling[name]} onClick=${() => store.cancelReply(name)}><${Icon} name="stop" size=${14} /><span class="stop-label">${t("chat.stopReply")}</span></button>
|
|
724
|
-
<button id="sendBtn" type="button" class="btn btn-primary btn-send" hidden=${busy && !content} disabled=${!content
|
|
737
|
+
<button id="sendBtn" type="button" class="btn btn-primary btn-send" hidden=${busy && !content} disabled=${!content}
|
|
725
738
|
onMouseDown=${(e) => e.preventDefault()}
|
|
726
739
|
aria-label=${t("chat.send")} title=${t("chat.send")} onClick=${submit}><${Icon} name="send" size=${16} /><span class="send-label">${t("chat.send")}</span></button>
|
|
727
740
|
</div></div>`;
|
|
@@ -25,8 +25,8 @@ import "./settings-strings.js";
|
|
|
25
25
|
import { api, attach, confirmedWrite, newKey } from "./settings-confirm.js";
|
|
26
26
|
import { dismissOperation, operationActive, restartFleet, startOperation, takeLeftover } from "./settings-apply.js";
|
|
27
27
|
import {
|
|
28
|
-
ACCESS_MODES, AGENT_MODES, DEFAULT_SCHEMA, HANG_TIMEOUT_DEFAULT_MINUTES, LOG_LEVELS, TOOL_PROGRESS, TOOL_SETS, VISIBILITY_MODES, batchImpact,
|
|
29
|
-
changedFields, channelId, channelsOf, chLabel, connectionState, effectiveSummary, fleetModel, fromYaml, fullModelRequests, groupAgents,
|
|
28
|
+
ACCESS_MODES, AGENT_MODES, DASHBOARD_FEATURES, DEFAULT_SCHEMA, HANG_TIMEOUT_DEFAULT_MINUTES, LOG_LEVELS, TOOL_PROGRESS, TOOL_SETS, VISIBILITY_MODES, batchImpact,
|
|
29
|
+
changedFields, channelId, channelsOf, chLabel, connectionState, dashboardDefault, effectiveSummary, fleetModel, fromYaml, fullModelRequests, groupAgents,
|
|
30
30
|
hasOwn, impactOf, localeOptions, defaultsImpact, requestImpact, nonNegative, positive, replyGuardSupported, shortName, STATUS_ORDER, toYaml,
|
|
31
31
|
toolSetDefault, visibilityDefault, worstImpact,
|
|
32
32
|
} from "./settings-model.js";
|
|
@@ -678,6 +678,7 @@ function General({ ctx }) {
|
|
|
678
678
|
backend: d.backend || "claude-code", model: d.model || "", locale: d.locale || "", auto: String(d.auto_pause_after ?? 0),
|
|
679
679
|
agent_mode: d.agent_mode || "mcp", tool_set: TOOL_SETS.includes(d.tool_set) ? d.tool_set : "", tool_progress: d.tool_progress ?? "off",
|
|
680
680
|
visibility: visibilityDefault(d), reply: d.reply_completion_guard ?? true, log_level: d.log_level || "info",
|
|
681
|
+
dash: Object.fromEntries(DASHBOARD_FEATURES.map((k) => [k, dashboardDefault(d, k)])),
|
|
681
682
|
hangOn: hang.enabled !== false, hang: String(hang.timeout_minutes ?? HANG_TIMEOUT_DEFAULT_MINUTES),
|
|
682
683
|
concurrency: String(startup.concurrency ?? 10), stagger: String(startup.stagger_delay_ms ?? 500),
|
|
683
684
|
classicBackend: c.backend || d.backend || "claude-code",
|
|
@@ -701,12 +702,15 @@ function General({ ctx }) {
|
|
|
701
702
|
backend: f.backend, model: f.model || undefined, locale: f.locale || null, auto_pause_after: num(f.auto),
|
|
702
703
|
agent_mode: f.agent_mode, tool_set: f.tool_set || null, tool_progress: f.tool_progress, cross_instance_visibility: f.visibility,
|
|
703
704
|
reply_completion_guard: f.reply, log_level: f.log_level,
|
|
705
|
+
// The dashboard features every agent may use unless its own setting says otherwise (all three, as shown).
|
|
706
|
+
dashboard: f.dash,
|
|
704
707
|
hang_detector: { enabled: f.hangOn, timeout_minutes: num(f.hang) },
|
|
705
708
|
startup: { concurrency: num(f.concurrency), stagger_delay_ms: num(f.stagger) },
|
|
706
709
|
}, {
|
|
707
710
|
backend: d.backend || "claude-code", model: d.model || undefined, locale: d.locale || null, auto_pause_after: d.auto_pause_after ?? 0,
|
|
708
711
|
agent_mode: d.agent_mode || "mcp", tool_set: TOOL_SETS.includes(d.tool_set) ? d.tool_set : null, tool_progress: d.tool_progress ?? "off",
|
|
709
712
|
cross_instance_visibility: visibilityDefault(d), reply_completion_guard: d.reply_completion_guard ?? true, log_level: d.log_level || "info",
|
|
713
|
+
dashboard: Object.fromEntries(DASHBOARD_FEATURES.map((k) => [k, dashboardDefault(d, k)])),
|
|
710
714
|
hang_detector: { enabled: hang.enabled !== false, timeout_minutes: hang.timeout_minutes ?? HANG_TIMEOUT_DEFAULT_MINUTES },
|
|
711
715
|
startup: { concurrency: startup.concurrency ?? 10, stagger_delay_ms: startup.stagger_delay_ms ?? 500 },
|
|
712
716
|
});
|
|
@@ -751,6 +755,11 @@ function General({ ctx }) {
|
|
|
751
755
|
<div class="field"><label class="check"><input type="checkbox" checked=${f.reply} onChange=${(e) => set("reply")(e.target.checked)} /><span>${tn("replyCompletionGuard")}</span><${Impact} schema=${schema} field="defaults.reply_completion_guard" /></label>
|
|
752
756
|
<p class="note">${fleetReply}</p></div>
|
|
753
757
|
</div>
|
|
758
|
+
<fieldset class="field dash-perms"><legend>${L(tn("dashboardAccess"), "defaults.dashboard")}</legend>
|
|
759
|
+
<p class="note">${tn("dashboardDefaultsHint")}</p>
|
|
760
|
+
${DASHBOARD_FEATURES.map((k) => html`<label key=${k} class="check"><input type="checkbox" checked=${f.dash[k]}
|
|
761
|
+
onChange=${(e) => setF((x) => ({ ...x, dash: { ...x.dash, [k]: e.target.checked } }))} /><span>${tn(`dashboard_${k}`)}</span></label>`)}
|
|
762
|
+
</fieldset>
|
|
754
763
|
</section>
|
|
755
764
|
<section class="card"><h3>ClassicBot</h3>
|
|
756
765
|
<div class="grid2">
|
|
@@ -814,7 +823,7 @@ function WebSection({ ctx, staged }) {
|
|
|
814
823
|
echo: web.echo_to_channel ?? true,
|
|
815
824
|
log: web.log_tab === true, // #1218: off by default
|
|
816
825
|
usagePub: web.usage_report_on_public_link === true, // #2122: off by default
|
|
817
|
-
pub: { allow_public: (web.public_link && web.public_link.allow_public) ?? true, ttl_minutes: (web.public_link && web.public_link.ttl_minutes) ?? 120, protocol: (web.public_link && web.public_link.protocol) ?? "http2", hostname: (web.public_link && web.public_link.hostname) ?? "", tunnel_token_env: (web.public_link && web.public_link.tunnel_token_env) ?? "", origin_port: (web.public_link && web.public_link.origin_port) ?? 19282 },
|
|
826
|
+
pub: { allow_public: (web.public_link && web.public_link.allow_public) ?? true, ttl_minutes: (web.public_link && web.public_link.ttl_minutes) ?? 120, protocol: (web.public_link && web.public_link.protocol) ?? "http2", hostname: (web.public_link && web.public_link.hostname) ?? "", tunnel_token_env: (web.public_link && web.public_link.tunnel_token_env) ?? "", origin_port: (web.public_link && web.public_link.origin_port) ?? 19282, auto_open: (web.public_link && web.public_link.auto_open) ?? false },
|
|
818
827
|
session: { absolute_hours: local.absolute_hours ?? null, idle_hours: local.idle_hours ?? null },
|
|
819
828
|
terminal: { enabled: term.enabled ?? null, ttl_minutes: term.ttl_minutes ?? null },
|
|
820
829
|
}), []);
|
|
@@ -829,7 +838,7 @@ function WebSection({ ctx, staged }) {
|
|
|
829
838
|
return {
|
|
830
839
|
echo: pick(pending, "echo_to_channel", original.echo), log: pick(pending, "log_tab", original.log), usagePub: pick(pending, "usage_report_on_public_link", original.usagePub),
|
|
831
840
|
pub: pick(pub, "allow_public", original.pub.allow_public), ttl: String(pick(pub, "ttl_minutes", original.pub.ttl_minutes)), protocol: pick(pub, "protocol", original.pub.protocol),
|
|
832
|
-
hostname: pick(pub, "hostname", original.pub.hostname), originPort: String(pick(pub, "origin_port", original.pub.origin_port)),
|
|
841
|
+
hostname: pick(pub, "hostname", original.pub.hostname), originPort: String(pick(pub, "origin_port", original.pub.origin_port)), autoOpen: pick(pub, "auto_open", original.pub.auto_open),
|
|
833
842
|
abs: text(pick(session, "absolute_hours", original.session.absolute_hours)), idle: text(pick(session, "idle_hours", original.session.idle_hours)),
|
|
834
843
|
termOn: pick(terminal, "enabled", original.terminal.enabled) !== false, termTtl: text(pick(terminal, "ttl_minutes", original.terminal.ttl_minutes)),
|
|
835
844
|
};
|
|
@@ -842,10 +851,15 @@ function WebSection({ ctx, staged }) {
|
|
|
842
851
|
// The defaults (#1670: 48 h / 48 h, DEFAULT_SESSION_POLICY); an idle time left blank follows a shorter session, as
|
|
843
852
|
// the store and the validator apply it — so only an idle time that is typed can exceed the session length.
|
|
844
853
|
const abs = value(f.abs) ?? 48, idle = value(f.idle) ?? Math.min(48, abs);
|
|
854
|
+
// Named mode: a hostname is typed (the server judges the merged result the same way).
|
|
855
|
+
const named = String(f.hostname || "").trim() !== "";
|
|
845
856
|
const errors = {
|
|
846
857
|
hostname: f.hostname && !/^(?:https:\/\/)?[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+\/?$/.test(f.hostname) ? "namedHostnameInvalid" : "",
|
|
847
858
|
originPort: !Number.isInteger(Number(f.originPort)) || Number(f.originPort) < 1024 || Number(f.originPort) > 65535 ? "namedPortInvalid" : "",
|
|
848
|
-
ttl:
|
|
859
|
+
// #named-link-ttl: a fixed hostname (named tunnel) allows up to 7 days, or 0 = no expiry; a quick tunnel 1–480.
|
|
860
|
+
ttl: String(f.ttl).trim() === "" || !Number.isInteger(Number(f.ttl)) || Number(f.ttl) < (named ? 0 : 1) || Number(f.ttl) > (named ? 10080 : 480)
|
|
861
|
+
? (named ? "publicTtlNamedRange" : "publicTtlQuickRange") : "",
|
|
862
|
+
autoOpen: f.autoOpen && !named ? "publicAutoOpenNeedsHostname" : "",
|
|
849
863
|
abs: hours(f.abs),
|
|
850
864
|
idle: hours(f.idle) || (!hours(f.abs) && idle > abs ? "sessionIdleOver" : ""),
|
|
851
865
|
termTtl: value(f.termTtl) !== null && (!Number.isInteger(value(f.termTtl)) || value(f.termTtl) < 1 || value(f.termTtl) > 20) ? "webTerminalTtlRange" : "",
|
|
@@ -858,7 +872,7 @@ function WebSection({ ctx, staged }) {
|
|
|
858
872
|
// reference adopts the standard env key; mounting or editing an unrelated field never rewrites credentials.
|
|
859
873
|
const tokenEnv = f.hostname === original.pub.hostname ? original.pub.tunnel_token_env
|
|
860
874
|
: f.hostname ? original.pub.tunnel_token_env || "AGEND_CLOUDFLARE_TUNNEL_TOKEN" : "";
|
|
861
|
-
const publicPatch = changedFields({ allow_public: f.pub, ttl_minutes: Number(f.ttl), protocol: f.protocol, hostname: f.hostname, tunnel_token_env: tokenEnv, origin_port: Number(f.originPort) }, original.pub);
|
|
875
|
+
const publicPatch = changedFields({ allow_public: f.pub, ttl_minutes: Number(f.ttl), protocol: f.protocol, hostname: f.hostname, tunnel_token_env: tokenEnv, origin_port: Number(f.originPort), auto_open: f.autoOpen }, original.pub);
|
|
862
876
|
const sessionPatch = changedFields({ absolute_hours: value(f.abs), idle_hours: value(f.idle) }, original.session);
|
|
863
877
|
// The checkbox shows the default (on) for an unset key: only a real toggle is written; on again = back to the default.
|
|
864
878
|
const terminalPatch = changedFields({ enabled: f.termOn === (original.terminal.enabled !== false) ? original.terminal.enabled : f.termOn ? null : false,
|
|
@@ -932,9 +946,12 @@ function WebSection({ ctx, staged }) {
|
|
|
932
946
|
</div>
|
|
933
947
|
<p class="note">${tn("namedTunnelHint")} <a href="https://github.com/songsid/AgEnD/blob/main/docs/named-tunnel.md" target="_blank" rel="noopener noreferrer">${tn("namedTunnelSetup")}</a></p>
|
|
934
948
|
<div class="grid2">
|
|
935
|
-
<div class="field"><label for="publicWebTtl">${tn("publicTtl")}</label><input id="publicWebTtl" type="number" min
|
|
949
|
+
<div class="field"><label for="publicWebTtl">${tn(named ? "publicTtlNamed" : "publicTtl")}</label><input id="publicWebTtl" type="number" min=${named ? "0" : "1"} max=${named ? "10080" : "480"} step="1" class=${errors.ttl ? "invalid" : ""} value=${f.ttl} onInput=${(e) => set("ttl")(e.target.value)} /><${Feedback} error=${errors.ttl} /></div>
|
|
936
950
|
<div class="field"><label for="publicWebProtocol">${tn("publicProtocol")}</label><${Select} id="publicWebProtocol" value=${f.protocol} onChange=${set("protocol")} options=${["http2", "quic", "auto"]} /></div>
|
|
937
951
|
</div>
|
|
952
|
+
${named ? html`<p class="note" id="publicTtlNamedHint">${tn("publicTtlNamedHint")}</p>` : null}
|
|
953
|
+
${named || f.autoOpen ? html`<label class="check"><input id="publicAutoOpen" type="checkbox" checked=${f.autoOpen} onChange=${(e) => set("autoOpen")(e.target.checked)} /><span>${tn("publicAutoOpen")}</span><${Impact} schema=${schema} field="web.public_link.auto_open" /></label>
|
|
954
|
+
<p class="note">${tn("publicAutoOpenHint")}</p><${Feedback} error=${errors.autoOpen} />` : null}
|
|
938
955
|
</section>
|
|
939
956
|
</fieldset>
|
|
940
957
|
${overPublicLink ? null : html`<${PublicTunnelToken} /><${GuestsCard} ctx=${ctx} staged=${staged} /><${WebAuthorityCard} ctx=${ctx} staged=${staged} />`}
|
|
@@ -17,9 +17,9 @@ import { startOperation } from "./settings-apply.js";
|
|
|
17
17
|
import "./settings-strings.js"; // its words, wherever a picker of this file is used (#2072: Fleet's create dialog too)
|
|
18
18
|
import { backendChoices, modelChoices, OTHER_MODEL, useBackendRows, useModelCatalog } from "./settings-catalog.js";
|
|
19
19
|
import {
|
|
20
|
-
ACCESS_MODES, AGENT_MODES, CH_TYPES, HANG_TIMEOUT_DEFAULT_MINUTES, LOG_LEVELS, TOOL_PROGRESS, TOOL_SETS,
|
|
20
|
+
ACCESS_MODES, AGENT_MODES, CH_TYPES, DASHBOARD_CHOICES, DASHBOARD_FEATURES, HANG_TIMEOUT_DEFAULT_MINUTES, LOG_LEVELS, TOOL_PROGRESS, TOOL_SETS,
|
|
21
21
|
VISIBILITY_MODES, accessConfirmations, agentForm, agentPatch, backendOptions, changedFields, channelId, channelsOf,
|
|
22
|
-
chLabel, hasOwn, impactOf, newConnectionImpact, nonNegative, nonNegativeInteger, positive, replyGuardSupported, sameValue, visibilityDefault,
|
|
22
|
+
chLabel, dashboardDefault, hasOwn, impactOf, newConnectionImpact, nonNegative, nonNegativeInteger, positive, replyGuardSupported, sameValue, visibilityDefault,
|
|
23
23
|
classicWebEchoEffective, toolSetDefault, worstImpact,
|
|
24
24
|
} from "./settings-model.js";
|
|
25
25
|
/** #2342: a field by id, for recordEdit before a pick or a reset writes it. */
|
|
@@ -287,6 +287,15 @@ export function AgentDialog({ name, inst, ctx, onClose }) {
|
|
|
287
287
|
<${Select} id=${`ag-${k}`} value=${shown(k, f[k], dflt)} disabled=${f.inherit[k]} onChange=${set(k)}
|
|
288
288
|
options=${opts.includes(shown(k, f[k], dflt)) || k === "cross_instance_visibility" ? opts : [shown(k, f[k], dflt), ...opts]} /></${Override}>
|
|
289
289
|
${k === "cross_instance_visibility" ? html`<p class="note">${tn("crossInstanceVisibilityHint")}</p>` : null}</div>`)}
|
|
290
|
+
<fieldset class="field dash-perms"><legend>${L(tn("dashboardAccess"), "instance.dashboard")}</legend>
|
|
291
|
+
<p class="note">${tn("dashboardAccessHint")}</p>
|
|
292
|
+
${DASHBOARD_FEATURES.map((feature) => html`<div key=${feature} class="field">
|
|
293
|
+
<label for=${`ag-dash-${feature}`}>${tn(`dashboard_${feature}`)}</label>
|
|
294
|
+
<select id=${`ag-dash-${feature}`} value=${f.dashboard[feature]} onChange=${(e) => setF((x) => ({ ...x, dashboard: { ...x.dashboard, [feature]: e.target.value } }))}>
|
|
295
|
+
${DASHBOARD_CHOICES.map((c) => html`<option key=${c} value=${c}>${c === "inherit"
|
|
296
|
+
? tn("dashboardInherit", tn(dashboardDefault(defaults, feature) ? "dashboardOn" : "dashboardOff")) : tn(c === "on" ? "dashboardOn" : "dashboardOff")}</option>`)}
|
|
297
|
+
</select></div>`)}
|
|
298
|
+
</fieldset>
|
|
290
299
|
<div class="field">${L(tn("replyCompletionGuard"), "instance.reply_completion_guard")}
|
|
291
300
|
<${Override} inherit=${f.inherit.reply_completion_guard} onInherit=${setInherit("reply_completion_guard")}>
|
|
292
301
|
<label class="check"><input type="checkbox" disabled=${f.inherit.reply_completion_guard} checked=${shown("reply_completion_guard", f.reply_completion_guard, defaults.reply_completion_guard ?? true)}
|
|
@@ -38,6 +38,23 @@ export function changedFields(next, baseline) {
|
|
|
38
38
|
return Object.fromEntries(Object.entries(next).filter(([key, value]) => !sameValue(value, baseline[key])));
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* The dashboard features an agent may use (dashboard-permissions.ts): each "inherit" (the fleet default: `defaults.dashboard`,
|
|
43
|
+
* else allowed), "on" or "off". The patch names only the features that changed: null follows the default again.
|
|
44
|
+
*/
|
|
45
|
+
export const DASHBOARD_FEATURES = ["appearance", "task_board", "web_settings"];
|
|
46
|
+
export const DASHBOARD_CHOICES = ["inherit", "on", "off"];
|
|
47
|
+
const dashboardOf = (v) => (v && typeof v === "object" && !Array.isArray(v) ? v : {});
|
|
48
|
+
/** What a feature is for an agent with no setting of its own: the default's, else allowed. */
|
|
49
|
+
export const dashboardDefault = (defaults, feature) => dashboardOf(defaults && defaults.dashboard)[feature] !== false;
|
|
50
|
+
export function dashboardForm(inst) {
|
|
51
|
+
const own = dashboardOf(inst.dashboard);
|
|
52
|
+
return Object.fromEntries(DASHBOARD_FEATURES.map((f) => [f, own[f] === true ? "on" : own[f] === false ? "off" : "inherit"]));
|
|
53
|
+
}
|
|
54
|
+
export function dashboardPatchValue(choices) {
|
|
55
|
+
return Object.fromEntries(DASHBOARD_FEATURES.map((f) => [f, choices[f] === "on" ? true : choices[f] === "off" ? false : null]));
|
|
56
|
+
}
|
|
57
|
+
|
|
41
58
|
/** #1302: cross-instance notices. Unset, or a value the fleet does not know, means full. */
|
|
42
59
|
export const visibilityDefault = (defaults) => (VISIBILITY_MODES.includes(defaults && defaults.cross_instance_visibility) ? defaults.cross_instance_visibility : "full");
|
|
43
60
|
|
|
@@ -155,6 +172,7 @@ export function agentPatch(form, inst, defaults, visibilityBaseline, statusEmoji
|
|
|
155
172
|
model_failover: form.inherit.model_failover ? null : (form.model_failover.trim() ? [form.model_failover.trim()] : []),
|
|
156
173
|
display_name: form.inherit.display_name ? null : form.display_name.trim(),
|
|
157
174
|
status_emojis: form.status_emojis,
|
|
175
|
+
dashboard: dashboardPatchValue(form.dashboard),
|
|
158
176
|
};
|
|
159
177
|
return changedFields(next, {
|
|
160
178
|
backend: inst.backend || defaults.backend || "claude-code",
|
|
@@ -177,6 +195,7 @@ export function agentPatch(form, inst, defaults, visibilityBaseline, statusEmoji
|
|
|
177
195
|
model_failover: hasOwn(inst, "model_failover") ? inst.model_failover : null,
|
|
178
196
|
display_name: hasOwn(inst, "display_name") ? inst.display_name : null,
|
|
179
197
|
status_emojis: statusEmojisBaseline,
|
|
198
|
+
dashboard: dashboardPatchValue(dashboardForm(inst)),
|
|
180
199
|
});
|
|
181
200
|
}
|
|
182
201
|
|
|
@@ -207,6 +226,7 @@ export function agentForm(inst, defaults, name = "") {
|
|
|
207
226
|
model_failover: Array.isArray(inst.model_failover) ? (inst.model_failover[0] || "") : ((defaults.model_failover && defaults.model_failover[0]) || ""),
|
|
208
227
|
display_name: inst.display_name ?? defaults.display_name ?? "",
|
|
209
228
|
status_emojis: inst.status_emojis || null,
|
|
229
|
+
dashboard: dashboardForm(inst),
|
|
210
230
|
inherit: {
|
|
211
231
|
auto_pause_after: inherit("auto_pause_after"), hang: !hasOwn(hang, "timeout_minutes"), agent_mode: inherit("agent_mode"),
|
|
212
232
|
tool_set: inherit("tool_set"), tool_progress: inherit("tool_progress"), cross_instance_visibility: inherit("cross_instance_visibility"),
|
|
@@ -12,6 +12,13 @@ register("settings", {
|
|
|
12
12
|
publicLink: "Offer temporary public web links",
|
|
13
13
|
publicLinkHint: "Explicit admin consent on every request. Cloudflare handles TLS; link users have full web-admin access. HTML previews get a second temporary tunnel unless web.preview_on_public_link is false.",
|
|
14
14
|
publicTtl: "Public link minutes (1–480)",
|
|
15
|
+
publicTtlNamed: "Public link minutes (0–10080; 0 = no expiry)",
|
|
16
|
+
publicTtlNamedHint: "With a fixed hostname the link may stay open up to 7 days (10080 minutes), or 0 = no expiry: it stays open until closed or AgEnD stops. Sign-in still needs a code each time. A changed lifetime applies at the next opening; Close the link to end the current one.",
|
|
17
|
+
publicTtlQuickRange: "A quick tunnel allows 1–480 minutes. Lower it, or set a fixed hostname for longer.",
|
|
18
|
+
publicTtlNamedRange: "With a fixed hostname: a whole number from 1 to 10080, or 0 for no expiry.",
|
|
19
|
+
publicAutoOpen: "Reopen the public link when AgEnD starts",
|
|
20
|
+
publicAutoOpenHint: "Fixed hostname only. After a restart the link reopens for the admin who last opened it with /web; nothing is posted. Sign-in still needs a code (from /web, or asked for on the sign-in page by a browser that signed in before). Close or /web revoke stops it until the next /web.",
|
|
21
|
+
publicAutoOpenNeedsHostname: "Needs a fixed hostname — untick it, or keep the hostname.",
|
|
15
22
|
publicProtocol: "Tunnel protocol",
|
|
16
23
|
namedHostname: "Fixed public hostname (optional)",
|
|
17
24
|
namedOriginPort: "Named tunnel origin port",
|
|
@@ -91,6 +98,15 @@ register("settings", {
|
|
|
91
98
|
crossInstanceVisibility: "Bot-to-bot messages in topics",
|
|
92
99
|
crossInstanceVisibilityHint: "full: the whole message · summary: one line · hidden: nothing. Messages are always delivered; the Mirror Topic still shows them.",
|
|
93
100
|
replyCompletionGuard: "Require a delivered reply",
|
|
101
|
+
dashboardAccess: "Dashboard access",
|
|
102
|
+
dashboardAccessHint: "What this agent may use on the dashboard, on top of its tool set. Switching a feature off applies at once (its tools are refused and leave its tool list); switching one on is confirmed first.",
|
|
103
|
+
dashboard_appearance: "Its look (theme, pictures, widgets, effects, mod proposals)",
|
|
104
|
+
dashboard_task_board: "The task board",
|
|
105
|
+
dashboard_web_settings: "Your web appearance settings",
|
|
106
|
+
dashboardInherit: "Fleet default ({0})",
|
|
107
|
+
dashboardOn: "allowed",
|
|
108
|
+
dashboardOff: "off",
|
|
109
|
+
dashboardDefaultsHint: "What every agent may use on the dashboard unless its own setting (its dialog under Agents) says otherwise. Unticking applies at once; ticking is confirmed first.",
|
|
94
110
|
replyGuardSupported: "Available for Claude Code, Codex, Muse and Kiro legacy/TUI in MCP mode.",
|
|
95
111
|
replyGuardUnsupported: "Stored but inactive: {0} in {1} mode does not support reply-drop recovery.",
|
|
96
112
|
logLevel: "Log level",
|
|
@@ -506,6 +522,8 @@ register("settings", {
|
|
|
506
522
|
vaEnabled: "Web messages carry authority", vaEnabledHint: "On by default: a session carries authority only once its account confirms it from Discord or Telegram. Off: no web message carries authority, and any already given reads as none at once; authority given before it was turned off never comes back.",
|
|
507
523
|
vaStuckOff: "Web authority is off for now: its state could not be saved on this computer (see the fleet log). It comes back on by itself once it can be.",
|
|
508
524
|
vaLoopbackOnly: "Only this computer can authorize", vaLoopbackOnlyHint: "On: only a browser on this computer qualifies, not one on your network or tailnet, and authority already given from elsewhere reads as none. Turning it off again doesn't bring that back.",
|
|
525
|
+
vaNamedLink: "Also over my named link ({0})", vaNamedLinkHint: "On: a browser signed in over your own named-tunnel hostname may be authorized too (the same Confirm in your DM). It is reached from the internet, and Cloudflare sees its traffic: turning this on is confirmed in a General or on this computer. Off applies at once, and every authorization given over it reads as none. A quick tunnel never qualifies.",
|
|
526
|
+
vaSessionNamed: "over your named link",
|
|
509
527
|
vaLanWarning: "The dashboard also answers to {0} (web.allowed_hosts). Anyone who reaches it there with a stolen sign-in could give authority in your name. Turn on \"Only this computer can authorize\" unless you trust everyone who can reach it.",
|
|
510
528
|
vaHours: "An authorization lasts (hours, {0}–{1})", vaHoursRange: "Must be a number from 1 to 48",
|
|
511
529
|
vaSessionsTitle: "Authorized sessions", vaSessionsNone: "No session is authorized now.", vaSessionUntil: "Authorized {0} · until {1}",
|
|
@@ -629,6 +647,13 @@ register("settings", {
|
|
|
629
647
|
publicLink: "提供臨時公開網頁連結",
|
|
630
648
|
publicLinkHint: "每次都需管理員明確啟用。Cloudflare 終止 TLS;登入者具有完整網頁管理權限。HTML 預覽會另開一條臨時通道,除非設定 web.preview_on_public_link: false。",
|
|
631
649
|
publicTtl: "公開連結分鐘數(1–480)",
|
|
650
|
+
publicTtlNamed: "公開連結分鐘數(0–10080;0 = 不到期)",
|
|
651
|
+
publicTtlNamedHint: "設定固定網域時,連結最長可開 7 天(10080 分鐘);設 0 則不會到期,直到關閉或 AgEnD 停止。每次登入仍需登入碼。修改後的期限從下次開啟起生效;要結束目前的連結,請按「關閉」。",
|
|
652
|
+
publicTtlQuickRange: "Quick tunnel 只能設 1–480 分鐘。請調低,或設定固定網域以使用更長時間。",
|
|
653
|
+
publicTtlNamedRange: "設定固定網域時:請輸入 1 到 10080 的整數,或 0 代表不到期。",
|
|
654
|
+
publicAutoOpen: "AgEnD 啟動時重新開啟公開連結",
|
|
655
|
+
publicAutoOpenHint: "僅限固定網域。重新啟動後,會替上次用 /web 開啟連結的管理員重新開啟;不會發任何訊息。登入仍需登入碼(用 /web 取得,或由曾登入過的瀏覽器在登入頁索取)。按「關閉」或 /web revoke 後,直到下次 /web 前都不會自動開啟。",
|
|
656
|
+
publicAutoOpenNeedsHostname: "需要固定網域——請取消勾選,或保留網域。",
|
|
632
657
|
publicProtocol: "Tunnel 協定",
|
|
633
658
|
namedHostname: "固定公開網域(選用)",
|
|
634
659
|
namedOriginPort: "Named tunnel 本機連接埠",
|
|
@@ -683,6 +708,15 @@ register("settings", {
|
|
|
683
708
|
crossInstanceVisibility: "Agent 之間的訊息在主題中",
|
|
684
709
|
crossInstanceVisibilityHint: "full:完整訊息 · summary:一行摘要 · hidden:不顯示。訊息一律照常送達,Mirror Topic 也照常顯示。",
|
|
685
710
|
replyCompletionGuard: "要求送出回覆",
|
|
711
|
+
dashboardAccess: "Dashboard 權限",
|
|
712
|
+
dashboardAccessHint: "這個 agent 能在 dashboard 上用哪些功能(在工具組之外再加一層限制)。關掉立即生效(工具會被拒絕,也從它的工具清單拿掉);打開要先確認。",
|
|
713
|
+
dashboard_appearance: "它的外觀(主題、圖片、小工具、特效、mod 提案)",
|
|
714
|
+
dashboard_task_board: "任務看板",
|
|
715
|
+
dashboard_web_settings: "你的網頁外觀設定",
|
|
716
|
+
dashboardInherit: "跟 fleet 預設({0})",
|
|
717
|
+
dashboardOn: "允許",
|
|
718
|
+
dashboardOff: "關閉",
|
|
719
|
+
dashboardDefaultsHint: "每個 agent 預設能在 dashboard 上用哪些功能;個別 agent 可以在 Agents 裡它的對話框另外設定。取消勾選立即生效;勾選要先確認。",
|
|
686
720
|
replyGuardSupported: "Claude Code、Codex、Muse 與 Kiro legacy/TUI 的 MCP 模式可用。",
|
|
687
721
|
replyGuardUnsupported: "設定會保留,但不會生效:{0} 的 {1} 模式不支援回覆遺失補救。",
|
|
688
722
|
logLevel: "Log 等級",
|
|
@@ -1097,6 +1131,8 @@ register("settings", {
|
|
|
1097
1131
|
vaEnabled: "網頁訊息帶有授權", vaEnabledHint: "預設開啟:session 要等它的帳號在 Discord 或 Telegram 上確認後才帶有授權。關閉:網頁訊息都不帶授權,已給出的授權立即視為無;關閉前給出的授權不會恢復。",
|
|
1098
1132
|
vaStuckOff: "網頁授權目前是關閉的:它的狀態無法存到這台電腦上(請看 fleet 紀錄)。可以存入後會自動恢復。",
|
|
1099
1133
|
vaLoopbackOnly: "只有這台電腦可以授權", vaLoopbackOnlyHint: "開啟:只有這台電腦上的瀏覽器符合,網路或 tailnet 上的不算,之前從其他地方給出的授權視為無。再關掉也不會讓那些授權恢復。",
|
|
1134
|
+
vaNamedLink: "也允許透過我的固定網域({0})", vaNamedLinkHint: "開啟:透過你自己的 named tunnel 固定網域登入的瀏覽器也能被授權(一樣要在私訊按「確認」)。它可從網際網路連到,Cloudflare 也看得到流量:開啟需在 General 或這台電腦上確認。關閉立即生效,透過它給出的授權都視為無。Quick tunnel 一律不行。",
|
|
1135
|
+
vaSessionNamed: "透過你的固定網域",
|
|
1100
1136
|
vaLanWarning: "dashboard 也接受 {0} 的連線(web.allowed_hosts)。任何人只要從那裡連上、又拿到被盜的登入,就能以你的名義給出授權。除非你信任所有能連到那裡的人,否則請開啟「只有這台電腦可以授權」。",
|
|
1101
1137
|
vaHours: "一次授權的有效期(小時,{0}–{1})", vaHoursRange: "必須是 1 到 48 之間的數字",
|
|
1102
1138
|
vaSessionsTitle: "已授權的 session", vaSessionsNone: "目前沒有已授權的 session。", vaSessionUntil: "{0} 授權 · 到 {1}",
|
|
@@ -22,6 +22,8 @@ export function verifiedAuthorityBody(original, f) {
|
|
|
22
22
|
if (f.enabled !== original.enabled) out.enabled = f.enabled ? null : false; // §0.1: on is the default
|
|
23
23
|
if (f.loopbackOnly !== original.loopbackOnly) out.loopback_only = f.loopbackOnly ? true : null;
|
|
24
24
|
if (f.hours !== original.hours) out.max_session_hours = f.hours === null ? null : f.hours;
|
|
25
|
+
// #2453 (§0.2): off by default; turning it on is confirmed in a General or on the host.
|
|
26
|
+
if (f.namedLink !== original.namedLink) out.named_link = f.namedLink ? true : null;
|
|
25
27
|
return Object.keys(out).length ? out : null;
|
|
26
28
|
}
|
|
27
29
|
|
|
@@ -39,7 +41,8 @@ export function WebAuthorityCard({ ctx, staged, disabled = false }) {
|
|
|
39
41
|
const o = r && r.ok ? r.body : null;
|
|
40
42
|
if (!o || typeof o !== "object" || !o.state || !o.hours || !Array.isArray(o.lanHosts) || !Array.isArray(o.authorized)) { setFailed(true); return; }
|
|
41
43
|
const c = o.configured || {};
|
|
42
|
-
const orig = { enabled: c.enabled !== false, loopbackOnly: c.loopback_only === true, hours: typeof c.max_session_hours === "number" ? c.max_session_hours : null
|
|
44
|
+
const orig = { enabled: c.enabled !== false, loopbackOnly: c.loopback_only === true, hours: typeof c.max_session_hours === "number" ? c.max_session_hours : null,
|
|
45
|
+
namedLink: c.named_link === true };
|
|
43
46
|
setOverview(o); setFailed(false);
|
|
44
47
|
setOriginal((prev) => prev || orig);
|
|
45
48
|
setF((prev) => prev || { ...orig });
|
|
@@ -78,6 +81,8 @@ export function WebAuthorityCard({ ctx, staged, disabled = false }) {
|
|
|
78
81
|
<label class="check"><input id="vaLoopbackOnly" type="checkbox" checked=${f.loopbackOnly} onChange=${(e) => set({ loopbackOnly: e.target.checked })} /><span>${tn("vaLoopbackOnly")}</span></label>
|
|
79
82
|
<p class="note">${tn("vaLoopbackOnlyHint")}</p>
|
|
80
83
|
${warnHosts ? html`<p class="note s-va-warn" role="status" id="vaLanWarning">${tn("vaLanWarning", overview.lanHosts.join(", "))}</p>` : null}
|
|
84
|
+
${overview.namedHost || original.namedLink ? html`<label class="check"><input id="vaNamedLink" type="checkbox" checked=${f.namedLink} onChange=${(e) => set({ namedLink: e.target.checked })} /><span>${tn("vaNamedLink", overview.namedHost || "—")}</span></label>
|
|
85
|
+
<p class="note">${tn("vaNamedLinkHint")}</p>` : null}
|
|
81
86
|
<div class="field"><label for="vaHours">${tn("vaHours", overview.hours.min, overview.hours.max)}</label>
|
|
82
87
|
<input id="vaHours" type="number" min=${overview.hours.min} max=${overview.hours.max} step="any" placeholder=${tn("defaultHours", overview.hours.default)}
|
|
83
88
|
class=${hoursError ? "invalid" : ""} value=${hoursText} onInput=${(e) => set({ hours: onHoursText(e.target.value) })} />
|
|
@@ -85,7 +90,7 @@ export function WebAuthorityCard({ ctx, staged, disabled = false }) {
|
|
|
85
90
|
<h4>${tn("vaSessionsTitle")}</h4>
|
|
86
91
|
${overview.authorized.length
|
|
87
92
|
? html`<ul class="s-va-sessions" id="vaSessions">${overview.authorized.map((a) => html`<li key=${a.handle} data-handle=${a.handle}>
|
|
88
|
-
<strong>${a.label}</strong> <span class="note">${a.account}</span
|
|
93
|
+
<strong>${a.label}</strong> <span class="note">${a.account}</span>${a.named ? html` <span class="note s-va-named">${tn("vaSessionNamed")}</span>` : null}<br /><span class="note">${tn("vaSessionUntil", when(a.authorizedAt), when(a.until))}</span></li>`)}</ul>`
|
|
89
94
|
: html`<p class="note" id="vaSessionsNone">${tn("vaSessionsNone")}</p>`}
|
|
90
95
|
</fieldset>
|
|
91
96
|
<div class="save-row"><button type="button" class="btn btn-primary" id="vaReview" disabled=${disabled || !!hoursError} onClick=${review}>${tn("vaReview")}</button>
|
|
@@ -106,6 +106,8 @@
|
|
|
106
106
|
/** Display hint from the same server session response; Start still reauthorizes. No CSRF is exposed here. */
|
|
107
107
|
trialSession() { return session().then(s => !ended && s?.handle && /^[a-f0-9]{16}$/.test(s.handle) ? Object.freeze({ epoch: s.handle, localAdmin: s.localAdmin === true }) : null); },
|
|
108
108
|
sessionEnded() { return ended !== null; },
|
|
109
|
+
/** A same-origin request made outside fetch (an upload's XHR, for its progress) was answered 401: the same end. */
|
|
110
|
+
unauthorized() { endSession("unauthorized"); sessionEndedBanner(); },
|
|
109
111
|
/** #1568: the session handle of this page's sign-in, or null when there is none. */
|
|
110
112
|
sessionHandle() { return session().then((s) => (s && s.handle ? s.handle : null)); },
|
|
111
113
|
async signOut() {
|