@stalfh233/omc-cli 0.4.100 → 0.4.101

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/README.md CHANGED
@@ -28,6 +28,9 @@
28
28
  - **应用目录(分组)**——`folder.tree/create/update/delete` + `model.move`;
29
29
  应用树是嵌套 `children`,读面已递归展平并给 `path`/`depth`。
30
30
  - **方法论即可读**——`skills/` 下的方法论通过 `omc help <topic>` 按需读取;agent 不必去翻仓库文件。
31
+ - **一次调用就是答案(0.4.100)**——`omc help --search "<词>"` 跨全部 topic 全文检索、命中节正文内联(严禁自己 `--all | grep`);`omc describe <tool>` 默认带 `firstCall` 最小可跑形状(上下文键占位符化并列入 missing,绝不冒充合法值);`omc next "<目标>"` 首答只给一个可执行动作或一个问题(`firstAnswer`);`omc commands --filter` 支持能力语言(聚合/字典/业务集成…),零命中回 ≤3 个最近候选。
32
+ - **拒绝即教学**——写前拒绝信封自带「你错在哪、正确形状、同源配方命令、不要做什么」;平台/权限/环境类只给「先修什么」,不给注定失败的重试命令。
33
+ - **复合写命令**——`omc submit-verify`(逐条提交 → 真实 id 读回 → 差异汇总)、`omc rule-save-enable`(保存成功才启用,回执回答「会不会真的运行」)、`omc workflow-update-publish`(显式合成最终模板:默认保留旧节点,`--confirm-replace` 才丢弃)。全部默认 dry-run、第一步失败立即停、不自动重试。
31
34
  - **凭据零回显**——凭据(0600 存储或环境变量)不会出现在任何输出、产物或错误里。
32
35
  - **不确定性语义**——传输失败时冻结(`status: "uncertain", frozen: true`),
33
36
  绝不伪造成功。
@@ -130,10 +133,19 @@ omc call discover_environment --env <env> --args '{}' # 环境就绪(真实
130
133
  - **intent**:`partial` 表示该能力可声明式(`omc intent`)覆盖一部分——通常 create/update;
131
134
  `none` 表示只走 `omc call`。删除类、方法面、分类/连接池等仍走 `omc call` 逃生舱。
132
135
 
133
- ## 近期能力(0.4.74 → 0.4.88)
136
+ ## 近期能力(0.4.84 → 0.4.100)
134
137
 
135
138
  | 版本 | 新增 / 修复 |
136
139
  |---|---|
140
+ | 0.4.100 | **回合经济学批(r19–r20,同题面实测 184 调用/503KB → 84 调用/98KB)**:`omc help --search "<词>"` 全文检索(命中节正文内联,单节 >6KB 只给精确回放命令);`describe` 默认带 `firstCall`(offline-valid 最小可跑形状,`executable === missing为空`);`omc next` 首答 `firstAnswer`(ready=无占位符可跑 / needs-input=一个问题 / blocked=不伪造路径),`discover_environment` 退出 firstCommands 首位、章节指针 ≤1 且非通用草稿模板、输出 ≤4096B;`commands --filter` 语义检索(工具名/能力别名/配方/摘要,大小写与分隔符归一;零命中最小信封+≤3 候选;宽词按能力分组 ≤3.5KB);拒绝即教学(拒绝信封自带正确形状+与 firstCall 同源的配方命令+不要做什么;平台/权限类只给先修什么);三条复合写命令 `submit-verify` / `rule-save-enable` / `workflow-update-publish`(默认 dry-run、第一步失败立即停、合并语义显式);`OMC_NO_USAGE=1` 计量隔离(读侧不受影响);报表能力按裁决暂缓(报表类目标 boundary 首答;`/dashboard/v1/*` 502 教学「环境边界不要重试」;`report.create` 半成功显式警示+清理命令) |
141
+ | 0.4.99 | 聚合算法三态实测定案:`LARGE/SMALL` 必带 k(缺 → 50000 掩码)、`UNION` 的 `resultType` 只能 0/1;审批提交**顶层 `agree` 才有效**(`approval.agree/opinion` 被静默忽略且仍按通过处理——写前拒绝 + `approvalNote` 顶层参数);聚合事件回算窗口 ≈3 分钟实测(`field.publish --force` 重置) |
142
+ | 0.4.98 | 聚合指向子表 schema 写前拒绝(会让目标模型整体 40000 无明细且匹配恒 0);`40000` 裸掩码给排查解释(先疑字段级配置);`omc next` 造数据场景路由 |
143
+ | 0.4.97 | 三个写前 gate:日期毫秒时间戳(落库 NULL)、单值关联传数组(50000)、更新时无 id 子表行按新增插入不判重(实测 2 行变 8 行);跨模型聚合行为图谱(混用条件/多组 OR/COUNTIF/MAX/来源删除回算) |
144
+ | 0.4.96 | 聚合定案(反编译 1.1.25 运行版 jar):`propertyType/operatorType/resultType` 等 `@JsonCreator get(int)` 枚举**只认整数码**(写名字 → 305003 掩码,CLI 自动转码);同模型子表聚合是平台死路(`checkCycleOfAggregation:1032` 判环,`field.create` 接受、`field.publish` 炸)→ 写前拒绝;跨模型汇总配方 `formula-aggregation-cross-model`(DYNAMIC 条件两侧字段分属两模型、`queryConditions` 不能为空) |
145
+ | 0.4.95 | `omc compute` 诚实化:`datarule` type2(shortTextStitch)在 REST 路径**永不执行**(`CalculateHandler` 由 `forceExecuteCalculateRule` 门控、全库 0 调用方;门户「能算」是客户端 JS 求值器)——不再假装可配 |
146
+ | 0.4.94 | QUERY 输出不可寻址写前拒绝;`100005` 判「同 workItem 已提交」(永久,非时间窗) |
147
+ | 0.4.92 | `rule.run` 真门(此前是假成功);custompage 八工具 |
148
+ | 0.4.89/90 | 修 0.4.88 DRAFT 实例回归;`/organization/department/tree` 深度 BFS(deptIds 只回直接子级);组织树参与人解析按 `ouScope ∩ 发起人部门链`(全空=静默回退发起人 → 写前拒绝);结构化 nextCommand |
137
149
  | 0.4.88 | **`omc model-purge`**:一条命令按依赖倒序清掉一个模型(流程实例 → model.delete 级联带走流程/视图/字段/表单/规则)。这是 r12–r14 三次「脏沙盒幽灵故障」的根治——顺手修了它的死锁(视图最后一条不能删 306103 + 列表绑定流程 303024,所以不删流程、让模型级联)。另:ladder 的 **recipe 命令提到 firstCommands**(r14 抱怨「ladder 没给可直接跑的命令」)、recipe 增加 `aliases` 意图词、`listview.configure` 的拒绝恢复补上 `targetCode` 按 bindAction 的语义 |
