@butterbase/cli 0.2.2 → 0.3.3
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 +118 -0
- package/dist/bin/butterbase.js +367 -1
- package/dist/bin/butterbase.js.map +1 -1
- package/dist/src/commands/ai.d.ts +36 -0
- package/dist/src/commands/ai.d.ts.map +1 -0
- package/dist/src/commands/ai.js +131 -0
- package/dist/src/commands/ai.js.map +1 -0
- package/dist/src/commands/app-billing.d.ts +62 -0
- package/dist/src/commands/app-billing.d.ts.map +1 -0
- package/dist/src/commands/app-billing.js +220 -0
- package/dist/src/commands/app-billing.js.map +1 -0
- package/dist/src/commands/app-config.d.ts +37 -0
- package/dist/src/commands/app-config.d.ts.map +1 -0
- package/dist/src/commands/app-config.js +152 -0
- package/dist/src/commands/app-config.js.map +1 -0
- package/dist/src/commands/audit.d.ts +15 -0
- package/dist/src/commands/audit.d.ts.map +1 -0
- package/dist/src/commands/audit.js +49 -0
- package/dist/src/commands/audit.js.map +1 -0
- package/dist/src/commands/do.js +1 -1
- package/dist/src/commands/do.js.map +1 -1
- package/dist/src/commands/functions.d.ts +4 -0
- package/dist/src/commands/functions.d.ts.map +1 -1
- package/dist/src/commands/functions.js +28 -2
- package/dist/src/commands/functions.js.map +1 -1
- package/dist/src/commands/integrations.d.ts +2 -0
- package/dist/src/commands/integrations.d.ts.map +1 -1
- package/dist/src/commands/integrations.js +2 -2
- package/dist/src/commands/integrations.js.map +1 -1
- package/dist/src/commands/keys.d.ts +1 -0
- package/dist/src/commands/keys.d.ts.map +1 -1
- package/dist/src/commands/keys.js +1 -1
- package/dist/src/commands/keys.js.map +1 -1
- package/dist/src/commands/kv.d.ts +56 -0
- package/dist/src/commands/kv.d.ts.map +1 -0
- package/dist/src/commands/kv.js +189 -0
- package/dist/src/commands/kv.js.map +1 -0
- package/dist/src/commands/move.d.ts +25 -0
- package/dist/src/commands/move.d.ts.map +1 -0
- package/dist/src/commands/move.js +157 -0
- package/dist/src/commands/move.js.map +1 -0
- package/dist/src/commands/oauth.d.ts +33 -0
- package/dist/src/commands/oauth.d.ts.map +1 -0
- package/dist/src/commands/oauth.js +122 -0
- package/dist/src/commands/oauth.js.map +1 -0
- package/dist/src/commands/regions.d.ts +4 -0
- package/dist/src/commands/regions.d.ts.map +1 -0
- package/dist/src/commands/regions.js +16 -0
- package/dist/src/commands/regions.js.map +1 -0
- package/dist/src/commands/rls.d.ts +1 -0
- package/dist/src/commands/rls.d.ts.map +1 -1
- package/dist/src/commands/rls.js +1 -0
- package/dist/src/commands/rls.js.map +1 -1
- package/dist/src/lib/api-client.d.ts +46 -3
- package/dist/src/lib/api-client.d.ts.map +1 -1
- package/dist/src/lib/api-client.js +167 -9
- package/dist/src/lib/api-client.js.map +1 -1
- package/dist/src/lib/errors.d.ts +8 -0
- package/dist/src/lib/errors.d.ts.map +1 -0
- package/dist/src/lib/errors.js +24 -0
- package/dist/src/lib/errors.js.map +1 -0
- package/dist/src/lib/kv-api.d.ts +15 -0
- package/dist/src/lib/kv-api.d.ts.map +1 -0
- package/dist/src/lib/kv-api.js +14 -0
- package/dist/src/lib/kv-api.js.map +1 -0
- package/dist/src/lib/load-kv-config.d.ts +14 -0
- package/dist/src/lib/load-kv-config.d.ts.map +1 -0
- package/dist/src/lib/load-kv-config.js +27 -0
- package/dist/src/lib/load-kv-config.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +16 -4
- package/templates/react-vite/.mcp.json +10 -10
package/README.md
CHANGED
|
@@ -139,6 +139,108 @@ butterbase storage upload ./image.png
|
|
|
139
139
|
butterbase storage delete obj_abc123
|
|
140
140
|
```
|
|
141
141
|
|
|
142
|
+
### AI
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
butterbase ai chat "Summarize this" --model openai/gpt-4o-mini
|
|
146
|
+
butterbase ai chat "Explain RAG" --system "You're a concise teacher." --temperature 0.2
|
|
147
|
+
butterbase ai embed "hello world" "another doc"
|
|
148
|
+
butterbase ai models
|
|
149
|
+
butterbase ai config get
|
|
150
|
+
butterbase ai config set --default-model openai/gpt-4o-mini --max-tokens-per-request 4000
|
|
151
|
+
butterbase ai config set --byok-key sk-or-... # rotate BYOK key; "" to clear
|
|
152
|
+
butterbase ai usage --start-date 2026-05-01 --end-date 2026-05-31
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### OAuth (admin)
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
butterbase oauth configure google \
|
|
159
|
+
--client-id ... --client-secret ... \
|
|
160
|
+
--redirect-uri https://app.example/cb \
|
|
161
|
+
--scope openid --scope email
|
|
162
|
+
butterbase oauth list
|
|
163
|
+
butterbase oauth get google
|
|
164
|
+
butterbase oauth update google --enabled false
|
|
165
|
+
butterbase oauth delete google
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Audit logs
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
butterbase audit query --category auth --event-type login --limit 50
|
|
172
|
+
butterbase audit query --from 2026-05-01 --to 2026-05-31 --action create --resource-type user
|
|
173
|
+
butterbase audit query --actor-id user_123 --json
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### App config (server-side)
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
butterbase apps config get
|
|
180
|
+
butterbase apps config cors --allowed-origin https://app.example --allow-credentials true
|
|
181
|
+
butterbase apps config jwt --access-token-ttl 15m --refresh-token-ttl-days 30
|
|
182
|
+
butterbase apps config storage --public-read true --max-file-size-mb 25 --allowed-content-type image/png --allowed-content-type image/jpeg
|
|
183
|
+
butterbase apps config access-mode authenticated
|
|
184
|
+
butterbase apps config secure --table posts --table comments --user-column author_id --access-mode authenticated
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Regions + multi-region moves
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
butterbase regions list
|
|
191
|
+
butterbase apps move app_abc us-west-2 --follow
|
|
192
|
+
butterbase apps migrations active # current app
|
|
193
|
+
butterbase apps migrations status app_abc m_xyz
|
|
194
|
+
butterbase apps migrations abort app_abc m_xyz # before cutover
|
|
195
|
+
butterbase apps migrations reverse app_abc m_xyz # after cutover
|
|
196
|
+
butterbase apps replicas list
|
|
197
|
+
butterbase apps replicas teardown m_xyz
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### App-level billing (Stripe Connect)
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
butterbase app-billing plans list
|
|
204
|
+
butterbase app-billing plans create --name pro --price-cents 1999 --interval month
|
|
205
|
+
butterbase app-billing plans update plan_abc --price-cents 2499
|
|
206
|
+
butterbase app-billing products list
|
|
207
|
+
butterbase app-billing products create --name "Lifetime access" --price-cents 9900
|
|
208
|
+
butterbase app-billing subscribe plan_abc
|
|
209
|
+
butterbase app-billing subscription
|
|
210
|
+
butterbase app-billing cancel
|
|
211
|
+
butterbase app-billing purchase prod_xyz
|
|
212
|
+
butterbase app-billing orders list
|
|
213
|
+
butterbase app-billing orders get order_abc
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Scoped API keys + integrations
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
butterbase keys generate ci-key --scope schema:read --scope functions:invoke
|
|
220
|
+
butterbase integrations configure github --scope repo --scope read:user
|
|
221
|
+
butterbase integrations connect github --redirect-url https://app.example/cb --scope repo
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Functions deploy (full options)
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
butterbase functions deploy fn.ts \
|
|
228
|
+
--name my-fn \
|
|
229
|
+
--trigger cron --trigger-config '{"schedule":"*/5 * * * *"}' \
|
|
230
|
+
--env API_KEY=sk_... --env DEBUG=true \
|
|
231
|
+
--timeout-ms 9000 --memory-mb 256
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### RLS
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
butterbase rls create --table posts --policy-name posts_own \
|
|
238
|
+
--command SELECT --using "author_id = auth.uid()" \
|
|
239
|
+
--role user --restrictive
|
|
240
|
+
butterbase rls delete posts # delete all policies on table
|
|
241
|
+
butterbase rls delete posts --policy posts_own
|
|
242
|
+
```
|
|
243
|
+
|
|
142
244
|
## Global Options
|
|
143
245
|
|
|
144
246
|
Most commands support the `--app` flag to specify an app ID:
|
|
@@ -284,6 +386,22 @@ butterbase integrations tools gmail --app app_abc123
|
|
|
284
386
|
butterbase integrations execute GMAIL_SEND_EMAIL --data '{"to":"x@y.com","subject":"Hi","body":"Hello"}' --app app_abc123
|
|
285
387
|
```
|
|
286
388
|
|
|
389
|
+
## Error output
|
|
390
|
+
|
|
391
|
+
The CLI throws typed `ButterbaseError`s from `@butterbase/sdk`. The top-level
|
|
392
|
+
handler renders the class name, message, and the structured fields the backend
|
|
393
|
+
returned (`code`, `status`, `remediation`). Example for an unauthenticated call:
|
|
394
|
+
|
|
395
|
+
```
|
|
396
|
+
AuthError: Invalid API key
|
|
397
|
+
code: AUTH_INVALID_API_KEY
|
|
398
|
+
status: 401
|
|
399
|
+
remediation: Rotate the key with `butterbase keys generate` and update ~/.butterbase/config.json.
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
The error codes come from `@butterbase/shared`'s `ErrorCodes` namespace — see
|
|
403
|
+
the SDK README for the full list.
|
|
404
|
+
|
|
287
405
|
## License
|
|
288
406
|
|
|
289
407
|
MIT
|
package/dist/bin/butterbase.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
|
+
import { renderError } from '../src/lib/errors.js';
|
|
3
4
|
import { initCommand } from '../src/commands/init.js';
|
|
4
5
|
import { loginCommand, logoutCommand, configGetCommand, configSetCommand } from '../src/commands/config.js';
|
|
5
6
|
import { appsListCommand, appsCreateCommand, appsUseCommand, appsDeleteCommand, appsPauseCommand, appsResumeCommand } from '../src/commands/apps.js';
|
|
@@ -24,6 +25,14 @@ import { rlsListCommand, rlsCreateCommand, rlsEnableCommand, rlsDeleteCommand }
|
|
|
24
25
|
import { billingStatusCommand, billingPortalCommand, billingTopupCommand, billingCapGetCommand, billingCapRaiseCommand, billingPlansCommand, billingUsageCommand, } from '../src/commands/billing.js';
|
|
25
26
|
import { ragCollectionsListCommand, ragCollectionsCreateCommand, ragCollectionsGetCommand, ragCollectionsDeleteCommand, ragIngestCommand, ragDocsListCommand, ragDocsDeleteCommand, ragQueryCommand, } from '../src/commands/rag.js';
|
|
26
27
|
import { doDeployCommand, doListCommand, doGetCommand, doDeleteCommand, doUsageCommand, doEnvListCommand, doEnvSetCommand, doEnvUnsetCommand, } from '../src/commands/do.js';
|
|
28
|
+
import { aiChatCommand, aiEmbedCommand, aiModelsCommand, aiConfigGetCommand, aiConfigSetCommand, aiUsageCommand, } from '../src/commands/ai.js';
|
|
29
|
+
import { oauthConfigureCommand, oauthListCommand, oauthGetCommand, oauthUpdateCommand, oauthDeleteCommand, } from '../src/commands/oauth.js';
|
|
30
|
+
import { auditQueryCommand } from '../src/commands/audit.js';
|
|
31
|
+
import { appConfigGetCommand, appCorsCommand, appJwtCommand, appStorageCommand, appAccessModeCommand, appSecureCommand, } from '../src/commands/app-config.js';
|
|
32
|
+
import { regionsListCommand } from '../src/commands/regions.js';
|
|
33
|
+
import { moveCommand, migrationStatusCommand, migrationActiveCommand, migrationAbortCommand, migrationReverseCommand, replicasListCommand, replicaTeardownCommand, } from '../src/commands/move.js';
|
|
34
|
+
import { plansListCommand, plansCreateCommand, plansUpdateCommand, productsListCommand, productsCreateCommand, productsUpdateCommand, subscribeCommand, subscriptionCommand, cancelCommand, purchaseCommand, ordersListCommand, ordersGetCommand, } from '../src/commands/app-billing.js';
|
|
35
|
+
import { kvGetCommand, kvSetCommand, kvDelCommand, kvLsCommand, kvStatsCommand, kvFlushCommand, kvRulesCommand, kvExposeCommand, kvUnexposeCommand, kvApplyCommand, } from '../src/commands/kv.js';
|
|
27
36
|
const program = new Command();
|
|
28
37
|
program
|
|
29
38
|
.name('butterbase')
|
|
@@ -80,6 +89,94 @@ apps
|
|
|
80
89
|
.command('resume [app-id]')
|
|
81
90
|
.description('Resume a paused app — restore data-plane traffic')
|
|
82
91
|
.action(appsResumeCommand);
|
|
92
|
+
const appsConfig = apps.command('config').description('Read or update the app\'s server-side config');
|
|
93
|
+
appsConfig
|
|
94
|
+
.command('get')
|
|
95
|
+
.description('Show the app\'s full config')
|
|
96
|
+
.option('--app <appId>', 'Override current app')
|
|
97
|
+
.option('--json', 'Output raw JSON')
|
|
98
|
+
.action((opts) => appConfigGetCommand(opts));
|
|
99
|
+
appsConfig
|
|
100
|
+
.command('cors')
|
|
101
|
+
.description('Update CORS config')
|
|
102
|
+
.option('--app <appId>', 'Override current app')
|
|
103
|
+
.option('--allowed-origin <origin>', 'Allowed origin (repeatable)', (v, prev) => prev.concat(v), [])
|
|
104
|
+
.option('--allowed-method <method>', 'Allowed method (repeatable)', (v, prev) => prev.concat(v), [])
|
|
105
|
+
.option('--allowed-header <header>', 'Allowed header (repeatable)', (v, prev) => prev.concat(v), [])
|
|
106
|
+
.option('--allow-credentials <bool>', '(true|false)', (v) => v === 'true')
|
|
107
|
+
.option('--json', 'Output raw JSON')
|
|
108
|
+
.action((opts) => appCorsCommand(opts));
|
|
109
|
+
appsConfig
|
|
110
|
+
.command('jwt')
|
|
111
|
+
.description('Update JWT TTLs')
|
|
112
|
+
.option('--app <appId>', 'Override current app')
|
|
113
|
+
.option('--access-token-ttl <duration>', 'e.g. "15m", "1h"')
|
|
114
|
+
.option('--refresh-token-ttl-days <n>', 'Refresh token lifetime in days', parseInt)
|
|
115
|
+
.option('--json', 'Output raw JSON')
|
|
116
|
+
.action((opts) => appJwtCommand(opts));
|
|
117
|
+
appsConfig
|
|
118
|
+
.command('storage')
|
|
119
|
+
.description('Update storage config')
|
|
120
|
+
.option('--app <appId>', 'Override current app')
|
|
121
|
+
.option('--public-read <bool>', 'Public-read default (true|false)', (v) => v === 'true')
|
|
122
|
+
.option('--max-file-size-mb <n>', 'Per-file size cap in MB', parseInt)
|
|
123
|
+
.option('--allowed-content-type <ct>', 'Allowed content-type (repeatable)', (v, prev) => prev.concat(v), [])
|
|
124
|
+
.option('--json', 'Output raw JSON')
|
|
125
|
+
.action((opts) => appStorageCommand(opts));
|
|
126
|
+
appsConfig
|
|
127
|
+
.command('access-mode <mode>')
|
|
128
|
+
.description('Set access mode: public | authenticated')
|
|
129
|
+
.option('--app <appId>', 'Override current app')
|
|
130
|
+
.option('--json', 'Output raw JSON')
|
|
131
|
+
.action((mode, opts) => appAccessModeCommand(mode, opts));
|
|
132
|
+
appsConfig
|
|
133
|
+
.command('secure')
|
|
134
|
+
.description('Enable RLS + access-mode in one shot')
|
|
135
|
+
.option('--app <appId>', 'Override current app')
|
|
136
|
+
.option('--table <name>', 'Table to secure (repeatable)', (v, prev) => prev.concat(v), [])
|
|
137
|
+
.option('--user-column <col>', 'User-id column name (default: user_id)')
|
|
138
|
+
.option('--access-mode <mode>', 'public | authenticated')
|
|
139
|
+
.option('--json', 'Output raw JSON')
|
|
140
|
+
.action((opts) => appSecureCommand(opts));
|
|
141
|
+
apps
|
|
142
|
+
.command('move <appId> <destRegion>')
|
|
143
|
+
.description('Migrate an app to another region')
|
|
144
|
+
.option('--follow', 'Poll status until terminal')
|
|
145
|
+
.option('--json', 'Output raw JSON')
|
|
146
|
+
.action((appId, destRegion, opts) => moveCommand(appId, destRegion, opts));
|
|
147
|
+
const appsMigrations = apps.command('migrations').description('Read or control in-flight migrations');
|
|
148
|
+
appsMigrations
|
|
149
|
+
.command('status <appId> <migrationId>')
|
|
150
|
+
.description('Get status of a specific migration')
|
|
151
|
+
.option('--json', 'Output raw JSON')
|
|
152
|
+
.action((appId, migrationId, opts) => migrationStatusCommand(appId, migrationId, opts));
|
|
153
|
+
appsMigrations
|
|
154
|
+
.command('active [appId]')
|
|
155
|
+
.description('Show the currently-active migration for an app')
|
|
156
|
+
.option('--app <appId>', 'Override current app')
|
|
157
|
+
.option('--json', 'Output raw JSON')
|
|
158
|
+
.action((appId, opts) => migrationActiveCommand(appId, opts));
|
|
159
|
+
appsMigrations
|
|
160
|
+
.command('abort <appId> <migrationId>')
|
|
161
|
+
.description('Cancel a migration that has not yet reached cutover')
|
|
162
|
+
.option('--json', 'Output raw JSON')
|
|
163
|
+
.action((appId, migrationId, opts) => migrationAbortCommand(appId, migrationId, opts));
|
|
164
|
+
appsMigrations
|
|
165
|
+
.command('reverse <appId> <migrationId>')
|
|
166
|
+
.description('Roll a completed migration back to source')
|
|
167
|
+
.option('--json', 'Output raw JSON')
|
|
168
|
+
.action((appId, migrationId, opts) => migrationReverseCommand(appId, migrationId, opts));
|
|
169
|
+
const appsReplicas = apps.command('replicas').description('Manage retained source replicas after a move');
|
|
170
|
+
appsReplicas
|
|
171
|
+
.command('list')
|
|
172
|
+
.description('List active retained source replicas')
|
|
173
|
+
.option('--json', 'Output raw JSON')
|
|
174
|
+
.action((opts) => replicasListCommand(opts));
|
|
175
|
+
appsReplicas
|
|
176
|
+
.command('teardown <migrationId>')
|
|
177
|
+
.description('Decommission a retained source replica')
|
|
178
|
+
.option('--json', 'Output raw JSON')
|
|
179
|
+
.action((migrationId, opts) => replicaTeardownCommand(migrationId, opts));
|
|
83
180
|
// Schema
|
|
84
181
|
const schema = program.command('schema').description('Manage database schema');
|
|
85
182
|
schema
|
|
@@ -107,8 +204,12 @@ functions
|
|
|
107
204
|
.description('Deploy a function')
|
|
108
205
|
.option('--app <app-id>', 'App ID (uses current app if not specified)')
|
|
109
206
|
.option('--name <name>', 'Function name (defaults to filename)')
|
|
110
|
-
.option('--trigger <type>', 'Trigger type (http, cron)', 'http')
|
|
207
|
+
.option('--trigger <type>', 'Trigger type (http, cron, s3_upload, webhook, websocket)', 'http')
|
|
208
|
+
.option('--trigger-config <json>', 'Trigger config as JSON (e.g. \'{"schedule":"*/5 * * * *"}\')')
|
|
111
209
|
.option('--description <desc>', 'Function description')
|
|
210
|
+
.option('--env <kv>', 'Env var as KEY=value (repeatable)', (v, prev) => prev.concat(v), [])
|
|
211
|
+
.option('--timeout-ms <n>', 'Per-invocation timeout (ms)', parseInt)
|
|
212
|
+
.option('--memory-mb <n>', 'Memory limit (MB)', parseInt)
|
|
112
213
|
.action(functionsDeployCommand);
|
|
113
214
|
functions
|
|
114
215
|
.command('logs <function-name>')
|
|
@@ -287,6 +388,7 @@ const keys = program.command('keys').description('Manage API keys');
|
|
|
287
388
|
keys
|
|
288
389
|
.command('generate [name]')
|
|
289
390
|
.description('Generate a new API key')
|
|
391
|
+
.option('--scope <scope>', 'Add a scope (repeatable)', (v, prev) => prev.concat(v), [])
|
|
290
392
|
.option('--json', 'Output as JSON')
|
|
291
393
|
.action(keysGenerateCommand);
|
|
292
394
|
keys
|
|
@@ -322,6 +424,7 @@ integrations
|
|
|
322
424
|
.description('Enable a toolkit for the app')
|
|
323
425
|
.option('--app <app-id>', 'App ID')
|
|
324
426
|
.option('--display-name <name>', 'Human-readable display name')
|
|
427
|
+
.option('--scope <scope>', 'Add a scope (repeatable)', (v, prev) => prev.concat(v), [])
|
|
325
428
|
.action(integrationsConfigureCommand);
|
|
326
429
|
integrations
|
|
327
430
|
.command('disable <toolkit>')
|
|
@@ -334,6 +437,7 @@ integrations
|
|
|
334
437
|
.option('--app <app-id>', 'App ID')
|
|
335
438
|
.option('--redirect-url <url>', 'URL to redirect after OAuth')
|
|
336
439
|
.option('--user-id <uuid>', 'User ID (for API key auth)')
|
|
440
|
+
.option('--scope <scope>', 'Add a scope (repeatable)', (v, prev) => prev.concat(v), [])
|
|
337
441
|
.action(integrationsConnectCommand);
|
|
338
442
|
integrations
|
|
339
443
|
.command('connections')
|
|
@@ -474,6 +578,7 @@ rls
|
|
|
474
578
|
.option('--using <expr>', 'USING expression')
|
|
475
579
|
.option('--with-check <expr>', 'WITH CHECK expression')
|
|
476
580
|
.option('--restrictive', 'Create as RESTRICTIVE policy')
|
|
581
|
+
.option('--role <role>', 'Restrict to a Postgres role (anon | user)')
|
|
477
582
|
.option('--json', 'Output as JSON')
|
|
478
583
|
.action(rlsCreateCommand);
|
|
479
584
|
rls
|
|
@@ -591,6 +696,267 @@ rag
|
|
|
591
696
|
.option('--model <model>', 'LLM model to use for synthesis')
|
|
592
697
|
.option('--json', 'Output as JSON')
|
|
593
698
|
.action(ragQueryCommand);
|
|
699
|
+
// AI Gateway
|
|
700
|
+
const ai = program.command('ai').description('Use the app\'s AI gateway (chat, embeddings, models, BYOK, usage)');
|
|
701
|
+
ai
|
|
702
|
+
.command('chat <prompt>')
|
|
703
|
+
.description('Send a single-turn chat completion')
|
|
704
|
+
.option('--app <appId>', 'Override current app')
|
|
705
|
+
.option('--model <model>', 'Model id (default: app default)')
|
|
706
|
+
.option('--temperature <n>', 'Sampling temperature', parseFloat)
|
|
707
|
+
.option('--max-tokens <n>', 'Max output tokens', parseInt)
|
|
708
|
+
.option('--system <message>', 'Prepend a system message')
|
|
709
|
+
.option('--json', 'Output raw JSON')
|
|
710
|
+
.action((prompt, opts) => aiChatCommand(prompt, opts));
|
|
711
|
+
ai
|
|
712
|
+
.command('embed <input...>')
|
|
713
|
+
.description('Embed text(s) into vectors')
|
|
714
|
+
.option('--app <appId>', 'Override current app')
|
|
715
|
+
.option('--model <model>', 'Embedding model')
|
|
716
|
+
.option('--json', 'Output raw JSON')
|
|
717
|
+
.action((input, opts) => aiEmbedCommand(input, opts));
|
|
718
|
+
ai
|
|
719
|
+
.command('models')
|
|
720
|
+
.description('List available AI models')
|
|
721
|
+
.option('--app <appId>', 'Override current app')
|
|
722
|
+
.option('--json', 'Output raw JSON')
|
|
723
|
+
.action((opts) => aiModelsCommand(opts));
|
|
724
|
+
const aiConfig = ai.command('config').description('Read or update AI config');
|
|
725
|
+
aiConfig
|
|
726
|
+
.command('get')
|
|
727
|
+
.option('--app <appId>', 'Override current app')
|
|
728
|
+
.option('--json', 'Output raw JSON')
|
|
729
|
+
.action((opts) => aiConfigGetCommand(opts));
|
|
730
|
+
aiConfig
|
|
731
|
+
.command('set')
|
|
732
|
+
.option('--app <appId>', 'Override current app')
|
|
733
|
+
.option('--default-model <model>')
|
|
734
|
+
.option('--allowed-models <models...>')
|
|
735
|
+
.option('--max-tokens-per-request <n>', 'Cap on tokens per request', parseInt)
|
|
736
|
+
.option('--byok-key <key>', 'Set or clear BYOK key (empty string clears)')
|
|
737
|
+
.option('--json', 'Output raw JSON')
|
|
738
|
+
.action((opts) => aiConfigSetCommand(opts));
|
|
739
|
+
ai
|
|
740
|
+
.command('usage')
|
|
741
|
+
.description('AI token + cost usage over a window')
|
|
742
|
+
.option('--app <appId>', 'Override current app')
|
|
743
|
+
.option('--start-date <date>', 'ISO date')
|
|
744
|
+
.option('--end-date <date>', 'ISO date')
|
|
745
|
+
.option('--json', 'Output raw JSON')
|
|
746
|
+
.action((opts) => aiUsageCommand({
|
|
747
|
+
app: opts.app, startDate: opts.startDate, endDate: opts.endDate, json: opts.json,
|
|
748
|
+
}));
|
|
749
|
+
// OAuth
|
|
750
|
+
const oauth = program.command('oauth').description('Manage OAuth providers for end-user auth');
|
|
751
|
+
oauth
|
|
752
|
+
.command('configure <provider>')
|
|
753
|
+
.description('Configure an OAuth provider (e.g. google, github, apple)')
|
|
754
|
+
.requiredOption('--client-id <id>')
|
|
755
|
+
.requiredOption('--client-secret <secret>')
|
|
756
|
+
.option('--app <appId>', 'Override current app')
|
|
757
|
+
.option('--redirect-uri <uri>', 'Add a redirect URI (repeatable)', (v, prev) => prev.concat(v), [])
|
|
758
|
+
.option('--scope <scope>', 'Add a scope (repeatable)', (v, prev) => prev.concat(v), [])
|
|
759
|
+
.option('--authorization-url <url>')
|
|
760
|
+
.option('--token-url <url>')
|
|
761
|
+
.option('--userinfo-url <url>')
|
|
762
|
+
.option('--json', 'Output raw JSON')
|
|
763
|
+
.action((provider, opts) => oauthConfigureCommand(provider, opts));
|
|
764
|
+
oauth
|
|
765
|
+
.command('list')
|
|
766
|
+
.description('List configured OAuth providers')
|
|
767
|
+
.option('--app <appId>', 'Override current app')
|
|
768
|
+
.option('--json', 'Output raw JSON')
|
|
769
|
+
.action((opts) => oauthListCommand(opts));
|
|
770
|
+
oauth
|
|
771
|
+
.command('get <provider>')
|
|
772
|
+
.description('Show config for a provider')
|
|
773
|
+
.option('--app <appId>', 'Override current app')
|
|
774
|
+
.option('--json', 'Output raw JSON')
|
|
775
|
+
.action((provider, opts) => oauthGetCommand(provider, opts));
|
|
776
|
+
oauth
|
|
777
|
+
.command('update <provider>')
|
|
778
|
+
.description('Update provider config (any field optional)')
|
|
779
|
+
.option('--app <appId>', 'Override current app')
|
|
780
|
+
.option('--client-id <id>')
|
|
781
|
+
.option('--client-secret <secret>')
|
|
782
|
+
.option('--redirect-uri <uri>', 'Replace redirect URIs', (v, prev) => prev.concat(v), [])
|
|
783
|
+
.option('--scope <scope>', 'Replace scopes', (v, prev) => prev.concat(v), [])
|
|
784
|
+
.option('--enabled <bool>', 'Enable/disable (true|false)', (v) => v === 'true')
|
|
785
|
+
.option('--json', 'Output raw JSON')
|
|
786
|
+
.action((provider, opts) => oauthUpdateCommand(provider, opts));
|
|
787
|
+
oauth
|
|
788
|
+
.command('delete <provider>')
|
|
789
|
+
.description('Delete an OAuth provider configuration')
|
|
790
|
+
.option('--app <appId>', 'Override current app')
|
|
791
|
+
.option('--json', 'Output raw JSON')
|
|
792
|
+
.action((provider, opts) => oauthDeleteCommand(provider, opts));
|
|
793
|
+
// Audit
|
|
794
|
+
const audit = program.command('audit').description('Query the app\'s audit log');
|
|
795
|
+
audit
|
|
796
|
+
.command('query')
|
|
797
|
+
.description('Query audit log entries with optional filters')
|
|
798
|
+
.option('--app <appId>', 'Override current app')
|
|
799
|
+
.option('--category <c>')
|
|
800
|
+
.option('--event-type <e>')
|
|
801
|
+
.option('--action <a>')
|
|
802
|
+
.option('--resource-type <t>')
|
|
803
|
+
.option('--resource-id <id>')
|
|
804
|
+
.option('--actor-id <id>')
|
|
805
|
+
.option('--from <iso>', 'Start of window (ISO date)')
|
|
806
|
+
.option('--to <iso>', 'End of window (ISO date)')
|
|
807
|
+
.option('--limit <n>', 'Max rows', parseInt)
|
|
808
|
+
.option('--offset <n>', 'Pagination offset', parseInt)
|
|
809
|
+
.option('--json', 'Output raw JSON')
|
|
810
|
+
.action((opts) => auditQueryCommand(opts));
|
|
811
|
+
// Regions
|
|
812
|
+
const regions = program.command('regions').description('Multi-region operations');
|
|
813
|
+
regions
|
|
814
|
+
.command('list')
|
|
815
|
+
.description('List supported regions')
|
|
816
|
+
.option('--json', 'Output raw JSON')
|
|
817
|
+
.action((opts) => regionsListCommand(opts));
|
|
818
|
+
// App Billing (Stripe Connect — plans/products/subscriptions/orders)
|
|
819
|
+
const appBilling = program.command('app-billing').description('Manage app-level Stripe Connect billing (plans/products/subscriptions/orders)');
|
|
820
|
+
const abPlans = appBilling.command('plans').description('Subscription plans');
|
|
821
|
+
abPlans
|
|
822
|
+
.command('list')
|
|
823
|
+
.option('--app <appId>', 'Override current app')
|
|
824
|
+
.option('--json', 'Output raw JSON')
|
|
825
|
+
.action((opts) => plansListCommand(opts));
|
|
826
|
+
abPlans
|
|
827
|
+
.command('create')
|
|
828
|
+
.requiredOption('--name <name>')
|
|
829
|
+
.requiredOption('--price-cents <n>', 'Price in cents', parseInt)
|
|
830
|
+
.requiredOption('--interval <month|year>')
|
|
831
|
+
.option('--description <desc>')
|
|
832
|
+
.option('--app <appId>', 'Override current app')
|
|
833
|
+
.option('--json', 'Output raw JSON')
|
|
834
|
+
.action((opts) => plansCreateCommand(opts));
|
|
835
|
+
abPlans
|
|
836
|
+
.command('update <planId>')
|
|
837
|
+
.option('--name <name>')
|
|
838
|
+
.option('--price-cents <n>', 'Price in cents', parseInt)
|
|
839
|
+
.option('--description <desc>')
|
|
840
|
+
.option('--app <appId>', 'Override current app')
|
|
841
|
+
.option('--json', 'Output raw JSON')
|
|
842
|
+
.action((planId, opts) => plansUpdateCommand(planId, opts));
|
|
843
|
+
const abProducts = appBilling.command('products').description('One-time products');
|
|
844
|
+
abProducts
|
|
845
|
+
.command('list')
|
|
846
|
+
.option('--app <appId>', 'Override current app')
|
|
847
|
+
.option('--json', 'Output raw JSON')
|
|
848
|
+
.action((opts) => productsListCommand(opts));
|
|
849
|
+
abProducts
|
|
850
|
+
.command('create')
|
|
851
|
+
.requiredOption('--name <name>')
|
|
852
|
+
.requiredOption('--price-cents <n>', 'Price in cents', parseInt)
|
|
853
|
+
.option('--description <desc>')
|
|
854
|
+
.option('--app <appId>', 'Override current app')
|
|
855
|
+
.option('--json', 'Output raw JSON')
|
|
856
|
+
.action((opts) => productsCreateCommand(opts));
|
|
857
|
+
abProducts
|
|
858
|
+
.command('update <productId>')
|
|
859
|
+
.option('--name <name>')
|
|
860
|
+
.option('--price-cents <n>', 'Price in cents', parseInt)
|
|
861
|
+
.option('--description <desc>')
|
|
862
|
+
.option('--app <appId>', 'Override current app')
|
|
863
|
+
.option('--json', 'Output raw JSON')
|
|
864
|
+
.action((productId, opts) => productsUpdateCommand(productId, opts));
|
|
865
|
+
appBilling
|
|
866
|
+
.command('subscribe <planId>')
|
|
867
|
+
.option('--app <appId>', 'Override current app')
|
|
868
|
+
.option('--json', 'Output raw JSON')
|
|
869
|
+
.action((planId, opts) => subscribeCommand(planId, opts));
|
|
870
|
+
appBilling
|
|
871
|
+
.command('subscription')
|
|
872
|
+
.description('Show the current subscription')
|
|
873
|
+
.option('--app <appId>', 'Override current app')
|
|
874
|
+
.option('--json', 'Output raw JSON')
|
|
875
|
+
.action((opts) => subscriptionCommand(opts));
|
|
876
|
+
appBilling
|
|
877
|
+
.command('cancel')
|
|
878
|
+
.description('Cancel the current subscription')
|
|
879
|
+
.option('--app <appId>', 'Override current app')
|
|
880
|
+
.option('--json', 'Output raw JSON')
|
|
881
|
+
.action((opts) => cancelCommand(opts));
|
|
882
|
+
appBilling
|
|
883
|
+
.command('purchase <productId>')
|
|
884
|
+
.option('--app <appId>', 'Override current app')
|
|
885
|
+
.option('--json', 'Output raw JSON')
|
|
886
|
+
.action((productId, opts) => purchaseCommand(productId, opts));
|
|
887
|
+
const abOrders = appBilling.command('orders').description('Order history');
|
|
888
|
+
abOrders
|
|
889
|
+
.command('list')
|
|
890
|
+
.option('--app <appId>', 'Override current app')
|
|
891
|
+
.option('--json', 'Output raw JSON')
|
|
892
|
+
.action((opts) => ordersListCommand(opts));
|
|
893
|
+
abOrders
|
|
894
|
+
.command('get <orderId>')
|
|
895
|
+
.option('--app <appId>', 'Override current app')
|
|
896
|
+
.option('--json', 'Output raw JSON')
|
|
897
|
+
.action((orderId, opts) => ordersGetCommand(orderId, opts));
|
|
898
|
+
// KV
|
|
899
|
+
const kv = program.command('kv').description('Manage app KV store');
|
|
900
|
+
kv.command('get <key>')
|
|
901
|
+
.description('Get a value by key')
|
|
902
|
+
.option('--app <id>', 'App ID (uses current app if not specified)')
|
|
903
|
+
.option('--raw', 'Return raw value without JSON decoding')
|
|
904
|
+
.action(kvGetCommand);
|
|
905
|
+
kv.command('set <key> <value>')
|
|
906
|
+
.description('Set a value')
|
|
907
|
+
.option('--app <id>', 'App ID (uses current app if not specified)')
|
|
908
|
+
.option('--ttl <ttl>', 'TTL: 30d, 1h, 60s, "null"/"forever" for no expiry')
|
|
909
|
+
.option('--ephemeral', 'Mark as ephemeral (not persisted)')
|
|
910
|
+
.action(kvSetCommand);
|
|
911
|
+
kv.command('del <key>')
|
|
912
|
+
.description('Delete a key')
|
|
913
|
+
.option('--app <id>', 'App ID (uses current app if not specified)')
|
|
914
|
+
.action(kvDelCommand);
|
|
915
|
+
kv.command('ls')
|
|
916
|
+
.description('List / scan keys')
|
|
917
|
+
.option('--app <id>', 'App ID (uses current app if not specified)')
|
|
918
|
+
.option('--prefix <p>', 'Key prefix filter')
|
|
919
|
+
.option('--limit <n>', 'Maximum keys to return (default 100)')
|
|
920
|
+
.action(kvLsCommand);
|
|
921
|
+
kv.command('stats')
|
|
922
|
+
.description('Show KV store statistics')
|
|
923
|
+
.option('--app <id>', 'App ID (uses current app if not specified)')
|
|
924
|
+
.action(kvStatsCommand);
|
|
925
|
+
kv.command('flush')
|
|
926
|
+
.description('Flush all keys (requires --confirm)')
|
|
927
|
+
.option('--app <id>', 'App ID (uses current app if not specified)')
|
|
928
|
+
.option('--confirm', 'Required: confirm destructive flush')
|
|
929
|
+
.option('--include-config', 'Also flush expose rules')
|
|
930
|
+
.action(kvFlushCommand);
|
|
931
|
+
kv.command('rules')
|
|
932
|
+
.description('List expose rules')
|
|
933
|
+
.option('--app <id>', 'App ID (uses current app if not specified)')
|
|
934
|
+
.action(kvRulesCommand);
|
|
935
|
+
kv.command('expose <pattern>')
|
|
936
|
+
.description('Create or update an expose rule')
|
|
937
|
+
.option('--app <id>', 'App ID (uses current app if not specified)')
|
|
938
|
+
.requiredOption('--read <role>', 'Read role (public|authed|owner|deny)')
|
|
939
|
+
.requiredOption('--write <role>', 'Write role (public|authed|owner|deny)')
|
|
940
|
+
.action(kvExposeCommand);
|
|
941
|
+
kv.command('unexpose <pattern>')
|
|
942
|
+
.description('Remove an expose rule')
|
|
943
|
+
.option('--app <id>', 'App ID (uses current app if not specified)')
|
|
944
|
+
.action(kvUnexposeCommand);
|
|
945
|
+
kv.command('apply <file>')
|
|
946
|
+
.description('Apply KV expose rules from a config file (kv.config.ts)')
|
|
947
|
+
.option('--app <id>', 'App ID (uses current app if not specified)')
|
|
948
|
+
.option('--dry-run', 'Preview changes without applying')
|
|
949
|
+
.option('--yes', 'Skip confirmation prompt')
|
|
950
|
+
.action((file, opts) => kvApplyCommand({ app: opts.app, file, dryRun: opts.dryRun, yes: opts.yes }));
|
|
951
|
+
// Top-level error handlers for unhandled exceptions / rejections
|
|
952
|
+
process.on('uncaughtException', (err) => {
|
|
953
|
+
console.error(renderError(err));
|
|
954
|
+
process.exit(1);
|
|
955
|
+
});
|
|
956
|
+
process.on('unhandledRejection', (err) => {
|
|
957
|
+
console.error(renderError(err));
|
|
958
|
+
process.exit(1);
|
|
959
|
+
});
|
|
594
960
|
// Parse arguments
|
|
595
961
|
program.parse();
|
|
596
962
|
//# sourceMappingURL=butterbase.js.map
|