@xano/cli 0.0.30 → 0.0.32

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 (51) hide show
  1. package/dist/base-command.js +1 -1
  2. package/dist/commands/platform/get/index.d.ts +18 -0
  3. package/dist/commands/platform/get/index.js +126 -0
  4. package/dist/commands/platform/list/index.d.ts +12 -0
  5. package/dist/commands/platform/list/index.js +113 -0
  6. package/dist/commands/release/create/index.d.ts +18 -0
  7. package/dist/commands/release/create/index.js +138 -0
  8. package/dist/commands/release/delete/index.d.ts +21 -0
  9. package/dist/commands/release/delete/index.js +134 -0
  10. package/dist/commands/release/edit/index.d.ts +21 -0
  11. package/dist/commands/release/edit/index.js +137 -0
  12. package/dist/commands/release/export/index.d.ts +20 -0
  13. package/dist/commands/release/export/index.js +142 -0
  14. package/dist/commands/release/get/index.d.ts +19 -0
  15. package/dist/commands/release/get/index.js +123 -0
  16. package/dist/commands/release/import/index.d.ts +15 -0
  17. package/dist/commands/release/import/index.js +114 -0
  18. package/dist/commands/release/list/index.d.ts +13 -0
  19. package/dist/commands/release/list/index.js +120 -0
  20. package/dist/commands/tenant/backup/create/index.d.ts +20 -0
  21. package/dist/commands/tenant/backup/create/index.js +113 -0
  22. package/dist/commands/tenant/backup/delete/index.d.ts +22 -0
  23. package/dist/commands/tenant/backup/delete/index.js +137 -0
  24. package/dist/commands/tenant/backup/export/index.d.ts +21 -0
  25. package/dist/commands/tenant/backup/export/index.js +147 -0
  26. package/dist/commands/tenant/backup/import/index.d.ts +21 -0
  27. package/dist/commands/tenant/backup/import/index.js +127 -0
  28. package/dist/commands/tenant/backup/list/index.d.ts +20 -0
  29. package/dist/commands/tenant/backup/list/index.js +137 -0
  30. package/dist/commands/tenant/backup/restore/index.d.ts +22 -0
  31. package/dist/commands/tenant/backup/restore/index.js +141 -0
  32. package/dist/commands/tenant/create/index.d.ts +21 -0
  33. package/dist/commands/tenant/create/index.js +155 -0
  34. package/dist/commands/tenant/delete/index.d.ts +21 -0
  35. package/dist/commands/tenant/delete/index.js +134 -0
  36. package/dist/commands/tenant/deploy-platform/index.d.ts +20 -0
  37. package/dist/commands/tenant/deploy-platform/index.js +116 -0
  38. package/dist/commands/tenant/deploy-release/index.d.ts +20 -0
  39. package/dist/commands/tenant/deploy-release/index.js +116 -0
  40. package/dist/commands/tenant/edit/index.d.ts +26 -0
  41. package/dist/commands/tenant/edit/index.js +167 -0
  42. package/dist/commands/tenant/get/index.d.ts +19 -0
  43. package/dist/commands/tenant/get/index.js +135 -0
  44. package/dist/commands/tenant/list/index.d.ts +13 -0
  45. package/dist/commands/tenant/list/index.js +123 -0
  46. package/dist/commands/workspace/pull/index.d.ts +1 -0
  47. package/dist/commands/workspace/pull/index.js +38 -4
  48. package/dist/commands/workspace/push/index.d.ts +3 -0
  49. package/dist/commands/workspace/push/index.js +33 -1
  50. package/oclif.manifest.json +3006 -1049
  51. package/package.json +10 -1