138
150
  | 0.4.87 | **r14 真机探针修复**:`workitem.list` 的 50000 **不再是无解题**——按 `participant` 走 JPA 查询端点(能查**别人**名下的待办、且不解析流程模板),全局搜索失败时回 `workitem-search-unavailable` 带成因与三条绕行;`workflow.delete` 的 402508 改 `workflow-has-instances` 并说明**不要改用 model.delete 绕过**;`model.delete` 在有未完结待办时回 `workflowWarning`(级联会留脏待办 → 全局待办搜索整页 NPE)。另修两个 composer 模板参数名写错(`form-default --value`→`--default`、`rule-when --rule`→`--on`),并加**模板 vs `--help` 自动校验测试** |
139
151
  | 0.4.86 | **r13 真机探针修复**:`omc next` 增加 **composer 路由**(含建模型+字段/审批流/计算/条件规则/表单默认值 → 直接给出对应一条命令;读面仍走 ladder,条件分支流程仍走 recipe)——r13 读了文档里那行 `model-scaffold` 却仍调 26 次 `field.create`,说明「写进文档」不等于「能被发现」。`data.submit` 审批提交**写前校验身份**:非本人在办的待办直接回 `submit-workitem-not-mine`(带待办持有者 + 三条出路),不再让平台回一个 `40005 权限不足`让 agent 猜 |
@@ -1240,7 +1240,7 @@
1240
1240
  {
1241
1241
  "capability": "rule.list latest business-rule listing and rule.get node route definition readback",
1242
1242
  "status": "executable",
1243
- "scope": "GET /api/api/runtime/business_rule/list_by_schemacode returning the full latest-version business-field set (no server-side filters); GET /api/api/app/businessrule/get returning the saved node and route graph for readback",
1243
+ "scope": "GET /api/api/runtime/business_rule/list_by_schemacode returning the full latest-version business-field set (no server-side filters); GET /api/api/app/businessrule/get returning the saved node and route graph for readback; cross-rule reference search over model.list + businessrule list/get scans (rule.references)",
1244
1244
  "version_family": "8.6",
1245
1245
  "anchor_version": "8.6.26",
1246
1246
  "evidence": [
@@ -1251,16 +1251,22 @@
1251
1251
  {
1252
1252
  "path": "packages/omc-cli/test/rule-list.test.ts",
1253
1253
  "anchor": "contract tests"
1254
+ },
1255
+ {
1256
+ "path": "packages/omc-cli/test/rule-references.test.ts",
1257
+ "anchor": "contract tests"
1254
1258
  }
1255
1259
  ],
1256
1260
  "lane": "B1-businessrule",
1257
1261
  "tools": [
1258
1262
  "rule.list",
1259
- "rule.get"
1263
+ "rule.get",
1264
+ "rule.references"
1260
1265
  ],
1261
1266
  "scopes": [
1262
1267
  "latest rule business-field listing",
1263
- "rule node route readback"
1268
+ "rule node route readback",
1269
+ "cross-rule reference search"
1264
1270
  ]
1265
1271
  },
