orquesta-cli 0.2.118 → 0.2.120

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.
@@ -4,7 +4,8 @@ import { logger } from '../../../utils/logger.js';
4
4
  import { isNativeWindows, findNativePowerShellPath, isDangerousPowerShellCommand, } from '../../../utils/platform-utils.js';
5
5
  import { filterSafeEnv } from '../../../utils/env-filter.js';
6
6
  import { configManager } from '../../../core/config/config-manager.js';
7
- async function executePowerShell(command, cwd, timeout = 30000, explicitEnv) {
7
+ const DEFAULT_POWERSHELL_TIMEOUT = 120000;
8
+ async function executePowerShell(command, cwd, timeout = DEFAULT_POWERSHELL_TIMEOUT, explicitEnv) {
8
9
  return new Promise((resolve, reject) => {
9
10
  const psPath = findNativePowerShellPath();
10
11
  const userSafeVars = configManager.getSafeEnvVars();
@@ -15,32 +16,55 @@ async function executePowerShell(command, cwd, timeout = 30000, explicitEnv) {
15
16
  let stdout = '';
16
17
  let stderr = '';
17
18
  let killed = false;
19
+ let settled = false;
20
+ const finish = (code) => {
21
+ if (settled)
22
+ return;
23
+ settled = true;
24
+ clearTimeout(timer);
25
+ clearTimeout(hardTimer);
26
+ resolve({
27
+ stdout: stdout.trim(),
28
+ stderr: stderr.trim(),
29
+ exitCode: code ?? (killed ? 124 : 0),
30
+ timedOut: killed,
31
+ });
32
+ };
18
33
  child.stdout?.on('data', (data) => {
19
34
  stdout += data.toString();
20
35
  });
21
36
  child.stderr?.on('data', (data) => {
22
37
  stderr += data.toString();
23
38
  });
24
- child.on('close', (code) => {
25
- if (!killed) {
26
- resolve({
27
- stdout: stdout.trim(),
28
- stderr: stderr.trim(),
29
- exitCode: code ?? 0,
30
- });
31
- }
32
- });
39
+ child.on('close', (code) => finish(code));
33
40
  child.on('error', (error) => {
41
+ if (settled)
42
+ return;
43
+ settled = true;
44
+ clearTimeout(timer);
45
+ clearTimeout(hardTimer);
34
46
  reject(error);
35
47
  });
36
48
  const timer = setTimeout(() => {
37
49
  killed = true;
38
- child.kill('SIGTERM');
39
- reject(new Error(`Command timed out after ${timeout}ms`));
50
+ try {
51
+ child.kill('SIGTERM');
52
+ }
53
+ catch { }
54
+ setTimeout(() => { try {
55
+ child.kill('SIGKILL');
56
+ }
57
+ catch { } }, 2000).unref?.();
40
58
  }, timeout);
41
- child.on('close', () => {
42
- clearTimeout(timer);
43
- });
59
+ const hardTimer = setTimeout(() => {
60
+ killed = true;
61
+ try {
62
+ child.kill('SIGKILL');
63
+ }
64
+ catch { }
65
+ finish(124);
66
+ }, timeout + 5000);
67
+ hardTimer.unref?.();
44
68
  });
45
69
  }
46
70
  const POWERSHELL_TOOL_DEFINITION = {
@@ -51,7 +75,7 @@ const POWERSHELL_TOOL_DEFINITION = {
51
75
 
52
76
  IMPORTANT:
53
77
  - Do NOT use for file reading/writing - use read_file, create_file, edit_file instead
54
- - Commands have a 30 second timeout by default
78
+ - Commands have a 120 second timeout by default
55
79
  - Dangerous commands (Remove-Item -Recurse -Force C:\\, Stop-Computer, etc.) are blocked
56
80
  - Output is truncated if too long
57
81
  - PowerShell 7 (pwsh) is used if available, otherwise PowerShell 5.1`,
@@ -78,7 +102,7 @@ Examples:
78
102
  },
79
103
  timeout: {
80
104
  type: 'number',
81
- description: 'Timeout in milliseconds (optional, default: 30000)',
105
+ description: 'Timeout in milliseconds (optional, default: 120000)',
82
106
  },
83
107
  env: {
84
108
  type: 'object',
@@ -98,7 +122,7 @@ export const powershellTool = {
98
122
  async execute(args) {
99
123
  const command = args['command'];
100
124
  const cwd = args['cwd'];
101
- const timeout = args['timeout'] || 30000;
125
+ const timeout = args['timeout'] || DEFAULT_POWERSHELL_TIMEOUT;
102
126
  const env = args['env'];
103
127
  logger.enter('powershellTool.execute', { command, cwd, timeout, envKeys: env ? Object.keys(env) : [] });
104
128
  if (!isNativeWindows()) {
@@ -149,6 +173,16 @@ export const powershellTool = {
149
173
  if (output.length > MAX_OUTPUT_LENGTH) {
150
174
  output = output.slice(0, MAX_OUTPUT_LENGTH) + '\n\n... [output truncated]';
151
175
  }
176
+ if (execResult.timedOut) {
177
+ const secs = Math.round(timeout / 1000);
178
+ const partial = output ? `\n\nPartial output before timeout:\n${output}` : '';
179
+ logger.exit('powershellTool.execute', { timedOut: true, outputLength: output.length });
180
+ return {
181
+ success: false,
182
+ error: `Command timed out after ${secs}s. For long-running commands, pass a larger \`timeout\` ` +
183
+ `(in ms) or run it in the background.${partial}`,
184
+ };
185
+ }
152
186
  if (execResult.exitCode !== 0) {
153
187
  output += `\n\n[Exit code: ${execResult.exitCode}]`;
154
188
  }
@@ -4,6 +4,33 @@ import { dirname, join } from 'path';
4
4
  import chalk from 'chalk';
5
5
  import { PROJECTS_DIR, cwdToProjectSegment } from '../constants.js';
6
6
  const FLUSH_INTERVAL_MS = 1000;
7
+ function safeStringify(value, space) {
8
+ const seen = new WeakSet();
9
+ return JSON.stringify(value, function (_key, val) {
10
+ if (val instanceof Error) {
11
+ return { name: val.name, message: val.message, stack: val.stack };
12
+ }
13
+ if (typeof val === 'bigint') {
14
+ return val.toString();
15
+ }
16
+ if (typeof val === 'function') {
17
+ return undefined;
18
+ }
19
+ if (typeof val === 'object' && val !== null) {
20
+ const ctor = val.constructor?.name;
21
+ if (ctor === 'TLSSocket' || ctor === 'Socket' || ctor === 'ClientRequest' ||
22
+ ctor === 'IncomingMessage' || ctor === 'HTTPParser' || ctor === 'Agent' ||
23
+ ctor === 'Server' || ctor === 'ReadStream' || ctor === 'WriteStream') {
24
+ return `[${ctor}]`;
25
+ }
26
+ if (seen.has(val)) {
27
+ return '[Circular]';
28
+ }
29
+ seen.add(val);
30
+ }
31
+ return val;
32
+ }, space);
33
+ }
7
34
  function getLogCategory(type) {
8
35
  switch (type) {
9
36
  case 'user_input':
@@ -241,20 +268,21 @@ export class JsonStreamLogger {
241
268
  if (!this.writeStream || this.buffer.length === 0) {
242
269
  return;
243
270
  }
244
- try {
245
- for (const entry of this.buffer) {
271
+ const entries = this.buffer;
272
+ this.buffer = [];
273
+ for (const entry of entries) {
274
+ try {
246
275
  if (!this.isFirstEntry) {
247
276
  this.writeStream.write(',\n');
248
277
  }
249
278
  this.isFirstEntry = false;
250
- const json = JSON.stringify(entry, null, 2);
279
+ const json = safeStringify(entry, 2);
251
280
  const indentedJson = json.split('\n').map(line => ' ' + line).join('\n');
252
281
  this.writeStream.write(indentedJson);
253
282
  }
254
- this.buffer = [];
255
- }
256
- catch (error) {
257
- console.error(chalk.red('Failed to write to JSON stream:'), error);
283
+ catch (error) {
284
+ console.error(chalk.red('Failed to write to JSON stream:'), error);
285
+ }
258
286
  }
259
287
  }
260
288
  async initializeErrorStream() {
@@ -292,20 +320,21 @@ export class JsonStreamLogger {
292
320
  if (!this.errorWriteStream || this.errorBuffer.length === 0) {
293
321
  return;
294
322
  }
295
- try {
296
- for (const entry of this.errorBuffer) {
323
+ const entries = this.errorBuffer;
324
+ this.errorBuffer = [];
325
+ for (const entry of entries) {
326
+ try {
297
327
  if (!this.isFirstErrorEntry) {
298
328
  this.errorWriteStream.write(',\n');
299
329
  }
300
330
  this.isFirstErrorEntry = false;
301
- const json = JSON.stringify(entry, null, 2);
331
+ const json = safeStringify(entry, 2);
302
332
  const indentedJson = json.split('\n').map(line => ' ' + line).join('\n');
303
333
  this.errorWriteStream.write(indentedJson);
304
334
  }
305
- this.errorBuffer = [];
306
- }
307
- catch (error) {
308
- console.error(chalk.red('Failed to write to error JSON stream:'), error);
335
+ catch (error) {
336
+ console.error(chalk.red('Failed to write to error JSON stream:'), error);
337
+ }
309
338
  }
310
339
  }
311
340
  flushCategories() {
@@ -313,20 +342,21 @@ export class JsonStreamLogger {
313
342
  if (!streamInfo.stream || !streamInfo.stream.writable || streamInfo.buffer.length === 0) {
314
343
  continue;
315
344
  }
316
- try {
317
- for (const entry of streamInfo.buffer) {
345
+ const entries = streamInfo.buffer;
346
+ streamInfo.buffer = [];
347
+ for (const entry of entries) {
348
+ try {
318
349
  if (!streamInfo.isFirstEntry) {
319
350
  streamInfo.stream.write(',\n');
320
351
  }
321
352
  streamInfo.isFirstEntry = false;
322
- const json = JSON.stringify(entry, null, 2);
353
+ const json = safeStringify(entry, 2);
323
354
  const indentedJson = json.split('\n').map(line => ' ' + line).join('\n');
324
355
  streamInfo.stream.write(indentedJson);
325
356
  }
326
- streamInfo.buffer = [];
327
- }
328
- catch (error) {
329
- console.error(chalk.red(`Failed to write to category stream:`), error);
357
+ catch (error) {
358
+ console.error(chalk.red(`Failed to write to category stream:`), error);
359
+ }
330
360
  }
331
361
  }
332
362
  }
@@ -371,8 +401,8 @@ export class JsonStreamLogger {
371
401
  }
372
402
  else if (typeof error === 'object' && error !== null) {
373
403
  try {
374
- errorMessage = JSON.stringify(error, null, 2);
375
- errorDetails = error;
404
+ errorMessage = safeStringify(error, 2);
405
+ errorDetails = JSON.parse(errorMessage);
376
406
  }
377
407
  catch {
378
408
  errorMessage = String(error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orquesta-cli",
3
- "version": "0.2.118",
3
+ "version": "0.2.120",
4
4
  "description": "Orquesta CLI - AI-powered coding assistant with team collaboration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",