@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.
Files changed (110) hide show
  1. package/README.md +4 -4
  2. package/npm/lib/runtime.mjs +13 -29
  3. package/package.json +1 -1
  4. package/pyproject.toml +2 -8
  5. package/skills/qingflow-cli/SKILL.md +322 -93
  6. package/skills/qingflow-cli/manifest.yaml +1 -1
  7. package/skills/qingflow-cli/reference/00-INDEX.md +4 -4
  8. package/skills/qingflow-cli/reference/QINGFLOW_CLI_ADMIN_CHEATSHEET.md +1 -1
  9. package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_VIEWS_WORKFLOW.md +9 -3
  10. package/skills/qingflow-cli/reference/QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md +137 -3
  11. package/skills/qingflow-cli/reference/QINGFLOW_CLI_EXPLORATION_REPORT.md +4 -6
  12. package/skills/qingflow-cli/reference/QINGFLOW_CLI_FIELD_DATA_TYPES.md +14 -14
  13. package/skills/qingflow-cli/reference/QINGFLOW_CLI_MEMBER_CHEATSHEET.md +19 -20
  14. package/skills/qingflow-cli/reference/QINGFLOW_CLI_ONE_SHOT_CHEATSHEET.md +14 -18
  15. package/skills/qingflow-cli/reference/QINGFLOW_CLI_RECORD_CREATE_WORKFLOW.md +12 -7
  16. package/skills/qingflow-cli/reference/QINGFLOW_CLI_RECORD_UPDATE_WORKFLOW.md +1 -2
  17. package/skills/qingflow-cli/reference/builder/20-build-complete-system.md +2 -3
  18. package/skills/qingflow-cli/reference/builder/30-schema-fields.md +5 -9
  19. package/skills/qingflow-cli/reference/builder/50-views.md +191 -192
  20. package/skills/qingflow-cli/reference/builder/80-buttons-associated-resources.md +0 -2
  21. package/skills/qingflow-cli/reference/builder/README.md +1 -1
  22. package/skills/qingflow-cli/reference/builder/reference/app-delivery-sop.md +6 -6
  23. package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_ADMIN_CHEATSHEET.md +1 -1
  24. package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md +95 -76
  25. package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_EXPLORATION_REPORT.md +4 -6
  26. package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_FIELD_DATA_TYPES.md +13 -13
  27. package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_MEMBER_CHEATSHEET.md +19 -20
  28. package/skills/qingflow-cli/reference/core/QINGFLOW_CLI_ONE_SHOT_CHEATSHEET.md +14 -18
  29. package/skills/qingflow-cli/reference/examples/README.md +1 -1
  30. package/skills/qingflow-cli/reference/examples/views/views_batch_full.example.json +159 -0
  31. package/skills/qingflow-cli/reference/examples/views/views_upsert_table_minimal.example.json +36 -30
  32. package/skills/qingflow-cli/reference/record/QINGFLOW_CLI_RECORD_UPDATE_WORKFLOW.md +1 -2
  33. package/skills/qingflow-cli/reference/record/analysis/README.md +113 -21
  34. package/skills/qingflow-cli/reference/record/analysis/analysis-gotchas.md +10 -10
  35. package/skills/qingflow-cli/reference/record/analysis/analysis-patterns.md +9 -9
  36. package/skills/qingflow-cli/reference/record/analysis/business-context.md +1 -1
  37. package/skills/qingflow-cli/reference/record/analysis/confidence-reporting.md +10 -10
  38. package/skills/qingflow-cli/reference/record/analysis/data-access-playbook.md +80 -52
  39. package/skills/qingflow-cli/reference/record/analysis/pandas-recipes.md +2 -2
  40. package/skills/qingflow-cli/reference/views_upsert_table_minimal.example.json +42 -0
  41. package/skills/qingflow-common/SKILL.md +4 -7
  42. package/src/qingflow_mcp/__init__.py +1 -1
  43. package/src/qingflow_mcp/app_form_compat.py +0 -3
  44. package/src/qingflow_mcp/app_form_validator.py +17 -69
  45. package/src/qingflow_mcp/builder_facade/app_form.py +100 -497
  46. package/src/qingflow_mcp/builder_facade/models.py +17 -227
  47. package/src/qingflow_mcp/builder_facade/schemas/app_form/manifest.json +0 -1
  48. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/complete.json +30 -64
  49. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/address.json +8 -4
  50. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/amount.json +8 -4
  51. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/attachment.json +10 -6
  52. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/code_block.json +10 -6
  53. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/data_relation.json +18 -11
  54. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/date.json +8 -4
  55. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/datetime.json +8 -4
  56. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/department.json +10 -6
  57. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/email.json +8 -4
  58. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/link.json +8 -4
  59. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/long_text.json +8 -4
  60. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/member.json +10 -6
  61. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/multi_select.json +8 -4
  62. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/number.json +8 -4
  63. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/phone.json +8 -4
  64. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/q_linker.json +10 -6
  65. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/relation.json +15 -46
  66. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/single_choice.json +8 -4
  67. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/single_select.json +8 -4
  68. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/subtable.json +27 -61
  69. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/text.json +8 -4
  70. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/formula.json +0 -43
  71. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/summary.json +12 -7
  72. package/src/qingflow_mcp/builder_facade/service.py +539 -1671
  73. package/src/qingflow_mcp/builder_facade/workflow_spec.py +1 -1
  74. package/src/qingflow_mcp/cli/commands/__init__.py +1 -2
  75. package/src/qingflow_mcp/cli/commands/builder.py +55 -76
  76. package/src/qingflow_mcp/cli/commands/record.py +1 -1
  77. package/src/qingflow_mcp/cli/context.py +0 -3
  78. package/src/qingflow_mcp/cli/formatters.py +1 -97
  79. package/src/qingflow_mcp/config.py +1 -0
  80. package/src/qingflow_mcp/public_surface.py +0 -8
  81. package/src/qingflow_mcp/response_trim.py +2 -11
  82. package/src/qingflow_mcp/server.py +0 -2
  83. package/src/qingflow_mcp/server_app_builder.py +10 -34
  84. package/src/qingflow_mcp/server_app_user.py +0 -4
  85. package/src/qingflow_mcp/solution/compiler/view_compiler.py +1 -2
  86. package/src/qingflow_mcp/solution/executor.py +8 -96
  87. package/src/qingflow_mcp/solution/spec_models.py +0 -1
  88. package/src/qingflow_mcp/tools/ai_builder_tools.py +398 -218
  89. package/src/qingflow_mcp/tools/base.py +42 -10
  90. package/src/qingflow_mcp/tools/record_tools.py +46 -11
  91. package/src/qingflow_mcp/tools/solution_tools.py +0 -2
  92. package/src/qingflow_mcp/tools/view_tools.py +6 -0
  93. package/skills/qingflow-cli/reference/data/README.md +0 -32
  94. package/skills/qingflow-cli/reference/examples/views/views_apply_v1alpha1.example.json +0 -14
  95. package/skills/qingflow-cli/reference/record/QINGFLOW_CLI_RECORD_SPECIALIZED_WORKFLOW.md +0 -50
  96. package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/rich_text.json +0 -146
  97. package/src/qingflow_mcp/builder_facade/schemas/views/manifest.json +0 -42
  98. package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/complete.json +0 -1245
  99. package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/details/button-config.json +0 -53
  100. package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/details/data-permission.json +0 -60
  101. package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/details/display.json +0 -73
  102. package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/details/operation-permission.json +0 -48
  103. package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/details/query-condition.json +0 -47
  104. package/src/qingflow_mcp/builder_facade/schemas/views/v1alpha1/summary.json +0 -319
  105. package/src/qingflow_mcp/builder_facade/views_extract.py +0 -592
  106. package/src/qingflow_mcp/builder_facade/views_schema.py +0 -400
  107. package/src/qingflow_mcp/cli/commands/data.py +0 -134
  108. package/src/qingflow_mcp/tools/csv_export.py +0 -168
  109. package/src/qingflow_mcp/tools/data_tools.py +0 -284
  110. 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-06"
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, data, exports, imports, portal, record, task, view, workspace
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
- load_list_arg,
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", required=True)
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("--view-file", help="单条新建或完整更新视图的 JSON 对象")
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, button_text=button_text)
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, button_text=button_text)
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
- view = load_object_arg(args.view_file, option_name="--view-file") if getattr(args, "view_file", None) else None
1273
- if view is None and not (getattr(args, "patch_views_file", None) or getattr(args, "remove_views_file", None)):
1274
- raise_config_error(
1275
- "builder views apply requires --view-file, --patch-views-file, or --remove-views-file.",
1276
- fix_hint="Pass one JSON object through --view-file to create or fully update one view.",
1277
- error_code="ARGUMENT_ERROR",
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
- upsert_view=view,
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
- schema_version=getattr(args, "schema_version", None),
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="日志、附件、导入、代码块及 record 专项能力")
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 in _DATA_FORMAT_HINTS:
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
- #: 这些校验命令的产出契约明确包含 ok 字段(供阶段 2 SKILL.md 引用),
54
+ #: 这两个校验命令的产出契约明确包含 ok 字段(供阶段 2 SKILL.md 引用),
55
55
  #: 需在 success 裁剪时保留 ok(默认 COMMON_SUCCESS_DROP_TOP 会丢弃 ok)。
56
- #: `builder:app_views_validate`(views 声明式校验)同属校验类工具,ok 同样进产出契约。
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 accepts field names, field IDs, or queIds and compiles them to backend queryCondition queIds, with queIds preferred. 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 a new or full view with app_views_apply.upsert_view.action_buttons; app_views_apply writes the view first, then creates/binds the button. For an existing view, use patch_views[].set.action_buttons. 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:. "
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, button_text: str | None = None) -> dict:
475
- return ai_builder.app_get_buttons(profile=profile, app_key=app_key, app_keys=app_keys, button_text=button_text)
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
- upsert_view: dict | None = None,
549
+ upsert_views: list[dict] | None = None,
576
550
  patch_views: list[dict] | None = None,
577
551
  remove_views: list[str] | None = None,
578
- schema_version: str | None = None,
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
- upsert_view=upsert_view,
559
+ upsert_views=upsert_views or [],
585
560
  patch_views=patch_views or [],
586
561
  remove_views=remove_views or [],
587
- schema_version=schema_version,
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, ViewType.calendar),
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,