refactor(daemon): rename servers to daemons

This commit is contained in:
Guillaume ARM 2026-06-18 23:53:32 +02:00
parent d69963f89c
commit dd778d0c29
24 changed files with 54 additions and 54 deletions

View File

@ -4,7 +4,7 @@ Concise guidance for agents working in this repository.
## Project
ComputerCraft / CC:Tweaked Lua APIs, servers, and programs for Minecraft 1.21. Code runs in the ComputerCraft runtime, not standard Lua.
ComputerCraft / CC:Tweaked Lua APIs, daemons, and programs for Minecraft 1.21. Code runs in the ComputerCraft runtime, not standard Lua.
Use [`docs/README.md`](docs/README.md) as the entrypoint for CC:Tweaked, CraftOS-PC, Advanced Peripherals, and Create integration documentation links. Use [`docs/adrs/README.md`](docs/adrs/README.md) for repository architecture decisions.
@ -38,19 +38,19 @@ Always go through the `just` recipes — they set repo-local state and the macOS
## Architecture
- `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).
- `startup/boot.lua` creates a single boot eventloop at `_G.bootEventLoop` and runs it alongside the shell via `parallel.waitForAny`. Daemons 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.
- `servers/` register handlers on the boot eventloop and return; `programs/` are clients that exit.
- `daemons/` register handlers on the boot eventloop and return; `programs/` are clients that exit.
## Boot And Install
- `startup/boot.lua` creates the boot eventloop, runs autostart server files (which register handlers and return), then runs the shell and the eventloop in parallel via `parallel.waitForAny`.
- `startup/boot.lua` creates the boot eventloop, runs autostart daemon files (which register handlers and return), then runs the shell and the eventloop in parallel via `parallel.waitForAny`.
- Preserve `periphemu` guards used for CraftOS-PC emulation; see [`docs/craftos_pc_glossary.md`](docs/craftos_pc_glossary.md) for upstream emulator references.
- Global CraftOS-PC save data lives in `~/Library/Application Support/CraftOS-PC` on macOS and `~/.local/share/craftos-pc` on Linux. Repo launchers use local state instead: `.craftos/` for TrapOS and `.craftos-vanilla/` for vanilla CraftOS-PC. See [`docs/install-craftos-pc.md`](docs/install-craftos-pc.md).
- TrapOS ships as packages, each described by `packages/<name>/ccpm.json` (`name`, `version`, `dependencies`, `files`, `autostart`); `packages/index.json` lists them. Source files stay in place — descriptors only reference them. To ship a new file, add it to the right package's `files` (and `autostart` if it is a server). `packages/trapos/ccpm.json` is the full OS meta-package. See [ADR-0010](docs/adrs/adr-0010-ccpm-package-manager.md).
- TrapOS ships as packages, each described by `packages/<name>/ccpm.json` (`name`, `version`, `dependencies`, `files`, `autostart`); `packages/index.json` lists them. Source files stay in place — descriptors only reference them. To ship a new file, add it to the right package's `files` (and `autostart` if it is a daemon). `packages/trapos/ccpm.json` is the full OS meta-package. See [ADR-0010](docs/adrs/adr-0010-ccpm-package-manager.md).
- `install-trapos.lua` is the one-time wget bootstrap (public entry point `https://os.trapcloud.fr/install`). It installs `trapos-core`/`ccpm`, seeds the default `guillaumearm/cc-libs` registry as a `gitea` registry on `git.trapcloud.fr` tracking `master` (the only channel), then runs `ccpm update` + `ccpm install trapos` and `trapos-postinstall`. Pass `--cpm-only` to stop after the ccpm bootstrap.
- `ccpm` (in `trapos-core`) is the package manager: `apis/libccpm.lua` is the testable core (factory with injectable `http`/`stateDir`/`installRoot`), `programs/ccpm.lua` the CLI. State: `/trapos/ccpm.json` (registries), `/trapos/ccpm.lock.json` (installed packages), and `/trapos/ccpm.cache.json` (available packages from `ccpm update`). Never use the word "manifest" in ccpm — it is reserved for the OS manifest.
- Add new servers to the relevant package `autostart` list as needed.
- Add new daemons to the relevant package `autostart` list as needed.
## Conventions

