@dashclaw/cli 0.7.1 → 0.7.4

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/lib/up/index.js CHANGED
@@ -20,7 +20,7 @@ import { join } from 'node:path';
20
20
  import { STEPS, loadInstance, saveInstance, checkpoint } from './instance.js';
21
21
  import { resolveAppVersion, downloadAndExtract } from './fetch-app.js';
22
22
  import { dockerAvailableSync, chooseDbMode, provisionDatabase } from './db.js';
23
- import { installDeps, buildApp, startServer, waitForHealth, openBrowser } from './run.js';
23
+ import { installDeps, buildApp, startServer, waitForHealth, openBrowser, winSafeSpawnSync } from './run.js';
24
24
  import { installClaude } from '../claude/install.js';
25
25
  import { parseUpArgs } from './args.js';
26
26
  import { ask } from '../config.js';
@@ -58,7 +58,7 @@ export function resolveBaseDir(args) {
58
58
  * @param {object} [opts.logger]
59
59
  * @param {Function} [opts.spawn] injectable spawnSync for testing (default: spawnSync)
60
60
  */
61
- export function runSetupScriptReal({ appDir, databaseUrl, logger = console, spawn: spawnFn = spawnSync }) {
61
+ export function runSetupScriptReal({ appDir, databaseUrl, logger = console, spawn: spawnFn = winSafeSpawnSync }) {
62
62
  logger.error('-> Running setup (migrations + first admin) ...');
63
63
  const res = spawnFn(
64
64
  'node',
@@ -71,7 +71,9 @@ export function runSetupScriptReal({ appDir, databaseUrl, logger = console, spaw
71
71
  ],
72
72
  // stdin MUST be 'ignore': the default open pipe makes any stray readline
73
73
  // prompt in the child hang forever (observed: 12-minute silent hang).
74
- { cwd: appDir, encoding: 'utf8', shell: process.platform === 'win32', stdio: ['ignore', 'pipe', 'pipe'] },
74
+ // Windows shell handling (npm-style .cmd resolution + DEP0190 avoidance)
75
+ // lives in winSafeSpawnSync.
76
+ { cwd: appDir, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] },
75
77
  );
76
78
  const stdout = res.stdout || '';
77
79
  const lines = stdout.split('\n').map((l) => l.trim()).filter(Boolean);
@@ -178,7 +180,7 @@ export async function runUp({ args, baseDir = join(homedir(), '.dashclaw'), deps
178
180
  // is both wrong and hangs. Reuse the saved URL when db_ready is already
179
181
  // checkpointed and inst.databaseUrl is set.
180
182
  const dbMode = inst.dbMode ?? await deps.chooseDbMode({
181
- flagDb: args.db, dockerAvailable: deps.dockerAvailable, yes: args.yes, promptFn: deps.promptFn,
183
+ flagDb: args.db, dockerAvailable: deps.dockerAvailable, yes: args.yes, promptFn: deps.promptFn, logger,
182
184
  });
183
185
  const db = (dbMode === 'url' && done('db_ready') && inst.databaseUrl)
184
186
  ? { databaseUrl: inst.databaseUrl, stop: async () => {} }
@@ -313,6 +315,15 @@ export async function runDown({
313
315
  dockerStop('dashclaw-pg');
314
316
  logger.log('[ok] Stopped Docker Postgres (container dashclaw-pg).');
315
317
  }
318
+ if (inst.dbMode === 'embedded' && process.platform === 'win32') {
319
+ // On Windows the embedded server runs detached via pg_ctl (see db.js) and
320
+ // survives the up process — stop it here, best-effort.
321
+ try {
322
+ const { pg_ctl } = await import('@embedded-postgres/windows-x64');
323
+ const res = spawnSync(pg_ctl, ['-D', join(baseDir, 'pg'), '-m', 'fast', 'stop'], { stdio: 'ignore' });
324
+ if (res.status === 0) logger.log('[ok] Stopped embedded Postgres.');
325
+ } catch { /* binaries not installed — nothing to stop */ }
326
+ }
316
327
  }
317
328
 
318
329
  /**
package/lib/up/run.js CHANGED
@@ -6,26 +6,45 @@ import { spawn, spawnSync } from 'node:child_process';
6
6
 
7
7
  const shell = process.platform === 'win32';
8
8
 
9
- function npm(args, cwd, logger, runner = spawnSync) {
9
+ // npm/npx are .cmd files on Windows, which node refuses to spawn without a
10
+ // shell — but shell:true + an args ARRAY is deprecated (DEP0190, and the
11
+ // warning prints mid-flow for first-run users). So on Windows the command is
12
+ // joined into ONE string (quoting any arg with shell-special characters) and
13
+ // handed to the shell whole; elsewhere args pass through verbatim, no shell.
14
+ const quoteArg = (a) => (/^[\w\-.:/@=]+$/.test(String(a)) ? String(a) : `"${a}"`);
15
+ export function winSafeSpawnArgs(cmd, args) {
16
+ return shell
17
+ ? { cmd: [cmd, ...args.map(quoteArg)].join(' '), args: [], opts: { shell: true } }
18
+ : { cmd, args, opts: {} };
19
+ }
20
+
21
+ /** spawnSync that takes LOGICAL (cmd, args) and win-joins only at the edge. */
22
+ export function winSafeSpawnSync(cmd, args, opts) {
23
+ const safe = winSafeSpawnArgs(cmd, args);
24
+ return spawnSync(safe.cmd, safe.args, { ...opts, ...safe.opts });
25
+ }
26
+
27
+ function npm(args, cwd, logger, runner = winSafeSpawnSync) {
10
28
  logger.error(`-> npm ${args.join(' ')}`);
11
- const res = runner('npm', args, { cwd, stdio: ['ignore', 'inherit', 'inherit'], shell });
29
+ const res = runner('npm', args, { cwd, stdio: ['ignore', 'inherit', 'inherit'] });
12
30
  if (res.status !== 0) throw new Error(`npm ${args[0]} failed (exit ${res.status}). Re-run \`npx dashclaw up\` to resume from this step.`);
13
31
  }
14
32
 
15
- export function installDeps(appDir, logger = console, runner = spawnSync) {
33
+ export function installDeps(appDir, logger = console, runner = winSafeSpawnSync) {
16
34
  try { npm(['ci', '--no-audit', '--no-fund'], appDir, logger, runner); }
17
35
  catch { npm(['install', '--no-audit', '--no-fund'], appDir, logger, runner); } // lockfile mismatch fallback
18
36
  }
19
37
 
20
- export function buildApp(appDir, logger = console, runner = spawnSync) {
38
+ export function buildApp(appDir, logger = console, runner = winSafeSpawnSync) {
21
39
  npm(['run', 'build'], appDir, logger, runner);
22
40
  }
23
41
 
24
42
  /** Start `next start` as a child; .env.local in appDir is loaded by Next itself. */
25
43
  export function startServer({ appDir, port, logger = console }) {
26
44
  logger.error(`-> Starting server on :${port}`);
27
- const child = spawn('npx', ['next', 'start', '-p', String(port)], {
28
- cwd: appDir, stdio: ['ignore', 'inherit', 'inherit'], shell,
45
+ const safe = winSafeSpawnArgs('npx', ['next', 'start', '-p', String(port)]);
46
+ const child = spawn(safe.cmd, safe.args, {
47
+ cwd: appDir, stdio: ['ignore', 'inherit', 'inherit'], ...safe.opts,
29
48
  });
30
49
  return child;
31
50
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dashclaw/cli",
3
- "version": "0.7.1",
3
+ "version": "0.7.4",
4
4
  "description": "DashClaw terminal client — approve agent actions and diagnose your instance",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -27,6 +27,7 @@
27
27
  "dependencies": {
28
28
  "dashclaw": "^2.2.1",
29
29
  "embedded-postgres": "18.4.0-beta.17",
30
+ "pg": "^8.22.0",
30
31
  "tar": "^7.4.0"
31
32
  },
32
33
  "license": "MIT",