From 38b3916e112b7fc65e630be37e2f1cf300e21de7 Mon Sep 17 00:00:00 2001 From: Chris Farhood Date: Sat, 25 Apr 2026 14:36:11 +0000 Subject: [PATCH] fix: support PAPERCLIP_DEV_API_KEY for external cancel polling External cancel polling in execute.ts used PAPERCLIP_API_KEY which is a short-lived run JWT for the main Paperclip instance. In multi-instance setups (dev vs main), the agent runs on the dev instance but the run JWT is only valid on the main instance, causing 401 on every poll. Now polls with PAPERCLIP_DEV_API_KEY if set, falling back to PAPERCLIP_API_KEY. The dev key is inherited through job-manifest.ts from the pod's inherited env. Co-Authored-By: Paperclip --- src/server/execute.ts | 5 ++++- src/server/job-manifest.ts | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/server/execute.ts b/src/server/execute.ts index 7b9ba36..cbdfb41 100644 --- a/src/server/execute.ts +++ b/src/server/execute.ts @@ -544,8 +544,11 @@ async function streamAndAwaitJob( await new Promise((resolve) => setTimeout(resolve, KEEPALIVE_INTERVAL_MS)); if (logStopSignal.stopped || cancelSignal.cancelled) break; try { + // Prefer PAPERCLIP_DEV_API_KEY if set (allows dev instance key to be + // distinct from the main-instance run JWT in PAPERCLIP_API_KEY). + const apiKey = process.env.PAPERCLIP_DEV_API_KEY ?? process.env.PAPERCLIP_API_KEY ?? ""; const resp = await fetch(`${apiUrl}/api/issues/${issueId}`, { - headers: { Authorization: `Bearer ${process.env.PAPERCLIP_API_KEY ?? ""}` }, + headers: { Authorization: `Bearer ${apiKey}` }, }); if (resp.ok) { const data = await resp.json() as { status?: string }; diff --git a/src/server/job-manifest.ts b/src/server/job-manifest.ts index c74fb6c..2e31dac 100644 --- a/src/server/job-manifest.ts +++ b/src/server/job-manifest.ts @@ -173,6 +173,12 @@ function buildEnvVars( if (selfPod.inheritedEnv.PAPERCLIP_API_URL) { paperclipEnv.PAPERCLIP_API_URL = selfPod.inheritedEnv.PAPERCLIP_API_URL; } + // Inherit PAPERCLIP_DEV_API_KEY if set (dev-instance key, distinct from the + // main-instance run JWT in PAPERCLIP_API_KEY). Used by the external cancel + // polling in execute.ts to authenticate against the dev Paperclip instance. + if (selfPod.inheritedEnv.PAPERCLIP_DEV_API_KEY) { + paperclipEnv.PAPERCLIP_DEV_API_KEY = selfPod.inheritedEnv.PAPERCLIP_DEV_API_KEY; + } // Layer 3: Inherited from Deployment (Bedrock, API keys, etc.) const merged: Record = {