@ai-setting/roy-agent-cli 1.0.0

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 (158) hide show
  1. package/README.md +126 -0
  2. package/dist/bin/roy.js +127297 -0
  3. package/dist/roy-agent-darwin-arm64/bin/roy.js +127297 -0
  4. package/dist/roy-agent-darwin-x64/bin/roy.js +127297 -0
  5. package/dist/roy-agent-linux-arm64/bin/roy.js +127297 -0
  6. package/dist/roy-agent-linux-x64/bin/roy.js +127297 -0
  7. package/dist/roy-agent-windows-x64/bin/roy.js +127297 -0
  8. package/package.json +91 -0
  9. package/src/bin/roy.ts +12 -0
  10. package/src/cli.ts +101 -0
  11. package/src/commands/act.ts +480 -0
  12. package/src/commands/commands-add.ts +110 -0
  13. package/src/commands/commands-dirs.ts +70 -0
  14. package/src/commands/commands-info.ts +90 -0
  15. package/src/commands/commands-list.ts +161 -0
  16. package/src/commands/commands-remove.ts +147 -0
  17. package/src/commands/commands.ts +55 -0
  18. package/src/commands/config/config-service.test.ts +449 -0
  19. package/src/commands/config/config-service.ts +312 -0
  20. package/src/commands/config/deep-merge.test.ts +168 -0
  21. package/src/commands/config/deep-merge.ts +63 -0
  22. package/src/commands/config/export.ts +97 -0
  23. package/src/commands/config/filter-history-e2e.test.ts +141 -0
  24. package/src/commands/config/import-preserve-refs.test.ts +212 -0
  25. package/src/commands/config/import.ts +119 -0
  26. package/src/commands/config/index.ts +35 -0
  27. package/src/commands/config/list.ts +281 -0
  28. package/src/commands/config/roy-config-e2e.test.ts +297 -0
  29. package/src/commands/config/types.ts +54 -0
  30. package/src/commands/debug/index.ts +38 -0
  31. package/src/commands/debug/log.test.ts +233 -0
  32. package/src/commands/debug/log.ts +123 -0
  33. package/src/commands/debug/span.test.ts +297 -0
  34. package/src/commands/debug/span.ts +211 -0
  35. package/src/commands/debug/trace.test.ts +254 -0
  36. package/src/commands/debug/trace.ts +140 -0
  37. package/src/commands/eventsource/add.ts +133 -0
  38. package/src/commands/eventsource/index.ts +48 -0
  39. package/src/commands/eventsource/list.ts +194 -0
  40. package/src/commands/eventsource/remove.ts +95 -0
  41. package/src/commands/eventsource/start.ts +103 -0
  42. package/src/commands/eventsource/status.ts +185 -0
  43. package/src/commands/eventsource/stop.ts +89 -0
  44. package/src/commands/index.ts +22 -0
  45. package/src/commands/input-handler.test.ts +76 -0
  46. package/src/commands/input-handler.ts +43 -0
  47. package/src/commands/interactive-esc.test.ts +254 -0
  48. package/src/commands/interactive.shutdown.test.ts +122 -0
  49. package/src/commands/interactive.test.ts +221 -0
  50. package/src/commands/interactive.ts +1015 -0
  51. package/src/commands/lsp/check.ts +92 -0
  52. package/src/commands/lsp/index.ts +32 -0
  53. package/src/commands/lsp/install.ts +126 -0
  54. package/src/commands/lsp/list.ts +64 -0
  55. package/src/commands/mcp/index.ts +27 -0
  56. package/src/commands/mcp/list.ts +116 -0
  57. package/src/commands/mcp/reload.ts +70 -0
  58. package/src/commands/mcp/tools.ts +121 -0
  59. package/src/commands/memory/extract-e2e.test.ts +388 -0
  60. package/src/commands/memory/index.ts +11 -0
  61. package/src/commands/memory/memory-simplified.test.ts +58 -0
  62. package/src/commands/memory/memory.ts +25 -0
  63. package/src/commands/memory/organize.ts +300 -0
  64. package/src/commands/memory/recall.test.ts +120 -0
  65. package/src/commands/memory/recall.ts +88 -0
  66. package/src/commands/memory/record-extract-handle-query.test.ts +385 -0
  67. package/src/commands/memory/record-prompt-component.test.ts +343 -0
  68. package/src/commands/memory/record.test.ts +92 -0
  69. package/src/commands/memory/record.ts +332 -0
  70. package/src/commands/plugin.test.ts +292 -0
  71. package/src/commands/plugin.ts +267 -0
  72. package/src/commands/sessions/active.ts +96 -0
  73. package/src/commands/sessions/add-message.ts +96 -0
  74. package/src/commands/sessions/checkpoints.ts +154 -0
  75. package/src/commands/sessions/compact.test.ts +215 -0
  76. package/src/commands/sessions/compact.ts +269 -0
  77. package/src/commands/sessions/delete.ts +236 -0
  78. package/src/commands/sessions/get.ts +165 -0
  79. package/src/commands/sessions/grep.ts +233 -0
  80. package/src/commands/sessions/index.ts +95 -0
  81. package/src/commands/sessions/list.ts +210 -0
  82. package/src/commands/sessions/messages.test.ts +333 -0
  83. package/src/commands/sessions/messages.ts +248 -0
  84. package/src/commands/sessions/mock.ts +194 -0
  85. package/src/commands/sessions/new.ts +82 -0
  86. package/src/commands/sessions/rename.ts +98 -0
  87. package/src/commands/shared/event-handler.ts +213 -0
  88. package/src/commands/shared/event-message-formatter.ts +295 -0
  89. package/src/commands/shared/index.ts +11 -0
  90. package/src/commands/shared/query-executor.test.ts +434 -0
  91. package/src/commands/shared/query-executor.ts +324 -0
  92. package/src/commands/shared/repl-engine.test.ts +354 -0
  93. package/src/commands/shared/session-manager.test.ts +212 -0
  94. package/src/commands/shared/session-manager.ts +114 -0
  95. package/src/commands/skills/get.ts +90 -0
  96. package/src/commands/skills/index.ts +39 -0
  97. package/src/commands/skills/list.ts +129 -0
  98. package/src/commands/skills/reload.ts +59 -0
  99. package/src/commands/skills/search.ts +132 -0
  100. package/src/commands/skills/show-config.ts +93 -0
  101. package/src/commands/tasks/complete.ts +92 -0
  102. package/src/commands/tasks/create.ts +118 -0
  103. package/src/commands/tasks/delete.ts +86 -0
  104. package/src/commands/tasks/get.ts +116 -0
  105. package/src/commands/tasks/index.ts +53 -0
  106. package/src/commands/tasks/list.ts +140 -0
  107. package/src/commands/tasks/operations.ts +120 -0
  108. package/src/commands/tasks/update.ts +122 -0
  109. package/src/commands/tools/exec-tool.ts +128 -0
  110. package/src/commands/tools/get.ts +114 -0
  111. package/src/commands/tools/index.ts +35 -0
  112. package/src/commands/tools/list.ts +107 -0
  113. package/src/commands/tools/shared/index.ts +7 -0
  114. package/src/commands/tools/shared/schema-helper.ts +111 -0
  115. package/src/commands/workflow/commands/add.ts +315 -0
  116. package/src/commands/workflow/commands/get.ts +193 -0
  117. package/src/commands/workflow/commands/list.ts +137 -0
  118. package/src/commands/workflow/commands/nodes.ts +528 -0
  119. package/src/commands/workflow/commands/remove.ts +94 -0
  120. package/src/commands/workflow/commands/run.ts +398 -0
  121. package/src/commands/workflow/commands/status.ts +147 -0
  122. package/src/commands/workflow/commands/stop.ts +91 -0
  123. package/src/commands/workflow/commands/update.ts +130 -0
  124. package/src/commands/workflow/commands/validate.ts +139 -0
  125. package/src/commands/workflow/commands/workflow-cli.test.ts +196 -0
  126. package/src/commands/workflow/index.ts +65 -0
  127. package/src/commands/workflow/renderers.ts +358 -0
  128. package/src/commands/workflow/validators/index.ts +8 -0
  129. package/src/commands/workflow/validators/node-validator-factory.ts +40 -0
  130. package/src/commands/workflow/validators/node-validator.ts +125 -0
  131. package/src/commands/workflow/validators/nodes/agent-node-validator.ts +58 -0
  132. package/src/commands/workflow/validators/nodes/condition-node-validator.ts +34 -0
  133. package/src/commands/workflow/validators/nodes/decorator-node-validator.ts +45 -0
  134. package/src/commands/workflow/validators/nodes/merge-node-validator.ts +46 -0
  135. package/src/commands/workflow/validators/nodes/skill-node-validator.ts +33 -0
  136. package/src/commands/workflow/validators/nodes/tool-node-validator.ts +54 -0
  137. package/src/commands/workflow/validators/nodes/workflow-node-validator.ts +33 -0
  138. package/src/commands/workflow/validators/types.ts +78 -0
  139. package/src/commands/workflow/validators/workflow-validator.test.ts +273 -0
  140. package/src/commands/workflow/validators/workflow-validator.ts +320 -0
  141. package/src/index.ts +19 -0
  142. package/src/plugin/apply.ts +103 -0
  143. package/src/plugin/discover.ts +219 -0
  144. package/src/plugin/index.ts +45 -0
  145. package/src/plugin/registry.ts +272 -0
  146. package/src/plugin/types.ts +165 -0
  147. package/src/services/context-handler.service.test.ts +501 -0
  148. package/src/services/context-handler.service.ts +372 -0
  149. package/src/services/environment.service.commands-prompt.test.ts +167 -0
  150. package/src/services/environment.service.ts +656 -0
  151. package/src/services/output.service.test.ts +92 -0
  152. package/src/services/output.service.ts +122 -0
  153. package/src/services/quiet-mode.service.test.ts +114 -0
  154. package/src/services/quiet-mode.service.ts +81 -0
  155. package/src/services/stream-output.service.test.ts +214 -0
  156. package/src/services/stream-output.service.ts +323 -0
  157. package/src/util/which.test.ts +101 -0
  158. package/src/util/which.ts +55 -0
