@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
package/README.md
CHANGED
|
@@ -38,10 +38,11 @@ The CLI is also bundled with every Rebase project as a local dependency.
|
|
|
38
38
|
| `rebase auth reset-password` | Reset a user's password |
|
|
39
39
|
| `rebase api-keys list \| get \| create \| revoke \| scopes` | Manage service API keys and the scopes they hold |
|
|
40
40
|
| `rebase doctor` | Detect schema drift between collections, Drizzle schema, and database |
|
|
41
|
+
| `rebase verify` | Check collections, RLS coverage and migration freshness in one fast pass (`--json` for agents, `--live` for the database) |
|
|
41
42
|
| `rebase status` | Show every resource this project declares and whether its variables are set |
|
|
42
43
|
| `rebase resources` | List the databases, buckets and topics this project declares |
|
|
43
44
|
| `rebase eject` | Own the server process and the image (one-way) |
|
|
44
|
-
| `rebase skills install` | Install Rebase agent skills for your AI coding assistant |
|
|
45
|
+
| `rebase skills install` | Install Rebase agent skills for your AI coding assistant (`--hooks`: the Claude Code `rebase verify` hook) |
|
|
45
46
|
| `rebase telemetry` | Anonymous usage sharing (opt-in, off by default) |
|
|
46
47
|
| `rebase cloud <command>` | Manage your apps on Rebase Cloud (auth, deploy, databases, …) |
|
|
47
48
|
|
package/bin/rebase.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
4
|
+
import { dirname, isAbsolute, join, relative, sep } from "node:path";
|
|
4
5
|
import { fileURLToPath } from "node:url";
|
|
5
6
|
|
|
6
7
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
@@ -29,6 +30,100 @@ const red = (text) => paint(31, text);
|
|
|
29
30
|
const yellow = (text) => paint(33, text);
|
|
30
31
|
const dim = (text) => paint(90, text);
|
|
31
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Run the project's own CLI when this one is a checkout and the project has one.
|
|
35
|
+
*
|
|
36
|
+
* The global `rebase` on a CLI developer's machine is a link to a checkout of
|
|
37
|
+
* this repo, so a bare `rebase cloud deploy` typed in a customer project ran
|
|
38
|
+
* the checkout's `dist/` — 280h older than its source, on the machine it was
|
|
39
|
+
* found on — and not the `@rebasepro/cli` 0.25.1 that project had pinned. Only
|
|
40
|
+
* `pnpm rebase` used the pinned one. Nothing said so, and a deploy is the
|
|
41
|
+
* command where the version matters most.
|
|
42
|
+
*
|
|
43
|
+
* So, as jest, webpack-cli and gulp do, a checkout hands off to the CLI the project
|
|
44
|
+
* installed: same argv, same stdio, its exit code. A note naming both versions
|
|
45
|
+
* was the alternative; it leaves the wrong CLI running and trains people to
|
|
46
|
+
* skim past a line on stderr. The project's pin is the answer to "which CLI",
|
|
47
|
+
* and a source checkout is never the release anyone pinned.
|
|
48
|
+
*
|
|
49
|
+
* Only a checkout hands off. An install runs itself, which is also what keeps
|
|
50
|
+
* this from looping: the CLI handed to is always an install. A project whose
|
|
51
|
+
* `@rebasepro/cli` resolves to a checkout (a workspace or `link:` dependency,
|
|
52
|
+
* as in this repo's own apps and end-to-end suites) is already running the
|
|
53
|
+
* build it asked for. `REBASE_CLI_DEV=1` runs the checkout anyway, for testing
|
|
54
|
+
* a dev build against a project elsewhere.
|
|
55
|
+
*/
|
|
56
|
+
function isInstalled(dir) {
|
|
57
|
+
return dir.split(sep).includes("node_modules");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function projectCli() {
|
|
61
|
+
const packageDir = realpathSync(join(here, ".."));
|
|
62
|
+
if (isInstalled(packageDir) || parseFlag(process.env.REBASE_CLI_DEV)) return null;
|
|
63
|
+
|
|
64
|
+
// Node's own lookup: the nearest `node_modules` up from where it was run.
|
|
65
|
+
let dir = process.cwd();
|
|
66
|
+
while (!existsSync(join(dir, "node_modules", "@rebasepro", "cli", "package.json"))) {
|
|
67
|
+
const parent = dirname(dir);
|
|
68
|
+
if (parent === dir) return null;
|
|
69
|
+
dir = parent;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const installed = realpathSync(join(dir, "node_modules", "@rebasepro", "cli"));
|
|
73
|
+
if (installed === packageDir || !isInstalled(installed)) return null;
|
|
74
|
+
|
|
75
|
+
const manifest = JSON.parse(readFileSync(join(installed, "package.json"), "utf8"));
|
|
76
|
+
const binPath = typeof manifest.bin === "string" ? manifest.bin : manifest.bin?.rebase;
|
|
77
|
+
if (typeof binPath !== "string" || !existsSync(join(installed, binPath))) return null;
|
|
78
|
+
return { bin: join(installed, binPath),
|
|
79
|
+
version: manifest.version,
|
|
80
|
+
packageDir };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function handOff({ bin, version, packageDir }) {
|
|
84
|
+
if (process.argv.includes("--debug") || parseFlag(process.env.REBASE_DEBUG)) {
|
|
85
|
+
process.stderr.write(dim(
|
|
86
|
+
`rebase CLI: running this project's @rebasepro/cli ${version} (${bin}) ` +
|
|
87
|
+
`instead of ${packageDir}. REBASE_CLI_DEV=1 runs that one.`
|
|
88
|
+
) + "\n");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const child = spawn(process.execPath, [...process.execArgv, bin, ...process.argv.slice(2)], { stdio: "inherit" });
|
|
92
|
+
|
|
93
|
+
// Ctrl+C and a closed terminal reach the child through the process group,
|
|
94
|
+
// so this process only has to outlive it. A `kill` names this pid alone.
|
|
95
|
+
const stayUp = () => { /* the child decides */ };
|
|
96
|
+
const forward = (signal) => child.kill(signal);
|
|
97
|
+
process.on("SIGINT", stayUp);
|
|
98
|
+
process.on("SIGHUP", stayUp);
|
|
99
|
+
process.on("SIGTERM", forward);
|
|
100
|
+
|
|
101
|
+
return new Promise(() => {
|
|
102
|
+
child.on("error", (error) => {
|
|
103
|
+
process.stderr.write(`${red(`✗ rebase CLI: could not start ${bin}: ${error.message}`)}\n`);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
});
|
|
106
|
+
child.on("exit", (code, signal) => {
|
|
107
|
+
if (signal) {
|
|
108
|
+
process.off("SIGINT", stayUp);
|
|
109
|
+
process.off("SIGHUP", stayUp);
|
|
110
|
+
process.off("SIGTERM", forward);
|
|
111
|
+
process.kill(process.pid, signal);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
process.exit(code ?? 1);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
let local = null;
|
|
120
|
+
try {
|
|
121
|
+
local = projectCli();
|
|
122
|
+
} catch {
|
|
123
|
+
// Looking for another CLI must never stop this one from running.
|
|
124
|
+
}
|
|
125
|
+
if (local) await handOff(local);
|
|
126
|
+
|
|
32
127
|
/**
|
|
33
128
|
* Refuse to run on a Node older than this CLI's own floor, before anything else.
|
|
34
129
|
*
|
|
@@ -95,8 +190,8 @@ assertNodeFloor();
|
|
|
95
190
|
* the command works, it just silently lacks the subcommand you added, which
|
|
96
191
|
* reads as "my change did nothing" rather than "you forgot to build".
|
|
97
192
|
*
|
|
98
|
-
* Development-only
|
|
99
|
-
*
|
|
193
|
+
* Development-only, and only for the person developing it — see
|
|
194
|
+
* `isCliDevelopment`.
|
|
100
195
|
*
|
|
101
196
|
* Writes to **stderr**. Agents parse stdout as JSON under `--json`, and a
|
|
102
197
|
* warning there would corrupt the one guarantee those commands make.
|
|
@@ -118,8 +213,52 @@ function newestMtimeMs(dir, deadline) {
|
|
|
118
213
|
return newest;
|
|
119
214
|
}
|
|
120
215
|
|
|
216
|
+
/**
|
|
217
|
+
* Whether the person running this is working on the CLI, which is the only
|
|
218
|
+
* person a stale build means anything to.
|
|
219
|
+
*
|
|
220
|
+
* `src/` alone did not say so. A published install has none — `files` leaves it
|
|
221
|
+
* out — but the global `rebase` on a CLI developer's machine is usually linked
|
|
222
|
+
* to the checkout, so a bare `rebase cloud deploy` typed in a customer project
|
|
223
|
+
* resolved to the monorepo's `bin/` and printed "dist/ is 159h older than src/"
|
|
224
|
+
* on every run. That project had pinned its own `@rebasepro/cli` 0.25.1, did not
|
|
225
|
+
* contain the source being compared, and could do nothing about it.
|
|
226
|
+
*
|
|
227
|
+
* So the warning needs all of:
|
|
228
|
+
* - the package is not under `node_modules` — anything there is an install,
|
|
229
|
+
* whatever files it happens to carry;
|
|
230
|
+
* - it sits in a pnpm workspace, which is what a checkout of this repo is;
|
|
231
|
+
* - the command was run from inside that workspace (a worktree under it
|
|
232
|
+
* counts), or `REBASE_CLI_DEV` is set, for testing a dev build against a
|
|
233
|
+
* project elsewhere.
|
|
234
|
+
*/
|
|
235
|
+
function isCliDevelopment() {
|
|
236
|
+
const packageDir = realpathSync(join(here, ".."));
|
|
237
|
+
if (packageDir.split(sep).includes("node_modules")) return false;
|
|
238
|
+
|
|
239
|
+
let root = packageDir;
|
|
240
|
+
while (!existsSync(join(root, "pnpm-workspace.yaml"))) {
|
|
241
|
+
const parent = dirname(root);
|
|
242
|
+
if (parent === root) return false;
|
|
243
|
+
root = parent;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (parseFlag(process.env.REBASE_CLI_DEV)) return true;
|
|
247
|
+
const fromRoot = relative(root, realpathSync(process.cwd()));
|
|
248
|
+
return !fromRoot.startsWith("..") && !isAbsolute(fromRoot);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* `parseEnvBoolean` from `@rebasepro/types`, which cannot be imported yet: this
|
|
253
|
+
* runs before the bundle, and in a fresh clone that package is not built either.
|
|
254
|
+
*/
|
|
255
|
+
function parseFlag(value) {
|
|
256
|
+
return value !== undefined && /^(1|true|yes|on)$/i.test(value.trim());
|
|
257
|
+
}
|
|
258
|
+
|
|
121
259
|
function warnIfStale() {
|
|
122
260
|
if (!existsSync(srcDir) || !existsSync(distEntry)) return;
|
|
261
|
+
if (!isCliDevelopment()) return;
|
|
123
262
|
const builtAt = statSync(distEntry).mtimeMs;
|
|
124
263
|
const editedAt = newestMtimeMs(srcDir, Date.now() + 150);
|
|
125
264
|
// A couple of seconds of slack: a build writes dist while src is being
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { t as detectPackageManager } from "./package-manager-DSEOwTqM.js";
|
|
2
2
|
import { n as cliVersion } from "./version-Buyyo1ut.js";
|
|
3
|
-
import { AGENTS, AGENT_KEYS, detectInstalledAgents, installSkills, loadBundledSkills } from "./skills-
|
|
3
|
+
import { AGENTS, AGENT_KEYS, detectInstalledAgents, installSkills, loadBundledSkills } from "./skills-BU2flMOy.js";
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
import fs from "fs";
|
|
6
6
|
import path from "path";
|
|
7
|
+
import { createHash } from "crypto";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
7
9
|
import inquirer from "inquirer";
|
|
8
10
|
//#region src/commands/agent-setup.ts
|
|
9
11
|
/**
|
|
@@ -318,7 +320,127 @@ async function promptForAgents(home) {
|
|
|
318
320
|
}]);
|
|
319
321
|
return AGENT_KEYS.filter((key) => selectedAgents.includes(key));
|
|
320
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* The guardrail every scaffold ships for Claude Code: a PostToolUse hook that
|
|
325
|
+
* runs `rebase verify --quiet` after each edit under `config/collections/` and
|
|
326
|
+
* hands any problem back to the agent (exit 2, the message on stderr).
|
|
327
|
+
*
|
|
328
|
+
* The files live in the template, under `.claude/`, so `rebase init` copies
|
|
329
|
+
* them like everything else and `rebase skills --hooks` installs the very same
|
|
330
|
+
* bytes into a project that predates them. Paths are project-relative.
|
|
331
|
+
*/
|
|
332
|
+
var AGENT_HOOK_SCRIPT = ".claude/hooks/post-edit-collection.sh";
|
|
333
|
+
var AGENT_HOOK_SETTINGS = ".claude/settings.json";
|
|
334
|
+
/** The CLI package's own directory, from wherever this module was bundled to. */
|
|
335
|
+
function cliPackageRoot() {
|
|
336
|
+
let dir = path.dirname(fileURLToPath(import.meta.url));
|
|
337
|
+
while (dir !== path.dirname(dir)) {
|
|
338
|
+
const manifest = path.join(dir, "package.json");
|
|
339
|
+
if (fs.existsSync(manifest)) try {
|
|
340
|
+
const parsed = JSON.parse(fs.readFileSync(manifest, "utf-8"));
|
|
341
|
+
if (isRecord(parsed) && parsed.name === "@rebasepro/cli") return dir;
|
|
342
|
+
} catch {}
|
|
343
|
+
dir = path.dirname(dir);
|
|
344
|
+
}
|
|
345
|
+
throw new Error("Could not find the @rebasepro/cli package directory to read the hook templates from.");
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Register the shipped PostToolUse hook in an existing settings file, keeping
|
|
349
|
+
* every other setting and hook in it. A file that is not plain JSON is left
|
|
350
|
+
* alone, for the reason {@link addJsonServer} gives.
|
|
351
|
+
*/
|
|
352
|
+
function mergeHookSettings(filePath, shippedText) {
|
|
353
|
+
if (!fs.existsSync(filePath)) {
|
|
354
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
355
|
+
fs.writeFileSync(filePath, shippedText, "utf-8");
|
|
356
|
+
return "added";
|
|
357
|
+
}
|
|
358
|
+
const text = fs.readFileSync(filePath, "utf-8");
|
|
359
|
+
let doc;
|
|
360
|
+
try {
|
|
361
|
+
doc = JSON.parse(text);
|
|
362
|
+
} catch {
|
|
363
|
+
return "unreadable";
|
|
364
|
+
}
|
|
365
|
+
const shipped = JSON.parse(shippedText);
|
|
366
|
+
const shippedEntries = isRecord(shipped) && isRecord(shipped.hooks) && Array.isArray(shipped.hooks.PostToolUse) ? shipped.hooks.PostToolUse : [];
|
|
367
|
+
if (!isRecord(doc)) return "unreadable";
|
|
368
|
+
const hooks = doc.hooks ?? {};
|
|
369
|
+
if (!isRecord(hooks)) return "unreadable";
|
|
370
|
+
const post = hooks.PostToolUse ?? [];
|
|
371
|
+
if (!Array.isArray(post)) return "unreadable";
|
|
372
|
+
if (JSON.stringify(post).includes(path.basename(".claude/hooks/post-edit-collection.sh"))) return "present";
|
|
373
|
+
doc.hooks = {
|
|
374
|
+
...hooks,
|
|
375
|
+
PostToolUse: [...post, ...shippedEntries]
|
|
376
|
+
};
|
|
377
|
+
const indent = /\n([ \t]+)"/.exec(text)?.[1] ?? " ";
|
|
378
|
+
fs.writeFileSync(filePath, `${JSON.stringify(doc, null, indent)}\n`, "utf-8");
|
|
379
|
+
return "added";
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* The SHA-256 of every version of `post-edit-collection.sh` this CLI has ever
|
|
383
|
+
* written, oldest first, the current one last — hashed with CRLF read as LF,
|
|
384
|
+
* so a Windows checkout of an untouched copy still matches.
|
|
385
|
+
*
|
|
386
|
+
* A copy that differs from today's script was read as "edited" and kept, so a
|
|
387
|
+
* fix to the hook never reached a project scaffolded before it: `rebase skills
|
|
388
|
+
* install --hooks` answered "was edited, so it was kept — pass --force" about a
|
|
389
|
+
* file nobody had touched. A copy whose hash is one of ours is our old
|
|
390
|
+
* version, and is replaced like any stale file; only a copy we never shipped is
|
|
391
|
+
* the developer's.
|
|
392
|
+
*
|
|
393
|
+
* Append, never replace: `agent-hooks.test.ts` fails when the shipped script's
|
|
394
|
+
* hash is not here, so changing the hook means adding its new hash — and the
|
|
395
|
+
* old one stays, which is the whole point.
|
|
396
|
+
*/
|
|
397
|
+
var SHIPPED_HOOK_SCRIPT_SHA256S = [
|
|
398
|
+
"335c56f268876293c93696cb557c0ff3d8e16b60f81f67ebccbab75b0df457ac",
|
|
399
|
+
"d624f805e1eb635d87cf769ad5e6f877d07d6aa8d547052d2d7ce9d6d6b3a27b",
|
|
400
|
+
"25c307e0c9a8838703c6e59ab5ea62ff249966823f071d26dd0dd8c2508f83a4",
|
|
401
|
+
"7a0eeaa301f3cb6b90d5675199ccfcb8fe1c2a80a327e3f7b53bc82c3f4541c2",
|
|
402
|
+
"dbdbfcec9c4c4e1d2643900a615585594869f6ba9fc19b5652013526367363f6"
|
|
403
|
+
];
|
|
404
|
+
/** The hash {@link SHIPPED_HOOK_SCRIPT_SHA256S} records for a script's text. */
|
|
405
|
+
function hookScriptSha256(text) {
|
|
406
|
+
return createHash("sha256").update(text.replace(/\r\n/g, "\n")).digest("hex");
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Install the Claude Code guardrail into a project: the hook script, and its
|
|
410
|
+
* registration in `.claude/settings.json`.
|
|
411
|
+
*
|
|
412
|
+
* A script the developer edited is kept unless `force`; a version this CLI
|
|
413
|
+
* shipped earlier is replaced. The settings are always merged, never
|
|
414
|
+
* overwritten.
|
|
415
|
+
*/
|
|
416
|
+
function installAgentHooks(projectDir, { force = false, templateDir = path.join(cliPackageRoot(), "templates", "template") } = {}) {
|
|
417
|
+
const shippedScript = fs.readFileSync(path.join(templateDir, AGENT_HOOK_SCRIPT), "utf-8");
|
|
418
|
+
const scriptPath = path.join(projectDir, AGENT_HOOK_SCRIPT);
|
|
419
|
+
let scriptStatus;
|
|
420
|
+
if (!fs.existsSync(scriptPath)) scriptStatus = "added";
|
|
421
|
+
else {
|
|
422
|
+
const current = fs.readFileSync(scriptPath, "utf-8");
|
|
423
|
+
if (current === shippedScript) scriptStatus = "present";
|
|
424
|
+
else {
|
|
425
|
+
const ours = SHIPPED_HOOK_SCRIPT_SHA256S.includes(hookScriptSha256(current));
|
|
426
|
+
scriptStatus = force || ours ? "replaced" : "kept";
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
if (scriptStatus === "added" || scriptStatus === "replaced") {
|
|
430
|
+
fs.mkdirSync(path.dirname(scriptPath), { recursive: true });
|
|
431
|
+
fs.writeFileSync(scriptPath, shippedScript, "utf-8");
|
|
432
|
+
}
|
|
433
|
+
if (scriptStatus !== "kept") fs.chmodSync(scriptPath, 493);
|
|
434
|
+
const settingsStatus = mergeHookSettings(path.join(projectDir, AGENT_HOOK_SETTINGS), fs.readFileSync(path.join(templateDir, AGENT_HOOK_SETTINGS), "utf-8"));
|
|
435
|
+
return [{
|
|
436
|
+
file: AGENT_HOOK_SCRIPT,
|
|
437
|
+
status: scriptStatus
|
|
438
|
+
}, {
|
|
439
|
+
file: AGENT_HOOK_SETTINGS,
|
|
440
|
+
status: settingsStatus
|
|
441
|
+
}];
|
|
442
|
+
}
|
|
321
443
|
//#endregion
|
|
322
|
-
export { MCP_SERVER_NAME, configureAgents, ensureMcpDependency, mcpDependencyNote, mcpServerRegistered, printAgentSetup, promptForAgents, retargetScaffoldMcpConfig, writeMcpConfig };
|
|
444
|
+
export { AGENT_HOOK_SCRIPT, MCP_SERVER_NAME, configureAgents, ensureMcpDependency, installAgentHooks, mcpDependencyNote, mcpServerRegistered, printAgentSetup, promptForAgents, retargetScaffoldMcpConfig, writeMcpConfig };
|
|
323
445
|
|
|
324
|
-
//# sourceMappingURL=agent-setup-
|
|
446
|
+
//# sourceMappingURL=agent-setup-D9mHRZ5R.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-setup-D9mHRZ5R.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 { createHash } from \"crypto\";\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 { fileURLToPath } from \"url\";\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\n// ─── Claude Code guardrail hooks ──────────────────────────────────────────────\n\n/**\n * The guardrail every scaffold ships for Claude Code: a PostToolUse hook that\n * runs `rebase verify --quiet` after each edit under `config/collections/` and\n * hands any problem back to the agent (exit 2, the message on stderr).\n *\n * The files live in the template, under `.claude/`, so `rebase init` copies\n * them like everything else and `rebase skills --hooks` installs the very same\n * bytes into a project that predates them. Paths are project-relative.\n */\nexport const AGENT_HOOK_SCRIPT = \".claude/hooks/post-edit-collection.sh\";\nexport const AGENT_HOOK_SETTINGS = \".claude/settings.json\";\n\n/** What installing one hook file did. */\nexport type HookWriteStatus =\n /** Written, or merged into the settings beside the hooks already there. */\n | \"added\"\n /** Already there, unchanged — or, for the settings, already registered. */\n | \"present\"\n /** There, and edited: left as it was. `--force` replaces it. */\n | \"kept\"\n /** There, edited, and replaced because `--force` asked. */\n | \"replaced\"\n /** The settings file is not plain JSON with a `hooks` object: left alone. */\n | \"unreadable\";\n\nexport interface HookWriteResult {\n file: string;\n status: HookWriteStatus;\n}\n\n/** The CLI package's own directory, from wherever this module was bundled to. */\nfunction cliPackageRoot(): string {\n let dir = path.dirname(fileURLToPath(import.meta.url));\n while (dir !== path.dirname(dir)) {\n const manifest = path.join(dir, \"package.json\");\n if (fs.existsSync(manifest)) {\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(manifest, \"utf-8\"));\n if (isRecord(parsed) && parsed.name === \"@rebasepro/cli\") return dir;\n } catch {\n // Not ours; keep walking.\n }\n }\n dir = path.dirname(dir);\n }\n throw new Error(\"Could not find the @rebasepro/cli package directory to read the hook templates from.\");\n}\n\n/**\n * Register the shipped PostToolUse hook in an existing settings file, keeping\n * every other setting and hook in it. A file that is not plain JSON is left\n * alone, for the reason {@link addJsonServer} gives.\n */\nfunction mergeHookSettings(filePath: string, shippedText: string): HookWriteStatus {\n if (!fs.existsSync(filePath)) {\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, shippedText, \"utf-8\");\n return \"added\";\n }\n const text = fs.readFileSync(filePath, \"utf-8\");\n let doc: unknown;\n try {\n doc = JSON.parse(text);\n } catch {\n return \"unreadable\";\n }\n const shipped: unknown = JSON.parse(shippedText);\n const shippedEntries = isRecord(shipped) && isRecord(shipped.hooks) && Array.isArray(shipped.hooks.PostToolUse)\n ? shipped.hooks.PostToolUse\n : [];\n if (!isRecord(doc)) return \"unreadable\";\n const hooks = doc.hooks ?? {};\n if (!isRecord(hooks)) return \"unreadable\";\n const post = hooks.PostToolUse ?? [];\n if (!Array.isArray(post)) return \"unreadable\";\n if (JSON.stringify(post).includes(path.basename(AGENT_HOOK_SCRIPT))) return \"present\";\n\n doc.hooks = { ...hooks, PostToolUse: [...post, ...shippedEntries] };\n const indent = /\\n([ \\t]+)\"/.exec(text)?.[1] ?? \" \";\n fs.writeFileSync(filePath, `${JSON.stringify(doc, null, indent)}\\n`, \"utf-8\");\n return \"added\";\n}\n\n/**\n * The SHA-256 of every version of `post-edit-collection.sh` this CLI has ever\n * written, oldest first, the current one last — hashed with CRLF read as LF,\n * so a Windows checkout of an untouched copy still matches.\n *\n * A copy that differs from today's script was read as \"edited\" and kept, so a\n * fix to the hook never reached a project scaffolded before it: `rebase skills\n * install --hooks` answered \"was edited, so it was kept — pass --force\" about a\n * file nobody had touched. A copy whose hash is one of ours is our old\n * version, and is replaced like any stale file; only a copy we never shipped is\n * the developer's.\n *\n * Append, never replace: `agent-hooks.test.ts` fails when the shipped script's\n * hash is not here, so changing the hook means adding its new hash — and the\n * old one stays, which is the whole point.\n */\nexport const SHIPPED_HOOK_SCRIPT_SHA256S: readonly string[] = [\n \"335c56f268876293c93696cb557c0ff3d8e16b60f81f67ebccbab75b0df457ac\", // 4451138e43 — first version\n \"d624f805e1eb635d87cf769ad5e6f877d07d6aa8d547052d2d7ce9d6d6b3a27b\", // ac7c5059d2 — Windows paths\n \"25c307e0c9a8838703c6e59ab5ea62ff249966823f071d26dd0dd8c2508f83a4\", // c694488bfe — CLI predating verify\n \"7a0eeaa301f3cb6b90d5675199ccfcb8fe1c2a80a327e3f7b53bc82c3f4541c2\", // af6eac33cb — rebase.json config dir\n \"dbdbfcec9c4c4e1d2643900a615585594869f6ba9fc19b5652013526367363f6\" // 08a7cd9ec8 — skip when not installed\n];\n\n/** The hash {@link SHIPPED_HOOK_SCRIPT_SHA256S} records for a script's text. */\nexport function hookScriptSha256(text: string): string {\n return createHash(\"sha256\").update(text.replace(/\\r\\n/g, \"\\n\")).digest(\"hex\");\n}\n\n/**\n * Install the Claude Code guardrail into a project: the hook script, and its\n * registration in `.claude/settings.json`.\n *\n * A script the developer edited is kept unless `force`; a version this CLI\n * shipped earlier is replaced. The settings are always merged, never\n * overwritten.\n */\nexport function installAgentHooks(\n projectDir: string,\n { force = false, templateDir = path.join(cliPackageRoot(), \"templates\", \"template\") }: { force?: boolean; templateDir?: string } = {}\n): HookWriteResult[] {\n const shippedScript = fs.readFileSync(path.join(templateDir, AGENT_HOOK_SCRIPT), \"utf-8\");\n const scriptPath = path.join(projectDir, AGENT_HOOK_SCRIPT);\n let scriptStatus: HookWriteStatus;\n if (!fs.existsSync(scriptPath)) {\n scriptStatus = \"added\";\n } else {\n const current = fs.readFileSync(scriptPath, \"utf-8\");\n if (current === shippedScript) {\n scriptStatus = \"present\";\n } else {\n const ours = SHIPPED_HOOK_SCRIPT_SHA256S.includes(hookScriptSha256(current));\n scriptStatus = force || ours ? \"replaced\" : \"kept\";\n }\n }\n if (scriptStatus === \"added\" || scriptStatus === \"replaced\") {\n fs.mkdirSync(path.dirname(scriptPath), { recursive: true });\n fs.writeFileSync(scriptPath, shippedScript, \"utf-8\");\n }\n if (scriptStatus !== \"kept\") fs.chmodSync(scriptPath, 0o755);\n\n const settingsStatus = mergeHookSettings(\n path.join(projectDir, AGENT_HOOK_SETTINGS),\n fs.readFileSync(path.join(templateDir, AGENT_HOOK_SETTINGS), \"utf-8\")\n );\n return [\n { file: AGENT_HOOK_SCRIPT, status: scriptStatus },\n { file: AGENT_HOOK_SETTINGS, status: settingsStatus }\n ];\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { d as readEnvFile, p as requireProjectRoot } from "./project-
|
|
2
|
-
import { a as unknownCommand, n as parseCommandArgs, r as wantsHelp, t as UsageError } from "./args-BX0YD14a.js";
|
|
1
|
+
import { E as unknownCommand, b as UsageError, d as readEnvFile, p as requireProjectRoot, w as wantsHelp, x as parseCommandArgs } from "./project-CasK56vN.js";
|
|
3
2
|
import { t as cliUserAgent } from "./version-Buyyo1ut.js";
|
|
4
|
-
import {
|
|
3
|
+
import { a as serverUnreachableHint } from "./dev-server-OJsUGZEB.js";
|
|
5
4
|
import chalk from "chalk";
|
|
6
5
|
import fs from "fs";
|
|
7
6
|
import path from "path";
|
|
@@ -63,11 +62,19 @@ function isAlive(pid) {
|
|
|
63
62
|
* other server may have taken since.
|
|
64
63
|
*/
|
|
65
64
|
function resolveBackendAddress(env, projectRoot) {
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
65
|
+
const fromShell = process.env.REBASE_BASE_URL?.trim();
|
|
66
|
+
const configured = fromShell || env.REBASE_BASE_URL?.trim();
|
|
67
|
+
if (configured) {
|
|
68
|
+
const cloudProject = fromShell ? void 0 : linkedCloudProjectAt(projectRoot, configured);
|
|
69
|
+
return cloudProject ? {
|
|
70
|
+
url: configured,
|
|
71
|
+
source: "configured",
|
|
72
|
+
cloudProject
|
|
73
|
+
} : {
|
|
74
|
+
url: configured,
|
|
75
|
+
source: "configured"
|
|
76
|
+
};
|
|
77
|
+
}
|
|
71
78
|
const state = readBackendState(projectRoot);
|
|
72
79
|
if (state && isAlive(state.pid)) return {
|
|
73
80
|
url: state.url,
|
|
@@ -78,6 +85,28 @@ function resolveBackendAddress(env, projectRoot) {
|
|
|
78
85
|
reason: `This project's server is not running, so the service key was not sent anywhere.${state ? ` The last one recorded here, at ${state.url} (pid ${state.pid}), has stopped.` : ""} Start it with \`rebase dev\`. If it runs some other way — \`rebase start\`, a container — set REBASE_BASE_URL in .env to its address.`
|
|
79
86
|
};
|
|
80
87
|
}
|
|
88
|
+
/** The slug of the cloud project `.rebase/cloud.json` links, when `url` is its address. */
|
|
89
|
+
function linkedCloudProjectAt(projectRoot, url) {
|
|
90
|
+
let link;
|
|
91
|
+
try {
|
|
92
|
+
link = JSON.parse(fs.readFileSync(path.join(projectRoot, ".rebase", "cloud.json"), "utf-8"));
|
|
93
|
+
} catch {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (!isRecord(link) || link.mode === "direct" || typeof link.apiUrl !== "string") return void 0;
|
|
97
|
+
const bare = (u) => u.trim().replace(/\/+$/, "");
|
|
98
|
+
if (bare(link.apiUrl) !== bare(url)) return void 0;
|
|
99
|
+
const slug = typeof link.slug === "string" && link.slug ? link.slug : link.projectId;
|
|
100
|
+
return typeof slug === "string" && slug ? slug : "linked";
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* What a local command says before it sends the service key to the cloud
|
|
104
|
+
* project `.env` names (see `BackendAddress.cloudProject`), or undefined.
|
|
105
|
+
*/
|
|
106
|
+
function cloudTargetNote(address) {
|
|
107
|
+
if (address.url === null || !address.cloudProject) return void 0;
|
|
108
|
+
return `Sending this to ${address.url}: REBASE_BASE_URL in .env is the address of cloud project ${address.cloudProject} (\`rebase cloud init --claim\` writes it there for the app, the SDK and the MCP server), and that project does not take this project's local service key. To reach the local backend instead, run the command with REBASE_BASE_URL set to it in the shell, e.g. REBASE_BASE_URL=http://localhost:3001.`;
|
|
109
|
+
}
|
|
81
110
|
/** What `.rebase/state.json` says about the backend that wrote it, or null. */
|
|
82
111
|
function readBackendState(projectRoot) {
|
|
83
112
|
let parsed;
|
|
@@ -100,6 +129,8 @@ function readBackendState(projectRoot) {
|
|
|
100
129
|
*/
|
|
101
130
|
function requireBackendUrl(env, projectRoot) {
|
|
102
131
|
const address = resolveBackendAddress(env, projectRoot);
|
|
132
|
+
const note = cloudTargetNote(address);
|
|
133
|
+
if (note) console.error(chalk.yellow(` ⚠ ${note}`));
|
|
103
134
|
if (address.url !== null) return address.url;
|
|
104
135
|
console.error(chalk.red("✗ No server to send the request to."));
|
|
105
136
|
console.error(chalk.gray(` ${address.reason}`));
|
|
@@ -583,6 +614,6 @@ ${chalk.green.bold("Examples")}
|
|
|
583
614
|
`);
|
|
584
615
|
}
|
|
585
616
|
//#endregion
|
|
586
|
-
export { API_KEYS_SUBCOMMANDS, apiKeysCommand, describeFailure, requireBackendUrl, resolveBackendAddress };
|
|
617
|
+
export { API_KEYS_SUBCOMMANDS, apiKeysCommand, cloudTargetNote, describeFailure, requireBackendUrl, resolveBackendAddress };
|
|
587
618
|
|
|
588
|
-
//# sourceMappingURL=api-keys-
|
|
619
|
+
//# sourceMappingURL=api-keys-CyJ26KEJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-keys-CyJ26KEJ.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 | {\n url: string;\n source: \"configured\" | \"running\";\n /**\n * Set when the address came from `.env` and is the cloud project this\n * directory is linked to — which `rebase cloud init --claim` writes\n * there for the app and the SDK, not as a choice of where local\n * commands go. The command still goes there; {@link cloudTargetNote}\n * says so.\n */\n cloudProject?: string;\n }\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 fromShell = process.env.REBASE_BASE_URL?.trim();\n const configured = fromShell || env.REBASE_BASE_URL?.trim();\n if (configured) {\n const cloudProject = fromShell ? undefined : linkedCloudProjectAt(projectRoot, configured);\n return cloudProject ? { url: configured, source: \"configured\", cloudProject } : { url: configured, source: \"configured\" };\n }\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/** The slug of the cloud project `.rebase/cloud.json` links, when `url` is its address. */\nfunction linkedCloudProjectAt(projectRoot: string, url: string): string | undefined {\n let link: unknown;\n try {\n link = JSON.parse(fs.readFileSync(path.join(projectRoot, \".rebase\", \"cloud.json\"), \"utf-8\"));\n } catch {\n return undefined;\n }\n if (!isRecord(link) || link.mode === \"direct\" || typeof link.apiUrl !== \"string\") return undefined;\n const bare = (u: string) => u.trim().replace(/\\/+$/, \"\");\n if (bare(link.apiUrl) !== bare(url)) return undefined;\n const slug = typeof link.slug === \"string\" && link.slug ? link.slug : link.projectId;\n return typeof slug === \"string\" && slug ? slug : \"linked\";\n}\n\n/**\n * What a local command says before it sends the service key to the cloud\n * project `.env` names (see `BackendAddress.cloudProject`), or undefined.\n */\nexport function cloudTargetNote(address: BackendAddress): string | undefined {\n if (address.url === null || !address.cloudProject) return undefined;\n return `Sending this to ${address.url}: REBASE_BASE_URL in .env is the address of cloud project ${address.cloudProject} `\n + \"(`rebase cloud init --claim` writes it there for the app, the SDK and the MCP server), and that project does not \"\n + \"take this project's local service key. To reach the local backend instead, run the command with \"\n + \"REBASE_BASE_URL set to it in the shell, e.g. REBASE_BASE_URL=http://localhost:3001.\";\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 const note = cloudTargetNote(address);\n if (note) console.error(chalk.yellow(` ⚠ ${note}`));\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":"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"}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { i as failAsJson, p as requireProjectRoot } from "./project-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { r as foldableApps } from "./fold-static-hDHtDfTq.js";
|
|
1
|
+
import { E as unknownCommand, i as failAsJson, p as requireProjectRoot, w as wantsHelp, x as parseCommandArgs } from "./project-CasK56vN.js";
|
|
2
|
+
import { d as findBackendApp, f as formatAppAddress, m as manifestExists, o as ManifestError, p as loadManifest, s as assessManagedCompatibility, v as staticAppAddress, x as writeManifest, y as synthesizeManifest } from "./manifest-BtzDy7KQ.js";
|
|
3
|
+
import { i as runningBackendUrl, n as devServerUrl } from "./dev-server-OJsUGZEB.js";
|
|
4
|
+
import { V as readLink } from "./context-GUxkZm-S.js";
|
|
5
|
+
import { r as foldableApps } from "./fold-static-BnAK-anQ.js";
|
|
7
6
|
import chalk from "chalk";
|
|
8
7
|
import path from "path";
|
|
9
8
|
//#region src/commands/apps.ts
|
|
@@ -323,4 +322,4 @@ function loadManifestOrExit(projectRoot, asJson = false) {
|
|
|
323
322
|
//#endregion
|
|
324
323
|
export { APPS_SUBCOMMANDS, appsCommand };
|
|
325
324
|
|
|
326
|
-
//# sourceMappingURL=apps-
|
|
325
|
+
//# sourceMappingURL=apps-CnLDV5b6.js.map
|