@xano/cli 0.0.94 → 0.0.95-beta.2
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 +19 -0
- package/dist/commands/ephemeral/access/index.d.ts +15 -0
- package/dist/commands/ephemeral/access/index.js +78 -0
- package/dist/commands/ephemeral/create/index.d.ts +17 -0
- package/dist/commands/ephemeral/create/index.js +102 -0
- package/dist/commands/ephemeral/delete/index.d.ts +16 -0
- package/dist/commands/ephemeral/delete/index.js +99 -0
- package/dist/commands/ephemeral/env/delete/index.d.ts +17 -0
- package/dist/commands/ephemeral/env/delete/index.js +105 -0
- package/dist/commands/ephemeral/env/get/index.d.ts +15 -0
- package/dist/commands/ephemeral/env/get/index.js +81 -0
- package/dist/commands/ephemeral/env/get_all/index.d.ts +16 -0
- package/dist/commands/ephemeral/env/get_all/index.js +94 -0
- package/dist/commands/ephemeral/env/list/index.d.ts +14 -0
- package/dist/commands/ephemeral/env/list/index.js +83 -0
- package/dist/commands/ephemeral/env/set/index.d.ts +16 -0
- package/dist/commands/ephemeral/env/set/index.js +90 -0
- package/dist/commands/ephemeral/env/set_all/index.d.ts +16 -0
- package/dist/commands/ephemeral/env/set_all/index.js +102 -0
- package/dist/commands/ephemeral/get/index.d.ts +14 -0
- package/dist/commands/ephemeral/get/index.js +102 -0
- package/dist/commands/ephemeral/impersonate/index.d.ts +16 -0
- package/dist/commands/ephemeral/impersonate/index.js +110 -0
- package/dist/commands/ephemeral/license/get/index.d.ts +16 -0
- package/dist/commands/ephemeral/license/get/index.js +94 -0
- package/dist/commands/ephemeral/license/set/index.d.ts +17 -0
- package/dist/commands/ephemeral/license/set/index.js +111 -0
- package/dist/commands/ephemeral/list/index.d.ts +15 -0
- package/dist/commands/ephemeral/list/index.js +109 -0
- package/dist/commands/ephemeral/pull/index.d.ts +18 -0
- package/dist/commands/ephemeral/pull/index.js +197 -0
- package/dist/commands/ephemeral/push/index.d.ts +19 -0
- package/dist/commands/ephemeral/push/index.js +158 -0
- package/dist/commands/ephemeral/shared/index.d.ts +15 -0
- package/dist/commands/ephemeral/shared/index.js +108 -0
- package/dist/commands/ephemeral/unit_test/list/index.d.ts +14 -0
- package/dist/commands/ephemeral/unit_test/list/index.js +105 -0
- package/dist/commands/ephemeral/unit_test/run/index.d.ts +15 -0
- package/dist/commands/ephemeral/unit_test/run/index.js +93 -0
- package/dist/commands/ephemeral/unit_test/run_all/index.d.ts +14 -0
- package/dist/commands/ephemeral/unit_test/run_all/index.js +183 -0
- package/dist/commands/ephemeral/workflow_test/delete/index.d.ts +18 -0
- package/dist/commands/ephemeral/workflow_test/delete/index.js +75 -0
- package/dist/commands/ephemeral/workflow_test/get/index.d.ts +18 -0
- package/dist/commands/ephemeral/workflow_test/get/index.js +77 -0
- package/dist/commands/ephemeral/workflow_test/list/index.d.ts +13 -0
- package/dist/commands/ephemeral/workflow_test/list/index.js +98 -0
- package/dist/commands/ephemeral/workflow_test/run/index.d.ts +18 -0
- package/dist/commands/ephemeral/workflow_test/run/index.js +91 -0
- package/dist/commands/ephemeral/workflow_test/run_all/index.d.ts +13 -0
- package/dist/commands/ephemeral/workflow_test/run_all/index.js +169 -0
- package/dist/commands/release/deploy/index.d.ts +17 -0
- package/dist/commands/release/deploy/index.js +107 -0
- package/dist/commands/tenant/create/index.d.ts +0 -1
- package/dist/commands/tenant/create/index.js +0 -5
- package/dist/commands/tenant/push/index.js +1 -1
- package/dist/commands/tenant/unit_test/list/index.d.ts +15 -0
- package/dist/commands/tenant/unit_test/list/index.js +140 -0
- package/dist/commands/tenant/unit_test/run/index.d.ts +16 -0
- package/dist/commands/tenant/unit_test/run/index.js +128 -0
- package/dist/commands/tenant/unit_test/run_all/index.d.ts +15 -0
- package/dist/commands/tenant/unit_test/run_all/index.js +215 -0
- package/dist/commands/tenant/workflow_test/delete/index.d.ts +19 -0
- package/dist/commands/tenant/workflow_test/delete/index.js +110 -0
- package/dist/commands/tenant/workflow_test/get/index.d.ts +19 -0
- package/dist/commands/tenant/workflow_test/get/index.js +112 -0
- package/dist/commands/tenant/workflow_test/list/index.d.ts +14 -0
- package/dist/commands/tenant/workflow_test/list/index.js +133 -0
- package/dist/commands/tenant/workflow_test/run/index.d.ts +19 -0
- package/dist/commands/tenant/workflow_test/run/index.js +126 -0
- package/dist/commands/tenant/workflow_test/run_all/index.d.ts +14 -0
- package/dist/commands/tenant/workflow_test/run_all/index.js +201 -0
- package/dist/help.d.ts +2 -1
- package/dist/help.js +39 -1
- package/oclif.manifest.json +5193 -2303
- package/package.json +16 -1
package/README.md
CHANGED
|
@@ -235,6 +235,11 @@ xano release pull ./my-release -r v1.0 --env --records
|
|
|
235
235
|
xano release push ./my-release -n "v2.0"
|
|
236
236
|
xano release push ./my-release -n "v2.0" --hotfix -d "Critical fix"
|
|
237
237
|
xano release push ./my-release -n "v2.0" --no-records --no-env
|
|
238
|
+
|
|
239
|
+
# Deploy a release to its workspace as a new branch
|
|
240
|
+
xano release deploy "v1.0"
|
|
241
|
+
xano release deploy "v1.0" --branch "restore-v1" --no-set_live
|
|
242
|
+
xano release deploy "v1.0" -w 40 -o json
|
|
238
243
|
```
|
|
239
244
|
|
|
240
245
|
### Platforms
|
|
@@ -440,6 +445,20 @@ xano tenant cluster license set <cluster_id>
|
|
|
440
445
|
xano tenant cluster license set <cluster_id> --file ./kubeconfig.yaml
|
|
441
446
|
```
|
|
442
447
|
|
|
448
|
+
### Ephemeral
|
|
449
|
+
|
|
450
|
+
Manage ephemeral tenants. Ephemeral tenants are workspace-agnostic (tier1 only) and do not run background tasks.
|
|
451
|
+
|
|
452
|
+
```bash
|
|
453
|
+
# Create an ephemeral tenant
|
|
454
|
+
xano ephemeral create "My Ephemeral"
|
|
455
|
+
xano ephemeral create "CI Tenant" -d "For CI/CD" -o json
|
|
456
|
+
|
|
457
|
+
# Get ephemeral tenant details
|
|
458
|
+
xano ephemeral get <tenant_name>
|
|
459
|
+
xano ephemeral get <tenant_name> -o json
|
|
460
|
+
```
|
|
461
|
+
|
|
443
462
|
### Static Hosts
|
|
444
463
|
|
|
445
464
|
```bash
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import BaseCommand from '../../../base-command.js';
|
|
2
|
+
export default class EphemeralAccess extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
tenant_name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
access: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
6
|
+
};
|
|
7
|
+
static description: string;
|
|
8
|
+
static examples: string[];
|
|
9
|
+
static flags: {
|
|
10
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
};
|
|
14
|
+
run(): Promise<void>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import BaseCommand from '../../../base-command.js';
|
|
3
|
+
export default class EphemeralAccess extends BaseCommand {
|
|
4
|
+
static args = {
|
|
5
|
+
tenant_name: Args.string({
|
|
6
|
+
description: 'Ephemeral tenant name',
|
|
7
|
+
required: true,
|
|
8
|
+
}),
|
|
9
|
+
access: Args.string({
|
|
10
|
+
description: 'Access level to set',
|
|
11
|
+
options: ['private', 'shared'],
|
|
12
|
+
required: true,
|
|
13
|
+
}),
|
|
14
|
+
};
|
|
15
|
+
static description = 'Change the access level of an ephemeral tenant';
|
|
16
|
+
static examples = [
|
|
17
|
+
`$ xano ephemeral access e1a2-b3c4-x5y6 shared
|
|
18
|
+
Access updated to shared for tenant e1a2-b3c4-x5y6
|
|
19
|
+
`,
|
|
20
|
+
`$ xano ephemeral access e1a2-b3c4-x5y6 private`,
|
|
21
|
+
];
|
|
22
|
+
static flags = {
|
|
23
|
+
...BaseCommand.baseFlags,
|
|
24
|
+
output: Flags.string({
|
|
25
|
+
char: 'o',
|
|
26
|
+
default: 'summary',
|
|
27
|
+
description: 'Output format',
|
|
28
|
+
options: ['summary', 'json'],
|
|
29
|
+
required: false,
|
|
30
|
+
}),
|
|
31
|
+
};
|
|
32
|
+
async run() {
|
|
33
|
+
const { args, flags } = await this.parse(EphemeralAccess);
|
|
34
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
35
|
+
const credentials = this.loadCredentialsFile();
|
|
36
|
+
if (!credentials || !(profileName in credentials.profiles)) {
|
|
37
|
+
this.error(`Profile '${profileName}' not found.\n` + `Create a profile using 'xano profile create'`);
|
|
38
|
+
}
|
|
39
|
+
const profile = credentials.profiles[profileName];
|
|
40
|
+
if (!profile.instance_origin) {
|
|
41
|
+
this.error(`Profile '${profileName}' is missing instance_origin`);
|
|
42
|
+
}
|
|
43
|
+
if (!profile.access_token) {
|
|
44
|
+
this.error(`Profile '${profileName}' is missing access_token`);
|
|
45
|
+
}
|
|
46
|
+
const apiUrl = `${profile.instance_origin}/api:meta/ephemeral/tenant/${encodeURIComponent(args.tenant_name)}/access`;
|
|
47
|
+
try {
|
|
48
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
49
|
+
body: JSON.stringify({ access: args.access }),
|
|
50
|
+
headers: {
|
|
51
|
+
accept: 'application/json',
|
|
52
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
53
|
+
'Content-Type': 'application/json',
|
|
54
|
+
},
|
|
55
|
+
method: 'PATCH',
|
|
56
|
+
}, flags.verbose, profile.access_token);
|
|
57
|
+
if (!response.ok) {
|
|
58
|
+
const errorText = await response.text();
|
|
59
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
60
|
+
}
|
|
61
|
+
const tenant = await response.json();
|
|
62
|
+
if (flags.output === 'json') {
|
|
63
|
+
this.log(JSON.stringify(tenant, null, 2));
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
this.log(`Access updated to ${args.access} for tenant ${args.tenant_name}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
if (error instanceof Error) {
|
|
71
|
+
this.error(`Failed to update access: ${error.message}`);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
this.error(`Failed to update access: ${String(error)}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import BaseCommand from '../../../base-command.js';
|
|
2
|
+
export default class EphemeralCreate extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
display: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
access: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
domain: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
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
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import BaseCommand from '../../../base-command.js';
|
|
3
|
+
export default class EphemeralCreate extends BaseCommand {
|
|
4
|
+
static args = {
|
|
5
|
+
display: Args.string({
|
|
6
|
+
description: 'Optional display name for the ephemeral tenant',
|
|
7
|
+
required: false,
|
|
8
|
+
}),
|
|
9
|
+
};
|
|
10
|
+
static description = 'Create an ephemeral tenant (workspace-agnostic, no background tasks)';
|
|
11
|
+
static examples = [
|
|
12
|
+
`$ xano ephemeral create
|
|
13
|
+
Created ephemeral tenant: e1a2-b3c4-x5y6 - ID: 42
|
|
14
|
+
`,
|
|
15
|
+
`$ xano ephemeral create "My Ephemeral"`,
|
|
16
|
+
`$ xano ephemeral create "CI Tenant" -d "For CI/CD" -o json`,
|
|
17
|
+
];
|
|
18
|
+
static flags = {
|
|
19
|
+
...BaseCommand.baseFlags,
|
|
20
|
+
access: Flags.string({
|
|
21
|
+
char: 'a',
|
|
22
|
+
default: 'private',
|
|
23
|
+
description: 'Access level (private or shared)',
|
|
24
|
+
options: ['private', 'shared'],
|
|
25
|
+
required: false,
|
|
26
|
+
}),
|
|
27
|
+
description: Flags.string({
|
|
28
|
+
char: 'd',
|
|
29
|
+
description: 'Tenant description',
|
|
30
|
+
required: false,
|
|
31
|
+
}),
|
|
32
|
+
domain: Flags.string({
|
|
33
|
+
description: 'Custom domain for the tenant',
|
|
34
|
+
required: false,
|
|
35
|
+
}),
|
|
36
|
+
output: Flags.string({
|
|
37
|
+
char: 'o',
|
|
38
|
+
default: 'summary',
|
|
39
|
+
description: 'Output format',
|
|
40
|
+
options: ['summary', 'json'],
|
|
41
|
+
required: false,
|
|
42
|
+
}),
|
|
43
|
+
};
|
|
44
|
+
async run() {
|
|
45
|
+
const { args, flags } = await this.parse(EphemeralCreate);
|
|
46
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
47
|
+
const credentials = this.loadCredentialsFile();
|
|
48
|
+
if (!credentials || !(profileName in credentials.profiles)) {
|
|
49
|
+
this.error(`Profile '${profileName}' not found.\n` + `Create a profile using 'xano profile create'`);
|
|
50
|
+
}
|
|
51
|
+
const profile = credentials.profiles[profileName];
|
|
52
|
+
if (!profile.instance_origin) {
|
|
53
|
+
this.error(`Profile '${profileName}' is missing instance_origin`);
|
|
54
|
+
}
|
|
55
|
+
if (!profile.access_token) {
|
|
56
|
+
this.error(`Profile '${profileName}' is missing access_token`);
|
|
57
|
+
}
|
|
58
|
+
const body = {
|
|
59
|
+
access: flags.access,
|
|
60
|
+
display: args.display || '',
|
|
61
|
+
tag: [],
|
|
62
|
+
};
|
|
63
|
+
if (flags.description)
|
|
64
|
+
body.description = flags.description;
|
|
65
|
+
if (flags.domain)
|
|
66
|
+
body.domain = flags.domain;
|
|
67
|
+
const apiUrl = `${profile.instance_origin}/api:meta/ephemeral/tenant`;
|
|
68
|
+
try {
|
|
69
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
70
|
+
body: JSON.stringify(body),
|
|
71
|
+
headers: {
|
|
72
|
+
accept: 'application/json',
|
|
73
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
74
|
+
'Content-Type': 'application/json',
|
|
75
|
+
},
|
|
76
|
+
method: 'POST',
|
|
77
|
+
}, flags.verbose, profile.access_token);
|
|
78
|
+
if (!response.ok) {
|
|
79
|
+
const errorText = await response.text();
|
|
80
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
81
|
+
}
|
|
82
|
+
const tenant = (await response.json());
|
|
83
|
+
if (flags.output === 'json') {
|
|
84
|
+
this.log(JSON.stringify(tenant, null, 2));
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
this.log(`Created ephemeral tenant: ${tenant.display || tenant.name} (${tenant.name}) - ID: ${tenant.id}`);
|
|
88
|
+
if (tenant.state) {
|
|
89
|
+
this.log(` State: ${tenant.state}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
if (error instanceof Error) {
|
|
95
|
+
this.error(`Failed to create ephemeral tenant: ${error.message}`);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
this.error(`Failed to create ephemeral tenant: ${String(error)}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import BaseCommand from '../../../base-command.js';
|
|
2
|
+
export default class EphemeralDelete 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
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
};
|
|
14
|
+
run(): Promise<void>;
|
|
15
|
+
private confirm;
|
|
16
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import BaseCommand from '../../../base-command.js';
|
|
3
|
+
export default class EphemeralDelete extends BaseCommand {
|
|
4
|
+
static args = {
|
|
5
|
+
tenant_name: Args.string({
|
|
6
|
+
description: 'Ephemeral tenant name to delete',
|
|
7
|
+
required: true,
|
|
8
|
+
}),
|
|
9
|
+
};
|
|
10
|
+
static description = 'Delete an ephemeral tenant permanently. This cannot be undone.';
|
|
11
|
+
static examples = [
|
|
12
|
+
`$ xano ephemeral delete my-tenant
|
|
13
|
+
Are you sure you want to delete ephemeral tenant my-tenant? This action cannot be undone. (y/N) y
|
|
14
|
+
Deleted ephemeral tenant my-tenant
|
|
15
|
+
`,
|
|
16
|
+
`$ xano ephemeral delete my-tenant --force`,
|
|
17
|
+
`$ xano ephemeral delete my-tenant -f -o json`,
|
|
18
|
+
];
|
|
19
|
+
static flags = {
|
|
20
|
+
...BaseCommand.baseFlags,
|
|
21
|
+
force: Flags.boolean({
|
|
22
|
+
char: 'f',
|
|
23
|
+
default: false,
|
|
24
|
+
description: 'Skip confirmation prompt',
|
|
25
|
+
required: false,
|
|
26
|
+
}),
|
|
27
|
+
output: Flags.string({
|
|
28
|
+
char: 'o',
|
|
29
|
+
default: 'summary',
|
|
30
|
+
description: 'Output format',
|
|
31
|
+
options: ['summary', 'json'],
|
|
32
|
+
required: false,
|
|
33
|
+
}),
|
|
34
|
+
};
|
|
35
|
+
async run() {
|
|
36
|
+
const { args, flags } = await this.parse(EphemeralDelete);
|
|
37
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
38
|
+
const credentials = this.loadCredentialsFile();
|
|
39
|
+
if (!credentials || !(profileName in credentials.profiles)) {
|
|
40
|
+
this.error(`Profile '${profileName}' not found.\n` + `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 tenantName = args.tenant_name;
|
|
50
|
+
if (!flags.force) {
|
|
51
|
+
const confirmed = await this.confirm(`Are you sure you want to delete ephemeral tenant ${tenantName}? This action cannot be undone.`);
|
|
52
|
+
if (!confirmed) {
|
|
53
|
+
this.log('Deletion cancelled.');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const apiUrl = `${profile.instance_origin}/api:meta/ephemeral/tenant/${encodeURIComponent(tenantName)}`;
|
|
58
|
+
try {
|
|
59
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
60
|
+
headers: {
|
|
61
|
+
accept: 'application/json',
|
|
62
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
63
|
+
},
|
|
64
|
+
method: 'DELETE',
|
|
65
|
+
}, flags.verbose, profile.access_token);
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
const errorText = await response.text();
|
|
68
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
69
|
+
}
|
|
70
|
+
if (flags.output === 'json') {
|
|
71
|
+
this.log(JSON.stringify({ deleted: true, tenant_name: tenantName }, null, 2));
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
this.log(`Deleted ephemeral tenant ${tenantName}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
if (error instanceof Error) {
|
|
79
|
+
this.error(`Failed to delete ephemeral tenant: ${error.message}`);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
this.error(`Failed to delete ephemeral tenant: ${String(error)}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
async confirm(message) {
|
|
87
|
+
const readline = await import('node:readline');
|
|
88
|
+
const rl = readline.createInterface({
|
|
89
|
+
input: process.stdin,
|
|
90
|
+
output: process.stdout,
|
|
91
|
+
});
|
|
92
|
+
return new Promise((resolve) => {
|
|
93
|
+
rl.question(`${message} (y/N) `, (answer) => {
|
|
94
|
+
rl.close();
|
|
95
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import BaseCommand from '../../../../base-command.js';
|
|
2
|
+
export default class EphemeralEnvDelete 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
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
name: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
+
};
|
|
15
|
+
run(): Promise<void>;
|
|
16
|
+
private confirm;
|
|
17
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import BaseCommand from '../../../../base-command.js';
|
|
3
|
+
export default class EphemeralEnvDelete extends BaseCommand {
|
|
4
|
+
static args = {
|
|
5
|
+
tenant_name: Args.string({
|
|
6
|
+
description: 'Ephemeral tenant name',
|
|
7
|
+
required: true,
|
|
8
|
+
}),
|
|
9
|
+
};
|
|
10
|
+
static description = 'Delete an environment variable from an ephemeral tenant';
|
|
11
|
+
static examples = [
|
|
12
|
+
`$ xano ephemeral env delete my-tenant --name DATABASE_URL
|
|
13
|
+
Are you sure you want to delete environment variable 'DATABASE_URL' from ephemeral tenant my-tenant? (y/N) y
|
|
14
|
+
Environment variable 'DATABASE_URL' deleted from ephemeral tenant my-tenant
|
|
15
|
+
`,
|
|
16
|
+
`$ xano ephemeral env delete my-tenant --name DATABASE_URL --force`,
|
|
17
|
+
`$ xano ephemeral env delete my-tenant --name DATABASE_URL -f -o json`,
|
|
18
|
+
];
|
|
19
|
+
static flags = {
|
|
20
|
+
...BaseCommand.baseFlags,
|
|
21
|
+
force: Flags.boolean({
|
|
22
|
+
char: 'f',
|
|
23
|
+
default: false,
|
|
24
|
+
description: 'Skip confirmation prompt',
|
|
25
|
+
required: false,
|
|
26
|
+
}),
|
|
27
|
+
name: Flags.string({
|
|
28
|
+
char: 'n',
|
|
29
|
+
description: 'Environment variable name',
|
|
30
|
+
required: true,
|
|
31
|
+
}),
|
|
32
|
+
output: Flags.string({
|
|
33
|
+
char: 'o',
|
|
34
|
+
default: 'summary',
|
|
35
|
+
description: 'Output format',
|
|
36
|
+
options: ['summary', 'json'],
|
|
37
|
+
required: false,
|
|
38
|
+
}),
|
|
39
|
+
};
|
|
40
|
+
async run() {
|
|
41
|
+
const { args, flags } = await this.parse(EphemeralEnvDelete);
|
|
42
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
43
|
+
const credentials = this.loadCredentialsFile();
|
|
44
|
+
if (!credentials || !(profileName in credentials.profiles)) {
|
|
45
|
+
this.error(`Profile '${profileName}' not found.\n` + `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 tenantName = args.tenant_name;
|
|
55
|
+
const envName = flags.name;
|
|
56
|
+
if (!flags.force) {
|
|
57
|
+
const confirmed = await this.confirm(`Are you sure you want to delete environment variable '${envName}' from ephemeral tenant ${tenantName}?`);
|
|
58
|
+
if (!confirmed) {
|
|
59
|
+
this.log('Deletion cancelled.');
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const apiUrl = `${profile.instance_origin}/api:meta/ephemeral/tenant/${tenantName}/env/${envName}`;
|
|
64
|
+
try {
|
|
65
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
66
|
+
headers: {
|
|
67
|
+
accept: 'application/json',
|
|
68
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
69
|
+
},
|
|
70
|
+
method: 'DELETE',
|
|
71
|
+
}, flags.verbose, profile.access_token);
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
const errorText = await response.text();
|
|
74
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
75
|
+
}
|
|
76
|
+
if (flags.output === 'json') {
|
|
77
|
+
this.log(JSON.stringify({ deleted: true, env_name: envName, tenant_name: tenantName }, null, 2));
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
this.log(`Environment variable '${envName}' deleted from ephemeral tenant ${tenantName}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
if (error instanceof Error) {
|
|
85
|
+
this.error(`Failed to delete ephemeral tenant environment variable: ${error.message}`);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
this.error(`Failed to delete ephemeral tenant environment variable: ${String(error)}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async confirm(message) {
|
|
93
|
+
const readline = await import('node:readline');
|
|
94
|
+
const rl = readline.createInterface({
|
|
95
|
+
input: process.stdin,
|
|
96
|
+
output: process.stdout,
|
|
97
|
+
});
|
|
98
|
+
return new Promise((resolve) => {
|
|
99
|
+
rl.question(`${message} (y/N) `, (answer) => {
|
|
100
|
+
rl.close();
|
|
101
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import BaseCommand from '../../../../base-command.js';
|
|
2
|
+
export default class EphemeralEnvGet 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
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
};
|
|
14
|
+
run(): Promise<void>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import BaseCommand from '../../../../base-command.js';
|
|
3
|
+
export default class EphemeralEnvGet extends BaseCommand {
|
|
4
|
+
static args = {
|
|
5
|
+
tenant_name: Args.string({
|
|
6
|
+
description: 'Ephemeral tenant name',
|
|
7
|
+
required: true,
|
|
8
|
+
}),
|
|
9
|
+
};
|
|
10
|
+
static description = 'Get a single environment variable for an ephemeral tenant';
|
|
11
|
+
static examples = [
|
|
12
|
+
`$ xano ephemeral env get my-tenant --name DATABASE_URL
|
|
13
|
+
postgres://localhost:5432/mydb
|
|
14
|
+
`,
|
|
15
|
+
`$ xano ephemeral env get my-tenant --name DATABASE_URL -o json`,
|
|
16
|
+
];
|
|
17
|
+
static flags = {
|
|
18
|
+
...BaseCommand.baseFlags,
|
|
19
|
+
name: Flags.string({
|
|
20
|
+
char: 'n',
|
|
21
|
+
description: 'Environment variable name',
|
|
22
|
+
required: true,
|
|
23
|
+
}),
|
|
24
|
+
output: Flags.string({
|
|
25
|
+
char: 'o',
|
|
26
|
+
default: 'summary',
|
|
27
|
+
description: 'Output format',
|
|
28
|
+
options: ['summary', 'json'],
|
|
29
|
+
required: false,
|
|
30
|
+
}),
|
|
31
|
+
};
|
|
32
|
+
async run() {
|
|
33
|
+
const { args, flags } = await this.parse(EphemeralEnvGet);
|
|
34
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
35
|
+
const credentials = this.loadCredentialsFile();
|
|
36
|
+
if (!credentials || !(profileName in credentials.profiles)) {
|
|
37
|
+
this.error(`Profile '${profileName}' not found.\n` + `Create a profile using 'xano profile create'`);
|
|
38
|
+
}
|
|
39
|
+
const profile = credentials.profiles[profileName];
|
|
40
|
+
if (!profile.instance_origin) {
|
|
41
|
+
this.error(`Profile '${profileName}' is missing instance_origin`);
|
|
42
|
+
}
|
|
43
|
+
if (!profile.access_token) {
|
|
44
|
+
this.error(`Profile '${profileName}' is missing access_token`);
|
|
45
|
+
}
|
|
46
|
+
const tenantName = args.tenant_name;
|
|
47
|
+
const envName = flags.name;
|
|
48
|
+
const apiUrl = `${profile.instance_origin}/api:meta/ephemeral/tenant/${tenantName}/env/${envName}`;
|
|
49
|
+
try {
|
|
50
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
51
|
+
headers: {
|
|
52
|
+
accept: 'application/json',
|
|
53
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
54
|
+
},
|
|
55
|
+
method: 'GET',
|
|
56
|
+
}, flags.verbose, profile.access_token);
|
|
57
|
+
if (!response.ok) {
|
|
58
|
+
const errorText = await response.text();
|
|
59
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
60
|
+
}
|
|
61
|
+
const envVar = (await response.json());
|
|
62
|
+
if (flags.output === 'json') {
|
|
63
|
+
this.log(JSON.stringify(envVar, null, 2));
|
|
64
|
+
}
|
|
65
|
+
else if (envVar) {
|
|
66
|
+
this.log(envVar.value);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
this.log(`Environment variable '${envName}' not found for ephemeral tenant ${tenantName}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
if (error instanceof Error) {
|
|
74
|
+
this.error(`Failed to get ephemeral tenant environment variable: ${error.message}`);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
this.error(`Failed to get ephemeral tenant environment variable: ${String(error)}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import BaseCommand from '../../../../base-command.js';
|
|
2
|
+
export default class EphemeralEnvGetAll 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
|
+
file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
view: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
+
};
|
|
15
|
+
run(): Promise<void>;
|
|
16
|
+
}
|