@rebasepro/cli 0.23.0 → 0.24.1-canary.g19596ef
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 +19 -3
- package/bin/rebase.js +4 -1
- package/dist/agent-setup-B31F00DC.js +324 -0
- package/dist/agent-setup-B31F00DC.js.map +1 -0
- package/dist/api-keys-Bl1I9q7M.js +525 -0
- package/dist/api-keys-Bl1I9q7M.js.map +1 -0
- package/dist/apps-CNgQJjg5.js +340 -0
- package/dist/apps-CNgQJjg5.js.map +1 -0
- package/dist/args-BnmsEXvu.js +109 -0
- package/dist/args-BnmsEXvu.js.map +1 -0
- package/dist/atlas-dev-database-D_LP_Woq.js +62 -0
- package/dist/atlas-dev-database-D_LP_Woq.js.map +1 -0
- package/dist/auth-aySHEF45.js +414 -0
- package/dist/auth-aySHEF45.js.map +1 -0
- package/dist/branch-pointer-DvSHBjjz.js +2 -0
- package/dist/build-DIHWIqO4.js +336 -0
- package/dist/build-DIHWIqO4.js.map +1 -0
- package/dist/bundle-CqURVN-K.js +1621 -0
- package/dist/bundle-CqURVN-K.js.map +1 -0
- package/dist/bundle.d.ts +12 -3
- package/dist/cli.d.ts +23 -2
- package/dist/cloud-DjByQlkW.js +9667 -0
- package/dist/cloud-DjByQlkW.js.map +1 -0
- package/dist/collection-drift-BKj_BoMq.js +106 -0
- package/dist/collection-drift-BKj_BoMq.js.map +1 -0
- package/dist/commands/__test_support__/parser-specs.d.ts +13 -0
- package/dist/commands/agent-setup.d.ts +116 -0
- package/dist/commands/api-keys.d.ts +86 -23
- package/dist/commands/apps.d.ts +17 -0
- package/dist/commands/auth.d.ts +7 -0
- package/dist/commands/build.d.ts +5 -1
- package/dist/commands/cloud/auth.d.ts +4 -3
- package/dist/commands/cloud/bundle-deploy.d.ts +45 -0
- package/dist/commands/cloud/context.d.ts +44 -5
- package/dist/commands/cloud/cron.d.ts +53 -0
- package/dist/commands/cloud/debug.d.ts +134 -0
- package/dist/commands/cloud/index.d.ts +89 -12
- package/dist/commands/cloud/token-capabilities.d.ts +76 -0
- package/dist/commands/cloud/tokens.d.ts +23 -0
- package/dist/commands/console.d.ts +12 -0
- package/dist/commands/db.d.ts +79 -25
- package/dist/commands/dev.d.ts +181 -13
- package/dist/commands/doctor.d.ts +5 -0
- package/dist/commands/generate_sdk.d.ts +29 -2
- package/dist/commands/init.d.ts +115 -14
- package/dist/commands/schema.d.ts +6 -0
- package/dist/commands/skills.d.ts +126 -7
- package/dist/commands/start.d.ts +44 -0
- package/dist/commands/storage.d.ts +46 -0
- package/dist/commands/upgrade.d.ts +1 -0
- package/dist/consent-ClRHmcxf.js +76 -0
- package/dist/consent-ClRHmcxf.js.map +1 -0
- package/dist/console-D6SJf8k_.js +102 -0
- package/dist/console-D6SJf8k_.js.map +1 -0
- package/dist/context-BqhRydBg.js +1234 -0
- package/dist/context-BqhRydBg.js.map +1 -0
- package/dist/{daemon-entry-LTFKtpHy.js → daemon-entry-Da-D3rmH.js} +3 -4
- package/dist/daemon-entry-Da-D3rmH.js.map +1 -0
- package/dist/daemon-pzsQwDHa.js +2 -0
- package/dist/db-B7RuNuaQ.js +1312 -0
- package/dist/db-B7RuNuaQ.js.map +1 -0
- package/dist/derive-Dl0z2OWi.js +484 -0
- package/dist/derive-Dl0z2OWi.js.map +1 -0
- package/dist/dev-CYr_WXan.js +1599 -0
- package/dist/dev-CYr_WXan.js.map +1 -0
- package/dist/dev-db/atlas-dev-database.d.ts +32 -0
- package/dist/dev-db/daemon-entry.d.ts +13 -0
- package/dist/dev-db/daemon.d.ts +10 -0
- package/dist/dev-db/last-dev-database.d.ts +41 -0
- package/dist/dev-db/prepare.d.ts +29 -3
- package/dist/dev-db/pull.d.ts +90 -5
- package/dist/dev-db/resolve.d.ts +26 -2
- package/dist/dev-preflight-B-LYyBf1.js +2 -0
- package/dist/dev-server-DG8Tt-uf.js +96 -0
- package/dist/dev-server-DG8Tt-uf.js.map +1 -0
- package/dist/doctor-BP8cZ-7F.js +771 -0
- package/dist/doctor-BP8cZ-7F.js.map +1 -0
- package/dist/eject-DizdoecV.js +335 -0
- package/dist/eject-DizdoecV.js.map +1 -0
- package/dist/fold-static-BC9CYicw.js +201 -0
- package/dist/fold-static-BC9CYicw.js.map +1 -0
- package/dist/fold-static.d.ts +26 -3
- package/dist/function-portability-BIvlvR9q.js +265 -0
- package/dist/function-portability-BIvlvR9q.js.map +1 -0
- package/dist/generate_sdk-BPqdJc3Q.js +489 -0
- package/dist/generate_sdk-BPqdJc3Q.js.map +1 -0
- package/dist/index.es.js +199 -22095
- package/dist/index.es.js.map +1 -1
- package/dist/init-Utstvh2Q.js +1149 -0
- package/dist/init-Utstvh2Q.js.map +1 -0
- package/dist/last-dev-database-B58g89uA.js +2 -0
- package/dist/manifest-DRp0UVTj.js +880 -0
- package/dist/manifest-DRp0UVTj.js.map +1 -0
- package/dist/manifest.d.ts +56 -4
- package/dist/normalize-imports-X0Tgze9y.js +107 -0
- package/dist/normalize-imports-X0Tgze9y.js.map +1 -0
- package/dist/package-manager-DSEOwTqM.js +193 -0
- package/dist/package-manager-DSEOwTqM.js.map +1 -0
- package/dist/path-flags-BFgG9V6y.js +91 -0
- package/dist/path-flags-BFgG9V6y.js.map +1 -0
- package/dist/precompress.d.ts +17 -0
- package/dist/prepare-CDN6lSpz.js +1236 -0
- package/dist/prepare-CDN6lSpz.js.map +1 -0
- package/dist/{project-3WCbrrfW.js → project-Wp2zoyeN.js} +34 -184
- package/dist/project-Wp2zoyeN.js.map +1 -0
- package/dist/pull-CrGYMEsy.js +458 -0
- package/dist/pull-CrGYMEsy.js.map +1 -0
- package/dist/resolve-B2O-wKKk.js +2 -0
- package/dist/resources-FD7AfZx8.js +183 -0
- package/dist/resources-FD7AfZx8.js.map +1 -0
- package/dist/schema-DMdr431S.js +125 -0
- package/dist/schema-DMdr431S.js.map +1 -0
- package/dist/skills-DTFibT1m.js +642 -0
- package/dist/skills-DTFibT1m.js.map +1 -0
- package/dist/start-DEa-hOBh.js +267 -0
- package/dist/start-DEa-hOBh.js.map +1 -0
- package/dist/{state-C59Elrnt.js → state-Do7ZJYkF.js} +49 -4
- package/dist/state-Do7ZJYkF.js.map +1 -0
- package/dist/status-DxnJTekv.js +352 -0
- package/dist/status-DxnJTekv.js.map +1 -0
- package/dist/storage-DyVq-f_F.js +303 -0
- package/dist/storage-DyVq-f_F.js.map +1 -0
- package/dist/telemetry/identity.d.ts +10 -1
- package/dist/telemetry-C8Uq8phb.js +129 -0
- package/dist/telemetry-C8Uq8phb.js.map +1 -0
- package/dist/telemetry-EMFRVRQi.js +351 -0
- package/dist/telemetry-EMFRVRQi.js.map +1 -0
- package/dist/unknown-command-DNwDcX3w.js +98 -0
- package/dist/unknown-command-DNwDcX3w.js.map +1 -0
- package/dist/upgrade-CtZtRkTE.js +1332 -0
- package/dist/upgrade-CtZtRkTE.js.map +1 -0
- package/dist/upgrade.d.ts +38 -5
- package/dist/utils/dev-preflight.d.ts +47 -5
- package/dist/utils/dev-server.d.ts +30 -0
- package/dist/utils/path-flags.d.ts +31 -0
- package/dist/utils/project.d.ts +16 -0
- package/dist/utils/temp-archive.d.ts +9 -0
- package/dist/version-Buyyo1ut.js +58 -0
- package/dist/version-Buyyo1ut.js.map +1 -0
- package/package.json +7 -7
- package/templates/eject/Dockerfile +4 -2
- package/templates/eject/backend/src/env.ts +1 -1
- package/templates/eject/backend/src/index.ts +42 -4
- package/templates/eject/docker-compose.custom.yml +8 -4
- package/templates/generated-schemas/blank.ts +40 -0
- package/templates/generated-schemas/blog.ts +127 -0
- package/templates/generated-schemas/ecommerce.ts +93 -0
- package/templates/overlays/baas/ai-instructions.md +1 -1
- package/templates/overlays/baas/config/package.json +1 -0
- package/templates/overlays/baas/config/storage.ts +5 -3
- package/templates/overlays/baas/package.json +1 -0
- package/templates/template/.cursorrules +1 -1
- package/templates/template/.env.example +20 -10
- package/templates/template/.github/copilot-instructions.md +1 -1
- package/templates/template/.mcp.json +2 -2
- package/templates/template/.windsurfrules +1 -1
- package/templates/template/AGENTS.md +1 -1
- package/templates/template/CLAUDE.md +1 -1
- package/templates/template/README.md +21 -15
- package/templates/template/ai-instructions.md +5 -4
- package/templates/template/backend/functions/hello.ts +8 -0
- package/templates/template/backend/package.json +1 -1
- package/templates/template/config/resources.ts +7 -6
- package/templates/template/config/storage.ts +5 -3
- package/templates/template/docker-compose.yml +15 -9
- package/templates/template/frontend/vite.config.ts +13 -3
- package/templates/template/gitignore +6 -1
- package/templates/template/package.json +5 -3
- package/templates/template/scripts/example.ts +6 -4
- package/templates/template/tsconfig.json +25 -0
- package/dist/branch-pointer-dBiClJ0p.js +0 -111
- package/dist/branch-pointer-dBiClJ0p.js.map +0 -1
- package/dist/constraints-DKGbNfUW.js +0 -48
- package/dist/constraints-DKGbNfUW.js.map +0 -1
- package/dist/daemon-DR9Tk0La.js +0 -355
- package/dist/daemon-DR9Tk0La.js.map +0 -1
- package/dist/daemon-entry-LTFKtpHy.js.map +0 -1
- package/dist/dev-preflight-CNLl4rdo.js +0 -367
- package/dist/dev-preflight-CNLl4rdo.js.map +0 -1
- package/dist/project-3WCbrrfW.js.map +0 -1
- package/dist/pull-Dywm5FWP.js +0 -232
- package/dist/pull-Dywm5FWP.js.map +0 -1
- package/dist/resolve-BAeRUBhg.js +0 -74
- package/dist/resolve-BAeRUBhg.js.map +0 -1
- package/dist/state-C59Elrnt.js.map +0 -1
- package/templates/template/backend/src/schema.generated.ts +0 -13
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
import { d as readEnvFile, p as requireProjectRoot } from "./project-Wp2zoyeN.js";
|
|
2
|
+
import { n as parseCommandArgs, r as wantsHelp, t as UsageError } from "./args-BnmsEXvu.js";
|
|
3
|
+
import { t as unknownCommand } from "./unknown-command-DNwDcX3w.js";
|
|
4
|
+
import { t as cliUserAgent } from "./version-Buyyo1ut.js";
|
|
5
|
+
import { r as serverUnreachableHint } from "./dev-server-DG8Tt-uf.js";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import fs from "fs";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import { ADMIN_SCOPES, DATA_PLANE_SCOPES, KEY_MANAGEMENT_SCOPES, parseScope } from "@rebasepro/types";
|
|
10
|
+
import { RebaseApiError, createRebaseClient } from "@rebasepro/client";
|
|
11
|
+
//#region src/commands/api-keys.ts
|
|
12
|
+
/**
|
|
13
|
+
* CLI command: rebase api-keys <action>
|
|
14
|
+
*
|
|
15
|
+
* The project's service keys, through `/api/admin/api-keys`, authenticated with
|
|
16
|
+
* the service key from `.env`.
|
|
17
|
+
*
|
|
18
|
+
* Subcommands:
|
|
19
|
+
* list — List the service keys (masked): kind, scopes, roles
|
|
20
|
+
* get — Show one key
|
|
21
|
+
* create — Create a service key holding the scopes named on the line
|
|
22
|
+
* revoke — Revoke a key
|
|
23
|
+
* scopes — List the scopes this backend knows, and which the caller holds
|
|
24
|
+
*/
|
|
25
|
+
/** Everything the switch below dispatches, for the did-you-mean. */
|
|
26
|
+
var API_KEYS_SUBCOMMANDS = [
|
|
27
|
+
"list",
|
|
28
|
+
"get",
|
|
29
|
+
"create",
|
|
30
|
+
"revoke",
|
|
31
|
+
"scopes"
|
|
32
|
+
];
|
|
33
|
+
/**
|
|
34
|
+
* Was a hand-rolled `indexOf("=")` loop. It keyed `export KEY=value` as
|
|
35
|
+
* `export KEY` and carried a trailing `# comment` into the value — so a key
|
|
36
|
+
* that was present read as absent, or reached an `Authorization` header with a
|
|
37
|
+
* comment attached and came back 401. See `readEnvFile`.
|
|
38
|
+
*/
|
|
39
|
+
var loadEnv = readEnvFile;
|
|
40
|
+
/** The running backend's URL. Shared with `rebase storage`. */
|
|
41
|
+
function resolveBaseUrl(env, projectRoot) {
|
|
42
|
+
if (env.REBASE_BASE_URL) return env.REBASE_BASE_URL;
|
|
43
|
+
if (projectRoot) try {
|
|
44
|
+
const urlFile = path.join(projectRoot, ".rebase-dev-url");
|
|
45
|
+
if (fs.existsSync(urlFile)) {
|
|
46
|
+
const devUrl = fs.readFileSync(urlFile, "utf-8").trim();
|
|
47
|
+
if (devUrl) return devUrl;
|
|
48
|
+
}
|
|
49
|
+
} catch {}
|
|
50
|
+
return `http://localhost:${env.PORT || env.REBASE_PORT || "3001"}`;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* An SDK client for this project's backend, holding the service key.
|
|
54
|
+
*
|
|
55
|
+
* The service key is the project's own machine identity — the `admin` role,
|
|
56
|
+
* every scope — so it is what may list, mint and revoke service keys. It is a
|
|
57
|
+
* static bearer token: no session to restore or refresh, and no socket.
|
|
58
|
+
*/
|
|
59
|
+
function connect() {
|
|
60
|
+
const projectRoot = requireProjectRoot();
|
|
61
|
+
const env = loadEnv(projectRoot);
|
|
62
|
+
const baseUrl = resolveBaseUrl(env, projectRoot);
|
|
63
|
+
const serviceKey = env.SERVICE_KEY || env.REBASE_SERVICE_KEY;
|
|
64
|
+
if (!serviceKey) {
|
|
65
|
+
console.error(chalk.red("✗ SERVICE_KEY not found in .env — required to manage API keys."));
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
client: createRebaseClient({
|
|
70
|
+
baseUrl,
|
|
71
|
+
token: serviceKey,
|
|
72
|
+
realtime: false,
|
|
73
|
+
headers: { "User-Agent": cliUserAgent() },
|
|
74
|
+
auth: {
|
|
75
|
+
persistSession: false,
|
|
76
|
+
autoRefresh: false
|
|
77
|
+
}
|
|
78
|
+
}),
|
|
79
|
+
baseUrl
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* What to print when a request fails: the server's error code and message,
|
|
84
|
+
* which is what says why — `SCOPE_EXCEEDS_CREATOR`, `INVALID_SCOPES`,
|
|
85
|
+
* `ROLE_EXCEEDS_CREATOR`, `KEY_MANAGEMENT_SCOPE` — and a hint where one helps.
|
|
86
|
+
*
|
|
87
|
+
* Exported so its tests can read the lines without a server.
|
|
88
|
+
*/
|
|
89
|
+
function describeFailure(action, error, baseUrl) {
|
|
90
|
+
if (!(error instanceof RebaseApiError)) return [`✗ ${action}: ${error instanceof Error ? error.message : String(error)}`];
|
|
91
|
+
if (error.status === 0 || error.code === "NETWORK_ERROR") return [`✗ ${action}: ${error.message}`, ...serverUnreachableHint(baseUrl)];
|
|
92
|
+
const label = [error.code, error.status ? `(${error.status})` : void 0].filter(Boolean).join(" ");
|
|
93
|
+
const lines = [`✗ ${action}${label ? ` — ${label}` : ""}: ${error.message}`];
|
|
94
|
+
if (error.status === 401) lines.push(` The server at ${baseUrl} did not accept SERVICE_KEY from .env — is it this project's server?`);
|
|
95
|
+
if (error.code === "UNKNOWN_SCOPE_TARGET" && isRecord(error.details)) for (const [kind, title] of [
|
|
96
|
+
["collections", "Collections"],
|
|
97
|
+
["buckets", "Buckets"],
|
|
98
|
+
["functions", "Functions"]
|
|
99
|
+
]) {
|
|
100
|
+
const names = error.details[kind];
|
|
101
|
+
if (Array.isArray(names) && names.length > 0) lines.push(` ${title}: ${names.map(String).join(", ")}`);
|
|
102
|
+
}
|
|
103
|
+
return lines;
|
|
104
|
+
}
|
|
105
|
+
function isRecord(value) {
|
|
106
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
107
|
+
}
|
|
108
|
+
function fail(action, error, baseUrl) {
|
|
109
|
+
for (const line of describeFailure(action, error, baseUrl)) console.error(line.startsWith("✗") ? chalk.red(line) : chalk.gray(line));
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
async function apiKeysCommand(subcommand, rawArgs) {
|
|
113
|
+
if (!subcommand || subcommand === "--help" || wantsHelp(rawArgs)) {
|
|
114
|
+
printApiKeysHelp();
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
switch (subcommand) {
|
|
118
|
+
case "list":
|
|
119
|
+
await listKeys(rawArgs);
|
|
120
|
+
break;
|
|
121
|
+
case "get":
|
|
122
|
+
await getKey(rawArgs);
|
|
123
|
+
break;
|
|
124
|
+
case "create":
|
|
125
|
+
await createKey(rawArgs);
|
|
126
|
+
break;
|
|
127
|
+
case "revoke":
|
|
128
|
+
await revokeKey(rawArgs);
|
|
129
|
+
break;
|
|
130
|
+
case "scopes":
|
|
131
|
+
await listScopes(rawArgs);
|
|
132
|
+
break;
|
|
133
|
+
default: unknownCommand(subcommand, API_KEYS_SUBCOMMANDS, "api-keys");
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function keyStatus(key, now) {
|
|
137
|
+
if (key.revoked_at) return chalk.red("revoked");
|
|
138
|
+
if (key.expires_at && new Date(key.expires_at) < now) return chalk.yellow("expired");
|
|
139
|
+
return chalk.green("active");
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* The lines that describe one key: what it is, what it may do and whom it
|
|
143
|
+
* runs as.
|
|
144
|
+
*
|
|
145
|
+
* A service key runs as the RLS role `service` plus its own `roles`; a
|
|
146
|
+
* personal key runs as its owner's roles as they are when it is used, so it
|
|
147
|
+
* has none of its own to show.
|
|
148
|
+
*
|
|
149
|
+
* Exported so its tests can read the lines without a server.
|
|
150
|
+
*/
|
|
151
|
+
function describeKey(key, now = /* @__PURE__ */ new Date()) {
|
|
152
|
+
const field = (label, value) => ` ${chalk.gray(`${label}:`.padEnd(12))}${value}`;
|
|
153
|
+
const roles = key.kind === "personal" ? `its owner's (${key.owner_uid ?? "unknown"})` : ["service", ...key.roles].join(", ");
|
|
154
|
+
const lines = [
|
|
155
|
+
` ${chalk.bold(key.name)} ${chalk.gray(`[${key.key_prefix}•••]`)} ${keyStatus(key, now)}`,
|
|
156
|
+
field("ID", key.id),
|
|
157
|
+
field("Kind", key.kind),
|
|
158
|
+
field("Scopes", key.scopes.length > 0 ? key.scopes.join(", ") : chalk.gray("none")),
|
|
159
|
+
field("Roles", roles)
|
|
160
|
+
];
|
|
161
|
+
if (key.rate_limit !== null) lines.push(field("Rate limit", `${key.rate_limit} requests / 15 min`));
|
|
162
|
+
if (key.expires_at) lines.push(field("Expires", new Date(key.expires_at).toLocaleDateString()));
|
|
163
|
+
lines.push(field("Created", new Date(key.created_at).toLocaleDateString()));
|
|
164
|
+
if (key.last_used_at) lines.push(field("Last used", new Date(key.last_used_at).toLocaleDateString()));
|
|
165
|
+
return lines;
|
|
166
|
+
}
|
|
167
|
+
/** The flags `rebase api-keys list` takes: none of its own. */
|
|
168
|
+
var LIST_KEYS_FLAGS = {};
|
|
169
|
+
async function listKeys(rawArgs) {
|
|
170
|
+
parseCommandArgs({
|
|
171
|
+
spec: LIST_KEYS_FLAGS,
|
|
172
|
+
rawArgs,
|
|
173
|
+
commandWords: 2,
|
|
174
|
+
command: "api-keys list",
|
|
175
|
+
maxPositionals: 0
|
|
176
|
+
});
|
|
177
|
+
const { client, baseUrl } = connect();
|
|
178
|
+
let keys;
|
|
179
|
+
try {
|
|
180
|
+
({keys} = await client.apiKeys.listKeys());
|
|
181
|
+
} catch (e) {
|
|
182
|
+
fail("Failed to list API keys", e, baseUrl);
|
|
183
|
+
}
|
|
184
|
+
console.log("");
|
|
185
|
+
console.log(chalk.bold(" 🔑 API Keys"));
|
|
186
|
+
console.log("");
|
|
187
|
+
if (keys.length === 0) {
|
|
188
|
+
console.log(chalk.gray(" No API keys found."));
|
|
189
|
+
console.log("");
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const now = /* @__PURE__ */ new Date();
|
|
193
|
+
for (const key of keys) {
|
|
194
|
+
for (const line of describeKey(key, now)) console.log(line);
|
|
195
|
+
console.log("");
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
/** The flags `rebase api-keys get` takes. */
|
|
199
|
+
var GET_KEY_FLAGS = { "--id": String };
|
|
200
|
+
/**
|
|
201
|
+
* Which key this invocation names, as a positional or as `--id`.
|
|
202
|
+
*
|
|
203
|
+
* Exported so its tests can drive the real parser rather than a copy of it.
|
|
204
|
+
*/
|
|
205
|
+
function resolveGetKeyArgs(rawArgs) {
|
|
206
|
+
const { flags, positionals } = parseCommandArgs({
|
|
207
|
+
spec: GET_KEY_FLAGS,
|
|
208
|
+
rawArgs,
|
|
209
|
+
commandWords: 2,
|
|
210
|
+
command: "api-keys get",
|
|
211
|
+
maxPositionals: 1
|
|
212
|
+
});
|
|
213
|
+
return { id: flags["--id"] || positionals[0] };
|
|
214
|
+
}
|
|
215
|
+
async function getKey(rawArgs) {
|
|
216
|
+
const { id } = resolveGetKeyArgs(rawArgs);
|
|
217
|
+
if (!id) throw new UsageError("Key ID is required: rebase api-keys get <key-id> (`rebase api-keys list` shows them).");
|
|
218
|
+
const { client, baseUrl } = connect();
|
|
219
|
+
let key;
|
|
220
|
+
try {
|
|
221
|
+
({key} = await client.apiKeys.getKey(id));
|
|
222
|
+
} catch (e) {
|
|
223
|
+
fail("Failed to read API key", e, baseUrl);
|
|
224
|
+
}
|
|
225
|
+
console.log("");
|
|
226
|
+
for (const line of describeKey(key)) console.log(line);
|
|
227
|
+
console.log("");
|
|
228
|
+
}
|
|
229
|
+
/** The flags `rebase api-keys create` takes. */
|
|
230
|
+
var CREATE_KEY_FLAGS = {
|
|
231
|
+
"--name": String,
|
|
232
|
+
"--scopes": [String],
|
|
233
|
+
"--full-access": Boolean,
|
|
234
|
+
"--roles": [String],
|
|
235
|
+
"--rate-limit": Number,
|
|
236
|
+
"--expires-in": String,
|
|
237
|
+
"--expires-at": String,
|
|
238
|
+
"-n": "--name"
|
|
239
|
+
};
|
|
240
|
+
/**
|
|
241
|
+
* Options `create` refuses by name, each with the flag that does that job.
|
|
242
|
+
*
|
|
243
|
+
* Checked before the line is parsed: the strict parser would answer them
|
|
244
|
+
* "unknown option", which does not say what to type instead.
|
|
245
|
+
*/
|
|
246
|
+
var RETIRED_CREATE_OPTIONS = {
|
|
247
|
+
"--permissions": "--permissions is gone: name what the key may do with --scopes, e.g. --scopes data:read:posts,data:write:posts (`rebase api-keys scopes` lists them).",
|
|
248
|
+
"--admin": "--admin is gone: give the key the RLS role with --roles admin, and the admin-plane scopes it needs with --scopes, e.g. --scopes users:read,schema:read.",
|
|
249
|
+
"--expires": "--expires is gone: use --expires-in <days> (e.g. --expires-in 90) or --expires-at <ISO date>."
|
|
250
|
+
};
|
|
251
|
+
/** Refuse a retired option, wherever on the line and however it is spelled. */
|
|
252
|
+
function refuseRetiredOptions(rawArgs) {
|
|
253
|
+
for (const token of rawArgs.slice(2)) {
|
|
254
|
+
if (token === "--") return;
|
|
255
|
+
const name = token.split("=", 1)[0];
|
|
256
|
+
const replacement = Object.prototype.hasOwnProperty.call(RETIRED_CREATE_OPTIONS, name) ? RETIRED_CREATE_OPTIONS[name] : void 0;
|
|
257
|
+
if (replacement) throw new UsageError(replacement);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* A list flag's values, whether repeated (`--scopes a --scopes b`) or
|
|
262
|
+
* comma-separated (`--scopes a,b`) — or both — trimmed and deduplicated.
|
|
263
|
+
* Whitespace separates too: no scope or role name contains any.
|
|
264
|
+
*/
|
|
265
|
+
function splitList(values) {
|
|
266
|
+
const out = /* @__PURE__ */ new Set();
|
|
267
|
+
for (const value of values ?? []) for (const entry of value.split(/[\s,]+/)) if (entry) out.add(entry);
|
|
268
|
+
return [...out];
|
|
269
|
+
}
|
|
270
|
+
var DAY_MS = 864e5;
|
|
271
|
+
function resolveExpiry(expiresIn, expiresAt, now) {
|
|
272
|
+
if (expiresIn !== void 0 && expiresAt !== void 0) throw new UsageError("Pass --expires-in or --expires-at, not both.");
|
|
273
|
+
if (expiresIn !== void 0) {
|
|
274
|
+
const days = /^\d+$/.test(expiresIn.trim()) ? Number(expiresIn.trim()) : NaN;
|
|
275
|
+
if (!Number.isSafeInteger(days) || days < 1) throw new UsageError(`--expires-in takes a whole number of days, e.g. --expires-in 90 (got "${expiresIn}").`);
|
|
276
|
+
return new Date(now.getTime() + days * DAY_MS).toISOString();
|
|
277
|
+
}
|
|
278
|
+
if (expiresAt !== void 0) {
|
|
279
|
+
const parsed = new Date(expiresAt);
|
|
280
|
+
if (isNaN(parsed.getTime())) throw new UsageError(`--expires-at takes an ISO date, e.g. --expires-at 2027-01-31 (got "${expiresAt}").`);
|
|
281
|
+
if (parsed.getTime() <= now.getTime()) throw new UsageError(`--expires-at must be in the future (got ${parsed.toISOString()}).`);
|
|
282
|
+
return parsed.toISOString();
|
|
283
|
+
}
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* What this invocation asks to be created.
|
|
288
|
+
*
|
|
289
|
+
* The name may be given either way — `--name "My Key"` or as the single
|
|
290
|
+
* positional — and under the old permissive parse an undeclared flag became
|
|
291
|
+
* that positional: `rebase api-keys create --debug --full-access` created a
|
|
292
|
+
* key called `--debug` with access to everything, and `--debug` is what the
|
|
293
|
+
* CLI prints after every failure as the thing to re-run with. Strict parsing
|
|
294
|
+
* makes the flag an error instead of a name.
|
|
295
|
+
*
|
|
296
|
+
* What the key may do has to be asked for by name — `--scopes` or
|
|
297
|
+
* `--full-access` — never defaulted: a forgotten flag must not mint a key that
|
|
298
|
+
* reaches everything.
|
|
299
|
+
*
|
|
300
|
+
* Exported so its tests can drive the real parser rather than a copy of it.
|
|
301
|
+
*/
|
|
302
|
+
function resolveCreateKeyArgs(rawArgs, now = /* @__PURE__ */ new Date()) {
|
|
303
|
+
refuseRetiredOptions(rawArgs);
|
|
304
|
+
const { flags, positionals } = parseCommandArgs({
|
|
305
|
+
spec: CREATE_KEY_FLAGS,
|
|
306
|
+
rawArgs,
|
|
307
|
+
commandWords: 2,
|
|
308
|
+
command: "api-keys create",
|
|
309
|
+
maxPositionals: 1
|
|
310
|
+
});
|
|
311
|
+
const name = (flags["--name"] || positionals[0] || "").trim();
|
|
312
|
+
if (!name) throw new UsageError("A key needs a name: rebase api-keys create \"CI\" --scopes data:read");
|
|
313
|
+
const named = splitList(flags["--scopes"]);
|
|
314
|
+
const fullAccess = flags["--full-access"] === true;
|
|
315
|
+
if (fullAccess && named.length > 0) throw new UsageError("Pass --scopes or --full-access, not both: --full-access already means every scope you hold.");
|
|
316
|
+
if (!fullAccess && named.length === 0) throw new UsageError("Name what the key may do: --scopes data:read:posts,data:write:posts, or --full-access for every scope you hold. `rebase api-keys scopes` lists them.");
|
|
317
|
+
const rateLimit = flags["--rate-limit"];
|
|
318
|
+
if (rateLimit !== void 0 && (!Number.isSafeInteger(rateLimit) || rateLimit < 1)) throw new UsageError("--rate-limit takes a whole number of requests per 15-minute window, e.g. --rate-limit 500.");
|
|
319
|
+
return {
|
|
320
|
+
name,
|
|
321
|
+
scopes: fullAccess ? "held" : named,
|
|
322
|
+
roles: splitList(flags["--roles"]),
|
|
323
|
+
rate_limit: rateLimit ?? null,
|
|
324
|
+
expires_at: resolveExpiry(flags["--expires-in"], flags["--expires-at"], now)
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* The scopes `--full-access` puts on a key: every one the caller holds, less
|
|
329
|
+
* key management, which no key may hold.
|
|
330
|
+
*
|
|
331
|
+
* Exported so its tests can check the subtraction without a server.
|
|
332
|
+
*/
|
|
333
|
+
function grantableScopes(held) {
|
|
334
|
+
const management = KEY_MANAGEMENT_SCOPES;
|
|
335
|
+
return held.filter((entry) => !management.includes(parseScope(entry)?.scope ?? entry));
|
|
336
|
+
}
|
|
337
|
+
async function createKey(rawArgs) {
|
|
338
|
+
const plan = resolveCreateKeyArgs(rawArgs);
|
|
339
|
+
const { client, baseUrl } = connect();
|
|
340
|
+
let scopes;
|
|
341
|
+
if (plan.scopes === "held") {
|
|
342
|
+
let held;
|
|
343
|
+
try {
|
|
344
|
+
({held} = await client.personalKeys.listScopes());
|
|
345
|
+
} catch (e) {
|
|
346
|
+
fail("Failed to read the scopes you hold", e, baseUrl);
|
|
347
|
+
}
|
|
348
|
+
scopes = grantableScopes(held);
|
|
349
|
+
if (scopes.length === 0) {
|
|
350
|
+
console.error(chalk.red("✗ You hold no scope a key may carry, so --full-access has nothing to grant."));
|
|
351
|
+
process.exit(1);
|
|
352
|
+
}
|
|
353
|
+
} else scopes = plan.scopes;
|
|
354
|
+
const request = {
|
|
355
|
+
name: plan.name,
|
|
356
|
+
scopes,
|
|
357
|
+
...plan.roles.length > 0 ? { roles: plan.roles } : {},
|
|
358
|
+
...plan.rate_limit !== null ? { rate_limit: plan.rate_limit } : {},
|
|
359
|
+
...plan.expires_at !== null ? { expires_at: plan.expires_at } : {}
|
|
360
|
+
};
|
|
361
|
+
let created;
|
|
362
|
+
try {
|
|
363
|
+
({key: created} = await client.apiKeys.createKey(request));
|
|
364
|
+
} catch (e) {
|
|
365
|
+
fail("Failed to create API key", e, baseUrl);
|
|
366
|
+
}
|
|
367
|
+
console.log("");
|
|
368
|
+
console.log(chalk.bold.green(" ✓ API key created"));
|
|
369
|
+
console.log("");
|
|
370
|
+
for (const line of describeKey(created)) console.log(line);
|
|
371
|
+
console.log("");
|
|
372
|
+
console.log(chalk.bold.yellow(" ⚠ Copy your key now — it won't be shown again:"));
|
|
373
|
+
console.log("");
|
|
374
|
+
console.log(` ${chalk.cyan(created.key)}`);
|
|
375
|
+
console.log("");
|
|
376
|
+
}
|
|
377
|
+
/** The flags `rebase api-keys revoke` takes. */
|
|
378
|
+
var REVOKE_KEY_FLAGS = { "--id": String };
|
|
379
|
+
/**
|
|
380
|
+
* Which key this invocation names.
|
|
381
|
+
*
|
|
382
|
+
* The id is a positional, so the permissive parse handed one straight to the
|
|
383
|
+
* DELETE: `rebase api-keys revoke --foo` sent
|
|
384
|
+
* `DELETE /api/admin/api-keys/--foo`, and `rebase --debug api-keys revoke <id>`
|
|
385
|
+
* shifted the words along and revoked the key named `revoke`.
|
|
386
|
+
*
|
|
387
|
+
* Exported so its tests can drive the real parser rather than a copy of it.
|
|
388
|
+
*/
|
|
389
|
+
function resolveRevokeKeyArgs(rawArgs) {
|
|
390
|
+
const { flags, positionals } = parseCommandArgs({
|
|
391
|
+
spec: REVOKE_KEY_FLAGS,
|
|
392
|
+
rawArgs,
|
|
393
|
+
commandWords: 2,
|
|
394
|
+
command: "api-keys revoke",
|
|
395
|
+
maxPositionals: 1
|
|
396
|
+
});
|
|
397
|
+
return { id: flags["--id"] || positionals[0] };
|
|
398
|
+
}
|
|
399
|
+
async function revokeKey(rawArgs) {
|
|
400
|
+
const { id } = resolveRevokeKeyArgs(rawArgs);
|
|
401
|
+
if (!id) throw new UsageError("Key ID is required: rebase api-keys revoke <key-id> (`rebase api-keys list` shows them).");
|
|
402
|
+
const { client, baseUrl } = connect();
|
|
403
|
+
try {
|
|
404
|
+
await client.apiKeys.revokeKey(id);
|
|
405
|
+
} catch (e) {
|
|
406
|
+
fail("Failed to revoke API key", e, baseUrl);
|
|
407
|
+
}
|
|
408
|
+
console.log("");
|
|
409
|
+
console.log(chalk.bold.green(" ✓ API key revoked"));
|
|
410
|
+
console.log(` ${chalk.gray("ID:")} ${id}`);
|
|
411
|
+
console.log("");
|
|
412
|
+
}
|
|
413
|
+
/** The flags `rebase api-keys scopes` takes: none of its own. */
|
|
414
|
+
var SCOPES_LIST_FLAGS = {};
|
|
415
|
+
var PLANES = [
|
|
416
|
+
{
|
|
417
|
+
plane: "data",
|
|
418
|
+
title: "Data",
|
|
419
|
+
note: "narrow what a key reaches; row-level security still decides the rows"
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
plane: "admin",
|
|
423
|
+
title: "Admin",
|
|
424
|
+
note: "nobody holds these unless a role grants them; the admin role holds all"
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
plane: "app",
|
|
428
|
+
title: "App",
|
|
429
|
+
note: "declared by this app under auth.scopes on the users collection"
|
|
430
|
+
}
|
|
431
|
+
];
|
|
432
|
+
/**
|
|
433
|
+
* The scope catalogue as printed lines: one section per plane, each scope with
|
|
434
|
+
* its label and what its target names, marked when the caller holds it.
|
|
435
|
+
*
|
|
436
|
+
* Exported so its tests can read the lines without a server.
|
|
437
|
+
*/
|
|
438
|
+
function describeScopes(scopes, held) {
|
|
439
|
+
const management = KEY_MANAGEMENT_SCOPES;
|
|
440
|
+
const width = Math.max(0, ...scopes.map((s) => s.scope.length)) + 2;
|
|
441
|
+
const labelWidth = Math.max(0, ...scopes.map((s) => s.label.length)) + 2;
|
|
442
|
+
const lines = [];
|
|
443
|
+
for (const { plane, title, note } of PLANES) {
|
|
444
|
+
const inPlane = scopes.filter((s) => s.plane === plane);
|
|
445
|
+
if (inPlane.length === 0) continue;
|
|
446
|
+
lines.push(` ${chalk.bold(title)} ${chalk.gray(`— ${note}`)}`);
|
|
447
|
+
for (const s of inPlane) {
|
|
448
|
+
const mark = held.includes(s.scope) ? chalk.green("✓") : " ";
|
|
449
|
+
const extra = management.includes(s.scope) ? chalk.gray("never on a key") : s.target ? chalk.gray(`:<${s.target}>`) : "";
|
|
450
|
+
lines.push(` ${mark} ${s.scope.padEnd(width)}${extra ? s.label.padEnd(labelWidth) : s.label}${extra}`);
|
|
451
|
+
}
|
|
452
|
+
lines.push("");
|
|
453
|
+
}
|
|
454
|
+
return lines;
|
|
455
|
+
}
|
|
456
|
+
async function listScopes(rawArgs) {
|
|
457
|
+
parseCommandArgs({
|
|
458
|
+
spec: SCOPES_LIST_FLAGS,
|
|
459
|
+
rawArgs,
|
|
460
|
+
commandWords: 2,
|
|
461
|
+
command: "api-keys scopes",
|
|
462
|
+
maxPositionals: 0
|
|
463
|
+
});
|
|
464
|
+
const { client, baseUrl } = connect();
|
|
465
|
+
let catalogue;
|
|
466
|
+
try {
|
|
467
|
+
catalogue = await client.personalKeys.listScopes();
|
|
468
|
+
} catch (e) {
|
|
469
|
+
fail("Failed to read the scopes", e, baseUrl);
|
|
470
|
+
}
|
|
471
|
+
console.log("");
|
|
472
|
+
console.log(chalk.bold(" Scopes") + chalk.gray(" (✓ = you hold it; :<target> narrows it to one)"));
|
|
473
|
+
console.log("");
|
|
474
|
+
for (const line of describeScopes(catalogue.scopes, catalogue.held)) console.log(line);
|
|
475
|
+
}
|
|
476
|
+
function printApiKeysHelp() {
|
|
477
|
+
const keyable = ADMIN_SCOPES.filter((scope) => !KEY_MANAGEMENT_SCOPES.includes(scope));
|
|
478
|
+
console.log(`
|
|
479
|
+
${chalk.bold("rebase api-keys")} — Manage service API keys
|
|
480
|
+
|
|
481
|
+
${chalk.green.bold("Usage")}
|
|
482
|
+
rebase api-keys ${chalk.blue("<command>")} [options]
|
|
483
|
+
|
|
484
|
+
${chalk.green.bold("Commands")}
|
|
485
|
+
${chalk.blue.bold("list")} List the service keys: kind, scopes, roles
|
|
486
|
+
${chalk.blue.bold("get")} ${chalk.gray("<id>")} Show one key
|
|
487
|
+
${chalk.blue.bold("create")} Create a service key
|
|
488
|
+
${chalk.blue.bold("revoke")} ${chalk.gray("<id>")} Revoke a key
|
|
489
|
+
${chalk.blue.bold("scopes")} List the scopes this backend knows, and which you hold
|
|
490
|
+
|
|
491
|
+
${chalk.green.bold("create Options")}
|
|
492
|
+
${chalk.blue("--name, -n")} Key name ${chalk.gray("(required; or the first argument)")}
|
|
493
|
+
${chalk.blue("--scopes")} What the key may do, comma-separated or repeated
|
|
494
|
+
${chalk.gray("(required unless --full-access)")}
|
|
495
|
+
${chalk.blue("--full-access")} Every scope you hold, less keys:read and keys:write
|
|
496
|
+
${chalk.blue("--roles")} RLS roles the key runs as beside ${chalk.gray("service")}, e.g. admin
|
|
497
|
+
${chalk.blue("--rate-limit")} Requests per 15-min window ${chalk.gray("(default: the server's API-key limit)")}
|
|
498
|
+
${chalk.blue("--expires-in")} Days until the key expires
|
|
499
|
+
${chalk.blue("--expires-at")} Expiry as an ISO date, e.g. 2027-01-31
|
|
500
|
+
|
|
501
|
+
${chalk.green.bold("get / revoke Options")}
|
|
502
|
+
${chalk.blue("--id")} API key ID ${chalk.gray("(or the first argument)")}
|
|
503
|
+
|
|
504
|
+
${chalk.green.bold("Scopes")}
|
|
505
|
+
A scope is ${chalk.blue("resource:action")}, optionally narrowed to one target:
|
|
506
|
+
${chalk.blue("data:read:posts")} reads the posts collection and nothing else.
|
|
507
|
+
Data ${DATA_PLANE_SCOPES.join(", ")}
|
|
508
|
+
${chalk.gray("target: a collection slug, a storage source id (default), a function name")}
|
|
509
|
+
Admin ${keyable.join(", ")}
|
|
510
|
+
App whatever the app declares under auth.scopes ${chalk.gray("(rebase api-keys scopes)")}
|
|
511
|
+
|
|
512
|
+
${chalk.green.bold("Examples")}
|
|
513
|
+
rebase api-keys list
|
|
514
|
+
rebase api-keys create "Analytics" --scopes data:read:events
|
|
515
|
+
rebase api-keys create -n "Blog CI" --scopes data:read:posts,data:write:posts --expires-in 90
|
|
516
|
+
rebase api-keys create -n "Mailer" --scopes "functions:invoke:send-email,storage:read:(default)"
|
|
517
|
+
rebase api-keys create -n "Ops" --full-access --roles admin --expires-at 2027-01-31
|
|
518
|
+
rebase api-keys get abc123-def456
|
|
519
|
+
rebase api-keys revoke abc123-def456
|
|
520
|
+
`);
|
|
521
|
+
}
|
|
522
|
+
//#endregion
|
|
523
|
+
export { API_KEYS_SUBCOMMANDS, apiKeysCommand, describeFailure, resolveBaseUrl };
|
|
524
|
+
|
|
525
|
+
//# sourceMappingURL=api-keys-Bl1I9q7M.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-keys-Bl1I9q7M.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/** The running backend's URL. Shared with `rebase storage`. */\nexport function resolveBaseUrl(env: Record<string, string>, projectRoot?: string): string {\n // An explicit override always wins.\n if (env.REBASE_BASE_URL) return env.REBASE_BASE_URL;\n\n // `rebase dev` runs on a derived per-project port, not the .env PORT, and\n // records the URL it actually bound. Without this, these commands default to\n // :3001 and report \"Is the Rebase server running?\" while it is running.\n if (projectRoot) {\n try {\n const urlFile = path.join(projectRoot, \".rebase-dev-url\");\n if (fs.existsSync(urlFile)) {\n const devUrl = fs.readFileSync(urlFile, \"utf-8\").trim();\n if (devUrl) return devUrl;\n }\n } catch { /* fall through to the configured port */ }\n }\n\n const port = env.PORT || env.REBASE_PORT || \"3001\";\n return `http://localhost:${port}`;\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}\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 baseUrl = resolveBaseUrl(env, projectRoot);\n const serviceKey = env.SERVICE_KEY || env.REBASE_SERVICE_KEY;\n\n if (!serviceKey) {\n console.error(chalk.red(\"✗ SERVICE_KEY not found in .env — required to manage API keys.\"));\n process.exit(1);\n }\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 };\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(action: string, error: unknown, baseUrl: string): 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 SERVICE_KEY 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, baseUrl: string): never {\n for (const line of describeFailure(action, error, baseUrl)) {\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 { client, baseUrl } = connect();\n let keys: ApiKeyMasked[];\n try {\n ({ keys } = await client.apiKeys.listKeys());\n } catch (e: unknown) {\n fail(\"Failed to list API keys\", e, baseUrl);\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 { client, baseUrl } = connect();\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, baseUrl);\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 { client, baseUrl } = connect();\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, baseUrl);\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, baseUrl);\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 { client, baseUrl } = connect();\n try {\n await client.apiKeys.revokeKey(id);\n } catch (e: unknown) {\n fail(\"Failed to revoke API key\", e, baseUrl);\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 { client, baseUrl } = connect();\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, baseUrl);\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":"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"}
|