mindforge-sdk 11.0.0 → 11.7.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/README.md CHANGED
@@ -64,7 +64,7 @@ if (!valid) console.error(errors);
64
64
  - The SDK operates on local files and provides no network authentication. Do not expose SDK
65
65
  endpoints to the public internet.
66
66
 
67
- ## New in v11.0.0
67
+ ## New in v11.7.0
68
68
 
69
69
  ### Additional exports
70
70
 
@@ -73,7 +73,7 @@ import {
73
73
  MindForgeClient,
74
74
  MindForgeEventStream,
75
75
  WebSocketEventStream,
76
- VERSION, // '11.0.0'
76
+ VERSION, // '11.7.0'
77
77
  } from 'mindforge-sdk';
78
78
 
79
79
  import type {
@@ -102,11 +102,34 @@ for await (const chunk of stream) {
102
102
 
103
103
  ### Batch execution
104
104
 
105
+ Runs commands concurrently (semaphore-bounded by `maxConcurrency`, default 3). Each
106
+ task's `command` is the executable and `options.args` is a **string array** — it is NOT a
107
+ shell string. Commands run with `shell: false`, so arguments are passed directly to the
108
+ process and are safe from shell injection.
109
+
105
110
  ```typescript
106
- const results = await client.batchExecute([
107
- { phase: 1, taskId: 'task-a' },
108
- { phase: 1, taskId: 'task-b' },
109
- ], { concurrency: 4 });
111
+ const batch = await client.batchExecute({
112
+ tasks: [
113
+ { id: 'task-a', command: 'node', options: { args: ['--version'] } },
114
+ { id: 'task-b', command: 'git', options: { args: ['rev-parse', 'HEAD'] } },
115
+ ],
116
+ maxConcurrency: 4,
117
+ });
118
+
119
+ for (const entry of batch.results) {
120
+ if (entry.status === 'fulfilled') {
121
+ // entry.result is { stdout, stderr, exitCode }
122
+ const { stdout, stderr, exitCode } = entry.result as {
123
+ stdout: string; stderr: string; exitCode: number;
124
+ };
125
+ console.log(`${entry.taskId} exited ${exitCode}: ${stdout.trim()}`);
126
+ } else {
127
+ // entry.status === 'rejected'
128
+ console.error(`${entry.taskId} failed: ${entry.error}`);
129
+ }
130
+ }
131
+
132
+ console.log(`batch finished in ${batch.totalDurationMs}ms`);
110
133
  ```
111
134
 
112
135
  ### Runtime config validation
package/dist/client.js CHANGED
@@ -38,6 +38,7 @@ var __importStar = (this && this.__importStar) || (function () {
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.MindForgeClient = void 0;
40
40
  const events_1 = require("events");
41
+ const child_process_1 = require("child_process");
41
42
  const fs = __importStar(require("fs"));
42
43
  const path = __importStar(require("path"));
43
44
  const events_2 = require("./events");
@@ -235,7 +236,8 @@ class MindForgeClient extends events_1.EventEmitter {
235
236
  await eventSource.connect();
236
237
  const chunks = [];
237
238
  let resolveNext = null;
238
- eventSource.on('stream_chunk', (data) => {
239
+ eventSource.on('stream_chunk', (raw) => {
240
+ const data = raw; // socket payload is untyped JSON; narrow at the boundary
239
241
  if (resolveNext) {
240
242
  resolveNext({ value: data, done: data.type === 'done' });
241
243
  resolveNext = null;
@@ -307,8 +309,42 @@ class MindForgeClient extends events_1.EventEmitter {
307
309
  errors.push('taskTimeoutMs must be positive');
308
310
  return { valid: errors.length === 0, errors };
309
311
  }
310
- async executeCommand(command, options) {
311
- return { command, options, executed: true };
312
+ executeCommand(command, options) {
313
+ const args = Array.isArray(options?.args) ? options.args : [];
314
+ const cwd = options?.cwd ?? this.projectRoot;
315
+ const timeoutMs = this.config.taskTimeoutMs;
316
+ return new Promise((resolve, reject) => {
317
+ const child = (0, child_process_1.spawn)(command, args, { cwd, shell: false });
318
+ let stdout = '';
319
+ let stderr = '';
320
+ let settled = false;
321
+ const timer = setTimeout(() => {
322
+ if (settled)
323
+ return;
324
+ child.kill('SIGTERM');
325
+ setTimeout(() => { if (!child.killed)
326
+ child.kill('SIGKILL'); }, 2000).unref();
327
+ settled = true;
328
+ reject(new Error(`Command timed out after ${timeoutMs}ms: ${command}`));
329
+ }, timeoutMs);
330
+ timer.unref();
331
+ child.stdout?.on('data', (d) => { stdout += d.toString(); });
332
+ child.stderr?.on('data', (d) => { stderr += d.toString(); });
333
+ child.on('error', (err) => {
334
+ if (settled)
335
+ return;
336
+ clearTimeout(timer);
337
+ settled = true;
338
+ reject(err);
339
+ });
340
+ child.on('close', (code) => {
341
+ if (settled)
342
+ return;
343
+ clearTimeout(timer);
344
+ settled = true;
345
+ resolve({ stdout, stderr, exitCode: code ?? -1 });
346
+ });
347
+ });
312
348
  }
313
349
  }
314
350
  exports.MindForgeClient = MindForgeClient;
package/dist/events.d.ts CHANGED
@@ -25,6 +25,7 @@ export declare class MindForgeEventStream {
25
25
  */
26
26
  stop(): void;
27
27
  }
28
+ type EventHandler = (data: unknown) => void;
28
29
  export declare class WebSocketEventStream {
29
30
  private url;
30
31
  private ws;
@@ -33,7 +34,8 @@ export declare class WebSocketEventStream {
33
34
  private listeners;
34
35
  constructor(url?: string);
35
36
  connect(): Promise<void>;
36
- on(eventType: string, handler: (data: any) => void): void;
37
- off(eventType: string, handler: (data: any) => void): void;
37
+ on(eventType: string, handler: EventHandler): void;
38
+ off(eventType: string, handler: EventHandler): void;
38
39
  disconnect(): void;
39
40
  }
41
+ export {};
package/dist/events.js CHANGED
@@ -202,7 +202,7 @@ class WebSocketEventStream {
202
202
  this.ws.onerror = (err) => reject(err);
203
203
  this.ws.onmessage = (event) => {
204
204
  try {
205
- const parsed = JSON.parse(event.data.toString());
205
+ const parsed = JSON.parse(String(event.data));
206
206
  const handlers = this.listeners.get(parsed.type) || new Set();
207
207
  handlers.forEach(handler => handler(parsed.data));
208
208
  }
package/dist/index.d.ts CHANGED
@@ -8,4 +8,4 @@ export { commands, batch } from './commands';
8
8
  export { MindForgeMemory } from './memory';
9
9
  export type { CommandOptions } from './commands';
10
10
  export type { MindForgeConfig, PhaseResult, TaskResult, SecurityFinding, GateResult, HealthReport, HealthIssue, MindForgeEvent, AuditLogEntry, WaveExecutionResult, MigrationResult, StreamChunk, StreamingExecutionResult, BatchExecutionRequest, BatchExecutionResult, } from './types';
11
- export declare const VERSION = "11.0.0";
11
+ export declare const VERSION = "11.7.0";
package/dist/index.js CHANGED
@@ -15,4 +15,4 @@ Object.defineProperty(exports, "commands", { enumerable: true, get: function ()
15
15
  Object.defineProperty(exports, "batch", { enumerable: true, get: function () { return commands_1.batch; } });
16
16
  var memory_1 = require("./memory");
17
17
  Object.defineProperty(exports, "MindForgeMemory", { enumerable: true, get: function () { return memory_1.MindForgeMemory; } });
18
- exports.VERSION = '11.0.0';
18
+ exports.VERSION = '11.7.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mindforge-sdk",
3
- "version": "11.0.0",
3
+ "version": "11.7.0",
4
4
  "description": "MindForge SDK \u2014 Programmatic API for embedding MindForge in tools",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -20,9 +20,10 @@
20
20
  },
21
21
  "scripts": {
22
22
  "build": "tsc",
23
- "test": "node tests/sdk.test.js",
23
+ "pretest": "npm run build",
24
+ "test": "node tests/sdk.test.js && node tests/execute-command.test.js",
24
25
  "lint": "eslint .",
25
- "prepublishOnly": "npm run build && npm test"
26
+ "prepublishOnly": "npm test"
26
27
  },
27
28
  "keywords": [
28
29
  "mindforge",