edsger 0.72.5 → 0.72.6

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/dist/index.js CHANGED
@@ -58,7 +58,7 @@ import { runWorkflow } from './commands/workflow/index.js';
58
58
  import { DEFAULT_MAX_FILES as FIND_ARCHITECTURE_DEFAULT_MAX_FILES } from './phases/find-architecture/index.js';
59
59
  import { DEFAULT_MAX_FILES as FIND_SMELLS_DEFAULT_MAX_FILES } from './phases/find-smells/index.js';
60
60
  import { SMELL_CATEGORIES, } from './phases/find-smells/types.js';
61
- import { deregisterSession, registerSession, } from './system/session-manager.js';
61
+ import { deregisterSession, registerSession, startHeartbeat, } from './system/session-manager.js';
62
62
  import { logError, logInfo } from './utils/logger.js';
63
63
  // Get package.json version dynamically
64
64
  // eslint-disable-next-line @typescript-eslint/naming-convention -- ESM __filename/__dirname polyfill
@@ -89,6 +89,10 @@ program
89
89
  program.hook('preAction', async (_thisCommand, actionCommand) => {
90
90
  if (process.env.EDSGER_PROCESS_KEY) {
91
91
  await registerSession({ command: actionCommand.name() });
92
+ // Keep last_heartbeat fresh while the run is alive, so consumers of the
93
+ // cli_sessions row (the desktop's tab views) can tell a live session from
94
+ // a stale one left behind by a crash / SIGKILL.
95
+ startHeartbeat();
92
96
  }
93
97
  });
94
98
  program.hook('postAction', async () => {
@@ -96,6 +100,46 @@ program.hook('postAction', async () => {
96
100
  await deregisterSession();
97
101
  }
98
102
  });
103
+ /**
104
+ * Mark the session row stopped (bounded, best-effort) and exit non-zero.
105
+ *
106
+ * Every command's catch block used to call `process.exit(1)` directly, which
107
+ * skips the postAction hook above — the cli_sessions row stayed 'running'
108
+ * forever and the desktop kept showing a ghost in-progress run that no Stop
109
+ * button could clear.
110
+ */
111
+ function exitWithError(error) {
112
+ logError(error instanceof Error ? error.message : String(error));
113
+ void Promise.race([
114
+ deregisterSession(),
115
+ new Promise((resolve) => {
116
+ setTimeout(resolve, 3000);
117
+ }),
118
+ ]).finally(() => process.exit(1));
119
+ }
120
+ // The desktop's Stop button sends SIGTERM (escalating to SIGKILL after 5s).
121
+ // Without a handler the process dies with its cli_sessions row still
122
+ // 'running' — same ghost-run problem as the error path above. Finalize the
123
+ // row (bounded) before exiting. Commands that install their own handlers
124
+ // (build, agent-workflow, task-worker, chat-serve, ...) own the process
125
+ // lifetime; in that case only the session row is finalized here.
126
+ for (const signal of ['SIGTERM', 'SIGINT']) {
127
+ const onTermination = () => {
128
+ const finalize = Promise.race([
129
+ deregisterSession(),
130
+ new Promise((resolve) => {
131
+ setTimeout(resolve, 3000);
132
+ }),
133
+ ]);
134
+ const others = process
135
+ .listeners(signal)
136
+ .filter((listener) => listener !== onTermination);
137
+ if (others.length === 0) {
138
+ void finalize.finally(() => process.exit(signal === 'SIGINT' ? 130 : 143));
139
+ }
140
+ };
141
+ process.on(signal, onTermination);
142
+ }
99
143
  // ============================================================
100
144
  // Subcommand: edsger login
101
145
  // ============================================================
@@ -107,8 +151,7 @@ program
107
151
  await runLogin();
108
152
  }
109
153
  catch (error) {
110
- logError(error instanceof Error ? error.message : String(error));
111
- process.exit(1);
154
+ exitWithError(error);
112
155
  }
113
156
  });
114
157
  // ============================================================
@@ -122,8 +165,7 @@ program
122
165
  runLogout();
123
166
  }
124
167
  catch (error) {
125
- logError(error instanceof Error ? error.message : String(error));
126
- process.exit(1);
168
+ exitWithError(error);
127
169
  }
128
170
  });
129
171
  // ============================================================
@@ -137,8 +179,7 @@ program
137
179
  runStatus();
138
180
  }
139
181
  catch (error) {
140
- logError(error instanceof Error ? error.message : String(error));
141
- process.exit(1);
182
+ exitWithError(error);
142
183
  }
143
184
  });
144
185
  // ============================================================
@@ -190,8 +231,7 @@ program
190
231
  });
191
232
  }
192
233
  catch (error) {
193
- logError(error instanceof Error ? error.message : String(error));
194
- process.exit(1);
234
+ exitWithError(error);
195
235
  }
