mirror of
https://github.com/mvschwarz/openrig.git
synced 2026-11-07 05:56:21 -10:00
fix conveyor release cli paths
This commit is contained in:
1 parent
2f561633ec
commit
b74b1c5d48
10 files changed
+161
-23
No files matched your search
@@ -57,7 +57,7 @@ Every agent runs in a tmux session you can attach to, inspect, and work with dir
|
||||
OpenRig ships with a conveyor starter rig for learning workflow handoff:
|
||||
|
||||
```bash
|
||||
rig specs preview conveyor
|
||||
rig specs preview conveyor --kind rig
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
@@ -27,7 +27,8 @@ export function importCommand(depsOverride?: ImportDeps): Command {
|
||||
.option("--preflight", "Run preflight checks")
|
||||
.option("--target-rig <rigId>", "Target existing rig for additive materialization")
|
||||
.option("--rig-root <root>", "Root directory for pod-aware resolution")
|
||||
.action(async (filePath: string, opts: { instantiate?: boolean; materializeOnly?: boolean; preflight?: boolean; targetRig?: string; rigRoot?: string }) => {
|
||||
.option("--cwd <path>", "Override launch/materialization working directory for all members")
|
||||
.action(async (filePath: string, opts: { instantiate?: boolean; materializeOnly?: boolean; preflight?: boolean; targetRig?: string; rigRoot?: string; cwd?: string }) => {
|
||||
const deps = getDeps();
|
||||
|
||||
// Read local file first (before daemon check — fail fast on missing file)
|
||||
@@ -59,7 +60,11 @@ export function importCommand(depsOverride?: ImportDeps): Command {
|
||||
const rigRoot = podAware
|
||||
? (opts.rigRoot ? nodePath.resolve(opts.rigRoot) : nodePath.dirname(nodePath.resolve(filePath)))
|
||||
: undefined;
|
||||
const extraHeaders = rigRoot ? { "X-Rig-Root": rigRoot } : undefined;
|
||||
const cwdOverride = opts.cwd ? nodePath.resolve(opts.cwd) : undefined;
|
||||
const extraHeaders = {
|
||||
...(rigRoot ? { "X-Rig-Root": rigRoot } : {}),
|
||||
...(cwdOverride ? { "X-Cwd-Override": cwdOverride } : {}),
|
||||
};
|
||||
|
||||
if (opts.instantiate && opts.materializeOnly) {
|
||||
console.error("Choose either --instantiate or --materialize-only, not both.");
|
||||
@@ -97,7 +102,7 @@ export function importCommand(depsOverride?: ImportDeps): Command {
|
||||
return;
|
||||
}
|
||||
const headers = {
|
||||
...(extraHeaders ?? {}),
|
||||
...extraHeaders,
|
||||
...(opts.targetRig ? { "X-Target-Rig-Id": opts.targetRig } : {}),
|
||||
};
|
||||
const res = await client.postText<{ rigId: string; specName: string; specVersion: string; nodes: Array<{ logicalId: string; status: string }> } | { ok: false; code: string; errors?: string[]; message?: string }>("/api/rigs/import/materialize", yaml, "text/yaml", headers);
|
||||
|
||||
@@ -20,7 +20,7 @@ import type { StatusDeps } from "./status.js";
|
||||
|
||||
interface LibraryEntry {
|
||||
id: string;
|
||||
kind: string;
|
||||
kind: "rig" | "agent" | "workflow";
|
||||
name: string;
|
||||
version: string;
|
||||
sourceType: string;
|
||||
@@ -145,8 +145,9 @@ function installSpecSource(source: AddSpecSource, userRoot: string): string {
|
||||
export async function resolveLibrarySpec(
|
||||
client: DaemonClient,
|
||||
nameOrId: string,
|
||||
opts?: { kind?: LibraryEntry["kind"] },
|
||||
): Promise<LibraryEntry> {
|
||||
const res = await client.get<LibraryEntry[]>("/api/specs/library");
|
||||
const res = await client.get<LibraryEntry[]>(opts?.kind ? `/api/specs/library?kind=${opts.kind}` : "/api/specs/library");
|
||||
const entries = res.data ?? [];
|
||||
|
||||
// Try exact ID match first
|
||||
@@ -158,14 +159,16 @@ export async function resolveLibrarySpec(
|
||||
if (byName.length === 1) return byName[0]!;
|
||||
|
||||
if (byName.length > 1) {
|
||||
const candidates = byName.map((e) => ` ${e.id} — ${e.sourcePath}`).join("\n");
|
||||
const candidates = byName.map((e) => ` ${e.id} (${e.kind}) — ${e.sourcePath}`).join("\n");
|
||||
const scope = opts?.kind ? ` ${opts.kind}` : "";
|
||||
throw new Error(
|
||||
`Spec name '${nameOrId}' is ambiguous — ${byName.length} entries match.\nUse the ID instead:\n${candidates}`
|
||||
`Spec name '${nameOrId}' is ambiguous — ${byName.length}${scope} entries match.\nUse the ID instead:\n${candidates}`
|
||||
);
|
||||
}
|
||||
|
||||
const scope = opts?.kind ? ` ${opts.kind}` : "";
|
||||
throw new Error(
|
||||
`Spec '${nameOrId}' not found in library. Run 'rig specs ls' to see available rigs, agents, and managed apps.`
|
||||
`Spec '${nameOrId}' not found in${scope} library. Run 'rig specs ls' to see available rigs, agents, workflows, and managed apps.`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -195,7 +198,7 @@ Examples:
|
||||
// specs ls
|
||||
cmd.command("ls")
|
||||
.description("List library rigs, agents, and managed apps")
|
||||
.option("--kind <kind>", "Filter by kind (rig or agent)")
|
||||
.option("--kind <kind>", "Filter by kind (rig, agent, or workflow)")
|
||||
.option("--json", "JSON output")
|
||||
.action(async (opts: { kind?: string; json?: boolean }) => {
|
||||
try {
|
||||
@@ -227,11 +230,12 @@ Examples:
|
||||
cmd.command("show")
|
||||
.argument("<name-or-id>", "Spec name or library ID")
|
||||
.description("Show spec metadata and path")
|
||||
.option("--kind <kind>", "Disambiguate by kind (rig, agent, or workflow)")
|
||||
.option("--json", "JSON output")
|
||||
.action(async (nameOrId: string, opts: { json?: boolean }) => {
|
||||
.action(async (nameOrId: string, opts: { kind?: LibraryEntry["kind"]; json?: boolean }) => {
|
||||
try {
|
||||
const client = await getClient();
|
||||
const entry = await resolveLibrarySpec(client, nameOrId);
|
||||
const entry = await resolveLibrarySpec(client, nameOrId, opts.kind ? { kind: opts.kind } : undefined);
|
||||
|
||||
if (opts.json) {
|
||||
console.log(JSON.stringify(entry, null, 2));
|
||||
@@ -254,11 +258,12 @@ Examples:
|
||||
cmd.command("preview")
|
||||
.argument("<name-or-id>", "Spec name or library ID")
|
||||
.description("Show structured spec review, including managed app details")
|
||||
.option("--kind <kind>", "Disambiguate by kind (rig, agent, or workflow)")
|
||||
.option("--json", "JSON output")
|
||||
.action(async (nameOrId: string, opts: { json?: boolean }) => {
|
||||
.action(async (nameOrId: string, opts: { kind?: LibraryEntry["kind"]; json?: boolean }) => {
|
||||
try {
|
||||
const client = await getClient();
|
||||
const entry = await resolveLibrarySpec(client, nameOrId);
|
||||
const entry = await resolveLibrarySpec(client, nameOrId, opts.kind ? { kind: opts.kind } : undefined);
|
||||
const res = await client.get<Record<string, unknown>>(`/api/specs/library/${encodeURIComponent(entry.id)}/review`);
|
||||
|
||||
if (opts.json) {
|
||||
|
||||
@@ -123,7 +123,7 @@ Examples:
|
||||
if (isRigName && !opts.existing) {
|
||||
try {
|
||||
const { resolveLibrarySpec } = await import("./specs.js");
|
||||
const entry = await resolveLibrarySpec(client, source);
|
||||
const entry = await resolveLibrarySpec(client, source, { kind: "rig" });
|
||||
// Library match found — check for existing-rig collision
|
||||
// Use /api/rigs/summary which mirrors findRigsByName (includes stopped rigs)
|
||||
const rigSummaries = await fetchRigSummaries();
|
||||
|
||||
@@ -124,6 +124,7 @@ function createMockDaemon() {
|
||||
if (req.method === "POST" && url.pathname === "/api/rigs/import") {
|
||||
capturedImportHeaders = {
|
||||
"x-rig-root": req.headers["x-rig-root"],
|
||||
"x-cwd-override": req.headers["x-cwd-override"],
|
||||
};
|
||||
let body = "";
|
||||
req.on("data", (c: Buffer) => { body += c.toString(); });
|
||||
@@ -144,6 +145,7 @@ function createMockDaemon() {
|
||||
capturedImportHeaders = {
|
||||
"x-rig-root": req.headers["x-rig-root"],
|
||||
"x-target-rig-id": req.headers["x-target-rig-id"],
|
||||
"x-cwd-override": req.headers["x-cwd-override"],
|
||||
};
|
||||
res.writeHead(201, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({
|
||||
@@ -298,6 +300,16 @@ describe("rig export + import", () => {
|
||||
expect(capturedImportHeaders["x-target-rig-id"]).toBe("rig-123");
|
||||
});
|
||||
|
||||
it("import --materialize-only with --cwd forwards absolute cwd override", async () => {
|
||||
capturedImportHeaders = {};
|
||||
const deps = importDeps(`version: "0.2"\nname: test\npods: []\n`);
|
||||
const program = new Command();
|
||||
program.addCommand(importCommand(deps));
|
||||
await captureLogs(() => program.parseAsync(["node", "rig", "import", "rig.yaml", "--materialize-only", "--cwd", "relative/project", "--rig-root", "/tmp"]));
|
||||
expect(capturedImportHeaders["x-cwd-override"]).toMatch(/^\//);
|
||||
expect(String(capturedImportHeaders["x-cwd-override"])).toContain("relative/project");
|
||||
});
|
||||
|
||||
// Test 8: import --instantiate with preflight fail (409)
|
||||
it("import --instantiate: preflight conflict (409)", async () => {
|
||||
const deps = importDeps("CONFLICT");
|
||||
@@ -423,6 +435,16 @@ describe("rig export + import", () => {
|
||||
expect(capturedImportHeaders["x-rig-root"]).toMatch(/\/my\/project/);
|
||||
});
|
||||
|
||||
it("import --instantiate with --cwd forwards absolute cwd override", async () => {
|
||||
capturedImportHeaders = {};
|
||||
const deps = importDeps("schema_version: 1\nname: test\npods:\n - name: pod-a\n");
|
||||
const program = new Command();
|
||||
program.addCommand(importCommand(deps));
|
||||
await captureLogs(() => program.parseAsync(["node", "rig", "import", "rig.yaml", "--instantiate", "--cwd", "relative/project", "--rig-root", "/my/project"]));
|
||||
expect(capturedImportHeaders["x-cwd-override"]).toMatch(/^\//);
|
||||
expect(String(capturedImportHeaders["x-cwd-override"])).toContain("relative/project");
|
||||
});
|
||||
|
||||
// NS-T14: import --instantiate handoff includes attach command
|
||||
it("import --instantiate success shows attach command", async () => {
|
||||
const deps: ImportDeps = {
|
||||
|
||||
@@ -146,18 +146,46 @@ describe("Specs CLI", () => {
|
||||
expect(exitCode).toBe(1);
|
||||
});
|
||||
|
||||
it("specs show --kind resolves same-name rig/workflow entries", async () => {
|
||||
const dupServer = http.createServer((req, res) => {
|
||||
const url = decodeURIComponent(req.url ?? "");
|
||||
if (url === "/api/specs/library?kind=rig") {
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify([{ ...LIBRARY_ENTRIES[0], id: "rig-same", kind: "rig", name: "conveyor" }]));
|
||||
} else {
|
||||
res.writeHead(404).end();
|
||||
}
|
||||
});
|
||||
await new Promise<void>((resolve) => { dupServer.listen(0, resolve); });
|
||||
const dupPort = (dupServer.address() as { port: number }).port;
|
||||
|
||||
const prog = new Command();
|
||||
prog.exitOverride();
|
||||
prog.addCommand(specsCommand(runningDeps(dupPort)));
|
||||
|
||||
const { logs, exitCode } = await captureLogs(async () => {
|
||||
await prog.parseAsync(["node", "rig", "specs", "show", "conveyor", "--kind", "rig"]);
|
||||
});
|
||||
dupServer.close();
|
||||
|
||||
const output = logs.join("\n");
|
||||
expect(output).toContain("Kind: rig");
|
||||
expect(output).toContain("/builtin/review-rig.yaml");
|
||||
expect(exitCode).toBeUndefined();
|
||||
});
|
||||
|
||||
it("specs show not-found error points people back to managed apps in the library", async () => {
|
||||
const { logs, exitCode } = await captureLogs(async () => {
|
||||
await makeCmd().parseAsync(["node", "rig", "specs", "show", "missing-app"]);
|
||||
});
|
||||
|
||||
expect(logs.join("\n")).toContain("Run 'rig specs ls' to see available rigs, agents, and managed apps.");
|
||||
expect(logs.join("\n")).toContain("Run 'rig specs ls' to see available rigs, agents, workflows, and managed apps.");
|
||||
expect(exitCode).toBe(1);
|
||||
});
|
||||
|
||||
it("specs preview --json returns structured review", async () => {
|
||||
const { logs } = await captureLogs(async () => {
|
||||
await makeCmd().parseAsync(["node", "rig", "specs", "preview", "review-rig", "--json"]);
|
||||
await makeCmd().parseAsync(["node", "rig", "specs", "preview", "review-rig", "--kind", "rig", "--json"]);
|
||||
});
|
||||
const parsed = JSON.parse(logs.join("\n"));
|
||||
expect(parsed.kind).toBe("rig");
|
||||
@@ -177,12 +205,14 @@ describe("Specs CLI", () => {
|
||||
const { join } = await import("node:path");
|
||||
const { tmpdir } = await import("node:os");
|
||||
|
||||
// Use temp dir as HOME to avoid polluting real ~/.openrig/specs/
|
||||
// Use temp OpenRig home to avoid polluting real ~/.openrig/specs/
|
||||
const tmpDir = mkdtempSync(join(tmpdir(), "specs-add-"));
|
||||
const specPath = join(tmpDir, "test-spec.yaml");
|
||||
writeFileSync(specPath, 'name: test-spec\nversion: "0.2"\npods: []\nedges: []\n');
|
||||
const savedHome = process.env["HOME"];
|
||||
const savedOpenRigHome = process.env["OPENRIG_HOME"];
|
||||
process.env["HOME"] = tmpDir;
|
||||
process.env["OPENRIG_HOME"] = join(tmpDir, ".openrig");
|
||||
|
||||
const addServer = http.createServer((req, res) => {
|
||||
const url = decodeURIComponent(req.url ?? "");
|
||||
@@ -220,6 +250,8 @@ describe("Specs CLI", () => {
|
||||
expect(existsSync(join(tmpDir, ".openrig", "specs", "test-spec.yaml"))).toBe(true);
|
||||
|
||||
process.env["HOME"] = savedHome;
|
||||
if (savedOpenRigHome !== undefined) process.env["OPENRIG_HOME"] = savedOpenRigHome;
|
||||
else delete process.env["OPENRIG_HOME"];
|
||||
rmSync(tmpDir, { recursive: true, force: true });
|
||||
|
||||
const output = logs.join("\n");
|
||||
@@ -275,7 +307,9 @@ describe("Specs CLI", () => {
|
||||
].join("\n"));
|
||||
|
||||
const savedHome = process.env["HOME"];
|
||||
const savedOpenRigHome = process.env["OPENRIG_HOME"];
|
||||
process.env["HOME"] = tmpDir;
|
||||
process.env["OPENRIG_HOME"] = join(tmpDir, ".openrig");
|
||||
|
||||
const addServer = http.createServer((req, res) => {
|
||||
const url = decodeURIComponent(req.url ?? "");
|
||||
@@ -320,6 +354,8 @@ describe("Specs CLI", () => {
|
||||
} finally {
|
||||
addServer.close();
|
||||
process.env["HOME"] = savedHome;
|
||||
if (savedOpenRigHome !== undefined) process.env["OPENRIG_HOME"] = savedOpenRigHome;
|
||||
else delete process.env["OPENRIG_HOME"];
|
||||
rmSync(tmpDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -690,6 +690,42 @@ describe("Up CLI", () => {
|
||||
expect(exitCode).toBe(1);
|
||||
});
|
||||
|
||||
it("up resolves a same-name rig and workflow to the rig library entry", async () => {
|
||||
const origListeners = server.listeners("request");
|
||||
let lastBody: Record<string, unknown> = {};
|
||||
server.removeAllListeners("request");
|
||||
server.on("request", async (req: http.IncomingMessage, res: http.ServerResponse) => {
|
||||
let body = "";
|
||||
for await (const chunk of req) body += chunk;
|
||||
const url = decodeURIComponent(req.url ?? "");
|
||||
if (url === "/api/specs/library?kind=rig") {
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify([{ id: "rig1", kind: "rig", name: "conveyor", sourceType: "builtin", sourcePath: "/specs/rigs/conveyor/rig.yaml" }]));
|
||||
} else if (url === "/api/rigs/summary") {
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify([]));
|
||||
} else if (url === "/api/up" && req.method === "POST") {
|
||||
lastBody = JSON.parse(body);
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ status: "planned", runId: "r", stages: [], errors: [] }));
|
||||
} else {
|
||||
res.writeHead(500, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ error: `unexpected request: ${req.method} ${url}` }));
|
||||
}
|
||||
});
|
||||
|
||||
const { exitCode } = await captureLogs(async () => {
|
||||
await makeCmd().parseAsync(["node", "rig", "up", "conveyor", "--plan"]);
|
||||
});
|
||||
|
||||
server.removeAllListeners("request");
|
||||
for (const l of origListeners) server.on("request", l as (...args: unknown[]) => void);
|
||||
|
||||
expect(lastBody.sourceRef).toBe("/specs/rigs/conveyor/rig.yaml");
|
||||
expect(lastBody.cwdOverride).toBe(process.cwd());
|
||||
expect(exitCode).toBeUndefined();
|
||||
});
|
||||
|
||||
it("up --existing bypasses library-name ambiguity and posts the rig name", async () => {
|
||||
const origListeners = server.listeners("request");
|
||||
let lastBody: Record<string, unknown> = {};
|
||||
|
||||
@@ -365,7 +365,7 @@ export class PodRigInstantiator {
|
||||
async materialize(
|
||||
rigSpecYaml: string,
|
||||
rigRoot: string,
|
||||
opts?: { targetRigId?: string; suppressSummaryEvent?: boolean },
|
||||
opts?: { targetRigId?: string; suppressSummaryEvent?: boolean; cwdOverride?: string },
|
||||
): Promise<MaterializeOutcome> {
|
||||
let raw: unknown;
|
||||
try {
|
||||
@@ -390,6 +390,7 @@ export class PodRigInstantiator {
|
||||
const preflight = rigPreflight({
|
||||
rigSpecYaml,
|
||||
rigRoot,
|
||||
cwdOverride: opts?.cwdOverride,
|
||||
fsOps: this.deps.fsOps,
|
||||
rigNameOverride: targetRig?.rig.name,
|
||||
externalQualifiedIds: targetRig?.nodes.map((node) => node.logicalId),
|
||||
@@ -452,11 +453,12 @@ export class PodRigInstantiator {
|
||||
|
||||
for (const member of pod.members) {
|
||||
const qualifiedId = `${pod.id}.${member.id}`;
|
||||
const effectiveCwd = resolveLaunchCwd(member.cwd, rigRoot, opts?.cwdOverride);
|
||||
const node = this.deps.rigRepo.addNode(materializedRigId, qualifiedId, {
|
||||
runtime: member.runtime,
|
||||
model: member.model,
|
||||
codexConfigProfile: member.codexConfigProfile,
|
||||
cwd: member.cwd,
|
||||
cwd: effectiveCwd,
|
||||
restorePolicy: member.restorePolicy,
|
||||
podId: podRecord.id,
|
||||
agentRef: member.agentRef,
|
||||
|
||||
@@ -80,8 +80,9 @@ rigspecImportRoutes.post("/", async (c) => {
|
||||
if (isPodAware) {
|
||||
const rigRoot = c.req.header("X-Rig-Root");
|
||||
if (!rigRoot) return c.json({ error: "X-Rig-Root header required for pod-aware specs", code: "missing_rig_root" }, 400);
|
||||
const cwdOverride = c.req.header("X-Cwd-Override") ?? undefined;
|
||||
|
||||
const outcome = await podInstantiator.instantiate(body, rigRoot);
|
||||
const outcome = await podInstantiator.instantiate(body, rigRoot, { cwdOverride });
|
||||
if (!outcome.ok) {
|
||||
const status = outcome.code === "validation_failed" ? 400
|
||||
: outcome.code === "preflight_failed" ? 409
|
||||
@@ -133,7 +134,8 @@ rigspecImportRoutes.post("/materialize", async (c) => {
|
||||
if (!rigRoot) return c.json({ error: "X-Rig-Root header required for pod-aware specs", code: "missing_rig_root" }, 400);
|
||||
|
||||
const targetRigId = c.req.header("X-Target-Rig-Id") ?? undefined;
|
||||
const outcome = await podInstantiator.materialize(body, rigRoot, { targetRigId });
|
||||
const cwdOverride = c.req.header("X-Cwd-Override") ?? undefined;
|
||||
const outcome = await podInstantiator.materialize(body, rigRoot, { targetRigId, cwdOverride });
|
||||
if (!outcome.ok) {
|
||||
const status = outcome.code === "validation_failed" ? 400
|
||||
: outcome.code === "preflight_failed" ? 409
|
||||
@@ -181,8 +183,9 @@ rigspecImportRoutes.post("/preflight", async (c) => {
|
||||
if (isPodAware) {
|
||||
const rigRoot = c.req.header("X-Rig-Root");
|
||||
if (!rigRoot) return c.json({ ready: false, errors: ["X-Rig-Root header required for pod-aware specs"], warnings: [] }, 400);
|
||||
const cwdOverride = c.req.header("X-Cwd-Override") ?? undefined;
|
||||
const fsOps = { readFile: (p: string) => fs.readFileSync(p, "utf-8"), exists: (p: string) => fs.existsSync(p) };
|
||||
const result = rigPreflight({ rigSpecYaml: body, rigRoot, fsOps });
|
||||
const result = rigPreflight({ rigSpecYaml: body, rigRoot, cwdOverride, fsOps });
|
||||
return c.json(result);
|
||||
}
|
||||
|
||||
|
||||
@@ -320,6 +320,22 @@ pods:
|
||||
edges: []
|
||||
`;
|
||||
|
||||
const MATERIALIZE_RELATIVE_CWD_YAML = `
|
||||
version: "0.2"
|
||||
name: cwd-override-topology
|
||||
pods:
|
||||
- id: research
|
||||
label: Research
|
||||
members:
|
||||
- id: scout
|
||||
agent_ref: "builtin:terminal"
|
||||
profile: none
|
||||
runtime: terminal
|
||||
cwd: /openrig-install-should-not-be-used
|
||||
edges: []
|
||||
edges: []
|
||||
`;
|
||||
|
||||
const MATERIALIZE_FRAGMENT_YAML = `
|
||||
version: "0.2"
|
||||
name: research-fragment
|
||||
@@ -440,6 +456,19 @@ describe("Rigspec import routes (pod-aware dual-stack)", () => {
|
||||
expect(body.nodes).toEqual([{ logicalId: "research.scout", status: "materialized" }]);
|
||||
});
|
||||
|
||||
it("POST /api/rigs/import/materialize honors X-Cwd-Override", async () => {
|
||||
const setup = createTestApp(db);
|
||||
const res = await setup.app.request("/api/rigs/import/materialize", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "text/plain", "X-Rig-Root": "/tmp", "X-Cwd-Override": "/tmp" },
|
||||
body: MATERIALIZE_RELATIVE_CWD_YAML,
|
||||
});
|
||||
expect(res.status).toBe(201);
|
||||
const body = await res.json();
|
||||
const rig = setup.rigRepo.getRig(body.rigId);
|
||||
expect(rig?.nodes.find((node) => node.logicalId === "research.scout")?.cwd).toBe("/tmp");
|
||||
});
|
||||
|
||||
it("POST /api/rigs/import/materialize can target an existing rig", async () => {
|
||||
const setup = createTestApp(db);
|
||||
const rig = setup.rigRepo.createRig("host-rig");
|
||||
|
||||
Reference in new issue
Block a user