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.
Files changed (101) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +35 -29
  4. package/dist/cli.js +519 -23
  5. package/dist/commands/add.d.ts +33 -14
  6. package/dist/commands/add.js +383 -16
  7. package/dist/commands/compose.js +60 -23
  8. package/dist/commands/index.d.ts +4 -0
  9. package/dist/commands/index.js +4 -0
  10. package/dist/commands/init.js +23 -1
  11. package/dist/commands/migrate.d.ts +30 -0
  12. package/dist/commands/migrate.js +650 -0
  13. package/dist/commands/pipe.d.ts +1 -0
  14. package/dist/commands/pipe.js +31 -11
  15. package/dist/commands/remove.js +33 -2
  16. package/dist/commands/run.d.ts +2 -0
  17. package/dist/commands/run.js +61 -28
  18. package/dist/commands/search.d.ts +28 -0
  19. package/dist/commands/search.js +143 -0
  20. package/dist/commands/test.d.ts +65 -0
  21. package/dist/commands/test.js +454 -0
  22. package/dist/commands/update.d.ts +1 -0
  23. package/dist/commands/update.js +106 -14
  24. package/dist/commands/validate.d.ts +36 -0
  25. package/dist/commands/validate.js +97 -0
  26. package/dist/errors/index.d.ts +225 -0
  27. package/dist/errors/index.js +420 -0
  28. package/dist/mcp/server.js +84 -79
  29. package/dist/modules/composition.js +97 -32
  30. package/dist/modules/loader.js +4 -2
  31. package/dist/modules/runner.d.ts +72 -5
  32. package/dist/modules/runner.js +306 -59
  33. package/dist/modules/subagent.d.ts +6 -1
  34. package/dist/modules/subagent.js +18 -13
  35. package/dist/modules/validator.js +14 -6
  36. package/dist/providers/anthropic.d.ts +15 -0
  37. package/dist/providers/anthropic.js +147 -5
  38. package/dist/providers/base.d.ts +11 -0
  39. package/dist/providers/base.js +18 -0
  40. package/dist/providers/gemini.d.ts +15 -0
  41. package/dist/providers/gemini.js +122 -5
  42. package/dist/providers/ollama.d.ts +15 -0
  43. package/dist/providers/ollama.js +111 -3
  44. package/dist/providers/openai.d.ts +11 -0
  45. package/dist/providers/openai.js +133 -0
  46. package/dist/registry/client.d.ts +212 -0
  47. package/dist/registry/client.js +359 -0
  48. package/dist/registry/index.d.ts +4 -0
  49. package/dist/registry/index.js +4 -0
  50. package/dist/registry/tar.d.ts +8 -0
  51. package/dist/registry/tar.js +353 -0
  52. package/dist/server/http.js +301 -45
  53. package/dist/server/index.d.ts +2 -0
  54. package/dist/server/index.js +1 -0
  55. package/dist/server/sse.d.ts +13 -0
  56. package/dist/server/sse.js +22 -0
  57. package/dist/types.d.ts +32 -1
  58. package/dist/types.js +4 -1
  59. package/dist/version.d.ts +1 -0
  60. package/dist/version.js +4 -0
  61. package/package.json +31 -7
  62. package/dist/modules/composition.test.d.ts +0 -11
  63. package/dist/modules/composition.test.js +0 -450
  64. package/dist/modules/policy.test.d.ts +0 -10
  65. package/dist/modules/policy.test.js +0 -369
  66. package/src/cli.ts +0 -471
  67. package/src/commands/add.ts +0 -315
  68. package/src/commands/compose.ts +0 -185
  69. package/src/commands/index.ts +0 -13
  70. package/src/commands/init.ts +0 -94
  71. package/src/commands/list.ts +0 -33
  72. package/src/commands/pipe.ts +0 -76
  73. package/src/commands/remove.ts +0 -57
  74. package/src/commands/run.ts +0 -80
  75. package/src/commands/update.ts +0 -130
  76. package/src/commands/versions.ts +0 -79
  77. package/src/index.ts +0 -90
  78. package/src/mcp/index.ts +0 -5
  79. package/src/mcp/server.ts +0 -403
  80. package/src/modules/composition.test.ts +0 -558
  81. package/src/modules/composition.ts +0 -1674
  82. package/src/modules/index.ts +0 -9
  83. package/src/modules/loader.ts +0 -508
  84. package/src/modules/policy.test.ts +0 -455
  85. package/src/modules/runner.ts +0 -1983
  86. package/src/modules/subagent.ts +0 -277
  87. package/src/modules/validator.ts +0 -700
  88. package/src/providers/anthropic.ts +0 -89
  89. package/src/providers/base.ts +0 -29
  90. package/src/providers/deepseek.ts +0 -83
  91. package/src/providers/gemini.ts +0 -117
  92. package/src/providers/index.ts +0 -78
  93. package/src/providers/minimax.ts +0 -81
  94. package/src/providers/moonshot.ts +0 -82
  95. package/src/providers/ollama.ts +0 -83
  96. package/src/providers/openai.ts +0 -84
  97. package/src/providers/qwen.ts +0 -82
  98. package/src/server/http.ts +0 -316
  99. package/src/server/index.ts +0 -6
  100. package/src/types.ts +0 -599
  101. package/tsconfig.json +0 -17
