@stalfh233/omc-cli 0.4.77 → 0.4.78
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/form-support.js +62 -16
- package/package.json +3 -3
- package/skills/omc-capabilities.md +2 -0
package/dist/form-support.js
CHANGED
|
@@ -719,21 +719,25 @@ export function validateFormButtons(buttons, tool) {
|
|
|
719
719
|
if (!FORM_BUTTON_LOCATIONS.includes(useLocation))
|
|
720
720
|
throw new ToolRefusalError({ status: "refused", blocker: "form-buttons-invalid", message: `${tool}: buttons[${index}].useLocation 只能是 ${FORM_BUTTON_LOCATIONS.join("|")}(表单工具栏按钮用 FORM)` });
|
|
721
721
|
const bindAction = typeof button.bindAction === "string" ? button.bindAction : "";
|
|
722
|
+
// `script` is the ergonomic spelling of actionConfig.scriptData (same as
|
|
723
|
+
// listview.configure); the explicit script wins over a stale config blob.
|
|
724
|
+
const scriptShorthand = typeof button.script === "string" ? button.script : undefined;
|
|
722
725
|
if (!FORM_BUTTON_BIND_ACTIONS.has(bindAction))
|
|
723
726
|
throw new ToolRefusalError({ status: "refused", blocker: "form-buttons-invalid", message: `${tool}: buttons[${index}].bindAction 必须是 ButtonBindAction 名(SCRIPT/BUSINESS_RULE/FLOW/FORM/ADD_DATA/CUSTOM_PAGE/…)` });
|
|
724
727
|
const operateType = typeof button.operateType === "string" ? button.operateType : "";
|
|
725
728
|
if (!FORM_BUTTON_OPERATE_TYPES.has(operateType))
|
|
726
729
|
throw new ToolRefusalError({ status: "refused", blocker: "form-buttons-invalid", message: `${tool}: buttons[${index}].operateType 必须是 WITH_VIEW 或 WITH_OUT_VIEW` });
|
|
730
|
+
// Decode once: the SCRIPT guard and the script shorthand both read it.
|
|
731
|
+
const rawConfig = button.actionConfig;
|
|
732
|
+
const parsedConfigRaw = typeof rawConfig === "string" ? (() => { try {
|
|
733
|
+
return JSON.parse(rawConfig);
|
|
734
|
+
}
|
|
735
|
+
catch {
|
|
736
|
+
return undefined;
|
|
737
|
+
} })() : rawConfig;
|
|
738
|
+
const config = parsedConfigRaw && typeof parsedConfigRaw === "object" && !Array.isArray(parsedConfigRaw) ? parsedConfigRaw : undefined;
|
|
727
739
|
if (bindAction === "SCRIPT" || bindAction === "BUSINESS_RULE" || bindAction === "FLOW" || bindAction === "ADD_DATA" || bindAction === "FORM") {
|
|
728
|
-
|
|
729
|
-
const record = typeof actionConfig === "string" ? (() => { try {
|
|
730
|
-
return JSON.parse(actionConfig);
|
|
731
|
-
}
|
|
732
|
-
catch {
|
|
733
|
-
return undefined;
|
|
734
|
-
} })() : actionConfig;
|
|
735
|
-
const config = record && typeof record === "object" && !Array.isArray(record) ? record : undefined;
|
|
736
|
-
if (bindAction === "SCRIPT" && typeof config?.scriptData !== "string")
|
|
740
|
+
if (bindAction === "SCRIPT" && scriptShorthand === undefined && typeof config?.scriptData !== "string")
|
|
737
741
|
throw new ToolRefusalError({ status: "refused", blocker: "form-buttons-invalid", message: `${tool}: buttons[${index}] SCRIPT 需要 actionConfig.scriptData(前端脚本源码字符串)` });
|
|
738
742
|
if ((bindAction === "BUSINESS_RULE" || bindAction === "FLOW") && (typeof button.targetCode !== "string" || button.targetCode === ""))
|
|
739
743
|
throw new ToolRefusalError({ status: "refused", blocker: "form-buttons-invalid", message: `${tool}: buttons[${index}] ${bindAction} 需要 targetCode` });
|
|
@@ -745,9 +749,13 @@ export function validateFormButtons(buttons, tool) {
|
|
|
745
749
|
useLocation,
|
|
746
750
|
bindAction,
|
|
747
751
|
operateType,
|
|
748
|
-
//
|
|
749
|
-
//
|
|
750
|
-
...(
|
|
752
|
+
// actionConfig is a String column: serialize exactly once (double
|
|
753
|
+
// encoding breaks the renderer). `script` merges into scriptData.
|
|
754
|
+
...(scriptShorthand !== undefined
|
|
755
|
+
? { actionConfig: JSON.stringify({ ...(config ?? {}), scriptData: scriptShorthand }) }
|
|
756
|
+
: button.actionConfig !== undefined && typeof button.actionConfig !== "string"
|
|
757
|
+
? { actionConfig: JSON.stringify(button.actionConfig) }
|
|
758
|
+
: {}),
|
|
751
759
|
sortKey: typeof button.sortKey === "number" ? button.sortKey : index,
|
|
752
760
|
};
|
|
753
761
|
});
|
|
@@ -779,7 +787,8 @@ export function mergeFormOptionsWithButtons(existingOptions, input, tool) {
|
|
|
779
787
|
const removeCodes = input.removeButtonCodes ?? [];
|
|
780
788
|
if (hasButtons && hasJson)
|
|
781
789
|
throw new ToolRefusalError({ status: "refused", blocker: "form-buttons-ambiguous", message: `${tool}: buttons 与 customButtonJson 只能给一种(buttons 数组更好读;customButtonJson 用于原样透传读回值)` });
|
|
782
|
-
|
|
790
|
+
const scriptPatches = input.buttonScripts ?? [];
|
|
791
|
+
if (!hasButtons && !hasJson && removeCodes.length === 0 && scriptPatches.length === 0)
|
|
783
792
|
return undefined;
|
|
784
793
|
let incoming = [];
|
|
785
794
|
if (hasButtons) {
|
|
@@ -802,6 +811,21 @@ export function mergeFormOptionsWithButtons(existingOptions, input, tool) {
|
|
|
802
811
|
// `replace` restores the whole-list semantics.
|
|
803
812
|
const mode = input.buttonsMode ?? "merge";
|
|
804
813
|
const existing = readFormButtons(options) ?? [];
|
|
814
|
+
if (scriptPatches.length > 0) {
|
|
815
|
+
// Patch scriptData on EXISTING buttons only — the write counterpart of
|
|
816
|
+
// form.get --buttonScripts. An unknown code refuses rather than silently
|
|
817
|
+
// creating a button with no bindAction.
|
|
818
|
+
const known = new Set(existing.map((button) => String(button.code ?? "")));
|
|
819
|
+
const missing = scriptPatches.filter((entry) => !known.has(entry.code)).map((entry) => entry.code);
|
|
820
|
+
if (missing.length > 0)
|
|
821
|
+
throw new ToolRefusalError({
|
|
822
|
+
status: "refused",
|
|
823
|
+
blocker: "form-button-absent",
|
|
824
|
+
buttonCodes: missing,
|
|
825
|
+
available: [...known],
|
|
826
|
+
message: `${tool}: 表单没有按钮 ${missing.join("、")}(buttonScripts 只改已有按钮的脚本);可用:${[...known].join("、") || "(无)"}。新建按钮用 buttons + script`,
|
|
827
|
+
});
|
|
828
|
+
}
|
|
805
829
|
let final;
|
|
806
830
|
if (mode === "replace") {
|
|
807
831
|
final = incoming;
|
|
@@ -812,6 +836,27 @@ export function mergeFormOptionsWithButtons(existingOptions, input, tool) {
|
|
|
812
836
|
byCode.set(String(button.code), { ...(byCode.get(String(button.code)) ?? {}), ...button });
|
|
813
837
|
for (const code of removeCodes)
|
|
814
838
|
byCode.delete(String(code));
|
|
839
|
+
for (const patch of scriptPatches) {
|
|
840
|
+
const current = byCode.get(patch.code);
|
|
841
|
+
if (!current)
|
|
842
|
+
continue;
|
|
843
|
+
let config = {};
|
|
844
|
+
const rawConfig = current.actionConfig;
|
|
845
|
+
if (typeof rawConfig === "string" && rawConfig.trim() !== "") {
|
|
846
|
+
try {
|
|
847
|
+
const parsed = JSON.parse(rawConfig);
|
|
848
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed))
|
|
849
|
+
config = parsed;
|
|
850
|
+
}
|
|
851
|
+
catch {
|
|
852
|
+
config = {};
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
else if (rawConfig !== null && typeof rawConfig === "object" && !Array.isArray(rawConfig)) {
|
|
856
|
+
config = { ...rawConfig };
|
|
857
|
+
}
|
|
858
|
+
byCode.set(patch.code, { ...current, actionConfig: JSON.stringify({ ...config, scriptData: patch.script }) });
|
|
859
|
+
}
|
|
815
860
|
final = [...byCode.values()].map((button, index) => ({ ...button, sortKey: typeof button.sortKey === "number" ? button.sortKey : index }));
|
|
816
861
|
}
|
|
817
862
|
if (removeCodes.length > 0 && mode === "replace")
|
|
@@ -1062,7 +1107,7 @@ export async function executeFormDraftWrite(session, args) {
|
|
|
1062
1107
|
// turns them into h_biz_button rows (triggerType=FORM). Merge rather than
|
|
1063
1108
|
// replace so unrelated options (presetButtonManage, sheetPresetButtonManage)
|
|
1064
1109
|
// survive.
|
|
1065
|
-
const mergedOptions = mergeFormOptionsWithButtons(pre?.options, { buttons: args.buttons, customButtonJson: args.customButtonJson, buttonsMode: args.buttonsMode, removeButtonCodes: args.removeButtonCodes }, "form.draft");
|
|
1110
|
+
const mergedOptions = mergeFormOptionsWithButtons(pre?.options, { buttons: args.buttons, customButtonJson: args.customButtonJson, buttonsMode: args.buttonsMode, removeButtonCodes: args.removeButtonCodes, buttonScripts: args.buttonScripts }, "form.draft");
|
|
1066
1111
|
const payload = {
|
|
1067
1112
|
...(pre ?? {}),
|
|
1068
1113
|
schemaCode: args.schemaCode,
|
|
@@ -1243,7 +1288,8 @@ export async function executeFormPublishWrite(session, args) {
|
|
|
1243
1288
|
// drop the buttons (live 2026-10-23).
|
|
1244
1289
|
const hasButtonDelta = args.buttons !== undefined ||
|
|
1245
1290
|
args.customButtonJson !== undefined ||
|
|
1246
|
-
(Array.isArray(args.removeButtonCodes) && args.removeButtonCodes.length > 0)
|
|
1291
|
+
(Array.isArray(args.removeButtonCodes) && args.removeButtonCodes.length > 0) ||
|
|
1292
|
+
(Array.isArray(args.buttonScripts) && args.buttonScripts.length > 0);
|
|
1247
1293
|
const forcePromote = args.force === true || args.rebuild === true || hasButtonDelta;
|
|
1248
1294
|
if (pre.published === true && !forcePromote)
|
|
1249
1295
|
return { performed: { skipped: "already-published" }, readback: { published: true } };
|
|
@@ -1274,7 +1320,7 @@ export async function executeFormPublishWrite(session, args) {
|
|
|
1274
1320
|
// h_biz_button(triggerType=FORM) rows during THIS publish call
|
|
1275
1321
|
// (ModelManagementFacadeImpl.publishBizForm:1022). Passing them here is the
|
|
1276
1322
|
// only way to create form buttons without the designer.
|
|
1277
|
-
const mergedOptions = mergeFormOptionsWithButtons(pre.options, { buttons: args.buttons, customButtonJson: args.customButtonJson, buttonsMode: args.buttonsMode, removeButtonCodes: args.removeButtonCodes }, "form.publish");
|
|
1323
|
+
const mergedOptions = mergeFormOptionsWithButtons(pre.options, { buttons: args.buttons, customButtonJson: args.customButtonJson, buttonsMode: args.buttonsMode, removeButtonCodes: args.removeButtonCodes, buttonScripts: args.buttonScripts }, "form.publish");
|
|
1278
1324
|
const payload = {
|
|
1279
1325
|
...pre,
|
|
1280
1326
|
...(mergedOptions !== undefined ? { options: mergedOptions } : {}),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stalfh233/omc-cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.78",
|
|
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.78",
|
|
54
54
|
"commander": "14.0.3",
|
|
55
|
-
"omc-core": "0.4.
|
|
55
|
+
"omc-core": "0.4.78",
|
|
56
56
|
"proper-lockfile": "4.1.2",
|
|
57
57
|
"write-file-atomic": "6.0.0",
|
|
58
58
|
"zod": "^3.23.8",
|
|
@@ -99,6 +99,7 @@ only the section for the capability you need —
|
|
|
99
99
|
|
|
100
100
|
- **能做哪些事**:表单 draft-first 布局、发布、`publishedHtmlJson` 绑定读回;把已发布字段绑进默认表单;**表单工具栏按钮**(`options.customButtonJson` → publish 生成 FORM 按钮)与按钮执行。
|
|
101
101
|
- **工具**:`form.get`、`form.draft`、`form.publish`、`configure_form`、`form.runButton`
|
|
102
|
+
- **按钮脚本(JS)读写**:`form.get --buttonScripts` 回 `scriptData`/sha;写用 `form.publish --buttonScripts [{code,script}]`(只改已有按钮脚本),或在 `buttons[]` 里给 `script`。**视图(列表)本身没有 JS 面**(`h_biz_query` 无脚本列);脚本只有三处:表单在线JS、按钮JS、自定义页面 JS。
|
|
102
103
|
- **recipe**:`omc describe configure_form --example publish-default-form`
|
|
103
104
|
- **边界 / 陷阱**:默认表单已存在,用 `configure_form`/`form.draft(rebuild)`/`form.publish(rebuild:true)` 改它,不要新建;改字段形状后刷新表单一调用 `form.publish rebuild:true`。
|
|
104
105
|
- **每个模型交付前其默认表单必须已发布**:只建草稿=用户无法录入/查看。用 `configure_form` 或 `form.draft(rebuild:true)`+`form.publish` 发布。
|
|
@@ -110,6 +111,7 @@ only the section for the capability you need —
|
|
|
110
111
|
|
|
111
112
|
- **能做哪些事**:视图表头列、排序、筛选条件生命周期(create/configure/delete)。
|
|
112
113
|
- **工具**:`listview.get`、`listview.list`、`listview.create`、`listview.configure`、`listview.delete`
|
|
114
|
+
- **按钮脚本(JS)读写**:`listview.get --buttonCode <code>`(或 `--buttons`)直接回 `scriptData`/`scriptBytes`/`scriptSha256`,不用翻 artifact;写用 `listview.configure --buttonScripts [{code,script}]` 只改脚本(保留其它字段),或在 `buttons[]` 里用 `script` 简写(= `actionConfig.scriptData`)。
|
|
113
115
|
- **recipe**:`omc describe listview.configure --example configure-default-list`
|
|
114
116
|
- **边界 / 陷阱**:默认视图已存在,`listview.configure` 省略 code 即配置默认视图。**按字段分组(queryLayer)未取证**——别猜写。
|
|
115
117
|
|