@xano/cli 0.0.66 → 0.0.67
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
CHANGED
|
@@ -74,6 +74,10 @@ xano profile token
|
|
|
74
74
|
# Print workspace ID (useful for piping)
|
|
75
75
|
xano profile workspace
|
|
76
76
|
|
|
77
|
+
# Interactively change the workspace on a profile
|
|
78
|
+
xano profile workspace set
|
|
79
|
+
xano profile workspace set -p production
|
|
80
|
+
|
|
77
81
|
# Delete a profile
|
|
78
82
|
xano profile delete myprofile
|
|
79
83
|
```
|
|
@@ -44,19 +44,34 @@ Opening browser for Xano login at https://custom.xano.com...`,
|
|
|
44
44
|
// Step 1: Get token via browser auth
|
|
45
45
|
this.log('Starting authentication flow...');
|
|
46
46
|
const token = await this.startAuthServer(flags.origin);
|
|
47
|
+
this.log(`Received token: ${token}`);
|
|
47
48
|
// Step 2: Validate token and get user info
|
|
48
49
|
this.log('');
|
|
49
50
|
this.log('Validating authentication...');
|
|
50
51
|
const user = await this.validateToken(token, flags.origin);
|
|
51
52
|
this.log(`Authenticated as ${user.name} (${user.email})`);
|
|
52
53
|
// Step 3: Fetch and select instance
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
// Self-hosted instances (non-default origin) don't have /api:meta/instance
|
|
55
|
+
// The origin itself IS the instance
|
|
56
|
+
const isSelfHosted = !/^https:\/\/app\.(.*\.)?xano\.com$/.test(flags.origin);
|
|
57
|
+
let instance;
|
|
58
|
+
if (isSelfHosted) {
|
|
59
|
+
instance = {
|
|
60
|
+
display: flags.origin,
|
|
61
|
+
id: 'self-hosted',
|
|
62
|
+
name: 'self-hosted',
|
|
63
|
+
origin: flags.origin,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
this.log('');
|
|
68
|
+
this.log('Fetching available instances...');
|
|
69
|
+
const instances = await this.fetchInstances(token, flags.origin);
|
|
70
|
+
if (instances.length === 0) {
|
|
71
|
+
this.error('No instances found. Please check your account.');
|
|
72
|
+
}
|
|
73
|
+
instance = await this.selectInstance(instances);
|
|
58
74
|
}
|
|
59
|
-
const instance = await this.selectInstance(instances);
|
|
60
75
|
// Step 4: Workspace selection
|
|
61
76
|
let workspace;
|
|
62
77
|
let branch;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import BaseCommand from '../../../../base-command.js';
|
|
2
|
+
export default class ProfileWorkspaceSet extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
};
|
|
9
|
+
run(): Promise<void>;
|
|
10
|
+
private fetchWorkspaces;
|
|
11
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import * as yaml from 'js-yaml';
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import * as os from 'node:os';
|
|
5
|
+
import * as path from 'node:path';
|
|
6
|
+
import BaseCommand, { buildUserAgent } from '../../../../base-command.js';
|
|
7
|
+
export default class ProfileWorkspaceSet extends BaseCommand {
|
|
8
|
+
static description = 'Interactively select a workspace for a profile';
|
|
9
|
+
static examples = [
|
|
10
|
+
`$ xano profile workspace set
|
|
11
|
+
Fetching workspaces...
|
|
12
|
+
? Select a workspace: My Workspace
|
|
13
|
+
Workspace updated to 'My Workspace' (abc123) on profile 'default'
|
|
14
|
+
`,
|
|
15
|
+
`$ xano profile workspace set -p production
|
|
16
|
+
Fetching workspaces...
|
|
17
|
+
? Select a workspace: Production API
|
|
18
|
+
Workspace updated to 'Production API' (xyz789) on profile 'production'
|
|
19
|
+
`,
|
|
20
|
+
];
|
|
21
|
+
static flags = {
|
|
22
|
+
...BaseCommand.baseFlags,
|
|
23
|
+
};
|
|
24
|
+
async run() {
|
|
25
|
+
const { flags } = await this.parse(ProfileWorkspaceSet);
|
|
26
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
27
|
+
const credentials = this.loadCredentialsFile();
|
|
28
|
+
if (!credentials) {
|
|
29
|
+
this.error("Credentials file not found. Create a profile first using 'xano auth'.");
|
|
30
|
+
}
|
|
31
|
+
if (!(profileName in credentials.profiles)) {
|
|
32
|
+
this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}`);
|
|
33
|
+
}
|
|
34
|
+
const profile = credentials.profiles[profileName];
|
|
35
|
+
this.log('Fetching workspaces...');
|
|
36
|
+
const workspaces = await this.fetchWorkspaces(profile.access_token, profile.instance_origin);
|
|
37
|
+
if (workspaces.length === 0) {
|
|
38
|
+
this.error('No workspaces found on this instance.');
|
|
39
|
+
}
|
|
40
|
+
const { selectedWorkspace } = await inquirer.prompt([
|
|
41
|
+
{
|
|
42
|
+
choices: workspaces.map((ws) => ({
|
|
43
|
+
name: String(ws.id) === String(profile.workspace) ? `${ws.name} (current)` : ws.name,
|
|
44
|
+
value: ws.id,
|
|
45
|
+
})),
|
|
46
|
+
message: 'Select a workspace',
|
|
47
|
+
name: 'selectedWorkspace',
|
|
48
|
+
type: 'select',
|
|
49
|
+
},
|
|
50
|
+
]);
|
|
51
|
+
profile.workspace = selectedWorkspace;
|
|
52
|
+
credentials.profiles[profileName] = profile;
|
|
53
|
+
const credentialsPath = path.join(os.homedir(), '.xano', 'credentials.yaml');
|
|
54
|
+
const yamlContent = yaml.dump(credentials, {
|
|
55
|
+
indent: 2,
|
|
56
|
+
lineWidth: -1,
|
|
57
|
+
noRefs: true,
|
|
58
|
+
});
|
|
59
|
+
fs.writeFileSync(credentialsPath, yamlContent, 'utf8');
|
|
60
|
+
const selected = workspaces.find((ws) => ws.id === selectedWorkspace);
|
|
61
|
+
this.log(`Workspace updated to '${selected?.name}' (${selectedWorkspace}) on profile '${profileName}'`);
|
|
62
|
+
}
|
|
63
|
+
async fetchWorkspaces(accessToken, origin) {
|
|
64
|
+
const response = await fetch(`${origin}/api:meta/workspace`, {
|
|
65
|
+
headers: {
|
|
66
|
+
'User-Agent': buildUserAgent(this.config.version),
|
|
67
|
+
accept: 'application/json',
|
|
68
|
+
Authorization: `Bearer ${accessToken}`,
|
|
69
|
+
},
|
|
70
|
+
method: 'GET',
|
|
71
|
+
});
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
if (response.status === 401) {
|
|
74
|
+
this.error('Unauthorized. Your access token may be expired. Re-authenticate with "xano auth".');
|
|
75
|
+
}
|
|
76
|
+
this.error(`Failed to fetch workspaces (status ${response.status})`);
|
|
77
|
+
}
|
|
78
|
+
const data = (await response.json());
|
|
79
|
+
if (Array.isArray(data)) {
|
|
80
|
+
return data.map((ws) => ({
|
|
81
|
+
id: ws.id || ws.name,
|
|
82
|
+
name: ws.name,
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
}
|