196
236
  });
197
237
  // ============================================================
@@ -217,8 +257,7 @@ program
217
257
  });
218
258
  }
219
259
  catch (error) {
220
- logError(error instanceof Error ? error.message : String(error));
221
- process.exit(1);
260
+ exitWithError(error);
222
261
  }
223
262
  });
224
263
  // ============================================================
@@ -244,8 +283,7 @@ program
244
283
  });
245
284
  }
246
285
  catch (error) {
247
- logError(error instanceof Error ? error.message : String(error));
248
- process.exit(1);
286
+ exitWithError(error);
249
287
  }
250
288
  });
251
289
  // ============================================================
@@ -271,8 +309,7 @@ program
271
309
  });
272
310
  }
273
311
  catch (error) {
274
- logError(error instanceof Error ? error.message : String(error));
275
- process.exit(1);
312
+ exitWithError(error);
276
313
  }
277
314
  });
278
315
  // ============================================================
@@ -298,8 +335,7 @@ program
298
335
  });
299
336
  }
300
337
  catch (error) {
301
- logError(error instanceof Error ? error.message : String(error));
302
- process.exit(1);
338
+ exitWithError(error);
303
339
  }
304
340
  });
305
341
  const diagramSubcommands = [
@@ -349,8 +385,7 @@ for (const sub of diagramSubcommands) {
349
385
  });
350
386
  }
351
387
  catch (error) {
352
- logError(error instanceof Error ? error.message : String(error));
353
- process.exit(1);
388
+ exitWithError(error);
354
389
  }
355
390
  });
356
391
  }
@@ -373,8 +408,7 @@ program
373
408
  });
374
409
  }
375
410
  catch (error) {
376
- logError(error instanceof Error ? error.message : String(error));
377
- process.exit(1);
411
+ exitWithError(error);
378
412
  }
379
413
  });
380
414
  // ============================================================
@@ -394,8 +428,7 @@ program
394
428
  });
395
429
  }
396
430
  catch (error) {
397
- logError(error instanceof Error ? error.message : String(error));
398
- process.exit(1);
431
+ exitWithError(error);
399
432
  }
400
433
  });
401
434
  // ============================================================
@@ -425,8 +458,7 @@ program
425
458
  });
426
459
  }
427
460
  catch (error) {
428
- logError(error instanceof Error ? error.message : String(error));
429
- process.exit(1);
461
+ exitWithError(error);
430
462
  }
431
463
  });
432
464
  // ============================================================
@@ -452,8 +484,7 @@ program
452
484
  });
453
485
  }
454
486
  catch (error) {
455
- logError(error instanceof Error ? error.message : String(error));
456
- process.exit(1);
487
+ exitWithError(error);
457
488
  }
458
489
  });
459
490
  // ============================================================
@@ -487,8 +518,7 @@ program
487
518
  });
488
519
  }
489
520
  catch (error) {
490
- logError(error instanceof Error ? error.message : String(error));
491
- process.exit(1);
521
+ exitWithError(error);
492
522
  }
493
523
  });
494
524
  // ============================================================
@@ -518,8 +548,7 @@ program
518
548
  });
519
549
  }
520
550
  catch (error) {
521
- logError(error instanceof Error ? error.message : String(error));
522
- process.exit(1);
551
+ exitWithError(error);
523
552
  }
524
553
  });
525
554
  // ============================================================
@@ -537,8 +566,7 @@ program
537
566
  });
538
567
  }
539
568
  catch (error) {
540
- logError(error instanceof Error ? error.message : String(error));
541
- process.exit(1);
569
+ exitWithError(error);
542
570
  }
543
571
  });
544
572
  // ============================================================
@@ -556,8 +584,7 @@ program
556
584
  });
557
585
  }
558
586
  catch (error) {
559
- logError(error instanceof Error ? error.message : String(error));
560
- process.exit(1);
587
+ exitWithError(error);
561
588
  }
562
589
  });
563
590
  // ============================================================
@@ -572,8 +599,7 @@ program
572
599
  await runIssueAnalysisCommand({ issueId, verbose: opts.verbose });
573
600
  }
574
601
  catch (error) {
575
- logError(error instanceof Error ? error.message : String(error));
576
- process.exit(1);
602
+ exitWithError(error);
577
603
  }
578
604
  });
579
605
  // ============================================================
@@ -605,8 +631,7 @@ program
605
631
  });
606
632
  }
607
633
  catch (error) {
608
- logError(error instanceof Error ? error.message : String(error));
609
- process.exit(1);
634
+ exitWithError(error);
610
635
  }
611
636
  });
612
637
  // ============================================================
@@ -633,8 +658,7 @@ program
633
658
  });
634
659
  }
