Skip to content

Commit 6f6d687

Browse files
opencolinclaude
andcommitted
Add ComputeSDK provider example
Run code on Tenki through ComputeSDK's unified sandbox interface, using the official @computesdk/tenki provider (computesdk/computesdk#584): runCommand via sh -lc, the native filesystem API, and destroy. verify.mjs proves create -> runCommand (42) -> filesystem round-trip -> destroy against the live API. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 0ccf76d commit 6f6d687

5 files changed

Lines changed: 147 additions & 1 deletion

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ Give an agent a sandboxed place to run the code it writes.
6161
| [MCP server](examples/mcp-tenki-sandbox/) | Sandbox tools for Claude, Cursor, or any MCP client |
6262
| [Composio](examples/composio-tenki/) | Tenki tools in a Composio agent |
6363
| [Covalent](examples/covalent-tenki/) | Each workflow task in its own microVM |
64+
| [ComputeSDK](examples/computesdk-tenki/) | Tenki as a provider for the unified sandbox interface |
6465

6566
### Migrating from another provider
6667

@@ -106,7 +107,7 @@ These open-source projects ship a Tenki provider or backend out of the box:
106107
| [RAGFlow](https://github.com/infiniflow/ragflow) | Sandbox provider for the RAG engine's code executor |
107108
| [DeerFlow](https://github.com/bytedance/deer-flow) | Sandbox provider for ByteDance's SuperAgent harness |
108109
| [AgentBox](https://github.com/madarco/agentbox) | Provider for running parallel agents in sandboxed VMs |
109-
| [ComputeSDK](https://github.com/computesdk/computesdk) | Tenki provider for the multi-provider compute toolkit |
110+
| [ComputeSDK](https://github.com/computesdk/computesdk) | Tenki provider for the multi-provider compute toolkit [example](examples/computesdk-tenki/) |
110111
| [OpenHermit](https://github.com/HCF-STUDIOS/openhermit) | Sandboxed exec backend for AI agent fleets |
111112

112113
## Contributing
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Tenki as a ComputeSDK provider
2+
3+
Run code on [Tenki](https://tenki.cloud) through [ComputeSDK](https://github.com/computesdk/computesdk) — one `compute.sandbox` interface, swappable backends. The official [`@computesdk/tenki`](https://www.npmjs.com/package/@computesdk/tenki) provider maps that interface onto Tenki microVMs: shell commands, a native filesystem API, and public preview URLs, with no code changes if you later switch providers.
4+
5+
## The code (`run.mjs`)
6+
7+
```js
8+
import { compute } from "computesdk";
9+
import { tenki } from "@computesdk/tenki";
10+
11+
compute.setConfig({ provider: tenki({ apiKey: process.env.TENKI_API_KEY }) });
12+
13+
const sandbox = await compute.sandbox.create();
14+
try {
15+
const hello = await sandbox.runCommand('echo "Hello from $(uname -sr)"');
16+
console.log(hello.stdout.trim());
17+
18+
await sandbox.filesystem.writeFile("/home/tenki/app.py", 'print(6 * 7)\n');
19+
const result = await sandbox.runCommand("python3 /home/tenki/app.py");
20+
console.log(result.stdout.trim()); // 42
21+
} finally {
22+
await sandbox.destroy();
23+
}
24+
```
25+
26+
## Run it
27+
28+
```bash
29+
npm install
30+
export TENKI_API_KEY=tk_... # or TENKI_AUTH_TOKEN; from your Tenki workspace settings
31+
node run.mjs # Hello from Linux ... / 42
32+
```
33+
34+
Workspace API keys infer their workspace server-side; set `TENKI_WORKSPACE_ID` only for trusted service credentials that need explicit scope.
35+
36+
## Beyond commands and files
37+
38+
```js
39+
// Serve something, then expose the port at a public https://<slug>.sb.tenki.sh URL
40+
await sandbox.runCommand("python3 -m http.server 3000", { background: true });
41+
const url = await sandbox.getUrl({ port: 3000 });
42+
43+
// Escape hatch: the underlying @tenkicloud/sandbox Session for SSH, volumes, snapshots
44+
const session = await sandbox.getInstance();
45+
```
46+
47+
## Notes
48+
49+
- **`runCommand` wraps commands in `sh -lc`** (Tenki's raw exec is argv-only, no shell), so pipes, globs, and env expansion behave as expected.
50+
- **The filesystem API is native**`writeFile`/`readFile`/`mkdir`/`readdir`/`exists`/`remove` ride Tenki's data plane, not shell commands, so any path or content works without escaping hazards.
51+
- **Long-running processes:** use `{ background: true }` rather than a trailing `&` — a bare `&` holds the exec output stream open.
52+
- Requires Node 20+ (the Tenki SDK's gRPC transport depends on it).
53+
- Provider source: [`packages/tenki`](https://github.com/computesdk/computesdk/tree/main/packages/tenki) in the ComputeSDK repo, added in [computesdk#584](https://github.com/computesdk/computesdk/pull/584).
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"name": "computesdk-tenki",
3+
"private": true,
4+
"type": "module",
5+
"description": "Cookbook example — run code on Tenki through ComputeSDK's unified provider interface.",
6+
"dependencies": {
7+
"@computesdk/tenki": "^0.1.3",
8+
"computesdk": "^4.1.4"
9+
}
10+
}

examples/computesdk-tenki/run.mjs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Run code on Tenki through ComputeSDK — one provider interface, swappable backends.
2+
// The official @computesdk/tenki provider drives Tenki microVMs under the hood.
3+
import { compute } from "computesdk";
4+
import { tenki } from "@computesdk/tenki";
5+
6+
// The provider also reads TENKI_API_KEY / TENKI_AUTH_TOKEN from the environment.
7+
compute.setConfig({ provider: tenki({ apiKey: process.env.TENKI_API_KEY }) });
8+
9+
const sandbox = await compute.sandbox.create();
10+
try {
11+
// runCommand wraps the command in `sh -lc`, so pipes, globs, and env expansion work.
12+
const hello = await sandbox.runCommand('echo "Hello from $(uname -sr)"');
13+
console.log(hello.stdout.trim());
14+
15+
// Native filesystem API — files move over Tenki's data plane, not through shell quoting.
16+
await sandbox.filesystem.writeFile("/home/tenki/app.py", 'print(6 * 7)\n');
17+
const result = await sandbox.runCommand("python3 /home/tenki/app.py");
18+
console.log(result.stdout.trim()); // 42
19+
} finally {
20+
await sandbox.destroy();
21+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* Proves the ComputeSDK Tenki provider works against live Tenki: create a
3+
* sandbox through `compute`, run a command (assert 42), round-trip a file
4+
* through the native filesystem API, then destroy the sandbox.
5+
* Token/workspace from env (CI) or ~/.config/tenki/config.yaml (local `tenki login`).
6+
* Exits non-zero on any failure.
7+
*/
8+
import { compute } from "computesdk";
9+
import { tenki } from "@computesdk/tenki";
10+
import { readFileSync } from "node:fs";
11+
import { homedir } from "node:os";
12+
13+
const cfg = (key) => {
14+
try {
15+
const c = readFileSync(`${homedir()}/.config/tenki/config.yaml`, "utf8");
16+
return (c.match(new RegExp(`^${key}:\\s*(.+)$`, "m"))?.[1] ?? "").trim();
17+
} catch {
18+
return "";
19+
}
20+
};
21+
22+
const apiKey = process.env.TENKI_AUTH_TOKEN || process.env.TENKI_API_KEY || cfg("auth_token");
23+
const workspaceId = process.env.TENKI_WORKSPACE_ID || cfg("current_workspace_id") || undefined;
24+
if (!apiKey) {
25+
console.error("No token. Set TENKI_AUTH_TOKEN, or run `tenki login`.");
26+
process.exit(1);
27+
}
28+
29+
compute.setConfig({ provider: tenki({ apiKey, ...(workspaceId ? { workspaceId } : {}) }) });
30+
31+
let sandbox;
32+
try {
33+
sandbox = await compute.sandbox.create();
34+
35+
// 1) runCommand (goes through `sh -lc`) → assert stdout
36+
const r = await sandbox.runCommand("python3 -c 'print(6 * 7)'");
37+
if (!(r.exitCode === 0 && r.stdout.trim() === "42")) {
38+
throw new Error(`runCommand: exit ${r.exitCode}, stdout ${JSON.stringify(r.stdout)}, stderr ${JSON.stringify(r.stderr)}`);
39+
}
40+
41+
// 2) native filesystem API → write, assert exists, read back
42+
const path = "/home/tenki/verify-note.txt";
43+
const content = "written via the ComputeSDK filesystem API\n";
44+
await sandbox.filesystem.writeFile(path, content);
45+
if (!(await sandbox.filesystem.exists(path))) throw new Error("exists() false after writeFile");
46+
const back = await sandbox.filesystem.readFile(path);
47+
if (back !== content) throw new Error(`file round-trip mismatch: ${JSON.stringify(back)}`);
48+
49+
console.log("✓ computesdk-tenki: compute.sandbox.create → runCommand (42) → filesystem round-trip → destroy");
50+
} catch (e) {
51+
console.error("✗ " + (e?.message ?? e));
52+
process.exitCode = 1;
53+
} finally {
54+
if (sandbox) {
55+
try {
56+
await sandbox.destroy();
57+
} catch {
58+
/* self-reaps via idle/lifetime caps */
59+
}
60+
}
61+
}

0 commit comments

Comments
 (0)