@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/web-api.js
CHANGED
|
@@ -53,6 +53,7 @@ import { resolveTz } from "./tz-utils.js";
|
|
|
53
53
|
import { filesPlayInChat } from "./web-effect.js";
|
|
54
54
|
import { QUERY_MAX_CHARS, RESULT_MAX, SEARCH_SOURCES } from "./search/search-store.js";
|
|
55
55
|
import { xmsgShown } from "./search/search-docs.js";
|
|
56
|
+
import { WebSendDedupe } from "./web-send-dedupe.js";
|
|
56
57
|
// ── Strict public-facing schemas ────────────────────────────────────────────
|
|
57
58
|
// web-api endpoints must reject unknown fields so the dashboard cannot inject
|
|
58
59
|
// internal-only flags that would reach handleCreate/scheduler/config writers.
|
|
@@ -169,6 +170,9 @@ const SendMessageSchema = z.object({
|
|
|
169
170
|
attachments: z.array(z.string().regex(/^[0-9a-f]{32}$/)).max(UPLOAD_LIMITS.maxFiles).optional(),
|
|
170
171
|
/** #2272: the message of this chat this one replies to — its row only; the server reads what it quotes. */
|
|
171
172
|
replyTo: z.object({ boot: z.string().regex(/^[0-9a-f]{1,32}$/), id: z.number().int().min(1).max(Number.MAX_SAFE_INTEGER) }).strict().optional(),
|
|
173
|
+
/** The sending page's own id for its pending bubble: echoed on the live event only (never recorded), so that page
|
|
174
|
+
* swaps its "sending" bubble for the real one even when the stream beats this answer. */
|
|
175
|
+
clientId: z.string().regex(/^[A-Za-z0-9_-]{8,64}$/).optional(),
|
|
172
176
|
}).strict().refine(v => v.message.trim().length > 0 || (v.attachments?.length ?? 0) > 0, { message: "a message or at least one file", path: ["message"] });
|
|
173
177
|
/** An instance name that is safe as one path segment (no separator, no NUL, not a dot name). */
|
|
174
178
|
const safeInstanceSegment = (name) => /^[^\\/\x00]+$/.test(name) && name !== "." && name !== "..";
|
|
@@ -1304,7 +1308,14 @@ function webAuthorityOf(req, url, ctx, instance) {
|
|
|
1304
1308
|
return { authority: noAuthority(0), refusal: "not_session" };
|
|
1305
1309
|
const v = evaluateWebRequest(req, url, ctx.webToken ?? null, ctx.webSessions, { touch: false });
|
|
1306
1310
|
return ctx.webAuthority({ session: v.kind === "allow" && v.via === "session" ? v.session : null, gateway: !!gatewayRequestContext(req),
|
|
1307
|
-
peer:
|
|
1311
|
+
peer: webPeerOf(req), ...(instance !== undefined ? { instance } : {}) });
|
|
1312
|
+
}
|
|
1313
|
+
/**
|
|
1314
|
+
* #1939 §3.4 / #2453 §0.2: where this request came from — the admin's named link by the gateway's own provenance (its
|
|
1315
|
+
* exact hostname), else what the TCP peer and Host say (a quick tunnel's request is `lan`, and refused as public_link).
|
|
1316
|
+
*/
|
|
1317
|
+
export function webPeerOf(req) {
|
|
1318
|
+
return gatewayRequestContext(req)?.named === true ? "named_link" : classifyWebPeer(req);
|
|
1308
1319
|
}
|
|
1309
1320
|
/**
|
|
1310
1321
|
* #2122: what let this request in, asked now — its session (by handle, under the token it was signed in with) or the
|
|
@@ -1626,15 +1637,12 @@ export function handleWebRequest(req, res, url, ctx) {
|
|
|
1626
1637
|
const after = url.searchParams.get("after");
|
|
1627
1638
|
const fresh = after === null || after === ""; // a cursor that is not ours in shape still gets nothing
|
|
1628
1639
|
if (guest) {
|
|
1629
|
-
// #2085 review:
|
|
1640
|
+
// #2085 review: with what became the channel's after its cursor (an older row, its copy sent since), once each —
|
|
1641
|
+
// replayFor counts a row where it was seen (seenSeq), as for every reader (#1774).
|
|
1630
1642
|
json(res, 200, guestPollBody(ctx.getUiStatus(), guest.scope(), Date.now(), guest, after, () => {
|
|
1631
1643
|
if (!history)
|
|
1632
1644
|
return [];
|
|
1633
|
-
|
|
1634
|
-
return history.after(0);
|
|
1635
|
-
const cursor = parseLastEventId(after);
|
|
1636
|
-
const rows = [...history.replayFor(cursor), ...history.seenAfter(cursor)];
|
|
1637
|
-
return rows.filter((m, i) => rows.findIndex((x) => x.boot === m.boot && x.id === m.id) === i);
|
|
1645
|
+
return fresh ? history.after(0) : history.replayFor(parseLastEventId(after));
|
|
1638
1646
|
}));
|
|
1639
1647
|
return true;
|
|
1640
1648
|
}
|
|
@@ -1972,6 +1980,100 @@ export function handleWebRequest(req, res, url, ctx) {
|
|
|
1972
1980
|
});
|
|
1973
1981
|
return true;
|
|
1974
1982
|
}
|
|
1983
|
+
// ── Fleet → Org chart → Office: the offices people made (web-offices.json, web-office-store.ts) ──
|
|
1984
|
+
// GET /ui/offices: { rev, offices, readable } for whoever may open the org chart (the public link too: it reads the
|
|
1985
|
+
// chart; a guest's routes do not include it). PUT /ui/offices { offices, rev? }: a person's change, as a theme's reset
|
|
1986
|
+
// or lock is — an admin session only, never the CLI token, never the public link (absent from its manifest, and
|
|
1987
|
+
// refused here too); asked again once the body is in. Every page hears `offices` on the stream and reads again.
|
|
1988
|
+
if (method === "GET" && path === "/ui/offices") {
|
|
1989
|
+
const store = ctx.officeStore;
|
|
1990
|
+
if (!store) {
|
|
1991
|
+
json(res, 404, { error: "Offices are not kept on this fleet" });
|
|
1992
|
+
return true;
|
|
1993
|
+
}
|
|
1994
|
+
json(res, 200, store.list());
|
|
1995
|
+
return true;
|
|
1996
|
+
}
|
|
1997
|
+
if (method === "PUT" && path === "/ui/offices") {
|
|
1998
|
+
const store = ctx.officeStore;
|
|
1999
|
+
if (!store) {
|
|
2000
|
+
json(res, 404, { error: "Offices are not kept on this fleet" });
|
|
2001
|
+
return true;
|
|
2002
|
+
}
|
|
2003
|
+
if (gatewayRequestContext(req)) {
|
|
2004
|
+
json(res, 403, { error: "offices_not_over_public_link" });
|
|
2005
|
+
return true;
|
|
2006
|
+
}
|
|
2007
|
+
// The person (an admin session's account), or the status that refuses the request.
|
|
2008
|
+
const who = () => {
|
|
2009
|
+
const verdict = evaluateWebRequest(req, url, ctx.webToken ?? null, ctx.webSessions, { touch: false });
|
|
2010
|
+
if (verdict.kind !== "allow")
|
|
2011
|
+
return { status: 401 };
|
|
2012
|
+
return verdict.via === "session" && verdict.session?.tier === "admin" ? { person: verdict.session.owner ? webPersonKey(verdict.session.owner) : "admin-session" } : { status: 403 };
|
|
2013
|
+
};
|
|
2014
|
+
const refuse = (status) => json(res, status, { error: status === 401 ? WEB_TOKEN_INVALID_MESSAGE : "offices_need_an_admin_session" });
|
|
2015
|
+
const first = who();
|
|
2016
|
+
if ("status" in first) {
|
|
2017
|
+
refuse(first.status);
|
|
2018
|
+
return true;
|
|
2019
|
+
}
|
|
2020
|
+
(async () => {
|
|
2021
|
+
let body;
|
|
2022
|
+
try {
|
|
2023
|
+
body = await parseBody(req);
|
|
2024
|
+
}
|
|
2025
|
+
catch {
|
|
2026
|
+
json(res, 400, { error: "Invalid JSON" });
|
|
2027
|
+
return;
|
|
2028
|
+
}
|
|
2029
|
+
if (!permitWebContinuation(req, res, ctx))
|
|
2030
|
+
return;
|
|
2031
|
+
const now = who();
|
|
2032
|
+
if ("status" in now) {
|
|
2033
|
+
refuse(now.status);
|
|
2034
|
+
return;
|
|
2035
|
+
}
|
|
2036
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
2037
|
+
json(res, 400, { error: "offices required" });
|
|
2038
|
+
return;
|
|
2039
|
+
}
|
|
2040
|
+
const extra = Object.keys(body).find(k => k !== "offices" && k !== "rev");
|
|
2041
|
+
if (extra !== undefined) {
|
|
2042
|
+
json(res, 400, { error: "offices_invalid", detail: `unknown key "${extra}"` });
|
|
2043
|
+
return;
|
|
2044
|
+
}
|
|
2045
|
+
// Office review: the rev the page read is required — a save without one would overwrite another person's unseen.
|
|
2046
|
+
if (body.rev === undefined) {
|
|
2047
|
+
json(res, 428, { error: "offices_rev_required", detail: "send the rev you read (GET /ui/offices)" });
|
|
2048
|
+
return;
|
|
2049
|
+
}
|
|
2050
|
+
if (!Number.isSafeInteger(body.rev)) {
|
|
2051
|
+
json(res, 400, { error: "offices_invalid", detail: "rev: an integer" });
|
|
2052
|
+
return;
|
|
2053
|
+
}
|
|
2054
|
+
const limiter = ctx.officeWrites, allowed = limiter ? limiter.check(now.person) : { ok: true };
|
|
2055
|
+
if (!allowed.ok) {
|
|
2056
|
+
res.setHeader("Retry-After", String(allowed.retry_after_s));
|
|
2057
|
+
json(res, 429, { error: "offices_rate_limited", retry_after_s: allowed.retry_after_s });
|
|
2058
|
+
return;
|
|
2059
|
+
}
|
|
2060
|
+
const r = store.replace(body.offices, body.rev);
|
|
2061
|
+
if (!r.ok) {
|
|
2062
|
+
json(res, r.status, { error: r.error, ...(r.detail ? { detail: r.detail } : {}) });
|
|
2063
|
+
return;
|
|
2064
|
+
}
|
|
2065
|
+
limiter?.record(now.person);
|
|
2066
|
+
ctx.emitSseEvent("offices", { rev: r.rev });
|
|
2067
|
+
json(res, 200, { rev: r.rev, offices: r.offices });
|
|
2068
|
+
})().catch(err => {
|
|
2069
|
+
ctx.logger.error({ err: err.message }, "Saving offices failed");
|
|
2070
|
+
try {
|
|
2071
|
+
json(res, 500, { error: "Saving offices failed" });
|
|
2072
|
+
}
|
|
2073
|
+
catch { /* already answered */ }
|
|
2074
|
+
});
|
|
2075
|
+
return true;
|
|
2076
|
+
}
|
|
1975
2077
|
// ── #1630: avatars and Discord server emoji, same-origin (the page never loads a platform's CDN; img-src stays 'self') ──
|
|
1976
2078
|
const imageMatch = path.match(/^\/ui\/img\/(bot|avatar|emoji)\/([^/]+)$/);
|
|
1977
2079
|
if (method === "GET" && imageMatch) {
|
|
@@ -2373,7 +2475,7 @@ export function handleWebRequest(req, res, url, ctx) {
|
|
|
2373
2475
|
if (path === "/ui/authorization" && (method === "GET" || method === "POST")) {
|
|
2374
2476
|
const judge = () => {
|
|
2375
2477
|
const v = evaluateWebRequest(req, url, ctx.webToken ?? null, ctx.webSessions, { touch: false });
|
|
2376
|
-
return { session: v.kind === "allow" && v.via === "session" ? v.session : null, gateway: !!gatewayRequestContext(req), peer:
|
|
2478
|
+
return { session: v.kind === "allow" && v.via === "session" ? v.session : null, gateway: !!gatewayRequestContext(req), peer: webPeerOf(req) };
|
|
2377
2479
|
};
|
|
2378
2480
|
res.setHeader("Cache-Control", "no-store");
|
|
2379
2481
|
if (method === "GET") {
|
|
@@ -3069,11 +3171,12 @@ export function handleWebRequest(req, res, url, ctx) {
|
|
|
3069
3171
|
}
|
|
3070
3172
|
return true;
|
|
3071
3173
|
}
|
|
3072
|
-
// #2293: global search — an admin's only: the header token or an admin session. Never
|
|
3073
|
-
//
|
|
3174
|
+
// #2293: global search — an admin's only: the header token or an admin session. Never a guest (not in GUEST_ROUTES).
|
|
3175
|
+
// #2478 (the user's request): over the public link too, unless web.search.public_link is false. Asked on arrival and
|
|
3176
|
+
// again before the answer is sent.
|
|
3074
3177
|
if (method === "GET" && (path === "/ui/search" || path === "/ui/search/context")) {
|
|
3075
3178
|
const refusal = () => {
|
|
3076
|
-
if (gatewayRequestContext(req))
|
|
3179
|
+
if (gatewayRequestContext(req) && ctx.fleetConfig?.web?.search?.public_link === false)
|
|
3077
3180
|
return [404, "search_not_over_public_link"];
|
|
3078
3181
|
if (guestRequestOf(req))
|
|
3079
3182
|
return [403, "guest_forbidden"];
|
|
@@ -3435,6 +3538,14 @@ function webDeliveryFailure(err) {
|
|
|
3435
3538
|
return { reason: "delivery_unavailable", error: "The instance could not receive this message. Check its status on the host, then retry." };
|
|
3436
3539
|
}
|
|
3437
3540
|
/** Handle POST /ui/send — extracted for readability. */
|
|
3541
|
+
/** Each web server's own memory of the sends it accepted by clientId (web-send-dedupe.ts). */
|
|
3542
|
+
const webSendDedupes = new WeakMap();
|
|
3543
|
+
function webSendDedupeOf(ctx) {
|
|
3544
|
+
let d = webSendDedupes.get(ctx);
|
|
3545
|
+
if (!d)
|
|
3546
|
+
webSendDedupes.set(ctx, d = new WebSendDedupe());
|
|
3547
|
+
return d;
|
|
3548
|
+
}
|
|
3438
3549
|
function handleSendMessage(req, res, ctx) {
|
|
3439
3550
|
const arrival = webAuthorityOf(req, new URL(req.url ?? "/", "http://localhost"), ctx); // #1939 §3: again after the body
|
|
3440
3551
|
let body = "";
|
|
@@ -3451,6 +3562,9 @@ function handleSendMessage(req, res, ctx) {
|
|
|
3451
3562
|
req.on("end", async () => {
|
|
3452
3563
|
if (!permitWebContinuation(req, res, ctx))
|
|
3453
3564
|
return;
|
|
3565
|
+
// A claimed clientId is settled however this ends: with the messageId once accepted, else forgotten.
|
|
3566
|
+
let claim;
|
|
3567
|
+
let acceptedId = null;
|
|
3454
3568
|
try {
|
|
3455
3569
|
const raw = JSON.parse(body);
|
|
3456
3570
|
const parsed = SendMessageSchema.safeParse(raw);
|
|
@@ -3459,7 +3573,7 @@ function handleSendMessage(req, res, ctx) {
|
|
|
3459
3573
|
json(res, 400, { error: `${issue.path.join(".") || "body"}: ${issue.message}` });
|
|
3460
3574
|
return;
|
|
3461
3575
|
}
|
|
3462
|
-
const { instance, message: typed, attachments = [], replyTo } = parsed.data;
|
|
3576
|
+
const { instance, message: typed, attachments = [], replyTo, clientId } = parsed.data;
|
|
3463
3577
|
// #1920 step 2b: a guest writes only to an in-scope instance, while guests may chat, and never a command.
|
|
3464
3578
|
const guest = guestRequestOf(req);
|
|
3465
3579
|
const refused = guestChatRefusal(guest, instance);
|
|
@@ -3486,6 +3600,33 @@ function handleSendMessage(req, res, ctx) {
|
|
|
3486
3600
|
json(res, 404, { error: `Instance not found: ${instance}` });
|
|
3487
3601
|
return;
|
|
3488
3602
|
}
|
|
3603
|
+
// #1827: who is writing — the name agents and chats see, and the id they tell people apart by. Asked now that the
|
|
3604
|
+
// body is in: a session revoked meanwhile sends nothing (not as the operator).
|
|
3605
|
+
const person = webPersonOf(req, new URL(req.url ?? "/", "http://localhost"), ctx);
|
|
3606
|
+
if (!person) {
|
|
3607
|
+
json(res, 401, { error: WEB_TOKEN_INVALID_MESSAGE });
|
|
3608
|
+
return;
|
|
3609
|
+
}
|
|
3610
|
+
// #2438 follow-up: the page's Retry of a send that already went (its answer and the live copy lost) is that send:
|
|
3611
|
+
// answered with its messageId, delivered once. One still going is waited for; one that failed is sent anew.
|
|
3612
|
+
if (clientId) {
|
|
3613
|
+
const key = WebSendDedupe.key(person.key, instance, clientId);
|
|
3614
|
+
for (;;) {
|
|
3615
|
+
const seen = webSendDedupeOf(ctx).claim(key);
|
|
3616
|
+
if (seen.kind === "going") {
|
|
3617
|
+
await seen.settled;
|
|
3618
|
+
if (res.destroyed)
|
|
3619
|
+
return;
|
|
3620
|
+
continue;
|
|
3621
|
+
}
|
|
3622
|
+
if (seen.kind === "accepted") {
|
|
3623
|
+
json(res, 200, { sent: true, messageId: seen.messageId, repeated: true });
|
|
3624
|
+
return;
|
|
3625
|
+
}
|
|
3626
|
+
claim = seen;
|
|
3627
|
+
break;
|
|
3628
|
+
}
|
|
3629
|
+
}
|
|
3489
3630
|
let files = [];
|
|
3490
3631
|
if (attachments.length > 0) {
|
|
3491
3632
|
const taken = ctx.webFiles?.takeForMessage(instance, attachments);
|
|
@@ -3512,14 +3653,6 @@ function handleSendMessage(req, res, ctx) {
|
|
|
3512
3653
|
&& (!guest || (guest.current() && !guestChatRefusal(guest, instance)))
|
|
3513
3654
|
&& (registered ? registeredChatInstance(ctx, instance) : ctx.instanceIpcClients.has(instance));
|
|
3514
3655
|
const message = typed;
|
|
3515
|
-
// #1827: who is writing — the name agents and chats see, and the id they tell people apart by. Asked now that the
|
|
3516
|
-
// body is in: a session revoked meanwhile sends nothing (not as the operator).
|
|
3517
|
-
const person = webPersonOf(req, new URL(req.url ?? "/", "http://localhost"), ctx);
|
|
3518
|
-
if (!person) {
|
|
3519
|
-
ctx.webFiles?.release(files);
|
|
3520
|
-
json(res, 401, { error: WEB_TOKEN_INVALID_MESSAGE });
|
|
3521
|
-
return;
|
|
3522
|
-
}
|
|
3523
3656
|
// #1939 S1: what the receipt records about who authorized it (observe only: agents still read web as recognition).
|
|
3524
3657
|
const { authority } = confirmAfterBody(arrival, webAuthorityOf(req, new URL(req.url ?? "/", "http://localhost"), ctx, instance));
|
|
3525
3658
|
const ts = new Date().toISOString();
|
|
@@ -3537,13 +3670,18 @@ function handleSendMessage(req, res, ctx) {
|
|
|
3537
3670
|
// Add no runtime credentials or local attachment paths to the display copy. A reply opens with the line it
|
|
3538
3671
|
// answers (#2272) — quoted only if that message reached the channel; a web-only one is named, never copied out.
|
|
3539
3672
|
const quoteLine = quoted ? webReplyEchoLine(quoted) : "";
|
|
3540
|
-
const
|
|
3673
|
+
const plainPreview = webEchoPreview(message, files.map(f => f.name));
|
|
3674
|
+
const preview = (quoteLine ? `${quoteLine}\n` : "") + plainPreview;
|
|
3675
|
+
// #2281: where the quoted message is on a platform. An echo to the same place is a native reply to it there, and
|
|
3676
|
+
// then needs no quoted line; anywhere else (or a message with no known post) keeps the line, as before.
|
|
3677
|
+
const quotedPosts = quoted ? ctx.webChatHistory?.postsOf(quoted) ?? [] : [];
|
|
3678
|
+
const echoText = (p) => formatWebChannelEcho(person.name, p, t("web.echo_full_text"), { guest: !!guest });
|
|
3541
3679
|
// ClassicBot echo (#1320 part B): same ordering lane, each room's setting (#2054: on unless it says false, or
|
|
3542
3680
|
// the fleet-wide switch is off for a room that is unset) resolved at send time inside sendClassicWebEcho.
|
|
3543
3681
|
const canClassicEcho = ctx.isClassicInstance?.(instance) === true && !!ctx.sendClassicWebEcho;
|
|
3544
3682
|
const settleClassicEcho = canClassicEcho ? ctx.reserveWebChannelEcho?.(instance, async () => {
|
|
3545
3683
|
const copies = [];
|
|
3546
|
-
const posted = await ctx.sendClassicWebEcho(instance,
|
|
3684
|
+
const posted = await ctx.sendClassicWebEcho(instance, echoText(preview), person.name, copies, quotedPosts.length ? { posts: quotedPosts, text: echoText(plainPreview) } : undefined);
|
|
3547
3685
|
if (posted > 0)
|
|
3548
3686
|
ctx.webMessageInChannel?.(instance, messageId, copies); // #1920 §5.2: the room saw it (#2305: its copies)
|
|
3549
3687
|
}) : undefined;
|
|
@@ -3553,12 +3691,13 @@ function handleSendMessage(req, res, ctx) {
|
|
|
3553
3691
|
|| (ctx.getGroupIdForInstance?.(instance) ?? String(ctx.fleetConfig?.channel?.group_id ?? "")) !== groupId
|
|
3554
3692
|
|| ctx.fleetConfig?.instances[instance]?.topic_id !== topicId)
|
|
3555
3693
|
return;
|
|
3556
|
-
const
|
|
3694
|
+
const native = quotedPosts.find(p => p.adapterId === syncAdapter.id && p.chatId === String(groupId) && (p.threadId ?? "") === String(topicId));
|
|
3695
|
+
const sent = await syncAdapter.sendText(String(groupId), echoText(native ? plainPreview : preview), { threadId: String(topicId), format: "text", allowedMentions: { parse: [] }, ...(native ? { replyTo: native.messageId } : {}) });
|
|
3557
3696
|
const copyId = sent && typeof sent === "object" && typeof sent.messageId === "string" ? sent.messageId : "";
|
|
3558
|
-
// #1920 §5.2: the topic saw it. #2305: and where it is, for a reaction to the web message.
|
|
3697
|
+
// #1920 §5.2: the topic saw it. #2305: and where it is, for a reaction to the web message (#2281: and a reply).
|
|
3559
3698
|
const reacting = syncAdapter;
|
|
3560
|
-
ctx.webMessageInChannel?.(instance, messageId, copyId
|
|
3561
|
-
? [{ adapter: reacting, chatId: String(groupId), threadId: String(topicId), messageId: copyId }] : []);
|
|
3699
|
+
ctx.webMessageInChannel?.(instance, messageId, copyId
|
|
3700
|
+
? [{ adapter: reacting, adapterId: syncAdapter.id, chatId: String(groupId), threadId: String(topicId), messageId: copyId }] : []);
|
|
3562
3701
|
return sent;
|
|
3563
3702
|
}) : undefined;
|
|
3564
3703
|
let delivered;
|
|
@@ -3604,6 +3743,9 @@ function handleSendMessage(req, res, ctx) {
|
|
|
3604
3743
|
json(res, 503, { reason: "delivery_changed", error: "The session or instance changed before this message was sent. Sign in again if needed, then retry." });
|
|
3605
3744
|
return;
|
|
3606
3745
|
}
|
|
3746
|
+
// The agent has it: from here on a repeat of this clientId is this send, whatever fails after (the echo, the
|
|
3747
|
+
// stream's copy) — a Retry then gets this messageId, never a second delivery.
|
|
3748
|
+
acceptedId = messageId;
|
|
3607
3749
|
settleEcho?.(true);
|
|
3608
3750
|
settleClassicEcho?.(true);
|
|
3609
3751
|
// #1666: the same cancel/progress bubble a platform inbound starts, in the topic the echo went to — a second turn
|
|
@@ -3618,11 +3760,19 @@ function handleSendMessage(req, res, ctx) {
|
|
|
3618
3760
|
ctx.eventLog?.logActivity("message", person.name, (message || `[${files.length} file(s)]`).slice(0, 200), instance);
|
|
3619
3761
|
// #1920: the account that wrote it (a guest sees its own); channel-visible once its copy was sent (above).
|
|
3620
3762
|
ctx.emitSseEvent("message", { instance, sender: person.name, role: "user", text: message, ts, attachments: files.map(publicAttachment), messageId, ...(person.bound ? { from: person.voterKey } : {}),
|
|
3621
|
-
...(quoted ? { replyTo: webChatReplyRef(quoted) } : {}) });
|
|
3763
|
+
...(quoted ? { replyTo: webChatReplyRef(quoted) } : {}), ...(clientId ? { clientId } : {}) });
|
|
3622
3764
|
json(res, 200, { sent: true, messageId });
|
|
3623
3765
|
}
|
|
3624
3766
|
catch {
|
|
3625
|
-
|
|
3767
|
+
// A failure after the agent has it is no failed send: the page is told it went (a Retry would only find it).
|
|
3768
|
+
if (res.headersSent) { /* answered already: the failure was in or after the answer */ }
|
|
3769
|
+
else if (acceptedId)
|
|
3770
|
+
json(res, 200, { sent: true, messageId: acceptedId });
|
|
3771
|
+
else
|
|
3772
|
+
json(res, 400, { error: "Invalid JSON" });
|
|
3773
|
+
}
|
|
3774
|
+
finally {
|
|
3775
|
+
claim?.settle(acceptedId);
|
|
3626
3776
|
}
|
|
3627
3777
|
});
|
|
3628
3778
|
}
|
|
@@ -3916,6 +4066,8 @@ function shellBodyTag(req, ctx, mode, viewer) {
|
|
|
3916
4066
|
+ ` data-owner-css-state="${ownerCssState}"${ownerCss ? ` data-owner-css="${attr(ownerCss)}"` : ""}`
|
|
3917
4067
|
// #1589: the public link shows attachments as downloads only (no inline text or media): its scope is unchanged.
|
|
3918
4068
|
+ `${gatewayRequestContext(req) ? ' data-public-link="1"' : ""}`
|
|
4069
|
+
// #2478: search is offered over the public link unless web.search.public_link is false (the page then shows no entry).
|
|
4070
|
+
+ `${gatewayRequestContext(req) && ctx.fleetConfig?.web?.search?.public_link === false ? ' data-search-link="0"' : ""}`
|
|
3919
4071
|
// #1920 phase 3: a guest's page — the shell shows only what a guest may use, and who it is signed in as.
|
|
3920
4072
|
+ `${mode === "full" && viewer?.guest && viewer.owner ? guestBodyAttrs(ctx, viewer.owner, attr) : ""}>`;
|
|
3921
4073
|
}
|