@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.
- package/README.md +30 -0
- package/docs/local-agent-install.md +235 -0
- package/entry_point.py +13 -0
- package/npm/bin/qingflow.mjs +5 -0
- package/npm/lib/runtime.mjs +204 -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 +5 -0
- package/src/qingflow_mcp/__main__.py +5 -0
- package/src/qingflow_mcp/backend_client.py +547 -0
- package/src/qingflow_mcp/builder_facade/__init__.py +3 -0
- package/src/qingflow_mcp/builder_facade/models.py +985 -0
- package/src/qingflow_mcp/builder_facade/service.py +8243 -0
- package/src/qingflow_mcp/cli/__init__.py +1 -0
- package/src/qingflow_mcp/cli/commands/__init__.py +15 -0
- package/src/qingflow_mcp/cli/commands/app.py +40 -0
- package/src/qingflow_mcp/cli/commands/auth.py +78 -0
- package/src/qingflow_mcp/cli/commands/builder.py +184 -0
- package/src/qingflow_mcp/cli/commands/common.py +47 -0
- package/src/qingflow_mcp/cli/commands/imports.py +86 -0
- package/src/qingflow_mcp/cli/commands/record.py +202 -0
- package/src/qingflow_mcp/cli/commands/task.py +87 -0
- package/src/qingflow_mcp/cli/commands/workspace.py +33 -0
- package/src/qingflow_mcp/cli/context.py +48 -0
- package/src/qingflow_mcp/cli/formatters.py +269 -0
- package/src/qingflow_mcp/cli/json_io.py +50 -0
- package/src/qingflow_mcp/cli/main.py +147 -0
- package/src/qingflow_mcp/config.py +221 -0
- package/src/qingflow_mcp/errors.py +66 -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/server.py +211 -0
- package/src/qingflow_mcp/server_app_builder.py +387 -0
- package/src/qingflow_mcp/server_app_user.py +317 -0
- package/src/qingflow_mcp/session_store.py +289 -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 +466 -0
- package/src/qingflow_mcp/solution/compiler/icon_utils.py +113 -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 +2339 -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 +853 -0
- package/src/qingflow_mcp/tools/__init__.py +1 -0
- package/src/qingflow_mcp/tools/ai_builder_tools.py +2063 -0
- package/src/qingflow_mcp/tools/app_tools.py +850 -0
- package/src/qingflow_mcp/tools/approval_tools.py +833 -0
- package/src/qingflow_mcp/tools/auth_tools.py +697 -0
- package/src/qingflow_mcp/tools/base.py +81 -0
- package/src/qingflow_mcp/tools/code_block_tools.py +679 -0
- package/src/qingflow_mcp/tools/directory_tools.py +648 -0
- package/src/qingflow_mcp/tools/feedback_tools.py +230 -0
- package/src/qingflow_mcp/tools/file_tools.py +385 -0
- package/src/qingflow_mcp/tools/import_tools.py +1971 -0
- package/src/qingflow_mcp/tools/navigation_tools.py +177 -0
- package/src/qingflow_mcp/tools/package_tools.py +240 -0
- package/src/qingflow_mcp/tools/portal_tools.py +131 -0
- package/src/qingflow_mcp/tools/qingbi_report_tools.py +269 -0
- package/src/qingflow_mcp/tools/record_tools.py +12739 -0
- package/src/qingflow_mcp/tools/role_tools.py +94 -0
- package/src/qingflow_mcp/tools/solution_tools.py +3887 -0
- package/src/qingflow_mcp/tools/task_context_tools.py +1423 -0
- package/src/qingflow_mcp/tools/task_tools.py +843 -0
- package/src/qingflow_mcp/tools/view_tools.py +280 -0
- package/src/qingflow_mcp/tools/workflow_tools.py +312 -0
- package/src/qingflow_mcp/tools/workspace_tools.py +219 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import date
|
|
4
|
+
|
|
5
|
+
from mcp.server.fastmcp import FastMCP
|
|
6
|
+
|
|
7
|
+
from .backend_client import BackendClient
|
|
8
|
+
from .session_store import SessionStore
|
|
9
|
+
from .tools.app_tools import AppTools
|
|
10
|
+
from .tools.auth_tools import AuthTools
|
|
11
|
+
from .tools.code_block_tools import CodeBlockTools
|
|
12
|
+
from .tools.feedback_tools import FeedbackTools
|
|
13
|
+
from .tools.file_tools import FileTools
|
|
14
|
+
from .tools.import_tools import ImportTools
|
|
15
|
+
from .tools.package_tools import PackageTools
|
|
16
|
+
from .tools.navigation_tools import NavigationTools
|
|
17
|
+
from .tools.directory_tools import DirectoryTools
|
|
18
|
+
from .tools.portal_tools import PortalTools
|
|
19
|
+
from .tools.qingbi_report_tools import QingbiReportTools
|
|
20
|
+
from .tools.role_tools import RoleTools
|
|
21
|
+
from .tools.solution_tools import SolutionTools
|
|
22
|
+
from .tools.task_context_tools import TaskContextTools
|
|
23
|
+
from .tools.view_tools import ViewTools
|
|
24
|
+
from .tools.workflow_tools import WorkflowTools
|
|
25
|
+
from .tools.workspace_tools import WorkspaceTools
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def build_server() -> FastMCP:
|
|
29
|
+
today = date.today()
|
|
30
|
+
current_year = today.year
|
|
31
|
+
server = FastMCP(
|
|
32
|
+
"Qingflow MCP",
|
|
33
|
+
instructions=f"""Use this server for Qingflow operational workflows. Current date: `{today.isoformat()}`.
|
|
34
|
+
|
|
35
|
+
## Authentication
|
|
36
|
+
|
|
37
|
+
Use `auth_login` first, then `workspace_list` and `workspace_select`.
|
|
38
|
+
All resource tools operate with the logged-in user's Qingflow permissions.
|
|
39
|
+
|
|
40
|
+
## Shared Helper
|
|
41
|
+
|
|
42
|
+
`feedback_submit` is always available as a cross-cutting helper.
|
|
43
|
+
|
|
44
|
+
- Use it when the current MCP capability is unsupported, awkward, or still cannot satisfy the user's need after reasonable use.
|
|
45
|
+
- It does not require Qingflow login or workspace selection.
|
|
46
|
+
- Call it only after the user explicitly confirms submission.
|
|
47
|
+
|
|
48
|
+
## App Discovery
|
|
49
|
+
|
|
50
|
+
If `app_key` is unknown, use `app_list` or `app_search` first.
|
|
51
|
+
If the app is known but the data range is not, use `app_get` first and choose from `accessible_views`.
|
|
52
|
+
If an accessible view has `analysis_supported=false`, do not use it for `record_list` or `record_analyze`. `boardView` and `ganttView` are special UI views, not list/analyze targets.
|
|
53
|
+
|
|
54
|
+
## Schema-First Rule
|
|
55
|
+
|
|
56
|
+
Call `record_insert_schema_get` before `record_insert`.
|
|
57
|
+
Call `record_update_schema_get` before `record_update`.
|
|
58
|
+
Call `record_code_block_schema_get` before `record_code_block_run`.
|
|
59
|
+
Call `app_get` first when the data range is unclear, then use `record_browse_schema_get(view_id=...)` before `record_list`, `record_get`, or `record_analyze`.
|
|
60
|
+
Call `record_import_schema_get` when the import field mapping is unclear before template download or verify.
|
|
61
|
+
|
|
62
|
+
- All `field_id` values must come from the schema response.
|
|
63
|
+
- Never guess field names or ids.
|
|
64
|
+
|
|
65
|
+
## Schema Scope
|
|
66
|
+
|
|
67
|
+
`record_insert_schema_get` returns the current user's insert-ready applicant schema; read `required_fields`, `optional_fields`, `runtime_linked_required_fields`, and `payload_template`.
|
|
68
|
+
`record_update_schema_get` returns the current record's overall update-ready writable field set across matched accessible views; read `writable_fields` and `payload_template`.
|
|
69
|
+
`record_browse_schema_get(view_id=...)` returns browse-schema fields for the selected accessible view.
|
|
70
|
+
`record_code_block_schema_get` returns code-block-ready schema for exact code block field selection.
|
|
71
|
+
`record_import_schema_get` returns import-ready column metadata.
|
|
72
|
+
|
|
73
|
+
- Hidden fields are omitted.
|
|
74
|
+
- Missing fields mean the field is not visible in the current permission scope.
|
|
75
|
+
- Read the top-level schema payload directly; do not guess missing writable fields.
|
|
76
|
+
|
|
77
|
+
## Analytics Path
|
|
78
|
+
|
|
79
|
+
`app_get -> record_browse_schema_get(view_id=...) -> record_analyze`
|
|
80
|
+
|
|
81
|
+
Prefer `view_id` entries from `accessible_views` where `analysis_supported=true`.
|
|
82
|
+
|
|
83
|
+
Use this DSL shape:
|
|
84
|
+
|
|
85
|
+
- `dimensions`: `{{field_id, alias, bucket}}`
|
|
86
|
+
- `metrics`: `{{op, field_id, alias}}`
|
|
87
|
+
- `filters`: `{{field_id, op, value}}`
|
|
88
|
+
- `sort`: `{{by, order}}`
|
|
89
|
+
|
|
90
|
+
Important key rules:
|
|
91
|
+
|
|
92
|
+
- Use `op`
|
|
93
|
+
- Do **not** use `type`
|
|
94
|
+
- Do **not** use `agg`
|
|
95
|
+
- Do **not** use `aggregation`
|
|
96
|
+
- Do **not** use `operator`
|
|
97
|
+
|
|
98
|
+
Analysis answers must include concrete numbers. When applicable, include percentages based on the returned totals.
|
|
99
|
+
|
|
100
|
+
## Record CRUD Path
|
|
101
|
+
|
|
102
|
+
`app_get -> record_browse_schema_get(view_id=...) -> record_list / record_get`
|
|
103
|
+
`record_insert_schema_get -> record_insert`
|
|
104
|
+
`record_update_schema_get -> record_update`
|
|
105
|
+
`record_list / record_get -> record_delete`
|
|
106
|
+
`record_code_block_schema_get -> record_code_block_run`
|
|
107
|
+
|
|
108
|
+
- Use `columns` as `[{{field_id}}]`
|
|
109
|
+
- Use `where` items as `{{field_id, op, value}}`
|
|
110
|
+
- Use `order_by` items as `{{field_id, direction}}`
|
|
111
|
+
- Legacy forms such as bare integer `field_id`, `fieldId`, `operator`, `values`, or `order` may still parse, but they are compatibility-only and not the canonical DSL
|
|
112
|
+
|
|
113
|
+
- `record_insert` uses an applicant-node `fields` map keyed by field title.
|
|
114
|
+
- `record_update` uses a field-title keyed `fields` map and internally selects the first accessible view that can execute the current payload.
|
|
115
|
+
- For insert, `runtime_linked_required_fields` means required-but-not-directly-writable fields that are usually supplied by runtime linkage or upstream context.
|
|
116
|
+
- For insert, fields marked `may_become_required=true` stay in `optional_fields`; they are still directly writable, but linked visibility or option-driven rules can make them required at runtime.
|
|
117
|
+
- Read field-level `linkage` whenever present on `record_insert_schema_get` or `record_update_schema_get`; it is the static hint for linked visibility, reference-driven auto fill, and formula/default auto-fill behavior.
|
|
118
|
+
- `linkage.sources` lists upstream field titles that influence the current field; `linkage.affects_fields` lists downstream fields that may change when the current field changes.
|
|
119
|
+
- `linkage.kind=logic_visibility` means linked visibility or option-driven rules are involved; `linkage.kind=reference_fill` means reference/default matching logic is involved; `linkage.kind=formula_fill` means formula/default auto-fill logic is involved.
|
|
120
|
+
- `record_update_schema_get` exposes the overall writable field set for the record, but not every field combination is guaranteed; `record_update` still needs one single matched accessible view that can cover the payload.
|
|
121
|
+
- `record_delete` deletes by `record_id` or `record_ids`.
|
|
122
|
+
- When readback shape matters after insert or update, prefer `record_get(..., output_profile="normalized")` or `record_list(..., output_profile="normalized")`.
|
|
123
|
+
|
|
124
|
+
- Read relation targets from `record_insert_schema_get` / `record_update_schema_get` relation metadata before preparing relation writes.
|
|
125
|
+
- Member and department fields may be written with natural strings directly on `record_insert` / `record_update`; only fall back to `record_member_candidates` or `record_department_candidates` when the user wants explicit candidate browsing or the write returns ambiguity that needs confirmation.
|
|
126
|
+
- If explicit candidate browsing is needed for default-all member or department fields, prefer those field candidate tools instead of starting with `directory_*`.
|
|
127
|
+
|
|
128
|
+
## Code Block Path
|
|
129
|
+
|
|
130
|
+
Use `record_code_block_run` when the user wants to execute a form code-block field against an existing record.
|
|
131
|
+
|
|
132
|
+
- Always resolve the exact code-block field from `record_code_block_schema_get` first.
|
|
133
|
+
- Treat code-block execution as write-capable, not read-only.
|
|
134
|
+
- If the code block is bound to relation outputs, Qingflow may calculate target answers and write them back automatically.
|
|
135
|
+
- In workflow context, pass `role=3` and the exact `workflow_node_id`.
|
|
136
|
+
- After execution, inspect `outputs.alias_results`, `relation.target_fields`, and `writeback.verification` before claiming success.
|
|
137
|
+
|
|
138
|
+
## Import Path
|
|
139
|
+
|
|
140
|
+
`app_get -> record_import_schema_get -> record_import_template_get -> record_import_verify -> (optional authorized record_import_repair_local) -> record_import_start -> record_import_status_get`
|
|
141
|
+
|
|
142
|
+
- Check `app_get.data.import_capability` before doing import work.
|
|
143
|
+
- If `import_capability.can_import=false`, stop before template download, file repair, or import start.
|
|
144
|
+
- Import must go through `verify -> start`; do not start directly from a raw file path.
|
|
145
|
+
- `record_import_start` requires an explicit `being_enter_auditing` choice. Do not assume a default.
|
|
146
|
+
- Do not modify user-uploaded files unless the user explicitly authorizes repair.
|
|
147
|
+
- If repair is authorized, keep the original file and repair a copy, then run `record_import_verify` again before `record_import_start`.
|
|
148
|
+
|
|
149
|
+
## Task Workflow Path
|
|
150
|
+
|
|
151
|
+
`task_list -> task_get -> task_action_execute`
|
|
152
|
+
|
|
153
|
+
- Use `task_associated_report_detail_get` for associated view or report details.
|
|
154
|
+
- Use `task_workflow_log_get` for full workflow log history.
|
|
155
|
+
- Task actions operate on `app_key + record_id + workflow_node_id`, not `task_id`.
|
|
156
|
+
|
|
157
|
+
## Time Handling
|
|
158
|
+
|
|
159
|
+
Normalize relative dates before building DSL.
|
|
160
|
+
|
|
161
|
+
- If the user says `3月` without a year, use the current year: `{current_year}`
|
|
162
|
+
- Convert month-only phrases into explicit legal date ranges
|
|
163
|
+
- Never send impossible dates such as `2026-02-29`
|
|
164
|
+
|
|
165
|
+
## Environment
|
|
166
|
+
|
|
167
|
+
Default to `prod` unless the user explicitly specifies `test`.
|
|
168
|
+
|
|
169
|
+
## Constraints
|
|
170
|
+
|
|
171
|
+
Avoid builder-side app or schema changes here.
|
|
172
|
+
|
|
173
|
+
## Feedback Path
|
|
174
|
+
|
|
175
|
+
If the current MCP capability is unsupported, the workflow is awkward, or the user's need still cannot be satisfied after reasonable use, offer to submit product feedback.
|
|
176
|
+
|
|
177
|
+
- First summarize what is still not working
|
|
178
|
+
- Ask the user whether to submit feedback
|
|
179
|
+
- Call `feedback_submit` only after explicit user confirmation""",
|
|
180
|
+
)
|
|
181
|
+
sessions = SessionStore()
|
|
182
|
+
backend = BackendClient()
|
|
183
|
+
AuthTools(sessions, backend).register(server)
|
|
184
|
+
FeedbackTools(backend, mcp_side="通用").register(server)
|
|
185
|
+
WorkspaceTools(sessions, backend).register(server)
|
|
186
|
+
FileTools(sessions, backend).register(server)
|
|
187
|
+
ImportTools(sessions, backend).register(server)
|
|
188
|
+
CodeBlockTools(sessions, backend).register(server)
|
|
189
|
+
TaskContextTools(sessions, backend).register(server)
|
|
190
|
+
RoleTools(sessions, backend).register(server)
|
|
191
|
+
AppTools(sessions, backend).register(server)
|
|
192
|
+
QingbiReportTools(sessions, backend).register(server)
|
|
193
|
+
PackageTools(sessions, backend).register(server)
|
|
194
|
+
NavigationTools(sessions, backend).register(server)
|
|
195
|
+
PortalTools(sessions, backend).register(server)
|
|
196
|
+
DirectoryTools(sessions, backend).register(server)
|
|
197
|
+
WorkflowTools(sessions, backend).register(server)
|
|
198
|
+
ViewTools(sessions, backend).register(server)
|
|
199
|
+
SolutionTools(sessions, backend).register(server)
|
|
200
|
+
return server
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
mcp = build_server()
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def main() -> None:
|
|
207
|
+
mcp.run()
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
if __name__ == "__main__":
|
|
211
|
+
main()
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from mcp.server.fastmcp import FastMCP
|
|
4
|
+
|
|
5
|
+
from .backend_client import BackendClient
|
|
6
|
+
from .config import DEFAULT_PROFILE
|
|
7
|
+
from .session_store import SessionStore
|
|
8
|
+
from .tools.ai_builder_tools import AiBuilderTools
|
|
9
|
+
from .tools.auth_tools import AuthTools
|
|
10
|
+
from .tools.feedback_tools import FeedbackTools
|
|
11
|
+
from .tools.file_tools import FileTools
|
|
12
|
+
from .tools.workspace_tools import WorkspaceTools
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def build_builder_server() -> FastMCP:
|
|
16
|
+
server = FastMCP(
|
|
17
|
+
"Qingflow App Builder MCP",
|
|
18
|
+
instructions=(
|
|
19
|
+
"Use this server for AI-native Qingflow builder workflows. "
|
|
20
|
+
"`feedback_submit` is always available as a cross-cutting helper when the current capability is unsupported, awkward, or still cannot satisfy the user's need after reasonable use; it does not require Qingflow login or workspace selection, and it should be called only after explicit user confirmation. "
|
|
21
|
+
"Follow the resource path resolve -> summary read -> apply -> attach -> publish_verify. "
|
|
22
|
+
"Use builder_tool_contract when you need a machine-readable contract, aliases, allowed enums, or a minimal valid example for a public builder tool. "
|
|
23
|
+
"If creating a new package may be appropriate, ask the user to confirm package creation before calling package_create; otherwise use package_resolve/package_list and app_resolve to locate resources, "
|
|
24
|
+
"app_read_summary/app_read_fields/app_read_layout_summary/app_read_views_summary/app_read_flow_summary/app_read_charts_summary/portal_read_summary for compact reads, "
|
|
25
|
+
"member_search/role_search/role_create when workflow assignees must come from the directory or role catalog, preferring roles over explicit members unless the user explicitly names members, "
|
|
26
|
+
"then app_schema_apply/app_layout_apply/app_flow_apply/app_views_apply/app_charts_apply/portal_apply to execute normalized patches; these apply tools perform planning, normalization, and dependency checks internally where applicable. Schema/layout/views noop requests skip publish, charts are immediate-live without publish and resolve targets by chart_id first then exact unique chart name, portal updates are replace-only and publish=false only guarantees draft/base-info updates, and flow should use publish=false whenever you only want draft/precheck behavior. "
|
|
27
|
+
"Use package_attach_app to attach apps to packages, and app_publish_verify for explicit final publish verification. "
|
|
28
|
+
"For workflow edits, keep the public builder surface on stable linear flows only: start/approve/fill/copy/webhook/end. Branch and condition nodes are intentionally disabled because the backend workflow route is not front-end stable for those node types. Declare node assignees and editable fields explicitly. "
|
|
29
|
+
"If builder writes are blocked by the current user's own edit lock, use app_release_edit_lock_if_mine with the lock owner details from the failed result. "
|
|
30
|
+
"Do not handcraft internal solution payloads or rely on build_id/stage/repair. "
|
|
31
|
+
"If the current MCP capability is unsupported, the workflow is awkward, or the user's need still cannot be satisfied after reasonable use, first summarize the gap, ask whether to submit feedback, and call feedback_submit only after explicit user confirmation."
|
|
32
|
+
),
|
|
33
|
+
)
|
|
34
|
+
sessions = SessionStore()
|
|
35
|
+
backend = BackendClient()
|
|
36
|
+
auth = AuthTools(sessions, backend)
|
|
37
|
+
workspace = WorkspaceTools(sessions, backend)
|
|
38
|
+
files = FileTools(sessions, backend)
|
|
39
|
+
ai_builder = AiBuilderTools(sessions, backend)
|
|
40
|
+
feedback = FeedbackTools(backend, mcp_side="App Builder MCP")
|
|
41
|
+
|
|
42
|
+
@server.tool()
|
|
43
|
+
def auth_login(
|
|
44
|
+
profile: str = DEFAULT_PROFILE,
|
|
45
|
+
base_url: str | None = None,
|
|
46
|
+
qf_version: str | None = None,
|
|
47
|
+
email: str = "",
|
|
48
|
+
password: str = "",
|
|
49
|
+
persist: bool = True,
|
|
50
|
+
) -> dict:
|
|
51
|
+
return auth.auth_login(
|
|
52
|
+
profile=profile,
|
|
53
|
+
base_url=base_url,
|
|
54
|
+
qf_version=qf_version,
|
|
55
|
+
email=email,
|
|
56
|
+
password=password,
|
|
57
|
+
persist=persist,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
@server.tool()
|
|
61
|
+
def auth_use_token(
|
|
62
|
+
profile: str = DEFAULT_PROFILE,
|
|
63
|
+
base_url: str | None = None,
|
|
64
|
+
qf_version: str | None = None,
|
|
65
|
+
token: str = "",
|
|
66
|
+
ws_id: int | None = None,
|
|
67
|
+
persist: bool = False,
|
|
68
|
+
) -> dict:
|
|
69
|
+
return auth.auth_use_token(
|
|
70
|
+
profile=profile,
|
|
71
|
+
base_url=base_url,
|
|
72
|
+
qf_version=qf_version,
|
|
73
|
+
token=token,
|
|
74
|
+
ws_id=ws_id,
|
|
75
|
+
persist=persist,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
@server.tool()
|
|
79
|
+
def auth_whoami(profile: str = DEFAULT_PROFILE) -> dict:
|
|
80
|
+
return auth.auth_whoami(profile=profile)
|
|
81
|
+
|
|
82
|
+
@server.tool()
|
|
83
|
+
def auth_logout(profile: str = DEFAULT_PROFILE, forget_persisted: bool = False) -> dict:
|
|
84
|
+
return auth.auth_logout(profile=profile, forget_persisted=forget_persisted)
|
|
85
|
+
|
|
86
|
+
@server.tool()
|
|
87
|
+
def workspace_list(
|
|
88
|
+
profile: str = DEFAULT_PROFILE,
|
|
89
|
+
page_num: int = 1,
|
|
90
|
+
page_size: int = 20,
|
|
91
|
+
include_external: bool = False,
|
|
92
|
+
) -> dict:
|
|
93
|
+
return workspace.workspace_list(
|
|
94
|
+
profile=profile,
|
|
95
|
+
page_num=page_num,
|
|
96
|
+
page_size=page_size,
|
|
97
|
+
include_external=include_external,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
@server.tool()
|
|
101
|
+
def workspace_select(profile: str = DEFAULT_PROFILE, ws_id: int = 0) -> dict:
|
|
102
|
+
return workspace.workspace_select(profile=profile, ws_id=ws_id)
|
|
103
|
+
|
|
104
|
+
@server.tool()
|
|
105
|
+
def file_upload_local(
|
|
106
|
+
profile: str = DEFAULT_PROFILE,
|
|
107
|
+
upload_kind: str = "attachment",
|
|
108
|
+
file_path: str = "",
|
|
109
|
+
upload_mark: str | None = None,
|
|
110
|
+
content_type: str | None = None,
|
|
111
|
+
bucket_type: str | None = None,
|
|
112
|
+
path_id: int | None = None,
|
|
113
|
+
file_related_url: str | None = None,
|
|
114
|
+
) -> dict:
|
|
115
|
+
return files.file_upload_local(
|
|
116
|
+
profile=profile,
|
|
117
|
+
upload_kind=upload_kind,
|
|
118
|
+
file_path=file_path,
|
|
119
|
+
upload_mark=upload_mark,
|
|
120
|
+
content_type=content_type,
|
|
121
|
+
bucket_type=bucket_type,
|
|
122
|
+
path_id=path_id,
|
|
123
|
+
file_related_url=file_related_url,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
feedback.register(server)
|
|
127
|
+
|
|
128
|
+
@server.tool()
|
|
129
|
+
def package_list(profile: str = DEFAULT_PROFILE, trial_status: str = "all") -> dict:
|
|
130
|
+
return ai_builder.package_list(profile=profile, trial_status=trial_status)
|
|
131
|
+
|
|
132
|
+
@server.tool()
|
|
133
|
+
def package_resolve(profile: str = DEFAULT_PROFILE, package_name: str = "") -> dict:
|
|
134
|
+
return ai_builder.package_resolve(profile=profile, package_name=package_name)
|
|
135
|
+
|
|
136
|
+
@server.tool()
|
|
137
|
+
def builder_tool_contract(tool_name: str = "") -> dict:
|
|
138
|
+
return ai_builder.builder_tool_contract(tool_name=tool_name)
|
|
139
|
+
|
|
140
|
+
@server.tool()
|
|
141
|
+
def package_create(profile: str = DEFAULT_PROFILE, package_name: str = "") -> dict:
|
|
142
|
+
return ai_builder.package_create(profile=profile, package_name=package_name)
|
|
143
|
+
|
|
144
|
+
@server.tool()
|
|
145
|
+
def member_search(
|
|
146
|
+
profile: str = DEFAULT_PROFILE,
|
|
147
|
+
query: str = "",
|
|
148
|
+
page_num: int = 1,
|
|
149
|
+
page_size: int = 20,
|
|
150
|
+
contain_disable: bool = False,
|
|
151
|
+
) -> dict:
|
|
152
|
+
return ai_builder.member_search(
|
|
153
|
+
profile=profile,
|
|
154
|
+
query=query,
|
|
155
|
+
page_num=page_num,
|
|
156
|
+
page_size=page_size,
|
|
157
|
+
contain_disable=contain_disable,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
@server.tool()
|
|
161
|
+
def role_search(
|
|
162
|
+
profile: str = DEFAULT_PROFILE,
|
|
163
|
+
keyword: str = "",
|
|
164
|
+
page_num: int = 1,
|
|
165
|
+
page_size: int = 20,
|
|
166
|
+
) -> dict:
|
|
167
|
+
return ai_builder.role_search(profile=profile, keyword=keyword, page_num=page_num, page_size=page_size)
|
|
168
|
+
|
|
169
|
+
@server.tool()
|
|
170
|
+
def role_create(
|
|
171
|
+
profile: str = DEFAULT_PROFILE,
|
|
172
|
+
role_name: str = "",
|
|
173
|
+
member_uids: list[int] | None = None,
|
|
174
|
+
member_emails: list[str] | None = None,
|
|
175
|
+
member_names: list[str] | None = None,
|
|
176
|
+
role_icon: str = "ex-user-outlined",
|
|
177
|
+
) -> dict:
|
|
178
|
+
return ai_builder.role_create(
|
|
179
|
+
profile=profile,
|
|
180
|
+
role_name=role_name,
|
|
181
|
+
member_uids=member_uids or [],
|
|
182
|
+
member_emails=member_emails or [],
|
|
183
|
+
member_names=member_names or [],
|
|
184
|
+
role_icon=role_icon,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
@server.tool()
|
|
188
|
+
def package_attach_app(
|
|
189
|
+
profile: str = DEFAULT_PROFILE,
|
|
190
|
+
tag_id: int = 0,
|
|
191
|
+
app_key: str = "",
|
|
192
|
+
app_title: str = "",
|
|
193
|
+
) -> dict:
|
|
194
|
+
return ai_builder.package_attach_app(profile=profile, tag_id=tag_id, app_key=app_key, app_title=app_title)
|
|
195
|
+
|
|
196
|
+
@server.tool()
|
|
197
|
+
def app_release_edit_lock_if_mine(
|
|
198
|
+
profile: str = DEFAULT_PROFILE,
|
|
199
|
+
app_key: str = "",
|
|
200
|
+
lock_owner_email: str = "",
|
|
201
|
+
lock_owner_name: str = "",
|
|
202
|
+
) -> dict:
|
|
203
|
+
return ai_builder.app_release_edit_lock_if_mine(
|
|
204
|
+
profile=profile,
|
|
205
|
+
app_key=app_key,
|
|
206
|
+
lock_owner_email=lock_owner_email,
|
|
207
|
+
lock_owner_name=lock_owner_name,
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
@server.tool()
|
|
211
|
+
def app_resolve(
|
|
212
|
+
profile: str = DEFAULT_PROFILE,
|
|
213
|
+
app_key: str = "",
|
|
214
|
+
app_name: str = "",
|
|
215
|
+
package_tag_id: int | None = None,
|
|
216
|
+
) -> dict:
|
|
217
|
+
return ai_builder.app_resolve(profile=profile, app_key=app_key, app_name=app_name, package_tag_id=package_tag_id)
|
|
218
|
+
|
|
219
|
+
@server.tool()
|
|
220
|
+
def app_read_summary(profile: str = DEFAULT_PROFILE, app_key: str = "") -> dict:
|
|
221
|
+
return ai_builder.app_read_summary(profile=profile, app_key=app_key)
|
|
222
|
+
|
|
223
|
+
@server.tool()
|
|
224
|
+
def app_read_fields(profile: str = DEFAULT_PROFILE, app_key: str = "") -> dict:
|
|
225
|
+
return ai_builder.app_read_fields(profile=profile, app_key=app_key)
|
|
226
|
+
|
|
227
|
+
@server.tool()
|
|
228
|
+
def app_read_layout_summary(profile: str = DEFAULT_PROFILE, app_key: str = "") -> dict:
|
|
229
|
+
return ai_builder.app_read_layout_summary(profile=profile, app_key=app_key)
|
|
230
|
+
|
|
231
|
+
@server.tool()
|
|
232
|
+
def app_read_views_summary(profile: str = DEFAULT_PROFILE, app_key: str = "") -> dict:
|
|
233
|
+
return ai_builder.app_read_views_summary(profile=profile, app_key=app_key)
|
|
234
|
+
|
|
235
|
+
@server.tool()
|
|
236
|
+
def app_read_flow_summary(profile: str = DEFAULT_PROFILE, app_key: str = "") -> dict:
|
|
237
|
+
return ai_builder.app_read_flow_summary(profile=profile, app_key=app_key)
|
|
238
|
+
|
|
239
|
+
@server.tool()
|
|
240
|
+
def app_read_charts_summary(profile: str = DEFAULT_PROFILE, app_key: str = "") -> dict:
|
|
241
|
+
return ai_builder.app_read_charts_summary(profile=profile, app_key=app_key)
|
|
242
|
+
|
|
243
|
+
@server.tool()
|
|
244
|
+
def portal_read_summary(
|
|
245
|
+
profile: str = DEFAULT_PROFILE,
|
|
246
|
+
dash_key: str = "",
|
|
247
|
+
being_draft: bool = True,
|
|
248
|
+
) -> dict:
|
|
249
|
+
return ai_builder.portal_read_summary(profile=profile, dash_key=dash_key, being_draft=being_draft)
|
|
250
|
+
|
|
251
|
+
@server.tool()
|
|
252
|
+
def app_schema_apply(
|
|
253
|
+
profile: str = DEFAULT_PROFILE,
|
|
254
|
+
app_key: str = "",
|
|
255
|
+
package_tag_id: int | None = None,
|
|
256
|
+
app_name: str = "",
|
|
257
|
+
app_title: str = "",
|
|
258
|
+
create_if_missing: bool = False,
|
|
259
|
+
publish: bool = True,
|
|
260
|
+
add_fields: list[dict] | None = None,
|
|
261
|
+
update_fields: list[dict] | None = None,
|
|
262
|
+
remove_fields: list[dict] | None = None,
|
|
263
|
+
) -> dict:
|
|
264
|
+
return ai_builder.app_schema_apply(
|
|
265
|
+
profile=profile,
|
|
266
|
+
app_key=app_key,
|
|
267
|
+
package_tag_id=package_tag_id,
|
|
268
|
+
app_name=app_name,
|
|
269
|
+
app_title=app_title,
|
|
270
|
+
create_if_missing=create_if_missing,
|
|
271
|
+
publish=publish,
|
|
272
|
+
add_fields=add_fields or [],
|
|
273
|
+
update_fields=update_fields or [],
|
|
274
|
+
remove_fields=remove_fields or [],
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
@server.tool()
|
|
278
|
+
def app_layout_apply(
|
|
279
|
+
profile: str = DEFAULT_PROFILE,
|
|
280
|
+
app_key: str = "",
|
|
281
|
+
mode: str = "merge",
|
|
282
|
+
publish: bool = True,
|
|
283
|
+
sections: list[dict] | None = None,
|
|
284
|
+
) -> dict:
|
|
285
|
+
return ai_builder.app_layout_apply(profile=profile, app_key=app_key, mode=mode, publish=publish, sections=sections or [])
|
|
286
|
+
|
|
287
|
+
@server.tool()
|
|
288
|
+
def app_flow_apply(
|
|
289
|
+
profile: str = DEFAULT_PROFILE,
|
|
290
|
+
app_key: str = "",
|
|
291
|
+
mode: str = "replace",
|
|
292
|
+
publish: bool = True,
|
|
293
|
+
nodes: list[dict] | None = None,
|
|
294
|
+
transitions: list[dict] | None = None,
|
|
295
|
+
) -> dict:
|
|
296
|
+
return ai_builder.app_flow_apply(
|
|
297
|
+
profile=profile,
|
|
298
|
+
app_key=app_key,
|
|
299
|
+
mode=mode,
|
|
300
|
+
publish=publish,
|
|
301
|
+
nodes=nodes or [],
|
|
302
|
+
transitions=transitions or [],
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
@server.tool()
|
|
306
|
+
def app_views_apply(
|
|
307
|
+
profile: str = DEFAULT_PROFILE,
|
|
308
|
+
app_key: str = "",
|
|
309
|
+
publish: bool = True,
|
|
310
|
+
upsert_views: list[dict] | None = None,
|
|
311
|
+
remove_views: list[str] | None = None,
|
|
312
|
+
) -> dict:
|
|
313
|
+
return ai_builder.app_views_apply(
|
|
314
|
+
profile=profile,
|
|
315
|
+
app_key=app_key,
|
|
316
|
+
publish=publish,
|
|
317
|
+
upsert_views=upsert_views or [],
|
|
318
|
+
remove_views=remove_views or [],
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
@server.tool()
|
|
322
|
+
def app_charts_apply(
|
|
323
|
+
profile: str = DEFAULT_PROFILE,
|
|
324
|
+
app_key: str = "",
|
|
325
|
+
upsert_charts: list[dict] | None = None,
|
|
326
|
+
remove_chart_ids: list[str] | None = None,
|
|
327
|
+
reorder_chart_ids: list[str] | None = None,
|
|
328
|
+
) -> dict:
|
|
329
|
+
return ai_builder.app_charts_apply(
|
|
330
|
+
profile=profile,
|
|
331
|
+
app_key=app_key,
|
|
332
|
+
upsert_charts=upsert_charts or [],
|
|
333
|
+
remove_chart_ids=remove_chart_ids or [],
|
|
334
|
+
reorder_chart_ids=reorder_chart_ids or [],
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
@server.tool()
|
|
338
|
+
def portal_apply(
|
|
339
|
+
profile: str = DEFAULT_PROFILE,
|
|
340
|
+
dash_key: str = "",
|
|
341
|
+
dash_name: str = "",
|
|
342
|
+
package_tag_id: int | None = None,
|
|
343
|
+
publish: bool = True,
|
|
344
|
+
sections: list[dict] | None = None,
|
|
345
|
+
auth: dict | None = None,
|
|
346
|
+
icon: str | None = None,
|
|
347
|
+
color: str | None = None,
|
|
348
|
+
hide_copyright: bool | None = None,
|
|
349
|
+
dash_global_config: dict | None = None,
|
|
350
|
+
config: dict | None = None,
|
|
351
|
+
) -> dict:
|
|
352
|
+
return ai_builder.portal_apply(
|
|
353
|
+
profile=profile,
|
|
354
|
+
dash_key=dash_key,
|
|
355
|
+
dash_name=dash_name,
|
|
356
|
+
package_tag_id=package_tag_id,
|
|
357
|
+
publish=publish,
|
|
358
|
+
sections=sections or [],
|
|
359
|
+
auth=auth,
|
|
360
|
+
icon=icon,
|
|
361
|
+
color=color,
|
|
362
|
+
hide_copyright=hide_copyright,
|
|
363
|
+
dash_global_config=dash_global_config,
|
|
364
|
+
config=config or {},
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
@server.tool()
|
|
368
|
+
def app_publish_verify(
|
|
369
|
+
profile: str = DEFAULT_PROFILE,
|
|
370
|
+
app_key: str = "",
|
|
371
|
+
expected_package_tag_id: int | None = None,
|
|
372
|
+
) -> dict:
|
|
373
|
+
return ai_builder.app_publish_verify(
|
|
374
|
+
profile=profile,
|
|
375
|
+
app_key=app_key,
|
|
376
|
+
expected_package_tag_id=expected_package_tag_id,
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
return server
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def main() -> None:
|
|
383
|
+
build_builder_server().run()
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
if __name__ == "__main__":
|
|
387
|
+
main()
|