mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-07 06:16:06 -10:00
fix(native): bind retained code lookup to current source identity
Co-Authored-By: Paperclip <[email protected]>
This commit is contained in:
1 parent
70082072bd
commit
12b0d2ba26
3 files changed
+16
-9
No files matched your search
@@ -25,10 +25,7 @@ export async function uploadVerifiedProviderPack(input: {
|
||||
stageDirectory: (root: string) => Promise<void>;
|
||||
verifyUploaded: () => Promise<void>;
|
||||
}): Promise<void> {
|
||||
// The controller can only stage the pack it ships. A retained continuation
|
||||
// pinned to an earlier release's pack cannot be satisfied by these bytes, by
|
||||
// either transport. Fail before clearing the remote pack or uploading.
|
||||
if (input.sourceDigest !== input.expectedDigest) {
|
||||
if (input.sourceDigest !== input.expectedDigest && !input.manifest) {
|
||||
const short = (digest: string) => digest.replace(/^sha256:/, "").slice(0, 12);
|
||||
throw new Error(`runner_remote_provider_artifact_incompatible: selected provider pack ${short(input.expectedDigest)} is not the controller's pack ${short(input.sourceDigest)}; install the matching Paperclip package and Daytona image`);
|
||||
}
|
||||
@@ -36,7 +33,7 @@ export async function uploadVerifiedProviderPack(input: {
|
||||
input.signal?.throwIfAborted();
|
||||
if (input.manifest && input.manifest.digest !== input.expectedDigest) throw Error("runner_remote_provider_source_invalid");
|
||||
const source = input.manifest ? await measure("provider_pack.source", () => resolveProviderPackUploadSource({
|
||||
root: input.root, manifest: input.manifest!, signal: input.signal,
|
||||
root: input.root, sourceDigest: input.sourceDigest, manifest: input.manifest!, signal: input.signal,
|
||||
})) : { root: input.root, archiveOnly: false };
|
||||
const verifyDirectory = () => measure("provider_pack.directory.verify", async () => input.verifyDirectory(source.root));
|
||||
// Archive installation reads the sealed archives, not the expanded tree.
|
||||
|
||||
@@ -148,7 +148,7 @@ async function fixture(options: { wrongCode?: boolean; unsafeOverlay?: boolean;
|
||||
}
|
||||
|
||||
function upload(input: Awaited<ReturnType<typeof fixture>>, extra: Partial<Parameters<typeof uploadVerifiedProviderPack>[0]> = {}) {
|
||||
return uploadVerifiedProviderPack({ root: input.current.pack, expectedDigest: input.selected.manifest.digest,
|
||||
return uploadVerifiedProviderPack({ root: input.current.pack, sourceDigest: input.current.manifest.digest, expectedDigest: input.selected.manifest.digest,
|
||||
manifest: input.selected.manifest, runner: input.runner, cwd: input.root, targetPath: input.targetPath,
|
||||
reuseRoots: [], verifyDirectory: () => { throw Error("current_directory_fallback_forbidden"); },
|
||||
stageDirectory: async () => { throw Error("current_directory_fallback_forbidden"); }, verifyUploaded: async () => {}, ...extra } as Parameters<typeof uploadVerifiedProviderPack>[0]);
|
||||
@@ -167,7 +167,7 @@ it("installs the exact retained pack through the real uploader on an empty host
|
||||
|
||||
it("keeps a matching current pack on its existing path without registry or cache reads", async () => {
|
||||
const input = await fixture();
|
||||
await upload(input, { root: input.selected.pack });
|
||||
await upload(input, { root: input.selected.pack, sourceDigest: input.selected.manifest.digest });
|
||||
expect(input.requests).toEqual([]);
|
||||
await expect(fs.stat(path.join(input.temporary, `paperclip-provider-pack-sources-${process.getuid?.()}`))).rejects.toMatchObject({ code: "ENOENT" });
|
||||
expect(await fs.readFile(path.join(input.targetPath, "bin/tool"), "utf8")).toBe("retained provider code");
|
||||
@@ -184,7 +184,15 @@ it("refuses a different historical code pack even when OCI source labels and all
|
||||
it("does not mask a corrupt matching current archive by looking up a different code source", async () => {
|
||||
const input = await fixture();
|
||||
await fs.writeFile(input.selected.pack + ".archive.json", "{");
|
||||
await expect(upload(input, { root: input.selected.pack })).rejects.toThrow("runner_remote_provider_archive_invalid");
|
||||
await expect(upload(input, { root: input.selected.pack, sourceDigest: input.selected.manifest.digest })).rejects.toThrow("runner_remote_provider_archive_invalid");
|
||||
expect(input.requests).toEqual([]);
|
||||
expect(input.runner.execute).not.toHaveBeenCalled();
|
||||
expect(input.runner.syncIn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("rejects a changed controller code identity before looking up a historical source", async () => {
|
||||
const input = await fixture();
|
||||
await expect(upload(input, { sourceDigest: input.selected.manifest.digest })).rejects.toThrow("runner_remote_provider_source_invalid");
|
||||
expect(input.requests).toEqual([]);
|
||||
expect(input.runner.execute).not.toHaveBeenCalled();
|
||||
expect(input.runner.syncIn).not.toHaveBeenCalled();
|
||||
|
||||
@@ -265,6 +265,7 @@ async function importSource(cache: string, pointer: string, manifest: ProviderPa
|
||||
|
||||
export async function resolveProviderPackUploadSource(input: {
|
||||
root: string;
|
||||
sourceDigest?: string;
|
||||
manifest: ProviderPackIdentity;
|
||||
signal?: AbortSignal;
|
||||
}): Promise<{ root: string; archiveOnly: boolean }> {
|
||||
@@ -272,7 +273,8 @@ export async function resolveProviderPackUploadSource(input: {
|
||||
if (manifest?.schema !== "paperclip-runner/remote-provider-pack/v1" || manifest.digest !== `sha256:${digest(canonicalProviderPackJson(manifest.payload))}`) fail();
|
||||
input.signal?.throwIfAborted();
|
||||
const configured = JSON.parse((await readStable(join(input.root, "provider-pack.json"))).toString()) as ProviderPackIdentity;
|
||||
if (configured?.schema !== manifest.schema || configured.digest !== `sha256:${digest(canonicalProviderPackJson(configured.payload))}`) fail();
|
||||
if (configured?.schema !== manifest.schema || configured.digest !== `sha256:${digest(canonicalProviderPackJson(configured.payload))}`
|
||||
|| input.sourceDigest !== undefined && input.sourceDigest !== configured.digest) fail();
|
||||
if (configured.digest === manifest.digest) return { root: input.root, archiveOnly: false };
|
||||
const cache = join(await realpath(tmpdir()), `paperclip-provider-pack-sources-${process.getuid?.()}`);
|
||||
await mkdir(cache, { recursive: true, mode: 0o700 });
|
||||
|
||||
Reference in new issue
Block a user