1266
1272
  {
@@ -220,7 +220,11 @@ export function classifyResult(result) {
220
220
  return { status: "error", error: withNext({ code: "invalid-input", message: `unknown tool ${String(result.tool ?? "")}${suffix}`, retryable: false }), exitCode: CLI_EXIT_CODES.invalidInput };
221
221
  }
222
222
  if (result.blocker === "tool-input-invalid")
223
- return { status: "error", error: withNext({ code: "invalid-input", message: String(result.message ?? "tool input is invalid"), retryable: false }), exitCode: CLI_EXIT_CODES.invalidInput };
223
+ return {
224
+ status: "error",
225
+ error: { ...withNext({ code: "invalid-input", message: String(result.message ?? "tool input is invalid"), retryable: false }), nextCommand: `omc describe ${String(result.tool ?? "<tool>")}` },
226
+ exitCode: CLI_EXIT_CODES.invalidInput,
227
+ };
224
228
  if (status === "uncertain")
225
229
  return { status, error: withNext({ code: "uncertain", message: String(result.message ?? result.reason ?? "outcome is uncertain"), retryable: true }), exitCode: CLI_EXIT_CODES.uncertain };
226
230
  if (status === "refused") {
package/dist/discovery.js CHANGED
@@ -182,6 +182,13 @@ function terseType(node) {
182
182
  const label = `${node.type ?? "string"}: ${values.join("|")}`;
183
183
  return label.length > 80 ? `${node.type ?? "string"} (enum)` : label;
184
184
  }
185
+ // 券商 issue 1.2(2026-10-27):union 渲染成 "any" 曾让调用方以为 controls 收
186
+ // 任意值、直觉传逗号串被 invalid_union 拒。anyOf 如实标注成员类型。
187
+ if (Array.isArray(node.anyOf) && node.anyOf.length > 0) {
188
+ const members = [...new Set(node.anyOf.map((member) => terseType(member)))];
189
+ const label = members.join("|");
190
+ return label.length > 60 ? `(${label.slice(0, 57)}...)` : label;
191
+ }
185
192
  if (node.type === "array")
186
193
  return `array<${terseType(node.items)}>`;
187
194
  return node.type ?? "any";
@@ -556,6 +556,38 @@ export function goalRoute(registry, goal) {
556
556
  // Composer before the capability solver: a recurring write SHAPE has a single
557
557
  // command that does the job with preflight + idempotency, which beats a ladder
558
558
  // of atomic tools even when the ladder also matches (r13: 26 field.create).
559
+ // r19 券商 issue 1.1(2026-10-27):子表**控件**(type 201)的显示/只读条件
560
+ // (displayFormula/readonlyFormula)不在字段面——field.update 对子表只管**列**、
561
+ // 对控件条件必拒;唯一路径是表单 attributes(form.get 读 draftAttributesJson
562
+ // 定位控件 → form.draft 改 → form.publish)。此前该场景路由进 field→form 梯子
563
+ // 且首级无可行工具(firstCommands 为空),agent 只能靠拒收文案自己悟。
564
+ if (/子表|控件/.test(goal) && /子表.*(显示|隐藏|只读|显隐).*(条件|公式|规则)|displayFormula|readonlyFormula/.test(goal)) {
565
+ return {
566
+ status: "ok",
567
+ goal,
568
+ confident: true,
569
+ mode: "modify",
570
+ write: true,
571
+ match: { kind: "capability", ref: "form", title: "子表控件显示/只读条件(表单 attributes 面)" },
572
+ route: {
573
+ capability: "form",
574
+ note: "子表控件的 displayFormula/readonlyFormula 走表单 attributes:form.get(controls:true) 读 draftAttributesJson 定位控件 → form.draft 改该控件条件 → form.publish。field.update 只管子表列,对控件条件必拒(券商 issue 2026-10-27 实证)",
575
+ readiness: DISCOVERY_AUTHORITY.readiness,
576
+ firstCommands: [
577
+ "omc call form.get --args '{\"schemaCode\":\"<schemaCode>\",\"controls\":true}'",
578
+ "omc describe form.draft",
579
+ ],
580
+ write: true,
581
+ },
582
+ firstAnswer: {
583
+ state: "needs-input",
584
+ question: "目标模型 schemaCode 与子表控件 code 是什么?(form.get controls:true 读 draftAttributesJson 定位控件,form.draft 改其条件后 form.publish)",
585
+ action: LIST_MODELS_ACTION,
586
+ pendingCommand: "omc call form.draft --args '{\"schemaCode\":\"<schemaCode>\",\"draftAttributesJson\":\"<改好后的完整 attributes JSON>\"}'",
587
+ why: "子表控件(type 201)的显示/只读条件在表单 attributes,不在字段面——field.update 对子表只管列、必拒",
588
+ },
589
+ };
590
+ }
559
591
  const composer = matchComposer(goal, mode);
560
592
  if (composer) {
561
593
  const topics = (composer.topics ?? []).filter((topic) => availableTopics.has(topic));
@@ -20,7 +20,7 @@ export async function admitGovernedWrite(ctx, request) {
20
20
  blocker: "environment-write-disabled",
21
21
  environment: resolved.name,
22
22
  production: resolved.profile.production === true,
23
- message: `${environmentNotice(resolved.profile)} 请在 omc setup 中开启“允许写入此环境”。`,
23
+ message: `${environmentNotice(resolved.profile)} 请在 omc setup 中开启“允许写入此环境”。若你本意是写项目环境:检查当前目录是否项目根(.omc-env 所在目录)——在其它目录跑 omc 会解析不到项目环境而落到全局只读策略,cd 回项目根再试。`,
24
24
  });
25
25
  let session;
26
26
  try {
@@ -416,7 +416,7 @@ export function registerB2Lane(tools) {
416
416
  });
417
417
  const afterRows = wireRows((await wireCall(session, { path: ENDPOINTS.bizServiceMethodList, query: { serviceCode: args.serviceCode, filterFlag: "false" } })).data);
418
418
  const after = afterRows.find((row) => row.code === args.methodCode);
419
- return { performed: { errcode: performed.errcode }, readback: { methodPresent: after != null, nameMatches: args.name === undefined || after?.name === args.name, configMatches: methodConfigPlain === undefined || after?.configJson === methodConfigPlain, inputParameters: decodeJsonColumn(after?.inputParametersJson), outputParameters: decodeJsonColumn(after?.outputParametersJson) } };
419
+ return { performed: { errcode: performed.errcode }, readback: { methodCode: args.methodCode, methodPresent: after != null, nameMatches: args.name === undefined || after?.name === args.name, configMatches: methodConfigPlain === undefined || after?.configJson === methodConfigPlain, inputParameters: decodeJsonColumn(after?.inputParametersJson), outputParameters: decodeJsonColumn(after?.outputParametersJson), mappingNote: "规则映射直接用这里的参数 code(bizPropertyType 供类型对账),不必再 service.get" } };
420
420
  }, (args) => {
421
421
  assertStructuredConfig(args.methodConfig, "methodConfig");
422
422
  }, undefined, b2ServiceRollbackSpec("method.update"));
@@ -463,7 +463,7 @@ export function registerB2Lane(tools) {
463
463
  });
464
464
  const afterRows = wireRows((await wireCall(session, { path: ENDPOINTS.bizServiceMethodList, query: { serviceCode: args.serviceCode, filterFlag: "false" } })).data);
465
465
  const after = afterRows.find((row) => row.code === args.methodCode);
466
- return { performed: { errcode: performed.errcode }, readback: { methodPresent: after != null, nameMatches: after?.name === args.name, configMatches: methodConfigPlain === undefined || after?.configJson === methodConfigPlain, inputParameters: decodeJsonColumn(after?.inputParametersJson), outputParameters: decodeJsonColumn(after?.outputParametersJson) } };
466
+ return { performed: { errcode: performed.errcode }, readback: { methodCode: args.methodCode, methodPresent: after != null, nameMatches: after?.name === args.name, configMatches: methodConfigPlain === undefined || after?.configJson === methodConfigPlain, inputParameters: decodeJsonColumn(after?.inputParametersJson), outputParameters: decodeJsonColumn(after?.outputParametersJson), mappingNote: "规则映射直接用这里的参数 code(bizPropertyType 供类型对账),不必再 service.get" } };
467
467
  }, (args) => {
468
468
  assertStructuredConfig(args.methodConfig, "methodConfig");
469
469
  }, undefined, b2ServiceRollbackSpec("method.create"));