@@ -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
- function parseBody(req) {
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
- req.on('data', (chunk) => (body += chunk));
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: '1.3.0',
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: '1.3.0',
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
- jsonResponse(res, 404, { error: `Module '${moduleName}' not found` });
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
- jsonResponse(res, 401, {
117
- error: 'Missing or invalid API Key. Use header: Authorization: Bearer <your-api-key>',
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
- jsonResponse(res, 400, { error: 'Invalid JSON body' });
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 (!request.module || !request.args) {
133
- jsonResponse(res, 400, { error: 'Missing required fields: module, args' });
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(request.module, searchPaths);
236
+ const moduleData = await findModule(reqBody.module, searchPaths);
138
237
  if (!moduleData) {
139
- jsonResponse(res, 404, { error: `Module '${request.module}' not found` });
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(request.provider, request.model);
145
- // Run module
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
- input: { query: request.args, code: request.args },
251
+ args: reqBody.args,
148
252
  useV22: true,
149
253
  });
150
- const response = {
151
- ok: result.ok,
152
- module: request.module,
153
- provider: request.provider || process.env.LLM_PROVIDER || 'openai',
154
- };
155
- if (result.ok) {
156
- if ('meta' in result)
157
- response.meta = result.meta;
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
- jsonResponse(res, 200, response);
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
- jsonResponse(res, 500, {
169
- ok: false,
170
- error: error instanceof Error ? error.message : String(error),
171
- module: request.module,
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
- jsonResponse(res, 404, { error: 'Not found' });
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
- jsonResponse(res, 500, {
217
- error: error instanceof Error ? error.message : 'Internal server error',
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
  });
@@ -3,3 +3,5 @@
3
3
  */
4
4
  export { serve, createServer } from './http.js';
5
5
  export type { ServeOptions } from './http.js';
6
+ export { encodeSseFrame } from './sse.js';
7
+ export type { SseFrameOptions } from './sse.js';
@@ -2,3 +2,4 @@
2
2
  * Server - Re-export all server functionality
3
3
  */
4
4
  export { serve, createServer } from './http.js';
5
+ export { encodeSseFrame } from './sse.js';
@@ -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 (e.g., "INVALID_INPUT", "PARSE_ERROR") */
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 level = riskLevels[item.risk ?? 'medium'];
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;
@@ -0,0 +1,4 @@
1
+ import { createRequire } from 'node:module';
2
+ const require = createRequire(import.meta.url);
3
+ const pkg = require('../package.json');
4
+ export const VERSION = pkg.version ?? '0.0.0';
package/package.json CHANGED
@@ -1,18 +1,32 @@
1
1
  {
2
2
  "name": "cognitive-modules-cli",
3
- "version": "2.2.1",
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
- "build": "tsc",
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
- "prepublishOnly": "npm run build"
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/ziel-io/cognitive-modules.git"
50
+ "url": "https://github.com/Cognary/cognitive.git"
31
51
  },
32
- "homepage": "https://ziel-io.github.io/cognitive-modules/",
52
+ "homepage": "https://cognary.github.io/cognitive/",
33
53
  "bugs": {
34
- "url": "https://github.com/ziel-io/cognitive-modules/issues"
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 {};