@josephyan/qingflow-cli 1.1.3 → 1.1.5
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 +7 -3
- package/docs/local-agent-install.md +57 -6
- package/entry_point.py +1 -1
- package/npm/bin/qingflow-skills.mjs +5 -0
- package/npm/bin/qingflow.mjs +1 -34
- package/npm/lib/runtime.mjs +21 -101
- package/npm/scripts/postinstall.mjs +1 -10
- package/package.json +3 -2
- package/pyproject.toml +1 -1
- package/skills/qingflow-cli/SKILL.md +58 -44
- package/skills/qingflow-cli/manifest.yaml +1 -1
- package/skills/qingflow-cli/reference/00-INDEX.md +35 -0
- package/skills/qingflow-cli/reference/builder/10-build-single-app.md +38 -0
- package/skills/qingflow-cli/reference/builder/20-build-complete-system.md +39 -0
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_SCHEMA_APPLY_FIELD_TYPES_AND_SCENARIOS.md → builder/30-schema-fields.md} +52 -10
- package/skills/qingflow-cli/reference/builder/40-layout.md +52 -0
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_VIEWS_WORKFLOW.md → builder/50-views.md} +39 -15
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_CHARTS_WORKFLOW.md → builder/60-charts.md} +36 -13
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_PORTAL_WORKFLOW.md → builder/70-portal.md} +36 -13
- package/skills/qingflow-cli/reference/builder/80-buttons-associated-resources.md +41 -0
- package/skills/qingflow-cli/reference/builder/90-workflow.md +34 -0
- package/skills/qingflow-cli/reference/builder/99-publish-verify.md +46 -0
- package/skills/qingflow-cli/reference/builder/README.md +41 -0
- package/skills/qingflow-cli/reference/builder/code-integrations/README.md +130 -0
- package/skills/qingflow-cli/reference/builder/code-integrations/code-block.md +66 -0
- package/skills/qingflow-cli/reference/builder/code-integrations/q-linker.md +77 -0
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_APP_DELIVERY_WORKFLOW.md → builder/reference/app-delivery-sop.md} +26 -16
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/README.md +293 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/build-complete-system.md +809 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/build-single-app.md +830 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/complete-system-development-guide.md +123 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/create-app.md +182 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/environments.md +63 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/flow-actors-and-permissions.md +142 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/gotchas.md +108 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/match-rules.md +114 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/public-surface-sync.md +75 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/single-app-development-guide.md +58 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/solution-playbooks.md +52 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/tool-selection.md +107 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/update-flow.md +7 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/update-layout.md +7 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/update-schema.md +7 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/update-views.md +7 -0
- package/skills/qingflow-cli/reference/builder/workflow/01-overview.md +45 -0
- package/skills/qingflow-cli/reference/builder/workflow/02-update-mode.md +53 -0
- package/skills/qingflow-cli/reference/builder/workflow/03-flow-patterns.md +57 -0
- package/skills/qingflow-cli/reference/builder/workflow/04-stage1-business-modeling.md +131 -0
- package/skills/qingflow-cli/reference/builder/workflow/05-stage2-members-roles.md +29 -0
- package/skills/qingflow-cli/reference/builder/workflow/06-stage3-build-spec.md +165 -0
- package/skills/qingflow-cli/reference/builder/workflow/07-stage4-validate-spec.md +33 -0
- package/skills/qingflow-cli/reference/builder/workflow/08-stage5-apply-verify.md +51 -0
- package/skills/qingflow-cli/reference/builder/workflow/09-stage6-summary.md +88 -0
- package/skills/qingflow-cli/reference/builder/workflow/10-node-config-reference.md +93 -0
- package/skills/qingflow-cli/reference/builder/workflow/11-troubleshooting.md +15 -0
- package/skills/qingflow-cli/reference/builder/workflow/README.md +88 -0
- package/skills/qingflow-cli/reference/builder/workflow/workflow-schema.json +1754 -0
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_ADMIN_CHEATSHEET.md → core/QINGFLOW_CLI_ADMIN_CHEATSHEET.md} +3 -3
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md → core/QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md} +6 -6
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_EXPLORATION_REPORT.md → core/QINGFLOW_CLI_EXPLORATION_REPORT.md} +2 -2
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_FIELD_DATA_TYPES.md → core/QINGFLOW_CLI_FIELD_DATA_TYPES.md} +11 -11
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_MEMBER_CHEATSHEET.md → core/QINGFLOW_CLI_MEMBER_CHEATSHEET.md} +4 -4
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_ONE_SHOT_CHEATSHEET.md → core/QINGFLOW_CLI_ONE_SHOT_CHEATSHEET.md} +4 -4
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_RECORD_CREATE_WORKFLOW.md → record/QINGFLOW_CLI_RECORD_CREATE_WORKFLOW.md} +3 -3
- package/skills/qingflow-cli/reference/record/QINGFLOW_CLI_RECORD_DELETE_WORKFLOW.md +31 -0
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_RECORD_IMPORT_WORKFLOW.md → record/QINGFLOW_CLI_RECORD_IMPORT_WORKFLOW.md} +4 -4
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_RECORD_UPDATE_WORKFLOW.md → record/QINGFLOW_CLI_RECORD_UPDATE_WORKFLOW.md} +7 -7
- package/skills/qingflow-cli/reference/record/analysis/README.md +130 -0
- package/skills/qingflow-cli/reference/record/analysis/analysis-gotchas.md +91 -0
- package/skills/qingflow-cli/reference/record/analysis/analysis-patterns.md +112 -0
- package/skills/qingflow-cli/reference/record/analysis/business-context.md +74 -0
- package/skills/qingflow-cli/reference/record/analysis/confidence-reporting.md +69 -0
- package/skills/qingflow-cli/reference/record/analysis/data-access-playbook.md +106 -0
- package/skills/qingflow-cli/reference/record/analysis/pandas-recipes.md +172 -0
- package/skills/qingflow-cli/reference/record/analysis/report-format.md +76 -0
- package/skills/qingflow-cli/reference/record/insert/README.md +75 -0
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_TASK_CONTEXT_WORKFLOW.md → task/QINGFLOW_CLI_TASK_CONTEXT_WORKFLOW.md} +5 -5
- package/skills/qingflow-cli/reference/task/ops/README.md +131 -0
- package/skills/qingflow-cli/reference/task/ops/environments.md +43 -0
- package/skills/qingflow-cli/reference/task/ops/workflow-usage.md +26 -0
- package/skills/qingflow-cli/scripts/validate_system_build_summary.py +124 -0
- package/skills/qingflow-cli/scripts/workflow/diff_flow_spec.py +275 -0
- package/skills/qingflow-cli/scripts/workflow/validate_flow_spec.py +605 -0
- package/skills/qingflow-mcp-setup/SKILL.md +115 -0
- package/skills/qingflow-mcp-setup/agents/openai.yaml +4 -0
- package/skills/qingflow-mcp-setup/references/claude-desktop.md +34 -0
- package/skills/qingflow-mcp-setup/references/environments.md +62 -0
- package/skills/qingflow-mcp-setup/references/generic-stdio.md +32 -0
- package/skills/qingflow-mcp-setup/scripts/check_local_server.sh +38 -0
- package/src/qingflow_mcp/__init__.py +1 -1
- package/src/qingflow_mcp/__main__.py +6 -2
- package/src/qingflow_mcp/builder_facade/models.py +287 -25
- package/src/qingflow_mcp/builder_facade/service.py +4195 -856
- package/src/qingflow_mcp/cli/commands/builder.py +316 -247
- package/src/qingflow_mcp/cli/commands/chart.py +1 -1
- package/src/qingflow_mcp/cli/commands/common.py +12 -3
- package/src/qingflow_mcp/cli/commands/exports.py +2 -2
- package/src/qingflow_mcp/cli/commands/imports.py +3 -3
- package/src/qingflow_mcp/cli/commands/portal.py +2 -2
- package/src/qingflow_mcp/cli/commands/record.py +101 -27
- package/src/qingflow_mcp/cli/commands/task.py +28 -47
- package/src/qingflow_mcp/cli/commands/view.py +1 -1
- package/src/qingflow_mcp/cli/context.py +0 -3
- package/src/qingflow_mcp/cli/formatters.py +784 -16
- package/src/qingflow_mcp/cli/main.py +117 -33
- package/src/qingflow_mcp/errors.py +43 -2
- package/src/qingflow_mcp/public_surface.py +26 -17
- package/src/qingflow_mcp/response_trim.py +81 -17
- package/src/qingflow_mcp/server.py +14 -12
- package/src/qingflow_mcp/server_app_builder.py +65 -21
- package/src/qingflow_mcp/server_app_user.py +22 -16
- package/src/qingflow_mcp/session_store.py +11 -7
- package/src/qingflow_mcp/solution/compiler/__init__.py +3 -1
- package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +173 -0
- package/src/qingflow_mcp/solution/executor.py +245 -18
- package/src/qingflow_mcp/tools/ai_builder_tools.py +1782 -399
- package/src/qingflow_mcp/tools/app_tools.py +184 -43
- package/src/qingflow_mcp/tools/approval_tools.py +197 -35
- package/src/qingflow_mcp/tools/auth_tools.py +92 -16
- package/src/qingflow_mcp/tools/code_block_tools.py +298 -40
- package/src/qingflow_mcp/tools/custom_button_tools.py +64 -10
- package/src/qingflow_mcp/tools/directory_tools.py +236 -72
- package/src/qingflow_mcp/tools/export_tools.py +244 -34
- package/src/qingflow_mcp/tools/feedback_tools.py +9 -0
- package/src/qingflow_mcp/tools/file_tools.py +9 -3
- package/src/qingflow_mcp/tools/import_tools.py +336 -49
- package/src/qingflow_mcp/tools/navigation_tools.py +91 -12
- package/src/qingflow_mcp/tools/package_tools.py +118 -6
- package/src/qingflow_mcp/tools/portal_tools.py +39 -3
- package/src/qingflow_mcp/tools/qingbi_report_tools.py +116 -7
- package/src/qingflow_mcp/tools/record_tools.py +1141 -356
- package/src/qingflow_mcp/tools/resource_read_tools.py +188 -39
- package/src/qingflow_mcp/tools/role_tools.py +80 -9
- package/src/qingflow_mcp/tools/solution_tools.py +59 -45
- package/src/qingflow_mcp/tools/task_context_tools.py +662 -158
- package/src/qingflow_mcp/tools/task_tools.py +113 -29
- package/src/qingflow_mcp/tools/view_tools.py +106 -3
- package/src/qingflow_mcp/tools/workflow_tools.py +48 -4
- package/src/qingflow_mcp/tools/workspace_tools.py +71 -3
- /package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_MATCH_RULES.md → builder/reference/match-rules.md} +0 -0
- /package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_WORKSPACE_ICONS.md → builder/reference/workspace-icons.md} +0 -0
- /package/skills/qingflow-cli/reference/{charts_remove.example.json → examples/charts/charts_remove.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{charts_reorder.example.json → examples/charts/charts_reorder.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{charts_upsert_bar.example.json → examples/charts/charts_upsert_bar.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{charts_upsert_dashboard_starter.example.json → examples/charts/charts_upsert_dashboard_starter.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{charts_upsert_minimal.example.json → examples/charts/charts_upsert_minimal.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{portal_sections_all_types.example.json → examples/portal/portal_sections_all_types.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{portal_sections_five_types.example.json → examples/portal/portal_sections_five_types.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{portal_sections_standard_workbench.example.json → examples/portal/portal_sections_standard_workbench.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{_batch_schema_complex.json → examples/schema/_batch_schema_complex.json} +0 -0
- /package/skills/qingflow-cli/reference/{_batch_schema_scalar.json → examples/schema/_batch_schema_scalar.json} +0 -0
- /package/skills/qingflow-cli/reference/{schema_add_fields_minimal.example.json → examples/schema/schema_add_fields_minimal.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{schema_apply_add_fields_all_types.json → examples/schema/schema_apply_add_fields_all_types.json} +0 -0
- /package/skills/qingflow-cli/reference/{views_upsert_table_minimal.example.json → examples/views/views_upsert_table_minimal.example.json} +0 -0
|
@@ -8,8 +8,11 @@ from typing import Any, TextIO
|
|
|
8
8
|
def emit_text_result(result: dict[str, Any], *, hint: str, stream: TextIO) -> None:
|
|
9
9
|
text = _format_cancelled_result(result)
|
|
10
10
|
if text is None:
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
if _is_failed_result(result) and hint != "task_action_execute":
|
|
12
|
+
text = _format_failed_result(result)
|
|
13
|
+
else:
|
|
14
|
+
formatter = _FORMATTERS.get(hint, _format_generic)
|
|
15
|
+
text = formatter(result)
|
|
13
16
|
stream.write(text)
|
|
14
17
|
if not text.endswith("\n"):
|
|
15
18
|
stream.write("\n")
|
|
@@ -21,6 +24,51 @@ def _format_cancelled_result(result: dict[str, Any]) -> str | None:
|
|
|
21
24
|
return str(result.get("message") or "已取消") + "\n"
|
|
22
25
|
|
|
23
26
|
|
|
27
|
+
def _is_failed_result(result: dict[str, Any]) -> bool:
|
|
28
|
+
if _is_executed_nonfatal_result(result):
|
|
29
|
+
return False
|
|
30
|
+
if result.get("ok") is False:
|
|
31
|
+
return True
|
|
32
|
+
return str(result.get("status") or "").lower() in {"failed", "blocked"}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _is_executed_nonfatal_result(result: dict[str, Any]) -> bool:
|
|
36
|
+
status = str(result.get("status") or "").lower()
|
|
37
|
+
executed = bool(
|
|
38
|
+
result.get("write_executed")
|
|
39
|
+
or result.get("write_may_have_succeeded")
|
|
40
|
+
or result.get("delete_executed")
|
|
41
|
+
or result.get("action_executed")
|
|
42
|
+
or result.get("export_executed")
|
|
43
|
+
)
|
|
44
|
+
return executed and status in {"partial_success", "verification_failed", "running", "queued", "unknown"}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _format_failed_result(result: dict[str, Any]) -> str:
|
|
48
|
+
lines = [f"Status: {result.get('status') or 'failed'}"]
|
|
49
|
+
for key, label in (
|
|
50
|
+
("error_code", "Error Code"),
|
|
51
|
+
("message", "Message"),
|
|
52
|
+
("backend_code", "Backend Code"),
|
|
53
|
+
("request_id", "Request ID"),
|
|
54
|
+
("http_status", "HTTP Status"),
|
|
55
|
+
("category", "Category"),
|
|
56
|
+
):
|
|
57
|
+
value = result.get(key)
|
|
58
|
+
if value not in (None, ""):
|
|
59
|
+
lines.append(f"{label}: {value}")
|
|
60
|
+
|
|
61
|
+
data = result.get("data") if isinstance(result.get("data"), dict) else {}
|
|
62
|
+
selection = data.get("selection") if isinstance(data.get("selection"), dict) else {}
|
|
63
|
+
if selection:
|
|
64
|
+
lines.append("Selection:")
|
|
65
|
+
lines.extend(f"- {line}" for line in _dict_scalar_lines(selection))
|
|
66
|
+
|
|
67
|
+
_append_warnings(lines, result.get("warnings"))
|
|
68
|
+
_append_verification(lines, result.get("verification"))
|
|
69
|
+
return "\n".join(lines) + "\n"
|
|
70
|
+
|
|
71
|
+
|
|
24
72
|
def _format_generic(result: dict[str, Any]) -> str:
|
|
25
73
|
lines: list[str] = []
|
|
26
74
|
title = _first_present(result, "status", "message")
|
|
@@ -180,6 +228,120 @@ def _format_app_get(result: dict[str, Any]) -> str:
|
|
|
180
228
|
_append_warnings(lines, result.get("warnings"))
|
|
181
229
|
return "\n".join(lines) + "\n"
|
|
182
230
|
|
|
231
|
+
|
|
232
|
+
def _format_portal_list(result: dict[str, Any]) -> str:
|
|
233
|
+
data = result.get("data") if isinstance(result.get("data"), dict) else {}
|
|
234
|
+
items = data.get("items") if isinstance(data.get("items"), list) else []
|
|
235
|
+
rows: list[list[str]] = []
|
|
236
|
+
for item in items:
|
|
237
|
+
if not isinstance(item, dict):
|
|
238
|
+
continue
|
|
239
|
+
rows.append(
|
|
240
|
+
[
|
|
241
|
+
str(item.get("dash_key") or ""),
|
|
242
|
+
str(item.get("dash_name") or ""),
|
|
243
|
+
",".join(str(tag_id) for tag_id in item.get("package_tag_ids") or []),
|
|
244
|
+
]
|
|
245
|
+
)
|
|
246
|
+
text = _render_titled_table("Portals", ["dash_key", "name", "package_tag_ids"], rows).rstrip("\n")
|
|
247
|
+
lines = [text, f"Total: {data.get('total') if data.get('total') is not None else len(items)}"]
|
|
248
|
+
_append_warnings(lines, result.get("warnings"))
|
|
249
|
+
_append_verification(lines, result.get("verification"))
|
|
250
|
+
return "\n".join(lines) + "\n"
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _format_portal_get(result: dict[str, Any]) -> str:
|
|
254
|
+
data = result.get("data") if isinstance(result.get("data"), dict) else {}
|
|
255
|
+
components = data.get("components") if isinstance(data.get("components"), list) else []
|
|
256
|
+
lines = [
|
|
257
|
+
f"Portal: {data.get('dash_name') or '-'}",
|
|
258
|
+
f"Dash Key: {data.get('dash_key') or '-'}",
|
|
259
|
+
f"Components: {data.get('component_count') if data.get('component_count') is not None else len(components)}",
|
|
260
|
+
]
|
|
261
|
+
package_tag_ids = data.get("package_tag_ids") if isinstance(data.get("package_tag_ids"), list) else []
|
|
262
|
+
if package_tag_ids:
|
|
263
|
+
lines.append("Package Tag IDs: " + ", ".join(str(item) for item in package_tag_ids))
|
|
264
|
+
if components:
|
|
265
|
+
lines.append("Component Refs:")
|
|
266
|
+
for item in components[:12]:
|
|
267
|
+
if not isinstance(item, dict):
|
|
268
|
+
continue
|
|
269
|
+
prefix = f"- {item.get('source_type') or 'unknown'}"
|
|
270
|
+
title = item.get("title")
|
|
271
|
+
chart_ref = item.get("chart_ref") if isinstance(item.get("chart_ref"), dict) else {}
|
|
272
|
+
view_ref = item.get("view_ref") if isinstance(item.get("view_ref"), dict) else {}
|
|
273
|
+
if chart_ref:
|
|
274
|
+
lines.append(f"{prefix}: {title or chart_ref.get('chart_name') or '-'} / chart_id={chart_ref.get('chart_id') or '-'}")
|
|
275
|
+
elif view_ref:
|
|
276
|
+
lines.append(
|
|
277
|
+
f"{prefix}: {title or view_ref.get('view_name') or '-'} / "
|
|
278
|
+
f"view_id={view_ref.get('view_id') or '-'} / app_key={view_ref.get('app_key') or '-'}"
|
|
279
|
+
)
|
|
280
|
+
else:
|
|
281
|
+
lines.append(f"{prefix}: {title or '-'}")
|
|
282
|
+
if len(components) > 12:
|
|
283
|
+
lines.append(f"... {len(components) - 12} more")
|
|
284
|
+
_append_warnings(lines, result.get("warnings"))
|
|
285
|
+
_append_verification(lines, result.get("verification"))
|
|
286
|
+
return "\n".join(lines) + "\n"
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def _format_view_get(result: dict[str, Any]) -> str:
|
|
290
|
+
data = result.get("data") if isinstance(result.get("data"), dict) else {}
|
|
291
|
+
export_capability = data.get("export_capability") if isinstance(data.get("export_capability"), dict) else {}
|
|
292
|
+
visible_columns = data.get("visible_columns") if isinstance(data.get("visible_columns"), list) else []
|
|
293
|
+
lines = [
|
|
294
|
+
f"View: {data.get('view_name') or '-'}",
|
|
295
|
+
f"View ID: {data.get('view_id') or '-'}",
|
|
296
|
+
f"View Key: {data.get('view_key') or '-'}",
|
|
297
|
+
f"App Key: {data.get('app_key') or '-'}",
|
|
298
|
+
f"View Type: {data.get('view_type') or '-'}",
|
|
299
|
+
f"Analysis Supported: {data.get('analysis_supported')}",
|
|
300
|
+
]
|
|
301
|
+
if export_capability:
|
|
302
|
+
lines.append(
|
|
303
|
+
"Export: "
|
|
304
|
+
f"supported={export_capability.get('supported')} / "
|
|
305
|
+
f"tool={export_capability.get('tool') or '-'} / "
|
|
306
|
+
f"requires_app_key={export_capability.get('requires_app_key')}"
|
|
307
|
+
)
|
|
308
|
+
lines.append(f"Visible Columns: {len(visible_columns)}")
|
|
309
|
+
for column in visible_columns[:20]:
|
|
310
|
+
lines.append(f"- {column}")
|
|
311
|
+
if len(visible_columns) > 20:
|
|
312
|
+
lines.append(f"... {len(visible_columns) - 20} more")
|
|
313
|
+
_append_warnings(lines, result.get("warnings"))
|
|
314
|
+
_append_verification(lines, result.get("verification"))
|
|
315
|
+
return "\n".join(lines) + "\n"
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def _format_chart_get(result: dict[str, Any]) -> str:
|
|
319
|
+
data = result.get("data") if isinstance(result.get("data"), dict) else {}
|
|
320
|
+
chart_data = data.get("data") if isinstance(data.get("data"), dict) else {}
|
|
321
|
+
config = data.get("config") if isinstance(data.get("config"), dict) else {}
|
|
322
|
+
lines = [
|
|
323
|
+
f"Chart: {data.get('chart_name') or '-'}",
|
|
324
|
+
f"Chart ID: {data.get('chart_id') or '-'}",
|
|
325
|
+
f"Chart Type: {data.get('chart_type') or '-'}",
|
|
326
|
+
f"Data Source: {data.get('data_source_type') or '-'} / {data.get('data_source_id') or '-'}",
|
|
327
|
+
f"Data Loaded: {bool(chart_data)}",
|
|
328
|
+
f"Config Loaded: {bool(config)}",
|
|
329
|
+
]
|
|
330
|
+
summary = chart_data.get("summary") if isinstance(chart_data.get("summary"), dict) else {}
|
|
331
|
+
rows = chart_data.get("rows") if isinstance(chart_data.get("rows"), list) else []
|
|
332
|
+
if summary:
|
|
333
|
+
lines.append("Summary:")
|
|
334
|
+
lines.extend(f"- {line}" for line in _dict_scalar_lines(summary))
|
|
335
|
+
if rows:
|
|
336
|
+
lines.append(f"Rows: {len(rows)}")
|
|
337
|
+
lines.append(json.dumps(rows[0], ensure_ascii=False))
|
|
338
|
+
elif config:
|
|
339
|
+
lines.append("Config Keys: " + ", ".join(str(key) for key in list(config.keys())[:12]))
|
|
340
|
+
_append_warnings(lines, result.get("warnings"))
|
|
341
|
+
_append_verification(lines, result.get("verification"))
|
|
342
|
+
return "\n".join(lines) + "\n"
|
|
343
|
+
|
|
344
|
+
|
|
183
345
|
def _format_record_list(result: dict[str, Any]) -> str:
|
|
184
346
|
data = result.get("data") if isinstance(result.get("data"), dict) else {}
|
|
185
347
|
items = data.get("items") if isinstance(data.get("items"), list) else []
|
|
@@ -234,6 +396,159 @@ def _format_record_access(result: dict[str, Any]) -> str:
|
|
|
234
396
|
return "\n".join(lines) + "\n"
|
|
235
397
|
|
|
236
398
|
|
|
399
|
+
def _format_record_candidates(result: dict[str, Any]) -> str:
|
|
400
|
+
data = result.get("data") if isinstance(result.get("data"), dict) else {}
|
|
401
|
+
selection = data.get("selection") if isinstance(data.get("selection"), dict) else {}
|
|
402
|
+
pagination = data.get("pagination") if isinstance(data.get("pagination"), dict) else {}
|
|
403
|
+
items = data.get("items") if isinstance(data.get("items"), list) else []
|
|
404
|
+
lines = [
|
|
405
|
+
f"Field: {selection.get('field_title') or '-'} ({selection.get('field_id') or '-'})",
|
|
406
|
+
f"App Key: {selection.get('app_key') or '-'}",
|
|
407
|
+
f"Scope Source: {data.get('scope_source') or '-'}",
|
|
408
|
+
f"Keyword: {selection.get('keyword') if selection.get('keyword') not in (None, '') else '-'}",
|
|
409
|
+
]
|
|
410
|
+
if selection.get("record_id") not in (None, "") or selection.get("workflow_node_id") not in (None, "") or selection.get("fields_present"):
|
|
411
|
+
lines.append(
|
|
412
|
+
"Runtime Context: "
|
|
413
|
+
f"record_id={selection.get('record_id') or '-'} / "
|
|
414
|
+
f"workflow_node_id={selection.get('workflow_node_id') or '-'} / "
|
|
415
|
+
f"fields_present={selection.get('fields_present')}"
|
|
416
|
+
)
|
|
417
|
+
if pagination:
|
|
418
|
+
lines.append(
|
|
419
|
+
"Pagination: "
|
|
420
|
+
f"page={pagination.get('page')} / "
|
|
421
|
+
f"page_size={pagination.get('page_size')} / "
|
|
422
|
+
f"returned={pagination.get('returned_items')} / "
|
|
423
|
+
f"total={pagination.get('reported_total')} / "
|
|
424
|
+
f"pages={pagination.get('page_amount')}"
|
|
425
|
+
)
|
|
426
|
+
lines.append(f"Candidates: {len(items)}")
|
|
427
|
+
for item in items[:20]:
|
|
428
|
+
if not isinstance(item, dict):
|
|
429
|
+
lines.append(f"- {item}")
|
|
430
|
+
continue
|
|
431
|
+
parts = [str(item.get("value") or item.get("name") or item.get("id") or "-")]
|
|
432
|
+
if item.get("id") not in (None, ""):
|
|
433
|
+
parts.append(f"id={item.get('id')}")
|
|
434
|
+
if item.get("userId") not in (None, ""):
|
|
435
|
+
parts.append(f"userId={item.get('userId')}")
|
|
436
|
+
if item.get("email") not in (None, ""):
|
|
437
|
+
parts.append(f"email={item.get('email')}")
|
|
438
|
+
if item.get("path") not in (None, ""):
|
|
439
|
+
parts.append(f"path={item.get('path')}")
|
|
440
|
+
sources = item.get("sources") if isinstance(item.get("sources"), list) else []
|
|
441
|
+
source_labels = [
|
|
442
|
+
str(source.get("kind"))
|
|
443
|
+
for source in sources
|
|
444
|
+
if isinstance(source, dict) and source.get("kind") not in (None, "")
|
|
445
|
+
]
|
|
446
|
+
if source_labels:
|
|
447
|
+
parts.append("sources=" + ",".join(source_labels))
|
|
448
|
+
lines.append("- " + " / ".join(parts))
|
|
449
|
+
if len(items) > 20:
|
|
450
|
+
lines.append(f"... {len(items) - 20} more")
|
|
451
|
+
_append_warnings(lines, result.get("warnings"))
|
|
452
|
+
_append_verification(lines, result.get("verification"))
|
|
453
|
+
return "\n".join(lines) + "\n"
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def _format_record_schema(result: dict[str, Any]) -> str:
|
|
457
|
+
lines = [
|
|
458
|
+
f"Status: {result.get('status') or '-'}",
|
|
459
|
+
f"App Key: {result.get('app_key') or '-'}",
|
|
460
|
+
f"Schema Scope: {result.get('schema_scope') or '-'}",
|
|
461
|
+
]
|
|
462
|
+
if result.get("record_id") not in (None, ""):
|
|
463
|
+
lines.append(f"Record ID: {result.get('record_id')}")
|
|
464
|
+
view_resolution = result.get("view_resolution") if isinstance(result.get("view_resolution"), dict) else {}
|
|
465
|
+
view_id = view_resolution.get("view_id") or view_resolution.get("id")
|
|
466
|
+
if view_id:
|
|
467
|
+
lines.append(f"View ID: {view_id}")
|
|
468
|
+
if result.get("schema_fingerprint") not in (None, ""):
|
|
469
|
+
lines.append(f"Schema Fingerprint: {result.get('schema_fingerprint')}")
|
|
470
|
+
|
|
471
|
+
sections: list[tuple[str, list[Any]]] = []
|
|
472
|
+
for key, label in (
|
|
473
|
+
("fields", "Fields"),
|
|
474
|
+
("required_fields", "Required Fields"),
|
|
475
|
+
("optional_fields", "Optional Fields"),
|
|
476
|
+
("runtime_linked_required_fields", "Runtime Linked Required Fields"),
|
|
477
|
+
("writable_fields", "Writable Fields"),
|
|
478
|
+
("columns", "Import Columns"),
|
|
479
|
+
("input_fields", "Input Fields"),
|
|
480
|
+
("code_block_fields", "Code Block Fields"),
|
|
481
|
+
("ambiguous_fields", "Ambiguous Fields"),
|
|
482
|
+
):
|
|
483
|
+
value = result.get(key)
|
|
484
|
+
if isinstance(value, list):
|
|
485
|
+
sections.append((label, value))
|
|
486
|
+
|
|
487
|
+
for label, items in sections:
|
|
488
|
+
lines.append(f"{label}: {len(items)}")
|
|
489
|
+
for item in items[:20]:
|
|
490
|
+
if isinstance(item, dict):
|
|
491
|
+
lines.append("- " + _schema_item_summary(item))
|
|
492
|
+
else:
|
|
493
|
+
lines.append(f"- {item}")
|
|
494
|
+
if len(items) > 20:
|
|
495
|
+
lines.append(f"... {len(items) - 20} more")
|
|
496
|
+
|
|
497
|
+
payload_template = result.get("payload_template") if isinstance(result.get("payload_template"), dict) else {}
|
|
498
|
+
if payload_template:
|
|
499
|
+
lines.append("Payload Template Keys: " + ", ".join(str(key) for key in list(payload_template.keys())[:20]))
|
|
500
|
+
|
|
501
|
+
available_routes = result.get("available_update_routes") if isinstance(result.get("available_update_routes"), list) else []
|
|
502
|
+
if available_routes:
|
|
503
|
+
route_labels = [
|
|
504
|
+
str(item.get("route_type") or item.get("endpoint_kind") or item.get("view_id") or "-")
|
|
505
|
+
for item in available_routes[:8]
|
|
506
|
+
if isinstance(item, dict)
|
|
507
|
+
]
|
|
508
|
+
lines.append("Available Update Routes: " + ", ".join(route_labels))
|
|
509
|
+
|
|
510
|
+
suggested_next_call = result.get("suggested_next_call") if isinstance(result.get("suggested_next_call"), dict) else {}
|
|
511
|
+
if suggested_next_call.get("tool_name"):
|
|
512
|
+
lines.append(f"Suggested Next Call: {suggested_next_call.get('tool_name')}")
|
|
513
|
+
|
|
514
|
+
_append_warnings(lines, result.get("warnings"))
|
|
515
|
+
_append_verification(lines, result.get("verification"))
|
|
516
|
+
return "\n".join(lines) + "\n"
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
def _schema_item_summary(item: dict[str, Any]) -> str:
|
|
520
|
+
field_id = (
|
|
521
|
+
item.get("field_id")
|
|
522
|
+
or item.get("que_id")
|
|
523
|
+
or item.get("queId")
|
|
524
|
+
or item.get("id")
|
|
525
|
+
or item.get("selector")
|
|
526
|
+
or "-"
|
|
527
|
+
)
|
|
528
|
+
title = item.get("title") or item.get("que_title") or item.get("queTitle") or item.get("name") or item.get("selector") or "-"
|
|
529
|
+
kind = item.get("kind") or item.get("write_kind") or item.get("que_type") or item.get("queType") or item.get("type") or "-"
|
|
530
|
+
parts = [f"{title} ({field_id})", f"kind={kind}"]
|
|
531
|
+
for key in ("required", "writable", "may_become_required", "requires_lookup", "requires_upload"):
|
|
532
|
+
if key in item:
|
|
533
|
+
parts.append(f"{key}={item.get(key)}")
|
|
534
|
+
if item.get("import_value_format") not in (None, ""):
|
|
535
|
+
parts.append(f"import_value_format={item.get('import_value_format')}")
|
|
536
|
+
if item.get("target_app_key") not in (None, ""):
|
|
537
|
+
parts.append(f"target_app_key={item.get('target_app_key')}")
|
|
538
|
+
options = item.get("options") if isinstance(item.get("options"), list) else []
|
|
539
|
+
if options:
|
|
540
|
+
parts.append(f"options={len(options)}")
|
|
541
|
+
bound_outputs = item.get("bound_output_fields") if isinstance(item.get("bound_output_fields"), list) else []
|
|
542
|
+
if bound_outputs:
|
|
543
|
+
parts.append("bound_outputs=" + ",".join(str(value) for value in bound_outputs[:5]))
|
|
544
|
+
aliases = item.get("configured_aliases") if isinstance(item.get("configured_aliases"), list) else []
|
|
545
|
+
if aliases:
|
|
546
|
+
parts.append("aliases=" + ",".join(str(value) for value in aliases[:5]))
|
|
547
|
+
if item.get("format_hint") not in (None, ""):
|
|
548
|
+
parts.append(f"hint={item.get('format_hint')}")
|
|
549
|
+
return " / ".join(parts)
|
|
550
|
+
|
|
551
|
+
|
|
237
552
|
def _format_record_get(result: dict[str, Any]) -> str:
|
|
238
553
|
record = result.get("record") if isinstance(result.get("record"), dict) else {}
|
|
239
554
|
app = result.get("app") if isinstance(result.get("app"), dict) else {}
|
|
@@ -323,6 +638,116 @@ def _format_record_logs(result: dict[str, Any]) -> str:
|
|
|
323
638
|
return "\n".join(lines) + "\n"
|
|
324
639
|
|
|
325
640
|
|
|
641
|
+
def _format_record_write(result: dict[str, Any]) -> str:
|
|
642
|
+
status = str(result.get("status") or "").strip().lower()
|
|
643
|
+
write_executed = bool(result.get("write_executed"))
|
|
644
|
+
verification_status = str(result.get("verification_status") or "").strip().lower()
|
|
645
|
+
if write_executed and verification_status == "failed":
|
|
646
|
+
title = "写入已提交(回读验证未完成)"
|
|
647
|
+
elif write_executed and status in {"success", "completed"}:
|
|
648
|
+
title = "写入成功"
|
|
649
|
+
elif status:
|
|
650
|
+
title = status
|
|
651
|
+
else:
|
|
652
|
+
title = "写入结果"
|
|
653
|
+
|
|
654
|
+
lines = [title]
|
|
655
|
+
if result.get("record_id") not in (None, ""):
|
|
656
|
+
lines.append(f"Record ID: {result.get('record_id')}")
|
|
657
|
+
if result.get("apply_id") not in (None, "") and result.get("apply_id") != result.get("record_id"):
|
|
658
|
+
lines.append(f"Apply ID: {result.get('apply_id')}")
|
|
659
|
+
|
|
660
|
+
update_route = result.get("update_route") if isinstance(result.get("update_route"), dict) else {}
|
|
661
|
+
route_name = update_route.get("route") or update_route.get("type") or update_route.get("label")
|
|
662
|
+
if route_name:
|
|
663
|
+
lines.append(f"Update Route: {route_name}")
|
|
664
|
+
|
|
665
|
+
if verification_status:
|
|
666
|
+
lines.append(f"Verification: {verification_status}")
|
|
667
|
+
if write_executed:
|
|
668
|
+
lines.append("Safe To Retry: false")
|
|
669
|
+
|
|
670
|
+
if write_executed and verification_status == "failed":
|
|
671
|
+
verification = result.get("data", {}).get("verification") if isinstance(result.get("data"), dict) else None
|
|
672
|
+
if isinstance(verification, dict):
|
|
673
|
+
warning_codes = [
|
|
674
|
+
str(item.get("code"))
|
|
675
|
+
for item in verification.get("warnings", [])
|
|
676
|
+
if isinstance(item, dict) and item.get("code")
|
|
677
|
+
]
|
|
678
|
+
if warning_codes:
|
|
679
|
+
lines.append("Verification Note: " + ", ".join(warning_codes[:3]))
|
|
680
|
+
lines.append("说明:写请求已执行;当前结果只表示后置回读未能确认字段值,不等同于写入被拒绝。")
|
|
681
|
+
|
|
682
|
+
_append_warnings(lines, result.get("warnings"))
|
|
683
|
+
return "\n".join(lines) + "\n"
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
def _format_record_delete(result: dict[str, Any]) -> str:
|
|
687
|
+
status = str(result.get("status") or "").strip().lower()
|
|
688
|
+
deleted_ids = [str(item) for item in result.get("deleted_ids", []) if item not in (None, "")]
|
|
689
|
+
failed_ids = [str(item) for item in result.get("failed_ids", []) if item not in (None, "")]
|
|
690
|
+
write_executed = bool(result.get("write_executed"))
|
|
691
|
+
|
|
692
|
+
if deleted_ids and failed_ids:
|
|
693
|
+
title = "删除部分完成"
|
|
694
|
+
elif deleted_ids:
|
|
695
|
+
title = "删除完成"
|
|
696
|
+
elif status == "failed" or result.get("ok") is False:
|
|
697
|
+
title = "删除未执行"
|
|
698
|
+
else:
|
|
699
|
+
title = "删除结果"
|
|
700
|
+
|
|
701
|
+
lines = [title]
|
|
702
|
+
lines.append(f"Deleted: {len(deleted_ids)}")
|
|
703
|
+
if deleted_ids:
|
|
704
|
+
lines.append("Deleted IDs: " + ", ".join(deleted_ids[:20]))
|
|
705
|
+
lines.append(f"Failed: {len(failed_ids)}")
|
|
706
|
+
if failed_ids:
|
|
707
|
+
lines.append("Failed IDs: " + ", ".join(failed_ids[:20]))
|
|
708
|
+
if write_executed:
|
|
709
|
+
lines.append("Safe To Retry: false")
|
|
710
|
+
elif result.get("safe_to_retry") is not None:
|
|
711
|
+
lines.append(f"Safe To Retry: {str(bool(result.get('safe_to_retry'))).lower()}")
|
|
712
|
+
_append_warnings(lines, result.get("warnings"))
|
|
713
|
+
return "\n".join(lines) + "\n"
|
|
714
|
+
|
|
715
|
+
|
|
716
|
+
def _format_code_block_run(result: dict[str, Any]) -> str:
|
|
717
|
+
status = str(result.get("status") or "").strip().lower()
|
|
718
|
+
execution = result.get("execution") if isinstance(result.get("execution"), dict) else {}
|
|
719
|
+
writeback = result.get("writeback") if isinstance(result.get("writeback"), dict) else {}
|
|
720
|
+
executed = bool(execution.get("executed"))
|
|
721
|
+
writeback_applied = bool(writeback.get("applied"))
|
|
722
|
+
write_verified = writeback.get("write_verified")
|
|
723
|
+
|
|
724
|
+
if executed and writeback_applied and status == "verification_failed":
|
|
725
|
+
title = "代码块已执行,回写已提交(验证未完成)"
|
|
726
|
+
elif executed and writeback_applied:
|
|
727
|
+
title = "代码块已执行,回写成功"
|
|
728
|
+
elif executed:
|
|
729
|
+
title = "代码块已执行"
|
|
730
|
+
elif status:
|
|
731
|
+
title = status
|
|
732
|
+
else:
|
|
733
|
+
title = "代码块结果"
|
|
734
|
+
|
|
735
|
+
lines = [title]
|
|
736
|
+
if result.get("record_id") not in (None, ""):
|
|
737
|
+
lines.append(f"Record ID: {result.get('record_id')}")
|
|
738
|
+
code_block_field = result.get("code_block_field") if isinstance(result.get("code_block_field"), dict) else {}
|
|
739
|
+
if code_block_field.get("title"):
|
|
740
|
+
lines.append(f"Code Block Field: {code_block_field.get('title')}")
|
|
741
|
+
if execution:
|
|
742
|
+
lines.append(f"Result Count: {execution.get('result_count', 0)}")
|
|
743
|
+
if writeback:
|
|
744
|
+
lines.append(f"Writeback: attempted={writeback.get('attempted')} applied={writeback_applied} verified={write_verified}")
|
|
745
|
+
if executed and writeback_applied and status == "verification_failed":
|
|
746
|
+
lines.append("说明:代码块执行和回写请求已完成;当前结果只表示后置回读未能确认字段值,不等同于回写被拒绝。")
|
|
747
|
+
_append_warnings(lines, result.get("warnings"))
|
|
748
|
+
return "\n".join(lines) + "\n"
|
|
749
|
+
|
|
750
|
+
|
|
326
751
|
def _format_task_list(result: dict[str, Any]) -> str:
|
|
327
752
|
data = result.get("data") if isinstance(result.get("data"), dict) else {}
|
|
328
753
|
items = data.get("items") if isinstance(data.get("items"), list) else []
|
|
@@ -409,6 +834,50 @@ def _format_task_get(result: dict[str, Any]) -> str:
|
|
|
409
834
|
return "\n".join(lines) + "\n"
|
|
410
835
|
|
|
411
836
|
|
|
837
|
+
def _format_task_workflow_log(result: dict[str, Any]) -> str:
|
|
838
|
+
data = result.get("data") if isinstance(result.get("data"), dict) else {}
|
|
839
|
+
selection = data.get("selection") if isinstance(data.get("selection"), dict) else {}
|
|
840
|
+
visibility = data.get("visibility") if isinstance(data.get("visibility"), dict) else {}
|
|
841
|
+
items = data.get("items") if isinstance(data.get("items"), list) else []
|
|
842
|
+
lines: list[str] = []
|
|
843
|
+
if selection.get("task_id") not in (None, ""):
|
|
844
|
+
lines.append(f"Task ID: {selection.get('task_id')}")
|
|
845
|
+
locator = " / ".join(
|
|
846
|
+
str(value or "-")
|
|
847
|
+
for value in (
|
|
848
|
+
selection.get("app_key"),
|
|
849
|
+
selection.get("record_id"),
|
|
850
|
+
selection.get("workflow_node_id"),
|
|
851
|
+
)
|
|
852
|
+
)
|
|
853
|
+
if locator != "- / - / -":
|
|
854
|
+
lines.append(f"Locator: {locator}")
|
|
855
|
+
if visibility:
|
|
856
|
+
lines.append(
|
|
857
|
+
"Visibility: "
|
|
858
|
+
f"audit_record={visibility.get('audit_record_visible')} / "
|
|
859
|
+
f"qrobot_record={visibility.get('qrobot_record_visible')}"
|
|
860
|
+
)
|
|
861
|
+
lines.append(f"Workflow Logs: {len(items)}")
|
|
862
|
+
for item in items[:20]:
|
|
863
|
+
if not isinstance(item, dict):
|
|
864
|
+
continue
|
|
865
|
+
operator = item.get("operator") if isinstance(item.get("operator"), dict) else {}
|
|
866
|
+
operator_name = operator.get("name") or operator.get("email") or operator.get("uid") or "-"
|
|
867
|
+
parts = [
|
|
868
|
+
str(item.get("operation_time") or "-"),
|
|
869
|
+
str(item.get("node_name") or item.get("node_id") or "-"),
|
|
870
|
+
str(operator_name),
|
|
871
|
+
str(item.get("remark") or item.get("operation") or "-"),
|
|
872
|
+
]
|
|
873
|
+
lines.append("- " + " / ".join(parts))
|
|
874
|
+
if len(items) > 20:
|
|
875
|
+
lines.append(f"... {len(items) - 20} more")
|
|
876
|
+
_append_warnings(lines, result.get("warnings"))
|
|
877
|
+
_append_verification(lines, result.get("verification"))
|
|
878
|
+
return "\n".join(lines) + "\n"
|
|
879
|
+
|
|
880
|
+
|
|
412
881
|
def _format_task_action(result: dict[str, Any]) -> str:
|
|
413
882
|
data = result.get("data") if isinstance(result.get("data"), dict) else {}
|
|
414
883
|
action = str(data.get("action") or "").strip().lower()
|
|
@@ -525,6 +994,42 @@ def _format_import_verify(result: dict[str, Any]) -> str:
|
|
|
525
994
|
return "\n".join(lines) + "\n"
|
|
526
995
|
|
|
527
996
|
|
|
997
|
+
def _format_import_template(result: dict[str, Any]) -> str:
|
|
998
|
+
verification = result.get("verification") if isinstance(result.get("verification"), dict) else {}
|
|
999
|
+
template_source = verification.get("template_source")
|
|
1000
|
+
if not template_source:
|
|
1001
|
+
template_source = "official" if result.get("template_url") else ("local_generated" if result.get("downloaded_to_path") else "unknown")
|
|
1002
|
+
if result.get("downloaded_to_path"):
|
|
1003
|
+
title = "导入模板已生成" if template_source == "local_generated" else "导入模板已下载"
|
|
1004
|
+
elif result.get("template_url"):
|
|
1005
|
+
title = "导入模板已获取"
|
|
1006
|
+
else:
|
|
1007
|
+
title = "导入模板结果"
|
|
1008
|
+
|
|
1009
|
+
lines = [
|
|
1010
|
+
title,
|
|
1011
|
+
f"App Key: {result.get('app_key') or '-'}",
|
|
1012
|
+
f"Template Source: {template_source}",
|
|
1013
|
+
]
|
|
1014
|
+
if result.get("downloaded_to_path"):
|
|
1015
|
+
lines.append(f"Local Path: {result.get('downloaded_to_path')}")
|
|
1016
|
+
if result.get("template_url"):
|
|
1017
|
+
lines.append(f"Template URL: {result.get('template_url')}")
|
|
1018
|
+
import_capability = result.get("import_capability") if isinstance(result.get("import_capability"), dict) else {}
|
|
1019
|
+
if import_capability:
|
|
1020
|
+
lines.append(
|
|
1021
|
+
"Import Capability: "
|
|
1022
|
+
f"{import_capability.get('auth_source') or 'unknown'} / "
|
|
1023
|
+
f"can_import={import_capability.get('can_import')}"
|
|
1024
|
+
)
|
|
1025
|
+
expected_columns = result.get("expected_columns") if isinstance(result.get("expected_columns"), list) else []
|
|
1026
|
+
if expected_columns:
|
|
1027
|
+
lines.append(f"Columns: {len(expected_columns)}")
|
|
1028
|
+
_append_warnings(lines, result.get("warnings"))
|
|
1029
|
+
_append_verification(lines, result.get("verification"))
|
|
1030
|
+
return "\n".join(lines) + "\n"
|
|
1031
|
+
|
|
1032
|
+
|
|
528
1033
|
def _format_import_status(result: dict[str, Any]) -> str:
|
|
529
1034
|
lines = [
|
|
530
1035
|
f"Status: {result.get('status') or '-'}",
|
|
@@ -548,6 +1053,59 @@ def _format_import_status(result: dict[str, Any]) -> str:
|
|
|
548
1053
|
return "\n".join(lines) + "\n"
|
|
549
1054
|
|
|
550
1055
|
|
|
1056
|
+
def _format_import_repair(result: dict[str, Any]) -> str:
|
|
1057
|
+
lines = [
|
|
1058
|
+
f"Status: {result.get('status') or '-'}",
|
|
1059
|
+
f"Source File: {result.get('source_file_path') or '-'}",
|
|
1060
|
+
f"Repaired File: {result.get('repaired_file_path') or '-'}",
|
|
1061
|
+
f"Can Import After Repair: {result.get('can_import_after_repair')}",
|
|
1062
|
+
]
|
|
1063
|
+
verification_id = result.get("verification_id") or result.get("new_verification_id")
|
|
1064
|
+
if verification_id:
|
|
1065
|
+
lines.append(f"Verification ID: {verification_id}")
|
|
1066
|
+
applied_repairs = result.get("applied_repairs") if isinstance(result.get("applied_repairs"), list) else []
|
|
1067
|
+
skipped_repairs = result.get("skipped_repairs") if isinstance(result.get("skipped_repairs"), list) else []
|
|
1068
|
+
lines.append(f"Applied Repairs: {', '.join(map(str, applied_repairs)) if applied_repairs else '-'}")
|
|
1069
|
+
if skipped_repairs:
|
|
1070
|
+
lines.append(f"Skipped Repairs: {', '.join(map(str, skipped_repairs))}")
|
|
1071
|
+
issue_summary = result.get("post_repair_issue_summary") if isinstance(result.get("post_repair_issue_summary"), dict) else {}
|
|
1072
|
+
if issue_summary:
|
|
1073
|
+
lines.append(
|
|
1074
|
+
"Post Repair Issues: "
|
|
1075
|
+
f"total={issue_summary.get('total', 0)}, "
|
|
1076
|
+
f"errors={issue_summary.get('errors', 0)}, "
|
|
1077
|
+
f"warnings={issue_summary.get('warnings', 0)}"
|
|
1078
|
+
)
|
|
1079
|
+
_append_warnings(lines, result.get("warnings"))
|
|
1080
|
+
_append_verification(lines, result.get("verification"))
|
|
1081
|
+
return "\n".join(lines) + "\n"
|
|
1082
|
+
|
|
1083
|
+
|
|
1084
|
+
def _format_import_start(result: dict[str, Any]) -> str:
|
|
1085
|
+
lines = [
|
|
1086
|
+
f"Status: {result.get('status') or '-'}",
|
|
1087
|
+
f"Accepted: {result.get('accepted')}",
|
|
1088
|
+
f"Write Executed: {result.get('write_executed')}",
|
|
1089
|
+
f"Safe To Retry: {result.get('safe_to_retry')}",
|
|
1090
|
+
f"Import ID: {result.get('import_id') or '-'}",
|
|
1091
|
+
f"Process ID: {result.get('process_id_str') or '-'}",
|
|
1092
|
+
]
|
|
1093
|
+
if result.get("file_url"):
|
|
1094
|
+
lines.append(f"File URL: {result.get('file_url')}")
|
|
1095
|
+
for key, label in (
|
|
1096
|
+
("total", "Total Rows"),
|
|
1097
|
+
("finished", "Finished Rows"),
|
|
1098
|
+
("succeeded", "Succeeded Rows"),
|
|
1099
|
+
("failed", "Failed Rows"),
|
|
1100
|
+
):
|
|
1101
|
+
value = result.get(key)
|
|
1102
|
+
if value not in (None, ""):
|
|
1103
|
+
lines.append(f"{label}: {value}")
|
|
1104
|
+
_append_warnings(lines, result.get("warnings"))
|
|
1105
|
+
_append_verification(lines, result.get("verification"))
|
|
1106
|
+
return "\n".join(lines) + "\n"
|
|
1107
|
+
|
|
1108
|
+
|
|
551
1109
|
def _format_export_common(result: dict[str, Any], *, title: str | None = None) -> str:
|
|
552
1110
|
lines: list[str] = []
|
|
553
1111
|
if title:
|
|
@@ -617,18 +1175,82 @@ def _format_export_direct(result: dict[str, Any]) -> str:
|
|
|
617
1175
|
|
|
618
1176
|
def _format_builder_summary(result: dict[str, Any]) -> str:
|
|
619
1177
|
lines = []
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
1178
|
+
emitted: set[str] = set()
|
|
1179
|
+
for key, label in (
|
|
1180
|
+
("status", "Status"),
|
|
1181
|
+
("message", "Message"),
|
|
1182
|
+
("app_key", "App Key"),
|
|
1183
|
+
("dash_key", "Dash Key"),
|
|
1184
|
+
("view_key", "View Key"),
|
|
1185
|
+
("chart_id", "Chart ID"),
|
|
1186
|
+
("package_id", "Package ID"),
|
|
1187
|
+
("verified", "Verified"),
|
|
1188
|
+
("write_executed", "Write Executed"),
|
|
1189
|
+
("delete_executed", "Delete Executed"),
|
|
1190
|
+
("safe_to_retry", "Safe To Retry"),
|
|
1191
|
+
):
|
|
1192
|
+
if key in result and result.get(key) not in (None, ""):
|
|
1193
|
+
lines.append(f"{label}: {result.get(key)}")
|
|
1194
|
+
emitted.add(key)
|
|
1195
|
+
for key, value in result.items():
|
|
1196
|
+
if key in emitted or key in {"data", "warnings", "verification", "details", "result", "raw"}:
|
|
1197
|
+
continue
|
|
1198
|
+
if isinstance(value, (str, int, float, bool)) or value is None:
|
|
1199
|
+
lines.append(f"{key}: {value}")
|
|
628
1200
|
data = result.get("data")
|
|
629
1201
|
if isinstance(data, dict):
|
|
630
1202
|
scalar_lines = _dict_scalar_lines(data)
|
|
631
|
-
|
|
1203
|
+
if scalar_lines:
|
|
1204
|
+
lines.append("Data:")
|
|
1205
|
+
lines.extend(f"- {line}" for line in scalar_lines[:12])
|
|
1206
|
+
for key in (
|
|
1207
|
+
"items",
|
|
1208
|
+
"apps",
|
|
1209
|
+
"fields",
|
|
1210
|
+
"sections",
|
|
1211
|
+
"components",
|
|
1212
|
+
"questions",
|
|
1213
|
+
"views",
|
|
1214
|
+
"charts",
|
|
1215
|
+
"buttons",
|
|
1216
|
+
"associations",
|
|
1217
|
+
"package_ids",
|
|
1218
|
+
"app_keys",
|
|
1219
|
+
):
|
|
1220
|
+
value = result.get(key)
|
|
1221
|
+
if not isinstance(value, list) and isinstance(data, dict):
|
|
1222
|
+
value = data.get(key)
|
|
1223
|
+
if isinstance(value, list):
|
|
1224
|
+
lines.append(f"{key}: {len(value)}")
|
|
1225
|
+
for item in value[:8]:
|
|
1226
|
+
lines.append("- " + _builder_item_summary(item))
|
|
1227
|
+
if len(value) > 8:
|
|
1228
|
+
lines.append(f"... {len(value) - 8} more")
|
|
1229
|
+
components = result.get("components") if isinstance(result.get("components"), list) else []
|
|
1230
|
+
if components:
|
|
1231
|
+
lines.append("Component Refs:")
|
|
1232
|
+
for item in components[:8]:
|
|
1233
|
+
if not isinstance(item, dict):
|
|
1234
|
+
continue
|
|
1235
|
+
chart_ref = item.get("chart_ref") if isinstance(item.get("chart_ref"), dict) else {}
|
|
1236
|
+
view_ref = item.get("view_ref") if isinstance(item.get("view_ref"), dict) else {}
|
|
1237
|
+
source_type = item.get("source_type") or item.get("type") or "unknown"
|
|
1238
|
+
title = item.get("title")
|
|
1239
|
+
if chart_ref:
|
|
1240
|
+
lines.append(f"- {source_type}: {title or chart_ref.get('chart_name') or '-'} / chart_id={chart_ref.get('chart_id') or '-'}")
|
|
1241
|
+
elif view_ref:
|
|
1242
|
+
lines.append(
|
|
1243
|
+
f"- {source_type}: {title or view_ref.get('view_name') or '-'} / "
|
|
1244
|
+
f"view_key={view_ref.get('view_key') or '-'} / app_key={view_ref.get('app_key') or '-'}"
|
|
1245
|
+
)
|
|
1246
|
+
else:
|
|
1247
|
+
lines.append(f"- {source_type}: {title or '-'}")
|
|
1248
|
+
if isinstance(result.get("base"), dict):
|
|
1249
|
+
lines.append(f"Base Keys: {', '.join(str(key) for key in list(result['base'].keys())[:12])}")
|
|
1250
|
+
if isinstance(result.get("config"), dict):
|
|
1251
|
+
lines.append(f"Config Keys: {', '.join(str(key) for key in list(result['config'].keys())[:12])}")
|
|
1252
|
+
if isinstance(result.get("visibility"), dict):
|
|
1253
|
+
lines.append(f"Visibility Keys: {', '.join(str(key) for key in list(result['visibility'].keys())[:12])}")
|
|
632
1254
|
_append_warnings(lines, result.get("warnings"))
|
|
633
1255
|
_append_verification(lines, result.get("verification"))
|
|
634
1256
|
if not lines:
|
|
@@ -636,6 +1258,135 @@ def _format_builder_summary(result: dict[str, Any]) -> str:
|
|
|
636
1258
|
return "\n".join(lines) + "\n"
|
|
637
1259
|
|
|
638
1260
|
|
|
1261
|
+
def _builder_item_summary(item: Any) -> str:
|
|
1262
|
+
if not isinstance(item, dict):
|
|
1263
|
+
return str(item)
|
|
1264
|
+
preferred_keys = (
|
|
1265
|
+
"app_key",
|
|
1266
|
+
"appKey",
|
|
1267
|
+
"app_name",
|
|
1268
|
+
"appName",
|
|
1269
|
+
"package_id",
|
|
1270
|
+
"packageId",
|
|
1271
|
+
"package_name",
|
|
1272
|
+
"packageName",
|
|
1273
|
+
"dash_key",
|
|
1274
|
+
"dashKey",
|
|
1275
|
+
"dash_name",
|
|
1276
|
+
"dashName",
|
|
1277
|
+
"view_key",
|
|
1278
|
+
"viewKey",
|
|
1279
|
+
"view_name",
|
|
1280
|
+
"viewName",
|
|
1281
|
+
"chart_id",
|
|
1282
|
+
"chartId",
|
|
1283
|
+
"chart_name",
|
|
1284
|
+
"chartName",
|
|
1285
|
+
"field_id",
|
|
1286
|
+
"queId",
|
|
1287
|
+
"title",
|
|
1288
|
+
"queTitle",
|
|
1289
|
+
"name",
|
|
1290
|
+
"role_id",
|
|
1291
|
+
"roleId",
|
|
1292
|
+
"role_name",
|
|
1293
|
+
"roleName",
|
|
1294
|
+
"uid",
|
|
1295
|
+
"userId",
|
|
1296
|
+
"email",
|
|
1297
|
+
)
|
|
1298
|
+
parts = [
|
|
1299
|
+
f"{key}={item.get(key)}"
|
|
1300
|
+
for key in preferred_keys
|
|
1301
|
+
if item.get(key) not in (None, "", [], {})
|
|
1302
|
+
]
|
|
1303
|
+
if not parts:
|
|
1304
|
+
parts = _dict_scalar_lines(item)[:6]
|
|
1305
|
+
return " / ".join(parts) if parts else json.dumps(item, ensure_ascii=False)
|
|
1306
|
+
|
|
1307
|
+
|
|
1308
|
+
def _format_file_upload_local(result: dict[str, Any]) -> str:
|
|
1309
|
+
lines = [
|
|
1310
|
+
f"Upload Kind: {result.get('upload_kind') or result.get('requested_upload_kind') or '-'}",
|
|
1311
|
+
f"Effective Upload Kind: {result.get('effective_upload_kind') or result.get('upload_kind') or '-'}",
|
|
1312
|
+
]
|
|
1313
|
+
if "upload_fallback_applied" in result:
|
|
1314
|
+
lines.append(f"Fallback Applied: {result.get('upload_fallback_applied')}")
|
|
1315
|
+
if result.get("upload_fallback_reason"):
|
|
1316
|
+
lines.append(f"Fallback Reason: {result.get('upload_fallback_reason')}")
|
|
1317
|
+
if result.get("file_name"):
|
|
1318
|
+
lines.append(f"File: {result.get('file_name')}")
|
|
1319
|
+
if result.get("file_size") is not None:
|
|
1320
|
+
lines.append(f"Size: {result.get('file_size')}")
|
|
1321
|
+
if result.get("content_type"):
|
|
1322
|
+
lines.append(f"Content Type: {result.get('content_type')}")
|
|
1323
|
+
if result.get("upload_protocol"):
|
|
1324
|
+
lines.append(f"Protocol: {result.get('upload_protocol')}")
|
|
1325
|
+
if result.get("download_url"):
|
|
1326
|
+
lines.append(f"Download URL: {result.get('download_url')}")
|
|
1327
|
+
|
|
1328
|
+
attachment_value = result.get("attachment_value") if isinstance(result.get("attachment_value"), dict) else {}
|
|
1329
|
+
if attachment_value:
|
|
1330
|
+
lines.append("Attachment Value:")
|
|
1331
|
+
for key in ("value", "name", "otherInfo"):
|
|
1332
|
+
value = attachment_value.get(key)
|
|
1333
|
+
if value not in (None, ""):
|
|
1334
|
+
lines.append(f"- {key}: {value}")
|
|
1335
|
+
|
|
1336
|
+
comment_file_info = result.get("comment_file_info") if isinstance(result.get("comment_file_info"), dict) else {}
|
|
1337
|
+
if comment_file_info:
|
|
1338
|
+
lines.append("Comment File:")
|
|
1339
|
+
for key in ("url", "name", "uploadFileSize"):
|
|
1340
|
+
value = comment_file_info.get(key)
|
|
1341
|
+
if value not in (None, ""):
|
|
1342
|
+
lines.append(f"- {key}: {value}")
|
|
1343
|
+
|
|
1344
|
+
_append_warnings(lines, result.get("warnings"))
|
|
1345
|
+
_append_verification(lines, result.get("verification"))
|
|
1346
|
+
return "\n".join(lines) + "\n"
|
|
1347
|
+
|
|
1348
|
+
|
|
1349
|
+
def _format_feedback_submit(result: dict[str, Any]) -> str:
|
|
1350
|
+
submitted = bool(result.get("ok") is True or result.get("feedback_request_id") or result.get("submission_mode"))
|
|
1351
|
+
lines = [
|
|
1352
|
+
"Feedback Submitted" if submitted else "Feedback Result",
|
|
1353
|
+
f"Submission Mode: {result.get('submission_mode') or '-'}",
|
|
1354
|
+
f"Request ID: {result.get('feedback_request_id') or '-'}",
|
|
1355
|
+
]
|
|
1356
|
+
target = result.get("feedback_target") if isinstance(result.get("feedback_target"), dict) else {}
|
|
1357
|
+
if target:
|
|
1358
|
+
lines.append(
|
|
1359
|
+
"Target: "
|
|
1360
|
+
f"app_key={target.get('app_key') or '-'} / "
|
|
1361
|
+
f"mcp_side={target.get('mcp_side') or '-'}"
|
|
1362
|
+
)
|
|
1363
|
+
payload = result.get("submission_summary") if isinstance(result.get("submission_summary"), dict) else {}
|
|
1364
|
+
if not payload:
|
|
1365
|
+
payload = result.get("normalized_payload") if isinstance(result.get("normalized_payload"), dict) else {}
|
|
1366
|
+
if payload:
|
|
1367
|
+
for key, label in (
|
|
1368
|
+
("category", "Category"),
|
|
1369
|
+
("反馈类型", "Category"),
|
|
1370
|
+
("title", "Title"),
|
|
1371
|
+
("tool_name", "Tool"),
|
|
1372
|
+
("关联工具", "Tool"),
|
|
1373
|
+
("app_key", "App Key"),
|
|
1374
|
+
("关联应用", "App Key"),
|
|
1375
|
+
("record_id", "Record ID"),
|
|
1376
|
+
("关联记录", "Record ID"),
|
|
1377
|
+
("workflow_node_id", "Workflow Node ID"),
|
|
1378
|
+
("关联节点", "Workflow Node ID"),
|
|
1379
|
+
("impact_scope", "Impact Scope"),
|
|
1380
|
+
("影响范围", "Impact Scope"),
|
|
1381
|
+
):
|
|
1382
|
+
value = payload.get(key)
|
|
1383
|
+
if value not in (None, ""):
|
|
1384
|
+
lines.append(f"{label}: {value}")
|
|
1385
|
+
_append_warnings(lines, result.get("warnings"))
|
|
1386
|
+
_append_verification(lines, result.get("verification"))
|
|
1387
|
+
return "\n".join(lines) + "\n"
|
|
1388
|
+
|
|
1389
|
+
|
|
639
1390
|
def emit_json_result(result: dict[str, Any], *, stream: TextIO) -> None:
|
|
640
1391
|
json.dump(result, stream, ensure_ascii=False, indent=2)
|
|
641
1392
|
stream.write("\n")
|
|
@@ -721,21 +1472,23 @@ def _task_action_failure_label(action: str) -> str:
|
|
|
721
1472
|
|
|
722
1473
|
|
|
723
1474
|
def _task_action_partial_success_message(result: dict[str, Any]) -> str:
|
|
724
|
-
error_code = str(result.get("error_code") or "").strip().upper()
|
|
725
|
-
if error_code == "WORKFLOW_CONTINUATION_UNVERIFIED":
|
|
726
|
-
return "动作已提交,但暂未完成后续流程验证。可使用 --json 查看详细信息。"
|
|
727
|
-
if error_code == "TASK_ALREADY_PROCESSED":
|
|
728
|
-
return "当前待办已不可操作,系统判断流程可能已被其他人处理。可使用 --json 查看详细信息。"
|
|
729
1475
|
warnings = result.get("warnings")
|
|
730
1476
|
if isinstance(warnings, list):
|
|
731
1477
|
for warning in warnings:
|
|
732
1478
|
if not isinstance(warning, dict):
|
|
733
1479
|
continue
|
|
734
1480
|
code = str(warning.get("code") or "").strip().upper()
|
|
1481
|
+
if code == "TASK_ACTION_VERIFICATION_PERMISSION_UNAVAILABLE":
|
|
1482
|
+
return "动作已提交;后置验证读取受当前权限限制,不能据此判断动作被拒绝。可使用 --json 查看详细信息。"
|
|
735
1483
|
if code == "TASK_ALREADY_PROCESSED_UNCONFIRMED_ACTOR":
|
|
736
1484
|
return "当前待办已不可操作,系统判断流程可能已被其他人处理。可使用 --json 查看详细信息。"
|
|
737
1485
|
if code == "WORKFLOW_CONTINUATION_UNVERIFIED":
|
|
738
1486
|
return "动作已提交,但暂未完成后续流程验证。可使用 --json 查看详细信息。"
|
|
1487
|
+
error_code = str(result.get("error_code") or "").strip().upper()
|
|
1488
|
+
if error_code == "WORKFLOW_CONTINUATION_UNVERIFIED":
|
|
1489
|
+
return "动作已提交,但暂未完成后续流程验证。可使用 --json 查看详细信息。"
|
|
1490
|
+
if error_code == "TASK_ALREADY_PROCESSED":
|
|
1491
|
+
return "当前待办已不可操作,系统判断流程可能已被其他人处理。可使用 --json 查看详细信息。"
|
|
739
1492
|
return "动作已提交,但结果验证不完整。可使用 --json 查看详细信息。"
|
|
740
1493
|
|
|
741
1494
|
|
|
@@ -820,20 +1573,35 @@ _FORMATTERS = {
|
|
|
820
1573
|
"workspace_select": _format_workspace_select,
|
|
821
1574
|
"app_list": _format_app_items,
|
|
822
1575
|
"app_get": _format_app_get,
|
|
1576
|
+
"portal_list": _format_portal_list,
|
|
1577
|
+
"portal_get": _format_portal_get,
|
|
1578
|
+
"view_get": _format_view_get,
|
|
1579
|
+
"chart_get": _format_chart_get,
|
|
823
1580
|
"record_list": _format_record_list,
|
|
824
1581
|
"record_access": _format_record_access,
|
|
1582
|
+
"record_candidates": _format_record_candidates,
|
|
1583
|
+
"record_schema": _format_record_schema,
|
|
825
1584
|
"record_get": _format_record_get,
|
|
826
1585
|
"record_logs": _format_record_logs,
|
|
1586
|
+
"record_write": _format_record_write,
|
|
1587
|
+
"record_delete": _format_record_delete,
|
|
1588
|
+
"code_block_run": _format_code_block_run,
|
|
827
1589
|
"task_list": _format_task_list,
|
|
828
1590
|
"task_workbench": _format_task_workbench,
|
|
829
1591
|
"task_get": _format_task_get,
|
|
1592
|
+
"task_workflow_log_get": _format_task_workflow_log,
|
|
830
1593
|
"task_action_execute": _format_task_action,
|
|
831
1594
|
"task_associated_report_detail_get": _format_task_associated_report_detail,
|
|
1595
|
+
"import_template": _format_import_template,
|
|
832
1596
|
"import_verify": _format_import_verify,
|
|
1597
|
+
"import_repair": _format_import_repair,
|
|
1598
|
+
"import_start": _format_import_start,
|
|
833
1599
|
"import_status": _format_import_status,
|
|
834
1600
|
"export_start": _format_export_start,
|
|
835
1601
|
"export_status": _format_export_status,
|
|
836
1602
|
"export_get": _format_export_get,
|
|
837
1603
|
"export_direct": _format_export_direct,
|
|
838
1604
|
"builder_summary": _format_builder_summary,
|
|
1605
|
+
"file_upload_local": _format_file_upload_local,
|
|
1606
|
+
"feedback_submit": _format_feedback_submit,
|
|
839
1607
|
}
|