@m-kopa/launchpad-cli 0.54.0 → 0.54.1
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/dist/cli.js +118 -74
- package/dist/deploy/git-files.d.ts +7 -11
- package/dist/deploy/git-files.d.ts.map +1 -1
- 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-identity/SKILL.md +1 -1
- package/skills/launchpad-onboard/SKILL.md +1 -1
- package/skills/launchpad-report/SKILL.md +1 -1
- package/skills/launchpad-status/SKILL.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,14 @@ pre-1.0 minor bumps may carry breaking changes per ADR 0005.
|
|
|
8
8
|
|
|
9
9
|
## Unreleased
|
|
10
10
|
|
|
11
|
+
## 0.54.1 - 2026-07-29
|
|
12
|
+
|
|
13
|
+
**Deleted application files now stay deleted after deployment.**
|
|
14
|
+
|
|
15
|
+
- The legacy clone deploy path excludes tracked files that no longer exist in the working tree, allowing the server to derive their deletion from the complete upload.
|
|
16
|
+
- The platform applies updates and proved app-owned deletions in one Git tree while preserving governance, workflow, ownership, Wrangler, Launchpad metadata, and out-of-boundary files.
|
|
17
|
+
- The server uses exact managed-tree enumeration before absence can authorize deletion and returns a retryable failure when that proof is unavailable.
|
|
18
|
+
|
|
11
19
|
## 0.54.0 - 2026-07-19
|
|
12
20
|
|
|
13
21
|
**Server-visible CLI compatibility, with dormant minimum-version enforcement.**
|
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.54.
|
|
22
|
+
var CLI_VERSION = "0.54.1";
|
|
23
23
|
|
|
24
24
|
// src/config.ts
|
|
25
25
|
import * as os from "node:os";
|
|
@@ -1867,7 +1867,7 @@ function describe10(e) {
|
|
|
1867
1867
|
}
|
|
1868
1868
|
|
|
1869
1869
|
// src/commands/deploy.ts
|
|
1870
|
-
import { existsSync as
|
|
1870
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
1871
1871
|
import * as path8 from "node:path";
|
|
1872
1872
|
|
|
1873
1873
|
// src/bundle/orchestrate.ts
|
|
@@ -2657,6 +2657,7 @@ var ManifestSchema = z.object({
|
|
|
2657
2657
|
app: AppBoundarySchema.optional(),
|
|
2658
2658
|
verify: VerifySchema.optional(),
|
|
2659
2659
|
confine_origin: z.boolean().optional(),
|
|
2660
|
+
origin: z.enum(["pages", "dispatch"]).optional(),
|
|
2660
2661
|
catalogue: z.object({ listed: z.boolean().default(true) }).strict().optional(),
|
|
2661
2662
|
sensitive: z.boolean().optional()
|
|
2662
2663
|
}).strict().superRefine((m, ctx) => {
|
|
@@ -3047,7 +3048,9 @@ var PLATFORM_SEEDED_GITHUB_PATHS = new Set([
|
|
|
3047
3048
|
".github/scripts/parse-tsc.mjs",
|
|
3048
3049
|
".github/scripts/parse-vitest.mjs",
|
|
3049
3050
|
".github/scripts/parse-gitleaks.mjs",
|
|
3050
|
-
".github/scripts/parse-bun-audit.mjs"
|
|
3051
|
+
".github/scripts/parse-bun-audit.mjs",
|
|
3052
|
+
".github/scripts/parse-html-validate.mjs",
|
|
3053
|
+
".github/scripts/parse-hadolint.mjs"
|
|
3051
3054
|
]);
|
|
3052
3055
|
function checkDenyList(path7) {
|
|
3053
3056
|
const segments = path7.split("/");
|
|
@@ -3290,6 +3293,7 @@ var SpecSchema = z2.object({
|
|
|
3290
3293
|
app: AppBoundarySchema.optional(),
|
|
3291
3294
|
verify: VerifySchema.optional(),
|
|
3292
3295
|
confine_origin: z2.boolean().optional(),
|
|
3296
|
+
origin: z2.enum(["pages", "dispatch"]).optional(),
|
|
3293
3297
|
sensitive: z2.boolean().optional(),
|
|
3294
3298
|
catalogue: z2.object({ listed: z2.boolean().default(true) }).strict().optional(),
|
|
3295
3299
|
env_vars: z2.record(z2.string().regex(ENV_NAME_REGEX, "env_vars keys must be UPPER_SNAKE_CASE"), EnvVarSchema).optional(),
|
|
@@ -3335,6 +3339,43 @@ var SpecSchema = z2.object({
|
|
|
3335
3339
|
});
|
|
3336
3340
|
}
|
|
3337
3341
|
}
|
|
3342
|
+
if (s.origin === "dispatch") {
|
|
3343
|
+
if (isContainer) {
|
|
3344
|
+
ctx.addIssue({
|
|
3345
|
+
code: z2.ZodIssueCode.custom,
|
|
3346
|
+
path: ["origin"],
|
|
3347
|
+
message: "origin: dispatch is not available for container apps — they run on the container runtime (ADR 0015/0016), not in a Workers dispatch namespace."
|
|
3348
|
+
});
|
|
3349
|
+
}
|
|
3350
|
+
if ((s.auth ?? "access") !== "gateway") {
|
|
3351
|
+
ctx.addIssue({
|
|
3352
|
+
code: z2.ZodIssueCode.custom,
|
|
3353
|
+
path: ["origin"],
|
|
3354
|
+
message: "origin: dispatch requires an explicit auth: gateway (auth defaults to 'access' when omitted). A dispatch-namespace Worker has no public hostname for Cloudflare Access to gate, so the gateway must be the sole gate."
|
|
3355
|
+
});
|
|
3356
|
+
}
|
|
3357
|
+
if (s.confine_origin !== undefined) {
|
|
3358
|
+
ctx.addIssue({
|
|
3359
|
+
code: z2.ZodIssueCode.custom,
|
|
3360
|
+
path: ["confine_origin"],
|
|
3361
|
+
message: "confine_origin has no meaning with origin: dispatch — there is no public pages.dev apex to confine. Remove it."
|
|
3362
|
+
});
|
|
3363
|
+
}
|
|
3364
|
+
if (s.env_vars !== undefined) {
|
|
3365
|
+
ctx.addIssue({
|
|
3366
|
+
code: z2.ZodIssueCode.custom,
|
|
3367
|
+
path: ["env_vars"],
|
|
3368
|
+
message: "env_vars are not yet supported with origin: dispatch — a dispatch workspace has no Pages module to apply them, and accepting them here would silently drop them. Worker-level vars land with the v2 deploy path."
|
|
3369
|
+
});
|
|
3370
|
+
}
|
|
3371
|
+
if (s.build !== undefined) {
|
|
3372
|
+
ctx.addIssue({
|
|
3373
|
+
code: z2.ZodIssueCode.custom,
|
|
3374
|
+
path: ["build"],
|
|
3375
|
+
message: "build is not yet supported with origin: dispatch — a dispatch workspace has no Pages module to carry it, and the v2 deploy path that will consume it is not shipped yet. Accepting it here would silently drop it."
|
|
3376
|
+
});
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3338
3379
|
if (!isContainer && s.containers !== undefined) {
|
|
3339
3380
|
ctx.addIssue({
|
|
3340
3381
|
code: z2.ZodIssueCode.custom,
|
|
@@ -6185,6 +6226,8 @@ import { readFileSync as readFileSync10 } from "node:fs";
|
|
|
6185
6226
|
|
|
6186
6227
|
// src/deploy/git-files.ts
|
|
6187
6228
|
import { spawn as spawn3 } from "node:child_process";
|
|
6229
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
6230
|
+
import { resolve as resolve7 } from "node:path";
|
|
6188
6231
|
|
|
6189
6232
|
class GitFilesError extends Error {
|
|
6190
6233
|
code = "git_files_error";
|
|
@@ -6202,6 +6245,7 @@ var PROTECTED_PATHS = new Set([
|
|
|
6202
6245
|
]);
|
|
6203
6246
|
async function listDeployFiles(opts) {
|
|
6204
6247
|
const sp = opts.spawner ?? spawn3;
|
|
6248
|
+
const exists = opts.exists ?? existsSync5;
|
|
6205
6249
|
const stdout = await runGit2(sp, opts.cwd, [
|
|
6206
6250
|
"ls-files",
|
|
6207
6251
|
"-c",
|
|
@@ -6209,11 +6253,11 @@ async function listDeployFiles(opts) {
|
|
|
6209
6253
|
"--exclude-standard",
|
|
6210
6254
|
"-z"
|
|
6211
6255
|
]);
|
|
6212
|
-
const paths = stdout.split("\x00").filter((p) => p.length > 0 && !PROTECTED_PATHS.has(p));
|
|
6256
|
+
const paths = stdout.split("\x00").filter((p) => p.length > 0 && exists(resolve7(opts.cwd, p)) && !PROTECTED_PATHS.has(p));
|
|
6213
6257
|
return [...paths].sort();
|
|
6214
6258
|
}
|
|
6215
6259
|
function runGit2(sp, cwd, args) {
|
|
6216
|
-
return new Promise((
|
|
6260
|
+
return new Promise((resolve8, reject) => {
|
|
6217
6261
|
const spawnOpts = { cwd, stdio: ["ignore", "pipe", "pipe"] };
|
|
6218
6262
|
const child = sp("git", [...args], spawnOpts);
|
|
6219
6263
|
let stdout = "";
|
|
@@ -6229,7 +6273,7 @@ function runGit2(sp, cwd, args) {
|
|
|
6229
6273
|
});
|
|
6230
6274
|
child.once("close", (code) => {
|
|
6231
6275
|
if (code === 0) {
|
|
6232
|
-
|
|
6276
|
+
resolve8(stdout);
|
|
6233
6277
|
return;
|
|
6234
6278
|
}
|
|
6235
6279
|
reject(new GitFilesError(`\`git ${args.join(" ")}\` exited ${code}: ${stderr.trim()}`));
|
|
@@ -6806,7 +6850,7 @@ function parseDeployFlags(args) {
|
|
|
6806
6850
|
}
|
|
6807
6851
|
|
|
6808
6852
|
// src/deploy/apply.ts
|
|
6809
|
-
import { existsSync as
|
|
6853
|
+
import { existsSync as existsSync6, rmSync } from "node:fs";
|
|
6810
6854
|
import { resolve as resolvePath, join as join10 } from "node:path";
|
|
6811
6855
|
|
|
6812
6856
|
// src/manifest/load.ts
|
|
@@ -7173,7 +7217,7 @@ function sleep(ms) {
|
|
|
7173
7217
|
}
|
|
7174
7218
|
function deletePinIfPresent(cfg, slug, io) {
|
|
7175
7219
|
const pinPath = join10(cfg.stateDir, slug, "group.json");
|
|
7176
|
-
if (!
|
|
7220
|
+
if (!existsSync6(pinPath))
|
|
7177
7221
|
return;
|
|
7178
7222
|
try {
|
|
7179
7223
|
rmSync(pinPath, { force: true });
|
|
@@ -7311,12 +7355,12 @@ async function handleAccessDrift(params) {
|
|
|
7311
7355
|
|
|
7312
7356
|
// src/deploy/dry-run.ts
|
|
7313
7357
|
import { execFileSync } from "node:child_process";
|
|
7314
|
-
import { resolve as
|
|
7358
|
+
import { resolve as resolve8 } from "node:path";
|
|
7315
7359
|
var MANIFEST_SHA_REGEX2 = /^[0-9a-f]{40}$/;
|
|
7316
7360
|
async function runDeployDryRun(opts, io, deps = {}) {
|
|
7317
7361
|
const cfg = deps.config ?? loadConfig();
|
|
7318
7362
|
const fetcher = deps.fetcher ?? fetch;
|
|
7319
|
-
const manifestPath =
|
|
7363
|
+
const manifestPath = resolve8(process.cwd(), opts.file ?? "launchpad.yaml");
|
|
7320
7364
|
const loaded = loadManifest(manifestPath);
|
|
7321
7365
|
if (loaded.kind !== "ok") {
|
|
7322
7366
|
return renderManifestError2(loaded, opts.json, io);
|
|
@@ -7726,7 +7770,7 @@ async function runDeploy(args, io) {
|
|
|
7726
7770
|
}
|
|
7727
7771
|
const cwd = process.cwd();
|
|
7728
7772
|
const manifestPath = path8.join(cwd, "launchpad.yaml");
|
|
7729
|
-
if (
|
|
7773
|
+
if (existsSync7(manifestPath)) {
|
|
7730
7774
|
const contentMode = flags.mode.kind === "content" ? flags.mode : {
|
|
7731
7775
|
allowStale: false,
|
|
7732
7776
|
rebaseOntoHead: false,
|
|
@@ -7780,7 +7824,7 @@ async function runDeploy(args, io) {
|
|
|
7780
7824
|
}
|
|
7781
7825
|
let contentManifestYaml = null;
|
|
7782
7826
|
const contentManifestPath = path8.join(process.cwd(), "launchpad.yaml");
|
|
7783
|
-
if (
|
|
7827
|
+
if (existsSync7(contentManifestPath)) {
|
|
7784
7828
|
try {
|
|
7785
7829
|
contentManifestYaml = readFileSync10(contentManifestPath, "utf8");
|
|
7786
7830
|
} catch {
|
|
@@ -8003,11 +8047,11 @@ function surfaceStaleBlock(body, io) {
|
|
|
8003
8047
|
}
|
|
8004
8048
|
var MANIFEST_DIFF_READ_TIMEOUT_MS = 5000;
|
|
8005
8049
|
function withTimeout(p, ms) {
|
|
8006
|
-
return new Promise((
|
|
8050
|
+
return new Promise((resolve9, reject) => {
|
|
8007
8051
|
const timer = setTimeout(() => reject(new Error(`manifest read timed out after ${ms}ms`)), ms);
|
|
8008
8052
|
p.then((v) => {
|
|
8009
8053
|
clearTimeout(timer);
|
|
8010
|
-
|
|
8054
|
+
resolve9(v);
|
|
8011
8055
|
}, (e) => {
|
|
8012
8056
|
clearTimeout(timer);
|
|
8013
8057
|
reject(e instanceof Error ? e : new Error(String(e)));
|
|
@@ -8366,7 +8410,7 @@ function pendingReconcileOutcome(body) {
|
|
|
8366
8410
|
}
|
|
8367
8411
|
|
|
8368
8412
|
// src/commands/redeploy.ts
|
|
8369
|
-
import { existsSync as
|
|
8413
|
+
import { existsSync as existsSync8, readFileSync as readFileSync11 } from "node:fs";
|
|
8370
8414
|
import { resolve as resolvePath2 } from "node:path";
|
|
8371
8415
|
import { parse as parseYaml7 } from "yaml";
|
|
8372
8416
|
var SLUG_RE6 = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
|
|
@@ -8444,7 +8488,7 @@ async function runRedeploy(opts, io, deps = {}) {
|
|
|
8444
8488
|
}
|
|
8445
8489
|
function readLocalAppType(cwd, file) {
|
|
8446
8490
|
const manifestPath = resolvePath2(cwd, file ?? "launchpad.yaml");
|
|
8447
|
-
if (!
|
|
8491
|
+
if (!existsSync8(manifestPath))
|
|
8448
8492
|
return null;
|
|
8449
8493
|
try {
|
|
8450
8494
|
const obj = parseYaml7(readFileSync11(manifestPath, "utf8"));
|
|
@@ -8732,7 +8776,7 @@ function describe16(e) {
|
|
|
8732
8776
|
|
|
8733
8777
|
// src/commands/generate.ts
|
|
8734
8778
|
import { mkdirSync as mkdirSync2, readFileSync as readFileSync12, writeFileSync as writeFileSync2 } from "node:fs";
|
|
8735
|
-
import { dirname as dirname6, resolve as
|
|
8779
|
+
import { dirname as dirname6, resolve as resolve9, relative as relative3 } from "node:path";
|
|
8736
8780
|
var generateCommand = {
|
|
8737
8781
|
name: "generate",
|
|
8738
8782
|
summary: "emit derived artefacts (wrangler.toml, deploy.yml) from launchpad.yaml",
|
|
@@ -8745,14 +8789,14 @@ async function runGenerate(args, io) {
|
|
|
8745
8789
|
io.err("Usage: launchpad generate [--file <path>] [--dry-run] [--force] [--json]");
|
|
8746
8790
|
return 64;
|
|
8747
8791
|
}
|
|
8748
|
-
const manifestPath =
|
|
8792
|
+
const manifestPath = resolve9(process.cwd(), flags.file ?? "launchpad.yaml");
|
|
8749
8793
|
const result = loadManifest(manifestPath);
|
|
8750
8794
|
if (result.kind !== "ok") {
|
|
8751
8795
|
return flags.json ? renderManifestErrorJson(result, io) : renderManifestErrorHuman(result, io);
|
|
8752
8796
|
}
|
|
8753
8797
|
const appRoot = dirname6(manifestPath);
|
|
8754
|
-
const wranglerPath =
|
|
8755
|
-
const workflowPath =
|
|
8798
|
+
const wranglerPath = resolve9(appRoot, "container", "wrangler.toml");
|
|
8799
|
+
const workflowPath = resolve9(appRoot, ".github", "workflows", "deploy.yml");
|
|
8756
8800
|
const wranglerOut = generateWranglerToml(result.manifest);
|
|
8757
8801
|
const workflowOut = generateGithubDeployWorkflow(result.manifest);
|
|
8758
8802
|
if (flags.dryRun) {
|
|
@@ -9557,17 +9601,17 @@ function describe19(e) {
|
|
|
9557
9601
|
|
|
9558
9602
|
// src/commands/init.ts
|
|
9559
9603
|
import { createInterface } from "node:readline/promises";
|
|
9560
|
-
import { existsSync as
|
|
9561
|
-
import { dirname as dirname7, resolve as
|
|
9604
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync14, writeFileSync as writeFileSync3 } from "node:fs";
|
|
9605
|
+
import { dirname as dirname7, resolve as resolve10 } from "node:path";
|
|
9562
9606
|
import { stringify as yamlStringify } from "yaml";
|
|
9563
9607
|
|
|
9564
9608
|
// src/detect/index.ts
|
|
9565
|
-
import { existsSync as
|
|
9609
|
+
import { existsSync as existsSync9, readFileSync as readFileSync13, statSync } from "node:fs";
|
|
9566
9610
|
import { join as join12 } from "node:path";
|
|
9567
9611
|
function detectAppShape(cwd) {
|
|
9568
|
-
const hasPackageJson =
|
|
9569
|
-
const hasAnyLockfile = LOCKFILES.some(({ file }) =>
|
|
9570
|
-
const hasViteConfig = VITE_CONFIG_NAMES.some((n) =>
|
|
9612
|
+
const hasPackageJson = existsSync9(join12(cwd, "package.json"));
|
|
9613
|
+
const hasAnyLockfile = LOCKFILES.some(({ file }) => existsSync9(join12(cwd, file)));
|
|
9614
|
+
const hasViteConfig = VITE_CONFIG_NAMES.some((n) => existsSync9(join12(cwd, n)));
|
|
9571
9615
|
if (!hasPackageJson && !hasAnyLockfile && !hasViteConfig) {
|
|
9572
9616
|
return {
|
|
9573
9617
|
kind: "not-applicable",
|
|
@@ -9613,7 +9657,7 @@ var LOCKFILES = [
|
|
|
9613
9657
|
{ file: "yarn.lock", pm: "yarn" }
|
|
9614
9658
|
];
|
|
9615
9659
|
function detectPackageManager(cwd) {
|
|
9616
|
-
const present = LOCKFILES.filter(({ file }) =>
|
|
9660
|
+
const present = LOCKFILES.filter(({ file }) => existsSync9(join12(cwd, file)));
|
|
9617
9661
|
if (present.length === 0) {
|
|
9618
9662
|
return {
|
|
9619
9663
|
kind: "ambiguous",
|
|
@@ -9636,7 +9680,7 @@ function detectPackageManager(cwd) {
|
|
|
9636
9680
|
function detectVitePresence(cwd) {
|
|
9637
9681
|
for (const name of VITE_CONFIG_NAMES) {
|
|
9638
9682
|
const p = join12(cwd, name);
|
|
9639
|
-
if (
|
|
9683
|
+
if (existsSync9(p)) {
|
|
9640
9684
|
return { kind: "ok", value: { path: p } };
|
|
9641
9685
|
}
|
|
9642
9686
|
}
|
|
@@ -9648,7 +9692,7 @@ function detectVitePresence(cwd) {
|
|
|
9648
9692
|
function detectAppType(cwd) {
|
|
9649
9693
|
const fnDir = join12(cwd, "functions");
|
|
9650
9694
|
let hasFunctionsDir = false;
|
|
9651
|
-
if (
|
|
9695
|
+
if (existsSync9(fnDir)) {
|
|
9652
9696
|
try {
|
|
9653
9697
|
hasFunctionsDir = statSync(fnDir).isDirectory();
|
|
9654
9698
|
} catch {
|
|
@@ -9659,7 +9703,7 @@ function detectAppType(cwd) {
|
|
|
9659
9703
|
}
|
|
9660
9704
|
function detectBuildCommand(cwd, pm) {
|
|
9661
9705
|
const pkgJsonPath = join12(cwd, "package.json");
|
|
9662
|
-
if (!
|
|
9706
|
+
if (!existsSync9(pkgJsonPath)) {
|
|
9663
9707
|
return {
|
|
9664
9708
|
kind: "ambiguous",
|
|
9665
9709
|
reason: "no package.json at repo root. Run your package manager's `init` first."
|
|
@@ -9731,8 +9775,8 @@ async function runInit(args, io, prompt) {
|
|
|
9731
9775
|
return 64;
|
|
9732
9776
|
}
|
|
9733
9777
|
const { inputs, options } = parsed;
|
|
9734
|
-
const outPath =
|
|
9735
|
-
if (
|
|
9778
|
+
const outPath = resolve10(process.cwd(), options.out);
|
|
9779
|
+
if (existsSync10(outPath) && !options.force) {
|
|
9736
9780
|
io.err(`launchpad init: ${outPath} already exists`);
|
|
9737
9781
|
io.err("Pass --force to overwrite.");
|
|
9738
9782
|
return 64;
|
|
@@ -9779,7 +9823,7 @@ async function runInit(args, io, prompt) {
|
|
|
9779
9823
|
}
|
|
9780
9824
|
io.out(`✓ wrote ${outPath}`);
|
|
9781
9825
|
if (options.gitignore) {
|
|
9782
|
-
const gitignorePath =
|
|
9826
|
+
const gitignorePath = resolve10(process.cwd(), ".gitignore");
|
|
9783
9827
|
try {
|
|
9784
9828
|
const changed = ensureGitignoreEntries(gitignorePath, [".env", ".env.local"]);
|
|
9785
9829
|
if (changed.length > 0) {
|
|
@@ -9794,9 +9838,9 @@ async function runInit(args, io, prompt) {
|
|
|
9794
9838
|
}
|
|
9795
9839
|
}
|
|
9796
9840
|
if (fillResult.inputs.type === "static") {
|
|
9797
|
-
const serveDir =
|
|
9798
|
-
const indexPath =
|
|
9799
|
-
if (!
|
|
9841
|
+
const serveDir = resolve10(dirname7(outPath), STATIC_SERVE_DIR);
|
|
9842
|
+
const indexPath = resolve10(serveDir, "index.html");
|
|
9843
|
+
if (!existsSync10(indexPath)) {
|
|
9800
9844
|
try {
|
|
9801
9845
|
mkdirSync3(serveDir, { recursive: true });
|
|
9802
9846
|
writeFileSync3(indexPath, starterIndexHtml(fillResult.inputs.name), {
|
|
@@ -10188,7 +10232,7 @@ function renderYaml(manifest) {
|
|
|
10188
10232
|
}
|
|
10189
10233
|
function ensureGitignoreEntries(path10, entries) {
|
|
10190
10234
|
let current = "";
|
|
10191
|
-
if (
|
|
10235
|
+
if (existsSync10(path10)) {
|
|
10192
10236
|
current = readFileSync14(path10, "utf8");
|
|
10193
10237
|
}
|
|
10194
10238
|
const lines = current.split(/\r?\n/);
|
|
@@ -10717,7 +10761,7 @@ function describe23(e) {
|
|
|
10717
10761
|
}
|
|
10718
10762
|
|
|
10719
10763
|
// src/commands/plan.ts
|
|
10720
|
-
import { resolve as
|
|
10764
|
+
import { resolve as resolve11 } from "node:path";
|
|
10721
10765
|
var planCommand = {
|
|
10722
10766
|
name: "plan",
|
|
10723
10767
|
summary: "summarise what the manifest would deploy (offline)",
|
|
@@ -10730,7 +10774,7 @@ async function runPlan(args, io) {
|
|
|
10730
10774
|
io.err("Usage: launchpad plan [--file <path>] [--json]");
|
|
10731
10775
|
return 64;
|
|
10732
10776
|
}
|
|
10733
|
-
const manifestPath =
|
|
10777
|
+
const manifestPath = resolve11(process.cwd(), flags.file ?? "launchpad.yaml");
|
|
10734
10778
|
const result = loadManifest(manifestPath);
|
|
10735
10779
|
return flags.json ? renderJson(result, io) : renderHuman(result, io);
|
|
10736
10780
|
}
|
|
@@ -11320,12 +11364,12 @@ async function listFiles(root, relative5 = "") {
|
|
|
11320
11364
|
return files.sort();
|
|
11321
11365
|
}
|
|
11322
11366
|
function gitOutput(cwd, args) {
|
|
11323
|
-
return new Promise((
|
|
11367
|
+
return new Promise((resolve13, reject) => {
|
|
11324
11368
|
execFile("git", [...args], { cwd, encoding: "utf8" }, (error, stdout) => {
|
|
11325
11369
|
if (error !== null)
|
|
11326
11370
|
reject(error);
|
|
11327
11371
|
else
|
|
11328
|
-
|
|
11372
|
+
resolve13(stdout);
|
|
11329
11373
|
});
|
|
11330
11374
|
});
|
|
11331
11375
|
}
|
|
@@ -12260,7 +12304,7 @@ function describe26(e) {
|
|
|
12260
12304
|
}
|
|
12261
12305
|
|
|
12262
12306
|
// src/deploy/rollback.ts
|
|
12263
|
-
import { existsSync as
|
|
12307
|
+
import { existsSync as existsSync11, readFileSync as readFileSync15, writeFileSync as writeFileSync5 } from "node:fs";
|
|
12264
12308
|
import { resolve as resolvePath3 } from "node:path";
|
|
12265
12309
|
import { createInterface as createInterface3 } from "node:readline/promises";
|
|
12266
12310
|
|
|
@@ -12385,7 +12429,7 @@ async function runRollback(opts, io, deps = {}) {
|
|
|
12385
12429
|
}, io, applyDeps);
|
|
12386
12430
|
}
|
|
12387
12431
|
function readCurrentManifest(path14) {
|
|
12388
|
-
if (!
|
|
12432
|
+
if (!existsSync11(path14))
|
|
12389
12433
|
return null;
|
|
12390
12434
|
let raw;
|
|
12391
12435
|
try {
|
|
@@ -12545,7 +12589,7 @@ function parseArgs12(args) {
|
|
|
12545
12589
|
}
|
|
12546
12590
|
|
|
12547
12591
|
// src/secrets/push.ts
|
|
12548
|
-
import { existsSync as
|
|
12592
|
+
import { existsSync as existsSync12, readFileSync as readFileSync16 } from "node:fs";
|
|
12549
12593
|
import { resolve as resolvePath4 } from "node:path";
|
|
12550
12594
|
import { createInterface as createInterface4 } from "node:readline/promises";
|
|
12551
12595
|
import { stdin as input2, stdout as output2 } from "node:process";
|
|
@@ -12602,7 +12646,7 @@ async function runSecretsPush(opts, io, deps = {}) {
|
|
|
12602
12646
|
return 0;
|
|
12603
12647
|
}
|
|
12604
12648
|
const envPath = resolvePath4(process.cwd(), opts.env ?? ".env");
|
|
12605
|
-
if (!
|
|
12649
|
+
if (!existsSync12(envPath)) {
|
|
12606
12650
|
io.err(`launchpad secrets push: ${envPath}`);
|
|
12607
12651
|
io.err(" .env file not found. Run `launchpad secrets template` to scaffold one.");
|
|
12608
12652
|
return 2;
|
|
@@ -12934,7 +12978,7 @@ function renderManifestError5(result, io) {
|
|
|
12934
12978
|
}
|
|
12935
12979
|
|
|
12936
12980
|
// src/secrets/set.ts
|
|
12937
|
-
import { existsSync as
|
|
12981
|
+
import { existsSync as existsSync13, readFileSync as readFileSync17 } from "node:fs";
|
|
12938
12982
|
import { resolve as resolvePath6 } from "node:path";
|
|
12939
12983
|
import { parse as parseYaml8 } from "yaml";
|
|
12940
12984
|
var CELL_LABEL2 = {
|
|
@@ -12944,7 +12988,7 @@ var CELL_LABEL2 = {
|
|
|
12944
12988
|
};
|
|
12945
12989
|
function loadSet(fleetFile, setName, io) {
|
|
12946
12990
|
const path14 = resolvePath6(process.cwd(), fleetFile ?? "fleet-secret-sets.yaml");
|
|
12947
|
-
if (!
|
|
12991
|
+
if (!existsSync13(path14)) {
|
|
12948
12992
|
io.err(`✗ ${path14}`);
|
|
12949
12993
|
io.err(" fleet-secret-sets.yaml not found. Run from the platform repo root or pass --fleet-file.");
|
|
12950
12994
|
return 2;
|
|
@@ -13036,7 +13080,7 @@ async function runSecretsPushSet(opts, io, deps = {}) {
|
|
|
13036
13080
|
if (typeof set === "number")
|
|
13037
13081
|
return set;
|
|
13038
13082
|
const envPath = resolvePath6(process.cwd(), opts.env ?? ".env");
|
|
13039
|
-
if (!
|
|
13083
|
+
if (!existsSync13(envPath)) {
|
|
13040
13084
|
io.err(`launchpad secrets push --set: ${envPath} not found.`);
|
|
13041
13085
|
io.err(` Create a .env carrying the set's secrets: ${set.secrets.join(", ")}`);
|
|
13042
13086
|
return 2;
|
|
@@ -13187,8 +13231,8 @@ function setPushExit(e) {
|
|
|
13187
13231
|
}
|
|
13188
13232
|
|
|
13189
13233
|
// src/commands/secrets-template.ts
|
|
13190
|
-
import { existsSync as
|
|
13191
|
-
import { resolve as
|
|
13234
|
+
import { existsSync as existsSync14, writeFileSync as writeFileSync6 } from "node:fs";
|
|
13235
|
+
import { resolve as resolve13 } from "node:path";
|
|
13192
13236
|
async function runSecretsTemplate(args, io) {
|
|
13193
13237
|
const flags = parseFlags4(args);
|
|
13194
13238
|
if (flags.kind === "usage-error") {
|
|
@@ -13196,7 +13240,7 @@ async function runSecretsTemplate(args, io) {
|
|
|
13196
13240
|
io.err("Usage: launchpad secrets template [--file <path>] [--out <path>] " + "[--stdout] [--force] [--include-platform-managed]");
|
|
13197
13241
|
return 64;
|
|
13198
13242
|
}
|
|
13199
|
-
const manifestPath =
|
|
13243
|
+
const manifestPath = resolve13(process.cwd(), flags.file ?? "launchpad.yaml");
|
|
13200
13244
|
const result = loadManifest(manifestPath);
|
|
13201
13245
|
const renderResult = renderManifest(result, io);
|
|
13202
13246
|
if (renderResult.kind !== "ok") {
|
|
@@ -13210,8 +13254,8 @@ async function runSecretsTemplate(args, io) {
|
|
|
13210
13254
|
}
|
|
13211
13255
|
return 0;
|
|
13212
13256
|
}
|
|
13213
|
-
const outPath =
|
|
13214
|
-
if (
|
|
13257
|
+
const outPath = resolve13(process.cwd(), flags.out);
|
|
13258
|
+
if (existsSync14(outPath) && !flags.force) {
|
|
13215
13259
|
io.err(`launchpad secrets template: ${outPath} already exists`);
|
|
13216
13260
|
io.err("Pass --force to overwrite, or --stdout to print without writing.");
|
|
13217
13261
|
return 64;
|
|
@@ -13507,8 +13551,8 @@ function printHelp3(io) {
|
|
|
13507
13551
|
|
|
13508
13552
|
// src/commands/skills.ts
|
|
13509
13553
|
import { fileURLToPath } from "node:url";
|
|
13510
|
-
import { dirname as dirname9, join as join15, resolve as
|
|
13511
|
-
import { promises as fs7, existsSync as
|
|
13554
|
+
import { dirname as dirname9, join as join15, resolve as resolve14 } from "node:path";
|
|
13555
|
+
import { promises as fs7, existsSync as existsSync15 } from "node:fs";
|
|
13512
13556
|
|
|
13513
13557
|
// src/skills-bundle.ts
|
|
13514
13558
|
import { homedir as homedir2 } from "node:os";
|
|
@@ -13613,11 +13657,11 @@ function resolveInstallEnv() {
|
|
|
13613
13657
|
function defaultBundleDir() {
|
|
13614
13658
|
const here = dirname9(fileURLToPath(import.meta.url));
|
|
13615
13659
|
const candidates = [
|
|
13616
|
-
|
|
13617
|
-
|
|
13660
|
+
resolve14(here, "..", "skills"),
|
|
13661
|
+
resolve14(here, "..", "..", "skills")
|
|
13618
13662
|
];
|
|
13619
13663
|
for (const c of candidates) {
|
|
13620
|
-
if (
|
|
13664
|
+
if (existsSync15(join15(c, "launchpad-onboard", "SKILL.md"))) {
|
|
13621
13665
|
return c;
|
|
13622
13666
|
}
|
|
13623
13667
|
}
|
|
@@ -13726,7 +13770,7 @@ function describe29(e) {
|
|
|
13726
13770
|
import { execFile as execFile2, spawn as spawn5 } from "node:child_process";
|
|
13727
13771
|
import { promisify } from "node:util";
|
|
13728
13772
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
13729
|
-
import { dirname as dirname10, resolve as
|
|
13773
|
+
import { dirname as dirname10, resolve as resolve15, relative as relative5, isAbsolute as isAbsolute3, join as join16 } from "node:path";
|
|
13730
13774
|
import { homedir as homedir3, tmpdir } from "node:os";
|
|
13731
13775
|
import { readFileSync as readFileSync19, mkdtempSync, writeFileSync as writeFileSync7, rmSync as rmSync2 } from "node:fs";
|
|
13732
13776
|
|
|
@@ -13771,9 +13815,9 @@ async function startLoopback(state, timeoutMs) {
|
|
|
13771
13815
|
resolveCode(code);
|
|
13772
13816
|
}
|
|
13773
13817
|
});
|
|
13774
|
-
const bound = await new Promise((
|
|
13775
|
-
server.once("error", () =>
|
|
13776
|
-
server.listen(0, "127.0.0.1", () =>
|
|
13818
|
+
const bound = await new Promise((resolve15) => {
|
|
13819
|
+
server.once("error", () => resolve15(false));
|
|
13820
|
+
server.listen(0, "127.0.0.1", () => resolve15(true));
|
|
13777
13821
|
});
|
|
13778
13822
|
if (!bound)
|
|
13779
13823
|
return null;
|
|
@@ -14066,7 +14110,7 @@ function errStderr(e) {
|
|
|
14066
14110
|
return "";
|
|
14067
14111
|
}
|
|
14068
14112
|
async function detectPackageManager2() {
|
|
14069
|
-
const pkgRoot =
|
|
14113
|
+
const pkgRoot = resolve15(dirname10(fileURLToPath2(import.meta.url)), "..", "..");
|
|
14070
14114
|
const candidates = [];
|
|
14071
14115
|
const npmRoot = await pmRoot("npm");
|
|
14072
14116
|
if (npmRoot !== null)
|
|
@@ -14074,7 +14118,7 @@ async function detectPackageManager2() {
|
|
|
14074
14118
|
const pnpmRoot = await pmRoot("pnpm");
|
|
14075
14119
|
if (pnpmRoot !== null)
|
|
14076
14120
|
candidates.push(["pnpm", pnpmRoot]);
|
|
14077
|
-
candidates.push(["bun",
|
|
14121
|
+
candidates.push(["bun", resolve15(homedir3(), ".bun/install/global/node_modules")]);
|
|
14078
14122
|
const matches = candidates.filter(([, root]) => pathContains(root, pkgRoot));
|
|
14079
14123
|
return matches.length === 1 ? matches[0][0] : null;
|
|
14080
14124
|
}
|
|
@@ -14211,8 +14255,8 @@ function printHelp5(io) {
|
|
|
14211
14255
|
}
|
|
14212
14256
|
|
|
14213
14257
|
// src/commands/validate.ts
|
|
14214
|
-
import { existsSync as
|
|
14215
|
-
import { dirname as dirname11, resolve as
|
|
14258
|
+
import { existsSync as existsSync16, readFileSync as readFileSync20, readdirSync as readdirSync3, statSync as statSync2 } from "node:fs";
|
|
14259
|
+
import { dirname as dirname11, resolve as resolve16 } from "node:path";
|
|
14216
14260
|
var validateCommand = {
|
|
14217
14261
|
name: "validate",
|
|
14218
14262
|
summary: "validate launchpad.yaml against the v1alpha1 schema",
|
|
@@ -14226,7 +14270,7 @@ async function runValidate(args, io) {
|
|
|
14226
14270
|
return 64;
|
|
14227
14271
|
}
|
|
14228
14272
|
const { file, json, strictGroups } = parseResult;
|
|
14229
|
-
const path14 =
|
|
14273
|
+
const path14 = resolve16(process.cwd(), file ?? "launchpad.yaml");
|
|
14230
14274
|
const result = loadManifest(path14);
|
|
14231
14275
|
if (result.kind !== "ok") {
|
|
14232
14276
|
return json ? renderJsonError(result, io) : renderHumanError(result, io);
|
|
@@ -14273,8 +14317,8 @@ function checkStaticServeDir(manifestPath, manifest) {
|
|
|
14273
14317
|
];
|
|
14274
14318
|
}
|
|
14275
14319
|
const dir = dirname11(manifestPath);
|
|
14276
|
-
const abs =
|
|
14277
|
-
if (!
|
|
14320
|
+
const abs = resolve16(dir, serveDir);
|
|
14321
|
+
if (!existsSync16(abs)) {
|
|
14278
14322
|
return [
|
|
14279
14323
|
`static app serves '${serveDir}/' but that directory does not exist — nothing would be served. ` + `Create ${serveDir}/index.html (or run \`launchpad init\` to scaffold it).`
|
|
14280
14324
|
];
|
|
@@ -14299,7 +14343,7 @@ function checkStaticServeDir(manifestPath, manifest) {
|
|
|
14299
14343
|
`static app's served directory '${serveDir}/' is empty — the deploy would serve an empty site.`
|
|
14300
14344
|
];
|
|
14301
14345
|
}
|
|
14302
|
-
if (!
|
|
14346
|
+
if (!existsSync16(resolve16(abs, "index.html"))) {
|
|
14303
14347
|
return [
|
|
14304
14348
|
`static app's served directory '${serveDir}/' has no index.html — the deploy would serve a directory with no entrypoint.`
|
|
14305
14349
|
];
|
|
@@ -15170,7 +15214,7 @@ import { spawn as spawn6 } from "node:child_process";
|
|
|
15170
15214
|
import { homedir as homedir4 } from "node:os";
|
|
15171
15215
|
import { join as join17 } from "node:path";
|
|
15172
15216
|
import {
|
|
15173
|
-
existsSync as
|
|
15217
|
+
existsSync as existsSync17,
|
|
15174
15218
|
mkdirSync as mkdirSync4,
|
|
15175
15219
|
readFileSync as readFileSync21,
|
|
15176
15220
|
writeFileSync as writeFileSync8
|
|
@@ -15202,7 +15246,7 @@ function writeCache2(state) {
|
|
|
15202
15246
|
var SKILLS_HINT_MARKER = join17(homedir4(), ".launchpad", "skills-hint-shown");
|
|
15203
15247
|
function absentHintShown() {
|
|
15204
15248
|
try {
|
|
15205
|
-
return
|
|
15249
|
+
return existsSync17(SKILLS_HINT_MARKER);
|
|
15206
15250
|
} catch {
|
|
15207
15251
|
return false;
|
|
15208
15252
|
}
|
|
@@ -15313,7 +15357,7 @@ var refreshUpdateCacheCommand = {
|
|
|
15313
15357
|
import { spawn as spawn7 } from "node:child_process";
|
|
15314
15358
|
import { homedir as homedir5 } from "node:os";
|
|
15315
15359
|
import { join as join18 } from "node:path";
|
|
15316
|
-
import { existsSync as
|
|
15360
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync5, readFileSync as readFileSync22, writeFileSync as writeFileSync9 } from "node:fs";
|
|
15317
15361
|
import { randomUUID } from "node:crypto";
|
|
15318
15362
|
var INTERNAL_EMIT_VERB = "__emit-telemetry";
|
|
15319
15363
|
var POSTHOG_HOST = "https://us.i.posthog.com";
|
|
@@ -15467,7 +15511,7 @@ function telemetryNoticeLine() {
|
|
|
15467
15511
|
}
|
|
15468
15512
|
function firstRunNoticeShown() {
|
|
15469
15513
|
try {
|
|
15470
|
-
return
|
|
15514
|
+
return existsSync18(FIRST_RUN_MARKER);
|
|
15471
15515
|
} catch {
|
|
15472
15516
|
return false;
|
|
15473
15517
|
}
|
|
@@ -3,23 +3,19 @@ export declare class GitFilesError extends Error {
|
|
|
3
3
|
readonly code: "git_files_error";
|
|
4
4
|
}
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
* (see `PROTECTED_PATHS` in `portal-bot/src/deploy-pr.ts`). Mirrored
|
|
8
|
-
* here so the CLI excludes them up front rather than shipping a
|
|
9
|
-
* tarball the bot will reject.
|
|
6
|
+
* Legacy convenience filter for known bot-owned files.
|
|
10
7
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* Keep in lock-step with the bot's list. A future endpoint that
|
|
16
|
-
* exposes this list would let the CLI fetch it dynamically; for
|
|
17
|
-
* v1 the set is small and rarely changes.
|
|
8
|
+
* This list is not a security boundary and does not grant deletion
|
|
9
|
+
* authority. The server owns the canonical, family-based path policy
|
|
10
|
+
* and preserves platform files even when an older or newer client
|
|
11
|
+
* sends a different set.
|
|
18
12
|
*/
|
|
19
13
|
export declare const PROTECTED_PATHS: ReadonlySet<string>;
|
|
20
14
|
export interface ListFilesOptions {
|
|
21
15
|
readonly cwd: string;
|
|
22
16
|
readonly spawner?: typeof spawn;
|
|
17
|
+
/** Test seam for the worktree existence check. Receives an absolute path. */
|
|
18
|
+
readonly exists?: (path: string) => boolean;
|
|
23
19
|
}
|
|
24
20
|
/**
|
|
25
21
|
* Return the deploy-eligible file paths under `cwd`, relative to
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-files.d.ts","sourceRoot":"","sources":["../../src/deploy/git-files.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"git-files.d.ts","sourceRoot":"","sources":["../../src/deploy/git-files.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAI9D,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,IAAI,EAAG,iBAAiB,CAAU;CAC5C;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,MAAM,CAU9C,CAAC;AAEH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IAChC,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CAC7C;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CA4BxF"}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "0.54.
|
|
1
|
+
export declare const CLI_VERSION = "0.54.1";
|
|
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.54.
|
|
4
|
+
version: 0.54.1
|
|
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.54.
|
|
4
|
+
version: 0.54.1
|
|
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), or watch provisioning live with `launchpad watch`. 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", "watch my deploy go live", "watch provisioning", "my app says failed but it's serving", or after `/launchpad-deploy` reports a non-`done` terminal stage.
|
|
4
|
-
version: 0.54.
|
|
4
|
+
version: 0.54.1
|
|
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.54.
|
|
4
|
+
version: 0.54.1
|
|
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-identity
|
|
3
3
|
description: Teach an app author how to use the signed-in user's identity inside a Launchpad app — read the gateway-forwarded X-Launchpad-User-Assertion in a Pages Function, VERIFY it with @m-kopa/platform-auth (fail-closed), and show who's logged in (sub/email/name). Use when someone says "who is logged in", "show the current user", "get the user's email in my app", "auth in my launchpad app", "read the user identity", "/launchpad-identity", or is wiring up an /api/me for a gateway-fronted app.
|
|
4
|
-
version: 0.54.
|
|
4
|
+
version: 0.54.1
|
|
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.54.
|
|
4
|
+
version: 0.54.1
|
|
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-report
|
|
3
3
|
description: File a bug report or feature request to the Launchpad team's tracker from the CLI. Use when someone reports something broken, hits an error in a launchpad command, or wishes a feature existed — e.g. "this is broken", "report a bug", "can you file that", "I wish launchpad could…", "/launchpad-bug", "/launchpad-feature". Always confirm and show exactly what you'll send before filing; never file silently.
|
|
4
|
-
version: 0.54.
|
|
4
|
+
version: 0.54.1
|
|
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: Safely refresh a Launchpad clone, show manifest drift, or read the deployed manifest. Wraps bare `launchpad pull` for source sync, `launchpad pull --manifest` for deployed YAML, and `launchpad status` for drift. Use for "sync my clone", "what's deployed", "show drift", "/launchpad-status", "/launchpad-pull", or post-deploy verification.
|
|
4
|
-
version: 0.54.
|
|
4
|
+
version: 0.54.1
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|