@josephyan/qingflow-app-builder-mcp 1.1.25 → 1.1.26
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 +5 -6
- package/docs/local-agent-install.md +6 -57
- package/entry_point.py +1 -1
- package/npm/bin/qingflow-app-builder-mcp.mjs +33 -2
- package/npm/lib/runtime.mjs +101 -21
- package/npm/scripts/postinstall.mjs +10 -1
- package/package.json +2 -3
- package/pyproject.toml +1 -1
- package/skills/qingflow-app-builder/SKILL.md +42 -104
- package/skills/qingflow-app-builder/references/build-complete-system.md +428 -0
- package/skills/qingflow-app-builder/references/build-single-app.md +530 -0
- package/skills/qingflow-app-builder/references/create-app.md +11 -30
- package/skills/qingflow-app-builder/references/environments.md +1 -1
- package/skills/qingflow-app-builder/references/gotchas.md +9 -31
- package/skills/qingflow-app-builder/references/match-rules.md +0 -15
- package/skills/qingflow-app-builder/references/solution-playbooks.md +0 -10
- package/skills/qingflow-app-builder/references/tool-selection.md +23 -38
- package/skills/qingflow-app-builder/references/update-flow.md +38 -22
- package/skills/qingflow-app-builder/references/update-schema.md +0 -14
- package/skills/qingflow-app-builder/references/update-views.md +14 -83
- package/skills/qingflow-app-builder-code-integrations/SKILL.md +3 -5
- package/skills/qingflow-app-builder-code-integrations/references/code-block.md +1 -1
- package/skills/qingflow-app-builder-code-integrations/references/q-linker.md +1 -1
- package/skills/qingflow-mcp-setup/SKILL.md +111 -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 +61 -0
- package/skills/qingflow-mcp-setup/references/generic-stdio.md +31 -0
- package/skills/qingflow-mcp-setup/scripts/check_local_server.sh +38 -0
- package/skills/qingflow-workflow-builder/SKILL.md +96 -0
- package/skills/qingflow-workflow-builder/manifest.yaml +8 -0
- package/skills/qingflow-workflow-builder/references/01-overview.md +45 -0
- package/skills/qingflow-workflow-builder/references/02-update-mode.md +53 -0
- package/skills/qingflow-workflow-builder/references/03-flow-patterns.md +57 -0
- package/skills/qingflow-workflow-builder/references/04-stage1-business-modeling.md +131 -0
- package/skills/qingflow-workflow-builder/references/05-stage2-members-roles.md +29 -0
- package/skills/qingflow-workflow-builder/references/06-stage3-build-spec.md +165 -0
- package/skills/qingflow-workflow-builder/references/07-stage4-validate-spec.md +33 -0
- package/skills/qingflow-workflow-builder/references/08-stage5-apply-verify.md +51 -0
- package/skills/qingflow-workflow-builder/references/09-stage6-summary.md +88 -0
- package/skills/qingflow-workflow-builder/references/10-node-config-reference.md +93 -0
- package/skills/qingflow-workflow-builder/references/11-troubleshooting.md +15 -0
- package/skills/qingflow-workflow-builder/scripts/diff_flow_spec.py +275 -0
- package/skills/qingflow-workflow-builder/scripts/validate_flow_spec.py +605 -0
- package/src/qingflow_mcp/__init__.py +1 -1
- package/src/qingflow_mcp/__main__.py +2 -6
- package/src/qingflow_mcp/builder_facade/models.py +108 -485
- package/src/qingflow_mcp/builder_facade/service.py +1686 -6674
- package/src/qingflow_mcp/cli/commands/builder.py +364 -322
- package/src/qingflow_mcp/cli/commands/chart.py +1 -1
- package/src/qingflow_mcp/cli/commands/common.py +3 -12
- 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 +27 -101
- package/src/qingflow_mcp/cli/commands/task.py +47 -28
- package/src/qingflow_mcp/cli/commands/view.py +1 -1
- package/src/qingflow_mcp/cli/context.py +3 -0
- package/src/qingflow_mcp/cli/formatters.py +16 -784
- package/src/qingflow_mcp/cli/main.py +33 -117
- package/src/qingflow_mcp/errors.py +2 -43
- package/src/qingflow_mcp/public_surface.py +17 -26
- package/src/qingflow_mcp/response_trim.py +17 -81
- package/src/qingflow_mcp/server.py +12 -14
- package/src/qingflow_mcp/server_app_builder.py +36 -79
- package/src/qingflow_mcp/server_app_user.py +16 -22
- package/src/qingflow_mcp/session_store.py +7 -11
- package/src/qingflow_mcp/solution/compiler/__init__.py +1 -3
- package/src/qingflow_mcp/solution/executor.py +18 -245
- package/src/qingflow_mcp/tools/ai_builder_tools.py +1396 -3103
- package/src/qingflow_mcp/tools/app_tools.py +43 -184
- package/src/qingflow_mcp/tools/approval_tools.py +35 -197
- package/src/qingflow_mcp/tools/auth_tools.py +16 -92
- package/src/qingflow_mcp/tools/code_block_tools.py +40 -298
- package/src/qingflow_mcp/tools/custom_button_tools.py +10 -64
- package/src/qingflow_mcp/tools/directory_tools.py +72 -236
- package/src/qingflow_mcp/tools/export_tools.py +34 -244
- package/src/qingflow_mcp/tools/feedback_tools.py +0 -9
- package/src/qingflow_mcp/tools/file_tools.py +3 -9
- package/src/qingflow_mcp/tools/import_tools.py +49 -336
- package/src/qingflow_mcp/tools/navigation_tools.py +12 -91
- package/src/qingflow_mcp/tools/package_tools.py +6 -118
- package/src/qingflow_mcp/tools/portal_tools.py +3 -39
- package/src/qingflow_mcp/tools/qingbi_report_tools.py +7 -116
- package/src/qingflow_mcp/tools/record_tools.py +360 -1145
- package/src/qingflow_mcp/tools/resource_read_tools.py +39 -188
- package/src/qingflow_mcp/tools/role_tools.py +9 -80
- package/src/qingflow_mcp/tools/solution_tools.py +45 -59
- package/src/qingflow_mcp/tools/task_context_tools.py +158 -662
- package/src/qingflow_mcp/tools/task_tools.py +29 -113
- package/src/qingflow_mcp/tools/view_tools.py +3 -106
- package/src/qingflow_mcp/tools/workflow_tools.py +4 -48
- package/src/qingflow_mcp/tools/workspace_tools.py +3 -71
- package/npm/bin/qingflow-skills.mjs +0 -5
- package/skills/qingflow-app-builder/references/complete-system-development-guide.md +0 -172
- package/skills/qingflow-app-builder/references/single-app-development-guide.md +0 -87
- package/skills/qingflow-app-builder/scripts/validate_system_build_summary.py +0 -124
- package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +0 -173
|
@@ -7,7 +7,7 @@ from mcp.server.fastmcp import FastMCP
|
|
|
7
7
|
|
|
8
8
|
from ..backend_client import BackendRequestContext
|
|
9
9
|
from ..config import DEFAULT_PROFILE
|
|
10
|
-
from ..errors import QingflowApiError,
|
|
10
|
+
from ..errors import QingflowApiError, raise_tool_error
|
|
11
11
|
from ..json_types import JSONObject
|
|
12
12
|
from ..list_type_labels import SYSTEM_VIEW_DEFINITIONS, get_app_publish_status_label
|
|
13
13
|
from ..solution.compiler.icon_utils import workspace_icon_config
|
|
@@ -132,20 +132,6 @@ class AppTools(ToolBase):
|
|
|
132
132
|
matched.append(item)
|
|
133
133
|
return matched
|
|
134
134
|
|
|
135
|
-
def _resolve_visible_app(self, context: BackendRequestContext, app_key: str) -> JSONObject | None:
|
|
136
|
-
"""Resolve app display metadata from the current user's visible app tree."""
|
|
137
|
-
try:
|
|
138
|
-
result = self.backend.request("GET", context, "/tag/apps")
|
|
139
|
-
except QingflowApiError as exc:
|
|
140
|
-
if not _is_optional_app_metadata_error(exc):
|
|
141
|
-
raise
|
|
142
|
-
return None
|
|
143
|
-
items, _source_shape = self._extract_visible_apps(result)
|
|
144
|
-
for item in items:
|
|
145
|
-
if str(item.get("app_key") or "").strip() == app_key:
|
|
146
|
-
return item
|
|
147
|
-
return None
|
|
148
|
-
|
|
149
135
|
@tool_cn_name("应用搜索")
|
|
150
136
|
def app_search(self, *, profile: str, keyword: str = "", page_num: int = 1, page_size: int = 50) -> JSONObject:
|
|
151
137
|
"""Search apps by keyword in name/title using backend search API.
|
|
@@ -155,50 +141,9 @@ class AppTools(ToolBase):
|
|
|
155
141
|
params: JSONObject = {"pageNum": page_num, "pageSize": page_size}
|
|
156
142
|
if keyword:
|
|
157
143
|
params["queryKey"] = keyword
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
try:
|
|
162
|
-
result = self.backend.request("GET", context, "/app/item", params=params)
|
|
163
|
-
except QingflowApiError as exc:
|
|
164
|
-
if is_auth_like_error(exc):
|
|
165
|
-
raise
|
|
166
|
-
if not _is_optional_app_metadata_error(exc):
|
|
167
|
-
raise
|
|
168
|
-
visible_payload = self.backend.request("GET", context, "/tag/apps")
|
|
169
|
-
all_apps, source_shape = self._extract_visible_apps(visible_payload)
|
|
170
|
-
matched_apps = self._filter_visible_apps(all_apps, keyword) if keyword else all_apps
|
|
171
|
-
page_start = max(page_num - 1, 0) * page_size
|
|
172
|
-
page_end = page_start + page_size
|
|
173
|
-
apps = matched_apps[page_start:page_end]
|
|
174
|
-
warnings.append(
|
|
175
|
-
{
|
|
176
|
-
"code": "APP_SEARCH_FALLBACK_VISIBLE_APPS",
|
|
177
|
-
"message": (
|
|
178
|
-
"app_search used the current user's visible app tree because the global app search "
|
|
179
|
-
"endpoint is unavailable in this permission context."
|
|
180
|
-
),
|
|
181
|
-
"backend_code": exc.backend_code,
|
|
182
|
-
"http_status": exc.http_status,
|
|
183
|
-
"request_id": exc.request_id,
|
|
184
|
-
}
|
|
185
|
-
)
|
|
186
|
-
return {
|
|
187
|
-
"profile": profile,
|
|
188
|
-
"ws_id": session_profile.selected_ws_id,
|
|
189
|
-
"keyword": keyword,
|
|
190
|
-
"page_num": page_num,
|
|
191
|
-
"page_size": page_size,
|
|
192
|
-
"total": len(matched_apps),
|
|
193
|
-
"items": apps,
|
|
194
|
-
"apps": apps,
|
|
195
|
-
"source": "visible_apps_fallback",
|
|
196
|
-
"source_shape": source_shape,
|
|
197
|
-
"unfiltered_count": len(all_apps),
|
|
198
|
-
"matched_count": len(matched_apps),
|
|
199
|
-
"warnings": warnings,
|
|
200
|
-
}
|
|
201
|
-
|
|
144
|
+
|
|
145
|
+
result = self.backend.request("GET", context, "/app/item", params=params)
|
|
146
|
+
|
|
202
147
|
apps = []
|
|
203
148
|
if isinstance(result, dict):
|
|
204
149
|
items = result.get("list", [])
|
|
@@ -223,7 +168,6 @@ class AppTools(ToolBase):
|
|
|
223
168
|
"total": result.get("total") if isinstance(result, dict) else len(apps),
|
|
224
169
|
"items": apps,
|
|
225
170
|
"apps": apps,
|
|
226
|
-
"source": source,
|
|
227
171
|
}
|
|
228
172
|
|
|
229
173
|
return self._run(profile, runner)
|
|
@@ -238,7 +182,6 @@ class AppTools(ToolBase):
|
|
|
238
182
|
app_name = app_key
|
|
239
183
|
base_info: JSONObject | None = None
|
|
240
184
|
app_icon = None
|
|
241
|
-
visible_app: JSONObject | None = None
|
|
242
185
|
|
|
243
186
|
try:
|
|
244
187
|
base_info = self.backend.request("GET", context, f"/app/{app_key}/baseInfo")
|
|
@@ -249,31 +192,31 @@ class AppTools(ToolBase):
|
|
|
249
192
|
)
|
|
250
193
|
app_icon = str(base_info.get("appIcon") or "").strip() or None
|
|
251
194
|
except QingflowApiError as exc:
|
|
252
|
-
if not
|
|
195
|
+
if exc.backend_code not in {40002, 40027}:
|
|
253
196
|
raise
|
|
254
|
-
visible_app = self._resolve_visible_app(context, app_key)
|
|
255
|
-
if visible_app is not None:
|
|
256
|
-
app_name = str(visible_app.get("app_name") or visible_app.get("title") or app_key).strip() or app_key
|
|
257
|
-
app_icon = str(visible_app.get("app_icon") or "").strip() or None
|
|
258
197
|
warnings.append(
|
|
259
|
-
|
|
260
|
-
{
|
|
198
|
+
{
|
|
261
199
|
"code": "APP_BASE_INFO_UNAVAILABLE",
|
|
200
|
+
"message": f"app_get could not load base info for {app_key}; using app_key as app_name.",
|
|
201
|
+
}
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
try:
|
|
205
|
+
can_create = self._probe_create_access(context, app_key)
|
|
206
|
+
except QingflowApiError as exc:
|
|
207
|
+
can_create = False
|
|
208
|
+
warnings.append(
|
|
209
|
+
{
|
|
210
|
+
"code": "APP_CREATE_PROBE_UNVERIFIED",
|
|
262
211
|
"message": (
|
|
263
|
-
f"app_get could not
|
|
264
|
-
"
|
|
212
|
+
f"app_get could not fully verify create access for {app_key}: "
|
|
213
|
+
f"{exc.message or 'probe failed'}"
|
|
265
214
|
),
|
|
266
|
-
|
|
267
|
-
exc,
|
|
268
|
-
)
|
|
215
|
+
}
|
|
269
216
|
)
|
|
270
|
-
|
|
271
|
-
can_create = self._probe_create_access(context, app_key)
|
|
272
217
|
accessible_views, system_view_warnings = self._resolve_accessible_system_views(context, app_key)
|
|
273
218
|
warnings.extend(system_view_warnings)
|
|
274
|
-
|
|
275
|
-
warnings.extend(custom_view_warnings)
|
|
276
|
-
accessible_views.extend(custom_views)
|
|
219
|
+
accessible_views.extend(self._resolve_accessible_custom_views(context, app_key))
|
|
277
220
|
import_capability, import_warnings = _derive_import_capability(base_info)
|
|
278
221
|
warnings.extend(import_warnings)
|
|
279
222
|
return {
|
|
@@ -287,7 +230,6 @@ class AppTools(ToolBase):
|
|
|
287
230
|
"app_name": app_name,
|
|
288
231
|
"app_icon": app_icon,
|
|
289
232
|
"icon_config": workspace_icon_config(app_icon),
|
|
290
|
-
**({"visible_app": visible_app} if base_info is None and visible_app is not None else {}),
|
|
291
233
|
"can_create": can_create,
|
|
292
234
|
"import_capability": import_capability,
|
|
293
235
|
"accessible_views": accessible_views,
|
|
@@ -302,30 +244,7 @@ class AppTools(ToolBase):
|
|
|
302
244
|
self._require_app_key(app_key)
|
|
303
245
|
|
|
304
246
|
def runner(session_profile, context):
|
|
305
|
-
|
|
306
|
-
verification: JSONObject = {"base_info_verified": True, "fallback_visible_app_used": False}
|
|
307
|
-
try:
|
|
308
|
-
result = self.backend.request("GET", context, f"/app/{app_key}/baseInfo")
|
|
309
|
-
except QingflowApiError as exc:
|
|
310
|
-
if not _is_optional_app_metadata_error(exc):
|
|
311
|
-
raise
|
|
312
|
-
visible_app = self._resolve_visible_app(context, app_key)
|
|
313
|
-
if visible_app is None:
|
|
314
|
-
raise
|
|
315
|
-
result = self._base_info_from_visible_app(app_key, visible_app)
|
|
316
|
-
warnings.append(
|
|
317
|
-
_with_api_error_fields(
|
|
318
|
-
{
|
|
319
|
-
"code": "APP_BASE_INFO_UNAVAILABLE",
|
|
320
|
-
"message": (
|
|
321
|
-
f"app_get_base could not load base info for {app_key}; "
|
|
322
|
-
"using the current user's visible app tree."
|
|
323
|
-
),
|
|
324
|
-
},
|
|
325
|
-
exc,
|
|
326
|
-
)
|
|
327
|
-
)
|
|
328
|
-
verification = {"base_info_verified": False, "fallback_visible_app_used": True}
|
|
247
|
+
result = self.backend.request("GET", context, f"/app/{app_key}/baseInfo")
|
|
329
248
|
publish_status = result.get("appPublishStatus") if isinstance(result, dict) else None
|
|
330
249
|
compact = self._compact_base_info(result if isinstance(result, dict) else {})
|
|
331
250
|
response = {
|
|
@@ -336,10 +255,7 @@ class AppTools(ToolBase):
|
|
|
336
255
|
"app_publish_status": publish_status,
|
|
337
256
|
"app_publish_status_label": get_app_publish_status_label(publish_status if isinstance(publish_status, int) else None),
|
|
338
257
|
"compact": not include_raw,
|
|
339
|
-
"verification": verification,
|
|
340
258
|
}
|
|
341
|
-
if warnings:
|
|
342
|
-
response["warnings"] = warnings
|
|
343
259
|
if include_raw:
|
|
344
260
|
response["summary"] = compact
|
|
345
261
|
return response
|
|
@@ -573,9 +489,7 @@ class AppTools(ToolBase):
|
|
|
573
489
|
)
|
|
574
490
|
return True
|
|
575
491
|
except QingflowApiError as exc:
|
|
576
|
-
if
|
|
577
|
-
raise
|
|
578
|
-
if backend_code_int(exc) in {40002, 40027, 404} or exc.http_status == 404:
|
|
492
|
+
if exc.backend_code in {40002, 40027}:
|
|
579
493
|
return False
|
|
580
494
|
raise
|
|
581
495
|
|
|
@@ -590,9 +504,7 @@ class AppTools(ToolBase):
|
|
|
590
504
|
)
|
|
591
505
|
return True
|
|
592
506
|
except QingflowApiError as exc:
|
|
593
|
-
if
|
|
594
|
-
raise
|
|
595
|
-
if backend_code_int(exc) in {40002, 40027, 404} or exc.http_status == 404:
|
|
507
|
+
if exc.backend_code in {40002, 40027}:
|
|
596
508
|
return False
|
|
597
509
|
raise
|
|
598
510
|
|
|
@@ -604,42 +516,40 @@ class AppTools(ToolBase):
|
|
|
604
516
|
try:
|
|
605
517
|
can_access = self._probe_list_type_access(context, app_key, list_type)
|
|
606
518
|
except QingflowApiError as exc:
|
|
607
|
-
|
|
519
|
+
warnings.append(
|
|
520
|
+
{
|
|
521
|
+
"code": "SYSTEM_VIEW_PROBE_UNVERIFIED",
|
|
522
|
+
"message": (
|
|
523
|
+
f"app_get skipped system view '{name}' because access probing failed: "
|
|
524
|
+
f"{exc.message or 'probe failed'}"
|
|
525
|
+
),
|
|
526
|
+
"view_id": view_id,
|
|
527
|
+
"list_type": list_type,
|
|
528
|
+
}
|
|
529
|
+
)
|
|
530
|
+
continue
|
|
608
531
|
if not can_access:
|
|
609
532
|
continue
|
|
610
533
|
items.append({"view_id": view_id, "name": name, "kind": "system", "analysis_supported": True})
|
|
611
534
|
return items, warnings
|
|
612
535
|
|
|
613
|
-
def _resolve_accessible_custom_views(self, context: BackendRequestContext, app_key: str) ->
|
|
536
|
+
def _resolve_accessible_custom_views(self, context: BackendRequestContext, app_key: str) -> list[JSONObject]:
|
|
614
537
|
"""执行内部辅助逻辑。"""
|
|
615
|
-
warnings: list[JSONObject] = []
|
|
616
538
|
try:
|
|
617
539
|
payload = self.backend.request("GET", context, f"/app/{app_key}/view/viewList")
|
|
618
540
|
except QingflowApiError as exc:
|
|
619
|
-
if
|
|
620
|
-
|
|
621
|
-
_with_api_error_fields(
|
|
622
|
-
{
|
|
623
|
-
"code": "CUSTOM_VIEW_LIST_UNAVAILABLE",
|
|
624
|
-
"message": (
|
|
625
|
-
f"app_get could not load custom views for {app_key}; "
|
|
626
|
-
"system views and other app metadata may still be usable."
|
|
627
|
-
),
|
|
628
|
-
},
|
|
629
|
-
exc,
|
|
630
|
-
)
|
|
631
|
-
)
|
|
632
|
-
return [], warnings
|
|
541
|
+
if exc.backend_code in {40002, 40027}:
|
|
542
|
+
return []
|
|
633
543
|
raise
|
|
634
544
|
|
|
635
545
|
items: list[JSONObject] = []
|
|
636
546
|
for item in _normalize_view_list(payload):
|
|
637
|
-
view_key = str(item.get("viewKey") or
|
|
547
|
+
view_key = str(item.get("viewKey") or "").strip()
|
|
638
548
|
if not view_key:
|
|
639
549
|
continue
|
|
640
550
|
normalized: JSONObject = {
|
|
641
551
|
"view_id": f"custom:{view_key}",
|
|
642
|
-
"name": str(item.get("viewName") or
|
|
552
|
+
"name": str(item.get("viewName") or view_key).strip() or view_key,
|
|
643
553
|
"kind": "custom",
|
|
644
554
|
}
|
|
645
555
|
view_type = str(item.get("viewType") or item.get("viewgraphType") or "").strip()
|
|
@@ -647,7 +557,7 @@ class AppTools(ToolBase):
|
|
|
647
557
|
normalized["view_type"] = view_type
|
|
648
558
|
normalized["analysis_supported"] = _analysis_supported_for_view_type(view_type or None)
|
|
649
559
|
items.append(normalized)
|
|
650
|
-
return items
|
|
560
|
+
return items
|
|
651
561
|
|
|
652
562
|
def _compact_base_info(self, result: dict[str, Any]) -> JSONObject:
|
|
653
563
|
"""执行内部辅助逻辑。"""
|
|
@@ -691,30 +601,6 @@ class AppTools(ToolBase):
|
|
|
691
601
|
},
|
|
692
602
|
}
|
|
693
603
|
|
|
694
|
-
def _base_info_from_visible_app(self, app_key: str, visible_app: JSONObject) -> JSONObject:
|
|
695
|
-
"""Build a base-info-shaped fallback from a current-user visible app item."""
|
|
696
|
-
tag_ids: list[int] = []
|
|
697
|
-
tag_id = _coerce_positive_int(visible_app.get("tag_id"))
|
|
698
|
-
if tag_id is not None:
|
|
699
|
-
tag_ids.append(tag_id)
|
|
700
|
-
for value in visible_app.get("tag_ids") if isinstance(visible_app.get("tag_ids"), list) else []:
|
|
701
|
-
tag_id = _coerce_positive_int(value)
|
|
702
|
-
if tag_id is not None and tag_id not in tag_ids:
|
|
703
|
-
tag_ids.append(tag_id)
|
|
704
|
-
|
|
705
|
-
fallback: JSONObject = {
|
|
706
|
-
"appKey": app_key,
|
|
707
|
-
"formId": visible_app.get("form_id"),
|
|
708
|
-
"formTitle": visible_app.get("app_name") or visible_app.get("title") or app_key,
|
|
709
|
-
"appIcon": visible_app.get("app_icon"),
|
|
710
|
-
"tagIds": tag_ids,
|
|
711
|
-
"visibleApp": visible_app,
|
|
712
|
-
}
|
|
713
|
-
package_name = visible_app.get("package_name") or visible_app.get("tag_name") or visible_app.get("group_name")
|
|
714
|
-
if package_name:
|
|
715
|
-
fallback["tagItems"] = [{"tagId": tag_id, "tagName": package_name} for tag_id in tag_ids]
|
|
716
|
-
return fallback
|
|
717
|
-
|
|
718
604
|
def _compact_form_schema(self, result: dict[str, Any]) -> JSONObject:
|
|
719
605
|
"""执行内部辅助逻辑。"""
|
|
720
606
|
base_questions_raw = result.get("baseQues") if isinstance(result.get("baseQues"), list) else []
|
|
@@ -873,7 +759,6 @@ class AppTools(ToolBase):
|
|
|
873
759
|
"app_key": app_key,
|
|
874
760
|
"app_name": title,
|
|
875
761
|
"title": title,
|
|
876
|
-
"app_icon": str(item.get("appIcon") or "").strip() or None,
|
|
877
762
|
"form_id": item.get("formId"),
|
|
878
763
|
"tag_id": package_tag_id,
|
|
879
764
|
"package_name": package_name,
|
|
@@ -934,26 +819,17 @@ def _normalize_form_type(value: int | str) -> int:
|
|
|
934
819
|
|
|
935
820
|
|
|
936
821
|
def _normalize_view_list(payload: Any) -> list[JSONObject]:
|
|
937
|
-
if isinstance(payload, dict):
|
|
938
|
-
raw_list = payload.get("list")
|
|
939
|
-
if isinstance(raw_list, list):
|
|
940
|
-
payload = raw_list
|
|
941
|
-
else:
|
|
942
|
-
payload = [payload]
|
|
943
822
|
if not isinstance(payload, list):
|
|
944
823
|
return []
|
|
945
824
|
flattened: list[JSONObject] = []
|
|
946
825
|
for group in payload:
|
|
947
826
|
if not isinstance(group, dict):
|
|
948
827
|
continue
|
|
949
|
-
if group.get("viewKey") or group.get("viewgraphKey"):
|
|
950
|
-
flattened.append(group)
|
|
951
|
-
continue
|
|
952
828
|
view_list = group.get("viewList")
|
|
953
829
|
if not isinstance(view_list, list):
|
|
954
830
|
continue
|
|
955
831
|
for item in view_list:
|
|
956
|
-
if isinstance(item, dict) and
|
|
832
|
+
if isinstance(item, dict) and item.get("viewKey"):
|
|
957
833
|
flattened.append(item)
|
|
958
834
|
return flattened
|
|
959
835
|
|
|
@@ -965,23 +841,6 @@ def _analysis_supported_for_view_type(view_type: str | None) -> bool:
|
|
|
965
841
|
return normalized not in {"boardview", "ganttview"}
|
|
966
842
|
|
|
967
843
|
|
|
968
|
-
def _is_optional_app_metadata_error(error: QingflowApiError) -> bool:
|
|
969
|
-
if is_auth_like_error(error):
|
|
970
|
-
return False
|
|
971
|
-
backend_code = backend_code_int(error)
|
|
972
|
-
return backend_code in {40002, 40027, 404} or error.http_status == 404
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
def _with_api_error_fields(payload: JSONObject, error: QingflowApiError) -> JSONObject:
|
|
976
|
-
if error.backend_code is not None:
|
|
977
|
-
payload["backend_code"] = error.backend_code
|
|
978
|
-
if error.http_status is not None:
|
|
979
|
-
payload["http_status"] = error.http_status
|
|
980
|
-
if error.request_id:
|
|
981
|
-
payload["request_id"] = error.request_id
|
|
982
|
-
return payload
|
|
983
|
-
|
|
984
|
-
|
|
985
844
|
def _derive_import_capability(base_info: Any) -> tuple[JSONObject, list[JSONObject]]:
|
|
986
845
|
warnings: list[JSONObject] = []
|
|
987
846
|
if not isinstance(base_info, dict):
|