@josephyan/qingflow-app-user-mcp 1.0.9 → 1.1.1
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 +6 -2
- package/npm/bin/qingflow-app-user-mcp.mjs +31 -2
- package/npm/lib/runtime.mjs +43 -2
- package/package.json +1 -1
- package/pyproject.toml +2 -1
- package/skills/qingflow-app-builder/SKILL.md +276 -0
- package/skills/qingflow-app-builder/agents/openai.yaml +4 -0
- 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 +149 -0
- package/skills/qingflow-app-builder/references/environments.md +63 -0
- package/skills/qingflow-app-builder/references/flow-actors-and-permissions.md +123 -0
- package/skills/qingflow-app-builder/references/gotchas.md +108 -0
- package/skills/qingflow-app-builder/references/match-rules.md +114 -0
- package/skills/qingflow-app-builder/references/public-surface-sync.md +75 -0
- package/skills/qingflow-app-builder/references/solution-playbooks.md +52 -0
- package/skills/qingflow-app-builder/references/tool-selection.md +107 -0
- package/skills/qingflow-app-builder/references/update-flow.md +186 -0
- package/skills/qingflow-app-builder/references/update-layout.md +68 -0
- package/skills/qingflow-app-builder/references/update-schema.md +72 -0
- package/skills/qingflow-app-builder/references/update-views.md +291 -0
- package/skills/qingflow-app-builder-code-integrations/SKILL.md +137 -0
- package/skills/qingflow-app-builder-code-integrations/agents/openai.yaml +4 -0
- package/skills/qingflow-app-builder-code-integrations/references/code-block.md +66 -0
- package/skills/qingflow-app-builder-code-integrations/references/q-linker.md +77 -0
- package/skills/qingflow-app-user/SKILL.md +2 -1
- package/skills/qingflow-app-user/references/data-gotchas.md +5 -3
- package/skills/qingflow-app-user/references/public-surface-sync.md +6 -3
- package/skills/qingflow-app-user/references/record-patterns.md +14 -4
- 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-record-analysis/SKILL.md +94 -186
- package/skills/qingflow-record-analysis/agents/openai.yaml +2 -2
- package/skills/qingflow-record-analysis/references/analysis-gotchas.md +54 -113
- package/skills/qingflow-record-analysis/references/analysis-patterns.md +95 -134
- package/skills/qingflow-record-analysis/references/business-context.md +74 -0
- package/skills/qingflow-record-analysis/references/confidence-reporting.md +47 -74
- package/skills/qingflow-record-analysis/references/data-access-playbook.md +106 -0
- package/skills/qingflow-record-analysis/references/pandas-recipes.md +172 -0
- package/skills/qingflow-record-analysis/references/report-format.md +76 -0
- package/skills/qingflow-record-insert/SKILL.md +28 -7
- package/skills/qingflow-record-update/SKILL.md +3 -1
- 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/backend_client.py +55 -1
- package/src/qingflow_mcp/builder_facade/models.py +532 -48
- package/src/qingflow_mcp/builder_facade/service.py +9194 -2384
- package/src/qingflow_mcp/builder_facade/workflow_spec.py +111 -0
- package/src/qingflow_mcp/cli/commands/app.py +3 -16
- package/src/qingflow_mcp/cli/commands/builder.py +354 -56
- package/src/qingflow_mcp/cli/commands/record.py +112 -6
- package/src/qingflow_mcp/cli/formatters.py +102 -2
- package/src/qingflow_mcp/cli/main.py +204 -3
- package/src/qingflow_mcp/public_surface.py +12 -9
- package/src/qingflow_mcp/response_trim.py +287 -22
- package/src/qingflow_mcp/server.py +29 -20
- package/src/qingflow_mcp/server_app_builder.py +108 -30
- package/src/qingflow_mcp/server_app_user.py +28 -23
- package/src/qingflow_mcp/solution/compiler/__init__.py +1 -3
- package/src/qingflow_mcp/solution/compiler/icon_utils.py +294 -0
- package/src/qingflow_mcp/solution/executor.py +3 -133
- package/src/qingflow_mcp/tools/ai_builder_tools.py +2617 -440
- package/src/qingflow_mcp/tools/app_tools.py +53 -8
- package/src/qingflow_mcp/tools/package_tools.py +16 -2
- package/src/qingflow_mcp/tools/record_tools.py +14283 -9162
- package/src/qingflow_mcp/tools/resource_read_tools.py +3 -0
- package/src/qingflow_mcp/tools/solution_tools.py +30 -2
- package/src/qingflow_mcp/tools/workflow_tools.py +3 -31
- package/skills/qingflow-record-analysis/references/dsl-templates.md +0 -93
- package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +0 -173
|
@@ -10,7 +10,11 @@ from .common import load_list_arg, load_object_arg, raise_config_error, require_
|
|
|
10
10
|
|
|
11
11
|
def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
|
|
12
12
|
parser = subparsers.add_parser("record", help="记录与表结构")
|
|
13
|
-
record_subparsers = parser.add_subparsers(
|
|
13
|
+
record_subparsers = parser.add_subparsers(
|
|
14
|
+
dest="record_command",
|
|
15
|
+
required=True,
|
|
16
|
+
metavar="{schema,list,access,get,logs,insert,update,delete,member-candidates,department-candidates,code-block-run}",
|
|
17
|
+
)
|
|
14
18
|
|
|
15
19
|
schema = record_subparsers.add_parser("schema", help="读取记录相关表结构")
|
|
16
20
|
schema.add_argument("--mode", dest="legacy_mode", help=argparse.SUPPRESS)
|
|
@@ -43,13 +47,37 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
43
47
|
schema_code_block.add_argument("--app-key", required=True)
|
|
44
48
|
schema_code_block.set_defaults(handler=_handle_schema_code_block, format_hint="")
|
|
45
49
|
|
|
50
|
+
member_candidates = record_subparsers.add_parser("member-candidates", help="读取成员字段候选项")
|
|
51
|
+
member_candidates.add_argument("--app-key", required=True)
|
|
52
|
+
member_candidates.add_argument("--field-id", type=int, required=True)
|
|
53
|
+
member_candidates.add_argument("--keyword", default="")
|
|
54
|
+
member_candidates.add_argument("--page-num", type=int, default=1)
|
|
55
|
+
member_candidates.add_argument("--page-size", type=int, default=20)
|
|
56
|
+
member_candidates.add_argument("--record-id")
|
|
57
|
+
member_candidates.add_argument("--workflow-node-id", type=int)
|
|
58
|
+
member_candidates.add_argument("--fields-file")
|
|
59
|
+
member_candidates.set_defaults(handler=_handle_member_candidates, format_hint="")
|
|
60
|
+
|
|
61
|
+
department_candidates = record_subparsers.add_parser("department-candidates", help="读取部门字段候选项")
|
|
62
|
+
department_candidates.add_argument("--app-key", required=True)
|
|
63
|
+
department_candidates.add_argument("--field-id", type=int, required=True)
|
|
64
|
+
department_candidates.add_argument("--keyword", default="")
|
|
65
|
+
department_candidates.add_argument("--page-num", type=int, default=1)
|
|
66
|
+
department_candidates.add_argument("--page-size", type=int, default=20)
|
|
67
|
+
department_candidates.add_argument("--record-id")
|
|
68
|
+
department_candidates.add_argument("--workflow-node-id", type=int)
|
|
69
|
+
department_candidates.add_argument("--fields-file")
|
|
70
|
+
department_candidates.set_defaults(handler=_handle_department_candidates, format_hint="")
|
|
71
|
+
|
|
46
72
|
list_parser = record_subparsers.add_parser("list", help="列出记录")
|
|
47
73
|
list_parser.add_argument("--app-key", required=True)
|
|
48
74
|
list_parser.add_argument("--column", dest="columns", action="append", type=int, default=[])
|
|
49
75
|
list_parser.add_argument("--columns-file")
|
|
76
|
+
list_parser.add_argument("--query")
|
|
77
|
+
list_parser.add_argument("--query-field", dest="query_fields", action="append", type=int, default=[])
|
|
78
|
+
list_parser.add_argument("--query-fields-file")
|
|
50
79
|
list_parser.add_argument("--where-file")
|
|
51
80
|
list_parser.add_argument("--order-by-file")
|
|
52
|
-
list_parser.add_argument("--limit", type=int, default=20)
|
|
53
81
|
list_parser.add_argument("--page", type=int, default=1)
|
|
54
82
|
list_parser.add_argument("--view-id")
|
|
55
83
|
list_parser.add_argument("--list-type", dest="legacy_list_type", type=int, help=argparse.SUPPRESS)
|
|
@@ -72,11 +100,18 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
72
100
|
get.add_argument("--column", dest="columns", action="append", type=int, default=[])
|
|
73
101
|
get.add_argument("--columns-file")
|
|
74
102
|
get.add_argument("--view-id")
|
|
75
|
-
get.set_defaults(handler=_handle_get, format_hint="")
|
|
103
|
+
get.set_defaults(handler=_handle_get, format_hint="record_get")
|
|
104
|
+
|
|
105
|
+
logs = record_subparsers.add_parser("logs", help="读取单条记录全量日志并写入本地 JSONL")
|
|
106
|
+
logs.add_argument("--app-key", required=True)
|
|
107
|
+
logs.add_argument("--record-id", required=True)
|
|
108
|
+
logs.add_argument("--view-id")
|
|
109
|
+
logs.set_defaults(handler=_handle_logs, format_hint="record_logs")
|
|
76
110
|
|
|
77
111
|
insert = record_subparsers.add_parser("insert", help="新增记录")
|
|
78
112
|
insert.add_argument("--app-key", required=True)
|
|
79
|
-
insert.add_argument("--fields-file",
|
|
113
|
+
insert.add_argument("--fields-file", help=argparse.SUPPRESS)
|
|
114
|
+
insert.add_argument("--items-file")
|
|
80
115
|
insert.add_argument("--verify-write", action=argparse.BooleanOptionalAction, default=True)
|
|
81
116
|
insert.set_defaults(handler=_handle_insert, format_hint="")
|
|
82
117
|
|
|
@@ -95,7 +130,12 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
95
130
|
delete.add_argument("--record-ids-file")
|
|
96
131
|
delete.set_defaults(handler=_handle_delete, format_hint="")
|
|
97
132
|
|
|
98
|
-
analyze = record_subparsers.add_parser("analyze", help=
|
|
133
|
+
analyze = record_subparsers.add_parser("analyze", help=argparse.SUPPRESS)
|
|
134
|
+
record_subparsers._choices_actions = [ # type: ignore[attr-defined]
|
|
135
|
+
action
|
|
136
|
+
for action in record_subparsers._choices_actions # type: ignore[attr-defined]
|
|
137
|
+
if getattr(action, "dest", None) != "analyze"
|
|
138
|
+
]
|
|
99
139
|
analyze.add_argument("--app-key", required=True)
|
|
100
140
|
analyze.add_argument("--dimensions-file")
|
|
101
141
|
analyze.add_argument("--metrics-file")
|
|
@@ -131,6 +171,13 @@ def _columns(args: argparse.Namespace) -> list[Any]:
|
|
|
131
171
|
return columns
|
|
132
172
|
|
|
133
173
|
|
|
174
|
+
def _query_fields(args: argparse.Namespace) -> list[Any]:
|
|
175
|
+
query_fields: list[Any] = list(getattr(args, "query_fields", None) or [])
|
|
176
|
+
if getattr(args, "query_fields_file", None):
|
|
177
|
+
query_fields.extend(require_list_arg(args.query_fields_file, option_name="--query-fields-file"))
|
|
178
|
+
return query_fields
|
|
179
|
+
|
|
180
|
+
|
|
134
181
|
def _handle_schema_root(args: argparse.Namespace, _context: CliContext) -> dict:
|
|
135
182
|
mode = (args.legacy_mode or "").strip()
|
|
136
183
|
if mode:
|
|
@@ -188,6 +235,38 @@ def _handle_schema_code_block(args: argparse.Namespace, context: CliContext) ->
|
|
|
188
235
|
return context.code_block.record_code_block_schema_get_public(profile=args.profile, app_key=args.app_key)
|
|
189
236
|
|
|
190
237
|
|
|
238
|
+
def _candidate_context_fields(args: argparse.Namespace) -> dict[str, Any]:
|
|
239
|
+
return load_object_arg(args.fields_file, option_name="--fields-file") or {}
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def _handle_member_candidates(args: argparse.Namespace, context: CliContext) -> dict:
|
|
243
|
+
return context.record.record_member_candidates(
|
|
244
|
+
profile=args.profile,
|
|
245
|
+
app_key=args.app_key,
|
|
246
|
+
field_id=args.field_id,
|
|
247
|
+
record_id=args.record_id,
|
|
248
|
+
workflow_node_id=args.workflow_node_id,
|
|
249
|
+
fields=_candidate_context_fields(args),
|
|
250
|
+
keyword=args.keyword,
|
|
251
|
+
page_num=args.page_num,
|
|
252
|
+
page_size=args.page_size,
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _handle_department_candidates(args: argparse.Namespace, context: CliContext) -> dict:
|
|
257
|
+
return context.record.record_department_candidates(
|
|
258
|
+
profile=args.profile,
|
|
259
|
+
app_key=args.app_key,
|
|
260
|
+
field_id=args.field_id,
|
|
261
|
+
record_id=args.record_id,
|
|
262
|
+
workflow_node_id=args.workflow_node_id,
|
|
263
|
+
fields=_candidate_context_fields(args),
|
|
264
|
+
keyword=args.keyword,
|
|
265
|
+
page_num=args.page_num,
|
|
266
|
+
page_size=args.page_size,
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
|
|
191
270
|
def _validate_public_view_selector(
|
|
192
271
|
*,
|
|
193
272
|
view_id: str | None,
|
|
@@ -225,9 +304,10 @@ def _handle_list(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
225
304
|
profile=args.profile,
|
|
226
305
|
app_key=args.app_key,
|
|
227
306
|
columns=_columns(args),
|
|
307
|
+
query=args.query,
|
|
308
|
+
query_fields=_query_fields(args),
|
|
228
309
|
where=load_list_arg(args.where_file, option_name="--where-file"),
|
|
229
310
|
order_by=load_list_arg(args.order_by_file, option_name="--order-by-file"),
|
|
230
|
-
limit=args.limit,
|
|
231
311
|
page=args.page,
|
|
232
312
|
view_id=args.view_id,
|
|
233
313
|
)
|
|
@@ -254,7 +334,33 @@ def _handle_get(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
254
334
|
)
|
|
255
335
|
|
|
256
336
|
|
|
337
|
+
def _handle_logs(args: argparse.Namespace, context: CliContext) -> dict:
|
|
338
|
+
return context.record.record_logs_get(
|
|
339
|
+
profile=args.profile,
|
|
340
|
+
app_key=args.app_key,
|
|
341
|
+
record_id=args.record_id,
|
|
342
|
+
view_id=args.view_id,
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
|
|
257
346
|
def _handle_insert(args: argparse.Namespace, context: CliContext) -> dict:
|
|
347
|
+
if args.items_file:
|
|
348
|
+
if args.fields_file:
|
|
349
|
+
raise_config_error(
|
|
350
|
+
"record insert batch mode does not accept --fields-file.",
|
|
351
|
+
fix_hint="Use `record insert --app-key APP_KEY --items-file ITEMS.json` for batch inserts.",
|
|
352
|
+
)
|
|
353
|
+
return context.record.record_insert_public(
|
|
354
|
+
profile=args.profile,
|
|
355
|
+
app_key=args.app_key,
|
|
356
|
+
items=require_list_arg(args.items_file, option_name="--items-file"),
|
|
357
|
+
verify_write=bool(args.verify_write),
|
|
358
|
+
)
|
|
359
|
+
if not args.fields_file:
|
|
360
|
+
raise_config_error(
|
|
361
|
+
"record insert requires --items-file.",
|
|
362
|
+
fix_hint="Use `record insert --app-key APP_KEY --items-file ITEMS.json`; a single insert is one item in the JSON array.",
|
|
363
|
+
)
|
|
258
364
|
return context.record.record_insert_public(
|
|
259
365
|
profile=args.profile,
|
|
260
366
|
app_key=args.app_key,
|
|
@@ -183,7 +183,17 @@ def _format_app_get(result: dict[str, Any]) -> str:
|
|
|
183
183
|
def _format_record_list(result: dict[str, Any]) -> str:
|
|
184
184
|
data = result.get("data") if isinstance(result.get("data"), dict) else {}
|
|
185
185
|
items = data.get("items") if isinstance(data.get("items"), list) else []
|
|
186
|
-
lines = [
|
|
186
|
+
lines: list[str] = []
|
|
187
|
+
lookup = result.get("lookup") if isinstance(result.get("lookup"), dict) else {}
|
|
188
|
+
if lookup:
|
|
189
|
+
lines.append("Lookup:")
|
|
190
|
+
lines.append(f"- query: {lookup.get('query')}")
|
|
191
|
+
lines.append(f"- confidence: {lookup.get('confidence')}")
|
|
192
|
+
lines.append(f"- next_action: {lookup.get('next_action')}")
|
|
193
|
+
lines.append(f"- total_count: {lookup.get('total_count')}")
|
|
194
|
+
lines.append(f"- returned_count: {lookup.get('returned_count')}")
|
|
195
|
+
lines.append(f"- truncated: {lookup.get('truncated')}")
|
|
196
|
+
lines.append(f"Returned Records: {len(items)}")
|
|
187
197
|
for item in items[:10]:
|
|
188
198
|
if isinstance(item, dict):
|
|
189
199
|
lines.append(json.dumps(item, ensure_ascii=False))
|
|
@@ -224,6 +234,95 @@ def _format_record_access(result: dict[str, Any]) -> str:
|
|
|
224
234
|
return "\n".join(lines) + "\n"
|
|
225
235
|
|
|
226
236
|
|
|
237
|
+
def _format_record_get(result: dict[str, Any]) -> str:
|
|
238
|
+
record = result.get("record") if isinstance(result.get("record"), dict) else {}
|
|
239
|
+
app = result.get("app") if isinstance(result.get("app"), dict) else {}
|
|
240
|
+
view = result.get("view") if isinstance(result.get("view"), dict) else {}
|
|
241
|
+
fields = result.get("fields") if isinstance(result.get("fields"), list) else []
|
|
242
|
+
data_logs = result.get("data_logs") if isinstance(result.get("data_logs"), dict) else {}
|
|
243
|
+
workflow_logs = result.get("workflow_logs") if isinstance(result.get("workflow_logs"), dict) else {}
|
|
244
|
+
media_assets = result.get("media_assets") if isinstance(result.get("media_assets"), dict) else {}
|
|
245
|
+
media_items = media_assets.get("items") if isinstance(media_assets.get("items"), list) else []
|
|
246
|
+
downloaded_media = [item for item in media_items if isinstance(item, dict) and item.get("access_status") == "downloaded"]
|
|
247
|
+
failed_media = [item for item in media_items if isinstance(item, dict) and item.get("access_status") != "downloaded"]
|
|
248
|
+
file_assets = result.get("file_assets") if isinstance(result.get("file_assets"), dict) else {}
|
|
249
|
+
file_items = file_assets.get("items") if isinstance(file_assets.get("items"), list) else []
|
|
250
|
+
downloaded_files = [item for item in file_items if isinstance(item, dict) and item.get("access_status") == "downloaded"]
|
|
251
|
+
failed_files = [item for item in file_items if isinstance(item, dict) and item.get("access_status") != "downloaded"]
|
|
252
|
+
extracted_files = [
|
|
253
|
+
item
|
|
254
|
+
for item in downloaded_files
|
|
255
|
+
if isinstance(item.get("extraction"), dict) and item["extraction"].get("status") == "ok"
|
|
256
|
+
]
|
|
257
|
+
associated_resources = result.get("associated_resources") if isinstance(result.get("associated_resources"), list) else []
|
|
258
|
+
unavailable_context = result.get("unavailable_context") if isinstance(result.get("unavailable_context"), list) else []
|
|
259
|
+
lines = [
|
|
260
|
+
f"Status: {result.get('status') or '-'}",
|
|
261
|
+
f"App: {app.get('app_name') or app.get('app_key') or '-'} ({app.get('app_key') or '-'})",
|
|
262
|
+
f"View: {view.get('name') or view.get('view_id') or '-'}",
|
|
263
|
+
f"Record: {record.get('title') or '-'} ({record.get('record_id') or '-'})",
|
|
264
|
+
f"Fields: {len(fields)}",
|
|
265
|
+
f"Data logs: {data_logs.get('status') or '-'} / loaded={data_logs.get('items_loaded')}",
|
|
266
|
+
f"Workflow logs: {workflow_logs.get('status') or '-'} / loaded={workflow_logs.get('items_loaded')}",
|
|
267
|
+
f"Media assets: {media_assets.get('status') or '-'} / downloaded={len(downloaded_media)} / failed={len(failed_media)}",
|
|
268
|
+
f"File assets: {file_assets.get('status') or '-'} / downloaded={len(downloaded_files)} / extracted={len(extracted_files)} / failed={len(failed_files)}",
|
|
269
|
+
f"Associated resources: {len(associated_resources)}",
|
|
270
|
+
f"Unavailable contexts: {len(unavailable_context)}",
|
|
271
|
+
]
|
|
272
|
+
if media_assets.get("local_dir"):
|
|
273
|
+
lines.append(f"Media dir: {media_assets.get('local_dir')}")
|
|
274
|
+
if file_assets.get("local_dir"):
|
|
275
|
+
lines.append(f"File dir: {file_assets.get('local_dir')}")
|
|
276
|
+
if failed_media:
|
|
277
|
+
failure_counts: dict[str, int] = {}
|
|
278
|
+
for item in failed_media:
|
|
279
|
+
key = f"{item.get('access_status') or 'unknown'} via {item.get('download_strategy') or 'unknown'}"
|
|
280
|
+
failure_counts[key] = failure_counts.get(key, 0) + 1
|
|
281
|
+
lines.append("Media failures: " + ", ".join(f"{key}={count}" for key, count in sorted(failure_counts.items())))
|
|
282
|
+
if failed_files:
|
|
283
|
+
failure_counts = {}
|
|
284
|
+
for item in failed_files:
|
|
285
|
+
key = f"{item.get('access_status') or 'unknown'} via {item.get('download_strategy') or 'unknown'}"
|
|
286
|
+
failure_counts[key] = failure_counts.get(key, 0) + 1
|
|
287
|
+
lines.append("File failures: " + ", ".join(f"{key}={count}" for key, count in sorted(failure_counts.items())))
|
|
288
|
+
summary = result.get("summary") if isinstance(result.get("summary"), dict) else {}
|
|
289
|
+
if summary.get("text"):
|
|
290
|
+
lines.append(f"Summary: {summary.get('text')}")
|
|
291
|
+
_append_warnings(lines, result.get("warnings"))
|
|
292
|
+
return "\n".join(lines) + "\n"
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def _format_record_logs(result: dict[str, Any]) -> str:
|
|
296
|
+
app = result.get("app") if isinstance(result.get("app"), dict) else {}
|
|
297
|
+
view = result.get("view") if isinstance(result.get("view"), dict) else {}
|
|
298
|
+
record = result.get("record") if isinstance(result.get("record"), dict) else {}
|
|
299
|
+
data_logs = result.get("data_logs") if isinstance(result.get("data_logs"), dict) else {}
|
|
300
|
+
workflow_logs = result.get("workflow_logs") if isinstance(result.get("workflow_logs"), dict) else {}
|
|
301
|
+
integrity = result.get("context_integrity") if isinstance(result.get("context_integrity"), dict) else {}
|
|
302
|
+
lines = [
|
|
303
|
+
f"Status: {result.get('status') or '-'}",
|
|
304
|
+
f"App: {app.get('app_name') or app.get('app_key') or '-'} ({app.get('app_key') or '-'})",
|
|
305
|
+
f"View: {view.get('name') or view.get('view_id') or '-'}",
|
|
306
|
+
f"Record: {record.get('title') or '-'} ({record.get('record_id') or '-'})",
|
|
307
|
+
f"Data logs: {data_logs.get('status') or '-'} / count={data_logs.get('items_count')} / pages={data_logs.get('pages_fetched')} / complete={data_logs.get('complete')}",
|
|
308
|
+
f"Workflow logs: {workflow_logs.get('status') or '-'} / count={workflow_logs.get('items_count')} / pages={workflow_logs.get('pages_fetched')} / complete={workflow_logs.get('complete')}",
|
|
309
|
+
f"Safe for full log conclusion: {integrity.get('safe_for_full_log_conclusion')}",
|
|
310
|
+
]
|
|
311
|
+
if result.get("local_dir"):
|
|
312
|
+
lines.append(f"Local dir: {result.get('local_dir')}")
|
|
313
|
+
if data_logs.get("local_path"):
|
|
314
|
+
lines.append(f"Data logs file: {data_logs.get('local_path')}")
|
|
315
|
+
if workflow_logs.get("local_path"):
|
|
316
|
+
lines.append(f"Workflow logs file: {workflow_logs.get('local_path')}")
|
|
317
|
+
if result.get("summary_path"):
|
|
318
|
+
lines.append(f"Summary file: {result.get('summary_path')}")
|
|
319
|
+
_append_warnings(lines, result.get("warnings"))
|
|
320
|
+
unavailable = result.get("unavailable_context") if isinstance(result.get("unavailable_context"), list) else []
|
|
321
|
+
if unavailable:
|
|
322
|
+
lines.append(f"Unavailable contexts: {len(unavailable)}")
|
|
323
|
+
return "\n".join(lines) + "\n"
|
|
324
|
+
|
|
325
|
+
|
|
227
326
|
def _format_task_list(result: dict[str, Any]) -> str:
|
|
228
327
|
data = result.get("data") if isinstance(result.get("data"), dict) else {}
|
|
229
328
|
items = data.get("items") if isinstance(data.get("items"), list) else []
|
|
@@ -720,10 +819,11 @@ _FORMATTERS = {
|
|
|
720
819
|
"workspace_get": _format_workspace_get,
|
|
721
820
|
"workspace_select": _format_workspace_select,
|
|
722
821
|
"app_list": _format_app_items,
|
|
723
|
-
"app_search": _format_app_items,
|
|
724
822
|
"app_get": _format_app_get,
|
|
725
823
|
"record_list": _format_record_list,
|
|
726
824
|
"record_access": _format_record_access,
|
|
825
|
+
"record_get": _format_record_get,
|
|
826
|
+
"record_logs": _format_record_logs,
|
|
727
827
|
"task_list": _format_task_list,
|
|
728
828
|
"task_workbench": _format_task_workbench,
|
|
729
829
|
"task_get": _format_task_get,
|
|
@@ -8,6 +8,7 @@ from typing import Any, Callable, TextIO
|
|
|
8
8
|
from ..errors import QingflowApiError
|
|
9
9
|
from ..public_surface import cli_public_tool_spec_from_namespace
|
|
10
10
|
from ..response_trim import resolve_cli_tool_name, trim_error_response, trim_public_response
|
|
11
|
+
from ..tools.ai_builder_tools import _attach_builder_apply_envelope
|
|
11
12
|
from .context import CliContext, build_cli_context
|
|
12
13
|
from .formatters import emit_json_result, emit_text_result
|
|
13
14
|
from .commands import register_all_commands
|
|
@@ -16,8 +17,21 @@ from .commands import register_all_commands
|
|
|
16
17
|
Handler = Callable[[argparse.Namespace, CliContext], dict[str, Any]]
|
|
17
18
|
|
|
18
19
|
|
|
20
|
+
class _CliArgumentError(Exception):
|
|
21
|
+
def __init__(self, *, prog: str, message: str, usage: str) -> None:
|
|
22
|
+
super().__init__(message)
|
|
23
|
+
self.prog = prog
|
|
24
|
+
self.message = message
|
|
25
|
+
self.usage = usage
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class _QingflowArgumentParser(argparse.ArgumentParser):
|
|
29
|
+
def error(self, message: str) -> None:
|
|
30
|
+
raise _CliArgumentError(prog=self.prog, message=message, usage=self.format_usage())
|
|
31
|
+
|
|
32
|
+
|
|
19
33
|
def build_parser() -> argparse.ArgumentParser:
|
|
20
|
-
parser =
|
|
34
|
+
parser = _QingflowArgumentParser(prog="qingflow", description="Qingflow CLI")
|
|
21
35
|
parser.add_argument("--profile", default="default", help="会话 profile,默认 default")
|
|
22
36
|
parser.add_argument("--json", action="store_true", help="输出 JSON")
|
|
23
37
|
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
@@ -42,6 +56,21 @@ def run(
|
|
|
42
56
|
normalized_argv = _normalize_global_args(list(argv) if argv is not None else sys.argv[1:])
|
|
43
57
|
try:
|
|
44
58
|
args = parser.parse_args(normalized_argv)
|
|
59
|
+
except _CliArgumentError as exc:
|
|
60
|
+
if _should_force_json_output_argv(normalized_argv):
|
|
61
|
+
payload = {
|
|
62
|
+
"category": "config",
|
|
63
|
+
"status": "failed",
|
|
64
|
+
"error_code": "ARGUMENT_ERROR",
|
|
65
|
+
"message": exc.message,
|
|
66
|
+
"details": {"usage": exc.usage.strip(), "prog": exc.prog},
|
|
67
|
+
}
|
|
68
|
+
payload = _maybe_attach_builder_apply_error_envelope_from_argv(normalized_argv, payload)
|
|
69
|
+
emit_json_result(payload, stream=out)
|
|
70
|
+
return 2
|
|
71
|
+
err.write(exc.usage)
|
|
72
|
+
err.write(f"{exc.prog}: error: {exc.message}\n")
|
|
73
|
+
return 2
|
|
45
74
|
except SystemExit as exc:
|
|
46
75
|
return int(exc.code or 0)
|
|
47
76
|
setattr(args, "_stdin", sys.stdin)
|
|
@@ -51,8 +80,10 @@ def run(
|
|
|
51
80
|
if handler is None:
|
|
52
81
|
parser.print_help(out)
|
|
53
82
|
return 2
|
|
54
|
-
context = context_factory()
|
|
55
83
|
try:
|
|
84
|
+
if _should_force_json_output(args):
|
|
85
|
+
setattr(args, "json", True)
|
|
86
|
+
context = context_factory()
|
|
56
87
|
if not bool(args.json):
|
|
57
88
|
_emit_cli_effective_context_notice(args, context, stream=err)
|
|
58
89
|
result = handler(args, context)
|
|
@@ -60,12 +91,15 @@ def run(
|
|
|
60
91
|
return int(exc.code or 0)
|
|
61
92
|
except RuntimeError as exc:
|
|
62
93
|
payload = trim_error_response(_parse_error_payload(exc))
|
|
94
|
+
payload = _maybe_attach_builder_apply_error_envelope_from_args(args, payload)
|
|
63
95
|
return _emit_error(payload, json_mode=bool(args.json), stdout=out, stderr=err)
|
|
64
96
|
except QingflowApiError as exc:
|
|
65
97
|
payload = trim_error_response(exc.to_dict())
|
|
98
|
+
payload = _maybe_attach_builder_apply_error_envelope_from_args(args, payload)
|
|
66
99
|
return _emit_error(payload, json_mode=bool(args.json), stdout=out, stderr=err)
|
|
67
100
|
finally:
|
|
68
|
-
context
|
|
101
|
+
if "context" in locals():
|
|
102
|
+
context.close()
|
|
69
103
|
|
|
70
104
|
exit_code = _result_exit_code(result)
|
|
71
105
|
trimmed_result = trim_public_response(resolve_cli_tool_name(args), result) if isinstance(result, dict) else result
|
|
@@ -104,6 +138,173 @@ def _normalize_global_args(argv: list[str]) -> list[str]:
|
|
|
104
138
|
return global_args + remaining
|
|
105
139
|
|
|
106
140
|
|
|
141
|
+
def _should_force_json_output(args: argparse.Namespace) -> bool:
|
|
142
|
+
if bool(getattr(args, "force_json_output", False)):
|
|
143
|
+
return True
|
|
144
|
+
if (
|
|
145
|
+
getattr(args, "command", "") == "builder"
|
|
146
|
+
and getattr(args, "builder_app_command", "") == "repair-code-blocks"
|
|
147
|
+
and bool(getattr(args, "apply", False))
|
|
148
|
+
):
|
|
149
|
+
return True
|
|
150
|
+
return False
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _should_force_json_output_argv(argv: list[str]) -> bool:
|
|
154
|
+
tokens = _strip_global_args(argv)
|
|
155
|
+
if not tokens or tokens[0] not in {"builder", "build"}:
|
|
156
|
+
return False
|
|
157
|
+
if len(tokens) < 3:
|
|
158
|
+
return False
|
|
159
|
+
section = tokens[1]
|
|
160
|
+
action = tokens[2]
|
|
161
|
+
if section in {"package", "button", "associated-resource", "associated-resources", "portal", "schema", "layout", "views", "flow", "charts"}:
|
|
162
|
+
return action == "apply"
|
|
163
|
+
if section == "publish":
|
|
164
|
+
return action == "verify"
|
|
165
|
+
if section == "app":
|
|
166
|
+
if action == "release-edit-lock-if-mine":
|
|
167
|
+
return True
|
|
168
|
+
if action == "repair-code-blocks":
|
|
169
|
+
return "--apply" in tokens
|
|
170
|
+
return False
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _maybe_attach_builder_apply_error_envelope_from_args(args: argparse.Namespace, payload: dict[str, Any]) -> dict[str, Any]:
|
|
174
|
+
operation = _builder_apply_operation_from_args(args)
|
|
175
|
+
if not operation:
|
|
176
|
+
return payload
|
|
177
|
+
enriched = dict(payload)
|
|
178
|
+
enriched.setdefault("status", "failed")
|
|
179
|
+
enriched.setdefault("ok", False)
|
|
180
|
+
enriched.setdefault("write_executed", False)
|
|
181
|
+
enriched.setdefault("safe_to_retry", False)
|
|
182
|
+
_copy_arg_identity(enriched, args)
|
|
183
|
+
return _attach_builder_apply_envelope(operation, enriched)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _maybe_attach_builder_apply_error_envelope_from_argv(argv: list[str], payload: dict[str, Any]) -> dict[str, Any]:
|
|
187
|
+
operation = _builder_apply_operation_from_argv(argv)
|
|
188
|
+
if not operation:
|
|
189
|
+
return payload
|
|
190
|
+
enriched = dict(payload)
|
|
191
|
+
enriched.setdefault("status", "failed")
|
|
192
|
+
enriched.setdefault("ok", False)
|
|
193
|
+
enriched.setdefault("write_executed", False)
|
|
194
|
+
enriched.setdefault("safe_to_retry", False)
|
|
195
|
+
_copy_argv_identity(enriched, argv)
|
|
196
|
+
return _attach_builder_apply_envelope(operation, enriched)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _builder_apply_operation_from_args(args: argparse.Namespace) -> str | None:
|
|
200
|
+
if getattr(args, "command", "") not in {"builder", "build"}:
|
|
201
|
+
return None
|
|
202
|
+
section = str(getattr(args, "builder_command", "") or "")
|
|
203
|
+
if section == "package" and getattr(args, "builder_package_command", "") == "apply":
|
|
204
|
+
return "package_apply"
|
|
205
|
+
if section == "button" and getattr(args, "builder_button_command", "") == "apply":
|
|
206
|
+
return "app_custom_buttons_apply"
|
|
207
|
+
if section in {"associated-resource", "associated-resources"} and getattr(args, "builder_associated_resource_command", "") == "apply":
|
|
208
|
+
return "app_associated_resources_apply"
|
|
209
|
+
if section == "portal" and getattr(args, "builder_portal_command", "") == "apply":
|
|
210
|
+
return "portal_apply"
|
|
211
|
+
if section == "schema" and getattr(args, "builder_schema_command", "") == "apply":
|
|
212
|
+
return "app_schema_apply"
|
|
213
|
+
if section == "layout" and getattr(args, "builder_layout_command", "") == "apply":
|
|
214
|
+
return "app_layout_apply"
|
|
215
|
+
if section == "views" and getattr(args, "builder_views_command", "") == "apply":
|
|
216
|
+
return "app_views_apply"
|
|
217
|
+
if section == "flow" and getattr(args, "builder_flow_command", "") == "apply":
|
|
218
|
+
return "app_flow_apply"
|
|
219
|
+
if section == "charts" and getattr(args, "builder_charts_command", "") == "apply":
|
|
220
|
+
return "app_charts_apply"
|
|
221
|
+
if section == "publish" and getattr(args, "builder_publish_command", "") == "verify":
|
|
222
|
+
return "app_publish_verify"
|
|
223
|
+
return None
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _builder_apply_operation_from_argv(argv: list[str]) -> str | None:
|
|
227
|
+
tokens = _strip_global_args(argv)
|
|
228
|
+
if not tokens or tokens[0] not in {"builder", "build"} or len(tokens) < 3:
|
|
229
|
+
return None
|
|
230
|
+
section = tokens[1]
|
|
231
|
+
action = tokens[2]
|
|
232
|
+
if action != "apply" and not (section == "publish" and action == "verify"):
|
|
233
|
+
return None
|
|
234
|
+
mapping = {
|
|
235
|
+
"package": "package_apply",
|
|
236
|
+
"button": "app_custom_buttons_apply",
|
|
237
|
+
"associated-resource": "app_associated_resources_apply",
|
|
238
|
+
"associated-resources": "app_associated_resources_apply",
|
|
239
|
+
"portal": "portal_apply",
|
|
240
|
+
"schema": "app_schema_apply",
|
|
241
|
+
"layout": "app_layout_apply",
|
|
242
|
+
"views": "app_views_apply",
|
|
243
|
+
"flow": "app_flow_apply",
|
|
244
|
+
"charts": "app_charts_apply",
|
|
245
|
+
"publish": "app_publish_verify",
|
|
246
|
+
}
|
|
247
|
+
return mapping.get(section)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def _copy_arg_identity(payload: dict[str, Any], args: argparse.Namespace) -> None:
|
|
251
|
+
for attr, key in (
|
|
252
|
+
("app_key", "app_key"),
|
|
253
|
+
("app_name", "app_name"),
|
|
254
|
+
("app_title", "app_title"),
|
|
255
|
+
("package_id", "package_id"),
|
|
256
|
+
("dash_key", "dash_key"),
|
|
257
|
+
("dash_name", "dash_name"),
|
|
258
|
+
):
|
|
259
|
+
value = getattr(args, attr, None)
|
|
260
|
+
if value not in (None, ""):
|
|
261
|
+
payload.setdefault(key, value)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def _copy_argv_identity(payload: dict[str, Any], argv: list[str]) -> None:
|
|
265
|
+
tokens = _strip_global_args(argv)
|
|
266
|
+
option_to_key = {
|
|
267
|
+
"--app-key": "app_key",
|
|
268
|
+
"--app-name": "app_name",
|
|
269
|
+
"--app-title": "app_title",
|
|
270
|
+
"--package-id": "package_id",
|
|
271
|
+
"--dash-key": "dash_key",
|
|
272
|
+
"--dash-name": "dash_name",
|
|
273
|
+
}
|
|
274
|
+
index = 0
|
|
275
|
+
while index < len(tokens):
|
|
276
|
+
token = tokens[index]
|
|
277
|
+
if token in option_to_key and index + 1 < len(tokens):
|
|
278
|
+
payload.setdefault(option_to_key[token], tokens[index + 1])
|
|
279
|
+
index += 2
|
|
280
|
+
continue
|
|
281
|
+
for option, key in option_to_key.items():
|
|
282
|
+
prefix = f"{option}="
|
|
283
|
+
if token.startswith(prefix):
|
|
284
|
+
payload.setdefault(key, token[len(prefix) :])
|
|
285
|
+
break
|
|
286
|
+
index += 1
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def _strip_global_args(argv: list[str]) -> list[str]:
|
|
290
|
+
stripped: list[str] = []
|
|
291
|
+
index = 0
|
|
292
|
+
while index < len(argv):
|
|
293
|
+
token = argv[index]
|
|
294
|
+
if token == "--json":
|
|
295
|
+
index += 1
|
|
296
|
+
continue
|
|
297
|
+
if token == "--profile":
|
|
298
|
+
index += 2
|
|
299
|
+
continue
|
|
300
|
+
if token.startswith("--profile="):
|
|
301
|
+
index += 1
|
|
302
|
+
continue
|
|
303
|
+
stripped.append(token)
|
|
304
|
+
index += 1
|
|
305
|
+
return stripped
|
|
306
|
+
|
|
307
|
+
|
|
107
308
|
def _parse_error_payload(exc: RuntimeError) -> dict[str, Any]:
|
|
108
309
|
raw = str(exc)
|
|
109
310
|
try:
|
|
@@ -38,7 +38,6 @@ USER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
|
|
|
38
38
|
PublicToolSpec(USER_DOMAIN, "workspace_get", ("workspace_get",), ("workspace", "get")),
|
|
39
39
|
PublicToolSpec(USER_DOMAIN, "workspace_select", ("workspace_select",), ("workspace", "select")),
|
|
40
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
41
|
PublicToolSpec(USER_DOMAIN, "app_get", ("app_get",), ("app", "get"), cli_show_effective_context=True),
|
|
43
42
|
PublicToolSpec(USER_DOMAIN, "portal_list", ("portal_list",), ("portal", "list"), cli_show_effective_context=True),
|
|
44
43
|
PublicToolSpec(USER_DOMAIN, "portal_get", ("portal_get",), ("portal", "get"), cli_show_effective_context=True),
|
|
@@ -78,12 +77,13 @@ USER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
|
|
|
78
77
|
("record_code_block_schema_get_public",),
|
|
79
78
|
("record", "schema", "code-block"),
|
|
80
79
|
),
|
|
81
|
-
PublicToolSpec(USER_DOMAIN, "record_member_candidates", ("record_member_candidates",),
|
|
82
|
-
PublicToolSpec(USER_DOMAIN, "record_department_candidates", ("record_department_candidates",),
|
|
83
|
-
PublicToolSpec(USER_DOMAIN, "record_analyze", ("record_analyze",), ("record", "analyze")),
|
|
80
|
+
PublicToolSpec(USER_DOMAIN, "record_member_candidates", ("record_member_candidates",), ("record", "member-candidates")),
|
|
81
|
+
PublicToolSpec(USER_DOMAIN, "record_department_candidates", ("record_department_candidates",), ("record", "department-candidates")),
|
|
82
|
+
PublicToolSpec(USER_DOMAIN, "record_analyze", ("record_analyze",), ("record", "analyze"), mcp_public=False),
|
|
84
83
|
PublicToolSpec(USER_DOMAIN, "record_list", ("record_list",), ("record", "list"), cli_show_effective_context=True),
|
|
85
84
|
PublicToolSpec(USER_DOMAIN, "record_access", ("record_access",), ("record", "access"), cli_show_effective_context=True),
|
|
86
85
|
PublicToolSpec(USER_DOMAIN, "record_get", ("record_get_public",), ("record", "get"), cli_show_effective_context=True),
|
|
86
|
+
PublicToolSpec(USER_DOMAIN, "record_logs_get", ("record_logs_get",), ("record", "logs"), cli_show_effective_context=True),
|
|
87
87
|
PublicToolSpec(USER_DOMAIN, "record_insert", ("record_insert_public",), ("record", "insert"), cli_show_effective_context=True, cli_context_write=True),
|
|
88
88
|
PublicToolSpec(USER_DOMAIN, "record_update", ("record_update_public",), ("record", "update"), cli_show_effective_context=True, cli_context_write=True),
|
|
89
89
|
PublicToolSpec(USER_DOMAIN, "record_delete", ("record_delete_public",), ("record", "delete"), cli_show_effective_context=True, cli_context_write=True),
|
|
@@ -127,6 +127,8 @@ BUILDER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
|
|
|
127
127
|
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),
|
|
128
128
|
PublicToolSpec(BUILDER_DOMAIN, "feedback_submit", ("feedback_submit",), ("builder", "feedback", "submit"), has_contract=True),
|
|
129
129
|
PublicToolSpec(BUILDER_DOMAIN, "builder_tool_contract", ("builder_tool_contract",), ("builder", "contract"), has_contract=False),
|
|
130
|
+
PublicToolSpec(BUILDER_DOMAIN, "workspace_icon_catalog_get", ("workspace_icon_catalog_get",), ("builder", "icon", "catalog"), has_contract=True, cli_show_effective_context=True),
|
|
131
|
+
PublicToolSpec(BUILDER_DOMAIN, "package_list", ("package_list",), ("builder", "package", "list"), has_contract=True, cli_show_effective_context=True),
|
|
130
132
|
PublicToolSpec(BUILDER_DOMAIN, "package_get", ("package_get",), ("builder", "package", "get"), has_contract=True, cli_show_effective_context=True),
|
|
131
133
|
PublicToolSpec(BUILDER_DOMAIN, "package_apply", ("package_apply",), ("builder", "package", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
132
134
|
PublicToolSpec(BUILDER_DOMAIN, "solution_install", ("solution_install",), ("builder", "solution", "install"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
@@ -136,11 +138,10 @@ BUILDER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
|
|
|
136
138
|
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),
|
|
137
139
|
PublicToolSpec(BUILDER_DOMAIN, "app_resolve", ("app_resolve",), ("builder", "app", "resolve"), has_contract=True, cli_show_effective_context=True),
|
|
138
140
|
PublicToolSpec(BUILDER_DOMAIN, "button_style_catalog_get", ("button_style_catalog_get",), ("builder", "button", "catalog"), has_contract=True, cli_show_effective_context=True),
|
|
139
|
-
PublicToolSpec(BUILDER_DOMAIN, "
|
|
140
|
-
PublicToolSpec(BUILDER_DOMAIN, "
|
|
141
|
-
PublicToolSpec(BUILDER_DOMAIN, "
|
|
142
|
-
PublicToolSpec(BUILDER_DOMAIN, "
|
|
143
|
-
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),
|
|
141
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_get_buttons", ("app_get_buttons",), ("builder", "button", "get"), has_contract=True, cli_show_effective_context=True),
|
|
142
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_custom_buttons_apply", ("app_custom_buttons_apply",), ("builder", "button", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
143
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_get_associated_resources", ("app_get_associated_resources",), ("builder", "associated-resource", "get"), has_contract=True, cli_show_effective_context=True),
|
|
144
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_associated_resources_apply", ("app_associated_resources_apply",), ("builder", "associated-resource", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
144
145
|
PublicToolSpec(BUILDER_DOMAIN, "app_get", ("app_get",), ("builder", "app", "get", "summary"), has_contract=True, cli_show_effective_context=True),
|
|
145
146
|
PublicToolSpec(BUILDER_DOMAIN, "app_get_fields", ("app_get_fields",), ("builder", "app", "get", "fields"), has_contract=True, cli_show_effective_context=True),
|
|
146
147
|
PublicToolSpec(BUILDER_DOMAIN, "app_repair_code_blocks", ("app_repair_code_blocks",), ("builder", "app", "repair-code-blocks"), has_contract=True, cli_show_effective_context=True),
|
|
@@ -154,6 +155,8 @@ BUILDER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
|
|
|
154
155
|
PublicToolSpec(BUILDER_DOMAIN, "chart_get", ("chart_get",), ("builder", "chart", "get"), has_contract=True, cli_show_effective_context=True),
|
|
155
156
|
PublicToolSpec(BUILDER_DOMAIN, "app_schema_apply", ("app_schema_apply",), ("builder", "schema", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
156
157
|
PublicToolSpec(BUILDER_DOMAIN, "app_layout_apply", ("app_layout_apply",), ("builder", "layout", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
158
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_flow_get_schema", ("app_flow_get_schema",), ("builder", "flow", "schema"), has_contract=True, cli_show_effective_context=True),
|
|
159
|
+
PublicToolSpec(BUILDER_DOMAIN, "app_flow_get", ("app_flow_get",), ("builder", "flow", "get"), has_contract=True, cli_show_effective_context=True),
|
|
157
160
|
PublicToolSpec(BUILDER_DOMAIN, "app_flow_apply", ("app_flow_apply",), ("builder", "flow", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
158
161
|
PublicToolSpec(BUILDER_DOMAIN, "app_views_apply", ("app_views_apply",), ("builder", "views", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|
|
159
162
|
PublicToolSpec(BUILDER_DOMAIN, "app_charts_apply", ("app_charts_apply",), ("builder", "charts", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
|