@arker-ai/sdk 0.3.0 → 0.5.1
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 +40 -125
- package/dist/chunk-PI3H3TGC.js +697 -0
- package/dist/cli.cjs +1316 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +606 -0
- package/dist/index.cjs +321 -103
- package/dist/index.d.cts +470 -206
- package/dist/index.d.ts +470 -206
- package/dist/index.js +9 -473
- package/package.json +17 -4
package/dist/index.cjs
CHANGED
|
@@ -20,14 +20,31 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
ARKER_ORG_ID: () => ARKER_ORG_ID,
|
|
23
24
|
Arker: () => Arker,
|
|
24
25
|
ArkerError: () => ArkerError,
|
|
25
26
|
CHUNK_SIZE: () => CHUNK_SIZE,
|
|
26
|
-
|
|
27
|
-
Sync: () => Sync
|
|
27
|
+
VM: () => VM
|
|
28
28
|
});
|
|
29
29
|
module.exports = __toCommonJS(index_exports);
|
|
30
30
|
var CHUNK_SIZE = 4 * 1024 * 1024;
|
|
31
|
+
var ARKER_ORG_ID = "ArkerHQ";
|
|
32
|
+
var GOLDEN_NAMES = /* @__PURE__ */ new Set([
|
|
33
|
+
"arkuntu",
|
|
34
|
+
"ubuntu",
|
|
35
|
+
"ubuntu-small",
|
|
36
|
+
"ubuntu-nodisk",
|
|
37
|
+
"ubuntu-nonet-nodisk",
|
|
38
|
+
"ubuntu-full",
|
|
39
|
+
"ubuntu-full-32",
|
|
40
|
+
"ubuntu-py-repl",
|
|
41
|
+
"ubuntu-js-repl",
|
|
42
|
+
"ubuntu-docker",
|
|
43
|
+
"ubuntu-chromium",
|
|
44
|
+
"ubuntu-servo",
|
|
45
|
+
"ubuntu-servo-js-repl",
|
|
46
|
+
"ubuntu-chromium-js-repl"
|
|
47
|
+
]);
|
|
31
48
|
var DEFAULT_RETRY_ATTEMPTS = 4;
|
|
32
49
|
var DEFAULT_RETRY_BASE_DELAY_MS = 200;
|
|
33
50
|
var DEFAULT_RETRY_MAX_DELAY_MS = 2e3;
|
|
@@ -38,6 +55,9 @@ var RETRYABLE_CODES = /* @__PURE__ */ new Set(["routing_unavailable", "unavailab
|
|
|
38
55
|
var TRANSIENT_HINTS = ["503", "Service Unavailable", "throttle", "SlowDown", "ThrottlingException"];
|
|
39
56
|
var ULID_ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
40
57
|
var DEFAULT_REGION_ENV = "ARKER_REGION";
|
|
58
|
+
var DEFAULT_PROVIDER_ENV = "ARKER_PROVIDER";
|
|
59
|
+
var DEFAULT_PROVIDER = "aws";
|
|
60
|
+
var DEFAULT_CONTROL_BASE_URL = "https://arker.ai/api";
|
|
41
61
|
var BURST_SOURCE_REFS = /* @__PURE__ */ new Set(["arkuntu"]);
|
|
42
62
|
var BURST_VM_ID = /^[0-9A-HJKMNP-TV-Z]{26}_[A-Za-z0-9]+$/;
|
|
43
63
|
var ArkerError = class extends Error {
|
|
@@ -51,39 +71,152 @@ var ArkerError = class extends Error {
|
|
|
51
71
|
}
|
|
52
72
|
};
|
|
53
73
|
var Arker = class {
|
|
74
|
+
/** Compute base URL for `provider` + `region` — used for fork/run/
|
|
75
|
+
* per-VM ops. SDK calls go straight to this host, skipping the CF
|
|
76
|
+
* Worker control plane. */
|
|
54
77
|
baseUrl;
|
|
78
|
+
/** Compute base URL for the burst provider in this region. */
|
|
55
79
|
burstBaseUrl;
|
|
80
|
+
/** CF Worker control-plane URL — used for cross-cutting admin calls
|
|
81
|
+
* like list-VMs and filesystems. */
|
|
82
|
+
controlBaseUrl;
|
|
56
83
|
region;
|
|
84
|
+
provider;
|
|
57
85
|
apiKey;
|
|
58
86
|
fetchImpl;
|
|
59
87
|
retry;
|
|
60
88
|
constructor(opts = {}) {
|
|
61
89
|
const apiKey = opts.apiKey ?? env("ARKER_API_KEY") ?? env("AUTH_KEY");
|
|
62
90
|
const explicitBaseUrl = opts.baseUrl ?? env("ARKER_BASE_URL");
|
|
63
|
-
const
|
|
64
|
-
const
|
|
65
|
-
const
|
|
91
|
+
const rawRegion = opts.region ?? (explicitBaseUrl ? void 0 : env(DEFAULT_REGION_ENV));
|
|
92
|
+
const rawProvider = opts.provider ?? env(DEFAULT_PROVIDER_ENV) ?? DEFAULT_PROVIDER;
|
|
93
|
+
const provider = parseProvider(rawProvider);
|
|
94
|
+
const { region, providerFromRegion } = splitRegion(rawRegion);
|
|
95
|
+
const effectiveProvider = providerFromRegion ?? provider;
|
|
96
|
+
const baseUrl = explicitBaseUrl ?? (region ? computeBaseUrl(effectiveProvider, region) : void 0);
|
|
97
|
+
const burstBaseUrl = opts.burstBaseUrl ?? env("ARKER_BURST_BASE_URL") ?? (region ? computeBaseUrl("aws-burst", region) : void 0);
|
|
98
|
+
const controlBaseUrl = opts.controlBaseUrl ?? env("ARKER_CONTROL_BASE_URL") ?? DEFAULT_CONTROL_BASE_URL;
|
|
66
99
|
if (!apiKey) throw new Error("apiKey is required; pass apiKey or set ARKER_API_KEY");
|
|
67
100
|
if (!baseUrl) throw new Error("region or baseUrl is required; pass region, baseUrl, ARKER_REGION, or ARKER_BASE_URL");
|
|
68
101
|
this.apiKey = apiKey;
|
|
69
102
|
this.baseUrl = normalizeBaseUrl(baseUrl);
|
|
70
103
|
this.burstBaseUrl = burstBaseUrl ? normalizeBaseUrl(burstBaseUrl) : void 0;
|
|
104
|
+
this.controlBaseUrl = normalizeBaseUrl(controlBaseUrl);
|
|
71
105
|
this.region = region ? normalizeRegion(region) : void 0;
|
|
106
|
+
this.provider = effectiveProvider;
|
|
72
107
|
this.fetchImpl = opts.fetch ?? globalThis.fetch;
|
|
73
108
|
this.retry = normalizeRetry(opts.retry);
|
|
74
109
|
if (!this.fetchImpl) throw new Error("fetch is required in this runtime");
|
|
75
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Address an existing VM. Doesn't make any network calls; returns a
|
|
113
|
+
* lightweight handle.
|
|
114
|
+
*/
|
|
76
115
|
vm(vmId) {
|
|
77
|
-
return new
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
116
|
+
return new VM(this, vmId, this._baseUrlFor(vmId));
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Create a new VM by forking from a source.
|
|
120
|
+
*
|
|
121
|
+
* fork("ubuntu-full") // public golden by name
|
|
122
|
+
* fork("base") // a VM by name in your org
|
|
123
|
+
* fork(vm) // an existing VM (uses its id)
|
|
124
|
+
* fork({ sourceVmId: "vm_abc..." })
|
|
125
|
+
* fork({ sourceVmName: "base", sourceOrgId: "org_..." })
|
|
126
|
+
*
|
|
127
|
+
* The source can be a name string, a `VM` handle, or a `ForkSource`
|
|
128
|
+
* object. `sourceOrgId` defaults to the Arker org when `sourceVmName`
|
|
129
|
+
* is a known public golden, otherwise to your own org; an explicit
|
|
130
|
+
* value always wins, and it's irrelevant when forking by id. Forking a
|
|
131
|
+
* VM in another org requires that VM to be `public: true`. The new VM's
|
|
132
|
+
* name (in your org) is passed as `name`. When the source is a name or
|
|
133
|
+
* `VM`, extra fork options go in the second `opts` argument.
|
|
134
|
+
*/
|
|
135
|
+
async fork(source, opts = {}) {
|
|
136
|
+
const src = typeof source === "string" ? { sourceVmName: source, ...opts } : source instanceof VM ? { sourceVmId: source.id, ...opts } : source;
|
|
137
|
+
if (!src.sourceVmId && !src.sourceVmName) {
|
|
138
|
+
throw new ArkerError(
|
|
139
|
+
"bad_request",
|
|
140
|
+
"fork requires a source (a name, a VM, sourceVmName, or sourceVmId)",
|
|
141
|
+
400
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
if (src.sourceVmId && src.sourceVmName) {
|
|
145
|
+
throw new ArkerError(
|
|
146
|
+
"bad_request",
|
|
147
|
+
"fork: pass only one of sourceVmId or sourceVmName",
|
|
148
|
+
400
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
const sourceOrgId = src.sourceOrgId ?? (src.sourceVmName !== void 0 && GOLDEN_NAMES.has(src.sourceVmName) ? ARKER_ORG_ID : void 0);
|
|
152
|
+
const body = {
|
|
153
|
+
source_vm_id: src.sourceVmId ?? null,
|
|
154
|
+
source_vm_name: src.sourceVmName ?? null,
|
|
155
|
+
source_org_id: sourceOrgId ?? null,
|
|
156
|
+
name: src.name ?? null,
|
|
157
|
+
public: src.public ?? null,
|
|
158
|
+
network: src.network ?? null,
|
|
159
|
+
disk: src.disk ?? true,
|
|
160
|
+
vcpu_count: src.vcpu_count ?? null,
|
|
161
|
+
memory_mib: src.memory_mib ?? null,
|
|
162
|
+
max_memory_mib: src.max_memory_mib ?? null,
|
|
163
|
+
disk_mib: src.disk_mib ?? null,
|
|
164
|
+
durable: src.durable ?? null
|
|
165
|
+
};
|
|
166
|
+
const useBurst = sourceOrgId === ARKER_ORG_ID && src.sourceVmName !== void 0 && isBurstRef(src.sourceVmName);
|
|
167
|
+
const baseUrl = useBurst && this.burstBaseUrl ? this.burstBaseUrl : this.baseUrl;
|
|
168
|
+
const vm = await this._request("POST", "/v1/fork", body, baseUrl);
|
|
169
|
+
const vmId = vm.vm_id ?? vm.id ?? "";
|
|
170
|
+
return new VM(this, vmId, baseUrl, vm);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* List VMs visible to the authenticated caller. **Admin call** —
|
|
174
|
+
* goes through the control plane (`controlBaseUrl`) so it can
|
|
175
|
+
* aggregate across providers and regions. Pass `?provider=` /
|
|
176
|
+
* `?region=` to narrow.
|
|
177
|
+
*/
|
|
178
|
+
async listVms(opts = {}) {
|
|
179
|
+
const resp = await this._request("GET", buildQuery("/v1/vms", {
|
|
180
|
+
cursor: opts.cursor,
|
|
181
|
+
limit: opts.limit,
|
|
182
|
+
region: opts.region,
|
|
183
|
+
provider: opts.provider,
|
|
184
|
+
state: opts.state,
|
|
185
|
+
source_org_id: opts.sourceOrgId,
|
|
186
|
+
started_after: opts.startedAfter,
|
|
187
|
+
started_before: opts.startedBefore
|
|
188
|
+
}), void 0, this.controlBaseUrl);
|
|
189
|
+
const vms = (resp.vms ?? []).map((v) => {
|
|
190
|
+
const id = v.vm_id ?? v.id ?? "";
|
|
191
|
+
return new VM(this, id, this._baseUrlFor(id), v);
|
|
192
|
+
});
|
|
193
|
+
return { vms, nextCursor: resp.next_cursor ?? null };
|
|
194
|
+
}
|
|
195
|
+
/** Compute call — goes direct to the backend hosting this VM (no
|
|
196
|
+
* control-plane hop). Returns a fully-populated VM handle. */
|
|
197
|
+
async getVm(vmId) {
|
|
198
|
+
const data = await this._request("GET", vmPath(vmId), void 0, this._baseUrlFor(vmId));
|
|
199
|
+
return new VM(this, vmId, this._baseUrlFor(vmId), data);
|
|
200
|
+
}
|
|
201
|
+
// ── Filesystems (region-scoped, served by arkerd directly) ──────────
|
|
202
|
+
// Route to the regional endpoint (baseUrl), not the control plane: the
|
|
203
|
+
// control-plane path (arker.ai → api_proxy_bash) does not route
|
|
204
|
+
// /v1/filesystems, while the regional NLB → arkerd serves the full CRUD.
|
|
205
|
+
async listFilesystems(opts = {}) {
|
|
206
|
+
return this._request("GET", buildQuery("/v1/filesystems", {
|
|
207
|
+
cursor: opts.cursor,
|
|
208
|
+
limit: opts.limit,
|
|
209
|
+
name_prefix: opts.namePrefix
|
|
210
|
+
}), void 0, this.baseUrl);
|
|
211
|
+
}
|
|
212
|
+
async createFilesystem(request) {
|
|
213
|
+
return this._request("POST", "/v1/filesystems", { name: request.name }, this.baseUrl);
|
|
214
|
+
}
|
|
215
|
+
async getFilesystem(filesystemId) {
|
|
216
|
+
return this._request("GET", `/v1/filesystems/${pathSegment(filesystemId)}`, void 0, this.baseUrl);
|
|
217
|
+
}
|
|
218
|
+
async deleteFilesystem(filesystemId) {
|
|
219
|
+
return this._request("DELETE", `/v1/filesystems/${pathSegment(filesystemId)}`, void 0, this.baseUrl);
|
|
87
220
|
}
|
|
88
221
|
/** @internal */
|
|
89
222
|
async _request(method, path, body, baseUrl = this.baseUrl, extraHeaders) {
|
|
@@ -153,79 +286,90 @@ var Arker = class {
|
|
|
153
286
|
return this.baseUrl;
|
|
154
287
|
}
|
|
155
288
|
};
|
|
156
|
-
var
|
|
289
|
+
var VM = class _VM {
|
|
157
290
|
id;
|
|
158
291
|
baseUrl;
|
|
159
|
-
sync;
|
|
160
292
|
/** @internal */
|
|
161
293
|
_client;
|
|
162
|
-
|
|
294
|
+
// ── Data fields ──────────────────────────────────────────────────
|
|
295
|
+
// Populated from fork/get/list/refresh; `undefined` on a bare handle
|
|
296
|
+
// from `arker.vm(id)` until you call `refresh()`. Names mirror the
|
|
297
|
+
// contract (`Vm`).
|
|
298
|
+
vm_id;
|
|
299
|
+
name;
|
|
300
|
+
state;
|
|
301
|
+
owner_org_id;
|
|
302
|
+
created_at;
|
|
303
|
+
public;
|
|
304
|
+
region;
|
|
305
|
+
provider;
|
|
306
|
+
vcpu_count;
|
|
307
|
+
memory_mib;
|
|
308
|
+
disk_mib;
|
|
309
|
+
started_at;
|
|
310
|
+
root_source_vm_id;
|
|
311
|
+
root_source_vm_name;
|
|
312
|
+
worker_id;
|
|
313
|
+
sessions;
|
|
314
|
+
tunnels;
|
|
315
|
+
constructor(client, vmId, baseUrl = client._baseUrlFor(vmId), data) {
|
|
163
316
|
this._client = client;
|
|
164
317
|
this.id = vmId;
|
|
165
318
|
this.baseUrl = baseUrl;
|
|
166
|
-
|
|
167
|
-
|
|
319
|
+
if (data) Object.assign(this, data);
|
|
320
|
+
Object.defineProperty(this, "_client", { enumerable: false });
|
|
321
|
+
}
|
|
322
|
+
/** Re-fetch this VM and return a fresh, fully-populated handle. */
|
|
323
|
+
async refresh() {
|
|
324
|
+
const data = await this._client._request("GET", vmPath(this.id), void 0, this.baseUrl);
|
|
325
|
+
return new _VM(this._client, this.id, this.baseUrl, data);
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* @deprecated Use `Arker.fork({ sourceVmId: this.id, ... })`.
|
|
329
|
+
* Kept for back-compat with older user code that called `.fork()` on
|
|
330
|
+
* a VM instance.
|
|
331
|
+
*/
|
|
168
332
|
async fork(request = {}) {
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
request
|
|
173
|
-
|
|
174
|
-
);
|
|
175
|
-
|
|
333
|
+
const merged = {
|
|
334
|
+
...request,
|
|
335
|
+
source_vm_id: request.source_vm_id ?? this.id,
|
|
336
|
+
disk: request.disk ?? true
|
|
337
|
+
};
|
|
338
|
+
const vm = await this._client._request("POST", "/v1/fork", merged, this.baseUrl);
|
|
339
|
+
const vmId = vm.vm_id ?? vm.id ?? "";
|
|
340
|
+
return new _VM(this._client, vmId, this.baseUrl, vm);
|
|
176
341
|
}
|
|
177
342
|
async run(command, options = {}) {
|
|
178
343
|
const { idempotencyKey, ...body } = options;
|
|
179
344
|
const headers = idempotencyKey ? { "Idempotency-Key": idempotencyKey } : void 0;
|
|
180
345
|
const response = await this._client._request(
|
|
181
346
|
"POST",
|
|
182
|
-
`${vmPath(this.id)}/
|
|
347
|
+
`${vmPath(this.id)}/runs`,
|
|
183
348
|
{ ...body, command },
|
|
184
349
|
this.baseUrl,
|
|
185
350
|
headers
|
|
186
351
|
);
|
|
187
352
|
return parseRunResponse(response);
|
|
188
353
|
}
|
|
189
|
-
async
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
async delete() {
|
|
196
|
-
return this._client._request("DELETE", vmPath(this.id), void 0, this.baseUrl);
|
|
197
|
-
}
|
|
198
|
-
};
|
|
199
|
-
var Sync = class {
|
|
200
|
-
/** @internal */
|
|
201
|
-
_vm;
|
|
202
|
-
constructor(vm) {
|
|
203
|
-
this._vm = vm;
|
|
354
|
+
async sync(path, data) {
|
|
355
|
+
if (data === void 0) return this.syncRead(path);
|
|
356
|
+
const bytes = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
357
|
+
if (bytes.length <= CHUNK_SIZE) await this.syncWriteInline(path, bytes);
|
|
358
|
+
else await this.syncWritePresigned(path, bytes);
|
|
204
359
|
}
|
|
205
|
-
async
|
|
206
|
-
const response = await this.
|
|
360
|
+
async syncRead(path) {
|
|
361
|
+
const response = await this._client._request(
|
|
207
362
|
"POST",
|
|
208
|
-
this.
|
|
363
|
+
`${vmPath(this.id)}/sync`,
|
|
209
364
|
{ op: "read", path },
|
|
210
|
-
this.
|
|
365
|
+
this.baseUrl
|
|
211
366
|
);
|
|
212
367
|
if ("content" in response) return decodeBytes(response.content, response.encoding);
|
|
213
|
-
const signed = await this.
|
|
368
|
+
const signed = await this._client._fetch(response.presigned_url);
|
|
214
369
|
if (!signed.ok) throw new ArkerError("internal", `signed GET failed: ${signed.status}`, signed.status);
|
|
215
370
|
return new Uint8Array(await signed.arrayBuffer());
|
|
216
371
|
}
|
|
217
|
-
async
|
|
218
|
-
const bytes = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
219
|
-
if (bytes.length <= CHUNK_SIZE) {
|
|
220
|
-
await this.writeInline(path, bytes);
|
|
221
|
-
} else {
|
|
222
|
-
await this.writePresigned(path, bytes);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
path() {
|
|
226
|
-
return `${vmPath(this._vm.id)}/sync`;
|
|
227
|
-
}
|
|
228
|
-
async writeInline(path, data) {
|
|
372
|
+
async syncWriteInline(path, data) {
|
|
229
373
|
const result = await this.sendOneWrite({
|
|
230
374
|
path,
|
|
231
375
|
size: data.length,
|
|
@@ -236,30 +380,22 @@ var Sync = class {
|
|
|
236
380
|
});
|
|
237
381
|
assertWriteComplete(result, "inline write");
|
|
238
382
|
}
|
|
239
|
-
async
|
|
240
|
-
const request = await this.sendOneWrite({
|
|
241
|
-
path,
|
|
242
|
-
size: data.length,
|
|
243
|
-
presigned: true
|
|
244
|
-
});
|
|
383
|
+
async syncWritePresigned(path, data) {
|
|
384
|
+
const request = await this.sendOneWrite({ path, size: data.length, presigned: true });
|
|
245
385
|
if (!("presigned_url" in request) || !request.presigned_url || !request.upload_id) {
|
|
246
386
|
throw new ArkerError("internal", "write response missing presigned upload fields", 200);
|
|
247
387
|
}
|
|
248
388
|
await this.putPresigned(request.presigned_url, data);
|
|
249
|
-
const commit = await this.sendOneWrite({
|
|
250
|
-
path,
|
|
251
|
-
size: data.length,
|
|
252
|
-
upload_id: request.upload_id
|
|
253
|
-
});
|
|
389
|
+
const commit = await this.sendOneWrite({ path, size: data.length, upload_id: request.upload_id });
|
|
254
390
|
assertWriteComplete(commit, "presigned write commit");
|
|
255
391
|
}
|
|
256
392
|
async putPresigned(url, data) {
|
|
257
|
-
const attempts = this.
|
|
393
|
+
const attempts = this._client._retryAttempts();
|
|
258
394
|
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
259
395
|
const controller = new AbortController();
|
|
260
396
|
const timeout = setTimeout(() => controller.abort(), PRESIGNED_PUT_TIMEOUT_MS);
|
|
261
397
|
try {
|
|
262
|
-
const response = await this.
|
|
398
|
+
const response = await this._client._fetch(url, {
|
|
263
399
|
method: "PUT",
|
|
264
400
|
body: data,
|
|
265
401
|
signal: controller.signal
|
|
@@ -277,28 +413,108 @@ var Sync = class {
|
|
|
277
413
|
throw new ArkerError("network_error", `upload PUT failed: ${message}`, 0);
|
|
278
414
|
}
|
|
279
415
|
}
|
|
280
|
-
await sleep(this.
|
|
416
|
+
await sleep(this._client._retryDelay(attempt));
|
|
281
417
|
}
|
|
282
418
|
}
|
|
283
419
|
async sendOneWrite(entry) {
|
|
284
420
|
let lastError;
|
|
285
|
-
const attempts = this.
|
|
421
|
+
const attempts = this._client._retryAttempts();
|
|
286
422
|
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
287
|
-
const response = await this.
|
|
423
|
+
const response = await this._client._request("POST", `${vmPath(this.id)}/sync`, {
|
|
288
424
|
op: "write",
|
|
289
425
|
writes: [entry]
|
|
290
|
-
}, this.
|
|
426
|
+
}, this.baseUrl);
|
|
291
427
|
const result = response.results[0];
|
|
292
428
|
if (!result) throw new ArkerError("internal", "write response missing results[0]", 200);
|
|
293
429
|
const error = result.error ?? void 0;
|
|
294
430
|
if (!error) return result;
|
|
295
431
|
lastError = error;
|
|
296
|
-
if (!isRetryable(200, error) || attempt === attempts - 1) break;
|
|
297
|
-
await sleep(this.
|
|
432
|
+
if (!isRetryable(200, { code: error.code, message: error.message }) || attempt === attempts - 1) break;
|
|
433
|
+
await sleep(this._client._retryDelay(attempt));
|
|
298
434
|
}
|
|
299
435
|
throw new ArkerError(lastError?.code ?? "internal", lastError?.message ?? "write failed", 200);
|
|
300
436
|
}
|
|
437
|
+
async resize(request) {
|
|
438
|
+
return this._client._request("POST", `${vmPath(this.id)}/resize`, request, this.baseUrl);
|
|
439
|
+
}
|
|
440
|
+
async delete() {
|
|
441
|
+
return this._client._request("DELETE", vmPath(this.id), void 0, this.baseUrl);
|
|
442
|
+
}
|
|
443
|
+
// ── Syncs: bindings of a filesystem into this VM at a path ────────
|
|
444
|
+
async listSyncs(opts = {}) {
|
|
445
|
+
return this._client._request("GET", buildQuery(`${vmPath(this.id)}/syncs`, {
|
|
446
|
+
cursor: opts.cursor,
|
|
447
|
+
limit: opts.limit,
|
|
448
|
+
filesystem_id: opts.filesystemId
|
|
449
|
+
}), void 0, this.baseUrl);
|
|
450
|
+
}
|
|
451
|
+
async createSync(request) {
|
|
452
|
+
return this._client._request("POST", `${vmPath(this.id)}/syncs`, {
|
|
453
|
+
filesystem_id: request.filesystemId,
|
|
454
|
+
path: request.path
|
|
455
|
+
}, this.baseUrl);
|
|
456
|
+
}
|
|
457
|
+
async deleteSync(syncId) {
|
|
458
|
+
return this._client._request("DELETE", `${vmPath(this.id)}/syncs/${pathSegment(syncId)}`, void 0, this.baseUrl);
|
|
459
|
+
}
|
|
460
|
+
// ── Runs ──────────────────────────────────────────────────────────
|
|
461
|
+
async listRuns(opts = {}) {
|
|
462
|
+
return this._client._request("GET", buildQuery(`${vmPath(this.id)}/runs`, {
|
|
463
|
+
cursor: opts.cursor,
|
|
464
|
+
limit: opts.limit,
|
|
465
|
+
state: opts.state,
|
|
466
|
+
started_after: opts.startedAfter,
|
|
467
|
+
started_before: opts.startedBefore,
|
|
468
|
+
completed_after: opts.completedAfter
|
|
469
|
+
}), void 0, this.baseUrl);
|
|
470
|
+
}
|
|
471
|
+
async getRun(runId) {
|
|
472
|
+
return this._client._request("GET", `${vmPath(this.id)}/runs/${pathSegment(runId)}`, void 0, this.baseUrl);
|
|
473
|
+
}
|
|
474
|
+
async cancelRun(runId) {
|
|
475
|
+
return this._client._request("DELETE", `${vmPath(this.id)}/runs/${pathSegment(runId)}`, void 0, this.baseUrl);
|
|
476
|
+
}
|
|
477
|
+
// ── Sessions ──────────────────────────────────────────────────────
|
|
478
|
+
async listSessions(opts = {}) {
|
|
479
|
+
return this._client._request("GET", buildQuery(`${vmPath(this.id)}/sessions`, {
|
|
480
|
+
cursor: opts.cursor,
|
|
481
|
+
limit: opts.limit,
|
|
482
|
+
state: opts.state
|
|
483
|
+
}), void 0, this.baseUrl);
|
|
484
|
+
}
|
|
485
|
+
async createSession(request = {}) {
|
|
486
|
+
return this._client._request("POST", `${vmPath(this.id)}/sessions`, request, this.baseUrl);
|
|
487
|
+
}
|
|
488
|
+
async getSession(sessionId) {
|
|
489
|
+
return this._client._request("GET", `${vmPath(this.id)}/sessions/${pathSegment(sessionId)}`, void 0, this.baseUrl);
|
|
490
|
+
}
|
|
491
|
+
async deleteSession(sessionId) {
|
|
492
|
+
return this._client._request("DELETE", `${vmPath(this.id)}/sessions/${pathSegment(sessionId)}`, void 0, this.baseUrl);
|
|
493
|
+
}
|
|
494
|
+
// ── Tunnels: opened as a side effect of fork/run, addressed by port ─
|
|
495
|
+
async listTunnels(opts = {}) {
|
|
496
|
+
return this._client._request("GET", buildQuery(`${vmPath(this.id)}/tunnels`, {
|
|
497
|
+
cursor: opts.cursor,
|
|
498
|
+
limit: opts.limit,
|
|
499
|
+
state: opts.state
|
|
500
|
+
}), void 0, this.baseUrl);
|
|
501
|
+
}
|
|
502
|
+
async getTunnel(port) {
|
|
503
|
+
return this._client._request("GET", `${vmPath(this.id)}/tunnels/${port}`, void 0, this.baseUrl);
|
|
504
|
+
}
|
|
505
|
+
async deleteTunnel(port) {
|
|
506
|
+
return this._client._request("DELETE", `${vmPath(this.id)}/tunnels/${port}`, void 0, this.baseUrl);
|
|
507
|
+
}
|
|
301
508
|
};
|
|
509
|
+
function buildQuery(path, params) {
|
|
510
|
+
const usp = new URLSearchParams();
|
|
511
|
+
for (const [key, value] of Object.entries(params)) {
|
|
512
|
+
if (value === void 0 || value === null) continue;
|
|
513
|
+
usp.append(key, String(value));
|
|
514
|
+
}
|
|
515
|
+
const qs = usp.toString();
|
|
516
|
+
return qs ? `${path}?${qs}` : path;
|
|
517
|
+
}
|
|
302
518
|
function normalizeBaseUrl(baseUrl) {
|
|
303
519
|
const trimmed = baseUrl.trim().replace(/\/+$/, "");
|
|
304
520
|
if (!trimmed) throw new Error("baseUrl must not be empty");
|
|
@@ -309,14 +525,26 @@ function normalizeRegion(region) {
|
|
|
309
525
|
if (!trimmed) throw new Error("region must not be empty");
|
|
310
526
|
return trimmed;
|
|
311
527
|
}
|
|
312
|
-
function
|
|
528
|
+
function computeBaseUrl(provider, region) {
|
|
313
529
|
const normalized = normalizeRegion(region);
|
|
314
|
-
|
|
315
|
-
return `https://${burstRegionHost(normalized)}.arker.ai/api`;
|
|
530
|
+
return `https://${provider}-${normalized}.arker.ai/api`;
|
|
316
531
|
}
|
|
317
|
-
function
|
|
318
|
-
if (
|
|
319
|
-
|
|
532
|
+
function parseProvider(value) {
|
|
533
|
+
if (!value) return DEFAULT_PROVIDER;
|
|
534
|
+
const trimmed = value.trim().toLowerCase();
|
|
535
|
+
if (trimmed === "aws-burst" || trimmed === "burst") return "aws-burst";
|
|
536
|
+
return "aws";
|
|
537
|
+
}
|
|
538
|
+
function splitRegion(value) {
|
|
539
|
+
if (!value) return {};
|
|
540
|
+
const normalized = value.trim().toLowerCase();
|
|
541
|
+
if (normalized.startsWith("aws-burst-")) {
|
|
542
|
+
return { region: normalized.slice("aws-burst-".length), providerFromRegion: "aws-burst" };
|
|
543
|
+
}
|
|
544
|
+
if (normalized.startsWith("aws-")) {
|
|
545
|
+
return { region: normalized.slice("aws-".length), providerFromRegion: "aws" };
|
|
546
|
+
}
|
|
547
|
+
return { region: normalized };
|
|
320
548
|
}
|
|
321
549
|
function isBurstRef(ref) {
|
|
322
550
|
const trimmed = ref.trim();
|
|
@@ -360,34 +588,24 @@ function parseRunResponse(payload) {
|
|
|
360
588
|
const stdoutEncoding = stringField(body.stdout_encoding, "run response.stdout_encoding");
|
|
361
589
|
const stderr = stringValue(body.stderr, "run response.stderr");
|
|
362
590
|
const stderrEncoding = stringField(body.stderr_encoding, "run response.stderr_encoding");
|
|
363
|
-
if (body.completed !== true) {
|
|
364
|
-
throw new ArkerError("internal", "completed run response must have completed=true", 200);
|
|
365
|
-
}
|
|
366
591
|
return {
|
|
367
592
|
type: "completed",
|
|
368
|
-
|
|
593
|
+
runId: typeof body.run_id === "string" ? body.run_id : void 0,
|
|
594
|
+
state: typeof body.state === "string" ? body.state : "completed",
|
|
369
595
|
stdout: decodeBytes(stdout, stdoutEncoding),
|
|
370
596
|
stdoutEncoding,
|
|
371
597
|
stderr: decodeBytes(stderr, stderrEncoding),
|
|
372
598
|
stderrEncoding,
|
|
373
|
-
exitCode: numberField(body.exit_code, "run response.exit_code")
|
|
599
|
+
exitCode: numberField(body.exit_code, "run response.exit_code"),
|
|
600
|
+
failReason: typeof body.fail_reason === "string" ? body.fail_reason : null
|
|
374
601
|
};
|
|
375
602
|
}
|
|
376
603
|
if (typeof body.run_id === "string") {
|
|
377
604
|
return {
|
|
378
605
|
type: "background",
|
|
379
|
-
completed: Boolean(body.completed),
|
|
380
606
|
runId: body.run_id,
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
};
|
|
384
|
-
}
|
|
385
|
-
if (body.pty === true) {
|
|
386
|
-
return {
|
|
387
|
-
type: "pty",
|
|
388
|
-
pty: true,
|
|
389
|
-
sessionId: stringField(body.session_id, "run response.session_id"),
|
|
390
|
-
wsUrl: stringField(body.ws_url, "run response.ws_url")
|
|
607
|
+
state: typeof body.state === "string" ? body.state : "running",
|
|
608
|
+
tunnels: Array.isArray(body.tunnels) ? body.tunnels : []
|
|
391
609
|
};
|
|
392
610
|
}
|
|
393
611
|
throw new ArkerError("internal", "unrecognized run response shape", 200);
|
|
@@ -499,9 +717,9 @@ function bufferConstructor() {
|
|
|
499
717
|
}
|
|
500
718
|
// Annotate the CommonJS export names for ESM import in node:
|
|
501
719
|
0 && (module.exports = {
|
|
720
|
+
ARKER_ORG_ID,
|
|
502
721
|
Arker,
|
|
503
722
|
ArkerError,
|
|
504
723
|
CHUNK_SIZE,
|
|
505
|
-
|
|
506
|
-
Sync
|
|
724
|
+
VM
|
|
507
725
|
});
|