@claude-flow/cli 3.46.0 → 3.46.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest": {
3
- "version": "3.46.0",
3
+ "version": "3.46.1",
4
4
  "files": {
5
5
  "auto-memory-hook.mjs": "85fe05c757421c52137c0bc8545a0896bab6b4714538c2a11d1d0835bfcc8c1c",
6
6
  "hook-handler.cjs": "209d9fafe10e17d1be0866727f6f9cf9ac66f9a0793f1c793a4f58319e8e4583",
@@ -9,6 +9,6 @@
9
9
  "router.js": "b6998397e7883191b62229ccdc66fb87f1ccca1d5b2039ecb8c1e686d1ad81f8"
10
10
  }
11
11
  },
12
- "signature": "LwKXka3qAKGSIVpcv4vpg5vlv8CFHolvyCQwzW0hNvPIn7Un3+qpidc7drdoDzsrNyzx3gnZJBFEWeXxlTabDA==",
12
+ "signature": "F1SDoeGrsywBpjrcev0ITXmm1FHHi2PuKGrZGudnoH96qyOhbYfvxhvlQIeusWqN7theLd6k3PQ6/67nCjCKAw==",
13
13
  "algorithm": "ed25519"
14
14
  }
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "generation": 6,
4
- "generatedAt": "2026-09-26T22:32:39.004Z",
5
- "gitSha": "39ea61fd",
4
+ "generatedAt": "2026-09-26T23:25:25.296Z",
5
+ "gitSha": "a5adb5aa",
6
6
  "catalog": {
7
7
  "agents": 167,
8
8
  "tools": 418,
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import { readFileSync, existsSync } from 'node:fs';
8
8
  import { validateIdentifier, validateText } from './validate-input.js';
9
+ import { resolveAgentBrowserLaunch, resolveNpxLaunch } from '../runtime/browser-command.js';
9
10
  // Session registry for multi-session support
10
11
  const browserSessions = new Map();
11
12
  /** Preserve agent-browser's JSON error on non-zero exit without echoing other process output. */
@@ -46,36 +47,39 @@ function browserCommandFailure(error, missingExecutable) {
46
47
  export async function execBrowserCommand(args, session = 'default') {
47
48
  const { execFileSync } = await import('child_process');
48
49
  const fullArgs = ['--session', session, '--json', ...args];
50
+ // ADR-401: fullArgs carries tool-supplied text (fill values, eval scripts,
51
+ // selectors), so no spawn here may use a shell. On Windows the npm shims are
52
+ // resolved to the process behind them instead of enabling `shell: true`,
53
+ // which would hand that text to cmd.exe. Never add a `shell` option below.
54
+ const missing = 'Neither agent-browser nor npx found. Install with: npm i -g agent-browser';
49
55
  let result;
50
- try {
51
- result = execFileSync('agent-browser', fullArgs, {
52
- encoding: 'utf-8',
53
- timeout: 30000,
54
- });
56
+ const global = resolveAgentBrowserLaunch();
57
+ if (global) {
58
+ try {
59
+ result = execFileSync(global.command, [...global.argsPrefix, ...fullArgs], {
60
+ encoding: 'utf-8',
61
+ timeout: 30000,
62
+ windowsHide: true,
63
+ });
64
+ }
65
+ catch (error) {
66
+ if (error.code !== 'ENOENT')
67
+ return browserCommandFailure(error);
68
+ }
55
69
  }
56
- catch (error) {
57
- const err = error;
58
- if (err.code === 'ENOENT') {
59
- try {
60
- // #2770: On Windows, `npx` ships as `npx.cmd`; execFileSync cannot spawn
61
- // a .cmd file without going through cmd.exe. Enable shell on win32 so
62
- // cmd.exe resolves the .cmd extension. POSIX keeps shell:false.
63
- // NOTE: shell:true joins args by spaces and passes to cmd.exe — the args
64
- // here are hard-coded flags + a package name, so no injection risk. If
65
- // user-controlled args are ever added, escape them before spawn.
66
- result = execFileSync('npx', ['--yes', 'agent-browser', ...fullArgs], {
67
- encoding: 'utf-8',
68
- timeout: 60000,
69
- shell: process.platform === 'win32',
70
- windowsHide: true,
71
- });
72
- }
73
- catch (npxError) {
74
- return browserCommandFailure(npxError, 'Neither agent-browser nor npx found. Install with: npm i -g agent-browser');
75
- }
70
+ if (result === undefined) {
71
+ const npx = resolveNpxLaunch();
72
+ if (!npx)
73
+ return browserCommandFailure(Object.assign(new Error('npx not resolvable'), { code: 'ENOENT' }), missing);
74
+ try {
75
+ result = execFileSync(npx.command, [...npx.argsPrefix, '--yes', 'agent-browser', ...fullArgs], {
76
+ encoding: 'utf-8',
77
+ timeout: 60000,
78
+ windowsHide: true,
79
+ });
76
80
  }
77
- else {
78
- return browserCommandFailure(error);
81
+ catch (npxError) {
82
+ return browserCommandFailure(npxError, missing);
79
83
  }
80
84
  }
81
85
  let data;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * A process target Node can spawn with `shell: false`.
3
+ * The caller appends its own argv after `argsPrefix`.
4
+ */
5
+ export interface BrowserLaunch {
6
+ command: string;
7
+ argsPrefix: string[];
8
+ }
9
+ export interface BrowserLaunchOptions {
10
+ platform?: NodeJS.Platform;
11
+ nodeExecutable?: string;
12
+ lookup?: (command: string, args: string[]) => string;
13
+ fileExists?: (path: string) => boolean;
14
+ readFile?: (path: string) => string;
15
+ }
16
+ /**
17
+ * Locate a globally installed `agent-browser`.
18
+ * POSIX: the command name itself (spawned without a shell, as before).
19
+ * Windows: a native `.exe`, or `node <entry.js>` behind the npm shim; null when
20
+ * neither can be resolved without a shell.
21
+ */
22
+ export declare function resolveAgentBrowserLaunch(options?: BrowserLaunchOptions): BrowserLaunch | null;
23
+ /**
24
+ * Locate `npx` for the `npx --yes agent-browser` fallback.
25
+ * POSIX: `npx` itself. Windows: `node <npm>/bin/npx-cli.js`, taken from the
26
+ * running Node's own npm or from the npm that `where.exe npx` points at.
27
+ */
28
+ export declare function resolveNpxLaunch(options?: BrowserLaunchOptions): BrowserLaunch | null;
29
+ //# sourceMappingURL=browser-command.d.ts.map
@@ -0,0 +1,101 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { posix, win32 } from 'node:path';
4
+ /**
5
+ * Windows npm shims (`agent-browser.cmd`, `npx.cmd`) are batch files. Node
6
+ * cannot spawn them with `shell: false`, and `shell: true` joins argv with
7
+ * spaces and hands the result to cmd.exe, so every tool-supplied value (a
8
+ * `fill` value, an `eval` script, a selector) becomes shell syntax. Resolve
9
+ * the shim to the JavaScript entry point (or native executable) behind it and
10
+ * spawn that directly, so argv reaches the process as literal data.
11
+ *
12
+ * A target ending in `.cmd` or `.bat` is never returned: spawning one implies
13
+ * cmd.exe regardless of the `shell` option.
14
+ */
15
+ const BATCH_TARGET = /\.(cmd|bat)$/i;
16
+ function defaults(options) {
17
+ const platform = options.platform ?? process.platform;
18
+ return {
19
+ platform,
20
+ path: platform === 'win32' ? win32 : posix,
21
+ nodeExecutable: options.nodeExecutable ?? process.execPath,
22
+ lookup: options.lookup ?? ((command, args) => execFileSync(command, args, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] })),
23
+ fileExists: options.fileExists ?? existsSync,
24
+ readFile: options.readFile ?? ((file) => readFileSync(file, 'utf8')),
25
+ };
26
+ }
27
+ function whereAll(lookup, name) {
28
+ try {
29
+ return lookup('where.exe', [name])
30
+ .split(/\r?\n/)
31
+ .map((line) => line.trim())
32
+ .filter(Boolean);
33
+ }
34
+ catch {
35
+ return [];
36
+ }
37
+ }
38
+ /** `bin` of a package.json, resolved to a path that stays inside the package. */
39
+ function packageBinEntry(packageRoot, binName, d) {
40
+ let manifest;
41
+ try {
42
+ manifest = JSON.parse(d.readFile(d.path.join(packageRoot, 'package.json')));
43
+ }
44
+ catch {
45
+ return null;
46
+ }
47
+ const relative = typeof manifest.bin === 'string' ? manifest.bin : manifest.bin?.[binName];
48
+ if (typeof relative !== 'string' || !relative)
49
+ return null;
50
+ const entry = d.path.resolve(packageRoot, relative);
51
+ const rootWithSep = d.path.resolve(packageRoot) + d.path.sep;
52
+ if (!entry.startsWith(rootWithSep))
53
+ return null;
54
+ return d.fileExists(entry) ? entry : null;
55
+ }
56
+ /**
57
+ * Locate a globally installed `agent-browser`.
58
+ * POSIX: the command name itself (spawned without a shell, as before).
59
+ * Windows: a native `.exe`, or `node <entry.js>` behind the npm shim; null when
60
+ * neither can be resolved without a shell.
61
+ */
62
+ export function resolveAgentBrowserLaunch(options = {}) {
63
+ const d = defaults(options);
64
+ if (d.platform !== 'win32')
65
+ return { command: 'agent-browser', argsPrefix: [] };
66
+ const matches = whereAll(d.lookup, 'agent-browser');
67
+ const executable = matches.find((candidate) => d.path.extname(candidate).toLowerCase() === '.exe' && d.fileExists(candidate));
68
+ if (executable)
69
+ return { command: executable, argsPrefix: [] };
70
+ for (const shim of matches) {
71
+ const packageRoot = d.path.join(d.path.dirname(shim), 'node_modules', 'agent-browser');
72
+ const entry = packageBinEntry(packageRoot, 'agent-browser', d);
73
+ if (!entry || BATCH_TARGET.test(entry))
74
+ continue;
75
+ if (d.path.extname(entry).toLowerCase() === '.exe')
76
+ return { command: entry, argsPrefix: [] };
77
+ return { command: d.nodeExecutable, argsPrefix: [entry] };
78
+ }
79
+ return null;
80
+ }
81
+ /**
82
+ * Locate `npx` for the `npx --yes agent-browser` fallback.
83
+ * POSIX: `npx` itself. Windows: `node <npm>/bin/npx-cli.js`, taken from the
84
+ * running Node's own npm or from the npm that `where.exe npx` points at.
85
+ */
86
+ export function resolveNpxLaunch(options = {}) {
87
+ const d = defaults(options);
88
+ if (d.platform !== 'win32')
89
+ return { command: 'npx', argsPrefix: [] };
90
+ const roots = [
91
+ d.path.dirname(d.nodeExecutable),
92
+ ...whereAll(d.lookup, 'npx').map((shim) => d.path.dirname(shim)),
93
+ ];
94
+ for (const root of roots) {
95
+ const entry = d.path.join(root, 'node_modules', 'npm', 'bin', 'npx-cli.js');
96
+ if (d.fileExists(entry))
97
+ return { command: d.nodeExecutable, argsPrefix: [entry] };
98
+ }
99
+ return null;
100
+ }
101
+ //# sourceMappingURL=browser-command.js.map
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-flow/cli",
3
- "version": "3.46.0",
3
+ "version": "3.46.1",
4
4
  "type": "module",
5
5
  "description": "Ruflo CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
6
6
  "main": "dist/src/index.js",