@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.
Files changed (147) hide show
  1. package/README.md +223 -0
  2. package/dist/application/app.d.ts +32 -0
  3. package/dist/application/app.d.ts.map +1 -0
  4. package/dist/application/app.js +133 -0
  5. package/dist/application/app.js.map +1 -0
  6. package/dist/application/services/validation-engine.d.ts +102 -0
  7. package/dist/application/services/validation-engine.d.ts.map +1 -0
  8. package/dist/application/services/validation-engine.js +293 -0
  9. package/dist/application/services/validation-engine.js.map +1 -0
  10. package/dist/application/services/workflow-service.d.ts +47 -0
  11. package/dist/application/services/workflow-service.d.ts.map +1 -0
  12. package/dist/application/services/workflow-service.js +80 -0
  13. package/dist/application/services/workflow-service.js.map +1 -0
  14. package/dist/application/use-cases/get-next-step.d.ts +22 -0
  15. package/dist/application/use-cases/get-next-step.d.ts.map +1 -0
  16. package/dist/application/use-cases/get-next-step.js +21 -0
  17. package/dist/application/use-cases/get-next-step.js.map +1 -0
  18. package/dist/application/use-cases/get-workflow.d.ts +13 -0
  19. package/dist/application/use-cases/get-workflow.d.ts.map +1 -0
  20. package/dist/application/use-cases/get-workflow.js +26 -0
  21. package/dist/application/use-cases/get-workflow.js.map +1 -0
  22. package/dist/application/use-cases/list-workflows.d.ts +13 -0
  23. package/dist/application/use-cases/list-workflows.d.ts.map +1 -0
  24. package/dist/application/use-cases/list-workflows.js +21 -0
  25. package/dist/application/use-cases/list-workflows.js.map +1 -0
  26. package/dist/application/use-cases/validate-step-output.d.ts +20 -0
  27. package/dist/application/use-cases/validate-step-output.d.ts.map +1 -0
  28. package/dist/application/use-cases/validate-step-output.js +21 -0
  29. package/dist/application/use-cases/validate-step-output.js.map +1 -0
  30. package/dist/application/validation.d.ts +6 -0
  31. package/dist/application/validation.d.ts.map +1 -0
  32. package/dist/application/validation.js +19 -0
  33. package/dist/application/validation.js.map +1 -0
  34. package/dist/cli.d.ts +3 -0
  35. package/dist/cli.d.ts.map +1 -0
  36. package/dist/cli.js +106 -0
  37. package/dist/cli.js.map +1 -0
  38. package/dist/container.d.ts +22 -0
  39. package/dist/container.d.ts.map +1 -0
  40. package/dist/container.js +25 -0
  41. package/dist/container.js.map +1 -0
  42. package/dist/core/error-handler.d.ts +93 -0
  43. package/dist/core/error-handler.d.ts.map +1 -0
  44. package/dist/core/error-handler.js +336 -0
  45. package/dist/core/error-handler.js.map +1 -0
  46. package/dist/domain/index.d.ts +3 -0
  47. package/dist/domain/index.d.ts.map +1 -0
  48. package/dist/domain/index.js +6 -0
  49. package/dist/domain/index.js.map +1 -0
  50. package/dist/index.d.ts +3 -0
  51. package/dist/index.d.ts.map +1 -0
  52. package/dist/index.js +30 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/infrastructure/index.d.ts +3 -0
  55. package/dist/infrastructure/index.d.ts.map +1 -0
  56. package/dist/infrastructure/index.js +6 -0
  57. package/dist/infrastructure/index.js.map +1 -0
  58. package/dist/infrastructure/rpc/handler.d.ts +26 -0
  59. package/dist/infrastructure/rpc/handler.d.ts.map +1 -0
  60. package/dist/infrastructure/rpc/handler.js +91 -0
  61. package/dist/infrastructure/rpc/handler.js.map +1 -0
  62. package/dist/infrastructure/rpc/index.d.ts +2 -0
  63. package/dist/infrastructure/rpc/index.d.ts.map +1 -0
  64. package/dist/infrastructure/rpc/index.js +5 -0
  65. package/dist/infrastructure/rpc/index.js.map +1 -0
  66. package/dist/infrastructure/rpc/server.d.ts +4 -0
  67. package/dist/infrastructure/rpc/server.d.ts.map +1 -0
  68. package/dist/infrastructure/rpc/server.js +40 -0
  69. package/dist/infrastructure/rpc/server.js.map +1 -0
  70. package/dist/infrastructure/storage/caching-workflow-storage.d.ts +22 -0
  71. package/dist/infrastructure/storage/caching-workflow-storage.d.ts.map +1 -0
  72. package/dist/infrastructure/storage/caching-workflow-storage.js +61 -0
  73. package/dist/infrastructure/storage/caching-workflow-storage.js.map +1 -0
  74. package/dist/infrastructure/storage/file-workflow-storage.d.ts +56 -0
  75. package/dist/infrastructure/storage/file-workflow-storage.d.ts.map +1 -0
  76. package/dist/infrastructure/storage/file-workflow-storage.js +206 -0
  77. package/dist/infrastructure/storage/file-workflow-storage.js.map +1 -0
  78. package/dist/infrastructure/storage/in-memory-storage.d.ts +17 -0
  79. package/dist/infrastructure/storage/in-memory-storage.d.ts.map +1 -0
  80. package/dist/infrastructure/storage/in-memory-storage.js +43 -0
  81. package/dist/infrastructure/storage/in-memory-storage.js.map +1 -0
  82. package/dist/infrastructure/storage/index.d.ts +6 -0
  83. package/dist/infrastructure/storage/index.d.ts.map +1 -0
  84. package/dist/infrastructure/storage/index.js +9 -0
  85. package/dist/infrastructure/storage/index.js.map +1 -0
  86. package/dist/infrastructure/storage/schema-validating-workflow-storage.d.ts +23 -0
  87. package/dist/infrastructure/storage/schema-validating-workflow-storage.d.ts.map +1 -0
  88. package/dist/infrastructure/storage/schema-validating-workflow-storage.js +70 -0
  89. package/dist/infrastructure/storage/schema-validating-workflow-storage.js.map +1 -0
  90. package/dist/infrastructure/storage/storage.d.ts +16 -0
  91. package/dist/infrastructure/storage/storage.d.ts.map +1 -0
  92. package/dist/infrastructure/storage/storage.js +32 -0
  93. package/dist/infrastructure/storage/storage.js.map +1 -0
  94. package/dist/mcp-server-simple.js +391 -0
  95. package/dist/mcp-server.d.ts +3 -0
  96. package/dist/mcp-server.d.ts.map +1 -0
  97. package/dist/mcp-server.js +223 -0
  98. package/dist/mcp-server.js.map +1 -0
  99. package/dist/tools/mcp_initialize.d.ts +3 -0
  100. package/dist/tools/mcp_initialize.d.ts.map +1 -0
  101. package/dist/tools/mcp_initialize.js +52 -0
  102. package/dist/tools/mcp_initialize.js.map +1 -0
  103. package/dist/tools/mcp_shutdown.d.ts +3 -0
  104. package/dist/tools/mcp_shutdown.d.ts.map +1 -0
  105. package/dist/tools/mcp_shutdown.js +11 -0
  106. package/dist/tools/mcp_shutdown.js.map +1 -0
  107. package/dist/tools/mcp_tools_list.d.ts +3 -0
  108. package/dist/tools/mcp_tools_list.d.ts.map +1 -0
  109. package/dist/tools/mcp_tools_list.js +61 -0
  110. package/dist/tools/mcp_tools_list.js.map +1 -0
  111. package/dist/types/mcp-types.d.ts +251 -0
  112. package/dist/types/mcp-types.d.ts.map +1 -0
  113. package/dist/types/mcp-types.js +27 -0
  114. package/dist/types/mcp-types.js.map +1 -0
  115. package/dist/types/server.d.ts +5 -0
  116. package/dist/types/server.d.ts.map +1 -0
  117. package/dist/types/server.js +3 -0
  118. package/dist/types/server.js.map +1 -0
  119. package/dist/types/storage.d.ts +27 -0
  120. package/dist/types/storage.d.ts.map +1 -0
  121. package/dist/types/storage.js +6 -0
  122. package/dist/types/storage.js.map +1 -0
  123. package/dist/types/workflow-types.d.ts +251 -0
  124. package/dist/types/workflow-types.d.ts.map +1 -0
  125. package/dist/types/workflow-types.js +5 -0
  126. package/dist/types/workflow-types.js.map +1 -0
  127. package/dist/utils/condition-evaluator.d.ts +32 -0
  128. package/dist/utils/condition-evaluator.d.ts.map +1 -0
  129. package/dist/utils/condition-evaluator.js +105 -0
  130. package/dist/utils/condition-evaluator.js.map +1 -0
  131. package/dist/utils/config.d.ts +195 -0
  132. package/dist/utils/config.d.ts.map +1 -0
  133. package/dist/utils/config.js +332 -0
  134. package/dist/utils/config.js.map +1 -0
  135. package/dist/validation/request-validator.d.ts +9 -0
  136. package/dist/validation/request-validator.d.ts.map +1 -0
  137. package/dist/validation/request-validator.js +32 -0
  138. package/dist/validation/request-validator.js.map +1 -0
  139. package/dist/validation/response-validator.d.ts +9 -0
  140. package/dist/validation/response-validator.d.ts.map +1 -0
  141. package/dist/validation/response-validator.js +83 -0
  142. package/dist/validation/response-validator.js.map +1 -0
  143. package/dist/validation/schemas.d.ts +6 -0
  144. package/dist/validation/schemas.d.ts.map +1 -0
  145. package/dist/validation/schemas.js +52 -0
  146. package/dist/validation/schemas.js.map +1 -0
  147. package/package.json +48 -0
@@ -0,0 +1,3 @@
1
+ export * from '../types/mcp-types';
2
+ export * from '../types/storage';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export { createWorkflowLookupServer } from './infrastructure/rpc/server';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -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,3 @@
1
+ export * from './storage';
2
+ export * from './rpc';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -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,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./storage"), exports);
5
+ tslib_1.__exportStar(require("./rpc"), exports);
6
+ //# sourceMappingURL=index.js.map
@@ -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,2 @@
1
+ export * from './server';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -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,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./server"), exports);
5
+ //# sourceMappingURL=index.js.map
@@ -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