@rynx-ai/remote-runtime-client 0.1.11-beta.33 → 0.1.11-beta.35
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/client.d.ts +26 -3
- package/dist/client.js +227 -109
- package/package.json +3 -3
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type RemoteRuntimeRpcMethod, type RemoteRuntimeRpcParams, type RemoteRuntimeRpcResultFor, type RemoteRuntimeSequencedSessionEvent, type
|
|
1
|
+
import { type RemoteRuntimeRpcMethod, type RemoteRuntimeRpcParams, type RemoteRuntimeRpcResultFor, type RemoteRuntimeSequencedSessionEvent, type RemoteRuntimeSessionTerminalRole } from "@rynx-ai/protocol/remote-runtime-rpc";
|
|
2
2
|
import { type DaemonStatus } from "@rynx-ai/protocol/remote-runtime";
|
|
3
3
|
import { type RuntimeEmulatorSurfaceClosedFrame, type RuntimeEmulatorSurfaceImageFrame, type RuntimeEmulatorSurfaceReadyFrame } from "@rynx-ai/protocol/runtime-emulator-surface";
|
|
4
4
|
import { type DirectRuntimeClientIdentity, type DirectRuntimeCredential } from "./credential.js";
|
|
@@ -34,11 +34,34 @@ export interface DirectRuntimeSessionTerminalOptions {
|
|
|
34
34
|
/** Timeout for the opened barrier and close acknowledgement. */
|
|
35
35
|
timeoutMs?: number;
|
|
36
36
|
}
|
|
37
|
-
export interface
|
|
37
|
+
export interface DirectRuntimeSessionTerminalRead {
|
|
38
|
+
data: Uint8Array;
|
|
39
|
+
nextOffset: number;
|
|
40
|
+
done: boolean;
|
|
41
|
+
finalOffset?: number;
|
|
42
|
+
}
|
|
43
|
+
export interface DirectRuntimeSessionTerminalCloseInfo {
|
|
44
|
+
reason: "client_closed" | "terminal_exited" | "not_live" | "unauthorized" | "capacity" | "backpressure" | "internal";
|
|
45
|
+
exitCode?: number;
|
|
46
|
+
finalOffset?: number;
|
|
47
|
+
}
|
|
48
|
+
export interface DirectRuntimeSessionTerminal {
|
|
38
49
|
readonly attachmentId: string;
|
|
39
50
|
readonly sessionId: string;
|
|
40
51
|
readonly role: RemoteRuntimeSessionTerminalRole;
|
|
41
|
-
readonly
|
|
52
|
+
readonly seedBytes: number;
|
|
53
|
+
readonly dimensions?: {
|
|
54
|
+
cols: number;
|
|
55
|
+
rows: number;
|
|
56
|
+
};
|
|
57
|
+
readonly closed: Promise<DirectRuntimeSessionTerminalCloseInfo>;
|
|
58
|
+
readSeed(offset: number, maxBytes: number): Promise<DirectRuntimeSessionTerminalRead>;
|
|
59
|
+
start(): Promise<void>;
|
|
60
|
+
read(offset: number, maxBytes: number): Promise<DirectRuntimeSessionTerminalRead>;
|
|
61
|
+
onResize(listener: (dimensions: {
|
|
62
|
+
cols: number;
|
|
63
|
+
rows: number;
|
|
64
|
+
}) => void): void;
|
|
42
65
|
write(data: Uint8Array): Promise<void>;
|
|
43
66
|
resize(cols: number, rows: number): Promise<void>;
|
|
44
67
|
/** Idempotently detach and wait for the remote close acknowledgement. */
|
package/dist/client.js
CHANGED
|
@@ -25,8 +25,6 @@ const MAX_PENDING_OUTBOUND_BYTES = 1024 * 1024;
|
|
|
25
25
|
const MAX_E2EE_ENVELOPE_OVERHEAD_BYTES = 256;
|
|
26
26
|
const MAX_SUBSCRIPTION_QUEUED_EVENTS = 128;
|
|
27
27
|
const MAX_SUBSCRIPTION_QUEUED_BYTES = 256 * 1024;
|
|
28
|
-
const MAX_TERMINAL_QUEUED_OUTPUT_FRAMES = 128;
|
|
29
|
-
const MAX_TERMINAL_QUEUED_OUTPUT_BYTES = 512 * 1024;
|
|
30
28
|
const establishedTextDecoder = new TextDecoder("utf-8", { fatal: true });
|
|
31
29
|
/** Enroll a client identity, authenticate the issued grant, and probe status. */
|
|
32
30
|
export async function pairDirectRuntime(untrustedOffer, options = {}) {
|
|
@@ -478,8 +476,14 @@ class DirectRuntimeConnectionImpl {
|
|
|
478
476
|
case "session.events.closed":
|
|
479
477
|
this.receiveSessionFrame(value, frame.byteLength);
|
|
480
478
|
return;
|
|
481
|
-
case "session.terminal.
|
|
482
|
-
case "session.terminal.
|
|
479
|
+
case "session.terminal.prepared":
|
|
480
|
+
case "session.terminal.seed.chunk":
|
|
481
|
+
case "session.terminal.started":
|
|
482
|
+
case "session.terminal.chunk":
|
|
483
|
+
case "session.terminal.input.ack":
|
|
484
|
+
case "session.terminal.resize.ack":
|
|
485
|
+
case "session.terminal.reader.done":
|
|
486
|
+
case "session.terminal.dimensions":
|
|
483
487
|
case "session.terminal.closed":
|
|
484
488
|
this.receiveTerminalFrame(value);
|
|
485
489
|
return;
|
|
@@ -625,16 +629,30 @@ class DirectRuntimeConnectionImpl {
|
|
|
625
629
|
return;
|
|
626
630
|
}
|
|
627
631
|
switch (frame.type) {
|
|
628
|
-
case "session.terminal.
|
|
632
|
+
case "session.terminal.prepared":
|
|
629
633
|
this.acceptTerminal(frame);
|
|
630
634
|
return;
|
|
631
|
-
case "session.terminal.
|
|
635
|
+
case "session.terminal.seed.chunk":
|
|
636
|
+
case "session.terminal.chunk":
|
|
637
|
+
case "session.terminal.started":
|
|
638
|
+
case "session.terminal.input.ack":
|
|
639
|
+
case "session.terminal.resize.ack":
|
|
640
|
+
case "session.terminal.reader.done": {
|
|
632
641
|
const terminal = this.terminal;
|
|
633
642
|
if (!terminal || terminal.attachmentId !== frame.attachmentId) {
|
|
634
|
-
this.fail(clientError("protocol_error", "Terminal
|
|
643
|
+
this.fail(clientError("protocol_error", "Terminal response has no active attachment"));
|
|
635
644
|
return;
|
|
636
645
|
}
|
|
637
|
-
terminal.
|
|
646
|
+
terminal.receive(frame);
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
case "session.terminal.dimensions": {
|
|
650
|
+
const terminal = this.terminal;
|
|
651
|
+
if (!terminal || terminal.attachmentId !== frame.attachmentId) {
|
|
652
|
+
this.fail(clientError("protocol_error", "Terminal dimensions have no active attachment"));
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
terminal.updateDimensions(frame.cols, frame.rows);
|
|
638
656
|
return;
|
|
639
657
|
}
|
|
640
658
|
case "session.terminal.closed":
|
|
@@ -653,7 +671,7 @@ class DirectRuntimeConnectionImpl {
|
|
|
653
671
|
}
|
|
654
672
|
this.pendingTerminal = undefined;
|
|
655
673
|
clearTimeout(pending.timer);
|
|
656
|
-
const terminal = new SessionTerminalImpl(frame, pending.timeoutMs, (
|
|
674
|
+
const terminal = new SessionTerminalImpl(frame, pending.timeoutMs, (value) => this.channel.socket.sendText(encodeRemoteRuntimeSessionTerminalClientFrame(value)), () => this.closeTerminal(frame.attachmentId), (error) => this.fail(error));
|
|
657
675
|
this.terminal = terminal;
|
|
658
676
|
pending.resolve(terminal);
|
|
659
677
|
}
|
|
@@ -756,28 +774,6 @@ class DirectRuntimeConnectionImpl {
|
|
|
756
774
|
...point,
|
|
757
775
|
}));
|
|
758
776
|
}
|
|
759
|
-
async writeTerminal(attachmentId, data) {
|
|
760
|
-
if (data.byteLength === 0)
|
|
761
|
-
return;
|
|
762
|
-
for (let offset = 0; offset < data.byteLength; offset += REMOTE_RUNTIME_SESSION_TERMINAL_MAX_DATA_BYTES) {
|
|
763
|
-
const chunk = data.subarray(offset, Math.min(offset + REMOTE_RUNTIME_SESSION_TERMINAL_MAX_DATA_BYTES, data.byteLength));
|
|
764
|
-
const frame = encodeRemoteRuntimeSessionTerminalClientFrame({
|
|
765
|
-
type: "session.terminal.input",
|
|
766
|
-
attachmentId,
|
|
767
|
-
data: Buffer.from(chunk).toString("base64"),
|
|
768
|
-
});
|
|
769
|
-
await this.channel.socket.sendText(frame);
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
async resizeTerminal(attachmentId, cols, rows) {
|
|
773
|
-
const frame = encodeRemoteRuntimeSessionTerminalClientFrame({
|
|
774
|
-
type: "session.terminal.resize",
|
|
775
|
-
attachmentId,
|
|
776
|
-
cols,
|
|
777
|
-
rows,
|
|
778
|
-
});
|
|
779
|
-
await this.channel.socket.sendText(frame);
|
|
780
|
-
}
|
|
781
777
|
async closeTerminal(attachmentId) {
|
|
782
778
|
const terminal = this.terminal;
|
|
783
779
|
if (!terminal || terminal.attachmentId !== attachmentId)
|
|
@@ -1187,31 +1183,39 @@ function sessionEventsError(frame, sessionId) {
|
|
|
1187
1183
|
}
|
|
1188
1184
|
class SessionTerminalImpl {
|
|
1189
1185
|
timeoutMs;
|
|
1190
|
-
|
|
1191
|
-
sendResize;
|
|
1186
|
+
sendFrame;
|
|
1192
1187
|
requestClose;
|
|
1193
1188
|
reportBackpressure;
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1189
|
+
state = "prepared";
|
|
1190
|
+
seedOffset = 0;
|
|
1191
|
+
liveOffset = 0;
|
|
1192
|
+
closedSettled = false;
|
|
1193
|
+
detachedSettled = false;
|
|
1194
|
+
pending = new Map();
|
|
1200
1195
|
resolveClosed;
|
|
1201
1196
|
rejectClosed;
|
|
1197
|
+
resolveDetached;
|
|
1198
|
+
rejectDetached;
|
|
1202
1199
|
closed;
|
|
1200
|
+
detached;
|
|
1203
1201
|
attachmentId;
|
|
1204
1202
|
sessionId;
|
|
1205
1203
|
role;
|
|
1206
|
-
|
|
1204
|
+
seedBytes;
|
|
1205
|
+
dimensions;
|
|
1206
|
+
resizeListener;
|
|
1207
|
+
constructor(opened, timeoutMs, sendFrame, requestClose, reportBackpressure) {
|
|
1207
1208
|
this.timeoutMs = timeoutMs;
|
|
1208
|
-
this.
|
|
1209
|
-
this.sendResize = sendResize;
|
|
1209
|
+
this.sendFrame = sendFrame;
|
|
1210
1210
|
this.requestClose = requestClose;
|
|
1211
1211
|
this.reportBackpressure = reportBackpressure;
|
|
1212
1212
|
this.attachmentId = opened.attachmentId;
|
|
1213
1213
|
this.sessionId = opened.sessionId;
|
|
1214
1214
|
this.role = opened.role;
|
|
1215
|
+
this.seedBytes = opened.seedBytes;
|
|
1216
|
+
this.dimensions = opened.cols === undefined || opened.rows === undefined
|
|
1217
|
+
? undefined
|
|
1218
|
+
: { cols: opened.cols, rows: opened.rows };
|
|
1215
1219
|
let resolveClosed;
|
|
1216
1220
|
let rejectClosed;
|
|
1217
1221
|
this.closed = new Promise((resolve, reject) => {
|
|
@@ -1220,111 +1224,225 @@ class SessionTerminalImpl {
|
|
|
1220
1224
|
});
|
|
1221
1225
|
this.resolveClosed = resolveClosed;
|
|
1222
1226
|
this.rejectClosed = rejectClosed;
|
|
1227
|
+
let resolveDetached;
|
|
1228
|
+
let rejectDetached;
|
|
1229
|
+
this.detached = new Promise((resolve, reject) => {
|
|
1230
|
+
resolveDetached = resolve;
|
|
1231
|
+
rejectDetached = reject;
|
|
1232
|
+
});
|
|
1233
|
+
this.resolveDetached = resolveDetached;
|
|
1234
|
+
this.rejectDetached = rejectDetached;
|
|
1223
1235
|
void this.closed.catch(() => undefined);
|
|
1236
|
+
void this.detached.catch(() => undefined);
|
|
1224
1237
|
}
|
|
1225
|
-
|
|
1226
|
-
|
|
1238
|
+
onResize(listener) {
|
|
1239
|
+
this.resizeListener = listener;
|
|
1240
|
+
if (this.dimensions)
|
|
1241
|
+
queueMicrotask(() => listener(this.dimensions));
|
|
1227
1242
|
}
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
+
updateDimensions(cols, rows) {
|
|
1244
|
+
if (this.state === "ended")
|
|
1245
|
+
return;
|
|
1246
|
+
this.dimensions = { cols, rows };
|
|
1247
|
+
this.resizeListener?.(this.dimensions);
|
|
1248
|
+
}
|
|
1249
|
+
readSeed(offset, maxBytes) {
|
|
1250
|
+
if (this.state !== "prepared" || offset !== this.seedOffset) {
|
|
1251
|
+
return Promise.reject(clientError("protocol_error", "Session terminal seed read is out of order"));
|
|
1252
|
+
}
|
|
1253
|
+
return this.requestRead("session.terminal.seed.chunk", {
|
|
1254
|
+
type: "session.terminal.seed.read",
|
|
1255
|
+
attachmentId: this.attachmentId,
|
|
1256
|
+
requestId: this.nextRequestId(),
|
|
1257
|
+
offset,
|
|
1258
|
+
maxBytes: Math.min(maxBytes, REMOTE_RUNTIME_SESSION_TERMINAL_MAX_DATA_BYTES),
|
|
1259
|
+
}).then((result) => {
|
|
1260
|
+
this.seedOffset = result.nextOffset;
|
|
1261
|
+
if (result.done && result.nextOffset !== this.seedBytes) {
|
|
1262
|
+
throw clientError("protocol_error", "Session terminal seed length changed");
|
|
1263
|
+
}
|
|
1264
|
+
return result;
|
|
1243
1265
|
});
|
|
1244
1266
|
}
|
|
1245
|
-
async
|
|
1246
|
-
|
|
1247
|
-
|
|
1267
|
+
async start() {
|
|
1268
|
+
if (this.state !== "prepared" || this.seedOffset !== this.seedBytes) {
|
|
1269
|
+
throw clientError("protocol_error", "Session terminal history must be consumed before start");
|
|
1270
|
+
}
|
|
1271
|
+
this.state = "starting";
|
|
1272
|
+
await this.requestVoid("session.terminal.started", {
|
|
1273
|
+
type: "session.terminal.start",
|
|
1274
|
+
attachmentId: this.attachmentId,
|
|
1275
|
+
requestId: this.nextRequestId(),
|
|
1276
|
+
});
|
|
1277
|
+
if (this.state === "starting")
|
|
1278
|
+
this.state = "started";
|
|
1279
|
+
}
|
|
1280
|
+
read(offset, maxBytes) {
|
|
1281
|
+
if (this.state !== "started" || offset !== this.liveOffset) {
|
|
1282
|
+
return Promise.reject(clientError("protocol_error", "Session terminal read is out of order"));
|
|
1283
|
+
}
|
|
1284
|
+
return this.requestRead("session.terminal.chunk", {
|
|
1285
|
+
type: "session.terminal.read",
|
|
1286
|
+
attachmentId: this.attachmentId,
|
|
1287
|
+
requestId: this.nextRequestId(),
|
|
1288
|
+
offset,
|
|
1289
|
+
maxBytes: Math.min(maxBytes, REMOTE_RUNTIME_SESSION_TERMINAL_MAX_DATA_BYTES),
|
|
1290
|
+
}).then((result) => {
|
|
1291
|
+
this.liveOffset = result.nextOffset;
|
|
1292
|
+
return result;
|
|
1293
|
+
});
|
|
1248
1294
|
}
|
|
1249
1295
|
async write(data) {
|
|
1250
|
-
if (this.
|
|
1296
|
+
if (this.state !== "started" || this.role !== "owner" || data.byteLength === 0)
|
|
1251
1297
|
return;
|
|
1252
|
-
|
|
1298
|
+
for (let offset = 0; offset < data.byteLength; offset += REMOTE_RUNTIME_SESSION_TERMINAL_MAX_DATA_BYTES) {
|
|
1299
|
+
const chunk = data.subarray(offset, Math.min(offset + REMOTE_RUNTIME_SESSION_TERMINAL_MAX_DATA_BYTES, data.byteLength));
|
|
1300
|
+
await this.requestVoid("session.terminal.input.ack", {
|
|
1301
|
+
type: "session.terminal.input",
|
|
1302
|
+
attachmentId: this.attachmentId,
|
|
1303
|
+
requestId: this.nextRequestId(),
|
|
1304
|
+
data: Buffer.from(chunk).toString("base64"),
|
|
1305
|
+
});
|
|
1306
|
+
}
|
|
1253
1307
|
}
|
|
1254
1308
|
async resize(cols, rows) {
|
|
1255
|
-
if (this.
|
|
1309
|
+
if (this.state !== "started")
|
|
1256
1310
|
return;
|
|
1257
|
-
await this.
|
|
1311
|
+
await this.requestVoid("session.terminal.resize.ack", {
|
|
1312
|
+
type: "session.terminal.resize",
|
|
1313
|
+
attachmentId: this.attachmentId,
|
|
1314
|
+
requestId: this.nextRequestId(),
|
|
1315
|
+
cols,
|
|
1316
|
+
rows,
|
|
1317
|
+
});
|
|
1258
1318
|
}
|
|
1259
1319
|
async close() {
|
|
1260
|
-
if (this.ended)
|
|
1320
|
+
if (this.state === "ended")
|
|
1261
1321
|
return;
|
|
1262
1322
|
await this.requestClose();
|
|
1263
1323
|
}
|
|
1264
|
-
|
|
1265
|
-
if (
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1324
|
+
receive(frame) {
|
|
1325
|
+
if (frame.type === "session.terminal.reader.done") {
|
|
1326
|
+
if (!this.closedSettled) {
|
|
1327
|
+
this.closedSettled = true;
|
|
1328
|
+
this.resolveClosed({
|
|
1329
|
+
reason: frame.reason,
|
|
1330
|
+
exitCode: frame.exitCode,
|
|
1331
|
+
finalOffset: frame.finalOffset,
|
|
1332
|
+
});
|
|
1333
|
+
}
|
|
1271
1334
|
return;
|
|
1272
1335
|
}
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
this.reportBackpressure(clientError("
|
|
1336
|
+
const pending = this.pending.get(frame.requestId);
|
|
1337
|
+
if (!pending || pending.expectedType !== frame.type) {
|
|
1338
|
+
this.reportBackpressure(clientError("protocol_error", "Remote Session terminal response is duplicate or out of order"));
|
|
1276
1339
|
return;
|
|
1277
1340
|
}
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1341
|
+
this.pending.delete(frame.requestId);
|
|
1342
|
+
clearTimeout(pending.timer);
|
|
1343
|
+
if (frame.type === "session.terminal.seed.chunk" || frame.type === "session.terminal.chunk") {
|
|
1344
|
+
pending.resolve({
|
|
1345
|
+
data: Uint8Array.from(Buffer.from(frame.data, "base64")),
|
|
1346
|
+
nextOffset: frame.nextOffset,
|
|
1347
|
+
done: frame.done,
|
|
1348
|
+
...(frame.finalOffset === undefined ? {} : { finalOffset: frame.finalOffset }),
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1351
|
+
else
|
|
1352
|
+
pending.resolve(undefined);
|
|
1281
1353
|
}
|
|
1282
1354
|
beginClose() {
|
|
1283
|
-
if (this.ended || this.closing)
|
|
1355
|
+
if (this.state === "ended" || this.state === "closing")
|
|
1284
1356
|
return false;
|
|
1285
|
-
this.
|
|
1286
|
-
this.
|
|
1357
|
+
this.state = "closing";
|
|
1358
|
+
this.rejectPending(clientError("closed", "Remote Session terminal is closing"));
|
|
1287
1359
|
return true;
|
|
1288
1360
|
}
|
|
1289
1361
|
remoteClose(frame) {
|
|
1290
|
-
if (this.ended)
|
|
1362
|
+
if (this.state === "ended")
|
|
1291
1363
|
return;
|
|
1364
|
+
this.state = "ended";
|
|
1292
1365
|
const error = frame.reason === "client_closed" || frame.reason === "terminal_exited"
|
|
1293
1366
|
? undefined
|
|
1294
1367
|
: sessionTerminalError(frame, this.sessionId);
|
|
1295
|
-
this.
|
|
1368
|
+
this.rejectPending(error ?? clientError("closed", "Remote Session terminal detached"));
|
|
1369
|
+
if (!this.closedSettled) {
|
|
1370
|
+
this.closedSettled = true;
|
|
1371
|
+
if (error)
|
|
1372
|
+
this.rejectClosed(error);
|
|
1373
|
+
else
|
|
1374
|
+
this.resolveClosed({ reason: frame.reason, ...(frame.exitCode === undefined ? {} : { exitCode: frame.exitCode }) });
|
|
1375
|
+
}
|
|
1376
|
+
if (!this.detachedSettled) {
|
|
1377
|
+
this.detachedSettled = true;
|
|
1378
|
+
if (error)
|
|
1379
|
+
this.rejectDetached(error);
|
|
1380
|
+
else
|
|
1381
|
+
this.resolveDetached();
|
|
1382
|
+
}
|
|
1296
1383
|
}
|
|
1297
1384
|
terminate(error) {
|
|
1298
|
-
if (this.ended)
|
|
1385
|
+
if (this.state === "ended")
|
|
1299
1386
|
return;
|
|
1300
|
-
this.
|
|
1387
|
+
this.state = "ended";
|
|
1388
|
+
this.rejectPending(error);
|
|
1389
|
+
if (!this.closedSettled) {
|
|
1390
|
+
this.closedSettled = true;
|
|
1391
|
+
this.rejectClosed(error);
|
|
1392
|
+
}
|
|
1393
|
+
if (!this.detachedSettled) {
|
|
1394
|
+
this.detachedSettled = true;
|
|
1395
|
+
this.rejectDetached(error);
|
|
1396
|
+
}
|
|
1301
1397
|
}
|
|
1302
1398
|
waitClosed() {
|
|
1303
|
-
return this.
|
|
1399
|
+
return this.detached;
|
|
1304
1400
|
}
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
this.
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
if (waiter) {
|
|
1315
|
-
if (error === undefined)
|
|
1316
|
-
waiter.resolve({ done: true, value: undefined });
|
|
1317
|
-
else
|
|
1318
|
-
waiter.reject(error);
|
|
1401
|
+
requestRead(expectedType, frame) {
|
|
1402
|
+
return this.request(expectedType, frame);
|
|
1403
|
+
}
|
|
1404
|
+
requestVoid(expectedType, frame) {
|
|
1405
|
+
return this.request(expectedType, frame).then(() => undefined);
|
|
1406
|
+
}
|
|
1407
|
+
request(expectedType, frame) {
|
|
1408
|
+
if (!("requestId" in frame)) {
|
|
1409
|
+
return Promise.reject(clientError("protocol_error", "terminal request id is missing"));
|
|
1319
1410
|
}
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1411
|
+
const pull = expectedType === "session.terminal.seed.chunk" || expectedType === "session.terminal.chunk";
|
|
1412
|
+
const sameClassPending = [...this.pending.values()].some((pending) => {
|
|
1413
|
+
const pendingPull = pending.expectedType === "session.terminal.seed.chunk" ||
|
|
1414
|
+
pending.expectedType === "session.terminal.chunk";
|
|
1415
|
+
return pendingPull === pull;
|
|
1416
|
+
});
|
|
1417
|
+
if (sameClassPending) {
|
|
1418
|
+
return Promise.reject(clientError("protocol_error", pull ? "concurrent terminal pulls are not allowed" : "concurrent terminal controls are not allowed"));
|
|
1419
|
+
}
|
|
1420
|
+
return new Promise((resolve, reject) => {
|
|
1421
|
+
const timer = setTimeout(() => {
|
|
1422
|
+
this.pending.delete(frame.requestId);
|
|
1423
|
+
reject(clientError("deadline_exceeded", "Remote Session terminal operation timed out"));
|
|
1424
|
+
}, this.timeoutMs);
|
|
1425
|
+
timer.unref?.();
|
|
1426
|
+
this.pending.set(frame.requestId, { expectedType, resolve, reject, timer });
|
|
1427
|
+
void this.sendFrame(frame).catch((error) => {
|
|
1428
|
+
const pending = this.pending.get(frame.requestId);
|
|
1429
|
+
if (!pending)
|
|
1430
|
+
return;
|
|
1431
|
+
this.pending.delete(frame.requestId);
|
|
1432
|
+
clearTimeout(pending.timer);
|
|
1433
|
+
pending.reject(error);
|
|
1434
|
+
});
|
|
1435
|
+
});
|
|
1324
1436
|
}
|
|
1325
|
-
|
|
1326
|
-
this.
|
|
1327
|
-
|
|
1437
|
+
rejectPending(error) {
|
|
1438
|
+
for (const pending of this.pending.values()) {
|
|
1439
|
+
clearTimeout(pending.timer);
|
|
1440
|
+
pending.reject(error);
|
|
1441
|
+
}
|
|
1442
|
+
this.pending.clear();
|
|
1443
|
+
}
|
|
1444
|
+
nextRequestId() {
|
|
1445
|
+
return `treq_${randomBytes(12).toString("base64url")}`;
|
|
1328
1446
|
}
|
|
1329
1447
|
}
|
|
1330
1448
|
function sessionTerminalError(frame, sessionId) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rynx-ai/remote-runtime-client",
|
|
3
|
-
"version": "0.1.11-beta.
|
|
3
|
+
"version": "0.1.11-beta.35",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/rynx-ai/rynx.git",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"ws": "^8.21.0",
|
|
28
|
-
"@rynx-ai/
|
|
29
|
-
"@rynx-ai/
|
|
28
|
+
"@rynx-ai/protocol": "0.1.11-beta.35",
|
|
29
|
+
"@rynx-ai/remote-runtime-e2ee": "0.1.11-beta.35"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/ws": "^8.18.1"
|