@exaudeus/workrail 3.19.1 → 3.20.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.
@@ -4,8 +4,8 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>WorkRail Console</title>
7
- <script type="module" crossorigin src="/console/assets/index-QhCFuxQV.js"></script>
8
- <link rel="stylesheet" crossorigin href="/console/assets/index-ibLhWBmX.css">
7
+ <script type="module" crossorigin src="/console/assets/index-De5T1Dpm.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/console/assets/index-B8NW82uK.css">
9
9
  </head>
10
10
  <body>
11
11
  <div id="root"></div>
@@ -369,16 +369,16 @@
369
369
  "sha256": "5fe866e54f796975dec5d8ba9983aefd86074db212d3fccd64eed04bc9f0b3da",
370
370
  "bytes": 8011
371
371
  },
372
- "console/assets/index-QhCFuxQV.js": {
373
- "sha256": "0096a86267fdf969abc3e3976ca44b3d3447271c7394345c8d6c27c084e6412c",
374
- "bytes": 719937
372
+ "console/assets/index-B8NW82uK.css": {
373
+ "sha256": "8588d6702df899c724f5abce3e45dd4a6f7ee06bf2b280cb3909f9cabcc0a32c",
374
+ "bytes": 58801
375
375
  },
376
- "console/assets/index-ibLhWBmX.css": {
377
- "sha256": "346e55635d4d3dc2836dae83edb8563872721bf4b0e7e1ecf47fb9603424c206",
378
- "bytes": 58826
376
+ "console/assets/index-De5T1Dpm.js": {
377
+ "sha256": "bad5fc857501a1253c800b6863e95cc854f3c8a52fea80959306ba32e15fd907",
378
+ "bytes": 721395
379
379
  },
380
380
  "console/index.html": {
381
- "sha256": "4232baa5128b860125a649d8e135da858f07290bbb9da04861945649b53ac85d",
381
+ "sha256": "3165437d9c2abc907ab1333dde87b0064e7c8fba62c3ffd2567e8bc02e75329b",
382
382
  "bytes": 417
383
383
  },
384
384
  "core/error-handler.d.ts": {
@@ -1102,20 +1102,20 @@
1102
1102
  "bytes": 3534
1103
1103
  },
1104
1104
  "mcp/transports/shutdown-hooks.d.ts": {
1105
- "sha256": "956a334d06ba7b967992e1ebfef67e2bac1e40cf3c1c151684d5aa7a3166f64e",
1106
- "bytes": 334
1105
+ "sha256": "0a0e638aa9b1ca714e9c9efe9bcb9b147ef802f05c71ba6be481d59d3c45c800",
1106
+ "bytes": 502
1107
1107
  },
1108
1108
  "mcp/transports/shutdown-hooks.js": {
1109
- "sha256": "5f5e1411db09d05588731bbaa08fc42db48a9023e87043e95aa01cd8ad426191",
1110
- "bytes": 1937
1109
+ "sha256": "1bbf5e8203adc7172cb8a3a4cf987ded4e747a993cc6c9346651ff5f192b1abb",
1110
+ "bytes": 2582
1111
1111
  },
1112
1112
  "mcp/transports/stdio-entry.d.ts": {
1113
1113
  "sha256": "4ced3c9e00ef67555781dea74315290eea8a9dbffd38155bc00c3fb07b0c1794",
1114
1114
  "bytes": 59
1115
1115
  },
1116
1116
  "mcp/transports/stdio-entry.js": {
1117
- "sha256": "aa2f863ee779a0f31c729e54d929dfd5cf416424f63044f8d0eda956bdcc9b9e",
1118
- "bytes": 3702
1117
+ "sha256": "d934d0a7bb5a5d4331ce424aa754c063dac0a2f914d6b9d5f75489740d325b0a",
1118
+ "bytes": 4039
1119
1119
  },
1120
1120
  "mcp/transports/transport-mode.d.ts": {
1121
1121
  "sha256": "1c59128ab0174bd2a113fff17521e6339ca367f2b8980c2f2c164ec393c10518",
@@ -2278,12 +2278,12 @@
2278
2278
  "bytes": 7393
2279
2279
  },
2280
2280
  "v2/infra/local/session-lock/index.d.ts": {
2281
- "sha256": "3b381b372da6039df678dfddf377600547e1067cf921cc1b4cdf578e5df0a1bb",
2282
- "bytes": 853
2281
+ "sha256": "e180a4202587af03e09dc82e2d8557ee2d3139ac6c5f1edee9a277a7032b9986",
2282
+ "bytes": 883
2283
2283
  },
2284
2284
  "v2/infra/local/session-lock/index.js": {
2285
- "sha256": "3ad09372cb30d959016cc2a69e91de7c7f3a251c22048a917d9862fd0d8a77f9",
2286
- "bytes": 1700
2285
+ "sha256": "a1cd3eaea35faa014922fe1502a9e029a58528421586c67ab44dc7df0183a0fe",
2286
+ "bytes": 2793
2287
2287
  },
2288
2288
  "v2/infra/local/session-store/index.d.ts": {
2289
2289
  "sha256": "33b18ccce92374f8e444a61c63238634087d222f09e834b49dd2ea38d2be3796",
@@ -2,6 +2,10 @@ export interface ShutdownHookOptions {
2
2
  readonly onBeforeTerminate: () => Promise<void>;
3
3
  }
4
4
  export declare function wireShutdownHooks(opts: ShutdownHookOptions): void;
5
+ export interface StdoutShutdownOptions {
6
+ readonly stdout?: NodeJS.WritableStream;
7
+ }
8
+ export declare function wireStdoutShutdown(opts?: StdoutShutdownOptions): void;
5
9
  export interface StdinShutdownOptions {
6
10
  readonly stdin?: NodeJS.ReadableStream;
7
11
  }
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.wireShutdownHooks = wireShutdownHooks;
4
+ exports.wireStdoutShutdown = wireStdoutShutdown;
4
5
  exports.wireStdinShutdown = wireStdinShutdown;
5
6
  const container_js_1 = require("../../di/container.js");
6
7
  const tokens_js_1 = require("../../di/tokens.js");
@@ -29,6 +30,20 @@ function wireShutdownHooks(opts) {
29
30
  })();
30
31
  });
