Files
self 7ddfd51139 feat: add Legion operator config
Create legion-config.jsonc beside local state and route its DNS TTL, recursive resolver, and ACME directory settings through runtime services.
2026-07-03 19:14:48 +02:00

891 lines
26 KiB
TypeScript

import assert from "node:assert/strict"
import { execFile } from "node:child_process"
import { randomUUID } from "node:crypto"
import { mkdtemp, rm, stat } from "node:fs/promises"
import { createConnection, type Socket } from "node:net"
import { tmpdir } from "node:os"
import { join } from "node:path"
import test from "node:test"
import {
createDaemonLegionApiClient,
createDaemonLegionCliClient,
getReachableDaemonMetadata,
getDaemonStatus,
runDaemonApplyCommand,
readDaemonMetadata,
renderDaemonTokenExport,
startLegionDaemonServer,
stopDaemon
} from "../../src/engine/daemon"
import { LegionEngineError } from "../../src/engine/errors"
import { createDaemonLineParser, encodeDaemonMessage } from "../../src/engine/daemon-protocol"
import type { LegionEnginePaths } from "../../src/engine/paths"
test("daemon server runs structured CLI calls over local transport", async () => {
const root = await mkdtemp(join(tmpdir(), "legion-daemon-test-"))
const paths = createPaths(root)
const calls: string[] = []
const server = await startLegionDaemonServer(paths, {
cli: {
async materialize() {
calls.push("materialize")
return { activity: null }
}
} as never
})
try {
const metadata = await readDaemonMetadata(paths.daemonMetadataPath)
assert.ok(metadata)
assert.equal("token" in metadata, false)
if (process.platform === "win32") {
assert.ok(server.endpoint.startsWith("\\\\.\\pipe\\legion-kk-"))
} else {
assert.equal((await stat(root)).mode & 0o777, 0o700)
assert.equal((await stat(paths.daemonMetadataPath)).mode & 0o777, 0o600)
assert.ok(server.endpoint.startsWith(join(tmpdir(), "legion-kk-")))
assert.ok(server.endpoint.length < 100)
assert.equal((await stat(server.endpoint)).mode & 0o777, 0o600)
}
const client = await createDaemonLegionCliClient(paths, { token: server.token })
assert.ok(client)
assert.deepEqual(await client.materialize(), { activity: null })
assert.deepEqual(calls, ["materialize"])
let stdout = ""
const statusCode = await getDaemonStatus(
paths,
{
stdout: { write: (chunk: string) => ((stdout += chunk), true) },
stderr: { write: () => true }
},
{ token: server.token }
)
assert.equal(statusCode, 0)
assert.match(stdout, /operation: .* completed cli\.materialize/)
} finally {
await server.close()
await rm(root, { recursive: true, force: true })
}
})
test("daemon apply command starts materialization asynchronously by default", async () => {
const root = await mkdtemp(join(tmpdir(), "legion-daemon-test-"))
const paths = createPaths(root)
let releaseMaterialize!: () => void
const materializeReleased = new Promise<void>((resolve) => {
releaseMaterialize = resolve
})
const server = await startLegionDaemonServer(paths, {
cli: {
async materialize() {
await materializeReleased
return { activity: null }
}
} as never
})
try {
let stdout = ""
let stderr = ""
const exitCode = await runDaemonApplyCommand(
paths,
{
stdout: { write: (chunk: string) => ((stdout += chunk), true) },
stderr: { write: (chunk: string) => ((stderr += chunk), true) }
},
{ token: server.token, json: true }
)
assert.equal(exitCode, 0)
assert.equal(stderr, "")
const output = JSON.parse(stdout) as { status: string; operationId: string }
assert.equal(output.status, "running")
assert.match(output.operationId, /.+/)
let statusOut = ""
assert.equal(
await getDaemonStatus(
paths,
{
stdout: { write: (chunk: string) => ((statusOut += chunk), true) },
stderr: { write: () => true }
},
{ token: server.token }
),
0
)
assert.match(
statusOut,
new RegExp(`operation: ${output.operationId} running cli\\.materialize`)
)
releaseMaterialize()
} finally {
releaseMaterialize()
await server.close()
await rm(root, { recursive: true, force: true })
}
})
test("daemon apply command waits for materialization when requested", async () => {
const root = await mkdtemp(join(tmpdir(), "legion-daemon-test-"))
const paths = createPaths(root)
let completed = false
const server = await startLegionDaemonServer(paths, {
cli: {
async materialize() {
await new Promise((resolve) => setTimeout(resolve, 10))
completed = true
return { activity: null }
}
} as never
})
try {
let stdout = ""
let stderr = ""
const exitCode = await runDaemonApplyCommand(
paths,
{
stdout: { write: (chunk: string) => ((stdout += chunk), true) },
stderr: { write: (chunk: string) => ((stderr += chunk), true) }
},
{ token: server.token, json: true, wait: true }
)
assert.equal(exitCode, 0)
assert.equal(stderr, "")
assert.equal(completed, true)
const output = JSON.parse(stdout) as { status: string; operationId: string }
assert.equal(output.status, "materialized")
assert.match(output.operationId, /.+/)
} finally {
await server.close()
await rm(root, { recursive: true, force: true })
}
})
test("daemon apply wait tolerates transient unreachable status polls", async () => {
const root = await mkdtemp(join(tmpdir(), "legion-daemon-test-"))
const paths = createPaths(root)
let releaseMaterialize!: () => void
const materializeReleased = new Promise<void>((resolve) => {
releaseMaterialize = resolve
})
const server = await startLegionDaemonServer(paths, {
cli: {
async materialize() {
await materializeReleased
return { activity: null }
}
} as never
})
const previousGrace = process.env["LEGION_DAEMON_OPERATION_UNREACHABLE_GRACE_MS"]
process.env["LEGION_DAEMON_OPERATION_UNREACHABLE_GRACE_MS"] = "25"
try {
let stderr = ""
const startedAt = Date.now()
const apply = runDaemonApplyCommand(
paths,
{
stdout: { write: () => true },
stderr: { write: (chunk: string) => ((stderr += chunk), true) }
},
{ token: server.token, wait: true }
)
await waitForCondition(async () => {
let stdout = ""
const statusCode = await getDaemonStatus(
paths,
{
stdout: { write: (chunk: string) => ((stdout += chunk), true) },
stderr: { write: () => true }
},
{ token: server.token }
)
return statusCode === 0 && /operation: .* running cli\.materialize/.test(stdout)
})
await server.close()
await assert.rejects(apply, /Legion daemon is unreachable\./)
assert.equal(stderr, "")
assert.ok(Date.now() - startedAt >= 25)
} finally {
if (previousGrace === undefined) {
delete process.env["LEGION_DAEMON_OPERATION_UNREACHABLE_GRACE_MS"]
} else {
process.env["LEGION_DAEMON_OPERATION_UNREACHABLE_GRACE_MS"] = previousGrace
}
releaseMaterialize()
await server.close().catch(() => undefined)
await rm(root, { recursive: true, force: true })
}
})
test("daemon apply trace forwards node streams", async () => {
const root = await mkdtemp(join(tmpdir(), "legion-daemon-test-"))
const paths = createPaths(root)
let publishActivity: (payload: unknown) => void = () => undefined
const server = await startLegionDaemonServer(paths, {
cli: {
async materialize() {
publishActivity({
nodeStreams: {
"node-a": [
{
sequence: 1,
occurredAt: "2026-06-08T00:00:00.000Z",
nodeId: "node-a",
stream: "stdout",
text: "install stdout\n"
},
{
sequence: 2,
occurredAt: "2026-06-08T00:00:01.000Z",
nodeId: "node-a",
stream: "stderr",
text: "install stderr\n"
}
]
}
})
await new Promise((resolve) => setTimeout(resolve, 10))
return { activity: null }
}
} as never
})
publishActivity = (payload) => server.publishEvent("activity:update", payload)
try {
let stdout = ""
let stderr = ""
const exitCode = await runDaemonApplyCommand(
paths,
{
stdout: { write: (chunk: string) => ((stdout += chunk), true) },
stderr: { write: (chunk: string) => ((stderr += chunk), true) }
},
{ token: server.token, trace: true }
)
assert.equal(exitCode, 0)
assert.match(stdout, /install stdout/)
assert.match(stdout, /materialized:/)
assert.match(stderr, /install stderr/)
} finally {
await server.close()
await rm(root, { recursive: true, force: true })
}
})
test("daemon server keeps structured CLI calls open until completion", async () => {
const root = await mkdtemp(join(tmpdir(), "legion-daemon-test-"))
const paths = createPaths(root)
const server = await startLegionDaemonServer(paths, {
cli: {
async materialize() {
await new Promise((resolve) => setTimeout(resolve, 50))
return { activity: null }
}
} as never
})
try {
const client = await createDaemonLegionCliClient(paths, { token: server.token })
assert.ok(client)
assert.deepEqual(await client.materialize(), { activity: null })
} finally {
await server.close()
await rm(root, { recursive: true, force: true })
}
})
test("daemon server runs structured engine calls and publishes events", async () => {
const root = await mkdtemp(join(tmpdir(), "legion-daemon-test-"))
const paths = createPaths(root)
const server = await startLegionDaemonServer(paths, {
engine: {
async getBootstrapState() {
return {
snapshot: null,
activity: null,
auth: { locked: true, availableMethods: ["password"] },
daemon: null
}
}
} as never
})
const events: Array<{ channel: string; payload: unknown }> = []
try {
const client = await createDaemonLegionApiClient(paths, {
token: server.token,
onEvent: (channel, payload) => events.push({ channel, payload })
})
assert.ok(client)
assert.deepEqual(await client.getBootstrapState(), {
snapshot: null,
activity: null,
auth: { locked: true, availableMethods: ["password"] },
daemon: null
})
server.publishEvent("activity:update", { title: "Activity" })
await new Promise((resolve) => setTimeout(resolve, 10))
assert.deepEqual(events, [{ channel: "activity:update", payload: { title: "Activity" } }])
} finally {
await server.close()
await rm(root, { recursive: true, force: true })
}
})
test("daemon client preserves structured app errors", async () => {
const root = await mkdtemp(join(tmpdir(), "legion-daemon-test-"))
const paths = createPaths(root)
const server = await startLegionDaemonServer(paths, {
engine: {
async performServerAction() {
throw new LegionEngineError(
"OPERATION_BUSY",
"Wait for the current operation to finish.",
"validation",
true,
{
workflowState: "apply",
requestedState: "performServerAction"
}
)
}
} as never
})
try {
const client = await createDaemonLegionApiClient(paths, { token: server.token })
assert.ok(client)
await assert.rejects(
() => client.performServerAction({ serverId: "server-a", action: "reboot" }),
(error) => {
assert.equal((error as { code?: string }).code, "OPERATION_BUSY")
assert.equal((error as { category?: string }).category, "validation")
assert.equal((error as { retryable?: boolean }).retryable, true)
assert.deepEqual((error as { details?: unknown }).details, {
workflowState: "apply",
requestedState: "performServerAction"
})
assert.equal((error as Error).message, "Wait for the current operation to finish.")
return true
}
)
} finally {
await server.close()
await rm(root, { recursive: true, force: true })
}
})
test("daemon event subscription reconnects after daemon restart", async () => {
const root = await mkdtemp(join(tmpdir(), "legion-daemon-test-"))
const paths = createPaths(root)
let server = await startLegionDaemonServer(paths, {
engine: {
async getBootstrapState() {
return {
snapshot: null,
activity: null,
auth: { locked: true, availableMethods: ["password"] },
daemon: null
}
}
} as never
})
const token = server.token
const events: Array<{ channel: string; payload: unknown }> = []
try {
const client = await createDaemonLegionApiClient(paths, {
token,
onEvent: (channel, payload) => events.push({ channel, payload })
})
assert.ok(client)
await server.close()
server = await startLegionDaemonServer(
paths,
{
engine: {
async getBootstrapState() {
return {
snapshot: null,
activity: null,
auth: { locked: true, availableMethods: ["password"] },
daemon: null
}
}
} as never
},
{ token }
)
await new Promise((resolve) => setTimeout(resolve, 750))
server.publishEvent("activity:update", { title: "After restart" })
await new Promise((resolve) => setTimeout(resolve, 20))
assert.deepEqual(events, [{ channel: "activity:update", payload: { title: "After restart" } }])
} finally {
await server.close().catch(() => undefined)
await rm(root, { recursive: true, force: true })
}
})
test("daemon buffers terminal events while a subscriber is backpressured", async () => {
const root = await mkdtemp(join(tmpdir(), "legion-daemon-test-"))
const paths = createPaths(root)
const server = await startLegionDaemonServer(paths, {
engine: {
async getBootstrapState() {
return {
snapshot: null,
activity: null,
auth: { locked: true, availableMethods: ["password"] },
daemon: null
}
}
} as never
})
let socket: Socket | null = null
const terminalEvents: unknown[] = []
try {
const subscription = await openRawDaemonSubscription(server.endpoint, server.token, (event) => {
if (event.channel === "terminal:data") {
terminalEvents.push(event.payload)
}
})
socket = subscription.socket
socket.pause()
const payloads = [0, 1, 2].map((sequence) => ({
sessionId: "session-a",
chunk: `${sequence}:${"x".repeat(512 * 1024)}`,
sequence
}))
for (const payload of payloads) {
server.publishEvent("terminal:data", payload)
}
await new Promise((resolve) => setTimeout(resolve, 20))
socket.resume()
await waitForCondition(async () => terminalEvents.length === payloads.length)
assert.deepEqual(
terminalEvents.map((event) => (event as { sequence: number }).sequence),
[0, 1, 2]
)
} finally {
socket?.destroy()
await server.close()
await rm(root, { recursive: true, force: true })
}
})
test("daemon token export renders shell syntax for each platform", () => {
assert.equal(
renderDaemonTokenExport("abc'123", "darwin"),
"export LEGION_DAEMON_TOKEN='abc'\\''123'"
)
assert.equal(renderDaemonTokenExport("abc'123", "win32"), "$env:LEGION_DAEMON_TOKEN = 'abc''123'")
})
test("reachable daemon metadata detects an already running daemon without the token", async () => {
const root = await mkdtemp(join(tmpdir(), "legion-daemon-test-"))
const paths = createPaths(root)
const server = await startLegionDaemonServer(paths, { cli: {} as never })
try {
const metadata = await getReachableDaemonMetadata(paths)
assert.ok(metadata)
assert.equal(metadata.endpoint, server.endpoint)
} finally {
await server.close()
await rm(root, { recursive: true, force: true })
}
})
test("daemon start refuses to spawn a second daemon for a running state directory", async () => {
const root = await mkdtemp(join(tmpdir(), "legion-daemon-test-"))
const paths = createPaths(root)
const server = await startLegionDaemonServer(paths, { cli: {} as never })
try {
const result = await execLegionCli(["daemon", "start"], {
LEGION_STATE_DIR: root,
LEGION_CACHE_DIR: root,
LEGION_APP_ROOT: process.cwd()
})
assert.equal(result.code, 1)
assert.equal(result.stdout, "")
assert.match(result.stderr, /daemon: already running/)
assert.match(result.stderr, /Refusing to start a second daemon/)
} finally {
await server.close()
await rm(root, { recursive: true, force: true })
}
})
test(
"daemon start launches a reachable detached daemon on Windows",
{ skip: process.platform !== "win32" },
async () => {
const root = await mkdtemp(join(tmpdir(), "legion-daemon-test-"))
const token = `detached-${randomUUID()}`
const env = {
LEGION_STATE_DIR: root,
LEGION_CACHE_DIR: root,
LEGION_APP_ROOT: process.cwd()
}
try {
const start = await execLegionCli(["--daemon-token", token, "daemon", "start"], env)
assert.equal(start.code, 0)
assert.match(start.stdout, /daemon: running/)
assert.match(start.stdout, /pid: \d+/)
assert.match(start.stdout, /log: /)
assert.equal(start.stderr, "")
const status = await execLegionCli(["--daemon-token", token, "daemon", "status"], env)
assert.equal(status.code, 0)
assert.match(status.stdout, /daemon: running/)
assert.equal(status.stderr, "")
} finally {
await execLegionCli(["--daemon-token", token, "daemon", "stop", "--force"], env).catch(
() => undefined
)
await rm(root, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 })
}
}
)
test("daemon-required CLI commands report a reachable daemon without an exported token", async () => {
const root = await mkdtemp(join(tmpdir(), "legion-daemon-test-"))
const paths = createPaths(root)
const server = await startLegionDaemonServer(paths, { cli: {} as never })
try {
const result = await execLegionCli(["node", "start", "node-a"], {
LEGION_STATE_DIR: root,
LEGION_CACHE_DIR: root,
LEGION_APP_ROOT: process.cwd(),
LEGION_DAEMON_TOKEN: ""
})
assert.equal(result.code, 1)
assert.equal(result.stdout, "")
assert.match(result.stderr, /LEGION_DAEMON_TOKEN is not set/)
assert.match(result.stderr, /close the GUI\/current daemon/)
} finally {
await server.close()
await rm(root, { recursive: true, force: true })
}
})
test("daemon client requires an explicit token when metadata exists", async () => {
const root = await mkdtemp(join(tmpdir(), "legion-daemon-test-"))
const paths = createPaths(root)
const server = await startLegionDaemonServer(paths, { cli: {} as never })
try {
await assert.rejects(() => createDaemonLegionCliClient(paths), /LEGION_DAEMON_TOKEN/)
} finally {
await server.close()
await rm(root, { recursive: true, force: true })
}
})
test("daemon status and stop commands report the running server", async () => {
const root = await mkdtemp(join(tmpdir(), "legion-daemon-test-"))
const paths = createPaths(root)
const server = await startLegionDaemonServer(paths, { cli: {} as never })
let stdout = ""
let stderr = ""
try {
const statusCode = await getDaemonStatus(
paths,
{
stdout: { write: (chunk: string) => ((stdout += chunk), true) },
stderr: { write: (chunk: string) => ((stderr += chunk), true) }
},
{
token: server.token
}
)
assert.equal(statusCode, 0)
assert.match(stdout, /daemon: running/)
assert.equal(stderr, "")
stdout = ""
const stopCode = await stopDaemon(
paths,
{
stdout: { write: (chunk: string) => ((stdout += chunk), true) },
stderr: { write: (chunk: string) => ((stderr += chunk), true) }
},
{
token: server.token
}
)
assert.equal(stopCode, 0)
assert.match(stdout, /daemon: stopping/)
} finally {
await server.close().catch(() => undefined)
await rm(root, { recursive: true, force: true })
}
})
test("daemon stop requires force when engine operations are active", async () => {
const root = await mkdtemp(join(tmpdir(), "legion-daemon-test-"))
const paths = createPaths(root)
const server = await startLegionDaemonServer(paths, {
engine: {
getActiveOperations() {
return [
{
id: "stream-session-a",
kind: "sender-stream",
label: "Sender stream stream-session-a",
detail: "running",
startedAt: "2026-06-17T10:00:00.000Z"
}
]
}
} as never
})
let stdout = ""
let stderr = ""
try {
const blockedCode = await stopDaemon(
paths,
{
stdout: { write: (chunk: string) => ((stdout += chunk), true) },
stderr: { write: (chunk: string) => ((stderr += chunk), true) }
},
{
token: server.token
}
)
assert.equal(blockedCode, 1)
assert.equal(stdout, "")
assert.match(stderr, /The Legion daemon has active operations:/)
assert.match(stderr, /Sender stream stream-session-a \(running\)/)
assert.match(stderr, /daemon stop --force/)
stdout = ""
stderr = ""
const forcedCode = await stopDaemon(
paths,
{
stdout: { write: (chunk: string) => ((stdout += chunk), true) },
stderr: { write: (chunk: string) => ((stderr += chunk), true) }
},
{
token: server.token,
force: true
}
)
assert.equal(forcedCode, 0)
assert.match(stdout, /daemon: stopping/)
assert.equal(stderr, "")
} finally {
await server.close().catch(() => undefined)
await rm(root, { recursive: true, force: true })
}
})
test("daemon stop requires force for a GUI-owned daemon", async () => {
const root = await mkdtemp(join(tmpdir(), "legion-daemon-test-"))
const paths = createPaths(root)
const events: { channel: string; payload: unknown }[] = []
const server = await startLegionDaemonServer(
paths,
{
cli: {} as never
},
{
owner: "gui"
}
)
const subscription = await openRawDaemonSubscription(server.endpoint, server.token, (event) => {
events.push(event)
})
let stdout = ""
let stderr = ""
try {
const blockedCode = await stopDaemon(
paths,
{
stdout: { write: (chunk: string) => ((stdout += chunk), true) },
stderr: { write: (chunk: string) => ((stderr += chunk), true) }
},
{
token: server.token
}
)
assert.equal(blockedCode, 1)
assert.equal(stdout, "")
assert.match(stderr, /belongs to a running GUI session/)
assert.match(stderr, /daemon stop --force/)
assert.equal((await getReachableDaemonMetadata(paths))?.pid, process.pid)
stdout = ""
stderr = ""
const forcedCode = await stopDaemon(
paths,
{
stdout: { write: (chunk: string) => ((stdout += chunk), true) },
stderr: { write: (chunk: string) => ((stderr += chunk), true) }
},
{
token: server.token,
force: true
}
)
assert.equal(forcedCode, 0)
assert.match(stdout, /daemon: stopping/)
assert.equal(stderr, "")
await waitForCondition(async () =>
events.some(
(event) =>
event.channel === "daemon:shutdown" &&
JSON.stringify(event.payload) ===
JSON.stringify({ owner: "gui", forced: true, reason: "requested" })
)
)
} finally {
subscription.socket.destroy()
await server.close().catch(() => undefined)
await rm(root, { recursive: true, force: true })
}
})
async function waitForCondition(predicate: () => Promise<boolean>): Promise<void> {
const deadline = Date.now() + 1_000
while (Date.now() < deadline) {
if (await predicate()) {
return
}
await new Promise((resolve) => setTimeout(resolve, 10))
}
assert.equal(await predicate(), true)
}
async function openRawDaemonSubscription(
endpoint: string,
token: string,
onEvent: (event: { channel: string; payload: unknown }) => void
): Promise<{ socket: Socket }> {
const socket = createConnection(endpoint)
let resolveSubscribed!: () => void
let rejectSubscribed!: (error: unknown) => void
const subscribed = new Promise<void>((resolve, reject) => {
resolveSubscribed = resolve
rejectSubscribed = reject
})
const parser = createDaemonLineParser((raw) => {
const response = raw as {
type: string
channel?: string
payload?: unknown
message?: string
}
if (response.type === "subscribed") {
resolveSubscribed()
return
}
if (response.type === "event" && response.channel) {
onEvent({ channel: response.channel, payload: response.payload })
return
}
if (response.type === "error") {
rejectSubscribed(new Error(response.message ?? "Daemon subscription failed."))
}
})
socket.on("connect", () => {
socket.write(
encodeDaemonMessage({
type: "subscribe",
id: randomUUID(),
token
})
)
})
socket.on("data", (chunk) => parser.push(chunk))
socket.on("error", rejectSubscribed)
await subscribed
return { socket }
}
function createPaths(root: string): LegionEnginePaths {
return {
stateFilePath: join(root, "legion-state.json"),
configFilePath: join(root, "legion-config.jsonc"),
providerCatalogCachePath: join(root, "provider-catalogs.json"),
embeddedCatalogPath: join(root, "embedded-provider-catalogs.json"),
logDirPath: join(root, "logs"),
daemonMetadataPath: join(root, "daemon.json")
}
}
async function execLegionCli(
args: string[],
env: Record<string, string>
): Promise<{ code: number; stdout: string; stderr: string }> {
return await new Promise((resolve, reject) => {
execFile(
process.execPath,
["--import", "tsx", join(process.cwd(), "src", "engine", "cli-main.ts"), ...args],
{
env: {
...process.env,
...env
}
},
(error, stdout, stderr) => {
if (error && typeof (error as { code?: unknown }).code !== "number") {
reject(error)
return
}
resolve({
code: error ? (error as { code: number }).code : 0,
stdout,
stderr
})
}
)
})
}