diff --git a/package.json b/package.json index b656816..3fd2eeb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "paperclip-adapter-opencode-k8s", - "version": "0.1.26", + "version": "0.1.27", "description": "Paperclip adapter plugin that runs OpenCode agents as Kubernetes Jobs", "license": "MIT", "type": "module", diff --git a/src/server/execute.test.ts b/src/server/execute.test.ts index 6f2f1e1..8d912c9 100644 --- a/src/server/execute.test.ts +++ b/src/server/execute.test.ts @@ -1185,3 +1185,30 @@ describe("ensureAgentDbPvc — unit", () => { expect(vi.mocked(getPvc)).toHaveBeenCalledWith(NAMESPACE, `opencode-db-${expectedSlug}`, undefined); }); }); + +describe("isK8s404", () => { + it("recognizes @kubernetes/client-node v1.x ApiException with code=404", async () => { + const { isK8s404 } = await import("./execute.js"); + const err = Object.assign(new Error("not found"), { code: 404 }); + expect(isK8s404(err)).toBe(true); + }); + + it("still recognizes legacy errors with statusCode=404", async () => { + const { isK8s404 } = await import("./execute.js"); + const err = Object.assign(new Error("not found"), { statusCode: 404 }); + expect(isK8s404(err)).toBe(true); + }); + + it("still recognizes errors with response.statusCode=404", async () => { + const { isK8s404 } = await import("./execute.js"); + const err = Object.assign(new Error("not found"), { response: { statusCode: 404 } }); + expect(isK8s404(err)).toBe(true); + }); + + it("returns false for non-404 errors", async () => { + const { isK8s404 } = await import("./execute.js"); + expect(isK8s404(Object.assign(new Error("server error"), { code: 500 }))).toBe(false); + expect(isK8s404(new Error("plain error"))).toBe(false); + expect(isK8s404(null)).toBe(false); + }); +}); diff --git a/src/server/execute.ts b/src/server/execute.ts index 34d3c16..c428d47 100644 --- a/src/server/execute.ts +++ b/src/server/execute.ts @@ -29,6 +29,8 @@ const LOG_EXIT_COMPLETION_GRACE_MS = parseInt(process.env.LOG_EXIT_COMPLETION_GR export function isK8s404(err: unknown): boolean { if (!(err instanceof Error)) return false; const asAny = err as unknown as Record; + // @kubernetes/client-node v1.x ApiException exposes HTTP status as `code`. + if (typeof asAny.code === "number" && asAny.code === 404) return true; if (typeof asAny.statusCode === "number" && asAny.statusCode === 404) return true; const resp = asAny.response as Record | undefined; if (typeof resp?.statusCode === "number" && resp.statusCode === 404) return true; diff --git a/src/server/k8s-client.ts b/src/server/k8s-client.ts index 4937920..af29928 100644 --- a/src/server/k8s-client.ts +++ b/src/server/k8s-client.ts @@ -178,6 +178,8 @@ export function resetCache(): void { function isNotFound(err: unknown): boolean { if (!(err instanceof Error)) return false; const asAny = err as unknown as Record; + // @kubernetes/client-node v1.x ApiException exposes HTTP status as `code`. + if (typeof asAny.code === "number" && asAny.code === 404) return true; if (typeof asAny.statusCode === "number" && asAny.statusCode === 404) return true; const resp = asAny.response as Record | undefined; return typeof resp?.statusCode === "number" && resp.statusCode === 404;