@josephyan/qingflow-app-user-mcp 0.2.0-beta.995 → 0.2.0-beta.996
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
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
Install:
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npm install @josephyan/qingflow-app-user-mcp@0.2.0-beta.
|
|
6
|
+
npm install @josephyan/qingflow-app-user-mcp@0.2.0-beta.996
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
Run:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npx -y -p @josephyan/qingflow-app-user-mcp@0.2.0-beta.
|
|
12
|
+
npx -y -p @josephyan/qingflow-app-user-mcp@0.2.0-beta.996 qingflow-app-user-mcp
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Environment:
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -40,7 +40,11 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
|
|
|
40
40
|
action.add_argument("--action", required=True)
|
|
41
41
|
action.add_argument("--payload-file")
|
|
42
42
|
action.add_argument("--fields-file")
|
|
43
|
-
action.set_defaults(
|
|
43
|
+
action.set_defaults(
|
|
44
|
+
handler=_handle_action,
|
|
45
|
+
format_hint="task_action_execute",
|
|
46
|
+
hide_effective_context_line=True,
|
|
47
|
+
)
|
|
44
48
|
|
|
45
49
|
report = task_subparsers.add_parser("report", help="读取待办关联报表详情;推荐直接传 --task-id")
|
|
46
50
|
report.add_argument("--task-id")
|
|
@@ -249,6 +249,30 @@ def _format_task_get(result: dict[str, Any]) -> str:
|
|
|
249
249
|
return "\n".join(lines) + "\n"
|
|
250
250
|
|
|
251
251
|
|
|
252
|
+
def _format_task_action(result: dict[str, Any]) -> str:
|
|
253
|
+
data = result.get("data") if isinstance(result.get("data"), dict) else {}
|
|
254
|
+
action = str(data.get("action") or "").strip().lower()
|
|
255
|
+
status = str(result.get("status") or "").strip().lower()
|
|
256
|
+
|
|
257
|
+
if status == "failed" or result.get("ok") is False:
|
|
258
|
+
lines = [_task_action_failure_label(action)]
|
|
259
|
+
reason = _task_action_failure_reason(result)
|
|
260
|
+
if reason:
|
|
261
|
+
lines.append(f"原因:{reason}")
|
|
262
|
+
debug_lines = _task_action_debug_lines(result)
|
|
263
|
+
if debug_lines:
|
|
264
|
+
lines.append("调试信息:")
|
|
265
|
+
lines.extend(f"- {line}" for line in debug_lines)
|
|
266
|
+
return "\n".join(lines) + "\n"
|
|
267
|
+
|
|
268
|
+
if status == "partial_success":
|
|
269
|
+
lines = [_task_action_success_label(action)]
|
|
270
|
+
lines.append(f"说明:{_task_action_partial_success_message(result)}")
|
|
271
|
+
return "\n".join(lines) + "\n"
|
|
272
|
+
|
|
273
|
+
return _task_action_success_label(action) + "\n"
|
|
274
|
+
|
|
275
|
+
|
|
252
276
|
def _format_task_associated_report_detail(result: dict[str, Any]) -> str:
|
|
253
277
|
data = result.get("data") if isinstance(result.get("data"), dict) else {}
|
|
254
278
|
selection = data.get("selection") if isinstance(data.get("selection"), dict) else {}
|
|
@@ -416,6 +440,121 @@ def _first_present(payload: dict[str, Any], *keys: str) -> Any:
|
|
|
416
440
|
return None
|
|
417
441
|
|
|
418
442
|
|
|
443
|
+
def _task_action_success_label(action: str) -> str:
|
|
444
|
+
return {
|
|
445
|
+
"approve": "已通过",
|
|
446
|
+
"reject": "已驳回",
|
|
447
|
+
"rollback": "已退回",
|
|
448
|
+
"transfer": "已转交",
|
|
449
|
+
"save_only": "已保存",
|
|
450
|
+
"urge": "已催办",
|
|
451
|
+
}.get(action, "已执行")
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def _task_action_failure_label(action: str) -> str:
|
|
455
|
+
return {
|
|
456
|
+
"approve": "审批失败",
|
|
457
|
+
"reject": "驳回失败",
|
|
458
|
+
"rollback": "退回失败",
|
|
459
|
+
"transfer": "转交失败",
|
|
460
|
+
"save_only": "保存失败",
|
|
461
|
+
"urge": "催办失败",
|
|
462
|
+
}.get(action, "执行失败")
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
def _task_action_partial_success_message(result: dict[str, Any]) -> str:
|
|
466
|
+
error_code = str(result.get("error_code") or "").strip().upper()
|
|
467
|
+
if error_code == "WORKFLOW_CONTINUATION_UNVERIFIED":
|
|
468
|
+
return "动作已提交,但暂未完成后续流程验证。可使用 --json 查看详细信息。"
|
|
469
|
+
if error_code == "TASK_ALREADY_PROCESSED":
|
|
470
|
+
return "当前待办已不可操作,系统判断流程可能已被其他人处理。可使用 --json 查看详细信息。"
|
|
471
|
+
warnings = result.get("warnings")
|
|
472
|
+
if isinstance(warnings, list):
|
|
473
|
+
for warning in warnings:
|
|
474
|
+
if not isinstance(warning, dict):
|
|
475
|
+
continue
|
|
476
|
+
code = str(warning.get("code") or "").strip().upper()
|
|
477
|
+
if code == "TASK_ALREADY_PROCESSED_UNCONFIRMED_ACTOR":
|
|
478
|
+
return "当前待办已不可操作,系统判断流程可能已被其他人处理。可使用 --json 查看详细信息。"
|
|
479
|
+
if code == "WORKFLOW_CONTINUATION_UNVERIFIED":
|
|
480
|
+
return "动作已提交,但暂未完成后续流程验证。可使用 --json 查看详细信息。"
|
|
481
|
+
return "动作已提交,但结果验证不完整。可使用 --json 查看详细信息。"
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
def _task_action_failure_reason(result: dict[str, Any]) -> str | None:
|
|
485
|
+
error_code = str(result.get("error_code") or "").strip().upper()
|
|
486
|
+
mapped_error = {
|
|
487
|
+
"TASK_CONTEXT_VISIBILITY_UNVERIFIED": "当前待办已不可操作,且系统未能确认是否已被处理。",
|
|
488
|
+
"TASK_SAVE_ONLY_VERIFICATION_FAILED": "保存请求已发送,但未能确认字段是否全部保存成功。",
|
|
489
|
+
"WORKFLOW_CONTINUATION_UNVERIFIED": "动作已提交,但暂未验证到流程继续推进。",
|
|
490
|
+
"TASK_ALREADY_PROCESSED": "当前待办已不可操作,系统判断流程可能已被其他人处理。",
|
|
491
|
+
}.get(error_code)
|
|
492
|
+
if mapped_error:
|
|
493
|
+
return mapped_error
|
|
494
|
+
|
|
495
|
+
warnings = result.get("warnings")
|
|
496
|
+
if isinstance(warnings, list):
|
|
497
|
+
for warning in warnings:
|
|
498
|
+
if isinstance(warning, dict) and warning.get("message"):
|
|
499
|
+
return str(warning.get("message"))
|
|
500
|
+
|
|
501
|
+
data = result.get("data") if isinstance(result.get("data"), dict) else {}
|
|
502
|
+
transport_error = data.get("transport_error") if isinstance(data.get("transport_error"), dict) else {}
|
|
503
|
+
backend_code = transport_error.get("backend_code")
|
|
504
|
+
http_status = transport_error.get("http_status")
|
|
505
|
+
if backend_code not in (None, ""):
|
|
506
|
+
return f"后端返回错误码 {backend_code}。"
|
|
507
|
+
if http_status not in (None, ""):
|
|
508
|
+
return f"请求返回 HTTP {http_status}。"
|
|
509
|
+
|
|
510
|
+
verification = result.get("verification") if isinstance(result.get("verification"), dict) else {}
|
|
511
|
+
record_state_error = verification.get("record_state_error") if isinstance(verification.get("record_state_error"), dict) else {}
|
|
512
|
+
backend_code = record_state_error.get("backend_code")
|
|
513
|
+
http_status = record_state_error.get("http_status")
|
|
514
|
+
if backend_code not in (None, ""):
|
|
515
|
+
return f"后端返回错误码 {backend_code}。"
|
|
516
|
+
if http_status not in (None, ""):
|
|
517
|
+
return f"请求返回 HTTP {http_status}。"
|
|
518
|
+
if error_code:
|
|
519
|
+
return f"错误码:{error_code}"
|
|
520
|
+
return None
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
def _task_action_debug_lines(result: dict[str, Any]) -> list[str]:
|
|
524
|
+
lines: list[str] = []
|
|
525
|
+
error_code = result.get("error_code")
|
|
526
|
+
if error_code not in (None, ""):
|
|
527
|
+
lines.append(f"error_code: {error_code}")
|
|
528
|
+
|
|
529
|
+
data = result.get("data") if isinstance(result.get("data"), dict) else {}
|
|
530
|
+
transport_error = data.get("transport_error") if isinstance(data.get("transport_error"), dict) else {}
|
|
531
|
+
for key in ("backend_code", "http_status", "category"):
|
|
532
|
+
value = transport_error.get(key)
|
|
533
|
+
if value not in (None, ""):
|
|
534
|
+
lines.append(f"{key}: {value}")
|
|
535
|
+
|
|
536
|
+
verification = result.get("verification") if isinstance(result.get("verification"), dict) else {}
|
|
537
|
+
for key in (
|
|
538
|
+
"runtime_continuation_verified",
|
|
539
|
+
"task_context_visibility_verified",
|
|
540
|
+
"fields_saved_verified",
|
|
541
|
+
"task_still_actionable",
|
|
542
|
+
"workflow_not_advanced",
|
|
543
|
+
"record_state_readable",
|
|
544
|
+
):
|
|
545
|
+
if key in verification and verification.get(key) is not None:
|
|
546
|
+
lines.append(f"{key}: {verification.get(key)}")
|
|
547
|
+
|
|
548
|
+
record_state_error = verification.get("record_state_error") if isinstance(verification.get("record_state_error"), dict) else {}
|
|
549
|
+
for key in ("backend_code", "http_status", "category"):
|
|
550
|
+
value = record_state_error.get(key)
|
|
551
|
+
if value not in (None, ""):
|
|
552
|
+
entry = f"record_state_{key}: {value}"
|
|
553
|
+
if entry not in lines:
|
|
554
|
+
lines.append(entry)
|
|
555
|
+
return lines
|
|
556
|
+
|
|
557
|
+
|
|
419
558
|
_FORMATTERS = {
|
|
420
559
|
"auth_whoami": _format_whoami,
|
|
421
560
|
"workspace_list": _format_workspace_list,
|
|
@@ -426,6 +565,7 @@ _FORMATTERS = {
|
|
|
426
565
|
"record_list": _format_record_list,
|
|
427
566
|
"task_list": _format_task_list,
|
|
428
567
|
"task_get": _format_task_get,
|
|
568
|
+
"task_action_execute": _format_task_action,
|
|
429
569
|
"task_associated_report_detail_get": _format_task_associated_report_detail,
|
|
430
570
|
"import_verify": _format_import_verify,
|
|
431
571
|
"import_status": _format_import_status,
|
|
@@ -152,6 +152,7 @@ def _emit_cli_effective_context_notice(args: argparse.Namespace, context: CliCon
|
|
|
152
152
|
spec = cli_public_tool_spec_from_namespace(args)
|
|
153
153
|
if spec is None or not spec.cli_show_effective_context:
|
|
154
154
|
return
|
|
155
|
+
hide_context_line = bool(getattr(args, "hide_effective_context_line", False))
|
|
155
156
|
sessions = getattr(context, "sessions", None)
|
|
156
157
|
if sessions is None or not hasattr(sessions, "get_profile"):
|
|
157
158
|
return
|
|
@@ -168,9 +169,13 @@ def _emit_cli_effective_context_notice(args: argparse.Namespace, context: CliCon
|
|
|
168
169
|
workspace_label = f"{workspace_name} ({workspace_id})"
|
|
169
170
|
else:
|
|
170
171
|
workspace_label = str(workspace_id)
|
|
171
|
-
lines
|
|
172
|
+
lines: list[str] = []
|
|
173
|
+
if not hide_context_line:
|
|
174
|
+
lines.append(f"Context: profile={profile_name} workspace={workspace_label}")
|
|
172
175
|
if spec.cli_context_write and profile_name == "default":
|
|
173
176
|
lines.append("Warning: using default profile for a workspace-sensitive write command")
|
|
177
|
+
if not lines:
|
|
178
|
+
return
|
|
174
179
|
stream.write("\n".join(lines) + "\n")
|
|
175
180
|
|
|
176
181
|
|