@@ -0,0 +1,213 @@
1
+ /**
2
+ * @fileoverview Event Handler
3
+ *
4
+ * 管理环境事件的订阅、队列和处理
5
+ */
6
+
7
+ import type { BaseEnvironment, EnvEvent } from "@ai-setting/roy-agent-core";
8
+ import { EventMessageFormatter } from "./event-message-formatter";
9
+
10
+ /**
11
+ * EventHandler 配置选项
12
+ */
13
+ export interface EventHandlerOptions {
14
+ /** 环境实例 */
15
+ env: BaseEnvironment;
16
+ /** 监听的事件类型(支持通配符,如 'task.*') */
17
+ eventTypes: string[];
18
+ /** 事件消息触发回调 */
19
+ onEvent: (message: string) => Promise<void>;
20
+ /** 消息格式化器 */
21
+ formatter?: EventMessageFormatter;
22
+ /** 检查 agent 是否空闲 */
23
+ isIdle?: () => boolean;
24
+ /** 空闲检查间隔(毫秒) */
25
+ idleCheckInterval?: number;
26
+ }
27
+
28
+ /**
29
+ * 事件处理器
30
+ *
31
+ * 订阅环境事件,格式化后通过队列管理,agent 空闲时触发处理
32
+ */
33
+ export class EventHandler {
34
+ private env: BaseEnvironment;
35
+ private eventTypes: string[];
36
+ private onEvent: (message: string) => Promise<void>;
37
+ private formatter: EventMessageFormatter;
38
+ private isIdle: () => boolean;
39
+ private idleCheckInterval: number;
40
+
41
+ private queue: string[] = [];
42
+ private isProcessing = false;
43
+ private isStopped = false;
44
+ private unsubscribe: (() => void) | null = null;
45
+
46
+ constructor(options: EventHandlerOptions) {
47
+ this.env = options.env;
48
+ this.eventTypes = options.eventTypes;
49
+ this.onEvent = options.onEvent;
50
+ this.formatter = options.formatter ?? new EventMessageFormatter();
51
+ this.isIdle = options.isIdle ?? (() => true); // 默认认为总是空闲
52
+ this.idleCheckInterval = options.idleCheckInterval ?? 100;
53
+ }
54
+
55
+ /**
56
+ * 启动事件处理
57
+ * @returns 取消订阅函数
58
+ */
59
+ start(): () => void {
60
+ this.isStopped = false;
61
+
62
+ // 使用 subscribeAll 接收所有事件,然后在 handler 中过滤类型
63
+ // 因为 subscribeTo 不支持通配符模式
64
+ this.unsubscribe = this.env.subscribeAll((event: EnvEvent) => {
65
+ // 检查事件类型是否匹配
66
+ if (this.matchesEventType(event.type)) {
67
+ this.handleEvent(event);
68
+ }
69
+ });
70
+
71
+ return () => this.stop();
72
+ }
73
+
74
+ /**
75
+ * 检查事件类型是否匹配
76
+ */
77
+ private matchesEventType(eventType: string): boolean {
78
+ for (const pattern of this.eventTypes) {
79
+ if (this.matchPattern(eventType, pattern)) {
80
+ return true;
81
+ }
82
+ }
83
+ return false;
84
+ }
85
+
86
+ /**
87
+ * 通配符模式匹配
88
+ * 支持:
89
+ * - "task.*" 匹配 "task.started", "task.completed" 等
90
+ * - "task.started" 精确匹配
91
+ */
92
+ private matchPattern(eventType: string, pattern: string): boolean {
93
+ if (pattern === "*") return true;
94
+
95
+ if (pattern.endsWith(".*")) {
96
+ const prefix = pattern.slice(0, -2);
97
+ return eventType === prefix || eventType.startsWith(prefix + ".");
98
+ }
99
+
100
+ return eventType === pattern;
101
+ }
102
+
103
+ /**
104
+ * 停止事件处理
105
+ */
106
+ stop(): void {
107
+ this.isStopped = true;
108
+
109
+ if (this.unsubscribe) {
110
+ this.unsubscribe();
111
+ this.unsubscribe = null;
112
+ }
113
+
114
+ // 清空队列
115
+ this.queue = [];
116
+ this.isProcessing = false;
117
+ }
118
+
119
+ /**
120
+ * 处理收到的事件
121
+ */
122
+ private handleEvent(event: EnvEvent): void {
123
+ // 如果已停止,忽略事件
124
+ if (this.isStopped) {
125
+ return;
126
+ }
127
+
128
+ // 格式化事件为用户消息
129
+ const message = this.formatter.format(event);
130
+
131
+ // 加入队列
132
+ this.enqueue(message);
133
+ }
134
+
135
+ /**
136
+ * 将消息加入队列
137
+ */
138
+ enqueue(message: string): void {
139
+ // 如果已停止,忽略
140
+ if (this.isStopped) {
141
+ return;
142
+ }
143
+
144
+ this.queue.push(message);
145
+
146
+ // 如果没有在处理,触发处理
147
+ if (!this.isProcessing) {
148
+ this.processQueue();
149
+ }
150
+ }
151
+
152
+ /**
153
+ * 获取队列长度
154
+ */
155
+ getQueueLength(): number {
156
+ return this.queue.length;
157
+ }
158
+
159
+ /**
160
+ * 检查队列是否为空
161
+ */
162
+ isQueueEmpty(): boolean {
163
+ return this.queue.length === 0;
164
+ }
165
+
166
+ /**
167
+ * 处理队列中的消息(使用 while 循环避免递归栈溢出)
168
+ */
169
+ private async processQueue(): Promise<void> {
170
+ this.isProcessing = true;
171
+
172
+ while (!this.isStopped && this.queue.length > 0) {
173
+ // 等待 agent 空闲
174
+ await this.waitForIdle();
175
+
176
+ // 如果已停止,退出循环
177
+ if (this.isStopped) {
178
+ break;
179
+ }
180
+
181
+ // 取出队首消息
182
+ const message = this.queue.shift();
183
+ if (!message) {
184
+ break;
185
+ }
186
+
187
+ try {
188
+ // 触发事件处理
189
+ await this.onEvent(message);
190
+ } catch (error) {
191
+ console.error(`[EventHandler] 处理事件失败:`, error);
192
+ }
193
+ }
194
+
195
+ this.isProcessing = false;
196
+ }
197
+
198
+ /**
199
+ * 等待 agent 空闲
200
+ */
201
+ private async waitForIdle(): Promise<void> {
202
+ while (!this.isStopped && !this.isIdle()) {
203
+ await this.sleep(this.idleCheckInterval);
204
+ }
205
+ }
206
+
207
+ /**
208
+ * 睡眠工具函数
209
+ */
210
+ private sleep(ms: number): Promise<void> {
211
+ return new Promise(resolve => setTimeout(resolve, ms));
212
+ }
213
+ }
@@ -0,0 +1,295 @@
1
+ /**
2
+ * @fileoverview Event Message Formatter
3
+ *
4
+ * 将 EnvEvent 格式化为用户可读的消息
5
+ */
6
+
7
+ import type { EnvEvent } from "@ai-setting/roy-agent-core";
8
+
9
+ // ============================================================================
10
+ // Event Type Mapping
11
+ // ============================================================================
12
+
13
+ /**
14
+ * 任务事件类型
15
+ */
16
+ export const TaskEventTypes = {
17
+ TASK_STARTED: "task.started",
18
+ TASK_PROGRESS: "task.progress",
19
+ TASK_COMPLETED: "task.completed",
20
+ TASK_FAILED: "task.failed",
21
+ TASK_TIMEOUT: "task.timeout",
22
+ TASK_STOPPED: "task.stopped",
23
+ } as const;
24
+
25
+ /**
26
+ * EventSource 事件类型
27
+ */
28
+ export const EventSourceEventTypes = {
29
+ EVENT_SOURCE_STARTED: "event-source.started",
30
+ EVENT_SOURCE_STOPPED: "event-source.stopped",
31
+ EVENT_SOURCE_EVENT_PREFIX: "event-source.event.",
32
+ } as const;
33
+
34
+ /**
35
+ * 事件元信息接口
36
+ */
37
+ export interface EventMetadata {
38
+ eventType?: string;
39
+ appId?: string;
40
+ chatId?: string;
41
+ chatType?: string;
42
+ messageId?: string;
43
+ messageType?: string;
44
+ senderId?: string;
45
+ tenantKey?: string;
46
+ [key: string]: unknown;
47
+ }
48
+
49
+ /**
50
+ * 推荐动作接口
51
+ */
52
+ export interface RecommendedAction {
53
+ action: string;
54
+ replyTo?: {
55
+ appId?: string;
56
+ chatId?: string;
57
+ messageId?: string;
58
+ };
59
+ rawCommand?: string;
60
+ }
61
+
62
+ /**
63
+ * EventSource 事件负载接口
64
+ */
65
+ export interface EventSourceEventPayload {
66
+ sourceId: string;
67
+ sourceType: string;
68
+ rawEvent: unknown;
69
+ message: string;
70
+ metadata: EventMetadata;
71
+ recommendedAction?: RecommendedAction;
72
+ timestamp: number;
73
+ }
74
+
75
+ /**
76
+ * 任务事件负载接口
77
+ */
78
+ export interface TaskEventPayload {
79
+ taskId: string;
80
+ subSessionId: string;
81
+ parentSessionId: string;
82
+ description: string;
83
+ subagentType: string;
84
+ status?: string;
85
+ progress?: number;
86
+ progressMessage?: string;
87
+ result?: string;
88
+ error?: string;
89
+ executionTimeMs?: number;
90
+ associatedTaskId?: number;
91
+ }
92
+
93
+ /**
94
+ * 格式化选项
95
+ */
96
+ export interface EventMessageFormatterOptions {
97
+ /** 消息前缀 */
98
+ prefix?: string;
99
+ /** 是否显示详细时间 */
100
+ showTimestamp?: boolean;
101
+ }
102
+
103
+ // ============================================================================
104
+ // EventMessageFormatter
105
+ // ============================================================================
106
+
107
+ /**
108
+ * 事件消息格式化器
109
+ *
110
+ * 将 EnvEvent 转换为用户可读的文本消息
111
+ */
112
+ export class EventMessageFormatter {
113
+ private prefix: string;
114
+ private showTimestamp: boolean;
115
+
116
+ constructor(options: EventMessageFormatterOptions = {}) {
117
+ this.prefix = options.prefix ?? "[通知]";
118
+ this.showTimestamp = options.showTimestamp ?? false;
119
+ }
120
+
121
+ /**
122
+ * 格式化事件为用户消息
123
+ */
124
+ format(event: EnvEvent): string {
125
+ const { type, payload } = event;
126
+
127
+ // 根据事件类型格式化
128
+ switch (type) {
129
+ case TaskEventTypes.TASK_STARTED:
130
+ return this.formatTaskStarted(payload as TaskEventPayload);
131
+ case TaskEventTypes.TASK_PROGRESS:
132
+ return this.formatTaskProgress(payload as TaskEventPayload);
133
+ case TaskEventTypes.TASK_COMPLETED:
134
+ return this.formatTaskCompleted(payload as TaskEventPayload);
135
+ case TaskEventTypes.TASK_FAILED:
136
+ return this.formatTaskFailed(payload as TaskEventPayload);
137
+ case TaskEventTypes.TASK_TIMEOUT:
138
+ return this.formatTaskTimeout(payload as TaskEventPayload);
139
+ case TaskEventTypes.TASK_STOPPED:
140
+ return this.formatTaskStopped(payload as TaskEventPayload);
141
+ case EventSourceEventTypes.EVENT_SOURCE_STARTED:
142
+ return this.formatEventSourceStarted(payload as EventSourceEventPayload);
143
+ case EventSourceEventTypes.EVENT_SOURCE_STOPPED:
144
+ return this.formatEventSourceStopped(payload as EventSourceEventPayload);
145
+ default:
146
+ // 处理 event-source.event.* 类型的事件
147
+ if (type.startsWith(EventSourceEventTypes.EVENT_SOURCE_EVENT_PREFIX)) {
148
+ return this.formatEventSourceEvent(event);
149
+ }
150
+ return this.formatGeneric(event);
151
+ }
152
+ }
153
+
154
+ /**
155
+ * 格式化任务开始事件
156
+ */
157
+ private formatTaskStarted(payload: TaskEventPayload): string {
158
+ const desc = payload.description || "未命名任务";
159
+ return `${this.prefix} 📢 后台任务「${desc}」已启动`;
160
+ }
161
+
162
+ /**
163
+ * 格式化任务进度事件
164
+ */
165
+ private formatTaskProgress(payload: TaskEventPayload): string {
166
+ const desc = payload.description || "未命名任务";
167
+ const progress = payload.progress !== undefined ? `${payload.progress}%` : "";
168
+ const message = payload.progressMessage || "";
169
+
170
+ let result = `${this.prefix} 🔄 后台任务「${desc}」进度更新`;
171
+ if (progress) result += `: ${progress}`;
172
+ if (message) result += ` - ${message}`;
173
+
174
+ return result;
175
+ }
176
+
177
+ /**
178
+ * 格式化任务完成事件
179
+ */
180
+ private formatTaskCompleted(payload: TaskEventPayload): string {
181
+ const desc = payload.description || "未命名任务";
182
+ const result = payload.result;
183
+ const time = payload.executionTimeMs ? `(${this.formatDuration(payload.executionTimeMs)})` : "";
184
+
185
+ let message = `${this.prefix} ✅ 后台任务「${desc}」已完成 ${time}`;
186
+ if (result) {
187
+ // 截断过长结果
188
+ const truncatedResult = result.length > 100
189
+ ? result.substring(0, 100) + "..."
190
+ : result;
191
+ message += `\n结果: ${truncatedResult}`;
192
+ }
193
+
194
+ return message;
195
+ }
196
+
197
+ /**
198
+ * 格式化任务失败事件
199
+ */
200
+ private formatTaskFailed(payload: TaskEventPayload): string {
201
+ const desc = payload.description || "未命名任务";
202
+ const error = payload.error || "未知错误";
203
+ const time = payload.executionTimeMs ? `(${this.formatDuration(payload.executionTimeMs)})` : "";
204
+
205
+ let message = `${this.prefix} ❌ 后台任务「${desc}」失败 ${time}`;
206
+ if (error) {
207
+ // 截断过长错误信息
208
+ const truncatedError = error.length > 100
209
+ ? error.substring(0, 100) + "..."
210
+ : error;
211
+ message += `\n错误: ${truncatedError}`;
212
+ }
213
+
214
+ return message;
215
+ }
216
+
217
+ /**
218
+ * 格式化任务超时事件
219
+ */
220
+ private formatTaskTimeout(payload: TaskEventPayload): string {
221
+ const desc = payload.description || "未命名任务";
222
+ const time = payload.executionTimeMs ? `(${this.formatDuration(payload.executionTimeMs)})` : "";
223
+
224
+ return `${this.prefix} ⏱️ 后台任务「${desc}」超时 ${time}`;
225
+ }
226
+
227
+ /**
228
+ * 格式化任务停止事件
229
+ */
230
+ private formatTaskStopped(payload: TaskEventPayload): string {
231
+ const desc = payload.description || "未命名任务";
232
+ const time = payload.executionTimeMs ? `(${this.formatDuration(payload.executionTimeMs)})` : "";
233
+
234
+ return `${this.prefix} 🛑 后台任务「${desc}」已停止 ${time}`;
235
+ }
236
+
237
+ /**
238
+ * 格式化通用事件
239
+ */
240
+ private formatGeneric(event: EnvEvent): string {
241
+ return `${this.prefix} 收到事件: ${event.type}`;
242
+ }
243
+
244
+ // ============================================================================
245
+ // EventSource 事件格式化
246
+ // ============================================================================
247
+
248
+ /**
249
+ * 格式化事件源启动事件
250
+ */
251
+ private formatEventSourceStarted(payload: EventSourceEventPayload): string {
252
+ return `${this.prefix} 📡 事件源「${payload.sourceId}」(类型: ${payload.sourceType}) 已启动`;
253
+ }
254
+
255
+ /**
256
+ * 格式化事件源停止事件
257
+ */
258
+ private formatEventSourceStopped(payload: EventSourceEventPayload): string {
259
+ return `${this.prefix} 📡 事件源「${payload.sourceId}」(类型: ${payload.sourceType}) 已停止`;
260
+ }
261
+
262
+ /**
263
+ * 格式化事件源收到的事件(如飞书消息)
264
+ *
265
+ * 泛化处理:直接序列化事件为可读文本,不依赖特定字段
266
+ */
267
+ private formatEventSourceEvent(event: EnvEvent): string {
268
+ const payload = event.payload as any;
269
+ const sourceType = payload?.sourceType || "unknown";
270
+ const message = payload?.message || "";
271
+
272
+ // 直接使用 EventSourceComponent 格式化好的 message
273
+ if (message) {
274
+ return `[${sourceType}] ${message}`;
275
+ }
276
+
277
+ // 如果没有预格式化的 message,整个事件序列化
278
+ return `[${sourceType}] ${JSON.stringify(payload, null, 2)}`;
279
+ }
280
+
281
+ // ============================================================================
282
+ // 辅助方法
283
+ // ============================================================================
284
+
285
+ /**
286
+ * 格式化时长
287
+ */
288
+ private formatDuration(ms: number): string {
289
+ if (ms < 1000) return `${ms}ms`;
290
+ if (ms < 60000) return `${(ms / 1000).toFixed(1)}秒`;
291
+ const minutes = Math.floor(ms / 60000);
292
+ const seconds = Math.floor((ms % 60000) / 1000);
293
+ return `${minutes}分${seconds}秒`;
294
+ }
295
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @fileoverview Shared modules for CLI commands
3
+ *
4
+ * 导出共享模块供 act、interactive 等命令复用
5
+ */
6
+
7
+ export { SessionManager } from "./session-manager";
8
+ export type { SessionInitResult, SessionManagerOptions } from "./session-manager";
9
+
10
+ export { QueryExecutor } from "./query-executor";
11
+ export type { QueryExecutorOptions, StreamOptions } from "./query-executor";