Read native plugin task output files on the execution host (#17285)

This commit is contained in:
Nicky Leach authored and GitHub committed 2026-10-22 23:38:09 -07:00
1 parent d69631b01b
commit 412914cfa7
15 files changed
+202 -24

No files matched your search

@@ -1,5 +1,5 @@
import { existsSync } from "node:fs";
import { randomUUID } from "node:crypto";
import { createHash, randomUUID } from "node:crypto";
import { chmod, writeFile, symlink, mkdir, open } from "node:fs/promises";
import { join, dirname } from "node:path";
import { eq } from "drizzle-orm";
@@ -933,6 +933,22 @@ else if(m.id!==undefined) send({id:m.id,result:{}});
expect((await fixture.snapshot()).assets).toEqual(expect.arrayContaining([expect.objectContaining({ id: download.artifact.artifactId, companyId: fixture.companyId, createdByAgentId: fixture.agentId })]));
});
it.each(["sample.txt", "/home/agent/proof.txt"])("uploads remote task bytes at %s and fails closed without the reader", async (sourcePath) => {
const fixture=await server.fixture();
const bytes=Buffer.from("proof exists only on the execution host\n");
const read=vi.fn(async(path:string,maxBytes:number)=>{expect(path).toBe(sourcePath);expect(maxBytes).toBeGreaterThan(bytes.length);return bytes;});
const binding={...fixture.authority.binding,executionTargetKind:"remote" as const,readRemoteApiFile:read};
const authority=new PaperclipRunnerToolAuthority(server.db,binding);
const call={tool:"call_api" as const,callId:randomUUID(),arguments:{operationId:"POST /api/companies/{companyId}/issues/{issueId}/attachments",pathParams:{issueId:fixture.issueId},files:[{path:sourcePath}]}};
await expect(authority.execute(call)).resolves.toMatchObject({status:201});
expect(read).toHaveBeenCalledOnce();
expect((await fixture.snapshot()).assets).toEqual(expect.arrayContaining([expect.objectContaining({sha256:createHash("sha256").update(bytes).digest("hex"),byteSize:bytes.length})]));
const unsupported=new PaperclipRunnerToolAuthority(server.db,{...binding,readRemoteApiFile:undefined});
await expect(unsupported.execute({...call,callId:randomUUID()})).rejects.toThrow("Remote workspace file uploads are unavailable");
const changing=new PaperclipRunnerToolAuthority(server.db,{...binding,readRemoteApiFile:async()=>{await server.db.update(heartbeatRuns).set({status:"cancelled"}).where(eq(heartbeatRuns.id,fixture.runId));return bytes;}});
await expect(changing.execute({...call,callId:randomUUID()})).rejects.toThrow();
});
it("does not accept caller-supplied identity in an API comment", async () => {
const fixture = await server.fixture();
const result = await fixture.authority.execute({ tool: "call_api", callId: "comment", arguments: { operationId: "POST /api/issues/{id}/comments", pathParams: { id: fixture.issueId }, body: { body: "Identity proof", authorAgentId: randomUUID(), authorUserId: "spoofed", runId: randomUUID() } } });