@engineeros/connector 0.14.4 → 0.15.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.
- package/README.md +110 -104
- package/bin/engineeros-connector.mjs +1010 -965
- package/package.json +41 -41
- package/src/assessment-spool.mjs +259 -0
- package/src/runner.mjs +2013 -1988
|
@@ -1,965 +1,1010 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import os from "node:os";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import {
|
|
5
|
-
assessmentResultUrl,
|
|
6
|
-
connectorResumeCredentials,
|
|
7
|
-
loadConfig,
|
|
8
|
-
mergePairedConfig,
|
|
9
|
-
resultUrl,
|
|
10
|
-
saveConfig,
|
|
11
|
-
socketUrl,
|
|
12
|
-
workspaceUrl,
|
|
13
|
-
} from "../src/config.mjs";
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
} from "../src/
|
|
42
|
-
import {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
import {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
codingAgent.
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
let
|
|
217
|
-
let
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
);
|
|
303
|
-
if (
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
if (
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
pump();
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
325
|
-
if (message.type === "
|
|
326
|
-
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
if (message.type === "
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
}
|
|
349
|
-
if (message.type === "
|
|
350
|
-
|
|
351
|
-
console.error(
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
)
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
);
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
console.log(
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
);
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
};
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
};
|
|
700
|
-
const
|
|
701
|
-
const
|
|
702
|
-
|
|
703
|
-
const
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
);
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import {
|
|
5
|
+
assessmentResultUrl,
|
|
6
|
+
connectorResumeCredentials,
|
|
7
|
+
loadConfig,
|
|
8
|
+
mergePairedConfig,
|
|
9
|
+
resultUrl,
|
|
10
|
+
saveConfig,
|
|
11
|
+
socketUrl,
|
|
12
|
+
workspaceUrl,
|
|
13
|
+
} from "../src/config.mjs";
|
|
14
|
+
import {
|
|
15
|
+
ASSESSMENT_RESULT_TIMEOUT_MS,
|
|
16
|
+
assessmentWorkerSnapshots,
|
|
17
|
+
assessmentInactivityFailure,
|
|
18
|
+
assessmentProgressMessage,
|
|
19
|
+
applyAcceptedChange,
|
|
20
|
+
enqueueWorkspaceAssessment,
|
|
21
|
+
executeAssignment,
|
|
22
|
+
executeConnectedPrompt,
|
|
23
|
+
executeWorkspaceAssessment,
|
|
24
|
+
inspectCodingAgent,
|
|
25
|
+
promptStreamEvent,
|
|
26
|
+
requeueInterruptedAssessment,
|
|
27
|
+
stopProcess,
|
|
28
|
+
submitAssessmentResultWithRetry,
|
|
29
|
+
takeWorkspaceAssessmentWave,
|
|
30
|
+
workspaceAssessmentWorkerLimit,
|
|
31
|
+
workspaceSnapshot,
|
|
32
|
+
} from "../src/runner.mjs";
|
|
33
|
+
import {
|
|
34
|
+
assessmentCheckpointPayload,
|
|
35
|
+
assessmentCompletionBundle,
|
|
36
|
+
clearAssessmentSpool,
|
|
37
|
+
loadAssessmentStageResult,
|
|
38
|
+
persistAssessmentStageResult,
|
|
39
|
+
prepareLocalAssessmentAssignment,
|
|
40
|
+
removeAssessmentStageResult,
|
|
41
|
+
} from "../src/assessment-spool.mjs";
|
|
42
|
+
import { disposeAcpRuntimes } from "../src/acp-client.mjs";
|
|
43
|
+
import {
|
|
44
|
+
installRegisteredAgent,
|
|
45
|
+
inspectRegisteredAgent,
|
|
46
|
+
registeredAgentConfig,
|
|
47
|
+
registeredAgents,
|
|
48
|
+
} from "../src/agent-registry.mjs";
|
|
49
|
+
import {
|
|
50
|
+
describeRejectedResponse,
|
|
51
|
+
describeWebSocketError,
|
|
52
|
+
protocolFailureMessage,
|
|
53
|
+
startConnectionWatchdog,
|
|
54
|
+
} from "../src/connection.mjs";
|
|
55
|
+
import { advertisedCapabilities } from "../src/capabilities.mjs";
|
|
56
|
+
import { parseConnectorArgs } from "../src/cli-args.mjs";
|
|
57
|
+
import { runMcpServer } from "../src/mcp-server.mjs";
|
|
58
|
+
import packageJson from "../package.json" with { type: "json" };
|
|
59
|
+
|
|
60
|
+
const { command, positional, flags } = parseConnectorArgs(
|
|
61
|
+
process.argv.slice(2),
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
if (command === "agents") {
|
|
65
|
+
let agents;
|
|
66
|
+
try {
|
|
67
|
+
agents = await registeredAgents();
|
|
68
|
+
} catch (error) {
|
|
69
|
+
fail(error instanceof Error ? error.message : String(error));
|
|
70
|
+
}
|
|
71
|
+
const statuses = await Promise.all(
|
|
72
|
+
agents.map(async (agent) => {
|
|
73
|
+
try {
|
|
74
|
+
await inspectRegisteredAgent(agent.id);
|
|
75
|
+
return `${agent.id}: ready (${agent.name} ${agent.version}, ${agent.distribution_type})`;
|
|
76
|
+
} catch (error) {
|
|
77
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
78
|
+
return `${agent.id}: setup needed (${agent.name} ${agent.version}, ${agent.distribution_type}) - ${detail}`;
|
|
79
|
+
}
|
|
80
|
+
}),
|
|
81
|
+
);
|
|
82
|
+
for (const status of statuses) {
|
|
83
|
+
console.log(status);
|
|
84
|
+
}
|
|
85
|
+
process.exit(0);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (command === "agent") {
|
|
89
|
+
const agentId = positional[0];
|
|
90
|
+
const action = positional[1] || "check";
|
|
91
|
+
if (!agentId || !["check", "install"].includes(action)) {
|
|
92
|
+
fail("Usage: engineeros-connector agent AGENT_ID [check|install]");
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const installed =
|
|
96
|
+
action === "install"
|
|
97
|
+
? await installRegisteredAgent(agentId)
|
|
98
|
+
: await inspectRegisteredAgent(agentId);
|
|
99
|
+
console.log(
|
|
100
|
+
`${installed.name} is ready (${installed.version}, ${installed.distribution}).`,
|
|
101
|
+
);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
fail(error instanceof Error ? error.message : String(error));
|
|
104
|
+
}
|
|
105
|
+
process.exit(0);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (command === "mcp") {
|
|
109
|
+
const config = await loadConfig(flags.workspace || process.cwd());
|
|
110
|
+
if (!config) fail("This workspace is not paired with EngineerOS.");
|
|
111
|
+
const runId = flags["run-id"] || positional[0];
|
|
112
|
+
if (!runId)
|
|
113
|
+
fail("Usage: engineeros-connector mcp --run-id RUN_ID [--workspace PATH]");
|
|
114
|
+
await runMcpServer({
|
|
115
|
+
config,
|
|
116
|
+
runId,
|
|
117
|
+
version: packageJson.version,
|
|
118
|
+
input: process.stdin,
|
|
119
|
+
output: process.stdout,
|
|
120
|
+
});
|
|
121
|
+
process.exit(0);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (command === "status") {
|
|
125
|
+
const config = await loadConfig(flags.workspace || process.cwd());
|
|
126
|
+
console.log(
|
|
127
|
+
config
|
|
128
|
+
? `Paired as ${config.name} (${config.connector_id}) for ${config.workspace}`
|
|
129
|
+
: "Not paired",
|
|
130
|
+
);
|
|
131
|
+
process.exit(config ? 0 : 1);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
let config;
|
|
135
|
+
let firstMessage;
|
|
136
|
+
let existingConfig;
|
|
137
|
+
if (command === "pair") {
|
|
138
|
+
const pairingCode = positional[0];
|
|
139
|
+
if (!pairingCode)
|
|
140
|
+
fail(
|
|
141
|
+
"Usage: engineeros-connector pair CODE --url URL [--agent AGENT_ID] [--name NAME] [--workspace PATH] [--assessment-workers COUNT] [--skip-git-repo-check]",
|
|
142
|
+
);
|
|
143
|
+
const url = flags.url;
|
|
144
|
+
if (!url) fail("Pairing requires --url with the EngineerOS backend address.");
|
|
145
|
+
if (flags.agent && flags["agent-command"]) {
|
|
146
|
+
fail("Use either --agent or --agent-command, not both.");
|
|
147
|
+
}
|
|
148
|
+
let agentConfig = {};
|
|
149
|
+
try {
|
|
150
|
+
if (flags.agent) {
|
|
151
|
+
await inspectRegisteredAgent(flags.agent);
|
|
152
|
+
agentConfig = await registeredAgentConfig(flags.agent);
|
|
153
|
+
}
|
|
154
|
+
} catch (error) {
|
|
155
|
+
fail(error instanceof Error ? error.message : String(error));
|
|
156
|
+
}
|
|
157
|
+
config = {
|
|
158
|
+
server_url: socketUrl(url),
|
|
159
|
+
workspace: path.resolve(flags.workspace || process.cwd()),
|
|
160
|
+
onboard: flags.onboard === true,
|
|
161
|
+
onboarding_pending: flags.onboard === true,
|
|
162
|
+
agent_protocol: flags["agent-command"] ? "acp" : "codex",
|
|
163
|
+
agent_command: flags["agent-command"] || null,
|
|
164
|
+
agent_args: parseAgentArgs(flags["agent-args"]),
|
|
165
|
+
agent_name: flags["agent-name"] || null,
|
|
166
|
+
...agentConfig,
|
|
167
|
+
...(flags["assessment-workers"] === undefined
|
|
168
|
+
? {}
|
|
169
|
+
: {
|
|
170
|
+
assessment_workers: configuredAssessmentWorkerLimit(
|
|
171
|
+
flags["assessment-workers"],
|
|
172
|
+
),
|
|
173
|
+
}),
|
|
174
|
+
skip_git_repo_check: flags["skip-git-repo-check"] === true,
|
|
175
|
+
name:
|
|
176
|
+
flags.name ||
|
|
177
|
+
`${os.hostname()} - ${path.basename(path.resolve(flags.workspace || process.cwd()))}`,
|
|
178
|
+
};
|
|
179
|
+
existingConfig = await loadConfig(config.workspace);
|
|
180
|
+
firstMessage = {
|
|
181
|
+
type: "pair",
|
|
182
|
+
pairing_code: pairingCode,
|
|
183
|
+
name: config.name,
|
|
184
|
+
capabilities: {},
|
|
185
|
+
...connectorResumeCredentials(existingConfig, config.server_url),
|
|
186
|
+
};
|
|
187
|
+
} else if (command === "start") {
|
|
188
|
+
config = await loadConfig(flags.workspace || process.cwd());
|
|
189
|
+
if (!config)
|
|
190
|
+
fail(
|
|
191
|
+
"This connector is not paired. Create a pairing command in EngineerOS first.",
|
|
192
|
+
);
|
|
193
|
+
firstMessage = {
|
|
194
|
+
type: "authenticate",
|
|
195
|
+
connector_id: config.connector_id,
|
|
196
|
+
token: config.token,
|
|
197
|
+
};
|
|
198
|
+
} else {
|
|
199
|
+
fail(
|
|
200
|
+
"Use `engineeros-connector pair`, `start`, `status`, `agents`, `agent`, or `mcp`.",
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
let codingAgent;
|
|
205
|
+
try {
|
|
206
|
+
codingAgent = await inspectCodingAgent(config, config.workspace);
|
|
207
|
+
} catch (error) {
|
|
208
|
+
fail(error instanceof Error ? error.message : String(error));
|
|
209
|
+
}
|
|
210
|
+
console.log(
|
|
211
|
+
`Using ${codingAgent.name} through ${codingAgent.protocol} (${codingAgent.version}).`,
|
|
212
|
+
);
|
|
213
|
+
const capabilities = advertisedCapabilities(config, codingAgent);
|
|
214
|
+
firstMessage.capabilities = capabilities;
|
|
215
|
+
|
|
216
|
+
let stopped = false;
|
|
217
|
+
let active = null;
|
|
218
|
+
const activeAssessments = new Map();
|
|
219
|
+
const ASSESSMENT_WORKER_LIMIT = configuredAssessmentWorkerLimit(
|
|
220
|
+
flags["assessment-workers"] ?? config.assessment_workers,
|
|
221
|
+
);
|
|
222
|
+
console.log(
|
|
223
|
+
`Assessment supervisor can run up to ${ASSESSMENT_WORKER_LIMIT} independent ${codingAgent.name} stages.`,
|
|
224
|
+
);
|
|
225
|
+
const available = [];
|
|
226
|
+
const assessments = [];
|
|
227
|
+
const activePrompts = new Map();
|
|
228
|
+
let socket;
|
|
229
|
+
let pingTimer;
|
|
230
|
+
let reconnectTimer;
|
|
231
|
+
let clearConnectionWatchdog;
|
|
232
|
+
let reconnectDelay = 1_000;
|
|
233
|
+
let connectionRejected = false;
|
|
234
|
+
let lastConnectionError;
|
|
235
|
+
let snapshotInFlight = false;
|
|
236
|
+
|
|
237
|
+
process.on("SIGINT", async () => {
|
|
238
|
+
stopped = true;
|
|
239
|
+
clearConnectionWatchdog?.();
|
|
240
|
+
clearTimeout(reconnectTimer);
|
|
241
|
+
clearInterval(pingTimer);
|
|
242
|
+
await Promise.all([
|
|
243
|
+
stopProcess(active?.child),
|
|
244
|
+
...[...activeAssessments.values()].map((state) => stopProcess(state.child)),
|
|
245
|
+
]);
|
|
246
|
+
await Promise.all([...activePrompts.values()].map(cancelPrompt));
|
|
247
|
+
await disposeAcpRuntimes();
|
|
248
|
+
socket?.close();
|
|
249
|
+
process.exit(0);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
await connect();
|
|
253
|
+
|
|
254
|
+
async function connect() {
|
|
255
|
+
console.log(`Connecting ${config.name} to ${config.server_url}`);
|
|
256
|
+
connectionRejected = false;
|
|
257
|
+
lastConnectionError = undefined;
|
|
258
|
+
socket = new WebSocket(config.server_url);
|
|
259
|
+
clearConnectionWatchdog = startConnectionWatchdog(socket, config.server_url, {
|
|
260
|
+
onTimeout: (message) => {
|
|
261
|
+
lastConnectionError = message;
|
|
262
|
+
console.error(message);
|
|
263
|
+
scheduleReconnect();
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
socket.addEventListener("open", () =>
|
|
267
|
+
socket.send(JSON.stringify(firstMessage)),
|
|
268
|
+
);
|
|
269
|
+
socket.addEventListener("message", async (event) => {
|
|
270
|
+
const message = JSON.parse(String(event.data));
|
|
271
|
+
if (message.type === "paired") {
|
|
272
|
+
clearConnectionWatchdog?.();
|
|
273
|
+
const reusedConnector =
|
|
274
|
+
existingConfig?.connector_id === message.connector.id;
|
|
275
|
+
config = mergePairedConfig(
|
|
276
|
+
config,
|
|
277
|
+
existingConfig,
|
|
278
|
+
message.connector.id,
|
|
279
|
+
message.token,
|
|
280
|
+
);
|
|
281
|
+
await saveConfig(config);
|
|
282
|
+
firstMessage = {
|
|
283
|
+
type: "authenticate",
|
|
284
|
+
connector_id: config.connector_id,
|
|
285
|
+
token: config.token,
|
|
286
|
+
capabilities,
|
|
287
|
+
};
|
|
288
|
+
console.log(
|
|
289
|
+
reusedConnector
|
|
290
|
+
? `Reconnected existing workspace connector ${config.connector_id}; assessment history is preserved.`
|
|
291
|
+
: `Paired. Connector ${config.connector_id} is online.`,
|
|
292
|
+
);
|
|
293
|
+
reconnectDelay = 1_000;
|
|
294
|
+
startPings();
|
|
295
|
+
if (config.onboarding_pending) void submitWorkspaceSnapshot();
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
if (message.type === "authenticated") {
|
|
299
|
+
clearConnectionWatchdog?.();
|
|
300
|
+
console.log("Connected and waiting for EngineerOS runs.");
|
|
301
|
+
reconnectDelay = 1_000;
|
|
302
|
+
startPings();
|
|
303
|
+
if (config.onboarding_pending) void submitWorkspaceSnapshot();
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
if (message.type === "workspace.refresh") {
|
|
307
|
+
void submitWorkspaceSnapshot();
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
if (message.type === "workspace.assessment") {
|
|
311
|
+
const assessmentKey = `${message.assessment_id}:${message.stage}`;
|
|
312
|
+
const disposition = enqueueWorkspaceAssessment(
|
|
313
|
+
assessments,
|
|
314
|
+
activeAssessments.get(assessmentKey),
|
|
315
|
+
message,
|
|
316
|
+
);
|
|
317
|
+
if (disposition === "deferred") {
|
|
318
|
+
console.log(
|
|
319
|
+
`Assessment stage ${message.stage} is still active after reconnect; preserving the replay until it finishes.`,
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
pump();
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
if (message.type === "prompt.execute") {
|
|
326
|
+
if (!activePrompts.has(message.prompt_id)) void executePrompt(message);
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
if (message.type === "prompt.cancel") {
|
|
330
|
+
const promptState = activePrompts.get(message.prompt_id);
|
|
331
|
+
if (promptState) await cancelPrompt(promptState);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
if (message.type === "run.available") {
|
|
335
|
+
if (!available.includes(message.run_id)) available.push(message.run_id);
|
|
336
|
+
pump();
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (message.type === "run.assignment") {
|
|
340
|
+
await execute(message);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
if (message.type === "run.cancelled" && active?.runId === message.run_id) {
|
|
344
|
+
console.log(`Run ${message.run_id} cancelled by EngineerOS.`);
|
|
345
|
+
active.cancelled = true;
|
|
346
|
+
await stopProcess(active.child);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
if (message.type === "connector.revoked") {
|
|
350
|
+
stopped = true;
|
|
351
|
+
console.error("This connector was revoked in EngineerOS.");
|
|
352
|
+
socket.close();
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
if (message.type === "run.error") {
|
|
356
|
+
console.error(`EngineerOS: ${message.message}`);
|
|
357
|
+
if (!message.run_id) snapshotInFlight = false;
|
|
358
|
+
if (active?.runId === message.run_id && !active.child) {
|
|
359
|
+
active = null;
|
|
360
|
+
pump();
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
if (message.type === "connection.error") {
|
|
364
|
+
connectionRejected = true;
|
|
365
|
+
console.error(`EngineerOS: ${message.message}`);
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
socket.addEventListener("close", (event) => {
|
|
369
|
+
clearConnectionWatchdog?.();
|
|
370
|
+
clearInterval(pingTimer);
|
|
371
|
+
for (const promptState of activePrompts.values())
|
|
372
|
+
void cancelPrompt(promptState);
|
|
373
|
+
for (const assessmentState of activeAssessments.values()) {
|
|
374
|
+
if (event.code === 4001) void stopProcess(assessmentState.child);
|
|
375
|
+
}
|
|
376
|
+
if (connectionRejected) {
|
|
377
|
+
stopped = true;
|
|
378
|
+
console.error(
|
|
379
|
+
"Connection rejected. Create a new pairing command in EngineerOS if this connector was revoked.",
|
|
380
|
+
);
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
if (stopped) return;
|
|
384
|
+
scheduleReconnect();
|
|
385
|
+
});
|
|
386
|
+
socket.addEventListener("error", (event) => {
|
|
387
|
+
lastConnectionError = describeWebSocketError(event);
|
|
388
|
+
console.error(
|
|
389
|
+
`WebSocket connection to ${config.server_url} failed: ${lastConnectionError}`,
|
|
390
|
+
);
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function scheduleReconnect() {
|
|
395
|
+
if (stopped || connectionRejected || reconnectTimer) return;
|
|
396
|
+
const detail = lastConnectionError
|
|
397
|
+
? ` Last error: ${lastConnectionError}`
|
|
398
|
+
: "";
|
|
399
|
+
console.error(
|
|
400
|
+
`Connection unavailable.${detail} Retrying in ${Math.round(reconnectDelay / 1_000)}s.`,
|
|
401
|
+
);
|
|
402
|
+
reconnectTimer = setTimeout(() => {
|
|
403
|
+
reconnectTimer = undefined;
|
|
404
|
+
void connect();
|
|
405
|
+
}, reconnectDelay);
|
|
406
|
+
reconnectDelay = Math.min(30_000, reconnectDelay * 2);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
async function submitWorkspaceSnapshot() {
|
|
410
|
+
if (snapshotInFlight || socket.readyState !== WebSocket.OPEN) return;
|
|
411
|
+
snapshotInFlight = true;
|
|
412
|
+
console.log("Inspecting the workspace without executing its code.");
|
|
413
|
+
try {
|
|
414
|
+
const snapshot = await workspaceSnapshot(config.workspace);
|
|
415
|
+
const response = await fetch(
|
|
416
|
+
workspaceUrl(config.server_url, config.connector_id),
|
|
417
|
+
{
|
|
418
|
+
method: "POST",
|
|
419
|
+
headers: {
|
|
420
|
+
"Content-Type": "application/json",
|
|
421
|
+
Authorization: `Bearer ${config.token}`,
|
|
422
|
+
},
|
|
423
|
+
body: JSON.stringify(snapshot),
|
|
424
|
+
},
|
|
425
|
+
);
|
|
426
|
+
if (!response.ok) {
|
|
427
|
+
throw new Error(
|
|
428
|
+
await describeRejectedResponse(response, "the workspace"),
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
const result = await response.json();
|
|
432
|
+
config = { ...config, onboarding_pending: false };
|
|
433
|
+
await saveConfig(config);
|
|
434
|
+
console.log(
|
|
435
|
+
`Inventoried ${snapshot.total_file_count.toLocaleString()} safe file(s).`,
|
|
436
|
+
);
|
|
437
|
+
console.log(
|
|
438
|
+
`Uploaded ${snapshot.evidence_file_count.toLocaleString()} prioritized source file(s).`,
|
|
439
|
+
);
|
|
440
|
+
console.log(
|
|
441
|
+
`Excluded ${snapshot.excluded_file_count.toLocaleString()} sensitive or generated file(s).`,
|
|
442
|
+
);
|
|
443
|
+
if (snapshot.omitted_evidence_file_count > 0) {
|
|
444
|
+
console.log(
|
|
445
|
+
`${snapshot.omitted_evidence_file_count.toLocaleString()} additional file(s) remain available to the connected Agent locally.`,
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
const executionProfiles =
|
|
449
|
+
result.connector?.capabilities?.execution_profiles;
|
|
450
|
+
const canSelectAssessmentModel =
|
|
451
|
+
executionProfiles?.model_selection === true &&
|
|
452
|
+
Array.isArray(executionProfiles.models) &&
|
|
453
|
+
executionProfiles.models.length > 0;
|
|
454
|
+
console.log(
|
|
455
|
+
canSelectAssessmentModel
|
|
456
|
+
? `Workspace inventory registered as ${result.workspace_kind}. Select the assessment model in EngineerOS to continue.`
|
|
457
|
+
: `Workspace inventory registered as ${result.workspace_kind}. This agent did not advertise selectable models; update it and reconnect before starting the baseline assessment.`,
|
|
458
|
+
);
|
|
459
|
+
snapshotInFlight = false;
|
|
460
|
+
} catch (error) {
|
|
461
|
+
snapshotInFlight = false;
|
|
462
|
+
console.error(
|
|
463
|
+
`Workspace assessment failed: ${protocolFailureMessage(error)}`,
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function startPings() {
|
|
469
|
+
clearInterval(pingTimer);
|
|
470
|
+
sendPing();
|
|
471
|
+
pingTimer = setInterval(sendPing, 10_000);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function sendPing() {
|
|
475
|
+
if (socket.readyState !== WebSocket.OPEN) return;
|
|
476
|
+
try {
|
|
477
|
+
socket.send(
|
|
478
|
+
JSON.stringify({
|
|
479
|
+
type: "ping",
|
|
480
|
+
active_run_id: active?.kind === "goal" ? active.runId : null,
|
|
481
|
+
assessment_workers: assessmentWorkerSnapshots(activeAssessments),
|
|
482
|
+
}),
|
|
483
|
+
);
|
|
484
|
+
} catch (error) {
|
|
485
|
+
lastConnectionError = protocolFailureMessage(error);
|
|
486
|
+
try {
|
|
487
|
+
socket.close();
|
|
488
|
+
} catch {
|
|
489
|
+
// The reconnect timer below owns recovery.
|
|
490
|
+
}
|
|
491
|
+
scheduleReconnect();
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function sendPromptEvent(promptId, event) {
|
|
496
|
+
if (!event || socket.readyState !== WebSocket.OPEN) return;
|
|
497
|
+
const payload = {
|
|
498
|
+
type: "prompt.event",
|
|
499
|
+
prompt_id: promptId,
|
|
500
|
+
kind: event.kind,
|
|
501
|
+
};
|
|
502
|
+
if (event.message) payload.message = String(event.message).slice(0, 500);
|
|
503
|
+
if (event.delta) payload.delta = String(event.delta).slice(0, 50_000);
|
|
504
|
+
if (event.status) payload.status = String(event.status).slice(0, 100);
|
|
505
|
+
socket.send(JSON.stringify(payload));
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function pump() {
|
|
509
|
+
if (socket.readyState !== WebSocket.OPEN || active) return;
|
|
510
|
+
const wave = takeWorkspaceAssessmentWave(
|
|
511
|
+
assessments,
|
|
512
|
+
activeAssessments.size,
|
|
513
|
+
ASSESSMENT_WORKER_LIMIT,
|
|
514
|
+
);
|
|
515
|
+
for (const assessment of wave) {
|
|
516
|
+
const assessmentKey = `${assessment.assessment_id}:${assessment.stage}`;
|
|
517
|
+
const assessmentState = {
|
|
518
|
+
kind: "assessment",
|
|
519
|
+
key: assessmentKey,
|
|
520
|
+
runId: assessment.assessment_id,
|
|
521
|
+
stage: assessment.stage,
|
|
522
|
+
child: null,
|
|
523
|
+
controller: null,
|
|
524
|
+
resumeAssignment: null,
|
|
525
|
+
phase: "starting",
|
|
526
|
+
progressPercent: 5,
|
|
527
|
+
lastMessage: `Starting ${assessment.stage} assessment stage`,
|
|
528
|
+
startedAt: Date.now(),
|
|
529
|
+
lastActivityAt: Date.now(),
|
|
530
|
+
eventCount: 0,
|
|
531
|
+
};
|
|
532
|
+
activeAssessments.set(assessmentKey, assessmentState);
|
|
533
|
+
void executeAssessment(assessment, assessmentState);
|
|
534
|
+
}
|
|
535
|
+
if (wave.length > 0) sendPing();
|
|
536
|
+
if (activeAssessments.size > 0 || assessments.length > 0) return;
|
|
537
|
+
const runId = available.shift();
|
|
538
|
+
if (!runId) return;
|
|
539
|
+
active = { kind: "goal", runId, child: null, cancelled: false };
|
|
540
|
+
socket.send(
|
|
541
|
+
JSON.stringify({
|
|
542
|
+
type: "run.claim",
|
|
543
|
+
run_id: runId,
|
|
544
|
+
runner_name: codingAgent.name,
|
|
545
|
+
metadata: {
|
|
546
|
+
hostname: os.hostname(),
|
|
547
|
+
workspace_name: path.basename(config.workspace),
|
|
548
|
+
},
|
|
549
|
+
}),
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
async function executePrompt(assignment) {
|
|
554
|
+
const promptId = assignment.prompt_id;
|
|
555
|
+
const promptState = {
|
|
556
|
+
runId: promptId,
|
|
557
|
+
child: null,
|
|
558
|
+
controller: null,
|
|
559
|
+
cancelled: false,
|
|
560
|
+
};
|
|
561
|
+
activePrompts.set(promptId, promptState);
|
|
562
|
+
let lastEvent;
|
|
563
|
+
const reportEvent = (event) => {
|
|
564
|
+
const serialized = event ? JSON.stringify(event) : null;
|
|
565
|
+
if (
|
|
566
|
+
!serialized ||
|
|
567
|
+
serialized === lastEvent ||
|
|
568
|
+
socket.readyState !== WebSocket.OPEN ||
|
|
569
|
+
activePrompts.get(promptId) !== promptState
|
|
570
|
+
) {
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
lastEvent = serialized;
|
|
574
|
+
sendPromptEvent(promptId, event);
|
|
575
|
+
};
|
|
576
|
+
sendPromptEvent(promptId, {
|
|
577
|
+
kind: "status",
|
|
578
|
+
message: "Agent started this request",
|
|
579
|
+
});
|
|
580
|
+
console.log(
|
|
581
|
+
`Answering ${assignment.purpose || "project"} prompt with ${codingAgent.name}.`,
|
|
582
|
+
);
|
|
583
|
+
try {
|
|
584
|
+
const result = await executeConnectedPrompt(assignment, config, {
|
|
585
|
+
onProcess: (child) => {
|
|
586
|
+
if (activePrompts.get(promptId) === promptState)
|
|
587
|
+
promptState.child = child;
|
|
588
|
+
},
|
|
589
|
+
onController: (controller) => {
|
|
590
|
+
if (activePrompts.get(promptId) === promptState) {
|
|
591
|
+
promptState.controller = controller;
|
|
592
|
+
}
|
|
593
|
+
},
|
|
594
|
+
onEvent: (event) => reportEvent(promptStreamEvent(event)),
|
|
595
|
+
});
|
|
596
|
+
config = {
|
|
597
|
+
...config,
|
|
598
|
+
sessions: {
|
|
599
|
+
...(config.sessions || {}),
|
|
600
|
+
[result.sessionKey]: result.sessionId,
|
|
601
|
+
},
|
|
602
|
+
};
|
|
603
|
+
await saveConfig(config);
|
|
604
|
+
if (promptState.cancelled || socket.readyState !== WebSocket.OPEN) return;
|
|
605
|
+
socket.send(
|
|
606
|
+
JSON.stringify({
|
|
607
|
+
type: "prompt.completed",
|
|
608
|
+
prompt_id: promptId,
|
|
609
|
+
content: result.content,
|
|
610
|
+
model: result.model,
|
|
611
|
+
session_id: result.sessionId,
|
|
612
|
+
usage: result.usage,
|
|
613
|
+
}),
|
|
614
|
+
);
|
|
615
|
+
} catch (error) {
|
|
616
|
+
const message = protocolFailureMessage(error);
|
|
617
|
+
if (!promptState.cancelled && socket.readyState === WebSocket.OPEN) {
|
|
618
|
+
socket.send(
|
|
619
|
+
JSON.stringify({
|
|
620
|
+
type: "prompt.failed",
|
|
621
|
+
prompt_id: promptId,
|
|
622
|
+
message,
|
|
623
|
+
}),
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
if (!promptState.cancelled) {
|
|
627
|
+
console.error(message);
|
|
628
|
+
}
|
|
629
|
+
} finally {
|
|
630
|
+
if (activePrompts.get(promptId) === promptState)
|
|
631
|
+
activePrompts.delete(promptId);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
async function cancelPrompt(promptState) {
|
|
636
|
+
promptState.cancelled = true;
|
|
637
|
+
if (typeof promptState.controller?.cancel === "function") {
|
|
638
|
+
await promptState.controller.cancel();
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
await stopProcess(promptState.child);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
async function cancelAssessment(assessmentState) {
|
|
645
|
+
if (typeof assessmentState.controller?.cancel === "function") {
|
|
646
|
+
await assessmentState.controller.cancel();
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
await stopProcess(assessmentState.child);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
async function executeAssessment(assignment, assessmentState) {
|
|
653
|
+
const assessmentId = assignment.assessment_id;
|
|
654
|
+
const stage = assignment.stage;
|
|
655
|
+
console.log(`Agent is running assessment stage ${stage} (${assessmentId}).`);
|
|
656
|
+
const stageStartedAt = assessmentState.startedAt;
|
|
657
|
+
let agentProcessStarted = false;
|
|
658
|
+
let agentCompleted = false;
|
|
659
|
+
let statusTicks = 0;
|
|
660
|
+
let progress = 5;
|
|
661
|
+
let accepted = false;
|
|
662
|
+
let failureReported = false;
|
|
663
|
+
let inactivityFailure = null;
|
|
664
|
+
let lastReportedMilestone = null;
|
|
665
|
+
const creditedMilestones = new Set();
|
|
666
|
+
const isActiveAssessment = () =>
|
|
667
|
+
activeAssessments.get(assessmentState.key) === assessmentState;
|
|
668
|
+
const sendAssessmentMessage = (payload) => {
|
|
669
|
+
if (socket.readyState !== WebSocket.OPEN) return false;
|
|
670
|
+
try {
|
|
671
|
+
socket.send(JSON.stringify(payload));
|
|
672
|
+
return true;
|
|
673
|
+
} catch (error) {
|
|
674
|
+
lastConnectionError = protocolFailureMessage(error);
|
|
675
|
+
try {
|
|
676
|
+
socket.close();
|
|
677
|
+
} catch {
|
|
678
|
+
// The reconnect timer below owns recovery.
|
|
679
|
+
}
|
|
680
|
+
scheduleReconnect();
|
|
681
|
+
return false;
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
const reportProgress = (message, { milestone = true } = {}) => {
|
|
685
|
+
if (!isActiveAssessment()) return;
|
|
686
|
+
if (milestone && lastReportedMilestone === message) return;
|
|
687
|
+
if (milestone) {
|
|
688
|
+
lastReportedMilestone = message;
|
|
689
|
+
if (!creditedMilestones.has(message)) {
|
|
690
|
+
creditedMilestones.add(message);
|
|
691
|
+
progress = Math.min(90, progress + 10);
|
|
692
|
+
}
|
|
693
|
+
console.log(`[assessment:${stage}] ${message}.`);
|
|
694
|
+
}
|
|
695
|
+
const boundedMessage = String(message).slice(0, 500);
|
|
696
|
+
assessmentState.progressPercent = progress;
|
|
697
|
+
assessmentState.lastMessage = boundedMessage;
|
|
698
|
+
};
|
|
699
|
+
reportProgress(`Starting ${stage} assessment stage`);
|
|
700
|
+
const heartbeat = setInterval(() => {
|
|
701
|
+
const now = Date.now();
|
|
702
|
+
const inactiveMs = now - assessmentState.lastActivityAt;
|
|
703
|
+
const nextInactivityFailure =
|
|
704
|
+
agentProcessStarted && !agentCompleted
|
|
705
|
+
? assessmentInactivityFailure(stage, inactiveMs)
|
|
706
|
+
: null;
|
|
707
|
+
if (!inactivityFailure && nextInactivityFailure) {
|
|
708
|
+
inactivityFailure = nextInactivityFailure;
|
|
709
|
+
console.error(`[assessment:${stage}] ${inactivityFailure}`);
|
|
710
|
+
void cancelAssessment(assessmentState);
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
if (inactivityFailure) return;
|
|
714
|
+
statusTicks += 1;
|
|
715
|
+
if (statusTicks % 2 === 0) {
|
|
716
|
+
console.log(
|
|
717
|
+
`[assessment:${stage}] ${formatAssessmentDuration(now - stageStartedAt)} elapsed · ` +
|
|
718
|
+
`${assessmentState.lastMessage} · last agent activity ${formatAssessmentDuration(inactiveMs)} ago ` +
|
|
719
|
+
`(${assessmentState.eventCount.toLocaleString()} events).`,
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
}, 15_000);
|
|
723
|
+
try {
|
|
724
|
+
if (assignment.assessment_mode === "incremental") {
|
|
725
|
+
progress = 15;
|
|
726
|
+
reportProgress("Calculating changed files and affected behavior");
|
|
727
|
+
}
|
|
728
|
+
const preparedAssignment = await prepareLocalAssessmentAssignment(
|
|
729
|
+
config.workspace,
|
|
730
|
+
assignment,
|
|
731
|
+
);
|
|
732
|
+
let result = await loadAssessmentStageResult(
|
|
733
|
+
config.workspace,
|
|
734
|
+
assessmentId,
|
|
735
|
+
stage,
|
|
736
|
+
);
|
|
737
|
+
if (result) {
|
|
738
|
+
reportProgress("Recovered completed stage report from connector storage");
|
|
739
|
+
} else {
|
|
740
|
+
result = await executeWorkspaceAssessment(preparedAssignment, config, {
|
|
741
|
+
onController: (controller) => {
|
|
742
|
+
if (isActiveAssessment()) assessmentState.controller = controller;
|
|
743
|
+
},
|
|
744
|
+
onProcess: (child) => {
|
|
745
|
+
if (isActiveAssessment()) {
|
|
746
|
+
assessmentState.child = child;
|
|
747
|
+
agentProcessStarted = true;
|
|
748
|
+
assessmentState.phase = "running";
|
|
749
|
+
assessmentState.lastActivityAt = Date.now();
|
|
750
|
+
reportProgress("Connected agent process started");
|
|
751
|
+
}
|
|
752
|
+
},
|
|
753
|
+
onEvent: (event) => {
|
|
754
|
+
assessmentState.lastActivityAt = Date.now();
|
|
755
|
+
assessmentState.eventCount += 1;
|
|
756
|
+
const message = assessmentProgressMessage(event);
|
|
757
|
+
if (message) reportProgress(message);
|
|
758
|
+
},
|
|
759
|
+
});
|
|
760
|
+
await persistAssessmentStageResult(
|
|
761
|
+
config.workspace,
|
|
762
|
+
assessmentId,
|
|
763
|
+
preparedAssignment,
|
|
764
|
+
result,
|
|
765
|
+
);
|
|
766
|
+
}
|
|
767
|
+
agentCompleted = true;
|
|
768
|
+
assessmentState.phase = "delivering";
|
|
769
|
+
assessmentState.progressPercent = 95;
|
|
770
|
+
assessmentState.lastMessage =
|
|
771
|
+
stage === "synthesis"
|
|
772
|
+
? "Delivering completed assessment bundle"
|
|
773
|
+
: "Checkpointing connector-local stage result";
|
|
774
|
+
const submitResult = (payload) =>
|
|
775
|
+
fetch(
|
|
776
|
+
assessmentResultUrl(
|
|
777
|
+
config.server_url,
|
|
778
|
+
config.connector_id,
|
|
779
|
+
assessmentId,
|
|
780
|
+
),
|
|
781
|
+
{
|
|
782
|
+
method: "POST",
|
|
783
|
+
headers: {
|
|
784
|
+
"Content-Type": "application/json",
|
|
785
|
+
Authorization: `Bearer ${config.token}`,
|
|
786
|
+
},
|
|
787
|
+
body: JSON.stringify(payload),
|
|
788
|
+
signal: AbortSignal.timeout(ASSESSMENT_RESULT_TIMEOUT_MS),
|
|
789
|
+
},
|
|
790
|
+
);
|
|
791
|
+
const deliverResult = (payload) =>
|
|
792
|
+
submitAssessmentResultWithRetry(payload, submitResult, {
|
|
793
|
+
isActive: () => isActiveAssessment() && !stopped,
|
|
794
|
+
onRetry: (error, delayMs) => {
|
|
795
|
+
reportProgress("Completed result is waiting for EngineerOS", {
|
|
796
|
+
milestone: false,
|
|
797
|
+
});
|
|
798
|
+
console.warn(
|
|
799
|
+
`[assessment:${stage}] Completed result delivery failed: ${protocolFailureMessage(error)} ` +
|
|
800
|
+
`Retrying in ${Math.round(delayMs / 1_000)}s without rerunning the agent.`,
|
|
801
|
+
);
|
|
802
|
+
},
|
|
803
|
+
});
|
|
804
|
+
const deliveryPayload =
|
|
805
|
+
stage === "synthesis"
|
|
806
|
+
? await assessmentCompletionBundle(
|
|
807
|
+
config.workspace,
|
|
808
|
+
assessmentId,
|
|
809
|
+
result,
|
|
810
|
+
)
|
|
811
|
+
: assessmentCheckpointPayload(result);
|
|
812
|
+
let response = await deliverResult(deliveryPayload);
|
|
813
|
+
if (response.status === 422 && result.correctAfterRejection) {
|
|
814
|
+
const rejection = await describeRejectedResponse(
|
|
815
|
+
response,
|
|
816
|
+
"the assessment",
|
|
817
|
+
);
|
|
818
|
+
agentCompleted = false;
|
|
819
|
+
assessmentState.phase = "correcting";
|
|
820
|
+
assessmentState.lastMessage = "Correcting a rejected stage result";
|
|
821
|
+
result = await result.correctAfterRejection(rejection);
|
|
822
|
+
await removeAssessmentStageResult(config.workspace, assessmentId, stage);
|
|
823
|
+
await persistAssessmentStageResult(
|
|
824
|
+
config.workspace,
|
|
825
|
+
assessmentId,
|
|
826
|
+
preparedAssignment,
|
|
827
|
+
result,
|
|
828
|
+
);
|
|
829
|
+
agentCompleted = true;
|
|
830
|
+
assessmentState.phase = "delivering";
|
|
831
|
+
assessmentState.lastMessage = "Delivering corrected stage result";
|
|
832
|
+
response = await deliverResult(
|
|
833
|
+
stage === "synthesis"
|
|
834
|
+
? await assessmentCompletionBundle(
|
|
835
|
+
config.workspace,
|
|
836
|
+
assessmentId,
|
|
837
|
+
result,
|
|
838
|
+
)
|
|
839
|
+
: assessmentCheckpointPayload(result),
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
if (!response.ok) {
|
|
843
|
+
throw new Error(
|
|
844
|
+
await describeRejectedResponse(response, "the assessment"),
|
|
845
|
+
);
|
|
846
|
+
}
|
|
847
|
+
accepted = true;
|
|
848
|
+
if (stage === "synthesis") {
|
|
849
|
+
await clearAssessmentSpool(config.workspace, assessmentId);
|
|
850
|
+
console.log(
|
|
851
|
+
`Workspace assessment ${assessmentId} was accepted by EngineerOS.`,
|
|
852
|
+
);
|
|
853
|
+
} else {
|
|
854
|
+
console.log(
|
|
855
|
+
`Workspace assessment stage ${stage} was checkpointed by EngineerOS.`,
|
|
856
|
+
);
|
|
857
|
+
}
|
|
858
|
+
} catch (error) {
|
|
859
|
+
const message = inactivityFailure || protocolFailureMessage(error);
|
|
860
|
+
if (isActiveAssessment()) {
|
|
861
|
+
failureReported = sendAssessmentMessage({
|
|
862
|
+
type: "workspace.assessment.failed",
|
|
863
|
+
assessment_id: assessmentId,
|
|
864
|
+
stage,
|
|
865
|
+
message,
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
console.error(message);
|
|
869
|
+
} finally {
|
|
870
|
+
clearInterval(heartbeat);
|
|
871
|
+
if (isActiveAssessment()) {
|
|
872
|
+
activeAssessments.delete(assessmentState.key);
|
|
873
|
+
const replayQueued = requeueInterruptedAssessment(
|
|
874
|
+
assessments,
|
|
875
|
+
assessmentState,
|
|
876
|
+
{ accepted, failureReported },
|
|
877
|
+
);
|
|
878
|
+
if (replayQueued) {
|
|
879
|
+
console.log(`Restarting interrupted assessment stage ${stage}.`);
|
|
880
|
+
}
|
|
881
|
+
pump();
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
function formatAssessmentDuration(milliseconds) {
|
|
887
|
+
const totalSeconds = Math.max(0, Math.floor(milliseconds / 1_000));
|
|
888
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
889
|
+
const seconds = totalSeconds % 60;
|
|
890
|
+
return minutes ? `${minutes}m ${seconds}s` : `${seconds}s`;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
async function execute(assignment) {
|
|
894
|
+
const runId = assignment.run_id;
|
|
895
|
+
console.log(`Running Goal ${runId} with ${codingAgent.name}.`);
|
|
896
|
+
let progress = 10;
|
|
897
|
+
const heartbeat = setInterval(() => {
|
|
898
|
+
if (socket.readyState === WebSocket.OPEN && active?.runId === runId) {
|
|
899
|
+
progress = Math.min(90, progress + 5);
|
|
900
|
+
socket.send(
|
|
901
|
+
JSON.stringify({
|
|
902
|
+
type: "run.progress",
|
|
903
|
+
run_id: runId,
|
|
904
|
+
progress_percent: progress,
|
|
905
|
+
message: "Agent is working",
|
|
906
|
+
}),
|
|
907
|
+
);
|
|
908
|
+
}
|
|
909
|
+
}, 15_000);
|
|
910
|
+
try {
|
|
911
|
+
const result = await executeAssignment(assignment, config, {
|
|
912
|
+
onProcess: (child) => {
|
|
913
|
+
if (active?.runId === runId) active.child = child;
|
|
914
|
+
},
|
|
915
|
+
onEvent: (event) => {
|
|
916
|
+
const message =
|
|
917
|
+
event.message || event.item?.text || event.type || "Agent is working";
|
|
918
|
+
if (socket.readyState === WebSocket.OPEN) {
|
|
919
|
+
socket.send(
|
|
920
|
+
JSON.stringify({
|
|
921
|
+
type: "run.progress",
|
|
922
|
+
run_id: runId,
|
|
923
|
+
progress_percent: progress,
|
|
924
|
+
message: String(message).slice(0, 500),
|
|
925
|
+
}),
|
|
926
|
+
);
|
|
927
|
+
}
|
|
928
|
+
},
|
|
929
|
+
});
|
|
930
|
+
if (active?.cancelled) return;
|
|
931
|
+
const response = await fetch(
|
|
932
|
+
resultUrl(config.server_url, config.connector_id, runId),
|
|
933
|
+
{
|
|
934
|
+
method: "POST",
|
|
935
|
+
headers: {
|
|
936
|
+
"Content-Type": "application/json",
|
|
937
|
+
Authorization: `Bearer ${config.token}`,
|
|
938
|
+
},
|
|
939
|
+
body: JSON.stringify(result),
|
|
940
|
+
},
|
|
941
|
+
);
|
|
942
|
+
if (!response.ok) {
|
|
943
|
+
throw new Error(await describeRejectedResponse(response, "the result"));
|
|
944
|
+
}
|
|
945
|
+
const integration = await applyAcceptedChange(
|
|
946
|
+
config.workspace,
|
|
947
|
+
assignment.base_revision,
|
|
948
|
+
result.head_revision,
|
|
949
|
+
);
|
|
950
|
+
if (integration.applied) {
|
|
951
|
+
console.log(
|
|
952
|
+
`Run accepted and applied to the connected repository at ${integration.revision}.`,
|
|
953
|
+
);
|
|
954
|
+
} else {
|
|
955
|
+
console.warn(
|
|
956
|
+
`Run accepted but not applied locally. ${integration.reason}`,
|
|
957
|
+
);
|
|
958
|
+
console.warn(
|
|
959
|
+
`The verified run workspace remains at ${result.run_workspace}.`,
|
|
960
|
+
);
|
|
961
|
+
}
|
|
962
|
+
} catch (error) {
|
|
963
|
+
const message = protocolFailureMessage(error);
|
|
964
|
+
if (!active?.cancelled && socket.readyState === WebSocket.OPEN) {
|
|
965
|
+
socket.send(
|
|
966
|
+
JSON.stringify({
|
|
967
|
+
type: "run.failed",
|
|
968
|
+
run_id: runId,
|
|
969
|
+
message,
|
|
970
|
+
}),
|
|
971
|
+
);
|
|
972
|
+
}
|
|
973
|
+
if (!active?.cancelled) console.error(message);
|
|
974
|
+
} finally {
|
|
975
|
+
clearInterval(heartbeat);
|
|
976
|
+
active = null;
|
|
977
|
+
pump();
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
function parseAgentArgs(value) {
|
|
982
|
+
if (!value) return [];
|
|
983
|
+
try {
|
|
984
|
+
const parsed = JSON.parse(value);
|
|
985
|
+
if (
|
|
986
|
+
!Array.isArray(parsed) ||
|
|
987
|
+
parsed.some((item) => typeof item !== "string")
|
|
988
|
+
) {
|
|
989
|
+
throw new Error();
|
|
990
|
+
}
|
|
991
|
+
return parsed;
|
|
992
|
+
} catch {
|
|
993
|
+
fail(
|
|
994
|
+
'--agent-args must be a JSON array, for example: --agent-args "[\\"acp\\"]"',
|
|
995
|
+
);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
function configuredAssessmentWorkerLimit(value) {
|
|
1000
|
+
try {
|
|
1001
|
+
return workspaceAssessmentWorkerLimit(value);
|
|
1002
|
+
} catch (error) {
|
|
1003
|
+
fail(error instanceof Error ? error.message : String(error));
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
function fail(message) {
|
|
1008
|
+
console.error(message);
|
|
1009
|
+
process.exit(1);
|
|
1010
|
+
}
|