635
660
  catch (error) {
636
- logError(error instanceof Error ? error.message : String(error));
637
- process.exit(1);
661
+ exitWithError(error);
638
662
  }
639
663
  });
640
664
  // ============================================================
@@ -649,8 +673,7 @@ program
649
673
  await runChatServeCommand({ verbose: opts.verbose });
650
674
  }
651
675
  catch (error) {
652
- logError(error instanceof Error ? error.message : String(error));
653
- process.exit(1);
676
+ exitWithError(error);
654
677
  }
655
678
  });
656
679
  // ============================================================
@@ -665,8 +688,7 @@ program
665
688
  await runUserStoriesAnalysisCommand({ issueId, verbose: opts.verbose });
666
689
  }
667
690
  catch (error) {
668
- logError(error instanceof Error ? error.message : String(error));
669
- process.exit(1);
691
+ exitWithError(error);
670
692
  }
671
693
  });
672
694
  // ============================================================
@@ -681,8 +703,7 @@ program
681
703
  await runTestCasesAnalysisCommand({ issueId, verbose: opts.verbose });
682
704
  }
683
705
  catch (error) {
684
- logError(error instanceof Error ? error.message : String(error));
685
- process.exit(1);
706
+ exitWithError(error);
686
707
  }
687
708
  });
688
709
  // ============================================================
@@ -697,8 +718,7 @@ program
697
718
  await runTechnicalDesignCommand({ issueId, verbose: opts.verbose });
698
719
  }
699
720
  catch (error) {
700
- logError(error instanceof Error ? error.message : String(error));
701
- process.exit(1);
721
+ exitWithError(error);
702
722
  }
703
723
  });
704
724
  // ============================================================
@@ -716,8 +736,7 @@ program
716
736
  });
717
737
  }
718
738
  catch (error) {
719
- logError(error instanceof Error ? error.message : String(error));
720
- process.exit(1);
739
+ exitWithError(error);
721
740
  }
722
741
  });
723
742
  // ============================================================
@@ -737,8 +756,7 @@ program
737
756
  });
738
757
  }
739
758
  catch (error) {
740
- logError(error instanceof Error ? error.message : String(error));
741
- process.exit(1);
759
+ exitWithError(error);
742
760
  }
743
761
  });
744
762
  // ============================================================
@@ -755,8 +773,7 @@ program
755
773
  await runPRReview(productId, opts);
756
774
  }
757
775
  catch (error) {
758
- logError(error instanceof Error ? error.message : String(error));
759
- process.exit(1);
776
+ exitWithError(error);
760
777
  }
761
778
  });
762
779
  // ============================================================
@@ -780,8 +797,7 @@ program
780
797
  await runFindBugs(productId, opts);
781
798
  }
782
799
  catch (error) {
783
- logError(error instanceof Error ? error.message : String(error));
784
- process.exit(1);
800
+ exitWithError(error);
785
801
  }
786
802
  });
787
803
  // ============================================================
@@ -807,8 +823,7 @@ program
807
823
  await runQualityBenchmarkCli(productId ?? '', opts);
808
824
  }
809
825
  catch (error) {
810
- logError(error instanceof Error ? error.message : String(error));
811
- process.exit(1);
826
+ exitWithError(error);
812
827
  }
813
828
  });
814
829
  // ============================================================
@@ -823,8 +838,7 @@ program
823
838
  await runSyncGithubIssues(productId, opts);
824
839
  }
825
840
  catch (error) {
826
- logError(error instanceof Error ? error.message : String(error));
827
- process.exit(1);
841
+ exitWithError(error);
828
842
  }
829
843
  });
830
844
  // ============================================================
@@ -840,8 +854,7 @@ program
840
854
  await runSyncOrgRepos(teamId, opts);
841
855
  }
842
856
  catch (error) {
843
- logError(error instanceof Error ? error.message : String(error));
844
- process.exit(1);
857
+ exitWithError(error);
845
858
  }
846
859
  });
847
860
  // ============================================================
@@ -859,8 +872,7 @@ program
859
872
  await runSyncSentryIssues(productId, opts);
860
873
  }
861
874
  catch (error) {
862
- logError(error instanceof Error ? error.message : String(error));
863
- process.exit(1);
875
+ exitWithError(error);
864
876
  }
865
877
  });
866
878
  // ============================================================
@@ -878,8 +890,7 @@ program
878
890
  await runSyncDatadog(teamId, opts);
879
891
  }
880
892
  catch (error) {
881
- logError(error instanceof Error ? error.message : String(error));
882
- process.exit(1);
893
+ exitWithError(error);
883
894
  }
884
895
  });
885
896
  // ============================================================
@@ -904,8 +915,7 @@ program
904
915
  });
905
916
  }
