@qingflow-tech/qingflow-cli 1.1.50-beta.5 → 1.1.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/npm/lib/runtime.mjs +13 -29
- package/package.json +1 -1
- package/pyproject.toml +2 -8
- package/skills/qingflow-cli/SKILL.md +322 -93
- package/skills/qingflow-cli/manifest.yaml +1 -1
- package/skills/qingflow-cli/reference/00-INDEX.md +4 -4
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_ADMIN_CHEATSHEET.md +1 -1
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_VIEWS_WORKFLOW.md +9 -3
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md +137 -3
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_EXPLORATION_REPORT.md +4 -6
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_FIELD_DATA_TYPES.md +14 -14
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_MEMBER_CHEATSHEET.md +19 -20
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_ONE_SHOT_CHEATSHEET.md +14 -18
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_RECORD_CREATE_WORKFLOW.md +12 -7
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_RECORD_UPDATE_WORKFLOW.md +1 -2
- package/skills/qingflow-cli/reference/builder/20-build-complete-system.md +2 -3
- package/skills/qingflow-cli/reference/builder/30-schema-fields.md +5 -9
- package/skills/qingflow-cli/reference/builder/50-views.md +191 -192
- package/skills/qingflow-cli/reference/builder/80-buttons-associated-resources.md +0 -2
- package/skills/qingflow-cli/reference/builder/README.md +1 -1
- package/skills/qingflow-cli/reference/builder/reference/app-delivery-sop.md +6 -6
- package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_ADMIN_CHEATSHEET.md +1 -1
- package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md +95 -76
- package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_EXPLORATION_REPORT.md +4 -6
- package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_FIELD_DATA_TYPES.md +13 -13
- package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_MEMBER_CHEATSHEET.md +19 -20
- package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_ONE_SHOT_CHEATSHEET.md +14 -18
- package/skills/qingflow-cli/reference/examples/README.md +1 -1
- package/skills/qingflow-cli/reference/examples/views/views_batch_full.example.json +159 -0
- package/skills/qingflow-cli/reference/examples/views/views_upsert_table_minimal.example.json +36 -30
- package/skills/qingflow-cli/reference/record/QINGFLOW_CLI_RECORD_UPDATE_WORKFLOW.md +1 -2
- package/skills/qingflow-cli/reference/record/analysis/README.md +113 -21
- package/skills/qingflow-cli/reference/record/analysis/analysis-gotchas.md +10 -10
- package/skills/qingflow-cli/reference/record/analysis/analysis-patterns.md +9 -9
- package/skills/qingflow-cli/reference/record/analysis/business-context.md +1 -1
- package/skills/qingflow-cli/reference/record/analysis/confidence-reporting.md +10 -10
- package/skills/qingflow-cli/reference/record/analysis/data-access-playbook.md +80 -52
- package/skills/qingflow-cli/reference/record/analysis/pandas-recipes.md +2 -2
- package/skills/qingflow-cli/reference/views_upsert_table_minimal.example.json +42 -0
- package/skills/qingflow-common/SKILL.md +4 -7
- package/src/qingflow_mcp/__init__.py +1 -1
- package/src/qingflow_mcp/app_form_compat.py +0 -3
- package/src/qingflow_mcp/app_form_validator.py +17 -69
- package/src/qingflow_mcp/builder_facade/app_form.py +100 -497
- package/src/qingflow_mcp/builder_facade/models.py +17 -227
- package/src/qingflow_mcp/builder_facade/schemas/app_form/manifest.json +0 -1
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/complete.json +30 -64
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/address.json +8 -4
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/amount.json +8 -4
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/attachment.json +10 -6
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/code_block.json +10 -6
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/data_relation.json +18 -11
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/date.json +8 -4
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/datetime.json +8 -4
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/department.json +10 -6
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/email.json +8 -4
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/link.json +8 -4
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/long_text.json +8 -4
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/member.json +10 -6
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/multi_select.json +8 -4
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/number.json +8 -4
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/phone.json +8 -4
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/q_linker.json +10 -6
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/relation.json +15 -46
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/single_choice.json +8 -4
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/single_select.json +8 -4
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/subtable.json +27 -61
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/text.json +8 -4
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/formula.json +0 -43
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/summary.json +12 -7
- package/src/qingflow_mcp/builder_facade/service.py +539 -1671
- package/src/qingflow_mcp/builder_facade/workflow_spec.py +1 -1
- package/src/qingflow_mcp/cli/commands/__init__.py +1 -2
- package/src/qingflow_mcp/cli/commands/builder.py +55 -76
- package/src/qingflow_mcp/cli/commands/record.py +1 -1
- package/src/qingflow_mcp/cli/context.py +0 -3
- package/src/qingflow_mcp/cli/formatters.py +1 -97
- package/src/qingflow_mcp/config.py +1 -0
- package/src/qingflow_mcp/public_surface.py +0 -8
- package/src/qingflow_mcp/response_trim.py +2 -11
- package/src/qingflow_mcp/server.py +0 -2
- package/src/qingflow_mcp/server_app_builder.py +10 -34
- package/src/qingflow_mcp/server_app_user.py +0 -4
- package/src/qingflow_mcp/solution/compiler/view_compiler.py +1 -2
- package/src/qingflow_mcp/solution/executor.py +8 -96
- package/src/qingflow_mcp/solution/spec_models.py +0 -1
- package/src/qingflow_mcp/tools/ai_builder_tools.py +398 -218
- package/src/qingflow_mcp/tools/base.py +42 -10
- package/src/qingflow_mcp/tools/record_tools.py +46 -11
- package/src/qingflow_mcp/tools/solution_tools.py +0 -2
- package/src/qingflow_mcp/tools/view_tools.py +6 -0
- package/skills/qingflow-cli/reference/data/README.md +0 -32
- package/skills/qingflow-cli/reference/examples/views/views_apply_v1alpha1.example.json +0 -14
- package/skills/qingflow-cli/reference/record/QINGFLOW_CLI_RECORD_SPECIALIZED_WORKFLOW.md +0 -50
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/rich_text.json +0 -146
- package/src/qingflow_mcp/builder_facade/schemas/views/manifest.json +0 -42
- package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/complete.json +0 -1245
- package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/details/button-config.json +0 -53
- package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/details/data-permission.json +0 -60
- package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/details/display.json +0 -73
- package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/details/operation-permission.json +0 -48
- package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/details/query-condition.json +0 -47
- package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/summary.json +0 -319
- package/src/qingflow_mcp/builder_facade/views_extract.py +0 -592
- package/src/qingflow_mcp/builder_facade/views_schema.py +0 -400
- package/src/qingflow_mcp/cli/commands/data.py +0 -134
- package/src/qingflow_mcp/tools/csv_export.py +0 -168
- package/src/qingflow_mcp/tools/data_tools.py +0 -284
- package/src/qingflow_mcp/tools/view_writer.py +0 -44
|
@@ -13,7 +13,7 @@ WORKFLOW_SPEC_APPLY_PATH = "/api/workflow/spec:apply"
|
|
|
13
13
|
# 若后端实际路径有出入,只需改这里的两个常量。
|
|
14
14
|
WORKFLOW_SPEC_VERSIONS_PATH = "/api/workflow/spec/versions"
|
|
15
15
|
WORKFLOW_SPEC_RESTORE_PATH = "/api/workflow/spec:restore"
|
|
16
|
-
DEFAULT_SCHEMA_VERSION = "vnext-2026-
|
|
16
|
+
DEFAULT_SCHEMA_VERSION = "vnext-2026-09"
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def fetch_schema(
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
4
|
|
|
5
|
-
from . import app, auth, builder, chart,
|
|
5
|
+
from . import app, auth, builder, chart, exports, imports, portal, record, task, view, workspace
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def register_all_commands(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
|
|
@@ -13,7 +13,6 @@ def register_all_commands(subparsers: argparse._SubParsersAction[argparse.Argume
|
|
|
13
13
|
view.register(subparsers)
|
|
14
14
|
chart.register(subparsers)
|
|
15
15
|
record.register(subparsers)
|
|
16
|
-
data.register(subparsers)
|
|
17
16
|
imports.register(subparsers)
|
|
18
17
|
exports.register(subparsers)
|
|
19
18
|
task.register(subparsers)
|
|
@@ -4,30 +4,24 @@ import argparse
|
|
|
4
4
|
from copy import deepcopy
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
|
+
from ..context import CliContext
|
|
7
8
|
from ...app_form_validator import (
|
|
8
9
|
AppFormValidationError,
|
|
9
10
|
build_encoded_id_map,
|
|
11
|
+
referenced_formula_field_ids,
|
|
12
|
+
translate_formula_test_case_values,
|
|
10
13
|
encode_code_block_field_ids,
|
|
11
14
|
referenced_code_block_field_ids,
|
|
12
|
-
referenced_formula_field_ids,
|
|
13
15
|
restore_decimal_ids_in_text,
|
|
14
16
|
strip_placeholder_quotes,
|
|
15
|
-
translate_formula_test_case_values,
|
|
16
17
|
translate_select_test_case_values,
|
|
17
18
|
validate_code_block_static,
|
|
18
19
|
validate_declaration,
|
|
19
20
|
validate_formula_expression,
|
|
20
21
|
)
|
|
21
|
-
from ...builder_facade.views_schema import VIEWS_SUPPORTED_SCHEMA_KINDS
|
|
22
|
-
from ..context import CliContext
|
|
23
22
|
from ..json_io import load_json_value
|
|
24
|
-
from .common import
|
|
25
|
-
|
|
26
|
-
load_object_arg,
|
|
27
|
-
parse_bool_text,
|
|
28
|
-
raise_config_error,
|
|
29
|
-
require_list_arg,
|
|
30
|
-
)
|
|
23
|
+
from .common import load_list_arg, load_object_arg, parse_bool_text, raise_config_error, require_list_arg
|
|
24
|
+
|
|
31
25
|
|
|
32
26
|
def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
|
|
33
27
|
parser = subparsers.add_parser("builder", aliases=["build"], help="稳定 builder 命令")
|
|
@@ -225,7 +219,6 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
225
219
|
button_get.add_argument("--app-key", default="")
|
|
226
220
|
button_get.add_argument("--app-keys", default="", help="逗号分隔的多应用 app_key;用于批量读取按钮配置")
|
|
227
221
|
button_get.add_argument("--app-keys-file", help="JSON 字符串数组;用于批量读取按钮配置")
|
|
228
|
-
button_get.add_argument("--button-text", default="", help="按按钮文案精确查重;命中复用其 button_id,未命中再创建")
|
|
229
222
|
button_get.set_defaults(handler=_handle_button_get, format_hint="builder_summary")
|
|
230
223
|
|
|
231
224
|
button_apply = button_subparsers.add_parser("apply", help="声明式创建、更新或删除自定义按钮")
|
|
@@ -289,32 +282,14 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
289
282
|
|
|
290
283
|
views_apply = builder_subparsers.add_parser("views", help="视图")
|
|
291
284
|
views_apply_subparsers = views_apply.add_subparsers(dest="builder_views_command", required=True)
|
|
292
|
-
views_schema = views_apply_subparsers.add_parser("schema", help="读取视图声明 Schema")
|
|
293
|
-
views_schema.add_argument("--schema-version")
|
|
294
|
-
views_schema.add_argument(
|
|
295
|
-
"--schema-kind",
|
|
296
|
-
choices=VIEWS_SUPPORTED_SCHEMA_KINDS,
|
|
297
|
-
)
|
|
298
|
-
views_schema.set_defaults(handler=_handle_views_schema, format_hint="builder_summary", force_json_output=True)
|
|
299
|
-
|
|
300
|
-
views_get = views_apply_subparsers.add_parser("get", help="读取 canonical 视图声明")
|
|
301
|
-
views_get.add_argument("--app-key", required=True)
|
|
302
|
-
views_get.add_argument("--schema-version")
|
|
303
|
-
views_get.add_argument("--schema-kind", choices=("summary", "complete"), help="summary=投影字段;complete=逐视图读 viewConfig 反向编译(含覆盖度 gaps)")
|
|
304
|
-
views_get.set_defaults(handler=_handle_views_get, format_hint="builder_summary", force_json_output=True)
|
|
305
|
-
|
|
306
|
-
views_validate = views_apply_subparsers.add_parser("validate", help="只校验视图声明,不访问后端")
|
|
307
|
-
views_validate.add_argument("--file", required=True, help="视图声明 JSON 文件,- 表示 stdin")
|
|
308
|
-
views_validate.add_argument("--schema-version", help="固定使用的视图 Schema 版本;缺省读文件内 schema_version,再缺省 latest")
|
|
309
|
-
views_validate.set_defaults(handler=_handle_views_validate, format_hint="builder_summary", force_json_output=True)
|
|
310
|
-
|
|
311
285
|
views_apply_apply = views_apply_subparsers.add_parser("apply", help="执行视图变更")
|
|
312
|
-
views_apply_apply.add_argument("--app-key",
|
|
286
|
+
views_apply_apply.add_argument("--app-key", default="")
|
|
287
|
+
views_apply_apply.add_argument("--views-file", help='推荐批量视图 JSON 对象:{"views":[...]};每项包含 app_key、operation、视图配置')
|
|
288
|
+
views_apply_apply.add_argument("--apps-file", help="多应用视图 JSON 数组;每项包含 app_key 和 upsert/patch/remove payload")
|
|
313
289
|
views_apply_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=True)
|
|
314
|
-
views_apply_apply.add_argument("--
|
|
290
|
+
views_apply_apply.add_argument("--upsert-views-file")
|
|
315
291
|
views_apply_apply.add_argument("--patch-views-file")
|
|
316
292
|
views_apply_apply.add_argument("--remove-views-file")
|
|
317
|
-
views_apply_apply.add_argument("--schema-version", help="声明使用的 Schema 版本;缺省 latest,信封回显 schemaVersion")
|
|
318
293
|
views_apply_apply.set_defaults(handler=_handle_views_apply, format_hint="builder_summary", force_json_output=True)
|
|
319
294
|
|
|
320
295
|
flow_apply = builder_subparsers.add_parser("flow", help="流程")
|
|
@@ -899,13 +874,12 @@ def _handle_associated_resource_apply(args: argparse.Namespace, context: CliCont
|
|
|
899
874
|
|
|
900
875
|
|
|
901
876
|
def _handle_button_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
902
|
-
button_text = str(getattr(args, "button_text", "") or "").strip() or None
|
|
903
877
|
app_keys = _app_keys_from_args(args)
|
|
904
878
|
if app_keys is not None:
|
|
905
|
-
return context.builder.app_get_buttons(profile=args.profile, app_key="", app_keys=app_keys
|
|
879
|
+
return context.builder.app_get_buttons(profile=args.profile, app_key="", app_keys=app_keys)
|
|
906
880
|
if not (args.app_key or "").strip():
|
|
907
881
|
raise_config_error("builder button get requires --app-key unless --app-keys or --app-keys-file is provided")
|
|
908
|
-
return context.builder.app_get_buttons(profile=args.profile, app_key=args.app_key
|
|
882
|
+
return context.builder.app_get_buttons(profile=args.profile, app_key=args.app_key)
|
|
909
883
|
|
|
910
884
|
|
|
911
885
|
def _handle_associated_resource_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
@@ -1213,6 +1187,30 @@ def _coerce_apps_file_bool(value: object, *, field_name: str, default: bool) ->
|
|
|
1213
1187
|
)
|
|
1214
1188
|
|
|
1215
1189
|
|
|
1190
|
+
def _load_views_file_arg(path: str | None) -> list[object] | None:
|
|
1191
|
+
if not path:
|
|
1192
|
+
return None
|
|
1193
|
+
payload = load_json_value(path, option_name="--views-file")
|
|
1194
|
+
if isinstance(payload, dict):
|
|
1195
|
+
views = payload.get("views")
|
|
1196
|
+
if not isinstance(views, list):
|
|
1197
|
+
raise_config_error(
|
|
1198
|
+
"--views-file JSON object must contain a views array.",
|
|
1199
|
+
fix_hint='Use {"views": [{"operation": "upsert", "app_key": "...", "name": "...", "type": "table", "columns": [...]}]}.',
|
|
1200
|
+
error_code="VIEWS_FILE_SHAPE_INVALID",
|
|
1201
|
+
details={"expected": '{"views": [...]}', "actual_type": type(views).__name__},
|
|
1202
|
+
)
|
|
1203
|
+
return views
|
|
1204
|
+
if isinstance(payload, list):
|
|
1205
|
+
return payload
|
|
1206
|
+
raise_config_error(
|
|
1207
|
+
"--views-file must be a JSON object with views[] or a JSON array.",
|
|
1208
|
+
fix_hint='Prefer {"views": [...]} for new automation. A bare array is accepted only for compatibility.',
|
|
1209
|
+
error_code="VIEWS_FILE_SHAPE_INVALID",
|
|
1210
|
+
details={"actual_type": type(payload).__name__},
|
|
1211
|
+
)
|
|
1212
|
+
|
|
1213
|
+
|
|
1216
1214
|
def _handle_layout_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
1217
1215
|
apps = load_list_arg(args.apps_file, option_name="--apps-file") if getattr(args, "apps_file", None) else None
|
|
1218
1216
|
if apps is None and not (args.app_key or "").strip():
|
|
@@ -1237,53 +1235,34 @@ def _handle_layout_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
1237
1235
|
)
|
|
1238
1236
|
|
|
1239
1237
|
|
|
1240
|
-
def _handle_views_schema(args: argparse.Namespace, context: CliContext) -> dict:
|
|
1241
|
-
return context.builder.app_views_schema(
|
|
1242
|
-
schema_version=args.schema_version,
|
|
1243
|
-
schema_kind=args.schema_kind,
|
|
1244
|
-
)
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
def _handle_views_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
1248
|
-
return context.builder.app_views_get(
|
|
1249
|
-
profile=args.profile,
|
|
1250
|
-
app_key=args.app_key,
|
|
1251
|
-
schema_version=args.schema_version,
|
|
1252
|
-
schema_kind=getattr(args, "schema_kind", None) or "summary",
|
|
1253
|
-
)
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
def _handle_views_validate(args: argparse.Namespace, context: CliContext) -> dict:
|
|
1257
|
-
payload = load_json_value(
|
|
1258
|
-
args.file,
|
|
1259
|
-
option_name="--file",
|
|
1260
|
-
max_bytes=2 * 1024 * 1024,
|
|
1261
|
-
max_depth=64,
|
|
1262
|
-
)
|
|
1263
|
-
if not isinstance(payload, dict):
|
|
1264
|
-
raise_config_error("builder views validate --file must contain a JSON object")
|
|
1265
|
-
return context.builder.app_views_validate(
|
|
1266
|
-
declaration=payload,
|
|
1267
|
-
schema_version=args.schema_version,
|
|
1268
|
-
)
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
1238
|
def _handle_views_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1239
|
+
views = _load_views_file_arg(args.views_file) if getattr(args, "views_file", None) else None
|
|
1240
|
+
apps = load_list_arg(args.apps_file, option_name="--apps-file") if getattr(args, "apps_file", None) else None
|
|
1241
|
+
if views is not None:
|
|
1242
|
+
if apps is not None or (args.app_key or "").strip() or getattr(args, "upsert_views_file", None) or getattr(args, "patch_views_file", None) or getattr(args, "remove_views_file", None):
|
|
1243
|
+
raise_config_error(
|
|
1244
|
+
"builder views apply --views-file cannot be mixed with --app-key, --apps-file, --upsert-views-file, --patch-views-file, or --remove-views-file.",
|
|
1245
|
+
fix_hint="Use one shape: preferred --views-file for flat batch jobs, or legacy --app-key/--apps-file for grouped payloads.",
|
|
1246
|
+
)
|
|
1247
|
+
return context.builder.app_views_apply(
|
|
1248
|
+
profile=args.profile,
|
|
1249
|
+
app_key="",
|
|
1250
|
+
publish=bool(args.publish),
|
|
1251
|
+
upsert_views=[],
|
|
1252
|
+
patch_views=[],
|
|
1253
|
+
remove_views=[],
|
|
1254
|
+
views=views,
|
|
1278
1255
|
)
|
|
1256
|
+
if apps is None and not (args.app_key or "").strip():
|
|
1257
|
+
raise_config_error("builder views apply requires --views-file, --apps-file, or --app-key")
|
|
1279
1258
|
return context.builder.app_views_apply(
|
|
1280
1259
|
profile=args.profile,
|
|
1281
1260
|
app_key=args.app_key,
|
|
1282
1261
|
publish=bool(args.publish),
|
|
1283
|
-
|
|
1262
|
+
upsert_views=load_list_arg(args.upsert_views_file, option_name="--upsert-views-file"),
|
|
1284
1263
|
patch_views=load_list_arg(args.patch_views_file, option_name="--patch-views-file"),
|
|
1285
1264
|
remove_views=load_list_arg(args.remove_views_file, option_name="--remove-views-file"),
|
|
1286
|
-
|
|
1265
|
+
apps=apps,
|
|
1287
1266
|
)
|
|
1288
1267
|
|
|
1289
1268
|
|
|
@@ -9,7 +9,7 @@ from .common import load_list_arg, load_object_arg, raise_config_error, require_
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
|
|
12
|
-
parser = subparsers.add_parser("record", help="
|
|
12
|
+
parser = subparsers.add_parser("record", help="记录与表结构")
|
|
13
13
|
record_subparsers = parser.add_subparsers(
|
|
14
14
|
dest="record_command",
|
|
15
15
|
required=True,
|
|
@@ -11,7 +11,6 @@ from ..tools.ai_builder_tools import AiBuilderTools
|
|
|
11
11
|
from ..tools.app_tools import AppTools
|
|
12
12
|
from ..tools.auth_tools import AuthTools
|
|
13
13
|
from ..tools.code_block_tools import CodeBlockTools
|
|
14
|
-
from ..tools.data_tools import DataTools
|
|
15
14
|
from ..tools.export_tools import ExportTools
|
|
16
15
|
from ..tools.feedback_tools import FeedbackTools
|
|
17
16
|
from ..tools.file_tools import FileTools
|
|
@@ -34,7 +33,6 @@ class CliContext:
|
|
|
34
33
|
app: AppTools
|
|
35
34
|
resource: ResourceReadTools
|
|
36
35
|
record: RecordTools
|
|
37
|
-
data: DataTools
|
|
38
36
|
code_block: CodeBlockTools
|
|
39
37
|
imports: ImportTools
|
|
40
38
|
exports: ExportTools
|
|
@@ -66,7 +64,6 @@ def build_cli_context() -> CliContext:
|
|
|
66
64
|
app=AppTools(sessions, backend),
|
|
67
65
|
resource=ResourceReadTools(sessions, backend),
|
|
68
66
|
record=RecordTools(sessions, backend),
|
|
69
|
-
data=DataTools(sessions, backend),
|
|
70
67
|
code_block=CodeBlockTools(sessions, backend),
|
|
71
68
|
imports=ImportTools(sessions, backend),
|
|
72
69
|
exports=ExportTools(sessions, backend),
|
|
@@ -8,10 +8,7 @@ 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
|
-
if hint
|
|
12
|
-
# 数据面失败也必须保留 field_errors 与列表型 hints,不能先走通用失败裁剪。
|
|
13
|
-
text = _format_data_result(result)
|
|
14
|
-
elif _is_failed_result(result) and hint != "task_action_execute":
|
|
11
|
+
if _is_failed_result(result) and hint != "task_action_execute":
|
|
15
12
|
text = _format_failed_result(result)
|
|
16
13
|
else:
|
|
17
14
|
formatter = _FORMATTERS.get(hint, _format_generic)
|
|
@@ -93,92 +90,6 @@ def _format_generic(result: dict[str, Any]) -> str:
|
|
|
93
90
|
return "\n".join(lines) + "\n"
|
|
94
91
|
|
|
95
92
|
|
|
96
|
-
def _format_data_result(result: dict[str, Any]) -> str:
|
|
97
|
-
lines = [f"Status: {result.get('status') or ('success' if result.get('ok') is not False else 'failed')}"]
|
|
98
|
-
if result.get("format") == "csv":
|
|
99
|
-
files = result.get("files") if isinstance(result.get("files"), list) else []
|
|
100
|
-
lines.extend(
|
|
101
|
-
[
|
|
102
|
-
f"Local dir: {result.get('local_dir') or '-'}",
|
|
103
|
-
f"Files: {len(files)}",
|
|
104
|
-
]
|
|
105
|
-
)
|
|
106
|
-
for item in files:
|
|
107
|
-
if isinstance(item, dict):
|
|
108
|
-
lines.append(
|
|
109
|
-
f"- part {item.get('part')}: {item.get('local_path')} ({item.get('row_count')} rows)"
|
|
110
|
-
)
|
|
111
|
-
lines.extend(
|
|
112
|
-
[
|
|
113
|
-
f"Rows: {result.get('row_count')}",
|
|
114
|
-
f"Complete: {result.get('complete')}",
|
|
115
|
-
f"Truncated: {result.get('truncated')}",
|
|
116
|
-
f"Safe for final conclusion: {result.get('safe_for_final_conclusion')}",
|
|
117
|
-
f"Stopped reason: {result.get('stopped_reason') or '-'}",
|
|
118
|
-
]
|
|
119
|
-
)
|
|
120
|
-
access = result.get("access") if isinstance(result.get("access"), dict) else {}
|
|
121
|
-
if access:
|
|
122
|
-
sources = access.get("scope_sources") if isinstance(access.get("scope_sources"), list) else []
|
|
123
|
-
lines.append(
|
|
124
|
-
f"Access: level={access.get('level') or '-'} / can_write={access.get('can_write')}"
|
|
125
|
-
f" / scope_sources={','.join(str(item) for item in sources) or '-'}"
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
records = result.get("records") if isinstance(result.get("records"), dict) else {}
|
|
129
|
-
if records:
|
|
130
|
-
items = records.get("items") if isinstance(records.get("items"), list) else []
|
|
131
|
-
lines.append(
|
|
132
|
-
"Pagination: "
|
|
133
|
-
f"page={records.get('page')} / requested_page_size={records.get('requested_page_size')} / "
|
|
134
|
-
f"page_size={records.get('page_size')} / total={records.get('total')} / "
|
|
135
|
-
f"has_more={records.get('has_more')} / returned={len(items)}"
|
|
136
|
-
)
|
|
137
|
-
record = result.get("record") if isinstance(result.get("record"), dict) else {}
|
|
138
|
-
if record:
|
|
139
|
-
lines.append(f"Record ID: {record.get('record_id') or '-'}")
|
|
140
|
-
items = result.get("items") if isinstance(result.get("items"), list) else []
|
|
141
|
-
if items:
|
|
142
|
-
lines.append(f"Items: {len(items)}")
|
|
143
|
-
|
|
144
|
-
for key, label in (
|
|
145
|
-
("error_code", "Error Code"),
|
|
146
|
-
("message", "Message"),
|
|
147
|
-
("write_executed", "Write Executed"),
|
|
148
|
-
("retry_safe", "Retry Safe"),
|
|
149
|
-
):
|
|
150
|
-
if key in result:
|
|
151
|
-
lines.append(f"{label}: {result.get(key)}")
|
|
152
|
-
|
|
153
|
-
field_errors = result.get("field_errors") if isinstance(result.get("field_errors"), list) else []
|
|
154
|
-
if field_errors:
|
|
155
|
-
lines.append("Field Errors:")
|
|
156
|
-
_append_data_field_errors(lines, field_errors)
|
|
157
|
-
for index, item in enumerate(items):
|
|
158
|
-
if not isinstance(item, dict) or not isinstance(item.get("field_errors"), list):
|
|
159
|
-
continue
|
|
160
|
-
lines.append(f"Item {index} Field Errors:")
|
|
161
|
-
_append_data_field_errors(lines, item["field_errors"])
|
|
162
|
-
|
|
163
|
-
hints = result.get("hints") if isinstance(result.get("hints"), list) else []
|
|
164
|
-
lines.append(f"Hints: {len(hints)}")
|
|
165
|
-
for hint in hints:
|
|
166
|
-
if isinstance(hint, dict):
|
|
167
|
-
lines.append(f"- {hint.get('code') or '-'}: {hint.get('message') or '-'}")
|
|
168
|
-
else:
|
|
169
|
-
lines.append(f"- {hint}")
|
|
170
|
-
return "\n".join(lines) + "\n"
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
def _append_data_field_errors(lines: list[str], field_errors: list[Any]) -> None:
|
|
174
|
-
for error in field_errors:
|
|
175
|
-
if isinstance(error, dict):
|
|
176
|
-
lines.append(
|
|
177
|
-
f"- {error.get('path') or '-'}: {error.get('message') or '-'}"
|
|
178
|
-
f" / fix_hint={error.get('fix_hint') or '-'}"
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
|
|
182
93
|
def _format_whoami(result: dict[str, Any]) -> str:
|
|
183
94
|
lines = [
|
|
184
95
|
f"User: {result.get('nick_name') or '-'} ({result.get('email') or '-'})",
|
|
@@ -1758,11 +1669,4 @@ _FORMATTERS = {
|
|
|
1758
1669
|
"builder_summary": _format_builder_summary,
|
|
1759
1670
|
"file_upload_local": _format_file_upload_local,
|
|
1760
1671
|
"feedback_submit": _format_feedback_submit,
|
|
1761
|
-
"data_query": _format_data_result,
|
|
1762
|
-
"data_get": _format_data_result,
|
|
1763
|
-
"data_insert": _format_data_result,
|
|
1764
|
-
"data_update": _format_data_result,
|
|
1765
|
-
"data_delete": _format_data_result,
|
|
1766
1672
|
}
|
|
1767
|
-
|
|
1768
|
-
_DATA_FORMAT_HINTS = {"data_query", "data_get", "data_insert", "data_update", "data_delete"}
|
|
@@ -26,6 +26,7 @@ DEFAULT_REPOSITORY_PROD_BRANCH = "prod"
|
|
|
26
26
|
DEFAULT_REPOSITORY_AUTHOR_NAME = "qingflow-mcp"
|
|
27
27
|
DEFAULT_REPOSITORY_AUTHOR_EMAIL = "qingflow-mcp@local.invalid"
|
|
28
28
|
DEFAULT_REPOSITORY_INTERNAL_SHARE_TOKEN_KEY = "tokenKey"
|
|
29
|
+
DEFAULT_MEMBER_CREDIT_AVAILABILITY_PATH = "/credit/member/availability"
|
|
29
30
|
DEFAULT_CREDIT_USAGE_RECORD_PATH = "/user/credit/usage"
|
|
30
31
|
DEFAULT_MCPORTER_CONFIG_PATH = "~/.openclaw/workspace/config/mcporter.json"
|
|
31
32
|
DEFAULT_CLOUD_AUTH_CONTEXT_TTL_SECONDS = 604800
|
|
@@ -40,11 +40,6 @@ USER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
|
|
|
40
40
|
PublicToolSpec(USER_DOMAIN, "workspace_select", ("workspace_select",), ("workspace", "select")),
|
|
41
41
|
PublicToolSpec(USER_DOMAIN, "app_list", ("app_list",), ("app", "list"), cli_show_effective_context=True),
|
|
42
42
|
PublicToolSpec(USER_DOMAIN, "app_get", ("app_get",), ("app", "get"), cli_show_effective_context=True),
|
|
43
|
-
PublicToolSpec(USER_DOMAIN, "data_query", ("data_query",), ("data", "query"), cli_show_effective_context=True),
|
|
44
|
-
PublicToolSpec(USER_DOMAIN, "data_get", ("data_get",), ("data", "get"), cli_show_effective_context=True),
|
|
45
|
-
PublicToolSpec(USER_DOMAIN, "data_insert", ("data_insert",), ("data", "insert"), cli_show_effective_context=True, cli_context_write=True),
|
|
46
|
-
PublicToolSpec(USER_DOMAIN, "data_update", ("data_update",), ("data", "update"), cli_show_effective_context=True, cli_context_write=True),
|
|
47
|
-
PublicToolSpec(USER_DOMAIN, "data_delete", ("data_delete",), ("data", "delete"), cli_show_effective_context=True, cli_context_write=True),
|
|
48
43
|
PublicToolSpec(USER_DOMAIN, "portal_list", ("portal_list",), ("portal", "list"), cli_show_effective_context=True),
|
|
49
44
|
PublicToolSpec(USER_DOMAIN, "portal_get", ("portal_get",), ("portal", "get"), cli_show_effective_context=True),
|
|
50
45
|
PublicToolSpec(USER_DOMAIN, "view_get", ("view_get",), ("view", "get"), cli_show_effective_context=True),
|
|
@@ -178,9 +173,6 @@ BUILDER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
|
|
|
178
173
|
PublicToolSpec(BUILDER_DOMAIN, "app_flow_list_versions", ("app_flow_list_versions",), ("builder", "flow", "versions"), has_contract=True, cli_show_effective_context=True),
|
|
179
174
|
PublicToolSpec(BUILDER_DOMAIN, "app_flow_restore_version", ("app_flow_restore_version",), ("builder", "flow", "restore"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
180
175
|
PublicToolSpec(BUILDER_DOMAIN, "app_flow_apply", ("app_flow_apply",), ("builder", "flow", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
181
|
-
PublicToolSpec(BUILDER_DOMAIN, "app_views_schema", ("app_views_schema",), ("builder", "views", "schema")),
|
|
182
|
-
PublicToolSpec(BUILDER_DOMAIN, "app_views_get", ("app_views_get",), ("builder", "views", "get"), cli_show_effective_context=True),
|
|
183
|
-
PublicToolSpec(BUILDER_DOMAIN, "app_views_validate", (), ("builder", "views", "validate"), mcp_public=True),
|
|
184
176
|
PublicToolSpec(BUILDER_DOMAIN, "app_views_apply", ("app_views_apply",), ("builder", "views", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
185
177
|
PublicToolSpec(BUILDER_DOMAIN, "app_charts_apply", ("app_charts_apply",), ("builder", "charts", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
186
178
|
PublicToolSpec(BUILDER_DOMAIN, "portal_apply", ("portal_apply",), ("builder", "portal", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
@@ -51,15 +51,9 @@ COMMON_ERROR_DROP_TOP = {
|
|
|
51
51
|
|
|
52
52
|
SUCCESS_POLICY_BY_TOOL: dict[str, TransformFn] = {}
|
|
53
53
|
|
|
54
|
-
#:
|
|
54
|
+
#: 这两个校验命令的产出契约明确包含 ok 字段(供阶段 2 SKILL.md 引用),
|
|
55
55
|
#: 需在 success 裁剪时保留 ok(默认 COMMON_SUCCESS_DROP_TOP 会丢弃 ok)。
|
|
56
|
-
|
|
57
|
-
_KEEP_OK_TOOLS = {
|
|
58
|
-
"builder:app_form_validate_formula",
|
|
59
|
-
"builder:app_form_validate_code_block",
|
|
60
|
-
"builder:app_flow_validate",
|
|
61
|
-
"builder:app_views_validate",
|
|
62
|
-
}
|
|
56
|
+
_KEEP_OK_TOOLS = {"builder:app_form_validate_formula", "builder:app_form_validate_code_block", "builder:app_flow_validate"}
|
|
63
57
|
|
|
64
58
|
|
|
65
59
|
def trim_public_response(tool_name: str | None, payload: dict[str, Any]) -> dict[str, Any]:
|
|
@@ -1316,9 +1310,6 @@ _register_policy(
|
|
|
1316
1310
|
"app_flow_validate",
|
|
1317
1311
|
"app_flow_list_versions",
|
|
1318
1312
|
"app_flow_restore_version",
|
|
1319
|
-
"app_views_schema",
|
|
1320
|
-
"app_views_get",
|
|
1321
|
-
"app_views_validate",
|
|
1322
1313
|
"app_get_charts",
|
|
1323
1314
|
"app_get_buttons",
|
|
1324
1315
|
"app_get_associated_resources",
|
|
@@ -7,7 +7,6 @@ from mcp.server.fastmcp import FastMCP
|
|
|
7
7
|
from .backend_client import BackendClient
|
|
8
8
|
from .session_store import SessionStore
|
|
9
9
|
from .tools.app_tools import AppTools
|
|
10
|
-
from .tools.data_tools import DataTools
|
|
11
10
|
from .tools.auth_tools import AuthTools
|
|
12
11
|
from .tools.code_block_tools import CodeBlockTools
|
|
13
12
|
from .tools.feedback_tools import FeedbackTools
|
|
@@ -250,7 +249,6 @@ If the current MCP capability is unsupported, the workflow is awkward, or the us
|
|
|
250
249
|
TaskContextTools(sessions, backend).register(server)
|
|
251
250
|
RoleTools(sessions, backend).register(server)
|
|
252
251
|
AppTools(sessions, backend).register(server)
|
|
253
|
-
DataTools(sessions, backend).register(server)
|
|
254
252
|
QingbiReportTools(sessions, backend).register(server)
|
|
255
253
|
PackageTools(sessions, backend).register(server)
|
|
256
254
|
NavigationTools(sessions, backend).register(server)
|
|
@@ -56,8 +56,8 @@ def build_builder_server() -> FastMCP:
|
|
|
56
56
|
"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. "
|
|
57
57
|
"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. "
|
|
58
58
|
"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. "
|
|
59
|
-
"For app_views_apply, keep fixed saved filters in filters and configure the frontend query panel separately with query_conditions; query_conditions.rows
|
|
60
|
-
"For ordinary business buttons, declare them on
|
|
59
|
+
"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. "
|
|
60
|
+
"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:. "
|
|
61
61
|
"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. "
|
|
62
62
|
"For associated views/reports, use app_associated_resources_apply. Use match_mappings for filtering associated resources: dynamic current-record conditions use source_field, static conditions use value. match_mappings also supports 数据ID(-17) and 编号(0). Do not ask agents to write raw match_rules unless preserving a legacy backend config. "
|
|
63
63
|
"For associated reports/views, use app_associated_resources_apply for both the app-level associated_resources pool and per-view display config; associated_item_id is the app-level form_asos_chart.id, and view_configs/remove/reorder may also pass an existing resource's chart_id/chart_key/view_key because the tool resolves those to the internal id. Before creating an associated resource, read app_get.associated_resources and reuse an existing matching target_app_key + view_key/chart_key through patch_resources; client_key only works inside one apply call and is not persisted. Do not ask agents to pass backend raw sourceType: views infer the internal Qingflow view source, reports default to BI app reports, and dataset reports use report_source=dataset. "
|
|
@@ -231,32 +231,6 @@ def build_builder_server() -> FastMCP:
|
|
|
231
231
|
) -> dict:
|
|
232
232
|
return ai_builder.app_form_apply(profile=profile, spec=spec or {})
|
|
233
233
|
|
|
234
|
-
@server.tool()
|
|
235
|
-
def app_views_schema(
|
|
236
|
-
schema_version: str | None = None,
|
|
237
|
-
schema_kind: str | None = None,
|
|
238
|
-
) -> dict:
|
|
239
|
-
"""Get a complete views schema or a focused detail schema such as display or button-config."""
|
|
240
|
-
return ai_builder.app_views_schema(schema_version=schema_version, schema_kind=schema_kind)
|
|
241
|
-
|
|
242
|
-
@server.tool()
|
|
243
|
-
def app_views_get(
|
|
244
|
-
profile: str = DEFAULT_PROFILE,
|
|
245
|
-
app_key: str = "",
|
|
246
|
-
schema_version: str | None = None,
|
|
247
|
-
schema_kind: str | None = None,
|
|
248
|
-
) -> dict:
|
|
249
|
-
return ai_builder.app_views_get(
|
|
250
|
-
profile=profile, app_key=app_key, schema_version=schema_version, schema_kind=schema_kind
|
|
251
|
-
)
|
|
252
|
-
|
|
253
|
-
@server.tool()
|
|
254
|
-
def app_views_validate(
|
|
255
|
-
declaration: dict | None = None,
|
|
256
|
-
schema_version: str | None = None,
|
|
257
|
-
) -> dict:
|
|
258
|
-
return ai_builder.app_views_validate(declaration=declaration or {}, schema_version=schema_version)
|
|
259
|
-
|
|
260
234
|
@server.tool(description=ai_builder._high_risk_tool_description(operation="delete", target="app configuration"))
|
|
261
235
|
def app_delete(
|
|
262
236
|
profile: str = DEFAULT_PROFILE,
|
|
@@ -471,8 +445,8 @@ def build_builder_server() -> FastMCP:
|
|
|
471
445
|
return ai_builder.app_get_charts(profile=profile, app_key=app_key, app_keys=app_keys)
|
|
472
446
|
|
|
473
447
|
@server.tool()
|
|
474
|
-
def app_get_buttons(profile: str = DEFAULT_PROFILE, app_key: str = "", app_keys: list[str] | None = None
|
|
475
|
-
return ai_builder.app_get_buttons(profile=profile, app_key=app_key, app_keys=app_keys
|
|
448
|
+
def app_get_buttons(profile: str = DEFAULT_PROFILE, app_key: str = "", app_keys: list[str] | None = None) -> dict:
|
|
449
|
+
return ai_builder.app_get_buttons(profile=profile, app_key=app_key, app_keys=app_keys)
|
|
476
450
|
|
|
477
451
|
@server.tool()
|
|
478
452
|
def app_get_associated_resources(profile: str = DEFAULT_PROFILE, app_key: str = "", app_keys: list[str] | None = None) -> dict:
|
|
@@ -572,19 +546,21 @@ def build_builder_server() -> FastMCP:
|
|
|
572
546
|
profile: str = DEFAULT_PROFILE,
|
|
573
547
|
app_key: str = "",
|
|
574
548
|
publish: bool = True,
|
|
575
|
-
|
|
549
|
+
upsert_views: list[dict] | None = None,
|
|
576
550
|
patch_views: list[dict] | None = None,
|
|
577
551
|
remove_views: list[str] | None = None,
|
|
578
|
-
|
|
552
|
+
apps: list[dict] | None = None,
|
|
553
|
+
views: list[dict] | None = None,
|
|
579
554
|
) -> dict:
|
|
580
555
|
return ai_builder.app_views_apply(
|
|
581
556
|
profile=profile,
|
|
582
557
|
app_key=app_key,
|
|
583
558
|
publish=publish,
|
|
584
|
-
|
|
559
|
+
upsert_views=upsert_views or [],
|
|
585
560
|
patch_views=patch_views or [],
|
|
586
561
|
remove_views=remove_views or [],
|
|
587
|
-
|
|
562
|
+
apps=apps,
|
|
563
|
+
views=views,
|
|
588
564
|
)
|
|
589
565
|
|
|
590
566
|
@server.tool()
|
|
@@ -10,7 +10,6 @@ from .config import DEFAULT_PROFILE
|
|
|
10
10
|
from .response_trim import USER_SERVER_METHOD_MAP, trim_error_response, trim_public_response, wrap_trimmed_methods
|
|
11
11
|
from .session_store import SessionStore
|
|
12
12
|
from .tools.app_tools import AppTools
|
|
13
|
-
from .tools.data_tools import DataTools
|
|
14
13
|
from .tools.auth_tools import AuthTools
|
|
15
14
|
from .tools.code_block_tools import CodeBlockTools
|
|
16
15
|
from .tools.directory_tools import DirectoryTools
|
|
@@ -243,7 +242,6 @@ If the current MCP capability is unsupported, the workflow is awkward, or the us
|
|
|
243
242
|
backend = BackendClient()
|
|
244
243
|
auth = wrap_trimmed_methods(AuthTools(sessions, backend), USER_SERVER_METHOD_MAP)
|
|
245
244
|
apps = wrap_trimmed_methods(AppTools(sessions, backend), USER_SERVER_METHOD_MAP)
|
|
246
|
-
data_tools = wrap_trimmed_methods(DataTools(sessions, backend), USER_SERVER_METHOD_MAP)
|
|
247
245
|
workspace = wrap_trimmed_methods(WorkspaceTools(sessions, backend), USER_SERVER_METHOD_MAP)
|
|
248
246
|
file_tools = wrap_trimmed_methods(FileTools(sessions, backend), USER_SERVER_METHOD_MAP)
|
|
249
247
|
imports = wrap_trimmed_methods(ImportTools(sessions, backend), USER_SERVER_METHOD_MAP)
|
|
@@ -397,8 +395,6 @@ If the current MCP capability is unsupported, the workflow is awkward, or the us
|
|
|
397
395
|
def app_get(profile: str = DEFAULT_PROFILE, app_key: str = "") -> dict:
|
|
398
396
|
return apps.app_get(profile=profile, app_key=app_key)
|
|
399
397
|
|
|
400
|
-
data_tools.register(server)
|
|
401
|
-
|
|
402
398
|
@server.tool()
|
|
403
399
|
def portal_list(profile: str = DEFAULT_PROFILE) -> dict:
|
|
404
400
|
return resources.portal_list(profile=profile)
|
|
@@ -12,7 +12,6 @@ VIEW_TYPE_MAP = {
|
|
|
12
12
|
ViewType.board: "boardView",
|
|
13
13
|
ViewType.gantt: "ganttView",
|
|
14
14
|
ViewType.hierarchy: "hierarchyView",
|
|
15
|
-
ViewType.calendar: "calendarView",
|
|
16
15
|
}
|
|
17
16
|
|
|
18
17
|
|
|
@@ -33,7 +32,7 @@ def compile_views(entity: EntitySpec) -> list[dict[str, Any]]:
|
|
|
33
32
|
"viewgraphQueIds": list(view.field_ids),
|
|
34
33
|
"viewgraphQuestions": [],
|
|
35
34
|
"ordinal": ordinal,
|
|
36
|
-
"beingShowTitleQue": view.type in (ViewType.card, ViewType.board, ViewType.gantt, ViewType.hierarchy
|
|
35
|
+
"beingShowTitleQue": view.type in (ViewType.card, ViewType.board, ViewType.gantt, ViewType.hierarchy),
|
|
37
36
|
"beingShowCover": False,
|
|
38
37
|
"viewgraphLimitType": 1,
|
|
39
38
|
"viewgraphLimit": view.filters,
|