@promigence/sdk 0.1.0-beta.12

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 (104) hide show
  1. package/NOTICE +14 -0
  2. package/README.md +54 -0
  3. package/dist/blake3.d.ts +10 -0
  4. package/dist/blake3.js +111 -0
  5. package/dist/bundle.d.ts +96 -0
  6. package/dist/bundle.js +227 -0
  7. package/dist/ca-guard.d.ts +1 -0
  8. package/dist/ca-guard.js +166 -0
  9. package/dist/caller.d.ts +73 -0
  10. package/dist/caller.js +47 -0
  11. package/dist/cjs/blake3.d.ts +10 -0
  12. package/dist/cjs/blake3.js +116 -0
  13. package/dist/cjs/bundle.d.ts +96 -0
  14. package/dist/cjs/bundle.js +242 -0
  15. package/dist/cjs/ca-guard.d.ts +1 -0
  16. package/dist/cjs/ca-guard.js +174 -0
  17. package/dist/cjs/caller.d.ts +73 -0
  18. package/dist/cjs/caller.js +57 -0
  19. package/dist/cjs/client.d.ts +3015 -0
  20. package/dist/cjs/client.js +2617 -0
  21. package/dist/cjs/command-errors.d.ts +41 -0
  22. package/dist/cjs/command-errors.js +43 -0
  23. package/dist/cjs/context.d.ts +119 -0
  24. package/dist/cjs/context.js +598 -0
  25. package/dist/cjs/core.d.ts +285 -0
  26. package/dist/cjs/core.js +290 -0
  27. package/dist/cjs/credstore.d.ts +90 -0
  28. package/dist/cjs/credstore.js +219 -0
  29. package/dist/cjs/dispatcher.d.ts +58 -0
  30. package/dist/cjs/dispatcher.js +91 -0
  31. package/dist/cjs/github.d.ts +158 -0
  32. package/dist/cjs/github.js +84 -0
  33. package/dist/cjs/gitlab.d.ts +104 -0
  34. package/dist/cjs/gitlab.js +24 -0
  35. package/dist/cjs/handles.d.ts +82 -0
  36. package/dist/cjs/handles.js +139 -0
  37. package/dist/cjs/hint.d.ts +1 -0
  38. package/dist/cjs/hint.js +10 -0
  39. package/dist/cjs/http2.d.ts +206 -0
  40. package/dist/cjs/http2.js +1932 -0
  41. package/dist/cjs/index.d.ts +34 -0
  42. package/dist/cjs/index.js +31 -0
  43. package/dist/cjs/keyed.gen.d.ts +1 -0
  44. package/dist/cjs/keyed.gen.js +10 -0
  45. package/dist/cjs/models.gen.d.ts +6162 -0
  46. package/dist/cjs/models.gen.js +2 -0
  47. package/dist/cjs/package.json +1 -0
  48. package/dist/cjs/proxy.d.ts +40 -0
  49. package/dist/cjs/proxy.js +257 -0
  50. package/dist/cjs/pty.d.ts +171 -0
  51. package/dist/cjs/pty.js +504 -0
  52. package/dist/cjs/sandbox.d.ts +537 -0
  53. package/dist/cjs/sandbox.js +736 -0
  54. package/dist/cjs/ssh.d.ts +25 -0
  55. package/dist/cjs/ssh.js +117 -0
  56. package/dist/cjs/trace.d.ts +33 -0
  57. package/dist/cjs/trace.js +88 -0
  58. package/dist/cjs/transport-worker.d.ts +1 -0
  59. package/dist/cjs/transport-worker.js +468 -0
  60. package/dist/cjs/warm.d.ts +1 -0
  61. package/dist/cjs/warm.js +43 -0
  62. package/dist/client.d.ts +3015 -0
  63. package/dist/client.js +2583 -0
  64. package/dist/command-errors.d.ts +41 -0
  65. package/dist/command-errors.js +36 -0
  66. package/dist/context.d.ts +119 -0
  67. package/dist/context.js +588 -0
  68. package/dist/core.d.ts +285 -0
  69. package/dist/core.js +266 -0
  70. package/dist/credstore.d.ts +90 -0
  71. package/dist/credstore.js +206 -0
  72. package/dist/dispatcher.d.ts +58 -0
  73. package/dist/dispatcher.js +86 -0
  74. package/dist/github.d.ts +158 -0
  75. package/dist/github.js +78 -0
  76. package/dist/gitlab.d.ts +104 -0
  77. package/dist/gitlab.js +20 -0
  78. package/dist/handles.d.ts +82 -0
  79. package/dist/handles.js +135 -0
  80. package/dist/hint.d.ts +1 -0
  81. package/dist/hint.js +7 -0
  82. package/dist/http2.d.ts +206 -0
  83. package/dist/http2.js +1921 -0
  84. package/dist/index.d.ts +34 -0
  85. package/dist/index.js +15 -0
  86. package/dist/keyed.gen.d.ts +1 -0
  87. package/dist/keyed.gen.js +7 -0
  88. package/dist/models.gen.d.ts +6162 -0
  89. package/dist/models.gen.js +1 -0
  90. package/dist/proxy.d.ts +40 -0
  91. package/dist/proxy.js +249 -0
  92. package/dist/pty.d.ts +171 -0
  93. package/dist/pty.js +496 -0
  94. package/dist/sandbox.d.ts +537 -0
  95. package/dist/sandbox.js +725 -0
  96. package/dist/ssh.d.ts +25 -0
  97. package/dist/ssh.js +114 -0
  98. package/dist/trace.d.ts +33 -0
  99. package/dist/trace.js +80 -0
  100. package/dist/transport-worker.d.ts +1 -0
  101. package/dist/transport-worker.js +466 -0
  102. package/dist/warm.d.ts +1 -0
  103. package/dist/warm.js +40 -0
  104. package/package.json +54 -0
