lan-low-code-cli 2.6.0 → 2.7.0

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.
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ /**
3
+ * 动作流节点元数据表
4
+ *
5
+ * 逐 case 翻译 `action-flow-ide/enter.tsx:217-336` 的 generateNodeData:
6
+ * IDE 保存时只挑每种 nodeType 的专属字段,其余字段丢弃。
7
+ * 必填规则来自 `configuration/nodes/utils.tsx:37-288` 与 `validate.ts:13-17`。
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.CONTEXT_KEYS = exports.FLOW_CODE_PATTERN = exports.NODE_ID_PATTERN = exports.KNOWN_START_WAYS = exports.MULTI_OUT_NODE_TYPES = exports.NODE_REQUIRED = exports.NODE_DEFAULTS = exports.NODE_FIELD_WHITELIST = void 0;
11
+ /** 节点专属字段白名单;未列出的 nodeType 视为未知类型(透传 config 并告警) */
12
+ exports.NODE_FIELD_WHITELIST = {
13
+ StartNode: ['startNodeDef'],
14
+ EndNode: ['outParamType', 'outParam', 'outputParamMapping'],
15
+ BranchNode: ['conditionType', 'flowWay', 'defaultTargetRef', 'ruleConfig', 'formulaConfig'],
16
+ LoopNode: ['loopDefine', 'subProcess'],
17
+ QueryLoopNode: [
18
+ 'target', 'conditions', 'sortType', 'containsDeleted', 'executeTime',
19
+ 'loopDefine', 'context_outputParam', 'limitTime',
20
+ ],
21
+ FilterDataNode: ['bindNode', 'formula'],
22
+ MessageNode: [
23
+ 'bindNode', 'subject', 'notifyType', 'todoType', 'messageTarget', 'messageLink',
24
+ 'eventCode', 'codeValue', 'notifyCategoryCode', 'subjectType', 'templateCode',
25
+ 'templateFields', 'grantPermission',
26
+ ],
27
+ AddDataNode: ['bindNode', 'dataSourceVO', 'flow', 'commitType', 'fields', 'autoMatchField', 'fastExecute'],
28
+ DeleteDataNode: ['bindNode'],
29
+ QueryDataNode: ['target', 'conditions', 'sorts', 'resultType', 'maxSize', 'containsDeleted'],
30
+ UpdateDataNode: ['bindNode', 'fields', 'fastExecute'],
31
+ CreateKmReviewNode: ['bindNode', 'kmReviewTemplate', 'fdCreator', 'fdSubject', 'commitType', 'fields'],
32
+ AddOrUpdateNode: [
33
+ 'bindNode', 'dataSourceVO', 'flow', 'commitType', 'fields', 'condition',
34
+ 'autoMatchField', 'fastExecute',
35
+ ],
36
+ AssignValueNode: ['assignValueRuleConfigs'],
37
+ TransformNode: ['outParamType', 'outParam', 'outputParamMapping'],
38
+ ExecuteNode: ['outParamType', 'executeCode', 'inputParamMapping'],
39
+ FlowBotsNode: ['outParamType', 'flowBotsCode', 'inputParamMapping'],
40
+ InterfaceNode: ['apiType', 'apiMethodPath', 'inputParamMapping'],
41
+ SubActionNode: ['outParam', 'outParamType', 'actionFlowCode', 'inputParamMapping'],
42
+ MessageEliminateNode: ['eliminateRange', 'eliminateType', 'eventCode', 'bindNode'],
43
+ QueryStatisticsNode: [
44
+ 'nativeNodeType', 'resultType', 'target', 'conditions', 'sorts',
45
+ 'columns', 'statisticsDimensions', 'statisticsItems',
46
+ ],
47
+ DispatchNode: [],
48
+ };
49
+ /** 与 IDE 一致的缺省值(styleSheet.ts:404-445) */
50
+ exports.NODE_DEFAULTS = {
51
+ BranchNode: { conditionType: 'formula', flowWay: 'singleBranch' },
52
+ QueryDataNode: { resultType: 'single' },
53
+ LoopNode: { loopDefine: { type: 'LoopArray' } },
54
+ MessageNode: { grantPermission: true },
55
+ };
56
+ /** 发布前 IDE 强校验的必填字段(configuration/nodes/utils.tsx:37-288) */
57
+ exports.NODE_REQUIRED = {
58
+ AddDataNode: ['dataSourceVO', 'flow', 'fields'],
59
+ QueryDataNode: ['target'],
60
+ UpdateDataNode: ['bindNode'],
61
+ DeleteDataNode: ['bindNode'],
62
+ FilterDataNode: ['bindNode'],
63
+ BranchNode: ['conditionType', 'flowWay'],
64
+ SubActionNode: ['actionFlowCode'],
65
+ InterfaceNode: ['apiType', 'apiMethodPath'],
66
+ ExecuteNode: ['executeCode'],
67
+ QueryStatisticsNode: ['target'],
68
+ AddOrUpdateNode: ['dataSourceVO', 'flow', 'fields'],
69
+ LoopNode: ['loopDefine'],
70
+ AssignValueNode: ['assignValueRuleConfigs'],
71
+ CreateKmReviewNode: ['kmReviewTemplate', 'fdSubject'],
72
+ FlowBotsNode: ['flowBotsCode'],
73
+ MessageNode: ['subject', 'messageTarget'],
74
+ MessageEliminateNode: ['eliminateRange', 'eliminateType'],
75
+ };
76
+ /** 允许多条出边的节点(validateCommonVertex 的 getwayMap,另有 StartNode) */
77
+ exports.MULTI_OUT_NODE_TYPES = new Set(['BranchNode', 'parallelGateway', 'StartNode']);
78
+ exports.KNOWN_START_WAYS = [
79
+ 'StartScheduledNode',
80
+ 'StartDataNode',
81
+ 'StartEventNode',
82
+ 'StartActiveNode',
83
+ ];
84
+ /** 节点编码 /^[0-9a-zA-Z_]{2,32}$/(configuration/nodes/utils.tsx:12-30) */
85
+ exports.NODE_ID_PATTERN = /^[0-9a-zA-Z_]{2,32}$/;
86
+ /** 动作流编码 /^[a-zA-Z0-9_]{1,30}$/(topbar/index.tsx:262-279) */
87
+ exports.FLOW_CODE_PATTERN = /^[a-zA-Z0-9_]{1,30}$/;
88
+ /** extraAttr 六个上下文键(enter.tsx:383-390) */
89
+ exports.CONTEXT_KEYS = [
90
+ 'context_bindFormId',
91
+ 'context_bindNode',
92
+ 'context_nodeParam',
93
+ 'context_isMulti',
94
+ 'context_fdInParam',
95
+ 'context_outputParam',
96
+ ];
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ /**
3
+ * 动作流编译内核 · 类型定义
4
+ *
5
+ * 依据 `sys-modeling/src/components/action-flow-ide/enter.tsx:339-428`:
6
+ * `fdDesign` 是 JSON 字符串,顶层 `{ nodes, lines, processVar? }`;
7
+ * 节点为扁平结构:`drawAttr.bounds` + `extraAttr.context_*` + 各 nodeType 专属字段。
8
+ * IDE 回显时原样使用 x/y,不重算坐标,因此坐标必须由本内核生成。
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ /**
3
+ * 动作流离线校验器
4
+ *
5
+ * 移植 IDE 发布前的 validate(true):连线数量、必填、编码格式、无环
6
+ * (enter.tsx:468-529、validate.ts:150-327、mxgraph/utils.tsx:1206-1258)。
7
+ * 目的是在发包之前把错误挡住——CLI 直接调 addPublish 会绕过 IDE 校验。
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.validateSpec = validateSpec;
11
+ exports.validateGraph = validateGraph;
12
+ exports.hasErrors = hasErrors;
13
+ const schema_1 = require("./schema");
14
+ const err = (code, message, nodeId) => ({
15
+ level: 'error', code, message, nodeId,
16
+ });
17
+ const warn = (code, message, nodeId) => ({
18
+ level: 'warn', code, message, nodeId,
19
+ });
20
+ /** 基础信息校验 */
21
+ function validateSpec(spec) {
22
+ const out = [];
23
+ if (!spec.name || !String(spec.name).trim())
24
+ out.push(err('SPEC_NAME_REQUIRED', '缺少流程名称 name'));
25
+ if (!spec.code || !schema_1.FLOW_CODE_PATTERN.test(spec.code)) {
26
+ out.push(err('SPEC_CODE_INVALID', `流程编码 code 必须匹配 /^[a-zA-Z0-9_]{1,30}$/:${spec.code ?? '(空)'}`));
27
+ }
28
+ const startWay = spec.startWay ?? 'StartActiveNode';
29
+ if (!schema_1.KNOWN_START_WAYS.includes(startWay)) {
30
+ out.push(err('SPEC_START_WAY_INVALID', `未知启动方式:${startWay}`));
31
+ }
32
+ if (!Array.isArray(spec.nodes))
33
+ out.push(err('SPEC_NODES_REQUIRED', '缺少 nodes 数组'));
34
+ if (!Array.isArray(spec.edges))
35
+ out.push(err('SPEC_EDGES_REQUIRED', '缺少 edges 数组'));
36
+ return out;
37
+ }
38
+ /** 开始节点按启动方式的必填项(configuration/nodes/utils.tsx:40-61、validate.ts:13-17) */
39
+ function validateStartNode(graph, spec, startWay) {
40
+ const out = [];
41
+ const start = graph.nodes.find((n) => n._isStart);
42
+ if (!start)
43
+ return out;
44
+ const provided = start.config?.startNodeDef;
45
+ const def = provided ?? spec.trigger ?? {};
46
+ const has = (k) => def[k] !== undefined && def[k] !== null && def[k] !== '';
47
+ if (startWay === 'StartScheduledNode' && !has('scheduledDef')) {
48
+ out.push(err('START_SCHEDULE_REQUIRED', '定时触发必须在 trigger 中提供 scheduledDef', start.id));
49
+ }
50
+ if (startWay === 'StartDataNode' && !has('target')) {
51
+ out.push(err('START_DATA_TARGET_REQUIRED', '数据监听必须在 trigger 中提供 target(触发表单)', start.id));
52
+ }
53
+ if (startWay === 'StartEventNode' && !has('eventCode')) {
54
+ out.push(err('START_EVENT_REQUIRED', '事件监听必须在 trigger 中提供 eventCode', start.id));
55
+ }
56
+ if (startWay === 'StartActiveNode') {
57
+ const runTypes = def.fdRunType;
58
+ const byFlags = def.operateTrigger === true || def.bpmTrigger === true || def.apiTrigger === true;
59
+ const byList = Array.isArray(runTypes) && runTypes.length > 0;
60
+ if (!byFlags && !byList) {
61
+ out.push(err('START_ACTIVE_TRIGGER_REQUIRED', '主动触发必须提供 trigger.fdRunType(operate / bpm / api 至少一项)或对应布尔开关', start.id));
62
+ }
63
+ }
64
+ return out;
65
+ }
66
+ /** 图结构 + 节点必填校验 */
67
+ function validateGraph(graph, spec, startWay = 'StartActiveNode') {
68
+ const out = [];
69
+ const ids = new Set();
70
+ const byId = new Map(graph.nodes.map((n) => [n.id, n]));
71
+ for (const node of graph.nodes) {
72
+ if (!schema_1.NODE_ID_PATTERN.test(node.id)) {
73
+ out.push(err('NODE_ID_INVALID', `节点 id 必须匹配 /^[0-9a-zA-Z_]{2,32}$/:${node.id}`, node.id));
74
+ }
75
+ if (ids.has(node.id))
76
+ out.push(err('NODE_ID_DUPLICATE', `节点 id 重复:${node.id}`, node.id));
77
+ ids.add(node.id);
78
+ if (!schema_1.NODE_FIELD_WHITELIST[node.type]) {
79
+ out.push(warn('NODE_TYPE_UNKNOWN', `未知节点类型 ${node.type},config 将原样透传;请用 llc flow templates list 核对环境支持的节点`, node.id));
80
+ }
81
+ // 与编译器一致:默认值(如 BranchNode.conditionType / flowWay)也算已填
82
+ const config = { ...(schema_1.NODE_DEFAULTS[node.type] ?? {}), ...(node.config ?? {}) };
83
+ for (const field of schema_1.NODE_REQUIRED[node.type] ?? []) {
84
+ if (config[field] === undefined || config[field] === null || config[field] === '') {
85
+ out.push(err('NODE_FIELD_REQUIRED', `节点 ${node.id}(${node.type})缺少必填字段 ${field}`, node.id));
86
+ }
87
+ }
88
+ }
89
+ const inCount = new Map();
90
+ const outCount = new Map();
91
+ const seenEdge = new Set();
92
+ for (const edge of graph.edges) {
93
+ const key = `${edge.from}->${edge.to}`;
94
+ if (seenEdge.has(key)) {
95
+ out.push(err('EDGE_DUPLICATE', `重复连线:${key}(IDE 设置 setMultigraph(false))`));
96
+ }
97
+ seenEdge.add(key);
98
+ if (!byId.has(edge.from))
99
+ out.push(err('EDGE_SOURCE_MISSING', `连线起点不存在:${edge.from}`));
100
+ if (!byId.has(edge.to))
101
+ out.push(err('EDGE_TARGET_MISSING', `连线终点不存在:${edge.to}`));
102
+ if (edge.from === edge.to)
103
+ out.push(err('EDGE_SELF_LOOP', `禁止自连:${edge.from}`));
104
+ inCount.set(edge.to, (inCount.get(edge.to) ?? 0) + 1);
105
+ outCount.set(edge.from, (outCount.get(edge.from) ?? 0) + 1);
106
+ }
107
+ for (const node of graph.nodes) {
108
+ const incoming = inCount.get(node.id) ?? 0;
109
+ const outgoing = outCount.get(node.id) ?? 0;
110
+ if (node._isStart && incoming > 0)
111
+ out.push(err('START_HAS_IN_EDGE', '开始节点不能有入边', node.id));
112
+ if (node._isEnd && outgoing > 0)
113
+ out.push(err('END_HAS_OUT_EDGE', '结束节点不能有出边', node.id));
114
+ if (!node._isEnd && outgoing === 0) {
115
+ out.push(err('NODE_NO_OUT_EDGE', `节点 ${node.id} 没有出边`, node.id));
116
+ }
117
+ if (!node._isStart && incoming === 0) {
118
+ out.push(err('NODE_NO_IN_EDGE', `节点 ${node.id} 没有入边`, node.id));
119
+ }
120
+ if (outgoing > 1 && !schema_1.MULTI_OUT_NODE_TYPES.has(node.type)) {
121
+ out.push(err('NODE_MULTI_OUT_EDGE', `节点 ${node.id}(${node.type})有 ${outgoing} 条出边;仅 BranchNode / StartNode / parallelGateway 允许多出边`, node.id));
122
+ }
123
+ }
124
+ out.push(...validateStartNode(graph, spec, startWay));
125
+ return out;
126
+ }
127
+ function hasErrors(diagnostics) {
128
+ return diagnostics.some((d) => d.level === 'error');
129
+ }
package/dist/index.js CHANGED
@@ -6,12 +6,13 @@ const app_1 = require("./commands/app");
6
6
  const config_1 = require("./commands/config");
