@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.
@@ -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
- listview: ["列表", "视图", "查询列", "筛选", "表头", "甘特"],
195
- datarule: ["字段级规则", "控件规则", "校验规则", "计算拼接", "显示规则", "规则", "type1", "type2", "type3"],
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
- console.error(`--args 必须是合法 JSON:${error instanceof Error ? error.message : String(error)}(Windows PowerShell 下内联 JSON 引号转义不可靠,请改用 --args-file <path> 写入临时文件后引用)`);
331
- writeEnvelope(envelope("error", {}, request, { code: "invalid-input", message: error instanceof Error ? error.message : String(error), retryable: false }));
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
- if (argv[2] === "call" || argv[2] === "script" || argv[2] === "artifact" || argv[2] === "commands" || argv[2] === "describe" || argv[2] === "contract" || argv[2] === "capabilities" || argv[2] === "capability" || argv[2] === "next" || argv[2] === "usage" || argv[2] === "intent" || argv[2] === "help" || argv[2] === "--version" || argv[2] === "-V") {
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",
@@ -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
- const getAll = await runToolCall("form.get", { args: { schemaCode: schema, controls: true, ...(environment ? { environment } : {}) }, requestId: request, environment });
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 fullExtracted = extractControls(getAll.result ?? {});
84
- if (!fullExtracted)
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 fullControls = fullExtracted.controls.map((c) => (String(c.key ?? c.dataItemName ?? "") === field ? nextControl : c));
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: { schemaCode: schema, draftAttributesJson: JSON.stringify(fullControls), ...(environment ? { environment } : {}) },
89
- requestId: request, environment,
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);