feat(js): add Computers client with interactive terminal support#8
Conversation
Bring the JS SDK to feature-parity with the Python SDK for the
Computers service: all 7 HTTP methods plus an interactive WebSocket
terminal. Bumps @celestoai/sdk to 0.2.0.
What's new
- js/src/computers/client.ts — ComputersClient with create, list,
get, exec, stop, start, delete. Public API is camelCase (cpus,
memory, ramMb, exitCode); wire DTOs map to snake_case (vcpus,
ram_mb, exit_code) to match the Python SDK and backend.
- js/src/computers/terminal.ts — openTerminalConnection() and a
Terminal class extending EventEmitter with typed on/once overloads.
Emits "data" (string | Buffer), "close" (code, reason), "error".
write(), resize(cols, rows), close() round out the control surface.
- ComputersClient.openTerminal(nameOrId) resolves name → canonical
ID via GET before the WebSocket handshake (CLAUDE.md rule: the WS
endpoint does not resolve names). Sends Authorization header on
connect AND the legacy first-message {"token": ...} JSON for
backend compat, matching src/celesto/computer.py.
- CelestoClient now composes both gatekeeper and computers.
Tests
- js/tests/computers.test.ts — 7 unit tests mocking fetch via
ClientConfig.fetch. Covers payload shape, defaults, wire-format
mapping, error surfacing (CelestoApiError on 404).
- js/tests/terminal.test.ts — 7 unit tests using a real in-process
WebSocketServer on 127.0.0.1:0. Covers handshake (URL, auth
header), first-message token, text + binary data, write/resize,
close code/reason propagation, post-close write guard, missing
token rejection.
- Runner: node:test via tsx (new devDep). Zero framework ceremony.
- .github/workflows/js-test.yml now runs `npm test` after build.
Dependencies
- Runtime: ws ^8.18.0 (first runtime dep — only for the terminal)
- Dev: @types/node, @types/ws, tsx
Package plumbing
- New ./computers subpath export in package.json (mirrors
./gatekeeper). tsup builds computers/index entry.
- tsconfig.json now includes tests/ so lint covers them.
- README rewritten to cover both Gatekeeper and Computers with
copy-paste quickstart snippets.
- CLAUDE.md JS section documents the new scope, terminal rules,
camelCase ↔ snake_case mapping, and testing approach.
What's deliberately out of scope
- No auto-resume for stopped computers in openTerminal(). That's
application logic in the Python CLI, not the SDK class — strict
parity. Users can call start() and poll themselves.
- No release workflow changes — publish is still manual.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 10 minutes and 16 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (14)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Both Python and JS test workflows now run on every push/PR regardless of which files changed. Avoids deadlocks if either check is ever promoted to a required status — no more "check pending because the paths filter didn't match" scenarios. The extra ~30s of CI on irrelevant PRs is worth the simplicity. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CLAUDE.md line 12 still said "Gatekeeper-only today" after the Computers module was added in PR #8. Update to "Gatekeeper + Computers" to match the detailed section at line 57. - README: "long running" → "long-running" (compound adjective). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…#9) * refactor(js)!: rename CelestoClient to Celesto, update README Align the JS SDK's main class name with the Python SDK so both use `Celesto`. Simpler, consistent across languages: Python: from celesto import Celesto JS/TS: import { Celesto } from "@celestoai/sdk" Also promotes JS/TS to a first-class citizen in the root README: mentioned in the intro, shown in the install block, and given its own Quick Start example alongside Python. BREAKING CHANGE: `CelestoClient` is removed. Replace with `Celesto`. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * update * docs: fix stale JS SDK scope in CLAUDE.md, hyphenate long-running - CLAUDE.md line 12 still said "Gatekeeper-only today" after the Computers module was added in PR #8. Update to "Gatekeeper + Computers" to match the detailed section at line 57. - README: "long running" → "long-running" (compound adjective). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(js)!: drop ws dependency, harden error handling Three changes that make the JS SDK leaner and more robust: 1. Replace openTerminal() with getTerminalConnection() The ws package was the only runtime dependency, used solely for the interactive terminal — a niche feature most users never touch. Replace with a lightweight helper that returns { url, headers, firstMessage } so users can BYO any WebSocket library (ws, Node 22+ built-in, etc.). Delete terminal.ts and its tests entirely. Result: zero runtime dependencies. 2. Wrap fetch() failures in CelestoNetworkError Previously, DNS failures, timeouts, and offline errors bubbled up as raw TypeError/DOMException. Now they're caught in http.ts and re-thrown as CelestoNetworkError, which extends the new CelestoError base class. CelestoApiError also extends CelestoError, so users can catch(CelestoError) for everything or be specific with CelestoApiError / CelestoNetworkError. 3. Default provider to "google_drive" in gatekeeper.connect() GateKeeper currently only supports Google Drive. Python defaults this, JS forced the user to pass it every time. Now optional. Tests: 10 pass (was 14 — 7 terminal tests removed, 3 new tests added for network errors, getTerminalConnection, and error hierarchy). BREAKING: openTerminal() and Terminal class removed. Use getTerminalConnection() with your own WebSocket client instead. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
create,list,get,exec,stop,start,delete) plus an interactiveopenTerminal()using WebSockets.What's new
`js/src/computers/client.ts`
`ComputersClient` with all 7 HTTP methods. Public API is camelCase; wire DTOs map to the same snake_case payloads the Python SDK uses (`vcpus`, `ram_mb`, `exit_code`, etc.), following the Gatekeeper pattern.
```ts
const celesto = new CelestoClient({ token: process.env.CELESTO_API_KEY });
const computer = await celesto.computers.create({ cpus: 2, memory: 2048 });
const result = await celesto.computers.exec(computer.id, "uname -a");
console.log(result.stdout);
await celesto.computers.delete(computer.id);
```
`js/src/computers/terminal.ts`
`openTerminalConnection()` + a `Terminal` class extending `EventEmitter` with typed `on` / `once` overloads.
```ts
const terminal = await celesto.computers.openTerminal(computer.id);
terminal.on("data", (chunk) => process.stdout.write(chunk));
terminal.on("close", (code, reason) => console.log(`closed: ${code} ${reason}`));
terminal.write("ls\n");
terminal.resize(120, 40);
await terminal.close();
```
Terminal rules (mirror `src/celesto/computer.py` and the rules in CLAUDE.md):
Tests — `js/tests/`
```
tests 14
pass 14
fail 0
```
Dependencies
Package plumbing
What's deliberately out of scope
Test plan
```ts
const celesto = new CelestoClient({ token: process.env.CELESTO_API_KEY });
const c = await celesto.computers.create({ cpus: 1, memory: 1024 });
const r = await celesto.computers.exec(c.id, "echo hi");
console.log(r.stdout);
const t = await celesto.computers.openTerminal(c.id);
t.on("data", (d) => process.stdout.write(d));
t.write("ls\n");
setTimeout(() => t.close(), 2000);
```
🤖 Generated with Claude Code