Conversation
- Add xiaomimo-web-client-browser.ts with browser-based API client - Add xiaomimo-web-auth.ts for browser authentication - Add xiaomimo-web-stream.ts for SSE response parsing - Update models-config.providers.ts with xiaomimo-web provider - Update onboard-web-auth.ts with xiaomimo-web option - Add xiaomimo-web to MODEL_APIS types - Add xiaomimo-web to web-models extension - Add prompt truncation for API length limits (3000 chars) API format uses query/msgId/conversationId/modelConfig/multiMedias Response format uses SSE events: dialogId, message, usage
- Fix conversationId persistence from dialogId response - Add tool instruction injection and tool call parsing - Reduce prompt to 2500 chars with recent message history - Support multi-turn conversations - Parse tool_call XML tags from response
- Pass systemPrompt to client for context - Combine system prompt with user message - Fix variable name errors
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
📝 WalkthroughWalkthrough此次变更添加对 Xiaomi MiMo / MiMo Web 的提供商、客户端与流处理支持;大量代码风格/引号统一化调整;新增认证与文档条目并微调若干脚本与 UI 导入。仅格式化与新增 MiMo 功能,无其他行为变更说明。 Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Browser as Playwright<br/>(Browser)
participant OpenClaw as OpenClaw<br/>(Auth Capture)
participant MiMoAPI as MiMo<br/>(Auth/API)
User->>Browser: 打开登录页面 (openUrl)
Browser->>MiMoAPI: 浏览器加载 /login 等页面
User->>MiMoAPI: 完成登录交互
MiMoAPI-->>Browser: 返回 Set-Cookie / Authorization header
Browser->>OpenClaw: 拦截 request/response (Cookie/Authorization)
OpenClaw->>Browser: 读取 document.cookie / localStorage / userAgent
Browser-->>OpenClaw: 返回凭证 (cookie, bearer, sessionId, userAgent)
OpenClaw-->>User: 持久化认证配置 (保存 profile)
sequenceDiagram
participant Client
participant OpenClaw as Orchestrator
participant MimoStream as MiMo Stream
participant MimoClient as MiMo Client
participant MiMoAPI as MiMo Server
Client->>OpenClaw: 发起 query(messages, model)
OpenClaw->>MimoStream: createMimoWebStreamFn(credentials)
MimoStream->>MimoClient: chatCompletions({messages, stream:true})
MimoClient->>MiMoAPI: POST /chat (流式)
MiMoAPI-->>MimoClient: SSE / 行式流响应
MimoClient-->>MimoStream: ReadableStream
MimoStream->>MimoStream: 解析并规范化事件 (text_delta, thinking, toolCall)
MimoStream-->>OpenClaw: 逐步推送流事件
OpenClaw-->>Client: 汇总并返回最终响应
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Important Merge conflicts detected (Beta)
✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Comment |
There was a problem hiding this comment.
Actionable comments posted: 12
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/agents/grok-web-stream.ts (1)
370-376:⚠️ Potential issue | 🟡 Minor复制粘贴错误:日志标签不正确
错误日志中使用了
[Qwen Stream]标签,但这是GrokWebStream模块。这会导致调试时产生混淆。🔧 建议修复
} catch (e) { part.arguments = { raw: argStr }; console.error( - `[Qwen Stream] Failed to parse JSON for tool call ${currentToolName}:`, + `[GrokWebStream] Failed to parse JSON for tool call ${currentToolName}:`, argStr, "\nError:", e, ); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/agents/grok-web-stream.ts` around lines 370 - 376, The error log in GrokWebStream incorrectly uses the "[Qwen Stream]" tag; update the console.error call in GrokWebStream (the block that references currentToolName, argStr, and e) to use the correct module tag (e.g., "[GrokWebStream]" or "[Grok Web Stream]") so logs accurately reflect the source and avoid confusion when debugging.
🟡 Minor comments (16)
src/providers/xiaomimo-web-client.ts-59-102 (1)
59-102:⚠️ Potential issue | 🟡 Minor返回类型与实际返回值不一致
方法签名声明返回
ReadableStream<Uint8Array> | null,但res.body在某些情况下可能为null(即使res.ok为true)。建议添加 null 检查或调整返回类型处理:🛡️ 建议的修复
console.log( `[XiaomiMimoWebClient] Chat completion response OK (status: ${res.status}). Content-Type: ${res.headers.get("content-type")}`, ); - return res.body; + if (!res.body) { + throw new Error("XiaomiMimo response body is null"); + } + return res.body; }同时可以将返回类型简化为
Promise<ReadableStream<Uint8Array>>。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/providers/xiaomimo-web-client.ts` around lines 59 - 102, The method chatCompletions declares Promise<ReadableStream<Uint8Array> | null> but returns res.body which can be null even when res.ok is true; update chatCompletions to ensure a non-null ReadableStream is always returned by checking res.body after the success response and throwing a descriptive Error if it's null (or alternatively construct/pipe an empty ReadableStream), and then tighten the method signature to Promise<ReadableStream<Uint8Array>>; refer to the chatCompletions function and the res.body use to locate where to add the null-check and signature change.src/providers/xiaomimo-web-client.ts-104-125 (1)
104-125:⚠️ Potential issue | 🟡 Minor模型配置中
api属性缺失导致客户端实现不一致
xiaomimo-web-client.ts的discoverModels()返回的模型配置缺少api属性。虽然ModelDefinitionConfig中api字段是可选的,但其他 web 客户端(如chatgpt-web-client-browser.ts、qwen-web-client-browser.ts)都包含此属性。doubao-web-client.ts也存在同样问题。建议为一致性添加api: "xiaomimo-web"属性。🔧 建议的修复
async discoverModels(): Promise<ModelDefinitionConfig[]> { return [ { id: "xiaomimo-chat", name: "MiMo Chat", + api: "xiaomimo-web", reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 128000, maxTokens: 4096, }, { id: "mimo-v2-pro", name: "MiMo V2 Pro", + api: "xiaomimo-web", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 128000, maxTokens: 8192, }, ]; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/providers/xiaomimo-web-client.ts` around lines 104 - 125, discoverModels currently returns model objects missing the optional api field, causing inconsistency with other web clients; update the discoverModels method in xiaomimo-web-client.ts to add api: "xiaomimo-web" to each ModelDefinitionConfig object (the entries with id "xiaomimo-chat" and "mimo-v2-pro") so the model definitions match other clients like chatgpt-web-client-browser and qwen-web-client-browser.src/providers/xiaomimo-web-client-browser.ts-124-125 (1)
124-125:⚠️ Potential issue | 🟡 Minor
signal入参未被使用,调用方无法可靠中断请求函数签名接收了
AbortSignal,但整个请求流程没有中断检查,超时/取消场景下可观测性与控制性较差。💡 建议修复
async chatCompletions(params: { @@ }): Promise<ReadableStream<Uint8Array>> { + if (params.signal?.aborted) { + throw new Error("XiaomiMimo request aborted"); + } const { page } = await this.ensureBrowser(); @@ const result = await page.evaluate( @@ ); + if (params.signal?.aborted) { + throw new Error("XiaomiMimo request aborted"); + }Also applies to: 126-227
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/providers/xiaomimo-web-client-browser.ts` around lines 124 - 125, 该函数声明接收了参数 signal 并返回 Promise<ReadableStream<Uint8Array>>,但实现未使用该 AbortSignal;请在该函数(接受 signal 的那个方法)中把 signal 传递给底层的 fetch 请求(即在调用 fetch 时将 { signal } 作为选项),在开始读取流前检查 signal.aborted 并在 signal 的 'abort' 事件触发时调用 reader.cancel() / response.body.cancel() 来停止流处理,同时将 fetch 的错误/AbortError 正确向外抛出或映射为拒绝的 Promise;若函数内部创建了自己的 AbortController(例如用于超时),确保将其与传入的 signal 联合(当任一中止时取消整体操作)。src/commands/auth-choice.apply.mimo-web.ts-35-37 (1)
35-37:⚠️ Potential issue | 🟡 Minor错误处理中缺少类型守卫。
error.message访问未经类型检查,如果error不是Error实例会导致运行时错误。🐛 建议的修复
} catch (error) { - console.error("? MiMo-Web authentication failed:", error.message); + console.error("? MiMo-Web authentication failed:", error instanceof Error ? error.message : String(error)); process.exit(1); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/auth-choice.apply.mimo-web.ts` around lines 35 - 37, The catch block currently assumes "error" is an Error and reads error.message; add a type guard to safely extract message (e.g., check if error instanceof Error or typeof error === 'object' && 'message' in error) and fall back to String(error) or JSON.stringify for non-Error values before calling console.error in the catch of the MiMo-Web auth flow; ensure the log uses the computed message and keep the existing process.exit(1) behavior.src/providers/mimo-web.ts-64-160 (1)
64-160:⚠️ Potential issue | 🟡 Minor
setInterval资源泄漏:超时或请求处理器成功时未清理定时器。当
timeout触发(第 66-70 行)或requestHandler成功捕获凭证(第 89-98 行)时,第 153 行创建的setInterval没有被清理。虽然下次定时器回调时会检测到resolved=true并自行清理,但这仍然是一个小的资源泄漏。🛠️ 建议修复:将 interval 提升到外层作用域并在所有退出路径清理
private async waitForLogin( page: Page, onProgress: (msg: string) => void, ): Promise<typeof this.credentials | null> { return new Promise((resolve) => { let resolved = false; + let interval: ReturnType<typeof setInterval> | undefined; + + const cleanup = () => { + resolved = true; + clearTimeout(timeout); + if (interval) clearInterval(interval); + page.removeListener("request", requestHandler); + }; + const timeout = setTimeout(() => { if (!resolved) { - resolved = true; + cleanup(); resolve(null); } }, 300000); const requestHandler = async (request: any) => { if (resolved) return; // ... existing logic ... if (headers["authorization"] || headers["cookie"]) { onProgress("检测到认证信息,正在保存..."); const cookies = await page.context().cookies(); const cookieStr = cookies.map((c) => `${c.name}=${c.value}`).join("; "); - resolved = true; - clearTimeout(timeout); - page.removeListener("request", requestHandler); + cleanup(); resolve({ /* ... */ }); } }; // ... checkLogin function ... - const interval = setInterval(() => { + interval = setInterval(() => { if (resolved) { clearInterval(interval); return; } checkLogin(); }, 2000); }); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/providers/mimo-web.ts` around lines 64 - 160, The interval started at the bottom is not cleared on all exit paths causing a resource leak; move the interval variable into the outer scope and ensure you call clearInterval(interval) whenever you resolve (in the timeout handler, in requestHandler when you capture credentials, and in the checkLogin success branch), and also clearTimeout(timeout) and remove the "request" listener in those same exit paths (requestHandler, checkLogin success, and timeout) so every early/normal exit cleans interval, timeout and the listener (references: timeout, interval, requestHandler, checkLogin).src/providers/xiaomimo-web-auth.ts-161-251 (1)
161-251:⚠️ Potential issue | 🟡 Minor事件监听器未清理
page.on("request")、page.on("response")和page.on("framenavigated")的事件监听器在凭据捕获成功或超时后从未被移除。这与src/providers/mimo-web-auth.ts中的问题相同。建议提取监听器处理函数,并在
tryResolve成功解析时调用清理函数移除所有监听器。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/providers/xiaomimo-web-auth.ts` around lines 161 - 251, Event listeners registered with page.on("request"), page.on("response") and page.on("framenavigated") are never removed after capturedToken is set or tryResolve completes; extract each listener into named handler functions (e.g., requestHandler, responseHandler, frameNavigatedHandler), add a cleanup function that calls page.off(...) (or page.removeListener) to remove those handlers, and invoke that cleanup inside tryResolve when it successfully resolves or on the timeout/failure path so handlers are not left attached; ensure capturedToken and tryResolve remain referenced so cleanup runs exactly when a token is captured or the operation ends.src/providers/mimo-web-client.ts-128-128 (1)
128-128:⚠️ Potential issue | 🟡 Minor避免使用
any类型多个方法参数和返回值使用了
any类型:
- 第 128 行:
body?: any- 第 159 行:
parseModels(data: any)- 第 182 行:
createStreamFromResponse(data: any)As per coding guidelines: "Maintain strict TypeScript typing - avoid using
anytype."Also applies to: 159-159, 182-182
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/providers/mimo-web-client.ts` at line 128, 方法签名中使用了 any(makeRequest 的 body?: any,parseModels(data: any),createStreamFromResponse(data: any));请替换为严格类型:为请求体定义具体接口或使用 Record<string, unknown> / unknown 并在函数体内做显式类型缩小/验证;为 parseModels 返回值和输入创建一个明确的 ModelResponse 接口并在 parseModels(data: unknown) 中做运行时校验;为 createStreamFromResponse 输入定义 StreamPayload 或类似类型并在函数内部转换/校验后再使用。更新 makeRequest、parseModels、createStreamFromResponse 的签名以使用这些新接口,确保任何 JSON.parse 或外部数据入口处都有类型守卫或转换函数以保持类型安全。src/agents/mimo-web-stream.ts-37-37 (1)
37-37:⚠️ Potential issue | 🟡 Minor避免使用
any类型多处使用了
any类型,违反了编码规范:
- 第 37 行:
messages as any- 第 102 行:
transformMimoChunk(mimoChunk: any): any- 第 139 行:
formatMimoTools(tools: any[]): any[]♻️ 建议为 transformMimoChunk 添加类型
+interface MimoChunk { + id?: string; + object?: string; + created?: number; + model?: string; + content?: string; + text?: string; + response?: string; + message?: { content?: string }; + choices?: Array<{ index: number; delta: { role?: string; content?: string }; finish_reason?: string | null }>; + finish_reason?: string; + done?: boolean; +} -function transformMimoChunk(mimoChunk: any): any { +function transformMimoChunk(mimoChunk: MimoChunk): MimoChunk {As per coding guidelines: "Maintain strict TypeScript typing - avoid using
anytype."Also applies to: 102-102, 139-139
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/agents/mimo-web-stream.ts` at line 37, Replace all uses of `any` by introducing proper TypeScript interfaces and updating function signatures: define a MimoMessage (or Message) interface and use it instead of `messages as any` (e.g., messages: MimoMessage[]), create a MimoChunk (or RawMimoChunk) and ProcessedChunk (or TransformedMimoChunk) interfaces and change the signature of transformMimoChunk from `transformMimoChunk(mimoChunk: any): any` to `transformMimoChunk(mimoChunk: MimoChunk): ProcessedChunk`, and define RawTool/FormattedTool types and change `formatMimoTools(tools: any[]): any[]` to `formatMimoTools(tools: RawTool[]): FormattedTool[]`; update all internal property accesses in transformMimoChunk and formatMimoTools to match the new interfaces so callers (including where `messages` is passed) get strict typing.src/agents/xiaomimo-web-stream.ts-37-37 (1)
37-37:⚠️ Potential issue | 🟡 Minor避免使用
any类型根据编码规范,应避免使用
any类型。以下位置使用了any:
- 第 37 行:
(context as any).sessionId- 第 435 行:
currentMode = "error" as any- 第 544 行:
} as any);建议扩展类型定义以包含所需的属性,或使用更具体的类型断言。
♻️ 针对第 37 行的建议
考虑扩展 context 类型或使用类型守卫:
-const sessionKey = model.id + ((context as any).sessionId ? `:${(context as any).sessionId}` : ''); +const contextWithSession = context as typeof context & { sessionId?: string }; +const sessionKey = model.id + (contextWithSession.sessionId ? `:${contextWithSession.sessionId}` : '');As per coding guidelines: "Maintain strict TypeScript typing - avoid using
anytype."Also applies to: 435-435, 544-544
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/agents/xiaomimo-web-stream.ts` at line 37, The code uses `any` in three places—building sessionKey, assigning currentMode, and a final object cast—so add/extend proper TypeScript types and replace the unsafe casts: declare or extend an interface (e.g., AgentContext { sessionId?: string }) and use that type for the `context` parameter or a local typed variable to access `sessionId` instead of `(context as any).sessionId`; change `currentMode = "error" as any` to use the correct union/enum type or a properly typed variable (e.g., CurrentMode = "idle" | "running" | "error") and assign `"error"` without `any`; and replace the final `} as any` cast with the actual interface/type for that object so the compiler can verify properties—update function signatures or local type assertions to these specific types rather than using `any`.src/agents/xiaomimo-web-stream.ts-91-92 (1)
91-92:⚠️ Potential issue | 🟡 Minor模板字符串中的换行符
模板字符串中的换行符可能导致输出格式异常(
</think>标签前有额外的换行)。♻️ 建议的修复
} else if (part.type === "thinking") { - content += `<think>\n${part.thinking}\n -</think>\n`; + content += `<think>\n${part.thinking}\n</think>\n`; } else if (part.type === "toolCall") {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/agents/xiaomimo-web-stream.ts` around lines 91 - 92, 在 xiaomimo-web-stream.ts 中构建 content 的模板字面量在插入 part.thinking 后多出一个换行,导致 </think> 前有额外空行;修复方法:修改生成模板的位置(涉及变量 content 和 part.thinking)去掉多余的换行,例如将模板从在 </think> 前留空行的写法改为直接闭合(或对 part.thinking 使用 trimEnd())以确保没有多余的换行。src/providers/xiaomimo-web-auth.ts-253-258 (1)
253-258:⚠️ Potential issue | 🟡 Minor页面关闭时的清理不完整
当页面关闭时,
reject被调用但timeout未被清除,且resolved未设置为true。这意味着超时处理器仍可能触发,导致在 Promise 已拒绝后再次调用reject(虽然第二次调用不会有影响,但逻辑不完整)。🛡️ 建议的修复
page.on("close", () => { + if (resolved) return; + resolved = true; + clearTimeout(timeout); if (checkInterval) clearInterval(checkInterval); reject(new Error("Browser window closed before login was captured.")); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/providers/xiaomimo-web-auth.ts` around lines 253 - 258, When handling the page "close" event, ensure full cleanup by clearing the timeout and interval and marking the operation resolved to prevent the timeout/tryResolve from firing after reject; specifically, inside the page.on("close") handler (the callback that currently clears checkInterval and calls reject), also clear the timeout variable (clearTimeout(timeout)), set resolved = true, and ensure checkInterval is cleared (clearInterval(checkInterval)) so tryResolve cannot run after the Promise has been rejected.src/agents/xiaomimo-web-stream.ts-26-43 (1)
26-43:⚠️ Potential issue | 🟡 Minor未使用的客户端实例和变量遮蔽
第 26 行创建了一个
client实例,但在第 39 行又声明了一个同名的let client变量,这会遮蔽外部的client。实际上,第 26 行创建的client从未被使用。♻️ 建议的修复
export function createXiaomiMimoWebStreamFn(cookieOrJson: string): StreamFn { let options: XiaomiMimoWebClientOptions; try { const parsed = JSON.parse(cookieOrJson); options = { cookie: parsed.cookie || cookieOrJson }; } catch { options = { cookie: cookieOrJson }; } - const client = new XiaomiMimoWebClientBrowser(options); return (model, context, streamOptions) => { const stream = createAssistantMessageEventStream(); const run = async () => { try { console.log('[XiaomiMimoWebStream] context keys:', Object.keys(context).join(', ')); const messages = context.messages; const systemPrompt = context.systemPrompt || ""; const tools = context.tools || []; const sessionKey = model.id + ((context as any).sessionId ? `:${(context as any).sessionId}` : ''); const sessionId = sessionMap.get(sessionKey); - let client = clientMap.get(sessionKey); + const client = clientMap.get(sessionKey) ?? new XiaomiMimoWebClientBrowser(options); if (!client) { - client = new XiaomiMimoWebClientBrowser(options); clientMap.set(sessionKey, client); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/agents/xiaomimo-web-stream.ts` around lines 26 - 43, The top-level creation of client using XiaomiMimoWebClientBrowser is unused and is shadowed by a later local "let client" inside the returned function; remove the unused outer "const client = new XiaomiMimoWebClientBrowser(options)" (or rename/repurpose it) and ensure the function uses the client obtained from clientMap (the local "client" tied to sessionKey) so there is no variable shadowing; update any references to "client" to consistently use the client from clientMap and keep only one client creation point related to sessionKey/sessionMap.src/agents/xiaomimo-web-stream.ts-149-149 (1)
149-149:⚠️ Potential issue | 🟡 Minor变量名遮蔽
第 149 行的
messages变量遮蔽了第 34 行从context.messages获取的messages。这可能导致混淆。♻️ 建议的修复
- const messages = prompt.split(/\n\n(?=User:|Assistant:)/); + const promptParts = prompt.split(/\n\n(?=User:|Assistant:)/); let accumulated = ""; - for (let i = messages.length - 1; i >= 0; i--) { - const candidate = messages[i] + (accumulated ? "\n\n" + accumulated : ""); + for (let i = promptParts.length - 1; i >= 0; i--) { + const candidate = promptParts[i] + (accumulated ? "\n\n" + accumulated : ""); if (candidate.length > MAX_PROMPT_LENGTH) { break; } accumulated = candidate; } if (!accumulated) { - const lastMessage = messages[messages.length - 1]; + const lastMessage = promptParts[promptParts.length - 1]; accumulated = lastMessage.slice(-MAX_PROMPT_LENGTH); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/agents/xiaomimo-web-stream.ts` at line 149, The local variable named messages created by const messages = prompt.split(/\n\n(?=User:|Assistant:)/) shadows the earlier messages obtained from context.messages (around where context.messages is read); rename the local variable (e.g., promptMessages or parsedMessages) and update all subsequent uses in this scope to that new name so there is no shadowing and the original context.messages remains unambiguous.src/providers/mimo-web-auth.ts-63-118 (1)
63-118:⚠️ Potential issue | 🟡 Minor事件监听器未清理 - 潜在内存泄漏
page.on("request")和page.on("load")事件监听器在凭据捕获成功或超时后从未被移除。这可能导致:
- 内存泄漏(如果页面对象长期存在)
- 在凭据已捕获后仍然执行多余的处理逻辑
建议在
resolve之前使用page.off()或page.removeListener()移除监听器。🛡️ 建议的修复方向
async function captureMimoCredentials( page: Page, onProgress: (msg: string) => void, ): Promise<MimoAuthCredentials | null> { return new Promise((resolve) => { let captured = false; + + const requestHandler = async (request: any) => { + // ... existing request handling logic + }; + + const loadHandler = async () => { + // ... existing load handling logic + }; + + const cleanup = () => { + page.off("request", requestHandler); + page.off("load", loadHandler); + }; + const timeout = setTimeout(() => { if (!captured) { + cleanup(); onProgress("Timeout waiting for MiMo login"); resolve(null); } }, 300000); - page.on("request", async (request) => { + page.on("request", requestHandler); + page.on("load", loadHandler);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/providers/mimo-web-auth.ts` around lines 63 - 118, The page request and load event handlers (the page.on("request") listener in this snippet and the page.on("load") listener used elsewhere) are never removed, causing potential memory leaks and extra work after credentials are captured or on timeout; update the capture flow so that before calling resolve(...) or on timeout/reject you remove both listeners (use page.off or page.removeListener for the same handler references), clear the timeout, and ensure you stop further processing (e.g., check captured flag) — specifically remove the request handler defined for page.on("request") and the corresponding load handler wherever it is registered, then call resolve/reject.src/providers/mimo-web-auth.ts-93-93 (1)
93-93:⚠️ Potential issue | 🟡 Minor变量名遮蔽
循环变量
cookie遮蔽了外部作用域中第 75 行定义的cookie变量(请求头中的 cookie 字符串)。这会导致混淆且容易出错。♻️ 建议的修复
- for (const cookie of cookies) { + for (const c of cookies) { if ( - cookie.name.includes("session") || - cookie.name.includes("token") || - cookie.name.includes("auth") + c.name.includes("session") || + c.name.includes("token") || + c.name.includes("auth") ) { - sessionId = cookie.value; + sessionId = c.value; break; } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/providers/mimo-web-auth.ts` at line 93, The loop variable "cookie" in the for (const cookie of cookies) statement shadows the outer "cookie" variable defined earlier (request header cookie string) — rename the loop variable (e.g., cookiePair or parsedCookie) and update all usages inside that loop (the for...of block) to the new name so the outer "cookie" variable is not shadowed; ensure any comparisons or assignments that previously referenced the inner "cookie" now use the new identifier.src/providers/mimo-web-client.ts-146-150 (1)
146-150:⚠️ Potential issue | 🟡 Minor移除冗余的
credentials: "include"选项
credentials: "include"是浏览器 fetch 选项,虽然 Node.js 的 fetch(18+)在语法上接受此选项,但在 Node.js 中它没有任何实际效果。由于代码已经通过Cookie请求头(第 135 行)手动管理 cookies,此选项是冗余的,会造成代码混淆。建议移除此选项以明确代码意图仅针对 Node.js 环境。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/providers/mimo-web-client.ts` around lines 146 - 150, Remove the redundant browser-specific credentials: "include" entry from the RequestInit object named options in the function where you build the request (const options: RequestInit = { method, headers, ... }). The Node.js fetch call already manages cookies via the manually-set Cookie header (see headers variable), so delete the credentials property from options and ensure no other code relies on it before committing.
🧹 Nitpick comments (24)
src/agents/grok-web-stream.ts (1)
510-512: 避免使用as any类型断言根据编码规范,应避免使用
any类型。此处的as any类型断言绕过了 TypeScript 的类型检查。♻️ 建议修复
考虑为错误事件定义正确的类型,或使用更精确的类型断言:
- } as any); + } as Parameters<typeof stream.push>[0]);或者定义一个明确的错误事件类型接口。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/agents/grok-web-stream.ts` around lines 510 - 512, Replace the unsafe "as any" cast on the emitted error payload with a proper typed shape: define an interface (e.g., ErrorEventPayload) matching the object fields (including timestamp) and use that type where the object is passed instead of "as any"; alternatively, if an existing event type exists in this module, cast to that specific type. Update the function/method that constructs/emits the event (the spot creating the object with timestamp: Date.now()) to return/accept the concrete type so TypeScript checks the payload structure rather than bypassing it with "any".src/providers/gemini-web-client-browser.ts (1)
231-231: 可选改进:提取长度阈值常量代码中使用了多个长度阈值(20、30、40、50 字符),它们分别用于不同的过滤场景。虽然当前实现功能正常,但可以考虑将这些"魔法数字"提取为命名常量以提升代码可读性:
const UI_ELEMENT_MIN_LENGTH = 20; // isSkip 使用 const MODEL_RESPONSE_MIN_LENGTH = 30; // 模型响应选择器策略 const FINAL_RESPONSE_MIN_LENGTH = 40; // 最终有效响应 const TEXT_BLOCK_MIN_LENGTH = 50; // 文本块候选当前分层过滤逻辑是合理的,此项为代码可维护性的细微优化。
Also applies to: 270-270, 284-284, 299-300
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/providers/gemini-web-client-browser.ts` at line 231, The code uses multiple magic numeric length thresholds (e.g., 20, 30, 40, 50) in filtering expressions such as the skip check (skipTexts.some((s) => t.includes(s)) || isGreeting(t) || t.length < 20) and other selection logic; extract these literals into clearly named constants (e.g., UI_ELEMENT_MIN_LENGTH, MODEL_RESPONSE_MIN_LENGTH, FINAL_RESPONSE_MIN_LENGTH, TEXT_BLOCK_MIN_LENGTH) and replace the raw numeric comparisons in the functions/expressions that reference skipTexts, isGreeting, and the response selection logic so the intent is explicit and maintainable.auth-summary.md (1)
86-88: 补充跨平台启动说明当前仅提供了 Windows 的 Chrome 启动命令。考虑到 README 中提到项目支持 macOS/Linux/Windows(WSL2),建议补充其他平台的启动方式或引用现有的
start-chrome-debug.sh脚本。💡 建议的改进方案
## 注意事项 1. **Chrome 调试模式**: 确保 Chrome 已启动调试模式 - - Windows: `chrome.exe --remote-debugging-port=9222` + ```bash + # macOS/Linux + ./start-chrome-debug.sh + + # Windows (如果不使用脚本) + chrome.exe --remote-debugging-port=9222 + ``` 2. **认证有效期**: Web 模型认证通常有有效期,需要定期重新认证🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@auth-summary.md` around lines 86 - 88, 在“Chrome 调试模式”项补充跨平台启动说明:在现有只列出 Windows 的 chrome.exe --remote-debugging-port=9222 之外,加入 macOS/Linux 的启动方法或明确引用现有脚本 start-chrome-debug.sh(例如说明可运行 ./start-chrome-debug.sh 来启动带远程调试端口的 Chrome),并保留 Windows 的手动命令作为备用说明;确保文本简洁、与 README 中声明的 macOS/Linux/Windows(WSL2) 支持一致并示例化如何在各平台启动调试模式。src/agents/pi-embedded-runner/run/attempt.ts (1)
1258-1357: 建议把 Web provider 分发提取为映射表,减少重复分支这次新增
xiaomimo-web分支与既有分支完全同构;继续沿用长链else if会让后续 provider 接入和回归测试成本持续上升。💡 可选重构方向
+const WEB_STREAM_FACTORIES: Record<string, (cookie: string) => StreamFn> = { + "deepseek-web": createDeepseekWebStreamFn as unknown as (cookie: string) => StreamFn, + "claude-web": createClaudeWebStreamFn as unknown as (cookie: string) => StreamFn, + "doubao-web": createDoubaoWebStreamFn as unknown as (cookie: string) => StreamFn, + "chatgpt-web": createChatGPTWebStreamFn as unknown as (cookie: string) => StreamFn, + "qwen-web": createQwenWebStreamFn as unknown as (cookie: string) => StreamFn, + "kimi-web": createKimiWebStreamFn as unknown as (cookie: string) => StreamFn, + "gemini-web": createGeminiWebStreamFn as unknown as (cookie: string) => StreamFn, + "grok-web": createGrokWebStreamFn as unknown as (cookie: string) => StreamFn, + "glm-web": createGlmWebStreamFn as unknown as (cookie: string) => StreamFn, + "glm-intl-web": createGlmIntlWebStreamFn as unknown as (cookie: string) => StreamFn, + "xiaomimo-web": createXiaomiMimoWebStreamFn as unknown as (cookie: string) => StreamFn, +}; @@ - } else if (params.model.api === "xiaomimo-web") { - const cookie = (await params.authStorage.getApiKey("xiaomimo-web")) || ""; - if (cookie) { - activeSession.agent.streamFn = createXiaomiMimoWebStreamFn(cookie) as StreamFn; - ensureCustomApiRegistered(params.model.api, activeSession.agent.streamFn); - } else { - log.warn(`[web-stream] no API key for xiaomimo-web`); - activeSession.agent.streamFn = streamSimple; - } + } else if (params.model.api && WEB_STREAM_FACTORIES[params.model.api]) { + const api = params.model.api; + const cookie = (await params.authStorage.getApiKey(api)) || ""; + if (cookie) { + activeSession.agent.streamFn = WEB_STREAM_FACTORIES[api](cookie); + ensureCustomApiRegistered(api, activeSession.agent.streamFn); + } else { + log.warn(`[web-stream] no API key for ${api}`); + activeSession.agent.streamFn = streamSimple; + } }As per coding guidelines: Keep files concise; extract helpers instead of "V2" copies. Use existing patterns for CLI options and dependency injection via
createDefaultDeps.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/agents/pi-embedded-runner/run/attempt.ts` around lines 1258 - 1357, The long chain of else-if blocks setting activeSession.agent.streamFn for each web provider (checking params.model.api, calling create*WebStreamFn and ensureCustomApiRegistered or defaulting to streamSimple) should be replaced with a mapping-driven helper to remove duplication: create a map from provider key (e.g., "chatgpt-web", "xiaomimo-web") to the corresponding factory function (createChatGPTWebStreamFn, createXiaomiMimoWebStreamFn, etc.), then implement a helper like getWebStreamFnFor(provider, authStorage) that looks up the factory, fetches the cookie via params.authStorage.getApiKey(provider), returns the created streamFn (or streamSimple when missing) and calls ensureCustomApiRegistered when present; replace the entire else-if sequence with a single call that assigns activeSession.agent.streamFn = await getWebStreamFnFor(params.model.api, params.authStorage) so future providers only need a map entry and follow existing createDefaultDeps patterns.src/commands/onboard-auth.config-core.ts (1)
618-618: 括号分组改进是正确的。将
existingProvider?.models as any[] || []改为(existingProvider?.models as any[]) || []使类型断言的作用范围更加明确,提高了代码可读性。注意:这些行使用了
as any[]类型断言,这与项目的严格类型规范有冲突。建议在后续重构中为existingProvider?.models定义更精确的类型。Also applies to: 647-647, 676-676, 705-705, 734-734, 763-763, 792-792, 821-821, 850-850, 879-879
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/onboard-auth.config-core.ts` at line 618, Wrap the type assertion so it applies to the optional property before the fallback: change the occurrences where `existingProvider?.models as any[] || []` is used (notably the `defaultModels` assignment in onboard-auth.config-core and the similar lines mentioned) to use a grouped assertion `(existingProvider?.models as any[]) || []`; update all listed occurrences (lines referenced in the review) to the same pattern to ensure the type assertion scope is correct and keep a note to replace `any[]` with a precise type in future refactors.extensions/askonce/src/askonce/adapters/gemini.ts (1)
5-8: LGTM!引号风格统一从单引号改为双引号。适配器元数据、凭证类型检查和响应状态字符串的格式化保持一致。
注意:Line 94 中的
as any类型断言是既有代码,建议在后续重构中为model和context参数提供更精确的类型定义,以符合严格类型规范。Also applies to: 10-17, 33-33, 46-51, 59-81, 88-89, 94-94, 96-96, 99-123
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@extensions/askonce/src/askonce/adapters/gemini.ts` around lines 5 - 8, Summary: remove the loose "as any" assertion for the Gemini adapter and add precise types for the model and context parameters. Fix: locate the function/method in this file that currently casts model and/or context with "as any" (used when calling createGeminiWebStreamFn or inside the adapter implementation that extends BaseAdapter), replace the assertion by declaring concrete parameter types (e.g., create/consume specific GeminiModel and GeminiContext types or reuse ModelResponse/AdapterQueryOptions where appropriate), update imports to include any new types, and adjust call sites to satisfy the stricter types; also ensure the file continues to use double-quote string style consistently. Reference symbols: createGeminiWebStreamFn, BaseAdapter, ModelResponse, AdapterQueryOptions, and the local model/context parameters that are currently cast "as any".extensions/askonce/src/index.ts (1)
70-70: 建议使用严格类型替代any。
options: any使用了any类型,这违反了编码规范中关于避免any的要求。建议定义具体的选项接口类型。♻️ 建议的类型定义
interface AskOnceOptions { models?: string; timeout?: string; output?: string; file?: string; stream?: boolean; list?: boolean; }然后将
options: any改为options: AskOnceOptions。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@extensions/askonce/src/index.ts` at line 70, Replace the use of the unsafe any type for the CLI options in runAskOnce by declaring a concrete interface (e.g., AskOnceOptions) that lists the expected properties (models?: string, timeout?: string, output?: string, file?: string, stream?: boolean, list?: boolean) and change the function signature from options: any to options: AskOnceOptions; update any internal references or destructuring in runAskOnce to rely on the typed properties so the compiler can check usage.extensions/web-models/index.ts (1)
312-312:as any类型断言可能隐藏类型问题。
api: provider.id as any的类型断言表明provider.id的类型与期望的api字段类型不兼容。考虑扩展api类型定义以包含新的提供商 ID。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@extensions/web-models/index.ts` at line 312, The code uses a blanket assertion "api: provider.id as any" which hides a real type mismatch; update the types so provider.id matches the api property's declared type instead of using any — locate the assignment of api (the object where api: provider.id as any is set) and either widen the api field's type union to include the provider.id values or cast provider.id to the correct specific type (e.g., the api enum/type) and adjust the provider id definition accordingly (references: provider.id and the api property in the object in extensions/web-models/index.ts).src/commands/auth-choice.apply.glm-web.ts (1)
33-39: 格式化可简化。
openUrl回调的显式包装略显冗余,但功能正确。如果openUrl已经返回Promise<boolean>或兼容类型,可以直接使用简写形式。♻️ 可选的简化形式
- openUrl: async (url) => { - await openUrl(url); - return true; - }, + openUrl,注意:仅当
openUrl函数本身返回符合预期的类型时适用。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/auth-choice.apply.glm-web.ts` around lines 33 - 39, 简短:loginZWeb 的 openUrl 回调显式包裹成 async (url) => { await openUrl(url); return true; } 冗余;如果外部 openUrl 已返回 Promise<boolean>(或兼容类型),请直接传入该函数以简化代码:在调用 loginZWeb 时将 openUrl 作为回调(而保留 onProgress: (msg) => spin.update(msg) 不变),并删除多余的 async 包装器,确保 loginZWeb、openUrl 和 spin.update 的预期返回类型仍然匹配。extensions/askonce/src/askonce/adapters/grok.ts (1)
93-93: 避免使用as any类型断言。根据编码规范,应保持严格的 TypeScript 类型,避免使用
any。这里使用了两次as any断言来绕过类型检查。建议为
streamFn的参数创建合适的类型定义,或者在grok-web-stream.ts中导出相应的类型以便复用。♻️ 建议的类型改进方向
+// 在 grok-web-stream.ts 或 types.ts 中定义 +interface StreamModel { + id: string; + api: string; + provider: string; +} + +interface StreamContext { + messages: Array<{ role: string; content: string }>; + systemPrompt: string; + tools: unknown[]; + sessionId: string; +} -const stream = streamFn(model as any, context as any, { signal: options?.signal }); +const stream = streamFn(model as StreamModel, context as StreamContext, { signal: options?.signal });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@extensions/askonce/src/askonce/adapters/grok.ts` at line 93, The call to streamFn currently uses two unsafe `as any` assertions; replace them by importing or defining proper TypeScript types for the stream function signature and the model/context objects (e.g. export the StreamFn type from grok-web-stream.ts or declare ModelType/ContextType there), update the streamFn variable to be typed as StreamFn and the parameters `model` and `context` to their concrete types, and then call streamFn(model, context, { signal: options?.signal }) without `as any` casts so the call is fully type-checked.extensions/askonce/src/askonce/adapters/qwen-cn.ts (1)
93-93: 与其他适配器相同的as any问题。建议在重构时统一处理所有适配器中的类型断言问题,创建共享的类型定义。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@extensions/askonce/src/askonce/adapters/qwen-cn.ts` at line 93, The call to streamFn in qwen-cn.ts uses broad "as any" casts (streamFn(model as any, context as any, { signal: options?.signal })) which hides typing errors; replace these ad-hoc casts by introducing or importing a shared adapter type for Model, Context, and StreamOptions used across adapters and update the call to use those concrete types (e.g., ensure streamFn, model, context, and options are typed to the new shared interfaces) so you can remove the "as any" assertions and keep type-safety consistent with other adapters.src/commands/onboard-web-auth.ts (1)
53-92: 建议为新提供商添加模型别名映射。
modelAliases对象中没有为xiaomimo-web定义别名映射。虽然代码会回退到使用原始modelId,但为保持一致性,建议添加相应的别名。♻️ 建议添加 xiaomimo-web 别名
"qwen-cn-web": { "qwen-turbo": "Qwen CN Web", }, + "xiaomimo-web": { + "xiaomimo-chat": "Xiaomi MiMo Chat", + }, };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/onboard-web-auth.ts` around lines 53 - 92, Add an entry for "xiaomimo-web" inside the existing modelAliases mapping so the onboarding flow uses friendly names instead of raw modelId for that provider; update the modelAliases object (the variable named modelAliases in this file) to include a "xiaomimo-web" key mapping its known model IDs to display names (e.g., map the xiaomimo model IDs to appropriate human-friendly aliases consistent with the other provider entries).extensions/askonce/src/askonce/adapters/deepseek.ts (1)
94-94: 与其他适配器相同的as any问题。此处与
grok.ts存在相同的类型断言问题。建议在统一修复时,为所有适配器创建共享的流函数参数类型定义。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@extensions/askonce/src/askonce/adapters/deepseek.ts` at line 94, The current cast "as any" used when calling streamFn in deepseek.ts (const stream = streamFn(model as any, context as any, { signal: options?.signal });) mirrors the same unsafe typing in grok.ts; replace these ad-hoc casts by introducing a shared StreamFunction parameter type (e.g., StreamFnParams or StreamInvokeArgs) used across adapters and update streamFn's signature to consume that type so you can call streamFn(model, context, { signal: options?.signal }) without casts; apply the same shared type to other adapters (grok.ts and any similar files) and adjust their streamFn declarations/consumers accordingly.src/commands/auth-choice.apply.mimo-web.ts (1)
9-10: 改进类型安全性。
argv参数未指定类型globalThis as any绕过了类型检查建议定义适当的类型来描述
__openclaw_browser__的结构。♻️ 建议的类型改进
+import type { Browser, Page } from "playwright-core"; + +interface OpenClawBrowserContext { + browser: Browser; + page: Page; +} + +declare global { + var __openclaw_browser__: Promise<OpenClawBrowserContext> | undefined; +} + export const mimoWebAuthCommand: CommandModule = { command: "mimo-web", describe: "Authenticate with Xiaomi MiMo-Web via browser login", - handler: async (argv) => { - const { browser, page } = await (globalThis as any).__openclaw_browser__; + handler: async (_argv) => { + if (!globalThis.__openclaw_browser__) { + throw new Error("Browser context not initialized"); + } + const { browser, page } = await globalThis.__openclaw_browser__;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/auth-choice.apply.mimo-web.ts` around lines 9 - 10, The handler's signature and use of globalThis.__openclaw_browser__ are currently untyped (handler: async (argv) and globalThis as any), so add proper TypeScript types: declare an interface describing the runtime object (e.g., an OpenClawBrowser shape containing browser and page) and augment globalThis (or add a global.d.ts) so __openclaw_browser__ has that type; then update the handler parameter to a concrete type (or unknown/Record if appropriate) and replace the any cast with a typed access to (globalThis as unknown as { __openclaw_browser__: OpenClawBrowser }). This ensures the references to browser and page inside handler are type-checked and documents the expected structure for __openclaw_browser__.extensions/askonce/src/cli.ts (2)
101-106: 进度回调中的event参数使用了any类型。应使用
ProgressEvent类型(已在types.ts中定义)。♻️ 建议的修复
+import type { ProgressEvent } from "../askonce/types.js"; + - const onProgress = (event: any) => { + const onProgress = (event: ProgressEvent) => { if (options.stream) { process.stdout.write(`\r[${event.modelId}] ${event.type}...`); } };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@extensions/askonce/src/cli.ts` around lines 101 - 106, The onProgress callback currently types its parameter as any; replace that with the defined ProgressEvent type from types.ts so the signature becomes onProgress(event: ProgressEvent) and update any imports to pull ProgressEvent from the module; ensure references to event.modelId and event.type remain valid with the ProgressEvent shape in types.ts (function name: onProgress, parameter: event, type: ProgressEvent).
46-49:options参数使用了any类型。根据编码规范应避免使用
any。建议定义具体的选项接口类型。♻️ 建议的类型定义
+interface AskOnceCliOptions { + list?: boolean; + models?: string; + output?: "console" | "markdown" | "json"; + timeout?: string; + stream?: boolean; + file?: string; +} + export async function registerAskOnceCli( program: Command, - options: any, + options: AskOnceCliOptions, question?: string[], ): Promise<void> {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@extensions/askonce/src/cli.ts` around lines 46 - 49, 函数 registerAskOnceCli 的参数 options 使用了 any,应改为具体类型以遵守编码规范:定义一个接口(例如 AskOnceCliOptions)列出所有可能的选项字段和类型(可选字段用 ? 标注),将 registerAskOnceCli(program: Command, options: AskOnceCliOptions, question?: string[]) 的签名更新为使用该接口,导出该接口供外部使用,并在函数体内/调用处修复因类型更改引入的任何属性访问或默认值处理(如用可选链和默认值赋值);同时更新相关调用点和测试以匹配新类型。src/providers/mimo-web.ts (3)
1-1: 应使用import type导入仅用于类型的 Playwright 类型。
Browser和Page仅用于方法签名中的类型标注,应使用import type以符合编码规范并优化打包体积。♻️ 建议修复
-import { Browser, Page } from "playwright"; +import type { Browser, Page } from "playwright";As per coding guidelines: "Use
import type { X }for type-only imports in TypeScript files."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/providers/mimo-web.ts` at line 1, The Playwright types Browser and Page are used only for type annotations—change the import to a type-only import by replacing the current import of Browser and Page with an import type for those symbols (i.e., use import type { Browser, Page } from "playwright") so TypeScript treats them as type-only and bundlers can tree-shake appropriately; update the import statement that currently references Browser and Page to use import type and leave any runtime imports unchanged.
200-242:chat方法静默吞掉错误,可能导致调试困难。第 236-238 行的
catch (e) { continue; }会静默忽略所有错误。建议至少记录错误信息,或者累积错误以便在所有端点都失败时提供更有用的诊断信息。💡 建议:记录或累积错误信息
+ const errors: string[] = []; for (const endpoint of endpoints) { try { const response = await fetch(endpoint, { // ... }); if (response.ok) { return await response.json(); } + errors.push(`${endpoint}: HTTP ${response.status}`); } catch (e) { + errors.push(`${endpoint}: ${e instanceof Error ? e.message : String(e)}`); continue; } } - throw new Error("无法连接到 MiMo API"); + throw new Error(`无法连接到 MiMo API: ${errors.join("; ")}`);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/providers/mimo-web.ts` around lines 200 - 242, The chat method currently swallows all exceptions in the for-loop catch (e) { continue; }, making failures hard to diagnose; update chat to record/log each error (e.g., push error messages into an errors array and/or call this.logger.error/console.error inside the catch) and also capture non-OK responses (include response.status/text) so that when all endpoints fail you throw a new Error that includes the accumulated error details (instead of the opaque "无法连接到 MiMo API"); reference the chat function, the endpoints array, the catch block and the final throw so you collect and surface useful diagnostics.
74-74: 避免使用any类型以保持类型安全。第 74 行的
request: any和第 179 行的cookies: any[]使用了any类型,违反了严格类型检查规范。♻️ 建议使用具体类型
- const requestHandler = async (request: any) => { + const requestHandler = async (request: import("playwright").Request) => { - private extractSessionId(cookies: any[]): string | undefined { + private extractSessionId(cookies: Array<{ name: string; value: string }>): string | undefined {As per coding guidelines: "Maintain strict TypeScript typing - avoid using
anytype."Also applies to: 179-179
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/providers/mimo-web.ts` at line 74, Replace the use of the unsafe any types by defining and using concrete types: change the request param type in requestHandler to a specific request interface (e.g. MimoRequest or the framework's Request type) and replace cookies: any[] with a typed array (e.g. Cookie[] or string[]), creating a Cookie interface if needed; update the function signature for requestHandler and any other functions referencing cookies to use these new types and import framework request types where appropriate (symbols to edit: requestHandler and the cookies parameter).src/providers/xiaomimo-web-auth.ts (1)
125-127: 生产环境中的调试日志代码中包含多个
console.log语句用于调试。考虑在生产环境中使用结构化日志记录器,或确保这些日志在正常情况下不会过于频繁地输出。Also applies to: 144-144, 151-153, 156-156
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/providers/xiaomimo-web-auth.ts` around lines 125 - 127, Replace all console.log calls in src/providers/xiaomimo-web-auth.ts (the debug prints that show cookie counts and related messages at the locations referenced) with the project's structured logger and appropriate log level (e.g., logger.debug or processLogger.debug) and/or guard them behind an environment or debug flag so they don't spam production; update each occurrence (the console.log at the cookies map and the other occurrences noted) to call the shared logger instance and include the same message text and contextual fields rather than raw console output.src/providers/mimo-web-auth.ts (1)
1-3: 移除未使用的导入并使用类型导入
EventEmitter被导入但从未使用。另外,Browser和Page仅用作类型注解,应使用import type。♻️ 建议的修复
-import { EventEmitter } from "events"; -// src/providers/mimo-web-auth.ts -import { Browser, Page } from "playwright"; +// src/providers/mimo-web-auth.ts +import type { Browser, Page } from "playwright";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/providers/mimo-web-auth.ts` around lines 1 - 3, Remove the unused EventEmitter import and change the Playwright type imports to type-only imports: replace the current import of Browser and Page with an `import type` for Browser and Page and delete the EventEmitter import so only needed type imports remain; target the imports referencing EventEmitter, Browser, and Page in mimo-web-auth.ts.src/agents/xiaomimo-web-stream.ts (1)
15-16: 模块级缓存无清理机制
sessionMap和clientMap是模块级 Map,会随着不同会话的使用而持续增长,但没有清理机制。在长时间运行的进程中,这可能导致内存泄漏。考虑实现以下方案之一:
- 设置最大缓存大小,使用 LRU 策略淘汰旧条目
- 提供显式的清理函数
- 使用 WeakMap(如果适用)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/agents/xiaomimo-web-stream.ts` around lines 15 - 16, sessionMap and clientMap are module-level Maps that can grow without bound; add a bounded cache or explicit cleanup to avoid leaks: replace or wrap these Maps with an LRU-capable structure (e.g., an LRUMap) or implement size checks on set to evict oldest entries, and expose an explicit API like clearSessionCache() and removeClient(sessionId: string) to allow callers to drop entries; alternatively evaluate using WeakMap for clientMap if keys are objects and lifecycle matches. Update usages of sessionMap and clientMap in XiaomiMimoWebClientBrowser-related code to use the new cache API (get/set/delete/clear) so eviction and explicit cleanup are honored.src/providers/mimo-web-client.ts (1)
54-56: 空的 catch 块吞噬错误信息空的 catch 块会吞噬错误信息,使调试变得困难。至少应该记录警告或保留错误信息。
♻️ 建议的修复
} catch (error) { - console.warn("Failed to fetch MiMo models via API, using defaults"); + console.warn("Failed to fetch MiMo models via API, using defaults:", error instanceof Error ? error.message : error); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/providers/mimo-web-client.ts` around lines 54 - 56, The catch block that handles failures when fetching MiMo models via API currently swallows the error; update the catch after the API fetch (the try/catch handling "Failed to fetch MiMo models via API, using defaults") to log the caught error details (e.g., include the error object or its message/stack) instead of discarding it so debugging info is preserved; keep the existing fallback behavior but change the console.warn call to include the error information (or use the project's logger if available).src/agents/mimo-web-stream.ts (1)
45-47: 错误处理丢失堆栈信息当前错误处理将原始错误转换为字符串,丢失了堆栈跟踪信息,这会使调试变得困难。
♻️ 建议的修复
} catch (error) { - throw new Error(`MiMo stream error: ${error}`); + const message = error instanceof Error ? error.message : String(error); + const wrappedError = new Error(`MiMo stream error: ${message}`); + if (error instanceof Error && error.stack) { + wrappedError.stack = error.stack; + } + throw wrappedError; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/agents/mimo-web-stream.ts` around lines 45 - 47, The catch block in mimo-web-stream.ts currently replaces the original error with a new Error containing only the stringified error, losing the original stack; update the catch to preserve the original error (either by throwing a new Error that uses the native "cause" option with the original error as cause, or by attaching the original error.stack to the new error before throwing) so the stack trace and original error metadata are retained; locate the catch handling the "MiMo stream error" and modify it accordingly (preserve original error object instead of interpolating it into a string).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: cefef7c9-ee7a-4861-ba78-d449e9dc6070
📒 Files selected for processing (92)
.openclaw-state.example/openclaw.jsonREADME.zh-CN.mdauth-summary.mdextensions/askonce/src/askonce/adapters/base.tsextensions/askonce/src/askonce/adapters/chatgpt.tsextensions/askonce/src/askonce/adapters/claude.tsextensions/askonce/src/askonce/adapters/deepseek.tsextensions/askonce/src/askonce/adapters/doubao.tsextensions/askonce/src/askonce/adapters/gemini.tsextensions/askonce/src/askonce/adapters/glm.tsextensions/askonce/src/askonce/adapters/grok.tsextensions/askonce/src/askonce/adapters/index.tsextensions/askonce/src/askonce/adapters/kimi.tsextensions/askonce/src/askonce/adapters/qwen-cn.tsextensions/askonce/src/askonce/adapters/qwen.tsextensions/askonce/src/askonce/adapters/registry.tsextensions/askonce/src/askonce/concurrent-engine.tsextensions/askonce/src/askonce/constants.tsextensions/askonce/src/askonce/formatters/console.tsextensions/askonce/src/askonce/formatters/index.tsextensions/askonce/src/askonce/formatters/json.tsextensions/askonce/src/askonce/formatters/markdown.tsextensions/askonce/src/askonce/index.tsextensions/askonce/src/askonce/query-orchestrator.tsextensions/askonce/src/askonce/types.tsextensions/askonce/src/cli.tsextensions/askonce/src/index.tsextensions/web-models/index.tsscripts/bundle-a2ui.shsrc/agents/chatgpt-web-stream.tssrc/agents/claude-web-stream.tssrc/agents/doubao-web-stream.tssrc/agents/gemini-web-stream.tssrc/agents/glm-intl-web-stream.tssrc/agents/glm-web-stream.tssrc/agents/grok-web-stream.tssrc/agents/kimi-web-stream.tssrc/agents/mimo-web-stream.tssrc/agents/models-config.providers.tssrc/agents/pi-embedded-runner/compact.tssrc/agents/pi-embedded-runner/run/attempt.tssrc/agents/qwen-cn-web-stream.tssrc/agents/qwen-web-stream.tssrc/agents/xiaomimo-web-stream.tssrc/browser/chrome.tssrc/commands/auth-choice.apply.chatgpt-web.tssrc/commands/auth-choice.apply.claude-web.tssrc/commands/auth-choice.apply.deepseek-web.tssrc/commands/auth-choice.apply.doubao-web.tssrc/commands/auth-choice.apply.gemini-web.tssrc/commands/auth-choice.apply.glm-intl-web.tssrc/commands/auth-choice.apply.glm-web.tssrc/commands/auth-choice.apply.grok-web.tssrc/commands/auth-choice.apply.kimi-web.tssrc/commands/auth-choice.apply.manus-api.tssrc/commands/auth-choice.apply.mimo-web.tssrc/commands/auth-choice.apply.qwen-web.tssrc/commands/onboard-auth.config-core.tssrc/commands/onboard-web-auth.tssrc/config/types.models.tssrc/providers/chatgpt-web-auth.tssrc/providers/chatgpt-web-client-browser.tssrc/providers/claude-web-auth.tssrc/providers/claude-web-client.tssrc/providers/deepseek-web-auth.tssrc/providers/doubao-web-auth.tssrc/providers/doubao-web-client-browser.tssrc/providers/doubao-web-client.tssrc/providers/gemini-web-client-browser.tssrc/providers/glm-intl-web-auth.tssrc/providers/glm-intl-web-client-browser.tssrc/providers/glm-web-auth.tssrc/providers/glm-web-client-browser.tssrc/providers/grok-web-auth.tssrc/providers/grok-web-client-browser.tssrc/providers/index.tssrc/providers/kimi-web-auth.tssrc/providers/kimi-web-client-browser.tssrc/providers/manus-api-client.tssrc/providers/mimo-web-auth.tssrc/providers/mimo-web-client.tssrc/providers/mimo-web.tssrc/providers/qwen-cn-web-auth.tssrc/providers/qwen-cn-web-client-browser.tssrc/providers/qwen-web-auth.tssrc/providers/qwen-web-client-browser.tssrc/providers/xiaomimo-web-auth.tssrc/providers/xiaomimo-web-client-browser.tssrc/providers/xiaomimo-web-client.tsui/src/ui/views/app-render.tsui/src/ui/views/app-view-state.tsui/src/ui/views/app.ts
| sessionId, | ||
| }; | ||
|
|
||
| const stream = streamFn(model as any, context as any, { signal: options?.signal }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# 检查 createKimiWebStreamFn 的类型定义
ast-grep --pattern 'export function createKimiWebStreamFn($_): $_ {
$$$
}'Repository: linuxhsj/openclaw-zero-token
Length of output: 36142
🏁 Script executed:
find . -path "*extensions/askonce/src/askonce/adapters/kimi.ts" -type fRepository: linuxhsj/openclaw-zero-token
Length of output: 121
🏁 Script executed:
# Search for StreamFn type definition
rg -n "type StreamFn|interface StreamFn" --type typescript --type tsxRepository: linuxhsj/openclaw-zero-token
Length of output: 106
🏁 Script executed:
# Check if askonce adapter exists and view its content around potential line 93
fd "kimi.ts" --path "*/askonce/*" -x wc -l {}Repository: linuxhsj/openclaw-zero-token
Length of output: 287
🏁 Script executed:
# Read the kimi.ts file to see the actual code
cat -n ./extensions/askonce/src/askonce/adapters/kimi.ts | head -150Repository: linuxhsj/openclaw-zero-token
Length of output: 4489
🏁 Script executed:
# Search for StreamFn type definition with correct syntax
rg "type StreamFn|interface StreamFn" -t ts -t tsxRepository: linuxhsj/openclaw-zero-token
Length of output: 99
🏁 Script executed:
# Also search for where StreamFn is imported or defined
rg "StreamFn" -B 2 -A 2 -t ts -t tsx | head -100Repository: linuxhsj/openclaw-zero-token
Length of output: 99
🏁 Script executed:
# Search for StreamFn definition without specifying file type
rg "type StreamFn|interface StreamFn" -B 2 -A 5Repository: linuxhsj/openclaw-zero-token
Length of output: 784
🏁 Script executed:
# Also search in all files for StreamFn
rg "StreamFn" -B 1 -A 1 | head -200Repository: linuxhsj/openclaw-zero-token
Length of output: 13872
🏁 Script executed:
# Check what types are exported from kimi-web-stream.ts
cat -n ./src/agents/kimi-web-stream.ts | head -50Repository: linuxhsj/openclaw-zero-token
Length of output: 2232
避免使用 as any 类型断言,应显式定义参数类型。
model as any 和 context as any 绕过了 TypeScript 类型检查。由于 model 和 context 对象已在调用前构造完成(包含 id、api、provider、messages、systemPrompt、tools、sessionId 等属性),可以为其定义具体的类型接口,而不是使用 any。
建议为 model 和 context 定义明确的类型,然后在调用 streamFn 时移除 as any 断言,以保持类型安全。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@extensions/askonce/src/askonce/adapters/kimi.ts` at line 93, The call to
streamFn currently uses unsafe assertions (model as any, context as any);
instead define explicit interfaces/types for the objects passed (e.g., ModelType
with id, api, provider, messages, systemPrompt, tools, sessionId, etc., and
ContextType for the streaming context) and update the streamFn signature to
accept those types so you can replace the casted call const stream =
streamFn(model as any, context as any, { signal: options?.signal }); with a
type-safe call const stream = streamFn(model, context, { signal: options?.signal
});; ensure any helper functions that construct model/context (the factory or
callers) are updated to return the new ModelType/ContextType so TypeScript
enforces correctness.
| "xiaomimo-web": [ | ||
| { | ||
| id: "xiaomimo-chat", | ||
| name: "MiMo Chat", | ||
| input: ["text"], | ||
| cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, | ||
| contextWindow: 128000, | ||
| maxTokens: 4096, | ||
| }, | ||
| ], |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Xiaomi MiMo AI model context window limit
💡 Result:
- MiMo‑7B: 32,768 tokens context window. [1]
- MiMo‑V2‑Flash: up to 256k tokens context window (commonly implemented as 262,144 tokens). [2][3]
Sources:
[1] The Decoder (MiMo‑7B) (the-decoder.com)
[2] arXiv: MiMo‑V2‑Flash Technical Report (arxiv.org)
[3] Hugging Face README (MiMo‑V2‑Flash) (huggingface.co)
Citations:
- 1: https://the-decoder.com/xiaomi-introduces-mimo-7b-a-compact-model-for-math-and-coding-tasks/?utm_source=openai
- 2: https://arxiv.org/abs/2601.02780
- 3: https://huggingface.co/XiaomiMiMo/MiMo-V2-Flash-Base/blob/main/README.md
🏁 Script executed:
# 在代码库中搜索 xiaomimo 相关的配置或注释
rg "xiaomimo" -i -A 5 -B 5Repository: linuxhsj/openclaw-zero-token
Length of output: 21161
🌐 Web query:
Xiaomi MiMo Chat API specifications maxTokens context window
💡 Result:
For Xiaomi MiMo (MiMo‑V2‑Flash) Chat API:
- Context window: 262,144 tokens (~256K) for
mimo-v2-flash. (docs.openclaw.ai) - Max output tokens (maxTokens / max_tokens): integrator configs for Xiaomi MiMo commonly cap generation at 8,192 tokens (i.e., you can request up to 8192 output tokens, subject to the overall context limit). (docs.openclaw.ai)
- Rule of thumb:
input_tokens + requested_output_tokens ≤ context_window(so long prompts reduce the available completion tokens). (docs.siliconflow.com)
Citations:
- 1: https://docs.openclaw.ai/xiaomi
- 2: https://docs.openclaw.ai/xiaomi
- 3: https://docs.siliconflow.com/en/api-reference/chat-completions/chat-completions?utm_source=openai
更新 Xiaomi MiMo Chat 的实际规格参数。
根据官方文档,MiMo-V2-Flash 模型支持 262,144 tokens 上下文窗口和 8,192 tokens 最大输出,但代码中配置为 contextWindow: 128000 和 maxTokens: 4096,约为实际限制的 50%。需要将这两个值更正为:
contextWindow: 262144maxTokens: 8192
注意:同一代码库中的 mimo-v2-pro 已正确配置 maxTokens: 8192,应保持一致。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@extensions/web-models/index.ts` around lines 270 - 279, Update the Xiaomi
MiMo Chat spec under the "xiaomimo-web" array for the entry with id
"xiaomimo-chat": change the contextWindow property from 128000 to 262144 and
change maxTokens from 4096 to 8192 to match the MiMo-V2-Flash official limits
(ensure consistency with the existing "mimo-v2-pro" maxTokens setting).
| # Try to find node via Windows PATH (useful if running in WSL or MinGW) | ||
| if command -v cmd.exe >/dev/null 2>&1; then | ||
| node_path=$(cmd.exe //c where node 2>/dev/null | head -n 1 | tr -d '\r') | ||
| if [ -n "$node_path" ] && [ -x "$node_path" ]; then | ||
| node_cmd="$node_path" | ||
| fi |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
f="$(fd 'bundle-a2ui\.sh$' | head -n1)"
echo "Checking: $f"
nl -ba "$f" | sed -n '34,62p'
echo
echo "[verify] cmd.exe fallback present:"
rg -n 'cmd\.exe\s*//c\s*where node' "$f"
echo
echo "[verify] path conversion present (expect wslpath/cygpath after fix):"
rg -n 'wslpath|cygpath' "$f" || true
echo
echo "[verify] where-node failure tolerant (expect || true after fix):"
rg -n 'where node.*\|\|\s*true' "$f" || trueRepository: linuxhsj/openclaw-zero-token
Length of output: 164
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find the file
f=$(find . -name 'bundle-a2ui.sh' -type f | head -n1)
echo "Checking: $f"
echo
# Show lines 34-62 with line numbers
echo "[Section: Lines 34-62]"
sed -n '34,62p' "$f" | cat -n -v
echo
echo "[verify] cmd.exe fallback present:"
grep -n 'cmd\.exe.*where node' "$f" || echo "Not found"
echo
echo "[verify] path conversion (wslpath/cygpath):"
grep -n 'wslpath\|cygpath' "$f" || echo "Not found"
echo
echo "[verify] where node failure handling (|| true):"
grep -n 'where node.*|| *true' "$f" || echo "Not found"
echo
echo "[verify] set -euo pipefail present:"
grep -n 'set -euo pipefail' "$f" || echo "Not found"Repository: linuxhsj/openclaw-zero-token
Length of output: 1692
🏁 Script executed:
#!/bin/bash
set -euo pipefail
f="./scripts/bundle-a2ui.sh"
echo "[1] 查看完整 compute_hash 函数:"
sed -n '34,62p' "$f"
echo
echo "[2] 检查 pipefail 行为:cmd.exe where node 是否有容错"
grep -A 3 "cmd.exe.*where node" "$f"
echo
echo "[3] 检查 -x 检查对 Windows 路径的有效性:"
echo " 如果 node_path 是 Windows 格式路径(如 C:\... 或 /c/...),"
echo " Bash 的 [ -x ... ] 检查会失败。来源:Bash 手册,-x 测试本地文件可执行性。"Repository: linuxhsj/openclaw-zero-token
Length of output: 1523
🏁 Script executed:
#!/bin/bash
# 验证 [ -x ] 对 Windows 路径的行为
echo "Testing [ -x ] with Windows-style paths:"
test_path="/c/Program Files/nodejs/node.exe"
if [ -x "$test_path" ]; then
echo " Path $test_path is executable (unlikely in WSL)"
else
echo " Path $test_path failed -x check (expected in WSL)"
fi
test_path2="C:\\Program Files\\nodejs\\node.exe"
if [ -x "$test_path2" ]; then
echo " Path $test_path2 is executable (unlikely)"
else
echo " Path $test_path2 failed -x check (expected)"
fiRepository: linuxhsj/openclaw-zero-token
Length of output: 253
cmd.exe where node 返回的 Windows 路径格式与 Bash 的 [ -x ] 检查不兼容,导致此回退分支在 WSL/MinGW 下失效。
第 49 行中 cmd.exe //c where node 可能返回 Windows 格式路径(如 C:\... 或 /c/Program Files/...),但第 50 行的 [ -x "$node_path" ] 在 WSL 环境中无法正确验证此类路径的可执行性,从而导致即使 where 命令成功,也会因路径检查失败而被拒绝。此外,代码中缺少 wslpath/cygpath 路径转换逻辑和显式的容错处理(如 || true),使得这个备选方案在目标环境中完全不可用。
建议添加路径格式转换,将 Windows 路径转换为 POSIX 格式,并增强命令失败的容错能力。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/bundle-a2ui.sh` around lines 47 - 52, The Windows path returned by
`cmd.exe //c where node` can be in Windows format and fail the POSIX file test
`[ -x "$node_path" ]`; update the fallback block that sets
`node_path`/`node_cmd` to (1) tolerate command failures with `|| true`, (2)
convert returned Windows paths to POSIX using `wslpath -a` (and fall back to
`cygpath -u` if `wslpath` is unavailable) before testing, and (3) only perform
the `[ -x "$node_path" ]` check on the converted POSIX path; reference the
`cmd.exe //c where node`, `node_path`, `node_cmd`, `[ -x "$node_path" ]`,
`wslpath`, and `cygpath` symbols when making these changes.
| return new Promise(async (resolve, reject) => { | ||
| try { | ||
| onProgress("Starting MiMo-Web authentication..."); | ||
|
|
||
| // Navigate to MiMo AI Studio | ||
| const loginUrl = "https://aistudio.xiaomimimo.com/"; | ||
| onProgress(`Opening ${loginUrl}...`); | ||
| await page.goto(loginUrl, { waitUntil: "networkidle" }); | ||
|
|
||
| // Wait for user to login manually (scan QR code or enter credentials) | ||
| onProgress("Please login to MiMo AI Studio in the browser..."); | ||
| onProgress("Waiting for login completion..."); | ||
|
|
||
| // Monitor network requests to capture authentication tokens | ||
| const credentials = await captureMimoCredentials(page, onProgress); | ||
|
|
||
| if (!credentials) { | ||
| throw new Error("Failed to capture MiMo credentials"); | ||
| } | ||
|
|
||
| onProgress("MiMo authentication successful!"); | ||
| resolve(credentials); | ||
| } catch (error) { | ||
| onProgress(`MiMo authentication failed: ${error}`); | ||
| reject(error); | ||
| } | ||
| }); |
There was a problem hiding this comment.
修复异步 Promise 执行器反模式
使用 async 函数作为 Promise 执行器是一个反模式(由静态分析工具 noAsyncPromiseExecutor 标记)。如果在第一个 await 之前抛出异常,该异常不会被 Promise 的 reject 捕获,而是会变成未处理的 Promise 拒绝。
由于函数本身已经是 async,完全不需要包装在 new Promise 中。
🐛 建议的修复
export async function loginMimoWeb(params: {
onProgress: (msg: string) => void;
openUrl: (url: string) => Promise<boolean>;
browser: Browser;
page: Page;
}): Promise<MimoAuthCredentials> {
const { onProgress, openUrl, browser, page } = params;
- return new Promise(async (resolve, reject) => {
- try {
- onProgress("Starting MiMo-Web authentication...");
+ onProgress("Starting MiMo-Web authentication...");
- // Navigate to MiMo AI Studio
- const loginUrl = "https://aistudio.xiaomimimo.com/";
- onProgress(`Opening ${loginUrl}...`);
- await page.goto(loginUrl, { waitUntil: "networkidle" });
+ // Navigate to MiMo AI Studio
+ const loginUrl = "https://aistudio.xiaomimimo.com/";
+ onProgress(`Opening ${loginUrl}...`);
+ await page.goto(loginUrl, { waitUntil: "networkidle" });
- // Wait for user to login manually (scan QR code or enter credentials)
- onProgress("Please login to MiMo AI Studio in the browser...");
- onProgress("Waiting for login completion...");
+ // Wait for user to login manually
+ onProgress("Please login to MiMo AI Studio in the browser...");
+ onProgress("Waiting for login completion...");
- // Monitor network requests to capture authentication tokens
- const credentials = await captureMimoCredentials(page, onProgress);
+ // Monitor network requests to capture authentication tokens
+ const credentials = await captureMimoCredentials(page, onProgress);
- if (!credentials) {
- throw new Error("Failed to capture MiMo credentials");
- }
+ if (!credentials) {
+ throw new Error("Failed to capture MiMo credentials");
+ }
- onProgress("MiMo authentication successful!");
- resolve(credentials);
- } catch (error) {
- onProgress(`MiMo authentication failed: ${error}`);
- reject(error);
- }
- });
+ onProgress("MiMo authentication successful!");
+ return credentials;
}🧰 Tools
🪛 Biome (2.4.7)
[error] 20-46: Promise executor functions should not be async.
(lint/suspicious/noAsyncPromiseExecutor)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/providers/mimo-web-auth.ts` around lines 20 - 46, The code wraps async
logic in a new Promise which is an anti-pattern; refactor the function to be
async and remove the new Promise wrapper: make the enclosing function async (if
it isn't already), move the try/catch out of the Promise executor, directly
await captureMimoCredentials(page, onProgress), and return the credentials
instead of resolve; on errors throw or rethrow inside the catch after calling
onProgress. Update references to captureMimoCredentials, onProgress and page in
this block so no new Promise(...) is used.
| if (stream) { | ||
| return response.body as unknown as Readable; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if Readable.fromWeb is available in the project's Node.js version
node -e "console.log('Node.js version:', process.version); console.log('Readable.fromWeb available:', typeof require('stream').Readable.fromWeb === 'function')"Repository: linuxhsj/openclaw-zero-token
Length of output: 129
🏁 Script executed:
cat -n src/providers/mimo-web-client.ts | head -200Repository: linuxhsj/openclaw-zero-token
Length of output: 6557
修复 Web ReadableStream 与 Node.js Readable 的类型不兼容问题
第 113 行的类型断言 response.body as unknown as Readable 存在严重的类型安全问题。response.body 是 Web Streams API 的 ReadableStream<Uint8Array>,而返回类型声明为 Node.js 的 Readable。这两个类型的 API 完全不兼容:
ReadableStream使用getReader()和read()方法Readable使用事件 ('data','end') 和pipe()方法
该类型断言会导致运行时错误,因为消费者将尝试调用 Node.js Readable 的 API。
使用 Readable.fromWeb() 进行正确转换(Node.js 17+ 已支持,项目使用 v24.3.0):
if (stream) {
- return response.body as unknown as Readable;
+ return Readable.fromWeb(response.body!);
}同时修复代码指南违规:
- 第 128 行:将
body?: any改为body?: Record<string, unknown> - 第 159 行:将
data: any改为data: unknown - 第 182 行:将
data: any改为data: unknown - 第 149 行:移除浏览器专用的
credentials: "include"选项
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/providers/mimo-web-client.ts` around lines 112 - 113, The code
incorrectly asserts a Web ReadableStream to a Node.js Readable at the return in
getResponse/getStream (response.body as unknown as Readable) which is unsafe and
will break consumers; replace that assertion by converting the Web stream to a
Node.js stream via Readable.fromWeb(response.body) when response.body is a
ReadableStream (use global.Readable or import from 'stream' depending on module
system) so the returned type matches Node's Readable. Also update the loose any
types: change body?: any to body?: Record<string, unknown>, and change
parameters named data: any to data: unknown in the functions referenced around
the diff, and remove the browser-only credentials: "include" option from the
fetch init so server-side fetch usage is correct. Ensure function/method names
mentioned (e.g., the method that returns response.body, any request/response
handlers around lines with body?: any and data: any) are updated accordingly and
preserve type-safe checks before calling Readable.fromWeb.
| constructor(options: XiaomiMimoWebClientOptions | string) { | ||
| if (typeof options === "string") { | ||
| try { | ||
| const parsed = JSON.parse(options) as XiaomiMimoWebClientOptions; | ||
| this.cookie = parsed.cookie; | ||
| } catch { | ||
| this.cookie = options; | ||
| } | ||
| } else { | ||
| this.cookie = options.cookie; | ||
| } |
There was a problem hiding this comment.
构造参数缺少 cookie 必填校验,存在运行时崩溃风险
当前 JSON 字符串解析后直接读取 parsed.cookie,若字段缺失会在后续 this.cookie.split(...) 触发异常。
💡 建议修复
constructor(options: XiaomiMimoWebClientOptions | string) {
if (typeof options === "string") {
try {
- const parsed = JSON.parse(options) as XiaomiMimoWebClientOptions;
- this.cookie = parsed.cookie;
+ const parsed = JSON.parse(options) as Partial<XiaomiMimoWebClientOptions>;
+ this.cookie = typeof parsed.cookie === "string" ? parsed.cookie : "";
} catch {
this.cookie = options;
}
} else {
this.cookie = options.cookie;
}
+ if (!this.cookie.trim()) {
+ throw new Error("XiaomiMimo cookie is required");
+ }
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/providers/xiaomimo-web-client-browser.ts` around lines 41 - 51, The
constructor in XiaomiMimoWebClient currently parses string options and assigns
this.cookie without validating presence, which can cause a runtime crash when
later calling this.cookie.split(...); update the constructor to validate that a
cookie string exists after JSON.parse (and when options is an object) and throw
a clear error or handle a safe fallback if absent; specifically, in the
constructor method (constructor(options: XiaomiMimoWebClientOptions | string))
check parsed.cookie (and options.cookie) for a non-empty string and either set
this.cookie only when valid or throw a descriptive error like "cookie is
required" so downstream code using this.cookie.split(...) is safe.
| const modelInternal = MODEL_MAP[params.model] || params.model; | ||
| const convId = params.conversationId || "0"; | ||
| const msgId = crypto.randomUUID().replace(/-/g, ""); | ||
|
|
||
| // 构建完整消息 (系统提示 + 用户消息) | ||
| let fullMessage = params.message; | ||
| if (params.systemPrompt && params.systemPrompt.length > 0) { | ||
| const sysSnippet = params.systemPrompt.slice(0, 500); // 系统提示截断 | ||
| fullMessage = `[系统提示: ${sysSnippet}]\n\n用户问题: ${params.message}`; | ||
| } |
There was a problem hiding this comment.
systemPrompt 与会话续接逻辑未真正生效
已拼接 fullMessage,但实际传入 page.evaluate 的仍是 params.message;同时保存了 this.conversationId 却未作为默认 convId 使用,导致多轮上下文难以延续。
💡 建议修复
- const convId = params.conversationId || "0";
+ const convId = params.conversationId ?? this.conversationId ?? "0";
@@
- { message: params.message, modelInternal, convId, msgId },
+ { message: fullMessage, modelInternal, convId, msgId },
);Also applies to: 226-227, 233-235
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/providers/xiaomimo-web-client-browser.ts` around lines 128 - 137, The
code builds fullMessage but still passes params.message into page.evaluate and
doesn't use this.conversationId as the default convId; update the logic so
convId defaults to this.conversationId when params.conversationId is absent and
ensure this.conversationId is updated when a new conversationId is produced, and
replace all usages where params.message is passed into page.evaluate (and the
similar occurrences around the other blocks at the indicated ranges) with the
constructed fullMessage so the systemPrompt and multi-turn context are actually
sent; refer to MODEL_MAP, params, convId, msgId, fullMessage, page.evaluate and
this.conversationId to locate and fix all instances.
| console.log(`[XiaomiMimo] Model: ${params.model} → ${modelInternal}`); | ||
| console.log(`[XiaomiMimo] ConvId: ${convId}, Msg: ${fullMessage.length} chars`); |
There was a problem hiding this comment.
避免在常规日志中输出用户输入/模型响应片段
当前日志会打印请求体片段和响应正文片段,容易把敏感内容落盘。建议仅记录长度、状态码和追踪 ID。
💡 建议修复
- console.log(`[XiaomiMimo] ConvId: ${convId}, Msg: ${fullMessage.length} chars`);
+ console.debug(`[XiaomiMimo] ConvId: ${convId}, MsgChars: ${fullMessage.length}`);
@@
- console.log("[MiMo XHR] Body:", JSON.stringify(body).substring(0, 200));
+ console.debug("[MiMo XHR] BodySize:", JSON.stringify(body).length);
@@
- console.log(`[XiaomiMimo] Response: ${result.data?.substring(0, 300)}`);
+ console.debug(`[XiaomiMimo] ResponseLen: ${result.data?.length || 0}`);Also applies to: 169-170, 229-230
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/providers/xiaomimo-web-client-browser.ts` around lines 139 - 140, The
current console.log statements in xiaomimo-web-client-browser (references: the
logs that interpolate params.model and modelInternal, and the ones using convId
and fullMessage) output user input/response fragments; replace these to only log
non-sensitive metadata (e.g., model name only if non-sensitive or better:
modelInternal identifier, message length via fullMessage.length, HTTP status
codes, and a trace/convId) and remove any direct content or body snippets;
update the same pattern for the other occurrences around the blocks that
reference fullMessage and response body (mentions at the other logs around lines
169-170 and 229-230) so all logs record length/status/trace id instead of
payloads.
Updated file paths to be relative instead of absolute.
|
此PR为抛砖引玉.希望各位大佬友好讨论. |
关于提示词限制的logs: 17:21:16 [XiaomiMimoWebStream] Stream completed. Parts: 1, Tools: 0 |
Summary
Describe the problem and fix in 2–5 bullets:
Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
User-visible / Behavior Changes
List user-visible changes (including defaults/config).
If none, write
None.Security Impact (required)
Yes/No)Yes/No)Yes/No)Yes/No)Yes/No)Yes, explain risk + mitigation:Repro + Verification
Environment
Steps
Expected
Actual
Evidence
Attach at least one:
Human Verification (required)
What you personally verified (not just CI), and how:
Review Conversations
If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.
Compatibility / Migration
Yes/No)Yes/No)Yes/No)Failure Recovery (if this breaks)
Risks and Mitigations
List only real risks for this PR. Add/remove entries as needed. If none, write
None.Summary by CodeRabbit
发布说明
新功能
文档