@kontextmind/kxm
Version:
KXM local-first multi-agent orchestration and operator dashboard
484 lines (455 loc) • 21.2 kB
JavaScript
import { spawn, spawnSync } from "node:child_process";
import { createHash, createHmac, randomBytes, randomUUID } from "node:crypto";
import { chmodSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { createServer } from "node:net";
import { tmpdir } from "node:os";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const hubScript = join(root, "scripts", "kxm-hub.mjs");
const workerScript = join(root, "scripts", "kxm-worker.mjs");
const extensionPath = join(root, "plugins", "kxm", "src", "extension.ts");
const DEFAULT_TIMEOUT_MS = 120_000;
const MAX_CAPTURE_CHARS = 32_768;
export function parseSmokeModels(value) {
const models = [...new Set(String(value ?? "").split(",").map((item) => item.trim()).filter(Boolean))];
if (models.some((model) => !/^[A-Za-z0-9._:/-]{1,160}$/.test(model))) {
throw new Error("KXM_SMOKE_MODELS contains an invalid model ID");
}
return models;
}
export function boundedSafeMessage(value, secrets = []) {
let result = value instanceof Error ? value.message : String(value);
for (const secret of secrets) {
if (secret) result = result.replaceAll(secret, "[REDACTED]");
}
result = result.replace(/(?:sk|key|token|secret)[-_A-Za-z0-9]{12,}/gi, "[REDACTED]");
return result.slice(0, 500);
}
export async function runSmokePhases(phases, cleanup) {
const completed = [];
try {
const values = {};
for (const [name, action] of phases) {
values[name] = await action(values);
completed.push(name);
}
return { values, completed };
} finally {
await cleanup();
}
}
function writeOutcome(outcome) {
process.stdout.write(`${JSON.stringify(outcome)}\n`);
}
function runCommand(command, args, options = {}) {
return spawnSync(command, args, {
encoding: "utf8",
windowsHide: true,
shell: process.platform === "win32" && /\.(?:cmd|bat)$/i.test(command),
timeout: options.timeout ?? 15_000,
env: options.env ?? process.env,
stdio: ["ignore", "pipe", "pipe"],
});
}
function resolvePiCommand() {
const configured = process.env.KXM_SMOKE_PI_COMMAND?.trim();
if (configured) return configured;
const locator = runCommand(process.platform === "win32" ? "where.exe" : "which", ["pi"]);
if (locator.status !== 0) return undefined;
const candidates = String(locator.stdout).split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
return process.platform === "win32"
? candidates.find((candidate) => /\.cmd$/i.test(candidate)) ?? candidates[0]
: candidates[0];
}
function modelAuthReady(piCommand, model) {
const checked = runCommand(piCommand, ["auth", "check", "--model", model, "--json", "--no-refresh"]);
if (checked.status !== 0) return false;
try {
return JSON.parse(String(checked.stdout)).status === "ready";
} catch {
return false;
}
}
async function freePort() {
const server = createServer();
await new Promise((accept, reject) => {
server.once("error", reject);
server.listen(0, "127.0.0.1", accept);
});
const address = server.address();
if (!address || typeof address === "string") throw new Error("failed to allocate a local smoke port");
await new Promise((accept, reject) => server.close((error) => error ? reject(error) : accept()));
return address.port;
}
function captureProcess(child, label) {
const state = { label, stdout: "", stderr: "", exit: undefined };
child.stdout?.on("data", (chunk) => {
state.stdout = `${state.stdout}${chunk}`.slice(-MAX_CAPTURE_CHARS);
});
child.stderr?.on("data", (chunk) => {
state.stderr = `${state.stderr}${chunk}`.slice(-MAX_CAPTURE_CHARS);
});
child.once("exit", (code, signal) => {
state.exit = { code, signal };
});
return state;
}
function spawnNode(script, env, label) {
const child = spawn(process.execPath, [script], {
cwd: root,
env,
stdio: ["ignore", "pipe", "pipe"],
windowsHide: true,
});
return { child, capture: captureProcess(child, label) };
}
async function waitFor(check, timeoutMs, label, intervalMs = 250) {
const deadline = Date.now() + timeoutMs;
let lastError;
while (Date.now() < deadline) {
try {
const value = await check();
if (value) return value;
} catch (error) {
lastError = error;
}
await new Promise((accept) => setTimeout(accept, intervalMs));
}
throw new Error(`${label} timed out${lastError ? `: ${boundedSafeMessage(lastError)}` : ""}`);
}
async function api(baseUrl, path, options = {}) {
const response = await fetch(`${baseUrl}${path}`, {
method: options.method ?? "GET",
headers: {
"content-type": "application/json",
authorization: `Bearer ${options.authToken}`,
...(options.identity ? {
"x-kxm-agent-id": options.identity.agent.id,
"x-kxm-agent-key": options.identity.agentKey,
} : {}),
...(options.headers ?? {}),
},
...(options.body === undefined ? {} : { body: typeof options.body === "string" ? options.body : JSON.stringify(options.body) }),
signal: AbortSignal.timeout(options.timeoutMs ?? 10_000),
});
const text = await response.text();
let body = {};
if (text) {
try {
body = JSON.parse(text);
} catch {
throw new Error(`hub returned invalid JSON with HTTP ${response.status}`);
}
}
if (!response.ok) throw new Error(`hub HTTP ${response.status}: ${body.code ?? "request_failed"}`);
return body;
}
async function registerOperator(baseUrl, authToken, project) {
return await api(baseUrl, "/v1/agents/register", {
method: "POST",
authToken,
body: { name: "smoke-operator", purpose: "bounded smoke assertions", project },
});
}
async function agents(baseUrl, authToken, operator) {
const result = await api(baseUrl, "/v1/agents", { authToken, identity: operator });
return result.agents;
}
export function buildSmokeRequest(target, content, idempotencyKey) {
return { target, content, delivery: "followUp", idempotencyKey };
}
async function sendAndAwait(baseUrl, authToken, operator, target, content, timeoutMs, idempotencyKey) {
const sent = await api(baseUrl, "/v1/messages", {
method: "POST",
authToken,
identity: operator,
body: buildSmokeRequest(target, content, idempotencyKey),
});
return await waitFor(async () => {
const result = await api(baseUrl, `/v1/messages/${encodeURIComponent(sent.message.id)}`, {
authToken,
identity: operator,
});
if (["cancelled", "expired", "error"].includes(result.message.status)) {
throw new Error(`peer request ended as ${result.message.status}`);
}
return result.message.status === "replied" && typeof result.message.reply?.content === "string"
&& result.message.reply.content.trim() ? result.message : undefined;
}, timeoutMs, `reply from ${target}`, 500);
}
function createPiShim(tempRoot, piCommand) {
if (process.platform === "win32") {
const commandShim = join(tempRoot, "pi-smoke-shim.cmd");
const quoteBatch = (value) => `"${String(value).replaceAll('"', '""')}"`;
writeFileSync(commandShim, [
"@echo off",
`call ${quoteBatch(piCommand)} --extension ${quoteBatch(extensionPath)} --approve --no-context-files --no-skills --no-prompt-templates --no-themes %*`,
"exit /b %errorlevel%",
"",
].join("\r\n"), "utf8");
return commandShim;
}
const shim = join(tempRoot, "pi-smoke-shim.mjs");
writeFileSync(shim, [
"#!/usr/bin/env node",
'import { spawn } from "node:child_process";',
`const command = ${JSON.stringify(piCommand)};`,
`const prefix = ${JSON.stringify(["--extension", extensionPath, "--approve", "--no-context-files", "--no-skills", "--no-prompt-templates", "--no-themes"])};`,
"const windowsScript = process.platform === 'win32' && /\\.(?:cmd|bat)$/i.test(command);",
"const quote = (value) => `\"${String(value).replaceAll('\\\"', '\\\"\\\"')}\"`;",
"const executable = windowsScript ? process.env.ComSpec || 'cmd.exe' : command;",
"const args = [...prefix, ...process.argv.slice(2)];",
"const launchArgs = windowsScript ? ['/d', '/s', '/v:off', '/c', `\"${[command, ...args].map(quote).join(' ')}\"`] : args;",
"const child = spawn(executable, launchArgs, { stdio: 'inherit', windowsHide: true });",
"child.once('error', (error) => { process.stderr.write(`${error.message}\\n`); process.exit(1); });",
"child.once('exit', (code, signal) => { if (signal) process.kill(process.pid, signal); else process.exit(code ?? 1); });",
"for (const signal of ['SIGINT', 'SIGTERM']) process.once(signal, () => child.kill(signal));",
].join("\n"), { encoding: "utf8", mode: 0o700 });
chmodSync(shim, 0o700);
return shim;
}
function readPidRecord(path) {
try {
const value = JSON.parse(readFileSync(path, "utf8"));
return Number.isInteger(value.pid) && typeof value.startedAt === "string" && typeof value.controlFile === "string"
? value : undefined;
} catch {
return undefined;
}
}
function requestGracefulStop(stateDir, role, name, project) {
const safeName = name?.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 32) || "agent";
const safeProject = project?.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 24) || "project";
const digest = name && project
? createHash("sha256").update(JSON.stringify({ project, agentName: name })).digest("hex").slice(0, 24)
: undefined;
const pidPath = join(stateDir, role === "hub" ? "hub.pid" : `worker-${safeProject}-${safeName}-${digest}.pid`);
const record = readPidRecord(pidPath);
if (!record) return false;
writeFileSync(join(stateDir, record.controlFile), `${JSON.stringify({ version: 1, startedAt: record.startedAt, ...(record.generation ? { generation: record.generation } : {}), requestedAt: new Date().toISOString() })}\n`, {
encoding: "utf8",
mode: 0o600,
});
return true;
}
async function waitForExit(processRecord, timeoutMs) {
if (processRecord.capture.exit) return;
await Promise.race([
new Promise((accept) => processRecord.child.once("exit", accept)),
new Promise((_, reject) => setTimeout(() => reject(new Error(`${processRecord.capture.label} did not stop gracefully`)), timeoutMs)),
]);
}
function forceTreeStop(child) {
if (!child || child.exitCode !== null) return;
if (process.platform === "win32" && child.pid) {
spawnSync("taskkill.exe", ["/pid", String(child.pid), "/t", "/f"], { windowsHide: true, stdio: "ignore" });
} else {
child.kill("SIGKILL");
}
}
async function stopRecord(processRecord, stateDir, role, name, project) {
if (!processRecord || processRecord.capture.exit) return;
requestGracefulStop(stateDir, role, name, project);
try {
await waitForExit(processRecord, 20_000);
} catch {
forceTreeStop(processRecord.child);
await waitForExit(processRecord, 5_000).catch(() => undefined);
}
}
export async function runRealSmoke(options = {}) {
const models = options.models ?? parseSmokeModels(process.env.KXM_SMOKE_MODELS);
if (models.length < 2) throw new Error("KXM_SMOKE_MODELS must name at least two distinct models");
const piCommand = options.piCommand ?? resolvePiCommand();
if (!piCommand) return { ok: true, skipped: true, reason: "real Pi binary unavailable" };
const probe = runCommand(piCommand, ["--version"]);
if (probe.status !== 0) return { ok: true, skipped: true, reason: "real Pi binary unavailable" };
const unavailableModels = models.filter((model) => !modelAuthReady(piCommand, model));
if (unavailableModels.length > 0) {
return { ok: true, skipped: true, reason: "model credentials unavailable", unavailableModels };
}
const timeoutMs = Number(process.env.KXM_SMOKE_TIMEOUT_MS ?? DEFAULT_TIMEOUT_MS);
if (!Number.isInteger(timeoutMs) || timeoutMs < 30_000 || timeoutMs > 600_000) {
throw new Error("KXM_SMOKE_TIMEOUT_MS must be an integer from 30000 to 600000");
}
const tempRoot = mkdtempSync(join(tmpdir(), "pi-mesh-real-smoke-"));
const workspaceDir = join(tempRoot, ".kxm");
const stateDir = join(workspaceDir, "state");
const logsDir = join(workspaceDir, "logs");
const assetsDir = join(workspaceDir, "assets");
for (const directory of [workspaceDir, stateDir, logsDir, assetsDir]) mkdirSync(directory, { recursive: true });
const port = await freePort();
const baseUrl = `http://127.0.0.1:${port}`;
const project = `smoke-${randomUUID()}`;
const authToken = randomBytes(32).toString("hex");
const webhookSecret = randomBytes(32).toString("hex");
const shim = createPiShim(tempRoot, piCommand);
const workerNames = ["smoke-alpha", "smoke-beta"];
const workflow = [{
id: "real-pi-smoke",
source: "generic",
project,
target: "smoke-operator",
secretEnv: "KXM_SMOKE_WEBHOOK_SECRET",
event: "smoke.requested",
delivery: "followUp",
promptTemplate: "Run isolated smoke {{runId}}",
stages: [{ id: "verify", label: "Verify", instructions: "Record the deterministic smoke result", requiredEvidence: ["real Pi evidence"], maxAttempts: 1 }],
}];
const commonEnv = {
...process.env,
KXM_HOST: "127.0.0.1",
KXM_PORT: String(port),
KXM_SERVER_URL: baseUrl,
KXM_AUTH_TOKEN: authToken,
KXM_PROJECT: project,
KXM_WORKDIR: tempRoot,
KXM_WORKSPACE_DIR: workspaceDir,
KXM_CONFIG_DIR: join(workspaceDir, "config"),
KXM_LOGS_DIR: logsDir,
KXM_ASSETS_DIR: assetsDir,
KXM_STATE_DIR: stateDir,
KXM_DATA_PATH: join(stateDir, "kxm.db"),
KXM_WEBHOOK_WORKFLOWS: JSON.stringify(workflow),
KXM_SMOKE_WEBHOOK_SECRET: webhookSecret,
};
const processes = { hub: undefined, workers: new Map() };
let stage = "start";
try {
const startHub = () => {
const record = spawnNode(hubScript, commonEnv, "hub");
processes.hub = record;
return record;
};
const startWorker = (index) => {
const name = workerNames[index];
const record = spawnNode(workerScript, {
...commonEnv,
KXM_AGENT_NAME: name,
KXM_AGENT_PURPOSE: "real Pi smoke peer",
KXM_WORKER_MODEL: models[index],
KXM_PI_COMMAND: shim,
KXM_WORKER_DRAIN_MS: "10000",
KXM_WORKER_MAX_RESTARTS: "2",
KXM_WORKER_SESSION_ISOLATION: "workflow",
}, name);
processes.workers.set(name, record);
return record;
};
await runSmokePhases([
["hub-ready", async () => {
stage = "hub-ready";
startHub();
await waitFor(async () => {
const response = await fetch(`${baseUrl}/health`, { signal: AbortSignal.timeout(1_000) });
return response.ok;
}, 20_000, "hub readiness");
return registerOperator(baseUrl, authToken, project);
}],
["discovery", async (values) => {
stage = "discovery";
const operator = values["hub-ready"];
startWorker(0);
startWorker(1);
const discovered = await waitFor(async () => {
const current = await agents(baseUrl, authToken, operator);
return workerNames.every((name) => current.some((agent) => agent.name === name)) ? current : undefined;
}, timeoutMs, "two real Pi workers");
return { operator, originalIds: Object.fromEntries(workerNames.map((name) => [name, discovered.find((agent) => agent.name === name).id])) };
}],
["request-reply", async (values) => {
stage = "request-reply";
const { operator } = values.discovery;
return sendAndAwait(baseUrl, authToken, operator, workerNames[0], "This is a transport smoke check. Reply with a short acknowledgement and do not call tools.", timeoutMs, `single-${randomUUID()}`);
}],
["fanout", async (values) => {
stage = "fanout";
const { operator } = values.discovery;
const replies = await Promise.all(workerNames.map((name) => sendAndAwait(baseUrl, authToken, operator, name, "Independently confirm the transport smoke check in one short sentence. Do not call tools.", timeoutMs, `fanout-${name}-${randomUUID()}`)));
if (replies.length !== 2) throw new Error("fanout did not return two replies");
return replies;
}],
["durable-restart-resume", async (values) => {
stage = "durable-restart-resume";
const { originalIds } = values.discovery;
await stopRecord(processes.workers.get(workerNames[0]), stateDir, "worker", workerNames[0], project);
await stopRecord(processes.hub, stateDir, "hub");
startHub();
await waitFor(async () => {
const response = await fetch(`${baseUrl}/health`, { signal: AbortSignal.timeout(1_000) });
return response.ok;
}, 20_000, "restarted hub readiness");
const resumedOperator = await registerOperator(baseUrl, authToken, project);
startWorker(0);
const resumed = await waitFor(async () => {
const current = await agents(baseUrl, authToken, resumedOperator);
return workerNames.every((name) => current.some((agent) => agent.name === name)) ? current : undefined;
}, timeoutMs, "worker resume");
for (const name of workerNames) {
if (resumed.find((agent) => agent.name === name).id !== originalIds[name]) throw new Error(`durable identity changed for ${name}`);
}
await sendAndAwait(baseUrl, authToken, resumedOperator, workerNames[0], "Confirm transport still works after the durable restart. Reply briefly and do not call tools.", timeoutMs, `post-restart-${randomUUID()}`);
return resumedOperator;
}],
["journal-checkpoint", async (values) => {
stage = "workflow-journal-checkpoint";
const resumedOperator = values["durable-restart-resume"];
const payload = JSON.stringify({ event: "smoke.requested" });
// KXM webhook contract (workflowWebhookSignedMaterial in plugins/kxm/src/workflow.ts).
const deliveryId = `smoke-${randomUUID()}`;
const timestamp = String(Math.floor(Date.now() / 1_000));
const signedMaterial = ["kxm-webhook-v1", "start", timestamp, deliveryId, "real-pi-smoke", "", ""].join("\n") + "\n" + payload;
const started = await api(baseUrl, "/v1/webhooks/real-pi-smoke", {
method: "POST",
authToken,
headers: { "x-kxm-delivery-id": deliveryId, "x-kxm-timestamp": timestamp, "x-kxm-signature": `sha256=${createHmac("sha256", webhookSecret).update(signedMaterial).digest("hex")}` },
body: payload,
});
await api(baseUrl, `/v1/workflows/${encodeURIComponent(started.run.id)}/journal`, {
method: "POST",
authToken,
identity: resumedOperator,
body: { category: "lesson", area: "harness", severity: "info", summary: "Real Pi smoke transport completed", evidence: ["discovery", "request-reply", "fanout", "post-restart-request-reply"] },
});
const checkpoint = await api(baseUrl, `/v1/workflows/${encodeURIComponent(started.run.id)}/checkpoints`, {
method: "POST",
authToken,
identity: resumedOperator,
body: { stageId: "verify", status: "passed", summary: "All real Pi smoke assertions passed", evidence: { "real Pi evidence": "discovery, request/reply, fanout, and post-restart request/reply passed" } },
});
if (!checkpoint.completed) throw new Error("workflow checkpoint did not complete");
const recorded = await api(baseUrl, `/v1/workflows/${encodeURIComponent(started.run.id)}`, { authToken, identity: resumedOperator });
if (recorded.run.status !== "completed"
|| recorded.run.stages[0]?.status !== "passed"
|| !recorded.journal.some((entry) => entry.category === "lesson" && entry.area === "harness")) {
throw new Error("workflow journal/checkpoint was not durable");
}
}],
], async () => {
for (const [name, record] of processes.workers) await stopRecord(record, stateDir, "worker", name, project);
await stopRecord(processes.hub, stateDir, "hub");
for (const record of processes.workers.values()) forceTreeStop(record.child);
forceTreeStop(processes.hub?.child);
rmSync(tempRoot, { recursive: true, force: true });
});
return { ok: true, skipped: false, realPiWorkers: 2, assertions: ["discovery", "request-reply", "fanout", "durable-restart-resume", "post-restart-request-reply", "journal", "checkpoint"] };
} catch (error) {
throw new Error(`${stage}: ${boundedSafeMessage(error, [authToken, webhookSecret])}`);
}
}
async function main() {
const enabled = process.env.KXM_SMOKE === "1" || process.argv.includes("--real-pi");
if (!enabled) {
writeOutcome({ ok: true, skipped: true, reason: "KXM_SMOKE is not 1" });
return;
}
try {
writeOutcome(await runRealSmoke());
} catch (error) {
writeOutcome({ ok: false, skipped: false, error: boundedSafeMessage(error) });
process.exitCode = 1;
}
}
if (process.argv[1] && pathToFileURL(resolve(process.argv[1])).href === import.meta.url) await main();