@xano/cli 0.0.36 → 0.0.39

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 (115) hide show
  1. package/README.md +325 -102
  2. package/dist/commands/auth/index.d.ts +0 -2
  3. package/dist/commands/auth/index.js +2 -55
  4. package/dist/commands/profile/create/index.d.ts +0 -2
  5. package/dist/commands/profile/create/index.js +0 -15
  6. package/dist/commands/profile/edit/index.d.ts +0 -4
  7. package/dist/commands/profile/edit/index.js +7 -38
  8. package/dist/commands/profile/wizard/index.d.ts +0 -2
  9. package/dist/commands/profile/wizard/index.js +0 -106
  10. package/dist/commands/profile/{project → workspace}/index.d.ts +1 -1
  11. package/dist/commands/profile/{project → workspace}/index.js +10 -10
  12. package/dist/commands/release/delete/index.d.ts +2 -4
  13. package/dist/commands/release/delete/index.js +39 -12
  14. package/dist/commands/release/edit/index.d.ts +2 -4
  15. package/dist/commands/release/edit/index.js +31 -5
  16. package/dist/commands/release/export/index.d.ts +2 -4
  17. package/dist/commands/release/export/index.js +39 -11
  18. package/dist/commands/release/get/index.d.ts +2 -4
  19. package/dist/commands/release/get/index.js +31 -5
  20. package/dist/commands/release/pull/index.d.ts +31 -0
  21. package/dist/commands/release/pull/index.js +345 -0
  22. package/dist/commands/release/push/index.d.ts +26 -0
  23. package/dist/commands/release/push/index.js +230 -0
  24. package/dist/commands/tenant/backup/delete/index.d.ts +1 -1
  25. package/dist/commands/tenant/backup/delete/index.js +8 -9
  26. package/dist/commands/tenant/backup/export/index.d.ts +1 -1
  27. package/dist/commands/tenant/backup/export/index.js +9 -10
  28. package/dist/commands/tenant/backup/restore/index.d.ts +1 -1
  29. package/dist/commands/tenant/backup/restore/index.js +8 -9
  30. package/dist/commands/tenant/cluster/create/index.d.ts +18 -0
  31. package/dist/commands/tenant/cluster/create/index.js +149 -0
  32. package/dist/commands/{run/sessions/start → tenant/cluster/delete}/index.d.ts +9 -3
  33. package/dist/commands/tenant/cluster/delete/index.js +125 -0
  34. package/dist/commands/tenant/cluster/edit/index.d.ts +22 -0
  35. package/dist/commands/tenant/cluster/edit/index.js +128 -0
  36. package/dist/commands/{run/sessions → tenant/cluster}/get/index.d.ts +7 -3
  37. package/dist/commands/tenant/cluster/get/index.js +114 -0
  38. package/dist/commands/{run/info → tenant/cluster/license/get}/index.d.ts +10 -7
  39. package/dist/commands/tenant/cluster/license/get/index.js +118 -0
  40. package/dist/commands/tenant/cluster/license/set/index.d.ts +21 -0
  41. package/dist/commands/tenant/cluster/license/set/index.js +132 -0
  42. package/dist/commands/{run/env → tenant/cluster}/list/index.d.ts +3 -3
  43. package/dist/commands/tenant/cluster/list/index.js +109 -0
  44. package/dist/commands/tenant/create/index.d.ts +6 -3
  45. package/dist/commands/tenant/create/index.js +28 -20
  46. package/dist/commands/tenant/deploy_platform/index.d.ts +1 -1
  47. package/dist/commands/tenant/deploy_platform/index.js +8 -9
  48. package/dist/commands/tenant/deploy_release/index.d.ts +1 -1
  49. package/dist/commands/tenant/deploy_release/index.js +8 -9
  50. package/dist/commands/tenant/env/delete/index.d.ts +19 -0
  51. package/dist/commands/tenant/env/delete/index.js +139 -0
  52. package/dist/commands/{run/projects/create → tenant/env/get}/index.d.ts +7 -4
  53. package/dist/commands/tenant/env/get/index.js +113 -0
  54. package/dist/commands/{run/projects/update → tenant/env/get_all}/index.d.ts +7 -5
  55. package/dist/commands/tenant/env/get_all/index.js +123 -0
  56. package/dist/commands/{run/secrets/get → tenant/env/list}/index.d.ts +5 -3
  57. package/dist/commands/tenant/env/list/index.js +116 -0
  58. package/dist/commands/tenant/env/set/index.d.ts +18 -0
  59. package/dist/commands/tenant/env/set/index.js +122 -0
  60. package/dist/commands/tenant/env/set_all/index.d.ts +18 -0
  61. package/dist/commands/tenant/env/set_all/index.js +131 -0
  62. package/dist/commands/tenant/get/index.js +6 -5
  63. package/dist/commands/tenant/impersonate/index.d.ts +19 -0
  64. package/dist/commands/tenant/impersonate/index.js +146 -0
  65. package/dist/commands/tenant/license/get/index.d.ts +18 -0
  66. package/dist/commands/tenant/license/get/index.js +127 -0
  67. package/dist/commands/tenant/license/set/index.d.ts +19 -0
  68. package/dist/commands/tenant/license/set/index.js +141 -0
  69. package/dist/commands/tenant/list/index.js +6 -6
  70. package/dist/commands/tenant/pull/index.d.ts +31 -0
  71. package/dist/commands/tenant/pull/index.js +327 -0
  72. package/dist/commands/tenant/push/index.d.ts +24 -0
  73. package/dist/commands/tenant/push/index.js +245 -0
  74. package/dist/commands/workspace/push/index.js +21 -6
  75. package/oclif.manifest.json +2323 -1918
  76. package/package.json +1 -19
  77. package/dist/commands/run/env/delete/index.d.ts +0 -14
  78. package/dist/commands/run/env/delete/index.js +0 -65
  79. package/dist/commands/run/env/get/index.d.ts +0 -14
  80. package/dist/commands/run/env/get/index.js +0 -52
  81. package/dist/commands/run/env/list/index.js +0 -56
  82. package/dist/commands/run/env/set/index.d.ts +0 -14
  83. package/dist/commands/run/env/set/index.js +0 -51
  84. package/dist/commands/run/exec/index.d.ts +0 -31
  85. package/dist/commands/run/exec/index.js +0 -431
  86. package/dist/commands/run/info/index.js +0 -160
  87. package/dist/commands/run/projects/create/index.js +0 -75
  88. package/dist/commands/run/projects/delete/index.d.ts +0 -14
  89. package/dist/commands/run/projects/delete/index.js +0 -65
  90. package/dist/commands/run/projects/list/index.d.ts +0 -13
  91. package/dist/commands/run/projects/list/index.js +0 -66
  92. package/dist/commands/run/projects/update/index.js +0 -86
  93. package/dist/commands/run/secrets/delete/index.d.ts +0 -14
  94. package/dist/commands/run/secrets/delete/index.js +0 -65
  95. package/dist/commands/run/secrets/get/index.js +0 -52
  96. package/dist/commands/run/secrets/list/index.d.ts +0 -12
  97. package/dist/commands/run/secrets/list/index.js +0 -60
  98. package/dist/commands/run/secrets/set/index.d.ts +0 -16
  99. package/dist/commands/run/secrets/set/index.js +0 -74
  100. package/dist/commands/run/sessions/delete/index.d.ts +0 -14
  101. package/dist/commands/run/sessions/delete/index.js +0 -65
  102. package/dist/commands/run/sessions/get/index.js +0 -72
  103. package/dist/commands/run/sessions/list/index.d.ts +0 -13
  104. package/dist/commands/run/sessions/list/index.js +0 -64
  105. package/dist/commands/run/sessions/start/index.js +0 -56
  106. package/dist/commands/run/sessions/stop/index.d.ts +0 -14
  107. package/dist/commands/run/sessions/stop/index.js +0 -56
  108. package/dist/commands/run/sink/get/index.d.ts +0 -14
  109. package/dist/commands/run/sink/get/index.js +0 -63
  110. package/dist/lib/base-run-command.d.ts +0 -41
  111. package/dist/lib/base-run-command.js +0 -75
  112. package/dist/lib/run-http-client.d.ts +0 -64
  113. package/dist/lib/run-http-client.js +0 -171
  114. package/dist/lib/run-types.d.ts +0 -226
  115. package/dist/lib/run-types.js +0 -5