View File

@ -29,13 +29,13 @@ TrapOS is split into packages, each described by a `packages/<name>/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-boot`: the startup MOTD and autostart daemon launcher.
- `trapos-ui`: the terminal UI toolkit (`libtui`, `tuidemo`).
- `trapos-ai`: the AI client for `opencode serve`.
- `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`.
files and autostart daemons; installed state is tracked under `/trapos`.
## ccpm

View File

@ -95,7 +95,7 @@ set cloud.password secret # must match TRAPOS_SERVER_PASSWORD on the host
reboot
```
The `servers/cloud` daemon connects automatically at boot, and `servers/mcp-computer-server`
The `daemons/cloud` daemon connects automatically at boot, and `daemons/mcp-computer-server`
starts serving `exec-lua` / `write-file` over that one connection. Check the link with:
```sh
@ -142,8 +142,8 @@ fail instead of being created automatically.
- `ai` says missing `opencc.server_url`: run the `set opencc.server_url ...` command again.
- `ai` cannot reach server: check `opencode serve`, public host, port `4242`, and ComputerCraft HTTP rules.
- `cloud health` fails: enable ComputerCraft HTTP/WebSocket support and check `cloud.url` (port `4444`).
- MCP sees no computers: confirm `cloud.url` is set and the computer was rebooted so `servers/cloud` is running.
- `exec-lua` / `write-file` return `unknown_type`: that computer is not running `servers/mcp-computer-server` (set `cloud.url` and reboot).
- MCP sees no computers: confirm `cloud.url` is set and the computer was rebooted so `daemons/cloud` is running.
- `exec-lua` / `write-file` return `unknown_type`: that computer is not running `daemons/mcp-computer-server` (set `cloud.url` and reboot).
- `exec-lua` or `write-file` is missing after updating: restart OpenCode so it reloads the MCP tool list.
More detail: [`opencode_server_guide.md`](opencode_server_guide.md), [`public-ports.md`](public-ports.md).

View File

