@microi.net/cli 5.6.2 → 5.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/.codebuddy-plugin/marketplace.json +2 -2
  2. package/.codebuddy-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +2 -2
  4. package/.workbuddy-plugin/marketplace.json +2 -2
  5. package/.workbuddy-plugin/plugin.json +1 -1
  6. package/README.md +2 -2
  7. package/assets/build-meta.json +7 -6
  8. package/assets/feature-matrix.json +138 -129
  9. package/cordis.patch.yml +2 -2
  10. package/package.json +1 -1
  11. package/scripts/mcp-server.js +82 -80
  12. package/scripts/microi-cli.js +59 -58
  13. package/scripts/microi-skills.meta.json +225 -222
  14. package/skills/.microi-skills-version.json +3 -3
  15. package/skills/.progressive-disclosure-manifest.json +58 -58
  16. package/skills/app-store/SKILL.md +6 -0
  17. package/skills/email-engine/SKILL.md +3 -2
  18. package/skills/job-engine/SKILL.md +10 -3
  19. package/skills/microi-client-frontend/SKILL.md +5 -2
  20. package/skills/microi-codex/SKILL.md +59 -48
  21. package/skills/microi-codex-installer/SKILL.md +12 -3
  22. package/skills/microi-deployment/SKILL.md +2 -0
  23. package/skills/microi-form-engine/SKILL.md +18 -0
  24. package/skills/microi-form-engine/references/component-catalog.md +1 -1
  25. package/skills/page-engine/SKILL.md +11 -1
  26. package/skills/page-engine/examples/compact-dashboard.json +1444 -0
  27. package/skills/playwright-e2e/SKILL.md +8 -8
  28. package/skills/playwright-e2e/references/progressive-01-/345/205/250/350/207/252/345/212/250/347/231/273/345/275/225-/345/205/215/351/252/214/350/257/201/347/240/201-/344/275/206/344/270/215/345/205/215/345/257/206/347/240/201-/345/277/205/350/257/273.md +3 -3
  29. package/skills/playwright-e2e/references/progressive-02-/346/226/207/345/255/227/345/257/271/346/257/224/345/272/246/344/270/216/345/217/257/350/257/273/346/200/247/350/207/252/345/212/250/345/214/226/346/243/200/346/237/245-/345/277/205/345/201/232.md +2 -2
  30. package/skills/playwright-e2e/references/progressive-03-microi-helper-/346/250/241/346/235/277.md +3 -3
  31. package/skills/playwright-e2e/references/progressive-04-ci-/345/273/272/350/256/256.md +3 -3
  32. package/skills/system-observability/SKILL.md +62 -8
  33. package/skills/v8-explorer-tree/SKILL.md +228 -226
  34. package/skills/v8-file-upload/SKILL.md +26 -1
  35. package/skills/v8-file-upload/references/progressive-01-/345/205/254/346/234/211/346/241/266-vs-/347/247/201/346/234/211/346/241/266.md +1 -1
  36. package/skills/v8-file-upload/references/progressive-02-office-/346/226/207/344/273/266/345/234/250/347/272/277/347/274/226/350/276/221/347/211/210/346/234/254/345/217/267/350/247/204/345/210/231.md +3 -3
  37. package/skills/v8-http-integration/SKILL.md +3 -1
  38. package/skills/v8-security/SKILL.md +3 -1
  39. package/skills/v8-sql-query/SKILL.md +141 -133
  40. package/skills/v8-tcp-integration/SKILL.md +2 -2
  41. package/skills/v8-workflow/SKILL.md +12 -4
  42. package/skills/workspace-conventions/SKILL.md +8 -6
  43. package/skills/workspace-conventions/references/progressive-01-/347/211/210/346/234/254/346/233/264/346/226/260/346/227/245/345/277/227/344/277/235/346/212/244/350/247/204/345/210/231-/345/274/272/345/210/266.md +6 -6
  44. package/skills/workspace-conventions/references/progressive-02-microi-net-api-/346/234/254/345/234/260/345/220/257/345/212/250/347/272/246/345/256/232.md +21 -21
  45. package/skills/workspace-conventions/references/progressive-03-cli-/344/270/216-ide-/346/217/222/344/273/266/351/224/231/347/211/210/345/205/261/345/255/230/347/272/246/345/256/232.md +1 -1
