@northflare/runner 0.0.12 → 0.0.14

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 (80) hide show
  1. package/dist/utils/logger.d.ts.map +1 -1
  2. package/dist/utils/logger.js +5 -8
  3. package/dist/utils/logger.js.map +1 -1
  4. package/package.json +1 -2
  5. package/coverage/base.css +0 -224
  6. package/coverage/block-navigation.js +0 -87
  7. package/coverage/coverage-final.json +0 -12
  8. package/coverage/favicon.png +0 -0
  9. package/coverage/index.html +0 -176
  10. package/coverage/lib/index.html +0 -116
  11. package/coverage/lib/preload-script.js.html +0 -964
  12. package/coverage/prettify.css +0 -1
  13. package/coverage/prettify.js +0 -2
  14. package/coverage/sort-arrow-sprite.png +0 -0
  15. package/coverage/sorter.js +0 -196
  16. package/coverage/src/collections/index.html +0 -116
  17. package/coverage/src/collections/runner-messages.ts.html +0 -312
  18. package/coverage/src/components/claude-manager.ts.html +0 -1290
  19. package/coverage/src/components/index.html +0 -146
  20. package/coverage/src/components/message-handler.ts.html +0 -730
  21. package/coverage/src/components/repository-manager.ts.html +0 -841
  22. package/coverage/src/index.html +0 -131
  23. package/coverage/src/index.ts.html +0 -448
  24. package/coverage/src/runner.ts.html +0 -1239
  25. package/coverage/src/utils/config.ts.html +0 -780
  26. package/coverage/src/utils/console.ts.html +0 -121
  27. package/coverage/src/utils/index.html +0 -161
  28. package/coverage/src/utils/logger.ts.html +0 -475
  29. package/coverage/src/utils/status-line.ts.html +0 -445
  30. package/exceptions.log +0 -24
  31. package/lib/codex-sdk/src/codex.ts +0 -38
  32. package/lib/codex-sdk/src/codexOptions.ts +0 -10
  33. package/lib/codex-sdk/src/events.ts +0 -80
  34. package/lib/codex-sdk/src/exec.ts +0 -336
  35. package/lib/codex-sdk/src/index.ts +0 -39
  36. package/lib/codex-sdk/src/items.ts +0 -127
  37. package/lib/codex-sdk/src/outputSchemaFile.ts +0 -40
  38. package/lib/codex-sdk/src/thread.ts +0 -155
  39. package/lib/codex-sdk/src/threadOptions.ts +0 -18
  40. package/lib/codex-sdk/src/turnOptions.ts +0 -6
  41. package/lib/codex-sdk/tests/abort.test.ts +0 -165
  42. package/lib/codex-sdk/tests/codexExecSpy.ts +0 -37
  43. package/lib/codex-sdk/tests/responsesProxy.ts +0 -225
  44. package/lib/codex-sdk/tests/run.test.ts +0 -687
  45. package/lib/codex-sdk/tests/runStreamed.test.ts +0 -211
  46. package/lib/codex-sdk/tsconfig.json +0 -24
  47. package/rejections.log +0 -68
  48. package/runner.log +0 -488
  49. package/src/components/claude-sdk-manager.ts +0 -1425
  50. package/src/components/codex-sdk-manager.ts +0 -1358
  51. package/src/components/enhanced-repository-manager.ts +0 -823
  52. package/src/components/message-handler-sse.ts +0 -1097
  53. package/src/components/repository-manager.ts +0 -337
  54. package/src/index.ts +0 -168
  55. package/src/runner-sse.ts +0 -917
  56. package/src/services/RunnerAPIClient.ts +0 -175
  57. package/src/services/SSEClient.ts +0 -258
  58. package/src/types/claude.ts +0 -66
  59. package/src/types/computer-name.d.ts +0 -4
  60. package/src/types/index.ts +0 -64
  61. package/src/types/messages.ts +0 -39
  62. package/src/types/runner-interface.ts +0 -36
  63. package/src/utils/StateManager.ts +0 -187
  64. package/src/utils/config.ts +0 -327
  65. package/src/utils/console.ts +0 -15
  66. package/src/utils/debug.ts +0 -18
  67. package/src/utils/expand-env.ts +0 -22
  68. package/src/utils/logger.ts +0 -134
  69. package/src/utils/model.ts +0 -29
  70. package/src/utils/status-line.ts +0 -122
  71. package/src/utils/tool-response-sanitizer.ts +0 -160
  72. package/test-debug.sh +0 -26
  73. package/tests/retry-strategies.test.ts +0 -410
  74. package/tests/sdk-integration.test.ts +0 -329
  75. package/tests/sdk-streaming.test.ts +0 -1180
  76. package/tests/setup.ts +0 -5
  77. package/tests/test-claude-manager.ts +0 -120
  78. package/tests/tool-response-sanitizer.test.ts +0 -63
  79. package/tsconfig.json +0 -36
  80. 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
- });