@northflare/runner 0.0.12 → 0.0.13

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 (77) hide show
  1. package/package.json +2 -3
  2. package/coverage/base.css +0 -224
  3. package/coverage/block-navigation.js +0 -87
  4. package/coverage/coverage-final.json +0 -12
  5. package/coverage/favicon.png +0 -0
  6. package/coverage/index.html +0 -176
  7. package/coverage/lib/index.html +0 -116
  8. package/coverage/lib/preload-script.js.html +0 -964
  9. package/coverage/prettify.css +0 -1
  10. package/coverage/prettify.js +0 -2
  11. package/coverage/sort-arrow-sprite.png +0 -0
  12. package/coverage/sorter.js +0 -196
  13. package/coverage/src/collections/index.html +0 -116
  14. package/coverage/src/collections/runner-messages.ts.html +0 -312
  15. package/coverage/src/components/claude-manager.ts.html +0 -1290
  16. package/coverage/src/components/index.html +0 -146
  17. package/coverage/src/components/message-handler.ts.html +0 -730
  18. package/coverage/src/components/repository-manager.ts.html +0 -841
  19. package/coverage/src/index.html +0 -131
  20. package/coverage/src/index.ts.html +0 -448
  21. package/coverage/src/runner.ts.html +0 -1239
  22. package/coverage/src/utils/config.ts.html +0 -780
  23. package/coverage/src/utils/console.ts.html +0 -121
  24. package/coverage/src/utils/index.html +0 -161
  25. package/coverage/src/utils/logger.ts.html +0 -475
  26. package/coverage/src/utils/status-line.ts.html +0 -445
  27. package/exceptions.log +0 -24
  28. package/lib/codex-sdk/src/codex.ts +0 -38
  29. package/lib/codex-sdk/src/codexOptions.ts +0 -10
  30. package/lib/codex-sdk/src/events.ts +0 -80
  31. package/lib/codex-sdk/src/exec.ts +0 -336
  32. package/lib/codex-sdk/src/index.ts +0 -39
  33. package/lib/codex-sdk/src/items.ts +0 -127
  34. package/lib/codex-sdk/src/outputSchemaFile.ts +0 -40
  35. package/lib/codex-sdk/src/thread.ts +0 -155
  36. package/lib/codex-sdk/src/threadOptions.ts +0 -18
  37. package/lib/codex-sdk/src/turnOptions.ts +0 -6
  38. package/lib/codex-sdk/tests/abort.test.ts +0 -165
  39. package/lib/codex-sdk/tests/codexExecSpy.ts +0 -37
  40. package/lib/codex-sdk/tests/responsesProxy.ts +0 -225
  41. package/lib/codex-sdk/tests/run.test.ts +0 -687
  42. package/lib/codex-sdk/tests/runStreamed.test.ts +0 -211
  43. package/lib/codex-sdk/tsconfig.json +0 -24
  44. package/rejections.log +0 -68
  45. package/runner.log +0 -488
  46. package/src/components/claude-sdk-manager.ts +0 -1425
  47. package/src/components/codex-sdk-manager.ts +0 -1358
  48. package/src/components/enhanced-repository-manager.ts +0 -823
  49. package/src/components/message-handler-sse.ts +0 -1097
  50. package/src/components/repository-manager.ts +0 -337
  51. package/src/index.ts +0 -168
  52. package/src/runner-sse.ts +0 -917
  53. package/src/services/RunnerAPIClient.ts +0 -175
  54. package/src/services/SSEClient.ts +0 -258
  55. package/src/types/claude.ts +0 -66
  56. package/src/types/computer-name.d.ts +0 -4
  57. package/src/types/index.ts +0 -64
  58. package/src/types/messages.ts +0 -39
  59. package/src/types/runner-interface.ts +0 -36
  60. package/src/utils/StateManager.ts +0 -187
  61. package/src/utils/config.ts +0 -327
  62. package/src/utils/console.ts +0 -15
  63. package/src/utils/debug.ts +0 -18
  64. package/src/utils/expand-env.ts +0 -22
  65. package/src/utils/logger.ts +0 -134
  66. package/src/utils/model.ts +0 -29
  67. package/src/utils/status-line.ts +0 -122
  68. package/src/utils/tool-response-sanitizer.ts +0 -160
  69. package/test-debug.sh +0 -26
  70. package/tests/retry-strategies.test.ts +0 -410
  71. package/tests/sdk-integration.test.ts +0 -329
  72. package/tests/sdk-streaming.test.ts +0 -1180
  73. package/tests/setup.ts +0 -5
  74. package/tests/test-claude-manager.ts +0 -120
  75. package/tests/tool-response-sanitizer.test.ts +0 -63
  76. package/tsconfig.json +0 -36
  77. package/vitest.config.ts +0 -27
