@xano/cli 0.0.37 → 0.0.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +325 -102
- package/dist/commands/auth/index.d.ts +0 -2
- package/dist/commands/auth/index.js +2 -55
- package/dist/commands/profile/create/index.d.ts +0 -2
- package/dist/commands/profile/create/index.js +0 -15
- package/dist/commands/profile/edit/index.d.ts +0 -4
- package/dist/commands/profile/edit/index.js +7 -38
- package/dist/commands/profile/wizard/index.d.ts +0 -2
- package/dist/commands/profile/wizard/index.js +0 -106
- package/dist/commands/profile/{project → workspace}/index.d.ts +1 -1
- package/dist/commands/profile/{project → workspace}/index.js +10 -10
- package/dist/commands/release/delete/index.d.ts +2 -4
- package/dist/commands/release/delete/index.js +39 -12
- package/dist/commands/release/edit/index.d.ts +2 -4
- package/dist/commands/release/edit/index.js +31 -5
- package/dist/commands/release/export/index.d.ts +2 -4
- package/dist/commands/release/export/index.js +39 -11
- package/dist/commands/release/get/index.d.ts +2 -4
- package/dist/commands/release/get/index.js +31 -5
- package/dist/commands/release/pull/index.d.ts +31 -0
- package/dist/commands/release/pull/index.js +345 -0
- package/dist/commands/release/push/index.d.ts +26 -0
- package/dist/commands/release/push/index.js +230 -0
- package/dist/commands/tenant/backup/delete/index.d.ts +1 -1
- package/dist/commands/tenant/backup/delete/index.js +8 -9
- package/dist/commands/tenant/backup/export/index.d.ts +1 -1
- package/dist/commands/tenant/backup/export/index.js +9 -10
- package/dist/commands/tenant/backup/restore/index.d.ts +1 -1
- package/dist/commands/tenant/backup/restore/index.js +8 -9
- package/dist/commands/tenant/cluster/create/index.d.ts +18 -0
- package/dist/commands/tenant/cluster/create/index.js +149 -0
- package/dist/commands/{run/sessions/start → tenant/cluster/delete}/index.d.ts +9 -3
- package/dist/commands/tenant/cluster/delete/index.js +125 -0
- package/dist/commands/tenant/cluster/edit/index.d.ts +22 -0
- package/dist/commands/tenant/cluster/edit/index.js +128 -0
- package/dist/commands/{run/sessions → tenant/cluster}/get/index.d.ts +7 -3
- package/dist/commands/tenant/cluster/get/index.js +114 -0
- package/dist/commands/{run/info → tenant/cluster/license/get}/index.d.ts +10 -7
- package/dist/commands/tenant/cluster/license/get/index.js +118 -0
- package/dist/commands/tenant/cluster/license/set/index.d.ts +21 -0
- package/dist/commands/tenant/cluster/license/set/index.js +132 -0
- package/dist/commands/{run/env → tenant/cluster}/list/index.d.ts +3 -3
- package/dist/commands/tenant/cluster/list/index.js +109 -0
- package/dist/commands/tenant/create/index.d.ts +6 -3
- package/dist/commands/tenant/create/index.js +28 -20
- package/dist/commands/tenant/deploy_platform/index.d.ts +1 -1
- package/dist/commands/tenant/deploy_platform/index.js +8 -9
- package/dist/commands/tenant/deploy_release/index.d.ts +1 -1
- package/dist/commands/tenant/deploy_release/index.js +13 -13
- package/dist/commands/tenant/env/delete/index.d.ts +19 -0
- package/dist/commands/tenant/env/delete/index.js +139 -0
- package/dist/commands/{run/projects/create → tenant/env/get}/index.d.ts +7 -4
- package/dist/commands/tenant/env/get/index.js +113 -0
- package/dist/commands/{run/projects/update → tenant/env/get_all}/index.d.ts +7 -5
- package/dist/commands/tenant/env/get_all/index.js +123 -0
- package/dist/commands/{run/secrets/get → tenant/env/list}/index.d.ts +5 -3
- package/dist/commands/tenant/env/list/index.js +116 -0
- package/dist/commands/tenant/env/set/index.d.ts +18 -0
- package/dist/commands/tenant/env/set/index.js +122 -0
- package/dist/commands/tenant/env/set_all/index.d.ts +18 -0
- package/dist/commands/tenant/env/set_all/index.js +131 -0
- package/dist/commands/tenant/get/index.js +6 -5
- package/dist/commands/tenant/impersonate/index.d.ts +19 -0
- package/dist/commands/tenant/impersonate/index.js +146 -0
- package/dist/commands/tenant/license/get/index.d.ts +18 -0
- package/dist/commands/tenant/license/get/index.js +127 -0
- package/dist/commands/tenant/license/set/index.d.ts +19 -0
- package/dist/commands/tenant/license/set/index.js +141 -0
- package/dist/commands/tenant/list/index.js +6 -6
- package/dist/commands/tenant/pull/index.d.ts +31 -0
- package/dist/commands/tenant/pull/index.js +327 -0
- package/dist/commands/tenant/push/index.d.ts +24 -0
- package/dist/commands/tenant/push/index.js +245 -0
- package/oclif.manifest.json +2076 -1670
- package/package.json +1 -19
- package/dist/commands/run/env/delete/index.d.ts +0 -14
- package/dist/commands/run/env/delete/index.js +0 -65
- package/dist/commands/run/env/get/index.d.ts +0 -14
- package/dist/commands/run/env/get/index.js +0 -52
- package/dist/commands/run/env/list/index.js +0 -56
- package/dist/commands/run/env/set/index.d.ts +0 -14
- package/dist/commands/run/env/set/index.js +0 -51
- package/dist/commands/run/exec/index.d.ts +0 -31
- package/dist/commands/run/exec/index.js +0 -431
- package/dist/commands/run/info/index.js +0 -160
- package/dist/commands/run/projects/create/index.js +0 -75
- package/dist/commands/run/projects/delete/index.d.ts +0 -14
- package/dist/commands/run/projects/delete/index.js +0 -65
- package/dist/commands/run/projects/list/index.d.ts +0 -13
- package/dist/commands/run/projects/list/index.js +0 -66
- package/dist/commands/run/projects/update/index.js +0 -86
- package/dist/commands/run/secrets/delete/index.d.ts +0 -14
- package/dist/commands/run/secrets/delete/index.js +0 -65
- package/dist/commands/run/secrets/get/index.js +0 -52
- package/dist/commands/run/secrets/list/index.d.ts +0 -12
- package/dist/commands/run/secrets/list/index.js +0 -60
- package/dist/commands/run/secrets/set/index.d.ts +0 -16
- package/dist/commands/run/secrets/set/index.js +0 -74
- package/dist/commands/run/sessions/delete/index.d.ts +0 -14
- package/dist/commands/run/sessions/delete/index.js +0 -65
- package/dist/commands/run/sessions/get/index.js +0 -72
- package/dist/commands/run/sessions/list/index.d.ts +0 -13
- package/dist/commands/run/sessions/list/index.js +0 -64
- package/dist/commands/run/sessions/start/index.js +0 -56
- package/dist/commands/run/sessions/stop/index.d.ts +0 -14
- package/dist/commands/run/sessions/stop/index.js +0 -56
- package/dist/commands/run/sink/get/index.d.ts +0 -14
- package/dist/commands/run/sink/get/index.js +0 -63
- package/dist/lib/base-run-command.d.ts +0 -41
- package/dist/lib/base-run-command.js +0 -75
- package/dist/lib/run-http-client.d.ts +0 -64
- package/dist/lib/run-http-client.js +0 -171
- package/dist/lib/run-types.d.ts +0 -226
- package/dist/lib/run-types.js +0 -5
|
@@ -20,9 +20,6 @@ Profile 'staging' created successfully at ~/.xano/credentials.yaml
|
|
|
20
20
|
`,
|
|
21
21
|
`$ xano profile:create dev -i https://dev-instance.xano.com -t token789 -w my-workspace -b feature-branch
|
|
22
22
|
Profile 'dev' created successfully at ~/.xano/credentials.yaml
|
|
23
|
-
`,
|
|
24
|
-
`$ xano profile:create dev -i https://dev-instance.xano.com -t token789 -w my-workspace -b feature-branch -j my-project
|
|
25
|
-
Profile 'dev' created successfully at ~/.xano/credentials.yaml
|
|
26
23
|
`,
|
|
27
24
|
`$ xano profile:create production --account_origin https://account.xano.com --instance_origin https://instance.xano.com --access_token token123 --default
|
|
28
25
|
Profile 'production' created successfully at ~/.xano/credentials.yaml
|
|
@@ -55,16 +52,6 @@ Default profile set to 'production'
|
|
|
55
52
|
description: 'Instance origin URL',
|
|
56
53
|
required: true,
|
|
57
54
|
}),
|
|
58
|
-
project: Flags.string({
|
|
59
|
-
char: 'j',
|
|
60
|
-
description: 'Project name',
|
|
61
|
-
required: false,
|
|
62
|
-
}),
|
|
63
|
-
run_base_url: Flags.string({
|
|
64
|
-
char: 'r',
|
|
65
|
-
description: 'Xano Run API base URL (default: https://app.xano.com/)',
|
|
66
|
-
required: false,
|
|
67
|
-
}),
|
|
68
55
|
workspace: Flags.string({
|
|
69
56
|
char: 'w',
|
|
70
57
|
description: 'Workspace name',
|
|
@@ -106,8 +93,6 @@ Default profile set to 'production'
|
|
|
106
93
|
instance_origin: flags.instance_origin,
|
|
107
94
|
...(flags.workspace && { workspace: flags.workspace }),
|
|
108
95
|
...(flags.branch && { branch: flags.branch }),
|
|
109
|
-
...(flags.project && { project: flags.project }),
|
|
110
|
-
...(flags.run_base_url && { run_base_url: flags.run_base_url }),
|
|
111
96
|
};
|
|
112
97
|
// Set default if flag is provided
|
|
113
98
|
if (flags.default) {
|
|
@@ -10,12 +10,8 @@ export default class ProfileEdit extends BaseCommand {
|
|
|
10
10
|
account_origin: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
11
|
branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
12
|
instance_origin: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
-
project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
13
|
'remove-branch': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
|
-
'remove-project': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
|
-
'remove-run-base-url': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
14
|
'remove-workspace': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
18
|
-
run_base_url: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
15
|
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
16
|
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
21
17
|
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
@@ -27,12 +27,6 @@ Profile 'default' updated successfully at ~/.xano/credentials.yaml
|
|
|
27
27
|
`,
|
|
28
28
|
`$ xano profile:edit --remove-branch
|
|
29
29
|
Profile 'default' updated successfully at ~/.xano/credentials.yaml
|
|
30
|
-
`,
|
|
31
|
-
`$ xano profile:edit -j my-project
|
|
32
|
-
Profile 'default' updated successfully at ~/.xano/credentials.yaml
|
|
33
|
-
`,
|
|
34
|
-
`$ xano profile:edit --remove-project
|
|
35
|
-
Profile 'default' updated successfully at ~/.xano/credentials.yaml
|
|
36
30
|
`,
|
|
37
31
|
];
|
|
38
32
|
static flags = {
|
|
@@ -57,36 +51,16 @@ Profile 'default' updated successfully at ~/.xano/credentials.yaml
|
|
|
57
51
|
description: 'Update instance origin URL',
|
|
58
52
|
required: false,
|
|
59
53
|
}),
|
|
60
|
-
project: Flags.string({
|
|
61
|
-
char: 'j',
|
|
62
|
-
description: 'Update project ID',
|
|
63
|
-
required: false,
|
|
64
|
-
}),
|
|
65
54
|
'remove-branch': Flags.boolean({
|
|
66
55
|
default: false,
|
|
67
56
|
description: 'Remove branch from profile',
|
|
68
57
|
required: false,
|
|
69
58
|
}),
|
|
70
|
-
'remove-project': Flags.boolean({
|
|
71
|
-
default: false,
|
|
72
|
-
description: 'Remove project from profile',
|
|
73
|
-
required: false,
|
|
74
|
-
}),
|
|
75
|
-
'remove-run-base-url': Flags.boolean({
|
|
76
|
-
default: false,
|
|
77
|
-
description: 'Remove run_base_url from profile (use default)',
|
|
78
|
-
required: false,
|
|
79
|
-
}),
|
|
80
59
|
'remove-workspace': Flags.boolean({
|
|
81
60
|
default: false,
|
|
82
61
|
description: 'Remove workspace from profile',
|
|
83
62
|
required: false,
|
|
84
63
|
}),
|
|
85
|
-
run_base_url: Flags.string({
|
|
86
|
-
char: 'r',
|
|
87
|
-
description: 'Update Xano Run API base URL',
|
|
88
|
-
required: false,
|
|
89
|
-
}),
|
|
90
64
|
workspace: Flags.string({
|
|
91
65
|
char: 'w',
|
|
92
66
|
description: 'Update workspace name',
|
|
@@ -123,10 +97,13 @@ Profile 'default' updated successfully at ~/.xano/credentials.yaml
|
|
|
123
97
|
// Get the existing profile
|
|
124
98
|
const existingProfile = credentials.profiles[profileName];
|
|
125
99
|
// Check if any flags were provided
|
|
126
|
-
const hasFlags = flags.account_origin ||
|
|
127
|
-
flags.
|
|
128
|
-
flags
|
|
129
|
-
flags
|
|
100
|
+
const hasFlags = flags.account_origin ||
|
|
101
|
+
flags.instance_origin ||
|
|
102
|
+
flags.access_token ||
|
|
103
|
+
flags.workspace ||
|
|
104
|
+
flags.branch ||
|
|
105
|
+
flags['remove-workspace'] ||
|
|
106
|
+
flags['remove-branch'];
|
|
130
107
|
if (!hasFlags) {
|
|
131
108
|
this.error('No fields specified to update. Use at least one flag to edit the profile.');
|
|
132
109
|
}
|
|
@@ -138,8 +115,6 @@ Profile 'default' updated successfully at ~/.xano/credentials.yaml
|
|
|
138
115
|
...(flags.access_token !== undefined && { access_token: flags.access_token }),
|
|
139
116
|
...(flags.workspace !== undefined && { workspace: flags.workspace }),
|
|
140
117
|
...(flags.branch !== undefined && { branch: flags.branch }),
|
|
141
|
-
...(flags.project !== undefined && { project: flags.project }),
|
|
142
|
-
...(flags.run_base_url !== undefined && { run_base_url: flags.run_base_url }),
|
|
143
118
|
};
|
|
144
119
|
// Handle removal flags
|
|
145
120
|
if (flags['remove-workspace']) {
|
|
@@ -148,12 +123,6 @@ Profile 'default' updated successfully at ~/.xano/credentials.yaml
|
|
|
148
123
|
if (flags['remove-branch']) {
|
|
149
124
|
delete updatedProfile.branch;
|
|
150
125
|
}
|
|
151
|
-
if (flags['remove-project']) {
|
|
152
|
-
delete updatedProfile.project;
|
|
153
|
-
}
|
|
154
|
-
if (flags['remove-run-base-url']) {
|
|
155
|
-
delete updatedProfile.run_base_url;
|
|
156
|
-
}
|
|
157
126
|
credentials.profiles[profileName] = updatedProfile;
|
|
158
127
|
// Write the updated credentials back to the file
|
|
159
128
|
try {
|
|
@@ -9,8 +9,6 @@ export default class ProfileWizard extends Command {
|
|
|
9
9
|
run(): Promise<void>;
|
|
10
10
|
private fetchBranches;
|
|
11
11
|
private fetchInstances;
|
|
12
|
-
private fetchProjects;
|
|
13
|
-
private fetchRunProjects;
|
|
14
12
|
private fetchWorkspaces;
|
|
15
13
|
private getDefaultProfileName;
|
|
16
14
|
private saveProfile;
|
|
@@ -4,7 +4,6 @@ import * as yaml from 'js-yaml';
|
|
|
4
4
|
import * as fs from 'node:fs';
|
|
5
5
|
import * as os from 'node:os';
|
|
6
6
|
import * as path from 'node:path';
|
|
7
|
-
import { DEFAULT_RUN_BASE_URL } from '../../../lib/run-http-client.js';
|
|
8
7
|
export default class ProfileWizard extends Command {
|
|
9
8
|
static description = 'Create a new profile configuration using an interactive wizard';
|
|
10
9
|
static examples = [
|
|
@@ -97,7 +96,6 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
|
|
|
97
96
|
// Step 5: Workspace selection
|
|
98
97
|
let workspace;
|
|
99
98
|
let branch;
|
|
100
|
-
let project;
|
|
101
99
|
// Fetch workspaces from the selected instance
|
|
102
100
|
this.log('');
|
|
103
101
|
this.log('Fetching available workspaces...');
|
|
@@ -157,56 +155,8 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
|
|
|
157
155
|
]);
|
|
158
156
|
branch = selectedBranch || undefined;
|
|
159
157
|
}
|
|
160
|
-
// Step 6: Project selection
|
|
161
|
-
this.log('');
|
|
162
|
-
this.log('Fetching available projects...');
|
|
163
|
-
let projects = [];
|
|
164
|
-
try {
|
|
165
|
-
projects = await this.fetchProjects(accessToken, selectedInstance.origin, workspace, branch);
|
|
166
|
-
}
|
|
167
|
-
catch (error) {
|
|
168
|
-
this.warn(`Failed to fetch projects: ${error instanceof Error ? error.message : String(error)}`);
|
|
169
|
-
}
|
|
170
|
-
// If projects were fetched, let user select one
|
|
171
|
-
if (projects.length > 0) {
|
|
172
|
-
this.log('');
|
|
173
|
-
const { selectedProject } = await inquirer.prompt([
|
|
174
|
-
{
|
|
175
|
-
choices: [
|
|
176
|
-
{ name: '(Skip project)', value: '' },
|
|
177
|
-
...projects.map((proj) => ({
|
|
178
|
-
name: proj.name,
|
|
179
|
-
value: proj.id,
|
|
180
|
-
})),
|
|
181
|
-
],
|
|
182
|
-
message: 'Select a project',
|
|
183
|
-
name: 'selectedProject',
|
|
184
|
-
type: 'list',
|
|
185
|
-
},
|
|
186
|
-
]);
|
|
187
|
-
project = selectedProject || undefined;
|
|
188
|
-
}
|
|
189
158
|
}
|
|
190
159
|
}
|
|
191
|
-
// Step 7: Fetch run projects and auto-select the first one if no project was selected
|
|
192
|
-
this.log('');
|
|
193
|
-
this.log('Fetching available run projects...');
|
|
194
|
-
try {
|
|
195
|
-
const runProjects = await this.fetchRunProjects(accessToken);
|
|
196
|
-
if (runProjects.length > 0) {
|
|
197
|
-
// Use run project if no metadata project was selected
|
|
198
|
-
if (!project) {
|
|
199
|
-
project = runProjects[0].id;
|
|
200
|
-
}
|
|
201
|
-
this.log(`✓ Found ${runProjects.length} run project(s). Using "${runProjects[0].name}" as default.`);
|
|
202
|
-
}
|
|
203
|
-
else {
|
|
204
|
-
this.log('No run projects found. You can create one later with "xano run projects create".');
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
catch {
|
|
208
|
-
// Silently ignore - project will remain undefined
|
|
209
|
-
}
|
|
210
160
|
// Save profile
|
|
211
161
|
await this.saveProfile({
|
|
212
162
|
access_token: accessToken,
|
|
@@ -214,7 +164,6 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
|
|
|
214
164
|
branch,
|
|
215
165
|
instance_origin: selectedInstance.origin,
|
|
216
166
|
name: profileName,
|
|
217
|
-
project,
|
|
218
167
|
workspace,
|
|
219
168
|
}, true);
|
|
220
169
|
this.log('');
|
|
@@ -302,60 +251,6 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
|
|
|
302
251
|
}
|
|
303
252
|
return [];
|
|
304
253
|
}
|
|
305
|
-
async fetchProjects(accessToken, origin, workspaceId, branchId) {
|
|
306
|
-
const branchParam = branchId ? `?branch=${branchId}` : '';
|
|
307
|
-
const response = await fetch(`${origin}/api:meta/workspace/${workspaceId}/project${branchParam}`, {
|
|
308
|
-
headers: {
|
|
309
|
-
accept: 'application/json',
|
|
310
|
-
Authorization: `Bearer ${accessToken}`,
|
|
311
|
-
},
|
|
312
|
-
method: 'GET',
|
|
313
|
-
});
|
|
314
|
-
if (!response.ok) {
|
|
315
|
-
if (response.status === 401) {
|
|
316
|
-
throw new Error('Unauthorized. Please check your access token.');
|
|
317
|
-
}
|
|
318
|
-
throw new Error(`API request failed with status ${response.status}`);
|
|
319
|
-
}
|
|
320
|
-
const data = (await response.json());
|
|
321
|
-
// Transform API response to Project format
|
|
322
|
-
// Assuming the API returns an array or object with projects
|
|
323
|
-
if (Array.isArray(data)) {
|
|
324
|
-
return data.map((proj) => ({
|
|
325
|
-
id: proj.id || proj.name,
|
|
326
|
-
name: proj.name,
|
|
327
|
-
}));
|
|
328
|
-
}
|
|
329
|
-
// If it's an object, try to extract projects
|
|
330
|
-
if (data && typeof data === 'object') {
|
|
331
|
-
const projects = data.projects || data.data || [];
|
|
332
|
-
if (Array.isArray(projects)) {
|
|
333
|
-
return projects.map((proj) => ({
|
|
334
|
-
id: proj.id || proj.name,
|
|
335
|
-
name: proj.name,
|
|
336
|
-
}));
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
return [];
|
|
340
|
-
}
|
|
341
|
-
async fetchRunProjects(accessToken, runBaseUrl = DEFAULT_RUN_BASE_URL) {
|
|
342
|
-
const baseUrl = runBaseUrl.endsWith('/') ? runBaseUrl.slice(0, -1) : runBaseUrl;
|
|
343
|
-
const response = await fetch(`${baseUrl}/api:run/project`, {
|
|
344
|
-
headers: {
|
|
345
|
-
Authorization: `Bearer ${accessToken}`,
|
|
346
|
-
'Content-Type': 'application/json',
|
|
347
|
-
},
|
|
348
|
-
method: 'GET',
|
|
349
|
-
});
|
|
350
|
-
if (!response.ok) {
|
|
351
|
-
if (response.status === 401) {
|
|
352
|
-
throw new Error('Unauthorized. Please check your access token.');
|
|
353
|
-
}
|
|
354
|
-
throw new Error(`API request failed with status ${response.status}`);
|
|
355
|
-
}
|
|
356
|
-
const data = (await response.json());
|
|
357
|
-
return Array.isArray(data) ? data : [];
|
|
358
|
-
}
|
|
359
254
|
async fetchWorkspaces(accessToken, origin) {
|
|
360
255
|
const response = await fetch(`${origin}/api:meta/workspace`, {
|
|
361
256
|
headers: {
|
|
@@ -437,7 +332,6 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
|
|
|
437
332
|
instance_origin: profile.instance_origin,
|
|
438
333
|
...(profile.workspace && { workspace: profile.workspace }),
|
|
439
334
|
...(profile.branch && { branch: profile.branch }),
|
|
440
|
-
...(profile.project && { project: profile.project }),
|
|
441
335
|
};
|
|
442
336
|
// Set as default if requested
|
|
443
337
|
if (setAsDefault) {
|
|
@@ -3,14 +3,14 @@ import * as yaml from 'js-yaml';
|
|
|
3
3
|
import * as fs from 'node:fs';
|
|
4
4
|
import * as os from 'node:os';
|
|
5
5
|
import * as path from 'node:path';
|
|
6
|
-
export default class
|
|
7
|
-
static description = 'Print the
|
|
6
|
+
export default class ProfileWorkspace extends Command {
|
|
7
|
+
static description = 'Print the workspace ID for the default profile';
|
|
8
8
|
static examples = [
|
|
9
|
-
`$ xano profile:
|
|
10
|
-
|
|
9
|
+
`$ xano profile:workspace
|
|
10
|
+
abc123-workspace-id
|
|
11
11
|
`,
|
|
12
|
-
`$ xano profile:
|
|
13
|
-
# Copies the
|
|
12
|
+
`$ xano profile:workspace | pbcopy
|
|
13
|
+
# Copies the workspace ID to clipboard on macOS
|
|
14
14
|
`,
|
|
15
15
|
];
|
|
16
16
|
async run() {
|
|
@@ -43,12 +43,12 @@ my-project-id
|
|
|
43
43
|
this.error(`Default profile '${defaultProfileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}`);
|
|
44
44
|
}
|
|
45
45
|
const profile = credentials.profiles[defaultProfileName];
|
|
46
|
-
// Get and display the
|
|
47
|
-
if (profile.
|
|
48
|
-
this.log(profile.
|
|
46
|
+
// Get and display the workspace ID
|
|
47
|
+
if (profile.workspace) {
|
|
48
|
+
this.log(profile.workspace);
|
|
49
49
|
}
|
|
50
50
|
else {
|
|
51
|
-
this.error(`Profile '${defaultProfileName}' does not have a
|
|
51
|
+
this.error(`Profile '${defaultProfileName}' does not have a workspace set. Set one using 'profile:edit ${defaultProfileName} -w <workspace_id>'.`);
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import BaseCommand from '../../../base-command.js';
|
|
2
2
|
export default class ReleaseDelete extends BaseCommand {
|
|
3
3
|
static args: {
|
|
4
|
-
|
|
5
|
-
max?: number;
|
|
6
|
-
min?: number;
|
|
7
|
-
}>;
|
|
4
|
+
release_name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
8
5
|
};
|
|
9
6
|
static description: string;
|
|
10
7
|
static examples: string[];
|
|
@@ -18,4 +15,5 @@ export default class ReleaseDelete extends BaseCommand {
|
|
|
18
15
|
run(): Promise<void>;
|
|
19
16
|
private confirm;
|
|
20
17
|
private loadCredentials;
|
|
18
|
+
private resolveReleaseName;
|
|
21
19
|
}
|
|
@@ -6,21 +6,21 @@ import * as path from 'node:path';
|
|
|
6
6
|
import BaseCommand from '../../../base-command.js';
|
|
7
7
|
export default class ReleaseDelete extends BaseCommand {
|
|
8
8
|
static args = {
|
|
9
|
-
|
|
10
|
-
description: 'Release
|
|
9
|
+
release_name: Args.string({
|
|
10
|
+
description: 'Release name to delete',
|
|
11
11
|
required: true,
|
|
12
12
|
}),
|
|
13
13
|
};
|
|
14
14
|
static description = 'Delete a release permanently. This action cannot be undone.';
|
|
15
15
|
static examples = [
|
|
16
|
-
`$ xano release delete
|
|
17
|
-
Are you sure you want to delete release
|
|
18
|
-
Deleted release
|
|
16
|
+
`$ xano release delete v1.0
|
|
17
|
+
Are you sure you want to delete release 'v1.0'? This action cannot be undone. (y/N) y
|
|
18
|
+
Deleted release 'v1.0'
|
|
19
19
|
`,
|
|
20
|
-
`$ xano release delete
|
|
21
|
-
Deleted release
|
|
20
|
+
`$ xano release delete v1.0 --force
|
|
21
|
+
Deleted release 'v1.0'
|
|
22
22
|
`,
|
|
23
|
-
`$ xano release delete
|
|
23
|
+
`$ xano release delete v1.0 -f -o json`,
|
|
24
24
|
];
|
|
25
25
|
static flags = {
|
|
26
26
|
...BaseCommand.baseFlags,
|
|
@@ -62,14 +62,15 @@ Deleted release #10
|
|
|
62
62
|
if (!workspaceId) {
|
|
63
63
|
this.error('No workspace ID provided. Use --workspace flag or set one in your profile.');
|
|
64
64
|
}
|
|
65
|
-
const
|
|
65
|
+
const releaseName = args.release_name;
|
|
66
66
|
if (!flags.force) {
|
|
67
|
-
const confirmed = await this.confirm(`Are you sure you want to delete release
|
|
67
|
+
const confirmed = await this.confirm(`Are you sure you want to delete release '${releaseName}'? This action cannot be undone.`);
|
|
68
68
|
if (!confirmed) {
|
|
69
69
|
this.log('Deletion cancelled.');
|
|
70
70
|
return;
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
|
+
const releaseId = await this.resolveReleaseName(profile, workspaceId, releaseName, flags.verbose);
|
|
73
74
|
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/release/${releaseId}`;
|
|
74
75
|
try {
|
|
75
76
|
const response = await this.verboseFetch(apiUrl, {
|
|
@@ -84,10 +85,10 @@ Deleted release #10
|
|
|
84
85
|
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
85
86
|
}
|
|
86
87
|
if (flags.output === 'json') {
|
|
87
|
-
this.log(JSON.stringify({ deleted: true,
|
|
88
|
+
this.log(JSON.stringify({ deleted: true, release_name: releaseName }, null, 2));
|
|
88
89
|
}
|
|
89
90
|
else {
|
|
90
|
-
this.log(`Deleted release
|
|
91
|
+
this.log(`Deleted release '${releaseName}'`);
|
|
91
92
|
}
|
|
92
93
|
}
|
|
93
94
|
catch (error) {
|
|
@@ -131,4 +132,30 @@ Deleted release #10
|
|
|
131
132
|
this.error(`Failed to parse credentials file: ${error}`);
|
|
132
133
|
}
|
|
133
134
|
}
|
|
135
|
+
async resolveReleaseName(profile, workspaceId, releaseName, verbose) {
|
|
136
|
+
const listUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/release`;
|
|
137
|
+
const response = await this.verboseFetch(listUrl, {
|
|
138
|
+
headers: {
|
|
139
|
+
'accept': 'application/json',
|
|
140
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
141
|
+
},
|
|
142
|
+
method: 'GET',
|
|
143
|
+
}, verbose, profile.access_token);
|
|
144
|
+
if (!response.ok) {
|
|
145
|
+
const errorText = await response.text();
|
|
146
|
+
this.error(`Failed to list releases: ${response.status} ${response.statusText}\n${errorText}`);
|
|
147
|
+
}
|
|
148
|
+
const data = await response.json();
|
|
149
|
+
const releases = Array.isArray(data)
|
|
150
|
+
? data
|
|
151
|
+
: (data && typeof data === 'object' && 'items' in data && Array.isArray(data.items))
|
|
152
|
+
? data.items
|
|
153
|
+
: [];
|
|
154
|
+
const match = releases.find(r => r.name === releaseName);
|
|
155
|
+
if (!match) {
|
|
156
|
+
const available = releases.map(r => r.name).join(', ');
|
|
157
|
+
this.error(`Release '${releaseName}' not found.${available ? ` Available releases: ${available}` : ''}`);
|
|
158
|
+
}
|
|
159
|
+
return match.id;
|
|
160
|
+
}
|
|
134
161
|
}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import BaseCommand from '../../../base-command.js';
|
|
2
2
|
export default class ReleaseEdit extends BaseCommand {
|
|
3
3
|
static args: {
|
|
4
|
-
|
|
5
|
-
max?: number;
|
|
6
|
-
min?: number;
|
|
7
|
-
}>;
|
|
4
|
+
release_name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
8
5
|
};
|
|
9
6
|
static description: string;
|
|
10
7
|
static examples: string[];
|
|
@@ -18,4 +15,5 @@ export default class ReleaseEdit extends BaseCommand {
|
|
|
18
15
|
};
|
|
19
16
|
run(): Promise<void>;
|
|
20
17
|
private loadCredentials;
|
|
18
|
+
private resolveReleaseName;
|
|
21
19
|
}
|
|
@@ -6,17 +6,17 @@ import * as path from 'node:path';
|
|
|
6
6
|
import BaseCommand from '../../../base-command.js';
|
|
7
7
|
export default class ReleaseEdit extends BaseCommand {
|
|
8
8
|
static args = {
|
|
9
|
-
|
|
10
|
-
description: 'Release
|
|
9
|
+
release_name: Args.string({
|
|
10
|
+
description: 'Release name to edit',
|
|
11
11
|
required: true,
|
|
12
12
|
}),
|
|
13
13
|
};
|
|
14
14
|
static description = 'Edit an existing release';
|
|
15
15
|
static examples = [
|
|
16
|
-
`$ xano release edit
|
|
16
|
+
`$ xano release edit v1.0 --name "v1.0-final" --description "Updated description"
|
|
17
17
|
Updated release: v1.0-final - ID: 10
|
|
18
18
|
`,
|
|
19
|
-
`$ xano release edit
|
|
19
|
+
`$ xano release edit v1.0 --description "New description" -o json`,
|
|
20
20
|
];
|
|
21
21
|
static flags = {
|
|
22
22
|
...BaseCommand.baseFlags,
|
|
@@ -62,7 +62,7 @@ Updated release: v1.0-final - ID: 10
|
|
|
62
62
|
if (!workspaceId) {
|
|
63
63
|
this.error('No workspace ID provided. Use --workspace flag or set one in your profile.');
|
|
64
64
|
}
|
|
65
|
-
const releaseId = args.
|
|
65
|
+
const releaseId = await this.resolveReleaseName(profile, workspaceId, args.release_name, flags.verbose);
|
|
66
66
|
const baseUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/release/${releaseId}`;
|
|
67
67
|
const headers = {
|
|
68
68
|
'accept': 'application/json',
|
|
@@ -134,4 +134,30 @@ Updated release: v1.0-final - ID: 10
|
|
|
134
134
|
this.error(`Failed to parse credentials file: ${error}`);
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
|
+
async resolveReleaseName(profile, workspaceId, releaseName, verbose) {
|
|
138
|
+
const listUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/release`;
|
|
139
|
+
const response = await this.verboseFetch(listUrl, {
|
|
140
|
+
headers: {
|
|
141
|
+
'accept': 'application/json',
|
|
142
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
143
|
+
},
|
|
144
|
+
method: 'GET',
|
|
145
|
+
}, verbose, profile.access_token);
|
|
146
|
+
if (!response.ok) {
|
|
147
|
+
const errorText = await response.text();
|
|
148
|
+
this.error(`Failed to list releases: ${response.status} ${response.statusText}\n${errorText}`);
|
|
149
|
+
}
|
|
150
|
+
const data = await response.json();
|
|
151
|
+
const releases = Array.isArray(data)
|
|
152
|
+
? data
|
|
153
|
+
: (data && typeof data === 'object' && 'items' in data && Array.isArray(data.items))
|
|
154
|
+
? data.items
|
|
155
|
+
: [];
|
|
156
|
+
const match = releases.find(r => r.name === releaseName);
|
|
157
|
+
if (!match) {
|
|
158
|
+
const available = releases.map(r => r.name).join(', ');
|
|
159
|
+
this.error(`Release '${releaseName}' not found.${available ? ` Available releases: ${available}` : ''}`);
|
|
160
|
+
}
|
|
161
|
+
return match.id;
|
|
162
|
+
}
|
|
137
163
|
}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import BaseCommand from '../../../base-command.js';
|
|
2
2
|
export default class ReleaseExport extends BaseCommand {
|
|
3
3
|
static args: {
|
|
4
|
-
|
|
5
|
-
max?: number;
|
|
6
|
-
min?: number;
|
|
7
|
-
}>;
|
|
4
|
+
release_name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
8
5
|
};
|
|
9
6
|
static description: string;
|
|
10
7
|
static examples: string[];
|
|
@@ -17,4 +14,5 @@ export default class ReleaseExport extends BaseCommand {
|
|
|
17
14
|
};
|
|
18
15
|
run(): Promise<void>;
|
|
19
16
|
private loadCredentials;
|
|
17
|
+
private resolveReleaseName;
|
|
20
18
|
}
|
|
@@ -6,18 +6,18 @@ import * as yaml from 'js-yaml';
|
|
|
6
6
|
import BaseCommand from '../../../base-command.js';
|
|
7
7
|
export default class ReleaseExport extends BaseCommand {
|
|
8
8
|
static args = {
|
|
9
|
-
|
|
10
|
-
description: 'Release
|
|
9
|
+
release_name: Args.string({
|
|
10
|
+
description: 'Release name to export',
|
|
11
11
|
required: true,
|
|
12
12
|
}),
|
|
13
13
|
};
|
|
14
14
|
static description = 'Export (download) a release to a local file';
|
|
15
15
|
static examples = [
|
|
16
|
-
`$ xano release export
|
|
17
|
-
Downloaded release
|
|
16
|
+
`$ xano release export v1.0
|
|
17
|
+
Downloaded release 'v1.0' to ./release-v1.0.tar.gz
|
|
18
18
|
`,
|
|
19
|
-
`$ xano release export
|
|
20
|
-
`$ xano release export
|
|
19
|
+
`$ xano release export v1.0 --output ./backups/my-release.tar.gz`,
|
|
20
|
+
`$ xano release export v1.0 -o json`,
|
|
21
21
|
];
|
|
22
22
|
static flags = {
|
|
23
23
|
...BaseCommand.baseFlags,
|
|
@@ -29,7 +29,7 @@ Downloaded release #10 to ./release-10.tar.gz
|
|
|
29
29
|
required: false,
|
|
30
30
|
}),
|
|
31
31
|
output: Flags.string({
|
|
32
|
-
description: 'Output file path (defaults to ./release-{
|
|
32
|
+
description: 'Output file path (defaults to ./release-{name}.tar.gz)',
|
|
33
33
|
required: false,
|
|
34
34
|
}),
|
|
35
35
|
workspace: Flags.string({
|
|
@@ -57,7 +57,8 @@ Downloaded release #10 to ./release-10.tar.gz
|
|
|
57
57
|
if (!workspaceId) {
|
|
58
58
|
this.error('No workspace ID provided. Use --workspace flag or set one in your profile.');
|
|
59
59
|
}
|
|
60
|
-
const
|
|
60
|
+
const releaseName = args.release_name;
|
|
61
|
+
const releaseId = await this.resolveReleaseName(profile, workspaceId, releaseName, flags.verbose);
|
|
61
62
|
// Step 1: Get signed download URL
|
|
62
63
|
const exportUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/release/${releaseId}/export`;
|
|
63
64
|
try {
|
|
@@ -77,7 +78,8 @@ Downloaded release #10 to ./release-10.tar.gz
|
|
|
77
78
|
this.error('API did not return a download URL');
|
|
78
79
|
}
|
|
79
80
|
// Step 2: Download the file
|
|
80
|
-
const
|
|
81
|
+
const safeFilename = releaseName.replaceAll(/[^\w.-]/g, '_');
|
|
82
|
+
const outputPath = flags.output || `release-${safeFilename}.tar.gz`;
|
|
81
83
|
const resolvedPath = path.resolve(outputPath);
|
|
82
84
|
const downloadResponse = await fetch(exportLink.src);
|
|
83
85
|
if (!downloadResponse.ok) {
|
|
@@ -104,11 +106,11 @@ Downloaded release #10 to ./release-10.tar.gz
|
|
|
104
106
|
fileStream.on('error', reject);
|
|
105
107
|
});
|
|
106
108
|
if (flags.format === 'json') {
|
|
107
|
-
this.log(JSON.stringify({ bytes: totalBytes, file: resolvedPath,
|
|
109
|
+
this.log(JSON.stringify({ bytes: totalBytes, file: resolvedPath, release_name: releaseName }, null, 2));
|
|
108
110
|
}
|
|
109
111
|
else {
|
|
110
112
|
const sizeMb = (totalBytes / 1024 / 1024).toFixed(2);
|
|
111
|
-
this.log(`Downloaded release
|
|
113
|
+
this.log(`Downloaded release '${releaseName}' to ${resolvedPath} (${sizeMb} MB)`);
|
|
112
114
|
}
|
|
113
115
|
}
|
|
114
116
|
catch (error) {
|
|
@@ -139,4 +141,30 @@ Downloaded release #10 to ./release-10.tar.gz
|
|
|
139
141
|
this.error(`Failed to parse credentials file: ${error}`);
|
|
140
142
|
}
|
|
141
143
|
}
|
|
144
|
+
async resolveReleaseName(profile, workspaceId, releaseName, verbose) {
|
|
145
|
+
const listUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/release`;
|
|
146
|
+
const response = await this.verboseFetch(listUrl, {
|
|
147
|
+
headers: {
|
|
148
|
+
'accept': 'application/json',
|
|
149
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
150
|
+
},
|
|
151
|
+
method: 'GET',
|
|
152
|
+
}, verbose, profile.access_token);
|
|
153
|
+
if (!response.ok) {
|
|
154
|
+
const errorText = await response.text();
|
|
155
|
+
this.error(`Failed to list releases: ${response.status} ${response.statusText}\n${errorText}`);
|
|
156
|
+
}
|
|
157
|
+
const data = await response.json();
|
|
158
|
+
const releases = Array.isArray(data)
|
|
159
|
+
? data
|
|
160
|
+
: (data && typeof data === 'object' && 'items' in data && Array.isArray(data.items))
|
|
161
|
+
? data.items
|
|
162
|
+
: [];
|
|
163
|
+
const match = releases.find(r => r.name === releaseName);
|
|
164
|
+
if (!match) {
|
|
165
|
+
const available = releases.map(r => r.name).join(', ');
|
|
166
|
+
this.error(`Release '${releaseName}' not found.${available ? ` Available releases: ${available}` : ''}`);
|
|
167
|
+
}
|
|
168
|
+
return match.id;
|
|
169
|
+
}
|
|
142
170
|
}
|