@ai-setting/roy-agent-core 1.7.65 → 1.7.67

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.
Files changed (64) hide show
  1. package/dist/env/agent/agent-component.d.ts +56 -3
  2. package/dist/env/agent/agent-component.js +2 -2
  3. package/dist/env/agent/index.js +2 -2
  4. package/dist/env/agent/types.d.ts +28 -0
  5. package/dist/env/environment.d.ts +13 -0
  6. package/dist/env/event-source/command-card-action.d.ts +105 -0
  7. package/dist/env/event-source/command-cards.d.ts +72 -0
  8. package/dist/env/event-source/command-registry.d.ts +181 -0
  9. package/dist/env/event-source/command-types.d.ts +8 -0
  10. package/dist/env/event-source/event-source-handlers.d.ts +66 -0
  11. package/dist/env/event-source/event-source-handlers.js +7 -4
  12. package/dist/env/event-source/fallback-model-command.d.ts +80 -0
  13. package/dist/env/event-source/index.d.ts +7 -0
  14. package/dist/env/event-source/index.js +48 -7
  15. package/dist/env/event-source/timer-handler.d.ts +93 -1
  16. package/dist/env/index.js +15 -14
  17. package/dist/env/llm/index.js +1 -1
  18. package/dist/env/prompt/index.js +3 -2
  19. package/dist/env/prompt/prompts-index.d.ts +3 -8
  20. package/dist/env/task/delegate/delegate-tool.d.ts +58 -2
  21. package/dist/env/task/delegate/delegate-tool.js +5 -2
  22. package/dist/env/task/delegate/index.js +3 -2
  23. package/dist/env/task/index.js +9 -8
  24. package/dist/env/task/mcp/index.js +3 -2
  25. package/dist/env/task/plugins/__e2e__/laya-completion-e2e.d.ts +16 -0
  26. package/dist/env/task/plugins/index.js +4 -3
  27. package/dist/env/task/plugins/laya-completion-plugin.d.ts +133 -0
  28. package/dist/env/task/plugins/laya-port.d.ts +23 -0
  29. package/dist/env/task/storage/index.js +2 -2
  30. package/dist/env/task/storage/migrations/003_add_process_description.d.ts +1 -1
  31. package/dist/env/task/storage/migrations/log-migration.d.ts +49 -3
  32. package/dist/env/task/task-component.d.ts +43 -0
  33. package/dist/env/task/tools/completion-copy.d.ts +27 -0
  34. package/dist/env/task/tools/index.js +2 -1
  35. package/dist/env/task/tools/operation/create-tool.d.ts +1 -1
  36. package/dist/env/task/tools/operation/index.js +1 -1
  37. package/dist/env/task/tools/operation/operation-types.d.ts +10 -8
  38. package/dist/env/task/types.d.ts +3 -3
  39. package/dist/env/tool/built-in/index.js +2 -2
  40. package/dist/env/tool/built-in/query-background-process-tool.d.ts +10 -0
  41. package/dist/env/tool/index.js +3 -3
  42. package/dist/env/tool/types.d.ts +6 -0
  43. package/dist/index.js +60 -19
  44. package/dist/shared/@ai-setting/{roy-agent-core-rjzdj0xb.js → roy-agent-core-02j24hrg.js} +2 -0
  45. package/dist/shared/@ai-setting/{roy-agent-core-hmdcz3g9.js → roy-agent-core-1k83dq3a.js} +16 -6
  46. package/dist/shared/@ai-setting/{roy-agent-core-e42v9bcq.js → roy-agent-core-1kxf910n.js} +1 -1
  47. package/dist/shared/@ai-setting/{roy-agent-core-amkqnyzy.js → roy-agent-core-58804s3j.js} +10 -0
  48. package/dist/shared/@ai-setting/roy-agent-core-6m4bw8jy.js +6 -0
  49. package/dist/shared/@ai-setting/roy-agent-core-8zas6w5q.js +1083 -0
  50. package/dist/shared/@ai-setting/{roy-agent-core-9bs6xrav.js → roy-agent-core-abqhkbn3.js} +6 -3
  51. package/dist/shared/@ai-setting/{roy-agent-core-f6c0nadn.js → roy-agent-core-b7jbqfsy.js} +25 -11
  52. package/dist/shared/@ai-setting/{roy-agent-core-hjejpcg5.js → roy-agent-core-dnjm17sa.js} +51 -9
  53. package/dist/shared/@ai-setting/{roy-agent-core-t0ktaevq.js → roy-agent-core-h5vmff8w.js} +60 -4
  54. package/dist/shared/@ai-setting/{roy-agent-core-xhrrgfdy.js → roy-agent-core-hfr6bzts.js} +168 -52
  55. package/dist/shared/@ai-setting/{roy-agent-core-18am1h07.js → roy-agent-core-k4ps1hxp.js} +1085 -46
  56. package/dist/shared/@ai-setting/{roy-agent-core-jz8dsypm.js → roy-agent-core-kpg25s2q.js} +1 -1
  57. package/dist/shared/@ai-setting/{roy-agent-core-6masakz4.js → roy-agent-core-ma35jyd9.js} +19 -8
  58. package/dist/shared/@ai-setting/{roy-agent-core-gxzxq1sf.js → roy-agent-core-ntq0gz7q.js} +1 -1
  59. package/dist/shared/@ai-setting/{roy-agent-core-rn57psp0.js → roy-agent-core-p5cz9m8v.js} +3 -3
  60. package/dist/shared/@ai-setting/{roy-agent-core-w07ymg29.js → roy-agent-core-wksgtqf9.js} +1 -1
  61. package/package.json +1 -1
  62. package/dist/shared/@ai-setting/roy-agent-core-6z412atw.js +0 -654
  63. /package/dist/shared/@ai-setting/{roy-agent-core-5enxfga2.js → roy-agent-core-b4x6x4k2.js} +0 -0
  64. /package/dist/shared/@ai-setting/{roy-agent-core-es467wsz.js → roy-agent-core-dt104qa1.js} +0 -0
@@ -13,7 +13,7 @@ import {
13
13
  createPerTaskGroupChatPlugin,
14
14
  extractPerTaskChatId,
15
15
  extractPerTaskChatName
16
- } from "./roy-agent-core-f6c0nadn.js";
16
+ } from "./roy-agent-core-b7jbqfsy.js";
17
17
  import {
18
18
  getSharedEventStore
19
19
  } from "./roy-agent-core-na18zwj0.js";
@@ -311,6 +311,88 @@ var SELF_REMINDER_MESSAGE = `[Timer Self-Reminder]
311
311
 
312
312
  请根据分析结果,创建 1-3 个有价值的任务并执行。`;
313
313
  var TIMER_OPTIONS_KEY_GET_MANAGER = "getBackgroundTaskManager";
314
+ var SCHEDULE_TICK_INTERVAL_MS = 30 * 1000;
315
+ var TIMER_OPTIONS_KEY_SCHEDULE = "schedule";
316
+ var TIMER_OPTIONS_KEY_SESSION_ID = "sessionId";
317
+ var TIMER_OPTIONS_KEY_SKIP_WHEN_RUNNING = "skipWhenRunning";
318
+ var TIME_PATTERN = /^([01]\d|2[0-3]):([0-5]\d)$/;
319
+ function isValidScheduleTime(time) {
320
+ return typeof time === "string" && TIME_PATTERN.test(time);
321
+ }
322
+ function parseSchedule(raw) {
323
+ if (!Array.isArray(raw))
324
+ return [];
325
+ const result = [];
326
+ for (const item of raw) {
327
+ if (!item || typeof item !== "object")
328
+ continue;
329
+ const obj = item;
330
+ if (!isValidScheduleTime(obj.time))
331
+ continue;
332
+ if (typeof obj.prompt !== "string" || obj.prompt.trim().length === 0)
333
+ continue;
334
+ const entry = {
335
+ time: obj.time,
336
+ prompt: obj.prompt
337
+ };
338
+ if (typeof obj.label === "string" && obj.label.length > 0) {
339
+ entry.label = obj.label;
340
+ }
341
+ if (typeof obj.sessionId === "string" && obj.sessionId.length > 0) {
342
+ entry.sessionId = obj.sessionId;
343
+ }
344
+ result.push(entry);
345
+ }
346
+ return result;
347
+ }
348
+ function validateScheduleConfig(config) {
349
+ const errors = [];
350
+ const options = config.options ?? {};
351
+ const raw = options[TIMER_OPTIONS_KEY_SCHEDULE];
352
+ if (raw === undefined || raw === null)
353
+ return errors;
354
+ if (!Array.isArray(raw)) {
355
+ errors.push("Timer schedule must be an array of { time, prompt } entries");
356
+ return errors;
357
+ }
358
+ if (raw.length === 0) {
359
+ errors.push("Timer schedule must not be empty");
360
+ return errors;
361
+ }
362
+ raw.forEach((item, index) => {
363
+ if (!item || typeof item !== "object") {
364
+ errors.push(`Timer schedule[${index}] must be an object`);
365
+ return;
366
+ }
367
+ const obj = item;
368
+ if (!isValidScheduleTime(obj.time)) {
369
+ errors.push(`Timer schedule[${index}].time must be in HH:MM format (24-hour), got: ${String(obj.time)}`);
370
+ }
371
+ if (typeof obj.prompt !== "string" || obj.prompt.trim().length === 0) {
372
+ errors.push(`Timer schedule[${index}].prompt is required and must be a non-empty string`);
373
+ }
374
+ });
375
+ const seenTimes = new Set;
376
+ const duplicatedTimes = new Set;
377
+ for (const item of raw) {
378
+ if (!item || typeof item !== "object")
379
+ continue;
380
+ const time = item.time;
381
+ if (!isValidScheduleTime(time))
382
+ continue;
383
+ if (seenTimes.has(time)) {
384
+ duplicatedTimes.add(time);
385
+ }
386
+ seenTimes.add(time);
387
+ }
388
+ if (duplicatedTimes.size > 0) {
389
+ errors.push(`Timer schedule contains duplicate time entries: ${Array.from(duplicatedTimes).join(", ")}`);
390
+ }
391
+ if (config.interval !== undefined && config.interval !== null) {
392
+ errors.push("Timer schedule and interval are mutually exclusive: use schedule for fixed daily times, or interval for periodic triggering");
393
+ }
394
+ return errors;
395
+ }
314
396
 
