@josephyan/qingflow-cli 0.2.0-beta.1000

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/README.md +31 -0
  2. package/docs/local-agent-install.md +309 -0
  3. package/entry_point.py +13 -0
  4. package/npm/bin/qingflow.mjs +5 -0
  5. package/npm/lib/runtime.mjs +346 -0
  6. package/npm/scripts/postinstall.mjs +16 -0
  7. package/package.json +34 -0
  8. package/pyproject.toml +67 -0
  9. package/qingflow +15 -0
  10. package/src/qingflow_mcp/__init__.py +37 -0
  11. package/src/qingflow_mcp/__main__.py +5 -0
  12. package/src/qingflow_mcp/backend_client.py +649 -0
  13. package/src/qingflow_mcp/builder_facade/__init__.py +3 -0
  14. package/src/qingflow_mcp/builder_facade/models.py +1846 -0
  15. package/src/qingflow_mcp/builder_facade/service.py +16502 -0
  16. package/src/qingflow_mcp/cli/__init__.py +1 -0
  17. package/src/qingflow_mcp/cli/commands/__init__.py +18 -0
  18. package/src/qingflow_mcp/cli/commands/app.py +40 -0
  19. package/src/qingflow_mcp/cli/commands/auth.py +112 -0
  20. package/src/qingflow_mcp/cli/commands/builder.py +539 -0
  21. package/src/qingflow_mcp/cli/commands/chart.py +18 -0
  22. package/src/qingflow_mcp/cli/commands/common.py +62 -0
  23. package/src/qingflow_mcp/cli/commands/imports.py +96 -0
  24. package/src/qingflow_mcp/cli/commands/portal.py +25 -0
  25. package/src/qingflow_mcp/cli/commands/record.py +331 -0
  26. package/src/qingflow_mcp/cli/commands/repo.py +80 -0
  27. package/src/qingflow_mcp/cli/commands/task.py +141 -0
  28. package/src/qingflow_mcp/cli/commands/view.py +18 -0
  29. package/src/qingflow_mcp/cli/commands/workspace.py +110 -0
  30. package/src/qingflow_mcp/cli/context.py +60 -0
  31. package/src/qingflow_mcp/cli/formatters.py +573 -0
  32. package/src/qingflow_mcp/cli/json_io.py +50 -0
  33. package/src/qingflow_mcp/cli/main.py +186 -0
  34. package/src/qingflow_mcp/cli/qingflow_login.py +116 -0
  35. package/src/qingflow_mcp/cli/terminal_ui.py +173 -0
  36. package/src/qingflow_mcp/config.py +407 -0
  37. package/src/qingflow_mcp/errors.py +66 -0
  38. package/src/qingflow_mcp/id_utils.py +49 -0
  39. package/src/qingflow_mcp/import_store.py +121 -0
  40. package/src/qingflow_mcp/json_types.py +18 -0
  41. package/src/qingflow_mcp/list_type_labels.py +76 -0
  42. package/src/qingflow_mcp/public_surface.py +243 -0
  43. package/src/qingflow_mcp/repository_store.py +71 -0
  44. package/src/qingflow_mcp/response_trim.py +841 -0
  45. package/src/qingflow_mcp/server.py +216 -0
  46. package/src/qingflow_mcp/server_app_builder.py +543 -0
  47. package/src/qingflow_mcp/server_app_user.py +386 -0
  48. package/src/qingflow_mcp/session_store.py +369 -0
  49. package/src/qingflow_mcp/solution/__init__.py +6 -0
  50. package/src/qingflow_mcp/solution/build_assembly_store.py +181 -0
  51. package/src/qingflow_mcp/solution/compiler/__init__.py +282 -0
  52. package/src/qingflow_mcp/solution/compiler/chart_compiler.py +96 -0
  53. package/src/qingflow_mcp/solution/compiler/form_compiler.py +495 -0
  54. package/src/qingflow_mcp/solution/compiler/icon_utils.py +187 -0
  55. package/src/qingflow_mcp/solution/compiler/navigation_compiler.py +57 -0
  56. package/src/qingflow_mcp/solution/compiler/package_compiler.py +19 -0
  57. package/src/qingflow_mcp/solution/compiler/portal_compiler.py +60 -0
  58. package/src/qingflow_mcp/solution/compiler/view_compiler.py +51 -0
  59. package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +173 -0
  60. package/src/qingflow_mcp/solution/design_session.py +222 -0
  61. package/src/qingflow_mcp/solution/design_store.py +100 -0
  62. package/src/qingflow_mcp/solution/executor.py +2398 -0
  63. package/src/qingflow_mcp/solution/normalizer.py +23 -0
  64. package/src/qingflow_mcp/solution/requirements_builder.py +536 -0
  65. package/src/qingflow_mcp/solution/run_store.py +244 -0
  66. package/src/qingflow_mcp/solution/spec_models.py +855 -0
  67. package/src/qingflow_mcp/tools/__init__.py +1 -0
  68. package/src/qingflow_mcp/tools/ai_builder_tools.py +3449 -0
  69. package/src/qingflow_mcp/tools/app_tools.py +926 -0
  70. package/src/qingflow_mcp/tools/approval_tools.py +1062 -0
  71. package/src/qingflow_mcp/tools/auth_tools.py +1133 -0
  72. package/src/qingflow_mcp/tools/base.py +281 -0
  73. package/src/qingflow_mcp/tools/code_block_tools.py +777 -0
  74. package/src/qingflow_mcp/tools/custom_button_tools.py +202 -0
  75. package/src/qingflow_mcp/tools/directory_tools.py +675 -0
  76. package/src/qingflow_mcp/tools/feedback_tools.py +238 -0
  77. package/src/qingflow_mcp/tools/file_tools.py +409 -0
  78. package/src/qingflow_mcp/tools/import_tools.py +2223 -0
  79. package/src/qingflow_mcp/tools/navigation_tools.py +210 -0
  80. package/src/qingflow_mcp/tools/package_tools.py +326 -0
  81. package/src/qingflow_mcp/tools/portal_tools.py +158 -0
  82. package/src/qingflow_mcp/tools/qingbi_report_tools.py +374 -0
  83. package/src/qingflow_mcp/tools/record_tools.py +14291 -0
  84. package/src/qingflow_mcp/tools/repository_dev_tools.py +552 -0
  85. package/src/qingflow_mcp/tools/resource_read_tools.py +503 -0
  86. package/src/qingflow_mcp/tools/role_tools.py +112 -0
  87. package/src/qingflow_mcp/tools/solution_tools.py +4054 -0
  88. package/src/qingflow_mcp/tools/task_context_tools.py +2986 -0
  89. package/src/qingflow_mcp/tools/task_tools.py +889 -0
  90. package/src/qingflow_mcp/tools/view_tools.py +335 -0
  91. package/src/qingflow_mcp/tools/workflow_tools.py +376 -0
  92. package/src/qingflow_mcp/tools/workspace_tools.py +266 -0
