@qingflow-tech/qingflow-cli 1.1.41 → 1.1.43-beta.1
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/package.json +2 -1
- package/pyproject.toml +3 -2
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_TASK_CONTEXT_WORKFLOW.md +1 -1
- package/skills/qingflow-cli/reference/task/QINGFLOW_CLI_TASK_CONTEXT_WORKFLOW.md +1 -1
- package/src/qingflow_mcp/app_form_compat.py +115 -0
- package/src/qingflow_mcp/app_form_validator.py +199 -0
- package/src/qingflow_mcp/builder_facade/app_form.py +2836 -0
- package/src/qingflow_mcp/builder_facade/app_form_schema.py +269 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/manifest.json +37 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/complete.json +1766 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/address.json +76 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/amount.json +76 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/attachment.json +76 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/code_block.json +223 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/data_relation.json +538 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/date.json +76 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/datetime.json +76 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/department.json +149 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/email.json +76 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/link.json +76 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/long_text.json +76 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/member.json +76 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/multi_select.json +92 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/number.json +76 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/phone.json +76 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/q_linker.json +338 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/relation.json +137 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/single_choice.json +92 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/single_select.json +92 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/subtable.json +810 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/text.json +76 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/summary.json +713 -0
- package/src/qingflow_mcp/builder_facade/service.py +5 -2
- package/src/qingflow_mcp/cli/commands/builder.py +91 -28
- package/src/qingflow_mcp/cli/json_io.py +21 -2
- package/src/qingflow_mcp/cli/main.py +3 -39
- package/src/qingflow_mcp/cloud_auth.py +11 -0
- package/src/qingflow_mcp/cloud_context.py +5 -0
- package/src/qingflow_mcp/cloud_oauth_proxy.py +453 -0
- package/src/qingflow_mcp/cloud_redis.py +25 -0
- package/src/qingflow_mcp/config.py +44 -0
- package/src/qingflow_mcp/public_surface.py +5 -2
- package/src/qingflow_mcp/response_trim.py +8 -0
- package/src/qingflow_mcp/server_app_builder.py +38 -86
- package/src/qingflow_mcp/server_app_user.py +2 -1
- package/src/qingflow_mcp/server_cloud.py +245 -22
- package/src/qingflow_mcp/tools/ai_builder_tools.py +93 -100
- package/src/qingflow_mcp/tools/app_tools.py +46 -65
- package/src/qingflow_mcp/tools/base.py +2 -1
- package/src/qingflow_mcp/tools/task_context_tools.py +6 -0
|
@@ -9191,6 +9191,7 @@ class AiBuilderFacade:
|
|
|
9191
9191
|
remove_fields: list[FieldRemovePatch],
|
|
9192
9192
|
inline_form_layout_sections: list[LayoutSectionPatch] | None = None,
|
|
9193
9193
|
) -> JSONObject:
|
|
9194
|
+
# TODO(deprecation): Remove after all internal callers use the AppForm reconciler.
|
|
9194
9195
|
apply_started_at = time.perf_counter()
|
|
9195
9196
|
schema_write_ms: int | None = None
|
|
9196
9197
|
inline_layout_ms: int | None = None
|
|
@@ -10182,6 +10183,7 @@ class AiBuilderFacade:
|
|
|
10182
10183
|
sections: list[LayoutSectionPatch],
|
|
10183
10184
|
publish: bool = True,
|
|
10184
10185
|
) -> JSONObject:
|
|
10186
|
+
# TODO(deprecation): Remove after all internal callers use the AppForm reconciler.
|
|
10185
10187
|
requested_sections = [section.model_dump(mode="json", exclude_none=True) for section in sections]
|
|
10186
10188
|
normalized_args = {
|
|
10187
10189
|
"app_key": app_key,
|
|
@@ -22236,7 +22238,7 @@ def _compile_code_block_binding_fields(
|
|
|
22236
22238
|
alias_name = str(output_item.get("alias") or "").strip()
|
|
22237
22239
|
alias_path = str(output_item.get("path") or "").strip()
|
|
22238
22240
|
existing_alias = existing_alias_by_name.get(alias_name) or {}
|
|
22239
|
-
alias_id = _coerce_positive_int(existing_alias.get("alias_id"))
|
|
22241
|
+
alias_id = _coerce_positive_int(output_item.get("alias_id")) or _coerce_positive_int(existing_alias.get("alias_id"))
|
|
22240
22242
|
compiled_outputs.append(
|
|
22241
22243
|
{
|
|
22242
22244
|
"alias_name": alias_name,
|
|
@@ -22649,7 +22651,8 @@ def _compile_q_linker_binding_fields(
|
|
|
22649
22651
|
{
|
|
22650
22652
|
"alias_name": alias_name,
|
|
22651
22653
|
"alias_path": alias_path,
|
|
22652
|
-
"alias_id": _coerce_positive_int(
|
|
22654
|
+
"alias_id": _coerce_positive_int(output_item.get("alias_id"))
|
|
22655
|
+
or _coerce_positive_int((existing_alias_by_name.get(alias_name) or {}).get("alias_id")),
|
|
22653
22656
|
"target_field": {
|
|
22654
22657
|
"field_id": target_field.get("field_id"),
|
|
22655
22658
|
"que_id": target_ref,
|
|
@@ -4,6 +4,7 @@ import argparse
|
|
|
4
4
|
from copy import deepcopy
|
|
5
5
|
|
|
6
6
|
from ..context import CliContext
|
|
7
|
+
from ...app_form_validator import AppFormValidationError, validate_declaration
|
|
7
8
|
from ..json_io import load_json_value
|
|
8
9
|
from .common import load_list_arg, load_object_arg, parse_bool_text, raise_config_error, require_list_arg
|
|
9
10
|
|
|
@@ -98,6 +99,29 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
98
99
|
package_apply.add_argument("--config-file", required=True)
|
|
99
100
|
package_apply.set_defaults(handler=_handle_package_apply, format_hint="builder_summary", force_json_output=True)
|
|
100
101
|
|
|
102
|
+
app_form = builder_subparsers.add_parser("app-form", help="声明式应用表单")
|
|
103
|
+
app_form_subparsers = app_form.add_subparsers(dest="builder_app_form_command", required=True)
|
|
104
|
+
|
|
105
|
+
app_form_schema = app_form_subparsers.add_parser("schema", help="读取 AppForm Schema")
|
|
106
|
+
app_form_schema.add_argument("--schema-version")
|
|
107
|
+
app_form_schema.add_argument("--field-type")
|
|
108
|
+
app_form_schema.set_defaults(handler=_handle_app_form_schema, format_hint="builder_summary", force_json_output=True)
|
|
109
|
+
|
|
110
|
+
app_form_get = app_form_subparsers.add_parser("get", help="读取 canonical AppForm")
|
|
111
|
+
app_form_get.add_argument("--app-key", required=True)
|
|
112
|
+
app_form_get.add_argument("--schema-version")
|
|
113
|
+
app_form_get.add_argument("--being-draft", action=argparse.BooleanOptionalAction, default=False)
|
|
114
|
+
app_form_get.set_defaults(handler=_handle_app_form_get, format_hint="builder_summary", force_json_output=True)
|
|
115
|
+
|
|
116
|
+
app_form_apply = app_form_subparsers.add_parser("apply", help="应用完整 AppForm 声明")
|
|
117
|
+
app_form_apply.add_argument("--file", required=True, help="AppForm JSON 文件,- 表示 stdin")
|
|
118
|
+
app_form_apply.set_defaults(handler=_handle_app_form_apply, format_hint="builder_summary", force_json_output=True)
|
|
119
|
+
|
|
120
|
+
app_form_validate = app_form_subparsers.add_parser("validate", help="只校验 AppForm 声明,不访问后端")
|
|
121
|
+
app_form_validate.add_argument("--file", required=True, help="AppForm JSON 文件,- 表示 stdin")
|
|
122
|
+
app_form_validate.add_argument("--schema-version", help="固定使用的 AppForm Schema 版本")
|
|
123
|
+
app_form_validate.set_defaults(handler=_handle_app_form_validate, format_hint="builder_summary", force_json_output=True)
|
|
124
|
+
|
|
101
125
|
app = builder_subparsers.add_parser("app", help="应用")
|
|
102
126
|
app_subparsers = app.add_subparsers(dest="builder_app_command", required=True)
|
|
103
127
|
|
|
@@ -113,6 +137,10 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
113
137
|
app_release_lock.add_argument("--lock-owner-name", required=True)
|
|
114
138
|
app_release_lock.set_defaults(handler=_handle_app_release_edit_lock_if_mine, format_hint="builder_summary", force_json_output=True)
|
|
115
139
|
|
|
140
|
+
app_delete = app_subparsers.add_parser("delete", help="删除应用")
|
|
141
|
+
app_delete.add_argument("--app-key", required=True)
|
|
142
|
+
app_delete.set_defaults(handler=_handle_app_delete, format_hint="builder_summary", force_json_output=True)
|
|
143
|
+
|
|
116
144
|
app_get = app_subparsers.add_parser("get", help="读取应用配置(字段请使用: builder app get --app-key APP fields)")
|
|
117
145
|
app_get.add_argument(
|
|
118
146
|
"builder_app_get_section",
|
|
@@ -202,34 +230,6 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
202
230
|
portal_delete.add_argument("--dash-key", required=True)
|
|
203
231
|
portal_delete.set_defaults(handler=_handle_portal_delete, format_hint="builder_summary", force_json_output=True)
|
|
204
232
|
|
|
205
|
-
schema_apply = builder_subparsers.add_parser("schema", help="字段搭建")
|
|
206
|
-
schema_apply_subparsers = schema_apply.add_subparsers(dest="builder_schema_command", required=True)
|
|
207
|
-
schema_apply_apply = schema_apply_subparsers.add_parser("apply", help="执行字段变更")
|
|
208
|
-
schema_apply_apply.add_argument("--app-key", default="")
|
|
209
|
-
schema_apply_apply.add_argument("--package-id", type=int)
|
|
210
|
-
schema_apply_apply.add_argument("--app-name", default="")
|
|
211
|
-
schema_apply_apply.add_argument("--app-title", default="")
|
|
212
|
-
schema_apply_apply.add_argument("--icon")
|
|
213
|
-
schema_apply_apply.add_argument("--color")
|
|
214
|
-
schema_apply_apply.add_argument("--visibility-file")
|
|
215
|
-
schema_apply_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=None)
|
|
216
|
-
schema_apply_apply.add_argument("--apps-file", help="多应用 schema JSON 对象;每项可带 client_key/app_name/form,支持 relation target_app_ref")
|
|
217
|
-
schema_apply_apply.add_argument("--form-file", help="单应用表单结构 JSON 数组;form[].rows[][] 同时声明字段和表单分组布局")
|
|
218
|
-
schema_apply_apply.add_argument("--add-fields-file", help="字段 JSON 数组;字段可用 as_data_title/as_data_cover 标记数据标题/封面")
|
|
219
|
-
schema_apply_apply.add_argument("--update-fields-file", help="字段更新 JSON 数组;set 内可用 as_data_title/as_data_cover 标记数据标题/封面")
|
|
220
|
-
schema_apply_apply.add_argument("--remove-fields-file")
|
|
221
|
-
schema_apply_apply.set_defaults(handler=_handle_schema_apply, format_hint="builder_summary", force_json_output=True)
|
|
222
|
-
|
|
223
|
-
layout_apply = builder_subparsers.add_parser("layout", help="布局")
|
|
224
|
-
layout_apply_subparsers = layout_apply.add_subparsers(dest="builder_layout_command", required=True)
|
|
225
|
-
layout_apply_apply = layout_apply_subparsers.add_parser("apply", help="执行布局变更")
|
|
226
|
-
layout_apply_apply.add_argument("--app-key", default="")
|
|
227
|
-
layout_apply_apply.add_argument("--apps-file", help="多应用布局 JSON 数组;每项包含 app_key/mode?/publish?/sections")
|
|
228
|
-
layout_apply_apply.add_argument("--mode", choices=["merge", "replace"], default="merge")
|
|
229
|
-
layout_apply_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=True)
|
|
230
|
-
layout_apply_apply.add_argument("--sections-file")
|
|
231
|
-
layout_apply_apply.set_defaults(handler=_handle_layout_apply, format_hint="builder_summary", force_json_output=True)
|
|
232
|
-
|
|
233
233
|
views_apply = builder_subparsers.add_parser("views", help="视图")
|
|
234
234
|
views_apply_subparsers = views_apply.add_subparsers(dest="builder_views_command", required=True)
|
|
235
235
|
views_apply_apply = views_apply_subparsers.add_parser("apply", help="执行视图变更")
|
|
@@ -331,6 +331,67 @@ def _handle_contract(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
331
331
|
return context.builder.builder_tool_contract(tool_name=args.tool_name)
|
|
332
332
|
|
|
333
333
|
|
|
334
|
+
def _handle_app_form_schema(args: argparse.Namespace, context: CliContext) -> dict:
|
|
335
|
+
return context.builder.app_form_schema(
|
|
336
|
+
schema_version=args.schema_version,
|
|
337
|
+
field_type=args.field_type,
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def _handle_app_form_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
342
|
+
return context.builder.app_form_get(
|
|
343
|
+
profile=args.profile,
|
|
344
|
+
app_key=args.app_key,
|
|
345
|
+
schema_version=args.schema_version,
|
|
346
|
+
being_draft=bool(args.being_draft),
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def _handle_app_form_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
351
|
+
payload = load_json_value(
|
|
352
|
+
args.file,
|
|
353
|
+
option_name="--file",
|
|
354
|
+
max_bytes=2 * 1024 * 1024,
|
|
355
|
+
max_depth=64,
|
|
356
|
+
)
|
|
357
|
+
if not isinstance(payload, dict):
|
|
358
|
+
raise_config_error("builder app-form apply --file must contain a JSON object")
|
|
359
|
+
return context.builder.app_form_apply(profile=args.profile, spec=payload)
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def _handle_app_form_validate(args: argparse.Namespace, _context: CliContext) -> dict:
|
|
363
|
+
payload = load_json_value(
|
|
364
|
+
args.file,
|
|
365
|
+
option_name="--file",
|
|
366
|
+
max_bytes=2 * 1024 * 1024,
|
|
367
|
+
max_depth=64,
|
|
368
|
+
)
|
|
369
|
+
if not isinstance(payload, dict):
|
|
370
|
+
raise_config_error("builder app-form validate --file must contain a JSON object")
|
|
371
|
+
try:
|
|
372
|
+
result = validate_declaration(payload, schema_version=args.schema_version)
|
|
373
|
+
except AppFormValidationError as error:
|
|
374
|
+
return {
|
|
375
|
+
"category": "validation",
|
|
376
|
+
"status": "failed",
|
|
377
|
+
"ok": False,
|
|
378
|
+
"error_code": "INVALID_SPEC",
|
|
379
|
+
"message": "AppForm declaration failed validation",
|
|
380
|
+
"details": {
|
|
381
|
+
"schemaVersion": error.schema_version,
|
|
382
|
+
"apiVersion": error.api_version,
|
|
383
|
+
"issues": error.issues,
|
|
384
|
+
},
|
|
385
|
+
}
|
|
386
|
+
except (ValueError, RuntimeError) as error:
|
|
387
|
+
raise_config_error(str(error), error_code="VALIDATION_ERROR")
|
|
388
|
+
return {"category": "validation", "status": "success", "ok": True, **result, "message": "AppForm declaration is valid"}
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
def _handle_app_delete(args: argparse.Namespace, context: CliContext) -> dict:
|
|
392
|
+
return context.builder.app_delete(profile=args.profile, app_key=args.app_key)
|
|
393
|
+
|
|
394
|
+
|
|
334
395
|
def _handle_icon_catalog(args: argparse.Namespace, context: CliContext) -> dict:
|
|
335
396
|
return context.builder.workspace_icon_catalog_get(profile=args.profile)
|
|
336
397
|
|
|
@@ -554,6 +615,8 @@ def _handle_chart_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
554
615
|
return context.builder.chart_get(profile=args.profile, chart_id=args.chart_id)
|
|
555
616
|
|
|
556
617
|
|
|
618
|
+
# TODO(deprecation): The legacy schema/layout CLI routes are removed. Keep the implementation below only
|
|
619
|
+
# while internal migration and focused regression coverage still depend on the imperative adapters.
|
|
557
620
|
def _handle_schema_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
558
621
|
apps_payload = _load_apps_file_arg(args.apps_file)
|
|
559
622
|
apps = apps_payload["apps"]
|
|
@@ -8,7 +8,13 @@ from typing import Any
|
|
|
8
8
|
from ..errors import QingflowApiError
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
def load_json_value(
|
|
11
|
+
def load_json_value(
|
|
12
|
+
path: str,
|
|
13
|
+
*,
|
|
14
|
+
option_name: str,
|
|
15
|
+
max_bytes: int | None = None,
|
|
16
|
+
max_depth: int | None = None,
|
|
17
|
+
) -> Any:
|
|
12
18
|
if not path:
|
|
13
19
|
raise QingflowApiError.config_error(f"{option_name} is required")
|
|
14
20
|
if path == "-":
|
|
@@ -18,10 +24,23 @@ def load_json_value(path: str, *, option_name: str) -> Any:
|
|
|
18
24
|
raw = Path(path).expanduser().read_text(encoding="utf-8")
|
|
19
25
|
except OSError as exc:
|
|
20
26
|
raise QingflowApiError.config_error(f"failed to read {option_name} from '{path}': {exc}") from exc
|
|
27
|
+
if max_bytes is not None and len(raw.encode("utf-8")) > max_bytes:
|
|
28
|
+
raise QingflowApiError.config_error(f"{option_name} exceeds the {max_bytes}-byte limit")
|
|
21
29
|
try:
|
|
22
|
-
|
|
30
|
+
value = json.loads(raw)
|
|
23
31
|
except json.JSONDecodeError as exc:
|
|
24
32
|
raise QingflowApiError.config_error(f"{option_name} must contain valid JSON: {exc}") from exc
|
|
33
|
+
if max_depth is not None:
|
|
34
|
+
stack = [(value, 1)]
|
|
35
|
+
while stack:
|
|
36
|
+
item, depth = stack.pop()
|
|
37
|
+
if depth > max_depth:
|
|
38
|
+
raise QingflowApiError.config_error(f"{option_name} exceeds the maximum nesting depth {max_depth}")
|
|
39
|
+
if isinstance(item, dict):
|
|
40
|
+
stack.extend((child, depth + 1) for child in item.values())
|
|
41
|
+
elif isinstance(item, list):
|
|
42
|
+
stack.extend((child, depth + 1) for child in item)
|
|
43
|
+
return value
|
|
25
44
|
|
|
26
45
|
|
|
27
46
|
def load_json_object(path: str, *, option_name: str) -> dict[str, Any]:
|
|
@@ -79,23 +79,6 @@ def run(
|
|
|
79
79
|
return 2
|
|
80
80
|
except SystemExit as exc:
|
|
81
81
|
return int(exc.code or 0)
|
|
82
|
-
try:
|
|
83
|
-
_validate_conditional_args(args)
|
|
84
|
-
except _CliArgumentError as exc:
|
|
85
|
-
if _should_force_json_output_argv(normalized_argv):
|
|
86
|
-
payload = {
|
|
87
|
-
"category": "config",
|
|
88
|
-
"status": "failed",
|
|
89
|
-
"error_code": "ARGUMENT_ERROR",
|
|
90
|
-
"message": exc.message,
|
|
91
|
-
"details": {"usage": exc.usage.strip(), "prog": exc.prog},
|
|
92
|
-
}
|
|
93
|
-
payload = _maybe_attach_builder_apply_error_envelope_from_args(args, payload)
|
|
94
|
-
emit_json_result(payload, stream=out)
|
|
95
|
-
return 2
|
|
96
|
-
err.write(exc.usage)
|
|
97
|
-
err.write(f"{exc.prog}: error: {exc.message}\n")
|
|
98
|
-
return 2
|
|
99
82
|
setattr(args, "_stdin", sys.stdin)
|
|
100
83
|
setattr(args, "_stdout_stream", out)
|
|
101
84
|
setattr(args, "_stderr_stream", err)
|
|
@@ -161,21 +144,6 @@ def _emit_version(*, json_mode: bool, stdout: TextIO) -> int:
|
|
|
161
144
|
return 0
|
|
162
145
|
|
|
163
146
|
|
|
164
|
-
def _validate_conditional_args(args: argparse.Namespace) -> None:
|
|
165
|
-
if (
|
|
166
|
-
getattr(args, "command", "") in {"builder", "build"}
|
|
167
|
-
and getattr(args, "builder_command", "") == "layout"
|
|
168
|
-
and getattr(args, "builder_layout_command", "") == "apply"
|
|
169
|
-
and not getattr(args, "apps_file", None)
|
|
170
|
-
and not getattr(args, "sections_file", None)
|
|
171
|
-
):
|
|
172
|
-
raise _CliArgumentError(
|
|
173
|
-
prog="qingflow builder layout apply",
|
|
174
|
-
message="--sections-file is required",
|
|
175
|
-
usage="usage: qingflow builder layout apply --app-key APP_KEY --sections-file SECTIONS.json\n",
|
|
176
|
-
)
|
|
177
|
-
|
|
178
|
-
|
|
179
147
|
def _should_force_json_output_argv_for_version(argv: list[str]) -> bool:
|
|
180
148
|
return "--json" in argv
|
|
181
149
|
|
|
@@ -231,8 +199,10 @@ def _should_force_json_output_argv(argv: list[str]) -> bool:
|
|
|
231
199
|
return False
|
|
232
200
|
section = tokens[1]
|
|
233
201
|
action = tokens[2]
|
|
234
|
-
if section in {"package", "button", "associated-resource", "associated-resources", "portal", "
|
|
202
|
+
if section in {"package", "button", "associated-resource", "associated-resources", "portal", "views", "flow", "charts"}:
|
|
235
203
|
return action == "apply"
|
|
204
|
+
if section == "app-form":
|
|
205
|
+
return action in {"schema", "get", "validate", "apply"}
|
|
236
206
|
if section == "publish":
|
|
237
207
|
return action == "verify"
|
|
238
208
|
if section == "app":
|
|
@@ -283,10 +253,6 @@ def _builder_apply_operation_from_args(args: argparse.Namespace) -> str | None:
|
|
|
283
253
|
return "portal_apply"
|
|
284
254
|
if section == "portal" and getattr(args, "builder_portal_command", "") == "delete":
|
|
285
255
|
return "portal_delete"
|
|
286
|
-
if section == "schema" and getattr(args, "builder_schema_command", "") == "apply":
|
|
287
|
-
return "app_schema_apply"
|
|
288
|
-
if section == "layout" and getattr(args, "builder_layout_command", "") == "apply":
|
|
289
|
-
return "app_layout_apply"
|
|
290
256
|
if section == "views" and getattr(args, "builder_views_command", "") == "apply":
|
|
291
257
|
return "app_views_apply"
|
|
292
258
|
if section == "flow" and getattr(args, "builder_flow_command", "") == "apply":
|
|
@@ -312,8 +278,6 @@ def _builder_apply_operation_from_argv(argv: list[str]) -> str | None:
|
|
|
312
278
|
"associated-resource": "app_associated_resources_apply",
|
|
313
279
|
"associated-resources": "app_associated_resources_apply",
|
|
314
280
|
"portal": "portal_apply",
|
|
315
|
-
"schema": "app_schema_apply",
|
|
316
|
-
"layout": "app_layout_apply",
|
|
317
281
|
"views": "app_views_apply",
|
|
318
282
|
"flow": "app_flow_apply",
|
|
319
283
|
"charts": "app_charts_apply",
|
|
@@ -25,6 +25,7 @@ from .json_types import JSONObject
|
|
|
25
25
|
AUTH_CONTEXT_KEY_PREFIX = "mcp:auth:context"
|
|
26
26
|
AUTH_CONTEXT_LOCK_KEY_PREFIX = "mcp:auth:context:lock"
|
|
27
27
|
AUTH_CONTEXT_NEGATIVE_KEY_PREFIX = "mcp:auth:context:negative"
|
|
28
|
+
ACCESS_PLATFORM_KEY_PREFIX = "oauth2:access-platform:"
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
@dataclass(slots=True)
|
|
@@ -103,6 +104,16 @@ class CloudAuthBroker:
|
|
|
103
104
|
def invalidate(self, credential_hash: str) -> None:
|
|
104
105
|
self.cache.delete(self._auth_key(credential_hash))
|
|
105
106
|
|
|
107
|
+
def resolve_call_platform(self, access_token: str) -> str:
|
|
108
|
+
normalized_token = self._normalize_text(access_token)
|
|
109
|
+
if normalized_token is None:
|
|
110
|
+
return ""
|
|
111
|
+
try:
|
|
112
|
+
value = self.cache.get_text(f"{ACCESS_PLATFORM_KEY_PREFIX}{normalized_token}")
|
|
113
|
+
except QingflowApiError:
|
|
114
|
+
return ""
|
|
115
|
+
return self._normalize_text(value) or ""
|
|
116
|
+
|
|
106
117
|
@staticmethod
|
|
107
118
|
def credential_hash(credential: str) -> str:
|
|
108
119
|
return sha256(credential.encode("utf-8")).hexdigest()
|