@songsid/agend 2.3.0-beta.25 → 2.3.0-beta.26
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/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.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.js +29 -4
- 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.js +15 -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 +18 -4
- 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 +73 -3
- 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.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 +76 -6
- package/dist/fleet-manager.js +479 -145
- 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 +33 -11
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +42 -13
- package/dist/host-memory.js.map +1 -1
- 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 +22 -0
- 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 +4 -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/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 +20 -1
- package/dist/ui/chat-render.js +1 -15
- package/dist/ui/chat-store.js +171 -61
- package/dist/ui/chat-strings.js +7 -2
- 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 +14 -13
- 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 +12 -0
- package/dist/ui/shared/app-store.js +1 -0
- package/dist/ui/shared/app-stream.js +2 -1
- package/dist/ui/shared/app.css +132 -3
- 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/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/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 +15 -2
- package/dist/web-api.js +174 -25
- 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" }));
|
|
@@ -425,6 +427,9 @@ function threadEntry(name, doc) {
|
|
|
425
427
|
canTryEffect: x => getAppEffects()?.canTry(x) === true,
|
|
426
428
|
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
429
|
clickReplyButton: (id, index) => store.clickReplyButton(name, id, index), // #1266
|
|
430
|
+
retryOutgoing: (clientId) => store.retryOutgoing(name, clientId), // a failed send's Retry and Delete
|
|
431
|
+
deleteOutgoing: (clientId) => store.deleteOutgoing(name, clientId),
|
|
432
|
+
putBackOutgoing: (clientId) => { recordEdit(composerField()); store.putBackOutgoing(name, clientId); }, // undoable, as typing
|
|
428
433
|
// #2272: reply to a message (the bar above the composer), and show the one a reply quotes.
|
|
429
434
|
startReply: (x) => { store.startReply(name, x); doc.getElementById("msgIn")?.focus(); },
|
|
430
435
|
findQuoted: (key) => store.findQuoted(name, key),
|
|
@@ -446,12 +451,13 @@ function follow(name, thread) {
|
|
|
446
451
|
// #1682: the same for mention names — asked for the ids the messages carry; when names arrive, drawn again.
|
|
447
452
|
const wantEmojis = (msgs) => { if (emojiIndex && emojiIndex.wants(msgs)) emojiIndex.ensure(); if (mentionIndex) mentionIndex.ensure(msgs); };
|
|
448
453
|
const offs = [];
|
|
449
|
-
if (emojiIndex) offs.push(emojiIndex.subscribe(() => thread.render(store.
|
|
450
|
-
if (mentionIndex) offs.push(mentionIndex.subscribe(() => thread.render(store.
|
|
454
|
+
if (emojiIndex) offs.push(emojiIndex.subscribe(() => thread.render(store.threadRows(name))));
|
|
455
|
+
if (mentionIndex) offs.push(mentionIndex.subscribe(() => thread.render(store.threadRows(name))));
|
|
451
456
|
wantEmojis(store.state.msgs[name]);
|
|
452
457
|
offs.push(store.subscribe((instance, kind) => {
|
|
453
458
|
if (instance !== name) return;
|
|
454
|
-
if (kind === "msgs") { thread.render(store.
|
|
459
|
+
if (kind === "msgs") { thread.render(store.threadRows(name)); wantEmojis(store.state.msgs[name]); }
|
|
460
|
+
else if (kind === "progress") thread.progress(); // an upload moved: its bar, in place
|
|
455
461
|
else if (kind === "sent") thread.jumpLatest();
|
|
456
462
|
}));
|
|
457
463
|
offs.push(P() ? P().onChange(() => thread.refreshCards()) : () => {});
|
|
@@ -461,10 +467,10 @@ function follow(name, thread) {
|
|
|
461
467
|
const next = getAppEffects();
|
|
462
468
|
if (next === trialOwner) return;
|
|
463
469
|
trialOff?.(); trialOff = null; trialOwner = next;
|
|
464
|
-
trialOff = next?.subscribeTrial(() => thread.render(store.
|
|
470
|
+
trialOff = next?.subscribeTrial(() => thread.render(store.threadRows(name))) ?? null;
|
|
465
471
|
};
|
|
466
472
|
watchTrial();
|
|
467
|
-
offs.push(onEffectsAwake(() => { watchTrial(); thread.render(store.
|
|
473
|
+
offs.push(onEffectsAwake(() => { watchTrial(); thread.render(store.threadRows(name)); }));
|
|
468
474
|
offs.push(() => { trialOff?.(); trialOff = null; trialOwner = null; });
|
|
469
475
|
offs.push(panelStore.subscribe(() => thread.refreshCards())); // a card shows whether the panel has its block
|
|
470
476
|
return () => { for (const off of offs) off(); };
|
|
@@ -493,7 +499,7 @@ function Thread({ name, th }) {
|
|
|
493
499
|
thread.attach(sc);
|
|
494
500
|
th.current = thread;
|
|
495
501
|
// 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.
|
|
502
|
+
thread.render(store.threadRows(name), { restore: store.state.scrollMemo[name] ?? null });
|
|
497
503
|
if (kept) thread.refreshCards();
|
|
498
504
|
const unfollow = follow(name, thread);
|
|
499
505
|
const onScroll = () => { store.state.scrollMemo[name] = thread.onScroll(); };
|
|
@@ -546,12 +552,7 @@ function Dock({ name, inst, lease, exec, awaiting, workingLabel }) {
|
|
|
546
552
|
<${WorkBar} name=${name} lease=${lease} exec=${exec} awaiting=${awaiting} workingLabel=${workingLabel} />
|
|
547
553
|
<${CommandCard} name=${name} lease=${lease} />
|
|
548
554
|
<${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
555
|
${s.waking[name] ? html`<p class="send-waking" role="status">${t("chat.waking", name)}</p>` : null}
|
|
553
|
-
${s.sendFailures[name] ? html`<div class="failed-send send-failure" role="alert"><span>${s.sendFailures[name].error}</span>
|
|
554
|
-
<button type="button" class="btn btn-sm" disabled=${!store.canRetrySend(name)} onClick=${() => store.retrySend(name)}>${t("chat.retrySend")}</button></div>` : null}
|
|
555
556
|
<${Composer} name=${name} lease=${lease} busy=${R().isBusy(exec)} />
|
|
556
557
|
<div class="composer-row">${guestPage() ? html`<span class="guest-note" title=${t("chat.guestNote")}>${t("chat.guestNote")}</span>` : html`<${ComposerAuth} />`}<span
|
|
557
558
|
class="hint" title=${t(guestPage() ? "chat.guestMore" : "chat.composerMore")}>${t("chat.composerHint")}</span></div>
|
|
@@ -721,7 +722,7 @@ function Composer({ name, lease, busy }) {
|
|
|
721
722
|
aria-activedescendant=${pick ? optId(pick) : undefined} onInput=${onInput} onKeyDown=${onKeyDown} onPaste=${onPaste}></textarea>
|
|
722
723
|
<button id="stopBtn" type="button" class="btn btn-stop" hidden=${!busy} title=${t("chat.stopReplyTitle")}
|
|
723
724
|
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
|
|
725
|
+
<button id="sendBtn" type="button" class="btn btn-primary btn-send" hidden=${busy && !content} disabled=${!content}
|
|
725
726
|
onMouseDown=${(e) => e.preventDefault()}
|
|
726
727
|
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
728
|
</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() {
|
|
@@ -69,7 +69,7 @@ register("app", {
|
|
|
69
69
|
needsYou: "needs you", approxNote: "Read from the terminal, so approximate", chat: "Chat", details: "Details", log: "Log", instanceViews: "Views of {0}",
|
|
70
70
|
textSize: "Text size: {0}", textSize_s: "S", textSize_m: "M", textSize_l: "L", textSize_fit: "Fit", usage: "Usage", usageHide: "Hide usage",
|
|
71
71
|
ownerCssKeep: "Keep this custom CSS?", ownerCssKeepLeft: "Reverts in {0} s", ownerCssKeepYes: "Keep", ownerCssKeepNo: "Revert",
|
|
72
|
-
statusRunning: "running", statusStopped: "stopped", statusPaused: "paused", statusCrashed: "crashed", statusWorking: "working", statusStuck: "looks stuck", statusIdle: "idle",
|
|
72
|
+
statusQueued: "queued", statusStarting: "starting", fleetStarting: "Fleet starting {0}/{1}…", statusRunning: "running", statusStopped: "stopped", statusPaused: "paused", statusCrashed: "crashed", statusWorking: "working", statusStuck: "looks stuck", statusIdle: "idle",
|
|
73
73
|
connReconnecting: "Can't reach AgEnD — it may be restarting. Reconnecting…", connReconnectingIn: "Can't reach AgEnD — it may be restarting. Trying again in {0} s.",
|
|
74
74
|
hydrating: "Loading what is open now…", hydrateFailed: "Could not load what is open now (prompts, ticks).",
|
|
75
75
|
skip: "Skip to the conversation", loadFailed: "This could not be loaded.", retry: "Try again", loading: "Loading…",
|
|
@@ -106,7 +106,7 @@ register("app", {
|
|
|
106
106
|
needsYou: "等你回應", approxNote: "從終端畫面判讀,僅供參考", chat: "聊天", details: "詳細資訊", log: "紀錄", instanceViews: "{0} 的檢視",
|
|
107
107
|
textSize: "字級:{0}", textSize_s: "小", textSize_m: "中", textSize_l: "大", textSize_fit: "填滿", usage: "用量", usageHide: "隱藏用量",
|
|
108
108
|
ownerCssKeep: "保留這份自訂 CSS?", ownerCssKeepLeft: "{0} 秒後還原", ownerCssKeepYes: "保留", ownerCssKeepNo: "還原",
|
|
109
|
-
statusRunning: "執行中", statusStopped: "已停止", statusPaused: "已暫停", statusCrashed: "已當掉", statusWorking: "工作中", statusStuck: "似乎卡住", statusIdle: "閒置",
|
|
109
|
+
statusQueued: "等待啟動", statusStarting: "啟動中", fleetStarting: "Fleet 啟動中 {0}/{1}…", statusRunning: "執行中", statusStopped: "已停止", statusPaused: "已暫停", statusCrashed: "已當掉", statusWorking: "工作中", statusStuck: "似乎卡住", statusIdle: "閒置",
|
|
110
110
|
connReconnecting: "連不上 AgEnD——可能正在重新啟動。正在重新連線…", connReconnectingIn: "連不上 AgEnD——可能正在重新啟動。{0} 秒後再試。",
|
|
111
111
|
hydrating: "正在載入目前的狀態…", hydrateFailed: "無法載入目前的狀態(提示、送達標記)。",
|
|
112
112
|
skip: "跳到對話", loadFailed: "無法載入。", retry: "再試一次", loading: "載入中…",
|
|
@@ -45,6 +45,8 @@ export const SYNCED = {
|
|
|
45
45
|
"color.show": { ls: "agend_color_show", ...json },
|
|
46
46
|
"theme.pageByInstance": { ls: "agend_theme_page", ...json }, // #2196
|
|
47
47
|
"color.off": { ls: "agend_color_off", toLocal: (v) => (v === true ? "on" : null), fromLocal: (s) => (s === "on" ? true : undefined) },
|
|
48
|
+
// The AI usage view's compact mode: one line per login (usage-dialog.js), in the dialog and the rail alike.
|
|
49
|
+
"usage.compact": { ls: "agend_usage_compact", toLocal: (v) => (v === true ? "on" : null), fromLocal: (s) => (s === "on" ? true : undefined) },
|
|
48
50
|
};
|
|
49
51
|
// #1629: fixed personal scope. A separate session-bound owner-effect store synchronizes these, never this host cache.
|
|
50
52
|
export const OWNER_EFFECT_PREF_KEYS = Object.freeze(["fx.sidebar", "fx.header", "fx.page", "fx.box", "fx.cursor"]);
|
|
@@ -284,6 +284,8 @@ appStore.subscribe((s) => {
|
|
|
284
284
|
|
|
285
285
|
function statusLabel(i, exec, awaiting) {
|
|
286
286
|
if (awaiting != null) return t("app.needsYou");
|
|
287
|
+
if (i.startup_phase === "queued") return t("app.statusQueued");
|
|
288
|
+
if (i.startup_phase === "starting") return t("app.statusStarting");
|
|
287
289
|
if (i.status !== "running") return i.status === "crashed" ? t("app.statusCrashed") : t("app.statusStopped");
|
|
288
290
|
if (exec === "working") return t("app.statusWorking");
|
|
289
291
|
if (exec === "stuck") return t("app.statusStuck");
|
|
@@ -291,6 +293,8 @@ function statusLabel(i, exec, awaiting) {
|
|
|
291
293
|
}
|
|
292
294
|
export function statusClass(i, exec, awaiting) {
|
|
293
295
|
if (awaiting != null) return "warn";
|
|
296
|
+
if (i?.startup_phase === "starting") return "busy";
|
|
297
|
+
if (i?.startup_phase === "queued") return "off";
|
|
294
298
|
if (!i || i.status !== "running") return i && i.status === "crashed" ? "bad" : "off";
|
|
295
299
|
return exec === "working" ? "busy" : exec === "stuck" ? "warn" : "ok";
|
|
296
300
|
}
|
|
@@ -500,6 +504,13 @@ function ConnectionLine() {
|
|
|
500
504
|
// (agend-auth's banner says that) draw nothing either; only reconnecting and a failed catch-up are shown.
|
|
501
505
|
return null;
|
|
502
506
|
}
|
|
507
|
+
|
|
508
|
+
/** A connected fleet is still bringing up agents; this is distinct from the reconnect banner. */
|
|
509
|
+
export function StartupLine() {
|
|
510
|
+
const { startup } = useStore(appStore);
|
|
511
|
+
if (!startup) return null;
|
|
512
|
+
return html`<div data-trust="status" class="conn fleet-starting" role="status">${t("app.fleetStarting", startup.ready, startup.total)}</div>`;
|
|
513
|
+
}
|
|
503
514
|
/** #1580: the fleet is not answering (a restart): said, with when the next try is — never a silent stale page. */
|
|
504
515
|
function Reconnecting() {
|
|
505
516
|
const { retryAt } = useStore(appStore);
|
|
@@ -658,6 +669,7 @@ export function Shell({ panels, onNewInstance, viewOnly = false }) {
|
|
|
658
669
|
<div class="scrim" onClick=${closeDrawer} aria-hidden="true"></div>
|
|
659
670
|
<main id="main" class="main" tabindex="-1" inert=${shell.drawer && narrow() ? true : undefined}>
|
|
660
671
|
<${ConnectionLine} />
|
|
672
|
+
<${StartupLine} />
|
|
661
673
|
${viewOnly || guest ? null : html`<${OperationLine} route=${nav.route} />`}
|
|
662
674
|
${viewOnly || guest || !shell.banner ? null : html`<${shell.banner} />`}
|
|
663
675
|
<${Outlet} route=${nav.route} seq=${nav.seq} panels=${panels} />
|