@stalfh233/omc-cli 0.4.68 → 0.4.70
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/endpoints.js
CHANGED
|
@@ -55,6 +55,7 @@ export const ENDPOINTS = {
|
|
|
55
55
|
businessRuleCreate: "/api/api/app/businessrule/create",
|
|
56
56
|
businessRuleUpdate: "/api/api/app/businessrule/update",
|
|
57
57
|
businessRulePublish: "/api/api/app/businessrule/publish",
|
|
58
|
+
businessRuleNodeRunMap: "/api/api/app/businessrule/node_run_map",
|
|
58
59
|
businessRuleEnable: "/api/api/app/businessrule/enable",
|
|
59
60
|
businessRuleDelete: "/api/api/app/businessrule/delete",
|
|
60
61
|
dataRuleCreate: "/api/api/app/bizdatarule/create",
|
|
@@ -3,7 +3,7 @@ import { ENDPOINTS } from "../endpoints.js";
|
|
|
3
3
|
import { writeTool } from "../write.js";
|
|
4
4
|
import { registerPackageTool } from "../package-tool.js";
|
|
5
5
|
import { writeSummary } from "../human-summary.js";
|
|
6
|
-
import { executeRuleSaveWrite, ruleNodeGraphArg, assertRuleSchemaExists } from "../rule-support.js";
|
|
6
|
+
import { executeRuleSaveWrite, ruleNodeGraphArg, assertRuleSchemaExists, explainRuleSelfCheck } from "../rule-support.js";
|
|
7
7
|
import { ToolRefusalError } from "../tool-types.js";
|
|
8
8
|
import { prepareRuleGraph, validateRuleNodeGraph, PLATFORM_TRIGGER_RULE_CODES } from "./rule-graph.js";
|
|
9
9
|
import { wireCall, wireRows, WireError } from "../wire.js";
|
|
@@ -52,7 +52,25 @@ export async function executeRuleEnableWrite(session, args) {
|
|
|
52
52
|
const full = (await wireCall(session, { path: ENDPOINTS.businessRuleGet, query: { id: String(row.id) } })).data;
|
|
53
53
|
if (!full)
|
|
54
54
|
throw new WireError("business", `rule ${args.ruleCode} 完整模型读回为空(businessrule/get id ${row.id}),拒绝用列表投影发布`, 302024);
|
|
55
|
-
|
|
55
|
+
let published;
|
|
56
|
+
try {
|
|
57
|
+
published = await wireCall(session, { path: ENDPOINTS.businessRulePublish, method: "POST", body: { ...full, enabled: true, status: "ENABLED" } });
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
// 550018 is the platform's self-check refusal (see explainRuleSelfCheck).
|
|
61
|
+
if (error instanceof WireError && error.errcode === 550018) {
|
|
62
|
+
const detail = await explainRuleSelfCheck(session, { ...full, enabled: true, status: "ENABLED" });
|
|
63
|
+
throw new ToolRefusalError({
|
|
64
|
+
status: "refused",
|
|
65
|
+
blocker: "rule-self-check-failed",
|
|
66
|
+
schemaCode: args.schemaCode,
|
|
67
|
+
ruleCode: args.ruleCode,
|
|
68
|
+
...detail,
|
|
69
|
+
message: `规则 ${args.ruleCode} 启用时被平台自检拒绝(550018)。${detail.nodeChecks.length > 0 ? `问题节点:${JSON.stringify(detail.nodeChecks).slice(0, 600)}` : "先用 field.list(all:true) 核对引用的字段是否已发布。"}`,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
56
74
|
let enableErrcode = 0;
|
|
57
75
|
let alreadyEnabled = false;
|
|
58
76
|
try {
|
|
@@ -1048,22 +1048,14 @@ export function registerRuntimeDataLane(tools) {
|
|
|
1048
1048
|
draftObjectId = targetObjectId;
|
|
1049
1049
|
}
|
|
1050
1050
|
else if (formType === "2") {
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
if (!draftObjectId)
|
|
1059
|
-
throw new WireError("business", "submit chain: draft save returned no bizObjectId", 304001);
|
|
1060
|
-
// No post-save form/load (ticket 18): the submit persists
|
|
1061
|
-
// bizObject.data itself and updateBizObject merges patch-style onto
|
|
1062
|
-
// the stored row, and initSubmitBizObject.setOwner re-reads the
|
|
1063
|
-
// stored row's owner server-side — so formData + the saved id is the
|
|
1064
|
-
// complete submit payload.
|
|
1051
|
+
// Live 1.1.25 (r7-vehicle 2026-10-21): the previous chain did
|
|
1052
|
+
// form/save (creating a DRAFT row) and then submitted with that id —
|
|
1053
|
+
// the platform INSERTED A SECOND ROW instead of completing the first,
|
|
1054
|
+
// leaving one COMPLETED + one orphan DRAFT per submit (verified in
|
|
1055
|
+
// ia38g_veh_vehicle: same plateNo, same second, statuses COMPLETED and
|
|
1056
|
+
// DRAFT). form/submit persists bizObject.data itself (ticket 18), so
|
|
1057
|
+
// skip the pre-save and let the platform create+complete in one call.
|
|
1065
1058
|
payload.bizObject = {
|
|
1066
|
-
id: draftObjectId,
|
|
1067
1059
|
schemaCode: args.schemaCode,
|
|
1068
1060
|
sheetCode,
|
|
1069
1061
|
data: stripEngineOwnedKeys(formData),
|
|
@@ -77,7 +77,7 @@ const ruleBranchGraph = Object.freeze({
|
|
|
77
77
|
conditionalRequirements: [
|
|
78
78
|
"**系统节点顺序:BRANCH 必须排在 `SYSTEM_CREATE` 之后**——BRANCH 直接接在 START 后会运行时静默不生效、动作不回写(工具 fail closed)。注意这与 BIZ_ACTION 回写场景相反(那里系统节点要在动作之后)——本 recipe 是分支场景。**`SYSTEM_CREATE` 必须显式写**(工具对分支图的自动注入不可靠)。",
|
|
79
79
|
"**出口条件叶只 4 个键** `{currentSchemaDataItem(左值字段),ruleDataConditionType(操作符),ruleValueType,value}`;用 UI 键 `targetSchemaDataItem`+`ruleConditionType` 会 save+publish 过但运行时 50000(工具 fail closed `rule-graph-branch-condition-keys`)。",
|
|
80
|
-
"**下列键可省、工具自动补**(省 token;真机验证极简图 1170B vs 全显式 1690B,−31%):每个节点的 `nodeName`;BRANCH 的 `inputParam:\"_input\"`/`judgeMethod:\"DEFAULT\"`;自更新 UPDATE(`targetObjectCode`=本模型)的 `inputParam:\"_input\"`/`filterCondition`(身份匹配);非兜底路由的 `routeCondition:true`/`matchType`。仅 BRANCH 的 `sourceInputMethod` 与兜底 else 的 `routeCondition:false`
|
|
80
|
+
"**下列键可省、工具自动补**(省 token;真机验证极简图 1170B vs 全显式 1690B,−31%):每个节点的 `nodeName`;BRANCH 的 `inputParam:\"_input\"`/`judgeMethod:\"DEFAULT\"`;自更新 UPDATE(`targetObjectCode`=本模型)的 `inputParam:\"_input\"`/`filterCondition`(身份匹配);非兜底路由的 `routeCondition:true`/`matchType`。仅 BRANCH 的 `sourceInputMethod` 与兜底 else 的 `routeCondition:false` 需显式。**跨模型写(`targetObjectCode`≠规则模型)已真机验证可用**(1.1.25,2026-10-21:FIXED id / 文本字段 / 单跳关联字段三种 DYNAMIC 来源均命中)——前提是 ① 承载规则真的触发(Create 在提交时、Update 在 data.update、或绑流程事件);② **目标行必须已提交(COMPLETED)**,DRAFT 行不参与查询(日志「查找范围不存在数据」)。点号路径按子表解析:`关联字段.id` 与两跳 `a.b` 不可用。",
|
|
81
81
|
"预置规则的系统节点类型必须与 ruleCode 匹配(Create→SYSTEM_CREATE);放 SYSTEM_UPDATE 会 data.submit 50000(工具 fail closed)。",
|
|
82
82
|
],
|
|
83
83
|
priorToolReferences: [
|
package/dist/rule-support.js
CHANGED
|
@@ -463,6 +463,71 @@ export async function computeCarriedNodeCodes(session, schemaCode, ruleCode, pre
|
|
|
463
463
|
return undefined; // 取不到存量图 → 维持严格闭集,行为与放宽前一致
|
|
464
464
|
}
|
|
465
465
|
}
|
|
466
|
+
/**
|
|
467
|
+
* 550018 is the platform's catch-all "rule self-check failed" (ErrorCode
|
|
468
|
+
* BUSINESS_RULE_PARAM_ERROR from BusinessRuleFacadeImpl.publishBusinessRule when
|
|
469
|
+
* checkBizRuleIsEnable() is false). The message names nothing, and the real原因
|
|
470
|
+
* is one of: a referenced property that does not exist, a missing org object, or
|
|
471
|
+
* a dead cycle. `businessrule/node_run_map` (BusinessRuleController.getNodeRunMaps
|
|
472
|
+
* → checkBizRuleError) returns exactly that per node, so a refusal can name the
|
|
473
|
+
* offending node and field instead of asking the caller to open the designer.
|
|
474
|
+
*/
|
|
475
|
+
const RULE_SELF_CHECK_ERRCODE = 550018;
|
|
476
|
+
function ruleSelfCheckMessage(ruleCode, checks) {
|
|
477
|
+
if (checks.length === 0)
|
|
478
|
+
return `规则 ${ruleCode} 发布被平台自检拒绝(550018「保存规则失败,规则存在异常」),但 node_run_map 未给出具体节点——常见原因:引用的字段未发布/不存在、组织对象失效、或存在死循环。先用 field.list(all:true) 核对引用的字段是否已发布。`;
|
|
479
|
+
const detail = checks.map((check) => {
|
|
480
|
+
const parts = [];
|
|
481
|
+
const missing = check.missingProperties;
|
|
482
|
+
if (missing?.length)
|
|
483
|
+
parts.push(`字段不存在/未发布:${missing.map((item) => `${item.objectName ?? "?"}.${item.propertyName ?? item.propertyCode ?? "?"}`).join("、")}`);
|
|
484
|
+
if (check.missingOrgObjects)
|
|
485
|
+
parts.push("组织对象不存在");
|
|
486
|
+
if (check.deadCycles)
|
|
487
|
+
parts.push("存在死循环");
|
|
488
|
+
return `${String(check.nodeCode ?? "?")}${check.nodeName ? `(${String(check.nodeName)})` : ""} ${parts.join(";")}`;
|
|
489
|
+
});
|
|
490
|
+
return `规则 ${ruleCode} 发布被平台自检拒绝(550018):${detail.join(" | ")}。节点内引用了不存在或未发布的字段时,保存能过、发布必拒——先补字段并 field.publish,再重发。`;
|
|
491
|
+
}
|
|
492
|
+
export async function explainRuleSelfCheck(session, model) {
|
|
493
|
+
try {
|
|
494
|
+
const response = await wireCall(session, { path: ENDPOINTS.businessRuleNodeRunMap, method: "POST", body: model });
|
|
495
|
+
const rows = wireRows(response.data);
|
|
496
|
+
const nodeChecks = rows.flatMap((row) => {
|
|
497
|
+
// Wire shape (QuoteObjectVO): {objectCode, objectName, quotePropertyList:
|
|
498
|
+
// [{propertyCode, propertyName, exist, propertyPublished}]} — the property
|
|
499
|
+
// sits one level deeper than the object.
|
|
500
|
+
const missingProperties = Array.isArray(row.notExistPropertyObjectList)
|
|
501
|
+
? row.notExistPropertyObjectList.flatMap((object) => {
|
|
502
|
+
const properties = Array.isArray(object.quotePropertyList) ? object.quotePropertyList : [];
|
|
503
|
+
const broken = properties.filter((property) => property.exist === false || property.propertyPublished === false);
|
|
504
|
+
const source = broken.length > 0 ? broken : properties;
|
|
505
|
+
return source.map((property) => ({
|
|
506
|
+
objectName: object.objectName ?? object.objectCode ?? null,
|
|
507
|
+
propertyCode: property.propertyCode ?? null,
|
|
508
|
+
propertyName: property.propertyName ?? null,
|
|
509
|
+
published: property.propertyPublished ?? null,
|
|
510
|
+
}));
|
|
511
|
+
})
|
|
512
|
+
: [];
|
|
513
|
+
const missingOrgObjects = Array.isArray(row.notExistOrgObjectList) ? row.notExistOrgObjectList : [];
|
|
514
|
+
const deadCycles = Array.isArray(row.deadCycleRunMapList) ? row.deadCycleRunMapList : [];
|
|
515
|
+
if (missingProperties.length === 0 && missingOrgObjects.length === 0 && deadCycles.length === 0)
|
|
516
|
+
return [];
|
|
517
|
+
return [{
|
|
518
|
+
nodeCode: row.nodeCode ?? null,
|
|
519
|
+
nodeName: row.nodeName ?? null,
|
|
520
|
+
...(missingProperties.length > 0 ? { missingProperties } : {}),
|
|
521
|
+
...(missingOrgObjects.length > 0 ? { missingOrgObjects } : {}),
|
|
522
|
+
...(deadCycles.length > 0 ? { deadCycles } : {}),
|
|
523
|
+
}];
|
|
524
|
+
});
|
|
525
|
+
return { nodeChecks };
|
|
526
|
+
}
|
|
527
|
+
catch {
|
|
528
|
+
return { nodeChecks: [] };
|
|
529
|
+
}
|
|
530
|
+
}
|
|
466
531
|
export async function executeRuleSaveWrite(session, args) {
|
|
467
532
|
// Pre-flight dry lint (ticket 14): a mis-shaped graph must be refused BEFORE
|
|
468
533
|
// any wire call, with structured blockers locating the node/key — never
|
|
@@ -594,7 +659,23 @@ export async function executeRuleSaveWrite(session, args) {
|
|
|
594
659
|
// schedule and a TIMING_TASK rule never gets a quartz job (2026-10-06). Use
|
|
595
660
|
// the authoritative `businessrule/get?id` model (same as rule.get/enable).
|
|
596
661
|
const full = (await wireCall(session, { path: ENDPOINTS.businessRuleGet, query: { id: String(row.id) } })).data;
|
|
597
|
-
|
|
662
|
+
try {
|
|
663
|
+
await wireCall(session, { path: ENDPOINTS.businessRulePublish, method: "POST", body: full ?? row });
|
|
664
|
+
}
|
|
665
|
+
catch (error) {
|
|
666
|
+
if (error instanceof WireError && error.errcode === RULE_SELF_CHECK_ERRCODE) {
|
|
667
|
+
const detail = await explainRuleSelfCheck(session, (full ?? row));
|
|
668
|
+
throw new ToolRefusalError({
|
|
669
|
+
status: "refused",
|
|
670
|
+
blocker: "rule-self-check-failed",
|
|
671
|
+
schemaCode: args.schemaCode,
|
|
672
|
+
ruleCode: args.ruleCode,
|
|
673
|
+
...detail,
|
|
674
|
+
message: ruleSelfCheckMessage(args.ruleCode, detail.nodeChecks),
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
throw error;
|
|
678
|
+
}
|
|
598
679
|
}
|
|
599
680
|
const effective = await currentRow();
|
|
600
681
|
// Live 8.6.26 (2026-09-17): businessrule/publish mints a version but does
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stalfh233/omc-cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.70",
|
|
4
4
|
"description": "CloudPivot 8.6 configuration capability CLI — governed, fail-closed, coverage-manifest bounded.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -50,9 +50,9 @@
|
|
|
50
50
|
"access": "public"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@stalfh233/omc-fake-platform": "0.4.
|
|
53
|
+
"@stalfh233/omc-fake-platform": "0.4.70",
|
|
54
54
|
"commander": "14.0.3",
|
|
55
|
-
"omc-core": "0.4.
|
|
55
|
+
"omc-core": "0.4.70",
|
|
56
56
|
"proper-lockfile": "4.1.2",
|
|
57
57
|
"write-file-atomic": "6.0.0",
|
|
58
58
|
"zod": "^3.23.8",
|