@josephyan/qingflow-app-builder-mcp 0.2.0-beta.98 → 0.2.0-beta.981
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/qingflow_mcp/__init__.py +1 -1
- package/src/qingflow_mcp/cli/formatters.py +22 -14
- package/src/qingflow_mcp/response_trim.py +324 -7
- package/src/qingflow_mcp/tools/record_tools.py +1 -0
- package/src/qingflow_mcp/tools/task_context_tools.py +13 -15
package/README.md
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
Install:
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npm install @josephyan/qingflow-app-builder-mcp@0.2.0-beta.
|
|
6
|
+
npm install @josephyan/qingflow-app-builder-mcp@0.2.0-beta.981
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
Run:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npx -y -p @josephyan/qingflow-app-builder-mcp@0.2.0-beta.
|
|
12
|
+
npx -y -p @josephyan/qingflow-app-builder-mcp@0.2.0-beta.981 qingflow-app-builder-mcp
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Environment:
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -177,10 +177,10 @@ def _format_task_list(result: dict[str, Any]) -> str:
|
|
|
177
177
|
str(item.get("app_key") or ""),
|
|
178
178
|
str(item.get("record_id") or ""),
|
|
179
179
|
str(item.get("workflow_node_id") or ""),
|
|
180
|
-
str(item.get("
|
|
180
|
+
str(item.get("workflow_node_name") or ""),
|
|
181
181
|
]
|
|
182
182
|
)
|
|
183
|
-
output = _render_titled_table("Tasks", ["app_key", "record_id", "node_id", "
|
|
183
|
+
output = _render_titled_table("Tasks", ["app_key", "record_id", "node_id", "node_name"], rows)
|
|
184
184
|
lines = output.rstrip("\n").split("\n")
|
|
185
185
|
_append_warnings(lines, result.get("warnings"))
|
|
186
186
|
return "\n".join(lines) + "\n"
|
|
@@ -193,11 +193,13 @@ def _format_task_get(result: dict[str, Any]) -> str:
|
|
|
193
193
|
editable_fields = data.get("editable_fields") if isinstance(data.get("editable_fields"), list) else []
|
|
194
194
|
available_actions = data.get("available_actions") if isinstance(data.get("available_actions"), list) else []
|
|
195
195
|
extras = data.get("extras") if isinstance(data.get("extras"), dict) else {}
|
|
196
|
+
initiator = task.get("initiator") if isinstance(task.get("initiator"), dict) else {}
|
|
197
|
+
initiator_label = initiator.get("displayName") or initiator.get("email") or "-"
|
|
196
198
|
lines = [
|
|
197
199
|
f"Task: {task.get('app_key') or '-'} / {task.get('record_id') or '-'} / {task.get('workflow_node_id') or '-'}",
|
|
198
200
|
f"Node: {task.get('workflow_node_name') or '-'}",
|
|
199
201
|
f"App: {task.get('app_name') or '-'}",
|
|
200
|
-
f"Initiator: {
|
|
202
|
+
f"Initiator: {initiator_label}",
|
|
201
203
|
f"Apply Status: {record_summary.get('apply_status')}",
|
|
202
204
|
f"Available Actions: {', '.join(str(item) for item in available_actions) or '-'}",
|
|
203
205
|
f"Editable Fields: {len(editable_fields)}",
|
|
@@ -238,17 +240,22 @@ def _format_import_verify(result: dict[str, Any]) -> str:
|
|
|
238
240
|
f"App Key: {result.get('app_key') or '-'}",
|
|
239
241
|
f"File: {result.get('file_name') or result.get('file_path') or '-'}",
|
|
240
242
|
f"Can Import: {result.get('can_import')}",
|
|
241
|
-
f"Apply Rows: {result.get('apply_rows')}",
|
|
242
243
|
f"Verification ID: {result.get('verification_id') or '-'}",
|
|
243
244
|
]
|
|
244
|
-
|
|
245
|
-
if
|
|
246
|
-
lines.append(
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
245
|
+
issue_summary = result.get("issue_summary") if isinstance(result.get("issue_summary"), dict) else {}
|
|
246
|
+
if issue_summary:
|
|
247
|
+
lines.append(
|
|
248
|
+
"Issues: "
|
|
249
|
+
f"total={issue_summary.get('total', 0)}, "
|
|
250
|
+
f"errors={issue_summary.get('errors', 0)}, "
|
|
251
|
+
f"warnings={issue_summary.get('warnings', 0)}"
|
|
252
|
+
)
|
|
253
|
+
sample = issue_summary.get("sample") if isinstance(issue_summary.get("sample"), list) else []
|
|
254
|
+
if sample:
|
|
255
|
+
lines.append("Issue Samples:")
|
|
256
|
+
for item in sample:
|
|
257
|
+
if isinstance(item, dict):
|
|
258
|
+
lines.append(f"- {item.get('code') or 'ISSUE'}: {item.get('message') or ''}".rstrip())
|
|
252
259
|
_append_warnings(lines, result.get("warnings"))
|
|
253
260
|
_append_verification(lines, result.get("verification"))
|
|
254
261
|
return "\n".join(lines) + "\n"
|
|
@@ -259,8 +266,9 @@ def _format_import_status(result: dict[str, Any]) -> str:
|
|
|
259
266
|
f"Status: {result.get('status') or '-'}",
|
|
260
267
|
f"Import ID: {result.get('import_id') or '-'}",
|
|
261
268
|
f"Process ID: {result.get('process_id_str') or '-'}",
|
|
262
|
-
f"
|
|
263
|
-
f"
|
|
269
|
+
f"Total Rows: {result.get('total') or 0}",
|
|
270
|
+
f"Finished Rows: {result.get('finished') or 0}",
|
|
271
|
+
f"Failed Rows: {result.get('failed') or 0}",
|
|
264
272
|
f"Progress: {result.get('progress') or '-'}",
|
|
265
273
|
]
|
|
266
274
|
_append_warnings(lines, result.get("warnings"))
|
|
@@ -293,34 +293,150 @@ def _trim_file_upload_local(payload: JSONObject) -> None:
|
|
|
293
293
|
|
|
294
294
|
|
|
295
295
|
def _trim_import_schema(payload: JSONObject) -> None:
|
|
296
|
-
|
|
296
|
+
columns: list[JSONObject] | None = None
|
|
297
|
+
if isinstance(payload.get("columns"), list):
|
|
298
|
+
columns = [item for item in payload.get("columns", []) if isinstance(item, dict)]
|
|
299
|
+
elif isinstance(payload.get("expected_columns"), list):
|
|
300
|
+
columns = [item for item in payload.get("expected_columns", []) if isinstance(item, dict)]
|
|
301
|
+
if columns is not None:
|
|
302
|
+
payload["columns"] = [_compact_import_column(item) for item in columns]
|
|
303
|
+
payload.pop("expected_columns", None)
|
|
304
|
+
payload.pop("schema_fingerprint", None)
|
|
305
|
+
payload.pop("import_capability", None)
|
|
306
|
+
payload.pop("request_route", None)
|
|
307
|
+
payload.pop("verification", None)
|
|
308
|
+
|
|
309
|
+
if _looks_like_import_verify(payload):
|
|
310
|
+
_trim_import_verify_payload(payload)
|
|
311
|
+
return
|
|
312
|
+
if "applied_repairs" in payload or "repaired_file_path" in payload:
|
|
313
|
+
_trim_import_repair_payload(payload)
|
|
314
|
+
return
|
|
315
|
+
if "template_url" in payload or "downloaded_to_path" in payload:
|
|
316
|
+
_trim_import_template_payload(payload)
|
|
317
|
+
return
|
|
318
|
+
if "import_id" in payload or "process_id_str" in payload:
|
|
319
|
+
_trim_import_status_payload(payload)
|
|
320
|
+
return
|
|
297
321
|
|
|
298
322
|
|
|
299
323
|
def _trim_record_schema(payload: JSONObject) -> None:
|
|
300
324
|
payload.pop("legacy_schema", None)
|
|
325
|
+
template_map = payload.get("payload_template")
|
|
326
|
+
if not isinstance(template_map, dict):
|
|
327
|
+
template_map = None
|
|
328
|
+
|
|
329
|
+
if "writable_fields" in payload:
|
|
330
|
+
writable_fields = payload.get("writable_fields")
|
|
331
|
+
payload.pop("writable_fields", None)
|
|
332
|
+
required_fields: list[JSONObject] = []
|
|
333
|
+
optional_fields: list[JSONObject] = []
|
|
334
|
+
if isinstance(writable_fields, list):
|
|
335
|
+
for item in writable_fields:
|
|
336
|
+
compact = _compact_schema_field(item, template_map=template_map)
|
|
337
|
+
if not compact:
|
|
338
|
+
continue
|
|
339
|
+
if compact.get("required") is True:
|
|
340
|
+
required_fields.append(compact)
|
|
341
|
+
else:
|
|
342
|
+
optional_fields.append(compact)
|
|
343
|
+
payload["required_fields"] = required_fields
|
|
344
|
+
payload["optional_fields"] = optional_fields
|
|
345
|
+
|
|
346
|
+
for key in ("required_fields", "optional_fields", "runtime_linked_required_fields", "fields"):
|
|
347
|
+
if key in payload:
|
|
348
|
+
payload[key] = _compact_schema_fields(payload.get(key), template_map=template_map)
|
|
349
|
+
|
|
350
|
+
for key in ("suggested_dimensions", "suggested_metrics", "suggested_time_fields"):
|
|
351
|
+
if isinstance(payload.get(key), list):
|
|
352
|
+
payload[key] = [
|
|
353
|
+
_pick(item, ("field_id", "title")) for item in payload.get(key) if isinstance(item, dict)
|
|
354
|
+
]
|
|
355
|
+
|
|
356
|
+
for key in ("workflow_node", "view_resolution", "field_count", "record_context_probe", "view_probe_summary", "ambiguous_fields"):
|
|
357
|
+
payload.pop(key, None)
|
|
301
358
|
|
|
302
359
|
|
|
303
360
|
def _trim_record_write(payload: JSONObject) -> None:
|
|
304
361
|
data = payload.get("data")
|
|
305
362
|
if not isinstance(data, dict):
|
|
306
363
|
return
|
|
364
|
+
data.pop("debug", None)
|
|
307
365
|
data.pop("normalized_payload", None)
|
|
308
366
|
data.pop("human_review", None)
|
|
309
367
|
data.pop("action", None)
|
|
368
|
+
resource = _compact_record_resource(data.get("resource"))
|
|
369
|
+
if resource:
|
|
370
|
+
data["resource"] = resource
|
|
371
|
+
else:
|
|
372
|
+
data.pop("resource", None)
|
|
310
373
|
|
|
311
374
|
|
|
312
375
|
def _trim_record_get(payload: JSONObject) -> None:
|
|
313
|
-
|
|
314
|
-
|
|
376
|
+
data = payload.get("data")
|
|
377
|
+
if not isinstance(data, dict):
|
|
378
|
+
return
|
|
379
|
+
compact: dict[str, Any] = {}
|
|
380
|
+
app_key = data.get("app_key")
|
|
381
|
+
if app_key:
|
|
382
|
+
compact["app_key"] = app_key
|
|
383
|
+
record_id = data.get("record_id")
|
|
384
|
+
if record_id not in (None, ""):
|
|
385
|
+
compact["record_id"] = str(record_id)
|
|
386
|
+
record = data.get("record")
|
|
387
|
+
if isinstance(record, dict):
|
|
388
|
+
compact["record"] = record
|
|
389
|
+
payload["data"] = compact
|
|
315
390
|
|
|
316
391
|
|
|
317
392
|
def _trim_record_list(payload: JSONObject) -> None:
|
|
318
|
-
|
|
319
|
-
|
|
393
|
+
data = payload.get("data")
|
|
394
|
+
if not isinstance(data, dict):
|
|
395
|
+
return
|
|
396
|
+
pagination = data.get("pagination") if isinstance(data.get("pagination"), dict) else {}
|
|
397
|
+
returned_items = pagination.get("returned_items")
|
|
398
|
+
result_amount = pagination.get("result_amount")
|
|
399
|
+
limit = pagination.get("limit")
|
|
400
|
+
truncated = False
|
|
401
|
+
if isinstance(result_amount, int) and isinstance(returned_items, int):
|
|
402
|
+
truncated = result_amount > returned_items
|
|
403
|
+
compact_pagination = {
|
|
404
|
+
"loaded": True,
|
|
405
|
+
"page_size": limit,
|
|
406
|
+
"fetched_pages": 1,
|
|
407
|
+
"reported_total": result_amount,
|
|
408
|
+
"truncated": truncated,
|
|
409
|
+
}
|
|
410
|
+
selection = data.get("selection") if isinstance(data.get("selection"), dict) else {}
|
|
411
|
+
view = selection.get("view") if isinstance(selection.get("view"), dict) else {}
|
|
412
|
+
compact: dict[str, Any] = {
|
|
413
|
+
"app_key": data.get("app_key"),
|
|
414
|
+
"items": data.get("items") if isinstance(data.get("items"), list) else [],
|
|
415
|
+
"pagination": compact_pagination,
|
|
416
|
+
}
|
|
417
|
+
if view:
|
|
418
|
+
compact["view"] = _pick(view, ("view_id", "name"))
|
|
419
|
+
payload["data"] = compact
|
|
320
420
|
|
|
321
421
|
|
|
322
422
|
def _trim_record_analyze(payload: JSONObject) -> None:
|
|
323
|
-
|
|
423
|
+
summary: dict[str, Any] = {}
|
|
424
|
+
completeness = payload.get("completeness")
|
|
425
|
+
if isinstance(completeness, dict):
|
|
426
|
+
summary["completeness"] = completeness
|
|
427
|
+
presentation = payload.get("presentation")
|
|
428
|
+
if isinstance(presentation, dict):
|
|
429
|
+
summary["presentation"] = presentation
|
|
430
|
+
ranking = payload.get("ranking")
|
|
431
|
+
if isinstance(ranking, dict):
|
|
432
|
+
summary["ranking"] = ranking
|
|
433
|
+
error = payload.get("error")
|
|
434
|
+
if isinstance(error, dict):
|
|
435
|
+
summary["error"] = error
|
|
436
|
+
if summary:
|
|
437
|
+
payload["summary"] = summary
|
|
438
|
+
for key in ("query", "ranking", "ratios", "completeness", "presentation", "error", "debug"):
|
|
439
|
+
payload.pop(key, None)
|
|
324
440
|
|
|
325
441
|
|
|
326
442
|
def _trim_code_block_schema(payload: JSONObject) -> None:
|
|
@@ -344,6 +460,207 @@ def _trim_task_context_detail(payload: JSONObject) -> None:
|
|
|
344
460
|
_drop_deep_keys(payload, {"request_route", "output_profile"})
|
|
345
461
|
|
|
346
462
|
|
|
463
|
+
def _trim_record_delete(payload: JSONObject) -> None:
|
|
464
|
+
data = payload.get("data")
|
|
465
|
+
if not isinstance(data, dict):
|
|
466
|
+
return
|
|
467
|
+
resource = data.get("resource")
|
|
468
|
+
deleted_ids: list[str] = []
|
|
469
|
+
if isinstance(resource, dict):
|
|
470
|
+
raw_ids = resource.get("record_ids") or resource.get("apply_ids") or resource.get("applyIds")
|
|
471
|
+
if isinstance(raw_ids, list):
|
|
472
|
+
deleted_ids = [str(item) for item in raw_ids if item not in (None, "")]
|
|
473
|
+
data["deleted_ids"] = deleted_ids
|
|
474
|
+
data.setdefault("failed_ids", [])
|
|
475
|
+
for key in (
|
|
476
|
+
"resource",
|
|
477
|
+
"action",
|
|
478
|
+
"normalized_payload",
|
|
479
|
+
"human_review",
|
|
480
|
+
"verification",
|
|
481
|
+
"blockers",
|
|
482
|
+
"field_errors",
|
|
483
|
+
"confirmation_requests",
|
|
484
|
+
"resolved_fields",
|
|
485
|
+
"debug",
|
|
486
|
+
):
|
|
487
|
+
data.pop(key, None)
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def _compact_record_resource(resource: Any) -> dict[str, Any] | None:
|
|
491
|
+
if not isinstance(resource, dict):
|
|
492
|
+
return None
|
|
493
|
+
compact: dict[str, Any] = {}
|
|
494
|
+
if resource.get("type") not in (None, ""):
|
|
495
|
+
compact["type"] = resource.get("type")
|
|
496
|
+
app_key = resource.get("app_key") or resource.get("appKey")
|
|
497
|
+
if app_key not in (None, ""):
|
|
498
|
+
compact["app_key"] = app_key
|
|
499
|
+
record_id = resource.get("record_id")
|
|
500
|
+
if record_id not in (None, ""):
|
|
501
|
+
compact["record_id"] = str(record_id)
|
|
502
|
+
apply_id = resource.get("apply_id") or resource.get("applyId")
|
|
503
|
+
if apply_id not in (None, "") and "record_id" not in compact:
|
|
504
|
+
compact["record_id"] = str(apply_id)
|
|
505
|
+
record_ids = resource.get("record_ids")
|
|
506
|
+
if isinstance(record_ids, list):
|
|
507
|
+
compact["record_ids"] = [str(item) for item in record_ids if item not in (None, "")]
|
|
508
|
+
apply_ids = resource.get("apply_ids") or resource.get("applyIds")
|
|
509
|
+
if isinstance(apply_ids, list) and "record_ids" not in compact:
|
|
510
|
+
compact["record_ids"] = [str(item) for item in apply_ids if item not in (None, "")]
|
|
511
|
+
return compact or None
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
def _compact_schema_fields(items: Any, *, template_map: dict[str, Any] | None) -> list[JSONObject]:
|
|
515
|
+
if not isinstance(items, list):
|
|
516
|
+
return []
|
|
517
|
+
compacted: list[JSONObject] = []
|
|
518
|
+
for item in items:
|
|
519
|
+
compact = _compact_schema_field(item, template_map=template_map)
|
|
520
|
+
if compact:
|
|
521
|
+
compacted.append(compact)
|
|
522
|
+
return compacted
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
def _compact_schema_field(item: Any, *, template_map: dict[str, Any] | None) -> JSONObject | None:
|
|
526
|
+
if not isinstance(item, dict):
|
|
527
|
+
return None
|
|
528
|
+
compact: dict[str, Any] = {}
|
|
529
|
+
field_id = item.get("field_id")
|
|
530
|
+
if field_id not in (None, ""):
|
|
531
|
+
compact["field_id"] = field_id
|
|
532
|
+
title = item.get("title")
|
|
533
|
+
if title not in (None, ""):
|
|
534
|
+
compact["title"] = title
|
|
535
|
+
kind = item.get("kind") or item.get("write_kind")
|
|
536
|
+
if kind not in (None, ""):
|
|
537
|
+
compact["kind"] = kind
|
|
538
|
+
if "required" in item:
|
|
539
|
+
compact["required"] = bool(item.get("required"))
|
|
540
|
+
if template_map is not None and isinstance(title, str) and title in template_map:
|
|
541
|
+
compact["template"] = template_map.get(title)
|
|
542
|
+
candidate_hint = item.get("candidate_hint")
|
|
543
|
+
if isinstance(candidate_hint, dict):
|
|
544
|
+
compact["candidate_hint"] = candidate_hint
|
|
545
|
+
options = item.get("options")
|
|
546
|
+
if isinstance(options, list) and options:
|
|
547
|
+
compact["options"] = options
|
|
548
|
+
target_app_key = item.get("target_app_key")
|
|
549
|
+
if isinstance(target_app_key, str) and target_app_key:
|
|
550
|
+
compact["target_app_key"] = target_app_key
|
|
551
|
+
searchable_fields = item.get("searchable_fields")
|
|
552
|
+
if isinstance(searchable_fields, list) and searchable_fields:
|
|
553
|
+
compact["searchable_fields"] = searchable_fields
|
|
554
|
+
row_fields = item.get("row_fields")
|
|
555
|
+
if isinstance(row_fields, list) and row_fields:
|
|
556
|
+
compact["row_fields"] = _compact_schema_fields(row_fields, template_map=None)
|
|
557
|
+
return compact or None
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
def _compact_import_column(item: dict[str, Any]) -> dict[str, Any]:
|
|
561
|
+
compact: dict[str, Any] = {
|
|
562
|
+
"title": item.get("title"),
|
|
563
|
+
"kind": item.get("kind") or item.get("write_kind"),
|
|
564
|
+
"required": bool(item.get("required")),
|
|
565
|
+
}
|
|
566
|
+
options = item.get("options")
|
|
567
|
+
if isinstance(options, list) and options:
|
|
568
|
+
compact["options"] = options
|
|
569
|
+
if bool(item.get("accepts_natural_input")):
|
|
570
|
+
compact["accepts_natural_input"] = True
|
|
571
|
+
if bool(item.get("requires_upload")):
|
|
572
|
+
compact["requires_upload"] = True
|
|
573
|
+
target_app_key = item.get("target_app_key")
|
|
574
|
+
if isinstance(target_app_key, str) and target_app_key:
|
|
575
|
+
compact["target_app_key"] = target_app_key
|
|
576
|
+
target_app_name = item.get("target_app_name")
|
|
577
|
+
if isinstance(target_app_name, str) and target_app_name:
|
|
578
|
+
compact["target_app_name"] = target_app_name
|
|
579
|
+
searchable_fields = item.get("searchable_fields")
|
|
580
|
+
if isinstance(searchable_fields, list) and searchable_fields:
|
|
581
|
+
compact["searchable_fields"] = searchable_fields
|
|
582
|
+
return compact
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
def _looks_like_import_verify(payload: JSONObject) -> bool:
|
|
586
|
+
return "verification_id" in payload and "can_import" in payload
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
def _trim_import_verify_payload(payload: JSONObject) -> None:
|
|
590
|
+
issues = payload.get("issues") if isinstance(payload.get("issues"), list) else []
|
|
591
|
+
issue_summary = _summarize_import_issues(issues)
|
|
592
|
+
payload["issue_summary"] = issue_summary
|
|
593
|
+
file_name = payload.get("file_name")
|
|
594
|
+
if not file_name:
|
|
595
|
+
file_path = payload.get("file_path")
|
|
596
|
+
if isinstance(file_path, str) and file_path:
|
|
597
|
+
payload["file_name"] = file_path.split("/")[-1]
|
|
598
|
+
for key in ("issues", "apply_rows", "expected_columns", "schema_fingerprint", "import_capability", "file_path", "file_sha256", "verified_file_sha256", "file_format", "auto_normalized", "local_precheck_limited"):
|
|
599
|
+
payload.pop(key, None)
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
def _trim_import_repair_payload(payload: JSONObject) -> None:
|
|
603
|
+
payload["verification_id"] = payload.get("new_verification_id") or payload.get("verification_id")
|
|
604
|
+
for key in ("new_verification_id", "source_file_path", "repaired_file_path", "post_repair_issues", "verification"):
|
|
605
|
+
payload.pop(key, None)
|
|
606
|
+
|
|
607
|
+
|
|
608
|
+
def _trim_import_template_payload(payload: JSONObject) -> None:
|
|
609
|
+
for key in ("schema_fingerprint", "verification"):
|
|
610
|
+
payload.pop(key, None)
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
def _trim_import_status_payload(payload: JSONObject) -> None:
|
|
614
|
+
total_rows = payload.get("total_rows")
|
|
615
|
+
success_rows = payload.get("success_rows")
|
|
616
|
+
failed_rows = payload.get("failed_rows")
|
|
617
|
+
payload["total"] = total_rows
|
|
618
|
+
payload["finished"] = success_rows
|
|
619
|
+
payload["failed"] = failed_rows
|
|
620
|
+
for key in (
|
|
621
|
+
"matched_by",
|
|
622
|
+
"source_file_name",
|
|
623
|
+
"total_rows",
|
|
624
|
+
"success_rows",
|
|
625
|
+
"failed_rows",
|
|
626
|
+
"error_file_urls",
|
|
627
|
+
"operate_time",
|
|
628
|
+
"operate_user",
|
|
629
|
+
"verification",
|
|
630
|
+
):
|
|
631
|
+
payload.pop(key, None)
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
def _summarize_import_issues(issues: list[Any]) -> dict[str, Any]:
|
|
635
|
+
total = 0
|
|
636
|
+
error_count = 0
|
|
637
|
+
warning_count = 0
|
|
638
|
+
sample: list[dict[str, Any]] = []
|
|
639
|
+
for item in issues:
|
|
640
|
+
if not isinstance(item, dict):
|
|
641
|
+
continue
|
|
642
|
+
total += 1
|
|
643
|
+
severity = str(item.get("severity") or "").lower()
|
|
644
|
+
if severity == "error":
|
|
645
|
+
error_count += 1
|
|
646
|
+
if severity == "warning":
|
|
647
|
+
warning_count += 1
|
|
648
|
+
if len(sample) < 3:
|
|
649
|
+
sample.append(
|
|
650
|
+
{
|
|
651
|
+
"code": item.get("code"),
|
|
652
|
+
"message": item.get("message"),
|
|
653
|
+
"severity": item.get("severity"),
|
|
654
|
+
}
|
|
655
|
+
)
|
|
656
|
+
return {
|
|
657
|
+
"total": total,
|
|
658
|
+
"errors": error_count,
|
|
659
|
+
"warnings": warning_count,
|
|
660
|
+
"sample": sample,
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
|
|
347
664
|
def _trim_directory(payload: JSONObject) -> None:
|
|
348
665
|
pass
|
|
349
666
|
|
|
@@ -442,10 +759,10 @@ _register_policy(
|
|
|
442
759
|
(
|
|
443
760
|
"record_member_candidates",
|
|
444
761
|
"record_department_candidates",
|
|
445
|
-
"record_delete",
|
|
446
762
|
),
|
|
447
763
|
_trim_builder_list_like,
|
|
448
764
|
)
|
|
765
|
+
_register_policy((USER_DOMAIN,), ("record_delete",), _trim_record_delete)
|
|
449
766
|
_register_policy(
|
|
450
767
|
(BUILDER_DOMAIN,),
|
|
451
768
|
(
|
|
@@ -1025,6 +1025,7 @@ class RecordTools(ToolBase):
|
|
|
1025
1025
|
else:
|
|
1026
1026
|
required = bool(required_override) if required_override is not None else bool(field.required)
|
|
1027
1027
|
payload: JSONObject = {
|
|
1028
|
+
"field_id": field.que_id,
|
|
1028
1029
|
"title": field.que_title,
|
|
1029
1030
|
"kind": kind,
|
|
1030
1031
|
"required": required,
|
|
@@ -1371,7 +1371,7 @@ class TaskContextTools(ToolBase):
|
|
|
1371
1371
|
"record_id": task.get("record_id"),
|
|
1372
1372
|
"workflow_node_id": task.get("workflow_node_id"),
|
|
1373
1373
|
"workflow_node_name": task.get("workflow_node_name"),
|
|
1374
|
-
"initiator": record.get("apply_user"),
|
|
1374
|
+
"initiator": self._compact_initiator(record.get("apply_user")),
|
|
1375
1375
|
"actionable": task.get("actionable"),
|
|
1376
1376
|
},
|
|
1377
1377
|
"record_summary": {
|
|
@@ -1416,12 +1416,6 @@ class TaskContextTools(ToolBase):
|
|
|
1416
1416
|
},
|
|
1417
1417
|
},
|
|
1418
1418
|
}
|
|
1419
|
-
action_metadata = self._compact_task_action_metadata(capabilities)
|
|
1420
|
-
if action_metadata:
|
|
1421
|
-
compact["action_metadata"] = action_metadata
|
|
1422
|
-
editable_metadata = self._compact_task_editable_metadata(update_schema)
|
|
1423
|
-
if editable_metadata:
|
|
1424
|
-
compact["editable_metadata"] = editable_metadata
|
|
1425
1419
|
return compact
|
|
1426
1420
|
|
|
1427
1421
|
def _compact_task_action_metadata(self, capabilities: dict[str, Any]) -> dict[str, Any]:
|
|
@@ -1449,6 +1443,18 @@ class TaskContextTools(ToolBase):
|
|
|
1449
1443
|
metadata["warnings"] = warnings
|
|
1450
1444
|
return metadata
|
|
1451
1445
|
|
|
1446
|
+
def _compact_initiator(self, payload: Any) -> dict[str, Any] | None:
|
|
1447
|
+
if not isinstance(payload, dict):
|
|
1448
|
+
return None
|
|
1449
|
+
compact = {
|
|
1450
|
+
"uid": payload.get("uid"),
|
|
1451
|
+
"displayName": payload.get("displayName") or payload.get("name") or payload.get("nickName"),
|
|
1452
|
+
"email": payload.get("email"),
|
|
1453
|
+
"mobile": payload.get("mobile"),
|
|
1454
|
+
"headImg": payload.get("headImg"),
|
|
1455
|
+
}
|
|
1456
|
+
return {key: value for key, value in compact.items() if value not in (None, "", [])} or None
|
|
1457
|
+
|
|
1452
1458
|
def _task_app_name(self, detail: dict[str, Any], node_info: dict[str, Any]) -> Any:
|
|
1453
1459
|
for source in (detail, node_info):
|
|
1454
1460
|
for key in ("formTitle", "appName", "worksheetName", "appTitle"):
|
|
@@ -1550,12 +1556,6 @@ class TaskContextTools(ToolBase):
|
|
|
1550
1556
|
app_key = raw.get("appKey") or raw.get("app_key")
|
|
1551
1557
|
record_id = raw.get("rowRecordId") or raw.get("recordId") or raw.get("applyId")
|
|
1552
1558
|
workflow_node_id = raw.get("nodeId") or raw.get("auditNodeId")
|
|
1553
|
-
apply_user = raw.get("applyUser")
|
|
1554
|
-
if apply_user is None:
|
|
1555
|
-
user_uid = raw.get("applyUserUid")
|
|
1556
|
-
user_name = raw.get("applyUserName")
|
|
1557
|
-
if user_uid is not None or user_name is not None:
|
|
1558
|
-
apply_user = {"uid": user_uid, "name": user_name}
|
|
1559
1559
|
return {
|
|
1560
1560
|
"task_id": raw.get("id") or raw.get("taskId") or record_id,
|
|
1561
1561
|
"app_key": app_key,
|
|
@@ -1563,8 +1563,6 @@ class TaskContextTools(ToolBase):
|
|
|
1563
1563
|
"record_id": record_id,
|
|
1564
1564
|
"workflow_node_id": workflow_node_id,
|
|
1565
1565
|
"workflow_node_name": raw.get("nodeName") or raw.get("auditNodeName"),
|
|
1566
|
-
"title": raw.get("title") or raw.get("applyTitle") or raw.get("name") or raw.get("formTitle"),
|
|
1567
|
-
"apply_user": apply_user,
|
|
1568
1566
|
"apply_time": raw.get("applyTime") or raw.get("receiveTime"),
|
|
1569
1567
|
"task_box": task_box,
|
|
1570
1568
|
"flow_status": flow_status,
|