feat: route code agent m3 io through hwlab api skill cli

This commit is contained in:
Code Queue Review
2026-05-23 18:38:47 +00:00
parent f19c3d1c85
commit fc7ff9b022
9 changed files with 791 additions and 52 deletions
+34 -4
View File
@@ -1,4 +1,5 @@
import { DEV_ENDPOINT, loadMvpGateSummary } from "../../../internal/mvp-gate/summary.mjs";
import { runM3IoSkillCommand } from "../../../skills/hwlab-agent-runtime/scripts/src/m3-io-skill-client.mjs";
function formatJson(value) {
return JSON.stringify(value, null, 2);
@@ -169,6 +170,9 @@ function writeHelp(stdout) {
commands: [
"health",
"project list",
"m3 status --api-base-url URL",
"m3 io --action do.write --value true --approved --api-base-url URL",
"m3 io --action di.read --api-base-url URL",
"test e2e --env dev --mvp",
"test e2e --env dev --mvp --dry-run",
"test e2e --env dev --mvp --live --confirm-dev --confirmed-non-production"
@@ -185,8 +189,25 @@ export async function runCli(argv, io) {
const stdout = io.stdout ?? process.stdout;
const stderr = io.stderr ?? process.stderr;
const repoRoot = io.cwd ?? process.cwd();
let summary;
const env = io.env ?? process.env;
const [command = "help", subcommand, ...rest] = argv;
const { flags, options } = parseArgs(rest);
if (command === "m3" && ["io", "status"].includes(subcommand)) {
const result = await runM3IoSkillCommand(["m3", subcommand, ...rest], {
env,
now: io.now,
requestJson: io.requestJson
});
writeJson(result.ok ? stdout : stderr, {
...result,
command: `hwlab-cli ${["m3", subcommand, ...redactM3CliArgs(rest)].join(" ")}`
});
return result.ok ? 0 : 2;
}
let summary;
try {
summary = loadMvpGateSummary(repoRoot);
} catch (error) {
@@ -194,9 +215,6 @@ export async function runCli(argv, io) {
return 1;
}
const [command = "help", subcommand, ...rest] = argv;
const { flags, options } = parseArgs(rest);
if (command === "health") {
writeJson(stdout, mvpGateOverview(summary));
return 0;
@@ -264,6 +282,18 @@ export async function runCli(argv, io) {
return 0;
}
function redactM3CliArgs(args = []) {
const redacted = [];
for (let index = 0; index < args.length; index += 1) {
redacted.push(args[index]);
if (args[index] === "--api-base-url" && index + 1 < args.length) {
redacted.push("<hwlab-api-base-url>");
index += 1;
}
}
return redacted;
}
export function formatRuntime(runtime) {
return formatJson(runtime);
}
+107
View File
@@ -0,0 +1,107 @@
import assert from "node:assert/strict";
import test from "node:test";
import {
HWLAB_M3_IO_API_ROUTE,
HWLAB_M3_STATUS_API_ROUTE
} from "../../../skills/hwlab-agent-runtime/scripts/src/m3-io-skill-client.mjs";
import { runCli } from "./cli.mjs";
async function captureCli(args, options = {}) {
let stdout = "";
let stderr = "";
const exitCode = await runCli(args, {
cwd: options.cwd ?? process.cwd(),
env: options.env ?? {
PATH: process.env.PATH
},
requestJson: options.requestJson,
stdout: {
write(chunk) {
stdout += chunk;
}
},
stderr: {
write(chunk) {
stderr += chunk;
}
}
});
return { exitCode, stdout, stderr };
}
test("repo hwlab-cli m3 status uses Skill CLI and calls only HWLAB API /v1/m3/status", async () => {
const calls = [];
const result = await captureCli(
[
"m3",
"status",
"--api-base-url",
"http://hwlab-cloud-api.hwlab-dev.svc.cluster.local:6667",
"--trace-id",
"trc_hwlab_cli_status"
],
{
requestJson: async (url, request) => {
calls.push({ url, request });
assert.equal(url, `http://hwlab-cloud-api.hwlab-dev.svc.cluster.local:6667${HWLAB_M3_STATUS_API_ROUTE}`);
assert.equal(request.method, "GET");
return {
ok: true,
status: 200,
body: {
status: "live",
sourceKind: "DEV-LIVE",
traceId: "trc_hwlab_cli_status",
boxes: [{
id: "boxsimu_2",
resourceId: "res_boxsimu_2",
online: true,
ports: {
DI1: {
value: false
}
}
}]
}
};
}
}
);
assert.equal(result.exitCode, 0, result.stderr);
const body = JSON.parse(result.stdout);
assert.equal(body.route, HWLAB_M3_STATUS_API_ROUTE);
assert.equal(body.method, "GET");
assert.equal(body.readback.value, false);
assert.equal(body.command.includes("hwlab-cloud-api.hwlab-dev"), false);
assert.equal(calls.length, 1);
});
test("repo hwlab-cli m3 io blocks direct hardware targets before request", async () => {
const result = await captureCli(
[
"m3",
"io",
"--action",
"di.read",
"--api-base-url",
"http://hwlab-patch-panel.hwlab-dev.svc.cluster.local:7301",
"--trace-id",
"trc_hwlab_cli_direct_blocked"
],
{
requestJson: async () => {
throw new Error("direct hardware target must not be called");
}
}
);
assert.equal(result.exitCode, 2);
const body = JSON.parse(result.stderr);
assert.equal(body.route, HWLAB_M3_IO_API_ROUTE);
assert.equal(body.blocker.code, "direct_hardware_target_blocked");
assert.equal(body.hwlabApi.redactedUrl, null);
assert.equal(JSON.stringify(body).includes("patch-panel.hwlab-dev"), false);
});