@wrongstack/acp 0.276.4 → 0.277.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/dist/client.js CHANGED
@@ -4,6 +4,32 @@ import * as path from 'path';
4
4
  import { spawn } from 'child_process';
5
5
  import '@wrongstack/core/coordination';
6
6
 
7
+ // src/agent/stdio-transport.ts
8
+
9
+ // src/win32-cmd.ts
10
+ var WIN32_CMD_META = /[&|<>"\r\n\0]/;
11
+ function buildWin32CmdShimInvocation(command, args = []) {
12
+ assertSafeWin32CmdArgs([command, ...args]);
13
+ const line = ["call", quoteWin32CmdArg(command), ...args.map(quoteWin32CmdArg)].join(" ");
14
+ return {
15
+ command: process.env["COMSPEC"] ?? "cmd.exe",
16
+ args: ["/d", "/c", line],
17
+ windowsVerbatimArguments: true
18
+ };
19
+ }
20
+ function assertSafeWin32CmdArgs(args) {
21
+ for (const arg of args) {
22
+ if (typeof arg === "string" && WIN32_CMD_META.test(arg)) {
23
+ throw new Error(
24
+ 'win32 cmd shim spawn: argument contains a shell metacharacter (one of & | < > ", or a newline) that could enable command injection through the .cmd/.bat wrapper - refusing to run. Offending argument: ' + JSON.stringify(arg)
25
+ );
26
+ }
27
+ }
28
+ }
29
+ function quoteWin32CmdArg(arg) {
30
+ return `"${arg}"`;
31
+ }
32
+
7
33
  // src/agent/stdio-transport.ts
8
34
  var ClientTransport = class {
9
35
  child = null;
@@ -35,19 +61,14 @@ var ClientTransport = class {
35
61
  const isPkgLauncher = this.opts.command === "npx" || this.opts.command === "uvx";
36
62
  const spawnCwd = isPkgLauncher ? os.homedir() : this.opts.cwd;
37
63
  try {
38
- this.child = spawn2(this.opts.command, this.opts.args ?? [], {
64
+ const childArgs = this.opts.args ?? [];
65
+ const shim = process.platform === "win32" ? buildWin32CmdShimInvocation(this.opts.command, childArgs) : null;
66
+ this.child = spawn2(shim?.command ?? this.opts.command, shim?.args ?? childArgs, {
39
67
  env: { ...buildChildEnv(), ...this.opts.env },
40
68
  cwd: spawnCwd,
41
69
  stdio: ["pipe", "pipe", "pipe"],
42
70
  windowsHide: true,
43
- // On Windows, most ACP-supporting tools (claude, gemini, codex,
44
- // qwen, copilot) are installed as `.cmd` shims under
45
- // AppData\Roaming\npm\. Node's spawn won't find them via
46
- // `shell: false` because the .cmd extension is not in the
47
- // default PATHEXT lookup. The argv here is always from our
48
- // own static catalog or from a hardcoded spec, never from
49
- // user input, so shell-expansion is bounded.
50
- shell: process.platform === "win32"
71
+ ...shim ? { windowsVerbatimArguments: shim.windowsVerbatimArguments } : {}
51
72
  });
52
73
  } catch (err) {
53
74
  clearTimeout(timeout);