@montytools/cli 0.2.1 → 0.2.3
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/bin/monty.mjs +65 -8
- package/package.json +1 -1
package/bin/monty.mjs
CHANGED
|
@@ -376,6 +376,7 @@ function installDeps() {
|
|
|
376
376
|
const pm = spawnSync("pnpm", ["--version"], { stdio: "ignore" }).status === 0 ? "pnpm" : "npm";
|
|
377
377
|
run(appDir, "install", [pm, "install"],
|
|
378
378
|
"Dependency install failed. Read the package manager error above; usually network or a bad package.json edit.");
|
|
379
|
+
ensureSdk(appDir);
|
|
379
380
|
}
|
|
380
381
|
|
|
381
382
|
function buildApp() {
|
|
@@ -420,6 +421,45 @@ async function freePort(start) {
|
|
|
420
421
|
fail("NO_FREE_PORT", `No free port between ${start} and ${start + 49}. Pass --port <n>.`);
|
|
421
422
|
}
|
|
422
423
|
|
|
424
|
+
|
|
425
|
+
// Apps pin @montytools/sdk at scaffold time and go stale — the CLI knows the
|
|
426
|
+
// minimum SDK its workflows need (e.g. tunnel-host allowlisting lives in the
|
|
427
|
+
// SDK's vite plugin) and upgrades the app automatically before dev/deploy.
|
|
428
|
+
const MIN_SDK = "0.1.1";
|
|
429
|
+
|
|
430
|
+
function installedSdkVersion(appDir) {
|
|
431
|
+
try {
|
|
432
|
+
return JSON.parse(
|
|
433
|
+
readFileSync(join(appDir, "node_modules", "@montytools", "sdk", "package.json"), "utf8"),
|
|
434
|
+
).version;
|
|
435
|
+
} catch {
|
|
436
|
+
return null;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function semverLt(a, b) {
|
|
441
|
+
const pa = a.split(".").map(Number);
|
|
442
|
+
const pb = b.split(".").map(Number);
|
|
443
|
+
for (let i = 0; i < 3; i++) {
|
|
444
|
+
if ((pa[i] || 0) < (pb[i] || 0)) return true;
|
|
445
|
+
if ((pa[i] || 0) > (pb[i] || 0)) return false;
|
|
446
|
+
}
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function ensureSdk(appDir) {
|
|
451
|
+
const v = installedSdkVersion(appDir);
|
|
452
|
+
if (v && !semverLt(v, MIN_SDK)) return;
|
|
453
|
+
console.log(
|
|
454
|
+
v
|
|
455
|
+
? `sdk: installed ${v} < required ${MIN_SDK} — updating @montytools/sdk`
|
|
456
|
+
: "sdk: @montytools/sdk missing — installing",
|
|
457
|
+
);
|
|
458
|
+
const pm = spawnSync("pnpm", ["--version"], { stdio: "ignore" }).status === 0 ? "pnpm" : "npm";
|
|
459
|
+
run(appDir, "sdk-update", [pm, "install", "@montytools/sdk@latest"],
|
|
460
|
+
"Could not update @montytools/sdk. Run the install manually in the app folder, then retry.");
|
|
461
|
+
}
|
|
462
|
+
|
|
423
463
|
// ── monty dev ──────────────────────────────────────────────────────────────
|
|
424
464
|
// Development mode: vite locally + a Cloudflare quick tunnel registered as
|
|
425
465
|
// the app's DEV channel, so workspace admins see the app live (HMR included)
|
|
@@ -428,6 +468,7 @@ async function freePort(start) {
|
|
|
428
468
|
// clicks Publish in the workspace, this process builds + uploads for real.
|
|
429
469
|
async function dev() {
|
|
430
470
|
const appDir = requireAppDir("dev");
|
|
471
|
+
ensureSdk(appDir);
|
|
431
472
|
const meta = await compileConfig(appDir);
|
|
432
473
|
const cfg = loadConfig();
|
|
433
474
|
const host = cfg?.host ?? DEFAULT_HOST;
|
|
@@ -555,16 +596,31 @@ async function dev() {
|
|
|
555
596
|
// the origin before it resolves would make admins' browsers cache NXDOMAIN
|
|
556
597
|
// (macOS negative cache ≈ 30 min of a broken iframe) — so gate on DNS via
|
|
557
598
|
// DoH, which never touches the local resolver.
|
|
599
|
+
async function resolvesVia(dohBase, hostname) {
|
|
600
|
+
try {
|
|
601
|
+
const r = await fetch(`${dohBase}?name=${hostname}&type=A`, {
|
|
602
|
+
headers: { accept: "application/dns-json" },
|
|
603
|
+
});
|
|
604
|
+
const d = await r.json();
|
|
605
|
+
return Array.isArray(d.Answer) && d.Answer.some((a) => a.type === 1);
|
|
606
|
+
} catch {
|
|
607
|
+
return false;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
558
611
|
async function waitForDns(hostname) {
|
|
612
|
+
// Confirm on TWO independent public resolvers, then hold a grace period so
|
|
613
|
+
// slower home/ISP resolver chains catch up — an admin's browser asking one
|
|
614
|
+
// beat too early caches NXDOMAIN for a long time (macOS ≈ 30 min).
|
|
559
615
|
for (let i = 0; i < 36; i++) {
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
}
|
|
616
|
+
const [cf, goog] = await Promise.all([
|
|
617
|
+
resolvesVia("https://cloudflare-dns.com/dns-query", hostname),
|
|
618
|
+
resolvesVia("https://dns.google/resolve", hostname),
|
|
619
|
+
]);
|
|
620
|
+
if (cf && goog) {
|
|
621
|
+
await new Promise((res) => setTimeout(res, 10_000));
|
|
622
|
+
return true;
|
|
623
|
+
}
|
|
568
624
|
await new Promise((res) => setTimeout(res, 5000));
|
|
569
625
|
}
|
|
570
626
|
return false;
|
|
@@ -711,6 +767,7 @@ async function docs() {
|
|
|
711
767
|
// ── monty deploy ───────────────────────────────────────────────────────────
|
|
712
768
|
async function deploy() {
|
|
713
769
|
const appDir = requireAppDir("deploy");
|
|
770
|
+
ensureSdk(appDir);
|
|
714
771
|
if (!rest.includes("--from-dev")) {
|
|
715
772
|
console.log("note: direct deploy skips workspace review — the usual flow is `monty dev` + the Publish button in the workspace.");
|
|
716
773
|
}
|