@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 CHANGED
@@ -6,6 +6,83 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
  This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html);
7
7
  pre-1.0 minor bumps may carry breaking changes per ADR 0005.
8
8
 
9
+ ## 0.27.1 — 2026-06-12
10
+
11
+ Bundled-skills refresh (sp-s4k9wm) — no CLI code changes. Every
12
+ `launchpad-*` skill audited claim-by-claim against shipped behaviour
13
+ (141 claims: 43 corrected, 13 coverage gaps filled):
14
+
15
+ - **Auth guidance** now matches the 0.27.0 gateway login: gateway-first
16
+ sign-in, the ≤ 0.26.x `launchpad update && launchpad login` recovery,
17
+ silent rotating-refresh session model (the "~24 h Cf Access session"
18
+ framing is gone).
19
+ - **`launchpad-content-pr`** reworked to the real deploy model:
20
+ subsequent deploys commit directly to the app repo's `main` (no
21
+ content PR), the CLI returns at the bot's 202 ack, and verification
22
+ is its own `launchpad status` step. The fictional "stack-fit
23
+ pre-flight" is replaced by the real gates (bundle policy, secret
24
+ scan, build-command allowlist — delta-judged per ADR 0025).
25
+ - **`launchpad-deploy`** uses `launchpad init`'s real flag vocabulary,
26
+ documents group displayName|UUID resolution, pages-tier D1
27
+ (`d1_binding`) auto-provisioning incl. the empty-DB gotcha, and the
28
+ real server-side gate set.
29
+ - **`launchpad-deploy-status`** carries the full stage taxonomy
30
+ (`content_seeded`, the `tf_env_*` trio) and folds in
31
+ `launchpad recover` for terminal-failed-but-serving apps.
32
+ - **`launchpad-status`** documents the full lifecycle/live-truth state
33
+ union and the live Pages build-outcome rendering.
34
+ - **`launchpad-destroy`** documents the per-app-workspace dispatch
35
+ teardown (no destroy PR on that path) and that the app's D1 database
36
+ is never dropped.
37
+
38
+ Run `launchpad skills update` after upgrading to pick up the refreshed
39
+ bundle.
40
+
41
+ ## 0.27.0 — 2026-06-12
42
+
43
+ `launchpad login` moves onto the platform's auth gateway (sp-cli7kq
44
+ Task 3, ADR 0026). Dual-path: gateway first, legacy Cloudflare Access
45
+ as a deprecated fallback for the duration of the dual-auth window.
46
+
47
+ ### Added
48
+
49
+ - **Gateway login is the default.** `launchpad login` now authenticates
50
+ against the auth gateway's `cli-session` grant
51
+ (`auth.launchpad.m-kopa.us`): loopback Authorization-Code + PKCE into
52
+ the Entra SSO, yielding a short-lived (15-minute) RS256 access token
53
+ plus an opaque refresh token that **rotates on every refresh**. No
54
+ discovery, no dynamic client registration — the endpoints are fixed.
55
+ Sessions persist as a new `version: 2` shape in
56
+ `~/.launchpad/session.json` (same location, same `0600` mode); the
57
+ rotated refresh token is persisted atomically (write-rename) **before**
58
+ the new access token is used, so a crash can never strand the session
59
+ on a rotated-away token.
60
+ - **Server-side logout.** `launchpad logout` now revokes gateway
61
+ sessions at the gateway (`POST /__cli_logout`) before clearing the
62
+ local file: refresh dies immediately; in-flight access tokens expire
63
+ within 15 minutes. Offline-safe — if the gateway is unreachable the
64
+ local session is still cleared with a warning, exit code `0`.
65
+ - **Kill-switch.** `LAUNCHPAD_AUTH_LEGACY=1` forces the legacy
66
+ Cloudflare Access login outright (no gateway attempt).
67
+ `LAUNCHPAD_AUTH_GATEWAY_URL` overrides the gateway base URL for
68
+ tests/previews.
69
+ - Gateway `429` rate-limit responses are honoured: short `Retry-After`
70
+ waits are absorbed with a single retry; longer ones surface a clear
71
+ "session intact, retry shortly" error without burning the refresh
72
+ token.
73
+
74
+ ### Changed
75
+
76
+ - A revoked / idle-expired (7 days) / capped (30 days) gateway session
77
+ now clears itself locally and prompts ``run `launchpad login` ``;
78
+ authenticated verbs keep the exact exit-`3` contract for auth
79
+ failures.
80
+ - The legacy Cloudflare Access login path is **deprecated but intact**
81
+ — it runs automatically (with a notice) when the gateway flow fails,
82
+ and will be deleted after the dual-auth window closes (AC-DECOM).
83
+ - `launchpad update`'s installer-bearer channel auth (ADR 0023) is
84
+ untouched by all of the above.
85
+
9
86
  ## 0.26.1 — 2026-06-11