@@ -1,226 +1,228 @@
1
- ---
2
- name: v8-explorer-tree
3
- description: Microi.VSCode 插件 V8 资源管理器目录规范(表单引擎 / 字段V8事件 / 模块引擎按钮)。覆盖 diy_table V8 事件、diy_field V8 事件、sys_menu 按钮 JSON 拆分回写,含 7 条安全不变量。用于实现 VSCode V8 事件树、重构目录布局或处理 sys_menu 按钮 JSON 序列化。
4
- ---
5
-
6
- > **Codex 非阻塞自动更新:** 当前宿主为 Codex 时,吾码 CLI、Codex 插件与工作区 AI/MCP 由后台自动更新;需要诊断时读取 `../microi-codex-installer/SKILL.md`。更新失败、等待空闲或尚未重载均不得阻断当前、正在进行或新建任务。非 Codex 宿主跳过此项。
7
-
8
- # v8-explorer-tree — V8 资源管理器目录规范 v2(2026-05)
9
-
10
- > 涉及文件:[Microi.VSCode/src/explorer/engineTreeProvider.ts](Microi.VSCode/src/explorer/engineTreeProvider.ts)、[Microi.VSCode/src/sync/syncManager.ts](Microi.VSCode/src/sync/syncManager.ts)、[Microi.VSCode/src/constants.ts](Microi.VSCode/src/constants.ts)、[Microi.VSCode/src/utils.ts](Microi.VSCode/src/utils.ts)、[Microi.Server/Microi.net.Api/Controllers/V8EngineController.cs](Microi.Server/Microi.net.Api/Controllers/V8EngineController.cs)
11
-
12
- ---
13
-
14
- ## 0. 命名规范(强制)
15
-
16
- **所有目录与文件**统一使用 **中文(英文)** 形式,使用**全角括号 `()`**:
17
-
18
- | 元素 | 格式 | 示例 |
19
- |------|------|------|
20
- | 表 | `<表Label>(<表Name>)` | `仓库档案(diy_cangkuda)` |
21
- | 字段 | `<字段Label>(<字段Name>)` | `仓库名称(CangKuName)` |
22
- | 菜单 | `<菜单Name中文>(<菜单英文Name>)` | `订单管理(OrderManage)` |
23
- | 事件 | `<事件中文名>(<EventType>)` | `前端表单进入V8事件(InFormV8).js` |
24
- | 字段事件 | `<事件中文名>(<英文Key>)` | `值变更V8事件(V8Code).js` |
25
- | 按钮 | `<按钮Name>(<按钮Id>)` | `指派(01K3X...).js` |
26
- | 按钮显隐 | `<按钮Name>_显隐判断(<按钮Id>)` | `指派_显隐判断(01K3X...).js` |
27
- | 按钮分类目录 | `<按钮类型中文>(<EnglishField>)` | `[行]更多按钮(MoreBtns)/` |
28
-
29
- ### 0.1 7 类表单 V8 事件中文名(以 `diy_table` 对应 `diy_field.Label` 为准)
30
-
31
- | EventType | 中文名 |
32
- |-----------|--------|
33
- | InFormV8 | 前端表单进入V8事件 |
34
- | SubmitFormV8 | 前端表单提交前V8事件 |
35
- | SubmitBeforeServerV8 | 后端表单提交前V8事件 |
36
- | SubmitAfterServerV8 | 后端表单提交后V8事件 |
37
- | ServerDataV8 | 后端数据处理V8事件 |
38
- | OutFormV8 | 前端表单提交后V8事件 |
39
- | DataFilterV8 | 后端数据过滤V8事件 |
40
-
41
- > 统一用“后端”,不要再生成“服务端”或“服务器端”可见目录/文件名。
42
-
43
- ### 0.2 4 类字段 V8 事件中文名
44
-
45
- | Key | 中文名 |
46
- |-----|--------|
47
- | V8Code | 值变更V8事件 |
48
- | KeyupV8Code | 键盘抬起V8事件 |
49
- | V8TmpEngineTable | 表格列模板V8事件 |
50
- | V8TmpEngineForm | 表单字段模板V8事件 |
51
-
52
- ### 0.3 6 类按钮中文名
53
-
54
- | Field | 中文名 |
55
- |-------|--------|
56
- | PageTabs | 页面多Tab |
57
- | PageBtns | [页面]更多按钮 |
58
- | MoreBtns | [行]更多按钮 |
59
- | FormBtns | [表单]更多按钮 |
60
- | BatchSelectMoreBtns | [批量选择]更多按钮 |
61
- | ExportMoreBtns | [导出]更多按钮 |
62
-
63
- ---
64
-
65
- ## 1. 当前结构(v1 基线)
66
-
67
- ```
68
- <workspace>/microi-v8-engine/
69
- ├── 接口引擎/
70
- │ └── <ApiEngineName>(<ApiEngineKey>).js // 半角括号,保留不变
71
- └── V8事件/
72
- └── <表Description(表Name)>/
73
- └── <EventType>.js // 扁平,无中文事件名
74
- ```
75
-
76
- ## 2. 目标结构(v2)
77
-
78
- ```
79
- <workspace>/microi-v8-engine/<server>/<osClient>/
80
- ├── 接口引擎/ // 保持不变
81
- │ └── <分类>/<ApiEngineName>(<ApiEngineKey>).js
82
- │
83
- ├── 表单引擎/
84
- │ └── <表Label>(<表Name>)/
85
- │ ├── 表单V8事件/
86
- │ │ ├── 前端表单进入V8事件(InFormV8).js // 仅远端非空代码生成文件
87
- │ │ ├── 前端表单提交前V8事件(SubmitFormV8).js
88
- │ │ ├── 后端表单提交前V8事件(SubmitBeforeServerV8).js
89
- │ │ ├── 后端表单提交后V8事件(SubmitAfterServerV8).js
90
- │ │ ├── 后端数据处理V8事件(ServerDataV8).js
91
- │ │ ├── 前端表单提交后V8事件(OutFormV8).js
92
- │ │ └── 后端数据过滤V8事件(DataFilterV8).js
93
- │ └── 字段V8事件/
94
- │ └── <字段Label>(<字段Name>)/
95
- │ ├── 值变更V8事件(V8Code).js // 右键单表拉取后生成,仅远端非空代码生成文件
96
- │ ├── 键盘V8事件(KeyupV8Code).js
97
- │ ├── 模板V8引擎(表格)(V8TmpEngineTable).js
98
- │ └── 模板V8引擎(表单)(V8TmpEngineForm).js
99
- │
100
- └── 模块引擎/
101
- └── <父菜单Name>(<ParentMenuId>)/ // 非叶子模块只显示子模块,不生成按钮目录
102
- └── <叶子菜单Name>(<LeafMenuId>)/
103
- ├── Join关联(SqlJoin).js // 始终生成,直接映射 sys_menu.SqlJoin
104
- ├── Where条件(SqlWhere).js // 始终生成,直接映射 sys_menu.SqlWhere
105
- ├── 页面多Tab(PageTabs)/ // 叶子模块固定生成全部 6 个按钮目录,即使为空
106
- │ ├── <按钮Name>(<按钮Id>).js // V8Code,仅远端非空代码生成文件
107
- │ └── <按钮Name>_显隐判断(<按钮Id>).js // V8CodeShow,仅在非空时生成
108
- ├── [页面]更多按钮(PageBtns)/
109
- ├── [行]更多按钮(MoreBtns)/
110
- ├── [表单]更多按钮(FormBtns)/
111
- ├── [批量选择]更多按钮(BatchSelectMoreBtns)/
112
- └── [导出]更多按钮(ExportMoreBtns)/
113
- ```
114
-
115
- ### 2.1 ⚠️ 空代码不落文件规则(关键约束)
116
-
117
- > **前端 V8 引擎**只要事件 / 按钮代码字符串非空(哪怕只有注释、空格),就会调用 Jint 解析并执行——产生不必要的 CPU 开销。
118
- >
119
- > 因此:拉取远端资源时,空 V8 代码**不要生成本地 `.js` 文件**。若历史同步留下空 `.js` 文件,且远端代码同样为空,应删除本地空文件,避免被“查看同步状态”误判为本地未推送。
120
- >
121
- > 用户需要清空远端代码时,可以把已有非空文件清空并推送;推送成功后插件应删除该本地空文件或对齐为“无本地文件”状态。
122
- >
123
- > 例外:`SqlJoin`、`SqlWhere` 是模块查询配置,不是按钮 V8 事件。叶子模块必须始终生成两个平级文件,空文件表示远端字段为空,并允许通过推送空文件清空对应字段。
124
-
125
- ---
126
-
127
- ## 3. 数据流
128
-
129
- ### 3.1 表单 V8 事件(✅ 已实现 2026-05)
130
-
131
- - **拉取**:复用 `GetV8EventList`。对每个返回项:
132
- 1. 每张表固定生成表单 V8 事件目录
133
- 2. 仅远端非空代码生成 `.js`;空代码不生成文件,历史空文件应删除
134
- - **保存**:`parseEventFilePath` 解析新旧两种结构,调 `UpdateV8EventCode`
135
- - **删除**:用户删除本地文件默认只删除本地文件,不自动清空远端;清空远端必须通过清空已有文件内容并执行推送,或使用明确的“清空远端代码”命令
136
-
137
- ### 3.2 字段 V8 事件(✅ 已实现 2026-05)
138
-
139
- **接口**:
140
- - `GET /api/V8Debug/GetFieldList?osClient&tableId` — 返回字段列表带 `V8Code/KeyupV8Code/V8TmpEngineTable/V8TmpEngineForm`
141
- - `POST /api/V8Debug/UpdateField` — Body 含 `Id` 或 `TableName + Name`,仅 V8 字段更新时直接写 `diy_field`,不触发表字段物理列变更
142
-
143
- **实施清单**:
144
- 1. 在 `engineTreeProvider.ts` 的表节点加 inline action「拉取此表字段V8事件」
145
- 2. 调 `GetFieldList` 拿字段,对每个字段固定生成 4 个文件:`字段V8事件/<Label>(<Name>)/<事件中文>(<Key>).js`
146
- 3. 保存时按文件路径解析 `(TableId, FieldName, V8Key)` → 调 `UpdateField`
147
-
148
- ### 3.3 sys_menu 按钮 JSON 拆分(✅ 已实现 2026-05)
149
-
150
- **接口**:
151
- - `GET /api/V8Debug/GetModule?osClient&moduleId` — 返回菜单详情,包含 6 类按钮 JSON 以及 `SqlJoin/SqlWhere`
152
- - `POST /api/V8Debug/UpdateModule` — Body 含 `OsClient/ModuleId` 以及目标按钮字段或 `SqlJoin/SqlWhere`
153
-
154
- **拉取**:
155
- 1. 解析 `PageTabs/PageBtns/MoreBtns/FormBtns/BatchSelectMoreBtns/ExportMoreBtns` 6 个 JSON 数组(非法 JSON 记录错误并跳过该字段)
156
- 2. 按 `ParentId` 生成与 `sys_menu/GetSysMenuStep` 一致的模块树;非叶子模块只生成子模块目录,不生成按钮目录
157
- 3. 叶子模块固定生成全部 6 个按钮分类目录,并直接生成 `Join关联(SqlJoin).js`、`Where条件(SqlWhere).js`;两个配置文件即使远端为空也必须生成
158
- 4. 对每个按钮:
159
- - 仅在 `V8Code` 非空时生成 `<按钮Name>(<按钮Id>).js`
160
- - 若 `V8CodeShow` 非空 → 生成 `<按钮Name>_显隐判断(<按钮Id>).js`
161
- - 在空按钮目录中新建 `<按钮Name>(<按钮Id>).js` 并推送时,可按文件名创建远端新按钮
162
- 5. `.microi-meta.json` 写入按钮 JSON hash;回写前写 `.backups/<moduleId>/<ButtonField>-<timestamp>.json`
163
-
164
- **保存(事务,必须满足 §4 全部不变量)**:
165
- 1. 从文件路径解析 `ModuleId / ButtonField / ButtonId / V8Code 或 V8CodeShow`
166
- 2. 二次调 `GetModule` 拉远端当前 JSON
167
- 3. JSON 解析失败立即中止
168
- 4. **Id-Diff 合并**:只修改目标按钮的 `V8Code` 或 `V8CodeShow`,远端其它按钮全部原样保留
169
- 5. 写一份 `.backups/<moduleId>/<ButtonField>-<timestamp>.json`(变更前的远端 JSON)
170
- 6. 调 `UpdateModule` 整段写回;任何一步失败不写库
171
-
172
- **SqlJoin/SqlWhere 保存**:从文件路径解析 `ModuleId / SqlJoin|SqlWhere`,推送前重新 `GetModule`,备份远端原字段,`UpdateModule` 只提交目标字段;成功后必须再次 `GetModule` 回读正文完全一致,才更新 meta/mtime。空正文正常保留为空文件。
173
-
174
- **修改状态**:接口引擎、表单V8事件、字段V8事件、模块按钮、模块 SqlJoin/SqlWhere 文件都必须写入 `.microi-meta.json` 的 `updateTime/filePath`;本地 `mtime > updateTime + 1s` 时树节点显示 `已修改`,推送成功后同步更新 meta 并将文件 mtime 设置为远端更新时间以清除标记。
175
-
176
- **Controller OsClient 规则**:`CheckPermission()` 返回的 `token` 是 `dynamic`。调用 `V8McpLogic.ResolveOsClient` 时必须传 `(object)token`,并让 `ResolveOsClient(string osClient, object currentToken)` 返回真正的 `string`;Controller 中 OsClient 空判断用 `string.IsNullOrWhiteSpace(osClient)`,不要对 `osClient` 调 `DosIsNullOrWhiteSpace()`。
177
-
178
- ---
179
-
180
- ## 4. 风险与不变量(实施 Stage 2/3 前必读)
181
-
182
- | # | 不变量 | 违反后果 | 检测手段 |
183
- |---|--------|---------|---------|
184
- | I1 | 按钮 `Id` 必须保留且唯一 | 远端按钮丢失/重复 | 文件名中 `(<Id>)` 段 + 头部 `@ButtonId` 双校验,二者必须一致 |
185
- | I2 | 远端按钮在本地缺失时**保留**而非删除 | 用户只拉了一类按钮就保存 → 其余被清空 | 保存前必须 `GetModule` 重新拉取做 Id-Diff |
186
- | I3 | 远端整段 JSON 哈希在保存前必须匹配 | 多人/多端冲突覆盖 | `.microi-menu.json.RemoteHash` 检查 |
187
- | I4 | 任何 JSON 解析失败必须中止保存 | JSON 损坏 | try/catch + 弹窗 |
188
- | I5 | 按钮元数据头被用户改坏(如 `@ButtonId` 被删) | 写回失败 | 元数据头丢失 → 该文件标记 dirty,提示从远端覆盖 |
189
- | I6 | 每次回写前自动写 `.backups/<timestamp>.json` | 误操作可恢复 | 强制执行,无配置开关 |
190
- | I7 | 用户重命名/删除按钮文件 | 误操作 | 重命名按 Id 匹配;删除需弹窗输入按钮名二次确认 |
191
-
192
- ---
193
-
194
- ## 5. 实施状态(2026-05-22)
195
-
196
- - ✅ **Stage 1 表单V8事件分层 + 中文命名 + 空代码跳过文件生成** —— 已实现
197
- - ✅ **Stage 2 字段V8事件** —— 已实现:右键单表拉取字段 V8 事件;空代码不生成文件;推送仅更新目标字段的目标 V8 字段
198
- - ✅ **Stage 3 sys_menu 按钮拆分** —— 已实现:主拉取同步模块引擎;按钮 V8Code 仅在非空时生成 `.js`;推送前重拉远端 JSON、按 ButtonId 只改目标按钮并保留其它按钮,写入前自动备份
199
- - ✅ **Stage 4 树形模块引擎 + 全类型修改状态** —— 已实现:模块按 ParentId 树形拉取;非叶子只显示子模块;叶子固定生成 6 个按钮目录;字段/按钮文件也显示并清除 `已修改`
200
- - ✅ **Stage 5 sys_menu 查询脚本本地化** —— 已实现:叶子模块固定生成 `SqlJoin/SqlWhere` 两个平级 `.js` 文件,支持备份、预检、推送、远端回读和修改状态
201
-
202
- ---
203
-
204
- ## 6. 实施约束总结
205
-
206
- - **不要**在没看过 §4 全部 7 条不变量就动手实现 Stage 3
207
- - **不要**为远端空 V8 事件/按钮代码生成 `.js` 文件;`SqlJoin/SqlWhere` 属于查询配置,必须保留空文件
208
- - **不要**用半角括号;统一用全角 `()`
209
- - **不要**清空 V8事件/ 旧目录;保留兼容直到用户主动删除
210
- - 文件名中 `<英文Key>` 段必须 ASCII,避免 Windows 不可见字符
211
- - 远端 Key 若含 `\`、`/`、括号或 Windows 非法字符,文件名中的 Key 段必须使用可逆百分号编码;解析和推送时必须解码回原始 Key,禁止用 `_` 不可逆替换后误更新另一条远端记录。文件名还要处理控制字符、结尾空格/点和 `CON/PRN/AUX/NUL/COM1/LPT1` 等 Windows 保留名。
212
- - “拉取此服务器代码”必须按接口引擎、表单引擎、模块引擎、流程引擎、数据库结构分阶段执行。某一类型失败时要记录明细并继续后续类型;同一类型内单个文件写入失败也要继续下一项,禁止首个 `ENOENT` 让整个服务器只留下半套源码。
213
-
214
- ## 7. AI 远端修改后的同步约束
215
-
216
- AI 使用 MCP、接口引擎、数据库脚本或平台 API 修改远端 V8 代码后,必须在任务收尾时同步本地 V8 资源树,并用“查看同步状态”或等价 dry-run 复核。
217
-
218
- - 远端写入后以远端当前生效代码为准,回写本地文件并更新/对齐 meta 与文件 mtime。
219
- - 覆盖范围至少包含本次 touched 的接口引擎、表单 V8 事件、字段 V8 事件、模块按钮 V8、工作流节点 V8、数据源 V8。
220
- - 若没有插件 API 可调用,可在 `.tmp/` 编写一次性同步脚本;脚本必须先 dry-run 输出差异数量,再 apply。
221
- - 收尾复核应确认 touched 范围没有 `changed/created/local-only` 差异;若存在差异,必须在最终回复中说明原因。
222
- - 空 V8 代码不生成本地 `.js` 文件;同步收尾时应清理历史空文件。
223
- - 插件“查看同步状态”不能只显示数量。若本地未推送数量大于 0,必须能列出具体资源类型、Key、名称、同步基准时间、本地修改时间和文件路径,并支持直接打开该文件。
224
- - 接口引擎、表单引擎、模块引擎、流程引擎四个顶层分类必须分别提供行内“查看同步状态”,分类检测只请求该类型的远端数据,禁止每次强制扫描全部引擎。表单引擎范围必须同时覆盖表单 V8 事件与字段 V8 事件,模块引擎范围必须覆盖按钮 V8 与 `SqlJoin/SqlWhere`。
225
- - 一次同步检测的结果必须持久保留在侧边“同步结果”树中,按双方冲突、服务器较新、本地未推送分组;用户应能连续点击不同文件查看远端/本地差异,只有主动刷新时才重新请求服务器,禁止每查看一个文件都重新执行整次检测。
226
- - 表单事件、字段事件、按钮事件等远端 Key 比对要做大小写兼容;当本地代码与远端代码相同但 mtime 晚于 meta 时,应自动刷新 meta/mtime,避免误报“本地未推送”。
1
+ ---
2
+ name: v8-explorer-tree
3
+ description: Microi.Code 插件 V8 资源管理器目录规范(表单引擎 / 字段V8事件 / 模块引擎按钮)。覆盖 diy_table V8 事件、diy_field V8 事件、sys_menu 按钮 JSON 拆分回写,含 7 条安全不变量。用于实现 VSCode V8 事件树、重构目录布局或处理 sys_menu 按钮 JSON 序列化。
4
+ ---
5
+
6
+ > **Codex 非阻塞自动更新:** 当前宿主为 Codex 时,吾码 CLI、Codex 插件与工作区 AI/MCP 由后台自动更新;需要诊断时读取 `../microi-codex-installer/SKILL.md`。更新失败、等待空闲或尚未重载均不得阻断当前、正在进行或新建任务。非 Codex 宿主跳过此项。
7
+
8
+ # v8-explorer-tree — V8 资源管理器目录规范 v2(2026-05)
9
+
10
+ > 涉及文件:[Microi.Code/src/explorer/engineTreeProvider.ts](Microi.Code/src/explorer/engineTreeProvider.ts)、[Microi.Code/src/sync/syncManager.ts](Microi.Code/src/sync/syncManager.ts)、[Microi.Code/src/constants.ts](Microi.Code/src/constants.ts)、[Microi.Code/src/utils.ts](Microi.Code/src/utils.ts)、[Microi.Server/Microi.net.Api/Controllers/V8EngineController.cs](Microi.Server/Microi.net.Api/Controllers/V8EngineController.cs)
11
+
12
+ ---
13
+
14
+ MCP 后端实现位于 `Microi.Server/Microi.MCP/`:`V8Engine/V8McpLogic*.cs` 维护业务操作,`Http/V8McpEndpointService.cs` 维护参数归一化与调用,HTTP/调试权限和 WebSocket 会话也由该独立库承载。API 的 `V8EngineController.cs` 只声明原有路由、HTTP 特性与转发,继续兼容 `/api/V8Engine/*`、`/api/V8Debug/*` 和 `/api/V8Debug/ws`。
15
+
16
+ ## 0. 命名规范(强制)
17
+
18
+ **所有目录与文件**统一使用 **中文(英文)** 形式,使用**全角括号 `()`**:
19
+
20
+ | 元素 | 格式 | 示例 |
21
+ |------|------|------|
22
+ | 表 | `<表Label>(<表Name>)` | `仓库档案(diy_cangkuda)` |
23
+ | 字段 | `<字段Label>(<字段Name>)` | `仓库名称(CangKuName)` |
24
+ | 菜单 | `<菜单Name中文>(<菜单英文Name>)` | `订单管理(OrderManage)` |
25
+ | 事件 | `<事件中文名>(<EventType>)` | `前端表单进入V8事件(InFormV8).js` |
26
+ | 字段事件 | `<事件中文名>(<英文Key>)` | `值变更V8事件(V8Code).js` |
27
+ | 按钮 | `<按钮Name>(<按钮Id>)` | `指派(01K3X...).js` |
28
+ | 按钮显隐 | `<按钮Name>_显隐判断(<按钮Id>)` | `指派_显隐判断(01K3X...).js` |
29
+ | 按钮分类目录 | `<按钮类型中文>(<EnglishField>)` | `[行]更多按钮(MoreBtns)/` |
30
+
31
+ ### 0.1 7 类表单 V8 事件中文名(以 `diy_table` 对应 `diy_field.Label` 为准)
32
+
33
+ | EventType | 中文名 |
34
+ |-----------|--------|
35
+ | InFormV8 | 前端表单进入V8事件 |
36
+ | SubmitFormV8 | 前端表单提交前V8事件 |
37
+ | SubmitBeforeServerV8 | 后端表单提交前V8事件 |
38
+ | SubmitAfterServerV8 | 后端表单提交后V8事件 |
39
+ | ServerDataV8 | 后端数据处理V8事件 |
40
+ | OutFormV8 | 前端表单提交后V8事件 |
41
+ | DataFilterV8 | 后端数据过滤V8事件 |
42
+
43
+ > 统一用“后端”,不要再生成“服务端”或“服务器端”可见目录/文件名。
44
+
45
+ ### 0.2 4 类字段 V8 事件中文名
46
+
47
+ | Key | 中文名 |
48
+ |-----|--------|
49
+ | V8Code | 值变更V8事件 |
50
+ | KeyupV8Code | 键盘抬起V8事件 |
51
+ | V8TmpEngineTable | 表格列模板V8事件 |
52
+ | V8TmpEngineForm | 表单字段模板V8事件 |
53
+
54
+ ### 0.3 6 类按钮中文名
55
+
56
+ | Field | 中文名 |
57
+ |-------|--------|
58
+ | PageTabs | 页面多Tab |
59
+ | PageBtns | [页面]更多按钮 |
60
+ | MoreBtns | [行]更多按钮 |
61
+ | FormBtns | [表单]更多按钮 |
62
+ | BatchSelectMoreBtns | [批量选择]更多按钮 |
63
+ | ExportMoreBtns | [导出]更多按钮 |
64
+
65
+ ---
66
+
67
+ ## 1. 当前结构(v1 基线)
68
+
69
+ ```
70
+ <workspace>/microi-v8-engine/
71
+ ├── 接口引擎/
72
+ │ └── <ApiEngineName>(<ApiEngineKey>).js // 半角括号,保留不变
73
+ └── V8事件/
74
+ └── <表Description(表Name)>/
75
+ └── <EventType>.js // 扁平,无中文事件名
76
+ ```
77
+
78
+ ## 2. 目标结构(v2)
79
+
80
+ ```
81
+ <workspace>/microi-v8-engine/<server>/<osClient>/
82
+ ├── 接口引擎/ // 保持不变
83
+ │ └── <分类>/<ApiEngineName>(<ApiEngineKey>).js
84
+ │
85
+ ├── 表单引擎/
86
+ │ └── <表Label>(<表Name>)/
87
+ │ ├── 表单V8事件/
88
+ │ │ ├── 前端表单进入V8事件(InFormV8).js // 仅远端非空代码生成文件
89
+ │ │ ├── 前端表单提交前V8事件(SubmitFormV8).js
90
+ │ │ ├── 后端表单提交前V8事件(SubmitBeforeServerV8).js
91
+ │ │ ├── 后端表单提交后V8事件(SubmitAfterServerV8).js
92
+ │ │ ├── 后端数据处理V8事件(ServerDataV8).js
93
+ │ │ ├── 前端表单提交后V8事件(OutFormV8).js
94
+ │ │ └── 后端数据过滤V8事件(DataFilterV8).js
95
+ │ └── 字段V8事件/
96
+ │ └── <字段Label>(<字段Name>)/
97
+ │ ├── 值变更V8事件(V8Code).js // 右键单表拉取后生成,仅远端非空代码生成文件
98
+ │ ├── 键盘V8事件(KeyupV8Code).js
99
+ │ ├── 模板V8引擎(表格)(V8TmpEngineTable).js
100
+ │ └── 模板V8引擎(表单)(V8TmpEngineForm).js
101
+ │
102
+ └── 模块引擎/
103
+ └── <父菜单Name>(<ParentMenuId>)/ // 非叶子模块只显示子模块,不生成按钮目录
104
+ └── <叶子菜单Name>(<LeafMenuId>)/
105
+ ├── Join关联(SqlJoin).js // 始终生成,直接映射 sys_menu.SqlJoin
106
+ ├── Where条件(SqlWhere).js // 始终生成,直接映射 sys_menu.SqlWhere
107
+ ├── 页面多Tab(PageTabs)/ // 叶子模块固定生成全部 6 个按钮目录,即使为空
108
+ │ ├── <按钮Name>(<按钮Id>).js // V8Code,仅远端非空代码生成文件
109
+ │ └── <按钮Name>_显隐判断(<按钮Id>).js // V8CodeShow,仅在非空时生成
110
+ ├── [页面]更多按钮(PageBtns)/
111
+ ├── [行]更多按钮(MoreBtns)/
112
+ ├── [表单]更多按钮(FormBtns)/
113
+ ├── [批量选择]更多按钮(BatchSelectMoreBtns)/
114
+ └── [导出]更多按钮(ExportMoreBtns)/
115
+ ```
116
+
117
+ ### 2.1 ⚠️ 空代码不落文件规则(关键约束)
118
+
119
+ > **前端 V8 引擎**只要事件 / 按钮代码字符串非空(哪怕只有注释、空格),就会调用 Jint 解析并执行——产生不必要的 CPU 开销。
120
+ >
121
+ > 因此:拉取远端资源时,空 V8 代码**不要生成本地 `.js` 文件**。若历史同步留下空 `.js` 文件,且远端代码同样为空,应删除本地空文件,避免被“查看同步状态”误判为本地未推送。
122
+ >
123
+ > 用户需要清空远端代码时,可以把已有非空文件清空并推送;推送成功后插件应删除该本地空文件或对齐为“无本地文件”状态。
124
+ >
125
+ > 例外:`SqlJoin`、`SqlWhere` 是模块查询配置,不是按钮 V8 事件。叶子模块必须始终生成两个平级文件,空文件表示远端字段为空,并允许通过推送空文件清空对应字段。
126
+
127
+ ---
128
+
129
+ ## 3. 数据流
130
+
131
+ ### 3.1 表单 V8 事件(✅ 已实现 2026-05)
132
+
133
+ - **拉取**:复用 `GetV8EventList`。对每个返回项:
134
+ 1. 每张表固定生成表单 V8 事件目录
135
+ 2. 仅远端非空代码生成 `.js`;空代码不生成文件,历史空文件应删除
136
+ - **保存**:`parseEventFilePath` 解析新旧两种结构,调 `UpdateV8EventCode`
137
+ - **删除**:用户删除本地文件默认只删除本地文件,不自动清空远端;清空远端必须通过清空已有文件内容并执行推送,或使用明确的“清空远端代码”命令
138
+
139
+ ### 3.2 字段 V8 事件(✅ 已实现 2026-05)
140
+
141
+ **接口**:
142
+ - `GET /api/V8Debug/GetFieldList?osClient&tableId` — 返回字段列表带 `V8Code/KeyupV8Code/V8TmpEngineTable/V8TmpEngineForm`
143
+ - `POST /api/V8Debug/UpdateField` — Body 含 `Id` 或 `TableName + Name`,仅 V8 字段更新时直接写 `diy_field`,不触发表字段物理列变更
144
+
145
+ **实施清单**:
146
+ 1. 在 `engineTreeProvider.ts` 的表节点加 inline action「拉取此表字段V8事件」
147
+ 2. 调 `GetFieldList` 拿字段,对每个字段固定生成 4 个文件:`字段V8事件/<Label>(<Name>)/<事件中文>(<Key>).js`
148
+ 3. 保存时按文件路径解析 `(TableId, FieldName, V8Key)` → 调 `UpdateField`
149
+
150
+ ### 3.3 sys_menu 按钮 JSON 拆分(✅ 已实现 2026-05)
151
+
152
+ **接口**:
153
+ - `GET /api/V8Debug/GetModule?osClient&moduleId` — 返回菜单详情,包含 6 类按钮 JSON 以及 `SqlJoin/SqlWhere`
154
+ - `POST /api/V8Debug/UpdateModule` — Body 含 `OsClient/ModuleId` 以及目标按钮字段或 `SqlJoin/SqlWhere`
155
+
156
+ **拉取**:
157
+ 1. 解析 `PageTabs/PageBtns/MoreBtns/FormBtns/BatchSelectMoreBtns/ExportMoreBtns` 6 个 JSON 数组(非法 JSON 记录错误并跳过该字段)
158
+ 2. 按 `ParentId` 生成与 `sys_menu/GetSysMenuStep` 一致的模块树;非叶子模块只生成子模块目录,不生成按钮目录
159
+ 3. 叶子模块固定生成全部 6 个按钮分类目录,并直接生成 `Join关联(SqlJoin).js`、`Where条件(SqlWhere).js`;两个配置文件即使远端为空也必须生成
160
+ 4. 对每个按钮:
161
+ - 仅在 `V8Code` 非空时生成 `<按钮Name>(<按钮Id>).js`
162
+ - 若 `V8CodeShow` 非空 → 生成 `<按钮Name>_显隐判断(<按钮Id>).js`
163
+ - 在空按钮目录中新建 `<按钮Name>(<按钮Id>).js` 并推送时,可按文件名创建远端新按钮
164
+ 5. `.microi-meta.json` 写入按钮 JSON hash;回写前写 `.backups/<moduleId>/<ButtonField>-<timestamp>.json`
165
+
166
+ **保存(事务,必须满足 §4 全部不变量)**:
167
+ 1. 从文件路径解析 `ModuleId / ButtonField / ButtonId / V8Code 或 V8CodeShow`
168
+ 2. 二次调 `GetModule` 拉远端当前 JSON
169
+ 3. JSON 解析失败立即中止
170
+ 4. **Id-Diff 合并**:只修改目标按钮的 `V8Code` 或 `V8CodeShow`,远端其它按钮全部原样保留
171
+ 5. 写一份 `.backups/<moduleId>/<ButtonField>-<timestamp>.json`(变更前的远端 JSON)
172
+ 6. 调 `UpdateModule` 整段写回;任何一步失败不写库
173
+
174
+ **SqlJoin/SqlWhere 保存**:从文件路径解析 `ModuleId / SqlJoin|SqlWhere`,推送前重新 `GetModule`,备份远端原字段,`UpdateModule` 只提交目标字段;成功后必须再次 `GetModule` 回读正文完全一致,才更新 meta/mtime。空正文正常保留为空文件。
175
+
176
+ **修改状态**:接口引擎、表单V8事件、字段V8事件、模块按钮、模块 SqlJoin/SqlWhere 文件都必须写入 `.microi-meta.json` 的 `updateTime/filePath`;本地 `mtime > updateTime + 1s` 时树节点显示 `已修改`,推送成功后同步更新 meta 并将文件 mtime 设置为远端更新时间以清除标记。
177
+
178
+ **MCP 请求服务 OsClient 规则**:`CheckPermission()` 返回的 `token` 是 `dynamic`。调用 `V8McpLogic.ResolveOsClient` 时必须传 `(object)token`,并让 `ResolveOsClient(string osClient, object currentToken)` 返回真正的 `string`;`Microi.MCP/Http/V8McpEndpointService.cs` 中 OsClient 空判断用 `string.IsNullOrWhiteSpace(osClient)`,不要对 `osClient` 调 `DosIsNullOrWhiteSpace()`。
179
+
180
+ ---
181
+
182
+ ## 4. 风险与不变量(实施 Stage 2/3 前必读)
183
+
184
+ | # | 不变量 | 违反后果 | 检测手段 |
185
+ |---|--------|---------|---------|
186
+ | I1 | 按钮 `Id` 必须保留且唯一 | 远端按钮丢失/重复 | 文件名中 `(<Id>)` 段 + 头部 `@ButtonId` 双校验,二者必须一致 |
187
+ | I2 | 远端按钮在本地缺失时**保留**而非删除 | 用户只拉了一类按钮就保存 → 其余被清空 | 保存前必须 `GetModule` 重新拉取做 Id-Diff |
188
+ | I3 | 远端整段 JSON 哈希在保存前必须匹配 | 多人/多端冲突覆盖 | `.microi-menu.json.RemoteHash` 检查 |
189
+ | I4 | 任何 JSON 解析失败必须中止保存 | JSON 损坏 | try/catch + 弹窗 |
190
+ | I5 | 按钮元数据头被用户改坏(如 `@ButtonId` 被删) | 写回失败 | 元数据头丢失 → 该文件标记 dirty,提示从远端覆盖 |
191
+ | I6 | 每次回写前自动写 `.backups/<timestamp>.json` | 误操作可恢复 | 强制执行,无配置开关 |
192
+ | I7 | 用户重命名/删除按钮文件 | 误操作 | 重命名按 Id 匹配;删除需弹窗输入按钮名二次确认 |
193
+
194
+ ---
195
+
196
+ ## 5. 实施状态(2026-05-22)
197
+
198
+ - ✅ **Stage 1 表单V8事件分层 + 中文命名 + 空代码跳过文件生成** —— 已实现
199
+ - ✅ **Stage 2 字段V8事件** —— 已实现:右键单表拉取字段 V8 事件;空代码不生成文件;推送仅更新目标字段的目标 V8 字段
200
+ - ✅ **Stage 3 sys_menu 按钮拆分** —— 已实现:主拉取同步模块引擎;按钮 V8Code 仅在非空时生成 `.js`;推送前重拉远端 JSON、按 ButtonId 只改目标按钮并保留其它按钮,写入前自动备份
201
+ - ✅ **Stage 4 树形模块引擎 + 全类型修改状态** —— 已实现:模块按 ParentId 树形拉取;非叶子只显示子模块;叶子固定生成 6 个按钮目录;字段/按钮文件也显示并清除 `已修改`
202
+ - ✅ **Stage 5 sys_menu 查询脚本本地化** —— 已实现:叶子模块固定生成 `SqlJoin/SqlWhere` 两个平级 `.js` 文件,支持备份、预检、推送、远端回读和修改状态
203
+
204
+ ---
205
+
206
+ ## 6. 实施约束总结
207
+
208
+ - **不要**在没看过 §4 全部 7 条不变量就动手实现 Stage 3
209
+ - **不要**为远端空 V8 事件/按钮代码生成 `.js` 文件;`SqlJoin/SqlWhere` 属于查询配置,必须保留空文件
210
+ - **不要**用半角括号;统一用全角 `()`
211
+ - **不要**清空 V8事件/ 旧目录;保留兼容直到用户主动删除
212
+ - 文件名中 `<英文Key>` 段必须 ASCII,避免 Windows 不可见字符
213
+ - 远端 Key 若含 `\`、`/`、括号或 Windows 非法字符,文件名中的 Key 段必须使用可逆百分号编码;解析和推送时必须解码回原始 Key,禁止用 `_` 不可逆替换后误更新另一条远端记录。文件名还要处理控制字符、结尾空格/点和 `CON/PRN/AUX/NUL/COM1/LPT1` 等 Windows 保留名。
214
+ - “拉取此服务器代码”必须按接口引擎、表单引擎、模块引擎、流程引擎、数据库结构分阶段执行。某一类型失败时要记录明细并继续后续类型;同一类型内单个文件写入失败也要继续下一项,禁止首个 `ENOENT` 让整个服务器只留下半套源码。
215
+
216
+ ## 7. AI 远端修改后的同步约束
217
+
218
+ AI 使用 MCP、接口引擎、数据库脚本或平台 API 修改远端 V8 代码后,必须在任务收尾时同步本地 V8 资源树,并用“查看同步状态”或等价 dry-run 复核。
219
+
220
+ - 远端写入后以远端当前生效代码为准,回写本地文件并更新/对齐 meta 与文件 mtime。
221
+ - 覆盖范围至少包含本次 touched 的接口引擎、表单 V8 事件、字段 V8 事件、模块按钮 V8、工作流节点 V8、数据源 V8。
222
+ - 若没有插件 API 可调用,可在 `.tmp/` 编写一次性同步脚本;脚本必须先 dry-run 输出差异数量,再 apply。
223
+ - 收尾复核应确认 touched 范围没有 `changed/created/local-only` 差异;若存在差异,必须在最终回复中说明原因。
224
+ - 空 V8 代码不生成本地 `.js` 文件;同步收尾时应清理历史空文件。
225
+ - 插件“查看同步状态”不能只显示数量。若本地未推送数量大于 0,必须能列出具体资源类型、Key、名称、同步基准时间、本地修改时间和文件路径,并支持直接打开该文件。
226
+ - 接口引擎、表单引擎、模块引擎、流程引擎四个顶层分类必须分别提供行内“查看同步状态”,分类检测只请求该类型的远端数据,禁止每次强制扫描全部引擎。表单引擎范围必须同时覆盖表单 V8 事件与字段 V8 事件,模块引擎范围必须覆盖按钮 V8 与 `SqlJoin/SqlWhere`。
227
+ - 一次同步检测的结果必须持久保留在侧边“同步结果”树中,按双方冲突、服务器较新、本地未推送分组;用户应能连续点击不同文件查看远端/本地差异,只有主动刷新时才重新请求服务器,禁止每查看一个文件都重新执行整次检测。
228
+ - 表单事件、字段事件、按钮事件等远端 Key 比对要做大小写兼容;当本地代码与远端代码相同但 mtime 晚于 meta 时,应自动刷新 meta/mtime,避免误报“本地未推送”。
@@ -13,6 +13,31 @@ description: Microi V8 与 MCP 文件上传下载指南。用于处理流式 AI
13
13
 
14
14
  ## 表单字段的公有桶与私有桶(强制)
15
15
 
16
+ - `Config.FileUpload.EnableRolePermission=true` 的字段强制私有上传,优先级高于字段和请求 `Limit`。
17
+ 每个附件 `VisibleRoleIds` 多选真实角色,三个默认关闭的开关为 `HideUnauthorizedFiles`、
18
+ `ShowUnauthorizedFileName`、`DisableRoleInheritance`。无权限时后端仅返回脱敏占位,不含 Path、
19
+ Versions 或签名地址;普通保存必须保留无权附件,不能改角色、改名、删除或通过复制路径重新授权。
20
+ 上传响应的 `_UploadProof` 绑定当前租户、用户、字段、记录、私有路径及有效期,只用于首次保存校验,
21
+ 持久化时移除;新文件须携带当前记录/字段上下文上传。历史公有附件需重新私有上传后才能设置角色。
22
+ 完整配置、继承与 MCP 流程见 `../microi-form-engine/SKILL.md`。
23
+
24
+ ### 旧上传返回与接口引擎扩展
25
+
26
+ - 系统设置 `CompatiblePlatformOldVersion` 默认关闭,空值/缺字段也关闭;开启时旧 HTTP 上传
27
+ 保留 `Code/Data`,单文件也统一返回数组(包括 `Multiple=false`),补 `url/type/size/duration/uploading/progress/path/name/id`。
28
+ 图片 `type=image`,名称去掉最后一个扩展名;`url` 必须沿用实际 `Url`,不能把私有文件改拼公有地址。
29
+ - `/api/HDFS/upload`、`/api/Upload`、`/apiengine/platform-hdfs-upload` 使用当前租户上传引擎。
30
+ 只有主库确认完整地址、别名及固定 Key 缺失才执行编译兜底;禁用、StopHttp、拒绝或异常不兜底。
31
+ - `V8.Method.UploadCurrentRequestAsync()` 复用已验证的当前请求文件流,重复调用只上传一次;
32
+ `V8.Method.IsLegacyUploadCompatibilityEnabled()` 读取宿主取得的开关,二者均不接受参数。
33
+ 它们绑定当前租户与选定引擎,普通脚本或嵌套其它引擎不能借用;无请求时使用原有 `V8.Method.Upload`。
34
+ - 返回编排由系统设置应用拥有的 Managed `platform-hdfs-upload` 实现;持久定制使用
35
+ CreateIfMissing `platform-hdfs-upload-hook`,返回 `{Code:1, UploadResult:完整结果}`。
36
+ 先用 `JSON.parse(JSON.stringify(V8.Param.Result))` 转为普通 JS 对象,再判断 `Data` 数组并遍历追加字段;不能直接对 CLR 包装对象使用 `Array.isArray` 或 `length` 分支。
37
+ 系统设置及基础空库包都交付可空字段,但上传引擎只由系统设置包拥有;不携带租户开关值。
38
+ - MCP 复用 `microi_add_field`、`microi_update_field`、`microi_create_engine`、
39
+ `microi_save_engine_code` 和管理员回读;验收分别覆盖真实上传、配置缓存、引擎优先、缺失及错误分支。
40
+
16
41
  - `ImgUpload`、`FileUpload`、`RichText` 的“禁止匿名访问”是字段权威策略:`Limit=false` 写公有桶,`Limit=true` 写私有桶。普通用户只要通过当前菜单/表的新增或编辑动作授权,也必须按该字段配置执行;不得按用户等级把全部非超级管理员上传统一改成私有桶。
17
42
  - 浏览器上传必须携带 `FormEngineKey + FieldId + SysMenuId`,编辑已有记录再带 `FormDataId`,TableChild 再带父子授权上下文。后端先用 FormEngine 校验动作权限,再从当前租户回读 `diy_field.Component/Config`,用权威 `Limit` 覆盖请求值,并把目录固定为 `ImgUpload→img`、`FileUpload→file`、`RichText→editor`。
18
43
  - 客户端 `Limit`、`Path`、字段 Id 和菜单 Id 都只是待验证线索。没有可验证字段上下文的普通交互式上传默认私有并限制到安全一级目录;不能为了恢复公有字段语义而重新信任裸 `Limit=false`。
@@ -79,7 +104,7 @@ description: Microi V8 与 MCP 文件上传下载指南。用于处理流式 AI
79
104
  可信后端 V8 可用 `V8.Http.GetResponse({ Url: url }).RawBytes` 下载,再用 `System.Convert.ToBase64String` 和 `V8.Method.Upload` 上传。该路径同样必须校验域名、大小、Content-Type、后缀和最终重定向目标。
80
105
 
81
106
  <!-- /microi-progressive:chunk -->
82
- <!-- microi-progressive:chunk id=v8-file-upload-002 sha256=ed7e727c51bd2416ae1f38cf3c4bd09491e616df08473a610372e0ca648e1298 -->
107
+ <!-- microi-progressive:chunk id=v8-file-upload-002 sha256=d55c1a7fce715bf15224a74a2ae3006f38bab04bfe876ad88d9cc064fbe7cb9a -->
83
108
  ## 接收前端上传的文件
84
109
 
85
110
  前端发起文件上传时,平台自动把文件以 base64 形式注入到 `V8.FilesByteBase64`:
@@ -2,7 +2,7 @@
2
2
 
3
3
  > 按需读取;本文件由 SKILL.md 的原章节无损拆分。
4
4
 
5
- <!-- microi-progressive:chunk id=v8-file-upload-005 sha256=b09a96f25aef20eb8cbb204bd785cce370dcd2c9a28563c6f6d720fbdaa5218f -->
5
+ <!-- microi-progressive:chunk id=v8-file-upload-005 sha256=9efd186d8af26854bbed4d675b31fff57202a0139094798d9a41c3e4769fd092 -->
6
6
  ## 公有桶 vs 私有桶
7
7
 
8
8
  ### 应用商城 ZIP
@@ -2,7 +2,7 @@
2
2
 
3
3
  > 按需读取;本文件由 SKILL.md 的原章节无损拆分。
4
4
 
5
- <!-- microi-progressive:chunk id=v8-file-upload-008 sha256=5e3b59486745d3c54bd8ad25c43cbe2396e6d614394340b93d93794b6df56292 -->
5
+ <!-- microi-progressive:chunk id=v8-file-upload-008 sha256=7cda0fe7bb978e7219cb3c1ba30813e1a5e777f1272410f85d4c7ecc1882cc3d -->
6
6
  ## Office 文件在线编辑版本号规则
7
7
 
8
8
  当文件上传控件开启【Office 在线预览】、【允许在线编辑】和【开启 Office 文件版本号】时,前后端必须遵循统一版本规则:
@@ -39,7 +39,7 @@
39
39
  ```
40
40
 
41
41
  <!-- /microi-progressive:chunk -->
42
- <!-- microi-progressive:chunk id=v8-file-upload-009 sha256=50fba71370f45de3dd677f5731f3956c328a8d3b1ca2125b853128067f9af40a -->
42
+ <!-- microi-progressive:chunk id=v8-file-upload-009 sha256=e01219d4f1a8f8c535e0b5aa5620120c37bd5e79af27dbbd63babda8d887cd13 -->
43
43
  ## ImgUpload / FileUpload 字段值兼容规则
44
44
 
45
45
  `ImgUpload` 不能假设只是一种值结构。PC 表单、移动端、旧数据、单图/多图、公开/私有桶会混合出现以下格式:
@@ -120,7 +120,7 @@ function publicUploadUrl(path) {
120
120
  私有桶(`Limit === true`)不要拼 `FileServer`,必须把归一化后的 `Path` 传给 `V8.Method.GetPrivateFileUrl({ FilePathName: path })` 或后端签名接口换临时 URL。
121
121
 
122
122
  <!-- /microi-progressive:chunk -->
123
- <!-- microi-progressive:chunk id=v8-file-upload-010 sha256=f34d476435c3a8321df017b481771d757f580c8d75f7648fdd54ee1c624d116c -->
123
+ <!-- microi-progressive:chunk id=v8-file-upload-010 sha256=229294a07a8db953219c35292db5558430addb576505695b9ce88a275eb46232 -->
124
124
  ## 安全注意
125
125
 
126
126
  - ❌ 不要让前端任意指定 `Path`(路径穿越风险),只允许后端固定路径
@@ -5,7 +5,9 @@ description: Microi V8 HTTP 集成指南。用于通过 V8.Http.Get/Post/Patch
5
5
 
6
6
  > **Codex 非阻塞自动更新:** 当前宿主为 Codex 时,吾码 CLI、Codex 插件与工作区 AI/MCP 由后台自动更新;需要诊断时读取 `../microi-codex-installer/SKILL.md`。更新失败、等待空闲或尚未重载均不得阻断当前、正在进行或新建任务。非 Codex 宿主跳过此项。
7
7
 
8
- # Microi V8 HTTP 外部接口集成
8
+ # Microi V8 HTTP 外部接口集成
9
+
10
+ 第三方集成一旦需要 App Secret、ClientSecret、API Key(例如畅捷通 T+),先发现现有“系统设置 → 安全与服务接入”,调用 `microi_manage_server_private_secret` 的 List/Save,将 Secret 存为当前租户 `mci_system_setting` 的加密私密项。后端通过 `V8.SysConfig.ServerPrivateSettings['Integration.Changjet.AppSecret']` 使用。不要因为 `V8.OsClientModel` 已脱敏就判断平台无法存储或读取 Secret,也不要要求新增服务器配置/C# 端点。已有用户授权即可执行保存与脱敏回读;缺少工具时先读取 `../v8-security/SKILL.md` 的现有能力与实际连接版本。
9
11
 
10
12
  你正在开发 Microi 吾码平台的 V8 引擎代码,需要调用外部 HTTP API(微信、支付宝、短信、ERP 等第三方系统)。
11
13
 
@@ -15,7 +15,9 @@ description: Microi V8 安全指南。用于审查 DiyToken 与权限、可逆
15
15
 
16
16
  <!-- microi-progressive:begin -->
17
17
  <!-- microi-progressive:chunk id=v8-security-000 sha256=d03ee34e72925db55f9022de26dfc251c0d3d69fac52ae6a910d42ddfc142ae7 -->
18
- ## 0. 租户动态系统设置与密钥边界
18
+ ## 0. 租户动态系统设置与密钥边界
19
+
20
+ **AI 默认处理路径:** 用户交付第三方 Secret 并授权配置时,优先调用 `microi_manage_server_private_secret`:List 只确认 Key/HasSecret;Save 使用 `confirmExecution=SAVE:<ConfigKey>`,固定 `IsSecret=true / IsPublic=false` 并自动脱敏回读。例如畅捷通使用 `Integration.Changjet.AppSecret`,后端从 `V8.SysConfig.ServerPrivateSettings` 使用。不要仅拒绝硬编码后停止,也不要因为 `OsClientModel` 不暴露密钥而误判服务端 V8 无法调用第三方。工具不提供原文揭示;更换已有 Key 前回读现状,超时只回读、不盲目重写。
19
21
 
20
22
  第三方密钥(微信、支付宝、OpenAI、阿里云、ERP、SMTP)**禁止**硬编码在 V8 代码或前端。公开的租户配置必须建成当前租户 `sys_config` 的实体字段;敏感或仅供后端使用的租户业务配置保存到 `mci_system_setting`。数据库、Redis、MongoDB、MinIO、MQ 等部署控制面仍由主库 `sys_osclients` 托管,子租户不能修改。
21
23