@xano/cli 0.0.95-beta.2 → 0.0.95-beta.20
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 +22 -12
- package/dist/base-command.d.ts +30 -0
- package/dist/base-command.js +61 -0
- package/dist/commands/auth/index.js +1 -1
- package/dist/commands/branch/create/index.d.ts +3 -1
- package/dist/commands/branch/create/index.js +21 -17
- package/dist/commands/profile/create/index.js +2 -2
- package/dist/commands/profile/edit/index.js +2 -2
- package/dist/commands/profile/me/index.js +21 -2
- package/dist/commands/profile/wizard/index.js +3 -3
- package/dist/commands/profile/workspace/set/index.js +1 -1
- package/dist/commands/{ephemeral → sandbox}/delete/index.d.ts +1 -5
- package/dist/commands/sandbox/delete/index.js +71 -0
- package/dist/commands/{ephemeral → sandbox}/env/delete/index.d.ts +1 -4
- package/dist/commands/{ephemeral → sandbox}/env/delete/index.js +20 -36
- package/dist/commands/{ephemeral → sandbox}/env/get/index.d.ts +1 -4
- package/dist/commands/sandbox/env/get/index.js +65 -0
- package/dist/commands/{ephemeral → sandbox}/env/get_all/index.d.ts +1 -4
- package/dist/commands/sandbox/env/get_all/index.js +78 -0
- package/dist/commands/{ephemeral → sandbox}/env/list/index.d.ts +1 -4
- package/dist/commands/sandbox/env/list/index.js +67 -0
- package/dist/commands/{ephemeral → sandbox}/env/set/index.d.ts +1 -4
- package/dist/commands/sandbox/env/set/index.js +74 -0
- package/dist/commands/{ephemeral → sandbox}/env/set_all/index.d.ts +1 -4
- package/dist/commands/{ephemeral → sandbox}/env/set_all/index.js +19 -35
- package/dist/commands/{ephemeral → sandbox}/get/index.d.ts +1 -4
- package/dist/commands/sandbox/get/index.js +63 -0
- package/dist/commands/sandbox/impersonate/index.d.ts +5 -0
- package/dist/commands/sandbox/impersonate/index.js +5 -0
- package/dist/commands/{ephemeral → sandbox}/license/get/index.d.ts +1 -4
- package/dist/commands/sandbox/license/get/index.js +78 -0
- package/dist/commands/{ephemeral → sandbox}/license/set/index.d.ts +1 -4
- package/dist/commands/{ephemeral → sandbox}/license/set/index.js +20 -36
- package/dist/commands/{ephemeral → sandbox}/pull/index.d.ts +1 -2
- package/dist/commands/{ephemeral → sandbox}/pull/index.js +13 -28
- package/dist/commands/{ephemeral → sandbox}/push/index.d.ts +3 -2
- package/dist/commands/{ephemeral → sandbox}/push/index.js +56 -31
- package/dist/commands/sandbox/reset/index.d.ts +12 -0
- package/dist/commands/sandbox/reset/index.js +71 -0
- package/dist/commands/{ephemeral/impersonate → sandbox/review}/index.d.ts +1 -4
- package/dist/commands/{ephemeral/impersonate → sandbox/review}/index.js +17 -33
- package/dist/commands/{ephemeral/unit_test/run_all → sandbox/unit_test/list}/index.d.ts +1 -2
- package/dist/commands/{ephemeral → sandbox}/unit_test/list/index.js +12 -26
- package/dist/commands/{ephemeral → sandbox}/unit_test/run/index.d.ts +1 -2
- package/dist/commands/{ephemeral → sandbox}/unit_test/run/index.js +11 -25
- package/dist/commands/{ephemeral/unit_test/list → sandbox/unit_test/run_all}/index.d.ts +1 -2
- package/dist/commands/{ephemeral → sandbox}/unit_test/run_all/index.js +11 -23
- package/dist/commands/{ephemeral/workflow_test/run_all → sandbox/workflow_test/list}/index.d.ts +1 -2
- package/dist/commands/{ephemeral → sandbox}/workflow_test/list/index.js +13 -27
- package/dist/commands/{ephemeral/workflow_test/get → sandbox/workflow_test/run}/index.d.ts +1 -2
- package/dist/commands/{ephemeral → sandbox}/workflow_test/run/index.js +11 -25
- package/dist/commands/{ephemeral/workflow_test/list → sandbox/workflow_test/run_all}/index.d.ts +1 -2
- package/dist/commands/{ephemeral → sandbox}/workflow_test/run_all/index.js +11 -23
- package/dist/commands/tenant/create/index.d.ts +2 -1
- package/dist/commands/tenant/create/index.js +23 -6
- package/dist/commands/tenant/deploy_release/index.d.ts +1 -0
- package/dist/commands/tenant/deploy_release/index.js +9 -1
- package/dist/commands/tenant/get/index.js +2 -2
- package/dist/commands/tenant/list/index.js +2 -2
- package/dist/commands/tenant/push/index.js +0 -34
- package/dist/commands/tenant/unit_test/list/index.js +2 -27
- package/dist/commands/tenant/unit_test/run/index.js +2 -27
- package/dist/commands/tenant/unit_test/run_all/index.js +2 -27
- package/dist/commands/tenant/workflow_test/list/index.js +2 -27
- package/dist/commands/tenant/workflow_test/run/index.js +2 -27
- package/dist/commands/tenant/workflow_test/run_all/index.js +2 -27
- package/dist/commands/workspace/edit/index.d.ts +1 -0
- package/dist/commands/workspace/edit/index.js +16 -6
- package/dist/commands/workspace/get/index.js +9 -7
- package/dist/commands/workspace/list/index.d.ts +1 -0
- package/dist/commands/workspace/list/index.js +14 -7
- package/dist/commands/workspace/push/index.d.ts +2 -0
- package/dist/commands/workspace/push/index.js +81 -6
- package/dist/utils/reference-checker.d.ts +57 -0
- package/dist/utils/reference-checker.js +232 -0
- package/oclif.manifest.json +1745 -2451
- package/package.json +8 -8
- package/dist/commands/ephemeral/access/index.d.ts +0 -15
- package/dist/commands/ephemeral/access/index.js +0 -78
- package/dist/commands/ephemeral/create/index.d.ts +0 -17
- package/dist/commands/ephemeral/create/index.js +0 -102
- package/dist/commands/ephemeral/delete/index.js +0 -99
- package/dist/commands/ephemeral/env/get/index.js +0 -81
- package/dist/commands/ephemeral/env/get_all/index.js +0 -94
- package/dist/commands/ephemeral/env/list/index.js +0 -83
- package/dist/commands/ephemeral/env/set/index.js +0 -90
- package/dist/commands/ephemeral/get/index.js +0 -102
- package/dist/commands/ephemeral/license/get/index.js +0 -94
- package/dist/commands/ephemeral/list/index.d.ts +0 -15
- package/dist/commands/ephemeral/list/index.js +0 -109
- package/dist/commands/ephemeral/shared/index.d.ts +0 -15
- package/dist/commands/ephemeral/shared/index.js +0 -108
- package/dist/commands/ephemeral/workflow_test/delete/index.d.ts +0 -18
- package/dist/commands/ephemeral/workflow_test/delete/index.js +0 -75
- package/dist/commands/ephemeral/workflow_test/get/index.js +0 -77
- package/dist/commands/ephemeral/workflow_test/run/index.d.ts +0 -18
- package/dist/commands/tenant/workflow_test/delete/index.d.ts +0 -19
- package/dist/commands/tenant/workflow_test/delete/index.js +0 -110
- package/dist/commands/tenant/workflow_test/get/index.d.ts +0 -19
- package/dist/commands/tenant/workflow_test/get/index.js +0 -112
package/README.md
CHANGED
|
@@ -145,6 +145,8 @@ xano workspace git pull ./output -r https://github.com/owner/repo --path subdir
|
|
|
145
145
|
|
|
146
146
|
All branch commands use **branch labels** (e.g., `v1`, `dev`), not IDs.
|
|
147
147
|
|
|
148
|
+
The `v1` branch is the default branch and always exists. It cannot be created, edited, or deleted.
|
|
149
|
+
|
|
148
150
|
```bash
|
|
149
151
|
# List branches
|
|
150
152
|
xano branch list
|
|
@@ -153,9 +155,9 @@ xano branch list
|
|
|
153
155
|
xano branch get <branch_label>
|
|
154
156
|
|
|
155
157
|
# Create a branch
|
|
156
|
-
xano branch create
|
|
157
|
-
xano branch create
|
|
158
|
-
xano branch create
|
|
158
|
+
xano branch create dev
|
|
159
|
+
xano branch create feature-auth -s dev -d "Auth feature"
|
|
160
|
+
xano branch create staging --color "#ebc346"
|
|
159
161
|
|
|
160
162
|
# Edit a branch
|
|
161
163
|
xano branch edit <branch_label> --label "new-label"
|
|
@@ -307,7 +309,8 @@ xano tenant get <tenant_name>
|
|
|
307
309
|
|
|
308
310
|
# Create a tenant
|
|
309
311
|
xano tenant create "My Tenant"
|
|
310
|
-
xano tenant create "My Tenant" -d "Description" --cluster_id 1 --platform_id 5
|
|
312
|
+
xano tenant create "My Tenant" -d "Description" --type tier2 --cluster_id 1 --platform_id 5
|
|
313
|
+
xano tenant create "My Tenant" --type tier2 --cluster_id 1 --license ./license.yaml
|
|
311
314
|
|
|
312
315
|
# Edit a tenant
|
|
313
316
|
xano tenant edit <tenant_name> --display "New Name" -d "New description"
|
|
@@ -445,18 +448,25 @@ xano tenant cluster license set <cluster_id>
|
|
|
445
448
|
xano tenant cluster license set <cluster_id> --file ./kubeconfig.yaml
|
|
446
449
|
```
|
|
447
450
|
|
|
448
|
-
###
|
|
451
|
+
### Sandbox
|
|
449
452
|
|
|
450
|
-
Manage
|
|
453
|
+
Manage your sandbox tenant. Each user has a single sandbox tenant that is auto-provisioned on first use.
|
|
451
454
|
|
|
452
455
|
```bash
|
|
453
|
-
#
|
|
454
|
-
xano
|
|
455
|
-
xano
|
|
456
|
+
# Get your sandbox tenant (creates if needed)
|
|
457
|
+
xano sandbox get
|
|
458
|
+
xano sandbox get -o json
|
|
459
|
+
|
|
460
|
+
# Push/pull workspace data
|
|
461
|
+
xano sandbox push ./my-workspace
|
|
462
|
+
xano sandbox pull ./my-tenant
|
|
463
|
+
|
|
464
|
+
# Impersonate (open in browser)
|
|
465
|
+
xano sandbox impersonate
|
|
456
466
|
|
|
457
|
-
#
|
|
458
|
-
xano
|
|
459
|
-
xano
|
|
467
|
+
# Reset all workspace data
|
|
468
|
+
xano sandbox reset
|
|
469
|
+
xano sandbox reset --force
|
|
460
470
|
```
|
|
461
471
|
|
|
462
472
|
### Static Hosts
|
package/dist/base-command.d.ts
CHANGED
|
@@ -14,6 +14,17 @@ export interface CredentialsFile {
|
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
16
|
export declare function buildUserAgent(version: string): string;
|
|
17
|
+
export interface SandboxTenant {
|
|
18
|
+
created_at?: string;
|
|
19
|
+
description?: string;
|
|
20
|
+
display?: string;
|
|
21
|
+
ephemeral?: boolean;
|
|
22
|
+
id: number;
|
|
23
|
+
name: string;
|
|
24
|
+
sandbox_expires_at?: string | number;
|
|
25
|
+
state?: string;
|
|
26
|
+
xano_domain?: string;
|
|
27
|
+
}
|
|
17
28
|
export default abstract class BaseCommand extends Command {
|
|
18
29
|
static baseFlags: {
|
|
19
30
|
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -38,6 +49,25 @@ export default abstract class BaseCommand extends Command {
|
|
|
38
49
|
* Load and parse the credentials file. Returns null if the file doesn't exist.
|
|
39
50
|
*/
|
|
40
51
|
protected loadCredentialsFile(): CredentialsFile | null;
|
|
52
|
+
/**
|
|
53
|
+
* Get or create the singleton sandbox environment for the authenticated user.
|
|
54
|
+
* Returns the sandbox object (existing or newly created).
|
|
55
|
+
*/
|
|
56
|
+
protected getOrCreateSandbox(profile: ProfileConfig, verbose: boolean): Promise<SandboxTenant>;
|
|
57
|
+
/**
|
|
58
|
+
* Resolve profile from flags, validating instance_origin and access_token exist.
|
|
59
|
+
*/
|
|
60
|
+
protected resolveProfile(flags: {
|
|
61
|
+
profile?: string;
|
|
62
|
+
}): {
|
|
63
|
+
profile: ProfileConfig;
|
|
64
|
+
profileName: string;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Parse an API error response and return a clean error message.
|
|
68
|
+
* Extracts the message from JSON responses and adds context for common errors.
|
|
69
|
+
*/
|
|
70
|
+
protected parseApiError(response: Response, fallbackPrefix: string): Promise<string>;
|
|
41
71
|
/**
|
|
42
72
|
* Make an HTTP request with optional verbose logging.
|
|
43
73
|
* Use this for all Metadata API calls to support the --verbose flag.
|
package/dist/base-command.js
CHANGED
|
@@ -103,6 +103,67 @@ export default class BaseCommand extends Command {
|
|
|
103
103
|
}
|
|
104
104
|
return null;
|
|
105
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Get or create the singleton sandbox environment for the authenticated user.
|
|
108
|
+
* Returns the sandbox object (existing or newly created).
|
|
109
|
+
*/
|
|
110
|
+
async getOrCreateSandbox(profile, verbose) {
|
|
111
|
+
const apiUrl = `${profile.instance_origin}/api:meta/sandbox/me`;
|
|
112
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
113
|
+
headers: {
|
|
114
|
+
accept: 'application/json',
|
|
115
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
116
|
+
},
|
|
117
|
+
method: 'GET',
|
|
118
|
+
}, verbose, profile.access_token);
|
|
119
|
+
if (!response.ok) {
|
|
120
|
+
const message = await this.parseApiError(response, 'Failed to get sandbox environment');
|
|
121
|
+
this.error(message);
|
|
122
|
+
}
|
|
123
|
+
return (await response.json());
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Resolve profile from flags, validating instance_origin and access_token exist.
|
|
127
|
+
*/
|
|
128
|
+
resolveProfile(flags) {
|
|
129
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
130
|
+
const credentials = this.loadCredentialsFile();
|
|
131
|
+
if (!credentials || !(profileName in credentials.profiles)) {
|
|
132
|
+
this.error(`Profile '${profileName}' not found.\n` + `Create a profile using 'xano profile create'`);
|
|
133
|
+
}
|
|
134
|
+
const profile = credentials.profiles[profileName];
|
|
135
|
+
if (!profile.instance_origin) {
|
|
136
|
+
this.error(`Profile '${profileName}' is missing instance_origin`);
|
|
137
|
+
}
|
|
138
|
+
if (!profile.access_token) {
|
|
139
|
+
this.error(`Profile '${profileName}' is missing access_token`);
|
|
140
|
+
}
|
|
141
|
+
return { profile, profileName };
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Parse an API error response and return a clean error message.
|
|
145
|
+
* Extracts the message from JSON responses and adds context for common errors.
|
|
146
|
+
*/
|
|
147
|
+
async parseApiError(response, fallbackPrefix) {
|
|
148
|
+
const errorText = await response.text();
|
|
149
|
+
let message = `${fallbackPrefix} (${response.status})`;
|
|
150
|
+
try {
|
|
151
|
+
const errorJson = JSON.parse(errorText);
|
|
152
|
+
if (errorJson.message) {
|
|
153
|
+
message = errorJson.message;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
if (errorText) {
|
|
158
|
+
message += `\n${errorText}`;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Provide guidance when sandbox access is denied (free plan restriction)
|
|
162
|
+
if (response.status === 500 && message === 'Access Denied.') {
|
|
163
|
+
message = 'Sandbox is not available on the Free plan. Upgrade your plan to use sandbox features.';
|
|
164
|
+
}
|
|
165
|
+
return message;
|
|
166
|
+
}
|
|
106
167
|
/**
|
|
107
168
|
* Make an HTTP request with optional verbose logging.
|
|
108
169
|
* Use this for all Metadata API calls to support the --verbose flag.
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import BaseCommand from '../../../base-command.js';
|
|
2
2
|
export default class BranchCreate extends BaseCommand {
|
|
3
3
|
static description: string;
|
|
4
|
+
static args: {
|
|
5
|
+
label: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
6
|
+
};
|
|
4
7
|
static examples: string[];
|
|
5
8
|
static flags: {
|
|
6
9
|
color: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
10
|
description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
-
label: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
11
|
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
12
|
source: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
13
|
workspace: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Flags } from '@oclif/core';
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import * as yaml from 'js-yaml';
|
|
3
3
|
import * as fs from 'node:fs';
|
|
4
4
|
import * as os from 'node:os';
|
|
@@ -6,17 +6,23 @@ import * as path from 'node:path';
|
|
|
6
6
|
import BaseCommand from '../../../base-command.js';
|
|
7
7
|
export default class BranchCreate extends BaseCommand {
|
|
8
8
|
static description = 'Create a new branch by cloning from an existing branch';
|
|
9
|
+
static args = {
|
|
10
|
+
label: Args.string({
|
|
11
|
+
description: 'Label for the new branch',
|
|
12
|
+
required: true,
|
|
13
|
+
}),
|
|
14
|
+
};
|
|
9
15
|
static examples = [
|
|
10
|
-
`$ xano branch create
|
|
16
|
+
`$ xano branch create dev
|
|
11
17
|
Created branch: dev
|
|
12
18
|
Cloned from: v1
|
|
13
19
|
`,
|
|
14
|
-
`$ xano branch create
|
|
20
|
+
`$ xano branch create feature-auth -s dev -d "Authentication feature"
|
|
15
21
|
Created branch: feature-auth
|
|
16
22
|
Cloned from: dev
|
|
17
23
|
Description: Authentication feature
|
|
18
24
|
`,
|
|
19
|
-
`$ xano branch create
|
|
25
|
+
`$ xano branch create staging --color "#ebc346" --output json
|
|
20
26
|
{
|
|
21
27
|
"created_at": "2024-02-11T10:00:00Z",
|
|
22
28
|
"label": "staging",
|
|
@@ -37,11 +43,6 @@ Created branch: feature-auth
|
|
|
37
43
|
description: 'Description for the new branch',
|
|
38
44
|
required: false,
|
|
39
45
|
}),
|
|
40
|
-
label: Flags.string({
|
|
41
|
-
char: 'l',
|
|
42
|
-
description: 'Label for the new branch',
|
|
43
|
-
required: true,
|
|
44
|
-
}),
|
|
45
46
|
output: Flags.string({
|
|
46
47
|
char: 'o',
|
|
47
48
|
default: 'summary',
|
|
@@ -62,7 +63,7 @@ Created branch: feature-auth
|
|
|
62
63
|
}),
|
|
63
64
|
};
|
|
64
65
|
async run() {
|
|
65
|
-
const { flags } = await this.parse(BranchCreate);
|
|
66
|
+
const { args, flags } = await this.parse(BranchCreate);
|
|
66
67
|
// Get profile name (default or from flag/env)
|
|
67
68
|
const profileName = flags.profile || this.getDefaultProfile();
|
|
68
69
|
// Load credentials
|
|
@@ -84,11 +85,15 @@ Created branch: feature-auth
|
|
|
84
85
|
const workspaceId = flags.workspace || profile.workspace;
|
|
85
86
|
if (!workspaceId) {
|
|
86
87
|
this.error('No workspace ID provided. Either use --workspace flag or set one in your profile.\n' +
|
|
87
|
-
'Usage: xano branch create
|
|
88
|
+
'Usage: xano branch create <label> [--workspace <workspace_id>]');
|
|
89
|
+
}
|
|
90
|
+
// Validate reserved branch names
|
|
91
|
+
if (args.label.toLowerCase() === 'v1') {
|
|
92
|
+
this.error("Cannot create a branch named 'v1'. This is the default branch and always exists.");
|
|
88
93
|
}
|
|
89
94
|
// Build request body
|
|
90
95
|
const body = {
|
|
91
|
-
label:
|
|
96
|
+
label: args.label,
|
|
92
97
|
source_branch: flags.source,
|
|
93
98
|
};
|
|
94
99
|
if (flags.description) {
|
|
@@ -104,8 +109,8 @@ Created branch: feature-auth
|
|
|
104
109
|
const response = await this.verboseFetch(apiUrl, {
|
|
105
110
|
body: JSON.stringify(body),
|
|
106
111
|
headers: {
|
|
107
|
-
|
|
108
|
-
|
|
112
|
+
accept: 'application/json',
|
|
113
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
109
114
|
'Content-Type': 'application/json',
|
|
110
115
|
},
|
|
111
116
|
method: 'POST',
|
|
@@ -114,7 +119,7 @@ Created branch: feature-auth
|
|
|
114
119
|
const errorText = await response.text();
|
|
115
120
|
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
116
121
|
}
|
|
117
|
-
const branch = await response.json();
|
|
122
|
+
const branch = (await response.json());
|
|
118
123
|
// Output results
|
|
119
124
|
if (flags.output === 'json') {
|
|
120
125
|
this.log(JSON.stringify(branch, null, 2));
|
|
@@ -145,8 +150,7 @@ Created branch: feature-auth
|
|
|
145
150
|
const credentialsPath = path.join(configDir, 'credentials.yaml');
|
|
146
151
|
// Check if credentials file exists
|
|
147
152
|
if (!fs.existsSync(credentialsPath)) {
|
|
148
|
-
this.error(`Credentials file not found at ${credentialsPath}\n` +
|
|
149
|
-
`Create a profile using 'xano profile create'`);
|
|
153
|
+
this.error(`Credentials file not found at ${credentialsPath}\n` + `Create a profile using 'xano profile create'`);
|
|
150
154
|
}
|
|
151
155
|
// Read credentials file
|
|
152
156
|
try {
|
|
@@ -98,8 +98,8 @@ Profile 'selfhosted' created successfully at ~/.xano/credentials.yaml
|
|
|
98
98
|
const profileExists = args.name in credentials.profiles;
|
|
99
99
|
credentials.profiles[args.name] = {
|
|
100
100
|
access_token: flags.access_token,
|
|
101
|
-
account_origin: flags.account_origin ?? '',
|
|
102
|
-
instance_origin: flags.instance_origin,
|
|
101
|
+
account_origin: (flags.account_origin ?? '').replace(/\/+$/, ''),
|
|
102
|
+
instance_origin: flags.instance_origin.replace(/\/+$/, ''),
|
|
103
103
|
...(flags.workspace && { workspace: flags.workspace }),
|
|
104
104
|
...(flags.branch && { branch: flags.branch }),
|
|
105
105
|
...(flags.insecure && { insecure: true }),
|
|
@@ -128,8 +128,8 @@ Profile 'default' updated successfully at ~/.xano/credentials.yaml
|
|
|
128
128
|
// Update only the fields that were provided
|
|
129
129
|
const updatedProfile = {
|
|
130
130
|
...existingProfile,
|
|
131
|
-
...(flags.account_origin !== undefined && { account_origin: flags.account_origin }),
|
|
132
|
-
...(flags.instance_origin !== undefined && { instance_origin: flags.instance_origin }),
|
|
131
|
+
...(flags.account_origin !== undefined && { account_origin: flags.account_origin.replace(/\/+$/, '') }),
|
|
132
|
+
...(flags.instance_origin !== undefined && { instance_origin: flags.instance_origin.replace(/\/+$/, '') }),
|
|
133
133
|
...(flags.access_token !== undefined && { access_token: flags.access_token }),
|
|
134
134
|
...(flags.workspace !== undefined && { workspace: flags.workspace }),
|
|
135
135
|
...(flags.branch !== undefined && { branch: flags.branch }),
|
|
@@ -111,8 +111,27 @@ User Information:
|
|
|
111
111
|
this.log(` Name: ${inst.name}`);
|
|
112
112
|
if (inst.display)
|
|
113
113
|
this.log(` Display: ${inst.display}`);
|
|
114
|
-
if (profile.workspace)
|
|
115
|
-
|
|
114
|
+
if (profile.workspace) {
|
|
115
|
+
let wsLabel = String(profile.workspace);
|
|
116
|
+
try {
|
|
117
|
+
const wsResponse = await this.verboseFetch(`${profile.instance_origin}/api:meta/workspace/${profile.workspace}`, {
|
|
118
|
+
headers: {
|
|
119
|
+
accept: 'application/json',
|
|
120
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
121
|
+
},
|
|
122
|
+
method: 'GET',
|
|
123
|
+
}, false, profile.access_token);
|
|
124
|
+
if (wsResponse.ok) {
|
|
125
|
+
const ws = (await wsResponse.json());
|
|
126
|
+
if (ws.name)
|
|
127
|
+
wsLabel = `${ws.name} (ID: ${profile.workspace})`;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// Fall back to just showing the ID
|
|
132
|
+
}
|
|
133
|
+
this.log(` Workspace: ${wsLabel}`);
|
|
134
|
+
}
|
|
116
135
|
if (profile.branch)
|
|
117
136
|
this.log(` Branch: ${profile.branch}`);
|
|
118
137
|
}
|
|
@@ -126,7 +126,7 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
|
|
|
126
126
|
choices: [
|
|
127
127
|
{ name: '(Skip workspace)', value: '' },
|
|
128
128
|
...workspaces.map((ws) => ({
|
|
129
|
-
name: ws.name
|
|
129
|
+
name: `${ws.name} (${ws.id})`,
|
|
130
130
|
value: ws.id,
|
|
131
131
|
})),
|
|
132
132
|
],
|
|
@@ -339,8 +339,8 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
|
|
|
339
339
|
// Add or update the profile
|
|
340
340
|
credentials.profiles[profile.name] = {
|
|
341
341
|
access_token: profile.access_token,
|
|
342
|
-
account_origin: profile.account_origin,
|
|
343
|
-
instance_origin: profile.instance_origin,
|
|
342
|
+
account_origin: (profile.account_origin || '').replace(/\/+$/, ''),
|
|
343
|
+
instance_origin: profile.instance_origin.replace(/\/+$/, ''),
|
|
344
344
|
...(profile.workspace && { workspace: profile.workspace }),
|
|
345
345
|
...(profile.branch && { branch: profile.branch }),
|
|
346
346
|
...(profile.insecure && { insecure: true }),
|
|
@@ -40,7 +40,7 @@ Workspace updated to 'Production API' (xyz789) on profile 'production'
|
|
|
40
40
|
const { selectedWorkspace } = await inquirer.prompt([
|
|
41
41
|
{
|
|
42
42
|
choices: workspaces.map((ws) => ({
|
|
43
|
-
name: String(ws.id) === String(profile.workspace) ? `${ws.name} (current)` : ws.name
|
|
43
|
+
name: String(ws.id) === String(profile.workspace) ? `${ws.name} (${ws.id}) (current)` : `${ws.name} (${ws.id})`,
|
|
44
44
|
value: ws.id,
|
|
45
45
|
})),
|
|
46
46
|
message: 'Select a workspace',
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import BaseCommand from '../../../base-command.js';
|
|
2
|
-
export default class
|
|
3
|
-
static args: {
|
|
4
|
-
tenant_name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
-
};
|
|
2
|
+
export default class SandboxDelete extends BaseCommand {
|
|
6
3
|
static description: string;
|
|
7
4
|
static examples: string[];
|
|
8
5
|
static flags: {
|
|
9
6
|
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
-
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
7
|
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
8
|
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
9
|
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import BaseCommand from '../../../base-command.js';
|
|
3
|
+
export default class SandboxDelete extends BaseCommand {
|
|
4
|
+
static description = 'Delete your sandbox environment completely (debugging only — it will be re-created on next access)';
|
|
5
|
+
static examples = [
|
|
6
|
+
`$ xano sandbox delete
|
|
7
|
+
Are you sure you want to DELETE your sandbox environment? This destroys all data. (y/N) y
|
|
8
|
+
Sandbox environment deleted.
|
|
9
|
+
`,
|
|
10
|
+
`$ xano sandbox delete --force`,
|
|
11
|
+
];
|
|
12
|
+
static flags = {
|
|
13
|
+
...BaseCommand.baseFlags,
|
|
14
|
+
force: Flags.boolean({
|
|
15
|
+
char: 'f',
|
|
16
|
+
default: false,
|
|
17
|
+
description: 'Skip confirmation prompt',
|
|
18
|
+
required: false,
|
|
19
|
+
}),
|
|
20
|
+
};
|
|
21
|
+
async run() {
|
|
22
|
+
const { flags } = await this.parse(SandboxDelete);
|
|
23
|
+
const { profile } = this.resolveProfile(flags);
|
|
24
|
+
if (!flags.force) {
|
|
25
|
+
const confirmed = await this.confirm(`Are you sure you want to DELETE your sandbox environment? This destroys all data and the tenant will be re-created on next access.`);
|
|
26
|
+
if (!confirmed) {
|
|
27
|
+
this.log('Delete cancelled.');
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const apiUrl = `${profile.instance_origin}/api:meta/sandbox/me`;
|
|
32
|
+
try {
|
|
33
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
34
|
+
headers: {
|
|
35
|
+
accept: 'application/json',
|
|
36
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
37
|
+
'Content-Type': 'application/json',
|
|
38
|
+
},
|
|
39
|
+
method: 'DELETE',
|
|
40
|
+
}, flags.verbose, profile.access_token);
|
|
41
|
+
if (!response.ok) {
|
|
42
|
+
const message = await this.parseApiError(response, 'Failed to delete sandbox environment');
|
|
43
|
+
this.error(message);
|
|
44
|
+
}
|
|
45
|
+
this.log('Sandbox environment deleted.');
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
if (error instanceof Error && 'oclif' in error)
|
|
49
|
+
throw error;
|
|
50
|
+
if (error instanceof Error) {
|
|
51
|
+
this.error(`Failed to delete sandbox environment: ${error.message}`);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
this.error(`Failed to delete sandbox environment: ${String(error)}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async confirm(message) {
|
|
59
|
+
const readline = await import('node:readline');
|
|
60
|
+
const rl = readline.createInterface({
|
|
61
|
+
input: process.stdin,
|
|
62
|
+
output: process.stdout,
|
|
63
|
+
});
|
|
64
|
+
return new Promise((resolve) => {
|
|
65
|
+
rl.question(`${message} (y/N) `, (answer) => {
|
|
66
|
+
rl.close();
|
|
67
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import BaseCommand from '../../../../base-command.js';
|
|
2
|
-
export default class
|
|
3
|
-
static args: {
|
|
4
|
-
tenant_name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
-
};
|
|
2
|
+
export default class SandboxEnvDelete extends BaseCommand {
|
|
6
3
|
static description: string;
|
|
7
4
|
static examples: string[];
|
|
8
5
|
static flags: {
|
|
@@ -1,20 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
2
|
import BaseCommand from '../../../../base-command.js';
|
|
3
|
-
export default class
|
|
4
|
-
static
|
|
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';
|
|
3
|
+
export default class SandboxEnvDelete extends BaseCommand {
|
|
4
|
+
static description = 'Delete an environment variable from a sandbox environment';
|
|
11
5
|
static examples = [
|
|
12
|
-
`$ xano
|
|
13
|
-
Are you sure you want to delete environment variable 'DATABASE_URL'
|
|
14
|
-
Environment variable 'DATABASE_URL' deleted
|
|
6
|
+
`$ xano sandbox env delete --name DATABASE_URL
|
|
7
|
+
Are you sure you want to delete environment variable 'DATABASE_URL'? (y/N) y
|
|
8
|
+
Environment variable 'DATABASE_URL' deleted
|
|
15
9
|
`,
|
|
16
|
-
`$ xano
|
|
17
|
-
`$ xano
|
|
10
|
+
`$ xano sandbox env delete --name DATABASE_URL --force`,
|
|
11
|
+
`$ xano sandbox env delete --name DATABASE_URL -f -o json`,
|
|
18
12
|
];
|
|
19
13
|
static flags = {
|
|
20
14
|
...BaseCommand.baseFlags,
|
|
@@ -38,29 +32,17 @@ Environment variable 'DATABASE_URL' deleted from ephemeral tenant my-tenant
|
|
|
38
32
|
}),
|
|
39
33
|
};
|
|
40
34
|
async run() {
|
|
41
|
-
const {
|
|
42
|
-
const
|
|
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;
|
|
35
|
+
const { flags } = await this.parse(SandboxEnvDelete);
|
|
36
|
+
const { profile } = this.resolveProfile(flags);
|
|
55
37
|
const envName = flags.name;
|
|
56
38
|
if (!flags.force) {
|
|
57
|
-
const confirmed = await this.confirm(`Are you sure you want to delete environment variable '${envName}' from
|
|
39
|
+
const confirmed = await this.confirm(`Are you sure you want to delete environment variable '${envName}' from sandbox environment?`);
|
|
58
40
|
if (!confirmed) {
|
|
59
41
|
this.log('Deletion cancelled.');
|
|
60
42
|
return;
|
|
61
43
|
}
|
|
62
44
|
}
|
|
63
|
-
const apiUrl = `${profile.instance_origin}/api:meta/
|
|
45
|
+
const apiUrl = `${profile.instance_origin}/api:meta/sandbox/env/${envName}`;
|
|
64
46
|
try {
|
|
65
47
|
const response = await this.verboseFetch(apiUrl, {
|
|
66
48
|
headers: {
|
|
@@ -70,22 +52,24 @@ Environment variable 'DATABASE_URL' deleted from ephemeral tenant my-tenant
|
|
|
70
52
|
method: 'DELETE',
|
|
71
53
|
}, flags.verbose, profile.access_token);
|
|
72
54
|
if (!response.ok) {
|
|
73
|
-
const
|
|
74
|
-
this.error(
|
|
55
|
+
const message = await this.parseApiError(response, 'API request failed');
|
|
56
|
+
this.error(message);
|
|
75
57
|
}
|
|
76
58
|
if (flags.output === 'json') {
|
|
77
|
-
this.log(JSON.stringify({ deleted: true, env_name: envName
|
|
59
|
+
this.log(JSON.stringify({ deleted: true, env_name: envName }, null, 2));
|
|
78
60
|
}
|
|
79
61
|
else {
|
|
80
|
-
this.log(`Environment variable '${envName}' deleted from
|
|
62
|
+
this.log(`Environment variable '${envName}' deleted from sandbox environment`);
|
|
81
63
|
}
|
|
82
64
|
}
|
|
83
65
|
catch (error) {
|
|
66
|
+
if (error instanceof Error && 'oclif' in error)
|
|
67
|
+
throw error;
|
|
84
68
|
if (error instanceof Error) {
|
|
85
|
-
this.error(`Failed to delete
|
|
69
|
+
this.error(`Failed to delete sandbox environment variable: ${error.message}`);
|
|
86
70
|
}
|
|
87
71
|
else {
|
|
88
|
-
this.error(`Failed to delete
|
|
72
|
+
this.error(`Failed to delete sandbox environment variable: ${String(error)}`);
|
|
89
73
|
}
|
|
90
74
|
}
|
|
91
75
|
}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import BaseCommand from '../../../../base-command.js';
|
|
2
|
-
export default class
|
|
3
|
-
static args: {
|
|
4
|
-
tenant_name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
-
};
|
|
2
|
+
export default class SandboxEnvGet extends BaseCommand {
|
|
6
3
|
static description: string;
|
|
7
4
|
static examples: string[];
|
|
8
5
|
static flags: {
|