@@ -850,9 +850,26 @@ export function registerB2Lane(tools) {
850
850
  ? wireRows((await wireCall(session, { path: ENDPOINTS.bizServiceMethodList, query: { serviceCode: args.serviceCode, filterFlag: "false" } })).data)
851
851
  : [];
852
852
  const registered = args.methodName ? methodRows.some((row) => row.code === args.methodName) : undefined;
853
+ // r19 p7:readback 只回布尔时,agent 只能再调 service.get 拿参数 code(12 次里
854
+ // 6 次是这种写后验证读)。方法视图内联进 readback,rule.save 的 methodMapping
855
+ // 直接用这里的 code,不必再读。
856
+ const methodRow = args.methodName ? methodRows.find((row) => row.code === args.methodName) : undefined;
853
857
  return {
854
858
  performed: { errcode: performed.errcode },
855
- readback: { servicePresent: readback.data != null, ...(registered !== undefined ? { methodPresent: registered, methodCount: methodRows.length } : {}) },
859
+ readback: {
860
+ servicePresent: readback.data != null,
861
+ ...(registered !== undefined ? { methodPresent: registered, methodCount: methodRows.length } : {}),
862
+ ...(methodRow !== undefined
863
+ ? {
864
+ method: {
865
+ code: methodRow.code,
866
+ inputParameters: decodeJsonColumn(methodRow.inputParametersJson),
867
+ outputParameters: decodeJsonColumn(methodRow.outputParametersJson),
868
+ },
869
+ mappingNote: "rule.save 的 methodMapping 直接用上面的参数 code:inputMappings.bizCode=来源字段 code、serviceMethodParameterCode=参数 code;不必再 service.get",
870
+ }
871
+ : {}),
872
+ },
856
873
  };
857
874
  }, (args) => {
858
875
  // Dry-lint (zero wire). `omc call` bypasses zod, so every schema-level
@@ -151,6 +151,10 @@ export const BUSINESSRULE_LANE_CONTRACT = Object.freeze({
151
151
  scope: "rule node route readback",
152
152
  description: "读回一条业务规则的完整定义(businessrule/get?id=),解析并回显 node/route 图 JSON(触发节点、UPDATE/CREATE 动作节点的 targetObjectCode/inputParam/dataActions/filterCondition 等)——规则族读写链的权威读回面,也是对照设计器生成图形的取证入口。",
153
153
  }),
154
+ "rule.references": Object.freeze({
155
+ scope: "cross-rule reference search",
156
+ description: "跨规则引用检索(r23 实证的能力缺失:agent 为查『谁引用了服务 X』枚举了全平台 147 个 schema×ruleCode 组合/171 次 rule.get——本工具一调替代)。kind=service 查 BIZ_ACTION 的 methodMapping.serviceCode;kind=model 查 CREATE/UPDATE/DELETE 的 targetObjectCode 与 methodMapping.schemaCode(删模型前的影响面);kind=field 查 dataActions.targetDataItem 与 inputMappings/outputMappings 的 bizCode(删字段前的影响面;字段 code 跨模型不唯一,建议配 schemaCodes 限范围)。实现为客户端聚合(model.list→rule.list→rule.get 全扫,约几十次 wire 读、几秒完),输出只回命中行。第一版不含:条件叶子里的字段引用、datarule 引用、workflow.bindRule 绑定。",
157
+ }),
154
158
  "rule.save": Object.freeze({
155
159
  scope: "dead-state CRUD conditional node graphs",
156
160
  description: RULE_SAVE_DESCRIPTION,
@@ -10,7 +10,7 @@ export const BUSINESSRULE_COVERAGE = declareLaneCoverage(BUSINESSRULE_LANE_CONTR
10
10
  {
11
11
  capability: "rule.list latest business-rule listing and rule.get node route definition readback",
12
12
  evidence: ["packages/omc-cli/src/tools.ts", "packages/omc-cli/test/rule-list.test.ts"],
13
- tools: [claim("rule.list", T["rule.list"].scope), claim("rule.get", T["rule.get"].scope)],
13
+ tools: [claim("rule.list", T["rule.list"].scope), claim("rule.get", T["rule.get"].scope), claim("rule.references", T["rule.references"].scope)],
14
14
  },
15
15
  ]);
16
16
  //# sourceMappingURL=businessrule-coverage.js.map
@@ -116,6 +116,99 @@ export function registerBusinessRuleLane(tools) {
116
116
  };
117
117
  }, { full: args.full === true, target: `rule.list:${args.schemaCode}` }),
118
118
  }));
