@xano/cli 0.0.25 → 0.0.26
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/dist/base-command.d.ts +1 -3
- package/dist/base-command.js +5 -12
- package/dist/commands/function/create/index.d.ts +6 -7
- package/dist/commands/function/create/index.js +55 -55
- package/dist/commands/function/edit/index.d.ts +10 -11
- package/dist/commands/function/edit/index.js +162 -155
- package/dist/commands/function/get/index.d.ts +5 -6
- package/dist/commands/function/get/index.js +60 -55
- package/dist/commands/function/list/index.d.ts +5 -6
- package/dist/commands/function/list/index.js +52 -52
- package/dist/commands/profile/create/index.d.ts +6 -6
- package/dist/commands/profile/create/index.js +37 -37
- package/dist/commands/profile/delete/index.d.ts +2 -2
- package/dist/commands/profile/delete/index.js +9 -9
- package/dist/commands/profile/edit/index.d.ts +7 -8
- package/dist/commands/profile/edit/index.js +48 -48
- package/dist/commands/profile/get-default/index.js +1 -1
- package/dist/commands/profile/list/index.d.ts +2 -2
- package/dist/commands/profile/list/index.js +9 -9
- package/dist/commands/profile/me/index.d.ts +3 -4
- package/dist/commands/profile/me/index.js +21 -21
- package/dist/commands/profile/project/index.js +1 -1
- package/dist/commands/profile/set-default/index.js +1 -1
- package/dist/commands/profile/token/index.js +1 -1
- package/dist/commands/profile/wizard/index.d.ts +4 -5
- package/dist/commands/profile/wizard/index.js +108 -142
- package/dist/commands/run/env/delete/index.d.ts +2 -3
- package/dist/commands/run/env/delete/index.js +10 -10
- package/dist/commands/run/env/get/index.d.ts +2 -3
- package/dist/commands/run/env/get/index.js +11 -11
- package/dist/commands/run/env/list/index.d.ts +2 -3
- package/dist/commands/run/env/list/index.js +19 -17
- package/dist/commands/run/env/set/index.d.ts +2 -3
- package/dist/commands/run/env/set/index.js +5 -5
- package/dist/commands/run/exec/index.d.ts +8 -19
- package/dist/commands/run/exec/index.js +108 -186
- package/dist/commands/run/info/index.d.ts +4 -5
- package/dist/commands/run/info/index.js +27 -27
- package/dist/commands/run/projects/create/index.d.ts +3 -4
- package/dist/commands/run/projects/create/index.js +23 -23
- package/dist/commands/run/projects/delete/index.d.ts +2 -3
- package/dist/commands/run/projects/delete/index.js +10 -10
- package/dist/commands/run/projects/list/index.d.ts +2 -3
- package/dist/commands/run/projects/list/index.js +12 -12
- package/dist/commands/run/projects/update/index.d.ts +3 -4
- package/dist/commands/run/projects/update/index.js +21 -21
- package/dist/commands/run/secrets/delete/index.d.ts +2 -3
- package/dist/commands/run/secrets/delete/index.js +10 -10
- package/dist/commands/run/secrets/get/index.d.ts +2 -3
- package/dist/commands/run/secrets/get/index.js +11 -11
- package/dist/commands/run/secrets/list/index.d.ts +2 -3
- package/dist/commands/run/secrets/list/index.js +24 -22
- package/dist/commands/run/secrets/set/index.d.ts +3 -4
- package/dist/commands/run/secrets/set/index.js +16 -16
- package/dist/commands/run/sessions/delete/index.d.ts +2 -3
- package/dist/commands/run/sessions/delete/index.js +10 -10
- package/dist/commands/run/sessions/get/index.d.ts +2 -3
- package/dist/commands/run/sessions/get/index.js +11 -11
- package/dist/commands/run/sessions/list/index.d.ts +2 -3
- package/dist/commands/run/sessions/list/index.js +11 -11
- package/dist/commands/run/sessions/start/index.d.ts +2 -3
- package/dist/commands/run/sessions/start/index.js +11 -11
- package/dist/commands/run/sessions/stop/index.d.ts +2 -3
- package/dist/commands/run/sessions/stop/index.js +11 -11
- package/dist/commands/run/sink/get/index.d.ts +2 -3
- package/dist/commands/run/sink/get/index.js +11 -11
- package/dist/commands/static_host/build/create/index.d.ts +4 -5
- package/dist/commands/static_host/build/create/index.js +33 -33
- package/dist/commands/static_host/build/get/index.d.ts +4 -5
- package/dist/commands/static_host/build/get/index.js +20 -20
- package/dist/commands/static_host/build/list/index.d.ts +3 -4
- package/dist/commands/static_host/build/list/index.js +31 -31
- package/dist/commands/static_host/list/index.d.ts +3 -4
- package/dist/commands/static_host/list/index.js +31 -31
- package/dist/commands/workspace/list/index.d.ts +2 -3
- package/dist/commands/workspace/list/index.js +15 -15
- package/dist/commands/workspace/pull/index.d.ts +4 -5
- package/dist/commands/workspace/pull/index.js +52 -47
- package/dist/commands/workspace/push/index.d.ts +0 -1
- package/dist/commands/workspace/push/index.js +4 -4
- package/dist/help.d.ts +1 -1
- package/dist/lib/base-run-command.d.ts +6 -6
- package/dist/lib/base-run-command.js +6 -8
- package/dist/lib/run-http-client.d.ts +18 -24
- package/dist/lib/run-http-client.js +61 -96
- package/dist/lib/run-types.d.ts +80 -80
- package/oclif.manifest.json +849 -2027
- package/package.json +3 -1
- package/dist/commands/auth/index.d.ts +0 -21
- package/dist/commands/auth/index.js +0 -533
- package/dist/commands/branch/create/index.d.ts +0 -17
- package/dist/commands/branch/create/index.js +0 -164
- package/dist/commands/branch/delete/index.d.ts +0 -18
- package/dist/commands/branch/delete/index.js +0 -156
- package/dist/commands/branch/edit/index.d.ts +0 -19
- package/dist/commands/branch/edit/index.js +0 -166
- package/dist/commands/branch/get/index.d.ts +0 -16
- package/dist/commands/branch/get/index.js +0 -135
- package/dist/commands/branch/list/index.d.ts +0 -18
- package/dist/commands/branch/list/index.js +0 -138
- package/dist/commands/branch/set-live/index.d.ts +0 -18
- package/dist/commands/branch/set-live/index.js +0 -155
- package/dist/commands/workspace/create/index.d.ts +0 -14
- package/dist/commands/workspace/create/index.js +0 -131
- package/dist/commands/workspace/delete/index.d.ts +0 -20
- package/dist/commands/workspace/delete/index.js +0 -141
- package/dist/commands/workspace/edit/index.d.ts +0 -22
- package/dist/commands/workspace/edit/index.js +0 -176
- package/dist/commands/workspace/get/index.d.ts +0 -18
- package/dist/commands/workspace/get/index.js +0 -136
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": "0.0.
|
|
4
|
+
"version": "0.0.26",
|
|
5
5
|
"author": "Sean Montgomery",
|
|
6
6
|
"bin": {
|
|
7
7
|
"xano": "./bin/run.js"
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"@oclif/plugin-plugins": "^5",
|
|
14
14
|
"inquirer": "^8.2.5",
|
|
15
15
|
"js-yaml": "^4.1.0",
|
|
16
|
+
"lodash.snakecase": "^4.1.1",
|
|
16
17
|
"open": "^11.0.0"
|
|
17
18
|
},
|
|
18
19
|
"devDependencies": {
|
|
@@ -22,6 +23,7 @@
|
|
|
22
23
|
"@types/chai": "^4",
|
|
23
24
|
"@types/inquirer": "^8.2.5",
|
|
24
25
|
"@types/js-yaml": "^4.0.9",
|
|
26
|
+
"@types/lodash.snakecase": "^4.1.9",
|
|
25
27
|
"@types/mocha": "^10",
|
|
26
28
|
"@types/node": "^18",
|
|
27
29
|
"chai": "^4",
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { Command } from '@oclif/core';
|
|
2
|
-
export default class Auth extends Command {
|
|
3
|
-
static description: string;
|
|
4
|
-
static examples: string[];
|
|
5
|
-
static flags: {
|
|
6
|
-
origin: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
-
};
|
|
8
|
-
run(): Promise<void>;
|
|
9
|
-
private fetchBranches;
|
|
10
|
-
private fetchInstances;
|
|
11
|
-
private fetchProjects;
|
|
12
|
-
private fetchWorkspaces;
|
|
13
|
-
private promptProfileName;
|
|
14
|
-
private saveProfile;
|
|
15
|
-
private selectBranch;
|
|
16
|
-
private selectInstance;
|
|
17
|
-
private selectProject;
|
|
18
|
-
private selectWorkspace;
|
|
19
|
-
private startAuthServer;
|
|
20
|
-
private validateToken;
|
|
21
|
-
}
|
|
@@ -1,533 +0,0 @@
|
|
|
1
|
-
import { Command, Flags } from '@oclif/core';
|
|
2
|
-
import inquirer from 'inquirer';
|
|
3
|
-
import * as yaml from 'js-yaml';
|
|
4
|
-
import * as fs from 'node:fs';
|
|
5
|
-
import * as http from 'node:http';
|
|
6
|
-
import * as os from 'node:os';
|
|
7
|
-
import { join } from 'node:path';
|
|
8
|
-
import open from 'open';
|
|
9
|
-
const AUTH_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
|
|
10
|
-
export default class Auth extends Command {
|
|
11
|
-
static description = 'Authenticate with Xano via browser login';
|
|
12
|
-
static examples = [
|
|
13
|
-
`$ xano auth
|
|
14
|
-
Opening browser for Xano login...
|
|
15
|
-
Waiting for authentication...
|
|
16
|
-
Authenticated as John Doe (john@example.com)
|
|
17
|
-
? Select an instance: US-1 (Production)
|
|
18
|
-
? Profile name: default
|
|
19
|
-
Profile 'default' created successfully!`,
|
|
20
|
-
`$ xano auth --origin https://custom.xano.com
|
|
21
|
-
Opening browser for Xano login at https://custom.xano.com...`,
|
|
22
|
-
];
|
|
23
|
-
static flags = {
|
|
24
|
-
origin: Flags.string({
|
|
25
|
-
char: 'o',
|
|
26
|
-
default: 'https://app.xano.com',
|
|
27
|
-
description: 'Xano account origin URL',
|
|
28
|
-
}),
|
|
29
|
-
};
|
|
30
|
-
async run() {
|
|
31
|
-
const { flags } = await this.parse(Auth);
|
|
32
|
-
try {
|
|
33
|
-
// Step 1: Get token via browser auth
|
|
34
|
-
this.log('Starting authentication flow...');
|
|
35
|
-
const token = await this.startAuthServer(flags.origin);
|
|
36
|
-
// Step 2: Validate token and get user info
|
|
37
|
-
this.log('');
|
|
38
|
-
this.log('Validating authentication...');
|
|
39
|
-
const user = await this.validateToken(token, flags.origin);
|
|
40
|
-
this.log(`Authenticated as ${user.name} (${user.email})`);
|
|
41
|
-
// Step 3: Fetch and select instance
|
|
42
|
-
this.log('');
|
|
43
|
-
this.log('Fetching available instances...');
|
|
44
|
-
const instances = await this.fetchInstances(token, flags.origin);
|
|
45
|
-
if (instances.length === 0) {
|
|
46
|
-
this.error('No instances found. Please check your account.');
|
|
47
|
-
}
|
|
48
|
-
const instance = await this.selectInstance(instances);
|
|
49
|
-
// Step 4: Workspace selection
|
|
50
|
-
let workspace;
|
|
51
|
-
let branch;
|
|
52
|
-
let project;
|
|
53
|
-
this.log('');
|
|
54
|
-
this.log('Fetching available workspaces...');
|
|
55
|
-
const workspaces = await this.fetchWorkspaces(token, instance.origin);
|
|
56
|
-
if (workspaces.length > 0) {
|
|
57
|
-
workspace = await this.selectWorkspace(workspaces);
|
|
58
|
-
if (workspace) {
|
|
59
|
-
// Step 5: Branch selection
|
|
60
|
-
this.log('');
|
|
61
|
-
this.log('Fetching available branches...');
|
|
62
|
-
const branches = await this.fetchBranches(token, instance.origin, workspace.id);
|
|
63
|
-
if (branches.length > 0) {
|
|
64
|
-
branch = await this.selectBranch(branches);
|
|
65
|
-
}
|
|
66
|
-
// Step 6: Project selection
|
|
67
|
-
this.log('');
|
|
68
|
-
this.log('Fetching available projects...');
|
|
69
|
-
const projects = await this.fetchProjects(token, instance.origin, workspace.id, branch);
|
|
70
|
-
if (projects.length > 0) {
|
|
71
|
-
project = await this.selectProject(projects);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
// Step 7: Profile name
|
|
76
|
-
this.log('');
|
|
77
|
-
const profileName = await this.promptProfileName();
|
|
78
|
-
// Step 8: Save profile
|
|
79
|
-
await this.saveProfile({
|
|
80
|
-
access_token: token,
|
|
81
|
-
account_origin: flags.origin,
|
|
82
|
-
branch,
|
|
83
|
-
instance_origin: instance.origin,
|
|
84
|
-
name: profileName,
|
|
85
|
-
project,
|
|
86
|
-
workspace: workspace?.id,
|
|
87
|
-
});
|
|
88
|
-
this.log('');
|
|
89
|
-
this.log(`Profile '${profileName}' created successfully!`);
|
|
90
|
-
// Ensure clean exit (the open() call can keep the event loop alive)
|
|
91
|
-
process.exit(0);
|
|
92
|
-
}
|
|
93
|
-
catch (error) {
|
|
94
|
-
if (error instanceof Error && error.message.includes('User force closed the prompt')) {
|
|
95
|
-
this.log('Authentication cancelled.');
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
throw error;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
async fetchBranches(accessToken, origin, workspaceId) {
|
|
102
|
-
try {
|
|
103
|
-
const response = await fetch(`${origin}/api:meta/workspace/${workspaceId}/branch`, {
|
|
104
|
-
headers: {
|
|
105
|
-
accept: 'application/json',
|
|
106
|
-
Authorization: `Bearer ${accessToken}`,
|
|
107
|
-
},
|
|
108
|
-
method: 'GET',
|
|
109
|
-
});
|
|
110
|
-
if (!response.ok) {
|
|
111
|
-
throw new Error(`API request failed with status ${response.status}`);
|
|
112
|
-
}
|
|
113
|
-
const data = (await response.json());
|
|
114
|
-
if (Array.isArray(data)) {
|
|
115
|
-
return data.map((br) => ({
|
|
116
|
-
id: br.id || br.label,
|
|
117
|
-
label: br.label,
|
|
118
|
-
}));
|
|
119
|
-
}
|
|
120
|
-
return [];
|
|
121
|
-
}
|
|
122
|
-
catch {
|
|
123
|
-
return [];
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
async fetchInstances(accessToken, origin) {
|
|
127
|
-
const response = await fetch(`${origin}/api:meta/instance`, {
|
|
128
|
-
headers: {
|
|
129
|
-
accept: 'application/json',
|
|
130
|
-
Authorization: `Bearer ${accessToken}`,
|
|
131
|
-
},
|
|
132
|
-
method: 'GET',
|
|
133
|
-
});
|
|
134
|
-
if (!response.ok) {
|
|
135
|
-
if (response.status === 401) {
|
|
136
|
-
throw new Error('Unauthorized. Please check your access token.');
|
|
137
|
-
}
|
|
138
|
-
throw new Error(`API request failed with status ${response.status}`);
|
|
139
|
-
}
|
|
140
|
-
const data = (await response.json());
|
|
141
|
-
if (Array.isArray(data)) {
|
|
142
|
-
return data.map((inst) => ({
|
|
143
|
-
display: inst.display,
|
|
144
|
-
id: inst.id || inst.name,
|
|
145
|
-
name: inst.name,
|
|
146
|
-
origin: new URL(inst.meta_api).origin,
|
|
147
|
-
}));
|
|
148
|
-
}
|
|
149
|
-
return [];
|
|
150
|
-
}
|
|
151
|
-
async fetchProjects(accessToken, origin, workspaceId, branchId) {
|
|
152
|
-
try {
|
|
153
|
-
const branchParam = branchId ? `?branch=${branchId}` : '';
|
|
154
|
-
const response = await fetch(`${origin}/api:meta/workspace/${workspaceId}/project${branchParam}`, {
|
|
155
|
-
headers: {
|
|
156
|
-
accept: 'application/json',
|
|
157
|
-
Authorization: `Bearer ${accessToken}`,
|
|
158
|
-
},
|
|
159
|
-
method: 'GET',
|
|
160
|
-
});
|
|
161
|
-
if (!response.ok) {
|
|
162
|
-
throw new Error(`API request failed with status ${response.status}`);
|
|
163
|
-
}
|
|
164
|
-
const data = (await response.json());
|
|
165
|
-
if (Array.isArray(data)) {
|
|
166
|
-
return data.map((proj) => ({
|
|
167
|
-
id: proj.id || proj.name,
|
|
168
|
-
name: proj.name,
|
|
169
|
-
}));
|
|
170
|
-
}
|
|
171
|
-
return [];
|
|
172
|
-
}
|
|
173
|
-
catch {
|
|
174
|
-
return [];
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
async fetchWorkspaces(accessToken, origin) {
|
|
178
|
-
try {
|
|
179
|
-
const response = await fetch(`${origin}/api:meta/workspace`, {
|
|
180
|
-
headers: {
|
|
181
|
-
accept: 'application/json',
|
|
182
|
-
Authorization: `Bearer ${accessToken}`,
|
|
183
|
-
},
|
|
184
|
-
method: 'GET',
|
|
185
|
-
});
|
|
186
|
-
if (!response.ok) {
|
|
187
|
-
throw new Error(`API request failed with status ${response.status}`);
|
|
188
|
-
}
|
|
189
|
-
const data = (await response.json());
|
|
190
|
-
if (Array.isArray(data)) {
|
|
191
|
-
return data.map((ws) => ({
|
|
192
|
-
id: ws.id || ws.name,
|
|
193
|
-
name: ws.name,
|
|
194
|
-
}));
|
|
195
|
-
}
|
|
196
|
-
return [];
|
|
197
|
-
}
|
|
198
|
-
catch {
|
|
199
|
-
return [];
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
async promptProfileName() {
|
|
203
|
-
const { profileName } = await inquirer.prompt([
|
|
204
|
-
{
|
|
205
|
-
default: 'default',
|
|
206
|
-
message: 'Profile name',
|
|
207
|
-
name: 'profileName',
|
|
208
|
-
type: 'input',
|
|
209
|
-
validate(input) {
|
|
210
|
-
const trimmed = input.trim();
|
|
211
|
-
if (trimmed === '') {
|
|
212
|
-
return true; // Will use default
|
|
213
|
-
}
|
|
214
|
-
return true;
|
|
215
|
-
},
|
|
216
|
-
},
|
|
217
|
-
]);
|
|
218
|
-
return profileName.trim() || 'default';
|
|
219
|
-
}
|
|
220
|
-
async saveProfile(profile) {
|
|
221
|
-
const configDir = join(os.homedir(), '.xano');
|
|
222
|
-
const credentialsPath = join(configDir, 'credentials.yaml');
|
|
223
|
-
// Ensure the .xano directory exists
|
|
224
|
-
if (!fs.existsSync(configDir)) {
|
|
225
|
-
fs.mkdirSync(configDir, { recursive: true });
|
|
226
|
-
}
|
|
227
|
-
// Read existing credentials file or create new structure
|
|
228
|
-
let credentials = { profiles: {} };
|
|
229
|
-
if (fs.existsSync(credentialsPath)) {
|
|
230
|
-
try {
|
|
231
|
-
const fileContent = fs.readFileSync(credentialsPath, 'utf8');
|
|
232
|
-
const parsed = yaml.load(fileContent);
|
|
233
|
-
if (parsed && typeof parsed === 'object' && 'profiles' in parsed) {
|
|
234
|
-
credentials = parsed;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
catch {
|
|
238
|
-
// Continue with empty credentials if parse fails
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
// Add or update the profile
|
|
242
|
-
credentials.profiles[profile.name] = {
|
|
243
|
-
access_token: profile.access_token,
|
|
244
|
-
account_origin: profile.account_origin,
|
|
245
|
-
instance_origin: profile.instance_origin,
|
|
246
|
-
...(profile.workspace && { workspace: profile.workspace }),
|
|
247
|
-
...(profile.branch && { branch: profile.branch }),
|
|
248
|
-
...(profile.project && { project: profile.project }),
|
|
249
|
-
};
|
|
250
|
-
// Set as default profile
|
|
251
|
-
credentials.default = profile.name;
|
|
252
|
-
// Write the updated credentials back to the file
|
|
253
|
-
const yamlContent = yaml.dump(credentials, {
|
|
254
|
-
indent: 2,
|
|
255
|
-
lineWidth: -1,
|
|
256
|
-
noRefs: true,
|
|
257
|
-
});
|
|
258
|
-
fs.writeFileSync(credentialsPath, yamlContent, 'utf8');
|
|
259
|
-
}
|
|
260
|
-
async selectBranch(branches) {
|
|
261
|
-
const { selectedBranch } = await inquirer.prompt([
|
|
262
|
-
{
|
|
263
|
-
choices: [
|
|
264
|
-
{ name: '(Skip and use live branch)', value: '' },
|
|
265
|
-
...branches.map((br) => ({
|
|
266
|
-
name: br.label,
|
|
267
|
-
value: br.id,
|
|
268
|
-
})),
|
|
269
|
-
],
|
|
270
|
-
message: 'Select a branch',
|
|
271
|
-
name: 'selectedBranch',
|
|
272
|
-
type: 'list',
|
|
273
|
-
},
|
|
274
|
-
]);
|
|
275
|
-
return selectedBranch || undefined;
|
|
276
|
-
}
|
|
277
|
-
async selectInstance(instances) {
|
|
278
|
-
const { instanceId } = await inquirer.prompt([
|
|
279
|
-
{
|
|
280
|
-
choices: instances.map((inst) => ({
|
|
281
|
-
name: `${inst.name} (${inst.display})`,
|
|
282
|
-
value: inst.id,
|
|
283
|
-
})),
|
|
284
|
-
message: 'Select an instance',
|
|
285
|
-
name: 'instanceId',
|
|
286
|
-
type: 'list',
|
|
287
|
-
},
|
|
288
|
-
]);
|
|
289
|
-
return instances.find((inst) => inst.id === instanceId);
|
|
290
|
-
}
|
|
291
|
-
async selectProject(projects) {
|
|
292
|
-
const { selectedProject } = await inquirer.prompt([
|
|
293
|
-
{
|
|
294
|
-
choices: [
|
|
295
|
-
{ name: '(Skip project)', value: '' },
|
|
296
|
-
...projects.map((proj) => ({
|
|
297
|
-
name: proj.name,
|
|
298
|
-
value: proj.id,
|
|
299
|
-
})),
|
|
300
|
-
],
|
|
301
|
-
message: 'Select a project',
|
|
302
|
-
name: 'selectedProject',
|
|
303
|
-
type: 'list',
|
|
304
|
-
},
|
|
305
|
-
]);
|
|
306
|
-
return selectedProject || undefined;
|
|
307
|
-
}
|
|
308
|
-
async selectWorkspace(workspaces) {
|
|
309
|
-
const { selectedWorkspace } = await inquirer.prompt([
|
|
310
|
-
{
|
|
311
|
-
choices: [
|
|
312
|
-
{ name: '(Skip workspace)', value: '' },
|
|
313
|
-
...workspaces.map((ws) => ({
|
|
314
|
-
name: ws.name,
|
|
315
|
-
value: ws.id,
|
|
316
|
-
})),
|
|
317
|
-
],
|
|
318
|
-
message: 'Select a workspace',
|
|
319
|
-
name: 'selectedWorkspace',
|
|
320
|
-
type: 'list',
|
|
321
|
-
},
|
|
322
|
-
]);
|
|
323
|
-
if (!selectedWorkspace) {
|
|
324
|
-
return undefined;
|
|
325
|
-
}
|
|
326
|
-
return workspaces.find((ws) => ws.id === selectedWorkspace);
|
|
327
|
-
}
|
|
328
|
-
startAuthServer(origin) {
|
|
329
|
-
return new Promise((resolve, reject) => {
|
|
330
|
-
const server = http.createServer((req, res) => {
|
|
331
|
-
const url = new URL(req.url || '', `http://${req.headers.host}`);
|
|
332
|
-
if (url.pathname === '/callback') {
|
|
333
|
-
const token = url.searchParams.get('token');
|
|
334
|
-
if (token) {
|
|
335
|
-
// Send success response to browser
|
|
336
|
-
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
337
|
-
res.end(`
|
|
338
|
-
<!DOCTYPE html>
|
|
339
|
-
<html>
|
|
340
|
-
<head>
|
|
341
|
-
<title>Xano CLI - Authentication Successful</title>
|
|
342
|
-
<style>
|
|
343
|
-
body {
|
|
344
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
345
|
-
display: flex;
|
|
346
|
-
flex-direction: column;
|
|
347
|
-
justify-content: center;
|
|
348
|
-
align-items: center;
|
|
349
|
-
min-height: 100vh;
|
|
350
|
-
margin: 0;
|
|
351
|
-
background: #f4f5f7;
|
|
352
|
-
color: #1a1a2e;
|
|
353
|
-
}
|
|
354
|
-
.container {
|
|
355
|
-
text-align: center;
|
|
356
|
-
padding: 48px;
|
|
357
|
-
background: white;
|
|
358
|
-
border-radius: 12px;
|
|
359
|
-
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
360
|
-
max-width: 400px;
|
|
361
|
-
}
|
|
362
|
-
.checkmark {
|
|
363
|
-
width: 64px;
|
|
364
|
-
height: 64px;
|
|
365
|
-
background: #1b62f8;
|
|
366
|
-
border-radius: 50%;
|
|
367
|
-
display: flex;
|
|
368
|
-
justify-content: center;
|
|
369
|
-
align-items: center;
|
|
370
|
-
margin: 0 auto 24px;
|
|
371
|
-
}
|
|
372
|
-
.checkmark svg {
|
|
373
|
-
width: 32px;
|
|
374
|
-
height: 32px;
|
|
375
|
-
fill: white;
|
|
376
|
-
}
|
|
377
|
-
h1 {
|
|
378
|
-
font-size: 24px;
|
|
379
|
-
font-weight: 600;
|
|
380
|
-
margin: 0 0 12px;
|
|
381
|
-
color: #1a1a2e;
|
|
382
|
-
}
|
|
383
|
-
p {
|
|
384
|
-
font-size: 14px;
|
|
385
|
-
color: #6b7280;
|
|
386
|
-
margin: 0;
|
|
387
|
-
}
|
|
388
|
-
</style>
|
|
389
|
-
</head>
|
|
390
|
-
<body>
|
|
391
|
-
<div class="container">
|
|
392
|
-
<div class="checkmark">
|
|
393
|
-
<svg viewBox="0 0 24 24"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z"/></svg>
|
|
394
|
-
</div>
|
|
395
|
-
<h1>Authentication Successful</h1>
|
|
396
|
-
<p>You can now close this window and return to your terminal.</p>
|
|
397
|
-
</div>
|
|
398
|
-
</body>
|
|
399
|
-
</html>
|
|
400
|
-
`);
|
|
401
|
-
// Close server and resolve with token
|
|
402
|
-
clearTimeout(timeout);
|
|
403
|
-
server.close();
|
|
404
|
-
resolve(decodeURIComponent(token));
|
|
405
|
-
}
|
|
406
|
-
else {
|
|
407
|
-
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
408
|
-
res.end(`
|
|
409
|
-
<!DOCTYPE html>
|
|
410
|
-
<html>
|
|
411
|
-
<head>
|
|
412
|
-
<title>Xano CLI - Authentication Failed</title>
|
|
413
|
-
<style>
|
|
414
|
-
body {
|
|
415
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
416
|
-
display: flex;
|
|
417
|
-
flex-direction: column;
|
|
418
|
-
justify-content: center;
|
|
419
|
-
align-items: center;
|
|
420
|
-
min-height: 100vh;
|
|
421
|
-
margin: 0;
|
|
422
|
-
background: #f4f5f7;
|
|
423
|
-
color: #1a1a2e;
|
|
424
|
-
}
|
|
425
|
-
.container {
|
|
426
|
-
text-align: center;
|
|
427
|
-
padding: 48px;
|
|
428
|
-
background: white;
|
|
429
|
-
border-radius: 12px;
|
|
430
|
-
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
431
|
-
max-width: 400px;
|
|
432
|
-
}
|
|
433
|
-
.error-icon {
|
|
434
|
-
width: 64px;
|
|
435
|
-
height: 64px;
|
|
436
|
-
background: #ef4444;
|
|
437
|
-
border-radius: 50%;
|
|
438
|
-
display: flex;
|
|
439
|
-
justify-content: center;
|
|
440
|
-
align-items: center;
|
|
441
|
-
margin: 0 auto 24px;
|
|
442
|
-
}
|
|
443
|
-
.error-icon svg {
|
|
444
|
-
width: 32px;
|
|
445
|
-
height: 32px;
|
|
446
|
-
fill: white;
|
|
447
|
-
}
|
|
448
|
-
h1 {
|
|
449
|
-
font-size: 24px;
|
|
450
|
-
font-weight: 600;
|
|
451
|
-
margin: 0 0 12px;
|
|
452
|
-
color: #1a1a2e;
|
|
453
|
-
}
|
|
454
|
-
p {
|
|
455
|
-
font-size: 14px;
|
|
456
|
-
color: #6b7280;
|
|
457
|
-
margin: 0;
|
|
458
|
-
}
|
|
459
|
-
</style>
|
|
460
|
-
</head>
|
|
461
|
-
<body>
|
|
462
|
-
<div class="container">
|
|
463
|
-
<div class="error-icon">
|
|
464
|
-
<svg viewBox="0 0 24 24"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/></svg>
|
|
465
|
-
</div>
|
|
466
|
-
<h1>Authentication Failed</h1>
|
|
467
|
-
<p>No token received. Please close this window and try again.</p>
|
|
468
|
-
</div>
|
|
469
|
-
</body>
|
|
470
|
-
</html>
|
|
471
|
-
`);
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
else {
|
|
475
|
-
res.writeHead(404);
|
|
476
|
-
res.end('Not found');
|
|
477
|
-
}
|
|
478
|
-
});
|
|
479
|
-
// Set timeout
|
|
480
|
-
const timeout = setTimeout(() => {
|
|
481
|
-
server.close();
|
|
482
|
-
reject(new Error('Authentication timed out. Please try again.'));
|
|
483
|
-
}, AUTH_TIMEOUT_MS);
|
|
484
|
-
// Handle server errors
|
|
485
|
-
server.on('error', (err) => {
|
|
486
|
-
clearTimeout(timeout);
|
|
487
|
-
reject(new Error(`Failed to start auth server: ${err.message}`));
|
|
488
|
-
});
|
|
489
|
-
// Start server on random available port
|
|
490
|
-
server.listen(0, '127.0.0.1', async () => {
|
|
491
|
-
const address = server.address();
|
|
492
|
-
if (!address || typeof address === 'string') {
|
|
493
|
-
clearTimeout(timeout);
|
|
494
|
-
server.close();
|
|
495
|
-
reject(new Error('Failed to get server address'));
|
|
496
|
-
return;
|
|
497
|
-
}
|
|
498
|
-
const { port } = address;
|
|
499
|
-
const callbackUrl = encodeURIComponent(`http://127.0.0.1:${port}/callback`);
|
|
500
|
-
const authUrl = `${origin}/login?dest=cli&callback=${callbackUrl}`;
|
|
501
|
-
this.log(`Opening browser for Xano login...`);
|
|
502
|
-
this.log('');
|
|
503
|
-
try {
|
|
504
|
-
await open(authUrl);
|
|
505
|
-
this.log('Waiting for authentication...');
|
|
506
|
-
this.log('(If the browser did not open, visit this URL manually:)');
|
|
507
|
-
this.log(authUrl);
|
|
508
|
-
}
|
|
509
|
-
catch {
|
|
510
|
-
this.log('Could not open browser automatically.');
|
|
511
|
-
this.log('Please visit this URL to authenticate:');
|
|
512
|
-
this.log(authUrl);
|
|
513
|
-
}
|
|
514
|
-
});
|
|
515
|
-
});
|
|
516
|
-
}
|
|
517
|
-
async validateToken(token, origin) {
|
|
518
|
-
const response = await fetch(`${origin}/api:meta/auth/me`, {
|
|
519
|
-
headers: {
|
|
520
|
-
accept: 'application/json',
|
|
521
|
-
Authorization: `Bearer ${token}`,
|
|
522
|
-
},
|
|
523
|
-
method: 'GET',
|
|
524
|
-
});
|
|
525
|
-
if (!response.ok) {
|
|
526
|
-
if (response.status === 401) {
|
|
527
|
-
throw new Error('Invalid or expired token. Please try again.');
|
|
528
|
-
}
|
|
529
|
-
throw new Error(`Token validation failed with status ${response.status}`);
|
|
530
|
-
}
|
|
531
|
-
return (await response.json());
|
|
532
|
-
}
|
|
533
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import BaseCommand from '../../../base-command.js';
|
|
2
|
-
export default class BranchCreate extends BaseCommand {
|
|
3
|
-
static description: string;
|
|
4
|
-
static examples: string[];
|
|
5
|
-
static flags: {
|
|
6
|
-
color: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
-
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
|
-
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
-
source: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
-
workspace: import("@oclif/core/interfaces").OptionFlag<number | 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
|
-
private loadCredentials;
|
|
17
|
-
}
|