@cleocode/adapters 2026.4.60 → 2026.4.63

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,20 +9,79 @@
9
9
  * - Awaits full completion before returning (synchronous output capture)
10
10
  * - Session IDs from the SDK enable future multi-turn resumption
11
11
  * - No temp files, no OS PIDs — tracking is purely in-memory session IDs
12
- * - `canSpawn()` checks for `ANTHROPIC_API_KEY` rather than CLI availability
12
+ * - `canSpawn()` uses 3-tier key resolution (env var → stored key → Claude Code OAuth)
13
13
  *
14
14
  * CANT enrichment is identical to the CLI provider: `buildCantEnrichedPrompt()`
15
15
  * is called before `query()` and the result is passed as the SDK prompt string.
16
16
  *
17
17
  * @task T581
18
+ * @see T752 — canSpawn() OAuth fix
18
19
  */
19
20
 
21
+ import { existsSync, readFileSync } from 'node:fs';
22
+ import { homedir } from 'node:os';
23
+ import { join } from 'node:path';
20
24
  import type { AdapterSpawnProvider, SpawnContext, SpawnResult } from '@cleocode/contracts';
21
25
  import { getErrorMessage } from '@cleocode/contracts';
22
26
  import { getServers } from './mcp-registry.js';
23
27
  import { SessionStore } from './session-store.js';
24
28
  import { resolveTools } from './tool-bridge.js';
25
29
 
30
+ // ---------------------------------------------------------------------------
31
+ // Inline 3-tier Anthropic key resolver
32
+ // NOTE: Cannot import from @cleocode/core — circular dependency
33
+ // (@cleocode/core depends on @cleocode/adapters). This is a deliberate
34
+ // inline copy of the resolution logic from anthropic-key-resolver.ts.
35
+ // Keep in sync with packages/core/src/memory/anthropic-key-resolver.ts.
36
+ // T752 — OAuth fix for canSpawn()
37
+ // ---------------------------------------------------------------------------
38
+
39
+ /**
40
+ * Resolve the Anthropic API key using a 3-tier priority chain:
41
+ * 1. `ANTHROPIC_API_KEY` environment variable
42
+ * 2. `~/.local/share/cleo/anthropic-key` (user-stored via cleo config)
43
+ * 3. `~/.claude/.credentials.json` → claudeAiOauth.accessToken (Claude Code OAuth)
44
+ *
45
+ * @returns The key/token string, or null if unavailable.
46
+ */
47
+ function resolveAnthropicApiKey(): string | null {
48
+ // 1. Explicit env var
49
+ const envKey = process.env.ANTHROPIC_API_KEY;
50
+ if (envKey?.trim()) return envKey;
51
+
52
+ // 2. CLEO global stored key
53
+ try {
54
+ const xdg = process.env.XDG_DATA_HOME || join(homedir(), '.local', 'share');
55
+ const keyFile = join(xdg, 'cleo', 'anthropic-key');
56
+ if (existsSync(keyFile)) {
57
+ const stored = readFileSync(keyFile, 'utf-8').trim();
58
+ if (stored) return stored;
59
+ }
60
+ } catch {
61
+ // Not available — continue
62
+ }
63
+
64
+ // 3. Claude Code OAuth token (free for Claude Code users)
65
+ try {
66
+ const credPath = join(homedir(), '.claude', '.credentials.json');
67
+ if (!existsSync(credPath)) return null;
68
+ const raw = readFileSync(credPath, 'utf-8');
69
+ const creds = JSON.parse(raw) as {
70
+ claudeAiOauth?: { accessToken?: string; expiresAt?: number };
71
+ };
72
+ const token = creds.claudeAiOauth?.accessToken;
73
+ if (token?.trim()) {
74
+ const expiresAt = creds.claudeAiOauth?.expiresAt;
75
+ if (expiresAt && Date.now() > expiresAt) return null;
76
+ return token;
77
+ }
78
+ } catch {
79
+ // Credentials file missing or unreadable — not an error
80
+ }
81
+
82
+ return null;
83
+ }
84
+
26
85
  /** Model used when no model is specified in spawn options. */
27
86
  const DEFAULT_MODEL = 'claude-sonnet-4-5';
28
87
 
@@ -48,13 +107,18 @@ export class ClaudeSDKSpawnProvider implements AdapterSpawnProvider {
48
107
  /**
49
108
  * Check whether the SDK can be used in the current environment.
50
109
  *
51
- * Returns `true` if `ANTHROPIC_API_KEY` is set. No binary check is needed
52
- * because the SDK manages the Claude Code subprocess internally.
110
+ * Uses 3-tier key resolution so the provider works with:
111
+ * - `ANTHROPIC_API_KEY` environment variable (explicit)
112
+ * - `~/.local/share/cleo/anthropic-key` (user-stored via cleo config)
113
+ * - Claude Code OAuth token (zero-config for Claude Code users)
114
+ *
115
+ * No binary check is needed because the SDK manages the Claude Code
116
+ * subprocess internally.
53
117
  *
54
- * @returns `true` when an API key is present
118
+ * @returns `true` when any Anthropic credential is available
55
119
  */
56
120
  async canSpawn(): Promise<boolean> {
57
- return !!process.env.ANTHROPIC_API_KEY;
121
+ return !!resolveAnthropicApiKey();
58
122
  }
59
123
 
60
124
  /**