diff --git a/packages/adapters/openclaw/src/server/execute.ts b/packages/adapters/openclaw/src/server/execute.ts index 1a77b928..dca12b90 100644 --- a/packages/adapters/openclaw/src/server/execute.ts +++ b/packages/adapters/openclaw/src/server/execute.ts @@ -146,6 +146,7 @@ type WakePayload = { }; function buildWakeText(payload: WakePayload, paperclipEnv: Record): string { + const claimedApiKeyPath = "~/.openclaw/workspace/paperclip-claimed-api-key.json"; const orderedKeys = [ "PAPERCLIP_RUN_ID", "PAPERCLIP_AGENT_ID", @@ -171,6 +172,9 @@ function buildWakeText(payload: WakePayload, paperclipEnv: Record`, + "", + `Load PAPERCLIP_API_KEY from ${claimedApiKeyPath} (the token you saved after claim-api-key).`, "", `task_id=${payload.taskId ?? ""}`, `issue_id=${payload.issueId ?? ""}`, diff --git a/server/src/__tests__/invite-onboarding-text.test.ts b/server/src/__tests__/invite-onboarding-text.test.ts index 914f092e..6f22c2d5 100644 --- a/server/src/__tests__/invite-onboarding-text.test.ts +++ b/server/src/__tests__/invite-onboarding-text.test.ts @@ -49,6 +49,9 @@ describe("buildInviteOnboardingTextDocument", () => { expect(text).toContain("You MUST include agentDefaultsPayload.headers.x-openclaw-auth"); expect(text).toContain("will fail with 401 Unauthorized"); expect(text).toContain("set the first reachable candidate as agentDefaultsPayload.paperclipApiUrl"); + expect(text).toContain("~/.openclaw/workspace/paperclip-claimed-api-key.json"); + expect(text).toContain("PAPERCLIP_API_KEY"); + expect(text).toContain("saved token field"); }); it("includes loopback diagnostics for authenticated/private onboarding", () => { diff --git a/server/src/__tests__/openclaw-adapter.test.ts b/server/src/__tests__/openclaw-adapter.test.ts index 9922c85c..c8c38900 100644 --- a/server/src/__tests__/openclaw-adapter.test.ts +++ b/server/src/__tests__/openclaw-adapter.test.ts @@ -196,6 +196,8 @@ describe("openclaw adapter execute", () => { expect(text).toContain("PAPERCLIP_TASK_ID=task-123"); expect(text).toContain("PAPERCLIP_WAKE_REASON=issue_assigned"); expect(text).toContain("PAPERCLIP_LINKED_ISSUE_IDS=issue-123"); + expect(text).toContain("PAPERCLIP_API_KEY="); + expect(text).toContain("Load PAPERCLIP_API_KEY from ~/.openclaw/workspace/paperclip-claimed-api-key.json"); }); it("uses paperclipApiUrl override when provided", async () => { @@ -379,6 +381,7 @@ describe("openclaw adapter execute", () => { expect(body.model).toBe("openclaw"); expect(typeof body.input).toBe("string"); expect(String(body.input)).toContain("PAPERCLIP_RUN_ID=run-123"); + expect(String(body.input)).toContain("PAPERCLIP_API_KEY="); expect(body.metadata).toBeTypeOf("object"); expect((body.metadata as Record).PAPERCLIP_RUN_ID).toBe("run-123"); expect(body.text).toBeUndefined(); diff --git a/server/src/routes/access.ts b/server/src/routes/access.ts index 4eda1ad7..ebf86ee1 100644 --- a/server/src/routes/access.ts +++ b/server/src/routes/access.ts @@ -662,7 +662,7 @@ function buildInviteOnboardingManifest( invite: toInviteSummaryResponse(req, token, invite), onboarding: { instructions: - "Join as an OpenClaw agent, save your one-time claim secret, wait for board approval, then claim your API key and install the Paperclip skill before starting heartbeat loops. You MUST include agentDefaultsPayload.headers.x-openclaw-auth in your join request so Paperclip can authenticate callback requests.", + "Join as an OpenClaw agent, save your one-time claim secret, wait for board approval, then claim your API key. Save the claim response token to ~/.openclaw/workspace/paperclip-claimed-api-key.json and load PAPERCLIP_API_KEY from that file before starting heartbeat loops. You MUST include agentDefaultsPayload.headers.x-openclaw-auth in your join request so Paperclip can authenticate callback requests.", inviteMessage: extractInviteMessage(invite), recommendedAdapterType: "openclaw", requiredFields: { @@ -805,6 +805,10 @@ export function buildInviteOnboardingTextDocument( ' "claimSecret": ""', "}", "", + "On successful claim, save the full JSON response to:", + "- ~/.openclaw/workspace/paperclip-claimed-api-key.json", + "Then set PAPERCLIP_API_KEY from the saved token field for every heartbeat run.", + "", "Important:", "- claim secrets expire", "- claim secrets are single-use",