@kosa8/sdk 0.1.0 → 0.3.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 +82 -4
- package/dist/index.cjs +748 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +546 -1
- package/dist/index.d.ts +546 -1
- package/dist/index.js +737 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @kosa8/sdk
|
|
2
2
|
|
|
3
|
-
Typed TypeScript client for the [kosa8](https://kosa8.com) native API. kosa8 gives every AI agent its own sealed machine: a real microVM with its own kernel, with the network closed except for the hosts on an allow-list you write. This package talks to the kosa8 daemon over its unix socket. It covers sandboxes, running and exec'ing commands, checkpoint and rewind, fork, explore, and snapshots.
|
|
3
|
+
Typed TypeScript client for the [kosa8](https://kosa8.com) native API. kosa8 gives every AI agent its own sealed machine: a real microVM with its own kernel, with the network closed except for the hosts on an allow-list you write. This package talks to the kosa8 daemon over its unix socket. It covers sandboxes, running and exec'ing commands, the sandbox toolbox (code, files, git, shell sessions, terminals, previews, lifecycle, templates), checkpoint and rewind, fork, explore, and snapshots.
|
|
4
4
|
|
|
5
5
|
- No runtime dependencies. It uses Node's own `http` with `socketPath`.
|
|
6
6
|
- ESM and CommonJS builds with `.d.ts` types. Node 18 or later.
|
|
@@ -83,10 +83,17 @@ All methods return promises. Every method takes `{ approval?, signal? }` as its
|
|
|
83
83
|
| `host` | `KOSA8_HOST` | remote worker: `https://…`, `tcp://host:port`, `host:port`, or `unix:///path.sock` |
|
|
84
84
|
| `token` | `KOSA8_TOKEN` (with `KOSA8_HOST`) | bearer token for a remote worker |
|
|
85
85
|
| `insecure` | `KOSA8_INSECURE=1` | allow plain http to a non-loopback host (refused by default, as in the CLI) |
|
|
86
|
+
| `workerFile` | `$HOME/.kosa8/worker.json` | the CLI's saved worker connection, used when no `host`, `KOSA8_HOST` or `socketPath` is given; `null` reads none |
|
|
86
87
|
| `actor` | `'tool'` | `X-Kosa8-Actor`: how the audit log files these requests (`human`, `agent`, `tool`) |
|
|
87
88
|
| `agentToken` | `KOSA8_AGENT_TOKEN` | agent identity token, for policies that require one |
|
|
88
89
|
| `checkpointStore` | `FileCheckpointStore` (`~/.kosa8/checkpoints.json`) | where checkpoint stacks live; `MemoryCheckpointStore` keeps them in memory |
|
|
89
90
|
|
|
91
|
+
Where requests go, in order: `host` or `KOSA8_HOST`; else the connection the
|
|
92
|
+
CLI saved (`kosa8 wsl setup` on Windows, where the engine runs in WSL2, or
|
|
93
|
+
`kosa8 worker join` anywhere), with its token; else the local daemon's socket.
|
|
94
|
+
So on Windows, once `kosa8 wsl setup` has run, `new Kosa8Client()` reaches the
|
|
95
|
+
WSL2 engine with no configuration.
|
|
96
|
+
|
|
90
97
|
### Status
|
|
91
98
|
|
|
92
99
|
| method | endpoint |
|
|
@@ -99,7 +106,7 @@ All methods return promises. Every method takes `{ approval?, signal? }` as its
|
|
|
99
106
|
|
|
100
107
|
| method | endpoint |
|
|
101
108
|
| --- | --- |
|
|
102
|
-
| `createSandbox({ name?, cpus?, memMb?, diskGb?, mounts?, egress?, egressAllow?, capture?, labels?, broker? })` → `{ sandbox, boot_ms, broker? }` | `POST /kosa8/v1/sandboxes` |
|
|
109
|
+
| `createSandbox({ name?, cpus?, memMb?, diskGb?, mounts?, egress?, egressAllow?, capture?, labels?, broker?, autoStopMin?, ttlMin?, autoDeleteMin?, ephemeral?, template? })` → `{ sandbox, boot_ms, broker? }` | `POST /kosa8/v1/sandboxes` |
|
|
103
110
|
| `listSandboxes()` → `Sandbox[]` | `GET /kosa8/v1/sandboxes` |
|
|
104
111
|
| `getSandbox(idOrName)` → `Sandbox \| undefined` | (from the list) |
|
|
105
112
|
| `startSandbox(id)` | `POST /kosa8/v1/sandboxes/{id}/up` |
|
|
@@ -109,7 +116,7 @@ All methods return promises. Every method takes `{ approval?, signal? }` as its
|
|
|
109
116
|
| `sandboxImages(id)`, `sandboxContainers(id)` | `GET …/images`, `GET …/containers` |
|
|
110
117
|
| `sandboxEgress(id)` → the policy enforced, and pass/deny counters | `GET …/egress` |
|
|
111
118
|
|
|
112
|
-
`egress` takes `'deny'`, `'any'`, `'fakenet'`, `'allow:a.com,*.b.com,10.0.0.0/8'` or `{ allow: [...] }`. `egressAllow: [...]` is shorthand for the allow-list. If you set neither, the daemon applies the org policy's default egress, or `any` when there is none. `broker` puts the sandbox behind kosa8's egress broker: real credentials stay on the host and the sandbox holds only placeholders (returned in `broker.placeholders`).
|
|
119
|
+
`egress` takes `'deny'`, `'any'`, `'fakenet'`, `'allow:a.com,*.b.com,10.0.0.0/8'` or `{ allow: [...] }`. `egressAllow: [...]` is shorthand for the allow-list. If you set neither, the daemon applies the org policy's default egress, or `any` when there is none. `broker` puts the sandbox behind kosa8's egress broker: real credentials stay on the host and the sandbox holds only placeholders (returned in `broker.placeholders`). `autoStopMin` stops the sandbox after that many idle minutes, `ttlMin` that long after it was created, `autoDeleteMin` deletes it once it has been stopped that long, and `ephemeral` deletes it whenever it stops. `template` starts it with a template's image (see Toolbox).
|
|
113
120
|
|
|
114
121
|
### Run and exec
|
|
115
122
|
|
|
@@ -125,6 +132,47 @@ All methods return promises. Every method takes `{ approval?, signal? }` as its
|
|
|
125
132
|
|
|
126
133
|
`ExecResult` is `{ exitCode, stdout, stderr, stdoutBytes, stderrBytes }`. A non-zero exit code is a result, not an error.
|
|
127
134
|
|
|
135
|
+
### Toolbox
|
|
136
|
+
|
|
137
|
+
The toolbox is for agents that want to work in a sandbox the way they work on a laptop: run a snippet, read and write files, use git, keep a shell. None of it needs an image pulled first; the daemon picks one (or the sandbox template's).
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
const sb = kosa8.sandbox('demo');
|
|
141
|
+
|
|
142
|
+
const r = await sb.runCode('print(6 * 7)'); // python by default
|
|
143
|
+
console.log(r.ok, r.stdout, r.artifacts); // matplotlib charts come back as PNG bytes
|
|
144
|
+
|
|
145
|
+
await sb.writeFile('/workspace/app.py', 'print("hi")\n');
|
|
146
|
+
console.log(await sb.readText('/workspace/app.py'));
|
|
147
|
+
|
|
148
|
+
await sb.gitClone('https://github.com/octocat/Hello-World.git', { path: 'hw', depth: 1 });
|
|
149
|
+
await sb.gitCommit('hw', 'my change', { authorName: 'Agent', authorEmail: 'agent@example.com' });
|
|
150
|
+
|
|
151
|
+
await sb.shell('cd /tmp && export FOO=bar'); // the session "main" is made on first use
|
|
152
|
+
console.log((await sb.shell('echo $FOO; pwd')).stdout); // "bar\n/tmp\n"
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
| method | endpoint |
|
|
156
|
+
| --- | --- |
|
|
157
|
+
| `runCode(id, code, { language?, image?, env?, timeoutS? })` → `CodeResult` | `POST …/code` |
|
|
158
|
+
| `listFiles(id, path?)`, `statFile(id, path)` → `FileInfo` | `GET …/files`, `GET …/files/stat` |
|
|
159
|
+
| `readFile(id, path)` → `Buffer`, `readText(id, path)` → `string` | `GET …/files/content` |
|
|
160
|
+
| `writeFile(id, path, data, { mode? })` | `PUT …/files/content` (bytes, a string or an async iterable) |
|
|
161
|
+
| `deleteFile(id, path, { recursive? })`, `makeDir(id, path)`, `moveFile(id, from, to)` | `DELETE …/files`, `POST …/files/mkdir`, `POST …/files/move` |
|
|
162
|
+
| `git(id, op, fields)` → `GitResult` | `POST …/git` (`clone`, `status`, `add`, `commit`, `push`, `pull`, `checkout`, `branches`, `log`, `diff`) |
|
|
163
|
+
| `gitClone(id, url, { path?, depth?, … })`, `gitStatus(id, path)`, `gitCommit(id, path, message, { addAll? })` | `POST …/git` (`gitCommit` stages everything first) |
|
|
164
|
+
| `createSession(id, name?, { image?, env? })`, `listSessions(id)`, `deleteSession(id, name)` | `POST/GET …/sessions`, `DELETE …/sessions/{name}` |
|
|
165
|
+
| `sessionExec(id, session, command, { timeoutS? })` → `ShellResult` | `POST …/sessions/{name}/exec` |
|
|
166
|
+
| `shell(id, command, { session?, timeoutS? })` → `ShellResult` | as `sessionExec`, making the session (default `main`) on first use |
|
|
167
|
+
| `startPty(id, { image?, command?, cols?, rows?, env? })` → container id, `resizePty(id, cid, cols, rows)` | `POST …/pty`, `POST …/containers/{cid}/resize` |
|
|
168
|
+
| `createPreview(id, port, { session?, container?, expiresInS? })` → `{ url, token, expires_at, … }` | `POST …/previews` |
|
|
169
|
+
| `revokePreviews()` | `POST /kosa8/v1/previews/rotate` (ends every preview link) |
|
|
170
|
+
| `setLifecycle(id, { autoStopMin?, ttlMin?, autoDeleteMin?, ephemeral? })` → `Sandbox` | `POST …/lifecycle` (left out keeps its value, 0 turns it off) |
|
|
171
|
+
| `listTemplates()`, `deleteTemplate(name)` | `GET /kosa8/v1/templates`, `DELETE /kosa8/v1/templates/{name}` |
|
|
172
|
+
| `buildTemplate(name, contextDir?, { dockerfile?, buildArgs?, egress?, replace?, onOutput? })` → final frame | `POST /kosa8/v1/templates` |
|
|
173
|
+
|
|
174
|
+
`CodeResult` is `{ ok, exitCode, stdout, stderr, language, image, artifacts, durationMs, timedOut, truncated }` and `ShellResult` is `{ ok, exitCode, stdout, stderr, cwd, durationMs, timedOut, truncated }`. As with `run`, a non-zero exit code is a result, and so is a git failure (`ok: false`). Only a refusal throws. `buildTemplate` packs the context directory as a tar, leaving out what `.dockerignore` names (paths and their prefixes), builds it in a throwaway sandbox, and keeps the image, so that `createSandbox({ template })` starts from it. A failed build throws `Kosa8StreamError`. The sandbox handle has every per-sandbox toolbox method, with the sandbox id already bound (`sb.runCode(code)`, `sb.sessions()`, …).
|
|
175
|
+
|
|
128
176
|
### Snapshots, fork, explore
|
|
129
177
|
|
|
130
178
|
| method | endpoint |
|
|
@@ -148,6 +196,36 @@ All methods return promises. Every method takes `{ approval?, signal? }` as its
|
|
|
148
196
|
|
|
149
197
|
The daemon has no checkpoint endpoint. Checkpoints are bookkeeping kept on the client side over snapshots, exactly as in the kosa8 CLI and MCP server. By default they go in the CLI's own store, so `kosa8 rewind` sees checkpoints you took with the SDK, and the SDK sees the CLI's. A rewind deletes the sandbox and restores the checkpoint under the same name, and checkpoints taken after the target are discarded.
|
|
150
198
|
|
|
199
|
+
## kosa8 Cloud
|
|
200
|
+
|
|
201
|
+
The same client shape, against kosa8 Cloud instead of the local daemon. Make
|
|
202
|
+
an API key in the cloud dashboard and set `KOSA8_API_KEY`.
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
import { Kosa8Cloud, Kosa8PaymentRequiredError } from '@kosa8/sdk';
|
|
206
|
+
|
|
207
|
+
const cloud = new Kosa8Cloud(); // or new Kosa8Cloud({ apiKey: 'k8c_live_…' })
|
|
208
|
+
|
|
209
|
+
const box = await cloud.sandboxes.create({ template: 'python', sleepAfterS: 600 });
|
|
210
|
+
const r = await box.exec(['python3', '-c', 'print(6 * 7)']);
|
|
211
|
+
console.log(r.exitCode, r.stdout); // 0 "42\n"
|
|
212
|
+
await box.sleep(); // costs only snapshot storage while asleep
|
|
213
|
+
await box.exec('ls /workspace'); // wakes it, files and all
|
|
214
|
+
await box.delete();
|
|
215
|
+
|
|
216
|
+
await cloud.functions.deploy('hello', {
|
|
217
|
+
runtime: 'python3.12',
|
|
218
|
+
code: 'def handler(event):\n return {"hello": event.get("name", "world")}\n',
|
|
219
|
+
});
|
|
220
|
+
const out = await cloud.functions.invoke('hello', { name: 'kosa8' });
|
|
221
|
+
console.log(out.ok, out.result); // true { hello: 'kosa8' }
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
A new organization runs nothing until a payment method is on file; until
|
|
225
|
+
then creating a sandbox throws `Kosa8PaymentRequiredError` (`code`
|
|
226
|
+
`payment_required`), whose message says what to do. Every error from the
|
|
227
|
+
cloud carries its stable `code`.
|
|
228
|
+
|
|
151
229
|
## Errors
|
|
152
230
|
|
|
153
231
|
Every error extends `Kosa8Error`. An error from the daemon carries `status`, `daemonMessage` (the daemon's `error`, verbatim) and `remediation` (what to do), and its `message` is `"<error> (<remediation>)"`.
|
|
@@ -162,7 +240,7 @@ Every error extends `Kosa8Error`. An error from the daemon carries `status`, `da
|
|
|
162
240
|
| `Kosa8NotFoundError` | 404 |
|
|
163
241
|
| `Kosa8ConflictError` | 409: for example the sandbox exists but is stopped, or a snapshot id is already taken |
|
|
164
242
|
| `Kosa8UnsupportedError` | 501: no microVM backend on this platform |
|
|
165
|
-
| `Kosa8StreamError` | a run or
|
|
243
|
+
| `Kosa8StreamError` | a run, exec or template build ended with an error frame (output so far in `stdout`/`stderr`), or a file read was cut short |
|
|
166
244
|
| `Kosa8CheckpointError` | no checkpoint to rewind to, or an invalid name |
|
|
167
245
|
|
|
168
246
|
When an action needs approval, a human opens a request (`error.approval.request_command`), the approvers sign it, and you repeat the call with the id:
|