10
87
 
11
88
  Two `launchpad status` UX faults found live by the owner (fast-track,
@@ -1,10 +1,14 @@
1
- import { type CliSession } from "./session.js";
1
+ import { type AnyCliSession, type CliSession } from "./session.js";
2
2
  export declare class LoginRequiredError extends Error {
3
3
  readonly code: "login_required";
4
4
  }
5
5
  /** Refresh-window buffer: refresh `refreshSkewMs` before expiry
6
6
  * so a request fired right at the edge doesn't hit a 401. */
7
7
  export declare const REFRESH_SKEW_MS = 30000;
8
+ /** A gateway 429 with Retry-After at or under this is absorbed
9
+ * in-process (wait, then retry the refresh ONCE). Anything longer is
10
+ * surfaced to the user instead of silently stalling a verb. */
11
+ export declare const MAX_ABSORBED_RETRY_AFTER_SEC = 10;
8
12
  export interface LoginOptions {
9
13
  readonly botUrl: string;
10
14
  readonly sessionPath: string;
@@ -32,8 +36,8 @@ export declare function login(opts: LoginOptions): Promise<CliSession>;
32
36
  * `now` is injected for tests so we can simulate expiry without
33
37
  * mocking the system clock.
34
38
  */
35
- export declare function getValidAccessToken(sessionPath: string, fetcher?: typeof fetch, now?: () => number): Promise<{
39
+ export declare function getValidAccessToken(sessionPath: string, fetcher?: typeof fetch, now?: () => number, sleep?: (ms: number) => Promise<void>): Promise<{
36
40
  accessToken: string;
37
- session: CliSession;
41
+ session: AnyCliSession;
38
42
  }>;
39
43
  //# sourceMappingURL=flow.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"flow.d.ts","sourceRoot":"","sources":["../../src/auth/flow.ts"],"names":[],"mappings":"AAwCA,OAAO,EAGL,KAAK,UAAU,EAEhB,MAAM,cAAc,CAAC;AAGtB,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,IAAI,EAAG,gBAAgB,CAAU;CAC3C;AAED;8DAC8D;AAC9D,eAAO,MAAM,eAAe,QAAS,CAAC;AAEtC,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;sDAEkD;IAClD,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,kCAAkC;IAClC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IAChC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACzD;AAED;;;;GAIG;AACH,wBAAsB,KAAK,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,CA8DnE;AAED;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,OAAO,KAAa,EAC7B,GAAG,GAAE,MAAM,MAAiB,GAC3B,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,UAAU,CAAA;CAAE,CAAC,CAiDvD"}
1
+ {"version":3,"file":"flow.d.ts","sourceRoot":"","sources":["../../src/auth/flow.ts"],"names":[],"mappings":"AAwCA,OAAO,EAKL,KAAK,aAAa,EAClB,KAAK,UAAU,EAGhB,MAAM,cAAc,CAAC;AAQtB,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,IAAI,EAAG,gBAAgB,CAAU;CAC3C;AAED;8DAC8D;AAC9D,eAAO,MAAM,eAAe,QAAS,CAAC;AAEtC;;gEAEgE;AAChE,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAE/C,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;sDAEkD;IAClD,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,kCAAkC;IAClC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IAChC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACzD;AAED;;;;GAIG;AACH,wBAAsB,KAAK,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,CA8DnE;AAED;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,OAAO,KAAa,EAC7B,GAAG,GAAE,MAAM,MAAiB,EAC5B,KAAK,GAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CACI,GACtC,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,aAAa,CAAA;CAAE,CAAC,CAuD1D"}
@@ -0,0 +1,76 @@
1
+ import { type GatewayCliSession } from "./session.js";
2
+ export declare const CLI_SESSION_AUTH_PATH = "/__cli_session_auth";
3
+ export declare const CLI_SESSION_TOKEN_PATH = "/__cli_session_token";
4
+ export declare const CLI_LOGOUT_PATH = "/__cli_logout";
5
+ /** The gateway is unreachable or not serving the cli-session grant
6
+ * (404 wrong host / 503 config missing / network error). The login
7
+ * command treats this as "fall back to the legacy flow". */
8
+ export declare class GatewayUnavailableError extends Error {
9
+ readonly code: "gateway_unavailable";
10
+ }
11
+ /** Non-2xx from the session-token or logout endpoint. `httpStatus`
12
+ * lets flow.ts distinguish "session dead — re-login" (400/401) from
13
+ * transient server trouble (5xx). */
14
+ export declare class GatewayTokenError extends Error {
15
+ readonly code: "gateway_token_error";
16
+ readonly httpStatus?: number;
17
+ constructor(message: string, httpStatus?: number);
18
+ }
19
+ /** 429 from the gateway. The request was rejected BEFORE the body was
20
+ * read (the gateway rate-limits pre-parse), so a rate-limited refresh
21
+ * has NOT consumed the refresh token — the session is intact and the
22
+ * caller must NOT clear it. */
23
+ export declare class GatewayRateLimitError extends Error {
24
+ readonly code: "gateway_rate_limited";
25
+ readonly retryAfterSec: number;
26
+ constructor(retryAfterSec: number);
27
+ }
28
+ export interface GatewayTokenPair {
29
+ readonly accessToken: string;
30
+ readonly refreshToken: string;
31
+ readonly expiresInSec: number;
32
+ }
33
+ export interface GatewayLoginOptions {
34
+ readonly gatewayUrl: string;
35
+ readonly sessionPath: string;
36
+ /** Receives the auth URL once the localhost server is bound — the
37
+ * CLI verb prints it so the user can copy-paste if the browser
38
+ * doesn't open. */
39
+ readonly onAuthUrl?: (url: string) => void;
40
+ /** Injection points for tests. */
41
+ readonly fetcher?: typeof fetch;
42
+ readonly browserOpener?: (url: string) => Promise<void>;
43
+ }
44
+ /**
45
+ * Run the gateway cli-session login and persist the v2 session.
46
+ *
47
+ * Probes the gateway FIRST (one cheap GET) so an unreachable gateway
48
+ * or a host without the grant fails fast with `GatewayUnavailableError`
49
+ * — before any browser opens — and the verb can fall back to the
50
+ * legacy flow instead of hanging on a callback that will never come.
51
+ */
52
+ export declare function gatewayLogin(opts: GatewayLoginOptions): Promise<GatewayCliSession>;
53
+ /**
54
+ * Refresh-token grant. Returns the new pair — with a ROTATED refresh
55
+ * token the caller MUST persist before first use of the access token
56
+ * (flow.ts owns that ordering). Throws:
57
+ * * GatewayRateLimitError on 429 (token NOT consumed),
58
+ * * GatewayTokenError with httpStatus on any other non-2xx
59
+ * (400/401 = session dead; 5xx = transient),
60
+ * * GatewayTokenError without httpStatus on network failure.
61
+ */
62
+ export declare function refreshGatewayTokens(params: {
63
+ readonly gatewayUrl: string;
64
+ readonly refreshToken: string;
65
+ }, fetcher?: typeof fetch): Promise<GatewayTokenPair>;
66
+ /**
67
+ * `POST /__cli_logout` — server-side revocation. 204 = revoked (or
68
+ * idempotently already-gone). Throws on anything else; `launchpad
69
+ * logout` catches, warns, and clears the local session anyway (logout
70
+ * must work offline).
71
+ */
72
+ export declare function revokeGatewaySession(params: {
73
+ readonly gatewayUrl: string;
74
+ readonly refreshToken: string;
75
+ }, fetcher?: typeof fetch): Promise<void>;
76
+ //# sourceMappingURL=gateway-flow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway-flow.d.ts","sourceRoot":"","sources":["../../src/auth/gateway-flow.ts"],"names":[],"mappings":"AA0CA,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,cAAc,CAAC;AAEtB,eAAO,MAAM,qBAAqB,wBAAwB,CAAC;AAC3D,eAAO,MAAM,sBAAsB,yBAAyB,CAAC;AAC7D,eAAO,MAAM,eAAe,kBAAkB,CAAC;AAE/C;;6DAE6D;AAC7D,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,QAAQ,CAAC,IAAI,EAAG,qBAAqB,CAAU;CAChD;AAED;;sCAEsC;AACtC,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,IAAI,EAAG,qBAAqB,CAAU;IAC/C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;gBACjB,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM;CAKjD;AAED;;;gCAGgC;AAChC,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,IAAI,EAAG,sBAAsB,CAAU;IAChD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;gBACnB,aAAa,EAAE,MAAM;CAOlC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;wBAEoB;IACpB,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,kCAAkC;IAClC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IAChC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACzD;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,iBAAiB,CAAC,CAkD5B;AAED;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE;IAAE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAAE,EACtE,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,gBAAgB,CAAC,CAS3B;AAED;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE;IAAE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAAE,EACtE,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,IAAI,CAAC,CAsBf"}
@@ -1,4 +1,5 @@
1
1
  export declare const SESSION_VERSION = 1;
2
+ export declare const GATEWAY_SESSION_VERSION = 2;
2
3
  export interface CliSession {
3
4
  readonly version: typeof SESSION_VERSION;
4
5
  /** OAuth `access_token`. Sent to the bot as `Authorization: Bearer <accessToken>`. */
@@ -27,6 +28,38 @@ export interface CliSession {
27
28
  * by `whoami` (slice 3) for diagnostic display only. */
28
29
  readonly issuedAt: string;
29
30
  }
31
+ /** A `version: 2` session minted by the auth gateway's cli-session
32
+ * grant (ADR 0026, sp-cli7kq). Differences from the legacy shape:
33
+ * no `clientId`/`tokenEndpoint`/`resource` (the gateway endpoints
34
+ * are fixed — no discovery, no RFC 7591 registration), and the
35
+ * refresh token is OPAQUE and ROTATES on every refresh — a stale
36
+ * copy is not just useless, presenting it after rotation revokes
37
+ * the whole session server-side (the replay tripwire). That makes
38
+ * the write-before-use persistence ordering in `flow.ts`
39
+ * load-bearing, not cosmetic. */
40
+ export interface GatewayCliSession {
41
+ readonly version: typeof GATEWAY_SESSION_VERSION;
42
+ /** Discriminator for future non-gateway v2 shapes; always "gateway". */
43
+ readonly kind: "gateway";
44
+ /** RS256 `typ: cli-session` JWT. Sent to the bot as
45
+ * `Authorization: Bearer <accessToken>`. */
46
+ readonly accessToken: string;
47
+ /** Opaque rotating refresh token (`<jti>.<random>`). NEVER reuse a
48
+ * rotated-away value — the gateway treats reuse as theft. */
49
+ readonly refreshToken: string;
50
+ /** Epoch milliseconds at which `accessToken` ceases to be valid. */
51
+ readonly accessTokenExpiresAt: number;
52
+ /** Base URL of the auth gateway that minted this session — refresh
53
+ * and logout go back to the same issuer. */
54
+ readonly gatewayUrl: string;
55
+ /** ISO-8601 UTC timestamp of when this session was written. */
56
+ readonly issuedAt: string;
57
+ }
58
+ /** Either on-disk session shape. Readers that only need the common
59
+ * fields (accessToken / accessTokenExpiresAt / issuedAt) can stay
60
+ * agnostic; refresh + logout must dispatch on `isGatewaySession`. */
61
+ export type AnyCliSession = CliSession | GatewayCliSession;
62
+ export declare function isGatewaySession(s: AnyCliSession): s is GatewayCliSession;
30
63
  export declare class SessionParseError extends Error {
31
64
  readonly code: "session_parse_error";
32
65
  }
@@ -37,13 +70,13 @@ export declare class SessionParseError extends Error {
37
70
  * than to silently re-prompt for login when the storage is
38
71
  * actually broken.
39
72
  */
40
- export declare function readSession(sessionPath: string): Promise<CliSession | null>;
73
+ export declare function readSession(sessionPath: string): Promise<AnyCliSession | null>;
41
74
  /**
42
75
  * Atomic-ish write: writes to `<path>.tmp` then renames into
43
76
  * place. The whole-directory chmod is best-effort (Windows ignores
44
77
  * mode bits) — the per-file mode is the load-bearing one.
45
78
  */
46
- export declare function writeSession(sessionPath: string, session: CliSession): Promise<void>;
79
+ export declare function writeSession(sessionPath: string, session: AnyCliSession): Promise<void>;
47
80
  /**
48
81
  * `launchpad logout`: zero out the session file. Idempotent —
49
82
  * already-absent file is a no-op success. Returns whether a
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/auth/session.ts"],"names":[],"mappings":"AA0BA,eAAO,MAAM,eAAe,IAAI,CAAC;AAEjC,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,OAAO,EAAE,OAAO,eAAe,CAAC;IACzC,sFAAsF;IACtF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;qDACiD;IACjD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B;wEACoE;IACpE,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC;uCACmC;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;6CACyC;IACzC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B;;;;;;2BAMuB;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;6DACyD;IACzD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,IAAI,EAAG,qBAAqB,CAAU;CAChD;AAED;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAmD5B;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,IAAI,CAAC,CA4Bf;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQxE"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/auth/session.ts"],"names":[],"mappings":"AA2BA,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,OAAO,EAAE,OAAO,eAAe,CAAC;IACzC,sFAAsF;IACtF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;qDACiD;IACjD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B;wEACoE;IACpE,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC;uCACmC;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;6CACyC;IACzC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B;;;;;;2BAMuB;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;6DACyD;IACzD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;kCAQkC;AAClC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,OAAO,uBAAuB,CAAC;IACjD,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB;iDAC6C;IAC7C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;kEAC8D;IAC9D,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,oEAAoE;IACpE,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC;iDAC6C;IAC7C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,+DAA+D;IAC/D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED;;sEAEsE;AACtE,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,iBAAiB,CAAC;AAE3D,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,aAAa,GAAG,CAAC,IAAI,iBAAiB,CAEzE;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,IAAI,EAAG,qBAAqB,CAAU;CAChD;AAED;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAsD/B;AA0BD;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,IAAI,CAAC,CA4Bf;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQxE"}