create-walle 0.4.4 → 0.4.6
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.
package/package.json
CHANGED
|
@@ -208,40 +208,52 @@ function handleApi(req, res, url) {
|
|
|
208
208
|
return;
|
|
209
209
|
}
|
|
210
210
|
if (url.pathname === '/api/setup/detect-key' && req.method === 'GET') {
|
|
211
|
+
// Detect API credentials from external sources (NOT .env — that's what we're trying to populate).
|
|
212
|
+
// Priority: 1) devbox auth file, 2) shell environment, 3) Claude Code keychain
|
|
213
|
+
const { execFileSync } = require('child_process');
|
|
211
214
|
let key = '';
|
|
212
215
|
let source = '';
|
|
213
|
-
let gateway = null;
|
|
214
|
-
|
|
215
|
-
// 1. Check for corporate gateway setup (Portkey, cybertron, etc.)
|
|
216
|
-
if (process.env.ANTHROPIC_BASE_URL && process.env.ANTHROPIC_CUSTOM_HEADERS_B64) {
|
|
217
|
-
gateway = {
|
|
218
|
-
base_url: process.env.ANTHROPIC_BASE_URL,
|
|
219
|
-
auth_token: process.env.ANTHROPIC_AUTH_TOKEN || 'sk-ant-api03-unused',
|
|
220
|
-
custom_headers_b64: process.env.ANTHROPIC_CUSTOM_HEADERS_B64,
|
|
221
|
-
};
|
|
222
|
-
source = 'Claude Code gateway (' + process.env.ANTHROPIC_BASE_URL.replace(/https?:\/\//, '').split('/')[0] + ')';
|
|
223
|
-
}
|
|
216
|
+
let gateway = null;
|
|
224
217
|
|
|
225
|
-
//
|
|
226
|
-
if (
|
|
227
|
-
|
|
228
|
-
|
|
218
|
+
// 1. Devbox corporate gateway (reads auth headers file directly)
|
|
219
|
+
if (process.platform === 'darwin') {
|
|
220
|
+
try {
|
|
221
|
+
const authFile = path.join(process.env.HOME, '.devbox', 'secrets', 'claude', 'auth_headers');
|
|
222
|
+
const headers = fs.readFileSync(authFile, 'utf8').trim();
|
|
223
|
+
if (headers) {
|
|
224
|
+
let baseUrl = '';
|
|
225
|
+
try {
|
|
226
|
+
const claudeScript = fs.readFileSync(path.join(process.env.HOME, '.devbox', 'ai', 'claude', 'claude'), 'utf8');
|
|
227
|
+
const m = claudeScript.match(/VPN_CHECK_URL="(https?:\/\/[^"]+)"/);
|
|
228
|
+
if (m) baseUrl = m[1] + '/v1';
|
|
229
|
+
} catch {}
|
|
230
|
+
if (baseUrl) {
|
|
231
|
+
gateway = {
|
|
232
|
+
base_url: baseUrl,
|
|
233
|
+
auth_token: 'sk-ant-api03-unused',
|
|
234
|
+
custom_headers_b64: Buffer.from(headers).toString('base64'),
|
|
235
|
+
};
|
|
236
|
+
source = 'Claude Code devbox (' + baseUrl.replace(/https?:\/\//, '').split('/')[0] + ')';
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
} catch {}
|
|
229
240
|
}
|
|
230
241
|
|
|
231
|
-
//
|
|
242
|
+
// 2. Shell environment (fresh from login shell, not inherited .env)
|
|
232
243
|
if (!gateway && !key) {
|
|
233
244
|
try {
|
|
234
|
-
const { execFileSync } = require('child_process');
|
|
235
245
|
const shell = process.env.SHELL || '/bin/zsh';
|
|
236
|
-
const result = execFileSync(shell, ['-ilc', 'echo $ANTHROPIC_API_KEY'], { encoding: 'utf8', timeout:
|
|
237
|
-
if (result && result.
|
|
246
|
+
const result = execFileSync(shell, ['-ilc', 'echo "$ANTHROPIC_API_KEY"'], { encoding: 'utf8', timeout: 5000 }).trim();
|
|
247
|
+
if (result && result.length > 5 && result !== 'sk-ant-api03-unused') {
|
|
248
|
+
key = result;
|
|
249
|
+
source = 'shell environment';
|
|
250
|
+
}
|
|
238
251
|
} catch {}
|
|
239
252
|
}
|
|
240
253
|
|
|
241
|
-
//
|
|
254
|
+
// 3. Claude Code OAuth token from macOS Keychain
|
|
242
255
|
if (!gateway && !key && process.platform === 'darwin') {
|
|
243
256
|
try {
|
|
244
|
-
const { execFileSync } = require('child_process');
|
|
245
257
|
const credJson = execFileSync('security', ['find-generic-password', '-s', 'Claude Code-credentials', '-w'], { encoding: 'utf8', timeout: 3000 }).trim();
|
|
246
258
|
if (credJson) {
|
|
247
259
|
const cred = JSON.parse(credJson);
|
|
@@ -253,11 +265,7 @@ function handleApi(req, res, url) {
|
|
|
253
265
|
source = 'Claude Code (OAuth)';
|
|
254
266
|
} else {
|
|
255
267
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
256
|
-
res.end(JSON.stringify({
|
|
257
|
-
found: false,
|
|
258
|
-
claude_code_expired: true,
|
|
259
|
-
hint: 'Found Claude Code, but the OAuth token has expired. Run "claude" in your terminal once to refresh it, then click Detect again.'
|
|
260
|
-
}));
|
|
268
|
+
res.end(JSON.stringify({ found: false, claude_code_expired: true, hint: 'Found Claude Code, but the OAuth token has expired. Run "claude" in your terminal once to refresh it, then click Detect again.' }));
|
|
261
269
|
return;
|
|
262
270
|
}
|
|
263
271
|
}
|
|
@@ -271,7 +279,7 @@ function handleApi(req, res, url) {
|
|
|
271
279
|
} else if (key) {
|
|
272
280
|
res.end(JSON.stringify({ found: true, key, source }));
|
|
273
281
|
} else {
|
|
274
|
-
res.end(JSON.stringify({ found: false, hint: 'No API key found. Checked:
|
|
282
|
+
res.end(JSON.stringify({ found: false, hint: 'No API key found. Checked: devbox auth, shell environment, Claude Code keychain. Enter your key manually or get one at console.anthropic.com' }));
|
|
275
283
|
}
|
|
276
284
|
return;
|
|
277
285
|
}
|
|
@@ -305,16 +313,21 @@ function handleApi(req, res, url) {
|
|
|
305
313
|
// Accept any non-empty key (Anthropic, Portkey, or other providers)
|
|
306
314
|
const envPath = path.resolve(__dirname, '..', '.env');
|
|
307
315
|
const lines = [];
|
|
308
|
-
// Build set of keys
|
|
316
|
+
// Build set of keys to strip — API key and gateway are mutually exclusive
|
|
309
317
|
const keysToReplace = new Set();
|
|
310
318
|
if (ownerName) keysToReplace.add('WALLE_OWNER_NAME');
|
|
311
|
-
if (apiKey)
|
|
312
|
-
|
|
319
|
+
if (apiKey || gw) {
|
|
320
|
+
// Always strip both — they're mutually exclusive
|
|
321
|
+
keysToReplace.add('ANTHROPIC_API_KEY');
|
|
322
|
+
keysToReplace.add('ANTHROPIC_BASE_URL');
|
|
323
|
+
keysToReplace.add('ANTHROPIC_AUTH_TOKEN');
|
|
324
|
+
keysToReplace.add('ANTHROPIC_CUSTOM_HEADERS_B64');
|
|
325
|
+
}
|
|
313
326
|
// Read existing .env, keep lines that aren't being replaced
|
|
314
327
|
try {
|
|
315
328
|
const existing = fs.readFileSync(envPath, 'utf8');
|
|
316
329
|
for (const line of existing.split('\n')) {
|
|
317
|
-
const m = line.match(/^\s*#?\s*([A-
|
|
330
|
+
const m = line.match(/^\s*#?\s*([A-Z0-9_]+)\s*=/);
|
|
318
331
|
if (m && keysToReplace.has(m[1])) continue; // skip — will re-add below
|
|
319
332
|
lines.push(line);
|
|
320
333
|
}
|
|
@@ -328,16 +341,21 @@ function handleApi(req, res, url) {
|
|
|
328
341
|
process.env.WALLE_OWNER_NAME = ownerName;
|
|
329
342
|
}
|
|
330
343
|
if (gw) {
|
|
331
|
-
// Gateway setup: save
|
|
344
|
+
// Gateway setup: save gateway vars, clear direct API key
|
|
332
345
|
lines.push(`ANTHROPIC_BASE_URL=${gw.base_url}`);
|
|
333
346
|
lines.push(`ANTHROPIC_AUTH_TOKEN=${gw.auth_token}`);
|
|
334
347
|
lines.push(`ANTHROPIC_CUSTOM_HEADERS_B64=${gw.custom_headers_b64}`);
|
|
335
348
|
process.env.ANTHROPIC_BASE_URL = gw.base_url;
|
|
336
349
|
process.env.ANTHROPIC_AUTH_TOKEN = gw.auth_token;
|
|
337
350
|
process.env.ANTHROPIC_CUSTOM_HEADERS_B64 = gw.custom_headers_b64;
|
|
351
|
+
delete process.env.ANTHROPIC_API_KEY;
|
|
338
352
|
} else if (apiKey) {
|
|
353
|
+
// Direct API key: save key, clear gateway vars
|
|
339
354
|
lines.push(`ANTHROPIC_API_KEY=${apiKey}`);
|
|
340
355
|
process.env.ANTHROPIC_API_KEY = apiKey;
|
|
356
|
+
delete process.env.ANTHROPIC_BASE_URL;
|
|
357
|
+
delete process.env.ANTHROPIC_AUTH_TOKEN;
|
|
358
|
+
delete process.env.ANTHROPIC_CUSTOM_HEADERS_B64;
|
|
341
359
|
}
|
|
342
360
|
fs.writeFileSync(envPath, lines.join('\n') + '\n', { mode: 0o600 });
|
|
343
361
|
setup.clearSetupCache(); // so next / request goes to dashboard
|