@exaudeus/workrail 3.24.0 → 3.24.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.
- package/dist/console/assets/index-BXRk3te_.css +1 -0
- package/dist/console/assets/{index-CWETdPGj.js → index-TMfptYpQ.js} +9 -9
- package/dist/console/index.html +2 -2
- package/dist/di/container.js +1 -1
- package/dist/infrastructure/session/HttpServer.js +11 -0
- package/dist/manifest.json +31 -23
- package/dist/mcp/transports/bridge-entry.d.ts +1 -0
- package/dist/mcp/transports/bridge-entry.js +93 -0
- package/dist/mcp-server.d.ts +1 -0
- package/dist/mcp-server.js +48 -16
- package/dist/v2/infra/local/session-lock/index.d.ts +3 -1
- package/dist/v2/infra/local/session-lock/index.js +18 -1
- package/dist/v2/usecases/console-service.js +71 -1
- package/dist/v2/usecases/console-types.d.ts +5 -0
- package/package.json +1 -1
- package/workflows/mercury-android.clickstream-implementation.json +205 -0
- package/workflows/mercury-ios.clickstream-implementation.json +193 -0
- package/workflows/rich-object-contribution.json +258 -0
- package/dist/console/assets/index-ByW7d9qr.css +0 -1
package/dist/console/index.html
CHANGED
|
@@ -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-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/console/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/console/assets/index-TMfptYpQ.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/console/assets/index-BXRk3te_.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
package/dist/di/container.js
CHANGED
|
@@ -303,7 +303,7 @@ async function registerV2Services() {
|
|
|
303
303
|
const dataDir = c.resolve(tokens_js_1.DI.V2.DataDir);
|
|
304
304
|
const fs = c.resolve(tokens_js_1.DI.V2.FileSystem);
|
|
305
305
|
const clock = c.resolve(tokens_js_1.DI.V2.TimeClock);
|
|
306
|
-
return new LocalSessionLockV2(dataDir, fs, clock);
|
|
306
|
+
return new LocalSessionLockV2(dataDir, fs, clock, 'mcp-server');
|
|
307
307
|
}),
|
|
308
308
|
});
|
|
309
309
|
const { ExecutionSessionGateV2 } = await Promise.resolve().then(() => __importStar(require('../v2/usecases/execution-session-gate.js')));
|
|
@@ -392,6 +392,17 @@ let HttpServer = class HttpServer {
|
|
|
392
392
|
if (!lockData.pid || !lockData.port || !lockData.startedAt) {
|
|
393
393
|
return { reclaim: true, reason: 'invalid lock structure' };
|
|
394
394
|
}
|
|
395
|
+
if (lockData.projectId) {
|
|
396
|
+
const currentProjectId = this.sessionManager.getProjectId();
|
|
397
|
+
if (lockData.projectId !== currentProjectId) {
|
|
398
|
+
try {
|
|
399
|
+
process.kill(lockData.pid, 0);
|
|
400
|
+
return { reclaim: false, reason: `different project, primary alive (lock=${lockData.projectId}, current=${currentProjectId})` };
|
|
401
|
+
}
|
|
402
|
+
catch {
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
395
406
|
if (lockData.version !== CURRENT_VERSION) {
|
|
396
407
|
return { reclaim: true, reason: `version mismatch (lock=${lockData.version}, current=${CURRENT_VERSION})` };
|
|
397
408
|
}
|
package/dist/manifest.json
CHANGED
|
@@ -369,16 +369,16 @@
|
|
|
369
369
|
"sha256": "5fe866e54f796975dec5d8ba9983aefd86074db212d3fccd64eed04bc9f0b3da",
|
|
370
370
|
"bytes": 8011
|
|
371
371
|
},
|
|
372
|
-
"console/assets/index-
|
|
373
|
-
"sha256": "
|
|
374
|
-
"bytes":
|
|
372
|
+
"console/assets/index-BXRk3te_.css": {
|
|
373
|
+
"sha256": "2151b10aedd5da99f5bac5cdb54c4cb73894b9069eab0fb859db50b9aa71fba2",
|
|
374
|
+
"bytes": 59906
|
|
375
375
|
},
|
|
376
|
-
"console/assets/index-
|
|
377
|
-
"sha256": "
|
|
378
|
-
"bytes":
|
|
376
|
+
"console/assets/index-TMfptYpQ.js": {
|
|
377
|
+
"sha256": "475c71c4cca93f8ed30139569638b9ec272b43eb44feccb4ce08c6f7e2064dc7",
|
|
378
|
+
"bytes": 744551
|
|
379
379
|
},
|
|
380
380
|
"console/index.html": {
|
|
381
|
-
"sha256": "
|
|
381
|
+
"sha256": "1ff870763f5df29998d56597a3b138e07e23b8fb054c93f6ce9f7f5ae4a0c9a5",
|
|
382
382
|
"bytes": 417
|
|
383
383
|
},
|
|
384
384
|
"core/error-handler.d.ts": {
|
|
@@ -394,8 +394,8 @@
|
|
|
394
394
|
"bytes": 620
|
|
395
395
|
},
|
|
396
396
|
"di/container.js": {
|
|
397
|
-
"sha256": "
|
|
398
|
-
"bytes":
|
|
397
|
+
"sha256": "a3ac972a1d9d6412084cebffb1e612f855f8042c861223d9b849c6569af68f5c",
|
|
398
|
+
"bytes": 22040
|
|
399
399
|
},
|
|
400
400
|
"di/tokens.d.ts": {
|
|
401
401
|
"sha256": "04db6db34348aa36d897c85ab07eb1a386cb04e3595dbcb33525bd33974111ef",
|
|
@@ -546,8 +546,8 @@
|
|
|
546
546
|
"bytes": 2025
|
|
547
547
|
},
|
|
548
548
|
"infrastructure/session/HttpServer.js": {
|
|
549
|
-
"sha256": "
|
|
550
|
-
"bytes":
|
|
549
|
+
"sha256": "5d526849ef6e97e5341df73fb9b9789a4bfa7e132f2751b9892f3cbfe09aa2c9",
|
|
550
|
+
"bytes": 31215
|
|
551
551
|
},
|
|
552
552
|
"infrastructure/session/SessionDataNormalizer.d.ts": {
|
|
553
553
|
"sha256": "c89bb5e00d7d01fb4aa6d0095602541de53c425c6b99b67fa8367eb29cb63e9e",
|
|
@@ -670,12 +670,12 @@
|
|
|
670
670
|
"bytes": 6002
|
|
671
671
|
},
|
|
672
672
|
"mcp-server.d.ts": {
|
|
673
|
-
"sha256": "
|
|
674
|
-
"bytes":
|
|
673
|
+
"sha256": "d3ffbaa9f4fc3d3819de38bee485b9887429bfae3967496d33e4a7e602d97408",
|
|
674
|
+
"bytes": 273
|
|
675
675
|
},
|
|
676
676
|
"mcp-server.js": {
|
|
677
|
-
"sha256": "
|
|
678
|
-
"bytes":
|
|
677
|
+
"sha256": "7fe3d3ff4f60932f95213793b3369a9c927c1ea22626465c5f80f3cb112e378e",
|
|
678
|
+
"bytes": 2898
|
|
679
679
|
},
|
|
680
680
|
"mcp/assert-output.d.ts": {
|
|
681
681
|
"sha256": "f1b821c3652423b15a09d2d1c5a042ee565a503c3d7196bd8220fbe697e0dc75",
|
|
@@ -1085,6 +1085,14 @@
|
|
|
1085
1085
|
"sha256": "bdea37dfe3f2ef98be01899b067f841c645bda69c23dddd9e181f94b4b157c5e",
|
|
1086
1086
|
"bytes": 8822
|
|
1087
1087
|
},
|
|
1088
|
+
"mcp/transports/bridge-entry.d.ts": {
|
|
1089
|
+
"sha256": "94993c0731ec161943ea487376f5c2367e2b863ce9f0b2c0b6030f7bd3010e94",
|
|
1090
|
+
"bytes": 79
|
|
1091
|
+
},
|
|
1092
|
+
"mcp/transports/bridge-entry.js": {
|
|
1093
|
+
"sha256": "462259c99bd8c61703796faba7a83d5ad03cbdf930476bb273e0d8021bbe03e5",
|
|
1094
|
+
"bytes": 4154
|
|
1095
|
+
},
|
|
1088
1096
|
"mcp/transports/http-entry.d.ts": {
|
|
1089
1097
|
"sha256": "35d313b120dcf38643de9462559163581b89943fe432706986252e8b698b9507",
|
|
1090
1098
|
"bytes": 70
|
|
@@ -2278,12 +2286,12 @@
|
|
|
2278
2286
|
"bytes": 7393
|
|
2279
2287
|
},
|
|
2280
2288
|
"v2/infra/local/session-lock/index.d.ts": {
|
|
2281
|
-
"sha256": "
|
|
2282
|
-
"bytes":
|
|
2289
|
+
"sha256": "a0c2e5cbafa9633023f571d89bb41da3a7660d8371473a6344269801c001cbfc",
|
|
2290
|
+
"bytes": 966
|
|
2283
2291
|
},
|
|
2284
2292
|
"v2/infra/local/session-lock/index.js": {
|
|
2285
|
-
"sha256": "
|
|
2286
|
-
"bytes":
|
|
2293
|
+
"sha256": "4832225abcf2fcff1d7562b15a83cab02c8ea9d000b5d9e60ea1e4ef296394a9",
|
|
2294
|
+
"bytes": 3651
|
|
2287
2295
|
},
|
|
2288
2296
|
"v2/infra/local/session-store/index.d.ts": {
|
|
2289
2297
|
"sha256": "33b18ccce92374f8e444a61c63238634087d222f09e834b49dd2ea38d2be3796",
|
|
@@ -2666,12 +2674,12 @@
|
|
|
2666
2674
|
"bytes": 1572
|
|
2667
2675
|
},
|
|
2668
2676
|
"v2/usecases/console-service.js": {
|
|
2669
|
-
"sha256": "
|
|
2670
|
-
"bytes":
|
|
2677
|
+
"sha256": "99f25ac6595cddccb6941c99e9773b075c665eed8b820303efd41a1da1d95e05",
|
|
2678
|
+
"bytes": 30332
|
|
2671
2679
|
},
|
|
2672
2680
|
"v2/usecases/console-types.d.ts": {
|
|
2673
|
-
"sha256": "
|
|
2674
|
-
"bytes":
|
|
2681
|
+
"sha256": "9f1060c1689eb1fa6e23e6fbba00ad406c1956e196110c8eab7f95e8ceae81c9",
|
|
2682
|
+
"bytes": 7386
|
|
2675
2683
|
},
|
|
2676
2684
|
"v2/usecases/console-types.js": {
|
|
2677
2685
|
"sha256": "d43aa81f5bc89faa359e0f97c814ba25155591ff078fbb9bfd40f8c7c9683230",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function startBridgeServer(primaryPort: number): Promise<void>;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.startBridgeServer = startBridgeServer;
|
|
37
|
+
const FORWARD_TIMEOUT_MS = 30000;
|
|
38
|
+
async function startBridgeServer(primaryPort) {
|
|
39
|
+
const primaryUrl = new URL(`http://localhost:${primaryPort}/mcp`);
|
|
40
|
+
console.error(`[Bridge] Forwarding stdio → ${primaryUrl.href}`);
|
|
41
|
+
const { StdioServerTransport } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/server/stdio.js')));
|
|
42
|
+
const { StreamableHTTPClientTransport } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/client/streamableHttp.js')));
|
|
43
|
+
const stdioTransport = new StdioServerTransport();
|
|
44
|
+
const httpTransport = new StreamableHTTPClientTransport(primaryUrl);
|
|
45
|
+
stdioTransport.onerror = (err) => {
|
|
46
|
+
console.error('[Bridge] Stdio error:', err);
|
|
47
|
+
};
|
|
48
|
+
httpTransport.onerror = (err) => {
|
|
49
|
+
console.error('[Bridge] HTTP error:', err);
|
|
50
|
+
};
|
|
51
|
+
httpTransport.onclose = () => {
|
|
52
|
+
console.error('[Bridge] Primary closed connection, shutting down bridge');
|
|
53
|
+
process.exit(0);
|
|
54
|
+
};
|
|
55
|
+
stdioTransport.onmessage = (msg) => {
|
|
56
|
+
const timer = setTimeout(() => {
|
|
57
|
+
console.error('[Bridge] Warning: no response from primary after', FORWARD_TIMEOUT_MS, 'ms');
|
|
58
|
+
}, FORWARD_TIMEOUT_MS);
|
|
59
|
+
void httpTransport.send(msg)
|
|
60
|
+
.catch((err) => console.error('[Bridge] Forward to primary failed:', err))
|
|
61
|
+
.finally(() => clearTimeout(timer));
|
|
62
|
+
};
|
|
63
|
+
httpTransport.onmessage = (msg) => {
|
|
64
|
+
void stdioTransport.send(msg).catch((err) => {
|
|
65
|
+
console.error('[Bridge] Forward to IDE failed:', err);
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
await httpTransport.start();
|
|
69
|
+
console.error('[Bridge] Connected to primary');
|
|
70
|
+
process.stdout.on('error', (err) => {
|
|
71
|
+
const code = err.code;
|
|
72
|
+
if (code === 'EPIPE' || code === 'ERR_STREAM_DESTROYED') {
|
|
73
|
+
console.error('[Bridge] stdout pipe broken, shutting down');
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
console.error('[Bridge] stdout error:', err);
|
|
77
|
+
}
|
|
78
|
+
void httpTransport.close().finally(() => process.exit(0));
|
|
79
|
+
});
|
|
80
|
+
await stdioTransport.start();
|
|
81
|
+
console.error('[Bridge] WorkRail MCP bridge running on stdio');
|
|
82
|
+
process.stdin.once('end', () => {
|
|
83
|
+
console.error('[Bridge] stdin closed, shutting down');
|
|
84
|
+
void httpTransport.close().finally(() => process.exit(0));
|
|
85
|
+
});
|
|
86
|
+
const shutdown = (signal) => {
|
|
87
|
+
console.error(`[Bridge] Received ${signal}, shutting down`);
|
|
88
|
+
void httpTransport.close().finally(() => process.exit(0));
|
|
89
|
+
};
|
|
90
|
+
process.once('SIGINT', () => shutdown('SIGINT'));
|
|
91
|
+
process.once('SIGTERM', () => shutdown('SIGTERM'));
|
|
92
|
+
process.once('SIGHUP', () => shutdown('SIGHUP'));
|
|
93
|
+
}
|
package/dist/mcp-server.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
export { startStdioServer } from './mcp/transports/stdio-entry.js';
|
|
3
3
|
export { startHttpServer } from './mcp/transports/http-entry.js';
|
|
4
|
+
export { startBridgeServer } from './mcp/transports/bridge-entry.js';
|
|
4
5
|
export { composeServer } from './mcp/server.js';
|
package/dist/mcp-server.js
CHANGED
|
@@ -1,31 +1,63 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.composeServer = exports.startHttpServer = exports.startStdioServer = void 0;
|
|
4
|
+
exports.composeServer = exports.startBridgeServer = exports.startHttpServer = exports.startStdioServer = void 0;
|
|
5
5
|
const transport_mode_js_1 = require("./mcp/transports/transport-mode.js");
|
|
6
6
|
const stdio_entry_js_1 = require("./mcp/transports/stdio-entry.js");
|
|
7
7
|
const http_entry_js_1 = require("./mcp/transports/http-entry.js");
|
|
8
|
+
const bridge_entry_js_1 = require("./mcp/transports/bridge-entry.js");
|
|
8
9
|
const assert_never_js_1 = require("./runtime/assert-never.js");
|
|
9
10
|
var stdio_entry_js_2 = require("./mcp/transports/stdio-entry.js");
|
|
10
11
|
Object.defineProperty(exports, "startStdioServer", { enumerable: true, get: function () { return stdio_entry_js_2.startStdioServer; } });
|
|
11
12
|
var http_entry_js_2 = require("./mcp/transports/http-entry.js");
|
|
12
13
|
Object.defineProperty(exports, "startHttpServer", { enumerable: true, get: function () { return http_entry_js_2.startHttpServer; } });
|
|
14
|
+
var bridge_entry_js_2 = require("./mcp/transports/bridge-entry.js");
|
|
15
|
+
Object.defineProperty(exports, "startBridgeServer", { enumerable: true, get: function () { return bridge_entry_js_2.startBridgeServer; } });
|
|
13
16
|
var server_js_1 = require("./mcp/server.js");
|
|
14
17
|
Object.defineProperty(exports, "composeServer", { enumerable: true, get: function () { return server_js_1.composeServer; } });
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
const DEFAULT_MCP_PORT = 3100;
|
|
19
|
+
async function detectHealthyPrimary(port) {
|
|
20
|
+
try {
|
|
21
|
+
const response = await fetch(`http://localhost:${port}/mcp`, {
|
|
22
|
+
method: 'GET',
|
|
23
|
+
signal: AbortSignal.timeout(500),
|
|
24
|
+
headers: { Accept: 'application/json, text/event-stream' },
|
|
21
25
|
});
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
await response.body?.cancel().catch(() => undefined);
|
|
27
|
+
return port;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function main() {
|
|
34
|
+
const mode = (0, transport_mode_js_1.resolveTransportMode)(process.env);
|
|
35
|
+
if (mode.kind === 'stdio') {
|
|
36
|
+
const primaryPort = await detectHealthyPrimary(DEFAULT_MCP_PORT);
|
|
37
|
+
if (primaryPort != null) {
|
|
38
|
+
console.error(`[Startup] Primary detected on :${primaryPort} — starting in bridge mode`);
|
|
39
|
+
try {
|
|
40
|
+
await (0, bridge_entry_js_1.startBridgeServer)(primaryPort);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
console.error('[Bridge] Fatal error, falling back to full stdio server:', error);
|
|
44
|
+
await (0, stdio_entry_js_1.startStdioServer)();
|
|
45
|
+
}
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
switch (mode.kind) {
|
|
50
|
+
case 'stdio':
|
|
51
|
+
await (0, stdio_entry_js_1.startStdioServer)();
|
|
52
|
+
break;
|
|
53
|
+
case 'http':
|
|
54
|
+
await (0, http_entry_js_1.startHttpServer)(mode.port);
|
|
55
|
+
break;
|
|
56
|
+
default:
|
|
57
|
+
(0, assert_never_js_1.assertNever)(mode);
|
|
58
|
+
}
|
|
31
59
|
}
|
|
60
|
+
main().catch((error) => {
|
|
61
|
+
console.error('[Startup] Fatal error:', error);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
});
|
|
@@ -8,7 +8,9 @@ export declare class LocalSessionLockV2 implements SessionLockPortV2 {
|
|
|
8
8
|
private readonly dataDir;
|
|
9
9
|
private readonly fs;
|
|
10
10
|
private readonly clock;
|
|
11
|
-
|
|
11
|
+
private readonly workerId;
|
|
12
|
+
private readonly instanceId;
|
|
13
|
+
constructor(dataDir: DataDirPortV2, fs: FileSystemPortV2, clock: TimeClockPortV2, workerId?: string);
|
|
12
14
|
private clearIfStaleLock;
|
|
13
15
|
acquire(sessionId: SessionId): ResultAsync<SessionLockHandleV2, SessionLockError>;
|
|
14
16
|
release(handle: SessionLockHandleV2): ResultAsync<void, SessionLockError>;
|
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.LocalSessionLockV2 = void 0;
|
|
4
4
|
const neverthrow_1 = require("neverthrow");
|
|
5
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
6
|
class LocalSessionLockV2 {
|
|
6
|
-
constructor(dataDir, fs, clock) {
|
|
7
|
+
constructor(dataDir, fs, clock, workerId = 'default') {
|
|
7
8
|
this.dataDir = dataDir;
|
|
8
9
|
this.fs = fs;
|
|
9
10
|
this.clock = clock;
|
|
11
|
+
this.workerId = workerId;
|
|
12
|
+
this.instanceId = (0, node_crypto_1.randomUUID)();
|
|
10
13
|
}
|
|
11
14
|
clearIfStaleLock(lockPath) {
|
|
12
15
|
return this.fs
|
|
@@ -17,6 +20,18 @@ class LocalSessionLockV2 {
|
|
|
17
20
|
const pid = typeof data.pid === 'number' ? data.pid : null;
|
|
18
21
|
if (pid === null)
|
|
19
22
|
return false;
|
|
23
|
+
const myPid = this.clock.getPid();
|
|
24
|
+
const lockWorkerId = typeof data.workerId === 'string' ? data.workerId : undefined;
|
|
25
|
+
if (pid === myPid && lockWorkerId !== undefined) {
|
|
26
|
+
if (lockWorkerId !== this.workerId) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
const lockInstanceId = typeof data.instanceId === 'string' ? data.instanceId : undefined;
|
|
30
|
+
if (lockInstanceId === this.instanceId) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
20
35
|
try {
|
|
21
36
|
process.kill(pid, 0);
|
|
22
37
|
return false;
|
|
@@ -58,6 +73,8 @@ class LocalSessionLockV2 {
|
|
|
58
73
|
v: 1,
|
|
59
74
|
sessionId,
|
|
60
75
|
pid: this.clock.getPid(),
|
|
76
|
+
workerId: this.workerId,
|
|
77
|
+
instanceId: this.instanceId,
|
|
61
78
|
startedAtMs: this.clock.nowMs(),
|
|
62
79
|
}))))
|
|
63
80
|
.andThen(({ fd }) => this.fs.fsyncFile(fd).andThen(() => this.fs.closeFile(fd)))
|
|
@@ -231,6 +231,75 @@ function resolveWorkflowNames(dag, pinnedWorkflowStore) {
|
|
|
231
231
|
return names;
|
|
232
232
|
});
|
|
233
233
|
}
|
|
234
|
+
function resolveSkippedSteps(dag, events, pinnedWorkflowStore) {
|
|
235
|
+
const traceRes = (0, run_execution_trace_js_1.projectRunExecutionTraceV2)(events);
|
|
236
|
+
if (traceRes.isErr())
|
|
237
|
+
return (0, neverthrow_2.okAsync)({});
|
|
238
|
+
const skippedByRunId = {};
|
|
239
|
+
for (const [runId, traceSummary] of Object.entries(traceRes.value.byRunId)) {
|
|
240
|
+
for (const item of traceSummary.items) {
|
|
241
|
+
if (item.kind !== 'evaluated_condition')
|
|
242
|
+
continue;
|
|
243
|
+
if (!item.summary.startsWith('SKIP:'))
|
|
244
|
+
continue;
|
|
245
|
+
const stepRef = item.refs.find((r) => r.kind === 'step_id');
|
|
246
|
+
if (!stepRef)
|
|
247
|
+
continue;
|
|
248
|
+
const existing = skippedByRunId[runId] ?? [];
|
|
249
|
+
existing.push({ stepId: stepRef.value, recordedAtEventIndex: item.recordedAtEventIndex });
|
|
250
|
+
skippedByRunId[runId] = existing;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (Object.keys(skippedByRunId).length === 0)
|
|
254
|
+
return (0, neverthrow_2.okAsync)({});
|
|
255
|
+
const hashSet = new Set();
|
|
256
|
+
for (const run of Object.values(dag.runsById)) {
|
|
257
|
+
if (run.workflow.kind === 'with_workflow') {
|
|
258
|
+
hashSet.add(run.workflow.workflowHash);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (hashSet.size === 0) {
|
|
262
|
+
const result = {};
|
|
263
|
+
for (const [runId, items] of Object.entries(skippedByRunId)) {
|
|
264
|
+
const seen = new Set();
|
|
265
|
+
const steps = [];
|
|
266
|
+
for (const { stepId } of [...items].sort((a, b) => a.recordedAtEventIndex - b.recordedAtEventIndex)) {
|
|
267
|
+
if (!seen.has(stepId)) {
|
|
268
|
+
seen.add(stepId);
|
|
269
|
+
steps.push({ stepId, stepLabel: null });
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
result[runId] = steps;
|
|
273
|
+
}
|
|
274
|
+
return (0, neverthrow_2.okAsync)(result);
|
|
275
|
+
}
|
|
276
|
+
const workflowTasks = [...hashSet].map((hash) => pinnedWorkflowStore
|
|
277
|
+
.get((0, index_js_1.asWorkflowHash)((0, index_js_1.asSha256Digest)(hash)))
|
|
278
|
+
.map((compiled) => [
|
|
279
|
+
hash,
|
|
280
|
+
compiled ? extractStepTitlesFromCompiled(compiled) : new Map(),
|
|
281
|
+
])
|
|
282
|
+
.orElse(() => (0, neverthrow_2.okAsync)([hash, new Map()])));
|
|
283
|
+
return neverthrow_1.ResultAsync.combine(workflowTasks).map((workflowEntries) => {
|
|
284
|
+
const titlesByHash = new Map(workflowEntries);
|
|
285
|
+
const result = {};
|
|
286
|
+
for (const [runId, items] of Object.entries(skippedByRunId)) {
|
|
287
|
+
const run = dag.runsById[runId];
|
|
288
|
+
const wfHash = run?.workflow.kind === 'with_workflow' ? run.workflow.workflowHash : null;
|
|
289
|
+
const titles = wfHash ? titlesByHash.get(wfHash) : undefined;
|
|
290
|
+
const seen = new Set();
|
|
291
|
+
const steps = [];
|
|
292
|
+
for (const { stepId } of [...items].sort((a, b) => a.recordedAtEventIndex - b.recordedAtEventIndex)) {
|
|
293
|
+
if (!seen.has(stepId)) {
|
|
294
|
+
seen.add(stepId);
|
|
295
|
+
steps.push({ stepId, stepLabel: titles?.get(stepId) ?? null });
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
result[runId] = steps;
|
|
299
|
+
}
|
|
300
|
+
return result;
|
|
301
|
+
});
|
|
302
|
+
}
|
|
234
303
|
function extractPendingStepId(snapshot) {
|
|
235
304
|
const state = snapshot.enginePayload.engineState;
|
|
236
305
|
if ((state.kind === 'running' || state.kind === 'blocked') && state.pending.kind === 'some') {
|
|
@@ -430,7 +499,7 @@ function projectSessionSummary(sessionId, truth, completionByRunId, workflowName
|
|
|
430
499
|
lastModifiedMs,
|
|
431
500
|
};
|
|
432
501
|
}
|
|
433
|
-
function projectSessionDetail(sessionId, truth, completionByRunId, stepLabels, workflowNames) {
|
|
502
|
+
function projectSessionDetail(sessionId, truth, completionByRunId, stepLabels, workflowNames, skippedStepsMap = {}) {
|
|
434
503
|
const { events } = truth;
|
|
435
504
|
const health = (0, session_health_js_1.projectSessionHealthV2)(truth);
|
|
436
505
|
const sessionHealth = health.isOk() && health.value.kind === 'healthy' ? 'healthy' : 'corrupt';
|
|
@@ -504,6 +573,7 @@ function projectSessionDetail(sessionId, truth, completionByRunId, stepLabels, w
|
|
|
504
573
|
executionTraceSummary: executionTraceRes.isOk()
|
|
505
574
|
? (executionTraceRes.value.byRunId[run.runId] ?? null)
|
|
506
575
|
: null,
|
|
576
|
+
skippedSteps: skippedStepsMap[run.runId] ?? [],
|
|
507
577
|
};
|
|
508
578
|
});
|
|
509
579
|
return { sessionId, sessionTitle, health: sessionHealth, runs };
|
|
@@ -72,6 +72,7 @@ export interface ConsoleDagRun {
|
|
|
72
72
|
readonly status: ConsoleRunStatus;
|
|
73
73
|
readonly hasUnresolvedCriticalGaps: boolean;
|
|
74
74
|
readonly executionTraceSummary: ConsoleExecutionTraceSummary | null;
|
|
75
|
+
readonly skippedSteps: readonly ConsoleGhostStep[];
|
|
75
76
|
}
|
|
76
77
|
export interface ConsoleSessionDetail {
|
|
77
78
|
readonly sessionId: string;
|
|
@@ -195,3 +196,7 @@ export interface ConsoleWorkflowDetail {
|
|
|
195
196
|
readonly examples?: readonly string[];
|
|
196
197
|
readonly preconditions?: readonly string[];
|
|
197
198
|
}
|
|
199
|
+
export interface ConsoleGhostStep {
|
|
200
|
+
readonly stepId: string;
|
|
201
|
+
readonly stepLabel: string | null;
|
|
202
|
+
}
|