@rynx-ai/daemon 0.1.11-beta.2 → 0.1.11-beta.21

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.
Files changed (88) hide show
  1. package/bundled-plugins/plugins/lark/.rynx-plugin/plugin.json +0 -12
  2. package/bundled-plugins/plugins/lark/dist/cli-mirror/mirror-index.js +3 -1
  3. package/bundled-plugins/plugins/lark/dist/host-adapters.d.ts +3 -3
  4. package/bundled-plugins/plugins/lark/dist/host-adapters.js +20 -18
  5. package/bundled-plugins/plugins/lark/dist/lark.js +18 -12
  6. package/bundled-plugins/plugins/lark/dist/runtime.js +6046 -6022
  7. package/bundled-plugins/plugins/lark/dist/settings.js +6006 -5988
  8. package/bundled-plugins/plugins/lark/package.json +1 -1
  9. package/bundled-plugins/plugins/lark/ui/assets/index-CGUVAMsv.css +1 -0
  10. package/bundled-plugins/plugins/lark/ui/index.html +2 -2
  11. package/dist/agent-file.js +0 -1
  12. package/dist/browser-artifact-management.d.ts +4 -1
  13. package/dist/browser-artifact-management.js +22 -11
  14. package/dist/browser-artifacts.d.ts +1 -0
  15. package/dist/browser-artifacts.js +1 -0
  16. package/dist/chrome-for-testing-runtime.d.ts +27 -0
  17. package/dist/chrome-for-testing-runtime.js +194 -0
  18. package/dist/chrome-for-testing-store.d.ts +2 -0
  19. package/dist/chrome-for-testing-store.js +37 -6
  20. package/dist/control-deps.d.ts +6 -2
  21. package/dist/control-deps.js +87 -4
  22. package/dist/control-link-store.js +4 -3
  23. package/dist/daemon-owner.js +4 -7
  24. package/dist/daemon-server.d.ts +4 -1
  25. package/dist/daemon-server.js +158 -45
  26. package/dist/db.d.ts +2 -2
  27. package/dist/db.js +55 -20
  28. package/dist/entry-path.d.ts +3 -0
  29. package/dist/entry-path.js +12 -0
  30. package/dist/index-daemon.js +3 -87
  31. package/dist/lifecycle.d.ts +13 -0
  32. package/dist/lifecycle.js +19 -0
  33. package/dist/maintenance-lease.d.ts +39 -0
  34. package/dist/maintenance-lease.js +250 -0
  35. package/dist/migrations/cleanup-legacy-sessions.js +4 -3
  36. package/dist/plugin-cli.d.ts +10 -0
  37. package/dist/plugin-cli.js +19 -2
  38. package/dist/plugin-host-rpc.d.ts +35 -6
  39. package/dist/plugin-host-rpc.js +222 -59
  40. package/dist/plugin-installer.js +62 -2
  41. package/dist/plugin-management-service.js +0 -2
  42. package/dist/plugin-marketplace.js +2 -1
  43. package/dist/plugin-store.js +2 -4
  44. package/dist/plugin-supervisor.d.ts +5 -0
  45. package/dist/plugin-supervisor.js +218 -33
  46. package/dist/pm2.d.ts +11 -2
  47. package/dist/pm2.js +116 -19
  48. package/dist/process.d.ts +12 -0
  49. package/dist/process.js +91 -0
  50. package/dist/remote-runtime-access-store.d.ts +2 -2
  51. package/dist/remote-runtime-access-store.js +6 -9
  52. package/dist/remote-runtime-target-store.d.ts +2 -2
  53. package/dist/remote-runtime-target-store.js +6 -6
  54. package/dist/session-emulator-binding-store.js +4 -6
  55. package/dist/session-fork-store.js +24 -8
  56. package/dist/session-launch-operations.d.ts +10 -1
  57. package/dist/session-launch-operations.js +21 -11
  58. package/dist/session-log-store.js +2 -1
  59. package/dist/session-meta-store.d.ts +5 -0
  60. package/dist/session-meta-store.js +25 -2
  61. package/dist/session-portal-grant-store.d.ts +29 -0
  62. package/dist/session-portal-grant-store.js +85 -0
  63. package/dist/session-resource-store.js +3 -2
  64. package/dist/setup-service.d.ts +21 -0
  65. package/dist/setup-service.js +39 -0
  66. package/dist/skills-catalog.d.ts +11 -2
  67. package/dist/skills-catalog.js +307 -10
  68. package/dist/sqlite.d.ts +22 -0
  69. package/dist/sqlite.js +57 -0
  70. package/dist/system-dependencies.d.ts +22 -0
  71. package/dist/system-dependencies.js +79 -0
  72. package/package.json +37 -21
  73. package/bundled-plugins/plugins/lark/ui/assets/index-B2dhMlMU.css +0 -1
  74. package/dist/browser-cli-args.d.ts +0 -20
  75. package/dist/browser-cli-args.js +0 -100
  76. package/dist/cli.d.ts +0 -2
  77. package/dist/cli.js +0 -550
  78. package/dist/control-client.d.ts +0 -122
  79. package/dist/control-client.js +0 -855
  80. package/dist/control-endpoint.d.ts +0 -14
  81. package/dist/control-endpoint.js +0 -124
  82. package/dist/desktop-browser-host-client.d.ts +0 -35
  83. package/dist/desktop-browser-host-client.js +0 -415
  84. package/dist/setup.d.ts +0 -20
  85. package/dist/setup.js +0 -386
  86. package/dist/update.d.ts +0 -25
  87. package/dist/update.js +0 -133
  88. /package/bundled-plugins/plugins/lark/ui/assets/{index-D9N-AkLX.js → index-D8Amoe6a.js} +0 -0
