opencode-cloud-core 23.0.0

Core library for opencode-cloud - config management, singleton enforcement, and shared utilities
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
# opencode-cloud

[![CI](https://github.com/pRizz/opencode-cloud/actions/workflows/ci.yml/badge.svg)](https://github.com/pRizz/opencode-cloud/actions/workflows/ci.yml)
[![Mirror](https://img.shields.io/badge/mirror-gitea-blue?logo=gitea)](https://gitea.com/pRizz/opencode-cloud)
[![crates.io](https://img.shields.io/crates/v/opencode-cloud.svg)](https://crates.io/crates/opencode-cloud)
[![Crates Downloads](https://img.shields.io/crates/d/opencode-cloud.svg)](https://crates.io/crates/opencode-cloud)
[![npm Downloads](https://img.shields.io/npm/dt/opencode-cloud?logo=npm)](https://www.npmjs.com/package/opencode-cloud)
[![Docker Hub](https://img.shields.io/docker/v/prizz/opencode-cloud-sandbox?label=docker&sort=semver)](https://hub.docker.com/r/prizz/opencode-cloud-sandbox)
[![Docker Pulls](https://img.shields.io/docker/pulls/prizz/opencode-cloud-sandbox)](https://hub.docker.com/r/prizz/opencode-cloud-sandbox)
[![GHCR](https://img.shields.io/badge/ghcr.io-sandbox-blue?logo=github)](https://github.com/pRizz/opencode-cloud/pkgs/container/opencode-cloud-sandbox)
[![docs.rs](https://docs.rs/opencode-cloud/badge.svg)](https://docs.rs/opencode-cloud)
[![MSRV](https://img.shields.io/badge/MSRV-1.85-blue.svg)](https://blog.rust-lang.org/2025/02/20/Rust-1.85.0.html)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

> [!WARNING]
> This tool is still a work in progress and is rapidly evolving. Expect frequent updates and breaking changes. Follow updates at https://github.com/pRizz/opencode-cloud (mirror: https://gitea.com/pRizz/opencode-cloud). Stability will be announced at some point. Use with caution.

A production-ready toolkit for deploying and managing [opencode](https://github.com/anomalyco/opencode) as a persistent cloud service, **sandboxed inside a Docker container** for isolation and security.

This project uses the opencode fork at https://github.com/pRizz/opencode, which adds additional authentication and security features.

## Quick Deploy (Docker)

Deploy opencode-cloud with one command. Installs Docker if needed (Linux), downloads or refreshes the Docker Compose config, pulls the latest `prizz/opencode-cloud-sandbox:latest` image, reconciles services, and prints the login credentials:

```bash
curl -fsSL https://raw.githubusercontent.com/pRizz/opencode-cloud/main/scripts/quick-deploy.sh | bash
```

Then open [http://localhost:3000](http://localhost:3000) and enter the Initial One-Time Password (IOTP) to complete setup.

> **macOS/Windows:** Install [Docker Desktop]https://www.docker.com/products/docker-desktop/ first, then run the command above.

> **Remote server:** SSH into the server, run the command, then access via SSH tunnel: `ssh -L 3000:localhost:3000 root@<server-ip>`

> **Interactive mode:** Add `--interactive` to be prompted before each step: `curl -fsSL .../scripts/quick-deploy.sh | bash -s -- --interactive`

> **Compose refresh behavior:** By default, the script fetches the latest upstream `docker-compose.yml`. If your local file differs, it is replaced and a backup is written as `docker-compose.yml.bak.<timestamp>`.

> **Image refresh behavior:** By default, the script runs `docker compose pull` before `docker compose up -d`, so rerunning quick deploy updates to the latest image.

## Quick install (cargo)

```bash
cargo install opencode-cloud
opencode-cloud --version
```

## Quick install (npm)

```bash
npx opencode-cloud@latest --version
```

```bash
bunx opencode-cloud@latest --version
```

Or install globally:
```bash
npm install -g opencode-cloud
opencode-cloud --version
```

## Deploy to AWS

[![Deploy to AWS](https://s3.amazonaws.com/cloudformation-examples/cloudformation-launch-stack.png)](https://console.aws.amazon.com/cloudformation/home#/stacks/create/review?templateURL=https://opencode-cloud-templates.s3.us-east-2.amazonaws.com/cloudformation/opencode-cloud-quick.yaml)

Quick deploy provisions a private EC2 instance behind a public ALB with HTTPS.
**A domain name is required** for ACM certificate validation.
**A Route53 hosted zone ID is required** for automated DNS validation.

Docs: `docs/deploy/aws.md` (includes teardown steps and S3 hosting setup for forks)
Credentials: `docs/deploy/aws.md#retrieving-credentials`

## Deploy to Railway

<!-- TODO: Replace TEMPLATE_CODE with the actual Railway template code once the template is created. See docs/deploy/railway.md for template creation instructions. -->
[![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/new/template/TEMPLATE_CODE)

One-click deploy provisions a Railway service with automatic HTTPS.

> **Important:** Attach a Railway Volume mounted to `/home/opencoder/.local/share/opencode` to prevent data loss across redeploys.

Docs: `docs/deploy/railway.md`

## Run with Docker / Docker Desktop

> **Tip:** For a fully automated setup, see [Quick Deploy]#quick-deploy-docker above.

The fastest way to run opencode-cloud locally:

```bash
docker compose up -d
```

This uses the included `docker-compose.yml` which configures all persistent volumes automatically.

Retrieve the Initial One-Time Password (IOTP) and open `http://localhost:3000`:

```bash
docker compose logs | grep -F "INITIAL ONE-TIME PASSWORD (IOTP): " | tail -n1 | sed 's/.*INITIAL ONE-TIME PASSWORD (IOTP): //'
```

Docs: `docs/deploy/docker-desktop.md`

## Deploy to DigitalOcean

### Marketplace (Coming Soon)

DigitalOcean Marketplace one-click deployment is in progress. Support is coming soon.

Docs: `docs/deploy/digitalocean-marketplace.md`

### Droplet (Manual)

SSH into an Ubuntu 24.04 Droplet and run:

```bash
curl -fsSL https://raw.githubusercontent.com/pRizz/opencode-cloud/main/scripts/quick-deploy.sh | bash
```

This installs Docker, by default refreshes the Compose file from upstream (with backup if your local copy differs), pulls the latest image, reconciles services, and prints the IOTP.

Access via SSH tunnel: `ssh -L 3000:localhost:3000 root@<droplet-ip>`, then open `http://localhost:3000`.

Docs: `docs/deploy/digitalocean-droplet.md`

## Features

- **Sandboxed execution** - opencode runs inside a Docker container, isolated from your host system
- **Persistent environment** - Your projects, settings, and shell history persist across restarts
- **Cross-platform CLI** (`opencode-cloud` / `occ`) - Works on Linux and macOS
- **Service lifecycle commands** - start, stop, restart, status, logs
- **Platform service integration** - systemd (Linux) / launchd (macOS) for auto-start on boot
- **Remote host management** - Manage opencode containers on remote servers via SSH

## How it works

opencode-cloud runs opencode inside a Docker container, providing:

- **Isolation** - opencode and its AI-generated code run in a sandbox, separate from your host system
- **Reproducibility** - The container includes a full development environment (languages, tools, runtimes)
- **Persistence** - Docker volumes preserve your work across container restarts and updates
- **Security** - Network exposure is opt-in; by default, the service only binds to localhost

The CLI manages the container lifecycle, so you don't need to interact with Docker directly.

## Docker Images

The sandbox container image is named **`opencode-cloud-sandbox`** (not `opencode-cloud`) to clearly distinguish it from the CLI tool. The preferred way to use and manage the image is via the opencode-cloud CLI ([GitHub](https://github.com/pRizz/opencode-cloud), mirror: https://gitea.com/pRizz/opencode-cloud). It handles image pulling, container setup, and upgrades for you.

**Why use the CLI?** It configures volumes, ports, and upgrades safely, so you don’t have to manage `docker run` flags or image updates yourself.

The image is published to both registries (Docker Hub is the primary distribution):

| Registry | Image |
|----------|-------|
| Docker Hub | [`prizz/opencode-cloud-sandbox`]https://hub.docker.com/r/prizz/opencode-cloud-sandbox |
| GitHub Container Registry | [`ghcr.io/prizz/opencode-cloud-sandbox`]https://github.com/pRizz/opencode-cloud/pkgs/container/opencode-cloud-sandbox |

Pull commands:

Docker Hub:
```bash
docker pull prizz/opencode-cloud-sandbox:latest
```

GitHub Container Registry:
```bash
docker pull ghcr.io/prizz/opencode-cloud-sandbox:latest
```

**For most users:** Just use the CLI - it handles image pulling/building automatically:
```bash
occ start  # Pulls or builds the image as needed
```

**Running the image directly** (without the CLI)? Use Docker Compose or configure named volumes for persistence. See `docs/deploy/docker-desktop.md` for Docker Desktop / `docker run`, or `docs/deploy/railway.md` for Railway.

## Requirements

- **Rust 1.85+** - Install via [rustup]https://rustup.rs: `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh`
- **Docker** - For running the opencode container
- **Supported platforms** - Linux and macOS

## Installation

### Via cargo (recommended)

```bash
cargo install opencode-cloud
occ --version
```

### Via npm

```bash
npx opencode-cloud@latest --version
```

```bash
bunx opencode-cloud@latest --version
```

Or install globally:
```bash
npm install -g opencode-cloud
occ --version
```

## First run

```bash
# Install as a system service (recommended for background use)
occ install

# Start the service
occ start
```

If this is the first startup with no configured managed users, the container logs will print an **Initial One-Time Password (IOTP)**.
Open the login page, use the first-time setup panel, then continue to passkey setup where you can either enroll a passkey or choose username/password registration.
The IOTP is invalidated after successful passkey enrollment or successful username/password bootstrap signup.
Built-in image users (for example `ubuntu`/`opencoder`) do not count as configured users for IOTP bootstrap.

Quick IOTP extraction from logs:

```bash
occ logs | grep -F "INITIAL ONE-TIME PASSWORD (IOTP): " | tail -n1 | sed 's/.*INITIAL ONE-TIME PASSWORD (IOTP): //'
```

You can also run the setup wizard:

```bash
occ setup
```

The wizard now configures runtime settings (image source, bind/port, mounts), keeps authentication on IOTP-first onboarding, and attempts to auto-detect the IOTP from logs after start.

### From source (install locally)

```bash
# GitHub (primary)
git clone https://github.com/pRizz/opencode-cloud.git

# Gitea (mirror)
git clone https://gitea.com/pRizz/opencode-cloud.git
cd opencode-cloud
git submodule update --init --recursive packages/opencode
cargo install --path packages/cli-rust
```

### From source (development run)

```bash
# GitHub (primary)
git clone https://github.com/pRizz/opencode-cloud.git

# Gitea (mirror)
git clone https://gitea.com/pRizz/opencode-cloud.git
cd opencode-cloud
git submodule update --init --recursive packages/opencode
 
# Bun is required for this repo
bun --version

just setup
just build
cargo run -p opencode-cloud -- --version
```

## Usage

```bash
# Show version
occ --version

# Start the service (builds Docker container on first run, ~10-15 min)
occ start

# Start on a custom port
occ start --port 8080

# Start and open browser
occ start --open

# Check service status (includes broker health: Healthy/Degraded/Unhealthy)
occ status

# View logs
occ logs

# Follow logs in real-time
occ logs -f

# View opencode-broker logs (systemd/journald required)
occ logs --broker

# Troubleshoot broker health issues reported by `occ status`
occ logs --broker --no-follow

# Note: Broker logs require systemd/journald. This is enabled by default on supported Linux
# hosts. Docker Desktop/macOS/Windows use Tini, so broker logs aren't available there.
# Existing containers may need to be recreated after upgrading.

# Stop the service
occ stop

# Restart the service
occ restart

# Check for updates and choose what to update
occ update

# Update the opencode-cloud CLI binary
occ update cli

# Update the opencode-cloud container image
occ update container

# Update opencode inside the container
occ update opencode

# Update opencode using a specific branch or commit
occ update opencode --branch dev
occ update opencode --commit <sha>

# Remove the container (keeps volumes)
occ reset container

# Remove container and volumes (data loss)
occ reset container --volumes --force

# Reset completed IOTP bootstrap and generate a fresh one-time password
occ reset iotp

# If bind_address is exposed (for example behind HTTPS reverse proxy), confirm intentionally
occ reset iotp --force

# Clean bind mount contents (data loss)
occ mount clean --force

# Purge bind mounts (data loss, removes config entries)
occ mount clean --purge --force

# Mount a local project into the workspace
occ mount add /Users/<username>/Desktop/opencode:/home/opencoder/workspace

# Apply mount changes (you may be prompted to recreate the container)
occ restart

# Factory reset host (container, volumes, mounts, config/data)
occ reset host --force

### Workspace Mounts

Use `/home/opencoder/workspace` when you want your host project folder to appear in the
web UI's project picker and inside the container workspace.

Important behavior:
- `/home/opencoder/workspace` is a single mount target.
- Adding a new mount to this same target replaces the previous mount entry.

Recommended workflow:
```bash
occ mount add /Users/<username>/Desktop/opencode:/home/opencoder/workspace
occ restart
```

Verify the mount:
1. Run `occ status` and check `Mounts` -> `Bind mounts` includes your host path mapped to `/home/opencoder/workspace`.
2. In the web UI, open the project picker and confirm your project files appear under `~/workspace`.

### Container Mode

When `occ` runs inside the opencode container, it will auto-detect this and switch to **container runtime**.
Override if needed:

```bash
occ --runtime host <command>
OPENCODE_RUNTIME=host occ <command>
```

Supported commands in container runtime:
- `occ status`
- `occ logs`
- `occ user`
- `occ update opencode`

Notes:
- Host/Docker lifecycle commands are disabled in container runtime.
- `occ logs` and `occ update opencode` require systemd inside the container. If systemd is not available, run those commands from the host instead.

### Webapp-triggered update (command file)

When running in foreground mode (for example via `occ install`, which uses `occ start --no-daemon`),
the host listens for a command file on a bind mount. The webapp can write a simple JSON payload
to request an update.

Default paths (with default bind mounts enabled):
- Host: `~/.local/state/opencode/opencode-cloud/commands/update-command.json`
- Container: `/home/opencoder/.local/state/opencode/opencode-cloud/commands/update-command.json`

Example payload:
```json
{
  "command": "update_opencode",
  "request_id": "webapp-1234",
  "branch": "dev"
}
```

The host writes the result to:
`~/.local/state/opencode/opencode-cloud/commands/update-command.result.json`

# Install as a system service (starts on login/boot)
occ install

# Uninstall the system service
occ uninstall

# View configuration
occ config show
```

## Authentication

Security details: `docs/security/passkey-registration.md` (IOTP bootstrap and passkey enrollment flow)

opencode-cloud uses **PAM (Pluggable Authentication Modules)** for authentication.

First boot path:
- If no managed users are configured, startup logs print an Initial One-Time Password (IOTP).
- Extract only the IOTP quickly: `occ logs | grep -F "INITIAL ONE-TIME PASSWORD (IOTP): " | tail -n1 | sed 's/.*INITIAL ONE-TIME PASSWORD (IOTP): //'`
- `occ setup` attempts to auto-detect and print the IOTP after starting/restarting the service.
- Enter that IOTP in the web login page first-time setup panel.
- Continue to passkey setup, then choose one of:
  - Enroll a passkey for the default `opencoder` account, or
  - Use the username/password fallback to create your first managed user.
- The IOTP is deleted after successful passkey enrollment or successful username/password bootstrap signup.
- To restart first-time onboarding after completion, run `occ reset iotp`.
- Built-in image users (for example `ubuntu`/`opencoder`) do not disable IOTP bootstrap.

Admin path:
- You can always create/manage users directly via `occ user add`, `occ user passwd`, and related user commands.

Login UX:
- Passkey sign-in is the primary option on the login page.
- Username/password sign-in remains available as fallback.
- 2FA setup/management is available from the upper-right session menu after login.

### Creating Users

Create a user with a password:
```bash
occ user add <username>
```

Generate a random password:
```bash
occ user add <username> --generate
```

### Managing Users

- List users: `occ user list` (managed users only)
- Change password: `occ user passwd <username>`
- Remove user: `occ user remove <username>`
- Enable/disable account: `occ user enable <username>` / `occ user disable <username>`

### User Persistence

User accounts (including password hashes and lock status) persist across container updates and rebuilds.
The CLI stores user records in a managed Docker volume mounted at `/var/lib/opencode-users` inside the container.
This record store is the source of truth for configured users and bootstrap decisions.
No plaintext passwords are stored on the host.

### Rebuilding the Docker Image

When developing locally or after updating opencode-cloud, you may need to rebuild the Docker image to pick up changes in the embedded Dockerfile:

```bash
# Rebuild using Docker cache (fast - only rebuilds changed layers)
occ start --cached-rebuild-sandbox-image

# Rebuild from scratch without cache (slow - for troubleshooting)
occ start --full-rebuild-sandbox-image
```

**`--cached-rebuild-sandbox-image`** (recommended for most cases):
- Uses Docker layer cache for fast rebuilds
- Only rebuilds layers that changed (e.g., if only the CMD changed, it's nearly instant)
- Stops and removes any existing container before rebuilding

**`--full-rebuild-sandbox-image`** (for troubleshooting):
- Ignores Docker cache and rebuilds everything from scratch
- Takes 10-15 minutes but guarantees a completely fresh image
- Use when cached rebuild doesn't fix issues

**When to rebuild:**
- After pulling updates to opencode-cloud → use `--cached-rebuild-sandbox-image`
- After pulling new commits in `packages/opencode` (submodule) → run `just run start --cached-rebuild-sandbox-image` once so the running container picks up the new opencode commit
- When modifying the Dockerfile during development → use `--cached-rebuild-sandbox-image`
- When the container fails to start due to image issues → try `--cached-rebuild-sandbox-image` first, then `--full-rebuild-sandbox-image`
- When you want a completely fresh environment → use `--full-rebuild-sandbox-image`

**Local submodule dev rebuild (no push required):**
```bash
# Fast rebuild using local packages/opencode checkout (including uncommitted edits)
just run start --cached-rebuild-sandbox-image --local-opencode-submodule

# Full no-cache rebuild from local packages/opencode checkout
just run start --full-rebuild-sandbox-image --local-opencode-submodule
```
- This mode is for local development/debugging only and bypasses the default remote clone path.
- `just run status` will show commit metadata derived from your local checkout (dirty trees are marked with `-dirty`).
- Local context packaging intentionally skips heavyweight/dev metadata folders (for example `.planning`, `.git`, `node_modules`, `target`, and `dist`).
- Keep CI/release workflows on the default pinned remote mode.

### Dockerfile Optimization Checklist

For new Docker build steps, follow this checklist:
- Prefer BuildKit cache mounts (`RUN --mount=type=cache`) for package caches (`apt`, `bun`, `cargo`, `pip`, and `npm`).
- For `bun install` in container builds, use a dedicated install-cache mount plus a short retry loop that clears that cache between attempts to recover from occasional corrupted/interrupted cache artifacts.
- Create and remove temporary workdirs in the same `RUN` layer (for example `/tmp/opencode-repo`).
- Do not defer cleanup to later layers; deleted files still exist in lower layers.
- Keep builder-stage artifacts out of runtime layers by copying only final outputs.
- When adding Docker build assets, update build-context inclusion logic in `packages/core/src/docker/image.rs`.
- Keep local submodule exclude lists aligned with Dockerfile hygiene rules (`.planning`, `.git`, `node_modules`, `target`, `dist`, and similar dev metadata).

This policy is intentional for both image-size hygiene and fast local rebuilds.

**Worktree-isolated sandbox profiles (opt-in):**
```bash
# Derive a stable instance name from the current git worktree root
just run --sandbox-instance auto start --cached-rebuild-sandbox-image

# Use an explicit instance name
just run --sandbox-instance mytree start --cached-rebuild-sandbox-image
```
- Default behavior (no `--sandbox-instance`) remains the shared legacy sandbox.
- Isolated instances use separate container names, image tags, Docker volumes, and image-state files.
- You can also set `OPENCODE_SANDBOX_INSTANCE=<name|auto>` instead of passing the CLI flag every time.

## Configuration

Configuration is stored at:
- Linux/macOS: `~/.config/opencode-cloud/config.json`

Data (PID files, etc.) is stored at:
- Linux/macOS: `~/.local/share/opencode-cloud/`

## Development

```bash
# Bun is required for this repo
bun --version

# One-time setup (hooks + deps + submodule bootstrap)
just setup

# Build everything
just build

# Compile and run occ (arguments automatically get passed to the binary)
just run --version

# Run tests
just test

# Format and lint
just fmt
just lint
```

### Visual E2E (Playwright)

From the repository root, run UI tests in a visible browser with:

```bash
bun run --cwd packages/opencode/packages/app test:e2e:local -- --headed --project=chromium e2e/settings/settings-authentication.spec.ts
bun run --cwd packages/opencode/packages/app test:e2e:local -- --ui e2e/settings/settings-authentication.spec.ts
PWDEBUG=1 bun run --cwd packages/opencode/packages/app test:e2e:local -- --headed --project=chromium e2e/settings/settings-authentication.spec.ts
```

Headless run (default Playwright mode):

```bash
bun run --cwd packages/opencode/packages/app test:e2e:local -- --project=chromium e2e/settings/settings-authentication.spec.ts
```

Use `test:e2e:local` for local harness/sandbox provisioning. Plain `test:e2e` expects a backend that is already running at the configured Playwright host/port.

> **Note:** The git hooks automatically sync `README.md` to npm package directories on commit.

## Architecture

This is a monorepo with:
- `packages/core` - Rust core library
- `packages/cli-rust` - Rust CLI binary (recommended)
- `packages/cli-node` - Node.js CLI (fully supported and in parity with the Rust CLI)

### Cargo.toml Sync Requirement

The `packages/core/Cargo.toml` file must use **explicit values** rather than `workspace = true` references.

When updating package metadata (version, edition, rust-version, etc.), keep both files in sync:
- `Cargo.toml` (workspace root)
- `packages/core/Cargo.toml`

Use `scripts/set-all-versions.sh <version>` to update versions across all files automatically.

## License

MIT