@stalfh233/omc-cli 0.4.77 → 0.4.79
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -27
- package/dist/form-support.js +62 -16
- package/package.json +3 -3
- package/skills/omc-capabilities.md +2 -0
package/README.md
CHANGED
|
@@ -18,6 +18,15 @@
|
|
|
18
18
|
(x=327、158×40、y=36/116/196…),缺失路由坐标按节点**边中点**推导,缺失
|
|
19
19
|
`routeCondition`/`matchType` 补 `true`/`ANY`;已可渲染的布局与分支图原样保留
|
|
20
20
|
——调用方无需手算几何。
|
|
21
|
+
- **读面一步取值**——大结果落到 artifact 时,`omc call <tool> … --extract <path>` / `--extract-keys`
|
|
22
|
+
直接在**同一次调用**里取值,不必再跑一次 `artifact get`;路径支持 `.rules[code=Create].name`
|
|
23
|
+
这类数组选择器。
|
|
24
|
+
- **脚本面可读写**——表单在线 JS(`onlinejs.get/compose`)与**按钮 JS**
|
|
25
|
+
(`listview.get --buttonCode` / `listview.configure --buttonScripts`、
|
|
26
|
+
`form.get --buttonScripts` / `form.publish --buttonScripts`)都能一次读写,
|
|
27
|
+
不必翻 artifact、也不必重发整张按钮表。
|
|
28
|
+
- **应用目录(分组)**——`folder.tree/create/update/delete` + `model.move`;
|
|
29
|
+
应用树是嵌套 `children`,读面已递归展平并给 `path`/`depth`。
|
|
21
30
|
- **方法论即可读**——`skills/` 下的方法论通过 `omc help <topic>` 按需读取;agent 不必去翻仓库文件。
|
|
22
31
|
- **凭据零回显**——凭据(0600 存储或环境变量)不会出现在任何输出、产物或错误里。
|
|
23
32
|
- **不确定性语义**——传输失败时冻结(`status: "uncertain", frozen: true`),
|
|
@@ -51,12 +60,12 @@ omc call discover_environment --env <env> --args '{}' # 环境就绪(真实
|
|
|
51
60
|
`describe` 与 `contract` 重复所选能力路由,切换面不必再宽扫 topic 或契约。
|
|
52
61
|
`omc eval:discovery-comparison` 输出相对 ticket 01 基线的调用数/字节数对比。
|
|
53
62
|
|
|
54
|
-
## 工具面(
|
|
63
|
+
## 工具面(142 个工具)
|
|
55
64
|
|
|
56
65
|
| 类别 | 数量 | 说明 |
|
|
57
66
|
|---|---|---|
|
|
58
|
-
| 写工具 |
|
|
59
|
-
| 读工具 |
|
|
67
|
+
| 写工具 | 87 | 高层编排 5(`configure_*` + `apply_changes`)+ 低层原子写 82 |
|
|
68
|
+
| 读工具 | 50 | `*.list` / `*.get` / `*.load` / `*.tree` / `discover_environment` |
|
|
60
69
|
| 治理/元工具 | 5 | `get_status`、`doctor`、`coverage.manifest`、`setup.*` |
|
|
61
70
|
|
|
62
71
|
**全集与逐工具参数**:`omc commands --json`(name/lane/kind/requiredArgs/writeGate/timeout)、
|
|
@@ -73,22 +82,27 @@ omc call discover_environment --env <env> --args '{}' # 环境就绪(真实
|
|
|
73
82
|
| `setup.start` | 配置引导入口:返回待填字段与当前非密值 |
|
|
74
83
|
| `setup.commit` | 幂等提交环境配置(写 `omc.config.json` + 0600 凭据库),随后跑连通性+登录+版本族校验 |
|
|
75
84
|
|
|
76
|
-
### 读工具(
|
|
85
|
+
### 读工具(50)
|
|
77
86
|
|
|
78
87
|
| 域(lane) | 工具 |
|
|
79
88
|
|---|---|
|
|
80
89
|
| 环境 `environment-governance` | `discover_environment`、`environment.fingerprint` |
|
|
81
90
|
| 应用 `app-management` | `app.list` |
|
|
91
|
+
| 应用目录 `app-tree-folders` | `folder.tree`(递归展平嵌套 `children`,给 `path`/`depth`;`foldersOnly` 只回目录) |
|
|
82
92
|
| 数据字典 `dictionary-management` | `dictionary.list`、`dictionary.type`、`dictionary.names`、`dictionary.get`、`dictionary.records`、`dictionary.parent`、`dictionary.classes` |
|
|
93
|
+
| 业务模型 `model-management` | `model.list`、`model.inspect`(一次拿全字段/表单/视图/规则/流程摘要) |
|
|
83
94
|
| 字段 `field-families` | `field.list` |
|
|
84
|
-
| 表单 `form-publish-chain` | `form.get` |
|
|
95
|
+
| 表单 `form-publish-chain` | `form.get`(紧凑面直接回 `formButtons`/`formButtonCodes`;`--buttonScripts` 回按钮 JS 原文与 sha) |
|
|
96
|
+
| 列表视图 `W1-list-view` | `listview.get`(`--buttonCode`/`--buttons` 直接回按钮 JS 的 `scriptData`/sha)、`listview.list` |
|
|
85
97
|
| 字段级规则 `G1-datarule` | `datarule.list` |
|
|
86
98
|
| 业务规则 `B1-businessrule` | `rule.list`、`rule.get` |
|
|
87
|
-
| 流程 / 待办 `W7-S29-greenfield` | `workflow.get_draft
|
|
99
|
+
| 流程 / 待办 / 组织 `W7-S29-greenfield` | `workflow.get_draft`(每节点 `formButtons.enabled/disabled`)、`workflow.get_published`、`workflow.validate`、`workflow.instanceActivity`、`workitem.list`、`org.role.list`、`org.role.users`、`org.role.userScope`、`org.department.list`、`org.department.users`、`org.user.search`、`org.user.info`、`org.user.departments` |
|
|
100
|
+
| 在线脚本 `online-js` | `onlinejs.get`(草稿/发布面 `customScript` 原文 + SHA-256) |
|
|
88
101
|
| 运行数据 `M2-runtime-data` | `data.list`、`data.load` |
|
|
89
|
-
|
|
|
102
|
+
| 报表 `R1-report` | `report.tree`、`report.get`、`report.chartdata`、`report.datasource.list` |
|
|
103
|
+
| 业务集成 `B2-registration` | `service.list`、`service.get`、`method.get`、`service.category.list`、`service.dbconnpool.list`、`service.adapter_config`、`service.table_list`、`service.table_sql`、`service.export`、`service.check` |
|
|
90
104
|
|
|
91
|
-
## 能力总览(
|
|
105
|
+
## 能力总览(16 项)
|
|
92
106
|
|
|
93
107
|
`omc capabilities` 的静态快照;跑 `omc capabilities` / `omc capability <id>` 取实时投影。
|
|
94
108
|
「依赖」= `requires → precedes`,即能力之间的配置顺序。
|
|
@@ -96,53 +110,73 @@ omc call discover_environment --env <env> --args '{}' # 环境就绪(真实
|
|
|
96
110
|
| 能力 | 标题 | lane | 覆盖 | intent | 依赖 |
|
|
97
111
|
|---|---|---|---|---|---|
|
|
98
112
|
| `environment` | 环境治理 | `environment-governance` | executable | none | - → - |
|
|
99
|
-
| `app` | 应用 | `app-management` | executable | none | - →
|
|
113
|
+
| `app` | 应用 | `app-management` | executable | none | - → folder |
|
|
114
|
+
| `folder` | 应用目录(分组) | `app-tree-folders` | executable | none | app → - |
|
|
100
115
|
| `dictionary` | 数据字典 | `dictionary-management` | executable | none | - → - |
|
|
101
116
|
| `model` | 业务模型 | `model-management` | executable | partial | - → field, workflow |
|
|
102
|
-
| `field` |
|
|
117
|
+
| `field` | 字段(含子表、计算字段) | `field-families` | executable | partial | model → form, listview, datarule, businessrule, data |
|
|
103
118
|
| `form` | 默认表单 | `form-publish-chain` | executable | partial | field → onlinejs |
|
|
104
119
|
| `listview` | 列表视图 | `W1-list-view` | partial | partial | field → - |
|
|
105
120
|
| `datarule` | 字段级控件规则 | `G1-datarule` | partial | partial | field → - |
|
|
106
|
-
| `businessrule` |
|
|
121
|
+
| `businessrule` | 业务规则(图形引擎,含定时任务) | `B1-businessrule` | executable | partial | field → rule-lifecycle |
|
|
107
122
|
| `rule-lifecycle` | 规则生命周期 | `S25-rule-lifecycle` | executable | none | businessrule → - |
|
|
108
123
|
| `workflow` | 流程模板 | `W7-S29-greenfield` | executable | partial | model → - |
|
|
109
124
|
| `onlinejs` | 表单在线 JS | `online-js` | executable | partial | form → - |
|
|
110
125
|
| `bizservice` | 业务集成 | `B2-registration` | executable | partial | - → - |
|
|
126
|
+
| `report` | 报表 | `R1-report` | partial | partial | - → - |
|
|
111
127
|
| `data` | 运行数据 | `M2-runtime-data` | executable | none | field → - |
|
|
112
128
|
|
|
113
129
|
- **覆盖**:`executable` 全量可用;`partial` 有取证边界(见 `omc capability <id>` 的 notes)。
|
|
114
130
|
- **intent**:`partial` 表示该能力可声明式(`omc intent`)覆盖一部分——通常 create/update;
|
|
115
131
|
`none` 表示只走 `omc call`。删除类、方法面、分类/连接池等仍走 `omc call` 逃生舱。
|
|
116
132
|
|
|
133
|
+
## 近期能力(0.4.74 → 0.4.78)
|
|
134
|
+
|
|
135
|
+
| 版本 | 新增 / 修复 |
|
|
136
|
+
|---|---|
|
|
137
|
+
| 0.4.78 | **按钮 JS 直接读写**:`listview.get --buttonCode/--buttons`、`listview.configure --buttonScripts`、`form.get --buttonScripts`、`form.publish --buttonScripts`,外加 `buttons[].script` 简写 |
|
|
138
|
+
| 0.4.77 | **应用目录(分组)**:`folder.tree/create/update/delete` + `model.move`(`bizmodels/update` 换 parentId,跨应用目录单独报错) |
|
|
139
|
+
| 0.4.76 | **读面一键取值**:`call --extract <path>`(可重复)/`--extract-keys`,省掉一次 `artifact get`;`form.draft fromReadback:true` 允许平台原样布局回写;`--jq` 支持数组选择器 `[code=X]` |
|
|
140
|
+
| 0.4.75 | 节点级事件 `workflow.bindRule {activityCode,nodeEvent}`;`workflow.update` merge 拓扑保护(边数收缩即拒);`workflow.setButtons`;`rule.run` / `form.runButton`;表单按钮 `options.customButtonJson` |
|
|
141
|
+
| 0.4.74 | 组织写面 15 工具(角色组/角色/成员/管理范围/部门/用户) |
|
|
142
|
+
|
|
143
|
+
> **脚本面只有三处**:表单在线 JS(`onlinejs.*`)、按钮 JS(本轮补齐读写)、自定义页面 JS
|
|
144
|
+
> (`h_custom_page`,CLI 未覆盖)。**列表视图本身没有 JS 面**(`h_biz_query` 无脚本列)。
|
|
145
|
+
|
|
117
146
|
## 写工具做什么
|
|
118
147
|
|
|
119
148
|
写工具分三层用法,**全部走同一条受治理管道**。
|
|
120
149
|
|
|
121
|
-
### 1. 高层编排(
|
|
150
|
+
### 1. 高层编排(5 个,一次调用 = 一个变更包)
|
|
122
151
|
|
|
123
152
|
| 工具 | 作用 |
|
|
124
153
|
|---|---|
|
|
125
154
|
| `configure_model` | 建模型 → 建 1..N 个字段 → 模型级 `field.publish`,`publishForm:true` 时再 `form.draft` + `form.publish`,一步到达**结构可用形态**。字段族门逐字段在写入前拒绝。 |
|
|
126
155
|
| `configure_form` | 给模型配默认表单:`fieldCodes` 决定绑定哪些已发布字段(按序;省略=全量;未知 code 在建表前拒绝 `form-fieldcodes-unknown`),再发布。 |
|
|
127
156
|
| `configure_rule` | 保存并启用一条业务规则(graph 规则引擎)——委托 `rule.save` 的 upsert + `rule.enable`。 |
|
|
157
|
+
| `configure_workflow` | 建流程模板 → 写活动/路由 → 发布 → 绑定事件规则,一步到达可发起形态(节点级事件用 `workflow.bindRule {activityCode,nodeEvent}`)。 |
|
|
128
158
|
| `apply_changes` | **批量入口**:一次提交异构变更清单 `changes:[{tool,args}]`(只收低层原子写工具),按序执行、逐步权威读回、**首败即停**并报已完成步骤与失败点;每步传输超时按 `uncertain` 冻结。含**整批时间预算** `totalTimeoutMs`(缺省 8 分钟):超预算即停并返回 `uncertain` + `progressFile` + `resumeFromIndex`,绝不盲重试。 |
|
|
129
159
|
|
|
130
|
-
### 2. 低层原子写(
|
|
160
|
+
### 2. 低层原子写(82 个,按域)
|
|
131
161
|
|
|
132
162
|
| 域(lane) | 工具 |
|
|
133
163
|
|---|---|
|
|
134
|
-
| 模型 `model-management` | `model.create`、`model.update`(应用树节点改名补丁:bizSchemaCode/id/name/parentId 四键必需,modelType
|
|
135
|
-
|
|
|
136
|
-
|
|
|
164
|
+
| 模型 `model-management` | `model.create`、`model.update`(应用树节点改名补丁:bizSchemaCode/id/name/parentId 四键必需,modelType 恒拒)、`model.move`(换目录,只改菜单节点归属)、`model.setSummary`、`model.delete`、`model.deleteMany` |
|
|
165
|
+
| 应用目录 `app-tree-folders` | `folder.create`(省略 `parentId` = 顶层;同父同名默认拒)、`folder.update`(改名/挂载)、`folder.delete`(非递归,有子节点即拒) |
|
|
166
|
+
| 字段 `field-families` | `field.create`、`field.update`、`field.delete`、`field.publish`(模型级一次发布 + 逐字段读回比对) |
|
|
167
|
+
| 表单 `form-publish-chain` | `configure_form`、`form.draft`(`buttons`/`buttonScripts`/`fromReadback`)、`form.publish`(`buttons`/`buttonScripts`)、`form.runButton`(表单按钮执行,`runtime/form/executeCustomButton`) |
|
|
137
168
|
| 字段级规则 `G1-datarule` | `datarule.create`、`datarule.update`、`datarule.delete` |
|
|
138
|
-
| 业务规则图 `B1-businessrule`
|
|
169
|
+
| 业务规则图 `B1-businessrule` | `rule.save`、`rule.node.update` |
|
|
170
|
+
| 规则生命周期 `S25-rule-lifecycle` | `configure_rule`、`rule.enable`、`rule.disable`、`rule.disableMany`、`rule.delete`、`rule.run`(自定义规则手动执行/按钮验收) |
|
|
139
171
|
| 运行时数据 `M2-runtime-data` | `data.save`、`data.update`、`data.submit`、`data.delete` |
|
|
140
|
-
| 流程 `W7-S29-greenfield` | `workflow.create`、`workflow.update`、`workflow.publish`、`workflow.bindRule
|
|
141
|
-
| 在线脚本 `online-js` | `onlinejs.compose
|
|
142
|
-
| 列表视图 `W1-list-view` | `listview.create`、`listview.configure
|
|
172
|
+
| 流程 / 组织 `W7-S29-greenfield` | `configure_workflow`、`workflow.create`、`workflow.update`、`workflow.publish`、`workflow.bindRule`(流程级 `handler` 或节点级 `activityCode+nodeEvent`)、`workflow.setButtons`(节点「表单操作」勾选)、`workflow.delete`;组织:`org.roleGroup.create/delete`、`org.role.create/update/delete`、`org.role.addUsers/removeUsers/setUserScope`、`org.department.create/update/delete`、`org.user.create/update/delete/transferDepartment` |
|
|
173
|
+
| 在线脚本 `online-js` | `onlinejs.compose`(表单在线 JS;平台模板保真 + SHA-256 读回) |
|
|
174
|
+
| 列表视图 `W1-list-view` | `listview.create`、`listview.configure`(`buttons[].script` / `buttonScripts` 直接写按钮 JS)、`listview.delete` |
|
|
143
175
|
| 应用 `app-management` | `app.create`、`app.update`、`app.delete` |
|
|
144
|
-
| 数据字典 `dictionary-management` | `dictionary.save
|
|
145
|
-
|
|
|
176
|
+
| 数据字典 `dictionary-management` | `dictionary.save`、`dictionary.status`、`dictionary.usage`、`dictionary.class.save`、`dictionary.class.delete` |
|
|
177
|
+
| 报表 `R1-report` | `report.create`、`report.update`、`report.configure`、`report.datasource.link`、`report.delete` |
|
|
178
|
+
| 后端服务注册 `B2-registration` | `service.register/update/delete/import`、`method.create/update/delete/test`、`service.category.create/update/delete`、`service.dbconnpool.create/update/delete` |
|
|
179
|
+
| 批量编排(lane-less) | `apply_changes` |
|
|
146
180
|
|
|
147
181
|
### 3. 每个写调用都经过的治理管道
|
|
148
182
|
|
|
@@ -186,7 +220,8 @@ omc intent readback intent.yaml --env <env> # 解码回 intent 形状做等价
|
|
|
186
220
|
|
|
187
221
|
| 命令 | 作用 |
|
|
188
222
|
|---|---|
|
|
189
|
-
| `omc artifact get <id
|
|
223
|
+
| `omc artifact get <id> [--summary\|--keys\|--jq <path>]` | 取回大读回的 `detailFile` 工件(0600,校验 sha256) |
|
|
224
|
+
| `omc call <tool> … --extract <path>` | **一步取值**:在本次结果的 artifact 内解析路径并直接回值(可重复给多个路径);`--extract-keys` 列顶层键与体积。路径支持 `.rules[code=Create].name` 这种数组选择器,免掉一次 `artifact get` |
|
|
190
225
|
| `omc setup [--list\|--edit <env>\|--remove <env>]` | 命名环境的增删改查 |
|
|
191
226
|
| `omc doctor [--environment <env>]` | 只报告的健康链 |
|
|
192
227
|
| `omc bench [--live-sample] [--env <env>]` | 工具面/wire 面的双面埋点跑分(离线 fake 或 live 采样) |
|
|
@@ -204,7 +239,7 @@ omc intent readback intent.yaml --env <env> # 解码回 intent 形状做等价
|
|
|
204
239
|
|
|
205
240
|
```
|
|
206
241
|
coverage/ m1-coverage-manifest-v1.json(能力基线)
|
|
207
|
-
skills/
|
|
242
|
+
skills/ 13 篇方法论 topic —— 以 `omc help <topic>` 暴露(见「方法论 topics」)
|
|
208
243
|
docs/ tool-surface-map.md —— 工具面地图
|
|
209
244
|
src/ args/support/plan/read/write/package/context(治理管道)
|
|
210
245
|
lanes/(各域适配器)· fake/(离线夹具)
|
|
@@ -246,7 +281,7 @@ src/ args/support/plan/read/write/package/context(治理管道)
|
|
|
246
281
|
|
|
247
282
|
## npm 分发
|
|
248
283
|
|
|
249
|
-
本包(`@stalfh233/omc-cli`,可执行命令仍是 `omc`)通过公共 npm registry 分发。`omc-core` 是 `omc-cli` 的内部依赖,发布时先发 `omc-core`,再发 `omc-cli`;两者版本保持一致。当前版本为 `0.4.
|
|
284
|
+
本包(`@stalfh233/omc-cli`,可执行命令仍是 `omc`)通过公共 npm registry 分发。`omc-core` 是 `omc-cli` 的内部依赖,发布时先发 `omc-core`,再发 `omc-cli`;两者版本保持一致。当前版本为 `0.4.78`,尚未宣称 `1.0.0` 稳定契约。
|
|
250
285
|
|
|
251
286
|
### 使用已发布包
|
|
252
287
|
|
|
@@ -264,7 +299,8 @@ omc help omc-business-configuration
|
|
|
264
299
|
|
|
265
300
|
```bash
|
|
266
301
|
npm run verify
|
|
267
|
-
npm publish --workspace
|
|
302
|
+
npm publish --workspace omc-core
|
|
303
|
+
npm publish --workspace packages/omc-fake-platform
|
|
268
304
|
npm publish --workspace packages/omc-cli
|
|
269
305
|
```
|
|
270
306
|
|
|
@@ -276,7 +312,7 @@ stdout 是机器解析的 JSON,编码按消费方选择:**终端**(`isTTY`
|
|
|
276
312
|
|
|
277
313
|
### 版本策略
|
|
278
314
|
|
|
279
|
-
- `0.
|
|
315
|
+
- `0.4.x`:继续演进 CLI/覆盖清单/配置契约,允许新增能力和必要的行为修复。
|
|
280
316
|
- `1.0.0`:等 CLI 面、intent 面、覆盖清单和分发安装流程稳定后再发布;发布前应增加迁移说明和兼容性检查。
|
|
281
317
|
|
|
282
318
|
### 分发后自检
|
package/dist/form-support.js
CHANGED
|
@@ -719,21 +719,25 @@ export function validateFormButtons(buttons, tool) {
|
|
|
719
719
|
if (!FORM_BUTTON_LOCATIONS.includes(useLocation))
|
|
720
720
|
throw new ToolRefusalError({ status: "refused", blocker: "form-buttons-invalid", message: `${tool}: buttons[${index}].useLocation 只能是 ${FORM_BUTTON_LOCATIONS.join("|")}(表单工具栏按钮用 FORM)` });
|
|
721
721
|
const bindAction = typeof button.bindAction === "string" ? button.bindAction : "";
|
|
722
|
+
// `script` is the ergonomic spelling of actionConfig.scriptData (same as
|
|
723
|
+
// listview.configure); the explicit script wins over a stale config blob.
|
|
724
|
+
const scriptShorthand = typeof button.script === "string" ? button.script : undefined;
|
|
722
725
|
if (!FORM_BUTTON_BIND_ACTIONS.has(bindAction))
|
|
723
726
|
throw new ToolRefusalError({ status: "refused", blocker: "form-buttons-invalid", message: `${tool}: buttons[${index}].bindAction 必须是 ButtonBindAction 名(SCRIPT/BUSINESS_RULE/FLOW/FORM/ADD_DATA/CUSTOM_PAGE/…)` });
|
|
724
727
|
const operateType = typeof button.operateType === "string" ? button.operateType : "";
|
|
725
728
|
if (!FORM_BUTTON_OPERATE_TYPES.has(operateType))
|
|
726
729
|
throw new ToolRefusalError({ status: "refused", blocker: "form-buttons-invalid", message: `${tool}: buttons[${index}].operateType 必须是 WITH_VIEW 或 WITH_OUT_VIEW` });
|
|
730
|
+
// Decode once: the SCRIPT guard and the script shorthand both read it.
|
|
731
|
+
const rawConfig = button.actionConfig;
|
|
732
|
+
const parsedConfigRaw = typeof rawConfig === "string" ? (() => { try {
|
|
733
|
+
return JSON.parse(rawConfig);
|
|
734
|
+
}
|
|
735
|
+
catch {
|
|
736
|
+
return undefined;
|
|
737
|
+
} })() : rawConfig;
|
|
738
|
+
const config = parsedConfigRaw && typeof parsedConfigRaw === "object" && !Array.isArray(parsedConfigRaw) ? parsedConfigRaw : undefined;
|
|
727
739
|
if (bindAction === "SCRIPT" || bindAction === "BUSINESS_RULE" || bindAction === "FLOW" || bindAction === "ADD_DATA" || bindAction === "FORM") {
|
|
728
|
-
|
|
729
|
-
const record = typeof actionConfig === "string" ? (() => { try {
|
|
730
|
-
return JSON.parse(actionConfig);
|
|
731
|
-
}
|
|
732
|
-
catch {
|
|
733
|
-
return undefined;
|
|
734
|
-
} })() : actionConfig;
|
|
735
|
-
const config = record && typeof record === "object" && !Array.isArray(record) ? record : undefined;
|
|
736
|
-
if (bindAction === "SCRIPT" && typeof config?.scriptData !== "string")
|
|
740
|
+
if (bindAction === "SCRIPT" && scriptShorthand === undefined && typeof config?.scriptData !== "string")
|
|
737
741
|
throw new ToolRefusalError({ status: "refused", blocker: "form-buttons-invalid", message: `${tool}: buttons[${index}] SCRIPT 需要 actionConfig.scriptData(前端脚本源码字符串)` });
|
|
738
742
|
if ((bindAction === "BUSINESS_RULE" || bindAction === "FLOW") && (typeof button.targetCode !== "string" || button.targetCode === ""))
|
|
739
743
|
throw new ToolRefusalError({ status: "refused", blocker: "form-buttons-invalid", message: `${tool}: buttons[${index}] ${bindAction} 需要 targetCode` });
|
|
@@ -745,9 +749,13 @@ export function validateFormButtons(buttons, tool) {
|
|
|
745
749
|
useLocation,
|
|
746
750
|
bindAction,
|
|
747
751
|
operateType,
|
|
748
|
-
//
|
|
749
|
-
//
|
|
750
|
-
...(
|
|
752
|
+
// actionConfig is a String column: serialize exactly once (double
|
|
753
|
+
// encoding breaks the renderer). `script` merges into scriptData.
|
|
754
|
+
...(scriptShorthand !== undefined
|
|
755
|
+
? { actionConfig: JSON.stringify({ ...(config ?? {}), scriptData: scriptShorthand }) }
|
|
756
|
+
: button.actionConfig !== undefined && typeof button.actionConfig !== "string"
|
|
757
|
+
? { actionConfig: JSON.stringify(button.actionConfig) }
|
|
758
|
+
: {}),
|
|
751
759
|
sortKey: typeof button.sortKey === "number" ? button.sortKey : index,
|
|
752
760
|
};
|
|
753
761
|
});
|
|
@@ -779,7 +787,8 @@ export function mergeFormOptionsWithButtons(existingOptions, input, tool) {
|
|
|
779
787
|
const removeCodes = input.removeButtonCodes ?? [];
|
|
780
788
|
if (hasButtons && hasJson)
|
|
781
789
|
throw new ToolRefusalError({ status: "refused", blocker: "form-buttons-ambiguous", message: `${tool}: buttons 与 customButtonJson 只能给一种(buttons 数组更好读;customButtonJson 用于原样透传读回值)` });
|
|
782
|
-
|
|
790
|
+
const scriptPatches = input.buttonScripts ?? [];
|
|
791
|
+
if (!hasButtons && !hasJson && removeCodes.length === 0 && scriptPatches.length === 0)
|
|
783
792
|
return undefined;
|
|
784
793
|
let incoming = [];
|
|
785
794
|
if (hasButtons) {
|
|
@@ -802,6 +811,21 @@ export function mergeFormOptionsWithButtons(existingOptions, input, tool) {
|
|
|
802
811
|
// `replace` restores the whole-list semantics.
|
|
803
812
|
const mode = input.buttonsMode ?? "merge";
|
|
804
813
|
const existing = readFormButtons(options) ?? [];
|
|
814
|
+
if (scriptPatches.length > 0) {
|
|
815
|
+
// Patch scriptData on EXISTING buttons only — the write counterpart of
|
|
816
|
+
// form.get --buttonScripts. An unknown code refuses rather than silently
|
|
817
|
+
// creating a button with no bindAction.
|
|
818
|
+
const known = new Set(existing.map((button) => String(button.code ?? "")));
|
|
819
|
+
const missing = scriptPatches.filter((entry) => !known.has(entry.code)).map((entry) => entry.code);
|
|
820
|
+
if (missing.length > 0)
|
|
821
|
+
throw new ToolRefusalError({
|
|
822
|
+
status: "refused",
|
|
823
|
+
blocker: "form-button-absent",
|
|
824
|
+
buttonCodes: missing,
|
|
825
|
+
available: [...known],
|
|
826
|
+
message: `${tool}: 表单没有按钮 ${missing.join("、")}(buttonScripts 只改已有按钮的脚本);可用:${[...known].join("、") || "(无)"}。新建按钮用 buttons + script`,
|
|
827
|
+
});
|
|
828
|
+
}
|
|
805
829
|
let final;
|
|
806
830
|
if (mode === "replace") {
|
|
807
831
|
final = incoming;
|
|
@@ -812,6 +836,27 @@ export function mergeFormOptionsWithButtons(existingOptions, input, tool) {
|
|
|
812
836
|
byCode.set(String(button.code), { ...(byCode.get(String(button.code)) ?? {}), ...button });
|
|
813
837
|
for (const code of removeCodes)
|
|
814
838
|
byCode.delete(String(code));
|
|
839
|
+
for (const patch of scriptPatches) {
|
|
840
|
+
const current = byCode.get(patch.code);
|
|
841
|
+
if (!current)
|
|
842
|
+
continue;
|
|
843
|
+
let config = {};
|
|
844
|
+
const rawConfig = current.actionConfig;
|
|
845
|
+
if (typeof rawConfig === "string" && rawConfig.trim() !== "") {
|
|
846
|
+
try {
|
|
847
|
+
const parsed = JSON.parse(rawConfig);
|
|
848
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed))
|
|
849
|
+
config = parsed;
|
|
850
|
+
}
|
|
851
|
+
catch {
|
|
852
|
+
config = {};
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
else if (rawConfig !== null && typeof rawConfig === "object" && !Array.isArray(rawConfig)) {
|
|
856
|
+
config = { ...rawConfig };
|
|
857
|
+
}
|
|
858
|
+
byCode.set(patch.code, { ...current, actionConfig: JSON.stringify({ ...config, scriptData: patch.script }) });
|
|
859
|
+
}
|
|
815
860
|
final = [...byCode.values()].map((button, index) => ({ ...button, sortKey: typeof button.sortKey === "number" ? button.sortKey : index }));
|
|
816
861
|
}
|
|
817
862
|
if (removeCodes.length > 0 && mode === "replace")
|
|
@@ -1062,7 +1107,7 @@ export async function executeFormDraftWrite(session, args) {
|
|
|
1062
1107
|
// turns them into h_biz_button rows (triggerType=FORM). Merge rather than
|
|
1063
1108
|
// replace so unrelated options (presetButtonManage, sheetPresetButtonManage)
|
|
1064
1109
|
// survive.
|
|
1065
|
-
const mergedOptions = mergeFormOptionsWithButtons(pre?.options, { buttons: args.buttons, customButtonJson: args.customButtonJson, buttonsMode: args.buttonsMode, removeButtonCodes: args.removeButtonCodes }, "form.draft");
|
|
1110
|
+
const mergedOptions = mergeFormOptionsWithButtons(pre?.options, { buttons: args.buttons, customButtonJson: args.customButtonJson, buttonsMode: args.buttonsMode, removeButtonCodes: args.removeButtonCodes, buttonScripts: args.buttonScripts }, "form.draft");
|
|
1066
1111
|
const payload = {
|
|
1067
1112
|
...(pre ?? {}),
|
|
1068
1113
|
schemaCode: args.schemaCode,
|
|
@@ -1243,7 +1288,8 @@ export async function executeFormPublishWrite(session, args) {
|
|
|
1243
1288
|
// drop the buttons (live 2026-10-23).
|
|
1244
1289
|
const hasButtonDelta = args.buttons !== undefined ||
|
|
1245
1290
|
args.customButtonJson !== undefined ||
|
|
1246
|
-
(Array.isArray(args.removeButtonCodes) && args.removeButtonCodes.length > 0)
|
|
1291
|
+
(Array.isArray(args.removeButtonCodes) && args.removeButtonCodes.length > 0) ||
|
|
1292
|
+
(Array.isArray(args.buttonScripts) && args.buttonScripts.length > 0);
|
|
1247
1293
|
const forcePromote = args.force === true || args.rebuild === true || hasButtonDelta;
|
|
1248
1294
|
if (pre.published === true && !forcePromote)
|
|
1249
1295
|
return { performed: { skipped: "already-published" }, readback: { published: true } };
|
|
@@ -1274,7 +1320,7 @@ export async function executeFormPublishWrite(session, args) {
|
|
|
1274
1320
|
// h_biz_button(triggerType=FORM) rows during THIS publish call
|
|
1275
1321
|
// (ModelManagementFacadeImpl.publishBizForm:1022). Passing them here is the
|
|
1276
1322
|
// only way to create form buttons without the designer.
|
|
1277
|
-
const mergedOptions = mergeFormOptionsWithButtons(pre.options, { buttons: args.buttons, customButtonJson: args.customButtonJson, buttonsMode: args.buttonsMode, removeButtonCodes: args.removeButtonCodes }, "form.publish");
|
|
1323
|
+
const mergedOptions = mergeFormOptionsWithButtons(pre.options, { buttons: args.buttons, customButtonJson: args.customButtonJson, buttonsMode: args.buttonsMode, removeButtonCodes: args.removeButtonCodes, buttonScripts: args.buttonScripts }, "form.publish");
|
|
1278
1324
|
const payload = {
|
|
1279
1325
|
...pre,
|
|
1280
1326
|
...(mergedOptions !== undefined ? { options: mergedOptions } : {}),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stalfh233/omc-cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.79",
|
|
4
4
|
"description": "CloudPivot 8.6 configuration capability CLI — governed, fail-closed, coverage-manifest bounded.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -50,9 +50,9 @@
|
|
|
50
50
|
"access": "public"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@stalfh233/omc-fake-platform": "0.4.
|
|
53
|
+
"@stalfh233/omc-fake-platform": "0.4.79",
|
|
54
54
|
"commander": "14.0.3",
|
|
55
|
-
"omc-core": "0.4.
|
|
55
|
+
"omc-core": "0.4.79",
|
|
56
56
|
"proper-lockfile": "4.1.2",
|
|
57
57
|
"write-file-atomic": "6.0.0",
|
|
58
58
|
"zod": "^3.23.8",
|
|
@@ -99,6 +99,7 @@ only the section for the capability you need —
|
|
|
99
99
|
|
|
100
100
|
- **能做哪些事**:表单 draft-first 布局、发布、`publishedHtmlJson` 绑定读回;把已发布字段绑进默认表单;**表单工具栏按钮**(`options.customButtonJson` → publish 生成 FORM 按钮)与按钮执行。
|
|
101
101
|
- **工具**:`form.get`、`form.draft`、`form.publish`、`configure_form`、`form.runButton`
|
|
102
|
+
- **按钮脚本(JS)读写**:`form.get --buttonScripts` 回 `scriptData`/sha;写用 `form.publish --buttonScripts [{code,script}]`(只改已有按钮脚本),或在 `buttons[]` 里给 `script`。**视图(列表)本身没有 JS 面**(`h_biz_query` 无脚本列);脚本只有三处:表单在线JS、按钮JS、自定义页面 JS。
|
|
102
103
|
- **recipe**:`omc describe configure_form --example publish-default-form`
|
|
103
104
|
- **边界 / 陷阱**:默认表单已存在,用 `configure_form`/`form.draft(rebuild)`/`form.publish(rebuild:true)` 改它,不要新建;改字段形状后刷新表单一调用 `form.publish rebuild:true`。
|
|
104
105
|
- **每个模型交付前其默认表单必须已发布**:只建草稿=用户无法录入/查看。用 `configure_form` 或 `form.draft(rebuild:true)`+`form.publish` 发布。
|
|
@@ -110,6 +111,7 @@ only the section for the capability you need —
|
|
|
110
111
|
|
|
111
112
|
- **能做哪些事**:视图表头列、排序、筛选条件生命周期(create/configure/delete)。
|
|
112
113
|
- **工具**:`listview.get`、`listview.list`、`listview.create`、`listview.configure`、`listview.delete`
|
|
114
|
+
- **按钮脚本(JS)读写**:`listview.get --buttonCode <code>`(或 `--buttons`)直接回 `scriptData`/`scriptBytes`/`scriptSha256`,不用翻 artifact;写用 `listview.configure --buttonScripts [{code,script}]` 只改脚本(保留其它字段),或在 `buttons[]` 里用 `script` 简写(= `actionConfig.scriptData`)。
|
|
113
115
|
- **recipe**:`omc describe listview.configure --example configure-default-list`
|
|
114
116
|
- **边界 / 陷阱**:默认视图已存在,`listview.configure` 省略 code 即配置默认视图。**按字段分组(queryLayer)未取证**——别猜写。
|
|
115
117
|
|