@qingflow-tech/qingflow-app-user-mcp 1.0.2 → 1.0.4
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/docs/local-agent-install.md +9 -3
- package/npm/lib/runtime.mjs +10 -3
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/skills/qingflow-app-user/SKILL.md +21 -12
- package/skills/qingflow-app-user/references/data-gotchas.md +1 -1
- package/skills/qingflow-app-user/references/public-surface-sync.md +70 -0
- package/skills/qingflow-app-user/references/record-patterns.md +1 -1
- package/skills/qingflow-record-analysis/SKILL.md +44 -2
- package/skills/qingflow-record-insert/SKILL.md +3 -0
- package/skills/qingflow-record-update/SKILL.md +3 -0
- package/skills/qingflow-task-ops/SKILL.md +31 -10
- package/src/qingflow_mcp/__init__.py +33 -1
- package/src/qingflow_mcp/backend_client.py +109 -0
- package/src/qingflow_mcp/builder_facade/button_style_catalog.py +282 -0
- package/src/qingflow_mcp/builder_facade/models.py +58 -9
- package/src/qingflow_mcp/builder_facade/service.py +1711 -240
- package/src/qingflow_mcp/cli/commands/__init__.py +2 -1
- package/src/qingflow_mcp/cli/commands/app.py +47 -1
- package/src/qingflow_mcp/cli/commands/auth.py +63 -0
- package/src/qingflow_mcp/cli/commands/builder.py +11 -3
- package/src/qingflow_mcp/cli/commands/exports.py +111 -0
- package/src/qingflow_mcp/cli/commands/record.py +5 -5
- package/src/qingflow_mcp/cli/commands/task.py +701 -27
- package/src/qingflow_mcp/cli/commands/workspace.py +84 -0
- package/src/qingflow_mcp/cli/context.py +3 -0
- package/src/qingflow_mcp/cli/formatters.py +424 -50
- package/src/qingflow_mcp/cli/interaction.py +72 -0
- package/src/qingflow_mcp/cli/main.py +11 -1
- package/src/qingflow_mcp/cli/qingflow_login.py +116 -0
- package/src/qingflow_mcp/cli/terminal_ui.py +218 -0
- package/src/qingflow_mcp/config.py +1 -1
- package/src/qingflow_mcp/errors.py +4 -4
- package/src/qingflow_mcp/export_store.py +14 -0
- package/src/qingflow_mcp/id_utils.py +49 -0
- package/src/qingflow_mcp/public_surface.py +16 -1
- package/src/qingflow_mcp/response_trim.py +394 -9
- package/src/qingflow_mcp/server.py +26 -0
- package/src/qingflow_mcp/server_app_builder.py +15 -1
- package/src/qingflow_mcp/server_app_user.py +113 -0
- package/src/qingflow_mcp/session_store.py +126 -21
- package/src/qingflow_mcp/solution/compiler/form_compiler.py +2 -2
- package/src/qingflow_mcp/solution/executor.py +2 -2
- package/src/qingflow_mcp/tools/ai_builder_tools.py +107 -34
- package/src/qingflow_mcp/tools/app_tools.py +1 -0
- package/src/qingflow_mcp/tools/auth_tools.py +243 -9
- package/src/qingflow_mcp/tools/base.py +6 -2
- package/src/qingflow_mcp/tools/code_block_tools.py +2 -2
- package/src/qingflow_mcp/tools/custom_button_tools.py +0 -2
- package/src/qingflow_mcp/tools/export_tools.py +1565 -0
- package/src/qingflow_mcp/tools/import_tools.py +78 -4
- package/src/qingflow_mcp/tools/record_tools.py +551 -165
- package/src/qingflow_mcp/tools/resource_read_tools.py +154 -33
- package/src/qingflow_mcp/tools/task_context_tools.py +917 -141
- package/src/qingflow_mcp/tools/workspace_tools.py +141 -0
|
@@ -3,6 +3,9 @@ from __future__ import annotations
|
|
|
3
3
|
import argparse
|
|
4
4
|
|
|
5
5
|
from ..context import CliContext
|
|
6
|
+
from ..interaction import cancelled_result, resolve_interactive_selection
|
|
7
|
+
from ..terminal_ui import SelectionOption
|
|
8
|
+
from .common import raise_config_error
|
|
6
9
|
|
|
7
10
|
|
|
8
11
|
def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
|
|
@@ -15,6 +18,14 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
15
18
|
list_parser.add_argument("--include-external", action="store_true")
|
|
16
19
|
list_parser.set_defaults(handler=_handle_list, format_hint="workspace_list")
|
|
17
20
|
|
|
21
|
+
get_parser = workspace_subparsers.add_parser("get", help="读取工作区详情")
|
|
22
|
+
get_parser.add_argument("--ws-id", type=int, default=0)
|
|
23
|
+
get_parser.set_defaults(handler=_handle_get, format_hint="workspace_get")
|
|
24
|
+
|
|
25
|
+
select_parser = workspace_subparsers.add_parser("select", help="切换当前工作区")
|
|
26
|
+
select_parser.add_argument("--ws-id", type=int, default=0, help="不传时在交互终端中选择工作区")
|
|
27
|
+
select_parser.set_defaults(handler=_handle_select, format_hint="workspace_select")
|
|
28
|
+
|
|
18
29
|
|
|
19
30
|
def _handle_list(args: argparse.Namespace, context: CliContext) -> dict:
|
|
20
31
|
return context.workspace.workspace_list(
|
|
@@ -23,3 +34,76 @@ def _handle_list(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
23
34
|
page_size=args.page_size,
|
|
24
35
|
include_external=bool(args.include_external),
|
|
25
36
|
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _handle_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
40
|
+
return context.workspace.workspace_get(
|
|
41
|
+
profile=args.profile,
|
|
42
|
+
ws_id=args.ws_id if int(args.ws_id or 0) > 0 else None,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _handle_select(args: argparse.Namespace, context: CliContext) -> dict:
|
|
47
|
+
if int(args.ws_id or 0) <= 0:
|
|
48
|
+
selection = _choose_workspace_interactively(args, context)
|
|
49
|
+
if selection.status == "unavailable":
|
|
50
|
+
raise_config_error(
|
|
51
|
+
"workspace select requires --ws-id, or an interactive terminal to choose a workspace",
|
|
52
|
+
fix_hint="Retry in an interactive terminal, or pass `--ws-id WS_ID` explicitly.",
|
|
53
|
+
)
|
|
54
|
+
if selection.status == "empty":
|
|
55
|
+
raise_config_error(
|
|
56
|
+
selection.message or "workspace select could not open a selector because no workspaces are available.",
|
|
57
|
+
fix_hint="Run `workspace list` to confirm visible workspaces, or retry with `--ws-id WS_ID`.",
|
|
58
|
+
)
|
|
59
|
+
if selection.status == "cancelled":
|
|
60
|
+
return cancelled_result(selection.message or "已取消")
|
|
61
|
+
args.ws_id = int(selection.value or 0)
|
|
62
|
+
return context.workspace.workspace_select(
|
|
63
|
+
profile=args.profile,
|
|
64
|
+
ws_id=int(args.ws_id),
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _choose_workspace_interactively(args: argparse.Namespace, context: CliContext):
|
|
69
|
+
current_ws_id = None
|
|
70
|
+
sessions = getattr(context, "sessions", None)
|
|
71
|
+
if sessions is not None and hasattr(sessions, "get_profile"):
|
|
72
|
+
try:
|
|
73
|
+
session_profile = sessions.get_profile(args.profile)
|
|
74
|
+
except Exception:
|
|
75
|
+
session_profile = None
|
|
76
|
+
current_ws_id = getattr(session_profile, "selected_ws_id", None) if session_profile is not None else None
|
|
77
|
+
def load_options() -> list[SelectionOption[int]]:
|
|
78
|
+
page = context.workspace.workspace_list(
|
|
79
|
+
profile=args.profile,
|
|
80
|
+
page_num=1,
|
|
81
|
+
page_size=100,
|
|
82
|
+
include_external=False,
|
|
83
|
+
).get("page")
|
|
84
|
+
items = page.get("list") if isinstance(page, dict) and isinstance(page.get("list"), list) else []
|
|
85
|
+
options: list[SelectionOption[int]] = []
|
|
86
|
+
for item in items:
|
|
87
|
+
if not isinstance(item, dict):
|
|
88
|
+
continue
|
|
89
|
+
ws_id = int(item.get("wsId") or 0)
|
|
90
|
+
if ws_id <= 0:
|
|
91
|
+
continue
|
|
92
|
+
workspace_name = str(item.get("workspaceName") or item.get("wsName") or f"Workspace {ws_id}")
|
|
93
|
+
remark = str(item.get("remark") or "").strip()
|
|
94
|
+
label = workspace_name
|
|
95
|
+
if remark:
|
|
96
|
+
label = f"{workspace_name} - {remark}"
|
|
97
|
+
hint = f"ws_id={ws_id}"
|
|
98
|
+
if current_ws_id == ws_id:
|
|
99
|
+
hint += " · 当前"
|
|
100
|
+
options.append(SelectionOption(value=ws_id, label=label, hint=hint))
|
|
101
|
+
return options
|
|
102
|
+
|
|
103
|
+
return resolve_interactive_selection(
|
|
104
|
+
args,
|
|
105
|
+
title="选择工作区",
|
|
106
|
+
unavailable_message="workspace select requires --ws-id, or an interactive terminal to choose a workspace",
|
|
107
|
+
empty_message="workspace select could not open a selector because no visible workspaces were returned.",
|
|
108
|
+
load_options=load_options,
|
|
109
|
+
)
|
|
@@ -8,6 +8,7 @@ from ..tools.ai_builder_tools import AiBuilderTools
|
|
|
8
8
|
from ..tools.app_tools import AppTools
|
|
9
9
|
from ..tools.auth_tools import AuthTools
|
|
10
10
|
from ..tools.code_block_tools import CodeBlockTools
|
|
11
|
+
from ..tools.export_tools import ExportTools
|
|
11
12
|
from ..tools.feedback_tools import FeedbackTools
|
|
12
13
|
from ..tools.file_tools import FileTools
|
|
13
14
|
from ..tools.import_tools import ImportTools
|
|
@@ -29,6 +30,7 @@ class CliContext:
|
|
|
29
30
|
record: RecordTools
|
|
30
31
|
code_block: CodeBlockTools
|
|
31
32
|
imports: ImportTools
|
|
33
|
+
exports: ExportTools
|
|
32
34
|
task: TaskContextTools
|
|
33
35
|
files: FileTools
|
|
34
36
|
builder_feedback: FeedbackTools
|
|
@@ -52,6 +54,7 @@ def build_cli_context() -> CliContext:
|
|
|
52
54
|
record=RecordTools(sessions, backend),
|
|
53
55
|
code_block=CodeBlockTools(sessions, backend),
|
|
54
56
|
imports=ImportTools(sessions, backend),
|
|
57
|
+
exports=ExportTools(sessions, backend),
|
|
55
58
|
task=TaskContextTools(sessions, backend),
|
|
56
59
|
files=FileTools(sessions, backend),
|
|
57
60
|
builder_feedback=FeedbackTools(backend, mcp_side="App Builder MCP"),
|