@qingflow-tech/qingflow-cli 1.1.50-beta.1 → 1.1.50-beta.2
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 +3 -3
- package/package.json +1 -1
- package/pyproject.toml +2 -1
- package/skills/qingflow-cli/reference/builder/20-build-complete-system.md +2 -2
- package/skills/qingflow-cli/reference/builder/30-schema-fields.md +9 -5
- package/skills/qingflow-cli/reference/builder/reference/app-delivery-sop.md +3 -3
- package/src/qingflow_mcp/app_form_compat.py +3 -0
- package/src/qingflow_mcp/app_form_validator.py +16 -3
- package/src/qingflow_mcp/builder_facade/app_form.py +360 -68
- package/src/qingflow_mcp/builder_facade/schemas/app_form/manifest.json +1 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/complete.json +61 -27
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/address.json +3 -7
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/amount.json +3 -7
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/attachment.json +5 -9
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/code_block.json +3 -7
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/data_relation.json +8 -15
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/date.json +3 -7
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/datetime.json +3 -7
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/department.json +3 -7
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/email.json +3 -7
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/link.json +3 -7
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/long_text.json +3 -7
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/member.json +3 -7
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/multi_select.json +3 -7
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/number.json +3 -7
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/phone.json +3 -7
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/q_linker.json +3 -7
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/relation.json +43 -12
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/rich_text.json +146 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/single_choice.json +3 -7
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/single_select.json +3 -7
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/subtable.json +58 -24
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/text.json +3 -7
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/formula.json +8 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/summary.json +5 -10
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Qingflow CLI 是轻流面向开发者和 AI Agent 的命令行工具。它把轻
|
|
|
7
7
|
## 安装
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npm install @qingflow-tech/qingflow-cli@1.1.50-beta.
|
|
10
|
+
npm install @qingflow-tech/qingflow-cli@1.1.50-beta.2
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
推荐从官网复制这一条一键安装 Qingflow CLI:
|
|
@@ -21,7 +21,7 @@ npx qingflow-cli@latest install
|
|
|
21
21
|
## 运行
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
npx -y -p @qingflow-tech/qingflow-cli@1.1.50-beta.
|
|
24
|
+
npx -y -p @qingflow-tech/qingflow-cli@1.1.50-beta.2 qingflow
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
全局安装后可以直接运行:
|
|
@@ -62,7 +62,7 @@ qingflow-skills install --agent codex --scope user
|
|
|
62
62
|
如果你通过一次性 `npx -p` 执行 skills 安装,建议加 `--copy`,避免 symlink 指向 npm 临时执行缓存:
|
|
63
63
|
|
|
64
64
|
```bash
|
|
65
|
-
npx -y -p @qingflow-tech/qingflow-cli@1.1.50-beta.
|
|
65
|
+
npx -y -p @qingflow-tech/qingflow-cli@1.1.50-beta.2 qingflow-skills install --agent codex --scope user --copy
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
`qingflow-skills install` 默认使用 symlink,且遇到同名 skill 不会覆盖;只有显式传入 `--force` 才会替换。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qingflow-tech/qingflow-cli",
|
|
3
|
-
"version": "1.1.50-beta.
|
|
3
|
+
"version": "1.1.50-beta.2",
|
|
4
4
|
"description": "Human-friendly Qingflow command line interface for auth, record operations, import, tasks, and stable builder flows.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/pyproject.toml
CHANGED
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "qingflow-mcp"
|
|
7
|
-
version = "1.1.
|
|
7
|
+
version = "1.1.50b2"
|
|
8
8
|
description = "User-authenticated MCP server for Qingflow"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -40,6 +40,7 @@ dependencies = [
|
|
|
40
40
|
dev = [
|
|
41
41
|
"pytest>=8.3,<9.0",
|
|
42
42
|
"respx>=0.22,<1.0",
|
|
43
|
+
"pymysql>=1.2.2",
|
|
43
44
|
]
|
|
44
45
|
build = [
|
|
45
46
|
"pyinstaller>=6.0,<7.0",
|
|
@@ -22,7 +22,7 @@ package apply/get -> AppForm schema/get/apply per app -> app readback -> workflo
|
|
|
22
22
|
- Do not call the retired `builder schema apply` or `builder layout apply` routes, and do not use their `add_fields`, `apps-file`, or `form-file` payloads as the current path.
|
|
23
23
|
- For relations, create relation-independent apps first, read back their published `appKey`s, then submit complete declarations using the canonical relation shape and known target app keys.
|
|
24
24
|
- One app may contain multiple `relation` fields when the business model needs them.
|
|
25
|
-
- Relation selectors use the AppForm camelCase shape, not bare strings: write `config.
|
|
25
|
+
- Relation selectors use the AppForm camelCase shape, not bare strings: write ordered `config.displayFields: [{"field":{"name":"产品编码"},"auth":"visible"}]` (include hidden items) and `config.twoWayBoundField: {"queId":N}` when the binding is two-way (omit for one-way). `config.targetAppKey` must be a target app key confirmed by readback.
|
|
26
26
|
- If AppForm apply times out or returns uncertain write state, perform draft/published form readback before retrying. Do not recreate apps with `V2`, `测试`, or random suffixes.
|
|
27
27
|
- If a same/similar package or app set already exists and the user did not say whether to extend, repair, replace, or create new, stop and ask. Do not start probing with new names.
|
|
28
28
|
- If readback shows part of the requested system already exists, list existing apps/fields/views/charts/portal, identify gaps, and patch only the missing or incorrect slices.
|
|
@@ -72,7 +72,7 @@ A complete system is not just several created apps. Before reporting completion,
|
|
|
72
72
|
|
|
73
73
|
- Package exists, and its `package_id` is known.
|
|
74
74
|
- Every core business object is a separate app with `app_key`, `app_name`, `icon`, `color`, and exactly one readable data title field.
|
|
75
|
-
- Cross-app relation fields are present where the business model needs them; each relation uses a confirmed `targetAppKey` and object selectors in `config.
|
|
75
|
+
- Cross-app relation fields are present where the business model needs them; each relation uses a confirmed `targetAppKey` and object selectors in `config.displayFields[].field` (+ `twoWayBoundField` for two-way bindings).
|
|
76
76
|
- Core apps were created with complete AppForm declarations, so fields and readable form sections/rows were submitted together.
|
|
77
77
|
- Core operating views exist and their `query_conditions` and `action_buttons` are verified; list-row buttons and detail-page buttons are both configured through the view.
|
|
78
78
|
- Each new app has 5 realistic sample records unless the user explicitly skipped sample data.
|
|
@@ -185,7 +185,7 @@ current CLI payload.
|
|
|
185
185
|
| 负责人、审批人、参与人 | `member` | 不要创建“申请人/提交人”等平台系统字段 |
|
|
186
186
|
| 部门、组织 | `department` | 可按需要补 `config.departmentScope` |
|
|
187
187
|
| 图片、文件、附件 | `attachment` | 只有顶层 `attachment` 可被应用级 `dataCoverField` 选择 |
|
|
188
|
-
| 跨应用业务对象关系 | `relation` | 使用已确认的 `targetAppKey
|
|
188
|
+
| 跨应用业务对象关系 | `relation` | 使用已确认的 `targetAppKey`;展示字段写有序 `displayFields[]`(含 `auth`),双向绑定写 `twoWayBoundField`;旧 `displayField`/`visibleFields` 兼容窗口内仍可读 |
|
|
189
189
|
|
|
190
190
|
禁止创建平台系统字段:`数据ID`、`编号`、`申请人`、`申请时间`、`创建人`、`创建时间`、`提交人`、`提交时间`、`更新时间`、`更新人`、`当前流程状态`、`当前处理人`、`当前处理节点`、`流程标题`。需要引用系统字段时,只在工具明确支持的位置引用,例如按钮 `source_field: "数据ID"`。
|
|
191
191
|
|
|
@@ -243,7 +243,7 @@ current CLI payload.
|
|
|
243
243
|
|--------|----------|-----------------|
|
|
244
244
|
| 标量与选择器 | `name` + `questionType`(+ 业务 `beingRequired` 等) | `questionDescription`、`beingRequired`;数据标题和封面在应用级 `dataTitleField/dataCoverField` 中选择;**`single_select` / `multi_select` 的 `config.options` 是非空字符串数组**,如 `["A", "B"]` |
|
|
245
245
|
| `department` | 同左 | **`config.departmentScope`**:`mode: all \| custom`;`custom` 时 **`departments[]`**(含 `deptId` 等) |
|
|
246
|
-
| `relation` | **`targetAppKey`**;另需 **`
|
|
246
|
+
| `relation` | **`targetAppKey`**;另需 **`displayFields[]`**(含 `field`+`auth`;可为空列表——空 = 双向绑定未配展示字段的存量形态,round-trip 空↔空稳定;与旧形态不可混用、条目 queId 不可重复) | **`relationMode`**;双向绑定加 **`twoWayBoundField`**(省略 = 单向);目标应用元数据不可读时先读已发布表单,不要猜目标 key |
|
|
247
247
|
| `subtable` | **`subfields[]` 非空** | 子列递归同父级规则;使用 Schema 返回的 camelCase 子字段配置 |
|
|
248
248
|
| `q_linker` | **`config.qLinkerBinding`**:`inputs`、`request`、**`outputs[]` 每项须含 `targetField`**(能解析到本表单字段) | `request.url` 等须符合**租户/后端远程查询策略**;以字段类型 Schema 返回的配置为准 |
|
|
249
249
|
| `code_block` | **`config.codeBlockBinding`**:`inputs`、`code`、**`outputs[]` + `targetField`**;配置 outputs 时,代码必须显式写 **`qf_output = {...}`**,仅 `return {...}` 不会产生可回写结果,且每个 `outputs[].path` 必须从 `qf_output` 取值 | 以字段类型 Schema 返回的 camelCase 配置为准 |
|
|
@@ -265,13 +265,17 @@ current CLI payload.
|
|
|
265
265
|
"config": {
|
|
266
266
|
"targetAppKey": "PRODUCT_APP_KEY",
|
|
267
267
|
"relationMode": "single",
|
|
268
|
-
"
|
|
269
|
-
"
|
|
268
|
+
"twoWayBoundField": {"name": "产品编码"},
|
|
269
|
+
"displayFields": [
|
|
270
|
+
{"field": {"name": "产品编码"}, "auth": "visible"},
|
|
271
|
+
{"field": {"name": "产品名称"}, "auth": "hidden"},
|
|
272
|
+
{"field": {"name": "产品线"}, "auth": "visible"}
|
|
273
|
+
]
|
|
270
274
|
}
|
|
271
275
|
}
|
|
272
276
|
```
|
|
273
277
|
|
|
274
|
-
只在目标应用已经创建并通过 readback 确认后填写 `targetAppKey`。如果返回选择器校验错误,修复 `config.
|
|
278
|
+
只在目标应用已经创建并通过 readback 确认后填写 `targetAppKey`。如果返回选择器校验错误,修复 `config.displayFields[].field` / `config.twoWayBoundField`,不要重建应用或改 `appKey`。旧形态 `displayField`/`visibleFields` 在兼容窗口内仍可读(displayField 同时视为双向绑定目标),新声明一律用 `displayFields`。
|
|
275
279
|
|
|
276
280
|
引用字段的公式默认值写在 `config.defaultValueFormula`,表达式必须符合固定版本 `relation` 字段详情中的 `FormulaExpression`。不要把后端 `referenceConfig` 字段名或其他 Schema 版本的公式写法直接放进声明。
|
|
277
281
|
|
|
@@ -201,8 +201,8 @@ qingflow builder schema apply \
|
|
|
201
201
|
- 如果返回 `MULTI_APP_STATIC_VALIDATION_FAILED`,读取 `details.issues[]` 的 `path/error_code/fix_hint` 修 payload;这是写入前失败,通常可以修正后重试,不需要 readback。
|
|
202
202
|
- 多应用 schema apply 不做事务回滚;如果部分失败,只补失败的 `apps[].row_number`,不要整批重复提交。
|
|
203
203
|
- 这个模式只负责应用壳与字段;视图、按钮、关联资源、报表仍拿返回的 `app_key` 继续走各自 apply 工具。
|
|
204
|
-
- `target_app_ref`、`as_data_title`、`display_field` 等只属于旧载荷。迁移时将关系改写为 AppForm `config.targetAppKey`、`config.
|
|
205
|
-
- 历史 schema adapter 写入后可读 `details.relation_readback_matrix[]`:逐项核对旧 `target_app_key`、`relation_mode`、`display_field`、`visible_fields`。迁移到当前 AppForm 后,改为读取 draft/published form 的 `config.targetAppKey`、`config.relationMode`、`config.
|
|
204
|
+
- `target_app_ref`、`as_data_title`、`display_field` 等只属于旧载荷。迁移时将关系改写为 AppForm `config.targetAppKey`、`config.displayFields[]`(含 `auth`)、`config.twoWayBoundField`,并使用已确认的真实 app key。
|
|
205
|
+
- 历史 schema adapter 写入后可读 `details.relation_readback_matrix[]`:逐项核对旧 `target_app_key`、`relation_mode`、`display_field`、`visible_fields`。迁移到当前 AppForm 后,改为读取 draft/published form 的 `config.targetAppKey`、`config.relationMode`、`config.twoWayBoundField`、`config.displayFields[]`,不要提交旧 `update_fields` patch。
|
|
206
206
|
- 一个应用可以包含多个 `relation` 字段;不要因为 relation 数量超过 1 就降级成文本。若真实写入失败或 relation readback mismatch,只按实际错误和 `details.relation_repair_plan[]` 做最小修复。
|
|
207
207
|
- 完整系统遇到超时、`partial_success`、`write_executed=true`、`safe_to_retry=false` 或 readback 不完整时,先按 §3.4 读回,不要直接拆成单应用创建。
|
|
208
208
|
|
|
@@ -349,7 +349,7 @@ qingflow builder flow apply \
|
|
|
349
349
|
| **`address`** | 地址复合字段。 |
|
|
350
350
|
| **`attachment`** | 附件。 |
|
|
351
351
|
| **`boolean`** | 是否。 |
|
|
352
|
-
| **`relation`** | 旧载荷使用 `target_app_key` / `target_app_ref`;当前 AppForm 使用 `config.targetAppKey`、`config.relationMode`、`config.
|
|
352
|
+
| **`relation`** | 旧载荷使用 `target_app_key` / `target_app_ref`;当前 AppForm 使用 `config.targetAppKey`、`config.relationMode`、`config.twoWayBoundField`、`config.displayFields[]`。 |
|
|
353
353
|
| **`subtable`** | 当前 AppForm 使用 `subfields[]` 及 Schema 返回的 camelCase 配置;旧 `subfield_updates` 只作迁移线索。 |
|
|
354
354
|
| **`q_linker`** | 当前 AppForm 使用 `config.qLinkerBinding`,其 `outputs[].targetField` 必须按字段 Schema 配置。 |
|
|
355
355
|
| **`code_block`** | 当前 AppForm 使用 `config.codeBlockBinding`,其 `outputs[].targetField` 必须按字段 Schema 配置。 |
|
|
@@ -10,6 +10,9 @@ from .json_types import JSONObject
|
|
|
10
10
|
TYPE_ALIASES = {
|
|
11
11
|
"multiline": "long_text",
|
|
12
12
|
"textarea": "long_text",
|
|
13
|
+
"richtext": "rich_text",
|
|
14
|
+
"rich-text": "rich_text",
|
|
15
|
+
"rich_text": "rich_text",
|
|
13
16
|
"radio": "single_choice",
|
|
14
17
|
"boolean": "single_choice",
|
|
15
18
|
"select": "single_select",
|
|
@@ -218,8 +218,14 @@ def _static_errors(payload: dict[str, Any]) -> list[tuple[str, str]]:
|
|
|
218
218
|
widths = ["width" in field for field in fields if isinstance(field, dict)]
|
|
219
219
|
if widths and any(widths) and not all(widths):
|
|
220
220
|
errors.append((row_path, "width must be present on every field in the row or omitted from all"))
|
|
221
|
-
|
|
222
|
-
|
|
221
|
+
# 行宽契约(与后端语义对齐):任意 1-100 整数 + 全有或全无。
|
|
222
|
+
# 后端 html_width 无取值/总和校验,存量存在 33/34 三等分与字段删除后
|
|
223
|
+
# 留白(如 3×25=75)的行;get 读出的原布局必须能原样过校验
|
|
224
|
+
# (apply(get(S)) ≡ S 命题的必要条件)。
|
|
225
|
+
if widths and all(widths):
|
|
226
|
+
bad = [f.get("width") for f in fields if isinstance(f, dict) and not (isinstance(f.get("width"), int) and 1 <= f["width"] <= 100)]
|
|
227
|
+
if bad:
|
|
228
|
+
errors.append((row_path, "field widths must be an integer between 1 and 100"))
|
|
223
229
|
for field_index, field in enumerate(fields):
|
|
224
230
|
if isinstance(field, dict):
|
|
225
231
|
top_fields.append((f"{row_path}/{field_index}", field))
|
|
@@ -258,7 +264,14 @@ def _static_errors(payload: dict[str, Any]) -> list[tuple[str, str]]:
|
|
|
258
264
|
if top_fields:
|
|
259
265
|
if "dataTitleField" not in spec and "appKey" in payload:
|
|
260
266
|
pass
|
|
261
|
-
elif title_selector is None
|
|
267
|
+
elif title_selector is None:
|
|
268
|
+
# dataTitleField=null 仅在带 appKey 的更新/round-trip 场景放行:后端写链
|
|
269
|
+
# FormSvcImpl.normalizeDataTitleQueId 对 null 自动 fallback 到第一个可用
|
|
270
|
+
# 字段,get 读面在后端未设置时原样输出 null。创建场景(无 appKey)仍需
|
|
271
|
+
# 显式 selector。
|
|
272
|
+
if "appKey" not in payload:
|
|
273
|
+
errors.append(("/spec/dataTitleField", "dataTitleField must resolve to exactly one top-level field"))
|
|
274
|
+
elif resolve_local_selector(title_selector)[0] == "invalid":
|
|
262
275
|
errors.append(("/spec/dataTitleField", "dataTitleField must resolve to exactly one top-level field"))
|
|
263
276
|
elif title_selector is not None:
|
|
264
277
|
errors.append(("/spec/dataTitleField", "dataTitleField must be null when body is empty"))
|