aicomputer 0.1.0
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/README.md +88 -0
- package/dist/index.d.ts +380 -0
- package/dist/index.js +1856 -0
- package/package.json +40 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1856 @@
|
|
|
1
|
+
// ../public-api-client/dist/index.js
|
|
2
|
+
var PublicApiError = class extends Error {
|
|
3
|
+
status;
|
|
4
|
+
code;
|
|
5
|
+
details;
|
|
6
|
+
constructor(status, message, code, details) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "PublicApiError";
|
|
9
|
+
this.status = status;
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.details = details;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
function delay(ms, signal) {
|
|
15
|
+
if (signal?.aborted) {
|
|
16
|
+
return Promise.reject(new DOMException("Aborted", "AbortError"));
|
|
17
|
+
}
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
const timer = setTimeout(resolve, ms);
|
|
20
|
+
if (signal) {
|
|
21
|
+
const abort = () => {
|
|
22
|
+
clearTimeout(timer);
|
|
23
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
24
|
+
};
|
|
25
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function createPublicApiClient(options) {
|
|
30
|
+
const baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
31
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
32
|
+
if (!fetchImpl) {
|
|
33
|
+
throw new Error("fetch is required");
|
|
34
|
+
}
|
|
35
|
+
const getAccessToken = async () => {
|
|
36
|
+
if (typeof options.accessToken === "function") {
|
|
37
|
+
const token = await options.accessToken();
|
|
38
|
+
return token?.trim() || void 0;
|
|
39
|
+
}
|
|
40
|
+
return options.accessToken?.trim() || void 0;
|
|
41
|
+
};
|
|
42
|
+
const request = async (path, init = {}) => {
|
|
43
|
+
const headers = new Headers(init.headers);
|
|
44
|
+
if (!headers.has("accept")) {
|
|
45
|
+
headers.set("accept", "application/json");
|
|
46
|
+
}
|
|
47
|
+
if (init.body !== void 0 && !headers.has("content-type")) {
|
|
48
|
+
headers.set("content-type", "application/json");
|
|
49
|
+
}
|
|
50
|
+
const token = await getAccessToken();
|
|
51
|
+
if (token) {
|
|
52
|
+
headers.set("authorization", `Bearer ${token}`);
|
|
53
|
+
}
|
|
54
|
+
const response = await fetchImpl(`${baseUrl}${path}`, {
|
|
55
|
+
...init,
|
|
56
|
+
headers
|
|
57
|
+
});
|
|
58
|
+
if (!response.ok) {
|
|
59
|
+
throw await toPublicApiError(response);
|
|
60
|
+
}
|
|
61
|
+
if (response.status === 204) {
|
|
62
|
+
return void 0;
|
|
63
|
+
}
|
|
64
|
+
return await response.json();
|
|
65
|
+
};
|
|
66
|
+
const requestNoAuth = async (path, init = {}) => {
|
|
67
|
+
const headers = new Headers(init.headers);
|
|
68
|
+
if (!headers.has("accept")) {
|
|
69
|
+
headers.set("accept", "application/json");
|
|
70
|
+
}
|
|
71
|
+
if (init.body !== void 0 && !headers.has("content-type")) {
|
|
72
|
+
headers.set("content-type", "application/json");
|
|
73
|
+
}
|
|
74
|
+
const response = await fetchImpl(`${baseUrl}${path}`, {
|
|
75
|
+
...init,
|
|
76
|
+
headers
|
|
77
|
+
});
|
|
78
|
+
if (!response.ok) {
|
|
79
|
+
throw await toPublicApiError(response);
|
|
80
|
+
}
|
|
81
|
+
if (response.status === 204) {
|
|
82
|
+
return void 0;
|
|
83
|
+
}
|
|
84
|
+
return await response.json();
|
|
85
|
+
};
|
|
86
|
+
const client = {
|
|
87
|
+
request,
|
|
88
|
+
requestNoAuth,
|
|
89
|
+
async getMe(signal) {
|
|
90
|
+
return request("/v1/me", { signal });
|
|
91
|
+
},
|
|
92
|
+
async getUsage(signal) {
|
|
93
|
+
return request("/v1/usage", { signal });
|
|
94
|
+
},
|
|
95
|
+
async listComputerImages(signal) {
|
|
96
|
+
return request("/v1/computer-images", { signal });
|
|
97
|
+
},
|
|
98
|
+
async getComputerImage(imageId, signal) {
|
|
99
|
+
return request(`/v1/computer-images/${encodeURIComponent(imageId)}`, { signal });
|
|
100
|
+
},
|
|
101
|
+
async listComputers(signal) {
|
|
102
|
+
return request("/v1/computers", { signal });
|
|
103
|
+
},
|
|
104
|
+
async getComputer(computerId, signal) {
|
|
105
|
+
return request(`/v1/computers/${encodeURIComponent(computerId)}`, { signal });
|
|
106
|
+
},
|
|
107
|
+
async createComputer(body, signal) {
|
|
108
|
+
return request("/v1/computers", {
|
|
109
|
+
method: "POST",
|
|
110
|
+
body: JSON.stringify(body),
|
|
111
|
+
signal
|
|
112
|
+
});
|
|
113
|
+
},
|
|
114
|
+
async updateComputer(computerId, body, signal) {
|
|
115
|
+
return request(`/v1/computers/${encodeURIComponent(computerId)}`, {
|
|
116
|
+
method: "PATCH",
|
|
117
|
+
body: JSON.stringify(body),
|
|
118
|
+
signal
|
|
119
|
+
});
|
|
120
|
+
},
|
|
121
|
+
async deleteComputer(computerId, signal) {
|
|
122
|
+
return request(`/v1/computers/${encodeURIComponent(computerId)}`, {
|
|
123
|
+
method: "DELETE",
|
|
124
|
+
signal
|
|
125
|
+
});
|
|
126
|
+
},
|
|
127
|
+
async startComputer(computerId, signal) {
|
|
128
|
+
return request(`/v1/computers/${encodeURIComponent(computerId)}/actions/start`, {
|
|
129
|
+
method: "POST",
|
|
130
|
+
signal
|
|
131
|
+
});
|
|
132
|
+
},
|
|
133
|
+
async stopComputer(computerId, signal) {
|
|
134
|
+
return request(`/v1/computers/${encodeURIComponent(computerId)}/actions/stop`, {
|
|
135
|
+
method: "POST",
|
|
136
|
+
signal
|
|
137
|
+
});
|
|
138
|
+
},
|
|
139
|
+
async getComputerConnection(computerId, signal) {
|
|
140
|
+
return request(`/v1/computers/${encodeURIComponent(computerId)}/connection`, { signal });
|
|
141
|
+
},
|
|
142
|
+
async execCommand(computerId, body, signal) {
|
|
143
|
+
return request(`/v1/computers/${encodeURIComponent(computerId)}/exec`, {
|
|
144
|
+
method: "POST",
|
|
145
|
+
body: JSON.stringify(body),
|
|
146
|
+
signal
|
|
147
|
+
});
|
|
148
|
+
},
|
|
149
|
+
async getOperation(operationId, signal) {
|
|
150
|
+
return request(`/v1/operations/${encodeURIComponent(operationId)}`, { signal });
|
|
151
|
+
},
|
|
152
|
+
async waitForOperation(operationId, options2 = {}) {
|
|
153
|
+
const pollIntervalMs = options2.pollIntervalMs ?? 1500;
|
|
154
|
+
const timeoutMs = options2.timeoutMs ?? 2 * 6e4;
|
|
155
|
+
const startedAt = Date.now();
|
|
156
|
+
while (true) {
|
|
157
|
+
const { operation } = await client.getOperation(operationId, options2.signal);
|
|
158
|
+
if (operation.status === "succeeded") {
|
|
159
|
+
return operation;
|
|
160
|
+
}
|
|
161
|
+
if (operation.status === "failed") {
|
|
162
|
+
throw new PublicApiError(409, operation.error_message || "operation failed", "operation_failed", operation);
|
|
163
|
+
}
|
|
164
|
+
if (Date.now() - startedAt > timeoutMs) {
|
|
165
|
+
throw new PublicApiError(408, "operation timed out", "operation_timeout", operation);
|
|
166
|
+
}
|
|
167
|
+
await delay(pollIntervalMs, options2.signal);
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
async createAccessSession(computerId, body, signal) {
|
|
171
|
+
return request(`/v1/computers/${encodeURIComponent(computerId)}/access-sessions`, {
|
|
172
|
+
method: "POST",
|
|
173
|
+
body: JSON.stringify(body),
|
|
174
|
+
signal
|
|
175
|
+
});
|
|
176
|
+
},
|
|
177
|
+
async getAccessSession(computerId, sessionId, signal) {
|
|
178
|
+
return request(`/v1/computers/${encodeURIComponent(computerId)}/access-sessions/${encodeURIComponent(sessionId)}`, { signal });
|
|
179
|
+
},
|
|
180
|
+
async deleteAccessSession(computerId, sessionId, signal) {
|
|
181
|
+
return request(`/v1/computers/${encodeURIComponent(computerId)}/access-sessions/${encodeURIComponent(sessionId)}`, {
|
|
182
|
+
method: "DELETE",
|
|
183
|
+
signal
|
|
184
|
+
});
|
|
185
|
+
},
|
|
186
|
+
async listSSHKeys(signal) {
|
|
187
|
+
return request("/v1/ssh-keys", { signal });
|
|
188
|
+
},
|
|
189
|
+
async createSSHKey(body, signal) {
|
|
190
|
+
return request("/v1/ssh-keys", {
|
|
191
|
+
method: "POST",
|
|
192
|
+
body: JSON.stringify(body),
|
|
193
|
+
signal
|
|
194
|
+
});
|
|
195
|
+
},
|
|
196
|
+
async deleteSSHKey(sshKeyId, signal) {
|
|
197
|
+
return request(`/v1/ssh-keys/${encodeURIComponent(sshKeyId)}`, {
|
|
198
|
+
method: "DELETE",
|
|
199
|
+
signal
|
|
200
|
+
});
|
|
201
|
+
},
|
|
202
|
+
async createSSHCertificate(computerId, body, signal) {
|
|
203
|
+
return request(`/v1/computers/${encodeURIComponent(computerId)}/ssh/certificates`, {
|
|
204
|
+
method: "POST",
|
|
205
|
+
body: JSON.stringify(body),
|
|
206
|
+
signal
|
|
207
|
+
});
|
|
208
|
+
},
|
|
209
|
+
async listShares(computerId, signal) {
|
|
210
|
+
return request(`/v1/computers/${encodeURIComponent(computerId)}/shares`, { signal });
|
|
211
|
+
},
|
|
212
|
+
async createLinkShare(computerId, body, signal) {
|
|
213
|
+
return request(`/v1/computers/${encodeURIComponent(computerId)}/shares/links`, {
|
|
214
|
+
method: "POST",
|
|
215
|
+
body: JSON.stringify(body),
|
|
216
|
+
signal
|
|
217
|
+
});
|
|
218
|
+
},
|
|
219
|
+
async deleteLinkShare(computerId, shareId, signal) {
|
|
220
|
+
return request(`/v1/computers/${encodeURIComponent(computerId)}/shares/links/${encodeURIComponent(shareId)}`, {
|
|
221
|
+
method: "DELETE",
|
|
222
|
+
signal
|
|
223
|
+
});
|
|
224
|
+
},
|
|
225
|
+
async createEmailShare(computerId, body, signal) {
|
|
226
|
+
return request(`/v1/computers/${encodeURIComponent(computerId)}/shares/emails`, {
|
|
227
|
+
method: "POST",
|
|
228
|
+
body: JSON.stringify(body),
|
|
229
|
+
signal
|
|
230
|
+
});
|
|
231
|
+
},
|
|
232
|
+
async deleteEmailShare(computerId, shareId, signal) {
|
|
233
|
+
return request(`/v1/computers/${encodeURIComponent(computerId)}/shares/emails/${encodeURIComponent(shareId)}`, {
|
|
234
|
+
method: "DELETE",
|
|
235
|
+
signal
|
|
236
|
+
});
|
|
237
|
+
},
|
|
238
|
+
async resolveShare(shareToken, signal) {
|
|
239
|
+
return requestNoAuth(`/v1/shares/${encodeURIComponent(shareToken)}`, { signal });
|
|
240
|
+
},
|
|
241
|
+
async redeemShare(shareToken, body, signal) {
|
|
242
|
+
return requestNoAuth(`/v1/shares/${encodeURIComponent(shareToken)}/access-sessions`, {
|
|
243
|
+
method: "POST",
|
|
244
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
245
|
+
signal
|
|
246
|
+
});
|
|
247
|
+
},
|
|
248
|
+
async listSnapshots(computerId, signal) {
|
|
249
|
+
return request(`/v1/computers/${encodeURIComponent(computerId)}/snapshots`, { signal });
|
|
250
|
+
},
|
|
251
|
+
async getSnapshot(snapshotId, signal) {
|
|
252
|
+
return request(`/v1/snapshots/${encodeURIComponent(snapshotId)}`, { signal });
|
|
253
|
+
},
|
|
254
|
+
async createSnapshot(computerId, signal) {
|
|
255
|
+
return request(`/v1/computers/${encodeURIComponent(computerId)}/snapshots`, {
|
|
256
|
+
method: "POST",
|
|
257
|
+
signal
|
|
258
|
+
});
|
|
259
|
+
},
|
|
260
|
+
async deleteSnapshot(snapshotId, signal) {
|
|
261
|
+
return request(`/v1/snapshots/${encodeURIComponent(snapshotId)}`, {
|
|
262
|
+
method: "DELETE",
|
|
263
|
+
signal
|
|
264
|
+
});
|
|
265
|
+
},
|
|
266
|
+
async restoreSnapshot(snapshotId, body, signal) {
|
|
267
|
+
return request(`/v1/snapshots/${encodeURIComponent(snapshotId)}/restore`, {
|
|
268
|
+
method: "POST",
|
|
269
|
+
body: JSON.stringify(body),
|
|
270
|
+
signal
|
|
271
|
+
});
|
|
272
|
+
},
|
|
273
|
+
async listPublishedPorts(computerId, signal) {
|
|
274
|
+
return request(`/v1/computers/${encodeURIComponent(computerId)}/ports`, { signal });
|
|
275
|
+
},
|
|
276
|
+
async createPublishedPort(computerId, body, signal) {
|
|
277
|
+
return request(`/v1/computers/${encodeURIComponent(computerId)}/ports`, {
|
|
278
|
+
method: "POST",
|
|
279
|
+
body: JSON.stringify(body),
|
|
280
|
+
signal
|
|
281
|
+
});
|
|
282
|
+
},
|
|
283
|
+
async deletePublishedPort(computerId, port, signal) {
|
|
284
|
+
return request(`/v1/computers/${encodeURIComponent(computerId)}/ports/${encodeURIComponent(String(port))}`, {
|
|
285
|
+
method: "DELETE",
|
|
286
|
+
signal
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
return client;
|
|
291
|
+
}
|
|
292
|
+
async function toPublicApiError(response) {
|
|
293
|
+
const contentType = response.headers.get("content-type") || "";
|
|
294
|
+
if (contentType.includes("application/json")) {
|
|
295
|
+
try {
|
|
296
|
+
const payload = await response.json();
|
|
297
|
+
const code = typeof payload === "object" && payload !== null && "error" in payload && typeof payload.error === "object" && payload.error?.code ? payload.error?.code : void 0;
|
|
298
|
+
const message = typeof payload === "object" && payload !== null && "error" in payload && typeof payload.error === "object" && payload.error?.message ? payload.error?.message : JSON.stringify(payload);
|
|
299
|
+
return new PublicApiError(response.status, message || response.statusText || "request failed", code, payload);
|
|
300
|
+
} catch (error) {
|
|
301
|
+
return new PublicApiError(response.status, response.statusText || "request failed", void 0, error);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
const body = await response.text().catch(() => "");
|
|
305
|
+
return new PublicApiError(response.status, body || response.statusText || "request failed");
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// src/index.ts
|
|
309
|
+
var DEFAULT_BASE_URL = "https://micro-api.computer.agentcomputer.ai";
|
|
310
|
+
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
311
|
+
var DEFAULT_OPERATION_TIMEOUT_MS = 12e4;
|
|
312
|
+
var DEFAULT_COMPUTER_STATE_TIMEOUT_MS = 3e5;
|
|
313
|
+
var DEFAULT_CONNECT_REQUEST_TIMEOUT_MS = 3e4;
|
|
314
|
+
var NOT_FOUND_CODES = /* @__PURE__ */ new Set(["computer_not_found", "snapshot_not_found"]);
|
|
315
|
+
var COMPUTER_FAILURE_STATES = /* @__PURE__ */ new Set(["failed", "deleted", "deleting"]);
|
|
316
|
+
var AgentComputer = class {
|
|
317
|
+
images;
|
|
318
|
+
computers;
|
|
319
|
+
snapshots;
|
|
320
|
+
sshKeys;
|
|
321
|
+
client;
|
|
322
|
+
fetchImpl;
|
|
323
|
+
defaultPollIntervalMs;
|
|
324
|
+
defaultOperationTimeoutMs;
|
|
325
|
+
defaultComputerStateTimeoutMs;
|
|
326
|
+
defaultConnectRequestTimeoutMs;
|
|
327
|
+
constructor(options = {}) {
|
|
328
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
329
|
+
if (!fetchImpl) {
|
|
330
|
+
throw new Error("fetch is required");
|
|
331
|
+
}
|
|
332
|
+
this.fetchImpl = fetchImpl;
|
|
333
|
+
this.defaultPollIntervalMs = options.defaultPollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
334
|
+
this.defaultOperationTimeoutMs = options.defaultOperationTimeoutMs ?? DEFAULT_OPERATION_TIMEOUT_MS;
|
|
335
|
+
this.defaultComputerStateTimeoutMs = options.defaultComputerStateTimeoutMs ?? DEFAULT_COMPUTER_STATE_TIMEOUT_MS;
|
|
336
|
+
this.defaultConnectRequestTimeoutMs = options.defaultConnectRequestTimeoutMs ?? DEFAULT_CONNECT_REQUEST_TIMEOUT_MS;
|
|
337
|
+
this.client = createPublicApiClient({
|
|
338
|
+
baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,
|
|
339
|
+
accessToken: options.apiKey ?? options.accessToken,
|
|
340
|
+
fetch: fetchImpl
|
|
341
|
+
});
|
|
342
|
+
this.images = new ComputerImagesClient(this);
|
|
343
|
+
this.computers = new ComputersClient(this);
|
|
344
|
+
this.snapshots = new SnapshotsClient(this);
|
|
345
|
+
this.sshKeys = new SSHKeysClient(this);
|
|
346
|
+
}
|
|
347
|
+
async me(signal) {
|
|
348
|
+
return this.client.getMe(signal);
|
|
349
|
+
}
|
|
350
|
+
async usage(signal) {
|
|
351
|
+
return this.client.getUsage(signal);
|
|
352
|
+
}
|
|
353
|
+
async resolveShare(shareToken, signal) {
|
|
354
|
+
return this.client.resolveShare(shareToken, signal);
|
|
355
|
+
}
|
|
356
|
+
async redeemShare(shareToken, body, signal) {
|
|
357
|
+
return this.client.redeemShare(shareToken, body, signal);
|
|
358
|
+
}
|
|
359
|
+
async waitForOperation(operationOrId, options = {}) {
|
|
360
|
+
const operationId = typeof operationOrId === "string" ? operationOrId : operationOrId.id;
|
|
361
|
+
return this.client.waitForOperation(operationId, {
|
|
362
|
+
pollIntervalMs: options.pollIntervalMs ?? this.defaultPollIntervalMs,
|
|
363
|
+
timeoutMs: options.timeoutMs ?? this.defaultOperationTimeoutMs,
|
|
364
|
+
signal: options.signal
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
async waitForComputerState(computerId, targetState, options = {}) {
|
|
368
|
+
const timeoutMs = options.timeoutMs ?? this.defaultComputerStateTimeoutMs;
|
|
369
|
+
const pollIntervalMs = options.pollIntervalMs ?? this.defaultPollIntervalMs;
|
|
370
|
+
const startedAt = Date.now();
|
|
371
|
+
while (true) {
|
|
372
|
+
const computer = await this.client.getComputer(computerId, options.signal);
|
|
373
|
+
if (computer.computer.state === targetState) {
|
|
374
|
+
return new Computer(this, computer.computer);
|
|
375
|
+
}
|
|
376
|
+
if (COMPUTER_FAILURE_STATES.has(computer.computer.state)) {
|
|
377
|
+
throw new PublicApiError(
|
|
378
|
+
409,
|
|
379
|
+
`computer entered terminal state ${computer.computer.state}`,
|
|
380
|
+
"computer_terminal_state",
|
|
381
|
+
computer.computer
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
if (Date.now() - startedAt > timeoutMs) {
|
|
385
|
+
throw new PublicApiError(
|
|
386
|
+
408,
|
|
387
|
+
`computer did not reach ${targetState} before timeout`,
|
|
388
|
+
"computer_state_timeout",
|
|
389
|
+
computer.computer
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
await delay(pollIntervalMs, options.signal);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
computer(idOrHandle) {
|
|
396
|
+
return this.computers.resolve(idOrHandle);
|
|
397
|
+
}
|
|
398
|
+
/** @internal */
|
|
399
|
+
internalClient() {
|
|
400
|
+
return this.client;
|
|
401
|
+
}
|
|
402
|
+
/** @internal */
|
|
403
|
+
internalFetch() {
|
|
404
|
+
return this.fetchImpl;
|
|
405
|
+
}
|
|
406
|
+
/** @internal */
|
|
407
|
+
internalDefaultPollIntervalMs() {
|
|
408
|
+
return this.defaultPollIntervalMs;
|
|
409
|
+
}
|
|
410
|
+
/** @internal */
|
|
411
|
+
internalDefaultComputerStateTimeoutMs() {
|
|
412
|
+
return this.defaultComputerStateTimeoutMs;
|
|
413
|
+
}
|
|
414
|
+
/** @internal */
|
|
415
|
+
internalDefaultOperationTimeoutMs() {
|
|
416
|
+
return this.defaultOperationTimeoutMs;
|
|
417
|
+
}
|
|
418
|
+
/** @internal */
|
|
419
|
+
internalDefaultConnectRequestTimeoutMs() {
|
|
420
|
+
return this.defaultConnectRequestTimeoutMs;
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
var ComputerImagesClient = class {
|
|
424
|
+
constructor(sdk) {
|
|
425
|
+
this.sdk = sdk;
|
|
426
|
+
}
|
|
427
|
+
sdk;
|
|
428
|
+
async list(signal) {
|
|
429
|
+
const response = await this.sdk.internalClient().listComputerImages(signal);
|
|
430
|
+
return response.images;
|
|
431
|
+
}
|
|
432
|
+
async get(imageId, signal) {
|
|
433
|
+
const response = await this.sdk.internalClient().getComputerImage(imageId, signal);
|
|
434
|
+
return response.image;
|
|
435
|
+
}
|
|
436
|
+
async getDefault(signal) {
|
|
437
|
+
const images = await this.list(signal);
|
|
438
|
+
const active = images.find((image) => image.status === "active");
|
|
439
|
+
if (active) {
|
|
440
|
+
return active;
|
|
441
|
+
}
|
|
442
|
+
if (images[0]) {
|
|
443
|
+
return images[0];
|
|
444
|
+
}
|
|
445
|
+
throw new PublicApiError(404, "no computer images are available", "image_not_found");
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
var ComputersClient = class {
|
|
449
|
+
constructor(sdk) {
|
|
450
|
+
this.sdk = sdk;
|
|
451
|
+
}
|
|
452
|
+
sdk;
|
|
453
|
+
async list(signal) {
|
|
454
|
+
const response = await this.sdk.internalClient().listComputers(signal);
|
|
455
|
+
return response.computers.map((computer) => new Computer(this.sdk, computer));
|
|
456
|
+
}
|
|
457
|
+
async get(computerId, signal) {
|
|
458
|
+
const response = await this.sdk.internalClient().getComputer(computerId, signal);
|
|
459
|
+
return new Computer(this.sdk, response.computer);
|
|
460
|
+
}
|
|
461
|
+
async getByHandle(handle, signal) {
|
|
462
|
+
const computers = await this.list(signal);
|
|
463
|
+
return computers.find((computer) => computer.handle === handle) ?? null;
|
|
464
|
+
}
|
|
465
|
+
async resolve(idOrHandle, signal) {
|
|
466
|
+
try {
|
|
467
|
+
return await this.get(idOrHandle, signal);
|
|
468
|
+
} catch (error) {
|
|
469
|
+
if (!(error instanceof PublicApiError)) {
|
|
470
|
+
throw error;
|
|
471
|
+
}
|
|
472
|
+
if (!NOT_FOUND_CODES.has(error.code ?? "")) {
|
|
473
|
+
throw error;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
const byHandle = await this.getByHandle(idOrHandle, signal);
|
|
477
|
+
if (byHandle) {
|
|
478
|
+
return byHandle;
|
|
479
|
+
}
|
|
480
|
+
throw new PublicApiError(404, `computer ${idOrHandle} not found`, "computer_not_found");
|
|
481
|
+
}
|
|
482
|
+
async create(options = {}, signal) {
|
|
483
|
+
const source = options.source ?? await this.defaultSource(signal);
|
|
484
|
+
const response = await this.sdk.internalClient().createComputer(
|
|
485
|
+
{
|
|
486
|
+
handle: options.handle,
|
|
487
|
+
display_name: options.displayName,
|
|
488
|
+
source,
|
|
489
|
+
guest: options.authorizedKeys && options.authorizedKeys.length > 0 ? { authorized_keys: options.authorizedKeys } : void 0
|
|
490
|
+
},
|
|
491
|
+
signal
|
|
492
|
+
);
|
|
493
|
+
await waitForOperationIfPresent(this.sdk, response.operation, signal);
|
|
494
|
+
const refreshed = await this.sdk.internalClient().getComputer(response.computer.id, signal);
|
|
495
|
+
return new Computer(this.sdk, refreshed.computer);
|
|
496
|
+
}
|
|
497
|
+
async ensure(options = {}, signal) {
|
|
498
|
+
let computer;
|
|
499
|
+
if (options.id) {
|
|
500
|
+
computer = await this.get(options.id, signal);
|
|
501
|
+
} else if (options.handle) {
|
|
502
|
+
const existing = await this.getByHandle(options.handle, signal);
|
|
503
|
+
computer = existing ?? await this.create(options, signal);
|
|
504
|
+
} else {
|
|
505
|
+
computer = await this.create(options, signal);
|
|
506
|
+
}
|
|
507
|
+
if (options.updateMetadata && options.displayName !== void 0) {
|
|
508
|
+
computer = await computer.update(
|
|
509
|
+
{
|
|
510
|
+
displayName: options.displayName
|
|
511
|
+
},
|
|
512
|
+
signal
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
if (options.start) {
|
|
516
|
+
await computer.ensureRunning({ signal });
|
|
517
|
+
}
|
|
518
|
+
return computer;
|
|
519
|
+
}
|
|
520
|
+
async defaultSource(signal) {
|
|
521
|
+
const image = await this.sdk.images.getDefault(signal);
|
|
522
|
+
return {
|
|
523
|
+
kind: "image",
|
|
524
|
+
image_id: image.id
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
var Computer = class {
|
|
529
|
+
constructor(sdk, computer) {
|
|
530
|
+
this.sdk = sdk;
|
|
531
|
+
this.computer = computer;
|
|
532
|
+
this.commands = new ComputerCommandsClient(this);
|
|
533
|
+
this.pty = new ComputerPtyClient(this);
|
|
534
|
+
this.files = new ComputerFilesClient(this);
|
|
535
|
+
this.git = new ComputerGitClient(this);
|
|
536
|
+
this.ports = new ComputerPortsClient(this);
|
|
537
|
+
this.shares = new ComputerSharesClient(this);
|
|
538
|
+
this.snapshots = new ComputerSnapshotsClient(this);
|
|
539
|
+
}
|
|
540
|
+
sdk;
|
|
541
|
+
computer;
|
|
542
|
+
commands;
|
|
543
|
+
pty;
|
|
544
|
+
files;
|
|
545
|
+
git;
|
|
546
|
+
ports;
|
|
547
|
+
shares;
|
|
548
|
+
snapshots;
|
|
549
|
+
get raw() {
|
|
550
|
+
return this.computer;
|
|
551
|
+
}
|
|
552
|
+
get id() {
|
|
553
|
+
return this.computer.id;
|
|
554
|
+
}
|
|
555
|
+
get handle() {
|
|
556
|
+
return this.computer.handle;
|
|
557
|
+
}
|
|
558
|
+
get displayName() {
|
|
559
|
+
return this.computer.display_name;
|
|
560
|
+
}
|
|
561
|
+
get state() {
|
|
562
|
+
return this.computer.state;
|
|
563
|
+
}
|
|
564
|
+
get sourceKind() {
|
|
565
|
+
return this.computer.source_kind;
|
|
566
|
+
}
|
|
567
|
+
get sourceSnapshotId() {
|
|
568
|
+
return this.computer.source_snapshot_id;
|
|
569
|
+
}
|
|
570
|
+
get createdAt() {
|
|
571
|
+
return this.computer.created_at;
|
|
572
|
+
}
|
|
573
|
+
get updatedAt() {
|
|
574
|
+
return this.computer.updated_at;
|
|
575
|
+
}
|
|
576
|
+
get deletedAt() {
|
|
577
|
+
return this.computer.deleted_at;
|
|
578
|
+
}
|
|
579
|
+
get primaryUrl() {
|
|
580
|
+
return this.computer.primary_url;
|
|
581
|
+
}
|
|
582
|
+
get failureReason() {
|
|
583
|
+
return this.computer.failure_reason;
|
|
584
|
+
}
|
|
585
|
+
internalSdk() {
|
|
586
|
+
return this.sdk;
|
|
587
|
+
}
|
|
588
|
+
async refresh(signal) {
|
|
589
|
+
const response = await this.sdk.internalClient().getComputer(this.id, signal);
|
|
590
|
+
this.computer = response.computer;
|
|
591
|
+
return this;
|
|
592
|
+
}
|
|
593
|
+
async update(options, signal) {
|
|
594
|
+
const response = await this.sdk.internalClient().updateComputer(
|
|
595
|
+
this.id,
|
|
596
|
+
{
|
|
597
|
+
display_name: options.displayName ?? this.displayName
|
|
598
|
+
},
|
|
599
|
+
signal
|
|
600
|
+
);
|
|
601
|
+
this.computer = response.computer;
|
|
602
|
+
return this;
|
|
603
|
+
}
|
|
604
|
+
async start(signal) {
|
|
605
|
+
const response = await this.sdk.internalClient().startComputer(this.id, signal);
|
|
606
|
+
await waitForOperationIfPresent(this.sdk, response.operation, signal);
|
|
607
|
+
this.computer = (await this.sdk.internalClient().getComputer(this.id, signal)).computer;
|
|
608
|
+
return this;
|
|
609
|
+
}
|
|
610
|
+
async stop(signal) {
|
|
611
|
+
const response = await this.sdk.internalClient().stopComputer(this.id, signal);
|
|
612
|
+
await waitForOperationIfPresent(this.sdk, response.operation, signal);
|
|
613
|
+
this.computer = (await this.sdk.internalClient().getComputer(this.id, signal)).computer;
|
|
614
|
+
return this;
|
|
615
|
+
}
|
|
616
|
+
async delete(signal) {
|
|
617
|
+
const response = await this.sdk.internalClient().deleteComputer(this.id, signal);
|
|
618
|
+
await waitForOperationIfPresent(this.sdk, response.operation, signal);
|
|
619
|
+
this.computer = response.computer;
|
|
620
|
+
}
|
|
621
|
+
async ensureRunning(options = {}) {
|
|
622
|
+
await this.refresh(options.signal);
|
|
623
|
+
if (this.state === "running") {
|
|
624
|
+
return this;
|
|
625
|
+
}
|
|
626
|
+
if (this.state === "stopping") {
|
|
627
|
+
await this.waitUntilStopped(options);
|
|
628
|
+
} else if (COMPUTER_FAILURE_STATES.has(this.state)) {
|
|
629
|
+
throw new PublicApiError(
|
|
630
|
+
409,
|
|
631
|
+
`computer cannot be started from ${this.state}`,
|
|
632
|
+
"computer_not_startable",
|
|
633
|
+
this.computer
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
if (this.state === "stopped") {
|
|
637
|
+
await this.start(options.signal);
|
|
638
|
+
}
|
|
639
|
+
await this.waitUntilRunning(options);
|
|
640
|
+
return this;
|
|
641
|
+
}
|
|
642
|
+
async ensureStopped(options = {}) {
|
|
643
|
+
await this.refresh(options.signal);
|
|
644
|
+
if (this.state === "stopped") {
|
|
645
|
+
return this;
|
|
646
|
+
}
|
|
647
|
+
if (this.state === "starting" || this.state === "provisioning") {
|
|
648
|
+
await this.waitUntilRunning(options);
|
|
649
|
+
}
|
|
650
|
+
if (this.state === "running") {
|
|
651
|
+
await this.stop(options.signal);
|
|
652
|
+
} else if (COMPUTER_FAILURE_STATES.has(this.state)) {
|
|
653
|
+
throw new PublicApiError(
|
|
654
|
+
409,
|
|
655
|
+
`computer cannot be stopped from ${this.state}`,
|
|
656
|
+
"computer_not_stoppable",
|
|
657
|
+
this.computer
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
await this.waitUntilStopped(options);
|
|
661
|
+
return this;
|
|
662
|
+
}
|
|
663
|
+
async waitUntilRunning(options = {}) {
|
|
664
|
+
const computer = await this.sdk.waitForComputerState(this.id, "running", options);
|
|
665
|
+
this.computer = computer.raw;
|
|
666
|
+
return this;
|
|
667
|
+
}
|
|
668
|
+
async waitUntilStopped(options = {}) {
|
|
669
|
+
const computer = await this.sdk.waitForComputerState(this.id, "stopped", options);
|
|
670
|
+
this.computer = computer.raw;
|
|
671
|
+
return this;
|
|
672
|
+
}
|
|
673
|
+
async getConnection(signal) {
|
|
674
|
+
return this.sdk.internalClient().getComputerConnection(this.id, signal);
|
|
675
|
+
}
|
|
676
|
+
async createBrowserAccess(options = {}) {
|
|
677
|
+
await this.ensureRunning({ signal: options.signal });
|
|
678
|
+
const response = await this.sdk.internalClient().createAccessSession(
|
|
679
|
+
this.id,
|
|
680
|
+
{
|
|
681
|
+
kind: "browser",
|
|
682
|
+
ttl_seconds: options.ttlSeconds
|
|
683
|
+
},
|
|
684
|
+
options.signal
|
|
685
|
+
);
|
|
686
|
+
return response.session;
|
|
687
|
+
}
|
|
688
|
+
async createVNCAccess(options = {}) {
|
|
689
|
+
await this.ensureRunning({ signal: options.signal });
|
|
690
|
+
const response = await this.sdk.internalClient().createAccessSession(
|
|
691
|
+
this.id,
|
|
692
|
+
{
|
|
693
|
+
kind: "vnc",
|
|
694
|
+
ttl_seconds: options.ttlSeconds
|
|
695
|
+
},
|
|
696
|
+
options.signal
|
|
697
|
+
);
|
|
698
|
+
return response.session;
|
|
699
|
+
}
|
|
700
|
+
async createSSHCertificate(body, signal) {
|
|
701
|
+
await this.ensureRunning({ signal });
|
|
702
|
+
return this.sdk.internalClient().createSSHCertificate(this.id, body, signal);
|
|
703
|
+
}
|
|
704
|
+
async execCapabilities(signal) {
|
|
705
|
+
await this.ensureRunning({ signal });
|
|
706
|
+
const response = await this.sdk.internalClient().createAccessSession(
|
|
707
|
+
this.id,
|
|
708
|
+
{ kind: "exec" },
|
|
709
|
+
signal
|
|
710
|
+
);
|
|
711
|
+
try {
|
|
712
|
+
return response.session.transport?.capabilities ?? [];
|
|
713
|
+
} finally {
|
|
714
|
+
await this.sdk.internalClient().deleteAccessSession(this.id, response.session.id, signal).catch(() => void 0);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
async supportsCapability(capability, signal) {
|
|
718
|
+
return (await this.execCapabilities(signal)).includes(capability);
|
|
719
|
+
}
|
|
720
|
+
async requireCapability(capability, signal) {
|
|
721
|
+
const availableCapabilities = await this.execCapabilities(signal);
|
|
722
|
+
if (!availableCapabilities.includes(capability)) {
|
|
723
|
+
throw new UnsupportedCapabilityError(capability, availableCapabilities);
|
|
724
|
+
}
|
|
725
|
+
return availableCapabilities;
|
|
726
|
+
}
|
|
727
|
+
async waitForPort(port, options = {}) {
|
|
728
|
+
const timeoutMs = options.timeoutMs ?? this.sdk.internalDefaultComputerStateTimeoutMs();
|
|
729
|
+
const pollIntervalMs = options.pollIntervalMs ?? this.sdk.internalDefaultPollIntervalMs();
|
|
730
|
+
const startedAt = Date.now();
|
|
731
|
+
while (true) {
|
|
732
|
+
const ports = await this.ports.list(options.signal);
|
|
733
|
+
const publishedPort = ports.find((candidate) => candidate.port === port);
|
|
734
|
+
if (publishedPort?.state === "ready") {
|
|
735
|
+
return publishedPort;
|
|
736
|
+
}
|
|
737
|
+
if (publishedPort?.state === "failed") {
|
|
738
|
+
throw new PublicApiError(
|
|
739
|
+
409,
|
|
740
|
+
publishedPort.failure_reason || `published port ${port} failed`,
|
|
741
|
+
"published_port_failed",
|
|
742
|
+
publishedPort
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
if (Date.now() - startedAt > timeoutMs) {
|
|
746
|
+
throw new PublicApiError(
|
|
747
|
+
408,
|
|
748
|
+
`computer port ${port} did not become ready before timeout`,
|
|
749
|
+
"computer_port_timeout",
|
|
750
|
+
{ computer_id: this.id, port }
|
|
751
|
+
);
|
|
752
|
+
}
|
|
753
|
+
await delay(pollIntervalMs, options.signal);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
async waitForUrl(url, options = {}) {
|
|
757
|
+
const timeoutMs = options.timeoutMs ?? this.sdk.internalDefaultComputerStateTimeoutMs();
|
|
758
|
+
const pollIntervalMs = options.pollIntervalMs ?? this.sdk.internalDefaultPollIntervalMs();
|
|
759
|
+
const startedAt = Date.now();
|
|
760
|
+
while (true) {
|
|
761
|
+
try {
|
|
762
|
+
const response = await this.sdk.internalFetch()(url, {
|
|
763
|
+
method: "GET",
|
|
764
|
+
redirect: "follow",
|
|
765
|
+
signal: options.signal
|
|
766
|
+
});
|
|
767
|
+
if (response.ok) {
|
|
768
|
+
return response;
|
|
769
|
+
}
|
|
770
|
+
} catch {
|
|
771
|
+
}
|
|
772
|
+
if (Date.now() - startedAt > timeoutMs) {
|
|
773
|
+
throw new PublicApiError(
|
|
774
|
+
408,
|
|
775
|
+
`url ${url} did not become reachable before timeout`,
|
|
776
|
+
"computer_url_timeout",
|
|
777
|
+
{ computer_id: this.id, url }
|
|
778
|
+
);
|
|
779
|
+
}
|
|
780
|
+
await delay(pollIntervalMs, options.signal);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
async waitForProcess(selectorOrPredicate, options = {}) {
|
|
784
|
+
const timeoutMs = options.timeoutMs ?? this.sdk.internalDefaultComputerStateTimeoutMs();
|
|
785
|
+
const pollIntervalMs = options.pollIntervalMs ?? this.sdk.internalDefaultPollIntervalMs();
|
|
786
|
+
const matcher = typeof selectorOrPredicate === "function" ? selectorOrPredicate : (command) => matchesCommandSelector(command, selectorOrPredicate);
|
|
787
|
+
const startedAt = Date.now();
|
|
788
|
+
while (true) {
|
|
789
|
+
const commands = await this.commands.list(options.signal);
|
|
790
|
+
const match = commands.find(matcher);
|
|
791
|
+
if (match) {
|
|
792
|
+
return match;
|
|
793
|
+
}
|
|
794
|
+
if (Date.now() - startedAt > timeoutMs) {
|
|
795
|
+
throw new PublicApiError(
|
|
796
|
+
408,
|
|
797
|
+
"computer process did not become available before timeout",
|
|
798
|
+
"computer_process_timeout",
|
|
799
|
+
{ computer_id: this.id, selector: selectorOrPredicate }
|
|
800
|
+
);
|
|
801
|
+
}
|
|
802
|
+
await delay(pollIntervalMs, options.signal);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
async waitForFile(path, options = {}) {
|
|
806
|
+
const timeoutMs = options.timeoutMs ?? this.sdk.internalDefaultComputerStateTimeoutMs();
|
|
807
|
+
const pollIntervalMs = options.pollIntervalMs ?? this.sdk.internalDefaultPollIntervalMs();
|
|
808
|
+
const startedAt = Date.now();
|
|
809
|
+
while (true) {
|
|
810
|
+
let exists = false;
|
|
811
|
+
try {
|
|
812
|
+
await this.commands.run(
|
|
813
|
+
["sh", "-lc", `test -e ${shellQuote(path)}`],
|
|
814
|
+
{ signal: options.signal }
|
|
815
|
+
);
|
|
816
|
+
exists = true;
|
|
817
|
+
} catch (error) {
|
|
818
|
+
if (!(error instanceof CommandExitError)) {
|
|
819
|
+
throw error;
|
|
820
|
+
}
|
|
821
|
+
if (error.result.exitCode !== 1) {
|
|
822
|
+
throw error;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
if (exists) {
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
if (Date.now() - startedAt > timeoutMs) {
|
|
829
|
+
throw new PublicApiError(
|
|
830
|
+
408,
|
|
831
|
+
`file ${path} did not appear before timeout`,
|
|
832
|
+
"computer_file_timeout",
|
|
833
|
+
{ computer_id: this.id, path }
|
|
834
|
+
);
|
|
835
|
+
}
|
|
836
|
+
await delay(pollIntervalMs, options.signal);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
};
|
|
840
|
+
var ExecTransportError = class extends Error {
|
|
841
|
+
status;
|
|
842
|
+
code;
|
|
843
|
+
details;
|
|
844
|
+
constructor(message, options = {}) {
|
|
845
|
+
super(message);
|
|
846
|
+
this.name = "ExecTransportError";
|
|
847
|
+
this.status = options.status;
|
|
848
|
+
this.code = options.code;
|
|
849
|
+
this.details = options.details;
|
|
850
|
+
}
|
|
851
|
+
};
|
|
852
|
+
var UnsupportedCapabilityError = class extends Error {
|
|
853
|
+
capability;
|
|
854
|
+
availableCapabilities;
|
|
855
|
+
constructor(capability, availableCapabilities = []) {
|
|
856
|
+
super(`${capability} capability is not advertised by computer exec sessions`);
|
|
857
|
+
this.name = "UnsupportedCapabilityError";
|
|
858
|
+
this.capability = capability;
|
|
859
|
+
this.availableCapabilities = availableCapabilities;
|
|
860
|
+
}
|
|
861
|
+
};
|
|
862
|
+
var CommandExitError = class extends Error {
|
|
863
|
+
result;
|
|
864
|
+
constructor(result) {
|
|
865
|
+
super(result.error || `command exited with code ${result.exitCode}`);
|
|
866
|
+
this.name = "CommandExitError";
|
|
867
|
+
this.result = result;
|
|
868
|
+
}
|
|
869
|
+
};
|
|
870
|
+
var ExecGatewayConnection = class {
|
|
871
|
+
constructor(client, fetchImpl, computerId, session) {
|
|
872
|
+
this.client = client;
|
|
873
|
+
this.fetchImpl = fetchImpl;
|
|
874
|
+
this.computerId = computerId;
|
|
875
|
+
this.session = session;
|
|
876
|
+
}
|
|
877
|
+
client;
|
|
878
|
+
fetchImpl;
|
|
879
|
+
computerId;
|
|
880
|
+
session;
|
|
881
|
+
async close(signal) {
|
|
882
|
+
try {
|
|
883
|
+
await this.client.deleteAccessSession(this.computerId, this.session.id, signal);
|
|
884
|
+
} catch (error) {
|
|
885
|
+
if (error instanceof PublicApiError && (error.status === 404 || error.status === 410)) {
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
throw error;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
async requestJson(method, body, options = {}) {
|
|
892
|
+
const response = await this.request(method, body, {
|
|
893
|
+
accept: "application/json",
|
|
894
|
+
signal: options.signal,
|
|
895
|
+
requestTimeoutMs: options.requestTimeoutMs
|
|
896
|
+
});
|
|
897
|
+
return await response.json();
|
|
898
|
+
}
|
|
899
|
+
async openStream(method, body, options = {}) {
|
|
900
|
+
const response = await this.request(method, body, {
|
|
901
|
+
accept: "application/connect+json",
|
|
902
|
+
signal: options.signal,
|
|
903
|
+
requestTimeoutMs: options.requestTimeoutMs,
|
|
904
|
+
connectStream: true
|
|
905
|
+
});
|
|
906
|
+
if (!response.body) {
|
|
907
|
+
throw new ExecTransportError("exec gateway did not return a stream body");
|
|
908
|
+
}
|
|
909
|
+
return response.body.getReader();
|
|
910
|
+
}
|
|
911
|
+
endpoint(method) {
|
|
912
|
+
return `${this.session.connect_url.replace(/\/+$/, "")}/process.Process/${method}`;
|
|
913
|
+
}
|
|
914
|
+
authorizationHeader() {
|
|
915
|
+
if (!this.session.connect_token) {
|
|
916
|
+
throw new PublicApiError(
|
|
917
|
+
401,
|
|
918
|
+
"exec session does not include a connect token",
|
|
919
|
+
"missing_connect_token",
|
|
920
|
+
this.session
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
return `Bearer ${this.session.connect_token}`;
|
|
924
|
+
}
|
|
925
|
+
async request(method, body, options) {
|
|
926
|
+
const { signal, clear } = createTimedSignal(
|
|
927
|
+
options.signal,
|
|
928
|
+
options.requestTimeoutMs ?? DEFAULT_CONNECT_REQUEST_TIMEOUT_MS
|
|
929
|
+
);
|
|
930
|
+
try {
|
|
931
|
+
const headers = new Headers();
|
|
932
|
+
headers.set("authorization", this.authorizationHeader());
|
|
933
|
+
headers.set("accept", options.accept);
|
|
934
|
+
headers.set("content-type", "application/json");
|
|
935
|
+
if (options.connectStream) {
|
|
936
|
+
headers.set("connect-protocol-version", "1");
|
|
937
|
+
}
|
|
938
|
+
const response = await this.fetchImpl(this.endpoint(method), {
|
|
939
|
+
method: "POST",
|
|
940
|
+
headers,
|
|
941
|
+
body: JSON.stringify(body),
|
|
942
|
+
signal
|
|
943
|
+
});
|
|
944
|
+
if (!response.ok) {
|
|
945
|
+
throw await responseToExecTransportError(response);
|
|
946
|
+
}
|
|
947
|
+
return response;
|
|
948
|
+
} finally {
|
|
949
|
+
clear();
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
};
|
|
953
|
+
var CommandHandle = class {
|
|
954
|
+
constructor(commands, gateway, reader, initialSelector, handlers = {}) {
|
|
955
|
+
this.commands = commands;
|
|
956
|
+
this.gateway = gateway;
|
|
957
|
+
this.reader = reader;
|
|
958
|
+
this.selector = initialSelector;
|
|
959
|
+
this.pidPromise = new Promise((resolve, reject) => {
|
|
960
|
+
this.pidResolve = resolve;
|
|
961
|
+
this.pidReject = reject;
|
|
962
|
+
});
|
|
963
|
+
if (typeof initialSelector === "number") {
|
|
964
|
+
this.resolvedPid = initialSelector;
|
|
965
|
+
this.pidResolve(initialSelector);
|
|
966
|
+
} else if (typeof initialSelector === "object" && "pid" in initialSelector) {
|
|
967
|
+
this.resolvedPid = initialSelector.pid;
|
|
968
|
+
this.pidResolve(initialSelector.pid);
|
|
969
|
+
}
|
|
970
|
+
this.resultPromise = this.consume(handlers);
|
|
971
|
+
}
|
|
972
|
+
commands;
|
|
973
|
+
gateway;
|
|
974
|
+
reader;
|
|
975
|
+
selector;
|
|
976
|
+
stdoutChunks = [];
|
|
977
|
+
stderrChunks = [];
|
|
978
|
+
pidPromise;
|
|
979
|
+
resultPromise;
|
|
980
|
+
pidResolve;
|
|
981
|
+
pidReject;
|
|
982
|
+
resolvedPid;
|
|
983
|
+
closed = false;
|
|
984
|
+
get pid() {
|
|
985
|
+
if (this.resolvedPid === void 0) {
|
|
986
|
+
throw new Error("command pid is not available yet");
|
|
987
|
+
}
|
|
988
|
+
return this.resolvedPid;
|
|
989
|
+
}
|
|
990
|
+
get stdout() {
|
|
991
|
+
return this.stdoutChunks.join("");
|
|
992
|
+
}
|
|
993
|
+
get stderr() {
|
|
994
|
+
return this.stderrChunks.join("");
|
|
995
|
+
}
|
|
996
|
+
async wait(options = {}) {
|
|
997
|
+
const result = await this.resultPromise;
|
|
998
|
+
if (options.rejectOnNonZero ?? true) {
|
|
999
|
+
assertSuccessfulCommand(result);
|
|
1000
|
+
}
|
|
1001
|
+
return result;
|
|
1002
|
+
}
|
|
1003
|
+
async disconnect() {
|
|
1004
|
+
if (this.closed) {
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
this.closed = true;
|
|
1008
|
+
await this.reader.cancel("disconnected").catch(() => void 0);
|
|
1009
|
+
await this.gateway.close().catch(() => void 0);
|
|
1010
|
+
}
|
|
1011
|
+
async kill(signal = "SIGTERM", requestSignal) {
|
|
1012
|
+
await this.commands.kill(this.currentSelector(), signal, requestSignal);
|
|
1013
|
+
}
|
|
1014
|
+
async sendStdin(data, requestSignal) {
|
|
1015
|
+
await this.commands.sendStdin(this.currentSelector(), data, requestSignal);
|
|
1016
|
+
}
|
|
1017
|
+
async closeStdin(requestSignal) {
|
|
1018
|
+
await this.commands.closeStdin(this.currentSelector(), requestSignal);
|
|
1019
|
+
}
|
|
1020
|
+
async awaitPid() {
|
|
1021
|
+
return this.pidPromise;
|
|
1022
|
+
}
|
|
1023
|
+
currentSelector() {
|
|
1024
|
+
if (this.resolvedPid !== void 0) {
|
|
1025
|
+
return { pid: this.resolvedPid };
|
|
1026
|
+
}
|
|
1027
|
+
if (!this.selector) {
|
|
1028
|
+
throw new ExecTransportError("command selector is not available until the process starts");
|
|
1029
|
+
}
|
|
1030
|
+
return this.selector;
|
|
1031
|
+
}
|
|
1032
|
+
async consume(handlers) {
|
|
1033
|
+
try {
|
|
1034
|
+
const result = await consumeProcessStream(this.reader, {
|
|
1035
|
+
onStart: (pid) => {
|
|
1036
|
+
this.resolvedPid = pid;
|
|
1037
|
+
this.pidResolve(pid);
|
|
1038
|
+
},
|
|
1039
|
+
onStdout: (chunk, raw) => {
|
|
1040
|
+
this.stdoutChunks.push(chunk);
|
|
1041
|
+
handlers.onStdout?.(chunk, raw);
|
|
1042
|
+
},
|
|
1043
|
+
onStderr: (chunk, raw) => {
|
|
1044
|
+
this.stderrChunks.push(chunk);
|
|
1045
|
+
handlers.onStderr?.(chunk, raw);
|
|
1046
|
+
}
|
|
1047
|
+
});
|
|
1048
|
+
if (this.resolvedPid === void 0) {
|
|
1049
|
+
throw new ExecTransportError("command stream ended before pid was reported");
|
|
1050
|
+
}
|
|
1051
|
+
const commandResult = {
|
|
1052
|
+
pid: this.resolvedPid,
|
|
1053
|
+
tag: extractTag(this.selector),
|
|
1054
|
+
stdout: this.stdout,
|
|
1055
|
+
stderr: this.stderr,
|
|
1056
|
+
exitCode: result.exitCode,
|
|
1057
|
+
status: result.status,
|
|
1058
|
+
error: result.error
|
|
1059
|
+
};
|
|
1060
|
+
handlers.onExit?.(commandResult);
|
|
1061
|
+
return commandResult;
|
|
1062
|
+
} catch (error) {
|
|
1063
|
+
this.pidReject(error);
|
|
1064
|
+
throw error;
|
|
1065
|
+
} finally {
|
|
1066
|
+
this.closed = true;
|
|
1067
|
+
await this.gateway.close().catch(() => void 0);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
};
|
|
1071
|
+
var PtySession = class {
|
|
1072
|
+
constructor(commands, gateway, reader, initialSelector, handlers = {}) {
|
|
1073
|
+
this.commands = commands;
|
|
1074
|
+
this.gateway = gateway;
|
|
1075
|
+
this.reader = reader;
|
|
1076
|
+
this.selector = initialSelector;
|
|
1077
|
+
this.pidPromise = new Promise((resolve, reject) => {
|
|
1078
|
+
this.pidResolve = resolve;
|
|
1079
|
+
this.pidReject = reject;
|
|
1080
|
+
});
|
|
1081
|
+
if (typeof initialSelector === "number") {
|
|
1082
|
+
this.resolvedPid = initialSelector;
|
|
1083
|
+
this.pidResolve(initialSelector);
|
|
1084
|
+
} else if (typeof initialSelector === "object" && "pid" in initialSelector) {
|
|
1085
|
+
this.resolvedPid = initialSelector.pid;
|
|
1086
|
+
this.pidResolve(initialSelector.pid);
|
|
1087
|
+
}
|
|
1088
|
+
this.resultPromise = this.consume(handlers);
|
|
1089
|
+
}
|
|
1090
|
+
commands;
|
|
1091
|
+
gateway;
|
|
1092
|
+
reader;
|
|
1093
|
+
selector;
|
|
1094
|
+
outputChunks = [];
|
|
1095
|
+
pidPromise;
|
|
1096
|
+
resultPromise;
|
|
1097
|
+
pidResolve;
|
|
1098
|
+
pidReject;
|
|
1099
|
+
resolvedPid;
|
|
1100
|
+
closed = false;
|
|
1101
|
+
get pid() {
|
|
1102
|
+
if (this.resolvedPid === void 0) {
|
|
1103
|
+
throw new Error("pty pid is not available yet");
|
|
1104
|
+
}
|
|
1105
|
+
return this.resolvedPid;
|
|
1106
|
+
}
|
|
1107
|
+
get output() {
|
|
1108
|
+
return this.outputChunks.join("");
|
|
1109
|
+
}
|
|
1110
|
+
async wait(options = {}) {
|
|
1111
|
+
const result = await this.resultPromise;
|
|
1112
|
+
if (options.rejectOnNonZero ?? true) {
|
|
1113
|
+
assertSuccessfulCommand({
|
|
1114
|
+
pid: result.pid,
|
|
1115
|
+
tag: result.tag,
|
|
1116
|
+
stdout: result.output,
|
|
1117
|
+
stderr: "",
|
|
1118
|
+
exitCode: result.exitCode,
|
|
1119
|
+
status: result.status,
|
|
1120
|
+
error: result.error
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1123
|
+
return result;
|
|
1124
|
+
}
|
|
1125
|
+
async sendInput(data, requestSignal) {
|
|
1126
|
+
await this.commands.sendPtyInput(this.currentSelector(), data, requestSignal);
|
|
1127
|
+
}
|
|
1128
|
+
async resize(cols, rows, requestSignal) {
|
|
1129
|
+
await this.commands.resizePty(this.currentSelector(), cols, rows, requestSignal);
|
|
1130
|
+
}
|
|
1131
|
+
async kill(signal = "SIGTERM", requestSignal) {
|
|
1132
|
+
await this.commands.kill(this.currentSelector(), signal, requestSignal);
|
|
1133
|
+
}
|
|
1134
|
+
async disconnect() {
|
|
1135
|
+
if (this.closed) {
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
this.closed = true;
|
|
1139
|
+
await this.reader.cancel("disconnected").catch(() => void 0);
|
|
1140
|
+
await this.gateway.close().catch(() => void 0);
|
|
1141
|
+
}
|
|
1142
|
+
async awaitPid() {
|
|
1143
|
+
return this.pidPromise;
|
|
1144
|
+
}
|
|
1145
|
+
currentSelector() {
|
|
1146
|
+
if (this.resolvedPid !== void 0) {
|
|
1147
|
+
return { pid: this.resolvedPid };
|
|
1148
|
+
}
|
|
1149
|
+
if (!this.selector) {
|
|
1150
|
+
throw new ExecTransportError("pty selector is not available until the process starts");
|
|
1151
|
+
}
|
|
1152
|
+
return this.selector;
|
|
1153
|
+
}
|
|
1154
|
+
async consume(handlers) {
|
|
1155
|
+
try {
|
|
1156
|
+
const result = await consumeProcessStream(this.reader, {
|
|
1157
|
+
onStart: (pid) => {
|
|
1158
|
+
this.resolvedPid = pid;
|
|
1159
|
+
this.pidResolve(pid);
|
|
1160
|
+
},
|
|
1161
|
+
onPty: (chunk, raw) => {
|
|
1162
|
+
this.outputChunks.push(chunk);
|
|
1163
|
+
handlers.onData?.(chunk, raw);
|
|
1164
|
+
}
|
|
1165
|
+
});
|
|
1166
|
+
if (this.resolvedPid === void 0) {
|
|
1167
|
+
throw new ExecTransportError("pty stream ended before pid was reported");
|
|
1168
|
+
}
|
|
1169
|
+
const ptyResult = {
|
|
1170
|
+
pid: this.resolvedPid,
|
|
1171
|
+
tag: extractTag(this.selector),
|
|
1172
|
+
output: this.output,
|
|
1173
|
+
exitCode: result.exitCode,
|
|
1174
|
+
status: result.status,
|
|
1175
|
+
error: result.error
|
|
1176
|
+
};
|
|
1177
|
+
handlers.onExit?.(ptyResult);
|
|
1178
|
+
return ptyResult;
|
|
1179
|
+
} catch (error) {
|
|
1180
|
+
this.pidReject(error);
|
|
1181
|
+
throw error;
|
|
1182
|
+
} finally {
|
|
1183
|
+
this.closed = true;
|
|
1184
|
+
await this.gateway.close().catch(() => void 0);
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
};
|
|
1188
|
+
var ComputerCommandsClient = class {
|
|
1189
|
+
constructor(computer) {
|
|
1190
|
+
this.computer = computer;
|
|
1191
|
+
}
|
|
1192
|
+
computer;
|
|
1193
|
+
async run(command, options = {}) {
|
|
1194
|
+
await this.computer.ensureRunning({ signal: options.signal });
|
|
1195
|
+
const response = await this.computer.internalSdk().internalClient().execCommand(
|
|
1196
|
+
this.computer.id,
|
|
1197
|
+
{
|
|
1198
|
+
command: normalizeCommand(command),
|
|
1199
|
+
cwd: options.cwd,
|
|
1200
|
+
env: options.env,
|
|
1201
|
+
timeout_ms: options.timeoutMs,
|
|
1202
|
+
user: options.user
|
|
1203
|
+
},
|
|
1204
|
+
options.signal
|
|
1205
|
+
);
|
|
1206
|
+
const result = commandResultFromExec(response);
|
|
1207
|
+
assertSuccessfulCommand(result);
|
|
1208
|
+
return result;
|
|
1209
|
+
}
|
|
1210
|
+
async start(command, options = {}) {
|
|
1211
|
+
const gateway = await this.createGateway({
|
|
1212
|
+
signal: options.signal,
|
|
1213
|
+
requestTimeoutMs: options.requestTimeoutMs
|
|
1214
|
+
});
|
|
1215
|
+
const reader = await gateway.openStream(
|
|
1216
|
+
"Start",
|
|
1217
|
+
{
|
|
1218
|
+
process: buildProcessConfig(command, options),
|
|
1219
|
+
stdin: options.stdin ?? true,
|
|
1220
|
+
tag: options.tag
|
|
1221
|
+
},
|
|
1222
|
+
options
|
|
1223
|
+
);
|
|
1224
|
+
const handle = new CommandHandle(
|
|
1225
|
+
this,
|
|
1226
|
+
gateway,
|
|
1227
|
+
reader,
|
|
1228
|
+
options.tag ? { tag: options.tag } : void 0,
|
|
1229
|
+
options
|
|
1230
|
+
);
|
|
1231
|
+
await handle.awaitPid();
|
|
1232
|
+
return handle;
|
|
1233
|
+
}
|
|
1234
|
+
async connect(selector, options = {}) {
|
|
1235
|
+
const gateway = await this.createGateway({
|
|
1236
|
+
signal: options.signal,
|
|
1237
|
+
requestTimeoutMs: options.requestTimeoutMs
|
|
1238
|
+
});
|
|
1239
|
+
const reader = await gateway.openStream(
|
|
1240
|
+
"Connect",
|
|
1241
|
+
{
|
|
1242
|
+
process: toProcessSelectorWire(selector)
|
|
1243
|
+
},
|
|
1244
|
+
options
|
|
1245
|
+
);
|
|
1246
|
+
const handle = new CommandHandle(this, gateway, reader, selector, options);
|
|
1247
|
+
await handle.awaitPid();
|
|
1248
|
+
return handle;
|
|
1249
|
+
}
|
|
1250
|
+
async list(signal) {
|
|
1251
|
+
const gateway = await this.createGateway({ signal });
|
|
1252
|
+
try {
|
|
1253
|
+
const response = await gateway.requestJson("List", {}, { signal });
|
|
1254
|
+
return (response.processes ?? []).map((process) => ({
|
|
1255
|
+
pid: process.pid ?? 0,
|
|
1256
|
+
tag: process.tag,
|
|
1257
|
+
cmd: process.config?.cmd ?? "",
|
|
1258
|
+
args: process.config?.args ?? [],
|
|
1259
|
+
envs: process.config?.envs ?? {},
|
|
1260
|
+
cwd: process.config?.cwd
|
|
1261
|
+
}));
|
|
1262
|
+
} finally {
|
|
1263
|
+
await gateway.close(signal).catch(() => void 0);
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
async sendStdin(selector, data, signal) {
|
|
1267
|
+
await this.sendProcessInput(selector, { stdin: bytesToBase64(encodeData(data)) }, signal);
|
|
1268
|
+
}
|
|
1269
|
+
async sendPtyInput(selector, data, signal) {
|
|
1270
|
+
await this.sendProcessInput(selector, { pty: bytesToBase64(encodeData(data)) }, signal);
|
|
1271
|
+
}
|
|
1272
|
+
async closeStdin(selector, signal) {
|
|
1273
|
+
const gateway = await this.createGateway({ signal });
|
|
1274
|
+
try {
|
|
1275
|
+
await gateway.requestJson("CloseStdin", { process: toProcessSelectorWire(selector) }, { signal });
|
|
1276
|
+
} finally {
|
|
1277
|
+
await gateway.close(signal).catch(() => void 0);
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
async kill(selector, signalName = "SIGTERM", signal) {
|
|
1281
|
+
const gateway = await this.createGateway({ signal });
|
|
1282
|
+
try {
|
|
1283
|
+
await gateway.requestJson(
|
|
1284
|
+
"SendSignal",
|
|
1285
|
+
{
|
|
1286
|
+
process: toProcessSelectorWire(selector),
|
|
1287
|
+
signal: signalName === "SIGKILL" ? "SIGNAL_SIGKILL" : "SIGNAL_SIGTERM"
|
|
1288
|
+
},
|
|
1289
|
+
{ signal }
|
|
1290
|
+
);
|
|
1291
|
+
} finally {
|
|
1292
|
+
await gateway.close(signal).catch(() => void 0);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
async resizePty(selector, cols, rows, signal) {
|
|
1296
|
+
const gateway = await this.createGateway({ signal });
|
|
1297
|
+
try {
|
|
1298
|
+
await gateway.requestJson(
|
|
1299
|
+
"Update",
|
|
1300
|
+
{
|
|
1301
|
+
process: toProcessSelectorWire(selector),
|
|
1302
|
+
pty: { size: { cols, rows } }
|
|
1303
|
+
},
|
|
1304
|
+
{ signal }
|
|
1305
|
+
);
|
|
1306
|
+
} finally {
|
|
1307
|
+
await gateway.close(signal).catch(() => void 0);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
async sendProcessInput(selector, input, signal) {
|
|
1311
|
+
const gateway = await this.createGateway({ signal });
|
|
1312
|
+
try {
|
|
1313
|
+
await gateway.requestJson(
|
|
1314
|
+
"SendInput",
|
|
1315
|
+
{
|
|
1316
|
+
process: toProcessSelectorWire(selector),
|
|
1317
|
+
input
|
|
1318
|
+
},
|
|
1319
|
+
{ signal }
|
|
1320
|
+
);
|
|
1321
|
+
} finally {
|
|
1322
|
+
await gateway.close(signal).catch(() => void 0);
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
/** @internal */
|
|
1326
|
+
async createGateway(options) {
|
|
1327
|
+
await this.computer.ensureRunning({ signal: options.signal });
|
|
1328
|
+
const response = await this.computer.internalSdk().internalClient().createAccessSession(
|
|
1329
|
+
this.computer.id,
|
|
1330
|
+
{ kind: "exec" },
|
|
1331
|
+
options.signal
|
|
1332
|
+
);
|
|
1333
|
+
return new ExecGatewayConnection(
|
|
1334
|
+
this.computer.internalSdk().internalClient(),
|
|
1335
|
+
this.computer.internalSdk().internalFetch(),
|
|
1336
|
+
this.computer.id,
|
|
1337
|
+
response.session
|
|
1338
|
+
);
|
|
1339
|
+
}
|
|
1340
|
+
};
|
|
1341
|
+
var ComputerPtyClient = class {
|
|
1342
|
+
constructor(computer) {
|
|
1343
|
+
this.computer = computer;
|
|
1344
|
+
}
|
|
1345
|
+
computer;
|
|
1346
|
+
async create(options = {}) {
|
|
1347
|
+
return this.start(["sh"], options);
|
|
1348
|
+
}
|
|
1349
|
+
async start(command, options = {}) {
|
|
1350
|
+
const gateway = await this.computer.commands.createGateway({
|
|
1351
|
+
signal: options.signal,
|
|
1352
|
+
requestTimeoutMs: options.requestTimeoutMs
|
|
1353
|
+
});
|
|
1354
|
+
const reader = await gateway.openStream(
|
|
1355
|
+
"Start",
|
|
1356
|
+
{
|
|
1357
|
+
process: buildProcessConfig(command, options),
|
|
1358
|
+
pty: {
|
|
1359
|
+
size: {
|
|
1360
|
+
cols: options.cols ?? 80,
|
|
1361
|
+
rows: options.rows ?? 24
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
},
|
|
1365
|
+
options
|
|
1366
|
+
);
|
|
1367
|
+
const session = new PtySession(
|
|
1368
|
+
this.computer.commands,
|
|
1369
|
+
gateway,
|
|
1370
|
+
reader,
|
|
1371
|
+
options.tag ? { tag: options.tag } : void 0,
|
|
1372
|
+
options
|
|
1373
|
+
);
|
|
1374
|
+
await session.awaitPid();
|
|
1375
|
+
return session;
|
|
1376
|
+
}
|
|
1377
|
+
async attach(selector, options = {}) {
|
|
1378
|
+
return this.connect(selector, options);
|
|
1379
|
+
}
|
|
1380
|
+
async connect(selector, options = {}) {
|
|
1381
|
+
const gateway = await this.computer.commands.createGateway({
|
|
1382
|
+
signal: options.signal,
|
|
1383
|
+
requestTimeoutMs: options.requestTimeoutMs
|
|
1384
|
+
});
|
|
1385
|
+
const reader = await gateway.openStream(
|
|
1386
|
+
"Connect",
|
|
1387
|
+
{ process: toProcessSelectorWire(selector) },
|
|
1388
|
+
options
|
|
1389
|
+
);
|
|
1390
|
+
const session = new PtySession(this.computer.commands, gateway, reader, selector, options);
|
|
1391
|
+
await session.awaitPid();
|
|
1392
|
+
return session;
|
|
1393
|
+
}
|
|
1394
|
+
};
|
|
1395
|
+
var ComputerFilesClient = class {
|
|
1396
|
+
constructor(computer) {
|
|
1397
|
+
this.computer = computer;
|
|
1398
|
+
void computer;
|
|
1399
|
+
}
|
|
1400
|
+
computer;
|
|
1401
|
+
async readText(path, signal) {
|
|
1402
|
+
await this.computer.requireCapability("files", signal);
|
|
1403
|
+
throw new ExecTransportError("files capability is advertised but the SDK file transport is not implemented yet", {
|
|
1404
|
+
code: "files_transport_unimplemented",
|
|
1405
|
+
details: { computerId: this.computer.id, path }
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1408
|
+
async writeText(path, content, signal) {
|
|
1409
|
+
await this.computer.requireCapability("files", signal);
|
|
1410
|
+
throw new ExecTransportError("files capability is advertised but the SDK file transport is not implemented yet", {
|
|
1411
|
+
code: "files_transport_unimplemented",
|
|
1412
|
+
details: { computerId: this.computer.id, path, size: content.length }
|
|
1413
|
+
});
|
|
1414
|
+
}
|
|
1415
|
+
async remove(path, signal) {
|
|
1416
|
+
await this.computer.requireCapability("files", signal);
|
|
1417
|
+
throw new ExecTransportError("files capability is advertised but the SDK file transport is not implemented yet", {
|
|
1418
|
+
code: "files_transport_unimplemented",
|
|
1419
|
+
details: { computerId: this.computer.id, path }
|
|
1420
|
+
});
|
|
1421
|
+
}
|
|
1422
|
+
};
|
|
1423
|
+
var ComputerGitClient = class {
|
|
1424
|
+
constructor(computer) {
|
|
1425
|
+
this.computer = computer;
|
|
1426
|
+
void computer;
|
|
1427
|
+
}
|
|
1428
|
+
computer;
|
|
1429
|
+
async status(signal) {
|
|
1430
|
+
await this.computer.requireCapability("git", signal);
|
|
1431
|
+
throw new ExecTransportError("git capability is advertised but the SDK git transport is not implemented yet", {
|
|
1432
|
+
code: "git_transport_unimplemented",
|
|
1433
|
+
details: { computerId: this.computer.id }
|
|
1434
|
+
});
|
|
1435
|
+
}
|
|
1436
|
+
async clone(repoUrl, signal) {
|
|
1437
|
+
await this.computer.requireCapability("git", signal);
|
|
1438
|
+
throw new ExecTransportError("git capability is advertised but the SDK git transport is not implemented yet", {
|
|
1439
|
+
code: "git_transport_unimplemented",
|
|
1440
|
+
details: { computerId: this.computer.id, repoUrl }
|
|
1441
|
+
});
|
|
1442
|
+
}
|
|
1443
|
+
async pull(signal) {
|
|
1444
|
+
await this.computer.requireCapability("git", signal);
|
|
1445
|
+
throw new ExecTransportError("git capability is advertised but the SDK git transport is not implemented yet", {
|
|
1446
|
+
code: "git_transport_unimplemented",
|
|
1447
|
+
details: { computerId: this.computer.id }
|
|
1448
|
+
});
|
|
1449
|
+
}
|
|
1450
|
+
};
|
|
1451
|
+
var ComputerPortsClient = class {
|
|
1452
|
+
constructor(computer) {
|
|
1453
|
+
this.computer = computer;
|
|
1454
|
+
}
|
|
1455
|
+
computer;
|
|
1456
|
+
async list(signal) {
|
|
1457
|
+
const response = await this.computer.internalSdk().internalClient().listPublishedPorts(
|
|
1458
|
+
this.computer.id,
|
|
1459
|
+
signal
|
|
1460
|
+
);
|
|
1461
|
+
return response.ports;
|
|
1462
|
+
}
|
|
1463
|
+
async publish(options) {
|
|
1464
|
+
const response = await this.computer.internalSdk().internalClient().createPublishedPort(
|
|
1465
|
+
this.computer.id,
|
|
1466
|
+
{
|
|
1467
|
+
port: options.port,
|
|
1468
|
+
name: options.name
|
|
1469
|
+
},
|
|
1470
|
+
options.signal
|
|
1471
|
+
);
|
|
1472
|
+
await waitForOperationIfPresent(this.computer.internalSdk(), response.operation, options.signal);
|
|
1473
|
+
return response.port;
|
|
1474
|
+
}
|
|
1475
|
+
async delete(port, signal) {
|
|
1476
|
+
const response = await this.computer.internalSdk().internalClient().deletePublishedPort(
|
|
1477
|
+
this.computer.id,
|
|
1478
|
+
port,
|
|
1479
|
+
signal
|
|
1480
|
+
);
|
|
1481
|
+
await waitForOperationIfPresent(this.computer.internalSdk(), response.operation, signal);
|
|
1482
|
+
return response.port;
|
|
1483
|
+
}
|
|
1484
|
+
};
|
|
1485
|
+
var ComputerSharesClient = class {
|
|
1486
|
+
constructor(computer) {
|
|
1487
|
+
this.computer = computer;
|
|
1488
|
+
}
|
|
1489
|
+
computer;
|
|
1490
|
+
async list(signal) {
|
|
1491
|
+
const response = await this.computer.internalSdk().internalClient().listShares(
|
|
1492
|
+
this.computer.id,
|
|
1493
|
+
signal
|
|
1494
|
+
);
|
|
1495
|
+
return response.shares;
|
|
1496
|
+
}
|
|
1497
|
+
async createLink(options = {}) {
|
|
1498
|
+
const response = await this.computer.internalSdk().internalClient().createLinkShare(
|
|
1499
|
+
this.computer.id,
|
|
1500
|
+
{
|
|
1501
|
+
expires_at: options.expiresAt,
|
|
1502
|
+
allow_browser: options.allowBrowser,
|
|
1503
|
+
allow_vnc: options.allowVnc
|
|
1504
|
+
},
|
|
1505
|
+
options.signal
|
|
1506
|
+
);
|
|
1507
|
+
return response.share;
|
|
1508
|
+
}
|
|
1509
|
+
async createEmail(options) {
|
|
1510
|
+
const response = await this.computer.internalSdk().internalClient().createEmailShare(
|
|
1511
|
+
this.computer.id,
|
|
1512
|
+
{
|
|
1513
|
+
email: options.email,
|
|
1514
|
+
expires_at: options.expiresAt,
|
|
1515
|
+
allow_browser: options.allowBrowser,
|
|
1516
|
+
allow_vnc: options.allowVnc
|
|
1517
|
+
},
|
|
1518
|
+
options.signal
|
|
1519
|
+
);
|
|
1520
|
+
return response.share;
|
|
1521
|
+
}
|
|
1522
|
+
async delete(shareId, signal) {
|
|
1523
|
+
const shares = await this.list(signal);
|
|
1524
|
+
const existing = shares.find((share) => share.id === shareId);
|
|
1525
|
+
if (!existing) {
|
|
1526
|
+
throw new PublicApiError(404, `share ${shareId} not found`, "share_not_found");
|
|
1527
|
+
}
|
|
1528
|
+
if (existing.kind === "email") {
|
|
1529
|
+
await this.computer.internalSdk().internalClient().deleteEmailShare(this.computer.id, shareId, signal);
|
|
1530
|
+
return;
|
|
1531
|
+
}
|
|
1532
|
+
await this.computer.internalSdk().internalClient().deleteLinkShare(this.computer.id, shareId, signal);
|
|
1533
|
+
}
|
|
1534
|
+
};
|
|
1535
|
+
var ComputerSnapshotsClient = class {
|
|
1536
|
+
constructor(computer) {
|
|
1537
|
+
this.computer = computer;
|
|
1538
|
+
}
|
|
1539
|
+
computer;
|
|
1540
|
+
async list(signal) {
|
|
1541
|
+
const response = await this.computer.internalSdk().internalClient().listSnapshots(
|
|
1542
|
+
this.computer.id,
|
|
1543
|
+
signal
|
|
1544
|
+
);
|
|
1545
|
+
return response.snapshots;
|
|
1546
|
+
}
|
|
1547
|
+
async create(signal) {
|
|
1548
|
+
const response = await this.computer.internalSdk().internalClient().createSnapshot(
|
|
1549
|
+
this.computer.id,
|
|
1550
|
+
signal
|
|
1551
|
+
);
|
|
1552
|
+
await waitForOperationIfPresent(this.computer.internalSdk(), response.operation, signal);
|
|
1553
|
+
const snapshot = await this.computer.internalSdk().internalClient().getSnapshot(
|
|
1554
|
+
response.snapshot.id,
|
|
1555
|
+
signal
|
|
1556
|
+
);
|
|
1557
|
+
return snapshot.snapshot;
|
|
1558
|
+
}
|
|
1559
|
+
};
|
|
1560
|
+
var SnapshotsClient = class {
|
|
1561
|
+
constructor(sdk) {
|
|
1562
|
+
this.sdk = sdk;
|
|
1563
|
+
}
|
|
1564
|
+
sdk;
|
|
1565
|
+
async get(snapshotId, signal) {
|
|
1566
|
+
const response = await this.sdk.internalClient().getSnapshot(snapshotId, signal);
|
|
1567
|
+
return response.snapshot;
|
|
1568
|
+
}
|
|
1569
|
+
async delete(snapshotId, signal) {
|
|
1570
|
+
const response = await this.sdk.internalClient().deleteSnapshot(snapshotId, signal);
|
|
1571
|
+
await waitForOperationIfPresent(this.sdk, response.operation, signal);
|
|
1572
|
+
return response.snapshot;
|
|
1573
|
+
}
|
|
1574
|
+
async restore(snapshotId, options = {}) {
|
|
1575
|
+
const response = await this.sdk.internalClient().restoreSnapshot(
|
|
1576
|
+
snapshotId,
|
|
1577
|
+
{
|
|
1578
|
+
handle: options.handle,
|
|
1579
|
+
display_name: options.displayName,
|
|
1580
|
+
guest: options.authorizedKeys && options.authorizedKeys.length > 0 ? { authorized_keys: options.authorizedKeys } : void 0
|
|
1581
|
+
},
|
|
1582
|
+
options.signal
|
|
1583
|
+
);
|
|
1584
|
+
await waitForOperationIfPresent(this.sdk, response.operation, options.signal);
|
|
1585
|
+
return new Computer(this.sdk, response.computer);
|
|
1586
|
+
}
|
|
1587
|
+
};
|
|
1588
|
+
var SSHKeysClient = class {
|
|
1589
|
+
constructor(sdk) {
|
|
1590
|
+
this.sdk = sdk;
|
|
1591
|
+
}
|
|
1592
|
+
sdk;
|
|
1593
|
+
async list(signal) {
|
|
1594
|
+
const response = await this.sdk.internalClient().listSSHKeys(signal);
|
|
1595
|
+
return response.ssh_keys;
|
|
1596
|
+
}
|
|
1597
|
+
async create(body, signal) {
|
|
1598
|
+
const response = await this.sdk.internalClient().createSSHKey(body, signal);
|
|
1599
|
+
return response.ssh_key;
|
|
1600
|
+
}
|
|
1601
|
+
async delete(sshKeyId, signal) {
|
|
1602
|
+
await this.sdk.internalClient().deleteSSHKey(sshKeyId, signal);
|
|
1603
|
+
}
|
|
1604
|
+
};
|
|
1605
|
+
function normalizeCommand(command) {
|
|
1606
|
+
if (typeof command === "string") {
|
|
1607
|
+
const trimmed = command.trim();
|
|
1608
|
+
if (!trimmed) {
|
|
1609
|
+
throw new Error("command must not be empty");
|
|
1610
|
+
}
|
|
1611
|
+
return ["sh", "-lc", trimmed];
|
|
1612
|
+
}
|
|
1613
|
+
if (command.length === 0) {
|
|
1614
|
+
throw new Error("command must not be empty");
|
|
1615
|
+
}
|
|
1616
|
+
return [...command];
|
|
1617
|
+
}
|
|
1618
|
+
function buildProcessConfig(command, options) {
|
|
1619
|
+
if (options.user) {
|
|
1620
|
+
throw new ExecTransportError("exec gateway processes do not yet support user overrides");
|
|
1621
|
+
}
|
|
1622
|
+
const [cmd, ...args] = normalizeCommand(command);
|
|
1623
|
+
return {
|
|
1624
|
+
cmd,
|
|
1625
|
+
args,
|
|
1626
|
+
cwd: options.cwd,
|
|
1627
|
+
envs: options.env
|
|
1628
|
+
};
|
|
1629
|
+
}
|
|
1630
|
+
async function waitForOperationIfPresent(sdk, operation, signal) {
|
|
1631
|
+
if (!operation?.id) {
|
|
1632
|
+
return;
|
|
1633
|
+
}
|
|
1634
|
+
await sdk.waitForOperation(operation.id, { signal });
|
|
1635
|
+
}
|
|
1636
|
+
function commandResultFromExec(response) {
|
|
1637
|
+
return {
|
|
1638
|
+
pid: 0,
|
|
1639
|
+
stdout: response.stdout,
|
|
1640
|
+
stderr: response.stderr,
|
|
1641
|
+
exitCode: response.exit_code,
|
|
1642
|
+
status: response.exit_code === 0 ? "exited" : "failed"
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
function assertSuccessfulCommand(result) {
|
|
1646
|
+
if (result.exitCode !== 0) {
|
|
1647
|
+
throw new CommandExitError(result);
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
function toProcessSelectorWire(selector) {
|
|
1651
|
+
if (typeof selector === "number") {
|
|
1652
|
+
return { pid: selector };
|
|
1653
|
+
}
|
|
1654
|
+
if (typeof selector === "string") {
|
|
1655
|
+
return { tag: selector };
|
|
1656
|
+
}
|
|
1657
|
+
if ("pid" in selector) {
|
|
1658
|
+
return { pid: selector.pid };
|
|
1659
|
+
}
|
|
1660
|
+
return { tag: selector.tag };
|
|
1661
|
+
}
|
|
1662
|
+
function matchesCommandSelector(command, selector) {
|
|
1663
|
+
if (typeof selector === "number") {
|
|
1664
|
+
return command.pid === selector;
|
|
1665
|
+
}
|
|
1666
|
+
if (typeof selector === "string") {
|
|
1667
|
+
return command.tag === selector;
|
|
1668
|
+
}
|
|
1669
|
+
if ("pid" in selector) {
|
|
1670
|
+
return command.pid === selector.pid;
|
|
1671
|
+
}
|
|
1672
|
+
return command.tag === selector.tag;
|
|
1673
|
+
}
|
|
1674
|
+
function extractTag(selector) {
|
|
1675
|
+
if (typeof selector === "string") {
|
|
1676
|
+
return selector;
|
|
1677
|
+
}
|
|
1678
|
+
if (typeof selector === "object" && "tag" in selector) {
|
|
1679
|
+
return selector.tag;
|
|
1680
|
+
}
|
|
1681
|
+
return void 0;
|
|
1682
|
+
}
|
|
1683
|
+
function shellQuote(value) {
|
|
1684
|
+
return `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
1685
|
+
}
|
|
1686
|
+
function encodeData(data) {
|
|
1687
|
+
return typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
1688
|
+
}
|
|
1689
|
+
function bytesToBase64(bytes) {
|
|
1690
|
+
let binary = "";
|
|
1691
|
+
for (const byte of bytes) {
|
|
1692
|
+
binary += String.fromCharCode(byte);
|
|
1693
|
+
}
|
|
1694
|
+
return btoa(binary);
|
|
1695
|
+
}
|
|
1696
|
+
function base64ToBytes(value) {
|
|
1697
|
+
const binary = atob(value);
|
|
1698
|
+
const bytes = new Uint8Array(binary.length);
|
|
1699
|
+
for (let index = 0; index < binary.length; index += 1) {
|
|
1700
|
+
bytes[index] = binary.charCodeAt(index);
|
|
1701
|
+
}
|
|
1702
|
+
return bytes;
|
|
1703
|
+
}
|
|
1704
|
+
function createTimedSignal(parentSignal, timeoutMs) {
|
|
1705
|
+
if (!parentSignal && (!timeoutMs || timeoutMs <= 0)) {
|
|
1706
|
+
return { signal: void 0, clear: () => {
|
|
1707
|
+
} };
|
|
1708
|
+
}
|
|
1709
|
+
const controller = new AbortController();
|
|
1710
|
+
let timer;
|
|
1711
|
+
const abortFromParent = () => {
|
|
1712
|
+
controller.abort(parentSignal?.reason);
|
|
1713
|
+
};
|
|
1714
|
+
if (parentSignal) {
|
|
1715
|
+
if (parentSignal.aborted) {
|
|
1716
|
+
controller.abort(parentSignal.reason);
|
|
1717
|
+
} else {
|
|
1718
|
+
parentSignal.addEventListener("abort", abortFromParent, { once: true });
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
if (timeoutMs && timeoutMs > 0) {
|
|
1722
|
+
timer = setTimeout(() => {
|
|
1723
|
+
controller.abort(new DOMException("Timed out", "TimeoutError"));
|
|
1724
|
+
}, timeoutMs);
|
|
1725
|
+
}
|
|
1726
|
+
return {
|
|
1727
|
+
signal: controller.signal,
|
|
1728
|
+
clear: () => {
|
|
1729
|
+
if (timer) {
|
|
1730
|
+
clearTimeout(timer);
|
|
1731
|
+
}
|
|
1732
|
+
if (parentSignal) {
|
|
1733
|
+
parentSignal.removeEventListener("abort", abortFromParent);
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
};
|
|
1737
|
+
}
|
|
1738
|
+
async function consumeProcessStream(reader, handlers) {
|
|
1739
|
+
let buffered = new Uint8Array(0);
|
|
1740
|
+
while (true) {
|
|
1741
|
+
const { done, value } = await reader.read();
|
|
1742
|
+
if (done) {
|
|
1743
|
+
break;
|
|
1744
|
+
}
|
|
1745
|
+
buffered = concatBytes(buffered, value);
|
|
1746
|
+
while (buffered.length >= 5) {
|
|
1747
|
+
const flags = buffered[0];
|
|
1748
|
+
const messageLength = buffered[1] << 24 >>> 0 | buffered[2] << 16 | buffered[3] << 8 | buffered[4];
|
|
1749
|
+
const frameLength = 5 + messageLength;
|
|
1750
|
+
if (buffered.length < frameLength) {
|
|
1751
|
+
break;
|
|
1752
|
+
}
|
|
1753
|
+
const payload = buffered.slice(5, frameLength);
|
|
1754
|
+
buffered = buffered.slice(frameLength);
|
|
1755
|
+
if (flags & 1) {
|
|
1756
|
+
throw new ExecTransportError("compressed connect stream messages are not supported");
|
|
1757
|
+
}
|
|
1758
|
+
const text = new TextDecoder().decode(payload);
|
|
1759
|
+
const message = text ? JSON.parse(text) : {};
|
|
1760
|
+
if (flags & 2) {
|
|
1761
|
+
const endError = typeof message === "object" && message !== null ? message.error : void 0;
|
|
1762
|
+
if (endError?.message) {
|
|
1763
|
+
throw new ExecTransportError(endError.message, {
|
|
1764
|
+
code: endError.code,
|
|
1765
|
+
details: endError.details
|
|
1766
|
+
});
|
|
1767
|
+
}
|
|
1768
|
+
continue;
|
|
1769
|
+
}
|
|
1770
|
+
const event = message.event;
|
|
1771
|
+
if (!event) {
|
|
1772
|
+
continue;
|
|
1773
|
+
}
|
|
1774
|
+
if (event.start?.pid !== void 0) {
|
|
1775
|
+
handlers.onStart?.(event.start.pid);
|
|
1776
|
+
continue;
|
|
1777
|
+
}
|
|
1778
|
+
if (event.data?.stdout) {
|
|
1779
|
+
const raw = base64ToBytes(event.data.stdout);
|
|
1780
|
+
handlers.onStdout?.(new TextDecoder().decode(raw), raw);
|
|
1781
|
+
continue;
|
|
1782
|
+
}
|
|
1783
|
+
if (event.data?.stderr) {
|
|
1784
|
+
const raw = base64ToBytes(event.data.stderr);
|
|
1785
|
+
handlers.onStderr?.(new TextDecoder().decode(raw), raw);
|
|
1786
|
+
continue;
|
|
1787
|
+
}
|
|
1788
|
+
if (event.data?.pty) {
|
|
1789
|
+
const raw = base64ToBytes(event.data.pty);
|
|
1790
|
+
handlers.onPty?.(new TextDecoder().decode(raw), raw);
|
|
1791
|
+
continue;
|
|
1792
|
+
}
|
|
1793
|
+
if (event.end) {
|
|
1794
|
+
return {
|
|
1795
|
+
exitCode: event.end.exitCode ?? event.end.exit_code ?? 0,
|
|
1796
|
+
status: event.end.status || "exited",
|
|
1797
|
+
error: event.end.error
|
|
1798
|
+
};
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
throw new ExecTransportError("exec stream ended without a process end event");
|
|
1803
|
+
}
|
|
1804
|
+
function concatBytes(left, right) {
|
|
1805
|
+
const merged = new Uint8Array(left.length + right.length);
|
|
1806
|
+
merged.set(left);
|
|
1807
|
+
merged.set(right, left.length);
|
|
1808
|
+
return merged;
|
|
1809
|
+
}
|
|
1810
|
+
async function responseToExecTransportError(response) {
|
|
1811
|
+
const contentType = response.headers.get("content-type") || "";
|
|
1812
|
+
if (contentType.includes("application/json")) {
|
|
1813
|
+
try {
|
|
1814
|
+
const payload = await response.json();
|
|
1815
|
+
const nestedError = typeof payload === "object" && payload !== null && "error" in payload ? payload.error : void 0;
|
|
1816
|
+
const code = nestedError?.code || (typeof payload === "object" && payload !== null && "code" in payload ? payload.code : void 0);
|
|
1817
|
+
const message = nestedError?.message || (typeof payload === "object" && payload !== null && "message" in payload ? payload.message : response.statusText || "exec request failed");
|
|
1818
|
+
const details = nestedError?.details ?? payload;
|
|
1819
|
+
return new ExecTransportError(message || "exec request failed", {
|
|
1820
|
+
status: response.status,
|
|
1821
|
+
code,
|
|
1822
|
+
details
|
|
1823
|
+
});
|
|
1824
|
+
} catch (error) {
|
|
1825
|
+
return new ExecTransportError(response.statusText || "exec request failed", {
|
|
1826
|
+
status: response.status,
|
|
1827
|
+
details: error
|
|
1828
|
+
});
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
const body = await response.text().catch(() => "");
|
|
1832
|
+
return new ExecTransportError(body || response.statusText || "exec request failed", {
|
|
1833
|
+
status: response.status
|
|
1834
|
+
});
|
|
1835
|
+
}
|
|
1836
|
+
export {
|
|
1837
|
+
AgentComputer,
|
|
1838
|
+
CommandExitError,
|
|
1839
|
+
CommandHandle,
|
|
1840
|
+
Computer,
|
|
1841
|
+
ComputerCommandsClient,
|
|
1842
|
+
ComputerFilesClient,
|
|
1843
|
+
ComputerGitClient,
|
|
1844
|
+
ComputerImagesClient,
|
|
1845
|
+
ComputerPortsClient,
|
|
1846
|
+
ComputerPtyClient,
|
|
1847
|
+
ComputerSharesClient,
|
|
1848
|
+
ComputerSnapshotsClient,
|
|
1849
|
+
ComputersClient,
|
|
1850
|
+
ExecTransportError,
|
|
1851
|
+
PtySession,
|
|
1852
|
+
PublicApiError,
|
|
1853
|
+
SSHKeysClient,
|
|
1854
|
+
SnapshotsClient,
|
|
1855
|
+
UnsupportedCapabilityError
|
|
1856
|
+
};
|