From abbe47b1b9b3b2ec5541e451bbe84a24b70e6481 Mon Sep 17 00:00:00 2001 From: Daniel Samson <12231216+daniel-samson@users.noreply.github.com> Date: Sun, 12 Apr 2026 14:52:01 +0100 Subject: [PATCH] Support Claude Code Max via OAuth token Pass CLAUDE_CODE_OAUTH_TOKEN to workspaces so they use the Max subscription instead of pay-per-use API credits. Falls back to ANTHROPIC_API_KEY if OAuth token not set. Co-Authored-By: Claude Opus 4.6 --- coder/cloudflare-worker/main.tf | 17 ++++++++++++++++- src/config.ts | 7 +++++-- src/services/coder.ts | 3 +++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/coder/cloudflare-worker/main.tf b/coder/cloudflare-worker/main.tf index cceb92f..d409d91 100644 --- a/coder/cloudflare-worker/main.tf +++ b/coder/cloudflare-worker/main.tf @@ -196,6 +196,15 @@ data "coder_parameter" "callback_url" { mutable = false } +data "coder_parameter" "claude_oauth_token" { + name = "claude_oauth_token" + display_name = "Claude OAuth Token" + description = "OAuth token for Claude Code Max subscription" + type = "string" + default = "" + mutable = false +} + data "coder_parameter" "deploy_env" { name = "deploy_env" display_name = "Deploy Environment" @@ -414,7 +423,8 @@ resource "coder_agent" "main" { } env = { - ANTHROPIC_API_KEY = data.coder_parameter.anthropic_api_key.value + ANTHROPIC_API_KEY = data.coder_parameter.anthropic_api_key.value + CLAUDE_CODE_OAUTH_TOKEN = data.coder_parameter.claude_oauth_token.value GITHUB_TOKEN = data.coder_external_auth.github.access_token GITEA_TOKEN = data.coder_parameter.gitea_token.value GITEA_ORG = data.coder_parameter.gitea_org.value @@ -576,6 +586,11 @@ resource "kubernetes_deployment_v1" "workspace" { value = data.coder_parameter.anthropic_api_key.value } + env { + name = "CLAUDE_CODE_OAUTH_TOKEN" + value = data.coder_parameter.claude_oauth_token.value + } + env { name = "GITHUB_TOKEN" value = data.coder_external_auth.github.access_token diff --git a/src/config.ts b/src/config.ts index d8388f0..9a01808 100644 --- a/src/config.ts +++ b/src/config.ts @@ -11,8 +11,10 @@ export interface Config { giteaDevToken: string; /** Gitea API token for the review bot account */ giteaReviewToken: string; - /** Anthropic API key passed to Claude Code in workspaces */ + /** Anthropic API key passed to Claude Code in workspaces (fallback) */ anthropicApiKey: string; + /** Claude Code OAuth token for Max subscription (preferred) */ + claudeOauthToken: string; /** Username of the dev bot (to filter out self-replies) */ botDevUsername: string; /** Username of the review bot (to filter out self-replies) */ @@ -41,7 +43,8 @@ export function loadConfig(): Config { coderTemplateId: required("CODER_TEMPLATE_ID"), giteaDevToken: required("GITEA_DEV_TOKEN"), giteaReviewToken: required("GITEA_REVIEW_TOKEN"), - anthropicApiKey: required("ANTHROPIC_API_KEY"), + anthropicApiKey: process.env.ANTHROPIC_API_KEY || "", + claudeOauthToken: process.env.CLAUDE_OAUTH_TOKEN || "", botDevUsername: process.env.BOT_DEV_USERNAME || "claude-dev", botReviewUsername: process.env.BOT_REVIEW_USERNAME || "claude-review", giteaUrl: process.env.GITEA_URL || "https://gitea.samson.media", diff --git a/src/services/coder.ts b/src/services/coder.ts index fc3b319..d20a898 100644 --- a/src/services/coder.ts +++ b/src/services/coder.ts @@ -7,6 +7,7 @@ export class CoderClient { private token: string; private templateId: string; private anthropicApiKey: string; + private claudeOauthToken: string; private callbackUrl: string; constructor(config: Config) { @@ -14,6 +15,7 @@ export class CoderClient { this.token = config.coderToken; this.templateId = config.coderTemplateId; this.anthropicApiKey = config.anthropicApiKey; + this.claudeOauthToken = config.claudeOauthToken; this.callbackUrl = config.callbackUrl; } @@ -31,6 +33,7 @@ export class CoderClient { { name: "gitea_org", value: task.giteaOrg }, { name: "gitea_repo", value: task.giteaRepo }, { name: "anthropic_api_key", value: this.anthropicApiKey }, + { name: "claude_oauth_token", value: this.claudeOauthToken }, { name: "callback_url", value: `${this.callbackUrl}/webhook/task-complete/${name}` }, ...(task.deployEnv ? [{ name: "deploy_env", value: task.deployEnv }]