@exaudeus/workrail 0.0.16 → 0.0.18
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/LICENSE +21 -0
- package/README.md +9 -9
- package/dist/application/app.d.ts +0 -7
- package/dist/application/app.js +1 -18
- package/dist/application/services/enhanced-error-service.d.ts +0 -64
- package/dist/application/services/enhanced-error-service.js +11 -82
- package/dist/application/services/validation-engine.d.ts +0 -65
- package/dist/application/services/validation-engine.js +5 -91
- package/dist/application/services/workflow-service.d.ts +0 -11
- package/dist/application/services/workflow-service.js +0 -17
- package/dist/application/use-cases/get-next-step.d.ts +0 -9
- package/dist/application/use-cases/get-next-step.js +0 -9
- package/dist/application/use-cases/get-workflow.d.ts +0 -9
- package/dist/application/use-cases/get-workflow.js +0 -17
- package/dist/application/use-cases/list-workflows.d.ts +0 -9
- package/dist/application/use-cases/list-workflows.js +0 -9
- package/dist/application/use-cases/validate-step-output.d.ts +0 -9
- package/dist/application/use-cases/validate-step-output.js +0 -9
- package/dist/application/use-cases/validate-workflow-json.d.ts +0 -12
- package/dist/application/use-cases/validate-workflow-json.js +0 -21
- package/dist/application/validation.d.ts +0 -1
- package/dist/application/validation.js +6 -5
- package/dist/cli.d.ts +0 -1
- package/dist/cli.js +7 -12
- package/dist/container.d.ts +0 -11
- package/dist/container.js +0 -6
- package/dist/core/error-handler.d.ts +0 -43
- package/dist/core/error-handler.js +0 -65
- package/dist/domain/index.d.ts +0 -1
- package/dist/domain/index.js +16 -4
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -2
- package/dist/infrastructure/index.d.ts +0 -1
- package/dist/infrastructure/index.js +16 -4
- package/dist/infrastructure/rpc/handler.d.ts +0 -9
- package/dist/infrastructure/rpc/handler.js +3 -16
- package/dist/infrastructure/rpc/index.d.ts +0 -1
- package/dist/infrastructure/rpc/index.js +15 -3
- package/dist/infrastructure/rpc/server.d.ts +0 -1
- package/dist/infrastructure/rpc/server.js +0 -3
- package/dist/infrastructure/storage/caching-workflow-storage.d.ts +0 -4
- package/dist/infrastructure/storage/caching-workflow-storage.js +2 -9
- package/dist/infrastructure/storage/file-workflow-storage.d.ts +0 -28
- package/dist/infrastructure/storage/file-workflow-storage.js +14 -57
- package/dist/infrastructure/storage/git-workflow-storage.d.ts +0 -14
- package/dist/infrastructure/storage/git-workflow-storage.js +19 -51
- package/dist/infrastructure/storage/in-memory-storage.d.ts +0 -6
- package/dist/infrastructure/storage/in-memory-storage.js +0 -7
- package/dist/infrastructure/storage/index.d.ts +0 -1
- package/dist/infrastructure/storage/index.js +19 -7
- package/dist/infrastructure/storage/multi-directory-workflow-storage.d.ts +0 -18
- package/dist/infrastructure/storage/multi-directory-workflow-storage.js +7 -36
- package/dist/infrastructure/storage/plugin-workflow-storage.d.ts +0 -43
- package/dist/infrastructure/storage/plugin-workflow-storage.js +17 -78
- package/dist/infrastructure/storage/remote-workflow-storage.d.ts +0 -10
- package/dist/infrastructure/storage/remote-workflow-storage.js +6 -39
- package/dist/infrastructure/storage/schema-validating-workflow-storage.d.ts +0 -5
- package/dist/infrastructure/storage/schema-validating-workflow-storage.js +7 -12
- package/dist/infrastructure/storage/storage.d.ts +0 -14
- package/dist/infrastructure/storage/storage.js +2 -21
- package/dist/mcp-server.d.ts +0 -1
- package/dist/mcp-server.js +2 -14
- package/dist/tools/mcp_initialize.d.ts +0 -1
- package/dist/tools/mcp_initialize.js +2 -9
- package/dist/tools/mcp_shutdown.d.ts +0 -1
- package/dist/tools/mcp_shutdown.js +0 -1
- package/dist/tools/mcp_tools_list.d.ts +0 -1
- package/dist/tools/mcp_tools_list.js +0 -1
- package/dist/types/mcp-types.d.ts +0 -1
- package/dist/types/mcp-types.js +0 -8
- package/dist/types/server.d.ts +0 -1
- package/dist/types/server.js +0 -1
- package/dist/types/storage.d.ts +0 -20
- package/dist/types/storage.js +0 -4
- package/dist/types/workflow-types.d.ts +0 -1
- package/dist/types/workflow-types.js +0 -3
- package/dist/utils/condition-evaluator.d.ts +0 -15
- package/dist/utils/condition-evaluator.js +0 -24
- package/dist/utils/config.d.ts +0 -55
- package/dist/utils/config.js +0 -84
- package/dist/utils/storage-security.d.ts +0 -62
- package/dist/utils/storage-security.js +6 -62
- package/dist/validation/request-validator.d.ts +0 -1
- package/dist/validation/request-validator.js +6 -6
- package/dist/validation/response-validator.d.ts +0 -1
- package/dist/validation/response-validator.js +4 -21
- package/dist/validation/schemas.d.ts +0 -5
- package/dist/validation/schemas.js +0 -5
- package/package.json +7 -14
- package/spec/mcp-protocol-handshake.md +4 -3
- package/workflows/coding-task-workflow.json +46 -8
- package/workflows/document-creation-workflow.json +235 -0
- package/workflows/exploration-workflow.json +254 -0
- package/workflows/presentation-creation.json +71 -0
- package/workflows/systemic-bug-investigation.json +32 -14
- package/workflows/systemic-bug-investigation.json.bak +196 -0
- package/dist/mcp-server-simple.js +0 -391
- package/dist/types/session-types.d.ts +0 -354
- package/dist/types/session-types.d.ts.map +0 -1
- package/dist/types/session-types.js +0 -89
- package/dist/types/session-types.js.map +0 -1
- package/workflows/example-agent-role-workflow.json +0 -83
|
@@ -25,26 +25,6 @@ export interface ValidatedPluginWorkflowConfig extends Required<PluginWorkflowCo
|
|
|
25
25
|
maxFiles: number;
|
|
26
26
|
maxPlugins: number;
|
|
27
27
|
}
|
|
28
|
-
/**
|
|
29
|
-
* Plugin-based workflow storage that loads workflows from npm packages
|
|
30
|
-
* Workflows are distributed as npm packages with a specific structure
|
|
31
|
-
*
|
|
32
|
-
* Security features:
|
|
33
|
-
* - Path traversal prevention
|
|
34
|
-
* - File size limits
|
|
35
|
-
* - Plugin count limits
|
|
36
|
-
* - Safe package.json parsing
|
|
37
|
-
*
|
|
38
|
-
* Example package structure:
|
|
39
|
-
* ```
|
|
40
|
-
* my-workflow-pack/
|
|
41
|
-
* ├── package.json
|
|
42
|
-
* ├── index.js
|
|
43
|
-
* └── workflows/
|
|
44
|
-
* ├── my-workflow-1.json
|
|
45
|
-
* └── my-workflow-2.json
|
|
46
|
-
* ```
|
|
47
|
-
*/
|
|
48
28
|
export declare class PluginWorkflowStorage implements IWorkflowStorage {
|
|
49
29
|
private readonly config;
|
|
50
30
|
private pluginCache;
|
|
@@ -63,28 +43,6 @@ export declare class PluginWorkflowStorage implements IWorkflowStorage {
|
|
|
63
43
|
getLoadedPlugins(): WorkflowPlugin[];
|
|
64
44
|
getConfig(): ValidatedPluginWorkflowConfig;
|
|
65
45
|
}
|
|
66
|
-
/**
|
|
67
|
-
* Example package.json for a workflow plugin:
|
|
68
|
-
*
|
|
69
|
-
* ```json
|
|
70
|
-
* {
|
|
71
|
-
* "name": "workrail-workflows-ai-coding",
|
|
72
|
-
* "version": "1.0.0",
|
|
73
|
-
* "description": "AI-powered coding workflows for Workrail",
|
|
74
|
-
* "main": "index.js",
|
|
75
|
-
* "workrail": {
|
|
76
|
-
* "workflows": true,
|
|
77
|
-
* "category": "coding"
|
|
78
|
-
* },
|
|
79
|
-
* "keywords": ["workrail", "workflow", "ai", "coding"],
|
|
80
|
-
* "author": "Your Name",
|
|
81
|
-
* "license": "MIT"
|
|
82
|
-
* }
|
|
83
|
-
* ```
|
|
84
|
-
*/
|
|
85
|
-
/**
|
|
86
|
-
* Example configuration for different environments
|
|
87
|
-
*/
|
|
88
46
|
export declare const PLUGIN_WORKFLOW_CONFIGS: {
|
|
89
47
|
development: {
|
|
90
48
|
scanInterval: number;
|
|
@@ -99,4 +57,3 @@ export declare const PLUGIN_WORKFLOW_CONFIGS: {
|
|
|
99
57
|
maxPlugins: number;
|
|
100
58
|
};
|
|
101
59
|
};
|
|
102
|
-
//# sourceMappingURL=plugin-workflow-storage.d.ts.map
|
|
@@ -1,52 +1,31 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.PLUGIN_WORKFLOW_CONFIGS = exports.PluginWorkflowStorage = void 0;
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const promises_1 = tslib_1.__importDefault(require("fs/promises"));
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
7
9
|
const fs_1 = require("fs");
|
|
8
10
|
const storage_security_1 = require("../../utils/storage-security");
|
|
9
11
|
const error_handler_1 = require("../../core/error-handler");
|
|
10
|
-
/**
|
|
11
|
-
* Plugin-based workflow storage that loads workflows from npm packages
|
|
12
|
-
* Workflows are distributed as npm packages with a specific structure
|
|
13
|
-
*
|
|
14
|
-
* Security features:
|
|
15
|
-
* - Path traversal prevention
|
|
16
|
-
* - File size limits
|
|
17
|
-
* - Plugin count limits
|
|
18
|
-
* - Safe package.json parsing
|
|
19
|
-
*
|
|
20
|
-
* Example package structure:
|
|
21
|
-
* ```
|
|
22
|
-
* my-workflow-pack/
|
|
23
|
-
* ├── package.json
|
|
24
|
-
* ├── index.js
|
|
25
|
-
* └── workflows/
|
|
26
|
-
* ├── my-workflow-1.json
|
|
27
|
-
* └── my-workflow-2.json
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
12
|
class PluginWorkflowStorage {
|
|
31
|
-
config;
|
|
32
|
-
pluginCache = new Map();
|
|
33
|
-
lastScan = 0;
|
|
34
13
|
constructor(config = {}) {
|
|
14
|
+
this.pluginCache = new Map();
|
|
15
|
+
this.lastScan = 0;
|
|
35
16
|
this.config = this.validateAndNormalizeConfig(config);
|
|
36
17
|
}
|
|
37
18
|
validateAndNormalizeConfig(config) {
|
|
38
19
|
const securityOptions = (0, storage_security_1.validateSecurityOptions)({
|
|
39
|
-
maxFileSizeBytes: config.maxFileSize || 1024 * 1024
|
|
20
|
+
maxFileSizeBytes: config.maxFileSize || 1024 * 1024
|
|
40
21
|
});
|
|
41
22
|
const pluginPaths = (config.pluginPaths && config.pluginPaths.length > 0)
|
|
42
23
|
? config.pluginPaths
|
|
43
24
|
: this.getDefaultPluginPaths();
|
|
44
|
-
// Validate all plugin paths are safe
|
|
45
25
|
for (const pluginPath of pluginPaths) {
|
|
46
26
|
try {
|
|
47
|
-
// Ensure plugin paths are within reasonable bounds
|
|
48
27
|
if (path_1.default.isAbsolute(pluginPath)) {
|
|
49
|
-
(0, storage_security_1.assertWithinBase)(pluginPath, '/');
|
|
28
|
+
(0, storage_security_1.assertWithinBase)(pluginPath, '/');
|
|
50
29
|
}
|
|
51
30
|
}
|
|
52
31
|
catch (error) {
|
|
@@ -55,15 +34,14 @@ class PluginWorkflowStorage {
|
|
|
55
34
|
}
|
|
56
35
|
return {
|
|
57
36
|
pluginPaths,
|
|
58
|
-
scanInterval: Math.max(30000, config.scanInterval || 300000),
|
|
37
|
+
scanInterval: Math.max(30000, config.scanInterval || 300000),
|
|
59
38
|
maxFileSize: securityOptions.maxFileSizeBytes,
|
|
60
|
-
maxFiles: Math.max(1, config.maxFiles || 50),
|
|
61
|
-
maxPlugins: Math.max(1, config.maxPlugins || 20)
|
|
39
|
+
maxFiles: Math.max(1, config.maxFiles || 50),
|
|
40
|
+
maxPlugins: Math.max(1, config.maxPlugins || 20)
|
|
62
41
|
};
|
|
63
42
|
}
|
|
64
43
|
getDefaultPluginPaths() {
|
|
65
44
|
const paths = [];
|
|
66
|
-
// Global npm modules
|
|
67
45
|
try {
|
|
68
46
|
const globalPath = require.resolve('npm').replace(/\/npm\/.*$/, '');
|
|
69
47
|
const globalNodeModules = path_1.default.join(globalPath, 'node_modules');
|
|
@@ -72,7 +50,6 @@ class PluginWorkflowStorage {
|
|
|
72
50
|
}
|
|
73
51
|
}
|
|
74
52
|
catch {
|
|
75
|
-
// Fallback: try common global paths
|
|
76
53
|
const commonPaths = [
|
|
77
54
|
'/usr/local/lib/node_modules',
|
|
78
55
|
'/usr/lib/node_modules'
|
|
@@ -83,7 +60,6 @@ class PluginWorkflowStorage {
|
|
|
83
60
|
}
|
|
84
61
|
}
|
|
85
62
|
}
|
|
86
|
-
// Local project
|
|
87
63
|
const localNodeModules = path_1.default.join(process.cwd(), 'node_modules');
|
|
88
64
|
if ((0, fs_1.existsSync)(localNodeModules)) {
|
|
89
65
|
paths.push(localNodeModules);
|
|
@@ -136,17 +112,14 @@ class PluginWorkflowStorage {
|
|
|
136
112
|
continue;
|
|
137
113
|
}
|
|
138
114
|
try {
|
|
139
|
-
// Security: Ensure we're scanning within expected directory
|
|
140
115
|
(0, storage_security_1.assertWithinBase)(pluginPath, pluginPath);
|
|
141
116
|
const entries = await promises_1.default.readdir(pluginPath);
|
|
142
117
|
for (const entry of entries) {
|
|
143
|
-
// Check plugin count limit
|
|
144
118
|
if (pluginCount >= this.config.maxPlugins) {
|
|
145
119
|
throw new error_handler_1.StorageError(`Too many plugins found (${pluginCount}), maximum allowed: ${this.config.maxPlugins}`);
|
|
146
120
|
}
|
|
147
121
|
if (this.isWorkflowPlugin(entry)) {
|
|
148
122
|
const fullPath = path_1.default.join(pluginPath, entry);
|
|
149
|
-
// Security: Ensure plugin path is within scan directory
|
|
150
123
|
(0, storage_security_1.assertWithinBase)(fullPath, pluginPath);
|
|
151
124
|
const plugin = await this.loadPlugin(fullPath);
|
|
152
125
|
if (plugin) {
|
|
@@ -170,17 +143,14 @@ class PluginWorkflowStorage {
|
|
|
170
143
|
}
|
|
171
144
|
async loadPlugin(pluginPath) {
|
|
172
145
|
try {
|
|
173
|
-
// Security: Ensure plugin path is safe
|
|
174
146
|
(0, storage_security_1.assertWithinBase)(pluginPath, path_1.default.dirname(pluginPath));
|
|
175
147
|
const packageJsonPath = path_1.default.join(pluginPath, 'package.json');
|
|
176
148
|
if (!(0, fs_1.existsSync)(packageJsonPath)) {
|
|
177
149
|
return null;
|
|
178
150
|
}
|
|
179
|
-
// Security: Ensure package.json is within plugin directory
|
|
180
151
|
(0, storage_security_1.assertWithinBase)(packageJsonPath, pluginPath);
|
|
181
|
-
// Validate package.json size
|
|
182
152
|
const packageStats = await promises_1.default.stat(packageJsonPath);
|
|
183
|
-
(0, storage_security_1.validateFileSize)(packageStats.size, Math.min(this.config.maxFileSize, 64 * 1024), 'package.json');
|
|
153
|
+
(0, storage_security_1.validateFileSize)(packageStats.size, Math.min(this.config.maxFileSize, 64 * 1024), 'package.json');
|
|
184
154
|
const packageContent = await promises_1.default.readFile(packageJsonPath, 'utf-8');
|
|
185
155
|
let packageJson;
|
|
186
156
|
try {
|
|
@@ -189,11 +159,9 @@ class PluginWorkflowStorage {
|
|
|
189
159
|
catch (parseError) {
|
|
190
160
|
throw new error_handler_1.InvalidWorkflowError(pluginPath, `Invalid package.json: ${parseError.message}`);
|
|
191
161
|
}
|
|
192
|
-
// Validate it's a workflow plugin
|
|
193
162
|
if (!packageJson.workrail || !packageJson.workrail.workflows) {
|
|
194
163
|
return null;
|
|
195
164
|
}
|
|
196
|
-
// Validate package name
|
|
197
165
|
if (!packageJson.name || typeof packageJson.name !== 'string') {
|
|
198
166
|
throw new error_handler_1.InvalidWorkflowError(pluginPath, `Invalid package name`);
|
|
199
167
|
}
|
|
@@ -201,7 +169,6 @@ class PluginWorkflowStorage {
|
|
|
201
169
|
if (!(0, fs_1.existsSync)(workflowsPath)) {
|
|
202
170
|
return null;
|
|
203
171
|
}
|
|
204
|
-
// Security: Ensure workflows directory is within plugin
|
|
205
172
|
(0, storage_security_1.assertWithinBase)(workflowsPath, pluginPath);
|
|
206
173
|
const workflows = await this.loadWorkflowsFromDirectory(workflowsPath);
|
|
207
174
|
return {
|
|
@@ -234,9 +201,7 @@ class PluginWorkflowStorage {
|
|
|
234
201
|
for (const file of jsonFiles) {
|
|
235
202
|
try {
|
|
236
203
|
const filePath = path_1.default.join(workflowsPath, file);
|
|
237
|
-
// Security: Ensure file is within workflows directory
|
|
238
204
|
(0, storage_security_1.assertWithinBase)(filePath, workflowsPath);
|
|
239
|
-
// Validate file size
|
|
240
205
|
const stats = await promises_1.default.stat(filePath);
|
|
241
206
|
(0, storage_security_1.validateFileSize)(stats.size, this.config.maxFileSize, file);
|
|
242
207
|
const content = await promises_1.default.readFile(filePath, 'utf-8');
|
|
@@ -247,7 +212,6 @@ class PluginWorkflowStorage {
|
|
|
247
212
|
catch (parseError) {
|
|
248
213
|
throw new error_handler_1.InvalidWorkflowError(file, `Invalid JSON in workflow file: ${parseError.message}`);
|
|
249
214
|
}
|
|
250
|
-
// Validate workflow ID
|
|
251
215
|
const sanitizedId = (0, storage_security_1.sanitizeId)(workflow.id);
|
|
252
216
|
if (workflow.id !== sanitizedId) {
|
|
253
217
|
throw new error_handler_1.InvalidWorkflowError(workflow.id, `Invalid workflow ID in file ${file}`);
|
|
@@ -278,42 +242,17 @@ class PluginWorkflowStorage {
|
|
|
278
242
|
}
|
|
279
243
|
}
|
|
280
244
|
exports.PluginWorkflowStorage = PluginWorkflowStorage;
|
|
281
|
-
/**
|
|
282
|
-
* Example package.json for a workflow plugin:
|
|
283
|
-
*
|
|
284
|
-
* ```json
|
|
285
|
-
* {
|
|
286
|
-
* "name": "workrail-workflows-ai-coding",
|
|
287
|
-
* "version": "1.0.0",
|
|
288
|
-
* "description": "AI-powered coding workflows for Workrail",
|
|
289
|
-
* "main": "index.js",
|
|
290
|
-
* "workrail": {
|
|
291
|
-
* "workflows": true,
|
|
292
|
-
* "category": "coding"
|
|
293
|
-
* },
|
|
294
|
-
* "keywords": ["workrail", "workflow", "ai", "coding"],
|
|
295
|
-
* "author": "Your Name",
|
|
296
|
-
* "license": "MIT"
|
|
297
|
-
* }
|
|
298
|
-
* ```
|
|
299
|
-
*/
|
|
300
|
-
/**
|
|
301
|
-
* Example configuration for different environments
|
|
302
|
-
*/
|
|
303
245
|
exports.PLUGIN_WORKFLOW_CONFIGS = {
|
|
304
|
-
// Development environment with relaxed limits
|
|
305
246
|
development: {
|
|
306
|
-
scanInterval: 60000,
|
|
307
|
-
maxFileSize: 2 * 1024 * 1024,
|
|
247
|
+
scanInterval: 60000,
|
|
248
|
+
maxFileSize: 2 * 1024 * 1024,
|
|
308
249
|
maxFiles: 100,
|
|
309
250
|
maxPlugins: 50
|
|
310
251
|
},
|
|
311
|
-
// Production environment with strict limits
|
|
312
252
|
production: {
|
|
313
|
-
scanInterval: 300000,
|
|
314
|
-
maxFileSize: 1024 * 1024,
|
|
253
|
+
scanInterval: 300000,
|
|
254
|
+
maxFileSize: 1024 * 1024,
|
|
315
255
|
maxFiles: 50,
|
|
316
256
|
maxPlugins: 20
|
|
317
257
|
}
|
|
318
258
|
};
|
|
319
|
-
//# sourceMappingURL=plugin-workflow-storage.js.map
|
|
@@ -8,11 +8,6 @@ export interface RemoteWorkflowRegistryConfig extends StorageSecurityOptions {
|
|
|
8
8
|
retryAttempts?: number;
|
|
9
9
|
userAgent?: string;
|
|
10
10
|
}
|
|
11
|
-
/**
|
|
12
|
-
* Remote workflow storage that fetches workflows from a community registry.
|
|
13
|
-
* Implements security best practices and proper error handling.
|
|
14
|
-
* Similar to npm registry but for workflows.
|
|
15
|
-
*/
|
|
16
11
|
export declare class RemoteWorkflowStorage implements IWorkflowStorage {
|
|
17
12
|
private readonly config;
|
|
18
13
|
private readonly securityOptions;
|
|
@@ -28,10 +23,6 @@ export declare class RemoteWorkflowStorage implements IWorkflowStorage {
|
|
|
28
23
|
private validateSummaries;
|
|
29
24
|
private validateWorkflowForSave;
|
|
30
25
|
}
|
|
31
|
-
/**
|
|
32
|
-
* Multi-source workflow storage that combines bundled, local, and remote workflows.
|
|
33
|
-
* Uses composition to cleanly separate concerns.
|
|
34
|
-
*/
|
|
35
26
|
export declare class CommunityWorkflowStorage implements IWorkflowStorage {
|
|
36
27
|
private readonly sources;
|
|
37
28
|
private readonly remoteStorage;
|
|
@@ -41,4 +32,3 @@ export declare class CommunityWorkflowStorage implements IWorkflowStorage {
|
|
|
41
32
|
listWorkflowSummaries(): Promise<WorkflowSummary[]>;
|
|
42
33
|
save(workflow: Workflow): Promise<void>;
|
|
43
34
|
}
|
|
44
|
-
//# sourceMappingURL=remote-workflow-storage.d.ts.map
|
|
@@ -3,21 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.CommunityWorkflowStorage = exports.RemoteWorkflowStorage = void 0;
|
|
4
4
|
const storage_security_1 = require("../../utils/storage-security");
|
|
5
5
|
const error_handler_1 = require("../../core/error-handler");
|
|
6
|
-
/**
|
|
7
|
-
* Remote workflow storage that fetches workflows from a community registry.
|
|
8
|
-
* Implements security best practices and proper error handling.
|
|
9
|
-
* Similar to npm registry but for workflows.
|
|
10
|
-
*/
|
|
11
6
|
class RemoteWorkflowStorage {
|
|
12
|
-
config;
|
|
13
|
-
securityOptions;
|
|
14
7
|
constructor(config) {
|
|
15
|
-
// Validate and secure the configuration
|
|
16
8
|
this.validateConfig(config);
|
|
17
9
|
this.securityOptions = (0, storage_security_1.validateSecurityOptions)(config);
|
|
18
10
|
this.config = {
|
|
19
11
|
...this.securityOptions,
|
|
20
|
-
baseUrl: config.baseUrl.replace(/\/$/, ''),
|
|
12
|
+
baseUrl: config.baseUrl.replace(/\/$/, ''),
|
|
21
13
|
apiKey: config.apiKey || '',
|
|
22
14
|
timeout: config.timeout || 10000,
|
|
23
15
|
retryAttempts: config.retryAttempts || 3,
|
|
@@ -28,9 +20,7 @@ class RemoteWorkflowStorage {
|
|
|
28
20
|
if (!config.baseUrl) {
|
|
29
21
|
throw new error_handler_1.SecurityError('baseUrl is required for remote storage', 'config-validation');
|
|
30
22
|
}
|
|
31
|
-
// Validate URL security
|
|
32
23
|
(0, storage_security_1.validateSecureUrl)(config.baseUrl);
|
|
33
|
-
// Validate timeout and retry settings (allow shorter timeouts for testing)
|
|
34
24
|
if (config.timeout && (config.timeout < 100 || config.timeout > 60000)) {
|
|
35
25
|
throw new error_handler_1.SecurityError('timeout must be between 100ms and 60000ms', 'config-validation');
|
|
36
26
|
}
|
|
@@ -42,15 +32,13 @@ class RemoteWorkflowStorage {
|
|
|
42
32
|
try {
|
|
43
33
|
const response = await this.fetchWithRetry('/workflows');
|
|
44
34
|
const data = await this.parseResponse(response);
|
|
45
|
-
// Handle different response formats
|
|
46
35
|
const workflows = data.workflows || data.data || [];
|
|
47
36
|
return this.validateWorkflows(workflows);
|
|
48
37
|
}
|
|
49
38
|
catch (error) {
|
|
50
39
|
if (error instanceof error_handler_1.SecurityError || error instanceof error_handler_1.StorageError) {
|
|
51
|
-
throw error;
|
|
40
|
+
throw error;
|
|
52
41
|
}
|
|
53
|
-
// Transform unknown errors to storage errors for graceful degradation
|
|
54
42
|
throw new error_handler_1.StorageError(`Failed to load workflows from remote registry: ${error.message}`, 'remote-fetch');
|
|
55
43
|
}
|
|
56
44
|
}
|
|
@@ -60,12 +48,11 @@ class RemoteWorkflowStorage {
|
|
|
60
48
|
const response = await this.fetchWithRetry(`/workflows/${encodeURIComponent(sanitizedId)}`);
|
|
61
49
|
if (!response.ok) {
|
|
62
50
|
if (response.status === 404) {
|
|
63
|
-
return null;
|
|
51
|
+
return null;
|
|
64
52
|
}
|
|
65
53
|
throw new error_handler_1.StorageError(`Remote registry returned ${response.status}: ${response.statusText}`, 'remote-fetch');
|
|
66
54
|
}
|
|
67
55
|
const workflow = await this.parseResponse(response);
|
|
68
|
-
// Verify the returned workflow ID matches what we requested
|
|
69
56
|
if (workflow.id !== sanitizedId) {
|
|
70
57
|
throw new error_handler_1.InvalidWorkflowError(sanitizedId, `Registry returned workflow with mismatched ID: ${workflow.id}`);
|
|
71
58
|
}
|
|
@@ -141,9 +128,8 @@ class RemoteWorkflowStorage {
|
|
|
141
128
|
catch (error) {
|
|
142
129
|
lastError = error;
|
|
143
130
|
if (attempt === this.config.retryAttempts) {
|
|
144
|
-
break;
|
|
131
|
+
break;
|
|
145
132
|
}
|
|
146
|
-
// Exponential backoff with jitter
|
|
147
133
|
const delay = Math.pow(2, attempt) * 1000 + Math.random() * 1000;
|
|
148
134
|
await new Promise(resolve => setTimeout(resolve, delay));
|
|
149
135
|
}
|
|
@@ -171,7 +157,6 @@ class RemoteWorkflowStorage {
|
|
|
171
157
|
}
|
|
172
158
|
return workflows.filter((workflow) => {
|
|
173
159
|
try {
|
|
174
|
-
// Basic validation - detailed validation should be handled by schema validator decorator
|
|
175
160
|
if (!workflow || typeof workflow !== 'object') {
|
|
176
161
|
return false;
|
|
177
162
|
}
|
|
@@ -179,12 +164,11 @@ class RemoteWorkflowStorage {
|
|
|
179
164
|
if (!wf.id || !wf.name || !wf.steps) {
|
|
180
165
|
return false;
|
|
181
166
|
}
|
|
182
|
-
// Validate the ID is safe
|
|
183
167
|
(0, storage_security_1.sanitizeId)(wf.id);
|
|
184
168
|
return true;
|
|
185
169
|
}
|
|
186
170
|
catch {
|
|
187
|
-
return false;
|
|
171
|
+
return false;
|
|
188
172
|
}
|
|
189
173
|
});
|
|
190
174
|
}
|
|
@@ -201,7 +185,6 @@ class RemoteWorkflowStorage {
|
|
|
201
185
|
if (!s.id || !s.name) {
|
|
202
186
|
return false;
|
|
203
187
|
}
|
|
204
|
-
// Validate the ID is safe
|
|
205
188
|
(0, storage_security_1.sanitizeId)(s.id);
|
|
206
189
|
return true;
|
|
207
190
|
}
|
|
@@ -217,7 +200,6 @@ class RemoteWorkflowStorage {
|
|
|
217
200
|
if (!workflow.id || !workflow.name || !workflow.steps) {
|
|
218
201
|
throw new error_handler_1.InvalidWorkflowError(workflow.id || 'unknown', 'Workflow must have id, name, and steps');
|
|
219
202
|
}
|
|
220
|
-
// Sanitize the ID
|
|
221
203
|
const sanitizedId = (0, storage_security_1.sanitizeId)(workflow.id);
|
|
222
204
|
return {
|
|
223
205
|
...workflow,
|
|
@@ -226,13 +208,7 @@ class RemoteWorkflowStorage {
|
|
|
226
208
|
}
|
|
227
209
|
}
|
|
228
210
|
exports.RemoteWorkflowStorage = RemoteWorkflowStorage;
|
|
229
|
-
/**
|
|
230
|
-
* Multi-source workflow storage that combines bundled, local, and remote workflows.
|
|
231
|
-
* Uses composition to cleanly separate concerns.
|
|
232
|
-
*/
|
|
233
211
|
class CommunityWorkflowStorage {
|
|
234
|
-
sources;
|
|
235
|
-
remoteStorage;
|
|
236
212
|
constructor(bundledStorage, localStorage, remoteConfig) {
|
|
237
213
|
this.remoteStorage = new RemoteWorkflowStorage(remoteConfig);
|
|
238
214
|
this.sources = [bundledStorage, localStorage, this.remoteStorage];
|
|
@@ -240,13 +216,11 @@ class CommunityWorkflowStorage {
|
|
|
240
216
|
async loadAllWorkflows() {
|
|
241
217
|
const allWorkflows = [];
|
|
242
218
|
const seenIds = new Set();
|
|
243
|
-
// Load from all sources, with later sources taking precedence
|
|
244
219
|
for (const source of this.sources) {
|
|
245
220
|
try {
|
|
246
221
|
const workflows = await source.loadAllWorkflows();
|
|
247
222
|
for (const workflow of workflows) {
|
|
248
223
|
if (seenIds.has(workflow.id)) {
|
|
249
|
-
// Replace existing workflow with same ID
|
|
250
224
|
const existingIndex = allWorkflows.findIndex(wf => wf.id === workflow.id);
|
|
251
225
|
if (existingIndex >= 0) {
|
|
252
226
|
allWorkflows[existingIndex] = workflow;
|
|
@@ -259,8 +233,6 @@ class CommunityWorkflowStorage {
|
|
|
259
233
|
}
|
|
260
234
|
}
|
|
261
235
|
catch (error) {
|
|
262
|
-
// For storage sources, we want to continue even if one fails
|
|
263
|
-
// This is intentional graceful degradation behavior
|
|
264
236
|
if (error instanceof error_handler_1.StorageError) {
|
|
265
237
|
console.warn(`Storage source failed (graceful degradation):`, error.message);
|
|
266
238
|
}
|
|
@@ -274,7 +246,6 @@ class CommunityWorkflowStorage {
|
|
|
274
246
|
async getWorkflowById(id) {
|
|
275
247
|
try {
|
|
276
248
|
const sanitizedId = (0, storage_security_1.sanitizeId)(id);
|
|
277
|
-
// Search in reverse order (later sources take precedence)
|
|
278
249
|
for (let i = this.sources.length - 1; i >= 0; i--) {
|
|
279
250
|
try {
|
|
280
251
|
const workflow = await this.sources[i].getWorkflowById(sanitizedId);
|
|
@@ -283,7 +254,6 @@ class CommunityWorkflowStorage {
|
|
|
283
254
|
}
|
|
284
255
|
}
|
|
285
256
|
catch (error) {
|
|
286
|
-
// Continue searching other sources if one fails
|
|
287
257
|
if (error instanceof error_handler_1.StorageError) {
|
|
288
258
|
console.warn(`Storage source failed for workflow ${sanitizedId}:`, error.message);
|
|
289
259
|
}
|
|
@@ -296,13 +266,12 @@ class CommunityWorkflowStorage {
|
|
|
296
266
|
}
|
|
297
267
|
catch (error) {
|
|
298
268
|
if (error instanceof error_handler_1.SecurityError || error instanceof error_handler_1.InvalidWorkflowError) {
|
|
299
|
-
throw error;
|
|
269
|
+
throw error;
|
|
300
270
|
}
|
|
301
271
|
throw new error_handler_1.StorageError(`Failed to retrieve workflow ${id}: ${error.message}`, 'multi-source-error');
|
|
302
272
|
}
|
|
303
273
|
}
|
|
304
274
|
async listWorkflowSummaries() {
|
|
305
|
-
// Reuse loadAllWorkflows for consistency and caching benefits
|
|
306
275
|
const workflows = await this.loadAllWorkflows();
|
|
307
276
|
return workflows.map(workflow => ({
|
|
308
277
|
id: workflow.id,
|
|
@@ -313,9 +282,7 @@ class CommunityWorkflowStorage {
|
|
|
313
282
|
}));
|
|
314
283
|
}
|
|
315
284
|
async save(workflow) {
|
|
316
|
-
// Delegate to remote storage for publishing
|
|
317
285
|
return this.remoteStorage.save(workflow);
|
|
318
286
|
}
|
|
319
287
|
}
|
|
320
288
|
exports.CommunityWorkflowStorage = CommunityWorkflowStorage;
|
|
321
|
-
//# sourceMappingURL=remote-workflow-storage.js.map
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { IWorkflowStorage } from '../../types/storage';
|
|
2
2
|
import { Workflow } from '../../types/mcp-types';
|
|
3
|
-
/**
|
|
4
|
-
* Decorator that filters or throws when underlying storage returns workflows
|
|
5
|
-
* that do not conform to the JSON schema.
|
|
6
|
-
*/
|
|
7
3
|
export declare class SchemaValidatingWorkflowStorage implements IWorkflowStorage {
|
|
8
4
|
private readonly inner;
|
|
9
5
|
private validator;
|
|
@@ -20,4 +16,3 @@ export declare class SchemaValidatingWorkflowStorage implements IWorkflowStorage
|
|
|
20
16
|
}[]>;
|
|
21
17
|
save?(workflow: Workflow): Promise<void>;
|
|
22
18
|
}
|
|
23
|
-
//# sourceMappingURL=schema-validating-workflow-storage.d.ts.map
|
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.SchemaValidatingWorkflowStorage = void 0;
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const ajv_1 = tslib_1.__importDefault(require("ajv"));
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const ajv_1 = __importDefault(require("ajv"));
|
|
8
10
|
const error_handler_1 = require("../../core/error-handler");
|
|
9
|
-
/**
|
|
10
|
-
* Decorator that filters or throws when underlying storage returns workflows
|
|
11
|
-
* that do not conform to the JSON schema.
|
|
12
|
-
*/
|
|
13
11
|
class SchemaValidatingWorkflowStorage {
|
|
14
|
-
inner;
|
|
15
|
-
validator;
|
|
16
12
|
constructor(inner) {
|
|
17
13
|
this.inner = inner;
|
|
18
14
|
const schemaPath = path_1.default.resolve(__dirname, '../../../spec/workflow.schema.json');
|
|
@@ -34,7 +30,7 @@ class SchemaValidatingWorkflowStorage {
|
|
|
34
30
|
return this.ensureValid(wf);
|
|
35
31
|
}
|
|
36
32
|
catch {
|
|
37
|
-
return false;
|
|
33
|
+
return false;
|
|
38
34
|
}
|
|
39
35
|
});
|
|
40
36
|
}
|
|
@@ -67,4 +63,3 @@ class SchemaValidatingWorkflowStorage {
|
|
|
67
63
|
}
|
|
68
64
|
}
|
|
69
65
|
exports.SchemaValidatingWorkflowStorage = SchemaValidatingWorkflowStorage;
|
|
70
|
-
//# sourceMappingURL=schema-validating-workflow-storage.js.map
|
|
@@ -2,20 +2,6 @@ import { FileWorkflowStorage } from './file-workflow-storage';
|
|
|
2
2
|
import { SchemaValidatingWorkflowStorage } from './schema-validating-workflow-storage';
|
|
3
3
|
import { CachingWorkflowStorage } from './caching-workflow-storage';
|
|
4
4
|
import { MultiDirectoryWorkflowStorage, createMultiDirectoryWorkflowStorage } from './multi-directory-workflow-storage';
|
|
5
|
-
/**
|
|
6
|
-
* Create the default, production-grade storage stack consisting of:
|
|
7
|
-
* 1. Multi-directory workflow storage (bundled + user + project + custom)
|
|
8
|
-
* 2. JSON-Schema validation decorator
|
|
9
|
-
* 3. In-memory TTL cache decorator
|
|
10
|
-
*
|
|
11
|
-
* The function is intentionally side-effect-free – each invocation returns a
|
|
12
|
-
* brand-new, fully-composed instance so that callers can choose whether to
|
|
13
|
-
* share or isolate storage state.
|
|
14
|
-
*/
|
|
15
5
|
export declare function createDefaultWorkflowStorage(): CachingWorkflowStorage;
|
|
16
|
-
/**
|
|
17
|
-
* Create the legacy single-directory storage (for backward compatibility)
|
|
18
|
-
*/
|
|
19
6
|
export declare function createLegacyWorkflowStorage(): CachingWorkflowStorage;
|
|
20
7
|
export { FileWorkflowStorage, SchemaValidatingWorkflowStorage, CachingWorkflowStorage, MultiDirectoryWorkflowStorage, createMultiDirectoryWorkflowStorage };
|
|
21
|
-
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// Re-export new modular storage pieces and compose them to keep backward compatibility
|
|
3
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
3
|
exports.createMultiDirectoryWorkflowStorage = exports.MultiDirectoryWorkflowStorage = exports.CachingWorkflowStorage = exports.SchemaValidatingWorkflowStorage = exports.FileWorkflowStorage = void 0;
|
|
5
4
|
exports.createDefaultWorkflowStorage = createDefaultWorkflowStorage;
|
|
@@ -13,33 +12,15 @@ Object.defineProperty(exports, "CachingWorkflowStorage", { enumerable: true, get
|
|
|
13
12
|
const multi_directory_workflow_storage_1 = require("./multi-directory-workflow-storage");
|
|
14
13
|
Object.defineProperty(exports, "MultiDirectoryWorkflowStorage", { enumerable: true, get: function () { return multi_directory_workflow_storage_1.MultiDirectoryWorkflowStorage; } });
|
|
15
14
|
Object.defineProperty(exports, "createMultiDirectoryWorkflowStorage", { enumerable: true, get: function () { return multi_directory_workflow_storage_1.createMultiDirectoryWorkflowStorage; } });
|
|
16
|
-
// (intentionally left blank – no direct dependency on Workflow types here)
|
|
17
|
-
// -----------------------------------------------------------------------------
|
|
18
|
-
// Default composition helper – now exposed as a factory for DI friendliness
|
|
19
|
-
// -----------------------------------------------------------------------------
|
|
20
|
-
/**
|
|
21
|
-
* Create the default, production-grade storage stack consisting of:
|
|
22
|
-
* 1. Multi-directory workflow storage (bundled + user + project + custom)
|
|
23
|
-
* 2. JSON-Schema validation decorator
|
|
24
|
-
* 3. In-memory TTL cache decorator
|
|
25
|
-
*
|
|
26
|
-
* The function is intentionally side-effect-free – each invocation returns a
|
|
27
|
-
* brand-new, fully-composed instance so that callers can choose whether to
|
|
28
|
-
* share or isolate storage state.
|
|
29
|
-
*/
|
|
30
15
|
function createDefaultWorkflowStorage() {
|
|
31
16
|
const baseStorage = (0, multi_directory_workflow_storage_1.createMultiDirectoryWorkflowStorage)();
|
|
32
17
|
const validatingStorage = new schema_validating_workflow_storage_1.SchemaValidatingWorkflowStorage(baseStorage);
|
|
33
|
-
const cacheTtlMs = Number(process.env['CACHE_TTL'] ??
|
|
18
|
+
const cacheTtlMs = Number(process.env['CACHE_TTL'] ?? 300000);
|
|
34
19
|
return new caching_workflow_storage_1.CachingWorkflowStorage(validatingStorage, cacheTtlMs);
|
|
35
20
|
}
|
|
36
|
-
/**
|
|
37
|
-
* Create the legacy single-directory storage (for backward compatibility)
|
|
38
|
-
*/
|
|
39
21
|
function createLegacyWorkflowStorage() {
|
|
40
22
|
const baseStorage = (0, file_workflow_storage_1.createDefaultFileWorkflowStorage)();
|
|
41
23
|
const validatingStorage = new schema_validating_workflow_storage_1.SchemaValidatingWorkflowStorage(baseStorage);
|
|
42
|
-
const cacheTtlMs = Number(process.env['CACHE_TTL'] ??
|
|
24
|
+
const cacheTtlMs = Number(process.env['CACHE_TTL'] ?? 300000);
|
|
43
25
|
return new caching_workflow_storage_1.CachingWorkflowStorage(validatingStorage, cacheTtlMs);
|
|
44
26
|
}
|
|
45
|
-
//# sourceMappingURL=storage.js.map
|
package/dist/mcp-server.d.ts
CHANGED