119
+ tools.push(defineTool({
120
+ name: "rule.references",
121
+ layer: "low",
122
+ kind: "read",
123
+ lane: RULE_LANE,
124
+ scope: RULE_TOOLS["rule.references"].scope,
125
+ description: RULE_TOOLS["rule.references"].description,
126
+ inputSchema: {
127
+ environment: environmentArg,
128
+ kind: z.enum(["service", "model", "field"]).describe("按什么找引用:service=BIZ_ACTION methodMapping.serviceCode;model=CREATE/UPDATE/DELETE targetObjectCode 与 methodMapping.schemaCode;field=dataActions.targetDataItem 与映射 bizCode"),
129
+ code: z.string().min(1).describe("要查引用的编码(服务 code / 模型 code / 字段 code)"),
130
+ schemaCodes: z.union([z.array(z.string().min(1)), z.string()]).optional().describe("只扫这些模型(省 wire 读);省略=全平台模型"),
131
+ capability: capabilityArg,
132
+ },
133
+ handler: async (ctx, args, self) => governedRead(ctx, self, args, async (session) => {
134
+ // r23(2026-10-27):agent 为查「谁引用了服务 X」枚举了 147 个
135
+ // schema×ruleCode 组合(171 次 rule.get / 229KB)。没有引用检索面时,
136
+ // 全库枚举是理性选择——所以给它一个。客户端聚合:模型清单 → 每模型
137
+ // rule.list → 每规则 rule.get → 图内匹配,一次调用、只回命中行。
138
+ const scoped = args.schemaCodes === undefined
139
+ ? wireRows((await wireCall(session, { path: ENDPOINTS.modelList })).data).map((row) => String(row.schemaCode ?? "")).filter((code) => code !== "")
140
+ : (Array.isArray(args.schemaCodes) ? args.schemaCodes : [args.schemaCodes]);
141
+ const wanted = args.code;
142
+ const matches = [];
143
+ let scannedRules = 0;
144
+ let parseFailures = 0;
145
+ for (const schemaCode of scoped) {
146
+ let rows = [];
147
+ try {
148
+ rows = wireRows((await wireCall(session, { path: ENDPOINTS.businessRuleListBySchemaCode, query: { schemaCode } })).data);
149
+ }
150
+ catch {
151
+ continue; // 模型无规则面(读失败/空)按无规则处理,不让单个模型挡住全扫
152
+ }
153
+ for (const row of rows) {
154
+ const ruleCode = String(row.code ?? "");
155
+ const ruleId = row.id;
156
+ scannedRules += 1;
157
+ if (typeof ruleId !== "string" || ruleId === "")
158
+ continue;
159
+ let record = null;
160
+ try {
161
+ record = (await wireCall(session, { path: ENDPOINTS.businessRuleGet, query: { id: ruleId } })).data;
162
+ }
163
+ catch {
164
+ continue;
165
+ }
166
+ if (!record)
167
+ continue;
168
+ const nodes = decodeJsonColumn(record.node, JSON_COLUMN_NULL_COALESCE);
169
+ if (!Array.isArray(nodes)) {
170
+ parseFailures += 1;
171
+ continue;
172
+ }
173
+ for (const rawNode of nodes) {
174
+ const nodeCode = String(rawNode.nodeCode ?? "");
175
+ const nodeType = String(rawNode.nodeType ?? "");
176
+ const mapping = rawNode.methodMapping;
177
+ if (args.kind === "service") {
178
+ if (nodeType === "BIZ_ACTION" && mapping && String(mapping.serviceCode ?? "") === wanted)
179
+ matches.push({ schemaCode, ruleCode, nodeCode, nodeType, via: "methodMapping.serviceCode" });
180
+ }
181
+ else if (args.kind === "model") {
182
+ if (["CREATE", "UPDATE", "DELETE"].includes(nodeType) && String(rawNode.targetObjectCode ?? "") === wanted)
183
+ matches.push({ schemaCode, ruleCode, nodeCode, nodeType, via: "targetObjectCode" });
184
+ if (mapping && String(mapping.schemaCode ?? "") === wanted)
185
+ matches.push({ schemaCode, ruleCode, nodeCode, nodeType, via: "methodMapping.schemaCode" });
186
+ }
187
+ else {
188
+ if (["CREATE", "UPDATE", "DELETE"].includes(nodeType) && Array.isArray(rawNode.dataActions)
189
+ && rawNode.dataActions.some((action) => String(action.targetDataItem ?? "") === wanted))
190
+ matches.push({ schemaCode, ruleCode, nodeCode, nodeType, via: "dataActions.targetDataItem" });
191
+ if (mapping) {
192
+ for (const [key, label] of [["inputMappings", "inputMappings.bizCode"], ["outputMappings", "outputMappings.bizCode"]]) {
193
+ if (Array.isArray(mapping[key]) && mapping[key].some((row2) => String(row2.bizCode ?? "") === wanted && (row2.codeType === undefined || row2.codeType === 1)))
194
+ matches.push({ schemaCode, ruleCode, nodeCode, nodeType, via: label });
195
+ }
196
+ }
197
+ }
198
+ }
199
+ }
200
+ }
201
+ return {
202
+ kind: args.kind,
203
+ code: wanted,
204
+ scanned: { models: scoped.length, rules: scannedRules, ...(parseFailures > 0 ? { parseFailures } : {}) },
205
+ matches,
206
+ ...(matches.length === 0
207
+ ? { note: "零命中=全平台(或指定 schemaCodes 范围内)没有规则引用它。范围:service→methodMapping.serviceCode;model→targetObjectCode/methodMapping.schemaCode;field→dataActions.targetDataItem/映射 bizCode(不含条件叶子与 datarule,第一版边界)" }
208
+ : {}),
209
+ };
210
+ }, { target: `rule.references:${args.kind}:${args.code}` }),
211
+ }));
119
212
  tools.push(defineTool({
120
213
  name: "rule.get",
121
214
  layer: "low",
@@ -333,7 +333,16 @@ export function registerFieldLane(tools) {
333
333
  ...(selector.length > 0 ? { selected: selector } : {}),
334
334
  ...(args.all !== true ? { businessOnly: true } : {}),
335
335
  fields: projected,
336
- ...(detailed ? {} : { inventory: true, hint: '字段详情(options/subSchema)用 code/fields 选择器或 full:true' }),
336
+ // 券商 issue 1.4(2026-10-27):默认面空清单曾被误读成「模型没有字段」。
337
+ // 空清单要自己解释三种可能,别让 agent 二次试错。
338
+ ...(detailed
339
+ ? {}
340
+ : {
341
+ inventory: true,
342
+ hint: projected.length === 0
343
+ ? "字段详情(options/subSchema)用 code/fields 选择器或 full:true。当前 draft 面没有业务字段——字段可能都在 published 面(published:true)、尚未创建,或只有系统字段(all:true 核对)"
344
+ : "字段详情(options/subSchema)用 code/fields 选择器或 full:true",
345
+ }),
337
346
  };
338
347
  }, {
339
348
  // Round 4: the default diet reduced the whole list to a count, so agents
@@ -50,7 +50,7 @@ export function registerFormLane(tools) {
50
50
  controls: z
51
51
  .union([z.boolean(), z.array(z.string().min(1))])
52
52
  .optional()
53
- .describe("控件定义投影:true = 返回全部控件的 draft/published 定义;字符串数组 = 只返回这些控件码的定义;省略 = 只回控件码清单"),
53
+ .describe("布尔或字符串数组:true=返回全部控件的 draft/published 定义;数组如 [\"controlA\",\"controlB\"]=只回这些控件码的定义;省略=只回控件码清单。注意:逗号拼出的单个字符串不是合法取值(invalid_union)——多控件用 JSON 数组。要控件的 options/subSchema 时改用 full:true 再 artifact get --jq <field> 取单字段"),
54
54
  full: z.boolean().optional().describe("返回全部六份布局 JSON 原文(大体积)"),
55
55
  buttonScripts: z.boolean().optional().describe("附带表单按钮的脚本原文(options.customButtonJson 里 SCRIPT 按钮的 actionConfig.scriptData)及 sha/字节数"),
56
56
  capability: capabilityArg,
@@ -148,7 +148,7 @@ export function registerFormLane(tools) {
148
148
  writeTool(tools, "form.draft", FORM_LANE, FORM_TOOLS["form.draft"].scope, FORM_TOOLS["form.draft"].description, {
149
149
  schemaCode: z.string().min(1),
150
150
  sheetCode: z.string().min(1).optional().describe("表单槽位码;省略时默认 = schemaCode(模型码即默认表单槽位)"),
151
- draftViewJson: z.string().optional(),
151
+ draftViewJson: z.string().optional().describe("行分组:一行 N 个 code=N 列;改列数须改行分组,非改 span"),
152
152
  draftAttributesJson: z.string().optional(),
153
153
  draftHtmlJson: z.string().optional(),
154
154
  fieldCodes: z.array(z.string().min(1)).optional().describe("脚手架绑定的已发布字段(按此顺序);由 configure_form/intent 显式传入"),
@@ -1686,6 +1686,34 @@ function sanitizeWorkflowSnapshotForReplay(row) {
1686
1686
  }),
1687
1687
  };
1688
1688
  }
1689
+ /**
1690
+ * 2026-10-27 用户需求:创建流程时流程名默认与表单名一致。
1691
+ *
1692
+ * 解析链(全部真实读回,不捏造):
1693
+ * ① 显式 name → "args"
1694
+ * ② 该模型默认表单的名字(bizsheet/get 的 name 字段)→ "form-name"
1695
+ * ③ 模型名(bizmodels/list_schema 行的 name)→ "model-name"
1696
+ * ④ 都读不到(模型不存在/无名字段)→ 拒绝:让调用方显式给 name,
1697
+ * 绝不拿 schemaCode 冒充流程名(占位符冒充合法值是形状红线)。
1698
+ */
1699
+ async function resolveWorkflowName(session, explicitName, schemaCode) {
1700
+ if (typeof explicitName === "string" && explicitName.trim().length > 0)
1701
+ return { name: explicitName, resolvedFrom: "args" };
1702
+ const sheet = await wireCall(session, { path: ENDPOINTS.sheetGet, query: { schemaCode, sheetCode: schemaCode } }).catch(() => null);
1703
+ const sheetName = sheet?.data?.name;
1704
+ if (typeof sheetName === "string" && sheetName.trim().length > 0)
1705
+ return { name: sheetName, resolvedFrom: "form-name" };
1706
+ const listed = await wireCall(session, { path: ENDPOINTS.modelList, method: "POST", body: { schemaCodes: [schemaCode] } }).catch(() => null);
1707
+ const rows = wireRows(listed?.data);
1708
+ const modelName = rows.find((row) => String(row.schemaCode ?? "") === schemaCode)?.name;
1709
+ if (typeof modelName === "string" && modelName.trim().length > 0)
1710
+ return { name: modelName, resolvedFrom: "model-name" };
1711
+ throw new ToolRefusalError({
1712
+ status: "refused",
1713
+ blocker: "invalid-input",
1714
+ message: `流程 name 缺省解析失败:模型 ${schemaCode} 读不到表单名或模型名(模型不存在?)。显式传 name,或先确认模型存在(omc call model.list --args '{"filter":"<名或码>"}')`,
1715
+ });
1716
+ }
1689
1717
  function workflowRollbackSpec(toolName) {
1690
1718
  return {
1691
1719
  target: (args) => `workflow:${String(args.schemaCode ?? "")}/${String(args.code ?? args.workflowCode ?? "")}`,
@@ -2121,7 +2149,13 @@ export function registerWorkflowLane(tools) {
2121
2149
  return { workflowCode, valid: errors.length === 0, errorCount: errors.length, warningCount: warnings.length, errors, warnings };
2122
2150
  }, { target: `workflow.validate:${args.workflowCode ?? args.schemaCode}` }),
2123
2151
  }));
2124
- writeTool(tools, "workflow.create", WORKFLOW_LANE, WORKFLOW_TOOLS["workflow.create"].scope, WORKFLOW_TOOLS["workflow.create"].description, { schemaCode: z.string().min(1), name: z.string().min(1), code: z.string().min(1) }, async (session, args) => {
2152
+ writeTool(tools, "workflow.create", WORKFLOW_LANE, WORKFLOW_TOOLS["workflow.create"].scope, WORKFLOW_TOOLS["workflow.create"].description, {
2153
+ schemaCode: z.string().min(1),
2154
+ // 2026-10-27 用户需求:流程名默认与表单名一致。省略时的解析链见
2155
+ // resolveWorkflowName——表单名 → 模型名 → 拒绝(不拿 schemaCode 冒充)。
2156
+ name: z.string().min(1).optional().describe("流程名称;省略时默认 = 该模型默认表单的名字(无表单草稿时用模型名),与设计器行为一致"),
2157
+ code: z.string().min(1),
2158
+ }, async (session, args) => {
2125
2159
  // Ticket 76 idempotent retry: get_header FIRST — a post-timeout
2126
2160
  // re-create of the same code short-circuits with the existing header
2127
2161
  // and ZERO create wires. On the happy path this read REPLACES the
@@ -2131,12 +2165,13 @@ export function registerWorkflowLane(tools) {
2131
2165
  if (existingHeader != null) {
2132
2166
  return { performed: { skipped: "already-present" }, readback: { alreadyPresent: true, header: existingHeader } };
2133
2167
  }
2168
+ const { name: workflowName, resolvedFrom } = await resolveWorkflowName(session, args.name, args.schemaCode);
2134
2169
  let performed;
2135
2170
  try {
2136
2171
  performed = await wireCall(session, {
2137
2172
  path: ENDPOINTS.workflowCreate,
2138
2173
  method: "POST",
2139
- body: { workflowCode: args.code, workflowName: args.name, schemaCode: args.schemaCode, icon: "h-icon-all-logic" },
2174
+ body: { workflowCode: args.code, workflowName, schemaCode: args.schemaCode, icon: "h-icon-all-logic" },
2140
2175
  });
2141
2176
  }
2142
2177
  catch (error) {
@@ -2163,7 +2198,7 @@ export function registerWorkflowLane(tools) {
2163
2198
  const draft = await getWorkflowDraftOrNull(session, args.code);
2164
2199
  return {
2165
2200
  performed: { errcode: performed.errcode },
2166
- readback: { workflowCode: args.code, headerPresent: true, draftPresent: draft != null, templateId: draft?.id ?? null },
2201
+ readback: { workflowCode: args.code, name: workflowName, nameResolvedFrom: resolvedFrom, headerPresent: true, draftPresent: draft != null, templateId: draft?.id ?? null },
2167
2202
  };
2168
2203
  }, undefined, undefined, workflowRollbackSpec("workflow.create"));
2169
2204
  writeTool(tools, "workflow.update", WORKFLOW_LANE, WORKFLOW_TOOLS["workflow.update"].scope, WORKFLOW_TOOLS["workflow.update"].description, { schemaCode: z.string().min(1), code: z.string().min(1), template: workflowTemplateArg, merge: z.boolean().optional().describe("增量:只发改动/新增的活动与路由,工具按 activityCode 合并到草稿(草稿为基;新增节点自动接链),不必先读回整模板再重发。默认自动:模板缺 START 或 END 时按增量 merge(部分模板),给了完整 START..END 才整模板替换;显式 true 强制增量、false 强制替换。merge 是并集、绝不删边:结果比草稿少边会被拒(workflow-merge-would-drop-routes);--dry-run 会返回 routesBefore/routesAfter/routesAdded/routesDropped") }, async (session, args) => {
@@ -16,14 +16,15 @@ const restfulQueryService = Object.freeze({
16
16
  },
17
17
  methodName: "querySupplier",
18
18
  methodConfig: { httpType: "Post", requestBodyType: "JSON", url: "/supplier/query", frontEndExtensionEnable: true },
19
- methodInputParameters: [{ code: "keyword", name: "关键字", required: true, configJson: { position: "REQUEST_BODY" } }],
20
- methodOutputParameters: [{ code: "name", name: "供应商名称", configJson: { position: "REQUEST_BODY" } }],
19
+ methodInputParameters: [{ code: "keyword", name: "关键字", required: true, bizPropertyType: "SHORT_TEXT", configJson: { position: "REQUEST_BODY" } }],
20
+ methodOutputParameters: [{ code: "name", name: "供应商名称", bizPropertyType: "SHORT_TEXT", configJson: { position: "REQUEST_BODY" } }],
21
21
  },
22
22
  preconditions: ["serviceCategoryId 由 service.category.list 解析(「全部」是空 id 虚拟项不可用);RESTFUL 服务 configJson 需要 auth 与 commonlist[url];带 methodName 时必须有 methodConfig"],
23
23
  conditionalRequirements: [
24
24
  "RESTFUL auth.code ∈ customer(parameterlist=[]) / basic(client… username/password) / oauth_password / oauth_client;parameterlist 每项 {code,value} 且必须与 auth.code 期望参数完全一致",
25
25
  "RESTFUL 方法 configJson 必需键:httpType(Post/Get/Put/Patch/Delete)、requestBodyType(Form-urlencoded/JSON/Text)、url、frontEndExtensionEnable(布尔)",
26
26
  "RESTFUL 每个输入/输出参数必须带 configJson.position ∈ QUERY_PARAMS/REQUEST_HEADER/REQUEST_BODY/PATH_PARAMS(URL_VARIABLE 被运行时拒 501007)",
27
+ "每个输入/输出参数必须带 bizPropertyType(如 SHORT_TEXT/NUMERICAL)——缺它提交即被 bizservice-parameter-face-invalid 拒(r19-p7 首拒根因,2026-10-26 修正本配方)",
27
28
  "一次写只建一个方法;给既有服务加方法走 method.create(escape hatch)",
28
29
  ],
29
30
  priorToolReferences: [
@@ -92,10 +93,10 @@ const methodCreateRestfulGet = Object.freeze({
92
93
  name: "供应商查询",
93
94
  methodConfig: { httpType: "Get", requestBodyType: "JSON", url: "/openapi/supplier/query", frontEndExtensionEnable: true },
94
95
  methodInputParameters: [
95
- { code: "keyword", name: "关键字", required: true, configJson: { position: "QUERY_PARAMS" } },
96
- { code: "x-lowcode-corpId", name: "企业ID(平台注入常量)", required: false, codeType: 0, bizCode: "<corpId>", configJson: { position: "REQUEST_HEADER" } },
96
+ { code: "keyword", name: "关键字", required: true, bizPropertyType: "SHORT_TEXT", configJson: { position: "QUERY_PARAMS" } },
97
+ { code: "x-lowcode-corpId", name: "企业ID(平台注入常量)", required: false, bizPropertyType: "SHORT_TEXT", codeType: 0, bizCode: "<corpId>", configJson: { position: "REQUEST_HEADER" } },
97
98
  ],
98
- methodOutputParameters: [{ code: "name", name: "供应商名称", configJson: { position: "REQUEST_BODY" } }],
99
+ methodOutputParameters: [{ code: "name", name: "供应商名称", bizPropertyType: "SHORT_TEXT", configJson: { position: "REQUEST_BODY" } }],
99
100
  },
100
101
  preconditions: ["服务已存在(method.create 只加方法);methodCode 未存在;RESTFUL 每个入参与出参都带 configJson.position"],
101
102
  conditionalRequirements: [
@@ -127,8 +128,8 @@ const methodCreateRestfulPost = Object.freeze({
127
128
  methodCode: "saveSupplier",
128
129
  name: "供应商保存",
129
130
  methodConfig: { httpType: "Post", requestBodyType: "JSON", url: "/openapi/supplier/save", frontEndExtensionEnable: true },
130
- methodInputParameters: [{ code: "body", name: "供应商对象", required: true, configJson: { position: "REQUEST_BODY" } }],
131
- methodOutputParameters: [{ code: "id", name: "主键", configJson: { position: "REQUEST_BODY" } }],
131
+ methodInputParameters: [{ code: "body", name: "供应商对象", required: true, bizPropertyType: "SHORT_TEXT", configJson: { position: "REQUEST_BODY" } }],
132
+ methodOutputParameters: [{ code: "id", name: "主键", bizPropertyType: "SHORT_TEXT", configJson: { position: "REQUEST_BODY" } }],
132
133
  },
133
134
  preconditions: ["服务已存在;methodCode 未存在;POST 入参 position=REQUEST_BODY,requestBodyType=JSON"],
134
135
  conditionalRequirements: [
@@ -255,6 +255,7 @@ const ruleCrossModelCreate = Object.freeze({
255
255
  "上游是 QUERY 时 `inputParam=\"_<QUERY节点code>_output\"`(QUERY 查 N 行→CREATE 逐条生成即 N→N 扇出,真机验证支持,同/跨模型均可);否则 `\"_input\"`。",
256
256
  "`QUERY` 节点只能查**主模型行**:查子表 schema(`parent.child` 或子 schema 名)空条件返回 0 行、带条件 550018(真机 2026-10-08)——逐子表明细扇出才需要二开。",
257
257
  "跨模型 CREATE/UPDATE 真机验证支持(2026-09-28 / 2026-10-08);承载规则的触发时机要对(Create 在提交时触发、Update 只在 data.update 触发、TIMING 定时触发)。",
258
+ "**CREATE 目标是已发布流程的模型时**(2026-10-27 真机四格矩阵):不传 startWorkflow → 建行**不发起流程**;要发起必须 startWorkflow:true + targetWorkflowCode:<该流程> + originatorValue 用户对象数组 [{\"id\":\"<userId>\",\"name\":\"<名>\"}](传对象 {} → 触发提交 50000 expect '[';无效 id → 50000 发起人不存在);设计器手工编辑可打开该开关,API 与设计器配置会漂移,rule.get 回读核对。",
258
259
  ],
259
260
  priorToolReferences: [
260
261
  { tool: "field.list", args: { environment, schemaCode: "<targetModel>", published: true }, purpose: "确认目标模型/字段已发布" },
@@ -269,7 +270,63 @@ const ruleCrossModelCreate = Object.freeze({
269
270
  ],
270
271
  requiresEnvironmentDerivedIds: false,
271
272
  });
273
+ /**
274
+ * r19 p7(2026-10-26):「Create 规则调用二开服务回写」在真机上全链路走通,但
275
+ * agent 为 methodMapping 形状 describe 了 rule.save 4 次(最大 16.8KB)——五条
276
+ * 既有配方里只有 data-duplicate-flag 带单向 inputMappings。本配方以 p7 的真实
277
+ * 成功参数为模板(输入 cname→来源字段、输出 level→目标字段,codeType:1),
278
+ * 双向映射一步可抄。
279
+ */
280
+ const ruleBizactionInvokeService = Object.freeze({
281
+ id: "rule-bizaction-invoke-service",
282
+ title: "Create 规则调用服务方法并映射回写(BIZ_ACTION 双向)",
283
+ aliases: ["服务方法", "调用服务", "BIZ_ACTION", "回写服务", "查服务回写"],
284
+ status: "requires-prior-readback",
285
+ tool: "rule.save",
286
+ args: {
287
+ environment,
288
+ schemaCode: model,
289
+ ruleCode: "Create",
290
+ name: "数据新增",
291
+ nodeGraph: {
292
+ nodes: [
293
+ { nodeCode: "Start", nodeName: "开始", nodeType: "START" },
294
+ { nodeCode: "A1", nodeName: "调用服务并回写", nodeType: "BIZ_ACTION", inputParam: "_input", methodMapping: { schemaCode: model, serviceCode: "<svc>", serviceMethodCode: "<method>", inputMappings: [{ serviceMethodParameterCode: "<in_code>", codeType: 1, bizCode: "<src_field>", required: true }], outputMappings: [{ serviceMethodParameterCode: "<out_code>", codeType: 1, bizCode: "<dst_field>" }] } },
295
+ { nodeCode: "Create", nodeName: "新增数据", nodeType: "SYSTEM_CREATE", isSystemNode: true },
296
+ { nodeCode: "End", nodeName: "结束", nodeType: "END" },
297
+ ],
298
+ routes: [
299
+ { preNode: "Start", postNode: "A1", routeCondition: true },
300
+ { preNode: "A1", postNode: "Create", routeCondition: true },
301
+ { preNode: "Create", postNode: "End", routeCondition: true },
302
+ ],
303
+ },
304
+ publish: true,
305
+ },
306
+ preconditions: [
307
+ "模型与来源/目标字段已发布;服务与方法已注册(service.register 的 readback.method 直接给参数 code)",
308
+ "方法出参类型(bizPropertyType)与目标字段类型匹配",
309
+ ],
310
+ conditionalRequirements: [
311
+ "codeType:1 = 映射模型字段(bizCode=字段 code);codeType:0 = 常量(bizCode=常量值);serviceMethodParameterCode 必须与方法声明的参数 code 逐字一致",
312
+ "**接口响应必须在 JSON 根级**返回与出参 code 同名的键——嵌套在信封里(如 {data:{level}})会静默映射不上(p7 实测)",
313
+ "**平台自调自己(127.0.0.1)会被运行时鉴权拦**:errcode 500012「绑定的API需要提供正确的身份验证或凭据」——需要带出站凭据的真实外部服务,或服务端配置鉴权放行;这不是 BIZ_ACTION 不支持(p7 定案)",
314
+ "BIZ_ACTION 放在 SYSTEM_CREATE **之前**(与自更新 UPDATE 相反:ASSIGN/BIZ_ACTION 在系统节点前,UPDATE 在系统节点后)",
315
+ ],
316
+ priorToolReferences: [
317
+ { tool: "service.register", args: { environment }, purpose: "注册服务+方法;readback.method 内联参数面(code/bizPropertyType),不必再 service.get" },
318
+ ],
319
+ dryRun: { supported: true, command: `omc call rule.save --env ${environment} --dry-run --args '<recipe.args>'`, notes: "dry-run 跑图 lint + 映射键校验(schemaCode/serviceCode/serviceMethodCode 非空、codeType 枚举);服务参数面一致性真机 save 才核" },
320
+ expectedReadback: { readback: { published: true, enabled: false, next: "rule.enable → data.submit 触发 → data.load 读回目标字段" } },
321
+ refusalRecovery: [
322
+ { code: "rule-graph-bizmethod-unresolved", action: "serviceCode/serviceMethodCode 必须是已注册服务(service.list);方法级详情 service.get" },
323
+ { code: "rule-graph-bizaction-mapping-field-missing", action: "inputMappings 的 bizCode 必须是本模型已发布字段 code(field.list 核对)" },
324
+ ...commonRecovery,
325
+ ],
326
+ notes: "p7 实测链(sandbox-r19/.scratch/report-p7.md §4):注册查询服务 → 本配方建 Create 规则 → rule.enable → 提交触发。运行期若 500012 见 conditionalRequirements 第 3 条(出站凭据边界)",
327
+ requiresEnvironmentDerivedIds: true,
328
+ });
272
329
  export const BUSINESSRULE_METADATA = Object.freeze({
273
- "rule.save": Object.freeze({ recipes: [ruleGraph, ruleBranchGraph, ruleTimingTask, dataDuplicateFlag, ruleCrossModelCreate], relatedTools: ["field.list", "rule.get", "rule.enable"] }),
330
+ "rule.save": Object.freeze({ recipes: [ruleGraph, ruleBranchGraph, ruleTimingTask, dataDuplicateFlag, ruleCrossModelCreate, ruleBizactionInvokeService], relatedTools: ["field.list", "rule.get", "rule.enable", "service.get"] }),
274
331
  });
275
332
  //# sourceMappingURL=businessrule.js.map