@@ -1,410 +0,0 @@
1
- /**
2
- * Tests for runner registration retry strategies
3
- */
4
-
5
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
6
- import { RunnerApp } from "../src/runner-sse";
7
- import { RunnerConfig } from "../src/types";
8
-
9
- // Mock dependencies
10
- vi.mock('simple-git', () => ({
11
- default: vi.fn(() => ({
12
- init: vi.fn().mockResolvedValue(undefined),
13
- addConfig: vi.fn().mockResolvedValue(undefined),
14
- }))
15
- }));
16
-
17
- vi.mock('fs/promises', () => ({
18
- default: {
19
- mkdir: vi.fn().mockResolvedValue(undefined),
20
- access: vi.fn().mockRejectedValue(new Error('Not found')),
21
- readFile: vi.fn().mockResolvedValue('{}'),
22
- writeFile: vi.fn().mockResolvedValue(undefined),
23
- }
24
- }));
25
-
26
- vi.mock('../src/components/message-handler-sse', () => ({
27
- MessageHandler: vi.fn().mockImplementation(() => ({
28
- startProcessing: vi.fn().mockResolvedValue(undefined),
29
- stopProcessing: vi.fn().mockResolvedValue(undefined),
30
- initializeCollectionAfterRegistration: vi.fn(),
31
- }))
32
- }));
33
-
34
- vi.mock('../src/components/claude-sdk-manager', () => ({
35
- ClaudeManager: vi.fn().mockImplementation(() => ({
36
- stopConversation: vi.fn().mockResolvedValue(undefined),
37
- }))
38
- }));
39
-
40
- vi.mock('../src/components/enhanced-repository-manager', () => ({
41
- EnhancedRepositoryManager: vi.fn().mockImplementation(() => ({
42
- createLocalTaskHandle: vi.fn(),
43
- createTaskWorktree: vi.fn(),
44
- checkoutRepository: vi.fn(),
45
- getWorkspacePath: vi.fn(),
46
- }))
47
- }));
48
-
49
- vi.mock('../src/utils/logger', () => ({
50
- createLogger: vi.fn(() => ({
51
- info: vi.fn(),
52
- warn: vi.fn(),
53
- error: vi.fn(),
54
- debug: vi.fn(),
55
- }))
56
- }));
57
-
58
- vi.mock('../src/utils/status-line', () => ({
59
- statusLineManager: {
60
- dispose: vi.fn(),
61
- }
62
- }));
63
-
64
- beforeEach(() => {
65
- vi.clearAllMocks();
66
- });
67
-
68
- afterEach(() => {
69
- vi.clearAllMocks();
70
- vi.clearAllTimers();
71
- });
72
-
73
- describe("Runner Registration Retry Strategies", () => {
74
- const baseConfig: RunnerConfig = {
75
- orchestratorUrl: "http://orchestrator.test",
76
- dataDir: "./test-data",
77
- heartbeatInterval: 120000,
78
- retryStrategy: "none",
79
- retryIntervalSecs: 60,
80
- retryDurationSecs: 900,
81
- workspaceDir: "/test/workspace",
82
- };
83
-
84
- describe("No Retry Strategy", () => {
85
- it("should fail immediately if registration fails", async () => {
86
- const config = { ...baseConfig, retryStrategy: "none" as const };
87
- const runner = new RunnerApp(config);
88
-
89
- // Mock sendToOrchestrator to fail
90
- runner.sendToOrchestrator = vi.fn().mockRejectedValue(new Error("Connection refused"));
91
-
92
- await expect(runner.start()).rejects.toThrow("Connection refused");
93
- expect(runner.sendToOrchestrator).toHaveBeenCalledTimes(1);
94
- });
95
-
96
- it("should succeed on first attempt", async () => {
97
- const config = { ...baseConfig, retryStrategy: "none" as const };
98
- const runner = new RunnerApp(config);
99
-
100
- // Mock successful registration
101
- runner.sendToOrchestrator = vi.fn().mockResolvedValue({
102
- jsonrpc: "2.0",
103
- result: {
104
- success: true,
105
- runnerId: "runner-123"
106
- }
107
- });
108
-
109
- await runner.start();
110
- expect(runner.getRunnerId()).toBe("runner-123");
111
- expect(runner.sendToOrchestrator).toHaveBeenCalledTimes(1);
112
- });
113
- });
114
-
115
- describe("Interval Retry Strategy", () => {
116
-
117
- it("should retry at fixed intervals until success", async () => {
118
- const config = {
119
- ...baseConfig,
120
- retryStrategy: "interval" as const,
121
- retryIntervalSecs: 2, // 2 seconds for testing
122
- };
123
- const runner = new RunnerApp(config);
124
-
125
- // Mock delay function to resolve immediately
126
- let delayCallCount = 0;
127
- runner.setDelayFunction(async (ms: number) => {
128
- delayCallCount++;
129
- expect(ms).toBe(2000); // Should always be 2 seconds
130
- });
131
-
132
- // Mock: fail 2 times, then succeed
133
- const mock = vi.fn()
134
- .mockRejectedValueOnce(new Error("Connection refused"))
135
- .mockRejectedValueOnce(new Error("Server error"))
136
- .mockResolvedValueOnce({
137
- jsonrpc: "2.0",
138
- result: {
139
- success: true,
140
- runnerId: "runner-456"
141
- }
142
- });
143
- runner.sendToOrchestrator = mock;
144
-
145
- await runner.start();
146
-
147
- expect(mock).toHaveBeenCalledTimes(3);
148
- expect(delayCallCount).toBe(2); // Two delays between three attempts
149
- expect(runner.getRunnerId()).toBe("runner-456");
150
- });
151
-
152
- it("should keep retrying indefinitely with interval strategy", async () => {
153
- const config = {
154
- ...baseConfig,
155
- retryStrategy: "interval" as const,
156
- retryIntervalSecs: 1,
157
- };
158
- const runner = new RunnerApp(config);
159
-
160
- // Track attempts
161
- let attemptCount = 0;
162
- const mock = vi.fn().mockImplementation(() => {
163
- attemptCount++;
164
- if (attemptCount <= 10) {
165
- return Promise.reject(new Error("Connection refused"));
166
- }
167
- return Promise.resolve({
168
- jsonrpc: "2.0",
169
- result: { success: true, runnerId: "runner-stopped" }
170
- });
171
- });
172
- runner.sendToOrchestrator = mock;
173
-
174
- // Mock delay to resolve immediately but track calls
175
- let delayCallCount = 0;
176
- runner.setDelayFunction(async (ms: number) => {
177
- delayCallCount++;
178
- expect(ms).toBe(1000);
179
- });
180
-
181
- await runner.start();
182
-
183
- expect(mock).toHaveBeenCalledTimes(11); // Initial + 10 retries
184
- expect(delayCallCount).toBe(10);
185
- await runner.stop();
186
- });
187
- });
188
-
189
- describe("Exponential Retry Strategy", () => {
190
-
191
- it("should retry with exponential backoff", async () => {
192
- const config = {
193
- ...baseConfig,
194
- retryStrategy: "exponential" as const,
195
- retryDurationSecs: 120, // 2 minutes for testing
196
- };
197
- const runner = new RunnerApp(config);
198
-
199
- // Track delay calls
200
- const delayCalls: number[] = [];
201
- runner.setDelayFunction(async (ms: number) => {
202
- delayCalls.push(ms);
203
- });
204
-
205
- // Mock: fail 3 times, then succeed
206
- const mock = vi.fn()
207
- .mockRejectedValueOnce(new Error("Error 1"))
208
- .mockRejectedValueOnce(new Error("Error 2"))
209
- .mockRejectedValueOnce(new Error("Error 3"))
210
- .mockResolvedValueOnce({
211
- jsonrpc: "2.0",
212
- result: {
213
- success: true,
214
- runnerId: "runner-789"
215
- }
216
- });
217
- runner.sendToOrchestrator = mock;
218
-
219
- await runner.start();
220
-
221
- expect(mock).toHaveBeenCalledTimes(4);
222
- expect(delayCalls).toEqual([8000, 16000, 32000]); // Exponential backoff
223
- expect(runner.getRunnerId()).toBe("runner-789");
224
- });
225
-
226
- it("should stop retrying after max duration", async () => {
227
- const config = {
228
- ...baseConfig,
229
- retryStrategy: "exponential" as const,
230
- retryDurationSecs: 30, // 30 seconds max
231
- };
232
- const runner = new RunnerApp(config);
233
-
234
- // Mock Date.now() for precise time control
235
- let currentTime = 0;
236
- vi.spyOn(Date, 'now').mockImplementation(() => currentTime);
237
-
238
- // Track delay calls
239
- const delayCalls: number[] = [];
240
- runner.setDelayFunction(async (ms: number) => {
241
- delayCalls.push(ms);
242
- currentTime += ms;
243
- });
244
-
245
- // Always fail
246
- runner.sendToOrchestrator = vi.fn().mockRejectedValue(new Error("Connection refused"));
247
-
248
- await expect(runner.start()).rejects.toThrow("Failed to register after 30 seconds");
249
-
250
- // Should have attempted: immediate, +8s, +16s, then final at 30s
251
- expect(runner.sendToOrchestrator).toHaveBeenCalledTimes(4);
252
- expect(delayCalls).toEqual([8000, 16000, 6000]); // Last delay is 6s to reach 30s total
253
-
254
- vi.spyOn(Date, 'now').mockRestore();
255
- await runner.stop();
256
- });
257
-
258
- it("should handle final retry at max duration", async () => {
259
- const config = {
260
- ...baseConfig,
261
- retryStrategy: "exponential" as const,
262
- retryDurationSecs: 60, // 1 minute
263
- };
264
- const runner = new RunnerApp(config);
265
-
266
- // Mock Date.now() for precise time control
267
- let currentTime = 0;
268
- vi.spyOn(Date, 'now').mockImplementation(() => currentTime);
269
-
270
- // Track delay calls
271
- const delayCalls: number[] = [];
272
- runner.setDelayFunction(async (ms: number) => {
273
- delayCalls.push(ms);
274
- currentTime += ms;
275
- });
276
-
277
- // Fail until the final retry
278
- let callCount = 0;
279
- runner.sendToOrchestrator = vi.fn().mockImplementation(() => {
280
- callCount++;
281
- if (callCount < 4) {
282
- return Promise.reject(new Error("Still failing"));
283
- }
284
- return Promise.resolve({
285
- jsonrpc: "2.0",
286
- result: {
287
- success: true,
288
- runnerId: "runner-final"
289
- }
290
- });
291
- });
292
-
293
- await runner.start();
294
-
295
- expect(runner.sendToOrchestrator).toHaveBeenCalledTimes(4);
296
- // First delay is 8s, second is 16s, third would be 32s but gets limited to remaining time
297
- // After 8+16=24s, only 36s remain to 60s total, but next delay would be 32s, so it's min(32, 36) = 32
298
- expect(delayCalls).toEqual([8000, 16000, 32000]);
299
- expect(runner.getRunnerId()).toBe("runner-final");
300
-
301
- vi.spyOn(Date, 'now').mockRestore();
302
- });
303
- });
304
-
305
- describe("Registration Response Handling", () => {
306
- it("should handle missing runnerId in response", async () => {
307
- const config = { ...baseConfig, retryStrategy: "none" as const };
308
- const runner = new RunnerApp(config);
309
-
310
- // Mock response without runnerId
311
- runner.sendToOrchestrator = vi.fn().mockResolvedValue({
312
- jsonrpc: "2.0",
313
- result: {
314
- success: true
315
- } // Missing runnerId
316
- });
317
-
318
- await expect(runner.start()).rejects.toThrow("Registration response did not include runnerId");
319
- });
320
-
321
- it("should store runnerId from successful registration", async () => {
322
- const config = { ...baseConfig, retryStrategy: "none" as const };
323
- const runner = new RunnerApp(config);
324
-
325
- runner.sendToOrchestrator = vi.fn().mockResolvedValue({
326
- jsonrpc: "2.0",
327
- result: {
328
- success: true,
329
- runnerId: "runner-abc-123"
330
- }
331
- });
332
-
333
- await runner.start();
334
- expect(runner.getRunnerId()).toBe("runner-abc-123");
335
- });
336
- });
337
-
338
- describe("Heartbeat Behavior", () => {
339
-
340
- it("should not start heartbeat if registration fails", async () => {
341
- vi.useFakeTimers();
342
-
343
- const config = {
344
- ...baseConfig,
345
- retryStrategy: "none" as const,
346
- heartbeatInterval: 5000,
347
- };
348
- const runner = new RunnerApp(config);
349
-
350
- // Mock failed registration
351
- const mock = vi.fn().mockRejectedValue(new Error("Registration failed"));
352
- runner.sendToOrchestrator = mock;
353
-
354
- await expect(runner.start()).rejects.toThrow("Registration failed");
355
-
356
- // Advance time to check if heartbeat was started
357
- await vi.advanceTimersByTimeAsync(10000);
358
-
359
- // Should only have the registration attempt, no heartbeat calls
360
- expect(mock).toHaveBeenCalledTimes(1);
361
-
362
- vi.useRealTimers();
363
- });
364
-
365
- it("should start heartbeat after successful registration", async () => {
366
- vi.useFakeTimers();
367
-
368
- const config = {
369
- ...baseConfig,
370
- retryStrategy: "none" as const,
371
- heartbeatInterval: 5000,
372
- };
373
- const runner = new RunnerApp(config);
374
-
375
- // Mock successful registration and heartbeat
376
- const mock = vi.fn()
377
- .mockResolvedValueOnce({
378
- jsonrpc: "2.0",
379
- result: {
380
- success: true,
381
- runnerId: "runner-heartbeat"
382
- }
383
- })
384
- .mockResolvedValue({
385
- jsonrpc: "2.0",
386
- result: {
387
- success: true
388
- }
389
- });
390
- runner.sendToOrchestrator = mock;
391
-
392
- await runner.start();
393
-
394
- // Initial registration call
395
- expect(mock).toHaveBeenCalledTimes(1);
396
-
397
- // Advance time for heartbeat
398
- await vi.advanceTimersByTimeAsync(5000);
399
- expect(mock).toHaveBeenCalledTimes(2);
400
-
401
- // Check heartbeat was called with correct params
402
- const heartbeatCall = mock.mock.calls[1];
403
- expect(heartbeatCall![0].method).toBe("runner.heartbeat");
404
- expect(heartbeatCall![0].params.runnerId).toBe("runner-heartbeat");
405
-
406
- await runner.stop();
407
- vi.useRealTimers();
408
- });
409
- });
410
- });