@exaudeus/workrail 3.20.0 → 3.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -362,8 +362,14 @@ async function startAsyncServices() {
362
362
  const flags = tsyringe_1.container.resolve(tokens_js_1.DI.Infra.FeatureFlags);
363
363
  if (flags.isEnabled('sessionTools')) {
364
364
  const server = tsyringe_1.container.resolve(tokens_js_1.DI.Infra.HttpServer);
365
- await server.start();
366
- console.error('[DI] HTTP server started');
365
+ try {
366
+ await server.start();
367
+ console.error('[DI] HTTP server started');
368
+ }
369
+ catch (httpError) {
370
+ const message = httpError instanceof Error ? httpError.message : String(httpError);
371
+ console.error(`[DI] Dashboard HTTP server unavailable: ${message}. MCP tools will still work.`);
372
+ }
367
373
  }
368
374
  asyncInitialized = true;
369
375
  }
@@ -33,6 +33,7 @@ export declare class HttpServer {
33
33
  private isPrimary;
34
34
  private lockFile;
35
35
  private heartbeat;
36
+ private _stopPromise;
36
37
  constructor(sessionManager: SessionManager, processLifecyclePolicy: ProcessLifecyclePolicy, processSignals: ProcessSignals, shutdownEvents: ShutdownEvents, dashboardMode: DashboardMode, browserBehavior: BrowserBehavior);
37
38
  private config;
38
39
  setConfig(config: ServerConfig): this;
@@ -48,6 +49,7 @@ export declare class HttpServer {
48
49
  private printBanner;
49
50
  openDashboard(sessionId?: string): Promise<string>;
50
51
  stop(): Promise<void>;
52
+ private _runStop;
51
53
  mountRoutes(installer: (app: Application) => (() => void) | void): void;
52
54
  private readonly _routeDisposers;
53
55
  finalize(): void;
@@ -42,6 +42,7 @@ let HttpServer = class HttpServer {
42
42
  this.server = null;
43
43
  this.baseUrl = '';
44
44
  this.isPrimary = false;
45
+ this._stopPromise = null;
45
46
  this.config = {};
46
47
  this._routeDisposers = [];
47
48
  this.port = 3456;
@@ -331,6 +332,7 @@ let HttpServer = class HttpServer {
331
332
  });
332
333
  }
333
334
  async start() {
335
+ this._stopPromise = null;
334
336
  const mode = this.config.dashboardMode ?? this.dashboardMode;
335
337
  if (mode.kind === 'legacy') {
336
338
  console.error('[Dashboard] Unified dashboard disabled, using legacy mode');
@@ -556,6 +558,7 @@ let HttpServer = class HttpServer {
556
558
  throw error;
557
559
  }
558
560
  }
561
+ this.server = null;
559
562
  throw new Error('No available ports in range 3457-3499');
560
563
  }
561
564
  printBanner() {
@@ -570,6 +573,10 @@ let HttpServer = class HttpServer {
570
573
  console.error();
571
574
  }
572
575
  async openDashboard(sessionId) {
576
+ if (!this.baseUrl) {
577
+ throw new Error('Dashboard is unavailable -- the HTTP server did not start successfully ' +
578
+ '(likely due to port exhaustion). MCP tools still work normally.');
579
+ }
573
580
  let url = this.baseUrl;
574
581
  if (sessionId) {
575
582
  url += `?session=${sessionId}`;
@@ -587,6 +594,14 @@ let HttpServer = class HttpServer {
587
594
  return url;
588
595
  }
589
596
  async stop() {
597
+ if (this._stopPromise !== null)
598
+ return this._stopPromise;
599
+ if (this.server === null)
600
+ return Promise.resolve();
601
+ this._stopPromise = this._runStop();
602
+ return this._stopPromise;
603
+ }
604
+ async _runStop() {
590
605
  this.heartbeat.stop();
591
606
  this.sessionManager.unwatchAll();
592
607
  for (const dispose of this._routeDisposers) {
@@ -394,8 +394,8 @@
394
394
  "bytes": 620
395
395
  },
396
396
  "di/container.js": {
397
- "sha256": "6b5ca3f79bff1d6e3bbf9d19d76574611460d6a5f908ebe83fe202706fe4ae2c",
398
- "bytes": 21727
397
+ "sha256": "7956cb925d40f61746ccdcec245c5ee40d7d317893eff240a10cff595304d467",
398
+ "bytes": 22026
399
399
  },
400
400
  "di/tokens.d.ts": {
401
401
  "sha256": "04db6db34348aa36d897c85ab07eb1a386cb04e3595dbcb33525bd33974111ef",
@@ -542,12 +542,12 @@
542
542
  "bytes": 818
543
543
  },
544
544
  "infrastructure/session/HttpServer.d.ts": {
545
- "sha256": "e2676f55158fb35249fc6b8d8ae051e6b043f6445e153f1f20fe837acc2ab65c",
546
- "bytes": 1977
545
+ "sha256": "79c0d8855d9437084756186f5e115aab82c829f48bafc1a5742903a1e7d808b6",
546
+ "bytes": 2025
547
547
  },
548
548
  "infrastructure/session/HttpServer.js": {
549
- "sha256": "92c00fa2f82a503bdd35ce0b1d3665cc63a261d16be53ff82a7007a55b08994e",
550
- "bytes": 30162
549
+ "sha256": "3f2bc1fb56b8191da261567eb7fb8e78f4c3e33b54c71da361aeea51d047ccd8",
550
+ "bytes": 30743
551
551
  },
552
552
  "infrastructure/session/SessionDataNormalizer.d.ts": {
553
553
  "sha256": "c89bb5e00d7d01fb4aa6d0095602541de53c425c6b99b67fa8367eb29cb63e9e",
@@ -1102,12 +1102,12 @@
1102
1102
  "bytes": 3534
1103
1103
  },
1104
1104
  "mcp/transports/shutdown-hooks.d.ts": {
1105
- "sha256": "0a0e638aa9b1ca714e9c9efe9bcb9b147ef802f05c71ba6be481d59d3c45c800",
1106
- "bytes": 502
1105
+ "sha256": "abf3799e44183c8a7e3614e2f14c1d7c8c4bb4ce0b720d3005ee165e4dd6f211",
1106
+ "bytes": 547
1107
1107
  },
1108
1108
  "mcp/transports/shutdown-hooks.js": {
1109
- "sha256": "1bbf5e8203adc7172cb8a3a4cf987ded4e747a993cc6c9346651ff5f192b1abb",
1110
- "bytes": 2582
1109
+ "sha256": "b44d0daaabfc4a896a910691637473d238127157f762743fec35625b6d12d4eb",
1110
+ "bytes": 2701
1111
1111
  },
1112
1112
  "mcp/transports/stdio-entry.d.ts": {
1113
1113
  "sha256": "4ced3c9e00ef67555781dea74315290eea8a9dbffd38155bc00c3fb07b0c1794",
@@ -1,5 +1,6 @@
1
1
  export interface ShutdownHookOptions {
2
2
  readonly onBeforeTerminate: () => Promise<void>;
3
+ readonly stdout?: NodeJS.WritableStream;
3
4
  }
4
5
  export declare function wireShutdownHooks(opts: ShutdownHookOptions): void;
5
6
  export interface StdoutShutdownOptions {
@@ -9,6 +9,10 @@ function wireShutdownHooks(opts) {
9
9
  const shutdownEvents = container_js_1.container.resolve(tokens_js_1.DI.Runtime.ShutdownEvents);
10
10
  const processSignals = container_js_1.container.resolve(tokens_js_1.DI.Runtime.ProcessSignals);
11
11
  const terminator = container_js_1.container.resolve(tokens_js_1.DI.Runtime.ProcessTerminator);
12
+ const stdout = opts.stdout ?? process.stdout;
13
+ stdout.on('error', () => {
14
+ stdout.emit('drain');
15
+ });
12
16
  processSignals.on('SIGINT', () => shutdownEvents.emit({ kind: 'shutdown_requested', signal: 'SIGINT' }));
13
17
  processSignals.on('SIGTERM', () => shutdownEvents.emit({ kind: 'shutdown_requested', signal: 'SIGTERM' }));
14
18
  processSignals.on('SIGHUP', () => shutdownEvents.emit({ kind: 'shutdown_requested', signal: 'SIGHUP' }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exaudeus/workrail",
3
- "version": "3.20.0",
3
+ "version": "3.20.1",
4
4
  "description": "Step-by-step workflow enforcement for AI agents via MCP",
5
5
  "license": "MIT",
6
6
  "repository": {