create-walle 0.3.1 → 0.3.3
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
|
@@ -159,13 +159,6 @@
|
|
|
159
159
|
btn.disabled = false;
|
|
160
160
|
return;
|
|
161
161
|
}
|
|
162
|
-
if (apiVal && !apiVal.startsWith('sk-ant-')) {
|
|
163
|
-
errMsg.textContent = 'API key should start with sk-ant-';
|
|
164
|
-
errMsg.style.display = 'inline';
|
|
165
|
-
btn.disabled = false;
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
162
|
const body = { owner_name: ownerVal, api_key: apiVal };
|
|
170
163
|
|
|
171
164
|
try {
|
|
@@ -243,13 +236,16 @@
|
|
|
243
236
|
document.getElementById('api-key').value = '';
|
|
244
237
|
document.getElementById('api-key').placeholder = '••••••••••••••• (from ' + (d.source || 'environment') + ')';
|
|
245
238
|
document.getElementById('api-dot').className = 'status-dot ok';
|
|
246
|
-
okMsg.textContent = '
|
|
239
|
+
okMsg.textContent = 'Detected: ' + (d.source || 'environment') + '!';
|
|
247
240
|
okMsg.style.display = 'inline';
|
|
248
241
|
const ownerVal = document.getElementById('owner-name').value.trim();
|
|
242
|
+
const saveBody = { owner_name: ownerVal };
|
|
243
|
+
if (d.gateway) saveBody.gateway = d.gateway;
|
|
244
|
+
else saveBody.api_key = d.key;
|
|
249
245
|
await fetch('/api/setup/save', {
|
|
250
246
|
method: 'POST',
|
|
251
247
|
headers: { 'Content-Type': 'application/json' },
|
|
252
|
-
body: JSON.stringify(
|
|
248
|
+
body: JSON.stringify(saveBody),
|
|
253
249
|
});
|
|
254
250
|
} else {
|
|
255
251
|
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,29 +311,45 @@ 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
|
: '';
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
314
|
+
// Gateway config (corporate Portkey/cybertron setups)
|
|
315
|
+
const gw = data.gateway;
|
|
316
|
+
// Accept any non-empty key (Anthropic, Portkey, or other providers)
|
|
309
317
|
const envPath = path.resolve(__dirname, '..', '.env');
|
|
310
318
|
const lines = [];
|
|
311
|
-
// Read existing .env
|
|
319
|
+
// Read existing .env, strip lines we're about to replace
|
|
320
|
+
const stripPatterns = [/^#?\s*WALLE_OWNER_NAME=/, /^#?\s*ANTHROPIC_API_KEY=/, /^#?\s*ANTHROPIC_BASE_URL=/, /^#?\s*ANTHROPIC_AUTH_TOKEN=/, /^#?\s*ANTHROPIC_CUSTOM_HEADERS_B64=/];
|
|
312
321
|
try {
|
|
313
322
|
const existing = fs.readFileSync(envPath, 'utf8');
|
|
314
323
|
for (const line of existing.split('\n')) {
|
|
315
|
-
|
|
316
|
-
if (
|
|
317
|
-
|
|
324
|
+
const shouldStrip = (apiKey || gw) && stripPatterns.some(p => p.test(line));
|
|
325
|
+
if (!shouldStrip || (!apiKey && !gw)) lines.push(line);
|
|
326
|
+
else if (!ownerName || !line.match(/WALLE_OWNER_NAME/)) {
|
|
327
|
+
// Only strip if we have a replacement
|
|
328
|
+
if (stripPatterns.some(p => p.test(line))) continue;
|
|
329
|
+
lines.push(line);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
// Also strip owner name line if we have a new one
|
|
333
|
+
if (ownerName) {
|
|
334
|
+
const idx = lines.findIndex(l => /^#?\s*WALLE_OWNER_NAME=/.test(l));
|
|
335
|
+
if (idx >= 0) lines.splice(idx, 1);
|
|
318
336
|
}
|
|
319
337
|
} catch { lines.push('# Wall-E configuration'); lines.push(''); }
|
|
320
|
-
// Add values
|
|
338
|
+
// Add values
|
|
321
339
|
if (ownerName) {
|
|
322
340
|
const insertIdx = lines.findIndex(l => !l.startsWith('#') && l.trim() !== '') || lines.length;
|
|
323
341
|
lines.splice(insertIdx, 0, `WALLE_OWNER_NAME=${ownerName}`);
|
|
324
342
|
process.env.WALLE_OWNER_NAME = ownerName;
|
|
325
343
|
}
|
|
326
|
-
if (
|
|
344
|
+
if (gw) {
|
|
345
|
+
// Gateway setup: save all three env vars
|
|
346
|
+
lines.push(`ANTHROPIC_BASE_URL=${gw.base_url}`);
|
|
347
|
+
lines.push(`ANTHROPIC_AUTH_TOKEN=${gw.auth_token}`);
|
|
348
|
+
lines.push(`ANTHROPIC_CUSTOM_HEADERS_B64=${gw.custom_headers_b64}`);
|
|
349
|
+
process.env.ANTHROPIC_BASE_URL = gw.base_url;
|
|
350
|
+
process.env.ANTHROPIC_AUTH_TOKEN = gw.auth_token;
|
|
351
|
+
process.env.ANTHROPIC_CUSTOM_HEADERS_B64 = gw.custom_headers_b64;
|
|
352
|
+
} else if (apiKey) {
|
|
327
353
|
lines.push(`ANTHROPIC_API_KEY=${apiKey}`);
|
|
328
354
|
process.env.ANTHROPIC_API_KEY = apiKey;
|
|
329
355
|
}
|