Skip to content

feat(js): add Computers client with interactive terminal support#8

Merged
aniketmaurya merged 2 commits into
mainfrom
feat/js-computers
Apr 16, 2026
Merged

feat(js): add Computers client with interactive terminal support#8
aniketmaurya merged 2 commits into
mainfrom
feat/js-computers

Conversation

@aniketmaurya
Copy link
Copy Markdown
Collaborator

Summary

  • Bring the JS SDK to feature-parity with the Python SDK for the Computers service: all 7 HTTP methods (create, list, get, exec, stop, start, delete) plus an interactive openTerminal() using WebSockets.
  • Bumps `@celestoai/sdk` to 0.2.0. `CelestoClient` now composes both `gatekeeper` and `computers` sub-clients.
  • Introduces the first unit tests for the JS SDK: 14 total, using Node's built-in `node:test` runner via `tsx`. No test framework ceremony.

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):

  • Always resolve name → canonical ID via `get()` before the WebSocket handshake. The WS endpoint does not resolve names. `ComputersClient.openTerminal()` does this for you.
  • Sends `Authorization: Bearer` header on the WS handshake and the legacy first-message `{"token": ...}` JSON after connect for backend compatibility.
  • Resize frames are `{"type": "resize", "cols": N, "rows": N}`.
  • Does not auto-resume stopped computers. That's application logic — the Python CLI does it in its CLI layer, not in the SDK class. Strict parity with the Python SDK surface.

Tests — `js/tests/`

  • `computers.test.ts` — 7 unit tests. Mocks `fetch` via `ClientConfig.fetch`. Covers payload shape, defaults, wire mapping, and `CelestoApiError` on 404s.
  • `terminal.test.ts` — 7 unit tests. Starts a real in-process `WebSocketServer` from the `ws` package on `127.0.0.1:0` instead of mocking the library. Covers handshake URL + auth header, first-message token, text + binary data, write/resize, close-code propagation, post-close write guards, and missing-token rejection.
  • CI job `js-test.yml` now runs `npm test` as a third step after lint and build.

```

tests 14

pass 14

fail 0

```

Dependencies

  • Runtime (new): `ws` `^8.18.0` — the first runtime dependency the package has ever had. Only used by the terminal; HTTP methods are still `fetch`-based.
  • Dev (new): `@types/node`, `@types/ws`, `tsx`

Package plumbing

  • New `./computers` subpath export in `package.json`, mirroring `./gatekeeper`. `tsup` builds a `computers/index` entry (ESM + CJS + DTS).
  • `tsconfig.json` now includes `tests` so the lint step type-checks the test files.
  • `README` rewritten to cover both Gatekeeper and Computers with copy-paste quickstart snippets.
  • `CLAUDE.md` JS section expanded to document the new scope, terminal rules, and testing approach.

What's deliberately out of scope

  • Auto-resume for stopped computers in `openTerminal()`. The Python CLI does this in its CLI layer (`src/celesto/computer.py:204`), but the `Computers` SDK class itself doesn't — strict parity. A consumer that wants auto-resume can do `await get(); if stopped: await start(); poll; await openTerminal()` in ~10 lines.
  • Release workflow. Publishing `@celestoai/sdk` is still manual. A `js-release.yml` can come in a follow-up once an `NPM_TOKEN` secret is in place.

Test plan

  • `JS Tests` workflow runs on this PR and passes (lint, build, 14 unit tests)
  • `Tests` (Python) workflow is skipped (path filters — only `js/**` and `.github/workflows/js-test.yml` changed here, except for `CLAUDE.md` which neither workflow watches)
  • After merge: `cd js && npm ci && npm run lint && npm run build && npm test` in a fresh clone to confirm reproducibility
  • Manual smoke test against a live computer:
    ```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

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>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 15, 2026

Warning

Rate limit exceeded

@aniketmaurya has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 10 minutes and 16 seconds before requesting another review.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b481dd0d-05bb-4897-b0d0-783c9039a6cb

📥 Commits

Reviewing files that changed from the base of the PR and between 996ecc7 and 81dd580.

⛔ Files ignored due to path filters (1)
  • js/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (14)
  • .github/workflows/js-test.yml
  • .github/workflows/test.yml
  • CLAUDE.md
  • js/README.md
  • js/package.json
  • js/src/computers/client.ts
  • js/src/computers/index.ts
  • js/src/computers/terminal.ts
  • js/src/computers/types.ts
  • js/src/index.ts
  • js/tests/computers.test.ts
  • js/tests/terminal.test.ts
  • js/tsconfig.json
  • js/tsup.config.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/js-computers

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

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>
@aniketmaurya aniketmaurya merged commit 21144ee into main Apr 16, 2026
3 checks passed
@aniketmaurya aniketmaurya deleted the feat/js-computers branch April 16, 2026 00:18
aniketmaurya added a commit that referenced this pull request Apr 16, 2026
- 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>
aniketmaurya added a commit that referenced this pull request Apr 16, 2026
…#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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant