@m-kopa/launchpad-cli 0.27.4 → 0.27.5
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 +18 -0
- package/dist/cli.js +322 -122
- package/dist/commands/deploy-flags.d.ts.map +1 -1
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/deploy/served-verify.d.ts +31 -0
- package/dist/deploy/served-verify.d.ts.map +1 -0
- package/dist/deploy/verify-deploy.d.ts +25 -0
- package/dist/deploy/verify-deploy.d.ts.map +1 -0
- package/dist/deploy/wait-for-deployment.d.ts +37 -0
- package/dist/deploy/wait-for-deployment.d.ts.map +1 -0
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
- package/skills/launchpad-content-pr/SKILL.md +1 -1
- package/skills/launchpad-deploy/SKILL.md +1 -1
- package/skills/launchpad-deploy-status/SKILL.md +1 -1
- package/skills/launchpad-destroy/SKILL.md +1 -1
- package/skills/launchpad-onboard/SKILL.md +1 -1
- package/skills/launchpad-status/SKILL.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,24 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
6
6
|
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html);
|
|
7
7
|
pre-1.0 minor bumps may carry breaking changes per ADR 0005.
|
|
8
8
|
|
|
9
|
+
## 0.27.5 — 2026-06-16
|
|
10
|
+
|
|
11
|
+
Post-deploy verification — `launchpad deploy` confirms the live deployment
|
|
12
|
+
serves what you shipped (sp-dplvfy).
|
|
13
|
+
|
|
14
|
+
- After a content deploy commits, the CLI now **waits for the Cloudflare Pages
|
|
15
|
+
build to settle and then checks the served deployment matches your shipped
|
|
16
|
+
`index.html`** (by hash). A mismatch — the silent-revert class where the
|
|
17
|
+
build reads the entrypoint from a different path than your files land at —
|
|
18
|
+
fails the deploy with exit `65` and a build-config diagnosis, instead of a
|
|
19
|
+
misleading "success". A failed build exits `1` (with a log excerpt); a build
|
|
20
|
+
still pending within the timeout is a soft, non-failing outcome.
|
|
21
|
+
- Static apps get a byte/hash compare; react/react+api get a reachability check.
|
|
22
|
+
Content is compared by hash + length only — never echoed back.
|
|
23
|
+
- New `--no-verify` (skip the wait/check) and `--verify-timeout=<seconds>`
|
|
24
|
+
(default 180) flags. A status-read hiccup degrades to "couldn't verify"
|
|
25
|
+
rather than failing a committed deploy.
|
|
26
|
+
|
|
9
27
|
## 0.27.4 — 2026-06-16
|
|
10
28
|
|
|
11
29
|
Self-serve developer-access grants (sp-grnted, Bundle 1).
|
package/dist/cli.js
CHANGED
|
@@ -19,7 +19,7 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
19
19
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
20
20
|
|
|
21
21
|
// src/version.ts
|
|
22
|
-
var CLI_VERSION = "0.27.
|
|
22
|
+
var CLI_VERSION = "0.27.5";
|
|
23
23
|
|
|
24
24
|
// src/config.ts
|
|
25
25
|
import * as os from "node:os";
|
|
@@ -1525,7 +1525,7 @@ function describe10(e) {
|
|
|
1525
1525
|
}
|
|
1526
1526
|
|
|
1527
1527
|
// src/commands/deploy.ts
|
|
1528
|
-
import { existsSync as
|
|
1528
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
1529
1529
|
import * as path8 from "node:path";
|
|
1530
1530
|
|
|
1531
1531
|
// src/bundle/orchestrate.ts
|
|
@@ -3616,9 +3616,212 @@ async function bundleAndDeploy(args) {
|
|
|
3616
3616
|
};
|
|
3617
3617
|
}
|
|
3618
3618
|
|
|
3619
|
+
// src/deploy/verify-deploy.ts
|
|
3620
|
+
import { readFileSync as readFileSync5, existsSync as existsSync3 } from "node:fs";
|
|
3621
|
+
import { join as join8 } from "node:path";
|
|
3622
|
+
|
|
3623
|
+
// src/deploy/deployment-status.ts
|
|
3624
|
+
async function fetchStandingExceptions(cfg, slug, fetcher = fetch) {
|
|
3625
|
+
let raw;
|
|
3626
|
+
try {
|
|
3627
|
+
raw = await apiJson(cfg, { path: `/apps/${encodeURIComponent(slug)}/exceptions` }, fetcher);
|
|
3628
|
+
} catch (e) {
|
|
3629
|
+
if (e instanceof NotFoundError)
|
|
3630
|
+
return null;
|
|
3631
|
+
throw e;
|
|
3632
|
+
}
|
|
3633
|
+
if (!Array.isArray(raw.exceptions))
|
|
3634
|
+
return null;
|
|
3635
|
+
return raw.exceptions.filter((e) => typeof e === "object" && e !== null && typeof e.path === "string" && typeof e.rule === "string" && typeof e.detectedAt === "string" && typeof e.deployRef === "string");
|
|
3636
|
+
}
|
|
3637
|
+
async function fetchDeploymentStatus(cfg, slug, fetcher = fetch) {
|
|
3638
|
+
let raw;
|
|
3639
|
+
try {
|
|
3640
|
+
raw = await apiJson(cfg, { path: `/apps/${encodeURIComponent(slug)}/deployment-status` }, fetcher);
|
|
3641
|
+
} catch (e) {
|
|
3642
|
+
if (e instanceof NotFoundError)
|
|
3643
|
+
return null;
|
|
3644
|
+
throw e;
|
|
3645
|
+
}
|
|
3646
|
+
if (typeof raw.supported !== "boolean")
|
|
3647
|
+
return null;
|
|
3648
|
+
return {
|
|
3649
|
+
slug: typeof raw.slug === "string" ? raw.slug : slug,
|
|
3650
|
+
supported: raw.supported,
|
|
3651
|
+
liveDeployment: raw.liveDeployment ?? null,
|
|
3652
|
+
lastSuccessfulDeployment: raw.lastSuccessfulDeployment ?? null
|
|
3653
|
+
};
|
|
3654
|
+
}
|
|
3655
|
+
|
|
3656
|
+
// src/deploy/wait-for-deployment.ts
|
|
3657
|
+
var DEFAULT_POLL_INTERVAL_MS = 5000;
|
|
3658
|
+
function realWaitDeps(cfg) {
|
|
3659
|
+
return {
|
|
3660
|
+
fetchStatus: (slug) => fetchDeploymentStatus(cfg, slug),
|
|
3661
|
+
sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
|
|
3662
|
+
now: () => Date.now()
|
|
3663
|
+
};
|
|
3664
|
+
}
|
|
3665
|
+
async function waitForDeployment(args, deps) {
|
|
3666
|
+
const pollIntervalMs = args.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
3667
|
+
const start = deps.now();
|
|
3668
|
+
for (;; ) {
|
|
3669
|
+
let status;
|
|
3670
|
+
try {
|
|
3671
|
+
status = await deps.fetchStatus(args.slug);
|
|
3672
|
+
} catch {
|
|
3673
|
+
return { kind: "unknown" };
|
|
3674
|
+
}
|
|
3675
|
+
if (status === null)
|
|
3676
|
+
return { kind: "unknown" };
|
|
3677
|
+
if (!status.supported)
|
|
3678
|
+
return { kind: "unsupported" };
|
|
3679
|
+
const live = status.liveDeployment;
|
|
3680
|
+
if (live) {
|
|
3681
|
+
if (live.buildStatus === "success") {
|
|
3682
|
+
return { kind: "success", deploymentUrl: live.url };
|
|
3683
|
+
}
|
|
3684
|
+
if (live.buildStatus === "failure") {
|
|
3685
|
+
return { kind: "failure", logExcerpt: live.logExcerpt, failedStage: live.failedStage };
|
|
3686
|
+
}
|
|
3687
|
+
}
|
|
3688
|
+
if (deps.now() - start >= args.timeoutMs)
|
|
3689
|
+
return { kind: "pending" };
|
|
3690
|
+
await deps.sleep(pollIntervalMs);
|
|
3691
|
+
}
|
|
3692
|
+
}
|
|
3693
|
+
|
|
3694
|
+
// src/deploy/served-verify.ts
|
|
3695
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
3696
|
+
function sha256(bytes) {
|
|
3697
|
+
return createHash2("sha256").update(bytes).digest("hex");
|
|
3698
|
+
}
|
|
3699
|
+
function toBytes(v) {
|
|
3700
|
+
return typeof v === "string" ? new TextEncoder().encode(v) : v;
|
|
3701
|
+
}
|
|
3702
|
+
async function verifyServed(args, deps = { fetch }) {
|
|
3703
|
+
let res;
|
|
3704
|
+
try {
|
|
3705
|
+
res = await deps.fetch(args.servedUrl, { method: "GET" });
|
|
3706
|
+
} catch (e) {
|
|
3707
|
+
return {
|
|
3708
|
+
kind: "unreachable",
|
|
3709
|
+
reason: `could not reach ${args.servedUrl}: ${e instanceof Error ? e.message : String(e)}`
|
|
3710
|
+
};
|
|
3711
|
+
}
|
|
3712
|
+
if (!res.ok) {
|
|
3713
|
+
return {
|
|
3714
|
+
kind: "unreachable",
|
|
3715
|
+
status: res.status,
|
|
3716
|
+
reason: `served URL returned HTTP ${res.status}`
|
|
3717
|
+
};
|
|
3718
|
+
}
|
|
3719
|
+
if (args.appType !== "static") {
|
|
3720
|
+
return { kind: "match" };
|
|
3721
|
+
}
|
|
3722
|
+
let servedBytes;
|
|
3723
|
+
try {
|
|
3724
|
+
servedBytes = new Uint8Array(await res.arrayBuffer());
|
|
3725
|
+
} catch (e) {
|
|
3726
|
+
return {
|
|
3727
|
+
kind: "unreachable",
|
|
3728
|
+
reason: `could not read served body: ${e instanceof Error ? e.message : String(e)}`
|
|
3729
|
+
};
|
|
3730
|
+
}
|
|
3731
|
+
const shippedBytes = toBytes(args.shippedEntrypoint);
|
|
3732
|
+
const servedHash = sha256(servedBytes);
|
|
3733
|
+
const shippedHash = sha256(shippedBytes);
|
|
3734
|
+
if (servedHash === shippedHash)
|
|
3735
|
+
return { kind: "match" };
|
|
3736
|
+
return {
|
|
3737
|
+
kind: "mismatch",
|
|
3738
|
+
servedHash,
|
|
3739
|
+
shippedHash,
|
|
3740
|
+
servedLen: servedBytes.byteLength,
|
|
3741
|
+
shippedLen: shippedBytes.byteLength
|
|
3742
|
+
};
|
|
3743
|
+
}
|
|
3744
|
+
|
|
3745
|
+
// src/deploy/verify-deploy.ts
|
|
3746
|
+
var VERIFY_MISMATCH_EXIT = 65;
|
|
3747
|
+
function readLocalEntrypoint(cwd) {
|
|
3748
|
+
for (const rel of ["index.html", join8("static", "index.html")]) {
|
|
3749
|
+
const p = join8(cwd, rel);
|
|
3750
|
+
if (existsSync3(p))
|
|
3751
|
+
return new Uint8Array(readFileSync5(p));
|
|
3752
|
+
}
|
|
3753
|
+
return null;
|
|
3754
|
+
}
|
|
3755
|
+
function realVerifyDeployDeps(cfg) {
|
|
3756
|
+
return {
|
|
3757
|
+
wait: waitForDeployment,
|
|
3758
|
+
verify: verifyServed,
|
|
3759
|
+
readLocalEntrypoint,
|
|
3760
|
+
waitDeps: realWaitDeps(cfg),
|
|
3761
|
+
verifyDeps: { fetch }
|
|
3762
|
+
};
|
|
3763
|
+
}
|
|
3764
|
+
async function runDeployVerification(args, io, deps) {
|
|
3765
|
+
io.out("");
|
|
3766
|
+
io.out("Verifying the live deployment serves what you shipped …");
|
|
3767
|
+
const settle = await deps.wait({ slug: args.slug, timeoutMs: args.timeoutMs }, deps.waitDeps);
|
|
3768
|
+
switch (settle.kind) {
|
|
3769
|
+
case "unsupported":
|
|
3770
|
+
io.out(" (skipped — this app has no Pages deployment surface)");
|
|
3771
|
+
return 0;
|
|
3772
|
+
case "unknown":
|
|
3773
|
+
io.out(` (couldn't read deployment status — verify manually with \`launchpad status ${args.slug}\`)`);
|
|
3774
|
+
return 0;
|
|
3775
|
+
case "pending":
|
|
3776
|
+
io.out(`⏳ Build still in progress after ${Math.round(args.timeoutMs / 1000)}s — couldn't verify yet.`);
|
|
3777
|
+
io.out(` Re-check the outcome: \`launchpad status ${args.slug}\``);
|
|
3778
|
+
return 0;
|
|
3779
|
+
case "failure":
|
|
3780
|
+
io.err("✗ The Cloudflare Pages build FAILED — your content is NOT live.");
|
|
3781
|
+
if (settle.failedStage)
|
|
3782
|
+
io.err(` failed stage: ${settle.failedStage}`);
|
|
3783
|
+
if (settle.logExcerpt)
|
|
3784
|
+
io.err(` ${settle.logExcerpt}`);
|
|
3785
|
+
io.err(` Full logs: \`launchpad status ${args.slug}\``);
|
|
3786
|
+
return 1;
|
|
3787
|
+
case "success":
|
|
3788
|
+
return verifySuccess(args, settle.deploymentUrl, io, deps);
|
|
3789
|
+
}
|
|
3790
|
+
}
|
|
3791
|
+
async function verifySuccess(args, deploymentUrl, io, deps) {
|
|
3792
|
+
if (deploymentUrl === null || deploymentUrl.length === 0) {
|
|
3793
|
+
io.out(" (build succeeded but no served URL was reported — verify manually)");
|
|
3794
|
+
return 0;
|
|
3795
|
+
}
|
|
3796
|
+
const entrypoint = deps.readLocalEntrypoint(args.cwd);
|
|
3797
|
+
if (entrypoint === null) {
|
|
3798
|
+
io.out(" (no local index.html found to compare against — skipped)");
|
|
3799
|
+
return 0;
|
|
3800
|
+
}
|
|
3801
|
+
const verdict = await deps.verify({ servedUrl: deploymentUrl, shippedEntrypoint: entrypoint, appType: args.appType }, deps.verifyDeps);
|
|
3802
|
+
switch (verdict.kind) {
|
|
3803
|
+
case "match":
|
|
3804
|
+
io.out("✓ Verified — the live deployment serves exactly what you shipped.");
|
|
3805
|
+
return 0;
|
|
3806
|
+
case "unreachable":
|
|
3807
|
+
io.out(` (couldn't reach the served URL to verify: ${verdict.reason}) — check manually`);
|
|
3808
|
+
return 0;
|
|
3809
|
+
case "mismatch":
|
|
3810
|
+
io.err("");
|
|
3811
|
+
io.err("✗ LIVE SERVES DIFFERENT CONTENT THAN YOU SHIPPED.");
|
|
3812
|
+
io.err(" Your deploy committed, but the served page does not match your local index.html.");
|
|
3813
|
+
io.err(" The app's build likely reads the entrypoint from a different path than your files");
|
|
3814
|
+
io.err(" landed — e.g. it serves `static/index.html` while your content is at the repo root");
|
|
3815
|
+
io.err(" (or vice-versa). This is the silent-revert class; your content did NOT go live.");
|
|
3816
|
+
io.err(` served ${verdict.servedLen}B (sha ${verdict.servedHash.slice(0, 12)}) ≠ shipped ${verdict.shippedLen}B (sha ${verdict.shippedHash.slice(0, 12)})`);
|
|
3817
|
+
io.err(` Check the app's Pages build config; see \`launchpad status ${args.slug}\`.`);
|
|
3818
|
+
return VERIFY_MISMATCH_EXIT;
|
|
3819
|
+
}
|
|
3820
|
+
}
|
|
3821
|
+
|
|
3619
3822
|
// src/commands/deploy.ts
|
|
3620
3823
|
import { parse as parseYaml5 } from "yaml";
|
|
3621
|
-
import { readFileSync as
|
|
3824
|
+
import { readFileSync as readFileSync8 } from "node:fs";
|
|
3622
3825
|
|
|
3623
3826
|
// src/deploy/git-files.ts
|
|
3624
3827
|
import { spawn as spawn3 } from "node:child_process";
|
|
@@ -3894,6 +4097,19 @@ function parseDeployFlags(args) {
|
|
|
3894
4097
|
i += 2;
|
|
3895
4098
|
continue;
|
|
3896
4099
|
}
|
|
4100
|
+
if (a === "--no-verify") {
|
|
4101
|
+
i += 1;
|
|
4102
|
+
continue;
|
|
4103
|
+
}
|
|
4104
|
+
if (a.startsWith("--verify-timeout=")) {
|
|
4105
|
+
const v = a.slice("--verify-timeout=".length);
|
|
4106
|
+
const n = Number(v);
|
|
4107
|
+
if (!Number.isFinite(n) || n <= 0 || Math.floor(n) !== n) {
|
|
4108
|
+
return `invalid --verify-timeout "${v}" — expected positive integer seconds`;
|
|
4109
|
+
}
|
|
4110
|
+
i += 1;
|
|
4111
|
+
continue;
|
|
4112
|
+
}
|
|
3897
4113
|
return `unknown argument "${a}"`;
|
|
3898
4114
|
}
|
|
3899
4115
|
if (modeFlagsSeen > 1) {
|
|
@@ -4045,16 +4261,16 @@ function parseDeployFlags(args) {
|
|
|
4045
4261
|
}
|
|
4046
4262
|
|
|
4047
4263
|
// src/deploy/apply.ts
|
|
4048
|
-
import { existsSync as
|
|
4049
|
-
import { resolve as resolvePath, join as
|
|
4264
|
+
import { existsSync as existsSync4, rmSync } from "node:fs";
|
|
4265
|
+
import { resolve as resolvePath, join as join9 } from "node:path";
|
|
4050
4266
|
|
|
4051
4267
|
// src/manifest/load.ts
|
|
4052
|
-
import { readFileSync as
|
|
4268
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
4053
4269
|
import { parse as parseYaml3, YAMLParseError } from "yaml";
|
|
4054
4270
|
function loadManifest(path7) {
|
|
4055
4271
|
let raw;
|
|
4056
4272
|
try {
|
|
4057
|
-
raw =
|
|
4273
|
+
raw = readFileSync6(path7, "utf8");
|
|
4058
4274
|
} catch (err) {
|
|
4059
4275
|
const e = err;
|
|
4060
4276
|
if (e.code === "ENOENT") {
|
|
@@ -4276,8 +4492,8 @@ function sleep(ms) {
|
|
|
4276
4492
|
return new Promise((res) => setTimeout(res, ms));
|
|
4277
4493
|
}
|
|
4278
4494
|
function deletePinIfPresent(cfg, slug, io) {
|
|
4279
|
-
const pinPath =
|
|
4280
|
-
if (!
|
|
4495
|
+
const pinPath = join9(cfg.stateDir, slug, "group.json");
|
|
4496
|
+
if (!existsSync4(pinPath))
|
|
4281
4497
|
return;
|
|
4282
4498
|
try {
|
|
4283
4499
|
rmSync(pinPath, { force: true });
|
|
@@ -4710,7 +4926,7 @@ function handleNetworkError(e, io, slug, verb) {
|
|
|
4710
4926
|
|
|
4711
4927
|
// src/commands/infer-slug.ts
|
|
4712
4928
|
import * as path7 from "node:path";
|
|
4713
|
-
import { existsSync as
|
|
4929
|
+
import { existsSync as existsSync5, readFileSync as readFileSync7 } from "node:fs";
|
|
4714
4930
|
import { parse as parseYaml4 } from "yaml";
|
|
4715
4931
|
var DIRNAME_RE = /^launchpad-app-([a-z0-9][a-z0-9-]*[a-z0-9])$/;
|
|
4716
4932
|
function inferSlugFromCwd(cwd) {
|
|
@@ -4730,10 +4946,10 @@ function resolveManifestSlug(parsed) {
|
|
|
4730
4946
|
return null;
|
|
4731
4947
|
}
|
|
4732
4948
|
function inferSlugFromManifestFile(manifestPath) {
|
|
4733
|
-
if (!
|
|
4949
|
+
if (!existsSync5(manifestPath))
|
|
4734
4950
|
return null;
|
|
4735
4951
|
try {
|
|
4736
|
-
return resolveManifestSlug(parseYaml4(
|
|
4952
|
+
return resolveManifestSlug(parseYaml4(readFileSync7(manifestPath, "utf8")));
|
|
4737
4953
|
} catch {
|
|
4738
4954
|
return null;
|
|
4739
4955
|
}
|
|
@@ -4800,7 +5016,7 @@ async function runDeploy(args, io) {
|
|
|
4800
5016
|
}
|
|
4801
5017
|
const cwd = process.cwd();
|
|
4802
5018
|
const manifestPath = path8.join(cwd, "launchpad.yaml");
|
|
4803
|
-
if (
|
|
5019
|
+
if (existsSync6(manifestPath)) {
|
|
4804
5020
|
const contentMode = flags.mode.kind === "content" ? flags.mode : { allowStale: false, rebaseOntoHead: false, overrideStale: null };
|
|
4805
5021
|
return runModelADeploy({
|
|
4806
5022
|
cwd,
|
|
@@ -4844,9 +5060,9 @@ async function runDeploy(args, io) {
|
|
|
4844
5060
|
}
|
|
4845
5061
|
let contentManifestYaml = null;
|
|
4846
5062
|
const contentManifestPath = path8.join(process.cwd(), "launchpad.yaml");
|
|
4847
|
-
if (
|
|
5063
|
+
if (existsSync6(contentManifestPath)) {
|
|
4848
5064
|
try {
|
|
4849
|
-
contentManifestYaml =
|
|
5065
|
+
contentManifestYaml = readFileSync8(contentManifestPath, "utf8");
|
|
4850
5066
|
} catch {
|
|
4851
5067
|
contentManifestYaml = null;
|
|
4852
5068
|
}
|
|
@@ -5055,17 +5271,31 @@ function surfaceStaleBlock(body, io) {
|
|
|
5055
5271
|
async function runModelADeploy(args) {
|
|
5056
5272
|
const { cwd, manifestPath, io } = args;
|
|
5057
5273
|
let slug;
|
|
5274
|
+
let appType = "static";
|
|
5058
5275
|
try {
|
|
5059
|
-
const
|
|
5276
|
+
const manifestObj = parseYaml5(readFileSync8(manifestPath, "utf8"));
|
|
5277
|
+
const metaSlug = resolveManifestSlug(manifestObj);
|
|
5060
5278
|
if (metaSlug === null) {
|
|
5061
5279
|
io.err(`launchpad deploy: launchpad.yaml is missing metadata.slug (v2) / metadata.name (v1). ` + `Run \`launchpad init\` again to regenerate the manifest.`);
|
|
5062
5280
|
return 64;
|
|
5063
5281
|
}
|
|
5064
5282
|
slug = metaSlug;
|
|
5283
|
+
const dtype = manifestObj?.deployment?.type;
|
|
5284
|
+
if (dtype === "static" || dtype === "react" || dtype === "react+api" || dtype === "container") {
|
|
5285
|
+
appType = dtype;
|
|
5286
|
+
}
|
|
5065
5287
|
} catch (e) {
|
|
5066
5288
|
io.err(`launchpad deploy: failed to read ${manifestPath}: ${describe13(e)}`);
|
|
5067
5289
|
return 1;
|
|
5068
5290
|
}
|
|
5291
|
+
const noVerify = args.argv.includes("--no-verify");
|
|
5292
|
+
let verifyTimeoutMs = 180000;
|
|
5293
|
+
const vtFlag = args.argv.find((a) => a.startsWith("--verify-timeout="));
|
|
5294
|
+
if (vtFlag !== undefined) {
|
|
5295
|
+
const secs = Number.parseInt(vtFlag.slice("--verify-timeout=".length), 10);
|
|
5296
|
+
if (Number.isFinite(secs) && secs > 0)
|
|
5297
|
+
verifyTimeoutMs = secs * 1000;
|
|
5298
|
+
}
|
|
5069
5299
|
if (!SLUG_RE4.test(slug)) {
|
|
5070
5300
|
io.err(`launchpad deploy: invalid slug "${slug}" in manifest — expected ${SLUG_RE4.source}`);
|
|
5071
5301
|
return 64;
|
|
@@ -5174,11 +5404,11 @@ async function runModelADeploy(args) {
|
|
|
5174
5404
|
}
|
|
5175
5405
|
if (success.status === "provisioning_started") {
|
|
5176
5406
|
const submissionId = typeof success.submissionId === "string" ? success.submissionId : "(missing)";
|
|
5177
|
-
const
|
|
5407
|
+
const appType2 = typeof success.appType === "string" ? success.appType : "(missing)";
|
|
5178
5408
|
const message = typeof success.message === "string" ? success.message : "Bot accepted the upload and started provisioning.";
|
|
5179
5409
|
io.out(`✓ First-time deploy — provisioning workflow started for ${slug}`);
|
|
5180
5410
|
io.out(` submission: ${submissionId}`);
|
|
5181
|
-
io.out(` appType: ${
|
|
5411
|
+
io.out(` appType: ${appType2}`);
|
|
5182
5412
|
io.out("");
|
|
5183
5413
|
io.out(message);
|
|
5184
5414
|
io.out("");
|
|
@@ -5209,8 +5439,11 @@ async function runModelADeploy(args) {
|
|
|
5209
5439
|
surfaceDeployExtras(success, io, slug, args.allowStale);
|
|
5210
5440
|
io.out("");
|
|
5211
5441
|
io.out("Committed; build pending — Cloudflare Pages is building this deploy now.");
|
|
5212
|
-
|
|
5213
|
-
|
|
5442
|
+
if (noVerify) {
|
|
5443
|
+
io.out(`Run \`launchpad status ${slug}\` to confirm the build outcome (success / failure + log excerpt).`);
|
|
5444
|
+
return 0;
|
|
5445
|
+
}
|
|
5446
|
+
return runDeployVerification({ slug, cwd, appType, timeoutMs: verifyTimeoutMs }, io, realVerifyDeployDeps(cfg));
|
|
5214
5447
|
}
|
|
5215
5448
|
}
|
|
5216
5449
|
}
|
|
@@ -5406,7 +5639,7 @@ function describe14(e) {
|
|
|
5406
5639
|
}
|
|
5407
5640
|
|
|
5408
5641
|
// src/commands/generate.ts
|
|
5409
|
-
import { mkdirSync, readFileSync as
|
|
5642
|
+
import { mkdirSync, readFileSync as readFileSync9, writeFileSync } from "node:fs";
|
|
5410
5643
|
import { dirname as dirname5, resolve as resolve8, relative as relative3 } from "node:path";
|
|
5411
5644
|
var generateCommand = {
|
|
5412
5645
|
name: "generate",
|
|
@@ -5494,7 +5727,7 @@ function applyOne(artefact, path10, out, force) {
|
|
|
5494
5727
|
}
|
|
5495
5728
|
function readIfExists(path10) {
|
|
5496
5729
|
try {
|
|
5497
|
-
return { kind: "ok", content:
|
|
5730
|
+
return { kind: "ok", content: readFileSync9(path10, "utf8") };
|
|
5498
5731
|
} catch (err) {
|
|
5499
5732
|
const e = err;
|
|
5500
5733
|
if (e.code === "ENOENT")
|
|
@@ -5705,13 +5938,13 @@ function parseFlags(args) {
|
|
|
5705
5938
|
}
|
|
5706
5939
|
|
|
5707
5940
|
// src/groups/client.ts
|
|
5708
|
-
import { mkdirSync as mkdirSync2, readFileSync as
|
|
5709
|
-
import { dirname as dirname6, join as
|
|
5941
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync10, writeFileSync as writeFileSync2 } from "node:fs";
|
|
5942
|
+
import { dirname as dirname6, join as join11 } from "node:path";
|
|
5710
5943
|
var CACHE_TTL_MS = 60 * 60 * 1000;
|
|
5711
5944
|
var CACHE_FILENAME = "groups.json";
|
|
5712
5945
|
async function fetchGroups(cfg, opts = {}) {
|
|
5713
5946
|
const now = opts.now ?? Date.now;
|
|
5714
|
-
const cachePath =
|
|
5947
|
+
const cachePath = join11(cfg.cacheDir, CACHE_FILENAME);
|
|
5715
5948
|
if (opts.forceRefresh !== true) {
|
|
5716
5949
|
const cached = readCache(cachePath);
|
|
5717
5950
|
if (cached !== null) {
|
|
@@ -5749,7 +5982,7 @@ async function fetchGroups(cfg, opts = {}) {
|
|
|
5749
5982
|
function readCache(path10) {
|
|
5750
5983
|
let raw;
|
|
5751
5984
|
try {
|
|
5752
|
-
raw =
|
|
5985
|
+
raw = readFileSync10(path10, "utf8");
|
|
5753
5986
|
} catch {
|
|
5754
5987
|
return null;
|
|
5755
5988
|
}
|
|
@@ -6250,17 +6483,17 @@ function describe18(e) {
|
|
|
6250
6483
|
|
|
6251
6484
|
// src/commands/init.ts
|
|
6252
6485
|
import { createInterface } from "node:readline/promises";
|
|
6253
|
-
import { existsSync as
|
|
6486
|
+
import { existsSync as existsSync8, readFileSync as readFileSync12, writeFileSync as writeFileSync3 } from "node:fs";
|
|
6254
6487
|
import { resolve as resolve9 } from "node:path";
|
|
6255
6488
|
import { stringify as yamlStringify } from "yaml";
|
|
6256
6489
|
|
|
6257
6490
|
// src/detect/index.ts
|
|
6258
|
-
import { existsSync as
|
|
6259
|
-
import { join as
|
|
6491
|
+
import { existsSync as existsSync7, readFileSync as readFileSync11, statSync } from "node:fs";
|
|
6492
|
+
import { join as join12 } from "node:path";
|
|
6260
6493
|
function detectAppShape(cwd) {
|
|
6261
|
-
const hasPackageJson =
|
|
6262
|
-
const hasAnyLockfile = LOCKFILES.some(({ file }) =>
|
|
6263
|
-
const hasViteConfig = VITE_CONFIG_NAMES.some((n) =>
|
|
6494
|
+
const hasPackageJson = existsSync7(join12(cwd, "package.json"));
|
|
6495
|
+
const hasAnyLockfile = LOCKFILES.some(({ file }) => existsSync7(join12(cwd, file)));
|
|
6496
|
+
const hasViteConfig = VITE_CONFIG_NAMES.some((n) => existsSync7(join12(cwd, n)));
|
|
6264
6497
|
if (!hasPackageJson && !hasAnyLockfile && !hasViteConfig) {
|
|
6265
6498
|
return {
|
|
6266
6499
|
kind: "not-applicable",
|
|
@@ -6306,7 +6539,7 @@ var LOCKFILES = [
|
|
|
6306
6539
|
{ file: "yarn.lock", pm: "yarn" }
|
|
6307
6540
|
];
|
|
6308
6541
|
function detectPackageManager(cwd) {
|
|
6309
|
-
const present = LOCKFILES.filter(({ file }) =>
|
|
6542
|
+
const present = LOCKFILES.filter(({ file }) => existsSync7(join12(cwd, file)));
|
|
6310
6543
|
if (present.length === 0) {
|
|
6311
6544
|
return {
|
|
6312
6545
|
kind: "ambiguous",
|
|
@@ -6328,8 +6561,8 @@ function detectPackageManager(cwd) {
|
|
|
6328
6561
|
}
|
|
6329
6562
|
function detectVitePresence(cwd) {
|
|
6330
6563
|
for (const name of VITE_CONFIG_NAMES) {
|
|
6331
|
-
const p =
|
|
6332
|
-
if (
|
|
6564
|
+
const p = join12(cwd, name);
|
|
6565
|
+
if (existsSync7(p)) {
|
|
6333
6566
|
return { kind: "ok", value: { path: p } };
|
|
6334
6567
|
}
|
|
6335
6568
|
}
|
|
@@ -6339,9 +6572,9 @@ function detectVitePresence(cwd) {
|
|
|
6339
6572
|
};
|
|
6340
6573
|
}
|
|
6341
6574
|
function detectAppType(cwd) {
|
|
6342
|
-
const fnDir =
|
|
6575
|
+
const fnDir = join12(cwd, "functions");
|
|
6343
6576
|
let hasFunctionsDir = false;
|
|
6344
|
-
if (
|
|
6577
|
+
if (existsSync7(fnDir)) {
|
|
6345
6578
|
try {
|
|
6346
6579
|
hasFunctionsDir = statSync(fnDir).isDirectory();
|
|
6347
6580
|
} catch {
|
|
@@ -6351,8 +6584,8 @@ function detectAppType(cwd) {
|
|
|
6351
6584
|
return { kind: "ok", value: hasFunctionsDir ? "react+api" : "react" };
|
|
6352
6585
|
}
|
|
6353
6586
|
function detectBuildCommand(cwd, pm) {
|
|
6354
|
-
const pkgJsonPath =
|
|
6355
|
-
if (!
|
|
6587
|
+
const pkgJsonPath = join12(cwd, "package.json");
|
|
6588
|
+
if (!existsSync7(pkgJsonPath)) {
|
|
6356
6589
|
return {
|
|
6357
6590
|
kind: "ambiguous",
|
|
6358
6591
|
reason: "no package.json at repo root. Run your package manager's `init` first."
|
|
@@ -6360,7 +6593,7 @@ function detectBuildCommand(cwd, pm) {
|
|
|
6360
6593
|
}
|
|
6361
6594
|
let pkgJson;
|
|
6362
6595
|
try {
|
|
6363
|
-
pkgJson = JSON.parse(
|
|
6596
|
+
pkgJson = JSON.parse(readFileSync11(pkgJsonPath, "utf8"));
|
|
6364
6597
|
} catch (e) {
|
|
6365
6598
|
return {
|
|
6366
6599
|
kind: "ambiguous",
|
|
@@ -6395,7 +6628,7 @@ var OUT_DIR_REGEX = /\bbuild\s*:\s*\{[^{}]*?\boutDir\s*:\s*['"]([^'"]+)['"]/s;
|
|
|
6395
6628
|
function detectDestinationDir(cwd, vite) {
|
|
6396
6629
|
let text;
|
|
6397
6630
|
try {
|
|
6398
|
-
text =
|
|
6631
|
+
text = readFileSync11(vite.path, "utf8");
|
|
6399
6632
|
} catch (e) {
|
|
6400
6633
|
return {
|
|
6401
6634
|
kind: "ambiguous",
|
|
@@ -6425,7 +6658,7 @@ async function runInit(args, io, prompt) {
|
|
|
6425
6658
|
}
|
|
6426
6659
|
const { inputs, options } = parsed;
|
|
6427
6660
|
const outPath = resolve9(process.cwd(), options.out);
|
|
6428
|
-
if (
|
|
6661
|
+
if (existsSync8(outPath) && !options.force) {
|
|
6429
6662
|
io.err(`launchpad init: ${outPath} already exists`);
|
|
6430
6663
|
io.err("Pass --force to overwrite.");
|
|
6431
6664
|
return 64;
|
|
@@ -6843,8 +7076,8 @@ function renderYaml(manifest) {
|
|
|
6843
7076
|
}
|
|
6844
7077
|
function ensureGitignoreEntries(path10, entries) {
|
|
6845
7078
|
let current = "";
|
|
6846
|
-
if (
|
|
6847
|
-
current =
|
|
7079
|
+
if (existsSync8(path10)) {
|
|
7080
|
+
current = readFileSync12(path10, "utf8");
|
|
6848
7081
|
}
|
|
6849
7082
|
const lines = current.split(/\r?\n/);
|
|
6850
7083
|
const present = new Set(lines.map((l) => l.trim()));
|
|
@@ -7833,39 +8066,6 @@ async function fetchManifestStatus(cfg, slug, fetcher = fetch) {
|
|
|
7833
8066
|
return apiJson(cfg, { path: path12 }, fetcher);
|
|
7834
8067
|
}
|
|
7835
8068
|
|
|
7836
|
-
// src/deploy/deployment-status.ts
|
|
7837
|
-
async function fetchStandingExceptions(cfg, slug, fetcher = fetch) {
|
|
7838
|
-
let raw;
|
|
7839
|
-
try {
|
|
7840
|
-
raw = await apiJson(cfg, { path: `/apps/${encodeURIComponent(slug)}/exceptions` }, fetcher);
|
|
7841
|
-
} catch (e) {
|
|
7842
|
-
if (e instanceof NotFoundError)
|
|
7843
|
-
return null;
|
|
7844
|
-
throw e;
|
|
7845
|
-
}
|
|
7846
|
-
if (!Array.isArray(raw.exceptions))
|
|
7847
|
-
return null;
|
|
7848
|
-
return raw.exceptions.filter((e) => typeof e === "object" && e !== null && typeof e.path === "string" && typeof e.rule === "string" && typeof e.detectedAt === "string" && typeof e.deployRef === "string");
|
|
7849
|
-
}
|
|
7850
|
-
async function fetchDeploymentStatus(cfg, slug, fetcher = fetch) {
|
|
7851
|
-
let raw;
|
|
7852
|
-
try {
|
|
7853
|
-
raw = await apiJson(cfg, { path: `/apps/${encodeURIComponent(slug)}/deployment-status` }, fetcher);
|
|
7854
|
-
} catch (e) {
|
|
7855
|
-
if (e instanceof NotFoundError)
|
|
7856
|
-
return null;
|
|
7857
|
-
throw e;
|
|
7858
|
-
}
|
|
7859
|
-
if (typeof raw.supported !== "boolean")
|
|
7860
|
-
return null;
|
|
7861
|
-
return {
|
|
7862
|
-
slug: typeof raw.slug === "string" ? raw.slug : slug,
|
|
7863
|
-
supported: raw.supported,
|
|
7864
|
-
liveDeployment: raw.liveDeployment ?? null,
|
|
7865
|
-
lastSuccessfulDeployment: raw.lastSuccessfulDeployment ?? null
|
|
7866
|
-
};
|
|
7867
|
-
}
|
|
7868
|
-
|
|
7869
8069
|
// src/commands/pull.ts
|
|
7870
8070
|
var pullCommand = {
|
|
7871
8071
|
name: "pull",
|
|
@@ -8205,7 +8405,7 @@ function printUsage5(io) {
|
|
|
8205
8405
|
}
|
|
8206
8406
|
|
|
8207
8407
|
// src/commands/status.ts
|
|
8208
|
-
import { readFileSync as
|
|
8408
|
+
import { readFileSync as readFileSync13 } from "node:fs";
|
|
8209
8409
|
var statusCommand = {
|
|
8210
8410
|
name: "status",
|
|
8211
8411
|
summary: "show drift between local launchpad.yaml and deployed state",
|
|
@@ -8262,7 +8462,7 @@ async function runStatus(args, io) {
|
|
|
8262
8462
|
}
|
|
8263
8463
|
let localYaml = null;
|
|
8264
8464
|
try {
|
|
8265
|
-
localYaml =
|
|
8465
|
+
localYaml = readFileSync13(parsed.file, "utf8");
|
|
8266
8466
|
} catch (e) {
|
|
8267
8467
|
if (!isEnoent(e)) {
|
|
8268
8468
|
io.err(`launchpad status: cannot read local manifest at ${parsed.file}: ${describe25(e)}`);
|
|
@@ -8865,7 +9065,7 @@ function describe26(e) {
|
|
|
8865
9065
|
}
|
|
8866
9066
|
|
|
8867
9067
|
// src/deploy/rollback.ts
|
|
8868
|
-
import { existsSync as
|
|
9068
|
+
import { existsSync as existsSync9, readFileSync as readFileSync14, writeFileSync as writeFileSync5 } from "node:fs";
|
|
8869
9069
|
import { resolve as resolvePath2 } from "node:path";
|
|
8870
9070
|
import { createInterface as createInterface3 } from "node:readline/promises";
|
|
8871
9071
|
|
|
@@ -8990,11 +9190,11 @@ async function runRollback(opts, io, deps = {}) {
|
|
|
8990
9190
|
}, io, applyDeps);
|
|
8991
9191
|
}
|
|
8992
9192
|
function readCurrentManifest(path13) {
|
|
8993
|
-
if (!
|
|
9193
|
+
if (!existsSync9(path13))
|
|
8994
9194
|
return null;
|
|
8995
9195
|
let raw;
|
|
8996
9196
|
try {
|
|
8997
|
-
raw =
|
|
9197
|
+
raw = readFileSync14(path13, "utf8");
|
|
8998
9198
|
} catch {
|
|
8999
9199
|
return null;
|
|
9000
9200
|
}
|
|
@@ -9150,7 +9350,7 @@ function parseArgs11(args) {
|
|
|
9150
9350
|
}
|
|
9151
9351
|
|
|
9152
9352
|
// src/secrets/push.ts
|
|
9153
|
-
import { existsSync as
|
|
9353
|
+
import { existsSync as existsSync10, readFileSync as readFileSync15 } from "node:fs";
|
|
9154
9354
|
import { resolve as resolvePath3 } from "node:path";
|
|
9155
9355
|
|
|
9156
9356
|
// src/secrets/env-parse.ts
|
|
@@ -9205,14 +9405,14 @@ async function runSecretsPush(opts, io, deps = {}) {
|
|
|
9205
9405
|
return 0;
|
|
9206
9406
|
}
|
|
9207
9407
|
const envPath = resolvePath3(process.cwd(), opts.env ?? ".env");
|
|
9208
|
-
if (!
|
|
9408
|
+
if (!existsSync10(envPath)) {
|
|
9209
9409
|
io.err(`launchpad secrets push: ${envPath}`);
|
|
9210
9410
|
io.err(" .env file not found. Run `launchpad secrets template` to scaffold one.");
|
|
9211
9411
|
return 2;
|
|
9212
9412
|
}
|
|
9213
9413
|
let envText;
|
|
9214
9414
|
try {
|
|
9215
|
-
envText =
|
|
9415
|
+
envText = readFileSync15(envPath, "utf8");
|
|
9216
9416
|
} catch (e) {
|
|
9217
9417
|
io.err(`launchpad secrets push: failed to read ${envPath}: ${describe28(e)}`);
|
|
9218
9418
|
return 2;
|
|
@@ -9484,7 +9684,7 @@ function renderManifestError5(result, io) {
|
|
|
9484
9684
|
}
|
|
9485
9685
|
|
|
9486
9686
|
// src/secrets/set.ts
|
|
9487
|
-
import { existsSync as
|
|
9687
|
+
import { existsSync as existsSync11, readFileSync as readFileSync16 } from "node:fs";
|
|
9488
9688
|
import { resolve as resolvePath5 } from "node:path";
|
|
9489
9689
|
import { parse as parseYaml6 } from "yaml";
|
|
9490
9690
|
var CELL_LABEL2 = {
|
|
@@ -9494,14 +9694,14 @@ var CELL_LABEL2 = {
|
|
|
9494
9694
|
};
|
|
9495
9695
|
function loadSet(fleetFile, setName, io) {
|
|
9496
9696
|
const path13 = resolvePath5(process.cwd(), fleetFile ?? "fleet-secret-sets.yaml");
|
|
9497
|
-
if (!
|
|
9697
|
+
if (!existsSync11(path13)) {
|
|
9498
9698
|
io.err(`✗ ${path13}`);
|
|
9499
9699
|
io.err(" fleet-secret-sets.yaml not found. Run from the platform repo root or pass --fleet-file.");
|
|
9500
9700
|
return 2;
|
|
9501
9701
|
}
|
|
9502
9702
|
let obj;
|
|
9503
9703
|
try {
|
|
9504
|
-
obj = parseYaml6(
|
|
9704
|
+
obj = parseYaml6(readFileSync16(path13, "utf8"));
|
|
9505
9705
|
} catch (e) {
|
|
9506
9706
|
io.err(`✗ ${path13}`);
|
|
9507
9707
|
io.err(` YAML parse error: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -9586,14 +9786,14 @@ async function runSecretsPushSet(opts, io, deps = {}) {
|
|
|
9586
9786
|
if (typeof set === "number")
|
|
9587
9787
|
return set;
|
|
9588
9788
|
const envPath = resolvePath5(process.cwd(), opts.env ?? ".env");
|
|
9589
|
-
if (!
|
|
9789
|
+
if (!existsSync11(envPath)) {
|
|
9590
9790
|
io.err(`launchpad secrets push --set: ${envPath} not found.`);
|
|
9591
9791
|
io.err(` Create a .env carrying the set's secrets: ${set.secrets.join(", ")}`);
|
|
9592
9792
|
return 2;
|
|
9593
9793
|
}
|
|
9594
9794
|
let envText;
|
|
9595
9795
|
try {
|
|
9596
|
-
envText =
|
|
9796
|
+
envText = readFileSync16(envPath, "utf8");
|
|
9597
9797
|
} catch (e) {
|
|
9598
9798
|
io.err(`launchpad secrets push --set: failed to read ${envPath}: ${e instanceof Error ? e.message : String(e)}`);
|
|
9599
9799
|
return 2;
|
|
@@ -9737,7 +9937,7 @@ function setPushExit(e) {
|
|
|
9737
9937
|
}
|
|
9738
9938
|
|
|
9739
9939
|
// src/commands/secrets-template.ts
|
|
9740
|
-
import { existsSync as
|
|
9940
|
+
import { existsSync as existsSync12, writeFileSync as writeFileSync6 } from "node:fs";
|
|
9741
9941
|
import { resolve as resolve11 } from "node:path";
|
|
9742
9942
|
async function runSecretsTemplate(args, io) {
|
|
9743
9943
|
const flags = parseFlags4(args);
|
|
@@ -9761,7 +9961,7 @@ async function runSecretsTemplate(args, io) {
|
|
|
9761
9961
|
return 0;
|
|
9762
9962
|
}
|
|
9763
9963
|
const outPath = resolve11(process.cwd(), flags.out);
|
|
9764
|
-
if (
|
|
9964
|
+
if (existsSync12(outPath) && !flags.force) {
|
|
9765
9965
|
io.err(`launchpad secrets template: ${outPath} already exists`);
|
|
9766
9966
|
io.err("Pass --force to overwrite, or --stdout to print without writing.");
|
|
9767
9967
|
return 64;
|
|
@@ -10049,8 +10249,8 @@ function printHelp2(io) {
|
|
|
10049
10249
|
|
|
10050
10250
|
// src/commands/skills.ts
|
|
10051
10251
|
import { fileURLToPath } from "node:url";
|
|
10052
|
-
import { dirname as dirname7, join as
|
|
10053
|
-
import { promises as fs6, existsSync as
|
|
10252
|
+
import { dirname as dirname7, join as join13, resolve as resolve12 } from "node:path";
|
|
10253
|
+
import { promises as fs6, existsSync as existsSync13 } from "node:fs";
|
|
10054
10254
|
import { homedir as homedir2 } from "node:os";
|
|
10055
10255
|
var BUNDLE_PREFIX = "launchpad-";
|
|
10056
10256
|
var BUNDLED_SKILLS = [
|
|
@@ -10111,7 +10311,7 @@ function printHelp3(io) {
|
|
|
10111
10311
|
}
|
|
10112
10312
|
function resolveInstallEnv() {
|
|
10113
10313
|
const bundleDir = process.env.LAUNCHPAD_SKILLS_BUNDLE_DIR ?? defaultBundleDir();
|
|
10114
|
-
const userSkillsDir = process.env.LAUNCHPAD_SKILLS_TARGET_DIR ??
|
|
10314
|
+
const userSkillsDir = process.env.LAUNCHPAD_SKILLS_TARGET_DIR ?? join13(homedir2(), ".claude", "skills");
|
|
10115
10315
|
return { bundleDir, userSkillsDir };
|
|
10116
10316
|
}
|
|
10117
10317
|
function defaultBundleDir() {
|
|
@@ -10121,7 +10321,7 @@ function defaultBundleDir() {
|
|
|
10121
10321
|
resolve12(here, "..", "..", "skills")
|
|
10122
10322
|
];
|
|
10123
10323
|
for (const c of candidates) {
|
|
10124
|
-
if (
|
|
10324
|
+
if (existsSync13(join13(c, "launchpad-onboard", "SKILL.md"))) {
|
|
10125
10325
|
return c;
|
|
10126
10326
|
}
|
|
10127
10327
|
}
|
|
@@ -10142,12 +10342,12 @@ async function doInstall(io) {
|
|
|
10142
10342
|
}
|
|
10143
10343
|
let installed = 0;
|
|
10144
10344
|
for (const skill of BUNDLED_SKILLS) {
|
|
10145
|
-
const src =
|
|
10345
|
+
const src = join13(env.bundleDir, skill);
|
|
10146
10346
|
if (!await isDir(src)) {
|
|
10147
10347
|
io.err(`launchpad skills install: bundled skill "${skill}" missing from ${env.bundleDir} — package is incomplete.`);
|
|
10148
10348
|
return 1;
|
|
10149
10349
|
}
|
|
10150
|
-
const dest =
|
|
10350
|
+
const dest = join13(env.userSkillsDir, skill);
|
|
10151
10351
|
await fs6.rm(dest, { recursive: true, force: true });
|
|
10152
10352
|
await fs6.cp(src, dest, { recursive: true });
|
|
10153
10353
|
installed++;
|
|
@@ -10172,7 +10372,7 @@ async function doUninstall(io) {
|
|
|
10172
10372
|
continue;
|
|
10173
10373
|
if (!isBundleManaged(entry.name))
|
|
10174
10374
|
continue;
|
|
10175
|
-
const target =
|
|
10375
|
+
const target = join13(env.userSkillsDir, entry.name);
|
|
10176
10376
|
await fs6.rm(target, { recursive: true, force: true });
|
|
10177
10377
|
removed++;
|
|
10178
10378
|
io.out(`✗ removed ${target}`);
|
|
@@ -10195,7 +10395,7 @@ async function doList(io) {
|
|
|
10195
10395
|
}
|
|
10196
10396
|
const width = managedDirs.reduce((n, s) => Math.max(n, s.length), 0);
|
|
10197
10397
|
for (const name of managedDirs) {
|
|
10198
|
-
const skillFile =
|
|
10398
|
+
const skillFile = join13(env.userSkillsDir, name, "SKILL.md");
|
|
10199
10399
|
const version = await readVersion(skillFile);
|
|
10200
10400
|
io.out(` ${name.padEnd(width + 2)}${version ?? "(no version)"}`);
|
|
10201
10401
|
}
|
|
@@ -10230,13 +10430,13 @@ function describe29(e) {
|
|
|
10230
10430
|
import { execFile, spawn as spawn5 } from "node:child_process";
|
|
10231
10431
|
import { promisify } from "node:util";
|
|
10232
10432
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
10233
|
-
import { dirname as dirname8, resolve as resolve13, relative as relative4, isAbsolute as isAbsolute2, join as
|
|
10433
|
+
import { dirname as dirname8, resolve as resolve13, relative as relative4, isAbsolute as isAbsolute2, join as join14 } from "node:path";
|
|
10234
10434
|
import { homedir as homedir3, tmpdir } from "node:os";
|
|
10235
|
-
import { readFileSync as
|
|
10435
|
+
import { readFileSync as readFileSync17, mkdtempSync, writeFileSync as writeFileSync7, rmSync as rmSync2 } from "node:fs";
|
|
10236
10436
|
|
|
10237
10437
|
// src/commands/channel-auth.ts
|
|
10238
10438
|
import { createServer as createServer2 } from "node:http";
|
|
10239
|
-
import { createHash as
|
|
10439
|
+
import { createHash as createHash3, randomBytes as randomBytes5 } from "node:crypto";
|
|
10240
10440
|
var CHANNEL_BASE = "https://get.launchpad.m-kopa.us";
|
|
10241
10441
|
var CLI_AUTH_URL = `${CHANNEL_BASE}/__cli_auth`;
|
|
10242
10442
|
var CLI_TOKEN_URL = `${CHANNEL_BASE}/__cli_token`;
|
|
@@ -10247,7 +10447,7 @@ function base64url(b) {
|
|
|
10247
10447
|
}
|
|
10248
10448
|
function pkcePair() {
|
|
10249
10449
|
const verifier = base64url(randomBytes5(32));
|
|
10250
|
-
const challenge = base64url(
|
|
10450
|
+
const challenge = base64url(createHash3("sha256").update(verifier).digest());
|
|
10251
10451
|
return { verifier, challenge };
|
|
10252
10452
|
}
|
|
10253
10453
|
async function startLoopback(state, timeoutMs) {
|
|
@@ -10353,7 +10553,7 @@ var PKG = "@m-kopa/launchpad-cli";
|
|
|
10353
10553
|
var REGISTRY = "https://registry.npmjs.org";
|
|
10354
10554
|
var CHANNEL_VERSION_URL = "https://get.launchpad.m-kopa.us/version.json";
|
|
10355
10555
|
var CHANNEL_INSTALL_URL = "https://get.launchpad.m-kopa.us";
|
|
10356
|
-
var CHANNEL_MARKER =
|
|
10556
|
+
var CHANNEL_MARKER = join14(homedir3(), ".launchpad", "channel");
|
|
10357
10557
|
var EXIT_UPDATE_AVAILABLE = 10;
|
|
10358
10558
|
var UPGRADE_ARGS = {
|
|
10359
10559
|
npm: ["install", "-g", `${PKG}@latest`],
|
|
@@ -10463,8 +10663,8 @@ async function openSystemBrowser(url) {
|
|
|
10463
10663
|
await execFileAsync(opener, [url]);
|
|
10464
10664
|
}
|
|
10465
10665
|
async function runInstallerScript(script) {
|
|
10466
|
-
const dir = mkdtempSync(
|
|
10467
|
-
const file =
|
|
10666
|
+
const dir = mkdtempSync(join14(tmpdir(), "launchpad-update-"));
|
|
10667
|
+
const file = join14(dir, "install.sh");
|
|
10468
10668
|
try {
|
|
10469
10669
|
writeFileSync7(file, script, { mode: 448 });
|
|
10470
10670
|
return await new Promise((resolvePromise) => {
|
|
@@ -10488,7 +10688,7 @@ function resolveLatestVersion() {
|
|
|
10488
10688
|
}
|
|
10489
10689
|
function detectInstallChannel() {
|
|
10490
10690
|
try {
|
|
10491
|
-
return
|
|
10691
|
+
return readFileSync17(CHANNEL_MARKER, "utf8").trim() === "platform" ? "platform" : "github";
|
|
10492
10692
|
} catch {
|
|
10493
10693
|
return "github";
|
|
10494
10694
|
}
|
|
@@ -10695,7 +10895,7 @@ function printHelp4(io) {
|
|
|
10695
10895
|
}
|
|
10696
10896
|
|
|
10697
10897
|
// src/commands/validate.ts
|
|
10698
|
-
import { readFileSync as
|
|
10898
|
+
import { readFileSync as readFileSync18 } from "node:fs";
|
|
10699
10899
|
import { dirname as dirname9, resolve as resolve14 } from "node:path";
|
|
10700
10900
|
var validateCommand = {
|
|
10701
10901
|
name: "validate",
|
|
@@ -10729,7 +10929,7 @@ function checkBoundary(manifestPath, declared) {
|
|
|
10729
10929
|
}
|
|
10730
10930
|
let manifestYaml;
|
|
10731
10931
|
try {
|
|
10732
|
-
manifestYaml =
|
|
10932
|
+
manifestYaml = readFileSync18(manifestPath, "utf8");
|
|
10733
10933
|
} catch {
|
|
10734
10934
|
manifestYaml = null;
|
|
10735
10935
|
}
|
|
@@ -11234,21 +11434,21 @@ var revokeEditorCommand = makeEditorCommand("revoke");
|
|
|
11234
11434
|
// src/update-notifier.ts
|
|
11235
11435
|
import { spawn as spawn6 } from "node:child_process";
|
|
11236
11436
|
import { homedir as homedir4 } from "node:os";
|
|
11237
|
-
import { join as
|
|
11437
|
+
import { join as join15 } from "node:path";
|
|
11238
11438
|
import {
|
|
11239
|
-
existsSync as
|
|
11439
|
+
existsSync as existsSync14,
|
|
11240
11440
|
mkdirSync as mkdirSync3,
|
|
11241
|
-
readFileSync as
|
|
11441
|
+
readFileSync as readFileSync19,
|
|
11242
11442
|
readdirSync as readdirSync2,
|
|
11243
11443
|
writeFileSync as writeFileSync8
|
|
11244
11444
|
} from "node:fs";
|
|
11245
11445
|
var INTERNAL_REFRESH_VERB = "__refresh-update-cache";
|
|
11246
11446
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
11247
11447
|
var OPT_OUT_ENV = "LAUNCHPAD_NO_UPDATE_NOTIFIER";
|
|
11248
|
-
var CACHE_FILE =
|
|
11448
|
+
var CACHE_FILE = join15(homedir4(), ".launchpad", "update-check.json");
|
|
11249
11449
|
function readCache2() {
|
|
11250
11450
|
try {
|
|
11251
|
-
const raw = JSON.parse(
|
|
11451
|
+
const raw = JSON.parse(readFileSync19(CACHE_FILE, "utf8"));
|
|
11252
11452
|
if (typeof raw === "object" && raw !== null && typeof raw.checkedAt === "number") {
|
|
11253
11453
|
const latest = raw.latest;
|
|
11254
11454
|
return {
|
|
@@ -11261,15 +11461,15 @@ function readCache2() {
|
|
|
11261
11461
|
}
|
|
11262
11462
|
function writeCache2(state) {
|
|
11263
11463
|
try {
|
|
11264
|
-
mkdirSync3(
|
|
11464
|
+
mkdirSync3(join15(homedir4(), ".launchpad"), { recursive: true });
|
|
11265
11465
|
writeFileSync8(CACHE_FILE, `${JSON.stringify(state)}
|
|
11266
11466
|
`, { mode: 384 });
|
|
11267
11467
|
} catch {}
|
|
11268
11468
|
}
|
|
11269
11469
|
var SKILL_PREFIX = "launchpad-";
|
|
11270
|
-
var SKILLS_HINT_MARKER =
|
|
11470
|
+
var SKILLS_HINT_MARKER = join15(homedir4(), ".launchpad", "skills-hint-shown");
|
|
11271
11471
|
function skillsTargetDir() {
|
|
11272
|
-
return process.env.LAUNCHPAD_SKILLS_TARGET_DIR ??
|
|
11472
|
+
return process.env.LAUNCHPAD_SKILLS_TARGET_DIR ?? join15(homedir4(), ".claude", "skills");
|
|
11273
11473
|
}
|
|
11274
11474
|
function readInstalledSkills() {
|
|
11275
11475
|
try {
|
|
@@ -11277,14 +11477,14 @@ function readInstalledSkills() {
|
|
|
11277
11477
|
const bundle = readdirSync2(dir, { withFileTypes: true }).find((e) => e.isDirectory() && e.name.startsWith(SKILL_PREFIX));
|
|
11278
11478
|
if (!bundle)
|
|
11279
11479
|
return { present: false, version: null };
|
|
11280
|
-
return { present: true, version: readSkillVersion(
|
|
11480
|
+
return { present: true, version: readSkillVersion(join15(dir, bundle.name, "SKILL.md")) };
|
|
11281
11481
|
} catch {
|
|
11282
11482
|
return { present: false, version: null };
|
|
11283
11483
|
}
|
|
11284
11484
|
}
|
|
11285
11485
|
function readSkillVersion(path13) {
|
|
11286
11486
|
try {
|
|
11287
|
-
const text =
|
|
11487
|
+
const text = readFileSync19(path13, "utf8");
|
|
11288
11488
|
const fenceEnd = text.indexOf(`
|
|
11289
11489
|
---`, 4);
|
|
11290
11490
|
const front = fenceEnd === -1 ? text.slice(0, 1024) : text.slice(0, fenceEnd);
|
|
@@ -11296,14 +11496,14 @@ function readSkillVersion(path13) {
|
|
|
11296
11496
|
}
|
|
11297
11497
|
function absentHintShown() {
|
|
11298
11498
|
try {
|
|
11299
|
-
return
|
|
11499
|
+
return existsSync14(SKILLS_HINT_MARKER);
|
|
11300
11500
|
} catch {
|
|
11301
11501
|
return false;
|
|
11302
11502
|
}
|
|
11303
11503
|
}
|
|
11304
11504
|
function markAbsentHintShown() {
|
|
11305
11505
|
try {
|
|
11306
|
-
mkdirSync3(
|
|
11506
|
+
mkdirSync3(join15(homedir4(), ".launchpad"), { recursive: true });
|
|
11307
11507
|
writeFileSync8(SKILLS_HINT_MARKER, `${Date.now()}
|
|
11308
11508
|
`, { mode: 384 });
|
|
11309
11509
|
} catch {}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy-flags.d.ts","sourceRoot":"","sources":["../../src/commands/deploy-flags.ts"],"names":[],"mappings":"AA0BA,OAAO,EAAa,KAAK,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtD,MAAM,MAAM,UAAU,GAClB;IACE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,oEAAoE;IACpE,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B;uEACmE;IACnE,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC;gEAC4D;IAC5D,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3C,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE;AACrD;;;;;;;GAOG;GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE;AACpF;;;;;;;;;;GAUG;GACD;IACE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB;;;;;;OAMG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B;;;OAGG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CACxC,CAAC;AAEN,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,uDAAuD;IACvD,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CACxC;AAQD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,WAAW,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"deploy-flags.d.ts","sourceRoot":"","sources":["../../src/commands/deploy-flags.ts"],"names":[],"mappings":"AA0BA,OAAO,EAAa,KAAK,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtD,MAAM,MAAM,UAAU,GAClB;IACE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,oEAAoE;IACpE,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B;uEACmE;IACnE,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC;gEAC4D;IAC5D,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3C,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE;AACrD;;;;;;;GAOG;GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE;AACpF;;;;;;;;;;GAUG;GACD;IACE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB;;;;;;OAMG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B;;;OAGG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CACxC,CAAC;AAEN,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,uDAAuD;IACvD,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CACxC;AAQD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,WAAW,GAAG,MAAM,CAqX9E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAyDA,OAAO,EAGL,4BAA4B,EAC7B,MAAM,uBAAuB,CAAC;AAM/B,OAAO,EAAa,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACnF,OAAO,KAAK,EAAS,OAAO,EAAY,MAAM,kBAAkB,CAAC;AAKjE,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,CAAC;AAEjD,eAAO,MAAM,aAAa,EAAE,OAI3B,CAAC;AAEF,UAAU,UAAU;IAClB,6BAA6B;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAmQD;;;;;;GAMG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAoBpC;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,GAAG,IAAI,CAwBpE;AA+KD,2BAA2B;AAC3B,OAAO,EAAE,4BAA4B,EAAE,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export type VerifyAppType = "static" | "react" | "react+api" | "container";
|
|
2
|
+
export type VerifyVerdict = {
|
|
3
|
+
readonly kind: "match";
|
|
4
|
+
} | {
|
|
5
|
+
readonly kind: "mismatch";
|
|
6
|
+
readonly servedHash: string;
|
|
7
|
+
readonly shippedHash: string;
|
|
8
|
+
readonly servedLen: number;
|
|
9
|
+
readonly shippedLen: number;
|
|
10
|
+
} | {
|
|
11
|
+
readonly kind: "unreachable";
|
|
12
|
+
readonly status?: number;
|
|
13
|
+
readonly reason: string;
|
|
14
|
+
};
|
|
15
|
+
export interface VerifyDeps {
|
|
16
|
+
readonly fetch: typeof fetch;
|
|
17
|
+
}
|
|
18
|
+
export interface VerifyArgs {
|
|
19
|
+
/** The served deployment URL to fetch (the public `<id>.pages.dev`). */
|
|
20
|
+
readonly servedUrl: string;
|
|
21
|
+
/** The locally-shipped entrypoint (index.html) bytes or text. */
|
|
22
|
+
readonly shippedEntrypoint: Uint8Array | string;
|
|
23
|
+
readonly appType: VerifyAppType;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Fetch the served entrypoint and compare to what was shipped. Never
|
|
27
|
+
* throws — a network failure or non-2xx becomes an `unreachable`
|
|
28
|
+
* verdict so the caller decides how to report it.
|
|
29
|
+
*/
|
|
30
|
+
export declare function verifyServed(args: VerifyArgs, deps?: VerifyDeps): Promise<VerifyVerdict>;
|
|
31
|
+
//# sourceMappingURL=served-verify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"served-verify.d.ts","sourceRoot":"","sources":["../../src/deploy/served-verify.ts"],"names":[],"mappings":"AAmBA,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,OAAO,GAAG,WAAW,GAAG,WAAW,CAAC;AAE3E,MAAM,MAAM,aAAa,GACrB;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAC1B;IACE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAExF,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,KAAK,CAAC;CAC9B;AAED,MAAM,WAAW,UAAU;IACzB,wEAAwE;IACxE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,iEAAiE;IACjE,QAAQ,CAAC,iBAAiB,EAAE,UAAU,GAAG,MAAM,CAAC;IAChD,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;CACjC;AAUD;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,UAAU,EAChB,IAAI,GAAE,UAAsB,GAC3B,OAAO,CAAC,aAAa,CAAC,CA8CxB"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { CliIo, ExitCode } from "../dispatcher.js";
|
|
2
|
+
import { type SettleOutcome, type WaitArgs, type WaitDeps } from "./wait-for-deployment.js";
|
|
3
|
+
import { type VerifyVerdict, type VerifyArgs, type VerifyDeps, type VerifyAppType } from "./served-verify.js";
|
|
4
|
+
import type { CliConfig } from "../config.js";
|
|
5
|
+
/** EX_DATAERR — the live deployment serves content that doesn't match what shipped. */
|
|
6
|
+
export declare const VERIFY_MISMATCH_EXIT = 65;
|
|
7
|
+
export interface VerifyDeployArgs {
|
|
8
|
+
readonly slug: string;
|
|
9
|
+
readonly cwd: string;
|
|
10
|
+
readonly appType: VerifyAppType;
|
|
11
|
+
readonly timeoutMs: number;
|
|
12
|
+
}
|
|
13
|
+
export interface VerifyDeployDeps {
|
|
14
|
+
readonly wait: (args: WaitArgs, deps: WaitDeps) => Promise<SettleOutcome>;
|
|
15
|
+
readonly verify: (args: VerifyArgs, deps: VerifyDeps) => Promise<VerifyVerdict>;
|
|
16
|
+
/** Read the local shipped entrypoint (root `index.html`, else `static/index.html`). */
|
|
17
|
+
readonly readLocalEntrypoint: (cwd: string) => Uint8Array | null;
|
|
18
|
+
readonly waitDeps: WaitDeps;
|
|
19
|
+
readonly verifyDeps: VerifyDeps;
|
|
20
|
+
}
|
|
21
|
+
/** Real entrypoint reader: prefer root `index.html`, fall back to `static/index.html`. */
|
|
22
|
+
export declare function readLocalEntrypoint(cwd: string): Uint8Array | null;
|
|
23
|
+
export declare function realVerifyDeployDeps(cfg: CliConfig): VerifyDeployDeps;
|
|
24
|
+
export declare function runDeployVerification(args: VerifyDeployArgs, io: CliIo, deps: VerifyDeployDeps): Promise<ExitCode>;
|
|
25
|
+
//# sourceMappingURL=verify-deploy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify-deploy.d.ts","sourceRoot":"","sources":["../../src/deploy/verify-deploy.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,QAAQ,EACb,KAAK,QAAQ,EACd,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,aAAa,EACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,uFAAuF;AACvF,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;IAC1E,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;IAChF,uFAAuF;IACvF,QAAQ,CAAC,mBAAmB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,UAAU,GAAG,IAAI,CAAC;IACjE,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;CACjC;AAED,0FAA0F;AAC1F,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAMlE;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,SAAS,GAAG,gBAAgB,CAQrE;AAED,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,gBAAgB,EACtB,EAAE,EAAE,KAAK,EACT,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,QAAQ,CAAC,CA4BnB"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { CliConfig } from "../config.js";
|
|
2
|
+
import { type DeploymentStatusView } from "./deployment-status.js";
|
|
3
|
+
export type SettleOutcome = {
|
|
4
|
+
readonly kind: "success";
|
|
5
|
+
readonly deploymentUrl: string | null;
|
|
6
|
+
} | {
|
|
7
|
+
readonly kind: "failure";
|
|
8
|
+
readonly logExcerpt?: string | undefined;
|
|
9
|
+
readonly failedStage?: string | undefined;
|
|
10
|
+
} | {
|
|
11
|
+
readonly kind: "pending";
|
|
12
|
+
} | {
|
|
13
|
+
readonly kind: "unsupported";
|
|
14
|
+
} | {
|
|
15
|
+
readonly kind: "unknown";
|
|
16
|
+
};
|
|
17
|
+
export interface WaitDeps {
|
|
18
|
+
/** Read the live deployment status (defaults to the real bot call). */
|
|
19
|
+
readonly fetchStatus: (slug: string) => Promise<DeploymentStatusView | null>;
|
|
20
|
+
/** Sleep between polls (injected so tests don't actually wait). */
|
|
21
|
+
readonly sleep: (ms: number) => Promise<void>;
|
|
22
|
+
/** Monotonic clock in ms (injected so tests control elapsed time). */
|
|
23
|
+
readonly now: () => number;
|
|
24
|
+
}
|
|
25
|
+
export interface WaitArgs {
|
|
26
|
+
readonly slug: string;
|
|
27
|
+
readonly timeoutMs: number;
|
|
28
|
+
readonly pollIntervalMs?: number;
|
|
29
|
+
}
|
|
30
|
+
export declare function realWaitDeps(cfg: CliConfig): WaitDeps;
|
|
31
|
+
/**
|
|
32
|
+
* Poll until the Pages build settles or the timeout elapses. Never
|
|
33
|
+
* throws on a transient status read; an old bot (null) degrades to
|
|
34
|
+
* `unknown`, a non-Pages app to `unsupported`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function waitForDeployment(args: WaitArgs, deps: WaitDeps): Promise<SettleOutcome>;
|
|
37
|
+
//# sourceMappingURL=wait-for-deployment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wait-for-deployment.d.ts","sourceRoot":"","sources":["../../src/deploy/wait-for-deployment.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAyB,KAAK,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAE1F,MAAM,MAAM,aAAa,GACrB;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACnE;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GACjH;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GAC5B;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;CAAE,GAChC;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAEjC,MAAM,WAAW,QAAQ;IACvB,uEAAuE;IACvE,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;IAC7E,mEAAmE;IACnE,QAAQ,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,sEAAsE;IACtE,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAID,wBAAgB,YAAY,CAAC,GAAG,EAAE,SAAS,GAAG,QAAQ,CAMrD;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,aAAa,CAAC,CA+B9F"}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "0.27.
|
|
1
|
+
export declare const CLI_VERSION = "0.27.5";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: launchpad-content-pr
|
|
3
3
|
description: Push a content change to a Launchpad app via `launchpad deploy` and verify it shipped via `launchpad status`. Covers the post-first-deploy iteration loop (edit → deploy → verify) — subsequent deploys commit directly to the app repo's main and the Pages build runs asynchronously, so verification is its own step. Use when someone says "push a content change", "ship an update", "/launchpad-content-pr", "verify my deploy", or after `/launchpad-deploy` reports `done` and they want to follow up with an edit.
|
|
4
|
-
version: 0.27.
|
|
4
|
+
version: 0.27.5
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: launchpad-deploy
|
|
3
3
|
description: Walk a Launchpad user through deploying an app from their local working directory (Model A — `launchpad init` + `launchpad deploy`). Wraps the CLI verbs end-to-end: detects the app shape, scaffolds `launchpad.yaml`, resolves the allowed Entra group via `launchpad groups`, bundles the CWD via `launchpad deploy`, and watches the rollout via `launchpad status`. Use when someone says "deploy a new app", "ship my app to Launchpad", "/launchpad-deploy", "I have an app locally — get it on Launchpad", or any variant. Resume/abandon for legacy in-flight provisioning is at the bottom.
|
|
4
|
-
version: 0.27.
|
|
4
|
+
version: 0.27.5
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: launchpad-deploy-status
|
|
3
3
|
description: Show the current provisioning stage + failure reason for a Launchpad app via `launchpad status` (Model A drift + deployment_verified) and `launchpad apps` (lifecycle bucket). Renders the M-892 stage trace for in-flight provisioning, and is the canonical home for `launchpad recover` (repair a terminal-failed app record that is actually live). Use when someone says "what's the status of demo-X", "/launchpad-deploy-status", "is my deploy stuck", "my app says failed but it's serving", or after `/launchpad-deploy` reports a non-`done` terminal stage.
|
|
4
|
-
version: 0.27.
|
|
4
|
+
version: 0.27.5
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: launchpad-destroy
|
|
3
3
|
description: Tear down a Launchpad app end-to-end via `launchpad destroy` — Cloudflare Pages project, edge-auth wiring (gateway KV/audience entries, or the Access app for `auth: access` apps), custom hostname, platform-repo TF, and the app repo (archive-renamed). Owner-only verb with a two-step destructive confirmation. Use when someone says "destroy this app", "/launchpad-destroy", "tear down `<slug>`", "delete the app", or asks to clean up a smoke-test / orphan / retired app.
|
|
4
|
-
version: 0.27.
|
|
4
|
+
version: 0.27.5
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: launchpad-onboard
|
|
3
3
|
description: One-time setup for the Launchpad CLI + Claude Code skill bundle. Verifies the `launchpad` CLI is installed and current, runs `launchpad whoami` to confirm the session is fresh, and checks the bundled skills are installed and in lock-step with the CLI. Idempotent — safe to re-run any time. Use when someone says "set me up for Launchpad", "I just got a new machine and want to use Launchpad", "/launchpad-onboard", or any of the other launchpad-* skills fails on a prereq check.
|
|
4
|
-
version: 0.27.
|
|
4
|
+
version: 0.27.5
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: launchpad-status
|
|
3
3
|
description: Show whether a Launchpad app's local launchpad.yaml matches what's deployed, and read the deployed manifest. Wraps `launchpad pull` (fetch deployed YAML) and `launchpad status` (drift report). Use when someone says "is my app in sync", "what's deployed", "show drift", "/launchpad-status", "/launchpad-pull", or after `launchpad deploy` to verify the change landed.
|
|
4
|
-
version: 0.27.
|
|
4
|
+
version: 0.27.5
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|