fix: expose resource aliases on runner path
This commit is contained in:
@@ -3,6 +3,7 @@ import type { BackendProfile, ExecutionPolicy, JsonRecord, JsonValue, RunRecord,
|
||||
import { backendProfileSpec } from "../common/backend-profiles.js";
|
||||
|
||||
const defaultBootRepoUrl = "http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/agentrun.git";
|
||||
const defaultResourceBinPath = "/usr/local/bin";
|
||||
|
||||
export interface RunnerJobRenderOptions {
|
||||
run: RunRecord;
|
||||
@@ -165,6 +166,7 @@ function runnerEnv(options: RunnerJobRenderOptions, context: { namespace: string
|
||||
{ name: "AGENTRUN_SESSION_REF_JSON", value: JSON.stringify(options.run.sessionRef ?? null) },
|
||||
{ name: "AGENTRUN_RESOURCE_BUNDLE_JSON", value: JSON.stringify(options.run.resourceBundleRef ?? null) },
|
||||
{ name: "AGENTRUN_WORKSPACE_ROOT", value: "/home/agentrun/workspaces" },
|
||||
{ name: "AGENTRUN_RESOURCE_BIN_PATH", value: defaultResourceBinPath },
|
||||
{ name: "AGENTRUN_SOURCE_COMMIT", value: context.sourceCommit },
|
||||
{ name: "AGENTRUN_BOOT_COMMIT", value: context.sourceCommit },
|
||||
{ name: "AGENTRUN_BOOT_REPO_URL", value: defaultBootRepoUrl },
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { spawn } from "node:child_process";
|
||||
import { chmod, mkdir, writeFile } from "node:fs/promises";
|
||||
import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { AgentRunError } from "../common/errors.js";
|
||||
import { redactText } from "../common/redaction.js";
|
||||
@@ -59,7 +59,9 @@ async function materializeToolAliases(checkoutPath: string, aliases: NonNullable
|
||||
for (const alias of aliases) {
|
||||
const target = resolveBundlePath(checkoutPath, alias.path, `toolAliases.${alias.name}.path`);
|
||||
const wrapper = path.join(binPath, alias.name);
|
||||
await writeFile(wrapper, aliasWrapper(alias.kind, target), "utf8");
|
||||
const content = aliasWrapper(alias.kind, target);
|
||||
await assertAliasWrapperWritable(wrapper, alias.name);
|
||||
await writeFile(wrapper, content, "utf8");
|
||||
await chmod(wrapper, 0o755);
|
||||
names.push(alias.name);
|
||||
}
|
||||
@@ -67,10 +69,22 @@ async function materializeToolAliases(checkoutPath: string, aliases: NonNullable
|
||||
}
|
||||
|
||||
function aliasWrapper(kind: string, target: string): string {
|
||||
if (kind === "node-script") return `#!/usr/bin/env sh\nexec node ${shellArg(target)} "$@"\n`;
|
||||
if (kind === "bun-script") return `#!/usr/bin/env sh\nexec bun ${shellArg(target)} "$@"\n`;
|
||||
if (kind === "sh-script") return `#!/usr/bin/env sh\nexec sh ${shellArg(target)} "$@"\n`;
|
||||
return `#!/usr/bin/env sh\nexec ${shellArg(target)} "$@"\n`;
|
||||
if (kind === "node-script") return `#!/usr/bin/env sh\n# agentrun-resource-alias-wrapper\nexec node ${shellArg(target)} "$@"\n`;
|
||||
if (kind === "bun-script") return `#!/usr/bin/env sh\n# agentrun-resource-alias-wrapper\nexec bun ${shellArg(target)} "$@"\n`;
|
||||
if (kind === "sh-script") return `#!/usr/bin/env sh\n# agentrun-resource-alias-wrapper\nexec sh ${shellArg(target)} "$@"\n`;
|
||||
return `#!/usr/bin/env sh\n# agentrun-resource-alias-wrapper\nexec ${shellArg(target)} "$@"\n`;
|
||||
}
|
||||
|
||||
async function assertAliasWrapperWritable(wrapper: string, name: string): Promise<void> {
|
||||
try {
|
||||
const existing = await readFile(wrapper, "utf8");
|
||||
if (existing.includes("agentrun-resource-alias-wrapper")) return;
|
||||
throw new AgentRunError("schema-invalid", `resource bundle tool alias ${name} would overwrite an existing command`, { httpStatus: 409, details: { wrapper: pathSummary(wrapper) } });
|
||||
} catch (error) {
|
||||
if (error instanceof AgentRunError) throw error;
|
||||
if (error && typeof error === "object" && "code" in error && (error as { code?: unknown }).code === "ENOENT") return;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function git(args: string[], cwd: string, options: { allowFailure?: boolean } = {}): Promise<{ stdout: string; stderr: string }> {
|
||||
|
||||
@@ -7,10 +7,11 @@ import { startManagerServer } from "../../mgr/server.js";
|
||||
import { ManagerClient } from "../../mgr/client.js";
|
||||
import { MemoryAgentRunStore } from "../../mgr/store.js";
|
||||
import { runOnce } from "../../runner/run-once.js";
|
||||
import type { JsonRecord } from "../../common/types.js";
|
||||
import type { JsonRecord, ResourceBundleRef } from "../../common/types.js";
|
||||
import { assertNoSecretLeak, type SelfTestCase, type SelfTestContext } from "../harness.js";
|
||||
|
||||
const execFile = promisify(execFileCallback);
|
||||
type LocalBundle = { repoUrl: string; commitId: string; toolAliases?: ResourceBundleRef["toolAliases"] };
|
||||
|
||||
const selfTest: SelfTestCase = async (context) => {
|
||||
const containerfile = await readFile(path.join(context.root, "deploy/container/Containerfile"), "utf8");
|
||||
@@ -56,6 +57,7 @@ console.log(JSON.stringify({ apiVersion: manifest.apiVersion, kind: manifest.kin
|
||||
);
|
||||
const manifest = JSON.parse(await readFile(createdManifest, "utf8")) as JsonRecord;
|
||||
assert.ok(JSON.stringify(manifest).includes("AGENTRUN_RESOURCE_BUNDLE_JSON"));
|
||||
assert.equal(runnerEnvValue(manifest, "AGENTRUN_RESOURCE_BIN_PATH"), "/usr/local/bin");
|
||||
assert.ok(JSON.stringify(manifest).includes("/opt/agentrun/deploy/runtime/boot/agentrun-runner.sh"));
|
||||
assert.ok(JSON.stringify(manifest).includes("AGENTRUN_BOOT_COMMIT"));
|
||||
assertNoSecretLeak(created);
|
||||
@@ -78,6 +80,11 @@ console.log(JSON.stringify({ apiVersion: manifest.apiVersion, kind: manifest.kin
|
||||
assert.equal(runResult.terminalStatus, "completed");
|
||||
const hwpod = await execFile(path.join(resourceBin, "hwpod"), ["profile", "list"]);
|
||||
assert.match(hwpod.stdout, /"argv":\["profile","list"\]/u);
|
||||
await writeFile(path.join(resourceBin, "blocked"), "#!/usr/bin/env sh\necho existing\n", "utf8");
|
||||
const blockedRun = await createHwlabRun(client, context, { ...bundle, toolAliases: [{ name: "blocked", path: "tools/device-pod-cli.mjs", kind: "node-script" }] }, "hwlab-session-blocked-alias", "blocked alias", "hwlab-command-blocked-alias");
|
||||
const blockedResult = await runOnce({ managerUrl: server.baseUrl, runId: blockedRun.runId, codexCommand: context.fakeCodexCommand, codexArgs: context.fakeCodexArgs, codexHome: context.codexHome, env: { CODEX_HOME: context.codexHome, AGENTRUN_WORKSPACE_ROOT: path.join(context.tmp, "workspaces-blocked"), AGENTRUN_RESOURCE_BIN_PATH: resourceBin }, oneShot: true });
|
||||
assert.equal(blockedResult.terminalStatus, "blocked");
|
||||
assert.equal(blockedResult.failureKind, "schema-invalid");
|
||||
const session = await store.getSession("hwlab-session-resume");
|
||||
assert.equal(session?.threadId, "thread_selftest_1");
|
||||
const resultEnvelope = await client.get(`/api/v1/runs/${sessionRun.runId}/commands/${sessionRun.commandId}/result`) as JsonRecord;
|
||||
@@ -137,7 +144,7 @@ console.log(JSON.stringify({ apiVersion: manifest.apiVersion, kind: manifest.kin
|
||||
}
|
||||
};
|
||||
|
||||
async function createLocalGitBundle(context: SelfTestContext): Promise<{ repoUrl: string; commitId: string }> {
|
||||
async function createLocalGitBundle(context: SelfTestContext): Promise<LocalBundle> {
|
||||
const repo = path.join(context.tmp, "bundle-repo");
|
||||
await mkdir(repo, { recursive: true });
|
||||
await execFile("git", ["init"], { cwd: repo });
|
||||
@@ -150,13 +157,14 @@ async function createLocalGitBundle(context: SelfTestContext): Promise<{ repoUrl
|
||||
return { repoUrl: repo, commitId: stdout.trim() };
|
||||
}
|
||||
|
||||
async function createHwlabRun(client: ManagerClient, context: SelfTestContext, bundle: { repoUrl: string; commitId: string }, sessionId: string, prompt: string, idempotencyKey: string, timeoutMs = 15_000): Promise<{ runId: string; commandId: string }> {
|
||||
async function createHwlabRun(client: ManagerClient, context: SelfTestContext, bundle: LocalBundle, sessionId: string, prompt: string, idempotencyKey: string, timeoutMs = 15_000): Promise<{ runId: string; commandId: string }> {
|
||||
const toolAliases = bundle.toolAliases ?? [{ name: "hwpod", path: "tools/device-pod-cli.mjs", kind: "node-script" }];
|
||||
const run = await client.post("/api/v1/runs", {
|
||||
tenantId: "hwlab",
|
||||
projectId: "pikasTech/HWLAB",
|
||||
workspaceRef: { kind: "opaque", repo: "pikasTech/HWLAB" },
|
||||
sessionRef: { sessionId, conversationId: sessionId },
|
||||
resourceBundleRef: { kind: "git", repoUrl: bundle.repoUrl, commitId: bundle.commitId, toolAliases: [{ name: "hwpod", path: "tools/device-pod-cli.mjs", kind: "node-script" }], submodules: false, lfs: false },
|
||||
resourceBundleRef: { kind: "git", repoUrl: bundle.repoUrl, commitId: bundle.commitId, toolAliases, submodules: false, lfs: false },
|
||||
providerId: "G14",
|
||||
backendProfile: "codex",
|
||||
executionPolicy: {
|
||||
@@ -195,4 +203,13 @@ async function readTextIfExists(filePath: string): Promise<string> {
|
||||
}
|
||||
}
|
||||
|
||||
function runnerEnvValue(manifest: JsonRecord, name: string): unknown {
|
||||
const spec = manifest.spec as JsonRecord;
|
||||
const template = spec.template as JsonRecord;
|
||||
const podSpec = template.spec as JsonRecord;
|
||||
const containers = podSpec.containers as JsonRecord[];
|
||||
const env = containers[0]?.env as JsonRecord[];
|
||||
return env.find((entry) => entry.name === name)?.value;
|
||||
}
|
||||
|
||||
export default selfTest;
|
||||
|
||||
Reference in New Issue
Block a user