@rebasepro/cli 0.25.1 → 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.
Files changed (151) hide show
  1. package/README.md +2 -1
  2. package/bin/rebase.js +143 -4
  3. package/dist/{agent-setup-BYb2Tbdz.js → agent-setup-D9mHRZ5R.js} +125 -3
  4. package/dist/agent-setup-D9mHRZ5R.js.map +1 -0
  5. package/dist/{api-keys-CSMbsmAn.js → api-keys-CyJ26KEJ.js} +41 -9
  6. package/dist/api-keys-CyJ26KEJ.js.map +1 -0
  7. package/dist/{apps-iBXNZhVN.js → apps-CnLDV5b6.js} +6 -6
  8. package/dist/{apps-iBXNZhVN.js.map → apps-CnLDV5b6.js.map} +1 -1
  9. package/dist/{atlas-dev-database-BusbHP6n.js → atlas-dev-database-BODqdCWt.js} +2 -2
  10. package/dist/{atlas-dev-database-BusbHP6n.js.map → atlas-dev-database-BODqdCWt.js.map} +1 -1
  11. package/dist/{auth-DRsTWPky.js → auth-dII2jCO1.js} +10 -7
  12. package/dist/auth-dII2jCO1.js.map +1 -0
  13. package/dist/{branch-pointer-Bn_FjNld.js → branch-pointer-BQUF6lYa.js} +1 -1
  14. package/dist/{build-et7Rccxi.js → build-Cza7ZQrp.js} +6 -6
  15. package/dist/{build-et7Rccxi.js.map → build-Cza7ZQrp.js.map} +1 -1
  16. package/dist/{bundle-DlqkDYSe.js → bundle-C6yEtMJ-.js} +53 -5
  17. package/dist/bundle-C6yEtMJ-.js.map +1 -0
  18. package/dist/bundle.d.ts +14 -0
  19. package/dist/{cloud-B-ZIjHrA.js → cloud-BdnmqQaZ.js} +8527 -4340
  20. package/dist/cloud-BdnmqQaZ.js.map +1 -0
  21. package/dist/collections-dir-DlvDhime.js +69 -0
  22. package/dist/collections-dir-DlvDhime.js.map +1 -0
  23. package/dist/commands/agent-setup.d.ts +58 -0
  24. package/dist/commands/api-keys.d.ts +13 -0
  25. package/dist/commands/cloud/activity.d.ts +25 -0
  26. package/dist/commands/cloud/app-key.d.ts +18 -0
  27. package/dist/commands/cloud/auth.d.ts +1 -0
  28. package/dist/commands/cloud/billing-invoices.d.ts +39 -0
  29. package/dist/commands/cloud/claim.d.ts +7 -0
  30. package/dist/commands/cloud/context.d.ts +115 -6
  31. package/dist/commands/cloud/deploy-summary.d.ts +162 -0
  32. package/dist/commands/cloud/deploy.d.ts +64 -0
  33. package/dist/commands/cloud/env.d.ts +24 -0
  34. package/dist/commands/cloud/index.d.ts +13 -1
  35. package/dist/commands/cloud/init.d.ts +143 -0
  36. package/dist/commands/cloud/login-flow.d.ts +51 -0
  37. package/dist/commands/cloud/notifications.d.ts +28 -0
  38. package/dist/commands/cloud/orgs.d.ts +13 -0
  39. package/dist/commands/cloud/previews.d.ts +123 -0
  40. package/dist/commands/cloud/resources.d.ts +68 -14
  41. package/dist/commands/cloud/runtime-logs.d.ts +176 -0
  42. package/dist/commands/cloud/service-key.d.ts +15 -0
  43. package/dist/commands/cloud/token-capabilities.d.ts +61 -8
  44. package/dist/commands/cloud/tokens.d.ts +18 -3
  45. package/dist/commands/cloud/usage.d.ts +63 -0
  46. package/dist/commands/dev.d.ts +10 -0
  47. package/dist/commands/doctor.d.ts +1 -1
  48. package/dist/commands/generate-view.d.ts +94 -0
  49. package/dist/commands/generate_sdk.d.ts +15 -0
  50. package/dist/commands/skills.d.ts +4 -3
  51. package/dist/commands/storage.d.ts +4 -0
  52. package/dist/commands/verify.d.ts +96 -0
  53. package/dist/{consent-BaB-eQKG.js → consent-BD7F29T3.js} +2 -2
  54. package/dist/{consent-BaB-eQKG.js.map → consent-BD7F29T3.js.map} +1 -1
  55. package/dist/{console-BlD9kjFW.js → console-BXB6-8GT.js} +12 -6
  56. package/dist/console-BXB6-8GT.js.map +1 -0
  57. package/dist/{context-DHiiiJ5C.js → context-GUxkZm-S.js} +481 -24
  58. package/dist/context-GUxkZm-S.js.map +1 -0
  59. package/dist/{daemon-DDMRbz4T.js → daemon-Cp7oNXth.js} +2 -2
  60. package/dist/{daemon-DDMRbz4T.js.map → daemon-Cp7oNXth.js.map} +1 -1
  61. package/dist/{db-9P6MnmdC.js → db-KzlqddGD.js} +37 -30
  62. package/dist/db-KzlqddGD.js.map +1 -0
  63. package/dist/{derive-MTdClnkc.js → derive-ae9UOoOB.js} +2 -2
  64. package/dist/{derive-MTdClnkc.js.map → derive-ae9UOoOB.js.map} +1 -1
  65. package/dist/{dev-BDN_OKXA.js → dev-DTqVMgB3.js} +59 -32
  66. package/dist/dev-DTqVMgB3.js.map +1 -0
  67. package/dist/{dev-preflight-FT9GbJVj.js → dev-preflight-Cxv3TiD1.js} +2 -2
  68. package/dist/{dev-preflight-FT9GbJVj.js.map → dev-preflight-Cxv3TiD1.js.map} +1 -1
  69. package/dist/{dev-server-CIZwAfZa.js → dev-server-OJsUGZEB.js} +2 -2
  70. package/dist/{dev-server-CIZwAfZa.js.map → dev-server-OJsUGZEB.js.map} +1 -1
  71. package/dist/{doctor-D8YXz7df.js → doctor-BRWOlqeG.js} +13 -11
  72. package/dist/doctor-BRWOlqeG.js.map +1 -0
  73. package/dist/{eject-izG2KjMo.js → eject-DABcDnPN.js} +3 -3
  74. package/dist/{eject-izG2KjMo.js.map → eject-DABcDnPN.js.map} +1 -1
  75. package/dist/{fold-static-CybSay_W.js → fold-static-BnAK-anQ.js} +3 -3
  76. package/dist/{fold-static-CybSay_W.js.map → fold-static-BnAK-anQ.js.map} +1 -1
  77. package/dist/generate-view-IFwCADvb.js +662 -0
  78. package/dist/generate-view-IFwCADvb.js.map +1 -0
  79. package/dist/{generate_sdk-B4R65oUV.js → generate_sdk-BvcWvoUh.js} +33 -7
  80. package/dist/generate_sdk-BvcWvoUh.js.map +1 -0
  81. package/dist/index.es.js +57 -38
  82. package/dist/index.es.js.map +1 -1
  83. package/dist/{init-DYaeppEN.js → init-CaU6GhwI.js} +8 -8
  84. package/dist/{init-DYaeppEN.js.map → init-CaU6GhwI.js.map} +1 -1
  85. package/dist/last-dev-database-DpW5dfoX.js +2 -0
  86. package/dist/{manifest-nvPdLlJY.js → manifest-BtzDy7KQ.js} +36 -3
  87. package/dist/{manifest-nvPdLlJY.js.map → manifest-BtzDy7KQ.js.map} +1 -1
  88. package/dist/{normalize-imports-BYw8ad87.js → normalize-imports-D0zXlM6g.js} +3 -3
  89. package/dist/{normalize-imports-BYw8ad87.js.map → normalize-imports-D0zXlM6g.js.map} +1 -1
  90. package/dist/{path-flags-BFgG9V6y.js → path-flags-DNvtEi2-.js} +5 -4
  91. package/dist/path-flags-DNvtEi2-.js.map +1 -0
  92. package/dist/{prepare-1rVNd2PF.js → prepare-CTw9NEkh.js} +4 -4
  93. package/dist/{prepare-1rVNd2PF.js.map → prepare-CTw9NEkh.js.map} +1 -1
  94. package/dist/{project-g4K6kV1t.js → project-CasK56vN.js} +2 -2
  95. package/dist/{project-g4K6kV1t.js.map → project-CasK56vN.js.map} +1 -1
  96. package/dist/resolve-D647F1LA.js +2 -0
  97. package/dist/{resources-CovP8AZ-.js → resources-CPSC5Kcu.js} +4 -4
  98. package/dist/{resources-CovP8AZ-.js.map → resources-CPSC5Kcu.js.map} +1 -1
  99. package/dist/{schema-nuxvvc50.js → schema-BSXwZSiI.js} +9 -7
  100. package/dist/schema-BSXwZSiI.js.map +1 -0
  101. package/dist/{skills-DeUIsZcT.js → skills-BU2flMOy.js} +52 -7
  102. package/dist/skills-BU2flMOy.js.map +1 -0
  103. package/dist/{start-RzZqDroW.js → start-mVb6upwY.js} +5 -5
  104. package/dist/{start-RzZqDroW.js.map → start-mVb6upwY.js.map} +1 -1
  105. package/dist/static-syntax-check.d.ts +64 -0
  106. package/dist/{status-D9Zm88NZ.js → status-BREvtzJm.js} +7 -7
  107. package/dist/{status-D9Zm88NZ.js.map → status-BREvtzJm.js.map} +1 -1
  108. package/dist/{storage-BB3umRfw.js → storage-DtOXGflp.js} +159 -15
  109. package/dist/storage-DtOXGflp.js.map +1 -0
  110. package/dist/{telemetry-HfU5uAh1.js → telemetry-CgwJPVMO.js} +2 -2
  111. package/dist/{telemetry-HfU5uAh1.js.map → telemetry-CgwJPVMO.js.map} +1 -1
  112. package/dist/{telemetry-TseNLk6X.js → telemetry-DwK5RQJl.js} +4 -4
  113. package/dist/{telemetry-TseNLk6X.js.map → telemetry-DwK5RQJl.js.map} +1 -1
  114. package/dist/{upgrade-apiPKKE_.js → upgrade-B7sVwL8S.js} +4 -4
  115. package/dist/{upgrade-apiPKKE_.js.map → upgrade-B7sVwL8S.js.map} +1 -1
  116. package/dist/utils/collections-dir.d.ts +14 -0
  117. package/dist/utils/path-flags.d.ts +2 -1
  118. package/dist/utils/tool-stdio.d.ts +1 -1
  119. package/dist/verify-D7UsV_qe.js +730 -0
  120. package/dist/verify-D7UsV_qe.js.map +1 -0
  121. package/package.json +9 -7
  122. package/templates/eject/backend/src/index.ts +9 -1
  123. package/templates/generated-schemas/ecommerce.ts +3 -0
  124. package/templates/overlays/baas/ai-instructions.md +13 -0
  125. package/templates/template/.claude/hooks/post-edit-collection.sh +99 -0
  126. package/templates/template/.claude/settings.json +17 -0
  127. package/templates/template/.env.example +10 -1
  128. package/templates/template/ai-instructions.md +21 -1
  129. package/templates/template/config/collections/presets/ecommerce/index.ts +23 -0
  130. package/templates/template/docker-compose.yml +4 -1
  131. package/templates/template/frontend/package.json +1 -0
  132. package/templates/views/AppView.tsx.tmpl +138 -0
  133. package/templates/views/EntityView.tsx.tmpl +64 -0
  134. package/templates/views/index.ts.tmpl +8 -0
  135. package/dist/agent-setup-BYb2Tbdz.js.map +0 -1
  136. package/dist/api-keys-CSMbsmAn.js.map +0 -1
  137. package/dist/auth-DRsTWPky.js.map +0 -1
  138. package/dist/bundle-DlqkDYSe.js.map +0 -1
  139. package/dist/cloud-B-ZIjHrA.js.map +0 -1
  140. package/dist/console-BlD9kjFW.js.map +0 -1
  141. package/dist/context-DHiiiJ5C.js.map +0 -1
  142. package/dist/db-9P6MnmdC.js.map +0 -1
  143. package/dist/dev-BDN_OKXA.js.map +0 -1
  144. package/dist/doctor-D8YXz7df.js.map +0 -1
  145. package/dist/generate_sdk-B4R65oUV.js.map +0 -1
  146. package/dist/last-dev-database-B_75fxZC.js +0 -2
  147. package/dist/path-flags-BFgG9V6y.js.map +0 -1
  148. package/dist/resolve-BQUAC3Wi.js +0 -2
  149. package/dist/schema-nuxvvc50.js.map +0 -1
  150. package/dist/skills-DeUIsZcT.js.map +0 -1
  151. package/dist/storage-BB3umRfw.js.map +0 -1
