@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.
- package/README.md +31 -0
- package/docs/local-agent-install.md +309 -0
- package/entry_point.py +13 -0
- package/npm/bin/qingflow.mjs +5 -0
- package/npm/lib/runtime.mjs +346 -0
- package/npm/scripts/postinstall.mjs +16 -0
- package/package.json +34 -0
- package/pyproject.toml +67 -0
- package/qingflow +15 -0
- package/src/qingflow_mcp/__init__.py +37 -0
- package/src/qingflow_mcp/__main__.py +5 -0
- package/src/qingflow_mcp/backend_client.py +649 -0
- package/src/qingflow_mcp/builder_facade/__init__.py +3 -0
- package/src/qingflow_mcp/builder_facade/models.py +1846 -0
- package/src/qingflow_mcp/builder_facade/service.py +16502 -0
- package/src/qingflow_mcp/cli/__init__.py +1 -0
- package/src/qingflow_mcp/cli/commands/__init__.py +18 -0
- package/src/qingflow_mcp/cli/commands/app.py +40 -0
- package/src/qingflow_mcp/cli/commands/auth.py +112 -0
- package/src/qingflow_mcp/cli/commands/builder.py +539 -0
- package/src/qingflow_mcp/cli/commands/chart.py +18 -0
- package/src/qingflow_mcp/cli/commands/common.py +62 -0
- package/src/qingflow_mcp/cli/commands/imports.py +96 -0
- package/src/qingflow_mcp/cli/commands/portal.py +25 -0
- package/src/qingflow_mcp/cli/commands/record.py +331 -0
- package/src/qingflow_mcp/cli/commands/repo.py +80 -0
- package/src/qingflow_mcp/cli/commands/task.py +141 -0
- package/src/qingflow_mcp/cli/commands/view.py +18 -0
- package/src/qingflow_mcp/cli/commands/workspace.py +110 -0
- package/src/qingflow_mcp/cli/context.py +60 -0
- package/src/qingflow_mcp/cli/formatters.py +573 -0
- package/src/qingflow_mcp/cli/json_io.py +50 -0
- package/src/qingflow_mcp/cli/main.py +186 -0
- package/src/qingflow_mcp/cli/qingflow_login.py +116 -0
- package/src/qingflow_mcp/cli/terminal_ui.py +173 -0
- package/src/qingflow_mcp/config.py +407 -0
- package/src/qingflow_mcp/errors.py +66 -0
- package/src/qingflow_mcp/id_utils.py +49 -0
- package/src/qingflow_mcp/import_store.py +121 -0
- package/src/qingflow_mcp/json_types.py +18 -0
- package/src/qingflow_mcp/list_type_labels.py +76 -0
- package/src/qingflow_mcp/public_surface.py +243 -0
- package/src/qingflow_mcp/repository_store.py +71 -0
- package/src/qingflow_mcp/response_trim.py +841 -0
- package/src/qingflow_mcp/server.py +216 -0
- package/src/qingflow_mcp/server_app_builder.py +543 -0
- package/src/qingflow_mcp/server_app_user.py +386 -0
- package/src/qingflow_mcp/session_store.py +369 -0
- package/src/qingflow_mcp/solution/__init__.py +6 -0
- package/src/qingflow_mcp/solution/build_assembly_store.py +181 -0
- package/src/qingflow_mcp/solution/compiler/__init__.py +282 -0
- package/src/qingflow_mcp/solution/compiler/chart_compiler.py +96 -0
- package/src/qingflow_mcp/solution/compiler/form_compiler.py +495 -0
- package/src/qingflow_mcp/solution/compiler/icon_utils.py +187 -0
- package/src/qingflow_mcp/solution/compiler/navigation_compiler.py +57 -0
- package/src/qingflow_mcp/solution/compiler/package_compiler.py +19 -0
- package/src/qingflow_mcp/solution/compiler/portal_compiler.py +60 -0
- package/src/qingflow_mcp/solution/compiler/view_compiler.py +51 -0
- package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +173 -0
- package/src/qingflow_mcp/solution/design_session.py +222 -0
- package/src/qingflow_mcp/solution/design_store.py +100 -0
- package/src/qingflow_mcp/solution/executor.py +2398 -0
- package/src/qingflow_mcp/solution/normalizer.py +23 -0
- package/src/qingflow_mcp/solution/requirements_builder.py +536 -0
- package/src/qingflow_mcp/solution/run_store.py +244 -0
- package/src/qingflow_mcp/solution/spec_models.py +855 -0
- package/src/qingflow_mcp/tools/__init__.py +1 -0
- package/src/qingflow_mcp/tools/ai_builder_tools.py +3449 -0
- package/src/qingflow_mcp/tools/app_tools.py +926 -0
- package/src/qingflow_mcp/tools/approval_tools.py +1062 -0
- package/src/qingflow_mcp/tools/auth_tools.py +1133 -0
- package/src/qingflow_mcp/tools/base.py +281 -0
- package/src/qingflow_mcp/tools/code_block_tools.py +777 -0
- package/src/qingflow_mcp/tools/custom_button_tools.py +202 -0
- package/src/qingflow_mcp/tools/directory_tools.py +675 -0
- package/src/qingflow_mcp/tools/feedback_tools.py +238 -0
- package/src/qingflow_mcp/tools/file_tools.py +409 -0
- package/src/qingflow_mcp/tools/import_tools.py +2223 -0
- package/src/qingflow_mcp/tools/navigation_tools.py +210 -0
- package/src/qingflow_mcp/tools/package_tools.py +326 -0
- package/src/qingflow_mcp/tools/portal_tools.py +158 -0
- package/src/qingflow_mcp/tools/qingbi_report_tools.py +374 -0
- package/src/qingflow_mcp/tools/record_tools.py +14291 -0
- package/src/qingflow_mcp/tools/repository_dev_tools.py +552 -0
- package/src/qingflow_mcp/tools/resource_read_tools.py +503 -0
- package/src/qingflow_mcp/tools/role_tools.py +112 -0
- package/src/qingflow_mcp/tools/solution_tools.py +4054 -0
- package/src/qingflow_mcp/tools/task_context_tools.py +2986 -0
- package/src/qingflow_mcp/tools/task_tools.py +889 -0
- package/src/qingflow_mcp/tools/view_tools.py +335 -0
- package/src/qingflow_mcp/tools/workflow_tools.py +376 -0
- package/src/qingflow_mcp/tools/workspace_tools.py +266 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
RECORD_LIST_TYPE_LABELS: dict[int, str] = {
|
|
5
|
+
1: "待办",
|
|
6
|
+
2: "已办",
|
|
7
|
+
3: "我发起的-已通过",
|
|
8
|
+
4: "我发起的-已拒绝",
|
|
9
|
+
5: "我发起的-草稿",
|
|
10
|
+
6: "我发起的-待完善",
|
|
11
|
+
7: "我发起的-流程中",
|
|
12
|
+
8: "数据管理-全部数据",
|
|
13
|
+
9: "数据管理-已通过",
|
|
14
|
+
10: "数据管理-已拒绝",
|
|
15
|
+
11: "数据管理-流程中",
|
|
16
|
+
12: "抄送",
|
|
17
|
+
13: "图表分享者",
|
|
18
|
+
14: "我发起的",
|
|
19
|
+
15: "数据管理-已结束",
|
|
20
|
+
16: "我发起的-已结束",
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
SYSTEM_VIEW_DEFINITIONS: tuple[tuple[str, int, str], ...] = (
|
|
24
|
+
("system:all", 8, "全部数据"),
|
|
25
|
+
("system:initiated", 14, "我发起的"),
|
|
26
|
+
("system:todo", 1, "待办"),
|
|
27
|
+
("system:done", 2, "已办"),
|
|
28
|
+
("system:cc", 12, "抄送我的"),
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
SYSTEM_VIEW_ID_TO_LIST_TYPE: dict[str, int] = {view_id: list_type for view_id, list_type, _ in SYSTEM_VIEW_DEFINITIONS}
|
|
32
|
+
SYSTEM_VIEW_ID_TO_NAME: dict[str, str] = {view_id: name for view_id, _, name in SYSTEM_VIEW_DEFINITIONS}
|
|
33
|
+
SYSTEM_LIST_TYPE_TO_VIEW_ID: dict[int, str] = {list_type: view_id for view_id, list_type, _ in SYSTEM_VIEW_DEFINITIONS}
|
|
34
|
+
|
|
35
|
+
TASK_TYPE_LABELS: dict[int, str] = {
|
|
36
|
+
1: "待办",
|
|
37
|
+
2: "我发起的",
|
|
38
|
+
3: "抄送",
|
|
39
|
+
5: "已办",
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
APP_PUBLISH_STATUS_LABELS: dict[int, str] = {
|
|
43
|
+
0: "未发布",
|
|
44
|
+
1: "已发布-有修改",
|
|
45
|
+
2: "已发布",
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def get_record_list_type_label(list_type: int | None) -> str | None:
|
|
50
|
+
if list_type is None:
|
|
51
|
+
return None
|
|
52
|
+
return RECORD_LIST_TYPE_LABELS.get(list_type)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def get_system_view_id(list_type: int | None) -> str | None:
|
|
56
|
+
if list_type is None:
|
|
57
|
+
return None
|
|
58
|
+
return SYSTEM_LIST_TYPE_TO_VIEW_ID.get(list_type)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def get_system_view_name(view_id: str | None) -> str | None:
|
|
62
|
+
if view_id is None:
|
|
63
|
+
return None
|
|
64
|
+
return SYSTEM_VIEW_ID_TO_NAME.get(view_id)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def get_task_type_label(type_value: int | None) -> str | None:
|
|
68
|
+
if type_value is None:
|
|
69
|
+
return None
|
|
70
|
+
return TASK_TYPE_LABELS.get(type_value)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def get_app_publish_status_label(status: int | None) -> str | None:
|
|
74
|
+
if status is None:
|
|
75
|
+
return None
|
|
76
|
+
return APP_PUBLISH_STATUS_LABELS.get(status)
|
|
@@ -0,0 +1,243 @@
|
|
|
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
|
+
cli_show_effective_context: bool = False
|
|
21
|
+
cli_context_write: bool = False
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def trim_key(self) -> str:
|
|
25
|
+
return tool_key(self.domain, self.tool_name)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def tool_key(domain: str, tool_name: str) -> str:
|
|
29
|
+
return f"{domain}:{tool_name}"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
USER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
|
|
33
|
+
PublicToolSpec(USER_DOMAIN, "auth_login", cli_route=("auth", "login"), mcp_public=False),
|
|
34
|
+
PublicToolSpec(USER_DOMAIN, "auth_use_credential", ("auth_use_credential",), ("auth", "use-credential")),
|
|
35
|
+
PublicToolSpec(USER_DOMAIN, "auth_whoami", ("auth_whoami",), ("auth", "whoami")),
|
|
36
|
+
PublicToolSpec(USER_DOMAIN, "auth_logout", ("auth_logout",), ("auth", "logout")),
|
|
37
|
+
PublicToolSpec(USER_DOMAIN, "workspace_list", ("workspace_list",), ("workspace", "list")),
|
|
38
|
+
PublicToolSpec(USER_DOMAIN, "workspace_get", ("workspace_get",), ("workspace", "get")),
|
|
39
|
+
PublicToolSpec(USER_DOMAIN, "workspace_select", ("workspace_select",), ("workspace", "select")),
|
|
40
|
+
PublicToolSpec(USER_DOMAIN, "app_list", ("app_list",), ("app", "list"), cli_show_effective_context=True),
|
|
41
|
+
PublicToolSpec(USER_DOMAIN, "app_search", ("app_search",), ("app", "search"), cli_show_effective_context=True),
|
|
42
|
+
PublicToolSpec(USER_DOMAIN, "app_get", ("app_get",), ("app", "get"), cli_show_effective_context=True),
|
|
43
|
+
PublicToolSpec(USER_DOMAIN, "portal_list", ("portal_list",), ("portal", "list"), cli_show_effective_context=True),
|
|
44
|
+
PublicToolSpec(USER_DOMAIN, "portal_get", ("portal_get",), ("portal", "get"), cli_show_effective_context=True),
|
|
45
|
+
PublicToolSpec(USER_DOMAIN, "view_get", ("view_get",), ("view", "get"), cli_show_effective_context=True),
|
|
46
|
+
PublicToolSpec(USER_DOMAIN, "chart_get", ("chart_get",), ("chart", "get"), cli_show_effective_context=True),
|
|
47
|
+
PublicToolSpec(USER_DOMAIN, "file_get_upload_info", ("file_get_upload_info",), cli_public=False),
|
|
48
|
+
PublicToolSpec(USER_DOMAIN, "file_upload_local", ("file_upload_local",), cli_public=False),
|
|
49
|
+
PublicToolSpec(USER_DOMAIN, "feedback_submit", ("feedback_submit",), cli_public=False),
|
|
50
|
+
PublicToolSpec(
|
|
51
|
+
USER_DOMAIN,
|
|
52
|
+
"record_schema_get",
|
|
53
|
+
cli_route=("record", "schema", "applicant"),
|
|
54
|
+
mcp_public=False,
|
|
55
|
+
),
|
|
56
|
+
PublicToolSpec(
|
|
57
|
+
USER_DOMAIN,
|
|
58
|
+
"record_browse_schema_get",
|
|
59
|
+
("record_browse_schema_get_public",),
|
|
60
|
+
("record", "schema", "browse"),
|
|
61
|
+
),
|
|
62
|
+
PublicToolSpec(
|
|
63
|
+
USER_DOMAIN,
|
|
64
|
+
"record_insert_schema_get",
|
|
65
|
+
("record_insert_schema_get_public",),
|
|
66
|
+
("record", "schema", "insert"),
|
|
67
|
+
),
|
|
68
|
+
PublicToolSpec(
|
|
69
|
+
USER_DOMAIN,
|
|
70
|
+
"record_update_schema_get",
|
|
71
|
+
("record_update_schema_get_public",),
|
|
72
|
+
("record", "schema", "update"),
|
|
73
|
+
),
|
|
74
|
+
PublicToolSpec(USER_DOMAIN, "record_import_schema_get", ("record_import_schema_get",), ("record", "schema", "import")),
|
|
75
|
+
PublicToolSpec(
|
|
76
|
+
USER_DOMAIN,
|
|
77
|
+
"record_code_block_schema_get",
|
|
78
|
+
("record_code_block_schema_get_public",),
|
|
79
|
+
("record", "schema", "code-block"),
|
|
80
|
+
),
|
|
81
|
+
PublicToolSpec(USER_DOMAIN, "record_member_candidates", ("record_member_candidates",), cli_public=False),
|
|
82
|
+
PublicToolSpec(USER_DOMAIN, "record_department_candidates", ("record_department_candidates",), cli_public=False),
|
|
83
|
+
PublicToolSpec(USER_DOMAIN, "record_analyze", ("record_analyze",), ("record", "analyze")),
|
|
84
|
+
PublicToolSpec(USER_DOMAIN, "record_list", ("record_list",), ("record", "list"), cli_show_effective_context=True),
|
|
85
|
+
PublicToolSpec(USER_DOMAIN, "record_get", ("record_get_public",), ("record", "get"), cli_show_effective_context=True),
|
|
86
|
+
PublicToolSpec(USER_DOMAIN, "record_insert", ("record_insert_public",), ("record", "insert"), cli_show_effective_context=True, cli_context_write=True),
|
|
87
|
+
PublicToolSpec(USER_DOMAIN, "record_update", ("record_update_public",), ("record", "update"), cli_show_effective_context=True, cli_context_write=True),
|
|
88
|
+
PublicToolSpec(USER_DOMAIN, "record_delete", ("record_delete_public",), ("record", "delete"), cli_show_effective_context=True, cli_context_write=True),
|
|
89
|
+
PublicToolSpec(USER_DOMAIN, "record_import_template_get", ("record_import_template_get",), ("import", "template")),
|
|
90
|
+
PublicToolSpec(USER_DOMAIN, "record_import_verify", ("record_import_verify",), ("import", "verify")),
|
|
91
|
+
PublicToolSpec(USER_DOMAIN, "record_import_repair_local", ("record_import_repair_local",), ("import", "repair")),
|
|
92
|
+
PublicToolSpec(USER_DOMAIN, "record_import_start", ("record_import_start",), ("import", "start")),
|
|
93
|
+
PublicToolSpec(USER_DOMAIN, "record_import_status_get", ("record_import_status_get",), ("import", "status")),
|
|
94
|
+
PublicToolSpec(USER_DOMAIN, "record_code_block_run", ("record_code_block_run",), ("record", "code-block-run"), cli_show_effective_context=True, cli_context_write=True),
|
|
95
|
+
PublicToolSpec(USER_DOMAIN, "task_list", ("task_list",), ("task", "list"), cli_show_effective_context=True),
|
|
96
|
+
PublicToolSpec(USER_DOMAIN, "task_get", ("task_get",), ("task", "get"), cli_show_effective_context=True),
|
|
97
|
+
PublicToolSpec(USER_DOMAIN, "task_action_execute", ("task_action_execute",), ("task", "action"), cli_show_effective_context=True, cli_context_write=True),
|
|
98
|
+
PublicToolSpec(
|
|
99
|
+
USER_DOMAIN,
|
|
100
|
+
"task_associated_report_detail_get",
|
|
101
|
+
("task_associated_report_detail_get",),
|
|
102
|
+
("task", "report"),
|
|
103
|
+
cli_show_effective_context=True,
|
|
104
|
+
),
|
|
105
|
+
PublicToolSpec(USER_DOMAIN, "task_workflow_log_get", ("task_workflow_log_get",), ("task", "log"), cli_show_effective_context=True),
|
|
106
|
+
PublicToolSpec(USER_DOMAIN, "directory_search", ("directory_search",), cli_public=False),
|
|
107
|
+
PublicToolSpec(USER_DOMAIN, "directory_list_internal_users", ("directory_list_internal_users",), cli_public=False),
|
|
108
|
+
PublicToolSpec(USER_DOMAIN, "directory_list_all_internal_users", ("directory_list_all_internal_users",), cli_public=False),
|
|
109
|
+
PublicToolSpec(USER_DOMAIN, "directory_list_internal_departments", ("directory_list_internal_departments",), cli_public=False),
|
|
110
|
+
PublicToolSpec(USER_DOMAIN, "directory_list_all_departments", ("directory_list_all_departments",), cli_public=False),
|
|
111
|
+
PublicToolSpec(USER_DOMAIN, "directory_list_sub_departments", ("directory_list_sub_departments",), cli_public=False),
|
|
112
|
+
PublicToolSpec(USER_DOMAIN, "directory_list_external_members", ("directory_list_external_members",), cli_public=False),
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
BUILDER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
|
|
117
|
+
PublicToolSpec(BUILDER_DOMAIN, "auth_use_credential", ("auth_use_credential",), ("builder", "auth", "use-credential"), cli_public=False),
|
|
118
|
+
PublicToolSpec(BUILDER_DOMAIN, "auth_whoami", ("auth_whoami",), ("builder", "auth", "whoami"), cli_public=False),
|
|
119
|
+
PublicToolSpec(BUILDER_DOMAIN, "auth_logout", ("auth_logout",), ("builder", "auth", "logout"), cli_public=False),
|
|
120
|
+
PublicToolSpec(BUILDER_DOMAIN, "workspace_list", ("workspace_list",), ("builder", "workspace", "list"), cli_public=False),
|
|
121
|
+
PublicToolSpec(BUILDER_DOMAIN, "workspace_get", ("workspace_get",), ("builder", "workspace", "get"), cli_public=False),
|
|
122
|
+
PublicToolSpec(BUILDER_DOMAIN, "file_upload_local", ("file_upload_local",), ("builder", "file", "upload-local"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
123
|
+
PublicToolSpec(BUILDER_DOMAIN, "feedback_submit", ("feedback_submit",), ("builder", "feedback", "submit"), has_contract=True),
|
|
124
|
+
PublicToolSpec(BUILDER_DOMAIN, "builder_tool_contract", ("builder_tool_contract",), ("builder", "contract"), has_contract=False),
|
|
125
|
+
PublicToolSpec(BUILDER_DOMAIN, "package_get", ("package_get",), ("builder", "package", "get"), has_contract=True, cli_show_effective_context=True),
|
|
126
|
+
PublicToolSpec(BUILDER_DOMAIN, "package_apply", ("package_apply",), ("builder", "package", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
127
|
+
PublicToolSpec(BUILDER_DOMAIN, "solution_install", ("solution_install",), ("builder", "solution", "install"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
128
|
+
PublicToolSpec(BUILDER_DOMAIN, "member_search", ("member_search",), ("builder", "member", "search"), has_contract=True, cli_show_effective_context=True),
|
|
129
|
+
PublicToolSpec(BUILDER_DOMAIN, "role_search", ("role_search",), ("builder", "role", "search"), has_contract=True, cli_show_effective_context=True),
|
|
130
|
+
PublicToolSpec(BUILDER_DOMAIN, "role_create", ("role_create",), ("builder", "role", "create"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
131
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_release_edit_lock_if_mine", ("app_release_edit_lock_if_mine",), ("builder", "app", "release-edit-lock-if-mine"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
132
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_resolve", ("app_resolve",), ("builder", "app", "resolve"), has_contract=True, cli_show_effective_context=True),
|
|
133
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_custom_button_list", ("app_custom_button_list",), ("builder", "button", "list"), has_contract=True, cli_show_effective_context=True),
|
|
134
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_custom_button_get", ("app_custom_button_get",), ("builder", "button", "get"), has_contract=True, cli_show_effective_context=True),
|
|
135
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_custom_button_create", ("app_custom_button_create",), ("builder", "button", "create"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
136
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_custom_button_update", ("app_custom_button_update",), ("builder", "button", "update"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
137
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_custom_button_delete", ("app_custom_button_delete",), ("builder", "button", "delete"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
138
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_get", ("app_get",), ("builder", "app", "get", "summary"), has_contract=True, cli_show_effective_context=True),
|
|
139
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_get_fields", ("app_get_fields",), ("builder", "app", "get", "fields"), has_contract=True, cli_show_effective_context=True),
|
|
140
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_repair_code_blocks", ("app_repair_code_blocks",), ("builder", "app", "repair-code-blocks"), has_contract=True, cli_show_effective_context=True),
|
|
141
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_get_layout", ("app_get_layout",), ("builder", "app", "get", "layout"), has_contract=True, cli_show_effective_context=True),
|
|
142
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_get_views", ("app_get_views",), ("builder", "app", "get", "views"), has_contract=True, cli_show_effective_context=True),
|
|
143
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_get_flow", ("app_get_flow",), ("builder", "app", "get", "flow"), has_contract=True, cli_show_effective_context=True),
|
|
144
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_get_charts", ("app_get_charts",), ("builder", "app", "get", "charts"), has_contract=True, cli_show_effective_context=True),
|
|
145
|
+
PublicToolSpec(BUILDER_DOMAIN, "portal_list", ("portal_list",), ("builder", "portal", "list"), has_contract=True, cli_show_effective_context=True),
|
|
146
|
+
PublicToolSpec(BUILDER_DOMAIN, "portal_get", ("portal_get",), ("builder", "portal", "get"), has_contract=True, cli_show_effective_context=True),
|
|
147
|
+
PublicToolSpec(BUILDER_DOMAIN, "view_get", ("view_get",), ("builder", "view", "get"), has_contract=True, cli_show_effective_context=True),
|
|
148
|
+
PublicToolSpec(BUILDER_DOMAIN, "chart_get", ("chart_get",), ("builder", "chart", "get"), has_contract=True, cli_show_effective_context=True),
|
|
149
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_schema_apply", ("app_schema_apply",), ("builder", "schema", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
150
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_layout_apply", ("app_layout_apply",), ("builder", "layout", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
151
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_flow_apply", ("app_flow_apply",), ("builder", "flow", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
152
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_views_apply", ("app_views_apply",), ("builder", "views", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
153
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_charts_apply", ("app_charts_apply",), ("builder", "charts", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
154
|
+
PublicToolSpec(BUILDER_DOMAIN, "portal_apply", ("portal_apply",), ("builder", "portal", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
155
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_publish_verify", ("app_publish_verify",), ("builder", "publish", "verify"), has_contract=True, cli_show_effective_context=True),
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
ALL_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = USER_PUBLIC_TOOL_SPECS + BUILDER_PUBLIC_TOOL_SPECS
|
|
160
|
+
PUBLIC_TOOL_BY_KEY: dict[str, PublicToolSpec] = {spec.trim_key: spec for spec in ALL_PUBLIC_TOOL_SPECS}
|
|
161
|
+
PUBLIC_TOOL_BY_CLI_ROUTE: dict[tuple[str, ...], PublicToolSpec] = {
|
|
162
|
+
spec.cli_route: spec
|
|
163
|
+
for spec in ALL_PUBLIC_TOOL_SPECS
|
|
164
|
+
if spec.cli_public and spec.cli_route is not None
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def public_tool_specs(domain: str | None = None) -> tuple[PublicToolSpec, ...]:
|
|
169
|
+
if domain is None:
|
|
170
|
+
return ALL_PUBLIC_TOOL_SPECS
|
|
171
|
+
return tuple(spec for spec in ALL_PUBLIC_TOOL_SPECS if spec.domain == domain)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def public_mcp_tool_names(domain: str) -> set[str]:
|
|
175
|
+
return {spec.tool_name for spec in public_tool_specs(domain) if spec.mcp_public}
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def public_builder_contract_tool_names() -> list[str]:
|
|
179
|
+
return sorted(
|
|
180
|
+
spec.tool_name
|
|
181
|
+
for spec in BUILDER_PUBLIC_TOOL_SPECS
|
|
182
|
+
if spec.has_contract and (spec.mcp_public or spec.cli_public)
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def server_method_map(domain: str) -> dict[str, str]:
|
|
187
|
+
mapping: dict[str, str] = {}
|
|
188
|
+
for spec in public_tool_specs(domain):
|
|
189
|
+
if not spec.mcp_public:
|
|
190
|
+
continue
|
|
191
|
+
for method_name in spec.method_names:
|
|
192
|
+
mapping[method_name] = spec.trim_key
|
|
193
|
+
return mapping
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def cli_trim_key_from_namespace(args: Namespace) -> str | None:
|
|
197
|
+
route = cli_route_from_namespace(args)
|
|
198
|
+
if route is None:
|
|
199
|
+
return None
|
|
200
|
+
spec = PUBLIC_TOOL_BY_CLI_ROUTE.get(route)
|
|
201
|
+
return spec.trim_key if spec is not None else None
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def cli_public_tool_spec_from_namespace(args: Namespace) -> PublicToolSpec | None:
|
|
205
|
+
route = cli_route_from_namespace(args)
|
|
206
|
+
if route is None:
|
|
207
|
+
return None
|
|
208
|
+
return PUBLIC_TOOL_BY_CLI_ROUTE.get(route)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def cli_route_from_namespace(args: Namespace) -> tuple[str, ...] | None:
|
|
212
|
+
command = getattr(args, "command", None)
|
|
213
|
+
if not isinstance(command, str) or not command:
|
|
214
|
+
return None
|
|
215
|
+
command = "builder" if command == "build" else command
|
|
216
|
+
if command == "record":
|
|
217
|
+
record_command = getattr(args, "record_command", None)
|
|
218
|
+
if record_command == "schema":
|
|
219
|
+
schema_command = getattr(args, "record_schema_command", None)
|
|
220
|
+
return (command, "schema", schema_command) if isinstance(schema_command, str) and schema_command else None
|
|
221
|
+
return (command, record_command) if isinstance(record_command, str) and record_command else None
|
|
222
|
+
if command == "task":
|
|
223
|
+
task_command = getattr(args, "task_command", None)
|
|
224
|
+
return (command, task_command) if isinstance(task_command, str) and task_command else None
|
|
225
|
+
if command != "builder":
|
|
226
|
+
child_attr = f"{command}_command"
|
|
227
|
+
child = getattr(args, child_attr, None)
|
|
228
|
+
return (command, child) if isinstance(child, str) and child else None
|
|
229
|
+
|
|
230
|
+
builder_command = getattr(args, "builder_command", None)
|
|
231
|
+
if not isinstance(builder_command, str) or not builder_command:
|
|
232
|
+
return None
|
|
233
|
+
if builder_command == "contract":
|
|
234
|
+
return ("builder", "contract")
|
|
235
|
+
if builder_command == "app":
|
|
236
|
+
app_command = getattr(args, "builder_app_command", None)
|
|
237
|
+
if app_command == "get":
|
|
238
|
+
section = getattr(args, "builder_app_get_section", "summary") or "summary"
|
|
239
|
+
return ("builder", "app", "get", str(section))
|
|
240
|
+
return ("builder", "app", app_command) if isinstance(app_command, str) and app_command else None
|
|
241
|
+
child_attr = f"builder_{builder_command.replace('-', '_')}_command"
|
|
242
|
+
child = getattr(args, child_attr, None)
|
|
243
|
+
return ("builder", builder_command, child) if isinstance(child, str) and child else None
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from datetime import datetime, timezone
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from .config import get_repository_metadata_dir
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _utc_now() -> str:
|
|
13
|
+
return datetime.now(timezone.utc).isoformat()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _safe_key(value: str) -> str:
|
|
17
|
+
keep: list[str] = []
|
|
18
|
+
for char in value:
|
|
19
|
+
if char.isalnum() or char in {"-", "_"}:
|
|
20
|
+
keep.append(char)
|
|
21
|
+
else:
|
|
22
|
+
keep.append("_")
|
|
23
|
+
normalized = "".join(keep).strip("_")
|
|
24
|
+
return normalized or "repository"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(slots=True)
|
|
28
|
+
class RepositoryMetadataStore:
|
|
29
|
+
base_dir: Path | None = None
|
|
30
|
+
_dir: Path = field(init=False, repr=False)
|
|
31
|
+
|
|
32
|
+
def __post_init__(self) -> None:
|
|
33
|
+
self._dir = self.base_dir or get_repository_metadata_dir()
|
|
34
|
+
self._dir.mkdir(parents=True, exist_ok=True)
|
|
35
|
+
|
|
36
|
+
def put(self, repo_name: str, payload: dict[str, Any]) -> dict[str, Any]:
|
|
37
|
+
now = _utc_now()
|
|
38
|
+
existing = self.get(repo_name) or {}
|
|
39
|
+
data = dict(existing)
|
|
40
|
+
data.update(payload)
|
|
41
|
+
data["repo_name"] = repo_name
|
|
42
|
+
data["created_at"] = existing.get("created_at") or now
|
|
43
|
+
data["updated_at"] = now
|
|
44
|
+
self._path(repo_name).write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
45
|
+
return data
|
|
46
|
+
|
|
47
|
+
def get(self, repo_name: str) -> dict[str, Any] | None:
|
|
48
|
+
path = self._path(repo_name)
|
|
49
|
+
if not path.exists():
|
|
50
|
+
return None
|
|
51
|
+
try:
|
|
52
|
+
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
53
|
+
except (OSError, json.JSONDecodeError):
|
|
54
|
+
path.unlink(missing_ok=True)
|
|
55
|
+
return None
|
|
56
|
+
return payload if isinstance(payload, dict) else None
|
|
57
|
+
|
|
58
|
+
def list(self) -> list[dict[str, Any]]:
|
|
59
|
+
entries: list[dict[str, Any]] = []
|
|
60
|
+
for path in sorted(self._dir.glob("*.json")):
|
|
61
|
+
try:
|
|
62
|
+
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
63
|
+
except (OSError, json.JSONDecodeError):
|
|
64
|
+
continue
|
|
65
|
+
if isinstance(payload, dict):
|
|
66
|
+
entries.append(payload)
|
|
67
|
+
entries.sort(key=lambda item: str(item.get("updated_at") or ""), reverse=True)
|
|
68
|
+
return entries
|
|
69
|
+
|
|
70
|
+
def _path(self, repo_name: str) -> Path:
|
|
71
|
+
return self._dir / f"{_safe_key(repo_name)}.json"
|