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.
Files changed (2) hide show
  1. package/core/migrate.mjs +89 -2
  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?.token
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wispy-cli",
3
- "version": "2.3.0",
3
+ "version": "2.3.1",
4
4
  "description": "🌿 Wispy — AI workspace assistant with trustworthy execution (harness, receipts, approvals, diffs)",
5
5
  "license": "MIT",
6
6
  "author": "Minseo & Poropo",