@@ -1,14 +0,0 @@
1
- export interface DaemonControlEndpoint {
2
- origin: string;
3
- pid: number;
4
- managementToken: string;
5
- }
6
- /**
7
- * Resolve the single resident daemon advertised by RYNX_HOME. If no live owner
8
- * exists, start the pm2-supervised daemon and wait for it to publish its
9
- * endpoint. This deliberately never guesses another port or starts an
10
- * app-owned daemon.
11
- */
12
- export declare function ensureDaemonControlEndpoint(options?: {
13
- signal?: AbortSignal;
14
- }): Promise<DaemonControlEndpoint>;
@@ -1,124 +0,0 @@
1
- import { readFile } from "node:fs/promises";
2
- import path from "node:path";
3
- import { rynxHome } from "@rynx-ai/core";
4
- import { isPm2DaemonOnline, startDaemon } from "./pm2.js";
5
- const STARTUP_TIMEOUT_MS = 20_000;
6
- const PROBE_TIMEOUT_MS = 1_000;
7
- /**
8
- * Resolve the single resident daemon advertised by RYNX_HOME. If no live owner
9
- * exists, start the pm2-supervised daemon and wait for it to publish its
10
- * endpoint. This deliberately never guesses another port or starts an
11
- * app-owned daemon.
12
- */
13
- export async function ensureDaemonControlEndpoint(options = {}) {
14
- const { signal } = options;
15
- signal?.throwIfAborted();
16
- const deadline = Date.now() + STARTUP_TIMEOUT_MS;
17
- const current = await readControlEndpoint();
18
- if (current && isProcessAlive(current.pid)) {
19
- const recovered = await waitForOwnedEndpoint(current, deadline, signal);
20
- if (recovered)
21
- return recovered;
22
- if (isProcessAlive(current.pid)) {
23
- throw new Error(`Rynx daemon process ${current.pid} is alive but its control endpoint did not become healthy`);
24
- }
25
- }
26
- if (Date.now() >= deadline) {
27
- throw new Error("Rynx daemon stopped before its control endpoint became healthy");
28
- }
29
- const force = isPm2DaemonOnline();
30
- const status = startDaemon({ force, quiet: true });
31
- if (status !== 0)
32
- throw new Error("failed to start the Rynx daemon");
33
- while (Date.now() < deadline) {
34
- signal?.throwIfAborted();
35
- const endpoint = await readControlEndpoint();
36
- if (endpoint && isProcessAlive(endpoint.pid) && await isHealthy(endpoint.origin, signal))
37
- return endpoint;
38
- await abortableDelay(100, signal);
39
- }
40
- throw new Error("Rynx daemon did not publish a ready control endpoint");
41
- }
42
- async function waitForOwnedEndpoint(endpoint, deadline, signal) {
43
- while (Date.now() < deadline && isProcessAlive(endpoint.pid)) {
44
- signal?.throwIfAborted();
45
- if (await isHealthy(endpoint.origin, signal))
46
- return endpoint;
47
- await abortableDelay(100, signal);
48
- }
49
- return null;
50
- }
51
- async function readControlEndpoint() {
52
- try {
53
- const parsed = JSON.parse(await readFile(path.join(rynxHome(), "control.json"), "utf8"));
54
- if (typeof parsed.origin !== "string" ||
55
- typeof parsed.pid !== "number" ||
56
- !Number.isInteger(parsed.pid) ||
57
- parsed.pid <= 0 ||
58
- typeof parsed.managementToken !== "string" ||
59
- parsed.managementToken.length < 32 ||
60
- parsed.managementToken.length > 256) {
61
- return null;
62
- }
63
- const origin = new URL(parsed.origin);
64
- if ((origin.protocol !== "http:" && origin.protocol !== "https:") ||
65
- origin.username ||
66
- origin.password ||
67
- origin.search ||
68
- origin.hash) {
69
- return null;
70
- }
71
- return {
72
- origin: origin.toString().replace(/\/$/, ""),
73
- pid: parsed.pid,
74
- managementToken: parsed.managementToken,
75
- };
76
- }
77
- catch {
78
- return null;
79
- }
80
- }
81
- async function isHealthy(origin, signal) {
82
- try {
83
- const response = await fetch(`${origin}/health`, {
84
- signal: signal
85
- ? AbortSignal.any([signal, AbortSignal.timeout(PROBE_TIMEOUT_MS)])
86
- : AbortSignal.timeout(PROBE_TIMEOUT_MS),
87
- });
88
- return response.ok;
89
- }
90
- catch {
91
- signal?.throwIfAborted();
92
- return false;
93
- }
94
- }
95
- async function abortableDelay(ms, signal) {
96
- signal?.throwIfAborted();
97
- if (!signal) {
98
- await new Promise((resolve) => setTimeout(resolve, ms));
99
- return;
100
- }
101
- const activeSignal = signal;
102
- await new Promise((resolve, reject) => {
103
- const timer = setTimeout(done, ms);
104
- const abort = () => {
105
- clearTimeout(timer);
106
- activeSignal.removeEventListener("abort", abort);
107
- reject(activeSignal.reason);
108
- };
109
- function done() {
110
- activeSignal.removeEventListener("abort", abort);
111
- resolve();
112
- }
113
- activeSignal.addEventListener("abort", abort, { once: true });
114
- });
115
- }
116
- function isProcessAlive(pid) {
117
- try {
118
- process.kill(pid, 0);
119
- return true;
120
- }
121
- catch {
122
- return false;
123
- }
124
- }
@@ -1,35 +0,0 @@
1
- import { type DesktopBrowserHostCommandFrame, type DesktopBrowserHostErrorCode, type DesktopBrowserHostEvent, type DesktopBrowserHostLeaseFrame, type DesktopBrowserHostPresentation, type DesktopBrowserHostSurfaceCapability, type DesktopBrowserHostSurfaceFrame } from "@rynx-ai/protocol/desktop-browser-host";
2
- export interface ResidentDesktopBrowserHostConnectOptions {
3
- hostInstanceId: string;
4
- /** Advertise only handlers that are installed in this main-process Host. */
5
- capabilities: {
6
- semanticPageBinding: boolean;
7
- presentation: DesktopBrowserHostPresentation;
8
- surface: DesktopBrowserHostSurfaceCapability;
9
- };
10
- signal?: AbortSignal;
11
- }
12
- export interface ResidentDesktopBrowserHostFailure {
13
- code: DesktopBrowserHostErrorCode;
14
- message: string;
15
- }
16
- /**
17
- * Process-private lease used by Electron main. The management credential is
18
- * resolved and consumed in this package and never returned to the app renderer.
19
- */
20
- export interface ResidentDesktopBrowserHostCommandRequest {
21
- readonly frame: DesktopBrowserHostCommandFrame;
22
- /** Aborts at the daemon-supplied absolute deadline or on lease loss. */
23
- readonly signal: AbortSignal;
24
- reply(result: unknown): Promise<void>;
25
- reject(failure: ResidentDesktopBrowserHostFailure): Promise<void>;
26
- }
27
- export interface ResidentDesktopBrowserHostConnection extends AsyncIterable<ResidentDesktopBrowserHostCommandRequest> {
28
- readonly lease: DesktopBrowserHostLeaseFrame;
29
- readonly closed: Promise<void>;
30
- emit(event: DesktopBrowserHostEvent): Promise<void>;
31
- emitSurfaceFrame(frame: Omit<DesktopBrowserHostSurfaceFrame, "leaseId">): Promise<void>;
32
- close(): Promise<void>;
33
- }
34
- /** Open the sole resident daemon's authenticated loopback Desktop Host lease. */
35
- export declare function connectResidentDesktopBrowserHost(options: ResidentDesktopBrowserHostConnectOptions): Promise<ResidentDesktopBrowserHostConnection>;
@@ -1,415 +0,0 @@
1
- import { DESKTOP_BROWSER_HOST_MAX_BINARY_FRAME_BYTES, DESKTOP_BROWSER_HOST_MAX_CONTROL_FRAME_BYTES, DESKTOP_BROWSER_HOST_MAX_PENDING_COMMANDS, DESKTOP_BROWSER_HOST_PATH, DESKTOP_BROWSER_HOST_PROTOCOL_VERSION, encodeDesktopBrowserHostSurfaceFrame, parseDesktopBrowserHostClientFrame, parseDesktopBrowserHostServerFrame, } from "@rynx-ai/protocol/desktop-browser-host";
2
- import { WebSocket } from "ws";
3
- import { ensureDaemonControlEndpoint } from "./control-endpoint.js";
4
- const CONNECT_TIMEOUT_MS = 5_000;
5
- const MAX_BUFFERED_BYTES = DESKTOP_BROWSER_HOST_MAX_BINARY_FRAME_BYTES + DESKTOP_BROWSER_HOST_MAX_CONTROL_FRAME_BYTES;
6
- /** Open the sole resident daemon's authenticated loopback Desktop Host lease. */
7
- export async function connectResidentDesktopBrowserHost(options) {
8
- options.signal?.throwIfAborted();
9
- const hello = parseDesktopBrowserHostClientFrame({
10
- type: "desktop.browser.host.hello",
11
- protocolVersion: DESKTOP_BROWSER_HOST_PROTOCOL_VERSION,
12
- hostInstanceId: options.hostInstanceId,
13
- capabilities: {
14
- semanticPageBinding: options.capabilities.semanticPageBinding,
15
- presentation: options.capabilities.presentation,
16
- surface: options.capabilities.surface,
17
- },
18
- });
19
- if (hello.type !== "desktop.browser.host.hello") {
20
- throw new Error("Desktop Browser Host hello parser returned the wrong frame");
21
- }
22
- const endpoint = await ensureDaemonControlEndpoint({ signal: options.signal });
23
- assertLoopbackOrigin(endpoint.origin);
24
- const url = new URL(DESKTOP_BROWSER_HOST_PATH, `${endpoint.origin}/`);
25
- url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
26
- const socket = new WebSocket(url, {
27
- headers: { "x-rynx-management-token": endpoint.managementToken },
28
- handshakeTimeout: CONNECT_TIMEOUT_MS,
29
- maxPayload: DESKTOP_BROWSER_HOST_MAX_CONTROL_FRAME_BYTES,
30
- perMessageDeflate: false,
31
- });
32
- const queue = new BoundedCommandQueue(DESKTOP_BROWSER_HOST_MAX_PENDING_COMMANDS);
33
- let lease;
34
- let leaseResolve;
35
- let leaseReject;
36
- const leaseReady = new Promise((resolve, reject) => {
37
- leaseResolve = resolve;
38
- leaseReject = reject;
39
- });
40
- let closedResolve;
41
- const closed = new Promise((resolve) => {
42
- closedResolve = resolve;
43
- });
44
- let terminalError;
45
- let eventSequence = 0;
46
- let closing = false;
47
- let writerTail = Promise.resolve();
48
- const activeRequests = new Map();
49
- const abortActiveRequests = (error) => {
50
- for (const request of activeRequests.values()) {
51
- if (request.timer)
52
- clearTimeout(request.timer);
53
- request.settled = true;
54
- request.controller.abort(error);
55
- }
56
- activeRequests.clear();
57
- };
58
- const fail = (error) => {
59
- terminalError ??= error;
60
- if (!lease)
61
- leaseReject(error);
62
- queue.close(error);
63
- abortActiveRequests(error);
64
- };
65
- const failConnection = (error, code, reason) => {
66
- fail(error);
67
- if (socket.readyState < WebSocket.CLOSING)
68
- socket.close(code, reason);
69
- };
70
- const enqueueWrite = (operation) => {
71
- const current = writerTail.then(async () => {
72
- if (terminalError)
73
- throw terminalError;
74
- await operation();
75
- });
76
- writerTail = current.catch(() => undefined);
77
- return current.catch((error) => {
78
- const failure = asError(error, "Desktop Browser Host output failed");
79
- failConnection(failure, 1011, "host output failed");
80
- throw failure;
81
- });
82
- };
83
- const onAbort = () => {
84
- const error = abortError(options.signal?.reason);
85
- failConnection(error, 1000, "aborted");
86
- };
87
- options.signal?.addEventListener("abort", onAbort, { once: true });
88
- socket.on("open", () => {
89
- void enqueueWrite(() => sendJson(socket, hello));
90
- });
91
- socket.on("message", (raw, isBinary) => {
92
- if (isBinary) {
93
- failConnection(new Error("Desktop Browser Host daemon sent an unexpected binary frame"), 1003, "binary frame unsupported");
94
- return;
95
- }
96
- let frame;
97
- try {
98
- const bytes = rawDataBytes(raw);
99
- if (bytes.byteLength === 0 || bytes.byteLength > DESKTOP_BROWSER_HOST_MAX_CONTROL_FRAME_BYTES) {
100
- throw new Error("Desktop Browser Host daemon frame exceeded its bound");
101
- }
102
- frame = parseDesktopBrowserHostServerFrame(JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes)));
103
- }
104
- catch (error) {
105
- failConnection(asError(error, "Desktop Browser Host daemon frame was invalid"), 1007, "invalid daemon frame");
106
- return;
107
- }
108
- if (!lease) {
109
- if (frame.type !== "desktop.browser.host.lease") {
110
- failConnection(new Error("Desktop Browser Host daemon did not begin with a lease"), 1002, "lease required");
111
- return;
112
- }
113
- if (frame.capabilities.semanticPageBinding &&
114
- !options.capabilities.semanticPageBinding) {
115
- failConnection(new Error("Desktop Browser Host daemon enabled an unadvertised Page binding capability"), 1002, "invalid lease capabilities");
116
- return;
117
- }
118
- if (frame.capabilities.surface === "binary-v1" &&
119
- options.capabilities.surface !== "binary-v1") {
120
- failConnection(new Error("Desktop Browser Host daemon enabled an unadvertised Surface capability"), 1002, "invalid lease capabilities");
121
- return;
122
- }
123
- lease = frame;
124
- leaseResolve(frame);
125
- return;
126
- }
127
- if (frame.type !== "desktop.browser.host.command" || frame.leaseId !== lease.leaseId) {
128
- failConnection(new Error("Desktop Browser Host daemon sent a frame outside the active lease"), 1002, "lease mismatch");
129
- return;
130
- }
131
- if (!queue.push(frame)) {
132
- failConnection(new Error("Desktop Browser Host command capacity exceeded"), 1013, "command capacity exceeded");
133
- }
134
- });
135
- socket.on("error", (error) => {
136
- failConnection(new Error("Desktop Browser Host connection failed", { cause: error }), 1011, "connection failed");
137
- });
138
- socket.once("close", (code, reason) => {
139
- options.signal?.removeEventListener("abort", onAbort);
140
- if (!terminalError && !closing) {
141
- terminalError = new Error(`Desktop Browser Host lease closed (${code})${reason.length > 0 ? `: ${reason.toString("utf8")}` : ""}`);
142
- }
143
- if (!lease && terminalError)
144
- leaseReject(terminalError);
145
- queue.close(terminalError);
146
- abortActiveRequests(terminalError ?? new Error("Desktop Browser Host lease closed"));
147
- closedResolve();
148
- });
149
- try {
150
- lease = await abortableTimeout(leaseReady, CONNECT_TIMEOUT_MS, options.signal);
151
- }
152
- catch (error) {
153
- if (socket.readyState < WebSocket.CLOSING)
154
- socket.close(1000, "lease failed");
155
- throw error;
156
- }
157
- const activeLease = lease;
158
- const settle = async (command, value) => {
159
- const request = activeRequests.get(command.commandId);
160
- if (!request || request.settled) {
161
- throw new Error("Desktop Browser Host command is already settled or stale");
162
- }
163
- if (command.leaseId !== activeLease.leaseId ||
164
- request.controller.signal.aborted ||
165
- Date.now() >= command.deadlineUnixMilliseconds) {
166
- expireRequest(command, request);
167
- throw new Error("Desktop Browser Host command deadline expired before settlement");
168
- }
169
- request.settled = true;
170
- if (request.timer)
171
- clearTimeout(request.timer);
172
- activeRequests.delete(command.commandId);
173
- await enqueueWrite(async () => {
174
- const frame = parseDesktopBrowserHostClientFrame(value);
175
- if (frame.type !== "desktop.browser.host.reply") {
176
- throw new Error("Desktop Browser Host reply parser returned the wrong frame");
177
- }
178
- await sendJson(socket, frame);
179
- });
180
- };
181
- const expireRequest = (command, request) => {
182
- if (request.settled)
183
- return;
184
- request.settled = true;
185
- if (request.timer)
186
- clearTimeout(request.timer);
187
- activeRequests.delete(command.commandId);
188
- const error = new Error(`Desktop Browser Host ${command.command.method} deadline expired`);
189
- request.controller.abort(error);
190
- if (isMutatingCommand(command)) {
191
- failConnection(error, 1000, "command deadline expired");
192
- }
193
- };
194
- const requestIterator = queue[Symbol.asyncIterator]();
195
- const claimedIterator = {
196
- next: async () => {
197
- const next = await requestIterator.next();
198
- if (next.done)
199
- return { done: true, value: undefined };
200
- const command = next.value;
201
- if (activeRequests.has(command.commandId)) {
202
- const error = new Error("Desktop Browser Host daemon repeated a commandId");
203
- failConnection(error, 1002, "duplicate command");
204
- throw error;
205
- }
206
- const controller = new AbortController();
207
- const remaining = command.deadlineUnixMilliseconds - Date.now();
208
- const request = {
209
- controller,
210
- settled: false,
211
- };
212
- request.timer = setTimeout(() => expireRequest(command, request), Math.max(1, remaining));
213
- request.timer.unref?.();
214
- activeRequests.set(command.commandId, request);
215
- if (remaining <= 0) {
216
- expireRequest(command, request);
217
- throw new Error("Desktop Browser Host received an expired command");
218
- }
219
- return {
220
- done: false,
221
- value: {
222
- frame: command,
223
- signal: controller.signal,
224
- reply: (result) => settle(command, {
225
- type: "desktop.browser.host.reply",
226
- protocolVersion: DESKTOP_BROWSER_HOST_PROTOCOL_VERSION,
227
- leaseId: activeLease.leaseId,
228
- commandId: command.commandId,
229
- ok: true,
230
- result,
231
- }),
232
- reject: (failure) => settle(command, {
233
- type: "desktop.browser.host.reply",
234
- protocolVersion: DESKTOP_BROWSER_HOST_PROTOCOL_VERSION,
235
- leaseId: activeLease.leaseId,
236
- commandId: command.commandId,
237
- ok: false,
238
- error: failure,
239
- }),
240
- },
241
- };
242
- },
243
- };
244
- return {
245
- lease: activeLease,
246
- closed,
247
- [Symbol.asyncIterator]: () => claimedIterator,
248
- emit: (event) => enqueueWrite(async () => {
249
- const nextSequence = eventSequence + 1;
250
- const frame = parseDesktopBrowserHostClientFrame({
251
- type: "desktop.browser.host.event",
252
- protocolVersion: DESKTOP_BROWSER_HOST_PROTOCOL_VERSION,
253
- leaseId: activeLease.leaseId,
254
- eventSequence: nextSequence,
255
- event,
256
- });
257
- if (frame.type !== "desktop.browser.host.event") {
258
- throw new Error("Desktop Browser Host event parser returned the wrong frame");
259
- }
260
- await sendJson(socket, frame);
261
- eventSequence = nextSequence;
262
- }),
263
- emitSurfaceFrame: (frame) => enqueueWrite(async () => {
264
- if (activeLease.capabilities.surface !== "binary-v1") {
265
- throw new Error("Desktop Browser Host Surface was not negotiated");
266
- }
267
- const encoded = encodeDesktopBrowserHostSurfaceFrame({
268
- ...frame,
269
- leaseId: activeLease.leaseId,
270
- });
271
- await sendBinary(socket, encoded);
272
- }),
273
- close: async () => {
274
- if (closing)
275
- return closed;
276
- closing = true;
277
- queue.close();
278
- abortActiveRequests(new Error("Desktop Browser Host stopped"));
279
- if (socket.readyState < WebSocket.CLOSING)
280
- socket.close(1000, "host stopped");
281
- if (socket.readyState === WebSocket.CLOSED)
282
- closedResolve();
283
- await closed;
284
- },
285
- };
286
- }
287
- async function sendBinary(socket, bytes) {
288
- if (socket.readyState !== WebSocket.OPEN)
289
- throw new Error("Desktop Browser Host lease is closed");
290
- if (bytes.byteLength === 0 || bytes.byteLength > DESKTOP_BROWSER_HOST_MAX_BINARY_FRAME_BYTES) {
291
- throw new Error("Desktop Browser Host binary frame exceeded its bound");
292
- }
293
- if (socket.bufferedAmount + bytes.byteLength > MAX_BUFFERED_BYTES) {
294
- throw new Error("Desktop Browser Host output buffer exceeded its bound");
295
- }
296
- await new Promise((resolve, reject) => {
297
- socket.send(bytes, { binary: true, compress: false }, (error) => {
298
- if (error)
299
- reject(error);
300
- else
301
- resolve();
302
- });
303
- });
304
- }
305
- async function sendJson(socket, value) {
306
- if (socket.readyState !== WebSocket.OPEN)
307
- throw new Error("Desktop Browser Host lease is closed");
308
- const bytes = Buffer.from(JSON.stringify(value), "utf8");
309
- if (bytes.byteLength === 0 || bytes.byteLength > DESKTOP_BROWSER_HOST_MAX_CONTROL_FRAME_BYTES) {
310
- throw new Error("Desktop Browser Host client frame exceeded its bound");
311
- }
312
- if (socket.bufferedAmount + bytes.byteLength > MAX_BUFFERED_BYTES) {
313
- throw new Error("Desktop Browser Host output buffer exceeded its bound");
314
- }
315
- await new Promise((resolve, reject) => {
316
- socket.send(bytes, { binary: false, compress: false }, (error) => {
317
- if (error)
318
- reject(error);
319
- else
320
- resolve();
321
- });
322
- });
323
- }
324
- class BoundedCommandQueue {
325
- capacity;
326
- values = [];
327
- waiters = [];
328
- ended = false;
329
- error;
330
- constructor(capacity) {
331
- this.capacity = capacity;
332
- }
333
- push(value) {
334
- if (this.ended)
335
- return false;
336
- const waiter = this.waiters.shift();
337
- if (waiter) {
338
- waiter.resolve({ done: false, value });
339
- return true;
340
- }
341
- if (this.values.length >= this.capacity)
342
- return false;
343
- this.values.push(value);
344
- return true;
345
- }
346
- close(error) {
347
- if (this.ended)
348
- return;
349
- this.ended = true;
350
- this.error = error;
351
- // Commands are capabilities of this exact socket lease. Never drain a
352
- // buffered command after abort/disconnect, even if it was parsed earlier.
353
- this.values.length = 0;
354
- for (const waiter of this.waiters.splice(0)) {
355
- if (error)
356
- waiter.reject(error);
357
- else
358
- waiter.resolve({ done: true, value: undefined });
359
- }
360
- }
361
- [Symbol.asyncIterator]() {
362
- return {
363
- next: () => {
364
- const value = this.values.shift();
365
- if (value)
366
- return Promise.resolve({ done: false, value });
367
- if (this.ended) {
368
- return this.error
369
- ? Promise.reject(this.error)
370
- : Promise.resolve({ done: true, value: undefined });
371
- }
372
- return new Promise((resolve, reject) => this.waiters.push({ resolve, reject }));
373
- },
374
- };
375
- }
376
- }
377
- function rawDataBytes(data) {
378
- if (Buffer.isBuffer(data))
379
- return data;
380
- if (data instanceof ArrayBuffer)
381
- return new Uint8Array(data);
382
- if (Array.isArray(data))
383
- return Buffer.concat(data);
384
- throw new Error("Desktop Browser Host received an unsupported WebSocket payload");
385
- }
386
- function isMutatingCommand(command) {
387
- return command.command.method !== "browser.snapshot" &&
388
- command.command.method !== "browser.cdp-endpoint" &&
389
- command.command.method !== "page.cdp-endpoint";
390
- }
391
- async function abortableTimeout(promise, timeoutMs, signal) {
392
- signal?.throwIfAborted();
393
- const timeout = AbortSignal.timeout(timeoutMs);
394
- const combined = signal ? AbortSignal.any([signal, timeout]) : timeout;
395
- return await new Promise((resolve, reject) => {
396
- const onAbort = () => reject(abortError(combined.reason));
397
- combined.addEventListener("abort", onAbort, { once: true });
398
- promise.then(resolve, reject).finally(() => combined.removeEventListener("abort", onAbort));
399
- });
400
- }
401
- function abortError(reason) {
402
- if (reason instanceof Error)
403
- return reason;
404
- return new Error("Desktop Browser Host connection was aborted", { cause: reason });
405
- }
406
- function asError(error, message) {
407
- return error instanceof Error ? error : new Error(message, { cause: error });
408
- }
409
- function assertLoopbackOrigin(origin) {
410
- const url = new URL(origin);
411
- const host = url.hostname.replace(/^\[|\]$/g, "").toLowerCase();
412
- if (host !== "localhost" && host !== "127.0.0.1" && host !== "::1") {
413
- throw new Error("resident daemon Desktop Browser Host endpoint is not loopback");
414
- }
415
- }
package/dist/setup.d.ts DELETED
@@ -1,20 +0,0 @@
1
- type ControlListenMode = "loopback" | "host";
2
- type ManagedBrowserSetupPlan = {
3
- kind: "ready";
4
- } | {
5
- kind: "unsupported";
6
- } | {
7
- kind: "offer";
8
- installByDefault: boolean;
9
- };
10
- export declare function controlListenModeFromHost(host: unknown): ControlListenMode;
11
- export declare function controlHostForListenMode(mode: ControlListenMode): "127.0.0.1" | "0.0.0.0";
12
- export declare function managedBrowserSetupPlan(listenMode: ControlListenMode, browser: {
13
- supported: boolean;
14
- installed: boolean;
15
- }): ManagedBrowserSetupPlan;
16
- /** Walk the user through config.json step by step. Returns a process exit code. */
17
- export declare function runSetup(): Promise<number>;
18
- /** Read-only diagnostics. Returns a process exit code (0 ok, 1 on any failure). */
19
- export declare function runDoctor(): Promise<number>;
20
- export {};