Skip to content

Added claude setup-token workflow to generate long lived tokens#66

Merged
nezhar merged 1 commit intomainfrom
issue-27
Apr 21, 2026
Merged

Added claude setup-token workflow to generate long lived tokens#66
nezhar merged 1 commit intomainfrom
issue-27

Conversation

@nezhar
Copy link
Copy Markdown
Collaborator

@nezhar nezhar commented Apr 21, 2026

Introduces a robust workaround for Claude Code's OAuth token refresh bug by supporting long-lived tokens, adds a new diagnostic doctor command for inspecting agent authentication state, and improves the CLI's flexibility and usability. The most significant changes are grouped below.

Claude OAuth Long-lived Token Support:

  • Implements detection, storage, and automatic injection of a long-lived Claude OAuth token (oauth-token file) as CLAUDE_CODE_OAUTH_TOKEN, bypassing the upstream refresh bug and reducing the need for frequent re-logins. [1] [2]
  • Updates documentation to explain the token workaround, setup steps, caveats, and upstream bug history with references to official Anthropic docs and GitHub issues.

CLI and Command Improvements:

  • Adds a new doctor subcommand to the CLI for inspecting agent authentication and config state, with detailed output for Claude credentials, stored tokens, environment overrides, and effective auth mode. [1] [2]
  • Enhances vp run and its agent aliases to forward extra arguments, enabling flows like vp run claude setup-token. [1] [2] [3] [4]

These changes make authentication with Claude more reliable.

Summary by CodeRabbit

  • New Features

    • Added vp doctor to inspect and report Claude authentication status.
    • Added vp run claude setup-token one-time flow and automatic token injection on subsequent runs.
    • CLI agent aliases now forward extra/passthrough arguments to runs.
  • Documentation

    • Added comprehensive guide for token setup, verification, precedence, caveats, and troubleshooting.
  • Tests

    • Added tests for token persistence, CLI passthrough behavior, and doctor checks.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 21, 2026

📝 Walkthrough

Walkthrough

Adds persistent host-side Claude long-lived OAuth token support with secure storage and auto-injection, a new vp doctor claude diagnostic command to inspect auth state, CLI changes to forward passthrough args for agent aliases, updated run logic to capture/persist tokens, and accompanying docs and tests.

Changes

Cohort / File(s) Summary
CLI Infrastructure
src/vibepod/cli.py
Registered doctor subcommand; enabled context_settings={"allow_extra_args": True, "ignore_unknown_options": True} on run and hidden agent aliases; changed alias factory to forward explicit options (workspace/pull/detach/env/name/network/paste-images/ikwid) and pass agent name to run.
Doctor Diagnostic Command
src/vibepod/commands/doctor.py
New vp doctor claude command that inspects agent config dir, reads/parses .credentials.json and .claude.json, reports access/refresh/token fields, checks oauth-token file, scans host envs (ANTHROPIC_API_KEY, CLAUDE_CODE_OAUTH_TOKEN, CLAUDE_CONFIG_DIR), determines effective auth mode, prints masked values, and exits with code 2 when creds are expired and no fallback token exists.
Token Storage & Run Logic
src/vibepod/commands/run.py
Added helpers to compute/read/write oauth-token under agent config dir with 0600 perms; run() now captures passthrough args from Click context and appends them to container command; when appropriate injects stored token as CLAUDE_CODE_OAUTH_TOKEN (unless env override or ANTHROPIC_API_KEY present); implements interactive claude setup-token post-run masked capture and persistence; includes robust read/write warnings/fallbacks.
Documentation
docs/agents/index.md
Added Claude long-lived token documentation: vp run claude setup-token flow, token file location ~/.config/vibepod/agents/claude/oauth-token (mode 0600), precedence order, verification steps (vp doctor claude, file inspection), limitations, rotation notice, and rationale for the workaround.
Token Storage Tests
tests/test_claude_token.py
New tests validating _read_claude_stored_token/_write_claude_stored_token: missing file -> None, trimming/normalization on write, trailing newline in file, whitespace-only file treated as None, filename is oauth-token, and permissions forced to 600 (skipped where fchmod unavailable).
CLI & Alias Tests
tests/test_cli.py
Updated test_full_agent_name_alias_runs_agent to capture passthrough args (ctx.args); added test_alias_forwards_extra_args asserting alias forwards extra args (e.g., ["claude","setup-token"]) into Click context.
Doctor Command Tests
tests/test_doctor.py
New Typer CLI smoke tests covering missing config dir, valid/non‑expired creds, expired creds with/without stored token and expected exit codes (including 2 for expired-no-fallback), missing refreshToken reporting, stored-token and host-env modes, and output markers.
Config Test Adjustment
tests/test_config.py
test_default_config_includes_llm_section now runs in isolated cwd and clears LLM-related env vars before calling get_config() to ensure defaults are used.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI as vp CLI
    participant Container as Claude Container
    participant FS as File System (~/.config/vibepod/...)
    
    User->>CLI: vp run claude setup-token
    CLI->>Container: Launch container (interactive)
    Container->>User: Display long-lived token
    Container-->>CLI: Exit
    CLI->>User: Prompt "Paste token:" (masked)
    User->>CLI: Paste token
    CLI->>FS: write oauth-token (0600)
    FS-->>CLI: File created
    CLI->>User: Confirm saved
