@litmers/cursorflow-orchestrator 0.1.40 → 0.2.2
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/CHANGELOG.md +0 -2
- package/README.md +7 -3
- package/commands/cursorflow-init.md +0 -4
- package/dist/cli/logs.js +108 -9
- package/dist/cli/logs.js.map +1 -1
- package/dist/cli/models.js +20 -3
- package/dist/cli/models.js.map +1 -1
- package/dist/cli/monitor.d.ts +7 -10
- package/dist/cli/monitor.js +1088 -1240
- package/dist/cli/monitor.js.map +1 -1
- package/dist/cli/resume.js +21 -1
- package/dist/cli/resume.js.map +1 -1
- package/dist/cli/run.js +28 -9
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/signal.d.ts +6 -1
- package/dist/cli/signal.js +94 -12
- package/dist/cli/signal.js.map +1 -1
- package/dist/cli/tasks.js +3 -46
- package/dist/cli/tasks.js.map +1 -1
- package/dist/core/agent-supervisor.d.ts +23 -0
- package/dist/core/agent-supervisor.js +42 -0
- package/dist/core/agent-supervisor.js.map +1 -0
- package/dist/core/auto-recovery.d.ts +2 -1
- package/dist/core/auto-recovery.js +6 -1
- package/dist/core/auto-recovery.js.map +1 -1
- package/dist/core/failure-policy.d.ts +0 -1
- package/dist/core/failure-policy.js +0 -1
- package/dist/core/failure-policy.js.map +1 -1
- package/dist/core/git-lifecycle-manager.d.ts +284 -0
- package/dist/core/git-lifecycle-manager.js +778 -0
- package/dist/core/git-lifecycle-manager.js.map +1 -0
- package/dist/core/git-pipeline-coordinator.d.ts +21 -0
- package/dist/core/git-pipeline-coordinator.js +205 -0
- package/dist/core/git-pipeline-coordinator.js.map +1 -0
- package/dist/core/intervention.d.ts +176 -0
- package/dist/core/intervention.js +424 -0
- package/dist/core/intervention.js.map +1 -0
- package/dist/core/lane-state-machine.d.ts +423 -0
- package/dist/core/lane-state-machine.js +890 -0
- package/dist/core/lane-state-machine.js.map +1 -0
- package/dist/core/orchestrator.d.ts +4 -1
- package/dist/core/orchestrator.js +29 -62
- package/dist/core/orchestrator.js.map +1 -1
- package/dist/core/runner/agent.d.ts +7 -1
- package/dist/core/runner/agent.js +45 -30
- package/dist/core/runner/agent.js.map +1 -1
- package/dist/core/runner/pipeline.js +283 -123
- package/dist/core/runner/pipeline.js.map +1 -1
- package/dist/core/runner/task.d.ts +4 -5
- package/dist/core/runner/task.js +6 -80
- package/dist/core/runner/task.js.map +1 -1
- package/dist/core/runner.js +8 -2
- package/dist/core/runner.js.map +1 -1
- package/dist/core/stall-detection.d.ts +11 -4
- package/dist/core/stall-detection.js +62 -27
- package/dist/core/stall-detection.js.map +1 -1
- package/dist/hooks/contexts/index.d.ts +104 -0
- package/dist/hooks/contexts/index.js +134 -0
- package/dist/hooks/contexts/index.js.map +1 -0
- package/dist/hooks/data-accessor.d.ts +86 -0
- package/dist/hooks/data-accessor.js +410 -0
- package/dist/hooks/data-accessor.js.map +1 -0
- package/dist/hooks/flow-controller.d.ts +136 -0
- package/dist/hooks/flow-controller.js +351 -0
- package/dist/hooks/flow-controller.js.map +1 -0
- package/dist/hooks/index.d.ts +68 -0
- package/dist/hooks/index.js +105 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/manager.d.ts +129 -0
- package/dist/hooks/manager.js +389 -0
- package/dist/hooks/manager.js.map +1 -0
- package/dist/hooks/types.d.ts +463 -0
- package/dist/hooks/types.js +45 -0
- package/dist/hooks/types.js.map +1 -0
- package/dist/services/logging/buffer.d.ts +2 -2
- package/dist/services/logging/buffer.js +95 -42
- package/dist/services/logging/buffer.js.map +1 -1
- package/dist/services/logging/console.js +6 -1
- package/dist/services/logging/console.js.map +1 -1
- package/dist/services/logging/formatter.d.ts +9 -4
- package/dist/services/logging/formatter.js +64 -18
- package/dist/services/logging/formatter.js.map +1 -1
- package/dist/services/logging/index.d.ts +0 -1
- package/dist/services/logging/index.js +0 -1
- package/dist/services/logging/index.js.map +1 -1
- package/dist/services/logging/paths.d.ts +8 -0
- package/dist/services/logging/paths.js +48 -0
- package/dist/services/logging/paths.js.map +1 -0
- package/dist/services/logging/raw-log.d.ts +6 -0
- package/dist/services/logging/raw-log.js +37 -0
- package/dist/services/logging/raw-log.js.map +1 -0
- package/dist/services/process/index.js +1 -1
- package/dist/services/process/index.js.map +1 -1
- package/dist/types/agent.d.ts +15 -0
- package/dist/types/config.d.ts +22 -1
- package/dist/types/event-categories.d.ts +601 -0
- package/dist/types/event-categories.js +233 -0
- package/dist/types/event-categories.js.map +1 -0
- package/dist/types/events.d.ts +0 -20
- package/dist/types/flow.d.ts +10 -6
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.js +17 -3
- package/dist/types/index.js.map +1 -1
- package/dist/types/lane.d.ts +1 -1
- package/dist/types/logging.d.ts +1 -1
- package/dist/types/task.d.ts +12 -1
- package/dist/ui/log-viewer.d.ts +3 -0
- package/dist/ui/log-viewer.js +3 -0
- package/dist/ui/log-viewer.js.map +1 -1
- package/dist/utils/config.js +10 -1
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/cursor-agent.d.ts +11 -1
- package/dist/utils/cursor-agent.js +63 -16
- package/dist/utils/cursor-agent.js.map +1 -1
- package/dist/utils/enhanced-logger.d.ts +5 -1
- package/dist/utils/enhanced-logger.js +98 -19
- package/dist/utils/enhanced-logger.js.map +1 -1
- package/dist/utils/event-registry.d.ts +222 -0
- package/dist/utils/event-registry.js +463 -0
- package/dist/utils/event-registry.js.map +1 -0
- package/dist/utils/events.d.ts +1 -13
- package/dist/utils/events.js.map +1 -1
- package/dist/utils/flow.d.ts +10 -0
- package/dist/utils/flow.js +75 -0
- package/dist/utils/flow.js.map +1 -1
- package/dist/utils/log-constants.d.ts +1 -0
- package/dist/utils/log-constants.js +2 -1
- package/dist/utils/log-constants.js.map +1 -1
- package/dist/utils/log-formatter.d.ts +2 -1
- package/dist/utils/log-formatter.js +10 -10
- package/dist/utils/log-formatter.js.map +1 -1
- package/dist/utils/logger.d.ts +11 -0
- package/dist/utils/logger.js +82 -3
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/repro-thinking-logs.js +0 -13
- package/dist/utils/repro-thinking-logs.js.map +1 -1
- package/dist/utils/run-service.js +1 -1
- package/dist/utils/run-service.js.map +1 -1
- package/examples/README.md +0 -2
- package/examples/demo-project/README.md +1 -2
- package/package.json +18 -28
- package/scripts/setup-security.sh +0 -1
- package/scripts/test-log-parser.ts +171 -0
- package/scripts/verify-change.sh +272 -0
- package/src/cli/logs.ts +121 -10
- package/src/cli/models.ts +20 -3
- package/src/cli/monitor.ts +1257 -1342
- package/src/cli/resume.ts +27 -1
- package/src/cli/run.ts +29 -11
- package/src/cli/signal.ts +115 -17
- package/src/cli/tasks.ts +2 -59
- package/src/core/agent-supervisor.ts +64 -0
- package/src/core/auto-recovery.ts +7 -1
- package/src/core/failure-policy.ts +0 -1
- package/src/core/git-lifecycle-manager.ts +1011 -0
- package/src/core/git-pipeline-coordinator.ts +221 -0
- package/src/core/intervention.ts +481 -0
- package/src/core/lane-state-machine.ts +1097 -0
- package/src/core/orchestrator.ts +35 -61
- package/src/core/runner/agent.ts +66 -33
- package/src/core/runner/pipeline.ts +318 -138
- package/src/core/runner/task.ts +12 -97
- package/src/core/runner.ts +8 -2
- package/src/core/stall-detection.ts +72 -27
- package/src/hooks/contexts/index.ts +256 -0
- package/src/hooks/data-accessor.ts +488 -0
- package/src/hooks/flow-controller.ts +425 -0
- package/src/hooks/index.ts +154 -0
- package/src/hooks/manager.ts +434 -0
- package/src/hooks/types.ts +544 -0
- package/src/services/logging/buffer.ts +104 -43
- package/src/services/logging/console.ts +7 -1
- package/src/services/logging/formatter.ts +74 -18
- package/src/services/logging/index.ts +0 -2
- package/src/services/logging/paths.ts +14 -0
- package/src/services/logging/raw-log.ts +43 -0
- package/src/services/process/index.ts +1 -1
- package/src/types/agent.ts +15 -0
- package/src/types/config.ts +23 -1
- package/src/types/event-categories.ts +663 -0
- package/src/types/events.ts +0 -25
- package/src/types/flow.ts +10 -6
- package/src/types/index.ts +50 -4
- package/src/types/lane.ts +1 -2
- package/src/types/logging.ts +2 -1
- package/src/types/task.ts +12 -1
- package/src/ui/log-viewer.ts +3 -0
- package/src/utils/config.ts +11 -1
- package/src/utils/cursor-agent.ts +68 -16
- package/src/utils/enhanced-logger.ts +105 -19
- package/src/utils/event-registry.ts +595 -0
- package/src/utils/events.ts +0 -16
- package/src/utils/flow.ts +83 -0
- package/src/utils/log-constants.ts +2 -1
- package/src/utils/log-formatter.ts +10 -11
- package/src/utils/logger.ts +49 -3
- package/src/utils/repro-thinking-logs.ts +0 -15
- package/src/utils/run-service.ts +1 -1
- package/dist/services/logging/file-writer.d.ts +0 -71
- package/dist/services/logging/file-writer.js +0 -516
- package/dist/services/logging/file-writer.js.map +0 -1
- package/dist/types/review.d.ts +0 -17
- package/dist/types/review.js +0 -6
- package/dist/types/review.js.map +0 -1
- package/scripts/ai-security-check.js +0 -233
- package/src/services/logging/file-writer.ts +0 -526
- package/src/types/review.ts +0 -20
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event Registry - 타입화된 이벤트 버스
|
|
3
|
+
*
|
|
4
|
+
* 기존 events.ts를 확장하여 타입 안전한 이벤트 발행/구독을 제공합니다.
|
|
5
|
+
*
|
|
6
|
+
* 특징:
|
|
7
|
+
* - 타입 안전한 이벤트 발행/구독
|
|
8
|
+
* - 카테고리별 이벤트 필터링
|
|
9
|
+
* - 이벤트 히스토리 관리
|
|
10
|
+
* - 이벤트 직렬화/역직렬화
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { EventEmitter } from 'events';
|
|
14
|
+
import {
|
|
15
|
+
EventCategory,
|
|
16
|
+
AllEventTypes,
|
|
17
|
+
EventPayloadMap,
|
|
18
|
+
TypedCursorFlowEvent,
|
|
19
|
+
TypedEventHandler,
|
|
20
|
+
GenericEventHandler,
|
|
21
|
+
getCategoryFromEventType,
|
|
22
|
+
|
|
23
|
+
// Event Types
|
|
24
|
+
OrchestrationEventType,
|
|
25
|
+
LaneEventType,
|
|
26
|
+
TaskEventType,
|
|
27
|
+
GitEventType,
|
|
28
|
+
RecoveryEventType,
|
|
29
|
+
AgentEventType,
|
|
30
|
+
StateEventType,
|
|
31
|
+
SystemEventType,
|
|
32
|
+
} from '../types/event-categories';
|
|
33
|
+
|
|
34
|
+
// ============================================================================
|
|
35
|
+
// Event Registry Configuration
|
|
36
|
+
// ============================================================================
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 이벤트 레지스트리 설정
|
|
40
|
+
*/
|
|
41
|
+
export interface EventRegistryConfig {
|
|
42
|
+
/** 이벤트 히스토리 최대 크기 (0 = 무제한) */
|
|
43
|
+
maxHistorySize: number;
|
|
44
|
+
/** 이벤트 히스토리 유지 시간 (ms, 0 = 영구) */
|
|
45
|
+
historyTtlMs: number;
|
|
46
|
+
/** 디버그 모드 */
|
|
47
|
+
debug: boolean;
|
|
48
|
+
/** 이벤트 발행 시 콘솔 출력 */
|
|
49
|
+
logEvents: boolean;
|
|
50
|
+
/** 로그할 이벤트 카테고리 (빈 배열 = 모두) */
|
|
51
|
+
logCategories: EventCategory[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const DEFAULT_CONFIG: EventRegistryConfig = {
|
|
55
|
+
maxHistorySize: 1000,
|
|
56
|
+
historyTtlMs: 30 * 60 * 1000, // 30분
|
|
57
|
+
debug: false,
|
|
58
|
+
logEvents: false,
|
|
59
|
+
logCategories: [],
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// ============================================================================
|
|
63
|
+
// Event Registry
|
|
64
|
+
// ============================================================================
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 타입화된 이벤트 레지스트리
|
|
68
|
+
*
|
|
69
|
+
* 사용 예:
|
|
70
|
+
* ```typescript
|
|
71
|
+
* const registry = EventRegistry.getInstance();
|
|
72
|
+
*
|
|
73
|
+
* // 타입 안전한 이벤트 발행
|
|
74
|
+
* registry.emit(OrchestrationEventType.STARTED, {
|
|
75
|
+
* runId: 'run-123',
|
|
76
|
+
* tasksDir: '/path/to/tasks',
|
|
77
|
+
* laneCount: 3,
|
|
78
|
+
* runRoot: '/path/to/run'
|
|
79
|
+
* });
|
|
80
|
+
*
|
|
81
|
+
* // 타입 안전한 이벤트 구독
|
|
82
|
+
* registry.on(LaneEventType.COMPLETED, (event) => {
|
|
83
|
+
* console.log(event.payload.laneName, event.payload.exitCode);
|
|
84
|
+
* });
|
|
85
|
+
*
|
|
86
|
+
* // 카테고리별 구독
|
|
87
|
+
* registry.onCategory(EventCategory.GIT, (event) => {
|
|
88
|
+
* console.log('Git event:', event.type);
|
|
89
|
+
* });
|
|
90
|
+
*
|
|
91
|
+
* // 와일드카드 구독
|
|
92
|
+
* registry.onAny((event) => {
|
|
93
|
+
* console.log('Any event:', event.type);
|
|
94
|
+
* });
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export class EventRegistry extends EventEmitter {
|
|
98
|
+
private static instance: EventRegistry | null = null;
|
|
99
|
+
|
|
100
|
+
private config: EventRegistryConfig;
|
|
101
|
+
private runId: string = '';
|
|
102
|
+
private eventHistory: TypedCursorFlowEvent[] = [];
|
|
103
|
+
private categoryHandlers: Map<EventCategory, GenericEventHandler[]> = new Map();
|
|
104
|
+
|
|
105
|
+
private constructor(config: Partial<EventRegistryConfig> = {}) {
|
|
106
|
+
super();
|
|
107
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
108
|
+
this.setMaxListeners(100); // 많은 리스너 허용
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* 싱글톤 인스턴스 획득
|
|
113
|
+
*/
|
|
114
|
+
static getInstance(config?: Partial<EventRegistryConfig>): EventRegistry {
|
|
115
|
+
if (!EventRegistry.instance) {
|
|
116
|
+
EventRegistry.instance = new EventRegistry(config);
|
|
117
|
+
} else if (config) {
|
|
118
|
+
EventRegistry.instance.updateConfig(config);
|
|
119
|
+
}
|
|
120
|
+
return EventRegistry.instance;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* 인스턴스 리셋 (테스트용)
|
|
125
|
+
*/
|
|
126
|
+
static resetInstance(): void {
|
|
127
|
+
EventRegistry.instance = null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* 설정 업데이트
|
|
132
|
+
*/
|
|
133
|
+
updateConfig(config: Partial<EventRegistryConfig>): void {
|
|
134
|
+
this.config = { ...this.config, ...config };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Run ID 설정
|
|
139
|
+
*/
|
|
140
|
+
setRunId(id: string): void {
|
|
141
|
+
this.runId = id;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Run ID 조회
|
|
146
|
+
*/
|
|
147
|
+
getRunId(): string {
|
|
148
|
+
return this.runId;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// --------------------------------------------------------------------------
|
|
152
|
+
// Type-safe Event Emission
|
|
153
|
+
// --------------------------------------------------------------------------
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* 타입 안전한 이벤트 발행
|
|
157
|
+
*/
|
|
158
|
+
emit<T extends keyof EventPayloadMap>(
|
|
159
|
+
type: T,
|
|
160
|
+
payload: EventPayloadMap[T],
|
|
161
|
+
options: { laneName?: string } = {}
|
|
162
|
+
): boolean {
|
|
163
|
+
const category = getCategoryFromEventType(type as string);
|
|
164
|
+
|
|
165
|
+
const event: TypedCursorFlowEvent<T> = {
|
|
166
|
+
id: this.generateEventId(),
|
|
167
|
+
type,
|
|
168
|
+
category,
|
|
169
|
+
timestamp: new Date().toISOString(),
|
|
170
|
+
runId: this.runId,
|
|
171
|
+
laneName: options.laneName || this.extractLaneName(payload),
|
|
172
|
+
payload,
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
// 히스토리에 추가
|
|
176
|
+
this.addToHistory(event as TypedCursorFlowEvent);
|
|
177
|
+
|
|
178
|
+
// 디버그 로깅
|
|
179
|
+
if (this.config.logEvents && this.shouldLogEvent(category)) {
|
|
180
|
+
this.logEvent(event as TypedCursorFlowEvent);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// 이벤트 발행
|
|
184
|
+
super.emit(type as string, event);
|
|
185
|
+
|
|
186
|
+
// 카테고리별 핸들러 호출
|
|
187
|
+
const categoryHandlers = this.categoryHandlers.get(category);
|
|
188
|
+
if (categoryHandlers) {
|
|
189
|
+
for (const handler of categoryHandlers) {
|
|
190
|
+
try {
|
|
191
|
+
handler(event as TypedCursorFlowEvent);
|
|
192
|
+
} catch (error) {
|
|
193
|
+
console.error(`Event handler error for ${type}:`, error);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// 와일드카드 패턴 발행
|
|
199
|
+
super.emit(`${category}.*`, event);
|
|
200
|
+
super.emit('*', event);
|
|
201
|
+
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* 이벤트 ID 생성
|
|
207
|
+
*/
|
|
208
|
+
private generateEventId(): string {
|
|
209
|
+
return `evt_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* 페이로드에서 laneName 추출
|
|
214
|
+
*/
|
|
215
|
+
private extractLaneName(payload: any): string | undefined {
|
|
216
|
+
if (typeof payload === 'object' && payload !== null) {
|
|
217
|
+
return payload.laneName;
|
|
218
|
+
}
|
|
219
|
+
return undefined;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// --------------------------------------------------------------------------
|
|
223
|
+
// Type-safe Event Subscription
|
|
224
|
+
// --------------------------------------------------------------------------
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* 타입 안전한 이벤트 구독
|
|
228
|
+
*/
|
|
229
|
+
on<T extends keyof EventPayloadMap>(
|
|
230
|
+
type: T,
|
|
231
|
+
handler: TypedEventHandler<T>
|
|
232
|
+
): this {
|
|
233
|
+
return super.on(type as string, handler);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* 일회성 이벤트 구독
|
|
238
|
+
*/
|
|
239
|
+
once<T extends keyof EventPayloadMap>(
|
|
240
|
+
type: T,
|
|
241
|
+
handler: TypedEventHandler<T>
|
|
242
|
+
): this {
|
|
243
|
+
return super.once(type as string, handler);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* 이벤트 구독 해제
|
|
248
|
+
*/
|
|
249
|
+
off<T extends keyof EventPayloadMap>(
|
|
250
|
+
type: T,
|
|
251
|
+
handler: TypedEventHandler<T>
|
|
252
|
+
): this {
|
|
253
|
+
return super.off(type as string, handler);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* 카테고리별 이벤트 구독
|
|
258
|
+
*/
|
|
259
|
+
onCategory(category: EventCategory, handler: GenericEventHandler): () => void {
|
|
260
|
+
if (!this.categoryHandlers.has(category)) {
|
|
261
|
+
this.categoryHandlers.set(category, []);
|
|
262
|
+
}
|
|
263
|
+
this.categoryHandlers.get(category)!.push(handler);
|
|
264
|
+
|
|
265
|
+
// 와일드카드 패턴도 등록
|
|
266
|
+
super.on(`${category}.*`, handler);
|
|
267
|
+
|
|
268
|
+
// 구독 해제 함수 반환
|
|
269
|
+
return () => {
|
|
270
|
+
const handlers = this.categoryHandlers.get(category);
|
|
271
|
+
if (handlers) {
|
|
272
|
+
const index = handlers.indexOf(handler);
|
|
273
|
+
if (index > -1) {
|
|
274
|
+
handlers.splice(index, 1);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
super.off(`${category}.*`, handler);
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* 모든 이벤트 구독
|
|
283
|
+
*/
|
|
284
|
+
onAny(handler: GenericEventHandler): () => void {
|
|
285
|
+
super.on('*', handler);
|
|
286
|
+
|
|
287
|
+
return () => {
|
|
288
|
+
super.off('*', handler);
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// --------------------------------------------------------------------------
|
|
293
|
+
// Event History
|
|
294
|
+
// --------------------------------------------------------------------------
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* 히스토리에 이벤트 추가
|
|
298
|
+
*/
|
|
299
|
+
private addToHistory(event: TypedCursorFlowEvent): void {
|
|
300
|
+
this.eventHistory.push(event);
|
|
301
|
+
|
|
302
|
+
// 크기 제한 적용
|
|
303
|
+
if (this.config.maxHistorySize > 0 && this.eventHistory.length > this.config.maxHistorySize) {
|
|
304
|
+
this.eventHistory = this.eventHistory.slice(-this.config.maxHistorySize);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// TTL 적용
|
|
308
|
+
if (this.config.historyTtlMs > 0) {
|
|
309
|
+
const cutoffTime = Date.now() - this.config.historyTtlMs;
|
|
310
|
+
this.eventHistory = this.eventHistory.filter(
|
|
311
|
+
e => new Date(e.timestamp).getTime() > cutoffTime
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* 이벤트 히스토리 조회
|
|
318
|
+
*/
|
|
319
|
+
getHistory(options: {
|
|
320
|
+
category?: EventCategory;
|
|
321
|
+
laneName?: string;
|
|
322
|
+
type?: string;
|
|
323
|
+
since?: number;
|
|
324
|
+
limit?: number;
|
|
325
|
+
} = {}): TypedCursorFlowEvent[] {
|
|
326
|
+
let result = this.eventHistory;
|
|
327
|
+
|
|
328
|
+
if (options.category) {
|
|
329
|
+
result = result.filter(e => e.category === options.category);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (options.laneName) {
|
|
333
|
+
result = result.filter(e => e.laneName === options.laneName);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (options.type) {
|
|
337
|
+
result = result.filter(e => e.type === options.type);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (options.since) {
|
|
341
|
+
result = result.filter(e => new Date(e.timestamp).getTime() >= options.since!);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (options.limit) {
|
|
345
|
+
result = result.slice(-options.limit);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return result;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Lane별 이벤트 히스토리 조회
|
|
353
|
+
*/
|
|
354
|
+
getLaneHistory(laneName: string, limit?: number): TypedCursorFlowEvent[] {
|
|
355
|
+
return this.getHistory({ laneName, limit });
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* 카테고리별 이벤트 히스토리 조회
|
|
360
|
+
*/
|
|
361
|
+
getCategoryHistory(category: EventCategory, limit?: number): TypedCursorFlowEvent[] {
|
|
362
|
+
return this.getHistory({ category, limit });
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* 히스토리 초기화
|
|
367
|
+
*/
|
|
368
|
+
clearHistory(): void {
|
|
369
|
+
this.eventHistory = [];
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// --------------------------------------------------------------------------
|
|
373
|
+
// Event Logging
|
|
374
|
+
// --------------------------------------------------------------------------
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* 이벤트 로깅 여부 결정
|
|
378
|
+
*/
|
|
379
|
+
private shouldLogEvent(category: EventCategory): boolean {
|
|
380
|
+
if (this.config.logCategories.length === 0) {
|
|
381
|
+
return true;
|
|
382
|
+
}
|
|
383
|
+
return this.config.logCategories.includes(category);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* 이벤트 로깅
|
|
388
|
+
*/
|
|
389
|
+
private logEvent(event: TypedCursorFlowEvent): void {
|
|
390
|
+
const { type, category, laneName, timestamp } = event;
|
|
391
|
+
const laneLabel = laneName ? `[${laneName}]` : '';
|
|
392
|
+
console.log(`[Event] ${timestamp} ${category}.${type.split('.')[1]} ${laneLabel}`);
|
|
393
|
+
|
|
394
|
+
if (this.config.debug) {
|
|
395
|
+
console.log(' Payload:', JSON.stringify(event.payload, null, 2));
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// --------------------------------------------------------------------------
|
|
400
|
+
// Event Serialization
|
|
401
|
+
// --------------------------------------------------------------------------
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* 이벤트를 JSON으로 직렬화
|
|
405
|
+
*/
|
|
406
|
+
serializeEvent(event: TypedCursorFlowEvent): string {
|
|
407
|
+
return JSON.stringify(event);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* JSON에서 이벤트 역직렬화
|
|
412
|
+
*/
|
|
413
|
+
deserializeEvent(json: string): TypedCursorFlowEvent | null {
|
|
414
|
+
try {
|
|
415
|
+
return JSON.parse(json) as TypedCursorFlowEvent;
|
|
416
|
+
} catch {
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* 히스토리를 JSONL 형식으로 내보내기
|
|
423
|
+
*/
|
|
424
|
+
exportHistory(): string {
|
|
425
|
+
return this.eventHistory.map(e => JSON.stringify(e)).join('\n');
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* JSONL에서 히스토리 가져오기
|
|
430
|
+
*/
|
|
431
|
+
importHistory(jsonl: string): number {
|
|
432
|
+
const lines = jsonl.split('\n').filter(l => l.trim());
|
|
433
|
+
let imported = 0;
|
|
434
|
+
|
|
435
|
+
for (const line of lines) {
|
|
436
|
+
const event = this.deserializeEvent(line);
|
|
437
|
+
if (event) {
|
|
438
|
+
this.eventHistory.push(event);
|
|
439
|
+
imported++;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return imported;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// --------------------------------------------------------------------------
|
|
447
|
+
// Convenience Methods
|
|
448
|
+
// --------------------------------------------------------------------------
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* 마지막 이벤트 조회
|
|
452
|
+
*/
|
|
453
|
+
getLastEvent(laneName?: string): TypedCursorFlowEvent | undefined {
|
|
454
|
+
if (laneName) {
|
|
455
|
+
const laneHistory = this.getLaneHistory(laneName, 1);
|
|
456
|
+
return laneHistory[0];
|
|
457
|
+
}
|
|
458
|
+
return this.eventHistory[this.eventHistory.length - 1];
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* 이벤트 카운트 조회
|
|
463
|
+
*/
|
|
464
|
+
getEventCount(options: {
|
|
465
|
+
category?: EventCategory;
|
|
466
|
+
laneName?: string;
|
|
467
|
+
} = {}): number {
|
|
468
|
+
return this.getHistory(options).length;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* 특정 이벤트 대기 (Promise)
|
|
473
|
+
*/
|
|
474
|
+
waitFor<T extends keyof EventPayloadMap>(
|
|
475
|
+
type: T,
|
|
476
|
+
options: {
|
|
477
|
+
timeout?: number;
|
|
478
|
+
filter?: (event: TypedCursorFlowEvent<T>) => boolean;
|
|
479
|
+
} = {}
|
|
480
|
+
): Promise<TypedCursorFlowEvent<T>> {
|
|
481
|
+
return new Promise((resolve, reject) => {
|
|
482
|
+
const timeout = options.timeout || 30000;
|
|
483
|
+
|
|
484
|
+
const timer = setTimeout(() => {
|
|
485
|
+
this.off(type, handler);
|
|
486
|
+
reject(new Error(`Timeout waiting for event: ${type}`));
|
|
487
|
+
}, timeout);
|
|
488
|
+
|
|
489
|
+
const handler: TypedEventHandler<T> = (event) => {
|
|
490
|
+
if (!options.filter || options.filter(event)) {
|
|
491
|
+
clearTimeout(timer);
|
|
492
|
+
this.off(type, handler);
|
|
493
|
+
resolve(event);
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
this.on(type, handler);
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// ============================================================================
|
|
503
|
+
// Event Type Guards
|
|
504
|
+
// ============================================================================
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Orchestration 이벤트인지 확인
|
|
508
|
+
*/
|
|
509
|
+
export function isOrchestrationEvent(event: TypedCursorFlowEvent): boolean {
|
|
510
|
+
return event.category === EventCategory.ORCHESTRATION;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Lane 이벤트인지 확인
|
|
515
|
+
*/
|
|
516
|
+
export function isLaneEvent(event: TypedCursorFlowEvent): boolean {
|
|
517
|
+
return event.category === EventCategory.LANE;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Task 이벤트인지 확인
|
|
522
|
+
*/
|
|
523
|
+
export function isTaskEvent(event: TypedCursorFlowEvent): boolean {
|
|
524
|
+
return event.category === EventCategory.TASK;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Git 이벤트인지 확인
|
|
529
|
+
*/
|
|
530
|
+
export function isGitEvent(event: TypedCursorFlowEvent): boolean {
|
|
531
|
+
return event.category === EventCategory.GIT;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Recovery 이벤트인지 확인
|
|
536
|
+
*/
|
|
537
|
+
export function isRecoveryEvent(event: TypedCursorFlowEvent): boolean {
|
|
538
|
+
return event.category === EventCategory.RECOVERY;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* 오류 이벤트인지 확인
|
|
543
|
+
*/
|
|
544
|
+
export function isErrorEvent(event: TypedCursorFlowEvent): boolean {
|
|
545
|
+
const errorTypes = [
|
|
546
|
+
OrchestrationEventType.FAILED,
|
|
547
|
+
LaneEventType.FAILED,
|
|
548
|
+
TaskEventType.FAILED,
|
|
549
|
+
GitEventType.ERROR,
|
|
550
|
+
GitEventType.MERGE_CONFLICT,
|
|
551
|
+
GitEventType.PUSH_REJECTED,
|
|
552
|
+
AgentEventType.CONNECTION_ERROR,
|
|
553
|
+
AgentEventType.AUTH_ERROR,
|
|
554
|
+
AgentEventType.TIMEOUT,
|
|
555
|
+
StateEventType.TRANSITION_FAILED,
|
|
556
|
+
StateEventType.CORRUPTED,
|
|
557
|
+
];
|
|
558
|
+
|
|
559
|
+
return errorTypes.includes(event.type as any);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// ============================================================================
|
|
563
|
+
// Convenience Functions
|
|
564
|
+
// ============================================================================
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* 싱글톤 인스턴스 획득
|
|
568
|
+
*/
|
|
569
|
+
export function getEventRegistry(config?: Partial<EventRegistryConfig>): EventRegistry {
|
|
570
|
+
return EventRegistry.getInstance(config);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* 인스턴스 리셋 (테스트용)
|
|
575
|
+
*/
|
|
576
|
+
export function resetEventRegistry(): void {
|
|
577
|
+
EventRegistry.resetInstance();
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// ============================================================================
|
|
581
|
+
// Re-exports for convenience
|
|
582
|
+
// ============================================================================
|
|
583
|
+
|
|
584
|
+
export {
|
|
585
|
+
EventCategory,
|
|
586
|
+
OrchestrationEventType,
|
|
587
|
+
LaneEventType,
|
|
588
|
+
TaskEventType,
|
|
589
|
+
GitEventType,
|
|
590
|
+
RecoveryEventType,
|
|
591
|
+
AgentEventType,
|
|
592
|
+
StateEventType,
|
|
593
|
+
SystemEventType,
|
|
594
|
+
};
|
|
595
|
+
|
package/src/utils/events.ts
CHANGED
|
@@ -14,10 +14,6 @@ import {
|
|
|
14
14
|
TaskFailedPayload,
|
|
15
15
|
AgentPromptSentPayload,
|
|
16
16
|
AgentResponseReceivedPayload,
|
|
17
|
-
ReviewStartedPayload,
|
|
18
|
-
ReviewCompletedPayload,
|
|
19
|
-
ReviewApprovedPayload,
|
|
20
|
-
ReviewRejectedPayload
|
|
21
17
|
} from './types';
|
|
22
18
|
|
|
23
19
|
class CursorFlowEvents extends EventEmitter {
|
|
@@ -40,10 +36,6 @@ class CursorFlowEvents extends EventEmitter {
|
|
|
40
36
|
emit(type: 'task.failed', payload: TaskFailedPayload): boolean;
|
|
41
37
|
emit(type: 'agent.prompt_sent', payload: AgentPromptSentPayload): boolean;
|
|
42
38
|
emit(type: 'agent.response_received', payload: AgentResponseReceivedPayload): boolean;
|
|
43
|
-
emit(type: 'review.started', payload: ReviewStartedPayload): boolean;
|
|
44
|
-
emit(type: 'review.completed', payload: ReviewCompletedPayload): boolean;
|
|
45
|
-
emit(type: 'review.approved', payload: ReviewApprovedPayload): boolean;
|
|
46
|
-
emit(type: 'review.rejected', payload: ReviewRejectedPayload): boolean;
|
|
47
39
|
emit(type: string, payload: any): boolean;
|
|
48
40
|
emit(type: string, payload: any): boolean {
|
|
49
41
|
const event: CursorFlowEvent = {
|
|
@@ -82,10 +74,6 @@ class CursorFlowEvents extends EventEmitter {
|
|
|
82
74
|
on(pattern: 'task.failed', handler: EventHandler<TaskFailedPayload>): this;
|
|
83
75
|
on(pattern: 'agent.prompt_sent', handler: EventHandler<AgentPromptSentPayload>): this;
|
|
84
76
|
on(pattern: 'agent.response_received', handler: EventHandler<AgentResponseReceivedPayload>): this;
|
|
85
|
-
on(pattern: 'review.started', handler: EventHandler<ReviewStartedPayload>): this;
|
|
86
|
-
on(pattern: 'review.completed', handler: EventHandler<ReviewCompletedPayload>): this;
|
|
87
|
-
on(pattern: 'review.approved', handler: EventHandler<ReviewApprovedPayload>): this;
|
|
88
|
-
on(pattern: 'review.rejected', handler: EventHandler<ReviewRejectedPayload>): this;
|
|
89
77
|
on(pattern: string, handler: EventHandler): this;
|
|
90
78
|
on(pattern: string, handler: EventHandler): this {
|
|
91
79
|
return super.on(pattern, handler);
|
|
@@ -103,10 +91,6 @@ class CursorFlowEvents extends EventEmitter {
|
|
|
103
91
|
once(pattern: 'task.failed', handler: EventHandler<TaskFailedPayload>): this;
|
|
104
92
|
once(pattern: 'agent.prompt_sent', handler: EventHandler<AgentPromptSentPayload>): this;
|
|
105
93
|
once(pattern: 'agent.response_received', handler: EventHandler<AgentResponseReceivedPayload>): this;
|
|
106
|
-
once(pattern: 'review.started', handler: EventHandler<ReviewStartedPayload>): this;
|
|
107
|
-
once(pattern: 'review.completed', handler: EventHandler<ReviewCompletedPayload>): this;
|
|
108
|
-
once(pattern: 'review.approved', handler: EventHandler<ReviewApprovedPayload>): this;
|
|
109
|
-
once(pattern: 'review.rejected', handler: EventHandler<ReviewRejectedPayload>): this;
|
|
110
94
|
once(pattern: string, handler: EventHandler): this;
|
|
111
95
|
once(pattern: string, handler: EventHandler): this {
|
|
112
96
|
return super.once(pattern, handler);
|