@ -86,7 +86,7 @@ On macOS, use the `--rom` form shown above if the command fails with `Could not
## Repository-local launches
`just trapos` launches CraftOS-PC with the TrapOS dev environment: persistent save data rooted at `.craftos` instead of the platform default user-data directory, and read-only mounts of the repository at `/trapos` plus each shipped top-level directory at its ComputerCraft root path (such as `/apis`, `/programs`, `/servers`, `/startup`, and `/tests`). Generated files live under `.craftos/config/` and `.craftos/computer/`, while the root `.gitignore` keeps that state untracked.
`just trapos` launches CraftOS-PC with the TrapOS dev environment: persistent save data rooted at `.craftos` instead of the platform default user-data directory, and read-only mounts of the repository at `/trapos` plus each shipped top-level directory at its ComputerCraft root path (such as `/apis`, `/programs`, `/daemons`, `/startup`, and `/tests`). Generated files live under `.craftos/config/` and `.craftos/computer/`, while the root `.gitignore` keeps that state untracked.
`just craftos` launches a vanilla CraftOS-PC with no mounts, persistent under `.craftos-vanilla/` (also gitignored). Use it when a probe should not see TrapOS files — for example, to confirm a behavior is upstream rather than TrapOS-specific.

View File

@ -170,7 +170,7 @@ shell.setDir('/');
fs.makeDir('/programs');
fs.makeDir('/apis');
fs.makeDir('/startup');
fs.makeDir('/servers');
fs.makeDir('/daemons');
fs.makeDir(LOCAL_STATE_DIR);
local allFiles = {};
@ -206,7 +206,7 @@ for _, desc in ipairs(resolved) do
};
end
-- Aggregated OS state for motd/servers/upgrade (unchanged consumers).
-- Aggregated OS state for motd/daemons/upgrade (unchanged consumers).
writeJsonFile(LOCAL_MANIFEST_PATH, {
name = manifest.name or 'TrapOS',
version = manifest.version or '?',

View File

@ -12,7 +12,7 @@ trapos *args: check-install stage
argv+=(--rom /Applications/CraftOS-PC.app/Contents/Resources)
fi
argv+=(--mount-ro "/trapos=$repo")
for dir in apis programs servers startup tests; do
for dir in apis programs daemons startup tests; do
if [ -d "$repo/.stage/$dir" ]; then
argv+=(--mount-ro "/$dir=$repo/.stage/$dir")
fi
@ -79,7 +79,7 @@ trapos-exec code: stage
printf '%s\n' 'status.close()'
printf '%s\n' 'os.shutdown()'
} > "$runner"
mount_arg=(--mount-ro "/trapos=$repo" --mount-ro "/apis=$repo/.stage/apis" --mount-ro "/programs=$repo/.stage/programs" --mount-ro "/servers=$repo/.stage/servers" --mount-ro "/startup=$repo/.stage/startup" --mount-ro "/tests=$repo/.stage/tests" --mount-ro "/headless=$stage_dir")
mount_arg=(--mount-ro "/trapos=$repo" --mount-ro "/apis=$repo/.stage/apis" --mount-ro "/programs=$repo/.stage/programs" --mount-ro "/daemons=$repo/.stage/daemons" --mount-ro "/startup=$repo/.stage/startup" --mount-ro "/tests=$repo/.stage/tests" --mount-ro "/headless=$stage_dir")
craftos --directory "$data_dir" --headless "${rom_arg[@]}" "${mount_arg[@]}" --exec "shell.run('/headless/exec.lua')" >"$tmp" 2>&1 &
pid="$!"
( sleep "$timeout_seconds"; kill -TERM "$pid" >/dev/null 2>&1 ) &

View File

@ -1,9 +1,9 @@
# Assemble the flat mount roots the CraftOS-PC harness expects from the
# per-package sources. Lua loads its deps by absolute mount path (require
# '/apis/x') and ccpm installs files flat, but sources now live under
# packages/<pkg>/{apis,programs,servers,startup,tests}. This merges them back
# packages/<pkg>/{apis,programs,daemons,startup,tests}. This merges them back
# into a gitignored .stage/ that the dev/test recipes mount as /apis, /programs,
# /servers, /startup, /tests. Regenerated on every run so it always reflects the
# /daemons, /startup, /tests. Regenerated on every run so it always reflects the
# working tree.
stage:
#!/usr/bin/env bash
@ -12,7 +12,7 @@ stage:
cd "$repo"
stage="$repo/.stage"
rm -rf "$stage"
for dir in apis programs servers startup tests; do
for dir in apis programs daemons startup tests; do
for pkgdir in packages/*/"$dir"; do
[ -d "$pkgdir" ] || continue
mkdir -p "$stage/$dir"

View File

@ -1,6 +1,6 @@
{
"name": "TrapOS",
"version": "0.12.5",
"version": "0.12.6",
"branch": "master",
"packages": [
"trapos"

View File

@ -1,12 +1,12 @@
{
"packages": {
"trapos-core": "0.9.4",
"trapos-core": "0.9.5",
"trapos-test": "0.2.3",
"trapos-boot": "0.4.3",
"trapos-boot": "0.4.4",
"trapos-ui": "0.2.4",
"trapos-ai": "0.8.0",
"trapos-cloud": "0.4.4",
"trapos-cloud": "0.4.5",
"trapos-sandbox-legacy": "0.3.2",
"trapos": "0.12.5"
"trapos": "0.12.6"
}
}

View File

@ -1,6 +1,6 @@
{
"name": "trapos-boot",
"version": "0.4.3",
"version": "0.4.4",
"description": "TrapOS boot: startup MOTD and autostart server launcher",
"dependencies": ["trapos-core"],
"files": ["programs/motd.lua", "startup/boot.lua"],

View File

@ -74,15 +74,15 @@ local function eventLoopFn()
end
local manifest = readLocalManifest()
local SERVERS = (manifest and manifest.autostart) or {}
local DAEMONS = (manifest and manifest.autostart) or {}
if #SERVERS > 0 then
print("\nStarting servers...")
for _, serverName in ipairs(SERVERS) do
print("\t\t" .. serverName)
local ok, err = pcall(shell.run, serverName)
if #DAEMONS > 0 then
print("\nStarting daemons...")
for _, daemonName in ipairs(DAEMONS) do
print("\t\t" .. daemonName)
local ok, err = pcall(shell.run, daemonName)
if not ok then
print("server '" .. serverName .. "' failed to start: " .. tostring(err))
print("daemon '" .. daemonName .. "' failed to start: " .. tostring(err))
end
end
end

View File

@ -140,7 +140,7 @@ local function createCloud()
if frame.event == 'server_request' then
-- Reverse direction. `ping` stays built-in so it answers without any handler.
-- Other types are delegated to in-OS servers (re-emitted via ctx.queueRequest)
-- Other types are delegated to in-OS daemons (re-emitted via ctx.queueRequest)
-- when a handler has advertised the type (ctx.isServed); an unadvertised type
-- still gets an immediate unknown_type reply.
if frame.type == 'ping' then
@ -224,7 +224,7 @@ local function createCloud()
-- open / connection failed / connection closed) so a long outage logs once on entry
-- and once on recovery instead of flooding the file every reconnectDelay seconds.
local reconnecting = false;
-- Gateway_request types advertised by in-OS servers (via trapos_cloud_serve).
-- Gateway_request types advertised by in-OS daemons (via trapos_cloud_serve).
-- Inbound frames of an advertised type are re-emitted as trapos_cloud_request
-- events; unadvertised (non-ping) types get an immediate unknown_type reply.
local servedTypes = {};
@ -402,7 +402,7 @@ local function createCloud()
end
end);
-- In-OS servers advertise the server_request types they handle. Recorded so
-- In-OS daemons advertise the server_request types they handle. Recorded so
-- onMessage routes those (and only those) as trapos_cloud_request events.
el.register('trapos_cloud_serve', function(msgType)
if type(msgType) == 'string' and msgType ~= '' then

View File

@ -1,7 +1,7 @@
-- Computer-side MCP execution primitives.
--
-- Protocol-agnostic building blocks for the MCP `exec-lua` / `write-file` features.
-- The transport lives elsewhere: servers/mcp-computer-server.lua reacts to cloud
-- The transport lives elsewhere: daemons/mcp-computer-server.lua reacts to cloud
-- server_request events and replies through the trapos-server gateway (see
-- apis/libcloud.lua). This module only knows how to run code and write files.

View File

@ -1,14 +1,14 @@
{
"name": "trapos-cloud",
"version": "0.4.4",
"description": "TrapOS cloud gateway client: WS daemon (servers/cloud) + cloud command + MCP exec/write server",
"version": "0.4.5",
"description": "TrapOS cloud gateway client: WS daemon (daemons/cloud) + cloud command + MCP exec/write server",
"dependencies": ["trapos-core"],
"files": [
"apis/libcloud.lua",
"apis/libmcpcomputer.lua",
"programs/cloud.lua",
"servers/cloud.lua",
"servers/mcp-computer-server.lua"
"daemons/cloud.lua",
"daemons/mcp-computer-server.lua"
],
"autostart": ["servers/cloud", "servers/mcp-computer-server"]
"autostart": ["daemons/cloud", "daemons/mcp-computer-server"]
}

View File

@ -2,7 +2,7 @@
--
-- No longer owns a websocket. It registers `exec-lua` / `write-file` handlers on the
-- cloud daemon's event bus (apis/libcloud.serve), so requests arrive over the one
-- gateway connection owned by servers/cloud.lua and replies are sent back through it.
-- gateway connection owned by daemons/cloud.lua and replies are sent back through it.
-- It only handles requests while a cloud is configured, so exec stays gated behind
-- the same trusted, authenticated connection as the rest of the gateway.

View File

@ -407,7 +407,7 @@ local function createCcpm(opts)
end
local function cleanupEmptyGeneratedDirs()
for _, dir in ipairs({ 'programs', 'apis', 'servers', 'startup' }) do
for _, dir in ipairs({ 'programs', 'apis', 'daemons', 'startup' }) do
local target = installTarget(dir);
if target ~= '/' and fs.exists(target) and fs.isDir(target) and #fs.list(target) == 0 then
fs.delete(target);

View File

@ -1,6 +1,6 @@
{
"name": "trapos-core",
"version": "0.9.4",
"version": "0.9.5",
"description": "TrapOS base: package manager, event loop, upgrade and event tools",
"dependencies": [],
"files": [

View File

@ -235,7 +235,7 @@ testlib.test("installing trapos writes aggregated os state", function()
version = "1",
dependencies = { "trapos-core" },
files = { "apis/libtui.lua" },
autostart = { "servers/demo" },
autostart = { "daemons/demo" },
}),
[base .. "packages/trapos-core/programs/ccpm.lua"] = "ccpm-body",
[base .. "packages/trapos-ui/apis/libtui.lua"] = "ui-body",
@ -251,7 +251,7 @@ testlib.test("installing trapos writes aggregated os state", function()
f.close()
testlib.assertEquals(manifest.version, "1")
testlib.assertEquals(#manifest.files, 2)
testlib.assertEquals(manifest.autostart[1], "servers/demo")
testlib.assertEquals(manifest.autostart[1], "daemons/demo")
end)
testlib.test("registry add and remove round-trip", function()

View File

@ -1,6 +1,6 @@
{
"name": "trapos",
"version": "0.12.5",
"version": "0.12.6",
"description": "TrapOS full install meta-package",
"dependencies": [
"trapos-boot",

View File

@ -2,7 +2,7 @@ import assert from "node:assert/strict";
import test from "node:test";
import { formatFailure, startCraftos, startFakeOpencode, startServer } from "./harness.js";
// Full-boot e2e: a real CraftOS computer runs the real servers/cloud.lua daemon
// Full-boot e2e: a real CraftOS computer runs the real daemons/cloud.lua daemon
// (driven by cloud.* settings) and the real `cloud health` program against the
// real gateway, with a healthy fake-opencode behind it.
test("full boot: settings -> daemon -> cloud health prints serverOk/opencodeOk true", async () => {

View File

@ -135,8 +135,8 @@ export type CraftosHandle = {
};
// Spawn headless CraftOS-PC running a Lua script from test-integration/lua, with the
// repo mounted read-only (/apis, /servers, /programs, /trapos) so the scripts can
// require the real libcloud / servers / programs.
// repo mounted read-only (/apis, /daemons, /programs, /trapos) so the scripts can
// require the real libcloud / daemons / programs.
export function startCraftos(
luaName: string,
opts: { computerId?: number; computerLabel?: string; shellArgs?: string[]; timeoutMs?: number } = {},
@ -160,7 +160,7 @@ export function startCraftos(
"--mount-ro", `/staging=${LUA_DIR}`,
"--mount-ro", `/trapos=${REPO_ROOT}`,
"--mount-ro", `/apis=${join(REPO_ROOT, ".stage/apis")}`,
"--mount-ro", `/servers=${join(REPO_ROOT, ".stage/servers")}`,
"--mount-ro", `/daemons=${join(REPO_ROOT, ".stage/daemons")}`,
"--mount-ro", `/programs=${join(REPO_ROOT, ".stage/programs")}`,
];
if (opts.computerId !== undefined) {

View File

@ -1,6 +1,6 @@
-- Full-boot e2e: replicate the real boot path (startup/boot.lua) closely enough
-- to prove the whole stack works end to end. Sets the cloud.* settings, creates
-- the global event loop, runs the REAL servers/cloud.lua daemon, then under
-- the global event loop, runs the REAL daemons/cloud.lua daemon, then under
-- parallel waits for the connection and runs the REAL `cloud status` program.
--
-- Args (via shell.run): url, secret ('' => none).
@ -17,7 +17,7 @@ end
_G.bootEventLoop = require('/apis/eventloop')();
-- Start the real daemon (reads cloud.url/cloud.password, opens the WS).
shell.run('/servers/cloud.lua');
shell.run('/daemons/cloud.lua');
parallel.waitForAny(
function() _G.bootEventLoop.runLoop(true); end,

View File

@ -1,6 +1,6 @@
-- Integration-test driver: the REAL reactive MCP computer server. Connects the real
-- libcloud daemon to the gateway and serves exec-lua / run-file / write-file via cloud.serve
-- (same wiring as servers/mcp-computer-server.lua), then stays registered so the TS
-- (same wiring as daemons/mcp-computer-server.lua), then stays registered so the TS
-- side can drive the MCP tools over the gateway.
--
-- Args (via shell.run): url, secret.