@cosmicstack/mercury-agent 1.1.0 → 1.1.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/README.md +4 -0
- package/README.zh-CN.md +240 -0
- package/dist/index.js +205 -42
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -90,6 +90,20 @@ function getDefaultConfig() {
|
|
|
90
90
|
baseUrl: getEnv("OLLAMA_LOCAL_BASE_URL", "http://127.0.0.1:11434/api"),
|
|
91
91
|
model: getEnv("OLLAMA_LOCAL_MODEL", "gpt-oss:20b"),
|
|
92
92
|
enabled: getEnvBool("OLLAMA_LOCAL_ENABLED", false)
|
|
93
|
+
},
|
|
94
|
+
mimo: {
|
|
95
|
+
name: "mimo",
|
|
96
|
+
apiKey: getEnv("MIMO_API_KEY", ""),
|
|
97
|
+
baseUrl: getEnv("MIMO_BASE_URL", "https://api.xiaomimimo.com/v1"),
|
|
98
|
+
model: getEnv("MIMO_MODEL", "mimo-v2.5-pro"),
|
|
99
|
+
enabled: getEnvBool("MIMO_ENABLED", true)
|
|
100
|
+
},
|
|
101
|
+
mimoTokenPlan: {
|
|
102
|
+
name: "mimoTokenPlan",
|
|
103
|
+
apiKey: getEnv("MIMO_TOKEN_PLAN_API_KEY", ""),
|
|
104
|
+
baseUrl: getEnv("MIMO_TOKEN_PLAN_BASE_URL", "https://token-plan-cn.xiaomimimo.com/v1"),
|
|
105
|
+
model: getEnv("MIMO_TOKEN_PLAN_MODEL", "mimo-v2.5-pro"),
|
|
106
|
+
enabled: getEnvBool("MIMO_TOKEN_PLAN_ENABLED", false)
|
|
93
107
|
}
|
|
94
108
|
},
|
|
95
109
|
channels: {
|
|
@@ -1679,6 +1693,36 @@ var OllamaProvider = class extends BaseProvider {
|
|
|
1679
1693
|
}
|
|
1680
1694
|
};
|
|
1681
1695
|
|
|
1696
|
+
// src/providers/mimo.ts
|
|
1697
|
+
import { createOpenAI as createOpenAI2 } from "@ai-sdk/openai";
|
|
1698
|
+
var MiMoProvider = class extends BaseProvider {
|
|
1699
|
+
name;
|
|
1700
|
+
model;
|
|
1701
|
+
modelInstance;
|
|
1702
|
+
constructor(config) {
|
|
1703
|
+
super(config);
|
|
1704
|
+
this.name = config.name;
|
|
1705
|
+
this.model = config.model;
|
|
1706
|
+
const client = createOpenAI2({
|
|
1707
|
+
apiKey: config.apiKey,
|
|
1708
|
+
baseURL: config.baseUrl
|
|
1709
|
+
});
|
|
1710
|
+
this.modelInstance = client.chat(config.model);
|
|
1711
|
+
}
|
|
1712
|
+
async generateText(_prompt, _systemPrompt) {
|
|
1713
|
+
throw new Error("Use getModelInstance() with the AI SDK agent loop");
|
|
1714
|
+
}
|
|
1715
|
+
async *streamText(_prompt, _systemPrompt) {
|
|
1716
|
+
throw new Error("Use getModelInstance() with the AI SDK agent loop");
|
|
1717
|
+
}
|
|
1718
|
+
isAvailable() {
|
|
1719
|
+
return this.config.apiKey.length > 0;
|
|
1720
|
+
}
|
|
1721
|
+
getModelInstance() {
|
|
1722
|
+
return this.modelInstance;
|
|
1723
|
+
}
|
|
1724
|
+
};
|
|
1725
|
+
|
|
1682
1726
|
// src/providers/registry.ts
|
|
1683
1727
|
var ProviderRegistry = class {
|
|
1684
1728
|
providers = /* @__PURE__ */ new Map();
|
|
@@ -1692,7 +1736,9 @@ var ProviderRegistry = class {
|
|
|
1692
1736
|
config.providers.anthropic,
|
|
1693
1737
|
config.providers.grok,
|
|
1694
1738
|
config.providers.ollamaCloud,
|
|
1695
|
-
config.providers.ollamaLocal
|
|
1739
|
+
config.providers.ollamaLocal,
|
|
1740
|
+
config.providers.mimo,
|
|
1741
|
+
config.providers.mimoTokenPlan
|
|
1696
1742
|
];
|
|
1697
1743
|
for (const pc of entries) {
|
|
1698
1744
|
if (!isProviderConfigured(pc)) continue;
|
|
@@ -1704,6 +1750,8 @@ var ProviderRegistry = class {
|
|
|
1704
1750
|
provider = new DeepSeekProvider(pc);
|
|
1705
1751
|
} else if (pc.name === "ollamaCloud" || pc.name === "ollamaLocal") {
|
|
1706
1752
|
provider = new OllamaProvider(pc);
|
|
1753
|
+
} else if (pc.name === "mimo" || pc.name === "mimoTokenPlan") {
|
|
1754
|
+
provider = new MiMoProvider(pc);
|
|
1707
1755
|
} else {
|
|
1708
1756
|
provider = new OpenAICompatProvider(pc);
|
|
1709
1757
|
}
|
|
@@ -4581,6 +4629,22 @@ Or use /budget override, /budget reset, /budget set <number> anytime.`,
|
|
|
4581
4629
|
this.shutdown();
|
|
4582
4630
|
return true;
|
|
4583
4631
|
}
|
|
4632
|
+
if (cmd === "/permissions") {
|
|
4633
|
+
if (channelType === "cli" && channel instanceof CLIChannel) {
|
|
4634
|
+
const mode = await channel.askPermissionMode?.();
|
|
4635
|
+
if (mode === "allow-all") {
|
|
4636
|
+
this.capabilities.permissions.setAutoApproveAll(true);
|
|
4637
|
+
this.capabilities.permissions.addTempScope("/", true, true);
|
|
4638
|
+
await channel.send("Allow All mode active for this session. All scopes, commands, and loops auto-approved. Resets on restart.", channelId);
|
|
4639
|
+
} else {
|
|
4640
|
+
this.capabilities.permissions.setAutoApproveAll(false);
|
|
4641
|
+
await channel.send("Ask Me mode active. Risky actions will prompt for confirmation.", channelId);
|
|
4642
|
+
}
|
|
4643
|
+
return true;
|
|
4644
|
+
}
|
|
4645
|
+
await channel.send("Use /permissions in CLI to switch permission mode. On Telegram, use the /permissions button or command.", channelId);
|
|
4646
|
+
return true;
|
|
4647
|
+
}
|
|
4584
4648
|
if (cmd === "/status") {
|
|
4585
4649
|
const config = ctx.config();
|
|
4586
4650
|
const budget = ctx.tokenBudget();
|
|
@@ -4833,9 +4897,11 @@ Or use /budget override, /budget reset, /budget set <number> anytime.`,
|
|
|
4833
4897
|
await channel.withMenu(async (select) => {
|
|
4834
4898
|
while (true) {
|
|
4835
4899
|
const streamLabel = this.telegramStreaming ? "Disable Telegram Streaming" : "Enable Telegram Streaming";
|
|
4900
|
+
const permLabel = this.capabilities.permissions.isAutoApproveAll() ? "Switch to Ask Me" : "Switch to Allow All";
|
|
4836
4901
|
const action = await select("Mercury Commands", [
|
|
4837
4902
|
{ value: "status", label: "Status" },
|
|
4838
4903
|
{ value: "memory", label: "Memory" },
|
|
4904
|
+
{ value: "permissions", label: permLabel },
|
|
4839
4905
|
{ value: "telegram", label: "Telegram" },
|
|
4840
4906
|
{ value: "tools", label: "Tools" },
|
|
4841
4907
|
{ value: "skills", label: "Skills" },
|
|
@@ -4858,6 +4924,10 @@ Or use /budget override, /budget reset, /budget set <number> anytime.`,
|
|
|
4858
4924
|
}
|
|
4859
4925
|
continue;
|
|
4860
4926
|
}
|
|
4927
|
+
if (action === "permissions") {
|
|
4928
|
+
await this.handleChatCommand("/permissions", "cli", channelId);
|
|
4929
|
+
continue;
|
|
4930
|
+
}
|
|
4861
4931
|
if (action === "telegram") {
|
|
4862
4932
|
await this.openCliTelegramMenu(channel, channelId, select);
|
|
4863
4933
|
continue;
|
|
@@ -5365,6 +5435,11 @@ var ChannelRegistry = class {
|
|
|
5365
5435
|
import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
5366
5436
|
import { join as join7 } from "path";
|
|
5367
5437
|
var TOKEN_FILE = "token-usage.json";
|
|
5438
|
+
function safeNumber(value) {
|
|
5439
|
+
if (value === null || value === void 0) return 0;
|
|
5440
|
+
const n = Number(value);
|
|
5441
|
+
return isNaN(n) ? 0 : n;
|
|
5442
|
+
}
|
|
5368
5443
|
var TokenBudget = class {
|
|
5369
5444
|
constructor(config) {
|
|
5370
5445
|
this.config = config;
|
|
@@ -5380,7 +5455,7 @@ var TokenBudget = class {
|
|
|
5380
5455
|
forceNext = false;
|
|
5381
5456
|
canAfford(estimatedTokens) {
|
|
5382
5457
|
this.resetIfNewDay();
|
|
5383
|
-
return this.dailyUsed + estimatedTokens <= this.dailyBudget;
|
|
5458
|
+
return safeNumber(this.dailyUsed) + estimatedTokens <= safeNumber(this.dailyBudget);
|
|
5384
5459
|
}
|
|
5385
5460
|
isOverBudget() {
|
|
5386
5461
|
this.resetIfNewDay();
|
|
@@ -5388,7 +5463,7 @@ var TokenBudget = class {
|
|
|
5388
5463
|
this.forceNext = false;
|
|
5389
5464
|
return false;
|
|
5390
5465
|
}
|
|
5391
|
-
return this.dailyUsed >= this.dailyBudget;
|
|
5466
|
+
return safeNumber(this.dailyUsed) >= safeNumber(this.dailyBudget);
|
|
5392
5467
|
}
|
|
5393
5468
|
forceAllowNext() {
|
|
5394
5469
|
this.forceNext = true;
|
|
@@ -5416,18 +5491,24 @@ var TokenBudget = class {
|
|
|
5416
5491
|
}
|
|
5417
5492
|
recordUsage(entry) {
|
|
5418
5493
|
this.resetIfNewDay();
|
|
5419
|
-
const
|
|
5420
|
-
|
|
5494
|
+
const inputTokens = safeNumber(entry.inputTokens);
|
|
5495
|
+
const outputTokens = safeNumber(entry.outputTokens);
|
|
5496
|
+
const totalTokens = safeNumber(entry.totalTokens) || inputTokens + outputTokens;
|
|
5497
|
+
const safeEntry = { ...entry, inputTokens, outputTokens, totalTokens };
|
|
5498
|
+
const logEntry = { ...safeEntry, timestamp: Date.now() };
|
|
5499
|
+
this.dailyUsed += totalTokens;
|
|
5421
5500
|
this.requestLog.push(logEntry);
|
|
5422
5501
|
this.persist();
|
|
5423
5502
|
}
|
|
5424
5503
|
getRemaining() {
|
|
5425
5504
|
this.resetIfNewDay();
|
|
5426
|
-
return Math.max(0, this.dailyBudget - this.dailyUsed);
|
|
5505
|
+
return Math.max(0, safeNumber(this.dailyBudget) - safeNumber(this.dailyUsed));
|
|
5427
5506
|
}
|
|
5428
5507
|
getUsagePercentage() {
|
|
5429
5508
|
this.resetIfNewDay();
|
|
5430
|
-
|
|
5509
|
+
const budget = safeNumber(this.dailyBudget);
|
|
5510
|
+
const used = safeNumber(this.dailyUsed);
|
|
5511
|
+
return budget > 0 ? used / budget * 100 : 0;
|
|
5431
5512
|
}
|
|
5432
5513
|
getStatusText() {
|
|
5433
5514
|
const pct = Math.round(this.getUsagePercentage());
|
|
@@ -5466,8 +5547,16 @@ var TokenBudget = class {
|
|
|
5466
5547
|
const data = JSON.parse(raw);
|
|
5467
5548
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
5468
5549
|
if (data.lastResetDate === today) {
|
|
5469
|
-
|
|
5470
|
-
|
|
5550
|
+
const restored = safeNumber(data.dailyUsed);
|
|
5551
|
+
if (!isNaN(restored)) {
|
|
5552
|
+
this.dailyUsed = restored;
|
|
5553
|
+
}
|
|
5554
|
+
this.requestLog = (data.requestLog ?? []).map((entry) => ({
|
|
5555
|
+
...entry,
|
|
5556
|
+
inputTokens: safeNumber(entry.inputTokens),
|
|
5557
|
+
outputTokens: safeNumber(entry.outputTokens),
|
|
5558
|
+
totalTokens: safeNumber(entry.totalTokens)
|
|
5559
|
+
}));
|
|
5471
5560
|
}
|
|
5472
5561
|
this.lastResetDate = data.lastResetDate ?? today;
|
|
5473
5562
|
} catch (err) {
|
|
@@ -5592,7 +5681,6 @@ var PermissionManager = class {
|
|
|
5592
5681
|
askHandler;
|
|
5593
5682
|
autoApproveAll = false;
|
|
5594
5683
|
elevatedCommands = /* @__PURE__ */ new Set();
|
|
5595
|
-
pendingApprovals = /* @__PURE__ */ new Set();
|
|
5596
5684
|
currentChannelType = "cli";
|
|
5597
5685
|
tempScopes = [];
|
|
5598
5686
|
constructor() {
|
|
@@ -5635,12 +5723,6 @@ var PermissionManager = class {
|
|
|
5635
5723
|
isShellElevated() {
|
|
5636
5724
|
return this.elevatedCommands.has("run_command");
|
|
5637
5725
|
}
|
|
5638
|
-
addPendingApproval(baseCommand) {
|
|
5639
|
-
this.pendingApprovals.add(baseCommand);
|
|
5640
|
-
}
|
|
5641
|
-
clearPendingApprovals() {
|
|
5642
|
-
this.pendingApprovals.clear();
|
|
5643
|
-
}
|
|
5644
5726
|
load() {
|
|
5645
5727
|
if (existsSync7(PERMISSIONS_FILE)) {
|
|
5646
5728
|
try {
|
|
@@ -5699,6 +5781,9 @@ var PermissionManager = class {
|
|
|
5699
5781
|
if (mode === "write" && tempScope.write) return { allowed: true };
|
|
5700
5782
|
return { allowed: false, reason: `Permission denied: ${mode} access to ${path3}` };
|
|
5701
5783
|
}
|
|
5784
|
+
if (!this.autoApproveAll && this.askHandler && this.currentChannelType !== "internal") {
|
|
5785
|
+
return this.requestScopeExternal(path3, mode);
|
|
5786
|
+
}
|
|
5702
5787
|
return { allowed: false, reason: `Permission denied for ${mode} access to ${path3}` };
|
|
5703
5788
|
}
|
|
5704
5789
|
async checkShellCommand(command) {
|
|
@@ -5716,10 +5801,6 @@ var PermissionManager = class {
|
|
|
5716
5801
|
}
|
|
5717
5802
|
const trimmed = command.trim();
|
|
5718
5803
|
const baseCmd = trimmed.split(/\s+/)[0];
|
|
5719
|
-
if (this.pendingApprovals.has(baseCmd)) {
|
|
5720
|
-
logger.info({ cmd: trimmed }, "Shell command auto-approved (pending approval)");
|
|
5721
|
-
return { allowed: true, needsApproval: false };
|
|
5722
|
-
}
|
|
5723
5804
|
for (const pattern of shell.blocked) {
|
|
5724
5805
|
if (this.matchPattern(trimmed, pattern)) {
|
|
5725
5806
|
return { allowed: false, reason: `Blocked command: matches "${pattern}"`, needsApproval: false };
|
|
@@ -5742,7 +5823,7 @@ var PermissionManager = class {
|
|
|
5742
5823
|
}
|
|
5743
5824
|
for (const pattern of shell.needsApproval) {
|
|
5744
5825
|
if (this.matchPattern(trimmed, pattern)) {
|
|
5745
|
-
if (this.currentChannelType
|
|
5826
|
+
if (this.askHandler && this.currentChannelType !== "internal") {
|
|
5746
5827
|
const result = await this.askHandler(`Run command: ${trimmed}`);
|
|
5747
5828
|
if (result === "yes") {
|
|
5748
5829
|
return { allowed: true, needsApproval: false };
|
|
@@ -5756,7 +5837,7 @@ var PermissionManager = class {
|
|
|
5756
5837
|
return { allowed: false, reason: `Command requires approval: matches "${pattern}"`, needsApproval: true };
|
|
5757
5838
|
}
|
|
5758
5839
|
}
|
|
5759
|
-
if (this.currentChannelType
|
|
5840
|
+
if (this.askHandler && this.currentChannelType !== "internal") {
|
|
5760
5841
|
const result = await this.askHandler(`Run command: ${trimmed}`);
|
|
5761
5842
|
if (result === "yes") {
|
|
5762
5843
|
return { allowed: true, needsApproval: false };
|
|
@@ -5862,6 +5943,11 @@ Allow access?`;
|
|
|
5862
5943
|
return null;
|
|
5863
5944
|
}
|
|
5864
5945
|
mergeDefaults(parsed) {
|
|
5946
|
+
const mergeArray = (existing, defaults) => {
|
|
5947
|
+
if (!existing) return [...defaults];
|
|
5948
|
+
const combined = /* @__PURE__ */ new Set([...defaults, ...existing]);
|
|
5949
|
+
return [...combined];
|
|
5950
|
+
};
|
|
5865
5951
|
return {
|
|
5866
5952
|
capabilities: {
|
|
5867
5953
|
filesystem: {
|
|
@@ -5870,9 +5956,9 @@ Allow access?`;
|
|
|
5870
5956
|
},
|
|
5871
5957
|
shell: {
|
|
5872
5958
|
enabled: parsed.capabilities?.shell?.enabled ?? DEFAULT_MANIFEST.capabilities.shell.enabled,
|
|
5873
|
-
blocked: parsed.capabilities?.shell?.blocked
|
|
5874
|
-
autoApproved: parsed.capabilities?.shell?.autoApproved
|
|
5875
|
-
needsApproval: parsed.capabilities?.shell?.needsApproval
|
|
5959
|
+
blocked: mergeArray(parsed.capabilities?.shell?.blocked, DEFAULT_MANIFEST.capabilities.shell.blocked),
|
|
5960
|
+
autoApproved: mergeArray(parsed.capabilities?.shell?.autoApproved, DEFAULT_MANIFEST.capabilities.shell.autoApproved),
|
|
5961
|
+
needsApproval: mergeArray(parsed.capabilities?.shell?.needsApproval, DEFAULT_MANIFEST.capabilities.shell.needsApproval),
|
|
5876
5962
|
cwdOnly: parsed.capabilities?.shell?.cwdOnly ?? DEFAULT_MANIFEST.capabilities.shell.cwdOnly
|
|
5877
5963
|
},
|
|
5878
5964
|
git: {
|
|
@@ -6222,20 +6308,13 @@ function createRunCommandTool(permissions, getCwd, setCwd) {
|
|
|
6222
6308
|
description: `Run a shell command in the current working directory. Use the cd tool to change directories first \u2014 cd commands within this tool only affect chained commands (e.g., "cd /path && ls"), not subsequent calls.
|
|
6223
6309
|
Blocked commands (sudo, rm -rf /, etc.) are never executed.
|
|
6224
6310
|
Auto-approved commands (ls, cat, git status, curl, etc.) run without asking.
|
|
6225
|
-
Other commands
|
|
6311
|
+
Other commands prompt the user for approval before execution.`,
|
|
6226
6312
|
inputSchema: zodSchema10(z10.object({
|
|
6227
6313
|
command: z10.string().describe("The shell command to execute")
|
|
6228
6314
|
})),
|
|
6229
6315
|
execute: async ({ command }) => {
|
|
6230
6316
|
const check = await permissions.checkShellCommand(command);
|
|
6231
6317
|
if (!check.allowed) {
|
|
6232
|
-
if (check.needsApproval) {
|
|
6233
|
-
const baseCmd = command.trim().split(/\s+/)[0];
|
|
6234
|
-
permissions.addPendingApproval(baseCmd);
|
|
6235
|
-
return `\u26A0 Command requires approval: ${command}
|
|
6236
|
-
|
|
6237
|
-
Tell the user what this command does and ask for permission. If they approve, try running it again. If they say "always", use the approve_command tool to permanently approve this command type.`;
|
|
6238
|
-
}
|
|
6239
6318
|
return `Error: ${check.reason}`;
|
|
6240
6319
|
}
|
|
6241
6320
|
const cwd = getCwd();
|
|
@@ -6711,12 +6790,6 @@ function createGitPushTool(permissions, getCwd) {
|
|
|
6711
6790
|
execute: async ({ remote, branch }) => {
|
|
6712
6791
|
const cmd = `git push ${remote || "origin"} ${branch || ""}`.trim();
|
|
6713
6792
|
const check = await permissions.checkShellCommand(cmd);
|
|
6714
|
-
if (!check.allowed && check.needsApproval) {
|
|
6715
|
-
const baseCmd = "git";
|
|
6716
|
-
permissions.addPendingApproval(baseCmd);
|
|
6717
|
-
return `\u26A0 This command pushes to a remote: ${cmd}
|
|
6718
|
-
Ask the user for permission. If they approve, try again. If they say "always", use the approve_command tool.`;
|
|
6719
|
-
}
|
|
6720
6793
|
if (!check.allowed) {
|
|
6721
6794
|
return `Error: ${check.reason}`;
|
|
6722
6795
|
}
|
|
@@ -8075,6 +8148,14 @@ var OLLAMA_LOCAL_PREFERRED_MODELS = [
|
|
|
8075
8148
|
"gpt-oss:20b",
|
|
8076
8149
|
"gpt-oss:120b"
|
|
8077
8150
|
];
|
|
8151
|
+
var MIMO_PREFERRED_MODELS = [
|
|
8152
|
+
"mimo-v2.5-pro",
|
|
8153
|
+
"mimo-v2.5",
|
|
8154
|
+
"mimo-v2-pro",
|
|
8155
|
+
"mimo-v2-omni",
|
|
8156
|
+
"mimo-v2-flash"
|
|
8157
|
+
];
|
|
8158
|
+
var MIMO_TOKEN_PLAN_PREFERRED_MODELS = MIMO_PREFERRED_MODELS;
|
|
8078
8159
|
var ProviderModelFetchError = class extends Error {
|
|
8079
8160
|
constructor(message) {
|
|
8080
8161
|
super(message);
|
|
@@ -8129,7 +8210,9 @@ function chooseRecommendedModel(provider, models, currentModel) {
|
|
|
8129
8210
|
anthropic: ANTHROPIC_PREFERRED_MODELS,
|
|
8130
8211
|
grok: GROK_PREFERRED_MODELS,
|
|
8131
8212
|
ollamaCloud: OLLAMA_CLOUD_PREFERRED_MODELS,
|
|
8132
|
-
ollamaLocal: OLLAMA_LOCAL_PREFERRED_MODELS
|
|
8213
|
+
ollamaLocal: OLLAMA_LOCAL_PREFERRED_MODELS,
|
|
8214
|
+
mimo: MIMO_PREFERRED_MODELS,
|
|
8215
|
+
mimoTokenPlan: MIMO_TOKEN_PLAN_PREFERRED_MODELS
|
|
8133
8216
|
};
|
|
8134
8217
|
for (const candidate of preferredByProvider[provider]) {
|
|
8135
8218
|
if (models.includes(candidate)) {
|
|
@@ -8153,7 +8236,9 @@ function buildModelCatalog(provider, models, currentModel) {
|
|
|
8153
8236
|
anthropic: ANTHROPIC_PREFERRED_MODELS,
|
|
8154
8237
|
grok: GROK_PREFERRED_MODELS,
|
|
8155
8238
|
ollamaCloud: OLLAMA_CLOUD_PREFERRED_MODELS,
|
|
8156
|
-
ollamaLocal: OLLAMA_LOCAL_PREFERRED_MODELS
|
|
8239
|
+
ollamaLocal: OLLAMA_LOCAL_PREFERRED_MODELS,
|
|
8240
|
+
mimo: MIMO_PREFERRED_MODELS,
|
|
8241
|
+
mimoTokenPlan: MIMO_TOKEN_PLAN_PREFERRED_MODELS
|
|
8157
8242
|
};
|
|
8158
8243
|
const withoutRecommended = filtered.filter((model) => model !== recommendedModel);
|
|
8159
8244
|
const prioritized = prioritizeModels(withoutRecommended, preferredByProvider[provider]);
|
|
@@ -8219,6 +8304,38 @@ async function fetchOllamaModels(provider, config) {
|
|
|
8219
8304
|
const ids = (data.models ?? []).map((model) => model.model?.trim() || model.name?.trim() || "").filter(Boolean);
|
|
8220
8305
|
return buildModelCatalog(provider, ids, config.model);
|
|
8221
8306
|
}
|
|
8307
|
+
async function fetchMiMoModels(config) {
|
|
8308
|
+
const data = await fetchJson(
|
|
8309
|
+
`${trimTrailingSlash(config.baseUrl)}/models`,
|
|
8310
|
+
{
|
|
8311
|
+
headers: {
|
|
8312
|
+
Authorization: `Bearer ${config.apiKey}`
|
|
8313
|
+
}
|
|
8314
|
+
},
|
|
8315
|
+
"Mercury could not fetch models for this MiMo key. Please re-enter it."
|
|
8316
|
+
);
|
|
8317
|
+
const ids = (data.data ?? []).map((model) => model.id?.trim() ?? "").filter((id) => {
|
|
8318
|
+
const lower = id.toLowerCase();
|
|
8319
|
+
return lower.startsWith("mimo-") && !lower.includes("tts");
|
|
8320
|
+
});
|
|
8321
|
+
return buildModelCatalog("mimo", ids, config.model);
|
|
8322
|
+
}
|
|
8323
|
+
async function fetchMiMoTokenPlanModels(config) {
|
|
8324
|
+
const data = await fetchJson(
|
|
8325
|
+
`${trimTrailingSlash(config.baseUrl)}/models`,
|
|
8326
|
+
{
|
|
8327
|
+
headers: {
|
|
8328
|
+
Authorization: `Bearer ${config.apiKey}`
|
|
8329
|
+
}
|
|
8330
|
+
},
|
|
8331
|
+
"Mercury could not fetch models for this MiMo Token Plan key. Please re-enter it."
|
|
8332
|
+
);
|
|
8333
|
+
const ids = (data.data ?? []).map((model) => model.id?.trim() ?? "").filter((id) => {
|
|
8334
|
+
const lower = id.toLowerCase();
|
|
8335
|
+
return lower.startsWith("mimo-") && !lower.includes("tts");
|
|
8336
|
+
});
|
|
8337
|
+
return buildModelCatalog("mimoTokenPlan", ids, config.model);
|
|
8338
|
+
}
|
|
8222
8339
|
async function fetchProviderModelCatalog(provider, config) {
|
|
8223
8340
|
if (provider === "anthropic") {
|
|
8224
8341
|
return fetchAnthropicModels(config);
|
|
@@ -8229,6 +8346,12 @@ async function fetchProviderModelCatalog(provider, config) {
|
|
|
8229
8346
|
if (provider === "ollamaCloud" || provider === "ollamaLocal") {
|
|
8230
8347
|
return fetchOllamaModels(provider, config);
|
|
8231
8348
|
}
|
|
8349
|
+
if (provider === "mimo") {
|
|
8350
|
+
return fetchMiMoModels(config);
|
|
8351
|
+
}
|
|
8352
|
+
if (provider === "mimoTokenPlan") {
|
|
8353
|
+
return fetchMiMoTokenPlanModels(config);
|
|
8354
|
+
}
|
|
8232
8355
|
return fetchOpenAICompatModels(provider, config);
|
|
8233
8356
|
}
|
|
8234
8357
|
|
|
@@ -8286,7 +8409,9 @@ var PROVIDER_OPTIONS = [
|
|
|
8286
8409
|
{ key: "anthropic", label: "Anthropic" },
|
|
8287
8410
|
{ key: "grok", label: "Grok (xAI)" },
|
|
8288
8411
|
{ key: "ollamaCloud", label: "Ollama Cloud" },
|
|
8289
|
-
{ key: "ollamaLocal", label: "Ollama Local" }
|
|
8412
|
+
{ key: "ollamaLocal", label: "Ollama Local" },
|
|
8413
|
+
{ key: "mimo", label: "MiMo (Xiaomi)" },
|
|
8414
|
+
{ key: "mimoTokenPlan", label: "MiMo Token Plan (Xiaomi)" }
|
|
8290
8415
|
];
|
|
8291
8416
|
function getConfiguredProviderNames(config) {
|
|
8292
8417
|
return PROVIDER_OPTIONS.map((option) => option.key).filter((key) => isProviderConfigured(config.providers[key]));
|
|
@@ -8387,6 +8512,12 @@ function validateApiKey(provider, value) {
|
|
|
8387
8512
|
if (provider === "ollamaCloud") {
|
|
8388
8513
|
return looksLikeToken(value) ? null : "Ollama Cloud keys must look like a real API token: long, no spaces, and not plain text.";
|
|
8389
8514
|
}
|
|
8515
|
+
if (provider === "mimo") {
|
|
8516
|
+
return /^sk-[A-Za-z0-9_-]{16,}$/i.test(value) ? null : "MiMo keys must start with `sk-`.";
|
|
8517
|
+
}
|
|
8518
|
+
if (provider === "mimoTokenPlan") {
|
|
8519
|
+
return /^tp-[A-Za-z0-9_-]{16,}$/i.test(value) ? null : "MiMo Token Plan keys must start with `tp-`.";
|
|
8520
|
+
}
|
|
8390
8521
|
return null;
|
|
8391
8522
|
}
|
|
8392
8523
|
function validateBaseUrl(value) {
|
|
@@ -8735,6 +8866,38 @@ async function configure(existingConfig) {
|
|
|
8735
8866
|
config.providers.ollamaLocal.model = result.model;
|
|
8736
8867
|
config.providers.ollamaLocal.enabled = true;
|
|
8737
8868
|
}
|
|
8869
|
+
continue;
|
|
8870
|
+
}
|
|
8871
|
+
if (provider === "mimo") {
|
|
8872
|
+
const mask = isReconfig && config.providers.mimo.apiKey ? ` [${maskKey(config.providers.mimo.apiKey)}]` : "";
|
|
8873
|
+
const result = await promptApiKeyWithModelSelection(
|
|
8874
|
+
config,
|
|
8875
|
+
"mimo",
|
|
8876
|
+
"MiMo",
|
|
8877
|
+
chalk7.white(` MiMo API key${mask}${isReconfig ? "" : " (Enter to skip)"}: `),
|
|
8878
|
+
isReconfig
|
|
8879
|
+
);
|
|
8880
|
+
if (!result.skipped && result.apiKey && result.model) {
|
|
8881
|
+
config.providers.mimo.apiKey = result.apiKey;
|
|
8882
|
+
config.providers.mimo.model = result.model;
|
|
8883
|
+
config.providers.mimo.enabled = true;
|
|
8884
|
+
}
|
|
8885
|
+
continue;
|
|
8886
|
+
}
|
|
8887
|
+
if (provider === "mimoTokenPlan") {
|
|
8888
|
+
const mask = isReconfig && config.providers.mimoTokenPlan.apiKey ? ` [${maskKey(config.providers.mimoTokenPlan.apiKey)}]` : "";
|
|
8889
|
+
const result = await promptApiKeyWithModelSelection(
|
|
8890
|
+
config,
|
|
8891
|
+
"mimoTokenPlan",
|
|
8892
|
+
"MiMo Token Plan",
|
|
8893
|
+
chalk7.white(` MiMo Token Plan API key${mask}${isReconfig ? "" : " (Enter to skip)"}: `),
|
|
8894
|
+
isReconfig
|
|
8895
|
+
);
|
|
8896
|
+
if (!result.skipped && result.apiKey && result.model) {
|
|
8897
|
+
config.providers.mimoTokenPlan.apiKey = result.apiKey;
|
|
8898
|
+
config.providers.mimoTokenPlan.model = result.model;
|
|
8899
|
+
config.providers.mimoTokenPlan.enabled = true;
|
|
8900
|
+
}
|
|
8738
8901
|
}
|
|
8739
8902
|
}
|
|
8740
8903
|
const configuredProviders = getConfiguredProviderNames(config);
|