@stalfh233/omc-cli 0.4.80 → 0.4.81
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 +2 -1
- package/dist/capability-overview.js +7 -0
- package/dist/cli.js +7 -2
- package/dist/lanes/rule-graph.js +14 -0
- package/dist/lanes/workflow-contract.js +1 -1
- package/dist/lanes/workflow.js +68 -5
- package/dist/usage-log.js +6 -1
- package/package.json +3 -3
- package/skills/omc-requirement-vocabulary.md +6 -2
package/README.md
CHANGED
|
@@ -130,10 +130,11 @@ omc call discover_environment --env <env> --args '{}' # 环境就绪(真实
|
|
|
130
130
|
- **intent**:`partial` 表示该能力可声明式(`omc intent`)覆盖一部分——通常 create/update;
|
|
131
131
|
`none` 表示只走 `omc call`。删除类、方法面、分类/连接池等仍走 `omc call` 逃生舱。
|
|
132
132
|
|
|
133
|
-
## 近期能力(0.4.74 → 0.4.
|
|
133
|
+
## 近期能力(0.4.74 → 0.4.81)
|
|
134
134
|
|
|
135
135
|
| 版本 | 新增 / 修复 |
|
|
136
136
|
|---|---|
|
|
137
|
+
| 0.4.81 | **r11 真机探针修复**:`rule.save` 对 `AGGREGATION` 规则节点**写前 fail-closed**(平台会存进去、运行时 50000,真实路径是 `omc compute`/datarule type2);`omc next "统计子表行数"` 路由补 `datarule` 阶梯与 `field-compute-subtable-sum` 配方;`org.user.search` 省略关键字时**按部门树列全员**(平台 `wd` 不支持空串,此前只能枚举 0–9);`omc artifact get --jq` 无值回键清单(不再 commander 报错);`usage` 的 `faces` 纳入 `--jq/--summary/--keys/--path-only/--extract`(此前读面投影完全不记账) |
|
|
137
138
|
| 0.4.80 | **省 token 与用量账本**:`omc help <topic>` 默认只回节索引(约 1KB,含每节字节数),`--all` 才读整篇;`omc artifact get` 正文 >8KB 默认只回形状(`--full` 才整包);`omc usage` 默认只回汇总(`byTool`/`redundancy`/`estimatedTokens`),`--records` 才展开;`omc script` 的**每行工具调用单独记账**(`script-call` + `parentId`);未绑定环境的目录回落 `$OMC_CONFIG_HOME/usage/<cwd-hash>.jsonl` |
|
|
138
139
|
| 0.4.79 | README 对齐 0.4.79 实际工具面(142 工具 / 16 能力) |
|
|
139
140
|
| 0.4.78 | **按钮 JS 直接读写**:`listview.get --buttonCode/--buttons`、`listview.configure --buttonScripts`、`form.get --buttonScripts`、`form.publish --buttonScripts`,外加 `buttons[].script` 简写 |
|
|
@@ -210,7 +210,14 @@ export const CAPABILITY_CUES = Object.freeze({
|
|
|
210
210
|
// 日期) because no cue named the VALIDATION intent, so the agent went to the
|
|
211
211
|
// rule graph and concluded "the platform has no today variable" — wrong, and
|
|
212
212
|
// the form designer's 提交校验 (datarule type1/checkType2) is the real face.
|
|
213
|
+
// r11 (2026-10-24): "统计子表行数聚合 COUNT 写入主表字段" routed to `field`
|
|
214
|
+
// (it matched 子表+字段) and answered with the "create a child table" recipe,
|
|
215
|
+
// so the agent hand-built an AGGREGATION rule node — a shape the platform
|
|
216
|
+
// accepts and then fails at runtime. Row aggregation is a FIELD COMPUTE RULE
|
|
217
|
+
// (`omc compute` / datarule type2). These compound cues outscore field's
|
|
218
|
+
// 子表+字段 so the aggregate intent reaches the right ladder.
|
|
213
219
|
datarule: ["字段级规则", "控件规则", "校验规则", "计算拼接", "显示规则", "规则", "type1", "type2", "type3",
|
|
220
|
+
"统计子表", "子表行数", "子表计数", "子表求和", "子表汇总", "行数统计", "计数写回", "汇总回主表", "求和回主表", "聚合计算",
|
|
214
221
|
"不早于", "不能早于", "不晚于", "不能晚于", "早于今天", "晚于今天", "日期校验", "提交校验", "校验不通过", "必填校验", "正则校验", "重复校验", "长度校验"],
|
|
215
222
|
// NOTE: bare "自动" was a cue (round 10 bug: "自动识别"/"自动发邮件" matched
|
|
216
223
|
// businessrule and routed an UNSUPPORTED ask onto the write ladder). A business
|
package/dist/cli.js
CHANGED
|
@@ -1231,8 +1231,13 @@ async function main() {
|
|
|
1231
1231
|
console.error(`已清理 ${result.removed} 个读回产物,释放 ${formatBytes(result.freedBytes)};保留 ${result.kept} 个(${formatBytes(result.keptBytes)})`);
|
|
1232
1232
|
commandExitCode = CLI_EXIT_CODES.ok;
|
|
1233
1233
|
});
|
|
1234
|
-
artifactCommand.command("get <id>").option("--jq
|
|
1235
|
-
|
|
1234
|
+
artifactCommand.command("get <id>").option("--jq [path]").option("--path-only").option("--summary").option("--keys [a,b]").option("--full", "整包返回正文(默认:正文超过 8KB 时只回形状 outline + fetch,按需 --jq/--keys 定向取值)").option("--max-output-bytes <bytes>").action(async (id, options) => {
|
|
1235
|
+
// A bare `--jq`/`--keys` is a key-inventory request, not a missing argument:
|
|
1236
|
+
// commander's "argument missing" (invalid-input + retry) is a dead end the
|
|
1237
|
+
// caller cannot fix without already knowing the flag takes a value (r11
|
|
1238
|
+
// burned six rounds on `artifact get <id> --jq` before trying `--summary`).
|
|
1239
|
+
const bareJq = options.jq === true;
|
|
1240
|
+
commandExitCode = await withTransport(options, request, (transport) => cmdArtifactGet(id, transport, { ...(typeof options.jq === "string" ? { jq: options.jq } : {}), pathOnly: options.pathOnly === true, summary: options.summary === true || options.keys === true || bareJq, ...(typeof options.keys === "string" ? { keys: options.keys } : {}), full: options.full === true }));
|
|
1236
1241
|
});
|
|
1237
1242
|
command.command("bench").option("--live-sample").option("--env <environment>").action(async (options) => { commandExitCode = await cmdBench([...(options.liveSample ? ["--live-sample"] : []), ...(options.env ? ["--env", options.env] : [])]); });
|
|
1238
1243
|
command.command("commands").option("--json").option("--compact").option("--filter <keyword>").option("--lane <id>").option("--max-output-bytes <bytes>").action(async (options) => { commandExitCode = await withTransport(options, request, (transport) => cmdDiscovery("commands", transport, undefined, { json: options.json === true, compact: options.compact === true, ...(options.filter ? { filter: options.filter } : {}), ...(options.lane ? { lane: options.lane } : {}) })); });
|
package/dist/lanes/rule-graph.js
CHANGED
|
@@ -1225,6 +1225,20 @@ export function validateDataAction(action, nodeCode, index) {
|
|
|
1225
1225
|
const blockers = [];
|
|
1226
1226
|
const where = `dataActions[${index}]`;
|
|
1227
1227
|
const valueType = String(action.ruleValueType ?? "");
|
|
1228
|
+
// 1.2 (r11 2026-10-24, 1.1.25): AGGREGATION on a rule node is a TRAP. The
|
|
1229
|
+
// platform accepts it (save + publish + enable all answer ok) and then throws
|
|
1230
|
+
// at runtime: the aggregate comes back as java.util.ArrayList and the scalar
|
|
1231
|
+
// target field rejects it (DecimalBizField.fillClientContent → 50000 服务异常).
|
|
1232
|
+
// Row aggregation is a FORMULA(100) FIELD feature, not a rule action. The r11
|
|
1233
|
+
// probe burned 6 rule.save + 4 data.submit + 3 docker-log reads before finding
|
|
1234
|
+
// the real path, and the only symptom the CLI ever showed was "服务异常".
|
|
1235
|
+
if (valueType.toUpperCase() === "AGGREGATION")
|
|
1236
|
+
return [{
|
|
1237
|
+
code: "rule-graph-aggregation-is-a-field-feature",
|
|
1238
|
+
nodeCode,
|
|
1239
|
+
key: `dataActions[${index}].ruleValueType`,
|
|
1240
|
+
message: `${nodeCode}: 规则节点不支持 AGGREGATION 赋值——平台会存进去并启用,但运行期聚合结果是集合(ArrayList),标量目标收到集合即 50000「服务异常」(DecimalBizField.fillClientContent)。**子表计数/求和/取最值回主表用字段计算规则**:目标字段建成普通数值字段,然后 \`omc compute --schema <主表> --target <目标字段> --formula 'COUNTIF({<子表code>.<子列code>},"异常")'\`(默认 dry-run,--apply 写入;等价 datarule.create dataRuleType:2,配方 \`field-compute-subtable-sum\`)。规则图里只做 FIXED/DYNAMIC/EXPRESSION 的直接赋值。注意计算由门户前端执行、API data.load 不回填(平台既有行为,见 FIELD_FORMULA_WIRE.runtimeVerification):要 API 侧断言结果,请在测试数据里显式写预算值,不要用回读判定公式对错。来源:r11 巡检沙盒 2026-10-24(1.1.25)。`,
|
|
1241
|
+
}];
|
|
1228
1242
|
if (!RULE_VALUE_TYPES.includes(valueType))
|
|
1229
1243
|
blockers.push({ code: "rule-graph-action-value-type", nodeCode, key: "ruleValueType", message: `${where}: ruleValueType "${valueType}" 不在 ${RULE_VALUE_TYPES.join("/")}` });
|
|
1230
1244
|
if (typeof action.targetDataItem !== "string" || !action.targetDataItem.trim())
|
|
@@ -192,7 +192,7 @@ export const WORKFLOW_LANE_CONTRACT = Object.freeze({
|
|
|
192
192
|
}),
|
|
193
193
|
"org.user.search": Object.freeze({
|
|
194
194
|
scope: "workflow participant resolution",
|
|
195
|
-
description: "按关键字搜索用户(`POST /api/api/organization/user/search`,body `{wd,page,size,filterType?}
|
|
195
|
+
description: "按关键字搜索用户(`POST /api/api/organization/user/search`,body `{wd,page,size,filterType?}`,返回 {id,name,username,userId,departmentId,corpId,status})。平台 `wd` 必填、空串报 10005,所以**省略 wd 时不打 user/search**:工具改为遍历部门树(`organization/department/tree` + `department/list_user`)聚合成全员清单(返回 `keyword:null` 与 `departmentsScanned`;>200 部门会带 `truncated`)。用于把「张三」解析成真实用户 id/username 再构造参与者对象(`[{\"code\":\"<username|userId>\",\"corpId\":\"<corpId|other>\",\"id\":\"<userId>\",\"name\":\"<姓名>\",\"type\":3}]`,type 3=用户 / 2=角色 / 多个用 `+` 连接);只要某部门的人用 org.department.users,角色用 org.role.list。",
|
|
196
196
|
}),
|
|
197
197
|
"workitem.list": Object.freeze({
|
|
198
198
|
scope: "runtime workflow work-item query",
|
package/dist/lanes/workflow.js
CHANGED
|
@@ -2819,9 +2819,9 @@ export function registerWorkflowLane(tools) {
|
|
|
2819
2819
|
// `wd` is the platform's own key; `keyword`/`q` are accepted aliases so a
|
|
2820
2820
|
// caller that reaches for the natural word is not bounced with
|
|
2821
2821
|
// "wd: Required" (round 10: an agent sent `keyword` and burned a retry).
|
|
2822
|
-
wd: z.string().
|
|
2823
|
-
keyword: z.string().
|
|
2824
|
-
q: z.string().
|
|
2822
|
+
wd: z.string().optional().describe("搜索关键字(用户姓名或账号用户名);**省略则列出全部员工**(工具按部门树聚合,见 description)。可用 keyword/q 作别名"),
|
|
2823
|
+
keyword: z.string().optional().describe("`wd` 的别名"),
|
|
2824
|
+
q: z.string().optional().describe("`wd` 的别名"),
|
|
2825
2825
|
page: z.number().int().min(0).optional().describe("页号,默认 0"),
|
|
2826
2826
|
size: z.number().int().min(1).optional().describe("每页条数,默认 100"),
|
|
2827
2827
|
filterType: z.string().optional().describe("admin=只搜当前应用管理员可见范围(可省)"),
|
|
@@ -2829,8 +2829,71 @@ export function registerWorkflowLane(tools) {
|
|
|
2829
2829
|
},
|
|
2830
2830
|
handler: async (ctx, args, self) => {
|
|
2831
2831
|
const keyword = args.wd ?? args.keyword ?? args.q;
|
|
2832
|
-
|
|
2833
|
-
|
|
2832
|
+
// 0.4.81 (r11 2026-10-24): no keyword used to be a hard refusal, and the
|
|
2833
|
+
// platform rejects an empty `wd` (10005), so the r11 probe enumerated
|
|
2834
|
+
// `wd` = 0..9 to discover users — 11 calls, ~16 KB, to learn a roster the
|
|
2835
|
+
// platform can already answer. Omitted keyword now aggregates
|
|
2836
|
+
// `department/list_user` over the department tree (one tool call).
|
|
2837
|
+
if (!keyword) {
|
|
2838
|
+
return governedRead(ctx, self, args, async (session) => {
|
|
2839
|
+
const orgQuery = { corpId: session.enterpriseId() };
|
|
2840
|
+
const seenDepartments = new Set();
|
|
2841
|
+
const order = [];
|
|
2842
|
+
const pushRows = (rows) => {
|
|
2843
|
+
for (const row of rows) {
|
|
2844
|
+
const id = String(row.id ?? "");
|
|
2845
|
+
if (id === "" || seenDepartments.has(id))
|
|
2846
|
+
continue;
|
|
2847
|
+
seenDepartments.add(id);
|
|
2848
|
+
order.push(id);
|
|
2849
|
+
}
|
|
2850
|
+
};
|
|
2851
|
+
// `/department/tree` answers `{departmentList:[...]}`, not a bare array;
|
|
2852
|
+
// `wireRows` only understands arrays/page envelopes.
|
|
2853
|
+
const treeRows = async (deptId) => {
|
|
2854
|
+
const data = (await wireCall(session, {
|
|
2855
|
+
path: ENDPOINTS.organizationDepartmentTree,
|
|
2856
|
+
query: deptId === undefined ? orgQuery : { ...orgQuery, deptIds: deptId },
|
|
2857
|
+
})).data;
|
|
2858
|
+
return Array.isArray(data?.departmentList) ? data.departmentList : [];
|
|
2859
|
+
};
|
|
2860
|
+
pushRows(await treeRows());
|
|
2861
|
+
// `deptIds` returns the subtree BELOW that department; walk the roots.
|
|
2862
|
+
for (const rootId of [...order]) {
|
|
2863
|
+
if (order.length >= 200)
|
|
2864
|
+
break;
|
|
2865
|
+
pushRows(await treeRows(rootId));
|
|
2866
|
+
}
|
|
2867
|
+
const seenUsers = new Map();
|
|
2868
|
+
let departmentsScanned = 0;
|
|
2869
|
+
for (const deptId of order.slice(0, 200)) {
|
|
2870
|
+
departmentsScanned += 1;
|
|
2871
|
+
for (const row of wireRows((await wireCall(session, { path: ENDPOINTS.organizationDepartmentUsers, query: { ...orgQuery, deptId } })).data)) {
|
|
2872
|
+
const id = String(row.id ?? row.userId ?? "");
|
|
2873
|
+
if (id === "" || seenUsers.has(id))
|
|
2874
|
+
continue;
|
|
2875
|
+
seenUsers.set(id, {
|
|
2876
|
+
id: row.id ?? row.userId ?? null,
|
|
2877
|
+
name: row.name ?? null,
|
|
2878
|
+
username: row.username ?? null,
|
|
2879
|
+
userId: row.userId ?? null,
|
|
2880
|
+
departmentId: row.departmentId ?? deptId,
|
|
2881
|
+
mobile: row.mobile ?? null,
|
|
2882
|
+
});
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2885
|
+
const users = [...seenUsers.values()];
|
|
2886
|
+
return {
|
|
2887
|
+
keyword: null,
|
|
2888
|
+
count: users.length,
|
|
2889
|
+
total: users.length,
|
|
2890
|
+
departmentsScanned,
|
|
2891
|
+
...(order.length > 200 ? { truncated: true, note: `部门树超过 200 个,只聚合了前 200 个;用 wd 关键字精确检索` } : {}),
|
|
2892
|
+
users,
|
|
2893
|
+
hint: "无关键字=列全员(按部门树聚合)。平台 user/search 的 wd 不支持空串(10005),所以这条路径走 department/list_user;只要某部门的人用 org.department.users。",
|
|
2894
|
+
};
|
|
2895
|
+
});
|
|
2896
|
+
}
|
|
2834
2897
|
return governedRead(ctx, self, { ...args, wd: keyword }, async (session) => {
|
|
2835
2898
|
const body = { wd: keyword, page: args.page ?? 0, size: args.size ?? 100 };
|
|
2836
2899
|
if (args.filterType !== undefined)
|
package/dist/usage-log.js
CHANGED
|
@@ -222,7 +222,12 @@ function facesOf(argv) {
|
|
|
222
222
|
// (bounded index vs full catalogue) but `commandOf` drops every flag, so
|
|
223
223
|
// without this they were indistinguishable in the log and byte attribution
|
|
224
224
|
// was ambiguous (round 10).
|
|
225
|
-
|
|
225
|
+
// Read projections count as faces too. r11 (2026-10-24) reported `--jq`/`--summary`/
|
|
226
|
+
// `--keys` usage as 0 because only the `describe`/`call` flags were listed:
|
|
227
|
+
// `omc artifact get <id> --jq <path>` showed up as a bare `artifact get` with
|
|
228
|
+
// empty argKeys, so the cost report could not tell a 200 B projection from a
|
|
229
|
+
// 44 KB pull — precisely the read path these flags exist to make cheap.
|
|
230
|
+
const faces = ["--full", "--compact", "--json", "--schema-only", "--param", "--example", "--section", "--sections", "--out", "--quiet", "--jq", "--summary", "--keys", "--path-only", "--extract", "--extract-keys", "--records"];
|
|
226
231
|
return faces.filter((face) => argv.includes(face));
|
|
227
232
|
}
|
|
228
233
|
const safeArgumentKeys = new Set(["schemaCode", "propertyCode", "code", "workflowCode", "ruleCode", "sheetCode", "id", "dataRuleType", "propertyType", "published", "handler"]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stalfh233/omc-cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.81",
|
|
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.81",
|
|
54
54
|
"commander": "14.0.3",
|
|
55
|
-
"omc-core": "0.4.
|
|
55
|
+
"omc-core": "0.4.81",
|
|
56
56
|
"proper-lockfile": "4.1.2",
|
|
57
57
|
"write-file-atomic": "6.0.0",
|
|
58
58
|
"zod": "^3.23.8",
|
|
@@ -44,8 +44,12 @@ capability's coverage/tools/boundaries from `omc-capabilities`.
|
|
|
44
44
|
Coverage status and boundaries for every capability (including 权限/子表/聚合/
|
|
45
45
|
上线/内联/OCR) live in **`omc-capabilities`** — its 能力索引 table and
|
|
46
46
|
[边界] section are the authority; read the relevant section there rather than a
|
|
47
|
-
copy here. In particular,
|
|
48
|
-
|
|
47
|
+
copy here. In particular, **跨模型/子表聚合、汇总、求和是配置能力**,不要当二开。
|
|
48
|
+
落点是**字段计算规则**:`omc compute --schema <主表> --target <目标字段> --formula 'COUNTIF({<子表code>.<子列code>},"异常")'`
|
|
49
|
+
(等价 `datarule.create` `dataRuleType:2`,配方 `field-compute-subtable-sum` / `field-compute-child-column`)。
|
|
50
|
+
**不要**在规则图(`rule.save`)里用 `ruleValueType:"AGGREGATION"` —— 平台会存进去并启用,运行期聚合结果是集合,
|
|
51
|
+
标量目标收到集合即 50000「服务异常」(r11 巡检沙盒 2026-10-24,1.1.25;OMC 现已写前 fail-closed)。
|
|
52
|
+
计算由门户前端执行、API `data.load` 不回填,属平台既有行为:要断言结果请在测试数据里写预算值。
|
|
49
53
|
|
|
50
54
|
Cue → capability is a starting point, never a substitute for 定面. A cue that
|
|
51
55
|
could name two faces is resolved by the 定面表 in the parent skill.
|