315
397
  class TimerInstance {
316
398
  config;
@@ -318,21 +400,38 @@ class TimerInstance {
318
400
  sessionId;
319
401
  getRunningTasks;
320
402
  customPrompt;
403
+ schedule;
404
+ scheduleMode;
405
+ invalidScheduleWarned = false;
406
+ scheduleSessionId;
407
+ skipWhenRunning;
408
+ firedKeys = new Set;
409
+ now;
321
410
  status = "created";
322
411
  timerId;
323
412
  eventHandler;
324
- constructor(config, getRunningTasks) {
413
+ constructor(config, getRunningTasks, now) {
325
414
  this.config = config;
326
- this.interval = typeof config.interval === "number" && config.interval > 0 ? config.interval : DEFAULT_TIMER_INTERVAL_MS;
415
+ this.now = now ?? (() => Date.now());
416
+ const options = config.options ?? {};
417
+ const rawSchedule = options[TIMER_OPTIONS_KEY_SCHEDULE];
418
+ const scheduleConfigured = rawSchedule !== undefined && rawSchedule !== null;
419
+ this.schedule = parseSchedule(rawSchedule);
420
+ this.scheduleMode = scheduleConfigured;
421
+ const isScheduleMode = this.scheduleMode;
422
+ this.interval = isScheduleMode ? SCHEDULE_TICK_INTERVAL_MS : typeof config.interval === "number" && config.interval > 0 ? config.interval : DEFAULT_TIMER_INTERVAL_MS;
327
423
  this.sessionId = `timer-${config.id}-${process.pid}`;
328
- const optionsPrompt = config.options?.prompt;
424
+ const explicitSessionId = options[TIMER_OPTIONS_KEY_SESSION_ID];
425
+ this.scheduleSessionId = typeof explicitSessionId === "string" && explicitSessionId.length > 0 ? explicitSessionId : `timer-${config.id}`;
426
+ this.skipWhenRunning = options[TIMER_OPTIONS_KEY_SKIP_WHEN_RUNNING] === true;
427
+ const optionsPrompt = options.prompt;
329
428
  if (typeof optionsPrompt === "string" && optionsPrompt.trim().length > 0) {
330
429
  this.customPrompt = optionsPrompt;
331
430
  }
332
431
  if (getRunningTasks) {
333
432
  this.getRunningTasks = getRunningTasks;
334
433
  } else {
335
- const factory = config.options?.[TIMER_OPTIONS_KEY_GET_MANAGER];
434
+ const factory = options[TIMER_OPTIONS_KEY_GET_MANAGER];
336
435
  if (typeof factory === "function") {
337
436
  const manager = factory();
338
437
  this.getRunningTasks = () => manager.listTasks();
@@ -341,6 +440,9 @@ class TimerInstance {
341
440
  }
342
441
  }
343
442
  }
443
+ get isScheduleMode() {
444
+ return this.scheduleMode;
445
+ }
344
446
  async start() {
345
447
  if (this.status === "running")
346
448
  return;
@@ -366,6 +468,10 @@ class TimerInstance {
366
468
  this.eventHandler = undefined;
367
469
  }
368
470
  tick() {
471
+ if (this.isScheduleMode) {
472
+ this.tickSchedule();
473
+ return;
474
+ }
369
475
  let allTasks;
370
476
  try {
371
477
  allTasks = this.getRunningTasks();
@@ -380,19 +486,81 @@ class TimerInstance {
380
486
  const event = this.buildSelfReminderEvent();
381
487
  this.eventHandler?.(event);
382
488
  }
383
- buildSelfReminderEvent() {
384
- const timestamp = Date.now();
385
- const message = this.customPrompt ?? SELF_REMINDER_MESSAGE;
489
+ tickSchedule() {
490
+ if (this.schedule.length === 0) {
491
+ if (!this.invalidScheduleWarned) {
492
+ this.invalidScheduleWarned = true;
493
+ console.warn(`[TimerInstance] Timer ${this.config.id} has an invalid or empty schedule configuration; ` + `no events will be fired. Check options.schedule (expected [{ time: "HH:MM", prompt: "..." }]).`);
494
+ }
495
+ return;
496
+ }
497
+ const now = new Date(this.now());
498
+ const hh = String(now.getHours()).padStart(2, "0");
499
+ const mm = String(now.getMinutes()).padStart(2, "0");
500
+ const currentTime = `${hh}:${mm}`;
501
+ const dateKey = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
502
+ for (const entry of this.schedule) {
503
+ if (entry.time !== currentTime)
504
+ continue;
505
+ const firedKey = `${dateKey}#${entry.time}`;
506
+ if (this.firedKeys.has(firedKey)) {
507
+ continue;
508
+ }
509
+ if (this.skipWhenRunning) {
510
+ let hasRunning = false;
511
+ try {
512
+ const allTasks = this.getRunningTasks();
513
+ hasRunning = Array.isArray(allTasks) && allTasks.some((t) => t.status === "running");
514
+ } catch (error) {
515
+ console.warn(`[TimerInstance] Failed to get running tasks, skipping tick: ${error}`);
516
+ return;
517
+ }
518
+ if (hasRunning) {
519
+ continue;
520
+ }
521
+ }
522
+ this.firedKeys.add(firedKey);
523
+ this.pruneFiredKeys(dateKey);
524
+ const event = this.buildSelfReminderEvent(entry);
525
+ this.eventHandler?.(event);
526
+ return;
527
+ }
528
+ }
529
+ pruneFiredKeys(currentDateKey) {
530
+ if (this.firedKeys.size <= 32)
531
+ return;
532
+ for (const key of this.firedKeys) {
533
+ const datePart = key.split("#")[0];
534
+ if (datePart !== currentDateKey) {
535
+ this.firedKeys.delete(key);
536
+ }
537
+ }
538
+ }
539
+ buildSelfReminderEvent(entry) {
540
+ const timestamp = this.now();
541
+ const message = entry?.prompt ?? this.customPrompt ?? SELF_REMINDER_MESSAGE;
542
+ const triggerSessionId = entry !== undefined ? entry.sessionId ?? this.scheduleSessionId : undefined;
543
+ const metadata = {
544
+ source: "event-source",
545
+ sourceId: this.config.id,
546
+ sourceMessageType: "timer.selfReminder",
547
+ sessionId: this.sessionId
548
+ };
549
+ if (triggerSessionId !== undefined) {
550
+ metadata.trigger_session_id = triggerSessionId;
551
+ metadata.createSessionIfMissing = true;
552
+ }
553
+ if (entry?.label !== undefined) {
554
+ metadata.scheduleLabel = entry.label;
555
+ }
556
+ if (entry !== undefined) {
557
+ metadata.scheduleTime = entry.time;
558
+ }
386
559
  return {
387
560
  id: `es-${this.config.id}-${timestamp}-${Math.random().toString(36).slice(2, 8)}`,
388
561
  type: "event-source.event.timer",
389
562
  timestamp,
390
- metadata: {
391
- source: "event-source",
392
- sourceId: this.config.id,
393
- sourceMessageType: "timer.selfReminder",
394
- sessionId: this.sessionId
395
- },
563
+ metadata,
396
564
  payload: {
397
565
  sourceId: this.config.id,
398
566
  sourceType: "timer",
@@ -415,6 +583,9 @@ class TimerInstance {
415
583
  __legacyDecorateClassTS([
416
584
  TracedAs("timer.tick", { recordParams: false, log: false })
417
585
  ], TimerInstance.prototype, "tick", null);
586
+ __legacyDecorateClassTS([
587
+ TracedAs("timer.tickSchedule", { recordParams: false, log: false })
588
+ ], TimerInstance.prototype, "tickSchedule", null);
418
589
  __legacyDecorateClassTS([
419
590
  TracedAs("timer.buildSelfReminderEvent", { recordParams: false, log: false })
420
591
  ], TimerInstance.prototype, "buildSelfReminderEvent", null);
@@ -423,7 +594,9 @@ var timerHandler = {
423
594
  validateConfig(config) {
424
595
  const errors = [];
425
596
  const { interval } = config;
426
- if (interval !== undefined && interval !== null) {
597
+ errors.push(...validateScheduleConfig(config));
598
+ const hasSchedule = config.options?.[TIMER_OPTIONS_KEY_SCHEDULE] !== undefined;
599
+ if (!hasSchedule && interval !== undefined && interval !== null) {
427
600
  if (typeof interval !== "number" || !Number.isFinite(interval)) {
428
601
  errors.push("Timer interval must be a finite number");
429
602
  } else if (interval <= 0) {
@@ -439,7 +612,7 @@ var timerHandler = {
439
612
  return [
440
613
  {
441
614
  name: "interval",
442
- description: `Timer interval in milliseconds (default: ${DEFAULT_TIMER_INTERVAL_MS} = 10 minutes)`,
615
+ description: `Timer interval in milliseconds (default: ${DEFAULT_TIMER_INTERVAL_MS} = 10 minutes). Mutually exclusive with schedule.`,
443
616
  required: false,
444
617
  type: "number"
445
618
  },
@@ -448,6 +621,18 @@ var timerHandler = {
448
621
  description: "Custom prompt message for SelfReminder events (replaces default message)",
449
622
  required: false,
450
623
  type: "string"
624
+ },
625
+ {
626
+ name: TIMER_OPTIONS_KEY_SCHEDULE,
627
+ description: "JSON array of { time: 'HH:MM', prompt: string, label?: string, sessionId?: string } " + "for fixed daily trigger times (e.g. 10:00 / 17:00 / 20:00). Mutually exclusive with interval.",
628
+ required: false,
629
+ type: "string"
630
+ },
631
+ {
632
+ name: TIMER_OPTIONS_KEY_SESSION_ID,
633
+ description: "Explicit session ID for timer events (schedule mode). Events are routed to this " + "session instead of the main REPL session.",
634
+ required: false,
635
+ type: "string"
451
636
  }
452
637
  ];
453
638
  }
@@ -1083,10 +1268,658 @@ var bountyCliHandler = {
1083
1268
  }
1084
1269
  };
1085
1270
 
1271
+ // src/env/event-source/command-registry.ts
1272
+ init_logger();
1273
+
1274
+ // src/env/event-source/fallback-model-command.ts
1275
+ import * as fs from "fs";
1276
+ import * as os from "os";
1277
+ import * as path from "path";
1278
+ import { modify, applyEdits, parse as parseJSONCInternal } from "jsonc-parser";
1279
+
1280
+ // src/env/event-source/command-cards.ts
1281
+ var THEME = {
1282
+ primary: "blue",
1283
+ success: "green",
1284
+ warning: "orange"
1285
+ };
1286
+ var FALLBACK_MODEL_ACTION_NS = "fbmodel";
1287
+ var FALLBACK_MODEL_ACTIONS = {
1288
+ add: `${FALLBACK_MODEL_ACTION_NS}_add`,
1289
+ up: `${FALLBACK_MODEL_ACTION_NS}_up`,
1290
+ down: `${FALLBACK_MODEL_ACTION_NS}_down`
1291
+ };
1292
+ function isFallbackModelCard(card) {
1293
+ if (!card || typeof card !== "object")
1294
+ return false;
1295
+ const c = card;
1296
+ const header = c.header;
1297
+ const title = header?.title;
1298
+ return title?.content === "⚙️ Fallback Model 列表";
1299
+ }
1300
+ function renderTextCard(title, content) {
1301
+ return {
1302
+ schema: "2.0",
1303
+ config: { wide_screen_mode: true },
1304
+ header: {
1305
+ title: { tag: "plain_text", content: title },
1306
+ template: THEME.primary
1307
+ },
1308
+ body: {
1309
+ direction: "vertical",
1310
+ elements: [
1311
+ {
1312
+ tag: "markdown",
1313
+ content
1314
+ }
1315
+ ]
1316
+ }
1317
+ };
1318
+ }
1319
+ function renderFallbackModelCard(list, primaryModel) {
1320
+ const elements = [];
1321
+ if (primaryModel) {
1322
+ elements.push({
1323
+ tag: "markdown",
1324
+ content: `**主模型**:\`${primaryModel}\``
1325
+ });
1326
+ }
1327
+ if (list.length === 0) {
1328
+ elements.push({
1329
+ tag: "markdown",
1330
+ content: "**fallback 列表**:⚠️ 未配置。主模型失败时不会自动降级。"
1331
+ });
1332
+ } else {
1333
+ elements.push({
1334
+ tag: "markdown",
1335
+ content: "**fallback 列表**(顺序 = 优先级,1 最高):"
1336
+ });
1337
+ list.forEach((model, i) => {
1338
+ const row = [
1339
+ {
1340
+ tag: "markdown",
1341
+ content: `${i + 1}. \`${model}\``
1342
+ }
1343
+ ];
1344
+ if (i > 0) {
1345
+ row.push({
1346
+ tag: "button",
1347
+ text: { tag: "plain_text", content: "↑" },
1348
+ type: "default",
1349
+ size: "small",
1350
+ behaviors: [
1351
+ {
1352
+ type: "callback",
1353
+ value: {
1354
+ action: FALLBACK_MODEL_ACTIONS.up,
1355
+ index: i + 1,
1356
+ model
1357
+ }
1358
+ }
1359
+ ]
1360
+ });
1361
+ }
1362
+ if (i < list.length - 1) {
1363
+ row.push({
1364
+ tag: "button",
1365
+ text: { tag: "plain_text", content: "↓" },
1366
+ type: "default",
1367
+ size: "small",
1368
+ behaviors: [
1369
+ {
1370
+ type: "callback",
1371
+ value: {
1372
+ action: FALLBACK_MODEL_ACTIONS.down,
1373
+ index: i + 1,
1374
+ model
1375
+ }
1376
+ }
1377
+ ]
1378
+ });
1379
+ }
1380
+ elements.push({
1381
+ tag: "column_set",
1382
+ horizontal_spacing: "small",
1383
+ columns: [
1384
+ { tag: "column", width: "weighted", weight: 1, elements: row }
1385
+ ]
1386
+ });
1387
+ });
1388
+ }
1389
+ elements.push({ tag: "hr" });
1390
+ elements.push({
1391
+ tag: "form",
1392
+ name: "fbmodel_form",
1393
+ elements: [
1394
+ {
1395
+ tag: "input",
1396
+ name: "fbmodel_model",
1397
+ placeholder: { tag: "plain_text", content: "provider/model,如 tong/GLM-5.3-Flash" },
1398
+ required: true
1399
+ },
1400
+ {
1401
+ tag: "button",
1402
+ text: { tag: "plain_text", content: "新增到 fallback 列表" },
1403
+ type: "primary_filled",
1404
+ form_action_type: "submit",
1405
+ name: FALLBACK_MODEL_ACTIONS.add
1406
+ }
1407
+ ]
1408
+ });
1409
+ elements.push({ tag: "hr" });
1410
+ elements.push({
1411
+ tag: "markdown",
1412
+ content: [
1413
+ "**文本指令**(按钮不可用时用这个):",
1414
+ "- 新增:`##command##: fallback-model add <provider/model>`",
1415
+ "- 删除:`##command##: fallback-model remove <序号>`",
1416
+ "- 调优先级:`##command##: fallback-model reorder <原序号> <新序号>`",
1417
+ "- 查看:`##command##: list model`"
1418
+ ].join(`
1419
+ `)
1420
+ });
1421
+ return {
1422
+ schema: "2.0",
1423
+ config: { wide_screen_mode: true },
1424
+ header: {
1425
+ title: { tag: "plain_text", content: "⚙️ Fallback Model 列表" },
1426
+ template: THEME.primary
1427
+ },
1428
+ body: {
1429
+ direction: "vertical",
1430
+ elements
1431
+ }
1432
+ };
1433
+ }
1434
+
1435
+ // src/env/event-source/fallback-model-command.ts
1436
+ var DEFAULT_CONFIG_PATH = path.join(os.homedir(), ".roy-agent", "config.jsonc");
1437
+ function readConfigFile(configPath) {
1438
+ if (!fs.existsSync(configPath))
1439
+ return {};
1440
+ const text = fs.readFileSync(configPath, "utf-8");
1441
+ const errors = [];
1442
+ const parsed = parseJSONCInternal(text, errors, {
1443
+ allowTrailingComma: true,
1444
+ disallowComments: false
1445
+ });
1446
+ if (errors.length > 0 && parsed === undefined) {
1447
+ throw new Error(`config.jsonc 解析失败:${configPath}`);
1448
+ }
1449
+ return parsed ?? {};
1450
+ }
1451
+ function assertObjectParent(configPath, parsed, dotPath) {
1452
+ let cursor = parsed;
1453
+ for (let i = 0;i < dotPath.length - 1; i++) {
1454
+ if (cursor === undefined || cursor === null) {
1455
+ return;
1456
+ }
1457
+ if (typeof cursor !== "object" || Array.isArray(cursor)) {
1458
+ throw new Error(`config.jsonc 的「${dotPath.slice(0, i + 1).join(".")}」不是对象,` + `无法写入 ${dotPath.join(".")}(${configPath})`);
1459
+ }
1460
+ cursor = cursor[dotPath[i]];
1461
+ }
1462
+ if (cursor === undefined)
1463
+ return;
1464
+ if (cursor === null || typeof cursor !== "object" || Array.isArray(cursor)) {
1465
+ throw new Error(`config.jsonc 的「${dotPath.slice(0, -1).join(".")}」不是对象,` + `无法写入 ${dotPath.join(".")}(${configPath})`);
1466
+ }
1467
+ }
1468
+ function writeConfigPath(configPath, dotPath, value) {
1469
+ let text;
1470
+ let parsedForCheck = {};
1471
+ if (fs.existsSync(configPath)) {
1472
+ text = fs.readFileSync(configPath, "utf-8");
1473
+ const errors = [];
1474
+ const parsed = parseJSONCInternal(text, errors, { allowTrailingComma: true });
1475
+ if (errors.length > 0) {
1476
+ throw new Error(`config.jsonc 已损坏(解析失败),拒绝写入:${configPath}`);
1477
+ }
1478
+ parsedForCheck = parsed ?? {};
1479
+ } else {
1480
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
1481
+ text = "{}";
1482
+ }
1483
+ assertObjectParent(configPath, parsedForCheck, dotPath);
1484
+ const edits = modify(text, dotPath, value, {
1485
+ formattingOptions: {
1486
+ tabSize: 2,
1487
+ insertSpaces: true
1488
+ }
1489
+ });
1490
+ const result = applyEdits(text, edits);
1491
+ fs.writeFileSync(configPath, result, "utf-8");
1492
+ }
1493
+ function readFallbackModelList(configPath) {
1494
+ const config = readConfigFile(configPath);
1495
+ const llm = config.llm ?? {};
1496
+ const arr = llm.fallbackModels;
1497
+ if (Array.isArray(arr)) {
1498
+ return arr.map((m) => typeof m === "string" ? m.trim() : "");
1499
+ }
1500
+ const single = llm.fallbackModel;
1501
+ if (typeof single === "string" && single.trim() !== "") {
1502
+ return [single.trim()];
1503
+ }
1504
+ return [];
1505
+ }
1506
+ function readRawFallbackModelArray(configPath) {
1507
+ const config = readConfigFile(configPath);
1508
+ const llm = config.llm ?? {};
1509
+ const arr = llm.fallbackModels;
1510
+ if (Array.isArray(arr))
1511
+ return [...arr];
1512
+ const single = llm.fallbackModel;
1513
+ if (typeof single === "string" && single.trim() !== "") {
1514
+ return [single.trim()];
1515
+ }
1516
+ return [];
1517
+ }
1518
+ function writeFallbackModelList(configPath, list) {
1519
+ const raw = readRawFallbackModelArray(configPath);
1520
+ const next = raw.slice(0, list.length);
1521
+ for (let i = 0;i < list.length; i++) {
1522
+ const existing = next[i];
1523
+ if (typeof existing !== "string" && existing !== undefined)
1524
+ continue;
1525
+ next[i] = list[i];
1526
+ }
1527
+ while (next.length < list.length)
1528
+ next.push(list[next.length]);
1529
+ writeConfigPath(configPath, ["llm", "fallbackModels"], next);
1530
+ }
1531
+ var USAGE = [
1532
+ "用法:##command##: fallback-model <子命令> [参数]",
1533
+ "",
1534
+ "子命令:",
1535
+ " list 查看兜底模型列表(顺序 = 优先级)",
1536
+ " add <provider/model> 新增到列表末尾(最低优先级)",
1537
+ " remove <序号> 按序号删除(1-based)",
1538
+ " reorder <原序号> <新序号> 调整优先级(1-based)",
1539
+ " move <模型标识> <up|down> 按模型标识上下移动(卡片按钮用)"
1540
+ ].join(`
1541
+ `);
1542
+ function effectiveList(configPath) {
1543
+ return readFallbackModelList(configPath).filter((m) => m !== "");
1544
+ }
1545
+ function rawWithIndexMap(configPath) {
1546
+ const raw = readRawFallbackModelArray(configPath);
1547
+ const map = [];
1548
+ raw.forEach((m, i) => {
1549
+ if (typeof m === "string" && m.trim() !== "")
1550
+ map.push(i);
1551
+ });
1552
+ return { raw, map };
1553
+ }
1554
+ function moveInRaw(raw, map, fromEff, toEff) {
1555
+ const rawFrom = map[fromEff];
1556
+ const [moved] = raw.splice(rawFrom, 1);
1557
+ const rawTo = map[toEff] > rawFrom ? map[toEff] - 1 : map[toEff];
1558
+ raw.splice(rawTo, 0, moved);
1559
+ }
1560
+ function dualResult(text, configPath) {
1561
+ const list = effectiveList(configPath);
1562
+ const config = readConfigFile(configPath);
1563
+ const llm = config.llm ?? {};
1564
+ const primary = typeof llm.defaultModel === "string" ? llm.defaultModel : undefined;
1565
+ return {
1566
+ ok: true,
1567
+ output: {
1568
+ kind: "card",
1569
+ text,
1570
+ card: renderFallbackModelCard(list, primary)
1571
+ }
1572
+ };
1573
+ }
1574
+ function parsePositiveIndex(raw) {
1575
+ const n = Number.parseInt(raw, 10);
1576
+ if (!Number.isFinite(n) || n < 1)
1577
+ return null;
1578
+ return n;
1579
+ }
1580
+ async function executeFallbackModel(ctx) {
1581
+ const configPath = ctx.configPath ?? DEFAULT_CONFIG_PATH;
1582
+ const [sub, ...rest] = ctx.args;
1583
+ switch (sub) {
1584
+ case "list":
1585
+ case "show": {
1586
+ const list = effectiveList(configPath);
1587
+ const config = readConfigFile(configPath);
1588
+ const llm = config.llm ?? {};
1589
+ const primary = typeof llm.defaultModel === "string" ? llm.defaultModel : undefined;
1590
+ const text = list.length ? `\uD83D\uDCCB 兜底模型列表(顺序 = 优先级,1 最高):
1591
+ ${list.map((m, i) => ` ${i + 1}. ${m}`).join(`
1592
+ `)}` : "⚠️ 兜底模型列表未配置(主模型失败时不会自动降级)";
1593
+ return {
1594
+ ok: true,
1595
+ output: {
1596
+ kind: "card",
1597
+ text,
1598
+ card: renderFallbackModelCard(list, primary)
1599
+ }
1600
+ };
1601
+ }
1602
+ case "add": {
1603
+ const model = rest[0]?.trim();
1604
+ if (!model) {
1605
+ return { ok: false, error: `用法:##command##: fallback-model add <provider/model>
1606
+ ${USAGE}` };
1607
+ }
1608
+ const list = effectiveList(configPath);
1609
+ if (list.some((m) => m === model)) {
1610
+ return { ok: false, error: `「${model}」已在 fallback 列表中(重复添加被拒绝)` };
1611
+ }
1612
+ const raw = readRawFallbackModelArray(configPath);
1613
+ const next = [...raw, model];
1614
+ writeConfigPath(configPath, ["llm", "fallbackModels"], next);
1615
+ return dualResult(`✅ 已添加「${model}」到 fallback 列表末尾(当前 ${list.length + 1} 项:${list.join(" → ")} → ${model})`, configPath);
1616
+ }
1617
+ case "remove":
1618
+ case "rm": {
1619
+ const idx = rest[0] !== undefined ? parsePositiveIndex(rest[0]) : null;
1620
+ if (idx === null) {
1621
+ return { ok: false, error: `用法:##command##: fallback-model remove <序号>(1-based)
1622
+ ${USAGE}` };
1623
+ }
1624
+ const list = effectiveList(configPath);
1625
+ if (idx > list.length) {
1626
+ return {
1627
+ ok: false,
1628
+ error: `序号 ${idx} 越界(当前列表 ${list.length} 项:${list.map((m, i) => `${i + 1}.${m}`).join(",") || "空"})`
1629
+ };
1630
+ }
1631
+ const { raw, map } = rawWithIndexMap(configPath);
1632
+ const rawIdx = map[idx - 1];
1633
+ const removed = list[idx - 1];
1634
+ raw.splice(rawIdx, 1);
1635
+ writeConfigPath(configPath, ["llm", "fallbackModels"], raw);
1636
+ return dualResult(`✅ 已删除「${removed}」(剩余 ${list.length - 1} 项${list.length > 1 ? `:${list.filter((_, i) => i !== idx - 1).join(" → ")}` : ""})`, configPath);
1637
+ }
1638
+ case "move": {
1639
+ const model = rest[0]?.trim();
1640
+ const direction = rest[1]?.trim().toLowerCase();
1641
+ if (!model || direction !== "up" && direction !== "down") {
1642
+ return {
1643
+ ok: false,
1644
+ error: `用法:##command##: fallback-model move <模型标识> <up|down>
1645
+ ${USAGE}`
1646
+ };
1647
+ }
1648
+ const list = effectiveList(configPath);
1649
+ const idx = list.findIndex((m) => m === model);
1650
+ if (idx < 0) {
1651
+ return {
1652
+ ok: false,
1653
+ error: `列表中找不到「${model}」(当前:${list.map((m, i) => `${i + 1}.${m}`).join(",") || "空"})`
1654
+ };
1655
+ }
1656
+ const targetIdx = direction === "up" ? idx - 1 : idx + 1;
1657
+ if (targetIdx < 0 || targetIdx >= list.length) {
1658
+ return {
1659
+ ok: false,
1660
+ error: `「${model}」已经在${direction === "up" ? "第 1 位" : "最后一位"},无法再${direction === "up" ? "上移" : "下移"}`
1661
+ };
1662
+ }
1663
+ const { raw, map } = rawWithIndexMap(configPath);
1664
+ moveInRaw(raw, map, idx, targetIdx);
1665
+ writeConfigPath(configPath, ["llm", "fallbackModels"], raw);
1666
+ const nextOrder = effectiveList(configPath);
1667
+ return dualResult(`✅ 已把「${model}」移到第 ${targetIdx + 1} 位(当前顺序:${nextOrder.join(" → ")})`, configPath);
1668
+ }
1669
+ case "reorder":
1670
+ case "move-index": {
1671
+ const from = rest[0] !== undefined ? parsePositiveIndex(rest[0]) : null;
1672
+ const to = rest[1] !== undefined ? parsePositiveIndex(rest[1]) : null;
1673
+ if (from === null || to === null) {
1674
+ return {
1675
+ ok: false,
1676
+ error: `用法:##command##: fallback-model reorder <原序号> <新序号>(1-based)
1677
+ ${USAGE}`
1678
+ };
1679
+ }
1680
+ const list = effectiveList(configPath);
1681
+ if (from > list.length || to > list.length) {
1682
+ return {
1683
+ ok: false,
1684
+ error: `序号越界(当前列表 ${list.length} 项:${list.map((m, i) => `${i + 1}.${m}`).join(",") || "空"})`
1685
+ };
1686
+ }
1687
+ const { raw, map } = rawWithIndexMap(configPath);
1688
+ const moved = raw[map[from - 1]];
1689
+ moveInRaw(raw, map, from - 1, to - 1);
1690
+ writeConfigPath(configPath, ["llm", "fallbackModels"], raw);
1691
+ const nextOrder = effectiveList(configPath);
1692
+ return dualResult(`✅ 已把「${moved}」移到第 ${to} 位(当前顺序:${nextOrder.join(" → ")})`, configPath);
1693
+ }
1694
+ case undefined:
1695
+ return { ok: false, error: USAGE };
1696
+ default:
1697
+ return { ok: false, error: `未知子命令「${sub}」。
1698
+ ${USAGE}` };
1699
+ }
1700
+ }
1701
+ var fallbackModelCommand = {
1702
+ name: "fallback-model",
1703
+ description: "管理兜底模型列表(llm.fallbackModels,顺序 = 优先级)",
1704
+ usage: "fallback-model list|add|remove|reorder [参数]",
1705
+ execute: executeFallbackModel
1706
+ };
1707
+ var listCommand = {
1708
+ name: "list",
1709
+ description: "查看资源列表(list model = fallback-model list)",
1710
+ usage: "list model",
1711
+ execute: async (ctx) => {
1712
+ const resource = ctx.args[0];
1713
+ if (resource === "model") {
1714
+ return executeFallbackModel({ ...ctx, args: ["list"] });
1715
+ }
1716
+ return {
1717
+ ok: false,
1718
+ error: `未知资源「${resource ?? "(空)"}」。可用:list model`
1719
+ };
1720
+ }
1721
+ };
1722
+
1723
+ // src/env/event-source/command-registry.ts
1724
+ var logger2 = createLogger("command-registry");
1725
+ var COMMAND_PREFIX = "##command##:";
1726
+ var COMMAND_PREFIX_LOWER = COMMAND_PREFIX.toLowerCase();
1727
+ function stripLeadingMentions(text) {
1728
+ let rest = text;
1729
+ const atTagRe = /^\s*<at\s+user_id\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)\s*(?:\/>|>\s*<\/at\s*>|>[^<]*<\/at\s*>)/i;
1730
+ for (;; ) {
1731
+ const m = rest.match(atTagRe);
1732
+ if (!m)
1733
+ break;
1734
+ rest = rest.slice(m[0].length);
1735
+ }
1736
+ const legacyAtRe = /^\s*@_(?:user_\d+|all)\b/i;
1737
+ for (;; ) {
1738
+ const m = rest.match(legacyAtRe);
1739
+ if (!m)
1740
+ break;
1741
+ rest = rest.slice(m[0].length);
1742
+ }
1743
+ return rest;
1744
+ }
1745
+ function isCommandMessage(text) {
1746
+ return stripLeadingMentions(text).trimStart().toLowerCase().startsWith(COMMAND_PREFIX_LOWER);
1747
+ }
1748
+ function extractCommandInvocation(text) {
1749
+ const trimmed = stripLeadingMentions(text).trimStart();
1750
+ if (!trimmed.toLowerCase().startsWith(COMMAND_PREFIX_LOWER))
1751
+ return null;
1752
+ const rest = trimmed.slice(COMMAND_PREFIX.length).trim();
1753
+ if (!rest) {
1754
+ return { subcommand: "", args: [] };
1755
+ }
1756
+ const tokens = rest.split(/\s+/).filter(Boolean);
1757
+ return { subcommand: tokens[0], args: tokens.slice(1) };
1758
+ }
1759
+
1760
+ class CommandRegistry {
1761
+ commands = new Map;
1762
+ register(def) {
1763
+ if (this.commands.has(def.name)) {
1764
+ throw new Error(`command "${def.name}" already registered`);
1765
+ }
1766
+ this.commands.set(def.name, def);
1767
+ }
1768
+ has(name) {
1769
+ return this.commands.has(name);
1770
+ }
1771
+ list() {
1772
+ return Array.from(this.commands.values());
1773
+ }
1774
+ async dispatch(subcommand, args, extra) {
1775
+ if (!subcommand) {
1776
+ return {
1777
+ ok: false,
1778
+ error: this.renderHelp()
1779
+ };
1780
+ }
1781
+ const def = this.commands.get(subcommand);
1782
+ if (!def) {
1783
+ return {
1784
+ ok: false,
1785
+ error: `未知命令「${subcommand}」。
1786
+ ${this.renderHelp()}`
1787
+ };
1788
+ }
1789
+ try {
1790
+ return await def.execute({
1791
+ args,
1792
+ rawText: extra?.rawText ?? "",
1793
+ configPath: extra?.configPath
1794
+ });
1795
+ } catch (err) {
1796
+ const msg = err instanceof Error ? err.message : String(err);
1797
+ logger2.error(`[CommandRegistry] command "${subcommand}" failed: ${msg}`);
1798
+ return { ok: false, error: `命令执行失败:${msg}` };
1799
+ }
1800
+ }
1801
+ renderHelp() {
1802
+ const lines = this.list().map((c) => ` ${c.usage.padEnd(40)} ${c.description}`);
1803
+ return `可用命令:
1804
+ ${lines.join(`
1805
+ `)}
1806
+ ` + `(通过「##command##: <命令> [参数]」调用)`;
1807
+ }
1808
+ }
1809
+ function getGlobalCommandRegistry() {
1810
+ const g = globalThis;
1811
+ if (!g.__royCommandRegistry) {
1812
+ const registry = new CommandRegistry;
1813
+ registry.register(fallbackModelCommand);
1814
+ registry.register(listCommand);
1815
+ g.__royCommandRegistry = registry;
1816
+ }
1817
+ return g.__royCommandRegistry;
1818
+ }
1819
+ async function handleCommandMessage(text, opts = {}) {
1820
+ if (!isCommandMessage(text))
1821
+ return null;
1822
+ const invocation = extractCommandInvocation(text);
1823
+ if (!invocation)
1824
+ return null;
1825
+ const registry = opts.registry ?? getGlobalCommandRegistry();
1826
+ logger2.info(`[handleCommandMessage] dispatching "${invocation.subcommand}" args=${JSON.stringify(invocation.args)}`);
1827
+ return registry.dispatch(invocation.subcommand, invocation.args, {
1828
+ rawText: text,
1829
+ configPath: opts.configPath
1830
+ });
1831
+ }
1832
+
1833
+ // src/env/event-source/command-card-action.ts
1834
+ init_logger();
1835
+ var logger3 = createLogger("command-card-action");
1836
+ function isCommandCardAction(event) {
1837
+ const av = event.actionValue;
1838
+ if (av && typeof av === "object") {
1839
+ const action = av.action;
1840
+ if (typeof action === "string" && action.startsWith(FALLBACK_MODEL_ACTION_NS)) {
1841
+ return true;
1842
+ }
1843
+ }
1844
+ if (typeof event.actionName === "string" && event.actionName.startsWith(FALLBACK_MODEL_ACTION_NS)) {
1845
+ return true;
1846
+ }
1847
+ return false;
1848
+ }
1849
+ function translateCardActionToCommand(event) {
1850
+ if (event.actionName === FALLBACK_MODEL_ACTIONS.add) {
1851
+ const model2 = String(event.formValue?.["fbmodel_model"] ?? event.formValue?.model ?? "").trim();
1852
+ if (!model2)
1853
+ return null;
1854
+ return { subcommand: "fallback-model", args: ["add", model2] };
1855
+ }
1856
+ const av = event.actionValue;
1857
+ if (!av || typeof av !== "object")
1858
+ return null;
1859
+ const action = av.action;
1860
+ const rawIndex = av.index;
1861
+ if (action !== FALLBACK_MODEL_ACTIONS.up && action !== FALLBACK_MODEL_ACTIONS.down) {
1862
+ return null;
1863
+ }
1864
+ const index = Number.parseInt(String(rawIndex ?? ""), 10);
1865
+ if (!Number.isFinite(index) || index < 1)
1866
+ return null;
1867
+ const target = action === FALLBACK_MODEL_ACTIONS.up ? index - 1 : index + 1;
1868
+ if (target < 1)
1869
+ return null;
1870
+ const model = av.model;
1871
+ if (typeof model === "string" && model.trim() !== "") {
1872
+ return {
1873
+ subcommand: "fallback-model",
1874
+ args: [
1875
+ "move",
1876
+ model.trim(),
1877
+ action === FALLBACK_MODEL_ACTIONS.up ? "up" : "down"
1878
+ ]
1879
+ };
1880
+ }
1881
+ return {
1882
+ subcommand: "fallback-model",
1883
+ args: ["reorder", String(index), String(target)]
1884
+ };
1885
+ }
1886
+ async function handleCommandCardAction(event, ports) {
1887
+ const call = translateCardActionToCommand(event);
1888
+ if (!call) {
1889
+ return { ok: false, error: "无法识别的 command 卡片动作" };
1890
+ }
1891
+ const executed = `${call.subcommand} ${call.args.join(" ")}`;
1892
+ logger3.info(`[handleCommandCardAction] 执行 ${executed}`);
1893
+ let result;
1894
+ try {
1895
+ result = await ports.executeCommand(call.subcommand, call.args, {
1896
+ configPath: ports.configPath
1897
+ });
1898
+ } catch (err) {
1899
+ const msg = err instanceof Error ? err.message : String(err);
1900
+ logger3.error(`[handleCommandCardAction] ${executed} 执行异常:${msg}`);
1901
+ return { ok: false, executed, error: `命令执行异常:${msg}` };
1902
+ }
1903
+ if (!result.ok) {
1904
+ return { ok: false, executed, error: result.error ?? "命令执行失败" };
1905
+ }
1906
+ const card = result.output?.card;
1907
+ let patchError;
1908
+ if (card && ports.patchCard && event.token) {
1909
+ try {
1910
+ await ports.patchCard(event.token, card, event.profile);
1911
+ } catch (err) {
1912
+ patchError = err.message;
1913
+ logger3.error(`[handleCommandCardAction] patch 卡片失败:${patchError}`);
1914
+ }
1915
+ }
1916
+ return { ok: true, executed, card, patchError };
1917
+ }
1918
+
1086
1919
  // src/env/event-source/event-source-handlers.ts
1087
1920
  init_logger();
1088
1921
  var execAsync2 = promisify2(execSync);
1089
- var logger2 = createLogger("lark-cli:event-source");
1922
+ var logger4 = createLogger("lark-cli:event-source");
1090
1923
  var CHILD_READY_TIMEOUT_MS = 5000;
1091
1924
  var GRACEFUL_STOP_TIMEOUT_MS = 5000;
1092
1925
  var CARD_ACTION_EVENT_TYPE = "card.action.trigger";
@@ -1315,8 +2148,8 @@ class LarkCliInstance {
1315
2148
  cardActionPorts;
1316
2149
  setCardActionPorts(ports) {
1317
2150
  this.cardActionPorts = ports;
1318
- if (typeof logger2 !== "undefined" && logger2?.debug) {
1319
- logger2.debug(`[LarkCliInstance] CardActionPorts injected: ` + `getTask=${typeof ports.getTask}, updateTaskContext=${typeof ports.updateTaskContext}, ` + `createGroupChat=${typeof ports.createGroupChat}, deleteGroupChat=${typeof ports.deleteGroupChat}, ` + `notifier=${typeof ports.notifier}`);
2151
+ if (typeof logger4 !== "undefined" && logger4?.debug) {
2152
+ logger4.debug(`[LarkCliInstance] CardActionPorts injected: ` + `getTask=${typeof ports.getTask}, updateTaskContext=${typeof ports.updateTaskContext}, ` + `createGroupChat=${typeof ports.createGroupChat}, deleteGroupChat=${typeof ports.deleteGroupChat}, ` + `notifier=${typeof ports.notifier}`);
1320
2153
  }
1321
2154
  }
1322
2155
  hasCardActionPorts() {
@@ -1343,7 +2176,7 @@ class LarkCliInstance {
1343
2176
  if (this.status === "running")
1344
2177
  return;
1345
2178
  if (this.starting) {
1346
- logger2.warn(`[LarkCliInstance] start() 已在执行中,忽略重复调用`);
2179
+ logger4.warn(`[LarkCliInstance] start() 已在执行中,忽略重复调用`);
1347
2180
  return;
1348
2181
  }
1349
2182
  this.starting = true;
@@ -1373,9 +2206,9 @@ class LarkCliInstance {
1373
2206
  if (!consumesCardAction)
1374
2207
  return;
1375
2208
  if (this.config.eventTypes?.length && !matchEventType(CARD_ACTION_EVENT_TYPE, this.config.eventTypes)) {
1376
- logger2.warn(`[LarkCliInstance] eventKeys 含 ${CARD_ACTION_EVENT_TYPE},但 eventTypes=` + `${JSON.stringify(this.config.eventTypes)} 不含它 → 卡片点击会被**静默丢弃**。` + `请把 ${CARD_ACTION_EVENT_TYPE} 加进 eventTypes,或清空 eventTypes。`);
2209
+ logger4.warn(`[LarkCliInstance] eventKeys 含 ${CARD_ACTION_EVENT_TYPE},但 eventTypes=` + `${JSON.stringify(this.config.eventTypes)} 不含它 → 卡片点击会被**静默丢弃**。` + `请把 ${CARD_ACTION_EVENT_TYPE} 加进 eventTypes,或清空 eventTypes。`);
1377
2210
  }
1378
- logger2.info(`[LarkCliInstance] 已监听 ${CARD_ACTION_EVENT_TYPE}。` + `⚠️ 若点击卡片无反应,先确认飞书开发者后台「事件与回调 → 回调配置」已开启该事件。`);
2211
+ logger4.info(`[LarkCliInstance] 已监听 ${CARD_ACTION_EVENT_TYPE}。` + `⚠️ 若点击卡片无反应,先确认飞书开发者后台「事件与回调 → 回调配置」已开启该事件。`);
1379
2212
  }
1380
2213
  spawnConsumeChild(command) {
1381
2214
  return spawn2("exec " + command, [], {
@@ -1415,7 +2248,7 @@ class LarkCliInstance {
1415
2248
  const conflicts = keys.filter((k) => busy.has(k));
1416
2249
  if (conflicts.length > 0) {
1417
2250
  const profile = this.config.profile;
1418
- logger2.warn(`[LarkCliInstance] eventKey 已被其它 consumer 占用(lark-cli 一个 key 只允许一个 consumer):` + `${conflicts.join(", ")}。本 event-source 启动后可能 exit 2。` + `用 \`lark-cli${profile ? ` --profile ${profile}` : ""} event status\` 查看,必要时 \`event stop\`。`);
2251
+ logger4.warn(`[LarkCliInstance] eventKey 已被其它 consumer 占用(lark-cli 一个 key 只允许一个 consumer):` + `${conflicts.join(", ")}。本 event-source 启动后可能 exit 2。` + `用 \`lark-cli${profile ? ` --profile ${profile}` : ""} event status\` 查看,必要时 \`event stop\`。`);
1419
2252
  }
1420
2253
  }
1421
2254
  async probeBusyEventKeys() {
@@ -1466,16 +2299,16 @@ class LarkCliInstance {
1466
2299
  });
1467
2300
  const handleExit = (code, source) => {
1468
2301
  if (code !== 0 && code !== null) {
1469
- logger2.warn(`[LarkCliInstance] consume 子进程异常退出 code=${code} (${source}) cmd="${command}"`);
2302
+ logger4.warn(`[LarkCliInstance] consume 子进程异常退出 code=${code} (${source}) cmd="${command}"`);
1470
2303
  this.status = "error";
1471
2304
  } else {
1472
- logger2.info(`[LarkCliInstance] consume 子进程已退出 code=${code} (${source}) cmd="${command}"`);
2305
+ logger4.info(`[LarkCliInstance] consume 子进程已退出 code=${code} (${source}) cmd="${command}"`);
1473
2306
  }
1474
2307
  settle();
1475
2308
  queueMicrotask(() => {
1476
2309
  if (this.children.length > 0 && this.children.every((c) => !isChildAlive(c))) {
1477
2310
  if (this.status !== "error") {
1478
- logger2.warn(`[LarkCliInstance] 所有 consume 子进程都已退出,状态从 running 改为 stopped`);
2311
+ logger4.warn(`[LarkCliInstance] 所有 consume 子进程都已退出,状态从 running 改为 stopped`);
1479
2312
  this.status = "stopped";
1480
2313
  }
1481
2314
  }
@@ -1484,12 +2317,12 @@ class LarkCliInstance {
1484
2317
  child.on("exit", (code) => handleExit(code, "exit"));
1485
2318
  child.on("close", (code) => handleExit(code, "close"));
1486
2319
  child.on("error", (error) => {
1487
- logger2.error(`[LarkCliInstance] consume 子进程 error: ${error.message}`);
2320
+ logger4.error(`[LarkCliInstance] consume 子进程 error: ${error.message}`);
1488
2321
  this.status = "error";
1489
2322
  settle();
1490
2323
  });
1491
2324
  const timer = setTimeout(() => {
1492
- logger2.warn(`[LarkCliInstance] consume 子进程 ${CHILD_READY_TIMEOUT_MS}ms 内未见 ready marker: cmd="${command}"`);
2325
+ logger4.warn(`[LarkCliInstance] consume 子进程 ${CHILD_READY_TIMEOUT_MS}ms 内未见 ready marker: cmd="${command}"`);
1493
2326
  settle();
1494
2327
  }, CHILD_READY_TIMEOUT_MS);
1495
2328
  ready.then(() => clearTimeout(timer));
@@ -1512,7 +2345,7 @@ class LarkCliInstance {
1512
2345
  try {
1513
2346
  child.kill("SIGTERM");
1514
2347
  } catch (error) {
1515
- logger2.warn(`[LarkCliInstance] Error sending SIGTERM to ${child.pid}: ${error}`);
2348
+ logger4.warn(`[LarkCliInstance] Error sending SIGTERM to ${child.pid}: ${error}`);
1516
2349
  }
1517
2350
  }
1518
2351
  const allExited = Promise.all(children.map((child) => new Promise((resolve) => {
@@ -1567,12 +2400,12 @@ class LarkCliInstance {
1567
2400
  }
1568
2401
  wireCardActionPorts() {
1569
2402
  if (this.cardActionPorts) {
1570
- logger2.debug("[LarkCliInstance] cardActionPorts 已由外部注入,跳过默认 cross-wire");
2403
+ logger4.debug("[LarkCliInstance] cardActionPorts 已由外部注入,跳过默认 cross-wire");
1571
2404
  return;
1572
2405
  }
1573
2406
  const taskComponent = this.environment?.getComponent?.("task");
1574
2407
  if (!taskComponent?.getTask || !taskComponent?.updateTask) {
1575
- logger2.debug(`[LarkCliInstance] card.action.trigger 端口未注入:taskComponent 不可用` + `(getTask=${typeof taskComponent?.getTask}, updateTask=${typeof taskComponent?.updateTask})。` + `卡片按钮点击会被跳过。`);
2408
+ logger4.debug(`[LarkCliInstance] card.action.trigger 端口未注入:taskComponent 不可用` + `(getTask=${typeof taskComponent?.getTask}, updateTask=${typeof taskComponent?.updateTask})。` + `卡片按钮点击会被跳过。`);
1576
2409
  return;
1577
2410
  }
1578
2411
  const sharedNotifier = this.pluginInstances.get("LarkCliTaskNotifyPlugin")?.cardNotifier ?? this.pluginInstances.get("PerTaskGroupChatPlugin")?.cardNotifier;
@@ -1601,7 +2434,7 @@ class LarkCliInstance {
1601
2434
  deleteGroupChat: (chatId, profile) => deleteGroupChatViaLarkCli(chatId, profile),
1602
2435
  notifier: sharedNotifier ?? new LarkCardPatchNotifier
1603
2436
  });
1604
- logger2.info(`[LarkCliInstance] card.action.trigger 端口已注入` + `(notifier ${sharedNotifier ? "复用插件共享实例" : "新建实例"})`);
2437
+ logger4.info(`[LarkCliInstance] card.action.trigger 端口已注入` + `(notifier ${sharedNotifier ? "复用插件共享实例" : "新建实例"})`);
1605
2438
  }
1606
2439
  getPluginHookPoints(plugin) {
1607
2440
  if (Array.isArray(plugin.hooks)) {
@@ -1799,7 +2632,7 @@ class LarkCliInstance {
1799
2632
  }
1800
2633
  return null;
1801
2634
  } catch (err) {
1802
- logger2.warn(`[LarkCliInstance] resolveChatTaskBinding failed for chatId=${chatId}: ${err}. ` + `群消息将不会注入 parentTaskId。`);
2635
+ logger4.warn(`[LarkCliInstance] resolveChatTaskBinding failed for chatId=${chatId}: ${err}. ` + `群消息将不会注入 parentTaskId。`);
1803
2636
  return null;
1804
2637
  }
1805
2638
  }
@@ -1821,7 +2654,7 @@ class LarkCliInstance {
1821
2654
  processStreamWith(data, buffer, opts = {}) {
1822
2655
  buffer.value += data;
1823
2656
  if (buffer.value.length > 2 * 1024 * 1024) {
1824
- logger2.warn(`[LarkCliInstance] stream 行缓冲超过 2MB 仍无换行,丢弃(疑似非 NDJSON 输出)`);
2657
+ logger4.warn(`[LarkCliInstance] stream 行缓冲超过 2MB 仍无换行,丢弃(疑似非 NDJSON 输出)`);
1825
2658
  buffer.value = "";
1826
2659
  return;
1827
2660
  }
@@ -1857,12 +2690,12 @@ class LarkCliInstance {
1857
2690
  const now = Date.now();
1858
2691
  const lastShared = LarkCliInstance.SHARED_SEEN.get(dedupKey);
1859
2692
  if (lastShared !== undefined && now - lastShared < LarkCliInstance.SHARED_MSG_TTL_MS) {
1860
- logger2.warn(`[LarkCliInstance] duplicate lark-cli event suppressed across instances (event_id=${dedupKey}, ` + `lastSeen=${now - lastShared}ms ago). ` + `Task #4137: SHARED_SEEN 跨实例去重。`);
2693
+ logger4.warn(`[LarkCliInstance] duplicate lark-cli event suppressed across instances (event_id=${dedupKey}, ` + `lastSeen=${now - lastShared}ms ago). ` + `Task #4137: SHARED_SEEN 跨实例去重。`);
1861
2694
  return;
1862
2695
  }
1863
2696
  const last = this.recentEventIds.get(dedupKey);
1864
2697
  if (last !== undefined && now - last < this.DEDUP_WINDOW_MS) {
1865
- logger2.warn(`[LarkCliInstance] duplicate lark-cli event suppressed (event_id=${dedupKey}, ` + `lastSeen=${now - last}ms ago). ` + `Task #3977/3978: 防止同一飞书事件触发两次任务创建。`);
2698
+ logger4.warn(`[LarkCliInstance] duplicate lark-cli event suppressed (event_id=${dedupKey}, ` + `lastSeen=${now - last}ms ago). ` + `Task #3977/3978: 防止同一飞书事件触发两次任务创建。`);
1866
2699
  return;
1867
2700
  }
1868
2701
  LarkCliInstance.SHARED_SEEN.set(dedupKey, now);
@@ -1924,6 +2757,7 @@ class LarkCliInstance {
1924
2757
  };
1925
2758
  const { metadata, replyChannel, recommendedAction } = extractMetadata(enrichedEvent, eventType);
1926
2759
  failureReplyChannel = replyChannel;
2760
+ const commandText = isCardAction ? undefined : extractCommandText(enrichedEvent);
1927
2761
  const parentTaskId = isCardAction ? null : await this.resolveChatTaskBinding(metadata?.chatId);
1928
2762
  const userId = this.extractUserId(rawEvent);
1929
2763
  let triggerEventId;
@@ -2021,6 +2855,10 @@ class LarkCliInstance {
2021
2855
  this.handleCardActionEvent(rawEvent, envReplyChannel?.profile);
2022
2856
  return;
2023
2857
  }
2858
+ if (commandText !== undefined) {
2859
+ this.handleCommandMessage(commandText, envReplyChannel?.profile, replyChannel?.messageId, replyChannel?.chatId);
2860
+ return;
2861
+ }
2024
2862
  this.eventHandler?.(evt);
2025
2863
  });
2026
2864
  } catch (error) {
@@ -2035,6 +2873,10 @@ class LarkCliInstance {
2035
2873
  async handleCardActionEvent(rawEvent, profile) {
2036
2874
  try {
2037
2875
  const event = rawEvent;
2876
+ if (this.isCommandCardActionEvent(event)) {
2877
+ await this.handleCommandCardActionEvent(event, profile);
2878
+ return;
2879
+ }
2038
2880
  const actionValueStr = event.action_value || "{}";
2039
2881
  const actionValue = JSON.parse(actionValueStr);
2040
2882
  const token = event.token || "";
@@ -2042,11 +2884,11 @@ class LarkCliInstance {
2042
2884
  const chatId = event.chat_id || "";
2043
2885
  const operatorId = event.operator_id?.open_id ?? event.operator_id ?? undefined;
2044
2886
  if (!token || !messageId || !chatId) {
2045
- logger2.warn(`[LarkCliInstance] handleCardActionEvent: missing token/messageId/chatId, skip`);
2887
+ logger4.warn(`[LarkCliInstance] handleCardActionEvent: missing token/messageId/chatId, skip`);
2046
2888
  return;
2047
2889
  }
2048
2890
  if (!this.cardActionPorts) {
2049
- logger2.warn(`[LarkCliInstance] handleCardActionEvent: cardActionPorts 未注入,` + `card.action.trigger 事件被跳过。` + `调 instance.setCardActionPorts({getTask, updateTaskContext, createGroupChat, deleteGroupChat, notifier}) 注入。`);
2891
+ logger4.warn(`[LarkCliInstance] handleCardActionEvent: cardActionPorts 未注入,` + `card.action.trigger 事件被跳过。` + `调 instance.setCardActionPorts({getTask, updateTaskContext, createGroupChat, deleteGroupChat, notifier}) 注入。`);
2050
2892
  return;
2051
2893
  }
2052
2894
  const result = await handleCardAction({
@@ -2061,12 +2903,179 @@ class LarkCliInstance {
2061
2903
  const patch = this.cardActionPorts.patchByToken ?? patchCardByLarkCli;
2062
2904
  await patch(token, result.cardUpdate, profile);
2063
2905
  } else {
2064
- logger2.warn(`[LarkCliInstance] handleCardActionEvent failed: ${result.error ?? "unknown"}`);
2906
+ logger4.warn(`[LarkCliInstance] handleCardActionEvent failed: ${result.error ?? "unknown"}`);
2065
2907
  }
2066
2908
  } catch (err) {
2067
- logger2.error(`[LarkCliInstance] handleCardActionEvent error: ${err.message}`);
2909
+ logger4.error(`[LarkCliInstance] handleCardActionEvent error: ${err.message}`);
2068
2910
  }
2069
2911
  }
2912
+ isCommandCardActionEvent(event) {
2913
+ let actionValue;
2914
+ const raw = event.action_value;
2915
+ if (typeof raw === "string") {
2916
+ try {
2917
+ const parsed = JSON.parse(raw);
2918
+ if (parsed && typeof parsed === "object") {
2919
+ actionValue = parsed;
2920
+ }
2921
+ } catch {
2922
+ actionValue = undefined;
2923
+ }
2924
+ } else if (raw && typeof raw === "object") {
2925
+ actionValue = raw;
2926
+ }
2927
+ let formValue;
2928
+ const rawForm = event.form_value;
2929
+ if (typeof rawForm === "string") {
2930
+ try {
2931
+ const parsed = JSON.parse(rawForm);
2932
+ if (parsed && typeof parsed === "object") {
2933
+ formValue = parsed;
2934
+ }
2935
+ } catch {
2936
+ formValue = undefined;
2937
+ }
2938
+ } else if (rawForm && typeof rawForm === "object") {
2939
+ formValue = rawForm;
2940
+ }
2941
+ return isCommandCardAction({
2942
+ actionValue,
2943
+ actionName: typeof event.action_name === "string" ? event.action_name : undefined
2944
+ });
2945
+ }
2946
+ async handleCommandCardActionEvent(event, profile) {
2947
+ let actionValue;
2948
+ try {
2949
+ actionValue = typeof event.action_value === "string" ? JSON.parse(event.action_value) : event.action_value;
2950
+ } catch {
2951
+ actionValue = undefined;
2952
+ }
2953
+ let formValue;
2954
+ try {
2955
+ formValue = typeof event.form_value === "string" ? JSON.parse(event.form_value) : event.form_value;
2956
+ } catch {
2957
+ formValue = undefined;
2958
+ }
2959
+ const result = await handleCommandCardAction({
2960
+ actionValue,
2961
+ formValue,
2962
+ actionName: typeof event.action_name === "string" ? event.action_name : undefined,
2963
+ chatId: typeof event.chat_id === "string" ? event.chat_id : undefined,
2964
+ messageId: typeof event.message_id === "string" ? event.message_id : undefined,
2965
+ token: typeof event.token === "string" ? event.token : undefined,
2966
+ profile
2967
+ }, {
2968
+ executeCommand: async (subcommand, args, extra) => {
2969
+ const registry = getGlobalCommandRegistry();
2970
+ return registry.dispatch(subcommand, args, {
2971
+ rawText: `##command##: ${subcommand} ${args.join(" ")}`,
2972
+ configPath: extra?.configPath ?? this.resolveCommandConfigPath()
2973
+ });
2974
+ },
2975
+ configPath: this.resolveCommandConfigPath(),
2976
+ patchCard: async (token, card, prof) => {
2977
+ await patchCardByLarkCli(token, card, prof);
2978
+ }
2979
+ });
2980
+ if (!result.ok) {
2981
+ const text = `⚠️ 命令卡片操作失败:${result.error ?? "未知错误"}`;
2982
+ const messageId = typeof event.message_id === "string" ? event.message_id : undefined;
2983
+ const chatId = typeof event.chat_id === "string" ? event.chat_id : undefined;
2984
+ try {
2985
+ await this.sendLarkText(text, profile, messageId, chatId);
2986
+ } catch {}
2987
+ return;
2988
+ }
2989
+ if (result.patchError) {
2990
+ const text = `⚠️ 操作已生效(${result.executed ?? "命令"}),但卡片刷新失败:` + `${result.patchError}
2991
+ ` + `(飞书卡片 token 有效期 30 分钟、最多刷新 2 次;可用 ` + `「##command##: list model」重新获取一张新卡片)`;
2992
+ const messageId = typeof event.message_id === "string" ? event.message_id : undefined;
2993
+ const chatId = typeof event.chat_id === "string" ? event.chat_id : undefined;
2994
+ try {
2995
+ await this.sendLarkText(text, profile, messageId, chatId);
2996
+ } catch {}
2997
+ }
2998
+ }
2999
+ async handleCommandMessage(text, profile, messageId, chatId) {
3000
+ try {
3001
+ const configPath = this.resolveCommandConfigPath();
3002
+ const result = await handleCommandMessage(text, { configPath });
3003
+ if (!result) {
3004
+ const card = renderTextCard("⚠️ 命令无法识别", "这条消息没有被识别为有效命令。请用「##command##: <命令> [参数]」格式。");
3005
+ await this.sendLarkCard(card, profile, messageId, chatId);
3006
+ return;
3007
+ }
3008
+ if (!result.ok) {
3009
+ const card = renderTextCard("⚠️ 命令执行失败", result.error ?? "未知错误");
3010
+ await this.sendLarkCard(card, profile, messageId, chatId);
3011
+ return;
3012
+ }
3013
+ const output = result.output;
3014
+ if (!output) {
3015
+ const card = renderTextCard("⚠️ 命令无输出", "命令执行成功但没有产生任何输出。");
3016
+ await this.sendLarkCard(card, profile, messageId, chatId);
3017
+ return;
3018
+ }
3019
+ if (output.card) {
3020
+ await this.sendLarkCard(output.card, profile, messageId, chatId);
3021
+ return;
3022
+ }
3023
+ await this.sendLarkText(output.text, profile, messageId, chatId);
3024
+ } catch (err) {
3025
+ logger4.error(`[LarkCliInstance] handleCommandMessage error: ${err.message}`);
3026
+ try {
3027
+ const card = renderTextCard("⚠️ 命令执行异常", `命令处理时发生异常:${err.message}`);
3028
+ await this.sendLarkCard(card, profile, messageId, chatId);
3029
+ } catch {}
3030
+ }
3031
+ }
3032
+ resolveCommandConfigPath() {
3033
+ const configured = this.config?.options?.configPath;
3034
+ return typeof configured === "string" ? configured : undefined;
3035
+ }
3036
+ async sendLarkCard(card, profile, messageId, chatId) {
3037
+ const content = JSON.stringify(card);
3038
+ await this.spawnLarkCli(["im"], profile, messageId, chatId, [
3039
+ "--msg-type",
3040
+ "interactive",
3041
+ "--content",
3042
+ content
3043
+ ]);
3044
+ }
3045
+ async sendLarkText(text, profile, messageId, chatId) {
3046
+ await this.spawnLarkCli(["im"], profile, messageId, chatId, ["--markdown", text]);
3047
+ }
3048
+ async spawnLarkCli(baseArgs, profile, messageId, chatId, contentArgs) {
3049
+ const args = [...baseArgs];
3050
+ if (messageId) {
3051
+ args.push("+messages-reply", "--message-id", messageId, "--as", "bot");
3052
+ } else if (chatId) {
3053
+ args.push("+messages-send", "--chat-id", chatId, "--as", "bot");
3054
+ } else {
3055
+ throw new Error("No target specified for sending message");
3056
+ }
3057
+ args.push(...contentArgs);
3058
+ if (profile) {
3059
+ args.unshift("--profile", profile);
3060
+ }
3061
+ await new Promise((resolve, reject) => {
3062
+ const proc = spawn2("lark-cli", args, {
3063
+ shell: false,
3064
+ stdio: ["pipe", "pipe", "pipe"]
3065
+ });
3066
+ let stderr = "";
3067
+ proc.stderr?.on("data", (d) => {
3068
+ stderr += d.toString();
3069
+ });
3070
+ proc.on("close", (code) => {
3071
+ if (code === 0)
3072
+ resolve();
3073
+ else
3074
+ reject(new Error(`lark-cli exited with code ${code}: ${stderr}`));
3075
+ });
3076
+ proc.on("error", reject);
3077
+ });
3078
+ }
2070
3079
  async patchCardByToken(token, card, profile) {
2071
3080
  return patchCardByLarkCli(token, card, profile);
2072
3081
  }
@@ -2238,6 +3247,34 @@ function renderRuleNoticeSection(ruleNotice, headline) {
2238
3247
  return `❌ **回执未送达**(原因:${ruleNotice.error ?? "unknown"})
2239
3248
  ` + `→ 用户目前**还不知道**「${headline}」这件事,必须由你在回复里讲清楚,` + `并且不要假装系统已经通知过。`;
2240
3249
  }
3250
+ function extractCommandText(rawEvent) {
3251
+ const event = rawEvent;
3252
+ const innerEvent = event.event;
3253
+ const message = innerEvent?.message || event.message;
3254
+ const rawContent = message?.content ?? event.content;
3255
+ if (rawContent === undefined || rawContent === null)
3256
+ return;
3257
+ let contentText;
3258
+ if (typeof rawContent === "string") {
3259
+ try {
3260
+ const parsed = JSON.parse(rawContent);
3261
+ if (typeof parsed === "string") {
3262
+ contentText = parsed;
3263
+ } else if (parsed && typeof parsed === "object" && "text" in parsed) {
3264
+ const t = parsed.text;
3265
+ contentText = typeof t === "string" ? t : undefined;
3266
+ }
3267
+ } catch {
3268
+ contentText = rawContent;
3269
+ }
3270
+ } else if (typeof rawContent === "object" && "text" in rawContent) {
3271
+ const t = rawContent.text;
3272
+ contentText = typeof t === "string" ? t : undefined;
3273
+ }
3274
+ if (!contentText)
3275
+ return;
3276
+ return isCommandMessage(contentText) ? contentText : undefined;
3277
+ }
2241
3278
  var larkCliHandler = {
2242
3279
  type: "lark-cli",
2243
3280
  validateConfig(config) {
@@ -2249,7 +3286,7 @@ var larkCliHandler = {
2249
3286
  },
2250
3287
  async emitTriggerEvent(payload, store) {
2251
3288
  const eventId = await store.insertTriggerEvent(payload, "lark-cli");
2252
- logger2.info(`[larkCliHandler] emitTriggerEvent: id=${eventId}`, {
3289
+ logger4.info(`[larkCliHandler] emitTriggerEvent: id=${eventId}`, {
2253
3290
  chatId: payload.metadata?.chatId,
2254
3291
  senderId: payload.senderId
2255
3292
  });
@@ -2264,7 +3301,7 @@ var larkCliHandler = {
2264
3301
  try {
2265
3302
  const result = await runner.run(command);
2266
3303
  if (result.code !== 0) {
2267
- logger2.error(`[larkCliHandler] replyMessage non-zero exit`, {
3304
+ logger4.error(`[larkCliHandler] replyMessage non-zero exit`, {
2268
3305
  code: result.code,
2269
3306
  stderr: result.stderr
2270
3307
  });
@@ -2278,7 +3315,7 @@ var larkCliHandler = {
2278
3315
  return { success: true, messageId: replyMessageId };
2279
3316
  } catch (err) {
2280
3317
  const errMsg = err instanceof Error ? err.message : String(err);
2281
- logger2.error(`[larkCliHandler] replyMessage spawn error`, { error: errMsg });
3318
+ logger4.error(`[larkCliHandler] replyMessage spawn error`, { error: errMsg });
2282
3319
  return { success: false, error: errMsg };
2283
3320
  }
2284
3321
  },
@@ -2385,9 +3422,11 @@ async function patchCardByLarkCli(token, card, profile) {
2385
3422
  const cmd = `lark-cli ${profileFlag}api POST /open-apis/interactive/v1/card/update ` + `--data '${JSON.stringify({ token, card }).replace(/'/g, "'\\''")}' --as bot`;
2386
3423
  try {
2387
3424
  const { stdout } = await execAsync2(cmd, { timeout: 30000 });
2388
- logger2.debug(`patchCardByLarkCli ok: ${stdout?.slice(0, 200)}`);
3425
+ logger4.debug(`patchCardByLarkCli ok: ${stdout?.slice(0, 200)}`);
2389
3426
  } catch (err) {
2390
- logger2.error(`patchCardByLarkCli failed: ${err.message}`);
3427
+ const msg = err.message;
3428
+ logger4.error(`patchCardByLarkCli failed: ${msg}`);
3429
+ throw new Error(`卡片刷新失败:${msg}`);
2391
3430
  }
2392
3431
  }
2393
3432
  var builtInHandlers = [
@@ -2401,4 +3440,4 @@ function getBuiltInHandler(type) {
2401
3440
  return builtInHandlers.find((h) => h.type === type);
2402
3441
  }
2403
3442
 
2404
- export { TimerInstance, timerHandler, buildDefaultBountyIMSystemPrompt, resolveBountyIMSystemPrompt, BountyIMInstance, bountyIMHandler, bountyCliHandler, buildLarkRecommendedAction, extractMetadata, LarkCliInstance, renderRuleNoticeSection, larkCliHandler, patchCardByLarkCli, builtInHandlers, getBuiltInHandler };
3443
+ export { TimerInstance, timerHandler, buildDefaultBountyIMSystemPrompt, resolveBountyIMSystemPrompt, BountyIMInstance, bountyIMHandler, bountyCliHandler, FALLBACK_MODEL_ACTION_NS, FALLBACK_MODEL_ACTIONS, isFallbackModelCard, renderTextCard, renderFallbackModelCard, DEFAULT_CONFIG_PATH, readFallbackModelList, readRawFallbackModelArray, writeFallbackModelList, fallbackModelCommand, listCommand, COMMAND_PREFIX, isCommandMessage, extractCommandInvocation, CommandRegistry, getGlobalCommandRegistry, handleCommandMessage, isCommandCardAction, translateCardActionToCommand, handleCommandCardAction, buildLarkRecommendedAction, extractMetadata, LarkCliInstance, renderRuleNoticeSection, extractCommandText, larkCliHandler, patchCardByLarkCli, builtInHandlers, getBuiltInHandler };