fix: harden doctor install checks
This commit is contained in:
39
src/commands/doctor-install.ts
Normal file
39
src/commands/doctor-install.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
import { note } from "../terminal/note.js";
|
||||
|
||||
export function noteSourceInstallIssues(root: string | null) {
|
||||
if (!root) return;
|
||||
|
||||
const workspaceMarker = path.join(root, "pnpm-workspace.yaml");
|
||||
if (!fs.existsSync(workspaceMarker)) return;
|
||||
|
||||
const warnings: string[] = [];
|
||||
const nodeModules = path.join(root, "node_modules");
|
||||
const pnpmStore = path.join(nodeModules, ".pnpm");
|
||||
const tsxBin = path.join(nodeModules, ".bin", "tsx");
|
||||
const srcEntry = path.join(root, "src", "entry.ts");
|
||||
|
||||
if (fs.existsSync(nodeModules) && !fs.existsSync(pnpmStore)) {
|
||||
warnings.push(
|
||||
"- node_modules was not installed by pnpm (missing node_modules/.pnpm). Run: pnpm install",
|
||||
);
|
||||
}
|
||||
|
||||
if (fs.existsSync(path.join(root, "package-lock.json"))) {
|
||||
warnings.push(
|
||||
"- package-lock.json present in a pnpm workspace. If you ran npm install, remove it and reinstall with pnpm.",
|
||||
);
|
||||
}
|
||||
|
||||
if (fs.existsSync(srcEntry) && !fs.existsSync(tsxBin)) {
|
||||
warnings.push(
|
||||
"- tsx binary is missing for source runs. Run: pnpm install",
|
||||
);
|
||||
}
|
||||
|
||||
if (warnings.length > 0) {
|
||||
note(warnings.join("\n"), "Install");
|
||||
}
|
||||
}
|
||||
@@ -23,10 +23,52 @@ export async function maybeRepairUiProtocolFreshness(
|
||||
|
||||
try {
|
||||
const [schemaStats, uiStats] = await Promise.all([
|
||||
fs.stat(schemaPath),
|
||||
fs.stat(uiIndexPath),
|
||||
fs.stat(schemaPath).catch(() => null),
|
||||
fs.stat(uiIndexPath).catch(() => null),
|
||||
]);
|
||||
|
||||
if (schemaStats && !uiStats) {
|
||||
note(
|
||||
[
|
||||
"- Control UI assets are missing.",
|
||||
"- Run: pnpm ui:build",
|
||||
].join("\n"),
|
||||
"UI",
|
||||
);
|
||||
|
||||
const shouldRepair = await prompter.confirmRepair({
|
||||
message: "Build Control UI assets now?",
|
||||
initialValue: true,
|
||||
});
|
||||
|
||||
if (shouldRepair) {
|
||||
note("Building Control UI assets... (this may take a moment)", "UI");
|
||||
const uiScriptPath = path.join(root, "scripts/ui.js");
|
||||
const buildResult = await runCommandWithTimeout(
|
||||
[process.execPath, uiScriptPath, "build"],
|
||||
{
|
||||
cwd: root,
|
||||
timeoutMs: 120_000,
|
||||
env: { ...process.env, FORCE_COLOR: "1" },
|
||||
},
|
||||
);
|
||||
if (buildResult.code === 0) {
|
||||
note("UI build complete.", "UI");
|
||||
} else {
|
||||
const details = [
|
||||
`UI build failed (exit ${buildResult.code ?? "unknown"}).`,
|
||||
buildResult.stderr.trim() ? buildResult.stderr.trim() : null,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("\n");
|
||||
note(details, "UI");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!schemaStats || !uiStats) return;
|
||||
|
||||
if (schemaStats.mtime > uiStats.mtime) {
|
||||
const uiMtimeIso = uiStats.mtime.toISOString();
|
||||
// Find changes since the UI build
|
||||
|
||||
@@ -62,6 +62,7 @@ import {
|
||||
maybeRepairGatewayServiceConfig,
|
||||
maybeScanExtraGatewayServices,
|
||||
} from "./doctor-gateway-services.js";
|
||||
import { noteSourceInstallIssues } from "./doctor-install.js";
|
||||
import {
|
||||
maybeMigrateLegacyConfigFile,
|
||||
normalizeLegacyConfigValues,
|
||||
@@ -187,6 +188,12 @@ export async function doctorCommand(
|
||||
printWizardHeader(runtime);
|
||||
intro("Clawdbot doctor");
|
||||
|
||||
const root = await resolveClawdbotPackageRoot({
|
||||
moduleUrl: import.meta.url,
|
||||
argv1: process.argv[1],
|
||||
cwd: process.cwd(),
|
||||
});
|
||||
|
||||
const updateInProgress = process.env.CLAWDBOT_UPDATE_IN_PROGRESS === "1";
|
||||
const canOfferUpdate =
|
||||
!updateInProgress &&
|
||||
@@ -195,11 +202,6 @@ export async function doctorCommand(
|
||||
options.repair !== true &&
|
||||
Boolean(process.stdin.isTTY);
|
||||
if (canOfferUpdate) {
|
||||
const root = await resolveClawdbotPackageRoot({
|
||||
moduleUrl: import.meta.url,
|
||||
argv1: process.argv[1],
|
||||
cwd: process.cwd(),
|
||||
});
|
||||
if (root) {
|
||||
const git = await detectClawdbotGitCheckout(root);
|
||||
if (git === "git") {
|
||||
@@ -250,6 +252,7 @@ export async function doctorCommand(
|
||||
}
|
||||
|
||||
await maybeRepairUiProtocolFreshness(runtime, prompter);
|
||||
noteSourceInstallIssues(root);
|
||||
|
||||
await maybeMigrateLegacyConfigFile(runtime);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user