@stalfh233/omc-cli 0.4.31 → 0.4.33
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/dist/execution-metadata.js +49 -27
- package/dist/goal-routes.js +1 -2
- package/dist/human-summary.js +4 -1
- package/dist/intent/cli.js +1 -1
- package/dist/intent/schema.js +15 -14
- package/dist/lanes/model.js +25 -4
- package/dist/lanes/runtime-data.js +20 -0
- package/package.json +2 -2
- package/skills/omc-business-configuration.md +10 -10
- package/skills/omc-start-here.md +1 -1
|
@@ -54,8 +54,8 @@ const childTable = Object.freeze({
|
|
|
54
54
|
name: "明细",
|
|
55
55
|
propertyType: "CHILD_TABLE",
|
|
56
56
|
subFields: [
|
|
57
|
-
{ code: "
|
|
58
|
-
{ code: "
|
|
57
|
+
{ code: "itemName", name: "名称", propertyType: "SHORT_TEXT" },
|
|
58
|
+
{ code: "itemQty", name: "数量", propertyType: "NUMERICAL", options: { scale: 1 } },
|
|
59
59
|
],
|
|
60
60
|
},
|
|
61
61
|
preconditions: ["目标模型已存在;父字段 code 和子列 codes 尚未存在;父模型字段发布后再配置表单"],
|
|
@@ -65,7 +65,7 @@ const childTable = Object.freeze({
|
|
|
65
65
|
],
|
|
66
66
|
priorToolReferences: [],
|
|
67
67
|
dryRun: { supported: true, command: `omc call field.create --env ${environment} --dry-run --args '<recipe.args>'`, notes: "dry-run 验证 subFields 并跳过环境写入" },
|
|
68
|
-
expectedReadback: { readback: { field: { code: "items", propertyType: "CHILD_TABLE", subSchema: { properties: ["
|
|
68
|
+
expectedReadback: { readback: { field: { code: "items", propertyType: "CHILD_TABLE", subSchema: { properties: ["itemName", "itemQty"] } } }, next: ["field.publish", "form.draft", "form.publish"] },
|
|
69
69
|
refusalRecovery: [
|
|
70
70
|
{ code: "capability-out-of-coverage", action: "补齐 subFields,并移除不支持的嵌套/高级子列族" },
|
|
71
71
|
{ code: "child-table-update-unsupported", action: "子列用 schemaCode=items 的 field.create/update;不要向父字段 update 传 options/choices/subFields" },
|
|
@@ -294,9 +294,9 @@ const workflowTemplateUpdate = Object.freeze({
|
|
|
294
294
|
args: {
|
|
295
295
|
environment,
|
|
296
296
|
schemaCode: model,
|
|
297
|
-
code: "
|
|
297
|
+
code: "applyWorkflow",
|
|
298
298
|
template: {
|
|
299
|
-
workflowCode: "
|
|
299
|
+
workflowCode: "applyWorkflow",
|
|
300
300
|
activities: [
|
|
301
301
|
{ activityCode: "Activity1", activityType: "START", activityName: "开始", i18nMessage: "开始" },
|
|
302
302
|
{ activityCode: "Activity2", activityType: "PARTICIPANT", activityName: "填写申请单", i18nMessage: "填写申请单", participant: "{Originator}" },
|
|
@@ -317,7 +317,7 @@ const workflowTemplateUpdate = Object.freeze({
|
|
|
317
317
|
"**平台 workflow.create 会自动生成含「填写申请单」发起节点的草稿(START → 填写申请单 → 审批 → END);workflow.update 是 merge,不删除既有节点——要严格三节点需在设计器删该节点,或如实接受该发起节点**",
|
|
318
318
|
"**读回 participant 为空是平台模板更新面的已知残留,不要反复 update 试图修正(实测 6 次 update 仍为空);记录该差异并留待设计器核对,一次读回确认即可**",
|
|
319
319
|
],
|
|
320
|
-
priorToolReferences: [{ tool: "workflow.get_draft", args: { environment, workflowCode: "
|
|
320
|
+
priorToolReferences: [{ tool: "workflow.get_draft", args: { environment, workflowCode: "applyWorkflow" }, purpose: "取草稿基线作为 merge 基线(默认面即含 activityCode/name/edges;不要 --full)" }],
|
|
321
321
|
dryRun: { supported: true, command: `omc call workflow.update --env ${environment} --dry-run --args '<recipe.args>'`, notes: "dry-run 校验模板形状,不写入" },
|
|
322
322
|
expectedReadback: { readback: { draftUpdated: true, activities: "<节点数=你数组长度>", edges: "<链路>" }, next: ["用默认 workflow.get_draft 复核 activities/edges(不要 --full)", "workflow.publish", "workflow.bindRule"] },
|
|
323
323
|
refusalRecovery: [
|
|
@@ -336,9 +336,9 @@ const workflowConditionalBranch = Object.freeze({
|
|
|
336
336
|
args: {
|
|
337
337
|
environment,
|
|
338
338
|
schemaCode: model,
|
|
339
|
-
code: "
|
|
339
|
+
code: "applyWorkflow",
|
|
340
340
|
template: {
|
|
341
|
-
workflowCode: "
|
|
341
|
+
workflowCode: "applyWorkflow",
|
|
342
342
|
activities: [
|
|
343
343
|
{ activityCode: "A1", activityType: "START", activityName: "开始", i18nMessage: "开始" },
|
|
344
344
|
{ activityCode: "A2", activityType: "PARTICIPANT", activityName: "填写申请单", i18nMessage: "填写申请单", participant: "{Originator}" },
|
|
@@ -361,7 +361,7 @@ const workflowConditionalBranch = Object.freeze({
|
|
|
361
361
|
"分支可只写 activities + routes:工具按数组顺序补全到 END 的连线(`con` 兜底线仍需你显式给,除非它就是主链)。",
|
|
362
362
|
],
|
|
363
363
|
priorToolReferences: [
|
|
364
|
-
{ tool: "workflow.get_draft", args: { environment, workflowCode: "
|
|
364
|
+
{ tool: "workflow.get_draft", args: { environment, workflowCode: "applyWorkflow" }, purpose: "取草稿基线(apply_workflow 换成真实 workflowCode;默认面即含 activityCode/name/edges,不要 --full)" },
|
|
365
365
|
{ tool: "org.role.list", args: { environment }, purpose: "取真实参与人/角色 code,避免自由文本名解析不到人" },
|
|
366
366
|
],
|
|
367
367
|
dryRun: { supported: true, command: `omc call workflow.update --env ${environment} --dry-run --args '<recipe.args>'`, notes: "dry-run 校验 activities/routes/条件叶,不写入" },
|
|
@@ -512,12 +512,12 @@ const configureModelRecipe = Object.freeze({
|
|
|
512
512
|
args: {
|
|
513
513
|
environment,
|
|
514
514
|
name: "采购申请",
|
|
515
|
-
code: "
|
|
515
|
+
code: "purchaseApply",
|
|
516
516
|
applicationCode: "<applicationCode>",
|
|
517
517
|
fields: [
|
|
518
|
-
{ code: "
|
|
519
|
-
{ code: "
|
|
520
|
-
{ code: "
|
|
518
|
+
{ code: "applyNo", name: "申请编号", propertyType: "SHORT_TEXT" },
|
|
519
|
+
{ code: "applyAmount", name: "申请金额", propertyType: "NUMERICAL", options: { scale: 2 } },
|
|
520
|
+
{ code: "applyNote", name: "申请说明", propertyType: "LONG_TEXT" },
|
|
521
521
|
],
|
|
522
522
|
publishForm: true,
|
|
523
523
|
},
|
|
@@ -528,7 +528,7 @@ const configureModelRecipe = Object.freeze({
|
|
|
528
528
|
],
|
|
529
529
|
priorToolReferences: [{ tool: "discover_environment", args: { environment }, purpose: "取 applicationCode 与已有模型" }],
|
|
530
530
|
dryRun: { supported: true, command: `omc call configure_model --env ${environment} --dry-run --args '<recipe.args>'`, notes: "dry-run 走 schema/语义/治理预检,不创建" },
|
|
531
|
-
expectedReadback: { readback: { modelPresent: true, publishedFields: ["
|
|
531
|
+
expectedReadback: { readback: { modelPresent: true, publishedFields: ["applyNo", "applyAmount", "applyNote"] }, next: ["field.update(表单层条件)", "listview.configure(默认视图已自动存在,直接配置,勿 create)"] },
|
|
532
532
|
refusalRecovery: [{ code: "capability-out-of-coverage", action: "标量族之外改用低层 field.create;NUMERICAL 补 options.scale" }, ...commonRecovery],
|
|
533
533
|
requiresEnvironmentDerivedIds: true,
|
|
534
534
|
prerequisiteData: { source: "discover_environment", fields: ["applicationCode"] },
|
|
@@ -552,7 +552,7 @@ const numericTwoDecimals = Object.freeze({
|
|
|
552
552
|
title: "数值字段(金额,两位小数 / 整数)— scale 必填",
|
|
553
553
|
status: "offline-valid",
|
|
554
554
|
tool: "field.create",
|
|
555
|
-
args: { environment, schemaCode: model, code: "
|
|
555
|
+
args: { environment, schemaCode: model, code: "applyAmount", name: "申请金额", propertyType: "NUMERICAL", options: { scale: 3 } },
|
|
556
556
|
preconditions: ["模型已存在"],
|
|
557
557
|
conditionalRequirements: ["NUMERICAL 必须显式 options.scale:scale = 小数位数(0=整数, 1=1位, 2=2位 … 8=8位);缺 scale 门户显示裸 decimal 且工具会拒绝。"],
|
|
558
558
|
priorToolReferences: [{ tool: "discover_environment", args: { environment }, purpose: "确认目标模型已存在" }],
|
|
@@ -677,7 +677,7 @@ const dataruleRequired = Object.freeze({
|
|
|
677
677
|
args: {
|
|
678
678
|
environment,
|
|
679
679
|
schemaCode: model,
|
|
680
|
-
propertyCode: "
|
|
680
|
+
propertyCode: "leaveReason",
|
|
681
681
|
dataRuleType: 4,
|
|
682
682
|
options: { requiredFormula: '{leave_amount} 大于 {"valueType":"custom","value":5000}' },
|
|
683
683
|
name: "金额超过阈值说明必填",
|
|
@@ -748,7 +748,7 @@ const formConditionalRequired = Object.freeze({
|
|
|
748
748
|
args: {
|
|
749
749
|
environment,
|
|
750
750
|
schemaCode: model,
|
|
751
|
-
code: "
|
|
751
|
+
code: "leaveReason",
|
|
752
752
|
name: "请假事由",
|
|
753
753
|
options: { requiredFormula: '{leave_amount} 大于 {"valueType":"custom","value":5000}' },
|
|
754
754
|
},
|
|
@@ -756,7 +756,7 @@ const formConditionalRequired = Object.freeze({
|
|
|
756
756
|
conditionalRequirements: ["条件串:`{字段} 操作符 [值]`(操作符两侧留空格)。操作符用**中文**(真实设计器产出):等于/不等于/大于/小于/大于等于/小于等于/包含/不包含/包含全部/包含任意一个/等于任意一个/为空/不为空;也接受符号 == != > >= <= Contains IsEmpty。为空/不为空**不带值**(`{reason} 不为空`),其余带 JSON 值 {\"valueType\":...,\"value\":...}。多条件用 ` && `、多组用 ` || `。**不是** eq/gt/isnull——会让门户渲染 SyntaxError"],
|
|
757
757
|
priorToolReferences: [{ tool: "field.list", args: { environment, schemaCode: model, published: true }, purpose: "确认条件字段与目标字段都已发布" }],
|
|
758
758
|
dryRun: { supported: true, command: `omc call field.update --env ${environment} --dry-run --args '<recipe.args>'`, notes: "dry-run 校验字段 options 形状,不写入" },
|
|
759
|
-
expectedReadback: { readback: { field: { code: "
|
|
759
|
+
expectedReadback: { readback: { field: { code: "leaveReason", options: { requiredFormula: "<protocol>" } } }, next: ["(一调)field.update cascadeRepublish:true", "(分步)field.publish → form.draft(rebuild) → form.publish", "(只刷新表单)form.publish rebuild:true"] },
|
|
760
760
|
refusalRecovery: [
|
|
761
761
|
{ code: "capability-out-of-coverage", action: "条件串用中文或符号操作符(等于/大于/不为空 或 == />/IsEmpty);恒必填用字面量 \"true\"" },
|
|
762
762
|
...commonRecovery,
|
|
@@ -768,12 +768,12 @@ const formAlwaysReadonly = Object.freeze({
|
|
|
768
768
|
title: "表单层恒只读字段(字段 options.readonlyFormula:true)",
|
|
769
769
|
status: "offline-valid",
|
|
770
770
|
tool: "field.update",
|
|
771
|
-
args: { environment, schemaCode: model, code: "
|
|
771
|
+
args: { environment, schemaCode: model, code: "approvalLevel", name: "审批级别", options: { readonlyFormula: true } },
|
|
772
772
|
preconditions: ["目标字段已发布"],
|
|
773
773
|
conditionalRequirements: ["恒只读用布尔 readonlyFormula:true;条件只读用 readonlyCondition 条件串"],
|
|
774
774
|
priorToolReferences: [{ tool: "field.list", args: { environment, schemaCode: model, published: true }, purpose: "确认字段已发布" }],
|
|
775
775
|
dryRun: { supported: true, command: `omc call field.update --env ${environment} --dry-run --args '<recipe.args>'`, notes: "dry-run 校验 readonlyFormula 是布尔", },
|
|
776
|
-
expectedReadback: { readback: { field: { code: "
|
|
776
|
+
expectedReadback: { readback: { field: { code: "approvalLevel", options: { readonlyFormula: true } } }, next: ["(一调)field.update cascadeRepublish:true", "(分步)field.publish → form.draft(rebuild) → form.publish", "(只刷新表单)form.publish rebuild:true"] },
|
|
777
777
|
refusalRecovery: [{ code: "capability-out-of-coverage", action: "readonlyFormula 必须是布尔 true;条件只读才用 readonlyCondition" }, ...commonRecovery],
|
|
778
778
|
requiresEnvironmentDerivedIds: false,
|
|
779
779
|
});
|
|
@@ -785,7 +785,7 @@ const formConditionalReadonly = Object.freeze({
|
|
|
785
785
|
args: {
|
|
786
786
|
environment,
|
|
787
787
|
schemaCode: model,
|
|
788
|
-
code: "
|
|
788
|
+
code: "approvalLevel",
|
|
789
789
|
name: "审批级别",
|
|
790
790
|
options: { readonlyCondition: '{status} 等于 {"valueType":"specifyOption","value":"已通过"}' },
|
|
791
791
|
},
|
|
@@ -793,7 +793,7 @@ const formConditionalReadonly = Object.freeze({
|
|
|
793
793
|
conditionalRequirements: ["条件串协议同必填;恒只读改用 readonlyFormula:true"],
|
|
794
794
|
priorToolReferences: [{ tool: "field.list", args: { environment, schemaCode: model, published: true }, purpose: "确认字段已发布" }],
|
|
795
795
|
dryRun: { supported: true, command: `omc call field.update --env ${environment} --dry-run --args '<recipe.args>'`, notes: "dry-run 校验条件串协议" },
|
|
796
|
-
expectedReadback: { readback: { field: { code: "
|
|
796
|
+
expectedReadback: { readback: { field: { code: "approvalLevel", options: { readonlyCondition: "<protocol>" } } }, next: ["(一调)field.update cascadeRepublish:true", "(分步)field.publish → form.draft(rebuild) → form.publish", "(只刷新表单)form.publish rebuild:true"] },
|
|
797
797
|
refusalRecovery: [{ code: "capability-out-of-coverage", action: "使用中文或符号操作符与三段条件串" }, ...commonRecovery],
|
|
798
798
|
requiresEnvironmentDerivedIds: false,
|
|
799
799
|
});
|
|
@@ -1010,7 +1010,7 @@ const listViewConfigure = Object.freeze({
|
|
|
1010
1010
|
title: "配置模型的默认列表视图(列/排序;默认视图已自动存在)",
|
|
1011
1011
|
status: "requires-prior-readback",
|
|
1012
1012
|
tool: "listview.configure",
|
|
1013
|
-
args: { environment, schemaCode: model, fieldCodes: ["
|
|
1013
|
+
args: { environment, schemaCode: model, fieldCodes: ["applyNo", "applyAmount", "applyNote"] },
|
|
1014
1014
|
preconditions: ["字段已 field.publish(视图与表单脚手架都依赖已发布字段)", "默认视图在 model.create 时由平台自动生成,code = schemaCode"],
|
|
1015
1015
|
conditionalRequirements: [
|
|
1016
1016
|
"**省略 `code` 即配置默认视图(code = schemaCode);不要先 listview.create——那会多出一个自定义视图。** 只有确实需要第二个视图时才 listview.create。",
|
|
@@ -1023,7 +1023,7 @@ const listViewConfigure = Object.freeze({
|
|
|
1023
1023
|
{ tool: "field.list", args: { environment, schemaCode: model, published: true }, purpose: "确认字段已发布" },
|
|
1024
1024
|
],
|
|
1025
1025
|
dryRun: { supported: true, command: `omc call listview.configure --env ${environment} --dry-run --args '<recipe.args>'`, notes: "dry-run 预检 fieldCodes/形状,不写" },
|
|
1026
|
-
expectedReadback: { readback: { code: "<schemaCode>", added: ["
|
|
1026
|
+
expectedReadback: { readback: { code: "<schemaCode>", added: ["applyNo", "applyAmount"], columns: "…" }, next: ["data.list 验证读面"] },
|
|
1027
1027
|
refusalRecovery: [
|
|
1028
1028
|
{ code: "listview-columns-unknown-field", action: "字段未发布或名字写错;先 field.create + field.publish 或改引用" },
|
|
1029
1029
|
{ code: "listview-not-found", action: "默认视图应已存在;用 listview.list 确认 code,必要时 listview.create" },
|
|
@@ -1050,6 +1050,27 @@ const dataListFilter = Object.freeze({
|
|
|
1050
1050
|
refusalRecovery: [{ code: "invalid-filter-DSL", action: "按错误里的 minimalValidFilter/acceptedOps 修正 op/field/value" }],
|
|
1051
1051
|
requiresEnvironmentDerivedIds: false,
|
|
1052
1052
|
});
|
|
1053
|
+
const dataDeleteSweep = Object.freeze({
|
|
1054
|
+
id: "sweep-all-readback",
|
|
1055
|
+
title: "清空模型数据并权威验证(list 取 ids → delete → 复验 total:0)",
|
|
1056
|
+
status: "offline-valid",
|
|
1057
|
+
tool: "data.delete",
|
|
1058
|
+
args: { environment, schemaCode: model, bizObjectIds: ["<data.list 逐条 id>"] },
|
|
1059
|
+
preconditions: [
|
|
1060
|
+
"先 data.list 取全量 ids(page 0 起、size 够大或翻页);**submit 后平台会同时保留 DRAFT 与 COMPLETED 两类行(r30 实证 5 次提交 = 10 行),清理对象按 data.list 全量**",
|
|
1061
|
+
],
|
|
1062
|
+
conditionalRequirements: [
|
|
1063
|
+
"参数名是 `bizObjectIds`(数组)——不是 ids/bizObjectId(单条删除另有其参)",
|
|
1064
|
+
"id 已被删时幂等 no-op,重复清理安全",
|
|
1065
|
+
],
|
|
1066
|
+
priorToolReferences: [
|
|
1067
|
+
{ tool: "data.list", args: { environment, schemaCode: model }, purpose: "取全量 bizObjectIds(含 DRAFT 行)" },
|
|
1068
|
+
],
|
|
1069
|
+
dryRun: { supported: true, command: `omc call data.delete --env ${environment} --dry-run --args '<recipe.args>'`, notes: "dry-run 出删除计划不落库" },
|
|
1070
|
+
expectedReadback: { readback: { deleted: "N", modelResidue: { total: 0 } }, next: ["data.list 复验 total:0 / items:[](权威清空判据)"] },
|
|
1071
|
+
refusalRecovery: [{ code: "wire-business", action: "模型须已创建且有已发布字段;运行面 50000 时先 model.inspect modelPresent 判存在" }],
|
|
1072
|
+
requiresEnvironmentDerivedIds: false,
|
|
1073
|
+
});
|
|
1053
1074
|
const dictionaryKeyValue = Object.freeze({
|
|
1054
1075
|
id: "dictionary-key-value",
|
|
1055
1076
|
title: "建一个键值字典并用其 id 作字段下拉来源",
|
|
@@ -1057,7 +1078,7 @@ const dictionaryKeyValue = Object.freeze({
|
|
|
1057
1078
|
tool: "dictionary.save",
|
|
1058
1079
|
args: {
|
|
1059
1080
|
environment,
|
|
1060
|
-
code: "
|
|
1081
|
+
code: "visitType",
|
|
1061
1082
|
name: "回访方式",
|
|
1062
1083
|
dictionaryType: 0,
|
|
1063
1084
|
records: [
|
|
@@ -1074,10 +1095,10 @@ const dictionaryKeyValue = Object.freeze({
|
|
|
1074
1095
|
],
|
|
1075
1096
|
priorToolReferences: [
|
|
1076
1097
|
{ tool: "dictionary.names", args: { environment }, purpose: "查 code/name 冲突并读回既有 id(字典多时结果会溢成 artifact;只需 code/name,用 `artifact get <id> --keys` 或 --jq,别整份取)" },
|
|
1077
|
-
{ tool: "dictionary.get", args: { environment, code: "
|
|
1098
|
+
{ tool: "dictionary.get", args: { environment, code: "visitType" }, purpose: "按 code 精确查存在性(小结果,优于 dictionary.names 全量)" },
|
|
1078
1099
|
],
|
|
1079
1100
|
dryRun: { supported: true, command: `omc call dictionary.save --env ${environment} --dry-run --args '<recipe.args>'`, notes: "dry-run 预检 code 冲突/形状,不写" },
|
|
1080
|
-
expectedReadback: { performed: { errcode: 0 }, readback: { present: true, dictionary: { id: "<dictionary.id>", code: "
|
|
1101
|
+
expectedReadback: { performed: { errcode: 0 }, readback: { present: true, dictionary: { id: "<dictionary.id>", code: "visitType", records: ["phone", "visit", "online"] } }, next: ["field.create --example dictionary-backed-single-select(用回读的 id)"] },
|
|
1081
1102
|
refusalRecovery: [
|
|
1082
1103
|
{ code: "dictionary-create-duplicate", action: "code 已存在:从 dictionary.names 取 id 后改传 id 做更新" },
|
|
1083
1104
|
{ code: "wire-business", action: "平台 `errcode -1 系统中已经存在相同名称的字典` = **字典名称全局唯一**(code 唯一还不够)——换一个 name(真机 2026-09-25:新 code + 旧 name 仍被拒)" },
|
|
@@ -1103,6 +1124,7 @@ export const EXECUTION_METADATA = Object.freeze({
|
|
|
1103
1124
|
"method.update": Object.freeze({ recipes: [bizserviceSelfCallCorp], relatedTools: ["method.get", "method.create", "service.get"] }),
|
|
1104
1125
|
"listview.configure": Object.freeze({ recipes: [listViewConfigure], relatedTools: ["listview.list", "listview.get", "field.list", "field.publish"] }),
|
|
1105
1126
|
"data.list": Object.freeze({ recipes: [dataListFilter], relatedTools: ["data.load", "data.delete", "listview.configure", "field.list"] }),
|
|
1127
|
+
"data.delete": Object.freeze({ recipes: [dataDeleteSweep], relatedTools: ["data.list", "data.load", "data.submit"] }),
|
|
1106
1128
|
});
|
|
1107
1129
|
export function executionMetadataFor(tool) {
|
|
1108
1130
|
return EXECUTION_METADATA[tool.name];
|
package/dist/goal-routes.js
CHANGED
|
@@ -46,7 +46,7 @@ function intentGuidance() {
|
|
|
46
46
|
why: "整包需求:用一份声明式意图文件描述 models/fields/form/listview/rules/workflow/onlinejs/services,由编译器合成平台原生结构并按依赖顺序执行;比逐能力拼装更省上下文、更不易漂移。",
|
|
47
47
|
starter: `intentVersion: 1
|
|
48
48
|
models:
|
|
49
|
-
- code: <模型code> # 如
|
|
49
|
+
- code: <模型code> # 如 omcTodoTask(小写下划线也合规;字段码必须 lowerCamelCase ≤25 字符——产品实施规范)
|
|
50
50
|
name: <业务名>
|
|
51
51
|
fields:
|
|
52
52
|
- {code: title, name: 标题, type: SHORT_TEXT}
|
|
@@ -56,7 +56,6 @@ models:
|
|
|
56
56
|
listview: {fieldCodes: [title, level]}
|
|
57
57
|
rules:
|
|
58
58
|
- code: Create # 平台预置触发行(name 省略)
|
|
59
|
-
name: 新增规则 # 仅自定义规则需要 name;预置行可省
|
|
60
59
|
enabled: true
|
|
61
60
|
actions:
|
|
62
61
|
- when: [{field: amount, gt: 10}]
|
package/dist/human-summary.js
CHANGED
|
@@ -128,7 +128,10 @@ function phraseOf(tool, args, outcome) {
|
|
|
128
128
|
case "form.draft":
|
|
129
129
|
return `保存表单 ${stringArg(args, "sheetCode") ?? "?"}(模型 ${schemaCode ?? "?"})的草稿布局`;
|
|
130
130
|
case "form.publish":
|
|
131
|
-
|
|
131
|
+
// r26 finding: sheetCode is optional (resolved from schemaCode for the
|
|
132
|
+
// default form) — the summary showed "?" when the agent omitted it.
|
|
133
|
+
const shownSheet = stringArg(args, "sheetCode") ?? (schemaCode !== undefined ? String(schemaCode) : "?");
|
|
134
|
+
return `发布表单 ${shownSheet}(模型 ${schemaCode ?? "?"}${args.rebuild === true ? ",重建后提拔" : ""})`;
|
|
132
135
|
case "configure_model": {
|
|
133
136
|
const fields = args.fields;
|
|
134
137
|
const count = Array.isArray(fields) ? fields.length : stringArg(args, "fieldCode") ? 1 : 0;
|
package/dist/intent/cli.js
CHANGED
|
@@ -82,7 +82,7 @@ export function intentCapabilityBlock() {
|
|
|
82
82
|
"a rule declares actions (compact UPDATE) or nodeGraph, never both; non-UPDATE node types use nodeGraph",
|
|
83
83
|
"escape hatch: uncovered capabilities stay available through omc call or a raw nodeGraph",
|
|
84
84
|
],
|
|
85
|
-
example: 'intentVersion: 1\nmodels:\n - code:
|
|
85
|
+
example: 'intentVersion: 1\nmodels:\n - code: evalM1\n name: 评测建模\n fields:\n - code: fTitle\n name: 标题\n type: SHORT_TEXT\n - code: items\n name: 明细\n type: CHILD_TABLE\n subFields:\n - code: itemName\n name: 项目\n type: SHORT_TEXT\n form:\n publish: true\n',
|
|
86
86
|
};
|
|
87
87
|
}
|
|
88
88
|
export function intentConfigRoot() {
|
package/dist/intent/schema.js
CHANGED
|
@@ -29,14 +29,15 @@ export const RUNNABLE_EXAMPLES = Object.freeze([
|
|
|
29
29
|
yaml: [
|
|
30
30
|
"intentVersion: 1",
|
|
31
31
|
"models:",
|
|
32
|
-
" - code:
|
|
32
|
+
" - code: exOrder",
|
|
33
33
|
" name: 订单",
|
|
34
34
|
" fields:",
|
|
35
|
-
" - { code:
|
|
35
|
+
" - { code: orderNo, name: 订单号, type: SHORT_TEXT }",
|
|
36
36
|
" - { code: amount, name: 金额, type: NUMERICAL, options: { scale: 2 } }",
|
|
37
37
|
" - { code: level, name: 等级, type: RADIO, choices: [A, B, C] } # choices 只用于 RADIO/CHECKBOX/DROPDOWN_BOX/DROPDOWN_MULTI_BOX",
|
|
38
|
+
" # 字段 code 避开平台保留字(id/name/owner/creater/createdTime/modifiedTime/version/…)——撞保留字 offline 校验即拒(code_reserved);name 是字段中文名,不是 code(r31 实证 code:name 被拒后改 itemName 成功)",
|
|
38
39
|
" form: { publish: true }",
|
|
39
|
-
" listview: { fieldCodes: [
|
|
40
|
+
" listview: { fieldCodes: [orderNo, amount, level] }",
|
|
40
41
|
"",
|
|
41
42
|
].join("\n"),
|
|
42
43
|
},
|
|
@@ -46,19 +47,19 @@ export const RUNNABLE_EXAMPLES = Object.freeze([
|
|
|
46
47
|
yaml: [
|
|
47
48
|
"intentVersion: 1",
|
|
48
49
|
"models:",
|
|
49
|
-
" - code:
|
|
50
|
+
" - code: exSupplier",
|
|
50
51
|
" name: 供应商",
|
|
51
52
|
" fields:",
|
|
52
|
-
" - { code:
|
|
53
|
+
" - { code: supName, name: 名称, type: SHORT_TEXT }",
|
|
53
54
|
" form: { publish: true }",
|
|
54
|
-
" - code:
|
|
55
|
+
" - code: exContract",
|
|
55
56
|
" name: 合同",
|
|
56
57
|
" fields:",
|
|
57
58
|
" - code: supplier",
|
|
58
59
|
" name: 供应商",
|
|
59
60
|
" type: WORK_SHEET",
|
|
60
|
-
" relativeCode:
|
|
61
|
-
" options: { displayField:
|
|
61
|
+
" relativeCode: exSupplier # 目标模型 code",
|
|
62
|
+
" options: { displayField: supName } # 显示字段",
|
|
62
63
|
" form: { publish: true }",
|
|
63
64
|
"",
|
|
64
65
|
].join("\n"),
|
|
@@ -69,12 +70,12 @@ export const RUNNABLE_EXAMPLES = Object.freeze([
|
|
|
69
70
|
yaml: [
|
|
70
71
|
"intentVersion: 1",
|
|
71
72
|
"models:",
|
|
72
|
-
" - code:
|
|
73
|
+
" - code: exOrder",
|
|
73
74
|
" name: 订单",
|
|
74
75
|
" fields:",
|
|
75
76
|
" - { code: amount, name: 金额, type: NUMERICAL, options: { scale: 2 } }",
|
|
76
77
|
" - { code: status, name: 状态, type: SHORT_TEXT }",
|
|
77
|
-
" - { code: supplier, name: 供应商, type: WORK_SHEET, relativeCode:
|
|
78
|
+
" - { code: supplier, name: 供应商, type: WORK_SHEET, relativeCode: exSupplier }",
|
|
78
79
|
" rules:",
|
|
79
80
|
" - code: Create",
|
|
80
81
|
" name: 新增分级",
|
|
@@ -82,17 +83,17 @@ export const RUNNABLE_EXAMPLES = Object.freeze([
|
|
|
82
83
|
" actions:",
|
|
83
84
|
" # 同一规则内多个 action:按数组顺序编译为串行节点",
|
|
84
85
|
" - type: UPDATE",
|
|
85
|
-
" target:
|
|
86
|
+
" target: exOrder",
|
|
86
87
|
" when: [{ field: amount, gte: 100000 }] # 执行门:满足才执行(gte/lte 合法)",
|
|
87
88
|
" set: [{ field: status, value: 大额 }] # 省略 filter -> 只改当前记录",
|
|
88
89
|
" - type: UPDATE",
|
|
89
|
-
" target:
|
|
90
|
+
" target: exOrder",
|
|
90
91
|
" when: [{ field: amount, lt: 100000 }]",
|
|
91
92
|
" set: [{ field: status, value: 小额 }]",
|
|
92
93
|
" - type: UPDATE",
|
|
93
|
-
" target:
|
|
94
|
+
" target: exSupplier # 跨模型回写",
|
|
94
95
|
" filter: [{ field: id, from: supplier }] # 定位目标行:目标 id = 当前记录的 supplier",
|
|
95
|
-
" set: [{ field:
|
|
96
|
+
" set: [{ field: totalAmount, from: amount }]",
|
|
96
97
|
"",
|
|
97
98
|
].join("\n"),
|
|
98
99
|
},
|
package/dist/lanes/model.js
CHANGED
|
@@ -171,7 +171,7 @@ export function registerModelLane(tools) {
|
|
|
171
171
|
const codes = args.schemaCodes && args.schemaCodes.length > 0 ? args.schemaCodes : args.schemaCode ? [args.schemaCode] : [];
|
|
172
172
|
if (codes.length === 0)
|
|
173
173
|
throw new ToolInputError({ status: "error", blocker: "tool-input-invalid", tool: "model.inspect", message: "需要 schemaCode(单个)或 schemaCodes(数组,批量)" });
|
|
174
|
-
const face = async (name, toolArgs) => {
|
|
174
|
+
const face = async (name, toolArgs, errcodes) => {
|
|
175
175
|
const tool = siblings.find((entry) => entry.name === name);
|
|
176
176
|
if (!tool)
|
|
177
177
|
return undefined;
|
|
@@ -184,17 +184,24 @@ export function registerModelLane(tools) {
|
|
|
184
184
|
const result = (await withoutReadbackCache(ctx, async () => (await tool.handler(ctx, parsed, tool))));
|
|
185
185
|
return result;
|
|
186
186
|
}
|
|
187
|
-
catch {
|
|
187
|
+
catch (error) {
|
|
188
|
+
// r30: record the wire errcode so inspectOne can turn the field
|
|
189
|
+
// face's 300004 into a `modelPresent:false` verdict instead of an
|
|
190
|
+
// all-empty aggregate that reads as "registered, no fields".
|
|
191
|
+
const errcode = error.errcode;
|
|
192
|
+
if (errcodes && typeof errcode === "number")
|
|
193
|
+
errcodes.set(name, errcode);
|
|
188
194
|
return undefined; // a single face being unreachable must not sink the whole inspect
|
|
189
195
|
}
|
|
190
196
|
};
|
|
191
197
|
const inspectOne = async (schemaCode) => {
|
|
198
|
+
const faceErrcodes = new Map();
|
|
192
199
|
// F5: the six read faces are INDEPENDENT wire reads; fan them out in
|
|
193
200
|
// parallel (bounded) instead of paying six sequential round-trips. Only
|
|
194
201
|
// workflow.get_published stays chained (it needs workflowCode from the
|
|
195
202
|
// draft face). Output ordering is unchanged (results land by index).
|
|
196
203
|
const [fieldFace, formFace, listviewFace, ruleFace, dataruleFace, draftFace] = await mapBounded([
|
|
197
|
-
() => face("field.list", { schemaCode, published: true, ...(all ? { all: true } : {}) }),
|
|
204
|
+
() => face("field.list", { schemaCode, published: true, ...(all ? { all: true } : {}) }, faceErrcodes),
|
|
198
205
|
() => face("form.get", { schemaCode }),
|
|
199
206
|
() => face("listview.get", { schemaCode }),
|
|
200
207
|
() => face("rule.list", { schemaCode }),
|
|
@@ -245,15 +252,29 @@ export function registerModelLane(tools) {
|
|
|
245
252
|
activities: Array.isArray(summary?.activities) ? summary.activities.map((activity) => ({ code: activity.code, type: activity.type, name: activity.name, participant: activity.participant })) : [],
|
|
246
253
|
edges: summary?.edges,
|
|
247
254
|
};
|
|
255
|
+
// r30 verdict: field face ok ⇒ model exists. The wire refusal for a
|
|
256
|
+
// missing model is a RETURNED payload (status:"refused", errcode:300004
|
|
257
|
+
// 数据模型不存在), not a throw — read both signals (returned refusal +
|
|
258
|
+
// thrown WireError) before deciding; unreachable face ⇒ undefined.
|
|
259
|
+
const fieldRefused = fieldFace !== undefined && fieldFace.status === "refused";
|
|
260
|
+
const fieldRefusalErrcode = fieldRefused ? Number(fieldFace.errcode) : undefined;
|
|
261
|
+
const absent = fieldRefusalErrcode === 300004 || faceErrcodes.get("field.list") === 300004;
|
|
262
|
+
const modelPresent = absent ? false : fieldFace !== undefined && !fieldRefused ? true : undefined;
|
|
248
263
|
return {
|
|
249
264
|
schemaCode,
|
|
265
|
+
...(modelPresent !== undefined ? { modelPresent } : {}),
|
|
266
|
+
...(modelPresent === false
|
|
267
|
+
? {
|
|
268
|
+
existenceHint: "模型不存在(field.list 300004)——先 model.create / configure_model 再挂字段;勿把空聚合读成『已存在但无字段』(r30 实证该误读导致 field.create 300004 失败往返)",
|
|
269
|
+
}
|
|
270
|
+
: {}),
|
|
250
271
|
fields,
|
|
251
272
|
form,
|
|
252
273
|
listview,
|
|
253
274
|
rules,
|
|
254
275
|
datarules,
|
|
255
276
|
workflow,
|
|
256
|
-
hint: "model.inspect 聚合设计时六面(fields/form/listview/rules/datarules/workflow
|
|
277
|
+
hint: "model.inspect 聚合设计时六面(fields/form/listview/rules/datarules/workflow,摘要)。modelPresent 是存在性判据(field 面 300004=false);字段 options 细节用 field.list --code;规则图用 rule.get schemaCode+ruleCode;运行数据用 data.list(不在此面)。",
|
|
257
278
|
};
|
|
258
279
|
};
|
|
259
280
|
const one = await inspectOne(codes[0]);
|
|
@@ -434,6 +434,26 @@ export function registerRuntimeDataLane(tools) {
|
|
|
434
434
|
if (error instanceof WireError && error.kind === "business" && error.errcode === RUNTIME_DATA_WIRE.emptyModelErrcode) {
|
|
435
435
|
return { schemaCode: args.schemaCode, total: 0, totalPages: 0, page: args.page ?? 0, size: args.size ?? 10, items: [] };
|
|
436
436
|
}
|
|
437
|
+
// r30: a MISSING model answers this face with a generic 50000
|
|
438
|
+
// 服务异常 — indistinguishable from a real outage. One field-face
|
|
439
|
+
// probe disambiguates: its 300004 (数据模型不存在) turns the
|
|
440
|
+
// refusal into an existence verdict so the caller creates instead
|
|
441
|
+
// of retrying; any other outcome rethrows the original 50000.
|
|
442
|
+
if (error instanceof WireError && error.kind === "business" && error.errcode === 50000) {
|
|
443
|
+
try {
|
|
444
|
+
await wireCall(session, { path: ENDPOINTS.fieldList, query: { schemaCode: args.schemaCode, isPublish: "true" } });
|
|
445
|
+
}
|
|
446
|
+
catch (probeError) {
|
|
447
|
+
if (probeError instanceof WireError && probeError.kind === "business" && probeError.errcode === 300004) {
|
|
448
|
+
return {
|
|
449
|
+
status: "refused",
|
|
450
|
+
blocker: "wire-business",
|
|
451
|
+
errcode: 300004,
|
|
452
|
+
message: `数据模型不存在(${args.schemaCode})——运行面读取前模型必须已创建并发布字段;先 model.create / configure_model(存在性判据用 model.inspect 的 modelPresent)`,
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
437
457
|
throw error;
|
|
438
458
|
}
|
|
439
459
|
const page = extractBizObjectPage(result.data);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stalfh233/omc-cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.33",
|
|
4
4
|
"description": "CloudPivot 8.6 configuration capability CLI — governed, fail-closed, coverage-manifest bounded.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"commander": "14.0.3",
|
|
54
|
-
"omc-core": "0.4.
|
|
54
|
+
"omc-core": "0.4.33",
|
|
55
55
|
"proper-lockfile": "4.1.2",
|
|
56
56
|
"write-file-atomic": "6.0.0",
|
|
57
57
|
"zod": "^3.23.8",
|
|
@@ -55,13 +55,13 @@ node "$CLI" help omc-five-piece-flow
|
|
|
55
55
|
```json
|
|
56
56
|
{
|
|
57
57
|
"schemaCode": "expense",
|
|
58
|
-
"code": "
|
|
58
|
+
"code": "expenseItems",
|
|
59
59
|
"name": "费用明细",
|
|
60
60
|
"propertyType": "CHILD_TABLE",
|
|
61
61
|
"subFields": [
|
|
62
|
-
{ "code": "
|
|
63
|
-
{ "code": "
|
|
64
|
-
{ "code": "
|
|
62
|
+
{ "code": "itemName", "name": "费用项目", "propertyType": "SHORT_TEXT" },
|
|
63
|
+
{ "code": "itemAmount", "name": "金额", "propertyType": "NUMERICAL" },
|
|
64
|
+
{ "code": "itemDate", "name": "发生日期", "propertyType": "DATE" }
|
|
65
65
|
]
|
|
66
66
|
}
|
|
67
67
|
```
|
|
@@ -70,9 +70,9 @@ node "$CLI" help omc-five-piece-flow
|
|
|
70
70
|
|
|
71
71
|
```bash
|
|
72
72
|
# --env 在单环境项目(.omc-env 在位)可省;显式写出仅为多环境演示
|
|
73
|
-
omc call field.create --env local --args '{"schemaCode":"
|
|
74
|
-
omc call field.update --env local --args '{"schemaCode":"
|
|
75
|
-
omc call field.delete --env local --args '{"schemaCode":"
|
|
73
|
+
omc call field.create --env local --args '{"schemaCode":"expenseItems","code":"itemNote","name":"明细备注","propertyType":"LONG_TEXT"}'
|
|
74
|
+
omc call field.update --env local --args '{"schemaCode":"expenseItems","code":"itemNote","name":"备注"}'
|
|
75
|
+
omc call field.delete --env local --args '{"schemaCode":"expenseItems","code":"itemNote"}'
|
|
76
76
|
omc call field.publish --env local --args '{"schemaCode":"expense"}'
|
|
77
77
|
```
|
|
78
78
|
|
|
@@ -85,14 +85,14 @@ models:
|
|
|
85
85
|
- code: expense
|
|
86
86
|
name: 费用报销单
|
|
87
87
|
fields:
|
|
88
|
-
- code:
|
|
88
|
+
- code: expenseItems
|
|
89
89
|
name: 费用明细
|
|
90
90
|
type: CHILD_TABLE
|
|
91
91
|
subFields:
|
|
92
|
-
- code:
|
|
92
|
+
- code: itemName
|
|
93
93
|
name: 费用项目
|
|
94
94
|
type: SHORT_TEXT
|
|
95
|
-
- code:
|
|
95
|
+
- code: itemAmount
|
|
96
96
|
name: 金额
|
|
97
97
|
type: NUMERICAL
|
|
98
98
|
```
|
package/skills/omc-start-here.md
CHANGED
|
@@ -23,7 +23,7 @@ be treated as a second source. Read this, then follow the topics it names.
|
|
|
23
23
|
3. 环境名以 `omc doctor` 的「环境=」或 `.omc-env` 为准;工具不确定先用默认 `describe`/`--example`,只有单个工具缺形状才用 `--full`;`describe` 带 `exampleHint`/`recipeHint` 时优先 `--example`。
|
|
24
24
|
4. 只有工具不在 `next` 命中能力内、或要看 purpose/枚举时,才补一次 `commands --json --compact --lane <lane>`。非显然形状(规则图、流程模板、表单布局、RESTFUL)才单独 `describe --example`。
|
|
25
25
|
5. 键名按对象不同:模型 `schemaCode`、默认表单 `sheetCode`(可省略,默认 == 模型 code)、流程 `workflowCode`/`code`——报错会提示同义键;字段族名认不出会给「最接近的族」。
|
|
26
|
-
6.
|
|
26
|
+
6. **写操作串行**:同一环境不要并发写(含多个 `omc call` 并行进程——环境写锁互斥,r31 实证并行 field.create 2/3 被拒);多个写合并为一条 `omc script` 逐行跑(进程内自动串行);`write-lock-held` 是瞬态,等几秒重试一次即可。
|
|
27
27
|
7. **单个对象优先高阶编排工具**:模型+字段用 `configure_model`(一次含全部字段),表单用 `configure_form`,列表用 `listview.configure`(**默认视图已存在,省略 code 即配置它,不要 `listview.create`**)。原子工具留给单点修改。
|
|
28
28
|
8. 拿到 `detailArtifactId` 先 `artifact get <id> --summary` 看结构,再 `--keys/--jq` 取所需;`field.list`/`workflow.*` 默认面已给清单与节点摘要;`workflow.get_draft` 默认面已含 `edges`,改流程只需传 `activities`+`routes`。
|
|
29
29
|
9. 每个 section 读一次即可;同一 topic 不要反复读。
|