@stalfh233/omc-cli 0.4.74 → 0.4.75
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/coverage/m1-coverage-manifest-v1.json +9 -3
- package/dist/capability-overview.js +12 -3
- package/dist/cli.js +18 -3
- package/dist/endpoints.js +24 -0
- package/dist/form-default.js +97 -10
- package/dist/form-support.js +309 -3
- package/dist/human-summary.js +4 -0
- package/dist/lanes/businessrule.js +7 -0
- package/dist/lanes/form-contract.js +13 -3
- package/dist/lanes/form-coverage.js +2 -1
- package/dist/lanes/form.js +43 -1
- package/dist/lanes/organization.js +37 -3
- package/dist/lanes/rule-lifecycle-contract.js +17 -0
- package/dist/lanes/rule-lifecycle-coverage.js +2 -0
- package/dist/lanes/rule-lifecycle.js +103 -19
- package/dist/lanes/workflow-contract.js +57 -10
- package/dist/lanes/workflow-coverage.js +4 -0
- package/dist/lanes/workflow.js +458 -40
- package/dist/rollback-spec.js +32 -1
- package/dist/usage-log.js +11 -2
- package/package.json +3 -3
- package/skills/omc-business-configuration.md +20 -2
- package/skills/omc-capabilities.md +8 -7
- package/skills/omc-write-hazards.md +4 -4
|
@@ -337,6 +337,7 @@
|
|
|
337
337
|
"workflow.update",
|
|
338
338
|
"workflow.publish",
|
|
339
339
|
"workflow.bindRule",
|
|
340
|
+
"workflow.setButtons",
|
|
340
341
|
"workflow.delete",
|
|
341
342
|
"configure_workflow",
|
|
342
343
|
"workflow.validate",
|
|
@@ -363,6 +364,7 @@
|
|
|
363
364
|
],
|
|
364
365
|
"scopes": [
|
|
365
366
|
"greenfield workflow authoring",
|
|
367
|
+
"workflow node form-button checkboxes",
|
|
366
368
|
"organization role member read (role/users)",
|
|
367
369
|
"organization role member management scope read (role/get_ouscope)",
|
|
368
370
|
"organization role group create (role/add_role_group)",
|
|
@@ -496,10 +498,12 @@
|
|
|
496
498
|
"rule.disable",
|
|
497
499
|
"rule.delete",
|
|
498
500
|
"rule.disableMany",
|
|
501
|
+
"rule.run",
|
|
499
502
|
"configure_rule"
|
|
500
503
|
],
|
|
501
504
|
"scopes": [
|
|
502
|
-
"enable invoke disable delete"
|
|
505
|
+
"enable invoke disable delete",
|
|
506
|
+
"manual rule execution (button trigger)"
|
|
503
507
|
]
|
|
504
508
|
},
|
|
505
509
|
{
|
|
@@ -856,11 +860,13 @@
|
|
|
856
860
|
"form.get",
|
|
857
861
|
"form.draft",
|
|
858
862
|
"form.publish",
|
|
859
|
-
"configure_form"
|
|
863
|
+
"configure_form",
|
|
864
|
+
"form.runButton"
|
|
860
865
|
],
|
|
861
866
|
"scopes": [
|
|
862
867
|
"publishedHtmlJson authoritative readback",
|
|
863
|
-
"draft publish publishedHtmlJson readback"
|
|
868
|
+
"draft publish publishedHtmlJson readback",
|
|
869
|
+
"form toolbar button execution"
|
|
864
870
|
]
|
|
865
871
|
},
|
|
866
872
|
{
|
|
@@ -191,13 +191,22 @@ export const CAPABILITY_CUES = Object.freeze({
|
|
|
191
191
|
model: ["模型", "对象", "主数据", "物理删除", "汇总表", "统计表", "汇总对象", "月度汇总表", "汇总模型"],
|
|
192
192
|
field: ["字段", "属性", "子表", "明细", "下拉", "单选", "多选", "附件", "人员", "部门", "日期", "数值", "文本", "关联", "公式", "计算", "计算字段", "求和", "汇总", "合计", "回主表", "超链接", "地址", "表单层", "必填", "只读", "条件必填", "恒必填", "条件只读", "恒只读", "表单必填", "表单只读"],
|
|
193
193
|
form: ["表单", "页面", "布局", "控件", "只读", "显示"],
|
|
194
|
-
|
|
195
|
-
|
|
194
|
+
// r10 (2026-10-22, issue §1.4): an ask for a FORM toolbar button was
|
|
195
|
+
// answered "CLI 无按钮能力" — the capability exists (`listview.configure`
|
|
196
|
+
// `buttons` with useLocation FORM + bindAction SCRIPT). Cue the compound
|
|
197
|
+
// phrases so they outweigh businessrule's generic "按钮".
|
|
198
|
+
listview: ["列表", "视图", "查询列", "筛选", "表头", "甘特", "表单按钮", "工具栏按钮", "列表按钮", "行级按钮", "自定义按钮", "工具栏"],
|
|
199
|
+
// r10 (2026-10-22): "计划日期不能早于今天" routed to `field` (it matched
|
|
200
|
+
// 日期) because no cue named the VALIDATION intent, so the agent went to the
|
|
201
|
+
// rule graph and concluded "the platform has no today variable" — wrong, and
|
|
202
|
+
// the form designer's 提交校验 (datarule type1/checkType2) is the real face.
|
|
203
|
+
datarule: ["字段级规则", "控件规则", "校验规则", "计算拼接", "显示规则", "规则", "type1", "type2", "type3",
|
|
204
|
+
"不早于", "不能早于", "不晚于", "不能晚于", "早于今天", "晚于今天", "日期校验", "提交校验", "校验不通过", "必填校验", "正则校验", "重复校验", "长度校验"],
|
|
196
205
|
// NOTE: bare "自动" was a cue (round 10 bug: "自动识别"/"自动发邮件" matched
|
|
197
206
|
// businessrule and routed an UNSUPPORTED ask onto the write ladder). A business
|
|
198
207
|
// rule is named by 业务规则/触发器/规则图, not by "automatic".
|
|
199
208
|
businessrule: ["业务规则", "触发器", "规则图", "校验", "事件", "按钮", "定时任务", "定时触发", "定时器", "每天", "每日", "周期执行", "调度", "SCHEDULER", "TIMING_TASK"],
|
|
200
|
-
"rule-lifecycle": ["启用规则", "停用规则", "删除规则", "规则启停"],
|
|
209
|
+
"rule-lifecycle": ["启用规则", "停用规则", "删除规则", "规则启停", "触发规则", "执行规则", "跑规则", "按钮规则", "规则验收"],
|
|
201
210
|
workflow: ["流程", "工作流", "审批", "节点", "待办", "流转", "参与人", "审批人", "角色", "部门负责人", "审批角色"],
|
|
202
211
|
onlinejs: ["在线js", "脚本", "联动", "onload", "onvalidate"],
|
|
203
212
|
bizservice: ["集成", "接口", "服务", "回调", "对接", "外部系统", "datasql", "restful"],
|
package/dist/cli.js
CHANGED
|
@@ -327,8 +327,15 @@ async function cmdCall(store, args) {
|
|
|
327
327
|
validateAgentPaths(parsedArgs);
|
|
328
328
|
}
|
|
329
329
|
catch (error) {
|
|
330
|
-
|
|
331
|
-
|
|
330
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
331
|
+
// r10 F9: an out-of-root `--args-file` was reported under "--args 必须是
|
|
332
|
+
// 合法 JSON", which points the reader at the wrong flag. Name the real
|
|
333
|
+
// cause (and the configuration root) when the failure came from the file.
|
|
334
|
+
const message = /must be inside the configuration root/.test(detail)
|
|
335
|
+
? `--args-file 越界:${detail}(只接受配置根 ${resolve(process.env.OMC_CONFIG_HOME ?? process.cwd())} 内的路径;把参数文件放进项目目录后重试)`
|
|
336
|
+
: `--args 必须是合法 JSON:${detail}(Windows PowerShell 下内联 JSON 引号转义不可靠,请改用 --args-file <path> 写入临时文件后引用)`;
|
|
337
|
+
console.error(message);
|
|
338
|
+
writeEnvelope(envelope("error", {}, request, { code: "invalid-input", message: detail, retryable: false }));
|
|
332
339
|
return CLI_EXIT_CODES.invalidInput;
|
|
333
340
|
}
|
|
334
341
|
const registry = buildToolRegistry();
|
|
@@ -1174,7 +1181,15 @@ const request = requestId();
|
|
|
1174
1181
|
main().then((code) => {
|
|
1175
1182
|
// Commands that own their stdout never grow a trailing envelope. `--version`/
|
|
1176
1183
|
// `-V` are here because their short-circuit already wrote the bare version.
|
|
1177
|
-
|
|
1184
|
+
// Composers (`form-default`, `compute`, `rule-when`, `approval-flow`) own their
|
|
1185
|
+
// stdout exactly like `call` does. Without them in this list every composer
|
|
1186
|
+
// printed its real envelope AND a trailing `{"ok":true,"result":{}}` — r10 read
|
|
1187
|
+
// that as a second, empty answer. `rollback` / `env` emit their own
|
|
1188
|
+
// envelope (or, for `env --json`, their own JSON) and were printing a second
|
|
1189
|
+
// empty envelope after it (live 2026-10-23: `omc rollback apply` answered two
|
|
1190
|
+
// lines, violating the one-envelope stdout contract).
|
|
1191
|
+
const ownsStdout = ["call", "script", "artifact", "commands", "describe", "contract", "capabilities", "capability", "next", "usage", "intent", "help", "form-default", "compute", "rule-when", "approval-flow", "rollback", "env", "--version", "-V"];
|
|
1192
|
+
if (ownsStdout.includes(argv[2] ?? "")) {
|
|
1178
1193
|
completeUsage(code);
|
|
1179
1194
|
finish(code);
|
|
1180
1195
|
return;
|
package/dist/endpoints.js
CHANGED
|
@@ -58,6 +58,30 @@ export const ENDPOINTS = {
|
|
|
58
58
|
businessRuleNodeRunMap: "/api/api/app/businessrule/node_run_map",
|
|
59
59
|
businessRuleEnable: "/api/api/app/businessrule/enable",
|
|
60
60
|
businessRuleDelete: "/api/api/app/businessrule/delete",
|
|
61
|
+
/**
|
|
62
|
+
* Manual execution of a CUSTOM-trigger rule (the list/form button path).
|
|
63
|
+
* Live 2026-10-23 (AI-Platform 1.1.25, Playwright capture of a
|
|
64
|
+
* `listview.configure` BUSINESS_RULE row button):
|
|
65
|
+
* POST /api/api/launcher/domain_service_runtime/<schemaCode>/<ruleCode>
|
|
66
|
+
* body {schemaCode, ids:[bizObjectId...], ruleCode, consolidation:false}
|
|
67
|
+
* → {errcode:0, errmsg:"Execution Completed", data:[<pre-execution bizObjects>], traceId}
|
|
68
|
+
* The response objects are the PRE-execution snapshot (the frontend sends
|
|
69
|
+
* reacquireDataFromDb:false), so effects must be read back separately.
|
|
70
|
+
*/
|
|
71
|
+
ruleRuntimeExecuteBase: "/api/api/launcher/domain_service_runtime",
|
|
72
|
+
/**
|
|
73
|
+
* Form-toolbar button execution — a DIFFERENT face from the list-button one.
|
|
74
|
+
* Live 2026-10-23 (1.1.25, Playwright capture of a `triggerType=FORM` button
|
|
75
|
+
* rendered on /form/detail):
|
|
76
|
+
* POST /api/api/runtime/form/executeCustomButton
|
|
77
|
+
* {bizButtonModel:<the h_biz_button row>, bizObjectList:[<full bizObject>],
|
|
78
|
+
* reacquireDataFromDb:true}
|
|
79
|
+
* The list button posts ids to the launcher path instead; the two are not
|
|
80
|
+
* interchangeable.
|
|
81
|
+
*/
|
|
82
|
+
runtimeFormExecuteCustomButton: "/api/api/runtime/form/executeCustomButton",
|
|
83
|
+
/** Designer's form-button inventory: form trigger infos + their BizButtonModels. */
|
|
84
|
+
systemGetCustomButtons: "/api/api/system/getCustomButtons",
|
|
61
85
|
dataRuleCreate: "/api/api/app/bizdatarule/create",
|
|
62
86
|
dataRuleList: "/api/api/app/bizdatarule/list",
|
|
63
87
|
dataRuleUpdate: "/api/api/app/bizdatarule/update",
|
package/dist/form-default.js
CHANGED
|
@@ -14,9 +14,14 @@
|
|
|
14
14
|
* 将要做的修改。
|
|
15
15
|
*/
|
|
16
16
|
import { runToolCall } from "./call.js";
|
|
17
|
+
import { readFileSync } from "node:fs";
|
|
18
|
+
import { readArtifact } from "./artifacts.js";
|
|
19
|
+
import { artifactRoot } from "./command-transport.js";
|
|
17
20
|
import { envelope, requestId, classifyResult, CLI_EXIT_CODES } from "./cli-contract.js";
|
|
18
21
|
import { writeEnvelope } from "./cli-output.js";
|
|
19
22
|
import { argvValue } from "./args.js";
|
|
23
|
+
/** Sub-calls must not print their own envelopes (the composer owns stdout). */
|
|
24
|
+
const silent = () => { };
|
|
20
25
|
/** 从 form.get 结果提取控件数组(draft 优先,published 兜底)。 */
|
|
21
26
|
function toControlList(value) {
|
|
22
27
|
if (Array.isArray(value))
|
|
@@ -35,6 +40,64 @@ function extractControls(formGet) {
|
|
|
35
40
|
return { controls: published, face: "published" };
|
|
36
41
|
return undefined;
|
|
37
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* `form.get` summarizes large JSON as `{length, sha256}` (token diet), and with
|
|
45
|
+
* `controls:true` even the control LISTS come back as `{keys, sha256}` — treating
|
|
46
|
+
* those as arrays yields "2 controls". The raw faces ride the detail artifact.
|
|
47
|
+
*
|
|
48
|
+
* r10 (2026-10-22) F1: `form-default --apply` cloned that摘要 as the draft, wrote
|
|
49
|
+
* a 2-control form, and `form.publish`'s degrade guard then refused its own
|
|
50
|
+
* output (`form-publish-degrade`, 7 identical failures).
|
|
51
|
+
*/
|
|
52
|
+
function readDetailForm(result) {
|
|
53
|
+
// In-process composers see `detailFile` (the tool's own artifact path); the CLI
|
|
54
|
+
// transport rewrites it to `detailArtifactId` before printing. Handle both.
|
|
55
|
+
const raw = (() => {
|
|
56
|
+
if (typeof result?.detailArtifactId === "string") {
|
|
57
|
+
try {
|
|
58
|
+
return readArtifact(artifactRoot(), result.detailArtifactId).content.toString("utf8");
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (typeof result?.detailFile === "string") {
|
|
65
|
+
try {
|
|
66
|
+
return readFileSync(result.detailFile, "utf8");
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return undefined;
|
|
73
|
+
})();
|
|
74
|
+
if (raw === undefined)
|
|
75
|
+
return undefined;
|
|
76
|
+
try {
|
|
77
|
+
const parsed = JSON.parse(raw);
|
|
78
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : undefined;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function parseJsonColumn(value) {
|
|
85
|
+
if (typeof value !== "string")
|
|
86
|
+
return value;
|
|
87
|
+
try {
|
|
88
|
+
return JSON.parse(value);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** The published control map `{ <controlKey>: {key,type,options} }`, raw. */
|
|
95
|
+
function controlMap(value) {
|
|
96
|
+
const parsed = parseJsonColumn(value);
|
|
97
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
98
|
+
return undefined;
|
|
99
|
+
return parsed;
|
|
100
|
+
}
|
|
38
101
|
export async function cmdFormDefault(args) {
|
|
39
102
|
const request = requestId();
|
|
40
103
|
const flag = (name) => argvValue(args, name);
|
|
@@ -51,7 +114,7 @@ export async function cmdFormDefault(args) {
|
|
|
51
114
|
if (!schema || !field || value === undefined)
|
|
52
115
|
return invalid("--schema/--field/--default 均必填");
|
|
53
116
|
// 1) 读控件(单控件投影,拿全对象含 options)
|
|
54
|
-
const getOutcome = await runToolCall("form.get", { args: { schemaCode: schema, controls: [field], ...(environment ? { environment } : {}) }, requestId: request, environment });
|
|
117
|
+
const getOutcome = await runToolCall("form.get", { args: { schemaCode: schema, controls: [field], ...(environment ? { environment } : {}) }, requestId: request, environment, print: silent });
|
|
55
118
|
if (getOutcome.exitCode !== 0) {
|
|
56
119
|
const classified = classifyResult(getOutcome.result);
|
|
57
120
|
writeEnvelope(envelope(classified.status, getOutcome.result, request, classified.error));
|
|
@@ -73,34 +136,58 @@ export async function cmdFormDefault(args) {
|
|
|
73
136
|
writeEnvelope(envelope("ok", { schemaCode: schema, field, from: options.defaultValue === value ? undefined : control, to: nextControl, dryRun: true, note: "--apply 执行 form.draft→form.publish" }, request));
|
|
74
137
|
return 0;
|
|
75
138
|
}
|
|
76
|
-
// 2) form.draft:需要完整 draftAttributesJson
|
|
77
|
-
|
|
139
|
+
// 2) form.draft:需要完整 draftAttributesJson——单控件投影不够,取发布面的全量控件
|
|
140
|
+
// 图。`controls:true` / `full:true` 的大字段在 CLI 面被摘要成 {length,sha256}
|
|
141
|
+
// 或 {keys,sha256},必须从 detail artifact 取原始 JSON。
|
|
142
|
+
const getAll = await runToolCall("form.get", { args: { schemaCode: schema, full: true, ...(environment ? { environment } : {}) }, requestId: request, environment, print: silent });
|
|
78
143
|
if (getAll.exitCode !== 0) {
|
|
79
144
|
const classified = classifyResult(getAll.result);
|
|
80
145
|
writeEnvelope(envelope(classified.status, getAll.result, request, classified.error));
|
|
81
146
|
return getAll.exitCode;
|
|
82
147
|
}
|
|
83
|
-
const
|
|
84
|
-
|
|
148
|
+
const detail = readDetailForm(getAll.result) ?? (getAll.result ?? {});
|
|
149
|
+
const publishedAttrs = controlMap(detail.publishedAttributesJson);
|
|
150
|
+
const draftAttrs = controlMap(detail.draftAttributesJson);
|
|
151
|
+
const publishedCount = publishedAttrs ? Object.keys(publishedAttrs).length : 0;
|
|
152
|
+
const draftCount = draftAttrs ? Object.keys(draftAttrs).length : 0;
|
|
153
|
+
// Clone the COMPLETE face: a partial draft (or a 摘要) would shrink the form and
|
|
154
|
+
// trip the platform's own degrade guard at publish time.
|
|
155
|
+
const base = draftAttrs && draftCount >= publishedCount && draftCount > 0 ? draftAttrs : publishedAttrs;
|
|
156
|
+
if (!base || Object.keys(base).length === 0)
|
|
85
157
|
return invalid("form.get 未取到任何控件(表单为空?先发布默认表单)");
|
|
86
|
-
const
|
|
158
|
+
const controlKey = Object.keys(base).find((key) => key === field)
|
|
159
|
+
?? Object.keys(base).find((key) => String(base[key].key ?? "") === field);
|
|
160
|
+
if (!controlKey)
|
|
161
|
+
return invalid(`发布面里没有字段 "${field}" 的控件——先 field.publish 并 form.publish`);
|
|
162
|
+
const cloned = JSON.parse(JSON.stringify(base));
|
|
163
|
+
const targetControl = cloned[controlKey];
|
|
164
|
+
const targetOptions = (targetControl.options ?? {});
|
|
165
|
+
targetOptions.defaultValue = Array.isArray(targetOptions.defaultValue) ? [value] : value;
|
|
166
|
+
targetControl.options = targetOptions;
|
|
167
|
+
const draftViewRaw = detail.publishedViewJson ?? detail.draftViewJson;
|
|
168
|
+
const draftView = parseJsonColumn(draftViewRaw);
|
|
87
169
|
const draftOutcome = await runToolCall("form.draft", {
|
|
88
|
-
args: {
|
|
89
|
-
|
|
170
|
+
args: {
|
|
171
|
+
schemaCode: schema,
|
|
172
|
+
draftAttributesJson: JSON.stringify(cloned),
|
|
173
|
+
...(draftView !== undefined ? { draftViewJson: JSON.stringify(draftView) } : {}),
|
|
174
|
+
...(environment ? { environment } : {}),
|
|
175
|
+
},
|
|
176
|
+
requestId: request, environment, print: silent,
|
|
90
177
|
});
|
|
91
178
|
if (draftOutcome.exitCode !== 0) {
|
|
92
179
|
const classified = classifyResult(draftOutcome.result);
|
|
93
180
|
writeEnvelope(envelope(classified.status, draftOutcome.result, request, classified.error));
|
|
94
181
|
return draftOutcome.exitCode;
|
|
95
182
|
}
|
|
96
|
-
const publishOutcome = await runToolCall("form.publish", { args: { schemaCode: schema, ...(environment ? { environment } : {}) }, requestId: request, environment });
|
|
183
|
+
const publishOutcome = await runToolCall("form.publish", { args: { schemaCode: schema, ...(environment ? { environment } : {}) }, requestId: request, environment, print: silent });
|
|
97
184
|
if (publishOutcome.exitCode !== 0) {
|
|
98
185
|
const classified = classifyResult(publishOutcome.result);
|
|
99
186
|
writeEnvelope(envelope(classified.status, publishOutcome.result, request, classified.error));
|
|
100
187
|
return publishOutcome.exitCode;
|
|
101
188
|
}
|
|
102
189
|
// 3) 权威回读
|
|
103
|
-
const verify = await runToolCall("form.get", { args: { schemaCode: schema, controls: [field], ...(environment ? { environment } : {}) }, requestId: request, environment });
|
|
190
|
+
const verify = await runToolCall("form.get", { args: { schemaCode: schema, controls: [field], ...(environment ? { environment } : {}) }, requestId: request, environment, print: silent });
|
|
104
191
|
const verifyResult = verify.result ?? {};
|
|
105
192
|
const verifyControls = extractControls(verifyResult)?.controls ?? [];
|
|
106
193
|
const verifyControl = verifyControls.find((c) => String(c.key ?? c.dataItemName ?? "") === field);
|