wispy-cli 2.3.0 → 2.3.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.
- package/core/migrate.mjs +89 -2
- package/package.json +1 -1
package/core/migrate.mjs
CHANGED
|
@@ -71,6 +71,7 @@ export class OpenClawMigrator {
|
|
|
71
71
|
await this._migrateWorkspaceFiles(dryRun);
|
|
72
72
|
await this._migrateCronJobs(dryRun);
|
|
73
73
|
await this._migrateChannels(dryRun);
|
|
74
|
+
await this._migrateApiKeys(dryRun);
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
return {
|
|
@@ -274,9 +275,11 @@ export class OpenClawMigrator {
|
|
|
274
275
|
} catch { /* empty */ }
|
|
275
276
|
|
|
276
277
|
// Extract Telegram config
|
|
277
|
-
const telegramToken = config.telegram?.
|
|
278
|
+
const telegramToken = config.channels?.telegram?.botToken
|
|
279
|
+
?? config.telegram?.token
|
|
278
280
|
?? config.channels?.telegram?.token
|
|
279
|
-
?? config.plugins?.telegram?.token
|
|
281
|
+
?? config.plugins?.telegram?.token
|
|
282
|
+
?? config.plugins?.entries?.telegram?.config?.botToken;
|
|
280
283
|
|
|
281
284
|
if (telegramToken && !wispyChannels.telegram) {
|
|
282
285
|
wispyChannels.telegram = { token: telegramToken };
|
|
@@ -302,6 +305,83 @@ export class OpenClawMigrator {
|
|
|
302
305
|
}
|
|
303
306
|
}
|
|
304
307
|
|
|
308
|
+
// ── API keys migration ───────────────────────────────────────────────────────
|
|
309
|
+
|
|
310
|
+
async _migrateApiKeys(dryRun) {
|
|
311
|
+
// Read API keys from environment (same as OpenClaw uses)
|
|
312
|
+
const keyMap = {
|
|
313
|
+
google: { env: ["GOOGLE_AI_KEY", "GOOGLE_GENERATIVE_AI_KEY", "GEMINI_API_KEY"], model: "gemini-2.5-flash" },
|
|
314
|
+
anthropic: { env: ["ANTHROPIC_API_KEY"], model: "claude-sonnet-4-20250514" },
|
|
315
|
+
openai: { env: ["OPENAI_API_KEY"], model: "gpt-4o" },
|
|
316
|
+
groq: { env: ["GROQ_API_KEY"], model: "llama-3.3-70b-versatile" },
|
|
317
|
+
deepseek: { env: ["DEEPSEEK_API_KEY"], model: "deepseek-chat" },
|
|
318
|
+
openrouter:{ env: ["OPENROUTER_API_KEY"], model: "anthropic/claude-sonnet-4-20250514" },
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
// Also check macOS keychain
|
|
322
|
+
const { execSync } = await import("node:child_process");
|
|
323
|
+
function keychainGet(service, account) {
|
|
324
|
+
try {
|
|
325
|
+
return execSync(`security find-generic-password -s "${service}" -a "${account}" -w 2>/dev/null`, { encoding: "utf8" }).trim();
|
|
326
|
+
} catch { return null; }
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const keychainMap = {
|
|
330
|
+
google: { service: "google-ai-key", account: "poropo" },
|
|
331
|
+
anthropic: { service: "anthropic-api-key", account: "poropo" },
|
|
332
|
+
openai: { service: "openai-api-key", account: "poropo" },
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
const configPath = join(this.wispyDir, "config.json");
|
|
336
|
+
let config = {};
|
|
337
|
+
try { config = JSON.parse(await readFile(configPath, "utf8")); } catch { /* empty */ }
|
|
338
|
+
if (!config.providers) config.providers = {};
|
|
339
|
+
|
|
340
|
+
let found = 0;
|
|
341
|
+
for (const [provider, info] of Object.entries(keyMap)) {
|
|
342
|
+
if (config.providers[provider]?.apiKey) continue; // already configured
|
|
343
|
+
|
|
344
|
+
// Check env vars
|
|
345
|
+
let key = null;
|
|
346
|
+
for (const envName of info.env) {
|
|
347
|
+
if (process.env[envName]) { key = process.env[envName]; break; }
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Check keychain (macOS)
|
|
351
|
+
if (!key && keychainMap[provider]) {
|
|
352
|
+
key = keychainGet(keychainMap[provider].service, keychainMap[provider].account);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Check ~/.zshenv for exports
|
|
356
|
+
if (!key) {
|
|
357
|
+
try {
|
|
358
|
+
const zshenv = await readFile(join(homedir(), ".zshenv"), "utf8");
|
|
359
|
+
for (const envName of info.env) {
|
|
360
|
+
const match = zshenv.match(new RegExp(`export\\s+${envName}=["']?([^"'\\n]+)`));
|
|
361
|
+
if (match) { key = match[1]; break; }
|
|
362
|
+
}
|
|
363
|
+
} catch { /* no zshenv */ }
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (key) {
|
|
367
|
+
found++;
|
|
368
|
+
this.report.apiKeys = this.report.apiKeys || [];
|
|
369
|
+
this.report.apiKeys.push({ provider, masked: key.slice(0, 6) + "..." + key.slice(-4) });
|
|
370
|
+
|
|
371
|
+
if (!dryRun) {
|
|
372
|
+
config.providers[provider] = { apiKey: key, model: info.model };
|
|
373
|
+
if (!config.defaultProvider) config.defaultProvider = provider;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (found > 0 && !dryRun) {
|
|
379
|
+
config.onboarded = true;
|
|
380
|
+
await mkdir(this.wispyDir, { recursive: true });
|
|
381
|
+
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
305
385
|
// ── Report formatting ─────────────────────────────────────────────────────────
|
|
306
386
|
|
|
307
387
|
formatReport() {
|
|
@@ -345,6 +425,13 @@ export class OpenClawMigrator {
|
|
|
345
425
|
}
|
|
346
426
|
}
|
|
347
427
|
|
|
428
|
+
if (r.apiKeys?.length > 0) {
|
|
429
|
+
lines.push(`🔑 API Keys (${r.apiKeys.length}):`);
|
|
430
|
+
for (const k of r.apiKeys) {
|
|
431
|
+
lines.push(` import: ${k.provider} (${k.masked})`);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
348
435
|
if (r.errors.length > 0) {
|
|
349
436
|
lines.push(`⚠️ Errors (${r.errors.length}):`);
|
|
350
437
|
for (const e of r.errors) {
|