@josephyan/qingflow-cli 0.2.0-beta.73 → 0.2.0-beta.75
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 +2 -2
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/qingflow_mcp/backend_client.py +102 -0
- package/src/qingflow_mcp/builder_facade/models.py +121 -0
- package/src/qingflow_mcp/builder_facade/service.py +906 -71
- package/src/qingflow_mcp/cli/commands/builder.py +33 -1
- package/src/qingflow_mcp/cli/commands/repo.py +80 -0
- package/src/qingflow_mcp/cli/commands/task.py +17 -0
- package/src/qingflow_mcp/cli/context.py +3 -0
- package/src/qingflow_mcp/config.py +147 -0
- package/src/qingflow_mcp/public_surface.py +231 -0
- package/src/qingflow_mcp/repository_store.py +71 -0
- package/src/qingflow_mcp/response_trim.py +17 -246
- package/src/qingflow_mcp/server_app_builder.py +26 -1
- package/src/qingflow_mcp/server_app_user.py +5 -1
- package/src/qingflow_mcp/tools/ai_builder_tools.py +145 -5
- package/src/qingflow_mcp/tools/record_tools.py +298 -25
- package/src/qingflow_mcp/tools/repository_dev_tools.py +533 -0
- package/src/qingflow_mcp/tools/task_context_tools.py +146 -7
|
@@ -77,6 +77,14 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
77
77
|
package_resolve.add_argument("--package-name", required=True)
|
|
78
78
|
package_resolve.set_defaults(handler=_handle_package_resolve, format_hint="builder_summary")
|
|
79
79
|
|
|
80
|
+
solution = builder_subparsers.add_parser("solution", help="解决方案")
|
|
81
|
+
solution_subparsers = solution.add_subparsers(dest="builder_solution_command", required=True)
|
|
82
|
+
solution_install = solution_subparsers.add_parser("install", help="安装解决方案")
|
|
83
|
+
solution_install.add_argument("--solution-key", required=True)
|
|
84
|
+
solution_install.add_argument("--being-copy-data", action=argparse.BooleanOptionalAction, default=True)
|
|
85
|
+
solution_install.add_argument("--solution-source", default="solutionDetail")
|
|
86
|
+
solution_install.set_defaults(handler=_handle_solution_install, format_hint="builder_summary")
|
|
87
|
+
|
|
80
88
|
package_create = package_subparsers.add_parser("create", help="创建应用包")
|
|
81
89
|
package_create.add_argument("--package-name", required=True)
|
|
82
90
|
package_create.add_argument("--icon")
|
|
@@ -114,6 +122,12 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
114
122
|
app_get.add_argument("--app-key", required=True)
|
|
115
123
|
app_get.set_defaults(handler=_handle_app_get, format_hint="builder_summary")
|
|
116
124
|
|
|
125
|
+
app_repair_code_blocks = app_subparsers.add_parser("repair-code-blocks", help="扫描或修复现有代码块配置")
|
|
126
|
+
app_repair_code_blocks.add_argument("--app-key", required=True)
|
|
127
|
+
app_repair_code_blocks.add_argument("--field")
|
|
128
|
+
app_repair_code_blocks.add_argument("--apply", action="store_true")
|
|
129
|
+
app_repair_code_blocks.set_defaults(handler=_handle_app_repair_code_blocks, format_hint="builder_summary")
|
|
130
|
+
|
|
117
131
|
button = builder_subparsers.add_parser("button", help="自定义按钮")
|
|
118
132
|
button_subparsers = button.add_subparsers(dest="builder_button_command", required=True)
|
|
119
133
|
|
|
@@ -144,7 +158,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
144
158
|
|
|
145
159
|
portal = builder_subparsers.add_parser("portal", help="门户")
|
|
146
160
|
portal_subparsers = portal.add_subparsers(dest="builder_portal_command", required=True)
|
|
147
|
-
portal_list = portal_subparsers.add_parser("list", help="
|
|
161
|
+
portal_list = portal_subparsers.add_parser("list", help="列出可配置门户")
|
|
148
162
|
portal_list.set_defaults(handler=_handle_portal_list, format_hint="builder_summary")
|
|
149
163
|
|
|
150
164
|
portal_get = portal_subparsers.add_parser("get", help="读取门户详情")
|
|
@@ -310,6 +324,15 @@ def _handle_package_resolve(args: argparse.Namespace, context: CliContext) -> di
|
|
|
310
324
|
return context.builder.package_resolve(profile=args.profile, package_name=args.package_name)
|
|
311
325
|
|
|
312
326
|
|
|
327
|
+
def _handle_solution_install(args: argparse.Namespace, context: CliContext) -> dict:
|
|
328
|
+
return context.builder.solution_install(
|
|
329
|
+
profile=args.profile,
|
|
330
|
+
solution_key=args.solution_key,
|
|
331
|
+
being_copy_data=bool(args.being_copy_data),
|
|
332
|
+
solution_source=args.solution_source,
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
|
|
313
336
|
def _handle_package_create(args: argparse.Namespace, context: CliContext) -> dict:
|
|
314
337
|
return context.builder.package_create(
|
|
315
338
|
profile=args.profile,
|
|
@@ -404,6 +427,15 @@ def _handle_app_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
404
427
|
return handlers[args.builder_app_get_section](profile=args.profile, app_key=args.app_key)
|
|
405
428
|
|
|
406
429
|
|
|
430
|
+
def _handle_app_repair_code_blocks(args: argparse.Namespace, context: CliContext) -> dict:
|
|
431
|
+
return context.builder.app_repair_code_blocks(
|
|
432
|
+
profile=args.profile,
|
|
433
|
+
app_key=args.app_key,
|
|
434
|
+
field=args.field,
|
|
435
|
+
apply=bool(args.apply),
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
|
|
407
439
|
def _handle_portal_list(args: argparse.Namespace, context: CliContext) -> dict:
|
|
408
440
|
return context.builder.portal_list(profile=args.profile)
|
|
409
441
|
|
|
@@ -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
|
+
)
|
|
@@ -37,6 +37,13 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
37
37
|
action.add_argument("--fields-file")
|
|
38
38
|
action.set_defaults(handler=_handle_action, format_hint="")
|
|
39
39
|
|
|
40
|
+
save_only = task_subparsers.add_parser("save-only", help="仅保存当前待办字段,不提交流程")
|
|
41
|
+
save_only.add_argument("--app-key", required=True)
|
|
42
|
+
save_only.add_argument("--record-id", required=True, type=int)
|
|
43
|
+
save_only.add_argument("--workflow-node-id", required=True, type=int)
|
|
44
|
+
save_only.add_argument("--fields-file", required=True)
|
|
45
|
+
save_only.set_defaults(handler=_handle_save_only, format_hint="")
|
|
46
|
+
|
|
40
47
|
log = task_subparsers.add_parser("log", help="读取流程日志")
|
|
41
48
|
log.add_argument("--app-key", required=True)
|
|
42
49
|
log.add_argument("--record-id", required=True, type=int)
|
|
@@ -80,6 +87,16 @@ def _handle_action(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
80
87
|
)
|
|
81
88
|
|
|
82
89
|
|
|
90
|
+
def _handle_save_only(args: argparse.Namespace, context: CliContext) -> dict:
|
|
91
|
+
return context.task.task_save_only(
|
|
92
|
+
profile=args.profile,
|
|
93
|
+
app_key=args.app_key,
|
|
94
|
+
record_id=args.record_id,
|
|
95
|
+
workflow_node_id=args.workflow_node_id,
|
|
96
|
+
fields=load_object_arg(args.fields_file, option_name="--fields-file") or {},
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
|
|
83
100
|
def _handle_log(args: argparse.Namespace, context: CliContext) -> dict:
|
|
84
101
|
return context.task.task_workflow_log_get(
|
|
85
102
|
profile=args.profile,
|
|
@@ -12,6 +12,7 @@ from ..tools.feedback_tools import FeedbackTools
|
|
|
12
12
|
from ..tools.file_tools import FileTools
|
|
13
13
|
from ..tools.import_tools import ImportTools
|
|
14
14
|
from ..tools.record_tools import RecordTools
|
|
15
|
+
from ..tools.repository_dev_tools import RepositoryDevTools
|
|
15
16
|
from ..tools.resource_read_tools import ResourceReadTools
|
|
16
17
|
from ..tools.task_context_tools import TaskContextTools
|
|
17
18
|
from ..tools.workspace_tools import WorkspaceTools
|
|
@@ -32,6 +33,7 @@ class CliContext:
|
|
|
32
33
|
files: FileTools
|
|
33
34
|
builder_feedback: FeedbackTools
|
|
34
35
|
builder: AiBuilderTools
|
|
36
|
+
repo: RepositoryDevTools
|
|
35
37
|
|
|
36
38
|
def close(self) -> None:
|
|
37
39
|
self.backend.close()
|
|
@@ -54,4 +56,5 @@ def build_cli_context() -> CliContext:
|
|
|
54
56
|
files=FileTools(sessions, backend),
|
|
55
57
|
builder_feedback=FeedbackTools(backend, mcp_side="App Builder MCP"),
|
|
56
58
|
builder=AiBuilderTools(sessions, backend),
|
|
59
|
+
repo=RepositoryDevTools(sessions, backend),
|
|
57
60
|
)
|
|
@@ -14,6 +14,13 @@ ATTACHMENT_QUESTION_TYPE = 13
|
|
|
14
14
|
DEFAULT_BASE_URL = "https://qingflow.com/api"
|
|
15
15
|
DEFAULT_FEEDBACK_APP_KEY = "e0d017kju002"
|
|
16
16
|
DEFAULT_FEEDBACK_QSOURCE_TOKEN = "mcp-feedback-7755d14748fc"
|
|
17
|
+
DEFAULT_REPOSITORY_GIT_REMOTE_TEMPLATE = "git@hackers.oalite.com:{group}/{repo}.git"
|
|
18
|
+
DEFAULT_REPOSITORY_PREVIEW_ADDRESS_TEMPLATE = "https://%s.preview.oalite.com"
|
|
19
|
+
DEFAULT_REPOSITORY_DEVELOP_BRANCH = "develop"
|
|
20
|
+
DEFAULT_REPOSITORY_PROD_BRANCH = "prod"
|
|
21
|
+
DEFAULT_REPOSITORY_AUTHOR_NAME = "qingflow-mcp"
|
|
22
|
+
DEFAULT_REPOSITORY_AUTHOR_EMAIL = "qingflow-mcp@local.invalid"
|
|
23
|
+
DEFAULT_REPOSITORY_INTERNAL_SHARE_TOKEN_KEY = "tokenKey"
|
|
17
24
|
|
|
18
25
|
|
|
19
26
|
def get_mcp_home() -> Path:
|
|
@@ -25,6 +32,10 @@ def get_profiles_path() -> Path:
|
|
|
25
32
|
return get_mcp_home() / "profiles.json"
|
|
26
33
|
|
|
27
34
|
|
|
35
|
+
def get_repository_metadata_dir() -> Path:
|
|
36
|
+
return get_mcp_home() / "repository-metadata"
|
|
37
|
+
|
|
38
|
+
|
|
28
39
|
def get_config_file_paths() -> list[Path]:
|
|
29
40
|
"""
|
|
30
41
|
获取可能的配置文件路径列表,按优先级排序:
|
|
@@ -197,6 +208,142 @@ def get_log_level() -> str:
|
|
|
197
208
|
)
|
|
198
209
|
|
|
199
210
|
|
|
211
|
+
def get_repository_default_group() -> str | None:
|
|
212
|
+
value = get_config_value(
|
|
213
|
+
"repository.default_group",
|
|
214
|
+
env_var="QINGFLOW_MCP_REPOSITORY_DEFAULT_GROUP",
|
|
215
|
+
default=None,
|
|
216
|
+
)
|
|
217
|
+
normalized = str(value or "").strip()
|
|
218
|
+
return normalized or None
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def get_repository_git_remote_template() -> str:
|
|
222
|
+
value = get_config_value(
|
|
223
|
+
"repository.git_remote_template",
|
|
224
|
+
env_var="QINGFLOW_MCP_REPOSITORY_GIT_REMOTE_TEMPLATE",
|
|
225
|
+
default=DEFAULT_REPOSITORY_GIT_REMOTE_TEMPLATE,
|
|
226
|
+
)
|
|
227
|
+
normalized = str(value or "").strip()
|
|
228
|
+
return normalized or DEFAULT_REPOSITORY_GIT_REMOTE_TEMPLATE
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def get_repository_preview_address_template() -> str:
|
|
232
|
+
value = get_config_value(
|
|
233
|
+
"repository.preview_address_template",
|
|
234
|
+
env_var="QINGFLOW_MCP_REPOSITORY_PREVIEW_ADDRESS_TEMPLATE",
|
|
235
|
+
default=DEFAULT_REPOSITORY_PREVIEW_ADDRESS_TEMPLATE,
|
|
236
|
+
)
|
|
237
|
+
normalized = str(value or "").strip()
|
|
238
|
+
return normalized or DEFAULT_REPOSITORY_PREVIEW_ADDRESS_TEMPLATE
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def get_repository_develop_branch() -> str:
|
|
242
|
+
value = get_config_value(
|
|
243
|
+
"repository.develop_branch",
|
|
244
|
+
env_var="QINGFLOW_MCP_REPOSITORY_DEVELOP_BRANCH",
|
|
245
|
+
default=DEFAULT_REPOSITORY_DEVELOP_BRANCH,
|
|
246
|
+
)
|
|
247
|
+
normalized = str(value or "").strip()
|
|
248
|
+
return normalized or DEFAULT_REPOSITORY_DEVELOP_BRANCH
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def get_repository_prod_branch() -> str:
|
|
252
|
+
value = get_config_value(
|
|
253
|
+
"repository.prod_branch",
|
|
254
|
+
env_var="QINGFLOW_MCP_REPOSITORY_PROD_BRANCH",
|
|
255
|
+
default=DEFAULT_REPOSITORY_PROD_BRANCH,
|
|
256
|
+
)
|
|
257
|
+
normalized = str(value or "").strip()
|
|
258
|
+
return normalized or DEFAULT_REPOSITORY_PROD_BRANCH
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def get_repository_author_name() -> str:
|
|
262
|
+
value = get_config_value(
|
|
263
|
+
"repository.author_name",
|
|
264
|
+
env_var="QINGFLOW_MCP_REPOSITORY_AUTHOR_NAME",
|
|
265
|
+
default=DEFAULT_REPOSITORY_AUTHOR_NAME,
|
|
266
|
+
)
|
|
267
|
+
normalized = str(value or "").strip()
|
|
268
|
+
return normalized or DEFAULT_REPOSITORY_AUTHOR_NAME
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def get_repository_author_email() -> str:
|
|
272
|
+
value = get_config_value(
|
|
273
|
+
"repository.author_email",
|
|
274
|
+
env_var="QINGFLOW_MCP_REPOSITORY_AUTHOR_EMAIL",
|
|
275
|
+
default=DEFAULT_REPOSITORY_AUTHOR_EMAIL,
|
|
276
|
+
)
|
|
277
|
+
normalized = str(value or "").strip()
|
|
278
|
+
return normalized or DEFAULT_REPOSITORY_AUTHOR_EMAIL
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def get_repository_internal_base_url() -> str | None:
|
|
282
|
+
value = get_config_value(
|
|
283
|
+
"repository.internal_base_url",
|
|
284
|
+
env_var="QINGFLOW_MCP_REPOSITORY_INTERNAL_BASE_URL",
|
|
285
|
+
default=None,
|
|
286
|
+
)
|
|
287
|
+
if value is None:
|
|
288
|
+
return None
|
|
289
|
+
normalized = normalize_base_url(str(value).strip())
|
|
290
|
+
return normalized or None
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def get_repository_internal_share_token() -> str | None:
|
|
294
|
+
value = get_config_value(
|
|
295
|
+
"repository.internal_share_token",
|
|
296
|
+
env_var="QINGFLOW_MCP_REPOSITORY_INTERNAL_SHARE_TOKEN",
|
|
297
|
+
default=None,
|
|
298
|
+
)
|
|
299
|
+
normalized = str(value or "").strip()
|
|
300
|
+
return normalized or None
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def get_repository_internal_share_token_key() -> str:
|
|
304
|
+
value = get_config_value(
|
|
305
|
+
"repository.internal_share_token_key",
|
|
306
|
+
env_var="QINGFLOW_MCP_REPOSITORY_INTERNAL_SHARE_TOKEN_KEY",
|
|
307
|
+
default=DEFAULT_REPOSITORY_INTERNAL_SHARE_TOKEN_KEY,
|
|
308
|
+
)
|
|
309
|
+
normalized = str(value or "").strip()
|
|
310
|
+
return normalized or DEFAULT_REPOSITORY_INTERNAL_SHARE_TOKEN_KEY
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def get_repository_generate_default_agent_id() -> int | None:
|
|
314
|
+
value = get_config_value(
|
|
315
|
+
"repository.generate.default_agent_id",
|
|
316
|
+
env_var="QINGFLOW_MCP_REPOSITORY_GENERATE_DEFAULT_AGENT_ID",
|
|
317
|
+
default=None,
|
|
318
|
+
)
|
|
319
|
+
if value is None:
|
|
320
|
+
return None
|
|
321
|
+
try:
|
|
322
|
+
return int(str(value).strip())
|
|
323
|
+
except (TypeError, ValueError):
|
|
324
|
+
return None
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def get_repository_generate_default_route_prefix() -> str | None:
|
|
328
|
+
value = get_config_value(
|
|
329
|
+
"repository.generate.default_route_prefix",
|
|
330
|
+
env_var="QINGFLOW_MCP_REPOSITORY_GENERATE_DEFAULT_ROUTE_PREFIX",
|
|
331
|
+
default=None,
|
|
332
|
+
)
|
|
333
|
+
normalized = str(value or "").strip()
|
|
334
|
+
return normalized or None
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def get_repository_generate_default_token_name() -> str | None:
|
|
338
|
+
value = get_config_value(
|
|
339
|
+
"repository.generate.default_token_name",
|
|
340
|
+
env_var="QINGFLOW_MCP_REPOSITORY_GENERATE_DEFAULT_TOKEN_NAME",
|
|
341
|
+
default=None,
|
|
342
|
+
)
|
|
343
|
+
normalized = str(value or "").strip()
|
|
344
|
+
return normalized or None
|
|
345
|
+
|
|
346
|
+
|
|
200
347
|
def normalize_base_url(base_url: str | None) -> str | None:
|
|
201
348
|
"""规范化 base URL"""
|
|
202
349
|
if base_url is None:
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from argparse import Namespace
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
USER_DOMAIN = "user"
|
|
8
|
+
BUILDER_DOMAIN = "builder"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True, slots=True)
|
|
12
|
+
class PublicToolSpec:
|
|
13
|
+
domain: str
|
|
14
|
+
tool_name: str
|
|
15
|
+
method_names: tuple[str, ...] = ()
|
|
16
|
+
cli_route: tuple[str, ...] | None = None
|
|
17
|
+
mcp_public: bool = True
|
|
18
|
+
cli_public: bool = True
|
|
19
|
+
has_contract: bool = False
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def trim_key(self) -> str:
|
|
23
|
+
return tool_key(self.domain, self.tool_name)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def tool_key(domain: str, tool_name: str) -> str:
|
|
27
|
+
return f"{domain}:{tool_name}"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
USER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
|
|
31
|
+
PublicToolSpec(USER_DOMAIN, "auth_login", ("auth_login",), ("auth", "login")),
|
|
32
|
+
PublicToolSpec(USER_DOMAIN, "auth_use_token", ("auth_use_token",), ("auth", "use-token")),
|
|
33
|
+
PublicToolSpec(USER_DOMAIN, "auth_whoami", ("auth_whoami",), ("auth", "whoami")),
|
|
34
|
+
PublicToolSpec(USER_DOMAIN, "auth_logout", ("auth_logout",), ("auth", "logout")),
|
|
35
|
+
PublicToolSpec(USER_DOMAIN, "workspace_list", ("workspace_list",), ("workspace", "list")),
|
|
36
|
+
PublicToolSpec(USER_DOMAIN, "workspace_select", ("workspace_select",), ("workspace", "select")),
|
|
37
|
+
PublicToolSpec(USER_DOMAIN, "app_list", ("app_list",), ("app", "list")),
|
|
38
|
+
PublicToolSpec(USER_DOMAIN, "app_search", ("app_search",), ("app", "search")),
|
|
39
|
+
PublicToolSpec(USER_DOMAIN, "app_get", ("app_get",), ("app", "get")),
|
|
40
|
+
PublicToolSpec(USER_DOMAIN, "portal_list", ("portal_list",), ("portal", "list")),
|
|
41
|
+
PublicToolSpec(USER_DOMAIN, "portal_get", ("portal_get",), ("portal", "get")),
|
|
42
|
+
PublicToolSpec(USER_DOMAIN, "view_get", ("view_get",), ("view", "get")),
|
|
43
|
+
PublicToolSpec(USER_DOMAIN, "chart_get", ("chart_get",), ("chart", "get")),
|
|
44
|
+
PublicToolSpec(USER_DOMAIN, "file_get_upload_info", ("file_get_upload_info",), cli_public=False),
|
|
45
|
+
PublicToolSpec(USER_DOMAIN, "file_upload_local", ("file_upload_local",), cli_public=False),
|
|
46
|
+
PublicToolSpec(USER_DOMAIN, "feedback_submit", ("feedback_submit",), cli_public=False),
|
|
47
|
+
PublicToolSpec(
|
|
48
|
+
USER_DOMAIN,
|
|
49
|
+
"record_schema_get",
|
|
50
|
+
cli_route=("record", "schema", "applicant"),
|
|
51
|
+
mcp_public=False,
|
|
52
|
+
),
|
|
53
|
+
PublicToolSpec(
|
|
54
|
+
USER_DOMAIN,
|
|
55
|
+
"record_browse_schema_get",
|
|
56
|
+
("record_browse_schema_get_public",),
|
|
57
|
+
("record", "schema", "browse"),
|
|
58
|
+
),
|
|
59
|
+
PublicToolSpec(
|
|
60
|
+
USER_DOMAIN,
|
|
61
|
+
"record_insert_schema_get",
|
|
62
|
+
("record_insert_schema_get_public",),
|
|
63
|
+
("record", "schema", "insert"),
|
|
64
|
+
),
|
|
65
|
+
PublicToolSpec(
|
|
66
|
+
USER_DOMAIN,
|
|
67
|
+
"record_update_schema_get",
|
|
68
|
+
("record_update_schema_get_public",),
|
|
69
|
+
("record", "schema", "update"),
|
|
70
|
+
),
|
|
71
|
+
PublicToolSpec(USER_DOMAIN, "record_import_schema_get", ("record_import_schema_get",), ("record", "schema", "import")),
|
|
72
|
+
PublicToolSpec(
|
|
73
|
+
USER_DOMAIN,
|
|
74
|
+
"record_code_block_schema_get",
|
|
75
|
+
("record_code_block_schema_get_public",),
|
|
76
|
+
("record", "schema", "code-block"),
|
|
77
|
+
),
|
|
78
|
+
PublicToolSpec(USER_DOMAIN, "record_member_candidates", ("record_member_candidates",), cli_public=False),
|
|
79
|
+
PublicToolSpec(USER_DOMAIN, "record_department_candidates", ("record_department_candidates",), cli_public=False),
|
|
80
|
+
PublicToolSpec(USER_DOMAIN, "record_analyze", ("record_analyze",), ("record", "analyze")),
|
|
81
|
+
PublicToolSpec(USER_DOMAIN, "record_list", ("record_list",), ("record", "list")),
|
|
82
|
+
PublicToolSpec(USER_DOMAIN, "record_get", ("record_get_public",), ("record", "get")),
|
|
83
|
+
PublicToolSpec(USER_DOMAIN, "record_insert", ("record_insert_public",), ("record", "insert")),
|
|
84
|
+
PublicToolSpec(USER_DOMAIN, "record_update", ("record_update_public",), ("record", "update")),
|
|
85
|
+
PublicToolSpec(USER_DOMAIN, "record_delete", ("record_delete_public",), ("record", "delete")),
|
|
86
|
+
PublicToolSpec(USER_DOMAIN, "record_import_template_get", ("record_import_template_get",), ("import", "template")),
|
|
87
|
+
PublicToolSpec(USER_DOMAIN, "record_import_verify", ("record_import_verify",), ("import", "verify")),
|
|
88
|
+
PublicToolSpec(USER_DOMAIN, "record_import_repair_local", ("record_import_repair_local",), ("import", "repair")),
|
|
89
|
+
PublicToolSpec(USER_DOMAIN, "record_import_start", ("record_import_start",), ("import", "start")),
|
|
90
|
+
PublicToolSpec(USER_DOMAIN, "record_import_status_get", ("record_import_status_get",), ("import", "status")),
|
|
91
|
+
PublicToolSpec(USER_DOMAIN, "record_code_block_run", ("record_code_block_run",), ("record", "code-block-run")),
|
|
92
|
+
PublicToolSpec(USER_DOMAIN, "task_list", ("task_list",), ("task", "list")),
|
|
93
|
+
PublicToolSpec(USER_DOMAIN, "task_get", ("task_get",), ("task", "get")),
|
|
94
|
+
PublicToolSpec(USER_DOMAIN, "task_save_only", ("task_save_only",), ("task", "save-only")),
|
|
95
|
+
PublicToolSpec(USER_DOMAIN, "task_action_execute", ("task_action_execute",), ("task", "action")),
|
|
96
|
+
PublicToolSpec(USER_DOMAIN, "task_associated_report_detail_get", ("task_associated_report_detail_get",), cli_public=False),
|
|
97
|
+
PublicToolSpec(USER_DOMAIN, "task_workflow_log_get", ("task_workflow_log_get",), ("task", "log")),
|
|
98
|
+
PublicToolSpec(USER_DOMAIN, "directory_search", ("directory_search",), cli_public=False),
|
|
99
|
+
PublicToolSpec(USER_DOMAIN, "directory_list_internal_users", ("directory_list_internal_users",), cli_public=False),
|
|
100
|
+
PublicToolSpec(USER_DOMAIN, "directory_list_all_internal_users", ("directory_list_all_internal_users",), cli_public=False),
|
|
101
|
+
PublicToolSpec(USER_DOMAIN, "directory_list_internal_departments", ("directory_list_internal_departments",), cli_public=False),
|
|
102
|
+
PublicToolSpec(USER_DOMAIN, "directory_list_all_departments", ("directory_list_all_departments",), cli_public=False),
|
|
103
|
+
PublicToolSpec(USER_DOMAIN, "directory_list_sub_departments", ("directory_list_sub_departments",), cli_public=False),
|
|
104
|
+
PublicToolSpec(USER_DOMAIN, "directory_list_external_members", ("directory_list_external_members",), cli_public=False),
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
BUILDER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
|
|
109
|
+
PublicToolSpec(BUILDER_DOMAIN, "auth_login", ("auth_login",), ("builder", "auth", "login"), cli_public=False),
|
|
110
|
+
PublicToolSpec(BUILDER_DOMAIN, "auth_use_token", ("auth_use_token",), ("builder", "auth", "use-token"), cli_public=False),
|
|
111
|
+
PublicToolSpec(BUILDER_DOMAIN, "auth_whoami", ("auth_whoami",), ("builder", "auth", "whoami"), cli_public=False),
|
|
112
|
+
PublicToolSpec(BUILDER_DOMAIN, "auth_logout", ("auth_logout",), ("builder", "auth", "logout"), cli_public=False),
|
|
113
|
+
PublicToolSpec(BUILDER_DOMAIN, "workspace_list", ("workspace_list",), ("builder", "workspace", "list"), cli_public=False),
|
|
114
|
+
PublicToolSpec(BUILDER_DOMAIN, "workspace_select", ("workspace_select",), ("builder", "workspace", "select"), cli_public=False),
|
|
115
|
+
PublicToolSpec(BUILDER_DOMAIN, "file_upload_local", ("file_upload_local",), ("builder", "file", "upload-local"), has_contract=True),
|
|
116
|
+
PublicToolSpec(BUILDER_DOMAIN, "feedback_submit", ("feedback_submit",), ("builder", "feedback", "submit"), has_contract=True),
|
|
117
|
+
PublicToolSpec(BUILDER_DOMAIN, "package_list", ("package_list",), ("builder", "package", "list"), has_contract=True),
|
|
118
|
+
PublicToolSpec(BUILDER_DOMAIN, "package_resolve", ("package_resolve",), ("builder", "package", "resolve"), has_contract=True),
|
|
119
|
+
PublicToolSpec(BUILDER_DOMAIN, "builder_tool_contract", ("builder_tool_contract",), ("builder", "contract"), has_contract=False),
|
|
120
|
+
PublicToolSpec(BUILDER_DOMAIN, "package_create", ("package_create",), ("builder", "package", "create"), has_contract=True),
|
|
121
|
+
PublicToolSpec(BUILDER_DOMAIN, "solution_install", ("solution_install",), ("builder", "solution", "install"), has_contract=True),
|
|
122
|
+
PublicToolSpec(BUILDER_DOMAIN, "member_search", ("member_search",), ("builder", "member", "search"), has_contract=True),
|
|
123
|
+
PublicToolSpec(BUILDER_DOMAIN, "role_search", ("role_search",), ("builder", "role", "search"), has_contract=True),
|
|
124
|
+
PublicToolSpec(BUILDER_DOMAIN, "role_create", ("role_create",), ("builder", "role", "create"), has_contract=True),
|
|
125
|
+
PublicToolSpec(BUILDER_DOMAIN, "package_attach_app", ("package_attach_app",), ("builder", "package", "attach-app"), has_contract=True),
|
|
126
|
+
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),
|
|
127
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_resolve", ("app_resolve",), ("builder", "app", "resolve"), has_contract=True),
|
|
128
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_custom_button_list", ("app_custom_button_list",), ("builder", "button", "list"), has_contract=True),
|
|
129
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_custom_button_get", ("app_custom_button_get",), ("builder", "button", "get"), has_contract=True),
|
|
130
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_custom_button_create", ("app_custom_button_create",), ("builder", "button", "create"), has_contract=True),
|
|
131
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_custom_button_update", ("app_custom_button_update",), ("builder", "button", "update"), has_contract=True),
|
|
132
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_custom_button_delete", ("app_custom_button_delete",), ("builder", "button", "delete"), has_contract=True),
|
|
133
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_get", ("app_get",), ("builder", "app", "get", "summary"), has_contract=True),
|
|
134
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_get_fields", ("app_get_fields",), ("builder", "app", "get", "fields"), has_contract=True),
|
|
135
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_repair_code_blocks", ("app_repair_code_blocks",), ("builder", "app", "repair-code-blocks"), has_contract=True),
|
|
136
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_get_layout", ("app_get_layout",), ("builder", "app", "get", "layout"), has_contract=True),
|
|
137
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_get_views", ("app_get_views",), ("builder", "app", "get", "views"), has_contract=True),
|
|
138
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_get_flow", ("app_get_flow",), ("builder", "app", "get", "flow"), has_contract=True),
|
|
139
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_get_charts", ("app_get_charts",), ("builder", "app", "get", "charts"), has_contract=True),
|
|
140
|
+
PublicToolSpec(BUILDER_DOMAIN, "portal_list", ("portal_list",), ("builder", "portal", "list"), has_contract=True),
|
|
141
|
+
PublicToolSpec(BUILDER_DOMAIN, "portal_get", ("portal_get",), ("builder", "portal", "get"), has_contract=True),
|
|
142
|
+
PublicToolSpec(BUILDER_DOMAIN, "view_get", ("view_get",), ("builder", "view", "get"), has_contract=True),
|
|
143
|
+
PublicToolSpec(BUILDER_DOMAIN, "chart_get", ("chart_get",), ("builder", "chart", "get"), has_contract=True),
|
|
144
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_schema_apply", ("app_schema_apply",), ("builder", "schema", "apply"), has_contract=True),
|
|
145
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_layout_apply", ("app_layout_apply",), ("builder", "layout", "apply"), has_contract=True),
|
|
146
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_flow_apply", ("app_flow_apply",), ("builder", "flow", "apply"), has_contract=True),
|
|
147
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_views_apply", ("app_views_apply",), ("builder", "views", "apply"), has_contract=True),
|
|
148
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_charts_apply", ("app_charts_apply",), ("builder", "charts", "apply"), has_contract=True),
|
|
149
|
+
PublicToolSpec(BUILDER_DOMAIN, "portal_apply", ("portal_apply",), ("builder", "portal", "apply"), has_contract=True),
|
|
150
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_publish_verify", ("app_publish_verify",), ("builder", "publish", "verify"), has_contract=True),
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
ALL_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = USER_PUBLIC_TOOL_SPECS + BUILDER_PUBLIC_TOOL_SPECS
|
|
155
|
+
PUBLIC_TOOL_BY_KEY: dict[str, PublicToolSpec] = {spec.trim_key: spec for spec in ALL_PUBLIC_TOOL_SPECS}
|
|
156
|
+
PUBLIC_TOOL_BY_CLI_ROUTE: dict[tuple[str, ...], PublicToolSpec] = {
|
|
157
|
+
spec.cli_route: spec
|
|
158
|
+
for spec in ALL_PUBLIC_TOOL_SPECS
|
|
159
|
+
if spec.cli_public and spec.cli_route is not None
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def public_tool_specs(domain: str | None = None) -> tuple[PublicToolSpec, ...]:
|
|
164
|
+
if domain is None:
|
|
165
|
+
return ALL_PUBLIC_TOOL_SPECS
|
|
166
|
+
return tuple(spec for spec in ALL_PUBLIC_TOOL_SPECS if spec.domain == domain)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def public_mcp_tool_names(domain: str) -> set[str]:
|
|
170
|
+
return {spec.tool_name for spec in public_tool_specs(domain) if spec.mcp_public}
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def public_builder_contract_tool_names() -> list[str]:
|
|
174
|
+
return sorted(
|
|
175
|
+
spec.tool_name
|
|
176
|
+
for spec in BUILDER_PUBLIC_TOOL_SPECS
|
|
177
|
+
if spec.has_contract and (spec.mcp_public or spec.cli_public)
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def server_method_map(domain: str) -> dict[str, str]:
|
|
182
|
+
mapping: dict[str, str] = {}
|
|
183
|
+
for spec in public_tool_specs(domain):
|
|
184
|
+
if not spec.mcp_public:
|
|
185
|
+
continue
|
|
186
|
+
for method_name in spec.method_names:
|
|
187
|
+
mapping[method_name] = spec.trim_key
|
|
188
|
+
return mapping
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def cli_trim_key_from_namespace(args: Namespace) -> str | None:
|
|
192
|
+
route = cli_route_from_namespace(args)
|
|
193
|
+
if route is None:
|
|
194
|
+
return None
|
|
195
|
+
spec = PUBLIC_TOOL_BY_CLI_ROUTE.get(route)
|
|
196
|
+
return spec.trim_key if spec is not None else None
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def cli_route_from_namespace(args: Namespace) -> tuple[str, ...] | None:
|
|
200
|
+
command = getattr(args, "command", None)
|
|
201
|
+
if not isinstance(command, str) or not command:
|
|
202
|
+
return None
|
|
203
|
+
command = "builder" if command == "build" else command
|
|
204
|
+
if command == "record":
|
|
205
|
+
record_command = getattr(args, "record_command", None)
|
|
206
|
+
if record_command == "schema":
|
|
207
|
+
schema_command = getattr(args, "record_schema_command", None)
|
|
208
|
+
return (command, "schema", schema_command) if isinstance(schema_command, str) and schema_command else None
|
|
209
|
+
return (command, record_command) if isinstance(record_command, str) and record_command else None
|
|
210
|
+
if command == "task":
|
|
211
|
+
task_command = getattr(args, "task_command", None)
|
|
212
|
+
return (command, task_command) if isinstance(task_command, str) and task_command else None
|
|
213
|
+
if command != "builder":
|
|
214
|
+
child_attr = f"{command}_command"
|
|
215
|
+
child = getattr(args, child_attr, None)
|
|
216
|
+
return (command, child) if isinstance(child, str) and child else None
|
|
217
|
+
|
|
218
|
+
builder_command = getattr(args, "builder_command", None)
|
|
219
|
+
if not isinstance(builder_command, str) or not builder_command:
|
|
220
|
+
return None
|
|
221
|
+
if builder_command == "contract":
|
|
222
|
+
return ("builder", "contract")
|
|
223
|
+
if builder_command == "app":
|
|
224
|
+
app_command = getattr(args, "builder_app_command", None)
|
|
225
|
+
if app_command == "get":
|
|
226
|
+
section = getattr(args, "builder_app_get_section", "summary") or "summary"
|
|
227
|
+
return ("builder", "app", "get", str(section))
|
|
228
|
+
return ("builder", "app", app_command) if isinstance(app_command, str) and app_command else None
|
|
229
|
+
child_attr = f"builder_{builder_command.replace('-', '_')}_command"
|
|
230
|
+
child = getattr(args, child_attr, None)
|
|
231
|
+
return ("builder", builder_command, child) if isinstance(child, str) and child else None
|