cognitive-modules-cli 2.2.1 → 2.2.7
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/CHANGELOG.md +11 -0
- package/LICENSE +21 -0
- package/README.md +35 -29
- package/dist/cli.js +519 -23
- package/dist/commands/add.d.ts +33 -14
- package/dist/commands/add.js +383 -16
- package/dist/commands/compose.js +60 -23
- package/dist/commands/index.d.ts +4 -0
- package/dist/commands/index.js +4 -0
- package/dist/commands/init.js +23 -1
- package/dist/commands/migrate.d.ts +30 -0
- package/dist/commands/migrate.js +650 -0
- package/dist/commands/pipe.d.ts +1 -0
- package/dist/commands/pipe.js +31 -11
- package/dist/commands/remove.js +33 -2
- package/dist/commands/run.d.ts +2 -0
- package/dist/commands/run.js +61 -28
- package/dist/commands/search.d.ts +28 -0
- package/dist/commands/search.js +143 -0
- package/dist/commands/test.d.ts +65 -0
- package/dist/commands/test.js +454 -0
- package/dist/commands/update.d.ts +1 -0
- package/dist/commands/update.js +106 -14
- package/dist/commands/validate.d.ts +36 -0
- package/dist/commands/validate.js +97 -0
- package/dist/errors/index.d.ts +225 -0
- package/dist/errors/index.js +420 -0
- package/dist/mcp/server.js +84 -79
- package/dist/modules/composition.js +97 -32
- package/dist/modules/loader.js +4 -2
- package/dist/modules/runner.d.ts +72 -5
- package/dist/modules/runner.js +306 -59
- package/dist/modules/subagent.d.ts +6 -1
- package/dist/modules/subagent.js +18 -13
- package/dist/modules/validator.js +14 -6
- package/dist/providers/anthropic.d.ts +15 -0
- package/dist/providers/anthropic.js +147 -5
- package/dist/providers/base.d.ts +11 -0
- package/dist/providers/base.js +18 -0
- package/dist/providers/gemini.d.ts +15 -0
- package/dist/providers/gemini.js +122 -5
- package/dist/providers/ollama.d.ts +15 -0
- package/dist/providers/ollama.js +111 -3
- package/dist/providers/openai.d.ts +11 -0
- package/dist/providers/openai.js +133 -0
- package/dist/registry/client.d.ts +212 -0
- package/dist/registry/client.js +359 -0
- package/dist/registry/index.d.ts +4 -0
- package/dist/registry/index.js +4 -0
- package/dist/registry/tar.d.ts +8 -0
- package/dist/registry/tar.js +353 -0
- package/dist/server/http.js +301 -45
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.js +1 -0
- package/dist/server/sse.d.ts +13 -0
- package/dist/server/sse.js +22 -0
- package/dist/types.d.ts +32 -1
- package/dist/types.js +4 -1
- package/dist/version.d.ts +1 -0
- package/dist/version.js +4 -0
- package/package.json +31 -7
- package/dist/modules/composition.test.d.ts +0 -11
- package/dist/modules/composition.test.js +0 -450
- package/dist/modules/policy.test.d.ts +0 -10
- package/dist/modules/policy.test.js +0 -369
- package/src/cli.ts +0 -471
- package/src/commands/add.ts +0 -315
- package/src/commands/compose.ts +0 -185
- package/src/commands/index.ts +0 -13
- package/src/commands/init.ts +0 -94
- package/src/commands/list.ts +0 -33
- package/src/commands/pipe.ts +0 -76
- package/src/commands/remove.ts +0 -57
- package/src/commands/run.ts +0 -80
- package/src/commands/update.ts +0 -130
- package/src/commands/versions.ts +0 -79
- package/src/index.ts +0 -90
- package/src/mcp/index.ts +0 -5
- package/src/mcp/server.ts +0 -403
- package/src/modules/composition.test.ts +0 -558
- package/src/modules/composition.ts +0 -1674
- package/src/modules/index.ts +0 -9
- package/src/modules/loader.ts +0 -508
- package/src/modules/policy.test.ts +0 -455
- package/src/modules/runner.ts +0 -1983
- package/src/modules/subagent.ts +0 -277
- package/src/modules/validator.ts +0 -700
- package/src/providers/anthropic.ts +0 -89
- package/src/providers/base.ts +0 -29
- package/src/providers/deepseek.ts +0 -83
- package/src/providers/gemini.ts +0 -117
- package/src/providers/index.ts +0 -78
- package/src/providers/minimax.ts +0 -81
- package/src/providers/moonshot.ts +0 -82
- package/src/providers/ollama.ts +0 -83
- package/src/providers/openai.ts +0 -84
- package/src/providers/qwen.ts +0 -82
- package/src/server/http.ts +0 -316
- package/src/server/index.ts +0 -6
- package/src/types.ts +0 -599
- package/tsconfig.json +0 -17
package/dist/server/http.js
CHANGED
|
@@ -13,24 +13,65 @@
|
|
|
13
13
|
import http from 'node:http';
|
|
14
14
|
import { URL } from 'node:url';
|
|
15
15
|
import { findModule, listModules, getDefaultSearchPaths } from '../modules/loader.js';
|
|
16
|
-
import { runModule } from '../modules/runner.js';
|
|
16
|
+
import { runModule, runModuleStream } from '../modules/runner.js';
|
|
17
17
|
import { getProvider } from '../providers/index.js';
|
|
18
|
+
import { VERSION } from '../version.js';
|
|
19
|
+
import { ErrorCodes, attachContext, makeErrorEnvelope, makeHttpError, httpStatusForErrorCode } from '../errors/index.js';
|
|
20
|
+
import { encodeSseFrame } from './sse.js';
|
|
21
|
+
// Supported protocol versions
|
|
22
|
+
const SUPPORTED_VERSIONS = ['2.2'];
|
|
23
|
+
const DEFAULT_VERSION = '2.2';
|
|
24
|
+
/**
|
|
25
|
+
* Get requested protocol version from request
|
|
26
|
+
* Priority: body.version > X-Cognitive-Version header > query param > default
|
|
27
|
+
*/
|
|
28
|
+
function getRequestedVersion(req, url, bodyVersion) {
|
|
29
|
+
// Body version takes priority
|
|
30
|
+
if (bodyVersion && SUPPORTED_VERSIONS.includes(bodyVersion)) {
|
|
31
|
+
return bodyVersion;
|
|
32
|
+
}
|
|
33
|
+
// Check header
|
|
34
|
+
const headerVersion = req.headers['x-cognitive-version'];
|
|
35
|
+
if (headerVersion && SUPPORTED_VERSIONS.includes(headerVersion)) {
|
|
36
|
+
return headerVersion;
|
|
37
|
+
}
|
|
38
|
+
// Check query param
|
|
39
|
+
const queryVersion = url.searchParams.get('version');
|
|
40
|
+
if (queryVersion && SUPPORTED_VERSIONS.includes(queryVersion)) {
|
|
41
|
+
return queryVersion;
|
|
42
|
+
}
|
|
43
|
+
return DEFAULT_VERSION;
|
|
44
|
+
}
|
|
18
45
|
// =============================================================================
|
|
19
46
|
// Helpers
|
|
20
47
|
// =============================================================================
|
|
21
|
-
function jsonResponse(res, status, data) {
|
|
48
|
+
function jsonResponse(res, status, data, protocolVersion = DEFAULT_VERSION) {
|
|
22
49
|
res.writeHead(status, {
|
|
23
50
|
'Content-Type': 'application/json',
|
|
24
51
|
'Access-Control-Allow-Origin': '*',
|
|
25
52
|
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
|
26
|
-
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
|
53
|
+
'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-Cognitive-Version',
|
|
54
|
+
'X-Cognitive-Version': protocolVersion,
|
|
27
55
|
});
|
|
28
56
|
res.end(JSON.stringify(data, null, 2));
|
|
29
57
|
}
|
|
30
|
-
|
|
58
|
+
const MAX_BODY_BYTES = 1024 * 1024; // 1MB
|
|
59
|
+
function parseBody(req, maxBytes = MAX_BODY_BYTES) {
|
|
31
60
|
return new Promise((resolve, reject) => {
|
|
32
61
|
let body = '';
|
|
33
|
-
|
|
62
|
+
let received = 0;
|
|
63
|
+
req.on('data', (chunk) => {
|
|
64
|
+
const chunkSize = typeof chunk === 'string' ? Buffer.byteLength(chunk) : chunk.length;
|
|
65
|
+
received += chunkSize;
|
|
66
|
+
if (received > maxBytes) {
|
|
67
|
+
const err = new Error('Payload too large');
|
|
68
|
+
err.code = 'PAYLOAD_TOO_LARGE';
|
|
69
|
+
req.destroy(err);
|
|
70
|
+
reject(err);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
body += chunk;
|
|
74
|
+
});
|
|
34
75
|
req.on('end', () => resolve(body));
|
|
35
76
|
req.on('error', reject);
|
|
36
77
|
});
|
|
@@ -51,7 +92,16 @@ function verifyApiKey(req) {
|
|
|
51
92
|
async function handleRoot(res) {
|
|
52
93
|
jsonResponse(res, 200, {
|
|
53
94
|
name: 'Cognitive Modules API',
|
|
54
|
-
version:
|
|
95
|
+
version: VERSION,
|
|
96
|
+
protocol: {
|
|
97
|
+
version: DEFAULT_VERSION,
|
|
98
|
+
supported: SUPPORTED_VERSIONS,
|
|
99
|
+
negotiation: {
|
|
100
|
+
header: 'X-Cognitive-Version',
|
|
101
|
+
query: '?version=2.2',
|
|
102
|
+
body: 'version field in request body',
|
|
103
|
+
},
|
|
104
|
+
},
|
|
55
105
|
docs: '/docs',
|
|
56
106
|
endpoints: {
|
|
57
107
|
run: 'POST /run',
|
|
@@ -72,7 +122,7 @@ async function handleHealth(res) {
|
|
|
72
122
|
};
|
|
73
123
|
jsonResponse(res, 200, {
|
|
74
124
|
status: 'healthy',
|
|
75
|
-
version:
|
|
125
|
+
version: VERSION,
|
|
76
126
|
providers,
|
|
77
127
|
});
|
|
78
128
|
}
|
|
@@ -95,7 +145,12 @@ async function handleModules(res, searchPaths) {
|
|
|
95
145
|
async function handleModuleInfo(res, moduleName, searchPaths) {
|
|
96
146
|
const moduleData = await findModule(moduleName, searchPaths);
|
|
97
147
|
if (!moduleData) {
|
|
98
|
-
|
|
148
|
+
const envelope = makeErrorEnvelope({
|
|
149
|
+
code: ErrorCodes.MODULE_NOT_FOUND,
|
|
150
|
+
message: `Module '${moduleName}' not found`,
|
|
151
|
+
suggestion: 'Use GET /modules to list available modules',
|
|
152
|
+
});
|
|
153
|
+
jsonResponse(res, 404, envelope);
|
|
99
154
|
return;
|
|
100
155
|
}
|
|
101
156
|
jsonResponse(res, 200, {
|
|
@@ -110,12 +165,30 @@ async function handleModuleInfo(res, moduleName, searchPaths) {
|
|
|
110
165
|
outputSchema: moduleData.outputSchema,
|
|
111
166
|
});
|
|
112
167
|
}
|
|
113
|
-
async function handleRun(req, res, searchPaths) {
|
|
168
|
+
async function handleRun(req, res, searchPaths, url) {
|
|
169
|
+
// Version will be determined after parsing body
|
|
170
|
+
let protocolVersion = DEFAULT_VERSION;
|
|
171
|
+
// Helper to build error envelope using unified error factory
|
|
172
|
+
const buildHttpError = (code, message, options = {}) => {
|
|
173
|
+
const moduleName = options.moduleName ?? request?.module ?? 'unknown';
|
|
174
|
+
const providerName = options.providerName ?? request?.provider ?? 'unknown';
|
|
175
|
+
const [status, envelope] = makeHttpError({
|
|
176
|
+
code,
|
|
177
|
+
message,
|
|
178
|
+
version: protocolVersion,
|
|
179
|
+
suggestion: options.suggestion,
|
|
180
|
+
recoverable: options.recoverable,
|
|
181
|
+
retry_after_ms: options.retry_after_ms,
|
|
182
|
+
module: moduleName,
|
|
183
|
+
provider: providerName,
|
|
184
|
+
});
|
|
185
|
+
return [status, envelope];
|
|
186
|
+
};
|
|
114
187
|
// Verify API key
|
|
115
188
|
if (!verifyApiKey(req)) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
189
|
+
const [status, body] = buildHttpError(ErrorCodes.PERMISSION_DENIED, 'Missing or invalid API Key', { suggestion: 'Use header: Authorization: Bearer <your-api-key>' });
|
|
190
|
+
// Auth failures are better represented as 401 even if the CEP code is permission-related.
|
|
191
|
+
jsonResponse(res, 401, body, protocolVersion);
|
|
119
192
|
return;
|
|
120
193
|
}
|
|
121
194
|
// Parse request body
|
|
@@ -124,52 +197,220 @@ async function handleRun(req, res, searchPaths) {
|
|
|
124
197
|
const body = await parseBody(req);
|
|
125
198
|
request = JSON.parse(body);
|
|
126
199
|
}
|
|
127
|
-
catch {
|
|
128
|
-
|
|
200
|
+
catch (e) {
|
|
201
|
+
const err = e;
|
|
202
|
+
if (err?.code === 'PAYLOAD_TOO_LARGE') {
|
|
203
|
+
const [status, body] = buildHttpError(ErrorCodes.INPUT_TOO_LARGE, 'Payload too large', { suggestion: 'Reduce input size to under 1MB' });
|
|
204
|
+
jsonResponse(res, status, body, protocolVersion);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const [status, body] = buildHttpError(ErrorCodes.PARSE_ERROR, 'Invalid JSON body', { suggestion: 'Ensure request body is valid JSON' });
|
|
208
|
+
jsonResponse(res, status, body, protocolVersion);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (!request || typeof request !== 'object') {
|
|
212
|
+
const [status, body] = buildHttpError(ErrorCodes.INVALID_INPUT, 'Invalid request body', { suggestion: 'Ensure request body is a JSON object' });
|
|
213
|
+
jsonResponse(res, status, body, protocolVersion);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const reqBody = request;
|
|
217
|
+
// Determine protocol version (body > header > query > default)
|
|
218
|
+
protocolVersion = getRequestedVersion(req, url, reqBody.version);
|
|
219
|
+
// If the client explicitly requested an unsupported version, return a structured error.
|
|
220
|
+
const requested = reqBody.version ?? req.headers['x-cognitive-version'] ?? url.searchParams.get('version') ?? undefined;
|
|
221
|
+
if (requested && !SUPPORTED_VERSIONS.includes(requested)) {
|
|
222
|
+
const [status, body] = buildHttpError(ErrorCodes.UNSUPPORTED_VALUE, `Unsupported protocol version: ${requested}`, { suggestion: `Use version=${DEFAULT_VERSION}` });
|
|
223
|
+
jsonResponse(res, status, body, protocolVersion);
|
|
129
224
|
return;
|
|
130
225
|
}
|
|
131
226
|
// Validate request
|
|
132
|
-
if (!
|
|
133
|
-
|
|
227
|
+
if (!reqBody.module || !reqBody.args) {
|
|
228
|
+
const [status, body] = buildHttpError(ErrorCodes.MISSING_REQUIRED_FIELD, 'Missing required fields: module, args', {
|
|
229
|
+
moduleName: reqBody?.module ?? 'unknown',
|
|
230
|
+
suggestion: 'Provide both "module" and "args" fields in request body'
|
|
231
|
+
});
|
|
232
|
+
jsonResponse(res, status, body, protocolVersion);
|
|
134
233
|
return;
|
|
135
234
|
}
|
|
136
235
|
// Find module
|
|
137
|
-
const moduleData = await findModule(
|
|
236
|
+
const moduleData = await findModule(reqBody.module, searchPaths);
|
|
138
237
|
if (!moduleData) {
|
|
139
|
-
|
|
238
|
+
const [status, body] = buildHttpError(ErrorCodes.MODULE_NOT_FOUND, `Module '${reqBody.module}' not found`, {
|
|
239
|
+
moduleName: reqBody.module,
|
|
240
|
+
suggestion: 'Use GET /modules to list available modules'
|
|
241
|
+
});
|
|
242
|
+
jsonResponse(res, status, body, protocolVersion);
|
|
140
243
|
return;
|
|
141
244
|
}
|
|
142
245
|
try {
|
|
143
246
|
// Create provider
|
|
144
|
-
const provider = getProvider(
|
|
145
|
-
|
|
247
|
+
const provider = getProvider(reqBody.provider, reqBody.model);
|
|
248
|
+
const providerName = provider.name;
|
|
249
|
+
// Run module (always use v2.2 format internally)
|
|
146
250
|
const result = await runModule(moduleData, provider, {
|
|
147
|
-
|
|
251
|
+
args: reqBody.args,
|
|
148
252
|
useV22: true,
|
|
149
253
|
});
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
module:
|
|
153
|
-
provider:
|
|
154
|
-
};
|
|
155
|
-
if (
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if ('data' in result)
|
|
159
|
-
response.data = result.data;
|
|
160
|
-
}
|
|
161
|
-
else {
|
|
162
|
-
if ('error' in result)
|
|
163
|
-
response.error = result.error?.message;
|
|
254
|
+
// Attach transport context but do not rebuild the envelope.
|
|
255
|
+
const contextual = attachContext(result, {
|
|
256
|
+
module: reqBody.module,
|
|
257
|
+
provider: providerName,
|
|
258
|
+
});
|
|
259
|
+
if (contextual.ok) {
|
|
260
|
+
jsonResponse(res, 200, contextual, protocolVersion);
|
|
261
|
+
return;
|
|
164
262
|
}
|
|
165
|
-
|
|
263
|
+
const errorCode = (contextual.error?.code ?? ErrorCodes.INTERNAL_ERROR);
|
|
264
|
+
const status = httpStatusForErrorCode(errorCode);
|
|
265
|
+
jsonResponse(res, status, contextual, protocolVersion);
|
|
166
266
|
}
|
|
167
267
|
catch (error) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
268
|
+
// Infrastructure error - still return envelope
|
|
269
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
270
|
+
const [status, response] = buildHttpError(ErrorCodes.INTERNAL_ERROR, errorMessage, {
|
|
271
|
+
moduleName: reqBody?.module,
|
|
272
|
+
providerName: reqBody?.provider,
|
|
273
|
+
recoverable: false,
|
|
274
|
+
});
|
|
275
|
+
jsonResponse(res, status, response, protocolVersion);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
async function handleRunStream(req, res, searchPaths, url) {
|
|
279
|
+
let protocolVersion = DEFAULT_VERSION;
|
|
280
|
+
let sseStarted = false;
|
|
281
|
+
const beginSse = (version) => {
|
|
282
|
+
if (sseStarted)
|
|
283
|
+
return;
|
|
284
|
+
sseStarted = true;
|
|
285
|
+
res.writeHead(200, {
|
|
286
|
+
'Content-Type': 'text/event-stream; charset=utf-8',
|
|
287
|
+
'Cache-Control': 'no-cache, no-transform',
|
|
288
|
+
'Connection': 'keep-alive',
|
|
289
|
+
'X-Accel-Buffering': 'no',
|
|
290
|
+
'Access-Control-Allow-Origin': '*',
|
|
291
|
+
'Access-Control-Allow-Methods': 'POST, OPTIONS',
|
|
292
|
+
'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-Cognitive-Version',
|
|
293
|
+
'Access-Control-Expose-Headers': 'X-Cognitive-Version',
|
|
294
|
+
'X-Cognitive-Version': version,
|
|
172
295
|
});
|
|
296
|
+
};
|
|
297
|
+
const writeEvent = (ev, id) => {
|
|
298
|
+
const type = typeof ev.type === 'string' ? ev.type : 'message';
|
|
299
|
+
res.write(encodeSseFrame(ev, { event: type, id }));
|
|
300
|
+
};
|
|
301
|
+
// Helper: send an error as CEP events (start + error + end).
|
|
302
|
+
const sendErrorStream = (envelope) => {
|
|
303
|
+
beginSse(protocolVersion);
|
|
304
|
+
let id = 1;
|
|
305
|
+
writeEvent({ type: 'start', version: protocolVersion, timestamp_ms: 0, module: envelope.module ?? 'unknown' }, id++);
|
|
306
|
+
const err = envelope.error ?? { code: ErrorCodes.INTERNAL_ERROR, message: 'Unknown error' };
|
|
307
|
+
writeEvent({ type: 'error', version: protocolVersion, timestamp_ms: 0, module: envelope.module ?? 'unknown', provider: envelope.provider, error: err }, id++);
|
|
308
|
+
writeEvent({ type: 'end', version: protocolVersion, timestamp_ms: 0, module: envelope.module ?? 'unknown', provider: envelope.provider, result: envelope }, id++);
|
|
309
|
+
res.end();
|
|
310
|
+
};
|
|
311
|
+
// Verify API key
|
|
312
|
+
if (!verifyApiKey(req)) {
|
|
313
|
+
// Auth failures should still be structured; SSE payload carries the error.
|
|
314
|
+
const envelope = attachContext(makeErrorEnvelope({
|
|
315
|
+
code: ErrorCodes.PERMISSION_DENIED,
|
|
316
|
+
message: 'Missing or invalid API Key',
|
|
317
|
+
suggestion: 'Use header: Authorization: Bearer <your-api-key>',
|
|
318
|
+
version: protocolVersion,
|
|
319
|
+
}), { module: 'unknown', provider: 'unknown' });
|
|
320
|
+
sendErrorStream(envelope);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
// Parse request body
|
|
324
|
+
let request;
|
|
325
|
+
try {
|
|
326
|
+
const body = await parseBody(req);
|
|
327
|
+
request = JSON.parse(body);
|
|
328
|
+
}
|
|
329
|
+
catch (e) {
|
|
330
|
+
const err = e;
|
|
331
|
+
const code = err?.code === 'PAYLOAD_TOO_LARGE' ? ErrorCodes.INPUT_TOO_LARGE : ErrorCodes.PARSE_ERROR;
|
|
332
|
+
const message = err?.code === 'PAYLOAD_TOO_LARGE' ? 'Payload too large' : 'Invalid JSON body';
|
|
333
|
+
const envelope = attachContext(makeErrorEnvelope({
|
|
334
|
+
code,
|
|
335
|
+
message,
|
|
336
|
+
suggestion: code === ErrorCodes.INPUT_TOO_LARGE ? 'Reduce input size to under 1MB' : 'Ensure request body is valid JSON',
|
|
337
|
+
version: protocolVersion,
|
|
338
|
+
}), { module: 'unknown', provider: 'unknown' });
|
|
339
|
+
sendErrorStream(envelope);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
if (!request || typeof request !== 'object') {
|
|
343
|
+
const envelope = attachContext(makeErrorEnvelope({
|
|
344
|
+
code: ErrorCodes.INVALID_INPUT,
|
|
345
|
+
message: 'Invalid request body',
|
|
346
|
+
suggestion: 'Ensure request body is a JSON object',
|
|
347
|
+
version: protocolVersion,
|
|
348
|
+
}), { module: 'unknown', provider: 'unknown' });
|
|
349
|
+
sendErrorStream(envelope);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
const reqBody = request;
|
|
353
|
+
protocolVersion = getRequestedVersion(req, url, reqBody.version);
|
|
354
|
+
// If the client explicitly requested an unsupported version, return a structured error.
|
|
355
|
+
const requested = reqBody.version ?? req.headers['x-cognitive-version'] ?? url.searchParams.get('version') ?? undefined;
|
|
356
|
+
if (requested && !SUPPORTED_VERSIONS.includes(requested)) {
|
|
357
|
+
const envelope = attachContext(makeErrorEnvelope({
|
|
358
|
+
code: ErrorCodes.UNSUPPORTED_VALUE,
|
|
359
|
+
message: `Unsupported protocol version: ${requested}`,
|
|
360
|
+
suggestion: `Use version=${DEFAULT_VERSION}`,
|
|
361
|
+
version: protocolVersion,
|
|
362
|
+
}), { module: reqBody?.module ?? 'unknown', provider: reqBody?.provider ?? 'unknown' });
|
|
363
|
+
sendErrorStream(envelope);
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
// Validate request
|
|
367
|
+
if (!reqBody.module || !reqBody.args) {
|
|
368
|
+
const envelope = attachContext(makeErrorEnvelope({
|
|
369
|
+
code: ErrorCodes.MISSING_REQUIRED_FIELD,
|
|
370
|
+
message: 'Missing required fields: module, args',
|
|
371
|
+
suggestion: 'Provide both "module" and "args" fields in request body',
|
|
372
|
+
version: protocolVersion,
|
|
373
|
+
}), { module: reqBody?.module ?? 'unknown', provider: reqBody?.provider ?? 'unknown' });
|
|
374
|
+
sendErrorStream(envelope);
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
// Find module
|
|
378
|
+
const moduleData = await findModule(reqBody.module, searchPaths);
|
|
379
|
+
if (!moduleData) {
|
|
380
|
+
const envelope = attachContext(makeErrorEnvelope({
|
|
381
|
+
code: ErrorCodes.MODULE_NOT_FOUND,
|
|
382
|
+
message: `Module '${reqBody.module}' not found`,
|
|
383
|
+
suggestion: 'Use GET /modules to list available modules',
|
|
384
|
+
version: protocolVersion,
|
|
385
|
+
}), { module: reqBody.module, provider: reqBody.provider ?? 'unknown' });
|
|
386
|
+
sendErrorStream(envelope);
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
// Create provider
|
|
390
|
+
const provider = getProvider(reqBody.provider, reqBody.model);
|
|
391
|
+
const providerName = provider.name;
|
|
392
|
+
// Stream events
|
|
393
|
+
beginSse(protocolVersion);
|
|
394
|
+
let id = 1;
|
|
395
|
+
let closed = false;
|
|
396
|
+
const onClose = () => { closed = true; };
|
|
397
|
+
req.on('close', onClose);
|
|
398
|
+
res.on('close', onClose);
|
|
399
|
+
for await (const ev of runModuleStream(moduleData, provider, {
|
|
400
|
+
args: reqBody.args,
|
|
401
|
+
useV22: true,
|
|
402
|
+
})) {
|
|
403
|
+
if (closed)
|
|
404
|
+
break;
|
|
405
|
+
const contextualEv = {
|
|
406
|
+
...ev,
|
|
407
|
+
module: reqBody.module,
|
|
408
|
+
provider: providerName,
|
|
409
|
+
};
|
|
410
|
+
writeEvent(contextualEv, id++);
|
|
411
|
+
}
|
|
412
|
+
if (!closed) {
|
|
413
|
+
res.end();
|
|
173
414
|
}
|
|
174
415
|
}
|
|
175
416
|
export function createServer(options = {}) {
|
|
@@ -184,7 +425,8 @@ export function createServer(options = {}) {
|
|
|
184
425
|
res.writeHead(204, {
|
|
185
426
|
'Access-Control-Allow-Origin': '*',
|
|
186
427
|
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
|
187
|
-
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
|
428
|
+
'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-Cognitive-Version',
|
|
429
|
+
'Access-Control-Expose-Headers': 'X-Cognitive-Version',
|
|
188
430
|
});
|
|
189
431
|
res.end();
|
|
190
432
|
return;
|
|
@@ -205,17 +447,30 @@ export function createServer(options = {}) {
|
|
|
205
447
|
await handleModuleInfo(res, moduleName, searchPaths);
|
|
206
448
|
}
|
|
207
449
|
else if (path === '/run' && method === 'POST') {
|
|
208
|
-
await handleRun(req, res, searchPaths);
|
|
450
|
+
await handleRun(req, res, searchPaths, url);
|
|
451
|
+
}
|
|
452
|
+
else if (path === '/run/stream' && method === 'POST') {
|
|
453
|
+
await handleRunStream(req, res, searchPaths, url);
|
|
209
454
|
}
|
|
210
455
|
else {
|
|
211
|
-
|
|
456
|
+
const envelope = makeErrorEnvelope({
|
|
457
|
+
code: ErrorCodes.ENDPOINT_NOT_FOUND,
|
|
458
|
+
message: `Endpoint '${path}' not found`,
|
|
459
|
+
suggestion: 'Use GET / to see available endpoints',
|
|
460
|
+
risk: 'low',
|
|
461
|
+
});
|
|
462
|
+
jsonResponse(res, 404, envelope);
|
|
212
463
|
}
|
|
213
464
|
}
|
|
214
465
|
catch (error) {
|
|
215
466
|
console.error('Server error:', error);
|
|
216
|
-
|
|
217
|
-
|
|
467
|
+
const errorMessage = error instanceof Error ? error.message : 'Internal server error';
|
|
468
|
+
const envelope = makeErrorEnvelope({
|
|
469
|
+
code: ErrorCodes.INTERNAL_ERROR,
|
|
470
|
+
message: errorMessage,
|
|
471
|
+
recoverable: false,
|
|
218
472
|
});
|
|
473
|
+
jsonResponse(res, 500, envelope);
|
|
219
474
|
}
|
|
220
475
|
});
|
|
221
476
|
return server;
|
|
@@ -233,6 +488,7 @@ export async function serve(options = {}) {
|
|
|
233
488
|
console.log(' GET /modules - List modules');
|
|
234
489
|
console.log(' GET /modules/:name - Module info');
|
|
235
490
|
console.log(' POST /run - Run module');
|
|
491
|
+
console.log(' POST /run/stream - Run module (SSE stream)');
|
|
236
492
|
resolve();
|
|
237
493
|
});
|
|
238
494
|
});
|
package/dist/server/index.d.ts
CHANGED
package/dist/server/index.js
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface SseFrameOptions {
|
|
2
|
+
event?: string;
|
|
3
|
+
id?: string | number;
|
|
4
|
+
retryMs?: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Encode a payload into an SSE frame.
|
|
8
|
+
*
|
|
9
|
+
* Notes:
|
|
10
|
+
* - SSE requires each data line to be prefixed with `data:`.
|
|
11
|
+
* - `event:` maps cleanly to CEP `type` to keep transport mapping deterministic.
|
|
12
|
+
*/
|
|
13
|
+
export declare function encodeSseFrame(data: unknown, options?: SseFrameOptions): string;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encode a payload into an SSE frame.
|
|
3
|
+
*
|
|
4
|
+
* Notes:
|
|
5
|
+
* - SSE requires each data line to be prefixed with `data:`.
|
|
6
|
+
* - `event:` maps cleanly to CEP `type` to keep transport mapping deterministic.
|
|
7
|
+
*/
|
|
8
|
+
export function encodeSseFrame(data, options = {}) {
|
|
9
|
+
const lines = [];
|
|
10
|
+
if (options.retryMs !== undefined)
|
|
11
|
+
lines.push(`retry: ${options.retryMs}`);
|
|
12
|
+
if (options.id !== undefined)
|
|
13
|
+
lines.push(`id: ${options.id}`);
|
|
14
|
+
if (options.event)
|
|
15
|
+
lines.push(`event: ${options.event}`);
|
|
16
|
+
const json = JSON.stringify(data);
|
|
17
|
+
for (const line of json.split('\n')) {
|
|
18
|
+
lines.push(`data: ${line}`);
|
|
19
|
+
}
|
|
20
|
+
lines.push(''); // End of event
|
|
21
|
+
return lines.join('\n') + '\n';
|
|
22
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -6,6 +6,17 @@ export interface Provider {
|
|
|
6
6
|
name: string;
|
|
7
7
|
invoke(params: InvokeParams): Promise<InvokeResult>;
|
|
8
8
|
isConfigured(): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Stream-based invoke (optional).
|
|
11
|
+
* Returns an async generator that yields content chunks.
|
|
12
|
+
*
|
|
13
|
+
* If not implemented, falls back to non-streaming invoke.
|
|
14
|
+
*/
|
|
15
|
+
invokeStream?(params: InvokeParams): AsyncGenerator<string, InvokeResult, unknown>;
|
|
16
|
+
/**
|
|
17
|
+
* Check if this provider supports streaming.
|
|
18
|
+
*/
|
|
19
|
+
supportsStreaming?(): boolean;
|
|
9
20
|
}
|
|
10
21
|
export interface InvokeParams {
|
|
11
22
|
messages: Message[];
|
|
@@ -222,14 +233,19 @@ export interface EnvelopeMeta {
|
|
|
222
233
|
}
|
|
223
234
|
/**
|
|
224
235
|
* Enhanced error structure with retry and recovery info (v2.2.1).
|
|
236
|
+
*
|
|
237
|
+
* Consistent across HTTP, MCP, and CLI layers.
|
|
238
|
+
* See ERROR-CODES.md for error code taxonomy.
|
|
225
239
|
*/
|
|
226
240
|
export interface EnvelopeError {
|
|
227
|
-
/** Error code (
|
|
241
|
+
/** Error code (E-format like "E4006" or legacy like "MODULE_NOT_FOUND") */
|
|
228
242
|
code: string;
|
|
229
243
|
/** Human-readable error message */
|
|
230
244
|
message: string;
|
|
231
245
|
/** Whether the error can be retried */
|
|
232
246
|
recoverable?: boolean;
|
|
247
|
+
/** Suggested action to fix the error */
|
|
248
|
+
suggestion?: string;
|
|
233
249
|
/** Suggested wait time before retry (in milliseconds) */
|
|
234
250
|
retry_after_ms?: number;
|
|
235
251
|
/** Additional error context */
|
|
@@ -337,11 +353,26 @@ export interface CommandContext {
|
|
|
337
353
|
provider: Provider;
|
|
338
354
|
verbose?: boolean;
|
|
339
355
|
}
|
|
356
|
+
/**
|
|
357
|
+
* CLI command result (legacy format).
|
|
358
|
+
*
|
|
359
|
+
* For new code, prefer using CognitiveErrorEnvelope from errors/index.js
|
|
360
|
+
* for error responses to ensure cross-layer consistency.
|
|
361
|
+
*/
|
|
340
362
|
export interface CommandResult {
|
|
341
363
|
success: boolean;
|
|
342
364
|
data?: unknown;
|
|
343
365
|
error?: string;
|
|
344
366
|
}
|
|
367
|
+
/**
|
|
368
|
+
* Extended command result with unified error structure.
|
|
369
|
+
* Use this for commands that need full error details.
|
|
370
|
+
*/
|
|
371
|
+
export interface CommandResultV2 {
|
|
372
|
+
ok: boolean;
|
|
373
|
+
data?: unknown;
|
|
374
|
+
error?: EnvelopeError;
|
|
375
|
+
}
|
|
345
376
|
export interface ModuleInput {
|
|
346
377
|
code?: string;
|
|
347
378
|
query?: string;
|
package/dist/types.js
CHANGED
|
@@ -44,7 +44,10 @@ function aggregateRiskFromList(items) {
|
|
|
44
44
|
}
|
|
45
45
|
let maxLevel = 0;
|
|
46
46
|
for (const item of items) {
|
|
47
|
-
const
|
|
47
|
+
const riskKey = item.risk;
|
|
48
|
+
const level = typeof riskKey === 'string' && Object.prototype.hasOwnProperty.call(riskLevels, riskKey)
|
|
49
|
+
? riskLevels[riskKey]
|
|
50
|
+
: riskLevels.medium;
|
|
48
51
|
maxLevel = Math.max(maxLevel, level);
|
|
49
52
|
}
|
|
50
53
|
return riskNames[maxLevel];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const VERSION: string;
|
package/dist/version.js
ADDED
package/package.json
CHANGED
|
@@ -1,18 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognitive-modules-cli",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.7",
|
|
4
4
|
"description": "Cognitive Modules - Structured AI Task Execution with version management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./package.json": "./package.json"
|
|
14
|
+
},
|
|
7
15
|
"bin": {
|
|
8
16
|
"cognitive-modules-cli": "dist/cli.js",
|
|
9
17
|
"cog": "dist/cli.js"
|
|
10
18
|
},
|
|
11
19
|
"scripts": {
|
|
12
|
-
"
|
|
20
|
+
"clean": "node -e \"import('node:fs').then(fs=>fs.rmSync('dist',{recursive:true,force:true}))\"",
|
|
21
|
+
"build": "npm run clean && tsc",
|
|
13
22
|
"start": "node dist/cli.js",
|
|
14
23
|
"dev": "tsx src/cli.ts",
|
|
15
|
-
"
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"test:watch": "vitest",
|
|
26
|
+
"test:coverage": "vitest run --coverage",
|
|
27
|
+
"pack:check": "npm pack --dry-run --json --cache ../../.npm-cache",
|
|
28
|
+
"release:check": "npm run build && npm test && npm run pack:check",
|
|
29
|
+
"prepublishOnly": "npm run release:check"
|
|
16
30
|
},
|
|
17
31
|
"keywords": [
|
|
18
32
|
"cognitive",
|
|
@@ -25,13 +39,22 @@
|
|
|
25
39
|
],
|
|
26
40
|
"license": "MIT",
|
|
27
41
|
"author": "ziel-io",
|
|
42
|
+
"files": [
|
|
43
|
+
"dist",
|
|
44
|
+
"README.md",
|
|
45
|
+
"LICENSE",
|
|
46
|
+
"CHANGELOG.md"
|
|
47
|
+
],
|
|
28
48
|
"repository": {
|
|
29
49
|
"type": "git",
|
|
30
|
-
"url": "https://github.com/
|
|
50
|
+
"url": "https://github.com/Cognary/cognitive.git"
|
|
31
51
|
},
|
|
32
|
-
"homepage": "https://
|
|
52
|
+
"homepage": "https://cognary.github.io/cognitive/",
|
|
33
53
|
"bugs": {
|
|
34
|
-
"url": "https://github.com/
|
|
54
|
+
"url": "https://github.com/Cognary/cognitive/issues"
|
|
55
|
+
},
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public"
|
|
35
58
|
},
|
|
36
59
|
"engines": {
|
|
37
60
|
"node": ">=18.0.0"
|
|
@@ -47,6 +70,7 @@
|
|
|
47
70
|
"@types/js-yaml": "^4.0.9",
|
|
48
71
|
"@types/node": "^22.0.0",
|
|
49
72
|
"tsx": "^4.21.0",
|
|
50
|
-
"typescript": "^5.9.3"
|
|
73
|
+
"typescript": "^5.9.3",
|
|
74
|
+
"vitest": "^2.1.0"
|
|
51
75
|
}
|
|
52
76
|
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for Composition Engine
|
|
3
|
-
*
|
|
4
|
-
* Tests all COMPOSITION.md specified functionality:
|
|
5
|
-
* - JSONPath-like expression evaluation
|
|
6
|
-
* - Condition expression evaluation
|
|
7
|
-
* - Aggregation strategies
|
|
8
|
-
* - Version matching
|
|
9
|
-
* - Sequential, Parallel, Conditional, Iterative patterns
|
|
10
|
-
*/
|
|
11
|
-
export {};
|