906
917
  catch (error) {
907
- logError(error instanceof Error ? error.message : String(error));
908
- process.exit(1);
918
+ exitWithError(error);
909
919
  }
910
920
  });
911
921
  // ============================================================
@@ -925,8 +935,7 @@ program
925
935
  await runDiscover(teamId, runId, opts);
926
936
  }
927
937
  catch (error) {
928
- logError(error instanceof Error ? error.message : String(error));
929
- process.exit(1);
938
+ exitWithError(error);
930
939
  }
931
940
  });
932
941
  // Subcommand: edsger sync-terraform <teamId>
@@ -945,8 +954,7 @@ program
945
954
  await runSyncTerraform(teamId, opts);
946
955
  }
947
956
  catch (error) {
948
- logError(error instanceof Error ? error.message : String(error));
949
- process.exit(1);
957
+ exitWithError(error);
950
958
  }
951
959
  });
952
960
  // ============================================================
@@ -978,8 +986,7 @@ program
978
986
  await runFindFeatures(productId, opts);
979
987
  }
980
988
  catch (error) {
981
- logError(error instanceof Error ? error.message : String(error));
982
- process.exit(1);
989
+ exitWithError(error);
983
990
  }
984
991
  });
985
992
  // ============================================================
@@ -1004,8 +1011,7 @@ program
1004
1011
  await runFindSmells(productId, opts);
1005
1012
  }
1006
1013
  catch (error) {
1007
- logError(error instanceof Error ? error.message : String(error));
1008
- process.exit(1);
1014
+ exitWithError(error);
1009
1015
  }
1010
1016
  });
1011
1017
  // ============================================================
@@ -1029,8 +1035,7 @@ program
1029
1035
  await runFindArchitecture(productId, opts);
1030
1036
  }
1031
1037
  catch (error) {
1032
- logError(error instanceof Error ? error.message : String(error));
1033
- process.exit(1);
1038
+ exitWithError(error);
1034
1039
  }
1035
1040
  });
1036
1041
  // ============================================================
@@ -1046,8 +1051,7 @@ program
1046
1051
  await runProductTestCases(productId, opts);
1047
1052
  }
1048
1053
  catch (error) {
1049
- logError(error instanceof Error ? error.message : String(error));
1050
- process.exit(1);
1054
+ exitWithError(error);
1051
1055
  }
1052
1056
  });
1053
1057
  // ============================================================
@@ -1073,8 +1077,7 @@ program
1073
1077
  });
1074
1078
  }
1075
1079
  catch (error) {
1076
- logError(error instanceof Error ? error.message : String(error));
1077
- process.exit(1);
1080
+ exitWithError(error);
1078
1081
  }
1079
1082
  });
1080
1083
  // ============================================================
@@ -1092,8 +1095,7 @@ program
1092
1095
  await runPRResolve(productId, opts);
1093
1096
  }
1094
1097
  catch (error) {
1095
- logError(error instanceof Error ? error.message : String(error));
1096
- process.exit(1);
1098
+ exitWithError(error);
1097
1099
  }
1098
1100
  });
1099
1101
  // ============================================================
@@ -1117,16 +1119,15 @@ program.action(async (options, command) => {
1117
1119
  // bugs look like the agent inexplicably running with the wrong intent.
1118
1120
  // Treat any leftover positional arg as an unknown command and abort.
1119
1121
  if (command.args.length > 0) {
1120
- logError(`Unknown command: ${command.args[0]}`);
1121
1122
  logError(`Run 'edsger --help' to see available commands.`);
1122
- process.exit(1);
1123
+ exitWithError(new Error(`Unknown command: ${command.args[0]}`));
1124
+ return;
1123
1125
  }
1124
1126
  try {
1125
1127
  await runEdsger(options);
1126
1128
  }
1127
1129
  catch (error) {
1128
- logError(error instanceof Error ? error.message : String(error));
1129
- process.exit(1);
1130
+ exitWithError(error);
1130
1131
  }
1131
1132
  });
1132
1133
  export const runEdsger = async (options) => {
@@ -215,11 +215,16 @@ export async function sendHeartbeat(currentIssueId, currentIssueName) {
215
215
  * Start periodic heartbeats
216
216
  */
217
217
  export function startHeartbeat() {
218
+ if (heartbeatTimer) {
219
+ return;
220
+ }
218
221
  heartbeatTimer = setInterval(() => {
219
222
  sendHeartbeat().catch(() => {
220
223
  /* noop */
221
224
  });
222
225
  }, HEARTBEAT_INTERVAL);
226
+ // The heartbeat alone must never keep a finished command's process alive.
227
+ heartbeatTimer.unref?.();
223
228
  }
224
229
  /**
225
230
  * Stop periodic heartbeats
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "edsger",
3
- "version": "0.72.5",
3
+ "version": "0.72.6",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "edsger": "dist/index.js"