@yawlabs/mcph 0.43.0 → 0.45.0
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/CHANGELOG.md +8 -0
- package/README.md +9 -0
- package/dist/index.js +233 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@yawlabs/mcph` are documented here. This project uses [semantic versioning](https://semver.org) and a CI-gated release flow: pushing a `vX.Y.Z` tag triggers `.github/workflows/release.yml`, which publishes to npm.
|
|
4
4
|
|
|
5
|
+
## 0.45.0 — 2026-04-18
|
|
6
|
+
|
|
7
|
+
- **Clearer 401/403 errors with token fingerprint + actionable fix link** — When the backend rejects a token (`HTTP 401` revoked/malformed, `HTTP 403` accepted but scope-denied), `fetchConfig` now throws an error that names the offending token by its fingerprint (e.g., `mcp_pat_…abcd`), explains what state the token is in, and points directly at the tokens page with a concrete re-install command. Prior wording was "Invalid MCPH_TOKEN — check your token at mcp.hosting" and "Access denied — your token may have expired" — both too vague to action without pinging support. New wording is structured as three lines: cause, fix URL, and the `mcph install … --token mcp_pat_...` re-install command. Messages surface verbatim through `mcph servers`, the top-level `mcph` runtime, and anywhere else `fetchConfig` is awaited, so every user-facing rejection reads the same way.
|
|
8
|
+
|
|
9
|
+
## 0.44.0 — 2026-04-18
|
|
10
|
+
|
|
11
|
+
- **`mcph install --list` + `mcph install --all`** — Two new modes on the install subcommand. `--list` is read-only: it enumerates every client/scope combo for the current OS and shows whether an `mcp.hosting` entry is already wired up, plus a path-per-row and a one-line summary (`N/M client scopes have mcp.hosting configured on linux`). No token, no network, no writes — just a diagnostic view that mirrors the `doctor` CLIENTS section but without the rest of doctor's noise. `--all` walks `INSTALL_TARGETS`, picks the default scope per client (user where supported, else the first non-project-dir scope, else skipped unless `--project-dir` is passed), and calls `runInstall` in a loop — so `--dry-run`, `--force`, `--skip`, and `--token` all propagate as expected. Status is aggregated into a single summary line, and the process exit code is non-zero if any sub-install failed so CI can still gate on one-shot onboarding. Works around the main drop-off during setup ("which client am I supposed to pick?") by offering both the answer (`--list`) and the sledgehammer (`--all`) from the same subcommand.
|
|
12
|
+
|
|
5
13
|
## 0.43.0 — 2026-04-18
|
|
6
14
|
|
|
7
15
|
- **`mcph servers <namespace-filter>` — positional filter** — Passing a bare positional argument now filters the listing to servers whose namespace contains that substring (case-insensitive): `mcph servers git` matches both `github` and `gitlab`. Applies to both the text table and the `--json` output so the two surfaces agree. Summary line reflects the filtered count, and a filter that matches nothing prints an explanatory "No servers match …" instead of an empty table (which previously looked like an empty account).
|
package/README.md
CHANGED
|
@@ -65,6 +65,15 @@ Helpful flags:
|
|
|
65
65
|
- `--force` / `--skip` — overwrite or leave an existing `mcp.hosting` entry. Without either, mcph prompts (TTY) or refuses (non-TTY).
|
|
66
66
|
- `--no-mcph-config` — write only the client config; leave `~/.mcph/config.json` untouched.
|
|
67
67
|
|
|
68
|
+
Or install into every detected client at once:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
mcph install --list # read-only: detect clients + show install state per scope
|
|
72
|
+
mcph install --all --token mcp_pat_… # one-shot: install into every user-scope client on this machine
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`--list` never writes (no token needed). `--all` installs into every client whose user-scope target is resolvable on this OS — Claude Desktop is skipped on Linux, VS Code is skipped unless `--project-dir` is given (it's workspace-only). Aggregate exit code is non-zero if any sub-install fails.
|
|
76
|
+
|
|
68
77
|
Or [edit the JSON by hand](#manual-install) if you'd rather.
|
|
69
78
|
|
|
70
79
|
### Diagnose problems — `mcph doctor`
|
package/dist/index.js
CHANGED
|
@@ -464,12 +464,22 @@ async function fetchConfig(apiUrl6, token6, currentVersion) {
|
|
|
464
464
|
if (res.statusCode === 401) {
|
|
465
465
|
await res.body.text().catch(() => {
|
|
466
466
|
});
|
|
467
|
-
throw new ConfigError(
|
|
467
|
+
throw new ConfigError(
|
|
468
|
+
`Token rejected (HTTP 401) \u2014 the token ${tokenFingerprint(token6)} is invalid or revoked.
|
|
469
|
+
Generate a new token at https://mcp.hosting/dashboard/settings/tokens,
|
|
470
|
+
then re-run \`mcph install <client> --token mcp_pat_...\` or set MCPH_TOKEN.`,
|
|
471
|
+
true
|
|
472
|
+
);
|
|
468
473
|
}
|
|
469
474
|
if (res.statusCode === 403) {
|
|
470
475
|
await res.body.text().catch(() => {
|
|
471
476
|
});
|
|
472
|
-
throw new ConfigError(
|
|
477
|
+
throw new ConfigError(
|
|
478
|
+
`Access denied (HTTP 403) \u2014 the token ${tokenFingerprint(token6)} was accepted but lacks permission to read this account's servers.
|
|
479
|
+
The account may be suspended or the token scope reduced \u2014 check
|
|
480
|
+
https://mcp.hosting/dashboard/settings/tokens, or reach support@mcp.hosting.`,
|
|
481
|
+
true
|
|
482
|
+
);
|
|
473
483
|
}
|
|
474
484
|
if (res.statusCode !== 200) {
|
|
475
485
|
const body = await res.body.text().catch(() => "");
|
|
@@ -663,7 +673,18 @@ var SUBCOMMAND_SPEC = [
|
|
|
663
673
|
{
|
|
664
674
|
name: "install",
|
|
665
675
|
positional: [...INSTALL_CLIENTS],
|
|
666
|
-
flags: [
|
|
676
|
+
flags: [
|
|
677
|
+
"--scope",
|
|
678
|
+
"--token",
|
|
679
|
+
"--project-dir",
|
|
680
|
+
"--os",
|
|
681
|
+
"--force",
|
|
682
|
+
"--skip",
|
|
683
|
+
"--dry-run",
|
|
684
|
+
"--no-mcph-config",
|
|
685
|
+
"--list",
|
|
686
|
+
"--all"
|
|
687
|
+
]
|
|
667
688
|
},
|
|
668
689
|
{ name: "doctor", flags: ["--json", "--help"] },
|
|
669
690
|
{ name: "servers", flags: ["--json", "--help"] },
|
|
@@ -1435,7 +1456,7 @@ function selectFlakyNamespaces(entries, limit) {
|
|
|
1435
1456
|
}
|
|
1436
1457
|
|
|
1437
1458
|
// src/doctor-cmd.ts
|
|
1438
|
-
var VERSION = true ? "0.
|
|
1459
|
+
var VERSION = true ? "0.45.0" : "dev";
|
|
1439
1460
|
async function runDoctor(opts = {}) {
|
|
1440
1461
|
if (opts.json) return runDoctorJson(opts);
|
|
1441
1462
|
const lines = [];
|
|
@@ -1769,6 +1790,62 @@ function walkContainer(root, path4) {
|
|
|
1769
1790
|
if (typeof cur !== "object" || cur === null || Array.isArray(cur)) return null;
|
|
1770
1791
|
return cur;
|
|
1771
1792
|
}
|
|
1793
|
+
async function probeClientsAsync(opts) {
|
|
1794
|
+
const result = [];
|
|
1795
|
+
for (const target of INSTALL_TARGETS) {
|
|
1796
|
+
const unavailable = !target.availableOn.includes(opts.os);
|
|
1797
|
+
if (unavailable) {
|
|
1798
|
+
result.push({
|
|
1799
|
+
clientId: target.clientId,
|
|
1800
|
+
scope: target.scopes[0].scope,
|
|
1801
|
+
path: "(n/a)",
|
|
1802
|
+
exists: false,
|
|
1803
|
+
hasMcphEntry: false,
|
|
1804
|
+
malformed: false,
|
|
1805
|
+
unavailable: true
|
|
1806
|
+
});
|
|
1807
|
+
continue;
|
|
1808
|
+
}
|
|
1809
|
+
for (const scope of target.scopes) {
|
|
1810
|
+
const resolved = resolveInstallPath({
|
|
1811
|
+
clientId: target.clientId,
|
|
1812
|
+
scope: scope.scope,
|
|
1813
|
+
os: opts.os,
|
|
1814
|
+
home: opts.home,
|
|
1815
|
+
projectDir: scope.requiresProjectDir ? opts.cwd : void 0
|
|
1816
|
+
});
|
|
1817
|
+
const exists3 = existsSync(resolved.absolute);
|
|
1818
|
+
let hasMcphEntry = false;
|
|
1819
|
+
let malformed = false;
|
|
1820
|
+
if (exists3) {
|
|
1821
|
+
try {
|
|
1822
|
+
const raw = await readFile3(resolved.absolute, "utf8");
|
|
1823
|
+
if (raw.trim().length > 0) {
|
|
1824
|
+
const parsed = parseJsonc(raw);
|
|
1825
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
1826
|
+
const container = walkContainer(parsed, resolved.containerPath);
|
|
1827
|
+
if (container) hasMcphEntry = ENTRY_NAME in container;
|
|
1828
|
+
} else {
|
|
1829
|
+
malformed = true;
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
} catch {
|
|
1833
|
+
malformed = true;
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
result.push({
|
|
1837
|
+
clientId: target.clientId,
|
|
1838
|
+
scope: scope.scope,
|
|
1839
|
+
path: resolved.absolute,
|
|
1840
|
+
exists: exists3,
|
|
1841
|
+
hasMcphEntry,
|
|
1842
|
+
malformed,
|
|
1843
|
+
unavailable: false
|
|
1844
|
+
});
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
return result;
|
|
1848
|
+
}
|
|
1772
1849
|
async function fetchLatestVersion(override) {
|
|
1773
1850
|
if (override) {
|
|
1774
1851
|
try {
|
|
@@ -1950,7 +2027,7 @@ import { homedir as homedir5 } from "os";
|
|
|
1950
2027
|
import { dirname as dirname2 } from "path";
|
|
1951
2028
|
import { join as join5, resolve as resolve2 } from "path";
|
|
1952
2029
|
import { createInterface } from "readline/promises";
|
|
1953
|
-
var USAGE = "Usage: mcph install <claude-code|claude-desktop|cursor|vscode> [--scope user|project|local]\n [--token <mcp_pat_\u2026>] [--project-dir <path>] [--os macos|linux|windows]\n [--force | --skip] [--dry-run] [--no-mcph-config]";
|
|
2030
|
+
var USAGE = "Usage: mcph install <claude-code|claude-desktop|cursor|vscode> [--scope user|project|local]\n [--token <mcp_pat_\u2026>] [--project-dir <path>] [--os macos|linux|windows]\n [--force | --skip] [--dry-run] [--no-mcph-config]\n mcph install --list (detect clients; no writes)\n mcph install --all [--token <mcp_pat_\u2026>] (install into every detected client)";
|
|
1954
2031
|
async function runInstall(opts) {
|
|
1955
2032
|
const stdout = opts.io?.stdout ?? process.stdout;
|
|
1956
2033
|
const stderr = opts.io?.stderr ?? process.stderr;
|
|
@@ -1965,10 +2042,21 @@ async function runInstall(opts) {
|
|
|
1965
2042
|
stderr.write(`${s}
|
|
1966
2043
|
`);
|
|
1967
2044
|
};
|
|
2045
|
+
if (opts.listOnly && opts.all) {
|
|
2046
|
+
err("mcph install: --list and --all are mutually exclusive");
|
|
2047
|
+
return { written: [], wouldWrite: [], messages, exitCode: 2 };
|
|
2048
|
+
}
|
|
2049
|
+
if (opts.listOnly) return runInstallList(opts, log2);
|
|
2050
|
+
if (opts.all) return runInstallAll(opts, log2, err);
|
|
1968
2051
|
if (opts.force && opts.skip) {
|
|
1969
2052
|
err("mcph install: --force and --skip are mutually exclusive");
|
|
1970
2053
|
return { written: [], wouldWrite: [], messages, exitCode: 2 };
|
|
1971
2054
|
}
|
|
2055
|
+
if (!opts.clientId) {
|
|
2056
|
+
err(`mcph install: client argument required
|
|
2057
|
+
${USAGE}`);
|
|
2058
|
+
return { written: [], wouldWrite: [], messages, exitCode: 2 };
|
|
2059
|
+
}
|
|
1972
2060
|
const target = INSTALL_TARGETS.find((t) => t.clientId === opts.clientId);
|
|
1973
2061
|
if (!target) {
|
|
1974
2062
|
err(`mcph install: unknown client ${opts.clientId}
|
|
@@ -2294,6 +2382,12 @@ function parseInstallArgs(argv) {
|
|
|
2294
2382
|
case "--no-mcph-config":
|
|
2295
2383
|
opts.skipMcphConfig = true;
|
|
2296
2384
|
break;
|
|
2385
|
+
case "--list":
|
|
2386
|
+
opts.listOnly = true;
|
|
2387
|
+
break;
|
|
2388
|
+
case "--all":
|
|
2389
|
+
opts.all = true;
|
|
2390
|
+
break;
|
|
2297
2391
|
case "-h":
|
|
2298
2392
|
case "--help":
|
|
2299
2393
|
return { ok: false, error: USAGE };
|
|
@@ -2303,6 +2397,16 @@ ${USAGE}` };
|
|
|
2303
2397
|
positional.push(a);
|
|
2304
2398
|
}
|
|
2305
2399
|
}
|
|
2400
|
+
if (opts.listOnly || opts.all) {
|
|
2401
|
+
if (positional.length > 0) {
|
|
2402
|
+
return {
|
|
2403
|
+
ok: false,
|
|
2404
|
+
error: `mcph install: ${opts.listOnly ? "--list" : "--all"} does not take a client argument.
|
|
2405
|
+
${USAGE}`
|
|
2406
|
+
};
|
|
2407
|
+
}
|
|
2408
|
+
return { ok: true, options: opts };
|
|
2409
|
+
}
|
|
2306
2410
|
if (positional.length !== 1)
|
|
2307
2411
|
return { ok: false, error: `Expected exactly one client argument, got ${positional.length}.
|
|
2308
2412
|
${USAGE}` };
|
|
@@ -2316,6 +2420,127 @@ ${USAGE}` };
|
|
|
2316
2420
|
opts.clientId = clientId;
|
|
2317
2421
|
return { ok: true, options: opts };
|
|
2318
2422
|
}
|
|
2423
|
+
async function runInstallList(opts, log2) {
|
|
2424
|
+
const home = opts.home ?? homedir5();
|
|
2425
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
2426
|
+
const os = opts.os ?? CURRENT_OS;
|
|
2427
|
+
const probes = await probeClientsAsync({ home, os, cwd });
|
|
2428
|
+
const rows = probes.map((p) => ({
|
|
2429
|
+
client: INSTALL_TARGETS.find((t) => t.clientId === p.clientId)?.label ?? p.clientId,
|
|
2430
|
+
scope: p.scope,
|
|
2431
|
+
path: displayPath(p.path, home),
|
|
2432
|
+
status: statusFor(p)
|
|
2433
|
+
}));
|
|
2434
|
+
const installed = probes.filter((p) => p.hasMcphEntry).length;
|
|
2435
|
+
const available = probes.filter((p) => !p.unavailable).length;
|
|
2436
|
+
log2(`${installed}/${available} client scopes have mcp.hosting configured on ${os}.`);
|
|
2437
|
+
log2("");
|
|
2438
|
+
const widths = {
|
|
2439
|
+
client: Math.max("CLIENT".length, ...rows.map((r) => r.client.length)),
|
|
2440
|
+
scope: Math.max("SCOPE".length, ...rows.map((r) => r.scope.length)),
|
|
2441
|
+
path: Math.max("PATH".length, ...rows.map((r) => r.path.length)),
|
|
2442
|
+
status: Math.max("STATUS".length, ...rows.map((r) => r.status.length))
|
|
2443
|
+
};
|
|
2444
|
+
const header = ` ${"CLIENT".padEnd(widths.client)} ${"SCOPE".padEnd(widths.scope)} ${"PATH".padEnd(widths.path)} ${"STATUS".padEnd(widths.status)}`;
|
|
2445
|
+
log2(header);
|
|
2446
|
+
for (const r of rows) {
|
|
2447
|
+
log2(
|
|
2448
|
+
` ${r.client.padEnd(widths.client)} ${r.scope.padEnd(widths.scope)} ${r.path.padEnd(widths.path)} ${r.status.padEnd(widths.status)}`
|
|
2449
|
+
);
|
|
2450
|
+
}
|
|
2451
|
+
log2("");
|
|
2452
|
+
log2("Install into a specific client: `mcph install <client> [--scope user|project|local]`");
|
|
2453
|
+
log2("Install into every available user-scope client: `mcph install --all`");
|
|
2454
|
+
return { written: [], wouldWrite: [], messages: [], exitCode: 0 };
|
|
2455
|
+
}
|
|
2456
|
+
function statusFor(p) {
|
|
2457
|
+
if (p.unavailable) return "unavailable";
|
|
2458
|
+
if (p.malformed) return "malformed";
|
|
2459
|
+
if (p.hasMcphEntry) return "installed";
|
|
2460
|
+
if (p.exists) return "other-entries";
|
|
2461
|
+
return "not installed";
|
|
2462
|
+
}
|
|
2463
|
+
function displayPath(abs, home) {
|
|
2464
|
+
if (abs === "(n/a)") return abs;
|
|
2465
|
+
if (home && abs.startsWith(home)) {
|
|
2466
|
+
const tail = abs.slice(home.length).replace(/^[\\/]/, "");
|
|
2467
|
+
return `~${process.platform === "win32" ? "\\" : "/"}${tail}`;
|
|
2468
|
+
}
|
|
2469
|
+
return abs;
|
|
2470
|
+
}
|
|
2471
|
+
async function runInstallAll(opts, log2, err) {
|
|
2472
|
+
const os = opts.os ?? CURRENT_OS;
|
|
2473
|
+
const targets = INSTALL_TARGETS.filter((t) => t.availableOn.includes(os));
|
|
2474
|
+
if (targets.length === 0) {
|
|
2475
|
+
err(`mcph install --all: no installable clients on ${os}.`);
|
|
2476
|
+
return { written: [], wouldWrite: [], messages: [], exitCode: 1 };
|
|
2477
|
+
}
|
|
2478
|
+
const plans = [];
|
|
2479
|
+
const skipped = [];
|
|
2480
|
+
for (const t of targets) {
|
|
2481
|
+
const userScope = t.scopes.find((s) => s.scope === "user");
|
|
2482
|
+
if (userScope) {
|
|
2483
|
+
plans.push({ clientId: t.clientId, scope: "user" });
|
|
2484
|
+
continue;
|
|
2485
|
+
}
|
|
2486
|
+
const firstNoProj = t.scopes.find((s) => !s.requiresProjectDir);
|
|
2487
|
+
if (firstNoProj) {
|
|
2488
|
+
plans.push({ clientId: t.clientId, scope: firstNoProj.scope });
|
|
2489
|
+
continue;
|
|
2490
|
+
}
|
|
2491
|
+
if (opts.projectDir) {
|
|
2492
|
+
plans.push({ clientId: t.clientId, scope: t.scopes[0].scope });
|
|
2493
|
+
continue;
|
|
2494
|
+
}
|
|
2495
|
+
skipped.push({
|
|
2496
|
+
clientId: t.clientId,
|
|
2497
|
+
reason: `requires --project-dir (scopes: ${t.scopes.map((s) => s.scope).join(", ")})`
|
|
2498
|
+
});
|
|
2499
|
+
}
|
|
2500
|
+
log2(`Installing into ${plans.length} client${plans.length === 1 ? "" : "s"}\u2026`);
|
|
2501
|
+
if (skipped.length > 0) {
|
|
2502
|
+
for (const s of skipped) log2(` skip ${s.clientId}: ${s.reason}`);
|
|
2503
|
+
}
|
|
2504
|
+
log2("");
|
|
2505
|
+
const aggregateWritten = [];
|
|
2506
|
+
const aggregateWouldWrite = [];
|
|
2507
|
+
const aggregateMessages = [];
|
|
2508
|
+
let failed = 0;
|
|
2509
|
+
let succeeded = 0;
|
|
2510
|
+
for (const plan of plans) {
|
|
2511
|
+
log2(`\u2500\u2500 ${plan.clientId} (${plan.scope}) \u2500\u2500`);
|
|
2512
|
+
const result = await runInstall({
|
|
2513
|
+
...opts,
|
|
2514
|
+
listOnly: false,
|
|
2515
|
+
all: false,
|
|
2516
|
+
clientId: plan.clientId,
|
|
2517
|
+
scope: plan.scope
|
|
2518
|
+
});
|
|
2519
|
+
aggregateWritten.push(...result.written);
|
|
2520
|
+
aggregateWouldWrite.push(...result.wouldWrite);
|
|
2521
|
+
aggregateMessages.push(...result.messages);
|
|
2522
|
+
if (result.exitCode === 0) succeeded += 1;
|
|
2523
|
+
else failed += 1;
|
|
2524
|
+
log2("");
|
|
2525
|
+
}
|
|
2526
|
+
const totalPlanned = plans.length;
|
|
2527
|
+
if (failed === 0) {
|
|
2528
|
+
log2(`\u2713 ${succeeded}/${totalPlanned} clients installed successfully.`);
|
|
2529
|
+
return {
|
|
2530
|
+
written: aggregateWritten,
|
|
2531
|
+
wouldWrite: aggregateWouldWrite,
|
|
2532
|
+
messages: aggregateMessages,
|
|
2533
|
+
exitCode: 0
|
|
2534
|
+
};
|
|
2535
|
+
}
|
|
2536
|
+
err(`${failed}/${totalPlanned} client install${failed === 1 ? "" : "s"} failed. ${succeeded} succeeded.`);
|
|
2537
|
+
return {
|
|
2538
|
+
written: aggregateWritten,
|
|
2539
|
+
wouldWrite: aggregateWouldWrite,
|
|
2540
|
+
messages: aggregateMessages,
|
|
2541
|
+
exitCode: 1
|
|
2542
|
+
};
|
|
2543
|
+
}
|
|
2319
2544
|
var INSTALL_USAGE = USAGE;
|
|
2320
2545
|
|
|
2321
2546
|
// src/reset-learning-cmd.ts
|
|
@@ -4519,7 +4744,7 @@ function categorizeSpawnError(err) {
|
|
|
4519
4744
|
}
|
|
4520
4745
|
async function connectToUpstream(config, onDisconnect, onListChanged) {
|
|
4521
4746
|
const client = new Client(
|
|
4522
|
-
{ name: "mcph", version: true ? "0.
|
|
4747
|
+
{ name: "mcph", version: true ? "0.45.0" : "dev" },
|
|
4523
4748
|
{ capabilities: {} }
|
|
4524
4749
|
);
|
|
4525
4750
|
let transport;
|
|
@@ -5000,7 +5225,7 @@ var ConnectServer = class _ConnectServer {
|
|
|
5000
5225
|
this.apiUrl = apiUrl6;
|
|
5001
5226
|
this.token = token6;
|
|
5002
5227
|
this.server = new Server(
|
|
5003
|
-
{ name: "mcph", version: true ? "0.
|
|
5228
|
+
{ name: "mcph", version: true ? "0.45.0" : "dev" },
|
|
5004
5229
|
{
|
|
5005
5230
|
capabilities: {
|
|
5006
5231
|
tools: { listChanged: true },
|
|
@@ -7276,7 +7501,7 @@ ${installBlock}
|
|
|
7276
7501
|
);
|
|
7277
7502
|
process.exit(0);
|
|
7278
7503
|
} else if (subcommand === "--version" || subcommand === "-V") {
|
|
7279
|
-
process.stdout.write(`mcph ${true ? "0.
|
|
7504
|
+
process.stdout.write(`mcph ${true ? "0.45.0" : "dev"}
|
|
7280
7505
|
`);
|
|
7281
7506
|
process.exit(0);
|
|
7282
7507
|
} else if (subcommand && !subcommand.startsWith("-")) {
|
package/package.json
CHANGED