Loading
sequenceDiagram
    participant User
    participant CLI as vp CLI
    participant FS as File System (~/.config/vibepod/...)
    participant Container as Claude Container
    participant Env as Container Env
    
    User->>CLI: vp run claude -p "say ok"
    CLI->>FS: read oauth-token (if no host env)
    FS-->>CLI: token or None
    CLI->>CLI: Determine effective auth source (CLI env > agent config env > stored token > interactive creds)
    alt Token available
        CLI->>Env: set CLAUDE_CODE_OAUTH_TOKEN=token
    else ANTHROPIC_API_KEY present
        CLI->>Env: set ANTHROPIC_API_KEY=...
    end
    CLI->>Container: Launch with merged_env + passthrough args
    Container->>Env: Read auth env
    Container-->>User: Run command
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hid a token in a cozy nest,
Typed setup, then gave it my best—
A doctor checks and keeps it right,
Saved with care in the soft moonlight.
Hooray for tokens, snug and bright! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding a Claude setup-token workflow for long-lived tokens, which is the primary feature introduced across multiple files in this PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-27

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@augmentcode
Copy link
Copy Markdown

augmentcode Bot commented Apr 21, 2026

🤖 Augment PR Summary

Summary: Adds a long-lived Claude Code OAuth token workflow (to bypass upstream refresh issues), plus diagnostics and CLI passthrough improvements.

Changes:

  • Enhances vp run to forward trailing/passthrough arguments into the container command (e.g. vp run claude setup-token).
  • Adds hidden agent shortcut commands that mirror vp run <agent> options while still allowing passthrough args.
  • Implements host-side storage of a Claude long-lived token in ~/.config/vibepod/agents/claude/oauth-token and auto-injects it as CLAUDE_CODE_OAUTH_TOKEN on runs.
  • Captures the token after a successful claude setup-token flow and persists it with restricted permissions.
  • Introduces vp doctor claude to inspect host-side Claude credential files, stored token presence, and report an “effective auth mode”.
  • Adds docs describing setup, precedence rules, caveats, and upstream bug context; adds tests covering token persistence, doctor exits, and alias passthrough.

Technical Notes: Uses Click/Typer extra-args support to preserve unknown flags for forwarding, and adds logic to avoid injecting stored tokens during setup-token runs.

🤖 Was this summary useful? React with 👍 or 👎

Copy link
Copy Markdown

@augmentcode augmentcode Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed. 3 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

Comment thread src/vibepod/commands/run.py Outdated
Comment thread src/vibepod/commands/run.py
Comment thread src/vibepod/commands/doctor.py
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/vibepod/commands/run.py (2)

3-29: ⚠️ Potential issue | 🟡 Minor