31
32
  }
33
+ function wireStdoutShutdown(opts) {
34
+ const shutdownEvents = container_js_1.container.resolve(tokens_js_1.DI.Runtime.ShutdownEvents);
35
+ const stdout = opts?.stdout ?? process.stdout;
36
+ stdout.on('error', (err) => {
37
+ const code = err.code;
38
+ if (code === 'EPIPE' || code === 'ERR_STREAM_DESTROYED') {
39
+ console.error('[MCP] stdout pipe broken (client disconnected), initiating shutdown');
40
+ }
41
+ else {
42
+ console.error('[MCP] stdout error:', err);
43
+ }
44
+ shutdownEvents.emit({ kind: 'shutdown_requested', signal: 'SIGHUP' });
45
+ });
46
+ }
32
47
  function wireStdinShutdown(opts) {
33
48
  const shutdownEvents = container_js_1.container.resolve(tokens_js_1.DI.Runtime.ShutdownEvents);
34
49
  const stdin = opts?.stdin ?? process.stdin;
@@ -46,6 +46,13 @@ async function fetchInitialRootsWithTimeout(server) {
46
46
  ]);
47
47
  }
48
48
  async function startStdioServer() {
49
+ process.on('uncaughtException', (err) => {
50
+ console.error('[MCP] Uncaught exception -- process will exit:', err);
51
+ });
52
+ process.on('unhandledRejection', (reason) => {
53
+ console.error('[MCP] Unhandled promise rejection:', reason);
54
+ process.exit(1);
55
+ });
49
56
  const { server, ctx, rootsManager } = await (0, server_js_1.composeServer)();
50
57
  const { StdioServerTransport } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/server/stdio.js')));
51
58
  const { RootsListChangedNotificationSchema, } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/types.js')));
@@ -59,6 +66,7 @@ async function startStdioServer() {
59
66
  console.error('[Roots] Failed to fetch updated roots after change notification');
60
67
  }
61
68
  });
69
+ (0, shutdown_hooks_js_1.wireStdoutShutdown)();
62
70
  const transport = new StdioServerTransport();
63
71
  await server.connect(transport);
64
72
  console.error('[Transport] WorkRail MCP Server running on stdio');
@@ -9,6 +9,7 @@ export declare class LocalSessionLockV2 implements SessionLockPortV2 {
9
9
  private readonly fs;
10
10
  private readonly clock;
11
11
  constructor(dataDir: DataDirPortV2, fs: FileSystemPortV2, clock: TimeClockPortV2);
12
+ private clearIfStaleLock;
12
13
  acquire(sessionId: SessionId): ResultAsync<SessionLockHandleV2, SessionLockError>;
13
14
  release(handle: SessionLockHandleV2): ResultAsync<void, SessionLockError>;
14
15
  }
