@qingflow-tech/qingflow-cli 1.1.43 → 1.1.44-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -6
- package/entry_point.py +1 -1
- package/npm/bin/qingflow.mjs +1 -2
- package/npm/lib/runtime.mjs +3 -7
- package/package.json +2 -2
- package/pyproject.toml +3 -4
- package/skills/qingflow-app-form/SKILL.md +66 -0
- package/skills/qingflow-app-form/agents/openai.yaml +4 -0
- package/skills/qingflow-cli/SKILL.md +8 -6
- package/skills/qingflow-cli/manifest.yaml +1 -1
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_APP_DELIVERY_WORKFLOW.md +4 -4
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_EXPLORATION_REPORT.md +1 -1
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_FIELD_DATA_TYPES.md +1 -1
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_SCHEMA_APPLY_FIELD_TYPES_AND_SCENARIOS.md +4 -4
- package/skills/qingflow-cli/reference/builder/10-build-single-app.md +5 -5
- package/skills/qingflow-cli/reference/builder/20-build-complete-system.md +14 -14
- package/skills/qingflow-cli/reference/builder/30-schema-fields.md +108 -103
- package/skills/qingflow-cli/reference/builder/40-layout.md +39 -31
- package/skills/qingflow-cli/reference/builder/README.md +9 -9
- package/skills/qingflow-cli/reference/builder/code-integrations/README.md +4 -2
- package/skills/qingflow-cli/reference/builder/code-integrations/code-block.md +2 -0
- package/skills/qingflow-cli/reference/builder/code-integrations/q-linker.md +2 -0
- package/skills/qingflow-cli/reference/builder/reference/app-delivery-sop.md +45 -38
- package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_EXPLORATION_REPORT.md +1 -1
- package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_FIELD_DATA_TYPES.md +1 -1
- package/skills/qingflow-cli/reference/examples/README.md +3 -3
- package/skills/qingflow-common/SKILL.md +47 -0
- package/skills/qingflow-common/agents/openai.yaml +4 -0
- package/src/qingflow_mcp/__main__.py +2 -3
- package/src/qingflow_mcp/app_form_compat.py +104 -0
- package/src/qingflow_mcp/app_form_validator.py +462 -0
- package/src/qingflow_mcp/builder_facade/app_form.py +4205 -0
- package/src/qingflow_mcp/builder_facade/app_form_schema.py +425 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/manifest.json +42 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/complete.json +2969 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/address.json +392 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/amount.json +236 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/attachment.json +425 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/code_block.json +318 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/data_relation.json +580 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/date.json +150 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/datetime.json +150 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/department.json +345 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/email.json +190 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/link.json +190 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/long_text.json +190 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/member.json +484 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/multi_select.json +214 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/number.json +236 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/phone.json +199 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/q_linker.json +412 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/relation.json +454 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/single_choice.json +214 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/single_select.json +214 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/subtable.json +1921 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/text.json +190 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/formula.json +1594 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/summary.json +718 -0
- package/src/qingflow_mcp/builder_facade/service.py +8 -4
- package/src/qingflow_mcp/cli/commands/builder.py +93 -28
- package/src/qingflow_mcp/cli/json_io.py +21 -2
- package/src/qingflow_mcp/cli/main.py +3 -39
- package/src/qingflow_mcp/public_surface.py +5 -2
- package/src/qingflow_mcp/response_trim.py +50 -0
- package/src/qingflow_mcp/server.py +24 -3
- package/src/qingflow_mcp/server_app_builder.py +44 -87
- package/src/qingflow_mcp/tools/ai_builder_tools.py +103 -100
- package/src/qingflow_mcp/tools/app_tools.py +46 -65
- package/src/qingflow_mcp/tools/record_tools.py +3 -3
- package/src/qingflow_mcp/version.py +2 -4
- package/docs/local-agent-install.md +0 -370
- package/skills/qingflow-mcp-setup/SKILL.md +0 -115
- package/skills/qingflow-mcp-setup/agents/openai.yaml +0 -4
- package/skills/qingflow-mcp-setup/references/claude-desktop.md +0 -34
- package/skills/qingflow-mcp-setup/references/environments.md +0 -62
- package/skills/qingflow-mcp-setup/references/generic-stdio.md +0 -32
- package/skills/qingflow-mcp-setup/scripts/check_local_server.sh +0 -38
|
@@ -36,15 +36,15 @@ def build_builder_server() -> FastMCP:
|
|
|
36
36
|
"Use builder_tool_contract when you need a machine-readable contract, aliases, allowed enums, or a minimal valid example for a public builder tool. "
|
|
37
37
|
"Use solution_install when the user explicitly wants to install a packaged solution/template by solution_key, optionally copying bundled demo data. "
|
|
38
38
|
"Use package_list to find visible app packages by keyword and package_get to read package detail before editing; if creating or updating an app package may be appropriate, use package_apply with explicit user intent; otherwise use app_resolve to locate app resources, "
|
|
39
|
-
"Use workspace_icon_catalog_get before creating app packages
|
|
40
|
-
"app_get as the default app map read, then app_get_fields/app_repair_code_blocks/app_get_layout/app_get_views/app_get_flow/app_get_charts/portal_list/portal_get/view_get/chart_get for focused
|
|
39
|
+
"Use workspace_icon_catalog_get before creating app packages or portals when supported icon/color candidates are needed; those resources require explicit non-template icon + color. AppForm-created apps derive a stable default icon and color from spec.appName. "
|
|
40
|
+
"app_form_schema plus app_form_get for versioned form work; query app_form_schema with schema_kind=formula before writing defaultType=3 formulas, and use being_draft=true only for editing and the published default for relation or record consumers; use app_get as the default non-form app map read, then app_get_fields/app_repair_code_blocks/app_get_layout/app_get_views/app_get_flow/app_get_charts/portal_list/portal_get/view_get/chart_get for focused compatibility or non-form reads, "
|
|
41
41
|
"member_search/role_search/role_create when workflow assignees must come from the directory or role catalog, preferring roles over explicit members unless the user explicitly names members, "
|
|
42
|
-
"then
|
|
42
|
+
"then app_form_apply for complete app form targets, explicit app_delete for application deletion, and app_flow_apply/app_views_apply/app_associated_resources_apply/app_charts_apply/portal_apply/portal_delete for non-form patches. app_custom_buttons_apply is maintenance-only for explicit standalone button work outside normal view design. These apply/delete tools perform validation, normalization, writes, and readback verification where applicable. AppForm publishes only after a draft change or when the published form lags behind; views noop requests skip publish, app_custom_buttons_apply and app_associated_resources_apply publish after at least one write succeeds and expose no draft-only parameter, charts are immediate-live without publish and resolve targets by chart_id first then exact unique chart name, portal updates use replace semantics only when sections are supplied and edit-mode base-info-only updates may omit sections, portal delete separates DELETE execution from readback verification, portal pc layout is a 24-column grid and mobile is a 6-column grid so omit position or use layout_preset when unsure, publish=false only guarantees draft/base-info updates for tools that still expose that parameter, and flow should use publish=false whenever you only want draft/precheck behavior. "
|
|
43
43
|
"Builder apply/write outputs include schema_version, operation, summary, and resources[]; use resources[].id/key/name/ids/parent as the stable UI and agent display entry, and keep legacy fields such as field_diff/views_diff/chart_results only for compatibility or troubleshooting. "
|
|
44
44
|
"For existing object parameter replacement, prefer patch_views, patch_buttons, patch_resources, and patch_charts with set/unset; the tool reads current config and full-saves internally, while upsert_* is for creation or full target configuration and should not be used as an incomplete partial update. "
|
|
45
45
|
"For builder delete/remove apply results, separate delete execution from readback verification: after DELETE is sent, resources expose delete_executed, readback_status, and safe_to_retry_delete=false. If readback_status is unavailable or still_exists, do not blindly repeat the delete; confirm later with app_get/view_get/chart_get or the relevant apply readback. Views/buttons use single-item readback; associated resources use one app-level resource-pool readback because there is no confirmed single-item GET. "
|
|
46
46
|
"For builder write/apply results, separate write execution from final readback verification: status=partial_success with write_executed=true and safe_to_retry=false means the write was sent or succeeded, while final readback, publish verification, or metadata confirmation is pending or permission-restricted. Report it as written but unverified; do not repeat the same write blindly. "
|
|
47
|
-
"For
|
|
47
|
+
"For app_form_apply, pin the version from app_form_schema, preserve the complete body and existing IDs from a draft get, and configure data display through app-level spec.dataTitleField/spec.dataCoverField selectors rather than booleans on fields; App Builder MCP validates the declaration inside app_form_apply, while the CLI route has a separate static validate command. dataTitleField must resolve to exactly one top-level field for a non-empty body, while dataCoverField is optional and must resolve to a top-level attachment field. A root appKey means update and may include packageId; no appKey means create and requires packageId plus spec.appName. AppForm does not accept spec.icon or spec.color; creation derives a stable default icon and color from spec.appName. "
|
|
48
48
|
"For app_views_apply, keep fixed saved filters in filters and configure the frontend query panel separately with query_conditions; query_conditions.rows is a matrix of field names compiled to backend queryCondition queIds. New views default associated report/view display to visible with limit_type=all; existing views preserve their current associated display unless associated_resources is explicitly patched. View writes use ViewManagementAuth (beingViewManageStatus), falling back to DataManageAuth when advanced app permissions are not enabled. "
|
|
49
49
|
"For ordinary business buttons, declare them on the target view with app_views_apply.views[].action_buttons; app_views_apply writes the view first, then creates/binds the button. Use app_custom_buttons_apply only when the user explicitly asks to maintain existing standalone buttons outside view design, such as custom style/icon changes, cross-view reuse, deleting button bodies, bulk binding reorder, or exact qRobot/wings config. Button body writes require EditAppAuth; view_configs placement writes require ViewManagementAuth, matching the backend viewConfig route. For addData buttons, prefer action_buttons.target_app_key + field_mappings/default_values on the view path; do not ask agents to write raw que_relation unless maintaining a legacy config. field_mappings.source_field accepts source schema fields and supported system fields: 数据ID/row_record_id/apply_id/_id means current record id (-17), 编号/record_number means visible record number (0). To fill a target relation with the current record, map {'source_field': '数据ID', 'target_field': '目标引用字段'}; default_values is only for static constants. View button bindings merge by default; standalone view_configs are only for explicit maintenance. Builder view_key arguments are raw keys from app_get.views[].view_key and must not be prefixed with custom:. "
|
|
50
50
|
"For BI reports, keep report-body development separate from Qingflow in-app display: use app_charts_apply to create, update, remove, or reorder app-source QingBI chart bodies/configs with dataSourceType=qingflow; chart dimension/metric/filter/query fields must come from app_get_fields.chart_fields, not record schema or form-only fields; dataset BI reports are not created or edited by app_charts_apply yet and should be created in QingBI first, then attached with app_associated_resources_apply using report_source=dataset. "
|
|
@@ -180,6 +180,46 @@ def build_builder_server() -> FastMCP:
|
|
|
180
180
|
def builder_tool_contract(tool_name: str = "") -> dict:
|
|
181
181
|
return ai_builder.builder_tool_contract(tool_name=tool_name)
|
|
182
182
|
|
|
183
|
+
@server.tool()
|
|
184
|
+
def app_form_schema(
|
|
185
|
+
schema_version: str | None = None,
|
|
186
|
+
field_type: str | None = None,
|
|
187
|
+
schema_kind: str | None = None,
|
|
188
|
+
) -> dict:
|
|
189
|
+
return ai_builder.app_form_schema(
|
|
190
|
+
schema_version=schema_version,
|
|
191
|
+
field_type=field_type,
|
|
192
|
+
schema_kind=schema_kind,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
@server.tool()
|
|
196
|
+
def app_form_get(
|
|
197
|
+
profile: str = DEFAULT_PROFILE,
|
|
198
|
+
app_key: str = "",
|
|
199
|
+
schema_version: str | None = None,
|
|
200
|
+
being_draft: bool = False,
|
|
201
|
+
) -> dict:
|
|
202
|
+
return ai_builder.app_form_get(
|
|
203
|
+
profile=profile,
|
|
204
|
+
app_key=app_key,
|
|
205
|
+
schema_version=schema_version,
|
|
206
|
+
being_draft=being_draft,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
@server.tool()
|
|
210
|
+
def app_form_apply(
|
|
211
|
+
profile: str = DEFAULT_PROFILE,
|
|
212
|
+
spec: dict | None = None,
|
|
213
|
+
) -> dict:
|
|
214
|
+
return ai_builder.app_form_apply(profile=profile, spec=spec or {})
|
|
215
|
+
|
|
216
|
+
@server.tool(description=ai_builder._high_risk_tool_description(operation="delete", target="app configuration"))
|
|
217
|
+
def app_delete(
|
|
218
|
+
profile: str = DEFAULT_PROFILE,
|
|
219
|
+
app_key: str = "",
|
|
220
|
+
) -> dict:
|
|
221
|
+
return ai_builder.app_delete(profile=profile, app_key=app_key)
|
|
222
|
+
|
|
183
223
|
@server.tool()
|
|
184
224
|
def workspace_icon_catalog_get(profile: str = DEFAULT_PROFILE) -> dict:
|
|
185
225
|
return ai_builder.workspace_icon_catalog_get(profile=profile)
|
|
@@ -414,89 +454,6 @@ def build_builder_server() -> FastMCP:
|
|
|
414
454
|
def chart_get(profile: str = DEFAULT_PROFILE, chart_id: str = "") -> dict:
|
|
415
455
|
return ai_builder.chart_get(profile=profile, chart_id=chart_id)
|
|
416
456
|
|
|
417
|
-
@server.tool()
|
|
418
|
-
def app_schema_apply(
|
|
419
|
-
profile: str = DEFAULT_PROFILE,
|
|
420
|
-
app_key: str = "",
|
|
421
|
-
package_id: int | None = None,
|
|
422
|
-
app_name: str = "",
|
|
423
|
-
app_title: str = "",
|
|
424
|
-
icon: str = "",
|
|
425
|
-
color: str = "",
|
|
426
|
-
visibility: dict | None = None,
|
|
427
|
-
publish: bool = True,
|
|
428
|
-
form: list[dict] | None = None,
|
|
429
|
-
add_fields: list[dict] | None = None,
|
|
430
|
-
update_fields: list[dict] | None = None,
|
|
431
|
-
remove_fields: list[dict] | None = None,
|
|
432
|
-
apps: list[dict] | None = None,
|
|
433
|
-
) -> dict:
|
|
434
|
-
if apps:
|
|
435
|
-
if app_key or app_name or app_title or form or add_fields or update_fields or remove_fields:
|
|
436
|
-
return _config_failure(
|
|
437
|
-
"app_schema_apply multi-app mode accepts package_id plus apps only.",
|
|
438
|
-
fix_hint="Use apps[].form for batch mode, or use the single-app arguments without apps.",
|
|
439
|
-
)
|
|
440
|
-
if package_id is None:
|
|
441
|
-
return _config_failure(
|
|
442
|
-
"app_schema_apply multi-app mode requires package_id.",
|
|
443
|
-
fix_hint="Pass `package_id` and `apps[].app_name` for new apps, or `apps[].app_key` for existing apps.",
|
|
444
|
-
)
|
|
445
|
-
return ai_builder.app_schema_apply(
|
|
446
|
-
profile=profile,
|
|
447
|
-
package_id=package_id,
|
|
448
|
-
visibility=visibility,
|
|
449
|
-
publish=publish,
|
|
450
|
-
form=None,
|
|
451
|
-
add_fields=[],
|
|
452
|
-
update_fields=[],
|
|
453
|
-
remove_fields=[],
|
|
454
|
-
apps=apps,
|
|
455
|
-
)
|
|
456
|
-
has_app_key = bool((app_key or "").strip())
|
|
457
|
-
has_app_name = bool((app_name or "").strip())
|
|
458
|
-
has_app_title = bool((app_title or "").strip())
|
|
459
|
-
has_package_id = package_id is not None
|
|
460
|
-
create_by_shape = has_package_id and (has_app_name or has_app_title)
|
|
461
|
-
if has_app_key:
|
|
462
|
-
if has_package_id:
|
|
463
|
-
return _config_failure(
|
|
464
|
-
"app_schema_apply edit mode accepts app_key and optional app_name rename only.",
|
|
465
|
-
fix_hint="For existing apps, use `app_key` and optionally `app_name`. For create mode, use `package_id + app_name`.",
|
|
466
|
-
)
|
|
467
|
-
elif not create_by_shape:
|
|
468
|
-
return _config_failure(
|
|
469
|
-
"app_schema_apply create mode requires package_id and app_name.",
|
|
470
|
-
fix_hint="Use `app_key` for existing apps, or pass `package_id + app_name` to create a new app.",
|
|
471
|
-
)
|
|
472
|
-
return ai_builder.app_schema_apply(
|
|
473
|
-
profile=profile,
|
|
474
|
-
app_key=app_key,
|
|
475
|
-
package_id=package_id,
|
|
476
|
-
app_name=app_name,
|
|
477
|
-
app_title=app_title,
|
|
478
|
-
icon=icon,
|
|
479
|
-
color=color,
|
|
480
|
-
visibility=visibility,
|
|
481
|
-
publish=publish,
|
|
482
|
-
form=form or [],
|
|
483
|
-
add_fields=add_fields or [],
|
|
484
|
-
update_fields=update_fields or [],
|
|
485
|
-
remove_fields=remove_fields or [],
|
|
486
|
-
apps=None,
|
|
487
|
-
)
|
|
488
|
-
|
|
489
|
-
@server.tool()
|
|
490
|
-
def app_layout_apply(
|
|
491
|
-
profile: str = DEFAULT_PROFILE,
|
|
492
|
-
app_key: str = "",
|
|
493
|
-
mode: str = "merge",
|
|
494
|
-
publish: bool = True,
|
|
495
|
-
sections: list[dict] | None = None,
|
|
496
|
-
apps: list[dict] | None = None,
|
|
497
|
-
) -> dict:
|
|
498
|
-
return ai_builder.app_layout_apply(profile=profile, app_key=app_key, mode=mode, publish=publish, sections=sections or [], apps=apps)
|
|
499
|
-
|
|
500
457
|
@server.tool()
|
|
501
458
|
def app_flow_get(
|
|
502
459
|
profile: str = DEFAULT_PROFILE,
|
|
@@ -14,6 +14,8 @@ from ..builder_facade.button_style_catalog import (
|
|
|
14
14
|
BUTTON_STYLE_PRESETS,
|
|
15
15
|
BUTTON_TEXT_COLORS,
|
|
16
16
|
)
|
|
17
|
+
from ..builder_facade.app_form import AppFormReconciler
|
|
18
|
+
from ..builder_facade.app_form_schema import app_form_schema_registry
|
|
17
19
|
from ..public_surface import public_builder_contract_tool_names
|
|
18
20
|
from ..config import DEFAULT_PROFILE
|
|
19
21
|
from ..errors import QingflowApiError, backend_code_int, backend_code_value_int
|
|
@@ -130,10 +132,14 @@ def _inline_form_layout_was_handled(payload: JSONObject) -> bool:
|
|
|
130
132
|
|
|
131
133
|
PUBLIC_STABLE_FLOW_NODE_TYPES = ["start", "approve", "fill", "copy", "webhook", "end"]
|
|
132
134
|
BUILDER_APPLY_SCHEMA_VERSION = "builder.apply.v1"
|
|
133
|
-
|
|
134
|
-
|
|
135
|
+
# TODO(deprecation): Remove these names and their imperative adapters after internal AppForm migration completes.
|
|
136
|
+
LEGACY_IMPERATIVE_APP_FORM_TOOL_NAMES = {
|
|
135
137
|
"app_schema_apply",
|
|
136
138
|
"app_layout_apply",
|
|
139
|
+
}
|
|
140
|
+
BUILDER_APPLY_TOOL_NAMES = {
|
|
141
|
+
"package_apply",
|
|
142
|
+
*LEGACY_IMPERATIVE_APP_FORM_TOOL_NAMES,
|
|
137
143
|
"app_flow_apply",
|
|
138
144
|
"app_views_apply",
|
|
139
145
|
"app_custom_buttons_apply",
|
|
@@ -161,10 +167,12 @@ class AiBuilderTools(ToolBase):
|
|
|
161
167
|
def __init__(self, sessions, backend) -> None:
|
|
162
168
|
"""执行内部辅助逻辑。"""
|
|
163
169
|
super().__init__(sessions, backend)
|
|
170
|
+
apps = AppTools(sessions, backend)
|
|
171
|
+
packages = PackageTools(sessions, backend)
|
|
164
172
|
self._facade = AiBuilderFacade(
|
|
165
|
-
apps=
|
|
173
|
+
apps=apps,
|
|
166
174
|
buttons=CustomButtonTools(sessions, backend),
|
|
167
|
-
packages=
|
|
175
|
+
packages=packages,
|
|
168
176
|
views=ViewTools(sessions, backend),
|
|
169
177
|
workflows=WorkflowTools(sessions, backend),
|
|
170
178
|
portals=PortalTools(sessions, backend),
|
|
@@ -173,6 +181,11 @@ class AiBuilderTools(ToolBase):
|
|
|
173
181
|
directory=DirectoryTools(sessions, backend),
|
|
174
182
|
solutions=SolutionTools(sessions, backend),
|
|
175
183
|
)
|
|
184
|
+
self._app_form = AppFormReconciler(
|
|
185
|
+
apps=apps,
|
|
186
|
+
packages=packages,
|
|
187
|
+
registry=app_form_schema_registry(),
|
|
188
|
+
)
|
|
176
189
|
|
|
177
190
|
def _apply_app_batch(self, *, tool_name: str, profile: str, apps: list[JSONObject], apply_one) -> JSONObject:
|
|
178
191
|
started_at = time.perf_counter()
|
|
@@ -367,6 +380,46 @@ class AiBuilderTools(ToolBase):
|
|
|
367
380
|
def builder_tool_contract(tool_name: str = "") -> JSONObject:
|
|
368
381
|
return self.builder_tool_contract(tool_name=tool_name)
|
|
369
382
|
|
|
383
|
+
@mcp.tool()
|
|
384
|
+
def app_form_schema(
|
|
385
|
+
schema_version: str | None = None,
|
|
386
|
+
field_type: str | None = None,
|
|
387
|
+
schema_kind: str | None = None,
|
|
388
|
+
) -> JSONObject:
|
|
389
|
+
return self.app_form_schema(
|
|
390
|
+
schema_version=schema_version,
|
|
391
|
+
field_type=field_type,
|
|
392
|
+
schema_kind=schema_kind,
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
@mcp.tool()
|
|
396
|
+
def app_form_get(
|
|
397
|
+
profile: str = DEFAULT_PROFILE,
|
|
398
|
+
app_key: str = "",
|
|
399
|
+
schema_version: str | None = None,
|
|
400
|
+
being_draft: bool = False,
|
|
401
|
+
) -> JSONObject:
|
|
402
|
+
return self.app_form_get(
|
|
403
|
+
profile=profile,
|
|
404
|
+
app_key=app_key,
|
|
405
|
+
schema_version=schema_version,
|
|
406
|
+
being_draft=being_draft,
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
@mcp.tool()
|
|
410
|
+
def app_form_apply(
|
|
411
|
+
profile: str = DEFAULT_PROFILE,
|
|
412
|
+
spec: JSONObject | None = None,
|
|
413
|
+
) -> JSONObject:
|
|
414
|
+
return self.app_form_apply(profile=profile, spec=spec or {})
|
|
415
|
+
|
|
416
|
+
@mcp.tool(description=self._high_risk_tool_description(operation="delete", target="app configuration"))
|
|
417
|
+
def app_delete(
|
|
418
|
+
profile: str = DEFAULT_PROFILE,
|
|
419
|
+
app_key: str = "",
|
|
420
|
+
) -> JSONObject:
|
|
421
|
+
return self.app_delete(profile=profile, app_key=app_key)
|
|
422
|
+
|
|
370
423
|
@mcp.tool()
|
|
371
424
|
def workspace_icon_catalog_get(profile: str = DEFAULT_PROFILE) -> JSONObject:
|
|
372
425
|
return self.workspace_icon_catalog_get(profile=profile)
|
|
@@ -612,98 +665,6 @@ class AiBuilderTools(ToolBase):
|
|
|
612
665
|
) -> JSONObject:
|
|
613
666
|
return self.chart_get(profile=profile, chart_id=chart_id)
|
|
614
667
|
|
|
615
|
-
@mcp.tool()
|
|
616
|
-
def app_schema_apply(
|
|
617
|
-
profile: str = DEFAULT_PROFILE,
|
|
618
|
-
app_key: str = "",
|
|
619
|
-
package_id: int | None = None,
|
|
620
|
-
app_name: str = "",
|
|
621
|
-
app_title: str = "",
|
|
622
|
-
icon: str | JSONObject = "",
|
|
623
|
-
color: str = "",
|
|
624
|
-
icon_name: str | None = None,
|
|
625
|
-
icon_color: str | None = None,
|
|
626
|
-
icon_config: JSONObject | None = None,
|
|
627
|
-
visibility: JSONObject | None = None,
|
|
628
|
-
publish: bool = True,
|
|
629
|
-
form: list[JSONObject] | None = None,
|
|
630
|
-
add_fields: list[JSONObject] | None = None,
|
|
631
|
-
update_fields: list[JSONObject] | None = None,
|
|
632
|
-
remove_fields: list[JSONObject] | None = None,
|
|
633
|
-
apps: list[JSONObject] | None = None,
|
|
634
|
-
) -> JSONObject:
|
|
635
|
-
if apps is not None:
|
|
636
|
-
if app_key or app_name or app_title or form or add_fields or update_fields or remove_fields:
|
|
637
|
-
return _config_failure(
|
|
638
|
-
tool_name="app_schema_apply",
|
|
639
|
-
message="app_schema_apply multi-app mode accepts package_id plus apps only.",
|
|
640
|
-
fix_hint="Put per-app form in apps[].form when using batch mode.",
|
|
641
|
-
)
|
|
642
|
-
if package_id is None:
|
|
643
|
-
return _config_failure(
|
|
644
|
-
tool_name="app_schema_apply",
|
|
645
|
-
message="app_schema_apply multi-app mode requires package_id.",
|
|
646
|
-
fix_hint="Pass package_id and apps[].app_name for new apps, or apps[].app_key for existing apps.",
|
|
647
|
-
)
|
|
648
|
-
return self.app_schema_apply(
|
|
649
|
-
profile=profile,
|
|
650
|
-
package_id=package_id,
|
|
651
|
-
visibility=visibility,
|
|
652
|
-
publish=publish,
|
|
653
|
-
apps=apps,
|
|
654
|
-
form=None,
|
|
655
|
-
add_fields=[],
|
|
656
|
-
update_fields=[],
|
|
657
|
-
remove_fields=[],
|
|
658
|
-
)
|
|
659
|
-
has_app_key = bool((app_key or "").strip())
|
|
660
|
-
has_app_name = bool((app_name or "").strip())
|
|
661
|
-
has_app_title = bool((app_title or "").strip())
|
|
662
|
-
has_package_id = package_id is not None
|
|
663
|
-
if has_app_key:
|
|
664
|
-
if has_package_id:
|
|
665
|
-
return _config_failure(
|
|
666
|
-
tool_name="app_schema_apply",
|
|
667
|
-
message="app_schema_apply edit mode accepts app_key and optional app_name rename only.",
|
|
668
|
-
fix_hint="For existing apps, use `app_key` and optionally `app_name`. For create mode, use `package_id + app_name`.",
|
|
669
|
-
)
|
|
670
|
-
elif not (has_package_id and (has_app_name or has_app_title)):
|
|
671
|
-
return _config_failure(
|
|
672
|
-
tool_name="app_schema_apply",
|
|
673
|
-
message="app_schema_apply create mode requires package_id and app_name.",
|
|
674
|
-
fix_hint="Use `app_key` for existing apps, or pass `package_id + app_name` to create a new app.",
|
|
675
|
-
)
|
|
676
|
-
return self.app_schema_apply(
|
|
677
|
-
profile=profile,
|
|
678
|
-
app_key=app_key,
|
|
679
|
-
package_id=package_id,
|
|
680
|
-
app_name=app_name,
|
|
681
|
-
app_title=app_title,
|
|
682
|
-
icon=icon,
|
|
683
|
-
color=color,
|
|
684
|
-
icon_name=icon_name,
|
|
685
|
-
icon_color=icon_color,
|
|
686
|
-
icon_config=icon_config,
|
|
687
|
-
visibility=visibility,
|
|
688
|
-
publish=publish,
|
|
689
|
-
form=form or [],
|
|
690
|
-
add_fields=add_fields or [],
|
|
691
|
-
update_fields=update_fields or [],
|
|
692
|
-
remove_fields=remove_fields or [],
|
|
693
|
-
apps=None,
|
|
694
|
-
)
|
|
695
|
-
|
|
696
|
-
@mcp.tool()
|
|
697
|
-
def app_layout_apply(
|
|
698
|
-
profile: str = DEFAULT_PROFILE,
|
|
699
|
-
app_key: str = "",
|
|
700
|
-
mode: str = "merge",
|
|
701
|
-
publish: bool = True,
|
|
702
|
-
sections: list[JSONObject] | None = None,
|
|
703
|
-
apps: list[JSONObject] | None = None,
|
|
704
|
-
) -> JSONObject:
|
|
705
|
-
return self.app_layout_apply(profile=profile, app_key=app_key, mode=mode, publish=publish, sections=sections or [], apps=apps)
|
|
706
|
-
|
|
707
668
|
@mcp.tool()
|
|
708
669
|
def app_flow_get(
|
|
709
670
|
profile: str = DEFAULT_PROFILE,
|
|
@@ -857,6 +818,44 @@ class AiBuilderTools(ToolBase):
|
|
|
857
818
|
expected_package_id=expected_package_id,
|
|
858
819
|
)
|
|
859
820
|
|
|
821
|
+
def app_form_schema(
|
|
822
|
+
self,
|
|
823
|
+
*,
|
|
824
|
+
schema_version: str | None = None,
|
|
825
|
+
field_type: str | None = None,
|
|
826
|
+
schema_kind: str | None = None,
|
|
827
|
+
) -> JSONObject:
|
|
828
|
+
"""Return a versioned AppForm summary, field, or formula schema."""
|
|
829
|
+
return self._app_form.schema(
|
|
830
|
+
schema_version=schema_version,
|
|
831
|
+
field_type=field_type,
|
|
832
|
+
schema_kind=schema_kind,
|
|
833
|
+
)
|
|
834
|
+
|
|
835
|
+
def app_form_get(
|
|
836
|
+
self,
|
|
837
|
+
*,
|
|
838
|
+
profile: str,
|
|
839
|
+
app_key: str,
|
|
840
|
+
schema_version: str | None = None,
|
|
841
|
+
being_draft: bool = False,
|
|
842
|
+
) -> JSONObject:
|
|
843
|
+
"""Read a canonical draft or published AppForm resource."""
|
|
844
|
+
return self._app_form.get(
|
|
845
|
+
profile=profile,
|
|
846
|
+
app_key=app_key,
|
|
847
|
+
schema_version=schema_version,
|
|
848
|
+
being_draft=being_draft,
|
|
849
|
+
)
|
|
850
|
+
|
|
851
|
+
def app_form_apply(self, *, profile: str, spec: JSONObject) -> JSONObject:
|
|
852
|
+
"""Reconcile one complete declarative AppForm target."""
|
|
853
|
+
return self._app_form.apply(profile=profile, spec=spec)
|
|
854
|
+
|
|
855
|
+
def app_delete(self, *, profile: str, app_key: str) -> JSONObject:
|
|
856
|
+
"""Delete an application explicitly."""
|
|
857
|
+
return self._app_form.delete(profile=profile, app_key=app_key)
|
|
858
|
+
|
|
860
859
|
def package_list(self, *, profile: str, trial_status: str = "all", query: str = "") -> JSONObject:
|
|
861
860
|
"""执行分组与包相关逻辑。"""
|
|
862
861
|
normalized_args = {"trial_status": trial_status, "query": query}
|
|
@@ -2092,7 +2091,7 @@ class AiBuilderTools(ToolBase):
|
|
|
2092
2091
|
remove_fields: list[JSONObject],
|
|
2093
2092
|
apps: list[JSONObject] | None = None,
|
|
2094
2093
|
) -> JSONObject:
|
|
2095
|
-
"""
|
|
2094
|
+
"""Legacy imperative schema adapter retained only for internal callers pending deletion."""
|
|
2096
2095
|
icon, color = _normalize_builder_icon_args(
|
|
2097
2096
|
icon=icon,
|
|
2098
2097
|
color=color,
|
|
@@ -3435,7 +3434,7 @@ class AiBuilderTools(ToolBase):
|
|
|
3435
3434
|
sections: list[JSONObject],
|
|
3436
3435
|
apps: list[JSONObject] | None = None,
|
|
3437
3436
|
) -> JSONObject:
|
|
3438
|
-
"""
|
|
3437
|
+
"""Legacy imperative layout adapter retained only for internal callers pending deletion."""
|
|
3439
3438
|
if apps is not None:
|
|
3440
3439
|
return self._apply_app_batch(
|
|
3441
3440
|
tool_name="app_layout_apply",
|
|
@@ -7282,6 +7281,8 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
|
|
|
7282
7281
|
"remove_fields": [],
|
|
7283
7282
|
},
|
|
7284
7283
|
},
|
|
7284
|
+
# TODO(deprecation): Retained only for internal imperative adapter validation. This contract
|
|
7285
|
+
# is deliberately excluded from builder_tool_contract and must be deleted with the adapter.
|
|
7285
7286
|
"app_schema_apply": {
|
|
7286
7287
|
"allowed_keys": [
|
|
7287
7288
|
"app_key",
|
|
@@ -7638,6 +7639,8 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
|
|
|
7638
7639
|
},
|
|
7639
7640
|
"preset_example": {"profile": "default", "app_key": "APP_KEY", "mode": "merge", "preset": "balanced", "sections": []},
|
|
7640
7641
|
},
|
|
7642
|
+
# TODO(deprecation): Retained only for internal imperative adapter validation. This contract
|
|
7643
|
+
# is deliberately excluded from builder_tool_contract and must be deleted with the adapter.
|
|
7641
7644
|
"app_layout_apply": {
|
|
7642
7645
|
"allowed_keys": ["app_key", "mode", "publish", "sections"],
|
|
7643
7646
|
"aliases": {"overwrite": "replace", "sectionId": "section_id"},
|
|
@@ -8172,7 +8175,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
|
|
|
8172
8175
|
"allowed_values": {},
|
|
8173
8176
|
"execution_notes": [
|
|
8174
8177
|
"returns compact current field configuration for one app",
|
|
8175
|
-
"use
|
|
8178
|
+
"use app_form_get through the qingflow-app-form Skill for editable form definitions",
|
|
8176
8179
|
"also returns chart_fields from QingBI datasource fields; app_charts_apply field selectors should use chart_fields because record/schema-visible fields and QingBI fields are not the same schema",
|
|
8177
8180
|
"chart_fields[].field_id supports field_<queId> selectors, while chart_fields[].bi_field_id is the raw QingBI fieldId accepted by report configs",
|
|
8178
8181
|
"chart_fields[].chart_apply_examples contains copyable semantic app_charts_apply snippets such as count_by_field, filtered_count, and numeric sum_metric",
|
|
@@ -8189,7 +8192,7 @@ _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
|
|
|
8189
8192
|
"allowed_values": {},
|
|
8190
8193
|
"execution_notes": [
|
|
8191
8194
|
"returns compact current layout configuration for one app",
|
|
8192
|
-
"use
|
|
8195
|
+
"use app_form_get through the qingflow-app-form Skill for editable form layout",
|
|
8193
8196
|
],
|
|
8194
8197
|
"minimal_example": {
|
|
8195
8198
|
"profile": "default",
|
|
@@ -20,8 +20,11 @@ class AppTools(ToolBase):
|
|
|
20
20
|
类型:应用元数据与配置工具。
|
|
21
21
|
主要职责:
|
|
22
22
|
1. 查询应用列表、读取应用详情;
|
|
23
|
-
2.
|
|
24
|
-
3.
|
|
23
|
+
2. 为声明式 AppForm 和 Solution 编排保留底层应用适配器;
|
|
24
|
+
3. 仅将应用列表与详情注册为本类的 MCP 入口。
|
|
25
|
+
|
|
26
|
+
TODO(deprecation): Remove the imperative app-form adapters after AppForm and Solution
|
|
27
|
+
no longer call them. They are implementation details and must not be registered as MCP tools.
|
|
25
28
|
"""
|
|
26
29
|
|
|
27
30
|
def register(self, mcp: FastMCP) -> None:
|
|
@@ -34,58 +37,6 @@ class AppTools(ToolBase):
|
|
|
34
37
|
def app_get(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
|
|
35
38
|
return self.app_get(profile=profile, app_key=app_key)
|
|
36
39
|
|
|
37
|
-
@mcp.tool()
|
|
38
|
-
def app_get_base(profile: str = DEFAULT_PROFILE, app_key: str = "", include_raw: bool = False) -> JSONObject:
|
|
39
|
-
return self.app_get_base(profile=profile, app_key=app_key, include_raw=include_raw)
|
|
40
|
-
|
|
41
|
-
@mcp.tool(description=self._high_risk_tool_description(operation="update", target="app base settings"))
|
|
42
|
-
def app_update_base(profile: str = DEFAULT_PROFILE, app_key: str = "", payload: JSONObject | None = None) -> JSONObject:
|
|
43
|
-
return self.app_update_base(profile=profile, app_key=app_key, payload=payload or {})
|
|
44
|
-
|
|
45
|
-
@mcp.tool()
|
|
46
|
-
def app_get_form_schema(
|
|
47
|
-
profile: str = DEFAULT_PROFILE,
|
|
48
|
-
app_key: str = "",
|
|
49
|
-
form_type: int | str = 1,
|
|
50
|
-
being_draft: bool | None = None,
|
|
51
|
-
being_apply: bool | None = None,
|
|
52
|
-
audit_node_id: int | None = None,
|
|
53
|
-
include_raw: bool = False,
|
|
54
|
-
) -> JSONObject:
|
|
55
|
-
return self.app_get_form_schema(
|
|
56
|
-
profile=profile,
|
|
57
|
-
app_key=app_key,
|
|
58
|
-
form_type=form_type,
|
|
59
|
-
being_draft=being_draft,
|
|
60
|
-
being_apply=being_apply,
|
|
61
|
-
audit_node_id=audit_node_id,
|
|
62
|
-
include_raw=include_raw,
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
@mcp.tool()
|
|
66
|
-
def app_get_edit_version_no(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
|
|
67
|
-
return self.app_get_edit_version_no(profile=profile, app_key=app_key)
|
|
68
|
-
|
|
69
|
-
@mcp.tool()
|
|
70
|
-
def app_edit_finished(profile: str = DEFAULT_PROFILE, app_key: str = "", payload: JSONObject | None = None) -> JSONObject:
|
|
71
|
-
return self.app_edit_finished(profile=profile, app_key=app_key, payload=payload or {})
|
|
72
|
-
|
|
73
|
-
@mcp.tool()
|
|
74
|
-
def app_create(profile: str = DEFAULT_PROFILE, payload: JSONObject | None = None) -> JSONObject:
|
|
75
|
-
return self.app_create(profile=profile, payload=payload or {})
|
|
76
|
-
|
|
77
|
-
@mcp.tool(description=self._high_risk_tool_description(operation="delete", target="app configuration"))
|
|
78
|
-
def app_delete(profile: str = DEFAULT_PROFILE, app_key: str = "") -> JSONObject:
|
|
79
|
-
return self.app_delete(profile=profile, app_key=app_key)
|
|
80
|
-
|
|
81
|
-
@mcp.tool()
|
|
82
|
-
def app_publish(
|
|
83
|
-
profile: str = DEFAULT_PROFILE,
|
|
84
|
-
app_key: str = "",
|
|
85
|
-
payload: JSONObject | None = None,
|
|
86
|
-
) -> JSONObject:
|
|
87
|
-
return self.app_publish(profile=profile, app_key=app_key, payload=payload or {})
|
|
88
|
-
|
|
89
40
|
def app_list(self, *, profile: str, query: str = "", keyword: str = "", ship_auth: bool = False) -> JSONObject:
|
|
90
41
|
"""List current-user visible apps in the selected workspace."""
|
|
91
42
|
def runner(session_profile, context):
|
|
@@ -294,7 +245,7 @@ class AppTools(ToolBase):
|
|
|
294
245
|
return self._run(profile, runner, tool_name='应用详情')
|
|
295
246
|
|
|
296
247
|
def app_get_base(self, *, profile: str, app_key: str, include_raw: bool = False) -> JSONObject:
|
|
297
|
-
"""
|
|
248
|
+
"""Legacy low-level app-form adapter retained for internal callers pending deletion."""
|
|
298
249
|
self._require_app_key(app_key)
|
|
299
250
|
|
|
300
251
|
def runner(session_profile, context):
|
|
@@ -343,7 +294,7 @@ class AppTools(ToolBase):
|
|
|
343
294
|
return self._run(profile, runner, tool_name='应用基础信息')
|
|
344
295
|
|
|
345
296
|
def app_update_base(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
|
|
346
|
-
"""
|
|
297
|
+
"""Legacy low-level app-form adapter retained for internal callers pending deletion."""
|
|
347
298
|
self._require_app_key(app_key)
|
|
348
299
|
body = self._require_dict(payload)
|
|
349
300
|
|
|
@@ -368,7 +319,7 @@ class AppTools(ToolBase):
|
|
|
368
319
|
audit_node_id: int | None,
|
|
369
320
|
include_raw: bool = False,
|
|
370
321
|
) -> JSONObject:
|
|
371
|
-
"""
|
|
322
|
+
"""Legacy low-level app-form adapter retained for internal callers pending deletion."""
|
|
372
323
|
self._require_app_key(app_key)
|
|
373
324
|
resolved_form_type = _normalize_form_type(form_type)
|
|
374
325
|
|
|
@@ -398,7 +349,7 @@ class AppTools(ToolBase):
|
|
|
398
349
|
return self._run(profile, runner, tool_name='应用表单结构')
|
|
399
350
|
|
|
400
351
|
def app_get_edit_version_no(self, *, profile: str, app_key: str) -> JSONObject:
|
|
401
|
-
"""
|
|
352
|
+
"""Legacy low-level app-form adapter retained for internal callers pending deletion."""
|
|
402
353
|
self._require_app_key(app_key)
|
|
403
354
|
|
|
404
355
|
def runner(session_profile, context):
|
|
@@ -446,7 +397,7 @@ class AppTools(ToolBase):
|
|
|
446
397
|
return self._run(profile, runner, tool_name='更新应用流程配置')
|
|
447
398
|
|
|
448
399
|
def app_update_form_schema(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
|
|
449
|
-
"""
|
|
400
|
+
"""Legacy low-level app-form adapter retained for internal callers pending deletion."""
|
|
450
401
|
self._require_app_key(app_key)
|
|
451
402
|
body = self._require_dict(payload)
|
|
452
403
|
|
|
@@ -461,7 +412,7 @@ class AppTools(ToolBase):
|
|
|
461
412
|
return self._run(profile, runner, tool_name='更新应用表单结构')
|
|
462
413
|
|
|
463
414
|
def app_edit_finished(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
|
|
464
|
-
"""
|
|
415
|
+
"""Legacy low-level app-form adapter retained for internal callers pending deletion."""
|
|
465
416
|
self._require_app_key(app_key)
|
|
466
417
|
body = self._require_dict(payload)
|
|
467
418
|
|
|
@@ -471,13 +422,19 @@ class AppTools(ToolBase):
|
|
|
471
422
|
|
|
472
423
|
return self._run(profile, runner, tool_name='应用编辑完成')
|
|
473
424
|
|
|
474
|
-
def app_create(
|
|
475
|
-
|
|
425
|
+
def app_create(
|
|
426
|
+
self,
|
|
427
|
+
*,
|
|
428
|
+
profile: str,
|
|
429
|
+
payload: JSONObject,
|
|
430
|
+
retry_without_qf_version: bool = True,
|
|
431
|
+
) -> JSONObject:
|
|
432
|
+
"""Legacy low-level app-form adapter retained for internal callers pending deletion."""
|
|
476
433
|
body = self._require_dict(payload)
|
|
477
434
|
|
|
478
435
|
def runner(session_profile, context):
|
|
479
436
|
attempted_contexts = [context]
|
|
480
|
-
if context.qf_version is not None:
|
|
437
|
+
if retry_without_qf_version and context.qf_version is not None:
|
|
481
438
|
attempted_contexts.append(
|
|
482
439
|
BackendRequestContext(
|
|
483
440
|
base_url=context.base_url,
|
|
@@ -519,7 +476,7 @@ class AppTools(ToolBase):
|
|
|
519
476
|
return self._run(profile, runner, tool_name='创建应用')
|
|
520
477
|
|
|
521
478
|
def app_delete(self, *, profile: str, app_key: str) -> JSONObject:
|
|
522
|
-
"""
|
|
479
|
+
"""Legacy low-level app-form adapter retained for the declarative delete path pending deletion."""
|
|
523
480
|
self._require_app_key(app_key)
|
|
524
481
|
|
|
525
482
|
def runner(session_profile, context):
|
|
@@ -533,7 +490,7 @@ class AppTools(ToolBase):
|
|
|
533
490
|
return self._run(profile, runner, tool_name='删除应用')
|
|
534
491
|
|
|
535
492
|
def app_publish(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
|
|
536
|
-
"""
|
|
493
|
+
"""Legacy low-level app-form adapter retained for internal callers pending deletion."""
|
|
537
494
|
self._require_app_key(app_key)
|
|
538
495
|
body = self._require_dict(payload)
|
|
539
496
|
|
|
@@ -543,6 +500,30 @@ class AppTools(ToolBase):
|
|
|
543
500
|
|
|
544
501
|
return self._run(profile, runner, tool_name='发布应用')
|
|
545
502
|
|
|
503
|
+
def app_publish_raw(self, *, profile: str, app_key: str, payload: JSONObject) -> JSONObject:
|
|
504
|
+
"""Legacy low-level publish adapter retained for internal callers pending deletion."""
|
|
505
|
+
self._require_app_key(app_key)
|
|
506
|
+
body = self._require_dict(payload)
|
|
507
|
+
|
|
508
|
+
def runner(session_profile, context):
|
|
509
|
+
response = self.backend.request_with_meta(
|
|
510
|
+
"POST",
|
|
511
|
+
context,
|
|
512
|
+
f"/app/{app_key}/publish",
|
|
513
|
+
json_body=body,
|
|
514
|
+
unwrap=False,
|
|
515
|
+
)
|
|
516
|
+
return {
|
|
517
|
+
"profile": profile,
|
|
518
|
+
"ws_id": session_profile.selected_ws_id,
|
|
519
|
+
"app_key": app_key,
|
|
520
|
+
"http_status": response.http_status,
|
|
521
|
+
"request_id": response.request_id,
|
|
522
|
+
"response": response.data,
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
return self._run(profile, runner, tool_name='发布应用')
|
|
526
|
+
|
|
546
527
|
def _require_app_key(self, app_key: str) -> None:
|
|
547
528
|
"""执行内部辅助逻辑。"""
|
|
548
529
|
if not app_key:
|