wispy-cli 2.7.6 → 2.7.7
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/bin/wispy.mjs +91 -16
- package/core/config.mjs +54 -27
- package/core/onboarding.mjs +23 -2
- package/package.json +1 -1
package/bin/wispy.mjs
CHANGED
|
@@ -265,27 +265,102 @@ if (command === "handoff") {
|
|
|
265
265
|
if (command === "model") {
|
|
266
266
|
try {
|
|
267
267
|
const { loadConfig, saveConfig, detectProvider, PROVIDERS } = await import(join(rootDir, "core/config.mjs"));
|
|
268
|
-
const config = await loadConfig();
|
|
269
|
-
const detected = await detectProvider();
|
|
270
268
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
console.log(`\n Available providers:`);
|
|
283
|
-
for (const [key, p] of Object.entries(PROVIDERS)) {
|
|
284
|
-
console.log(` ${key.padEnd(14)} ${p.defaultModel}`);
|
|
269
|
+
const sub = args[1];
|
|
270
|
+
|
|
271
|
+
// wispy model list → print all providers and their models
|
|
272
|
+
if (sub === "list") {
|
|
273
|
+
console.log("\n Available models by provider:\n");
|
|
274
|
+
for (const [key, p] of Object.entries(PROVIDERS)) {
|
|
275
|
+
console.log(` ${key}`);
|
|
276
|
+
const models = p.models ?? [p.defaultModel];
|
|
277
|
+
for (const m of models) {
|
|
278
|
+
const tag = m === p.defaultModel ? " (default)" : "";
|
|
279
|
+
console.log(` ${m}${tag}`);
|
|
285
280
|
}
|
|
286
281
|
}
|
|
287
282
|
console.log("");
|
|
283
|
+
process.exit(0);
|
|
288
284
|
}
|
|
285
|
+
|
|
286
|
+
// wispy model <name> → set directly
|
|
287
|
+
if (sub && sub !== "list") {
|
|
288
|
+
const config = await loadConfig();
|
|
289
|
+
config.model = sub;
|
|
290
|
+
await saveConfig(config);
|
|
291
|
+
console.log(`Model set to: ${sub}`);
|
|
292
|
+
process.exit(0);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// wispy model (no args) → show current + interactive picker
|
|
296
|
+
const { select } = await import("@inquirer/prompts");
|
|
297
|
+
const config = await loadConfig();
|
|
298
|
+
const detected = await detectProvider();
|
|
299
|
+
|
|
300
|
+
console.log(`\n Current provider: ${detected?.provider ?? "none"}`);
|
|
301
|
+
console.log(` Current model: ${detected?.model ?? "none"}\n`);
|
|
302
|
+
|
|
303
|
+
// Find providers with configured keys (or local providers)
|
|
304
|
+
const availableProviders = Object.entries(PROVIDERS).filter(([key, p]) => {
|
|
305
|
+
if (p.local) return true;
|
|
306
|
+
// Check env keys
|
|
307
|
+
for (const k of (p.envKeys ?? [])) {
|
|
308
|
+
if (process.env[k]) return true;
|
|
309
|
+
}
|
|
310
|
+
// Check config
|
|
311
|
+
if (config.provider === key && config.apiKey) return true;
|
|
312
|
+
if (config.providers?.[key]) return true;
|
|
313
|
+
return false;
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
let providerChoices = availableProviders.map(([key, p]) => ({
|
|
317
|
+
name: p.label ?? key,
|
|
318
|
+
value: key,
|
|
319
|
+
}));
|
|
320
|
+
|
|
321
|
+
// If no providers detected, show all
|
|
322
|
+
if (providerChoices.length === 0) {
|
|
323
|
+
providerChoices = Object.entries(PROVIDERS).map(([key, p]) => ({
|
|
324
|
+
name: p.label ?? key,
|
|
325
|
+
value: key,
|
|
326
|
+
}));
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
let selectedProvider;
|
|
330
|
+
try {
|
|
331
|
+
selectedProvider = await select({
|
|
332
|
+
message: "Select provider:",
|
|
333
|
+
choices: providerChoices,
|
|
334
|
+
default: detected?.provider,
|
|
335
|
+
});
|
|
336
|
+
} catch {
|
|
337
|
+
console.log(" Cancelled.");
|
|
338
|
+
process.exit(0);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const providerInfo = PROVIDERS[selectedProvider];
|
|
342
|
+
const modelList = providerInfo.models ?? [providerInfo.defaultModel];
|
|
343
|
+
|
|
344
|
+
let selectedModel;
|
|
345
|
+
try {
|
|
346
|
+
selectedModel = await select({
|
|
347
|
+
message: "Select model:",
|
|
348
|
+
choices: modelList.map((m) => ({
|
|
349
|
+
name: m === providerInfo.defaultModel ? `${m} (default)` : m,
|
|
350
|
+
value: m,
|
|
351
|
+
})),
|
|
352
|
+
default: providerInfo.defaultModel,
|
|
353
|
+
});
|
|
354
|
+
} catch {
|
|
355
|
+
console.log(" Cancelled.");
|
|
356
|
+
process.exit(0);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
config.provider = selectedProvider;
|
|
360
|
+
config.model = selectedModel;
|
|
361
|
+
await saveConfig(config);
|
|
362
|
+
console.log(`\n Provider set to: ${selectedProvider}`);
|
|
363
|
+
console.log(` Model set to: ${selectedModel}\n`);
|
|
289
364
|
} catch (err) {
|
|
290
365
|
console.error("Model error:", err.message);
|
|
291
366
|
process.exit(1);
|
package/core/config.mjs
CHANGED
|
@@ -19,47 +19,74 @@ export const MEMORY_DIR = path.join(WISPY_DIR, "memory");
|
|
|
19
19
|
|
|
20
20
|
export const PROVIDERS = {
|
|
21
21
|
// ── Tier 0: Custom API providers ────────────────────────────────────────────
|
|
22
|
-
google: { envKeys: ["GOOGLE_AI_KEY", "GOOGLE_GENERATIVE_AI_KEY", "GEMINI_API_KEY"], defaultModel: "gemini-2.5-flash", label: "Google AI (Gemini)", signupUrl: "https://aistudio.google.com/apikey"
|
|
23
|
-
|
|
22
|
+
google: { envKeys: ["GOOGLE_AI_KEY", "GOOGLE_GENERATIVE_AI_KEY", "GEMINI_API_KEY"], defaultModel: "gemini-2.5-flash", label: "Google AI (Gemini)", signupUrl: "https://aistudio.google.com/apikey",
|
|
23
|
+
models: ["gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.0-flash", "gemini-2.0-flash-lite", "gemini-1.5-pro", "gemini-1.5-flash"] },
|
|
24
|
+
anthropic: { envKeys: ["ANTHROPIC_API_KEY"], defaultModel: "claude-sonnet-4-20250514", label: "Anthropic (Claude)", signupUrl: "https://console.anthropic.com/settings/keys",
|
|
25
|
+
models: ["claude-opus-4-20250514", "claude-sonnet-4-20250514", "claude-3-5-sonnet-20241022", "claude-3-5-haiku-20241022", "claude-3-opus-20240229"] },
|
|
24
26
|
|
|
25
27
|
// ── Tier 1: Popular OpenAI-compat ──────────────────────────────────────────
|
|
26
|
-
openai: { envKeys: ["OPENAI_API_KEY"], defaultModel: "gpt-4o", label: "OpenAI", signupUrl: "https://platform.openai.com/api-keys"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
openai: { envKeys: ["OPENAI_API_KEY"], defaultModel: "gpt-4o", label: "OpenAI", signupUrl: "https://platform.openai.com/api-keys",
|
|
29
|
+
models: ["gpt-4o", "gpt-4o-mini", "gpt-4-turbo", "o1", "o1-mini", "o3", "o3-mini", "o4-mini"] },
|
|
30
|
+
xai: { envKeys: ["XAI_API_KEY"], defaultModel: "grok-3-mini", label: "xAI (Grok)", signupUrl: "https://console.x.ai/",
|
|
31
|
+
models: ["grok-3", "grok-3-mini", "grok-3-fast", "grok-2", "grok-2-mini"] },
|
|
32
|
+
mistral: { envKeys: ["MISTRAL_API_KEY"], defaultModel: "mistral-large-latest", label: "Mistral", signupUrl: "https://console.mistral.ai/api-keys/",
|
|
33
|
+
models: ["mistral-large-latest", "mistral-medium-latest", "mistral-small-latest", "codestral-latest", "open-mistral-nemo"] },
|
|
34
|
+
together: { envKeys: ["TOGETHER_API_KEY"], defaultModel: "meta-llama/Llama-3.3-70B-Instruct-Turbo", label: "Together AI", signupUrl: "https://api.together.xyz/settings/api-keys",
|
|
35
|
+
models: ["meta-llama/Llama-3.3-70B-Instruct-Turbo", "meta-llama/Llama-3.1-8B-Instruct-Turbo", "mistralai/Mixtral-8x22B-Instruct-v0.1", "Qwen/Qwen2.5-72B-Instruct-Turbo", "deepseek-ai/deepseek-r1"] },
|
|
36
|
+
nvidia: { envKeys: ["NVIDIA_API_KEY"], defaultModel: "meta/llama-3.3-70b-instruct", label: "NVIDIA NIM", signupUrl: "https://build.nvidia.com/",
|
|
37
|
+
models: ["meta/llama-3.3-70b-instruct", "meta/llama-3.1-8b-instruct", "mistralai/mistral-7b-instruct-v0.3", "google/gemma-2-9b-it"] },
|
|
31
38
|
|
|
32
39
|
// ── Tier 1: Fast/free ───────────────────────────────────────────────────────
|
|
33
|
-
groq: { envKeys: ["GROQ_API_KEY"], defaultModel: "llama-3.3-70b-versatile", label: "Groq (fast inference)", signupUrl: "https://console.groq.com/keys"
|
|
34
|
-
|
|
35
|
-
|
|
40
|
+
groq: { envKeys: ["GROQ_API_KEY"], defaultModel: "llama-3.3-70b-versatile", label: "Groq (fast inference)", signupUrl: "https://console.groq.com/keys",
|
|
41
|
+
models: ["llama-3.3-70b-versatile", "llama-3.1-8b-instant", "mixtral-8x7b-32768", "gemma2-9b-it", "llama-3.3-70b-specdec"] },
|
|
42
|
+
deepseek: { envKeys: ["DEEPSEEK_API_KEY"], defaultModel: "deepseek-chat", label: "DeepSeek", signupUrl: "https://platform.deepseek.com/api_keys",
|
|
43
|
+
models: ["deepseek-chat", "deepseek-coder", "deepseek-reasoner"] },
|
|
44
|
+
chutes: { envKeys: ["CHUTES_API_KEY"], defaultModel: "deepseek-ai/DeepSeek-V3-0324", label: "Chutes", signupUrl: "https://chutes.ai/",
|
|
45
|
+
models: ["deepseek-ai/DeepSeek-V3-0324", "deepseek-ai/DeepSeek-R1", "Qwen/Qwen3-235B-A22B"] },
|
|
36
46
|
|
|
37
47
|
// ── Tier 1: Aggregators ─────────────────────────────────────────────────────
|
|
38
|
-
openrouter: { envKeys: ["OPENROUTER_API_KEY"], defaultModel: "anthropic/claude-sonnet-4-20250514", label: "OpenRouter (multi-model)", signupUrl: "https://openrouter.ai/keys"
|
|
39
|
-
|
|
48
|
+
openrouter: { envKeys: ["OPENROUTER_API_KEY"], defaultModel: "anthropic/claude-sonnet-4-20250514", label: "OpenRouter (multi-model)", signupUrl: "https://openrouter.ai/keys",
|
|
49
|
+
models: ["anthropic/claude-sonnet-4-20250514", "anthropic/claude-opus-4", "openai/gpt-4o", "openai/o3", "google/gemini-2.5-pro", "google/gemini-2.5-flash", "meta-llama/llama-3.3-70b-instruct", "deepseek/deepseek-r1"] },
|
|
50
|
+
vercelai: { envKeys: ["VERCEL_AI_TOKEN"], defaultModel: "anthropic/claude-3-5-sonnet", label: "Vercel AI Gateway", signupUrl: "https://vercel.com/docs/ai-gateway",
|
|
51
|
+
models: ["anthropic/claude-3-5-sonnet", "anthropic/claude-3-5-haiku", "openai/gpt-4o", "openai/gpt-4o-mini"] },
|
|
40
52
|
|
|
41
53
|
// ── Tier 2: Useful ──────────────────────────────────────────────────────────
|
|
42
|
-
kimi: { envKeys: ["MOONSHOT_API_KEY", "KIMI_API_KEY"], defaultModel: "moonshot-v1-8k", label: "Kimi/Moonshot", signupUrl: "https://platform.moonshot.cn/console/api-keys"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
54
|
+
kimi: { envKeys: ["MOONSHOT_API_KEY", "KIMI_API_KEY"], defaultModel: "moonshot-v1-8k", label: "Kimi/Moonshot", signupUrl: "https://platform.moonshot.cn/console/api-keys",
|
|
55
|
+
models: ["moonshot-v1-8k", "moonshot-v1-32k", "moonshot-v1-128k"] },
|
|
56
|
+
minimax: { envKeys: ["MINIMAX_API_KEY"], defaultModel: "MiniMax-Text-01", label: "MiniMax", signupUrl: "https://www.minimaxi.com/",
|
|
57
|
+
models: ["MiniMax-Text-01", "abab6.5s-chat", "abab6.5g-chat"] },
|
|
58
|
+
venice: { envKeys: ["VENICE_API_KEY"], defaultModel: "llama-3.3-70b", label: "Venice AI", signupUrl: "https://venice.ai/chat/api",
|
|
59
|
+
models: ["llama-3.3-70b", "mistral-31-24b", "deepseek-r1-671b"] },
|
|
60
|
+
huggingface: { envKeys: ["HF_TOKEN", "HUGGINGFACE_API_KEY"], defaultModel: "meta-llama/Llama-3.3-70B-Instruct", label: "Hugging Face", signupUrl: "https://huggingface.co/settings/tokens", special: "hf_model_in_url",
|
|
61
|
+
models: ["meta-llama/Llama-3.3-70B-Instruct", "meta-llama/Llama-3.1-8B-Instruct", "mistralai/Mistral-7B-Instruct-v0.3", "Qwen/Qwen2.5-72B-Instruct"] },
|
|
62
|
+
cloudflare: { envKeys: ["CF_API_TOKEN"], defaultModel: "@cf/meta/llama-3.3-70b-instruct-fp8-fast", label: "Cloudflare AI", signupUrl: "https://dash.cloudflare.com/", special: "cf_account_id",
|
|
63
|
+
models: ["@cf/meta/llama-3.3-70b-instruct-fp8-fast", "@cf/mistral/mistral-7b-instruct-v0.2", "@cf/google/gemma-7b-it", "@cf/qwen/qwen1.5-14b-chat-awq"] },
|
|
47
64
|
|
|
48
65
|
// ── Tier 3: Regional / niche ────────────────────────────────────────────────
|
|
49
|
-
volcengine: { envKeys: ["VOLCENGINE_API_KEY", "ARK_API_KEY"], defaultModel: "doubao-pro-32k", label: "Volcengine (ByteDance)", signupUrl: "https://console.volcengine.com/ark"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
66
|
+
volcengine: { envKeys: ["VOLCENGINE_API_KEY", "ARK_API_KEY"], defaultModel: "doubao-pro-32k", label: "Volcengine (ByteDance)", signupUrl: "https://console.volcengine.com/ark",
|
|
67
|
+
models: ["doubao-pro-32k", "doubao-pro-128k", "doubao-lite-32k"] },
|
|
68
|
+
byteplus: { envKeys: ["BYTEPLUS_API_KEY"], defaultModel: "doubao-pro-32k", label: "BytePlus", signupUrl: "https://console.byteplux.com/",
|
|
69
|
+
models: ["doubao-pro-32k", "doubao-lite-32k"] },
|
|
70
|
+
zai: { envKeys: ["ZAI_API_KEY", "GLM_API_KEY", "ZHIPU_API_KEY"], defaultModel: "glm-4-flash", label: "Z.AI / GLM", signupUrl: "https://open.bigmodel.cn/",
|
|
71
|
+
models: ["glm-4-flash", "glm-4-plus", "glm-4-long", "glm-z1-flash"] },
|
|
72
|
+
dashscope: { envKeys: ["DASHSCOPE_API_KEY"], defaultModel: "qwen-max", label: "DashScope (Alibaba)", signupUrl: "https://dashscope.aliyun.com/",
|
|
73
|
+
models: ["qwen-max", "qwen-plus", "qwen-turbo", "qwen-long", "qwen2.5-72b-instruct"] },
|
|
74
|
+
xiaomi: { envKeys: ["XIAOMI_API_KEY"], defaultModel: "MiMo-7B-RL", label: "Xiaomi AI", signupUrl: "https://ai.xiaomi.com/",
|
|
75
|
+
models: ["MiMo-7B-RL", "MiMo-7B-SFT"] },
|
|
54
76
|
|
|
55
77
|
// ── OAuth / subscription-based ────────────────────────────────────────────
|
|
56
|
-
"github-copilot": { envKeys: ["GITHUB_COPILOT_TOKEN"], defaultModel: "gpt-4o", label: "GitHub Copilot", signupUrl: "https://github.com/features/copilot", auth: "oauth"
|
|
78
|
+
"github-copilot": { envKeys: ["GITHUB_COPILOT_TOKEN"], defaultModel: "gpt-4o", label: "GitHub Copilot", signupUrl: "https://github.com/features/copilot", auth: "oauth",
|
|
79
|
+
models: ["gpt-4o", "gpt-4o-mini", "claude-sonnet-4-20250514", "o3-mini"] },
|
|
57
80
|
|
|
58
81
|
// ── Local / self-hosted ─────────────────────────────────────────────────────
|
|
59
|
-
ollama: { envKeys: [], defaultModel: "llama3.2", label: "Ollama (local)", signupUrl: null, local: true
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
82
|
+
ollama: { envKeys: [], defaultModel: "llama3.2", label: "Ollama (local)", signupUrl: null, local: true,
|
|
83
|
+
models: ["llama3.2", "llama3.1", "llama3.1:70b", "mistral", "mixtral", "deepseek-r1", "qwen2.5", "phi4", "gemma3"] },
|
|
84
|
+
vllm: { envKeys: [], defaultModel: "meta-llama/Llama-3.3-70B-Instruct", label: "vLLM (self-hosted)", signupUrl: "https://docs.vllm.ai/", local: true,
|
|
85
|
+
models: ["meta-llama/Llama-3.3-70B-Instruct", "meta-llama/Llama-3.1-8B-Instruct", "mistralai/Mistral-7B-Instruct-v0.3", "Qwen/Qwen2.5-72B-Instruct"] },
|
|
86
|
+
sglang: { envKeys: [], defaultModel: "meta-llama/Llama-3.3-70B-Instruct", label: "SGLang (self-hosted)", signupUrl: "https://docs.sglang.ai/", local: true,
|
|
87
|
+
models: ["meta-llama/Llama-3.3-70B-Instruct", "meta-llama/Llama-3.1-8B-Instruct", "deepseek-ai/DeepSeek-V3", "Qwen/Qwen2.5-72B-Instruct"] },
|
|
88
|
+
litellm: { envKeys: ["LITELLM_API_KEY"], defaultModel: "gpt-4o", label: "LiteLLM (proxy)", signupUrl: "https://docs.litellm.ai/", local: true,
|
|
89
|
+
models: ["gpt-4o", "claude-sonnet-4-20250514", "gemini/gemini-2.5-flash", "bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0"] },
|
|
63
90
|
};
|
|
64
91
|
|
|
65
92
|
async function tryKeychainKey(service) {
|
package/core/onboarding.mjs
CHANGED
|
@@ -569,10 +569,31 @@ export class OnboardingWizard {
|
|
|
569
569
|
const result = await validateApiKey(provKey, apiKey.trim());
|
|
570
570
|
|
|
571
571
|
if (result.ok) {
|
|
572
|
-
console.log(green(` ✅ Connected
|
|
572
|
+
console.log(green(` ✅ Connected!`));
|
|
573
|
+
|
|
574
|
+
// Model selection step
|
|
575
|
+
const provInfo = PROVIDERS[provKey];
|
|
576
|
+
const modelList = provInfo?.models ?? [getProviderDefaultModel(provKey)];
|
|
577
|
+
let chosenModel = result.model ?? getProviderDefaultModel(provKey);
|
|
578
|
+
if (modelList.length > 1) {
|
|
579
|
+
try {
|
|
580
|
+
chosenModel = await select({
|
|
581
|
+
message: ` Select model for ${info?.label ?? provKey}:`,
|
|
582
|
+
choices: modelList.map((m) => ({
|
|
583
|
+
name: m === getProviderDefaultModel(provKey) ? `${m} (default)` : m,
|
|
584
|
+
value: m,
|
|
585
|
+
})),
|
|
586
|
+
default: chosenModel,
|
|
587
|
+
});
|
|
588
|
+
} catch {
|
|
589
|
+
// keep default
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
console.log(dim(` Using model: ${chosenModel}`));
|
|
593
|
+
|
|
573
594
|
providers[provKey] = {
|
|
574
595
|
apiKey: apiKey.trim(),
|
|
575
|
-
model:
|
|
596
|
+
model: chosenModel,
|
|
576
597
|
};
|
|
577
598
|
validated = true;
|
|
578
599
|
} else {
|