@josephyan/qingflow-cli 1.1.3 → 1.1.5
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 +7 -3
- package/docs/local-agent-install.md +57 -6
- package/entry_point.py +1 -1
- package/npm/bin/qingflow-skills.mjs +5 -0
- package/npm/bin/qingflow.mjs +1 -34
- package/npm/lib/runtime.mjs +21 -101
- package/npm/scripts/postinstall.mjs +1 -10
- package/package.json +3 -2
- package/pyproject.toml +1 -1
- package/skills/qingflow-cli/SKILL.md +58 -44
- package/skills/qingflow-cli/manifest.yaml +1 -1
- package/skills/qingflow-cli/reference/00-INDEX.md +35 -0
- package/skills/qingflow-cli/reference/builder/10-build-single-app.md +38 -0
- package/skills/qingflow-cli/reference/builder/20-build-complete-system.md +39 -0
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_SCHEMA_APPLY_FIELD_TYPES_AND_SCENARIOS.md → builder/30-schema-fields.md} +52 -10
- package/skills/qingflow-cli/reference/builder/40-layout.md +52 -0
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_VIEWS_WORKFLOW.md → builder/50-views.md} +39 -15
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_CHARTS_WORKFLOW.md → builder/60-charts.md} +36 -13
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_PORTAL_WORKFLOW.md → builder/70-portal.md} +36 -13
- package/skills/qingflow-cli/reference/builder/80-buttons-associated-resources.md +41 -0
- package/skills/qingflow-cli/reference/builder/90-workflow.md +34 -0
- package/skills/qingflow-cli/reference/builder/99-publish-verify.md +46 -0
- package/skills/qingflow-cli/reference/builder/README.md +41 -0
- package/skills/qingflow-cli/reference/builder/code-integrations/README.md +130 -0
- package/skills/qingflow-cli/reference/builder/code-integrations/code-block.md +66 -0
- package/skills/qingflow-cli/reference/builder/code-integrations/q-linker.md +77 -0
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_APP_DELIVERY_WORKFLOW.md → builder/reference/app-delivery-sop.md} +26 -16
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/README.md +293 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/build-complete-system.md +809 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/build-single-app.md +830 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/complete-system-development-guide.md +123 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/create-app.md +182 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/environments.md +63 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/flow-actors-and-permissions.md +142 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/gotchas.md +108 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/match-rules.md +114 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/public-surface-sync.md +75 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/single-app-development-guide.md +58 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/solution-playbooks.md +52 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/tool-selection.md +107 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/update-flow.md +7 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/update-layout.md +7 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/update-schema.md +7 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/update-views.md +7 -0
- package/skills/qingflow-cli/reference/builder/workflow/01-overview.md +45 -0
- package/skills/qingflow-cli/reference/builder/workflow/02-update-mode.md +53 -0
- package/skills/qingflow-cli/reference/builder/workflow/03-flow-patterns.md +57 -0
- package/skills/qingflow-cli/reference/builder/workflow/04-stage1-business-modeling.md +131 -0
- package/skills/qingflow-cli/reference/builder/workflow/05-stage2-members-roles.md +29 -0
- package/skills/qingflow-cli/reference/builder/workflow/06-stage3-build-spec.md +165 -0
- package/skills/qingflow-cli/reference/builder/workflow/07-stage4-validate-spec.md +33 -0
- package/skills/qingflow-cli/reference/builder/workflow/08-stage5-apply-verify.md +51 -0
- package/skills/qingflow-cli/reference/builder/workflow/09-stage6-summary.md +88 -0
- package/skills/qingflow-cli/reference/builder/workflow/10-node-config-reference.md +93 -0
- package/skills/qingflow-cli/reference/builder/workflow/11-troubleshooting.md +15 -0
- package/skills/qingflow-cli/reference/builder/workflow/README.md +88 -0
- package/skills/qingflow-cli/reference/builder/workflow/workflow-schema.json +1754 -0
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_ADMIN_CHEATSHEET.md → core/QINGFLOW_CLI_ADMIN_CHEATSHEET.md} +3 -3
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md → core/QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md} +6 -6
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_EXPLORATION_REPORT.md → core/QINGFLOW_CLI_EXPLORATION_REPORT.md} +2 -2
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_FIELD_DATA_TYPES.md → core/QINGFLOW_CLI_FIELD_DATA_TYPES.md} +11 -11
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_MEMBER_CHEATSHEET.md → core/QINGFLOW_CLI_MEMBER_CHEATSHEET.md} +4 -4
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_ONE_SHOT_CHEATSHEET.md → core/QINGFLOW_CLI_ONE_SHOT_CHEATSHEET.md} +4 -4
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_RECORD_CREATE_WORKFLOW.md → record/QINGFLOW_CLI_RECORD_CREATE_WORKFLOW.md} +3 -3
- package/skills/qingflow-cli/reference/record/QINGFLOW_CLI_RECORD_DELETE_WORKFLOW.md +31 -0
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_RECORD_IMPORT_WORKFLOW.md → record/QINGFLOW_CLI_RECORD_IMPORT_WORKFLOW.md} +4 -4
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_RECORD_UPDATE_WORKFLOW.md → record/QINGFLOW_CLI_RECORD_UPDATE_WORKFLOW.md} +7 -7
- package/skills/qingflow-cli/reference/record/analysis/README.md +130 -0
- package/skills/qingflow-cli/reference/record/analysis/analysis-gotchas.md +91 -0
- package/skills/qingflow-cli/reference/record/analysis/analysis-patterns.md +112 -0
- package/skills/qingflow-cli/reference/record/analysis/business-context.md +74 -0
- package/skills/qingflow-cli/reference/record/analysis/confidence-reporting.md +69 -0
- package/skills/qingflow-cli/reference/record/analysis/data-access-playbook.md +106 -0
- package/skills/qingflow-cli/reference/record/analysis/pandas-recipes.md +172 -0
- package/skills/qingflow-cli/reference/record/analysis/report-format.md +76 -0
- package/skills/qingflow-cli/reference/record/insert/README.md +75 -0
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_TASK_CONTEXT_WORKFLOW.md → task/QINGFLOW_CLI_TASK_CONTEXT_WORKFLOW.md} +5 -5
- package/skills/qingflow-cli/reference/task/ops/README.md +131 -0
- package/skills/qingflow-cli/reference/task/ops/environments.md +43 -0
- package/skills/qingflow-cli/reference/task/ops/workflow-usage.md +26 -0
- package/skills/qingflow-cli/scripts/validate_system_build_summary.py +124 -0
- package/skills/qingflow-cli/scripts/workflow/diff_flow_spec.py +275 -0
- package/skills/qingflow-cli/scripts/workflow/validate_flow_spec.py +605 -0
- package/skills/qingflow-mcp-setup/SKILL.md +115 -0
- package/skills/qingflow-mcp-setup/agents/openai.yaml +4 -0
- package/skills/qingflow-mcp-setup/references/claude-desktop.md +34 -0
- package/skills/qingflow-mcp-setup/references/environments.md +62 -0
- package/skills/qingflow-mcp-setup/references/generic-stdio.md +32 -0
- package/skills/qingflow-mcp-setup/scripts/check_local_server.sh +38 -0
- package/src/qingflow_mcp/__init__.py +1 -1
- package/src/qingflow_mcp/__main__.py +6 -2
- package/src/qingflow_mcp/builder_facade/models.py +287 -25
- package/src/qingflow_mcp/builder_facade/service.py +4195 -856
- package/src/qingflow_mcp/cli/commands/builder.py +316 -247
- package/src/qingflow_mcp/cli/commands/chart.py +1 -1
- package/src/qingflow_mcp/cli/commands/common.py +12 -3
- package/src/qingflow_mcp/cli/commands/exports.py +2 -2
- package/src/qingflow_mcp/cli/commands/imports.py +3 -3
- package/src/qingflow_mcp/cli/commands/portal.py +2 -2
- package/src/qingflow_mcp/cli/commands/record.py +101 -27
- package/src/qingflow_mcp/cli/commands/task.py +28 -47
- package/src/qingflow_mcp/cli/commands/view.py +1 -1
- package/src/qingflow_mcp/cli/context.py +0 -3
- package/src/qingflow_mcp/cli/formatters.py +784 -16
- package/src/qingflow_mcp/cli/main.py +117 -33
- package/src/qingflow_mcp/errors.py +43 -2
- package/src/qingflow_mcp/public_surface.py +26 -17
- package/src/qingflow_mcp/response_trim.py +81 -17
- package/src/qingflow_mcp/server.py +14 -12
- package/src/qingflow_mcp/server_app_builder.py +65 -21
- package/src/qingflow_mcp/server_app_user.py +22 -16
- package/src/qingflow_mcp/session_store.py +11 -7
- package/src/qingflow_mcp/solution/compiler/__init__.py +3 -1
- package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +173 -0
- package/src/qingflow_mcp/solution/executor.py +245 -18
- package/src/qingflow_mcp/tools/ai_builder_tools.py +1782 -399
- package/src/qingflow_mcp/tools/app_tools.py +184 -43
- package/src/qingflow_mcp/tools/approval_tools.py +197 -35
- package/src/qingflow_mcp/tools/auth_tools.py +92 -16
- package/src/qingflow_mcp/tools/code_block_tools.py +298 -40
- package/src/qingflow_mcp/tools/custom_button_tools.py +64 -10
- package/src/qingflow_mcp/tools/directory_tools.py +236 -72
- package/src/qingflow_mcp/tools/export_tools.py +244 -34
- package/src/qingflow_mcp/tools/feedback_tools.py +9 -0
- package/src/qingflow_mcp/tools/file_tools.py +9 -3
- package/src/qingflow_mcp/tools/import_tools.py +336 -49
- package/src/qingflow_mcp/tools/navigation_tools.py +91 -12
- package/src/qingflow_mcp/tools/package_tools.py +118 -6
- package/src/qingflow_mcp/tools/portal_tools.py +39 -3
- package/src/qingflow_mcp/tools/qingbi_report_tools.py +116 -7
- package/src/qingflow_mcp/tools/record_tools.py +1141 -356
- package/src/qingflow_mcp/tools/resource_read_tools.py +188 -39
- package/src/qingflow_mcp/tools/role_tools.py +80 -9
- package/src/qingflow_mcp/tools/solution_tools.py +59 -45
- package/src/qingflow_mcp/tools/task_context_tools.py +662 -158
- package/src/qingflow_mcp/tools/task_tools.py +113 -29
- package/src/qingflow_mcp/tools/view_tools.py +106 -3
- package/src/qingflow_mcp/tools/workflow_tools.py +48 -4
- package/src/qingflow_mcp/tools/workspace_tools.py +71 -3
- /package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_MATCH_RULES.md → builder/reference/match-rules.md} +0 -0
- /package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_WORKSPACE_ICONS.md → builder/reference/workspace-icons.md} +0 -0
- /package/skills/qingflow-cli/reference/{charts_remove.example.json → examples/charts/charts_remove.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{charts_reorder.example.json → examples/charts/charts_reorder.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{charts_upsert_bar.example.json → examples/charts/charts_upsert_bar.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{charts_upsert_dashboard_starter.example.json → examples/charts/charts_upsert_dashboard_starter.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{charts_upsert_minimal.example.json → examples/charts/charts_upsert_minimal.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{portal_sections_all_types.example.json → examples/portal/portal_sections_all_types.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{portal_sections_five_types.example.json → examples/portal/portal_sections_five_types.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{portal_sections_standard_workbench.example.json → examples/portal/portal_sections_standard_workbench.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{_batch_schema_complex.json → examples/schema/_batch_schema_complex.json} +0 -0
- /package/skills/qingflow-cli/reference/{_batch_schema_scalar.json → examples/schema/_batch_schema_scalar.json} +0 -0
- /package/skills/qingflow-cli/reference/{schema_add_fields_minimal.example.json → examples/schema/schema_add_fields_minimal.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{schema_apply_add_fields_all_types.json → examples/schema/schema_apply_add_fields_all_types.json} +0 -0
- /package/skills/qingflow-cli/reference/{views_upsert_table_minimal.example.json → examples/views/views_upsert_table_minimal.example.json} +0 -0
|
@@ -5,7 +5,7 @@ import json
|
|
|
5
5
|
import sys
|
|
6
6
|
from typing import Any, Callable, TextIO
|
|
7
7
|
|
|
8
|
-
from ..errors import QingflowApiError
|
|
8
|
+
from ..errors import QingflowApiError, backend_code_value_int, message_looks_like_invalid_token
|
|
9
9
|
from ..public_surface import cli_public_tool_spec_from_namespace
|
|
10
10
|
from ..response_trim import resolve_cli_tool_name, trim_error_response, trim_public_response
|
|
11
11
|
from ..tools.ai_builder_tools import _attach_builder_apply_envelope
|
|
@@ -79,6 +79,23 @@ 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
|
|
82
99
|
setattr(args, "_stdin", sys.stdin)
|
|
83
100
|
setattr(args, "_stdout_stream", out)
|
|
84
101
|
setattr(args, "_stderr_stream", err)
|
|
@@ -119,6 +136,50 @@ def run(
|
|
|
119
136
|
return exit_code
|
|
120
137
|
|
|
121
138
|
|
|
139
|
+
def _handle_version(_args: argparse.Namespace, _context: CliContext) -> dict[str, Any]:
|
|
140
|
+
info = get_cli_version_info()
|
|
141
|
+
return {
|
|
142
|
+
"ok": True,
|
|
143
|
+
"status": "success",
|
|
144
|
+
**info,
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _emit_version(*, json_mode: bool, stdout: TextIO) -> int:
|
|
149
|
+
version = get_cli_version()
|
|
150
|
+
if json_mode:
|
|
151
|
+
emit_json_result(
|
|
152
|
+
{
|
|
153
|
+
"ok": True,
|
|
154
|
+
"status": "success",
|
|
155
|
+
**get_cli_version_info(),
|
|
156
|
+
},
|
|
157
|
+
stream=stdout,
|
|
158
|
+
)
|
|
159
|
+
else:
|
|
160
|
+
stdout.write(f"{version}\n")
|
|
161
|
+
return 0
|
|
162
|
+
|
|
163
|
+
|
|
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
|
+
def _should_force_json_output_argv_for_version(argv: list[str]) -> bool:
|
|
180
|
+
return "--json" in argv
|
|
181
|
+
|
|
182
|
+
|
|
122
183
|
def _normalize_global_args(argv: list[str]) -> list[str]:
|
|
123
184
|
global_args: list[str] = []
|
|
124
185
|
remaining: list[str] = []
|
|
@@ -150,35 +211,6 @@ def _normalize_global_args(argv: list[str]) -> list[str]:
|
|
|
150
211
|
return global_args + remaining
|
|
151
212
|
|
|
152
213
|
|
|
153
|
-
def _handle_version(_args: argparse.Namespace, _context: CliContext) -> dict[str, Any]:
|
|
154
|
-
info = get_cli_version_info()
|
|
155
|
-
return {
|
|
156
|
-
"ok": True,
|
|
157
|
-
"status": "success",
|
|
158
|
-
**info,
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
def _emit_version(*, json_mode: bool, stdout: TextIO) -> int:
|
|
163
|
-
version = get_cli_version()
|
|
164
|
-
if json_mode:
|
|
165
|
-
emit_json_result(
|
|
166
|
-
{
|
|
167
|
-
"ok": True,
|
|
168
|
-
"status": "success",
|
|
169
|
-
**get_cli_version_info(),
|
|
170
|
-
},
|
|
171
|
-
stream=stdout,
|
|
172
|
-
)
|
|
173
|
-
else:
|
|
174
|
-
stdout.write(f"{version}\n")
|
|
175
|
-
return 0
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
def _should_force_json_output_argv_for_version(argv: list[str]) -> bool:
|
|
179
|
-
return "--json" in argv
|
|
180
|
-
|
|
181
|
-
|
|
182
214
|
def _should_force_json_output(args: argparse.Namespace) -> bool:
|
|
183
215
|
if bool(getattr(args, "force_json_output", False)):
|
|
184
216
|
return True
|
|
@@ -249,6 +281,8 @@ def _builder_apply_operation_from_args(args: argparse.Namespace) -> str | None:
|
|
|
249
281
|
return "app_associated_resources_apply"
|
|
250
282
|
if section == "portal" and getattr(args, "builder_portal_command", "") == "apply":
|
|
251
283
|
return "portal_apply"
|
|
284
|
+
if section == "portal" and getattr(args, "builder_portal_command", "") == "delete":
|
|
285
|
+
return "portal_delete"
|
|
252
286
|
if section == "schema" and getattr(args, "builder_schema_command", "") == "apply":
|
|
253
287
|
return "app_schema_apply"
|
|
254
288
|
if section == "layout" and getattr(args, "builder_layout_command", "") == "apply":
|
|
@@ -270,7 +304,7 @@ def _builder_apply_operation_from_argv(argv: list[str]) -> str | None:
|
|
|
270
304
|
return None
|
|
271
305
|
section = tokens[1]
|
|
272
306
|
action = tokens[2]
|
|
273
|
-
if action != "apply" and not (section == "publish" and action == "verify"):
|
|
307
|
+
if action != "apply" and not (section == "publish" and action == "verify") and not (section == "portal" and action == "delete"):
|
|
274
308
|
return None
|
|
275
309
|
mapping = {
|
|
276
310
|
"package": "package_apply",
|
|
@@ -285,6 +319,8 @@ def _builder_apply_operation_from_argv(argv: list[str]) -> str | None:
|
|
|
285
319
|
"charts": "app_charts_apply",
|
|
286
320
|
"publish": "app_publish_verify",
|
|
287
321
|
}
|
|
322
|
+
if section == "portal" and action == "delete":
|
|
323
|
+
return "portal_delete"
|
|
288
324
|
return mapping.get(section)
|
|
289
325
|
|
|
290
326
|
|
|
@@ -364,6 +400,8 @@ def _emit_error(payload: dict[str, Any], *, json_mode: bool, stdout: TextIO, std
|
|
|
364
400
|
f"Category: {payload.get('category') or 'error'}",
|
|
365
401
|
f"Message: {payload.get('message') or 'Unknown error'}",
|
|
366
402
|
]
|
|
403
|
+
if payload.get("error_code"):
|
|
404
|
+
lines.append(f"Error Code: {payload.get('error_code')}")
|
|
367
405
|
if payload.get("backend_code") is not None:
|
|
368
406
|
lines.append(f"Backend Code: {payload.get('backend_code')}")
|
|
369
407
|
if payload.get("request_id"):
|
|
@@ -378,23 +416,69 @@ def _emit_error(payload: dict[str, Any], *, json_mode: bool, stdout: TextIO, std
|
|
|
378
416
|
|
|
379
417
|
|
|
380
418
|
def _error_exit_code(payload: dict[str, Any]) -> int:
|
|
381
|
-
|
|
382
|
-
|
|
419
|
+
if str(payload.get("error_code") or "").upper() == "ARGUMENT_ERROR":
|
|
420
|
+
return 2
|
|
421
|
+
if _is_auth_or_workspace_payload(payload):
|
|
383
422
|
return 3
|
|
384
423
|
return 4
|
|
385
424
|
|
|
386
425
|
|
|
426
|
+
def _is_auth_or_workspace_payload(payload: dict[str, Any]) -> bool:
|
|
427
|
+
category = str(payload.get("category") or "").lower()
|
|
428
|
+
error_code = str(payload.get("error_code") or "").upper()
|
|
429
|
+
http_status = backend_code_value_int(payload.get("http_status"))
|
|
430
|
+
if category in {"auth", "workspace"} or error_code in {"AUTH_REQUIRED", "WORKSPACE_NOT_SELECTED"}:
|
|
431
|
+
return True
|
|
432
|
+
if http_status == 401 or message_looks_like_invalid_token(payload.get("message")):
|
|
433
|
+
return True
|
|
434
|
+
return False
|
|
435
|
+
|
|
436
|
+
|
|
387
437
|
def _result_exit_code(result: dict[str, Any]) -> int:
|
|
388
438
|
if not isinstance(result, dict):
|
|
389
439
|
return 0
|
|
440
|
+
if _is_executed_nonfatal_result(result):
|
|
441
|
+
return 0
|
|
442
|
+
if _is_auth_or_workspace_payload(result):
|
|
443
|
+
return 3
|
|
390
444
|
if result.get("ok") is False:
|
|
391
445
|
return 4
|
|
392
446
|
status = str(result.get("status") or "").lower()
|
|
447
|
+
if status == "partial_success" and result.get("ok") is not True and not _has_readback_unavailable_verification(result):
|
|
448
|
+
return 4
|
|
393
449
|
if status in {"failed", "blocked"}:
|
|
394
450
|
return 4
|
|
395
451
|
return 0
|
|
396
452
|
|
|
397
453
|
|
|
454
|
+
def _is_executed_nonfatal_result(result: dict[str, Any]) -> bool:
|
|
455
|
+
status = str(result.get("status") or "").lower()
|
|
456
|
+
executed = bool(
|
|
457
|
+
result.get("write_executed")
|
|
458
|
+
or result.get("write_may_have_succeeded")
|
|
459
|
+
or result.get("delete_executed")
|
|
460
|
+
or result.get("action_executed")
|
|
461
|
+
or result.get("export_executed")
|
|
462
|
+
)
|
|
463
|
+
return executed and status in {"partial_success", "verification_failed", "running", "queued", "unknown"}
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
def _has_readback_unavailable_verification(result: dict[str, Any]) -> bool:
|
|
467
|
+
verification = result.get("verification")
|
|
468
|
+
if not isinstance(verification, dict):
|
|
469
|
+
return False
|
|
470
|
+
return any(
|
|
471
|
+
bool(verification.get(key))
|
|
472
|
+
for key in (
|
|
473
|
+
"readback_unavailable",
|
|
474
|
+
"readback_pending",
|
|
475
|
+
"metadata_unverified",
|
|
476
|
+
"views_read_unavailable",
|
|
477
|
+
"readback_before_retry",
|
|
478
|
+
)
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
|
|
398
482
|
def _emit_cli_effective_context_notice(args: argparse.Namespace, context: CliContext, *, stream: TextIO) -> None:
|
|
399
483
|
spec = cli_public_tool_spec_from_namespace(args)
|
|
400
484
|
if spec is None or not spec.cli_show_effective_context:
|
|
@@ -9,11 +9,24 @@ from .json_types import JSONObject, JSONScalar
|
|
|
9
9
|
INVALID_TOKEN_MARKERS = (
|
|
10
10
|
"invalid token",
|
|
11
11
|
"token invalid",
|
|
12
|
+
"token expired",
|
|
13
|
+
"expired token",
|
|
12
14
|
"token失效",
|
|
13
15
|
"无效token",
|
|
14
16
|
"登录失效",
|
|
17
|
+
"登录过期",
|
|
18
|
+
"会话过期",
|
|
19
|
+
"session expired",
|
|
20
|
+
"session invalid",
|
|
15
21
|
"login token invalid",
|
|
16
22
|
"access token invalid",
|
|
23
|
+
"not logged in",
|
|
24
|
+
"not login",
|
|
25
|
+
"please login",
|
|
26
|
+
"please log in",
|
|
27
|
+
"未登录",
|
|
28
|
+
"请登录",
|
|
29
|
+
"重新登录",
|
|
17
30
|
)
|
|
18
31
|
|
|
19
32
|
|
|
@@ -36,8 +49,7 @@ class QingflowApiError(Exception):
|
|
|
36
49
|
return self.as_json()
|
|
37
50
|
|
|
38
51
|
def looks_like_invalid_token(self) -> bool:
|
|
39
|
-
|
|
40
|
-
return any(marker in text for marker in INVALID_TOKEN_MARKERS)
|
|
52
|
+
return message_looks_like_invalid_token(self.message)
|
|
41
53
|
|
|
42
54
|
@classmethod
|
|
43
55
|
def auth_required(cls, profile: str) -> "QingflowApiError":
|
|
@@ -64,3 +76,32 @@ class QingflowApiError(Exception):
|
|
|
64
76
|
|
|
65
77
|
def raise_tool_error(error: QingflowApiError) -> None:
|
|
66
78
|
raise RuntimeError(error.as_json())
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def backend_code_value_int(code: JSONScalar) -> int | None:
|
|
82
|
+
if isinstance(code, bool) or code is None:
|
|
83
|
+
return None
|
|
84
|
+
if isinstance(code, int):
|
|
85
|
+
return code
|
|
86
|
+
if isinstance(code, str):
|
|
87
|
+
text = code.strip()
|
|
88
|
+
if text:
|
|
89
|
+
try:
|
|
90
|
+
return int(text)
|
|
91
|
+
except ValueError:
|
|
92
|
+
return None
|
|
93
|
+
return None
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def backend_code_int(error: QingflowApiError) -> int | None:
|
|
97
|
+
return backend_code_value_int(error.backend_code)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def message_looks_like_invalid_token(message: object) -> bool:
|
|
101
|
+
text = str(message or "").lower()
|
|
102
|
+
return any(marker in text for marker in INVALID_TOKEN_MARKERS)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def is_auth_like_error(error: QingflowApiError) -> bool:
|
|
106
|
+
category = str(error.category or "").strip().lower()
|
|
107
|
+
return category == "auth" or error.http_status == 401 or error.looks_like_invalid_token()
|
|
@@ -51,35 +51,40 @@ USER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
|
|
|
51
51
|
"record_schema_get",
|
|
52
52
|
cli_route=("record", "schema", "applicant"),
|
|
53
53
|
mcp_public=False,
|
|
54
|
+
cli_public=False,
|
|
54
55
|
),
|
|
55
56
|
PublicToolSpec(
|
|
56
57
|
USER_DOMAIN,
|
|
57
58
|
"record_browse_schema_get",
|
|
58
59
|
("record_browse_schema_get_public",),
|
|
59
60
|
("record", "schema", "browse"),
|
|
61
|
+
cli_show_effective_context=True,
|
|
60
62
|
),
|
|
61
63
|
PublicToolSpec(
|
|
62
64
|
USER_DOMAIN,
|
|
63
65
|
"record_insert_schema_get",
|
|
64
66
|
("record_insert_schema_get_public",),
|
|
65
67
|
("record", "schema", "insert"),
|
|
68
|
+
cli_show_effective_context=True,
|
|
66
69
|
),
|
|
67
70
|
PublicToolSpec(
|
|
68
71
|
USER_DOMAIN,
|
|
69
72
|
"record_update_schema_get",
|
|
70
73
|
("record_update_schema_get_public",),
|
|
71
74
|
("record", "schema", "update"),
|
|
75
|
+
cli_show_effective_context=True,
|
|
72
76
|
),
|
|
73
|
-
PublicToolSpec(USER_DOMAIN, "record_import_schema_get", ("record_import_schema_get",), ("record", "schema", "import")),
|
|
77
|
+
PublicToolSpec(USER_DOMAIN, "record_import_schema_get", ("record_import_schema_get",), ("record", "schema", "import"), cli_show_effective_context=True),
|
|
74
78
|
PublicToolSpec(
|
|
75
79
|
USER_DOMAIN,
|
|
76
80
|
"record_code_block_schema_get",
|
|
77
81
|
("record_code_block_schema_get_public",),
|
|
78
82
|
("record", "schema", "code-block"),
|
|
83
|
+
cli_show_effective_context=True,
|
|
79
84
|
),
|
|
80
|
-
PublicToolSpec(USER_DOMAIN, "record_member_candidates", ("record_member_candidates",), ("record", "member-candidates")),
|
|
81
|
-
PublicToolSpec(USER_DOMAIN, "record_department_candidates", ("record_department_candidates",), ("record", "department-candidates")),
|
|
82
|
-
PublicToolSpec(USER_DOMAIN, "record_analyze", ("record_analyze",), ("record", "analyze"), mcp_public=False),
|
|
85
|
+
PublicToolSpec(USER_DOMAIN, "record_member_candidates", ("record_member_candidates",), ("record", "member-candidates"), cli_show_effective_context=True),
|
|
86
|
+
PublicToolSpec(USER_DOMAIN, "record_department_candidates", ("record_department_candidates",), ("record", "department-candidates"), cli_show_effective_context=True),
|
|
87
|
+
PublicToolSpec(USER_DOMAIN, "record_analyze", ("record_analyze",), ("record", "analyze"), mcp_public=False, cli_public=False),
|
|
83
88
|
PublicToolSpec(USER_DOMAIN, "record_list", ("record_list",), ("record", "list"), cli_show_effective_context=True),
|
|
84
89
|
PublicToolSpec(USER_DOMAIN, "record_access", ("record_access",), ("record", "access"), cli_show_effective_context=True),
|
|
85
90
|
PublicToolSpec(USER_DOMAIN, "record_get", ("record_get_public",), ("record", "get"), cli_show_effective_context=True),
|
|
@@ -87,11 +92,11 @@ USER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
|
|
|
87
92
|
PublicToolSpec(USER_DOMAIN, "record_insert", ("record_insert_public",), ("record", "insert"), cli_show_effective_context=True, cli_context_write=True),
|
|
88
93
|
PublicToolSpec(USER_DOMAIN, "record_update", ("record_update_public",), ("record", "update"), cli_show_effective_context=True, cli_context_write=True),
|
|
89
94
|
PublicToolSpec(USER_DOMAIN, "record_delete", ("record_delete_public",), ("record", "delete"), cli_show_effective_context=True, cli_context_write=True),
|
|
90
|
-
PublicToolSpec(USER_DOMAIN, "record_import_template_get", ("record_import_template_get",), ("import", "template")),
|
|
91
|
-
PublicToolSpec(USER_DOMAIN, "record_import_verify", ("record_import_verify",), ("import", "verify")),
|
|
95
|
+
PublicToolSpec(USER_DOMAIN, "record_import_template_get", ("record_import_template_get",), ("import", "template"), cli_show_effective_context=True),
|
|
96
|
+
PublicToolSpec(USER_DOMAIN, "record_import_verify", ("record_import_verify",), ("import", "verify"), cli_show_effective_context=True),
|
|
92
97
|
PublicToolSpec(USER_DOMAIN, "record_import_repair_local", ("record_import_repair_local",), ("import", "repair")),
|
|
93
|
-
PublicToolSpec(USER_DOMAIN, "record_import_start", ("record_import_start",), ("import", "start")),
|
|
94
|
-
PublicToolSpec(USER_DOMAIN, "record_import_status_get", ("record_import_status_get",), ("import", "status")),
|
|
98
|
+
PublicToolSpec(USER_DOMAIN, "record_import_start", ("record_import_start",), ("import", "start"), cli_show_effective_context=True, cli_context_write=True),
|
|
99
|
+
PublicToolSpec(USER_DOMAIN, "record_import_status_get", ("record_import_status_get",), ("import", "status"), cli_show_effective_context=True),
|
|
95
100
|
PublicToolSpec(USER_DOMAIN, "record_export_start", ("record_export_start",), ("export", "start"), cli_show_effective_context=True),
|
|
96
101
|
PublicToolSpec(USER_DOMAIN, "record_export_status_get", ("record_export_status_get",), ("export", "status"), cli_show_effective_context=True),
|
|
97
102
|
PublicToolSpec(USER_DOMAIN, "record_export_get", ("record_export_get",), ("export", "get"), cli_show_effective_context=True),
|
|
@@ -109,12 +114,12 @@ USER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
|
|
|
109
114
|
),
|
|
110
115
|
PublicToolSpec(USER_DOMAIN, "task_workflow_log_get", ("task_workflow_log_get",), ("task", "log"), cli_show_effective_context=True),
|
|
111
116
|
PublicToolSpec(USER_DOMAIN, "directory_search", ("directory_search",), cli_public=False),
|
|
112
|
-
PublicToolSpec(USER_DOMAIN, "directory_list_internal_users", ("directory_list_internal_users",), cli_public=False),
|
|
113
|
-
PublicToolSpec(USER_DOMAIN, "directory_list_all_internal_users", ("directory_list_all_internal_users",), cli_public=False),
|
|
114
|
-
PublicToolSpec(USER_DOMAIN, "directory_list_internal_departments", ("directory_list_internal_departments",), cli_public=False),
|
|
115
|
-
PublicToolSpec(USER_DOMAIN, "directory_list_all_departments", ("directory_list_all_departments",), cli_public=False),
|
|
116
|
-
PublicToolSpec(USER_DOMAIN, "directory_list_sub_departments", ("directory_list_sub_departments",), cli_public=False),
|
|
117
|
-
PublicToolSpec(USER_DOMAIN, "directory_list_external_members", ("directory_list_external_members",), cli_public=False),
|
|
117
|
+
PublicToolSpec(USER_DOMAIN, "directory_list_internal_users", ("directory_list_internal_users",), mcp_public=False, cli_public=False),
|
|
118
|
+
PublicToolSpec(USER_DOMAIN, "directory_list_all_internal_users", ("directory_list_all_internal_users",), mcp_public=False, cli_public=False),
|
|
119
|
+
PublicToolSpec(USER_DOMAIN, "directory_list_internal_departments", ("directory_list_internal_departments",), mcp_public=False, cli_public=False),
|
|
120
|
+
PublicToolSpec(USER_DOMAIN, "directory_list_all_departments", ("directory_list_all_departments",), mcp_public=False, cli_public=False),
|
|
121
|
+
PublicToolSpec(USER_DOMAIN, "directory_list_sub_departments", ("directory_list_sub_departments",), mcp_public=False, cli_public=False),
|
|
122
|
+
PublicToolSpec(USER_DOMAIN, "directory_list_external_members", ("directory_list_external_members",), mcp_public=False, cli_public=False),
|
|
118
123
|
)
|
|
119
124
|
|
|
120
125
|
|
|
@@ -138,9 +143,7 @@ BUILDER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
|
|
|
138
143
|
PublicToolSpec(BUILDER_DOMAIN, "app_release_edit_lock_if_mine", ("app_release_edit_lock_if_mine",), ("builder", "app", "release-edit-lock-if-mine"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
139
144
|
PublicToolSpec(BUILDER_DOMAIN, "app_resolve", ("app_resolve",), ("builder", "app", "resolve"), has_contract=True, cli_show_effective_context=True),
|
|
140
145
|
PublicToolSpec(BUILDER_DOMAIN, "button_style_catalog_get", ("button_style_catalog_get",), ("builder", "button", "catalog"), has_contract=True, cli_show_effective_context=True),
|
|
141
|
-
PublicToolSpec(BUILDER_DOMAIN, "app_get_buttons", ("app_get_buttons",), ("builder", "button", "get"), has_contract=True, cli_show_effective_context=True),
|
|
142
146
|
PublicToolSpec(BUILDER_DOMAIN, "app_custom_buttons_apply", ("app_custom_buttons_apply",), ("builder", "button", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
143
|
-
PublicToolSpec(BUILDER_DOMAIN, "app_get_associated_resources", ("app_get_associated_resources",), ("builder", "associated-resource", "get"), has_contract=True, cli_show_effective_context=True),
|
|
144
147
|
PublicToolSpec(BUILDER_DOMAIN, "app_associated_resources_apply", ("app_associated_resources_apply",), ("builder", "associated-resource", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
145
148
|
PublicToolSpec(BUILDER_DOMAIN, "app_get", ("app_get",), ("builder", "app", "get", "summary"), has_contract=True, cli_show_effective_context=True),
|
|
146
149
|
PublicToolSpec(BUILDER_DOMAIN, "app_get_fields", ("app_get_fields",), ("builder", "app", "get", "fields"), has_contract=True, cli_show_effective_context=True),
|
|
@@ -149,6 +152,10 @@ BUILDER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
|
|
|
149
152
|
PublicToolSpec(BUILDER_DOMAIN, "app_get_views", ("app_get_views",), ("builder", "app", "get", "views"), has_contract=True, cli_show_effective_context=True),
|
|
150
153
|
PublicToolSpec(BUILDER_DOMAIN, "app_get_flow", ("app_get_flow",), ("builder", "app", "get", "flow"), has_contract=True, cli_show_effective_context=True),
|
|
151
154
|
PublicToolSpec(BUILDER_DOMAIN, "app_get_charts", ("app_get_charts",), ("builder", "app", "get", "charts"), has_contract=True, cli_show_effective_context=True),
|
|
155
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_get_buttons", ("app_get_buttons",), ("builder", "app", "get", "buttons"), has_contract=True, cli_show_effective_context=True),
|
|
156
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_get_associated_resources", ("app_get_associated_resources",), ("builder", "app", "get", "associated-resources"), has_contract=True, cli_show_effective_context=True),
|
|
157
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_get_buttons", ("app_get_buttons",), ("builder", "button", "get"), has_contract=True, cli_show_effective_context=True),
|
|
158
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_get_associated_resources", ("app_get_associated_resources",), ("builder", "associated-resource", "get"), has_contract=True, cli_show_effective_context=True),
|
|
152
159
|
PublicToolSpec(BUILDER_DOMAIN, "portal_list", ("portal_list",), ("builder", "portal", "list"), has_contract=True, cli_show_effective_context=True),
|
|
153
160
|
PublicToolSpec(BUILDER_DOMAIN, "portal_get", ("portal_get",), ("builder", "portal", "get"), has_contract=True, cli_show_effective_context=True),
|
|
154
161
|
PublicToolSpec(BUILDER_DOMAIN, "view_get", ("view_get",), ("builder", "view", "get"), has_contract=True, cli_show_effective_context=True),
|
|
@@ -161,7 +168,8 @@ BUILDER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
|
|
|
161
168
|
PublicToolSpec(BUILDER_DOMAIN, "app_views_apply", ("app_views_apply",), ("builder", "views", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
162
169
|
PublicToolSpec(BUILDER_DOMAIN, "app_charts_apply", ("app_charts_apply",), ("builder", "charts", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
163
170
|
PublicToolSpec(BUILDER_DOMAIN, "portal_apply", ("portal_apply",), ("builder", "portal", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
164
|
-
PublicToolSpec(BUILDER_DOMAIN, "
|
|
171
|
+
PublicToolSpec(BUILDER_DOMAIN, "portal_delete", ("portal_delete",), ("builder", "portal", "delete"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
172
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_publish_verify", ("app_publish_verify",), ("builder", "publish", "verify"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
165
173
|
)
|
|
166
174
|
|
|
167
175
|
|
|
@@ -239,6 +247,7 @@ def cli_route_from_namespace(args: Namespace) -> tuple[str, ...] | None:
|
|
|
239
247
|
builder_command = getattr(args, "builder_command", None)
|
|
240
248
|
if not isinstance(builder_command, str) or not builder_command:
|
|
241
249
|
return None
|
|
250
|
+
builder_command = "associated-resource" if builder_command == "associated-resources" else builder_command
|
|
242
251
|
if builder_command == "contract":
|
|
243
252
|
return ("builder", "contract")
|
|
244
253
|
if builder_command == "app":
|
|
@@ -47,7 +47,6 @@ COMMON_ERROR_DROP_TOP = {
|
|
|
47
47
|
"allowed_values",
|
|
48
48
|
"missing_fields",
|
|
49
49
|
"noop",
|
|
50
|
-
"ok",
|
|
51
50
|
}
|
|
52
51
|
|
|
53
52
|
SUCCESS_POLICY_BY_TOOL: dict[str, TransformFn] = {}
|
|
@@ -58,11 +57,18 @@ def trim_public_response(tool_name: str | None, payload: dict[str, Any]) -> dict
|
|
|
58
57
|
return payload
|
|
59
58
|
if _looks_like_failure_payload(payload):
|
|
60
59
|
status = str(payload.get("status") or "").lower()
|
|
61
|
-
if
|
|
62
|
-
"
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
60
|
+
if _is_executed_nonfatal_payload(payload) or (
|
|
61
|
+
tool_name in {"user:record_insert", "user:record_update", "user:record_delete"}
|
|
62
|
+
and status in {
|
|
63
|
+
"blocked",
|
|
64
|
+
"needs_confirmation",
|
|
65
|
+
"partial_success",
|
|
66
|
+
}
|
|
67
|
+
) or (
|
|
68
|
+
tool_name == "user:record_import_verify"
|
|
69
|
+
and status == "failed"
|
|
70
|
+
and "can_import" in payload
|
|
71
|
+
):
|
|
66
72
|
return trim_success_response(tool_name, payload)
|
|
67
73
|
return _trim_returned_failure(payload)
|
|
68
74
|
return trim_success_response(tool_name, payload)
|
|
@@ -94,7 +100,7 @@ def trim_error_response(payload: dict[str, Any]) -> dict[str, Any]:
|
|
|
94
100
|
details = trimmed.get("details")
|
|
95
101
|
if isinstance(details, dict):
|
|
96
102
|
preserved = {}
|
|
97
|
-
for key in ("blocking_issues", "compiled_match_rules"):
|
|
103
|
+
for key in ("blocking_issues", "compiled_match_rules", "issues", "expected_shape"):
|
|
98
104
|
if key in details:
|
|
99
105
|
preserved[key] = details.get(key)
|
|
100
106
|
compact_details = _compact_scalar_dict(details)
|
|
@@ -151,12 +157,26 @@ def _parse_runtime_error_payload(exc: RuntimeError) -> dict[str, Any] | None:
|
|
|
151
157
|
|
|
152
158
|
|
|
153
159
|
def _looks_like_failure_payload(payload: dict[str, Any]) -> bool:
|
|
160
|
+
if _is_executed_nonfatal_payload(payload):
|
|
161
|
+
return False
|
|
154
162
|
if payload.get("ok") is False:
|
|
155
163
|
return True
|
|
156
164
|
status = str(payload.get("status") or "").lower()
|
|
157
165
|
return status in {"failed", "blocked"}
|
|
158
166
|
|
|
159
167
|
|
|
168
|
+
def _is_executed_nonfatal_payload(payload: dict[str, Any]) -> bool:
|
|
169
|
+
status = str(payload.get("status") or "").lower()
|
|
170
|
+
executed = bool(
|
|
171
|
+
payload.get("write_executed")
|
|
172
|
+
or payload.get("write_may_have_succeeded")
|
|
173
|
+
or payload.get("delete_executed")
|
|
174
|
+
or payload.get("action_executed")
|
|
175
|
+
or payload.get("export_executed")
|
|
176
|
+
)
|
|
177
|
+
return executed and status in {"partial_success", "verification_failed", "running", "queued", "unknown"}
|
|
178
|
+
|
|
179
|
+
|
|
160
180
|
def _trim_returned_failure(payload: dict[str, Any]) -> dict[str, Any]:
|
|
161
181
|
trimmed = deepcopy(payload)
|
|
162
182
|
_drop_top_keys(trimmed, COMMON_ERROR_DROP_TOP)
|
|
@@ -164,7 +184,7 @@ def _trim_returned_failure(payload: dict[str, Any]) -> dict[str, Any]:
|
|
|
164
184
|
details = trimmed.get("details")
|
|
165
185
|
if isinstance(details, dict):
|
|
166
186
|
preserved = {}
|
|
167
|
-
for key in ("blocking_issues", "compiled_match_rules"):
|
|
187
|
+
for key in ("blocking_issues", "compiled_match_rules", "issues", "expected_shape"):
|
|
168
188
|
if key in details:
|
|
169
189
|
preserved[key] = details.get(key)
|
|
170
190
|
compact_details = _compact_scalar_dict(details)
|
|
@@ -325,15 +345,22 @@ def _trim_import_schema(payload: JSONObject) -> None:
|
|
|
325
345
|
columns = [item for item in payload.get("expected_columns", []) if isinstance(item, dict)]
|
|
326
346
|
if columns is not None:
|
|
327
347
|
payload["columns"] = [_compact_import_column(item) for item in columns]
|
|
348
|
+
import_capability = payload.get("import_capability") if isinstance(payload.get("import_capability"), dict) else {}
|
|
349
|
+
if import_capability:
|
|
350
|
+
verification = payload.get("verification") if isinstance(payload.get("verification"), dict) else {}
|
|
351
|
+
if not isinstance(payload.get("verification"), dict):
|
|
352
|
+
payload["verification"] = verification
|
|
353
|
+
verification.setdefault("import_auth_source", import_capability.get("auth_source"))
|
|
354
|
+
verification.setdefault("import_capability_can_import", import_capability.get("can_import"))
|
|
328
355
|
payload.pop("expected_columns", None)
|
|
329
356
|
payload.pop("schema_fingerprint", None)
|
|
330
357
|
payload.pop("import_capability", None)
|
|
331
358
|
payload.pop("request_route", None)
|
|
332
|
-
payload.pop("verification", None)
|
|
333
359
|
|
|
334
360
|
if _looks_like_import_verify(payload):
|
|
335
361
|
_trim_import_verify_payload(payload)
|
|
336
362
|
return
|
|
363
|
+
payload.pop("verification", None)
|
|
337
364
|
if "applied_repairs" in payload or "repaired_file_path" in payload:
|
|
338
365
|
_trim_import_repair_payload(payload)
|
|
339
366
|
return
|
|
@@ -394,10 +421,14 @@ def _trim_record_write(payload: JSONObject) -> None:
|
|
|
394
421
|
data.pop("normalized_payload", None)
|
|
395
422
|
data.pop("human_review", None)
|
|
396
423
|
data.pop("action", None)
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
424
|
+
if payload.get("status") == "success":
|
|
425
|
+
data.pop("tried_routes", None)
|
|
426
|
+
update_route = payload.get("update_route")
|
|
427
|
+
if update_route not in (None, [], {}, ""):
|
|
428
|
+
data["update_route"] = update_route
|
|
429
|
+
tried_routes = payload.get("tried_routes")
|
|
430
|
+
if payload.get("status") != "success" and tried_routes not in (None, [], {}, ""):
|
|
431
|
+
data["tried_routes"] = tried_routes
|
|
401
432
|
resource = _compact_record_resource(data.get("resource"))
|
|
402
433
|
if resource:
|
|
403
434
|
data["resource"] = resource
|
|
@@ -441,6 +472,7 @@ def _trim_record_write_batch(payload: JSONObject, data: JSONObject) -> None:
|
|
|
441
472
|
"record_id",
|
|
442
473
|
"apply_id",
|
|
443
474
|
"write_executed",
|
|
475
|
+
"write_succeeded",
|
|
444
476
|
"verification_status",
|
|
445
477
|
"safe_to_retry",
|
|
446
478
|
"update_route",
|
|
@@ -887,6 +919,12 @@ def _compact_import_column(item: dict[str, Any]) -> dict[str, Any]:
|
|
|
887
919
|
compact["options"] = options
|
|
888
920
|
if bool(item.get("accepts_natural_input")):
|
|
889
921
|
compact["accepts_natural_input"] = True
|
|
922
|
+
import_value_format = item.get("import_value_format")
|
|
923
|
+
if isinstance(import_value_format, str) and import_value_format:
|
|
924
|
+
compact["import_value_format"] = import_value_format
|
|
925
|
+
format_hint = item.get("format_hint")
|
|
926
|
+
if isinstance(format_hint, str) and format_hint:
|
|
927
|
+
compact["format_hint"] = format_hint
|
|
890
928
|
if bool(item.get("requires_upload")):
|
|
891
929
|
compact["requires_upload"] = True
|
|
892
930
|
target_app_key = item.get("target_app_key")
|
|
@@ -902,13 +940,37 @@ def _compact_import_column(item: dict[str, Any]) -> dict[str, Any]:
|
|
|
902
940
|
|
|
903
941
|
|
|
904
942
|
def _looks_like_import_verify(payload: JSONObject) -> bool:
|
|
905
|
-
|
|
943
|
+
if "verification_id" in payload and "can_import" in payload:
|
|
944
|
+
return True
|
|
945
|
+
return "can_import" in payload and (
|
|
946
|
+
"error_code" in payload
|
|
947
|
+
or "issues" in payload
|
|
948
|
+
or "file_path" in payload
|
|
949
|
+
)
|
|
906
950
|
|
|
907
951
|
|
|
908
952
|
def _trim_import_verify_payload(payload: JSONObject) -> None:
|
|
909
953
|
issues = payload.get("issues") if isinstance(payload.get("issues"), list) else []
|
|
910
954
|
issue_summary = _summarize_import_issues(issues)
|
|
911
955
|
payload["issue_summary"] = issue_summary
|
|
956
|
+
verification = payload.get("verification") if isinstance(payload.get("verification"), dict) else {}
|
|
957
|
+
compact_verification = {
|
|
958
|
+
key: verification.get(key)
|
|
959
|
+
for key in (
|
|
960
|
+
"import_auth_prechecked",
|
|
961
|
+
"import_auth_precheck_passed",
|
|
962
|
+
"import_auth_source",
|
|
963
|
+
"import_capability_can_import",
|
|
964
|
+
"local_precheck_passed",
|
|
965
|
+
"backend_verification_passed",
|
|
966
|
+
"auto_normalized",
|
|
967
|
+
)
|
|
968
|
+
if key in verification
|
|
969
|
+
}
|
|
970
|
+
if compact_verification:
|
|
971
|
+
payload["verification"] = compact_verification
|
|
972
|
+
else:
|
|
973
|
+
payload.pop("verification", None)
|
|
912
974
|
columns = payload.get("columns")
|
|
913
975
|
if "expected_columns" not in payload and isinstance(columns, list):
|
|
914
976
|
payload["expected_columns"] = columns
|
|
@@ -1004,13 +1066,14 @@ def _trim_feedback(payload: JSONObject) -> None:
|
|
|
1004
1066
|
|
|
1005
1067
|
|
|
1006
1068
|
def _trim_builder_envelope(payload: JSONObject) -> None:
|
|
1007
|
-
if str(payload.get("status") or "").lower()
|
|
1069
|
+
if str(payload.get("status") or "").lower() in {"success", "partial_success", "verification_failed"}:
|
|
1008
1070
|
details = payload.get("details")
|
|
1009
1071
|
if isinstance(details, dict):
|
|
1010
1072
|
_drop_deep_keys(details, {"request_route", "base_url", "normalized_args", "suggested_next_call", "transport", "response", "body", "raw"})
|
|
1011
1073
|
preserved = {}
|
|
1012
|
-
|
|
1013
|
-
|
|
1074
|
+
for key in ("compiled_match_rules", "issues", "expected_shape"):
|
|
1075
|
+
if key in details:
|
|
1076
|
+
preserved[key] = details.get(key)
|
|
1014
1077
|
compact = _compact_scalar_dict(details)
|
|
1015
1078
|
compact.update(preserved)
|
|
1016
1079
|
if compact:
|
|
@@ -1142,6 +1205,7 @@ _register_policy(
|
|
|
1142
1205
|
"app_views_apply",
|
|
1143
1206
|
"app_charts_apply",
|
|
1144
1207
|
"portal_apply",
|
|
1208
|
+
"portal_delete",
|
|
1145
1209
|
"app_publish_verify",
|
|
1146
1210
|
),
|
|
1147
1211
|
_trim_builder_list_like,
|