@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,103 +1,103 @@
1
- 'use strict';
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
- const api = require('../api');
6
- const prompt = require('../prompt');
7
- const rls = require('./rls');
8
- const { output, alias, parseKeyValues } = require('./_util');
9
- const { CliError } = require('../context');
10
-
11
- const subcommands = {
12
- tables: {
13
- summary: 'List database tables',
14
- usage: 'motherbase db tables',
15
- async run(ctx) {
16
- const data = await api.adminRequest(ctx, 'GET', '/schema/tables');
17
- const tables = (data && data.tables) || data || [];
18
- output(ctx, data, (_d, ui) =>
19
- ui.table(Array.isArray(tables) ? tables : [], [
20
- { key: 'name', label: 'TABLE' },
21
- { key: 'schema', label: 'SCHEMA' },
22
- ]),
23
- );
24
- },
25
- },
26
- sql: {
27
- summary: 'Run SQL from -e "..." or a file',
28
- usage: 'motherbase db sql -e "select 1" | motherbase db sql <file.sql>',
29
- async run(ctx, args, flags) {
30
- let sql = flags.e || flags.execute;
31
- if (!sql && args[0]) sql = fs.readFileSync(path.resolve(ctx.cwd, args[0]), 'utf8');
32
- if (!sql) throw new CliError('Provide SQL via -e "..." or a file path.');
33
- const data = await api.adminRequest(ctx, 'POST', '/sql', { body: { sql } });
34
- output(ctx, data, (d, ui) => {
35
- if (d && Array.isArray(d.rows)) ui.table(d.rows);
36
- else if (Array.isArray(d)) ui.table(d);
37
- else ui.json(d);
38
- });
39
- },
40
- },
41
- // `db dump` is Supabase's local dump (../migration/commands); this is the
42
- // server-side backup it used to be.
43
- backup: {
44
- summary: 'Create a database backup on the server',
45
- usage: 'motherbase db backup [file]',
46
- async run(ctx, args) {
47
- const body = args[0] ? { file: args[0] } : {};
48
- const data = await api.adminRequest(ctx, 'POST', '/backups', { body });
49
- output(ctx, data, (d, ui) => ui.success(`Backup created${d && d.file ? `: ${d.file}` : ''}`));
50
- },
51
- },
52
- restore: {
53
- summary: 'Restore the database from a server dump or offsite key',
54
- usage: 'motherbase db restore <file> | --key <remoteKey> --confirm',
55
- async run(ctx, args, flags) {
56
- const file = args[0];
57
- const key = flags.key;
58
- if (!file && !key) throw new CliError('Provide a <file> or --key <remoteKey>.');
59
- if (!flags.confirm && !flags.yes) {
60
- const ok = await prompt.confirm('This will overwrite the database. Continue?');
61
- if (!ok) throw new CliError('Aborted.');
62
- }
63
- const body = key ? { key, confirm: 'RESTORE' } : { file, confirm: 'RESTORE' };
64
- const data = await api.adminRequest(ctx, 'POST', '/backups/restore', { body });
65
- output(ctx, data, (d, ui) => ui.success('Restore complete'));
66
- },
67
- },
68
- backups: {
69
- summary: 'List backups, or manage backup config (`db backups config`)',
70
- usage: 'motherbase db backups [list] | motherbase db backups config [--set key=value ...]',
71
- async run(ctx, args, flags) {
72
- const sub = args[0];
73
- if (sub === 'config') {
74
- const sets = [].concat(flags.set || []).filter(Boolean);
75
- if (sets.length) {
76
- const body = parseKeyValues(sets);
77
- const data = await api.adminRequest(ctx, 'PUT', '/backups/config', { body });
78
- output(ctx, data, (d, ui) => ui.success('Backup config saved'));
79
- } else {
80
- const data = await api.adminRequest(ctx, 'GET', '/backups/config');
81
- output(ctx, data, (d, ui) => ui.keyValue(d || {}));
82
- }
83
- return;
84
- }
85
- const data = await api.adminRequest(ctx, 'GET', '/backups/list');
86
- output(ctx, data, (d, ui) => ui.json(d));
87
- },
88
- },
89
- };
90
-
91
- // Supabase's local-files workflow: push, pull, diff, reset, dump, seed.
92
- Object.assign(subcommands, require('../migration/commands').dbSubcommands);
93
- // Studio's Database section: indexes, enum types, roles, column privileges, publications.
94
- Object.assign(subcommands, require('./db-catalog'));
95
-
96
- // Supabase's `db lint`: the same advisor as `rls lint`.
97
- subcommands.lint = alias(rls.subcommands.lint, 'motherbase db lint [--level critical|warning|info]');
98
-
99
- module.exports = {
100
- summary: 'Database: tables, SQL, dumps, restores, indexes, types, roles, publications',
101
- usage: 'motherbase db <push|pull|diff|reset|dump|seed|tables|sql|backup|restore|backups|lint|indexes|types|roles|column-privileges|publications> ...',
102
- subcommands,
103
- };
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const api = require('../api');
6
+ const prompt = require('../prompt');
7
+ const rls = require('./rls');
8
+ const { output, alias, parseKeyValues } = require('./_util');
9
+ const { CliError } = require('../context');
10
+
11
+ const subcommands = {
12
+ tables: {
13
+ summary: 'List database tables',
14
+ usage: 'motherbase db tables',
15
+ async run(ctx) {
16
+ const data = await api.adminRequest(ctx, 'GET', '/schema/tables');
17
+ const tables = (data && data.tables) || data || [];
18
+ output(ctx, data, (_d, ui) =>
19
+ ui.table(Array.isArray(tables) ? tables : [], [
20
+ { key: 'name', label: 'TABLE' },
21
+ { key: 'schema', label: 'SCHEMA' },
22
+ ]),
23
+ );
24
+ },
25
+ },
26
+ sql: {
27
+ summary: 'Run SQL from -e "..." or a file',
28
+ usage: 'motherbase db sql -e "select 1" | motherbase db sql <file.sql>',
29
+ async run(ctx, args, flags) {
30
+ let sql = flags.e || flags.execute;
31
+ if (!sql && args[0]) sql = fs.readFileSync(path.resolve(ctx.cwd, args[0]), 'utf8');
32
+ if (!sql) throw new CliError('Provide SQL via -e "..." or a file path.');
33
+ const data = await api.adminRequest(ctx, 'POST', '/sql', { body: { sql } });
34
+ output(ctx, data, (d, ui) => {
35
+ if (d && Array.isArray(d.rows)) ui.table(d.rows);
36
+ else if (Array.isArray(d)) ui.table(d);
37
+ else ui.json(d);
38
+ });
39
+ },
40
+ },
41
+ // `db dump` is Supabase's local dump (../migration/commands); this is the
42
+ // server-side backup it used to be.
43
+ backup: {
44
+ summary: 'Create a database backup on the server',
45
+ usage: 'motherbase db backup [file]',
46
+ async run(ctx, args) {
47
+ const body = args[0] ? { file: args[0] } : {};
48
+ const data = await api.adminRequest(ctx, 'POST', '/backups', { body });
49
+ output(ctx, data, (d, ui) => ui.success(`Backup created${d && d.file ? `: ${d.file}` : ''}`));
50
+ },
51
+ },
52
+ restore: {
53
+ summary: 'Restore the database from a server dump or offsite key',
54
+ usage: 'motherbase db restore <file> | --key <remoteKey> --confirm',
55
+ async run(ctx, args, flags) {
56
+ const file = args[0];
57
+ const key = flags.key;
58
+ if (!file && !key) throw new CliError('Provide a <file> or --key <remoteKey>.');
59
+ if (!flags.confirm && !flags.yes) {
60
+ const ok = await prompt.confirm('This will overwrite the database. Continue?');
61
+ if (!ok) throw new CliError('Aborted.');
62
+ }
63
+ const body = key ? { key, confirm: 'RESTORE' } : { file, confirm: 'RESTORE' };
64
+ const data = await api.adminRequest(ctx, 'POST', '/backups/restore', { body });
65
+ output(ctx, data, (d, ui) => ui.success('Restore complete'));
66
+ },
67
+ },
68
+ backups: {
69
+ summary: 'List backups, or manage backup config (`db backups config`)',
70
+ usage: 'motherbase db backups [list] | motherbase db backups config [--set key=value ...]',
71
+ async run(ctx, args, flags) {
72
+ const sub = args[0];
73
+ if (sub === 'config') {
74
+ const sets = [].concat(flags.set || []).filter(Boolean);
75
+ if (sets.length) {
76
+ const body = parseKeyValues(sets);
77
+ const data = await api.adminRequest(ctx, 'PUT', '/backups/config', { body });
78
+ output(ctx, data, (d, ui) => ui.success('Backup config saved'));
79
+ } else {
80
+ const data = await api.adminRequest(ctx, 'GET', '/backups/config');
81
+ output(ctx, data, (d, ui) => ui.keyValue(d || {}));
82
+ }
83
+ return;
84
+ }
85
+ const data = await api.adminRequest(ctx, 'GET', '/backups/list');
86
+ output(ctx, data, (d, ui) => ui.json(d));
87
+ },
88
+ },
89
+ };
90
+
91
+ // Supabase's local-files workflow: push, pull, diff, reset, dump, seed.
92
+ Object.assign(subcommands, require('../migration/commands').dbSubcommands);
93
+ // Studio's Database section: indexes, enum types, roles, column privileges, publications.
94
+ Object.assign(subcommands, require('./db-catalog'));
95
+
96
+ // Supabase's `db lint`: the same advisor as `rls lint`.
97
+ subcommands.lint = alias(rls.subcommands.lint, 'motherbase db lint [--level critical|warning|info]');
98
+
99
+ module.exports = {
100
+ summary: 'Database: tables, SQL, dumps, restores, indexes, types, roles, publications',
101
+ usage: 'motherbase db <push|pull|diff|reset|dump|seed|tables|sql|backup|restore|backups|lint|indexes|types|roles|column-privileges|publications> ...',
102
+ subcommands,
103
+ };
@@ -1,124 +1,124 @@
1
- 'use strict';
2
-
3
- // Custom domains and vanity subdomains (Supabase's `supabase domains` and
4
- // `supabase vanity-subdomains`). Both are Console routes on the linked project.
5
-
6
- const api = require('../api');
7
- const { output, requireArg, consoleProjectPath } = require('./_util');
8
-
9
- function renderHostname(d, ui) {
10
- const custom = d.customHostname;
11
- if (!custom) {
12
- ui.info(`No custom domain. The API answers on ${d.projectDomain}.`);
13
- return;
14
- }
15
- ui.keyValue({ hostname: custom.hostname, status: custom.status, projectDomain: d.projectDomain });
16
- if (custom.status !== 'active') {
17
- ui.print('');
18
- ui.print('Publish these DNS records, then run `motherbase domains reverify`:');
19
- ui.table(custom.requiredRecords, [
20
- { key: 'type', label: 'TYPE' },
21
- { key: 'name', label: 'NAME' },
22
- { key: 'value', label: 'VALUE' },
23
- ]);
24
- }
25
- for (const check of custom.checks || []) {
26
- ui[check.ok ? 'success' : 'warn'](`${check.record} ${check.name}: ${check.ok ? 'ok' : `found ${check.found.join(', ') || 'nothing'}`}`);
27
- }
28
- }
29
-
30
- const hostnamePath = (ctx, suffix = '') => consoleProjectPath(ctx, `/custom-hostname${suffix}`);
31
-
32
- const domains = {
33
- summary: 'Custom domain for the linked project\'s API',
34
- usage: 'motherbase domains <create|get|reverify|activate|delete> ...',
35
- subcommands: {
36
- create: {
37
- summary: 'Start using a custom hostname (prints the DNS records to publish)',
38
- usage: 'motherbase domains create --custom-hostname <api.example.com>',
39
- async run(ctx, _args, flags) {
40
- const hostname = requireArg(flags['custom-hostname'], 'custom-hostname');
41
- const data = await api.consoleRequest(ctx, 'POST', hostnamePath(ctx), { body: { hostname } });
42
- output(ctx, data, renderHostname);
43
- },
44
- },
45
- get: {
46
- summary: 'Show the custom hostname and its verification state',
47
- usage: 'motherbase domains get',
48
- async run(ctx) {
49
- output(ctx, await api.consoleRequest(ctx, 'GET', hostnamePath(ctx)), renderHostname);
50
- },
51
- },
52
- reverify: {
53
- summary: 'Check the DNS records again',
54
- usage: 'motherbase domains reverify',
55
- async run(ctx) {
56
- output(ctx, await api.consoleRequest(ctx, 'POST', hostnamePath(ctx, '/reverify')), renderHostname);
57
- },
58
- },
59
- activate: {
60
- summary: 'Serve the API on the verified hostname (TLS is issued on first request)',
61
- usage: 'motherbase domains activate',
62
- async run(ctx) {
63
- output(ctx, await api.consoleRequest(ctx, 'POST', hostnamePath(ctx, '/activate')), renderHostname);
64
- },
65
- },
66
- delete: {
67
- summary: 'Stop using the custom hostname',
68
- usage: 'motherbase domains delete',
69
- async run(ctx) {
70
- const data = await api.consoleRequest(ctx, 'DELETE', hostnamePath(ctx));
71
- output(ctx, data, (_d, ui) => ui.success('Custom hostname removed'));
72
- },
73
- },
74
- },
75
- };
76
-
77
- function renderVanity(d, ui) {
78
- if (d.vanitySubdomain) ui.keyValue(d.vanitySubdomain);
79
- else ui.info(`No vanity subdomain${d.vanitySuffix ? ` (they live under ${d.vanitySuffix})` : ''}.`);
80
- }
81
-
82
- const vanityPath = (ctx, suffix = '') => consoleProjectPath(ctx, `/vanity-subdomain${suffix}`);
83
-
84
- const vanitySubdomains = {
85
- summary: 'Vanity subdomain under the platform domain',
86
- usage: 'motherbase vanity-subdomains <get|check-availability|activate|delete> ...',
87
- subcommands: {
88
- get: {
89
- summary: 'Show the active vanity subdomain',
90
- usage: 'motherbase vanity-subdomains get',
91
- async run(ctx) {
92
- output(ctx, await api.consoleRequest(ctx, 'GET', vanityPath(ctx)), renderVanity);
93
- },
94
- },
95
- 'check-availability': {
96
- summary: 'Is a subdomain free?',
97
- usage: 'motherbase vanity-subdomains check-availability --desired-subdomain <name>',
98
- async run(ctx, _args, flags) {
99
- const subdomain = requireArg(flags['desired-subdomain'], 'desired-subdomain');
100
- const data = await api.consoleRequest(ctx, 'POST', vanityPath(ctx, '/check-availability'), { body: { subdomain } });
101
- output(ctx, data, (d, ui) => (d.available ? ui.success(`${d.hostname} is available`) : ui.warn(`${d.hostname} is taken`)));
102
- },
103
- },
104
- activate: {
105
- summary: 'Serve the API on <name>.<platform domain> as well',
106
- usage: 'motherbase vanity-subdomains activate --desired-subdomain <name>',
107
- async run(ctx, _args, flags) {
108
- const subdomain = requireArg(flags['desired-subdomain'], 'desired-subdomain');
109
- const data = await api.consoleRequest(ctx, 'POST', vanityPath(ctx), { body: { subdomain } });
110
- output(ctx, data, renderVanity);
111
- },
112
- },
113
- delete: {
114
- summary: 'Remove the vanity subdomain',
115
- usage: 'motherbase vanity-subdomains delete',
116
- async run(ctx) {
117
- const data = await api.consoleRequest(ctx, 'DELETE', vanityPath(ctx));
118
- output(ctx, data, (_d, ui) => ui.success('Vanity subdomain removed'));
119
- },
120
- },
121
- },
122
- };
123
-
124
- module.exports = { domains, vanitySubdomains };
1
+ 'use strict';
2
+
3
+ // Custom domains and vanity subdomains (Supabase's `supabase domains` and
4
+ // `supabase vanity-subdomains`). Both are Console routes on the linked project.
5
+
6
+ const api = require('../api');
7
+ const { output, requireArg, consoleProjectPath } = require('./_util');
8
+
9
+ function renderHostname(d, ui) {
10
+ const custom = d.customHostname;
11
+ if (!custom) {
12
+ ui.info(`No custom domain. The API answers on ${d.projectDomain}.`);
13
+ return;
14
+ }
15
+ ui.keyValue({ hostname: custom.hostname, status: custom.status, projectDomain: d.projectDomain });
16
+ if (custom.status !== 'active') {
17
+ ui.print('');
18
+ ui.print('Publish these DNS records, then run `motherbase domains reverify`:');
19
+ ui.table(custom.requiredRecords, [
20
+ { key: 'type', label: 'TYPE' },
21
+ { key: 'name', label: 'NAME' },
22
+ { key: 'value', label: 'VALUE' },
23
+ ]);
24
+ }
25
+ for (const check of custom.checks || []) {
26
+ ui[check.ok ? 'success' : 'warn'](`${check.record} ${check.name}: ${check.ok ? 'ok' : `found ${check.found.join(', ') || 'nothing'}`}`);
27
+ }
28
+ }
29
+
30
+ const hostnamePath = (ctx, suffix = '') => consoleProjectPath(ctx, `/custom-hostname${suffix}`);
31
+
32
+ const domains = {
33
+ summary: 'Custom domain for the linked project\'s API',
34
+ usage: 'motherbase domains <create|get|reverify|activate|delete> ...',
35
+ subcommands: {
36
+ create: {
37
+ summary: 'Start using a custom hostname (prints the DNS records to publish)',
38
+ usage: 'motherbase domains create --custom-hostname <api.example.com>',
39
+ async run(ctx, _args, flags) {
40
+ const hostname = requireArg(flags['custom-hostname'], 'custom-hostname');
41
+ const data = await api.consoleRequest(ctx, 'POST', hostnamePath(ctx), { body: { hostname } });
42
+ output(ctx, data, renderHostname);
43
+ },
44
+ },
45
+ get: {
46
+ summary: 'Show the custom hostname and its verification state',
47
+ usage: 'motherbase domains get',
48
+ async run(ctx) {
49
+ output(ctx, await api.consoleRequest(ctx, 'GET', hostnamePath(ctx)), renderHostname);
50
+ },
51
+ },
52
+ reverify: {
53
+ summary: 'Check the DNS records again',
54
+ usage: 'motherbase domains reverify',
55
+ async run(ctx) {
56
+ output(ctx, await api.consoleRequest(ctx, 'POST', hostnamePath(ctx, '/reverify')), renderHostname);
57
+ },
58
+ },
59
+ activate: {
60
+ summary: 'Serve the API on the verified hostname (TLS is issued on first request)',
61
+ usage: 'motherbase domains activate',
62
+ async run(ctx) {
63
+ output(ctx, await api.consoleRequest(ctx, 'POST', hostnamePath(ctx, '/activate')), renderHostname);
64
+ },
65
+ },
66
+ delete: {
67
+ summary: 'Stop using the custom hostname',
68
+ usage: 'motherbase domains delete',
69
+ async run(ctx) {
70
+ const data = await api.consoleRequest(ctx, 'DELETE', hostnamePath(ctx));
71
+ output(ctx, data, (_d, ui) => ui.success('Custom hostname removed'));
72
+ },
73
+ },
74
+ },
75
+ };
76
+
77
+ function renderVanity(d, ui) {
78
+ if (d.vanitySubdomain) ui.keyValue(d.vanitySubdomain);
79
+ else ui.info(`No vanity subdomain${d.vanitySuffix ? ` (they live under ${d.vanitySuffix})` : ''}.`);
80
+ }
81
+
82
+ const vanityPath = (ctx, suffix = '') => consoleProjectPath(ctx, `/vanity-subdomain${suffix}`);
83
+
84
+ const vanitySubdomains = {
85
+ summary: 'Vanity subdomain under the platform domain',
86
+ usage: 'motherbase vanity-subdomains <get|check-availability|activate|delete> ...',
87
+ subcommands: {
88
+ get: {
89
+ summary: 'Show the active vanity subdomain',
90
+ usage: 'motherbase vanity-subdomains get',
91
+ async run(ctx) {
92
+ output(ctx, await api.consoleRequest(ctx, 'GET', vanityPath(ctx)), renderVanity);
93
+ },
94
+ },
95
+ 'check-availability': {
96
+ summary: 'Is a subdomain free?',
97
+ usage: 'motherbase vanity-subdomains check-availability --desired-subdomain <name>',
98
+ async run(ctx, _args, flags) {
99
+ const subdomain = requireArg(flags['desired-subdomain'], 'desired-subdomain');
100
+ const data = await api.consoleRequest(ctx, 'POST', vanityPath(ctx, '/check-availability'), { body: { subdomain } });
101
+ output(ctx, data, (d, ui) => (d.available ? ui.success(`${d.hostname} is available`) : ui.warn(`${d.hostname} is taken`)));
102
+ },
103
+ },
104
+ activate: {
105
+ summary: 'Serve the API on <name>.<platform domain> as well',
106
+ usage: 'motherbase vanity-subdomains activate --desired-subdomain <name>',
107
+ async run(ctx, _args, flags) {
108
+ const subdomain = requireArg(flags['desired-subdomain'], 'desired-subdomain');
109
+ const data = await api.consoleRequest(ctx, 'POST', vanityPath(ctx), { body: { subdomain } });
110
+ output(ctx, data, renderVanity);
111
+ },
112
+ },
113
+ delete: {
114
+ summary: 'Remove the vanity subdomain',
115
+ usage: 'motherbase vanity-subdomains delete',
116
+ async run(ctx) {
117
+ const data = await api.consoleRequest(ctx, 'DELETE', vanityPath(ctx));
118
+ output(ctx, data, (_d, ui) => ui.success('Vanity subdomain removed'));
119
+ },
120
+ },
121
+ },
122
+ };
123
+
124
+ module.exports = { domains, vanitySubdomains };