test: narrow M1 local contract follow-up
Host commander merge after read-only review. PR #160 is CLEAN/MERGEABLE, limited to M1 SOURCE/LOCAL contract smoke/docs, and does not reintroduce #157 Code Agent readiness/UI/health behavior. Runner validations reported: npm run m1:smoke, node scripts/code-agent-chat-smoke.mjs, node scripts/validate-contract.mjs, node --check scripts/m1-contract-smoke.mjs, and git diff --check.
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
#!/usr/bin/env node
|
||||
import assert from "node:assert/strict";
|
||||
import { spawn } from "node:child_process";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { mkdtemp, readFile, rm } from "node:fs/promises";
|
||||
import net from "node:net";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
@@ -63,6 +64,42 @@ function startNode(relativeScript, env = {}) {
|
||||
return record;
|
||||
}
|
||||
|
||||
function runNodeJson(relativeScript, args = [], env = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = spawn(process.execPath, [relativeScript, ...args], {
|
||||
cwd: repoRoot,
|
||||
env: {
|
||||
...process.env,
|
||||
...env
|
||||
},
|
||||
stdio: ["ignore", "pipe", "pipe"]
|
||||
});
|
||||
let stdout = "";
|
||||
let stderr = "";
|
||||
|
||||
child.stdout.setEncoding("utf8");
|
||||
child.stderr.setEncoding("utf8");
|
||||
child.stdout.on("data", (chunk) => {
|
||||
stdout += chunk;
|
||||
});
|
||||
child.stderr.on("data", (chunk) => {
|
||||
stderr += chunk;
|
||||
});
|
||||
child.on("error", reject);
|
||||
child.on("close", (code) => {
|
||||
if (code !== 0) {
|
||||
reject(new Error(`${relativeScript} ${args.join(" ")} exited with ${code}\nstdout:\n${stdout}\nstderr:\n${stderr}`));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
resolve(JSON.parse(stdout));
|
||||
} catch (error) {
|
||||
reject(new Error(`${relativeScript} ${args.join(" ")} did not return JSON: ${error.message}\nstdout:\n${stdout}\nstderr:\n${stderr}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function stopNode(record) {
|
||||
const { child } = record;
|
||||
if (child.exitCode !== null || child.signalCode !== null) {
|
||||
@@ -354,7 +391,8 @@ async function smokePatchPanelAndSimulators() {
|
||||
HWLAB_BOX_ID: "boxsim_alpha"
|
||||
});
|
||||
const patchPanel = startNode("cmd/hwlab-patch-panel/main.mjs", {
|
||||
PORT: String(patchPort)
|
||||
PORT: String(patchPort),
|
||||
HWLAB_WIRING_CONFIG_PATH: path.join(repoRoot, "fixtures/mvp-topology/topology.json")
|
||||
});
|
||||
|
||||
const gatewayBase = `http://127.0.0.1:${gatewayPort}`;
|
||||
@@ -467,9 +505,122 @@ async function smokeCliDryRunFixture() {
|
||||
logOk("CLI dry-run fixture parse");
|
||||
}
|
||||
|
||||
async function smokeAgentMgrLocalContract() {
|
||||
const stateDir = await mkdtemp(path.join(os.tmpdir(), "hwlab-m1-agent-mgr-"));
|
||||
const agentSessionId = "agt_m1_local_contract";
|
||||
const agentMgrPort = await freePort();
|
||||
let agentMgr;
|
||||
|
||||
try {
|
||||
agentMgr = startNode("cmd/hwlab-agent-mgr/main.mjs", {
|
||||
PORT: String(agentMgrPort),
|
||||
HWLAB_AGENT_RUNTIME_STATE_DIR: stateDir,
|
||||
HWLAB_ENVIRONMENT: ENVIRONMENT_DEV
|
||||
});
|
||||
const baseUrl = `http://127.0.0.1:${agentMgrPort}`;
|
||||
await waitForHttp(`${baseUrl}/health/live`, agentMgr);
|
||||
|
||||
const healthLive = await requestJson(`${baseUrl}/health/live`);
|
||||
assert.equal(healthLive.response.status, 200);
|
||||
assert.equal(healthLive.body.serviceId, "hwlab-agent-mgr");
|
||||
assert.equal(healthLive.body.ok, true);
|
||||
assert.equal(healthLive.body.environment, ENVIRONMENT_DEV);
|
||||
assert.equal(healthLive.body.health.serviceId, "hwlab-agent-mgr");
|
||||
assert.equal(healthLive.body.health.status, "degraded");
|
||||
assert.equal(healthLive.body.health.runtimeMode, "local-dry-run");
|
||||
assert.ok(healthLive.body.health.skills.missing.includes("manager.skillCommitId"));
|
||||
assert.ok(healthLive.body.health.skills.missing.includes("manager.skillVersion"));
|
||||
assert.equal(healthLive.body.skillsManifest, null);
|
||||
|
||||
const health = await runNodeJson("cmd/hwlab-agent-mgr/main.mjs", [
|
||||
"health",
|
||||
"--state-dir",
|
||||
stateDir,
|
||||
"--skill-commit-id",
|
||||
"local-contract",
|
||||
"--skill-version",
|
||||
"m1-local"
|
||||
]);
|
||||
assert.equal(health.serviceId, "hwlab-agent-mgr");
|
||||
assert.equal(health.ok, true);
|
||||
assert.equal(health.health.serviceId, "hwlab-agent-mgr");
|
||||
assert.equal(health.health.status, "ok");
|
||||
assert.equal(health.health.environment, ENVIRONMENT_DEV);
|
||||
assert.equal(health.health.runtimeMode, "local-dry-run");
|
||||
assert.equal(health.health.workspaceCleaned, false);
|
||||
assert.equal(health.health.skills.manager.status, "ready");
|
||||
assert.equal(health.skillsManifest.services[0].serviceId, "hwlab-agent-skills");
|
||||
assert.equal(health.skillsManifest.environment, ENVIRONMENT_DEV);
|
||||
|
||||
const created = await runNodeJson("cmd/hwlab-agent-mgr/main.mjs", [
|
||||
"create",
|
||||
"--state-dir",
|
||||
stateDir,
|
||||
"--agent-session-id",
|
||||
agentSessionId,
|
||||
"--project-id",
|
||||
"prj_m1_local_contract",
|
||||
"--goal",
|
||||
"verify M1 local agent manager contract",
|
||||
"--prompt",
|
||||
"local contract sample",
|
||||
"--skill-commit-id",
|
||||
"local-contract",
|
||||
"--skill-version",
|
||||
"m1-local"
|
||||
]);
|
||||
assert.equal(created.ok, true);
|
||||
assert.equal(created.serviceId, "hwlab-agent-mgr");
|
||||
assert.equal(created.agentSessionId, agentSessionId);
|
||||
assert.equal(created.status, "ok");
|
||||
assert.equal(created.environment, ENVIRONMENT_DEV);
|
||||
assert.equal(created.runtimeMode, "local-dry-run");
|
||||
assert.equal(created.agentSession.serviceId, "hwlab-agent-mgr");
|
||||
assert.equal(created.agentSession.projectId, "prj_m1_local_contract");
|
||||
assert.equal(created.task.status, "queued");
|
||||
assert.equal(created.traceCount, 1);
|
||||
assert.equal(created.evidenceCount, 0);
|
||||
|
||||
const status = await runNodeJson("cmd/hwlab-agent-mgr/main.mjs", [
|
||||
"status",
|
||||
"--state-dir",
|
||||
stateDir,
|
||||
"--agent-session-id",
|
||||
agentSessionId
|
||||
]);
|
||||
assert.equal(status.ok, true);
|
||||
assert.equal(status.serviceId, "hwlab-agent-mgr");
|
||||
assert.equal(status.agentSessionId, agentSessionId);
|
||||
assert.equal(status.status, "ok");
|
||||
assert.equal(status.task.taskId, `tsk_${agentSessionId}`);
|
||||
|
||||
const cleanup = await runNodeJson("cmd/hwlab-agent-mgr/main.mjs", [
|
||||
"cleanup",
|
||||
"--state-dir",
|
||||
stateDir,
|
||||
"--agent-session-id",
|
||||
agentSessionId
|
||||
]);
|
||||
assert.equal(cleanup.ok, true);
|
||||
assert.equal(cleanup.serviceId, "hwlab-agent-mgr");
|
||||
assert.equal(cleanup.agentSessionId, agentSessionId);
|
||||
assert.equal(cleanup.status, "ok");
|
||||
assert.equal(cleanup.cleanup.completed, true);
|
||||
assert.equal(cleanup.workspace.existsAfterCleanup, false);
|
||||
|
||||
logOk("agent-mgr health/sample local contract");
|
||||
} finally {
|
||||
if (agentMgr) {
|
||||
await stopNode(agentMgr);
|
||||
}
|
||||
await rm(stateDir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await smokeCloudApi();
|
||||
await smokePatchPanelAndSimulators();
|
||||
await smokeAgentMgrLocalContract();
|
||||
await smokeCliDryRunFixture();
|
||||
process.stdout.write("[m1-smoke] passed\n");
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user