@@ -7,7 +7,7 @@ export default class BaseCommand extends Command {
7
7
  static baseFlags = {
8
8
  profile: Flags.string({
9
9
  char: 'p',
10
- description: 'Profile to use for this command',
10
+ description: 'Profile to use (uses default profile if not specified)',
11
11
  env: 'XANO_PROFILE',
12
12
  required: false,
13
13
  }),
@@ -0,0 +1,18 @@
1
+ import BaseCommand from '../../../base-command.js';
2
+ export default class PlatformGet extends BaseCommand {
3
+ static args: {
4
+ platform_id: import("@oclif/core/interfaces").Arg<number, {
5
+ max?: number;
6
+ min?: number;
7
+ }>;
8
+ };
9
+ static description: string;
10
+ static examples: string[];
11
+ static flags: {
12
+ output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
13
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
+ };
16
+ run(): Promise<void>;
17
+ private loadCredentials;
18
+ }
@@ -0,0 +1,126 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import * as yaml from 'js-yaml';
3
+ import * as fs from 'node:fs';
4
+ import * as os from 'node:os';
5
+ import * as path from 'node:path';
6
+ import BaseCommand from '../../../base-command.js';
7
+ export default class PlatformGet extends BaseCommand {
8
+ static args = {
9
+ platform_id: Args.integer({
10
+ description: 'Platform ID to retrieve',
11
+ required: true,
12
+ }),
13
+ };
14
+ static description = 'Get details of a specific platform';
15
+ static examples = [
16
+ `$ xano platform get 23629
17
+ Platform ID: 23629
18
+ Created: 2025-11-28
19
+ Helm: 0.1.356
20
+ Images:
21
+ backend 0.0.2985
22
+ frontend 0.1.3427
23
+ database 0.1.6
24
+ node 0.1.192
25
+ deno 0.0.212
26
+ redis 0.1.34
27
+ realtime 0.1.149
28
+ standalone 0.0.2456
29
+ playwright 0.0.992
30
+ static 0.0.10
31
+ static-build 0.0.4
32
+ backend-encoded 0.0.1396
33
+ `,
34
+ `$ xano platform get 23629 -o json`,
35
+ ];
36
+ static flags = {
37
+ ...BaseCommand.baseFlags,
38
+ output: Flags.string({
39
+ char: 'o',
40
+ default: 'summary',
41
+ description: 'Output format',
42
+ options: ['summary', 'json'],
43
+ required: false,
44
+ }),
45
+ };
46
+ async run() {
47
+ const { args, flags } = await this.parse(PlatformGet);
48
+ const profileName = flags.profile || this.getDefaultProfile();
49
+ const credentials = this.loadCredentials();
50
+ if (!(profileName in credentials.profiles)) {
51
+ this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
52
+ `Create a profile using 'xano profile create'`);
53
+ }
54
+ const profile = credentials.profiles[profileName];
55
+ if (!profile.instance_origin) {
56
+ this.error(`Profile '${profileName}' is missing instance_origin`);
57
+ }
58
+ if (!profile.access_token) {
59
+ this.error(`Profile '${profileName}' is missing access_token`);
60
+ }
61
+ const platformId = args.platform_id;
62
+ const apiUrl = `${profile.instance_origin}/api:meta/platform/${platformId}`;
63
+ try {
64
+ const response = await this.verboseFetch(apiUrl, {
65
+ headers: {
66
+ 'accept': 'application/json',
67
+ 'Authorization': `Bearer ${profile.access_token}`,
68
+ },
69
+ method: 'GET',
70
+ }, flags.verbose, profile.access_token);
71
+ if (!response.ok) {
72
+ const errorText = await response.text();
73
+ this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
74
+ }
75
+ const platform = await response.json();
76
+ if (flags.output === 'json') {
77
+ this.log(JSON.stringify(platform, null, 2));
78
+ }
79
+ else {
80
+ const label = platform.name ? `Platform: ${platform.name} (ID: ${platform.id})` : `Platform ID: ${platform.id}`;
81
+ this.log(label);
82
+ if (platform.created_at) {
83
+ const createdDate = new Date(platform.created_at).toISOString().split('T')[0];
84
+ this.log(` Created: ${createdDate}`);
85
+ }
86
+ if (platform.helm?.tag) {
87
+ this.log(` Helm: ${platform.helm.tag}`);
88
+ }
89
+ if (platform.images && Object.keys(platform.images).length > 0) {
90
+ this.log(' Images:');
91
+ const maxLen = Math.max(...Object.keys(platform.images).map(k => k.length));
92
+ for (const [name, image] of Object.entries(platform.images)) {
93
+ this.log(` ${name.padEnd(maxLen)} ${image.tag ?? 'unknown'}`);
94
+ }
95
+ }
96
+ }
97
+ }
98
+ catch (error) {
99
+ if (error instanceof Error) {
100
+ this.error(`Failed to get platform: ${error.message}`);
101
+ }
102
+ else {
103
+ this.error(`Failed to get platform: ${String(error)}`);
104
+ }
105
+ }
106
+ }
107
+ loadCredentials() {
108
+ const configDir = path.join(os.homedir(), '.xano');
109
+ const credentialsPath = path.join(configDir, 'credentials.yaml');
110
+ if (!fs.existsSync(credentialsPath)) {
111
+ this.error(`Credentials file not found at ${credentialsPath}\n` +
112
+ `Create a profile using 'xano profile create'`);
113
+ }
114
+ try {
115
+ const fileContent = fs.readFileSync(credentialsPath, 'utf8');
116
+ const parsed = yaml.load(fileContent);
117
+ if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
118
+ this.error('Credentials file has invalid format.');
119
+ }
120
+ return parsed;
121
+ }
122
+ catch (error) {
123
+ this.error(`Failed to parse credentials file: ${error}`);
124
+ }
125
+ }
126
+ }
@@ -0,0 +1,12 @@
1
+ import BaseCommand from '../../../base-command.js';
2
+ export default class PlatformList extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ };
10
+ run(): Promise<void>;
11
+ private loadCredentials;
12
+ }
@@ -0,0 +1,113 @@
1
+ import { Flags } from '@oclif/core';
2
+ import * as yaml from 'js-yaml';
3
+ import * as fs from 'node:fs';
4
+ import * as os from 'node:os';
5
+ import * as path from 'node:path';
6
+ import BaseCommand from '../../../base-command.js';
7
+ export default class PlatformList extends BaseCommand {
8
+ static description = 'List all platforms';
9
+ static examples = [
10
+ `$ xano platform list
11
+ Platforms:
12
+ ID: 23629 | Helm: 0.1.356 | Created: 2025-11-28
13
+ `,
14
+ `$ xano platform list --output json`,
15
+ ];
16
+ static flags = {
17
+ ...BaseCommand.baseFlags,
18
+ output: Flags.string({
19
+ char: 'o',
20
+ default: 'summary',
21
+ description: 'Output format',
22
+ options: ['summary', 'json'],
23
+ required: false,
24
+ }),
25
+ };
26
+ async run() {
27
+ const { flags } = await this.parse(PlatformList);
28
+ const profileName = flags.profile || this.getDefaultProfile();
29
+ const credentials = this.loadCredentials();
30
+ if (!(profileName in credentials.profiles)) {
31
+ this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
32
+ `Create a profile using 'xano profile create'`);
33
+ }
34
+ const profile = credentials.profiles[profileName];
35
+ if (!profile.instance_origin) {
36
+ this.error(`Profile '${profileName}' is missing instance_origin`);
37
+ }
38
+ if (!profile.access_token) {
39
+ this.error(`Profile '${profileName}' is missing access_token`);
40
+ }
41
+ const apiUrl = `${profile.instance_origin}/api:meta/platform`;
42
+ try {
43
+ const response = await this.verboseFetch(apiUrl, {
44
+ headers: {
45
+ 'accept': 'application/json',
46
+ 'Authorization': `Bearer ${profile.access_token}`,
47
+ },
48
+ method: 'GET',
49
+ }, flags.verbose, profile.access_token);
50
+ if (!response.ok) {
51
+ const errorText = await response.text();
52
+ this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
53
+ }
54
+ const data = await response.json();
55
+ let platforms;
56
+ if (Array.isArray(data)) {
57
+ platforms = data;
58
+ }
59
+ else if (data && typeof data === 'object' && 'items' in data && Array.isArray(data.items)) {
60
+ platforms = data.items;
61
+ }
62
+ else {
63
+ this.error('Unexpected API response format');
64
+ }
65
+ if (flags.output === 'json') {
66
+ this.log(JSON.stringify(platforms, null, 2));
67
+ }
68
+ else {
69
+ if (platforms.length === 0) {
70
+ this.log('No platforms found');
71
+ }
72
+ else {
73
+ this.log('Platforms:');
74
+ for (const platform of platforms) {
75
+ const label = platform.name || `ID: ${platform.id}`;
76
+ const helmTag = platform.helm?.tag ? ` | Helm: ${platform.helm.tag}` : '';
77
+ const created = platform.created_at
78
+ ? ` | Created: ${new Date(platform.created_at).toISOString().split('T')[0]}`
79
+ : '';
80
+ this.log(` ${label}${helmTag}${created}`);
81
+ }
82
+ }
83
+ }
84
+ }
85
+ catch (error) {
86
+ if (error instanceof Error) {
87
+ this.error(`Failed to list platforms: ${error.message}`);
88
+ }
89
+ else {
90
+ this.error(`Failed to list platforms: ${String(error)}`);
91
+ }
92
+ }
93
+ }
94
+ loadCredentials() {
95
+ const configDir = path.join(os.homedir(), '.xano');
96
+ const credentialsPath = path.join(configDir, 'credentials.yaml');
97
+ if (!fs.existsSync(credentialsPath)) {
98
+ this.error(`Credentials file not found at ${credentialsPath}\n` +
99
+ `Create a profile using 'xano profile create'`);
100
+ }
101
+ try {
102
+ const fileContent = fs.readFileSync(credentialsPath, 'utf8');
103
+ const parsed = yaml.load(fileContent);
104
+ if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
105
+ this.error('Credentials file has invalid format.');
106
+ }
107
+ return parsed;
108
+ }
109
+ catch (error) {
110
+ this.error(`Failed to parse credentials file: ${error}`);
111
+ }
112
+ }
113
+ }
@@ -0,0 +1,18 @@
1
+ import BaseCommand from '../../../base-command.js';
2
+ export default class ReleaseCreate extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ branch: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ hotfix: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ name: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ 'table-ids': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
+ };
16
+ run(): Promise<void>;
17
+ private loadCredentials;
18
+ }
@@ -0,0 +1,138 @@
1
+ import { Flags } from '@oclif/core';
2
+ import * as yaml from 'js-yaml';
3
+ import * as fs from 'node:fs';
4
+ import * as os from 'node:os';
5
+ import * as path from 'node:path';
6
+ import BaseCommand from '../../../base-command.js';
7
+ export default class ReleaseCreate extends BaseCommand {
8
+ static description = 'Create a new release in a workspace';
9
+ static examples = [
10
+ `$ xano release create --name "v1.0" --branch main
11
+ Created release: v1.0 - ID: 10
12
+ `,
13
+ `$ xano release create --name "v1.1-hotfix" --branch main --hotfix --description "Critical fix" -o json`,
14
+ ];
15
+ static flags = {
16
+ ...BaseCommand.baseFlags,
17
+ branch: Flags.string({
18
+ char: 'b',
19
+ description: 'Branch to create the release from',
20
+ required: true,
21
+ }),
22
+ description: Flags.string({
23
+ char: 'd',
24
+ description: 'Release description',
25
+ required: false,
26
+ }),
27
+ hotfix: Flags.boolean({
28
+ default: false,
29
+ description: 'Mark as a hotfix release',
30
+ required: false,
31
+ }),
32
+ name: Flags.string({
33
+ char: 'n',
34
+ description: 'Name for the release',
35
+ required: true,
36
+ }),
37
+ output: Flags.string({
38
+ char: 'o',
39
+ default: 'summary',
40
+ description: 'Output format',
41
+ options: ['summary', 'json'],
42
+ required: false,
43
+ }),
44
+ 'table-ids': Flags.string({
45
+ description: 'Comma-separated table IDs to include',
46
+ required: false,
47
+ }),
48
+ workspace: Flags.string({
49
+ char: 'w',
50
+ description: 'Workspace ID (uses profile workspace if not provided)',
51
+ required: false,
52
+ }),
53
+ };
54
+ async run() {
55
+ const { flags } = await this.parse(ReleaseCreate);
56
+ const profileName = flags.profile || this.getDefaultProfile();
57
+ const credentials = this.loadCredentials();
58
+ if (!(profileName in credentials.profiles)) {
59
+ this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
60
+ `Create a profile using 'xano profile create'`);
61
+ }
62
+ const profile = credentials.profiles[profileName];
63
+ if (!profile.instance_origin) {
64
+ this.error(`Profile '${profileName}' is missing instance_origin`);
65
+ }
66
+ if (!profile.access_token) {
67
+ this.error(`Profile '${profileName}' is missing access_token`);
68
+ }
69
+ const workspaceId = flags.workspace || profile.workspace;
70
+ if (!workspaceId) {
71
+ this.error('No workspace ID provided. Use --workspace flag or set one in your profile.');
72
+ }
73
+ const body = {
74
+ branch: flags.branch,
75
+ hotfix: flags.hotfix,
76
+ name: flags.name,
77
+ };
78
+ if (flags.description)
79
+ body.description = flags.description;
80
+ if (flags['table-ids']) {
81
+ body.table_ids = flags['table-ids'].split(',').map(id => Number.parseInt(id.trim(), 10));
82
+ }
83
+ const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/release`;
84
+ try {
85
+ const response = await this.verboseFetch(apiUrl, {
86
+ body: JSON.stringify(body),
87
+ headers: {
88
+ 'accept': 'application/json',
89
+ 'Authorization': `Bearer ${profile.access_token}`,
90
+ 'Content-Type': 'application/json',
91
+ },
92
+ method: 'POST',
93
+ }, flags.verbose, profile.access_token);
94
+ if (!response.ok) {
95
+ const errorText = await response.text();
96
+ this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
97
+ }
98
+ const release = await response.json();
99
+ if (flags.output === 'json') {
100
+ this.log(JSON.stringify(release, null, 2));
101
+ }
102
+ else {
103
+ this.log(`Created release: ${release.name} - ID: ${release.id}`);
104
+ if (release.branch)
105
+ this.log(` Branch: ${release.branch}`);
106
+ if (release.hotfix)
107
+ this.log(` Hotfix: true`);
108
+ }
109
+ }
110
+ catch (error) {
111
+ if (error instanceof Error) {
112
+ this.error(`Failed to create release: ${error.message}`);
113
+ }
114
+ else {
115
+ this.error(`Failed to create release: ${String(error)}`);
116
+ }
117
+ }
118
+ }
119
+ loadCredentials() {
120
+ const configDir = path.join(os.homedir(), '.xano');
121
+ const credentialsPath = path.join(configDir, 'credentials.yaml');
122
+ if (!fs.existsSync(credentialsPath)) {
123
+ this.error(`Credentials file not found at ${credentialsPath}\n` +
124
+ `Create a profile using 'xano profile create'`);
125
+ }
126
+ try {
127
+ const fileContent = fs.readFileSync(credentialsPath, 'utf8');
128
+ const parsed = yaml.load(fileContent);
129
+ if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
130
+ this.error('Credentials file has invalid format.');
131
+ }
132
+ return parsed;
133
+ }
134
+ catch (error) {
135
+ this.error(`Failed to parse credentials file: ${error}`);
136
+ }
137
+ }
138
+ }
@@ -0,0 +1,21 @@
1
+ import BaseCommand from '../../../base-command.js';
2
+ export default class ReleaseDelete extends BaseCommand {
3
+ static args: {
4
+ release_id: import("@oclif/core/interfaces").Arg<number, {
5
+ max?: number;
6
+ min?: number;
7
+ }>;
8
+ };
9
+ static description: string;
10
+ static examples: string[];
11
+ static flags: {
12
+ force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
+ output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
14
+ workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
+ };
18
+ run(): Promise<void>;
19
+ private confirm;
20
+ private loadCredentials;
21
+ }
@@ -0,0 +1,134 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import * as yaml from 'js-yaml';
3
+ import * as fs from 'node:fs';
4
+ import * as os from 'node:os';
5
+ import * as path from 'node:path';
6
+ import BaseCommand from '../../../base-command.js';
7
+ export default class ReleaseDelete extends BaseCommand {
8
+ static args = {
9
+ release_id: Args.integer({
10
+ description: 'Release ID to delete',
11
+ required: true,
12
+ }),
13
+ };
14
+ static description = 'Delete a release permanently. This action cannot be undone.';
15
+ static examples = [
16
+ `$ xano release delete 10
17
+ Are you sure you want to delete release #10? This action cannot be undone. (y/N) y
18
+ Deleted release #10
19
+ `,
20
+ `$ xano release delete 10 --force
21
+ Deleted release #10
22
+ `,
23
+ `$ xano release delete 10 -f -o json`,
24
+ ];
25
+ static flags = {
26
+ ...BaseCommand.baseFlags,
27
+ force: Flags.boolean({
28
+ char: 'f',
29
+ default: false,
30
+ description: 'Skip confirmation prompt',
31
+ required: false,
32
+ }),
33
+ output: Flags.string({
34
+ char: 'o',
35
+ default: 'summary',
36
+ description: 'Output format',
37
+ options: ['summary', 'json'],
38
+ required: false,
39
+ }),
40
+ workspace: Flags.string({
41
+ char: 'w',
42
+ description: 'Workspace ID (uses profile workspace if not provided)',
43
+ required: false,
44
+ }),
45
+ };
46
+ async run() {
47
+ const { args, flags } = await this.parse(ReleaseDelete);
48
+ const profileName = flags.profile || this.getDefaultProfile();
49
+ const credentials = this.loadCredentials();
50
+ if (!(profileName in credentials.profiles)) {
51
+ this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
52
+ `Create a profile using 'xano profile create'`);
53
+ }
54
+ const profile = credentials.profiles[profileName];
55
+ if (!profile.instance_origin) {
56
+ this.error(`Profile '${profileName}' is missing instance_origin`);
57
+ }
58
+ if (!profile.access_token) {
59
+ this.error(`Profile '${profileName}' is missing access_token`);
60
+ }
61
+ const workspaceId = flags.workspace || profile.workspace;
62
+ if (!workspaceId) {
63
+ this.error('No workspace ID provided. Use --workspace flag or set one in your profile.');
64
+ }
65
+ const releaseId = args.release_id;
66
+ if (!flags.force) {
67
+ const confirmed = await this.confirm(`Are you sure you want to delete release #${releaseId}? This action cannot be undone.`);
68
+ if (!confirmed) {
69
+ this.log('Deletion cancelled.');
70
+ return;
71
+ }
72
+ }
73
+ const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/release/${releaseId}`;
74
+ try {
75
+ const response = await this.verboseFetch(apiUrl, {
76
+ headers: {
77
+ 'accept': 'application/json',
78
+ 'Authorization': `Bearer ${profile.access_token}`,
79
+ },
80
+ method: 'DELETE',
81
+ }, flags.verbose, profile.access_token);
82
+ if (!response.ok) {
83
+ const errorText = await response.text();
84
+ this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
85
+ }
86
+ if (flags.output === 'json') {
87
+ this.log(JSON.stringify({ deleted: true, release_id: releaseId }, null, 2));
88
+ }
89
+ else {
90
+ this.log(`Deleted release #${releaseId}`);
91
+ }
92
+ }
93
+ catch (error) {
94
+ if (error instanceof Error) {
95
+ this.error(`Failed to delete release: ${error.message}`);
96
+ }
97
+ else {
98
+ this.error(`Failed to delete release: ${String(error)}`);
99
+ }
100
+ }
101
+ }
102
+ async confirm(message) {
103
+ const readline = await import('node:readline');
104
+ const rl = readline.createInterface({
105
+ input: process.stdin,
106
+ output: process.stdout,
107
+ });
108
+ return new Promise((resolve) => {
109
+ rl.question(`${message} (y/N) `, (answer) => {
110
+ rl.close();
111
+ resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
112
+ });
113
+ });
114
+ }
115
+ loadCredentials() {
116
+ const configDir = path.join(os.homedir(), '.xano');
117
+ const credentialsPath = path.join(configDir, 'credentials.yaml');
118
+ if (!fs.existsSync(credentialsPath)) {
119
+ this.error(`Credentials file not found at ${credentialsPath}\n` +
120
+ `Create a profile using 'xano profile create'`);
121
+ }
122
+ try {
123
+ const fileContent = fs.readFileSync(credentialsPath, 'utf8');
124
+ const parsed = yaml.load(fileContent);
125
+ if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
126
+ this.error('Credentials file has invalid format.');
127
+ }
128
+ return parsed;
129
+ }
130
+ catch (error) {
131
+ this.error(`Failed to parse credentials file: ${error}`);
132
+ }
133
+ }
134
+ }
@@ -0,0 +1,21 @@
1
+ import BaseCommand from '../../../base-command.js';
2
+ export default class ReleaseEdit extends BaseCommand {
3
+ static args: {
4
+ release_id: import("@oclif/core/interfaces").Arg<number, {
5
+ max?: number;
6
+ min?: number;
7
+ }>;
8
+ };
9
+ static description: string;
10
+ static examples: string[];
11
+ static flags: {
12
+ description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
15
+ workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
18
+ };
19
+ run(): Promise<void>;
20
+ private loadCredentials;
21
+ }