@qingflow-tech/qingflow-cli 1.1.50-beta.1 → 1.1.50-beta.3
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 +8 -2
- package/skills/qingflow-cli/SKILL.md +74 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_VIEWS_WORKFLOW.md +3 -9
- package/skills/qingflow-cli/reference/builder/20-build-complete-system.md +3 -2
- package/skills/qingflow-cli/reference/builder/30-schema-fields.md +9 -5
- package/skills/qingflow-cli/reference/builder/50-views.md +176 -190
- package/skills/qingflow-cli/reference/builder/README.md +1 -1
- package/skills/qingflow-cli/reference/builder/reference/app-delivery-sop.md +6 -6
- package/skills/qingflow-cli/reference/examples/README.md +1 -1
- package/skills/qingflow-cli/reference/examples/views/views_apply_v1alpha1.example.json +14 -0
- package/skills/qingflow-cli/reference/examples/views/views_upsert_table_minimal.example.json +30 -36
- 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/models.py +208 -17
- 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/src/qingflow_mcp/builder_facade/schemas/views/manifest.json +43 -0
- package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/complete.json +1193 -0
- package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/details/button-config.json +53 -0
- package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/details/data-permission.json +60 -0
- package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/details/display.json +71 -0
- package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/details/operation-permission.json +48 -0
- package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/details/query-condition.json +47 -0
- package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/summary.json +274 -0
- package/src/qingflow_mcp/builder_facade/service.py +1427 -410
- package/src/qingflow_mcp/builder_facade/views_extract.py +572 -0
- package/src/qingflow_mcp/builder_facade/views_schema.py +400 -0
- package/src/qingflow_mcp/cli/commands/builder.py +72 -53
- package/src/qingflow_mcp/public_surface.py +3 -0
- package/src/qingflow_mcp/response_trim.py +11 -2
- package/src/qingflow_mcp/server_app_builder.py +32 -8
- package/src/qingflow_mcp/solution/executor.py +93 -7
- package/src/qingflow_mcp/tools/ai_builder_tools.py +211 -393
- package/src/qingflow_mcp/tools/solution_tools.py +2 -0
- package/src/qingflow_mcp/tools/view_tools.py +0 -6
- package/src/qingflow_mcp/tools/view_writer.py +44 -0
- package/skills/qingflow-cli/reference/examples/views/views_batch_full.example.json +0 -159
- package/skills/qingflow-cli/reference/views_upsert_table_minimal.example.json +0 -42
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
这些不是独立流程,而是读、写、改三个动作的多资源形态:
|
|
42
42
|
|
|
43
43
|
- **批量读**:读多个应用时优先用 `--app-keys`,例如 `builder app get --app-keys a,b fields`、`builder app get --app-keys a,b views`、`builder button get --app-keys a,b`、`builder associated-resource get --app-keys a,b`、`builder publish verify --app-keys a,b`。读结果同时看成功项和 `errors[]`,不要因单个应用失败否定全批。
|
|
44
|
-
-
|
|
44
|
+
- **批量写**:仅在资源契约明确支持时使用批量文件;视图例外,`builder views apply` 每次只提交一个视图的一种操作,不支持多视图批量创建或把 upsert/patch/remove 混在一次调用中。应用表单不走 `apps-file`,而是每个应用独立提交完整 AppForm 声明。
|
|
45
45
|
- **局部改**:只改已存在资源的少量参数时用对应 patch 文件;`patch_*` 内只写目标定位字段和 `set/unset`,不要用不完整 `upsert_*` 伪装局部更新。
|
|
46
46
|
|
|
47
47
|
---
|
|
@@ -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 配置。 |
|
|
@@ -374,14 +374,14 @@ qingflow builder flow apply \
|
|
|
374
374
|
|
|
375
375
|
后端很多保存接口仍是整份配置保存,但新版 CLI/MCP 对外提供 **public partial patch**:
|
|
376
376
|
|
|
377
|
-
- 已有视图:`builder views apply --views-file
|
|
377
|
+
- 已有视图:`builder views apply --app-key APP_KEY --patch-views-file PATH`,文件只放一项,写 `view_key`/`name` 与 `set`/`unset`
|
|
378
378
|
- 已有按钮:`builder button apply --patch-buttons-file`
|
|
379
379
|
- 已有关联资源:`builder associated-resource apply --patch-resources-file`
|
|
380
380
|
- 已有报表:`builder charts apply --patch-file`
|
|
381
381
|
- 已有流程节点:`builder flow apply --patch-nodes-file`
|
|
382
382
|
- 已有门户组件:`builder portal apply --patch-sections-file`
|
|
383
383
|
|
|
384
|
-
局部更新的语义是:你只写要替换的参数,工具内部读取当前配置、补齐后端必填字段,再整份保存。每个 patch 项直接使用对象真实定位字段 + `set` / 可选 `unset`,不要写字面量 `selector` key。示例:`{"
|
|
384
|
+
局部更新的语义是:你只写要替换的参数,工具内部读取当前配置、补齐后端必填字段,再整份保存。每个 patch 项直接使用对象真实定位字段 + `set` / 可选 `unset`,不要写字面量 `selector` key。示例:`{"view_key":"VIEW_KEY","set":{"query_conditions":{...}}}`、`{"button_id": 1001, "set": {"button_text": "新名称"}}`、`{"associated_item_id": 123, "set": {"match_mappings": [...]}}`、`{"chart_id": 456, "set": {"name": "新报表名"}}`、`{"id": "node_1", "set": {"name": "主管审批"}}`、`{"chart": {"chart_key": "xxx"}, "set": {"rows": 7}}`。
|
|
385
385
|
|
|
386
386
|
`upsert_*` 用于创建或提供完整目标配置;不要只给 `name/type/query_conditions` 这类局部片段然后期待后端自动合并。
|
|
387
387
|
|
|
@@ -7,7 +7,7 @@ Use these files as copyable bases after reading the matching resource document.
|
|
|
7
7
|
| Historical single-app schema probe | [schema/schema_single_app_form_standard.example.json](./schema/schema_single_app_form_standard.example.json) | Retired `--form-file` shape; use `builder app-form schema/get/validate/apply` for current declarations. |
|
|
8
8
|
| Historical multi-app schema probe | [schema/schema_multi_app_form_relation_layout.example.json](./schema/schema_multi_app_form_relation_layout.example.json) | Retired `--apps-file` shape; current systems create independent AppForms and then bind confirmed app keys. |
|
|
9
9
|
| Existing-app layout probe | [layout/layout_sections_full.example.json](./layout/layout_sections_full.example.json) | Historical layout-patch shape; current new and existing forms use the complete AppForm `spec.body`. |
|
|
10
|
-
| Views | [views/views_upsert_table_minimal.example.json](./views/views_upsert_table_minimal.example.json), [views/
|
|
10
|
+
| Views | [views/views_upsert_table_minimal.example.json](./views/views_upsert_table_minimal.example.json), [views/views_apply_v1alpha1.example.json](./views/views_apply_v1alpha1.example.json) | 单视图 JSON 对象,包含筛选、查询面板和视图绑定按钮;通过 `--app-key` 指定应用。 |
|
|
11
11
|
| Charts | [charts/charts_upsert_dashboard_starter.example.json](./charts/charts_upsert_dashboard_starter.example.json) | Standalone chart apply; portal usually creates missing charts inline. |
|
|
12
12
|
| Portal | [portal/portal_sections_standard_workbench.example.json](./portal/portal_sections_standard_workbench.example.json) | Standard workbench with inline QingBI charts and data views. |
|
|
13
13
|
| Associated resources | [associated/associated_resources_semantic_full.example.json](./associated/associated_resources_semantic_full.example.json) | Current-record matching through `match_mappings`. |
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "项目台账视图",
|
|
3
|
+
"type": "table",
|
|
4
|
+
"columns": [100, 101, 102, 103],
|
|
5
|
+
"data_permission": {"type": "custom", "scope": "dept"},
|
|
6
|
+
"sort": [{"que_id": 100, "ascending": false}],
|
|
7
|
+
"display": {"row_height": "compact", "pin_navigate": true},
|
|
8
|
+
"query_conditions": {
|
|
9
|
+
"enabled": true,
|
|
10
|
+
"exact": false,
|
|
11
|
+
"hide_before_query": false,
|
|
12
|
+
"rows": [["项目名称", "负责人"], ["创建时间"]]
|
|
13
|
+
}
|
|
14
|
+
}
|
package/skills/qingflow-cli/reference/examples/views/views_upsert_table_minimal.example.json
CHANGED
|
@@ -1,42 +1,36 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
2
|
+
"name": "示例业务操作视图_CLI模板",
|
|
3
|
+
"type": "table",
|
|
4
|
+
"columns": [100, 101, 102, 103],
|
|
5
|
+
"filters": [
|
|
6
|
+
{"que_id": 100, "operator": "neq", "value": "已关闭"}
|
|
7
|
+
],
|
|
8
|
+
"query_conditions": {
|
|
9
|
+
"enabled": true,
|
|
10
|
+
"exact": false,
|
|
11
|
+
"hide_before_query": false,
|
|
12
|
+
"rows": [["主记录编号", "主记录名称"], ["状态", "负责人"]]
|
|
13
|
+
},
|
|
14
|
+
"action_buttons": [
|
|
3
15
|
{
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
|
|
9
|
-
"filters": [
|
|
10
|
-
{"field_name": "状态", "operator": "neq", "value": "已关闭"}
|
|
16
|
+
"text": "行内创建子记录",
|
|
17
|
+
"action": "add_data",
|
|
18
|
+
"target_app_key": "TARGET_APP_KEY",
|
|
19
|
+
"field_mappings": [
|
|
20
|
+
{"source_field": "数据ID", "target_field": "关联主记录"}
|
|
11
21
|
],
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
{"source_field": "数据ID", "target_field": "关联主记录"}
|
|
25
|
-
],
|
|
26
|
-
"default_values": {"目标状态字段": "REPLACE_WITH_TARGET_OPTION_LABEL"},
|
|
27
|
-
"placement": "list"
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
"text": "详情创建子记录",
|
|
31
|
-
"action": "add_data",
|
|
32
|
-
"target_app_key": "TARGET_APP_KEY",
|
|
33
|
-
"field_mappings": [
|
|
34
|
-
{"source_field": "数据ID", "target_field": "关联主记录"}
|
|
35
|
-
],
|
|
36
|
-
"default_values": {"目标状态字段": "REPLACE_WITH_TARGET_OPTION_LABEL"},
|
|
37
|
-
"placement": "detail"
|
|
38
|
-
}
|
|
39
|
-
]
|
|
22
|
+
"default_values": {"目标状态字段": "REPLACE_WITH_TARGET_OPTION_LABEL"},
|
|
23
|
+
"placement": "list"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"text": "详情创建子记录",
|
|
27
|
+
"action": "add_data",
|
|
28
|
+
"target_app_key": "TARGET_APP_KEY",
|
|
29
|
+
"field_mappings": [
|
|
30
|
+
{"source_field": "数据ID", "target_field": "关联主记录"}
|
|
31
|
+
],
|
|
32
|
+
"default_values": {"目标状态字段": "REPLACE_WITH_TARGET_OPTION_LABEL"},
|
|
33
|
+
"placement": "detail"
|
|
40
34
|
}
|
|
41
35
|
]
|
|
42
36
|
}
|
|
@@ -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"))
|