@@ -4,7 +4,8 @@
|
|||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
- Usage: add MiniMax coding plan usage tracking.
|
- Usage: add MiniMax coding plan usage tracking.
|
||||||
- Auth: rename “Claude CLI” to “Claude Code CLI” in auth options. (#915) — thanks @SeanZoR.
|
- Auth: label Claude Code CLI auth options. (#915) — thanks @SeanZoR.
|
||||||
|
- Docs: standardize Claude Code CLI naming across docs and prompts. (follow-up to #915)
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
- Gateway/Dev: ensure `pnpm gateway:dev` always uses the dev profile config + state (`~/.clawdbot-dev`).
|
- Gateway/Dev: ensure `pnpm gateway:dev` always uses the dev profile config + state (`~/.clawdbot-dev`).
|
||||||
@@ -324,7 +325,7 @@
|
|||||||
- Dependencies: Pi 0.40.0 bump (#543) — thanks @mcinteerj.
|
- Dependencies: Pi 0.40.0 bump (#543) — thanks @mcinteerj.
|
||||||
- Build: Docker build cache layer (#605) — thanks @zknicker.
|
- Build: Docker build cache layer (#605) — thanks @zknicker.
|
||||||
|
|
||||||
- Auth: enable OAuth token refresh for Claude CLI credentials (`anthropic:claude-cli`) with bidirectional sync back to Claude Code storage (file on Linux/Windows, Keychain on macOS). This allows long-running agents to operate autonomously without manual re-authentication (#654 — thanks @radek-paclt).
|
- Auth: enable OAuth token refresh for Claude Code CLI credentials (`anthropic:claude-cli`) with bidirectional sync back to Claude Code storage (file on Linux/Windows, Keychain on macOS). This allows long-running agents to operate autonomously without manual re-authentication (#654 — thanks @radek-paclt).
|
||||||
|
|
||||||
## 2026.1.8
|
## 2026.1.8
|
||||||
|
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ JSON includes `auth.oauth` (warn window + profiles) and `auth.providers`
|
|||||||
(effective auth per provider).
|
(effective auth per provider).
|
||||||
Use `--check` for automation (exit `1` when missing/expired, `2` when expiring).
|
Use `--check` for automation (exit `1` when missing/expired, `2` when expiring).
|
||||||
|
|
||||||
Preferred Anthropic auth is the Claude CLI setup-token (run on the gateway host):
|
Preferred Anthropic auth is the Claude Code CLI setup-token (run on the gateway host):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
claude setup-token
|
claude setup-token
|
||||||
|
|||||||
@@ -47,10 +47,10 @@ API keys for daemon use: `clawdbot onboard`.
|
|||||||
See [/start/faq](/start/faq) for details on env inheritance (`env.shellEnv`,
|
See [/start/faq](/start/faq) for details on env inheritance (`env.shellEnv`,
|
||||||
`~/.clawdbot/.env`, systemd/launchd).
|
`~/.clawdbot/.env`, systemd/launchd).
|
||||||
|
|
||||||
## Anthropic: Claude CLI setup-token (supported)
|
## Anthropic: Claude Code CLI setup-token (supported)
|
||||||
|
|
||||||
For Anthropic, the recommended path is an **API key**. If you’re already using
|
For Anthropic, the recommended path is an **API key**. If you’re already using
|
||||||
Claude Code, the Claude CLI setup-token is also supported.
|
Claude Code CLI, the setup-token flow is also supported.
|
||||||
Run it on the **gateway host**:
|
Run it on the **gateway host**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -138,7 +138,7 @@ Use `--agent <id>` to target a specific agent; omit it to use the configured def
|
|||||||
`~/.clawdbot/agents/<agentId>/agent/auth-profiles.json` when the auth store is
|
`~/.clawdbot/agents/<agentId>/agent/auth-profiles.json` when the auth store is
|
||||||
loaded.
|
loaded.
|
||||||
3. Refreshable OAuth profiles can be refreshed automatically on use. Static
|
3. Refreshable OAuth profiles can be refreshed automatically on use. Static
|
||||||
token profiles (including Claude CLI setup-token) are not refreshable by
|
token profiles (including Claude Code CLI setup-token) are not refreshable by
|
||||||
Clawdbot.
|
Clawdbot.
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
summary: "CLI backends: text-only fallback via local AI CLIs"
|
summary: "CLI backends: text-only fallback via local AI CLIs"
|
||||||
read_when:
|
read_when:
|
||||||
- You want a reliable fallback when API providers fail
|
- You want a reliable fallback when API providers fail
|
||||||
- You are running Claude CLI or other local AI CLIs and want to reuse them
|
- You are running Claude Code CLI or other local AI CLIs and want to reuse them
|
||||||
- You need a text-only, tool-free path that still supports sessions and images
|
- You need a text-only, tool-free path that still supports sessions and images
|
||||||
---
|
---
|
||||||
# CLI backends (fallback runtime)
|
# CLI backends (fallback runtime)
|
||||||
@@ -20,7 +20,7 @@ want “always works” text responses without relying on external APIs.
|
|||||||
|
|
||||||
## Beginner-friendly quick start
|
## Beginner-friendly quick start
|
||||||
|
|
||||||
You can use Claude CLI **without any config** (Clawdbot ships a built-in default):
|
You can use Claude Code CLI **without any config** (Clawdbot ships a built-in default):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
clawdbot agent --message "hi" --model claude-cli/opus-4.5
|
clawdbot agent --message "hi" --model claude-cli/opus-4.5
|
||||||
@@ -162,7 +162,7 @@ imageMode: "repeat"
|
|||||||
Clawdbot will write base64 images to temp files. If `imageArg` is set, those
|
Clawdbot will write base64 images to temp files. If `imageArg` is set, those
|
||||||
paths are passed as CLI args. If `imageArg` is missing, Clawdbot appends the
|
paths are passed as CLI args. If `imageArg` is missing, Clawdbot appends the
|
||||||
file paths to the prompt (path injection), which is enough for CLIs that auto-
|
file paths to the prompt (path injection), which is enough for CLIs that auto-
|
||||||
load local files from plain paths (Claude CLI behavior).
|
load local files from plain paths (Claude Code CLI behavior).
|
||||||
|
|
||||||
## Inputs / outputs
|
## Inputs / outputs
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
---
|
---
|
||||||
summary: "Use Anthropic Claude via API keys or Claude CLI auth in Clawdbot"
|
summary: "Use Anthropic Claude via API keys or Claude Code CLI auth in Clawdbot"
|
||||||
read_when:
|
read_when:
|
||||||
- You want to use Anthropic models in Clawdbot
|
- You want to use Anthropic models in Clawdbot
|
||||||
- You want setup-token or Claude CLI auth instead of API keys
|
- You want setup-token or Claude Code CLI auth instead of API keys
|
||||||
---
|
---
|
||||||
# Anthropic (Claude)
|
# Anthropic (Claude)
|
||||||
|
|
||||||
Anthropic builds the **Claude** model family and provides access via an API.
|
Anthropic builds the **Claude** model family and provides access via an API.
|
||||||
In Clawdbot you can authenticate with an API key or reuse **Claude Code / Claude CLI**
|
In Clawdbot you can authenticate with an API key or reuse **Claude Code CLI** credentials
|
||||||
credentials (setup-token or OAuth).
|
(setup-token or OAuth).
|
||||||
|
|
||||||
## Option A: Anthropic API key
|
## Option A: Anthropic API key
|
||||||
|
|
||||||
@@ -34,9 +34,9 @@ clawdbot onboard --anthropic-api-key "$ANTHROPIC_API_KEY"
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Option B: Claude CLI (setup-token or OAuth)
|
## Option B: Claude Code CLI (setup-token or OAuth)
|
||||||
|
|
||||||
**Best for:** using your Claude subscription or existing Claude CLI login.
|
**Best for:** using your Claude subscription or existing Claude Code CLI login.
|
||||||
|
|
||||||
### CLI setup
|
### CLI setup
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ clawdbot onboard --anthropic-api-key "$ANTHROPIC_API_KEY"
|
|||||||
# Run setup-token on the gateway host (wizard can run it for you)
|
# Run setup-token on the gateway host (wizard can run it for you)
|
||||||
clawdbot onboard --auth-choice setup-token
|
clawdbot onboard --auth-choice setup-token
|
||||||
|
|
||||||
# Reuse Claude CLI OAuth credentials if already logged in
|
# Reuse Claude Code CLI OAuth credentials if already logged in
|
||||||
clawdbot onboard --auth-choice claude-cli
|
clawdbot onboard --auth-choice claude-cli
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ Looking for chat channel docs (WhatsApp/Telegram/Discord/Slack/etc.)? See [Chann
|
|||||||
## Provider docs
|
## Provider docs
|
||||||
|
|
||||||
- [OpenAI (API + Codex)](/providers/openai)
|
- [OpenAI (API + Codex)](/providers/openai)
|
||||||
- [Anthropic (API + Claude CLI)](/providers/anthropic)
|
- [Anthropic (API + Claude Code CLI)](/providers/anthropic)
|
||||||
- [OpenRouter](/providers/openrouter)
|
- [OpenRouter](/providers/openrouter)
|
||||||
- [Moonshot AI (Kimi)](/providers/moonshot)
|
- [Moonshot AI (Kimi)](/providers/moonshot)
|
||||||
- [OpenCode Zen](/providers/opencode)
|
- [OpenCode Zen](/providers/opencode)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ model as `provider/model`.
|
|||||||
## Supported providers (starter set)
|
## Supported providers (starter set)
|
||||||
|
|
||||||
- [OpenAI (API + Codex)](/providers/openai)
|
- [OpenAI (API + Codex)](/providers/openai)
|
||||||
- [Anthropic (API + Claude CLI)](/providers/anthropic)
|
- [Anthropic (API + Claude Code CLI)](/providers/anthropic)
|
||||||
- [OpenRouter](/providers/openrouter)
|
- [OpenRouter](/providers/openrouter)
|
||||||
- [Moonshot AI (Kimi)](/providers/moonshot)
|
- [Moonshot AI (Kimi)](/providers/moonshot)
|
||||||
- [Synthetic](/providers/synthetic)
|
- [Synthetic](/providers/synthetic)
|
||||||
|
|||||||
@@ -1017,11 +1017,11 @@ It means the system attempted to use the auth profile ID `anthropic:default`, bu
|
|||||||
|
|
||||||
### Fix checklist for `No credentials found for profile "anthropic:claude-cli"`
|
### Fix checklist for `No credentials found for profile "anthropic:claude-cli"`
|
||||||
|
|
||||||
This means the run is pinned to the **Claude CLI** profile, but the Gateway
|
This means the run is pinned to the **Claude Code CLI** profile, but the Gateway
|
||||||
can’t find that profile in its auth store.
|
can’t find that profile in its auth store.
|
||||||
|
|
||||||
- **Sync the Claude CLI token on the gateway host**
|
- **Sync the Claude Code CLI token on the gateway host**
|
||||||
- Run `clawdbot models status` (it loads + syncs Claude CLI credentials).
|
- Run `clawdbot models status` (it loads + syncs Claude Code CLI credentials).
|
||||||
- If it still says missing: run `claude setup-token` (or `clawdbot models auth setup-token --provider anthropic`) and retry.
|
- If it still says missing: run `claude setup-token` (or `clawdbot models auth setup-token --provider anthropic`) and retry.
|
||||||
- **If you want to use an API key instead**
|
- **If you want to use an API key instead**
|
||||||
- Put `ANTHROPIC_API_KEY` in `~/.clawdbot/.env` on the **gateway host**.
|
- Put `ANTHROPIC_API_KEY` in `~/.clawdbot/.env` on the **gateway host**.
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ Tip: `--json` does **not** imply non-interactive mode. Use `--non-interactive` (
|
|||||||
2) **Model/Auth**
|
2) **Model/Auth**
|
||||||
- **Anthropic API key (recommended)**: uses `ANTHROPIC_API_KEY` if present or prompts for a key, then saves it for daemon use.
|
- **Anthropic API key (recommended)**: uses `ANTHROPIC_API_KEY` if present or prompts for a key, then saves it for daemon use.
|
||||||
- **Anthropic token (setup-token)**: run `claude setup-token` on the gateway host (the wizard can run it for you and reuse the token).
|
- **Anthropic token (setup-token)**: run `claude setup-token` on the gateway host (the wizard can run it for you and reuse the token).
|
||||||
- **Anthropic OAuth (Claude CLI)**: on macOS the wizard checks Keychain item "Claude Code-credentials" (choose "Always Allow" so launchd starts don't block); on Linux/Windows it reuses `~/.claude/.credentials.json` if present.
|
- **Anthropic OAuth (Claude Code CLI)**: on macOS the wizard checks Keychain item "Claude Code-credentials" (choose "Always Allow" so launchd starts don't block); on Linux/Windows it reuses `~/.claude/.credentials.json` if present.
|
||||||
- **Anthropic token (paste setup-token)**: run `claude setup-token` in your terminal, then paste the token (you can name it; blank = default).
|
- **Anthropic token (paste setup-token)**: run `claude setup-token` in your terminal, then paste the token (you can name it; blank = default).
|
||||||
- **OpenAI Code (Codex) subscription (Codex CLI)**: if `~/.codex/auth.json` exists, the wizard can reuse it.
|
- **OpenAI Code (Codex) subscription (Codex CLI)**: if `~/.codex/auth.json` exists, the wizard can reuse it.
|
||||||
- **OpenAI Code (Codex) subscription (OAuth)**: browser flow; paste the `code#state`.
|
- **OpenAI Code (Codex) subscription (OAuth)**: browser flow; paste the `code#state`.
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ pnpm clawdbot models list --json
|
|||||||
## Live: Anthropic setup-token smoke
|
## Live: Anthropic setup-token smoke
|
||||||
|
|
||||||
- Test: `src/agents/anthropic.setup-token.live.test.ts`
|
- Test: `src/agents/anthropic.setup-token.live.test.ts`
|
||||||
- Goal: verify Claude CLI setup-token (or a pasted setup-token profile) can complete an Anthropic prompt.
|
- Goal: verify Claude Code CLI setup-token (or a pasted setup-token profile) can complete an Anthropic prompt.
|
||||||
- Enable:
|
- Enable:
|
||||||
- `pnpm test:live` (or `CLAWDBOT_LIVE_TEST=1` if invoking Vitest directly)
|
- `pnpm test:live` (or `CLAWDBOT_LIVE_TEST=1` if invoking Vitest directly)
|
||||||
- `CLAWDBOT_LIVE_SETUP_TOKEN=1`
|
- `CLAWDBOT_LIVE_SETUP_TOKEN=1`
|
||||||
@@ -174,7 +174,7 @@ clawdbot models auth paste-token --provider anthropic --profile-id anthropic:set
|
|||||||
CLAWDBOT_LIVE_SETUP_TOKEN=1 CLAWDBOT_LIVE_SETUP_TOKEN_PROFILE=anthropic:setup-token-test pnpm test:live src/agents/anthropic.setup-token.live.test.ts
|
CLAWDBOT_LIVE_SETUP_TOKEN=1 CLAWDBOT_LIVE_SETUP_TOKEN_PROFILE=anthropic:setup-token-test pnpm test:live src/agents/anthropic.setup-token.live.test.ts
|
||||||
```
|
```
|
||||||
|
|
||||||
## Live: CLI backend smoke (Claude CLI or other local CLIs)
|
## Live: CLI backend smoke (Claude Code CLI or other local CLIs)
|
||||||
|
|
||||||
- Test: `src/gateway/gateway-cli-backend.live.test.ts`
|
- Test: `src/gateway/gateway-cli-backend.live.test.ts`
|
||||||
- Goal: validate the Gateway + agent pipeline using a local CLI backend, without touching your default config.
|
- Goal: validate the Gateway + agent pipeline using a local CLI backend, without touching your default config.
|
||||||
@@ -195,7 +195,7 @@ CLAWDBOT_LIVE_SETUP_TOKEN=1 CLAWDBOT_LIVE_SETUP_TOKEN_PROFILE=anthropic:setup-to
|
|||||||
- `CLAWDBOT_LIVE_CLI_BACKEND_IMAGE_ARG="--image"` to pass image file paths as CLI args instead of prompt injection.
|
- `CLAWDBOT_LIVE_CLI_BACKEND_IMAGE_ARG="--image"` to pass image file paths as CLI args instead of prompt injection.
|
||||||
- `CLAWDBOT_LIVE_CLI_BACKEND_IMAGE_MODE="repeat"` (or `"list"`) to control how image args are passed when `IMAGE_ARG` is set.
|
- `CLAWDBOT_LIVE_CLI_BACKEND_IMAGE_MODE="repeat"` (or `"list"`) to control how image args are passed when `IMAGE_ARG` is set.
|
||||||
- `CLAWDBOT_LIVE_CLI_BACKEND_RESUME_PROBE=1` to send a second turn and validate resume flow.
|
- `CLAWDBOT_LIVE_CLI_BACKEND_RESUME_PROBE=1` to send a second turn and validate resume flow.
|
||||||
- `CLAWDBOT_LIVE_CLI_BACKEND_DISABLE_MCP_CONFIG=0` to keep Claude CLI MCP config enabled (default disables MCP config with a temporary empty file).
|
- `CLAWDBOT_LIVE_CLI_BACKEND_DISABLE_MCP_CONFIG=0` to keep Claude Code CLI MCP config enabled (default disables MCP config with a temporary empty file).
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
|||||||
@@ -283,7 +283,7 @@ const main = async () => {
|
|||||||
const keychain = readClaudeCliKeychain();
|
const keychain = readClaudeCliKeychain();
|
||||||
if (keychain) {
|
if (keychain) {
|
||||||
console.log(
|
console.log(
|
||||||
`Claude CLI keychain: accessToken=${opts.reveal ? keychain.accessToken : mask(keychain.accessToken)} scopes=${keychain.scopes?.join(",") ?? "(unknown)"}`,
|
`Claude Code CLI keychain: accessToken=${opts.reveal ? keychain.accessToken : mask(keychain.accessToken)} scopes=${keychain.scopes?.join(",") ?? "(unknown)"}`,
|
||||||
);
|
);
|
||||||
const oauth = await fetchAnthropicOAuthUsage(keychain.accessToken);
|
const oauth = await fetchAnthropicOAuthUsage(keychain.accessToken);
|
||||||
console.log(
|
console.log(
|
||||||
@@ -291,7 +291,7 @@ const main = async () => {
|
|||||||
);
|
);
|
||||||
console.log(oauth.text.slice(0, 200).replace(/\s+/g, " ").trim());
|
console.log(oauth.text.slice(0, 200).replace(/\s+/g, " ").trim());
|
||||||
} else {
|
} else {
|
||||||
console.log("Claude CLI keychain: missing/unreadable");
|
console.log("Claude Code CLI keychain: missing/unreadable");
|
||||||
}
|
}
|
||||||
|
|
||||||
const anthropic = pickAnthropicTokens(store);
|
const anthropic = pickAnthropicTokens(store);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ describe("external CLI credential sync", () => {
|
|||||||
try {
|
try {
|
||||||
await withTempHome(
|
await withTempHome(
|
||||||
async (tempHome) => {
|
async (tempHome) => {
|
||||||
// Create Claude CLI credentials
|
// Create Claude Code CLI credentials
|
||||||
const claudeDir = path.join(tempHome, ".claude");
|
const claudeDir = path.join(tempHome, ".claude");
|
||||||
fs.mkdirSync(claudeDir, { recursive: true });
|
fs.mkdirSync(claudeDir, { recursive: true });
|
||||||
const claudeCreds = {
|
const claudeCreds = {
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ import { withTempHome } from "../../test/helpers/temp-home.js";
|
|||||||
import { CLAUDE_CLI_PROFILE_ID, ensureAuthProfileStore } from "./auth-profiles.js";
|
import { CLAUDE_CLI_PROFILE_ID, ensureAuthProfileStore } from "./auth-profiles.js";
|
||||||
|
|
||||||
describe("external CLI credential sync", () => {
|
describe("external CLI credential sync", () => {
|
||||||
it("syncs Claude CLI OAuth credentials into anthropic:claude-cli", async () => {
|
it("syncs Claude Code CLI OAuth credentials into anthropic:claude-cli", async () => {
|
||||||
const agentDir = fs.mkdtempSync(path.join(os.tmpdir(), "clawdbot-cli-sync-"));
|
const agentDir = fs.mkdtempSync(path.join(os.tmpdir(), "clawdbot-cli-sync-"));
|
||||||
try {
|
try {
|
||||||
// Create a temp home with Claude CLI credentials
|
// Create a temp home with Claude Code CLI credentials
|
||||||
await withTempHome(
|
await withTempHome(
|
||||||
async (tempHome) => {
|
async (tempHome) => {
|
||||||
// Create Claude CLI credentials with refreshToken (OAuth)
|
// Create Claude Code CLI credentials with refreshToken (OAuth)
|
||||||
const claudeDir = path.join(tempHome, ".claude");
|
const claudeDir = path.join(tempHome, ".claude");
|
||||||
fs.mkdirSync(claudeDir, { recursive: true });
|
fs.mkdirSync(claudeDir, { recursive: true });
|
||||||
const claudeCreds = {
|
const claudeCreds = {
|
||||||
@@ -59,12 +59,12 @@ describe("external CLI credential sync", () => {
|
|||||||
fs.rmSync(agentDir, { recursive: true, force: true });
|
fs.rmSync(agentDir, { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
it("syncs Claude CLI credentials without refreshToken as token type", async () => {
|
it("syncs Claude Code CLI credentials without refreshToken as token type", async () => {
|
||||||
const agentDir = fs.mkdtempSync(path.join(os.tmpdir(), "clawdbot-cli-token-sync-"));
|
const agentDir = fs.mkdtempSync(path.join(os.tmpdir(), "clawdbot-cli-token-sync-"));
|
||||||
try {
|
try {
|
||||||
await withTempHome(
|
await withTempHome(
|
||||||
async (tempHome) => {
|
async (tempHome) => {
|
||||||
// Create Claude CLI credentials WITHOUT refreshToken (fallback to token type)
|
// Create Claude Code CLI credentials WITHOUT refreshToken (fallback to token type)
|
||||||
const claudeDir = path.join(tempHome, ".claude");
|
const claudeDir = path.join(tempHome, ".claude");
|
||||||
fs.mkdirSync(claudeDir, { recursive: true });
|
fs.mkdirSync(claudeDir, { recursive: true });
|
||||||
const claudeCreds = {
|
const claudeCreds = {
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ import {
|
|||||||
} from "./auth-profiles.js";
|
} from "./auth-profiles.js";
|
||||||
|
|
||||||
describe("external CLI credential sync", () => {
|
describe("external CLI credential sync", () => {
|
||||||
it("upgrades token to oauth when Claude CLI gets refreshToken", async () => {
|
it("upgrades token to oauth when Claude Code CLI gets refreshToken", async () => {
|
||||||
const agentDir = fs.mkdtempSync(path.join(os.tmpdir(), "clawdbot-cli-upgrade-"));
|
const agentDir = fs.mkdtempSync(path.join(os.tmpdir(), "clawdbot-cli-upgrade-"));
|
||||||
try {
|
try {
|
||||||
await withTempHome(
|
await withTempHome(
|
||||||
async (tempHome) => {
|
async (tempHome) => {
|
||||||
// Create Claude CLI credentials with refreshToken
|
// Create Claude Code CLI credentials with refreshToken
|
||||||
const claudeDir = path.join(tempHome, ".claude");
|
const claudeDir = path.join(tempHome, ".claude");
|
||||||
fs.mkdirSync(claudeDir, { recursive: true });
|
fs.mkdirSync(claudeDir, { recursive: true });
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ function isExternalProfileFresh(cred: AuthProfileCredential | undefined, now: nu
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sync OAuth credentials from external CLI tools (Claude CLI, Codex CLI) into the store.
|
* Sync OAuth credentials from external CLI tools (Claude Code CLI, Codex CLI) into the store.
|
||||||
* This allows clawdbot to use the same credentials as these tools without requiring
|
* This allows clawdbot to use the same credentials as these tools without requiring
|
||||||
* separate authentication, and keeps credentials in sync when CLI tools refresh tokens.
|
* separate authentication, and keeps credentials in sync when CLI tools refresh tokens.
|
||||||
*
|
*
|
||||||
@@ -66,7 +66,7 @@ export function syncExternalCliCredentials(
|
|||||||
let mutated = false;
|
let mutated = false;
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
// Sync from Claude CLI (supports both OAuth and Token credentials)
|
// Sync from Claude Code CLI (supports both OAuth and Token credentials)
|
||||||
const existingClaude = store.profiles[CLAUDE_CLI_PROFILE_ID];
|
const existingClaude = store.profiles[CLAUDE_CLI_PROFILE_ID];
|
||||||
const shouldSyncClaude =
|
const shouldSyncClaude =
|
||||||
!existingClaude ||
|
!existingClaude ||
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ async function refreshOAuthTokenWithLock(params: {
|
|||||||
};
|
};
|
||||||
saveAuthProfileStore(store, params.agentDir);
|
saveAuthProfileStore(store, params.agentDir);
|
||||||
|
|
||||||
// Sync refreshed credentials back to Claude CLI if this is the claude-cli profile
|
// Sync refreshed credentials back to Claude Code CLI if this is the claude-cli profile
|
||||||
// This ensures Claude Code continues to work after ClawdBot refreshes the token
|
// This ensures Claude Code continues to work after ClawdBot refreshes the token
|
||||||
if (params.profileId === CLAUDE_CLI_PROFILE_ID && cred.provider === "anthropic") {
|
if (params.profileId === CLAUDE_CLI_PROFILE_ID && cred.provider === "anthropic") {
|
||||||
writeClaudeCliCredentials(result.newCredentials);
|
writeClaudeCliCredentials(result.newCredentials);
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ describe("cli credentials", () => {
|
|||||||
expect(updated.claudeAiOauth?.expiresAt).toBeTypeOf("number");
|
expect(updated.claudeAiOauth?.expiresAt).toBeTypeOf("number");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("caches Claude CLI credentials within the TTL window", async () => {
|
it("caches Claude Code CLI credentials within the TTL window", async () => {
|
||||||
execSyncMock.mockImplementation(() =>
|
execSyncMock.mockImplementation(() =>
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
claudeAiOauth: {
|
claudeAiOauth: {
|
||||||
@@ -142,7 +142,7 @@ describe("cli credentials", () => {
|
|||||||
expect(execSyncMock).toHaveBeenCalledTimes(1);
|
expect(execSyncMock).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("refreshes Claude CLI credentials after the TTL window", async () => {
|
it("refreshes Claude Code CLI credentials after the TTL window", async () => {
|
||||||
execSyncMock.mockImplementation(() =>
|
execSyncMock.mockImplementation(() =>
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
claudeAiOauth: {
|
claudeAiOauth: {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ describe("buildAuthChoiceOptions", () => {
|
|||||||
|
|
||||||
expect(options.find((opt) => opt.value === "github-copilot")).toBeDefined();
|
expect(options.find((opt) => opt.value === "github-copilot")).toBeDefined();
|
||||||
});
|
});
|
||||||
it("includes Claude CLI option on macOS even when missing", () => {
|
it("includes Claude Code CLI option on macOS even when missing", () => {
|
||||||
const store: AuthProfileStore = { version: 1, profiles: {} };
|
const store: AuthProfileStore = { version: 1, profiles: {} };
|
||||||
const options = buildAuthChoiceOptions({
|
const options = buildAuthChoiceOptions({
|
||||||
store,
|
store,
|
||||||
@@ -29,7 +29,7 @@ describe("buildAuthChoiceOptions", () => {
|
|||||||
expect(claudeCli?.hint).toBe("requires Keychain access");
|
expect(claudeCli?.hint).toBe("requires Keychain access");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("skips missing Claude CLI option off macOS", () => {
|
it("skips missing Claude Code CLI option off macOS", () => {
|
||||||
const store: AuthProfileStore = { version: 1, profiles: {} };
|
const store: AuthProfileStore = { version: 1, profiles: {} };
|
||||||
const options = buildAuthChoiceOptions({
|
const options = buildAuthChoiceOptions({
|
||||||
store,
|
store,
|
||||||
@@ -41,7 +41,7 @@ describe("buildAuthChoiceOptions", () => {
|
|||||||
expect(options.find((opt) => opt.value === "claude-cli")).toBeUndefined();
|
expect(options.find((opt) => opt.value === "claude-cli")).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("uses token hint when Claude CLI credentials exist", () => {
|
it("uses token hint when Claude Code CLI credentials exist", () => {
|
||||||
const store: AuthProfileStore = {
|
const store: AuthProfileStore = {
|
||||||
version: 1,
|
version: 1,
|
||||||
profiles: {
|
profiles: {
|
||||||
|
|||||||
@@ -28,10 +28,10 @@ export async function applyAuthChoiceAnthropic(
|
|||||||
'Choose "Always Allow" so the launchd gateway can start without prompts.',
|
'Choose "Always Allow" so the launchd gateway can start without prompts.',
|
||||||
'If you choose "Allow" or "Deny", each restart will block on a Keychain alert.',
|
'If you choose "Allow" or "Deny", each restart will block on a Keychain alert.',
|
||||||
].join("\n"),
|
].join("\n"),
|
||||||
"Claude CLI Keychain",
|
"Claude Code CLI Keychain",
|
||||||
);
|
);
|
||||||
const proceed = await params.prompter.confirm({
|
const proceed = await params.prompter.confirm({
|
||||||
message: "Check Keychain for Claude CLI credentials now?",
|
message: "Check Keychain for Claude Code CLI credentials now?",
|
||||||
initialValue: true,
|
initialValue: true,
|
||||||
});
|
});
|
||||||
if (!proceed) return { config: nextConfig };
|
if (!proceed) return { config: nextConfig };
|
||||||
@@ -74,9 +74,9 @@ export async function applyAuthChoiceAnthropic(
|
|||||||
if (!refreshed.profiles[CLAUDE_CLI_PROFILE_ID]) {
|
if (!refreshed.profiles[CLAUDE_CLI_PROFILE_ID]) {
|
||||||
await params.prompter.note(
|
await params.prompter.note(
|
||||||
process.platform === "darwin"
|
process.platform === "darwin"
|
||||||
? 'No Claude CLI credentials found in Keychain ("Claude Code-credentials") or ~/.claude/.credentials.json.'
|
? 'No Claude Code CLI credentials found in Keychain ("Claude Code-credentials") or ~/.claude/.credentials.json.'
|
||||||
: "No Claude CLI credentials found at ~/.claude/.credentials.json.",
|
: "No Claude Code CLI credentials found at ~/.claude/.credentials.json.",
|
||||||
"Claude CLI OAuth",
|
"Claude Code CLI OAuth",
|
||||||
);
|
);
|
||||||
return { config: nextConfig };
|
return { config: nextConfig };
|
||||||
}
|
}
|
||||||
@@ -137,7 +137,7 @@ export async function applyAuthChoiceAnthropic(
|
|||||||
});
|
});
|
||||||
if (!store.profiles[CLAUDE_CLI_PROFILE_ID]) {
|
if (!store.profiles[CLAUDE_CLI_PROFILE_ID]) {
|
||||||
await params.prompter.note(
|
await params.prompter.note(
|
||||||
`No Claude CLI credentials found after setup-token. Expected ${CLAUDE_CLI_PROFILE_ID}.`,
|
`No Claude Code CLI credentials found after setup-token. Expected ${CLAUDE_CLI_PROFILE_ID}.`,
|
||||||
"Anthropic setup-token",
|
"Anthropic setup-token",
|
||||||
);
|
);
|
||||||
return { config: nextConfig };
|
return { config: nextConfig };
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ export async function modelsAuthSetupTokenCommand(
|
|||||||
const synced = store.profiles[CLAUDE_CLI_PROFILE_ID];
|
const synced = store.profiles[CLAUDE_CLI_PROFILE_ID];
|
||||||
if (!synced) {
|
if (!synced) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`No Claude CLI credentials found after setup-token. Expected auth profile ${CLAUDE_CLI_PROFILE_ID}.`,
|
`No Claude Code CLI credentials found after setup-token. Expected auth profile ${CLAUDE_CLI_PROFILE_ID}.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -261,8 +261,8 @@ export async function applyNonInteractiveAuthChoice(params: {
|
|||||||
if (!store.profiles[CLAUDE_CLI_PROFILE_ID]) {
|
if (!store.profiles[CLAUDE_CLI_PROFILE_ID]) {
|
||||||
runtime.error(
|
runtime.error(
|
||||||
process.platform === "darwin"
|
process.platform === "darwin"
|
||||||
? 'No Claude CLI credentials found. Run interactive onboarding to approve Keychain access for "Claude Code-credentials".'
|
? 'No Claude Code CLI credentials found. Run interactive onboarding to approve Keychain access for "Claude Code-credentials".'
|
||||||
: "No Claude CLI credentials found at ~/.claude/.credentials.json",
|
: "No Claude Code CLI credentials found at ~/.claude/.credentials.json",
|
||||||
);
|
);
|
||||||
runtime.exit(1);
|
runtime.exit(1);
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ async function resolveOAuthToken(params: {
|
|||||||
provider: params.provider,
|
provider: params.provider,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Claude CLI creds are the only Anthropic tokens that reliably include the
|
// Claude Code CLI creds are the only Anthropic tokens that reliably include the
|
||||||
// `user:profile` scope required for the OAuth usage endpoint.
|
// `user:profile` scope required for the OAuth usage endpoint.
|
||||||
const candidates = params.provider === "anthropic" ? [CLAUDE_CLI_PROFILE_ID, ...order] : order;
|
const candidates = params.provider === "anthropic" ? [CLAUDE_CLI_PROFILE_ID, ...order] : order;
|
||||||
const deduped: string[] = [];
|
const deduped: string[] = [];
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ export async function fetchClaudeUsage(
|
|||||||
// ignore parse errors
|
// ignore parse errors
|
||||||
}
|
}
|
||||||
|
|
||||||
// Claude CLI setup-token yields tokens that can be used for inference, but may not
|
// Claude Code CLI setup-token yields tokens that can be used for inference, but may not
|
||||||
// include user:profile scope required by the OAuth usage endpoint. When a claude.ai
|
// include user:profile scope required by the OAuth usage endpoint. When a claude.ai
|
||||||
// browser sessionKey is available, fall back to the web API.
|
// browser sessionKey is available, fall back to the web API.
|
||||||
if (res.status === 403 && message?.includes("scope requirement user:profile")) {
|
if (res.status === 403 && message?.includes("scope requirement user:profile")) {
|
||||||
|
|||||||
Reference in New Issue
Block a user