@@ -0,0 +1,69 @@
1
+ import { d as findBackendApp, g as resolveBackendPaths, n as DEFAULT_CONFIG_DIR, p as loadManifest } from "./manifest-BtzDy7KQ.js";
2
+ import path from "path";
3
+ //#region src/utils/collections-dir.ts
4
+ /**
5
+ * Where this project's collections are: one answer for every command.
6
+ *
7
+ * `rebase.json`'s `apps.<backend>.config` names the config package, and the
8
+ * collections live in `<config>/collections`. The dev runtime, `rebase build`
9
+ * and `rebase generate view` honoured it; `verify`, `doctor`, `generate-sdk`,
10
+ * `console`, and dev's watcher and schema/SDK regeneration each hardcoded
11
+ * `config/collections`. In a project with `"config": "shared"` the runtime
12
+ * loaded `shared/collections` while `rebase verify` reported "no collections
13
+ * directory … 0 errors", `generate-sdk` said the API derives from the
14
+ * database, `doctor` found no collections, and dev never regenerated on an
15
+ * edit.
16
+ *
17
+ * A `--collections` typed on the command line still wins wherever a command
18
+ * takes one; this is only the default.
19
+ */
20
+ /** The config package directory, relative to the project root, as the backend app declares it. */
21
+ function resolveConfigDir(projectRoot) {
22
+ try {
23
+ const backend = findBackendApp(loadManifest(projectRoot).manifest);
24
+ if (backend) return resolveBackendPaths(backend.app, projectRoot).config;
25
+ } catch {}
26
+ return DEFAULT_CONFIG_DIR;
27
+ }
28
+ /** `<config>/collections`, absolute. */
29
+ function resolveCollectionsDir(projectRoot) {
30
+ return path.join(projectRoot, resolveConfigDir(projectRoot), "collections");
31
+ }
32
+ /** Whether a command line already names a collections directory. */
33
+ function namesCollections(args) {
34
+ return args.some((arg) => arg === "--collections" || arg === "-c" || arg.startsWith("--collections=") || arg.startsWith("-c="));
35
+ }
36
+ /**
37
+ * The driver commands that read collections and take `--collections` — the
38
+ * driver defaults each of them to `../config/collections` from `backend/`.
39
+ * Keyed as the driver's argv starts: domain, then subcommand.
40
+ */
41
+ var DRIVER_COMMANDS_READING_COLLECTIONS = /* @__PURE__ */ new Set([
42
+ "db push",
43
+ "db generate",
44
+ "schema generate",
45
+ "schema stale",
46
+ "doctor",
47
+ "console"
48
+ ]);
49
+ /**
50
+ * `["--collections", <dir>]` to append to a driver command line, or nothing.
51
+ *
52
+ * `driverArgs` is the line as the driver gets it (`["db", "push", …]`,
53
+ * `["doctor", …]`). Nothing for a command that does not read collections, nothing
54
+ * when the line already names a directory, and nothing when the project keeps
55
+ * its collections where the driver looks by default, so a conventional
56
+ * project's command lines are unchanged.
57
+ */
58
+ function collectionsDirArgs(projectRoot, driverArgs) {
59
+ const command = DRIVER_COMMANDS_READING_COLLECTIONS.has(driverArgs[0]) ? driverArgs[0] : `${driverArgs[0]} ${driverArgs[1]}`;
60
+ if (!DRIVER_COMMANDS_READING_COLLECTIONS.has(command)) return [];
61
+ if (namesCollections(driverArgs)) return [];
62
+ const dir = resolveCollectionsDir(projectRoot);
63
+ if (dir === path.join(projectRoot, "config", "collections")) return [];
64
+ return ["--collections", dir];
65
+ }
66
+ //#endregion
67
+ export { collectionsDirArgs, resolveCollectionsDir, resolveConfigDir };
68
+
69
+ //# sourceMappingURL=collections-dir-DlvDhime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collections-dir-DlvDhime.js","names":[],"sources":["../src/utils/collections-dir.ts"],"sourcesContent":["/**\n * Where this project's collections are: one answer for every command.\n *\n * `rebase.json`'s `apps.<backend>.config` names the config package, and the\n * collections live in `<config>/collections`. The dev runtime, `rebase build`\n * and `rebase generate view` honoured it; `verify`, `doctor`, `generate-sdk`,\n * `console`, and dev's watcher and schema/SDK regeneration each hardcoded\n * `config/collections`. In a project with `\"config\": \"shared\"` the runtime\n * loaded `shared/collections` while `rebase verify` reported \"no collections\n * directory … 0 errors\", `generate-sdk` said the API derives from the\n * database, `doctor` found no collections, and dev never regenerated on an\n * edit.\n *\n * A `--collections` typed on the command line still wins wherever a command\n * takes one; this is only the default.\n */\nimport path from \"path\";\nimport { DEFAULT_CONFIG_DIR, findBackendApp, loadManifest, resolveBackendPaths } from \"../manifest\";\n\n/** The config package directory, relative to the project root, as the backend app declares it. */\nexport function resolveConfigDir(projectRoot: string): string {\n try {\n const backend = findBackendApp(loadManifest(projectRoot).manifest);\n if (backend) return resolveBackendPaths(backend.app, projectRoot).config;\n } catch {\n // A malformed rebase.json is reported by the commands that own it; the\n // conventional layout is the answer until then.\n }\n return DEFAULT_CONFIG_DIR;\n}\n\n/** `<config>/collections`, absolute. */\nexport function resolveCollectionsDir(projectRoot: string): string {\n return path.join(projectRoot, resolveConfigDir(projectRoot), \"collections\");\n}\n\n/** Whether a command line already names a collections directory. */\nfunction namesCollections(args: readonly string[]): boolean {\n return args.some(arg => arg === \"--collections\" || arg === \"-c\" || arg.startsWith(\"--collections=\") || arg.startsWith(\"-c=\"));\n}\n\n/**\n * The driver commands that read collections and take `--collections` — the\n * driver defaults each of them to `../config/collections` from `backend/`.\n * Keyed as the driver's argv starts: domain, then subcommand.\n */\nconst DRIVER_COMMANDS_READING_COLLECTIONS = new Set([\n \"db push\",\n \"db generate\",\n \"schema generate\",\n \"schema stale\",\n \"doctor\",\n \"console\"\n]);\n\n/**\n * `[\"--collections\", <dir>]` to append to a driver command line, or nothing.\n *\n * `driverArgs` is the line as the driver gets it (`[\"db\", \"push\", …]`,\n * `[\"doctor\", …]`). Nothing for a command that does not read collections, nothing\n * when the line already names a directory, and nothing when the project keeps\n * its collections where the driver looks by default, so a conventional\n * project's command lines are unchanged.\n */\nexport function collectionsDirArgs(projectRoot: string, driverArgs: readonly string[]): string[] {\n const command = DRIVER_COMMANDS_READING_COLLECTIONS.has(driverArgs[0])\n ? driverArgs[0]\n : `${driverArgs[0]} ${driverArgs[1]}`;\n if (!DRIVER_COMMANDS_READING_COLLECTIONS.has(command)) return [];\n if (namesCollections(driverArgs)) return [];\n const dir = resolveCollectionsDir(projectRoot);\n if (dir === path.join(projectRoot, DEFAULT_CONFIG_DIR, \"collections\")) return [];\n return [\"--collections\", dir];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,iBAAiB,aAA6B;CAC1D,IAAI;EACA,MAAM,UAAU,eAAe,aAAa,WAAW,CAAC,CAAC,QAAQ;EACjE,IAAI,SAAS,OAAO,oBAAoB,QAAQ,KAAK,WAAW,CAAC,CAAC;CACtE,QAAQ,CAGR;CACA,OAAO;AACX;;AAGA,SAAgB,sBAAsB,aAA6B;CAC/D,OAAO,KAAK,KAAK,aAAa,iBAAiB,WAAW,GAAG,aAAa;AAC9E;;AAGA,SAAS,iBAAiB,MAAkC;CACxD,OAAO,KAAK,MAAK,QAAO,QAAQ,mBAAmB,QAAQ,QAAQ,IAAI,WAAW,gBAAgB,KAAK,IAAI,WAAW,KAAK,CAAC;AAChI;;;;;;AAOA,IAAM,sDAAsC,IAAI,IAAI;CAChD;CACA;CACA;CACA;CACA;CACA;AACJ,CAAC;;;;;;;;;;AAWD,SAAgB,mBAAmB,aAAqB,YAAyC;CAC7F,MAAM,UAAU,oCAAoC,IAAI,WAAW,EAAE,IAC/D,WAAW,KACX,GAAG,WAAW,GAAG,GAAG,WAAW;CACrC,IAAI,CAAC,oCAAoC,IAAI,OAAO,GAAG,OAAO,CAAC;CAC/D,IAAI,iBAAiB,UAAU,GAAG,OAAO,CAAC;CAC1C,MAAM,MAAM,sBAAsB,WAAW;CAC7C,IAAI,QAAQ,KAAK,KAAK,aAAA,UAAiC,aAAa,GAAG,OAAO,CAAC;CAC/E,OAAO,CAAC,iBAAiB,GAAG;AAChC"}
@@ -114,3 +114,61 @@ export declare function printAgentSetup({ results, skillsError, mcpDependency }:
114
114
  * the developer says no or unticks everything.
115
115
  */
116
116
  export declare function promptForAgents(home?: string): Promise<AgentKey[]>;
117
+ /**
118
+ * The guardrail every scaffold ships for Claude Code: a PostToolUse hook that
119
+ * runs `rebase verify --quiet` after each edit under `config/collections/` and
120
+ * hands any problem back to the agent (exit 2, the message on stderr).
121
+ *
122
+ * The files live in the template, under `.claude/`, so `rebase init` copies
123
+ * them like everything else and `rebase skills --hooks` installs the very same
124
+ * bytes into a project that predates them. Paths are project-relative.
125
+ */
126
+ export declare const AGENT_HOOK_SCRIPT = ".claude/hooks/post-edit-collection.sh";
127
+ export declare const AGENT_HOOK_SETTINGS = ".claude/settings.json";
128
+ /** What installing one hook file did. */
129
+ export type HookWriteStatus =
130
+ /** Written, or merged into the settings beside the hooks already there. */
131
+ "added"
132
+ /** Already there, unchanged — or, for the settings, already registered. */
133
+ | "present"
134
+ /** There, and edited: left as it was. `--force` replaces it. */
135
+ | "kept"
136
+ /** There, edited, and replaced because `--force` asked. */
137
+ | "replaced"
138
+ /** The settings file is not plain JSON with a `hooks` object: left alone. */
139
+ | "unreadable";
140
+ export interface HookWriteResult {
141
+ file: string;
142
+ status: HookWriteStatus;
143
+ }
144
+ /**
145
+ * The SHA-256 of every version of `post-edit-collection.sh` this CLI has ever
146
+ * written, oldest first, the current one last — hashed with CRLF read as LF,
147
+ * so a Windows checkout of an untouched copy still matches.
148
+ *
149
+ * A copy that differs from today's script was read as "edited" and kept, so a
150
+ * fix to the hook never reached a project scaffolded before it: `rebase skills
151
+ * install --hooks` answered "was edited, so it was kept — pass --force" about a
152
+ * file nobody had touched. A copy whose hash is one of ours is our old
153
+ * version, and is replaced like any stale file; only a copy we never shipped is
154
+ * the developer's.
155
+ *
156
+ * Append, never replace: `agent-hooks.test.ts` fails when the shipped script's
157
+ * hash is not here, so changing the hook means adding its new hash — and the
158
+ * old one stays, which is the whole point.
159
+ */
160
+ export declare const SHIPPED_HOOK_SCRIPT_SHA256S: readonly string[];
161
+ /** The hash {@link SHIPPED_HOOK_SCRIPT_SHA256S} records for a script's text. */
162
+ export declare function hookScriptSha256(text: string): string;
163
+ /**
164
+ * Install the Claude Code guardrail into a project: the hook script, and its
165
+ * registration in `.claude/settings.json`.
166
+ *
167
+ * A script the developer edited is kept unless `force`; a version this CLI
168
+ * shipped earlier is replaced. The settings are always merged, never
169
+ * overwritten.
170
+ */
171
+ export declare function installAgentHooks(projectDir: string, { force, templateDir }?: {
172
+ force?: boolean;
173
+ templateDir?: string;
174
+ }): HookWriteResult[];
@@ -13,6 +13,14 @@ export declare const API_KEYS_SUBCOMMANDS: readonly ["list", "get", "create", "r
13
13
  export type BackendAddress = {
14
14
  url: string;
15
15
  source: "configured" | "running";
16
+ /**
17
+ * Set when the address came from `.env` and is the cloud project this
18
+ * directory is linked to — which `rebase cloud init --claim` writes
19
+ * there for the app and the SDK, not as a choice of where local
20
+ * commands go. The command still goes there; {@link cloudTargetNote}
21
+ * says so.
22
+ */
23
+ cloudProject?: string;
16
24
  } | {
17
25
  url: null;
18
26
  reason: string;
@@ -35,6 +43,11 @@ export type BackendAddress = {
35
43
  * other server may have taken since.
36
44
  */
37
45
  export declare function resolveBackendAddress(env: Record<string, string>, projectRoot: string): BackendAddress;
46
+ /**
47
+ * What a local command says before it sends the service key to the cloud
48
+ * project `.env` names (see `BackendAddress.cloudProject`), or undefined.
49
+ */
50
+ export declare function cloudTargetNote(address: BackendAddress): string | undefined;
38
51
  /**
39
52
  * The address to send the service key to, or the end of the command.
40
53
  *
@@ -0,0 +1,25 @@
1
+ export declare const ACTIVITY_FLAGS: {
2
+ readonly "--org": StringConstructor;
3
+ readonly "--action": StringConstructor;
4
+ readonly "--actor": StringConstructor;
5
+ readonly "--limit": StringConstructor;
6
+ readonly "--before": StringConstructor;
7
+ };
8
+ /** One event, as the control plane sends it. */
9
+ export interface CliAuditEvent {
10
+ id: string;
11
+ createdAt: string;
12
+ action: string;
13
+ target: string | null;
14
+ projectId: string | null;
15
+ projectLabel: string | null;
16
+ actorKind: string;
17
+ actorLabel: string | null;
18
+ actorUserId: string | null;
19
+ metadata: Record<string, unknown>;
20
+ }
21
+ /** Who, in a few words: an email, a token's name and owner, a hook, the platform. */
22
+ export declare function actorText(event: CliAuditEvent): string;
23
+ /** The line under an event, when its metadata says more than its action does. */
24
+ export declare function detailText(event: CliAuditEvent): string;
25
+ export declare function activityCommand(rawArgs: string[]): Promise<void>;
@@ -0,0 +1,18 @@
1
+ export declare const APP_KEY_FLAGS: {};
2
+ export declare const APP_KEY_ACTIONS: readonly ["rotate"];
3
+ /** An agent key on the app, as the control plane lists it. Never the key itself. */
4
+ export interface CliAgentKey {
5
+ id: string;
6
+ prefix: string | null;
7
+ createdAt: string | null;
8
+ expiresAt: string | null;
9
+ }
10
+ /** What a rotation answers: the new key, once. */
11
+ export interface CliRotatedAppKey {
12
+ apiKey: string;
13
+ keyId: string;
14
+ scopes: string[];
15
+ expiresAt: string | null;
16
+ revoked: string[];
17
+ }
18
+ export declare function appKeyCommand(rawArgs: string[]): Promise<void>;
@@ -9,6 +9,7 @@
9
9
  export declare const LOGIN_FLAGS: {
10
10
  readonly "--email": StringConstructor;
11
11
  readonly "--password": StringConstructor;
12
+ readonly "--code": BooleanConstructor;
12
13
  readonly "-e": "--email";
13
14
  };
14
15
  /**
@@ -0,0 +1,39 @@
1
+ import { type CloudClient } from "./context.js";
2
+ /** One issued invoice, as the control plane sends it. */
3
+ export interface CliInvoice {
4
+ id: string;
5
+ number: string | null;
6
+ status: string | null;
7
+ created: string | null;
8
+ periodStart: string | null;
9
+ periodEnd: string | null;
10
+ total: number;
11
+ amountDue: number;
12
+ currency: string;
13
+ hostedInvoiceUrl: string | null;
14
+ invoicePdf: string | null;
15
+ }
16
+ /** What one subscription will invoice next. */
17
+ export interface CliUpcomingInvoice {
18
+ subscriptionId: string;
19
+ kind: "project" | "usage";
20
+ projectId: string | null;
21
+ projectName: string | null;
22
+ total: number;
23
+ amountDue: number;
24
+ currency: string;
25
+ expectedAt: string | null;
26
+ periodStart: string | null;
27
+ periodEnd: string | null;
28
+ lines: {
29
+ description: string | null;
30
+ amount: number;
31
+ currency: string;
32
+ quantity: number | null;
33
+ }[];
34
+ moreLines: boolean;
35
+ }
36
+ /** An amount in a currency's minor unit, for a person. The divisor is the currency's own. */
37
+ export declare function formatMinor(amount: number, currency: string): string;
38
+ export declare function billingInvoices(client: CloudClient, org: string, limit?: number): Promise<void>;
39
+ export declare function billingUpcoming(client: CloudClient, org: string): Promise<void>;
@@ -0,0 +1,7 @@
1
+ /** What `rebase cloud claim` parses, beside the global flags. Its page pairs against this. */
2
+ export declare const CLAIM_FLAGS: {
3
+ readonly "--open": BooleanConstructor;
4
+ };
5
+ /** "41h", "25m", "under a minute": how long is left, for a person. */
6
+ export declare function timeLeft(expiresAt: string, now?: number): string;
7
+ export declare function claimCommand(rawArgs: string[], now?: () => number): Promise<void>;
@@ -13,6 +13,70 @@ import { createRebaseClient } from "@rebasepro/client";
13
13
  */
14
14
  export declare function billingAccountIdOf(org: unknown): string | number | undefined;
15
15
  export declare function projectLinkPath(cwd?: string): string;
16
+ /**
17
+ * What an account-less provision hands back, kept until it stops mattering.
18
+ *
19
+ * Both values are bearer secrets — the token deploys to the project, the claim
20
+ * link moves it into the organization of whoever opens it — so they live here, in
21
+ * the private credentials file, and never in the repository: not in `.env`,
22
+ * where `REBASE_API_TOKEN` beside `REBASE_BASE_URL` is what the MCP server and
23
+ * the SDK send to the APP (which knows nothing of a control-plane token; `init`
24
+ * writes a key minted on the app there instead), and not in
25
+ * `.rebase/cloud.json`, which a cloned repository supplies.
26
+ */
27
+ export interface EphemeralGrant {
28
+ token: string;
29
+ claimUrl: string;
30
+ /** ISO time the project, and the token with it, expires unless claimed. */
31
+ expiresAt: string;
32
+ }
33
+ /** Keep an unclaimed project's token and claim link for this host. */
34
+ export declare function saveEphemeralGrant(url: string, projectId: string, grant: EphemeralGrant): void;
35
+ /** Drop a grant — once its project is claimed, or its token is refused. */
36
+ export declare function forgetEphemeralGrant(url: string, projectId: string): void;
37
+ /** The grant for a project on a host, while it has not expired. */
38
+ export declare function ephemeralGrantFor(url: string, projectId: string, now?: number): EphemeralGrant | undefined;
39
+ /**
40
+ * The grant as it is stored, expired or not: what a message about a refused
41
+ * token can still show (the claim link) after the token itself stops counting.
42
+ */
43
+ export declare function storedEphemeralGrant(url: string, projectId: string): EphemeralGrant | undefined;
44
+ /**
45
+ * The grant for the project this directory is linked to, when the command
46
+ * targets the host it was provisioned on.
47
+ */
48
+ export declare function linkedEphemeralGrant(url: string): {
49
+ projectId: string;
50
+ grant: EphemeralGrant;
51
+ } | undefined;
52
+ /**
53
+ * The unexpired grant for the project a link names, on the host it names: what
54
+ * `status`, `unlink` and `init` read the claim URL back from. Undefined for a
55
+ * claimed project (its grant is gone), a direct link, or no link.
56
+ */
57
+ export declare function grantForLink(link: ProjectLink | null | undefined): EphemeralGrant | undefined;
58
+ /**
59
+ * The grant a link names as it is stored, expired or not, with the host it is
60
+ * kept under: what `rebase cloud claim` reads, so it can say "expired" rather
61
+ * than "never had one".
62
+ */
63
+ export declare function storedGrantForLink(link: ProjectLink | null | undefined): {
64
+ url: string;
65
+ grant: EphemeralGrant;
66
+ } | undefined;
67
+ /** Whether a `.env` sets a variable to something: an empty `KEY=` or `KEY=""` does not. */
68
+ export declare function dotenvSets(file: string, key: string): boolean;
69
+ /** Whether this project's `.env` (at its root, else here) holds an app key. */
70
+ export declare function projectDotenvSetsAppKey(): boolean;
71
+ /**
72
+ * When the app key `init --claim` minted stops working, and what to do about
73
+ * it. It is minted to expire with the claim window; a claim hands it over to
74
+ * the person who claims (the control plane lifts its expiry), so an unclaimed
75
+ * project's key stops at the window's end and a claimed one's does not. The
76
+ * claimer replaces it — the agent holds a copy — with `rebase cloud app-key
77
+ * rotate`. Every command that knows the date says so.
78
+ */
79
+ export declare function appKeyExpiryNote(expiresAt: string): string;
16
80
  /** Persist the active organization id for a host. */
17
81
  export declare function setContextOrg(url: string, org: string | undefined): void;
18
82
  export declare function getContextOrg(url: string): string | undefined;
@@ -67,6 +131,12 @@ export declare function createCloudClient(url: string): CloudClient;
67
131
  export declare const TOKEN_ENV = "REBASE_TOKEN";
68
132
  /** `REBASE_TOKEN`, trimmed, or undefined when it is unset or blank. */
69
133
  export declare function envToken(): string | undefined;
134
+ /**
135
+ * The project whose `rebase cloud init --claim` token a client holds, or
136
+ * undefined for a session or `REBASE_TOKEN`: what `whoami` reports, since
137
+ * {@link requireClient} may pick the grant over `REBASE_TOKEN`.
138
+ */
139
+ export declare function grantProjectOf(client: object): string | undefined;
70
140
  /**
71
141
  * A client that sends `token` as its bearer and nothing else.
72
142
  *
@@ -76,7 +146,30 @@ export declare function envToken(): string | undefined;
76
146
  * rather than by the SDK's session refresh, because there is no session to
77
147
  * refresh and "log in again" is the wrong remedy for a token.
78
148
  */
79
- export declare function createTokenClient(url: string, token: string): CloudClient;
149
+ export declare function createTokenClient(url: string, token: string, ephemeralProjectId?: string): CloudClient;
150
+ /**
151
+ * Say, once, that the control plane marked a token's request as deprecated.
152
+ *
153
+ * The control plane answers a request it still serves, but will not in a later
154
+ * release, with `Deprecation: true` (RFC 9745), a `Link` with
155
+ * `rel="deprecation"` to the page that explains it, and an
156
+ * `X-Rebase-Deprecation` sentence saying what to do — a token from before
157
+ * `org:billing` reading billing is the first. CI logs are where that is seen,
158
+ * so it goes to stderr as a warning, once per notice per process, and never
159
+ * into the `--json` value on stdout.
160
+ *
161
+ * Exported so its test can hand it a response.
162
+ */
163
+ export declare function noticeDeprecation(response: Response): void;
164
+ /** Forget what {@link tokenScopes} asked and who was told — each test is a new process. */
165
+ export declare function resetTokenScopesForTest(): void;
166
+ /**
167
+ * Whether the control plane still accepts a token: one request to
168
+ * `/api/auth/scopes`, the route every token may read. `refused` only when it
169
+ * answered 401 or 403; `unknown` when it could not be asked, or answered
170
+ * something else — which proves nothing about the token.
171
+ */
172
+ export declare function probeToken(url: string, token: string): Promise<"accepted" | "refused" | "unknown">;
80
173
  /** The half of a client that holds its session — all {@link freshAccessToken} reads. */
81
174
  export interface SessionHolder {
82
175
  auth: {
@@ -108,8 +201,14 @@ export declare function freshAccessToken(client: SessionHolder): Promise<string>
108
201
  * Return an authenticated client for the resolved host.
109
202
  *
110
203
  * With `REBASE_TOKEN` set, that token is the credential for every request and
111
- * the stored login is not consulted at all. Otherwise it is the `cloud login`
112
- * session — see {@link requireSessionClient}.
204
+ * the stored login is not consulted at all — except in a directory linked to a
205
+ * project `init --claim` provisioned, when `REBASE_TOKEN` holds no scope on that
206
+ * project (one `/api/auth/scopes` call): the project's own token acts there
207
+ * instead, and the command says so once. Next, a directory linked to a
208
+ * project `rebase cloud init --claim` provisioned uses the token stored for it
209
+ * (see {@link EphemeralGrant}) until it expires: nobody can sign in as that
210
+ * project's owner, so a login could not reach it anyway. Otherwise it is the
211
+ * `cloud login` session — see {@link requireSessionClient}.
113
212
  */
114
213
  export declare function requireClient(rawArgs: string[]): Promise<{
115
214
  client: CloudClient;
@@ -123,9 +222,11 @@ export declare function requireClient(rawArgs: string[]): Promise<{
123
222
  *
124
223
  * For the commands a token cannot run — minting and revoking tokens: a key that
125
224
  * could mint keys could mint its own successor, so the control plane refuses
126
- * every key on those routes.
225
+ * every key on those routes. Inviting people is the other: it hands out access
226
+ * to every project in an organization, which no token capability grants.
227
+ * `action` names what the refusal says a token cannot do.
127
228
  */
128
- export declare function requireSessionClient(rawArgs: string[]): Promise<{
229
+ export declare function requireSessionClient(rawArgs: string[], action?: string): Promise<{
129
230
  client: CloudClient;
130
231
  url: string;
131
232
  }>;
@@ -279,7 +380,15 @@ export declare function displayProjectRef(rawArgs: string[]): string;
279
380
  * `cloudCommand`, before anything can print or `fail`. Returns the resolved mode
280
381
  * (handy for tests, which otherwise leave it at its `false` default).
281
382
  */
282
- export declare function initOutputMode(rawArgs: string[]): boolean;
383
+ export declare function initOutputMode(rawArgs: string[], opts?: {
384
+ /**
385
+ * What a piped run with no explicit choice gets. `json` for the family.
386
+ * `human` for a command whose output a person needs to read even when a
387
+ * program is relaying it — `cloud deploy`, whose JSON told an agent's
388
+ * user neither where the app was nor whether it was live.
389
+ */
390
+ whenPiped?: "json" | "human";
391
+ }): boolean;
283
392
  /** Whether the current invocation is emitting machine-readable JSON. */
284
393
  export declare function isJsonMode(): boolean;
285
394
  /** Force the mode (tests only — production latches it via `initOutputMode`). */
@@ -0,0 +1,162 @@
1
+ import type { RebaseBundleManifest } from "@rebasepro/types";
2
+ import type { DeployHost } from "./bundle-deploy.js";
3
+ /**
4
+ * Where a backend's API is mounted on its host. The runtime's default
5
+ * `REBASE_BASE_PATH`, which the platform does not let a project change.
6
+ */
7
+ export declare const BACKEND_API_PATH = "/api";
8
+ /** How long the live check waits by default before saying it could not confirm. */
9
+ export declare const DEFAULT_LIVE_TIMEOUT_MS = 60000;
10
+ /** One app a deploy shipped, and the address it now answers on. */
11
+ export interface DeployedApp {
12
+ name: string;
13
+ type: "backend" | "static";
14
+ /**
15
+ * The address a person would open — the site for a static app, the API
16
+ * root for a backend. Absent when the host it is served on is unknown:
17
+ * a guessed URL is worse than none.
18
+ */
19
+ url?: string;
20
+ /** The hostname `url` is on. */
21
+ host?: string;
22
+ }
23
+ /**
24
+ * The apps a bundle ships, read off the bundle's own manifest.
25
+ *
26
+ * The manifest rather than `rebase.json`, because it is what was uploaded: it
27
+ * covers `--bundle-dir` and a static app deployed alone exactly as it covers a
28
+ * backend with its frontends folded in. A static app with a hostname of its own
29
+ * answers there; every other app answers on the project's host.
30
+ */
31
+ export declare function bundleApps(manifest: Pick<RebaseBundleManifest, "app" | "kind"> & {
32
+ entry?: {
33
+ static?: RebaseBundleManifest["entry"]["static"];
34
+ };
35
+ }, projectHost: string | undefined): DeployedApp[];
36
+ /**
37
+ * The one app a source build ships: whatever image the build produced, on the
38
+ * project's host. The CLI did not build it, so it cannot say what is inside.
39
+ */
40
+ export declare function sourceBuildApps(appName: string | undefined, projectHost: string | undefined): DeployedApp[];
41
+ /**
42
+ * Every hostname this deploy is served on: the project's own host, then the
43
+ * hostnames the control plane registered for apps that declare one.
44
+ *
45
+ * The control plane's `hosts` lists only the second kind — hostnames an app's
46
+ * `path` names, which a deploy has to register — so for the ordinary project,
47
+ * served on its own `<slug>.rebase.website`, it was always empty. That read as
48
+ * "this deploy is served nowhere". The project's host is served by the platform
49
+ * from the moment the project exists, so it goes first, as verified.
50
+ */
51
+ export declare function deployHosts(registered: DeployHost[] | undefined, projectHost: string | undefined): DeployHost[];
52
+ /** What probing one app's address found. */
53
+ export interface LiveCheck {
54
+ app: string;
55
+ /** The address probed: the site itself, or a backend's `/health` (or `/api/health`). */
56
+ url: string;
57
+ live: boolean;
58
+ /** The HTTP status of the last answer, when there was one. */
59
+ httpStatus?: number;
60
+ /**
61
+ * The deployment the answer says it came from (`x-rebase-deployment`), when
62
+ * it says. Only the platform's static hosting stamps it; a managed pod's
63
+ * readiness is the control plane's to confirm, and it did before `success`.
64
+ */
65
+ servingDeployment?: string;
66
+ /** Why it is not live, in a few words. */
67
+ reason?: string;
68
+ }
69
+ /**
70
+ * The URL that proves an app is up.
71
+ *
72
+ * For a backend, the bare `/health`: every rebase-server registers it, beside
73
+ * `<REBASE_BASE_PATH>/health`, and the platform routes it to the backend ahead
74
+ * of any static app. `/api/health` is only where it lives when the base path is
75
+ * the default — a project with `REBASE_BASE_PATH=/v1` and a site at `/` answers
76
+ * it with the site's index.html, and its deploy read as not live.
77
+ */
78
+ export declare function probeUrl(app: DeployedApp): string | undefined;
79
+ type FetchLike = typeof fetch;
80
+ /**
81
+ * Probe one app once.
82
+ *
83
+ * A backend is live when its health route answers `200 {"status":"ok"}` — a
84
+ * site at `/` with client-side routing answers 200 to *any* path, so a bare
85
+ * status would let a folded SPA vouch for an API that is down. A static app is
86
+ * live when its address answers below 400 and, if the answer names the
87
+ * deployment it came from, that deployment is this one.
88
+ */
89
+ export declare function probeOnce(app: DeployedApp, deploymentId: string, fetchImpl: FetchLike): Promise<LiveCheck | undefined>;
90
+ /**
91
+ * Probe every app on a verified host until each is live or `timeoutMs` passes.
92
+ *
93
+ * An app on a hostname whose DNS is not verified is reported, not probed: the
94
+ * platform does not route it, and waiting on it would only spend the timeout.
95
+ * Returns one entry per app that has a URL.
96
+ */
97
+ export declare function checkLive(apps: DeployedApp[], opts: {
98
+ deploymentId: string;
99
+ hosts: DeployHost[];
100
+ timeoutMs: number;
101
+ intervalMs?: number;
102
+ fetchImpl?: FetchLike;
103
+ sleep?: (ms: number) => Promise<void>;
104
+ now?: () => number;
105
+ }): Promise<LiveCheck[]>;
106
+ /** `42s`, `1m 12s`, `12m`. */
107
+ export declare function formatDuration(ms: number): string;
108
+ export interface DeploySummaryInput {
109
+ projectRef: string;
110
+ deploymentId: string;
111
+ elapsedMs: number;
112
+ apps: DeployedApp[];
113
+ /**
114
+ * The live check's results; `undefined` when it was not run
115
+ * (`--live-timeout 0`), which is said rather than passed over.
116
+ */
117
+ live: LiveCheck[] | undefined;
118
+ liveTimeoutMs: number;
119
+ }
120
+ /**
121
+ * The human summary a successful deploy ends on: the project, each app with its
122
+ * address and whether it answers, the deployment id and how long it took, and
123
+ * one line that says whether the new version is live.
124
+ */
125
+ export declare function deploySummaryLines(input: DeploySummaryInput): string[];
126
+ /**
127
+ * Whether the new version is live, as one sentence and one boolean.
128
+ *
129
+ * `live: null` when nothing was checked — the check was skipped, or no app had
130
+ * an address to check — which is not the same answer as "no".
131
+ */
132
+ export declare function liveVerdict(live: LiveCheck[] | undefined, timeoutMs: number): {
133
+ live: boolean | null;
134
+ message: string;
135
+ };
136
+ /**
137
+ * Why a deployment failed, in one line, from its build log.
138
+ *
139
+ * The control plane ends a failed job's log on a `❌ <heading>: <why>` line (a
140
+ * cancelled one on `🛑`), so that line is the reason. A log without one — an
141
+ * older control plane, a job that died mid-write — gives its last line instead,
142
+ * which is still closer to the truth than "failed".
143
+ */
144
+ export declare function failureReason(logs: string): string | undefined;
145
+ /**
146
+ * The one line a failed deploy ends on: which app, why, and where the rest is.
147
+ */
148
+ export declare function failureLine(opts: {
149
+ apps: string[];
150
+ status: string;
151
+ deploymentId: string;
152
+ logs: string;
153
+ }): string;
154
+ /**
155
+ * Where to read the rest of a failed deploy's story.
156
+ *
157
+ * A preview's build log is not production's: bare `rebase cloud logs` reads
158
+ * the newest PRODUCTION deployment, so pointing a failed preview at it showed
159
+ * the last good production build under the preview's deployment id.
160
+ */
161
+ export declare function failureHint(deploymentId: string, preview?: string): string;
162
+ export {};