@contentful/experience-design-system-generation 2.34.1-dev-build-928f202.0 → 2.34.1-dev-build-1576344.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-generation",
3
- "version": "2.34.1-dev-build-928f202.0",
3
+ "version": "2.34.1-dev-build-1576344.0",
4
4
  "description": "Agent-invocation and skill-prompt engine for the Contentful Experience Design System SDK",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,5 +1,4 @@
1
- import { spawn } from 'node:child_process';
2
- import { findBinary, resolveSpawn, spawnSpec } from './lib/binary-launch.js';
1
+ import { findBinary, spawnBinary } from './lib/binary-launch.js';
3
2
  export { AGENT_NAMES, DEFAULT_AGENT_NAME, isAgentName } from './agent-names.js';
4
3
  const VALID_SELECT_TOOL_NAMES = new Set(['select_component', 'reject_component']);
5
4
  function findJsonObjectEnd(line) {
@@ -472,14 +471,8 @@ export async function runAgent(options) {
472
471
  });
473
472
  return new Promise((resolve) => {
474
473
  const bedrockEnv = bedrock ? BEDROCK_ENV_BY_AGENT[agent] : undefined;
475
- // On Windows the agent CLIs are `.cmd` shims, which spawn can neither find
476
- // (libuv ignores PATHEXT) nor start directly (EINVAL). Resolve and wrap.
477
- // Falling back to the bare name keeps the existing 'error' handling path,
478
- // which reports a missing binary far better than throwing from here.
479
- const launch = resolveSpawn(binary, args) ?? { command: binary, args };
480
- const child = spawn(launch.command, launch.args, {
474
+ const child = spawnBinary(binary, args, {
481
475
  stdio: ['pipe', 'pipe', 'pipe'],
482
- ...(launch.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}),
483
476
  ...(bedrockEnv ? { env: { ...process.env, ...bedrockEnv } } : {}),
484
477
  });
485
478
  if (useStdin && child.stdin) {
@@ -535,10 +528,8 @@ export async function runAgent(options) {
535
528
  export async function checkAgentAuth(agent) {
536
529
  const binary = resolveBinary(agent);
537
530
  // Verify the selected agent's binary exists first — for EVERY agent, not
538
- // just claude. findBinary handles both shapes this has to cover: an absolute
539
- // path (e.g. EDS_AGENT_BINARY_<AGENT>=/opt/custom/bin, or C:\tools\claude.cmd)
540
- // and a bare name on PATH. It replaces shelling out to `which`, which doesn't
541
- // exist on Windows, and recognises Windows `.cmd` shims.
531
+ // just claude. Handles an absolute EDS_AGENT_BINARY_<AGENT> override as well as
532
+ // a bare name on PATH.
542
533
  const resolvedBinary = findBinary(binary);
543
534
  if (!resolvedBinary)
544
535
  return 'not-found';
@@ -550,11 +541,8 @@ export async function checkAgentAuth(agent) {
550
541
  // Use `claude auth status` — fast, no API call, works regardless of which
551
542
  // auth provider (direct, Bedrock, Vertex) or whether AWS_PROFILE is set.
552
543
  return new Promise((resolve) => {
553
- // Already resolved above, so wrap the real path rather than looking it up again.
554
- const launch = spawnSpec(resolvedBinary, ['auth', 'status', '--json']);
555
- const child = spawn(launch.command, launch.args, {
544
+ const child = spawnBinary(resolvedBinary, ['auth', 'status', '--json'], {
556
545
  stdio: ['ignore', 'pipe', 'pipe'],
557
- ...(launch.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}),
558
546
  });
559
547
  let stdout = '';
560
548
  let done = false;
@@ -7,5 +7,4 @@ export type { AgentInvoker, CreateLocalCliAgentInvokerOptions, InvokeAgentOption
7
7
  export { buildPrompt, formatCustomPromptBanner, resolveSkillPath } from './prompt-builder.js';
8
8
  export type { ComponentSourceRef, GeneratedCdf, Mode, PromptOptions, Skill } from './prompt-builder.js';
9
9
  export { formatGenerateProgressLine } from './progress.js';
10
- export { binaryExists, findBinary, resolveSpawn, spawnSpec } from './lib/binary-launch.js';
11
- export type { SpawnSpec } from './lib/binary-launch.js';
10
+ export { binaryExists, findBinary, spawnBinary } from './lib/binary-launch.js';
package/dist/src/index.js CHANGED
@@ -9,4 +9,4 @@ export { buildPrompt, formatCustomPromptBanner, resolveSkillPath } from './promp
9
9
  // Progress reporting
10
10
  export { formatGenerateProgressLine } from './progress.js';
11
11
  // Cross-platform binary lookup and launch (Windows .cmd shims, PATHEXT)
12
- export { binaryExists, findBinary, resolveSpawn, spawnSpec } from './lib/binary-launch.js';
12
+ export { binaryExists, findBinary, spawnBinary } from './lib/binary-launch.js';
@@ -1,10 +1,24 @@
1
1
  /**
2
- * Resolve a command to an absolute path, or null when it isn't on PATH.
2
+ * Cross-platform binary lookup and launch.
3
3
  *
4
- * Replaces shelling out to `which`, which doesn't exist on Windows. An absolute
5
- * path is checked directly — on Windows `which` wouldn't resolve one anyway, and
6
- * `isAbsolute` recognises `C:\...` where a `startsWith('/')` test does not.
4
+ * Two separate Windows problems live here, and they need different fixes:
5
+ *
6
+ * 1. LOOKUP. libuv's spawn only tries `.com` and `.exe` when resolving a bare
7
+ * command name against PATH — it ignores PATHEXT. Every tool installed via
8
+ * npm (`claude`, `codex`, `pnpm`) is a `.cmd` shim on Windows, so a bare
9
+ * name resolves to nothing and spawn fails with ENOENT. `findBinary` walks
10
+ * PATH itself and tries the Windows executable extensions.
11
+ *
12
+ * 2. LAUNCH. Even with the full path, Node refuses to start a `.bat`/`.cmd`
13
+ * directly — it throws EINVAL (a deliberate guard added for CVE-2024-27980).
14
+ * Such a file has to run through `cmd.exe /d /s /c`. `spawnSpec` builds that
15
+ * argv.
16
+ *
17
+ * Both take `platform` as an argument so tests can assert Windows behaviour
18
+ * while running on macOS or Linux.
7
19
  */
20
+ import { type ChildProcess, type SpawnOptions } from 'node:child_process';
21
+ /** Resolve a command to an absolute path, or null when it isn't on PATH. */
8
22
  export declare function findBinary(binary: string, platform?: NodeJS.Platform): string | null;
9
23
  /** True when `binary` resolves to something executable. */
10
24
  export declare function binaryExists(binary: string, platform?: NodeJS.Platform): boolean;
@@ -15,21 +29,14 @@ export type SpawnSpec = {
15
29
  windowsVerbatimArguments?: boolean;
16
30
  };
17
31
  /**
18
- * Build the argv for launching an already-resolved executable.
19
- *
20
- * Off Windows, and for a real `.exe`/`.com`, this passes straight through. A
21
- * Windows `.cmd`/`.bat` is wrapped in `cmd.exe /d /s /c "..."`, which is the only
22
- * way to start one.
32
+ * Build the argv for launching an already-resolved executable. A real `.exe`, and
33
+ * anything off Windows, passes straight through.
23
34
  *
24
- * Quoting note: the wrapped form re-parses the command line, so arguments are
25
- * quoted here. That quoting is deliberately simple — double quotes are doubled
26
- * and each argument wrapped — which is sufficient because the arguments reaching
27
- * this path are our own flags and model names. Large or user-controlled text
28
- * (agent prompts) travels over stdin instead, and must keep doing so. If
29
- * user-controlled values ever need to go through argv here, replace this with
30
- * `cross-spawn` rather than extending the escaping: it implements the full
31
- * cmd.exe rules, including the double-escaping that `node_modules/.bin` shims
32
- * require.
35
+ * The quoting is deliberately simple, and is only safe because the arguments
36
+ * reaching it are our own flags and model names — prompts travel over stdin and
37
+ * must keep doing so. If user-controlled text ever needs to go through argv here,
38
+ * switch to `cross-spawn` rather than extending this: it implements the full
39
+ * cmd.exe rules, including the double-escaping `node_modules/.bin` shims need.
33
40
  */
34
41
  export declare function spawnSpec(resolved: string, args: string[], platform?: NodeJS.Platform): SpawnSpec;
35
42
  /**
@@ -38,3 +45,12 @@ export declare function spawnSpec(resolved: string, args: string[], platform?: N
38
45
  * to run.
39
46
  */
40
47
  export declare function resolveSpawn(binary: string, args: string[], platform?: NodeJS.Platform): SpawnSpec | null;
48
+ /**
49
+ * `child_process.spawn`, with Windows shim resolution applied.
50
+ *
51
+ * Use this instead of `spawn` for anything installed by a package manager. When
52
+ * the command can't be resolved it is passed through unchanged, so a missing
53
+ * binary still surfaces as the caller's usual `error` event rather than throwing
54
+ * from here.
55
+ */
56
+ export declare function spawnBinary(command: string, args: string[], options?: SpawnOptions): ChildProcess;
@@ -17,22 +17,14 @@
17
17
  * Both take `platform` as an argument so tests can assert Windows behaviour
18
18
  * while running on macOS or Linux.
19
19
  */
20
+ import { spawn } from 'node:child_process';
20
21
  import { accessSync, constants } from 'node:fs';
21
22
  import { delimiter, isAbsolute, join } from 'node:path';
22
- /**
23
- * Executable suffixes to try for a bare command name. The POSIX case is a
24
- * single empty string: the name is already the whole filename.
25
- */
23
+ /** Suffixes to try for a bare command name; POSIX names carry their own. */
26
24
  function executableExtensions(platform) {
27
25
  return platform === 'win32' ? ['.exe', '.cmd', '.bat', '.com'] : [''];
28
26
  }
29
- /**
30
- * Resolve a command to an absolute path, or null when it isn't on PATH.
31
- *
32
- * Replaces shelling out to `which`, which doesn't exist on Windows. An absolute
33
- * path is checked directly — on Windows `which` wouldn't resolve one anyway, and
34
- * `isAbsolute` recognises `C:\...` where a `startsWith('/')` test does not.
35
- */
27
+ /** Resolve a command to an absolute path, or null when it isn't on PATH. */
36
28
  export function findBinary(binary, platform = process.platform) {
37
29
  const extensions = executableExtensions(platform);
38
30
  if (isAbsolute(binary)) {
@@ -49,8 +41,8 @@ export function findBinary(binary, platform = process.platform) {
49
41
  return null;
50
42
  }
51
43
  for (const directory of (process.env['PATH'] ?? '').split(delimiter)) {
52
- // Skip empty and relative PATH entries: resolving a command against the cwd
53
- // would make the result depend on where the CLI happens to be run from.
44
+ // Skip relative entries: resolving against the cwd would make the result
45
+ // depend on where the CLI was run from.
54
46
  if (!directory || !isAbsolute(directory))
55
47
  continue;
56
48
  for (const extension of extensions) {
@@ -71,21 +63,14 @@ export function binaryExists(binary, platform = process.platform) {
71
63
  return findBinary(binary, platform) !== null;
72
64
  }
73
65
  /**
74
- * Build the argv for launching an already-resolved executable.
66
+ * Build the argv for launching an already-resolved executable. A real `.exe`, and
67
+ * anything off Windows, passes straight through.
75
68
  *
76
- * Off Windows, and for a real `.exe`/`.com`, this passes straight through. A
77
- * Windows `.cmd`/`.bat` is wrapped in `cmd.exe /d /s /c "..."`, which is the only
78
- * way to start one.
79
- *
80
- * Quoting note: the wrapped form re-parses the command line, so arguments are
81
- * quoted here. That quoting is deliberately simple — double quotes are doubled
82
- * and each argument wrapped — which is sufficient because the arguments reaching
83
- * this path are our own flags and model names. Large or user-controlled text
84
- * (agent prompts) travels over stdin instead, and must keep doing so. If
85
- * user-controlled values ever need to go through argv here, replace this with
86
- * `cross-spawn` rather than extending the escaping: it implements the full
87
- * cmd.exe rules, including the double-escaping that `node_modules/.bin` shims
88
- * require.
69
+ * The quoting is deliberately simple, and is only safe because the arguments
70
+ * reaching it are our own flags and model names — prompts travel over stdin and
71
+ * must keep doing so. If user-controlled text ever needs to go through argv here,
72
+ * switch to `cross-spawn` rather than extending this: it implements the full
73
+ * cmd.exe rules, including the double-escaping `node_modules/.bin` shims need.
89
74
  */
90
75
  export function spawnSpec(resolved, args, platform = process.platform) {
91
76
  if (platform !== 'win32' || /\.(exe|com)$/i.test(resolved)) {
@@ -113,3 +98,18 @@ export function resolveSpawn(binary, args, platform = process.platform) {
113
98
  return null;
114
99
  return spawnSpec(resolved, args, platform);
115
100
  }
101
+ /**
102
+ * `child_process.spawn`, with Windows shim resolution applied.
103
+ *
104
+ * Use this instead of `spawn` for anything installed by a package manager. When
105
+ * the command can't be resolved it is passed through unchanged, so a missing
106
+ * binary still surfaces as the caller's usual `error` event rather than throwing
107
+ * from here.
108
+ */
109
+ export function spawnBinary(command, args, options = {}) {
110
+ const launch = resolveSpawn(command, args) ?? { command, args };
111
+ return spawn(launch.command, launch.args, {
112
+ ...options,
113
+ ...(launch.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}),
114
+ });
115
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-generation",
3
- "version": "2.34.1-dev-build-928f202.0",
3
+ "version": "2.34.1-dev-build-1576344.0",
4
4
  "description": "Agent-invocation and skill-prompt engine for the Contentful Experience Design System SDK",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -22,7 +22,7 @@
22
22
  "skills/"
23
23
  ],
24
24
  "dependencies": {
25
- "@contentful/experience-design-system-types": "2.34.1-dev-build-928f202.0"
25
+ "@contentful/experience-design-system-types": "2.34.1-dev-build-1576344.0"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@tsconfig/node24": "^24.0.4",