@@ -0,0 +1,96 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+
5
+ from ..context import CliContext
6
+ from .common import parse_bool_text, raise_config_error
7
+
8
+
9
+ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
10
+ parser = subparsers.add_parser("import", help="导入")
11
+ import_subparsers = parser.add_subparsers(dest="import_command", required=True)
12
+
13
+ template = import_subparsers.add_parser("template", help="下载导入模板")
14
+ template.add_argument("--app-key", required=True)
15
+ template.add_argument("--download-to-path")
16
+ template.set_defaults(handler=_handle_template, format_hint="")
17
+
18
+ verify = import_subparsers.add_parser("verify", help="校验导入文件")
19
+ verify.add_argument("--app-key", required=True)
20
+ verify.add_argument("--file-path", required=True)
21
+ verify.set_defaults(handler=_handle_verify, format_hint="import_verify")
22
+
23
+ repair = import_subparsers.add_parser("repair", help="授权后修复导入文件")
24
+ repair.add_argument("--verification-id", required=True)
25
+ repair.add_argument("--authorized-file-modification", action="store_true")
26
+ repair.add_argument("--output-path")
27
+ repair.add_argument("--repair", dest="selected_repairs", action="append", default=[])
28
+ repair.set_defaults(handler=_handle_repair, format_hint="")
29
+
30
+ start = import_subparsers.add_parser("start", help="启动导入")
31
+ start.add_argument("--app-key", required=True)
32
+ start.add_argument("--verification-id", required=True)
33
+ start.add_argument("--being-enter-auditing", type=parse_bool_text, required=True)
34
+ start.add_argument("--view-key")
35
+ start.set_defaults(handler=_handle_start, format_hint="")
36
+
37
+ status = import_subparsers.add_parser("status", help="查询导入状态")
38
+ status.add_argument("--app-key")
39
+ status.add_argument("--import-id")
40
+ status.add_argument("--process-id-str")
41
+ status.set_defaults(handler=_handle_status, format_hint="import_status")
42
+
43
+
44
+ def _handle_template(args: argparse.Namespace, context: CliContext) -> dict:
45
+ return context.imports.record_import_template_get(
46
+ profile=args.profile,
47
+ app_key=args.app_key,
48
+ download_to_path=args.download_to_path,
49
+ )
50
+
51
+
52
+ def _handle_verify(args: argparse.Namespace, context: CliContext) -> dict:
53
+ return context.imports.record_import_verify(
54
+ profile=args.profile,
55
+ app_key=args.app_key,
56
+ file_path=args.file_path,
57
+ )
58
+
59
+
60
+ def _handle_repair(args: argparse.Namespace, context: CliContext) -> dict:
61
+ return context.imports.record_import_repair_local(
62
+ profile=args.profile,
63
+ verification_id=args.verification_id,
64
+ authorized_file_modification=bool(args.authorized_file_modification),
65
+ output_path=args.output_path,
66
+ selected_repairs=list(args.selected_repairs or []),
67
+ )
68
+
69
+
70
+ def _handle_start(args: argparse.Namespace, context: CliContext) -> dict:
71
+ return context.imports.record_import_start(
72
+ profile=args.profile,
73
+ app_key=args.app_key,
74
+ verification_id=args.verification_id,
75
+ being_enter_auditing=bool(args.being_enter_auditing),
76
+ view_key=args.view_key,
77
+ )
78
+
79
+
80
+ def _handle_status(args: argparse.Namespace, context: CliContext) -> dict:
81
+ selectors = [
82
+ bool((args.app_key or "").strip()),
83
+ bool((args.import_id or "").strip()),
84
+ bool((args.process_id_str or "").strip()),
85
+ ]
86
+ if sum(selectors) != 1:
87
+ raise_config_error(
88
+ "import status accepts exactly one selector: --process-id-str, --import-id, or --app-key.",
89
+ fix_hint="Use `--process-id-str` or `--import-id` for a known import, or use only `--app-key` to read the latest import in that app.",
90
+ )
91
+ return context.imports.record_import_status_get(
92
+ profile=args.profile,
93
+ app_key=args.app_key,
94
+ import_id=args.import_id,
95
+ process_id_str=args.process_id_str,
96
+ )
@@ -0,0 +1,25 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+
5
+ from ..context import CliContext
6
+
7
+
8
+ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
9
+ parser = subparsers.add_parser("portal", help="门户访问")
10
+ portal_subparsers = parser.add_subparsers(dest="portal_command", required=True)
11
+
12
+ list_parser = portal_subparsers.add_parser("list", help="列出当前用户可访问的门户")
13
+ list_parser.set_defaults(handler=_handle_list, format_hint="generic")
14
+
15
+ get_parser = portal_subparsers.add_parser("get", help="读取门户内容清单")
16
+ get_parser.add_argument("--dash-key", required=True)
17
+ get_parser.set_defaults(handler=_handle_get, format_hint="generic")
18
+
19
+
20
+ def _handle_list(args: argparse.Namespace, context: CliContext) -> dict:
21
+ return context.resource.portal_list(profile=args.profile)
22
+
23
+
24
+ def _handle_get(args: argparse.Namespace, context: CliContext) -> dict:
25
+ return context.resource.portal_get(profile=args.profile, dash_key=args.dash_key)
@@ -0,0 +1,331 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ from typing import Any
5
+
6
+ from ...errors import QingflowApiError
7
+ from ..context import CliContext
8
+ from .common import load_list_arg, load_object_arg, raise_config_error, require_list_arg, require_object_arg
9
+
10
+
11
+ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
12
+ parser = subparsers.add_parser("record", help="记录与表结构")
13
+ record_subparsers = parser.add_subparsers(dest="record_command", required=True)
14
+
15
+ schema = record_subparsers.add_parser("schema", help="读取记录相关表结构")
16
+ schema.add_argument("--mode", dest="legacy_mode", help=argparse.SUPPRESS)
17
+ schema_subparsers = schema.add_subparsers(dest="record_schema_command")
18
+ schema.set_defaults(handler=_handle_schema_root, format_hint="")
19
+
20
+ schema_applicant = schema_subparsers.add_parser("applicant", help="读取申请节点表结构")
21
+ schema_applicant.add_argument("--app-key", required=True)
22
+ schema_applicant.set_defaults(handler=_handle_schema_applicant, format_hint="")
23
+
24
+ schema_browse = schema_subparsers.add_parser("browse", help="读取浏览视图表结构")
25
+ schema_browse.add_argument("--app-key", required=True)
26
+ schema_browse.add_argument("--view-id", required=True)
27
+ schema_browse.set_defaults(handler=_handle_schema_browse, format_hint="")
28
+
29
+ schema_insert = schema_subparsers.add_parser("insert", help="读取新增记录表结构")
30
+ schema_insert.add_argument("--app-key", required=True)
31
+ schema_insert.set_defaults(handler=_handle_schema_insert, format_hint="")
32
+
33
+ schema_update = schema_subparsers.add_parser("update", help="读取更新记录表结构")
34
+ schema_update.add_argument("--app-key", required=True)
35
+ schema_update.add_argument("--record-id", required=True)
36
+ schema_update.set_defaults(handler=_handle_schema_update, format_hint="")
37
+
38
+ schema_import = schema_subparsers.add_parser("import", help="读取导入表结构")
39
+ schema_import.add_argument("--app-key", required=True)
40
+ schema_import.set_defaults(handler=_handle_schema_import, format_hint="")
41
+
42
+ schema_code_block = schema_subparsers.add_parser("code-block", help="读取代码块执行表结构")
43
+ schema_code_block.add_argument("--app-key", required=True)
44
+ schema_code_block.set_defaults(handler=_handle_schema_code_block, format_hint="")
45
+
46
+ list_parser = record_subparsers.add_parser("list", help="列出记录")
47
+ list_parser.add_argument("--app-key", required=True)
48
+ list_parser.add_argument("--column", dest="columns", action="append", type=int, default=[])
49
+ list_parser.add_argument("--columns-file")
50
+ list_parser.add_argument("--where-file")
51
+ list_parser.add_argument("--order-by-file")
52
+ list_parser.add_argument("--limit", type=int, default=20)
53
+ list_parser.add_argument("--page", type=int, default=1)
54
+ list_parser.add_argument("--view-id")
55
+ list_parser.add_argument("--list-type", dest="legacy_list_type", type=int, help=argparse.SUPPRESS)
56
+ list_parser.add_argument("--view-key", dest="legacy_view_key", help=argparse.SUPPRESS)
57
+ list_parser.add_argument("--view-name", dest="legacy_view_name", help=argparse.SUPPRESS)
58
+ list_parser.set_defaults(handler=_handle_list, format_hint="record_list")
59
+
60
+ get = record_subparsers.add_parser("get", help="读取单条记录")
61
+ get.add_argument("--app-key", required=True)
62
+ get.add_argument("--record-id", required=True)
63
+ get.add_argument("--column", dest="columns", action="append", type=int, default=[])
64
+ get.add_argument("--columns-file")
65
+ get.add_argument("--view-id")
66
+ get.set_defaults(handler=_handle_get, format_hint="")
67
+
68
+ insert = record_subparsers.add_parser("insert", help="新增记录")
69
+ insert.add_argument("--app-key", required=True)
70
+ insert.add_argument("--fields-file", required=True)
71
+ insert.add_argument("--verify-write", action=argparse.BooleanOptionalAction, default=True)
72
+ insert.set_defaults(handler=_handle_insert, format_hint="")
73
+
74
+ update = record_subparsers.add_parser("update", help="更新记录")
75
+ update.add_argument("--app-key", required=True)
76
+ update.add_argument("--record-id")
77
+ update.add_argument("--fields-file")
78
+ update.add_argument("--items-file")
79
+ update.add_argument("--dry-run", action=argparse.BooleanOptionalAction, default=False)
80
+ update.add_argument("--verify-write", action=argparse.BooleanOptionalAction, default=True)
81
+ update.set_defaults(handler=_handle_update, format_hint="")
82
+
83
+ delete = record_subparsers.add_parser("delete", help="删除记录")
84
+ delete.add_argument("--app-key", required=True)
85
+ delete.add_argument("--record-id")
86
+ delete.add_argument("--record-ids-file")
87
+ delete.set_defaults(handler=_handle_delete, format_hint="")
88
+
89
+ analyze = record_subparsers.add_parser("analyze", help="分析记录数据")
90
+ analyze.add_argument("--app-key", required=True)
91
+ analyze.add_argument("--dimensions-file")
92
+ analyze.add_argument("--metrics-file")
93
+ analyze.add_argument("--filters-file")
94
+ analyze.add_argument("--sort-file")
95
+ analyze.add_argument("--limit", type=int, default=20)
96
+ analyze.add_argument("--strict-full", action=argparse.BooleanOptionalAction, default=False)
97
+ analyze.add_argument("--view-id")
98
+ analyze.add_argument("--list-type", dest="legacy_list_type", type=int, help=argparse.SUPPRESS)
99
+ analyze.add_argument("--view-key", dest="legacy_view_key", help=argparse.SUPPRESS)
100
+ analyze.add_argument("--view-name", dest="legacy_view_name", help=argparse.SUPPRESS)
101
+ analyze.set_defaults(handler=_handle_analyze, format_hint="")
102
+
103
+ code_block = record_subparsers.add_parser("code-block-run", help="执行代码块字段")
104
+ code_block.add_argument("--app-key", required=True)
105
+ code_block.add_argument("--record-id", required=True)
106
+ code_block.add_argument("--code-block-field", required=True)
107
+ code_block.add_argument("--role", type=int, default=1)
108
+ code_block.add_argument("--workflow-node-id", type=int)
109
+ code_block.add_argument("--answers-file")
110
+ code_block.add_argument("--fields-file")
111
+ code_block.add_argument("--manual", action=argparse.BooleanOptionalAction, default=True)
112
+ code_block.add_argument("--apply-writeback", action=argparse.BooleanOptionalAction, default=True)
113
+ code_block.add_argument("--verify-writeback", action=argparse.BooleanOptionalAction, default=True)
114
+ code_block.add_argument("--force-refresh-form", action="store_true")
115
+ code_block.set_defaults(handler=_handle_code_block_run, format_hint="")
116
+
117
+
118
+ def _columns(args: argparse.Namespace) -> list[Any]:
119
+ columns: list[Any] = list(args.columns or [])
120
+ if args.columns_file:
121
+ columns.extend(require_list_arg(args.columns_file, option_name="--columns-file"))
122
+ return columns
123
+
124
+
125
+ def _handle_schema_root(args: argparse.Namespace, _context: CliContext) -> dict:
126
+ mode = (args.legacy_mode or "").strip()
127
+ if mode:
128
+ replacement = {
129
+ "applicant": "record schema applicant --app-key APP_KEY",
130
+ "browse": "record schema browse --app-key APP_KEY --view-id VIEW_ID",
131
+ "insert": "record schema insert --app-key APP_KEY",
132
+ "update": "record schema update --app-key APP_KEY --record-id RECORD_ID",
133
+ "import": "record schema import --app-key APP_KEY",
134
+ "code-block": "record schema code-block --app-key APP_KEY",
135
+ }.get(mode, "record schema <applicant|browse|insert|update|import|code-block> ...")
136
+ raise_config_error(
137
+ "record schema --mode is no longer accepted.",
138
+ fix_hint=f"Use `{replacement}` instead.",
139
+ )
140
+ raise_config_error(
141
+ "record schema requires an explicit subcommand.",
142
+ fix_hint="Use one of: `record schema applicant`, `record schema browse`, `record schema insert`, `record schema update`, `record schema import`, or `record schema code-block`.",
143
+ )
144
+
145
+
146
+ def _handle_schema_applicant(args: argparse.Namespace, context: CliContext) -> dict:
147
+ return context.record.record_schema_get(
148
+ profile=args.profile,
149
+ app_key=args.app_key,
150
+ schema_mode="applicant",
151
+ )
152
+
153
+
154
+ def _handle_schema_browse(args: argparse.Namespace, context: CliContext) -> dict:
155
+ return context.record.record_browse_schema_get_public(
156
+ profile=args.profile,
157
+ app_key=args.app_key,
158
+ view_id=args.view_id,
159
+ )
160
+
161
+
162
+ def _handle_schema_insert(args: argparse.Namespace, context: CliContext) -> dict:
163
+ return context.record.record_insert_schema_get_public(profile=args.profile, app_key=args.app_key)
164
+
165
+
166
+ def _handle_schema_update(args: argparse.Namespace, context: CliContext) -> dict:
167
+ return context.record.record_update_schema_get_public(
168
+ profile=args.profile,
169
+ app_key=args.app_key,
170
+ record_id=args.record_id,
171
+ )
172
+
173
+
174
+ def _handle_schema_import(args: argparse.Namespace, context: CliContext) -> dict:
175
+ return context.imports.record_import_schema_get(profile=args.profile, app_key=args.app_key)
176
+
177
+
178
+ def _handle_schema_code_block(args: argparse.Namespace, context: CliContext) -> dict:
179
+ return context.code_block.record_code_block_schema_get_public(profile=args.profile, app_key=args.app_key)
180
+
181
+
182
+ def _validate_public_view_selector(
183
+ *,
184
+ view_id: str | None,
185
+ legacy_list_type: int | None,
186
+ legacy_view_key: str | None,
187
+ legacy_view_name: str | None,
188
+ tool_name: str,
189
+ ) -> None:
190
+ if legacy_list_type is not None:
191
+ raise_config_error(
192
+ f"{tool_name} no longer accepts list_type.",
193
+ fix_hint="Call `app_get` first and use `accessible_views[].view_id`.",
194
+ )
195
+ if legacy_view_key or legacy_view_name:
196
+ raise_config_error(
197
+ f"{tool_name} no longer accepts view_key or view_name.",
198
+ fix_hint="Call `app_get` first and pass the exact `view_id` from `accessible_views`.",
199
+ )
200
+ if not (view_id or "").strip():
201
+ raise_config_error(
202
+ f"{tool_name} requires view_id.",
203
+ fix_hint="Call `app_get` first and choose one `accessible_views[].view_id`, then retry with `--view-id`.",
204
+ )
205
+
206
+
207
+ def _handle_list(args: argparse.Namespace, context: CliContext) -> dict:
208
+ _validate_public_view_selector(
209
+ view_id=args.view_id,
210
+ legacy_list_type=args.legacy_list_type,
211
+ legacy_view_key=args.legacy_view_key,
212
+ legacy_view_name=args.legacy_view_name,
213
+ tool_name="record_list",
214
+ )
215
+ return context.record.record_list(
216
+ profile=args.profile,
217
+ app_key=args.app_key,
218
+ columns=_columns(args),
219
+ where=load_list_arg(args.where_file, option_name="--where-file"),
220
+ order_by=load_list_arg(args.order_by_file, option_name="--order-by-file"),
221
+ limit=args.limit,
222
+ page=args.page,
223
+ view_id=args.view_id,
224
+ )
225
+
226
+
227
+ def _handle_get(args: argparse.Namespace, context: CliContext) -> dict:
228
+ return context.record.record_get_public(
229
+ profile=args.profile,
230
+ app_key=args.app_key,
231
+ record_id=args.record_id,
232
+ columns=_columns(args),
233
+ view_id=args.view_id,
234
+ )
235
+
236
+
237
+ def _handle_insert(args: argparse.Namespace, context: CliContext) -> dict:
238
+ return context.record.record_insert_public(
239
+ profile=args.profile,
240
+ app_key=args.app_key,
241
+ fields=require_object_arg(args.fields_file, option_name="--fields-file"),
242
+ verify_write=bool(args.verify_write),
243
+ )
244
+
245
+
246
+ def _handle_update(args: argparse.Namespace, context: CliContext) -> dict:
247
+ if args.items_file:
248
+ if args.record_id is not None or args.fields_file:
249
+ raise_config_error(
250
+ "record update batch mode does not accept --record-id or --fields-file.",
251
+ fix_hint="Use `record update --app-key APP_KEY --items-file ITEMS.json [--dry-run]` for batch updates.",
252
+ )
253
+ return context.record.record_update_public(
254
+ profile=args.profile,
255
+ app_key=args.app_key,
256
+ record_id=None,
257
+ fields=None,
258
+ items=require_list_arg(args.items_file, option_name="--items-file"),
259
+ dry_run=bool(args.dry_run),
260
+ verify_write=bool(args.verify_write),
261
+ )
262
+ if args.dry_run:
263
+ raise_config_error(
264
+ "record update --dry-run currently requires --items-file.",
265
+ fix_hint="Use `record update --app-key APP_KEY --items-file ITEMS.json --dry-run` for batch preflight.",
266
+ )
267
+ if args.record_id is None or not args.fields_file:
268
+ raise_config_error(
269
+ "record update single mode requires --record-id and --fields-file.",
270
+ fix_hint="Use `record update --app-key APP_KEY --record-id RECORD_ID --fields-file FIELDS.json`.",
271
+ )
272
+ return context.record.record_update_public(
273
+ profile=args.profile,
274
+ app_key=args.app_key,
275
+ record_id=args.record_id,
276
+ fields=require_object_arg(args.fields_file, option_name="--fields-file"),
277
+ verify_write=bool(args.verify_write),
278
+ )
279
+
280
+
281
+ def _handle_delete(args: argparse.Namespace, context: CliContext) -> dict:
282
+ record_ids = load_list_arg(args.record_ids_file, option_name="--record-ids-file")
283
+ return context.record.record_delete_public(
284
+ profile=args.profile,
285
+ app_key=args.app_key,
286
+ record_id=args.record_id,
287
+ record_ids=record_ids,
288
+ )
289
+
290
+
291
+ def _handle_analyze(args: argparse.Namespace, context: CliContext) -> dict:
292
+ _validate_public_view_selector(
293
+ view_id=args.view_id,
294
+ legacy_list_type=args.legacy_list_type,
295
+ legacy_view_key=args.legacy_view_key,
296
+ legacy_view_name=args.legacy_view_name,
297
+ tool_name="record_analyze",
298
+ )
299
+ return context.record.record_analyze(
300
+ profile=args.profile,
301
+ app_key=args.app_key,
302
+ dimensions=load_list_arg(args.dimensions_file, option_name="--dimensions-file"),
303
+ metrics=load_list_arg(args.metrics_file, option_name="--metrics-file"),
304
+ filters=load_list_arg(args.filters_file, option_name="--filters-file"),
305
+ sort=load_list_arg(args.sort_file, option_name="--sort-file"),
306
+ limit=args.limit,
307
+ strict_full=bool(args.strict_full),
308
+ view_id=args.view_id,
309
+ )
310
+
311
+
312
+ def _handle_code_block_run(args: argparse.Namespace, context: CliContext) -> dict:
313
+ if args.workflow_node_id is not None and args.role != 3:
314
+ raise_config_error(
315
+ "workflow_node_id is only accepted when role=3.",
316
+ fix_hint="Remove `--workflow-node-id`, or set `--role 3` when running in workflow context.",
317
+ )
318
+ return context.code_block.record_code_block_run(
319
+ profile=args.profile,
320
+ app_key=args.app_key,
321
+ record_id=args.record_id,
322
+ code_block_field=args.code_block_field,
323
+ role=args.role,
324
+ workflow_node_id=args.workflow_node_id,
325
+ answers=load_list_arg(args.answers_file, option_name="--answers-file"),
326
+ fields=load_object_arg(args.fields_file, option_name="--fields-file") or {},
327
+ manual=bool(args.manual),
328
+ apply_writeback=bool(args.apply_writeback),
329
+ verify_writeback=bool(args.verify_writeback),
330
+ force_refresh_form=bool(args.force_refresh_form),
331
+ )
@@ -0,0 +1,80 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+
5
+ from ..context import CliContext
6
+ from .common import raise_config_error, require_list_arg, require_object_arg
7
+
8
+
9
+ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
10
+ parser = subparsers.add_parser("repo", help="代码仓库开发工具")
11
+ repo_subparsers = parser.add_subparsers(dest="repo_command", required=True)
12
+
13
+ init_parser = repo_subparsers.add_parser("init", help="从模板初始化仓库")
14
+ init_parser.add_argument("--group-name", required=True)
15
+ init_parser.add_argument("--repo-template", required=True)
16
+ init_parser.set_defaults(handler=_handle_init, format_hint="generic")
17
+
18
+ generate_parser = repo_subparsers.add_parser("generate", help="沿用官方 generate 链路生成并提交页面代码")
19
+ generate_parser.add_argument("--repo-name", required=True)
20
+ generate_parser.add_argument("--query", required=True)
21
+ generate_parser.add_argument("--tag-id", type=int)
22
+ generate_parser.add_argument("--app-keys-file")
23
+ generate_parser.add_argument("--extra-info-file")
24
+ generate_parser.add_argument("--file-messages-file")
25
+ generate_parser.add_argument("--agent-id", type=int)
26
+ generate_parser.add_argument("--allow-create-table", action="store_true")
27
+ generate_parser.add_argument("--route-prefix")
28
+ generate_parser.add_argument("--token-name")
29
+ generate_parser.add_argument("--session-id")
30
+ generate_parser.add_argument("--round-version", type=int)
31
+ generate_parser.add_argument("--disable-trace-log", action="store_true")
32
+ generate_parser.set_defaults(handler=_handle_generate, format_hint="generic")
33
+
34
+ publish_parser = repo_subparsers.add_parser("publish-prod", help="发布 develop 到生产分支")
35
+ publish_parser.add_argument("--repo-name", required=True)
36
+ publish_parser.add_argument("--confirm", action="store_true")
37
+ publish_parser.set_defaults(handler=_handle_publish_prod, format_hint="generic")
38
+
39
+
40
+ def _handle_init(args: argparse.Namespace, context: CliContext) -> dict:
41
+ return context.repo.repository_init(
42
+ profile=args.profile,
43
+ group_name=args.group_name,
44
+ repo_template=args.repo_template,
45
+ )
46
+
47
+
48
+ def _handle_generate(args: argparse.Namespace, context: CliContext) -> dict:
49
+ app_keys = require_list_arg(args.app_keys_file, option_name="--app-keys-file") if args.app_keys_file else []
50
+ extra_info = require_object_arg(args.extra_info_file, option_name="--extra-info-file") if args.extra_info_file else None
51
+ file_messages = require_list_arg(args.file_messages_file, option_name="--file-messages-file") if args.file_messages_file else []
52
+ return context.repo.repository_generate(
53
+ profile=args.profile,
54
+ repo_name=args.repo_name,
55
+ query=args.query,
56
+ tag_id=args.tag_id,
57
+ app_keys=app_keys,
58
+ extra_info=extra_info,
59
+ file_messages=file_messages,
60
+ being_trace_log_enabled=not args.disable_trace_log,
61
+ agent_id=args.agent_id,
62
+ allow_create_table=args.allow_create_table,
63
+ route_prefix=args.route_prefix,
64
+ token_name=args.token_name,
65
+ session_id=args.session_id,
66
+ round_version=args.round_version,
67
+ )
68
+
69
+
70
+ def _handle_publish_prod(args: argparse.Namespace, context: CliContext) -> dict:
71
+ if args.confirm is not True:
72
+ raise_config_error(
73
+ "repository_publish_prod requires explicit confirmation.",
74
+ fix_hint="Re-run with `--confirm` after verifying the target repo and release intent.",
75
+ )
76
+ return context.repo.repository_publish_prod(
77
+ profile=args.profile,
78
+ repo_name=args.repo_name,
79
+ confirm=args.confirm,
80
+ )
@@ -0,0 +1,141 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+
5
+ from ..context import CliContext
6
+ from .common import load_object_arg
7
+
8
+
9
+ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
10
+ parser = subparsers.add_parser("task", help="待办与流程上下文")
11
+ task_subparsers = parser.add_subparsers(dest="task_command", required=True)
12
+
13
+ list_parser = task_subparsers.add_parser("list", help="列出待办")
14
+ list_parser.add_argument("--task-box", default="todo")
15
+ list_parser.add_argument("--flow-status", default="all")
16
+ list_parser.add_argument("--app-key")
17
+ list_parser.add_argument("--workflow-node-id", type=int)
18
+ list_parser.add_argument(
19
+ "--query",
20
+ help="先走后端待办检索;当后端返回零结果时,公开 task_list 会回退到本地匹配 app_name / workflow_node_name / app_key / record_id。",
21
+ )
22
+ list_parser.add_argument("--page", type=int, default=1)
23
+ list_parser.add_argument("--page-size", type=int, default=20)
24
+ list_parser.set_defaults(handler=_handle_list, format_hint="task_list")
25
+
26
+ get = task_subparsers.add_parser("get", help="读取待办详情;推荐直接传 --task-id")
27
+ get.add_argument("--task-id")
28
+ get.add_argument("--app-key")
29
+ get.add_argument("--record-id")
30
+ get.add_argument("--workflow-node-id", type=int)
31
+ get.add_argument("--include-candidates", action=argparse.BooleanOptionalAction, default=True)
32
+ get.add_argument("--include-associated-reports", action=argparse.BooleanOptionalAction, default=True)
33
+ get.set_defaults(handler=_handle_get, format_hint="task_get")
34
+
35
+ action = task_subparsers.add_parser("action", help="执行待办动作")
36
+ action.add_argument("--task-id")
37
+ action.add_argument("--app-key")
38
+ action.add_argument("--record-id")
39
+ action.add_argument("--workflow-node-id", type=int)
40
+ action.add_argument("--action", required=True)
41
+ action.add_argument("--payload-file")
42
+ action.add_argument("--fields-file")
43
+ action.set_defaults(
44
+ handler=_handle_action,
45
+ format_hint="task_action_execute",
46
+ hide_effective_context_line=True,
47
+ )
48
+
49
+ report = task_subparsers.add_parser("report", help="读取待办关联报表详情;推荐直接传 --task-id")
50
+ report.add_argument("--task-id")
51
+ report.add_argument("--app-key")
52
+ report.add_argument("--record-id")
53
+ report.add_argument("--workflow-node-id", type=int)
54
+ report.add_argument("--report-id", required=True, type=int)
55
+ report.add_argument("--page", type=int, default=1)
56
+ report.add_argument("--page-size", type=int, default=20)
57
+ report.set_defaults(handler=_handle_report, format_hint="task_associated_report_detail_get")
58
+
59
+ log = task_subparsers.add_parser("log", help="读取流程日志;推荐直接传 --task-id")
60
+ log.add_argument("--task-id")
61
+ log.add_argument("--app-key")
62
+ log.add_argument("--record-id")
63
+ log.add_argument("--workflow-node-id", type=int)
64
+ log.set_defaults(handler=_handle_log, format_hint="")
65
+
66
+
67
+ def _handle_list(args: argparse.Namespace, context: CliContext) -> dict:
68
+ return context.task.task_list(
69
+ profile=args.profile,
70
+ task_box=args.task_box,
71
+ flow_status=args.flow_status,
72
+ app_key=args.app_key,
73
+ workflow_node_id=args.workflow_node_id,
74
+ query=args.query,
75
+ page=args.page,
76
+ page_size=args.page_size,
77
+ )
78
+
79
+
80
+ def _handle_get(args: argparse.Namespace, context: CliContext) -> dict:
81
+ if not args.task_id and not (args.app_key and args.record_id and args.workflow_node_id):
82
+ raise RuntimeError(
83
+ '{"category":"config","message":"task get requires --task-id, or --app-key together with --record-id and --workflow-node-id"}'
84
+ )
85
+ return context.task.task_get(
86
+ profile=args.profile,
87
+ task_id=args.task_id,
88
+ app_key=args.app_key or "",
89
+ record_id=args.record_id or "",
90
+ workflow_node_id=int(args.workflow_node_id or 0),
91
+ include_candidates=bool(args.include_candidates),
92
+ include_associated_reports=bool(args.include_associated_reports),
93
+ )
94
+
95
+
96
+ def _handle_action(args: argparse.Namespace, context: CliContext) -> dict:
97
+ if not args.task_id and not (args.app_key and args.record_id and args.workflow_node_id):
98
+ raise RuntimeError(
99
+ '{"category":"config","message":"task action requires --task-id, or --app-key together with --record-id and --workflow-node-id"}'
100
+ )
101
+ return context.task.task_action_execute(
102
+ profile=args.profile,
103
+ task_id=args.task_id,
104
+ app_key=args.app_key or "",
105
+ record_id=args.record_id or "",
106
+ workflow_node_id=int(args.workflow_node_id or 0),
107
+ action=args.action,
108
+ payload=load_object_arg(args.payload_file, option_name="--payload-file") or {},
109
+ fields=load_object_arg(args.fields_file, option_name="--fields-file") or {},
110
+ )
111
+
112
+
113
+ def _handle_log(args: argparse.Namespace, context: CliContext) -> dict:
114
+ if not args.task_id and not (args.app_key and args.record_id and args.workflow_node_id):
115
+ raise RuntimeError(
116
+ '{"category":"config","message":"task log requires --task-id, or --app-key together with --record-id and --workflow-node-id"}'
117
+ )
118
+ return context.task.task_workflow_log_get(
119
+ profile=args.profile,
120
+ task_id=args.task_id,
121
+ app_key=args.app_key or "",
122
+ record_id=args.record_id or "",
123
+ workflow_node_id=int(args.workflow_node_id or 0),
124
+ )
125
+
126
+
127
+ def _handle_report(args: argparse.Namespace, context: CliContext) -> dict:
128
+ if not args.task_id and not (args.app_key and args.record_id and args.workflow_node_id):
129
+ raise RuntimeError(
130
+ '{"category":"config","message":"task report requires --task-id, or --app-key together with --record-id and --workflow-node-id"}'
131
+ )
132
+ return context.task.task_associated_report_detail_get(
133
+ profile=args.profile,
134
+ task_id=args.task_id,
135
+ app_key=args.app_key or "",
136
+ record_id=args.record_id or "",
137
+ workflow_node_id=int(args.workflow_node_id or 0),
138
+ report_id=int(args.report_id),
139
+ page=int(args.page),
140
+ page_size=int(args.page_size),
141
+ )