@qingflow-tech/qingflow-cli 1.1.51 → 1.1.53
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 +4 -4
- package/npm/lib/runtime.mjs +29 -13
- package/package.json +1 -1
- package/pyproject.toml +8 -2
- package/skills/qingflow-cli/SKILL.md +93 -322
- package/skills/qingflow-cli/manifest.yaml +1 -1
- package/skills/qingflow-cli/reference/00-INDEX.md +4 -4
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_ADMIN_CHEATSHEET.md +1 -1
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_VIEWS_WORKFLOW.md +3 -9
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md +3 -137
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_EXPLORATION_REPORT.md +6 -4
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_FIELD_DATA_TYPES.md +14 -14
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_MEMBER_CHEATSHEET.md +20 -19
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_ONE_SHOT_CHEATSHEET.md +18 -14
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_RECORD_CREATE_WORKFLOW.md +7 -12
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_RECORD_UPDATE_WORKFLOW.md +2 -1
- 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 +192 -191
- package/skills/qingflow-cli/reference/builder/80-buttons-associated-resources.md +2 -0
- 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/core/QINGFLOW_CLI_ADMIN_CHEATSHEET.md +1 -1
- package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md +76 -95
- package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_EXPLORATION_REPORT.md +6 -4
- package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_FIELD_DATA_TYPES.md +13 -13
- package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_MEMBER_CHEATSHEET.md +20 -19
- package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_ONE_SHOT_CHEATSHEET.md +18 -14
- package/skills/qingflow-cli/reference/data/README.md +32 -0
- 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/skills/qingflow-cli/reference/record/QINGFLOW_CLI_RECORD_SPECIALIZED_WORKFLOW.md +50 -0
- package/skills/qingflow-cli/reference/record/QINGFLOW_CLI_RECORD_UPDATE_WORKFLOW.md +2 -1
- package/skills/qingflow-cli/reference/record/analysis/README.md +21 -113
- package/skills/qingflow-cli/reference/record/analysis/analysis-gotchas.md +10 -10
- package/skills/qingflow-cli/reference/record/analysis/analysis-patterns.md +9 -9
- package/skills/qingflow-cli/reference/record/analysis/business-context.md +1 -1
- package/skills/qingflow-cli/reference/record/analysis/confidence-reporting.md +10 -10
- package/skills/qingflow-cli/reference/record/analysis/data-access-playbook.md +52 -80
- package/skills/qingflow-cli/reference/record/analysis/pandas-recipes.md +2 -2
- package/skills/qingflow-common/SKILL.md +7 -4
- package/src/qingflow_mcp/app_form_compat.py +3 -0
- package/src/qingflow_mcp/app_form_validator.py +69 -17
- package/src/qingflow_mcp/builder_facade/app_form.py +497 -100
- package/src/qingflow_mcp/builder_facade/models.py +227 -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 +64 -30
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/address.json +4 -8
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/amount.json +4 -8
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/attachment.json +6 -10
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/code_block.json +6 -10
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/data_relation.json +11 -18
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/date.json +4 -8
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/datetime.json +4 -8
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/department.json +6 -10
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/email.json +4 -8
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/link.json +4 -8
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/long_text.json +4 -8
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/member.json +6 -10
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/multi_select.json +4 -8
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/number.json +4 -8
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/phone.json +4 -8
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/q_linker.json +6 -10
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/relation.json +46 -15
- 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 +4 -8
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/single_select.json +4 -8
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/subtable.json +61 -27
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/text.json +4 -8
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/formula.json +43 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/summary.json +7 -12
- package/src/qingflow_mcp/builder_facade/schemas/views/manifest.json +42 -0
- package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/complete.json +1245 -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 +73 -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 +319 -0
- package/src/qingflow_mcp/builder_facade/service.py +1666 -509
- package/src/qingflow_mcp/builder_facade/views_extract.py +592 -0
- package/src/qingflow_mcp/builder_facade/views_schema.py +400 -0
- package/src/qingflow_mcp/cli/commands/__init__.py +2 -1
- package/src/qingflow_mcp/cli/commands/builder.py +76 -55
- package/src/qingflow_mcp/cli/commands/data.py +134 -0
- package/src/qingflow_mcp/cli/commands/record.py +1 -1
- package/src/qingflow_mcp/cli/context.py +3 -0
- package/src/qingflow_mcp/cli/formatters.py +97 -1
- package/src/qingflow_mcp/public_surface.py +8 -0
- package/src/qingflow_mcp/response_trim.py +11 -2
- package/src/qingflow_mcp/server.py +2 -0
- package/src/qingflow_mcp/server_app_builder.py +34 -10
- package/src/qingflow_mcp/server_app_user.py +4 -0
- package/src/qingflow_mcp/solution/compiler/view_compiler.py +2 -1
- package/src/qingflow_mcp/solution/executor.py +96 -8
- package/src/qingflow_mcp/solution/spec_models.py +1 -0
- package/src/qingflow_mcp/tools/ai_builder_tools.py +218 -398
- package/src/qingflow_mcp/tools/csv_export.py +168 -0
- package/src/qingflow_mcp/tools/data_tools.py +284 -0
- package/src/qingflow_mcp/tools/record_tools.py +11 -46
- 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
|
@@ -36,6 +36,7 @@ class SolutionExecutor:
|
|
|
36
36
|
app_tools: AppTools,
|
|
37
37
|
record_tools: RecordTools,
|
|
38
38
|
view_tools: ViewTools,
|
|
39
|
+
view_writer: "ViewWriter | None" = None,
|
|
39
40
|
chart_tools: QingbiReportTools,
|
|
40
41
|
portal_tools: PortalTools,
|
|
41
42
|
navigation_tools: NavigationTools,
|
|
@@ -47,6 +48,7 @@ class SolutionExecutor:
|
|
|
47
48
|
self.app_tools = app_tools
|
|
48
49
|
self.record_tools = record_tools
|
|
49
50
|
self.view_tools = view_tools
|
|
51
|
+
self.view_writer = view_writer
|
|
50
52
|
self.chart_tools = chart_tools
|
|
51
53
|
self.portal_tools = portal_tools
|
|
52
54
|
self.navigation_tools = navigation_tools
|
|
@@ -460,6 +462,20 @@ class SolutionExecutor:
|
|
|
460
462
|
"Pass {app_key, spec} to solution_build_flow or use qingflow builder flow apply."
|
|
461
463
|
)
|
|
462
464
|
|
|
465
|
+
def _create_view_via_writer(self, *, profile: str, payload: dict[str, Any]) -> dict[str, Any]:
|
|
466
|
+
"""视图创建唯一落点:共享 ViewWriter(POST /view/viewConfig)。
|
|
467
|
+
|
|
468
|
+
solution DSL 内部创建路径不再直接调用 ViewTools.view_create(该方法已删除);
|
|
469
|
+
未注入 writer 的旧调用方退化为直接经 ViewTools 同一会话执行同一端点。
|
|
470
|
+
"""
|
|
471
|
+
if self.view_writer is not None:
|
|
472
|
+
return self.view_writer.create_view(profile=profile, payload=payload)
|
|
473
|
+
from ..tools.view_writer import ViewWriter as _FallbackViewWriter
|
|
474
|
+
|
|
475
|
+
return _FallbackViewWriter(self.view_tools.sessions, self.view_tools.backend).create_view(
|
|
476
|
+
profile=profile, payload=payload
|
|
477
|
+
)
|
|
478
|
+
|
|
463
479
|
def _build_views(self, profile: str, entity: CompiledEntity, store: RunArtifactStore) -> None:
|
|
464
480
|
app_key = self._get_app_key(store, entity.entity_id)
|
|
465
481
|
field_meta = store.get_artifact("field_maps", entity.entity_id, {})
|
|
@@ -474,7 +490,7 @@ class SolutionExecutor:
|
|
|
474
490
|
schema=schema,
|
|
475
491
|
group_by_field_id=view_plan["group_by_field_id"],
|
|
476
492
|
)
|
|
477
|
-
result = self.
|
|
493
|
+
result = self._create_view_via_writer(profile=profile, payload=create_payload)
|
|
478
494
|
view_result = result.get("result")
|
|
479
495
|
view_key = ((view_result or {}) if isinstance(view_result, dict) else {}).get("viewgraphKey")
|
|
480
496
|
if not view_key and isinstance(view_result, str):
|
|
@@ -895,13 +911,14 @@ class SolutionExecutor:
|
|
|
895
911
|
data.setdefault("asosChartConfig", {"limitType": 1, "asosChartIdList": []})
|
|
896
912
|
data.setdefault("viewgraphGanttConfigVO", None)
|
|
897
913
|
data.setdefault("viewgraphHierarchyConfigVO", None)
|
|
914
|
+
data.setdefault("viewgraphCalendarConfigVO", None)
|
|
898
915
|
data.setdefault("viewgraphLimitFormula", "")
|
|
899
916
|
data.setdefault("buttonConfigDTOList", [])
|
|
900
917
|
if not data.get("viewgraphQuestions"):
|
|
901
918
|
data["viewgraphQuestions"] = _build_viewgraph_questions(schema, visible_que_ids)
|
|
902
919
|
if not data.get("viewgraphSorts"):
|
|
903
920
|
data["viewgraphSorts"] = [{"queId": 0, "beingSortAscend": True, "queType": 8}]
|
|
904
|
-
if data.get("viewgraphType") in {"cardView", "boardView", "ganttView", "hierarchyView"}:
|
|
921
|
+
if data.get("viewgraphType") in {"cardView", "boardView", "ganttView", "hierarchyView", "calendarView"}:
|
|
905
922
|
data["titleQue"] = visible_que_ids[0] if visible_que_ids else None
|
|
906
923
|
else:
|
|
907
924
|
data.setdefault("titleQue", None)
|
|
@@ -2218,6 +2235,7 @@ def _portal_view_type(store: RunArtifactStore, entity_id: str, view_id: str) ->
|
|
|
2218
2235
|
"board": "boardView",
|
|
2219
2236
|
"gantt": "ganttView",
|
|
2220
2237
|
"hierarchy": "hierarchyView",
|
|
2238
|
+
"calendar": "calendarView",
|
|
2221
2239
|
}
|
|
2222
2240
|
for entity in store.data.get("normalized_solution_spec", {}).get("entities", []):
|
|
2223
2241
|
if entity["entity_id"] != entity_id:
|
|
@@ -2324,36 +2342,106 @@ def _resolve_package_item_insert_index(tag_items: list[dict[str, Any]], ordinal:
|
|
|
2324
2342
|
return max(0, min(len(tag_items), ordinal - 1))
|
|
2325
2343
|
|
|
2326
2344
|
|
|
2327
|
-
def
|
|
2345
|
+
def _flatten_viewgraph_questions(entries: list[Any] | None) -> dict[int, dict[str, Any]]:
|
|
2346
|
+
"""把读面的 viewgraphQuestions(含 subQues/innerQues 嵌套)按 queId 展平。"""
|
|
2347
|
+
flat: dict[int, dict[str, Any]] = {}
|
|
2348
|
+
|
|
2349
|
+
def walk(entry: Any) -> None:
|
|
2350
|
+
if not isinstance(entry, dict):
|
|
2351
|
+
return
|
|
2352
|
+
que_id = entry.get("queId")
|
|
2353
|
+
if isinstance(que_id, int) and que_id not in flat:
|
|
2354
|
+
flat[que_id] = entry
|
|
2355
|
+
for key in ("subQues", "innerQues"):
|
|
2356
|
+
for child in entry.get(key) or []:
|
|
2357
|
+
if isinstance(child, list):
|
|
2358
|
+
for inner in child:
|
|
2359
|
+
walk(inner)
|
|
2360
|
+
else:
|
|
2361
|
+
walk(child)
|
|
2362
|
+
|
|
2363
|
+
for entry in entries or []:
|
|
2364
|
+
if isinstance(entry, list):
|
|
2365
|
+
for inner in entry:
|
|
2366
|
+
walk(inner)
|
|
2367
|
+
else:
|
|
2368
|
+
walk(entry)
|
|
2369
|
+
return flat
|
|
2370
|
+
|
|
2371
|
+
|
|
2372
|
+
def _build_viewgraph_questions(
|
|
2373
|
+
schema: dict[str, Any],
|
|
2374
|
+
visible_que_ids: list[int],
|
|
2375
|
+
*,
|
|
2376
|
+
existing_by_que_id: dict[int, dict[str, Any]] | None = None,
|
|
2377
|
+
) -> list[dict[str, Any]]:
|
|
2378
|
+
"""重建业务列行;更新时原样保留已配置的系统列。
|
|
2379
|
+
|
|
2380
|
+
existing_by_que_id(读面展平行)存在时,以读面行为基底只更新可见性/序号——
|
|
2381
|
+
保留列级权限(queAuth/beingAddRow/beingDeleteRow/beingEditRow/beingDownload 等),
|
|
2382
|
+
否则重建会把这些标志归默认值,破坏既有列权限配置。
|
|
2383
|
+
系统列读面占位对象不写回;existing_by_que_id=None 表示新建。
|
|
2384
|
+
"""
|
|
2328
2385
|
visible_map = {que_id: index for index, que_id in enumerate(visible_que_ids, start=1)}
|
|
2386
|
+
existing = existing_by_que_id or {}
|
|
2329
2387
|
questions: list[dict[str, Any]] = []
|
|
2330
2388
|
for base_question in schema.get("baseQues", []):
|
|
2331
|
-
question = _build_viewgraph_question(base_question, visible_map, default_auth=3)
|
|
2389
|
+
question = _build_viewgraph_question(base_question, visible_map, default_auth=3, existing=existing_by_que_id)
|
|
2332
2390
|
if question is not None:
|
|
2333
2391
|
questions.append(question)
|
|
2334
2392
|
for line in schema.get("formQues", []):
|
|
2335
2393
|
for question in line:
|
|
2336
|
-
resolved = _build_viewgraph_question(question, visible_map, default_auth=1)
|
|
2394
|
+
resolved = _build_viewgraph_question(question, visible_map, default_auth=1, existing=existing_by_que_id)
|
|
2337
2395
|
if resolved is not None:
|
|
2338
2396
|
questions.append(resolved)
|
|
2397
|
+
# 某些系统列只在视图读面出现,不属于表单 schema。
|
|
2398
|
+
emitted_que_ids = _flatten_viewgraph_questions(questions)
|
|
2399
|
+
for que_id, prior in existing.items():
|
|
2400
|
+
if que_id <= 5 and que_id not in emitted_que_ids:
|
|
2401
|
+
preserved = _build_viewgraph_question(prior, visible_map, default_auth=3, existing=existing)
|
|
2402
|
+
if preserved is not None:
|
|
2403
|
+
questions.append(preserved)
|
|
2339
2404
|
return questions
|
|
2340
2405
|
|
|
2341
2406
|
|
|
2342
|
-
def _build_viewgraph_question(
|
|
2407
|
+
def _build_viewgraph_question(
|
|
2408
|
+
question: dict[str, Any],
|
|
2409
|
+
visible_map: dict[int, int],
|
|
2410
|
+
*,
|
|
2411
|
+
default_auth: int,
|
|
2412
|
+
existing: dict[int, dict[str, Any]] | None = None,
|
|
2413
|
+
) -> dict[str, Any] | None:
|
|
2343
2414
|
que_id = question.get("queId")
|
|
2344
2415
|
if que_id is None:
|
|
2345
2416
|
return None
|
|
2417
|
+
if isinstance(que_id, int) and que_id <= 5 and existing is not None:
|
|
2418
|
+
prior = existing.get(que_id)
|
|
2419
|
+
# 后端会生成未落库的系统字段占位对象;只有落库配置才带这些列状态。
|
|
2420
|
+
if prior is None or all(prior.get(key) is None for key in (
|
|
2421
|
+
"beingListDisplay", "displayOrdinal", "beingAddRow", "beingEditRow", "beingDeleteRow",
|
|
2422
|
+
)):
|
|
2423
|
+
return None
|
|
2424
|
+
return deepcopy(prior)
|
|
2346
2425
|
sub_questions = [
|
|
2347
2426
|
resolved
|
|
2348
2427
|
for sub_question in question.get("subQuestions", []) or []
|
|
2349
|
-
if (resolved := _build_viewgraph_question(sub_question, visible_map, default_auth=1)) is not None
|
|
2428
|
+
if (resolved := _build_viewgraph_question(sub_question, visible_map, default_auth=1, existing=existing)) is not None
|
|
2350
2429
|
]
|
|
2351
2430
|
inner_questions = [
|
|
2352
2431
|
resolved
|
|
2353
2432
|
for inner_row in question.get("innerQuestions", []) or []
|
|
2354
2433
|
for inner_question in inner_row or []
|
|
2355
|
-
if (resolved := _build_viewgraph_question(inner_question, visible_map, default_auth=1)) is not None
|
|
2434
|
+
if (resolved := _build_viewgraph_question(inner_question, visible_map, default_auth=1, existing=existing)) is not None
|
|
2356
2435
|
]
|
|
2436
|
+
prior = (existing or {}).get(que_id) if isinstance(que_id, int) else None
|
|
2437
|
+
if prior is not None:
|
|
2438
|
+
# 读面行为基底:保留全部列级标志,只更新可见性/序号;嵌套子项同样融合
|
|
2439
|
+
merged = deepcopy(prior)
|
|
2440
|
+
merged["beingListDisplay"] = que_id in visible_map
|
|
2441
|
+
merged["displayOrdinal"] = visible_map.get(que_id, -1)
|
|
2442
|
+
merged["subQues"] = sub_questions
|
|
2443
|
+
merged["innerQues"] = inner_questions
|
|
2444
|
+
return merged
|
|
2357
2445
|
return {
|
|
2358
2446
|
"queId": que_id,
|
|
2359
2447
|
"queTitle": question.get("queTitle"),
|