@josephyan/qingflow-cli 0.2.0-beta.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +30 -0
  2. package/docs/local-agent-install.md +235 -0
  3. package/entry_point.py +13 -0
  4. package/npm/bin/qingflow.mjs +5 -0
  5. package/npm/lib/runtime.mjs +204 -0
  6. package/npm/scripts/postinstall.mjs +16 -0
  7. package/package.json +34 -0
  8. package/pyproject.toml +67 -0
  9. package/qingflow +15 -0
  10. package/src/qingflow_mcp/__init__.py +5 -0
  11. package/src/qingflow_mcp/__main__.py +5 -0
  12. package/src/qingflow_mcp/backend_client.py +547 -0
  13. package/src/qingflow_mcp/builder_facade/__init__.py +3 -0
  14. package/src/qingflow_mcp/builder_facade/models.py +985 -0
  15. package/src/qingflow_mcp/builder_facade/service.py +8243 -0
  16. package/src/qingflow_mcp/cli/__init__.py +1 -0
  17. package/src/qingflow_mcp/cli/commands/__init__.py +15 -0
  18. package/src/qingflow_mcp/cli/commands/app.py +40 -0
  19. package/src/qingflow_mcp/cli/commands/auth.py +78 -0
  20. package/src/qingflow_mcp/cli/commands/builder.py +184 -0
  21. package/src/qingflow_mcp/cli/commands/common.py +47 -0
  22. package/src/qingflow_mcp/cli/commands/imports.py +86 -0
  23. package/src/qingflow_mcp/cli/commands/record.py +202 -0
  24. package/src/qingflow_mcp/cli/commands/task.py +87 -0
  25. package/src/qingflow_mcp/cli/commands/workspace.py +33 -0
  26. package/src/qingflow_mcp/cli/context.py +48 -0
  27. package/src/qingflow_mcp/cli/formatters.py +269 -0
  28. package/src/qingflow_mcp/cli/json_io.py +50 -0
  29. package/src/qingflow_mcp/cli/main.py +147 -0
  30. package/src/qingflow_mcp/config.py +221 -0
  31. package/src/qingflow_mcp/errors.py +66 -0
  32. package/src/qingflow_mcp/import_store.py +121 -0
  33. package/src/qingflow_mcp/json_types.py +18 -0
  34. package/src/qingflow_mcp/list_type_labels.py +76 -0
  35. package/src/qingflow_mcp/server.py +211 -0
  36. package/src/qingflow_mcp/server_app_builder.py +387 -0
  37. package/src/qingflow_mcp/server_app_user.py +317 -0
  38. package/src/qingflow_mcp/session_store.py +289 -0
  39. package/src/qingflow_mcp/solution/__init__.py +6 -0
  40. package/src/qingflow_mcp/solution/build_assembly_store.py +181 -0
  41. package/src/qingflow_mcp/solution/compiler/__init__.py +282 -0
  42. package/src/qingflow_mcp/solution/compiler/chart_compiler.py +96 -0
  43. package/src/qingflow_mcp/solution/compiler/form_compiler.py +466 -0
  44. package/src/qingflow_mcp/solution/compiler/icon_utils.py +113 -0
  45. package/src/qingflow_mcp/solution/compiler/navigation_compiler.py +57 -0
  46. package/src/qingflow_mcp/solution/compiler/package_compiler.py +19 -0
  47. package/src/qingflow_mcp/solution/compiler/portal_compiler.py +60 -0
  48. package/src/qingflow_mcp/solution/compiler/view_compiler.py +51 -0
  49. package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +173 -0
  50. package/src/qingflow_mcp/solution/design_session.py +222 -0
  51. package/src/qingflow_mcp/solution/design_store.py +100 -0
  52. package/src/qingflow_mcp/solution/executor.py +2339 -0
  53. package/src/qingflow_mcp/solution/normalizer.py +23 -0
  54. package/src/qingflow_mcp/solution/requirements_builder.py +536 -0
  55. package/src/qingflow_mcp/solution/run_store.py +244 -0
  56. package/src/qingflow_mcp/solution/spec_models.py +853 -0
  57. package/src/qingflow_mcp/tools/__init__.py +1 -0
  58. package/src/qingflow_mcp/tools/ai_builder_tools.py +2063 -0
  59. package/src/qingflow_mcp/tools/app_tools.py +850 -0
  60. package/src/qingflow_mcp/tools/approval_tools.py +833 -0
  61. package/src/qingflow_mcp/tools/auth_tools.py +697 -0
  62. package/src/qingflow_mcp/tools/base.py +81 -0
  63. package/src/qingflow_mcp/tools/code_block_tools.py +679 -0
  64. package/src/qingflow_mcp/tools/directory_tools.py +648 -0
  65. package/src/qingflow_mcp/tools/feedback_tools.py +230 -0
  66. package/src/qingflow_mcp/tools/file_tools.py +385 -0
  67. package/src/qingflow_mcp/tools/import_tools.py +1971 -0
  68. package/src/qingflow_mcp/tools/navigation_tools.py +177 -0
  69. package/src/qingflow_mcp/tools/package_tools.py +240 -0
  70. package/src/qingflow_mcp/tools/portal_tools.py +131 -0
  71. package/src/qingflow_mcp/tools/qingbi_report_tools.py +269 -0
  72. package/src/qingflow_mcp/tools/record_tools.py +12739 -0
  73. package/src/qingflow_mcp/tools/role_tools.py +94 -0
  74. package/src/qingflow_mcp/tools/solution_tools.py +3887 -0
  75. package/src/qingflow_mcp/tools/task_context_tools.py +1423 -0
  76. package/src/qingflow_mcp/tools/task_tools.py +843 -0
  77. package/src/qingflow_mcp/tools/view_tools.py +280 -0
  78. package/src/qingflow_mcp/tools/workflow_tools.py +312 -0
  79. package/src/qingflow_mcp/tools/workspace_tools.py +219 -0
