Merge branch 'master' into fix/typecheck-test
Project CI / AI game creator shell Rust crates (pull_request) Successful in 3m4s
Project CI / Backend tests (pull_request) Failing after 4m30s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 4m43s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 5m23s
Project CI / Frontend tests (pull_request) Successful in 3m21s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m17s
Project CI / Repository checks (pull_request) Failing after 4m28s
Project CI / Native shell tests (pull_request) Successful in 7m43s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 3m4s
Project CI / Backend tests (pull_request) Failing after 4m30s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 4m43s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 5m23s
Project CI / Frontend tests (pull_request) Successful in 3m21s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m17s
Project CI / Repository checks (pull_request) Failing after 4m28s
Project CI / Native shell tests (pull_request) Successful in 7m43s
This commit is contained in:
@@ -1,5 +1,21 @@
|
||||
# 决策记录
|
||||
|
||||
## 2026-10-03 AGC 栏目画布上传素材按入口栏目登记(Issue 359)
|
||||
|
||||
- 背景:AGC 客户端在资源栏目子画布(「UI 交互 / 角色与对象 / 场景与环境 / 音频」)左下角工具栏点「上传」后,提示条给出「已上传 1 个素材」,但当前栏目计数不变(仍「0 项」)、素材出现在「待归类」,用户看到的是"上传成功了但它从这一页消失了"。原因是上传登记的 manifest `kind` 只由**内容证据**推导(`assets.rs::uploaded_asset_kind`:图片 / 视频 / 代码 → `unclassified`,音频 → `audio`,文档 / 字体 → `document`),kind 派生分类与栏目词汇(`ui-interaction` / `character` / `scene` / `audio`)不是同一套,而 `upload_local_asset` 原先不接受入口栏目。
|
||||
- 决策:`upload_local_asset` 增加可选 `targetCategory`,Rust 走既有的 `register_local_asset_entry_with_category`(与生成入口 `start_local_project_asset_generation` 的 `targetCategory` **同一口径**:GUI 完成登记以入口栏目为准);前端 `uploadProjectAssetFilesAndReadSnapshot` 透传该字段,栏目画布工具栏上传取工具栏自己的栏目(`resourceCanvasBottomToolbarCategory`)。取值只接受共享分类枚举,非法值由原生失败关闭,前端不做伪分类。
|
||||
- 边界:不传 `targetCategory` 时保持既有 kind 派生行为——资源面板(`ResourceCanvasPanelView`,跨栏目列表而不是栏目工具)、UI 编辑器图片导入(`import_ui_editor_local_files`)、聊天附件上传都不变。`upload_local_asset_at` 签名保持不变(委托到新的 `upload_local_asset_at_with_category`),既有约 25 处调用点与 Rust 单测零改动。
|
||||
- 影响范围:`apps/ai-game-creator-shell/src-tauri/src/{assets.rs,commands/desktop.rs}`、`apps/ai-game-creator-shell/src/view/project-development/{projectResourceLiveUpdateModel.ts,index.tsx}`、`apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts`、PRD §3.10、`docs/technical/【AGC】栏目画布底部工具栏入口矩阵-2026-09-13.md`(§4 载荷表)、`docs/technical/【测试用例】AGC资源工作台V3端到端验收-2026-09-11.md`(S11a)、`pitfalls.md`。
|
||||
- 验证:`cargo test --locked --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --features=cocos-editor-execute,unity-editor-execute,godot-editor-execute --bin genarrative-ai-game-creator-shell assets::tests`(新增 `upload_registers_into_the_explicit_entry_category`:显式栏目 → `character`、不传 → `unclassified`、非法 `version` 失败关闭且不新增登记);`npx vitest run apps/ai-game-creator-shell/tests/appSurface.test.ts`(196 passed / 9 skipped,含新增「uploads toolbar files into the entry column so they stay visible where they were uploaded」断言工具栏上传载荷带 `targetCategory: 'character'`);`npm run agc:typecheck`、`npm run check:encoding`、`git diff --check`。
|
||||
|
||||
## 2026-10-03 生成绑定不再经 prettier:ts-rs 原始输出即提交形态
|
||||
|
||||
- 背景:`scripts/check-generated-bindings.mjs` 对 AGC 的 `chat/generated` / `services/generated` 在重生成后会就地跑一遍 `npx prettier --write` 再比较。这会直接改写生成文件,还把「Rust 声明真的变了」与「prettier 版本 / 配置造成的格式漂移」混在同一条告警里——本次报出的 `ThreadRequestKind.ts` / `TurnCompletedStatus.ts`「内容变化」无法复现为语义变化(已提交内容与当前 Rust 枚举一致),prettier 归一化把格式差异也报成了「与 Rust 声明不一致」;生成物被仓库格式化工具二次改写后,重跑 `cargo test export_bindings` 也不再幂等。
|
||||
- 决策:生成绑定一律以 ts-rs 原始输出提交,不接受 prettier / eslint 等工具二次改写。`check-generated-bindings.mjs` 删掉 prettier 步骤,直接逐字节比较原始输出;`packages/shared/src/contracts/generated`、`chat/generated`、`services/generated`、`features/ui-editor/types` 四个目录统一登记进 `.prettierignore` 与 `.eslintrc.cjs` 的 `ignorePatterns`;`.prettierrc.json` 里为 `contracts/generated` 设的 `printWidth: 1000` / `singleQuote: false` 覆盖随之删除。
|
||||
- 边界:ts-rs 原始输出在多行对象 / 枚举变体行尾带空格,`.gitattributes` 对四个生成目录设 `whitespace=-trailing-space`,CI 的 `git diff --check` 不再误报;手写文件的行尾空白检查不变。生成 `.ts` 仍提交进仓库供前端消费,并标 `linguist-generated=true`。
|
||||
- 影响范围:`scripts/check-generated-bindings.mjs`、`.prettierignore`、`.prettierrc.json`、`.eslintrc.cjs`、`.gitattributes`、`apps/ai-game-creator-shell/src/{view/project-development/chat,services}/generated/**`。
|
||||
- 验证:`cargo test --locked --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml export_bindings`、`npm run check:generated-bindings`(shared-contracts 1 + AGC 104 = 105 个文件)、`npx prettier --check` 四个生成目录(全部跳过)、`npm run check:encoding`、`git diff --check`。
|
||||
|
||||
## 2026-10-03 回合错误的处置分类层与线程条目脱敏边界
|
||||
|
||||
- 背景:`TurnError::terminal_failure -> Option<TurnFailure>` 用一个 `None` 同时表达"没有失败"和"不是失败、要继续跑(返修 / 复核控制流)",调用方看到 `None` 只会理解成前者,`dispatch.rs` 两处只能 `.expect("回合失败必可投影成失败载荷")`;同时 ThreadManager 在搬运线程条目时做字段级脱敏与限长,前端要被截断,而且与失败载荷的脱敏是两套实现。
|
||||
|
||||
@@ -2,13 +2,21 @@
|
||||
|
||||
这里只记录对当前开发仍有用的症状、根因、排查方法和风险边界。同一事实保留一个当前口径;退役对象的专属过程与单轮测试结果由 Git 历史追溯。遇到旧路径或版本时,以现行代码和专题文档为准。
|
||||
|
||||
## 2026-10-03 AGC 栏目画布上传素材落「待归类」:kind 派生分类不等于入口栏目
|
||||
|
||||
- **现象**(Issue 359):在 AGC 资源栏目子画布(如「UI 交互」「角色与对象」)左下角工具栏点「上传」选图片 / 视频 / 代码类文件,提示条给出「已上传 1 个素材」,但当前栏目计数纹丝不动(仍「0 项」),素材出现在「待归类」。用户看到的是"上传成功了,可它就消失在这个页面里"。
|
||||
- **原因**:上传登记的 manifest `kind` 只由**内容证据**推导(`assets.rs::uploaded_asset_kind`:图片 / 视频 / 代码 → `unclassified`,音频 → `audio`,文档 / 字体 → `document`),kind 派生分类与栏目词汇(`ui-interaction` / `character` / `scene` / `audio`)不是同一套;`upload_local_asset` 原先不接受入口栏目,GUI 工具栏上传只能落 kind 派生分类。
|
||||
- **处理(现行口径)**:`upload_local_asset` 增加可选 `targetCategory`,Rust 走既有的 `register_local_asset_entry_with_category`(非法值失败关闭,不回退 kind 派生);栏目画布工具栏上传时取工具栏自己的栏目(`resourceCanvasBottomToolbarCategory`)。生成入口 `start_local_project_asset_generation` 的 `targetCategory` 是同一口径——GUI 完成登记以入口栏目为准。
|
||||
- **判据/取证**:`cargo test --locked ... --bin genarrative-ai-game-creator-shell upload_` 的 `assets::tests::upload_registers_into_the_explicit_entry_category`(显式栏目 → `character`;不传 → `unclassified`;非法 `version` 失败关闭且不新增登记);appSurface「uploads toolbar files into the entry column so they stay visible where they were uploaded」断言工具栏上传载荷带 `targetCategory: 'character'`。
|
||||
- **边界**:资源面板(跨栏目列表)、UI 编辑器图片导入、聊天附件上传都**不带**入口栏目,保持 kind 派生。任何新增的「某个栏目里的上传入口」都必须显式带上当前栏目,否则又会复现本坑。
|
||||
|
||||
## 2026-10-01 用户输错一次密码被当成"客户端出问题了"引导上报
|
||||
|
||||
- **现象**:登录页密码输错(或密码长度不合规)后弹出「发现问题」,报告面板「错误事件(2)」列出 `密码长度需要在 6 到 128 位之间 — auth · 1 次` 与 `手机号或密码错误 — auth · 1 次`,默认全选,与 react-render / 5xx / agent-runtime 终态失败视觉等价。
|
||||
- **原因**:① 登录已下沉 Rust,`login_client_with_password` 等命令失败返回 `Err(String)`,Tauri 以**裸字符串**拒绝 `invoke`,前端拿不到任何类型信息;② `shouldCaptureClientError` 对非 object 值走默认 `return true`,`handleLoginSubmit` 的 catch 把预期业务拒绝报进了错误池。技术方案里"预期 4xx 登录/鉴权失败不进池"的口径早就成立,是错误通道的实现方式违背了它。
|
||||
- **处理(现行口径)**:命令错误一律按具体变体结构化(`Result<_, ClientAuthError>` + ts-rs 导出),认证命令统一经 `invokeClientAuth` 调用:结构化拒绝原样装进已有的 `ClientAuthErrorWrapper`(只有一个 `error` 字段,值是判别联合),UI 在 catch 里按具体变体分流——认得的业务 / 会话变体只给用户反馈,系统变体原样 `throw` 经 `unhandledrejection` 入池,非结构化拒绝原样抛出;删除 `shouldCaptureClientError`。详见 [`【ADR】AGC命令错误结构化与错误报告口径-2026-10-01`](../../adr/【ADR】AGC命令错误结构化与错误报告口径-2026-10-01.md) 与 [`【ADR】AGC认证失败的JS侧载体与抛出时机-2026-10-01`](../../adr/【ADR】AGC认证失败的JS侧载体与抛出时机-2026-10-01.md)。
|
||||
- **判据/取证**:`npx vitest run apps/ai-game-creator-shell/tests/authFailureReporting.test.tsx`——登录返回结构化业务变体时 `report_client_error` 不被调用;系统变体只上报一次(`source` 取全局 `unhandledrejection` handler 的显式入参;载体不再携带 `action`)。Rust 侧 `cargo test --locked --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml auth_error` 钉住变体 `type` 与 400/401/429/5xx/网络映射。
|
||||
- **形状约定**:`ClientAuthError` 顶层只放调用方要分流的类别,可枚举细分收进类型化枚举 `reason` 字段——无字段变体在 TS 里就是 `{ type: 'x' }`;带载荷变体是 `{ type: 'x' } & X`,`X` 由 ts-rs 导出到 `src/services/generated/X.ts`(Rust 侧是 newtype 变体持有同名结构体),细分原因枚举(`ServerAddressReason` / `AuthNetworkReason` / `AuthResponseInvalidReason`)同样由 ts-rs 生成。**不要手写这些类型**,也不要在前端再加一层分类 / 提示文案派生函数——判别一律写在 catch 子句里:`const failure = error.error; switch (failure.type)`,无字段 `case` 用本 catch 的固定文案,带载荷 `case` 先 `as X` 再读自己的字段,`reason` 是枚举时再 `switch (payload.reason)`,两处 `default` 都用 `expectNever` 保证漏接编译失败。改形状只能改 Rust 再跑 `cargo test` 重新导出,生成物随后交给 prettier;ts-rs 只写文件、不删文件,变体从有载荷改成无字段时要手动清掉孤立的 `X.ts`(本次 `AuthResponseServerRejected.ts` 就是这样删的)。
|
||||
- **形状约定**:`ClientAuthError` 顶层只放调用方要分流的类别,可枚举细分收进类型化枚举 `reason` 字段——无字段变体在 TS 里就是 `{ type: 'x' }`;带载荷变体是 `{ type: 'x' } & X`,`X` 由 ts-rs 导出到 `src/services/generated/X.ts`(Rust 侧是 newtype 变体持有同名结构体),细分原因枚举(`ServerAddressReason` / `AuthNetworkReason` / `AuthResponseInvalidReason`)同样由 ts-rs 生成。**不要手写这些类型**,也不要在前端再加一层分类 / 提示文案派生函数——判别一律写在 catch 子句里:`const failure = error.error; switch (failure.type)`,无字段 `case` 用本 catch 的固定文案,带载荷 `case` 先 `as X` 再读自己的字段,`reason` 是枚举时再 `switch (payload.reason)`,两处 `default` 都用 `expectNever` 保证漏接编译失败。改形状只能改 Rust 再跑 `cargo test` 重新导出,生成物保持 ts-rs 原始输出(不再经 prettier / eslint 二次改写,见本节「生成绑定」口径);ts-rs 只写文件、不删文件,变体从有载荷改成无字段时要手动清掉孤立的 `X.ts`(本次 `AuthResponseServerRejected.ts` 就是这样删的)。
|
||||
- **Rust 侧不得把结构化错误降级成字符串**:`refresh_session_inner` 的非权威失败直接返回 `Err(ClientAuthError)`,视图不带 `errorMessage`;一旦折成 `String`,前端就只能拿文案判断,变体信息永久丢失。
|
||||
- **关联**:`apps/ai-game-creator-shell/src-tauri/src/auth_session.rs`、`apps/ai-game-creator-shell/src/services/{clientAuth.ts,errorReporting.ts,platformSession.ts}`、`apps/ai-game-creator-shell/src/app/AuthenticatedClient.tsx`。
|
||||
|
||||
@@ -5795,8 +5803,8 @@ Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/`
|
||||
- **处理(现行口径)**:不要把重写结果当改动提交。跑过 `cargo test` 或构建后只恢复 `apps/ai-game-creator-shell/src/view/project-development/chat/generated/DirectCodexUser*.ts` 的仓库版本,再删掉多出来的 `DirectCodexUserMessageEnvelope.ts`;不要恢复整个 `generated/` 目录,以免误删 DirectThread 的现役绑定。绑定与前端形状冲突时以**已提交的 DirectCodexUser 绑定 + 前端**为基准排查。
|
||||
- **验证**:恢复仓库版本后 `npm run ai-game-creator-shell:typecheck` exit 0(`[skill-pack] OK`);保留重写结果时同一条命令 exit 2。release 构建本身还会在 `src/features/ui-editor/types/` 落下 `BindingChange.ts` / `BindingDTO.ts` 两个无人引用的生成产物;它们不属于前端契约,发现后直接删除,不提交。
|
||||
- **关联**:`apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_user_item/`(ts-rs 导出源)、`apps/ai-game-creator-shell/src/features/project-workspace/resourceReferences.ts`、`apps/ai-game-creator-shell/scripts/build-release.mjs`(`beforeBuildCommand`)。
|
||||
- **2026-09-24 补充(纯格式 churn 的一档)**:直接运行 AGC 的测试二进制(含全量套件,里面带 `export_bindings`)同样会把 `src/view/project-development/chat/generated/DirectThread*.ts` 重写成 raw ts-rs 形态——双引号、行尾空格、`};` 变 `, };`——于是 `git diff --check` 报一片 trailing whitespace,而 `npm run check:generated-bindings`(只覆盖 `packages/shared/src/contracts/generated`)不会报警。处理:对这组文件跑一次 `npx prettier --write "apps/ai-game-creator-shell/src/view/project-development/chat/generated/*.ts"` 做归一化,再确认 `git diff --stat` 为空——为空就说明这一档只是格式 churn,没有语义变化;不要为它盲目 `git checkout` 整个目录。该目录目前没有自动漂移门禁,靠「跑完测试后归一化 + diff 检查」把关。
|
||||
- **2026-10-02 补充(过滤跑也躲不开)**:`cargo test` 的位置参数是**子串匹配**,`-- agent::thread_manager::` 这类"只跑几个模块"的过滤会连带命中 `agent::thread_manager::wire::failure::export_bindings_turnfailure`,于是照样重写 `chat/generated/` 与 `src/services/generated/`(后者是 auth 绑定)。要按模块跑又不碰生成目录,用 `cargo test --bin genarrative-ai-game-creator-shell -- agent:: --skip export_bindings`;已经被重写时,`chat/generated/` 跑一次 prettier 归一化,`src/services/generated/` 直接 `git checkout --`(那一侧没有本轮的语义改动)。
|
||||
- **2026-09-24 补充(纯格式 churn 的一档)**:直接运行 AGC 的测试二进制(含全量套件,里面带 `export_bindings`)同样会把 `src/view/project-development/chat/generated/DirectThread*.ts` 重写成 raw ts-rs 形态——双引号、行尾空格、`};` 变 `, };`——于是 `git diff --check` 报一片 trailing whitespace,而 `npm run check:generated-bindings`(只覆盖 `packages/shared/src/contracts/generated`)不会报警。处理(2026-10-03 更新,口径已变):不再跑 prettier 归一化——`chat/generated` 与 `services/generated` 的提交形态就是 ts-rs 原始输出;两个目录已进 `.prettierignore` + eslint `ignorePatterns`,`git diff --check` 由 `.gitattributes` 的 `whitespace=-trailing-space` 豁免,`npm run check:generated-bindings` 会逐字节比较原始输出。跑完 `cargo test` 后 `git diff` 为空即为幂等。
|
||||
- **2026-10-02 补充(过滤跑也躲不开)**:`cargo test` 的位置参数是**子串匹配**,`-- agent::thread_manager::` 这类"只跑几个模块"的过滤会连带命中 `agent::thread_manager::wire::failure::export_bindings_turnfailure`,于是照样重写 `chat/generated/` 与 `src/services/generated/`(后者是 auth 绑定)。要按模块跑又不碰生成目录,用 `cargo test --bin genarrative-ai-game-creator-shell -- agent:: --skip export_bindings`;已经被重写时不要再做 prettier 归一化:两个目录的提交形态就是原始输出,按需重新生成或 `git checkout --`,再用 `npm run check:generated-bindings` 确认无漂移。
|
||||
|
||||
## 2026-09-16 Node 26 下 vitest 的 jsdom 用例拿不到 window.localStorage
|
||||
|
||||
@@ -6280,7 +6288,8 @@ Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/`
|
||||
- **根因 3(工具被拒)**:sidecar 用 `permissionMode: 'dontAsk'` 且没有 `allowedTools`,宿主 MCP 工具(`mcp__agc__*`)一律被直接拒绝,模型只能回"没有权限"。
|
||||
- **根因 4(界面看不到回复)**:聊天区是按 `item.completed` 事件流投影的(codex 路径在 `rawResponseItem/completed` 时下发 `ThreadItem::Message`),只把回复落进 `project.jsonl` 不会让本轮出现在界面上——用户看到"用户气泡 + 本轮结束于 … · 耗时",回复只在重进项目时从历史读出来。
|
||||
- **根因 5(验收反馈复用 assistant ID)**:同一 client turn 进入 `ReviewRequired` 后会再次调用 cc。首次回复已经占用 `direct-codex:<clientTurnId>:assistant`,第二次不同正文沿用该 ID 会被历史层正确拒绝为冲突,随后却被错误投影成 `runtime-unclassified`。真实诊断中可见「写入本项目对话历史失败:…assistant」且历史已经有该条回复。
|
||||
- **现行口径**:cc 成功出口由放行侧补写 `DirectTurnTerminal::completed()`(`finish_if_unfinished` 幂等,codex 已写过终态时是空操作);cc 每次解析成功后都把实际落盘的回复 item id 同步下发 `ThreadEvent::item_completed(ThreadItem::Message{role:"assistant"})`。首个回复沿用 `direct-codex:<clientTurnId>:assistant`,同一回合的反馈回复遇到内容冲突时追加 `:assistant:<uuid>`,相同内容仍按原 ID 幂等;落盘失败按回合失败收口。sidecar 按 `mcp__<server>` 前缀整体放行请求里声明的 MCP 服务器(权限策略在宿主侧执行)。
|
||||
- **根因 6(cc 字符串错误覆盖了上游分类)**:DirectProject 的 Claude Code 路由原本把 sidecar 返回的所有字符串都包装成 `LlmError::Transport`,因此 HTTP 429、401、408、5xx、sidecar 超时、空回执和无效 JSON 都显示成「执行通道未能建立或已断开」。
|
||||
- **现行口径**:cc 成功出口由放行侧补写 `DirectTurnTerminal::completed()`(`finish_if_unfinished` 幂等,codex 已写过终态时是空操作);cc 每次解析成功后都把实际落盘的回复 item id 同步下发 `ThreadEvent::item_completed(ThreadItem::Message{role:"assistant"})`。首个回复沿用 `direct-codex:<clientTurnId>:assistant`,同一回合的反馈回复遇到内容冲突时追加 `:assistant:<uuid>`,相同内容仍按原 ID 幂等;落盘失败按回合失败收口。Claude Code 的失败文本先投影到与 Codex 相同的 `LlmError` 分类:HTTP 状态、sidecar 超时、空回执和无效 JSON 分别复用上游、超时、空响应和反序列化语义;上游状态摘要与重试建议按状态码给出。sidecar 按 `mcp__<server>` 前缀整体放行请求里声明的 MCP 服务器(权限策略在宿主侧执行)。
|
||||
- **诊断口径**:`agent.direct_turn.host_dropped` / `agent.direct_turn.panic` 里的令牌字段必须写 `tt=`,写 `turnToken=` 会命中脱敏标记,整行变成 `<sensitive diagnostic details redacted>`,离线只剩"说不出原因"的 HostDropped。
|
||||
- **验证**:dev 栈里用 CDP 注入真实回合(`node %TEMP%\agc-cdp.mjs <expr>`):①读文件轮 `claude-parse-done chars=108`,`.agent/conversations/project.jsonl` 出现 `direct-codex:cdp-…:assistant` 条目,回复内容与 `game/index.html` 前两行(`<!doctype html>` / `<html lang="zh-CN">`)逐字一致(证明宿主工具真的执行了);②聊天视图打开时注入 `只回三个字:收到了`,DOM 断言(`document.body.innerText`)同时出现用户气泡 `11:40:05`、助手回复 `收到了` 与 `本轮结束于 11:40:16 · 耗时 10.7秒`(证明 `item.completed` 实时投影生效,不必重进项目);同一日志不再出现新的 `host_dropped`。另有 `agent::claude_code_cli::tests::direct_claude_feedback_reply_does_not_fail_on_a_reused_client_turn_id` 回归覆盖同一回合两次不同回复。`cargo test … -- claude_code_cli::tests direct_turn_failure::tests` 19 passed。
|
||||
- **关联**:`apps/ai-game-creator-shell/src-tauri/src/agent/thread_manager/dispatch.rs`、`apps/ai-game-creator-shell/src-tauri/src/agent/direct_turn_failure.rs`、`apps/ai-game-creator-shell/src-tauri/src/agent/claude_code_cli.rs`、`apps/ai-game-creator-shell/agent-sidecar/src/index.mjs`。
|
||||
|
||||
Reference in New Issue
Block a user