@stalfh233/omc-cli 0.6.7 → 0.6.11
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/approval-flow.js +5 -3
- package/dist/bizservice-config.js +26 -10
- package/dist/call.js +47 -0
- package/dist/capability-overview.js +47 -24
- package/dist/cli-contract.js +73 -26
- package/dist/cli-output.js +41 -2
- package/dist/cli-stdout.js +74 -0
- package/dist/cli.js +135 -52
- package/dist/command-transport.js +8 -1
- package/dist/config-store.js +17 -2
- package/dist/discovery.js +62 -3
- package/dist/field-families.js +35 -6
- package/dist/goal-routes.js +118 -11
- package/dist/human-summary.js +11 -0
- package/dist/lanes/app-contract.js +12 -5
- package/dist/lanes/app.js +221 -134
- package/dist/lanes/apply-changes.js +2 -1
- package/dist/lanes/b2-registration-contract.js +3 -3
- package/dist/lanes/b2-registration.js +890 -693
- package/dist/lanes/businessrule.js +75 -51
- package/dist/lanes/custom-page.js +166 -115
- package/dist/lanes/datarule-contract.js +2 -2
- package/dist/lanes/datarule.js +238 -154
- package/dist/lanes/dictionary.js +253 -110
- package/dist/lanes/environment.js +57 -9
- package/dist/lanes/field.js +160 -107
- package/dist/lanes/folder-contract.js +7 -3
- package/dist/lanes/folder.js +249 -118
- package/dist/lanes/form-contract.js +1 -1
- package/dist/lanes/form.js +188 -64
- package/dist/lanes/list-view.js +405 -369
- package/dist/lanes/meta.js +31 -6
- package/dist/lanes/model-contract.js +1 -1
- package/dist/lanes/model.js +610 -512
- package/dist/lanes/online-js-contract.js +1 -1
- package/dist/lanes/online-js.js +160 -146
- package/dist/lanes/organization.js +905 -640
- package/dist/lanes/programming.js +274 -136
- package/dist/lanes/report-contract.js +2 -2
- package/dist/lanes/report.js +354 -198
- package/dist/lanes/rule-lifecycle.js +294 -140
- package/dist/lanes/runtime-data-contract.js +4 -4
- package/dist/lanes/runtime-data.js +869 -745
- package/dist/lanes/workflow-contract.js +3 -3
- package/dist/lanes/workflow.js +732 -605
- package/dist/model-scaffold.js +26 -5
- package/dist/path-projection.js +196 -14
- package/dist/plan.js +25 -0
- package/dist/programming-workflow.js +55 -3
- package/dist/read-shapes.js +146 -0
- package/dist/read.js +40 -9
- package/dist/recipes/businessrule.js +74 -24
- package/dist/recipes/dictionary.js +7 -4
- package/dist/recipes/form.js +46 -0
- package/dist/recipes/runtime-data.js +3 -2
- package/dist/refusal-guidance.js +29 -5
- package/dist/rule-support.js +14 -1
- package/dist/rule-when.js +2 -2
- package/dist/tool-types.js +9 -3
- package/dist/version-family-gaps.js +101 -0
- package/dist/write.js +2 -1
- package/package.json +6 -4
- package/skills/omc-glossary.md +13 -0
- package/skills/omc-refusals.md +13 -5
- package/skills/omc-tool-selection.md +2 -1
- package/skills/omc-write-hazards.md +5 -2
package/dist/goal-routes.js
CHANGED
|
@@ -159,6 +159,28 @@ function bestRecipe(registry, capabilityId, lane, goal, minScore = 3) {
|
|
|
159
159
|
}
|
|
160
160
|
return best;
|
|
161
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* r28c F2: an authored recipe alias must beat the generic composer/capability
|
|
164
|
+
* ladder. `omc next "按比例赋值"` used to fall through to requirement-analysis
|
|
165
|
+
* (no capability cue) or to `rule-when` (composer groups match "条件…赋值"),
|
|
166
|
+
* even though `rule-condition-formula-action` already owns those phrases.
|
|
167
|
+
* Exact alias hits (≥2 chars) are decisive and return a ready describe action.
|
|
168
|
+
*/
|
|
169
|
+
function bestAliasRecipe(registry, goal) {
|
|
170
|
+
let best;
|
|
171
|
+
for (const tool of registry) {
|
|
172
|
+
for (const recipe of tool.executionMetadata?.recipes ?? []) {
|
|
173
|
+
for (const alias of recipe.aliases ?? []) {
|
|
174
|
+
if (alias.length < 2 || !goal.includes(alias))
|
|
175
|
+
continue;
|
|
176
|
+
const score = alias.length;
|
|
177
|
+
if (best === undefined || score > best.score)
|
|
178
|
+
best = { tool: tool.name, recipe: recipe.id, title: recipe.title, alias, score };
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return best;
|
|
183
|
+
}
|
|
162
184
|
/**
|
|
163
185
|
* The write tools a capability step will actually call, as a compact
|
|
164
186
|
* `{ toolName: [requiredArg] }` map. `omc next` inlines this so an agent stops
|
|
@@ -396,7 +418,8 @@ const COMPOSER_ROUTES = Object.freeze([
|
|
|
396
418
|
id: "approval-flow",
|
|
397
419
|
command: "omc approval-flow --schema <模型code> --steps '<节点名>@role:<角色code>' --apply",
|
|
398
420
|
usage: "omc approval-flow --help",
|
|
399
|
-
|
|
421
|
+
// F1(r27):边界前置——非发起人审批无法在单会话 CLI 内推进,别等 submit 失败才说。
|
|
422
|
+
summary: "线性审批流一条命令:建流程 → 模板+字段只读权限 → 发布(多级用重复 --steps)。边界:非发起人身份的审批无法在单会话 CLI 内推进(单 token);若要验跨账号审批,用第二账号另建环境,或把参与人设为 {Originator}",
|
|
400
423
|
groups: [["审批", "流程"], ["建", "创建", "新建", "配置", "加", "搭"]],
|
|
401
424
|
// Branching / countersign shapes are NOT this composer's business.
|
|
402
425
|
avoid: ["条件分支", "分支", "会签", "并行", "或签", "网关", "子流程", "动态审批人", "清理", "删除", "移除", "purge", "teardown"],
|
|
@@ -420,11 +443,12 @@ const COMPOSER_ROUTES = Object.freeze([
|
|
|
420
443
|
},
|
|
421
444
|
{
|
|
422
445
|
id: "rule-when",
|
|
423
|
-
// `--on` is the trigger (Create/Update
|
|
446
|
+
// `--on` is the trigger (Create/Update only — no Submit): r14's template
|
|
424
447
|
// invented a flag, and the new template-vs---help test caught it offline.
|
|
448
|
+
// r28c F1: "提交时" is NOT a third trigger; callers must pick create|update.
|
|
425
449
|
command: "omc rule-when --schema <模型code> --on Create --when '<条件>' --then 'set:<字段code>=<值>'",
|
|
426
450
|
usage: "omc rule-when --help",
|
|
427
|
-
summary: "
|
|
451
|
+
summary: "条件→动作规则一条命令(--on 仅 create|update,**没有 submit**;BRANCH + 赋值节点拼装),替代手拼 nodeGraph",
|
|
428
452
|
groups: [["规则", "条件"], ["如果", "当", "满足", "时"], ["更新", "赋值", "改为", "设为", "动作"]],
|
|
429
453
|
avoid: [],
|
|
430
454
|
topics: ["omc-business-configuration"],
|
|
@@ -442,8 +466,6 @@ const COMPOSER_ROUTES = Object.freeze([
|
|
|
442
466
|
topics: ["omc-business-configuration"],
|
|
443
467
|
},
|
|
444
468
|
]);
|
|
445
|
-
/** Test seam: the composer registry, so a template typo can be caught offline. */
|
|
446
|
-
export const COMPOSER_ROUTES_FOR_TEST = COMPOSER_ROUTES;
|
|
447
469
|
/**
|
|
448
470
|
* r19 ticket 04 — the ONE question that unlocks each composer. A composer
|
|
449
471
|
* skeleton always carries placeholders, so a composer match is a `needs-input`
|
|
@@ -453,11 +475,15 @@ const COMPOSER_QUESTIONS = Object.freeze({
|
|
|
453
475
|
"model-purge": "要清理哪个模型?给我 schemaCode(不确定就先跑下面的只读判别动作拿清单)",
|
|
454
476
|
"model-scaffold": "给我模型 code、显示名和字段清单(每字段 code/名称/类型;子表放 subFields),我一条命令建完",
|
|
455
477
|
"field-add": "目标存量模型 schemaCode 和字段清单(每字段 code/名称/类型;choices 给字符串数组、字典 dictCode 简写)是什么?需求还带列表固定过滤或演示行的话**一并整句给我——全折进本命令(--conditions/--demo-rows),不要拆成字段/过滤/数据三段串行做**;建完自动发布、重建表单、列表加列",
|
|
456
|
-
"approval-flow": "目标模型 schemaCode 和每级审批节点(节点名@角色code,多级用重复 --steps
|
|
478
|
+
"approval-flow": "目标模型 schemaCode 和每级审批节点(节点名@角色code,多级用重复 --steps)是什么?注意:非发起人身份的审批无法在单会话 CLI 内推进(单 token);若要验跨账号审批,用第二账号另建环境,或把参与人设为 {Originator}",
|
|
457
479
|
compute: "目标模型 schemaCode、结果字段 code、公式是什么?(子表条件聚合再加 --where \"子列 = 值\")",
|
|
458
|
-
"rule-when": "目标模型 schemaCode
|
|
480
|
+
"rule-when": "目标模型 schemaCode、触发时机(仅 Create 或 Update,没有 Submit)、条件(--when)和动作(--then)各是什么?",
|
|
459
481
|
"form-default": "目标模型 schemaCode、字段 code 和默认值是什么?",
|
|
460
482
|
});
|
|
483
|
+
/** Test seam: the composer registry, so a template typo can be caught offline. */
|
|
484
|
+
export const COMPOSER_ROUTES_FOR_TEST = COMPOSER_ROUTES;
|
|
485
|
+
/** Test seam: the clarifying question each composer asks (must mention trigger limits). */
|
|
486
|
+
export const COMPOSER_QUESTIONS_FOR_TEST = COMPOSER_QUESTIONS;
|
|
461
487
|
/** The read-only discriminator that supplies a missing schema code. */
|
|
462
488
|
const LIST_MODELS_ACTION = "omc call model.list --args '{}'";
|
|
463
489
|
function matchComposer(goal, mode) {
|
|
@@ -637,6 +663,71 @@ export function goalRoute(registry, goal) {
|
|
|
637
663
|
},
|
|
638
664
|
};
|
|
639
665
|
}
|
|
666
|
+
// r28c F2: authored recipe aliases beat composers. A short goal like
|
|
667
|
+
// "按比例赋值" has no capability cue and would otherwise fall through;
|
|
668
|
+
// "条件成立时按公式赋值" would be stolen by the rule-when composer.
|
|
669
|
+
if (!readish) {
|
|
670
|
+
const aliasHit = bestAliasRecipe(registry, goal);
|
|
671
|
+
if (aliasHit) {
|
|
672
|
+
const action = `omc describe ${aliasHit.tool} --example ${aliasHit.recipe}`;
|
|
673
|
+
return {
|
|
674
|
+
status: "ok",
|
|
675
|
+
goal,
|
|
676
|
+
confident: true,
|
|
677
|
+
mode,
|
|
678
|
+
write: true,
|
|
679
|
+
match: { kind: "recipe", ref: aliasHit.recipe, title: firstSentence(aliasHit.title), tool: aliasHit.tool, recipe: aliasHit.recipe, alias: aliasHit.alias },
|
|
680
|
+
route: {
|
|
681
|
+
recipe: { tool: aliasHit.tool, id: aliasHit.recipe, command: action },
|
|
682
|
+
readiness: DISCOVERY_AUTHORITY.readiness,
|
|
683
|
+
firstCommands: [action],
|
|
684
|
+
write: true,
|
|
685
|
+
readback: true,
|
|
686
|
+
},
|
|
687
|
+
firstAnswer: {
|
|
688
|
+
state: "ready",
|
|
689
|
+
action,
|
|
690
|
+
why: `命中配方别名「${aliasHit.alias}」→ ${aliasHit.recipe}:describe 首答自带 firstCall 形状,代入你的 schemaCode/字段 code 即可执行`,
|
|
691
|
+
},
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
// r28h F1/F7: org.* lives under workflow. A goal that names organisation
|
|
696
|
+
// nouns (部门/角色/角色组/权限范围/组织架构) and does NOT name model/field/
|
|
697
|
+
// form atoms must not fall into the field→workflow ladder (which then asks
|
|
698
|
+
// for a schemaCode the org faces do not have). Narrow cue-only shortcut —
|
|
699
|
+
// does not change the ladder structure for any other ask.
|
|
700
|
+
{
|
|
701
|
+
const orgCues = ["组织架构", "组织管理", "管理组织", "角色组", "权限范围", "管理范围", "部门树", "员工账号"];
|
|
702
|
+
const orgHit = orgCues.some((cue) => goal.includes(cue)) || (/组织/.test(goal) && /(部门|角色|用户|权限)/.test(goal));
|
|
703
|
+
const atomHit = /(模型|字段|子表|表单|列表|视图|业务规则|流程模板|审批流|在线js|字典)/i.test(goal);
|
|
704
|
+
if (orgHit && !atomHit) {
|
|
705
|
+
const action = "omc call org.department.list --args '{}'";
|
|
706
|
+
return {
|
|
707
|
+
status: "ok",
|
|
708
|
+
goal,
|
|
709
|
+
confident: true,
|
|
710
|
+
mode: readish ? mode : "read",
|
|
711
|
+
write: false,
|
|
712
|
+
match: { kind: "capability", ref: "workflow", title: "组织/部门/角色/权限范围(org.*)", capabilities: ["workflow"] },
|
|
713
|
+
route: {
|
|
714
|
+
capability: "workflow",
|
|
715
|
+
lane: "W7-S29-greenfield",
|
|
716
|
+
note: "组织读写(org.*)挂在 workflow 能力下:部门树 org.department.list、角色/组 org.role.list、用户 org.user.search、成员/范围 org.role.users / org.role.userScope / org.role.setUserScope。先只读盘点,再按需写。",
|
|
717
|
+
readTools: ["org.department.list", "org.role.list", "org.user.search", "org.role.users", "org.role.userScope"],
|
|
718
|
+
readiness: DISCOVERY_AUTHORITY.readiness,
|
|
719
|
+
firstCommands: [action, "omc call org.role.list --args '{}'", "omc call org.user.search --args '{}'"],
|
|
720
|
+
write: false,
|
|
721
|
+
readback: true,
|
|
722
|
+
},
|
|
723
|
+
firstAnswer: {
|
|
724
|
+
state: "ready",
|
|
725
|
+
action,
|
|
726
|
+
why: "命中组织/部门/角色/权限范围线索:先只读部门树,再用 org.role.list / org.user.search 盘点角色与用户(无 schemaCode)",
|
|
727
|
+
},
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
}
|
|
640
731
|
const composer = matchComposer(goal, mode);
|
|
641
732
|
if (composer) {
|
|
642
733
|
const topics = (composer.topics ?? []).filter((topic) => availableTopics.has(topic));
|
|
@@ -669,7 +760,8 @@ export function goalRoute(registry, goal) {
|
|
|
669
760
|
},
|
|
670
761
|
};
|
|
671
762
|
}
|
|
672
|
-
const
|
|
763
|
+
const scoreMap = capabilityScores(registry, goal);
|
|
764
|
+
const scores = [...scoreMap.entries()].sort((left, right) => right[1] - left[1]);
|
|
673
765
|
const fallback = {
|
|
674
766
|
status: "ok",
|
|
675
767
|
goal,
|
|
@@ -722,7 +814,11 @@ export function goalRoute(registry, goal) {
|
|
|
722
814
|
: { firstCommand: `omc commands --json --compact --lane ${capability.lane}` }),
|
|
723
815
|
};
|
|
724
816
|
});
|
|
725
|
-
|
|
817
|
+
// r28e F2:方法论节按「目标里点到哪几个能力」排序,而不是死按 ladder 顺序。
|
|
818
|
+
// 否则「建表单布局 + 在线JS」这类目标会先挂上 model 的通用「标准顺序」,
|
|
819
|
+
// 把真正相关的「表单与视图 / 按钮与在线 JS」挤到后面。
|
|
820
|
+
const sectionOrder = [...ordered].sort((a, b) => (scoreMap.get(b) ?? 0) - (scoreMap.get(a) ?? 0));
|
|
821
|
+
const readSections = sectionPointersFor(sectionOrder);
|
|
726
822
|
const write = readish ? false : ordered.some((id) => routeById.get(id)?.tools.some((name) => registryTools.get(name)?.kind === "write"));
|
|
727
823
|
const whole = isWholePackageWrite(mode, goal, ordered.length);
|
|
728
824
|
// r19 ticket 04: the ladder's first RECIPE command is the first answer
|
|
@@ -742,7 +838,14 @@ export function goalRoute(registry, goal) {
|
|
|
742
838
|
match: { kind: readish ? "read-ladder" : "ladder", ref: ordered.join("→"), capabilities: ordered },
|
|
743
839
|
route: {
|
|
744
840
|
...(whole ? { primary: "intent", atomicSupplement } : { ladder }),
|
|
745
|
-
|
|
841
|
+
// r28e F2(真机复现 2026-11-05):`next "建表单布局、在线JS、表单按钮"`
|
|
842
|
+
// 命中的是 model→form→listview→businessrule→onlinejs 的整包 ladder,
|
|
843
|
+
// 其中只有 businessrule 有配方 —— 于是 firstCommands 唯一一条变成了
|
|
844
|
+
// `rule.save --example business-rule-branch-conditional`(与目标毫无关系),
|
|
845
|
+
// 同时 recipeMatched 把**全部**方法论指针都压空了(readSections: [])。
|
|
846
|
+
// 规则修正:只有单能力路由才允许「命中配方 → 不给方法论节」;
|
|
847
|
+
// 多能力 ladder 里一条配方不能代表其余能力,节指针要留住。
|
|
848
|
+
...routeReads(readSections, recipeCommands.length > 0 && ordered.length === 1, LADDER_INLINE_BUDGET_BYTES),
|
|
746
849
|
readiness: DISCOVERY_AUTHORITY.readiness,
|
|
747
850
|
// r18: when a step already carries a goal-matched recipe, the generic
|
|
748
851
|
// first section ("3. Draft template") is noise — the probe read it and
|
|
@@ -757,7 +860,11 @@ export function goalRoute(registry, goal) {
|
|
|
757
860
|
// graph instead. Surface them at the top level where `firstCommands` is
|
|
758
861
|
// the thing an agent actually reads.
|
|
759
862
|
firstCommands: [
|
|
760
|
-
|
|
863
|
+
// r28e F2:整包 ladder 的主路径是 intent,firstAnswer.action 也写的是
|
|
864
|
+
// `omc intent schema`。此前这里只挑 ladder 里**任意一条**配方贴上来;
|
|
865
|
+
// 当只有 businessrule 有配方时,表单/视图目标会收到 rule.save 的样例,
|
|
866
|
+
// 与首答自相矛盾(且必然跑题)。整包就统一给 intent。
|
|
867
|
+
...(whole ? ["omc intent schema"] : recipeCommands),
|
|
761
868
|
// r13 #3: `capability workflow --executable` is 23 KB and still did not
|
|
762
869
|
// answer "how do I submit an approval" — the answer is a data.submit
|
|
763
870
|
// recipe, which lives in a DIFFERENT capability. When the goal is about
|
package/dist/human-summary.js
CHANGED
|
@@ -95,6 +95,17 @@ function noChange(outcome) {
|
|
|
95
95
|
* 无变更 instead of pretending something changed.
|
|
96
96
|
*/
|
|
97
97
|
export function writeSummary(tool, args, outcome, phase = "done") {
|
|
98
|
+
// r28b F11: data.delete already-absent must NOT read as「已删除…」— humans/CI
|
|
99
|
+
// that skim stderr treat the leading verb as the outcome. Say「未删除」first.
|
|
100
|
+
if (phase === "done" && tool === "data.delete" && outcome?.performed?.skipped === "already-absent") {
|
|
101
|
+
const id = (Array.isArray(args.bizObjectIds) && args.bizObjectIds.length > 0
|
|
102
|
+
? String(args.bizObjectIds[0])
|
|
103
|
+
: undefined) ??
|
|
104
|
+
stringArg(args, "bizObjectId") ??
|
|
105
|
+
"?";
|
|
106
|
+
const schema = stringArg(args, "schemaCode");
|
|
107
|
+
return `未删除(id 不存在,already-absent)${schema ? `:${id}(${schema})` : `:${id}`}`;
|
|
108
|
+
}
|
|
98
109
|
const phrase = phraseOf(tool, args, outcome);
|
|
99
110
|
// An explicitly requested cross-application target is named in BOTH the
|
|
100
111
|
// plan and the result (same source, no divergence).
|
|
@@ -11,8 +11,9 @@ export const APP_WIRE = Object.freeze({
|
|
|
11
11
|
defaultGroupResolution: "explicit groupId > configured app's groupId (auto-configured-app-group) > tenant first named non-fallback group (auto-first-group, warning:resolved-to-first-group); a fallback-group landing (code=all) warns resolved-to-fallback-group; portalHidden:true",
|
|
12
12
|
publishBoundary: "app publish is out of boundary; the operator must publish in the designer and verify the group, otherwise the portal is invisible",
|
|
13
13
|
updateBody: "apppackage/update submits the whole record by id; an explicit groupId takes effect, an omitted groupId is forced back to the default group, and enabled!==true clears workflow favorites — so app.update re-reads the record and only changes groupId while preserving the stored enabled value",
|
|
14
|
-
deleteBody: "apppackage/delete?appCode=; APP_PACKAGE_HAS_NODE refuses when the app still has non-
|
|
14
|
+
deleteBody: "apppackage/delete?appCode=; APP_PACKAGE_HAS_NODE / 600006 refuses when the app still has non-Folder nodes (models/reports/pages); tool pre-checks folder.tree and refuses app-not-empty with children[{id,type,path}] + nextCommand folder.tree (empty folders including isDefault default group do NOT block)",
|
|
15
15
|
deletePoll: "apppackage/list polls to exact absence",
|
|
16
|
+
defaultFolderSeed: "apppackage/create seeds one top-level Folder with name_i18n.en=defaultGroup (display「默认分组」); app.create readback surfaces defaultFolder{id,path,isDefault:true}; folder.tree marks the same node isDefault:true",
|
|
16
17
|
});
|
|
17
18
|
export const APP_LANE_CONTRACT = Object.freeze({
|
|
18
19
|
lane: LANES.app,
|
|
@@ -34,6 +35,7 @@ export const APP_LANE_CONTRACT = Object.freeze({
|
|
|
34
35
|
updateBody: APP_WIRE.updateBody,
|
|
35
36
|
deleteBody: APP_WIRE.deleteBody,
|
|
36
37
|
deletePoll: APP_WIRE.deletePoll,
|
|
38
|
+
defaultFolderSeed: APP_WIRE.defaultFolderSeed,
|
|
37
39
|
}),
|
|
38
40
|
wireBindings: Object.freeze({
|
|
39
41
|
createBody: Object.freeze([behaviorBinding("test/app-management.test.ts#app.create plans, executes under approval, and reads back presence", assertAppContract)]),
|
|
@@ -43,6 +45,7 @@ export const APP_LANE_CONTRACT = Object.freeze({
|
|
|
43
45
|
updateBody: Object.freeze([behaviorBinding("test/app-management.test.ts#app.update preserves false enabled and does not invent i18n fields", assertAppContract)]),
|
|
44
46
|
deleteBody: Object.freeze([behaviorBinding("test/app-management.test.ts#app.delete removes the app via the admin delete endpoint", assertAppContract)]),
|
|
45
47
|
deletePoll: Object.freeze([behaviorBinding("test/app-management.test.ts#app.delete of an absent app is a no-op with proven absence", assertAppContract)]),
|
|
48
|
+
defaultFolderSeed: Object.freeze([behaviorBinding("test/app-folder-and-model-move.test.ts#r28d F2: app.create surfaces defaultFolder; folder.tree marks isDefault", assertAppContract)]),
|
|
46
49
|
}),
|
|
47
50
|
evidence: Object.freeze([
|
|
48
51
|
"packages/omc-cli/test/app-management.test.ts",
|
|
@@ -57,7 +60,7 @@ export const APP_LANE_CONTRACT = Object.freeze({
|
|
|
57
60
|
}),
|
|
58
61
|
"app.create": Object.freeze({
|
|
59
62
|
scope: "app package create",
|
|
60
|
-
description: "
|
|
63
|
+
description: "创建一个设计时应用包(apppackage/create,不是应用内目录)。groupId 可选,缺省解析配置应用所在分组(操作者必然可见);配置应用不存在时回退租户第一个具名非兜底分组(auto-first-group,warning:resolved-to-first-group)。落到兜底分组 code=all 时读回带 warning:resolved-to-fallback-group; portalHidden:true。创建后以 apppackage/list 读回存在性;平台会自动生成默认分组(readback.defaultFolder,isDefault:true)。应用发布属边界外:建成后需人工在设计器发布并核对该分组,否则门户不可见。",
|
|
61
64
|
}),
|
|
62
65
|
"app.update": Object.freeze({
|
|
63
66
|
scope: "app package update",
|
|
@@ -65,7 +68,7 @@ export const APP_LANE_CONTRACT = Object.freeze({
|
|
|
65
68
|
}),
|
|
66
69
|
"app.delete": Object.freeze({
|
|
67
70
|
scope: "app package delete absence",
|
|
68
|
-
description: "
|
|
71
|
+
description: "删除一个应用包(apppackage/delete?appCode=,管理员面)。应用下还有模型/报表/自定义页等非目录节点时以 app-not-empty(平台 APP_PACKAGE_HAS_NODE / 600006)拒绝,回执列 children[{id,type,path}] 且 nextCommand 指向 folder.tree;空目录(含默认分组)不挡删除。以 apppackage/list 轮询读回精确缺席。必须显式传 `code`,没有默认应用,避免误删。",
|
|
69
72
|
}),
|
|
70
73
|
}),
|
|
71
74
|
});
|
|
@@ -74,11 +77,11 @@ export function assertAppContract() {
|
|
|
74
77
|
if (!APP_WIRE.defaultGroupResolution.includes("resolved-to-first-group"))
|
|
75
78
|
throw new Error("app contract pins the first-group fallback warning token");
|
|
76
79
|
assertDescriptionCarries(APP_LANE_CONTRACT, "app.list", ["apppackage/list", "跨应用"]);
|
|
77
|
-
assertDescriptionCarries(APP_LANE_CONTRACT, "app.create", ["apppackage/create", "resolved-to-first-group", "边界外"]);
|
|
80
|
+
assertDescriptionCarries(APP_LANE_CONTRACT, "app.create", ["apppackage/create", "resolved-to-first-group", "边界外", "defaultFolder"]);
|
|
78
81
|
if (!APP_WIRE.createReadiness.includes("zero"))
|
|
79
82
|
throw new Error("app contract must pin the zero-wire prerequisite refusal");
|
|
80
83
|
assertDescriptionCarries(APP_LANE_CONTRACT, "app.update", ["apppackage/update", "groupId", "enabled", "原样保留", "AppPackageController.update"]);
|
|
81
|
-
assertDescriptionCarries(APP_LANE_CONTRACT, "app.delete", ["apppackage/delete", "APP_PACKAGE_HAS_NODE", "缺席"]);
|
|
84
|
+
assertDescriptionCarries(APP_LANE_CONTRACT, "app.delete", ["apppackage/delete", "APP_PACKAGE_HAS_NODE", "app-not-empty", "缺席"]);
|
|
82
85
|
if (!APP_WIRE.createBody.includes("apppackage/create"))
|
|
83
86
|
throw new Error("app contract must pin the create endpoint");
|
|
84
87
|
if (!APP_WIRE.defaultGroupResolution.includes("auto-first-group"))
|
|
@@ -89,7 +92,11 @@ export function assertAppContract() {
|
|
|
89
92
|
throw new Error("app contract must pin the whole-record update quirk");
|
|
90
93
|
if (!APP_WIRE.deleteBody.includes("APP_PACKAGE_HAS_NODE"))
|
|
91
94
|
throw new Error("app contract must pin the delete precondition");
|
|
95
|
+
if (!APP_WIRE.deleteBody.includes("app-not-empty"))
|
|
96
|
+
throw new Error("app contract must pin the app-not-empty refusal");
|
|
92
97
|
if (!APP_WIRE.deletePoll.includes("exact absence"))
|
|
93
98
|
throw new Error("app contract must pin the delete absence poll");
|
|
99
|
+
if (!APP_WIRE.defaultFolderSeed.includes("defaultGroup"))
|
|
100
|
+
throw new Error("app contract must pin the default-folder seed token");
|
|
94
101
|
}
|
|
95
102
|
//# sourceMappingURL=app-contract.js.map
|