@xano/cli 1.0.6-beta.9 → 1.2.0
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 +46 -62
- package/dist/base-command.d.ts +0 -2
- package/dist/base-command.js +4 -10
- package/dist/commands/release/pull/index.d.ts +6 -0
- package/dist/commands/release/pull/index.js +95 -10
- package/dist/commands/sandbox/pull/index.d.ts +1 -0
- package/dist/commands/sandbox/pull/index.js +77 -10
- package/dist/commands/sandbox/unit_test/run/index.js +17 -11
- package/dist/commands/sandbox/unit_test/run_all/index.js +9 -1
- package/dist/commands/sandbox/workflow_test/run/index.js +12 -9
- package/dist/commands/sandbox/workflow_test/run_all/index.js +9 -1
- package/dist/commands/tenant/pull/index.d.ts +6 -0
- package/dist/commands/tenant/pull/index.js +95 -10
- package/dist/commands/tenant/unit_test/run/index.js +19 -11
- package/dist/commands/tenant/unit_test/run_all/index.js +13 -1
- package/dist/commands/tenant/workflow_test/run/index.js +14 -9
- package/dist/commands/tenant/workflow_test/run_all/index.js +13 -1
- package/dist/commands/unit_test/run/index.js +19 -11
- package/dist/commands/unit_test/run_all/index.js +13 -1
- package/dist/commands/workflow_test/run/index.js +15 -10
- package/dist/commands/workflow_test/run_all/index.js +15 -3
- package/dist/commands/workspace/git/pull/index.js +3 -53
- package/dist/commands/workspace/pull/index.d.ts +6 -0
- package/dist/commands/workspace/pull/index.js +95 -10
- package/dist/utils/document-parser.d.ts +0 -85
- package/dist/utils/document-parser.js +1 -214
- package/dist/utils/knowledge-sync.d.ts +0 -1
- package/dist/utils/knowledge-sync.js +5 -11
- package/dist/utils/paging.d.ts +1 -2
- package/dist/utils/reference-checker.js +0 -13
- package/oclif.manifest.json +2796 -4820
- package/package.json +1 -10
- package/dist/commands/ephemeral/create/index.d.ts +0 -18
- package/dist/commands/ephemeral/create/index.js +0 -103
- package/dist/commands/ephemeral/delete/index.d.ts +0 -18
- package/dist/commands/ephemeral/delete/index.js +0 -99
- package/dist/commands/ephemeral/edit/index.d.ts +0 -18
- package/dist/commands/ephemeral/edit/index.js +0 -109
- package/dist/commands/ephemeral/get/index.d.ts +0 -21
- package/dist/commands/ephemeral/get/index.js +0 -125
- package/dist/commands/ephemeral/impersonate/index.d.ts +0 -20
- package/dist/commands/ephemeral/impersonate/index.js +0 -120
- package/dist/commands/ephemeral/list/index.d.ts +0 -19
- package/dist/commands/ephemeral/list/index.js +0 -123
- package/dist/commands/ephemeral/pull/index.d.ts +0 -19
- package/dist/commands/ephemeral/pull/index.js +0 -164
- package/dist/commands/ephemeral/push/index.d.ts +0 -35
- package/dist/commands/ephemeral/push/index.js +0 -221
- package/dist/commands/ephemeral/static_host/build/delete/index.d.ts +0 -20
- package/dist/commands/ephemeral/static_host/build/delete/index.js +0 -121
- package/dist/commands/ephemeral/static_host/build/get/index.d.ts +0 -18
- package/dist/commands/ephemeral/static_host/build/get/index.js +0 -137
- package/dist/commands/ephemeral/static_host/build/list/index.d.ts +0 -18
- package/dist/commands/ephemeral/static_host/build/list/index.js +0 -137
- package/dist/commands/ephemeral/static_host/build/pull/index.d.ts +0 -53
- package/dist/commands/ephemeral/static_host/build/pull/index.js +0 -308
- package/dist/commands/ephemeral/static_host/build/push/index.d.ts +0 -24
- package/dist/commands/ephemeral/static_host/build/push/index.js +0 -250
- package/dist/commands/ephemeral/static_host/create/index.d.ts +0 -18
- package/dist/commands/ephemeral/static_host/create/index.js +0 -92
- package/dist/commands/ephemeral/static_host/deploy/index.d.ts +0 -19
- package/dist/commands/ephemeral/static_host/deploy/index.js +0 -112
- package/dist/commands/ephemeral/static_host/edit/index.d.ts +0 -24
- package/dist/commands/ephemeral/static_host/edit/index.js +0 -158
- package/dist/commands/ephemeral/static_host/get/index.d.ts +0 -19
- package/dist/commands/ephemeral/static_host/get/index.js +0 -101
- package/dist/commands/ephemeral/static_host/list/index.d.ts +0 -17
- package/dist/commands/ephemeral/static_host/list/index.js +0 -132
- package/dist/utils/microservice-wait.d.ts +0 -53
- package/dist/utils/microservice-wait.js +0 -83
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xano/cli",
|
|
3
3
|
"description": "CLI for Xano's Metadata API",
|
|
4
|
-
"version": "1.0
|
|
4
|
+
"version": "1.2.0",
|
|
5
5
|
"author": "Sean Montgomery",
|
|
6
6
|
"bin": {
|
|
7
7
|
"xano": "./bin/run.js"
|
|
@@ -98,15 +98,6 @@
|
|
|
98
98
|
"release": {
|
|
99
99
|
"description": "Manage releases in a workspace"
|
|
100
100
|
},
|
|
101
|
-
"ephemeral": {
|
|
102
|
-
"description": "Manage ephemeral tenants in a workspace"
|
|
103
|
-
},
|
|
104
|
-
"ephemeral:static_host": {
|
|
105
|
-
"description": "Manage static hosting for an ephemeral tenant"
|
|
106
|
-
},
|
|
107
|
-
"ephemeral:static_host:build": {
|
|
108
|
-
"description": "Manage static host builds for an ephemeral tenant"
|
|
109
|
-
},
|
|
110
101
|
"tenant": {
|
|
111
102
|
"description": "Manage tenants in a workspace"
|
|
112
103
|
},
|
|
@@ -1,18 +0,0 @@
|
|
|
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, Record<string, unknown>>;
|
|
5
|
-
};
|
|
6
|
-
static description: string;
|
|
7
|
-
static examples: string[];
|
|
8
|
-
static flags: {
|
|
9
|
-
description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
-
'expires-hours': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
-
output: 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
|
-
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
-
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
-
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
|
-
};
|
|
17
|
-
run(): Promise<void>;
|
|
18
|
-
}
|
|
@@ -1,103 +0,0 @@
|
|
|
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: 'Display name for the ephemeral tenant',
|
|
7
|
-
required: true,
|
|
8
|
-
}),
|
|
9
|
-
};
|
|
10
|
-
static description = 'Create a new ephemeral (short-lived, auto-expiring) tenant in a workspace';
|
|
11
|
-
static examples = [
|
|
12
|
-
`$ xano ephemeral create "PR preview"
|
|
13
|
-
Created ephemeral tenant: PR preview (e4f2-9ab1-...) - ID: 42
|
|
14
|
-
Expires: in 1 hour
|
|
15
|
-
`,
|
|
16
|
-
`$ xano ephemeral create "Demo" --expires-hours 24 -w 114`,
|
|
17
|
-
`$ xano ephemeral create "Load test" --description "48h soak" --expires-hours 48 -o json`,
|
|
18
|
-
];
|
|
19
|
-
static flags = {
|
|
20
|
-
...BaseCommand.baseFlags,
|
|
21
|
-
description: Flags.string({
|
|
22
|
-
char: 'd',
|
|
23
|
-
description: 'Ephemeral tenant description',
|
|
24
|
-
required: false,
|
|
25
|
-
}),
|
|
26
|
-
'expires-hours': Flags.integer({
|
|
27
|
-
default: 1,
|
|
28
|
-
description: 'Hours until the tenant auto-expires (1-72)',
|
|
29
|
-
max: 72,
|
|
30
|
-
min: 1,
|
|
31
|
-
required: false,
|
|
32
|
-
}),
|
|
33
|
-
output: Flags.string({
|
|
34
|
-
char: 'o',
|
|
35
|
-
default: 'summary',
|
|
36
|
-
description: 'Output format',
|
|
37
|
-
options: ['summary', 'json'],
|
|
38
|
-
required: false,
|
|
39
|
-
}),
|
|
40
|
-
workspace: Flags.string({
|
|
41
|
-
char: 'w',
|
|
42
|
-
description: 'Workspace ID (uses profile workspace if not provided)',
|
|
43
|
-
required: false,
|
|
44
|
-
}),
|
|
45
|
-
};
|
|
46
|
-
async run() {
|
|
47
|
-
const { args, flags } = await this.parse(EphemeralCreate);
|
|
48
|
-
const { profile } = this.resolveProfile(flags);
|
|
49
|
-
// Ephemeral tenants REQUIRE a workspace (unlike sandbox, which is workspace-agnostic).
|
|
50
|
-
const workspaceId = flags.workspace || profile.workspace;
|
|
51
|
-
if (!workspaceId) {
|
|
52
|
-
this.error('No workspace ID provided. Ephemeral tenants require a workspace — use --workspace or set one in your profile.');
|
|
53
|
-
}
|
|
54
|
-
const body = {
|
|
55
|
-
display: args.display,
|
|
56
|
-
expires_hours: flags['expires-hours'],
|
|
57
|
-
tag: [],
|
|
58
|
-
};
|
|
59
|
-
if (flags.description)
|
|
60
|
-
body.description = flags.description;
|
|
61
|
-
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/ephemeral`;
|
|
62
|
-
try {
|
|
63
|
-
const response = await this.verboseFetch(apiUrl, {
|
|
64
|
-
body: JSON.stringify(body),
|
|
65
|
-
headers: {
|
|
66
|
-
accept: 'application/json',
|
|
67
|
-
Authorization: `Bearer ${profile.access_token}`,
|
|
68
|
-
'Content-Type': 'application/json',
|
|
69
|
-
},
|
|
70
|
-
method: 'POST',
|
|
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
|
-
const tenant = (await response.json());
|
|
77
|
-
if (flags.output === 'json') {
|
|
78
|
-
this.log(JSON.stringify(tenant, null, 2));
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
this.log(`Created ephemeral tenant: ${tenant.display || tenant.name} (${tenant.name}) - ID: ${tenant.id}`);
|
|
82
|
-
if (tenant.state) {
|
|
83
|
-
this.log(` State: ${tenant.state}`);
|
|
84
|
-
}
|
|
85
|
-
if (tenant.ephemeral_expires_at) {
|
|
86
|
-
// The API serializes the timestamp field as a date string
|
|
87
|
-
// ("2026-07-24 19:49:15+0000"); tolerate a raw unix-epoch number too.
|
|
88
|
-
const raw = tenant.ephemeral_expires_at;
|
|
89
|
-
const expiresMs = typeof raw === 'number' ? raw * 1000 : Date.parse(String(raw).replace(' ', 'T'));
|
|
90
|
-
this.log(` Expires: ${Number.isNaN(expiresMs) ? String(raw) : new Date(expiresMs).toISOString()}`);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
catch (error) {
|
|
95
|
-
if (error instanceof Error) {
|
|
96
|
-
this.error(`Failed to create ephemeral tenant: ${error.message}`);
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
this.error(`Failed to create ephemeral tenant: ${String(error)}`);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
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
|
-
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
-
config: 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 confirm;
|
|
18
|
-
}
|
|
@@ -1,99 +0,0 @@
|
|
|
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 destroys all associated infrastructure and cannot be undone.';
|
|
11
|
-
static examples = [
|
|
12
|
-
`$ xano ephemeral delete e4f2-9ab1-xyz1
|
|
13
|
-
Are you sure you want to delete tenant e4f2-9ab1-xyz1? This action cannot be undone. (y/N) y
|
|
14
|
-
Deleted tenant e4f2-9ab1-xyz1
|
|
15
|
-
`,
|
|
16
|
-
`$ xano ephemeral delete e4f2-9ab1-xyz1 --force
|
|
17
|
-
Deleted tenant e4f2-9ab1-xyz1
|
|
18
|
-
`,
|
|
19
|
-
`$ xano ephemeral delete e4f2-9ab1-xyz1 -f -o json`,
|
|
20
|
-
];
|
|
21
|
-
static flags = {
|
|
22
|
-
...BaseCommand.baseFlags,
|
|
23
|
-
force: Flags.boolean({
|
|
24
|
-
char: 'f',
|
|
25
|
-
default: false,
|
|
26
|
-
description: '[CRITICAL] NEVER run without explicit user confirmation. Skips the confirmation prompt.',
|
|
27
|
-
required: false,
|
|
28
|
-
}),
|
|
29
|
-
output: Flags.string({
|
|
30
|
-
char: 'o',
|
|
31
|
-
default: 'summary',
|
|
32
|
-
description: 'Output format',
|
|
33
|
-
options: ['summary', 'json'],
|
|
34
|
-
required: false,
|
|
35
|
-
}),
|
|
36
|
-
workspace: Flags.string({
|
|
37
|
-
char: 'w',
|
|
38
|
-
description: 'Workspace ID (uses profile workspace if not provided)',
|
|
39
|
-
required: false,
|
|
40
|
-
}),
|
|
41
|
-
};
|
|
42
|
-
async run() {
|
|
43
|
-
const { args, flags } = await this.parse(EphemeralDelete);
|
|
44
|
-
const { profile } = this.resolveProfile(flags);
|
|
45
|
-
const workspaceId = flags.workspace || profile.workspace;
|
|
46
|
-
if (!workspaceId) {
|
|
47
|
-
this.error('No workspace ID provided. Use --workspace flag or set one in your profile.');
|
|
48
|
-
}
|
|
49
|
-
const tenantName = args.tenant_name;
|
|
50
|
-
if (!flags.force) {
|
|
51
|
-
const confirmed = await this.confirm(`Are you sure you want to delete 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/workspace/${workspaceId}/tenant/${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 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
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import BaseCommand from '../../../base-command.js';
|
|
2
|
-
export default class EphemeralEdit 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
|
-
description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
-
display: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
-
output: 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
|
-
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
-
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
-
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
|
-
};
|
|
17
|
-
run(): Promise<void>;
|
|
18
|
-
}
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import { Args, Flags } from '@oclif/core';
|
|
2
|
-
import BaseCommand from '../../../base-command.js';
|
|
3
|
-
export default class EphemeralEdit extends BaseCommand {
|
|
4
|
-
static args = {
|
|
5
|
-
tenant_name: Args.string({
|
|
6
|
-
description: 'Ephemeral tenant name to edit',
|
|
7
|
-
required: true,
|
|
8
|
-
}),
|
|
9
|
-
};
|
|
10
|
-
static description = 'Edit an existing ephemeral tenant';
|
|
11
|
-
static examples = [
|
|
12
|
-
`$ xano ephemeral edit e4f2-9ab1-xyz1 --display "New Name" --description "Updated description"
|
|
13
|
-
Updated tenant: New Name (e4f2-9ab1-xyz1) - ID: 42
|
|
14
|
-
`,
|
|
15
|
-
`$ xano ephemeral edit e4f2-9ab1-xyz1 -o json`,
|
|
16
|
-
];
|
|
17
|
-
static flags = {
|
|
18
|
-
...BaseCommand.baseFlags,
|
|
19
|
-
description: Flags.string({
|
|
20
|
-
char: 'd',
|
|
21
|
-
description: 'New description',
|
|
22
|
-
required: false,
|
|
23
|
-
}),
|
|
24
|
-
display: Flags.string({
|
|
25
|
-
description: 'New display name',
|
|
26
|
-
required: false,
|
|
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(EphemeralEdit);
|
|
43
|
-
const { profile } = this.resolveProfile(flags);
|
|
44
|
-
const workspaceId = flags.workspace || profile.workspace;
|
|
45
|
-
if (!workspaceId) {
|
|
46
|
-
this.error('No workspace ID provided. Use --workspace flag or set one in your profile.');
|
|
47
|
-
}
|
|
48
|
-
const tenantName = args.tenant_name;
|
|
49
|
-
const baseUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/tenant/${tenantName}`;
|
|
50
|
-
const headers = {
|
|
51
|
-
'accept': 'application/json',
|
|
52
|
-
'Authorization': `Bearer ${profile.access_token}`,
|
|
53
|
-
'Content-Type': 'application/json',
|
|
54
|
-
};
|
|
55
|
-
try {
|
|
56
|
-
// Fetch current tenant state (PUT requires all fields)
|
|
57
|
-
const getResponse = await this.verboseFetch(baseUrl, {
|
|
58
|
-
headers: {
|
|
59
|
-
'accept': 'application/json',
|
|
60
|
-
'Authorization': `Bearer ${profile.access_token}`,
|
|
61
|
-
},
|
|
62
|
-
method: 'GET',
|
|
63
|
-
}, flags.verbose, profile.access_token);
|
|
64
|
-
if (!getResponse.ok) {
|
|
65
|
-
const errorText = await getResponse.text();
|
|
66
|
-
this.error(`Failed to fetch tenant: ${getResponse.status} ${getResponse.statusText}\n${errorText}`);
|
|
67
|
-
}
|
|
68
|
-
const current = await getResponse.json();
|
|
69
|
-
// Merge in user-provided values
|
|
70
|
-
const body = {
|
|
71
|
-
description: flags.description !== undefined ? flags.description : (current.description ?? ''),
|
|
72
|
-
display: flags.display !== undefined ? flags.display : (current.display ?? current.name),
|
|
73
|
-
domain: current.domain ?? '',
|
|
74
|
-
ingress: current.ingress ?? false,
|
|
75
|
-
proxy: current.proxy ?? '',
|
|
76
|
-
rbac: {
|
|
77
|
-
enabled: current.rbac?.enabled ?? false,
|
|
78
|
-
},
|
|
79
|
-
tag: current.tag ?? [],
|
|
80
|
-
tasks: current.tasks ?? false,
|
|
81
|
-
};
|
|
82
|
-
// Update tenant
|
|
83
|
-
const putResponse = await this.verboseFetch(baseUrl, {
|
|
84
|
-
body: JSON.stringify(body),
|
|
85
|
-
headers,
|
|
86
|
-
method: 'PUT',
|
|
87
|
-
}, flags.verbose, profile.access_token);
|
|
88
|
-
if (!putResponse.ok) {
|
|
89
|
-
const errorText = await putResponse.text();
|
|
90
|
-
this.error(`API request failed with status ${putResponse.status}: ${putResponse.statusText}\n${errorText}`);
|
|
91
|
-
}
|
|
92
|
-
const tenant = await putResponse.json();
|
|
93
|
-
if (flags.output === 'json') {
|
|
94
|
-
this.log(JSON.stringify(tenant, null, 2));
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
this.log(`Updated tenant: ${tenant.display || tenant.name} (${tenant.name}) - ID: ${tenant.id}`);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
catch (error) {
|
|
101
|
-
if (error instanceof Error) {
|
|
102
|
-
this.error(`Failed to edit ephemeral tenant: ${error.message}`);
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
this.error(`Failed to edit ephemeral tenant: ${String(error)}`);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import BaseCommand from '../../../base-command.js';
|
|
2
|
-
export default class EphemeralGet 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
|
-
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>;
|
|
11
|
-
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, 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
|
-
/**
|
|
17
|
-
* Render a unix-epoch-seconds expiration as a human-readable "in Xh Ym" string,
|
|
18
|
-
* falling back to an ISO timestamp if the value has already passed or is absent.
|
|
19
|
-
*/
|
|
20
|
-
private formatExpiration;
|
|
21
|
-
}
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
import { Args, Flags } from '@oclif/core';
|
|
2
|
-
import BaseCommand from '../../../base-command.js';
|
|
3
|
-
export default class EphemeralGet extends BaseCommand {
|
|
4
|
-
static args = {
|
|
5
|
-
tenant_name: Args.string({
|
|
6
|
-
description: 'Ephemeral tenant name to retrieve',
|
|
7
|
-
required: true,
|
|
8
|
-
}),
|
|
9
|
-
};
|
|
10
|
-
static description = 'Get details of a specific ephemeral tenant';
|
|
11
|
-
static examples = [
|
|
12
|
-
`$ xano ephemeral get e4f2-9ab1-xyz1
|
|
13
|
-
Tenant: PR preview (e4f2-9ab1-xyz1)
|
|
14
|
-
State: ok
|
|
15
|
-
License: tier1
|
|
16
|
-
Expires: in 1h 0m
|
|
17
|
-
`,
|
|
18
|
-
`$ xano ephemeral get e4f2-9ab1-xyz1 -w 5 -o json`,
|
|
19
|
-
];
|
|
20
|
-
static flags = {
|
|
21
|
-
...BaseCommand.baseFlags,
|
|
22
|
-
output: Flags.string({
|
|
23
|
-
char: 'o',
|
|
24
|
-
default: 'summary',
|
|
25
|
-
description: 'Output format',
|
|
26
|
-
options: ['summary', 'json'],
|
|
27
|
-
required: false,
|
|
28
|
-
}),
|
|
29
|
-
workspace: Flags.string({
|
|
30
|
-
char: 'w',
|
|
31
|
-
description: 'Workspace ID (uses profile workspace if not provided)',
|
|
32
|
-
required: false,
|
|
33
|
-
}),
|
|
34
|
-
};
|
|
35
|
-
async run() {
|
|
36
|
-
const { args, flags } = await this.parse(EphemeralGet);
|
|
37
|
-
const { profile } = this.resolveProfile(flags);
|
|
38
|
-
const workspaceId = flags.workspace || profile.workspace;
|
|
39
|
-
if (!workspaceId) {
|
|
40
|
-
this.error('No workspace ID provided. Use --workspace flag or set one in your profile.');
|
|
41
|
-
}
|
|
42
|
-
const tenantName = args.tenant_name;
|
|
43
|
-
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/tenant/${tenantName}`;
|
|
44
|
-
try {
|
|
45
|
-
const response = await this.verboseFetch(apiUrl, {
|
|
46
|
-
headers: {
|
|
47
|
-
accept: 'application/json',
|
|
48
|
-
Authorization: `Bearer ${profile.access_token}`,
|
|
49
|
-
},
|
|
50
|
-
method: 'GET',
|
|
51
|
-
}, flags.verbose, profile.access_token);
|
|
52
|
-
if (!response.ok) {
|
|
53
|
-
const errorText = await response.text();
|
|
54
|
-
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
55
|
-
}
|
|
56
|
-
const tenant = (await response.json());
|
|
57
|
-
if (flags.output === 'json') {
|
|
58
|
-
this.log(JSON.stringify(tenant, null, 2));
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
this.log(`Tenant: ${tenant.display || tenant.name} (${tenant.name})`);
|
|
62
|
-
if (tenant.state)
|
|
63
|
-
this.log(` State: ${tenant.state}`);
|
|
64
|
-
if (tenant.license)
|
|
65
|
-
this.log(` License: ${tenant.license}`);
|
|
66
|
-
if (tenant.xano_domain)
|
|
67
|
-
this.log(` Domain: ${tenant.xano_domain}`);
|
|
68
|
-
if (tenant.domain)
|
|
69
|
-
this.log(` Custom Domain: ${tenant.domain}`);
|
|
70
|
-
if (tenant.cluster?.name)
|
|
71
|
-
this.log(` Cluster: ${tenant.cluster.name}`);
|
|
72
|
-
const releaseName = typeof tenant.release === 'string' ? tenant.release : tenant.release?.name;
|
|
73
|
-
const releaseId = typeof tenant.release === 'object' ? tenant.release?.id : undefined;
|
|
74
|
-
if (releaseName)
|
|
75
|
-
this.log(` Release: ${releaseName} (ID: ${releaseId})`);
|
|
76
|
-
if (tenant.platform?.name)
|
|
77
|
-
this.log(` Platform: ${tenant.platform.name}`);
|
|
78
|
-
if (tenant.version !== undefined)
|
|
79
|
-
this.log(` Version: ${tenant.version}`);
|
|
80
|
-
if (tenant.tasks !== undefined)
|
|
81
|
-
this.log(` Tasks: ${tenant.tasks}`);
|
|
82
|
-
if (tenant.ingress !== undefined)
|
|
83
|
-
this.log(` Ingress: ${tenant.ingress}`);
|
|
84
|
-
if (tenant.type)
|
|
85
|
-
this.log(` Type: ${tenant.type}`);
|
|
86
|
-
this.log(` Expires: ${this.formatExpiration(tenant.ephemeral_expires_at)}`);
|
|
87
|
-
if (tenant.deployed_at) {
|
|
88
|
-
const d = new Date(tenant.deployed_at);
|
|
89
|
-
const deployedDate = Number.isNaN(d.getTime()) ? tenant.deployed_at : d.toISOString().split('T')[0];
|
|
90
|
-
this.log(` Deployed: ${deployedDate}`);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
catch (error) {
|
|
95
|
-
if (error instanceof Error) {
|
|
96
|
-
this.error(`Failed to get ephemeral tenant: ${error.message}`);
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
this.error(`Failed to get ephemeral tenant: ${String(error)}`);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Render a unix-epoch-seconds expiration as a human-readable "in Xh Ym" string,
|
|
105
|
-
* falling back to an ISO timestamp if the value has already passed or is absent.
|
|
106
|
-
*/
|
|
107
|
-
formatExpiration(expiresAt) {
|
|
108
|
-
if (!expiresAt) {
|
|
109
|
-
return '-';
|
|
110
|
-
}
|
|
111
|
-
// The API serializes the timestamp field as a date string
|
|
112
|
-
// ("2026-07-24 19:49:15+0000"); tolerate a raw unix-epoch number too.
|
|
113
|
-
const expiresMs = typeof expiresAt === 'number' ? expiresAt * 1000 : Date.parse(String(expiresAt).replace(' ', 'T'));
|
|
114
|
-
if (Number.isNaN(expiresMs)) {
|
|
115
|
-
return String(expiresAt);
|
|
116
|
-
}
|
|
117
|
-
const diffSeconds = (expiresMs - Date.now()) / 1000;
|
|
118
|
-
if (diffSeconds <= 0) {
|
|
119
|
-
return `${new Date(expiresMs).toISOString()} (expired)`;
|
|
120
|
-
}
|
|
121
|
-
const hours = Math.floor(diffSeconds / 3600);
|
|
122
|
-
const minutes = Math.floor((diffSeconds % 3600) / 60);
|
|
123
|
-
return `in ${hours}h ${minutes}m`;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import BaseCommand from '../../../base-command.js';
|
|
2
|
-
export default class EphemeralImpersonate 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
|
-
guest: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
-
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
-
'url-only': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
|
-
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
-
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
-
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
-
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
|
-
};
|
|
17
|
-
run(): Promise<void>;
|
|
18
|
-
private getImpersonateResponse;
|
|
19
|
-
private getFrontendUrl;
|
|
20
|
-
}
|