@exaudeus/workrail 0.0.2-beta.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 +223 -0
- package/dist/application/app.d.ts +32 -0
- package/dist/application/app.d.ts.map +1 -0
- package/dist/application/app.js +133 -0
- package/dist/application/app.js.map +1 -0
- package/dist/application/services/validation-engine.d.ts +102 -0
- package/dist/application/services/validation-engine.d.ts.map +1 -0
- package/dist/application/services/validation-engine.js +293 -0
- package/dist/application/services/validation-engine.js.map +1 -0
- package/dist/application/services/workflow-service.d.ts +47 -0
- package/dist/application/services/workflow-service.d.ts.map +1 -0
- package/dist/application/services/workflow-service.js +80 -0
- package/dist/application/services/workflow-service.js.map +1 -0
- package/dist/application/use-cases/get-next-step.d.ts +22 -0
- package/dist/application/use-cases/get-next-step.d.ts.map +1 -0
- package/dist/application/use-cases/get-next-step.js +21 -0
- package/dist/application/use-cases/get-next-step.js.map +1 -0
- package/dist/application/use-cases/get-workflow.d.ts +13 -0
- package/dist/application/use-cases/get-workflow.d.ts.map +1 -0
- package/dist/application/use-cases/get-workflow.js +26 -0
- package/dist/application/use-cases/get-workflow.js.map +1 -0
- package/dist/application/use-cases/list-workflows.d.ts +13 -0
- package/dist/application/use-cases/list-workflows.d.ts.map +1 -0
- package/dist/application/use-cases/list-workflows.js +21 -0
- package/dist/application/use-cases/list-workflows.js.map +1 -0
- package/dist/application/use-cases/validate-step-output.d.ts +20 -0
- package/dist/application/use-cases/validate-step-output.d.ts.map +1 -0
- package/dist/application/use-cases/validate-step-output.js +21 -0
- package/dist/application/use-cases/validate-step-output.js.map +1 -0
- package/dist/application/validation.d.ts +6 -0
- package/dist/application/validation.d.ts.map +1 -0
- package/dist/application/validation.js +19 -0
- package/dist/application/validation.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +106 -0
- package/dist/cli.js.map +1 -0
- package/dist/container.d.ts +22 -0
- package/dist/container.d.ts.map +1 -0
- package/dist/container.js +25 -0
- package/dist/container.js.map +1 -0
- package/dist/core/error-handler.d.ts +93 -0
- package/dist/core/error-handler.d.ts.map +1 -0
- package/dist/core/error-handler.js +336 -0
- package/dist/core/error-handler.js.map +1 -0
- package/dist/domain/index.d.ts +3 -0
- package/dist/domain/index.d.ts.map +1 -0
- package/dist/domain/index.js +6 -0
- package/dist/domain/index.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/infrastructure/index.d.ts +3 -0
- package/dist/infrastructure/index.d.ts.map +1 -0
- package/dist/infrastructure/index.js +6 -0
- package/dist/infrastructure/index.js.map +1 -0
- package/dist/infrastructure/rpc/handler.d.ts +26 -0
- package/dist/infrastructure/rpc/handler.d.ts.map +1 -0
- package/dist/infrastructure/rpc/handler.js +91 -0
- package/dist/infrastructure/rpc/handler.js.map +1 -0
- package/dist/infrastructure/rpc/index.d.ts +2 -0
- package/dist/infrastructure/rpc/index.d.ts.map +1 -0
- package/dist/infrastructure/rpc/index.js +5 -0
- package/dist/infrastructure/rpc/index.js.map +1 -0
- package/dist/infrastructure/rpc/server.d.ts +4 -0
- package/dist/infrastructure/rpc/server.d.ts.map +1 -0
- package/dist/infrastructure/rpc/server.js +40 -0
- package/dist/infrastructure/rpc/server.js.map +1 -0
- package/dist/infrastructure/storage/caching-workflow-storage.d.ts +22 -0
- package/dist/infrastructure/storage/caching-workflow-storage.d.ts.map +1 -0
- package/dist/infrastructure/storage/caching-workflow-storage.js +61 -0
- package/dist/infrastructure/storage/caching-workflow-storage.js.map +1 -0
- package/dist/infrastructure/storage/file-workflow-storage.d.ts +56 -0
- package/dist/infrastructure/storage/file-workflow-storage.d.ts.map +1 -0
- package/dist/infrastructure/storage/file-workflow-storage.js +206 -0
- package/dist/infrastructure/storage/file-workflow-storage.js.map +1 -0
- package/dist/infrastructure/storage/in-memory-storage.d.ts +17 -0
- package/dist/infrastructure/storage/in-memory-storage.d.ts.map +1 -0
- package/dist/infrastructure/storage/in-memory-storage.js +43 -0
- package/dist/infrastructure/storage/in-memory-storage.js.map +1 -0
- package/dist/infrastructure/storage/index.d.ts +6 -0
- package/dist/infrastructure/storage/index.d.ts.map +1 -0
- package/dist/infrastructure/storage/index.js +9 -0
- package/dist/infrastructure/storage/index.js.map +1 -0
- package/dist/infrastructure/storage/schema-validating-workflow-storage.d.ts +23 -0
- package/dist/infrastructure/storage/schema-validating-workflow-storage.d.ts.map +1 -0
- package/dist/infrastructure/storage/schema-validating-workflow-storage.js +70 -0
- package/dist/infrastructure/storage/schema-validating-workflow-storage.js.map +1 -0
- package/dist/infrastructure/storage/storage.d.ts +16 -0
- package/dist/infrastructure/storage/storage.d.ts.map +1 -0
- package/dist/infrastructure/storage/storage.js +32 -0
- package/dist/infrastructure/storage/storage.js.map +1 -0
- package/dist/mcp-server-simple.js +391 -0
- package/dist/mcp-server.d.ts +3 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +223 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/tools/mcp_initialize.d.ts +3 -0
- package/dist/tools/mcp_initialize.d.ts.map +1 -0
- package/dist/tools/mcp_initialize.js +52 -0
- package/dist/tools/mcp_initialize.js.map +1 -0
- package/dist/tools/mcp_shutdown.d.ts +3 -0
- package/dist/tools/mcp_shutdown.d.ts.map +1 -0
- package/dist/tools/mcp_shutdown.js +11 -0
- package/dist/tools/mcp_shutdown.js.map +1 -0
- package/dist/tools/mcp_tools_list.d.ts +3 -0
- package/dist/tools/mcp_tools_list.d.ts.map +1 -0
- package/dist/tools/mcp_tools_list.js +61 -0
- package/dist/tools/mcp_tools_list.js.map +1 -0
- package/dist/types/mcp-types.d.ts +251 -0
- package/dist/types/mcp-types.d.ts.map +1 -0
- package/dist/types/mcp-types.js +27 -0
- package/dist/types/mcp-types.js.map +1 -0
- package/dist/types/server.d.ts +5 -0
- package/dist/types/server.d.ts.map +1 -0
- package/dist/types/server.js +3 -0
- package/dist/types/server.js.map +1 -0
- package/dist/types/storage.d.ts +27 -0
- package/dist/types/storage.d.ts.map +1 -0
- package/dist/types/storage.js +6 -0
- package/dist/types/storage.js.map +1 -0
- package/dist/types/workflow-types.d.ts +251 -0
- package/dist/types/workflow-types.d.ts.map +1 -0
- package/dist/types/workflow-types.js +5 -0
- package/dist/types/workflow-types.js.map +1 -0
- package/dist/utils/condition-evaluator.d.ts +32 -0
- package/dist/utils/condition-evaluator.d.ts.map +1 -0
- package/dist/utils/condition-evaluator.js +105 -0
- package/dist/utils/condition-evaluator.js.map +1 -0
- package/dist/utils/config.d.ts +195 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +332 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/validation/request-validator.d.ts +9 -0
- package/dist/validation/request-validator.d.ts.map +1 -0
- package/dist/validation/request-validator.js +32 -0
- package/dist/validation/request-validator.js.map +1 -0
- package/dist/validation/response-validator.d.ts +9 -0
- package/dist/validation/response-validator.d.ts.map +1 -0
- package/dist/validation/response-validator.js +83 -0
- package/dist/validation/response-validator.js.map +1 -0
- package/dist/validation/schemas.d.ts +6 -0
- package/dist/validation/schemas.d.ts.map +1 -0
- package/dist/validation/schemas.js +52 -0
- package/dist/validation/schemas.js.map +1 -0
- package/package.json +48 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/domain/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
tslib_1.__exportStar(require("../types/mcp-types"), exports);
|
|
5
|
+
tslib_1.__exportStar(require("../types/storage"), exports);
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/domain/index.ts"],"names":[],"mappings":";;;AAAA,6DAAmC;AACnC,2DAAiC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AA4BA,OAAO,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.createWorkflowLookupServer = void 0;
|
|
5
|
+
const container_1 = require("./container");
|
|
6
|
+
async function main() {
|
|
7
|
+
try {
|
|
8
|
+
const { server } = (0, container_1.createAppContainer)();
|
|
9
|
+
await server.start();
|
|
10
|
+
console.log('Workflow Lookup MCP Server started successfully');
|
|
11
|
+
// Graceful shutdown on SIGINT/SIGTERM
|
|
12
|
+
const shutdown = async (signal) => {
|
|
13
|
+
console.log(`Received ${signal}, shutting down...`);
|
|
14
|
+
await server.stop();
|
|
15
|
+
process.exit(0);
|
|
16
|
+
};
|
|
17
|
+
process.on('SIGINT', () => shutdown('SIGINT'));
|
|
18
|
+
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
console.error('Failed to start server:', error);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (require.main === module) {
|
|
26
|
+
main();
|
|
27
|
+
}
|
|
28
|
+
var server_1 = require("./infrastructure/rpc/server");
|
|
29
|
+
Object.defineProperty(exports, "createWorkflowLookupServer", { enumerable: true, get: function () { return server_1.createWorkflowLookupServer; } });
|
|
30
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;AAEA,2CAAiD;AAEjD,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,8BAAkB,GAAE,CAAC;QACxC,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,OAAO,CAAC,GAAG,CAAC,iDAAiD,CAAC,CAAC;QAE/D,sCAAsC;QACtC,MAAM,QAAQ,GAAG,KAAK,EAAE,MAAc,EAAE,EAAE;YACxC,OAAO,CAAC,GAAG,CAAC,YAAY,MAAM,oBAAoB,CAAC,CAAC;YACpD,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YACpB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,CAAC;QACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC/C,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;IACnD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAC;QAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;IAC5B,IAAI,EAAE,CAAC;AACT,CAAC;AAED,sDAAyE;AAAhE,oHAAA,0BAA0B,OAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/infrastructure/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,OAAO,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/infrastructure/index.ts"],"names":[],"mappings":";;;AAAA,oDAA0B;AAC1B,gDAAsB"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Readable, Writable } from 'stream';
|
|
2
|
+
/**
|
|
3
|
+
* RpcHandler encapsulates pure JSON-RPC 2.0 stdin/stdout (or arbitrary stream) handling.
|
|
4
|
+
* It is transport-only: it parses newline-delimited JSON-RPC messages, delegates
|
|
5
|
+
* execution to an injected dispatcher, and writes responses. It knows nothing
|
|
6
|
+
* about domain logic.
|
|
7
|
+
*/
|
|
8
|
+
export declare class RpcHandler {
|
|
9
|
+
private readonly input;
|
|
10
|
+
private readonly output;
|
|
11
|
+
private readonly dispatch;
|
|
12
|
+
private running;
|
|
13
|
+
private buffer;
|
|
14
|
+
private listener;
|
|
15
|
+
constructor(dispatch: (method: string, params: any, id: string | number | null) => Promise<any>, options?: {
|
|
16
|
+
input?: Readable;
|
|
17
|
+
output?: Writable;
|
|
18
|
+
});
|
|
19
|
+
/** Attach listeners and begin processing */
|
|
20
|
+
start(): void;
|
|
21
|
+
/** Detach listeners to stop processing */
|
|
22
|
+
stop(): void;
|
|
23
|
+
private processLine;
|
|
24
|
+
private writeResponse;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/rpc/handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAI5C;;;;;GAKG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAW;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAW;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAIP;IAElB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,QAAQ,CAA0C;gBAGxD,QAAQ,EAAE,CACR,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,GAAG,EACX,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,KACvB,OAAO,CAAC,GAAG,CAAC,EACjB,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,QAAQ,CAAC;QACjB,MAAM,CAAC,EAAE,QAAQ,CAAC;KACnB;IAOH,4CAA4C;IAC5C,KAAK,IAAI,IAAI;IAmBb,0CAA0C;IAC1C,IAAI,IAAI,IAAI;YASE,WAAW;IAuCzB,OAAO,CAAC,aAAa;CAGtB"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RpcHandler = void 0;
|
|
4
|
+
const error_handler_1 = require("../../core/error-handler");
|
|
5
|
+
/**
|
|
6
|
+
* RpcHandler encapsulates pure JSON-RPC 2.0 stdin/stdout (or arbitrary stream) handling.
|
|
7
|
+
* It is transport-only: it parses newline-delimited JSON-RPC messages, delegates
|
|
8
|
+
* execution to an injected dispatcher, and writes responses. It knows nothing
|
|
9
|
+
* about domain logic.
|
|
10
|
+
*/
|
|
11
|
+
class RpcHandler {
|
|
12
|
+
input;
|
|
13
|
+
output;
|
|
14
|
+
dispatch;
|
|
15
|
+
running = false;
|
|
16
|
+
buffer = '';
|
|
17
|
+
listener = null;
|
|
18
|
+
constructor(dispatch, options) {
|
|
19
|
+
this.dispatch = dispatch;
|
|
20
|
+
this.input = options?.input ?? process.stdin;
|
|
21
|
+
this.output = options?.output ?? process.stdout;
|
|
22
|
+
}
|
|
23
|
+
/** Attach listeners and begin processing */
|
|
24
|
+
start() {
|
|
25
|
+
if (this.running)
|
|
26
|
+
return;
|
|
27
|
+
this.listener = (chunk) => {
|
|
28
|
+
this.buffer += chunk.toString();
|
|
29
|
+
let newlineIndex;
|
|
30
|
+
while ((newlineIndex = this.buffer.indexOf('\n')) !== -1) {
|
|
31
|
+
const raw = this.buffer.slice(0, newlineIndex).trim();
|
|
32
|
+
this.buffer = this.buffer.slice(newlineIndex + 1);
|
|
33
|
+
if (!raw)
|
|
34
|
+
continue;
|
|
35
|
+
this.processLine(raw);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
this.input.on('data', this.listener);
|
|
39
|
+
this.running = true;
|
|
40
|
+
}
|
|
41
|
+
/** Detach listeners to stop processing */
|
|
42
|
+
stop() {
|
|
43
|
+
if (!this.running)
|
|
44
|
+
return;
|
|
45
|
+
if (this.listener) {
|
|
46
|
+
this.input.off('data', this.listener);
|
|
47
|
+
this.listener = null;
|
|
48
|
+
}
|
|
49
|
+
this.running = false;
|
|
50
|
+
}
|
|
51
|
+
async processLine(raw) {
|
|
52
|
+
const errorHandler = error_handler_1.ErrorHandler.getInstance();
|
|
53
|
+
let request;
|
|
54
|
+
try {
|
|
55
|
+
request = JSON.parse(raw);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
const errorResponse = errorHandler.createParseError();
|
|
59
|
+
this.writeResponse(errorResponse);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
// Basic JSON-RPC 2.0 request validation
|
|
63
|
+
const validStructure = request &&
|
|
64
|
+
request.jsonrpc === '2.0' &&
|
|
65
|
+
typeof request.method === 'string' &&
|
|
66
|
+
(typeof request.id === 'string' || typeof request.id === 'number' || request.id === null);
|
|
67
|
+
if (!validStructure) {
|
|
68
|
+
const invalid = errorHandler.createInvalidRequestError(request?.id ?? null);
|
|
69
|
+
this.writeResponse(invalid);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const result = await this.dispatch(request.method, request.params, request.id);
|
|
74
|
+
const response = {
|
|
75
|
+
jsonrpc: '2.0',
|
|
76
|
+
id: request.id,
|
|
77
|
+
result,
|
|
78
|
+
};
|
|
79
|
+
this.writeResponse(response);
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
const errorResponse = errorHandler.handleError(err, request.id);
|
|
83
|
+
this.writeResponse(errorResponse);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
writeResponse(response) {
|
|
87
|
+
this.output.write(JSON.stringify(response) + '\n');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
exports.RpcHandler = RpcHandler;
|
|
91
|
+
//# sourceMappingURL=handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.js","sourceRoot":"","sources":["../../../src/infrastructure/rpc/handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/rpc/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/infrastructure/rpc/index.ts"],"names":[],"mappings":";;;AAAA,mDAAyB"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { WorkflowLookupServer } from '../../types/server';
|
|
2
|
+
import { WorkflowService } from '../../application/services/workflow-service';
|
|
3
|
+
export declare function createWorkflowLookupServer(workflowService: WorkflowService): WorkflowLookupServer;
|
|
4
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/rpc/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAK9E,wBAAgB,0BAA0B,CACxC,eAAe,EAAE,eAAe,GAC/B,oBAAoB,CAoCtB"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createWorkflowLookupServer = createWorkflowLookupServer;
|
|
4
|
+
const app_1 = require("../../application/app");
|
|
5
|
+
const request_validator_1 = require("../../validation/request-validator");
|
|
6
|
+
const handler_1 = require("./handler");
|
|
7
|
+
function createWorkflowLookupServer(workflowService) {
|
|
8
|
+
let rpcHandler = null;
|
|
9
|
+
let running = false;
|
|
10
|
+
return {
|
|
11
|
+
start: async () => {
|
|
12
|
+
if (running)
|
|
13
|
+
return;
|
|
14
|
+
console.log('Initializing Workflow Lookup MCP Server...');
|
|
15
|
+
// Build mediator with current validator and services
|
|
16
|
+
const mediator = (0, app_1.buildWorkflowApplication)(workflowService, request_validator_1.requestValidator);
|
|
17
|
+
// Create RPC handler bound to mediator.execute
|
|
18
|
+
rpcHandler = new handler_1.RpcHandler((method, params, _id) => mediator.execute(method, params));
|
|
19
|
+
if (process.env['NODE_ENV'] !== 'test') {
|
|
20
|
+
rpcHandler.start();
|
|
21
|
+
}
|
|
22
|
+
running = true;
|
|
23
|
+
console.log('Server ready to accept JSON-RPC requests');
|
|
24
|
+
},
|
|
25
|
+
stop: async () => {
|
|
26
|
+
if (!running) {
|
|
27
|
+
console.log('Shutdown requested, but server is not running.');
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
console.log('Shutting down Workflow Lookup MCP Server...');
|
|
31
|
+
if (rpcHandler) {
|
|
32
|
+
rpcHandler.stop();
|
|
33
|
+
rpcHandler = null;
|
|
34
|
+
}
|
|
35
|
+
running = false;
|
|
36
|
+
console.log('Server stopped');
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/infrastructure/rpc/server.ts"],"names":[],"mappings":";;AAMA,gEAsCC;AA1CD,+CAAiE;AACjE,0EAAsE;AACtE,uCAAuC;AAEvC,SAAgB,0BAA0B,CACxC,eAAgC;IAEhC,IAAI,UAAU,GAAsB,IAAI,CAAC;IACzC,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,OAAO;QACL,KAAK,EAAE,KAAK,IAAI,EAAE;YAChB,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAC;YAE1D,qDAAqD;YACrD,MAAM,QAAQ,GAAG,IAAA,8BAAwB,EAAC,eAAe,EAAE,oCAAgB,CAAC,CAAC;YAE7E,+CAA+C;YAC/C,UAAU,GAAG,IAAI,oBAAU,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;YAEvF,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,MAAM,EAAE,CAAC;gBACvC,UAAU,CAAC,KAAK,EAAE,CAAC;YACrB,CAAC;YAED,OAAO,GAAG,IAAI,CAAC;YACf,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;QAC1D,CAAC;QACD,IAAI,EAAE,KAAK,IAAI,EAAE;YACf,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAC;gBAC9D,OAAO;YACT,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,6CAA6C,CAAC,CAAC;YAC3D,IAAI,UAAU,EAAE,CAAC;gBACf,UAAU,CAAC,IAAI,EAAE,CAAC;gBAClB,UAAU,GAAG,IAAI,CAAC;YACpB,CAAC;YACD,OAAO,GAAG,KAAK,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAChC,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { IWorkflowStorage } from '../../types/storage';
|
|
2
|
+
import { Workflow, WorkflowSummary } from '../../types/mcp-types';
|
|
3
|
+
/**
|
|
4
|
+
* Decorator that adds simple in-memory TTL caching to any IWorkflowStorage.
|
|
5
|
+
*/
|
|
6
|
+
export declare class CachingWorkflowStorage implements IWorkflowStorage {
|
|
7
|
+
private readonly inner;
|
|
8
|
+
private readonly ttlMs;
|
|
9
|
+
private cache;
|
|
10
|
+
private stats;
|
|
11
|
+
constructor(inner: IWorkflowStorage, ttlMs: number);
|
|
12
|
+
getCacheStats(): {
|
|
13
|
+
hits: number;
|
|
14
|
+
misses: number;
|
|
15
|
+
};
|
|
16
|
+
private isFresh;
|
|
17
|
+
loadAllWorkflows(): Promise<Workflow[]>;
|
|
18
|
+
getWorkflowById(id: string): Promise<Workflow | null>;
|
|
19
|
+
listWorkflowSummaries(): Promise<WorkflowSummary[]>;
|
|
20
|
+
save?(workflow: Workflow): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=caching-workflow-storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"caching-workflow-storage.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/storage/caching-workflow-storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAelE;;GAEG;AACH,qBAAa,sBAAuB,YAAW,gBAAgB;IAIjD,OAAO,CAAC,QAAQ,CAAC,KAAK;IAAoB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAH5E,OAAO,CAAC,KAAK,CAAmC;IAChD,OAAO,CAAC,KAAK,CAA0B;gBAEV,KAAK,EAAE,gBAAgB,EAAmB,KAAK,EAAE,MAAM;IAE7E,aAAa;;;;IAIpB,OAAO,CAAC,OAAO;IAIT,gBAAgB,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;IAWvC,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAMrD,qBAAqB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAWnD,IAAI,CAAC,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;CAK/C"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CachingWorkflowStorage = void 0;
|
|
4
|
+
const deepClone = (obj) => {
|
|
5
|
+
// Use structuredClone if available (Node 17+), otherwise fallback to JSON
|
|
6
|
+
if (typeof global.structuredClone === 'function') {
|
|
7
|
+
return global.structuredClone(obj);
|
|
8
|
+
}
|
|
9
|
+
return JSON.parse(JSON.stringify(obj));
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Decorator that adds simple in-memory TTL caching to any IWorkflowStorage.
|
|
13
|
+
*/
|
|
14
|
+
class CachingWorkflowStorage {
|
|
15
|
+
inner;
|
|
16
|
+
ttlMs;
|
|
17
|
+
cache = null;
|
|
18
|
+
stats = { hits: 0, misses: 0 };
|
|
19
|
+
constructor(inner, ttlMs) {
|
|
20
|
+
this.inner = inner;
|
|
21
|
+
this.ttlMs = ttlMs;
|
|
22
|
+
}
|
|
23
|
+
getCacheStats() {
|
|
24
|
+
return { ...this.stats };
|
|
25
|
+
}
|
|
26
|
+
isFresh() {
|
|
27
|
+
return this.cache !== null && Date.now() - this.cache.timestamp < this.ttlMs;
|
|
28
|
+
}
|
|
29
|
+
async loadAllWorkflows() {
|
|
30
|
+
if (this.isFresh()) {
|
|
31
|
+
this.stats.hits += 1;
|
|
32
|
+
return deepClone(this.cache.value);
|
|
33
|
+
}
|
|
34
|
+
this.stats.misses += 1;
|
|
35
|
+
const workflows = await this.inner.loadAllWorkflows();
|
|
36
|
+
this.cache = { value: workflows, timestamp: Date.now() };
|
|
37
|
+
return deepClone(workflows);
|
|
38
|
+
}
|
|
39
|
+
async getWorkflowById(id) {
|
|
40
|
+
const workflows = await this.loadAllWorkflows();
|
|
41
|
+
const wf = workflows.find((wf) => wf.id === id);
|
|
42
|
+
return wf ? deepClone(wf) : null;
|
|
43
|
+
}
|
|
44
|
+
async listWorkflowSummaries() {
|
|
45
|
+
const workflows = await this.loadAllWorkflows();
|
|
46
|
+
return workflows.map((wf) => ({
|
|
47
|
+
id: wf.id,
|
|
48
|
+
name: wf.name,
|
|
49
|
+
description: wf.description,
|
|
50
|
+
category: 'default',
|
|
51
|
+
version: wf.version
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
async save(workflow) {
|
|
55
|
+
if (typeof this.inner.save === 'function') {
|
|
56
|
+
return this.inner.save(workflow);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.CachingWorkflowStorage = CachingWorkflowStorage;
|
|
61
|
+
//# sourceMappingURL=caching-workflow-storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"caching-workflow-storage.js","sourceRoot":"","sources":["../../../src/infrastructure/storage/caching-workflow-storage.ts"],"names":[],"mappings":";;;AAGA,MAAM,SAAS,GAAG,CAAI,GAAM,EAAK,EAAE;IACjC,0EAA0E;IAC1E,IAAI,OAAQ,MAAc,CAAC,eAAe,KAAK,UAAU,EAAE,CAAC;QAC1D,OAAQ,MAAc,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;AACzC,CAAC,CAAC;AAOF;;GAEG;AACH,MAAa,sBAAsB;IAIJ;IAA0C;IAH/D,KAAK,GAA8B,IAAI,CAAC;IACxC,KAAK,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IAEvC,YAA6B,KAAuB,EAAmB,KAAa;QAAvD,UAAK,GAAL,KAAK,CAAkB;QAAmB,UAAK,GAAL,KAAK,CAAQ;IAAG,CAAC;IAEjF,aAAa;QAClB,OAAO,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;IAC3B,CAAC;IAEO,OAAO;QACb,OAAO,IAAI,CAAC,KAAK,KAAK,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC;IAC/E,CAAC;IAED,KAAK,CAAC,gBAAgB;QACpB,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;YACnB,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC;YACrB,OAAO,SAAS,CAAC,IAAI,CAAC,KAAM,CAAC,KAAK,CAAC,CAAC;QACtC,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;QACvB,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,gBAAgB,EAAE,CAAC;QACtD,IAAI,CAAC,KAAK,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QACzD,OAAO,SAAS,CAAC,SAAS,CAAC,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,EAAU;QAC9B,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAChD,MAAM,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QAChD,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,qBAAqB;QACzB,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAChD,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YAC5B,EAAE,EAAE,EAAE,CAAC,EAAE;YACT,IAAI,EAAE,EAAE,CAAC,IAAI;YACb,WAAW,EAAE,EAAE,CAAC,WAAW;YAC3B,QAAQ,EAAE,SAAS;YACnB,OAAO,EAAE,EAAE,CAAC,OAAO;SACpB,CAAC,CAAC,CAAC;IACN,CAAC;IAED,KAAK,CAAC,IAAI,CAAE,QAAkB;QAC5B,IAAI,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YAC1C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;CACF;AA/CD,wDA+CC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { IWorkflowStorage } from '../../types/storage';
|
|
2
|
+
import { Workflow, WorkflowSummary } from '../../types/mcp-types';
|
|
3
|
+
interface FileWorkflowStorageOptions {
|
|
4
|
+
/** Reject files larger than this size (bytes). Default 1_000_000 */
|
|
5
|
+
maxFileSizeBytes?: number;
|
|
6
|
+
/** Cache entry TTL in milliseconds. 0 to disable. Default 5000 */
|
|
7
|
+
cacheTTLms?: number;
|
|
8
|
+
/** Maximum cached workflows before evicting LRU. Default 100 */
|
|
9
|
+
cacheSize?: number;
|
|
10
|
+
/** Index cache TTL in milliseconds. Default 30000 (30 seconds) */
|
|
11
|
+
indexCacheTTLms?: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Optimized file-system based workflow storage with intelligent caching.
|
|
15
|
+
* Uses an index cache to avoid repeatedly scanning directories and
|
|
16
|
+
* reading files unnecessarily.
|
|
17
|
+
*/
|
|
18
|
+
export declare class FileWorkflowStorage implements IWorkflowStorage {
|
|
19
|
+
private readonly baseDirReal;
|
|
20
|
+
private readonly maxFileSize;
|
|
21
|
+
private readonly cacheTTL;
|
|
22
|
+
private readonly cacheLimit;
|
|
23
|
+
private readonly indexCacheTTL;
|
|
24
|
+
private readonly cache;
|
|
25
|
+
private workflowIndex;
|
|
26
|
+
private indexExpires;
|
|
27
|
+
constructor(directory: string, options?: FileWorkflowStorageOptions);
|
|
28
|
+
/**
|
|
29
|
+
* Build or refresh the workflow index by scanning the directory once
|
|
30
|
+
*/
|
|
31
|
+
private buildWorkflowIndex;
|
|
32
|
+
/**
|
|
33
|
+
* Get the workflow index, building it if necessary
|
|
34
|
+
*/
|
|
35
|
+
private getWorkflowIndex;
|
|
36
|
+
/**
|
|
37
|
+
* Load a specific workflow from file
|
|
38
|
+
*/
|
|
39
|
+
private loadWorkflowFromFile;
|
|
40
|
+
/**
|
|
41
|
+
* Load *all* JSON files from the configured directory.
|
|
42
|
+
* NOTE: This method is expensive and should be avoided when possible.
|
|
43
|
+
* Use getWorkflowIndex() + loadWorkflowFromFile() for better performance.
|
|
44
|
+
*/
|
|
45
|
+
loadAllWorkflows(): Promise<Workflow[]>;
|
|
46
|
+
getWorkflowById(id: string): Promise<Workflow | null>;
|
|
47
|
+
listWorkflowSummaries(): Promise<WorkflowSummary[]>;
|
|
48
|
+
save(): Promise<void>;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Helper factory that resolves the workflow directory according to the
|
|
52
|
+
* previous behaviour (env override → bundled workflows).
|
|
53
|
+
*/
|
|
54
|
+
export declare function createDefaultFileWorkflowStorage(): FileWorkflowStorage;
|
|
55
|
+
export {};
|
|
56
|
+
//# sourceMappingURL=file-workflow-storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-workflow-storage.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/storage/file-workflow-storage.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAyClE,UAAU,0BAA0B;IAClC,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;GAIG;AACH,qBAAa,mBAAoB,YAAW,gBAAgB;IAC1D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiC;IAGvD,OAAO,CAAC,aAAa,CAAgD;IACrE,OAAO,CAAC,YAAY,CAAa;gBAErB,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,0BAA+B;IAQvE;;OAEG;YACW,kBAAkB;IAgDhC;;OAEG;YACW,gBAAgB;IAc9B;;OAEG;YACW,oBAAoB;IAmBlC;;;;OAIG;IACU,gBAAgB,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;IAgBvC,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IA0CrD,qBAAqB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAMnD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAInC;AAED;;;GAGG;AACH,wBAAgB,gCAAgC,IAAI,mBAAmB,CAYtE"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FileWorkflowStorage = void 0;
|
|
4
|
+
exports.createDefaultFileWorkflowStorage = createDefaultFileWorkflowStorage;
|
|
5
|
+
const tslib_1 = require("tslib");
|
|
6
|
+
const promises_1 = tslib_1.__importDefault(require("fs/promises"));
|
|
7
|
+
const fs_1 = require("fs");
|
|
8
|
+
const path_1 = tslib_1.__importDefault(require("path"));
|
|
9
|
+
const error_handler_1 = require("../../core/error-handler");
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Helpers
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
function sanitizeId(id) {
|
|
14
|
+
if (id.includes('\u0000')) {
|
|
15
|
+
throw new error_handler_1.SecurityError('Null byte detected in identifier', 'sanitizeId');
|
|
16
|
+
}
|
|
17
|
+
const normalised = id.normalize('NFC');
|
|
18
|
+
const valid = /^[a-zA-Z0-9_-]+$/.test(normalised);
|
|
19
|
+
if (!valid) {
|
|
20
|
+
throw new error_handler_1.InvalidWorkflowError(id, 'Invalid characters in workflow id');
|
|
21
|
+
}
|
|
22
|
+
return normalised;
|
|
23
|
+
}
|
|
24
|
+
function assertWithinBase(resolvedPath, baseDir) {
|
|
25
|
+
if (!resolvedPath.startsWith(baseDir + path_1.default.sep) && resolvedPath !== baseDir) {
|
|
26
|
+
throw new error_handler_1.SecurityError('Path escapes storage sandbox', 'file-access');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Optimized file-system based workflow storage with intelligent caching.
|
|
31
|
+
* Uses an index cache to avoid repeatedly scanning directories and
|
|
32
|
+
* reading files unnecessarily.
|
|
33
|
+
*/
|
|
34
|
+
class FileWorkflowStorage {
|
|
35
|
+
baseDirReal;
|
|
36
|
+
maxFileSize;
|
|
37
|
+
cacheTTL;
|
|
38
|
+
cacheLimit;
|
|
39
|
+
indexCacheTTL;
|
|
40
|
+
cache = new Map();
|
|
41
|
+
// Index cache to avoid expensive directory scans
|
|
42
|
+
workflowIndex = null;
|
|
43
|
+
indexExpires = 0;
|
|
44
|
+
constructor(directory, options = {}) {
|
|
45
|
+
this.baseDirReal = path_1.default.resolve(directory);
|
|
46
|
+
this.maxFileSize = options.maxFileSizeBytes ?? 1_000_000; // 1 MB default
|
|
47
|
+
this.cacheTTL = options.cacheTTLms ?? 5000;
|
|
48
|
+
this.cacheLimit = options.cacheSize ?? 100;
|
|
49
|
+
this.indexCacheTTL = options.indexCacheTTLms ?? 30000; // 30 seconds
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Build or refresh the workflow index by scanning the directory once
|
|
53
|
+
*/
|
|
54
|
+
async buildWorkflowIndex() {
|
|
55
|
+
const dirEntries = await promises_1.default.readdir(this.baseDirReal);
|
|
56
|
+
const jsonFiles = dirEntries.filter((f) => f.endsWith('.json'));
|
|
57
|
+
const index = new Map();
|
|
58
|
+
for (const file of jsonFiles) {
|
|
59
|
+
const filePathRaw = path_1.default.resolve(this.baseDirReal, file);
|
|
60
|
+
assertWithinBase(filePathRaw, this.baseDirReal);
|
|
61
|
+
try {
|
|
62
|
+
const stats = (0, fs_1.statSync)(filePathRaw);
|
|
63
|
+
if (stats.size > this.maxFileSize) {
|
|
64
|
+
console.warn(`[FileWorkflowStorage] Skipping oversized file: ${file}`);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
// Read just enough to get the workflow ID and summary info
|
|
68
|
+
const raw = await promises_1.default.readFile(filePathRaw, 'utf-8');
|
|
69
|
+
const data = JSON.parse(raw);
|
|
70
|
+
if (!data.id) {
|
|
71
|
+
console.warn(`[FileWorkflowStorage] Skipping file without id: ${file}`);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const entry = {
|
|
75
|
+
id: data.id,
|
|
76
|
+
filename: file,
|
|
77
|
+
lastModified: stats.mtimeMs,
|
|
78
|
+
summary: {
|
|
79
|
+
id: data.id,
|
|
80
|
+
name: data.name,
|
|
81
|
+
description: data.description,
|
|
82
|
+
category: 'default',
|
|
83
|
+
version: data.version
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
index.set(data.id, entry);
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
console.warn(`[FileWorkflowStorage] Skipping invalid file: ${file}`, err);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return index;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Get the workflow index, building it if necessary
|
|
97
|
+
*/
|
|
98
|
+
async getWorkflowIndex() {
|
|
99
|
+
const now = Date.now();
|
|
100
|
+
if (this.workflowIndex && this.indexExpires > now) {
|
|
101
|
+
return this.workflowIndex;
|
|
102
|
+
}
|
|
103
|
+
// Rebuild index
|
|
104
|
+
this.workflowIndex = await this.buildWorkflowIndex();
|
|
105
|
+
this.indexExpires = now + this.indexCacheTTL;
|
|
106
|
+
return this.workflowIndex;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Load a specific workflow from file
|
|
110
|
+
*/
|
|
111
|
+
async loadWorkflowFromFile(filename) {
|
|
112
|
+
const filePath = path_1.default.resolve(this.baseDirReal, filename);
|
|
113
|
+
assertWithinBase(filePath, this.baseDirReal);
|
|
114
|
+
try {
|
|
115
|
+
const stats = (0, fs_1.statSync)(filePath);
|
|
116
|
+
if (stats.size > this.maxFileSize) {
|
|
117
|
+
throw new error_handler_1.SecurityError('Workflow file exceeds size limit', 'file-size');
|
|
118
|
+
}
|
|
119
|
+
const raw = await promises_1.default.readFile(filePath, 'utf-8');
|
|
120
|
+
const data = JSON.parse(raw);
|
|
121
|
+
return data;
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
console.warn(`[FileWorkflowStorage] Failed to load workflow from ${filename}:`, err);
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Load *all* JSON files from the configured directory.
|
|
130
|
+
* NOTE: This method is expensive and should be avoided when possible.
|
|
131
|
+
* Use getWorkflowIndex() + loadWorkflowFromFile() for better performance.
|
|
132
|
+
*/
|
|
133
|
+
async loadAllWorkflows() {
|
|
134
|
+
const index = await this.getWorkflowIndex();
|
|
135
|
+
const workflows = [];
|
|
136
|
+
// Load workflows in parallel for better performance
|
|
137
|
+
const loadPromises = Array.from(index.values()).map(async (entry) => {
|
|
138
|
+
const workflow = await this.loadWorkflowFromFile(entry.filename);
|
|
139
|
+
if (workflow) {
|
|
140
|
+
workflows.push(workflow);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
await Promise.all(loadPromises);
|
|
144
|
+
return workflows;
|
|
145
|
+
}
|
|
146
|
+
async getWorkflowById(id) {
|
|
147
|
+
const safeId = sanitizeId(id);
|
|
148
|
+
// Try cache first
|
|
149
|
+
const cached = this.cache.get(safeId);
|
|
150
|
+
if (cached && cached.expires > Date.now()) {
|
|
151
|
+
return cached.workflow;
|
|
152
|
+
}
|
|
153
|
+
// Check index for the workflow
|
|
154
|
+
const index = await this.getWorkflowIndex();
|
|
155
|
+
const indexEntry = index.get(safeId);
|
|
156
|
+
if (!indexEntry) {
|
|
157
|
+
return null; // Workflow doesn't exist
|
|
158
|
+
}
|
|
159
|
+
// Load the specific workflow file
|
|
160
|
+
const workflow = await this.loadWorkflowFromFile(indexEntry.filename);
|
|
161
|
+
if (!workflow) {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
// Verify ID matches (security check)
|
|
165
|
+
if (workflow.id !== safeId) {
|
|
166
|
+
throw new error_handler_1.InvalidWorkflowError(safeId, 'ID mismatch between index and workflow.id');
|
|
167
|
+
}
|
|
168
|
+
// Cache the result
|
|
169
|
+
if (this.cacheTTL > 0) {
|
|
170
|
+
if (this.cache.size >= this.cacheLimit) {
|
|
171
|
+
// Evict oldest (first inserted)
|
|
172
|
+
const firstKey = this.cache.keys().next().value;
|
|
173
|
+
this.cache.delete(firstKey);
|
|
174
|
+
}
|
|
175
|
+
this.cache.set(safeId, { workflow, expires: Date.now() + this.cacheTTL });
|
|
176
|
+
}
|
|
177
|
+
return workflow;
|
|
178
|
+
}
|
|
179
|
+
async listWorkflowSummaries() {
|
|
180
|
+
// Use the index to get summaries without loading full workflows
|
|
181
|
+
const index = await this.getWorkflowIndex();
|
|
182
|
+
return Array.from(index.values()).map(entry => entry.summary);
|
|
183
|
+
}
|
|
184
|
+
async save() {
|
|
185
|
+
// No-op for now – file storage is read-only in this phase.
|
|
186
|
+
return Promise.resolve();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
exports.FileWorkflowStorage = FileWorkflowStorage;
|
|
190
|
+
/**
|
|
191
|
+
* Helper factory that resolves the workflow directory according to the
|
|
192
|
+
* previous behaviour (env override → bundled workflows).
|
|
193
|
+
*/
|
|
194
|
+
function createDefaultFileWorkflowStorage() {
|
|
195
|
+
const DEFAULT_WORKFLOW_DIR = path_1.default.resolve(__dirname, '../../../workflows');
|
|
196
|
+
const envPath = process.env['WORKFLOW_STORAGE_PATH'];
|
|
197
|
+
const resolved = envPath ? path_1.default.resolve(envPath) : null;
|
|
198
|
+
const directory = resolved && (0, fs_1.existsSync)(resolved) ? resolved : DEFAULT_WORKFLOW_DIR;
|
|
199
|
+
// Use optimized settings for better performance
|
|
200
|
+
return new FileWorkflowStorage(directory, {
|
|
201
|
+
cacheTTLms: 10000, // 10 second cache for individual workflows
|
|
202
|
+
cacheSize: 200, // Larger cache
|
|
203
|
+
indexCacheTTLms: 60000, // 1 minute index cache
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=file-workflow-storage.js.map
|