@josephyan/qingflow-cli 1.0.11 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/npm/bin/qingflow.mjs +40 -2
- package/npm/lib/runtime.mjs +386 -15
- package/npm/scripts/postinstall.mjs +7 -2
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/skills/qingflow-cli/SKILL.md +440 -0
- package/skills/qingflow-cli/manifest.yaml +10 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_ADMIN_CHEATSHEET.md +94 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_APP_DELIVERY_WORKFLOW.md +485 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_CHARTS_WORKFLOW.md +237 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_MATCH_RULES.md +137 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_PORTAL_WORKFLOW.md +263 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_VIEWS_WORKFLOW.md +304 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_WORKSPACE_ICONS.md +41 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md +139 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_EXPLORATION_REPORT.md +84 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_FIELD_DATA_TYPES.md +129 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_MEMBER_CHEATSHEET.md +195 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_ONE_SHOT_CHEATSHEET.md +159 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_RECORD_CREATE_WORKFLOW.md +20 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_RECORD_IMPORT_WORKFLOW.md +176 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_RECORD_UPDATE_WORKFLOW.md +163 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_SCHEMA_APPLY_FIELD_TYPES_AND_SCENARIOS.md +107 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_TASK_CONTEXT_WORKFLOW.md +151 -0
- package/skills/qingflow-cli/reference/_batch_schema_complex.json +18 -0
- package/skills/qingflow-cli/reference/_batch_schema_scalar.json +17 -0
- package/skills/qingflow-cli/reference/charts_remove.example.json +1 -0
- package/skills/qingflow-cli/reference/charts_reorder.example.json +1 -0
- package/skills/qingflow-cli/reference/charts_upsert_bar.example.json +8 -0
- package/skills/qingflow-cli/reference/charts_upsert_dashboard_starter.example.json +37 -0
- package/skills/qingflow-cli/reference/charts_upsert_minimal.example.json +13 -0
- package/skills/qingflow-cli/reference/portal_sections_all_types.example.json +131 -0
- package/skills/qingflow-cli/reference/portal_sections_five_types.example.json +126 -0
- package/skills/qingflow-cli/reference/portal_sections_standard_workbench.example.json +128 -0
- package/skills/qingflow-cli/reference/schema_add_fields_minimal.example.json +7 -0
- package/skills/qingflow-cli/reference/schema_apply_add_fields_all_types.json +78 -0
- package/skills/qingflow-cli/reference/views_upsert_table_minimal.example.json +7 -0
- package/skills/qingflow-cli/scripts/builder-package-from-app-list.py +140 -0
- package/skills/qingflow-cli/scripts/find-app-by-keyword.py +132 -0
- package/skills/qingflow-cli/scripts/validate_qingflow_output_files.py +87 -0
- package/src/qingflow_mcp/__init__.py +1 -1
- package/src/qingflow_mcp/builder_facade/models.py +532 -48
- package/src/qingflow_mcp/builder_facade/service.py +9194 -2384
- package/src/qingflow_mcp/builder_facade/workflow_spec.py +111 -0
- package/src/qingflow_mcp/cli/commands/app.py +3 -16
- package/src/qingflow_mcp/cli/commands/builder.py +354 -56
- package/src/qingflow_mcp/cli/commands/record.py +89 -2
- package/src/qingflow_mcp/cli/formatters.py +32 -1
- package/src/qingflow_mcp/cli/main.py +245 -3
- package/src/qingflow_mcp/public_surface.py +11 -8
- package/src/qingflow_mcp/response_trim.py +143 -14
- package/src/qingflow_mcp/server.py +15 -12
- package/src/qingflow_mcp/server_app_builder.py +108 -30
- package/src/qingflow_mcp/server_app_user.py +17 -18
- package/src/qingflow_mcp/solution/compiler/__init__.py +1 -3
- package/src/qingflow_mcp/solution/compiler/icon_utils.py +294 -0
- package/src/qingflow_mcp/solution/executor.py +3 -133
- package/src/qingflow_mcp/tools/ai_builder_tools.py +2617 -440
- package/src/qingflow_mcp/tools/app_tools.py +53 -8
- package/src/qingflow_mcp/tools/package_tools.py +16 -2
- package/src/qingflow_mcp/tools/record_tools.py +2095 -176
- package/src/qingflow_mcp/tools/resource_read_tools.py +3 -0
- package/src/qingflow_mcp/tools/solution_tools.py +30 -2
- package/src/qingflow_mcp/tools/workflow_tools.py +3 -31
- package/src/qingflow_mcp/version.py +110 -0
- package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +0 -173
|
@@ -13,7 +13,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
13
13
|
record_subparsers = parser.add_subparsers(
|
|
14
14
|
dest="record_command",
|
|
15
15
|
required=True,
|
|
16
|
-
metavar="{schema,list,access,get,insert,update,delete,code-block-run}",
|
|
16
|
+
metavar="{schema,list,access,get,logs,insert,update,delete,member-candidates,department-candidates,code-block-run}",
|
|
17
17
|
)
|
|
18
18
|
|
|
19
19
|
schema = record_subparsers.add_parser("schema", help="读取记录相关表结构")
|
|
@@ -47,6 +47,28 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
47
47
|
schema_code_block.add_argument("--app-key", required=True)
|
|
48
48
|
schema_code_block.set_defaults(handler=_handle_schema_code_block, format_hint="")
|
|
49
49
|
|
|
50
|
+
member_candidates = record_subparsers.add_parser("member-candidates", help="读取成员字段候选项")
|
|
51
|
+
member_candidates.add_argument("--app-key", required=True)
|
|
52
|
+
member_candidates.add_argument("--field-id", type=int, required=True)
|
|
53
|
+
member_candidates.add_argument("--keyword", default="")
|
|
54
|
+
member_candidates.add_argument("--page-num", type=int, default=1)
|
|
55
|
+
member_candidates.add_argument("--page-size", type=int, default=20)
|
|
56
|
+
member_candidates.add_argument("--record-id")
|
|
57
|
+
member_candidates.add_argument("--workflow-node-id", type=int)
|
|
58
|
+
member_candidates.add_argument("--fields-file")
|
|
59
|
+
member_candidates.set_defaults(handler=_handle_member_candidates, format_hint="")
|
|
60
|
+
|
|
61
|
+
department_candidates = record_subparsers.add_parser("department-candidates", help="读取部门字段候选项")
|
|
62
|
+
department_candidates.add_argument("--app-key", required=True)
|
|
63
|
+
department_candidates.add_argument("--field-id", type=int, required=True)
|
|
64
|
+
department_candidates.add_argument("--keyword", default="")
|
|
65
|
+
department_candidates.add_argument("--page-num", type=int, default=1)
|
|
66
|
+
department_candidates.add_argument("--page-size", type=int, default=20)
|
|
67
|
+
department_candidates.add_argument("--record-id")
|
|
68
|
+
department_candidates.add_argument("--workflow-node-id", type=int)
|
|
69
|
+
department_candidates.add_argument("--fields-file")
|
|
70
|
+
department_candidates.set_defaults(handler=_handle_department_candidates, format_hint="")
|
|
71
|
+
|
|
50
72
|
list_parser = record_subparsers.add_parser("list", help="列出记录")
|
|
51
73
|
list_parser.add_argument("--app-key", required=True)
|
|
52
74
|
list_parser.add_argument("--column", dest="columns", action="append", type=int, default=[])
|
|
@@ -80,9 +102,16 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
80
102
|
get.add_argument("--view-id")
|
|
81
103
|
get.set_defaults(handler=_handle_get, format_hint="record_get")
|
|
82
104
|
|
|
105
|
+
logs = record_subparsers.add_parser("logs", help="读取单条记录全量日志并写入本地 JSONL")
|
|
106
|
+
logs.add_argument("--app-key", required=True)
|
|
107
|
+
logs.add_argument("--record-id", required=True)
|
|
108
|
+
logs.add_argument("--view-id")
|
|
109
|
+
logs.set_defaults(handler=_handle_logs, format_hint="record_logs")
|
|
110
|
+
|
|
83
111
|
insert = record_subparsers.add_parser("insert", help="新增记录")
|
|
84
112
|
insert.add_argument("--app-key", required=True)
|
|
85
|
-
insert.add_argument("--fields-file",
|
|
113
|
+
insert.add_argument("--fields-file", help=argparse.SUPPRESS)
|
|
114
|
+
insert.add_argument("--items-file")
|
|
86
115
|
insert.add_argument("--verify-write", action=argparse.BooleanOptionalAction, default=True)
|
|
87
116
|
insert.set_defaults(handler=_handle_insert, format_hint="")
|
|
88
117
|
|
|
@@ -206,6 +235,38 @@ def _handle_schema_code_block(args: argparse.Namespace, context: CliContext) ->
|
|
|
206
235
|
return context.code_block.record_code_block_schema_get_public(profile=args.profile, app_key=args.app_key)
|
|
207
236
|
|
|
208
237
|
|
|
238
|
+
def _candidate_context_fields(args: argparse.Namespace) -> dict[str, Any]:
|
|
239
|
+
return load_object_arg(args.fields_file, option_name="--fields-file") or {}
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def _handle_member_candidates(args: argparse.Namespace, context: CliContext) -> dict:
|
|
243
|
+
return context.record.record_member_candidates(
|
|
244
|
+
profile=args.profile,
|
|
245
|
+
app_key=args.app_key,
|
|
246
|
+
field_id=args.field_id,
|
|
247
|
+
record_id=args.record_id,
|
|
248
|
+
workflow_node_id=args.workflow_node_id,
|
|
249
|
+
fields=_candidate_context_fields(args),
|
|
250
|
+
keyword=args.keyword,
|
|
251
|
+
page_num=args.page_num,
|
|
252
|
+
page_size=args.page_size,
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _handle_department_candidates(args: argparse.Namespace, context: CliContext) -> dict:
|
|
257
|
+
return context.record.record_department_candidates(
|
|
258
|
+
profile=args.profile,
|
|
259
|
+
app_key=args.app_key,
|
|
260
|
+
field_id=args.field_id,
|
|
261
|
+
record_id=args.record_id,
|
|
262
|
+
workflow_node_id=args.workflow_node_id,
|
|
263
|
+
fields=_candidate_context_fields(args),
|
|
264
|
+
keyword=args.keyword,
|
|
265
|
+
page_num=args.page_num,
|
|
266
|
+
page_size=args.page_size,
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
|
|
209
270
|
def _validate_public_view_selector(
|
|
210
271
|
*,
|
|
211
272
|
view_id: str | None,
|
|
@@ -273,7 +334,33 @@ def _handle_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
273
334
|
)
|
|
274
335
|
|
|
275
336
|
|
|
337
|
+
def _handle_logs(args: argparse.Namespace, context: CliContext) -> dict:
|
|
338
|
+
return context.record.record_logs_get(
|
|
339
|
+
profile=args.profile,
|
|
340
|
+
app_key=args.app_key,
|
|
341
|
+
record_id=args.record_id,
|
|
342
|
+
view_id=args.view_id,
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
|
|
276
346
|
def _handle_insert(args: argparse.Namespace, context: CliContext) -> dict:
|
|
347
|
+
if args.items_file:
|
|
348
|
+
if args.fields_file:
|
|
349
|
+
raise_config_error(
|
|
350
|
+
"record insert batch mode does not accept --fields-file.",
|
|
351
|
+
fix_hint="Use `record insert --app-key APP_KEY --items-file ITEMS.json` for batch inserts.",
|
|
352
|
+
)
|
|
353
|
+
return context.record.record_insert_public(
|
|
354
|
+
profile=args.profile,
|
|
355
|
+
app_key=args.app_key,
|
|
356
|
+
items=require_list_arg(args.items_file, option_name="--items-file"),
|
|
357
|
+
verify_write=bool(args.verify_write),
|
|
358
|
+
)
|
|
359
|
+
if not args.fields_file:
|
|
360
|
+
raise_config_error(
|
|
361
|
+
"record insert requires --items-file.",
|
|
362
|
+
fix_hint="Use `record insert --app-key APP_KEY --items-file ITEMS.json`; a single insert is one item in the JSON array.",
|
|
363
|
+
)
|
|
277
364
|
return context.record.record_insert_public(
|
|
278
365
|
profile=args.profile,
|
|
279
366
|
app_key=args.app_key,
|
|
@@ -292,6 +292,37 @@ def _format_record_get(result: dict[str, Any]) -> str:
|
|
|
292
292
|
return "\n".join(lines) + "\n"
|
|
293
293
|
|
|
294
294
|
|
|
295
|
+
def _format_record_logs(result: dict[str, Any]) -> str:
|
|
296
|
+
app = result.get("app") if isinstance(result.get("app"), dict) else {}
|
|
297
|
+
view = result.get("view") if isinstance(result.get("view"), dict) else {}
|
|
298
|
+
record = result.get("record") if isinstance(result.get("record"), dict) else {}
|
|
299
|
+
data_logs = result.get("data_logs") if isinstance(result.get("data_logs"), dict) else {}
|
|
300
|
+
workflow_logs = result.get("workflow_logs") if isinstance(result.get("workflow_logs"), dict) else {}
|
|
301
|
+
integrity = result.get("context_integrity") if isinstance(result.get("context_integrity"), dict) else {}
|
|
302
|
+
lines = [
|
|
303
|
+
f"Status: {result.get('status') or '-'}",
|
|
304
|
+
f"App: {app.get('app_name') or app.get('app_key') or '-'} ({app.get('app_key') or '-'})",
|
|
305
|
+
f"View: {view.get('name') or view.get('view_id') or '-'}",
|
|
306
|
+
f"Record: {record.get('title') or '-'} ({record.get('record_id') or '-'})",
|
|
307
|
+
f"Data logs: {data_logs.get('status') or '-'} / count={data_logs.get('items_count')} / pages={data_logs.get('pages_fetched')} / complete={data_logs.get('complete')}",
|
|
308
|
+
f"Workflow logs: {workflow_logs.get('status') or '-'} / count={workflow_logs.get('items_count')} / pages={workflow_logs.get('pages_fetched')} / complete={workflow_logs.get('complete')}",
|
|
309
|
+
f"Safe for full log conclusion: {integrity.get('safe_for_full_log_conclusion')}",
|
|
310
|
+
]
|
|
311
|
+
if result.get("local_dir"):
|
|
312
|
+
lines.append(f"Local dir: {result.get('local_dir')}")
|
|
313
|
+
if data_logs.get("local_path"):
|
|
314
|
+
lines.append(f"Data logs file: {data_logs.get('local_path')}")
|
|
315
|
+
if workflow_logs.get("local_path"):
|
|
316
|
+
lines.append(f"Workflow logs file: {workflow_logs.get('local_path')}")
|
|
317
|
+
if result.get("summary_path"):
|
|
318
|
+
lines.append(f"Summary file: {result.get('summary_path')}")
|
|
319
|
+
_append_warnings(lines, result.get("warnings"))
|
|
320
|
+
unavailable = result.get("unavailable_context") if isinstance(result.get("unavailable_context"), list) else []
|
|
321
|
+
if unavailable:
|
|
322
|
+
lines.append(f"Unavailable contexts: {len(unavailable)}")
|
|
323
|
+
return "\n".join(lines) + "\n"
|
|
324
|
+
|
|
325
|
+
|
|
295
326
|
def _format_task_list(result: dict[str, Any]) -> str:
|
|
296
327
|
data = result.get("data") if isinstance(result.get("data"), dict) else {}
|
|
297
328
|
items = data.get("items") if isinstance(data.get("items"), list) else []
|
|
@@ -788,11 +819,11 @@ _FORMATTERS = {
|
|
|
788
819
|
"workspace_get": _format_workspace_get,
|
|
789
820
|
"workspace_select": _format_workspace_select,
|
|
790
821
|
"app_list": _format_app_items,
|
|
791
|
-
"app_search": _format_app_items,
|
|
792
822
|
"app_get": _format_app_get,
|
|
793
823
|
"record_list": _format_record_list,
|
|
794
824
|
"record_access": _format_record_access,
|
|
795
825
|
"record_get": _format_record_get,
|
|
826
|
+
"record_logs": _format_record_logs,
|
|
796
827
|
"task_list": _format_task_list,
|
|
797
828
|
"task_workbench": _format_task_workbench,
|
|
798
829
|
"task_get": _format_task_get,
|
|
@@ -8,6 +8,8 @@ from typing import Any, Callable, TextIO
|
|
|
8
8
|
from ..errors import QingflowApiError
|
|
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
|
+
from ..tools.ai_builder_tools import _attach_builder_apply_envelope
|
|
12
|
+
from ..version import get_cli_version, get_cli_version_info
|
|
11
13
|
from .context import CliContext, build_cli_context
|
|
12
14
|
from .formatters import emit_json_result, emit_text_result
|
|
13
15
|
from .commands import register_all_commands
|
|
@@ -16,11 +18,27 @@ from .commands import register_all_commands
|
|
|
16
18
|
Handler = Callable[[argparse.Namespace, CliContext], dict[str, Any]]
|
|
17
19
|
|
|
18
20
|
|
|
21
|
+
class _CliArgumentError(Exception):
|
|
22
|
+
def __init__(self, *, prog: str, message: str, usage: str) -> None:
|
|
23
|
+
super().__init__(message)
|
|
24
|
+
self.prog = prog
|
|
25
|
+
self.message = message
|
|
26
|
+
self.usage = usage
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class _QingflowArgumentParser(argparse.ArgumentParser):
|
|
30
|
+
def error(self, message: str) -> None:
|
|
31
|
+
raise _CliArgumentError(prog=self.prog, message=message, usage=self.format_usage())
|
|
32
|
+
|
|
33
|
+
|
|
19
34
|
def build_parser() -> argparse.ArgumentParser:
|
|
20
|
-
parser =
|
|
35
|
+
parser = _QingflowArgumentParser(prog="qingflow", description="Qingflow CLI")
|
|
21
36
|
parser.add_argument("--profile", default="default", help="会话 profile,默认 default")
|
|
22
37
|
parser.add_argument("--json", action="store_true", help="输出 JSON")
|
|
38
|
+
parser.add_argument("--version", action="store_true", help="输出 Qingflow CLI 版本")
|
|
23
39
|
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
40
|
+
version_parser = subparsers.add_parser("version", help="输出 Qingflow CLI 版本")
|
|
41
|
+
version_parser.set_defaults(handler=_handle_version, format_hint="version")
|
|
24
42
|
register_all_commands(subparsers)
|
|
25
43
|
return parser
|
|
26
44
|
|
|
@@ -40,19 +58,40 @@ def run(
|
|
|
40
58
|
err = stderr or sys.stderr
|
|
41
59
|
parser = build_parser()
|
|
42
60
|
normalized_argv = _normalize_global_args(list(argv) if argv is not None else sys.argv[1:])
|
|
61
|
+
if "--version" in normalized_argv:
|
|
62
|
+
return _emit_version(json_mode=_should_force_json_output_argv_for_version(normalized_argv), stdout=out)
|
|
43
63
|
try:
|
|
44
64
|
args = parser.parse_args(normalized_argv)
|
|
65
|
+
except _CliArgumentError as exc:
|
|
66
|
+
if _should_force_json_output_argv(normalized_argv):
|
|
67
|
+
payload = {
|
|
68
|
+
"category": "config",
|
|
69
|
+
"status": "failed",
|
|
70
|
+
"error_code": "ARGUMENT_ERROR",
|
|
71
|
+
"message": exc.message,
|
|
72
|
+
"details": {"usage": exc.usage.strip(), "prog": exc.prog},
|
|
73
|
+
}
|
|
74
|
+
payload = _maybe_attach_builder_apply_error_envelope_from_argv(normalized_argv, payload)
|
|
75
|
+
emit_json_result(payload, stream=out)
|
|
76
|
+
return 2
|
|
77
|
+
err.write(exc.usage)
|
|
78
|
+
err.write(f"{exc.prog}: error: {exc.message}\n")
|
|
79
|
+
return 2
|
|
45
80
|
except SystemExit as exc:
|
|
46
81
|
return int(exc.code or 0)
|
|
47
82
|
setattr(args, "_stdin", sys.stdin)
|
|
48
83
|
setattr(args, "_stdout_stream", out)
|
|
49
84
|
setattr(args, "_stderr_stream", err)
|
|
85
|
+
if getattr(args, "command", "") == "version":
|
|
86
|
+
return _emit_version(json_mode=bool(args.json), stdout=out)
|
|
50
87
|
handler = getattr(args, "handler", None)
|
|
51
88
|
if handler is None:
|
|
52
89
|
parser.print_help(out)
|
|
53
90
|
return 2
|
|
54
|
-
context = context_factory()
|
|
55
91
|
try:
|
|
92
|
+
if _should_force_json_output(args):
|
|
93
|
+
setattr(args, "json", True)
|
|
94
|
+
context = context_factory()
|
|
56
95
|
if not bool(args.json):
|
|
57
96
|
_emit_cli_effective_context_notice(args, context, stream=err)
|
|
58
97
|
result = handler(args, context)
|
|
@@ -60,12 +99,15 @@ def run(
|
|
|
60
99
|
return int(exc.code or 0)
|
|
61
100
|
except RuntimeError as exc:
|
|
62
101
|
payload = trim_error_response(_parse_error_payload(exc))
|
|
102
|
+
payload = _maybe_attach_builder_apply_error_envelope_from_args(args, payload)
|
|
63
103
|
return _emit_error(payload, json_mode=bool(args.json), stdout=out, stderr=err)
|
|
64
104
|
except QingflowApiError as exc:
|
|
65
105
|
payload = trim_error_response(exc.to_dict())
|
|
106
|
+
payload = _maybe_attach_builder_apply_error_envelope_from_args(args, payload)
|
|
66
107
|
return _emit_error(payload, json_mode=bool(args.json), stdout=out, stderr=err)
|
|
67
108
|
finally:
|
|
68
|
-
context
|
|
109
|
+
if "context" in locals():
|
|
110
|
+
context.close()
|
|
69
111
|
|
|
70
112
|
exit_code = _result_exit_code(result)
|
|
71
113
|
trimmed_result = trim_public_response(resolve_cli_tool_name(args), result) if isinstance(result, dict) else result
|
|
@@ -87,6 +129,10 @@ def _normalize_global_args(argv: list[str]) -> list[str]:
|
|
|
87
129
|
global_args.append(token)
|
|
88
130
|
index += 1
|
|
89
131
|
continue
|
|
132
|
+
if token == "--version":
|
|
133
|
+
global_args.append(token)
|
|
134
|
+
index += 1
|
|
135
|
+
continue
|
|
90
136
|
if token == "--profile":
|
|
91
137
|
global_args.append(token)
|
|
92
138
|
if index + 1 >= len(argv):
|
|
@@ -104,6 +150,202 @@ def _normalize_global_args(argv: list[str]) -> list[str]:
|
|
|
104
150
|
return global_args + remaining
|
|
105
151
|
|
|
106
152
|
|
|
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
|
+
def _should_force_json_output(args: argparse.Namespace) -> bool:
|
|
183
|
+
if bool(getattr(args, "force_json_output", False)):
|
|
184
|
+
return True
|
|
185
|
+
if (
|
|
186
|
+
getattr(args, "command", "") == "builder"
|
|
187
|
+
and getattr(args, "builder_app_command", "") == "repair-code-blocks"
|
|
188
|
+
and bool(getattr(args, "apply", False))
|
|
189
|
+
):
|
|
190
|
+
return True
|
|
191
|
+
return False
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _should_force_json_output_argv(argv: list[str]) -> bool:
|
|
195
|
+
tokens = _strip_global_args(argv)
|
|
196
|
+
if not tokens or tokens[0] not in {"builder", "build"}:
|
|
197
|
+
return False
|
|
198
|
+
if len(tokens) < 3:
|
|
199
|
+
return False
|
|
200
|
+
section = tokens[1]
|
|
201
|
+
action = tokens[2]
|
|
202
|
+
if section in {"package", "button", "associated-resource", "associated-resources", "portal", "schema", "layout", "views", "flow", "charts"}:
|
|
203
|
+
return action == "apply"
|
|
204
|
+
if section == "publish":
|
|
205
|
+
return action == "verify"
|
|
206
|
+
if section == "app":
|
|
207
|
+
if action == "release-edit-lock-if-mine":
|
|
208
|
+
return True
|
|
209
|
+
if action == "repair-code-blocks":
|
|
210
|
+
return "--apply" in tokens
|
|
211
|
+
return False
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _maybe_attach_builder_apply_error_envelope_from_args(args: argparse.Namespace, payload: dict[str, Any]) -> dict[str, Any]:
|
|
215
|
+
operation = _builder_apply_operation_from_args(args)
|
|
216
|
+
if not operation:
|
|
217
|
+
return payload
|
|
218
|
+
enriched = dict(payload)
|
|
219
|
+
enriched.setdefault("status", "failed")
|
|
220
|
+
enriched.setdefault("ok", False)
|
|
221
|
+
enriched.setdefault("write_executed", False)
|
|
222
|
+
enriched.setdefault("safe_to_retry", False)
|
|
223
|
+
_copy_arg_identity(enriched, args)
|
|
224
|
+
return _attach_builder_apply_envelope(operation, enriched)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _maybe_attach_builder_apply_error_envelope_from_argv(argv: list[str], payload: dict[str, Any]) -> dict[str, Any]:
|
|
228
|
+
operation = _builder_apply_operation_from_argv(argv)
|
|
229
|
+
if not operation:
|
|
230
|
+
return payload
|
|
231
|
+
enriched = dict(payload)
|
|
232
|
+
enriched.setdefault("status", "failed")
|
|
233
|
+
enriched.setdefault("ok", False)
|
|
234
|
+
enriched.setdefault("write_executed", False)
|
|
235
|
+
enriched.setdefault("safe_to_retry", False)
|
|
236
|
+
_copy_argv_identity(enriched, argv)
|
|
237
|
+
return _attach_builder_apply_envelope(operation, enriched)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def _builder_apply_operation_from_args(args: argparse.Namespace) -> str | None:
|
|
241
|
+
if getattr(args, "command", "") not in {"builder", "build"}:
|
|
242
|
+
return None
|
|
243
|
+
section = str(getattr(args, "builder_command", "") or "")
|
|
244
|
+
if section == "package" and getattr(args, "builder_package_command", "") == "apply":
|
|
245
|
+
return "package_apply"
|
|
246
|
+
if section == "button" and getattr(args, "builder_button_command", "") == "apply":
|
|
247
|
+
return "app_custom_buttons_apply"
|
|
248
|
+
if section in {"associated-resource", "associated-resources"} and getattr(args, "builder_associated_resource_command", "") == "apply":
|
|
249
|
+
return "app_associated_resources_apply"
|
|
250
|
+
if section == "portal" and getattr(args, "builder_portal_command", "") == "apply":
|
|
251
|
+
return "portal_apply"
|
|
252
|
+
if section == "schema" and getattr(args, "builder_schema_command", "") == "apply":
|
|
253
|
+
return "app_schema_apply"
|
|
254
|
+
if section == "layout" and getattr(args, "builder_layout_command", "") == "apply":
|
|
255
|
+
return "app_layout_apply"
|
|
256
|
+
if section == "views" and getattr(args, "builder_views_command", "") == "apply":
|
|
257
|
+
return "app_views_apply"
|
|
258
|
+
if section == "flow" and getattr(args, "builder_flow_command", "") == "apply":
|
|
259
|
+
return "app_flow_apply"
|
|
260
|
+
if section == "charts" and getattr(args, "builder_charts_command", "") == "apply":
|
|
261
|
+
return "app_charts_apply"
|
|
262
|
+
if section == "publish" and getattr(args, "builder_publish_command", "") == "verify":
|
|
263
|
+
return "app_publish_verify"
|
|
264
|
+
return None
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _builder_apply_operation_from_argv(argv: list[str]) -> str | None:
|
|
268
|
+
tokens = _strip_global_args(argv)
|
|
269
|
+
if not tokens or tokens[0] not in {"builder", "build"} or len(tokens) < 3:
|
|
270
|
+
return None
|
|
271
|
+
section = tokens[1]
|
|
272
|
+
action = tokens[2]
|
|
273
|
+
if action != "apply" and not (section == "publish" and action == "verify"):
|
|
274
|
+
return None
|
|
275
|
+
mapping = {
|
|
276
|
+
"package": "package_apply",
|
|
277
|
+
"button": "app_custom_buttons_apply",
|
|
278
|
+
"associated-resource": "app_associated_resources_apply",
|
|
279
|
+
"associated-resources": "app_associated_resources_apply",
|
|
280
|
+
"portal": "portal_apply",
|
|
281
|
+
"schema": "app_schema_apply",
|
|
282
|
+
"layout": "app_layout_apply",
|
|
283
|
+
"views": "app_views_apply",
|
|
284
|
+
"flow": "app_flow_apply",
|
|
285
|
+
"charts": "app_charts_apply",
|
|
286
|
+
"publish": "app_publish_verify",
|
|
287
|
+
}
|
|
288
|
+
return mapping.get(section)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def _copy_arg_identity(payload: dict[str, Any], args: argparse.Namespace) -> None:
|
|
292
|
+
for attr, key in (
|
|
293
|
+
("app_key", "app_key"),
|
|
294
|
+
("app_name", "app_name"),
|
|
295
|
+
("app_title", "app_title"),
|
|
296
|
+
("package_id", "package_id"),
|
|
297
|
+
("dash_key", "dash_key"),
|
|
298
|
+
("dash_name", "dash_name"),
|
|
299
|
+
):
|
|
300
|
+
value = getattr(args, attr, None)
|
|
301
|
+
if value not in (None, ""):
|
|
302
|
+
payload.setdefault(key, value)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def _copy_argv_identity(payload: dict[str, Any], argv: list[str]) -> None:
|
|
306
|
+
tokens = _strip_global_args(argv)
|
|
307
|
+
option_to_key = {
|
|
308
|
+
"--app-key": "app_key",
|
|
309
|
+
"--app-name": "app_name",
|
|
310
|
+
"--app-title": "app_title",
|
|
311
|
+
"--package-id": "package_id",
|
|
312
|
+
"--dash-key": "dash_key",
|
|
313
|
+
"--dash-name": "dash_name",
|
|
314
|
+
}
|
|
315
|
+
index = 0
|
|
316
|
+
while index < len(tokens):
|
|
317
|
+
token = tokens[index]
|
|
318
|
+
if token in option_to_key and index + 1 < len(tokens):
|
|
319
|
+
payload.setdefault(option_to_key[token], tokens[index + 1])
|
|
320
|
+
index += 2
|
|
321
|
+
continue
|
|
322
|
+
for option, key in option_to_key.items():
|
|
323
|
+
prefix = f"{option}="
|
|
324
|
+
if token.startswith(prefix):
|
|
325
|
+
payload.setdefault(key, token[len(prefix) :])
|
|
326
|
+
break
|
|
327
|
+
index += 1
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def _strip_global_args(argv: list[str]) -> list[str]:
|
|
331
|
+
stripped: list[str] = []
|
|
332
|
+
index = 0
|
|
333
|
+
while index < len(argv):
|
|
334
|
+
token = argv[index]
|
|
335
|
+
if token == "--json":
|
|
336
|
+
index += 1
|
|
337
|
+
continue
|
|
338
|
+
if token == "--profile":
|
|
339
|
+
index += 2
|
|
340
|
+
continue
|
|
341
|
+
if token.startswith("--profile="):
|
|
342
|
+
index += 1
|
|
343
|
+
continue
|
|
344
|
+
stripped.append(token)
|
|
345
|
+
index += 1
|
|
346
|
+
return stripped
|
|
347
|
+
|
|
348
|
+
|
|
107
349
|
def _parse_error_payload(exc: RuntimeError) -> dict[str, Any]:
|
|
108
350
|
raw = str(exc)
|
|
109
351
|
try:
|
|
@@ -38,7 +38,6 @@ USER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
|
|
|
38
38
|
PublicToolSpec(USER_DOMAIN, "workspace_get", ("workspace_get",), ("workspace", "get")),
|
|
39
39
|
PublicToolSpec(USER_DOMAIN, "workspace_select", ("workspace_select",), ("workspace", "select")),
|
|
40
40
|
PublicToolSpec(USER_DOMAIN, "app_list", ("app_list",), ("app", "list"), cli_show_effective_context=True),
|
|
41
|
-
PublicToolSpec(USER_DOMAIN, "app_search", ("app_search",), ("app", "search"), cli_show_effective_context=True),
|
|
42
41
|
PublicToolSpec(USER_DOMAIN, "app_get", ("app_get",), ("app", "get"), cli_show_effective_context=True),
|
|
43
42
|
PublicToolSpec(USER_DOMAIN, "portal_list", ("portal_list",), ("portal", "list"), cli_show_effective_context=True),
|
|
44
43
|
PublicToolSpec(USER_DOMAIN, "portal_get", ("portal_get",), ("portal", "get"), cli_show_effective_context=True),
|
|
@@ -78,12 +77,13 @@ USER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
|
|
|
78
77
|
("record_code_block_schema_get_public",),
|
|
79
78
|
("record", "schema", "code-block"),
|
|
80
79
|
),
|
|
81
|
-
PublicToolSpec(USER_DOMAIN, "record_member_candidates", ("record_member_candidates",),
|
|
82
|
-
PublicToolSpec(USER_DOMAIN, "record_department_candidates", ("record_department_candidates",),
|
|
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")),
|
|
83
82
|
PublicToolSpec(USER_DOMAIN, "record_analyze", ("record_analyze",), ("record", "analyze"), mcp_public=False),
|
|
84
83
|
PublicToolSpec(USER_DOMAIN, "record_list", ("record_list",), ("record", "list"), cli_show_effective_context=True),
|
|
85
84
|
PublicToolSpec(USER_DOMAIN, "record_access", ("record_access",), ("record", "access"), cli_show_effective_context=True),
|
|
86
85
|
PublicToolSpec(USER_DOMAIN, "record_get", ("record_get_public",), ("record", "get"), cli_show_effective_context=True),
|
|
86
|
+
PublicToolSpec(USER_DOMAIN, "record_logs_get", ("record_logs_get",), ("record", "logs"), cli_show_effective_context=True),
|
|
87
87
|
PublicToolSpec(USER_DOMAIN, "record_insert", ("record_insert_public",), ("record", "insert"), cli_show_effective_context=True, cli_context_write=True),
|
|
88
88
|
PublicToolSpec(USER_DOMAIN, "record_update", ("record_update_public",), ("record", "update"), cli_show_effective_context=True, cli_context_write=True),
|
|
89
89
|
PublicToolSpec(USER_DOMAIN, "record_delete", ("record_delete_public",), ("record", "delete"), cli_show_effective_context=True, cli_context_write=True),
|
|
@@ -127,6 +127,8 @@ BUILDER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
|
|
|
127
127
|
PublicToolSpec(BUILDER_DOMAIN, "file_upload_local", ("file_upload_local",), ("builder", "file", "upload-local"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
128
128
|
PublicToolSpec(BUILDER_DOMAIN, "feedback_submit", ("feedback_submit",), ("builder", "feedback", "submit"), has_contract=True),
|
|
129
129
|
PublicToolSpec(BUILDER_DOMAIN, "builder_tool_contract", ("builder_tool_contract",), ("builder", "contract"), has_contract=False),
|
|
130
|
+
PublicToolSpec(BUILDER_DOMAIN, "workspace_icon_catalog_get", ("workspace_icon_catalog_get",), ("builder", "icon", "catalog"), has_contract=True, cli_show_effective_context=True),
|
|
131
|
+
PublicToolSpec(BUILDER_DOMAIN, "package_list", ("package_list",), ("builder", "package", "list"), has_contract=True, cli_show_effective_context=True),
|
|
130
132
|
PublicToolSpec(BUILDER_DOMAIN, "package_get", ("package_get",), ("builder", "package", "get"), has_contract=True, cli_show_effective_context=True),
|
|
131
133
|
PublicToolSpec(BUILDER_DOMAIN, "package_apply", ("package_apply",), ("builder", "package", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
132
134
|
PublicToolSpec(BUILDER_DOMAIN, "solution_install", ("solution_install",), ("builder", "solution", "install"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
@@ -136,11 +138,10 @@ BUILDER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
|
|
|
136
138
|
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),
|
|
137
139
|
PublicToolSpec(BUILDER_DOMAIN, "app_resolve", ("app_resolve",), ("builder", "app", "resolve"), has_contract=True, cli_show_effective_context=True),
|
|
138
140
|
PublicToolSpec(BUILDER_DOMAIN, "button_style_catalog_get", ("button_style_catalog_get",), ("builder", "button", "catalog"), has_contract=True, cli_show_effective_context=True),
|
|
139
|
-
PublicToolSpec(BUILDER_DOMAIN, "
|
|
140
|
-
PublicToolSpec(BUILDER_DOMAIN, "
|
|
141
|
-
PublicToolSpec(BUILDER_DOMAIN, "
|
|
142
|
-
PublicToolSpec(BUILDER_DOMAIN, "
|
|
143
|
-
PublicToolSpec(BUILDER_DOMAIN, "app_custom_button_delete", ("app_custom_button_delete",), ("builder", "button", "delete"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
141
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_get_buttons", ("app_get_buttons",), ("builder", "button", "get"), has_contract=True, cli_show_effective_context=True),
|
|
142
|
+
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
|
+
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),
|
|
144
145
|
PublicToolSpec(BUILDER_DOMAIN, "app_get", ("app_get",), ("builder", "app", "get", "summary"), has_contract=True, cli_show_effective_context=True),
|
|
145
146
|
PublicToolSpec(BUILDER_DOMAIN, "app_get_fields", ("app_get_fields",), ("builder", "app", "get", "fields"), has_contract=True, cli_show_effective_context=True),
|
|
146
147
|
PublicToolSpec(BUILDER_DOMAIN, "app_repair_code_blocks", ("app_repair_code_blocks",), ("builder", "app", "repair-code-blocks"), has_contract=True, cli_show_effective_context=True),
|
|
@@ -154,6 +155,8 @@ BUILDER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
|
|
|
154
155
|
PublicToolSpec(BUILDER_DOMAIN, "chart_get", ("chart_get",), ("builder", "chart", "get"), has_contract=True, cli_show_effective_context=True),
|
|
155
156
|
PublicToolSpec(BUILDER_DOMAIN, "app_schema_apply", ("app_schema_apply",), ("builder", "schema", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
156
157
|
PublicToolSpec(BUILDER_DOMAIN, "app_layout_apply", ("app_layout_apply",), ("builder", "layout", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
158
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_flow_get_schema", ("app_flow_get_schema",), ("builder", "flow", "schema"), has_contract=True, cli_show_effective_context=True),
|
|
159
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_flow_get", ("app_flow_get",), ("builder", "flow", "get"), has_contract=True, cli_show_effective_context=True),
|
|
157
160
|
PublicToolSpec(BUILDER_DOMAIN, "app_flow_apply", ("app_flow_apply",), ("builder", "flow", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
158
161
|
PublicToolSpec(BUILDER_DOMAIN, "app_views_apply", ("app_views_apply",), ("builder", "views", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
159
162
|
PublicToolSpec(BUILDER_DOMAIN, "app_charts_apply", ("app_charts_apply",), ("builder", "charts", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|