@arker-ai/sdk 0.5.1 → 0.6.2
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 +154 -0
- package/dist/arker-provider-BNIL8NdM.d.ts +7 -0
- package/dist/arker-provider-DwUib5ZW.d.cts +7 -0
- package/dist/chunk-35IEV6BU.js +286 -0
- package/dist/{chunk-PI3H3TGC.js → chunk-7BHPVQNG.js} +265 -23
- package/dist/cli.cjs +621 -160
- package/dist/cli.js +356 -137
- package/dist/common-C5zJ-LkS.d.cts +9 -0
- package/dist/common-C5zJ-LkS.d.ts +9 -0
- package/dist/daytona.cjs +1274 -0
- package/dist/daytona.d.cts +37 -0
- package/dist/daytona.d.ts +37 -0
- package/dist/daytona.js +65 -0
- package/dist/e2b.cjs +1288 -0
- package/dist/e2b.d.cts +29 -0
- package/dist/e2b.d.ts +29 -0
- package/dist/e2b.js +75 -0
- package/dist/index.cjs +275 -23
- package/dist/index.d.cts +229 -101
- package/dist/index.d.ts +229 -101
- package/dist/index.js +1 -1
- package/dist/modal.cjs +1356 -0
- package/dist/modal.d.cts +49 -0
- package/dist/modal.d.ts +49 -0
- package/dist/modal.js +130 -0
- package/package.json +30 -4
package/dist/cli.js
CHANGED
|
@@ -3,13 +3,13 @@ import {
|
|
|
3
3
|
ARKER_ORG_ID,
|
|
4
4
|
Arker,
|
|
5
5
|
ArkerError
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-7BHPVQNG.js";
|
|
7
7
|
|
|
8
8
|
// src/cli.ts
|
|
9
9
|
import { readFileSync, existsSync } from "fs";
|
|
10
|
+
import { spawnSync } from "child_process";
|
|
10
11
|
import { homedir } from "os";
|
|
11
12
|
import { join } from "path";
|
|
12
|
-
import * as readline from "readline/promises";
|
|
13
13
|
import { stdin as input, stdout as output } from "process";
|
|
14
14
|
function parseArgs(argv) {
|
|
15
15
|
const positional = [];
|
|
@@ -38,20 +38,25 @@ function parseArgs(argv) {
|
|
|
38
38
|
return { positional, flags };
|
|
39
39
|
}
|
|
40
40
|
function readFileConfig() {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
for (const name of ["config.json", "config"]) {
|
|
42
|
+
const path = join(homedir(), ".arker", name);
|
|
43
|
+
if (!existsSync(path)) continue;
|
|
44
|
+
try {
|
|
45
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
46
|
+
} catch {
|
|
47
|
+
return {};
|
|
48
|
+
}
|
|
47
49
|
}
|
|
50
|
+
return {};
|
|
48
51
|
}
|
|
49
52
|
function clientFromArgs(args) {
|
|
50
53
|
const file = readFileConfig();
|
|
54
|
+
const explicitBaseUrl = args.flags["base-url"] ?? process.env.ARKER_BASE_URL;
|
|
55
|
+
const explicitRegion = args.flags.region ?? process.env.ARKER_REGION;
|
|
51
56
|
const apiKey = args.flags["api-key"] ?? process.env.ARKER_API_KEY ?? file.apiKey;
|
|
52
|
-
const baseUrl =
|
|
57
|
+
const baseUrl = explicitBaseUrl ?? (explicitRegion ? void 0 : file.baseUrl);
|
|
53
58
|
const controlBaseUrl = args.flags["control-base-url"] ?? process.env.ARKER_CONTROL_BASE_URL;
|
|
54
|
-
const region =
|
|
59
|
+
const region = explicitRegion ?? file.region;
|
|
55
60
|
const provider = args.flags.provider ?? process.env.ARKER_PROVIDER;
|
|
56
61
|
if (!apiKey) {
|
|
57
62
|
die("Missing API key. Set ARKER_API_KEY or pass --api-key.");
|
|
@@ -81,7 +86,8 @@ function fmtVm(vm) {
|
|
|
81
86
|
const region = vm.region ?? "?";
|
|
82
87
|
const name = vm.name ?? "\u2014";
|
|
83
88
|
const state = vm.state ?? "?";
|
|
84
|
-
|
|
89
|
+
const id = vm.vm_id ?? vm.id;
|
|
90
|
+
return `${id} ${provider}-${region} ${state} ${name}`;
|
|
85
91
|
}
|
|
86
92
|
async function cmdVms(args, client) {
|
|
87
93
|
const sub = args.positional[0];
|
|
@@ -126,6 +132,10 @@ async function cmdVms(args, client) {
|
|
|
126
132
|
await cmdRun({ ...args, positional: rest }, client);
|
|
127
133
|
return;
|
|
128
134
|
}
|
|
135
|
+
case "resize": {
|
|
136
|
+
await cmdResize({ ...args, positional: rest }, client);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
129
139
|
default:
|
|
130
140
|
die(`unknown vms subcommand: ${sub}`);
|
|
131
141
|
}
|
|
@@ -144,14 +154,22 @@ async function cmdFork(args, client) {
|
|
|
144
154
|
sourceVmName = refPositional;
|
|
145
155
|
}
|
|
146
156
|
if (!sourceVmId && !sourceVmName) {
|
|
147
|
-
die("usage: arker fork <vm_name> | --source-vm-id <id> | --source-vm-name <name> [--source-org-id <org>]");
|
|
157
|
+
die("usage: arker fork <vm_name> | --source-vm-id <id> | --source-vm-name <name> [--source-org-id <org>]\n [--vcpu N] [--memory-mib N] [--disk-mib N] [--no-disk]");
|
|
148
158
|
}
|
|
159
|
+
const vcpu = numFlag(args, "vcpu");
|
|
160
|
+
const memoryMib = numFlag(args, "memory-mib");
|
|
161
|
+
const diskMib = numFlag(args, "disk-mib");
|
|
162
|
+
const hasResources = vcpu !== void 0 || memoryMib !== void 0 || diskMib !== void 0;
|
|
163
|
+
const resources = hasResources ? { vcpu: vcpu ?? null, memory_mib: memoryMib ?? null, disk_mib: diskMib ?? null } : void 0;
|
|
164
|
+
const disk = boolFlag(args, "no-disk") ? false : void 0;
|
|
149
165
|
const computer = await client.fork({
|
|
150
166
|
sourceVmId,
|
|
151
167
|
sourceVmName,
|
|
152
168
|
sourceOrgId,
|
|
153
169
|
name,
|
|
154
|
-
public: publicFlag
|
|
170
|
+
public: publicFlag,
|
|
171
|
+
...resources ? { resources } : {},
|
|
172
|
+
...disk !== void 0 ? { disk } : {}
|
|
155
173
|
});
|
|
156
174
|
out({ vm_id: computer.id });
|
|
157
175
|
}
|
|
@@ -159,19 +177,54 @@ async function cmdRun(args, client) {
|
|
|
159
177
|
const vmId = args.positional[0] ?? die("usage: arker run <vm_id> <command...>");
|
|
160
178
|
const command = args.positional.slice(1).join(" ");
|
|
161
179
|
if (!command) die("missing command to run");
|
|
180
|
+
const sessionIdx = numFlag(args, "session-idx");
|
|
162
181
|
const result = await client.vm(vmId).run(command, {
|
|
163
182
|
background: boolFlag(args, "background"),
|
|
164
183
|
timeout: numFlag(args, "timeout"),
|
|
165
184
|
acquire: args.flags.acquire,
|
|
166
|
-
release: args.flags.release
|
|
185
|
+
release: args.flags.release,
|
|
186
|
+
session_id: args.flags["session-id"],
|
|
187
|
+
...sessionIdx !== void 0 ? { session_idx: sessionIdx } : {}
|
|
167
188
|
});
|
|
189
|
+
if (args.flags.json) {
|
|
190
|
+
out(runResultForJson(result));
|
|
191
|
+
if (result.type === "completed") process.exitCode = result.exitCode === 0 ? 0 : result.exitCode;
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
168
194
|
if (result.type === "completed") {
|
|
195
|
+
if (result.memoryPartial) {
|
|
196
|
+
err(`Memory target partially applied: requested ${formatMib(result.memoryRequestedMib)}, achieved ${formatMib(result.memoryAchievedMib)}.`);
|
|
197
|
+
}
|
|
169
198
|
process.stdout.write(new TextDecoder().decode(result.stdout));
|
|
170
199
|
if (result.stderr.length) process.stderr.write(new TextDecoder().decode(result.stderr));
|
|
171
200
|
process.exitCode = result.exitCode === 0 ? 0 : result.exitCode;
|
|
172
201
|
return;
|
|
173
202
|
}
|
|
174
|
-
out({ run_id: result.runId,
|
|
203
|
+
out({ run_id: result.runId, state: result.state });
|
|
204
|
+
}
|
|
205
|
+
function formatMib(value) {
|
|
206
|
+
return typeof value === "number" ? `${value} MiB` : "unknown";
|
|
207
|
+
}
|
|
208
|
+
function runResultForJson(result) {
|
|
209
|
+
switch (result.type) {
|
|
210
|
+
case "completed":
|
|
211
|
+
return {
|
|
212
|
+
type: result.type,
|
|
213
|
+
runId: result.runId,
|
|
214
|
+
state: result.state,
|
|
215
|
+
stdout: new TextDecoder().decode(result.stdout),
|
|
216
|
+
stdoutEncoding: result.stdoutEncoding,
|
|
217
|
+
stderr: new TextDecoder().decode(result.stderr),
|
|
218
|
+
stderrEncoding: result.stderrEncoding,
|
|
219
|
+
exitCode: result.exitCode,
|
|
220
|
+
failReason: result.failReason,
|
|
221
|
+
memoryRequestedMib: result.memoryRequestedMib,
|
|
222
|
+
memoryAchievedMib: result.memoryAchievedMib,
|
|
223
|
+
memoryPartial: result.memoryPartial
|
|
224
|
+
};
|
|
225
|
+
case "background":
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
175
228
|
}
|
|
176
229
|
async function cmdRuns(args, client) {
|
|
177
230
|
const sub = args.positional[0];
|
|
@@ -314,43 +367,24 @@ async function cmdSync(args, client) {
|
|
|
314
367
|
}
|
|
315
368
|
output.write(await client.vm(vm).sync(path));
|
|
316
369
|
}
|
|
317
|
-
async function
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
const res = await client.vm(vm).listTunnels({
|
|
326
|
-
state: args.flags.state,
|
|
327
|
-
cursor: args.flags.cursor,
|
|
328
|
-
limit: numFlag(args, "limit")
|
|
329
|
-
});
|
|
330
|
-
if (args.flags.json) return out(res);
|
|
331
|
-
for (const t of res.tunnels) {
|
|
332
|
-
out(`${t.port} ${t.state} ${t.protocol} ${t.url ?? "-"}`);
|
|
333
|
-
}
|
|
334
|
-
if (res.next_cursor) out(`# next_cursor=${res.next_cursor}`);
|
|
335
|
-
return;
|
|
336
|
-
}
|
|
337
|
-
case "get": {
|
|
338
|
-
if (!vm) die("usage: arker tunnels get <vm_id> <port>");
|
|
339
|
-
const port = Number(rest[1] ?? die("missing port"));
|
|
340
|
-
out(await client.vm(vm).getTunnel(port));
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
case "rm":
|
|
344
|
-
case "delete": {
|
|
345
|
-
if (!vm) die("usage: arker tunnels rm <vm_id> <port>");
|
|
346
|
-
const port = Number(rest[1] ?? die("missing port"));
|
|
347
|
-
const r = await client.vm(vm).deleteTunnel(port);
|
|
348
|
-
out(r.deleted ? `deleted tunnel ${port}` : "delete failed");
|
|
349
|
-
return;
|
|
350
|
-
}
|
|
351
|
-
default:
|
|
352
|
-
die(`usage: arker tunnels <ls|get|rm> ...`);
|
|
370
|
+
async function cmdResize(args, client) {
|
|
371
|
+
const vm = args.positional[0];
|
|
372
|
+
if (!vm) die("usage: arker resize <vm_id> [--memory-mib N] [--vcpu N] [--disk-mib N]");
|
|
373
|
+
const memoryMib = numFlag(args, "memory-mib");
|
|
374
|
+
const vcpu = numFlag(args, "vcpu");
|
|
375
|
+
const diskMib = numFlag(args, "disk-mib");
|
|
376
|
+
if (memoryMib === void 0 && vcpu === void 0 && diskMib === void 0) {
|
|
377
|
+
die("resize: pass at least one of --memory-mib, --vcpu, --disk-mib");
|
|
353
378
|
}
|
|
379
|
+
const updated = await client.vm(vm).resize({
|
|
380
|
+
resources: {
|
|
381
|
+
vcpu: vcpu ?? null,
|
|
382
|
+
memory_mib: memoryMib ?? null,
|
|
383
|
+
disk_mib: diskMib ?? null
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
if (args.flags.json) return out(updated);
|
|
387
|
+
out(fmtVm(updated));
|
|
354
388
|
}
|
|
355
389
|
async function cmdFilesystems(args, client) {
|
|
356
390
|
const sub = args.positional[0];
|
|
@@ -396,6 +430,10 @@ async function cmdFilesystems(args, client) {
|
|
|
396
430
|
async function cmdShell(args, client) {
|
|
397
431
|
let computer;
|
|
398
432
|
const vmIdArg = args.flags["vm-id"] ?? args.positional[0];
|
|
433
|
+
const explicitSessionId = args.flags["session-id"];
|
|
434
|
+
if (!vmIdArg && explicitSessionId) {
|
|
435
|
+
die("usage: arker shell <vm_id> --session-id <session_id>");
|
|
436
|
+
}
|
|
399
437
|
if (vmIdArg) {
|
|
400
438
|
computer = await client.vm(vmIdArg).refresh();
|
|
401
439
|
} else {
|
|
@@ -404,100 +442,242 @@ async function cmdShell(args, client) {
|
|
|
404
442
|
sourceVmName,
|
|
405
443
|
sourceOrgId: ARKER_ORG_ID
|
|
406
444
|
});
|
|
445
|
+
err(`forked ${computer.id}`);
|
|
407
446
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
447
|
+
let sessionId = explicitSessionId;
|
|
448
|
+
if (!sessionId) {
|
|
449
|
+
const session = await computer.createSession({
|
|
450
|
+
cwd: args.flags.cwd
|
|
451
|
+
});
|
|
452
|
+
sessionId = session.session_id ?? session.id;
|
|
453
|
+
if (!sessionId) die("createSession response missing session_id");
|
|
454
|
+
}
|
|
455
|
+
const persist = args.flags["no-persist"] === true ? false : boolFlag(args, "persist");
|
|
456
|
+
const colsFlag = numFlag(args, "cols");
|
|
457
|
+
const rowsFlag = numFlag(args, "rows");
|
|
458
|
+
const cols = colsFlag ?? output.columns ?? 80;
|
|
459
|
+
const rows = rowsFlag ?? output.rows ?? 24;
|
|
460
|
+
const cancelTtlSecs = numFlag(args, "cancel-ttl");
|
|
461
|
+
const pty = await computer.connectPty({
|
|
462
|
+
sessionId,
|
|
463
|
+
cols,
|
|
464
|
+
rows,
|
|
465
|
+
command: args.flags.command,
|
|
466
|
+
persist,
|
|
467
|
+
cancelTtlSecs
|
|
411
468
|
});
|
|
412
|
-
|
|
413
|
-
const
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
if (
|
|
427
|
-
|
|
428
|
-
exitCode = 1;
|
|
429
|
-
} else {
|
|
430
|
-
exitCode = step.exitCode;
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
if (exitAfter || preload === "exit") {
|
|
434
|
-
process.exit(exitCode);
|
|
435
|
-
}
|
|
436
|
-
const rl = readline.createInterface({ input, output, prompt: "> " });
|
|
437
|
-
rl.on("SIGINT", () => {
|
|
438
|
-
if (inFlight) {
|
|
439
|
-
process.stderr.write("^C\n");
|
|
440
|
-
return;
|
|
469
|
+
err(`connected ${computer.id} session ${sessionId}`);
|
|
470
|
+
const exitCode = await bridgePty(pty, {
|
|
471
|
+
fallbackCols: cols,
|
|
472
|
+
fallbackRows: rows,
|
|
473
|
+
autoResize: colsFlag === void 0 && rowsFlag === void 0 && Boolean(output.isTTY)
|
|
474
|
+
});
|
|
475
|
+
if (exitCode !== 0) process.exit(exitCode);
|
|
476
|
+
}
|
|
477
|
+
function bridgePty(pty, options) {
|
|
478
|
+
return new Promise((resolve) => {
|
|
479
|
+
let settled = false;
|
|
480
|
+
let rawEnabled = false;
|
|
481
|
+
const wasRaw = Boolean(input.isTTY && input.isRaw);
|
|
482
|
+
const restoreTerminal = () => {
|
|
483
|
+
if (rawEnabled && input.isTTY && typeof input.setRawMode === "function") {
|
|
484
|
+
input.setRawMode(wasRaw);
|
|
441
485
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
486
|
+
rawEnabled = false;
|
|
487
|
+
};
|
|
488
|
+
const finish = (code) => {
|
|
489
|
+
if (settled) return;
|
|
490
|
+
settled = true;
|
|
491
|
+
restoreTerminal();
|
|
492
|
+
input.off("data", onInput);
|
|
493
|
+
input.off("end", onInputEnd);
|
|
494
|
+
process.off("SIGWINCH", onResize);
|
|
495
|
+
process.off("SIGINT", onSigint);
|
|
496
|
+
process.off("SIGTERM", onSigterm);
|
|
497
|
+
process.off("SIGHUP", onSighup);
|
|
498
|
+
process.off("exit", restoreTerminal);
|
|
499
|
+
offData();
|
|
500
|
+
offClose();
|
|
501
|
+
offError();
|
|
502
|
+
resolve(code);
|
|
503
|
+
};
|
|
504
|
+
const onInput = (chunk) => {
|
|
505
|
+
pty.send(chunk);
|
|
506
|
+
};
|
|
507
|
+
const onInputEnd = () => {
|
|
508
|
+
pty.close();
|
|
509
|
+
};
|
|
510
|
+
const onResize = () => {
|
|
511
|
+
pty.resize(output.columns ?? options.fallbackCols, output.rows ?? options.fallbackRows);
|
|
512
|
+
};
|
|
513
|
+
const onSigint = () => {
|
|
514
|
+
pty.send(new Uint8Array([3]));
|
|
515
|
+
};
|
|
516
|
+
const onSigterm = () => {
|
|
517
|
+
pty.close();
|
|
518
|
+
finish(143);
|
|
519
|
+
};
|
|
520
|
+
const onSighup = () => {
|
|
521
|
+
pty.close();
|
|
522
|
+
finish(129);
|
|
523
|
+
};
|
|
524
|
+
const offData = pty.onData((data) => {
|
|
525
|
+
output.write(data);
|
|
445
526
|
});
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
const
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
if (
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
if (step.kind === "fatal") {
|
|
458
|
-
err(`shell ended: ${step.message}`);
|
|
459
|
-
exitCode = 1;
|
|
460
|
-
break;
|
|
461
|
-
}
|
|
462
|
-
if (step.kind === "recoverable") {
|
|
463
|
-
err(`error: ${step.message}`);
|
|
527
|
+
const offClose = pty.onClose(() => finish(0));
|
|
528
|
+
const offError = pty.onError((error) => {
|
|
529
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
530
|
+
err(`pty error: ${message}`);
|
|
531
|
+
});
|
|
532
|
+
process.once("exit", restoreTerminal);
|
|
533
|
+
pty.ready.then(() => {
|
|
534
|
+
if (settled) return;
|
|
535
|
+
if (input.isTTY && typeof input.setRawMode === "function") {
|
|
536
|
+
input.setRawMode(true);
|
|
537
|
+
rawEnabled = true;
|
|
464
538
|
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
539
|
+
input.resume();
|
|
540
|
+
input.on("data", onInput);
|
|
541
|
+
input.on("end", onInputEnd);
|
|
542
|
+
if (options.autoResize) process.on("SIGWINCH", onResize);
|
|
543
|
+
process.on("SIGINT", onSigint);
|
|
544
|
+
process.on("SIGTERM", onSigterm);
|
|
545
|
+
process.on("SIGHUP", onSighup);
|
|
546
|
+
if (options.autoResize) onResize();
|
|
547
|
+
}).catch((error) => {
|
|
548
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
549
|
+
err(`pty failed to open: ${message}`);
|
|
550
|
+
finish(1);
|
|
470
551
|
});
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
var SSH_PORT_DEFAULT = 22;
|
|
555
|
+
function defaultIdentityBase() {
|
|
556
|
+
return join(homedir(), ".ssh", "id_ed25519");
|
|
557
|
+
}
|
|
558
|
+
function resolveLocalSshKey(args) {
|
|
559
|
+
const explicit = args.flags.identity;
|
|
560
|
+
const privateKeyPath = explicit ? explicit.replace(/\.pub$/, "") : defaultIdentityBase();
|
|
561
|
+
const publicKeyPath = `${privateKeyPath}.pub`;
|
|
562
|
+
if (!existsSync(publicKeyPath)) {
|
|
563
|
+
if (!boolFlag(args, "generate")) {
|
|
564
|
+
die(
|
|
565
|
+
`no SSH public key at ${publicKeyPath}. Pass --identity <path>, or --generate to create an ed25519 key pair.`
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
err(`generating ed25519 key pair at ${privateKeyPath}`);
|
|
569
|
+
const gen = spawnSync(
|
|
570
|
+
"ssh-keygen",
|
|
571
|
+
["-t", "ed25519", "-N", "", "-f", privateKeyPath, "-C", "arker-cli"],
|
|
572
|
+
{ stdio: "inherit" }
|
|
573
|
+
);
|
|
574
|
+
if (gen.status !== 0) die("ssh-keygen failed to generate a key pair");
|
|
471
575
|
}
|
|
472
|
-
|
|
576
|
+
const publicKey = readFileSync(publicKeyPath, "utf8").trim();
|
|
577
|
+
if (!publicKey) die(`SSH public key at ${publicKeyPath} is empty`);
|
|
578
|
+
return { publicKey, privateKeyPath, publicKeyPath };
|
|
579
|
+
}
|
|
580
|
+
async function registerAccountSshKey(client, publicKey, label) {
|
|
581
|
+
return client._request(
|
|
582
|
+
"POST",
|
|
583
|
+
"/v1/account/ssh-keys",
|
|
584
|
+
{ public_key: publicKey, label: label ?? null },
|
|
585
|
+
client.baseUrl
|
|
586
|
+
);
|
|
473
587
|
}
|
|
474
|
-
|
|
588
|
+
function resolveSshHost(args, client) {
|
|
589
|
+
const explicit = args.flags.host ?? process.env.ARKER_SSH_HOST;
|
|
590
|
+
if (explicit) return explicit;
|
|
591
|
+
if (client.region) return `aws-${client.region}.arker.ai`;
|
|
475
592
|
try {
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
if (result.stderr.length) process.stderr.write(new TextDecoder().decode(result.stderr));
|
|
480
|
-
const tail = result.stderr.length > 0 ? result.stderr[result.stderr.length - 1] : result.stdout.length > 0 ? result.stdout[result.stdout.length - 1] : void 0;
|
|
481
|
-
if (tail !== void 0 && tail !== 10) process.stdout.write("\n");
|
|
482
|
-
return { kind: "ok", exitCode: result.exitCode };
|
|
483
|
-
}
|
|
484
|
-
out({ run_id: result.runId });
|
|
485
|
-
return { kind: "ok", exitCode: 0 };
|
|
486
|
-
} catch (e) {
|
|
487
|
-
const message = e instanceof Error ? e.message : String(e);
|
|
488
|
-
const kind = classifyShellError(message, computer.id);
|
|
489
|
-
return { kind, message };
|
|
593
|
+
return new URL(client.baseUrl).hostname;
|
|
594
|
+
} catch {
|
|
595
|
+
die("could not determine SSH host; pass --host <hostname>");
|
|
490
596
|
}
|
|
491
597
|
}
|
|
492
|
-
function
|
|
493
|
-
const
|
|
494
|
-
if (
|
|
495
|
-
|
|
598
|
+
async function cmdSsh(args, client) {
|
|
599
|
+
const vmId = args.flags["vm-id"] ?? args.positional[0];
|
|
600
|
+
if (!vmId) {
|
|
601
|
+
die(
|
|
602
|
+
"usage: arker ssh <vm_id> [--identity <path>] [--generate] [--host <h>] [--port <n>] [--connect|-c] [--skip-register]"
|
|
603
|
+
);
|
|
496
604
|
}
|
|
497
|
-
|
|
498
|
-
|
|
605
|
+
const { publicKey, privateKeyPath } = resolveLocalSshKey(args);
|
|
606
|
+
if (!boolFlag(args, "skip-register")) {
|
|
607
|
+
const label = args.flags.label ?? `arker-cli ${homedir().split("/").pop() ?? ""}`.trim();
|
|
608
|
+
try {
|
|
609
|
+
const key = await registerAccountSshKey(client, publicKey, label);
|
|
610
|
+
err(`registered SSH key ${key.fingerprint}${key.label ? ` (${key.label})` : ""}`);
|
|
611
|
+
} catch (e) {
|
|
612
|
+
if (e instanceof ArkerError && e.status === 409) {
|
|
613
|
+
die(`this SSH key is already registered to a different account: ${e.message}`);
|
|
614
|
+
}
|
|
615
|
+
if (e instanceof ArkerError && e.status === 403) {
|
|
616
|
+
die(
|
|
617
|
+
"your API key lacks the developer role required to register SSH keys. Register the key in the console, then re-run with --skip-register."
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
throw e;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
const host = resolveSshHost(args, client);
|
|
624
|
+
const port = numFlag(args, "port") ?? SSH_PORT_DEFAULT;
|
|
625
|
+
const sshArgs = [
|
|
626
|
+
"-i",
|
|
627
|
+
privateKeyPath,
|
|
628
|
+
...port !== 22 ? ["-p", String(port)] : [],
|
|
629
|
+
`${vmId}@${host}`
|
|
630
|
+
];
|
|
631
|
+
const command = `ssh ${sshArgs.join(" ")}`;
|
|
632
|
+
if (boolFlag(args, "connect") || boolFlag(args, "c")) {
|
|
633
|
+
err(`connecting: ${command}`);
|
|
634
|
+
const r = spawnSync("ssh", sshArgs, { stdio: "inherit" });
|
|
635
|
+
process.exit(r.status ?? 1);
|
|
636
|
+
}
|
|
637
|
+
out(command);
|
|
638
|
+
}
|
|
639
|
+
async function cmdSshKeys(args, client) {
|
|
640
|
+
const sub = args.positional[0];
|
|
641
|
+
const rest = args.positional.slice(1);
|
|
642
|
+
switch (sub) {
|
|
643
|
+
case void 0:
|
|
644
|
+
case "ls":
|
|
645
|
+
case "list": {
|
|
646
|
+
const res = await client._request(
|
|
647
|
+
"GET",
|
|
648
|
+
"/v1/account/ssh-keys",
|
|
649
|
+
void 0,
|
|
650
|
+
client.baseUrl
|
|
651
|
+
);
|
|
652
|
+
if (args.flags.json) return out(res);
|
|
653
|
+
for (const k of res.keys) {
|
|
654
|
+
out(`${k.id} ${k.fingerprint} ${k.label ?? "\u2014"}`);
|
|
655
|
+
}
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
case "add": {
|
|
659
|
+
const { publicKey } = resolveLocalSshKey(args);
|
|
660
|
+
const label = args.flags.label;
|
|
661
|
+
const key = await registerAccountSshKey(client, publicKey, label);
|
|
662
|
+
if (args.flags.json) return out(key);
|
|
663
|
+
out(`${key.id} ${key.fingerprint} ${key.label ?? "\u2014"}`);
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
case "rm":
|
|
667
|
+
case "delete": {
|
|
668
|
+
const id = rest[0] ?? die("usage: arker ssh-keys rm <key_id>");
|
|
669
|
+
const r = await client._request(
|
|
670
|
+
"DELETE",
|
|
671
|
+
`/v1/account/ssh-keys/${encodeURIComponent(id)}`,
|
|
672
|
+
void 0,
|
|
673
|
+
client.baseUrl
|
|
674
|
+
);
|
|
675
|
+
out(r.deleted ? `deleted ${id}` : "delete failed");
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
default:
|
|
679
|
+
die("usage: arker ssh-keys <ls|add|rm> ...");
|
|
499
680
|
}
|
|
500
|
-
return "recoverable";
|
|
501
681
|
}
|
|
502
682
|
function numFlag(args, name) {
|
|
503
683
|
const v = args.flags[name];
|
|
@@ -531,16 +711,21 @@ function usage() {
|
|
|
531
711
|
" arker fork --source-vm-id <id> fork by global id",
|
|
532
712
|
" arker fork --source-vm-name <n> --source-org-id <org>",
|
|
533
713
|
" fork by name in another org",
|
|
534
|
-
" arker
|
|
535
|
-
"
|
|
714
|
+
" arker fork <vm> [--vcpu N] [--memory-mib N] [--disk-mib N] [--no-disk]",
|
|
715
|
+
" fork with resource/network overrides",
|
|
716
|
+
" arker run <vm> <command> [--session-id <id>] [--session-idx N] run a command",
|
|
717
|
+
" arker resize <vm> [--memory-mib N] [--vcpu N] [--disk-mib N] resize a VM (PATCH)",
|
|
718
|
+
" arker shell [vm_id] native PTY shell (forks ubuntu-full if no vm)",
|
|
719
|
+
" arker ssh <vm_id> register your SSH key + print the ssh command",
|
|
720
|
+
" arker ssh <vm_id> --connect register + drop straight into the ssh session",
|
|
536
721
|
"",
|
|
537
722
|
"Resources:",
|
|
538
723
|
" arker vms <ls|get|rm|fork|run> ...",
|
|
539
724
|
" arker runs <ls|get|rm> <vm_id> ...",
|
|
540
725
|
" arker sessions <ls|get|create|rm> <vm_id> ...",
|
|
541
726
|
" arker syncs <ls|create|rm> <vm_id> ...",
|
|
542
|
-
" arker tunnels <ls|get|rm> <vm_id> ...",
|
|
543
727
|
" arker filesystems <ls|create|get|rm> ... (alias: fs)",
|
|
728
|
+
" arker ssh-keys <ls|add|rm> ... manage account SSH keys",
|
|
544
729
|
"",
|
|
545
730
|
"Flags:",
|
|
546
731
|
" --api-key <key> (or env ARKER_API_KEY)",
|
|
@@ -550,6 +735,35 @@ function usage() {
|
|
|
550
735
|
" --control-base-url <url> override CF Worker URL (env ARKER_CONTROL_BASE_URL)",
|
|
551
736
|
" --json emit JSON instead of tabular output",
|
|
552
737
|
"",
|
|
738
|
+
"Fork flags:",
|
|
739
|
+
" --vcpu <n> vCPU count for the new VM (capped by source max_vcpus)",
|
|
740
|
+
" --memory-mib <n> memory (MiB) for the new VM",
|
|
741
|
+
" --disk-mib <n> disk size (MiB) for the new VM",
|
|
742
|
+
" --no-disk fork a memory-backed (nodisk) VM",
|
|
743
|
+
"",
|
|
744
|
+
"Run flags:",
|
|
745
|
+
" --session-id <ulid> run in a specific existing session",
|
|
746
|
+
" --session-idx <n> run in the session at this index (default 0)",
|
|
747
|
+
" --background return a run id instead of blocking",
|
|
748
|
+
" --timeout <secs> per-run timeout",
|
|
749
|
+
" --acquire <list> warm resources before the run (cpu,memory,disk)",
|
|
750
|
+
" --release <list> release resources after the run (cpu,memory,disk)",
|
|
751
|
+
"",
|
|
752
|
+
"Shell flags:",
|
|
753
|
+
" --session-id <id> reconnect to an existing PTY session",
|
|
754
|
+
" --command <path> shell executable path (default: /bin/bash)",
|
|
755
|
+
" --cols <n> --rows <n> initial terminal size",
|
|
756
|
+
" --no-persist close the remote PTY process on disconnect",
|
|
757
|
+
"",
|
|
758
|
+
"SSH flags:",
|
|
759
|
+
" --identity <path> local key (private or .pub); default ~/.ssh/id_ed25519",
|
|
760
|
+
" --generate create an ed25519 key pair if none exists",
|
|
761
|
+
" --host <hostname> SSH host (or env ARKER_SSH_HOST; default aws-<region>.arker.ai)",
|
|
762
|
+
" --port <n> SSH port (default 22)",
|
|
763
|
+
" --connect, -c exec ssh instead of just printing the command",
|
|
764
|
+
" --skip-register don't register the key (assume already registered)",
|
|
765
|
+
" --label <text> label for the registered key",
|
|
766
|
+
"",
|
|
553
767
|
`Arker org id: ${ARKER_ORG_ID}`
|
|
554
768
|
].join("\n")
|
|
555
769
|
);
|
|
@@ -580,6 +794,11 @@ async function main() {
|
|
|
580
794
|
return await cmdSyncs(args, client);
|
|
581
795
|
case "shell":
|
|
582
796
|
return await cmdShell(args, client);
|
|
797
|
+
case "ssh":
|
|
798
|
+
return await cmdSsh(args, client);
|
|
799
|
+
case "ssh-keys":
|
|
800
|
+
case "ssh_keys":
|
|
801
|
+
return await cmdSshKeys(args, client);
|
|
583
802
|
// Resources.
|
|
584
803
|
case "vms":
|
|
585
804
|
return await cmdVms(args, client);
|
|
@@ -587,8 +806,8 @@ async function main() {
|
|
|
587
806
|
return await cmdRuns(args, client);
|
|
588
807
|
case "sessions":
|
|
589
808
|
return await cmdSessions(args, client);
|
|
590
|
-
case "
|
|
591
|
-
return await
|
|
809
|
+
case "resize":
|
|
810
|
+
return await cmdResize(args, client);
|
|
592
811
|
case "filesystems":
|
|
593
812
|
case "fs":
|
|
594
813
|
return await cmdFilesystems(args, client);
|