teleportation-cli 1.0.0 → 1.0.1

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.
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Claude Code API Key Extractor
3
+ *
4
+ * Extracts the ANTHROPIC_API_KEY from the user's local Claude Code installation.
5
+ * Tries multiple methods to find the key.
6
+ */
7
+
8
+ import { exec } from 'child_process';
9
+ import { promisify } from 'util';
10
+ import fs from 'fs/promises';
11
+ import os from 'os';
12
+ import path from 'path';
13
+
14
+ const execAsync = promisify(exec);
15
+
16
+ /**
17
+ * Try to get API key from macOS keychain
18
+ */
19
+ async function getFromKeychain() {
20
+ try {
21
+ // Try common keychain entries for Claude/Anthropic
22
+ const services = [
23
+ 'claude.ai',
24
+ 'anthropic.com',
25
+ 'Claude',
26
+ 'Anthropic',
27
+ 'claude-code',
28
+ ];
29
+
30
+ for (const service of services) {
31
+ try {
32
+ const { stdout } = await execAsync(`security find-generic-password -s "${service}" -w 2>/dev/null`);
33
+ if (stdout && stdout.trim().startsWith('sk-ant-')) {
34
+ return stdout.trim();
35
+ }
36
+ } catch {}
37
+ }
38
+
39
+ // Try finding by account name
40
+ const accounts = ['api-key', 'apikey', 'token'];
41
+ for (const acct of accounts) {
42
+ try {
43
+ const { stdout } = await execAsync(`security find-generic-password -a "${acct}" -w 2>/dev/null`);
44
+ if (stdout && stdout.trim().startsWith('sk-ant-')) {
45
+ return stdout.trim();
46
+ }
47
+ } catch {}
48
+ }
49
+
50
+ return null;
51
+ } catch (error) {
52
+ return null;
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Try to get API key from environment
58
+ */
59
+ function getFromEnvironment() {
60
+ return process.env.ANTHROPIC_API_KEY ||
61
+ process.env.CLAUDE_API_KEY ||
62
+ process.env.CLAUDE_CODE_API_KEY ||
63
+ null;
64
+ }
65
+
66
+ /**
67
+ * Try to get API key from Claude Code config files
68
+ */
69
+ async function getFromClaudeConfig() {
70
+ try {
71
+ const homeDir = os.homedir();
72
+ const configPaths = [
73
+ path.join(homeDir, '.claude', 'config.json'),
74
+ path.join(homeDir, '.claude', 'settings.json'),
75
+ path.join(homeDir, '.claude', 'credentials.json'),
76
+ path.join(homeDir, '.config', 'claude', 'config.json'),
77
+ ];
78
+
79
+ for (const configPath of configPaths) {
80
+ try {
81
+ const content = await fs.readFile(configPath, 'utf-8');
82
+ const config = JSON.parse(content);
83
+
84
+ // Check various possible key names
85
+ const key = config.apiKey ||
86
+ config.anthropicApiKey ||
87
+ config.ANTHROPIC_API_KEY ||
88
+ config.api_key ||
89
+ config.token;
90
+
91
+ if (key && key.startsWith('sk-ant-')) {
92
+ return key;
93
+ }
94
+ } catch {}
95
+ }
96
+
97
+ return null;
98
+ } catch (error) {
99
+ return null;
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Try to get API key from running Claude Code process environment
105
+ */
106
+ async function getFromRunningProcess() {
107
+ try {
108
+ // Get PID of running claude process
109
+ const { stdout: pids } = await execAsync(`pgrep -f "claude" 2>/dev/null || echo ""`);
110
+
111
+ if (!pids.trim()) {
112
+ return null;
113
+ }
114
+
115
+ // Try to read environment from process
116
+ for (const pid of pids.trim().split('\n')) {
117
+ if (!pid) continue;
118
+
119
+ try {
120
+ // On macOS, we can try to read process environment via ps
121
+ const { stdout } = await execAsync(`ps eww ${pid} 2>/dev/null | grep -o "ANTHROPIC_API_KEY=[^ ]*" | cut -d= -f2`);
122
+ if (stdout && stdout.trim().startsWith('sk-ant-')) {
123
+ return stdout.trim();
124
+ }
125
+ } catch {}
126
+ }
127
+
128
+ return null;
129
+ } catch (error) {
130
+ return null;
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Main function to extract Claude Code API key
136
+ * Tries multiple methods in order of reliability
137
+ *
138
+ * @returns {Promise<string|null>} API key if found, null otherwise
139
+ */
140
+ export async function extractClaudeApiKey() {
141
+ console.log('[claude-key-extractor] Attempting to extract API key from local Claude Code installation...');
142
+
143
+ // Method 1: Check environment first (fastest)
144
+ let apiKey = getFromEnvironment();
145
+ if (apiKey) {
146
+ console.log('[claude-key-extractor] ✅ Found API key in environment variables');
147
+ return apiKey;
148
+ }
149
+
150
+ // Method 2: Check Claude Code config files
151
+ apiKey = await getFromClaudeConfig();
152
+ if (apiKey) {
153
+ console.log('[claude-key-extractor] ✅ Found API key in Claude Code config');
154
+ return apiKey;
155
+ }
156
+
157
+ // Method 3: Check macOS keychain
158
+ apiKey = await getFromKeychain();
159
+ if (apiKey) {
160
+ console.log('[claude-key-extractor] ✅ Found API key in macOS keychain');
161
+ return apiKey;
162
+ }
163
+
164
+ // Method 4: Try to read from running process
165
+ apiKey = await getFromRunningProcess();
166
+ if (apiKey) {
167
+ console.log('[claude-key-extractor] ✅ Found API key in running Claude Code process');
168
+ return apiKey;
169
+ }
170
+
171
+ console.log('[claude-key-extractor] ⚠️ Could not find API key');
172
+ console.log('[claude-key-extractor] Please set ANTHROPIC_API_KEY environment variable');
173
+ return null;
174
+ }
175
+
176
+ /**
177
+ * Get API key with fallback message
178
+ */
179
+ export async function getClaudeApiKeyOrPrompt() {
180
+ const apiKey = await extractClaudeApiKey();
181
+
182
+ if (!apiKey) {
183
+ console.log('\n' + '='.repeat(60));
184
+ console.log('ANTHROPIC_API_KEY not found!');
185
+ console.log('='.repeat(60));
186
+ console.log('\nPlease set your API key using one of these methods:\n');
187
+ console.log('1. Environment variable:');
188
+ console.log(' export ANTHROPIC_API_KEY="sk-ant-..."');
189
+ console.log('\n2. Get your key from: https://console.anthropic.com/settings/keys');
190
+ console.log('\n3. If you\'re logged into Claude Code, try:');
191
+ console.log(' claude config show # (if supported)');
192
+ console.log('='.repeat(60) + '\n');
193
+ }
194
+
195
+ return apiKey;
196
+ }
@@ -27,8 +27,13 @@ async function getEncryptionKey(keyPath = DEFAULT_KEY_PATH) {
27
27
  // For Windows: use Credential Manager or fallback to file
28
28
 
29
29
  const platform = process.platform;
30
-
31
- if (platform === 'darwin') {
30
+
31
+ // Only attempt keychain when using the default key path. This allows tests and
32
+ // advanced callers to provide a custom key path without depending on keychain
33
+ // availability/behavior.
34
+ const shouldUseKeychain = keyPath === DEFAULT_KEY_PATH;
35
+
36
+ if (shouldUseKeychain && platform === 'darwin') {
32
37
  // macOS - try to use keychain
33
38
  try {
34
39
  const { execSync } = await import('child_process');