UNPKG

@mesh-tech/mesh-cli

Version:

CLI for Mesh platform development utilities

165 lines (161 loc) 6.28 kB
import { execFileSync } from "child_process"; import * as fs from "fs"; import * as os from "os"; import * as path from "path"; import { logError, logInfo, logSuccess, logWarn } from "../utils/log.js"; export function shimScript() { return `#!/bin/sh # mesh — canonical launcher shim (installed by \`mesh install-shim\`). # Walks up from the current directory to the nearest workspace mesh CLI and runs # it. That resolves to bin/mesh.mjs, which runs the CLI from TypeScript source — # so this always runs the LOCAL source of whatever worktree/app you are in, with # no build step. Nothing here is version-pinned; do not edit by hand. # # Outside a workspace it hands off to a globally installed mesh CLI. # Without that hand-off the shim shadows \`npm i -g @mesh-tech/mesh-cli\` on the # PATH and \`mesh login\` is unreachable from the home directory — which is the # very first thing a new developer runs. # True when $1 is another copy of this shim. Reads the marker line with the # shell alone — no head/grep — so a stripped PATH cannot break the check. _mesh_is_shim() { _n=0 while [ "$_n" -lt 6 ] && IFS= read -r _line; do case $_line in *"canonical launcher shim"*) return 0 ;; esac _n=$((_n + 1)) done < "$1" return 1 } # Walk up to the nearest workspace CLI. Parameter expansion, not \`dirname\`: # an unavailable dirname used to leave $dir empty and spin this loop forever. dir=$(pwd) while [ -n "$dir" ]; do if [ -x "$dir/node_modules/.bin/mesh" ]; then exec "$dir/node_modules/.bin/mesh" "$@" fi [ "$dir" = "/" ] && break parent=\${dir%/*} [ -n "$parent" ] || parent=/ dir=$parent done # Not in a workspace: exec the next \`mesh\` on PATH that is not another copy of # this shim (matched on the marker line above, so we can never exec ourselves). self_dir=\${0%/*} [ "$self_dir" = "$0" ] && self_dir=. self_dir=$(CDPATH= cd -- "$self_dir" 2>/dev/null && pwd -P) saved_ifs=$IFS IFS=: set -f for entry in $PATH; do IFS=$saved_ifs set +f [ -n "$entry" ] || entry=. candidate=$entry/mesh # -f as well as -x: [ -x ] is true for a DIRECTORY named mesh, which would be # taken for the CLI and exec'd into a bare 126 without ever reaching the real # one further down PATH. A shell's own PATH search skips directories. if [ -f "$candidate" ] && [ -x "$candidate" ]; then entry_dir=$(CDPATH= cd -- "$entry" 2>/dev/null && pwd -P) if [ -n "$entry_dir" ] && [ "$entry_dir" != "$self_dir" ] && ! _mesh_is_shim "$candidate"; then exec "$candidate" "$@" fi fi IFS=: set -f done IFS=$saved_ifs set +f echo "mesh: not inside a Mesh workspace (no node_modules/.bin/mesh found)," >&2 echo " and no global mesh CLI found on PATH." >&2 echo " Install it: npm i -g @mesh-tech/mesh-cli" >&2 echo " Or cd into your app/repo (after 'pnpm install') and use 'pnpm exec mesh'." >&2 exit 1 `; } export function defaultShimDir() { return path.join(os.homedir(), ".local", "bin"); } export function dirOnPath(dir, pathEnv) { if (!pathEnv) return false; return pathEnv.split(path.delimiter).some((p) => p === dir); } export function registerInstallShimCommand(program) { program .command("install-shim") .description("Install a build-free `mesh` shim on your PATH so bare `mesh` runs the local source") .option("--dir <dir>", "Directory to install into (must be on your PATH)", defaultShimDir()) .option("-f, --force", "Overwrite an existing file at the target path", false) .action((opts) => { const dir = path.resolve(opts.dir); const target = path.join(dir, "mesh"); if (fs.existsSync(target) && !opts.force) { logWarn(`A file already exists at ${target}.`); logInfo("Re-run with --force to overwrite it, or pass --dir <other-path-on-PATH>."); return; } try { fs.mkdirSync(dir, { recursive: true }); fs.writeFileSync(target, shimScript(), { mode: 0o755 }); fs.chmodSync(target, 0o755); } catch (err) { logError(`Could not write the shim to ${target}: ${err.message}`); process.exitCode = 1; return; } logSuccess(`Installed mesh shim → ${target}`); if (!dirOnPath(dir, process.env.PATH)) { logWarn(`${dir} is not on your PATH.`); logInfo(`Add it, e.g.: echo 'export PATH="${dir}:$PATH"' >> ~/.zshrc && source ~/.zshrc`); } else { logInfo("Bare `mesh <cmd>` now runs the local source of whatever workspace you're in."); } const diagPath = path.join(os.tmpdir(), "mesh-shim-selftest.log"); let stdout = ""; let stderr = ""; let status = 0; let ok = true; try { stdout = execFileSync(target, ["--help"], { cwd: process.cwd(), encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], }); } catch (err) { ok = false; const e = err; status = typeof e.status === "number" ? e.status : 1; stdout = e.stdout?.toString?.() ?? ""; stderr = e.stderr?.toString?.() ?? String(e.message ?? err); } const diag = [ `mesh install-shim self-test — ${new Date().toISOString()}`, `cwd: ${process.cwd()}`, `shim: ${target}`, `node: ${process.execPath} (${process.version})`, `PATH: ${process.env.PATH ?? ""}`, `command: mesh --help`, `exit: ${status}`, `--- stdout (${stdout.length} bytes) ---`, stdout, `--- stderr (${stderr.length} bytes) ---`, stderr, "", ].join("\n"); try { fs.writeFileSync(diagPath, diag); } catch { } if (ok && /Commands:|Usage:/.test(stdout)) { logSuccess(`Self-test passed: \`mesh --help\` ran cleanly. Diagnostic: ${diagPath}`); } else { logError(`Self-test FAILED (exit ${status}). Full diagnostic written to:`); logError(` ${diagPath}`); process.exitCode = 1; } }); }