@@ -1,12 +1,42 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LocalSessionLockV2 = void 0;
4
+ const neverthrow_1 = require("neverthrow");
4
5
  class LocalSessionLockV2 {
5
6
  constructor(dataDir, fs, clock) {
6
7
  this.dataDir = dataDir;
7
8
  this.fs = fs;
8
9
  this.clock = clock;
9
10
  }
11
+ clearIfStaleLock(lockPath) {
12
+ return this.fs
13
+ .readFileUtf8(lockPath)
14
+ .map((content) => {
15
+ try {
16
+ const data = JSON.parse(content);
17
+ const pid = typeof data.pid === 'number' ? data.pid : null;
18
+ if (pid === null)
19
+ return false;
20
+ try {
21
+ process.kill(pid, 0);
22
+ return false;
23
+ }
24
+ catch (e) {
25
+ return e.code === 'ESRCH';
26
+ }
27
+ }
28
+ catch {
29
+ return false;
30
+ }
31
+ })
32
+ .andThen((isStale) => {
33
+ if (!isStale)
34
+ return (0, neverthrow_1.okAsync)(undefined);
35
+ console.error(`[SessionLock] Removing stale lock at ${lockPath} (process no longer alive)`);
36
+ return this.fs.unlink(lockPath);
37
+ })
38
+ .orElse(() => (0, neverthrow_1.okAsync)(undefined));
39
+ }
10
40
  acquire(sessionId) {
11
41
  const sessionDir = this.dataDir.sessionDir(sessionId);
12
42
  const lockPath = this.dataDir.sessionLockPath(sessionId);
@@ -23,6 +53,7 @@ class LocalSessionLockV2 {
23
53
  };
24
54
  return this.fs
25
55
  .mkdirp(sessionDir)
56
+ .andThen(() => this.clearIfStaleLock(lockPath))
26
57
  .andThen(() => this.fs.openExclusive(lockPath, new TextEncoder().encode(JSON.stringify({
27
58
  v: 1,
28
59
  sessionId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exaudeus/workrail",
3
- "version": "3.19.1",
3
+ "version": "3.20.0",
4
4
  "description": "Step-by-step workflow enforcement for AI agents via MCP",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -47,6 +47,8 @@
47
47
  "validate:workflow-discovery": "node scripts/validate-workflow-discovery.js",
48
48
  "precommit": "npm run validate:registry",
49
49
  "preinstall": "node -e \"const v=parseInt(process.versions.node.split('.')[0],10); if(v<20){console.error('WorkRail requires Node.js >=20. Current: '+process.versions.node+'\\nPlease upgrade: https://nodejs.org/'); process.exit(1);}\"",
50
+ "dev:mcp": "pkill -f 'dist/mcp-server.js' 2>/dev/null; sleep 0.5; WORKRAIL_TRANSPORT=http WORKRAIL_ENABLE_SESSION_TOOLS=true node dist/mcp-server.js",
51
+ "dev:mcp:watch": "WORKRAIL_TRANSPORT=http WORKRAIL_ENABLE_SESSION_TOOLS=true nodemon --watch dist --ext js --delay 2 --exec 'node dist/mcp-server.js'",
50
52
  "web:dev": "npm run build && WORKRAIL_ENABLE_SESSION_TOOLS=true node dist/mcp-server.js",
51
53
  "web:ci": "WORKRAIL_ENABLE_SESSION_TOOLS=true node dist/mcp-server.js",
52
54
  "web:typecheck": "tsc -p tsconfig.web.json",
@@ -113,17 +115,20 @@
113
115
  "@semantic-release/exec": "^7.1.0",
114
116
  "@semantic-release/git": "^10.0.1",
115
117
  "@semantic-release/github": "^12.0.2",
118
+ "@testing-library/react": "^16.3.2",
119
+ "@testing-library/user-event": "^14.6.1",
116
120
  "@types/cors": "^2.8.19",
117
121
  "@types/express": "^5.0.3",
118
122
  "@types/node": "^20.19.9",
119
123
  "@types/semver": "^7.7.0",
120
124
  "@vitest/ui": "^3.2.4",
121
125
  "conventional-changelog-conventionalcommits": "^9.1.0",
122
- "fast-check": "^3.15.0",
126
+ "fast-check": "^4.6.0",
123
127
  "happy-dom": "^20.0.11",
124
- "jsdom": "^27.0.0",
128
+ "jsdom": "^29.0.2",
125
129
  "lit": "^3.3.1",
126
130
  "node-fetch": "^3.3.2",
131
+ "nodemon": "^3.0.0",
127
132
  "semantic-release": "^25.0.3",
128
133
  "ts-morph": "^27.0.2",
129
134
  "typescript": "^5.9.3",