@stalfh233/omc-cli 0.4.73 → 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 +51 -5
- package/dist/capability-overview.js +12 -3
- package/dist/cli.js +26 -5
- package/dist/describe-cache.js +91 -0
- package/dist/endpoints.js +46 -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-coverage.js +27 -0
- package/dist/lanes/organization.js +662 -0
- package/dist/lanes/rule-graph.js +37 -0
- package/dist/lanes/rule-lifecycle-contract.js +17 -0
- package/dist/lanes/rule-lifecycle-coverage.js +2 -0
- package/dist/lanes/rule-lifecycle.js +104 -0
- package/dist/lanes/workflow-contract.js +58 -11
- package/dist/lanes/workflow-coverage.js +25 -0
- package/dist/lanes/workflow.js +513 -46
- package/dist/rollback-spec.js +32 -1
- package/dist/tools.js +2 -0
- package/dist/usage-log.js +71 -0
- package/package.json +3 -3
- package/skills/omc-business-configuration.md +27 -2
- package/skills/omc-capabilities.md +8 -7
- package/skills/omc-write-hazards.md +4 -4
|
@@ -337,12 +337,54 @@
|
|
|
337
337
|
"workflow.update",
|
|
338
338
|
"workflow.publish",
|
|
339
339
|
"workflow.bindRule",
|
|
340
|
+
"workflow.setButtons",
|
|
340
341
|
"workflow.delete",
|
|
341
342
|
"configure_workflow",
|
|
342
|
-
"workflow.validate"
|
|
343
|
+
"workflow.validate",
|
|
344
|
+
"org.role.users",
|
|
345
|
+
"org.role.userScope",
|
|
346
|
+
"org.roleGroup.create",
|
|
347
|
+
"org.role.create",
|
|
348
|
+
"org.role.update",
|
|
349
|
+
"org.role.addUsers",
|
|
350
|
+
"org.role.removeUsers",
|
|
351
|
+
"org.role.setUserScope",
|
|
352
|
+
"org.role.delete",
|
|
353
|
+
"org.roleGroup.delete",
|
|
354
|
+
"org.user.transferDepartment",
|
|
355
|
+
"org.department.create",
|
|
356
|
+
"org.department.update",
|
|
357
|
+
"org.department.delete",
|
|
358
|
+
"org.department.users",
|
|
359
|
+
"org.user.create",
|
|
360
|
+
"org.user.update",
|
|
361
|
+
"org.user.delete",
|
|
362
|
+
"org.user.info",
|
|
363
|
+
"org.user.departments"
|
|
343
364
|
],
|
|
344
365
|
"scopes": [
|
|
345
|
-
"greenfield workflow authoring"
|
|
366
|
+
"greenfield workflow authoring",
|
|
367
|
+
"workflow node form-button checkboxes",
|
|
368
|
+
"organization role member read (role/users)",
|
|
369
|
+
"organization role member management scope read (role/get_ouscope)",
|
|
370
|
+
"organization role group create (role/add_role_group)",
|
|
371
|
+
"organization role create (role/add_role)",
|
|
372
|
+
"organization role update (role/modify_role)",
|
|
373
|
+
"organization role member add (role/add_role_user)",
|
|
374
|
+
"organization role member remove (role/strike_out_role_user)",
|
|
375
|
+
"organization role member scope update (role/update_ouscope)",
|
|
376
|
+
"organization role delete (role/strike_out_role)",
|
|
377
|
+
"organization role group delete (role/strike_out_role_group)",
|
|
378
|
+
"organization user department transfer (user/dept_transfer)",
|
|
379
|
+
"organization department create (department/add)",
|
|
380
|
+
"organization department update (department/modefied)",
|
|
381
|
+
"organization department delete (department/strike_out)",
|
|
382
|
+
"organization department member read (department/list_user)",
|
|
383
|
+
"organization user create (user/add)",
|
|
384
|
+
"organization user update (user/modify)",
|
|
385
|
+
"organization user delete (user/strike_out)",
|
|
386
|
+
"organization user read (user/info)",
|
|
387
|
+
"organization user department read (user/departments)"
|
|
346
388
|
]
|
|
347
389
|
},
|
|
348
390
|
{
|
|
@@ -456,10 +498,12 @@
|
|
|
456
498
|
"rule.disable",
|
|
457
499
|
"rule.delete",
|
|
458
500
|
"rule.disableMany",
|
|
501
|
+
"rule.run",
|
|
459
502
|
"configure_rule"
|
|
460
503
|
],
|
|
461
504
|
"scopes": [
|
|
462
|
-
"enable invoke disable delete"
|
|
505
|
+
"enable invoke disable delete",
|
|
506
|
+
"manual rule execution (button trigger)"
|
|
463
507
|
]
|
|
464
508
|
},
|
|
465
509
|
{
|
|
@@ -816,11 +860,13 @@
|
|
|
816
860
|
"form.get",
|
|
817
861
|
"form.draft",
|
|
818
862
|
"form.publish",
|
|
819
|
-
"configure_form"
|
|
863
|
+
"configure_form",
|
|
864
|
+
"form.runButton"
|
|
820
865
|
],
|
|
821
866
|
"scopes": [
|
|
822
867
|
"publishedHtmlJson authoritative readback",
|
|
823
|
-
"draft publish publishedHtmlJson readback"
|
|
868
|
+
"draft publish publishedHtmlJson readback",
|
|
869
|
+
"form toolbar button execution"
|
|
824
870
|
]
|
|
825
871
|
},
|
|
826
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
|
@@ -28,6 +28,7 @@ import { cmdFormDefault } from "./form-default.js";
|
|
|
28
28
|
if (process.argv.includes("--utf8"))
|
|
29
29
|
process.env.OMC_ASCII_STDOUT = "0";
|
|
30
30
|
import { commandIndex, commandInventory, describeContract, describeContractFull, describeContractTool, describeExample, describeParam, describeSchemaOnly, describeTool, describeToolFull } from "./discovery.js";
|
|
31
|
+
import { repeatFullDescribe } from "./describe-cache.js";
|
|
31
32
|
import { buildCapabilityOverview, capabilityDetail, compactCapabilities, executableCapabilityBundle, resolveCapabilityId, DISCOVERY_AUTHORITY } from "./capability-overview.js";
|
|
32
33
|
import { goalRoute } from "./goal-routes.js";
|
|
33
34
|
import { absencePointer } from "./capability-absences.js";
|
|
@@ -326,8 +327,15 @@ async function cmdCall(store, args) {
|
|
|
326
327
|
validateAgentPaths(parsedArgs);
|
|
327
328
|
}
|
|
328
329
|
catch (error) {
|
|
329
|
-
|
|
330
|
-
|
|
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 }));
|
|
331
339
|
return CLI_EXIT_CODES.invalidInput;
|
|
332
340
|
}
|
|
333
341
|
const registry = buildToolRegistry();
|
|
@@ -630,8 +638,13 @@ function cmdDiscovery(kind, transport, value, options = {}) {
|
|
|
630
638
|
result = describeParam(registry, value ?? "", options.param);
|
|
631
639
|
else if (options.example !== undefined)
|
|
632
640
|
result = describeExample(registry, value ?? "", typeof options.example === "string" ? options.example : undefined);
|
|
633
|
-
else if (options.full)
|
|
634
|
-
|
|
641
|
+
else if (options.full) {
|
|
642
|
+
const full = describeToolFull(registry, value ?? "");
|
|
643
|
+
// r9: the same `--full` contract was re-shipped 8 times (134 KB) while the
|
|
644
|
+
// caller only wanted to re-check a shape. Within a project session the
|
|
645
|
+
// second identical read returns an artifact reference instead of the body.
|
|
646
|
+
result = full ? (repeatFullDescribe(value ?? "", full) ?? full) : full;
|
|
647
|
+
}
|
|
635
648
|
else
|
|
636
649
|
result = describeTool(registry, value ?? "");
|
|
637
650
|
if (!result) {
|
|
@@ -1168,7 +1181,15 @@ const request = requestId();
|
|
|
1168
1181
|
main().then((code) => {
|
|
1169
1182
|
// Commands that own their stdout never grow a trailing envelope. `--version`/
|
|
1170
1183
|
// `-V` are here because their short-circuit already wrote the bare version.
|
|
1171
|
-
|
|
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] ?? "")) {
|
|
1172
1193
|
completeUsage(code);
|
|
1173
1194
|
finish(code);
|
|
1174
1195
|
return;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { artifactRoot, joinConfigRoot } from "./command-transport.js";
|
|
5
|
+
import { createArtifact, readArtifact } from "./artifacts.js";
|
|
6
|
+
import { projectSessionActive } from "./usage-log.js";
|
|
7
|
+
/**
|
|
8
|
+
* Repeat-read suppression for `describe <tool> --full`.
|
|
9
|
+
*
|
|
10
|
+
* r9 (2026-10-22) spent 134 KB on EIGHT `describe rule.save --full` calls: the
|
|
11
|
+
* same contract, unchanged, re-shipped every time the caller re-checked a shape.
|
|
12
|
+
* The hint at the bottom of the response ("先试 --example/--param") did not help
|
|
13
|
+
* because a caller editing a graph wants the authoritative schema again, and
|
|
14
|
+
* `--full` had no memory.
|
|
15
|
+
*
|
|
16
|
+
* The first `--full` in a project still returns the whole contract and spills a
|
|
17
|
+
* copy to an artifact. A later identical read returns `{artifactId, sha256,
|
|
18
|
+
* bytes, fetch}` instead: the caller can project the one key it needs with
|
|
19
|
+
* `omc artifact get <id> --jq <path>` instead of paying for the whole body
|
|
20
|
+
* again. Identity is the CONTENT hash, so a changed contract (new version, new
|
|
21
|
+
* field) always returns the full body again.
|
|
22
|
+
*
|
|
23
|
+
* Scoped to a project session by construction: the cache only exists where the
|
|
24
|
+
* usage log does (`.omc-env` in the working directory, or OMC_USAGE_LOG). Repo
|
|
25
|
+
* tests and the golden master run without it, so their bytes stay stable.
|
|
26
|
+
*/
|
|
27
|
+
const CACHE_FILE = "describe-full-cache.json";
|
|
28
|
+
const CACHE_TTL_MS = 6 * 60 * 60 * 1000;
|
|
29
|
+
function cachePath() {
|
|
30
|
+
return resolve(joinConfigRoot(), ".omc", CACHE_FILE);
|
|
31
|
+
}
|
|
32
|
+
function readCache() {
|
|
33
|
+
try {
|
|
34
|
+
const parsed = JSON.parse(readFileSync(cachePath(), "utf8"));
|
|
35
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return {};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function writeCache(cache) {
|
|
42
|
+
try {
|
|
43
|
+
const path = cachePath();
|
|
44
|
+
mkdirSync(join(path, ".."), { recursive: true });
|
|
45
|
+
writeFileSync(path, `${JSON.stringify(cache)}\n`, { mode: 0o600 });
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// Best-effort: a cache write must never fail the command.
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Return the compact repeat-read face for `tool`, or `undefined` when this is
|
|
53
|
+
* the first read (or the contract changed). Records the new entry as a side
|
|
54
|
+
* effect so the NEXT read is compact.
|
|
55
|
+
*/
|
|
56
|
+
export function repeatFullDescribe(tool, result) {
|
|
57
|
+
// No project session (repo tests, golden master, one-off runs) ⇒ no cache:
|
|
58
|
+
// those callers need byte-stable output, and a cache write would be litter.
|
|
59
|
+
if (!projectSessionActive())
|
|
60
|
+
return undefined;
|
|
61
|
+
const body = `${JSON.stringify(result, null, 2)}\n`;
|
|
62
|
+
const bytes = Buffer.byteLength(body, "utf8");
|
|
63
|
+
const digest = createHash("sha256").update(body).digest("hex");
|
|
64
|
+
const cache = readCache();
|
|
65
|
+
const previous = cache[tool];
|
|
66
|
+
if (previous && previous.sha256 === digest && Date.now() - previous.at < CACHE_TTL_MS) {
|
|
67
|
+
// A pruned artifact would turn the summary into a dead end; re-ship instead.
|
|
68
|
+
if (existsSync(join(artifactRoot(), `${previous.artifactId}.artifact`))) {
|
|
69
|
+
try {
|
|
70
|
+
readArtifact(artifactRoot(), previous.artifactId);
|
|
71
|
+
return {
|
|
72
|
+
name: tool,
|
|
73
|
+
repeated: true,
|
|
74
|
+
artifactId: previous.artifactId,
|
|
75
|
+
sha256: digest,
|
|
76
|
+
bytes,
|
|
77
|
+
fetch: `omc artifact get ${previous.artifactId} --summary(再 --keys/--jq 取需要的键)`,
|
|
78
|
+
note: `本项目的会话里已读过同一个契约(${bytes}B,内容 sha256 未变),本次不再重发全文;用 fetch 里的命令按需投影`,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// fall through to a full re-ship
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const artifact = createArtifact(artifactRoot(), body);
|
|
87
|
+
cache[tool] = { tool, sha256: digest, bytes, artifactId: artifact.artifactId, at: Date.now() };
|
|
88
|
+
writeCache(cache);
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=describe-cache.js.map
|
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",
|
|
@@ -120,6 +144,28 @@ export const ENDPOINTS = {
|
|
|
120
144
|
organizationRoleList: "/api/api/organization/role/list",
|
|
121
145
|
organizationDepartmentTree: "/api/api/organization/department/tree",
|
|
122
146
|
organizationUserSearch: "/api/api/organization/user/search",
|
|
147
|
+
organizationRoleUsers: "/api/api/organization/role/users",
|
|
148
|
+
organizationRoleGetOuScope: "/api/api/organization/role/get_ouscope",
|
|
149
|
+
organizationRoleUpdateOuScope: "/api/api/organization/role/update_ouscope",
|
|
150
|
+
organizationRoleAddGroup: "/api/api/organization/role/add_role_group",
|
|
151
|
+
organizationRoleAdd: "/api/api/organization/role/add_role",
|
|
152
|
+
organizationRoleModify: "/api/api/organization/role/modify_role",
|
|
153
|
+
organizationRoleGet: "/api/api/organization/role/get",
|
|
154
|
+
organizationDepartmentAdd: "/api/api/organization/department/add",
|
|
155
|
+
organizationDepartmentModify: "/api/api/organization/department/modefied",
|
|
156
|
+
organizationDepartmentDelete: "/api/api/organization/department/strike_out",
|
|
157
|
+
organizationDepartmentGet: "/api/api/organization/department/get_dept_info",
|
|
158
|
+
organizationDepartmentUsers: "/api/api/organization/department/list_user",
|
|
159
|
+
organizationUserAdd: "/api/api/organization/user/add",
|
|
160
|
+
organizationUserModify: "/api/api/organization/user/modify",
|
|
161
|
+
organizationUserDelete: "/api/api/organization/user/strike_out",
|
|
162
|
+
organizationUserInfo: "/api/api/organization/user/info",
|
|
163
|
+
organizationUserDepartments: "/api/api/organization/user/departments",
|
|
164
|
+
organizationRoleAddUser: "/api/api/organization/role/add_role_user",
|
|
165
|
+
organizationRoleRemoveUser: "/api/api/organization/role/strike_out_role_user",
|
|
166
|
+
organizationRoleDelete: "/api/api/organization/role/strike_out_role",
|
|
167
|
+
organizationRoleGroupDelete: "/api/api/organization/role/strike_out_role_group",
|
|
168
|
+
organizationUserDeptTransfer: "/api/api/organization/user/dept_transfer",
|
|
123
169
|
listQueryCreate: "/api/api/app/query/create",
|
|
124
170
|
listQueryGet: "/api/api/app/query/get",
|
|
125
171
|
listQueryUpdate: "/api/api/app/query/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);
|