Fix ruff I001 import ordering to unblock CI.

The CI pipeline flagged the import block as unsorted. Running ruff check --fix (or ruff format) locally should resolve it.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/vibepod/commands/run.py` around lines 3 - 29, The import block at the top
(starting with "from __future__ import annotations" and including json, os, sys,
time, datetime, pathlib.Path, typing.Annotated, typer, rich.prompt
Confirm/Prompt and the vibepod.* imports) is unsorted and triggers ruff I001;
reorder imports into standard-library, third-party, and local groups and
alphabetize names within each group (preserve the future import first), or
simply run "ruff check --fix" / "ruff format" to apply the correct ordering
automatically so functions like agent_config_dir, DockerManager, SessionLogger,
and console helpers remain available in the same scope.

539-579: ⚠️ Potential issue | 🟡 Minor

setup-token prompt is skipped when --detach is used.

If a user runs vp run claude setup-token -d (or sets detach defaults elsewhere), the function returns at line 541 before reaching _capture_claude_setup_token, so the token is never captured. The interactive claude setup-token flow fundamentally needs a terminal — consider either rejecting --detach with setup-token up front, or warning the user that detach disables token capture.

🛠️ Suggested guard
     if detach:
+        if selected_agent == "claude" and "setup-token" in passthrough_args:
+            warning(
+                "`setup-token` needs an interactive terminal to capture the token; "
+                "ignoring --detach and attaching."
+            )
+        else:
             success(f"Started {container.name}")
             return
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/vibepod/commands/run.py` around lines 539 - 579, The early return when
detach is true skips the Claude setup-token flow; before returning on detach,
check if selected_agent == "claude" and "setup-token" in passthrough_args and if
so, refuse detach (or warn and force attach): replace the simple detach return
with a guard that either raises/prints an error and exits (so users cannot run
`--detach` with the interactive `claude setup-token`), or logs a warning and
sets detach=False so the code continues to call _capture_claude_setup_token;
adjust the block around variables detach, selected_agent, passthrough_args,
container.name and the return to implement this guard.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/agents/index.md`:
- Around line 281-283: The fenced code block containing
"~/.config/vibepod/agents/claude/oauth-token   (mode 0600)" is missing a
language tag; update the opening triple-backtick to include "text" (i.e., change
``` to ```text) so the block satisfies Markdownlint MD040 and renders correctly.

In `@src/vibepod/cli.py`:
- Around line 31-41: The alias handler _alias in _register_run_alias currently
has no parameters so Typer doesn't create option parsers; update _alias (or
extract a shared signature) to mirror the public parameters of run.run (e.g.,
workspace, pull, detach, env, name, network, paste_images, ikwid) using
typer.Option/Annotated signatures so Typer generates the same CLI flags, then
call run.run(...) with agent=agent_name and forward those parameters;
alternatively factor the parameter list into a shared helper used by both
run.run and _alias to avoid duplication.

In `@src/vibepod/commands/doctor.py`:
- Around line 11-16: Remove the unused imports to fix the ruff F401 CI failure:
delete Annotated from the typing import list and remove the unused symbol info
from the vibepod.utils.console import list so the import line becomes only the
actually used names (e.g., keep Any if used, and keep console, error, success,
warning). Update the import statements referencing typing and
vibepod.utils.console accordingly in the module where agent_config_dir and typer
are used (look for the top-level imports of Annotated and info).

In `@src/vibepod/commands/run.py`:
- Around line 51-56: The token file is created with default permissive mode by
Path.write_text before chmod; change _write_claude_stored_token to create and
write the file atomically with restricted permissions from the start: ensure the
parent directory exists (call
_claude_stored_token_path(config_dir).parent.mkdir(...)) then open the file
using os.open with flags os.O_WRONLY|os.O_CREAT|os.O_TRUNC and mode 0o600, wrap
the returned fd with os.fdopen and write token.strip() + "\n" using UTF-8, close
the file and return the Path; remove the separate os.chmod call since the file
is created with the correct mode.

In `@tests/test_doctor.py`:
- Around line 122-129: The test test_doctor_reports_host_env_mode should
explicitly isolate the environment so it exercises the CLAUDE_CODE_OAUTH_TOKEN
branch: in the test, after monkeypatching agent_config_dir (and before invoking
runner.invoke for app), remove or unset ANTHROPIC_API_KEY from the environment
(via monkeypatch.delenv or setting it to an empty value) and ensure
CLAUDE_CODE_OAUTH_TOKEN is set; this guarantees doctor.py's branch that checks
CLAUDE_CODE_OAUTH_TOKEN is executed rather than the ANTHROPIC_API_KEY branch.

---

Outside diff comments:
In `@src/vibepod/commands/run.py`:
- Around line 3-29: The import block at the top (starting with "from __future__
import annotations" and including json, os, sys, time, datetime, pathlib.Path,
typing.Annotated, typer, rich.prompt Confirm/Prompt and the vibepod.* imports)
is unsorted and triggers ruff I001; reorder imports into standard-library,
third-party, and local groups and alphabetize names within each group (preserve
the future import first), or simply run "ruff check --fix" / "ruff format" to
apply the correct ordering automatically so functions like agent_config_dir,
DockerManager, SessionLogger, and console helpers remain available in the same
scope.
- Around line 539-579: The early return when detach is true skips the Claude
setup-token flow; before returning on detach, check if selected_agent ==
"claude" and "setup-token" in passthrough_args and if so, refuse detach (or warn
and force attach): replace the simple detach return with a guard that either
raises/prints an error and exits (so users cannot run `--detach` with the
interactive `claude setup-token`), or logs a warning and sets detach=False so
the code continues to call _capture_claude_setup_token; adjust the block around
variables detach, selected_agent, passthrough_args, container.name and the
return to implement this guard.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d7acdb54-f564-4cca-94bd-441b3ffeecbc

📥 Commits

Reviewing files that changed from the base of the PR and between 48ae72d and 1df301a.

📒 Files selected for processing (7)
  • docs/agents/index.md
  • src/vibepod/cli.py
  • src/vibepod/commands/doctor.py
  • src/vibepod/commands/run.py
  • tests/test_claude_token.py
  • tests/test_cli.py
  • tests/test_doctor.py

Comment thread docs/agents/index.md Outdated
Comment thread src/vibepod/cli.py
Comment thread src/vibepod/commands/doctor.py Outdated
Comment thread src/vibepod/commands/run.py
Comment thread tests/test_doctor.py
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
src/vibepod/commands/doctor.py (2)

66-222: LGTM — diagnostic is thorough and exit-code semantics are well-defined.

Path (cfg_dir / "oauth-token") matches the constant used by run.py (CLAUDE_TOKEN_FILENAME); precedence in the "Effective auth mode" section mirrors the actual auto-injection logic in run.py (lines 345–354). The exit-code 2 gate correctly avoids false positives when a stored token or host env override would still authenticate the next run.

One small consideration: cfg_dir / "oauth-token" is duplicated here instead of importing CLAUDE_TOKEN_FILENAME / _claude_stored_token_path from run.py. Minor drift risk if the filename ever changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/vibepod/commands/doctor.py` around lines 66 - 222, The cfg_dir /
"oauth-token" literal is duplicated instead of reusing the canonical
constant/helper from run.py; replace the hard-coded filename with the shared
symbol used by run.py (import CLAUDE_TOKEN_FILENAME or _claude_stored_token_path
from run.py) and use that symbol when constructing stored_path and any related
checks so the doctor command always matches run.py's CLAUDE token
filename/logic.

