@rynx-ai/server 0.1.0 → 0.1.9
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/dist/browser-surface-control-queue.d.ts +42 -0
- package/dist/browser-surface-control-queue.js +187 -0
- package/dist/browser-surface-ws.d.ts +14 -0
- package/dist/browser-surface-ws.js +355 -0
- package/dist/control-api.d.ts +59 -4
- package/dist/control-api.js +2213 -132
- package/dist/control-web/assets/highlighted-body-OFNGDK62-e-w61YLy.js +1 -0
- package/dist/control-web/assets/index-BQ7XVBKO.css +32 -0
- package/dist/control-web/assets/index-ClpzuBJx.js +606 -0
- package/dist/control-web/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/dist/control-web/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/dist/control-web/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/dist/control-web/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/dist/control-web/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/dist/control-web/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/dist/control-web/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/dist/control-web/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- package/dist/control-web/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- package/dist/control-web/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/dist/control-web/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/dist/control-web/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- package/dist/control-web/assets/mermaid-GHXKKRXX-DZn7Hbr2.js +131 -0
- package/dist/control-web/assets/rynx-wordmark-LL1QRYHD.png +0 -0
- package/dist/control-web/favicon.png +0 -0
- package/dist/control-web/favicon.svg +38 -0
- package/dist/control-web/index.html +16 -0
- package/dist/control-web-dist.d.ts +1 -1
- package/dist/control-web-dist.js +19 -14
- package/dist/desktop-browser-host.d.ts +153 -0
- package/dist/desktop-browser-host.js +936 -0
- package/dist/direct-runtime-browser-surface-server.d.ts +74 -0
- package/dist/direct-runtime-browser-surface-server.js +821 -0
- package/dist/direct-runtime-server.d.ts +137 -0
- package/dist/direct-runtime-server.js +1931 -0
- package/dist/emulator-surface-ws.d.ts +6 -0
- package/dist/emulator-surface-ws.js +197 -0
- package/dist/machine-session-service.d.ts +182 -0
- package/dist/machine-session-service.js +559 -0
- package/dist/provider-cli-host.d.ts +10 -0
- package/dist/provider-cli-host.js +1 -0
- package/dist/remote-runtime-dispatcher.d.ts +94 -0
- package/dist/remote-runtime-dispatcher.js +380 -0
- package/dist/remote-runtime-session-projection.d.ts +19 -0
- package/dist/remote-runtime-session-projection.js +556 -0
- package/dist/remote-runtime.d.ts +22 -0
- package/dist/remote-runtime.js +32 -0
- package/dist/runtime-web-auth.d.ts +21 -0
- package/dist/runtime-web-auth.js +92 -0
- package/dist/server.d.ts +292 -10
- package/dist/server.js +314 -98
- package/dist/session-browser-service.d.ts +218 -0
- package/dist/session-browser-service.js +686 -0
- package/dist/session-browser-surface-coordinator.d.ts +23 -0
- package/dist/session-browser-surface-coordinator.js +563 -0
- package/dist/session-emulator-service.d.ts +167 -0
- package/dist/session-emulator-service.js +464 -0
- package/dist/session-runtime-index.d.ts +17 -0
- package/dist/session-runtime-index.js +86 -0
- package/dist/session-terminal-host.d.ts +46 -0
- package/dist/session-terminal-host.js +252 -0
- package/dist/terminal-ws.d.ts +9 -20
- package/dist/terminal-ws.js +420 -97
- package/package.json +9 -7
|
@@ -0,0 +1,380 @@
|
|
|
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) => sessions.sendMessage(request.params.sessionId, request.params.message));
|
|
107
|
+
case "session.terminal.start":
|
|
108
|
+
return await this.withSessions(request, (sessions) => sessions.startTerminal(request.params.sessionId));
|
|
109
|
+
case "session.delete":
|
|
110
|
+
return await this.withSessions(request, (sessions) => sessions.delete(request.params.sessionId));
|
|
111
|
+
case "session.snapshot.get":
|
|
112
|
+
return await this.withSessions(request, async (sessions) => projectRemoteRuntimeSessionSnapshotResult(await sessions.snapshot(request.params)));
|
|
113
|
+
case "session.interrupt":
|
|
114
|
+
return await this.withSessions(request, (sessions) => sessions.interrupt(request.params.sessionId));
|
|
115
|
+
case "session.interaction.resolve":
|
|
116
|
+
return await this.withSessions(request, (sessions) => sessions.resolveInteraction(request.params.sessionId, request.params.interactionId, request.params.resolution));
|
|
117
|
+
case "browser.state.get":
|
|
118
|
+
return await this.withBrowsers(request, (browsers) => browsers.getState(request.params));
|
|
119
|
+
case "browser.open":
|
|
120
|
+
return await this.withBrowsers(request, (browsers) => browsers.open(request.params));
|
|
121
|
+
case "browser.close":
|
|
122
|
+
return await this.withBrowsers(request, (browsers) => browsers.close(request.params));
|
|
123
|
+
case "browser.page.create":
|
|
124
|
+
return await this.withBrowsers(request, (browsers) => browsers.createPage(request.params));
|
|
125
|
+
case "browser.page.close":
|
|
126
|
+
return await this.withBrowsers(request, (browsers) => browsers.closePage(request.params));
|
|
127
|
+
case "browser.page.activate":
|
|
128
|
+
return await this.withBrowsers(request, (browsers) => browsers.activatePage(request.params));
|
|
129
|
+
case "browser.page.navigate":
|
|
130
|
+
return await this.withBrowsers(request, (browsers) => browsers.navigatePage(request.params));
|
|
131
|
+
case "browser.page.back":
|
|
132
|
+
return await this.withBrowsers(request, (browsers) => browsers.goBack(request.params));
|
|
133
|
+
case "browser.page.forward":
|
|
134
|
+
return await this.withBrowsers(request, (browsers) => browsers.goForward(request.params));
|
|
135
|
+
case "browser.page.reload":
|
|
136
|
+
return await this.withBrowsers(request, (browsers) => browsers.reload(request.params));
|
|
137
|
+
}
|
|
138
|
+
return rpcError(request.id, "method_not_found", "Remote Runtime method is not supported");
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
this.options.onError?.(error, request);
|
|
142
|
+
const mapped = mapHostError(request, error);
|
|
143
|
+
return rpcError(request.id, mapped.code, mapped.message);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
openSessionEvents(sessionId, context) {
|
|
147
|
+
if (context.signal?.aborted) {
|
|
148
|
+
throw new RemoteRuntimeSessionEventsOpenError("invalid_request", "Remote Session subscription was cancelled");
|
|
149
|
+
}
|
|
150
|
+
if (!context.subject.effectiveAuthorities.includes(FULL_DAEMON_AUTHORITY)) {
|
|
151
|
+
throw new RemoteRuntimeSessionEventsOpenError("unauthorized", "Remote Runtime authority is insufficient");
|
|
152
|
+
}
|
|
153
|
+
const sessions = this.options.sessions;
|
|
154
|
+
if (!sessions) {
|
|
155
|
+
throw new RemoteRuntimeSessionEventsOpenError("method_not_found", "Remote Runtime sessions are unavailable");
|
|
156
|
+
}
|
|
157
|
+
const daemonInstanceId = this.options.runtime.status().daemonInstanceId;
|
|
158
|
+
let watch;
|
|
159
|
+
try {
|
|
160
|
+
// MachineSessionService.watch installs its bus listener synchronously.
|
|
161
|
+
watch = sessions.watch(sessionId, { signal: context.signal });
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
if (error instanceof MachineSessionServiceInputError) {
|
|
165
|
+
throw new RemoteRuntimeSessionEventsOpenError("invalid_request", "Remote Session subscription argument is invalid", { cause: error });
|
|
166
|
+
}
|
|
167
|
+
throw new RemoteRuntimeSessionEventsOpenError("internal", "Remote Session subscription could not be opened", { cause: error });
|
|
168
|
+
}
|
|
169
|
+
if (watch.sessionId !== sessionId) {
|
|
170
|
+
void watch.cancel().catch(() => undefined);
|
|
171
|
+
throw new RemoteRuntimeSessionEventsOpenError("internal", "Remote Session subscription identity mismatch");
|
|
172
|
+
}
|
|
173
|
+
const cancel = idempotentCancel(watch);
|
|
174
|
+
return {
|
|
175
|
+
sessionId,
|
|
176
|
+
daemonInstanceId,
|
|
177
|
+
events: projectedSessionEvents(watch, sessionId, cancel),
|
|
178
|
+
cancel,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
async openSessionTerminal(sessionId, options, context) {
|
|
182
|
+
if (context.signal?.aborted) {
|
|
183
|
+
throw new SessionTerminalOpenError("invalid_request", "Remote Session terminal open was cancelled");
|
|
184
|
+
}
|
|
185
|
+
if (!context.subject.effectiveAuthorities.includes(FULL_DAEMON_AUTHORITY)) {
|
|
186
|
+
throw new SessionTerminalOpenError("unauthorized", "Remote Runtime authority is insufficient");
|
|
187
|
+
}
|
|
188
|
+
const terminals = this.options.terminals;
|
|
189
|
+
if (!terminals) {
|
|
190
|
+
throw new SessionTerminalOpenError("method_not_found", "Remote Runtime Session terminals are unavailable");
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
const attachment = await terminals.open(sessionId, {
|
|
194
|
+
...options,
|
|
195
|
+
...(context.signal ? { signal: context.signal } : {}),
|
|
196
|
+
});
|
|
197
|
+
if (attachment.sessionId !== sessionId) {
|
|
198
|
+
await attachment.close().catch(() => undefined);
|
|
199
|
+
throw new SessionTerminalOpenError("internal", "Remote Session terminal identity mismatch");
|
|
200
|
+
}
|
|
201
|
+
return attachment;
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
if (error instanceof SessionTerminalOpenError)
|
|
205
|
+
throw error;
|
|
206
|
+
throw new SessionTerminalOpenError("internal", "Remote Session terminal could not be opened", { cause: error });
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
async withEmulator(request, invoke) {
|
|
210
|
+
const emulator = this.options.emulator;
|
|
211
|
+
if (!emulator) {
|
|
212
|
+
return rpcError(request.id, "method_not_found", "Remote Runtime emulator is unavailable");
|
|
213
|
+
}
|
|
214
|
+
return rpcResult(request, await invoke(emulator));
|
|
215
|
+
}
|
|
216
|
+
async withSessions(request, invoke) {
|
|
217
|
+
const sessions = this.options.sessions;
|
|
218
|
+
if (!sessions) {
|
|
219
|
+
return rpcError(request.id, "method_not_found", "Remote Runtime sessions are unavailable");
|
|
220
|
+
}
|
|
221
|
+
return rpcResult(request, await invoke(sessions));
|
|
222
|
+
}
|
|
223
|
+
async withSessionEmulators(request, invoke) {
|
|
224
|
+
const emulators = this.options.sessionEmulators;
|
|
225
|
+
if (!emulators) {
|
|
226
|
+
return rpcError(request.id, "method_not_found", "Remote Runtime Session Emulator is unavailable");
|
|
227
|
+
}
|
|
228
|
+
return rpcResult(request, await invoke(emulators));
|
|
229
|
+
}
|
|
230
|
+
async withBrowsers(request, invoke) {
|
|
231
|
+
const browsers = this.options.browsers;
|
|
232
|
+
if (!browsers) {
|
|
233
|
+
return rpcError(request.id, "method_not_found", "Remote Runtime Browser is unavailable");
|
|
234
|
+
}
|
|
235
|
+
return rpcResult(request, await invoke(browsers));
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function mapHostError(request, error) {
|
|
239
|
+
if (error instanceof SessionBrowserServiceError) {
|
|
240
|
+
switch (error.code) {
|
|
241
|
+
case "invalid_request":
|
|
242
|
+
return { code: "invalid_request", message: "Browser request argument is invalid" };
|
|
243
|
+
case "not_found":
|
|
244
|
+
return { code: "not_found", message: "Browser resource was not found" };
|
|
245
|
+
case "generation_mismatch":
|
|
246
|
+
return { code: "conflict", message: "Browser generation is stale" };
|
|
247
|
+
case "unavailable":
|
|
248
|
+
case "capacity":
|
|
249
|
+
case "shutting_down":
|
|
250
|
+
return { code: "failed_precondition", message: "Browser operation is unavailable" };
|
|
251
|
+
case "outcome_unknown":
|
|
252
|
+
return { code: "outcome_unknown", message: "Browser operation may have been applied" };
|
|
253
|
+
case "host_failure":
|
|
254
|
+
return { code: "internal", message: "Browser Host request failed" };
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (error instanceof SessionEmulatorServiceError) {
|
|
258
|
+
switch (error.code) {
|
|
259
|
+
case "session_not_found":
|
|
260
|
+
case "device_not_found":
|
|
261
|
+
return { code: "not_found", message: "Session Emulator resource was not found" };
|
|
262
|
+
case "device_in_use":
|
|
263
|
+
case "binding_mismatch":
|
|
264
|
+
return { code: "conflict", message: "Session Emulator binding changed or is in use" };
|
|
265
|
+
case "emulator_unavailable":
|
|
266
|
+
return { code: "failed_precondition", message: "Session Emulator is unavailable" };
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (error instanceof MachineSessionServiceInputError) {
|
|
270
|
+
return { code: "invalid_request", message: "Remote Session request argument is invalid" };
|
|
271
|
+
}
|
|
272
|
+
if (error instanceof MachineSessionServiceFailure) {
|
|
273
|
+
const message = error.code === "not_found"
|
|
274
|
+
? "Remote Session resource was not found"
|
|
275
|
+
: error.code === "failed_precondition"
|
|
276
|
+
? "Remote Session operation is unavailable"
|
|
277
|
+
: "Remote Session operation may have been applied";
|
|
278
|
+
return { code: error.code, message };
|
|
279
|
+
}
|
|
280
|
+
if (request.method === "session.interrupt") {
|
|
281
|
+
return {
|
|
282
|
+
code: "outcome_unknown",
|
|
283
|
+
message: "Session interrupt may have been applied",
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
if (request.method.startsWith("session.") &&
|
|
287
|
+
REMOTE_RUNTIME_RPC_METHOD_METADATA[request.method].retryPolicy === "never") {
|
|
288
|
+
return {
|
|
289
|
+
code: "outcome_unknown",
|
|
290
|
+
message: "Remote Session operation may have been applied",
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
if (!request.method.startsWith("emulator.")) {
|
|
294
|
+
return { code: "internal", message: "Remote Runtime request failed" };
|
|
295
|
+
}
|
|
296
|
+
const emulatorCode = emulatorHostErrorCode(error);
|
|
297
|
+
if (emulatorCode === "emulator_invalid_argument") {
|
|
298
|
+
return { code: "invalid_request", message: "Emulator operation argument is invalid" };
|
|
299
|
+
}
|
|
300
|
+
if (emulatorCode === "emulator_device_not_found" || emulatorCode === "emulator_app_not_found") {
|
|
301
|
+
return { code: "not_found", message: "Emulator device or application was not found" };
|
|
302
|
+
}
|
|
303
|
+
if (emulatorCode === "emulator_android_unavailable" ||
|
|
304
|
+
emulatorCode === "emulator_ios_unavailable" ||
|
|
305
|
+
emulatorCode === "emulator_android_device_unavailable" ||
|
|
306
|
+
emulatorCode === "emulator_unsupported") {
|
|
307
|
+
return { code: "failed_precondition", message: "Emulator operation is unavailable" };
|
|
308
|
+
}
|
|
309
|
+
if (emulatorCode === "emulator_command_failed" && sentEmulatorFrames(error) === 0) {
|
|
310
|
+
return { code: "operation_failed", message: "Emulator operation failed before input was sent" };
|
|
311
|
+
}
|
|
312
|
+
if (REMOTE_RUNTIME_RPC_METHOD_METADATA[request.method].retryPolicy === "never") {
|
|
313
|
+
return {
|
|
314
|
+
code: "outcome_unknown",
|
|
315
|
+
message: "Emulator operation may have been partially applied",
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
return { code: "internal", message: "Remote Runtime request failed" };
|
|
319
|
+
}
|
|
320
|
+
function idempotentCancel(watch) {
|
|
321
|
+
let pending;
|
|
322
|
+
return () => {
|
|
323
|
+
pending ??= Promise.resolve().then(() => watch.cancel());
|
|
324
|
+
return pending;
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
function projectedSessionEvents(watch, sessionId, cancel) {
|
|
328
|
+
const source = watch[Symbol.asyncIterator]();
|
|
329
|
+
return {
|
|
330
|
+
[Symbol.asyncIterator]() {
|
|
331
|
+
return {
|
|
332
|
+
async next() {
|
|
333
|
+
try {
|
|
334
|
+
const next = await source.next();
|
|
335
|
+
if (next.done) {
|
|
336
|
+
await cancel();
|
|
337
|
+
return { value: undefined, done: true };
|
|
338
|
+
}
|
|
339
|
+
return {
|
|
340
|
+
value: projectRemoteRuntimeSessionEvent(next.value, sessionId),
|
|
341
|
+
done: false,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
catch (error) {
|
|
345
|
+
await cancel().catch(() => undefined);
|
|
346
|
+
throw error;
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
async return() {
|
|
350
|
+
await cancel();
|
|
351
|
+
return { value: undefined, done: true };
|
|
352
|
+
},
|
|
353
|
+
};
|
|
354
|
+
},
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
function emulatorHostErrorCode(error) {
|
|
358
|
+
if (!error || typeof error !== "object" || !("code" in error))
|
|
359
|
+
return undefined;
|
|
360
|
+
const code = error.code;
|
|
361
|
+
return typeof code === "string" && code.startsWith("emulator_") ? code : undefined;
|
|
362
|
+
}
|
|
363
|
+
function sentEmulatorFrames(error) {
|
|
364
|
+
if (!error || typeof error !== "object" || !("sentFrames" in error))
|
|
365
|
+
return undefined;
|
|
366
|
+
const sentFrames = error.sentFrames;
|
|
367
|
+
return typeof sentFrames === "number" && Number.isSafeInteger(sentFrames) && sentFrames >= 0
|
|
368
|
+
? sentFrames
|
|
369
|
+
: undefined;
|
|
370
|
+
}
|
|
371
|
+
function rpcResult(request, result) {
|
|
372
|
+
const response = parseRemoteRuntimeRpcResponseForMethod({ type: "rpc.result", id: request.id, result }, request.method);
|
|
373
|
+
// Enforce the Direct binding's aggregate frame budget even for the Local
|
|
374
|
+
// binding, so transport selection cannot change accepted result semantics.
|
|
375
|
+
encodeRemoteRuntimeRpcResponse(response, request.method);
|
|
376
|
+
return response;
|
|
377
|
+
}
|
|
378
|
+
function rpcError(id, code, message) {
|
|
379
|
+
return { type: "rpc.error", id, error: { code, message } };
|
|
380
|
+
}
|
|
@@ -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;
|