@rebasepro/cli 0.25.0 → 0.26.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/README.md +2 -1
- package/bin/rebase.js +143 -4
- package/dist/{agent-setup-B5AKHOgt.js → agent-setup-D9mHRZ5R.js} +125 -3
- package/dist/agent-setup-D9mHRZ5R.js.map +1 -0
- package/dist/{api-keys-Tm7zvAPe.js → api-keys-CyJ26KEJ.js} +41 -10
- package/dist/api-keys-CyJ26KEJ.js.map +1 -0
- package/dist/{apps-BsU_ILbP.js → apps-CnLDV5b6.js} +6 -7
- package/dist/{apps-BsU_ILbP.js.map → apps-CnLDV5b6.js.map} +1 -1
- package/dist/{atlas-dev-database-OWtqnG7W.js → atlas-dev-database-BODqdCWt.js} +4 -4
- package/dist/{atlas-dev-database-OWtqnG7W.js.map → atlas-dev-database-BODqdCWt.js.map} +1 -1
- package/dist/{auth-Vl2hFKXP.js → auth-dII2jCO1.js} +10 -8
- package/dist/auth-dII2jCO1.js.map +1 -0
- package/dist/{branch-pointer-9DnwkFJJ.js → branch-pointer-BQUF6lYa.js} +1 -1
- package/dist/{build-BBgT8EJZ.js → build-Cza7ZQrp.js} +6 -7
- package/dist/{build-BBgT8EJZ.js.map → build-Cza7ZQrp.js.map} +1 -1
- package/dist/{bundle-DtCCCr4w.js → bundle-C6yEtMJ-.js} +53 -5
- package/dist/bundle-C6yEtMJ-.js.map +1 -0
- package/dist/bundle.d.ts +14 -0
- package/dist/{cloud-BpH0_bmV.js → cloud-BdnmqQaZ.js} +8528 -4342
- package/dist/cloud-BdnmqQaZ.js.map +1 -0
- package/dist/collections-dir-DlvDhime.js +69 -0
- package/dist/collections-dir-DlvDhime.js.map +1 -0
- package/dist/commands/agent-setup.d.ts +58 -0
- package/dist/commands/api-keys.d.ts +13 -0
- package/dist/commands/cloud/activity.d.ts +25 -0
- package/dist/commands/cloud/app-key.d.ts +18 -0
- package/dist/commands/cloud/auth.d.ts +1 -0
- package/dist/commands/cloud/billing-invoices.d.ts +39 -0
- package/dist/commands/cloud/claim.d.ts +7 -0
- package/dist/commands/cloud/context.d.ts +115 -6
- package/dist/commands/cloud/deploy-summary.d.ts +162 -0
- package/dist/commands/cloud/deploy.d.ts +64 -0
- package/dist/commands/cloud/env.d.ts +24 -0
- package/dist/commands/cloud/index.d.ts +13 -1
- package/dist/commands/cloud/init.d.ts +143 -0
- package/dist/commands/cloud/login-flow.d.ts +51 -0
- package/dist/commands/cloud/notifications.d.ts +28 -0
- package/dist/commands/cloud/orgs.d.ts +13 -0
- package/dist/commands/cloud/previews.d.ts +123 -0
- package/dist/commands/cloud/resources.d.ts +68 -14
- package/dist/commands/cloud/runtime-logs.d.ts +176 -0
- package/dist/commands/cloud/service-key.d.ts +15 -0
- package/dist/commands/cloud/token-capabilities.d.ts +61 -8
- package/dist/commands/cloud/tokens.d.ts +18 -3
- package/dist/commands/cloud/usage.d.ts +63 -0
- package/dist/commands/dev.d.ts +22 -0
- package/dist/commands/doctor.d.ts +1 -1
- package/dist/commands/generate-view.d.ts +94 -0
- package/dist/commands/generate_sdk.d.ts +19 -2
- package/dist/commands/init.d.ts +20 -0
- package/dist/commands/skills.d.ts +4 -3
- package/dist/commands/start.d.ts +10 -0
- package/dist/commands/storage.d.ts +4 -0
- package/dist/commands/upgrade.d.ts +12 -0
- package/dist/commands/verify.d.ts +96 -0
- package/dist/{consent-ClRHmcxf.js → consent-BD7F29T3.js} +2 -2
- package/dist/{consent-ClRHmcxf.js.map → consent-BD7F29T3.js.map} +1 -1
- package/dist/{console-CEGm0XNc.js → console-BXB6-8GT.js} +12 -6
- package/dist/console-BXB6-8GT.js.map +1 -0
- package/dist/{context-CyflUrKg.js → context-GUxkZm-S.js} +482 -26
- package/dist/context-GUxkZm-S.js.map +1 -0
- package/dist/{daemon-CknkDjd6.js → daemon-Cp7oNXth.js} +160 -5
- package/dist/daemon-Cp7oNXth.js.map +1 -0
- package/dist/{daemon-entry-CbLRrgzy.js → daemon-entry-zOC1revO.js} +6 -3
- package/dist/daemon-entry-zOC1revO.js.map +1 -0
- package/dist/{db-CRNpA5mH.js → db-KzlqddGD.js} +101 -54
- package/dist/db-KzlqddGD.js.map +1 -0
- package/dist/{derive-CsoFGAnK.js → derive-ae9UOoOB.js} +2 -2
- package/dist/{derive-CsoFGAnK.js.map → derive-ae9UOoOB.js.map} +1 -1
- package/dist/{dev-C0pbMmhS.js → dev-DTqVMgB3.js} +231 -44
- package/dist/dev-DTqVMgB3.js.map +1 -0
- package/dist/dev-db/daemon.d.ts +41 -0
- package/dist/dev-db/pull.d.ts +33 -5
- package/dist/dev-db/state.d.ts +9 -0
- package/dist/{dev-preflight-unHpiYO1.js → dev-preflight-Cxv3TiD1.js} +73 -27
- package/dist/dev-preflight-Cxv3TiD1.js.map +1 -0
- package/dist/{dev-server-B1UYifZJ.js → dev-server-OJsUGZEB.js} +14 -3
- package/dist/dev-server-OJsUGZEB.js.map +1 -0
- package/dist/{doctor-CATRdWm6.js → doctor-BRWOlqeG.js} +14 -12
- package/dist/doctor-BRWOlqeG.js.map +1 -0
- package/dist/{eject-DuasTr-F.js → eject-DABcDnPN.js} +3 -4
- package/dist/{eject-DuasTr-F.js.map → eject-DABcDnPN.js.map} +1 -1
- package/dist/{fold-static-hDHtDfTq.js → fold-static-BnAK-anQ.js} +3 -3
- package/dist/{fold-static-hDHtDfTq.js.map → fold-static-BnAK-anQ.js.map} +1 -1
- package/dist/generate-view-IFwCADvb.js +662 -0
- package/dist/generate-view-IFwCADvb.js.map +1 -0
- package/dist/{generate_sdk-Cvg5jjJF.js → generate_sdk-BvcWvoUh.js} +49 -13
- package/dist/generate_sdk-BvcWvoUh.js.map +1 -0
- package/dist/index.es.js +58 -40
- package/dist/index.es.js.map +1 -1
- package/dist/{init-BWAwHPtz.js → init-CaU6GhwI.js} +50 -62
- package/dist/init-CaU6GhwI.js.map +1 -0
- package/dist/invoked-command-CG_rIa_o.js +55 -0
- package/dist/invoked-command-CG_rIa_o.js.map +1 -0
- package/dist/last-dev-database-DpW5dfoX.js +2 -0
- package/dist/{manifest-DRp0UVTj.js → manifest-BtzDy7KQ.js} +36 -3
- package/dist/{manifest-DRp0UVTj.js.map → manifest-BtzDy7KQ.js.map} +1 -1
- package/dist/{normalize-imports-BoBVh5tU.js → normalize-imports-D0zXlM6g.js} +3 -3
- package/dist/{normalize-imports-BoBVh5tU.js.map → normalize-imports-D0zXlM6g.js.map} +1 -1
- package/dist/{path-flags-BFgG9V6y.js → path-flags-DNvtEi2-.js} +5 -4
- package/dist/path-flags-DNvtEi2-.js.map +1 -0
- package/dist/{prepare-DdnU70z8.js → prepare-CTw9NEkh.js} +5 -4
- package/dist/{prepare-DdnU70z8.js.map → prepare-CTw9NEkh.js.map} +1 -1
- package/dist/project-CasK56vN.js +701 -0
- package/dist/project-CasK56vN.js.map +1 -0
- package/dist/{pull-C3PXduzl.js → pull-DDMpsZ5r.js} +131 -24
- package/dist/pull-DDMpsZ5r.js.map +1 -0
- package/dist/refusal-ReeeGP8a.js +26 -0
- package/dist/refusal-ReeeGP8a.js.map +1 -0
- package/dist/resolve-D647F1LA.js +2 -0
- package/dist/{resources-BdNRDEAy.js → resources-CPSC5Kcu.js} +4 -5
- package/dist/{resources-BdNRDEAy.js.map → resources-CPSC5Kcu.js.map} +1 -1
- package/dist/{schema-B0-p6T1O.js → schema-BSXwZSiI.js} +9 -8
- package/dist/schema-BSXwZSiI.js.map +1 -0
- package/dist/{skills-DfnTeyRz.js → skills-BU2flMOy.js} +52 -8
- package/dist/skills-BU2flMOy.js.map +1 -0
- package/dist/{start-BmEZkS0n.js → start-mVb6upwY.js} +20 -7
- package/dist/start-mVb6upwY.js.map +1 -0
- package/dist/{state-CCB-FAQ9.js → state-CYdBvnwA.js} +3 -2
- package/dist/state-CYdBvnwA.js.map +1 -0
- package/dist/static-syntax-check.d.ts +64 -0
- package/dist/{status-BJ26yZOo.js → status-BREvtzJm.js} +8 -9
- package/dist/{status-BJ26yZOo.js.map → status-BREvtzJm.js.map} +1 -1
- package/dist/{storage-DKPUBTWG.js → storage-DtOXGflp.js} +159 -16
- package/dist/storage-DtOXGflp.js.map +1 -0
- package/dist/{telemetry-EMFRVRQi.js → telemetry-CgwJPVMO.js} +2 -2
- package/dist/{telemetry-EMFRVRQi.js.map → telemetry-CgwJPVMO.js.map} +1 -1
- package/dist/{telemetry-m9czTNGR.js → telemetry-DwK5RQJl.js} +4 -4
- package/dist/{telemetry-m9czTNGR.js.map → telemetry-DwK5RQJl.js.map} +1 -1
- package/dist/{upgrade-DjihWuZL.js → upgrade-B7sVwL8S.js} +26 -4
- package/dist/{upgrade-DjihWuZL.js.map → upgrade-B7sVwL8S.js.map} +1 -1
- package/dist/utils/args.d.ts +32 -3
- package/dist/utils/boot-retry.d.ts +41 -0
- package/dist/utils/collections-dir.d.ts +14 -0
- package/dist/utils/dev-preflight.d.ts +34 -1
- package/dist/utils/dev-server.d.ts +11 -0
- package/dist/utils/path-flags.d.ts +2 -1
- package/dist/utils/project.d.ts +5 -1
- package/dist/utils/tool-stdio.d.ts +1 -1
- package/dist/utils/unknown-command.d.ts +4 -0
- package/dist/verify-D7UsV_qe.js +730 -0
- package/dist/verify-D7UsV_qe.js.map +1 -0
- package/package.json +9 -7
- package/templates/eject/backend/src/index.ts +9 -1
- package/templates/generated-schemas/ecommerce.ts +3 -0
- package/templates/overlays/baas/ai-instructions.md +13 -0
- package/templates/resource-graphs/blank.json +25 -0
- package/templates/resource-graphs/blog.json +28 -0
- package/templates/resource-graphs/ecommerce.json +28 -0
- package/templates/resource-graphs/headless.json +22 -0
- package/templates/template/.claude/hooks/post-edit-collection.sh +99 -0
- package/templates/template/.claude/settings.json +17 -0
- package/templates/template/.env.example +15 -5
- package/templates/template/ai-instructions.md +21 -1
- package/templates/template/config/collections/presets/ecommerce/index.ts +23 -0
- package/templates/template/docker-compose.yml +4 -1
- package/templates/template/frontend/package.json +1 -0
- package/templates/template/frontend/src/App.tsx +10 -9
- package/templates/views/AppView.tsx.tmpl +138 -0
- package/templates/views/EntityView.tsx.tmpl +64 -0
- package/templates/views/index.ts.tmpl +8 -0
- package/dist/agent-setup-B5AKHOgt.js.map +0 -1
- package/dist/api-keys-Tm7zvAPe.js.map +0 -1
- package/dist/args-BX0YD14a.js +0 -266
- package/dist/args-BX0YD14a.js.map +0 -1
- package/dist/auth-Vl2hFKXP.js.map +0 -1
- package/dist/bundle-DtCCCr4w.js.map +0 -1
- package/dist/cloud-BpH0_bmV.js.map +0 -1
- package/dist/console-CEGm0XNc.js.map +0 -1
- package/dist/context-CyflUrKg.js.map +0 -1
- package/dist/daemon-CknkDjd6.js.map +0 -1
- package/dist/daemon-entry-CbLRrgzy.js.map +0 -1
- package/dist/db-CRNpA5mH.js.map +0 -1
- package/dist/dev-C0pbMmhS.js.map +0 -1
- package/dist/dev-preflight-unHpiYO1.js.map +0 -1
- package/dist/dev-server-B1UYifZJ.js.map +0 -1
- package/dist/doctor-CATRdWm6.js.map +0 -1
- package/dist/generate_sdk-Cvg5jjJF.js.map +0 -1
- package/dist/init-BWAwHPtz.js.map +0 -1
- package/dist/last-dev-database-c_dKz1S3.js +0 -2
- package/dist/path-flags-BFgG9V6y.js.map +0 -1
- package/dist/project-Wp2zoyeN.js +0 -349
- package/dist/project-Wp2zoyeN.js.map +0 -1
- package/dist/pull-C3PXduzl.js.map +0 -1
- package/dist/resolve-CNQjsatH.js +0 -2
- package/dist/schema-B0-p6T1O.js.map +0 -1
- package/dist/skills-DfnTeyRz.js.map +0 -1
- package/dist/start-BmEZkS0n.js.map +0 -1
- package/dist/state-CCB-FAQ9.js.map +0 -1
- package/dist/storage-DKPUBTWG.js.map +0 -1
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
|
|
3
1
|
import "@fontsource/jetbrains-mono";
|
|
4
2
|
import "@fontsource-variable/inter";
|
|
5
3
|
import "@fontsource-variable/instrument-sans";
|
|
@@ -19,14 +17,17 @@ import { collections } from "virtual:rebase-collections";
|
|
|
19
17
|
const API_URL = import.meta.env.VITE_API_URL;
|
|
20
18
|
const GOOGLE_CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID;
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
// One client for the page, created when this module loads. Inside a component,
|
|
21
|
+
// even in a `useMemo`, `<React.StrictMode>` builds a second one in development,
|
|
22
|
+
// and both restore the session and open their own connections.
|
|
23
|
+
const rebaseClient = createRebaseClient({
|
|
24
|
+
baseUrl: API_URL,
|
|
25
|
+
// Store the refresh token in an httpOnly cookie (XSS-safe) rather than
|
|
26
|
+
// localStorage. The backend issues it via `auth.cookieAuth`.
|
|
27
|
+
auth: { authFlowMode: "cookie" }
|
|
28
|
+
});
|
|
29
29
|
|
|
30
|
+
export function App() {
|
|
30
31
|
const authController = useRebaseAuthController({
|
|
31
32
|
client: rebaseClient,
|
|
32
33
|
googleClientId: GOOGLE_CLIENT_ID
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* __LABEL__ — a screen of the admin panel, at /__SLUG__.
|
|
3
|
+
*
|
|
4
|
+
* Scaffolded by `rebase generate view __SLUG__` and listed in `./index.ts`.
|
|
5
|
+
* It renders inside the admin shell, so the signed-in user, the sidebar, the
|
|
6
|
+
* router and the data layer are already there: read and write through
|
|
7
|
+
* `useData()` and the collection's security rules and callbacks apply.
|
|
8
|
+
*
|
|
9
|
+
* Replace the body with your screen. The `rebase-custom-views` agent skill
|
|
10
|
+
* documents the contract and the design tokens.
|
|
11
|
+
*/
|
|
12
|
+
import { useCallback, useEffect, useState } from "react";
|
|
13
|
+
import type { Entity } from "@rebasepro/types";
|
|
14
|
+
import { useAuthController, useData, useSnackbarController } from "@rebasepro/app";
|
|
15
|
+
import {
|
|
16
|
+
Button,
|
|
17
|
+
Card,
|
|
18
|
+
Chip,
|
|
19
|
+
CircularProgressCenter,
|
|
20
|
+
Container,
|
|
21
|
+
Sheet,
|
|
22
|
+
Tab,
|
|
23
|
+
Table,
|
|
24
|
+
TableBody,
|
|
25
|
+
TableCell,
|
|
26
|
+
TableHeader,
|
|
27
|
+
TableRow,
|
|
28
|
+
Tabs,
|
|
29
|
+
Typography
|
|
30
|
+
} from "@rebasepro/ui";
|
|
31
|
+
|
|
32
|
+
/** The collection this screen reads. Change it to the one you need. */
|
|
33
|
+
const COLLECTION = "__COLLECTION__";
|
|
34
|
+
|
|
35
|
+
export function __COMPONENT__() {
|
|
36
|
+
const data = useData();
|
|
37
|
+
const { user } = useAuthController();
|
|
38
|
+
const snackbar = useSnackbarController();
|
|
39
|
+
|
|
40
|
+
const [rows, setRows] = useState<Entity[] | undefined>();
|
|
41
|
+
const [error, setError] = useState<string | undefined>();
|
|
42
|
+
const [tab, setTab] = useState("rows");
|
|
43
|
+
const [selected, setSelected] = useState<Entity | undefined>();
|
|
44
|
+
|
|
45
|
+
const load = useCallback(async () => {
|
|
46
|
+
try {
|
|
47
|
+
const { data: found } = await data.collection(COLLECTION).find({ limit: 20 });
|
|
48
|
+
setRows(found);
|
|
49
|
+
setError(undefined);
|
|
50
|
+
} catch (e: unknown) {
|
|
51
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
52
|
+
}
|
|
53
|
+
}, [data]);
|
|
54
|
+
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
void load();
|
|
57
|
+
}, [load]);
|
|
58
|
+
|
|
59
|
+
const columns = rows?.[0] ? Object.keys(rows[0].values).slice(0, 3) : [];
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<Container maxWidth="6xl" className="py-8 flex flex-col gap-4">
|
|
63
|
+
<div className="flex items-start justify-between gap-4">
|
|
64
|
+
<div className="flex flex-col gap-1">
|
|
65
|
+
<Typography variant="h5">__LABEL_JSX__</Typography>
|
|
66
|
+
<Typography variant="body2" color="secondary">
|
|
67
|
+
Signed in as {user?.displayName ?? user?.email ?? "an anonymous user"}
|
|
68
|
+
</Typography>
|
|
69
|
+
</div>
|
|
70
|
+
<Button color="primary" onClick={() => load().then(() => snackbar.open({ type: "success", message: "Refreshed" }))}>
|
|
71
|
+
Refresh
|
|
72
|
+
</Button>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<Tabs value={tab} onValueChange={setTab}>
|
|
76
|
+
<Tab value="rows">Rows</Tab>
|
|
77
|
+
<Tab value="summary">Summary</Tab>
|
|
78
|
+
</Tabs>
|
|
79
|
+
|
|
80
|
+
{error && <Typography color="error">{error}</Typography>}
|
|
81
|
+
{!error && !rows && <CircularProgressCenter/>}
|
|
82
|
+
|
|
83
|
+
{rows && tab === "rows" && (
|
|
84
|
+
<Card className="overflow-hidden">
|
|
85
|
+
<Table>
|
|
86
|
+
<TableHeader>
|
|
87
|
+
<TableCell>id</TableCell>
|
|
88
|
+
{columns.map(column => <TableCell key={column}>{column}</TableCell>)}
|
|
89
|
+
</TableHeader>
|
|
90
|
+
<TableBody>
|
|
91
|
+
{rows.map(row => (
|
|
92
|
+
<TableRow key={String(row.id)} onClick={() => setSelected(row)}>
|
|
93
|
+
<TableCell>{String(row.id)}</TableCell>
|
|
94
|
+
{columns.map(column => <TableCell key={column}>{display(row.values[column])}</TableCell>)}
|
|
95
|
+
</TableRow>
|
|
96
|
+
))}
|
|
97
|
+
</TableBody>
|
|
98
|
+
</Table>
|
|
99
|
+
</Card>
|
|
100
|
+
)}
|
|
101
|
+
|
|
102
|
+
{rows && tab === "summary" && (
|
|
103
|
+
<div className="grid grid-cols-2 gap-4">
|
|
104
|
+
<Card className="p-4 flex flex-col gap-1">
|
|
105
|
+
<Typography variant="caption" color="secondary">Rows loaded</Typography>
|
|
106
|
+
<Typography variant="stat">{rows.length}</Typography>
|
|
107
|
+
</Card>
|
|
108
|
+
<Card className="p-4 flex flex-col gap-2 items-start">
|
|
109
|
+
<Typography variant="caption" color="secondary">Collection</Typography>
|
|
110
|
+
<Chip size="small">{COLLECTION}</Chip>
|
|
111
|
+
</Card>
|
|
112
|
+
</div>
|
|
113
|
+
)}
|
|
114
|
+
|
|
115
|
+
<Sheet open={selected !== undefined} onOpenChange={open => !open && setSelected(undefined)} title="Row">
|
|
116
|
+
<div className="w-[28rem] max-w-full h-full overflow-auto p-6 flex flex-col gap-3">
|
|
117
|
+
<Typography variant="h6">{selected ? String(selected.id) : ""}</Typography>
|
|
118
|
+
{selected && Object.entries(selected.values).map(([key, value]) => (
|
|
119
|
+
<div key={key} className="flex flex-col gap-0.5">
|
|
120
|
+
<Typography variant="caption" color="secondary">{key}</Typography>
|
|
121
|
+
<Typography variant="body2">{display(value)}</Typography>
|
|
122
|
+
</div>
|
|
123
|
+
))}
|
|
124
|
+
</div>
|
|
125
|
+
</Sheet>
|
|
126
|
+
</Container>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** A cell's text. Dates in `values` are `Date` objects in the admin. */
|
|
131
|
+
function display(value: unknown): string {
|
|
132
|
+
if (value === null || value === undefined) return "—";
|
|
133
|
+
if (value instanceof Date) return value.toLocaleString();
|
|
134
|
+
if (typeof value === "object") return JSON.stringify(value);
|
|
135
|
+
return String(value);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export default __COMPONENT__;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* __LABEL__ — a tab on every record of the `__COLLECTION__` collection.
|
|
3
|
+
*
|
|
4
|
+
* Scaffolded by `rebase generate view __SLUG__ --collection __COLLECTION__` and
|
|
5
|
+
* listed in that collection's `admin.entityViews` by path, so the backend,
|
|
6
|
+
* which reads the same collection file, never loads this one. The admin
|
|
7
|
+
* imports it lazily and renders the default export.
|
|
8
|
+
*
|
|
9
|
+
* The admin hands the component `EntityCustomViewParams`: the collection, the
|
|
10
|
+
* record (`entity`, absent while it is being created), its unsaved edits
|
|
11
|
+
* (`modifiedValues`) and the form (`formContext`). The `rebase-custom-views`
|
|
12
|
+
* agent skill documents the contract and the design tokens.
|
|
13
|
+
*/
|
|
14
|
+
import type { EntityCustomViewParams } from "@rebasepro/cms-types";
|
|
15
|
+
import { useSnackbarController } from "@rebasepro/app";
|
|
16
|
+
import { Button, Card, Chip, Typography } from "@rebasepro/ui";
|
|
17
|
+
|
|
18
|
+
export function __COMPONENT__({ collection, entity, modifiedValues, formContext }: EntityCustomViewParams) {
|
|
19
|
+
const snackbar = useSnackbarController();
|
|
20
|
+
const values = modifiedValues ?? entity?.values ?? {};
|
|
21
|
+
|
|
22
|
+
if (!entity) {
|
|
23
|
+
return (
|
|
24
|
+
<div className="p-6">
|
|
25
|
+
<Typography variant="body2" color="secondary">
|
|
26
|
+
Save this {collection.singularName ?? collection.name} to see __label_jsx__.
|
|
27
|
+
</Typography>
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const copy = () => navigator.clipboard.writeText(JSON.stringify(values, null, 2))
|
|
33
|
+
.then(() => snackbar.open({ type: "success", message: "Copied" }));
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div className="p-6 flex flex-col gap-4">
|
|
37
|
+
<div className="flex items-center justify-between gap-2">
|
|
38
|
+
<Typography variant="h6">__LABEL_JSX__</Typography>
|
|
39
|
+
{formContext.readOnly && <Chip size="small">Read only</Chip>}
|
|
40
|
+
</div>
|
|
41
|
+
<Card className="p-4 flex flex-col gap-3">
|
|
42
|
+
{Object.entries(values).map(([key, value]) => (
|
|
43
|
+
<div key={key} className="flex flex-col gap-0.5">
|
|
44
|
+
<Typography variant="caption" color="secondary">{key}</Typography>
|
|
45
|
+
<Typography variant="body2">{display(value)}</Typography>
|
|
46
|
+
</div>
|
|
47
|
+
))}
|
|
48
|
+
</Card>
|
|
49
|
+
<div>
|
|
50
|
+
<Button color="neutral" variant="outlined" onClick={copy}>Copy as JSON</Button>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** A field's text. Dates in `values` are `Date` objects in the admin. */
|
|
57
|
+
function display(value: unknown): string {
|
|
58
|
+
if (value === null || value === undefined) return "—";
|
|
59
|
+
if (value instanceof Date) return value.toLocaleString();
|
|
60
|
+
if (typeof value === "object") return JSON.stringify(value);
|
|
61
|
+
return String(value);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export default __COMPONENT__;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The admin's own screens, handed to `<RebaseCMS views>` in `App.tsx`.
|
|
3
|
+
*
|
|
4
|
+
* `rebase generate view <slug>` adds a component file beside this one and an
|
|
5
|
+
* entry here. Edit the entries freely: `name` is the sidebar label, `icon` a
|
|
6
|
+
* Lucide name, `group` a sidebar section, `roles` who may see the screen.
|
|
7
|
+
*/
|
|
8
|
+
import type { AppView } from "@rebasepro/cms-types";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"agent-setup-B5AKHOgt.js","names":[],"sources":["../src/commands/agent-setup.ts"],"sourcesContent":["/**\n * Setting a project up for the AI coding assistants a developer uses: the\n * Rebase skills, and the Rebase MCP server in each assistant's own config.\n *\n * `rebase init` used to end by *printing* `rebase skills install`, and nothing\n * ran it — so a generated project had pointer files telling every assistant to\n * read skills that were not there, and an MCP server only Claude Code knew\n * about. This asks once, while the project is being made, pre-ticking the\n * assistants installed on the machine.\n */\nimport chalk from \"chalk\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport inquirer from \"inquirer\";\nimport {\n AGENT_KEYS,\n AGENTS,\n type AgentKey,\n detectInstalledAgents,\n installSkills,\n type LoadedSkill,\n loadBundledSkills\n} from \"./skills\";\nimport { detectPackageManager, type PackageManager } from \"../utils/package-manager\";\nimport { cliVersion } from \"../utils/version\";\n\n/** The name the server is registered under, in every config. */\nexport const MCP_SERVER_NAME = \"rebase\";\n\n/** The MCP server's package: a devDependency of the project, pinned with the rest. */\nexport const MCP_PACKAGE = \"@rebasepro/mcp\";\n\n/** The bin {@link MCP_PACKAGE} declares. */\nexport const MCP_BIN = \"rebase-mcp\";\n\n/** The command an assistant spawns, and its arguments. */\nexport interface McpLaunch {\n command: string;\n args: string[];\n}\n\n/**\n * How an assistant starts the Rebase MCP server: the project's own copy, through\n * the project's package manager.\n *\n * Not `npx -y @rebasepro/mcp`, which fetched the newest release on every first\n * start. The server drives the project's own CLI (`pnpm exec rebase …`) and calls\n * the project's own backend (`/api/admin/schema/plan`, …), whose versions the\n * lockfile pins — so a server from the registry ran ahead of both, at every\n * start, and downloaded before it could answer. `@rebasepro/mcp` is a\n * devDependency of the scaffold, pinned to the CLI's version like every other\n * `@rebasepro` package, and this runs that copy.\n *\n * `npx --no`, not `npx`: an assistant spawns the server with no TTY, and npx\n * then assumes \"yes\" and installs a missing package from the registry under the\n * name it was given. `--no` makes a missing install an error instead.\n */\nexport function mcpLaunch(pm: PackageManager): McpLaunch {\n return pm === \"npm\"\n ? { command: \"npx\", args: [\"--no\", MCP_BIN] }\n : { command: \"pnpm\", args: [\"exec\", MCP_BIN] };\n}\n\n/** What writing one MCP config did. */\nexport type McpWriteStatus =\n /** The file was created, or the server was added beside the ones in it. */\n | \"added\"\n /** A `rebase` server was already there. It is left exactly as it was. */\n | \"present\"\n /** The file exists but is not something this can edit without losing data. */\n | \"unreadable\";\n\nexport interface McpWriteResult {\n /** Project-relative path of the config file. */\n file: string;\n status: McpWriteStatus;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Add the server to a JSON config under `key`, keeping everything else in it.\n *\n * A file that does not parse as plain JSON is left alone: VS Code and Gemini\n * both accept comments in these files, and rewriting one through\n * `JSON.stringify` would delete them. Refusing costs one line of hand-editing;\n * the alternative silently costs the developer theirs.\n */\nfunction addJsonServer(filePath: string, key: string, entry: Record<string, unknown>): McpWriteStatus {\n let doc: Record<string, unknown> = {};\n if (fs.existsSync(filePath)) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(fs.readFileSync(filePath, \"utf-8\"));\n } catch {\n return \"unreadable\";\n }\n if (!isRecord(parsed)) return \"unreadable\";\n doc = parsed;\n }\n\n const servers = doc[key] ?? {};\n if (!isRecord(servers)) return \"unreadable\";\n if (MCP_SERVER_NAME in servers) return \"present\";\n\n doc[key] = { ...servers, [MCP_SERVER_NAME]: entry };\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, `${JSON.stringify(doc, null, 2)}\\n`, \"utf-8\");\n return \"added\";\n}\n\n/**\n * The Codex block, as TOML. Appended rather than merged: there is no TOML\n * parser here, and an append keeps every byte already in the file.\n */\nexport function renderCodexServer(projectDir: string, launch: McpLaunch): string {\n // A JSON string literal is a valid TOML basic string for these values.\n const str = (v: string) => JSON.stringify(v);\n return [\n `[mcp_servers.${MCP_SERVER_NAME}]`,\n `command = ${str(launch.command)}`,\n `args = [${launch.args.map(str).join(\", \")}]`,\n \"\",\n `[mcp_servers.${MCP_SERVER_NAME}.env]`,\n `REBASE_PROJECT_DIR = ${str(projectDir)}`,\n \"\"\n ].join(\"\\n\");\n}\n\nfunction addCodexServer(filePath: string, projectDir: string, launch: McpLaunch): McpWriteStatus {\n const existing = fs.existsSync(filePath) ? fs.readFileSync(filePath, \"utf-8\") : \"\";\n const table = new RegExp(`^\\\\s*\\\\[mcp_servers\\\\.(?:${MCP_SERVER_NAME}|\"${MCP_SERVER_NAME}\")\\\\]`, \"m\");\n if (table.test(existing)) return \"present\";\n\n const separator = existing === \"\" ? \"\" : existing.endsWith(\"\\n\") ? \"\\n\" : \"\\n\\n\";\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, `${existing}${separator}${renderCodexServer(projectDir, launch)}`, \"utf-8\");\n return \"added\";\n}\n\n/**\n * Register the Rebase MCP server in the agent's project-level config, started\n * through the project's package manager (see {@link mcpLaunch}).\n *\n * Returns null for an agent with no project-level config to write.\n */\nexport function writeMcpConfig(\n agentKey: AgentKey,\n projectDir: string,\n pm: PackageManager = detectPackageManager(projectDir)\n): McpWriteResult | null {\n const mcp = AGENTS[agentKey].mcp;\n if (!mcp) return null;\n\n const filePath = path.join(projectDir, mcp.file);\n const env = { REBASE_PROJECT_DIR: mcp.projectDir };\n const { command, args } = mcpLaunch(pm);\n const write = (): McpWriteStatus => {\n switch (mcp.format) {\n case \"mcpServers\":\n return addJsonServer(filePath, \"mcpServers\", { command, args, env });\n case \"vscode\":\n // VS Code's own shape: `servers`, and an explicit transport.\n return addJsonServer(filePath, \"servers\", { type: \"stdio\", command, args, env });\n case \"codexToml\":\n return addCodexServer(filePath, mcp.projectDir, { command, args });\n }\n };\n return { file: mcp.file, status: write() };\n}\n\n/** What making sure the project depends on the MCP server did. */\nexport type McpDependencyStatus =\n /** Added to `devDependencies`, at this CLI's version: an install is due. */\n | \"added\"\n /** Already a dependency. Left at the version the project chose. */\n | \"present\"\n /** No `package.json`, or one that is not plain JSON: left alone. */\n | \"unreadable\";\n\n/**\n * Make the project depend on {@link MCP_PACKAGE}, which the config written by\n * {@link writeMcpConfig} runs from the project's own install.\n *\n * Pinned to this CLI's version, as `rebase init` pins every `@rebasepro`\n * package; a project that already lists it keeps its own choice.\n */\nexport function ensureMcpDependency(projectDir: string): McpDependencyStatus {\n const manifestPath = path.join(projectDir, \"package.json\");\n if (!fs.existsSync(manifestPath)) return \"unreadable\";\n const text = fs.readFileSync(manifestPath, \"utf-8\");\n let manifest: unknown;\n try {\n manifest = JSON.parse(text);\n } catch {\n return \"unreadable\";\n }\n if (!isRecord(manifest)) return \"unreadable\";\n for (const field of [\"dependencies\", \"devDependencies\"] as const) {\n const deps = manifest[field];\n if (isRecord(deps) && MCP_PACKAGE in deps) return \"present\";\n }\n const devDependencies = isRecord(manifest.devDependencies) ? manifest.devDependencies : {};\n const version = cliVersion();\n manifest.devDependencies = { ...devDependencies, [MCP_PACKAGE]: version === \"unknown\" ? \"latest\" : version };\n // The file's own indentation, so the change is one line in a diff.\n const indent = /\\n([ \\t]+)\"/.exec(text)?.[1] ?? \" \";\n fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, indent)}\\n`, \"utf-8\");\n return \"added\";\n}\n\n/**\n * Point the scaffold's `.mcp.json` at the project's package manager.\n *\n * The template ships the pnpm spelling (`pnpm exec rebase-mcp`); an npm project\n * has no pnpm to run it with. Only the entry the template wrote is rewritten.\n */\nexport function retargetScaffoldMcpConfig(projectDir: string, pm: PackageManager): void {\n const filePath = path.join(projectDir, \".mcp.json\");\n if (!fs.existsSync(filePath)) return;\n let doc: unknown;\n try {\n doc = JSON.parse(fs.readFileSync(filePath, \"utf-8\"));\n } catch {\n return;\n }\n const servers = isRecord(doc) ? doc.mcpServers : undefined;\n const entry = isRecord(servers) ? servers[MCP_SERVER_NAME] : undefined;\n const shipped = mcpLaunch(\"pnpm\");\n if (!isRecord(doc) || !isRecord(servers) || !isRecord(entry)) return;\n if (entry.command !== shipped.command || JSON.stringify(entry.args) !== JSON.stringify(shipped.args)) return;\n const wanted = mcpLaunch(pm);\n if (wanted.command === shipped.command && JSON.stringify(wanted.args) === JSON.stringify(shipped.args)) return;\n servers[MCP_SERVER_NAME] = { ...entry, command: wanted.command, args: wanted.args };\n fs.writeFileSync(filePath, `${JSON.stringify(doc, null, 4)}\\n`, \"utf-8\");\n}\n\n/**\n * Whether the agent's project-level config already registers the Rebase\n * server: `null` for an agent that has no such config, `false` when the file\n * is missing or does not name it. Reads only — for the hint `rebase skills\n * install` prints when it was not asked to write one.\n */\nexport function mcpServerRegistered(agentKey: AgentKey, projectDir: string): boolean | null {\n const mcp = AGENTS[agentKey].mcp;\n if (!mcp) return null;\n const filePath = path.join(projectDir, mcp.file);\n if (!fs.existsSync(filePath)) return false;\n const text = fs.readFileSync(filePath, \"utf-8\");\n if (mcp.format === \"codexToml\") {\n return new RegExp(`^\\\\s*\\\\[mcp_servers\\\\.(?:${MCP_SERVER_NAME}|\"${MCP_SERVER_NAME}\")\\\\]`, \"m\").test(text);\n }\n try {\n const doc: unknown = JSON.parse(text);\n const servers = isRecord(doc) ? doc[mcp.format === \"vscode\" ? \"servers\" : \"mcpServers\"] : undefined;\n return isRecord(servers) && MCP_SERVER_NAME in servers;\n } catch {\n // Not plain JSON (comments): cannot tell, so do not nag.\n return null;\n }\n}\n\n/** What `configureAgents` did for one agent. */\nexport interface AgentSetupResult {\n agent: AgentKey;\n /** Skills written, or null when the bundle could not be loaded. */\n skills: number | null;\n /** An earlier agent in the same run that already wrote the skills directory. */\n sharedWith?: AgentKey;\n mcp: McpWriteResult | null;\n}\n\n/**\n * Install the skills and register the MCP server for each agent.\n *\n * Never throws for a missing skills bundle — the scaffold it runs inside must\n * not fail over its assistants — and reports it as `skills: null` instead.\n */\nexport function configureAgents(\n agents: readonly AgentKey[],\n projectDir: string,\n load: () => LoadedSkill[] = loadBundledSkills,\n pm: PackageManager = detectPackageManager(projectDir)\n): { results: AgentSetupResult[]; skillsError?: string; mcpDependency?: McpDependencyStatus } {\n let skillsError: string | undefined;\n let installed: ReturnType<typeof installSkills> = [];\n try {\n installed = installSkills(agents, load(), projectDir);\n } catch (err) {\n skillsError = err instanceof Error ? err.message : String(err);\n }\n\n const results = agents.map((agent): AgentSetupResult => {\n const skills = installed.find(r => r.agent === agent);\n return {\n agent,\n skills: skills ? skills.skills : null,\n sharedWith: skills?.sharedWith,\n mcp: writeMcpConfig(agent, projectDir, pm)\n };\n });\n // A config that runs the project's copy of the server needs the project to\n // have one.\n const registered = results.some(r => r.mcp?.status === \"added\" || r.mcp?.status === \"present\");\n return { results, skillsError, ...(registered && { mcpDependency: ensureMcpDependency(projectDir) }) };\n}\n\n/** The line to print when {@link ensureMcpDependency} changed or could not change the manifest. */\nexport function mcpDependencyNote(status: McpDependencyStatus | undefined, pm: PackageManager): string | undefined {\n if (status === \"added\") {\n return `Added ${MCP_PACKAGE} to devDependencies — run \\`${pm} install\\` before your assistant starts it.`;\n }\n if (status === \"unreadable\") {\n return `Could not add ${MCP_PACKAGE} to package.json — add it to devDependencies by hand; the MCP config runs the project's own copy.`;\n }\n return undefined;\n}\n\n/** One line per agent, for the scaffold's output. */\nexport function printAgentSetup(\n { results, skillsError, mcpDependency }: ReturnType<typeof configureAgents>,\n pm: PackageManager = \"pnpm\"\n): void {\n for (const result of results) {\n const agent = AGENTS[result.agent];\n const parts: string[] = [];\n\n if (result.sharedWith) {\n parts.push(`skills in ${chalk.gray(agent.targetDir)} ${chalk.gray(`(shared with ${AGENTS[result.sharedWith].label})`)}`);\n } else if (result.skills !== null) {\n parts.push(`${result.skills} skills in ${chalk.gray(agent.targetDir)}`);\n }\n\n if (result.mcp?.status === \"added\") {\n parts.push(`MCP server in ${chalk.gray(result.mcp.file)}`);\n } else if (result.mcp?.status === \"present\") {\n parts.push(`MCP server already in ${chalk.gray(result.mcp.file)}`);\n }\n\n // A tick is a claim that something was written; with nothing, it isn't.\n const mark = parts.length ? chalk.green(\"✓\") : chalk.yellow(\"!\");\n console.log(` ${mark} ${chalk.bold(agent.label)}${parts.length ? ` — ${parts.join(\", \")}` : \"\"}`);\n\n if (result.mcp?.status === \"unreadable\") {\n console.log(chalk.yellow(` ${result.mcp.file} is not plain JSON, so it was left alone — add the \"${MCP_SERVER_NAME}\" server by hand.`));\n } else if (!result.mcp) {\n console.log(chalk.gray(` ${agent.label} has no project-level MCP config — add @rebasepro/mcp in its MCP settings.`));\n }\n }\n\n const dependencyNote = mcpDependencyNote(mcpDependency, pm);\n if (dependencyNote) console.log(chalk.gray(` ${dependencyNote}`));\n\n if (skillsError) {\n console.log(chalk.yellow(` Could not install the Rebase skills: ${skillsError}`));\n console.log(chalk.yellow(\" Run `rebase skills install` in the project once dependencies are installed.\"));\n }\n}\n\n/**\n * Ask whether to set up AI agents, then which, pre-ticking what is installed.\n *\n * Interactive only; the caller has already refused a non-TTY. Returns [] when\n * the developer says no or unticks everything.\n */\nexport async function promptForAgents(home?: string): Promise<AgentKey[]> {\n const { configure } = await inquirer.prompt([{\n type: \"confirm\",\n name: \"configure\",\n message: \"Set up Rebase skills and the Rebase MCP server for your AI coding agent(s)?\",\n default: true\n }]);\n if (!configure) return [];\n\n // Installed first, so the pre-ticked boxes are the ones under the cursor.\n const detected = detectInstalledAgents(home);\n const installed = [...detected.filter(a => a.found), ...detected.filter(a => !a.found)];\n console.log(\"\");\n console.log(\"Detecting installed AI coding agents...\");\n for (const { label, marker, found } of installed) {\n if (found) console.log(` ${chalk.green(\"✓\")} ${label} — ${marker}`);\n else if (marker) console.log(chalk.gray(` ✗ ${label} — ${marker} (not found)`));\n else console.log(chalk.gray(` · ${label} — can't be detected; tick it below if you use it`));\n }\n console.log(\"\");\n\n const { selectedAgents } = await inquirer.prompt([{\n type: \"checkbox\",\n name: \"selectedAgents\",\n message: \"Select agents to configure:\",\n choices: installed.map(({ key, label, found }) => {\n const { targetDir, mcp } = AGENTS[key];\n const writes = mcp ? `${targetDir} + ${mcp.file}` : targetDir;\n return { name: `${label} ${chalk.gray(`— ${writes}`)}`, short: label, value: key, checked: found };\n })\n }]);\n\n return AGENT_KEYS.filter(key => selectedAgents.includes(key));\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"api-keys-Tm7zvAPe.js","names":[],"sources":["../src/commands/api-keys.ts"],"sourcesContent":["/**\n * CLI command: rebase api-keys <action>\n *\n * The project's service keys, through `/api/admin/api-keys`, authenticated with\n * the service key from `.env`.\n *\n * Subcommands:\n * list — List the service keys (masked): kind, scopes, roles\n * get — Show one key\n * create — Create a service key holding the scopes named on the line\n * revoke — Revoke a key\n * scopes — List the scopes this backend knows, and which the caller holds\n */\nimport chalk from \"chalk\";\nimport type arg from \"arg\";\nimport { createRebaseClient, RebaseApiError } from \"@rebasepro/client\";\nimport {\n ADMIN_SCOPES,\n DATA_PLANE_SCOPES,\n KEY_MANAGEMENT_SCOPES,\n parseScope,\n type ApiKeyMasked,\n type ApiKeyWithSecret,\n type CreateApiKeyRequest,\n type ScopeSummary\n} from \"@rebasepro/types\";\nimport {\n requireProjectRoot,\n readEnvFile\n} from \"../utils/project\";\nimport { parseCommandArgs, UsageError, wantsHelp } from \"../utils/args\";\nimport { unknownCommand } from \"../utils/unknown-command\";\nimport { cliUserAgent } from \"../utils/version\";\nimport { serverUnreachableHint } from \"../utils/dev-server\";\nimport fs from \"fs\";\nimport path from \"path\";\n\n/** Everything the switch below dispatches, for the did-you-mean. */\nexport const API_KEYS_SUBCOMMANDS = [\"list\", \"get\", \"create\", \"revoke\", \"scopes\"] as const;\n\n/* ═══════════════════════════════════════════════════════════════\n Env helper — reads SERVICE_KEY and PORT from .env\n ═══════════════════════════════════════════════════════════════ */\n\n/**\n * Was a hand-rolled `indexOf(\"=\")` loop. It keyed `export KEY=value` as\n * `export KEY` and carried a trailing `# comment` into the value — so a key\n * that was present read as absent, or reached an `Authorization` header with a\n * comment attached and came back 401. See `readEnvFile`.\n */\nconst loadEnv = readEnvFile;\n\n/**\n * Where this project's backend is — or why the CLI will not say.\n *\n * `configured` is an address the developer named; `running` is the one this\n * project's backend recorded when it bound, and is still serving on. There is\n * no third, guessed, answer: the request carries the project's service key —\n * the `admin` role, every scope — and an address the CLI only guessed is one it\n * would hand that key to without knowing whose server answers.\n */\nexport type BackendAddress =\n | { url: string; source: \"configured\" | \"running\" }\n | { url: null; reason: string };\n\n/** Whether a process with this pid is alive and ours to signal. */\nfunction isAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n // ESRCH: gone. EPERM: alive, but another user's — not this project's backend.\n return false;\n }\n}\n\n/**\n * The address the service key may be sent to. Shared with `rebase storage`.\n *\n * In order:\n *\n * 1. `REBASE_BASE_URL`, in the shell or in `.env` — the developer said where.\n * 2. `.rebase/state.json`, which this project's backend writes when it binds\n * and removes when it stops — and only while the pid it names is alive,\n * because a backend that was killed leaves the file behind.\n *\n * Nothing else. `.env`'s `PORT` was the fallback, and every scaffold writes\n * `PORT=3001`: with `rebase dev` stopped, `rebase api-keys list` sent this\n * project's service key to whatever listened on 3001 — on the tester's machine,\n * another project's server. `.rebase-dev-url` is not enough on its own either:\n * a `rebase dev` that was killed leaves it behind, pointing at a port some\n * other server may have taken since.\n */\nexport function resolveBackendAddress(env: Record<string, string>, projectRoot: string): BackendAddress {\n const configured = process.env.REBASE_BASE_URL?.trim() || env.REBASE_BASE_URL?.trim();\n if (configured) return { url: configured, source: \"configured\" };\n\n const state = readBackendState(projectRoot);\n if (state && isAlive(state.pid)) return { url: state.url, source: \"running\" };\n\n const last = state ? ` The last one recorded here, at ${state.url} (pid ${state.pid}), has stopped.` : \"\";\n return {\n url: null,\n reason: `This project's server is not running, so the service key was not sent anywhere.${last} ` +\n \"Start it with `rebase dev`. If it runs some other way — `rebase start`, a container — \" +\n \"set REBASE_BASE_URL in .env to its address.\"\n };\n}\n\n/** What `.rebase/state.json` says about the backend that wrote it, or null. */\nfunction readBackendState(projectRoot: string): { url: string; pid: number } | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(fs.readFileSync(path.join(projectRoot, \".rebase\", \"state.json\"), \"utf-8\"));\n } catch {\n return null;\n }\n if (!isRecord(parsed) || typeof parsed.pid !== \"number\" || !Number.isInteger(parsed.pid) || parsed.pid <= 0) return null;\n const url = typeof parsed.baseUrl === \"string\" && parsed.baseUrl.trim()\n ? parsed.baseUrl.trim()\n : typeof parsed.port === \"number\" ? `http://localhost:${parsed.port}` : null;\n\n return url ? { url, pid: parsed.pid } : null;\n}\n\n/**\n * The address to send the service key to, or the end of the command.\n *\n * Exits before any request is made: the refusal is the point.\n */\nexport function requireBackendUrl(env: Record<string, string>, projectRoot: string): string {\n const address = resolveBackendAddress(env, projectRoot);\n if (address.url !== null) return address.url;\n\n console.error(chalk.red(\"✗ No server to send the request to.\"));\n console.error(chalk.gray(` ${address.reason}`));\n process.exit(1);\n}\n\n/* ═══════════════════════════════════════════════════════════════\n The backend, through the SDK\n ═══════════════════════════════════════════════════════════════ */\n\ntype ProjectClient = ReturnType<typeof createRebaseClient>;\n\ninterface Connection {\n client: ProjectClient;\n baseUrl: string;\n /** The variable the service key was read from, for a hint that names it. */\n keyVariable: ServiceKeyVariable;\n}\n\n/** Where a service key is read from: the name a scaffold writes, or the older one. */\ntype ServiceKeyVariable = \"REBASE_SERVICE_KEY\" | \"SERVICE_KEY\";\n\n/**\n * An SDK client for this project's backend, holding the service key.\n *\n * The service key is the project's own machine identity — the `admin` role,\n * every scope — so it is what may list, mint and revoke service keys. It is a\n * static bearer token: no session to restore or refresh, and no socket.\n */\nfunction connect(): Connection {\n const projectRoot = requireProjectRoot();\n const env = loadEnv(projectRoot);\n const keyVariable: ServiceKeyVariable = env.SERVICE_KEY ? \"SERVICE_KEY\" : \"REBASE_SERVICE_KEY\";\n const serviceKey = env[keyVariable];\n\n if (!serviceKey) {\n console.error(chalk.red(\"✗ REBASE_SERVICE_KEY not found in .env — required to manage API keys.\"));\n process.exit(1);\n }\n const baseUrl = requireBackendUrl(env, projectRoot);\n\n const client = createRebaseClient({\n baseUrl,\n token: serviceKey,\n realtime: false,\n headers: { \"User-Agent\": cliUserAgent() },\n auth: { persistSession: false, autoRefresh: false }\n });\n return { client, baseUrl, keyVariable };\n}\n\n/**\n * What to print when a request fails: the server's error code and message,\n * which is what says why — `SCOPE_EXCEEDS_CREATOR`, `INVALID_SCOPES`,\n * `ROLE_EXCEEDS_CREATOR`, `KEY_MANAGEMENT_SCOPE` — and a hint where one helps.\n *\n * Exported so its tests can read the lines without a server.\n */\nexport function describeFailure(\n action: string,\n error: unknown,\n baseUrl: string,\n keyVariable: ServiceKeyVariable = \"REBASE_SERVICE_KEY\"\n): string[] {\n if (!(error instanceof RebaseApiError)) {\n return [`✗ ${action}: ${error instanceof Error ? error.message : String(error)}`];\n }\n if (error.status === 0 || error.code === \"NETWORK_ERROR\") {\n return [`✗ ${action}: ${error.message}`, ...serverUnreachableHint(baseUrl)];\n }\n\n const label = [error.code, error.status ? `(${error.status})` : undefined].filter(Boolean).join(\" \");\n const lines = [`✗ ${action}${label ? ` — ${label}` : \"\"}: ${error.message}`];\n\n if (error.status === 401) {\n lines.push(` The server at ${baseUrl} did not accept ${keyVariable} from .env — is it this project's server?`);\n }\n // The message names the target that is not there; what *is* there rides\n // in `details`, and is the thing to pick from.\n if (error.code === \"UNKNOWN_SCOPE_TARGET\" && isRecord(error.details)) {\n for (const [kind, title] of [[\"collections\", \"Collections\"], [\"buckets\", \"Buckets\"], [\"functions\", \"Functions\"]] as const) {\n const names = error.details[kind];\n if (Array.isArray(names) && names.length > 0) {\n lines.push(` ${title}: ${names.map(String).join(\", \")}`);\n }\n }\n }\n return lines;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction fail(action: string, error: unknown, connection: Connection): never {\n for (const line of describeFailure(action, error, connection.baseUrl, connection.keyVariable)) {\n console.error(line.startsWith(\"✗\") ? chalk.red(line) : chalk.gray(line));\n }\n process.exit(1);\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Entry\n ═══════════════════════════════════════════════════════════════ */\n\nexport async function apiKeysCommand(subcommand: string | undefined, rawArgs: string[]): Promise<void> {\n // `--help` is answered before dispatch, never by a handler. `cli.ts` only\n // rewrites the subcommand to `\"--help\"` when none was named, so\n // `rebase api-keys list --help` used to *list the keys* and\n // `rebase api-keys revoke --help` sent `DELETE /api/admin/api-keys/--help`.\n if (!subcommand || subcommand === \"--help\" || wantsHelp(rawArgs)) {\n printApiKeysHelp();\n return;\n }\n\n switch (subcommand) {\n case \"list\":\n await listKeys(rawArgs);\n break;\n case \"get\":\n await getKey(rawArgs);\n break;\n case \"create\":\n await createKey(rawArgs);\n break;\n case \"revoke\":\n await revokeKey(rawArgs);\n break;\n case \"scopes\":\n await listScopes(rawArgs);\n break;\n default:\n // The shape every other family uses. This one printed a bare line\n // to stderr *and* the whole help page to stdout, so a typo wrote\n // twenty lines to a stream a `--json` caller reads, with no `✗`, no\n // pointer at `--help`, and no did-you-mean — `craete` is one\n // transposition from `create`.\n unknownCommand(subcommand, API_KEYS_SUBCOMMANDS, \"api-keys\");\n }\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Showing a key\n ═══════════════════════════════════════════════════════════════ */\n\nfunction keyStatus(key: ApiKeyMasked, now: Date): string {\n if (key.revoked_at) return chalk.red(\"revoked\");\n if (key.expires_at && new Date(key.expires_at) < now) return chalk.yellow(\"expired\");\n return chalk.green(\"active\");\n}\n\n/**\n * The lines that describe one key: what it is, what it may do and whom it\n * runs as.\n *\n * A service key runs as the RLS role `service` plus its own `roles`; a\n * personal key runs as its owner's roles as they are when it is used, so it\n * has none of its own to show.\n *\n * Exported so its tests can read the lines without a server.\n */\nexport function describeKey(key: ApiKeyMasked, now: Date = new Date()): string[] {\n const field = (label: string, value: string) => ` ${chalk.gray(`${label}:`.padEnd(12))}${value}`;\n const roles = key.kind === \"personal\"\n ? `its owner's (${key.owner_uid ?? \"unknown\"})`\n : [\"service\", ...key.roles].join(\", \");\n\n const lines = [\n ` ${chalk.bold(key.name)} ${chalk.gray(`[${key.key_prefix}•••]`)} ${keyStatus(key, now)}`,\n field(\"ID\", key.id),\n field(\"Kind\", key.kind),\n field(\"Scopes\", key.scopes.length > 0 ? key.scopes.join(\", \") : chalk.gray(\"none\")),\n field(\"Roles\", roles)\n ];\n if (key.rate_limit !== null) lines.push(field(\"Rate limit\", `${key.rate_limit} requests / 15 min`));\n if (key.expires_at) lines.push(field(\"Expires\", new Date(key.expires_at).toLocaleDateString()));\n lines.push(field(\"Created\", new Date(key.created_at).toLocaleDateString()));\n if (key.last_used_at) lines.push(field(\"Last used\", new Date(key.last_used_at).toLocaleDateString()));\n return lines;\n}\n\n/* ═══════════════════════════════════════════════════════════════\n list\n ═══════════════════════════════════════════════════════════════ */\n\n/** The flags `rebase api-keys list` takes: none of its own. */\nexport const LIST_KEYS_FLAGS = {} as const;\n\nasync function listKeys(rawArgs: string[]): Promise<void> {\n parseCommandArgs({\n spec: LIST_KEYS_FLAGS,\n rawArgs,\n commandWords: 2,\n command: \"api-keys list\",\n maxPositionals: 0\n });\n\n const connection = connect();\n const { client } = connection;\n let keys: ApiKeyMasked[];\n try {\n ({ keys } = await client.apiKeys.listKeys());\n } catch (e: unknown) {\n fail(\"Failed to list API keys\", e, connection);\n }\n\n console.log(\"\");\n console.log(chalk.bold(\" 🔑 API Keys\"));\n console.log(\"\");\n\n if (keys.length === 0) {\n console.log(chalk.gray(\" No API keys found.\"));\n console.log(\"\");\n return;\n }\n\n const now = new Date();\n for (const key of keys) {\n for (const line of describeKey(key, now)) console.log(line);\n console.log(\"\");\n }\n}\n\n/* ═══════════════════════════════════════════════════════════════\n get\n ═══════════════════════════════════════════════════════════════ */\n\n/** The flags `rebase api-keys get` takes. */\nexport const GET_KEY_FLAGS = {\n \"--id\": String\n} as const;\n\n/**\n * Which key this invocation names, as a positional or as `--id`.\n *\n * Exported so its tests can drive the real parser rather than a copy of it.\n */\nexport function resolveGetKeyArgs(rawArgs: string[]): { id?: string } {\n const { flags, positionals } = parseCommandArgs({\n spec: GET_KEY_FLAGS,\n rawArgs,\n commandWords: 2,\n command: \"api-keys get\",\n maxPositionals: 1\n });\n\n return { id: flags[\"--id\"] || positionals[0] };\n}\n\nasync function getKey(rawArgs: string[]): Promise<void> {\n const { id } = resolveGetKeyArgs(rawArgs);\n if (!id) {\n throw new UsageError(\"Key ID is required: rebase api-keys get <key-id> (`rebase api-keys list` shows them).\");\n }\n\n const connection = connect();\n const { client } = connection;\n let key: ApiKeyMasked;\n try {\n ({ key } = await client.apiKeys.getKey(id));\n } catch (e: unknown) {\n fail(\"Failed to read API key\", e, connection);\n }\n\n console.log(\"\");\n for (const line of describeKey(key)) console.log(line);\n console.log(\"\");\n}\n\n/* ═══════════════════════════════════════════════════════════════\n create\n ═══════════════════════════════════════════════════════════════ */\n\n/** The flags `rebase api-keys create` takes. */\nexport const CREATE_KEY_FLAGS = {\n \"--name\": String,\n \"--scopes\": [String],\n \"--full-access\": Boolean,\n \"--roles\": [String],\n \"--rate-limit\": Number,\n \"--expires-in\": String,\n \"--expires-at\": String,\n \"-n\": \"--name\"\n} satisfies arg.Spec;\n\n/**\n * Options `create` refuses by name, each with the flag that does that job.\n *\n * Checked before the line is parsed: the strict parser would answer them\n * \"unknown option\", which does not say what to type instead.\n */\nexport const RETIRED_CREATE_OPTIONS: Readonly<Record<string, string>> = {\n \"--permissions\":\n \"--permissions is gone: name what the key may do with --scopes, e.g. \" +\n \"--scopes data:read:posts,data:write:posts (`rebase api-keys scopes` lists them).\",\n \"--admin\":\n \"--admin is gone: give the key the RLS role with --roles admin, and the admin-plane scopes it needs \" +\n \"with --scopes, e.g. --scopes users:read,schema:read.\",\n \"--expires\":\n \"--expires is gone: use --expires-in <days> (e.g. --expires-in 90) or --expires-at <ISO date>.\"\n};\n\n/** Refuse a retired option, wherever on the line and however it is spelled. */\nfunction refuseRetiredOptions(rawArgs: string[]): void {\n for (const token of rawArgs.slice(2)) {\n // Past `--` everything is a value, never an option.\n if (token === \"--\") return;\n const name = token.split(\"=\", 1)[0];\n const replacement = Object.prototype.hasOwnProperty.call(RETIRED_CREATE_OPTIONS, name)\n ? RETIRED_CREATE_OPTIONS[name]\n : undefined;\n if (replacement) throw new UsageError(replacement);\n }\n}\n\n/**\n * A list flag's values, whether repeated (`--scopes a --scopes b`) or\n * comma-separated (`--scopes a,b`) — or both — trimmed and deduplicated.\n * Whitespace separates too: no scope or role name contains any.\n */\nfunction splitList(values: readonly string[] | undefined): string[] {\n const out = new Set<string>();\n for (const value of values ?? []) {\n for (const entry of value.split(/[\\s,]+/)) {\n if (entry) out.add(entry);\n }\n }\n return [...out];\n}\n\nconst DAY_MS = 86_400_000;\n\nfunction resolveExpiry(expiresIn: string | undefined, expiresAt: string | undefined, now: Date): string | null {\n if (expiresIn !== undefined && expiresAt !== undefined) {\n throw new UsageError(\"Pass --expires-in or --expires-at, not both.\");\n }\n if (expiresIn !== undefined) {\n const days = /^\\d+$/.test(expiresIn.trim()) ? Number(expiresIn.trim()) : NaN;\n if (!Number.isSafeInteger(days) || days < 1) {\n throw new UsageError(`--expires-in takes a whole number of days, e.g. --expires-in 90 (got \"${expiresIn}\").`);\n }\n return new Date(now.getTime() + days * DAY_MS).toISOString();\n }\n if (expiresAt !== undefined) {\n const parsed = new Date(expiresAt);\n if (isNaN(parsed.getTime())) {\n throw new UsageError(`--expires-at takes an ISO date, e.g. --expires-at 2027-01-31 (got \"${expiresAt}\").`);\n }\n if (parsed.getTime() <= now.getTime()) {\n throw new UsageError(`--expires-at must be in the future (got ${parsed.toISOString()}).`);\n }\n return parsed.toISOString();\n }\n return null;\n}\n\n/** What `rebase api-keys create` was asked to mint. */\nexport interface CreateKeyPlan {\n name: string;\n /** The scopes named with `--scopes`, or `\"held\"` for `--full-access`. */\n scopes: string[] | \"held\";\n /** RLS roles beside `service`. */\n roles: string[];\n rate_limit: number | null;\n expires_at: string | null;\n}\n\n/**\n * What this invocation asks to be created.\n *\n * The name may be given either way — `--name \"My Key\"` or as the single\n * positional — and under the old permissive parse an undeclared flag became\n * that positional: `rebase api-keys create --debug --full-access` created a\n * key called `--debug` with access to everything, and `--debug` is what the\n * CLI prints after every failure as the thing to re-run with. Strict parsing\n * makes the flag an error instead of a name.\n *\n * What the key may do has to be asked for by name — `--scopes` or\n * `--full-access` — never defaulted: a forgotten flag must not mint a key that\n * reaches everything.\n *\n * Exported so its tests can drive the real parser rather than a copy of it.\n */\nexport function resolveCreateKeyArgs(rawArgs: string[], now: Date = new Date()): CreateKeyPlan {\n refuseRetiredOptions(rawArgs);\n\n const { flags, positionals } = parseCommandArgs({\n spec: CREATE_KEY_FLAGS,\n rawArgs,\n commandWords: 2,\n command: \"api-keys create\",\n maxPositionals: 1\n });\n\n const name = (flags[\"--name\"] || positionals[0] || \"\").trim();\n if (!name) {\n throw new UsageError('A key needs a name: rebase api-keys create \"CI\" --scopes data:read');\n }\n\n const named = splitList(flags[\"--scopes\"]);\n const fullAccess = flags[\"--full-access\"] === true;\n if (fullAccess && named.length > 0) {\n throw new UsageError(\"Pass --scopes or --full-access, not both: --full-access already means every scope you hold.\");\n }\n if (!fullAccess && named.length === 0) {\n throw new UsageError(\n \"Name what the key may do: --scopes data:read:posts,data:write:posts, or --full-access for every \" +\n \"scope you hold. `rebase api-keys scopes` lists them.\"\n );\n }\n\n const rateLimit = flags[\"--rate-limit\"];\n if (rateLimit !== undefined && (!Number.isSafeInteger(rateLimit) || rateLimit < 1)) {\n throw new UsageError(\"--rate-limit takes a whole number of requests per 15-minute window, e.g. --rate-limit 500.\");\n }\n\n return {\n name,\n scopes: fullAccess ? \"held\" : named,\n roles: splitList(flags[\"--roles\"]),\n rate_limit: rateLimit ?? null,\n expires_at: resolveExpiry(flags[\"--expires-in\"], flags[\"--expires-at\"], now)\n };\n}\n\n/**\n * The scopes `--full-access` puts on a key: every one the caller holds, less\n * key management, which no key may hold.\n *\n * Exported so its tests can check the subtraction without a server.\n */\nexport function grantableScopes(held: readonly string[]): string[] {\n const management: readonly string[] = KEY_MANAGEMENT_SCOPES;\n return held.filter(entry => !management.includes(parseScope(entry)?.scope ?? entry));\n}\n\nasync function createKey(rawArgs: string[]): Promise<void> {\n const plan = resolveCreateKeyArgs(rawArgs);\n const connection = connect();\n const { client } = connection;\n\n let scopes: string[];\n if (plan.scopes === \"held\") {\n let held: string[];\n try {\n ({ held } = await client.personalKeys.listScopes());\n } catch (e: unknown) {\n fail(\"Failed to read the scopes you hold\", e, connection);\n }\n scopes = grantableScopes(held);\n if (scopes.length === 0) {\n console.error(chalk.red(\"✗ You hold no scope a key may carry, so --full-access has nothing to grant.\"));\n process.exit(1);\n }\n } else {\n scopes = plan.scopes;\n }\n\n const request: CreateApiKeyRequest = {\n name: plan.name,\n scopes,\n ...(plan.roles.length > 0 ? { roles: plan.roles } : {}),\n ...(plan.rate_limit !== null ? { rate_limit: plan.rate_limit } : {}),\n ...(plan.expires_at !== null ? { expires_at: plan.expires_at } : {})\n };\n\n let created: ApiKeyWithSecret;\n try {\n ({ key: created } = await client.apiKeys.createKey(request));\n } catch (e: unknown) {\n fail(\"Failed to create API key\", e, connection);\n }\n\n console.log(\"\");\n console.log(chalk.bold.green(\" ✓ API key created\"));\n console.log(\"\");\n for (const line of describeKey(created)) console.log(line);\n console.log(\"\");\n console.log(chalk.bold.yellow(\" ⚠ Copy your key now — it won't be shown again:\"));\n console.log(\"\");\n console.log(` ${chalk.cyan(created.key)}`);\n console.log(\"\");\n}\n\n/* ═══════════════════════════════════════════════════════════════\n revoke\n ═══════════════════════════════════════════════════════════════ */\n\n/** The flags `rebase api-keys revoke` takes. */\nexport const REVOKE_KEY_FLAGS = {\n \"--id\": String\n} as const;\n\n/**\n * Which key this invocation names.\n *\n * The id is a positional, so the permissive parse handed one straight to the\n * DELETE: `rebase api-keys revoke --foo` sent\n * `DELETE /api/admin/api-keys/--foo`, and `rebase --debug api-keys revoke <id>`\n * shifted the words along and revoked the key named `revoke`.\n *\n * Exported so its tests can drive the real parser rather than a copy of it.\n */\nexport function resolveRevokeKeyArgs(rawArgs: string[]): { id?: string } {\n const { flags, positionals } = parseCommandArgs({\n spec: REVOKE_KEY_FLAGS,\n rawArgs,\n commandWords: 2,\n command: \"api-keys revoke\",\n maxPositionals: 1\n });\n\n return { id: flags[\"--id\"] || positionals[0] };\n}\n\nasync function revokeKey(rawArgs: string[]): Promise<void> {\n const { id } = resolveRevokeKeyArgs(rawArgs);\n\n if (!id) {\n throw new UsageError(\"Key ID is required: rebase api-keys revoke <key-id> (`rebase api-keys list` shows them).\");\n }\n\n const connection = connect();\n const { client } = connection;\n try {\n await client.apiKeys.revokeKey(id);\n } catch (e: unknown) {\n fail(\"Failed to revoke API key\", e, connection);\n }\n\n console.log(\"\");\n console.log(chalk.bold.green(\" ✓ API key revoked\"));\n console.log(` ${chalk.gray(\"ID:\")} ${id}`);\n console.log(\"\");\n}\n\n/* ═══════════════════════════════════════════════════════════════\n scopes\n ═══════════════════════════════════════════════════════════════ */\n\n/** The flags `rebase api-keys scopes` takes: none of its own. */\nexport const SCOPES_LIST_FLAGS = {} as const;\n\nconst PLANES: ReadonlyArray<{ plane: ScopeSummary[\"plane\"]; title: string; note: string }> = [\n { plane: \"data\", title: \"Data\", note: \"narrow what a key reaches; row-level security still decides the rows\" },\n { plane: \"admin\", title: \"Admin\", note: \"nobody holds these unless a role grants them; the admin role holds all\" },\n { plane: \"app\", title: \"App\", note: \"declared by this app under auth.scopes on the users collection\" }\n];\n\n/**\n * The scope catalogue as printed lines: one section per plane, each scope with\n * its label and what its target names, marked when the caller holds it.\n *\n * Exported so its tests can read the lines without a server.\n */\nexport function describeScopes(scopes: readonly ScopeSummary[], held: readonly string[]): string[] {\n const management: readonly string[] = KEY_MANAGEMENT_SCOPES;\n const width = Math.max(0, ...scopes.map(s => s.scope.length)) + 2;\n const labelWidth = Math.max(0, ...scopes.map(s => s.label.length)) + 2;\n const lines: string[] = [];\n for (const { plane, title, note } of PLANES) {\n const inPlane = scopes.filter(s => s.plane === plane);\n if (inPlane.length === 0) continue;\n lines.push(` ${chalk.bold(title)} ${chalk.gray(`— ${note}`)}`);\n for (const s of inPlane) {\n const mark = held.includes(s.scope) ? chalk.green(\"✓\") : \" \";\n const extra = management.includes(s.scope)\n ? chalk.gray(\"never on a key\")\n : s.target ? chalk.gray(`:<${s.target}>`) : \"\";\n lines.push(` ${mark} ${s.scope.padEnd(width)}${extra ? s.label.padEnd(labelWidth) : s.label}${extra}`);\n }\n lines.push(\"\");\n }\n return lines;\n}\n\nasync function listScopes(rawArgs: string[]): Promise<void> {\n parseCommandArgs({\n spec: SCOPES_LIST_FLAGS,\n rawArgs,\n commandWords: 2,\n command: \"api-keys scopes\",\n maxPositionals: 0\n });\n\n const connection = connect();\n const { client } = connection;\n let catalogue: { scopes: ScopeSummary[]; held: string[] };\n try {\n catalogue = await client.personalKeys.listScopes();\n } catch (e: unknown) {\n fail(\"Failed to read the scopes\", e, connection);\n }\n\n console.log(\"\");\n console.log(chalk.bold(\" Scopes\") + chalk.gray(\" (✓ = you hold it; :<target> narrows it to one)\"));\n console.log(\"\");\n for (const line of describeScopes(catalogue.scopes, catalogue.held)) console.log(line);\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Help\n ═══════════════════════════════════════════════════════════════ */\n\nfunction printApiKeysHelp() {\n const keyable = ADMIN_SCOPES.filter(scope => !(KEY_MANAGEMENT_SCOPES as readonly string[]).includes(scope));\n console.log(`\n${chalk.bold(\"rebase api-keys\")} — Manage service API keys\n\n${chalk.green.bold(\"Usage\")}\n rebase api-keys ${chalk.blue(\"<command>\")} [options]\n\n${chalk.green.bold(\"Commands\")}\n ${chalk.blue.bold(\"list\")} List the service keys: kind, scopes, roles\n ${chalk.blue.bold(\"get\")} ${chalk.gray(\"<id>\")} Show one key\n ${chalk.blue.bold(\"create\")} Create a service key\n ${chalk.blue.bold(\"revoke\")} ${chalk.gray(\"<id>\")} Revoke a key\n ${chalk.blue.bold(\"scopes\")} List the scopes this backend knows, and which you hold\n\n${chalk.green.bold(\"create Options\")}\n ${chalk.blue(\"--name, -n\")} Key name ${chalk.gray(\"(required; or the first argument)\")}\n ${chalk.blue(\"--scopes\")} What the key may do, comma-separated or repeated\n ${chalk.gray(\"(required unless --full-access)\")}\n ${chalk.blue(\"--full-access\")} Every scope you hold, less keys:read and keys:write\n ${chalk.blue(\"--roles\")} RLS roles the key runs as beside ${chalk.gray(\"service\")}, e.g. admin\n ${chalk.blue(\"--rate-limit\")} Requests per 15-min window ${chalk.gray(\"(default: the server's API-key limit)\")}\n ${chalk.blue(\"--expires-in\")} Days until the key expires\n ${chalk.blue(\"--expires-at\")} Expiry as an ISO date, e.g. 2027-01-31\n\n${chalk.green.bold(\"get / revoke Options\")}\n ${chalk.blue(\"--id\")} API key ID ${chalk.gray(\"(or the first argument)\")}\n\n${chalk.green.bold(\"Scopes\")}\n A scope is ${chalk.blue(\"resource:action\")}, optionally narrowed to one target:\n ${chalk.blue(\"data:read:posts\")} reads the posts collection and nothing else.\n Data ${DATA_PLANE_SCOPES.join(\", \")}\n ${chalk.gray(\"target: a collection slug, a storage source id (default), a function name\")}\n Admin ${keyable.join(\", \")}\n App whatever the app declares under auth.scopes ${chalk.gray(\"(rebase api-keys scopes)\")}\n\n${chalk.green.bold(\"Examples\")}\n rebase api-keys list\n rebase api-keys create \"Analytics\" --scopes data:read:events\n rebase api-keys create -n \"Blog CI\" --scopes data:read:posts,data:write:posts --expires-in 90\n rebase api-keys create -n \"Mailer\" --scopes \"functions:invoke:send-email,storage:read:(default)\"\n rebase api-keys create -n \"Ops\" --full-access --roles admin --expires-at 2027-01-31\n rebase api-keys get abc123-def456\n rebase api-keys revoke abc123-def456\n`);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAsCA,IAAa,uBAAuB;CAAC;CAAQ;CAAO;CAAU;CAAU;AAAQ;;;;;;;AAYhF,IAAM,UAAU;;AAgBhB,SAAS,QAAQ,KAAsB;CACnC,IAAI;EACA,QAAQ,KAAK,KAAK,CAAC;EACnB,OAAO;CACX,QAAQ;EAEJ,OAAO;CACX;AACJ;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,sBAAsB,KAA6B,aAAqC;CACpG,MAAM,aAAa,QAAQ,IAAI,iBAAiB,KAAK,KAAK,IAAI,iBAAiB,KAAK;CACpF,IAAI,YAAY,OAAO;EAAE,KAAK;EAAY,QAAQ;CAAa;CAE/D,MAAM,QAAQ,iBAAiB,WAAW;CAC1C,IAAI,SAAS,QAAQ,MAAM,GAAG,GAAG,OAAO;EAAE,KAAK,MAAM;EAAK,QAAQ;CAAU;CAG5E,OAAO;EACH,KAAK;EACL,QAAQ,kFAHC,QAAQ,mCAAmC,MAAM,IAAI,QAAQ,MAAM,IAAI,mBAAmB,GAGJ;CAGnG;AACJ;;AAGA,SAAS,iBAAiB,aAA0D;CAChF,IAAI;CACJ,IAAI;EACA,SAAS,KAAK,MAAM,GAAG,aAAa,KAAK,KAAK,aAAa,WAAW,YAAY,GAAG,OAAO,CAAC;CACjG,QAAQ;EACJ,OAAO;CACX;CACA,IAAI,CAAC,SAAS,MAAM,KAAK,OAAO,OAAO,QAAQ,YAAY,CAAC,OAAO,UAAU,OAAO,GAAG,KAAK,OAAO,OAAO,GAAG,OAAO;CACpH,MAAM,MAAM,OAAO,OAAO,YAAY,YAAY,OAAO,QAAQ,KAAK,IAChE,OAAO,QAAQ,KAAK,IACpB,OAAO,OAAO,SAAS,WAAW,oBAAoB,OAAO,SAAS;CAE5E,OAAO,MAAM;EAAE;EAAK,KAAK,OAAO;CAAI,IAAI;AAC5C;;;;;;AAOA,SAAgB,kBAAkB,KAA6B,aAA6B;CACxF,MAAM,UAAU,sBAAsB,KAAK,WAAW;CACtD,IAAI,QAAQ,QAAQ,MAAM,OAAO,QAAQ;CAEzC,QAAQ,MAAM,MAAM,IAAI,qCAAqC,CAAC;CAC9D,QAAQ,MAAM,MAAM,KAAK,KAAK,QAAQ,QAAQ,CAAC;CAC/C,QAAQ,KAAK,CAAC;AAClB;;;;;;;;AAyBA,SAAS,UAAsB;CAC3B,MAAM,cAAc,mBAAmB;CACvC,MAAM,MAAM,QAAQ,WAAW;CAC/B,MAAM,cAAkC,IAAI,cAAc,gBAAgB;CAC1E,MAAM,aAAa,IAAI;CAEvB,IAAI,CAAC,YAAY;EACb,QAAQ,MAAM,MAAM,IAAI,uEAAuE,CAAC;EAChG,QAAQ,KAAK,CAAC;CAClB;CACA,MAAM,UAAU,kBAAkB,KAAK,WAAW;CASlD,OAAO;EAAE,QAPM,mBAAmB;GAC9B;GACA,OAAO;GACP,UAAU;GACV,SAAS,EAAE,cAAc,aAAa,EAAE;GACxC,MAAM;IAAE,gBAAgB;IAAO,aAAa;GAAM;EACtD,CACS;EAAQ;EAAS;CAAY;AAC1C;;;;;;;;AASA,SAAgB,gBACZ,QACA,OACA,SACA,cAAkC,sBAC1B;CACR,IAAI,EAAE,iBAAiB,iBACnB,OAAO,CAAC,KAAK,OAAO,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG;CAEpF,IAAI,MAAM,WAAW,KAAK,MAAM,SAAS,iBACrC,OAAO,CAAC,KAAK,OAAO,IAAI,MAAM,WAAW,GAAG,sBAAsB,OAAO,CAAC;CAG9E,MAAM,QAAQ,CAAC,MAAM,MAAM,MAAM,SAAS,IAAI,MAAM,OAAO,KAAK,KAAA,CAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;CACnG,MAAM,QAAQ,CAAC,KAAK,SAAS,QAAQ,MAAM,UAAU,GAAG,IAAI,MAAM,SAAS;CAE3E,IAAI,MAAM,WAAW,KACjB,MAAM,KAAK,mBAAmB,QAAQ,kBAAkB,YAAY,0CAA0C;CAIlH,IAAI,MAAM,SAAS,0BAA0B,SAAS,MAAM,OAAO,GAC/D,KAAK,MAAM,CAAC,MAAM,UAAU;EAAC,CAAC,eAAe,aAAa;EAAG,CAAC,WAAW,SAAS;EAAG,CAAC,aAAa,WAAW;CAAC,GAAY;EACvH,MAAM,QAAQ,MAAM,QAAQ;EAC5B,IAAI,MAAM,QAAQ,KAAK,KAAK,MAAM,SAAS,GACvC,MAAM,KAAK,KAAK,MAAM,IAAI,MAAM,IAAI,MAAM,CAAC,CAAC,KAAK,IAAI,GAAG;CAEhE;CAEJ,OAAO;AACX;AAEA,SAAS,SAAS,OAAkD;CAChE,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC9E;AAEA,SAAS,KAAK,QAAgB,OAAgB,YAA+B;CACzE,KAAK,MAAM,QAAQ,gBAAgB,QAAQ,OAAO,WAAW,SAAS,WAAW,WAAW,GACxF,QAAQ,MAAM,KAAK,WAAW,GAAG,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,KAAK,IAAI,CAAC;CAE3E,QAAQ,KAAK,CAAC;AAClB;AAMA,eAAsB,eAAe,YAAgC,SAAkC;CAKnG,IAAI,CAAC,cAAc,eAAe,YAAY,UAAU,OAAO,GAAG;EAC9D,iBAAiB;EACjB;CACJ;CAEA,QAAQ,YAAR;EACI,KAAK;GACD,MAAM,SAAS,OAAO;GACtB;EACJ,KAAK;GACD,MAAM,OAAO,OAAO;GACpB;EACJ,KAAK;GACD,MAAM,UAAU,OAAO;GACvB;EACJ,KAAK;GACD,MAAM,UAAU,OAAO;GACvB;EACJ,KAAK;GACD,MAAM,WAAW,OAAO;GACxB;EACJ,SAMI,eAAe,YAAY,sBAAsB,UAAU;CACnE;AACJ;AAMA,SAAS,UAAU,KAAmB,KAAmB;CACrD,IAAI,IAAI,YAAY,OAAO,MAAM,IAAI,SAAS;CAC9C,IAAI,IAAI,cAAc,IAAI,KAAK,IAAI,UAAU,IAAI,KAAK,OAAO,MAAM,OAAO,SAAS;CACnF,OAAO,MAAM,MAAM,QAAQ;AAC/B;;;;;;;;;;;AAYA,SAAgB,YAAY,KAAmB,sBAAY,IAAI,KAAK,GAAa;CAC7E,MAAM,SAAS,OAAe,UAAkB,KAAK,MAAM,KAAK,GAAG,MAAM,GAAG,OAAO,EAAE,CAAC,IAAI;CAC1F,MAAM,QAAQ,IAAI,SAAS,aACrB,gBAAgB,IAAI,aAAa,UAAU,KAC3C,CAAC,WAAW,GAAG,IAAI,KAAK,CAAC,CAAC,KAAK,IAAI;CAEzC,MAAM,QAAQ;EACV,KAAK,MAAM,KAAK,IAAI,IAAI,EAAE,GAAG,MAAM,KAAK,IAAI,IAAI,WAAW,KAAK,EAAE,GAAG,UAAU,KAAK,GAAG;EACvF,MAAM,MAAM,IAAI,EAAE;EAClB,MAAM,QAAQ,IAAI,IAAI;EACtB,MAAM,UAAU,IAAI,OAAO,SAAS,IAAI,IAAI,OAAO,KAAK,IAAI,IAAI,MAAM,KAAK,MAAM,CAAC;EAClF,MAAM,SAAS,KAAK;CACxB;CACA,IAAI,IAAI,eAAe,MAAM,MAAM,KAAK,MAAM,cAAc,GAAG,IAAI,WAAW,mBAAmB,CAAC;CAClG,IAAI,IAAI,YAAY,MAAM,KAAK,MAAM,WAAW,IAAI,KAAK,IAAI,UAAU,CAAC,CAAC,mBAAmB,CAAC,CAAC;CAC9F,MAAM,KAAK,MAAM,WAAW,IAAI,KAAK,IAAI,UAAU,CAAC,CAAC,mBAAmB,CAAC,CAAC;CAC1E,IAAI,IAAI,cAAc,MAAM,KAAK,MAAM,aAAa,IAAI,KAAK,IAAI,YAAY,CAAC,CAAC,mBAAmB,CAAC,CAAC;CACpG,OAAO;AACX;;AAOA,IAAa,kBAAkB,CAAC;AAEhC,eAAe,SAAS,SAAkC;CACtD,iBAAiB;EACb,MAAM;EACN;EACA,cAAc;EACd,SAAS;EACT,gBAAgB;CACpB,CAAC;CAED,MAAM,aAAa,QAAQ;CAC3B,MAAM,EAAE,WAAW;CACnB,IAAI;CACJ,IAAI;EACA,CAAC,CAAE,QAAS,MAAM,OAAO,QAAQ,SAAS;CAC9C,SAAS,GAAY;EACjB,KAAK,2BAA2B,GAAG,UAAU;CACjD;CAEA,QAAQ,IAAI,EAAE;CACd,QAAQ,IAAI,MAAM,KAAK,eAAe,CAAC;CACvC,QAAQ,IAAI,EAAE;CAEd,IAAI,KAAK,WAAW,GAAG;EACnB,QAAQ,IAAI,MAAM,KAAK,sBAAsB,CAAC;EAC9C,QAAQ,IAAI,EAAE;EACd;CACJ;CAEA,MAAM,sBAAM,IAAI,KAAK;CACrB,KAAK,MAAM,OAAO,MAAM;EACpB,KAAK,MAAM,QAAQ,YAAY,KAAK,GAAG,GAAG,QAAQ,IAAI,IAAI;EAC1D,QAAQ,IAAI,EAAE;CAClB;AACJ;;AAOA,IAAa,gBAAgB,EACzB,QAAQ,OACZ;;;;;;AAOA,SAAgB,kBAAkB,SAAoC;CAClE,MAAM,EAAE,OAAO,gBAAgB,iBAAiB;EAC5C,MAAM;EACN;EACA,cAAc;EACd,SAAS;EACT,gBAAgB;CACpB,CAAC;CAED,OAAO,EAAE,IAAI,MAAM,WAAW,YAAY,GAAG;AACjD;AAEA,eAAe,OAAO,SAAkC;CACpD,MAAM,EAAE,OAAO,kBAAkB,OAAO;CACxC,IAAI,CAAC,IACD,MAAM,IAAI,WAAW,uFAAuF;CAGhH,MAAM,aAAa,QAAQ;CAC3B,MAAM,EAAE,WAAW;CACnB,IAAI;CACJ,IAAI;EACA,CAAC,CAAE,OAAQ,MAAM,OAAO,QAAQ,OAAO,EAAE;CAC7C,SAAS,GAAY;EACjB,KAAK,0BAA0B,GAAG,UAAU;CAChD;CAEA,QAAQ,IAAI,EAAE;CACd,KAAK,MAAM,QAAQ,YAAY,GAAG,GAAG,QAAQ,IAAI,IAAI;CACrD,QAAQ,IAAI,EAAE;AAClB;;AAOA,IAAa,mBAAmB;CAC5B,UAAU;CACV,YAAY,CAAC,MAAM;CACnB,iBAAiB;CACjB,WAAW,CAAC,MAAM;CAClB,gBAAgB;CAChB,gBAAgB;CAChB,gBAAgB;CAChB,MAAM;AACV;;;;;;;AAQA,IAAa,yBAA2D;CACpE,iBACI;CAEJ,WACI;CAEJ,aACI;AACR;;AAGA,SAAS,qBAAqB,SAAyB;CACnD,KAAK,MAAM,SAAS,QAAQ,MAAM,CAAC,GAAG;EAElC,IAAI,UAAU,MAAM;EACpB,MAAM,OAAO,MAAM,MAAM,KAAK,CAAC,CAAC,CAAC;EACjC,MAAM,cAAc,OAAO,UAAU,eAAe,KAAK,wBAAwB,IAAI,IAC/E,uBAAuB,QACvB,KAAA;EACN,IAAI,aAAa,MAAM,IAAI,WAAW,WAAW;CACrD;AACJ;;;;;;AAOA,SAAS,UAAU,QAAiD;CAChE,MAAM,sBAAM,IAAI,IAAY;CAC5B,KAAK,MAAM,SAAS,UAAU,CAAC,GAC3B,KAAK,MAAM,SAAS,MAAM,MAAM,QAAQ,GACpC,IAAI,OAAO,IAAI,IAAI,KAAK;CAGhC,OAAO,CAAC,GAAG,GAAG;AAClB;AAEA,IAAM,SAAS;AAEf,SAAS,cAAc,WAA+B,WAA+B,KAA0B;CAC3G,IAAI,cAAc,KAAA,KAAa,cAAc,KAAA,GACzC,MAAM,IAAI,WAAW,8CAA8C;CAEvE,IAAI,cAAc,KAAA,GAAW;EACzB,MAAM,OAAO,QAAQ,KAAK,UAAU,KAAK,CAAC,IAAI,OAAO,UAAU,KAAK,CAAC,IAAI;EACzE,IAAI,CAAC,OAAO,cAAc,IAAI,KAAK,OAAO,GACtC,MAAM,IAAI,WAAW,yEAAyE,UAAU,IAAI;EAEhH,OAAO,IAAI,KAAK,IAAI,QAAQ,IAAI,OAAO,MAAM,CAAC,CAAC,YAAY;CAC/D;CACA,IAAI,cAAc,KAAA,GAAW;EACzB,MAAM,SAAS,IAAI,KAAK,SAAS;EACjC,IAAI,MAAM,OAAO,QAAQ,CAAC,GACtB,MAAM,IAAI,WAAW,sEAAsE,UAAU,IAAI;EAE7G,IAAI,OAAO,QAAQ,KAAK,IAAI,QAAQ,GAChC,MAAM,IAAI,WAAW,2CAA2C,OAAO,YAAY,EAAE,GAAG;EAE5F,OAAO,OAAO,YAAY;CAC9B;CACA,OAAO;AACX;;;;;;;;;;;;;;;;;AA6BA,SAAgB,qBAAqB,SAAmB,sBAAY,IAAI,KAAK,GAAkB;CAC3F,qBAAqB,OAAO;CAE5B,MAAM,EAAE,OAAO,gBAAgB,iBAAiB;EAC5C,MAAM;EACN;EACA,cAAc;EACd,SAAS;EACT,gBAAgB;CACpB,CAAC;CAED,MAAM,QAAQ,MAAM,aAAa,YAAY,MAAM,GAAA,CAAI,KAAK;CAC5D,IAAI,CAAC,MACD,MAAM,IAAI,WAAW,sEAAoE;CAG7F,MAAM,QAAQ,UAAU,MAAM,WAAW;CACzC,MAAM,aAAa,MAAM,qBAAqB;CAC9C,IAAI,cAAc,MAAM,SAAS,GAC7B,MAAM,IAAI,WAAW,6FAA6F;CAEtH,IAAI,CAAC,cAAc,MAAM,WAAW,GAChC,MAAM,IAAI,WACN,sJAEJ;CAGJ,MAAM,YAAY,MAAM;CACxB,IAAI,cAAc,KAAA,MAAc,CAAC,OAAO,cAAc,SAAS,KAAK,YAAY,IAC5E,MAAM,IAAI,WAAW,4FAA4F;CAGrH,OAAO;EACH;EACA,QAAQ,aAAa,SAAS;EAC9B,OAAO,UAAU,MAAM,UAAU;EACjC,YAAY,aAAa;EACzB,YAAY,cAAc,MAAM,iBAAiB,MAAM,iBAAiB,GAAG;CAC/E;AACJ;;;;;;;AAQA,SAAgB,gBAAgB,MAAmC;CAC/D,MAAM,aAAgC;CACtC,OAAO,KAAK,QAAO,UAAS,CAAC,WAAW,SAAS,WAAW,KAAK,CAAC,EAAE,SAAS,KAAK,CAAC;AACvF;AAEA,eAAe,UAAU,SAAkC;CACvD,MAAM,OAAO,qBAAqB,OAAO;CACzC,MAAM,aAAa,QAAQ;CAC3B,MAAM,EAAE,WAAW;CAEnB,IAAI;CACJ,IAAI,KAAK,WAAW,QAAQ;EACxB,IAAI;EACJ,IAAI;GACA,CAAC,CAAE,QAAS,MAAM,OAAO,aAAa,WAAW;EACrD,SAAS,GAAY;GACjB,KAAK,sCAAsC,GAAG,UAAU;EAC5D;EACA,SAAS,gBAAgB,IAAI;EAC7B,IAAI,OAAO,WAAW,GAAG;GACrB,QAAQ,MAAM,MAAM,IAAI,6EAA6E,CAAC;GACtG,QAAQ,KAAK,CAAC;EAClB;CACJ,OACI,SAAS,KAAK;CAGlB,MAAM,UAA+B;EACjC,MAAM,KAAK;EACX;EACA,GAAI,KAAK,MAAM,SAAS,IAAI,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;EACrD,GAAI,KAAK,eAAe,OAAO,EAAE,YAAY,KAAK,WAAW,IAAI,CAAC;EAClE,GAAI,KAAK,eAAe,OAAO,EAAE,YAAY,KAAK,WAAW,IAAI,CAAC;CACtE;CAEA,IAAI;CACJ,IAAI;EACA,CAAC,CAAE,KAAK,WAAY,MAAM,OAAO,QAAQ,UAAU,OAAO;CAC9D,SAAS,GAAY;EACjB,KAAK,4BAA4B,GAAG,UAAU;CAClD;CAEA,QAAQ,IAAI,EAAE;CACd,QAAQ,IAAI,MAAM,KAAK,MAAM,qBAAqB,CAAC;CACnD,QAAQ,IAAI,EAAE;CACd,KAAK,MAAM,QAAQ,YAAY,OAAO,GAAG,QAAQ,IAAI,IAAI;CACzD,QAAQ,IAAI,EAAE;CACd,QAAQ,IAAI,MAAM,KAAK,OAAO,kDAAkD,CAAC;CACjF,QAAQ,IAAI,EAAE;CACd,QAAQ,IAAI,KAAK,MAAM,KAAK,QAAQ,GAAG,GAAG;CAC1C,QAAQ,IAAI,EAAE;AAClB;;AAOA,IAAa,mBAAmB,EAC5B,QAAQ,OACZ;;;;;;;;;;;AAYA,SAAgB,qBAAqB,SAAoC;CACrE,MAAM,EAAE,OAAO,gBAAgB,iBAAiB;EAC5C,MAAM;EACN;EACA,cAAc;EACd,SAAS;EACT,gBAAgB;CACpB,CAAC;CAED,OAAO,EAAE,IAAI,MAAM,WAAW,YAAY,GAAG;AACjD;AAEA,eAAe,UAAU,SAAkC;CACvD,MAAM,EAAE,OAAO,qBAAqB,OAAO;CAE3C,IAAI,CAAC,IACD,MAAM,IAAI,WAAW,0FAA0F;CAGnH,MAAM,aAAa,QAAQ;CAC3B,MAAM,EAAE,WAAW;CACnB,IAAI;EACA,MAAM,OAAO,QAAQ,UAAU,EAAE;CACrC,SAAS,GAAY;EACjB,KAAK,4BAA4B,GAAG,UAAU;CAClD;CAEA,QAAQ,IAAI,EAAE;CACd,QAAQ,IAAI,MAAM,KAAK,MAAM,qBAAqB,CAAC;CACnD,QAAQ,IAAI,KAAK,MAAM,KAAK,KAAK,EAAE,GAAG,IAAI;CAC1C,QAAQ,IAAI,EAAE;AAClB;;AAOA,IAAa,oBAAoB,CAAC;AAElC,IAAM,SAAuF;CACzF;EAAE,OAAO;EAAQ,OAAO;EAAQ,MAAM;CAAuE;CAC7G;EAAE,OAAO;EAAS,OAAO;EAAS,MAAM;CAAyE;CACjH;EAAE,OAAO;EAAO,OAAO;EAAO,MAAM;CAAiE;AACzG;;;;;;;AAQA,SAAgB,eAAe,QAAiC,MAAmC;CAC/F,MAAM,aAAgC;CACtC,MAAM,QAAQ,KAAK,IAAI,GAAG,GAAG,OAAO,KAAI,MAAK,EAAE,MAAM,MAAM,CAAC,IAAI;CAChE,MAAM,aAAa,KAAK,IAAI,GAAG,GAAG,OAAO,KAAI,MAAK,EAAE,MAAM,MAAM,CAAC,IAAI;CACrE,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,EAAE,OAAO,OAAO,UAAU,QAAQ;EACzC,MAAM,UAAU,OAAO,QAAO,MAAK,EAAE,UAAU,KAAK;EACpD,IAAI,QAAQ,WAAW,GAAG;EAC1B,MAAM,KAAK,KAAK,MAAM,KAAK,KAAK,EAAE,GAAG,MAAM,KAAK,KAAK,MAAM,GAAG;EAC9D,KAAK,MAAM,KAAK,SAAS;GACrB,MAAM,OAAO,KAAK,SAAS,EAAE,KAAK,IAAI,MAAM,MAAM,GAAG,IAAI;GACzD,MAAM,QAAQ,WAAW,SAAS,EAAE,KAAK,IACnC,MAAM,KAAK,gBAAgB,IAC3B,EAAE,SAAS,MAAM,KAAK,KAAK,EAAE,OAAO,EAAE,IAAI;GAChD,MAAM,KAAK,KAAK,KAAK,GAAG,EAAE,MAAM,OAAO,KAAK,IAAI,QAAQ,EAAE,MAAM,OAAO,UAAU,IAAI,EAAE,QAAQ,OAAO;EAC1G;EACA,MAAM,KAAK,EAAE;CACjB;CACA,OAAO;AACX;AAEA,eAAe,WAAW,SAAkC;CACxD,iBAAiB;EACb,MAAM;EACN;EACA,cAAc;EACd,SAAS;EACT,gBAAgB;CACpB,CAAC;CAED,MAAM,aAAa,QAAQ;CAC3B,MAAM,EAAE,WAAW;CACnB,IAAI;CACJ,IAAI;EACA,YAAY,MAAM,OAAO,aAAa,WAAW;CACrD,SAAS,GAAY;EACjB,KAAK,6BAA6B,GAAG,UAAU;CACnD;CAEA,QAAQ,IAAI,EAAE;CACd,QAAQ,IAAI,MAAM,KAAK,UAAU,IAAI,MAAM,KAAK,kDAAkD,CAAC;CACnG,QAAQ,IAAI,EAAE;CACd,KAAK,MAAM,QAAQ,eAAe,UAAU,QAAQ,UAAU,IAAI,GAAG,QAAQ,IAAI,IAAI;AACzF;AAMA,SAAS,mBAAmB;CACxB,MAAM,UAAU,aAAa,QAAO,UAAS,CAAE,sBAA4C,SAAS,KAAK,CAAC;CAC1G,QAAQ,IAAI;EACd,MAAM,KAAK,iBAAiB,EAAE;;EAE9B,MAAM,MAAM,KAAK,OAAO,EAAE;oBACR,MAAM,KAAK,WAAW,EAAE;;EAE1C,MAAM,MAAM,KAAK,UAAU,EAAE;IAC3B,MAAM,KAAK,KAAK,MAAM,EAAE;IACxB,MAAM,KAAK,KAAK,KAAK,EAAE,GAAG,MAAM,KAAK,MAAM,EAAE;IAC7C,MAAM,KAAK,KAAK,QAAQ,EAAE;IAC1B,MAAM,KAAK,KAAK,QAAQ,EAAE,GAAG,MAAM,KAAK,MAAM,EAAE;IAChD,MAAM,KAAK,KAAK,QAAQ,EAAE;;EAE5B,MAAM,MAAM,KAAK,gBAAgB,EAAE;IACjC,MAAM,KAAK,YAAY,EAAE,mBAAmB,MAAM,KAAK,mCAAmC,EAAE;IAC5F,MAAM,KAAK,UAAU,EAAE;sBACL,MAAM,KAAK,iCAAiC,EAAE;IAChE,MAAM,KAAK,eAAe,EAAE;IAC5B,MAAM,KAAK,SAAS,EAAE,8CAA8C,MAAM,KAAK,SAAS,EAAE;IAC1F,MAAM,KAAK,cAAc,EAAE,mCAAmC,MAAM,KAAK,uCAAuC,EAAE;IAClH,MAAM,KAAK,cAAc,EAAE;IAC3B,MAAM,KAAK,cAAc,EAAE;;EAE7B,MAAM,MAAM,KAAK,sBAAsB,EAAE;IACvC,MAAM,KAAK,MAAM,EAAE,2BAA2B,MAAM,KAAK,yBAAyB,EAAE;;EAEtF,MAAM,MAAM,KAAK,QAAQ,EAAE;eACd,MAAM,KAAK,iBAAiB,EAAE;IACzC,MAAM,KAAK,iBAAiB,EAAE;WACvB,kBAAkB,KAAK,IAAI,EAAE;WAC7B,MAAM,KAAK,2EAA2E,EAAE;WACxF,QAAQ,KAAK,IAAI,EAAE;uDACyB,MAAM,KAAK,0BAA0B,EAAE;;EAE5F,MAAM,MAAM,KAAK,UAAU,EAAE;;;;;;;;CAQ9B;AACD"}
|