From c9677df9f89aa72e7c98332f60ae263b753f15be Mon Sep 17 00:00:00 2001 From: Hussain Nagaria Date: Thu, 2 Jul 2026 21:44:54 +0530 Subject: [PATCH] feat: box Claude auth via subscription OAuth token (setup-token) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a "Claude Code OAuth Token" (Password) field to Hive Settings and forwards it to boxes as CLAUDE_CODE_OAUTH_TOKEN in build_boot_env — an alternative to ANTHROPIC_API_KEY for when API access isn't available. Generate it with `claude setup-token` (uses a Claude subscription, not API billing). The box needs no change: devbox-init exports any boot_env key into the shell, and claude falls back to the OAuth token when ANTHROPIC_API_KEY is empty. The token is forwarded only when no API key is set, so the two auth modes never conflict — flip to the API key later by filling anthropic_api_key and clearing this. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_011C9jzHhfxDqtU13uWtazZm --- .../bwh_hive/doctype/hive_settings/hive_settings.json | 9 ++++++++- bwh_hive/bwh_hive/orchestrator/service.py | 10 +++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/bwh_hive/bwh_hive/doctype/hive_settings/hive_settings.json b/bwh_hive/bwh_hive/doctype/hive_settings/hive_settings.json index c8bcfe0..a4e4573 100644 --- a/bwh_hive/bwh_hive/doctype/hive_settings/hive_settings.json +++ b/bwh_hive/bwh_hive/doctype/hive_settings/hive_settings.json @@ -26,6 +26,7 @@ "default_agent_template_slug", "skills_repo", "anthropic_api_key", + "claude_code_oauth_token", "agent_prompts_section", "agent_spec_prompt", "agent_implement_prompt", @@ -170,11 +171,17 @@ "label": "Skills Repo" }, { - "description": "Injected into boxes as ANTHROPIC_API_KEY for Claude Code.", + "description": "Injected into boxes as ANTHROPIC_API_KEY for Claude Code (API billing).", "fieldname": "anthropic_api_key", "fieldtype": "Password", "label": "Anthropic API Key" }, + { + "description": "Claude subscription token from `claude setup-token`. Injected as CLAUDE_CODE_OAUTH_TOKEN. Used only when no Anthropic API Key is set — lets boxes auth via a Claude subscription instead of API billing.", + "fieldname": "claude_code_oauth_token", + "fieldtype": "Password", + "label": "Claude Code OAuth Token" + }, { "collapsible": 1, "fieldname": "agent_prompts_section", diff --git a/bwh_hive/bwh_hive/orchestrator/service.py b/bwh_hive/bwh_hive/orchestrator/service.py index a3673d7..973aa29 100644 --- a/bwh_hive/bwh_hive/orchestrator/service.py +++ b/bwh_hive/bwh_hive/orchestrator/service.py @@ -120,6 +120,13 @@ def build_boot_env(task: Document) -> dict: spec_run_timeout = max(spec_min - 5, 5) * 60 impl_run_timeout = max(impl_min - 5, 5) * 60 + # Claude auth: an Anthropic API key (API billing) OR a subscription OAuth token from + # `claude setup-token`. The box's claude falls back to the OAuth token when + # ANTHROPIC_API_KEY is empty, so only forward the token when no API key is set — + # never both, to avoid an ambiguous auth mode. + anthropic_api_key = settings.get_password("anthropic_api_key", raise_exception=False) or "" + oauth_token = settings.get_password("claude_code_oauth_token", raise_exception=False) or "" + env = { "AGENT_MODE": "1", "HIVE_BASE_URL": frappe.utils.get_url(), @@ -134,7 +141,8 @@ def build_boot_env(task: Document) -> dict: "TARGET_APP_REPO": project.get("target_app_repo") or "", "TARGET_APP_BRANCH": project.get("target_app_branch") or "develop", "SKILLS_REPO": skills_repo or "", - "ANTHROPIC_API_KEY": settings.get_password("anthropic_api_key", raise_exception=False) or "", + "ANTHROPIC_API_KEY": anthropic_api_key, + "CLAUDE_CODE_OAUTH_TOKEN": oauth_token if not anthropic_api_key else "", "SPEC_RUN_TIMEOUT": spec_run_timeout, "IMPL_RUN_TIMEOUT": impl_run_timeout, }