fix: make AgentRun CLI a render-only REST client (#263)

Co-authored-by: Codex <codex@noreply.local>
This commit is contained in:
Lyon
2026-06-11 15:41:11 +08:00
committed by GitHub
parent b1dcbb51ef
commit 238c270ac4
12 changed files with 1195 additions and 335 deletions
+125 -1
View File
@@ -1,5 +1,50 @@
import { readFileSync, rmSync } from "node:fs";
import { describe, expect, test } from "bun:test";
import { stripAgentRunResourceWrapperArgs } from "./agentrun";
import { parseAgentRunClientConfigYaml, resolveAgentRunAuth, runAgentRunCommand, stripAgentRunResourceWrapperArgs } from "./agentrun";
const agentRunClientYaml = [
"manager:",
" baseUrl: https://agentrun.127-0-0-1.nip.io/",
" timeoutMs: 15000",
"publicExposure:",
" enabled: true",
" proxyName: agentrun-v01-frpc",
" remotePort: 22880",
" publicBaseUrl: https://agentrun.127-0-0-1.nip.io/",
" masterBaseUrl: http://127.0.0.1:22880",
" masterFrps:",
" configPath: /opt/hwlab-frp/frps.dev.toml",
" containerName: hwlab-frps-dev",
" masterCaddy:",
" enabled: true",
" domain: agentrun.127-0-0-1.nip.io",
" configPath: /etc/caddy/Caddyfile",
" serviceName: caddy",
" upstreamBaseUrl: http://127.0.0.1:22880",
" responseHeaderTimeoutSeconds: 60",
"auth:",
" env: HWLAB_API_KEY",
" file: /tmp/hwlab-api-key.env",
" header: Authorization",
" scheme: Bearer",
"client:",
" role: render-only",
" transport: direct-http",
].join("\n");
const agentRunMinimalClientYaml = [
"manager:",
" baseUrl: http://agentrun.example.local:8080",
" timeoutMs: 15000",
"auth:",
" env: HWLAB_API_KEY",
" file: /tmp/hwlab-api-key.env",
" header: Authorization",
" scheme: Bearer",
"client:",
" role: render-only",
" transport: direct-http",
].join("\n");
describe("AgentRun resource bridge argv", () => {
test("does not forward UniDesk output flags to create task prompt argv", () => {
@@ -38,3 +83,82 @@ describe("AgentRun resource bridge argv", () => {
]);
});
});
describe("AgentRun render-only REST client config", () => {
test("parses explicit YAML config for direct REST render-only client", () => {
const config = parseAgentRunClientConfigYaml(agentRunClientYaml, "config/agentrun.yaml");
expect(config.manager.baseUrl).toBe("https://agentrun.127-0-0-1.nip.io/");
expect(config.publicExposure?.publicBaseUrl).toBe("https://agentrun.127-0-0-1.nip.io/");
expect(config.publicExposure?.masterCaddy.domain).toBe("agentrun.127-0-0-1.nip.io");
expect(config.auth.env).toBe("HWLAB_API_KEY");
expect(config.auth.header).toBe("Authorization");
expect(config.auth.scheme).toBe("Bearer");
expect(config.client.role).toBe("render-only");
expect(config.client.transport).toBe("direct-http");
});
test("uses env auth before configured file auth without exposing the key", () => {
const config = parseAgentRunClientConfigYaml(agentRunClientYaml, "config/agentrun.yaml");
const auth = resolveAgentRunAuth(config, { HWLAB_API_KEY: "secret-value" });
expect(auth.source).toBe("env");
expect(auth.value).toBe("secret-value");
});
test("requires explicit render-only direct-http client contract", () => {
expect(() => parseAgentRunClientConfigYaml(agentRunClientYaml.replace(" transport: direct-http", " transport: ssh-bridge"), "config/agentrun.yaml")).toThrow("client.transport must be direct-http");
expect(() => parseAgentRunClientConfigYaml(agentRunClientYaml.replace(" role: render-only", " role: proxy"), "config/agentrun.yaml")).toThrow("client.role must be render-only");
});
});
describe("AgentRun default transport contract", () => {
test("server-facing compatibility groups use REST dispatcher, not official SSH CLI wrapper", () => {
const source = readFileSync(new URL("./agentrun.ts", import.meta.url), "utf8");
const commandRouter = source.slice(source.indexOf("export async function runAgentRunCommand"), source.indexOf("function isAgentRunRestCompatGroup"));
expect(commandRouter).toContain("runAgentRunRestCompatCommand");
expect(commandRouter).not.toContain("runOfficialAgentRunCli");
expect(commandRouter).not.toContain("runPreparedOfficialAgentRunCli");
});
test("resource verbs can use direct REST without UniDesk SSH config", async () => {
const server = Bun.serve({
port: 0,
fetch(request) {
const url = new URL(request.url);
expect(request.headers.get("authorization")).toBe("Bearer secret-value");
expect(url.pathname).toBe("/api/v1/queue/tasks");
expect(url.searchParams.get("queue")).toBe("commander");
expect(url.searchParams.get("state")).toBe("running");
return Response.json({
ok: true,
data: {
items: [
{ id: "qt_1", state: "queued", queue: "commander" },
],
},
});
},
});
const previousConfig = process.env.AGENTRUN_CLIENT_CONFIG;
const previousKey = process.env.HWLAB_API_KEY;
process.env.HWLAB_API_KEY = "secret-value";
const tempConfigPath = `/tmp/unidesk-agentrun-test-${process.pid}-${Date.now()}.yaml`;
try {
process.env.AGENTRUN_CLIENT_CONFIG = tempConfigPath;
await Bun.write(process.env.AGENTRUN_CLIENT_CONFIG, agentRunMinimalClientYaml.replace("http://agentrun.example.local:8080", server.url.href.replace(/\/$/u, "")));
const result = await runAgentRunCommand(null, ["get", "tasks", "--queue", "commander", "-o", "json"]);
expect(result.ok).toBe(true);
expect("renderedText" in result).toBe(true);
if ("renderedText" in result) {
expect(result.renderedText).toContain("\"kind\": \"TaskList\"");
expect(result.renderedText).toContain("\"name\": \"qt_1\"");
}
} finally {
server.stop(true);
if (previousConfig === undefined) delete process.env.AGENTRUN_CLIENT_CONFIG;
else process.env.AGENTRUN_CLIENT_CONFIG = previousConfig;
if (previousKey === undefined) delete process.env.HWLAB_API_KEY;
else process.env.HWLAB_API_KEY = previousKey;
rmSync(tempConfigPath, { force: true });
}
});
});
+1017 -314
View File
File diff suppressed because it is too large Load Diff