@rynx-ai/server 0.1.0 → 0.1.10-beta.2

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 (65) hide show
  1. package/dist/browser-surface-control-queue.d.ts +42 -0
  2. package/dist/browser-surface-control-queue.js +187 -0
  3. package/dist/browser-surface-ws.d.ts +16 -0
  4. package/dist/browser-surface-ws.js +356 -0
  5. package/dist/control-api.d.ts +76 -4
  6. package/dist/control-api.js +3014 -133
  7. package/dist/control-web/assets/highlighted-body-OFNGDK62-DHu2g-rk.js +1 -0
  8. package/dist/control-web/assets/index-B6Pb5j9M.js +666 -0
  9. package/dist/control-web/assets/index-BU-_Qud_.css +32 -0
  10. package/dist/control-web/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  11. package/dist/control-web/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  12. package/dist/control-web/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  13. package/dist/control-web/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  14. package/dist/control-web/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  15. package/dist/control-web/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  16. package/dist/control-web/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  17. package/dist/control-web/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  18. package/dist/control-web/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  19. package/dist/control-web/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  20. package/dist/control-web/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  21. package/dist/control-web/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  22. package/dist/control-web/assets/mermaid-GHXKKRXX-CSYzOmBR.js +131 -0
  23. package/dist/control-web/assets/rynx-wordmark-LL1QRYHD.png +0 -0
  24. package/dist/control-web/favicon.png +0 -0
  25. package/dist/control-web/favicon.svg +38 -0
  26. package/dist/control-web/index.html +16 -0
  27. package/dist/control-web-dist.d.ts +1 -1
  28. package/dist/control-web-dist.js +19 -14
  29. package/dist/desktop-browser-host.d.ts +170 -0
  30. package/dist/desktop-browser-host.js +1018 -0
  31. package/dist/direct-runtime-browser-inspect-server.d.ts +72 -0
  32. package/dist/direct-runtime-browser-inspect-server.js +749 -0
  33. package/dist/direct-runtime-browser-surface-server.d.ts +74 -0
  34. package/dist/direct-runtime-browser-surface-server.js +821 -0
  35. package/dist/direct-runtime-server.d.ts +143 -0
  36. package/dist/direct-runtime-server.js +1959 -0
  37. package/dist/emulator-surface-ws.d.ts +8 -0
  38. package/dist/emulator-surface-ws.js +198 -0
  39. package/dist/machine-session-service.d.ts +265 -0
  40. package/dist/machine-session-service.js +822 -0
  41. package/dist/provider-cli-host.d.ts +10 -0
  42. package/dist/provider-cli-host.js +1 -0
  43. package/dist/remote-runtime-dispatcher.d.ts +94 -0
  44. package/dist/remote-runtime-dispatcher.js +403 -0
  45. package/dist/remote-runtime-session-projection.d.ts +19 -0
  46. package/dist/remote-runtime-session-projection.js +566 -0
  47. package/dist/remote-runtime.d.ts +22 -0
  48. package/dist/remote-runtime.js +32 -0
  49. package/dist/runtime-web-auth.d.ts +26 -0
  50. package/dist/runtime-web-auth.js +132 -0
  51. package/dist/server.d.ts +364 -12
  52. package/dist/server.js +341 -100
  53. package/dist/session-browser-service.d.ts +248 -0
  54. package/dist/session-browser-service.js +772 -0
  55. package/dist/session-browser-surface-coordinator.d.ts +24 -0
  56. package/dist/session-browser-surface-coordinator.js +669 -0
  57. package/dist/session-emulator-service.d.ts +167 -0
  58. package/dist/session-emulator-service.js +464 -0
  59. package/dist/session-runtime-index.d.ts +23 -0
  60. package/dist/session-runtime-index.js +96 -0
  61. package/dist/session-terminal-host.d.ts +51 -0
  62. package/dist/session-terminal-host.js +270 -0
  63. package/dist/terminal-ws.d.ts +12 -20
  64. package/dist/terminal-ws.js +421 -97
  65. package/package.json +14 -7
