@ai-setting/roy-agent-core 1.7.48 → 1.7.50
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/dist/config/index.js +7 -7
- package/dist/env/agent/index.js +10 -10
- package/dist/env/commands/index.js +7 -7
- package/dist/env/context/index.js +3 -3
- package/dist/env/debug/index.js +7 -7
- package/dist/env/event-source/index.js +17 -14
- package/dist/env/hook/index.js +9 -6
- package/dist/env/index.js +39 -34
- package/dist/env/llm/index.js +7 -7
- package/dist/env/log-trace/index.js +7 -7
- package/dist/env/mcp/index.js +7 -7
- package/dist/env/memory/index.js +11 -11
- package/dist/env/plugin/index.js +7 -7
- package/dist/env/prompt/index.js +8 -8
- package/dist/env/session/index.js +8 -7
- package/dist/env/skill/index.js +7 -7
- package/dist/env/task/delegate/index.js +8 -8
- package/dist/env/task/index.js +17 -17
- package/dist/env/task/plugins/index.js +10 -9
- package/dist/env/task/storage/index.js +2 -2
- package/dist/env/task/tools/index.js +3 -3
- package/dist/env/tool/built-in/index.js +2 -2
- package/dist/env/tool/index.js +8 -8
- package/dist/env/workflow/engine/index.js +13 -9
- package/dist/env/workflow/hooks/index.js +25 -0
- package/dist/env/workflow/index.js +20 -17
- package/dist/env/workflow/nodes/index.js +2 -2
- package/dist/env/workflow/plugins/index.js +18 -0
- package/dist/env/workflow/service/index.js +2 -2
- package/dist/env/workflow/storage/index.js +3 -3
- package/dist/env/workflow/tools/index.js +12 -11
- package/dist/env/workflow/utils/index.js +4 -4
- package/dist/index.js +80 -70
- package/dist/shared/@ai-setting/{roy-agent-core-0r90298w.js → roy-agent-core-0cfaqnz9.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-5j4awbgc.js → roy-agent-core-1cv9j4dg.js} +3 -3
- package/dist/shared/@ai-setting/{roy-agent-core-v5vs1jby.js → roy-agent-core-2gay3sjt.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-9cqpdrnk.js → roy-agent-core-2k4xffaa.js} +3 -3
- package/dist/shared/@ai-setting/{roy-agent-core-dabnevwe.js → roy-agent-core-35s1w7tc.js} +230 -96
- package/dist/shared/@ai-setting/{roy-agent-core-bjrtwfta.js → roy-agent-core-3tv1q6f2.js} +27 -11
- package/dist/shared/@ai-setting/{roy-agent-core-3j4by8rj.js → roy-agent-core-4z3rq5gw.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-zbb3sk72.js → roy-agent-core-5apmk4qw.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-jfptxs4f.js → roy-agent-core-6167phd4.js} +6 -2
- package/dist/shared/@ai-setting/{roy-agent-core-8nv07zgr.js → roy-agent-core-7evvpfwc.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-vsrfzaze.js → roy-agent-core-7g03zkkb.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-tm57tmds.js → roy-agent-core-8s9jd2qy.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-4kbvfnn1.js → roy-agent-core-978b6wfx.js} +1 -1
- package/dist/shared/@ai-setting/roy-agent-core-9csa89jx.js +46 -0
- package/dist/shared/@ai-setting/{roy-agent-core-y1n63tse.js → roy-agent-core-9jzysq50.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-4m02hnqf.js → roy-agent-core-ak4dc3kg.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-rgpkygbc.js → roy-agent-core-athdr2m0.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-83mk8wrn.js → roy-agent-core-avyd65je.js} +12 -6
- package/dist/shared/@ai-setting/{roy-agent-core-223v204d.js → roy-agent-core-cg9f4zch.js} +69 -4
- package/dist/shared/@ai-setting/{roy-agent-core-a9pbda78.js → roy-agent-core-dsevjbck.js} +38 -10
- package/dist/shared/@ai-setting/{roy-agent-core-4cj6hram.js → roy-agent-core-dv714bqh.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-amp8qckb.js → roy-agent-core-dwx9hjwx.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-d94ddn0r.js → roy-agent-core-f28va833.js} +8 -7
- package/dist/shared/@ai-setting/roy-agent-core-fs36xgcs.js +13 -0
- package/dist/shared/@ai-setting/{roy-agent-core-55hkeft7.js → roy-agent-core-fvs48d38.js} +26 -6
- package/dist/shared/@ai-setting/{roy-agent-core-nvcqp6p9.js → roy-agent-core-gpf2059p.js} +17 -13
- package/dist/shared/@ai-setting/{roy-agent-core-wn59p0zw.js → roy-agent-core-k8b440n3.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-pd0exvnq.js → roy-agent-core-nde5gzta.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-9qn92jhf.js → roy-agent-core-ne2t1pay.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-y1fp2mgf.js → roy-agent-core-p8ak03s0.js} +3 -3
- package/dist/shared/@ai-setting/{roy-agent-core-6vbgswjw.js → roy-agent-core-q6twb0df.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-zrgaemma.js → roy-agent-core-q9gnnv6v.js} +4 -4
- package/dist/shared/@ai-setting/{roy-agent-core-hxazjj4p.js → roy-agent-core-qamktze3.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-54wzmvnb.js → roy-agent-core-qcspfszy.js} +11 -4
- package/dist/shared/@ai-setting/{roy-agent-core-p1p9mr8f.js → roy-agent-core-qm9jda8p.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-4nnv0dze.js → roy-agent-core-rnwr39b0.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-3b2ceffs.js → roy-agent-core-rpnjzztt.js} +51 -76
- package/dist/shared/@ai-setting/{roy-agent-core-yjqj3taz.js → roy-agent-core-rpwn590p.js} +13 -13
- package/dist/shared/@ai-setting/{roy-agent-core-za09t5g3.js → roy-agent-core-rx16h2m1.js} +12 -6
- package/dist/shared/@ai-setting/{roy-agent-core-ajtsk20z.js → roy-agent-core-s7jpkfm1.js} +75 -754
- package/dist/shared/@ai-setting/{roy-agent-core-ygnva439.js → roy-agent-core-skzjs973.js} +6 -5
- package/dist/shared/@ai-setting/{roy-agent-core-1rm32m5t.js → roy-agent-core-t37wbysj.js} +4 -4
- package/dist/shared/@ai-setting/{roy-agent-core-ead21w5c.js → roy-agent-core-tdbv7ff4.js} +5 -3
- package/dist/shared/@ai-setting/{roy-agent-core-333apxaa.js → roy-agent-core-tjg71zqp.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-hkj5v9xq.js → roy-agent-core-vrsxhw9e.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-7p5zf8ay.js → roy-agent-core-vy69rpxy.js} +13 -13
- package/dist/shared/@ai-setting/roy-agent-core-wxsgay02.js +302 -0
- package/dist/shared/@ai-setting/{roy-agent-core-yjjqm4ww.js → roy-agent-core-x8n1r7h4.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-gy1zfe0d.js → roy-agent-core-yd21n6c7.js} +76 -13
- package/dist/shared/@ai-setting/{roy-agent-core-xyys5m1a.js → roy-agent-core-yhsssn86.js} +1 -1
- package/dist/shared/@ai-setting/roy-agent-core-zd077w51.js +1037 -0
- package/dist/shared/@ai-setting/{roy-agent-core-qvaqmj3f.js → roy-agent-core-zd3ddm4k.js} +1 -1
- package/dist/shared/@ai-setting/roy-agent-core-ze068vc9.js +37 -0
- package/package.json +1 -1
|
@@ -1,32 +1,36 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getSharedEventStore
|
|
3
|
-
} from "./roy-agent-core-
|
|
3
|
+
} from "./roy-agent-core-yhsssn86.js";
|
|
4
4
|
import {
|
|
5
5
|
TaskHookPoints
|
|
6
6
|
} from "./roy-agent-core-1pg0fepg.js";
|
|
7
7
|
import {
|
|
8
8
|
WORKFLOW_EXTRACT_PROMPT
|
|
9
|
-
} from "./roy-agent-core-
|
|
9
|
+
} from "./roy-agent-core-cg9f4zch.js";
|
|
10
|
+
import {
|
|
11
|
+
LarkCardPatchNotifier,
|
|
12
|
+
humanizeValue
|
|
13
|
+
} from "./roy-agent-core-zd077w51.js";
|
|
10
14
|
import {
|
|
11
15
|
validateWorkflowDefinition
|
|
12
|
-
} from "./roy-agent-core-
|
|
16
|
+
} from "./roy-agent-core-q6twb0df.js";
|
|
13
17
|
import {
|
|
14
18
|
enqueueToAddFakeUserMessage
|
|
15
|
-
} from "./roy-agent-core-
|
|
19
|
+
} from "./roy-agent-core-dv714bqh.js";
|
|
16
20
|
import {
|
|
17
21
|
BasePlugin
|
|
18
22
|
} from "./roy-agent-core-az13yzmc.js";
|
|
19
23
|
import {
|
|
20
24
|
init_context
|
|
21
|
-
} from "./roy-agent-core-
|
|
25
|
+
} from "./roy-agent-core-k8b440n3.js";
|
|
22
26
|
import {
|
|
23
27
|
getEnvContextOrEmpty
|
|
24
|
-
} from "./roy-agent-core-
|
|
28
|
+
} from "./roy-agent-core-5apmk4qw.js";
|
|
25
29
|
import {
|
|
26
30
|
TracedAs,
|
|
27
31
|
init_decorator,
|
|
28
32
|
wrapFunction
|
|
29
|
-
} from "./roy-agent-core-
|
|
33
|
+
} from "./roy-agent-core-dsevjbck.js";
|
|
30
34
|
import {
|
|
31
35
|
createLogger,
|
|
32
36
|
init_logger
|
|
@@ -295,11 +299,11 @@ ${prompt}`;
|
|
|
295
299
|
}
|
|
296
300
|
if (!this._workflowRepo) {
|
|
297
301
|
try {
|
|
298
|
-
const sqlite = await import("./roy-agent-core-
|
|
302
|
+
const sqlite = await import("./roy-agent-core-2k4xffaa.js");
|
|
299
303
|
this.ensureDataDir();
|
|
300
304
|
const db = sqlite.getDatabase();
|
|
301
305
|
sqlite.initializeTables();
|
|
302
|
-
const { WorkflowRepository } = await import("./roy-agent-core-
|
|
306
|
+
const { WorkflowRepository } = await import("./roy-agent-core-1cv9j4dg.js");
|
|
303
307
|
this._workflowRepo = new WorkflowRepository(db);
|
|
304
308
|
return this._workflowRepo;
|
|
305
309
|
} catch {
|
|
@@ -373,689 +377,6 @@ init_decorator();
|
|
|
373
377
|
init_logger();
|
|
374
378
|
init_context();
|
|
375
379
|
|
|
376
|
-
// src/env/task/plugins/lark-card-patch-notifier.ts
|
|
377
|
-
init_logger();
|
|
378
|
-
init_decorator();
|
|
379
|
-
import { spawn } from "node:child_process";
|
|
380
|
-
var logger3 = createLogger("lark-cli:card-patch-notifier");
|
|
381
|
-
var HEADER_TEMPLATE = {
|
|
382
|
-
running: "blue",
|
|
383
|
-
completed: "green",
|
|
384
|
-
failed: "red"
|
|
385
|
-
};
|
|
386
|
-
var STATUS_PHRASE = {
|
|
387
|
-
running: "实时同步中",
|
|
388
|
-
completed: "\uD83C\uDF89 全部完成!",
|
|
389
|
-
failed: "\uD83D\uDEA8 任务失败"
|
|
390
|
-
};
|
|
391
|
-
var SPINNER_EMOJIS = ["⏳", "\uD83D\uDD04", "\uD83D\uDD03", "⌛", "⏰", "\uD83D\uDD01"];
|
|
392
|
-
var PROGRESS_BAR_CELLS = 20;
|
|
393
|
-
function formatTimestampForDisplay(iso) {
|
|
394
|
-
if (!iso)
|
|
395
|
-
return "";
|
|
396
|
-
const d = new Date(iso);
|
|
397
|
-
if (isNaN(d.getTime())) {
|
|
398
|
-
return iso;
|
|
399
|
-
}
|
|
400
|
-
return new Intl.DateTimeFormat("en-GB", {
|
|
401
|
-
timeZone: "Asia/Shanghai",
|
|
402
|
-
hour: "2-digit",
|
|
403
|
-
minute: "2-digit",
|
|
404
|
-
second: "2-digit",
|
|
405
|
-
hour12: false
|
|
406
|
-
}).format(d);
|
|
407
|
-
}
|
|
408
|
-
function sanitizeInlineText(text, maxLen = 300) {
|
|
409
|
-
if (!text)
|
|
410
|
-
return "";
|
|
411
|
-
const flat = text.replace(/\r\n?/g, `
|
|
412
|
-
`).replace(/^#{1,6}\s+/gm, "").replace(/^\s*[-*+]\s+/gm, "· ").replace(/^\s*\d+\.\s+/gm, "· ").replace(/\*\*/g, "").replace(/`/g, "").replace(/\n+/g, " · ").replace(/\s{2,}/g, " ").replace(/(?:\s*·\s*){2,}/g, " · ").replace(/^(\s*·\s*)+/, "").replace(/(\s*·\s*)+$/, "").trim();
|
|
413
|
-
return flat.length <= maxLen ? flat : `${flat.slice(0, maxLen)}…`;
|
|
414
|
-
}
|
|
415
|
-
function formatDateTimeForDisplay(value) {
|
|
416
|
-
const d = new Date(value);
|
|
417
|
-
if (isNaN(d.getTime()))
|
|
418
|
-
return value;
|
|
419
|
-
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
420
|
-
timeZone: "Asia/Shanghai",
|
|
421
|
-
year: "numeric",
|
|
422
|
-
month: "2-digit",
|
|
423
|
-
day: "2-digit",
|
|
424
|
-
hour: "2-digit",
|
|
425
|
-
minute: "2-digit",
|
|
426
|
-
hour12: false
|
|
427
|
-
}).formatToParts(d);
|
|
428
|
-
const get = (t) => parts.find((p) => p.type === t)?.value ?? "";
|
|
429
|
-
return `${get("year")}-${get("month")}-${get("day")} ${get("hour")}:${get("minute")}`;
|
|
430
|
-
}
|
|
431
|
-
function looksLikeIsoDateTime(v) {
|
|
432
|
-
return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(v);
|
|
433
|
-
}
|
|
434
|
-
function renderProgressBar(progress) {
|
|
435
|
-
const clamped = Math.max(0, Math.min(100, progress));
|
|
436
|
-
const filled = Math.round(clamped / 100 * PROGRESS_BAR_CELLS);
|
|
437
|
-
const bar = "▓".repeat(filled) + "░".repeat(PROGRESS_BAR_CELLS - filled);
|
|
438
|
-
return `${bar} ${Math.round(clamped)}%`;
|
|
439
|
-
}
|
|
440
|
-
function humanizeValue(value, maxLen = 200) {
|
|
441
|
-
const truncate = (t) => t.length <= maxLen ? t : `${t.slice(0, maxLen)}…`;
|
|
442
|
-
if (value === null || value === undefined)
|
|
443
|
-
return "";
|
|
444
|
-
if (typeof value === "string") {
|
|
445
|
-
if (looksLikeIsoDateTime(value))
|
|
446
|
-
return formatDateTimeForDisplay(value);
|
|
447
|
-
return truncate(sanitizeInlineText(value, maxLen));
|
|
448
|
-
}
|
|
449
|
-
if (typeof value === "number" || typeof value === "boolean") {
|
|
450
|
-
return String(value);
|
|
451
|
-
}
|
|
452
|
-
if (Array.isArray(value)) {
|
|
453
|
-
return truncate(value.map((v) => humanizeValue(v, 80)).filter((t) => t.length > 0).join("、"));
|
|
454
|
-
}
|
|
455
|
-
if (typeof value === "object") {
|
|
456
|
-
const entries = Object.entries(value).map(([k, v]) => {
|
|
457
|
-
const rendered = humanizeValue(v, 80);
|
|
458
|
-
return rendered ? `${k}: ${rendered}` : "";
|
|
459
|
-
}).filter((t) => t.length > 0);
|
|
460
|
-
return truncate(entries.join(" · "));
|
|
461
|
-
}
|
|
462
|
-
return "";
|
|
463
|
-
}
|
|
464
|
-
var CONTEXT_LABELS = {
|
|
465
|
-
worktree_path: "\uD83C\uDF3F Worktree",
|
|
466
|
-
branch: "\uD83C\uDF3F 分支",
|
|
467
|
-
created_at: "\uD83D\uDD50 创建时间",
|
|
468
|
-
prior_context: "\uD83D\uDCDC 背景",
|
|
469
|
-
project_path: "\uD83D\uDCC2 项目",
|
|
470
|
-
type: "\uD83C\uDFF7️ 类型",
|
|
471
|
-
feature: "\uD83C\uDFF7️ 特性",
|
|
472
|
-
goal: "\uD83C\uDFAF 目标",
|
|
473
|
-
feishu_chat_id: "\uD83D\uDCAC 飞书群",
|
|
474
|
-
trigger_message_id: "✉️ 触发消息",
|
|
475
|
-
trigger_sender_id: "\uD83D\uDC64 触发人",
|
|
476
|
-
related_task: "\uD83D\uDD17 关联任务",
|
|
477
|
-
repo: "\uD83D\uDCE6 仓库",
|
|
478
|
-
commit: "\uD83D\uDD16 Commit",
|
|
479
|
-
session_id: "\uD83E\uDDF5 Session",
|
|
480
|
-
worktree: "\uD83C\uDF3F Worktree",
|
|
481
|
-
larkPerTaskChatId: "\uD83D\uDDC2️ 任务群聊",
|
|
482
|
-
larkPerTaskChatProfile: "\uD83D\uDDC2️ 群聊 Profile"
|
|
483
|
-
};
|
|
484
|
-
var CONTEXT_SKIP_KEYS = new Set([
|
|
485
|
-
"larkPerTaskChatTaskId",
|
|
486
|
-
"larkPerTaskChatCreatedAt"
|
|
487
|
-
]);
|
|
488
|
-
function renderTaskInfoLines(meta) {
|
|
489
|
-
const lines = [];
|
|
490
|
-
if (meta.id !== undefined)
|
|
491
|
-
lines.push(`- **\uD83C\uDD94 任务 ID**:#${meta.id}`);
|
|
492
|
-
if (meta.title) {
|
|
493
|
-
lines.push(`- **\uD83D\uDCCC 标题**:${sanitizeInlineText(meta.title, 120)}`);
|
|
494
|
-
}
|
|
495
|
-
if (meta.description) {
|
|
496
|
-
lines.push(`- **\uD83D\uDCDD 描述**:${sanitizeInlineText(meta.description, 300)}`);
|
|
497
|
-
}
|
|
498
|
-
if (meta.goals) {
|
|
499
|
-
lines.push(`- **\uD83C\uDFAF 目标**:${sanitizeInlineText(meta.goals, 300)}`);
|
|
500
|
-
}
|
|
501
|
-
if (meta.parentTaskId)
|
|
502
|
-
lines.push(`- **\uD83D\uDD17 父任务**:#${meta.parentTaskId}`);
|
|
503
|
-
if (meta.priority || meta.type) {
|
|
504
|
-
lines.push(`- **⚡ 优先级 / 类型**:${meta.priority ?? "—"} / ${meta.type ?? "—"}`);
|
|
505
|
-
}
|
|
506
|
-
if (meta.currentStatus) {
|
|
507
|
-
lines.push(`- **\uD83D\uDCF6 当前状态**:${sanitizeInlineText(meta.currentStatus, 200)}`);
|
|
508
|
-
}
|
|
509
|
-
const project = realValue(meta.projectPath);
|
|
510
|
-
if (project)
|
|
511
|
-
lines.push(`- **\uD83D\uDCC2 项目**:\`${sanitizeInlineText(project, 160)}\``);
|
|
512
|
-
const contextLines = renderContextAsLines(meta.context);
|
|
513
|
-
lines.push(...contextLines);
|
|
514
|
-
return lines;
|
|
515
|
-
}
|
|
516
|
-
function realValue(v) {
|
|
517
|
-
if (!v)
|
|
518
|
-
return;
|
|
519
|
-
const t = v.trim();
|
|
520
|
-
if (t.length === 0 || t === "unknown" || t === "undefined" || t === "null") {
|
|
521
|
-
return;
|
|
522
|
-
}
|
|
523
|
-
return t;
|
|
524
|
-
}
|
|
525
|
-
function renderContextAsLines(context) {
|
|
526
|
-
const raw = realValue(context);
|
|
527
|
-
if (!raw)
|
|
528
|
-
return [];
|
|
529
|
-
let parsed;
|
|
530
|
-
try {
|
|
531
|
-
parsed = JSON.parse(raw);
|
|
532
|
-
} catch {
|
|
533
|
-
return [];
|
|
534
|
-
}
|
|
535
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
536
|
-
return [];
|
|
537
|
-
const obj = parsed;
|
|
538
|
-
const lines = [];
|
|
539
|
-
const knownKeys = Object.keys(obj).filter((k) => CONTEXT_LABELS[k] && !CONTEXT_SKIP_KEYS.has(k));
|
|
540
|
-
const otherKeys = Object.keys(obj).filter((k) => !CONTEXT_LABELS[k] && !CONTEXT_SKIP_KEYS.has(k));
|
|
541
|
-
for (const k of knownKeys) {
|
|
542
|
-
const rendered = humanizeValue(obj[k], 160);
|
|
543
|
-
if (rendered)
|
|
544
|
-
lines.push(`- **${CONTEXT_LABELS[k]}**:${rendered}`);
|
|
545
|
-
}
|
|
546
|
-
for (const k of otherKeys) {
|
|
547
|
-
const rendered = humanizeValue(obj[k], 160);
|
|
548
|
-
if (rendered)
|
|
549
|
-
lines.push(`- **${k}**:${rendered}`);
|
|
550
|
-
}
|
|
551
|
-
return lines;
|
|
552
|
-
}
|
|
553
|
-
function renderMilestones(milestones) {
|
|
554
|
-
if (milestones.length === 0) {
|
|
555
|
-
return "_(暂无里程碑)_";
|
|
556
|
-
}
|
|
557
|
-
return milestones.map((m) => {
|
|
558
|
-
const ts = formatTimestampForDisplay(m.timestamp) || "—";
|
|
559
|
-
const head = `- **${ts}** ${sanitizeInlineText(m.title, 120)}`;
|
|
560
|
-
return m.description ? `${head}
|
|
561
|
-
- ${sanitizeInlineText(m.description, 300)}` : head;
|
|
562
|
-
}).join(`
|
|
563
|
-
`);
|
|
564
|
-
}
|
|
565
|
-
function renderProcessSteps(steps) {
|
|
566
|
-
if (steps.length === 0) {
|
|
567
|
-
return "_(暂无过程记录)_";
|
|
568
|
-
}
|
|
569
|
-
return steps.map((s) => {
|
|
570
|
-
const startTime = formatTimestampForDisplay(s.timestamp);
|
|
571
|
-
const endTime = formatTimestampForDisplay(s.endTimestamp) || "现在";
|
|
572
|
-
const range = startTime ? `${startTime} → ${endTime}` : endTime;
|
|
573
|
-
const title = s.category ? `${s.title}(${s.category})` : s.title;
|
|
574
|
-
const head = `- **${range}** ${sanitizeInlineText(title, 120)}`;
|
|
575
|
-
return s.description ? `${head}
|
|
576
|
-
- ${sanitizeInlineText(s.description, 300)}` : head;
|
|
577
|
-
}).join(`
|
|
578
|
-
`);
|
|
579
|
-
}
|
|
580
|
-
function milestoneKey(m) {
|
|
581
|
-
return `${m.title}|${m.timestamp ?? ""}|${m.description ?? ""}`;
|
|
582
|
-
}
|
|
583
|
-
function processStepKey(s) {
|
|
584
|
-
return `${s.title}|${s.timestamp ?? ""}|${s.endTimestamp ?? ""}|${s.description ?? ""}`;
|
|
585
|
-
}
|
|
586
|
-
function buildTaskCard(opts) {
|
|
587
|
-
const status = opts.status ?? "running";
|
|
588
|
-
const milestones = opts.milestones ?? [];
|
|
589
|
-
const processSteps = opts.processSteps ?? [];
|
|
590
|
-
const updateCount = opts.updateCount ?? 0;
|
|
591
|
-
const spinner = opts.spinner ?? "";
|
|
592
|
-
const elements = [];
|
|
593
|
-
const infoLines = renderTaskInfoLines(opts.meta);
|
|
594
|
-
if (infoLines.length > 0) {
|
|
595
|
-
elements.push({ tag: "markdown", content: infoLines.join(`
|
|
596
|
-
`) });
|
|
597
|
-
elements.push({ tag: "hr" });
|
|
598
|
-
}
|
|
599
|
-
elements.push({ tag: "markdown", content: `## \uD83C\uDFAF 里程碑序列` });
|
|
600
|
-
elements.push({ tag: "markdown", content: renderMilestones(milestones) });
|
|
601
|
-
elements.push({ tag: "hr" });
|
|
602
|
-
elements.push({ tag: "markdown", content: `## \uD83D\uDD27 过程信息` });
|
|
603
|
-
elements.push({ tag: "markdown", content: renderProcessSteps(processSteps) });
|
|
604
|
-
elements.push({ tag: "hr" });
|
|
605
|
-
const effectiveProgress = status === "completed" ? 100 : opts.progress;
|
|
606
|
-
elements.push({
|
|
607
|
-
tag: "markdown",
|
|
608
|
-
content: effectiveProgress !== undefined ? `**\uD83D\uDCCA 进度**:${renderProgressBar(effectiveProgress)}` : `**\uD83D\uDCCA 进度**:进行中(第 ${updateCount} 次更新,尚未上报百分比)`
|
|
609
|
-
});
|
|
610
|
-
const taskLabel = opts.meta.id !== undefined ? `#${opts.meta.id}` : "任务";
|
|
611
|
-
return {
|
|
612
|
-
schema: "2.0",
|
|
613
|
-
header: {
|
|
614
|
-
title: { tag: "plain_text", content: `\uD83D\uDCCA Task 实时更新(${taskLabel})` },
|
|
615
|
-
subtitle: {
|
|
616
|
-
tag: "plain_text",
|
|
617
|
-
content: `spinner: '${spinner}' · 第 ${updateCount} 次更新 | ` + STATUS_PHRASE[status]
|
|
618
|
-
},
|
|
619
|
-
template: HEADER_TEMPLATE[status]
|
|
620
|
-
},
|
|
621
|
-
body: { elements }
|
|
622
|
-
};
|
|
623
|
-
}
|
|
624
|
-
function buildChatAppLink(chatId, base = DEFAULT_APP_LINK_BASE) {
|
|
625
|
-
return `${base}/client/chat/open?openChatId=${chatId}`;
|
|
626
|
-
}
|
|
627
|
-
var DEFAULT_APP_LINK_BASE = "https://applink.feishu.cn";
|
|
628
|
-
function buildRedirectCard(opts) {
|
|
629
|
-
const taskLabel = opts.taskId !== undefined ? `#${opts.taskId}` : "本任务";
|
|
630
|
-
const title = opts.taskTitle ? ` ${sanitizeInlineText(opts.taskTitle, 120)}` : "";
|
|
631
|
-
const link = buildChatAppLink(opts.targetChatId);
|
|
632
|
-
const groupName = opts.targetChatName ? sanitizeInlineText(opts.targetChatName, 60) : "专属群";
|
|
633
|
-
return {
|
|
634
|
-
schema: "2.0",
|
|
635
|
-
header: {
|
|
636
|
-
title: { tag: "plain_text", content: "\uD83D\uDCC2 任务卡片已改投专属群" },
|
|
637
|
-
subtitle: { tag: "plain_text", content: "这里不是本任务的群,进展请看专属群" },
|
|
638
|
-
template: "grey"
|
|
639
|
-
},
|
|
640
|
-
body: {
|
|
641
|
-
elements: [
|
|
642
|
-
{
|
|
643
|
-
tag: "markdown",
|
|
644
|
-
content: [
|
|
645
|
-
`- **任务**:${taskLabel}${title}`,
|
|
646
|
-
`- **专属群**:[${groupName}](${link})`,
|
|
647
|
-
`- **群 ID**:\`${opts.targetChatId}\`(搜群名也能进)`,
|
|
648
|
-
opts.reason ? `- **原因**:${sanitizeInlineText(opts.reason, 280)}` : ""
|
|
649
|
-
].filter(Boolean).join(`
|
|
650
|
-
`)
|
|
651
|
-
}
|
|
652
|
-
]
|
|
653
|
-
}
|
|
654
|
-
};
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
class MutexImpl {
|
|
658
|
-
chain = Promise.resolve();
|
|
659
|
-
async run(fn) {
|
|
660
|
-
const prev = this.chain;
|
|
661
|
-
const next = prev.then(() => fn());
|
|
662
|
-
this.chain = next.catch(() => {});
|
|
663
|
-
return next;
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
class LarkCardPatchNotifier {
|
|
668
|
-
options;
|
|
669
|
-
mutexes = new Map;
|
|
670
|
-
states = new Map;
|
|
671
|
-
redirectsSent = new Set;
|
|
672
|
-
constructor(options = {}) {
|
|
673
|
-
this.options = {
|
|
674
|
-
pinOnStart: true,
|
|
675
|
-
unpinOnFinish: true,
|
|
676
|
-
...options
|
|
677
|
-
};
|
|
678
|
-
}
|
|
679
|
-
mutexFor(messageId) {
|
|
680
|
-
let m = this.mutexes.get(messageId);
|
|
681
|
-
if (!m) {
|
|
682
|
-
m = new MutexImpl;
|
|
683
|
-
this.mutexes.set(messageId, m);
|
|
684
|
-
}
|
|
685
|
-
return m;
|
|
686
|
-
}
|
|
687
|
-
async startTask(opts) {
|
|
688
|
-
const now = new Date().toISOString();
|
|
689
|
-
const spinner = SPINNER_EMOJIS[0];
|
|
690
|
-
const milestones = opts.milestones?.length ? opts.milestones : [{ title: "Task 创建", timestamp: now }];
|
|
691
|
-
const card = buildTaskCard({
|
|
692
|
-
status: "running",
|
|
693
|
-
meta: opts.meta,
|
|
694
|
-
milestones,
|
|
695
|
-
processSteps: [],
|
|
696
|
-
updateCount: 1,
|
|
697
|
-
spinner
|
|
698
|
-
});
|
|
699
|
-
const stdout = await this.runLarkCli(this.buildSendCardArgs({ chatId: opts.chatId, profile: opts.profile, card }), "send");
|
|
700
|
-
const messageId = this.extractMessageId(stdout);
|
|
701
|
-
if (!messageId) {
|
|
702
|
-
throw new Error(`lark-cli +messages-send did not return message_id: ${stdout.slice(0, 500)}`);
|
|
703
|
-
}
|
|
704
|
-
this.states.set(messageId, {
|
|
705
|
-
chatId: opts.chatId,
|
|
706
|
-
profile: opts.profile,
|
|
707
|
-
meta: opts.meta,
|
|
708
|
-
startedAt: now,
|
|
709
|
-
updateCount: 1,
|
|
710
|
-
spinnerIndex: 0,
|
|
711
|
-
milestones,
|
|
712
|
-
processSteps: []
|
|
713
|
-
});
|
|
714
|
-
let pinned = false;
|
|
715
|
-
let pinError;
|
|
716
|
-
if (this.options.pinOnStart) {
|
|
717
|
-
try {
|
|
718
|
-
await this.runLarkCli(this.buildPinArgs({ messageId, profile: opts.profile }), "pin");
|
|
719
|
-
pinned = true;
|
|
720
|
-
} catch (pinErr) {
|
|
721
|
-
pinError = pinErr.message;
|
|
722
|
-
logger3.warn(`Pin failed (non-fatal) for message ${messageId}: ${pinError}`);
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
return { messageId, pinned, pinError };
|
|
726
|
-
}
|
|
727
|
-
async sendRedirectNotice(opts) {
|
|
728
|
-
const redirectKey = opts.taskId !== undefined ? `${opts.taskId}:${opts.targetChatId}` : undefined;
|
|
729
|
-
if (redirectKey) {
|
|
730
|
-
if (this.redirectsSent.has(redirectKey)) {
|
|
731
|
-
logger3.debug(`sendRedirectNotice: 已发过改投卡片(${redirectKey})→ 跳过重复发送`);
|
|
732
|
-
return false;
|
|
733
|
-
}
|
|
734
|
-
this.redirectsSent.add(redirectKey);
|
|
735
|
-
}
|
|
736
|
-
const card = buildRedirectCard({
|
|
737
|
-
taskId: opts.taskId,
|
|
738
|
-
taskTitle: opts.taskTitle,
|
|
739
|
-
targetChatId: opts.targetChatId,
|
|
740
|
-
targetChatName: opts.targetChatName,
|
|
741
|
-
reason: opts.reason
|
|
742
|
-
});
|
|
743
|
-
try {
|
|
744
|
-
await this.runLarkCli(this.buildSendCardArgs({
|
|
745
|
-
chatId: opts.chatId,
|
|
746
|
-
profile: opts.profile,
|
|
747
|
-
card
|
|
748
|
-
}), "send-redirect");
|
|
749
|
-
return true;
|
|
750
|
-
} catch (err) {
|
|
751
|
-
if (redirectKey)
|
|
752
|
-
this.redirectsSent.delete(redirectKey);
|
|
753
|
-
logger3.warn(`sendRedirectNotice failed (non-fatal) for chat ${opts.chatId}: ${err.message}`);
|
|
754
|
-
return false;
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
async updateTask(opts) {
|
|
758
|
-
await this.mutexFor(opts.messageId).run(async () => {
|
|
759
|
-
const state = this.states.get(opts.messageId) ?? {
|
|
760
|
-
chatId: opts.chatId ?? "",
|
|
761
|
-
profile: opts.profile,
|
|
762
|
-
meta: opts.meta ?? { title: "任务" },
|
|
763
|
-
startedAt: new Date().toISOString(),
|
|
764
|
-
updateCount: 0,
|
|
765
|
-
spinnerIndex: 0,
|
|
766
|
-
milestones: [],
|
|
767
|
-
processSteps: []
|
|
768
|
-
};
|
|
769
|
-
const mergedMilestones = mergeUnique(state.milestones, opts.milestones ?? [], milestoneKey);
|
|
770
|
-
const mergedSteps = mergeUnique(state.processSteps, opts.processSteps ?? [], processStepKey);
|
|
771
|
-
const updateCount = state.updateCount + 1;
|
|
772
|
-
const spinner = SPINNER_EMOJIS[updateCount % SPINNER_EMOJIS.length];
|
|
773
|
-
const card = buildTaskCard({
|
|
774
|
-
status: opts.status,
|
|
775
|
-
meta: mergeTaskMeta(state.meta, opts.meta),
|
|
776
|
-
progress: opts.progress ?? state.progress,
|
|
777
|
-
milestones: mergedMilestones,
|
|
778
|
-
processSteps: mergedSteps,
|
|
779
|
-
updateCount,
|
|
780
|
-
spinner
|
|
781
|
-
});
|
|
782
|
-
await this.runLarkCli(this.buildPatchCardArgs({
|
|
783
|
-
messageId: opts.messageId,
|
|
784
|
-
profile: opts.profile ?? state.profile,
|
|
785
|
-
card
|
|
786
|
-
}), "patch");
|
|
787
|
-
this.states.set(opts.messageId, {
|
|
788
|
-
...state,
|
|
789
|
-
chatId: opts.chatId ?? state.chatId,
|
|
790
|
-
profile: opts.profile ?? state.profile,
|
|
791
|
-
meta: mergeTaskMeta(state.meta, opts.meta),
|
|
792
|
-
updateCount,
|
|
793
|
-
spinnerIndex: spinnerIndexFor(updateCount),
|
|
794
|
-
milestones: mergedMilestones,
|
|
795
|
-
processSteps: mergedSteps,
|
|
796
|
-
progress: opts.progress ?? state.progress
|
|
797
|
-
});
|
|
798
|
-
});
|
|
799
|
-
}
|
|
800
|
-
async finishTask(opts) {
|
|
801
|
-
await this.mutexFor(opts.messageId).run(async () => {
|
|
802
|
-
const state = this.states.get(opts.messageId);
|
|
803
|
-
const now = new Date().toISOString();
|
|
804
|
-
const updateCount = (state?.updateCount ?? 0) + 1;
|
|
805
|
-
const spinner = SPINNER_EMOJIS[updateCount % SPINNER_EMOJIS.length];
|
|
806
|
-
const finalMilestone = {
|
|
807
|
-
title: opts.status === "completed" ? "Task 完成" : "Task 失败",
|
|
808
|
-
timestamp: now,
|
|
809
|
-
description: opts.finalStep
|
|
810
|
-
};
|
|
811
|
-
const card = buildTaskCard({
|
|
812
|
-
status: opts.status,
|
|
813
|
-
meta: mergeTaskMeta(state?.meta, opts.meta),
|
|
814
|
-
progress: opts.status === "completed" ? 100 : opts.progress ?? state?.progress,
|
|
815
|
-
milestones: mergeUnique(state?.milestones ?? [], [finalMilestone], milestoneKey),
|
|
816
|
-
processSteps: state?.processSteps ?? [],
|
|
817
|
-
updateCount,
|
|
818
|
-
spinner
|
|
819
|
-
});
|
|
820
|
-
await this.runLarkCli(this.buildPatchCardArgs({
|
|
821
|
-
messageId: opts.messageId,
|
|
822
|
-
profile: opts.profile ?? state?.profile,
|
|
823
|
-
card
|
|
824
|
-
}), "patch");
|
|
825
|
-
});
|
|
826
|
-
if (this.options.unpinOnFinish) {
|
|
827
|
-
const state = this.states.get(opts.messageId);
|
|
828
|
-
try {
|
|
829
|
-
await this.runLarkCli(this.buildUnpinArgs({
|
|
830
|
-
messageId: opts.messageId,
|
|
831
|
-
profile: opts.profile ?? state?.profile
|
|
832
|
-
}), "unpin");
|
|
833
|
-
} catch (unpinErr) {
|
|
834
|
-
logger3.warn(`Unpin failed (non-fatal) for ${opts.messageId}: ${unpinErr.message}`);
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
this.states.delete(opts.messageId);
|
|
838
|
-
this.mutexes.delete(opts.messageId);
|
|
839
|
-
}
|
|
840
|
-
trackedCardCount() {
|
|
841
|
-
return this.states.size;
|
|
842
|
-
}
|
|
843
|
-
buildSendCardArgs(opts) {
|
|
844
|
-
const args = [
|
|
845
|
-
"im",
|
|
846
|
-
"+messages-send",
|
|
847
|
-
"--chat-id",
|
|
848
|
-
opts.chatId,
|
|
849
|
-
"--msg-type",
|
|
850
|
-
"interactive",
|
|
851
|
-
"--content",
|
|
852
|
-
JSON.stringify(opts.card),
|
|
853
|
-
"--as",
|
|
854
|
-
"bot"
|
|
855
|
-
];
|
|
856
|
-
if (opts.profile) {
|
|
857
|
-
args.unshift("--profile", opts.profile);
|
|
858
|
-
}
|
|
859
|
-
return args;
|
|
860
|
-
}
|
|
861
|
-
buildPatchCardArgs(opts) {
|
|
862
|
-
const data = JSON.stringify({ content: JSON.stringify(opts.card) });
|
|
863
|
-
const args = [
|
|
864
|
-
"im",
|
|
865
|
-
"messages",
|
|
866
|
-
"patch",
|
|
867
|
-
"--message-id",
|
|
868
|
-
opts.messageId,
|
|
869
|
-
"--data",
|
|
870
|
-
data,
|
|
871
|
-
"--as",
|
|
872
|
-
"bot"
|
|
873
|
-
];
|
|
874
|
-
if (opts.profile) {
|
|
875
|
-
args.unshift("--profile", opts.profile);
|
|
876
|
-
}
|
|
877
|
-
return args;
|
|
878
|
-
}
|
|
879
|
-
buildPinArgs(opts) {
|
|
880
|
-
const args = [
|
|
881
|
-
"im",
|
|
882
|
-
"pins",
|
|
883
|
-
"create",
|
|
884
|
-
"--data",
|
|
885
|
-
JSON.stringify({ message_id: opts.messageId }),
|
|
886
|
-
"--as",
|
|
887
|
-
"bot"
|
|
888
|
-
];
|
|
889
|
-
if (opts.profile) {
|
|
890
|
-
args.unshift("--profile", opts.profile);
|
|
891
|
-
}
|
|
892
|
-
return args;
|
|
893
|
-
}
|
|
894
|
-
buildUnpinArgs(opts) {
|
|
895
|
-
const args = [
|
|
896
|
-
"im",
|
|
897
|
-
"pins",
|
|
898
|
-
"delete",
|
|
899
|
-
"--message-id",
|
|
900
|
-
opts.messageId,
|
|
901
|
-
"--yes",
|
|
902
|
-
"--as",
|
|
903
|
-
"bot"
|
|
904
|
-
];
|
|
905
|
-
if (opts.profile) {
|
|
906
|
-
args.unshift("--profile", opts.profile);
|
|
907
|
-
}
|
|
908
|
-
return args;
|
|
909
|
-
}
|
|
910
|
-
runLarkCli(argv, op) {
|
|
911
|
-
return new Promise((resolve, reject) => {
|
|
912
|
-
if (process.env.ROY_HOOK_DEBUG === "1") {
|
|
913
|
-
console.error(`[DEBUG LarkCardPatchNotifier.runLarkCli] op=${op} args=${JSON.stringify(argv).slice(0, 300)}`);
|
|
914
|
-
}
|
|
915
|
-
const proc = spawn("lark-cli", argv, {
|
|
916
|
-
shell: false,
|
|
917
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
918
|
-
});
|
|
919
|
-
let stdout = "";
|
|
920
|
-
let stderr = "";
|
|
921
|
-
proc.stdout?.on("data", (data) => {
|
|
922
|
-
stdout += data.toString();
|
|
923
|
-
});
|
|
924
|
-
proc.stderr?.on("data", (data) => {
|
|
925
|
-
stderr += data.toString();
|
|
926
|
-
});
|
|
927
|
-
proc.on("close", (code) => {
|
|
928
|
-
if (process.env.ROY_HOOK_DEBUG === "1") {
|
|
929
|
-
console.error(`[DEBUG LarkCardPatchNotifier.runLarkCli] op=${op} CLOSE code=${code} stdoutLen=${stdout.length} stderrLen=${stderr.length}`);
|
|
930
|
-
}
|
|
931
|
-
if (code === 0) {
|
|
932
|
-
resolve(stdout);
|
|
933
|
-
} else {
|
|
934
|
-
reject(new Error(`lark-cli ${op} exited with code ${code}: ${sanitize(stderr || stdout)}`));
|
|
935
|
-
}
|
|
936
|
-
});
|
|
937
|
-
proc.on("error", (error) => {
|
|
938
|
-
reject(error);
|
|
939
|
-
});
|
|
940
|
-
});
|
|
941
|
-
}
|
|
942
|
-
extractMessageId(stdout) {
|
|
943
|
-
try {
|
|
944
|
-
const obj = JSON.parse(stdout);
|
|
945
|
-
const data = obj?.data ?? obj;
|
|
946
|
-
const id = data?.message_id ?? data?.messageId ?? obj?.message_id ?? obj?.messageId;
|
|
947
|
-
return typeof id === "string" ? id : undefined;
|
|
948
|
-
} catch {
|
|
949
|
-
return;
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
__legacyDecorateClassTS([
|
|
954
|
-
TracedAs("lark-card.startTask", {
|
|
955
|
-
recordParams: true,
|
|
956
|
-
recordResult: true,
|
|
957
|
-
flattenParamFilter: true,
|
|
958
|
-
paramFilter: (args) => ({
|
|
959
|
-
chatId: args[0]?.chatId,
|
|
960
|
-
profile: args[0]?.profile,
|
|
961
|
-
taskId: args[0]?.taskId
|
|
962
|
-
}),
|
|
963
|
-
resultFilter: (r) => ({
|
|
964
|
-
messageId: r?.messageId,
|
|
965
|
-
pinned: r?.pinned
|
|
966
|
-
})
|
|
967
|
-
})
|
|
968
|
-
], LarkCardPatchNotifier.prototype, "startTask", null);
|
|
969
|
-
__legacyDecorateClassTS([
|
|
970
|
-
TracedAs("lark-card.updateTask", {
|
|
971
|
-
recordParams: true,
|
|
972
|
-
recordResult: false,
|
|
973
|
-
flattenParamFilter: true,
|
|
974
|
-
paramFilter: (args) => ({
|
|
975
|
-
messageId: args[0]?.messageId,
|
|
976
|
-
profile: args[0]?.profile,
|
|
977
|
-
status: args[0]?.status,
|
|
978
|
-
taskId: args[0]?.taskId,
|
|
979
|
-
progress: args[0]?.progress,
|
|
980
|
-
newMilestones: args[0]?.milestones?.length ?? 0,
|
|
981
|
-
newProcessSteps: args[0]?.processSteps?.length ?? 0
|
|
982
|
-
})
|
|
983
|
-
})
|
|
984
|
-
], LarkCardPatchNotifier.prototype, "updateTask", null);
|
|
985
|
-
__legacyDecorateClassTS([
|
|
986
|
-
TracedAs("lark-card.finishTask", {
|
|
987
|
-
recordParams: true,
|
|
988
|
-
recordResult: false,
|
|
989
|
-
flattenParamFilter: true,
|
|
990
|
-
paramFilter: (args) => ({
|
|
991
|
-
messageId: args[0]?.messageId,
|
|
992
|
-
profile: args[0]?.profile,
|
|
993
|
-
status: args[0]?.status,
|
|
994
|
-
taskId: args[0]?.taskId
|
|
995
|
-
})
|
|
996
|
-
})
|
|
997
|
-
], LarkCardPatchNotifier.prototype, "finishTask", null);
|
|
998
|
-
__legacyDecorateClassTS([
|
|
999
|
-
TracedAs("lark-card.runLarkCli", {
|
|
1000
|
-
recordParams: true,
|
|
1001
|
-
recordResult: true,
|
|
1002
|
-
flattenParamFilter: true,
|
|
1003
|
-
paramFilter: (args) => {
|
|
1004
|
-
const argv = args[0] ?? [];
|
|
1005
|
-
const op = args[1] ?? "unknown";
|
|
1006
|
-
return {
|
|
1007
|
-
op,
|
|
1008
|
-
argv: argv.join(" ").slice(0, 300),
|
|
1009
|
-
hasProfile: argv.includes("--profile"),
|
|
1010
|
-
msgType: valueAfter(argv, "--msg-type"),
|
|
1011
|
-
contentLength: (valueAfter(argv, "--content") ?? "").length,
|
|
1012
|
-
dataLength: (valueAfter(argv, "--data") ?? "").length
|
|
1013
|
-
};
|
|
1014
|
-
},
|
|
1015
|
-
resultFilter: (r) => ({
|
|
1016
|
-
stdoutLength: (r ?? "").length
|
|
1017
|
-
})
|
|
1018
|
-
})
|
|
1019
|
-
], LarkCardPatchNotifier.prototype, "runLarkCli", null);
|
|
1020
|
-
function mergeTaskMeta(base, incoming) {
|
|
1021
|
-
const merged = { ...base ?? {} };
|
|
1022
|
-
if (!incoming)
|
|
1023
|
-
return merged;
|
|
1024
|
-
for (const [k, v] of Object.entries(incoming)) {
|
|
1025
|
-
if (v === undefined || v === null || v === "")
|
|
1026
|
-
continue;
|
|
1027
|
-
merged[k] = v;
|
|
1028
|
-
}
|
|
1029
|
-
return merged;
|
|
1030
|
-
}
|
|
1031
|
-
function mergeUnique(base, incoming, keyOf) {
|
|
1032
|
-
const seen = new Set(base.map(keyOf));
|
|
1033
|
-
const merged = [...base];
|
|
1034
|
-
for (const item of incoming) {
|
|
1035
|
-
const k = keyOf(item);
|
|
1036
|
-
if (seen.has(k))
|
|
1037
|
-
continue;
|
|
1038
|
-
seen.add(k);
|
|
1039
|
-
merged.push(item);
|
|
1040
|
-
}
|
|
1041
|
-
return merged;
|
|
1042
|
-
}
|
|
1043
|
-
function spinnerIndexFor(updateCount) {
|
|
1044
|
-
return updateCount % SPINNER_EMOJIS.length;
|
|
1045
|
-
}
|
|
1046
|
-
function valueAfter(argv, flag) {
|
|
1047
|
-
const idx = argv.indexOf(flag);
|
|
1048
|
-
if (idx < 0 || idx + 1 >= argv.length)
|
|
1049
|
-
return;
|
|
1050
|
-
return argv[idx + 1];
|
|
1051
|
-
}
|
|
1052
|
-
function sanitize(text) {
|
|
1053
|
-
const trimmed = text.trim();
|
|
1054
|
-
if (trimmed.length <= 500)
|
|
1055
|
-
return trimmed;
|
|
1056
|
-
return `${trimmed.slice(0, 500)}…`;
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
380
|
// src/env/task/plugins/card-chat-routing.ts
|
|
1060
381
|
var GROUP_NAME_TASK_ID_RE = /(?:Task\s*#?\s*|#)(\d{1,7})\b/i;
|
|
1061
382
|
var DEDICATED_CHAT_ID_FIELD_RE = /^(larkPerTaskChatId|[a-z0-9_]*(followup|group|per_?task)[a-z0-9_]*chat[_]?id)$/i;
|
|
@@ -1231,7 +552,7 @@ function decideCardChatTarget(input) {
|
|
|
1231
552
|
}
|
|
1232
553
|
|
|
1233
554
|
// src/env/task/plugins/lark-cli-task-notify-plugin.ts
|
|
1234
|
-
var
|
|
555
|
+
var logger3 = createLogger("lark-cli:task-notify");
|
|
1235
556
|
var CTX_FIELD_CHAT_ID = "larkPerTaskChatId";
|
|
1236
557
|
var CTX_FIELD_CHAT_PROFILE = "larkPerTaskChatProfile";
|
|
1237
558
|
function parseLarkPerTaskChatId(ctxStr) {
|
|
@@ -1301,7 +622,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1301
622
|
}
|
|
1302
623
|
setCardRoutingPorts(ports) {
|
|
1303
624
|
this.cardRoutingPorts = ports;
|
|
1304
|
-
|
|
625
|
+
logger3.debug(`CardRoutingPorts injected: lookup=${typeof ports.lookupChatOwnership}, ` + `getTask=${typeof ports.getTask}, ensureGroup=${typeof ports.ensureTaskGroupChat}`);
|
|
1305
626
|
}
|
|
1306
627
|
hasCardRoutingPorts() {
|
|
1307
628
|
return this.cardRoutingPorts !== undefined;
|
|
@@ -1314,7 +635,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1314
635
|
if (this.pluginName && this.sourceId) {
|
|
1315
636
|
const key = `${this.sourceId}:${this.pluginName}`;
|
|
1316
637
|
if (env.pluginEnabled && env.pluginEnabled[key] !== true) {
|
|
1317
|
-
|
|
638
|
+
logger3.debug(`Plugin not enabled for source: ${key}`);
|
|
1318
639
|
return;
|
|
1319
640
|
}
|
|
1320
641
|
}
|
|
@@ -1389,7 +710,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1389
710
|
return;
|
|
1390
711
|
const messageId = this.cardMessageIds.get(operation.taskId);
|
|
1391
712
|
if (!messageId || messageId === LarkCliTaskNotifyHook.CARD_START_FAILED_SENTINEL) {
|
|
1392
|
-
|
|
713
|
+
logger3.debug(`onAfterOperationCreate: no card for task #${operation.taskId}, skip`);
|
|
1393
714
|
return;
|
|
1394
715
|
}
|
|
1395
716
|
const { milestones, processSteps } = this.deriveTimelineFromOperation(operation);
|
|
@@ -1406,12 +727,12 @@ class LarkCliTaskNotifyHook {
|
|
|
1406
727
|
processSteps
|
|
1407
728
|
});
|
|
1408
729
|
} catch (err) {
|
|
1409
|
-
|
|
730
|
+
logger3.error(`sendCardOperation failed for task #${operation.taskId}: ${err.message}`);
|
|
1410
731
|
}
|
|
1411
732
|
}
|
|
1412
733
|
isCardModeEnabled() {
|
|
1413
734
|
if (this.config.cardMode === false || !this.cardNotifier) {
|
|
1414
|
-
|
|
735
|
+
logger3.debug("cardMode=false (legacy opt-out), skip task notification (no text fallback)");
|
|
1415
736
|
return false;
|
|
1416
737
|
}
|
|
1417
738
|
return true;
|
|
@@ -1447,7 +768,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1447
768
|
try {
|
|
1448
769
|
triggerOwnership = await this.cardRoutingPorts.lookupChatOwnership(triggerChatId);
|
|
1449
770
|
} catch (err) {
|
|
1450
|
-
|
|
771
|
+
logger3.warn(`lookupChatOwnership failed for ${triggerChatId} (degrade to trigger chat): ${err.message}`);
|
|
1451
772
|
}
|
|
1452
773
|
}
|
|
1453
774
|
const decision = decideCardChatTarget({
|
|
@@ -1472,7 +793,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1472
793
|
};
|
|
1473
794
|
}
|
|
1474
795
|
} catch (err) {
|
|
1475
|
-
|
|
796
|
+
logger3.warn(`ensureTaskGroupChat failed for task #${task.id}: ${err.message}`);
|
|
1476
797
|
}
|
|
1477
798
|
}
|
|
1478
799
|
return {
|
|
@@ -1511,7 +832,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1511
832
|
return;
|
|
1512
833
|
const claim = this.cardMessageIds.get(task.id);
|
|
1513
834
|
if (claim) {
|
|
1514
|
-
|
|
835
|
+
logger3.debug(`sendCardStart: task #${task.id} 已有首卡(${claim})→ 跳过重复发卡`);
|
|
1515
836
|
return;
|
|
1516
837
|
}
|
|
1517
838
|
this.cardMessageIds.set(task.id, LarkCliTaskNotifyHook.CARD_START_INFLIGHT_SENTINEL);
|
|
@@ -1526,7 +847,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1526
847
|
const profile = target.profile;
|
|
1527
848
|
if (!chatId) {
|
|
1528
849
|
this.cardMessageIds.delete(task.id);
|
|
1529
|
-
|
|
850
|
+
logger3.debug(`sendCardStart: no chatId for task #${task.id}, skip`);
|
|
1530
851
|
return;
|
|
1531
852
|
}
|
|
1532
853
|
try {
|
|
@@ -1543,7 +864,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1543
864
|
]
|
|
1544
865
|
});
|
|
1545
866
|
this.cardMessageIds.set(task.id, messageId);
|
|
1546
|
-
|
|
867
|
+
logger3.debug(`sendCardStart: task #${task.id} → message ${messageId} (pinned=${pinned}) ` + `chat=${chatId} source=${target.source} reason=${target.reason}`);
|
|
1547
868
|
if (target.reroutedFrom) {
|
|
1548
869
|
await this.cardNotifier.sendRedirectNotice({
|
|
1549
870
|
chatId: target.reroutedFrom.chatId,
|
|
@@ -1557,7 +878,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1557
878
|
}
|
|
1558
879
|
} catch (err) {
|
|
1559
880
|
this.cardMessageIds.set(task.id, LarkCliTaskNotifyHook.CARD_START_FAILED_SENTINEL);
|
|
1560
|
-
|
|
881
|
+
logger3.error(`sendCardStart failed for task #${task.id}: ${err.message}`);
|
|
1561
882
|
}
|
|
1562
883
|
}
|
|
1563
884
|
async sendCardUpdate(task, changes) {
|
|
@@ -1568,15 +889,15 @@ class LarkCliTaskNotifyHook {
|
|
|
1568
889
|
const chatId = target?.chatId ?? this.resolveChatId(task, getEnvContextOrEmpty());
|
|
1569
890
|
const profile = target?.profile ?? this.resolveProfile(task, getEnvContextOrEmpty());
|
|
1570
891
|
if (!chatId) {
|
|
1571
|
-
|
|
892
|
+
logger3.debug(`sendCardUpdate: no chatId for task #${task.id}, skip`);
|
|
1572
893
|
return;
|
|
1573
894
|
}
|
|
1574
895
|
if (messageId === LarkCliTaskNotifyHook.CARD_START_FAILED_SENTINEL) {
|
|
1575
|
-
|
|
896
|
+
logger3.debug(`sendCardUpdate: startTask failed previously for task #${task.id}, skip`);
|
|
1576
897
|
return;
|
|
1577
898
|
}
|
|
1578
899
|
if (messageId === LarkCliTaskNotifyHook.CARD_START_INFLIGHT_SENTINEL) {
|
|
1579
|
-
|
|
900
|
+
logger3.debug(`sendCardUpdate: task #${task.id} 首卡正在发送中(in-flight)→ 跳过本次 update`);
|
|
1580
901
|
return;
|
|
1581
902
|
}
|
|
1582
903
|
if (!messageId) {
|
|
@@ -1590,7 +911,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1590
911
|
this.cardMessageIds.set(task.id, messageId);
|
|
1591
912
|
} catch (err) {
|
|
1592
913
|
this.cardMessageIds.set(task.id, LarkCliTaskNotifyHook.CARD_START_FAILED_SENTINEL);
|
|
1593
|
-
|
|
914
|
+
logger3.error(`sendCardUpdate fallback start failed: ${err.message}`);
|
|
1594
915
|
return;
|
|
1595
916
|
}
|
|
1596
917
|
}
|
|
@@ -1614,7 +935,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1614
935
|
processSteps
|
|
1615
936
|
});
|
|
1616
937
|
} catch (err) {
|
|
1617
|
-
|
|
938
|
+
logger3.error(`sendCardUpdate failed: ${err.message}`);
|
|
1618
939
|
}
|
|
1619
940
|
}
|
|
1620
941
|
async sendCardFinish(task) {
|
|
@@ -1624,7 +945,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1624
945
|
const ctx = getEnvContextOrEmpty();
|
|
1625
946
|
const profile = this.resolveProfile(task, ctx);
|
|
1626
947
|
if (!messageId || messageId === LarkCliTaskNotifyHook.CARD_START_FAILED_SENTINEL) {
|
|
1627
|
-
|
|
948
|
+
logger3.debug(`sendCardFinish: no card messageId for task #${task.id}, skip`);
|
|
1628
949
|
return;
|
|
1629
950
|
}
|
|
1630
951
|
const status = String(task.status) === "failed" || String(task.status) === "cancelled" || String(task.status) === "timeout" ? "failed" : "completed";
|
|
@@ -1638,7 +959,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1638
959
|
});
|
|
1639
960
|
this.cardMessageIds.delete(task.id);
|
|
1640
961
|
} catch (err) {
|
|
1641
|
-
|
|
962
|
+
logger3.error(`sendCardFinish failed: ${err.message}`);
|
|
1642
963
|
}
|
|
1643
964
|
}
|
|
1644
965
|
resolveChatId(task, env) {
|
|
@@ -1711,7 +1032,7 @@ class LarkCliTaskNotifyHook {
|
|
|
1711
1032
|
const now = Date.now();
|
|
1712
1033
|
const lastTime = this.lastNotifyTimestamps.get(key);
|
|
1713
1034
|
if (lastTime && now - lastTime < throttleMs) {
|
|
1714
|
-
|
|
1035
|
+
logger3.debug(`[throttle] skip OPERATION_CREATED patch for task #${operation.taskId} (${now - lastTime}ms ago)`);
|
|
1715
1036
|
return true;
|
|
1716
1037
|
}
|
|
1717
1038
|
this.lastNotifyTimestamps.set(key, now);
|
|
@@ -1755,8 +1076,8 @@ function createLarkCliTaskNotifyHook(config = {}) {
|
|
|
1755
1076
|
init_decorator();
|
|
1756
1077
|
init_logger();
|
|
1757
1078
|
init_context();
|
|
1758
|
-
import { spawn
|
|
1759
|
-
var
|
|
1079
|
+
import { spawn } from "child_process";
|
|
1080
|
+
var logger4 = createLogger("lark-cli:per-task-group");
|
|
1760
1081
|
function isPluginDebugEnabled() {
|
|
1761
1082
|
return process.env.ROY_TASK_NOTIFY_DEBUG === "1" || process.env.ROY_TRACE_DEBUG === "1";
|
|
1762
1083
|
}
|
|
@@ -1810,7 +1131,7 @@ class PerTaskGroupChatPlugin {
|
|
|
1810
1131
|
const renderedName = this.renderTemplate(this.config.nameTemplate, task);
|
|
1811
1132
|
const existingChatId = this.getTaskChatId(task);
|
|
1812
1133
|
if (existingChatId) {
|
|
1813
|
-
|
|
1134
|
+
logger4.debug(`ensureGroupChat[${this.pluginName}]: task #${task.id} 已有专属群 ${existingChatId} → 复用(不重复建群)`);
|
|
1814
1135
|
return {
|
|
1815
1136
|
chatId: existingChatId,
|
|
1816
1137
|
profile: this.parseProfileFromContext(task.context),
|
|
@@ -1819,7 +1140,7 @@ class PerTaskGroupChatPlugin {
|
|
|
1819
1140
|
}
|
|
1820
1141
|
const inflight = this.groupChatInflight.get(task.id);
|
|
1821
1142
|
if (inflight) {
|
|
1822
|
-
|
|
1143
|
+
logger4.debug(`ensureGroupChat[${this.pluginName}]: task #${task.id} 建群已在进行中 → 复用同一 promise`);
|
|
1823
1144
|
return inflight;
|
|
1824
1145
|
}
|
|
1825
1146
|
const pending = this.doEnsureGroupChat(task, renderedName, replyChannelOverride);
|
|
@@ -1833,7 +1154,7 @@ class PerTaskGroupChatPlugin {
|
|
|
1833
1154
|
async doEnsureGroupChat(task, name, replyChannelOverride) {
|
|
1834
1155
|
const replyChannel = replyChannelOverride ?? getEnvContextOrEmpty().replyChannel;
|
|
1835
1156
|
if (!replyChannel?.chatId && this.config.requireReplyChannel) {
|
|
1836
|
-
|
|
1157
|
+
logger4.warn(`ensureGroupChat[${this.pluginName}]: no replyChannel.chatId for task #${task.id} ` + `(requireReplyChannel=true) → 无法建群`);
|
|
1837
1158
|
return;
|
|
1838
1159
|
}
|
|
1839
1160
|
const description = this.renderTemplate(this.config.descriptionTemplate, task);
|
|
@@ -1851,30 +1172,30 @@ class PerTaskGroupChatPlugin {
|
|
|
1851
1172
|
await this.writeChatBinding(task, chatId, profile, "creating");
|
|
1852
1173
|
await this.attachChatIdToTask(task, chatId, profile);
|
|
1853
1174
|
await this.writeChatBinding(task, chatId, profile, "ready");
|
|
1854
|
-
|
|
1175
|
+
logger4.info(`ensureGroupChat[${this.pluginName}]: created group ${chatId} for task #${task.id}`);
|
|
1855
1176
|
return { chatId, profile, name };
|
|
1856
1177
|
} catch (error) {
|
|
1857
|
-
|
|
1178
|
+
logger4.error(`ensureGroupChat[${this.pluginName}]: failed for task #${task.id}: ${error}`);
|
|
1858
1179
|
return;
|
|
1859
1180
|
}
|
|
1860
1181
|
}
|
|
1861
1182
|
setTaskUpdater(updater) {
|
|
1862
1183
|
this.taskUpdater = updater;
|
|
1863
|
-
|
|
1184
|
+
logger4.debug(`TaskUpdater injected for plugin ${this.name} (sourceId=${this.sourceId})`);
|
|
1864
1185
|
}
|
|
1865
1186
|
hasTaskUpdater() {
|
|
1866
1187
|
return this.taskUpdater !== undefined;
|
|
1867
1188
|
}
|
|
1868
1189
|
setTaskGetter(getter) {
|
|
1869
1190
|
this.taskGetter = getter;
|
|
1870
|
-
|
|
1191
|
+
logger4.debug(`TaskGetter injected for plugin ${this.name} (sourceId=${this.sourceId})`);
|
|
1871
1192
|
}
|
|
1872
1193
|
hasTaskGetter() {
|
|
1873
1194
|
return this.taskGetter !== undefined;
|
|
1874
1195
|
}
|
|
1875
1196
|
setSessionComponent(sessionComponent) {
|
|
1876
1197
|
this.sessionComponent = sessionComponent;
|
|
1877
|
-
|
|
1198
|
+
logger4.debug(`SessionComponent injected for plugin ${this.name} (sourceId=${this.sourceId})`);
|
|
1878
1199
|
}
|
|
1879
1200
|
hasSessionComponent() {
|
|
1880
1201
|
return this.sessionComponent !== undefined;
|
|
@@ -1887,7 +1208,7 @@ class PerTaskGroupChatPlugin {
|
|
|
1887
1208
|
if (process.env.ROY_HOOK_DEBUG === "1") {
|
|
1888
1209
|
console.error(`[DEBUG per-task-group-chat.execute] → SKIP (config.enabled=false)`);
|
|
1889
1210
|
}
|
|
1890
|
-
|
|
1211
|
+
logger4.debug(`execute: plugin disabled (pluginName=${this.pluginName})`);
|
|
1891
1212
|
return;
|
|
1892
1213
|
}
|
|
1893
1214
|
if (this.pluginName && this.sourceId) {
|
|
@@ -1900,13 +1221,13 @@ class PerTaskGroupChatPlugin {
|
|
|
1900
1221
|
if (process.env.ROY_HOOK_DEBUG === "1") {
|
|
1901
1222
|
console.error(`[DEBUG per-task-group-chat.execute] → SKIP (plugin NOT enabled for ${key})`);
|
|
1902
1223
|
}
|
|
1903
|
-
|
|
1224
|
+
logger4.debug(`execute[${this.pluginName}]: plugin NOT enabled (key=${key}, ` + `env.pluginEnabled=${JSON.stringify(env.pluginEnabled)}, ` + `hookPoint=${ctx.hookPoint}). ` + `→ skip. 修复:roy-agent eventsource update <id> 必须包含 PerTaskGroupChatPlugin`);
|
|
1904
1225
|
return;
|
|
1905
1226
|
}
|
|
1906
1227
|
const enabledState = env.pluginEnabled ? env.pluginEnabled[key] === true ? "✅ enabled" : "❌ NOT enabled" : "⚠️ env.pluginEnabled 未设置 (env 没创建 / 插件未注册到 eventsource)";
|
|
1907
|
-
|
|
1228
|
+
logger4.debug(`execute[${this.pluginName}]: ${enabledState} (key=${key}, ` + `hookPoint=${ctx.hookPoint}, taskUpdaterInjected=${this.hasTaskUpdater()})`);
|
|
1908
1229
|
} else {
|
|
1909
|
-
|
|
1230
|
+
logger4.debug(`execute[${this.pluginName}]: missing pluginName/sourceId metadata ` + `(pluginName=${this.pluginName}, sourceId=${this.sourceId}), ` + `skipping pluginEnabled check. hookPoint=${ctx.hookPoint}`);
|
|
1910
1231
|
}
|
|
1911
1232
|
if (process.env.ROY_HOOK_DEBUG === "1") {
|
|
1912
1233
|
console.error(`[DEBUG per-task-group-chat.execute] → PASSED pluginEnabled check, will dispatch hookPoint=${ctx.hookPoint}`);
|
|
@@ -1925,13 +1246,13 @@ class PerTaskGroupChatPlugin {
|
|
|
1925
1246
|
await this.onAfterComplete(ctx);
|
|
1926
1247
|
break;
|
|
1927
1248
|
default:
|
|
1928
|
-
|
|
1249
|
+
logger4.debug(`execute: unhandled hookPoint=${ctx.hookPoint}, skip`);
|
|
1929
1250
|
}
|
|
1930
1251
|
}
|
|
1931
1252
|
async onAfterCreate(ctx) {
|
|
1932
1253
|
const data = ctx.data;
|
|
1933
1254
|
if (!data?.task) {
|
|
1934
|
-
|
|
1255
|
+
logger4.debug("onAfterCreate: task is missing, skip");
|
|
1935
1256
|
return;
|
|
1936
1257
|
}
|
|
1937
1258
|
const { task } = data;
|
|
@@ -1942,34 +1263,34 @@ class PerTaskGroupChatPlugin {
|
|
|
1942
1263
|
console.error(`[DEBUG per-task-group-chat.onAfterCreate] env.messageId=${env.messageId}`);
|
|
1943
1264
|
console.error(`[DEBUG per-task-group-chat.onAfterCreate] env.sourceId=${env.sourceId}`);
|
|
1944
1265
|
}
|
|
1945
|
-
|
|
1266
|
+
logger4.debug(`onAfterCreate[${this.pluginName}]: enter task=${task.id} "${task.title}" ` + `parent_task_id=${task.parent_task_id ?? "none"} ` + `requireReplyChannel=${this.config.requireReplyChannel} ` + `disableSubTaskSkip=${this.config.disableSubTaskSkip} ` + `sendWelcomeMessage=${this.config.sendWelcomeMessage}`);
|
|
1946
1267
|
const replyChannel = this.getReplyChannelFromCtx(ctx);
|
|
1947
1268
|
if (!replyChannel?.chatId && this.config.requireReplyChannel) {
|
|
1948
|
-
|
|
1269
|
+
logger4.debug(`onAfterCreate[${this.pluginName}]: no replyChannel.chatId for task #${task.id}, skip. ` + `修复:eventsource 收到 im.message.receive_v1 事件时 replyChannel 应有值;` + `或在 plugin config 设 requireReplyChannel=false 强制创建群聊(profile 降级到 fallbackProfile/无)`);
|
|
1949
1270
|
return;
|
|
1950
1271
|
}
|
|
1951
1272
|
if (!replyChannel?.chatId && !this.config.requireReplyChannel) {
|
|
1952
|
-
|
|
1273
|
+
logger4.debug(`onAfterCreate[${this.pluginName}]: no replyChannel but requireReplyChannel=false, ` + `proceeding with profile fallback (fallbackProfile="${this.config.fallbackProfile}")`);
|
|
1953
1274
|
}
|
|
1954
1275
|
if (this.isSubTask(task) && !this.config.disableSubTaskSkip) {
|
|
1955
|
-
|
|
1276
|
+
logger4.debug(`onAfterCreate[${this.pluginName}]: task #${task.id} is sub-task, ` + `skip group chat creation. 关闭方式:config.disableSubTaskSkip=true`);
|
|
1956
1277
|
return;
|
|
1957
1278
|
}
|
|
1958
1279
|
if (this.isSubTask(task) && this.config.disableSubTaskSkip) {
|
|
1959
|
-
|
|
1280
|
+
logger4.debug(`onAfterCreate[${this.pluginName}]: task #${task.id} is sub-task but ` + `disableSubTaskSkip=true, proceeding to create group chat`);
|
|
1960
1281
|
}
|
|
1961
1282
|
try {
|
|
1962
1283
|
const created = await this.ensureGroupChat(task, replyChannel);
|
|
1963
1284
|
if (!created?.chatId) {
|
|
1964
|
-
|
|
1285
|
+
logger4.error(`onAfterCreate[${this.pluginName}]: ensureGroupChat 未返回 chatId for task #${task.id}`);
|
|
1965
1286
|
if (!this.config.fallbackToMain) {
|
|
1966
1287
|
throw new Error(`per-task group chat unavailable for task #${task.id}`);
|
|
1967
1288
|
}
|
|
1968
1289
|
return;
|
|
1969
1290
|
}
|
|
1970
|
-
|
|
1291
|
+
logger4.info(`onAfterCreate[${this.pluginName}]: task #${task.id} → group ${created.chatId} ` + `(initial card owned by LarkCliTaskNotifyHook)`);
|
|
1971
1292
|
} catch (error) {
|
|
1972
|
-
|
|
1293
|
+
logger4.error(`onAfterCreate[${this.pluginName}]: group chat failed for task #${task.id}: ${error}`);
|
|
1973
1294
|
if (!this.config.fallbackToMain)
|
|
1974
1295
|
throw error;
|
|
1975
1296
|
}
|
|
@@ -1977,7 +1298,7 @@ class PerTaskGroupChatPlugin {
|
|
|
1977
1298
|
async writeChatBinding(task, chatId, profile, status, errorMessage) {
|
|
1978
1299
|
if (!this.sessionComponent) {
|
|
1979
1300
|
if (isPluginDebugEnabled()) {
|
|
1980
|
-
|
|
1301
|
+
logger4.debug(`writeChatBinding[${this.pluginName}]: no sessionComponent injected, ` + `skip chat-task binding for chat ${chatId} (status=${status})`);
|
|
1981
1302
|
}
|
|
1982
1303
|
return;
|
|
1983
1304
|
}
|
|
@@ -1990,40 +1311,40 @@ class PerTaskGroupChatPlugin {
|
|
|
1990
1311
|
errorMessage: errorMessage ?? null
|
|
1991
1312
|
});
|
|
1992
1313
|
} catch (bindErr) {
|
|
1993
|
-
|
|
1314
|
+
logger4.warn(`writeChatBinding[${this.pluginName}]: failed to write chat-task binding for ` + `chat ${chatId} / task #${task.id} (status=${status}): ${bindErr}. ` + `群消息 handler 将无法自动注入 parentTaskId。`);
|
|
1994
1315
|
}
|
|
1995
1316
|
}
|
|
1996
1317
|
async onAfterUpdate(ctx) {
|
|
1997
1318
|
const data = ctx.data;
|
|
1998
1319
|
if (!data?.task) {
|
|
1999
|
-
|
|
1320
|
+
logger4.debug("onAfterUpdate: task is missing, skip");
|
|
2000
1321
|
return;
|
|
2001
1322
|
}
|
|
2002
|
-
|
|
1323
|
+
logger4.debug(`onAfterUpdate[${this.pluginName}]: task #${data.task.id} 卡片由 ` + `LarkCliTaskNotifyHook 负责(本插件不再 patch),skip`);
|
|
2003
1324
|
}
|
|
2004
1325
|
async onAfterOperationCreate(ctx) {
|
|
2005
1326
|
const data = ctx.data;
|
|
2006
1327
|
if (!data?.operation) {
|
|
2007
|
-
|
|
1328
|
+
logger4.debug("onAfterOperationCreate: operation is missing, skip");
|
|
2008
1329
|
return;
|
|
2009
1330
|
}
|
|
2010
|
-
|
|
1331
|
+
logger4.debug(`onAfterOperationCreate[${this.pluginName}]: operation #${data.operation.id} ` + `卡片由 LarkCliTaskNotifyHook 负责,skip`);
|
|
2011
1332
|
}
|
|
2012
1333
|
async onAfterComplete(ctx) {
|
|
2013
1334
|
const data = ctx.data;
|
|
2014
1335
|
if (!data?.task) {
|
|
2015
|
-
|
|
1336
|
+
logger4.debug("onAfterComplete: task is missing, skip");
|
|
2016
1337
|
return;
|
|
2017
1338
|
}
|
|
2018
|
-
|
|
1339
|
+
logger4.debug(`onAfterComplete[${this.pluginName}]: task #${data.task.id} 终态卡片由 ` + `LarkCliTaskNotifyHook 负责,skip`);
|
|
2019
1340
|
}
|
|
2020
1341
|
createGroupChat(opts) {
|
|
2021
1342
|
const args = this.buildCreateGroupChatArgs(opts);
|
|
2022
1343
|
if (isPluginDebugEnabled()) {
|
|
2023
|
-
|
|
1344
|
+
logger4.debug(`Creating group chat: lark-cli ${args.join(" ")}`);
|
|
2024
1345
|
}
|
|
2025
1346
|
return new Promise((resolve, reject) => {
|
|
2026
|
-
const proc =
|
|
1347
|
+
const proc = spawn("lark-cli", args, {
|
|
2027
1348
|
shell: false,
|
|
2028
1349
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2029
1350
|
});
|
|
@@ -2095,16 +1416,16 @@ class PerTaskGroupChatPlugin {
|
|
|
2095
1416
|
parsed[CTX_FIELD_CHAT_TASK_ID] = task.id;
|
|
2096
1417
|
const newContext = JSON.stringify(parsed);
|
|
2097
1418
|
if (!this.taskUpdater) {
|
|
2098
|
-
|
|
1419
|
+
logger4.warn(`attachChatIdToTask[${this.pluginName}]: ⚠️ no taskUpdater injected, ` + `chat_id ${chatId} NOT persisted to db for task #${task.id}. ` + `后果:下次 AFTER_UPDATE 从 DB 加载 task 时 getTaskChatId() 返回 undefined,` + `所有 update/complete/operation 通知 silently 丢失。` + `修复:event-source-handlers.ts:registerPluginHooks 应自动注入 setTaskUpdater。`);
|
|
2099
1420
|
task.context = newContext;
|
|
2100
1421
|
return;
|
|
2101
1422
|
}
|
|
2102
1423
|
try {
|
|
2103
1424
|
await this.taskUpdater(task.id, newContext);
|
|
2104
1425
|
task.context = newContext;
|
|
2105
|
-
|
|
1426
|
+
logger4.debug(`attachChatIdToTask[${this.pluginName}]: ✅ persisted chat_id ${chatId} ` + `to task #${task.id} context`);
|
|
2106
1427
|
} catch (error) {
|
|
2107
|
-
|
|
1428
|
+
logger4.error(`Failed to persist chat_id to task #${task.id}: ${error}`);
|
|
2108
1429
|
task.context = newContext;
|
|
2109
1430
|
}
|
|
2110
1431
|
}
|
|
@@ -2199,15 +1520,15 @@ class PerTaskGroupChatPlugin {
|
|
|
2199
1520
|
}
|
|
2200
1521
|
resolveBotAppIds(replyChannel) {
|
|
2201
1522
|
if (this.config.botAppIds.length > 0) {
|
|
2202
|
-
|
|
1523
|
+
logger4.debug(`resolveBotAppIds[${this.pluginName}]: using config.botAppIds=${JSON.stringify(this.config.botAppIds)}`);
|
|
2203
1524
|
return this.config.botAppIds;
|
|
2204
1525
|
}
|
|
2205
1526
|
const appId = replyChannel ? replyChannel.appId : undefined;
|
|
2206
1527
|
if (appId) {
|
|
2207
|
-
|
|
1528
|
+
logger4.debug(`resolveBotAppIds[${this.pluginName}]: using replyChannel.appId=${appId} (auto-detected from event header.app_id)`);
|
|
2208
1529
|
return [appId];
|
|
2209
1530
|
}
|
|
2210
|
-
|
|
1531
|
+
logger4.warn(`resolveBotAppIds[${this.pluginName}]: ⚠️ no botAppIds found, creating group WITHOUT --bots. ` + `Bot will NOT be invited automatically. ` + `可能原因:(1) eventsource 没有收到 im.message.receive_v1 事件(replyChannel.appId 缺失);` + `(2) 配置了 requireReplyChannel=false 模式;` + `(3) buildReplyChannel 没透传 appId(应在 v1.10+ 修复)。` + `修复:手动设置 config.botAppIds: ["cli_xxx"] 或确认 eventsource 处理了 lark event。`);
|
|
2211
1532
|
return [];
|
|
2212
1533
|
}
|
|
2213
1534
|
resolveUserIds(replyChannel) {
|
|
@@ -2217,7 +1538,7 @@ class PerTaskGroupChatPlugin {
|
|
|
2217
1538
|
if (id && id !== "sender" && id.startsWith("ou_")) {
|
|
2218
1539
|
ids.add(id);
|
|
2219
1540
|
} else if (id && id !== "sender") {
|
|
2220
|
-
|
|
1541
|
+
logger4.debug(`resolveUserIds[${this.pluginName}]: skip non-open_id in defaultInviteUserIds: "${id}"`);
|
|
2221
1542
|
}
|
|
2222
1543
|
}
|
|
2223
1544
|
}
|
|
@@ -2240,7 +1561,7 @@ class PerTaskGroupChatPlugin {
|
|
|
2240
1561
|
}
|
|
2241
1562
|
}
|
|
2242
1563
|
if (isPluginDebugEnabled()) {
|
|
2243
|
-
|
|
1564
|
+
logger4.debug(`resolveUserIds[${this.pluginName}]: ` + `defaultInviteUserIds=${JSON.stringify(this.config.defaultInviteUserIds)} ` + `inviteUserIds=${JSON.stringify(this.config.inviteUserIds)} ` + `replyChannel.userId=${replyChannel?.userId ?? "(none)"} ` + `→ final userIds=${JSON.stringify(Array.from(ids))}`);
|
|
2244
1565
|
}
|
|
2245
1566
|
return Array.from(ids);
|
|
2246
1567
|
}
|