@songsid/agend 2.2.0-beta.2 → 2.2.0-beta.20
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 +40 -24
- package/README.zh-TW.md +40 -24
- package/dist/agent-cli-instructions.md +6 -2
- package/dist/agent-cli.js +15 -7
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.js +3 -0
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/agent-whats-new.js +16 -4
- package/dist/agent-whats-new.js.map +1 -1
- package/dist/auth-api.d.ts +3 -1
- package/dist/auth-api.js +17 -1
- package/dist/auth-api.js.map +1 -1
- package/dist/backend/antigravity.d.ts +33 -0
- package/dist/backend/antigravity.js +126 -18
- package/dist/backend/antigravity.js.map +1 -1
- package/dist/backend/claude-carry.d.ts +22 -0
- package/dist/backend/claude-carry.js +170 -0
- package/dist/backend/claude-carry.js.map +1 -0
- package/dist/backend/claude-code.d.ts +65 -2
- package/dist/backend/claude-code.js +226 -33
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex-metadata.d.ts +33 -0
- package/dist/backend/codex-metadata.js +122 -18
- package/dist/backend/codex-metadata.js.map +1 -1
- package/dist/backend/codex.d.ts +3 -0
- package/dist/backend/codex.js +17 -1
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/credential-profile.d.ts +40 -3
- package/dist/backend/credential-profile.js +113 -5
- package/dist/backend/credential-profile.js.map +1 -1
- package/dist/backend/effort-metadata.d.ts +37 -1
- package/dist/backend/effort-metadata.js +86 -15
- package/dist/backend/effort-metadata.js.map +1 -1
- package/dist/backend/grok.d.ts +4 -0
- package/dist/backend/grok.js +14 -0
- package/dist/backend/grok.js.map +1 -1
- package/dist/backend/kiro.d.ts +27 -1
- package/dist/backend/kiro.js +63 -10
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/launch-effort.d.ts +61 -0
- package/dist/backend/launch-effort.js +170 -0
- package/dist/backend/launch-effort.js.map +1 -0
- package/dist/backend/muse.d.ts +84 -0
- package/dist/backend/muse.js +449 -53
- package/dist/backend/muse.js.map +1 -1
- package/dist/backend/types.d.ts +48 -2
- package/dist/backend/types.js +12 -1
- package/dist/backend/types.js.map +1 -1
- package/dist/backend-command.d.ts +91 -0
- package/dist/backend-command.js +122 -0
- package/dist/backend-command.js.map +1 -0
- package/dist/background-work-ended.d.ts +18 -0
- package/dist/background-work-ended.js +30 -0
- package/dist/background-work-ended.js.map +1 -0
- package/dist/cache-service.d.ts +2 -0
- package/dist/cache-service.js +2 -1
- package/dist/cache-service.js.map +1 -1
- package/dist/channel/adapters/discord.d.ts +26 -1
- package/dist/channel/adapters/discord.js +103 -3
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +35 -3
- package/dist/channel/adapters/telegram.js +122 -23
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/mcp-tools.js +5 -3
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/message-edit-errors.d.ts +5 -0
- package/dist/channel/message-edit-errors.js +24 -0
- package/dist/channel/message-edit-errors.js.map +1 -0
- package/dist/channel/telegram-markdown.d.ts +6 -0
- package/dist/channel/telegram-markdown.js +104 -0
- package/dist/channel/telegram-markdown.js.map +1 -0
- package/dist/channel/tool-router.d.ts +2 -0
- package/dist/channel/tool-router.js +6 -5
- package/dist/channel/tool-router.js.map +1 -1
- package/dist/channel/types.d.ts +46 -4
- package/dist/cli.js +184 -34
- package/dist/cli.js.map +1 -1
- package/dist/command-table.d.ts +2 -2
- package/dist/command-table.js +5 -2
- package/dist/command-table.js.map +1 -1
- package/dist/compact-watch.d.ts +86 -0
- package/dist/compact-watch.js +288 -0
- package/dist/compact-watch.js.map +1 -0
- package/dist/config-validator.js +55 -5
- package/dist/config-validator.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.js +2 -1
- package/dist/config.js.map +1 -1
- package/dist/cross-instance-envelope.js +2 -0
- package/dist/cross-instance-envelope.js.map +1 -1
- package/dist/daemon.d.ts +129 -1
- package/dist/daemon.js +754 -94
- package/dist/daemon.js.map +1 -1
- package/dist/delivery-outbox.d.ts +64 -8
- package/dist/delivery-outbox.js +315 -24
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/delivery-reconciliation.d.ts +18 -1
- package/dist/delivery-reconciliation.js +67 -2
- package/dist/delivery-reconciliation.js.map +1 -1
- package/dist/doctor.js +7 -0
- package/dist/doctor.js.map +1 -1
- package/dist/fleet-context.d.ts +12 -1
- package/dist/fleet-manager.d.ts +392 -19
- package/dist/fleet-manager.js +2030 -275
- package/dist/fleet-manager.js.map +1 -1
- package/dist/forged-envelope-scan.d.ts +4 -3
- package/dist/forged-envelope-scan.js +4 -3
- package/dist/forged-envelope-scan.js.map +1 -1
- package/dist/general-knowledge/skills/credential-profiles/SKILL.md +84 -16
- package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +16 -1
- package/dist/general-knowledge/skills/model-discovery/SKILL.md +4 -1
- package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +4 -0
- package/dist/host-memory.d.ts +10 -0
- package/dist/host-memory.js +33 -1
- package/dist/host-memory.js.map +1 -1
- package/dist/human-delivery-receipts.d.ts +83 -0
- package/dist/human-delivery-receipts.js +194 -0
- package/dist/human-delivery-receipts.js.map +1 -0
- package/dist/install-lock.d.ts +18 -1
- package/dist/install-lock.js +30 -3
- package/dist/install-lock.js.map +1 -1
- package/dist/instance-avatar.d.ts +5 -4
- package/dist/instance-avatar.js +12 -8
- package/dist/instance-avatar.js.map +1 -1
- package/dist/instance-config-impact.js +1 -0
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +25 -6
- package/dist/instance-lifecycle.js +76 -27
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/instructions.d.ts +1 -1
- package/dist/instructions.js +3 -3
- package/dist/instructions.js.map +1 -1
- package/dist/kiro-compact-pane.d.ts +38 -0
- package/dist/kiro-compact-pane.js +57 -0
- package/dist/kiro-compact-pane.js.map +1 -0
- package/dist/kiro-log-gate.d.ts +42 -0
- package/dist/kiro-log-gate.js +39 -0
- package/dist/kiro-log-gate.js.map +1 -0
- package/dist/launchd-handoff.d.ts +43 -0
- package/dist/launchd-handoff.js +56 -0
- package/dist/launchd-handoff.js.map +1 -0
- package/dist/locale.js +268 -42
- package/dist/locale.js.map +1 -1
- package/dist/log-service.d.ts +99 -0
- package/dist/log-service.js +243 -0
- package/dist/log-service.js.map +1 -0
- package/dist/log-steps.d.ts +169 -0
- package/dist/log-steps.js +858 -0
- package/dist/log-steps.js.map +1 -0
- package/dist/login-controller.d.ts +20 -3
- package/dist/login-controller.js +71 -19
- package/dist/login-controller.js.map +1 -1
- package/dist/login-flows.d.ts +5 -2
- package/dist/login-flows.js +5 -4
- package/dist/login-flows.js.map +1 -1
- package/dist/login-shell.d.ts +21 -0
- package/dist/login-shell.js +98 -0
- package/dist/login-shell.js.map +1 -0
- package/dist/memory-pressure.d.ts +13 -1
- package/dist/memory-pressure.js +50 -10
- package/dist/memory-pressure.js.map +1 -1
- package/dist/needs-you-hub.js +2 -0
- package/dist/needs-you-hub.js.map +1 -1
- package/dist/needs-you-live.d.ts +6 -0
- package/dist/needs-you-live.js +23 -3
- package/dist/needs-you-live.js.map +1 -1
- package/dist/outbound-handlers.d.ts +100 -0
- package/dist/outbound-handlers.js +277 -93
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +30 -15
- package/dist/outbound-schemas.js +23 -5
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/provider-probe.d.ts +29 -0
- package/dist/provider-probe.js +198 -2
- package/dist/provider-probe.js.map +1 -1
- package/dist/public-link-progress.d.ts +1 -1
- package/dist/public-link-progress.js +8 -2
- package/dist/public-link-progress.js.map +1 -1
- package/dist/public-preview.d.ts +101 -0
- package/dist/public-preview.js +236 -0
- package/dist/public-preview.js.map +1 -0
- package/dist/public-web-gateway.d.ts +2 -0
- package/dist/public-web-gateway.js +20 -6
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/public-web-link.d.ts +24 -1
- package/dist/public-web-link.js +63 -9
- package/dist/public-web-link.js.map +1 -1
- package/dist/quickstart-api.d.ts +1 -1
- package/dist/quickstart-api.js +8 -6
- package/dist/quickstart-api.js.map +1 -1
- package/dist/quickstart.d.ts +12 -0
- package/dist/quickstart.js +36 -6
- package/dist/quickstart.js.map +1 -1
- package/dist/reply-buttons-controller.d.ts +89 -5
- package/dist/reply-buttons-controller.js +273 -8
- package/dist/reply-buttons-controller.js.map +1 -1
- package/dist/reply-buttons.d.ts +48 -3
- package/dist/reply-buttons.js +105 -10
- package/dist/reply-buttons.js.map +1 -1
- package/dist/reply-polls.d.ts +85 -0
- package/dist/reply-polls.js +154 -0
- package/dist/reply-polls.js.map +1 -0
- package/dist/restart-guard.d.ts +6 -3
- package/dist/restart-guard.js +6 -3
- package/dist/restart-guard.js.map +1 -1
- package/dist/restart-progress.d.ts +33 -2
- package/dist/restart-progress.js +30 -2
- package/dist/restart-progress.js.map +1 -1
- package/dist/sd-notify.d.ts +22 -1
- package/dist/sd-notify.js +63 -10
- package/dist/sd-notify.js.map +1 -1
- package/dist/service-activation.d.ts +15 -7
- package/dist/service-activation.js +42 -26
- package/dist/service-activation.js.map +1 -1
- package/dist/service-installer.d.ts +55 -0
- package/dist/service-installer.js +90 -3
- package/dist/service-installer.js.map +1 -1
- package/dist/service-plan.d.ts +11 -0
- package/dist/service-plan.js +23 -0
- package/dist/service-plan.js.map +1 -1
- package/dist/settings-api.js +14 -4
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +4 -2
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-effect.js +2 -0
- package/dist/settings-effect.js.map +1 -1
- package/dist/spawn-gate.js +5 -1
- package/dist/spawn-gate.js.map +1 -1
- package/dist/spawn-hold-reply.d.ts +12 -0
- package/dist/spawn-hold-reply.js +39 -0
- package/dist/spawn-hold-reply.js.map +1 -0
- package/dist/systemd-peer-bus.d.ts +95 -0
- package/dist/systemd-peer-bus.js +430 -0
- package/dist/systemd-peer-bus.js.map +1 -0
- package/dist/systemd-runtime.d.ts +27 -1
- package/dist/systemd-runtime.js +69 -7
- package/dist/systemd-runtime.js.map +1 -1
- package/dist/tips.js +1 -1
- package/dist/tips.js.map +1 -1
- package/dist/tool-permissions.js +3 -3
- package/dist/tool-permissions.js.map +1 -1
- package/dist/tool-progress.js +3 -2
- package/dist/tool-progress.js.map +1 -1
- package/dist/topic-commands.d.ts +71 -4
- package/dist/topic-commands.js +167 -34
- package/dist/topic-commands.js.map +1 -1
- package/dist/transcript-delta-reader.d.ts +3 -3
- package/dist/transcript-delta-reader.js +8 -8
- package/dist/transcript-delta-reader.js.map +1 -1
- package/dist/transcript-jsonl.d.ts +85 -3
- package/dist/transcript-jsonl.js +156 -13
- package/dist/transcript-jsonl.js.map +1 -1
- package/dist/transcript-monitor.d.ts +11 -0
- package/dist/transcript-monitor.js +61 -13
- package/dist/transcript-monitor.js.map +1 -1
- package/dist/transcript-sources.d.ts +24 -0
- package/dist/transcript-sources.js +211 -56
- package/dist/transcript-sources.js.map +1 -1
- package/dist/tunnel/cloudflared.d.ts +7 -15
- package/dist/tunnel/cloudflared.js +36 -1
- package/dist/tunnel/cloudflared.js.map +1 -1
- package/dist/tunnel/purpose-lane.d.ts +5 -1
- package/dist/tunnel/purpose-lane.js +14 -7
- package/dist/tunnel/purpose-lane.js.map +1 -1
- package/dist/tunnel/types.d.ts +5 -1
- package/dist/tunnel/types.js.map +1 -1
- package/dist/turn-resume.d.ts +2 -0
- package/dist/turn-resume.js +1 -1
- package/dist/turn-resume.js.map +1 -1
- package/dist/types.d.ts +22 -0
- package/dist/ui/chat-commands.js +1 -0
- package/dist/ui/chat-mentions.js +63 -0
- package/dist/ui/chat-render.js +80 -10
- package/dist/ui/chat-store.js +70 -6
- package/dist/ui/chat-strings.js +62 -32
- package/dist/ui/chat-thread.js +199 -24
- package/dist/ui/chat-time.js +80 -0
- package/dist/ui/chat-tour.js +17 -14
- package/dist/ui/panel-chat.js +45 -21
- package/dist/ui/panel-details.js +2 -0
- package/dist/ui/panel-fleet.js +213 -58
- package/dist/ui/panel-log.js +428 -0
- package/dist/ui/panel-needs.js +14 -10
- package/dist/ui/panel-settings.js +82 -17
- package/dist/ui/preview-panel.js +55 -15
- package/dist/ui/preview-status.js +28 -0
- package/dist/ui/preview.js +44 -3
- package/dist/ui/settings-dialogs.js +11 -6
- package/dist/ui/settings-model.js +3 -1
- package/dist/ui/settings-strings.js +32 -12
- package/dist/ui/settings-token.js +8 -1
- package/dist/ui/settings-wizard.js +5 -3
- package/dist/ui/shared/app-i18n.js +41 -15
- package/dist/ui/shared/app-prefs.js +250 -0
- package/dist/ui/shared/app-route.js +6 -1
- package/dist/ui/shared/app-shell.js +165 -8
- package/dist/ui/shared/app-store.js +5 -1
- package/dist/ui/shared/app-stream.js +26 -5
- package/dist/ui/shared/app.css +170 -30
- package/dist/ui/shared/app.js +11 -1
- package/dist/ui/shared/fonts/README.md +1 -1
- package/dist/ui/shared/header-tools.js +83 -13
- package/dist/ui/shared/instance-nav.js +178 -51
- package/dist/ui/shared/instance-switch.js +8 -5
- package/dist/ui/shared/panel-view.js +21 -4
- package/dist/ui/shared/signin.js +15 -9
- package/dist/ui/shared/theme.js +39 -7
- package/dist/ui/shared/tokens.css +1 -1
- package/dist/ui/shared/ui-icons.js +2 -0
- package/dist/ui/shared/usage-dialog.js +23 -5
- package/dist/ui/shared/view-strings.js +40 -4
- package/dist/ui/signin.html +8 -3
- package/dist/unrecognised-prompt.d.ts +23 -0
- package/dist/unrecognised-prompt.js +74 -0
- package/dist/unrecognised-prompt.js.map +1 -0
- package/dist/update-check.d.ts +31 -0
- package/dist/update-check.js +47 -0
- package/dist/update-check.js.map +1 -1
- package/dist/update-install.d.ts +13 -1
- package/dist/update-install.js +16 -4
- package/dist/update-install.js.map +1 -1
- package/dist/update-launch.d.ts +48 -1
- package/dist/update-launch.js +159 -5
- package/dist/update-launch.js.map +1 -1
- package/dist/update-marker.d.ts +51 -0
- package/dist/update-marker.js +61 -0
- package/dist/update-marker.js.map +1 -1
- package/dist/usage/i18n-keys.d.ts +1 -1
- package/dist/usage/i18n-keys.js +1 -1
- package/dist/usage/i18n-keys.js.map +1 -1
- package/dist/usage/providers.d.ts +62 -13
- package/dist/usage/providers.js +256 -68
- package/dist/usage/providers.js.map +1 -1
- package/dist/usage/statusline-usage.d.ts +6 -2
- package/dist/usage/statusline-usage.js +10 -4
- package/dist/usage/statusline-usage.js.map +1 -1
- package/dist/view-api.js +31 -9
- package/dist/view-api.js.map +1 -1
- package/dist/web-api.d.ts +82 -4
- package/dist/web-api.js +632 -88
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.js +5 -2
- package/dist/web-auth.js.map +1 -1
- package/dist/web-chat-history.d.ts +26 -1
- package/dist/web-chat-history.js +47 -8
- package/dist/web-chat-history.js.map +1 -1
- package/dist/web-commands.d.ts +41 -5
- package/dist/web-commands.js +25 -7
- package/dist/web-commands.js.map +1 -1
- package/dist/web-login.d.ts +14 -0
- package/dist/web-login.js +4 -2
- package/dist/web-login.js.map +1 -1
- package/dist/web-mentions.d.ts +44 -0
- package/dist/web-mentions.js +75 -0
- package/dist/web-mentions.js.map +1 -0
- package/dist/web-prefs.d.ts +81 -0
- package/dist/web-prefs.js +297 -0
- package/dist/web-prefs.js.map +1 -0
- package/dist/web-preview.d.ts +14 -1
- package/dist/web-preview.js +35 -13
- package/dist/web-preview.js.map +1 -1
- package/dist/web-profile.d.ts +32 -0
- package/dist/web-profile.js +132 -0
- package/dist/web-profile.js.map +1 -0
- package/dist/web-reply-files.d.ts +45 -0
- package/dist/web-reply-files.js +223 -0
- package/dist/web-reply-files.js.map +1 -0
- package/dist/web-schedules.d.ts +12 -0
- package/dist/web-schedules.js +34 -0
- package/dist/web-schedules.js.map +1 -0
- package/dist/web-server-drain.d.ts +10 -0
- package/dist/web-server-drain.js +50 -0
- package/dist/web-server-drain.js.map +1 -0
- package/dist/web-session.d.ts +14 -0
- package/dist/web-session.js +11 -1
- package/dist/web-session.js.map +1 -1
- package/dist/web-settings-patch.d.ts +3 -1
- package/dist/web-settings-patch.js +4 -1
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/web-shell-routes.d.ts +4 -0
- package/dist/web-shell-routes.js +9 -0
- package/dist/web-shell-routes.js.map +1 -1
- package/dist/web-upload.d.ts +135 -7
- package/dist/web-upload.js +315 -51
- package/dist/web-upload.js.map +1 -1
- package/launcher/install-admission.cjs +61 -6
- package/package.json +3 -3
- package/templates/launchd.plist.ejs +3 -1
- package/templates/systemd.service.ejs +2 -1
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #1665: the web preferences that follow the person from device to device (theme, language, the sidebar's arrangement,
|
|
3
|
+
* the HTML-preview opt-in), kept on the host — design: the comment on #1665.
|
|
4
|
+
*
|
|
5
|
+
* - The tour and the first-run card stay per device (the user's call on #1665): they are not keys here.
|
|
6
|
+
* - One bucket per web identity. There is one today: every web session is the operator's (SessionRecord has no
|
|
7
|
+
* owner), so the file is keyed by identity and holds "operator" only; a per-owner key can come later unmigrated.
|
|
8
|
+
* - A fixed registry of keys, each with its own validator. Anything else is refused — "__proto__" and "constructor"
|
|
9
|
+
* are just unknown keys: every lookup is a Map, never an object's prototype chain.
|
|
10
|
+
* - The file (<home>/web-prefs.json, 0600) is replaced atomically. One that cannot be read or parsed is never
|
|
11
|
+
* overwritten: reads give the defaults, writes are refused, until a person looks at it.
|
|
12
|
+
* - `rev` rises with every change; the status frame carries it, so another device re-reads at its next poll.
|
|
13
|
+
*/
|
|
14
|
+
import { readFileSync } from "node:fs";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
import { atomicWriteFileSync } from "./atomic-write.js";
|
|
17
|
+
export const WEB_PREFS_FILE = "web-prefs.json";
|
|
18
|
+
export const OPERATOR = "operator";
|
|
19
|
+
const bad = (invalid) => ({ invalid });
|
|
20
|
+
const isInvalid = (v) => !!v && typeof v === "object" && Object.hasOwn(v, "invalid") && Object.keys(v).length === 1;
|
|
21
|
+
const plain = (v) => !!v && typeof v === "object" && !Array.isArray(v) && Object.getPrototypeOf(v) === Object.prototype;
|
|
22
|
+
const shortText = (v, max) => typeof v === "string" && v.length <= max;
|
|
23
|
+
const name = (v) => typeof v === "string" && v.length >= 1 && v.length <= 128 && !/[\u0000-\u001f\u007f]/.test(v);
|
|
24
|
+
/** Only the listed fields, each present: an extra field is a value this version does not understand. */
|
|
25
|
+
function exactly(v, fields) {
|
|
26
|
+
const keys = Object.keys(v);
|
|
27
|
+
return keys.length === fields.length && fields.every((f) => Object.hasOwn(v, f));
|
|
28
|
+
}
|
|
29
|
+
function uniqueStrings(v, max, each, what) {
|
|
30
|
+
if (!Array.isArray(v) || v.length > max)
|
|
31
|
+
return bad(`${what}: a list of at most ${max}`);
|
|
32
|
+
if (!v.every(each))
|
|
33
|
+
return bad(`${what}: each a name of 1–128 characters`);
|
|
34
|
+
if (new Set(v).size !== v.length)
|
|
35
|
+
return bad(`${what}: no repeats`);
|
|
36
|
+
return v;
|
|
37
|
+
}
|
|
38
|
+
/** #1827: a web person's profile keys (internal: not settable through /ui/prefs, not in its view). */
|
|
39
|
+
const PROFILE_NAME = "profile.name";
|
|
40
|
+
const PROFILE_PLATFORM_NAME = "profile.platformName";
|
|
41
|
+
const PROFILE_KEYS = new Set([PROFILE_NAME, PROFILE_PLATFORM_NAME]);
|
|
42
|
+
/** #1664: at most this many favorites. */
|
|
43
|
+
export const MAX_FAVORITES = 50;
|
|
44
|
+
const FAVORITES = "nav.favorites";
|
|
45
|
+
/** The status chips of the sidebar filter (src/ui/shared/instance-nav.js STATUS_FACETS). */
|
|
46
|
+
const STATUS_FACETS = new Set(["working", "needs", "idle", "stopped", "crashed"]);
|
|
47
|
+
/** Each key's validator: the value to store, or why it is refused. `null` (clear) never reaches one. */
|
|
48
|
+
const REGISTRY = new Map([
|
|
49
|
+
["theme", (v) => (v === "light" || v === "dark" || v === "system" ? v : bad("theme: light, dark or system"))],
|
|
50
|
+
["lang", (v) => (v === "en" || v === "zh-TW" ? v : bad("lang: en or zh-TW"))],
|
|
51
|
+
// The sidebar's drag order: the rows instance-nav.js keeps (group rows have no group_name).
|
|
52
|
+
["nav.order", (v) => {
|
|
53
|
+
if (!Array.isArray(v) || v.length > 1000)
|
|
54
|
+
return bad("nav.order: a list of at most 1000 rows");
|
|
55
|
+
for (const row of v) {
|
|
56
|
+
if (!plain(row) || !exactly(row, ["item_type", "item_name", "sort_index", "group_name"]))
|
|
57
|
+
return bad("nav.order: rows of item_type, item_name, sort_index, group_name");
|
|
58
|
+
const groupRow = row.item_type === "group";
|
|
59
|
+
if (!groupRow && row.item_type !== "instance")
|
|
60
|
+
return bad("nav.order: item_type group or instance");
|
|
61
|
+
if (!name(row.item_name))
|
|
62
|
+
return bad("nav.order: item_name of 1–128 characters");
|
|
63
|
+
if (!Number.isSafeInteger(row.sort_index) || row.sort_index < 0)
|
|
64
|
+
return bad("nav.order: sort_index a whole number from 0");
|
|
65
|
+
if (groupRow ? row.group_name !== null : !name(row.group_name))
|
|
66
|
+
return bad("nav.order: group_name null for a group, the group's name for an instance");
|
|
67
|
+
}
|
|
68
|
+
return v;
|
|
69
|
+
}],
|
|
70
|
+
["nav.collapsed", (v) => uniqueStrings(v, 200, name, "nav.collapsed")],
|
|
71
|
+
["nav.filter", (v) => {
|
|
72
|
+
if (!plain(v) || !exactly(v, ["q", "status", "cli"]))
|
|
73
|
+
return bad("nav.filter: { q, status, cli }");
|
|
74
|
+
if (!shortText(v.q, 200))
|
|
75
|
+
return bad("nav.filter: q at most 200 characters");
|
|
76
|
+
const status = uniqueStrings(v.status, STATUS_FACETS.size, (s) => typeof s === "string" && STATUS_FACETS.has(s), "nav.filter.status");
|
|
77
|
+
if (isInvalid(status))
|
|
78
|
+
return status;
|
|
79
|
+
const cli = uniqueStrings(v.cli, 32, (s) => typeof s === "string" && /^[a-z0-9][a-z0-9_-]{0,31}$/.test(s), "nav.filter.cli");
|
|
80
|
+
if (isInvalid(cli))
|
|
81
|
+
return cli;
|
|
82
|
+
return { q: v.q, status, cli };
|
|
83
|
+
}],
|
|
84
|
+
// #1664: the starred instances, in the order they were starred (or moved). Whether each one exists is checked where
|
|
85
|
+
// the fleet is known (parseWebPrefsPatch's `exists`); a deleted one is dropped on read and when it is removed.
|
|
86
|
+
[FAVORITES, (v) => uniqueStrings(v, MAX_FAVORITES, name, FAVORITES)],
|
|
87
|
+
// HTML previews allowed. Who may write it at all is the route's business (a signed-in session, never the public link).
|
|
88
|
+
["preview.optIn", (v) => (typeof v === "boolean" ? v : bad("preview.optIn: true or false"))],
|
|
89
|
+
// #1722: which name leads where the web shows an instance — its nickname (display name) or its real instance name.
|
|
90
|
+
["names.primary", (v) => (v === "nickname" || v === "instance" ? v : bad("names.primary: nickname or instance"))],
|
|
91
|
+
// #1827: a web person's own profile — the name they chose, and their account's display name at their last sign-in.
|
|
92
|
+
// Kept here so it follows the person (their bucket), but never a /ui/prefs key: read and written only through
|
|
93
|
+
// profile() / setProfileName() / setPlatformName(), and checked in full where it is used (web-profile.ts). The file's
|
|
94
|
+
// own check is only its shape.
|
|
95
|
+
[PROFILE_NAME, (v) => (typeof v === "string" && v.length >= 1 && v.length <= 256 ? v : bad("profile.name"))],
|
|
96
|
+
[PROFILE_PLATFORM_NAME, (v) => (typeof v === "string" && v.length >= 1 && v.length <= 256 ? v : bad("profile.platformName"))],
|
|
97
|
+
]);
|
|
98
|
+
export const WEB_PREF_KEYS = [...REGISTRY.keys()].filter((k) => !PROFILE_KEYS.has(k));
|
|
99
|
+
export function isWebPrefKey(key) { return REGISTRY.has(key) && !PROFILE_KEYS.has(key); }
|
|
100
|
+
/**
|
|
101
|
+
* A request's `set`: own keys only, each known, each valid — or the first reason it is not. All or nothing.
|
|
102
|
+
* `exists`: whether an instance of that name is in the fleet now (#1664: a favorite must be one).
|
|
103
|
+
*/
|
|
104
|
+
export function parseWebPrefsPatch(set, exists) {
|
|
105
|
+
if (!plain(set))
|
|
106
|
+
return { ok: false, error: "set: an object of preference keys" };
|
|
107
|
+
const patch = new Map();
|
|
108
|
+
const keys = Object.keys(set);
|
|
109
|
+
if (!keys.length)
|
|
110
|
+
return { ok: false, error: "set: at least one preference" };
|
|
111
|
+
for (const key of keys) {
|
|
112
|
+
const validate = PROFILE_KEYS.has(key) ? undefined : REGISTRY.get(key);
|
|
113
|
+
if (!validate)
|
|
114
|
+
return { ok: false, error: `unknown_pref: ${key.slice(0, 64)}` };
|
|
115
|
+
const raw = set[key];
|
|
116
|
+
if (raw === null) {
|
|
117
|
+
patch.set(key, null);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const value = validate(raw);
|
|
121
|
+
if (isInvalid(value))
|
|
122
|
+
return { ok: false, error: value.invalid };
|
|
123
|
+
if (key === FAVORITES) {
|
|
124
|
+
const unknown = value.find((n) => !exists || !exists(n));
|
|
125
|
+
if (unknown !== undefined)
|
|
126
|
+
return { ok: false, error: `unknown_instance: ${unknown.slice(0, 64)}` };
|
|
127
|
+
}
|
|
128
|
+
patch.set(key, value);
|
|
129
|
+
}
|
|
130
|
+
return { ok: true, patch };
|
|
131
|
+
}
|
|
132
|
+
export class WebPrefsStore {
|
|
133
|
+
dir;
|
|
134
|
+
opts;
|
|
135
|
+
state = null;
|
|
136
|
+
/**
|
|
137
|
+
* `exists` (#1664): whether an instance of that name is in the fleet now — a favorite that is not is never served,
|
|
138
|
+
* and is never accepted.
|
|
139
|
+
*/
|
|
140
|
+
constructor(dir, opts = {}) {
|
|
141
|
+
this.dir = dir;
|
|
142
|
+
this.opts = opts;
|
|
143
|
+
}
|
|
144
|
+
/** parseWebPrefsPatch against this fleet. */
|
|
145
|
+
parsePatch(set) { return parseWebPrefsPatch(set, this.opts.exists); }
|
|
146
|
+
get path() { return join(this.dir, WEB_PREFS_FILE); }
|
|
147
|
+
load() {
|
|
148
|
+
// A store that could not read its file tries again next time (a permission put back, a disk that recovered); one
|
|
149
|
+
// that did keeps what it read.
|
|
150
|
+
if (this.state && this.state !== "unreadable")
|
|
151
|
+
return this.state;
|
|
152
|
+
const empty = { buckets: new Map(), rev: 0 };
|
|
153
|
+
// Only a file that is definitely not there (ENOENT) is a new, empty store. Anything else — a read refused (EACCES),
|
|
154
|
+
// an I/O error, no place for the file at all, content of another shape — is "unreadable": the defaults are served
|
|
155
|
+
// and nothing is written, so the file is never replaced and a page or status frame never fails because of it.
|
|
156
|
+
try {
|
|
157
|
+
let raw;
|
|
158
|
+
try {
|
|
159
|
+
raw = readFileSync(this.path, "utf8");
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
if (err?.code === "ENOENT")
|
|
163
|
+
return (this.state = empty);
|
|
164
|
+
return (this.state = "unreadable");
|
|
165
|
+
}
|
|
166
|
+
const doc = JSON.parse(raw);
|
|
167
|
+
if (!plain(doc) || doc.version !== 1 || !plain(doc.identities) || !Number.isSafeInteger(doc.rev))
|
|
168
|
+
return (this.state = "unreadable");
|
|
169
|
+
const buckets = new Map();
|
|
170
|
+
for (const id of Object.keys(doc.identities)) {
|
|
171
|
+
const raw = doc.identities[id];
|
|
172
|
+
if (!plain(raw) || !plain(raw.values) || !plain(raw.updated))
|
|
173
|
+
return (this.state = "unreadable");
|
|
174
|
+
const bucket = { values: new Map(), updated: new Map() };
|
|
175
|
+
// A key this version no longer knows, or a value it would refuse now, is left out (never served).
|
|
176
|
+
for (const key of Object.keys(raw.values)) {
|
|
177
|
+
const validate = REGISTRY.get(key);
|
|
178
|
+
if (!validate)
|
|
179
|
+
continue;
|
|
180
|
+
const value = validate(raw.values[key]);
|
|
181
|
+
if (isInvalid(value))
|
|
182
|
+
continue;
|
|
183
|
+
bucket.values.set(key, value);
|
|
184
|
+
const at = raw.updated[key];
|
|
185
|
+
bucket.updated.set(key, typeof at === "number" && Number.isFinite(at) ? at : 0);
|
|
186
|
+
}
|
|
187
|
+
buckets.set(id, bucket);
|
|
188
|
+
}
|
|
189
|
+
return (this.state = { buckets, rev: doc.rev });
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return (this.state = "unreadable");
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
read(identity = OPERATOR) {
|
|
196
|
+
const state = this.load();
|
|
197
|
+
if (state === "unreadable")
|
|
198
|
+
return { values: {}, rev: 0, unreadable: true };
|
|
199
|
+
return this.viewOf(state, identity);
|
|
200
|
+
}
|
|
201
|
+
rev() { const s = this.load(); return s === "unreadable" ? 0 : s.rev; }
|
|
202
|
+
/** #1664: an instance was removed — drop it from every favorites list (a write, and a new rev, only if one had it). */
|
|
203
|
+
forgetInstance(name) {
|
|
204
|
+
const state = this.load();
|
|
205
|
+
if (state === "unreadable")
|
|
206
|
+
return;
|
|
207
|
+
for (const [identity, bucket] of state.buckets) {
|
|
208
|
+
const list = bucket.values.get(FAVORITES);
|
|
209
|
+
if (list && list.includes(name))
|
|
210
|
+
this.write(identity, new Map([[FAVORITES, list.filter((n) => n !== name)]]));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
viewOf(state, identity) {
|
|
214
|
+
const values = Object.create(null);
|
|
215
|
+
const bucket = state.buckets.get(identity);
|
|
216
|
+
if (bucket)
|
|
217
|
+
for (const [k, v] of bucket.values)
|
|
218
|
+
if (!PROFILE_KEYS.has(k))
|
|
219
|
+
values[k] = v;
|
|
220
|
+
// #1664: a favorite whose instance was deleted is dropped (paused or stopped ones are still instances: kept).
|
|
221
|
+
if (Object.hasOwn(values, FAVORITES)) {
|
|
222
|
+
const exists = this.opts.exists;
|
|
223
|
+
values[FAVORITES] = values[FAVORITES].filter((n) => !!exists && exists(n));
|
|
224
|
+
}
|
|
225
|
+
return { values: { ...values }, rev: state.rev };
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Set (or, with null, clear) the patch's keys. `onlyIfUnset`: a device's first upload of what it had locally — only
|
|
229
|
+
* the keys the host does not have yet are taken, so the first device to arrive wins and the host wins from then on.
|
|
230
|
+
*/
|
|
231
|
+
write(identity, patch, opts = {}) {
|
|
232
|
+
const state = this.load();
|
|
233
|
+
if (state === "unreadable")
|
|
234
|
+
return { ok: false, status: 503, error: "prefs_unreadable" };
|
|
235
|
+
const prior = state.buckets.get(identity);
|
|
236
|
+
const next = { values: new Map(prior?.values), updated: new Map(prior?.updated) };
|
|
237
|
+
const at = (this.opts.now ?? Date.now)();
|
|
238
|
+
const changed = [];
|
|
239
|
+
for (const [key, value] of patch) {
|
|
240
|
+
if (opts.onlyIfUnset && next.values.has(key))
|
|
241
|
+
continue;
|
|
242
|
+
if (value === null) {
|
|
243
|
+
if (!next.values.has(key))
|
|
244
|
+
continue;
|
|
245
|
+
next.values.delete(key);
|
|
246
|
+
next.updated.delete(key);
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
if (next.values.has(key) && JSON.stringify(next.values.get(key)) === JSON.stringify(value))
|
|
250
|
+
continue;
|
|
251
|
+
next.values.set(key, value);
|
|
252
|
+
next.updated.set(key, at);
|
|
253
|
+
}
|
|
254
|
+
changed.push(key);
|
|
255
|
+
}
|
|
256
|
+
if (!changed.length)
|
|
257
|
+
return { ok: true, view: this.viewOf(state, identity), changed };
|
|
258
|
+
const buckets = new Map(state.buckets);
|
|
259
|
+
buckets.set(identity, next);
|
|
260
|
+
const candidate = { buckets, rev: state.rev + 1 };
|
|
261
|
+
(this.opts.write ?? ((path, data) => atomicWriteFileSync(path, data, { mode: 0o600 })))(this.path, serialize(candidate));
|
|
262
|
+
// Only once it is on disk: a failed write leaves memory as the file is.
|
|
263
|
+
this.state = candidate;
|
|
264
|
+
return { ok: true, view: this.viewOf(candidate, identity), changed };
|
|
265
|
+
}
|
|
266
|
+
/** #1827: a web person's profile as kept (checked again where it is used); null while the file cannot be read. */
|
|
267
|
+
profile(identity) {
|
|
268
|
+
const state = this.load();
|
|
269
|
+
if (state === "unreadable")
|
|
270
|
+
return null;
|
|
271
|
+
const values = state.buckets.get(identity)?.values;
|
|
272
|
+
const name = values?.get(PROFILE_NAME);
|
|
273
|
+
const platformName = values?.get(PROFILE_PLATFORM_NAME);
|
|
274
|
+
return { ...(name !== undefined ? { name } : {}), ...(platformName !== undefined ? { platformName } : {}) };
|
|
275
|
+
}
|
|
276
|
+
/** #1827: set (or, with null, clear) a person's own name — already validated by the caller (web-profile.ts). */
|
|
277
|
+
setProfileName(identity, name) {
|
|
278
|
+
const w = this.write(identity, new Map([[PROFILE_NAME, name]]));
|
|
279
|
+
return w.ok ? { ok: true } : w;
|
|
280
|
+
}
|
|
281
|
+
/** #1827: their account's display name at a sign-in (a change is a write and a new rev; the same, nothing). */
|
|
282
|
+
setPlatformName(identity, name) {
|
|
283
|
+
const value = name.slice(0, 256);
|
|
284
|
+
if (value)
|
|
285
|
+
this.write(identity, new Map([[PROFILE_PLATFORM_NAME, value]]));
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
function serialize(state) {
|
|
289
|
+
const identities = {};
|
|
290
|
+
for (const [id, b] of state.buckets) {
|
|
291
|
+
identities[id] = { values: Object.fromEntries(b.values), updated: Object.fromEntries(b.updated) };
|
|
292
|
+
}
|
|
293
|
+
// Object.fromEntries with a "__proto__" entry defines an own property, but no such key ever gets in: ids are ours, keys
|
|
294
|
+
// are the registry's.
|
|
295
|
+
return JSON.stringify({ version: 1, rev: state.rev, identities }, null, 2) + "\n";
|
|
296
|
+
}
|
|
297
|
+
//# sourceMappingURL=web-prefs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-prefs.js","sourceRoot":"","sources":["../src/web-prefs.ts"],"names":[],"mappings":"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"}
|
package/dist/web-preview.d.ts
CHANGED
|
@@ -40,7 +40,7 @@ export interface PreviewAvailability {
|
|
|
40
40
|
* previewAvailability; `publicLink` and `notOffered` from the app shell, which asks it for neither the public link nor
|
|
41
41
|
* a page that is not the full app. One list: the page's dictionary is checked against it.
|
|
42
42
|
*/
|
|
43
|
-
export declare const PREVIEW_OFF_CODES: readonly ["fleetOff", "unchecked", "noPort", "notListed", "needOrigin", "needHttps", "sameOrigin", "publicLink", "notOffered"];
|
|
43
|
+
export declare const PREVIEW_OFF_CODES: readonly ["fleetOff", "unchecked", "noPort", "notListed", "needOrigin", "needHttps", "sameOrigin", "publicLink", "publicLinkStarting", "publicLinkPreviewFailed", "notOffered"];
|
|
44
44
|
export type PreviewOffCode = typeof PREVIEW_OFF_CODES[number];
|
|
45
45
|
/** scheme://host[:port] from a Host header the host guard already accepted; a default port is dropped. */
|
|
46
46
|
export declare function dashboardOriginFor(hostHeader: string | undefined, secure: boolean): string | null;
|
|
@@ -78,9 +78,22 @@ export interface PreviewListener {
|
|
|
78
78
|
* it does not answer to → empty 403. No X-Frame-Options (it must be framable by the dashboard), no cookie or
|
|
79
79
|
* header parsing beyond Host, nothing logged per request. Built once: the shim and its headers never change.
|
|
80
80
|
*/
|
|
81
|
+
/**
|
|
82
|
+
* The /frame response for one set of dashboard origins (design §5.1): the shim and its exact headers. Shared by the
|
|
83
|
+
* local preview listener and the public link's own preview gateway (#1723), so both serve the very same frame.
|
|
84
|
+
*/
|
|
85
|
+
export declare function previewFrameResponse(bootId: string, origins: readonly string[]): {
|
|
86
|
+
body: Buffer;
|
|
87
|
+
headers: Record<string, string>;
|
|
88
|
+
};
|
|
81
89
|
export declare function createPreviewListener(opts: {
|
|
82
90
|
settings: PreviewSettings;
|
|
83
91
|
healthPort: number;
|
|
84
92
|
config: HostGuardConfig | null | undefined;
|
|
85
93
|
bootId?: string;
|
|
94
|
+
/**
|
|
95
|
+
* #1723: dashboard origins that may frame the preview for now — the open public link's, when a web.preview_origin is
|
|
96
|
+
* configured (it is used instead of a preview tunnel). Asked per request; never cached past the answer.
|
|
97
|
+
*/
|
|
98
|
+
extraOrigins?(): readonly string[];
|
|
86
99
|
}): PreviewListener;
|
package/dist/web-preview.js
CHANGED
|
@@ -67,7 +67,7 @@ export function previewOriginProblem(value, config) {
|
|
|
67
67
|
* previewAvailability; `publicLink` and `notOffered` from the app shell, which asks it for neither the public link nor
|
|
68
68
|
* a page that is not the full app. One list: the page's dictionary is checked against it.
|
|
69
69
|
*/
|
|
70
|
-
export const PREVIEW_OFF_CODES = ["fleetOff", "unchecked", "noPort", "notListed", "needOrigin", "needHttps", "sameOrigin", "publicLink", "notOffered"];
|
|
70
|
+
export const PREVIEW_OFF_CODES = ["fleetOff", "unchecked", "noPort", "notListed", "needOrigin", "needHttps", "sameOrigin", "publicLink", "publicLinkStarting", "publicLinkPreviewFailed", "notOffered"];
|
|
71
71
|
const DEFAULT_PORT = { "http:": "80", "https:": "443" };
|
|
72
72
|
/** scheme://host[:port] from a Host header the host guard already accepted; a default port is dropped. */
|
|
73
73
|
export function dashboardOriginFor(hostHeader, secure) {
|
|
@@ -203,19 +203,40 @@ export function buildShim(bootId, origins) {
|
|
|
203
203
|
* it does not answer to → empty 403. No X-Frame-Options (it must be framable by the dashboard), no cookie or
|
|
204
204
|
* header parsing beyond Host, nothing logged per request. Built once: the shim and its headers never change.
|
|
205
205
|
*/
|
|
206
|
+
/**
|
|
207
|
+
* The /frame response for one set of dashboard origins (design §5.1): the shim and its exact headers. Shared by the
|
|
208
|
+
* local preview listener and the public link's own preview gateway (#1723), so both serve the very same frame.
|
|
209
|
+
*/
|
|
210
|
+
export function previewFrameResponse(bootId, origins) {
|
|
211
|
+
const body = Buffer.from(buildShim(bootId, origins), "utf8");
|
|
212
|
+
return {
|
|
213
|
+
body,
|
|
214
|
+
headers: {
|
|
215
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
216
|
+
"Content-Security-Policy": previewFrameCsp(origins),
|
|
217
|
+
"Permissions-Policy": PERMISSIONS_POLICY,
|
|
218
|
+
"Referrer-Policy": "no-referrer",
|
|
219
|
+
"Cross-Origin-Resource-Policy": "same-origin",
|
|
220
|
+
"X-Content-Type-Options": "nosniff",
|
|
221
|
+
"Cache-Control": "no-store",
|
|
222
|
+
"Content-Length": String(body.length),
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
}
|
|
206
226
|
export function createPreviewListener(opts) {
|
|
207
227
|
const bootId = opts.bootId ?? randomBytes(16).toString("hex");
|
|
208
228
|
const origins = dashboardOrigins(opts.settings, opts.healthPort, opts.config);
|
|
209
|
-
const body =
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
"
|
|
217
|
-
|
|
218
|
-
|
|
229
|
+
const { body, headers } = previewFrameResponse(bootId, origins);
|
|
230
|
+
let widened = null;
|
|
231
|
+
const frameFor = () => {
|
|
232
|
+
const extra = (opts.extraOrigins?.() ?? []).filter(o => !origins.includes(o));
|
|
233
|
+
if (extra.length === 0)
|
|
234
|
+
return { body, headers };
|
|
235
|
+
const all = [...origins, ...extra];
|
|
236
|
+
const key = all.join(" ");
|
|
237
|
+
if (widened?.key !== key)
|
|
238
|
+
widened = { key, ...previewFrameResponse(bootId, all) };
|
|
239
|
+
return widened;
|
|
219
240
|
};
|
|
220
241
|
const hosts = new Set(LOOPBACK_HOST_NAMES);
|
|
221
242
|
if (opts.settings.origin) {
|
|
@@ -234,8 +255,9 @@ export function createPreviewListener(opts) {
|
|
|
234
255
|
return empty(res, 403);
|
|
235
256
|
if (req.method !== "GET" || req.url !== "/frame")
|
|
236
257
|
return empty(res, 404);
|
|
237
|
-
|
|
238
|
-
res.
|
|
258
|
+
const frame = frameFor();
|
|
259
|
+
res.writeHead(200, frame.headers);
|
|
260
|
+
res.end(frame.body);
|
|
239
261
|
});
|
|
240
262
|
const settings = { ...opts.settings };
|
|
241
263
|
// A listener on port 0 learns its real port when it binds: that is the one a /ui load must be offered.
|
package/dist/web-preview.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web-preview.js","sourceRoot":"","sources":["../src/web-preview.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"web-preview.js","sourceRoot":"","sources":["../src/web-preview.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export declare const WEB_NAME_FALLBACK = "web-user";
|
|
2
|
+
export declare const WEB_NAME_MAX_GRAPHEMES = 32;
|
|
3
|
+
/** The bound account a session was signed in as (its sign-in code's owner); none is the host operator. */
|
|
4
|
+
export interface WebAccount {
|
|
5
|
+
readonly adapterId: string;
|
|
6
|
+
readonly userId: string;
|
|
7
|
+
}
|
|
8
|
+
/** A web person's key in the web preferences: their account (connection and user id), or the operator. */
|
|
9
|
+
export declare function webPersonKey(owner: WebAccount | undefined): string;
|
|
10
|
+
export declare function hasStrayInvisible(s: string): boolean;
|
|
11
|
+
/** A name's UTF-16 length: well under what web-prefs.json keeps (256), so every accepted name loads back. */
|
|
12
|
+
export declare const WEB_NAME_MAX_UNITS = 128;
|
|
13
|
+
export declare function graphemeCount(s: string): number;
|
|
14
|
+
/**
|
|
15
|
+
* A name as a person typed it: NFC, trimmed, inner whitespace one space; then checked. `reserved` says whether a name
|
|
16
|
+
* (compared case-insensitively) belongs to something else here — an instance, its display name, a bot.
|
|
17
|
+
*/
|
|
18
|
+
export declare function validateWebName(raw: unknown, reserved?: (lowered: string) => boolean): {
|
|
19
|
+
ok: true;
|
|
20
|
+
name: string;
|
|
21
|
+
} | {
|
|
22
|
+
ok: false;
|
|
23
|
+
error: string;
|
|
24
|
+
};
|
|
25
|
+
/** The name to show for a stored one: itself if it still passes, else the fallback. */
|
|
26
|
+
export declare function webNameInUse(stored: unknown, reserved?: (lowered: string) => boolean): string;
|
|
27
|
+
/**
|
|
28
|
+
* An account's display name as a web name: what the rules refuse is dropped (a grapheme at a time), whitespace is
|
|
29
|
+
* collapsed, and it is cut to the maximum. What is left must then pass in full (no "via", nothing reserved); if it does
|
|
30
|
+
* not, or nothing is left, it is the fallback.
|
|
31
|
+
*/
|
|
32
|
+
export declare function safeWebName(raw: unknown, reserved?: (lowered: string) => boolean): string;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #1827: the name a web person shows agents and chats, instead of "web-user". Design: #1827 issuecomment-6169960931.
|
|
3
|
+
*
|
|
4
|
+
* - A web person is whoever a session was signed in for: the chat user a `/dashboard` code was sent to privately
|
|
5
|
+
* (`<connection>:<user id>`, recorded on the session), or — a code from `agend web`, a session from before — the host
|
|
6
|
+
* operator. They keep one name and one id on every device; the id is random (web-prefs.ts), never the name.
|
|
7
|
+
* - The name is self-asserted display text. It grants nothing: web messages stay `via web` with no verified actor
|
|
8
|
+
* (#1756). What it must not do is pose as something else — a platform header, a mention, an instance, AgEnD itself —
|
|
9
|
+
* so it is refused (never silently rewritten) unless it is plain: letters, marks, digits, spaces, emoji, `. - _ ' ·`.
|
|
10
|
+
* - A stored name is checked again where it is used (the file can be edited, an instance can be named after it later):
|
|
11
|
+
* one that no longer passes reads as "web-user".
|
|
12
|
+
*/
|
|
13
|
+
import { OPERATOR } from "./web-prefs.js";
|
|
14
|
+
export const WEB_NAME_FALLBACK = "web-user";
|
|
15
|
+
export const WEB_NAME_MAX_GRAPHEMES = 32;
|
|
16
|
+
/** A web person's key in the web preferences: their account (connection and user id), or the operator. */
|
|
17
|
+
export function webPersonKey(owner) {
|
|
18
|
+
return owner ? `person:${owner.adapterId}:${owner.userId}` : OPERATOR;
|
|
19
|
+
}
|
|
20
|
+
// Bidi controls (ALM, LRM/RLM, embeddings and overrides, isolates) and zero-width / invisible formatting characters.
|
|
21
|
+
// eslint-disable-next-line no-misleading-character-class
|
|
22
|
+
const INVISIBLE = /[\u061c\u200b-\u200f\u202a-\u202e\u2060-\u2064\u2066-\u2069\ufeff\u00ad]/u;
|
|
23
|
+
// eslint-disable-next-line no-control-regex
|
|
24
|
+
const CONTROL = /[\u0000-\u001f\u007f-\u009f\u2028\u2029]/u;
|
|
25
|
+
/**
|
|
26
|
+
* What a name may contain. Letters, combining marks, digits, spaces; emoji (pictographs with variation selectors, skin
|
|
27
|
+
* tones and keycaps — not joined sequences: a zero-width joiner is invisible, refused above); and `. - _ ' ·`. Everything else — `[ ] < > @ : , # / \` among it — is refused:
|
|
28
|
+
* those are what a `[user:… via …, id:…]` header, a `<@id>` mention or a path would be forged with.
|
|
29
|
+
*/
|
|
30
|
+
const ALLOWED = /^(?:[\p{L}\p{M}\p{N} ._'\u00b7-]|\p{Extended_Pictographic}|[\ufe0e\ufe0f]|[\u{1f3fb}-\u{1f3ff}]|\u20e3)+$/u;
|
|
31
|
+
/**
|
|
32
|
+
* Default-ignorable characters render as nothing (or leave what they follow unchanged): a name spelled with one looks
|
|
33
|
+
* like a name it is not ("Pri"+CGJ+"sm" is not "prism" to a comparison, and reads as it). They are refused everywhere —
|
|
34
|
+
* bidi and zero-width ones included — except an emoji's own presentation selector: VS15/VS16 on a pictograph, or in a
|
|
35
|
+
* keycap (digit, # or * + VS16 + U+20E3). (#1833 review.)
|
|
36
|
+
*/
|
|
37
|
+
const DEFAULT_IGNORABLE = /\p{Default_Ignorable_Code_Point}/u;
|
|
38
|
+
const PICTOGRAPH = /\p{Extended_Pictographic}/u;
|
|
39
|
+
const VS15 = String.fromCharCode(0xfe0e), VS16 = String.fromCharCode(0xfe0f), KEYCAP = String.fromCharCode(0x20e3);
|
|
40
|
+
export function hasStrayInvisible(s) {
|
|
41
|
+
const cps = Array.from(s);
|
|
42
|
+
for (let i = 0; i < cps.length; i++) {
|
|
43
|
+
const c = cps[i];
|
|
44
|
+
if (!DEFAULT_IGNORABLE.test(c))
|
|
45
|
+
continue;
|
|
46
|
+
const prev = cps[i - 1] ?? "";
|
|
47
|
+
if ((c === VS15 || c === VS16) && (PICTOGRAPH.test(prev) || (/^[0-9#*]$/.test(prev) && cps[i + 1] === KEYCAP)))
|
|
48
|
+
continue;
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
/** A name's UTF-16 length: well under what web-prefs.json keeps (256), so every accepted name loads back. */
|
|
54
|
+
export const WEB_NAME_MAX_UNITS = 128;
|
|
55
|
+
const segmenter = typeof Intl !== "undefined" && "Segmenter" in Intl ? new Intl.Segmenter(undefined, { granularity: "grapheme" }) : null;
|
|
56
|
+
export function graphemeCount(s) {
|
|
57
|
+
if (segmenter) {
|
|
58
|
+
let n = 0;
|
|
59
|
+
for (const _ of segmenter.segment(s))
|
|
60
|
+
n++;
|
|
61
|
+
return n;
|
|
62
|
+
}
|
|
63
|
+
return Array.from(s).length;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A name as a person typed it: NFC, trimmed, inner whitespace one space; then checked. `reserved` says whether a name
|
|
67
|
+
* (compared case-insensitively) belongs to something else here — an instance, its display name, a bot.
|
|
68
|
+
*/
|
|
69
|
+
export function validateWebName(raw, reserved = () => false) {
|
|
70
|
+
if (typeof raw !== "string")
|
|
71
|
+
return { ok: false, error: "name: text" };
|
|
72
|
+
if (CONTROL.test(raw))
|
|
73
|
+
return { ok: false, error: "name: one line, without control characters" };
|
|
74
|
+
if (INVISIBLE.test(raw) || hasStrayInvisible(raw))
|
|
75
|
+
return { ok: false, error: "name: no invisible or text-direction characters" };
|
|
76
|
+
const name = raw.normalize("NFC").trim().replace(/\s+/gu, " ");
|
|
77
|
+
const n = graphemeCount(name);
|
|
78
|
+
if (n < 1)
|
|
79
|
+
return { ok: false, error: "name: at least one character" };
|
|
80
|
+
if (n > WEB_NAME_MAX_GRAPHEMES)
|
|
81
|
+
return { ok: false, error: `name: at most ${WEB_NAME_MAX_GRAPHEMES} characters (${n} given)` };
|
|
82
|
+
if (name.length > WEB_NAME_MAX_UNITS)
|
|
83
|
+
return { ok: false, error: "name: too long (too many accents or joined marks)" };
|
|
84
|
+
if (!ALLOWED.test(name))
|
|
85
|
+
return { ok: false, error: "name: letters, digits, spaces, emoji and . - _ ' · only" };
|
|
86
|
+
const lowered = name.toLocaleLowerCase("en-US");
|
|
87
|
+
if (/(^|[\s._'\u00b7-])via($|[\s._'\u00b7-])/u.test(lowered))
|
|
88
|
+
return { ok: false, error: "name: the word \"via\" is reserved (it would read as a platform)" };
|
|
89
|
+
if (lowered === WEB_NAME_FALLBACK || lowered === "general" || lowered.startsWith("agend")) {
|
|
90
|
+
return { ok: false, error: "name: reserved by AgEnD" };
|
|
91
|
+
}
|
|
92
|
+
if (reserved(lowered))
|
|
93
|
+
return { ok: false, error: "name: already the name of an agent or bot here" };
|
|
94
|
+
return { ok: true, name };
|
|
95
|
+
}
|
|
96
|
+
/** The name to show for a stored one: itself if it still passes, else the fallback. */
|
|
97
|
+
export function webNameInUse(stored, reserved = () => false) {
|
|
98
|
+
if (typeof stored !== "string")
|
|
99
|
+
return WEB_NAME_FALLBACK;
|
|
100
|
+
const v = validateWebName(stored, reserved);
|
|
101
|
+
return v.ok && v.name === stored ? v.name : WEB_NAME_FALLBACK;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* An account's display name as a web name: what the rules refuse is dropped (a grapheme at a time), whitespace is
|
|
105
|
+
* collapsed, and it is cut to the maximum. What is left must then pass in full (no "via", nothing reserved); if it does
|
|
106
|
+
* not, or nothing is left, it is the fallback.
|
|
107
|
+
*/
|
|
108
|
+
export function safeWebName(raw, reserved = () => false) {
|
|
109
|
+
if (typeof raw !== "string")
|
|
110
|
+
return WEB_NAME_FALLBACK;
|
|
111
|
+
let kept = "";
|
|
112
|
+
const pieces = segmenter ? Array.from(segmenter.segment(raw.normalize("NFC")), (p) => p.segment) : Array.from(raw.normalize("NFC"));
|
|
113
|
+
let n = 0;
|
|
114
|
+
for (const g of pieces) {
|
|
115
|
+
if (n >= WEB_NAME_MAX_GRAPHEMES)
|
|
116
|
+
break;
|
|
117
|
+
if (/\s/u.test(g)) {
|
|
118
|
+
if (kept && !kept.endsWith(" ")) {
|
|
119
|
+
kept += " ";
|
|
120
|
+
n++;
|
|
121
|
+
}
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (CONTROL.test(g) || INVISIBLE.test(g) || hasStrayInvisible(g) || !ALLOWED.test(g))
|
|
125
|
+
continue;
|
|
126
|
+
kept += g;
|
|
127
|
+
n++;
|
|
128
|
+
}
|
|
129
|
+
const v = validateWebName(kept, reserved);
|
|
130
|
+
return v.ok ? v.name : WEB_NAME_FALLBACK;
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=web-profile.js.map
|