inspector/scripts/install-clients.mjs
Bob Dickinson 2488ca5e38
feat: port CLI, TUI, and launcher from v1.5 into v2 (#1452)
Ports the CLI, TUI, and launcher from v1.5 into v2 behind a single
`mcp-inspector` entrypoint (launcher → web/cli/tui). Includes the
`mcp-inspector --web --dev` launcher fix (Vite alias resolution + prod-server
double-start), the root `postinstall` install cascade with simplified CI, and
spec/AGENTS docs (no-workspaces rationale, Development setup).

Closes #1246.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 20:27:49 -04:00

65 lines
2.4 KiB
JavaScript

#!/usr/bin/env node
/**
* Root postinstall: install dependencies for each client package.
*
* v2 deliberately does not use npm workspaces (see the rationale in
* specification/v2_cli_tui_launcher.md), so each client under clients/* keeps
* its own package.json and node_modules. Without this cascade a from-source
* checkout would have to run `npm install` in every client directory by hand —
* the install-friction gap. Running it from the root `postinstall` makes a
* single `npm install` at the repo root populate every client.
*
* Safe outside a source checkout:
* - When this package is installed as a dependency it lives under a
* node_modules directory; we detect that and exit early.
* - The published tarball ships only each client's build/ output (see the
* root package.json `files` array), not their package.json, so even if the
* node_modules check were bypassed the per-client guard finds nothing to
* install and the script no-ops.
*
* Set INSPECTOR_SKIP_CLIENT_INSTALL=1 to skip explicitly — e.g. CI that
* installs each client itself and wants to avoid the redundant pass.
*/
import { spawnSync } from "node:child_process";
import { existsSync } from "node:fs";
import { dirname, join, resolve, sep } from "node:path";
import { fileURLToPath } from "node:url";
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const CLIENTS = ["web", "cli", "tui", "launcher"];
if (process.env.INSPECTOR_SKIP_CLIENT_INSTALL) {
console.log(
"[install-clients] INSPECTOR_SKIP_CLIENT_INSTALL set — skipping client installs.",
);
process.exit(0);
}
// Installed as a dependency (lives under node_modules) → nothing to do.
if (repoRoot.split(sep).includes("node_modules")) {
process.exit(0);
}
for (const name of CLIENTS) {
const dir = join(repoRoot, "clients", name);
// The published tarball ships build/ output only, no client package.json.
if (!existsSync(join(dir, "package.json"))) {
continue;
}
console.log(
`[install-clients] Installing dependencies for clients/${name}...`,
);
const result = spawnSync("npm", ["install"], {
cwd: dir,
stdio: "inherit",
// npm is npm.cmd on Windows, which needs a shell to resolve.
shell: process.platform === "win32",
});
if (result.status !== 0) {
console.error(
`[install-clients] npm install failed in clients/${name} (exit ${result.status ?? "unknown"}).`,
);
process.exit(result.status ?? 1);
}
}