Dev#1809
Conversation
Update uv.lock
- 新增 src/lib/http 请求客户端深模块(createApiClient + ApiError + 路由未命中诊断 + 25 个单测),backendApi / statsApi / authApi 三个实例分别承担主后端、统计服务与认证流程 - 全量迁移 17 个 lib API 模块、plugin-api、setup 向导与 8 处页面内联请求到请求客户端,删除 fetch-with-auth.ts / api-helpers.ts / lib/api.ts,移除 axios 依赖(一言改原生 fetch) - 删除 memory-api 对 localhost:8001 的静默多路由兜底(保留诊断);修复 knowledge-api 裸 fetch 缺失认证;planner/adapter-config-api 的 HTTP 错误不再被当业务数据静默返回 - logout / checkAuthStatus 移至 lib/auth.ts;登录验证走 authApi,401 透传后端信息且不再触发整页跳转 - 引入 @tanstack/react-query:lib/query.ts 编码全局约定(查询失败局部呈现、变更失败全局 toast、不自动重试、不聚焦刷新),person 页面与 person-api 切换为 throw 契约样板
记录请求层与服务端状态的统一用语:请求客户端三实例、ApiError、 路由未命中诊断、查询/变更/queryKey 约定、配置与设置的边界。
fix: 修正插件列表竖向开关偏移 fix: 调整推理过程嵌入工具栏布局 build: 移除不存在的 axios 分包项
- 列表页 emoji / jargon、监控页 planner-monitor / replier-monitor、镜像源 plugin-mirrors 用 throw 契约 API 直接 useQuery/useMutation - 配置/详情页 prompt-generator / mcp-settings / plugin-detail 经新增的 unwrapApiResponse 桥接消费仍共享 ApiResponse 契约的 config-api / plugin-api,不触碰被多页共享的 API 文件 - 删除各页手写的 loading/error/data useState 与轮询 setInterval:列表参数进 queryKey、监控轮询改 refetchInterval、写操作成功后按领域前缀 invalidate;竞态防护交由 Query 默认行为 - 列表加载失败统一改为局部错误呈现 + 重试按钮(符合 lib/query.ts 约定);mcp-settings 配置草稿用渲染期重置模式从 query 数据 seed,避免 effect 内 setState
- 新增 hooks/useDataList.ts:包一个列表查询,queryKey 从分页/搜索/筛选状态派生;
内建搜索防抖、泛型 filters 袋、参数变化自动重置页码并清空选中、分页钳制、
invalidate 按领域前缀失效;queryFn 由页面适配各自 API 形状返回 {items,total}
- placeholderData: keepPreviousData 稳住翻页期间的 total 与分页钳制、避免骨架屏闪烁
- 13 个 renderHook 单测覆盖分页钳制/参数重置/多选清空/防抖驱动查询
- person/emoji/jargon 三页接入验证形态:各删约 8-12 个手写 useState 与防抖 useEffect,
竞态防护交由 Query 默认行为;列表 UI 渲染(表格/卡片)保留在各页
- CONTEXT.md 记入「数据列表(DataList)」术语
- 配置模板市场的分页/搜索/排序收编进 useDataList(固定每页 12、排序作 filters、无多选),删除手写的 packs/loading/page/totalPages/total/searchQuery/sortBy 状态与 loadPacks/useEffect - 点赞状态改为以当前页 Pack id 列表为键的旁路加载(id 不变时不重复检查);点赞后用 list.invalidate() 从服务端同步点赞数 - 搜索从每键即查改为 300ms 防抖;加载失败改为列表区局部呈现 + 重试
Walkthrough该 PR新增仓库指标 SVG 生成脚本并改造工作流;Dashboard 统一切换到新的 HTTP 客户端、React Query 与 DataList 状态;插件运行时增加主机版本探测、进度回调和插件标识传递;行为调试与裁切历史任务改为异步/后台执行,并同步更新版本与发布元数据。 Changes仓库指标自动生成
Dashboard 请求与查询基础设施
Dashboard 页面与样式迁移
插件运行时与进度上报
行为学习与调试流程
版本与发布元数据
Sequence Diagram(s)已包含于隐藏 review stack artifact 中。 Estimated code review effort🎯 5 (Critical) | ⏱️ ~120+ minutes Possibly related PRs
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts
|
There was a problem hiding this comment.
Actionable comments posted: 16
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 (4)
dashboard/src/routes/auth.tsx (1)
145-176:⚠️ Potential issue | 🟠 Major | ⚡ Quick win二次认证检查结果目前没有参与控制流。
Line 164 调用
checkAuthStatus()后,Line 167 到 Line 171 仍然会无条件跳转。这样只要/verify返回valid: true,即便 Cookie 没有真正写入,或者二次状态检查已经失败,页面也会先进入受保护路由,再被其它守卫踢回登录页,形成一次“假成功”。这里应根据二次检查结果决定是否继续导航,并在失败时留在当前页展示错误。建议修改
if (data.valid) { // Token 验证成功,Cookie 已由后端设置 // 等待一小段时间确保 Cookie 已设置 await new Promise((resolve) => setTimeout(resolve, 100)) // 再次检查认证状态 - await checkAuthStatus() + const isAuthenticated = await checkAuthStatus() + if (!isAuthenticated) { + setError(t('auth.verifyFailed')) + return false + } // 直接使用验证响应中的 is_first_setup 字段,避免额外请求 if (data.is_first_setup) { navigate({ to: '/setup' }) } else {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@dashboard/src/routes/auth.tsx` around lines 145 - 176, After calling checkAuthStatus() in the auth verification flow, use its return value to control navigation instead of unconditionally calling navigate; specifically, await checkAuthStatus(), store its boolean result (e.g., const authOk = await checkAuthStatus()), and only call navigate({ to: '/setup' }) or navigate({ to: '/' }) when authOk is true and data.valid is true, otherwise set an error (e.g., setError(...) or setError(t('auth.verifyFailed'))) and do not navigate so the page stays and displays the failure; also ensure setIsValidating(false) is called on both success and failure paths to clear the loading state. Reference: authApi.post('/api/webui/auth/verify'), checkAuthStatus(), navigate(...), setError(...), setIsValidating(true/false).dashboard/src/lib/adapter-config-api.ts (1)
34-46:⚠️ Potential issue | 🟠 Major | ⚡ Quick win不要把
success: false静默降级成“未配置路径”。Line 39 现在把
!data.success和!data.path合并成同一个null返回值。这样后端如果返回200 + { success: false }的业务失败,上层只会看到“没有保存过路径”,真实错误会被吞掉。这里应只在请求成功但path缺失时返回null,其余success: false场景继续抛错。建议修改
export async function getSavedConfigPath(): Promise<AdapterConfigPath | null> { const data = await backendApi.get<ConfigPathResponse>(`${API_BASE}/adapter-config/path`, { errorMessage: '获取适配器配置路径失败', }) - // 未保存过路径属于正常情况,返回 null 而不是抛错 - if (!data.success || !data.path) { + const result = requireSuccess(data, '获取适配器配置路径失败') + + // 未保存过路径属于正常情况,返回 null 而不是抛错 + if (!result.path) { return null } return { - path: data.path, - lastModified: data.lastModified, + path: result.path, + lastModified: result.lastModified, } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@dashboard/src/lib/adapter-config-api.ts` around lines 34 - 46, The code currently treats both !data.success and !data.path as the same "null" outcome and thus silences backend business failures; change the logic so that if data.success is false you surface the error (throw or return the error result) and only return null when the request succeeded but path is missing. Concretely, after calling backendApi.get<ConfigPathResponse>(...) check if (!data.success) and throw a meaningful error (or rethrow data.error/message), then if (data.success && !data.path) return null, otherwise return { path: data.path, lastModified: data.lastModified }. Reference: backendApi.get, ConfigPathResponse, API_BASE.dashboard/src/lib/config-api.ts (1)
43-56:⚠️ Potential issue | 🟠 Major | ⚡ Quick win不要把瞬时 HTTP 失败永久写进 schema 缓存。
这里会把任意带
status的ApiError结果保留在schemaRequestCache,但schemaRequestCache没有 TTL 或失效路径。只要第一次命中 500/502/503,后续整个会话都会持续返回同一个success: false,即使服务已经恢复,配置页也不会再重试,直到用户硬刷新。建议只缓存稳定失败(例如 404),或者像configDataCache一样给失败结果加 TTL。🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@dashboard/src/lib/config-api.ts` around lines 43 - 56, The code currently persists any ApiError with a status into schemaRequestCache, causing transient HTTP failures (500/502/503) to be cached forever; change the catch in the request creation so that ApiError handling only permanently caches stable errors (e.g., status === 404) and for other statuses either delete the cache entry and rethrow to allow immediate retries, or store a transient failure entry with a short TTL (same approach as configDataCache) so the service can recover without a hard refresh; update the catch block that references ApiError, schemaRequestCache, key and request to implement one of these two behaviors (prefer matching configDataCache’s TTL-on-failure strategy if you want automatic retries).dashboard/src/routes/plugin-detail.tsx (1)
113-117:⚠️ Potential issue | 🟠 Major | ⚡ Quick win切换到无仓库插件时未清空旧 README,会显示上一个插件内容
当前早退分支只关掉 loading,没有重置
readme。在同页面切换插件时会出现陈旧文档。🔧 建议修复
useEffect(() => { const loadReadme = async () => { + setReadme("") if (!plugin?.manifest?.repository_url) { setReadmeLoading(false) return }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@dashboard/src/routes/plugin-detail.tsx` around lines 113 - 117, The effect's early-return branch in useEffect / loadReadme only disables loading but doesn't clear the previous readme state; update that branch to also reset the readme (e.g., call setReadme('') or setReadme(null)) so switching to a plugin without repository_url won't show stale content, and also ensure any error/failure paths in loadReadme similarly clear readme (use setReadme and setReadmeLoading consistently).
🟡 Minor comments (5)
dashboard/src/hooks/useDataList.ts-79-83 (1)
79-83:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win
pageSize需要在入口处做正整数校验。
initialPageSize和setPageSize目前都能接收0、负数或NaN。这会把totalPages推成Infinity/NaN,随后把非法分页参数继续透传给queryFn。既然这是复用 Hook,建议统一 clamp 到>= 1的整数。Also applies to: 136-143
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@dashboard/src/hooks/useDataList.ts` around lines 79 - 83, Clamp and validate pageSize at both initialization and updates: when destructuring config, coerce initialPageSize to a positive integer >=1 (reject NaN/0/negatives) before passing to useState for pageSize, and wrap/replace setPageSizeRaw with a setter (e.g., setPageSize) that similarly coerces any new value to a Math.floor(Number(value)) >= 1 fallback; update any places that call setPageSizeRaw to use the new setter or ensure they pass valid values. Apply the same clamp logic to the second site mentioned (the setter logic around lines 136-143) so totalPages and queryFn always receive a valid integer pageSize. Ensure you keep page, setPageRaw, searchInput, and setSearchInputRaw unchanged except for replacing usages of setPageSizeRaw with the validated setter.dashboard/src/routes/plugin-mirrors.tsx-1-2 (1)
1-2:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win请去掉文件头 BOM,并统一用 UTF-8 无 BOM 保存。
Line 1 现在带了不可见 BOM,文件里也已经出现了多处中文注释乱码(例如 Line 55、Line 173、Line 231)。继续保留会让注释不可读,也容易在不同工具链里产生无意义 diff。
As per coding guidelines, "The project's primary language is Simplified Chinese for comments, logs, and WebUI display".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@dashboard/src/routes/plugin-mirrors.tsx` around lines 1 - 2, This file currently contains a UTF-8 BOM at the very start (visible before the first import like "import { useState, useEffect } from 'react'") and that BOM causes Chinese comment garbling; remove the BOM and re-save the file as UTF-8 without BOM so all Chinese comments render correctly, then commit the re-saved file; also scan other affected source files for BOMs (the reviewer noted corrupted Chinese comments elsewhere) and fix them the same way (remove BOM, save UTF-8 without BOM) so the toolchain and diffs remain clean.Source: Coding guidelines
dashboard/src/lib/survey-api.ts-85-99 (1)
85-99:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win新增的错误日志还在使用英文文案。
Line 97、Line 115、Line 142 和 Line 169 的
console.error仍是英文,和当前文件里其它中文提示混在一起后,排障时会把同一条链路拆成两套语言。建议这次一并统一成中文。As per coding guidelines,
**/*.{py,pyi,tsx,ts,jsx,js}: The project's primary language is Simplified Chinese for comments, logs, and WebUI display.Also applies to: 105-117, 123-144, 150-170
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@dashboard/src/lib/survey-api.ts` around lines 85 - 99, Catch blocks in survey-api.ts still log errors in English; update every console.error call in this file to a Chinese message while preserving the error object for diagnostics (e.g., replace "Error submitting survey:" with "提交问卷时出错:" and similarly convert the other English console.error texts to concise Chinese equivalents), ensuring you change all occurrences of console.error in this module so logs are consistently Simplified Chinese.Source: Coding guidelines
dashboard/src/routes/plugins/types.ts-53-67 (1)
53-67:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win进度细节使用 truthy 判断会吞掉
0值这里应判断“是否为
null/undefined”,而不是布尔真值;否则0会被错误忽略。🔧 建议修复
- const hasMirrorProgress = progress.mirror_index && progress.total_mirrors + const hasMirrorProgress = progress.mirror_index != null && progress.total_mirrors != null ... - if (progress.attempt && progress.max_attempts) { + if (progress.attempt != null && progress.max_attempts != null) { parts.push(`尝试 ${progress.attempt}/${progress.max_attempts}`) }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@dashboard/src/routes/plugins/types.ts` around lines 53 - 67, The code uses truthy checks that drop valid zero values; change the hasMirrorProgress computation from "const hasMirrorProgress = progress.mirror_index && progress.total_mirrors" to explicit null/undefined checks (e.g., "const hasMirrorProgress = progress.mirror_index != null && progress.total_mirrors != null"), and likewise replace "if (progress.attempt && progress.max_attempts)" with an explicit check ("if (progress.attempt != null && progress.max_attempts != null)") so 0 is treated as a legitimate value; keep the existing string checks for progress.mirror_name as-is.dashboard/src/lib/plugin-stats.ts-433-437 (1)
433-437:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win修复错误回退文案与空错误信息返回。
当前分支在失败路径里会返回乱码文案(如 Line 433、Line 437、Line 471),且 Line 467 在
detail.error缺失时可能返回error: undefined,会导致前端提示不可读或为空。建议统一为可读中文回退文案,并保证error始终是非空字符串。💡 建议修改
- return { success: false, error: getDetailError(error) || '鐠囧嫬鍨庢径杈Е' } + return { success: false, error: getDetailError(error) || "评分失败" } ... - return { success: false, error: '缂冩垹绮堕柨娆掝嚖' } + return { success: false, error: "网络请求失败" } ... - console.warn('Download recording rate limited') + console.warn("下载记录触发限流") ... - console.error('Failed to record download:', detailError) - return { success: false, error: detailError } + console.error("记录下载失败:", detailError ?? error.message) + return { success: false, error: detailError || "记录下载失败" } ... - return { success: false, error: '缂冩垹绮堕柨娆掝嚖' } + return { success: false, error: "网络请求失败" }As per coding guidelines
**/*.{py,pyi,tsx,ts,jsx,js}: The project's primary language is Simplified Chinese for comments, logs, and WebUI display.Also applies to: 460-471
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@dashboard/src/lib/plugin-stats.ts` around lines 433 - 437, 当前文件中多处失败分支返回了乱码或可能为 undefined 的 error 字段;请把所有这些返回值改为可读的中文回退文案并确保 error 始终为非空字符串。具体改法:在使用 getDetailError(error) 的位置(现有 return { success: false, error: getDetailError(error) || '…' })将回退文案替换为统一中文例如 '发生未知错误';在 console.error 路径的 return(当前返回 '缂冩垹绮堕柨娆掝嚖')改为 '提交评分失败';在直接读取 detail.error 的地方改为 detail?.error || '发生未知错误'。确保所有 return { success: false, error: ... } 保证 error 是非空字符串且使用简体中文可读文案,并保留原有的 console.error 调用以记录详细异常。Source: Coding guidelines
🧹 Nitpick comments (1)
dashboard/src/routes/config/pack-market.tsx (1)
96-103: ⚡ Quick win将点赞状态检查改为并发请求,避免串行网络瀑布。
当前
loadLikes在循环中逐个await checkPackLike,会线性叠加等待时间。这里可以并发请求并保持相同结果语义。♻️ 建议修改
async function loadLikes() { - const likedSet = new Set<string>() - for (const pack of packs) { - const liked = await checkPackLike(pack.id, userId) - if (liked) likedSet.add(pack.id) - } + const likeResults = await Promise.all( + packs.map(async (pack) => ({ id: pack.id, liked: await checkPackLike(pack.id, userId) })) + ) + const likedSet = new Set<string>(likeResults.filter((item) => item.liked).map((item) => item.id)) if (!cancelled) { setLikedPacks(likedSet) } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@dashboard/src/routes/config/pack-market.tsx` around lines 96 - 103, The loadLikes function currently awaits checkPackLike serially inside a for-loop causing a network waterfall; refactor it to run those checks concurrently by mapping packs to an array of promises (e.g., packs.map(p => checkPackLike(p.id, userId))) and await Promise.all on that array, then iterate the resolved boolean array to populate likedSet (keeping the existing cancelled guard and Set semantics). Ensure you still use the same symbols (loadLikes, checkPackLike, packs, likedSet, cancelled) and preserve behavior when cancelled is true.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/metrics.yml:
- Around line 16-18: Update the checkout step and the push to harden
supply-chain and credential scope: pin the actions/checkout@v4 usage to a
specific commit SHA (replace the tag with the action's full SHA), add with:
persist-credentials: false to the checkout configuration, and ensure any git
push uses a quoted/escaped ref (e.g. "refs/heads/${{ github.ref_name }}" or
quote "${{ github.ref_name }}") and performs authentication explicitly via the
provided GITHUB_TOKEN (set up git remote auth or use git -c
http.extraheader="AUTHORIZATION: bearer ${{ secrets.GITHUB_TOKEN }}" push ...)
as the last step so no writable credentials are persisted locally.
- Around line 28-37: The git push uses an unescaped interpolation of
github.ref_name which can lead to shell injection; capture the value into an
environment variable (e.g., REF = ${{ github.ref_name }}) and use that variable
in the push refspec while wrapping the entire refspec in double quotes (e.g.,
use "HEAD:${REF}") so the ref name is not parsed by the shell—also ensure any
use of the variable is not subject to word-splitting (keep it quoted) and avoid
further shell concatenation when calling git push.
In `@dashboard/src/hooks/useDataList.ts`:
- Around line 116-125: The code clamps page only inside goToPage; add logic to
clamp the current page whenever totalPages shrinks by adding a useEffect that
watches totalPages (and current page state if needed) and calls
setPageRaw(Math.min(currentPage, totalPages)) and clearSelection() when the page
must be reduced; reference the existing symbols totalPages, setPageRaw,
clearSelection, and goToPage so the new effect lives alongside them and prevents
staying on an out-of-range page after total changes.
In `@dashboard/src/index.css`:
- Around line 1089-1101: The Stylelint error is caused by missing empty lines
after custom properties; in both blocks for the selector
"[data-plugin-list-switch='true'][data-state='checked']
[data-dashboard-switch-thumb='true']" and
"[data-plugin-list-switch='true'][data-state='unchecked']
[data-dashboard-switch-thumb='true']", insert a single blank line after the two
custom-property declarations (--tw-translate-x and --tw-translate-y) and before
the subsequent "translate" declaration so the custom-property group is separated
by an empty line and satisfies declaration-empty-line-before.
In `@dashboard/src/lib/http/client.ts`:
- Around line 118-123: The request header construction in createApiClient
incorrectly uses object spread on headers (losing non-object HeadersInit forms)
and always injects 'Content-Type: application/json' even when body is absent;
change the logic around isFormData/body/headers so: detect FormData with
isFormData and if true pass headers through unchanged; if not FormData and body
is null/undefined, do not add Content-Type; otherwise (non-FormData with a body)
ensure you build headers without object-spreading a Headers or string[][] by
creating a Headers instance (e.g. const hdrs = headers instanceof Headers ?
headers : new Headers(headers) ) and then set
hdrs.set('Content-Type','application/json') only when body exists; assign
requestHeaders to either the original headers (when it's a Headers/string[][]
and no mutation needed) or hdrs for the fetch call, updating the code that
references requestHeaders to use this safe Headers handling.
In `@dashboard/src/lib/pack-api.ts`:
- Around line 279-283: The code is incorrectly treating error-shaped responses
as a LocalModelConfig by doing (responseData.config || responseData) as
LocalModelConfig; change this to explicitly validate/unpack the backend
response: check for a success flag or for the presence and shape of
responseData.config (or validate required fields like api_providers) and if the
response indicates failure or is missing expected config, throw or return an
error instead of casting; update both occurrences that use responseData ->
currentConfig so downstream functions like detectPackConflicts and applyPack
receive a real LocalModelConfig (or bail early), and ensure places that call
backendApi.get<ModelConfigResponse> handle the thrown error or returned failure
properly rather than proceeding with an invalid config.
In `@dashboard/src/lib/plugin-api/config.ts`:
- Around line 58-60: The code incorrectly treats an empty string config as
missing by using a truthy check on checked.config; update the condition in the
block that uses requireSuccess and ApiError so it only throws when
checked.config is null or undefined (e.g., if (checked.config === undefined ||
checked.config === null) or if (checked.config == null)), leaving empty-string
values allowed; keep the same error message throw new ApiError(checked.message
|| '获取配置失败', { detail: checked }) when the explicit null/undefined check fails.
In `@dashboard/src/lib/plugin-api/marketplace.ts`:
- Around line 327-345: The catch in checkGitStatus (and similarly in
getMaimaiVersion) incorrectly masks non-401 HTTP errors as a successful response
(success: true) with fabricated data; change the error handling so that for
ApiError with a defined status (and status !== 401) you do NOT return success:
true—instead return a failure response (e.g. { success: false, error: '无法检测 Git
安装状态' }) or rethrow as appropriate, while preserving the existing behavior of
rethrowing for network/transport failures and 401; update the catch block in
checkGitStatus (and the analogous catch in getMaimaiVersion) to return a proper
failure result rather than fabricated data.
In `@dashboard/src/routes/mcp-settings.tsx`:
- Around line 499-509: The current auto-fill branch unconditionally calls
setSeededVersion, setMcpConfig, setMcpSchema and setHasUnsavedChanges(false)
when dataVersion changes, which lets a refetch-on-reconnect overwrite unsaved
edits; fix by either disabling refetch-on-reconnect on the queries (pass
refetchOnReconnect: false into the useQueries/useQuery options that provide
configQuery and schemaQuery) or by guarding the fill logic with a check for
unsaved edits (wrap the block that calls setSeededVersion, setMcpConfig,
setMcpSchema and setHasUnsavedChanges(false) in if (!hasUnsavedChanges) so you
only overwrite when there are no local changes).
In `@dashboard/src/routes/monitor/planner-monitor.tsx`:
- Around line 52-76: Change the two queries (overviewQuery and chatLogsQuery) so
they preserve and expose React Query error state and use retry behavior instead
of hiding failures: stop coercing data to null on failure, surface query.error
and query.isError (e.g., keep overviewQuery.error and chatLogsQuery.error and
add overviewQuery.isError/chatLogsQuery.isError), add a sensible
retry/retryDelay config on both queries, and ensure the UI branches use isError
to show an error state rather than treating missing data as "empty" (update
derived vars overview/overviewLoading/chatLogs/chatLogsLoading to not mask
errors). Apply the same edits in the sibling file
dashboard/src/routes/monitor/replier-monitor.tsx (lines 52-76) so both
overviewQuery and chatLogsQuery there also expose error and retry behavior; no
other file changes required.
In `@dashboard/src/routes/plugin-config.tsx`:
- Around line 759-767: The plugin manifest URLs are used directly for anchor
hrefs (see plugin.manifest, manifestUrls, pluginHomepageUrl, pluginRepositoryUrl
and where pluginDetailItems is built), which allows javascript: or other unsafe
schemes; add a small URL sanitizer that only permits http: or https: schemes (or
returns undefined/null for anything else) and apply it when deriving
pluginHomepageUrl and pluginRepositoryUrl and in the other similar place noted
by the reviewer; ensure sanitized/empty values are used when constructing
pluginDetailItems so no unsafe href gets rendered.
In `@dashboard/src/routes/resource/jargon/index.tsx`:
- Around line 113-129: The query result for chat lists is being silently treated
as empty by falling back to [] (chatListQuery.data?.sidebar ?? [] and form ??
[]), which hides errors; change this by surfacing error states instead of
defaulting to empty arrays: either split the combined useQuery into two separate
queries calling getJargonChatList() and getJargonChatList({ include_empty: true
}) so each can show its own isLoading/isError and retry UI, or keep the single
chatListQuery but remove the silent fallbacks and use chatListQuery.isError /
chatListQuery.error to render an explicit error/ retry state (and only map data
when chatListQuery.data is present); update uses of chatList and formChatList to
guard on the query status rather than assuming [].
In `@src/maisaka/runtime.py`:
- Around line 1602-1604: The current concurrency guard in the method exits early
when self._trimmed_history_learning_task is running, which silently drops the
incoming context_messages; instead implement a short pending-batch queue: add a
field (e.g. self._pending_trimmed_history_batch) to store the latest
context_messages when self._trimmed_history_learning_task is not done, update
the branch that currently returns to set/replace that pending batch and log that
it's queued (using self.log_prefix and logger.info), and in the existing task's
done callback (where the previous task completion is handled) check
self._pending_trimmed_history_batch and, if present, clear it and schedule a new
trimmed-history learning task with that stored batch; alternatively replace with
a simple serial queue/worker that enqueues context_messages and processes them
one-by-one to guarantee no batches are dropped (ensure updates to
self._trimmed_history_learning_task reflect the newly scheduled task).
In `@src/plugin_runtime/__init__.py`:
- Around line 52-56: The code currently swallows all exceptions around
reading/parsing pyproject by using "except Exception: return ''"; change this so
only expected "file missing" cases are treated as a silent fallback and all
other errors are exposed: wrap the pyproject_path.open/tomllib.load block to
catch and return "" only for FileNotFoundError (and optionally
IsADirectoryError), but let PermissionError, tomllib.TOMLDecodeError and any
other exceptions propagate (or re-raise) so callers can see root causes;
reference the pyproject_path open, pyproject_file, and tomllib.load usages and
replace the broad except with targeted exception handlers that either return ""
for missing-file cases or re-raise/log and raise for all other errors.
In `@src/plugin_runtime/runner/manifest_validator.py`:
- Line 22: The module is calling tomllib.load(...) in
_detect_default_sdk_version and _load_host_dependency_requirements but never
imports tomllib, causing a NameError; fix by adding an import tomllib at the top
of the file (or move both parsing calls into a new shared helper that imports
tomllib) and ensure both _detect_default_sdk_version and
_load_host_dependency_requirements use that import/helper so tomllib is
available when reading pyproject.toml.
In `@src/webui/services/git_mirror_service.py`:
- Around line 730-741: The progress update is leaking raw Git stderr
(result.get("error") / last_error) to the progress channel via the
_update_progress call; remove the direct error text from the message payload and
instead send a generic failure message (e.g., "克隆失败,请查看服务端日志") and an error code
or boolean, while logging the full error server-side (or sanitizing it) inside
the surrounding code path that calls _update_progress (the same place where
result and last_error are available). Update the _update_progress invocation(s)
(e.g., the call that currently passes error=str(result.get("error") or "")) to
omit or replace the error field, and add a server-side log statement that
records the original result.get("error")/last_error after sanitization; apply
the same change to the other affected call sites noted in the review.
---
Outside diff comments:
In `@dashboard/src/lib/adapter-config-api.ts`:
- Around line 34-46: The code currently treats both !data.success and !data.path
as the same "null" outcome and thus silences backend business failures; change
the logic so that if data.success is false you surface the error (throw or
return the error result) and only return null when the request succeeded but
path is missing. Concretely, after calling
backendApi.get<ConfigPathResponse>(...) check if (!data.success) and throw a
meaningful error (or rethrow data.error/message), then if (data.success &&
!data.path) return null, otherwise return { path: data.path, lastModified:
data.lastModified }. Reference: backendApi.get, ConfigPathResponse, API_BASE.
In `@dashboard/src/lib/config-api.ts`:
- Around line 43-56: The code currently persists any ApiError with a status into
schemaRequestCache, causing transient HTTP failures (500/502/503) to be cached
forever; change the catch in the request creation so that ApiError handling only
permanently caches stable errors (e.g., status === 404) and for other statuses
either delete the cache entry and rethrow to allow immediate retries, or store a
transient failure entry with a short TTL (same approach as configDataCache) so
the service can recover without a hard refresh; update the catch block that
references ApiError, schemaRequestCache, key and request to implement one of
these two behaviors (prefer matching configDataCache’s TTL-on-failure strategy
if you want automatic retries).
In `@dashboard/src/routes/auth.tsx`:
- Around line 145-176: After calling checkAuthStatus() in the auth verification
flow, use its return value to control navigation instead of unconditionally
calling navigate; specifically, await checkAuthStatus(), store its boolean
result (e.g., const authOk = await checkAuthStatus()), and only call navigate({
to: '/setup' }) or navigate({ to: '/' }) when authOk is true and data.valid is
true, otherwise set an error (e.g., setError(...) or
setError(t('auth.verifyFailed'))) and do not navigate so the page stays and
displays the failure; also ensure setIsValidating(false) is called on both
success and failure paths to clear the loading state. Reference:
authApi.post('/api/webui/auth/verify'), checkAuthStatus(), navigate(...),
setError(...), setIsValidating(true/false).
In `@dashboard/src/routes/plugin-detail.tsx`:
- Around line 113-117: The effect's early-return branch in useEffect /
loadReadme only disables loading but doesn't clear the previous readme state;
update that branch to also reset the readme (e.g., call setReadme('') or
setReadme(null)) so switching to a plugin without repository_url won't show
stale content, and also ensure any error/failure paths in loadReadme similarly
clear readme (use setReadme and setReadmeLoading consistently).
---
Minor comments:
In `@dashboard/src/hooks/useDataList.ts`:
- Around line 79-83: Clamp and validate pageSize at both initialization and
updates: when destructuring config, coerce initialPageSize to a positive integer
>=1 (reject NaN/0/negatives) before passing to useState for pageSize, and
wrap/replace setPageSizeRaw with a setter (e.g., setPageSize) that similarly
coerces any new value to a Math.floor(Number(value)) >= 1 fallback; update any
places that call setPageSizeRaw to use the new setter or ensure they pass valid
values. Apply the same clamp logic to the second site mentioned (the setter
logic around lines 136-143) so totalPages and queryFn always receive a valid
integer pageSize. Ensure you keep page, setPageRaw, searchInput, and
setSearchInputRaw unchanged except for replacing usages of setPageSizeRaw with
the validated setter.
In `@dashboard/src/lib/plugin-stats.ts`:
- Around line 433-437: 当前文件中多处失败分支返回了乱码或可能为 undefined 的 error
字段;请把所有这些返回值改为可读的中文回退文案并确保 error 始终为非空字符串。具体改法:在使用 getDetailError(error) 的位置(现有
return { success: false, error: getDetailError(error) || '…' })将回退文案替换为统一中文例如
'发生未知错误';在 console.error 路径的 return(当前返回 '缂冩垹绮堕柨娆掝嚖')改为 '提交评分失败';在直接读取
detail.error 的地方改为 detail?.error || '发生未知错误'。确保所有 return { success: false,
error: ... } 保证 error 是非空字符串且使用简体中文可读文案,并保留原有的 console.error 调用以记录详细异常。
In `@dashboard/src/lib/survey-api.ts`:
- Around line 85-99: Catch blocks in survey-api.ts still log errors in English;
update every console.error call in this file to a Chinese message while
preserving the error object for diagnostics (e.g., replace "Error submitting
survey:" with "提交问卷时出错:" and similarly convert the other English console.error
texts to concise Chinese equivalents), ensuring you change all occurrences of
console.error in this module so logs are consistently Simplified Chinese.
In `@dashboard/src/routes/plugin-mirrors.tsx`:
- Around line 1-2: This file currently contains a UTF-8 BOM at the very start
(visible before the first import like "import { useState, useEffect } from
'react'") and that BOM causes Chinese comment garbling; remove the BOM and
re-save the file as UTF-8 without BOM so all Chinese comments render correctly,
then commit the re-saved file; also scan other affected source files for BOMs
(the reviewer noted corrupted Chinese comments elsewhere) and fix them the same
way (remove BOM, save UTF-8 without BOM) so the toolchain and diffs remain
clean.
In `@dashboard/src/routes/plugins/types.ts`:
- Around line 53-67: The code uses truthy checks that drop valid zero values;
change the hasMirrorProgress computation from "const hasMirrorProgress =
progress.mirror_index && progress.total_mirrors" to explicit null/undefined
checks (e.g., "const hasMirrorProgress = progress.mirror_index != null &&
progress.total_mirrors != null"), and likewise replace "if (progress.attempt &&
progress.max_attempts)" with an explicit check ("if (progress.attempt != null &&
progress.max_attempts != null)") so 0 is treated as a legitimate value; keep the
existing string checks for progress.mirror_name as-is.
---
Nitpick comments:
In `@dashboard/src/routes/config/pack-market.tsx`:
- Around line 96-103: The loadLikes function currently awaits checkPackLike
serially inside a for-loop causing a network waterfall; refactor it to run those
checks concurrently by mapping packs to an array of promises (e.g., packs.map(p
=> checkPackLike(p.id, userId))) and await Promise.all on that array, then
iterate the resolved boolean array to populate likedSet (keeping the existing
cancelled guard and Set semantics). Ensure you still use the same symbols
(loadLikes, checkPackLike, packs, likedSet, cancelled) and preserve behavior
when cancelled is true.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 589d8b85-9ada-400d-a379-6fdbed534b9d
⛔ Files ignored due to path filters (4)
dashboard/bun.lockis excluded by!**/*.lockdashboard/package-lock.jsonis excluded by!**/package-lock.jsondepends-data/repository-metrics.svgis excluded by!**/*.svg,!depends-data/**uv.lockis excluded by!**/*.lock
📒 Files selected for processing (96)
.github/scripts/generate_repository_metrics.py.github/workflows/metrics.ymlREADME.mdchangelogs/changelog.mddashboard/CONTEXT.mddashboard/package.jsondashboard/src/components/dynamic-form/DynamicConfigForm.tsxdashboard/src/components/dynamic-form/DynamicField.tsxdashboard/src/components/layout/Header.tsxdashboard/src/components/ui/slider.tsxdashboard/src/hooks/__tests__/useDataList.test.tsxdashboard/src/hooks/use-auth.tsdashboard/src/hooks/useDataList.tsdashboard/src/index.cssdashboard/src/lib/adapter-config-api.tsdashboard/src/lib/api-base.tsdashboard/src/lib/api-helpers.tsdashboard/src/lib/api.tsdashboard/src/lib/auth.tsdashboard/src/lib/behavior-api.tsdashboard/src/lib/config-api.tsdashboard/src/lib/emoji-api.tsdashboard/src/lib/expression-api.tsdashboard/src/lib/fetch-with-auth.tsdashboard/src/lib/http/__tests__/client.test.tsdashboard/src/lib/http/client.tsdashboard/src/lib/http/compat.tsdashboard/src/lib/http/envelope.tsdashboard/src/lib/http/errors.tsdashboard/src/lib/http/index.tsdashboard/src/lib/http/instances.tsdashboard/src/lib/jargon-api.tsdashboard/src/lib/knowledge-api.tsdashboard/src/lib/log-websocket.tsdashboard/src/lib/memory-api.tsdashboard/src/lib/pack-api.tsdashboard/src/lib/person-api.tsdashboard/src/lib/planner-api.tsdashboard/src/lib/plugin-api/config.tsdashboard/src/lib/plugin-api/install-flow.tsdashboard/src/lib/plugin-api/installed.tsdashboard/src/lib/plugin-api/marketplace.tsdashboard/src/lib/plugin-api/types.tsdashboard/src/lib/plugin-stats.tsdashboard/src/lib/prompt-api.tsdashboard/src/lib/prompt-generator-api.tsdashboard/src/lib/query.tsdashboard/src/lib/reasoning-process-api.tsdashboard/src/lib/survey-api.tsdashboard/src/lib/system-api.tsdashboard/src/lib/unified-ws.tsdashboard/src/lib/version.tsdashboard/src/main.tsxdashboard/src/routes/auth.tsxdashboard/src/routes/chat/index.tsxdashboard/src/routes/config/bot.tsxdashboard/src/routes/config/modelProvider/ProviderList.tsxdashboard/src/routes/config/pack-market.tsxdashboard/src/routes/index.tsxdashboard/src/routes/mcp-settings.tsxdashboard/src/routes/monitor/planner-monitor.tsxdashboard/src/routes/monitor/replier-monitor.tsxdashboard/src/routes/person.tsxdashboard/src/routes/plugin-config.tsxdashboard/src/routes/plugin-detail.tsxdashboard/src/routes/plugin-mirrors.tsxdashboard/src/routes/plugins/InstallDialog.tsxdashboard/src/routes/plugins/PluginCard.tsxdashboard/src/routes/plugins/types.tsdashboard/src/routes/prompt-generator.tsxdashboard/src/routes/reasoning-process.tsxdashboard/src/routes/resource/behavior/index.tsxdashboard/src/routes/resource/emoji/EmojiDialogs.tsxdashboard/src/routes/resource/emoji/index.tsxdashboard/src/routes/resource/expression/index.tsxdashboard/src/routes/resource/jargon/index.tsxdashboard/src/routes/settings/LocalCacheTab.tsxdashboard/src/routes/settings/OtherTab.tsxdashboard/src/routes/setup/api.tsdashboard/vite.config.tspyproject.tomlpytests/webui/test_plugin_management_routes.pyrequirements.txtsrc/chat/utils/statistic.pysrc/config/config.pysrc/learners/behavior_pattern_maintenance.pysrc/learners/behavior_selector.pysrc/maisaka/runtime.pysrc/plugin_runtime/__init__.pysrc/plugin_runtime/host/rpc_server.pysrc/plugin_runtime/host/supervisor.pysrc/plugin_runtime/runner/manifest_validator.pysrc/webui/routers/behavior.pysrc/webui/routers/plugin/management.pysrc/webui/routers/plugin/progress.pysrc/webui/services/git_mirror_service.py
💤 Files with no reviewable changes (5)
- dashboard/src/lib/api.ts
- dashboard/src/lib/fetch-with-auth.ts
- dashboard/src/lib/api-base.ts
- dashboard/src/lib/api-helpers.ts
- dashboard/vite.config.ts
| - uses: actions/checkout@v4 | ||
| with: | ||
| token: ${{ secrets.METRICS_TOKEN }} | ||
| filename: assets/repository-metrics.svg | ||
| template: repository | ||
| user: Mai-with-u | ||
| repo: MaiBot | ||
| base: header, activity, repositories, metadata | ||
| plugin_followup: yes | ||
| plugin_contributors: yes | ||
| ref: ${{ github.ref }} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
ls -la .github/workflows || true
sed -n '1,120p' .github/workflows/metrics.ymlRepository: Mai-with-u/MaiBot
Length of output: 1845
🏁 Script executed:
#!/bin/bash
set -euo pipefail
ls -la .github/workflows || true
sed -n '1,200p' .github/workflows/metrics.ymlRepository: Mai-with-u/MaiBot
Length of output: 1845
加固 metrics.yml 的 checkout 供应链与写权限边界。
.github/workflows/metrics.yml 中:
actions/checkout@v4未 pin 到提交 SHA(存在供应链投毒风险)。checkout未显式设置persist-credentials: false:会保留可写凭据到本地 git 配置;后续步骤再git push,扩大凭据暴露面。git push origin HEAD:${{ github.ref_name }}将github.ref_name直接拼入 shell:建议对分支名进行引用/转义(例如用引号包裹),并在关闭persist-credentials后在最后的 push 步骤中显式使用GITHUB_TOKEN完成认证。
🧰 Tools
🪛 zizmor (1.25.2)
[warning] 16-18: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[error] 16-16: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/metrics.yml around lines 16 - 18, Update the checkout step
and the push to harden supply-chain and credential scope: pin the
actions/checkout@v4 usage to a specific commit SHA (replace the tag with the
action's full SHA), add with: persist-credentials: false to the checkout
configuration, and ensure any git push uses a quoted/escaped ref (e.g.
"refs/heads/${{ github.ref_name }}" or quote "${{ github.ref_name }}") and
performs authentication explicitly via the provided GITHUB_TOKEN (set up git
remote auth or use git -c http.extraheader="AUTHORIZATION: bearer ${{
secrets.GITHUB_TOKEN }}" push ...) as the last step so no writable credentials
are persisted locally.
Source: Linters/SAST tools
| - name: Commit repository metrics | ||
| run: | | ||
| git config user.name "github-actions[bot]" | ||
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | ||
| git add depends-data/repository-metrics.svg | ||
| if git diff --cached --quiet; then | ||
| echo "No metrics changes." | ||
| else | ||
| git commit -m "docs: 更新仓库状态图" | ||
| git push origin HEAD:${{ github.ref_name }} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
ls -la
find . -maxdepth 4 -type f -path "./.github/workflows/*" -name "metrics.yml" -print
sed -n '1,120p' .github/workflows/metrics.ymlRepository: Mai-with-u/MaiBot
Length of output: 3995
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,140p' .github/workflows/metrics.ymlRepository: Mai-with-u/MaiBot
Length of output: 1074
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,200p' .github/workflows/metrics.ymlRepository: Mai-with-u/MaiBot
Length of output: 1074
🏁 Script executed:
#!/bin/bash
set -euo pipefail
wc -l .github/workflows/metrics.yml || true
nl -ba .github/workflows/metrics.yml | sed -n '1,120p'Repository: Mai-with-u/MaiBot
Length of output: 155
🌐 Web query:
Git refname allowed characters semicolon ; forbidden characters list space ~ ^ : ? * [ \ .. @{
💡 Result:
Git reference names (refnames), such as branch or tag names, must adhere to strict formatting rules enforced by the git-check-ref-format command [1][2]. The following characters and sequences are explicitly forbidden in Git refnames [1][3][4]: - ASCII control characters (bytes with values lower than 040, or 177 DEL) [1][5]. - Space, tilde (~), caret (^), and colon (:) [1][4]. - Question mark (?), asterisk (*), and open bracket ([) [1][3]. - Backslash () [1][3][4]. - The sequence @{ [1][4]. - The single character @ [1][4]. - Two consecutive dots (..) [1][5]. - A leading slash (/) or ending with a slash (/) [1][4]. - Multiple consecutive slashes (e.g., //) [1][4]. - A name ending with a dot (.) [1][4]. - Any slash-separated component beginning with a dot (.) [1][3]. - Any slash-separated component ending with the sequence.lock [1][4]. While you specifically asked about the semicolon (;), it is not listed in the official documentation as an explicitly forbidden character [1][3]. However, Git refnames must generally contain at least one slash (/) to define a category (such as heads/ or tags/), unless the --allow-onelevel option is used [1][4]. Additionally, while not every character is forbidden, it is strongly recommended to stick to alphanumeric characters, slashes, hyphens, and single dots to ensure compatibility across different operating systems and Git tooling [1].
Citations:
- 1: https://git-scm.org/docs/git-check-ref-format
- 2: https://git-scm.com/docs/git-check-ref-format
- 3: https://code.googlesource.com/git/+/HEAD/Documentation/git-check-ref-format.adoc
- 4: https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html
- 5: https://stackoverflow.com/questions/3651860/which-characters-are-illegal-within-a-branch-name
🌐 Web query:
GitHub branch name allowed characters semicolon dollar sign $ parentheses
💡 Result:
Git branch names are permitted to contain both semicolons (;) and dollar signs (
Citations:
- 1: https://git-scm.com/docs/git-check-ref-format
- 2: https://docs.github.com/en/get-started/using-git/dealing-with-special-characters-in-branch-and-tag-names
- 3: https://stackoverflow.com/questions/3651860/which-characters-are-illegal-within-a-branch-name
- 4: https://www.baeldung.com/ops/git-illegal-characters-ref-branch-tag
- 5: https://docs.github.com/en/enterprise-server@3.9/get-started/using-git/dealing-with-special-characters-in-branch-and-tag-names
- 6: https://github.com/github/docs/blob/main/content/get-started/using-git/dealing-with-special-characters-in-branch-and-tag-names.md
避免在 shell 中直接拼接 github.ref_name(可能导致命令注入)
github.ref_name 可能包含 shell 元字符(如 ;、$ 等)。当前 git push origin HEAD:${{ github.ref_name }} 会把这些内容直接交给脚本解析;应先落到环境变量并对整个 refspec 加整体引号。
🔧 建议修改
- name: Commit repository metrics
+ env:
+ TARGET_REF: ${{ github.ref_name }}
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]`@users.noreply.github.com`"
git add depends-data/repository-metrics.svg
if git diff --cached --quiet; then
echo "No metrics changes."
else
git commit -m "docs: 更新仓库状态图"
- git push origin HEAD:${{ github.ref_name }}
+ git push origin "HEAD:${TARGET_REF}"
fi📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - name: Commit repository metrics | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git add depends-data/repository-metrics.svg | |
| if git diff --cached --quiet; then | |
| echo "No metrics changes." | |
| else | |
| git commit -m "docs: 更新仓库状态图" | |
| git push origin HEAD:${{ github.ref_name }} | |
| - name: Commit repository metrics | |
| env: | |
| TARGET_REF: ${{ github.ref_name }} | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]`@users.noreply.github.com`" | |
| git add depends-data/repository-metrics.svg | |
| if git diff --cached --quiet; then | |
| echo "No metrics changes." | |
| else | |
| git commit -m "docs: 更新仓库状态图" | |
| git push origin "HEAD:${TARGET_REF}" |
🧰 Tools
🪛 zizmor (1.25.2)
[error] 37-37: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/metrics.yml around lines 28 - 37, The git push uses an
unescaped interpolation of github.ref_name which can lead to shell injection;
capture the value into an environment variable (e.g., REF = ${{ github.ref_name
}}) and use that variable in the push refspec while wrapping the entire refspec
in double quotes (e.g., use "HEAD:${REF}") so the ref name is not parsed by the
shell—also ensure any use of the variable is not subject to word-splitting (keep
it quoted) and avoid further shell concatenation when calling git push.
Source: Linters/SAST tools
| const total = query.data?.total ?? 0 | ||
| const totalPages = Math.max(1, Math.ceil(total / pageSize)) | ||
|
|
||
| const goToPage = useCallback( | ||
| (n: number) => { | ||
| setPageRaw(Math.min(Math.max(1, Math.trunc(n)), totalPages)) | ||
| clearSelection() | ||
| }, | ||
| [totalPages, clearSelection], | ||
| ) |
There was a problem hiding this comment.
在 totalPages 变小时补钳当前页。
现在只在 goToPage 调用时钳制页码;如果删除、筛选或外部失效让 total 变小,page 会保留旧值。像表情包页在最后一页删完数据后,这里会继续请求越界页并把页面停在空列表上。
🔧 建议修改
const total = query.data?.total ?? 0
const totalPages = Math.max(1, Math.ceil(total / pageSize))
+
+ useEffect(() => {
+ setPageRaw((prev) => Math.min(prev, totalPages))
+ }, [totalPages])
const goToPage = useCallback(📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const total = query.data?.total ?? 0 | |
| const totalPages = Math.max(1, Math.ceil(total / pageSize)) | |
| const goToPage = useCallback( | |
| (n: number) => { | |
| setPageRaw(Math.min(Math.max(1, Math.trunc(n)), totalPages)) | |
| clearSelection() | |
| }, | |
| [totalPages, clearSelection], | |
| ) | |
| const total = query.data?.total ?? 0 | |
| const totalPages = Math.max(1, Math.ceil(total / pageSize)) | |
| useEffect(() => { | |
| setPageRaw((prev) => Math.min(prev, totalPages)) | |
| }, [totalPages]) | |
| const goToPage = useCallback( | |
| (n: number) => { | |
| setPageRaw(Math.min(Math.max(1, Math.trunc(n)), totalPages)) | |
| clearSelection() | |
| }, | |
| [totalPages, clearSelection], | |
| ) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@dashboard/src/hooks/useDataList.ts` around lines 116 - 125, The code clamps
page only inside goToPage; add logic to clamp the current page whenever
totalPages shrinks by adding a useEffect that watches totalPages (and current
page state if needed) and calls setPageRaw(Math.min(currentPage, totalPages))
and clearSelection() when the page must be reduced; reference the existing
symbols totalPages, setPageRaw, clearSelection, and goToPage so the new effect
lives alongside them and prevents staying on an out-of-range page after total
changes.
| [data-plugin-list-switch='true'][data-state='checked'] [data-dashboard-switch-thumb='true'] { | ||
| --tw-translate-x: 0 !important; | ||
| --tw-translate-y: 1rem !important; | ||
| translate: 0 1rem !important; | ||
| transform: none !important; | ||
| } | ||
|
|
||
| [data-plugin-list-switch='true'][data-state='unchecked'] [data-dashboard-switch-thumb='true'] { | ||
| --tw-translate-x: 0 !important; | ||
| --tw-translate-y: 0 !important; | ||
| translate: 0 0 !important; | ||
| transform: none !important; | ||
| } |
There was a problem hiding this comment.
修复 Stylelint 报错:在自定义属性后补空行
当前块在 Line 1092 与 Line 1099 触发 declaration-empty-line-before,会导致样式检查失败。
🔧 建议修复
[data-plugin-list-switch='true'][data-state='checked'] [data-dashboard-switch-thumb='true'] {
--tw-translate-x: 0 !important;
--tw-translate-y: 1rem !important;
+
translate: 0 1rem !important;
transform: none !important;
}
[data-plugin-list-switch='true'][data-state='unchecked'] [data-dashboard-switch-thumb='true'] {
--tw-translate-x: 0 !important;
--tw-translate-y: 0 !important;
+
translate: 0 0 !important;
transform: none !important;
}As per coding guidelines,Don't flag formatting issues unless specifically indicated by static analysis tools;该处已被 Stylelint 明确报错。
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| [data-plugin-list-switch='true'][data-state='checked'] [data-dashboard-switch-thumb='true'] { | |
| --tw-translate-x: 0 !important; | |
| --tw-translate-y: 1rem !important; | |
| translate: 0 1rem !important; | |
| transform: none !important; | |
| } | |
| [data-plugin-list-switch='true'][data-state='unchecked'] [data-dashboard-switch-thumb='true'] { | |
| --tw-translate-x: 0 !important; | |
| --tw-translate-y: 0 !important; | |
| translate: 0 0 !important; | |
| transform: none !important; | |
| } | |
| [data-plugin-list-switch='true'][data-state='checked'] [data-dashboard-switch-thumb='true'] { | |
| --tw-translate-x: 0 !important; | |
| --tw-translate-y: 1rem !important; | |
| translate: 0 1rem !important; | |
| transform: none !important; | |
| } | |
| [data-plugin-list-switch='true'][data-state='unchecked'] [data-dashboard-switch-thumb='true'] { | |
| --tw-translate-x: 0 !important; | |
| --tw-translate-y: 0 !important; | |
| translate: 0 0 !important; | |
| transform: none !important; | |
| } |
🧰 Tools
🪛 Stylelint (17.12.0)
[error] 1092-1092: Expected empty line before declaration (declaration-empty-line-before)
(declaration-empty-line-before)
[error] 1099-1099: Expected empty line before declaration (declaration-empty-line-before)
(declaration-empty-line-before)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@dashboard/src/index.css` around lines 1089 - 1101, The Stylelint error is
caused by missing empty lines after custom properties; in both blocks for the
selector "[data-plugin-list-switch='true'][data-state='checked']
[data-dashboard-switch-thumb='true']" and
"[data-plugin-list-switch='true'][data-state='unchecked']
[data-dashboard-switch-thumb='true']", insert a single blank line after the two
custom-property declarations (--tw-translate-x and --tw-translate-y) and before
the subsequent "translate" declaration so the custom-property group is separated
by an empty line and satisfies declaration-empty-line-before.
Sources: Coding guidelines, Linters/SAST tools
| // 构建请求体与请求头:FormData 交给浏览器设置 multipart 边界,其余 JSON 序列化 | ||
| const isFormData = body instanceof FormData | ||
| const requestHeaders: HeadersInit = isFormData | ||
| ? { ...headers } | ||
| : { 'Content-Type': 'application/json', ...headers } | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
rg -nP --type=ts --type=tsx 'headers\s*:\s*new\s+Headers\s*\(' dashboard/src
rg -nP --type=ts --type=tsx 'headers\s*:\s*\[\s*\[' dashboard/srcRepository: Mai-with-u/MaiBot
Length of output: 117
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Inspect the relevant section in the reviewed file
sed -n '90,170p' dashboard/src/lib/http/client.ts | nl -ba | sed -n '1,120p'
# 2) Find usages/call sites that pass a Headers instance or string[][]-like tuple headers
rg -n --hidden --glob='*.ts' --glob='*.tsx' --glob='*.js' --glob='*.jsx' --glob='*.json' \
'new\s+Headers\s*\(' dashboard/src
rg -n --hidden --glob='*.ts' --glob='*.tsx' --glob='*.js' --glob='*.jsx' --glob='*.json' \
'headers\s*:\s*\[\s*\[\s*["'\'']' dashboard/src
# 3) Find the request call sites to see how headers are typed/constructed
rg -n --hidden --glob='*.ts' --glob='*.tsx' --glob='*.js' --glob='*.jsx' \
'client\.(get|post|put|patch|delete)\s*\(|request\s*\(|fetch\s*\(' dashboard/src/lib/http dashboard/srcRepository: Mai-with-u/MaiBot
Length of output: 104
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show the reviewed section with line numbers (avoid `nl`)
echo "---- dashboard/src/lib/http/client.ts (90-170) ----"
sed -n '90,170p' dashboard/src/lib/http/client.ts | cat -n
# Search for direct creation/use of Headers in the codebase
echo "---- new Headers( ... ) occurrences ----"
rg -n --hidden --glob='*.ts' --glob='*.tsx' --glob='*.js' --glob='*.jsx' \
'new\s+Headers\s*\(' dashboard/src || true
# Search for tuple/array-like headers usage
echo "---- string[][] / [ [ ... ], [ ... ] ] header literals ----"
rg -n --hidden --glob='*.ts' --glob='*.tsx' --glob='*.js' --glob='*.jsx' \
'headers\s*:\s*\[\s*\[\s*["'"'"']' dashboard/src || true
# Search where `client` methods (that likely feed headers) are called
echo "---- client.*( or request( call sites in http folder ----"
rg -n --hidden --glob='*.ts' --glob='*.tsx' --glob='*.js' --glob='*.jsx' \
'client\.(get|post|put|patch|delete)\s*\(|request\s*\(' dashboard/src/lib/http || trueRepository: Mai-with-u/MaiBot
Length of output: 6092
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Locate RequestOptions / ApiClientOptions / headers typing
rg -n --hidden --glob='*.ts' --glob='*.tsx' \
'interface RequestOptions|type RequestOptions|RequestOptions\s*=' dashboard/src/lib/http/client.ts dashboard/src/lib/http -S
# 2) Inspect the top of client.ts for type definitions
sed -n '1,120p' dashboard/src/lib/http/client.ts | cat -n
# 3) Search for callers passing `headers` into client.get/post/etc within http folder
rg -n --hidden --glob='*.ts' --glob='*.tsx' \
'client\.(get|post|put|patch|delete)\([^)]*,\s*\{[^}]*headers\s*:' dashboard/src/lib/http -S || true
rg -n --hidden --glob='*.ts' --glob='*.tsx' \
'headers\s*:' dashboard/src/lib/http/__tests__ -S || true
# 4) Broader search for `createApiClient` usage and passing headers
rg -n --hidden --glob='*.ts' --glob='*.tsx' \
'createApiClient\(' dashboard/src -S
rg -n --hidden --glob='*.ts' --glob='*.tsx' \
'\.get\([^)]*,\s*\{[^}]*headers\s*:|\.post\([^)]*,\s*\{[^}]*headers\s*:|\.put\([^)]*,\s*\{[^}]*headers\s*:|\.patch\([^)]*,\s*\{[^}]*headers\s*:|\.delete\([^)]*,\s*\{[^}]*headers\s*:' dashboard/src -S || trueRepository: Mai-with-u/MaiBot
Length of output: 5526
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "---- dashboard/src/lib/http/instances.ts ----"
sed -n '1,120p' dashboard/src/lib/http/instances.ts | cat -n
echo "---- dashboard/src/lib/http/__tests__/client.test.ts (focus headers/body cases) ----"
rg -n --hidden --glob='*.ts' 'headers\s*:' dashboard/src/lib/http/__tests__/client.test.ts dashboard/src/lib/http -S || true
sed -n '1,260p' dashboard/src/lib/http/__tests__/client.test.ts | cat -nRepository: Mai-with-u/MaiBot
Length of output: 11803
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find all call sites that pass `headers:` into client.* options
rg -n --hidden --glob='*.ts' --glob='*.tsx' --glob='*.js' --glob='*.jsx' \
'client\.(get|post|put|patch|delete)\([^)]*\{\s*[^}]*headers\s*:' dashboard/src -S || true
rg -n --hidden --glob='*.ts' --glob='*.tsx' --glob='*.js' --glob='*.jsx' \
'\.(get|post|put|patch|delete)\([^)]*\{\s*[^}]*headers\s*:' dashboard/src -S || true
# Also check for `createApiClient` usage with overrides containing headers (unlikely, but cheap)
rg -n --hidden --glob='*.ts' --glob='*.tsx' --glob='*.js' --glob='*.jsx' \
'createApiClient\([^)]*headers\s*:' dashboard/src -S || true
# Broader scan for `headers:` key usage in http request options
rg -n --hidden --glob='*.ts' --glob='*.tsx' --glob='*.js' --glob='*.jsx' \
'\bheaders\s*:' dashboard/src/lib/http -S || true
# Find any construction of Headers objects (not just `new Headers(` on same line)
rg -n --hidden --glob='*.ts' --glob='*.tsx' --glob='*.js' --glob='*.jsx' \
'new\s+Headers\s*\(' dashboard/src || trueRepository: Mai-with-u/MaiBot
Length of output: 131
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Check if any tests pass `headers` into the client
rg -n --hidden --glob='*.ts' --glob='*.tsx' \
'\bheaders\s*:' dashboard/src/lib/http/__tests__/client.test.ts dashboard/src/lib/http -S || true
# Check if any other codebase call site passes `headers:` into client.* (multiline tolerant)
rg -n --hidden -U --glob='*.ts' --glob='*.tsx' --glob='*.js' --glob='*.jsx' \
'client\.(get|post|put|patch|delete)\([\s\S]*?headers\s*:' dashboard/src -S || true
# Quick inventory of all `headers` keys in the http folder
rg -n --hidden --glob='*.ts' --glob='*.tsx' --glob='*.js' --glob='*.jsx' \
'\bheaders\b' dashboard/src/lib/http -S || trueRepository: Mai-with-u/MaiBot
Length of output: 790
修正请求头构建:对象展开会破坏 HeadersInit 非对象形态 + body 为空时仍注入 Content-Type
createApiClient里用{ ...headers }/{ 'Content-Type': 'application/json', ...headers }处理headers?: HeadersInit:当调用方传入Headers或string[][]时会被对象展开丢失/畸形(仓库内当前未发现使用options.headers的调用点,但实现对类型并不安全)。body未提供(非FormData)时仍会默认注入Content-Type: application/json,可能造成不必要的兼容性/预检开销风险。
🛠️ 建议修复
- const isFormData = body instanceof FormData
- const requestHeaders: HeadersInit = isFormData
- ? { ...headers }
- : { 'Content-Type': 'application/json', ...headers }
+ const isFormData = body instanceof FormData
+ const requestHeaders = new Headers(headers)
+ const serializedBody = isFormData || body === undefined ? body : JSON.stringify(body)
+ if (!isFormData && body !== undefined && !requestHeaders.has("Content-Type")) {
+ requestHeaders.set("Content-Type", "application/json")
+ }
let response: Response
try {
response = await fetch(url, {
method,
headers: requestHeaders,
- body: isFormData ? body : body === undefined ? undefined : JSON.stringify(body),
+ body: serializedBody,
credentials: auth === 'cookie' ? 'include' : undefined,
signal,
cache,
})📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // 构建请求体与请求头:FormData 交给浏览器设置 multipart 边界,其余 JSON 序列化 | |
| const isFormData = body instanceof FormData | |
| const requestHeaders: HeadersInit = isFormData | |
| ? { ...headers } | |
| : { 'Content-Type': 'application/json', ...headers } | |
| // 构建请求体与请求头:FormData 交给浏览器设置 multipart 边界,其余 JSON 序列化 | |
| const isFormData = body instanceof FormData | |
| const requestHeaders = new Headers(headers) | |
| const serializedBody = isFormData || body === undefined ? body : JSON.stringify(body) | |
| if (!isFormData && body !== undefined && !requestHeaders.has("Content-Type")) { | |
| requestHeaders.set("Content-Type", "application/json") | |
| } | |
| let response: Response | |
| try { | |
| response = await fetch(url, { | |
| method, | |
| headers: requestHeaders, | |
| body: serializedBody, | |
| credentials: auth === 'cookie' ? 'include' : undefined, | |
| signal, | |
| cache, | |
| }) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@dashboard/src/lib/http/client.ts` around lines 118 - 123, The request header
construction in createApiClient incorrectly uses object spread on headers
(losing non-object HeadersInit forms) and always injects 'Content-Type:
application/json' even when body is absent; change the logic around
isFormData/body/headers so: detect FormData with isFormData and if true pass
headers through unchanged; if not FormData and body is null/undefined, do not
add Content-Type; otherwise (non-FormData with a body) ensure you build headers
without object-spreading a Headers or string[][] by creating a Headers instance
(e.g. const hdrs = headers instanceof Headers ? headers : new Headers(headers) )
and then set hdrs.set('Content-Type','application/json') only when body exists;
assign requestHeaders to either the original headers (when it's a
Headers/string[][] and no mutation needed) or hdrs for the fetch call, updating
the code that references requestHeaders to use this safe Headers handling.
| // 聊天列表:侧边栏(仅有记录的聊天)与表单(含空聊天)各取一份 | ||
| const chatListQuery = useQuery({ | ||
| queryKey: ['jargon', 'chats'], | ||
| queryFn: async () => { | ||
| const [sidebarResponse, formResponse] = await Promise.all([ | ||
| getJargonChatList(), | ||
| getJargonChatList({ include_empty: true }), | ||
| ]) | ||
| if (sidebarResponse?.data) { | ||
| setChatList(sidebarResponse.data) | ||
| } | ||
| if (formResponse?.data) { | ||
| setFormChatList(formResponse.data) | ||
| return { | ||
| sidebar: sidebarResponse.data, | ||
| form: formResponse.data, | ||
| } | ||
| } catch (error) { | ||
| console.error('加载聊天列表失败:', error) | ||
| } | ||
| } | ||
| }, | ||
| }) | ||
| const chatList: JargonChatInfo[] = chatListQuery.data?.sidebar ?? [] | ||
| const formChatList: JargonChatInfo[] = chatListQuery.data?.form ?? [] | ||
|
|
There was a problem hiding this comment.
不要把聊天列表查询失败静默成空列表。
这里把 chatListQuery.data 直接回退成 [],但页面没有任何 chatListQuery.isError 分支。接口一旦失败,侧边栏会显示成“没有聊天”,创建/编辑对话框也会失去聊天选项,用户无法区分“确实为空”和“加载失败”。至少需要显式错误态/重试,或者把侧边栏数据与表单数据拆成独立失败态。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@dashboard/src/routes/resource/jargon/index.tsx` around lines 113 - 129, The
query result for chat lists is being silently treated as empty by falling back
to [] (chatListQuery.data?.sidebar ?? [] and form ?? []), which hides errors;
change this by surfacing error states instead of defaulting to empty arrays:
either split the combined useQuery into two separate queries calling
getJargonChatList() and getJargonChatList({ include_empty: true }) so each can
show its own isLoading/isError and retry UI, or keep the single chatListQuery
but remove the silent fallbacks and use chatListQuery.isError /
chatListQuery.error to render an explicit error/ retry state (and only map data
when chatListQuery.data is present); update uses of chatList and formChatList to
guard on the query status rather than assuming [].
| if self._trimmed_history_learning_task is not None and not self._trimmed_history_learning_task.done(): | ||
| logger.info(f"{self.log_prefix} 裁切历史学习仍在后台运行,跳过新的学习批次") | ||
| return |
There was a problem hiding this comment.
这里的并发保护会静默丢掉后续裁切批次。
只要上一轮后台学习还没结束,这里就直接 return。当前这批 context_messages 既没有缓存,也不会在 done callback 里补跑,所以高频裁切时会永久漏学一部分历史。建议至少保留 latest pending batch,在当前任务完成后续跑;更稳妥的是改成串行队列。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/maisaka/runtime.py` around lines 1602 - 1604, The current concurrency
guard in the method exits early when self._trimmed_history_learning_task is
running, which silently drops the incoming context_messages; instead implement a
short pending-batch queue: add a field (e.g.
self._pending_trimmed_history_batch) to store the latest context_messages when
self._trimmed_history_learning_task is not done, update the branch that
currently returns to set/replace that pending batch and log that it's queued
(using self.log_prefix and logger.info), and in the existing task's done
callback (where the previous task completion is handled) check
self._pending_trimmed_history_batch and, if present, clear it and schedule a new
trimmed-history learning task with that stored batch; alternatively replace with
a simple serial queue/worker that enqueues context_messages and processes them
one-by-one to guarantee no batches are dropped (ensure updates to
self._trimmed_history_learning_task reflect the newly scheduled task).
| try: | ||
| with pyproject_path.open("rb") as pyproject_file: | ||
| pyproject_data = tomllib.load(pyproject_file) | ||
| except Exception: | ||
| return "" |
There was a problem hiding this comment.
版本探测吞掉全部异常,Host 版本失效时无法及时暴露根因
当前 except Exception: return "" 会把解析/权限等真实故障静默掉,后续握手只看到空版本,排障困难。
🔧 建议修复
- try:
- with pyproject_path.open("rb") as pyproject_file:
- pyproject_data = tomllib.load(pyproject_file)
- except Exception:
- return ""
+ try:
+ with pyproject_path.open("rb") as pyproject_file:
+ pyproject_data = tomllib.load(pyproject_file)
+ except FileNotFoundError:
+ return ""
+ except tomllib.TOMLDecodeError as exc:
+ raise RuntimeError(f"解析 {pyproject_path} 失败") from exc
+ except OSError as exc:
+ raise RuntimeError(f"读取 {pyproject_path} 失败") from excAs per coding guidelines, “Do not always consider fallback; if there is an error, ensure it is exposed completely and timely rather than covering it up with fallback”.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/plugin_runtime/__init__.py` around lines 52 - 56, The code currently
swallows all exceptions around reading/parsing pyproject by using "except
Exception: return ''"; change this so only expected "file missing" cases are
treated as a silent fallback and all other errors are exposed: wrap the
pyproject_path.open/tomllib.load block to catch and return "" only for
FileNotFoundError (and optionally IsADirectoryError), but let PermissionError,
tomllib.TOMLDecodeError and any other exceptions propagate (or re-raise) so
callers can see root causes; reference the pyproject_path open, pyproject_file,
and tomllib.load usages and replace the broad except with targeted exception
handlers that either return "" for missing-file cases or re-raise/log and raise
for all other errors.
Source: Coding guidelines
| from pydantic import BaseModel, ConfigDict, Field, ValidationError, field_validator, model_validator | ||
|
|
||
| from src.common.logger import get_logger | ||
| from src.plugin_runtime import detect_host_application_version |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
sed -n '1,30p' src/plugin_runtime/runner/manifest_validator.py
printf '\n--- tomllib call sites ---\n'
rg -n '\btomllib\b' src/plugin_runtime/runner/manifest_validator.pyRepository: Mai-with-u/MaiBot
Length of output: 1352
🏁 Script executed:
#!/bin/bash
rg -n "^(import tomllib\b|from tomllib import\b)" src/plugin_runtime/runner/manifest_validator.py
rg -n "\btomllib\b" src/plugin_runtime/runner/manifest_validator.pyRepository: Mai-with-u/MaiBot
Length of output: 195
🏁 Script executed:
#!/bin/bash
sed -n '1,120p' src/plugin_runtime/runner/manifest_validator.py
sed -n '1160,1265p' src/plugin_runtime/runner/manifest_validator.py
sed -n '1265,1325p' src/plugin_runtime/runner/manifest_validator.pyRepository: Mai-with-u/MaiBot
Length of output: 8833
修复 tomllib.load 运行时 NameError(缺失 tomllib 导入)
src/plugin_runtime/runner/manifest_validator.py 在 _detect_default_sdk_version(约 1220 行)和 _load_host_dependency_requirements(约 1247 行)调用了 tomllib.load(...),但文件顶部没有 import tomllib(全文件也仅出现这两处调用),会在读取 pyproject.toml 时触发 NameError,导致 Manifest 校验链路中断。请恢复 import tomllib,或把这两处解析逻辑整体迁到新的实现入口。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/plugin_runtime/runner/manifest_validator.py` at line 22, The module is
calling tomllib.load(...) in _detect_default_sdk_version and
_load_host_dependency_requirements but never imports tomllib, causing a
NameError; fix by adding an import tomllib at the top of the file (or move both
parsing calls into a new shared helper that imports tomllib) and ensure both
_detect_default_sdk_version and _load_host_dependency_requirements use that
import/helper so tomllib is available when reading pyproject.toml.
| await _update_progress( | ||
| stage="loading", | ||
| progress=20 + int(index / max(total_mirrors, 1) * 60), | ||
| message=f"镜像源 {mirror['name']} 克隆失败,正在切换下一个源...", | ||
| operation=operation, | ||
| plugin_id=plugin_id, | ||
| error=str(result.get("error") or ""), | ||
| mirror_id=mirror.get("id"), | ||
| mirror_name=mirror.get("name"), | ||
| mirror_index=index, | ||
| total_mirrors=total_mirrors, | ||
| ) |
There was a problem hiding this comment.
避免在进度事件中透传原始 Git 错误文本(可能泄露凭据)
这里把 result.get("error") / last_error 直接发到进度通道。git 的 stderr 常包含完整远程 URL,若使用带 token 的自定义源,会把敏感信息暴露到前端可见链路。建议进度事件仅返回通用失败信息,详细错误只留服务端日志(或先做脱敏再透传)。
As per coding guidelines, "避免硬编码的敏感信息(API Key、密码等)".
🔧 建议修改
@@
await _update_progress(
stage="loading",
progress=20 + int(index / max(total_mirrors, 1) * 60),
message=f"镜像源 {mirror['name']} 克隆失败,正在切换下一个源...",
operation=operation,
plugin_id=plugin_id,
- error=str(result.get("error") or ""),
+ error="镜像克隆失败,请查看服务端日志",
mirror_id=mirror.get("id"),
mirror_name=mirror.get("name"),
mirror_index=index,
total_mirrors=total_mirrors,
)
@@
await _update_progress(
stage="loading",
progress=min(mirror_progress_base + int((attempt + 1) / self.max_retries * 15), 82),
message=(
f"{mirror_name or mirror_type} 克隆失败,"
f"准备重试 {attempt + 2}/{self.max_retries}"
),
operation=operation,
plugin_id=plugin_id,
- error=last_error,
+ error="克隆失败,准备重试;详见服务端日志",
mirror_id=mirror_type,
mirror_name=mirror_name or mirror_type,
mirror_index=mirror_index,
total_mirrors=total_mirrors,
attempt=attempt + 1,
max_attempts=self.max_retries,
)Also applies to: 905-921
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/webui/services/git_mirror_service.py` around lines 730 - 741, The
progress update is leaking raw Git stderr (result.get("error") / last_error) to
the progress channel via the _update_progress call; remove the direct error text
from the message payload and instead send a generic failure message (e.g.,
"克隆失败,请查看服务端日志") and an error code or boolean, while logging the full error
server-side (or sanitizing it) inside the surrounding code path that calls
_update_progress (the same place where result and last_error are available).
Update the _update_progress invocation(s) (e.g., the call that currently passes
error=str(result.get("error") or "")) to omit or replace the error field, and
add a server-side log statement that records the original
result.get("error")/last_error after sanitization; apply the same change to the
other affected call sites noted in the review.
Source: Coding guidelines
zh-CN目标翻译作为常规 GitHub 编辑面;常规翻译以 Crowdin ->l10n_*PR 回流为准,详见docs/i18n.md请填写以下内容
(删除掉中括号内的空格,并替换为小写的x)
main分支 禁止修改,请确认本次提交的分支 不是main分支src/A_memorix,我确认已阅读src/A_memorix/MODIFICATION_POLICY.md,不涉及则无需勾选其他信息
Summary by CodeRabbit
发布说明
新功能
性能优化
Bug 修复
其他