@@ -0,0 +1,10 @@
1
+ import type { ProviderCliId, ProviderCliJob, ProviderCliOperation, ProviderCliStatusList } from "@rynx-ai/protocol/control";
2
+ /** Read-only Runtime-owned Provider CLI inventory, safe to expose over Direct Runtime. */
3
+ export interface ProviderCliStatusHost {
4
+ listStatuses(): Promise<ProviderCliStatusList>;
5
+ }
6
+ /** Local-machine mutation port. Control never forwards this interface remotely. */
7
+ export interface ProviderCliManagementHost extends ProviderCliStatusHost {
8
+ startOperation(provider: ProviderCliId, operation: ProviderCliOperation): Promise<ProviderCliJob>;
9
+ getJob(id: string): ProviderCliJob | undefined;
10
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,94 @@
1
+ import type { RemoteRuntimeEmulatorGesturePoint, RemoteRuntimeRpcRequest, RemoteRuntimeRpcResponse, RemoteRuntimeSequencedSessionEvent } from "@rynx-ai/protocol/remote-runtime-rpc";
2
+ import { type DaemonRuntimeHost } from "./remote-runtime.js";
3
+ import { type MachineSessionService } from "./machine-session-service.js";
4
+ import { type SessionBrowserService } from "./session-browser-service.js";
5
+ import { type SessionEmulatorService } from "./session-emulator-service.js";
6
+ import { type SessionTerminalAttachment, type SessionTerminalHost, type SessionTerminalOpenOptions } from "./session-terminal-host.js";
7
+ import type { ProviderCliStatusHost } from "./provider-cli-host.js";
8
+ /** Binding-neutral application identity established before dispatch. */
9
+ export interface AuthenticatedRuntimeSubject {
10
+ subjectId: string;
11
+ effectiveAuthorities: readonly string[];
12
+ }
13
+ export interface RemoteRuntimeDispatchContext {
14
+ subject: AuthenticatedRuntimeSubject;
15
+ signal?: AbortSignal;
16
+ }
17
+ export interface RemoteRuntimeDispatcherOptions {
18
+ runtime: DaemonRuntimeHost;
19
+ emulator?: RemoteRuntimeEmulatorHost;
20
+ sessionEmulators?: RemoteRuntimeSessionEmulatorHost;
21
+ sessions?: RemoteRuntimeSessionHost;
22
+ browsers?: RemoteRuntimeBrowserHost;
23
+ terminals?: SessionTerminalHost;
24
+ providerClis?: ProviderCliStatusHost;
25
+ /** Optional diagnostic sink. Wire errors deliberately omit internal details. */
26
+ onError?: (error: unknown, request: RemoteRuntimeRpcRequest) => void;
27
+ }
28
+ export type RemoteRuntimeSessionHost = Pick<MachineSessionService, "list" | "snapshot" | "watch" | "interrupt" | "agentOptions" | "launchOptions" | "create" | "sendMessage" | "resourcePolicy" | "beginResourceUpload" | "writeResourceUploadChunk" | "commitResourceUpload" | "getResource" | "readResource" | "deleteResource" | "enqueueMessage" | "startTerminal" | "delete" | "resolveInteraction">;
29
+ export type RemoteRuntimeBrowserHost = Pick<SessionBrowserService, "getState" | "open" | "close" | "createPage" | "closePage" | "activatePage" | "navigatePage" | "goBack" | "goForward" | "reload">;
30
+ export type RemoteRuntimeSessionEmulatorHost = Pick<SessionEmulatorService, "getState" | "listDevices" | "attach" | "detach" | "tap" | "type" | "button" | "rotate" | "launch" | "gesture" | "releaseDevice" | "stopDevice">;
31
+ export interface OpenRemoteRuntimeSessionEvents {
32
+ sessionId: string;
33
+ daemonInstanceId: string;
34
+ events: AsyncIterable<RemoteRuntimeSequencedSessionEvent>;
35
+ cancel(): Promise<void>;
36
+ }
37
+ export type RemoteRuntimeSessionEventsOpenErrorCode = "unauthorized" | "invalid_request" | "method_not_found" | "internal";
38
+ export declare class RemoteRuntimeSessionEventsOpenError extends Error {
39
+ readonly code: RemoteRuntimeSessionEventsOpenErrorCode;
40
+ constructor(code: RemoteRuntimeSessionEventsOpenErrorCode, message: string, options?: ErrorOptions);
41
+ }
42
+ /** Transport-independent emulator application port used by Local and Direct bindings. */
43
+ export interface RemoteRuntimeEmulatorHost {
44
+ devices(): Promise<unknown[]>;
45
+ active(): Promise<unknown | null>;
46
+ deviceSession?(input: {
47
+ device: string;
48
+ }): Promise<unknown | null>;
49
+ attach(device?: string): Promise<unknown>;
50
+ tap(input: {
51
+ x: number;
52
+ y: number;
53
+ device?: string;
54
+ }): Promise<unknown>;
55
+ type(input: {
56
+ text: string;
57
+ device?: string;
58
+ mode?: string;
59
+ }): Promise<unknown>;
60
+ button(input: {
61
+ name?: string;
62
+ device?: string;
63
+ }): Promise<unknown>;
64
+ rotate(input: {
65
+ orientation: string;
66
+ device?: string;
67
+ }): Promise<unknown>;
68
+ launch(input: {
69
+ appId: string;
70
+ device?: string;
71
+ }): Promise<unknown>;
72
+ gesture(input: {
73
+ points: RemoteRuntimeEmulatorGesturePoint[];
74
+ device?: string;
75
+ }): Promise<unknown>;
76
+ kill(input: {
77
+ device?: string;
78
+ }): Promise<unknown>;
79
+ shutdown(input: {
80
+ device?: string;
81
+ }): Promise<unknown>;
82
+ }
83
+ /** Transport-neutral V1 unary dispatcher shared by Local and Direct bindings. */
84
+ export declare class RemoteRuntimeDispatcher {
85
+ private readonly options;
86
+ constructor(options: RemoteRuntimeDispatcherOptions);
87
+ dispatch(request: RemoteRuntimeRpcRequest, { subject, signal }: RemoteRuntimeDispatchContext): Promise<RemoteRuntimeRpcResponse>;
88
+ openSessionEvents(sessionId: string, context: RemoteRuntimeDispatchContext): OpenRemoteRuntimeSessionEvents;
89
+ openSessionTerminal(sessionId: string, options: SessionTerminalOpenOptions, context: RemoteRuntimeDispatchContext): Promise<SessionTerminalAttachment>;
90
+ private withEmulator;
91
+ private withSessions;
92
+ private withSessionEmulators;
93
+ private withBrowsers;
94
+ }
@@ -0,0 +1,403 @@
1
+ import { encodeRemoteRuntimeRpcResponse, parseRemoteRuntimeRpcResponseForMethod, REMOTE_RUNTIME_RPC_METHOD_METADATA, } from "@rynx-ai/protocol/remote-runtime-rpc";
2
+ import { encodeDaemonStatus } from "./remote-runtime.js";
3
+ import { MachineSessionServiceFailure, MachineSessionServiceInputError, } from "./machine-session-service.js";
4
+ import { SessionBrowserServiceError, } from "./session-browser-service.js";
5
+ import { SessionEmulatorServiceError, } from "./session-emulator-service.js";
6
+ import { projectRemoteRuntimeSessionEvent, projectRemoteRuntimeSessionListResult, projectRemoteRuntimeSessionSnapshotResult, } from "./remote-runtime-session-projection.js";
7
+ import { SessionTerminalOpenError, } from "./session-terminal-host.js";
8
+ const FULL_DAEMON_AUTHORITY = "daemon.full";
9
+ export class RemoteRuntimeSessionEventsOpenError extends Error {
10
+ code;
11
+ constructor(code, message, options) {
12
+ super(message, options);
13
+ this.code = code;
14
+ this.name = "RemoteRuntimeSessionEventsOpenError";
15
+ }
16
+ }
17
+ /** Transport-neutral V1 unary dispatcher shared by Local and Direct bindings. */
18
+ export class RemoteRuntimeDispatcher {
19
+ options;
20
+ constructor(options) {
21
+ this.options = options;
22
+ }
23
+ async dispatch(request, { subject, signal }) {
24
+ if (signal?.aborted) {
25
+ return rpcError(request.id, "invalid_request", "Remote Runtime request was cancelled");
26
+ }
27
+ if (!subject.effectiveAuthorities.includes(FULL_DAEMON_AUTHORITY)) {
28
+ return rpcError(request.id, "unauthorized", "Remote Runtime authority is insufficient");
29
+ }
30
+ try {
31
+ switch (request.method) {
32
+ case "status.get":
33
+ return rpcResult(request, encodeDaemonStatus(this.options.runtime.status()));
34
+ case "provider.cli.status.list":
35
+ return this.options.providerClis
36
+ ? rpcResult(request, await this.options.providerClis.listStatuses())
37
+ : rpcError(request.id, "method_not_found", "Provider CLI status is unavailable");
38
+ case "emulator.devices.list":
39
+ return await this.withEmulator(request, (emulator) => emulator.devices());
40
+ case "emulator.active.get":
41
+ return await this.withEmulator(request, (emulator) => {
42
+ if (!request.params.device)
43
+ return emulator.active();
44
+ if (!emulator.deviceSession) {
45
+ throw new Error("device-scoped emulator sessions are unavailable");
46
+ }
47
+ return emulator.deviceSession({ device: request.params.device });
48
+ });
49
+ case "emulator.attach":
50
+ return await this.withEmulator(request, (emulator) => emulator.attach(request.params.device));
51
+ case "emulator.tap":
52
+ return await this.withEmulator(request, (emulator) => emulator.tap(request.params));
53
+ case "emulator.type":
54
+ return await this.withEmulator(request, (emulator) => emulator.type(request.params));
55
+ case "emulator.button":
56
+ return await this.withEmulator(request, (emulator) => emulator.button(request.params));
57
+ case "emulator.rotate":
58
+ return await this.withEmulator(request, (emulator) => emulator.rotate(request.params));
59
+ case "emulator.launch":
60
+ return await this.withEmulator(request, (emulator) => emulator.launch(request.params));
61
+ case "emulator.gesture":
62
+ return await this.withEmulator(request, (emulator) => emulator.gesture(request.params));
63
+ case "emulator.kill":
64
+ return this.options.sessionEmulators
65
+ ? rpcResult(request, await this.options.sessionEmulators.stopDevice({
66
+ action: "kill",
67
+ ...(request.params.device ? { device: request.params.device } : {}),
68
+ }))
69
+ : await this.withEmulator(request, (emulator) => emulator.kill(request.params));
70
+ case "emulator.shutdown":
71
+ return this.options.sessionEmulators
72
+ ? rpcResult(request, await this.options.sessionEmulators.stopDevice({
73
+ action: "shutdown",
74
+ ...(request.params.device ? { device: request.params.device } : {}),
75
+ }))
76
+ : await this.withEmulator(request, (emulator) => emulator.shutdown(request.params));
77
+ case "session.emulator.state.get":
78
+ return await this.withSessionEmulators(request, (emulators) => emulators.getState(request.params));
79
+ case "session.emulator.devices.list":
80
+ return await this.withSessionEmulators(request, (emulators) => emulators.listDevices(request.params));
81
+ case "session.emulator.attach":
82
+ return await this.withSessionEmulators(request, (emulators) => emulators.attach(request.params));
83
+ case "session.emulator.detach":
84
+ return await this.withSessionEmulators(request, (emulators) => emulators.detach(request.params));
85
+ case "session.emulator.tap":
86
+ return await this.withSessionEmulators(request, (emulators) => emulators.tap(request.params));
87
+ case "session.emulator.type":
88
+ return await this.withSessionEmulators(request, (emulators) => emulators.type(request.params));
89
+ case "session.emulator.button":
90
+ return await this.withSessionEmulators(request, (emulators) => emulators.button(request.params));
91
+ case "session.emulator.rotate":
92
+ return await this.withSessionEmulators(request, (emulators) => emulators.rotate(request.params));
93
+ case "session.emulator.launch":
94
+ return await this.withSessionEmulators(request, (emulators) => emulators.launch(request.params));
95
+ case "session.emulator.gesture":
96
+ return await this.withSessionEmulators(request, (emulators) => emulators.gesture(request.params));
97
+ case "session.list":
98
+ return await this.withSessions(request, async (sessions) => projectRemoteRuntimeSessionListResult(await sessions.list(request.params)));
99
+ case "session.agentOptions.list":
100
+ return await this.withSessions(request, (sessions) => sessions.agentOptions());
101
+ case "session.launchOptions.list":
102
+ return await this.withSessions(request, (sessions) => sessions.launchOptions());
103
+ case "session.create":
104
+ return await this.withSessions(request, (sessions) => sessions.create(request.params));
105
+ case "session.message.send":
106
+ return await this.withSessions(request, (sessions) => {
107
+ const { sessionId, ...input } = request.params;
108
+ return sessions.sendMessage(sessionId, input.content === undefined && input.clientMessageId === undefined
109
+ ? input.message
110
+ : input);
111
+ });
112
+ case "session.resource.policy.get":
113
+ return await this.withSessions(request, async (sessions) => sessions.resourcePolicy(request.params.sessionId));
114
+ case "session.resource.upload.begin":
115
+ return await this.withSessions(request, (sessions) => sessions.beginResourceUpload(request.params));
116
+ case "session.resource.upload.chunk":
117
+ return await this.withSessions(request, (sessions) => sessions.writeResourceUploadChunk(request.params));
118
+ case "session.resource.upload.commit":
119
+ return await this.withSessions(request, (sessions) => sessions.commitResourceUpload(request.params));
120
+ case "session.resource.get":
121
+ return await this.withSessions(request, (sessions) => sessions.getResource(request.params));
122
+ case "session.resource.read":
123
+ return await this.withSessions(request, (sessions) => sessions.readResource(request.params));
124
+ case "session.resource.delete":
125
+ return await this.withSessions(request, (sessions) => sessions.deleteResource(request.params));
126
+ case "session.message.enqueue":
127
+ return await this.withSessions(request, (sessions) => sessions.enqueueMessage(request.params.sessionId, request.params.message));
128
+ case "session.terminal.start":
129
+ return await this.withSessions(request, (sessions) => sessions.startTerminal(request.params.sessionId));
130
+ case "session.delete":
131
+ return await this.withSessions(request, (sessions) => sessions.delete(request.params.sessionId));
132
+ case "session.snapshot.get":
133
+ return await this.withSessions(request, async (sessions) => projectRemoteRuntimeSessionSnapshotResult(await sessions.snapshot(request.params)));
134
+ case "session.interrupt":
135
+ return await this.withSessions(request, (sessions) => sessions.interrupt(request.params.sessionId));
136
+ case "session.interaction.resolve":
137
+ return await this.withSessions(request, (sessions) => sessions.resolveInteraction(request.params.sessionId, request.params.interactionId, request.params.resolution));
138
+ case "browser.state.get":
139
+ return await this.withBrowsers(request, (browsers) => browsers.getState(request.params));
140
+ case "browser.open":
141
+ return await this.withBrowsers(request, (browsers) => browsers.open(request.params));
142
+ case "browser.close":
143
+ return await this.withBrowsers(request, (browsers) => browsers.close(request.params));
144
+ case "browser.page.create":
145
+ return await this.withBrowsers(request, (browsers) => browsers.createPage(request.params));
146
+ case "browser.page.close":
147
+ return await this.withBrowsers(request, (browsers) => browsers.closePage(request.params));
148
+ case "browser.page.activate":
149
+ return await this.withBrowsers(request, (browsers) => browsers.activatePage(request.params));
150
+ case "browser.page.navigate":
151
+ return await this.withBrowsers(request, (browsers) => browsers.navigatePage(request.params));
152
+ case "browser.page.back":
153
+ return await this.withBrowsers(request, (browsers) => browsers.goBack(request.params));
154
+ case "browser.page.forward":
155
+ return await this.withBrowsers(request, (browsers) => browsers.goForward(request.params));
156
+ case "browser.page.reload":
157
+ return await this.withBrowsers(request, (browsers) => browsers.reload(request.params));
158
+ }
159
+ return rpcError(request.id, "method_not_found", "Remote Runtime method is not supported");
160
+ }
161
+ catch (error) {
162
+ this.options.onError?.(error, request);
163
+ const mapped = mapHostError(request, error);
164
+ return rpcError(request.id, mapped.code, mapped.message);
165
+ }
166
+ }
167
+ openSessionEvents(sessionId, context) {
168
+ if (context.signal?.aborted) {
169
+ throw new RemoteRuntimeSessionEventsOpenError("invalid_request", "Remote Session subscription was cancelled");
170
+ }
171
+ if (!context.subject.effectiveAuthorities.includes(FULL_DAEMON_AUTHORITY)) {
172
+ throw new RemoteRuntimeSessionEventsOpenError("unauthorized", "Remote Runtime authority is insufficient");
173
+ }
174
+ const sessions = this.options.sessions;
175
+ if (!sessions) {
176
+ throw new RemoteRuntimeSessionEventsOpenError("method_not_found", "Remote Runtime sessions are unavailable");
177
+ }
178
+ const daemonInstanceId = this.options.runtime.status().daemonInstanceId;
179
+ let watch;
180
+ try {
181
+ // MachineSessionService.watch installs its bus listener synchronously.
182
+ watch = sessions.watch(sessionId, { signal: context.signal });
183
+ }
184
+ catch (error) {
185
+ if (error instanceof MachineSessionServiceInputError) {
186
+ throw new RemoteRuntimeSessionEventsOpenError("invalid_request", "Remote Session subscription argument is invalid", { cause: error });
187
+ }
188
+ throw new RemoteRuntimeSessionEventsOpenError("internal", "Remote Session subscription could not be opened", { cause: error });
189
+ }
190
+ if (watch.sessionId !== sessionId) {
191
+ void watch.cancel().catch(() => undefined);
192
+ throw new RemoteRuntimeSessionEventsOpenError("internal", "Remote Session subscription identity mismatch");
193
+ }
194
+ const cancel = idempotentCancel(watch);
195
+ return {
196
+ sessionId,
197
+ daemonInstanceId,
198
+ events: projectedSessionEvents(watch, sessionId, cancel),
199
+ cancel,
200
+ };
201
+ }
202
+ async openSessionTerminal(sessionId, options, context) {
203
+ if (context.signal?.aborted) {
204
+ throw new SessionTerminalOpenError("invalid_request", "Remote Session terminal open was cancelled");
205
+ }
206
+ if (!context.subject.effectiveAuthorities.includes(FULL_DAEMON_AUTHORITY)) {
207
+ throw new SessionTerminalOpenError("unauthorized", "Remote Runtime authority is insufficient");
208
+ }
209
+ const terminals = this.options.terminals;
210
+ if (!terminals) {
211
+ throw new SessionTerminalOpenError("method_not_found", "Remote Runtime Session terminals are unavailable");
212
+ }
213
+ try {
214
+ const attachment = await terminals.open(sessionId, {
215
+ ...options,
216
+ ...(context.signal ? { signal: context.signal } : {}),
217
+ });
218
+ if (attachment.sessionId !== sessionId) {
219
+ await attachment.close().catch(() => undefined);
220
+ throw new SessionTerminalOpenError("internal", "Remote Session terminal identity mismatch");
221
+ }
222
+ return attachment;
223
+ }
224
+ catch (error) {
225
+ if (error instanceof SessionTerminalOpenError)
226
+ throw error;
227
+ throw new SessionTerminalOpenError("internal", "Remote Session terminal could not be opened", { cause: error });
228
+ }
229
+ }
230
+ async withEmulator(request, invoke) {
231
+ const emulator = this.options.emulator;
232
+ if (!emulator) {
233
+ return rpcError(request.id, "method_not_found", "Remote Runtime emulator is unavailable");
234
+ }
235
+ return rpcResult(request, await invoke(emulator));
236
+ }
237
+ async withSessions(request, invoke) {
238
+ const sessions = this.options.sessions;
239
+ if (!sessions) {
240
+ return rpcError(request.id, "method_not_found", "Remote Runtime sessions are unavailable");
241
+ }
242
+ return rpcResult(request, await invoke(sessions));
243
+ }
244
+ async withSessionEmulators(request, invoke) {
245
+ const emulators = this.options.sessionEmulators;
246
+ if (!emulators) {
247
+ return rpcError(request.id, "method_not_found", "Remote Runtime Session Emulator is unavailable");
248
+ }
249
+ return rpcResult(request, await invoke(emulators));
250
+ }
251
+ async withBrowsers(request, invoke) {
252
+ const browsers = this.options.browsers;
253
+ if (!browsers) {
254
+ return rpcError(request.id, "method_not_found", "Remote Runtime Browser is unavailable");
255
+ }
256
+ return rpcResult(request, await invoke(browsers));
257
+ }
258
+ }
259
+ function mapHostError(request, error) {
260
+ if (error instanceof SessionBrowserServiceError) {
261
+ switch (error.code) {
262
+ case "invalid_request":
263
+ return { code: "invalid_request", message: "Browser request argument is invalid" };
264
+ case "not_found":
265
+ return { code: "not_found", message: "Browser resource was not found" };
266
+ case "generation_mismatch":
267
+ return { code: "conflict", message: "Browser generation is stale" };
268
+ case "unavailable":
269
+ case "capacity":
270
+ case "shutting_down":
271
+ return { code: "failed_precondition", message: "Browser operation is unavailable" };
272
+ case "outcome_unknown":
273
+ return { code: "outcome_unknown", message: "Browser operation may have been applied" };
274
+ case "host_failure":
275
+ return { code: "internal", message: "Browser Host request failed" };
276
+ }
277
+ }
278
+ if (error instanceof SessionEmulatorServiceError) {
279
+ switch (error.code) {
280
+ case "session_not_found":
281
+ case "device_not_found":
282
+ return { code: "not_found", message: "Session Emulator resource was not found" };
283
+ case "device_in_use":
284
+ case "binding_mismatch":
285
+ return { code: "conflict", message: "Session Emulator binding changed or is in use" };
286
+ case "emulator_unavailable":
287
+ return { code: "failed_precondition", message: "Session Emulator is unavailable" };
288
+ }
289
+ }
290
+ if (error instanceof MachineSessionServiceInputError) {
291
+ return { code: "invalid_request", message: "Remote Session request argument is invalid" };
292
+ }
293
+ if (error instanceof MachineSessionServiceFailure) {
294
+ const message = error.code === "not_found"
295
+ ? "Remote Session resource was not found"
296
+ : error.code === "failed_precondition"
297
+ ? "Remote Session operation is unavailable"
298
+ : error.code === "conflict"
299
+ ? "Remote Session resource conflicts with existing state"
300
+ : "Remote Session operation may have been applied";
301
+ return { code: error.code, message };
302
+ }
303
+ if (request.method === "session.interrupt") {
304
+ return {
305
+ code: "outcome_unknown",
306
+ message: "Session interrupt may have been applied",
307
+ };
308
+ }
309
+ if (request.method.startsWith("session.") &&
310
+ REMOTE_RUNTIME_RPC_METHOD_METADATA[request.method].retryPolicy === "never") {
311
+ return {
312
+ code: "outcome_unknown",
313
+ message: "Remote Session operation may have been applied",
314
+ };
315
+ }
316
+ if (!request.method.startsWith("emulator.")) {
317
+ return { code: "internal", message: "Remote Runtime request failed" };
318
+ }
319
+ const emulatorCode = emulatorHostErrorCode(error);
320
+ if (emulatorCode === "emulator_invalid_argument") {
321
+ return { code: "invalid_request", message: "Emulator operation argument is invalid" };
322
+ }
323
+ if (emulatorCode === "emulator_device_not_found" || emulatorCode === "emulator_app_not_found") {
324
+ return { code: "not_found", message: "Emulator device or application was not found" };
325
+ }
326
+ if (emulatorCode === "emulator_android_unavailable" ||
327
+ emulatorCode === "emulator_ios_unavailable" ||
328
+ emulatorCode === "emulator_android_device_unavailable" ||
329
+ emulatorCode === "emulator_unsupported") {
330
+ return { code: "failed_precondition", message: "Emulator operation is unavailable" };
331
+ }
332
+ if (emulatorCode === "emulator_command_failed" && sentEmulatorFrames(error) === 0) {
333
+ return { code: "operation_failed", message: "Emulator operation failed before input was sent" };
334
+ }
335
+ if (REMOTE_RUNTIME_RPC_METHOD_METADATA[request.method].retryPolicy === "never") {
336
+ return {
337
+ code: "outcome_unknown",
338
+ message: "Emulator operation may have been partially applied",
339
+ };
340
+ }
341
+ return { code: "internal", message: "Remote Runtime request failed" };
342
+ }
343
+ function idempotentCancel(watch) {
344
+ let pending;
345
+ return () => {
346
+ pending ??= Promise.resolve().then(() => watch.cancel());
347
+ return pending;
348
+ };
349
+ }
350
+ function projectedSessionEvents(watch, sessionId, cancel) {
351
+ const source = watch[Symbol.asyncIterator]();
352
+ return {
353
+ [Symbol.asyncIterator]() {
354
+ return {
355
+ async next() {
356
+ try {
357
+ const next = await source.next();
358
+ if (next.done) {
359
+ await cancel();
360
+ return { value: undefined, done: true };
361
+ }
362
+ return {
363
+ value: projectRemoteRuntimeSessionEvent(next.value, sessionId),
364
+ done: false,
365
+ };
366
+ }
367
+ catch (error) {
368
+ await cancel().catch(() => undefined);
369
+ throw error;
370
+ }
371
+ },
372
+ async return() {
373
+ await cancel();
374
+ return { value: undefined, done: true };
375
+ },
376
+ };
377
+ },
378
+ };
379
+ }
380
+ function emulatorHostErrorCode(error) {
381
+ if (!error || typeof error !== "object" || !("code" in error))
382
+ return undefined;
383
+ const code = error.code;
384
+ return typeof code === "string" && code.startsWith("emulator_") ? code : undefined;
385
+ }
386
+ function sentEmulatorFrames(error) {
387
+ if (!error || typeof error !== "object" || !("sentFrames" in error))
388
+ return undefined;
389
+ const sentFrames = error.sentFrames;
390
+ return typeof sentFrames === "number" && Number.isSafeInteger(sentFrames) && sentFrames >= 0
391
+ ? sentFrames
392
+ : undefined;
393
+ }
394
+ function rpcResult(request, result) {
395
+ const response = parseRemoteRuntimeRpcResponseForMethod({ type: "rpc.result", id: request.id, result }, request.method);
396
+ // Enforce the Direct binding's aggregate frame budget even for the Local
397
+ // binding, so transport selection cannot change accepted result semantics.
398
+ encodeRemoteRuntimeRpcResponse(response, request.method);
399
+ return response;
400
+ }
401
+ function rpcError(id, code, message) {
402
+ return { type: "rpc.error", id, error: { code, message } };
403
+ }
@@ -0,0 +1,19 @@
1
+ import { type SequencedSessionEvent, type SessionItem } from "@rynx-ai/protocol";
2
+ import { type RemoteRuntimeSequencedSessionEvent, type RemoteRuntimeSessionItem, type RemoteRuntimeSessionListResult, type RemoteRuntimeSessionSnapshotResult } from "@rynx-ai/protocol/remote-runtime-rpc";
3
+ export declare class RemoteRuntimeSessionProjectionError extends Error {
4
+ constructor(message: string, options?: ErrorOptions);
5
+ }
6
+ /** Validate the list application result without truncating identity or summaries. */
7
+ export declare function projectRemoteRuntimeSessionListResult(value: unknown): RemoteRuntimeSessionListResult;
8
+ /** Project canonical history payloads into the bounded Direct Runtime DTO. */
9
+ export declare function projectRemoteRuntimeSessionSnapshotResult(value: {
10
+ sessionId: string;
11
+ items: SessionItem[];
12
+ runtime: unknown;
13
+ hasMore: boolean;
14
+ nextAfterId?: string;
15
+ }): RemoteRuntimeSessionSnapshotResult;
16
+ /** Project one canonical durable item, preserving every identity and array entry. */
17
+ export declare function projectRemoteRuntimeSessionItem(item: SessionItem): RemoteRuntimeSessionItem;
18
+ /** Project one live event and reject any event that can be proven cross-session. */
19
+ export declare function projectRemoteRuntimeSessionEvent(event: SequencedSessionEvent, expectedSessionId?: string): RemoteRuntimeSequencedSessionEvent;