feat(manifest): add agent_provider.auth_token for Claude OAuth via egress
Operators can now declare:
agent_provider:
template: claude
auth_token: BOT_BOTTLE_CLAUDE_OAUTH_TOKEN
and the provisioner injects a provider-owned api.anthropic.com egress
route (Bearer, tls_passthrough) rather than requiring a manually
declared route with the former claude_code_oauth role.
Changes:
- Add auth_token field to AgentProvider; validate claude-only.
- Remove claude_code_oauth from EGRESS_ROLES / PROVIDER_EGRESS_ROLES.
Manifests that declare the role now fail at parse time with "unknown
role" — the provisioner owns the route.
- agent_provision_plan: replace manifest_egress_routes/has_provider_auth
with auth_token; Claude branch injects the api.anthropic.com route,
placeholder env, and nonessential-traffic flags when auth_token is set.
- Add hidden_env_names: frozenset[str] to AgentProvisionPlan; Claude
branch populates it with CLAUDE_CODE_OAUTH_TOKEN.
- Remove auth_role from AgentProviderRuntime and placeholder_env_for().
- print_util.visible_agent_env_names: accept hidden_env_names from the
plan instead of dispatching on agent_provider_template.
- Both backends: drop manifest_egress_routes call, pass auth_token.
- PRD 0029 rescoped to cover both Codex and Claude provider auth.
Assisted-by: Claude Code
This commit is contained in:
@@ -13,7 +13,7 @@ from bot_bottle.agent_provider import (
|
||||
agent_provision_plan,
|
||||
runtime_for,
|
||||
)
|
||||
from bot_bottle.egress import CODEX_HOST_CREDENTIAL_TOKEN_REF, EgressRoute
|
||||
from bot_bottle.egress import CODEX_HOST_CREDENTIAL_TOKEN_REF
|
||||
|
||||
|
||||
def _jwt(exp: int) -> str:
|
||||
@@ -24,14 +24,6 @@ def _jwt(exp: int) -> str:
|
||||
|
||||
|
||||
class TestAgentProviderRuntime(unittest.TestCase):
|
||||
def test_claude_has_auth_role(self):
|
||||
runtime = runtime_for("claude")
|
||||
self.assertEqual("claude_code_oauth", runtime.auth_role)
|
||||
|
||||
def test_codex_has_no_auth_role(self):
|
||||
runtime = runtime_for("codex")
|
||||
self.assertEqual("", runtime.auth_role)
|
||||
|
||||
def test_codex_plan_declares_home_state(self):
|
||||
with tempfile.TemporaryDirectory(prefix="bb-provider.") as tmp:
|
||||
plan = agent_provision_plan(
|
||||
@@ -79,20 +71,24 @@ class TestAgentProviderRuntime(unittest.TestCase):
|
||||
self.assertEqual(1, len(plan.verify))
|
||||
self.assertIn("CODEX_HOME=/run/codex-home", plan.verify[0].argv)
|
||||
|
||||
def test_claude_with_provider_auth_sets_placeholder_and_disables_nonessential_traffic(self):
|
||||
def test_claude_with_auth_token_injects_provider_route_and_placeholder(self):
|
||||
with tempfile.TemporaryDirectory(prefix="bb-provider.") as tmp:
|
||||
plan = agent_provision_plan(
|
||||
template="claude",
|
||||
dockerfile="/tmp/Dockerfile.claude",
|
||||
state_dir=Path(tmp),
|
||||
manifest_egress_routes=(EgressRoute(
|
||||
host="api.anthropic.com",
|
||||
roles=("claude_code_oauth",),
|
||||
),),
|
||||
auth_token="BOT_BOTTLE_CLAUDE_OAUTH_TOKEN",
|
||||
)
|
||||
self.assertEqual(1, len(plan.egress_routes))
|
||||
route = plan.egress_routes[0]
|
||||
self.assertEqual("api.anthropic.com", route.host)
|
||||
self.assertEqual("Bearer", route.auth_scheme)
|
||||
self.assertEqual("BOT_BOTTLE_CLAUDE_OAUTH_TOKEN", route.token_ref)
|
||||
self.assertTrue(route.tls_passthrough)
|
||||
self.assertEqual("egress-placeholder", plan.env_vars["CLAUDE_CODE_OAUTH_TOKEN"])
|
||||
self.assertEqual("1", plan.env_vars["CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC"])
|
||||
self.assertEqual("1", plan.env_vars["DISABLE_ERROR_REPORTING"])
|
||||
self.assertEqual(frozenset({"CLAUDE_CODE_OAUTH_TOKEN"}), plan.hidden_env_names)
|
||||
|
||||
def test_codex_forward_host_credentials_populates_egress_routes(self):
|
||||
with tempfile.TemporaryDirectory(prefix="bb-provider.") as tmp:
|
||||
|
||||
Reference in New Issue
Block a user