@m-kopa/launchpad-cli 0.26.1 → 0.27.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 +77 -0
- package/dist/auth/flow.d.ts +7 -3
- package/dist/auth/flow.d.ts.map +1 -1
- package/dist/auth/gateway-flow.d.ts +76 -0
- package/dist/auth/gateway-flow.d.ts.map +1 -0
- package/dist/auth/session.d.ts +35 -2
- package/dist/auth/session.d.ts.map +1 -1
- package/dist/cli.js +401 -103
- package/dist/commands/login.d.ts +10 -0
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/logout.d.ts +7 -0
- package/dist/commands/logout.d.ts.map +1 -1
- package/dist/config.d.ts +11 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +2 -2
- package/skills/launchpad-content-pr/SKILL.md +146 -124
- package/skills/launchpad-deploy/SKILL.md +153 -67
- package/skills/launchpad-deploy-status/SKILL.md +136 -36
- package/skills/launchpad-destroy/SKILL.md +163 -65
- package/skills/launchpad-onboard/SKILL.md +43 -14
- package/skills/launchpad-status/SKILL.md +119 -25
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: launchpad-destroy
|
|
3
|
-
description: Tear down a Launchpad app end-to-end via `launchpad destroy` — Cloudflare Pages project, Access app, custom hostname, platform-repo TF
|
|
4
|
-
version: 0.
|
|
3
|
+
description: Tear down a Launchpad app end-to-end via `launchpad destroy` — Cloudflare Pages project, edge-auth wiring (gateway KV/audience entries, or the Access app for `auth: access` apps), custom hostname, platform-repo TF, and the app repo (archive-renamed). Owner-only verb with a two-step destructive confirmation. Use when someone says "destroy this app", "/launchpad-destroy", "tear down `<slug>`", "delete the app", or asks to clean up a smoke-test / orphan / retired app.
|
|
4
|
+
version: 0.27.1
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|
|
@@ -31,9 +31,11 @@ esac
|
|
|
31
31
|
# /launchpad-destroy
|
|
32
32
|
|
|
33
33
|
Destructive, owner-only verb. Tears down a Launchpad app end-to-end:
|
|
34
|
-
the Cloudflare Pages project,
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
the Cloudflare Pages project, the app's edge-auth wiring (gateway
|
|
35
|
+
KV/audience entries for gateway-fronted apps — the default; the
|
|
36
|
+
Access app for `auth: access` apps), custom hostname,
|
|
37
|
+
launchpad-platform TF, and the app repo (archive-renamed, not
|
|
38
|
+
deleted). Zero local platform-repo / terraform / Cloudflare /
|
|
37
39
|
GitHub credentials needed — the bot owns all of it. CLI is a thin
|
|
38
40
|
client.
|
|
39
41
|
|
|
@@ -44,8 +46,8 @@ production app, get a second pair of eyes first.
|
|
|
44
46
|
|
|
45
47
|
## Pre-flight
|
|
46
48
|
|
|
47
|
-
You need a current
|
|
48
|
-
Editors cannot destroy.
|
|
49
|
+
You need a current session (`launchpad login`) and OWNER role on the
|
|
50
|
+
app. Editors cannot destroy.
|
|
49
51
|
|
|
50
52
|
```bash
|
|
51
53
|
launchpad whoami
|
|
@@ -64,8 +66,8 @@ exits 1 with `no app with slug <slug>`.
|
|
|
64
66
|
removes the app entirely.
|
|
65
67
|
- **Re-creating an app under the same name.** Run `launchpad destroy
|
|
66
68
|
<slug>`, wait for the lifecycle to reach `destroyed`, then
|
|
67
|
-
`launchpad
|
|
68
|
-
in AC8.
|
|
69
|
+
`launchpad init` + `launchpad deploy` from the app's working
|
|
70
|
+
directory. The slug is freed by the archive-rename step in AC8.
|
|
69
71
|
- **Cleaning up the app's GitHub history.** The destroy verb
|
|
70
72
|
archive-renames the repo (preserving history); it does not
|
|
71
73
|
hard-delete. Use `gh repo delete` if you genuinely need the repo
|
|
@@ -97,26 +99,78 @@ Either flag can short-circuit its respective prompt.
|
|
|
97
99
|
|
|
98
100
|
## What the verb does (server-side flow)
|
|
99
101
|
|
|
102
|
+
Every destroy starts with the same shared steps:
|
|
103
|
+
|
|
100
104
|
1. **Server-side slug re-validation** (`checkSlug()` + `SLUG_REGEX`).
|
|
101
105
|
2. **Owner-only authz** — `owner | break_glass` only; editor → 403.
|
|
102
106
|
3. **Lifecycle precondition check** — see "Lifecycle states" below.
|
|
103
|
-
4. **
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
107
|
+
4. **Three-way TF-shape fork.** The bot probes where the app's
|
|
108
|
+
Terraform lives on launchpad-platform main:
|
|
109
|
+
- `main.tf`-resident (pre-M-1182) → **rejected** (see
|
|
110
|
+
"Carve-outs").
|
|
111
|
+
- **Per-app workspace** (`terraform/envs/prod/apps/<slug>/` with
|
|
112
|
+
isolated state — the default shape for current apps) → the
|
|
113
|
+
workspace path below.
|
|
114
|
+
- **Legacy file pair** (`<slug>.tf` + `<slug>.allow.tf` in the
|
|
115
|
+
shared root) → the legacy PR path below.
|
|
116
|
+
5. **Lifecycle → `destroying`**, then **enumerate open PRs on
|
|
117
|
+
`launchpad-app-<slug>`** (both paths): bot-authored PRs are
|
|
118
|
+
closed with a comment; human-authored PRs get a heads-up comment
|
|
119
|
+
(NOT closed — that's the human author's decision).
|
|
120
|
+
|
|
121
|
+
### Per-app-workspace path (the current default)
|
|
122
|
+
|
|
123
|
+
There is **no destroy PR and no `destroy:confirmed` label** on this
|
|
124
|
+
path. Workspace apps live in isolated TF state, so there is no
|
|
125
|
+
shared apply to pick up a file deletion — teardown is an explicit
|
|
126
|
+
`terraform destroy`:
|
|
127
|
+
|
|
128
|
+
1. The bot fires an **HMAC-signed `repository_dispatch`**
|
|
129
|
+
(`tf-destroy-per-app`): it signs `<slug>|<exp>` with a secret
|
|
130
|
+
only the bot holds (short-lived token, ~10 min). A
|
|
131
|
+
`repository_dispatch` already requires a repo-scoped token, so
|
|
132
|
+
the run cannot be fired from the Actions UI; the workflow
|
|
133
|
+
re-verifies the signature fail-closed AND re-checks
|
|
134
|
+
`lifecycle == destroying` before anything destructive runs.
|
|
135
|
+
2. The workflow runs **`terraform destroy` against the app's
|
|
136
|
+
isolated R2 state** (while the workspace files are still
|
|
137
|
+
present), then deletes the state object so the slug is
|
|
138
|
+
reclaimable.
|
|
139
|
+
3. On run success the bot opens **and auto-merges** a pure
|
|
140
|
+
config-cleanup PR deleting `terraform/envs/prod/apps/<slug>/`
|
|
141
|
+
(branch `bot/destroy-workspace/<slug>/<YYYYMMDD>`) — the
|
|
142
|
+
resources and state are already gone by then; merging just stops
|
|
143
|
+
a later per-app apply from re-creating the app.
|
|
144
|
+
4. **Archive-rename + `lifecycle: destroyed` happen only after the
|
|
145
|
+
destroy run concludes successfully.** A failed run →
|
|
146
|
+
`destroy_failed`, repo untouched; re-running `launchpad destroy`
|
|
147
|
+
re-dispatches the teardown.
|
|
148
|
+
|
|
149
|
+
### Legacy file-pair path (shared-root apps only)
|
|
150
|
+
|
|
151
|
+
Still shipped, for apps whose TF is the shared-root `<slug>.tf` +
|
|
152
|
+
`<slug>.allow.tf` pair:
|
|
153
|
+
|
|
154
|
+
1. **Atomic destroy PR** opens on `launchpad-platform`, deleting
|
|
111
155
|
`terraform/envs/prod/<slug>.tf` + `<slug>.allow.tf` in one commit.
|
|
112
|
-
|
|
156
|
+
2. **`destroy:confirmed` label gate** must be applied by a non-author
|
|
113
157
|
for auto-merge.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
158
|
+
3. **Existing `tf-apply` workflow** destroys the Cf resources on
|
|
159
|
+
merge. There is NO `tf-destroy` workflow on this path — the
|
|
160
|
+
standard apply with the removed module is the teardown.
|
|
161
|
+
4. **Archive-rename happens AFTER tf-apply terminal-success** — never
|
|
162
|
+
before (AC8). If tf-apply fails, the repo is untouched and
|
|
163
|
+
lifecycle moves to `destroy_failed`.
|
|
164
|
+
|
|
165
|
+
## What destroy does NOT remove
|
|
166
|
+
|
|
167
|
+
**The app's D1 database is never dropped.** D1 databases are
|
|
168
|
+
bot-API-created (not in the app's TF), and no destroy path touches
|
|
169
|
+
them. The bot's D1 handling is create-or-adopt **by name**: the data
|
|
170
|
+
survives the destroy, and a later `launchpad init` + `launchpad
|
|
171
|
+
deploy` of the **same slug** re-adopts the existing database — your
|
|
172
|
+
data is still there. If you genuinely need the data gone, that is a
|
|
173
|
+
platform-team request, not a destroy side-effect.
|
|
120
174
|
|
|
121
175
|
## Lifecycle states
|
|
122
176
|
|
|
@@ -124,10 +178,10 @@ Either flag can short-circuit its respective prompt.
|
|
|
124
178
|
|---|---|---|
|
|
125
179
|
| `live` (or absent) | App is operational. | Destroy works. |
|
|
126
180
|
| `provisioning` | Initial create still running. | Wait, then destroy. |
|
|
127
|
-
| `failed` | Provisioning failed. | Destroy works (cleans up partial state). |
|
|
128
|
-
| `destroying` |
|
|
129
|
-
| `destroyed` |
|
|
130
|
-
| `destroy_failed` |
|
|
181
|
+
| `failed` | Provisioning failed. | Destroy works (cleans up partial state). If the app is actually live and serving, consider `launchpad recover <slug>` first — it repairs the record instead of tearing down. |
|
|
182
|
+
| `destroying` | Teardown in flight (workspace: destroy run dispatched; legacy: destroy PR open, tf-apply pending). | Re-running `launchpad destroy` is idempotent — workspace: re-dispatches the run; legacy: returns the existing PR. |
|
|
183
|
+
| `destroyed` | Teardown succeeded + repo archive-renamed. | Re-running exits 0 with `already destroyed at <ts>`. Slug is free for re-use. |
|
|
184
|
+
| `destroy_failed` | The teardown failed or hung. | **Workspace path:** re-run `launchpad destroy` — it re-dispatches from the failure point. **Legacy path:** re-run is rejected with `platform-team intervention required` (409). On both, the app repo is **not** archive-renamed (recovery path stays clean). |
|
|
131
185
|
|
|
132
186
|
## Carve-outs
|
|
133
187
|
|
|
@@ -163,10 +217,14 @@ When the destroy starts, the bot enumerates every open PR on
|
|
|
163
217
|
>
|
|
164
218
|
> Destroy PR: <url>
|
|
165
219
|
|
|
166
|
-
The human author decides what to do (close, salvage, etc.).
|
|
220
|
+
The human author decides what to do (close, salvage, etc.). (The
|
|
221
|
+
`Destroy PR: <url>` line appears on the legacy path only — the
|
|
222
|
+
workspace path has no destroy PR at initiation time.)
|
|
167
223
|
|
|
168
|
-
|
|
169
|
-
|
|
224
|
+
The PR split is computed server-side once the destroy is initiated;
|
|
225
|
+
the CLI prints the bot/human PR lists in its post-initiation render
|
|
226
|
+
(after confirmation, not before), so review them there if you didn't
|
|
227
|
+
realise there were in-flight PRs.
|
|
170
228
|
|
|
171
229
|
## Recovery — the archive-rename window
|
|
172
230
|
|
|
@@ -185,9 +243,11 @@ gh repo unarchive M-KOPA/launchpad-app-<slug>
|
|
|
185
243
|
```
|
|
186
244
|
|
|
187
245
|
This restores the **repo**, not the Cloudflare infrastructure — you
|
|
188
|
-
would still need a fresh `launchpad
|
|
189
|
-
platform-repo PR re-adding the per-app TF
|
|
190
|
-
back online.
|
|
246
|
+
would still need a fresh `launchpad init` + `launchpad deploy` (or a
|
|
247
|
+
hand-rolled platform-repo PR re-adding the per-app TF) to bring the
|
|
248
|
+
app back online. Remember the app's D1 database survived the destroy
|
|
249
|
+
(see "What destroy does NOT remove") — a same-slug re-provision
|
|
250
|
+
re-adopts it.
|
|
191
251
|
|
|
192
252
|
**The 30-day window is informational, not enforced by Launchpad** —
|
|
193
253
|
the actual horizon is whatever GitHub's org policy is. If you
|
|
@@ -205,16 +265,17 @@ suspect a destroy was a mistake, recover within hours, not weeks.
|
|
|
205
265
|
|
|
206
266
|
### `launchpad destroy: session expired, run \`launchpad login\``
|
|
207
267
|
|
|
208
|
-
Standard auth refresh. `launchpad login` once; the
|
|
209
|
-
|
|
268
|
+
Standard auth refresh. `launchpad login` once; the session then stays
|
|
269
|
+
fresh by silent refresh as you use the CLI.
|
|
210
270
|
|
|
211
|
-
### `not authorised to destroy app X (you must be an owner
|
|
271
|
+
### `not authorised to destroy app X (you must be an owner)`
|
|
212
272
|
|
|
213
|
-
You're an editor, not the owner
|
|
273
|
+
You're an editor, not the owner — editor role is not sufficient for
|
|
274
|
+
destroy. Either:
|
|
214
275
|
|
|
215
276
|
- Ask the current owner to transfer ownership via the portal admin UI:
|
|
216
|
-
`https://launchpad.m-kopa.us/admin/apps/<slug>` (then
|
|
217
|
-
after destroy, if you want).
|
|
277
|
+
`https://portal.launchpad.m-kopa.us/admin/apps/<slug>` (then
|
|
278
|
+
transfer back after destroy, if you want).
|
|
218
279
|
- Get break-glass access from platform-team for this specific
|
|
219
280
|
destroy (audited; one-shot).
|
|
220
281
|
|
|
@@ -224,11 +285,12 @@ See "Carve-outs". Platform-team manual cleanup is required.
|
|
|
224
285
|
|
|
225
286
|
### `no per-app TF for <slug> in launchpad-platform/main`
|
|
226
287
|
|
|
227
|
-
The app exists in the registry but has no per-app TF
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
288
|
+
The app exists in the registry but has no per-app TF on platform
|
|
289
|
+
main — neither an `apps/<slug>/` workspace nor a `<slug>.tf` +
|
|
290
|
+
`<slug>.allow.tf` file pair (the bot probes both before this 409).
|
|
291
|
+
This usually means: (a) the app was never fully provisioned (look
|
|
292
|
+
at `lifecycle` — likely `failed`), or (b) the TF was hand-removed
|
|
293
|
+
outside of this verb. Either way: platform-team intervention.
|
|
232
294
|
|
|
233
295
|
### `slug ${urlSlug} is already being destroyed`
|
|
234
296
|
|
|
@@ -237,14 +299,17 @@ message it's from an old bot. Update the bot.)
|
|
|
237
299
|
|
|
238
300
|
### `slug <slug> is in destroy_failed state; platform-team intervention required`
|
|
239
301
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
302
|
+
This 409 fires on the **legacy file-pair path only**: tf-apply on
|
|
303
|
+
the destroy PR failed (commonly: a stuck Cf custom-hostname
|
|
304
|
+
deletion), and the file-pair model has no self-service retry —
|
|
305
|
+
contact platform-team for manual TF state surgery.
|
|
306
|
+
|
|
307
|
+
On the **per-app-workspace path** a `destroy_failed` slug never
|
|
308
|
+
returns this error: re-running `launchpad destroy <slug>` simply
|
|
309
|
+
re-dispatches the teardown run from the failure point.
|
|
243
310
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
2. Contact platform-team for manual TF state surgery if re-running
|
|
247
|
-
doesn't move past the failure.
|
|
311
|
+
On both paths the app repo is NOT archive-renamed in this state —
|
|
312
|
+
the recovery path is unblocked.
|
|
248
313
|
|
|
249
314
|
## --json output
|
|
250
315
|
|
|
@@ -254,6 +319,29 @@ For downstream scripts (CI cleanup jobs, automation):
|
|
|
254
319
|
launchpad destroy <slug> --confirm-slug <slug> --yes --json
|
|
255
320
|
```
|
|
256
321
|
|
|
322
|
+
The shape is per-path. **Per-app-workspace apps** (the default) have
|
|
323
|
+
no destroy PR at initiation — the response carries a `path`
|
|
324
|
+
discriminant instead:
|
|
325
|
+
|
|
326
|
+
```json
|
|
327
|
+
{
|
|
328
|
+
"slug": "<slug>",
|
|
329
|
+
"lifecycle": "destroying",
|
|
330
|
+
"path": "per-app-workspace",
|
|
331
|
+
"openBotPrs": [],
|
|
332
|
+
"openHumanPrs": [
|
|
333
|
+
{ "number": 22, "title": "...", "htmlUrl": "...", "authorLogin": "alice" }
|
|
334
|
+
]
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
(The workspace config-cleanup PR — branch
|
|
339
|
+
`bot/destroy-workspace/<slug>/<YYYYMMDD>` — is opened and auto-merged
|
|
340
|
+
by the bot later, after the destroy run succeeds; it never appears in
|
|
341
|
+
this response.)
|
|
342
|
+
|
|
343
|
+
**Legacy file-pair apps** return the destroy PR:
|
|
344
|
+
|
|
257
345
|
```json
|
|
258
346
|
{
|
|
259
347
|
"slug": "<slug>",
|
|
@@ -287,12 +375,18 @@ On an idempotent re-run against a `destroyed` slug:
|
|
|
287
375
|
- **`/launchpad-status`** — read the deployed manifest, compare to
|
|
288
376
|
local. Pair this with `launchpad pull <slug> --out backup.yaml`
|
|
289
377
|
before destroy if you want to keep the manifest for reference.
|
|
290
|
-
- **`/launchpad-deploy`** — provision a new app
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
`destroy_failed`.
|
|
378
|
+
- **`/launchpad-deploy`** — provision a new app via `launchpad init`
|
|
379
|
+
+ `launchpad deploy` (e.g. after a destroy that frees the slug).
|
|
380
|
+
Note the slug-lock during the destroy window: creating an app is
|
|
381
|
+
rejected while the slug is `destroying` or `destroy_failed`.
|
|
294
382
|
- **`/launchpad-deploy-status`** — diagnose `provisioning` /
|
|
295
383
|
`failed` lifecycle stages. Less relevant after destroy lands.
|
|
384
|
+
If the app shows `failed` but is actually live and serving, reach
|
|
385
|
+
for `launchpad recover <slug>` (documented there) before assuming
|
|
386
|
+
destroy + re-create is the fix — recover repairs the registry
|
|
387
|
+
record against live state. (Recover refuses destroy-side states —
|
|
388
|
+
`destroying` / `destroyed` / `destroy_failed` are owned by this
|
|
389
|
+
verb.)
|
|
296
390
|
|
|
297
391
|
## Anti-patterns
|
|
298
392
|
|
|
@@ -300,17 +394,21 @@ On an idempotent re-run against a `destroyed` slug:
|
|
|
300
394
|
running `launchpad destroy` — the destroy needs to enumerate open
|
|
301
395
|
PRs on the repo and the platform PR opens fine without it, but
|
|
302
396
|
you'll lose the audit trail in the app repo's history.
|
|
303
|
-
- **Don't** hand-edit
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
397
|
+
- **Don't** hand-edit the app's per-app TF (the
|
|
398
|
+
`terraform/envs/prod/apps/<slug>/` workspace, or legacy
|
|
399
|
+
`<slug>.tf`) to "blank out" the app — the bot still considers it
|
|
400
|
+
live and `launchpad destroy` is the only path that handles every
|
|
401
|
+
dependent (edge-auth wiring, hostname, registry record, app repo).
|
|
402
|
+
Manual TF edits leave orphan Cloudflare state.
|
|
308
403
|
- **Don't** parse the prose output of `launchpad destroy` in CI
|
|
309
404
|
scripts. Use `--json`.
|
|
310
|
-
- **Don't** rely on a destroy PR auto-merging without
|
|
311
|
-
`destroy:confirmed` label — a non-author must apply it. The
|
|
312
|
-
is the audit gate; treating it as ceremony defeats AC6's
|
|
313
|
-
eyes property.
|
|
405
|
+
- **Don't** rely on a **legacy-path** destroy PR auto-merging without
|
|
406
|
+
the `destroy:confirmed` label — a non-author must apply it. The
|
|
407
|
+
label is the audit gate; treating it as ceremony defeats AC6's
|
|
408
|
+
two-pair-of-eyes property. (The label gate does not exist on the
|
|
409
|
+
per-app-workspace path — there is no destroy PR there; the
|
|
410
|
+
equivalent control is the bot-only HMAC-signed dispatch + the
|
|
411
|
+
workflow's lifecycle re-check.)
|
|
314
412
|
|
|
315
413
|
## Version
|
|
316
414
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: launchpad-onboard
|
|
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
|
|
4
|
-
version: 0.
|
|
3
|
+
description: One-time setup for the Launchpad CLI + Claude Code skill bundle. Verifies the `launchpad` CLI is installed and current, runs `launchpad whoami` to confirm the session is fresh, and checks the bundled skills are installed and in lock-step with the CLI. Idempotent — safe to re-run any time. Use when someone says "set me up for Launchpad", "I just got a new machine and want to use Launchpad", "/launchpad-onboard", or any of the other launchpad-* skills fails on a prereq check.
|
|
4
|
+
version: 0.27.1
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|
|
@@ -45,9 +45,10 @@ missing — pointing back here.
|
|
|
45
45
|
|
|
46
46
|
## Zero non-Launchpad dependencies
|
|
47
47
|
|
|
48
|
-
Under Model A (M-1216), the CLI is fully self-contained: it
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
Under Model A (M-1216), the CLI is fully self-contained: it signs in
|
|
49
|
+
through the platform auth gateway and talks straight to the bot with
|
|
50
|
+
that bearer credential, never shelling out to `gh`, `jq`, `curl`,
|
|
51
|
+
`git`, or any other system tool. **External users with
|
|
51
52
|
no M-KOPA GitHub access are first-class.** This skill therefore
|
|
52
53
|
checks only two things: the `launchpad` binary is on PATH at the
|
|
53
54
|
expected version, and the bundled skills are installed and synced.
|
|
@@ -78,8 +79,14 @@ launchpad --version
|
|
|
78
79
|
self-contained `.ps1` may trip Defender — prefer the npm install
|
|
79
80
|
above.
|
|
80
81
|
- **Below the version bundled with this skill** (see step 4 below):
|
|
81
|
-
`
|
|
82
|
-
|
|
82
|
+
run `launchpad update` — the CLI's built-in self-update. It detects
|
|
83
|
+
the install channel itself (public npm vs the platform channel) and
|
|
84
|
+
upgrades in place; `launchpad update --check` reports current vs
|
|
85
|
+
latest without installing anything (exit 10 = update available).
|
|
86
|
+
If `update` can't tell which package manager owns the global
|
|
87
|
+
install, it prints the explicit upgrade commands — run the right
|
|
88
|
+
one (e.g. `npm update -g @m-kopa/launchpad-cli`). Then
|
|
89
|
+
`launchpad skills update` to re-sync the skill bundle.
|
|
83
90
|
|
|
84
91
|
### 2. Session is present and fresh
|
|
85
92
|
|
|
@@ -96,22 +103,44 @@ launchpad whoami
|
|
|
96
103
|
|
|
97
104
|
- **Exit 0** → session is valid; identity and expiry are printed.
|
|
98
105
|
- **"No session — run `launchpad login`"** → no session file on
|
|
99
|
-
disk. Run `launchpad login` (opens a browser; sign in
|
|
100
|
-
|
|
101
|
-
- **"session expired, run `launchpad login`"** →
|
|
102
|
-
|
|
106
|
+
disk. Run `launchpad login` (opens a browser; sign in with your
|
|
107
|
+
M-KOPA Microsoft account via the Launchpad auth gateway).
|
|
108
|
+
- **"session expired, run `launchpad login`"** → re-authenticate.
|
|
109
|
+
Sessions normally stay fresh on their own — the CLI silently
|
|
110
|
+
rotates a refresh token as you use it — so this only appears after
|
|
111
|
+
roughly a week of not using the CLI (or a hard cap of 30 days),
|
|
112
|
+
not on a daily cadence. The short access-token expiry `whoami`
|
|
113
|
+
prints is by design; verbs refresh it automatically.
|
|
103
114
|
- **Any other error** → surface the message verbatim. Do not fall
|
|
104
115
|
back to inspecting `~/.launchpad/session.json` by hand — the file
|
|
105
116
|
is internal to the CLI and its shape is not a stable contract.
|
|
106
117
|
|
|
118
|
+
**Upgrading from CLI ≤ 0.26.x:** the login flow moved from Cloudflare
|
|
119
|
+
Access to the platform auth gateway on 2026-06-12, and old clients are
|
|
120
|
+
hard-broken against the bot by design. The fix is always the same two
|
|
121
|
+
commands:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
launchpad update
|
|
125
|
+
launchpad login
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
(If the gateway flow itself fails, the CLI auto-falls back to the
|
|
129
|
+
legacy flow with a deprecation notice; `LAUNCHPAD_AUTH_LEGACY=1`
|
|
130
|
+
forces it outright. It is a temporary escape hatch slated for
|
|
131
|
+
removal — don't reach for it unless platform support says so.)
|
|
132
|
+
|
|
107
133
|
### 3. Skill bundle installed at `$HOME/.claude/skills/launchpad-*/`
|
|
108
134
|
|
|
109
135
|
```bash
|
|
110
136
|
launchpad skills list
|
|
111
137
|
```
|
|
112
138
|
|
|
113
|
-
Expected: the launchpad-* skills
|
|
114
|
-
|
|
139
|
+
Expected: the launchpad-* skills **plus the bundled `marquee-share`
|
|
140
|
+
skill** are all present. The launchpad-* skills each carry a
|
|
141
|
+
`version:` matching the CLI; `marquee-share` is vendored from
|
|
142
|
+
`M-KOPA/marquee` and prints `(no version)` — that is normal, not a
|
|
143
|
+
drift signal.
|
|
115
144
|
|
|
116
145
|
- **Any skill missing** (or `$HOME/.claude/skills/` doesn't exist):
|
|
117
146
|
```bash
|
|
@@ -166,7 +195,7 @@ Otherwise finish with:
|
|
|
166
195
|
|
|
167
196
|
- Do **not** run `launchpad login` without telling the user first — it
|
|
168
197
|
opens a browser. State that it's about to happen and what they need
|
|
169
|
-
to do (sign in
|
|
198
|
+
to do (sign in with their M-KOPA Microsoft account).
|
|
170
199
|
- Do **not** run destructive ops (e.g. `npm uninstall`, `rm`) on the
|
|
171
200
|
user's machine even if a check fails — only suggest the fix; the
|
|
172
201
|
user runs it themselves.
|