@m-kopa/launchpad-cli 0.46.2 → 0.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/dist/cli.js +188 -90
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/update.d.ts +7 -0
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/deploy/access-drift-offer.d.ts +46 -0
- package/dist/deploy/access-drift-offer.d.ts.map +1 -0
- package/dist/deploy/apply.d.ts +8 -0
- package/dist/deploy/apply.d.ts.map +1 -1
- package/dist/io/prompt.d.ts +13 -0
- package/dist/io/prompt.d.ts.map +1 -0
- package/dist/skills-bundle.d.ts +20 -0
- package/dist/skills-bundle.d.ts.map +1 -0
- package/dist/update-notifier.d.ts +3 -13
- package/dist/update-notifier.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +2 -2
- package/skills/launchpad-content-pr/SKILL.md +10 -6
- package/skills/launchpad-deploy/SKILL.md +10 -5
- 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
|
@@ -6,6 +6,37 @@ 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.47.0 — 2026-07-07
|
|
10
|
+
|
|
11
|
+
**`launchpad deploy` offers to reconcile access drift inline (PS-2071).** When a
|
|
12
|
+
plain deploy is refused because the manifest's access groups diverge from what
|
|
13
|
+
the gateway enforces, an **interactive terminal** now *offers to reconcile right
|
|
14
|
+
there* — answer `y` and it routes straight into the gated `launchpad deploy
|
|
15
|
+
--apply` flow instead of leaving you at a dead-end error to re-read, find the
|
|
16
|
+
flag, and re-run. This is convenience only: it changes no control. The apply
|
|
17
|
+
still shows the exact plan and prompts again before opening the PR, and the
|
|
18
|
+
byte-equal validator, the second-person `allowed-groups-change` label gate for
|
|
19
|
+
any access-widening change, and Terraform-as-sole-writer all still apply. A
|
|
20
|
+
**non-interactive** session (CI, a pipe, redirected stdin) never auto-runs an
|
|
21
|
+
access change — it prints the manual `--apply` route and exits non-zero so a
|
|
22
|
+
script fails loudly. After a successful reconcile the CLI points you back at
|
|
23
|
+
`launchpad deploy` to ship your content.
|
|
24
|
+
|
|
25
|
+
**`launchpad update` resyncs drifted skills even when the CLI is already current
|
|
26
|
+
(PS-2058).** Previously the skill bundle was re-synced only after an actual CLI
|
|
27
|
+
upgrade, so a machine already on the latest CLI could sit on a stale skill
|
|
28
|
+
bundle indefinitely with no warning. Now, when `update` finds the CLI already
|
|
29
|
+
current, it also checks the installed skill bundle and:
|
|
30
|
+
|
|
31
|
+
- **`launchpad update`** auto-resyncs a drifted bundle (best-effort; the update
|
|
32
|
+
still exits `0` regardless — a skills hiccup never fails it).
|
|
33
|
+
- **`launchpad update --check`** reports the drift as an informational line
|
|
34
|
+
pointing at `launchpad skills update`; exit codes are unchanged (`10` stays
|
|
35
|
+
CLI-update-only).
|
|
36
|
+
|
|
37
|
+
An in-sync bundle stays silent; an absent bundle is left to the passive
|
|
38
|
+
notifier's one-time hint.
|
|
39
|
+
|
|
9
40
|
## 0.46.2 — 2026-07-07
|
|
10
41
|
|
|
11
42
|
Fix: the apply-status poll no longer mistakes the endpoint's own
|
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.
|
|
22
|
+
var CLI_VERSION = "0.47.0";
|
|
23
23
|
|
|
24
24
|
// src/config.ts
|
|
25
25
|
import * as os from "node:os";
|
|
@@ -6262,6 +6262,7 @@ async function runDeployApply(opts, io, deps = {}) {
|
|
|
6262
6262
|
return outcomeToExit(outcome2, undefined, io);
|
|
6263
6263
|
deletePinIfPresent(cfg, slug2, io);
|
|
6264
6264
|
io.out(`✓ Applied ${slug2}.`);
|
|
6265
|
+
deps.onApplied?.();
|
|
6265
6266
|
return 0;
|
|
6266
6267
|
}
|
|
6267
6268
|
const manifestPath = resolvePath(process.cwd(), opts.file ?? "launchpad.yaml");
|
|
@@ -6344,6 +6345,7 @@ async function runDeployApply(opts, io, deps = {}) {
|
|
|
6344
6345
|
deletePinIfPresent(cfg, slug, io);
|
|
6345
6346
|
io.out("");
|
|
6346
6347
|
io.out(`✓ Applied ${slug}. (See ${apply.prUrl} for the merged PR.)`);
|
|
6348
|
+
deps.onApplied?.();
|
|
6347
6349
|
return 0;
|
|
6348
6350
|
}
|
|
6349
6351
|
async function pollUntilApplied(args) {
|
|
@@ -6556,6 +6558,80 @@ function renderManifestError(loaded, io) {
|
|
|
6556
6558
|
}
|
|
6557
6559
|
}
|
|
6558
6560
|
|
|
6561
|
+
// src/deploy/access-drift-offer.ts
|
|
6562
|
+
var asStrings = (v) => Array.isArray(v) ? v.map((x) => String(x)) : [];
|
|
6563
|
+
function renderAccessDriftSummary(slug, body) {
|
|
6564
|
+
const lines = [`launchpad deploy: refused — ACCESS DRIFT on "${slug}".`];
|
|
6565
|
+
const liveOnly = asStrings(body.live_only);
|
|
6566
|
+
const manifestOnly = asStrings(body.manifest_only);
|
|
6567
|
+
if (liveOnly.length > 0) {
|
|
6568
|
+
lines.push(` enforced but NOT declared (over-grant): ${liveOnly.join(", ")}`);
|
|
6569
|
+
}
|
|
6570
|
+
if (manifestOnly.length > 0) {
|
|
6571
|
+
lines.push(` declared but NOT enforced: ${manifestOnly.join(", ")}`);
|
|
6572
|
+
}
|
|
6573
|
+
lines.push("");
|
|
6574
|
+
lines.push(" Your manifest's access groups don't match what the gateway enforces.");
|
|
6575
|
+
lines.push(" `launchpad deploy` ships content only and cannot change access.");
|
|
6576
|
+
return lines;
|
|
6577
|
+
}
|
|
6578
|
+
function renderManualApplyGuidance() {
|
|
6579
|
+
return [
|
|
6580
|
+
" Run `launchpad deploy --apply` to reconcile — it opens the gated TF PR",
|
|
6581
|
+
" (an access-widening change needs the allowed-groups-change label from a",
|
|
6582
|
+
" second person) and waits for the apply verdict. Then redeploy your content.",
|
|
6583
|
+
" Nothing was committed by this attempt."
|
|
6584
|
+
];
|
|
6585
|
+
}
|
|
6586
|
+
function isYes(answer) {
|
|
6587
|
+
const a = answer.trim().toLowerCase();
|
|
6588
|
+
return a === "y" || a === "yes";
|
|
6589
|
+
}
|
|
6590
|
+
async function handleAccessDrift(params) {
|
|
6591
|
+
const { slug, manifestPath, body, io, deps } = params;
|
|
6592
|
+
for (const line of renderAccessDriftSummary(slug, body))
|
|
6593
|
+
io.err(line);
|
|
6594
|
+
if (!deps.interactive) {
|
|
6595
|
+
for (const line of renderManualApplyGuidance())
|
|
6596
|
+
io.err(line);
|
|
6597
|
+
return 1;
|
|
6598
|
+
}
|
|
6599
|
+
io.err("");
|
|
6600
|
+
const answer = await deps.prompt("Reconcile access now via the gated apply PR? You'll review the exact plan next. [y/N] ");
|
|
6601
|
+
if (!isYes(answer)) {
|
|
6602
|
+
io.err("");
|
|
6603
|
+
io.err("Skipped — no access change was made.");
|
|
6604
|
+
for (const line of renderManualApplyGuidance())
|
|
6605
|
+
io.err(line);
|
|
6606
|
+
return 1;
|
|
6607
|
+
}
|
|
6608
|
+
io.out("");
|
|
6609
|
+
io.out("Reconciling access via `launchpad deploy --apply` …");
|
|
6610
|
+
let applied = false;
|
|
6611
|
+
const exit = await deps.runApply({ file: manifestPath, platformRepo: null, rePin: false, yes: false }, io, { onApplied: () => {
|
|
6612
|
+
applied = true;
|
|
6613
|
+
} });
|
|
6614
|
+
if (applied) {
|
|
6615
|
+
io.out("");
|
|
6616
|
+
io.out("✓ Access reconciled. Re-run `launchpad deploy` to ship your content change.");
|
|
6617
|
+
}
|
|
6618
|
+
return exit;
|
|
6619
|
+
}
|
|
6620
|
+
|
|
6621
|
+
// src/io/prompt.ts
|
|
6622
|
+
function isInteractive() {
|
|
6623
|
+
return Boolean(process.stdin.isTTY);
|
|
6624
|
+
}
|
|
6625
|
+
async function defaultPrompt2(question) {
|
|
6626
|
+
const { createInterface } = await import("node:readline/promises");
|
|
6627
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
6628
|
+
try {
|
|
6629
|
+
return await rl.question(question);
|
|
6630
|
+
} finally {
|
|
6631
|
+
rl.close();
|
|
6632
|
+
}
|
|
6633
|
+
}
|
|
6634
|
+
|
|
6559
6635
|
// src/deploy/dry-run.ts
|
|
6560
6636
|
import { execFileSync } from "node:child_process";
|
|
6561
6637
|
import { resolve as resolve7 } from "node:path";
|
|
@@ -7402,23 +7478,17 @@ async function runModelADeploy(args) {
|
|
|
7402
7478
|
return 1;
|
|
7403
7479
|
}
|
|
7404
7480
|
if (result.status === 409 && errorCode === "access_drift") {
|
|
7405
|
-
|
|
7406
|
-
|
|
7407
|
-
|
|
7408
|
-
|
|
7409
|
-
io
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
|
|
7413
|
-
|
|
7414
|
-
|
|
7415
|
-
|
|
7416
|
-
io.err(" `launchpad deploy` ships content only and cannot change access.");
|
|
7417
|
-
io.err(" Run `launchpad deploy --apply` to reconcile — it opens the gated TF PR");
|
|
7418
|
-
io.err(" (access-widening changes need the allowed-groups-change label from a");
|
|
7419
|
-
io.err(" second person) and waits for the apply verdict. Then redeploy your content.");
|
|
7420
|
-
io.err(" Nothing was committed by this attempt.");
|
|
7421
|
-
return 1;
|
|
7481
|
+
return handleAccessDrift({
|
|
7482
|
+
slug,
|
|
7483
|
+
manifestPath,
|
|
7484
|
+
body,
|
|
7485
|
+
io,
|
|
7486
|
+
deps: {
|
|
7487
|
+
interactive: isInteractive(),
|
|
7488
|
+
prompt: defaultPrompt2,
|
|
7489
|
+
runApply: runDeployApply
|
|
7490
|
+
}
|
|
7491
|
+
});
|
|
7422
7492
|
}
|
|
7423
7493
|
io.err(`launchpad deploy: bot rejected the upload (HTTP ${result.status}, ${errorCode}).`);
|
|
7424
7494
|
if (typeof body.message === "string") {
|
|
@@ -8885,7 +8955,7 @@ var SLUG_REGEX3 = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
|
|
|
8885
8955
|
var initCommand = {
|
|
8886
8956
|
name: "init",
|
|
8887
8957
|
summary: "scaffold a new launchpad.yaml in the current directory",
|
|
8888
|
-
run: (args, io) => runInit(args, io,
|
|
8958
|
+
run: (args, io) => runInit(args, io, defaultPrompt3)
|
|
8889
8959
|
};
|
|
8890
8960
|
async function runInit(args, io, prompt) {
|
|
8891
8961
|
const parsed = parseArgs6(args);
|
|
@@ -9375,7 +9445,7 @@ function ensureGitignoreEntries(path10, entries) {
|
|
|
9375
9445
|
}
|
|
9376
9446
|
return added;
|
|
9377
9447
|
}
|
|
9378
|
-
async function
|
|
9448
|
+
async function defaultPrompt3(question, fallback) {
|
|
9379
9449
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
9380
9450
|
try {
|
|
9381
9451
|
const suffix = fallback !== undefined ? ` [${fallback}]` : "";
|
|
@@ -10056,7 +10126,7 @@ import { stdin as input, stdout as output } from "node:process";
|
|
|
10056
10126
|
var destroyCommand = {
|
|
10057
10127
|
name: "destroy",
|
|
10058
10128
|
summary: "tear down a Launchpad app (owner-only, destructive)",
|
|
10059
|
-
run: (args, io) => runDestroy(args, io,
|
|
10129
|
+
run: (args, io) => runDestroy(args, io, defaultPrompt4, defaultIsTty)
|
|
10060
10130
|
};
|
|
10061
10131
|
var SLUG_RE10 = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
|
|
10062
10132
|
var SLUG_MIN_LENGTH = 3;
|
|
@@ -10288,7 +10358,7 @@ function renderBotError2(status, env, slug, io) {
|
|
|
10288
10358
|
io.err(`launchpad destroy: bot returned HTTP ${status} (error=${errorCode}): ${message}`);
|
|
10289
10359
|
}
|
|
10290
10360
|
}
|
|
10291
|
-
async function
|
|
10361
|
+
async function defaultPrompt4(question) {
|
|
10292
10362
|
const rl = createInterface2({ input, output });
|
|
10293
10363
|
try {
|
|
10294
10364
|
return await rl.question(question);
|
|
@@ -10957,7 +11027,7 @@ async function runRollback(opts, io, deps = {}) {
|
|
|
10957
11027
|
io.out(`Rollback target: ${verifiedSha.slice(0, 12)} (${manifestRelpath})`);
|
|
10958
11028
|
renderDiff(current, parsed.manifest, io);
|
|
10959
11029
|
if (!opts.yes) {
|
|
10960
|
-
const prompt = deps.prompt ??
|
|
11030
|
+
const prompt = deps.prompt ?? defaultPrompt5;
|
|
10961
11031
|
if (!process.stdin.isTTY && deps.prompt === undefined) {
|
|
10962
11032
|
io.err("launchpad rollback: refusing to rollback non-interactively without --yes.");
|
|
10963
11033
|
return 64;
|
|
@@ -11045,7 +11115,7 @@ function formatHostnames(hs) {
|
|
|
11045
11115
|
return hs[0];
|
|
11046
11116
|
return `[${hs.join(", ")}]`;
|
|
11047
11117
|
}
|
|
11048
|
-
async function
|
|
11118
|
+
async function defaultPrompt5(question) {
|
|
11049
11119
|
const rl = createInterface3({ input: process.stdin, output: process.stderr });
|
|
11050
11120
|
try {
|
|
11051
11121
|
return await rl.question(question);
|
|
@@ -11352,7 +11422,7 @@ async function offerRedeploy(slug, opts, io, deps) {
|
|
|
11352
11422
|
const isTty = (deps.isTty ?? (() => process.stdin.isTTY === true))();
|
|
11353
11423
|
let doRedeploy = wantsAuto;
|
|
11354
11424
|
if (!wantsAuto && isTty) {
|
|
11355
|
-
const prompt = deps.prompt ??
|
|
11425
|
+
const prompt = deps.prompt ?? defaultPrompt6;
|
|
11356
11426
|
let answer;
|
|
11357
11427
|
try {
|
|
11358
11428
|
answer = (await prompt(`Redeploy ${slug} now to activate? [y/N] `)).trim().toLowerCase();
|
|
@@ -11373,7 +11443,7 @@ async function offerRedeploy(slug, opts, io, deps) {
|
|
|
11373
11443
|
};
|
|
11374
11444
|
return run({ slug, file: opts.file ?? null, verify: true, verifyTimeoutMs: 180000 }, io, rdeps);
|
|
11375
11445
|
}
|
|
11376
|
-
async function
|
|
11446
|
+
async function defaultPrompt6(question) {
|
|
11377
11447
|
const rl = createInterface4({ input: input2, output: output2 });
|
|
11378
11448
|
try {
|
|
11379
11449
|
return await rl.question(question);
|
|
@@ -12114,9 +12184,45 @@ function printHelp3(io) {
|
|
|
12114
12184
|
|
|
12115
12185
|
// src/commands/skills.ts
|
|
12116
12186
|
import { fileURLToPath } from "node:url";
|
|
12117
|
-
import { dirname as dirname8, join as
|
|
12187
|
+
import { dirname as dirname8, join as join14, resolve as resolve12 } from "node:path";
|
|
12118
12188
|
import { promises as fs6, existsSync as existsSync14 } from "node:fs";
|
|
12189
|
+
|
|
12190
|
+
// src/skills-bundle.ts
|
|
12119
12191
|
import { homedir as homedir2 } from "node:os";
|
|
12192
|
+
import { join as join13 } from "node:path";
|
|
12193
|
+
import { readFileSync as readFileSync18, readdirSync as readdirSync2 } from "node:fs";
|
|
12194
|
+
var SKILL_PREFIX = "launchpad-";
|
|
12195
|
+
function skillsTargetDir() {
|
|
12196
|
+
return process.env.LAUNCHPAD_SKILLS_TARGET_DIR ?? join13(homedir2(), ".claude", "skills");
|
|
12197
|
+
}
|
|
12198
|
+
function readInstalledSkills() {
|
|
12199
|
+
try {
|
|
12200
|
+
const dir = skillsTargetDir();
|
|
12201
|
+
const bundle = readdirSync2(dir, { withFileTypes: true }).find((e) => e.isDirectory() && e.name.startsWith(SKILL_PREFIX));
|
|
12202
|
+
if (!bundle)
|
|
12203
|
+
return { present: false, version: null };
|
|
12204
|
+
return {
|
|
12205
|
+
present: true,
|
|
12206
|
+
version: readSkillVersion(join13(dir, bundle.name, "SKILL.md"))
|
|
12207
|
+
};
|
|
12208
|
+
} catch {
|
|
12209
|
+
return { present: false, version: null };
|
|
12210
|
+
}
|
|
12211
|
+
}
|
|
12212
|
+
function readSkillVersion(path13) {
|
|
12213
|
+
try {
|
|
12214
|
+
const text = readFileSync18(path13, "utf8");
|
|
12215
|
+
const fenceEnd = text.indexOf(`
|
|
12216
|
+
---`, 4);
|
|
12217
|
+
const front = fenceEnd === -1 ? text.slice(0, 1024) : text.slice(0, fenceEnd);
|
|
12218
|
+
const m = /^version:\s*(.+?)\s*$/m.exec(front);
|
|
12219
|
+
return m === null ? null : m[1] ?? null;
|
|
12220
|
+
} catch {
|
|
12221
|
+
return null;
|
|
12222
|
+
}
|
|
12223
|
+
}
|
|
12224
|
+
|
|
12225
|
+
// src/commands/skills.ts
|
|
12120
12226
|
var BUNDLE_PREFIX = "launchpad-";
|
|
12121
12227
|
var BUNDLED_SKILLS = [
|
|
12122
12228
|
"launchpad-onboard",
|
|
@@ -12178,7 +12284,7 @@ function printHelp4(io) {
|
|
|
12178
12284
|
}
|
|
12179
12285
|
function resolveInstallEnv() {
|
|
12180
12286
|
const bundleDir = process.env.LAUNCHPAD_SKILLS_BUNDLE_DIR ?? defaultBundleDir();
|
|
12181
|
-
const userSkillsDir =
|
|
12287
|
+
const userSkillsDir = skillsTargetDir();
|
|
12182
12288
|
return { bundleDir, userSkillsDir };
|
|
12183
12289
|
}
|
|
12184
12290
|
function defaultBundleDir() {
|
|
@@ -12188,7 +12294,7 @@ function defaultBundleDir() {
|
|
|
12188
12294
|
resolve12(here, "..", "..", "skills")
|
|
12189
12295
|
];
|
|
12190
12296
|
for (const c of candidates) {
|
|
12191
|
-
if (existsSync14(
|
|
12297
|
+
if (existsSync14(join14(c, "launchpad-onboard", "SKILL.md"))) {
|
|
12192
12298
|
return c;
|
|
12193
12299
|
}
|
|
12194
12300
|
}
|
|
@@ -12209,12 +12315,12 @@ async function doInstall(io) {
|
|
|
12209
12315
|
}
|
|
12210
12316
|
let installed = 0;
|
|
12211
12317
|
for (const skill of BUNDLED_SKILLS) {
|
|
12212
|
-
const src =
|
|
12318
|
+
const src = join14(env.bundleDir, skill);
|
|
12213
12319
|
if (!await isDir(src)) {
|
|
12214
12320
|
io.err(`launchpad skills install: bundled skill "${skill}" missing from ${env.bundleDir} — package is incomplete.`);
|
|
12215
12321
|
return 1;
|
|
12216
12322
|
}
|
|
12217
|
-
const dest =
|
|
12323
|
+
const dest = join14(env.userSkillsDir, skill);
|
|
12218
12324
|
await fs6.rm(dest, { recursive: true, force: true });
|
|
12219
12325
|
await fs6.cp(src, dest, { recursive: true });
|
|
12220
12326
|
installed++;
|
|
@@ -12239,7 +12345,7 @@ async function doUninstall(io) {
|
|
|
12239
12345
|
continue;
|
|
12240
12346
|
if (!isBundleManaged(entry.name))
|
|
12241
12347
|
continue;
|
|
12242
|
-
const target =
|
|
12348
|
+
const target = join14(env.userSkillsDir, entry.name);
|
|
12243
12349
|
await fs6.rm(target, { recursive: true, force: true });
|
|
12244
12350
|
removed++;
|
|
12245
12351
|
io.out(`✗ removed ${target}`);
|
|
@@ -12262,7 +12368,7 @@ async function doList(io) {
|
|
|
12262
12368
|
}
|
|
12263
12369
|
const width = managedDirs.reduce((n, s) => Math.max(n, s.length), 0);
|
|
12264
12370
|
for (const name of managedDirs) {
|
|
12265
|
-
const skillFile =
|
|
12371
|
+
const skillFile = join14(env.userSkillsDir, name, "SKILL.md");
|
|
12266
12372
|
const version = await readVersion(skillFile);
|
|
12267
12373
|
io.out(` ${name.padEnd(width + 2)}${version ?? "(no version)"}`);
|
|
12268
12374
|
}
|
|
@@ -12297,9 +12403,9 @@ function describe29(e) {
|
|
|
12297
12403
|
import { execFile, spawn as spawn5 } from "node:child_process";
|
|
12298
12404
|
import { promisify } from "node:util";
|
|
12299
12405
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
12300
|
-
import { dirname as dirname9, resolve as resolve13, relative as relative4, isAbsolute as isAbsolute2, join as
|
|
12406
|
+
import { dirname as dirname9, resolve as resolve13, relative as relative4, isAbsolute as isAbsolute2, join as join15 } from "node:path";
|
|
12301
12407
|
import { homedir as homedir3, tmpdir } from "node:os";
|
|
12302
|
-
import { readFileSync as
|
|
12408
|
+
import { readFileSync as readFileSync19, mkdtempSync, writeFileSync as writeFileSync7, rmSync as rmSync2 } from "node:fs";
|
|
12303
12409
|
|
|
12304
12410
|
// src/commands/channel-auth.ts
|
|
12305
12411
|
import { createServer as createServer2 } from "node:http";
|
|
@@ -12420,7 +12526,7 @@ var PKG = "@m-kopa/launchpad-cli";
|
|
|
12420
12526
|
var REGISTRY = "https://registry.npmjs.org";
|
|
12421
12527
|
var CHANNEL_VERSION_URL = "https://get.launchpad.m-kopa.us/version.json";
|
|
12422
12528
|
var CHANNEL_INSTALL_URL = "https://get.launchpad.m-kopa.us";
|
|
12423
|
-
var CHANNEL_MARKER =
|
|
12529
|
+
var CHANNEL_MARKER = join15(homedir3(), ".launchpad", "channel");
|
|
12424
12530
|
var EXIT_UPDATE_AVAILABLE = 10;
|
|
12425
12531
|
var UPGRADE_ARGS = {
|
|
12426
12532
|
npm: ["install", "-g", `${PKG}@latest`],
|
|
@@ -12497,7 +12603,8 @@ function defaultDeps() {
|
|
|
12497
12603
|
detectPm: detectPackageManager2,
|
|
12498
12604
|
runUpgrade,
|
|
12499
12605
|
runChannelInstaller,
|
|
12500
|
-
syncSkills: runSkillsSync
|
|
12606
|
+
syncSkills: runSkillsSync,
|
|
12607
|
+
readInstalledSkills
|
|
12501
12608
|
};
|
|
12502
12609
|
}
|
|
12503
12610
|
async function runSkillsSync() {
|
|
@@ -12520,7 +12627,25 @@ async function runSkillsSync() {
|
|
|
12520
12627
|
async function syncSkillsAfterUpgrade(io, deps) {
|
|
12521
12628
|
const result = await deps.syncSkills();
|
|
12522
12629
|
if (result.ok) {
|
|
12523
|
-
io.out("✓ Claude Code skill bundle re-synced to the
|
|
12630
|
+
io.out("✓ Claude Code skill bundle re-synced to match the CLI.");
|
|
12631
|
+
} else {
|
|
12632
|
+
io.err("Note: couldn't auto-sync the skill bundle — run `launchpad skills update` to finish.");
|
|
12633
|
+
}
|
|
12634
|
+
}
|
|
12635
|
+
async function reconcileSkillBundleWhenCurrent(io, deps, checkOnly) {
|
|
12636
|
+
const skills = deps.readInstalledSkills();
|
|
12637
|
+
if (!skills.present || skills.version === null)
|
|
12638
|
+
return;
|
|
12639
|
+
if (compareVersions(deps.cliVersion, skills.version) !== 1)
|
|
12640
|
+
return;
|
|
12641
|
+
if (checkOnly) {
|
|
12642
|
+
io.out(`Skill bundle ${skills.version} is behind the CLI ${deps.cliVersion} — run \`launchpad skills update\`.`);
|
|
12643
|
+
return;
|
|
12644
|
+
}
|
|
12645
|
+
io.out(`Skill bundle ${skills.version} is behind the CLI ${deps.cliVersion}; re-syncing…`);
|
|
12646
|
+
const result = await deps.syncSkills();
|
|
12647
|
+
if (result.ok) {
|
|
12648
|
+
io.out("✓ Claude Code skill bundle re-synced to match the CLI.");
|
|
12524
12649
|
} else {
|
|
12525
12650
|
io.err("Note: couldn't auto-sync the skill bundle — run `launchpad skills update` to finish.");
|
|
12526
12651
|
}
|
|
@@ -12530,8 +12655,8 @@ async function openSystemBrowser(url) {
|
|
|
12530
12655
|
await execFileAsync(opener, [url]);
|
|
12531
12656
|
}
|
|
12532
12657
|
async function runInstallerScript(script) {
|
|
12533
|
-
const dir = mkdtempSync(
|
|
12534
|
-
const file =
|
|
12658
|
+
const dir = mkdtempSync(join15(tmpdir(), "launchpad-update-"));
|
|
12659
|
+
const file = join15(dir, "install.sh");
|
|
12535
12660
|
try {
|
|
12536
12661
|
writeFileSync7(file, script, { mode: 448 });
|
|
12537
12662
|
return await new Promise((resolvePromise) => {
|
|
@@ -12555,7 +12680,7 @@ function resolveLatestVersion() {
|
|
|
12555
12680
|
}
|
|
12556
12681
|
function detectInstallChannel() {
|
|
12557
12682
|
try {
|
|
12558
|
-
return
|
|
12683
|
+
return readFileSync19(CHANNEL_MARKER, "utf8").trim() === "platform" ? "platform" : "github";
|
|
12559
12684
|
} catch {
|
|
12560
12685
|
return "github";
|
|
12561
12686
|
}
|
|
@@ -12681,6 +12806,7 @@ async function runUpdate(args, io, deps) {
|
|
|
12681
12806
|
const current = deps.cliVersion;
|
|
12682
12807
|
if (compareVersions(current, latest.version) >= 0) {
|
|
12683
12808
|
io.out(`launchpad-cli is up to date (${current}).`);
|
|
12809
|
+
await reconcileSkillBundleWhenCurrent(io, deps, parsed.check);
|
|
12684
12810
|
return 0;
|
|
12685
12811
|
}
|
|
12686
12812
|
if (parsed.check) {
|
|
@@ -12762,7 +12888,7 @@ function printHelp5(io) {
|
|
|
12762
12888
|
}
|
|
12763
12889
|
|
|
12764
12890
|
// src/commands/validate.ts
|
|
12765
|
-
import { existsSync as existsSync15, readFileSync as
|
|
12891
|
+
import { existsSync as existsSync15, readFileSync as readFileSync20, readdirSync as readdirSync3, statSync as statSync2 } from "node:fs";
|
|
12766
12892
|
import { dirname as dirname10, resolve as resolve14 } from "node:path";
|
|
12767
12893
|
var validateCommand = {
|
|
12768
12894
|
name: "validate",
|
|
@@ -12800,7 +12926,7 @@ function checkBoundary(manifestPath, declared) {
|
|
|
12800
12926
|
}
|
|
12801
12927
|
let manifestYaml;
|
|
12802
12928
|
try {
|
|
12803
|
-
manifestYaml =
|
|
12929
|
+
manifestYaml = readFileSync20(manifestPath, "utf8");
|
|
12804
12930
|
} catch {
|
|
12805
12931
|
manifestYaml = null;
|
|
12806
12932
|
}
|
|
@@ -12841,7 +12967,7 @@ function checkStaticServeDir(manifestPath, manifest) {
|
|
|
12841
12967
|
}
|
|
12842
12968
|
let entries;
|
|
12843
12969
|
try {
|
|
12844
|
-
entries =
|
|
12970
|
+
entries = readdirSync3(abs);
|
|
12845
12971
|
} catch (e) {
|
|
12846
12972
|
return [`failed to read static served directory '${serveDir}/': ${describe30(e)}`];
|
|
12847
12973
|
}
|
|
@@ -13715,21 +13841,20 @@ var listOwnersCommand = makeListOwnersCommand();
|
|
|
13715
13841
|
// src/update-notifier.ts
|
|
13716
13842
|
import { spawn as spawn6 } from "node:child_process";
|
|
13717
13843
|
import { homedir as homedir4 } from "node:os";
|
|
13718
|
-
import { join as
|
|
13844
|
+
import { join as join16 } from "node:path";
|
|
13719
13845
|
import {
|
|
13720
13846
|
existsSync as existsSync16,
|
|
13721
13847
|
mkdirSync as mkdirSync4,
|
|
13722
|
-
readFileSync as
|
|
13723
|
-
readdirSync as readdirSync3,
|
|
13848
|
+
readFileSync as readFileSync21,
|
|
13724
13849
|
writeFileSync as writeFileSync8
|
|
13725
13850
|
} from "node:fs";
|
|
13726
13851
|
var INTERNAL_REFRESH_VERB = "__refresh-update-cache";
|
|
13727
13852
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
13728
13853
|
var OPT_OUT_ENV = "LAUNCHPAD_NO_UPDATE_NOTIFIER";
|
|
13729
|
-
var CACHE_FILE =
|
|
13854
|
+
var CACHE_FILE = join16(homedir4(), ".launchpad", "update-check.json");
|
|
13730
13855
|
function readCache2() {
|
|
13731
13856
|
try {
|
|
13732
|
-
const raw = JSON.parse(
|
|
13857
|
+
const raw = JSON.parse(readFileSync21(CACHE_FILE, "utf8"));
|
|
13733
13858
|
if (typeof raw === "object" && raw !== null && typeof raw.checkedAt === "number") {
|
|
13734
13859
|
const latest = raw.latest;
|
|
13735
13860
|
return {
|
|
@@ -13742,39 +13867,12 @@ function readCache2() {
|
|
|
13742
13867
|
}
|
|
13743
13868
|
function writeCache2(state) {
|
|
13744
13869
|
try {
|
|
13745
|
-
mkdirSync4(
|
|
13870
|
+
mkdirSync4(join16(homedir4(), ".launchpad"), { recursive: true });
|
|
13746
13871
|
writeFileSync8(CACHE_FILE, `${JSON.stringify(state)}
|
|
13747
13872
|
`, { mode: 384 });
|
|
13748
13873
|
} catch {}
|
|
13749
13874
|
}
|
|
13750
|
-
var
|
|
13751
|
-
var SKILLS_HINT_MARKER = join15(homedir4(), ".launchpad", "skills-hint-shown");
|
|
13752
|
-
function skillsTargetDir() {
|
|
13753
|
-
return process.env.LAUNCHPAD_SKILLS_TARGET_DIR ?? join15(homedir4(), ".claude", "skills");
|
|
13754
|
-
}
|
|
13755
|
-
function readInstalledSkills() {
|
|
13756
|
-
try {
|
|
13757
|
-
const dir = skillsTargetDir();
|
|
13758
|
-
const bundle = readdirSync3(dir, { withFileTypes: true }).find((e) => e.isDirectory() && e.name.startsWith(SKILL_PREFIX));
|
|
13759
|
-
if (!bundle)
|
|
13760
|
-
return { present: false, version: null };
|
|
13761
|
-
return { present: true, version: readSkillVersion(join15(dir, bundle.name, "SKILL.md")) };
|
|
13762
|
-
} catch {
|
|
13763
|
-
return { present: false, version: null };
|
|
13764
|
-
}
|
|
13765
|
-
}
|
|
13766
|
-
function readSkillVersion(path13) {
|
|
13767
|
-
try {
|
|
13768
|
-
const text = readFileSync20(path13, "utf8");
|
|
13769
|
-
const fenceEnd = text.indexOf(`
|
|
13770
|
-
---`, 4);
|
|
13771
|
-
const front = fenceEnd === -1 ? text.slice(0, 1024) : text.slice(0, fenceEnd);
|
|
13772
|
-
const m = /^version:\s*(.+?)\s*$/m.exec(front);
|
|
13773
|
-
return m === null ? null : m[1] ?? null;
|
|
13774
|
-
} catch {
|
|
13775
|
-
return null;
|
|
13776
|
-
}
|
|
13777
|
-
}
|
|
13875
|
+
var SKILLS_HINT_MARKER = join16(homedir4(), ".launchpad", "skills-hint-shown");
|
|
13778
13876
|
function absentHintShown() {
|
|
13779
13877
|
try {
|
|
13780
13878
|
return existsSync16(SKILLS_HINT_MARKER);
|
|
@@ -13784,7 +13882,7 @@ function absentHintShown() {
|
|
|
13784
13882
|
}
|
|
13785
13883
|
function markAbsentHintShown() {
|
|
13786
13884
|
try {
|
|
13787
|
-
mkdirSync4(
|
|
13885
|
+
mkdirSync4(join16(homedir4(), ".launchpad"), { recursive: true });
|
|
13788
13886
|
writeFileSync8(SKILLS_HINT_MARKER, `${Date.now()}
|
|
13789
13887
|
`, { mode: 384 });
|
|
13790
13888
|
} catch {}
|
|
@@ -13887,8 +13985,8 @@ var refreshUpdateCacheCommand = {
|
|
|
13887
13985
|
// src/telemetry.ts
|
|
13888
13986
|
import { spawn as spawn7 } from "node:child_process";
|
|
13889
13987
|
import { homedir as homedir5 } from "node:os";
|
|
13890
|
-
import { join as
|
|
13891
|
-
import { existsSync as existsSync17, mkdirSync as mkdirSync5, readFileSync as
|
|
13988
|
+
import { join as join17 } from "node:path";
|
|
13989
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync5, readFileSync as readFileSync22, writeFileSync as writeFileSync9 } from "node:fs";
|
|
13892
13990
|
import { randomUUID } from "node:crypto";
|
|
13893
13991
|
var INTERNAL_EMIT_VERB = "__emit-telemetry";
|
|
13894
13992
|
var POSTHOG_HOST = "https://us.i.posthog.com";
|
|
@@ -13896,8 +13994,8 @@ var POSTHOG_PROJECT_KEY = "phc_CYaCuETanWc36TMTiB7cdkdnmPPhFZCkDGmWGLaNkXnb";
|
|
|
13896
13994
|
var CAPTURE_PATH = "/i/v0/e/";
|
|
13897
13995
|
var SEND_TIMEOUT_MS = 3000;
|
|
13898
13996
|
var APP = "launchpad-cli";
|
|
13899
|
-
var DEVICE_ID_FILE =
|
|
13900
|
-
var FIRST_RUN_MARKER =
|
|
13997
|
+
var DEVICE_ID_FILE = join17(homedir5(), ".launchpad", "telemetry-id.json");
|
|
13998
|
+
var FIRST_RUN_MARKER = join17(homedir5(), ".launchpad", "telemetry-notice-shown");
|
|
13901
13999
|
function buildEvent(ctx, identity, nowMs) {
|
|
13902
14000
|
const properties = {
|
|
13903
14001
|
app: APP,
|
|
@@ -13964,14 +14062,14 @@ async function resolveIdentityReal() {
|
|
|
13964
14062
|
}
|
|
13965
14063
|
function getOrCreateDeviceId() {
|
|
13966
14064
|
try {
|
|
13967
|
-
const raw = JSON.parse(
|
|
14065
|
+
const raw = JSON.parse(readFileSync22(DEVICE_ID_FILE, "utf8"));
|
|
13968
14066
|
const id2 = raw?.deviceId;
|
|
13969
14067
|
if (typeof id2 === "string" && id2)
|
|
13970
14068
|
return id2;
|
|
13971
14069
|
} catch {}
|
|
13972
14070
|
const id = randomUUID();
|
|
13973
14071
|
try {
|
|
13974
|
-
mkdirSync5(
|
|
14072
|
+
mkdirSync5(join17(homedir5(), ".launchpad"), { recursive: true });
|
|
13975
14073
|
writeFileSync9(DEVICE_ID_FILE, `${JSON.stringify({ deviceId: id })}
|
|
13976
14074
|
`, {
|
|
13977
14075
|
mode: 384
|
|
@@ -14049,7 +14147,7 @@ function firstRunNoticeShown() {
|
|
|
14049
14147
|
}
|
|
14050
14148
|
function markFirstRunNotice() {
|
|
14051
14149
|
try {
|
|
14052
|
-
mkdirSync5(
|
|
14150
|
+
mkdirSync5(join17(homedir5(), ".launchpad"), { recursive: true });
|
|
14053
14151
|
writeFileSync9(FIRST_RUN_MARKER, `${Date.now()}
|
|
14054
14152
|
`, { mode: 384 });
|
|
14055
14153
|
} catch {}
|
|
@@ -14115,11 +14213,11 @@ import { createInterface as createInterface7 } from "node:readline/promises";
|
|
|
14115
14213
|
import { platform } from "node:os";
|
|
14116
14214
|
|
|
14117
14215
|
// src/report/breadcrumb.ts
|
|
14118
|
-
import { writeFileSync as writeFileSync10, readFileSync as
|
|
14216
|
+
import { writeFileSync as writeFileSync10, readFileSync as readFileSync23, mkdirSync as mkdirSync6 } from "node:fs";
|
|
14119
14217
|
import { homedir as homedir6 } from "node:os";
|
|
14120
|
-
import { join as
|
|
14218
|
+
import { join as join18, dirname as dirname11 } from "node:path";
|
|
14121
14219
|
function breadcrumbPath() {
|
|
14122
|
-
return
|
|
14220
|
+
return join18(homedir6(), ".launchpad", "last-run.json");
|
|
14123
14221
|
}
|
|
14124
14222
|
function writeBreadcrumb(verb, exit) {
|
|
14125
14223
|
try {
|
|
@@ -14130,7 +14228,7 @@ function writeBreadcrumb(verb, exit) {
|
|
|
14130
14228
|
}
|
|
14131
14229
|
function readBreadcrumb() {
|
|
14132
14230
|
try {
|
|
14133
|
-
const raw = JSON.parse(
|
|
14231
|
+
const raw = JSON.parse(readFileSync23(breadcrumbPath(), "utf8"));
|
|
14134
14232
|
if (typeof raw !== "object" || raw === null)
|
|
14135
14233
|
return null;
|
|
14136
14234
|
const r = raw;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAqEA,OAAO,EAGL,4BAA4B,EAC7B,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAqEA,OAAO,EAGL,4BAA4B,EAC7B,MAAM,uBAAuB,CAAC;AAQ/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;AAkMD,2BAA2B;AAC3B,OAAO,EAAE,4BAA4B,EAAE,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ChannelUpdateResult } from "./channel-auth.js";
|
|
2
|
+
import { type InstalledSkillsState } from "../skills-bundle.js";
|
|
2
3
|
import type { CliIo, Command, ExitCode } from "../dispatcher.js";
|
|
3
4
|
/** Browser-based installer the platform channel directs users to for upgrades. */
|
|
4
5
|
export declare const CHANNEL_INSTALL_URL = "https://get.launchpad.m-kopa.us";
|
|
@@ -96,6 +97,12 @@ export interface UpdateDeps {
|
|
|
96
97
|
* failure). See {@link runSkillsSync}.
|
|
97
98
|
*/
|
|
98
99
|
readonly syncSkills: () => Promise<SkillsSyncResult>;
|
|
100
|
+
/**
|
|
101
|
+
* Read the freshness of the on-disk Claude Code skill bundle. Injected so
|
|
102
|
+
* the already-up-to-date branch's drift check is unit-testable without
|
|
103
|
+
* touching the filesystem. See {@link reconcileSkillBundleWhenCurrent}.
|
|
104
|
+
*/
|
|
105
|
+
readonly readInstalledSkills: () => InstalledSkillsState;
|
|
99
106
|
}
|
|
100
107
|
/** Outcome of the post-upgrade skill-bundle re-sync. */
|
|
101
108
|
export interface SkillsSyncResult {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"AAuDA,OAAO,EAA4B,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACvF,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAWjE,kFAAkF;AAClF,eAAO,MAAM,mBAAmB,oCAAoC,CAAC;AAYrE,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;AAEpD,gEAAgE;AAChE,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,UAAU,CAAC;AASnD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,cAAc,GAAG,MAAM,CAGvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,CAEpB;AAED,eAAO,MAAM,aAAa,EAAE,OAI3B,CAAC;AAIF,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,uEAAuE;IACvE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAMD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,CA4BnE;AAID;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAiBhE;AAID,MAAM,MAAM,YAAY,GACpB;IAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAI3D,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,mEAAmE;IACnE,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC;IACzD,QAAQ,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IACxD,QAAQ,CAAC,UAAU,EAAE,CAAC,EAAE,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7D;;;;;;OAMG;IACH,QAAQ,CAAC,mBAAmB,EAAE,MAAM,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACjE;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { CliIo, ExitCode } from "../dispatcher.js";
|
|
2
|
+
import type { ApplyOptions } from "./apply.js";
|
|
3
|
+
/** The two-direction group diff the bot returns with a 409 `access_drift`. */
|
|
4
|
+
export interface AccessDriftBody {
|
|
5
|
+
readonly live_only?: unknown;
|
|
6
|
+
readonly manifest_only?: unknown;
|
|
7
|
+
}
|
|
8
|
+
export interface AccessDriftDeps {
|
|
9
|
+
/** True when we may prompt (stdin is a TTY). */
|
|
10
|
+
readonly interactive: boolean;
|
|
11
|
+
/** Ask a yes/no question; resolves the raw typed line. */
|
|
12
|
+
readonly prompt: (question: string) => Promise<string>;
|
|
13
|
+
/**
|
|
14
|
+
* Run the gated apply flow. Defaults to `runDeployApply` at the call site.
|
|
15
|
+
* The third arg carries an `onApplied` hook the flow fires only when a
|
|
16
|
+
* terraform apply actually completes — so we never claim success when the
|
|
17
|
+
* operator reviews the plan and aborts (both paths exit 0).
|
|
18
|
+
*/
|
|
19
|
+
readonly runApply: (opts: ApplyOptions, io: CliIo, deps?: {
|
|
20
|
+
readonly onApplied?: () => void;
|
|
21
|
+
}) => Promise<ExitCode>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The drift summary — printed in every case (interactive or not). Names both
|
|
25
|
+
* directions of the divergence so the operator can see exactly what would
|
|
26
|
+
* change before deciding.
|
|
27
|
+
*/
|
|
28
|
+
export declare function renderAccessDriftSummary(slug: string, body: AccessDriftBody): string[];
|
|
29
|
+
/**
|
|
30
|
+
* The manual fallback guidance — printed when the session is non-interactive,
|
|
31
|
+
* or when the operator declines the inline offer.
|
|
32
|
+
*/
|
|
33
|
+
export declare function renderManualApplyGuidance(): string[];
|
|
34
|
+
/**
|
|
35
|
+
* Surface an access-drift refusal and, when interactive, offer to reconcile
|
|
36
|
+
* inline via the gated apply flow. Returns the exit code the deploy command
|
|
37
|
+
* should return.
|
|
38
|
+
*/
|
|
39
|
+
export declare function handleAccessDrift(params: {
|
|
40
|
+
readonly slug: string;
|
|
41
|
+
readonly manifestPath: string;
|
|
42
|
+
readonly body: AccessDriftBody;
|
|
43
|
+
readonly io: CliIo;
|
|
44
|
+
readonly deps: AccessDriftDeps;
|
|
45
|
+
}): Promise<ExitCode>;
|
|
46
|
+
//# sourceMappingURL=access-drift-offer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access-drift-offer.d.ts","sourceRoot":"","sources":["../../src/deploy/access-drift-offer.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,8EAA8E;AAC9E,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,eAAe;IAC9B,gDAAgD;IAChD,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACvD;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,CACjB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,KAAK,EACT,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;KAAE,KACvC,OAAO,CAAC,QAAQ,CAAC,CAAC;CACxB;AAKD;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,eAAe,GACpB,MAAM,EAAE,CAoBV;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,EAAE,CAOpD;AAQD;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;CAChC,GAAG,OAAO,CAAC,QAAQ,CAAC,CA8CpB"}
|
package/dist/deploy/apply.d.ts
CHANGED
|
@@ -27,6 +27,14 @@ export interface ApplyDeps {
|
|
|
27
27
|
readonly fetcher?: typeof fetch;
|
|
28
28
|
/** Polling interval seconds (defaults to 10s; tests use 0). */
|
|
29
29
|
readonly pollIntervalSeconds?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Fired exactly once, when a terraform apply actually COMPLETES in this
|
|
32
|
+
* invocation (not when the operator aborts at the confirmation prompt, and
|
|
33
|
+
* not on a non-applied terminal state). Lets a caller — e.g. the PS-2071
|
|
34
|
+
* inline access-drift offer — distinguish a real reconcile from an abort,
|
|
35
|
+
* both of which exit 0.
|
|
36
|
+
*/
|
|
37
|
+
readonly onApplied?: () => void;
|
|
30
38
|
}
|
|
31
39
|
export declare function runDeployApply(opts: ApplyOptions, io: CliIo, deps?: ApplyDeps): Promise<ExitCode>;
|
|
32
40
|
//# sourceMappingURL=apply.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/deploy/apply.ts"],"names":[],"mappings":"AAqDA,OAAO,EAAc,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAU1D,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAExD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B;iCAC6B;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC;wDACoD;IACpD,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB;;;oEAGgE;IAChE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,iEAAiE;IACjE,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,8DAA8D;IAC9D,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACxD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;IAC5B,gEAAgE;IAChE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IAChC,+DAA+D;IAC/D,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/deploy/apply.ts"],"names":[],"mappings":"AAqDA,OAAO,EAAc,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAU1D,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAExD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B;iCAC6B;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC;wDACoD;IACpD,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB;;;oEAGgE;IAChE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,iEAAiE;IACjE,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,8DAA8D;IAC9D,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACxD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;IAC5B,gEAAgE;IAChE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IAChC,+DAA+D;IAC/D,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACjC;AAwCD,wBAAsB,cAAc,CAClC,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,KAAK,EACT,IAAI,GAAE,SAAc,GACnB,OAAO,CAAC,QAAQ,CAAC,CAsLnB"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* True when we can meaningfully prompt the operator — stdin is an interactive
|
|
3
|
+
* TTY. In a pipe, a CI job, or a redirected stdin this is false, and callers
|
|
4
|
+
* must fall back to a non-interactive path rather than block on a read that
|
|
5
|
+
* will never receive input.
|
|
6
|
+
*/
|
|
7
|
+
export declare function isInteractive(): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Ask a question on stdin/stdout and resolve the raw typed line (untrimmed).
|
|
10
|
+
* The readline interface is always closed, even if the question rejects.
|
|
11
|
+
*/
|
|
12
|
+
export declare function defaultPrompt(question: string): Promise<string>;
|
|
13
|
+
//# sourceMappingURL=prompt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/io/prompt.ts"],"names":[],"mappings":"AAQA;;;;;GAKG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQrE"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Prefix every bundled launchpad skill directory shares. */
|
|
2
|
+
export declare const SKILL_PREFIX = "launchpad-";
|
|
3
|
+
/** Freshness of the on-disk Claude Code skill bundle vs the running CLI. */
|
|
4
|
+
export interface InstalledSkillsState {
|
|
5
|
+
/** True if any `launchpad-*` skill is installed under the skills dir. */
|
|
6
|
+
readonly present: boolean;
|
|
7
|
+
/** The bundle's `version:` frontmatter, or null if unreadable. */
|
|
8
|
+
readonly version: string | null;
|
|
9
|
+
}
|
|
10
|
+
/** Where `launchpad skills install` writes the bundle (mirrors skills.ts). */
|
|
11
|
+
export declare function skillsTargetDir(): string;
|
|
12
|
+
/**
|
|
13
|
+
* Synchronously read the installed skill-bundle freshness. Hot-path safe:
|
|
14
|
+
* one `readdir` + at most one small `readFile`, no network. A missing or
|
|
15
|
+
* unreadable skills dir is reported as `{ present: false }` (not an error).
|
|
16
|
+
*/
|
|
17
|
+
export declare function readInstalledSkills(): InstalledSkillsState;
|
|
18
|
+
/** Parse the `version:` frontmatter from a SKILL.md (inside the `---` fence). */
|
|
19
|
+
export declare function readSkillVersion(path: string): string | null;
|
|
20
|
+
//# sourceMappingURL=skills-bundle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills-bundle.d.ts","sourceRoot":"","sources":["../src/skills-bundle.ts"],"names":[],"mappings":"AAiBA,6DAA6D;AAC7D,eAAO,MAAM,YAAY,eAAe,CAAC;AAEzC,4EAA4E;AAC5E,MAAM,WAAW,oBAAoB;IACnC,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,kEAAkE;IAClE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,8EAA8E;AAC9E,wBAAgB,eAAe,IAAI,MAAM,CAKxC;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,oBAAoB,CAc1D;AAED,iFAAiF;AACjF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW5D"}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { CliIo, Command } from "./dispatcher.js";
|
|
2
2
|
import { resolveLatestVersion, type InstallChannel } from "./commands/update.js";
|
|
3
|
+
import { readInstalledSkills, type InstalledSkillsState } from "./skills-bundle.js";
|
|
4
|
+
export { readInstalledSkills };
|
|
5
|
+
export type { InstalledSkillsState };
|
|
3
6
|
/** Internal verb the detached refresh process runs. Hidden from help. */
|
|
4
7
|
export declare const INTERNAL_REFRESH_VERB = "__refresh-update-cache";
|
|
5
8
|
/** Re-check the registry at most once per this window (24h). */
|
|
@@ -15,19 +18,6 @@ export interface CacheState {
|
|
|
15
18
|
export declare function readCache(): CacheState | null;
|
|
16
19
|
/** Write the cache file (0600), creating ~/.launchpad if needed. Best-effort. */
|
|
17
20
|
export declare function writeCache(state: CacheState): void;
|
|
18
|
-
/** Freshness of the on-disk Claude Code skill bundle vs the running CLI. */
|
|
19
|
-
export interface InstalledSkillsState {
|
|
20
|
-
/** True if any `launchpad-*` skill is installed under the skills dir. */
|
|
21
|
-
readonly present: boolean;
|
|
22
|
-
/** The bundle's `version:` frontmatter, or null if unreadable. */
|
|
23
|
-
readonly version: string | null;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Synchronously read the installed skill-bundle freshness. Hot-path safe:
|
|
27
|
-
* one `readdir` + at most one small `readFile`, no network. A missing or
|
|
28
|
-
* unreadable skills dir is reported as `{ present: false }` (not an error).
|
|
29
|
-
*/
|
|
30
|
-
export declare function readInstalledSkills(): InstalledSkillsState;
|
|
31
21
|
/** Has the one-time "skills not installed" hint already been shown? */
|
|
32
22
|
export declare function absentHintShown(): boolean;
|
|
33
23
|
/** Record that the one-time absent hint was shown. Best-effort. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-notifier.d.ts","sourceRoot":"","sources":["../src/update-notifier.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"update-notifier.d.ts","sourceRoot":"","sources":["../src/update-notifier.ts"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAIL,oBAAoB,EACpB,KAAK,cAAc,EACpB,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EACL,mBAAmB,EACnB,KAAK,oBAAoB,EAC1B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,mBAAmB,EAAE,CAAC;AAC/B,YAAY,EAAE,oBAAoB,EAAE,CAAC;AAErC,yEAAyE;AACzE,eAAO,MAAM,qBAAqB,2BAA2B,CAAC;AAE9D,gEAAgE;AAChE,eAAO,MAAM,iBAAiB,QAAsB,CAAC;AAQrD,6CAA6C;AAC7C,MAAM,WAAW,UAAU;IACzB,uEAAuE;IACvE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,uEAAuE;IACvE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,wEAAwE;AACxE,wBAAgB,SAAS,IAAI,UAAU,GAAG,IAAI,CAkB7C;AAED,iFAAiF;AACjF,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAOlD;AAQD,uEAAuE;AACvE,wBAAgB,eAAe,IAAI,OAAO,CAMzC;AAED,mEAAmE;AACnE,wBAAgB,mBAAmB,IAAI,IAAI,CAO1C;AAID,2EAA2E;AAC3E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC/B;AAED,yEAAyE;AACzE,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,cAAc,CAAC;IACvC,QAAQ,CAAC,SAAS,EAAE,MAAM,UAAU,GAAG,IAAI,CAAC;IAC5C,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAC;IAC3B,iEAAiE;IACjE,QAAQ,CAAC,YAAY,EAAE,MAAM,IAAI,CAAC;IAClC,gEAAgE;IAChE,QAAQ,CAAC,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;IACzD,uEAAuE;IACvE,QAAQ,CAAC,eAAe,EAAE,MAAM,OAAO,CAAC;IACxC,sDAAsD;IACtD,QAAQ,CAAC,mBAAmB,EAAE,MAAM,IAAI,CAAC;CAC1C;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAQxD;AAED,gDAAgD;AAChD,wBAAgB,UAAU,CACxB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,cAAc,GACtB,MAAM,CAMR;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAEjF;AAED,qEAAqE;AACrE,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,EAAE,EAAE,KAAK,EACT,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE,YAAY,GACjB,IAAI,CAgCN;AAwBD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,KAAK,EACT,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,GAAE,MAAM,GAAG,SAA2B,GAC5C,IAAI,CAuBN;AAID,yDAAyD;AACzD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,oBAAoB,EAAE,OAAO,oBAAoB,CAAC;IAC3D,QAAQ,CAAC,SAAS,EAAE,MAAM,UAAU,GAAG,IAAI,CAAC;IAC5C,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,CAAC;IAC7C,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAC;CAC5B;AASD;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,GAAE,WAA6B,GAClC,OAAO,CAAC,IAAI,CAAC,CASf;AAED,0EAA0E;AAC1E,eAAO,MAAM,yBAAyB,EAAE,OAQvC,CAAC"}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "0.
|
|
1
|
+
export declare const CLI_VERSION = "0.47.0";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@m-kopa/launchpad-cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Launchpad CLI
|
|
3
|
+
"version": "0.47.0",
|
|
4
|
+
"description": "Launchpad CLI — clone / deploy / review / merge against Launchpad-managed apps. Talks to the portal-bot endpoints (SCOPE-M-760 / T4).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"launchpad": "./dist/cli.js"
|
|
@@ -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.
|
|
4
|
+
version: 0.47.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|
|
@@ -193,11 +193,15 @@ group passes (no need to use `deploy --dry-run` as a workaround).
|
|
|
193
193
|
> `access.allowed_entra_groups` and running a plain `launchpad deploy`
|
|
194
194
|
> ships content only — it does **not** reconcile the gateway's access
|
|
195
195
|
> policy, so the change won't take effect (and is refused once the
|
|
196
|
-
> access-drift gate is enforced).
|
|
197
|
-
>
|
|
198
|
-
>
|
|
199
|
-
>
|
|
200
|
-
>
|
|
196
|
+
> access-drift gate is enforced). When that refusal happens in an
|
|
197
|
+
> **interactive terminal**, the CLI now **offers to reconcile inline** —
|
|
198
|
+
> answering `y` routes straight into the gated apply flow (which shows
|
|
199
|
+
> the plan and confirms again before opening the PR). To change a live
|
|
200
|
+
> app's groups the user self-serves the **gated Terraform route**:
|
|
201
|
+
> `launchpad deploy --apply` (preview with `--dry-run`, confirm with
|
|
202
|
+
> `launchpad status`) — or just accept the inline offer. Do **not** tell
|
|
203
|
+
> the user to raise a request with the platform team — `--apply` IS the
|
|
204
|
+
> self-serve route. See `/launchpad-status` and the
|
|
201
205
|
> [auth & groups](https://get.launchpad.m-kopa.us/docs/concepts/auth-groups#changing-access-on-an-existing-app)
|
|
202
206
|
> doc.
|
|
203
207
|
|
|
@@ -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.
|
|
4
|
+
version: 0.47.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|
|
@@ -235,10 +235,15 @@ Use whichever fits; `--dry-run` additionally previews the Terraform.
|
|
|
235
235
|
|
|
236
236
|
This `--group` flow sets the group at **first deploy**. Changing the
|
|
237
237
|
group on an **existing** app is a different path — a plain `launchpad
|
|
238
|
-
deploy` ships content only and won't move the gateway policy.
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
238
|
+
deploy` ships content only and won't move the gateway policy. When a
|
|
239
|
+
plain deploy detects access drift in an **interactive terminal** it now
|
|
240
|
+
**offers to reconcile inline** (answer `y` to route straight into the
|
|
241
|
+
gated apply flow, which shows the plan and confirms again before it
|
|
242
|
+
opens the PR); in a non-interactive session it prints the manual
|
|
243
|
+
`launchpad deploy --apply` route and exits non-zero. Either way the fix
|
|
244
|
+
is the gated Terraform apply — see `/launchpad-content-pr`. Never route
|
|
245
|
+
the user to the platform team for a group change, and never tell them
|
|
246
|
+
access drift is unfixable: the self-serve reconcile is one keystroke.
|
|
242
247
|
|
|
243
248
|
If `launchpad groups list` fails with:
|
|
244
249
|
|
|
@@ -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.
|
|
4
|
+
version: 0.47.0
|
|
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.
|
|
4
|
+
version: 0.47.0
|
|
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.
|
|
4
|
+
version: 0.47.0
|
|
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.
|
|
4
|
+
version: 0.47.0
|
|
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.
|
|
4
|
+
version: 0.47.0
|
|
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.
|
|
4
|
+
version: 0.47.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|