oysterun 1.6.0-beta.2 → 1.6.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/bin/oysterun.mjs +64 -17
- package/dev/client/connectivity/completed-message-neighbor-click.mjs +78 -0
- package/dev/client/connectivity/completed-message-neighbor-contract.mjs +205 -0
- package/dev/client/connectivity/completed-message-visibility.d.mts +4 -4
- package/dev/client/connectivity/completed-message-visibility.mjs +14 -11
- package/dev/client/connectivity/desktop-chat-sidebar-preference.mjs +57 -0
- package/dev/client/connectivity/managed-host-browser.mjs +10 -0
- package/dev/client/connectivity/session-navigation-intent.mjs +189 -0
- package/dev/client/connectivity/session-transition-state.mjs +18 -6
- package/dev/client/connectivity/test-completed-message-neighbor-click.mjs +180 -0
- package/dev/client/web/index.html +5109 -546
- package/dev/client/web-chat/dist/assets/{ReactPrism-BwJhidYf.js → ReactPrism-ClyFITK3.js} +1 -1
- package/dev/client/web-chat/dist/assets/ReactPrism-ClyFITK3.js.map +1 -0
- package/dev/client/web-chat/dist/assets/SessionsSetupApp-CblAxkYh.css +1 -0
- package/dev/client/web-chat/dist/assets/SessionsSetupApp-DjPZ5ktj.js +6442 -0
- package/dev/client/web-chat/dist/assets/SessionsSetupApp-DjPZ5ktj.js.map +1 -0
- package/dev/client/web-chat/dist/assets/index-B9fWrEk-.js.map +1 -1
- package/dev/client/web-chat/dist/assets/index-Bg19BOiF.js +9 -0
- package/dev/client/web-chat/dist/assets/index-Bg19BOiF.js.map +1 -0
- package/dev/client/web-chat/dist/assets/{index--1xmQ99x.js → index-CH6MWK2I.js} +2 -2
- package/dev/client/web-chat/dist/assets/index-CH6MWK2I.js.map +1 -0
- package/dev/client/web-chat/dist/assets/index-L3a5OiDt.js +2 -0
- package/dev/client/web-chat/dist/assets/index-L3a5OiDt.js.map +1 -0
- package/dev/client/web-chat/dist/assets/matrix-entry-CbU0Ygxr.js +594 -0
- package/dev/client/web-chat/dist/assets/matrix-entry-CbU0Ygxr.js.map +1 -0
- package/dev/client/web-chat/dist/assets/matrix-entry-W9GaZpsV.css +10 -0
- package/dev/client/web-chat/dist/assets/{pdf-ZjtalfgI.js → pdf-CxPJMrHN.js} +5 -5
- package/dev/client/web-chat/dist/assets/pdf-CxPJMrHN.js.map +1 -0
- package/dev/client/web-chat/dist/assets/session-action-menu-contract-DcS82CRX.css +1 -0
- package/dev/client/web-chat/dist/assets/web-9H0iDFny.js +1 -0
- package/dev/client/web-chat/dist/assets/web-9H0iDFny.js.map +1 -0
- package/dev/client/web-chat/dist/assets/web-DAe240E6.js +1 -0
- package/dev/client/web-chat/dist/assets/web-DAe240E6.js.map +1 -0
- package/dev/client/web-chat/dist/index.html +1 -2
- package/docs/routec/dev/cloud_deplyments.md +5 -0
- package/docs/routec/dev/npm_native_dependency_install.md +6 -2
- package/docs/routec/dev/npmjs_publish.md +55 -19
- package/docs/routec/dev/p262_setup_input_file.md +117 -0
- package/docs/routec/host2_turn_started_idempotency_diagnostics.md +8 -7
- package/docs/routec/host_setup.md +39 -10
- package/docs/routec/oysterun_operation_interface_spec.md +78 -2
- package/docs/routec/provider_contract.md +35 -5
- package/host-service/README.md +36 -0
- package/host-service/adapters/claude-code-adapter.mjs +48 -5
- package/host-service/adapters/codex-app-server-adapter.mjs +320 -18
- package/host-service/adapters/debug-fixture-provider-adapter.mjs +102 -1
- package/host-service/adapters/debug-large-tool-spillover-provider-adapter.mjs +2 -0
- package/host-service/adapters/debug-p135-codex-replay-provider-adapter.mjs +2 -0
- package/host-service/adapters/debug-routec-structural-replay-provider-adapter.mjs +2 -0
- package/host-service/agent-config.mjs +16 -5
- package/host-service/apns-complete-message-dispatcher.mjs +1 -0
- package/host-service/assets/product-skills/Oysterun/modules/oysterun-find-context/SKILL.md +24 -0
- package/host-service/assets/product-skills/Oysterun/modules/oysterun-session-chat/SKILL.md +66 -5
- package/host-service/assets/product-skills/Oysterun/modules/oysterun-sessions/SKILL.md +81 -0
- package/host-service/assets/product-skills/Oysterun/modules/oysterun-website/SKILL.md +7 -0
- package/host-service/assets/product-skills/oysterun-find-context/SKILL.md +24 -0
- package/host-service/assets/product-skills/oysterun-session-chat/SKILL.md +66 -5
- package/host-service/assets/product-skills/oysterun-sessions/SKILL.md +81 -0
- package/host-service/assets/product-skills/oysterun-website/SKILL.md +7 -0
- package/host-service/callback-durable-store.mjs +354 -47
- package/host-service/callback-foundation.mjs +21 -1
- package/host-service/callback-host-integration.mjs +345 -30
- package/host-service/claude-acp-model-discovery.mjs +23 -18
- package/host-service/cli/assert-installer-reinstall-target.mjs +9 -0
- package/host-service/cli/installer-recovery-guidance.mjs +20 -0
- package/host-service/cli/prepare-restart-restore.mjs +2 -0
- package/host-service/cli/product-cli.mjs +34 -33
- package/host-service/cli/reconcile-initial-preflight.mjs +19 -0
- package/host-service/cli/recover-installer-lifecycle.mjs +24 -0
- package/host-service/cli/run-host-lifecycle.mjs +2 -0
- package/host-service/cli/run-staged-lifecycle-recovery.mjs +161 -0
- package/host-service/codex-model-discovery.mjs +23 -18
- package/host-service/complete-cli-uninstall.mjs +176 -0
- package/host-service/complete-message-notification-predicate.mjs +8 -6
- package/host-service/complete-message-notification-title.mjs +4 -0
- package/host-service/completed-session-unread.mjs +425 -42
- package/host-service/completed-turn-navigation.mjs +444 -144
- package/host-service/completed-turn-notification-observer.mjs +116 -0
- package/host-service/completed-turn-service.mjs +445 -0
- package/host-service/config.mjs +115 -9
- package/host-service/desired-open-session-state.mjs +331 -0
- package/host-service/fixtures/p218-completed-turn-notification.json +11 -0
- package/host-service/frp-agent.mjs +9 -9
- package/host-service/grok-acp-model-discovery.mjs +31 -33
- package/host-service/host-lifecycle-recovery.mjs +37 -0
- package/host-service/host-port-resolution.mjs +98 -0
- package/host-service/host-runtime-diagnostics.mjs +18 -1
- package/host-service/initial-preflight-recovery.mjs +71 -0
- package/host-service/installer-recovery-guidance.mjs +85 -0
- package/host-service/installer-reinstall-bindings.mjs +49 -0
- package/host-service/installer-reinstall-target.mjs +49 -0
- package/host-service/legacy-continuity-observation.mjs +33 -0
- package/host-service/lifecycle-compat-cli.mjs +13 -0
- package/host-service/lifecycle-compat-preload.mjs +89 -0
- package/host-service/lifecycle-compatibility.mjs +164 -0
- package/host-service/lifecycle-continuity-assets.mjs +222 -0
- package/host-service/lifecycle-continuity-probe.mjs +97 -0
- package/host-service/lifecycle-continuity-projection.mjs +29 -0
- package/host-service/lifecycle-continuity-runner.mjs +192 -0
- package/host-service/lifecycle-continuity.mjs +493 -0
- package/host-service/lifecycle-service-render-bundle.mjs +120 -0
- package/host-service/lifecycle-user-path.mjs +34 -0
- package/host-service/matrix-credential-issuer.mjs +31 -1
- package/host-service/matrix-event-correlator.mjs +166 -27
- package/host-service/matrix-facade.mjs +71 -18
- package/host-service/matrix-room-binding.mjs +24 -0
- package/host-service/matrix-transcript-read-adapter.mjs +17 -3
- package/host-service/model-discovery-guardian.mjs +40 -0
- package/host-service/model-discovery-process.mjs +141 -0
- package/host-service/onboard-lifecycle-status.mjs +28 -0
- package/host-service/onboard.mjs +479 -0
- package/host-service/package.json +2 -1
- package/host-service/portable-scheduler-definitions.mjs +28 -0
- package/host-service/product-runtime-host-origin.mjs +22 -0
- package/host-service/provider-model-params.mjs +14 -17
- package/host-service/provider-model-refresh-runner.mjs +124 -169
- package/host-service/provider-model-refresh-schedule.mjs +11 -0
- package/host-service/provider-resume-contract.mjs +9 -3
- package/host-service/restart-restore-state.mjs +25 -0
- package/host-service/retained-host-state-holder.mjs +57 -0
- package/host-service/routec-matrix-sqlite-store.mjs +155 -10
- package/host-service/routec-matrix-storage-adapter.mjs +207 -25
- package/host-service/routec-matrix-storage-paths.mjs +12 -0
- package/host-service/routec-request-delay-window.mjs +55 -0
- package/host-service/scheduler-acp-provider-runner.mjs +2 -1
- package/host-service/scheduler-direct-provider-runner.mjs +71 -9
- package/host-service/scheduler-run-timeout-policy.mjs +85 -0
- package/host-service/scheduler-service.mjs +96 -9
- package/host-service/scheduler-store.mjs +9 -2
- package/host-service/server.mjs +2150 -536
- package/host-service/service-definition-migration.mjs +10 -3
- package/host-service/session-collection-service.mjs +320 -0
- package/host-service/session-history.mjs +99 -15
- package/host-service/session-list-preview-service.mjs +87 -12
- package/host-service/session-list-projection-service.mjs +182 -0
- package/host-service/session-loop-definitions.mjs +702 -75
- package/host-service/session-manager.mjs +473 -38
- package/host-service/session-switcher-projection.mjs +11 -3
- package/host-service/session-tags.mjs +451 -0
- package/host-service/session-transcript-target.mjs +103 -0
- package/host-service/setup-input-file.mjs +91 -0
- package/host-service/setup-provider-model-defaults.mjs +53 -0
- package/host-service/setup.mjs +133 -101
- package/host-service/stopped-user-host-package.mjs +60 -0
- package/host-service/test-fixtures/p236-navigation-original.mjs +500 -0
- package/host-service/update-reminder-state.mjs +7 -0
- package/package.json +1 -1
- package/static/oysterun-social-preview.png +0 -0
- package/static/site/assets/oysterun-social-preview.png +0 -0
- package/static/site/blog/en/index.html +17 -0
- package/static/site/blog/generated_manifest.json +9 -9
- package/static/site/blog/index.html +18 -0
- package/static/site/blog/voice-input-model-experiment/en/index.html +17 -0
- package/static/site/blog/voice-input-model-experiment/zh/index.html +17 -0
- package/static/site/blog/zh/index.html +17 -0
- package/static/site/docs/choose-and-recover-a-provider/en/index.html +17 -0
- package/static/site/docs/choose-and-recover-a-provider/zh/index.html +17 -0
- package/static/site/docs/configure-session-files-and-settings/en/index.html +17 -0
- package/static/site/docs/configure-session-files-and-settings/zh/index.html +17 -0
- package/static/site/docs/en/index.html +17 -0
- package/static/site/docs/generated_manifest.json +60 -60
- package/static/site/docs/host-authentication-and-connection/en/index.html +17 -0
- package/static/site/docs/host-authentication-and-connection/zh/index.html +17 -0
- package/static/site/docs/index.html +18 -0
- package/static/site/docs/install-and-use-a-skill/en/index.html +17 -0
- package/static/site/docs/install-and-use-a-skill/zh/index.html +17 -0
- package/static/site/docs/read-large-tool-results/en/index.html +17 -0
- package/static/site/docs/read-large-tool-results/zh/index.html +17 -0
- package/static/site/docs/recover-from-a-host-runtime-error/en/index.html +17 -0
- package/static/site/docs/recover-from-a-host-runtime-error/zh/index.html +17 -0
- package/static/site/docs/restore-work-after-a-host-restart/en/index.html +17 -0
- package/static/site/docs/restore-work-after-a-host-restart/zh/index.html +17 -0
- package/static/site/docs/send-images-and-media/en/index.html +17 -0
- package/static/site/docs/send-images-and-media/zh/index.html +17 -0
- package/static/site/docs/set-up-and-understand-a-host/en/index.html +17 -0
- package/static/site/docs/set-up-and-understand-a-host/zh/index.html +17 -0
- package/static/site/docs/understand-agent-activity/en/index.html +19 -2
- package/static/site/docs/understand-agent-activity/en/index.md +2 -2
- package/static/site/docs/understand-agent-activity/zh/index.html +19 -2
- package/static/site/docs/understand-agent-activity/zh/index.md +2 -2
- package/static/site/docs/use-the-oysterun-cli-safely/en/index.html +17 -0
- package/static/site/docs/use-the-oysterun-cli-safely/zh/index.html +17 -0
- package/static/site/docs/zh/index.html +17 -0
- package/static/site/en/index.html +18 -1
- package/static/site/en/index.md +1 -1
- package/static/site/faq/en/index.html +17 -0
- package/static/site/faq/index.html +18 -0
- package/static/site/faq/zh/index.html +17 -0
- package/static/site/getting-started/en/index.html +23 -1
- package/static/site/getting-started/en/index.md +7 -1
- package/static/site/getting-started/index.html +18 -0
- package/static/site/getting-started/zh/index.html +23 -1
- package/static/site/getting-started/zh/index.md +7 -1
- package/static/site/index.html +18 -0
- package/static/site/install.sh +676 -127
- package/static/site/overview/en/index.html +18 -1
- package/static/site/overview/en/index.md +2 -2
- package/static/site/overview/index.html +18 -0
- package/static/site/overview/zh/index.html +18 -1
- package/static/site/overview/zh/index.md +2 -2
- package/static/site/privacy/en/index.html +17 -0
- package/static/site/privacy/index.html +18 -0
- package/static/site/privacy/zh/index.html +17 -0
- package/static/site/zh/index.html +18 -1
- package/static/site/zh/index.md +1 -1
- package/static/site_source/content/tutorials/published/understand-agent-activity/en/article.html +2 -2
- package/static/site_source/content/tutorials/published/understand-agent-activity/zh/article.html +2 -2
- package/static/site_source/downloads/install.sh +676 -127
- package/static/site_source/manifests/host_install.json +2 -10
- package/static/site_source/pages/getting-started/en.html +6 -1
- package/static/site_source/pages/getting-started/zh.html +6 -1
- package/static/site_source/pages/home/en.html +1 -1
- package/static/site_source/pages/home/zh.html +1 -1
- package/static/site_source/pages/home_main.html +3 -2
- package/static/site_source/pages/overview/en.html +1 -1
- package/static/site_source/pages/overview/zh.html +1 -1
- package/tool_scripts/install_oysterun_update.mjs +78 -13
- package/tool_scripts/oysterun_continuity_transaction.mjs +438 -0
- package/tool_scripts/oysterun_npm_package_manifest.mjs +32 -0
- package/tool_scripts/oysterun_release_service.sh +5 -0
- package/tool_scripts/oysterun_service_common.sh +59 -24
- package/tool_scripts/oysterun_setup_input.sh +97 -0
- package/tool_scripts/oysterun_update_restart_runner.mjs +19 -3
- package/tool_scripts/p233_legacy_bootstrap.mjs +254 -0
- package/tool_scripts/resolve_user_host_port.mjs +15 -0
- package/tool_scripts/restart_oysterun.sh +8 -2
- package/tool_scripts/stack_dashboard_credentials.mjs +2 -1
- package/tool_scripts/status_oysterun_launch_agents.sh +13 -1
- package/tool_scripts/sync_host_stack_config.mjs +9 -6
- package/tool_scripts/test_environment_remote_cloud_deprovision.mjs +419 -0
- package/tool_scripts/uninstall_oysterun_launch_agents.sh +3 -5
- package/dev/client/web-chat/dist/assets/ReactPrism-BwJhidYf.js.map +0 -1
- package/dev/client/web-chat/dist/assets/index--1xmQ99x.js.map +0 -1
- package/dev/client/web-chat/dist/assets/index-Ce9NZBZf.js +0 -129
- package/dev/client/web-chat/dist/assets/index-Ce9NZBZf.js.map +0 -1
- package/dev/client/web-chat/dist/assets/index-DzIClZ3T.js +0 -2
- package/dev/client/web-chat/dist/assets/index-DzIClZ3T.js.map +0 -1
- package/dev/client/web-chat/dist/assets/index-NYnaSGBb.css +0 -1
- package/dev/client/web-chat/dist/assets/pdf-ZjtalfgI.js.map +0 -1
- package/dev/client/web-chat/dist/assets/web-BzHcZK2M.js +0 -1
- package/dev/client/web-chat/dist/assets/web-BzHcZK2M.js.map +0 -1
- package/dev/client/web-chat/dist/assets/web-c70YaskK.js +0 -1
- package/dev/client/web-chat/dist/assets/web-c70YaskK.js.map +0 -1
package/bin/oysterun.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { existsSync, readFileSync
|
|
3
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
import { spawnSync } from "node:child_process";
|
|
5
5
|
import { dirname, join, resolve } from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
@@ -13,9 +13,12 @@ import {
|
|
|
13
13
|
runProductCli,
|
|
14
14
|
} from "../host-service/cli/product-cli.mjs";
|
|
15
15
|
|
|
16
|
+
import { preflightCompleteUninstall, executeCompleteUninstall } from "../host-service/complete-cli-uninstall.mjs";
|
|
17
|
+
|
|
16
18
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
19
|
const repoRoot = resolve(__dirname, "..");
|
|
18
20
|
const setupScript = join(repoRoot, "host-service", "setup.mjs");
|
|
21
|
+
const onboardScript = join(repoRoot, "host-service", "onboard.mjs");
|
|
19
22
|
const releaseServiceScript = join(
|
|
20
23
|
repoRoot,
|
|
21
24
|
"tool_scripts",
|
|
@@ -39,11 +42,11 @@ const serviceCommands = new Map([
|
|
|
39
42
|
["service:uninstall", "uninstall"],
|
|
40
43
|
]);
|
|
41
44
|
|
|
42
|
-
function run(command, args) {
|
|
45
|
+
function run(command, args, { setupInput = false } = {}) {
|
|
43
46
|
const result = spawnSync(command, args, {
|
|
44
47
|
cwd: repoRoot,
|
|
45
48
|
env: process.env,
|
|
46
|
-
stdio: "inherit",
|
|
49
|
+
stdio: setupInput && process.env.OYSTERUN_SETUP_INPUT_FD !== undefined ? ["inherit", "inherit", "inherit", 3, 4] : "inherit",
|
|
47
50
|
});
|
|
48
51
|
if (result.error) {
|
|
49
52
|
console.error(`oysterun: failed to run ${command}: ${result.error.message}`);
|
|
@@ -96,13 +99,16 @@ function parseUninstallArgs(args = []) {
|
|
|
96
99
|
options.keepConfig = true;
|
|
97
100
|
} else if (arg === "--confirm") {
|
|
98
101
|
const value = args[index + 1];
|
|
99
|
-
if (!value) {
|
|
102
|
+
if (!value || value.startsWith("--")) {
|
|
100
103
|
throw new Error("--confirm requires a value");
|
|
101
104
|
}
|
|
105
|
+
if (value !== "DELETE") throw new Error("config deletion requires --confirm DELETE exactly");
|
|
102
106
|
options.confirm = value;
|
|
103
107
|
index += 1;
|
|
104
108
|
} else if (arg.startsWith("--confirm=")) {
|
|
105
|
-
|
|
109
|
+
const value = arg.slice("--confirm=".length);
|
|
110
|
+
if (value !== "DELETE") throw new Error("config deletion requires --confirm DELETE exactly");
|
|
111
|
+
options.confirm = value;
|
|
106
112
|
} else if (arg === "--stack") {
|
|
107
113
|
const value = args[index + 1];
|
|
108
114
|
if (!value) {
|
|
@@ -195,6 +201,7 @@ Usage:
|
|
|
195
201
|
oysterun
|
|
196
202
|
oysterun --version
|
|
197
203
|
oysterun setup [options]
|
|
204
|
+
oysterun onboard [--package-changed] [--channel latest|beta]
|
|
198
205
|
oysterun show-qr
|
|
199
206
|
oysterun uninstall [--confirm DELETE] [--keep-config] [--dry-run]
|
|
200
207
|
oysterun service:start
|
|
@@ -213,6 +220,20 @@ Usage:
|
|
|
213
220
|
Product modules:
|
|
214
221
|
auth, sessions, chat, scheduler, mail, notifications, website, telegram
|
|
215
222
|
|
|
223
|
+
Chat transcript reads:
|
|
224
|
+
chat recent|messages|messages-around|search [--session-id <exact_id>]
|
|
225
|
+
A verified live Agent can read its Agent's stopped/history target without
|
|
226
|
+
starting it. History names are unsupported; use an exact retained Session ID.
|
|
227
|
+
chat messages --session-id <history_id> [--limit 20] [--next-batch <opaque>]
|
|
228
|
+
History page limits are 1..100; live messages retain --after-seq.
|
|
229
|
+
History read authority does not authorize send, loop, profile or lifecycle.
|
|
230
|
+
|
|
231
|
+
Session tags:
|
|
232
|
+
sessions start|resume|branch-resume [--tags 'first,second' | --clear-tags]
|
|
233
|
+
sessions profile update [--tags 'first,second' | --clear-tags]
|
|
234
|
+
--clear-tags creates or saves an untagged Session; empty --tags is invalid.
|
|
235
|
+
Omitted start tags inside a Session copy the authenticated parent's name.
|
|
236
|
+
|
|
216
237
|
First run:
|
|
217
238
|
If Host config is missing or incomplete, 'oysterun' enters setup.
|
|
218
239
|
Release setup uses Oysterun Managed Tunnel by default.
|
|
@@ -236,8 +257,13 @@ Behavior:
|
|
|
236
257
|
- Stops/removes managed Oysterun service state where available.
|
|
237
258
|
- Preserves the config directory unless --confirm DELETE is supplied exactly.
|
|
238
259
|
- Does not delete the Default Browse Root or agent folders.
|
|
239
|
-
-
|
|
240
|
-
|
|
260
|
+
- Exact --confirm DELETE also removes the same global CLI package that launched
|
|
261
|
+
this command using its matching Node/npm and explicit prefix.
|
|
262
|
+
- Without DELETE (including --keep-config), preserves config and CLI package.
|
|
263
|
+
- --keep-config and --confirm DELETE cannot be combined.
|
|
264
|
+
- --dry-run prints the selected services/config and exact CLI/npm/prefix plan.
|
|
265
|
+
- --stack all removes production and staging services, but only the selected
|
|
266
|
+
config directory and the launching CLI package.
|
|
241
267
|
`);
|
|
242
268
|
}
|
|
243
269
|
|
|
@@ -273,6 +299,31 @@ function runUninstall(args = []) {
|
|
|
273
299
|
const configDir = dirname(configPath);
|
|
274
300
|
const defaultBrowsePath = readConfiguredDefaultBrowsePath();
|
|
275
301
|
const deleteConfig = options.confirm === "DELETE";
|
|
302
|
+
if (deleteConfig) {
|
|
303
|
+
try {
|
|
304
|
+
const plan = preflightCompleteUninstall({
|
|
305
|
+
cliPath: fileURLToPath(import.meta.url), configDir,
|
|
306
|
+
stack: options.stack || process.env.OYSTERUN_RELEASE_STACK || "production",
|
|
307
|
+
});
|
|
308
|
+
console.log("Oysterun complete uninstall plan:");
|
|
309
|
+
console.log(` Services: ${plan.services.map(t => `${t.name} (${t.label}, port ${t.port || "unconfigured"})`).join(", ")}`);
|
|
310
|
+
console.log(` Config directory: delete ${plan.config}`);
|
|
311
|
+
console.log(` CLI package: remove ${plan.root}`);
|
|
312
|
+
console.log(` npm: ${plan.identity.node_path} ${plan.identity.npm_cli_path} uninstall ${plan.npmArgs.join(" ")} oysterun`);
|
|
313
|
+
console.log(` Prefix: ${plan.prefix}; OS user: ${plan.user.name} (uid ${plan.user.uid})`);
|
|
314
|
+
console.log(" Default Browse Root and agent folders are never deleted by this command.");
|
|
315
|
+
if (options.dryRun) {
|
|
316
|
+
console.log("Dry run complete: no service, config or CLI changes were made.");
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
process.exitCode = executeCompleteUninstall(plan).exitCode;
|
|
320
|
+
} catch (error) {
|
|
321
|
+
console.error(`oysterun uninstall preflight: ${error.message}`);
|
|
322
|
+
console.error("No service, config or CLI changes were made.");
|
|
323
|
+
process.exitCode = 1;
|
|
324
|
+
}
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
276
327
|
const serviceArgs = ["uninstall"];
|
|
277
328
|
if (options.stack) {
|
|
278
329
|
serviceArgs.push("--stack", options.stack);
|
|
@@ -290,7 +341,7 @@ function runUninstall(args = []) {
|
|
|
290
341
|
defaultBrowsePath ? ` ${defaultBrowsePath}` : ""
|
|
291
342
|
}`
|
|
292
343
|
);
|
|
293
|
-
console.log(
|
|
344
|
+
console.log(` Global CLI package: preserve ${repoRoot} (npm/prefix removal not requested)`);
|
|
294
345
|
console.log("");
|
|
295
346
|
|
|
296
347
|
if (!deleteConfig) {
|
|
@@ -301,9 +352,7 @@ function runUninstall(args = []) {
|
|
|
301
352
|
console.log(
|
|
302
353
|
"Default Browse Root and agent folders are never deleted by this command; remove them manually only if intended."
|
|
303
354
|
);
|
|
304
|
-
console.log(
|
|
305
|
-
"To remove the global CLI after cleanup, run: npm uninstall -g oysterun"
|
|
306
|
-
);
|
|
355
|
+
console.log("To remove service, config and this installed CLI together, run: oysterun uninstall --confirm DELETE");
|
|
307
356
|
|
|
308
357
|
if (options.dryRun) {
|
|
309
358
|
console.log("Dry run complete: no service or config changes were made.");
|
|
@@ -325,12 +374,7 @@ function runUninstall(args = []) {
|
|
|
325
374
|
process.exit(result.status ?? 1);
|
|
326
375
|
}
|
|
327
376
|
|
|
328
|
-
|
|
329
|
-
rmSync(configDir, { recursive: true, force: true });
|
|
330
|
-
console.log(`Deleted config directory: ${configDir}`);
|
|
331
|
-
} else {
|
|
332
|
-
console.log(`Preserved config directory: ${configDir}`);
|
|
333
|
-
}
|
|
377
|
+
console.log(`Preserved config directory: ${configDir}`);
|
|
334
378
|
}
|
|
335
379
|
|
|
336
380
|
function printConfiguredStatus(status) {
|
|
@@ -382,6 +426,9 @@ switch (command) {
|
|
|
382
426
|
case "setup":
|
|
383
427
|
runSetup(rest);
|
|
384
428
|
break;
|
|
429
|
+
case "onboard":
|
|
430
|
+
run(process.execPath, [onboardScript, ...rest], { setupInput: true });
|
|
431
|
+
break;
|
|
385
432
|
case "show-qr":
|
|
386
433
|
run(process.execPath, [setupScript, "--show-qr", ...rest]);
|
|
387
434
|
break;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Owns the lifetime of one completed-message navigation request at a time.
|
|
5
|
+
* Timeline movement is deliberately absent from this API: only click() reads.
|
|
6
|
+
*
|
|
7
|
+
* @template TResponse
|
|
8
|
+
* @param {{ readNeighbors: (request: { roomId: string, anchorEventId?: string, anchorPosition?: "latest" }) => Promise<TResponse>, selectTarget: (response: TResponse, direction: "previous" | "next") => string | undefined }} options
|
|
9
|
+
*/
|
|
10
|
+
export function createCompletedMessageNeighborClickCoordinator(options) {
|
|
11
|
+
/** @type {string | null} */
|
|
12
|
+
let scope = null;
|
|
13
|
+
let generation = 0;
|
|
14
|
+
/** @type {{ generation: number, scope: string } | null} */
|
|
15
|
+
let active = null;
|
|
16
|
+
let disposed = false;
|
|
17
|
+
|
|
18
|
+
/** @param {string} nextScope */
|
|
19
|
+
function setScope(nextScope) {
|
|
20
|
+
if (scope === nextScope) return;
|
|
21
|
+
scope = nextScope;
|
|
22
|
+
generation += 1;
|
|
23
|
+
active = null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
setScope,
|
|
28
|
+
dispose() {
|
|
29
|
+
disposed = true;
|
|
30
|
+
generation += 1;
|
|
31
|
+
active = null;
|
|
32
|
+
},
|
|
33
|
+
isLoading() {
|
|
34
|
+
return active !== null;
|
|
35
|
+
},
|
|
36
|
+
/**
|
|
37
|
+
* @param {{ scope: string, roomId: string, direction: "previous" | "next", anchorEventId?: string, anchorPosition?: "latest" }} request
|
|
38
|
+
*/
|
|
39
|
+
async click(request) {
|
|
40
|
+
if (disposed || request.scope !== scope) return { status: "stale" };
|
|
41
|
+
if (active !== null) return { status: "busy" };
|
|
42
|
+
|
|
43
|
+
const transaction = { generation, scope: request.scope };
|
|
44
|
+
active = transaction;
|
|
45
|
+
try {
|
|
46
|
+
const response = await options.readNeighbors({
|
|
47
|
+
roomId: request.roomId,
|
|
48
|
+
anchorEventId: request.anchorEventId,
|
|
49
|
+
anchorPosition: request.anchorPosition,
|
|
50
|
+
});
|
|
51
|
+
if (
|
|
52
|
+
disposed ||
|
|
53
|
+
active !== transaction ||
|
|
54
|
+
generation !== transaction.generation ||
|
|
55
|
+
scope !== transaction.scope
|
|
56
|
+
) {
|
|
57
|
+
return { status: "stale" };
|
|
58
|
+
}
|
|
59
|
+
const targetEventId = options.selectTarget(response, request.direction);
|
|
60
|
+
return targetEventId
|
|
61
|
+
? { status: "target", response, targetEventId }
|
|
62
|
+
: { status: "no_target", response };
|
|
63
|
+
} catch (error) {
|
|
64
|
+
if (
|
|
65
|
+
disposed ||
|
|
66
|
+
active !== transaction ||
|
|
67
|
+
generation !== transaction.generation ||
|
|
68
|
+
scope !== transaction.scope
|
|
69
|
+
) {
|
|
70
|
+
return { status: "stale" };
|
|
71
|
+
}
|
|
72
|
+
return { status: "error", error };
|
|
73
|
+
} finally {
|
|
74
|
+
if (active === transaction) active = null;
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
export const COMPLETED_MESSAGE_NEIGHBOR_SCHEMA_VERSION =
|
|
4
|
+
"routec.p206_completed_turn_final_message_neighbors.v1";
|
|
5
|
+
|
|
6
|
+
/** @typedef {{ event_id: string, routec_stream_seq: number, origin_server_ts: number | null, provider_turn_id: string }} CompletedMessageNeighborEvent */
|
|
7
|
+
/** @typedef {{ anchor_position: "event", event_id: string, routec_stream_seq: number } | { anchor_position: "latest", event_id: null, routec_stream_seq: number | null }} CompletedMessageNeighborAnchor */
|
|
8
|
+
/** @typedef {{ schema_version: typeof COMPLETED_MESSAGE_NEIGHBOR_SCHEMA_VERSION, lookup_source: string, lookup_strategy: string, order_by: "final_candidate_stream_seq", candidate_predicate: "complete_message_notification_candidate", completion_predicate: "complete_message_notification_completion", navigation_index_version: number | null, navigation_index_names: string[], raw_event_scan_limit: null, raw_event_payload_returned: false, message_body_returned: false }} CompletedMessageNeighborProof */
|
|
9
|
+
/** @typedef {{ status: "ok", session_id: string, matrix_room_id: string, anchor: CompletedMessageNeighborAnchor, previous: CompletedMessageNeighborEvent | null, next: CompletedMessageNeighborEvent | null, boundaries: { no_completed_turns: boolean, at_first_completed_message: boolean, at_latest_completed_message: boolean }, proof: CompletedMessageNeighborProof, committed_transcript_truth?: "matrix_room_timeline", routec_completed_message_neighbor_endpoint?: true, completed_message_truth_source?: "complete_message_notification_candidate_and_completion_predicates", raw_event_payload_returned: false, message_body_returned: false }} CompletedMessageNeighborsResponse */
|
|
10
|
+
/** @typedef {{ sessionId?: string, matrixRoomId?: string, anchorEventId?: string | null, anchorPosition?: "latest" | null }} CompletedMessageNeighborExpectedIdentity */
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {unknown} value
|
|
14
|
+
* @returns {value is Record<string, unknown>}
|
|
15
|
+
*/
|
|
16
|
+
function isRecord(value) {
|
|
17
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {unknown} value
|
|
22
|
+
* @returns {value is string}
|
|
23
|
+
*/
|
|
24
|
+
function nonEmptyString(value) {
|
|
25
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {unknown} value
|
|
30
|
+
* @returns {value is number}
|
|
31
|
+
*/
|
|
32
|
+
function isNonnegativeSafeInteger(value) {
|
|
33
|
+
return Number.isSafeInteger(value) && Number(value) >= 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {unknown} value
|
|
38
|
+
* @returns {value is CompletedMessageNeighborEvent}
|
|
39
|
+
*/
|
|
40
|
+
function isNeighbor(value) {
|
|
41
|
+
return (
|
|
42
|
+
isRecord(value) &&
|
|
43
|
+
nonEmptyString(value.event_id) &&
|
|
44
|
+
isNonnegativeSafeInteger(value.routec_stream_seq) &&
|
|
45
|
+
(typeof value.origin_server_ts === "number" ||
|
|
46
|
+
value.origin_server_ts === null) &&
|
|
47
|
+
nonEmptyString(value.provider_turn_id)
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @param {CompletedMessageNeighborsResponse} value
|
|
53
|
+
* @param {CompletedMessageNeighborExpectedIdentity} expected
|
|
54
|
+
*/
|
|
55
|
+
function assertRequestIdentity(value, expected) {
|
|
56
|
+
if (expected.sessionId && value.session_id !== expected.sessionId) {
|
|
57
|
+
throw new Error("Completed-message neighbor Session identity mismatch.");
|
|
58
|
+
}
|
|
59
|
+
if (expected.matrixRoomId && value.matrix_room_id !== expected.matrixRoomId) {
|
|
60
|
+
throw new Error("Completed-message neighbor room identity mismatch.");
|
|
61
|
+
}
|
|
62
|
+
if (expected.anchorEventId) {
|
|
63
|
+
if (
|
|
64
|
+
value.anchor.anchor_position !== "event" ||
|
|
65
|
+
value.anchor.event_id !== expected.anchorEventId
|
|
66
|
+
) {
|
|
67
|
+
throw new Error("Completed-message neighbor anchor identity mismatch.");
|
|
68
|
+
}
|
|
69
|
+
} else if (expected.anchorPosition === "latest") {
|
|
70
|
+
if (
|
|
71
|
+
value.anchor.anchor_position !== "latest" ||
|
|
72
|
+
value.anchor.event_id !== null
|
|
73
|
+
) {
|
|
74
|
+
throw new Error("Completed-message neighbor latest anchor mismatch.");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @param {unknown} value
|
|
81
|
+
* @param {CompletedMessageNeighborExpectedIdentity} [expected]
|
|
82
|
+
* @returns {CompletedMessageNeighborsResponse}
|
|
83
|
+
*/
|
|
84
|
+
export function assertCompletedMessageNeighborsResponse(value, expected = {}) {
|
|
85
|
+
if (!isRecord(value)) {
|
|
86
|
+
throw new Error("Completed-message neighbor response must be an object.");
|
|
87
|
+
}
|
|
88
|
+
const anchor = value.anchor;
|
|
89
|
+
const boundaries = value.boundaries;
|
|
90
|
+
const proof = value.proof;
|
|
91
|
+
const validAnchor =
|
|
92
|
+
isRecord(anchor) &&
|
|
93
|
+
(anchor.anchor_position === "event" ||
|
|
94
|
+
anchor.anchor_position === "latest") &&
|
|
95
|
+
(typeof anchor.event_id === "string" || anchor.event_id === null) &&
|
|
96
|
+
(isNonnegativeSafeInteger(anchor.routec_stream_seq) ||
|
|
97
|
+
anchor.routec_stream_seq === null) &&
|
|
98
|
+
(anchor.anchor_position === "event"
|
|
99
|
+
? nonEmptyString(anchor.event_id) &&
|
|
100
|
+
isNonnegativeSafeInteger(anchor.routec_stream_seq)
|
|
101
|
+
: anchor.event_id === null);
|
|
102
|
+
const indexNames = isRecord(proof) ? proof.navigation_index_names : null;
|
|
103
|
+
const indexVersion = isRecord(proof)
|
|
104
|
+
? proof.navigation_index_version
|
|
105
|
+
: undefined;
|
|
106
|
+
const validIndexDiagnostics =
|
|
107
|
+
Array.isArray(indexNames) &&
|
|
108
|
+
indexNames.every(nonEmptyString) &&
|
|
109
|
+
new Set(indexNames).size === indexNames.length &&
|
|
110
|
+
(Number.isSafeInteger(indexVersion) || indexVersion === null) &&
|
|
111
|
+
((indexVersion === null && indexNames.length === 0) ||
|
|
112
|
+
(Number.isSafeInteger(indexVersion) && indexNames.length > 0));
|
|
113
|
+
const validProof =
|
|
114
|
+
isRecord(proof) &&
|
|
115
|
+
proof.schema_version === COMPLETED_MESSAGE_NEIGHBOR_SCHEMA_VERSION &&
|
|
116
|
+
nonEmptyString(proof.lookup_source) &&
|
|
117
|
+
nonEmptyString(proof.lookup_strategy) &&
|
|
118
|
+
proof.order_by === "final_candidate_stream_seq" &&
|
|
119
|
+
proof.candidate_predicate === "complete_message_notification_candidate" &&
|
|
120
|
+
proof.completion_predicate === "complete_message_notification_completion" &&
|
|
121
|
+
validIndexDiagnostics &&
|
|
122
|
+
proof.raw_event_scan_limit === null &&
|
|
123
|
+
proof.raw_event_payload_returned === false &&
|
|
124
|
+
proof.message_body_returned === false;
|
|
125
|
+
if (
|
|
126
|
+
value.status !== "ok" ||
|
|
127
|
+
!nonEmptyString(value.session_id) ||
|
|
128
|
+
!nonEmptyString(value.matrix_room_id) ||
|
|
129
|
+
!validAnchor ||
|
|
130
|
+
!(value.previous === null || isNeighbor(value.previous)) ||
|
|
131
|
+
!(value.next === null || isNeighbor(value.next)) ||
|
|
132
|
+
!isRecord(boundaries) ||
|
|
133
|
+
typeof boundaries.no_completed_turns !== "boolean" ||
|
|
134
|
+
typeof boundaries.at_first_completed_message !== "boolean" ||
|
|
135
|
+
typeof boundaries.at_latest_completed_message !== "boolean" ||
|
|
136
|
+
!validProof ||
|
|
137
|
+
!(
|
|
138
|
+
value.committed_transcript_truth === undefined ||
|
|
139
|
+
value.committed_transcript_truth === "matrix_room_timeline"
|
|
140
|
+
) ||
|
|
141
|
+
!(
|
|
142
|
+
value.routec_completed_message_neighbor_endpoint === undefined ||
|
|
143
|
+
value.routec_completed_message_neighbor_endpoint === true
|
|
144
|
+
) ||
|
|
145
|
+
!(
|
|
146
|
+
value.completed_message_truth_source === undefined ||
|
|
147
|
+
value.completed_message_truth_source ===
|
|
148
|
+
"complete_message_notification_candidate_and_completion_predicates"
|
|
149
|
+
) ||
|
|
150
|
+
value.raw_event_payload_returned !== false ||
|
|
151
|
+
value.message_body_returned !== false
|
|
152
|
+
) {
|
|
153
|
+
throw new Error("Completed-message neighbor response failed validation.");
|
|
154
|
+
}
|
|
155
|
+
const validated = /** @type {CompletedMessageNeighborsResponse} */ (value);
|
|
156
|
+
if (
|
|
157
|
+
validated.boundaries.at_first_completed_message !==
|
|
158
|
+
(validated.previous === null) ||
|
|
159
|
+
validated.boundaries.at_latest_completed_message !==
|
|
160
|
+
(validated.next === null) ||
|
|
161
|
+
(boundaries.no_completed_turns &&
|
|
162
|
+
(validated.previous !== null ||
|
|
163
|
+
validated.next !== null ||
|
|
164
|
+
!boundaries.at_first_completed_message ||
|
|
165
|
+
!boundaries.at_latest_completed_message))
|
|
166
|
+
) {
|
|
167
|
+
throw new Error("Completed-message neighbor boundaries are inconsistent.");
|
|
168
|
+
}
|
|
169
|
+
if (validated.anchor.anchor_position === "event") {
|
|
170
|
+
if (
|
|
171
|
+
(validated.previous &&
|
|
172
|
+
validated.previous.routec_stream_seq >=
|
|
173
|
+
validated.anchor.routec_stream_seq) ||
|
|
174
|
+
(validated.next &&
|
|
175
|
+
validated.next.routec_stream_seq <= validated.anchor.routec_stream_seq)
|
|
176
|
+
) {
|
|
177
|
+
throw new Error("Completed-message neighbor ordering is inconsistent.");
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
assertRequestIdentity(validated, expected);
|
|
181
|
+
return validated;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* @param {Omit<CompletedMessageNeighborsResponse, "status" | "raw_event_payload_returned" | "message_body_returned">} input
|
|
186
|
+
* @returns {CompletedMessageNeighborsResponse}
|
|
187
|
+
*/
|
|
188
|
+
export function buildCompletedMessageNeighborsResponse(input) {
|
|
189
|
+
return assertCompletedMessageNeighborsResponse(
|
|
190
|
+
{
|
|
191
|
+
status: "ok",
|
|
192
|
+
...input,
|
|
193
|
+
raw_event_payload_returned: false,
|
|
194
|
+
message_body_returned: false,
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
sessionId: input.session_id,
|
|
198
|
+
matrixRoomId: input.matrix_room_id,
|
|
199
|
+
anchorEventId:
|
|
200
|
+
input.anchor.anchor_position === "event" ? input.anchor.event_id : null,
|
|
201
|
+
anchorPosition:
|
|
202
|
+
input.anchor.anchor_position === "latest" ? "latest" : null,
|
|
203
|
+
}
|
|
204
|
+
);
|
|
205
|
+
}
|
|
@@ -19,12 +19,12 @@ export function evaluateCompletedMessageVisibilityAcknowledgement(input: {
|
|
|
19
19
|
currentMatrixRoomId?: string;
|
|
20
20
|
targetMatrixRoomId?: string;
|
|
21
21
|
latestCompletedEventId?: string | null;
|
|
22
|
-
|
|
22
|
+
latestTimelineLoaded?: boolean;
|
|
23
23
|
hasUnreadCompletedMessage?: boolean;
|
|
24
24
|
documentVisibilityState?: string;
|
|
25
25
|
documentHasFocus?: boolean;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
visibleMessageConnected?: boolean;
|
|
27
|
+
visibleMessageHidden?: boolean;
|
|
28
|
+
visibleMessageRect?: VisibilityRect | DOMRect;
|
|
29
29
|
viewportRect?: VisibilityRect | DOMRect;
|
|
30
30
|
}): { qualified: boolean; reason: string };
|
|
@@ -30,13 +30,13 @@ export function evaluateCompletedMessageVisibilityAcknowledgement({
|
|
|
30
30
|
currentMatrixRoomId,
|
|
31
31
|
targetMatrixRoomId,
|
|
32
32
|
latestCompletedEventId,
|
|
33
|
-
|
|
33
|
+
latestTimelineLoaded,
|
|
34
34
|
hasUnreadCompletedMessage,
|
|
35
35
|
documentVisibilityState,
|
|
36
36
|
documentHasFocus,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
visibleMessageConnected,
|
|
38
|
+
visibleMessageHidden,
|
|
39
|
+
visibleMessageRect,
|
|
40
40
|
viewportRect,
|
|
41
41
|
}) {
|
|
42
42
|
if (routeCChatShell !== true)
|
|
@@ -49,19 +49,22 @@ export function evaluateCompletedMessageVisibilityAcknowledgement({
|
|
|
49
49
|
}
|
|
50
50
|
if (hasUnreadCompletedMessage !== true)
|
|
51
51
|
return { qualified: false, reason: "not_unread" };
|
|
52
|
-
if (!latestCompletedEventId ||
|
|
53
|
-
return { qualified: false, reason: "
|
|
52
|
+
if (!latestCompletedEventId || latestTimelineLoaded !== true) {
|
|
53
|
+
return { qualified: false, reason: "latest_timeline_not_ready" };
|
|
54
54
|
}
|
|
55
55
|
if (documentVisibilityState !== "visible") {
|
|
56
56
|
return { qualified: false, reason: "hidden_document" };
|
|
57
57
|
}
|
|
58
58
|
if (documentHasFocus !== true)
|
|
59
59
|
return { qualified: false, reason: "unfocused_document" };
|
|
60
|
-
if (
|
|
61
|
-
return { qualified: false, reason: "
|
|
60
|
+
if (visibleMessageConnected !== true || visibleMessageHidden === true) {
|
|
61
|
+
return { qualified: false, reason: "no_visible_materialized_message" };
|
|
62
62
|
}
|
|
63
|
-
if (!isCompletedMessageViewportVisible({
|
|
64
|
-
|
|
63
|
+
if (!isCompletedMessageViewportVisible({
|
|
64
|
+
elementRect: visibleMessageRect,
|
|
65
|
+
viewportRect,
|
|
66
|
+
})) {
|
|
67
|
+
return { qualified: false, reason: "no_visible_materialized_message" };
|
|
65
68
|
}
|
|
66
|
-
return { qualified: true, reason: "
|
|
69
|
+
return { qualified: true, reason: "visible_focused_latest_timeline" };
|
|
67
70
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export const DESKTOP_CHAT_SIDEBAR_PREFERENCE_KEY =
|
|
2
|
+
"oysterun.desktop_chat_sidebar_expanded.v1";
|
|
3
|
+
|
|
4
|
+
const readStorage = (windowTarget) => {
|
|
5
|
+
try {
|
|
6
|
+
return windowTarget?.localStorage;
|
|
7
|
+
} catch {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function readDesktopChatSidebarPreference(windowTarget = globalThis) {
|
|
13
|
+
try {
|
|
14
|
+
const stored = readStorage(windowTarget)?.getItem(
|
|
15
|
+
DESKTOP_CHAT_SIDEBAR_PREFERENCE_KEY
|
|
16
|
+
);
|
|
17
|
+
if (stored === "false") return false;
|
|
18
|
+
if (stored === "true") return true;
|
|
19
|
+
} catch {
|
|
20
|
+
// Browser privacy modes may expose localStorage while rejecting reads.
|
|
21
|
+
}
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function writeDesktopChatSidebarPreference(
|
|
26
|
+
expanded,
|
|
27
|
+
windowTarget = globalThis
|
|
28
|
+
) {
|
|
29
|
+
try {
|
|
30
|
+
const storage = readStorage(windowTarget);
|
|
31
|
+
if (typeof storage?.setItem !== "function") return false;
|
|
32
|
+
storage.setItem(
|
|
33
|
+
DESKTOP_CHAT_SIDEBAR_PREFERENCE_KEY,
|
|
34
|
+
expanded === true ? "true" : "false"
|
|
35
|
+
);
|
|
36
|
+
return true;
|
|
37
|
+
} catch {
|
|
38
|
+
// Preference persistence must never make Chat unavailable.
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function subscribeDesktopChatSidebarPreference(
|
|
44
|
+
listener,
|
|
45
|
+
windowTarget = globalThis
|
|
46
|
+
) {
|
|
47
|
+
if (typeof listener !== "function") {
|
|
48
|
+
throw new TypeError("Desktop Chat sidebar preference listener is required");
|
|
49
|
+
}
|
|
50
|
+
if (typeof windowTarget?.addEventListener !== "function") return () => {};
|
|
51
|
+
const onStorage = (event) => {
|
|
52
|
+
if (event?.key !== DESKTOP_CHAT_SIDEBAR_PREFERENCE_KEY) return;
|
|
53
|
+
listener(readDesktopChatSidebarPreference(windowTarget));
|
|
54
|
+
};
|
|
55
|
+
windowTarget.addEventListener("storage", onStorage);
|
|
56
|
+
return () => windowTarget.removeEventListener("storage", onStorage);
|
|
57
|
+
}
|
|
@@ -2671,6 +2671,16 @@ export function startManagedHostConnection() {
|
|
|
2671
2671
|
return getManagedHostBrowserConnection().start();
|
|
2672
2672
|
}
|
|
2673
2673
|
|
|
2674
|
+
// A WebRTC connection is authenticated with the dashboard token used to open
|
|
2675
|
+
// it. Keep the Host's connection-bound authorization policy: a new login must
|
|
2676
|
+
// retire that connection rather than send a new bearer through the old peer.
|
|
2677
|
+
export async function refreshManagedHostAuthentication() {
|
|
2678
|
+
const connection = getManagedHostBrowserConnection();
|
|
2679
|
+
if (connection instanceof ManagedHostBrowserConnection) {
|
|
2680
|
+
await connection.restartWebRtc("dashboard_login", { initiator: "browser" });
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
|
|
2674
2684
|
export function managedHostFetch(input, init) {
|
|
2675
2685
|
return getManagedHostBrowserConnection().fetch(input, init);
|
|
2676
2686
|
}
|