@@ -0,0 +1,81 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Callable, TypeVar
4
+
5
+ from ..backend_client import BackendRequestContext, BackendClient
6
+ from ..errors import QingflowApiError, raise_tool_error
7
+ from ..json_types import JSONObject
8
+ from ..session_store import BackendSession, SessionProfile, SessionStore
9
+
10
+
11
+ T = TypeVar("T")
12
+
13
+
14
+ class ToolBase:
15
+ def __init__(self, sessions: SessionStore, backend: BackendClient) -> None:
16
+ self.sessions = sessions
17
+ self.backend = backend
18
+
19
+ def _require_context(self, profile: str, *, require_workspace: bool) -> tuple[SessionProfile, BackendSession, BackendRequestContext]:
20
+ session_profile = self.sessions.get_profile(profile)
21
+ if session_profile is None:
22
+ raise QingflowApiError.auth_required(profile)
23
+ backend_session = self.sessions.get_backend_session(profile)
24
+ if backend_session is None:
25
+ raise QingflowApiError.auth_required(profile)
26
+ if require_workspace and session_profile.selected_ws_id is None:
27
+ raise QingflowApiError.workspace_not_selected(profile)
28
+ context = BackendRequestContext(
29
+ base_url=backend_session.base_url,
30
+ token=backend_session.token,
31
+ ws_id=session_profile.selected_ws_id if require_workspace else None,
32
+ qf_version=backend_session.qf_version,
33
+ qf_version_source=backend_session.qf_version_source,
34
+ )
35
+ return session_profile, backend_session, context
36
+
37
+ def _run(self, profile: str, func: Callable[[SessionProfile, BackendRequestContext], T], *, require_workspace: bool = True) -> T:
38
+ try:
39
+ session_profile, _, context = self._require_context(profile, require_workspace=require_workspace)
40
+ return func(session_profile, context)
41
+ except QingflowApiError as error:
42
+ self._handle_error(profile, error)
43
+ raise AssertionError("unreachable")
44
+
45
+ def _handle_error(self, profile: str, error: QingflowApiError) -> None:
46
+ if error.looks_like_invalid_token():
47
+ self.sessions.invalidate(profile)
48
+ error = QingflowApiError(
49
+ category="auth",
50
+ message=f"Qingflow session for profile '{profile}' has expired. Run auth_login again.",
51
+ backend_code=error.backend_code,
52
+ request_id=error.request_id,
53
+ http_status=error.http_status,
54
+ )
55
+ raise_tool_error(error)
56
+
57
+ def _require_dict(self, payload: JSONObject | None, field_name: str = "payload") -> JSONObject:
58
+ if not isinstance(payload, dict) or not payload:
59
+ raise_tool_error(QingflowApiError.config_error(f"{field_name} must be a non-empty object"))
60
+ return payload
61
+
62
+ def _high_risk_tool_description(self, *, operation: str, target: str) -> str:
63
+ return (
64
+ f"High-risk {operation} operation for {target}. Read the current state first, "
65
+ "confirm the exact target IDs and intended diff with a human, and avoid running "
66
+ "against production without explicit approval."
67
+ )
68
+
69
+ def _attach_human_review_notice(self, response: JSONObject, *, operation: str, target: str) -> JSONObject:
70
+ payload = dict(response)
71
+ payload["requires_human_review"] = True
72
+ payload["risk_notice"] = {
73
+ "operation": operation,
74
+ "target": target,
75
+ "severity": "high",
76
+ "guidance": (
77
+ "Read the current state first, confirm the exact target IDs and intended diff with a human, "
78
+ "and require explicit approval before running against production."
79
+ ),
80
+ }
81
+ return payload