create-walle 0.3.1 → 0.3.2
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
|
@@ -243,13 +243,16 @@
|
|
|
243
243
|
document.getElementById('api-key').value = '';
|
|
244
244
|
document.getElementById('api-key').placeholder = '••••••••••••••• (from ' + (d.source || 'environment') + ')';
|
|
245
245
|
document.getElementById('api-dot').className = 'status-dot ok';
|
|
246
|
-
okMsg.textContent = '
|
|
246
|
+
okMsg.textContent = 'Detected: ' + (d.source || 'environment') + '!';
|
|
247
247
|
okMsg.style.display = 'inline';
|
|
248
248
|
const ownerVal = document.getElementById('owner-name').value.trim();
|
|
249
|
+
const saveBody = { owner_name: ownerVal };
|
|
250
|
+
if (d.gateway) saveBody.gateway = d.gateway;
|
|
251
|
+
else saveBody.api_key = d.key;
|
|
249
252
|
await fetch('/api/setup/save', {
|
|
250
253
|
method: 'POST',
|
|
251
254
|
headers: { 'Content-Type': 'application/json' },
|
|
252
|
-
body: JSON.stringify(
|
|
255
|
+
body: JSON.stringify(saveBody),
|
|
253
256
|
});
|
|
254
257
|
} else {
|
|
255
258
|
errMsg.textContent = d.hint || 'No API key found. Enter one manually.';
|
|
@@ -228,15 +228,26 @@ function handleApi(req, res, url) {
|
|
|
228
228
|
if (url.pathname === '/api/setup/detect-key' && req.method === 'GET') {
|
|
229
229
|
let key = '';
|
|
230
230
|
let source = '';
|
|
231
|
+
let gateway = null; // For corporate/Portkey gateway setups
|
|
232
|
+
|
|
233
|
+
// 1. Check for corporate gateway setup (Portkey, cybertron, etc.)
|
|
234
|
+
if (process.env.ANTHROPIC_BASE_URL && process.env.ANTHROPIC_CUSTOM_HEADERS_B64) {
|
|
235
|
+
gateway = {
|
|
236
|
+
base_url: process.env.ANTHROPIC_BASE_URL,
|
|
237
|
+
auth_token: process.env.ANTHROPIC_AUTH_TOKEN || 'sk-ant-api03-unused',
|
|
238
|
+
custom_headers_b64: process.env.ANTHROPIC_CUSTOM_HEADERS_B64,
|
|
239
|
+
};
|
|
240
|
+
source = 'Claude Code gateway (' + process.env.ANTHROPIC_BASE_URL.replace(/https?:\/\//, '').split('/')[0] + ')';
|
|
241
|
+
}
|
|
231
242
|
|
|
232
|
-
//
|
|
233
|
-
if (process.env.ANTHROPIC_API_KEY && process.env.ANTHROPIC_API_KEY.startsWith('sk-ant-')) {
|
|
243
|
+
// 2. Check for direct API key in process.env
|
|
244
|
+
if (!gateway && process.env.ANTHROPIC_API_KEY && process.env.ANTHROPIC_API_KEY.startsWith('sk-ant-')) {
|
|
234
245
|
key = process.env.ANTHROPIC_API_KEY;
|
|
235
246
|
source = 'environment variable';
|
|
236
247
|
}
|
|
237
248
|
|
|
238
|
-
//
|
|
239
|
-
if (!key) {
|
|
249
|
+
// 3. Try shell profile for direct API key
|
|
250
|
+
if (!gateway && !key) {
|
|
240
251
|
try {
|
|
241
252
|
const { execFileSync } = require('child_process');
|
|
242
253
|
const shell = process.env.SHELL || '/bin/zsh';
|
|
@@ -245,8 +256,8 @@ function handleApi(req, res, url) {
|
|
|
245
256
|
} catch {}
|
|
246
257
|
}
|
|
247
258
|
|
|
248
|
-
//
|
|
249
|
-
if (!key && process.platform === 'darwin') {
|
|
259
|
+
// 4. Try Claude Code OAuth token from macOS Keychain
|
|
260
|
+
if (!gateway && !key && process.platform === 'darwin') {
|
|
250
261
|
try {
|
|
251
262
|
const { execFileSync } = require('child_process');
|
|
252
263
|
const credJson = execFileSync('security', ['find-generic-password', '-s', 'Claude Code-credentials', '-w'], { encoding: 'utf8', timeout: 3000 }).trim();
|
|
@@ -259,7 +270,6 @@ function handleApi(req, res, url) {
|
|
|
259
270
|
key = oauth.accessToken;
|
|
260
271
|
source = 'Claude Code (OAuth)';
|
|
261
272
|
} else {
|
|
262
|
-
// Token expired — tell user to refresh
|
|
263
273
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
264
274
|
res.end(JSON.stringify({
|
|
265
275
|
found: false,
|
|
@@ -274,10 +284,10 @@ function handleApi(req, res, url) {
|
|
|
274
284
|
}
|
|
275
285
|
|
|
276
286
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
277
|
-
if (
|
|
287
|
+
if (gateway) {
|
|
288
|
+
res.end(JSON.stringify({ found: true, gateway, source }));
|
|
289
|
+
} else if (key) {
|
|
278
290
|
res.end(JSON.stringify({ found: true, key, source }));
|
|
279
|
-
} else if (process.env.ANTHROPIC_BASE_URL) {
|
|
280
|
-
res.end(JSON.stringify({ found: false, hint: 'Your environment uses an API gateway (' + process.env.ANTHROPIC_BASE_URL + '). You may not need a separate API key — try going to the dashboard.' }));
|
|
281
291
|
} else {
|
|
282
292
|
res.end(JSON.stringify({ found: false, hint: 'No API key found. Checked: environment variables, shell profile, Claude Code keychain. You can get a key at console.anthropic.com' }));
|
|
283
293
|
}
|
|
@@ -301,6 +311,8 @@ function handleApi(req, res, url) {
|
|
|
301
311
|
const apiKey = typeof data.api_key === 'string'
|
|
302
312
|
? data.api_key.replace(/[\r\n\s]/g, '').slice(0, 200)
|
|
303
313
|
: '';
|
|
314
|
+
// Gateway config (corporate Portkey/cybertron setups)
|
|
315
|
+
const gw = data.gateway;
|
|
304
316
|
if (apiKey && !/^sk-ant-/.test(apiKey)) {
|
|
305
317
|
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
306
318
|
res.end(JSON.stringify({ error: 'API key must start with sk-ant-' }));
|
|
@@ -308,22 +320,40 @@ function handleApi(req, res, url) {
|
|
|
308
320
|
}
|
|
309
321
|
const envPath = path.resolve(__dirname, '..', '.env');
|
|
310
322
|
const lines = [];
|
|
311
|
-
// Read existing .env
|
|
323
|
+
// Read existing .env, strip lines we're about to replace
|
|
324
|
+
const stripPatterns = [/^#?\s*WALLE_OWNER_NAME=/, /^#?\s*ANTHROPIC_API_KEY=/, /^#?\s*ANTHROPIC_BASE_URL=/, /^#?\s*ANTHROPIC_AUTH_TOKEN=/, /^#?\s*ANTHROPIC_CUSTOM_HEADERS_B64=/];
|
|
312
325
|
try {
|
|
313
326
|
const existing = fs.readFileSync(envPath, 'utf8');
|
|
314
327
|
for (const line of existing.split('\n')) {
|
|
315
|
-
|
|
316
|
-
if (
|
|
317
|
-
|
|
328
|
+
const shouldStrip = (apiKey || gw) && stripPatterns.some(p => p.test(line));
|
|
329
|
+
if (!shouldStrip || (!apiKey && !gw)) lines.push(line);
|
|
330
|
+
else if (!ownerName || !line.match(/WALLE_OWNER_NAME/)) {
|
|
331
|
+
// Only strip if we have a replacement
|
|
332
|
+
if (stripPatterns.some(p => p.test(line))) continue;
|
|
333
|
+
lines.push(line);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
// Also strip owner name line if we have a new one
|
|
337
|
+
if (ownerName) {
|
|
338
|
+
const idx = lines.findIndex(l => /^#?\s*WALLE_OWNER_NAME=/.test(l));
|
|
339
|
+
if (idx >= 0) lines.splice(idx, 1);
|
|
318
340
|
}
|
|
319
341
|
} catch { lines.push('# Wall-E configuration'); lines.push(''); }
|
|
320
|
-
// Add values
|
|
342
|
+
// Add values
|
|
321
343
|
if (ownerName) {
|
|
322
344
|
const insertIdx = lines.findIndex(l => !l.startsWith('#') && l.trim() !== '') || lines.length;
|
|
323
345
|
lines.splice(insertIdx, 0, `WALLE_OWNER_NAME=${ownerName}`);
|
|
324
346
|
process.env.WALLE_OWNER_NAME = ownerName;
|
|
325
347
|
}
|
|
326
|
-
if (
|
|
348
|
+
if (gw) {
|
|
349
|
+
// Gateway setup: save all three env vars
|
|
350
|
+
lines.push(`ANTHROPIC_BASE_URL=${gw.base_url}`);
|
|
351
|
+
lines.push(`ANTHROPIC_AUTH_TOKEN=${gw.auth_token}`);
|
|
352
|
+
lines.push(`ANTHROPIC_CUSTOM_HEADERS_B64=${gw.custom_headers_b64}`);
|
|
353
|
+
process.env.ANTHROPIC_BASE_URL = gw.base_url;
|
|
354
|
+
process.env.ANTHROPIC_AUTH_TOKEN = gw.auth_token;
|
|
355
|
+
process.env.ANTHROPIC_CUSTOM_HEADERS_B64 = gw.custom_headers_b64;
|
|
356
|
+
} else if (apiKey) {
|
|
327
357
|
lines.push(`ANTHROPIC_API_KEY=${apiKey}`);
|
|
328
358
|
process.env.ANTHROPIC_API_KEY = apiKey;
|
|
329
359
|
}
|