@stalfh233/omc-cli 0.4.15 → 0.4.16
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/LICENSE +21 -21
- package/README.md +297 -297
- package/coverage/m1-coverage-manifest-v1.json +1278 -1277
- package/dist/args.js +0 -0
- package/dist/artifacts.js +0 -0
- package/dist/bench/call-face-eval.js +0 -0
- package/dist/bench/context-attribution.js +0 -0
- package/dist/bench/discovery-cost-eval.js +0 -0
- package/dist/bench/driver.js +0 -0
- package/dist/bench/evals.js +0 -0
- package/dist/bench/fake-http-server.js +0 -0
- package/dist/bench/instrument.js +0 -0
- package/dist/bench/intent-face-eval.js +40 -40
- package/dist/bench/run.js +0 -0
- package/dist/bench/scenario.js +0 -0
- package/dist/bench/types.js +0 -0
- package/dist/bi-wire.js +0 -0
- package/dist/bizservice-config.js +38 -13
- package/dist/call.js +0 -0
- package/dist/capability-absences.js +0 -0
- package/dist/capability-overview.js +2 -2
- package/dist/capability-shape.js +0 -0
- package/dist/cli-contract.js +0 -0
- package/dist/cli-output.js +41 -15
- package/dist/cli.js +22 -2
- package/dist/code-rules.js +0 -0
- package/dist/command-transport.js +0 -0
- package/dist/config-store.js +0 -0
- package/dist/context.js +0 -0
- package/dist/contract-consistency.js +0 -0
- package/dist/contract-resources.js +0 -0
- package/dist/coverage-consistency.js +0 -0
- package/dist/coverage-registry.js +0 -0
- package/dist/coverage.js +0 -0
- package/dist/data-list-filter.js +0 -0
- package/dist/discovery.js +0 -0
- package/dist/endpoints.js +2 -0
- package/dist/environment-policy.js +0 -0
- package/dist/execution-metadata.js +4 -4
- package/dist/fake/app.js +0 -0
- package/dist/fake/b2-registration.js +26 -0
- package/dist/fake/businessrule.js +0 -0
- package/dist/fake/datarule.js +0 -0
- package/dist/fake/dictionary.js +0 -0
- package/dist/fake/environment.js +0 -0
- package/dist/fake/field.js +0 -0
- package/dist/fake/form.js +0 -0
- package/dist/fake/index.js +0 -0
- package/dist/fake/list-view.js +0 -0
- package/dist/fake/model.js +0 -0
- package/dist/fake/online-js.js +0 -0
- package/dist/fake/report.js +0 -0
- package/dist/fake/routes.js +0 -0
- package/dist/fake/rule-lifecycle.js +0 -0
- package/dist/fake/runtime-data.js +0 -0
- package/dist/fake/state.js +0 -0
- package/dist/fake/workflow.js +0 -0
- package/dist/field-change.js +0 -0
- package/dist/field-families.js +0 -0
- package/dist/form-layout.js +0 -0
- package/dist/form-support.js +12 -2
- package/dist/goal-routes.js +0 -0
- package/dist/governed-execution.js +0 -0
- package/dist/human-summary.js +0 -0
- package/dist/identity.js +0 -0
- package/dist/intent/baseline.js +0 -0
- package/dist/intent/capabilities/bizservice.js +0 -0
- package/dist/intent/capabilities/businessrule.js +0 -0
- package/dist/intent/capabilities/datarule.js +0 -0
- package/dist/intent/capabilities/field.js +0 -0
- package/dist/intent/capabilities/form.js +0 -0
- package/dist/intent/capabilities/index.js +0 -0
- package/dist/intent/capabilities/listview.js +0 -0
- package/dist/intent/capabilities/model.js +0 -0
- package/dist/intent/capabilities/onlinejs.js +0 -0
- package/dist/intent/capabilities/report.js +0 -0
- package/dist/intent/capabilities/workflow.js +0 -0
- package/dist/intent/capability.js +0 -0
- package/dist/intent/cli.js +0 -0
- package/dist/intent/compare.js +0 -0
- package/dist/intent/compiler.js +0 -0
- package/dist/intent/dsl.js +0 -0
- package/dist/intent/plan-file.js +0 -0
- package/dist/intent/readback.js +0 -0
- package/dist/intent/schema.js +0 -0
- package/dist/intent/validation.js +0 -0
- package/dist/intent/yaml.js +0 -0
- package/dist/json-column.js +0 -0
- package/dist/lanes/app-contract.js +0 -0
- package/dist/lanes/app-coverage.js +0 -0
- package/dist/lanes/app.js +0 -0
- package/dist/lanes/apply-changes.js +14 -1
- package/dist/lanes/b2-registration-contract.js +5 -0
- package/dist/lanes/b2-registration-coverage.js +1 -0
- package/dist/lanes/b2-registration.js +55 -6
- package/dist/lanes/businessrule-contract.js +0 -0
- package/dist/lanes/businessrule-coverage.js +0 -0
- package/dist/lanes/businessrule.js +0 -0
- package/dist/lanes/contract-support.js +0 -0
- package/dist/lanes/coverage-declaration.js +0 -0
- package/dist/lanes/datarule-contract.js +0 -0
- package/dist/lanes/datarule-coverage.js +0 -0
- package/dist/lanes/datarule-protocol.js +0 -0
- package/dist/lanes/datarule.js +0 -0
- package/dist/lanes/dictionary-contract.js +0 -0
- package/dist/lanes/dictionary-coverage.js +0 -0
- package/dist/lanes/dictionary.js +0 -0
- package/dist/lanes/environment-contract.js +0 -0
- package/dist/lanes/environment-coverage.js +0 -0
- package/dist/lanes/environment.js +0 -0
- package/dist/lanes/field-contract.js +3 -2
- package/dist/lanes/field-coverage.js +0 -0
- package/dist/lanes/field.js +268 -172
- package/dist/lanes/form-contract.js +2 -2
- package/dist/lanes/form-coverage.js +0 -0
- package/dist/lanes/form.js +4 -0
- package/dist/lanes/lane-ids.js +0 -0
- package/dist/lanes/list-view-contract.js +0 -0
- package/dist/lanes/list-view-coverage.js +0 -0
- package/dist/lanes/list-view-shapes.js +0 -0
- package/dist/lanes/list-view.js +0 -0
- package/dist/lanes/meta-contract.js +0 -0
- package/dist/lanes/meta.js +11 -1
- package/dist/lanes/model-contract.js +0 -0
- package/dist/lanes/model-coverage.js +0 -0
- package/dist/lanes/model.js +1 -1
- package/dist/lanes/online-js-contract.js +0 -0
- package/dist/lanes/online-js-coverage.js +0 -0
- package/dist/lanes/online-js.js +0 -0
- package/dist/lanes/report-contract.js +0 -0
- package/dist/lanes/report-coverage.js +0 -0
- package/dist/lanes/report.js +0 -0
- package/dist/lanes/rule-graph.js +0 -0
- package/dist/lanes/rule-lifecycle-contract.js +0 -0
- package/dist/lanes/rule-lifecycle-coverage.js +0 -0
- package/dist/lanes/rule-lifecycle.js +0 -0
- package/dist/lanes/runtime-data-contract.js +0 -0
- package/dist/lanes/runtime-data-coverage.js +0 -0
- package/dist/lanes/runtime-data.js +0 -0
- package/dist/lanes/workflow-contract.js +0 -0
- package/dist/lanes/workflow-coverage.js +0 -0
- package/dist/lanes/workflow.js +2 -0
- package/dist/omc-core-lazy.js +6 -0
- package/dist/online-js-layout.js +0 -0
- package/dist/online-js-source.js +28 -28
- package/dist/package-tool.js +0 -0
- package/dist/package.js +0 -0
- package/dist/plan.js +11 -1
- package/dist/read.js +0 -0
- package/dist/redact.js +0 -0
- package/dist/rule-support.js +0 -0
- package/dist/runtime-support.js +0 -0
- package/dist/service-doctor.js +28 -0
- package/dist/session-manager.js +0 -0
- package/dist/session.js +38 -12
- package/dist/skills.js +0 -0
- package/dist/sqlite-warning-filter.js +27 -0
- package/dist/support.js +0 -0
- package/dist/tool-types.js +0 -0
- package/dist/tools.js +0 -0
- package/dist/usage-log.js +4 -0
- package/dist/wire.js +0 -0
- package/dist/workflow-support.js +0 -0
- package/dist/write-lease.js +0 -0
- package/dist/write-lock.js +0 -0
- package/dist/write.js +24 -2
- package/dist/zip.js +0 -0
- package/docs/tool-surface-map.md +42 -42
- package/package.json +71 -71
- package/skills/omc-acceptance-criteria.md +47 -47
- package/skills/omc-business-configuration.md +258 -257
- package/skills/omc-capabilities.md +197 -197
- package/skills/omc-capability-scouting.md +55 -55
- package/skills/omc-config-draft-review.md +171 -171
- package/skills/omc-five-piece-flow.md +35 -35
- package/skills/omc-glossary.md +86 -86
- package/skills/omc-refusals.md +110 -110
- package/skills/omc-requirement-analysis.md +161 -161
- package/skills/omc-requirement-vocabulary.md +51 -51
- package/skills/omc-start-here.md +82 -82
- package/skills/omc-tool-selection.md +119 -119
- package/skills/omc-write-hazards.md +88 -87
- package/dist/approval-token.js +0 -102
- package/dist/server.js +0 -92
|
@@ -1,87 +1,88 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: omc-write-hazards
|
|
3
|
-
description: 分发版 CLI 的底层写面安全形状。配置表单、视图、按钮、子表、关联、规则或在线 JS 时使用;CLI dry-lint 是主防线,浏览器不是常规验收步骤。
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# CLI 写面安全形状
|
|
7
|
-
|
|
8
|
-
本技能随 `omc-cli` 包分发,可用:
|
|
9
|
-
|
|
10
|
-
```bash
|
|
11
|
-
omc help omc-write-hazards
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
优先依赖 CLI 的 schema、contract、dry-lint、writable/生产确认和权威读回。浏览器只在用户报告明确的 UI 回归时使用。
|
|
15
|
-
|
|
16
|
-
## 表单与 Sheet201
|
|
17
|
-
|
|
18
|
-
- 控件 `options` 必须是 **object**,不能是 JSON 字符串。8.6.34 渲染器会写 `options.name_i18n`;字符串会抛 `Cannot create property 'name_i18n' on string` 并白屏。
|
|
19
|
-
- 子表父控件是 `type: 201`,业务子列放 `columns`;子列 key 不得进入 `viewJson`。
|
|
20
|
-
- 调用方显式传 `draftAttributesJson`/`draftViewJson` 时,CLI 会在写前拒绝字符串 options、非字符串 viewJson 单元、子列混入 viewJson。
|
|
21
|
-
- `form.draft rebuild:true` 会重建布局,但会保留已存在的非结构性控件 options;Sheet201 columns 以最新 subSchema 为准。
|
|
22
|
-
- 空布局 `[]`/`{}` 默认拒绝;只有显式 `allowEmptyLayout:true` 才能清空。
|
|
23
|
-
|
|
24
|
-
## 模型字段与关联
|
|
25
|
-
|
|
26
|
-
- **code 定位写前先核在册**:`field.update` / `rule.node.update` 等按 code 定位的精确写,先 `model.inspect` / `field.list` 确认 code 在册;缺码即停并报"目标不存在 + 当前字段清单"——**别凭用户措辞假定 code 正确**(用户可能记错字段名),**也别擅自替换成语义相近的字段**(改错对象比不改更糟)。缺码 `field.update` 本就拒 301001,但写前先核能省一轮失败往返,并在草稿阶段就把"字段不存在"与"需求理解偏差"分清。
|
|
27
|
-
- **改名核对以字段读为准**:`field.update` 改名后,`model.inspect` 聚合面的 `fields[].name` 可能仍是旧值(发布态快照/聚合缓存);核对显示名、options 一律用 `field.list` / `field.get` 或 `form.publish` 回读——两处读数不一致时以字段读为准,别误判"写入没生效"而重复写。
|
|
28
|
-
- `propertyEmpty` 默认 `false`。模型层保持可空;必填、只读、可见性在表单和流程节点配置。
|
|
29
|
-
- 无映射关联单选 `WORK_SHEET` 使用 `mappings: ""`,不要用 `"[]"`。`"[]"` 是非空字符串,设计器会显示“已设置”,但弹窗没有映射行。
|
|
30
|
-
- 有映射时传非空 JSON 数组字符串,并在 draft/published 两面读回。
|
|
31
|
-
- **运行时写关联值是另一种形状**(`data.save`/`data.update` 的 formData):单选关联(`propertyType 9`)传 **id 字符串或 `{ id }`**,传**数组会稳定 50000**;多选关联(11)才用数组;**人员/组织(50/51/60/61)传 `[{"id","type","name"}]`,`type` 必填**(缺它稳定 50000)——`type` 是 UnitType 索引:**用户=3、部门=1**。用错形状只会拿到一个不透明的 50000。
|
|
32
|
-
- **`mappings` 由表单运行时在用户选择时套用**,服务端 `data.save` **不会**自动回填映射目标字段。API 侧要自己写被映射字段,否则读回为空——别把它误判成“映射配置失败”。
|
|
33
|
-
- **运行时写值的最小形状**(组合出错只会得到一个不透明的 `50000`/NPE,逐一最小化再合):单选关联(9) → id 字符串 `"<id>"`;**人员/组织(50/51/60/61) → `[{"id":"<id>","type":3,"name":"<名>"}]`,`type` 必填且为整数**(缺/非整数 → `BizField.parseFromClient` 拆箱 NPE→50000;用户=3、部门=1);日期(3) → 字符串 `"2026-09-30 10:00:00"`;子表(8) → 行对象数组 `[{"<子列code>":<值>, ...}]`。先各写一条最小行通过,再组合。
|
|
34
|
-
- **DATE(3) 字段的 `options.format` 必须是平台大写枚举**(`YYYY-MM-DD`、`YYYY-MM-DD HH:mm:ss`、`YYYY年MM月DD日`…),**不是 Java 的 `yyyy-MM-dd`**:平台 `DateBizField` 按字面比较,Java 写法能创建字段但**首次运行时 save 稳定 `50000`(`不支持的format`)**(2026-09-30 R5 实测)。`field.create`/`intent` 会就地拒绝非枚举值。
|
|
35
|
-
|
|
36
|
-
## 视图
|
|
37
|
-
|
|
38
|
-
- `conditions[].visible` 未传时 CLI 默认 `true`;只有需求明确隐藏时传 `false`。
|
|
39
|
-
- `fieldCodes` 不要加入系统字段。平台可能在最终查询面保留默认系统列,那不是 agent 配置的业务列。
|
|
40
|
-
- 新模型先配列表视图再依赖 `data.list`。部分 8.6.26 读面在没有 query/view 配置时会把业务列投影为 null;单行核对可用 `data.load`。
|
|
41
|
-
- **默认列表视图不能单独 `listview.delete`**(平台回 `303025 列表是默认列表`);要清掉它就删模型(`model.delete` 级联)。清理时别把它当失败。
|
|
42
|
-
|
|
43
|
-
## 按钮
|
|
44
|
-
|
|
45
|
-
- 列表按钮用 `listview.configure` 的 `buttons`(传 `code`/`name`/`useLocation`/`bindAction`/`targetModel`/`targetForm`…);**不要因为没找到就叫「CLI 无列表按钮能力」**(dogfood 实测该误判会假阻塞整条需求)。配完 `listview.get --full` 回读 `customButtons`。
|
|
46
|
-
- `BUSINESS_RULE` 按钮需要 target rule 和 actionConfig 对象。
|
|
47
|
-
- `FLOW`/`FORM`/`ADD_DATA` 的 actionConfig 必须有 `configureList: []`;FLOW 还需要 `WorkflowListData[0].workflowCode` 与 `schemaCodeVal`。CLI 只给 FLOW 的 targetCode 时会自动展开。
|
|
48
|
-
- `WITH_VIEW` 是打开表单;`WITH_OUT_VIEW` 才是直接执行。行级按钮要用 `useLocation: ROW` 才能拿到当前行上下文。
|
|
49
|
-
|
|
50
|
-
## 子表
|
|
51
|
-
|
|
52
|
-
- 一个主表可以有多个 `CHILD_TABLE`,子表不能嵌套 `CHILD_TABLE`。
|
|
53
|
-
- 子列是子表字段 code 对应 schema 上的普通字段:
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
|
|
57
|
-
omc call field.
|
|
58
|
-
omc call field.
|
|
59
|
-
omc call field.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
-
|
|
70
|
-
-
|
|
71
|
-
|
|
72
|
-
-
|
|
73
|
-
-
|
|
74
|
-
- `
|
|
75
|
-
-
|
|
76
|
-
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
-
|
|
82
|
-
-
|
|
83
|
-
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
1
|
+
---
|
|
2
|
+
name: omc-write-hazards
|
|
3
|
+
description: 分发版 CLI 的底层写面安全形状。配置表单、视图、按钮、子表、关联、规则或在线 JS 时使用;CLI dry-lint 是主防线,浏览器不是常规验收步骤。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# CLI 写面安全形状
|
|
7
|
+
|
|
8
|
+
本技能随 `omc-cli` 包分发,可用:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
omc help omc-write-hazards
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
优先依赖 CLI 的 schema、contract、dry-lint、writable/生产确认和权威读回。浏览器只在用户报告明确的 UI 回归时使用。
|
|
15
|
+
|
|
16
|
+
## 表单与 Sheet201
|
|
17
|
+
|
|
18
|
+
- 控件 `options` 必须是 **object**,不能是 JSON 字符串。8.6.34 渲染器会写 `options.name_i18n`;字符串会抛 `Cannot create property 'name_i18n' on string` 并白屏。
|
|
19
|
+
- 子表父控件是 `type: 201`,业务子列放 `columns`;子列 key 不得进入 `viewJson`。
|
|
20
|
+
- 调用方显式传 `draftAttributesJson`/`draftViewJson` 时,CLI 会在写前拒绝字符串 options、非字符串 viewJson 单元、子列混入 viewJson。
|
|
21
|
+
- `form.draft rebuild:true` 会重建布局,但会保留已存在的非结构性控件 options;Sheet201 columns 以最新 subSchema 为准。
|
|
22
|
+
- 空布局 `[]`/`{}` 默认拒绝;只有显式 `allowEmptyLayout:true` 才能清空。
|
|
23
|
+
|
|
24
|
+
## 模型字段与关联
|
|
25
|
+
|
|
26
|
+
- **code 定位写前先核在册**:`field.update` / `rule.node.update` 等按 code 定位的精确写,先 `model.inspect` / `field.list` 确认 code 在册;缺码即停并报"目标不存在 + 当前字段清单"——**别凭用户措辞假定 code 正确**(用户可能记错字段名),**也别擅自替换成语义相近的字段**(改错对象比不改更糟)。缺码 `field.update` 本就拒 301001,但写前先核能省一轮失败往返,并在草稿阶段就把"字段不存在"与"需求理解偏差"分清。
|
|
27
|
+
- **改名核对以字段读为准**:`field.update` 改名后,`model.inspect` 聚合面的 `fields[].name` 可能仍是旧值(发布态快照/聚合缓存);核对显示名、options 一律用 `field.list` / `field.get` 或 `form.publish` 回读——两处读数不一致时以字段读为准,别误判"写入没生效"而重复写。
|
|
28
|
+
- `propertyEmpty` 默认 `false`。模型层保持可空;必填、只读、可见性在表单和流程节点配置。
|
|
29
|
+
- 无映射关联单选 `WORK_SHEET` 使用 `mappings: ""`,不要用 `"[]"`。`"[]"` 是非空字符串,设计器会显示“已设置”,但弹窗没有映射行。
|
|
30
|
+
- 有映射时传非空 JSON 数组字符串,并在 draft/published 两面读回。
|
|
31
|
+
- **运行时写关联值是另一种形状**(`data.save`/`data.update` 的 formData):单选关联(`propertyType 9`)传 **id 字符串或 `{ id }`**,传**数组会稳定 50000**;多选关联(11)才用数组;**人员/组织(50/51/60/61)传 `[{"id","type","name"}]`,`type` 必填**(缺它稳定 50000)——`type` 是 UnitType 索引:**用户=3、部门=1**。用错形状只会拿到一个不透明的 50000。
|
|
32
|
+
- **`mappings` 由表单运行时在用户选择时套用**,服务端 `data.save` **不会**自动回填映射目标字段。API 侧要自己写被映射字段,否则读回为空——别把它误判成“映射配置失败”。
|
|
33
|
+
- **运行时写值的最小形状**(组合出错只会得到一个不透明的 `50000`/NPE,逐一最小化再合):单选关联(9) → id 字符串 `"<id>"`;**人员/组织(50/51/60/61) → `[{"id":"<id>","type":3,"name":"<名>"}]`,`type` 必填且为整数**(缺/非整数 → `BizField.parseFromClient` 拆箱 NPE→50000;用户=3、部门=1);日期(3) → 字符串 `"2026-09-30 10:00:00"`;子表(8) → 行对象数组 `[{"<子列code>":<值>, ...}]`。先各写一条最小行通过,再组合。
|
|
34
|
+
- **DATE(3) 字段的 `options.format` 必须是平台大写枚举**(`YYYY-MM-DD`、`YYYY-MM-DD HH:mm:ss`、`YYYY年MM月DD日`…),**不是 Java 的 `yyyy-MM-dd`**:平台 `DateBizField` 按字面比较,Java 写法能创建字段但**首次运行时 save 稳定 `50000`(`不支持的format`)**(2026-09-30 R5 实测)。`field.create`/`intent` 会就地拒绝非枚举值。
|
|
35
|
+
|
|
36
|
+
## 视图
|
|
37
|
+
|
|
38
|
+
- `conditions[].visible` 未传时 CLI 默认 `true`;只有需求明确隐藏时传 `false`。
|
|
39
|
+
- `fieldCodes` 不要加入系统字段。平台可能在最终查询面保留默认系统列,那不是 agent 配置的业务列。
|
|
40
|
+
- 新模型先配列表视图再依赖 `data.list`。部分 8.6.26 读面在没有 query/view 配置时会把业务列投影为 null;单行核对可用 `data.load`。
|
|
41
|
+
- **默认列表视图不能单独 `listview.delete`**(平台回 `303025 列表是默认列表`);要清掉它就删模型(`model.delete` 级联)。清理时别把它当失败。
|
|
42
|
+
|
|
43
|
+
## 按钮
|
|
44
|
+
|
|
45
|
+
- 列表按钮用 `listview.configure` 的 `buttons`(传 `code`/`name`/`useLocation`/`bindAction`/`targetModel`/`targetForm`…);**不要因为没找到就叫「CLI 无列表按钮能力」**(dogfood 实测该误判会假阻塞整条需求)。配完 `listview.get --full` 回读 `customButtons`。
|
|
46
|
+
- `BUSINESS_RULE` 按钮需要 target rule 和 actionConfig 对象。
|
|
47
|
+
- `FLOW`/`FORM`/`ADD_DATA` 的 actionConfig 必须有 `configureList: []`;FLOW 还需要 `WorkflowListData[0].workflowCode` 与 `schemaCodeVal`。CLI 只给 FLOW 的 targetCode 时会自动展开。
|
|
48
|
+
- `WITH_VIEW` 是打开表单;`WITH_OUT_VIEW` 才是直接执行。行级按钮要用 `useLocation: ROW` 才能拿到当前行上下文。
|
|
49
|
+
|
|
50
|
+
## 子表
|
|
51
|
+
|
|
52
|
+
- 一个主表可以有多个 `CHILD_TABLE`,子表不能嵌套 `CHILD_TABLE`。
|
|
53
|
+
- 子列是子表字段 code 对应 schema 上的普通字段:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# --env 在单环境项目(.omc-env 在位)可省;下例显式写出仅为多环境演示
|
|
57
|
+
omc call field.create --env local --args '{"schemaCode":"expense_items","code":"item_note","name":"明细备注","propertyType":"LONG_TEXT"}'
|
|
58
|
+
omc call field.update --env local --args '{"schemaCode":"expense_items","code":"item_note","name":"备注"}'
|
|
59
|
+
omc call field.delete --env local --args '{"schemaCode":"expense_items","code":"item_note"}'
|
|
60
|
+
omc call field.publish --env local --args '{"schemaCode":"expense"}'
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
父 `CHILD_TABLE` 行不接受 `options/choices/subFields` 更新;子列直接走上面的 CRUD 面。
|
|
64
|
+
|
|
65
|
+
## 规则与业务集成
|
|
66
|
+
|
|
67
|
+
- 规则节点缺 `nodeName` 时 CLI 按平台 `RuleNodeType` 补默认名并给 warning;业务节点优先显式命名。
|
|
68
|
+
- BIZ_ACTION 先 `service.get` 读取现存方法参数面,再写 methodMapping。PROPERTY 输入使用 `codeType: 1`、`bizCode` 为业务字段 code、`propertyName` 为显示名、`serviceMethodParameterCode` 命中方法参数。
|
|
69
|
+
- **`codeType:1` 映射的 `bizCode` 必须是该模型上**已创建且已发布**的字段 code**:指向不存在/未发布的字段时,平台 publish 只回一个**无明细的 `550018 保存规则失败,规则存在异常`**(真因只经设计器「检测」/`POST /api/app/businessrule/node_run_map → notExistPropertyObjectList` 可见)。`rule.save` 现已本地前置校验并拒绝(`rule-graph-bizaction-mapping-field-missing`);改字段族类型也要同步映射。
|
|
70
|
+
- **把 BIZ_ACTION 挂到预置数据事件规则(`Create`/`Update`)上时**:映射的 `bizCode` 数据项必须真实存在于该模型,否则发布被拒 `550010 业务规则使用的数据项不存在`(典型:REST 短文本入参要映射一个关联字段——先在模型加一个短文本「快照」字段承接,再映射它)。若预置规则仍不接受,改挂**自定义按钮触发规则**(`customTrigger:true`)或 `Update` 规则,更稳;不要因此判定“平台不支持 BIZ_ACTION”。
|
|
71
|
+
- **BIZ_ACTION 的 `outputMappings` 按“响应 JSON 根级路径”取值**(源码 `MapOps.toMapByBizServiceJson`/`convertJsonObject`:keyMap 以 `serviceMethodParameterCode` 为键,与响应体**根级**各键匹配)。所以 `serviceMethodParameterCode` 必须等于 REST 响应**根级**的键名:
|
|
72
|
+
- **扩展接口若返回信封 `ResponseResult{errcode,data:{x}}`,平铺出参 `x` 在根级找不到 → 静默空回写**(真机 2026-10-01 实证:`h_log_biz_service` 显示 invoke SUCCESS、`data.amount:300`,目标字段仍空)。**正解:扩展接口在根级返回业务负载**(`GET` handler 直接 `return Map.of("amount", n)`,不分信封),出参 code=根键 `amount`。
|
|
73
|
+
- 也**不要**试图用输出参数的 `subParameters` 下沉取值:实测把出参声明为父 `data`+`subParameters[value]` 会把整个 `{value:99}` 子对象赋给标量字段,触发 `数据项【x】的值是未知的类型:com.alibaba.JSONObject`(50000)。嵌套出参结构只用于 OBJECT/子表类的整体赋值,不能用来“取 `data.x` 这个标量”。
|
|
74
|
+
- 对照:平台自带 `skb_risk_service` 出参平铺 `riskLevel` ↔ 响应根级键;真机三态验证:平铺根级→回写 42 ✓;信封+平铺出参→不回写 ✗;信封+嵌套出参→50000 ✗。
|
|
75
|
+
- `method.test` 的 `externalSuccess:true` **不代表**回写成功——回写要另看目标字段实际值(`data.submit` 读回)。
|
|
76
|
+
- 流程结束新增目标表单必须运行验证;配置发布不等于映射成功。检查目标记录的每个字段。
|
|
77
|
+
- **`model.delete` 不物理清理 `h_business_rule`**:预置规则(Create/Update/GetList/Load/Delete/Available/Cancel)与历史版本行会残留在规则表(`rule.list` 对已删 schema 也读不到,属物理孤儿)。平台写面没有“按 schema 物理清规则”的手段;验收“零残留”以**可见面**为准(`model.list`/`rule.list`/`service.get`/列表/按钮),物理行需 DB 侧清理。清理顺序让规则先于模型删能减少、但不消除预置规则行。
|
|
78
|
+
|
|
79
|
+
## 在线 JS
|
|
80
|
+
|
|
81
|
+
- **本 CLI 只负责发布,不负责写 JS**。脚本内容的创作与增量合并不在本 CLI:若当前项目自带在线 JS 编写规范(例如其 `.cpdocs`/技能或项目文档),遵循它;否则向用户/设计者索取已定稿 `source`。不要在 omc 技能里手写/臆造 JS 逻辑。
|
|
82
|
+
- 经 omc 发布时用 `onlinejs.compose`:把已定稿的 `source` 组合进平台 skeleton 的 `customScript`,再 `publish`,并回读 `publishedHtmlJson` + SHA-256。不要用裸脚本覆盖平台 skeleton。
|
|
83
|
+
- **`replace:true` 对裸代码被拒是设计**:`replace` 必须传完整的 `(function(form){…})`,否则会写坏脚本槽让表单空白;同样,`merge` 传完整带外层的脚本也会被拒(避免嵌套丢内容)。远端 `customScript` 为空时,定稿脚本带外层 + `replace:true` 即可,不要把它当成缺陷。
|
|
84
|
+
- 源码不能有 import/export,事件名必须在支持集合内;compose 后读回 draft/published HTML,浏览器不是默认步骤。
|
|
85
|
+
|
|
86
|
+
## 固定验收顺序
|
|
87
|
+
|
|
88
|
+
执行阶段以 `omc-five-piece-flow` 为准;写面安全只额外强调两点:写前先 `commands/describe/contract` + dry-run 静态校验,写后权威 readback,必要时再用 CLI 触发运行验证。
|
package/dist/approval-token.js
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { createHash, createHmac, randomUUID, timingSafeEqual } from "node:crypto";
|
|
2
|
-
export class ApprovalTokenRejectedError extends Error {
|
|
3
|
-
reason;
|
|
4
|
-
constructor(reason) {
|
|
5
|
-
super(`approval token rejected: ${reason}`);
|
|
6
|
-
this.reason = reason;
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
function canonical(value) {
|
|
10
|
-
return JSON.stringify(sort(value));
|
|
11
|
-
}
|
|
12
|
-
function sort(value) {
|
|
13
|
-
if (Array.isArray(value))
|
|
14
|
-
return value.map(sort);
|
|
15
|
-
if (value && typeof value === "object")
|
|
16
|
-
return Object.fromEntries(Object.entries(value)
|
|
17
|
-
.sort(([a], [b]) => a.localeCompare(b))
|
|
18
|
-
.map(([k, v]) => [k, sort(v)]));
|
|
19
|
-
return value;
|
|
20
|
-
}
|
|
21
|
-
export function packageFingerprint(plan) {
|
|
22
|
-
return createHash("sha256")
|
|
23
|
-
.update(canonical({
|
|
24
|
-
environment: plan.environment,
|
|
25
|
-
environmentFingerprint: plan.environmentFingerprint,
|
|
26
|
-
actions: plan.actions.map((a) => ({ tool: a.tool, args: sort(a.args) })),
|
|
27
|
-
}))
|
|
28
|
-
.digest("hex");
|
|
29
|
-
}
|
|
30
|
-
export class ApprovalTokenService {
|
|
31
|
-
key;
|
|
32
|
-
ttlMs;
|
|
33
|
-
now;
|
|
34
|
-
used = new Set();
|
|
35
|
-
constructor(options) {
|
|
36
|
-
const key = Buffer.from(options.key, "base64");
|
|
37
|
-
if (key.length !== 32)
|
|
38
|
-
throw new Error("approval token key must be exactly 32 bytes (base64).");
|
|
39
|
-
this.key = key;
|
|
40
|
-
this.ttlMs = options.ttlMs ?? 10 * 60 * 1000;
|
|
41
|
-
this.now = options.now ?? Date.now;
|
|
42
|
-
}
|
|
43
|
-
requestApproval(plan) {
|
|
44
|
-
const approvalId = randomUUID();
|
|
45
|
-
const fingerprint = packageFingerprint(plan);
|
|
46
|
-
const issuedAt = this.now();
|
|
47
|
-
const expiresAt = issuedAt + this.ttlMs;
|
|
48
|
-
const payload = canonical({
|
|
49
|
-
v: 1,
|
|
50
|
-
approvalId,
|
|
51
|
-
packageFingerprint: fingerprint,
|
|
52
|
-
issuedAt,
|
|
53
|
-
expiresAt,
|
|
54
|
-
});
|
|
55
|
-
const sig = createHmac("sha256", this.key).update(payload).digest("base64url");
|
|
56
|
-
const token = Buffer.from(payload, "utf8").toString("base64url") + "." + sig;
|
|
57
|
-
return {
|
|
58
|
-
token,
|
|
59
|
-
approvalId,
|
|
60
|
-
packageFingerprint: fingerprint,
|
|
61
|
-
expiresAt: new Date(expiresAt).toISOString(),
|
|
62
|
-
ttlSeconds: Math.round(this.ttlMs / 1000),
|
|
63
|
-
singleUse: true,
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
verify(plan, token) {
|
|
67
|
-
const parts = token.split(".");
|
|
68
|
-
if (parts.length !== 2 || !parts[0] || !parts[1])
|
|
69
|
-
throw new ApprovalTokenRejectedError("token-malformed");
|
|
70
|
-
let payload;
|
|
71
|
-
try {
|
|
72
|
-
payload = Buffer.from(parts[0], "base64url").toString("utf8");
|
|
73
|
-
}
|
|
74
|
-
catch {
|
|
75
|
-
throw new ApprovalTokenRejectedError("token-malformed");
|
|
76
|
-
}
|
|
77
|
-
let decoded;
|
|
78
|
-
try {
|
|
79
|
-
decoded = JSON.parse(payload);
|
|
80
|
-
}
|
|
81
|
-
catch {
|
|
82
|
-
throw new ApprovalTokenRejectedError("token-malformed");
|
|
83
|
-
}
|
|
84
|
-
const expectedSig = createHmac("sha256", this.key).update(payload).digest("base64url");
|
|
85
|
-
if (!timingSafeEqualB64(parts[1], expectedSig))
|
|
86
|
-
throw new ApprovalTokenRejectedError("token-signature-invalid");
|
|
87
|
-
if (this.used.has(String(decoded.approvalId)))
|
|
88
|
-
throw new ApprovalTokenRejectedError("token-already-used");
|
|
89
|
-
if (Number(decoded.expiresAt) <= this.now())
|
|
90
|
-
throw new ApprovalTokenRejectedError("token-expired");
|
|
91
|
-
if (decoded.packageFingerprint !== packageFingerprint(plan))
|
|
92
|
-
throw new ApprovalTokenRejectedError("package-fingerprint-mismatch");
|
|
93
|
-
this.used.add(String(decoded.approvalId));
|
|
94
|
-
return { approvalId: String(decoded.approvalId) };
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
function timingSafeEqualB64(a, b) {
|
|
98
|
-
const ba = Buffer.from(a);
|
|
99
|
-
const bb = Buffer.from(b);
|
|
100
|
-
return ba.length === bb.length && timingSafeEqual(ba, bb);
|
|
101
|
-
}
|
|
102
|
-
//# sourceMappingURL=approval-token.js.map
|
package/dist/server.js
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
-
import { randomBytes } from "node:crypto";
|
|
5
|
-
import { realpathSync } from "node:fs";
|
|
6
|
-
import { fileURLToPath } from "node:url";
|
|
7
|
-
import { buildToolRegistry, makeToolContext } from "./tools.js";
|
|
8
|
-
import { SessionManager } from "./session-manager.js";
|
|
9
|
-
import { redact, redactError } from "./redact.js";
|
|
10
|
-
import { ConfigStore } from "./config-store.js";
|
|
11
|
-
import { contractMarkdown, contractResourceUri, LANE_CONTRACTS, shortToolDescription } from "./contract-resources.js";
|
|
12
|
-
import { listSkills, readSkill } from "./skills.js";
|
|
13
|
-
export function createMcpServer(options = {}) {
|
|
14
|
-
const context = makeToolContext({
|
|
15
|
-
sessions: options.sessions ?? new SessionManager(new ConfigStore()),
|
|
16
|
-
artifactsDir: options.artifactsDir,
|
|
17
|
-
tokenKey: options.tokenKey ?? randomBytes(32).toString("base64"),
|
|
18
|
-
tokenTtlMs: options.tokenTtlMs,
|
|
19
|
-
});
|
|
20
|
-
const secrets = options.secrets ?? (() => []);
|
|
21
|
-
const server = new McpServer({ name: "omc-cli", version: "0.1.0" }, {
|
|
22
|
-
capabilities: { tools: {}, resources: {} },
|
|
23
|
-
instructions: "Governed CloudPivot 8.6 configuration server. Tool schemas are self-describing (field families, relation options, rule nodeGraph, workflow template, formData, and the two-step approval flow). Methodology is served as MCP resources under omc://skills/ (five-piece flow, tool selection, acceptance criteria, requirement analysis, capability scouting, glossary, config draft review); read them with resources/list + resources/read rather than reading repository source. Before ANY write, read omc://skills/omc-config-draft-review: present the design (exact names/codes/types, layout, rule graph, view) as a plain-language draft and get explicit human approval first — the conversational human gate (approval granularity is the enumerated change package; an interrupted package loses its approval and resuming needs a fresh draft and go-ahead). Then: call without approvalToken to preview the plan, approval.request to get a token (the machine gate — it carries no human check), re-call with it. Production environments refuse writes by default (dual-key grant). Deliver ONE closing report per run — never interrupt per step.",
|
|
24
|
-
});
|
|
25
|
-
const register = server.tool.bind(server);
|
|
26
|
-
for (const definition of buildToolRegistry()) {
|
|
27
|
-
register(definition.name, shortToolDescription(definition.name, definition.kind, definition.lane) ?? definition.description, definition.inputSchema, async (args) => {
|
|
28
|
-
try {
|
|
29
|
-
const result = await definition.handler(context, args, definition);
|
|
30
|
-
return { content: [{ type: "text", text: redact(result, secrets()) }] };
|
|
31
|
-
}
|
|
32
|
-
catch (error) {
|
|
33
|
-
const refusal = error.payload;
|
|
34
|
-
if (refusal)
|
|
35
|
-
return { content: [{ type: "text", text: redact(refusal, secrets()) }] };
|
|
36
|
-
const redacted = redactError(error, secrets());
|
|
37
|
-
return { content: [{ type: "text", text: redact({ status: "error", message: redacted.message }, secrets()) }], isError: true };
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
registerSkillResources(server);
|
|
42
|
-
registerContractResources(server);
|
|
43
|
-
return { server, context };
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Serve the package's methodology notes as MCP resources so any client can
|
|
47
|
-
* read them on demand (host-agnostic) instead of the agent globbing the
|
|
48
|
-
* filesystem for them. Each skill file's YAML frontmatter supplies its name
|
|
49
|
-
* and description.
|
|
50
|
-
*/
|
|
51
|
-
function registerSkillResources(server) {
|
|
52
|
-
for (const entry of listSkills()) {
|
|
53
|
-
const document = readSkill(entry.name);
|
|
54
|
-
if (document === undefined)
|
|
55
|
-
continue;
|
|
56
|
-
server.registerResource(entry.name, entry.uri, { title: entry.name, description: entry.description, mimeType: "text/markdown" }, async (uri) => ({ contents: [{ uri: uri.href, mimeType: "text/markdown", text: document.content }] }));
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
function registerContractResources(server) {
|
|
60
|
-
for (const contract of LANE_CONTRACTS) {
|
|
61
|
-
const text = contractMarkdown(contract);
|
|
62
|
-
server.registerResource(`contract-${contract.lane}`, contractResourceUri(contract), {
|
|
63
|
-
title: `${contract.lane} contract`,
|
|
64
|
-
description: `CloudPivot ${contract.family} contract for ${contract.lane}; reference ${contract.referenceVersion}`,
|
|
65
|
-
mimeType: "text/markdown",
|
|
66
|
-
}, async (uri) => ({ contents: [{ uri: uri.href, mimeType: "text/markdown", text }] }));
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
export async function runStdioServer(options = {}) {
|
|
70
|
-
const { server } = createMcpServer(options);
|
|
71
|
-
const transport = new StdioServerTransport();
|
|
72
|
-
await server.connect(transport);
|
|
73
|
-
}
|
|
74
|
-
function isProcessEntryPoint() {
|
|
75
|
-
const entry = process.argv[1];
|
|
76
|
-
if (!entry)
|
|
77
|
-
return false;
|
|
78
|
-
try {
|
|
79
|
-
return realpathSync(entry) === fileURLToPath(import.meta.url);
|
|
80
|
-
}
|
|
81
|
-
catch {
|
|
82
|
-
return false;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
if (isProcessEntryPoint()) {
|
|
86
|
-
runStdioServer().catch((error) => {
|
|
87
|
-
console.error(error instanceof Error ? error.message : String(error));
|
|
88
|
-
process.exit(1);
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
export { buildToolRegistry, makeToolContext };
|
|
92
|
-
//# sourceMappingURL=server.js.map
|