cli4ai 1.2.2 → 1.2.3

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.
@@ -6,10 +6,9 @@ import { resolve, dirname, join, normalize } from 'path';
6
6
  import { createInterface } from 'readline';
7
7
  import { tmpdir, platform } from 'os';
8
8
  import { spawnSync } from 'child_process';
9
- // Windows-safe command helpers
9
+ // Windows compatibility: use shell: true on Windows to handle .cmd wrappers
10
+ // This is safe because package names are validated with strict regex patterns
10
11
  const isWindows = platform() === 'win32';
11
- const npmCmd = isWindows ? 'npm.cmd' : 'npm';
12
- const tarCmd = 'tar'; // Windows 10+ has tar built-in
13
12
  import { output, outputError, log } from '../lib/cli.js';
14
13
  import { loadManifest, tryLoadManifest } from '../core/manifest.js';
15
14
  import { ensureCli4aiHome, ensureLocalDir, PACKAGES_DIR, LOCAL_PACKAGES_DIR, loadConfig } from '../core/config.js';
@@ -102,10 +101,11 @@ async function downloadFromNpm(packageName, targetDir) {
102
101
  try {
103
102
  // Use npm pack to download the tarball - using spawnSync to prevent command injection
104
103
  log(`Downloading ${packageName} from npm...`);
105
- const packResult = spawnSync(npmCmd, ['pack', packageName, `--pack-destination=${tmpDir}`], {
104
+ const packResult = spawnSync('npm', ['pack', packageName, `--pack-destination=${tmpDir}`], {
106
105
  stdio: 'pipe',
107
106
  cwd: tmpDir,
108
- encoding: 'utf-8'
107
+ encoding: 'utf-8',
108
+ shell: isWindows // Required on Windows to find npm.cmd
109
109
  });
110
110
  if (packResult.error) {
111
111
  throw new Error(`npm not found or failed to execute: ${packResult.error.message}`);
@@ -122,9 +122,10 @@ async function downloadFromNpm(packageName, targetDir) {
122
122
  // Extract the tarball using spawnSync to prevent command injection
123
123
  // Use --strip-components=1 equivalent by extracting to a subdir
124
124
  const tarPath = join(tmpDir, tarball);
125
- const extractResult = spawnSync(tarCmd, ['-xzf', tarPath, '-C', tmpDir], {
125
+ const extractResult = spawnSync('tar', ['-xzf', tarPath, '-C', tmpDir], {
126
126
  stdio: 'pipe',
127
- encoding: 'utf-8'
127
+ encoding: 'utf-8',
128
+ shell: isWindows // Required on Windows
128
129
  });
129
130
  if (extractResult.status !== 0) {
130
131
  throw new Error(`Failed to extract package: ${extractResult.stderr || 'tar extraction failed'}`);
@@ -181,9 +182,10 @@ async function installNpmDependencies(pkgPath, dependencies) {
181
182
  if (deps.length === 0)
182
183
  return;
183
184
  log(`Installing npm dependencies: ${deps.join(', ')}`);
184
- const result = spawnSync(npmCmd, ['install', ...deps], {
185
+ const result = spawnSync('npm', ['install', ...deps], {
185
186
  cwd: pkgPath,
186
- stdio: 'inherit'
187
+ stdio: 'inherit',
188
+ shell: isWindows // Required on Windows to find npm.cmd
187
189
  });
188
190
  if (result.status !== 0) {
189
191
  throw new Error(`Failed to install dependencies (exit code: ${result.status})`);
@@ -5,8 +5,8 @@ import { spawnSync } from 'child_process';
5
5
  import { platform } from 'os';
6
6
  import { log, outputError } from '../lib/cli.js';
7
7
  import { getNpmGlobalPackages, getGlobalPackages, getLocalPackages } from '../core/config.js';
8
- // Windows-safe cli4ai command (npm creates .cmd wrappers on Windows)
9
- const cli4aiCmd = platform() === 'win32' ? 'cli4ai.cmd' : 'cli4ai';
8
+ // Windows compatibility
9
+ const isWindows = platform() === 'win32';
10
10
  // ANSI codes
11
11
  const RESET = '\x1B[0m';
12
12
  const BOLD = '\x1B[1m';
@@ -331,7 +331,7 @@ async function installPackages(packages, scope) {
331
331
  if (scopeFlag)
332
332
  addArgs.push(scopeFlag);
333
333
  addArgs.push('-y');
334
- const result = spawnSync(cli4aiCmd, addArgs, { stdio: 'pipe' });
334
+ const result = spawnSync('cli4ai', addArgs, { stdio: 'pipe', shell: isWindows });
335
335
  clearInterval(spinner);
336
336
  if (result.status === 0) {
337
337
  process.stderr.write(`\r ${GREEN}✓${RESET} ${BOLD}${shortName}${RESET} installed \n`);
@@ -5,8 +5,8 @@ import { resolve } from 'path';
5
5
  import { spawnSync } from 'child_process';
6
6
  import { platform } from 'os';
7
7
  import { output, outputError, log } from '../lib/cli.js';
8
- // Windows-safe npm command
9
- const npmCmd = platform() === 'win32' ? 'npm.cmd' : 'npm';
8
+ // Windows compatibility
9
+ const isWindows = platform() === 'win32';
10
10
  import { findPackage, loadConfig } from '../core/config.js';
11
11
  import { loadManifest, tryLoadManifest } from '../core/manifest.js';
12
12
  import { remotePackageInfo, RemoteConnectionError, RemoteApiError } from '../core/remote-client.js';
@@ -65,10 +65,11 @@ export async function infoCommand(packageName, options) {
65
65
  try {
66
66
  log(`Fetching ${scopedName} from npm...`);
67
67
  // Use spawnSync with argument array to prevent command injection
68
- const result = spawnSync(npmCmd, ['view', scopedName, '--json'], {
68
+ const result = spawnSync('npm', ['view', scopedName, '--json'], {
69
69
  encoding: 'utf-8',
70
70
  timeout: 10000,
71
- stdio: ['pipe', 'pipe', 'pipe']
71
+ stdio: ['pipe', 'pipe', 'pipe'],
72
+ shell: isWindows // Required on Windows to find npm.cmd
72
73
  });
73
74
  if (result.status === 0 && result.stdout) {
74
75
  const pkg = JSON.parse(result.stdout);
@@ -6,8 +6,8 @@ import { resolve } from 'path';
6
6
  import { spawnSync } from 'child_process';
7
7
  import { platform } from 'os';
8
8
  import { output, outputError, log } from '../lib/cli.js';
9
- // Windows-safe npm command
10
- const npmCmd = platform() === 'win32' ? 'npm.cmd' : 'npm';
9
+ // Windows compatibility
10
+ const isWindows = platform() === 'win32';
11
11
  import { loadConfig, getGlobalPackages, getLocalPackages } from '../core/config.js';
12
12
  import { tryLoadManifest } from '../core/manifest.js';
13
13
  import { remoteListPackages, RemoteConnectionError, RemoteApiError } from '../core/remote-client.js';
@@ -97,18 +97,20 @@ export async function searchCommand(query, options) {
97
97
  try {
98
98
  log(`Searching npm for @cli4ai packages...`);
99
99
  // Use spawnSync with argument array to prevent command injection
100
- const searchResult = spawnSync(npmCmd, ['search', `@cli4ai/${query}`, '--json'], {
100
+ const searchResult = spawnSync('npm', ['search', `@cli4ai/${query}`, '--json'], {
101
101
  encoding: 'utf-8',
102
102
  timeout: 10000,
103
- stdio: ['pipe', 'pipe', 'pipe']
103
+ stdio: ['pipe', 'pipe', 'pipe'],
104
+ shell: isWindows // Required on Windows to find npm.cmd
104
105
  });
105
106
  let npmResults = searchResult.stdout || '';
106
107
  // Fallback to searching @cli4ai if specific query fails
107
108
  if (!npmResults || npmResults === '[]') {
108
- const fallbackResult = spawnSync(npmCmd, ['search', '@cli4ai', '--json'], {
109
+ const fallbackResult = spawnSync('npm', ['search', '@cli4ai', '--json'], {
109
110
  encoding: 'utf-8',
110
111
  timeout: 10000,
111
- stdio: ['pipe', 'pipe', 'pipe']
112
+ stdio: ['pipe', 'pipe', 'pipe'],
113
+ shell: isWindows // Required on Windows to find npm.cmd
112
114
  });
113
115
  npmResults = fallbackResult.stdout || '[]';
114
116
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cli4ai",
3
- "version": "1.2.2",
3
+ "version": "1.2.3",
4
4
  "description": "The package manager for AI CLI tools - cli4ai.com",
5
5
  "type": "module",
6
6
  "bin": {