From 618e1a62dc286f052e1d0e2e7bf050b28d2da7f9 Mon Sep 17 00:00:00 2001 From: Guillaume ARM Date: Wed, 17 Jun 2026 01:25:16 +0200 Subject: [PATCH] refactor: rename trapos-sandbox to trapos-server --- .env.test | 2 +- .luacheckrc | 6 +- .mcp.json | 2 +- .opencode/agent/atm10-expert.md | 6 +- .opencode/opencode.json | 2 +- .plans/archived/boot-lifecycle-refactor.md | 6 +- .plans/archived/trap-sandbox/01-ts-gateway.md | 93 --------- .plans/archived/trap-sandbox/02-lua-client.md | 60 ------ .plans/archived/trap-sandbox/03-packages.md | 27 --- .../trap-sandbox/04-integration-tooling.md | 51 ----- .../trap-sandbox/trap-sandbox-gateway-spec.md | 144 ------------- .../trapos-create-installer-disk-plan.md | 6 +- .plans/remove-mcp-bridge-plan.md | 26 +-- AGENTS.md | 2 +- DEVELOPMENT.md | 2 +- Justfile | 2 +- README.md | 4 +- docs/adrs/README.md | 2 +- .../adr-0002-eventloop-and-service-bus.md | 2 +- .../adr-0005-craftos-pc-harness-and-probes.md | 2 +- docs/adrs/adr-0007-test-framework.md | 2 +- docs/adrs/adr-0016-js-tool-verification.md | 6 +- .../adrs/adr-0017-mcp-remote-lua-execution.md | 4 +- ....md => adr-0019-mcp-over-cloud-gateway.md} | 38 ++-- docs/ingame-trapos-ai-mcp-guide.md | 26 +-- docs/public-ports.md | 2 +- just/install.just | 6 +- just/npm.just | 32 +-- just/sandbox.just | 18 -- just/server.just | 18 ++ just/test.just | 2 +- manifest.json | 2 +- packages/index.json | 6 +- .../apis/libcloud.lua} | 72 +++---- .../apis/libmcpcomputer.lua | 8 +- packages/trapos-cloud/ccpm.json | 14 ++ .../programs/cloud.lua} | 24 +-- .../servers/cloud.lua} | 22 +- .../servers/mcp-computer-server.lua | 22 +- .../tests/cloud.lua} | 194 +++++++++--------- .../tests/mcpcomputer.lua | 0 packages/trapos-core/apis/libccpm.lua | 2 +- packages/trapos-core/ccpm.json | 2 +- .../programs/trapos-postinstall.lua | 30 +-- packages/trapos-core/tests/ccpm.lua | 2 +- packages/trapos-core/tests/create-disk.lua | 6 +- packages/trapos-core/tests/postinstall.lua | 40 ++-- packages/trapos-sandbox/ccpm.json | 14 -- .../tests/harness/fake-eventloop.lua | 2 +- packages/trapos/ccpm.json | 4 +- packages/trapos/tests/install-trapos.lua | 12 +- .../test-integration/lua/full-boot.lua | 30 --- .../eslint.config.js | 0 .../package-lock.json | 4 +- .../package.json | 4 +- .../src/app.ts | 4 +- .../src/auth.ts | 0 .../src/index.ts | 12 +- .../src/modules/mcp/index.ts | 0 .../src/modules/mcp/tools.ts | 4 +- .../trapos-cloud-gateway/connection.ts | 10 +- .../src/modules/trapos-cloud-gateway/index.ts | 0 .../modules/trapos-cloud-gateway/protocol.ts | 4 +- .../modules/trapos-cloud-gateway/registry.ts | 4 +- .../src/opencode.ts | 0 .../test-integration/auth.test.ts | 18 +- .../test-integration/displaced.test.ts | 24 +-- .../test-integration/duplicate.test.ts | 22 +- .../test-integration/full-boot.test.ts | 16 +- .../test-integration/harness.ts | 20 +- .../test-integration/health.test.ts | 26 +-- .../test-integration/lua/full-boot.lua | 30 +++ .../test-integration/lua/gateway-client.lua | 26 +-- .../lua/mcp-server-client.lua | 18 +- .../test-integration/mcp.test.ts | 28 +-- .../test-integration/reverse-ping.test.ts | 14 +- .../test/auth.test.ts | 0 .../test/connection.test.ts | 22 +- .../test/helpers.ts | 0 .../test/mcp.test.ts | 6 +- .../test/opencode.test.ts | 0 .../test/protocol.test.ts | 8 +- .../test/registry.test.ts | 4 +- .../tsconfig.json | 0 84 files changed, 531 insertions(+), 906 deletions(-) delete mode 100644 .plans/archived/trap-sandbox/01-ts-gateway.md delete mode 100644 .plans/archived/trap-sandbox/02-lua-client.md delete mode 100644 .plans/archived/trap-sandbox/03-packages.md delete mode 100644 .plans/archived/trap-sandbox/04-integration-tooling.md delete mode 100644 .plans/archived/trap-sandbox/trap-sandbox-gateway-spec.md rename docs/adrs/{adr-0019-mcp-over-sandbox-gateway.md => adr-0019-mcp-over-cloud-gateway.md} (68%) delete mode 100644 just/sandbox.just create mode 100644 just/server.just rename packages/{trapos-sandbox/apis/libsandbox.lua => trapos-cloud/apis/libcloud.lua} (87%) rename packages/{trapos-sandbox => trapos-cloud}/apis/libmcpcomputer.lua (96%) create mode 100644 packages/trapos-cloud/ccpm.json rename packages/{trapos-sandbox/programs/sandbox.lua => trapos-cloud/programs/cloud.lua} (63%) rename packages/{trapos-sandbox/servers/sandbox.lua => trapos-cloud/servers/cloud.lua} (67%) rename packages/{trapos-sandbox => trapos-cloud}/servers/mcp-computer-server.lua (71%) rename packages/{trapos-sandbox/tests/sandbox.lua => trapos-cloud/tests/cloud.lua} (73%) rename packages/{trapos-sandbox => trapos-cloud}/tests/mcpcomputer.lua (100%) delete mode 100644 packages/trapos-sandbox/ccpm.json delete mode 100644 tools/trap-sandbox/test-integration/lua/full-boot.lua rename tools/{trap-sandbox => trapos-server}/eslint.config.js (100%) rename tools/{trap-sandbox => trapos-server}/package-lock.json (99%) rename tools/{trap-sandbox => trapos-server}/package.json (96%) rename tools/{trap-sandbox => trapos-server}/src/app.ts (96%) rename tools/{trap-sandbox => trapos-server}/src/auth.ts (100%) rename tools/{trap-sandbox => trapos-server}/src/index.ts (85%) rename tools/{trap-sandbox => trapos-server}/src/modules/mcp/index.ts (100%) rename tools/{trap-sandbox => trapos-server}/src/modules/mcp/tools.ts (99%) rename tools/{trap-sandbox => trapos-server}/src/modules/trapos-cloud-gateway/connection.ts (95%) rename tools/{trap-sandbox => trapos-server}/src/modules/trapos-cloud-gateway/index.ts (100%) rename tools/{trap-sandbox => trapos-server}/src/modules/trapos-cloud-gateway/protocol.ts (97%) rename tools/{trap-sandbox => trapos-server}/src/modules/trapos-cloud-gateway/registry.ts (98%) rename tools/{trap-sandbox => trapos-server}/src/opencode.ts (100%) rename tools/{trap-sandbox => trapos-server}/test-integration/auth.test.ts (68%) rename tools/{trap-sandbox => trapos-server}/test-integration/displaced.test.ts (72%) rename tools/{trap-sandbox => trapos-server}/test-integration/duplicate.test.ts (68%) rename tools/{trap-sandbox => trapos-server}/test-integration/full-boot.test.ts (52%) rename tools/{trap-sandbox => trapos-server}/test-integration/harness.ts (92%) rename tools/{trap-sandbox => trapos-server}/test-integration/health.test.ts (51%) create mode 100644 tools/trapos-server/test-integration/lua/full-boot.lua rename tools/{trap-sandbox => trapos-server}/test-integration/lua/gateway-client.lua (73%) rename tools/{trap-sandbox => trapos-server}/test-integration/lua/mcp-server-client.lua (78%) rename tools/{trap-sandbox => trapos-server}/test-integration/mcp.test.ts (69%) rename tools/{trap-sandbox => trapos-server}/test-integration/reverse-ping.test.ts (58%) rename tools/{trap-sandbox => trapos-server}/test/auth.test.ts (100%) rename tools/{trap-sandbox => trapos-server}/test/connection.test.ts (88%) rename tools/{trap-sandbox => trapos-server}/test/helpers.ts (100%) rename tools/{trap-sandbox => trapos-server}/test/mcp.test.ts (97%) rename tools/{trap-sandbox => trapos-server}/test/opencode.test.ts (100%) rename tools/{trap-sandbox => trapos-server}/test/protocol.test.ts (89%) rename tools/{trap-sandbox => trapos-server}/test/registry.test.ts (97%) rename tools/{trap-sandbox => trapos-server}/tsconfig.json (100%) diff --git a/.env.test b/.env.test index 19cb126..de0abc5 100644 --- a/.env.test +++ b/.env.test @@ -7,4 +7,4 @@ TRAPOS_TEST_TIMEOUT_WATCHDOG_SECONDS=1 # Test placeholders. The real values are generated in `.env` on first `just install`, # or after `just clean` / `just reinstall`. OPENCODE_SERVER_PASSWORD=redacted -SANDBOX_PASSWORD=redacted +TRAPOS_SERVER_PASSWORD=redacted diff --git a/.luacheckrc b/.luacheckrc index aa4872c..aeec008 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -1,10 +1,10 @@ --- Luacheck config for CC:Tweaked (ComputerCraft) Lua sandbox. --- Runtime is Lua 5.1 inside the CC sandbox, not standard Lua, so we +-- Luacheck config for CC:Tweaked (ComputerCraft) Lua runtime. +-- Runtime is Lua 5.1 inside the CC:Tweaked runtime, not standard Lua, so we -- extend lua51 with the globals and os/table extensions CC provides. stds.cc = { read_globals = { - -- ComputerCraft APIs injected as globals by the sandbox. + -- ComputerCraft APIs injected as globals by the runtime. "fs", "peripheral", "parallel", diff --git a/.mcp.json b/.mcp.json index 72bad2d..8658add 100644 --- a/.mcp.json +++ b/.mcp.json @@ -1,6 +1,6 @@ { "mcpServers": { - "trapos-cloud-gateway-mcp": { + "trapos-mcp": { "type": "http", "url": "http://127.0.0.1:4445" } diff --git a/.opencode/agent/atm10-expert.md b/.opencode/agent/atm10-expert.md index 4ba58d0..2719378 100644 --- a/.opencode/agent/atm10-expert.md +++ b/.opencode/agent/atm10-expert.md @@ -12,8 +12,8 @@ permission: bash: "*": ask "just check": allow - trapos-cloud-gateway-mcp_probe-computers: allow - trapos-cloud-gateway-mcp_exec-lua: allow + trapos-mcp_probe-computers: allow + trapos-mcp_exec-lua: allow --- You answer players using TrapOS `ai` from inside ATM10 / All The Mods 10. @@ -23,7 +23,7 @@ Target environment: - ATM10 / All The Mods 10 pack version `7.0`; accept `0.7.0` as the same user alias. - Minecraft `1.21.1`. - NeoForge `21.1.228`. -- ComputerCraft is CC:Tweaked in the ComputerCraft sandbox, not desktop Lua. +- ComputerCraft is CC:Tweaked in the ComputerCraft runtime, not desktop Lua. Response style: diff --git a/.opencode/opencode.json b/.opencode/opencode.json index d1bafd0..a0194a9 100644 --- a/.opencode/opencode.json +++ b/.opencode/opencode.json @@ -1,7 +1,7 @@ { "$schema": "https://opencode.ai/config.json", "mcp": { - "trapos-cloud-gateway-mcp": { + "trapos-mcp": { "type": "remote", "url": "http://127.0.0.1:4445", "enabled": true diff --git a/.plans/archived/boot-lifecycle-refactor.md b/.plans/archived/boot-lifecycle-refactor.md index 86fce36..f63c9bb 100644 --- a/.plans/archived/boot-lifecycle-refactor.md +++ b/.plans/archived/boot-lifecycle-refactor.md @@ -114,7 +114,7 @@ ADR-0019, not fixed here to keep scope contained.) Rewrite the "postinstall before startup servers" test (lines 164-174): drop the `/startup/servers.lua` lookup; assert reboot occurs after `trapos-postinstall`. In the `--cpm-only` test (176-184), assert reboot was **not** called. -- `tools/trap-sandbox/test-integration/lua/full-boot.lua:1`: update the comment reference +- `tools/trapos-server/test-integration/lua/full-boot.lua:1`: update the comment reference `startup/servers.lua` → `startup/boot.lua` (no functional change — it doesn't load the file). ### 7. Docs @@ -138,7 +138,7 @@ ADR-0019, not fixed here to keep scope contained.) - `packages/trapos-boot/ccpm.json`, `packages/index.json` (file list + version bumps) - `tests/startup-servers.lua` → **rename** `tests/boot.lua` (policy cases) - `tests/install-trapos.lua` (os stub + reboot assertions) -- `tools/trap-sandbox/test-integration/lua/full-boot.lua` (comment) +- `tools/trapos-server/test-integration/lua/full-boot.lua` (comment) - `docs/adrs/adr-0019-boot-lifecycle.md` (**new**), `docs/adrs/README.md` - `README.md`, `AGENTS.md`, `docs/periphemu.md` @@ -157,5 +157,5 @@ ADR-0019, not fixed here to keep scope contained.) - **Upgrade migration**: on a computer with the old `startup/servers.lua` + `startup/motd.lua` present, drop in the new `boot.lua` and reboot; confirm both stale files are deleted and only `boot.lua` remains in `/startup/`. -- **Installer**: run `install-trapos.lua` end-to-end (or via the sandbox harness) and confirm it +- **Installer**: run `install-trapos.lua` end-to-end (or via the test harness) and confirm it finishes with `=> Rebooting...` and comes back up into a live TrapOS rather than a nested shell. diff --git a/.plans/archived/trap-sandbox/01-ts-gateway.md b/.plans/archived/trap-sandbox/01-ts-gateway.md deleted file mode 100644 index 909d165..0000000 --- a/.plans/archived/trap-sandbox/01-ts-gateway.md +++ /dev/null @@ -1,93 +0,0 @@ -# 01 — TS gateway (`tools/trap-sandbox/`) - -> Sub-plan of [trap-sandbox-gateway-spec](./trap-sandbox-gateway-spec.md). Implements the -> **Protocol** and **Handshake** contracts defined there — do not redefine frame shapes or error -> codes here; reference the spec. **Build first** (M1): no Lua dependency. - -> **Grill-refined (implemented).** This sub-plan was hardened in a follow-up grill and built. Deltas -> from the text below, all in the shipped code: -> - WS `type` `health` → **`probe_gateway`** (in-game command stays `sandbox health`). -> - Validation uses **zod** (`protocol.ts`); `parse()` throws on malformed / unknown `event` / failed -> schema (handler catches → debug-logs → drops). `payload` (`[]`/missing/non-object → `{}`) via zod. -> - `resolveAccount(secret, password)` owns the password gate (signature gained `password`). -> - Dispatch state machine extracted to **`connection.ts`** (pure, FakeSocket-testable); the module -> `index.ts` is the thin `ws` adapter. Registry depends on a minimal `GatewaySocket { send, close }`. -> - `registry.request()` rejects a typed **`GatewayError { code }`** on not_connected/timeout/ok:false, -> resolves the payload on success. Newest-wins eviction rejects the old socket's pendings + closes it; -> `unregister`/close are reference-identity-checked. -> - Logging via Fastify's **pino**; env `LOG_LEVEL` (default `info`, `silent` in tests), `LOG_PRETTY`; -> silent drops log at `debug` with `{ event, type, reason }`. **No `serve` script** — `dev` is the -> pretty entry point (`LOG_PRETTY=1 tsx watch …`); `pino-pretty` devDep; `zod` dep. -> - Every `computer_request` (incl. `hello`) must carry non-empty `traposSenderId` (zod-enforced). -> - Open `GET /health` is liveness-only (`{ok:true}`, no auth, no probe). Opencode probe: client built -> once, fresh uncached 5s-bounded `config.get()` per call, no-URL short-circuit. - -## Scope -The TypeScript service: Fastify app on port 4444, the `trapos-cloud-gateway` module -(protocol/registry/WS), the opencode probe, the auth seam, env/config, package wiring, and TS -**unit** tests. Integration/e2e tests live in [04-integration-tooling](./04-integration-tooling.md). - -## Task checklist -- [ ] Scaffold `tools/trap-sandbox/` (ESM, `tsc` build, `tsx` dev/test, `node:test`, typescript-eslint - flat config — copy `mcp-bridge`'s `tsconfig.json`/`eslint.config.js`). -- [ ] `package.json`: deps + devDeps + scripts (see below). -- [ ] `src/auth.ts` — `resolveAccount(secret)`. -- [ ] `src/opencode.ts` — `createOpencodeProbe(config)`. -- [ ] `src/modules/trapos-cloud-gateway/protocol.ts` — envelope/error types + parse/validate. -- [ ] `src/modules/trapos-cloud-gateway/registry.ts` — `GatewayRegistry`. -- [ ] `src/modules/trapos-cloud-gateway/index.ts` — Fastify plugin: `GET /health` (open) + `GET /gateway` (ws). -- [ ] `src/app.ts` — `createApp(config)` returning `{ app, registry }`. -- [ ] `src/index.ts` — env config, listen, `EADDRINUSE` clear message + exit. -- [ ] TS unit tests (`test/`): protocol, registry, auth/hello, probe. -- [ ] Gate: `npm test`, `npm run lint`/`check` clean. - -## Layout -ESM, `tsc` build, `tsx` dev/test, `node:test`, typescript-eslint flat config (copy mcp-bridge's -`tsconfig.json`/`eslint.config.js`). Fastify `^5` + `@fastify/websocket` `^11`; `engines.node>=20`. - -``` -src/ -├── index.ts # env config; build app; listen on SANDBOX_PORT; clear EADDRINUSE message + exit -├── app.ts # createApp(config): Fastify + GatewayRegistry; register @fastify/websocket + module; returns { app, registry } -├── auth.ts # resolveAccount(secret): "local" | null (constant seam; null = unauthorized) -├── opencode.ts # createOpencodeProbe(config): probe(): { ok, detail } via @opencode-ai/sdk -└── modules/trapos-cloud-gateway/ - ├── index.ts # Fastify plugin: GET /health (open) + GET /gateway (ws) - ├── protocol.ts # envelope/error types + parse/validate (4 events, hello, empty-payload→{}) - └── registry.ts # GatewayRegistry keyed by (accountId,traposId); newest-wins; request()+timeout; pending cleanup on close -test/ # TS unit (protocol, registry, auth/hello, probe) with FakeSocket + fake fetch -``` - -## Behavior -- **WS `/gateway`** (`{ websocket: true }`): no upgrade auth (auth is the hello frame). `maxPayload` - ~1MB. `socket.on("message")` parses a frame inside try/catch → dispatch: pre-hello only `hello` is - honored; post-hello handle `computer_request` (`health`, else `unknown_type`) and - `computer_response` (resolve pending `gateway_request` by messageId). `close`/`error` → unregister - + reject that connection's pendings. (Handshake steps & error codes: see spec **Handshake**.) -- **GatewayRegistry**: `register`/`unregister`/`has`/`count`; `request(accountId, traposId, type, - payload, timeoutMs=600000)` sends `gateway_request`, races matching `computer_response` vs timeout. - Keyed by `(accountId, traposId)`, newest-wins. -- **opencode probe**: `createOpencodeClient({ baseUrl: OPENCODE_URL, headers:{ Authorization: basic - ("opencode", OPENCODE_SERVER_PASSWORD) }, throwOnError:false })` then `config.get()` with - `AbortSignal.timeout(5000)`, wrapped in try/catch. `ok = response 2xx`; `detail` = url + status or - error. No `OPENCODE_URL` → `{ ok:false, detail:"OPENCODE_URL not set" }` without a call. -- **auth**: `resolveAccount(secret)` → `"local" | null` (constant seam; `null` = unauthorized). - -## Env (`index.ts`) -`SANDBOX_HOST`(`0.0.0.0`), `SANDBOX_PORT`(`4444`), `SANDBOX_PASSWORD`(unset⇒ auth off), -`SANDBOX_REQUEST_TIMEOUT_MS`(`600000`), `OPENCODE_URL`(`http://127.0.0.1:4242`), -`OPENCODE_SERVER_PASSWORD`, `OPENCODE_USERNAME`(`opencode`). - -## package.json -- deps: `fastify`, `@fastify/websocket`, `@opencode-ai/sdk` (move from devDeps). -- devDeps: copy mcp-bridge (`typescript`, `tsx`, `@eslint/js`, `typescript-eslint`, `@types/node`, - `globals`, `eslint`, `@types/ws`). -- scripts: copy mcp-bridge **plus `"lint":"npm run eslint"`**. - -## TS unit tests (`test/`) -Protocol parse/validate (4 events, hello, `ok`/`error`, empty-payload→`{}`), registry (duplicate -newest-wins per account, messageId correlation, timeout, pending cleanup on close), `resolveAccount`, -opencode probe (fake fetch/server: ok / 401 / timeout). Use `FakeSocket` + fake fetch. - -## Verification gate -`npm test` + `npm run lint`/`check` clean in `tools/trap-sandbox`; service starts and listens on 4444. diff --git a/.plans/archived/trap-sandbox/02-lua-client.md b/.plans/archived/trap-sandbox/02-lua-client.md deleted file mode 100644 index 2e71bb9..0000000 --- a/.plans/archived/trap-sandbox/02-lua-client.md +++ /dev/null @@ -1,60 +0,0 @@ -# 02 — Lua client (`trapos-sandbox`) - -> Sub-plan of [trap-sandbox-gateway-spec](./trap-sandbox-gateway-spec.md). Implements the -> **Protocol**, **Handshake**, and **os-event contract** from the spec — reference them, don't -> redefine. M2; depends on the protocol contract (not on M1 code existing). - -## Scope -The Lua side: `apis/libsandbox.lua`, `servers/sandbox.lua`, `programs/sandbox.lua`, and Lua **unit** -tests. Package/meta changes are [03-packages](./03-packages.md); integration/e2e is -[04-integration-tooling](./04-integration-tooling.md). - -## Task checklist -- [ ] `apis/libsandbox.lua` (template: `apis/libmcpcomputer.lua`). -- [ ] `servers/sandbox.lua` (template: `servers/mcp-computer-server.lua`). -- [ ] `programs/sandbox.lua` (template: `programs/ai.lua`). -- [ ] Lua unit tests (`tests/sandbox.lua`). -- [ ] Gate: `just _craftos-test` runs `tests/sandbox.lua` green. - -## `apis/libsandbox.lua` — template: `apis/libmcpcomputer.lua` -`createSandbox()` returns: -- `traposId(osLike)` → `id .. ":" .. (label or "")`. -- Envelope builders for the 4 events + hello. **Every `computer_request` (including `hello`) must - carry a non-empty `traposSenderId` = the computer's `traposId`** — the TS gateway's zod schema - requires it (decided in the 01-ts-gateway grill). The `hello` identity used for registration is - still `payload.traposId`, but the envelope `traposSenderId` must also be present. -- `onMessage(content, …)`: decode (pcall); `gateway_request`→ built-in `ping` (else - `ok:false unknown_type`) replied as `computer_response` via pcall-send; `gateway_response`→ if - `type=="hello"` drive the handshake state, else `queueEvent('trapos_sandbox_reply', messageId, ok, - payload, error)`. -- `startSession(opts)` (daemon; injectable `http`/`eventloop`/`encode`/`decode`/`os`): builds the - connect URL from base `opts.url`, computes `traposId`; `websocketAsync`; on `websocket_success` - send hello; on hello `ok:true` set state=ready + `queueEvent('trapos_sandbox_connected', traposId)`; - on hello `ok:false` set state=unauthorized (**terminal**, store `lastError`, close, no reconnect); - on `websocket_failure`/`_closed` reconnect via `el.setTimeout(connect, 5)` (unless unauthorized); - hello with no ack in ~10s → close + reconnect; `trapos_sandbox_send` handler builds - `computer_request`, **pcall-send** only when ready, else fast `trapos_sandbox_reply(messageId, - false, nil, {code:"not_connected"})`. Returns `{ isReady, lastError }`. -- `request(type, payload, opts)` (injectable `queueEvent`/`pullEvent`/`startTimer`): mint messageId, - queue `trapos_sandbox_send`, wait for matching `trapos_sandbox_reply` vs a timer (**default 10s**, - overridable). Returns `(ok, payloadOrError)`. - -## `servers/sandbox.lua` — template: `servers/mcp-computer-server.lua` -Read `sandbox.url` (unset/`http.websocket` missing → "daemon inactive", return) and `sandbox.password`; -`startSession{ eventloop=_G.bootEventLoop, url, password(secret), os }`; print version. - -## `programs/sandbox.lua` — template: `programs/ai.lua` -- Reads `sandbox.url` first; unset → immediate `set sandbox.url first` (no 10s wait). -- `health` command → `request('probe_gateway')` (wire `type` renamed from `health` during the - 01-ts-gateway grill; the in-game command name stays `health`); print `sandboxOk`/`opencodeOk` - (+ `opencode: ` when false); on `not ok` print `sandbox health failed: ` - (e.g. `unauthorized`, `not_connected`, `timeout`). -- `--version` / `--help`. - -## Lua unit tests (`tests/sandbox.lua`, libtest + fakes) -Envelope builders, `traposId`, `onMessage` dispatch (ping ok / unknown_type / hello), empty-payload -coercion, `startSession` wiring (fake eventloop+http: hello sent on open, ready on ack, terminal on -unauthorized, reconnect on drop), `request()` resolve + timeout via injected event primitives. - -## Verification gate -`just _craftos-test` runs the new `tests/sandbox.lua` green. diff --git a/.plans/archived/trap-sandbox/03-packages.md b/.plans/archived/trap-sandbox/03-packages.md deleted file mode 100644 index b2125d1..0000000 --- a/.plans/archived/trap-sandbox/03-packages.md +++ /dev/null @@ -1,27 +0,0 @@ -# 03 — Packages (rename + new package + meta) - -> Sub-plan of [trap-sandbox-gateway-spec](./trap-sandbox-gateway-spec.md). Mechanical wiring. M3; -> may run in parallel with M2. Depends on the Lua file paths from -> [02-lua-client](./02-lua-client.md) being settled. - -## Scope -ccpm package definitions only: rename the legacy package, register the new `trapos-sandbox`, keep -`packages/index.json` consistent, and add `trapos-sandbox` to the `trapos` meta with a version bump. - -## Task checklist -- [ ] **Rename** `packages/trapos-sandbox/` → `packages/trapos-sandbox-legacy/`; set - `"name": "trapos-sandbox-legacy"` (keep its files/autostart). -- [ ] **New** `packages/trapos-sandbox/ccpm.json`: name `trapos-sandbox`, version `0.1.0`, deps - `["trapos-core"]`, files `["apis/libsandbox.lua","programs/sandbox.lua","servers/sandbox.lua"]`, - autostart `["servers/sandbox"]`. -- [ ] **`packages/index.json`**: `trapos-sandbox: "0.1.0"` + add `trapos-sandbox-legacy: "0.2.2"`. -- [ ] **`packages/trapos/ccpm.json`**: add `trapos-sandbox` to deps; bump meta version. - -## Notes -- The daemon stays dormant until `sandbox.url` is set, so adding it to the `trapos` meta is safe. -- Confirm the legacy version pinned in `index.json` (`0.2.2`) matches the renamed package's current - version before committing. - -## Verification gate -`packages/index.json` resolves both names; `trapos` meta installs `trapos-sandbox`; legacy package -still installs under its new name with files/autostart intact. diff --git a/.plans/archived/trap-sandbox/04-integration-tooling.md b/.plans/archived/trap-sandbox/04-integration-tooling.md deleted file mode 100644 index 4e5e71d..0000000 --- a/.plans/archived/trap-sandbox/04-integration-tooling.md +++ /dev/null @@ -1,51 +0,0 @@ -# 04 — Integration & tooling - -> Sub-plan of [trap-sandbox-gateway-spec](./trap-sandbox-gateway-spec.md). **Build last** (M4): -> requires both the TS gateway (M1) and the Lua client (M2) to exist. References the spec's -> **Protocol**/**Handshake** for assertions. - -## Scope -Host tooling (`just`/`npm.just` recipes, `sandbox-serve`, docs) and the cross-side tests: TS -**integration** (real craftos ↔ real gateway) plus the **full-boot e2e**. - -## Task checklist -- [x] `just/npm.just`: extend `install/build/check/test/test-integration/ci` to cover - `tools/trap-sandbox` alongside mcp-bridge; ensure `just install`/`just ci` include it. - (Symmetric per-tool recipes; `npm-trap-sandbox-ci` removed.) -- [x] **`just sandbox-serve`** added in new `just/sandbox.just` (warn-only on missing secret). -- [x] `docs/public-ports.md`: added a `4444` row. -- [x] TS integration harness + cases (`tools/trap-sandbox/test-integration/`). -- [x] Full-boot e2e. -- [x] Gate: `npm run test:integration` green; `just ci` covers both tools. - -## Tooling -- `just/npm.just`: extend recipes (install/build/check/test/test-integration/ci) to cover - `tools/trap-sandbox` alongside mcp-bridge; ensure `just install`/`just ci` include it. -- `just/opencode.just` (or a new recipe): add **`just sandbox-serve`** mirroring `opencode-serve` - (source `.env`, export `OPENCODE_SERVER_PASSWORD`, default `OPENCODE_URL`/`SANDBOX_PORT`, run - `npm run dev --prefix tools/trap-sandbox`). -- `docs/public-ports.md`: add a `4444` row (note it sits outside the 4242–4244 range; 4244 reserved). - -## Test layout -``` -test-integration/ # harness.ts (app on :0, startCraftos, fake-opencode http server) + lua/ + *.test.ts -``` - -## TS integration (`tools/trap-sandbox/test-integration`, real craftos ↔ real gateway, serialized) -Self-contained `gateway-client.lua` for the health round-trip (opencodeOk true/false via the -fake-opencode server), reverse `ping` via `registry.request`, duplicate (newest-wins), and the -**auth canary** — wrong secret asserts in-band `hello ok:false code:"unauthorized"`, correct secret -asserts `hello ok:true`. - -## Full-boot e2e (TS integration) -craftos `--exec` replicates boot (set `sandbox.url`, `_G.bootEventLoop`, run real `servers/sandbox`, -then under `parallel` wait for `trapos_sandbox_connected` and run `sandbox health`); assert stdout -contains `sandboxOk: true`. Bounded by generous timeouts. - -## Verification gate (also the project-level acceptance) -- `npm test`, `npm run test:integration`, `npm run lint`/`check` clean in `tools/trap-sandbox`. -- `just _craftos-test` runs `tests/sandbox.lua`; `just ci` covers both tools. -- Manual: `just sandbox-serve`; in-game `set sandbox.url ws://:4444` (origin only — - libsandbox appends `/gateway`) (+ - `set sandbox.password ` if `SANDBOX_PASSWORD` set), reboot, `sandbox health` → - `sandboxOk: true` / `opencodeOk: …`. diff --git a/.plans/archived/trap-sandbox/trap-sandbox-gateway-spec.md b/.plans/archived/trap-sandbox/trap-sandbox-gateway-spec.md deleted file mode 100644 index afa781b..0000000 --- a/.plans/archived/trap-sandbox/trap-sandbox-gateway-spec.md +++ /dev/null @@ -1,144 +0,0 @@ -# Spec: trap-sandbox gateway + trapos-sandbox client (first iteration) - -> **Status: SETTLED — do not revisit the Decision log.** The decisions below were hardened -> through a full design interview and are frozen input for every sub-plan. If a sub-plan -> surfaces a reason to reopen one, stop and raise it here first; do not silently deviate. - -This is the **shared contract**. Sub-plans implement against it. The cross-cutting pieces -(Context, Decision log, Protocol, Out-of-scope, Known limitations) live here and are *not* -duplicated into sub-plans — sub-plans link back to this file. - -## Context - -`tools/trap-sandbox/` is a new TypeScript service that replaces the legacy `tools/mcp-bridge` -(kept only for inspiration). It delivers a **general-purpose Fastify API on port 4444**, organized -into "sandbox modules" (Fastify plugins). The only module in this iteration is the -**`trapos-cloud-gateway`**: a WebSocket gateway giving a CraftOS (trapos) computer and the -sandbox/opencode a generic, bi-directional request/reply protocol. - -On the Lua side, a new `trapos-sandbox` package installs `servers/sandbox.lua` (a boot daemon that -owns one persistent WS connection and bridges WS frames to in-OS events) plus a `sandbox` command. -The legacy `trapos-sandbox` package is renamed to `trapos-sandbox-legacy`. - -**Intended outcome:** after installing the new trapos and setting `sandbox.url`, running -`sandbox health` in-game prints `sandboxOk: true` / `opencodeOk: true|false`. - -This plan was hardened through a full design interview (see **Decision log**). Notably, auth moved -from Basic-Auth-at-upgrade to an **in-protocol `hello` handshake**, which also prepares a future -"one account owns many computers, account stored server-side" model without building it yet. - -## Milestones & todolist - -High-level only — each sub-plan owns its granular steps. Tick a milestone when its sub-plan's -verification gate is green. - -- [x] **M1 — TS gateway** ([01-ts-gateway](./01-ts-gateway.md)): service builds, listens on 4444, - TS unit tests green (`npm test`, `npm run lint`/`check` clean). *No Lua dependency — build first.* -- [x] **M2 — Lua client** ([02-lua-client](./02-lua-client.md)): `libsandbox`/`servers`/`programs` - in place, Lua unit tests green via `just _craftos-test`. *Implements the Protocol contract.* -- [x] **M3 — Packages** ([03-packages](./03-packages.md)): legacy renamed, new `trapos-sandbox` - package + meta bump wired; `packages/index.json` consistent. -- [x] **M4 — Integration & tooling** ([04-integration-tooling](./04-integration-tooling.md)): - `just` recipes, `docs/public-ports.md`, TS integration harness + full-boot e2e green. - *Requires both sides — build last.* - -Dependency order: **M1 → M2 → M3 → M4**. M3 may proceed in parallel with M2 (mechanical). - -## Sub-plans index - -| # | Sub-plan | Scope | -|---|---|---| -| 01 | [TS gateway](./01-ts-gateway.md) | `tools/trap-sandbox/` service, registry, protocol, opencode probe, TS unit tests | -| 02 | [Lua client](./02-lua-client.md) | `apis/libsandbox.lua`, `servers/sandbox.lua`, `programs/sandbox.lua`, Lua unit tests | -| 03 | [Packages](./03-packages.md) | legacy rename, new package, `index.json`, meta bump | -| 04 | [Integration & tooling](./04-integration-tooling.md) | `just`/`npm.just`, `sandbox-serve`, docs, TS integration + full-boot e2e | - -## Decision log (grill outcomes) - -| Topic | Decision | -|---|---| -| Event shape | Explicit `event` field: `computer_request|computer_response|gateway_request|gateway_response`;`type` = redux action | -| Bidi scope | All 4 events now; reverse direction (`gateway_request`→`computer_response`) exercised by tests | -| Duplicate trapos id | **Replace existing (newest-wins)** — deliberate deviation from INIT's "reject the duplicate" | -| Identity | **Trust the frame** — gateway uses the client-declared `traposId`; no connection↔frame cross-validation | -| **Auth** | **In-protocol `hello` handshake** — secret in payload; **supersedes** Basic-Auth-at-upgrade + `?id=&label=` query and the "header support" risk | -| Auth reject | **Terminal**: daemon stops reconnecting on `unauthorized`, stores `lastError`; network drops still reconnect | -| Error model | `ok` + `error: { code, message }` with a stable code enum | -| Health disconnected | **Fail fast** with clear error; ~10s command timeout; ~5s opencode probe bound | -| Opencode wiring | `OPENCODE_SERVER_PASSWORD` + default `OPENCODE_URL=http://127.0.0.1:4242`, Basic Auth (opencode link only) | -| Health payload | `{ sandboxOk, opencodeOk, opencodeDetail? }` (detail shown only on failure) | -| Reverse scope (Lua) | Built-in `ping` only; no extensibility fan-out/response path in v1 | -| Future account seam | `accountId` constant `"local"` via `resolveAccount(secret)`; registry keyed by `(accountId, traposId)`; structured error codes | -| Host tooling | Full wiring: `just sandbox-serve`, `npm.just`/`install`/`ci`, `docs/public-ports.md` row for 4444 | -| Meta package | Add `trapos-sandbox` to `trapos` meta + bump version (daemon dormant until `sandbox.url` set) | -| Versions | Fastify `^5` + `@fastify/websocket` `^11`; `engines.node >=20` (Node 24 in use) | -| Tests | 3 layers (Lua-unit, TS-unit, TS-integration) + one full-boot e2e | - -> The "basic password mechanism" from INIT is preserved *in spirit* (a shared secret = -> `SANDBOX_PASSWORD`, mirroring `OPENCODE_SERVER_PASSWORD`) but delivered **in-band via the hello -> frame**, not as a Basic-Auth header. The opencode probe still uses Basic Auth to the opencode -> server (unchanged). - -## Protocol - -JSON frames. Computer-originated carry `traposSenderId`; gateway-originated carry `traposReceiverId`. -`traposId = ":