@shaferllc/keel 0.83.4 → 0.83.5

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.
@@ -15,7 +15,17 @@ import { defaultConfig, resolveConfig } from "./config.js";
15
15
  import { accountsMigration } from "./migration.js";
16
16
  import { registerAccountsRoutes } from "./routes.js";
17
17
  import { setAccountStore, tableStore } from "./store.js";
18
- const here = dirname(fileURLToPath(import.meta.url));
18
+ function packageDir() {
19
+ try {
20
+ const url = import.meta.url;
21
+ if (!url)
22
+ return ".";
23
+ return dirname(fileURLToPath(url));
24
+ }
25
+ catch {
26
+ return ".";
27
+ }
28
+ }
19
29
  export class AccountsServiceProvider extends PackageProvider {
20
30
  name = "accounts";
21
31
  config;
@@ -24,7 +34,7 @@ export class AccountsServiceProvider extends PackageProvider {
24
34
  this.config = resolveConfig();
25
35
  setAccountStore(tableStore(this.config.userTable));
26
36
  this.migrations([accountsMigration(this.config.userTable)]);
27
- this.publishes({ [join(here, "accounts.config.stub")]: "config/accounts.ts" }, "accounts-config");
37
+ this.publishes({ [join(packageDir(), "accounts.config.stub")]: "config/accounts.ts" }, "accounts-config");
28
38
  }
29
39
  boot() {
30
40
  if (!this.config.routes.enabled)
@@ -17,7 +17,17 @@ import { BillingManager, setBilling } from "./manager.js";
17
17
  import { registerDefaultGateways } from "./drivers/index.js";
18
18
  import { billingMigration } from "./migration.js";
19
19
  import { registerBillingRoutes } from "./routes.js";
20
- const here = dirname(fileURLToPath(import.meta.url));
20
+ function packageDir() {
21
+ try {
22
+ const url = import.meta.url;
23
+ if (!url)
24
+ return ".";
25
+ return dirname(fileURLToPath(url));
26
+ }
27
+ catch {
28
+ return ".";
29
+ }
30
+ }
21
31
  export class BillingServiceProvider extends PackageProvider {
22
32
  name = "billing";
23
33
  config;
@@ -31,7 +41,7 @@ export class BillingServiceProvider extends PackageProvider {
31
41
  this.app.instance(BillingManager, this.manager);
32
42
  // Default billable table is `users`.
33
43
  this.migrations([billingMigration("users")]);
34
- this.publishes({ [join(here, "billing.config.stub")]: "config/billing.ts" }, "billing-config");
44
+ this.publishes({ [join(packageDir(), "billing.config.stub")]: "config/billing.ts" }, "billing-config");
35
45
  }
36
46
  boot() {
37
47
  this.routes((r) => registerBillingRoutes(r, this.config.webhook.path));
@@ -2,7 +2,8 @@
2
2
  * Keel Cloud MCP tools — registered only when KEEL_CLOUD_TOKEN is set.
3
3
  *
4
4
  * Talks to a Keel Cloud control plane over HTTP (Bearer token). Agents use these
5
- * to create sites, preview/publish, and manage secrets without leaving the IDE.
5
+ * to create sites, preview/publish, manage secrets/domains/billing, and export
6
+ * without leaving the IDE.
6
7
  */
7
8
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
9
  /** Register Cloud tools on an existing MCP server when a Cloud token is present. */
package/dist/mcp/cloud.js CHANGED
@@ -2,7 +2,8 @@
2
2
  * Keel Cloud MCP tools — registered only when KEEL_CLOUD_TOKEN is set.
3
3
  *
4
4
  * Talks to a Keel Cloud control plane over HTTP (Bearer token). Agents use these
5
- * to create sites, preview/publish, and manage secrets without leaving the IDE.
5
+ * to create sites, preview/publish, manage secrets/domains/billing, and export
6
+ * without leaving the IDE.
6
7
  */
7
8
  import { z } from "zod";
8
9
  const PRESETS = ["minimal", "api", "app", "saas"];
@@ -42,18 +43,49 @@ function jsonText(data, isError = false) {
42
43
  ...(isError ? { isError: true } : {}),
43
44
  };
44
45
  }
46
+ function requireConfirm(confirm, action) {
47
+ if (confirm)
48
+ return null;
49
+ return jsonText({
50
+ error: `${action} requires confirm=true. Ask the user to confirm before calling again.`,
51
+ }, true);
52
+ }
45
53
  /** Register Cloud tools on an existing MCP server when a Cloud token is present. */
46
54
  export function registerCloudTools(server) {
47
55
  if (!cloudConfig())
48
56
  return false;
49
57
  server.registerTool("keel_cloud_me", {
50
58
  title: "Keel Cloud — who am I",
51
- description: "Return the Keel Cloud user authenticated by KEEL_CLOUD_TOKEN.",
59
+ description: "Return the Keel Cloud user authenticated by KEEL_CLOUD_TOKEN (plan, site_limit, team_id). Token binds to the user's first team.",
52
60
  inputSchema: {},
53
61
  }, async () => {
54
62
  const res = await cloudFetch("/api/v1/me");
55
63
  return jsonText(res.body, !res.ok);
56
64
  });
65
+ server.registerTool("keel_cloud_billing", {
66
+ title: "Keel Cloud — billing status",
67
+ description: "Current team plan (free|pro), site limits, custom-domain eligibility, and whether the caller is the owner.",
68
+ inputSchema: {},
69
+ }, async () => {
70
+ const res = await cloudFetch("/api/v1/billing");
71
+ return jsonText(res.body, !res.ok);
72
+ });
73
+ server.registerTool("keel_cloud_billing_checkout", {
74
+ title: "Keel Cloud — Stripe checkout URL",
75
+ description: "Create a Stripe Checkout session for Keel Cloud Pro (owner only). Return the url for the user to open — do not scrape cards.",
76
+ inputSchema: {},
77
+ }, async () => {
78
+ const res = await cloudFetch("/api/v1/billing/checkout", { method: "POST", body: "{}" });
79
+ return jsonText(res.body, !res.ok);
80
+ });
81
+ server.registerTool("keel_cloud_billing_portal", {
82
+ title: "Keel Cloud — Stripe customer portal URL",
83
+ description: "Open the Stripe customer portal URL for the team (owner only) to update card / cancel. Return the url for the user.",
84
+ inputSchema: {},
85
+ }, async () => {
86
+ const res = await cloudFetch("/api/v1/billing/portal", { method: "POST", body: "{}" });
87
+ return jsonText(res.body, !res.ok);
88
+ });
57
89
  server.registerTool("keel_cloud_list_sites", {
58
90
  title: "Keel Cloud — list sites",
59
91
  description: "List sites on the current Keel Cloud team. Includes storage_path for local editing and hostnames.",
@@ -64,7 +96,7 @@ export function registerCloudTools(server) {
64
96
  });
65
97
  server.registerTool("keel_cloud_get_site", {
66
98
  title: "Keel Cloud — get site",
67
- description: "Fetch one Keel Cloud site by id (paths, hostnames, status).",
99
+ description: "Fetch one Keel Cloud site by id (paths, hostnames, status, git).",
68
100
  inputSchema: {
69
101
  site_id: z.number().int().describe("Site id from keel_cloud_list_sites"),
70
102
  },
@@ -86,6 +118,36 @@ export function registerCloudTools(server) {
86
118
  });
87
119
  return jsonText(res.body, !res.ok);
88
120
  });
121
+ server.registerTool("keel_cloud_delete_site", {
122
+ title: "Keel Cloud — soft-delete site",
123
+ description: "Soft-delete a site (detaches hostnames; recoverable within retention). Requires confirm=true.",
124
+ inputSchema: {
125
+ site_id: z.number().int().describe("Site id"),
126
+ confirm: z.boolean().describe("Must be true — ask the user before setting this"),
127
+ },
128
+ }, async ({ site_id, confirm }) => {
129
+ const blocked = requireConfirm(confirm, "Delete");
130
+ if (blocked)
131
+ return blocked;
132
+ const res = await cloudFetch(`/api/v1/sites/${site_id}/delete`, {
133
+ method: "POST",
134
+ body: JSON.stringify({ confirm: true }),
135
+ });
136
+ return jsonText(res.body, !res.ok);
137
+ });
138
+ server.registerTool("keel_cloud_restore_site", {
139
+ title: "Keel Cloud — restore soft-deleted site",
140
+ description: "Restore a soft-deleted site within the retention window. Redeploy to reattach hostnames.",
141
+ inputSchema: {
142
+ site_id: z.number().int().describe("Site id"),
143
+ },
144
+ }, async ({ site_id }) => {
145
+ const res = await cloudFetch(`/api/v1/sites/${site_id}/restore`, {
146
+ method: "POST",
147
+ body: "{}",
148
+ });
149
+ return jsonText(res.body, !res.ok);
150
+ });
89
151
  server.registerTool("keel_cloud_preview", {
90
152
  title: "Keel Cloud — deploy preview",
91
153
  description: "Deploy the site's preview Worker (and attach preview hostname when Cloudflare is configured). Safe to call freely while iterating.",
@@ -106,11 +168,9 @@ export function registerCloudTools(server) {
106
168
  .describe("Must be true to publish — ask the user before setting this"),
107
169
  },
108
170
  }, async ({ site_id, confirm }) => {
109
- if (!confirm) {
110
- return jsonText({
111
- error: "Publish requires confirm=true. Ask the user to confirm before calling again.",
112
- }, true);
113
- }
171
+ const blocked = requireConfirm(confirm, "Publish");
172
+ if (blocked)
173
+ return blocked;
114
174
  const res = await cloudFetch(`/api/v1/sites/${site_id}/publish`, {
115
175
  method: "POST",
116
176
  body: JSON.stringify({ confirm: true }),
@@ -127,6 +187,16 @@ export function registerCloudTools(server) {
127
187
  const res = await cloudFetch(`/api/v1/sites/${site_id}/deploys`);
128
188
  return jsonText(res.body, !res.ok);
129
189
  });
190
+ server.registerTool("keel_cloud_list_secrets", {
191
+ title: "Keel Cloud — list secret keys",
192
+ description: "List vault key names for a site (values are never returned).",
193
+ inputSchema: {
194
+ site_id: z.number().int().describe("Site id"),
195
+ },
196
+ }, async ({ site_id }) => {
197
+ const res = await cloudFetch(`/api/v1/sites/${site_id}/secrets`);
198
+ return jsonText(res.body, !res.ok);
199
+ });
130
200
  server.registerTool("keel_cloud_set_secret", {
131
201
  title: "Keel Cloud — set secret",
132
202
  description: "Store a secret in the site vault (never in git). Injected into the Worker on the next preview/publish.",
@@ -142,6 +212,45 @@ export function registerCloudTools(server) {
142
212
  });
143
213
  return jsonText(res.body, !res.ok);
144
214
  });
215
+ server.registerTool("keel_cloud_delete_secret", {
216
+ title: "Keel Cloud — delete secret",
217
+ description: "Remove a secret key from the site vault.",
218
+ inputSchema: {
219
+ site_id: z.number().int().describe("Site id"),
220
+ key: z.string().min(1).describe("Secret key to remove"),
221
+ },
222
+ }, async ({ site_id, key }) => {
223
+ const res = await cloudFetch(`/api/v1/sites/${site_id}/secrets?key=${encodeURIComponent(key)}`, { method: "DELETE" });
224
+ return jsonText(res.body, !res.ok);
225
+ });
226
+ server.registerTool("keel_cloud_set_custom_domain", {
227
+ title: "Keel Cloud — set custom domain",
228
+ description: "Set a Pro custom hostname (CNAME instructions returned). Optionally attach=true to verify DNS and attach to the production Worker.",
229
+ inputSchema: {
230
+ site_id: z.number().int().describe("Site id"),
231
+ hostname: z.string().min(1).describe("Customer hostname, e.g. app.example.com"),
232
+ attach: z
233
+ .boolean()
234
+ .optional()
235
+ .describe("If true, verify DNS and attach Workers Custom Domain now"),
236
+ },
237
+ }, async ({ site_id, hostname, attach }) => {
238
+ const res = await cloudFetch(`/api/v1/sites/${site_id}/custom-domain`, {
239
+ method: "PUT",
240
+ body: JSON.stringify({ hostname, attach: Boolean(attach) }),
241
+ });
242
+ return jsonText(res.body, !res.ok);
243
+ });
244
+ server.registerTool("keel_cloud_clear_custom_domain", {
245
+ title: "Keel Cloud — clear custom domain",
246
+ description: "Detach and clear the site's custom hostname (Pro).",
247
+ inputSchema: {
248
+ site_id: z.number().int().describe("Site id"),
249
+ },
250
+ }, async ({ site_id }) => {
251
+ const res = await cloudFetch(`/api/v1/sites/${site_id}/custom-domain`, { method: "DELETE" });
252
+ return jsonText(res.body, !res.ok);
253
+ });
145
254
  server.registerTool("keel_cloud_export", {
146
255
  title: "Keel Cloud — export manifest",
147
256
  description: "Return the site's export manifest (paths, hostnames, Keel version, SQL dump URLs). Use storage_path / git_url for code; use keel_cloud_export_sql for data.",
@@ -190,12 +190,14 @@ export async function createServer() {
190
190
  ? [
191
191
  "",
192
192
  "## Keel Cloud (token detected)",
193
- "Cloud tools are enabled. Typical loop:",
193
+ "Cloud tools are enabled. Token binds to the user's first team.",
194
+ "Typical loop:",
194
195
  "1. keel_cloud_create_site { name, preset }",
195
196
  "2. Edit files at the returned storage_path (real Keel app)",
196
197
  "3. keel_cloud_preview { site_id }",
197
198
  "4. keel_cloud_publish { site_id, confirm: true } after user approval",
198
- "Also: keel_cloud_list_sites, keel_cloud_get_site, keel_cloud_set_secret, keel_cloud_deploys, keel_cloud_export.",
199
+ "Also: me, billing(+checkout/portal), list/get/delete/restore sites,",
200
+ "secrets (list/set/delete), custom domain (set/clear), deploys, export(+sql).",
199
201
  ]
200
202
  : []),
201
203
  ].join("\n");
@@ -14,7 +14,17 @@ import { dirname, join } from "node:path";
14
14
  import { PackageProvider } from "../core/package.js";
15
15
  import { defaultConfig, resolveConfig } from "./config.js";
16
16
  import { teamsMigration } from "./migration.js";
17
- const here = dirname(fileURLToPath(import.meta.url));
17
+ function packageDir() {
18
+ try {
19
+ const url = import.meta.url;
20
+ if (!url)
21
+ return ".";
22
+ return dirname(fileURLToPath(url));
23
+ }
24
+ catch {
25
+ return ".";
26
+ }
27
+ }
18
28
  export class TeamsServiceProvider extends PackageProvider {
19
29
  name = "teams";
20
30
  config;
@@ -22,6 +32,6 @@ export class TeamsServiceProvider extends PackageProvider {
22
32
  this.mergeConfig("teams", defaultConfig);
23
33
  this.config = resolveConfig();
24
34
  this.migrations([teamsMigration(this.config.userTable)]);
25
- this.publishes({ [join(here, "teams.config.stub")]: "config/teams.ts" }, "teams-config");
35
+ this.publishes({ [join(packageDir(), "teams.config.stub")]: "config/teams.ts" }, "teams-config");
26
36
  }
27
37
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shaferllc/keel",
3
- "version": "0.83.4",
3
+ "version": "0.83.5",
4
4
  "description": "The house framework for Node.js — a service container, providers, routing, JSX views, and a code-generating console.",
5
5
  "repo": "https://github.com/shaferllc/keel",
6
6
  "generated": "run `npm run build:ai` to regenerate",
package/docs/ai.md CHANGED
@@ -62,18 +62,23 @@ package, so it always matches your installed version.
62
62
  | `keel_scaffold` | Generate a controller/provider/middleware/factory/seeder/job/notification/transformer stub. Returns code + target path — it does **not** write to disk. |
63
63
 
64
64
  When `KEEL_CLOUD_TOKEN` (and optional `KEEL_CLOUD_URL`) is set, Cloud tools are
65
- also registered:
65
+ also registered. Create a token at `/tokens` on the control plane — the plaintext
66
+ looks like `keel_<selector>.<verifier>`.
66
67
 
67
68
  | Tool | What it does |
68
69
  |------|--------------|
69
- | `keel_cloud_me` | Authenticated Cloud user |
70
- | `keel_cloud_list_sites` / `keel_cloud_get_site` | List or fetch a site (includes `storage_path`) |
70
+ | `keel_cloud_me` | Authenticated Cloud user (plan, site limit, team) |
71
+ | `keel_cloud_billing` | Team plan / limits / owner flag |
72
+ | `keel_cloud_billing_checkout` / `_portal` | Stripe Checkout or Customer Portal URL (owner) |
73
+ | `keel_cloud_list_sites` / `keel_cloud_get_site` | List or fetch a site (`storage_path`, hostnames, git) |
71
74
  | `keel_cloud_create_site` | Create from preset (`minimal` \| `api` \| `app` \| `saas`) |
75
+ | `keel_cloud_delete_site` / `keel_cloud_restore_site` | Soft-delete (confirm) / restore |
72
76
  | `keel_cloud_preview` | Deploy preview Worker |
73
77
  | `keel_cloud_publish` | Publish production — requires `confirm: true` |
74
- | `keel_cloud_set_secret` | Vault a secret (injected on next deploy) |
75
- | `keel_cloud_deploys` / `keel_cloud_export` | Deploy history and export manifest |
76
- | `keel_cloud_export_sql` | Portable `.sql` dump (`local` \| `preview` \| `production`) |
78
+ | `keel_cloud_deploys` | Deploy history + logs |
79
+ | `keel_cloud_list_secrets` / `keel_cloud_set_secret` / `keel_cloud_delete_secret` | Vault keys (values never returned) |
80
+ | `keel_cloud_set_custom_domain` / `keel_cloud_clear_custom_domain` | Pro custom hostname (+ optional attach) |
81
+ | `keel_cloud_export` / `keel_cloud_export_sql` | Export manifest / portable `.sql` dump |
77
82
 
78
83
  ```json
79
84
  {
@@ -82,7 +87,7 @@ also registered:
82
87
  "command": "npx",
83
88
  "args": ["-y", "keel-mcp"],
84
89
  "env": {
85
- "KEEL_CLOUD_TOKEN": "kc_…",
90
+ "KEEL_CLOUD_TOKEN": "keel_….…",
86
91
  "KEEL_CLOUD_URL": "https://app.keeljs.cloud"
87
92
  }
88
93
  }
@@ -90,6 +95,8 @@ also registered:
90
95
  }
91
96
  ```
92
97
 
98
+ The token binds to the user's **first team**. Switch teams in the dashboard before
99
+ minting a token if you need a different team context.
93
100
  ### Resources
94
101
 
95
102
  - `keel://overview` — the same orientation text as `keel_overview`
@@ -108,8 +115,11 @@ also registered:
108
115
 
109
116
  1. `keel_cloud_create_site { name: "Acme", preset: "app" }`
110
117
  2. Edit the returned `storage_path` (real Keel app + git)
111
- 3. `keel_cloud_preview { site_id }`
112
- 4. `keel_cloud_publish { site_id, confirm: true }` after the user approves
118
+ 3. `keel_cloud_set_secret` for env the Worker needs
119
+ 4. `keel_cloud_preview { site_id }`
120
+ 5. `keel_cloud_publish { site_id, confirm: true }` after the user approves
121
+ 6. Optional Pro: `keel_cloud_set_custom_domain { hostname, attach: true }`
122
+ 7. Escape hatch anytime: `keel_cloud_export` + `keel_cloud_export_sql`
113
123
 
114
124
  ## `llms.txt` and `llms-full.txt`
115
125
 
package/docs/changelog.md CHANGED
@@ -4,6 +4,17 @@ All notable changes to Keel are documented here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project aims to
5
5
  adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.83.5] — 2026-07-13
8
+
9
+ ### Added
10
+
11
+ - **Full Keel Cloud MCP surface** — billing status/checkout/portal, soft-delete
12
+ and restore sites, list/delete secrets, set/clear custom domains. Documented in
13
+ [Building with AI](./docs/ai.md). Pairs with expanded `/api/v1` routes on Keel
14
+ Cloud.
15
+
16
+ [0.83.5]: https://github.com/shaferllc/keel/releases/tag/v0.83.5
17
+
7
18
  ## [0.83.4] — 2026-07-13
8
19
 
9
20
  ### Added
package/llms-full.txt CHANGED
@@ -4726,18 +4726,23 @@ package, so it always matches your installed version.
4726
4726
  | `keel_scaffold` | Generate a controller/provider/middleware/factory/seeder/job/notification/transformer stub. Returns code + target path — it does **not** write to disk. |
4727
4727
 
4728
4728
  When `KEEL_CLOUD_TOKEN` (and optional `KEEL_CLOUD_URL`) is set, Cloud tools are
4729
- also registered:
4729
+ also registered. Create a token at `/tokens` on the control plane — the plaintext
4730
+ looks like `keel_<selector>.<verifier>`.
4730
4731
 
4731
4732
  | Tool | What it does |
4732
4733
  |------|--------------|
4733
- | `keel_cloud_me` | Authenticated Cloud user |
4734
- | `keel_cloud_list_sites` / `keel_cloud_get_site` | List or fetch a site (includes `storage_path`) |
4734
+ | `keel_cloud_me` | Authenticated Cloud user (plan, site limit, team) |
4735
+ | `keel_cloud_billing` | Team plan / limits / owner flag |
4736
+ | `keel_cloud_billing_checkout` / `_portal` | Stripe Checkout or Customer Portal URL (owner) |
4737
+ | `keel_cloud_list_sites` / `keel_cloud_get_site` | List or fetch a site (`storage_path`, hostnames, git) |
4735
4738
  | `keel_cloud_create_site` | Create from preset (`minimal` \| `api` \| `app` \| `saas`) |
4739
+ | `keel_cloud_delete_site` / `keel_cloud_restore_site` | Soft-delete (confirm) / restore |
4736
4740
  | `keel_cloud_preview` | Deploy preview Worker |
4737
4741
  | `keel_cloud_publish` | Publish production — requires `confirm: true` |
4738
- | `keel_cloud_set_secret` | Vault a secret (injected on next deploy) |
4739
- | `keel_cloud_deploys` / `keel_cloud_export` | Deploy history and export manifest |
4740
- | `keel_cloud_export_sql` | Portable `.sql` dump (`local` \| `preview` \| `production`) |
4742
+ | `keel_cloud_deploys` | Deploy history + logs |
4743
+ | `keel_cloud_list_secrets` / `keel_cloud_set_secret` / `keel_cloud_delete_secret` | Vault keys (values never returned) |
4744
+ | `keel_cloud_set_custom_domain` / `keel_cloud_clear_custom_domain` | Pro custom hostname (+ optional attach) |
4745
+ | `keel_cloud_export` / `keel_cloud_export_sql` | Export manifest / portable `.sql` dump |
4741
4746
 
4742
4747
  ```json
4743
4748
  {
@@ -4746,7 +4751,7 @@ also registered:
4746
4751
  "command": "npx",
4747
4752
  "args": ["-y", "keel-mcp"],
4748
4753
  "env": {
4749
- "KEEL_CLOUD_TOKEN": "kc_…",
4754
+ "KEEL_CLOUD_TOKEN": "keel_….…",
4750
4755
  "KEEL_CLOUD_URL": "https://app.keeljs.cloud"
4751
4756
  }
4752
4757
  }
@@ -4754,6 +4759,8 @@ also registered:
4754
4759
  }
4755
4760
  ```
4756
4761
 
4762
+ The token binds to the user's **first team**. Switch teams in the dashboard before
4763
+ minting a token if you need a different team context.
4757
4764
  ### Resources
4758
4765
 
4759
4766
  - `keel://overview` — the same orientation text as `keel_overview`
@@ -4772,8 +4779,11 @@ also registered:
4772
4779
 
4773
4780
  1. `keel_cloud_create_site { name: "Acme", preset: "app" }`
4774
4781
  2. Edit the returned `storage_path` (real Keel app + git)
4775
- 3. `keel_cloud_preview { site_id }`
4776
- 4. `keel_cloud_publish { site_id, confirm: true }` after the user approves
4782
+ 3. `keel_cloud_set_secret` for env the Worker needs
4783
+ 4. `keel_cloud_preview { site_id }`
4784
+ 5. `keel_cloud_publish { site_id, confirm: true }` after the user approves
4785
+ 6. Optional Pro: `keel_cloud_set_custom_domain { hostname, attach: true }`
4786
+ 7. Escape hatch anytime: `keel_cloud_export` + `keel_cloud_export_sql`
4777
4787
 
4778
4788
  ## `llms.txt` and `llms-full.txt`
4779
4789
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shaferllc/keel",
3
- "version": "0.83.4",
3
+ "version": "0.83.5",
4
4
  "type": "module",
5
5
  "description": "The house framework for Node.js — a service container, providers, routing, JSX views, and a code-generating console.",
6
6
  "license": "MIT",