@dinoxx/dinox-cli 2.8.0 → 2.8.1

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.
@@ -7,6 +7,7 @@ import { ClientSideConnection, ndJsonStream, PROTOCOL_VERSION, } from '@agentcli
7
7
  import { generateId } from '../../utils/id.js';
8
8
  import { getPackageVersion } from '../../utils/version.js';
9
9
  import { codexGeneratedImagesDir, codexImagesSince } from './codexImages.js';
10
+ import { agentLaunch } from '../spawnAgent.js';
10
11
  const TOOL_STATUS = {
11
12
  pending: 'pending',
12
13
  in_progress: 'running',
@@ -278,13 +279,14 @@ export class AcpGatewayAdapter {
278
279
  const command = this.gateway.command;
279
280
  if (!command)
280
281
  throw new Error(`Gateway ${this.gateway.name} has no ACP command`);
281
- const child = spawn(command, this.gateway.args ?? [], {
282
+ const launch = agentLaunch(command, this.gateway.args ?? [], {
282
283
  cwd: this.gateway.cwd ?? os.homedir(),
283
284
  env: { ...process.env, ...(this.gateway.env ?? {}) },
284
285
  stdio: ['pipe', 'pipe', 'pipe'],
285
286
  // The daemon runs without a visible console on Windows; keep agents that way.
286
287
  windowsHide: true,
287
288
  });
289
+ const child = spawn(launch.command, launch.args, launch.options);
288
290
  child.stderr.on('data', (chunk) => this.log('agent stderr', { text: chunk.toString('utf8').slice(0, 2000) }));
289
291
  const exited = new Promise((_, reject) => {
290
292
  child.once('error', reject);
@@ -3,6 +3,7 @@ import fs from 'node:fs/promises';
3
3
  import path from 'node:path';
4
4
  import { Readable, Writable } from 'node:stream';
5
5
  import { ClientSideConnection, ndJsonStream, PROTOCOL_VERSION } from '@agentclientprotocol/sdk';
6
+ import { agentLaunch } from './spawnAgent.js';
6
7
  import { getPackageVersion } from '../utils/version.js';
7
8
  /**
8
9
  * Known agents that speak ACP over stdio. Each lists launch variants in order;
@@ -70,7 +71,10 @@ export async function findExecutable(name, envPath = process.env.PATH ?? '') {
70
71
  }
71
72
  /** Starts the agent, runs `initialize`, and stops it. Never leaves the process running. */
72
73
  export async function probeAcpAgent(command, args, timeoutMs = PROBE_TIMEOUT_MS) {
73
- const child = spawn(command, args, { stdio: ['pipe', 'pipe', 'ignore'], env: process.env, windowsHide: true });
74
+ const launch = agentLaunch(command, args, { stdio: ['pipe', 'pipe', 'ignore'], env: process.env, windowsHide: true });
75
+ const child = spawn(launch.command, launch.args, launch.options);
76
+ if (!child.stdin || !child.stdout)
77
+ throw new Error('ACP probe stdio was not piped');
74
78
  let timer;
75
79
  try {
76
80
  const exited = new Promise((_, reject) => {
@@ -97,7 +101,7 @@ export async function probeAcpAgent(command, args, timeoutMs = PROBE_TIMEOUT_MS)
97
101
  }
98
102
  finally {
99
103
  clearTimeout(timer);
100
- child.stdin.destroy();
104
+ child.stdin?.destroy();
101
105
  child.kill('SIGTERM');
102
106
  }
103
107
  }
@@ -0,0 +1,12 @@
1
+ import type { SpawnOptions } from 'node:child_process';
2
+ export type AgentLaunch = {
3
+ command: string;
4
+ args: readonly string[];
5
+ options: SpawnOptions;
6
+ };
7
+ /**
8
+ * Node's `spawn` cannot execute a Windows `.cmd` / `.bat` directly: CreateProcess
9
+ * returns EINVAL. npm global shims are those files (`claude-agent-acp.CMD`).
10
+ * Launch them through `cmd.exe /d /s /c`, with the same argument array everywhere else.
11
+ */
12
+ export declare function agentLaunch(command: string, args: readonly string[], options: SpawnOptions, platform?: NodeJS.Platform): AgentLaunch;
@@ -0,0 +1,32 @@
1
+ const WINDOWS_BATCH = /\.(?:cmd|bat)$/i;
2
+ /** Characters cmd.exe would interpret if a batch path or argument reached the shell unquoted. */
3
+ const CMD_METACHARACTERS = /[\0\r\n"&|<>^%]/;
4
+ /**
5
+ * Node's `spawn` cannot execute a Windows `.cmd` / `.bat` directly: CreateProcess
6
+ * returns EINVAL. npm global shims are those files (`claude-agent-acp.CMD`).
7
+ * Launch them through `cmd.exe /d /s /c`, with the same argument array everywhere else.
8
+ */
9
+ export function agentLaunch(command, args, options, platform = process.platform) {
10
+ if (platform !== 'win32' || !WINDOWS_BATCH.test(command)) {
11
+ return { command, args, options };
12
+ }
13
+ for (const part of [command, ...args]) {
14
+ if (CMD_METACHARACTERS.test(part)) {
15
+ throw new Error(`Cannot launch a Windows batch file containing shell metacharacters (${part})`);
16
+ }
17
+ }
18
+ const inner = [quoteCmdArg(command), ...args.map(quoteCmdArg)].join(' ');
19
+ return {
20
+ command: process.env.ComSpec || 'cmd.exe',
21
+ args: ['/d', '/s', '/c', `"${inner}"`],
22
+ options: { ...options, windowsVerbatimArguments: true },
23
+ };
24
+ }
25
+ /** Quote one argv token for CommandLineToArgvW. Tokens without spaces stay bare. */
26
+ function quoteCmdArg(value) {
27
+ if (value.length === 0)
28
+ return '""';
29
+ if (!/[\s"]/.test(value))
30
+ return value;
31
+ return `"${value.replace(/(\\*)"/g, '$1$1\\"').replace(/(\\*)$/, '$1$1')}"`;
32
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dinoxx/dinox-cli",
3
- "version": "2.8.0",
3
+ "version": "2.8.1",
4
4
  "description": "Dinox CLI",
5
5
  "main": "dist/dinox.js",
6
6
  "scripts": {