From 9bc1ce711df3ce254dde9df955efd0cc9d223c43 Mon Sep 17 00:00:00 2001 From: Guillaume ARM Date: Thu, 18 Jun 2026 20:23:06 +0200 Subject: [PATCH] refactor: rename accountId in networkId + cleanup --- AGENTS.md | 5 +- README.md | 9 +- .../adr-0002-eventloop-and-service-bus.md | 14 +- docs/adrs/adr-0019-mcp-over-cloud-gateway.md | 2 +- docs/periphemu.md | 2 +- manifest.json | 2 +- packages/index.json | 3 +- packages/trapos-cloud/tests/cloud.lua | 4 +- packages/trapos-core/tests/ccpm.lua | 76 +++---- packages/trapos-core/tests/libversion.lua | 20 +- packages/trapos-net/apis/librouter.lua | 49 ---- packages/trapos-net/apis/net.lua | 212 ------------------ packages/trapos-net/ccpm.json | 15 -- packages/trapos-net/programs/ping.lua | 35 --- packages/trapos-net/programs/router.lua | 69 ------ packages/trapos-net/servers/net-registrar.lua | 24 -- packages/trapos-net/servers/ping-server.lua | 23 -- packages/trapos-net/tests/net.lua | 164 -------------- packages/trapos-net/tests/router.lua | 74 ------ packages/trapos/ccpm.json | 3 +- tools/trapos-server/src/app.ts | 6 +- tools/trapos-server/src/auth.ts | 22 +- tools/trapos-server/src/index.ts | 2 +- tools/trapos-server/src/modules/mcp/tools.ts | 12 +- .../trapos-cloud-gateway/connection.ts | 32 +-- .../src/modules/trapos-cloud-gateway/index.ts | 6 +- .../modules/trapos-cloud-gateway/registry.ts | 42 ++-- .../test-integration/displaced.test.ts | 4 +- .../test-integration/duplicate.test.ts | 6 +- .../test-integration/mcp.test.ts | 2 +- .../test-integration/reverse-ping.test.ts | 4 +- tools/trapos-server/test/auth.test.ts | 16 +- tools/trapos-server/test/connection.test.ts | 14 +- tools/trapos-server/test/mcp.test.ts | 28 +-- tools/trapos-server/test/registry.test.ts | 64 +++--- 35 files changed, 194 insertions(+), 871 deletions(-) delete mode 100644 packages/trapos-net/apis/librouter.lua delete mode 100644 packages/trapos-net/apis/net.lua delete mode 100644 packages/trapos-net/ccpm.json delete mode 100644 packages/trapos-net/programs/ping.lua delete mode 100644 packages/trapos-net/programs/router.lua delete mode 100644 packages/trapos-net/servers/net-registrar.lua delete mode 100644 packages/trapos-net/servers/ping-server.lua delete mode 100644 packages/trapos-net/tests/net.lua delete mode 100644 packages/trapos-net/tests/router.lua diff --git a/AGENTS.md b/AGENTS.md index 0faaa24..ed4e1f6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -18,7 +18,7 @@ Use [`docs/README.md`](docs/README.md) as the entrypoint for CC:Tweaked, CraftOS - Git hooks own commit/push verification: pre-commit runs `just check test`, and pre-push runs `just ci`. When explicitly asked to commit and/or push, do not run `just test` manually first; rely on the hooks. See [ADR-0011](docs/adrs/adr-0011-repo-conventions.md). - After editing Lua or Markdown, run `just check` and fix all `luacheck` warnings and `lychee` broken-link reports (markdown link validation is offline-only via `just lint-markdown`). - Use 2-space indent, semicolons, and `local function`. -- `require` paths are absolute ComputerCraft paths, for example `require('/apis/net')()`. +- `require` paths are absolute ComputerCraft paths, for example `require('/apis/eventloop')()`. - Most API modules return factories; call the required module once before use. ## Running CraftOS-PC @@ -40,10 +40,7 @@ Always go through the `just` recipes — they set repo-local state and the macOS - `apis/eventloop.lua` is the single-threaded event loop around `os.pullEventRaw`; consider using it everywhere async behavior is needed. A handler that returns `api.STOP` auto-unregisters. - `startup/boot.lua` creates a single boot eventloop at `_G.bootEventLoop` and runs it alongside the shell via `parallel.waitForAny`. Servers register handlers on it and return; programs call services via `os.pullEvent` without touching the loop. See [ADR-0002](docs/adrs/adr-0002-eventloop-and-service-bus.md). - `apis/libtest.lua` is the lightweight test helper used by scripts under `tests/`; `/programs/runtest.lua` discovers tests, renders suite output, and owns the `__TRAPOS_TEST_OK__` success marker. -- `apis/net.lua` is a service-name bus on a single channel (`10`). `net.serve(name, handler)` registers a server handler; `net.call(name, payload, { destId, timeout })` and `net.send(name, payload, { destId })` are the client surface. `require('/apis/net')()` returns a singleton bound to `_G.bootEventLoop` when present, otherwise an ephemeral instance. -- A router (`/programs/router.lua`) must be running somewhere on the network; it registers a `modem_message` handler that stamps `routerId`, resolves label-addressed packets via a TTL map populated by `servers/net-registrar.lua`, and rebroadcasts. Without it, packets stay unrouted and consumers ignore them. - `servers/` register handlers on the boot eventloop and return; `programs/` are clients that exit. -- Single well-known channel: `10` (the bus). Service multiplexing happens inside the packet body. ## Boot And Install diff --git a/README.md b/README.md index c919ddf..3754ea5 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,6 @@ cherry-pick packages yourself: ``` wget run https://os.trapcloud.fr/install --cpm-only ccpm update -ccpm install trapos-net ccpm install trapos-ui ``` @@ -31,10 +30,9 @@ TrapOS is split into packages, each described by a `packages//ccpm.json`: - `trapos-core`: the package manager (`ccpm`), event loop, `trapos-upgrade`, `trapos-postinstall`, `events`. - `trapos-test`: the test framework (`libtest`) and suite runner (`runtest`). - `trapos-boot`: the startup MOTD and autostart server launcher. -- `trapos-net`: routed modem networking (`net`, `router`, `ping`, `ping-server`). - `trapos-ui`: the terminal UI toolkit (`libtui`, `tuidemo`). - `trapos-ai`: the AI client for `opencode serve`. -- `trapos`: full TrapOS meta-package (`trapos-boot`, `trapos-net`, `trapos-ui`, `trapos-test`, `trapos-ai`, `trapos-cloud`). +- `trapos`: full TrapOS meta-package (`trapos-boot`, `trapos-ui`, `trapos-test`, `trapos-ai`, `trapos-cloud`). The `trapos` meta-package is the user-facing full install. Package descriptors list files and autostart servers; installed state is tracked under `/trapos`. @@ -67,16 +65,11 @@ supported. State lives in `/trapos/ccpm.json` (registries), ## APIs - `/apis/eventloop`: a simple event loop API. -- `/apis/net`: an API to simplify sending and receiving routed messages, based on the `eventloop` library. ## Servers Servers listed in `manifest.autostart` are launched at boot by `startup/boot.lua`. -- `/servers/ping-server`: allows a machine to respond to a `ping` command. - ## Programs -- `router`: routes messages. You need to set up a router to use all `apis/net`-based programs and libraries. -- `ping`: pings machines using `apis/net`. - `events`: emits and logs computer events. - `trapos-upgrade`: runs `ccpm update` then `ccpm upgrade`; pass `--reboot`/`-r` to reboot after upgrading. - `trapos-postinstall`: prints the post-install welcome and next steps (run automatically by the installer). diff --git a/docs/adrs/adr-0002-eventloop-and-service-bus.md b/docs/adrs/adr-0002-eventloop-and-service-bus.md index 3121038..f3811a3 100644 --- a/docs/adrs/adr-0002-eventloop-and-service-bus.md +++ b/docs/adrs/adr-0002-eventloop-and-service-bus.md @@ -13,7 +13,7 @@ Accepted ComputerCraft is event-driven. Direct `os.pullEvent` loops are easy to write but hard to compose when multiple things need to happen at the same time. Without a single substrate the repo accumulated several distinct problems: - Each long-lived process owned a private event loop, including the router (`programs/router.lua` was a hand-rolled `while true / os.pullEvent`). With N autostart servers, `parallel.waitForAll` ran N coroutines each pumping an independent `os.pullEventRaw`. Events were broadcast to every coroutine but only one would have a relevant handler — wasteful and conceptually awkward. -- `_G.isRouterEnabled` mutated send behavior across the codebase. [`apis/net.lua`](../../packages/trapos-net/apis/net.lua) `sendRaw` switched its transmit path based on a global flag set by the router program, so the same function call meant different things depending on which machine ran it. +- `_G.isRouterEnabled` mutated send behavior across the codebase. `apis/net.lua` `sendRaw` switched its transmit path based on a global flag set by the router program, so the same function call meant different things depending on which machine ran it. - Channel numbers leaked into every client. `servers/ping-server.lua` and `programs/ping.lua` both duplicated a `PING_CHANNEL` constant; there was no service registry. Adding a new service meant picking a free integer and replicating it on both ends. - Label collision was a silent footgun. Two machines sharing the same label both accepted and rebroadcast packets addressed to that label, producing duplicate responses and duplicate retransmits. - `os.sleep` looked innocent but broke the substrate. Its CC:Tweaked implementation yields via `os.pullEvent("timer")`. While the sleep is in flight, the enclosing eventloop's `os.pullEventRaw` is paused; non-`timer` events are silently discarded; even `eventloop.setTimeout` callbacks scheduled before the sleep cannot fire until it returns. This bit `apis/libai.lua` `pollMessage`, which used a sleep-based throttle and froze the whole loop the moment a caller invoked it from inside a handler. @@ -27,7 +27,7 @@ Net's blast radius at the time of the bus rewrite was small (only ping consumed New async code uses [`apis/eventloop.lua`](../../packages/trapos-core/apis/eventloop.lua). Event handlers, timers, server listeners, and UI behavior compose through the eventloop instead of each feature owning its own blocking loop. - Prefer `eventloop.register`, `setTimeout`, `onStart`, `onStop`, and `startLoop` for async behavior. -- APIs that listen for events accept an existing event loop as a constructor argument, the way [`apis/net.lua`](../../packages/trapos-net/apis/net.lua) does. Do not create a private loop inside a module. +- APIs that listen for events accept an existing event loop as a constructor argument, the way `apis/net.lua` does. Do not create a private loop inside a module. - Direct `os.pullEvent` loops should be rare and justified (CLI programs waiting for a single reply are the main exception). - A handler that returns `api.STOP` auto-unregisters. @@ -35,7 +35,7 @@ New async code uses [`apis/eventloop.lua`](../../packages/trapos-core/apis/event `startup/servers.lua` creates a single `createEventLoop()` instance, stores it at `_G.bootEventLoop`, runs autostart server files (which register handlers and return without blocking), then runs `parallel.waitForAny(shellFn, eventLoopFn)`. The shell and the eventloop are the only two coroutines. -[`apis/net.lua`](../../packages/trapos-net/apis/net.lua) exposes a service-name bus on a single channel: +`apis/net.lua` exposes a service-name bus on a single channel: - `net.serve(name, handler)` — register a server handler (server-side). - `net.call(name, payload, opts)` — request/response with timeout (client-side). @@ -44,11 +44,11 @@ New async code uses [`apis/eventloop.lua`](../../packages/trapos-core/apis/event All traffic flows on channel `10` and is demultiplexed inside the packet body via a `service` field. Channel numbers stop being a public concept. `require('/apis/net')()` returns a singleton bound to `_G.bootEventLoop` when present, otherwise an ephemeral instance. CLI programs stay standalone: `net.call` internally uses `os.pullEvent` with a timer, so programs do not need the boot eventloop to receive a response. -[`programs/router.lua`](../../packages/trapos-net/programs/router.lua) registers handlers on the same boot eventloop everything else uses. It owns a TTL-based label map extracted into [`apis/librouter.lua`](../../packages/trapos-net/apis/librouter.lua) for testability. Machines with a label autostart [`servers/net-registrar.lua`](../../packages/trapos-net/servers/net-registrar.lua), which periodically broadcasts `(id, label)` so the router can resolve label-addressed packets. Duplicate label registrations are rejected with a printed warning. `_G.isRouterEnabled` is gone; the router service flips a local flag via `net.setRouter(true)` instead. +`programs/router.lua` registers handlers on the same boot eventloop everything else uses. It owns a TTL-based label map extracted into `apis/librouter.lua` for testability. Machines with a label autostart `servers/net-registrar.lua`, which periodically broadcasts `(id, label)` so the router can resolve label-addressed packets. Duplicate label registrations are rejected with a printed warning. `_G.isRouterEnabled` is gone; the router service flips a local flag via `net.setRouter(true)` instead. ### 3. `os.sleep` discipline -In library, server, and program code that may run inside an eventloop (directly or transitively), use `eventloop.setTimeout` for any waiting, throttling, polling, or retry-with-delay. Libraries that need to temporize must take an eventloop factory through their constructor rather than baking a hardcoded sleep call. [`apis/net.lua`](../../packages/trapos-net/apis/net.lua) `sendRequest` is the canonical private-eventloop pattern: create a private eventloop, schedule the wait through `setTimeout`, then `runLoop` until the work resolves — synchronous from the caller's perspective, but the dispatcher stays alive internally so handlers can compose around it via `parallel.waitForAll`. +In library, server, and program code that may run inside an eventloop (directly or transitively), use `eventloop.setTimeout` for any waiting, throttling, polling, or retry-with-delay. Libraries that need to temporize must take an eventloop factory through their constructor rather than baking a hardcoded sleep call. `apis/net.lua` `sendRequest` is the canonical private-eventloop pattern: create a private eventloop, schedule the wait through `setTimeout`, then `runLoop` until the work resolves — synchronous from the caller's perspective, but the dispatcher stays alive internally so handlers can compose around it via `parallel.waitForAll`. `os.sleep` remains acceptable only in narrow cases: @@ -64,8 +64,8 @@ New code must not expose a `sleep` injection point on its constructor. If a wait - The router program returns immediately instead of blocking the shell. Users type `router` once on the chosen machine and continue using the shell. - Label collisions are detected and rejected at registration time, with a clear warning, instead of causing silent duplicate delivery. - A router must still be running somewhere on the network for cross-machine label-addressed packets; without one, non-router senders produce packets with `routerId = nil` and consumers drop them on receive. -- Programs that need to wait for events still work by direct `os.pullEvent`, but if a program registers a long-lived handler on `_G.bootEventLoop` and exits, the handler keeps firing with a stale closure. Programs should prefer `call`/`send` over `serve`/`listen`. This is documented in [`apis/net.lua`](../../packages/trapos-net/apis/net.lua) but not enforced. -- Tests for the router state machine live in [`tests/router.lua`](../../packages/trapos-net/tests/router.lua) and exercise [`apis/librouter.lua`](../../packages/trapos-net/apis/librouter.lua) with an injected clock. Tests for the net packet shape and dispatch live in [`tests/net.lua`](../../packages/trapos-net/tests/net.lua) with a fake modem. +- Programs that need to wait for events still work by direct `os.pullEvent`, but if a program registers a long-lived handler on `_G.bootEventLoop` and exits, the handler keeps firing with a stale closure. Programs should prefer `call`/`send` over `serve`/`listen`. This is documented in `apis/net.lua` but not enforced. +- Tests for the router state machine live in `tests/router.lua` and exercise `apis/librouter.lua` with an injected clock. Tests for the net packet shape and dispatch live in `tests/net.lua` with a fake modem. - Slightly more ceremony in "synchronous-looking" library functions that wait: a private eventloop plus a small `attempt`/`finish` pair. The benefit is clean composition with any caller's eventloop. - Test fakes shift from a `sleep` stub to a synchronous eventloop double. Ergonomics are comparable; the eventloop fake additionally lets tests observe `pending` and `stopped` state, catching leaks the sleep stub would have missed. - Existing call sites are migrated opportunistically when they cause observable bugs. The first `os.sleep` migration is `apis/libai.lua`. diff --git a/docs/adrs/adr-0019-mcp-over-cloud-gateway.md b/docs/adrs/adr-0019-mcp-over-cloud-gateway.md index 015c179..220a850 100644 --- a/docs/adrs/adr-0019-mcp-over-cloud-gateway.md +++ b/docs/adrs/adr-0019-mcp-over-cloud-gateway.md @@ -28,7 +28,7 @@ Move the MCP features onto the cloud gateway and retire the dedicated MCP transp - **Host (`tools/trapos-server`):** a second Fastify listener (`src/modules/mcp`) bound to `127.0.0.1` (default `MCP_PORT=4445`) speaks the same custom JSON-RPC 2.0 as the old - bridge. Each tool call is a `GatewayRegistry.request(accountId, traposId, type, payload)` + bridge. Each tool call is a `GatewayRegistry.request(networkId, traposId, type, payload)` reusing the existing gateway socket. Computers are addressed by **`traposId`** (`":