39-54: Minor: EXPIRED Xm ago is unfriendly for long-expired tokens.

If a token expired several days ago, the output is "EXPIRED 4320m ago" rather than "3d ago". Consider reusing the same tiered formatter used in _format_mtime for consistency. Non-blocking.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/vibepod/commands/doctor.py` around lines 39 - 54, The expiry formatter
_format_expiry currently shows long expirations as minutes (e.g., "EXPIRED 4320m
ago"); update _format_expiry to reuse the same tiered human-friendly formatting
used by _format_mtime (days/hours/minutes) so expired durations are rendered
like "3d ago" or "2h 5m ago" instead of large minute counts; implement this by
computing minutes = abs(delta_ms) // 60000 for negative deltas and using the
same branch logic (minutes < 60, <1440, else) to build the "EXPIRED X" string,
or factor the shared logic into a small helper that both _format_mtime and
_format_expiry call.
docs/agents/index.md (1)

308-312: Prefer cat over nano to inspect the token file.

nano opens an editor and risks accidentally saving modifications (even a stray newline change) to a secret file. cat (or less) is safer for read-only inspection.

📝 Proposed tweak
-ls -l ~/.config/vibepod/agents/claude/oauth-token
-# or to view contents (treat as a secret — do not share):
-nano ~/.config/vibepod/agents/claude/oauth-token
+ls -l ~/.config/vibepod/agents/claude/oauth-token
+# or to view contents (treat as a secret — do not share):
+cat ~/.config/vibepod/agents/claude/oauth-token
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/agents/index.md` around lines 308 - 312, The markdown snippet shows
using `nano` to view a secret token file which risks accidental edits; update
the example code block so it recommends a read-only viewer like `cat` (or
`less`) instead of `nano`, i.e., replace the `nano
~/.config/vibepod/agents/claude/oauth-token` line with a `cat` (or `less`)
command and update the accompanying comment to emphasize read-only inspection.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@docs/agents/index.md`:
- Around line 308-312: The markdown snippet shows using `nano` to view a secret
token file which risks accidental edits; update the example code block so it
recommends a read-only viewer like `cat` (or `less`) instead of `nano`, i.e.,
replace the `nano ~/.config/vibepod/agents/claude/oauth-token` line with a `cat`
(or `less`) command and update the accompanying comment to emphasize read-only
inspection.

In `@src/vibepod/commands/doctor.py`:
- Around line 66-222: The cfg_dir / "oauth-token" literal is duplicated instead
of reusing the canonical constant/helper from run.py; replace the hard-coded
filename with the shared symbol used by run.py (import CLAUDE_TOKEN_FILENAME or
_claude_stored_token_path from run.py) and use that symbol when constructing
stored_path and any related checks so the doctor command always matches run.py's
CLAUDE token filename/logic.
- Around line 39-54: The expiry formatter _format_expiry currently shows long
expirations as minutes (e.g., "EXPIRED 4320m ago"); update _format_expiry to
reuse the same tiered human-friendly formatting used by _format_mtime
(days/hours/minutes) so expired durations are rendered like "3d ago" or "2h 5m
ago" instead of large minute counts; implement this by computing minutes =
abs(delta_ms) // 60000 for negative deltas and using the same branch logic
(minutes < 60, <1440, else) to build the "EXPIRED X" string, or factor the
shared logic into a small helper that both _format_mtime and _format_expiry
call.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c3e53fb5-fd8b-4a2a-8c08-a04be8fce271

📥 Commits

Reviewing files that changed from the base of the PR and between 1df301a and 2160e27.

📒 Files selected for processing (8)
  • docs/agents/index.md
  • src/vibepod/cli.py
  • src/vibepod/commands/doctor.py
  • src/vibepod/commands/run.py
  • tests/test_claude_token.py
  • tests/test_cli.py
  • tests/test_config.py
  • tests/test_doctor.py
✅ Files skipped from review due to trivial changes (1)
  • tests/test_doctor.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/test_cli.py
  • tests/test_claude_token.py
  • src/vibepod/commands/run.py

Copy link
Copy Markdown

@augmentcode augmentcode Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed. 2 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

Comment thread src/vibepod/commands/doctor.py
Comment thread src/vibepod/commands/run.py
Copy link
Copy Markdown

@augmentcode augmentcode Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed. 2 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

Comment thread src/vibepod/commands/doctor.py
Comment thread src/vibepod/commands/run.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Claude Code credentials do not refresh automatically, requiring new login

1 participant