@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/claude/install.js +4 -1
- package/lib/local-doctor.js +563 -532
- package/lib/up/db.js +560 -318
- package/lib/up/index.js +15 -4
- package/lib/up/run.js +25 -6
- package/package.json +2 -1
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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']
|
|
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 =
|
|
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 =
|
|
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
|
|
28
|
-
|
|
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.
|
|
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",
|