@mhingston5/conduit 1.0.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/.env.example +13 -0
- package/.github/workflows/ci.yml +88 -0
- package/.github/workflows/pr-checks.yml +90 -0
- package/.tool-versions +2 -0
- package/README.md +177 -0
- package/conduit.yaml.test +3 -0
- package/docs/ARCHITECTURE.md +35 -0
- package/docs/CODE_MODE.md +33 -0
- package/docs/SECURITY.md +52 -0
- package/logo.png +0 -0
- package/package.json +74 -0
- package/src/assets/deno-shim.ts +93 -0
- package/src/assets/python-shim.py +21 -0
- package/src/core/asset.utils.ts +42 -0
- package/src/core/concurrency.service.ts +70 -0
- package/src/core/config.service.ts +147 -0
- package/src/core/execution.context.ts +37 -0
- package/src/core/execution.service.ts +209 -0
- package/src/core/interfaces/app.config.ts +17 -0
- package/src/core/interfaces/executor.interface.ts +31 -0
- package/src/core/interfaces/middleware.interface.ts +12 -0
- package/src/core/interfaces/url.validator.interface.ts +3 -0
- package/src/core/logger.ts +64 -0
- package/src/core/metrics.service.ts +112 -0
- package/src/core/middleware/auth.middleware.ts +56 -0
- package/src/core/middleware/error.middleware.ts +21 -0
- package/src/core/middleware/logging.middleware.ts +25 -0
- package/src/core/middleware/middleware.builder.ts +24 -0
- package/src/core/middleware/ratelimit.middleware.ts +31 -0
- package/src/core/network.policy.service.ts +106 -0
- package/src/core/ops.server.ts +74 -0
- package/src/core/otel.service.ts +41 -0
- package/src/core/policy.service.ts +77 -0
- package/src/core/registries/executor.registry.ts +26 -0
- package/src/core/request.controller.ts +297 -0
- package/src/core/security.service.ts +68 -0
- package/src/core/session.manager.ts +44 -0
- package/src/core/types.ts +47 -0
- package/src/executors/deno.executor.ts +342 -0
- package/src/executors/isolate.executor.ts +281 -0
- package/src/executors/pyodide.executor.ts +327 -0
- package/src/executors/pyodide.worker.ts +195 -0
- package/src/gateway/auth.service.ts +104 -0
- package/src/gateway/gateway.service.ts +345 -0
- package/src/gateway/schema.cache.ts +46 -0
- package/src/gateway/upstream.client.ts +244 -0
- package/src/index.ts +92 -0
- package/src/sdk/index.ts +2 -0
- package/src/sdk/sdk-generator.ts +245 -0
- package/src/sdk/tool-binding.ts +86 -0
- package/src/transport/socket.transport.ts +203 -0
- package/tests/__snapshots__/assets.test.ts.snap +97 -0
- package/tests/assets.test.ts +50 -0
- package/tests/auth.service.test.ts +78 -0
- package/tests/code-mode-lite-execution.test.ts +84 -0
- package/tests/code-mode-lite-gateway.test.ts +150 -0
- package/tests/concurrency.service.test.ts +50 -0
- package/tests/concurrency.test.ts +41 -0
- package/tests/config.service.test.ts +70 -0
- package/tests/contract.test.ts +43 -0
- package/tests/deno.executor.test.ts +68 -0
- package/tests/deno_hardening.test.ts +45 -0
- package/tests/dynamic.tool.test.ts +237 -0
- package/tests/e2e_stdio_upstream.test.ts +197 -0
- package/tests/fixtures/stdio-server.ts +42 -0
- package/tests/gateway.manifest.test.ts +82 -0
- package/tests/gateway.service.test.ts +58 -0
- package/tests/gateway.strict.unit.test.ts +74 -0
- package/tests/gateway.validation.unit.test.ts +89 -0
- package/tests/gateway_validation.test.ts +86 -0
- package/tests/hardening.test.ts +139 -0
- package/tests/hardening_v1.test.ts +72 -0
- package/tests/isolate.executor.test.ts +100 -0
- package/tests/log-limit.test.ts +55 -0
- package/tests/middleware.test.ts +106 -0
- package/tests/ops.server.test.ts +65 -0
- package/tests/policy.service.test.ts +90 -0
- package/tests/pyodide.executor.test.ts +101 -0
- package/tests/reference_mcp.ts +40 -0
- package/tests/remediation.test.ts +119 -0
- package/tests/routing.test.ts +148 -0
- package/tests/schema.cache.test.ts +27 -0
- package/tests/sdk/sdk-generator.test.ts +205 -0
- package/tests/socket.transport.test.ts +182 -0
- package/tests/stdio_upstream.test.ts +54 -0
- package/tsconfig.json +25 -0
- package/tsup.config.ts +22 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { ExecutionContext } from './execution.context.js';
|
|
2
|
+
import { Logger } from 'pino';
|
|
3
|
+
|
|
4
|
+
import { GatewayService } from '../gateway/gateway.service.js';
|
|
5
|
+
import { metrics } from './metrics.service.js';
|
|
6
|
+
import { ExecutionService } from './execution.service.js';
|
|
7
|
+
|
|
8
|
+
import { Middleware } from './interfaces/middleware.interface.js';
|
|
9
|
+
|
|
10
|
+
import { ConduitError, JSONRPCRequest, JSONRPCResponse } from './types.js';
|
|
11
|
+
|
|
12
|
+
export { ConduitError, JSONRPCRequest, JSONRPCResponse };
|
|
13
|
+
|
|
14
|
+
export class RequestController {
|
|
15
|
+
private logger: Logger;
|
|
16
|
+
private executionService: ExecutionService;
|
|
17
|
+
private gatewayService: GatewayService;
|
|
18
|
+
|
|
19
|
+
private middlewares: Middleware[] = [];
|
|
20
|
+
|
|
21
|
+
constructor(
|
|
22
|
+
logger: Logger,
|
|
23
|
+
executionService: ExecutionService,
|
|
24
|
+
gatewayService: GatewayService,
|
|
25
|
+
middlewares: Middleware[] = []
|
|
26
|
+
) {
|
|
27
|
+
this.logger = logger;
|
|
28
|
+
this.executionService = executionService;
|
|
29
|
+
this.gatewayService = gatewayService;
|
|
30
|
+
this.middlewares = middlewares;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
use(middleware: Middleware) {
|
|
34
|
+
this.middlewares.push(middleware);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
async handleRequest(request: JSONRPCRequest, context: ExecutionContext): Promise<JSONRPCResponse> {
|
|
40
|
+
return this.executePipeline(request, context);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private async executePipeline(request: JSONRPCRequest, context: ExecutionContext): Promise<JSONRPCResponse> {
|
|
44
|
+
let index = -1;
|
|
45
|
+
|
|
46
|
+
const dispatch = async (i: number): Promise<JSONRPCResponse> => {
|
|
47
|
+
if (i <= index) throw new Error('next() called multiple times');
|
|
48
|
+
index = i;
|
|
49
|
+
|
|
50
|
+
const middleware = this.middlewares[i];
|
|
51
|
+
if (middleware) {
|
|
52
|
+
return middleware.handle(request, context, () => dispatch(i + 1));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return this.finalHandler(request, context);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
return dispatch(0);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private async handleValidateTool(request: JSONRPCRequest, context: ExecutionContext): Promise<JSONRPCResponse> {
|
|
62
|
+
const params = request.params as { toolName: string; args: any };
|
|
63
|
+
if (!params || !params.toolName || !params.args) {
|
|
64
|
+
return {
|
|
65
|
+
jsonrpc: '2.0',
|
|
66
|
+
id: request.id,
|
|
67
|
+
error: {
|
|
68
|
+
code: -32602,
|
|
69
|
+
message: 'Missing toolName or args params',
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const result = await this.gatewayService.validateTool(params.toolName, params.args, context);
|
|
76
|
+
return {
|
|
77
|
+
jsonrpc: '2.0',
|
|
78
|
+
id: request.id,
|
|
79
|
+
result,
|
|
80
|
+
};
|
|
81
|
+
} catch (error: any) {
|
|
82
|
+
return {
|
|
83
|
+
jsonrpc: '2.0',
|
|
84
|
+
id: request.id,
|
|
85
|
+
error: {
|
|
86
|
+
code: -32603,
|
|
87
|
+
message: error.message || 'Validation failed',
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private async finalHandler(request: JSONRPCRequest, context: ExecutionContext): Promise<JSONRPCResponse> {
|
|
94
|
+
const { method, params, id } = request;
|
|
95
|
+
// Logging and metrics handled by middlewares now
|
|
96
|
+
|
|
97
|
+
// Try/catch handled by ErrorMiddleware, but we handle logic errors here if needed
|
|
98
|
+
// Actually routing logic should just throw and let middleware catch?
|
|
99
|
+
// Or specific logic.
|
|
100
|
+
|
|
101
|
+
switch (method) {
|
|
102
|
+
case 'mcp.discoverTools':
|
|
103
|
+
return this.handleDiscoverTools(params, context, id);
|
|
104
|
+
case 'mcp.listToolPackages':
|
|
105
|
+
return this.handleListToolPackages(params, context, id);
|
|
106
|
+
case 'mcp.listToolStubs':
|
|
107
|
+
return this.handleListToolStubs(params, context, id);
|
|
108
|
+
case 'mcp.readToolSchema':
|
|
109
|
+
return this.handleReadToolSchema(params, context, id);
|
|
110
|
+
case 'mcp.validateTool':
|
|
111
|
+
return this.handleValidateTool(request, context);
|
|
112
|
+
case 'mcp.callTool':
|
|
113
|
+
return this.handleCallTool(params, context, id);
|
|
114
|
+
case 'mcp.executeTypeScript':
|
|
115
|
+
return this.handleExecuteTypeScript(params, context, id);
|
|
116
|
+
case 'mcp.executePython':
|
|
117
|
+
return this.handleExecutePython(params, context, id);
|
|
118
|
+
case 'mcp.executeIsolate':
|
|
119
|
+
return this.handleExecuteIsolate(params, context, id);
|
|
120
|
+
default:
|
|
121
|
+
// metrics.recordExecutionEnd is handled by LoggingMiddleware??
|
|
122
|
+
// Wait, if 404, LoggingMiddleware records execution end?
|
|
123
|
+
// Yes, handle() in LoggingMiddleware wraps next().
|
|
124
|
+
return this.errorResponse(id, -32601, `Method not found: ${method}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private async handleDiscoverTools(params: any, context: ExecutionContext, id: string | number): Promise<JSONRPCResponse> {
|
|
129
|
+
const tools = await this.gatewayService.discoverTools(context);
|
|
130
|
+
return {
|
|
131
|
+
jsonrpc: '2.0',
|
|
132
|
+
id,
|
|
133
|
+
result: {
|
|
134
|
+
tools,
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private async handleListToolPackages(params: any, context: ExecutionContext, id: string | number): Promise<JSONRPCResponse> {
|
|
140
|
+
const packages = await this.gatewayService.listToolPackages();
|
|
141
|
+
return {
|
|
142
|
+
jsonrpc: '2.0',
|
|
143
|
+
id,
|
|
144
|
+
result: {
|
|
145
|
+
packages
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private async handleListToolStubs(params: any, context: ExecutionContext, id: string | number): Promise<JSONRPCResponse> {
|
|
151
|
+
const { packageId } = params;
|
|
152
|
+
if (!packageId) {
|
|
153
|
+
return this.errorResponse(id, -32602, 'Missing packageId parameter');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
const stubs = await this.gatewayService.listToolStubs(packageId, context);
|
|
158
|
+
return {
|
|
159
|
+
jsonrpc: '2.0',
|
|
160
|
+
id,
|
|
161
|
+
result: {
|
|
162
|
+
stubs
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
} catch (error: any) {
|
|
166
|
+
return this.errorResponse(id, -32001, error.message);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private async handleReadToolSchema(params: any, context: ExecutionContext, id: string | number): Promise<JSONRPCResponse> {
|
|
171
|
+
const { toolId } = params;
|
|
172
|
+
if (!toolId) {
|
|
173
|
+
return this.errorResponse(id, -32602, 'Missing toolId parameter');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
const schema = await this.gatewayService.getToolSchema(toolId, context);
|
|
178
|
+
if (!schema) {
|
|
179
|
+
return this.errorResponse(id, -32001, `Tool not found: ${toolId}`);
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
jsonrpc: '2.0',
|
|
183
|
+
id,
|
|
184
|
+
result: {
|
|
185
|
+
schema
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
} catch (error: any) {
|
|
189
|
+
return this.errorResponse(id, -32003, error.message);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private async handleCallTool(params: any, context: ExecutionContext, id: string | number): Promise<JSONRPCResponse> {
|
|
194
|
+
const { name, arguments: toolArgs } = params;
|
|
195
|
+
const response = await this.gatewayService.callTool(name, toolArgs, context);
|
|
196
|
+
return { ...response, id };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private async handleExecuteTypeScript(params: any, context: ExecutionContext, id: string | number): Promise<JSONRPCResponse> {
|
|
200
|
+
const { code, limits, allowedTools } = params;
|
|
201
|
+
|
|
202
|
+
if (Array.isArray(allowedTools)) {
|
|
203
|
+
context.allowedTools = allowedTools;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const result = await this.executionService.executeTypeScript(code, limits, context, allowedTools);
|
|
207
|
+
|
|
208
|
+
if (result.error) {
|
|
209
|
+
return this.errorResponse(id, result.error.code, result.error.message);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
jsonrpc: '2.0',
|
|
214
|
+
id,
|
|
215
|
+
result: {
|
|
216
|
+
stdout: result.stdout,
|
|
217
|
+
stderr: result.stderr,
|
|
218
|
+
exitCode: result.exitCode,
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private async handleExecutePython(params: any, context: ExecutionContext, id: string | number): Promise<JSONRPCResponse> {
|
|
224
|
+
const { code, limits, allowedTools } = params;
|
|
225
|
+
|
|
226
|
+
if (Array.isArray(allowedTools)) {
|
|
227
|
+
context.allowedTools = allowedTools;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const result = await this.executionService.executePython(code, limits, context, allowedTools);
|
|
231
|
+
|
|
232
|
+
if (result.error) {
|
|
233
|
+
return this.errorResponse(id, result.error.code, result.error.message);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
jsonrpc: '2.0',
|
|
238
|
+
id,
|
|
239
|
+
result: {
|
|
240
|
+
stdout: result.stdout,
|
|
241
|
+
stderr: result.stderr,
|
|
242
|
+
exitCode: result.exitCode,
|
|
243
|
+
},
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
private async handleExecuteIsolate(params: any, context: ExecutionContext, id: string | number): Promise<JSONRPCResponse> {
|
|
248
|
+
const { code, limits, allowedTools } = params;
|
|
249
|
+
|
|
250
|
+
if (Array.isArray(allowedTools)) {
|
|
251
|
+
context.allowedTools = allowedTools;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const result = await this.executionService.executeIsolate(code, limits, context, allowedTools);
|
|
255
|
+
|
|
256
|
+
if (result.error) {
|
|
257
|
+
return this.errorResponse(id, result.error.code, result.error.message);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
jsonrpc: '2.0',
|
|
262
|
+
id,
|
|
263
|
+
result: {
|
|
264
|
+
stdout: result.stdout,
|
|
265
|
+
stderr: result.stderr,
|
|
266
|
+
exitCode: result.exitCode,
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
private errorResponse(id: string | number, code: number, message: string): JSONRPCResponse {
|
|
272
|
+
return {
|
|
273
|
+
jsonrpc: '2.0',
|
|
274
|
+
id,
|
|
275
|
+
error: {
|
|
276
|
+
code,
|
|
277
|
+
message,
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async shutdown() {
|
|
283
|
+
await this.executionService.shutdown();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
async healthCheck() {
|
|
287
|
+
const pyodideHealth = await this.executionService.healthCheck();
|
|
288
|
+
return {
|
|
289
|
+
status: pyodideHealth.status === 'ok' ? 'ok' : 'error',
|
|
290
|
+
pyodide: pyodideHealth
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
async warmup() {
|
|
295
|
+
await this.executionService.warmup();
|
|
296
|
+
}
|
|
297
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Logger } from 'pino';
|
|
2
|
+
import { NetworkPolicyService } from './network.policy.service.js';
|
|
3
|
+
import { SessionManager, Session } from './session.manager.js';
|
|
4
|
+
import { IUrlValidator } from './interfaces/url.validator.interface.js';
|
|
5
|
+
import crypto from 'node:crypto';
|
|
6
|
+
|
|
7
|
+
export { Session };
|
|
8
|
+
|
|
9
|
+
export class SecurityService implements IUrlValidator {
|
|
10
|
+
private logger: Logger;
|
|
11
|
+
private ipcToken: string;
|
|
12
|
+
private networkPolicy: NetworkPolicyService;
|
|
13
|
+
private sessionManager: SessionManager;
|
|
14
|
+
|
|
15
|
+
constructor(logger: Logger, ipcToken: string) {
|
|
16
|
+
this.logger = logger;
|
|
17
|
+
this.ipcToken = ipcToken;
|
|
18
|
+
this.networkPolicy = new NetworkPolicyService(logger);
|
|
19
|
+
this.sessionManager = new SessionManager(logger);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
validateCode(code: string): { valid: boolean; message?: string } {
|
|
23
|
+
// [IMPORTANT] This is a SANITY CHECK only.
|
|
24
|
+
// We rely on RUNTIME isolation (Deno permissions, Isolate context) for actual security.
|
|
25
|
+
// Static analysis of code is fundamentally unable to prevent all sandbox escapes.
|
|
26
|
+
if (!code || code.length > 1024 * 1024) { // 1MB limit for sanity
|
|
27
|
+
return { valid: false, message: 'Code size exceeds limit or is empty' };
|
|
28
|
+
}
|
|
29
|
+
return { valid: true };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async validateUrl(url: string): Promise<{ valid: boolean; message?: string; resolvedIp?: string }> {
|
|
33
|
+
return this.networkPolicy.validateUrl(url);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
checkRateLimit(key: string): boolean {
|
|
37
|
+
return this.networkPolicy.checkRateLimit(key);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
validateIpcToken(token: string): boolean {
|
|
41
|
+
// Fix Sev1: Use timing-safe comparison for sensitive tokens
|
|
42
|
+
const expected = Buffer.from(this.ipcToken);
|
|
43
|
+
const actual = Buffer.from(token);
|
|
44
|
+
|
|
45
|
+
if (expected.length === actual.length && crypto.timingSafeEqual(expected, actual)) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return !!this.sessionManager.getSession(token);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
createSession(allowedTools?: string[]): string {
|
|
53
|
+
return this.sessionManager.createSession(allowedTools);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
getSession(token: string): Session | undefined {
|
|
57
|
+
return this.sessionManager.getSession(token);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
invalidateSession(token: string): void {
|
|
61
|
+
this.sessionManager.invalidateSession(token);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
getIpcToken(): string {
|
|
66
|
+
return this.ipcToken;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Logger } from 'pino';
|
|
2
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
3
|
+
import { LRUCache } from 'lru-cache';
|
|
4
|
+
|
|
5
|
+
export interface Session {
|
|
6
|
+
allowedTools?: string[];
|
|
7
|
+
createdAt: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class SessionManager {
|
|
11
|
+
private logger: Logger;
|
|
12
|
+
private sessions: LRUCache<string, Session>;
|
|
13
|
+
private readonly SESSION_TTL_MS = 3600000; // 1 hour
|
|
14
|
+
|
|
15
|
+
constructor(logger: Logger) {
|
|
16
|
+
this.logger = logger;
|
|
17
|
+
this.sessions = new LRUCache({
|
|
18
|
+
max: 10000,
|
|
19
|
+
ttl: this.SESSION_TTL_MS,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
createSession(allowedTools?: string[]): string {
|
|
24
|
+
const token = uuidv4();
|
|
25
|
+
this.sessions.set(token, {
|
|
26
|
+
allowedTools,
|
|
27
|
+
createdAt: Date.now()
|
|
28
|
+
});
|
|
29
|
+
return token;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
getSession(token: string): Session | undefined {
|
|
33
|
+
return this.sessions.get(token);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
invalidateSession(token: string): void {
|
|
37
|
+
this.sessions.delete(token);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
cleanupSessions() {
|
|
41
|
+
// LRUCache handles this automatically via TTL
|
|
42
|
+
this.sessions.purgeStale();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export enum ConduitError {
|
|
2
|
+
InternalError = -32603,
|
|
3
|
+
RequestTimeout = -32008,
|
|
4
|
+
Forbidden = -32003,
|
|
5
|
+
OutputLimitExceeded = -32013,
|
|
6
|
+
MemoryLimitExceeded = -32009,
|
|
7
|
+
LogLimitExceeded = -32014,
|
|
8
|
+
ServerBusy = -32000,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface JSONRPCRequest {
|
|
12
|
+
jsonrpc: '2.0';
|
|
13
|
+
id: string | number;
|
|
14
|
+
method: string;
|
|
15
|
+
params?: any;
|
|
16
|
+
auth?: {
|
|
17
|
+
bearerToken: string;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface JSONRPCResponse {
|
|
22
|
+
jsonrpc: '2.0';
|
|
23
|
+
id: string | number;
|
|
24
|
+
result?: any;
|
|
25
|
+
error?: {
|
|
26
|
+
code: number;
|
|
27
|
+
message: string;
|
|
28
|
+
data?: any;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ToolPackage {
|
|
33
|
+
id: string; // e.g., "github"
|
|
34
|
+
description?: string;
|
|
35
|
+
version?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface ToolStub {
|
|
39
|
+
id: string; // e.g., "github__create_issue"
|
|
40
|
+
name: string; // e.g., "create_issue"
|
|
41
|
+
description?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface ToolManifest {
|
|
45
|
+
version: string;
|
|
46
|
+
tools: ToolStub[];
|
|
47
|
+
}
|