@@ -0,0 +1,504 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PTY_RESTART_CLOSED_EXIT = exports.PTY_RESTART_EXIT = exports.PTY_RECONNECT_MAX_WAIT_MS = exports.PTY_RECONNECT_FIRST_WAIT_MS = exports.PTY_RECONNECT_FOR_MS = exports.PTY_RECONNECT_CODE = exports.PtySession = exports.FrameReader = exports.PTY_SECRETS_HEADER = exports.PTY_COMMAND_HEADER = exports.PTY_RESUME_HEADER = exports.PTY_INPUT_KEEP = exports.PTY_INPUT_CHUNK = void 0;
4
+ exports.wsAccept = wsAccept;
5
+ exports.encodeFrame = encodeFrame;
6
+ exports.connectPty = connectPty;
7
+ const node_crypto_1 = require("node:crypto");
8
+ const caller_js_1 = require("./caller.js");
9
+ const proxy_js_1 = require("./proxy.js");
10
+ exports.PTY_INPUT_CHUNK = 64 * 1024;
11
+ exports.PTY_INPUT_KEEP = 256 * 1024;
12
+ exports.PTY_RESUME_HEADER = "promigence-resume";
13
+ const WS_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
14
+ exports.PTY_COMMAND_HEADER = "promigence-pty-command";
15
+ exports.PTY_SECRETS_HEADER = "promigence-pty-secrets";
16
+ function wsAccept(key) {
17
+ return (0, node_crypto_1.createHash)("sha1").update(key + WS_GUID).digest("base64");
18
+ }
19
+ function encodeFrame(opcode, payload) {
20
+ const len = payload.length;
21
+ const head = [0x80 | opcode];
22
+ if (len < 126)
23
+ head.push(0x80 | len);
24
+ else if (len < 65536)
25
+ head.push(0x80 | 126, len >> 8, len & 0xff);
26
+ else {
27
+ head.push(0x80 | 127);
28
+ const big = Buffer.alloc(8);
29
+ big.writeBigUInt64BE(BigInt(len));
30
+ head.push(...big);
31
+ }
32
+ const mask = (0, node_crypto_1.randomBytes)(4);
33
+ const out = Buffer.alloc(head.length + 4 + len);
34
+ Buffer.from(head).copy(out, 0);
35
+ mask.copy(out, head.length);
36
+ const at = head.length + 4;
37
+ for (let i = 0; i < len; i++)
38
+ out[at + i] = payload[i] ^ mask[i % 4];
39
+ return out;
40
+ }
41
+ class FrameReader {
42
+ buf = Buffer.alloc(0);
43
+ partial;
44
+ push(chunk) {
45
+ this.buf = this.buf.length ? Buffer.concat([this.buf, chunk]) : Buffer.from(chunk);
46
+ const out = [];
47
+ for (;;) {
48
+ if (this.buf.length < 2)
49
+ break;
50
+ const b0 = this.buf[0];
51
+ const b1 = this.buf[1];
52
+ let len = b1 & 0x7f;
53
+ let at = 2;
54
+ if (len === 126) {
55
+ if (this.buf.length < 4)
56
+ break;
57
+ len = this.buf.readUInt16BE(2);
58
+ at = 4;
59
+ }
60
+ else if (len === 127) {
61
+ if (this.buf.length < 10)
62
+ break;
63
+ len = Number(this.buf.readBigUInt64BE(2));
64
+ at = 10;
65
+ }
66
+ const masked = (b1 & 0x80) !== 0;
67
+ if (this.buf.length < at + (masked ? 4 : 0) + len)
68
+ break;
69
+ let payload = this.buf.subarray(at + (masked ? 4 : 0), at + (masked ? 4 : 0) + len);
70
+ if (masked) {
71
+ const key = this.buf.subarray(at, at + 4);
72
+ payload = Buffer.from(payload.map((b, i) => b ^ key[i % 4]));
73
+ }
74
+ else {
75
+ payload = Buffer.from(payload);
76
+ }
77
+ this.buf = this.buf.subarray(at + (masked ? 4 : 0) + len);
78
+ const fin = (b0 & 0x80) !== 0;
79
+ const opcode = b0 & 0x0f;
80
+ if (opcode >= 0x8) {
81
+ out.push({ opcode, payload });
82
+ continue;
83
+ }
84
+ if (opcode !== 0)
85
+ this.partial = { opcode, parts: [payload] };
86
+ else
87
+ this.partial?.parts.push(payload);
88
+ if (fin && this.partial) {
89
+ out.push({ opcode: this.partial.opcode, payload: Buffer.concat(this.partial.parts) });
90
+ this.partial = undefined;
91
+ }
92
+ }
93
+ return out;
94
+ }
95
+ }
96
+ exports.FrameReader = FrameReader;
97
+ class PtySession {
98
+ socket;
99
+ pid;
100
+ cols;
101
+ rows;
102
+ closed = false;
103
+ done;
104
+ pending;
105
+ resumeToken;
106
+ inputSent = 0;
107
+ inputBase = 0;
108
+ inputLog = Buffer.alloc(0);
109
+ constructor(socket, open, done) {
110
+ this.socket = socket;
111
+ this.pid = open.pid;
112
+ this.cols = open.cols;
113
+ this.rows = open.rows;
114
+ this.resumeToken = open.resume;
115
+ this.done = done;
116
+ void done.then(() => { this.#ended = true; });
117
+ }
118
+ #ended = false;
119
+ get ended() {
120
+ return this.#ended;
121
+ }
122
+ holdInput() {
123
+ this.pending ??= [];
124
+ }
125
+ rebind(socket, open) {
126
+ this.socket = socket;
127
+ this.resumeToken = open.resume;
128
+ this.inputSent = 0;
129
+ this.inputBase = 0;
130
+ this.inputLog = Buffer.alloc(0);
131
+ this.flush(open);
132
+ }
133
+ resumed(socket, open) {
134
+ this.socket = socket;
135
+ this.resumeToken = open.resume ?? this.resumeToken;
136
+ const took = open.input ?? this.inputSent;
137
+ if (took < this.inputSent) {
138
+ const from = Math.max(took, this.inputBase) - this.inputBase;
139
+ const again = this.inputLog.subarray(from);
140
+ this.inputSent = Math.max(took, this.inputBase);
141
+ for (let at = 0; at < again.length; at += exports.PTY_INPUT_CHUNK) {
142
+ this.writeNow(0x2, again.subarray(at, at + exports.PTY_INPUT_CHUNK));
143
+ }
144
+ }
145
+ this.flush(open);
146
+ }
147
+ flush(open) {
148
+ const queued = this.pending ?? [];
149
+ this.pending = undefined;
150
+ if (open.cols !== this.cols || open.rows !== this.rows) {
151
+ this.resize({ cols: this.cols, rows: this.rows });
152
+ }
153
+ for (const f of queued)
154
+ this.writeNow(f.opcode, f.payload);
155
+ }
156
+ writeNow(opcode, payload) {
157
+ if (this.closed || this.socket.destroyed)
158
+ return;
159
+ this.socket.write(encodeFrame(opcode, payload));
160
+ if (opcode === 0x2) {
161
+ this.inputSent += payload.length;
162
+ this.inputLog = Buffer.concat([this.inputLog, payload]);
163
+ if (this.inputLog.length > exports.PTY_INPUT_KEEP) {
164
+ const drop = this.inputLog.length - exports.PTY_INPUT_KEEP;
165
+ this.inputLog = this.inputLog.subarray(drop);
166
+ this.inputBase += drop;
167
+ }
168
+ }
169
+ }
170
+ get hungUp() {
171
+ return this.closed;
172
+ }
173
+ send(opcode, payload) {
174
+ if (this.closed)
175
+ return;
176
+ if (this.pending) {
177
+ if (this.pending.reduce((n, f) => n + f.payload.length, 0) + payload.length <= 1 << 20) {
178
+ this.pending.push({ opcode, payload: Buffer.from(payload) });
179
+ }
180
+ return;
181
+ }
182
+ this.writeNow(opcode, payload);
183
+ }
184
+ write(data) {
185
+ const bytes = typeof data === "string" ? Buffer.from(data, "utf8") : data;
186
+ for (let at = 0; at < bytes.length || at === 0; at += exports.PTY_INPUT_CHUNK) {
187
+ this.send(0x2, bytes.subarray(at, at + exports.PTY_INPUT_CHUNK));
188
+ if (bytes.length === 0)
189
+ break;
190
+ }
191
+ }
192
+ resize(size) {
193
+ this.cols = size.cols;
194
+ this.rows = size.rows;
195
+ this.send(0x1, Buffer.from(JSON.stringify({ resize: { cols: size.cols, rows: size.rows } })));
196
+ }
197
+ signal(sig) {
198
+ this.send(0x1, Buffer.from(JSON.stringify({ signal: sig })));
199
+ }
200
+ eof() {
201
+ this.send(0x1, Buffer.from(JSON.stringify({ eof: true })));
202
+ }
203
+ wait() {
204
+ return this.done;
205
+ }
206
+ disconnect() {
207
+ if (this.closed)
208
+ return;
209
+ this.pending = undefined;
210
+ this.send(0x8, Buffer.from([0x03, 0xe8]));
211
+ this.closed = true;
212
+ this.socket.end();
213
+ }
214
+ }
215
+ exports.PtySession = PtySession;
216
+ exports.PTY_RECONNECT_CODE = 1012;
217
+ exports.PTY_RECONNECT_FOR_MS = 30_000;
218
+ exports.PTY_RECONNECT_FIRST_WAIT_MS = 250;
219
+ exports.PTY_RECONNECT_MAX_WAIT_MS = 4_000;
220
+ exports.PTY_RESTART_EXIT = {
221
+ exitCode: null,
222
+ signal: null,
223
+ error: {
224
+ code: "service_restart",
225
+ message: "the service restarted and the terminal could not reconnect within 30 s; open it again",
226
+ },
227
+ };
228
+ exports.PTY_RESTART_CLOSED_EXIT = {
229
+ exitCode: null,
230
+ signal: null,
231
+ error: { code: "service_restart", message: "the service restarted and closed the terminal; open it again" },
232
+ };
233
+ const CLOSED_EXIT = {
234
+ exitCode: null,
235
+ signal: null,
236
+ error: { code: "closed", message: "the terminal connection closed without an exit record" },
237
+ };
238
+ class LegRefused {
239
+ status;
240
+ error;
241
+ restart;
242
+ constructor(status, error, restart = false) {
243
+ this.status = status;
244
+ this.error = error;
245
+ this.restart = restart;
246
+ }
247
+ }
248
+ function openLeg(opts, url, resume) {
249
+ const key = (0, node_crypto_1.randomBytes)(16).toString("base64");
250
+ const req = (0, proxy_js_1.requestVia)(url, {
251
+ method: "GET",
252
+ agent: false,
253
+ headers: {
254
+ connection: "Upgrade",
255
+ upgrade: "websocket",
256
+ "sec-websocket-version": "13",
257
+ "sec-websocket-key": key,
258
+ authorization: `Bearer ${opts.apiKey}`,
259
+ "user-agent": opts.userAgent,
260
+ ...(opts.caller ? { [caller_js_1.CALLER_HEADER]: opts.caller } : {}),
261
+ ...(resume ? { [exports.PTY_RESUME_HEADER]: resume } : {}),
262
+ ...(opts.command && !resume
263
+ ? { [exports.PTY_COMMAND_HEADER]: Buffer.from(opts.command, "utf8").toString("base64") }
264
+ : {}),
265
+ ...(opts.secrets?.length && !resume
266
+ ? {
267
+ [exports.PTY_SECRETS_HEADER]: Buffer.from(JSON.stringify(opts.secrets), "utf8").toString("base64"),
268
+ }
269
+ : {}),
270
+ },
271
+ });
272
+ let resolveEnded;
273
+ const ended = new Promise((r) => { resolveEnded = r; });
274
+ const opened = new Promise((resolve, reject) => {
275
+ let settled = false;
276
+ const fail = (status, body, retryAfter, restart = false) => {
277
+ if (settled)
278
+ return;
279
+ settled = true;
280
+ reject(new LegRefused(status, opts.refuse(status, body, retryAfter), restart));
281
+ };
282
+ const timer = setTimeout(() => {
283
+ req.destroy();
284
+ fail(504, JSON.stringify({ error: { code: "timeout", message: `terminal handshake to ${url.host} took over ${opts.timeoutMs ?? 30_000} ms`, fix: "Retry; if it repeats, check the sandbox is ready (`promigence run report <run_id>`)." } }));
285
+ }, opts.timeoutMs ?? 30_000);
286
+ req.on("error", (e) => { clearTimeout(timer); fail(0, JSON.stringify({ error: { code: "unreachable", message: `cannot reach ${url.host}: ${e.code ?? e.message}` } })); });
287
+ req.on("response", (res) => {
288
+ clearTimeout(timer);
289
+ const parts = [];
290
+ res.on("data", (d) => parts.push(d));
291
+ const retryAfter = res.headers["retry-after"];
292
+ res.on("end", () => fail(res.statusCode ?? 0, Buffer.concat(parts).toString("utf8"), typeof retryAfter === "string" ? retryAfter : undefined));
293
+ });
294
+ req.on("upgrade", (res, socket, head) => {
295
+ clearTimeout(timer);
296
+ if (res.headers["sec-websocket-accept"] !== wsAccept(key)) {
297
+ socket.destroy();
298
+ fail(502, JSON.stringify({ error: { code: "internal", message: "the terminal server answered the WebSocket handshake with the wrong Sec-WebSocket-Accept" } }));
299
+ return;
300
+ }
301
+ socket.setNoDelay(true);
302
+ const reader = new FrameReader();
303
+ let exit;
304
+ let closeCode;
305
+ let wasOpen = false;
306
+ const onFrames = (chunk) => {
307
+ for (const f of reader.push(chunk)) {
308
+ if (f.opcode === 0x2) {
309
+ opts.onData?.(new Uint8Array(f.payload));
310
+ }
311
+ else if (f.opcode === 0x1) {
312
+ let v = {};
313
+ try {
314
+ v = JSON.parse(f.payload.toString("utf8"));
315
+ }
316
+ catch {
317
+ continue;
318
+ }
319
+ if (v["event"] === "open" && !wasOpen) {
320
+ wasOpen = true;
321
+ settled = true;
322
+ resolve({
323
+ socket,
324
+ pid: typeof v["req_id"] === "number" ? v["req_id"] : 0,
325
+ cols: Number(v["cols"] ?? 80),
326
+ rows: Number(v["rows"] ?? 24),
327
+ ...(typeof v["resume"] === "string" ? { resume: v["resume"] } : {}),
328
+ ...(v["reattached"] === true ? { reattached: true } : {}),
329
+ ...(typeof v["input"] === "number" ? { input: v["input"] } : {}),
330
+ });
331
+ }
332
+ else if (v["event"] === "exit") {
333
+ exit = {
334
+ exitCode: typeof v["exit_code"] === "number" ? v["exit_code"] : null,
335
+ signal: typeof v["signal"] === "number" ? v["signal"] : null,
336
+ ...(typeof v["wall_ms"] === "number" ? { wallMs: v["wall_ms"] } : {}),
337
+ };
338
+ }
339
+ else if (v["event"] === "error" || v["event"] === "closed") {
340
+ exit = {
341
+ exitCode: null, signal: null,
342
+ error: { code: String(v["code"] ?? v["event"]), message: String(v["msg"] ?? v["message"] ?? "the terminal ended") },
343
+ };
344
+ }
345
+ }
346
+ else if (f.opcode === 0x9) {
347
+ socket.write(encodeFrame(0xa, f.payload));
348
+ }
349
+ else if (f.opcode === 0x8) {
350
+ if (closeCode === undefined)
351
+ closeCode = f.payload.length >= 2 ? f.payload.readUInt16BE(0) : 1005;
352
+ if (!socket.destroyed)
353
+ socket.end(encodeFrame(0x8, f.payload.subarray(0, 2)));
354
+ }
355
+ }
356
+ };
357
+ socket.on("data", onFrames);
358
+ socket.on("close", () => {
359
+ if (!wasOpen) {
360
+ const restart = closeCode === exports.PTY_RECONNECT_CODE && exit === undefined;
361
+ const e = restart ? exports.PTY_RESTART_CLOSED_EXIT : (exit ?? CLOSED_EXIT);
362
+ fail(restart ? 503 : 502, JSON.stringify({ error: { code: e.error?.code ?? "internal", message: e.error?.message ?? "the terminal ended before it opened" } }), undefined, restart);
363
+ return;
364
+ }
365
+ resolveEnded({ code: closeCode, exit });
366
+ });
367
+ socket.on("error", () => { });
368
+ if (head.length)
369
+ onFrames(head);
370
+ });
371
+ req.end();
372
+ });
373
+ return { opened, ended };
374
+ }
375
+ function sizedUrl(url, size) {
376
+ const u = new URL(url);
377
+ u.searchParams.set("cols", String(Math.floor(size.cols)));
378
+ u.searchParams.set("rows", String(Math.floor(size.rows)));
379
+ return u;
380
+ }
381
+ function resumeUrl(url, size, received) {
382
+ const u = sizedUrl(url, size);
383
+ u.searchParams.set("received", String(received));
384
+ return u;
385
+ }
386
+ function worthRetrying(status) {
387
+ return status === 0 || status === 429 || status >= 500;
388
+ }
389
+ function connectPty(opts) {
390
+ const reconnect = opts.reconnect ?? opts.command === undefined;
391
+ const resumeOn = opts.resume ?? true;
392
+ let received = 0;
393
+ const legOpts = {
394
+ ...opts,
395
+ onData: (d) => {
396
+ received += d.length;
397
+ opts.onData?.(d);
398
+ },
399
+ };
400
+ let resolveDone;
401
+ const done = new Promise((r) => { resolveDone = r; });
402
+ let finished = false;
403
+ const finish = (e) => {
404
+ if (finished)
405
+ return;
406
+ finished = true;
407
+ opts.onExit?.(e);
408
+ resolveDone(e);
409
+ };
410
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
411
+ const follow = (session, leg) => {
412
+ void leg.ended.then(async ({ code, exit }) => {
413
+ if (exit !== undefined || session.hungUp) {
414
+ finish(exit ?? CLOSED_EXIT);
415
+ return;
416
+ }
417
+ const restart = code === exports.PTY_RECONNECT_CODE;
418
+ const dropped = code === undefined || code === 1006;
419
+ let resumable = resumeOn && session.resumeToken !== undefined && (restart || dropped);
420
+ if (!restart && !resumable) {
421
+ finish(CLOSED_EXIT);
422
+ return;
423
+ }
424
+ if (!resumable && !reconnect) {
425
+ finish(exports.PTY_RESTART_CLOSED_EXIT);
426
+ return;
427
+ }
428
+ session.holdInput();
429
+ const t0 = Date.now();
430
+ for (let attempt = 0;; attempt++) {
431
+ const wait = Math.min(exports.PTY_RECONNECT_FIRST_WAIT_MS * 2 ** attempt, exports.PTY_RECONNECT_MAX_WAIT_MS) * (0.5 + Math.random() / 2);
432
+ if (Date.now() - t0 + wait > (opts.reconnectForMs ?? exports.PTY_RECONNECT_FOR_MS))
433
+ break;
434
+ await sleep(wait);
435
+ if (session.hungUp) {
436
+ finish(CLOSED_EXIT);
437
+ return;
438
+ }
439
+ const next = resumable
440
+ ? openLeg(legOpts, resumeUrl(opts.url, session, received), session.resumeToken)
441
+ : openLeg(legOpts, sizedUrl(opts.url, session));
442
+ try {
443
+ const o = await next.opened;
444
+ if (session.hungUp) {
445
+ o.socket.destroy();
446
+ finish(CLOSED_EXIT);
447
+ return;
448
+ }
449
+ if (resumable) {
450
+ session.resumed(o.socket, o);
451
+ }
452
+ else {
453
+ received = 0;
454
+ session.rebind(o.socket, o);
455
+ }
456
+ follow(session, next);
457
+ opts.onReconnected?.({ resumed: resumable });
458
+ return;
459
+ }
460
+ catch (e) {
461
+ const refused = e instanceof LegRefused ? e : undefined;
462
+ if (resumable && refused?.status === 404) {
463
+ resumable = false;
464
+ if (!restart) {
465
+ finish(CLOSED_EXIT);
466
+ return;
467
+ }
468
+ if (!reconnect) {
469
+ finish(exports.PTY_RESTART_CLOSED_EXIT);
470
+ return;
471
+ }
472
+ continue;
473
+ }
474
+ if (refused && !worthRetrying(refused.status)) {
475
+ const err = refused.error;
476
+ finish({ exitCode: null, signal: null, error: { code: typeof err.code === "string" ? err.code : "internal", message: err.message } });
477
+ return;
478
+ }
479
+ }
480
+ }
481
+ finish(restart ? exports.PTY_RESTART_EXIT : CLOSED_EXIT);
482
+ });
483
+ };
484
+ return (async () => {
485
+ const t0 = Date.now();
486
+ for (let attempt = 0;; attempt++) {
487
+ const first = openLeg(legOpts, new URL(opts.url));
488
+ try {
489
+ const o = await first.opened;
490
+ const session = new PtySession(o.socket, o, done);
491
+ follow(session, first);
492
+ return session;
493
+ }
494
+ catch (e) {
495
+ const wait = Math.min(exports.PTY_RECONNECT_FIRST_WAIT_MS * 2 ** attempt, exports.PTY_RECONNECT_MAX_WAIT_MS) * (0.5 + Math.random() / 2);
496
+ if (e instanceof LegRefused && e.restart && reconnect && Date.now() - t0 + wait <= (opts.reconnectForMs ?? exports.PTY_RECONNECT_FOR_MS)) {
497
+ await sleep(wait);
498
+ continue;
499
+ }
500
+ throw e instanceof LegRefused ? e.error : e;
501
+ }
502
+ }
503
+ })();
504
+ }