@northflare/runner 0.0.1

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 (154) hide show
  1. package/DEBUG_LOGGING.md +60 -0
  2. package/LICENSE +21 -0
  3. package/MIGRATION_PLAN.md +52 -0
  4. package/README.md +220 -0
  5. package/SDK_IMPLEMENTATION_GUIDE.md +1036 -0
  6. package/bin/northflare-runner +367 -0
  7. package/coverage/base.css +224 -0
  8. package/coverage/block-navigation.js +87 -0
  9. package/coverage/coverage-final.json +12 -0
  10. package/coverage/favicon.png +0 -0
  11. package/coverage/index.html +176 -0
  12. package/coverage/lib/index.html +116 -0
  13. package/coverage/lib/preload-script.js.html +964 -0
  14. package/coverage/prettify.css +1 -0
  15. package/coverage/prettify.js +2 -0
  16. package/coverage/sort-arrow-sprite.png +0 -0
  17. package/coverage/sorter.js +196 -0
  18. package/coverage/src/collections/index.html +116 -0
  19. package/coverage/src/collections/runner-messages.ts.html +312 -0
  20. package/coverage/src/components/claude-manager.ts.html +1290 -0
  21. package/coverage/src/components/index.html +146 -0
  22. package/coverage/src/components/message-handler.ts.html +730 -0
  23. package/coverage/src/components/repository-manager.ts.html +841 -0
  24. package/coverage/src/index.html +131 -0
  25. package/coverage/src/index.ts.html +448 -0
  26. package/coverage/src/runner.ts.html +1239 -0
  27. package/coverage/src/utils/config.ts.html +780 -0
  28. package/coverage/src/utils/console.ts.html +121 -0
  29. package/coverage/src/utils/index.html +161 -0
  30. package/coverage/src/utils/logger.ts.html +475 -0
  31. package/coverage/src/utils/status-line.ts.html +445 -0
  32. package/dist/collections/runner-messages.d.ts +52 -0
  33. package/dist/collections/runner-messages.d.ts.map +1 -0
  34. package/dist/collections/runner-messages.js +161 -0
  35. package/dist/collections/runner-messages.js.map +1 -0
  36. package/dist/components/claude-manager.d.ts +39 -0
  37. package/dist/components/claude-manager.d.ts.map +1 -0
  38. package/dist/components/claude-manager.js +783 -0
  39. package/dist/components/claude-manager.js.map +1 -0
  40. package/dist/components/claude-sdk-manager.d.ts +47 -0
  41. package/dist/components/claude-sdk-manager.d.ts.map +1 -0
  42. package/dist/components/claude-sdk-manager.js +1088 -0
  43. package/dist/components/claude-sdk-manager.js.map +1 -0
  44. package/dist/components/enhanced-repository-manager.d.ts +134 -0
  45. package/dist/components/enhanced-repository-manager.d.ts.map +1 -0
  46. package/dist/components/enhanced-repository-manager.js +602 -0
  47. package/dist/components/enhanced-repository-manager.js.map +1 -0
  48. package/dist/components/message-handler-sse.d.ts +46 -0
  49. package/dist/components/message-handler-sse.d.ts.map +1 -0
  50. package/dist/components/message-handler-sse.js +734 -0
  51. package/dist/components/message-handler-sse.js.map +1 -0
  52. package/dist/components/message-handler.d.ts +35 -0
  53. package/dist/components/message-handler.d.ts.map +1 -0
  54. package/dist/components/message-handler.js +689 -0
  55. package/dist/components/message-handler.js.map +1 -0
  56. package/dist/components/repository-manager.d.ts +51 -0
  57. package/dist/components/repository-manager.d.ts.map +1 -0
  58. package/dist/components/repository-manager.js +295 -0
  59. package/dist/components/repository-manager.js.map +1 -0
  60. package/dist/index.d.ts +9 -0
  61. package/dist/index.d.ts.map +1 -0
  62. package/dist/index.js +166 -0
  63. package/dist/index.js.map +1 -0
  64. package/dist/runner-sse.d.ts +57 -0
  65. package/dist/runner-sse.d.ts.map +1 -0
  66. package/dist/runner-sse.js +698 -0
  67. package/dist/runner-sse.js.map +1 -0
  68. package/dist/runner.d.ts +51 -0
  69. package/dist/runner.d.ts.map +1 -0
  70. package/dist/runner.js +530 -0
  71. package/dist/runner.js.map +1 -0
  72. package/dist/services/RunnerAPIClient.d.ts +30 -0
  73. package/dist/services/RunnerAPIClient.d.ts.map +1 -0
  74. package/dist/services/RunnerAPIClient.js +112 -0
  75. package/dist/services/RunnerAPIClient.js.map +1 -0
  76. package/dist/services/SSEClient.d.ts +60 -0
  77. package/dist/services/SSEClient.d.ts.map +1 -0
  78. package/dist/services/SSEClient.js +204 -0
  79. package/dist/services/SSEClient.js.map +1 -0
  80. package/dist/types/claude.d.ts +45 -0
  81. package/dist/types/claude.d.ts.map +1 -0
  82. package/dist/types/claude.js +6 -0
  83. package/dist/types/claude.js.map +1 -0
  84. package/dist/types/index.d.ts +47 -0
  85. package/dist/types/index.d.ts.map +1 -0
  86. package/dist/types/index.js +23 -0
  87. package/dist/types/index.js.map +1 -0
  88. package/dist/types/messages.d.ts +31 -0
  89. package/dist/types/messages.d.ts.map +1 -0
  90. package/dist/types/messages.js +6 -0
  91. package/dist/types/messages.js.map +1 -0
  92. package/dist/types/runner-interface.d.ts +24 -0
  93. package/dist/types/runner-interface.d.ts.map +1 -0
  94. package/dist/types/runner-interface.js +6 -0
  95. package/dist/types/runner-interface.js.map +1 -0
  96. package/dist/utils/StateManager.d.ts +52 -0
  97. package/dist/utils/StateManager.d.ts.map +1 -0
  98. package/dist/utils/StateManager.js +162 -0
  99. package/dist/utils/StateManager.js.map +1 -0
  100. package/dist/utils/config.d.ts +41 -0
  101. package/dist/utils/config.d.ts.map +1 -0
  102. package/dist/utils/config.js +250 -0
  103. package/dist/utils/config.js.map +1 -0
  104. package/dist/utils/console.d.ts +11 -0
  105. package/dist/utils/console.d.ts.map +1 -0
  106. package/dist/utils/console.js +15 -0
  107. package/dist/utils/console.js.map +1 -0
  108. package/dist/utils/expand-env.d.ts +2 -0
  109. package/dist/utils/expand-env.d.ts.map +1 -0
  110. package/dist/utils/expand-env.js +20 -0
  111. package/dist/utils/expand-env.js.map +1 -0
  112. package/dist/utils/logger.d.ts +9 -0
  113. package/dist/utils/logger.d.ts.map +1 -0
  114. package/dist/utils/logger.js +108 -0
  115. package/dist/utils/logger.js.map +1 -0
  116. package/dist/utils/status-line.d.ts +37 -0
  117. package/dist/utils/status-line.d.ts.map +1 -0
  118. package/dist/utils/status-line.js +113 -0
  119. package/dist/utils/status-line.js.map +1 -0
  120. package/docs/claude-manager.md +91 -0
  121. package/exceptions.log +22 -0
  122. package/lib/preload-script.js +293 -0
  123. package/package.json +55 -0
  124. package/rejections.log +63 -0
  125. package/runner.log +488 -0
  126. package/src/components/claude-sdk-manager.ts +1354 -0
  127. package/src/components/enhanced-repository-manager.ts +823 -0
  128. package/src/components/message-handler-sse.ts +1011 -0
  129. package/src/components/repository-manager.ts +337 -0
  130. package/src/index.ts +166 -0
  131. package/src/runner-sse.ts +847 -0
  132. package/src/services/RunnerAPIClient.ts +135 -0
  133. package/src/services/SSEClient.ts +258 -0
  134. package/src/types/claude.ts +55 -0
  135. package/src/types/computer-name.d.ts +4 -0
  136. package/src/types/index.ts +63 -0
  137. package/src/types/messages.ts +39 -0
  138. package/src/types/runner-interface.ts +34 -0
  139. package/src/utils/StateManager.ts +187 -0
  140. package/src/utils/codex-sdk.js +448 -0
  141. package/src/utils/config.ts +315 -0
  142. package/src/utils/console.ts +13 -0
  143. package/src/utils/expand-env.ts +22 -0
  144. package/src/utils/logger.ts +131 -0
  145. package/src/utils/sdk-demo.js +34 -0
  146. package/src/utils/status-line.ts +121 -0
  147. package/test-debug.sh +26 -0
  148. package/tests/retry-strategies.test.ts +410 -0
  149. package/tests/sdk-integration.test.ts +329 -0
  150. package/tests/sdk-streaming.test.ts +1180 -0
  151. package/tests/setup.ts +5 -0
  152. package/tests/test-claude-manager.ts +120 -0
  153. package/tsconfig.json +36 -0
  154. package/vitest.config.ts +27 -0
