@xano/cli 0.0.37 → 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.
- package/README.md +325 -102
- package/dist/commands/auth/index.d.ts +0 -2
- package/dist/commands/auth/index.js +2 -55
- package/dist/commands/profile/create/index.d.ts +0 -2
- package/dist/commands/profile/create/index.js +0 -15
- package/dist/commands/profile/edit/index.d.ts +0 -4
- package/dist/commands/profile/edit/index.js +7 -38
- package/dist/commands/profile/wizard/index.d.ts +0 -2
- package/dist/commands/profile/wizard/index.js +0 -106
- package/dist/commands/profile/{project → workspace}/index.d.ts +1 -1
- package/dist/commands/profile/{project → workspace}/index.js +10 -10
- package/dist/commands/release/delete/index.d.ts +2 -4
- package/dist/commands/release/delete/index.js +39 -12
- package/dist/commands/release/edit/index.d.ts +2 -4
- package/dist/commands/release/edit/index.js +31 -5
- package/dist/commands/release/export/index.d.ts +2 -4
- package/dist/commands/release/export/index.js +39 -11
- package/dist/commands/release/get/index.d.ts +2 -4
- package/dist/commands/release/get/index.js +31 -5
- package/dist/commands/release/pull/index.d.ts +31 -0
- package/dist/commands/release/pull/index.js +345 -0
- package/dist/commands/release/push/index.d.ts +26 -0
- package/dist/commands/release/push/index.js +230 -0
- package/dist/commands/tenant/backup/delete/index.d.ts +1 -1
- package/dist/commands/tenant/backup/delete/index.js +8 -9
- package/dist/commands/tenant/backup/export/index.d.ts +1 -1
- package/dist/commands/tenant/backup/export/index.js +9 -10
- package/dist/commands/tenant/backup/restore/index.d.ts +1 -1
- package/dist/commands/tenant/backup/restore/index.js +8 -9
- package/dist/commands/tenant/cluster/create/index.d.ts +18 -0
- package/dist/commands/tenant/cluster/create/index.js +149 -0
- package/dist/commands/{run/sessions/start → tenant/cluster/delete}/index.d.ts +9 -3
- package/dist/commands/tenant/cluster/delete/index.js +125 -0
- package/dist/commands/tenant/cluster/edit/index.d.ts +22 -0
- package/dist/commands/tenant/cluster/edit/index.js +128 -0
- package/dist/commands/{run/sessions → tenant/cluster}/get/index.d.ts +7 -3
- package/dist/commands/tenant/cluster/get/index.js +114 -0
- package/dist/commands/{run/info → tenant/cluster/license/get}/index.d.ts +10 -7
- package/dist/commands/tenant/cluster/license/get/index.js +118 -0
- package/dist/commands/tenant/cluster/license/set/index.d.ts +21 -0
- package/dist/commands/tenant/cluster/license/set/index.js +132 -0
- package/dist/commands/{run/env → tenant/cluster}/list/index.d.ts +3 -3
- package/dist/commands/tenant/cluster/list/index.js +109 -0
- package/dist/commands/tenant/create/index.d.ts +6 -3
- package/dist/commands/tenant/create/index.js +28 -20
- package/dist/commands/tenant/deploy_platform/index.d.ts +1 -1
- package/dist/commands/tenant/deploy_platform/index.js +8 -9
- package/dist/commands/tenant/deploy_release/index.d.ts +1 -1
- package/dist/commands/tenant/deploy_release/index.js +8 -9
- package/dist/commands/tenant/env/delete/index.d.ts +19 -0
- package/dist/commands/tenant/env/delete/index.js +139 -0
- package/dist/commands/{run/projects/create → tenant/env/get}/index.d.ts +7 -4
- package/dist/commands/tenant/env/get/index.js +113 -0
- package/dist/commands/{run/projects/update → tenant/env/get_all}/index.d.ts +7 -5
- package/dist/commands/tenant/env/get_all/index.js +123 -0
- package/dist/commands/{run/secrets/get → tenant/env/list}/index.d.ts +5 -3
- package/dist/commands/tenant/env/list/index.js +116 -0
- package/dist/commands/tenant/env/set/index.d.ts +18 -0
- package/dist/commands/tenant/env/set/index.js +122 -0
- package/dist/commands/tenant/env/set_all/index.d.ts +18 -0
- package/dist/commands/tenant/env/set_all/index.js +131 -0
- package/dist/commands/tenant/get/index.js +6 -5
- package/dist/commands/tenant/impersonate/index.d.ts +19 -0
- package/dist/commands/tenant/impersonate/index.js +146 -0
- package/dist/commands/tenant/license/get/index.d.ts +18 -0
- package/dist/commands/tenant/license/get/index.js +127 -0
- package/dist/commands/tenant/license/set/index.d.ts +19 -0
- package/dist/commands/tenant/license/set/index.js +141 -0
- package/dist/commands/tenant/list/index.js +6 -6
- package/dist/commands/tenant/pull/index.d.ts +31 -0
- package/dist/commands/tenant/pull/index.js +327 -0
- package/dist/commands/tenant/push/index.d.ts +24 -0
- package/dist/commands/tenant/push/index.js +245 -0
- package/oclif.manifest.json +2218 -1813
- package/package.json +1 -19
- package/dist/commands/run/env/delete/index.d.ts +0 -14
- package/dist/commands/run/env/delete/index.js +0 -65
- package/dist/commands/run/env/get/index.d.ts +0 -14
- package/dist/commands/run/env/get/index.js +0 -52
- package/dist/commands/run/env/list/index.js +0 -56
- package/dist/commands/run/env/set/index.d.ts +0 -14
- package/dist/commands/run/env/set/index.js +0 -51
- package/dist/commands/run/exec/index.d.ts +0 -31
- package/dist/commands/run/exec/index.js +0 -431
- package/dist/commands/run/info/index.js +0 -160
- package/dist/commands/run/projects/create/index.js +0 -75
- package/dist/commands/run/projects/delete/index.d.ts +0 -14
- package/dist/commands/run/projects/delete/index.js +0 -65
- package/dist/commands/run/projects/list/index.d.ts +0 -13
- package/dist/commands/run/projects/list/index.js +0 -66
- package/dist/commands/run/projects/update/index.js +0 -86
- package/dist/commands/run/secrets/delete/index.d.ts +0 -14
- package/dist/commands/run/secrets/delete/index.js +0 -65
- package/dist/commands/run/secrets/get/index.js +0 -52
- package/dist/commands/run/secrets/list/index.d.ts +0 -12
- package/dist/commands/run/secrets/list/index.js +0 -60
- package/dist/commands/run/secrets/set/index.d.ts +0 -16
- package/dist/commands/run/secrets/set/index.js +0 -74
- package/dist/commands/run/sessions/delete/index.d.ts +0 -14
- package/dist/commands/run/sessions/delete/index.js +0 -65
- package/dist/commands/run/sessions/get/index.js +0 -72
- package/dist/commands/run/sessions/list/index.d.ts +0 -13
- package/dist/commands/run/sessions/list/index.js +0 -64
- package/dist/commands/run/sessions/start/index.js +0 -56
- package/dist/commands/run/sessions/stop/index.d.ts +0 -14
- package/dist/commands/run/sessions/stop/index.js +0 -56
- package/dist/commands/run/sink/get/index.d.ts +0 -14
- package/dist/commands/run/sink/get/index.js +0 -63
- package/dist/lib/base-run-command.d.ts +0 -41
- package/dist/lib/base-run-command.js +0 -75
- package/dist/lib/run-http-client.d.ts +0 -64
- package/dist/lib/run-http-client.js +0 -171
- package/dist/lib/run-types.d.ts +0 -226
- package/dist/lib/run-types.js +0 -5
|
@@ -0,0 +1,139 @@
|
|
|
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 TenantEnvDelete extends BaseCommand {
|
|
8
|
+
static args = {
|
|
9
|
+
tenant_name: Args.string({
|
|
10
|
+
description: 'Tenant name',
|
|
11
|
+
required: true,
|
|
12
|
+
}),
|
|
13
|
+
};
|
|
14
|
+
static description = 'Delete an environment variable from a tenant';
|
|
15
|
+
static examples = [
|
|
16
|
+
`$ xano tenant env delete my-tenant --name DATABASE_URL
|
|
17
|
+
Are you sure you want to delete environment variable 'DATABASE_URL' from tenant my-tenant? (y/N) y
|
|
18
|
+
Environment variable 'DATABASE_URL' deleted from tenant my-tenant
|
|
19
|
+
`,
|
|
20
|
+
`$ xano tenant env delete my-tenant --name DATABASE_URL --force
|
|
21
|
+
Environment variable 'DATABASE_URL' deleted from tenant my-tenant
|
|
22
|
+
`,
|
|
23
|
+
`$ xano tenant env delete my-tenant --name DATABASE_URL -f -w 5 -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
|
+
name: Flags.string({
|
|
34
|
+
char: 'n',
|
|
35
|
+
description: 'Environment variable name',
|
|
36
|
+
required: true,
|
|
37
|
+
}),
|
|
38
|
+
output: Flags.string({
|
|
39
|
+
char: 'o',
|
|
40
|
+
default: 'summary',
|
|
41
|
+
description: 'Output format',
|
|
42
|
+
options: ['summary', 'json'],
|
|
43
|
+
required: false,
|
|
44
|
+
}),
|
|
45
|
+
workspace: Flags.string({
|
|
46
|
+
char: 'w',
|
|
47
|
+
description: 'Workspace ID (uses profile workspace if not provided)',
|
|
48
|
+
required: false,
|
|
49
|
+
}),
|
|
50
|
+
};
|
|
51
|
+
async run() {
|
|
52
|
+
const { args, flags } = await this.parse(TenantEnvDelete);
|
|
53
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
54
|
+
const credentials = this.loadCredentials();
|
|
55
|
+
if (!(profileName in credentials.profiles)) {
|
|
56
|
+
this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
|
|
57
|
+
`Create a profile using 'xano profile create'`);
|
|
58
|
+
}
|
|
59
|
+
const profile = credentials.profiles[profileName];
|
|
60
|
+
if (!profile.instance_origin) {
|
|
61
|
+
this.error(`Profile '${profileName}' is missing instance_origin`);
|
|
62
|
+
}
|
|
63
|
+
if (!profile.access_token) {
|
|
64
|
+
this.error(`Profile '${profileName}' is missing access_token`);
|
|
65
|
+
}
|
|
66
|
+
const workspaceId = flags.workspace || profile.workspace;
|
|
67
|
+
if (!workspaceId) {
|
|
68
|
+
this.error('No workspace ID provided. Use --workspace flag or set one in your profile.');
|
|
69
|
+
}
|
|
70
|
+
const tenantName = args.tenant_name;
|
|
71
|
+
const envName = flags.name;
|
|
72
|
+
if (!flags.force) {
|
|
73
|
+
const confirmed = await this.confirm(`Are you sure you want to delete environment variable '${envName}' from tenant ${tenantName}?`);
|
|
74
|
+
if (!confirmed) {
|
|
75
|
+
this.log('Deletion cancelled.');
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/tenant/${tenantName}/env/${envName}`;
|
|
80
|
+
try {
|
|
81
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
82
|
+
headers: {
|
|
83
|
+
accept: 'application/json',
|
|
84
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
85
|
+
},
|
|
86
|
+
method: 'DELETE',
|
|
87
|
+
}, flags.verbose, profile.access_token);
|
|
88
|
+
if (!response.ok) {
|
|
89
|
+
const errorText = await response.text();
|
|
90
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
91
|
+
}
|
|
92
|
+
if (flags.output === 'json') {
|
|
93
|
+
this.log(JSON.stringify({ deleted: true, env_name: envName, tenant_name: tenantName }, null, 2));
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
this.log(`Environment variable '${envName}' deleted from tenant ${tenantName}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
if (error instanceof Error) {
|
|
101
|
+
this.error(`Failed to delete tenant environment variable: ${error.message}`);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
this.error(`Failed to delete tenant environment variable: ${String(error)}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async confirm(message) {
|
|
109
|
+
const readline = await import('node:readline');
|
|
110
|
+
const rl = readline.createInterface({
|
|
111
|
+
input: process.stdin,
|
|
112
|
+
output: process.stdout,
|
|
113
|
+
});
|
|
114
|
+
return new Promise((resolve) => {
|
|
115
|
+
rl.question(`${message} (y/N) `, (answer) => {
|
|
116
|
+
rl.close();
|
|
117
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
loadCredentials() {
|
|
122
|
+
const configDir = path.join(os.homedir(), '.xano');
|
|
123
|
+
const credentialsPath = path.join(configDir, 'credentials.yaml');
|
|
124
|
+
if (!fs.existsSync(credentialsPath)) {
|
|
125
|
+
this.error(`Credentials file not found at ${credentialsPath}\n` + `Create a profile using 'xano profile create'`);
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
const fileContent = fs.readFileSync(credentialsPath, 'utf8');
|
|
129
|
+
const parsed = yaml.load(fileContent);
|
|
130
|
+
if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
|
|
131
|
+
this.error('Credentials file has invalid format.');
|
|
132
|
+
}
|
|
133
|
+
return parsed;
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
this.error(`Failed to parse credentials file: ${error}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import
|
|
2
|
-
export default class
|
|
3
|
-
static args: {
|
|
1
|
+
import BaseCommand from '../../../../base-command.js';
|
|
2
|
+
export default class TenantEnvGet extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
tenant_name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
4
6
|
static description: string;
|
|
5
7
|
static examples: string[];
|
|
6
8
|
static flags: {
|
|
7
|
-
description: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
9
|
name: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
10
|
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
12
|
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
13
|
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
14
|
};
|
|
13
15
|
run(): Promise<void>;
|
|
16
|
+
private loadCredentials;
|
|
14
17
|
}
|
|
@@ -0,0 +1,113 @@
|
|
|
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 TenantEnvGet extends BaseCommand {
|
|
8
|
+
static args = {
|
|
9
|
+
tenant_name: Args.string({
|
|
10
|
+
description: 'Tenant name',
|
|
11
|
+
required: true,
|
|
12
|
+
}),
|
|
13
|
+
};
|
|
14
|
+
static description = 'Get a single environment variable for a tenant';
|
|
15
|
+
static examples = [
|
|
16
|
+
`$ xano tenant env get my-tenant --name DATABASE_URL
|
|
17
|
+
postgres://localhost:5432/mydb
|
|
18
|
+
`,
|
|
19
|
+
`$ xano tenant env get my-tenant --name DATABASE_URL -w 5 -o json`,
|
|
20
|
+
];
|
|
21
|
+
static flags = {
|
|
22
|
+
...BaseCommand.baseFlags,
|
|
23
|
+
name: Flags.string({
|
|
24
|
+
char: 'n',
|
|
25
|
+
description: 'Environment variable name',
|
|
26
|
+
required: true,
|
|
27
|
+
}),
|
|
28
|
+
output: Flags.string({
|
|
29
|
+
char: 'o',
|
|
30
|
+
default: 'summary',
|
|
31
|
+
description: 'Output format',
|
|
32
|
+
options: ['summary', 'json'],
|
|
33
|
+
required: false,
|
|
34
|
+
}),
|
|
35
|
+
workspace: Flags.string({
|
|
36
|
+
char: 'w',
|
|
37
|
+
description: 'Workspace ID (uses profile workspace if not provided)',
|
|
38
|
+
required: false,
|
|
39
|
+
}),
|
|
40
|
+
};
|
|
41
|
+
async run() {
|
|
42
|
+
const { args, flags } = await this.parse(TenantEnvGet);
|
|
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 workspaceId = flags.workspace || profile.workspace;
|
|
57
|
+
if (!workspaceId) {
|
|
58
|
+
this.error('No workspace ID provided. Use --workspace flag or set one in your profile.');
|
|
59
|
+
}
|
|
60
|
+
const tenantName = args.tenant_name;
|
|
61
|
+
const envName = flags.name;
|
|
62
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/tenant/${tenantName}/env/${envName}`;
|
|
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 envVar = (await response.json());
|
|
76
|
+
if (flags.output === 'json') {
|
|
77
|
+
this.log(JSON.stringify(envVar, null, 2));
|
|
78
|
+
}
|
|
79
|
+
else if (envVar) {
|
|
80
|
+
this.log(envVar.value);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
this.log(`Environment variable '${envName}' not found for tenant ${tenantName}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
if (error instanceof Error) {
|
|
88
|
+
this.error(`Failed to get tenant environment variable: ${error.message}`);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
this.error(`Failed to get tenant environment variable: ${String(error)}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
loadCredentials() {
|
|
96
|
+
const configDir = path.join(os.homedir(), '.xano');
|
|
97
|
+
const credentialsPath = path.join(configDir, 'credentials.yaml');
|
|
98
|
+
if (!fs.existsSync(credentialsPath)) {
|
|
99
|
+
this.error(`Credentials file not found at ${credentialsPath}\n` + `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
|
+
}
|
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
import
|
|
2
|
-
export default class
|
|
1
|
+
import BaseCommand from '../../../../base-command.js';
|
|
2
|
+
export default class TenantEnvGetAll extends BaseCommand {
|
|
3
3
|
static args: {
|
|
4
|
-
|
|
4
|
+
tenant_name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
5
|
};
|
|
6
6
|
static description: string;
|
|
7
7
|
static examples: string[];
|
|
8
8
|
static flags: {
|
|
9
|
-
|
|
10
|
-
name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
10
|
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
view: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
13
|
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
14
|
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
15
|
};
|
|
15
16
|
run(): Promise<void>;
|
|
17
|
+
private loadCredentials;
|
|
16
18
|
}
|
|
@@ -0,0 +1,123 @@
|
|
|
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 TenantEnvGetAll extends BaseCommand {
|
|
8
|
+
static args = {
|
|
9
|
+
tenant_name: Args.string({
|
|
10
|
+
description: 'Tenant name',
|
|
11
|
+
required: true,
|
|
12
|
+
}),
|
|
13
|
+
};
|
|
14
|
+
static description = 'Get all environment variables for a tenant and save to a YAML file';
|
|
15
|
+
static examples = [
|
|
16
|
+
`$ xano tenant env get_all my-tenant
|
|
17
|
+
Environment variables saved to env_my-tenant.yaml
|
|
18
|
+
`,
|
|
19
|
+
`$ xano tenant env get_all my-tenant --file ./my-env.yaml`,
|
|
20
|
+
`$ xano tenant env get_all my-tenant --view`,
|
|
21
|
+
`$ xano tenant env get_all my-tenant -o json`,
|
|
22
|
+
];
|
|
23
|
+
static flags = {
|
|
24
|
+
...BaseCommand.baseFlags,
|
|
25
|
+
file: Flags.string({
|
|
26
|
+
char: 'f',
|
|
27
|
+
description: 'Output file path (default: env_<tenant_name>.yaml)',
|
|
28
|
+
required: false,
|
|
29
|
+
}),
|
|
30
|
+
output: Flags.string({
|
|
31
|
+
char: 'o',
|
|
32
|
+
default: 'summary',
|
|
33
|
+
description: 'Output format',
|
|
34
|
+
options: ['summary', 'json'],
|
|
35
|
+
required: false,
|
|
36
|
+
}),
|
|
37
|
+
view: Flags.boolean({
|
|
38
|
+
default: false,
|
|
39
|
+
description: 'Print environment variables to stdout instead of saving to file',
|
|
40
|
+
required: false,
|
|
41
|
+
}),
|
|
42
|
+
workspace: Flags.string({
|
|
43
|
+
char: 'w',
|
|
44
|
+
description: 'Workspace ID (uses profile workspace if not provided)',
|
|
45
|
+
required: false,
|
|
46
|
+
}),
|
|
47
|
+
};
|
|
48
|
+
async run() {
|
|
49
|
+
const { args, flags } = await this.parse(TenantEnvGetAll);
|
|
50
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
51
|
+
const credentials = this.loadCredentials();
|
|
52
|
+
if (!(profileName in credentials.profiles)) {
|
|
53
|
+
this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
|
|
54
|
+
`Create a profile using 'xano profile create'`);
|
|
55
|
+
}
|
|
56
|
+
const profile = credentials.profiles[profileName];
|
|
57
|
+
if (!profile.instance_origin) {
|
|
58
|
+
this.error(`Profile '${profileName}' is missing instance_origin`);
|
|
59
|
+
}
|
|
60
|
+
if (!profile.access_token) {
|
|
61
|
+
this.error(`Profile '${profileName}' is missing access_token`);
|
|
62
|
+
}
|
|
63
|
+
const workspaceId = flags.workspace || profile.workspace;
|
|
64
|
+
if (!workspaceId) {
|
|
65
|
+
this.error('No workspace ID provided. Use --workspace flag or set one in your profile.');
|
|
66
|
+
}
|
|
67
|
+
const tenantName = args.tenant_name;
|
|
68
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/tenant/${tenantName}/env_all`;
|
|
69
|
+
try {
|
|
70
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
71
|
+
headers: {
|
|
72
|
+
accept: 'application/json',
|
|
73
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
74
|
+
},
|
|
75
|
+
method: 'GET',
|
|
76
|
+
}, flags.verbose, profile.access_token);
|
|
77
|
+
if (!response.ok) {
|
|
78
|
+
const errorText = await response.text();
|
|
79
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
80
|
+
}
|
|
81
|
+
const envMap = (await response.json());
|
|
82
|
+
if (flags.output === 'json') {
|
|
83
|
+
this.log(JSON.stringify(envMap, null, 2));
|
|
84
|
+
}
|
|
85
|
+
else if (flags.view) {
|
|
86
|
+
const envYaml = yaml.dump(envMap, { lineWidth: -1, sortKeys: true });
|
|
87
|
+
this.log(envYaml.trimEnd());
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
const filePath = path.resolve(flags.file || `env_${tenantName}.yaml`);
|
|
91
|
+
const envYaml = yaml.dump(envMap, { lineWidth: -1, sortKeys: true });
|
|
92
|
+
fs.writeFileSync(filePath, envYaml, 'utf8');
|
|
93
|
+
this.log(`Environment variables saved to ${filePath}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
if (error instanceof Error) {
|
|
98
|
+
this.error(`Failed to get tenant environment variables: ${error.message}`);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
this.error(`Failed to get tenant environment variables: ${String(error)}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
loadCredentials() {
|
|
106
|
+
const configDir = path.join(os.homedir(), '.xano');
|
|
107
|
+
const credentialsPath = path.join(configDir, 'credentials.yaml');
|
|
108
|
+
if (!fs.existsSync(credentialsPath)) {
|
|
109
|
+
this.error(`Credentials file not found at ${credentialsPath}\n` + `Create a profile using 'xano profile create'`);
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
const fileContent = fs.readFileSync(credentialsPath, 'utf8');
|
|
113
|
+
const parsed = yaml.load(fileContent);
|
|
114
|
+
if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
|
|
115
|
+
this.error('Credentials file has invalid format.');
|
|
116
|
+
}
|
|
117
|
+
return parsed;
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
this.error(`Failed to parse credentials file: ${error}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import
|
|
2
|
-
export default class
|
|
1
|
+
import BaseCommand from '../../../../base-command.js';
|
|
2
|
+
export default class TenantEnvList extends BaseCommand {
|
|
3
3
|
static args: {
|
|
4
|
-
|
|
4
|
+
tenant_name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
5
|
};
|
|
6
6
|
static description: string;
|
|
7
7
|
static examples: string[];
|
|
8
8
|
static flags: {
|
|
9
9
|
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
11
|
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
12
|
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
13
|
};
|
|
13
14
|
run(): Promise<void>;
|
|
15
|
+
private loadCredentials;
|
|
14
16
|
}
|
|
@@ -0,0 +1,116 @@
|
|
|
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 TenantEnvList extends BaseCommand {
|
|
8
|
+
static args = {
|
|
9
|
+
tenant_name: Args.string({
|
|
10
|
+
description: 'Tenant name',
|
|
11
|
+
required: true,
|
|
12
|
+
}),
|
|
13
|
+
};
|
|
14
|
+
static description = 'List environment variable keys for a tenant';
|
|
15
|
+
static examples = [
|
|
16
|
+
`$ xano tenant env list my-tenant
|
|
17
|
+
Environment variables for tenant my-tenant:
|
|
18
|
+
- DATABASE_URL
|
|
19
|
+
- API_KEY
|
|
20
|
+
- SECRET_TOKEN
|
|
21
|
+
`,
|
|
22
|
+
`$ xano tenant env list my-tenant -w 5 -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
|
+
workspace: Flags.string({
|
|
34
|
+
char: 'w',
|
|
35
|
+
description: 'Workspace ID (uses profile workspace if not provided)',
|
|
36
|
+
required: false,
|
|
37
|
+
}),
|
|
38
|
+
};
|
|
39
|
+
async run() {
|
|
40
|
+
const { args, flags } = await this.parse(TenantEnvList);
|
|
41
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
42
|
+
const credentials = this.loadCredentials();
|
|
43
|
+
if (!(profileName in credentials.profiles)) {
|
|
44
|
+
this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
|
|
45
|
+
`Create a profile using 'xano profile create'`);
|
|
46
|
+
}
|
|
47
|
+
const profile = credentials.profiles[profileName];
|
|
48
|
+
if (!profile.instance_origin) {
|
|
49
|
+
this.error(`Profile '${profileName}' is missing instance_origin`);
|
|
50
|
+
}
|
|
51
|
+
if (!profile.access_token) {
|
|
52
|
+
this.error(`Profile '${profileName}' is missing access_token`);
|
|
53
|
+
}
|
|
54
|
+
const workspaceId = flags.workspace || profile.workspace;
|
|
55
|
+
if (!workspaceId) {
|
|
56
|
+
this.error('No workspace ID provided. Use --workspace flag or set one in your profile.');
|
|
57
|
+
}
|
|
58
|
+
const tenantName = args.tenant_name;
|
|
59
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/tenant/${tenantName}/env_key`;
|
|
60
|
+
try {
|
|
61
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
62
|
+
headers: {
|
|
63
|
+
accept: 'application/json',
|
|
64
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
65
|
+
},
|
|
66
|
+
method: 'GET',
|
|
67
|
+
}, flags.verbose, profile.access_token);
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
const errorText = await response.text();
|
|
70
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
71
|
+
}
|
|
72
|
+
const data = (await response.json());
|
|
73
|
+
if (flags.output === 'json') {
|
|
74
|
+
this.log(JSON.stringify(data, null, 2));
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
const envVars = data.env || [];
|
|
78
|
+
if (envVars.length === 0) {
|
|
79
|
+
this.log(`No environment variables found for tenant ${tenantName}`);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
this.log(`Environment variables for tenant ${tenantName}:`);
|
|
83
|
+
for (const envVar of envVars) {
|
|
84
|
+
this.log(` - ${envVar.name}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
if (error instanceof Error) {
|
|
91
|
+
this.error(`Failed to list tenant environment variables: ${error.message}`);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
this.error(`Failed to list tenant environment variables: ${String(error)}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
loadCredentials() {
|
|
99
|
+
const configDir = path.join(os.homedir(), '.xano');
|
|
100
|
+
const credentialsPath = path.join(configDir, 'credentials.yaml');
|
|
101
|
+
if (!fs.existsSync(credentialsPath)) {
|
|
102
|
+
this.error(`Credentials file not found at ${credentialsPath}\n` + `Create a profile using 'xano profile create'`);
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
const fileContent = fs.readFileSync(credentialsPath, 'utf8');
|
|
106
|
+
const parsed = yaml.load(fileContent);
|
|
107
|
+
if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
|
|
108
|
+
this.error('Credentials file has invalid format.');
|
|
109
|
+
}
|
|
110
|
+
return parsed;
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
this.error(`Failed to parse credentials file: ${error}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import BaseCommand from '../../../../base-command.js';
|
|
2
|
+
export default class TenantEnvSet extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
tenant_name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
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
|
+
value: import("@oclif/core/interfaces").OptionFlag<string, 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
|
+
}
|