@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
- this.log('');
54
- this.log('Fetching available instances...');
55
- const instances = await this.fetchInstances(token, flags.origin);
56
- if (instances.length === 0) {
57
- this.error('No instances found. Please check your account.');
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
+ }