@@ -0,0 +1,125 @@
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 TenantClusterDelete extends BaseCommand {
8
+ static args = {
9
+ cluster_id: Args.integer({
10
+ description: 'Cluster ID to delete',
11
+ required: true,
12
+ }),
13
+ };
14
+ static description = 'Delete a tenant cluster. This action cannot be undone.';
15
+ static examples = [
16
+ `$ xano tenant cluster delete 3
17
+ Are you sure you want to delete tenant cluster 3? This action cannot be undone. (y/N) y
18
+ Tenant cluster 3 deleted successfully
19
+ `,
20
+ `$ xano tenant cluster delete 3 --force
21
+ Tenant cluster 3 deleted successfully
22
+ `,
23
+ `$ xano tenant cluster delete 3 -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
+ };
41
+ async run() {
42
+ const { args, flags } = await this.parse(TenantClusterDelete);
43
+ const profileName = flags.profile || this.getDefaultProfile();
44
+ const credentials = this.loadCredentials();
45
+ if (!(profileName in credentials.profiles)) {
46
+ this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
47
+ `Create a profile using 'xano profile create'`);
48
+ }
49
+ const profile = credentials.profiles[profileName];
50
+ if (!profile.instance_origin) {
51
+ this.error(`Profile '${profileName}' is missing instance_origin`);
52
+ }
53
+ if (!profile.access_token) {
54
+ this.error(`Profile '${profileName}' is missing access_token`);
55
+ }
56
+ const clusterId = args.cluster_id;
57
+ if (!flags.force) {
58
+ const confirmed = await this.confirm(`Are you sure you want to delete tenant cluster ${clusterId}? This action cannot be undone.`);
59
+ if (!confirmed) {
60
+ this.log('Deletion cancelled.');
61
+ return;
62
+ }
63
+ }
64
+ const apiUrl = `${profile.instance_origin}/api:meta/tenant/cluster/${clusterId}`;
65
+ try {
66
+ const response = await this.verboseFetch(apiUrl, {
67
+ headers: {
68
+ 'accept': 'application/json',
69
+ 'Authorization': `Bearer ${profile.access_token}`,
70
+ },
71
+ method: 'DELETE',
72
+ }, flags.verbose, profile.access_token);
73
+ if (!response.ok) {
74
+ const errorText = await response.text();
75
+ this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
76
+ }
77
+ if (flags.output === 'json') {
78
+ this.log(JSON.stringify({ cluster_id: clusterId, deleted: true }, null, 2));
79
+ }
80
+ else {
81
+ this.log(`Tenant cluster ${clusterId} deleted successfully`);
82
+ }
83
+ }
84
+ catch (error) {
85
+ if (error instanceof Error) {
86
+ this.error(`Failed to delete tenant cluster: ${error.message}`);
87
+ }
88
+ else {
89
+ this.error(`Failed to delete tenant cluster: ${String(error)}`);
90
+ }
91
+ }
92
+ }
93
+ async confirm(message) {
94
+ const readline = await import('node:readline');
95
+ const rl = readline.createInterface({
96
+ input: process.stdin,
97
+ output: process.stdout,
98
+ });
99
+ return new Promise((resolve) => {
100
+ rl.question(`${message} (y/N) `, (answer) => {
101
+ rl.close();
102
+ resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
103
+ });
104
+ });
105
+ }
106
+ loadCredentials() {
107
+ const configDir = path.join(os.homedir(), '.xano');
108
+ const credentialsPath = path.join(configDir, 'credentials.yaml');
109
+ if (!fs.existsSync(credentialsPath)) {
110
+ this.error(`Credentials file not found at ${credentialsPath}\n` +
111
+ `Create a profile using 'xano profile create'`);
112
+ }
113
+ try {
114
+ const fileContent = fs.readFileSync(credentialsPath, 'utf8');
115
+ const parsed = yaml.load(fileContent);
116
+ if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
117
+ this.error('Credentials file has invalid format.');
118
+ }
119
+ return parsed;
120
+ }
121
+ catch (error) {
122
+ this.error(`Failed to parse credentials file: ${error}`);
123
+ }
124
+ }
125
+ }
@@ -0,0 +1,22 @@
1
+ import BaseCommand from '../../../../base-command.js';
2
+ export default class TenantClusterEdit extends BaseCommand {
3
+ static args: {
4
+ cluster_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, import("@oclif/core/interfaces").CustomOptions>;
13
+ domain: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
14
+ name: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
15
+ output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
16
+ type: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
17
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
19
+ };
20
+ run(): Promise<void>;
21
+ private loadCredentials;
22
+ }
@@ -0,0 +1,128 @@
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 TenantClusterEdit extends BaseCommand {
8
+ static args = {
9
+ cluster_id: Args.integer({
10
+ description: 'Cluster ID to edit',
11
+ required: true,
12
+ }),
13
+ };
14
+ static description = 'Update an existing tenant cluster';
15
+ static examples = [
16
+ `$ xano tenant cluster edit 1 --name "us-east-1-updated" --description "Updated cluster" --domain "us-east.xano.io" --type standard
17
+ Updated tenant cluster: us-east-1-updated (standard) - ID: 1
18
+ `,
19
+ `$ xano tenant cluster edit 1 --name "eu-west" --description "" --domain "" --type run -o json`,
20
+ ];
21
+ static flags = {
22
+ ...BaseCommand.baseFlags,
23
+ description: Flags.string({
24
+ char: 'd',
25
+ description: 'Cluster description',
26
+ required: true,
27
+ }),
28
+ domain: Flags.string({
29
+ description: 'Custom domain for the cluster',
30
+ required: true,
31
+ }),
32
+ name: Flags.string({
33
+ char: 'n',
34
+ description: 'Cluster name',
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
+ type: Flags.string({
45
+ description: 'Cluster type',
46
+ options: ['standard', 'run'],
47
+ required: true,
48
+ }),
49
+ };
50
+ async run() {
51
+ const { args, flags } = await this.parse(TenantClusterEdit);
52
+ const profileName = flags.profile || this.getDefaultProfile();
53
+ const credentials = this.loadCredentials();
54
+ if (!(profileName in credentials.profiles)) {
55
+ this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
56
+ `Create a profile using 'xano profile create'`);
57
+ }
58
+ const profile = credentials.profiles[profileName];
59
+ if (!profile.instance_origin) {
60
+ this.error(`Profile '${profileName}' is missing instance_origin`);
61
+ }
62
+ if (!profile.access_token) {
63
+ this.error(`Profile '${profileName}' is missing access_token`);
64
+ }
65
+ const clusterId = args.cluster_id;
66
+ const body = {
67
+ description: flags.description,
68
+ domain: flags.domain,
69
+ name: flags.name,
70
+ type: flags.type,
71
+ };
72
+ const apiUrl = `${profile.instance_origin}/api:meta/tenant/cluster/${clusterId}`;
73
+ try {
74
+ const response = await this.verboseFetch(apiUrl, {
75
+ body: JSON.stringify(body),
76
+ headers: {
77
+ 'accept': 'application/json',
78
+ 'Authorization': `Bearer ${profile.access_token}`,
79
+ 'Content-Type': 'application/json',
80
+ },
81
+ method: 'PUT',
82
+ }, flags.verbose, profile.access_token);
83
+ if (!response.ok) {
84
+ const errorText = await response.text();
85
+ this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
86
+ }
87
+ const cluster = await response.json();
88
+ if (flags.output === 'json') {
89
+ this.log(JSON.stringify(cluster, null, 2));
90
+ }
91
+ else {
92
+ const type = cluster.type ? ` (${cluster.type})` : '';
93
+ this.log(`Updated tenant cluster: ${cluster.name}${type} - ID: ${cluster.id}`);
94
+ if (cluster.description)
95
+ this.log(` Description: ${cluster.description}`);
96
+ if (cluster.domain)
97
+ this.log(` Domain: ${cluster.domain}`);
98
+ }
99
+ }
100
+ catch (error) {
101
+ if (error instanceof Error) {
102
+ this.error(`Failed to edit tenant cluster: ${error.message}`);
103
+ }
104
+ else {
105
+ this.error(`Failed to edit tenant cluster: ${String(error)}`);
106
+ }
107
+ }
108
+ }
109
+ loadCredentials() {
110
+ const configDir = path.join(os.homedir(), '.xano');
111
+ const credentialsPath = path.join(configDir, 'credentials.yaml');
112
+ if (!fs.existsSync(credentialsPath)) {
113
+ this.error(`Credentials file not found at ${credentialsPath}\n` +
114
+ `Create a profile using 'xano profile create'`);
115
+ }
116
+ try {
117
+ const fileContent = fs.readFileSync(credentialsPath, 'utf8');
118
+ const parsed = yaml.load(fileContent);
119
+ if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
120
+ this.error('Credentials file has invalid format.');
121
+ }
122
+ return parsed;
123
+ }
124
+ catch (error) {
125
+ this.error(`Failed to parse credentials file: ${error}`);
126
+ }
127
+ }
128
+ }
@@ -1,7 +1,10 @@
1
- import BaseRunCommand from '../../../../lib/base-run-command.js';
2
- export default class RunSessionsGet extends BaseRunCommand {
1
+ import BaseCommand from '../../../../base-command.js';
2
+ export default class TenantClusterGet extends BaseCommand {
3
3
  static args: {
4
- sessionId: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
4
+ cluster_id: import("@oclif/core/interfaces").Arg<number, {
5
+ max?: number;
6
+ min?: number;
7
+ }>;
5
8
  };
6
9
  static description: string;
7
10
  static examples: string[];
@@ -11,4 +14,5 @@ export default class RunSessionsGet extends BaseRunCommand {
11
14
  verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
15
  };
13
16
  run(): Promise<void>;
17
+ private loadCredentials;
14
18
  }
@@ -0,0 +1,114 @@
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 TenantClusterGet extends BaseCommand {
8
+ static args = {
9
+ cluster_id: Args.integer({
10
+ description: 'Cluster ID to retrieve',
11
+ required: true,
12
+ }),
13
+ };
14
+ static description = 'Get details of a specific tenant cluster';
15
+ static examples = [
16
+ `$ xano tenant cluster get 1
17
+ Cluster: us-east-1
18
+ ID: 1
19
+ Type: standard
20
+ Domain: us-east-1.xano.io
21
+ `,
22
+ `$ xano tenant cluster get 1 -o json`,
23
+ ];
24
+ static flags = {
25
+ ...BaseCommand.baseFlags,
26
+ output: Flags.string({
27
+ char: 'o',
28
+ default: 'summary',
29
+ description: 'Output format',
30
+ options: ['summary', 'json'],
31
+ required: false,
32
+ }),
33
+ };
34
+ async run() {
35
+ const { args, flags } = await this.parse(TenantClusterGet);
36
+ const profileName = flags.profile || this.getDefaultProfile();
37
+ const credentials = this.loadCredentials();
38
+ if (!(profileName in credentials.profiles)) {
39
+ this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
40
+ `Create a profile using 'xano profile create'`);
41
+ }
42
+ const profile = credentials.profiles[profileName];
43
+ if (!profile.instance_origin) {
44
+ this.error(`Profile '${profileName}' is missing instance_origin`);
45
+ }
46
+ if (!profile.access_token) {
47
+ this.error(`Profile '${profileName}' is missing access_token`);
48
+ }
49
+ const clusterId = args.cluster_id;
50
+ const apiUrl = `${profile.instance_origin}/api:meta/tenant/cluster/${clusterId}`;
51
+ try {
52
+ const response = await this.verboseFetch(apiUrl, {
53
+ headers: {
54
+ accept: 'application/json',
55
+ Authorization: `Bearer ${profile.access_token}`,
56
+ },
57
+ method: 'GET',
58
+ }, flags.verbose, profile.access_token);
59
+ if (!response.ok) {
60
+ const errorText = await response.text();
61
+ this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
62
+ }
63
+ const cluster = (await response.json());
64
+ if (flags.output === 'json') {
65
+ this.log(JSON.stringify(cluster, null, 2));
66
+ }
67
+ else {
68
+ this.log(`Cluster: ${cluster.name}`);
69
+ this.log(` ID: ${cluster.id}`);
70
+ if (cluster.type)
71
+ this.log(` Type: ${cluster.type}`);
72
+ if (cluster.description)
73
+ this.log(` Description: ${cluster.description}`);
74
+ if (cluster.domain)
75
+ this.log(` Domain: ${cluster.domain}`);
76
+ if (cluster.warm)
77
+ this.log(` Warm: ${JSON.stringify(cluster.warm)}`);
78
+ if (cluster.ingress)
79
+ this.log(` Ingress: ${JSON.stringify(cluster.ingress)}`);
80
+ if (cluster.created_at) {
81
+ const d = new Date(cluster.created_at);
82
+ const createdDate = Number.isNaN(d.getTime()) ? cluster.created_at : d.toISOString().split('T')[0];
83
+ this.log(` Created: ${createdDate}`);
84
+ }
85
+ }
86
+ }
87
+ catch (error) {
88
+ if (error instanceof Error) {
89
+ this.error(`Failed to get tenant cluster: ${error.message}`);
90
+ }
91
+ else {
92
+ this.error(`Failed to get tenant cluster: ${String(error)}`);
93
+ }
94
+ }
95
+ }
96
+ loadCredentials() {
97
+ const configDir = path.join(os.homedir(), '.xano');
98
+ const credentialsPath = path.join(configDir, 'credentials.yaml');
99
+ if (!fs.existsSync(credentialsPath)) {
100
+ this.error(`Credentials file not found at ${credentialsPath}\n` + `Create a profile using 'xano profile create'`);
101
+ }
102
+ try {
103
+ const fileContent = fs.readFileSync(credentialsPath, 'utf8');
104
+ const parsed = yaml.load(fileContent);
105
+ if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
106
+ this.error('Credentials file has invalid format.');
107
+ }
108
+ return parsed;
109
+ }
110
+ catch (error) {
111
+ this.error(`Failed to parse credentials file: ${error}`);
112
+ }
113
+ }
114
+ }
@@ -1,17 +1,20 @@
1
- import BaseRunCommand from '../../../lib/base-run-command.js';
2
- export default class RunInfo extends BaseRunCommand {
3
- static args: {};
1
+ import BaseCommand from '../../../../../base-command.js';
2
+ export default class TenantClusterLicenseGet extends BaseCommand {
3
+ static args: {
4
+ cluster_id: import("@oclif/core/interfaces").Arg<number, {
5
+ max?: number;
6
+ min?: number;
7
+ }>;
8
+ };
4
9
  static description: string;
5
10
  static examples: string[];
6
11
  static flags: {
7
12
  file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
13
  output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
- stdin: import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
+ view: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
15
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
16
  verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
17
  };
13
18
  run(): Promise<void>;
14
- private isUrl;
15
- private outputSummary;
16
- private readStdin;
19
+ private loadCredentials;
17
20
  }
@@ -0,0 +1,118 @@
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 TenantClusterLicenseGet extends BaseCommand {
8
+ static args = {
9
+ cluster_id: Args.integer({
10
+ description: 'Tenant cluster ID',
11
+ required: true,
12
+ }),
13
+ };
14
+ static description = 'Get the license (kubeconfig) for a tenant cluster';
15
+ static examples = [
16
+ `$ xano tenant cluster license get 1
17
+ License saved to kubeconfig-1.yaml
18
+ `,
19
+ `$ xano tenant cluster license get 1 --file ./my-kubeconfig.yaml
20
+ License saved to my-kubeconfig.yaml
21
+ `,
22
+ `$ xano tenant cluster license get 1 --view`,
23
+ `$ xano tenant cluster license get 1 -o json`,
24
+ ];
25
+ static flags = {
26
+ ...BaseCommand.baseFlags,
27
+ file: Flags.string({
28
+ char: 'f',
29
+ description: 'Output file path (default: kubeconfig_<cluster_id>.yaml)',
30
+ required: false,
31
+ }),
32
+ output: Flags.string({
33
+ char: 'o',
34
+ default: 'summary',
35
+ description: 'Output format',
36
+ options: ['summary', 'json'],
37
+ required: false,
38
+ }),
39
+ view: Flags.boolean({
40
+ default: false,
41
+ description: 'Print license to stdout instead of saving to file',
42
+ required: false,
43
+ }),
44
+ };
45
+ async run() {
46
+ const { args, flags } = await this.parse(TenantClusterLicenseGet);
47
+ const profileName = flags.profile || this.getDefaultProfile();
48
+ const credentials = this.loadCredentials();
49
+ if (!(profileName in credentials.profiles)) {
50
+ this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
51
+ `Create a profile using 'xano profile create'`);
52
+ }
53
+ const profile = credentials.profiles[profileName];
54
+ if (!profile.instance_origin) {
55
+ this.error(`Profile '${profileName}' is missing instance_origin`);
56
+ }
57
+ if (!profile.access_token) {
58
+ this.error(`Profile '${profileName}' is missing access_token`);
59
+ }
60
+ const clusterId = args.cluster_id;
61
+ const apiUrl = `${profile.instance_origin}/api:meta/tenant/cluster/${clusterId}/license`;
62
+ try {
63
+ const response = await this.verboseFetch(apiUrl, {
64
+ headers: {
65
+ accept: 'application/json',
66
+ Authorization: `Bearer ${profile.access_token}`,
67
+ },
68
+ method: 'GET',
69
+ }, flags.verbose, profile.access_token);
70
+ if (!response.ok) {
71
+ const errorText = await response.text();
72
+ this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
73
+ }
74
+ const license = await response.json();
75
+ // The license is a raw YAML string (kubeconfig) — write it directly, not yaml.dump'd
76
+ const licenseContent = typeof license === 'string' ? license : JSON.stringify(license, null, 2);
77
+ if (flags.view || flags.output === 'json') {
78
+ if (flags.output === 'json') {
79
+ this.log(JSON.stringify(license, null, 2));
80
+ }
81
+ else {
82
+ this.log(licenseContent);
83
+ }
84
+ }
85
+ else {
86
+ const filePath = path.resolve(flags.file || `kubeconfig_${clusterId}.yaml`);
87
+ fs.writeFileSync(filePath, licenseContent, 'utf8');
88
+ this.log(`License saved to ${filePath}`);
89
+ }
90
+ }
91
+ catch (error) {
92
+ if (error instanceof Error) {
93
+ this.error(`Failed to get tenant cluster license: ${error.message}`);
94
+ }
95
+ else {
96
+ this.error(`Failed to get tenant cluster license: ${String(error)}`);
97
+ }
98
+ }
99
+ }
100
+ loadCredentials() {
101
+ const configDir = path.join(os.homedir(), '.xano');
102
+ const credentialsPath = path.join(configDir, 'credentials.yaml');
103
+ if (!fs.existsSync(credentialsPath)) {
104
+ this.error(`Credentials file not found at ${credentialsPath}\n` + `Create a profile using 'xano profile create'`);
105
+ }
106
+ try {
107
+ const fileContent = fs.readFileSync(credentialsPath, 'utf8');
108
+ const parsed = yaml.load(fileContent);
109
+ if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
110
+ this.error('Credentials file has invalid format.');
111
+ }
112
+ return parsed;
113
+ }
114
+ catch (error) {
115
+ this.error(`Failed to parse credentials file: ${error}`);
116
+ }
117
+ }
118
+ }
@@ -0,0 +1,21 @@
1
+ import BaseCommand from '../../../../../base-command.js';
2
+ export default class TenantClusterLicenseSet extends BaseCommand {
3
+ static args: {
4
+ cluster_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
+ clean: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
+ file: 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
+ value: 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
+ }