@arker-ai/sdk 0.2.0 → 0.3.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 CHANGED
@@ -1,9 +1,7 @@
1
- # Arker TypeScript SDK
1
+ # Arker TypeScript SDK
2
2
 
3
- TypeScript client for the [Arker](https://arker.ai) virtual computer
4
- platform. Spawn isolated Linux sandboxes, run shell / Python / Node
5
- code in them, read and write files. Zero runtime dependencies (uses
6
- the platform's built-in `fetch` and `crypto`).
3
+ Small TypeScript wrapper for the Arker VM API. The SDK keeps API keys,
4
+ region routing, retries, output decoding, and file sync ergonomics in one place.
7
5
 
8
6
  ## Install
9
7
 
@@ -11,126 +9,154 @@ the platform's built-in `fetch` and `crypto`).
11
9
  npm install @arker-ai/sdk
12
10
  ```
13
11
 
14
- Works in Node 18. ESM + CJS + types all included.
12
+ Node 18 or newer is required.
15
13
 
16
14
  ## Quickstart
17
15
 
18
16
  ```ts
19
- import { Arker, ArkerError } from "@arker-ai/sdk";
17
+ import { Arker } from "@arker-ai/sdk";
20
18
 
21
- const arker = new Arker({ apiKey: "ark_live_..." });
22
- const vm = await arker.vm("arkuntu").fork({ name: "hello" });
19
+ const arker = new Arker({
20
+ apiKey: process.env.ARKER_API_KEY,
21
+ region: process.env.ARKER_REGION ?? "aws-us-west-2",
22
+ });
23
23
 
24
- const result = await vm.run("python3 -c 'print(2+2)'");
25
- console.log(new TextDecoder().decode(result.stdout)); // "4\n"
24
+ const vm = await arker.vm("ubuntu").fork({ name: "hello" });
25
+ const result = await vm.run("printf 'hello\\n'");
26
26
 
27
- await vm.sync.writeFile("/home/user/data.csv", "a,b\n1,2\n");
28
- const data = await vm.sync.readFile("/home/user/data.csv");
27
+ if (result.type === "completed") {
28
+ console.log(new TextDecoder().decode(result.stdout));
29
+ }
30
+
31
+ await vm.sync.writeFile("/home/user/data.txt", "hello\n");
32
+ const data = await vm.sync.readFile("/home/user/data.txt");
29
33
 
30
- const child = await vm.fork({ name: "branch" }); // constant-time copy-on-write
31
- await child.delete();
32
34
  await vm.delete();
33
35
  ```
34
36
 
35
- List your VMs:
37
+ `region` selects the regional Arker endpoints. The SDK routes `arkuntu` and
38
+ burst VM ids to the burst endpoint for that region; other golden names and VM
39
+ ids use the normal regional endpoint. There is no cross-region VM replication.
36
40
 
37
- ```ts
38
- const page = await arker.list({ limit: 10, sort: "-created_at" });
39
- console.log(`${page.total} total`);
40
- for (const summary of page.items) {
41
- console.log(summary.vm_id, summary.name, summary.region, summary.created_at);
42
- }
41
+ ```bash
42
+ export ARKER_REGION=aws-us-west-2
43
43
  ```
44
44
 
45
+ For internal or dev targets, pass `baseUrl` directly. If an endpoint mounts the
46
+ API under `/api`, include that prefix.
47
+
45
48
  ## API
46
49
 
47
- ```
48
- new Arker({ apiKey, baseUrl? })
49
- .vm(vmId) -> Computer // open handle (no network call)
50
- .list({ limit?, offset?, q?, sort? }) -> Promise<VmList>
50
+ ```ts
51
+ new Arker({ apiKey?, region?, baseUrl?, burstBaseUrl?, retry? })
52
+ .vm(vmId)
53
+ .goldens()
54
+ .list()
55
+ .get(vmId)
51
56
 
52
57
  Computer
53
- .id, .delete()
54
- .fork({ name?, isPublic?, region? }) -> Promise<Computer>
55
- .run(command, { sessionId?, timeout? }) -> Promise<RunResult>
56
- .sync.readFile(path) -> Promise<Uint8Array>
57
- .sync.writeFile(path, data: Uint8Array | string) -> Promise<void>
58
-
59
- RunResult: stdout, stderr (Uint8Array), exitCode, durationMs, sessionId, cwd
60
- VmSummary: vm_id, name, base_image, region, created_at (ISO 8601)
61
- VmList: items (VmSummary[]), total (number)
62
-
63
- ArkerError(code, message, status) extends Error // single error type
58
+ .fork(request)
59
+ .run(command, options)
60
+ .runStatus(runId)
61
+ .cancelRun(runId)
62
+ .delete()
63
+ .sync.readFile(path)
64
+ .sync.writeFile(path, data)
64
65
  ```
65
66
 
66
- ### Routing
67
+ `apiKey` falls back to `ARKER_API_KEY` or `AUTH_KEY`.
68
+ `region` falls back to `ARKER_REGION`; `baseUrl` falls back to
69
+ `ARKER_BASE_URL`. There is no built-in default region.
67
70
 
68
- `fork`, `run`, `sync`, and `delete` use the regional endpoint set on the
69
- client (default `https://aws-us-west-2.burst.arker.ai`).
71
+ Retries are configured on the client:
70
72
 
71
- `list` always goes through `https://arker.ai` regardless of `baseUrl`,
72
- because list data is served from a global host rather than a regional
73
- one.
73
+ ```ts
74
+ const arker = new Arker({
75
+ apiKey: "ark_live_...",
76
+ region: "aws-us-west-2",
77
+ retry: { attempts: 4, baseDelayMs: 200, maxDelayMs: 2000 },
78
+ });
79
+ ```
74
80
 
75
- Public base-image names like `"arkuntu"` resolve to a ULID **client-side**
76
- (see `SOURCE_ALIASES` in `src/index.ts`), so `arker.vm("arkuntu").fork()`
77
- works on the default endpoint with no extra round-trip. Override
78
- `baseUrl` or set `ARKER_BASE_URL` to point at a different region or a
79
- self-hosted deployment.
81
+ Pass `retry: false` to disable SDK retries.
80
82
 
81
- ### Errors
83
+ ## Durability
82
84
 
83
- Every server-side error becomes an `ArkerError`:
85
+ For long-running or non-idempotent work, request a durable VM at fork
86
+ time and pass an idempotency key when retrying `run`:
84
87
 
85
88
  ```ts
86
- try {
87
- await vm.sync.readFile("/home/user/missing");
88
- } catch (err) {
89
- if (err instanceof ArkerError) {
90
- console.log(err.code); // "not_found"
91
- console.log(err.message); // "not_found: file not found: ..."
92
- console.log(err.status); // 404
93
- }
94
- }
89
+ const vm = await arker.vm("ubuntu").fork({ name: "job", durable: true });
90
+
91
+ const run = await vm.run("python3 train.py", {
92
+ background: true,
93
+ idempotencyKey: "550e8400-e29b-41d4-a716-446655440000",
94
+ });
95
95
  ```
96
96
 
97
- `code` is a stable enum: `bad_request`, `unauthorized`, `payment_required`,
98
- `forbidden`, `not_found`, `conflict`, `payload_too_large`, `internal`,
99
- `not_implemented`, `vm_busy`, `unsupported_*`, `command_not_found`.
97
+ - If the underlying host fails mid-run, the run resumes on a healthy
98
+ host with the VM's filesystem state preserved.
99
+ - A `run` retried with the same `idempotencyKey` and the same request
100
+ returns the original `run_id`. A different request under the same key
101
+ returns `ArkerError` code `conflict`.
102
+ - `runStatus().retry_count` is the number of automatic retries the run
103
+ has gone through — `0` for runs that completed without interruption.
104
+
105
+ Forked children default to non-durable. Backends without durability
106
+ support return `ArkerError` code `unsupported_operation` when
107
+ `durable: true` is requested.
100
108
 
101
- ### What the SDK does for you
109
+ ## API Contract
102
110
 
103
- Hidden behind these six methods:
111
+ The SDK request and response types are generated from
112
+ `../contract/openapi.json`. The client itself is handwritten.
104
113
 
105
- - **Write strategy**: files up to 100 MB. Small payloads go in one call;
106
- larger ones use a direct upload path so the bytes don't traverse the
107
- API layer. `writeFile` resolves once the bytes are durably stored.
108
- - **Read coalescing**: `readFile` always resolves to a `Uint8Array`,
109
- regardless of whether the server inlined the content or returned a
110
- signed URL.
111
- - **Idempotent retry**: transient errors are retried with exponential
112
- backoff. Writes are server-side idempotent on `upload_id`, so retries
113
- never produce duplicates.
114
- - **Path validation**: only `/home/user/...` paths accepted; `..` rejected.
114
+ After updating the vendored contract:
115
+
116
+ ```bash
117
+ npm run generate:api-types
118
+ npm run check:api-types
119
+ ```
115
120
 
116
- ## Demo / smoke test
121
+ ## Routing
117
122
 
118
- Run the full surface against a live deployment:
123
+ With `region: "aws-us-west-2"`, the SDK uses
124
+ `https://aws-us-west-2.arker.ai` for normal VMs and
125
+ `https://aws-burst-us-west-2.arker.ai/api` for `arkuntu` and burst VM ids.
126
+ The returned `Computer` stays pinned to the endpoint that created it.
127
+
128
+ ## Smoke Test
129
+
130
+ The conformance smoke test uses raw HTTP and checks the fork/run/sync wire
131
+ shape without going through the SDK:
119
132
 
120
133
  ```bash
121
- ARKER_API_KEY=ark_live_... npx tsx tests/demo.ts
134
+ ARKER_API_KEY=ark_live_... \
135
+ ARKER_BASE_URL=https://aws-us-west-2.arker.ai \
136
+ ARKER_SOURCE_VM=ubuntu \
137
+ npm run smoke
122
138
  ```
123
139
 
124
- It exercises every method (`list`, `vm`, `fork`, `run`, `sync.writeFile`,
125
- `sync.readFile`, error path, child fork, `delete`) and prints what each
126
- call hits on the wire — useful as living documentation.
140
+ To compare two backends:
141
+
142
+ ```bash
143
+ ARKER_API_KEY=ark_live_... \
144
+ ARKER_SMOKE_TARGETS='[
145
+ {"name":"burst","baseUrl":"https://aws-burst-us-west-2.arker.ai/api","source":"01KQH2ADR3DCAJF06N4R453WPJ_uswe"},
146
+ {"name":"ubuntu","baseUrl":"https://aws-us-west-2.arker.ai","source":"ubuntu"}
147
+ ]' \
148
+ npm run smoke
149
+ ```
127
150
 
128
- To fork from a specific source VM instead of the default `arkuntu`:
151
+ ## Demo
129
152
 
130
153
  ```bash
131
- ARKER_API_KEY=ark_live_... ARKER_SOURCE_VM=01KQ... npx tsx tests/demo.ts
154
+ ARKER_API_KEY=ark_live_... \
155
+ ARKER_REGION=aws-us-west-2 \
156
+ ARKER_SOURCE_VM=ubuntu \
157
+ npm run demo
132
158
  ```
133
159
 
134
160
  ## License
135
161
 
136
- Apache-2.0.
162
+ Apache-2.0