@@ -0,0 +1,329 @@
1
+ /**
2
+ * SDK Integration Test
3
+ *
4
+ * This test validates the core functionality of the new SDK-native implementation
5
+ * focusing on basic conversation operations that should work with the current code structure.
6
+ */
7
+
8
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
9
+ import { ClaudeManager } from '../src/components/claude-sdk-manager';
10
+ import { RunnerApp } from '../src/runner-sse';
11
+ import { EnhancedRepositoryManager } from '../src/components/enhanced-repository-manager';
12
+ import type { ConversationConfig, ConversationContext, Message } from '../src/types';
13
+ import type { IRunnerApp } from '../src/types/runner-interface';
14
+
15
+ // Mock the Claude SDKs
16
+ vi.mock("@anthropic-ai/claude-code", () => ({
17
+ query: vi.fn(),
18
+ }));
19
+
20
+ // Test configuration constants
21
+ const TEST_CONFIG = {
22
+ MESSAGE_TIMEOUT_MS: 1000,
23
+ } as const;
24
+
25
+ // Mock data
26
+ const mockSessionId = 'session_integration_test_12345';
27
+ const mockConversationId = 'conv_integration_test_67890';
28
+ const mockWorkspacePath = '/tmp/test-workspace-integration';
29
+ const mockApiKey = 'sk-test-integration-key-12345';
30
+
31
+ const mockConversationData = {
32
+ id: mockConversationId,
33
+ objectType: 'Task',
34
+ objectId: 'task_integration_123',
35
+ model: 'claude-3-5-sonnet-20241022',
36
+ globalInstructions: 'You are a test assistant.',
37
+ workspaceInstructions: 'This is a integration test workspace.',
38
+ permissionsMode: 'all',
39
+ agentSessionId: mockSessionId,
40
+ };
41
+
42
+ const mockConfig: ConversationConfig = {
43
+ anthropicApiKey: mockApiKey,
44
+ workspaceId: 'workspace_integration_123',
45
+ runnerRepoPath: mockWorkspacePath,
46
+ };
47
+
48
+ // Mock runner app
49
+ const createMockRunner = (): IRunnerApp => ({
50
+ activeConversations_: new Map<string, ConversationContext>(),
51
+ config_: {
52
+ orchestratorUrl: 'http://localhost:4000',
53
+ dataDir: '/tmp/test-data',
54
+ heartbeatInterval: 30000,
55
+ retryStrategy: 'exponential' as const,
56
+ retryIntervalSecs: 5,
57
+ retryDurationSecs: 300,
58
+ },
59
+ getRunnerUid: vi.fn().mockReturnValue('runner_integration_test_uid'),
60
+ getConversationContext: vi.fn(),
61
+ notify: vi.fn().mockResolvedValue(undefined),
62
+ sendToOrchestrator: vi.fn().mockResolvedValue({ result: {} }),
63
+ });
64
+
65
+ // Mock repository manager
66
+ const createMockRepositoryManager = (): EnhancedRepositoryManager => ({
67
+ checkoutRepository: vi.fn().mockResolvedValue(mockWorkspacePath),
68
+ getWorkspacePath: vi.fn().mockResolvedValue(mockWorkspacePath),
69
+ createTaskWorktree: vi.fn().mockResolvedValue({
70
+ worktreePath: mockWorkspacePath,
71
+ cleanup: vi.fn(),
72
+ }),
73
+ createLocalTaskHandle: vi.fn().mockResolvedValue({
74
+ path: mockWorkspacePath,
75
+ cleanup: vi.fn(),
76
+ }),
77
+ } as any);
78
+
79
+ describe('SDK Integration Tests', () => {
80
+ let mockRunner: IRunnerApp;
81
+ let mockRepositoryManager: EnhancedRepositoryManager;
82
+ let claudeManager: ClaudeManager;
83
+
84
+ beforeEach(async () => {
85
+ vi.clearAllMocks();
86
+
87
+ mockRunner = createMockRunner();
88
+ mockRepositoryManager = createMockRepositoryManager();
89
+ claudeManager = new ClaudeManager(mockRunner, mockRepositoryManager);
90
+
91
+ // Mock the getConversationContext method
92
+ mockRunner.getConversationContext = vi.fn().mockImplementation((id) =>
93
+ mockRunner.activeConversations_.get(id)
94
+ );
95
+ });
96
+
97
+ afterEach(() => {
98
+ // Clean up any active conversations
99
+ mockRunner.activeConversations_.clear();
100
+ });
101
+
102
+ describe('1. Basic SDK Manager Initialization', () => {
103
+ it('should create ClaudeManager instance successfully', () => {
104
+ expect(claudeManager).toBeInstanceOf(ClaudeManager);
105
+ });
106
+
107
+ it('should have required dependencies injected', () => {
108
+ expect(claudeManager).toHaveProperty('runner');
109
+ expect(claudeManager).toHaveProperty('repositoryManager');
110
+ });
111
+ });
112
+
113
+ describe('2. Conversation Lifecycle Management', () => {
114
+ it('should start a conversation with valid configuration', async () => {
115
+ // Mock SDK query to return a conversation-like object
116
+ const mockSdkQuery = vi.fn().mockImplementation(() => {
117
+ // Return an async iterable that simulates a conversation
118
+ return {
119
+ [Symbol.asyncIterator]: async function* () {
120
+ yield {
121
+ type: 'system',
122
+ message: { role: 'system', content: 'Session started' },
123
+ session_id: mockSessionId,
124
+ };
125
+ }
126
+ };
127
+ });
128
+
129
+ const mockModule = await import('@anthropic-ai/claude-code');
130
+ vi.mocked(mockModule.query).mockImplementation(mockSdkQuery);
131
+
132
+ // Start a conversation
133
+ const result = await claudeManager.startConversation(
134
+ 'Task',
135
+ 'task_integration_123',
136
+ mockConfig,
137
+ [{ content: 'Hello, this is a test message.' }],
138
+ mockConversationData
139
+ );
140
+
141
+ expect(result).toBeDefined();
142
+ expect(result.conversationId).toBe(mockConversationId);
143
+ expect(result.status).toBe('starting');
144
+ expect(result.conversationObjectType).toBe('Task');
145
+ expect(result.conversationObjectId).toBe('task_integration_123');
146
+ });
147
+
148
+ it('should throw error when starting conversation without conversationData.id', async () => {
149
+ await expect(
150
+ claudeManager.startConversation(
151
+ 'Task',
152
+ 'task_123',
153
+ mockConfig,
154
+ [{ content: 'Test message' }],
155
+ // Missing conversationData
156
+ )
157
+ ).rejects.toThrow('startConversation requires conversationData with a valid conversation.id');
158
+ });
159
+ });
160
+
161
+ describe('3. Message Sending Functionality', () => {
162
+ it('should handle sendUserMessage gracefully', async () => {
163
+ // Create a mock conversation context
164
+ const mockConversation = {
165
+ send: vi.fn().mockResolvedValue(undefined),
166
+ end: vi.fn().mockResolvedValue(undefined),
167
+ };
168
+
169
+ const context: ConversationContext = {
170
+ conversationId: mockConversationId,
171
+ agentSessionId: mockSessionId,
172
+ conversationObjectType: 'Task',
173
+ conversationObjectId: 'task_123',
174
+ status: 'active',
175
+ config: mockConfig,
176
+ startedAt: new Date(),
177
+ lastActivityAt: new Date(),
178
+ conversation: mockConversation as any,
179
+ model: 'claude-3-5-sonnet-20241022',
180
+ globalInstructions: '',
181
+ workspaceInstructions: '',
182
+ permissionsMode: 'all',
183
+ };
184
+
185
+ mockRunner.activeConversations_.set(mockConversationId, context);
186
+
187
+ // Test sending a message
188
+ await claudeManager.sendUserMessage(mockConversationId, 'Test message');
189
+
190
+ expect(mockConversation.send).toHaveBeenCalledWith({
191
+ type: 'text',
192
+ text: 'Test message',
193
+ });
194
+ });
195
+
196
+ it('should throw error for non-existent conversation', async () => {
197
+ // Try to send message to non-existent conversation
198
+ await expect(
199
+ claudeManager.sendUserMessage('non_existent_id', 'Test message')
200
+ ).rejects.toThrow('No active or fetchable conversation found for non_existent_id');
201
+ });
202
+ });
203
+
204
+ describe('4. Error Handling', () => {
205
+ it('should validate required conversationData', async () => {
206
+ // Test with missing conversationData
207
+ await expect(
208
+ claudeManager.startConversation(
209
+ 'Task',
210
+ 'task_123',
211
+ mockConfig,
212
+ [{ content: 'Test message' }],
213
+ undefined // Missing conversationData
214
+ )
215
+ ).rejects.toThrow('startConversation requires conversationData with a valid conversation.id');
216
+ });
217
+
218
+ it('should handle repository manager errors during task handle creation', async () => {
219
+ // Mock repository manager to throw error on createLocalTaskHandle
220
+ const errorRepo = createMockRepositoryManager();
221
+ vi.mocked(errorRepo.createLocalTaskHandle).mockRejectedValue(new Error('Repository not found'));
222
+
223
+ const errorManager = new ClaudeManager(mockRunner, errorRepo);
224
+
225
+ await expect(
226
+ errorManager.startConversation(
227
+ 'Task',
228
+ 'task_123',
229
+ mockConfig,
230
+ [{ content: 'Test message' }],
231
+ mockConversationData
232
+ )
233
+ ).rejects.toThrow('Repository not found');
234
+ });
235
+ });
236
+
237
+ describe('5. State Management', () => {
238
+ it('should track active conversations correctly', async () => {
239
+ // Initial state should have no conversations
240
+ expect(mockRunner.activeConversations_.size).toBe(0);
241
+
242
+ // Mock SDK query for conversation start
243
+ const mockSdkQuery = vi.fn().mockImplementation(() => ({
244
+ [Symbol.asyncIterator]: async function* () {
245
+ yield {
246
+ type: 'system',
247
+ message: { role: 'system', content: 'Session started' },
248
+ session_id: mockSessionId,
249
+ };
250
+ }
251
+ }));
252
+
253
+ const mockModule = await import('@anthropic-ai/claude-code');
254
+ vi.mocked(mockModule.query).mockImplementation(mockSdkQuery);
255
+
256
+ // Start a conversation
257
+ await claudeManager.startConversation(
258
+ 'Task',
259
+ 'task_state_123',
260
+ mockConfig,
261
+ [{ content: 'State test message' }],
262
+ { ...mockConversationData, objectId: 'task_state_123' }
263
+ );
264
+
265
+ // Should now have one active conversation
266
+ expect(mockRunner.activeConversations_.size).toBe(1);
267
+
268
+ const context = mockRunner.activeConversations_.get(mockConversationId);
269
+ expect(context).toBeDefined();
270
+ expect(context?.conversationObjectId).toBe('task_state_123');
271
+ });
272
+
273
+ it('should update conversation status appropriately', async () => {
274
+ // Create conversation context
275
+ const context: ConversationContext = {
276
+ conversationId: mockConversationId,
277
+ agentSessionId: mockSessionId,
278
+ conversationObjectType: 'Task',
279
+ conversationObjectId: 'task_123',
280
+ status: 'starting', // Initial status
281
+ config: mockConfig,
282
+ startedAt: new Date(),
283
+ lastActivityAt: new Date(),
284
+ model: 'claude-3-5-sonnet-20241022',
285
+ globalInstructions: '',
286
+ workspaceInstructions: '',
287
+ permissionsMode: 'all',
288
+ };
289
+
290
+ mockRunner.activeConversations_.set(mockConversationId, context);
291
+
292
+ // Verify initial status
293
+ expect(context.status).toBe('starting');
294
+
295
+ // Status should be updated during conversation operations
296
+ // This is tested implicitly through the conversation lifecycle
297
+ });
298
+ });
299
+
300
+ describe('6. Integration with Repository Manager', () => {
301
+ it('should use repository manager for task handle creation', async () => {
302
+ // Mock SDK query
303
+ const mockSdkQuery = vi.fn().mockImplementation(() => ({
304
+ [Symbol.asyncIterator]: async function* () {
305
+ yield {
306
+ type: 'system',
307
+ message: { role: 'system', content: 'Session started' },
308
+ session_id: mockSessionId,
309
+ };
310
+ }
311
+ }));
312
+
313
+ const mockModule = await import('@anthropic-ai/claude-code');
314
+ vi.mocked(mockModule.query).mockImplementation(mockSdkQuery);
315
+
316
+ // Start conversation (should call repository manager)
317
+ await claudeManager.startConversation(
318
+ 'Task',
319
+ 'task_repo_123',
320
+ mockConfig,
321
+ [{ content: 'Repository test message' }],
322
+ { ...mockConversationData, objectId: 'task_repo_123' }
323
+ );
324
+
325
+ // Verify repository manager was called for creating task handle
326
+ expect(mockRepositoryManager.createLocalTaskHandle).toHaveBeenCalled();
327
+ });
328
+ });
329
+ });