From 73b379f4441d47a99dfd978a80fd43727410af51 Mon Sep 17 00:00:00 2001 From: lyon Date: Fri, 22 May 2026 11:28:43 +0800 Subject: [PATCH] fix: run agent manager as health service --- cmd/hwlab-agent-mgr/main.mjs | 99 +++++++++++++++++++++++++++++------- 1 file changed, 81 insertions(+), 18 deletions(-) diff --git a/cmd/hwlab-agent-mgr/main.mjs b/cmd/hwlab-agent-mgr/main.mjs index 4ac2c253..40c19963 100644 --- a/cmd/hwlab-agent-mgr/main.mjs +++ b/cmd/hwlab-agent-mgr/main.mjs @@ -1,4 +1,6 @@ #!/usr/bin/env node +import { createServer } from "node:http"; + import { AGENT_MGR_SERVICE_ID, createSkillsManifest @@ -14,7 +16,7 @@ import { resolveAgentRuntimeStateDir } from "../../internal/agent/runtime.mjs"; -const command = process.argv[2] || "help"; +const command = process.argv[2] || process.env.HWLAB_AGENT_MGR_DEFAULT_COMMAND || "server"; const options = parseOptions(process.argv.slice(3)); try { @@ -40,23 +42,11 @@ async function dispatch(name, opts) { } if (name === "health") { - const managerSkills = assessSkillsInjection({ - skillCommitId: opts.skillCommitId, - skillVersion: opts.skillVersion, - role: "manager" - }); - const health = createRuntimeHealth({ - stateDir: opts.stateDir, - managerSkills - }); - return { - serviceId: AGENT_MGR_SERVICE_ID, - ok: true, - health, - skillsManifest: managerSkills.status === "ready" - ? createSkillsManifest({ commitId: managerSkills.commitId, version: managerSkills.version }) - : null - }; + return createHealthResponse(opts); + } + + if (name === "server") { + return serveHealth(opts); } if (name === "create") { @@ -152,11 +142,84 @@ function requireOption(opts, key, flag) { return opts[key]; } +function createHealthResponse(opts) { + const managerSkills = assessSkillsInjection({ + skillCommitId: opts.skillCommitId, + skillVersion: opts.skillVersion, + role: "manager" + }); + const health = createRuntimeHealth({ + stateDir: opts.stateDir, + managerSkills + }); + return { + serviceId: AGENT_MGR_SERVICE_ID, + ok: true, + health, + skillsManifest: managerSkills.status === "ready" + ? createSkillsManifest({ commitId: managerSkills.commitId, version: managerSkills.version }) + : null + }; +} + +async function serveHealth(opts) { + const port = Number.parseInt(process.env.PORT || process.env.HWLAB_PORT || "7410", 10); + if (!Number.isInteger(port) || port < 1 || port > 65535) { + throw new Error(`invalid PORT/HWLAB_PORT ${JSON.stringify(process.env.PORT || process.env.HWLAB_PORT)}`); + } + + const server = createServer((request, response) => { + const url = new URL(request.url || "/", "http://hwlab-agent-mgr.local"); + if (url.pathname === "/health" || url.pathname === "/health/live") { + sendJson(response, 200, { + ...createHealthResponse(opts), + environment: process.env.HWLAB_ENVIRONMENT || "dev", + commit: { + id: process.env.HWLAB_COMMIT_ID || "unknown", + source: "runtime-env" + }, + image: { + reference: process.env.HWLAB_IMAGE || "unknown", + tag: process.env.HWLAB_IMAGE_TAG || "unknown", + digest: process.env.HWLAB_IMAGE_DIGEST || "unknown" + } + }); + return; + } + if (url.pathname === "/" || url.pathname === "/help") { + sendJson(response, 200, help()); + return; + } + sendJson(response, 404, { serviceId: AGENT_MGR_SERVICE_ID, ok: false, error: "not_found", path: url.pathname }); + }); + + await new Promise((resolve, reject) => { + server.once("error", reject); + server.listen(port, "0.0.0.0", resolve); + }); + printJSON({ serviceId: AGENT_MGR_SERVICE_ID, ok: true, status: "listening", port }); + + return new Promise((resolve, reject) => { + server.once("close", resolve); + server.once("error", reject); + }); +} + +function sendJson(response, statusCode, body) { + const payload = JSON.stringify(body, null, 2); + response.writeHead(statusCode, { + "content-type": "application/json; charset=utf-8", + "content-length": Buffer.byteLength(payload) + }); + response.end(payload); +} + function help() { return { serviceId: AGENT_MGR_SERVICE_ID, ok: true, usage: [ + "node cmd/hwlab-agent-mgr/main.mjs server [--skill-commit-id COMMIT --skill-version VERSION] [--state-dir DIR]", "node cmd/hwlab-agent-mgr/main.mjs health [--skill-commit-id COMMIT --skill-version VERSION] [--state-dir DIR]", "node cmd/hwlab-agent-mgr/main.mjs create --agent-session-id ID --project-id ID [--goal TEXT --prompt TEXT] [--skill-commit-id COMMIT --skill-version VERSION] [--state-dir DIR]", "node cmd/hwlab-agent-mgr/main.mjs status --agent-session-id ID [--state-dir DIR]",