7
7
  const control_1 = require("./commands/control");
8
8
  const field_1 = require("./commands/field");
9
+ const flow_1 = require("./commands/flow");
9
10
  const form_1 = require("./commands/form");
10
11
  const locale_1 = require("./commands/locale");
11
12
  const operation_1 = require("./commands/operation");
12
13
  const template_1 = require("./commands/template");
13
14
  const logger_1 = require("./utils/logger");
14
- const VERSION = '2.6.0';
15
+ const VERSION = '2.7.0';
15
16
  const program = new commander_1.Command();
16
17
  exports.program = program;
17
18
  program
@@ -38,6 +39,7 @@ program.hook('preAction', (thisCommand, actionCommand) => {
38
39
  (0, field_1.registerFieldCommand)(program);
39
40
  (0, operation_1.registerOperationCommand)(program);
40
41
  (0, template_1.registerTemplateCommand)(program);
42
+ (0, flow_1.registerFlowCommand)(program);
41
43
  (0, locale_1.registerLocaleCommand)(program);
42
44
  (0, control_1.registerControlCommand)(program);
43
45
  /**
@@ -114,6 +116,15 @@ ${logger_1.color.bold('常用查询')}
114
116
  llc operation create --form-id <fdId> --name 打开帮助 --code openHelp --type link --url https://example.com
115
117
  llc operation delete --id <operationId> --yes 删除业务操作(被引用时会拒绝)
116
118
  llc operation attach --form-id <fdId> --operation-id <opId> 挂到列表视图(--target view 挂查看视图)
119
+ llc flow list --app-code <appCode> 应用下的动作流(只读)
120
+ llc flow get --id <flowId> --with-design 查看动作流并解析 fdDesign
121
+ llc flow spec --out flow.json 生成 Flow Spec 模板
122
+ llc flow validate --spec @flow.json 离线校验(图结构 / 必填 / 布局)
123
+ llc flow compile --spec @flow.json --json 编译出含编排布局的 fdDesign
124
+ llc flow apply --spec @flow.json --app-code <code> 预览提交体(追加 --yes 才写入)
125
+ llc flow diff --id <flowId> --spec @flow.json 对比本地与远端设计(忽略坐标)
126
+ llc flow script --id <flowId> 生成 flow_{fdCode} 按钮脚本
127
+ llc flow enable/disable/delete --id <flowId> 生命周期管理
117
128
 
118
129
  ${logger_1.color.gray('高级控件(desc / tags / mdm / association / mechnumber …)与普通控件同样写法:')}
119
130
  llc form autofill --id <fdId> --apply --add-field "字段名:标题:控件类型"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lan-low-code-cli",
3
- "version": "2.6.0",
3
+ "version": "2.7.0",
4
4
  "description": "lan-low-code cli:低代码表单引擎命令行工具(创建表单、获取可用字段、自动完善表单字段)",
5
5
  "keywords": [
6
6
  "low-code",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: lan-low-code-cli
3
- version: 2.6.0
3
+ version: 2.7.0
4
4
  author: Pig George
5
5
  description: Operate the Landray low-code form engine through the `llc` CLI. Use when the user asks to create, inspect, modify, publish, or delete low-code apps and forms; generate field configs; configure layouts (grid/tabs), field permissions, options, i18n, or control actions (onChange/onLoad); or run pre-publish validation. Also use for troubleshooting form loading/rendering errors.
6
6
  tags:
@@ -41,11 +41,11 @@ Verify: `llc -v` should print the version.
41
41
  ### Version check before upgrading
42
42
 
43
43
  Before upgrading (and whenever `llc -v` does not match this skill), compare the **major** version of the
44
- installed CLI with this skill's frontmatter `version:` (currently `2.4.1`):
44
+ installed CLI with the `version:` in this skill's frontmatter:
45
45
 
46
46
  | CLI version vs skill | Action |
47
47
  |---|---|
48
- | Same major (`2.4.1` → e.g. `2.6.0`) | **Upgrade directly**: `npm i -g lan-low-code-cli@latest`. Non-major releases keep the flags in this skill valid. |
48
+ | Same major (e.g. skill `2.x` → CLI `2.y`) | **Upgrade directly**: `npm i -g lan-low-code-cli@latest`. Non-major releases keep the flags in this skill valid. |
49
49
  | CLI major is higher (`2.x` skill vs `3.x` CLI) | A **major** version has been crossed: **this skill may be out of date**. Do not assume its flags/fields still exist — tell the user the skill may be stale and update it (see **Self-update** below) before running write operations. |
50
50
  | CLI major is lower | The CLI is older than the skill: upgrade the CLI first (`npm i -g lan-low-code-cli@latest`); never downgrade the skill. |
51
51
 
@@ -95,6 +95,9 @@ Always run `llc <command> --help` before inventing flags — the CLI is the sour
95
95
  | `llc field list / gen-key / library` | available fields |
96
96
  | `llc operation list / get / create / update / delete / attach / detach` | form business operations (link, script, dialog), including attaching to list/view pages |
97
97
  | `llc template list / create / add-fields / delete` | category-form templates: create/append/delete templates, porting fields/views/permissions from an existing form (auto category node, bypasses official init) |
98
+ | `llc flow spec / compile / validate` | **offline**: generate a Flow Spec, compile it into `fdDesign` with IDE-style layout, run offline validation |
99
+ | `llc flow list / get / category / logs / schema / templates` | **read-only** action-flow queries; `templates` extracts golden node skeletons (coordinates stripped) |
100
+ | `llc flow apply / diff / script / enable / disable / delete` | **write** action-flow lifecycle; `apply` previews by default and needs `--yes` + `--app-code` |
98
101
  | `llc control list / show` | controls installed in the environment (basis for `--add-field` third segment) |
99
102
  | `llc locale list` | languages with translations available (from control i18n resources) |
100
103
  | `llc config set / show / clear` | global credentials and defaults |
@@ -103,6 +106,13 @@ Always run `llc <command> --help` before inventing flags — the CLI is the sour
103
106
  >
104
107
  > Runtime `context` of operation scripts (selected rows, list refresh, query, navigation, event bus,
105
108
  > `window.MKXFORM`) is documented in [references/operation-script-context.md](references/operation-script-context.md) (Chinese).
109
+ >
110
+ > Action flows are **application-scoped** (`--app-code`, `fdAppCode`), not form-scoped. Always start with
111
+ > `llc flow spec --out flow.json` → `llc flow validate --spec @flow.json` → `llc flow compile --spec @flow.json --json`.
112
+ > `llc flow apply` **only previews** until `--yes` is added, and requires an explicit `--app-code`.
113
+ > `--layout ide` (default) reproduces the platform's branch arrangement; never hand-write `drawAttr.bounds`.
114
+ > Full Flow Spec syntax: **§10 of [references/cli-contract.md](references/cli-contract.md)**.
115
+ > Add `--resolve-schema` when publishing nodes that reference a data model (`sys-modeling#<fdId>`).
106
116
 
107
117
  ## 2. Core workflow
108
118
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: lan-low-code-cli
3
- version: 2.6.0
3
+ version: 2.7.0
4
4
  author: Pig George
5
5
  description: 通过 `llc` CLI 操作蓝凌低代码表单引擎。适用于:创建、查看、修改、发布或删除低代码应用与表单;生成字段配置;配置布局(栅格/页签)、字段权限、选项、多语言、控件动作(onChange/onLoad);或执行发布前校验、排查表单加载/渲染报错。
6
6
  tags:
@@ -45,7 +45,7 @@ npm i -g lan-low-code-cli # 全局安装,获得 `llc` 命令
45
45
 
46
46
  | CLI 版本 vs skill | 处理 |
47
47
  |---|---|
48
- | 同一大版本(`2.4.1` → 如 `2.6.0`) | **直接升级**:`npm i -g lan-low-code-cli@latest`。非大版本升级不影响本 skill 的参数与写法。 |
48
+ | 同一大版本(如 skill `2.x` → CLI `2.y`) | **直接升级**:`npm i -g lan-low-code-cli@latest`。非大版本升级不影响本 skill 的参数与写法。 |
49
49
  | CLI 大版本更高(skill `2.x` vs CLI `3.x`) | 已**跨大版本**:**本 skill 可能已过期**。不要假设其中的参数/字段仍然有效——告知用户 skill 可能过期,按下方**自更新**更新后再执行写操作。 |
50
50
  | CLI 大版本更低 | CLI 比 skill 旧:先升级 CLI(`npm i -g lan-low-code-cli@latest`),不要把 skill 降级。 |
51
51
 
@@ -92,6 +92,9 @@ llc config set --base-url http://<host>/data --token <X-AUTH-TOKEN> # 一次
92
92
  | `llc field list / gen-key / library` | 可用字段 |
93
93
  | `llc operation list / get / create / update / delete / attach / detach` | 表单业务操作(链接、脚本、对话框),含挂载到列表/查看视图 |
94
94
  | `llc template list / create / add-fields / delete` | 分类表单模板:创建模板并把既有表单的字段/视图/权限移植进来,追加字段,或清理模板(自动建分类节点、跳过官方 init) |
95
+ | `llc flow spec / compile / validate` | **纯离线**:生成 Flow Spec、编译出符合 IDE 编排布局的 `fdDesign`、离线校验 |
96
+ | `llc flow list / get / category / logs / schema / templates` | **只读**查询动作流;`templates` 抽取 golden 节点骨架(已剥离坐标) |
97
+ | `llc flow apply / diff / script / enable / disable / delete` | **写入**动作流全生命周期;`apply` 默认预览,需 `--yes` + `--app-code` 才提交 |
95
98
  | `llc control list / show` | 环境已安装控件(`--add-field` 第三段的取值依据) |
96
99
  | `llc locale list` | 环境中有现成译文的语言(来自控件多语言资源) |
97
100
  | `llc config set / show / clear` | 全局凭据与默认值 |
@@ -100,6 +103,13 @@ llc config set --base-url http://<host>/data --token <X-AUTH-TOKEN> # 一次
100
103
  >
101
104
  > 业务操作「脚本模式」的运行时 `context`(选中数据、刷新列表、查询、跳转、事件总线、`window.MKXFORM`)
102
105
  > 见 [references/operation-script-context.md](references/operation-script-context.md)。
106
+ >
107
+ > 动作流是**应用级**对象(`--app-code` 即 `fdAppCode`),不是表单级。固定起手式:
108
+ > `llc flow spec --out flow.json` → `llc flow validate --spec @flow.json` → `llc flow compile --spec @flow.json --json`。
109
+ > `llc flow apply` **默认只预览**,必须追加 `--yes`,且必须显式给 `--app-code`。
110
+ > `--layout ide`(默认)复刻平台分支编排,不要手写 `drawAttr.bounds`。
111
+ > Flow Spec 完整语法见 [references/cli-contract.md](references/cli-contract.md) **第 10 节**。
112
+ > 节点引用数据模型(`sys-modeling#<fdId>`)时,发布请加 `--resolve-schema`。
103
113
 
104
114
  ## 2. 核心流程
105
115
 
@@ -18,8 +18,9 @@ llc -v | --version
18
18
  - Most commands accept `--json` for script-friendly output.
19
19
  - Exit codes: `0` success (including `--help`/`--version`); `1` failure (arguments, validation, API).
20
20
  `LLC_DEBUG=1` also prints the stack.
21
- - Destructive commands (`app|form delete`, `app|form delete-data`) require `--yes`; without it the CLI
22
- only warns and does **not** execute.
21
+ - Destructive commands (`app|form delete`, `app|form delete-data`, `flow delete`, `flow disable`) require
22
+ `--yes`; without it the CLI only warns and does **not** execute. `flow apply` also needs `--yes` —
23
+ by default it only prints the payload.
23
24
  - An unknown `llc help` path prints `未知命令:llc ...` and exits `1`.
24
25
 
25
26
  ## 2. config
@@ -221,7 +222,76 @@ without error but never fires, so confirm it first:
221
222
  | `--locale` | `"Cn,Us,Tw"` or `all` | comma-separated, **first entry is primary**; `all` = `Cn,Us,Tw` |
222
223
  | `--lang-file` | `{"Us":{"姓名":"Name"}}` | inner keys may be field labels or field names; options support `field#value` |
223
224
 
224
- ## 10. Common failures
225
+ ## 10. flow (action flow / process define)
226
+
227
+ Application-scoped action flows. Every write command needs `--yes` and an explicit `--app-code`
228
+ (or `appCode` in the spec). `--json` goes to stdout; all other output goes to stderr.
229
+
230
+ | Command | Required | Key options | Notes |
231
+ |---|---|---|---|
232
+ | `llc flow spec` | — | `--app-code` `--name` `--code` `--start-way` `--out` `--json` | prints a Flow Spec template; Start/End nodes are added by the compiler |
233
+ | `llc flow compile` | `--spec <@file\|json>` | `--layout ide\|layered\|passthrough` `--center-x` `--rank-padding` `--lane-gap` `--min-gap` `--sub-center-x` `--emit-waypoints` `--out` `--force` `--json` | compiles to `fdDesign` (JSON string) + `basicInfo`; **offline** |
234
+ | `llc flow validate` | `--spec` | layout options, `--json` | offline checks only; exits `1` on errors |
235
+ | `llc flow list` | `--app-code` | `--keyword` `--status draft\|stopped\|enabled` `--page-no` `--page-size` `--json` | read-only |
236
+ | `llc flow get` | `--id` | `--with-design` `--raw` `--json` | `--with-design` parses `fdDesign` |
237
+ | `llc flow category list` | `--app-code` | `--keyword` `--json` | read-only |
238
+ | `llc flow logs` | `--id` | `--limit` `--endpoint` `--json` | log endpoints differ per deployment; `--endpoint` overrides |
239
+ | `llc flow schema` | `--kind` | `--entity-id` `--code` `--app-code` `--api-type` `--api-method-path` | read-only, always JSON. Kinds: `businessNodes` `events` `source` `jsonSchema` `condition` `out` `active` `event` `interface` `apiMethod` `flowBots` `meta` `dataModel` `recycle` |
240
+ | `llc flow templates` | `--from <@file\|json>` | `--out` `--json` | extracts golden node skeletons from a `get` response / compile result / design; strips `drawAttr.bounds` |
241
+ | `llc flow apply` | `--spec` `--app-code` | `--draft` `--enable` `--resolve-schema` `--strict-schema` layout options `--force` `--yes` `--json` | **preview by default**; `--yes` calls `addPublish`/`addDraft` (or `update*` when the spec has `fdId`), then reads back |
242
+ | `llc flow diff` | `--id` `--spec` | `--no-ignore-bounds` `--resolve-schema` `--strict-schema` layout options `--json` | compares local design with remote; ignores coordinates by default |
243
+ | `llc flow script` | `--id` | `--out` `--json` | generates a `MKXFORM.flow_<fdCode>` button script |
244
+ | `llc flow enable` | `--id` | `--json` | `startup` |
245
+ | `llc flow disable` | `--id` `--yes` | `--json` | `stop` |
246
+ | `llc flow delete` | `--id` `--yes` | `--json` | checks `listByRefer` before deleting |
247
+
248
+ **Flow Spec** (JSON — no new runtime dependency):
249
+
250
+ ```json
251
+ {
252
+ "appCode": "cli-test", "name": "My flow", "code": "flow_a",
253
+ "startWay": "StartActiveNode", "centerX": 679,
254
+ "trigger": { "fdRunType": ["operate"], "fdFormRelation": "0", "dataType": 1,
255
+ "inParamType": 2, "inParam": { "id": "requestBody", "type": "object", "properties": {} } },
256
+ "nodes": [
257
+ { "id": "N2", "type": "QueryDataNode", "name": "query",
258
+ "config": { "target": { "entityId": "sys-modeling#<fdId>" }, "resultType": "single" } },
259
+ { "id": "N3", "type": "BranchNode", "name": "branch" },
260
+ { "id": "N4", "type": "TransformNode", "name": "A", "config": { "outParamType": 0 } },
261
+ { "id": "N5", "type": "TransformNode", "name": "B", "config": { "outParamType": 0 } }
262
+ ],
263
+ "edges": [
264
+ { "from": "N2", "to": "N3" },
265
+ { "from": "N3", "to": "N4", "name": "yes", "formula": { "type": "Script", "script": "return true" } },
266
+ { "from": "N3", "to": "N5", "name": "no", "default": true }
267
+ ]
268
+ }
269
+ ```
270
+
271
+ - `--code` must match `^[a-zA-Z0-9_]{1,30}$`; node `id` `^[0-9a-zA-Z_]{2,32}$`. Start/End nodes
272
+ and their edges are auto-added; disconnected roots/leaves are wired automatically.
273
+ - **Layout**: `ide` (default) reproduces the platform's arrangement — main chain centred on
274
+ `--center-x` (default `679`), `BranchNode` 56×56, branch successors on the same rank at lanes ±1,
275
+ join one rank lower, rank spacing `maxHeight + 52`. `layered` pushes branch successors to the
276
+ next rank; `passthrough` keeps `bounds` from the spec. `drawAttr.bounds` uses top-left `x/y` with
277
+ `relative:false` and is persisted verbatim by the IDE (it never recomputes coordinates).
278
+ - Branch handling: `defaultTargetRef` comes from the edge with `"default": true`; a
279
+ `formulaConfig.branchFormulas` referencing the real line IDs is generated automatically, and branch
280
+ edges get `exitX=0/1` anchor styles so they do not overlap in the designer.
281
+ - `--resolve-schema` fills `extraAttr.context_*` via `getSourceJsonSchema` /
282
+ `getProcessDataOutJsonSchema`. Those endpoints require a `sys-modeling#` prefixed `entityId`
283
+ (the CLI adds it). Failures are warnings unless `--strict-schema`.
284
+ - `fields` of `AddDataNode` / `AddOrUpdateNode` uses the platform's real shape
285
+ (`{field:{...},value:{format,valueJson}}`); the CLI passes it through and only validates field
286
+ names — it does **not** synthesise formulas. Use `llc flow templates` / `flow get` for golden shapes.
287
+ - Publishing derives `fdProcessActive` on the backend from `startNodeDef`; `fdStatus=1` means enabled.
288
+ `flow logs` endpoints vary by deployment (`ModelingProcessLog/findAll` vs
289
+ `sysModelingLog/findAllWithAppId`); use `--endpoint` if the defaults fail.
290
+ - Runtime trigger: `flow script` emits a `MKXFORM.flow_<fdCode>` call; attach it with
291
+ `llc operation create --type script --script @file` + `llc operation attach`.
292
+ `executeFlowForAjax` lives in the `sys-xform` runtime package, not in a documented REST endpoint.
293
+
294
+ ## 11. Common failures
225
295
 
226
296
  | Message | Cause / fix |
227
297
  |---|---|
@@ -15,7 +15,7 @@ llc -v | --version # 版本号
15
15
  - 配置优先级:`--base-url`/`--token` > `LLC_BASE_URL`/`LLC_TOKEN` > `./lcode.config.json`(当前目录)> `~/.lcode/config.json`(`llc config set` 写入);`timeout` 默认 `30000` ms。
16
16
  - 多数查询/变更命令支持 `--json`(结构化输出,便于脚本处理)。
17
17
  - 退出码:`0` 成功(含 `--help`/`--version`);`1` 失败(参数、校验、接口错误)。`LLC_DEBUG=1` 额外打印堆栈。
18
- - 破坏性命令(`app|form delete`、`app|form delete-data`)必须带 `--yes`,否则仅告警且**不执行**。
18
+ - 破坏性命令(`app|form delete`、`app|form delete-data`、`flow delete`、`flow disable`)必须带 `--yes`,否则仅告警且**不执行**;`flow apply` 同样需要 `--yes`,默认只打印提交体。
19
19
  - `llc help xxx` 路径不存在时报 `未知命令:llc xxx` 并退出 `1`;未知子命令由 commander 报错(同为退出码 1)。
20
20
 
21
21
  ## 2. config
@@ -211,7 +211,74 @@ llc operation detach --form-id <fdId> --operation-id <operationId> #
211
211
  | `--locale` | `"Cn,Us,Tw"` 或 `all` | 逗号分隔,**第一个为主语言**;`all` = `Cn,Us,Tw` |
212
212
  | `--lang-file` | `{"Us":{"姓名":"Name"}}` | 内层 key 可为字段标题或字段名;选项支持 `字段名#值` |
213
213
 
214
- ## 10. 常见失败提示
214
+ ## 10. flow(动作流 / process define)
215
+
216
+ 应用级动作流(`fdAppCode`)。所有写命令都需要 `--yes` 与显式 `--app-code`(或 spec 中的 `appCode`)。
217
+ `--json` 输出到 stdout,其余输出走 stderr。
218
+
219
+ | 命令 | 必填 | 主要选项 | 说明 |
220
+ |---|---|---|---|
221
+ | `llc flow spec` | — | `--app-code` `--name` `--code` `--start-way` `--out` `--json` | 生成 Flow Spec 模板;开始/结束节点由编译器自动补齐 |
222
+ | `llc flow compile` | `--spec <@文件\|json>` | `--layout ide\|layered\|passthrough` `--center-x` `--rank-padding` `--lane-gap` `--min-gap` `--sub-center-x` `--emit-waypoints` `--out` `--force` `--json` | 编译出 `fdDesign`(JSON 字符串)与 `basicInfo`;**纯离线** |
223
+ | `llc flow validate` | `--spec` | 布局选项、`--json` | 仅离线校验;有错误退出 `1` |
224
+ | `llc flow list` | `--app-code` | `--keyword` `--status draft\|stopped\|enabled` `--page-no` `--page-size` `--json` | 只读 |
225
+ | `llc flow get` | `--id` | `--with-design` `--raw` `--json` | `--with-design` 解析 `fdDesign` |
226
+ | `llc flow category list` | `--app-code` | `--keyword` `--json` | 只读 |
227
+ | `llc flow logs` | `--id` | `--limit` `--endpoint` `--json` | 日志端点因部署而异;`--endpoint` 可覆盖 |
228
+ | `llc flow schema` | `--kind` | `--entity-id` `--code` `--app-code` `--api-type` `--api-method-path` | 只读、恒为 JSON。kind:`businessNodes` `events` `source` `jsonSchema` `condition` `out` `active` `event` `interface` `apiMethod` `flowBots` `meta` `dataModel` `recycle` |
229
+ | `llc flow templates` | `--from <@文件\|json>` | `--out` `--json` | 从 `get` 响应 / 编译结果 / 设计对象抽取 golden 节点骨架;剥离 `drawAttr.bounds` |
230
+ | `llc flow apply` | `--spec` `--app-code` | `--draft` `--enable` `--resolve-schema` `--strict-schema` 布局选项 `--force` `--yes` `--json` | **默认预览**;`--yes` 才调用 `addPublish`/`addDraft`(spec 带 `fdId` 时走 `update*`),随后回读 |
231
+ | `llc flow diff` | `--id` `--spec` | `--no-ignore-bounds` `--resolve-schema` `--strict-schema` 布局选项 `--json` | 对比本地与远端设计;默认忽略坐标 |
232
+ | `llc flow script` | `--id` | `--out` `--json` | 生成 `MKXFORM.flow_<fdCode>` 按钮脚本 |
233
+ | `llc flow enable` | `--id` | `--json` | `startup` |
234
+ | `llc flow disable` | `--id` `--yes` | `--json` | `stop` |
235
+ | `llc flow delete` | `--id` `--yes` | `--json` | 删除前先查 `listByRefer` |
236
+
237
+ **Flow Spec**(JSON,不引入新的运行时依赖):
238
+
239
+ ```json
240
+ {
241
+ "appCode": "cli-test", "name": "示例流程", "code": "flow_a",
242
+ "startWay": "StartActiveNode", "centerX": 679,
243
+ "trigger": { "fdRunType": ["operate"], "fdFormRelation": "0", "dataType": 1,
244
+ "inParamType": 2, "inParam": { "id": "requestBody", "type": "object", "properties": {} } },
245
+ "nodes": [
246
+ { "id": "N2", "type": "QueryDataNode", "name": "查询",
247
+ "config": { "target": { "entityId": "sys-modeling#<fdId>" }, "resultType": "single" } },
248
+ { "id": "N3", "type": "BranchNode", "name": "条件分支" },
249
+ { "id": "N4", "type": "TransformNode", "name": "分支A", "config": { "outParamType": 0 } },
250
+ { "id": "N5", "type": "TransformNode", "name": "分支B", "config": { "outParamType": 0 } }
251
+ ],
252
+ "edges": [
253
+ { "from": "N2", "to": "N3" },
254
+ { "from": "N3", "to": "N4", "name": "是", "formula": { "type": "Script", "script": "return true" } },
255
+ { "from": "N3", "to": "N5", "name": "否", "default": true }
256
+ ]
257
+ }
258
+ ```
259
+
260
+ - `--code` 需匹配 `^[a-zA-Z0-9_]{1,30}$`;节点 `id` 需匹配 `^[0-9a-zA-Z_]{2,32}$`。
261
+ 开始/结束节点及其连线自动补齐,无入边/无出边的业务节点自动接线。
262
+ - **布局**:`ide`(默认)复刻平台编排——主链以 `--center-x`(默认 `679`)居中,`BranchNode`
263
+ 为 56×56,分支后继与分支节点同层、泳道 ±1,汇聚下移一层,层距 = 该层最高节点 + 52。
264
+ `layered` 把分支后继放到下一层;`passthrough` 沿用 spec 中的 `bounds`。
265
+ `drawAttr.bounds` 是左上角 `x/y` 且带 `relative:false`,IDE **原样持久化、不会重算坐标**。
266
+ - 分支处理:`defaultTargetRef` 取自 `"default": true` 的出边;自动生成引用真实连线 ID 的
267
+ `formulaConfig.branchFormulas`;分支连线写入 `exitX=0/1` 锚点,避免 IDE 内两条线重叠。
268
+ - `--resolve-schema` 通过 `getSourceJsonSchema` / `getProcessDataOutJsonSchema` 补齐
269
+ `extraAttr.context_*`。这两个端点要求 `entityId` 带 `sys-modeling#` 前缀(CLI 自动补)。
270
+ 失败默认只告警,`--strict-schema` 时升级为错误。
271
+ - `AddDataNode` / `AddOrUpdateNode` 的 `fields` 使用平台真实结构
272
+ (`{field:{...},value:{format,valueJson}}`);CLI 原样透传并只校验字段名,**不臆造公式**。
273
+ 需要真实结构请用 `llc flow templates` / `flow get` 抽取 golden 骨架。
274
+ - 发布时后端从 `startNodeDef` 派生 `fdProcessActive`;`fdStatus=1` 表示已启用。
275
+ `flow logs` 端点因部署而异(`ModelingProcessLog/findAll` 与
276
+ `sysModelingLog/findAllWithAppId`);默认都失败时用 `--endpoint` 指定。
277
+ - 运行时触发:`flow script` 生成 `MKXFORM.flow_<fdCode>` 调用,可用
278
+ `llc operation create --type script --script @文件` + `llc operation attach` 挂载。
279
+ `executeFlowForAjax` 位于 `sys-xform` 运行时包,没有文档化的 REST 端点。
280
+
281
+ ## 11. 常见失败提示
215
282
 
216
283
  | 提示 | 原因与处理 |
217
284
  |---|---|