@josephyan/qingflow-app-builder-mcp 1.1.25 → 1.1.26
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 +5 -6
- package/docs/local-agent-install.md +6 -57
- package/entry_point.py +1 -1
- package/npm/bin/qingflow-app-builder-mcp.mjs +33 -2
- package/npm/lib/runtime.mjs +101 -21
- package/npm/scripts/postinstall.mjs +10 -1
- package/package.json +2 -3
- package/pyproject.toml +1 -1
- package/skills/qingflow-app-builder/SKILL.md +42 -104
- package/skills/qingflow-app-builder/references/build-complete-system.md +428 -0
- package/skills/qingflow-app-builder/references/build-single-app.md +530 -0
- package/skills/qingflow-app-builder/references/create-app.md +11 -30
- package/skills/qingflow-app-builder/references/environments.md +1 -1
- package/skills/qingflow-app-builder/references/gotchas.md +9 -31
- package/skills/qingflow-app-builder/references/match-rules.md +0 -15
- package/skills/qingflow-app-builder/references/solution-playbooks.md +0 -10
- package/skills/qingflow-app-builder/references/tool-selection.md +23 -38
- package/skills/qingflow-app-builder/references/update-flow.md +38 -22
- package/skills/qingflow-app-builder/references/update-schema.md +0 -14
- package/skills/qingflow-app-builder/references/update-views.md +14 -83
- package/skills/qingflow-app-builder-code-integrations/SKILL.md +3 -5
- package/skills/qingflow-app-builder-code-integrations/references/code-block.md +1 -1
- package/skills/qingflow-app-builder-code-integrations/references/q-linker.md +1 -1
- package/skills/qingflow-mcp-setup/SKILL.md +111 -0
- package/skills/qingflow-mcp-setup/agents/openai.yaml +4 -0
- package/skills/qingflow-mcp-setup/references/claude-desktop.md +34 -0
- package/skills/qingflow-mcp-setup/references/environments.md +61 -0
- package/skills/qingflow-mcp-setup/references/generic-stdio.md +31 -0
- package/skills/qingflow-mcp-setup/scripts/check_local_server.sh +38 -0
- package/skills/qingflow-workflow-builder/SKILL.md +96 -0
- package/skills/qingflow-workflow-builder/manifest.yaml +8 -0
- package/skills/qingflow-workflow-builder/references/01-overview.md +45 -0
- package/skills/qingflow-workflow-builder/references/02-update-mode.md +53 -0
- package/skills/qingflow-workflow-builder/references/03-flow-patterns.md +57 -0
- package/skills/qingflow-workflow-builder/references/04-stage1-business-modeling.md +131 -0
- package/skills/qingflow-workflow-builder/references/05-stage2-members-roles.md +29 -0
- package/skills/qingflow-workflow-builder/references/06-stage3-build-spec.md +165 -0
- package/skills/qingflow-workflow-builder/references/07-stage4-validate-spec.md +33 -0
- package/skills/qingflow-workflow-builder/references/08-stage5-apply-verify.md +51 -0
- package/skills/qingflow-workflow-builder/references/09-stage6-summary.md +88 -0
- package/skills/qingflow-workflow-builder/references/10-node-config-reference.md +93 -0
- package/skills/qingflow-workflow-builder/references/11-troubleshooting.md +15 -0
- package/skills/qingflow-workflow-builder/scripts/diff_flow_spec.py +275 -0
- package/skills/qingflow-workflow-builder/scripts/validate_flow_spec.py +605 -0
- package/src/qingflow_mcp/__init__.py +1 -1
- package/src/qingflow_mcp/__main__.py +2 -6
- package/src/qingflow_mcp/builder_facade/models.py +108 -485
- package/src/qingflow_mcp/builder_facade/service.py +1686 -6674
- package/src/qingflow_mcp/cli/commands/builder.py +364 -322
- package/src/qingflow_mcp/cli/commands/chart.py +1 -1
- package/src/qingflow_mcp/cli/commands/common.py +3 -12
- package/src/qingflow_mcp/cli/commands/exports.py +2 -2
- package/src/qingflow_mcp/cli/commands/imports.py +3 -3
- package/src/qingflow_mcp/cli/commands/portal.py +2 -2
- package/src/qingflow_mcp/cli/commands/record.py +27 -101
- package/src/qingflow_mcp/cli/commands/task.py +47 -28
- package/src/qingflow_mcp/cli/commands/view.py +1 -1
- package/src/qingflow_mcp/cli/context.py +3 -0
- package/src/qingflow_mcp/cli/formatters.py +16 -784
- package/src/qingflow_mcp/cli/main.py +33 -117
- package/src/qingflow_mcp/errors.py +2 -43
- package/src/qingflow_mcp/public_surface.py +17 -26
- package/src/qingflow_mcp/response_trim.py +17 -81
- package/src/qingflow_mcp/server.py +12 -14
- package/src/qingflow_mcp/server_app_builder.py +36 -79
- package/src/qingflow_mcp/server_app_user.py +16 -22
- package/src/qingflow_mcp/session_store.py +7 -11
- package/src/qingflow_mcp/solution/compiler/__init__.py +1 -3
- package/src/qingflow_mcp/solution/executor.py +18 -245
- package/src/qingflow_mcp/tools/ai_builder_tools.py +1396 -3103
- package/src/qingflow_mcp/tools/app_tools.py +43 -184
- package/src/qingflow_mcp/tools/approval_tools.py +35 -197
- package/src/qingflow_mcp/tools/auth_tools.py +16 -92
- package/src/qingflow_mcp/tools/code_block_tools.py +40 -298
- package/src/qingflow_mcp/tools/custom_button_tools.py +10 -64
- package/src/qingflow_mcp/tools/directory_tools.py +72 -236
- package/src/qingflow_mcp/tools/export_tools.py +34 -244
- package/src/qingflow_mcp/tools/feedback_tools.py +0 -9
- package/src/qingflow_mcp/tools/file_tools.py +3 -9
- package/src/qingflow_mcp/tools/import_tools.py +49 -336
- package/src/qingflow_mcp/tools/navigation_tools.py +12 -91
- package/src/qingflow_mcp/tools/package_tools.py +6 -118
- package/src/qingflow_mcp/tools/portal_tools.py +3 -39
- package/src/qingflow_mcp/tools/qingbi_report_tools.py +7 -116
- package/src/qingflow_mcp/tools/record_tools.py +360 -1145
- package/src/qingflow_mcp/tools/resource_read_tools.py +39 -188
- package/src/qingflow_mcp/tools/role_tools.py +9 -80
- package/src/qingflow_mcp/tools/solution_tools.py +45 -59
- package/src/qingflow_mcp/tools/task_context_tools.py +158 -662
- package/src/qingflow_mcp/tools/task_tools.py +29 -113
- package/src/qingflow_mcp/tools/view_tools.py +3 -106
- package/src/qingflow_mcp/tools/workflow_tools.py +4 -48
- package/src/qingflow_mcp/tools/workspace_tools.py +3 -71
- package/npm/bin/qingflow-skills.mjs +0 -5
- package/skills/qingflow-app-builder/references/complete-system-development-guide.md +0 -172
- package/skills/qingflow-app-builder/references/single-app-development-guide.md +0 -87
- package/skills/qingflow-app-builder/scripts/validate_system_build_summary.py +0 -124
- package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +0 -173
|
@@ -11,7 +11,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
11
11
|
|
|
12
12
|
get_parser = chart_subparsers.add_parser("get", help="读取报表数据")
|
|
13
13
|
get_parser.add_argument("--chart-id", required=True)
|
|
14
|
-
get_parser.set_defaults(handler=_handle_get, format_hint="
|
|
14
|
+
get_parser.set_defaults(handler=_handle_get, format_hint="generic")
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
def _handle_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
@@ -48,23 +48,14 @@ def read_secret_arg(value: str | None, *, stdin_enabled: bool, label: str) -> st
|
|
|
48
48
|
raise QingflowApiError.config_error(f"{label} is required")
|
|
49
49
|
|
|
50
50
|
|
|
51
|
-
def raise_config_error(
|
|
52
|
-
message: str,
|
|
53
|
-
*,
|
|
54
|
-
fix_hint: str | None = None,
|
|
55
|
-
error_code: str = "CONFIG_ERROR",
|
|
56
|
-
details: dict[str, Any] | None = None,
|
|
57
|
-
) -> None:
|
|
58
|
-
error_details: dict[str, Any] = {"fix_hint": fix_hint or message}
|
|
59
|
-
if details:
|
|
60
|
-
error_details.update(details)
|
|
51
|
+
def raise_config_error(message: str, *, fix_hint: str) -> None:
|
|
61
52
|
raise RuntimeError(
|
|
62
53
|
json.dumps(
|
|
63
54
|
{
|
|
64
55
|
"category": "config",
|
|
65
56
|
"message": message,
|
|
66
|
-
"error_code":
|
|
67
|
-
"details":
|
|
57
|
+
"error_code": "CONFIG_ERROR",
|
|
58
|
+
"details": {"fix_hint": fix_hint},
|
|
68
59
|
},
|
|
69
60
|
ensure_ascii=False,
|
|
70
61
|
)
|
|
@@ -12,7 +12,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
12
12
|
|
|
13
13
|
start = export_subparsers.add_parser("start", help="启动导出")
|
|
14
14
|
start.add_argument("--app-key", required=True)
|
|
15
|
-
start.add_argument("--view-id",
|
|
15
|
+
start.add_argument("--view-id", default="system:all")
|
|
16
16
|
start.add_argument("--column", dest="columns", action="append", type=int, default=[], help="只导出这些 field_id;不传时导出当前视图全部字段")
|
|
17
17
|
start.add_argument("--columns-file", help="JSON/YAML list,内容与 --column 语义一致")
|
|
18
18
|
start.add_argument("--where-file", help="JSON/YAML list,内容与 record list 的 where DSL 一致;内部先查命中 record_id 再走原生导出")
|
|
@@ -33,7 +33,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
33
33
|
|
|
34
34
|
direct = export_subparsers.add_parser("direct", help="直接导出并下载")
|
|
35
35
|
direct.add_argument("--app-key", required=True)
|
|
36
|
-
direct.add_argument("--view-id",
|
|
36
|
+
direct.add_argument("--view-id", default="system:all")
|
|
37
37
|
direct.add_argument("--column", dest="columns", action="append", type=int, default=[], help="只导出这些 field_id;不传时导出当前视图全部字段")
|
|
38
38
|
direct.add_argument("--columns-file", help="JSON/YAML list,内容与 --column 语义一致")
|
|
39
39
|
direct.add_argument("--where-file", help="JSON/YAML list,内容与 record list 的 where DSL 一致;内部先查命中 record_id 再走原生导出")
|
|
@@ -13,7 +13,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
13
13
|
template = import_subparsers.add_parser("template", help="下载导入模板")
|
|
14
14
|
template.add_argument("--app-key", required=True)
|
|
15
15
|
template.add_argument("--download-to-path")
|
|
16
|
-
template.set_defaults(handler=_handle_template, format_hint="
|
|
16
|
+
template.set_defaults(handler=_handle_template, format_hint="")
|
|
17
17
|
|
|
18
18
|
verify = import_subparsers.add_parser("verify", help="校验导入文件")
|
|
19
19
|
verify.add_argument("--app-key", required=True)
|
|
@@ -25,14 +25,14 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
25
25
|
repair.add_argument("--authorized-file-modification", action="store_true")
|
|
26
26
|
repair.add_argument("--output-path")
|
|
27
27
|
repair.add_argument("--repair", dest="selected_repairs", action="append", default=[])
|
|
28
|
-
repair.set_defaults(handler=_handle_repair, format_hint="
|
|
28
|
+
repair.set_defaults(handler=_handle_repair, format_hint="")
|
|
29
29
|
|
|
30
30
|
start = import_subparsers.add_parser("start", help="启动导入")
|
|
31
31
|
start.add_argument("--app-key", required=True)
|
|
32
32
|
start.add_argument("--verification-id", required=True)
|
|
33
33
|
start.add_argument("--being-enter-auditing", type=parse_bool_text, required=True)
|
|
34
34
|
start.add_argument("--view-key")
|
|
35
|
-
start.set_defaults(handler=_handle_start, format_hint="
|
|
35
|
+
start.set_defaults(handler=_handle_start, format_hint="")
|
|
36
36
|
|
|
37
37
|
status = import_subparsers.add_parser("status", help="查询导入状态")
|
|
38
38
|
status.add_argument("--app-key")
|
|
@@ -10,11 +10,11 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
10
10
|
portal_subparsers = parser.add_subparsers(dest="portal_command", required=True)
|
|
11
11
|
|
|
12
12
|
list_parser = portal_subparsers.add_parser("list", help="列出当前用户可访问的门户")
|
|
13
|
-
list_parser.set_defaults(handler=_handle_list, format_hint="
|
|
13
|
+
list_parser.set_defaults(handler=_handle_list, format_hint="generic")
|
|
14
14
|
|
|
15
15
|
get_parser = portal_subparsers.add_parser("get", help="读取门户内容清单")
|
|
16
16
|
get_parser.add_argument("--dash-key", required=True)
|
|
17
|
-
get_parser.set_defaults(handler=_handle_get, format_hint="
|
|
17
|
+
get_parser.set_defaults(handler=_handle_get, format_hint="generic")
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
def _handle_list(args: argparse.Namespace, context: CliContext) -> dict:
|
|
@@ -18,43 +18,34 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
18
18
|
|
|
19
19
|
schema = record_subparsers.add_parser("schema", help="读取记录相关表结构")
|
|
20
20
|
schema.add_argument("--mode", dest="legacy_mode", help=argparse.SUPPRESS)
|
|
21
|
-
schema_subparsers = schema.add_subparsers(
|
|
22
|
-
dest="record_schema_command",
|
|
23
|
-
metavar="{browse,insert,update,import,code-block}",
|
|
24
|
-
)
|
|
21
|
+
schema_subparsers = schema.add_subparsers(dest="record_schema_command")
|
|
25
22
|
schema.set_defaults(handler=_handle_schema_root, format_hint="")
|
|
26
23
|
|
|
27
|
-
schema_applicant = schema_subparsers.add_parser("applicant", help=
|
|
24
|
+
schema_applicant = schema_subparsers.add_parser("applicant", help="读取申请节点表结构")
|
|
28
25
|
schema_applicant.add_argument("--app-key", required=True)
|
|
29
|
-
schema_applicant.set_defaults(handler=_handle_schema_applicant, format_hint="
|
|
30
|
-
schema_subparsers._choices_actions = [ # type: ignore[attr-defined]
|
|
31
|
-
action
|
|
32
|
-
for action in schema_subparsers._choices_actions # type: ignore[attr-defined]
|
|
33
|
-
if getattr(action, "dest", None) != "applicant"
|
|
34
|
-
]
|
|
26
|
+
schema_applicant.set_defaults(handler=_handle_schema_applicant, format_hint="")
|
|
35
27
|
|
|
36
28
|
schema_browse = schema_subparsers.add_parser("browse", help="读取浏览视图表结构")
|
|
37
29
|
schema_browse.add_argument("--app-key", required=True)
|
|
38
30
|
schema_browse.add_argument("--view-id", required=True)
|
|
39
|
-
schema_browse.set_defaults(handler=_handle_schema_browse, format_hint="
|
|
31
|
+
schema_browse.set_defaults(handler=_handle_schema_browse, format_hint="")
|
|
40
32
|
|
|
41
33
|
schema_insert = schema_subparsers.add_parser("insert", help="读取新增记录表结构")
|
|
42
34
|
schema_insert.add_argument("--app-key", required=True)
|
|
43
|
-
schema_insert.set_defaults(handler=_handle_schema_insert, format_hint="
|
|
35
|
+
schema_insert.set_defaults(handler=_handle_schema_insert, format_hint="")
|
|
44
36
|
|
|
45
37
|
schema_update = schema_subparsers.add_parser("update", help="读取更新记录表结构")
|
|
46
38
|
schema_update.add_argument("--app-key", required=True)
|
|
47
39
|
schema_update.add_argument("--record-id", required=True)
|
|
48
|
-
schema_update.
|
|
49
|
-
schema_update.set_defaults(handler=_handle_schema_update, format_hint="record_schema")
|
|
40
|
+
schema_update.set_defaults(handler=_handle_schema_update, format_hint="")
|
|
50
41
|
|
|
51
42
|
schema_import = schema_subparsers.add_parser("import", help="读取导入表结构")
|
|
52
43
|
schema_import.add_argument("--app-key", required=True)
|
|
53
|
-
schema_import.set_defaults(handler=_handle_schema_import, format_hint="
|
|
44
|
+
schema_import.set_defaults(handler=_handle_schema_import, format_hint="")
|
|
54
45
|
|
|
55
46
|
schema_code_block = schema_subparsers.add_parser("code-block", help="读取代码块执行表结构")
|
|
56
47
|
schema_code_block.add_argument("--app-key", required=True)
|
|
57
|
-
schema_code_block.set_defaults(handler=_handle_schema_code_block, format_hint="
|
|
48
|
+
schema_code_block.set_defaults(handler=_handle_schema_code_block, format_hint="")
|
|
58
49
|
|
|
59
50
|
member_candidates = record_subparsers.add_parser("member-candidates", help="读取成员字段候选项")
|
|
60
51
|
member_candidates.add_argument("--app-key", required=True)
|
|
@@ -65,7 +56,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
65
56
|
member_candidates.add_argument("--record-id")
|
|
66
57
|
member_candidates.add_argument("--workflow-node-id", type=int)
|
|
67
58
|
member_candidates.add_argument("--fields-file")
|
|
68
|
-
member_candidates.set_defaults(handler=_handle_member_candidates, format_hint="
|
|
59
|
+
member_candidates.set_defaults(handler=_handle_member_candidates, format_hint="")
|
|
69
60
|
|
|
70
61
|
department_candidates = record_subparsers.add_parser("department-candidates", help="读取部门字段候选项")
|
|
71
62
|
department_candidates.add_argument("--app-key", required=True)
|
|
@@ -76,41 +67,18 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
76
67
|
department_candidates.add_argument("--record-id")
|
|
77
68
|
department_candidates.add_argument("--workflow-node-id", type=int)
|
|
78
69
|
department_candidates.add_argument("--fields-file")
|
|
79
|
-
department_candidates.set_defaults(handler=_handle_department_candidates, format_hint="
|
|
70
|
+
department_candidates.set_defaults(handler=_handle_department_candidates, format_hint="")
|
|
80
71
|
|
|
81
72
|
list_parser = record_subparsers.add_parser("list", help="列出记录")
|
|
82
73
|
list_parser.add_argument("--app-key", required=True)
|
|
83
|
-
list_parser.add_argument(
|
|
84
|
-
|
|
85
|
-
dest="columns",
|
|
86
|
-
action="append",
|
|
87
|
-
type=int,
|
|
88
|
-
default=[],
|
|
89
|
-
metavar="FIELD_ID",
|
|
90
|
-
help="只返回这些 field_id;可重复传",
|
|
91
|
-
)
|
|
92
|
-
list_parser.add_argument(
|
|
93
|
-
"--columns-file",
|
|
94
|
-
help="JSON/YAML list;元素为 field_id 整数/整数字符串或 {'field_id': ...}",
|
|
95
|
-
)
|
|
74
|
+
list_parser.add_argument("--column", dest="columns", action="append", type=int, default=[])
|
|
75
|
+
list_parser.add_argument("--columns-file")
|
|
96
76
|
list_parser.add_argument("--query")
|
|
97
|
-
list_parser.add_argument(
|
|
98
|
-
|
|
99
|
-
dest="query_fields",
|
|
100
|
-
action="append",
|
|
101
|
-
type=int,
|
|
102
|
-
default=[],
|
|
103
|
-
metavar="FIELD_ID",
|
|
104
|
-
help="全文搜索范围 field_id;可重复传",
|
|
105
|
-
)
|
|
106
|
-
list_parser.add_argument(
|
|
107
|
-
"--query-fields-file",
|
|
108
|
-
help="JSON/YAML list;元素为 field_id 整数/整数字符串或 {'field_id': ...}",
|
|
109
|
-
)
|
|
77
|
+
list_parser.add_argument("--query-field", dest="query_fields", action="append", type=int, default=[])
|
|
78
|
+
list_parser.add_argument("--query-fields-file")
|
|
110
79
|
list_parser.add_argument("--where-file")
|
|
111
80
|
list_parser.add_argument("--order-by-file")
|
|
112
81
|
list_parser.add_argument("--page", type=int, default=1)
|
|
113
|
-
list_parser.add_argument("--page-size", type=int, default=10)
|
|
114
82
|
list_parser.add_argument("--view-id")
|
|
115
83
|
list_parser.add_argument("--list-type", dest="legacy_list_type", type=int, help=argparse.SUPPRESS)
|
|
116
84
|
list_parser.add_argument("--view-key", dest="legacy_view_key", help=argparse.SUPPRESS)
|
|
@@ -119,19 +87,8 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
119
87
|
|
|
120
88
|
access_parser = record_subparsers.add_parser("access", help="访问记录并写入本地 CSV 分片")
|
|
121
89
|
access_parser.add_argument("--app-key", required=True)
|
|
122
|
-
access_parser.add_argument(
|
|
123
|
-
|
|
124
|
-
dest="columns",
|
|
125
|
-
action="append",
|
|
126
|
-
type=int,
|
|
127
|
-
default=[],
|
|
128
|
-
metavar="FIELD_ID",
|
|
129
|
-
help="导出这些 field_id 到 CSV;可重复传",
|
|
130
|
-
)
|
|
131
|
-
access_parser.add_argument(
|
|
132
|
-
"--columns-file",
|
|
133
|
-
help="JSON/YAML list;元素为 field_id 整数/整数字符串或 {'field_id': ...}",
|
|
134
|
-
)
|
|
90
|
+
access_parser.add_argument("--column", dest="columns", action="append", type=int, default=[])
|
|
91
|
+
access_parser.add_argument("--columns-file")
|
|
135
92
|
access_parser.add_argument("--where-file")
|
|
136
93
|
access_parser.add_argument("--order-by-file")
|
|
137
94
|
access_parser.add_argument("--view-id", required=True)
|
|
@@ -140,26 +97,15 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
140
97
|
get = record_subparsers.add_parser("get", help="读取单条记录")
|
|
141
98
|
get.add_argument("--app-key", required=True)
|
|
142
99
|
get.add_argument("--record-id", required=True)
|
|
143
|
-
get.add_argument(
|
|
144
|
-
|
|
145
|
-
dest="columns",
|
|
146
|
-
action="append",
|
|
147
|
-
type=int,
|
|
148
|
-
default=[],
|
|
149
|
-
metavar="FIELD_ID",
|
|
150
|
-
help="聚焦这些 field_id;可重复传",
|
|
151
|
-
)
|
|
152
|
-
get.add_argument(
|
|
153
|
-
"--columns-file",
|
|
154
|
-
help="JSON/YAML list;元素为 field_id 整数/整数字符串或 {'field_id': ...}",
|
|
155
|
-
)
|
|
100
|
+
get.add_argument("--column", dest="columns", action="append", type=int, default=[])
|
|
101
|
+
get.add_argument("--columns-file")
|
|
156
102
|
get.add_argument("--view-id")
|
|
157
103
|
get.set_defaults(handler=_handle_get, format_hint="record_get")
|
|
158
104
|
|
|
159
105
|
logs = record_subparsers.add_parser("logs", help="读取单条记录全量日志并写入本地 JSONL")
|
|
160
106
|
logs.add_argument("--app-key", required=True)
|
|
161
107
|
logs.add_argument("--record-id", required=True)
|
|
162
|
-
logs.add_argument("--view-id"
|
|
108
|
+
logs.add_argument("--view-id")
|
|
163
109
|
logs.set_defaults(handler=_handle_logs, format_hint="record_logs")
|
|
164
110
|
|
|
165
111
|
insert = record_subparsers.add_parser("insert", help="新增记录")
|
|
@@ -167,25 +113,22 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
167
113
|
insert.add_argument("--fields-file", help=argparse.SUPPRESS)
|
|
168
114
|
insert.add_argument("--items-file")
|
|
169
115
|
insert.add_argument("--verify-write", action=argparse.BooleanOptionalAction, default=True)
|
|
170
|
-
insert.set_defaults(handler=_handle_insert, format_hint="
|
|
116
|
+
insert.set_defaults(handler=_handle_insert, format_hint="")
|
|
171
117
|
|
|
172
118
|
update = record_subparsers.add_parser("update", help="更新记录")
|
|
173
119
|
update.add_argument("--app-key", required=True)
|
|
174
120
|
update.add_argument("--record-id")
|
|
175
121
|
update.add_argument("--fields-file")
|
|
176
122
|
update.add_argument("--items-file")
|
|
177
|
-
update.add_argument("--view-id")
|
|
178
123
|
update.add_argument("--dry-run", action=argparse.BooleanOptionalAction, default=False)
|
|
179
124
|
update.add_argument("--verify-write", action=argparse.BooleanOptionalAction, default=True)
|
|
180
|
-
update.set_defaults(handler=_handle_update, format_hint="
|
|
125
|
+
update.set_defaults(handler=_handle_update, format_hint="")
|
|
181
126
|
|
|
182
127
|
delete = record_subparsers.add_parser("delete", help="删除记录")
|
|
183
128
|
delete.add_argument("--app-key", required=True)
|
|
184
129
|
delete.add_argument("--record-id")
|
|
185
130
|
delete.add_argument("--record-ids-file")
|
|
186
|
-
delete.
|
|
187
|
-
delete.add_argument("--list-type", dest="legacy_list_type", type=int, help=argparse.SUPPRESS)
|
|
188
|
-
delete.set_defaults(handler=_handle_delete, format_hint="record_delete")
|
|
131
|
+
delete.set_defaults(handler=_handle_delete, format_hint="")
|
|
189
132
|
|
|
190
133
|
analyze = record_subparsers.add_parser("analyze", help=argparse.SUPPRESS)
|
|
191
134
|
record_subparsers._choices_actions = [ # type: ignore[attr-defined]
|
|
@@ -210,7 +153,6 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
210
153
|
code_block.add_argument("--app-key", required=True)
|
|
211
154
|
code_block.add_argument("--record-id", required=True)
|
|
212
155
|
code_block.add_argument("--code-block-field", required=True)
|
|
213
|
-
code_block.add_argument("--view-id")
|
|
214
156
|
code_block.add_argument("--role", type=int, default=1)
|
|
215
157
|
code_block.add_argument("--workflow-node-id", type=int)
|
|
216
158
|
code_block.add_argument("--answers-file")
|
|
@@ -219,7 +161,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
219
161
|
code_block.add_argument("--apply-writeback", action=argparse.BooleanOptionalAction, default=True)
|
|
220
162
|
code_block.add_argument("--verify-writeback", action=argparse.BooleanOptionalAction, default=True)
|
|
221
163
|
code_block.add_argument("--force-refresh-form", action="store_true")
|
|
222
|
-
code_block.set_defaults(handler=_handle_code_block_run, format_hint="
|
|
164
|
+
code_block.set_defaults(handler=_handle_code_block_run, format_hint="")
|
|
223
165
|
|
|
224
166
|
|
|
225
167
|
def _columns(args: argparse.Namespace) -> list[Any]:
|
|
@@ -240,20 +182,20 @@ def _handle_schema_root(args: argparse.Namespace, _context: CliContext) -> dict:
|
|
|
240
182
|
mode = (args.legacy_mode or "").strip()
|
|
241
183
|
if mode:
|
|
242
184
|
replacement = {
|
|
243
|
-
"applicant": "record schema
|
|
185
|
+
"applicant": "record schema applicant --app-key APP_KEY",
|
|
244
186
|
"browse": "record schema browse --app-key APP_KEY --view-id VIEW_ID",
|
|
245
187
|
"insert": "record schema insert --app-key APP_KEY",
|
|
246
|
-
"update": "record schema update --app-key APP_KEY --record-id RECORD_ID
|
|
188
|
+
"update": "record schema update --app-key APP_KEY --record-id RECORD_ID",
|
|
247
189
|
"import": "record schema import --app-key APP_KEY",
|
|
248
190
|
"code-block": "record schema code-block --app-key APP_KEY",
|
|
249
|
-
}.get(mode, "record schema <browse|insert|update|import|code-block> ...")
|
|
191
|
+
}.get(mode, "record schema <applicant|browse|insert|update|import|code-block> ...")
|
|
250
192
|
raise_config_error(
|
|
251
193
|
"record schema --mode is no longer accepted.",
|
|
252
194
|
fix_hint=f"Use `{replacement}` instead.",
|
|
253
195
|
)
|
|
254
196
|
raise_config_error(
|
|
255
197
|
"record schema requires an explicit subcommand.",
|
|
256
|
-
fix_hint="Use one of: `record schema browse`, `record schema insert`, `record schema update`, `record schema import`, or `record schema code-block`.",
|
|
198
|
+
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`.",
|
|
257
199
|
)
|
|
258
200
|
|
|
259
201
|
|
|
@@ -282,7 +224,6 @@ def _handle_schema_update(args: argparse.Namespace, context: CliContext) -> dict
|
|
|
282
224
|
profile=args.profile,
|
|
283
225
|
app_key=args.app_key,
|
|
284
226
|
record_id=args.record_id,
|
|
285
|
-
view_id=args.view_id,
|
|
286
227
|
)
|
|
287
228
|
|
|
288
229
|
|
|
@@ -368,7 +309,6 @@ def _handle_list(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
368
309
|
where=load_list_arg(args.where_file, option_name="--where-file"),
|
|
369
310
|
order_by=load_list_arg(args.order_by_file, option_name="--order-by-file"),
|
|
370
311
|
page=args.page,
|
|
371
|
-
page_size=args.page_size,
|
|
372
312
|
view_id=args.view_id,
|
|
373
313
|
)
|
|
374
314
|
|
|
@@ -442,7 +382,6 @@ def _handle_update(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
442
382
|
record_id=None,
|
|
443
383
|
fields=None,
|
|
444
384
|
items=require_list_arg(args.items_file, option_name="--items-file"),
|
|
445
|
-
view_id=args.view_id,
|
|
446
385
|
dry_run=bool(args.dry_run),
|
|
447
386
|
verify_write=bool(args.verify_write),
|
|
448
387
|
)
|
|
@@ -461,29 +400,17 @@ def _handle_update(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
461
400
|
app_key=args.app_key,
|
|
462
401
|
record_id=args.record_id,
|
|
463
402
|
fields=require_object_arg(args.fields_file, option_name="--fields-file"),
|
|
464
|
-
view_id=args.view_id,
|
|
465
403
|
verify_write=bool(args.verify_write),
|
|
466
404
|
)
|
|
467
405
|
|
|
468
406
|
|
|
469
407
|
def _handle_delete(args: argparse.Namespace, context: CliContext) -> dict:
|
|
470
|
-
if args.legacy_list_type is not None:
|
|
471
|
-
raise_config_error(
|
|
472
|
-
"record delete no longer accepts list_type.",
|
|
473
|
-
fix_hint="Call `app_get` first and pass an accessible system `view_id`, for example `--view-id system:all`.",
|
|
474
|
-
)
|
|
475
|
-
if not (args.view_id or "").strip():
|
|
476
|
-
raise_config_error(
|
|
477
|
-
"record delete requires --view-id.",
|
|
478
|
-
fix_hint="Use a system view_id from app get accessible_views, for example `--view-id system:all`; custom views are not supported for deletion.",
|
|
479
|
-
)
|
|
480
408
|
record_ids = load_list_arg(args.record_ids_file, option_name="--record-ids-file")
|
|
481
409
|
return context.record.record_delete_public(
|
|
482
410
|
profile=args.profile,
|
|
483
411
|
app_key=args.app_key,
|
|
484
412
|
record_id=args.record_id,
|
|
485
413
|
record_ids=record_ids,
|
|
486
|
-
view_id=args.view_id,
|
|
487
414
|
)
|
|
488
415
|
|
|
489
416
|
|
|
@@ -519,7 +446,6 @@ def _handle_code_block_run(args: argparse.Namespace, context: CliContext) -> dic
|
|
|
519
446
|
app_key=args.app_key,
|
|
520
447
|
record_id=args.record_id,
|
|
521
448
|
code_block_field=args.code_block_field,
|
|
522
|
-
view_id=args.view_id,
|
|
523
449
|
role=args.role,
|
|
524
450
|
workflow_node_id=args.workflow_node_id,
|
|
525
451
|
answers=load_list_arg(args.answers_file, option_name="--answers-file"),
|
|
@@ -47,14 +47,20 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
47
47
|
list_parser.add_argument("--page-size", type=int, default=20)
|
|
48
48
|
list_parser.set_defaults(handler=_handle_list, format_hint="task_list")
|
|
49
49
|
|
|
50
|
-
get = task_subparsers.add_parser("get", help="
|
|
51
|
-
get.add_argument("--task-id"
|
|
50
|
+
get = task_subparsers.add_parser("get", help="读取待办详情;推荐直接传 --task-id")
|
|
51
|
+
get.add_argument("--task-id")
|
|
52
|
+
get.add_argument("--app-key")
|
|
53
|
+
get.add_argument("--record-id")
|
|
54
|
+
get.add_argument("--workflow-node-id", type=int)
|
|
52
55
|
get.add_argument("--include-candidates", action=argparse.BooleanOptionalAction, default=True)
|
|
53
56
|
get.add_argument("--include-associated-reports", action=argparse.BooleanOptionalAction, default=True)
|
|
54
|
-
get.set_defaults(handler=_handle_get, format_hint="task_get"
|
|
57
|
+
get.set_defaults(handler=_handle_get, format_hint="task_get")
|
|
55
58
|
|
|
56
|
-
action = task_subparsers.add_parser("action", help="
|
|
57
|
-
action.add_argument("--task-id"
|
|
59
|
+
action = task_subparsers.add_parser("action", help="执行待办动作")
|
|
60
|
+
action.add_argument("--task-id")
|
|
61
|
+
action.add_argument("--app-key")
|
|
62
|
+
action.add_argument("--record-id")
|
|
63
|
+
action.add_argument("--workflow-node-id", type=int)
|
|
58
64
|
action.add_argument("--action", help="不传时在交互终端中选择当前待办可执行动作")
|
|
59
65
|
action.add_argument("--payload-file")
|
|
60
66
|
action.add_argument("--fields-file")
|
|
@@ -62,27 +68,24 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
62
68
|
handler=_handle_action,
|
|
63
69
|
format_hint="task_action_execute",
|
|
64
70
|
hide_effective_context_line=True,
|
|
65
|
-
app_key="",
|
|
66
|
-
record_id="",
|
|
67
|
-
workflow_node_id=0,
|
|
68
71
|
)
|
|
69
72
|
|
|
70
|
-
report = task_subparsers.add_parser("report", help="
|
|
71
|
-
report.add_argument("--task-id"
|
|
73
|
+
report = task_subparsers.add_parser("report", help="读取待办关联报表详情;推荐直接传 --task-id")
|
|
74
|
+
report.add_argument("--task-id")
|
|
75
|
+
report.add_argument("--app-key")
|
|
76
|
+
report.add_argument("--record-id")
|
|
77
|
+
report.add_argument("--workflow-node-id", type=int)
|
|
72
78
|
report.add_argument("--report-id", type=int, help="不传时在交互终端中选择关联报表")
|
|
73
79
|
report.add_argument("--page", type=int, default=1)
|
|
74
80
|
report.add_argument("--page-size", type=int, default=20)
|
|
75
|
-
report.set_defaults(
|
|
76
|
-
handler=_handle_report,
|
|
77
|
-
format_hint="task_associated_report_detail_get",
|
|
78
|
-
app_key="",
|
|
79
|
-
record_id="",
|
|
80
|
-
workflow_node_id=0,
|
|
81
|
-
)
|
|
81
|
+
report.set_defaults(handler=_handle_report, format_hint="task_associated_report_detail_get")
|
|
82
82
|
|
|
83
|
-
log = task_subparsers.add_parser("log", help="
|
|
84
|
-
log.add_argument("--task-id"
|
|
85
|
-
log.
|
|
83
|
+
log = task_subparsers.add_parser("log", help="读取流程日志;推荐直接传 --task-id")
|
|
84
|
+
log.add_argument("--task-id")
|
|
85
|
+
log.add_argument("--app-key")
|
|
86
|
+
log.add_argument("--record-id")
|
|
87
|
+
log.add_argument("--workflow-node-id", type=int)
|
|
88
|
+
log.set_defaults(handler=_handle_log, format_hint="")
|
|
86
89
|
|
|
87
90
|
|
|
88
91
|
def _handle_list(args: argparse.Namespace, context: CliContext) -> dict:
|
|
@@ -162,6 +165,9 @@ def _handle_action(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
162
165
|
return context.task.task_action_execute(
|
|
163
166
|
profile=args.profile,
|
|
164
167
|
task_id=args.task_id,
|
|
168
|
+
app_key=args.app_key or "",
|
|
169
|
+
record_id=args.record_id or "",
|
|
170
|
+
workflow_node_id=int(args.workflow_node_id or 0),
|
|
165
171
|
action=args.action,
|
|
166
172
|
payload=payload,
|
|
167
173
|
fields=fields,
|
|
@@ -222,6 +228,9 @@ def _run_task_workbench_task_loop(args: argparse.Namespace, context: CliContext)
|
|
|
222
228
|
result = context.task.task_action_execute(
|
|
223
229
|
profile=args.profile,
|
|
224
230
|
task_id=args.task_id,
|
|
231
|
+
app_key=args.app_key or "",
|
|
232
|
+
record_id=args.record_id or "",
|
|
233
|
+
workflow_node_id=int(args.workflow_node_id or 0),
|
|
225
234
|
action=args.action,
|
|
226
235
|
payload=payload_selection,
|
|
227
236
|
fields={},
|
|
@@ -234,20 +243,30 @@ def _run_task_workbench_task_loop(args: argparse.Namespace, context: CliContext)
|
|
|
234
243
|
def _resolve_task_locator_or_select(args: argparse.Namespace, context: CliContext, *, tool_name: str) -> dict | None:
|
|
235
244
|
if (args.task_id or "").strip():
|
|
236
245
|
return None
|
|
246
|
+
has_app_key = bool((args.app_key or "").strip())
|
|
247
|
+
has_record_id = bool((args.record_id or "").strip())
|
|
248
|
+
has_workflow_node_id = int(args.workflow_node_id or 0) > 0
|
|
249
|
+
if has_app_key and has_record_id and has_workflow_node_id:
|
|
250
|
+
return None
|
|
251
|
+
if has_app_key or has_record_id or has_workflow_node_id:
|
|
252
|
+
raise_config_error(
|
|
253
|
+
f"{tool_name} requires --task-id, or --app-key together with --record-id and --workflow-node-id",
|
|
254
|
+
fix_hint="Either pass `--task-id TASK_ID`, or provide the full locator triple `--app-key --record-id --workflow-node-id`.",
|
|
255
|
+
)
|
|
237
256
|
|
|
238
257
|
selection = _choose_todo_task_interactively(args, context, tool_name=tool_name)
|
|
239
258
|
if selection.status == "unavailable":
|
|
240
259
|
raise_config_error(
|
|
241
|
-
f"{tool_name} requires --task-id
|
|
260
|
+
f"{tool_name} requires --task-id, or --app-key together with --record-id and --workflow-node-id",
|
|
242
261
|
fix_hint=(
|
|
243
262
|
"Retry in an interactive terminal to choose from current todo tasks, "
|
|
244
|
-
"or pass `--task-id
|
|
263
|
+
"or pass `--task-id TASK_ID` explicitly."
|
|
245
264
|
),
|
|
246
265
|
)
|
|
247
266
|
if selection.status == "empty":
|
|
248
267
|
raise_config_error(
|
|
249
268
|
selection.message or f"{tool_name} could not open a selector because no current todo tasks are available.",
|
|
250
|
-
fix_hint="Run `task list` to confirm current todo tasks,
|
|
269
|
+
fix_hint="Run `task list` to confirm current todo tasks, or retry later with `--task-id TASK_ID`.",
|
|
251
270
|
)
|
|
252
271
|
if selection.status == "cancelled":
|
|
253
272
|
return cancelled_result(selection.message or "已取消")
|
|
@@ -308,7 +327,7 @@ def _choose_todo_task_interactively(
|
|
|
308
327
|
args,
|
|
309
328
|
title=title,
|
|
310
329
|
unavailable_message=(
|
|
311
|
-
f"{tool_name} requires --task-id
|
|
330
|
+
f"{tool_name} requires --task-id, or --app-key together with --record-id and --workflow-node-id"
|
|
312
331
|
),
|
|
313
332
|
empty_message=f"{tool_name} could not open a selector because no current todo tasks are available.",
|
|
314
333
|
load_options=load_options,
|
|
@@ -328,7 +347,7 @@ def _resolve_report_id_or_select(args: argparse.Namespace, context: CliContext)
|
|
|
328
347
|
if selection.status == "empty":
|
|
329
348
|
raise_config_error(
|
|
330
349
|
selection.message or "task report could not open a selector because the selected task has no visible associated reports.",
|
|
331
|
-
fix_hint="Run `task get --task-id
|
|
350
|
+
fix_hint="Run `task get --task-id TASK_ID` to inspect `extras.associated_reports`, or choose another task.",
|
|
332
351
|
)
|
|
333
352
|
if selection.status == "cancelled":
|
|
334
353
|
return cancelled_result(selection.message or "已取消")
|
|
@@ -376,7 +395,7 @@ def _resolve_task_action_or_select(
|
|
|
376
395
|
if selection.status == "empty":
|
|
377
396
|
raise_config_error(
|
|
378
397
|
selection.message or "task action could not open an action selector because no interactive actions are available.",
|
|
379
|
-
fix_hint="Run `task get --task-id
|
|
398
|
+
fix_hint="Run `task get --task-id TASK_ID` to inspect available_actions, or pass a supported `--action` explicitly.",
|
|
380
399
|
)
|
|
381
400
|
if selection.status == "cancelled":
|
|
382
401
|
return cancelled_result(selection.message or "已取消")
|
|
@@ -457,7 +476,7 @@ def _resolve_action_payload_or_select(
|
|
|
457
476
|
if selection.status == "empty":
|
|
458
477
|
raise_config_error(
|
|
459
478
|
selection.message or "task rollback could not open a selector because no rollback candidates are visible.",
|
|
460
|
-
fix_hint="Run `task get --task-id
|
|
479
|
+
fix_hint="Run `task get --task-id TASK_ID` to inspect rollback candidates, or choose another action.",
|
|
461
480
|
)
|
|
462
481
|
if selection.status == "cancelled":
|
|
463
482
|
return cancelled_result(selection.message or "已取消")
|
|
@@ -474,7 +493,7 @@ def _resolve_action_payload_or_select(
|
|
|
474
493
|
if selection.status == "empty":
|
|
475
494
|
raise_config_error(
|
|
476
495
|
selection.message or "task transfer could not open a selector because no transfer candidates are visible.",
|
|
477
|
-
fix_hint="Run `task get --task-id
|
|
496
|
+
fix_hint="Run `task get --task-id TASK_ID` to inspect transfer candidates, or choose another action.",
|
|
478
497
|
)
|
|
479
498
|
if selection.status == "cancelled":
|
|
480
499
|
return cancelled_result(selection.message or "已取消")
|
|
@@ -11,7 +11,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
11
11
|
|
|
12
12
|
get_parser = view_subparsers.add_parser("get", help="读取视图资源描述")
|
|
13
13
|
get_parser.add_argument("--view-id", required=True)
|
|
14
|
-
get_parser.set_defaults(handler=_handle_get, format_hint="
|
|
14
|
+
get_parser.set_defaults(handler=_handle_get, format_hint="generic")
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
def _handle_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
@@ -13,6 +13,7 @@ from ..tools.feedback_tools import FeedbackTools
|
|
|
13
13
|
from ..tools.file_tools import FileTools
|
|
14
14
|
from ..tools.import_tools import ImportTools
|
|
15
15
|
from ..tools.record_tools import RecordTools
|
|
16
|
+
from ..tools.repository_dev_tools import RepositoryDevTools
|
|
16
17
|
from ..tools.resource_read_tools import ResourceReadTools
|
|
17
18
|
from ..tools.task_context_tools import TaskContextTools
|
|
18
19
|
from ..tools.workspace_tools import WorkspaceTools
|
|
@@ -34,6 +35,7 @@ class CliContext:
|
|
|
34
35
|
files: FileTools
|
|
35
36
|
builder_feedback: FeedbackTools
|
|
36
37
|
builder: AiBuilderTools
|
|
38
|
+
repo: RepositoryDevTools
|
|
37
39
|
|
|
38
40
|
def close(self) -> None:
|
|
39
41
|
self.backend.close()
|
|
@@ -57,4 +59,5 @@ def build_cli_context() -> CliContext:
|
|
|
57
59
|
files=FileTools(sessions, backend),
|
|
58
60
|
builder_feedback=FeedbackTools(backend, mcp_side="App Builder MCP"),
|
|
59
61
|
builder=AiBuilderTools(sessions, backend),
|
|
62
|
+
repo=RepositoryDevTools(sessions, backend),
|
|
60
63
|
)
|