@motherbase/cli 0.2.5 → 0.2.6

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 (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +247 -247
  3. package/bin/motherbase.js +10 -10
  4. package/package.json +35 -26
  5. package/src/api.js +233 -241
  6. package/src/commands/_util.js +106 -93
  7. package/src/commands/backups.js +16 -16
  8. package/src/commands/branches.js +113 -113
  9. package/src/commands/completion.js +90 -90
  10. package/src/commands/config.js +47 -47
  11. package/src/commands/cron.js +65 -65
  12. package/src/commands/db-catalog.js +326 -326
  13. package/src/commands/db.js +103 -103
  14. package/src/commands/domains.js +124 -124
  15. package/src/commands/functions.js +304 -297
  16. package/src/commands/gen.js +172 -172
  17. package/src/commands/import.js +55 -55
  18. package/src/commands/index.js +85 -85
  19. package/src/commands/inspect.js +178 -178
  20. package/src/commands/keys.js +72 -72
  21. package/src/commands/link.js +35 -35
  22. package/src/commands/local.js +294 -273
  23. package/src/commands/login.js +39 -39
  24. package/src/commands/logout.js +16 -16
  25. package/src/commands/mcp.js +61 -61
  26. package/src/commands/migrate.js +207 -179
  27. package/src/commands/migration.js +108 -108
  28. package/src/commands/network.js +83 -83
  29. package/src/commands/orgs.js +40 -40
  30. package/src/commands/postgres-config.js +113 -113
  31. package/src/commands/projects.js +130 -130
  32. package/src/commands/queues.js +133 -133
  33. package/src/commands/realtime.js +35 -35
  34. package/src/commands/rls.js +176 -176
  35. package/src/commands/secrets.js +66 -66
  36. package/src/commands/signing.js +130 -130
  37. package/src/commands/snippets.js +44 -44
  38. package/src/commands/status.js +42 -42
  39. package/src/commands/storage.js +261 -261
  40. package/src/commands/test.js +9 -9
  41. package/src/commands/unlink.js +15 -15
  42. package/src/commands/users.js +142 -142
  43. package/src/commands/vault.js +82 -82
  44. package/src/commands/webhooks.js +62 -62
  45. package/src/commands/whoami.js +20 -20
  46. package/src/commands/wrappers.js +150 -150
  47. package/src/config.js +98 -98
  48. package/src/context.js +106 -102
  49. package/src/docs.js +68 -0
  50. package/src/edge-templates.js +194 -194
  51. package/src/format.js +92 -92
  52. package/src/inspect/csv.js +22 -22
  53. package/src/inspect/queries.js +562 -562
  54. package/src/main.js +224 -194
  55. package/src/mcp/platform-tools.js +233 -233
  56. package/src/mcp/protocol.js +152 -149
  57. package/src/mcp/storage-tools.js +93 -93
  58. package/src/mcp/tools.js +478 -478
  59. package/src/migration/commands.js +375 -375
  60. package/src/migration/files.js +245 -245
  61. package/src/prompt.js +91 -91
  62. package/src/storage/remote.js +62 -62
  63. package/src/ui.js +111 -111
@@ -1,108 +1,108 @@
1
- 'use strict';
2
-
3
- // `migration`: Supabase's name for the group. The server-side lifecycle
4
- // (list/diff/get/create/apply/rollback) and the local `new` scaffold live
5
- // together here; `migrations` is registered as an alias of this entry.
6
-
7
- const fs = require('fs');
8
- const path = require('path');
9
- const api = require('../api');
10
- const ui = require('../ui');
11
- const { output, requireArg } = require('./_util');
12
- const { CliError } = require('../context');
13
-
14
- function readSql(cwd, file) {
15
- if (!file) return undefined;
16
- return fs.readFileSync(path.resolve(cwd, file), 'utf8');
17
- }
18
-
19
- const subcommands = {
20
- new: {
21
- summary: 'Scaffold a local migration .sql file',
22
- usage: 'motherbase migration new <name>',
23
- async run(ctx, args) {
24
- const name = requireArg(args[0], 'name');
25
- if (!/^[a-zA-Z0-9_-]+$/.test(name)) throw new CliError('name must be alphanumeric / dash / underscore');
26
- const dir = path.join(ctx.cwd, 'motherbase', 'migrations');
27
- fs.mkdirSync(dir, { recursive: true });
28
- const stamp = new Date().toISOString().replace(/[-:]/g, '').replace(/\..+/, '').replace('T', '');
29
- const file = path.join(dir, `${stamp}_${name}.sql`);
30
- fs.writeFileSync(file, `-- Migration: ${name}\n\n\n-- @down\n-- Optional: SQL that undoes the above, for \`migration down\`.\n`);
31
- ui.success(`Created ${path.relative(ctx.cwd, file)}`);
32
- },
33
- },
34
- list: {
35
- summary: 'List schema migrations',
36
- usage: 'motherbase migration list',
37
- async run(ctx) {
38
- const data = await api.adminRequest(ctx, 'GET', '/migrations');
39
- const rows = (data && data.migrations) || [];
40
- output(ctx, data, (_d, ui) =>
41
- ui.table(rows, [
42
- { key: 'id', label: 'ID' },
43
- { key: 'name', label: 'NAME' },
44
- { key: 'status', label: 'STATUS' },
45
- { key: 'appliedAt', label: 'APPLIED' },
46
- ]),
47
- );
48
- },
49
- },
50
- diff: {
51
- summary: 'Show a diff between live schema and migrations',
52
- usage: 'motherbase migration diff',
53
- async run(ctx) {
54
- const data = await api.adminRequest(ctx, 'GET', '/migrations/diff');
55
- output(ctx, data, (d, ui) => ui.json(d));
56
- },
57
- },
58
- get: {
59
- summary: 'Show a single migration',
60
- usage: 'motherbase migration get <id>',
61
- async run(ctx, args) {
62
- const id = requireArg(args[0], 'id');
63
- const data = await api.adminRequest(ctx, 'GET', `/migrations/${id}`);
64
- output(ctx, data, (d, ui) => ui.keyValue((d && d.migration) || d || {}));
65
- },
66
- },
67
- create: {
68
- summary: 'Create a migration from up/down SQL files',
69
- usage: 'motherbase migration create --name <name> --up <file.sql> [--down <file.sql>]',
70
- async run(ctx, args, flags) {
71
- const name = flags.name || args[0];
72
- if (!name) throw new CliError('--name is required');
73
- const upSql = readSql(ctx.cwd, flags.up);
74
- const downSql = readSql(ctx.cwd, flags.down);
75
- if (!upSql) throw new CliError('--up <file.sql> is required');
76
- const data = await api.adminRequest(ctx, 'POST', '/migrations', { body: { name, upSql, downSql } });
77
- output(ctx, data, (d, ui) => ui.success(`Created migration ${name}`));
78
- },
79
- },
80
- apply: {
81
- summary: 'Apply a migration',
82
- usage: 'motherbase migration apply <id>',
83
- async run(ctx, args) {
84
- const id = requireArg(args[0], 'id');
85
- const data = await api.adminRequest(ctx, 'POST', `/migrations/${id}/apply`);
86
- output(ctx, data, (d, ui) => ui.success(`Applied ${id}`));
87
- },
88
- },
89
- rollback: {
90
- summary: 'Roll back a migration',
91
- usage: 'motherbase migration rollback <id>',
92
- async run(ctx, args) {
93
- const id = requireArg(args[0], 'id');
94
- const data = await api.adminRequest(ctx, 'POST', `/migrations/${id}/rollback`);
95
- output(ctx, data, (d, ui) => ui.success(`Rolled back ${id}`));
96
- },
97
- },
98
- };
99
-
100
- // The local-files workflow (list/up/down/repair/squash/fetch) lives with its
101
- // helpers in ../migration.
102
- Object.assign(subcommands, require('../migration/commands').migrationSubcommands);
103
-
104
- module.exports = {
105
- summary: 'Schema migrations: local files and the remote history',
106
- usage: 'motherbase migration <new|list|up|down|repair|squash|fetch|diff|get|create|apply|rollback> ...',
107
- subcommands,
108
- };
1
+ 'use strict';
2
+
3
+ // `migration`: Supabase's name for the group. The server-side lifecycle
4
+ // (list/diff/get/create/apply/rollback) and the local `new` scaffold live
5
+ // together here; `migrations` is registered as an alias of this entry.
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+ const api = require('../api');
10
+ const ui = require('../ui');
11
+ const { output, requireArg } = require('./_util');
12
+ const { CliError } = require('../context');
13
+
14
+ function readSql(cwd, file) {
15
+ if (!file) return undefined;
16
+ return fs.readFileSync(path.resolve(cwd, file), 'utf8');
17
+ }
18
+
19
+ const subcommands = {
20
+ new: {
21
+ summary: 'Scaffold a local migration .sql file',
22
+ usage: 'motherbase migration new <name>',
23
+ async run(ctx, args) {
24
+ const name = requireArg(args[0], 'name');
25
+ if (!/^[a-zA-Z0-9_-]+$/.test(name)) throw new CliError('name must be alphanumeric / dash / underscore');
26
+ const dir = path.join(ctx.cwd, 'motherbase', 'migrations');
27
+ fs.mkdirSync(dir, { recursive: true });
28
+ const stamp = new Date().toISOString().replace(/[-:]/g, '').replace(/\..+/, '').replace('T', '');
29
+ const file = path.join(dir, `${stamp}_${name}.sql`);
30
+ fs.writeFileSync(file, `-- Migration: ${name}\n\n\n-- @down\n-- Optional: SQL that undoes the above, for \`migration down\`.\n`);
31
+ ui.success(`Created ${path.relative(ctx.cwd, file)}`);
32
+ },
33
+ },
34
+ list: {
35
+ summary: 'List schema migrations',
36
+ usage: 'motherbase migration list',
37
+ async run(ctx) {
38
+ const data = await api.adminRequest(ctx, 'GET', '/migrations');
39
+ const rows = (data && data.migrations) || [];
40
+ output(ctx, data, (_d, ui) =>
41
+ ui.table(rows, [
42
+ { key: 'id', label: 'ID' },
43
+ { key: 'name', label: 'NAME' },
44
+ { key: 'status', label: 'STATUS' },
45
+ { key: 'appliedAt', label: 'APPLIED' },
46
+ ]),
47
+ );
48
+ },
49
+ },
50
+ diff: {
51
+ summary: 'Show a diff between live schema and migrations',
52
+ usage: 'motherbase migration diff',
53
+ async run(ctx) {
54
+ const data = await api.adminRequest(ctx, 'GET', '/migrations/diff');
55
+ output(ctx, data, (d, ui) => ui.json(d));
56
+ },
57
+ },
58
+ get: {
59
+ summary: 'Show a single migration',
60
+ usage: 'motherbase migration get <id>',
61
+ async run(ctx, args) {
62
+ const id = requireArg(args[0], 'id');
63
+ const data = await api.adminRequest(ctx, 'GET', `/migrations/${id}`);
64
+ output(ctx, data, (d, ui) => ui.keyValue((d && d.migration) || d || {}));
65
+ },
66
+ },
67
+ create: {
68
+ summary: 'Create a migration from up/down SQL files',
69
+ usage: 'motherbase migration create --name <name> --up <file.sql> [--down <file.sql>]',
70
+ async run(ctx, args, flags) {
71
+ const name = flags.name || args[0];
72
+ if (!name) throw new CliError('--name is required');
73
+ const upSql = readSql(ctx.cwd, flags.up);
74
+ const downSql = readSql(ctx.cwd, flags.down);
75
+ if (!upSql) throw new CliError('--up <file.sql> is required');
76
+ const data = await api.adminRequest(ctx, 'POST', '/migrations', { body: { name, upSql, downSql } });
77
+ output(ctx, data, (d, ui) => ui.success(`Created migration ${name}`));
78
+ },
79
+ },
80
+ apply: {
81
+ summary: 'Apply a migration',
82
+ usage: 'motherbase migration apply <id>',
83
+ async run(ctx, args) {
84
+ const id = requireArg(args[0], 'id');
85
+ const data = await api.adminRequest(ctx, 'POST', `/migrations/${id}/apply`);
86
+ output(ctx, data, (d, ui) => ui.success(`Applied ${id}`));
87
+ },
88
+ },
89
+ rollback: {
90
+ summary: 'Roll back a migration',
91
+ usage: 'motherbase migration rollback <id>',
92
+ async run(ctx, args) {
93
+ const id = requireArg(args[0], 'id');
94
+ const data = await api.adminRequest(ctx, 'POST', `/migrations/${id}/rollback`);
95
+ output(ctx, data, (d, ui) => ui.success(`Rolled back ${id}`));
96
+ },
97
+ },
98
+ };
99
+
100
+ // The local-files workflow (list/up/down/repair/squash/fetch) lives with its
101
+ // helpers in ../migration.
102
+ Object.assign(subcommands, require('../migration/commands').migrationSubcommands);
103
+
104
+ module.exports = {
105
+ summary: 'Schema migrations: local files and the remote history',
106
+ usage: 'motherbase migration <new|list|up|down|repair|squash|fetch|diff|get|create|apply|rollback> ...',
107
+ subcommands,
108
+ };
@@ -1,83 +1,83 @@
1
- 'use strict';
2
-
3
- // Who may open a direct Postgres connection (Supabase's `network-restrictions`
4
- // and `ssl-enforcement`). Both are project admin routes, so they work through
5
- // the Console proxy and in direct mode alike.
6
-
7
- const api = require('../api');
8
- const { output } = require('./_util');
9
- const { CliError } = require('../context');
10
-
11
- function renderRestrictions(d, ui) {
12
- ui.keyValue({
13
- 'IPv4 allowed': d.dbAllowedCidrs.join(', ') || '(none)',
14
- 'IPv6 allowed': d.dbAllowedCidrsV6.join(', ') || '(none)',
15
- status: d.status,
16
- exposure: d.exposure,
17
- });
18
- if (d.status !== 'applied') ui.warn('Stored only: this API does not manage its database access (not a Console stack).');
19
- if (d.exposure !== 'public') ui.info('The database port is not public, so only the stack and SSH tunnels reach it today.');
20
- }
21
-
22
- const networkRestrictions = {
23
- summary: 'IP ranges allowed to connect to the database directly',
24
- usage: 'motherbase network-restrictions <get|update> ...',
25
- subcommands: {
26
- get: {
27
- summary: 'Show the allowed ranges',
28
- usage: 'motherbase network-restrictions get',
29
- async run(ctx) {
30
- output(ctx, await api.adminRequest(ctx, 'GET', '/network-restrictions'), renderRestrictions);
31
- },
32
- },
33
- update: {
34
- summary: 'Replace the allowed ranges (repeat --db-allow-cidr; IPv4 and IPv6 are told apart)',
35
- usage: 'motherbase network-restrictions update --db-allow-cidr <cidr> [--db-allow-cidr <cidr> ...]',
36
- async run(ctx, _args, flags) {
37
- const cidrs = [].concat(flags['db-allow-cidr'] ?? []).map(String);
38
- if (!cidrs.length) {
39
- throw new CliError('Pass at least one --db-allow-cidr (0.0.0.0/0 and ::/0 allow everyone).');
40
- }
41
- const body = {
42
- dbAllowedCidrs: cidrs.filter(c => !c.includes(':')),
43
- dbAllowedCidrsV6: cidrs.filter(c => c.includes(':')),
44
- };
45
- output(ctx, await api.adminRequest(ctx, 'PUT', '/network-restrictions', { body }), renderRestrictions);
46
- },
47
- },
48
- },
49
- };
50
-
51
- function renderSsl(d, ui) {
52
- ui.keyValue({ enforced: d.enforced, 'TLS active': d.sslActive, status: d.status });
53
- if (d.enforced) ui.info('External clients must connect with sslmode=require.');
54
- }
55
-
56
- const sslEnforcement = {
57
- summary: 'Require TLS for direct database connections',
58
- usage: 'motherbase ssl-enforcement <get|update> ...',
59
- subcommands: {
60
- get: {
61
- summary: 'Show whether TLS is enforced',
62
- usage: 'motherbase ssl-enforcement get',
63
- async run(ctx) {
64
- output(ctx, await api.adminRequest(ctx, 'GET', '/ssl-enforcement'), renderSsl);
65
- },
66
- },
67
- update: {
68
- summary: 'Turn enforcement on or off',
69
- usage: 'motherbase ssl-enforcement update --enable-db-ssl-enforcement | --disable-db-ssl-enforcement',
70
- async run(ctx, _args, flags) {
71
- const enable = Boolean(flags['enable-db-ssl-enforcement']);
72
- const disable = Boolean(flags['disable-db-ssl-enforcement']);
73
- if (enable === disable) {
74
- throw new CliError('Pass exactly one of --enable-db-ssl-enforcement or --disable-db-ssl-enforcement.');
75
- }
76
- const data = await api.adminRequest(ctx, 'PUT', '/ssl-enforcement', { body: { enforced: enable } });
77
- output(ctx, data, renderSsl);
78
- },
79
- },
80
- },
81
- };
82
-
83
- module.exports = { networkRestrictions, sslEnforcement };
1
+ 'use strict';
2
+
3
+ // Who may open a direct Postgres connection (Supabase's `network-restrictions`
4
+ // and `ssl-enforcement`). Both are project admin routes, so they work through
5
+ // the Console proxy and in direct mode alike.
6
+
7
+ const api = require('../api');
8
+ const { output } = require('./_util');
9
+ const { CliError } = require('../context');
10
+
11
+ function renderRestrictions(d, ui) {
12
+ ui.keyValue({
13
+ 'IPv4 allowed': d.dbAllowedCidrs.join(', ') || '(none)',
14
+ 'IPv6 allowed': d.dbAllowedCidrsV6.join(', ') || '(none)',
15
+ status: d.status,
16
+ exposure: d.exposure,
17
+ });
18
+ if (d.status !== 'applied') ui.warn('Stored only: this API does not manage its database access (not a Console stack).');
19
+ if (d.exposure !== 'public') ui.info('The database port is not public, so only the stack and SSH tunnels reach it today.');
20
+ }
21
+
22
+ const networkRestrictions = {
23
+ summary: 'IP ranges allowed to connect to the database directly',
24
+ usage: 'motherbase network-restrictions <get|update> ...',
25
+ subcommands: {
26
+ get: {
27
+ summary: 'Show the allowed ranges',
28
+ usage: 'motherbase network-restrictions get',
29
+ async run(ctx) {
30
+ output(ctx, await api.adminRequest(ctx, 'GET', '/network-restrictions'), renderRestrictions);
31
+ },
32
+ },
33
+ update: {
34
+ summary: 'Replace the allowed ranges (repeat --db-allow-cidr; IPv4 and IPv6 are told apart)',
35
+ usage: 'motherbase network-restrictions update --db-allow-cidr <cidr> [--db-allow-cidr <cidr> ...]',
36
+ async run(ctx, _args, flags) {
37
+ const cidrs = [].concat(flags['db-allow-cidr'] ?? []).map(String);
38
+ if (!cidrs.length) {
39
+ throw new CliError('Pass at least one --db-allow-cidr (0.0.0.0/0 and ::/0 allow everyone).');
40
+ }
41
+ const body = {
42
+ dbAllowedCidrs: cidrs.filter(c => !c.includes(':')),
43
+ dbAllowedCidrsV6: cidrs.filter(c => c.includes(':')),
44
+ };
45
+ output(ctx, await api.adminRequest(ctx, 'PUT', '/network-restrictions', { body }), renderRestrictions);
46
+ },
47
+ },
48
+ },
49
+ };
50
+
51
+ function renderSsl(d, ui) {
52
+ ui.keyValue({ enforced: d.enforced, 'TLS active': d.sslActive, status: d.status });
53
+ if (d.enforced) ui.info('External clients must connect with sslmode=require.');
54
+ }
55
+
56
+ const sslEnforcement = {
57
+ summary: 'Require TLS for direct database connections',
58
+ usage: 'motherbase ssl-enforcement <get|update> ...',
59
+ subcommands: {
60
+ get: {
61
+ summary: 'Show whether TLS is enforced',
62
+ usage: 'motherbase ssl-enforcement get',
63
+ async run(ctx) {
64
+ output(ctx, await api.adminRequest(ctx, 'GET', '/ssl-enforcement'), renderSsl);
65
+ },
66
+ },
67
+ update: {
68
+ summary: 'Turn enforcement on or off',
69
+ usage: 'motherbase ssl-enforcement update --enable-db-ssl-enforcement | --disable-db-ssl-enforcement',
70
+ async run(ctx, _args, flags) {
71
+ const enable = Boolean(flags['enable-db-ssl-enforcement']);
72
+ const disable = Boolean(flags['disable-db-ssl-enforcement']);
73
+ if (enable === disable) {
74
+ throw new CliError('Pass exactly one of --enable-db-ssl-enforcement or --disable-db-ssl-enforcement.');
75
+ }
76
+ const data = await api.adminRequest(ctx, 'PUT', '/ssl-enforcement', { body: { enforced: enable } });
77
+ output(ctx, data, renderSsl);
78
+ },
79
+ },
80
+ },
81
+ };
82
+
83
+ module.exports = { networkRestrictions, sslEnforcement };
@@ -1,40 +1,40 @@
1
- 'use strict';
2
-
3
- // Tenant organizations on the Platform Console. Projects sit under an org, as
4
- // they do on Supabase.
5
-
6
- const api = require('../api');
7
- const { output, requireArg } = require('./_util');
8
-
9
- const subcommands = {
10
- list: {
11
- summary: 'List the organizations you belong to',
12
- usage: 'motherbase orgs list',
13
- async run(ctx) {
14
- const data = await api.consoleRequest(ctx, 'GET', '/orgs');
15
- output(ctx, data, (rows, ui) =>
16
- ui.table(Array.isArray(rows) ? rows : [], [
17
- { key: 'id', label: 'ID' },
18
- { key: 'name', label: 'NAME' },
19
- { key: 'role', label: 'ROLE' },
20
- { key: 'projectCount', label: 'PROJECTS' },
21
- ]),
22
- );
23
- },
24
- },
25
- create: {
26
- summary: 'Create an organization',
27
- usage: 'motherbase orgs create <name>',
28
- async run(ctx, args) {
29
- const name = requireArg(args[0], 'name');
30
- const data = await api.consoleRequest(ctx, 'POST', '/orgs', { body: { name } });
31
- output(ctx, data, (d, ui) => ui.success(`Created org ${d.name} (${d.id})`));
32
- },
33
- },
34
- };
35
-
36
- module.exports = {
37
- summary: 'Manage organizations on the Platform Console',
38
- usage: 'motherbase orgs <list|create> ...',
39
- subcommands,
40
- };
1
+ 'use strict';
2
+
3
+ // Tenant organizations on the Platform Console. Projects sit under an org, as
4
+ // they do on Supabase.
5
+
6
+ const api = require('../api');
7
+ const { output, requireArg } = require('./_util');
8
+
9
+ const subcommands = {
10
+ list: {
11
+ summary: 'List the organizations you belong to',
12
+ usage: 'motherbase orgs list',
13
+ async run(ctx) {
14
+ const data = await api.consoleRequest(ctx, 'GET', '/orgs');
15
+ output(ctx, data, (rows, ui) =>
16
+ ui.table(Array.isArray(rows) ? rows : [], [
17
+ { key: 'id', label: 'ID' },
18
+ { key: 'name', label: 'NAME' },
19
+ { key: 'role', label: 'ROLE' },
20
+ { key: 'projectCount', label: 'PROJECTS' },
21
+ ]),
22
+ );
23
+ },
24
+ },
25
+ create: {
26
+ summary: 'Create an organization',
27
+ usage: 'motherbase orgs create <name>',
28
+ async run(ctx, args) {
29
+ const name = requireArg(args[0], 'name');
30
+ const data = await api.consoleRequest(ctx, 'POST', '/orgs', { body: { name } });
31
+ output(ctx, data, (d, ui) => ui.success(`Created org ${d.name} (${d.id})`));
32
+ },
33
+ },
34
+ };
35
+
36
+ module.exports = {
37
+ summary: 'Manage organizations on the Platform Console',
38
+ usage: 'motherbase orgs <list|create> ...',
39
+ subcommands,
40
+ };