@xano/cli 0.0.14 → 0.0.16

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.
Files changed (66) hide show
  1. package/README.md +115 -14
  2. package/dist/commands/profile/create/index.d.ts +2 -0
  3. package/dist/commands/profile/create/index.js +15 -0
  4. package/dist/commands/profile/edit/index.d.ts +6 -0
  5. package/dist/commands/profile/edit/index.js +50 -1
  6. package/dist/commands/profile/list/index.js +5 -0
  7. package/dist/commands/profile/project/index.d.ts +6 -0
  8. package/dist/commands/profile/project/index.js +54 -0
  9. package/dist/commands/profile/token/index.d.ts +6 -0
  10. package/dist/commands/profile/token/index.js +54 -0
  11. package/dist/commands/profile/wizard/index.d.ts +2 -0
  12. package/dist/commands/profile/wizard/index.js +108 -0
  13. package/dist/commands/run/env/delete/index.d.ts +13 -0
  14. package/dist/commands/run/env/delete/index.js +65 -0
  15. package/dist/commands/run/env/get/index.d.ts +13 -0
  16. package/dist/commands/run/env/get/index.js +52 -0
  17. package/dist/commands/run/env/list/index.d.ts +11 -0
  18. package/dist/commands/run/env/list/index.js +58 -0
  19. package/dist/commands/run/env/set/index.d.ts +13 -0
  20. package/dist/commands/run/env/set/index.js +51 -0
  21. package/dist/commands/{ephemeral/run/job → run/exec}/index.d.ts +4 -3
  22. package/dist/commands/run/exec/index.js +353 -0
  23. package/dist/commands/{ephemeral/run/service → run/info}/index.d.ts +3 -5
  24. package/dist/commands/run/info/index.js +160 -0
  25. package/dist/commands/run/projects/create/index.d.ts +13 -0
  26. package/dist/commands/run/projects/create/index.js +75 -0
  27. package/dist/commands/run/projects/delete/index.d.ts +13 -0
  28. package/dist/commands/run/projects/delete/index.js +65 -0
  29. package/dist/commands/run/projects/list/index.d.ts +12 -0
  30. package/dist/commands/run/projects/list/index.js +66 -0
  31. package/dist/commands/run/projects/update/index.d.ts +15 -0
  32. package/dist/commands/run/projects/update/index.js +86 -0
  33. package/dist/commands/run/secrets/delete/index.d.ts +13 -0
  34. package/dist/commands/run/secrets/delete/index.js +65 -0
  35. package/dist/commands/run/secrets/get/index.d.ts +13 -0
  36. package/dist/commands/run/secrets/get/index.js +52 -0
  37. package/dist/commands/run/secrets/list/index.d.ts +11 -0
  38. package/dist/commands/run/secrets/list/index.js +62 -0
  39. package/dist/commands/run/secrets/set/index.d.ts +15 -0
  40. package/dist/commands/run/secrets/set/index.js +74 -0
  41. package/dist/commands/run/sessions/delete/index.d.ts +13 -0
  42. package/dist/commands/run/sessions/delete/index.js +65 -0
  43. package/dist/commands/run/sessions/get/index.d.ts +13 -0
  44. package/dist/commands/run/sessions/get/index.js +72 -0
  45. package/dist/commands/run/sessions/list/index.d.ts +12 -0
  46. package/dist/commands/run/sessions/list/index.js +64 -0
  47. package/dist/commands/run/sessions/start/index.d.ts +13 -0
  48. package/dist/commands/run/sessions/start/index.js +56 -0
  49. package/dist/commands/run/sessions/stop/index.d.ts +13 -0
  50. package/dist/commands/run/sessions/stop/index.js +56 -0
  51. package/dist/commands/run/sink/get/index.d.ts +13 -0
  52. package/dist/commands/run/sink/get/index.js +63 -0
  53. package/dist/commands/workspace/pull/index.d.ts +28 -0
  54. package/dist/commands/workspace/pull/index.js +238 -0
  55. package/dist/commands/workspace/push/index.d.ts +19 -0
  56. package/dist/commands/workspace/push/index.js +163 -0
  57. package/dist/lib/base-run-command.d.ts +42 -0
  58. package/dist/lib/base-run-command.js +75 -0
  59. package/dist/lib/run-http-client.d.ts +58 -0
  60. package/dist/lib/run-http-client.js +136 -0
  61. package/dist/lib/run-types.d.ts +226 -0
  62. package/dist/lib/run-types.js +5 -0
  63. package/oclif.manifest.json +1470 -218
  64. package/package.json +1 -1
  65. package/dist/commands/ephemeral/run/job/index.js +0 -311
  66. package/dist/commands/ephemeral/run/service/index.js +0 -287
package/README.md CHANGED
@@ -23,24 +23,27 @@ npm install -g @xano/cli
23
23
  xano workspace:list
24
24
  ```
25
25
 
26
- 3. Run an ephemeral job:
26
+ 3. Execute XanoScript code:
27
27
  ```bash
28
- xano ephemeral:run:job -f script.xs
28
+ xano run exec -f script.xs
29
29
  ```
30
30
 
31
31
  ## Commands
32
32
 
33
33
  ### Profile Management
34
34
 
35
- Profiles store your Xano credentials and default workspace settings.
35
+ Profiles store your Xano credentials and default workspace/project settings.
36
36
 
37
37
  ```bash
38
- # Create a profile interactively
38
+ # Create a profile interactively (auto-fetches run projects)
39
39
  xano profile:wizard
40
40
 
41
41
  # Create a profile manually
42
42
  xano profile:create myprofile -i https://instance.xano.com -t <access_token>
43
43
 
44
+ # Create a profile with workspace and project
45
+ xano profile:create myprofile -i https://instance.xano.com -t <access_token> -w my-workspace -j my-project
46
+
44
47
  # List profiles
45
48
  xano profile:list
46
49
  xano profile:list --details
@@ -49,12 +52,17 @@ xano profile:list --details
49
52
  xano profile:set-default myprofile
50
53
 
51
54
  # Edit a profile
52
- xano profile:edit myprofile -w 123 # Set default workspace
55
+ xano profile:edit myprofile -w 123 # Set default workspace
56
+ xano profile:edit myprofile -j my-project # Set default project
57
+ xano profile:edit myprofile --run-project <id> # Set run project for xano run commands
58
+ xano profile:edit myprofile --remove-run-project # Remove run project
53
59
 
54
60
  # Delete a profile
55
61
  xano profile:delete myprofile
56
62
  ```
57
63
 
64
+ The `profile:wizard` command automatically fetches your run projects and sets the first one as the default for `xano run` commands.
65
+
58
66
  ### Workspaces
59
67
 
60
68
  ```bash
@@ -84,18 +92,99 @@ xano function:edit 145 -f new.xs # Update from file
84
92
  xano function:edit 145 --publish # Publish after editing
85
93
  ```
86
94
 
87
- ### Ephemeral Jobs & Services
95
+ ### Xano Run
96
+
97
+ Execute XanoScript code and manage projects, sessions, environment variables, and secrets.
98
+
99
+ #### Executing Code
100
+
101
+ ```bash
102
+ # Execute XanoScript (job or service)
103
+ xano run exec -f script.xs
104
+ xano run exec -f https://example.com/script.xs # From URL
105
+ xano run exec -f script.xs -a args.json # With input arguments (file)
106
+ xano run exec -f script.xs -a https://ex.com/args.json # With input arguments (URL)
107
+ xano run exec -f script.xs --edit # Edit in $EDITOR first
108
+ xano run exec -f script.xs --env API_KEY=secret # With env overrides
109
+ cat script.xs | xano run exec --stdin # From stdin
110
+
111
+ # Get document info (type, inputs, env vars)
112
+ xano run info -f script.xs
113
+ ```
114
+
115
+ #### Projects
116
+
117
+ ```bash
118
+ # List projects
119
+ xano run projects list
120
+
121
+ # Create a project
122
+ xano run projects create -n "My Project"
123
+ xano run projects create -n "My Project" -d "Description"
124
+
125
+ # Update a project
126
+ xano run projects update <project-id> -n "New Name"
127
+ xano run projects update <project-id> -d "New description"
128
+
129
+ # Delete a project
130
+ xano run projects delete <project-id>
131
+ xano run projects delete <project-id> --force # Skip confirmation
132
+ ```
133
+
134
+ #### Sessions
135
+
136
+ ```bash
137
+ # List sessions
138
+ xano run sessions list
139
+
140
+ # Get session details
141
+ xano run sessions get <session-id>
142
+
143
+ # Start/stop a session
144
+ xano run sessions start <session-id>
145
+ xano run sessions stop <session-id>
146
+
147
+ # Delete a session
148
+ xano run sessions delete <session-id>
149
+ xano run sessions delete <session-id> --force # Skip confirmation
150
+
151
+ # Get sink data for a completed session
152
+ xano run sink get <session-id>
153
+ ```
154
+
155
+ #### Environment Variables
156
+
157
+ ```bash
158
+ # List environment variable keys
159
+ xano run env list
160
+
161
+ # Set an environment variable
162
+ xano run env set API_KEY my-secret-key
163
+
164
+ # Get an environment variable value
165
+ xano run env get API_KEY
166
+
167
+ # Delete an environment variable
168
+ xano run env delete API_KEY
169
+ xano run env delete API_KEY --force # Skip confirmation
170
+ ```
88
171
 
89
- Run XanoScript code without creating permanent resources.
172
+ #### Secrets
90
173
 
91
174
  ```bash
92
- # Run a job (executes and returns result)
93
- xano ephemeral:run:job -f script.xs
94
- xano ephemeral:run:job -f script.xs -a args.json # With input arguments
95
- xano ephemeral:run:job -f script.xs --edit # Edit in $EDITOR first
175
+ # List secrets
176
+ xano run secrets list
177
+
178
+ # Set a secret
179
+ xano run secrets set docker-registry -t dockerconfigjson -v '{"auths":{...}}' -r ghcr.io
180
+ xano run secrets set service-key -t service-account-token -v 'token-value'
96
181
 
97
- # Run a service (starts API endpoints)
98
- xano ephemeral:run:service -f service.xs
182
+ # Get a secret value
183
+ xano run secrets get docker-registry
184
+
185
+ # Delete a secret
186
+ xano run secrets delete docker-registry
187
+ xano run secrets delete docker-registry --force # Skip confirmation
99
188
  ```
100
189
 
101
190
  ### Static Hosts
@@ -126,7 +215,19 @@ All commands support these options:
126
215
 
127
216
  ## Configuration
128
217
 
129
- Profiles are stored in `~/.xano/credentials.yaml`.
218
+ Profiles are stored in `~/.xano/credentials.yaml`:
219
+
220
+ ```yaml
221
+ profiles:
222
+ default:
223
+ account_origin: https://app.xano.com
224
+ instance_origin: https://instance.xano.com
225
+ access_token: <token>
226
+ workspace: <workspace_id>
227
+ branch: <branch_id>
228
+ run_project: <run_project_id> # Used by xano run commands
229
+ default: default
230
+ ```
130
231
 
131
232
  ## Help
132
233
 
@@ -9,6 +9,8 @@ export default class ProfileCreate extends Command {
9
9
  access_token: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
10
  workspace: 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
+ project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ run_base_url: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
14
  default: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
15
  };
14
16
  static description: string;
@@ -36,6 +36,16 @@ export default class ProfileCreate extends Command {
36
36
  description: 'Branch name',
37
37
  required: false,
38
38
  }),
39
+ project: Flags.string({
40
+ char: 'j',
41
+ description: 'Project name',
42
+ required: false,
43
+ }),
44
+ run_base_url: Flags.string({
45
+ char: 'r',
46
+ description: 'Xano Run API base URL (default: https://app.xano.com/)',
47
+ required: false,
48
+ }),
39
49
  default: Flags.boolean({
40
50
  description: 'Set this profile as the default',
41
51
  required: false,
@@ -52,6 +62,9 @@ Profile 'staging' created successfully at ~/.xano/credentials.yaml
52
62
  `,
53
63
  `$ xano profile:create dev -i https://dev-instance.xano.com -t token789 -w my-workspace -b feature-branch
54
64
  Profile 'dev' created successfully at ~/.xano/credentials.yaml
65
+ `,
66
+ `$ xano profile:create dev -i https://dev-instance.xano.com -t token789 -w my-workspace -b feature-branch -j my-project
67
+ Profile 'dev' created successfully at ~/.xano/credentials.yaml
55
68
  `,
56
69
  `$ xano profile:create production --account_origin https://account.xano.com --instance_origin https://instance.xano.com --access_token token123 --default
57
70
  Profile 'production' created successfully at ~/.xano/credentials.yaml
@@ -93,6 +106,8 @@ Default profile set to 'production'
93
106
  access_token: flags.access_token,
94
107
  ...(flags.workspace && { workspace: flags.workspace }),
95
108
  ...(flags.branch && { branch: flags.branch }),
109
+ ...(flags.project && { project: flags.project }),
110
+ ...(flags.run_base_url && { run_base_url: flags.run_base_url }),
96
111
  };
97
112
  // Set default if flag is provided
98
113
  if (flags.default) {
@@ -9,8 +9,14 @@ export default class ProfileEdit extends BaseCommand {
9
9
  access_token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
10
  workspace: 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
+ project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ 'run-project': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
14
  'remove-workspace': import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
15
  'remove-branch': import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
+ 'remove-project': import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
+ 'remove-run-project': import("@oclif/core/interfaces").BooleanFlag<boolean>;
18
+ run_base_url: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
19
+ 'remove-run-base-url': import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
20
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
21
  };
16
22
  static description: string;
@@ -38,6 +38,15 @@ export default class ProfileEdit extends BaseCommand {
38
38
  description: 'Update branch name',
39
39
  required: false,
40
40
  }),
41
+ project: Flags.string({
42
+ char: 'j',
43
+ description: 'Update project name',
44
+ required: false,
45
+ }),
46
+ 'run-project': Flags.string({
47
+ description: 'Update run project ID (for xano run commands)',
48
+ required: false,
49
+ }),
41
50
  'remove-workspace': Flags.boolean({
42
51
  description: 'Remove workspace from profile',
43
52
  required: false,
@@ -48,6 +57,26 @@ export default class ProfileEdit extends BaseCommand {
48
57
  required: false,
49
58
  default: false,
50
59
  }),
60
+ 'remove-project': Flags.boolean({
61
+ description: 'Remove project from profile',
62
+ required: false,
63
+ default: false,
64
+ }),
65
+ 'remove-run-project': Flags.boolean({
66
+ description: 'Remove run project from profile',
67
+ required: false,
68
+ default: false,
69
+ }),
70
+ run_base_url: Flags.string({
71
+ char: 'r',
72
+ description: 'Update Xano Run API base URL',
73
+ required: false,
74
+ }),
75
+ 'remove-run-base-url': Flags.boolean({
76
+ description: 'Remove run_base_url from profile (use default)',
77
+ required: false,
78
+ default: false,
79
+ }),
51
80
  };
52
81
  static description = 'Edit an existing profile configuration';
53
82
  static examples = [
@@ -65,6 +94,12 @@ Profile 'default' updated successfully at ~/.xano/credentials.yaml
65
94
  `,
66
95
  `$ xano profile:edit --remove-branch
67
96
  Profile 'default' updated successfully at ~/.xano/credentials.yaml
97
+ `,
98
+ `$ xano profile:edit -j my-project
99
+ Profile 'default' updated successfully at ~/.xano/credentials.yaml
100
+ `,
101
+ `$ xano profile:edit --remove-project
102
+ Profile 'default' updated successfully at ~/.xano/credentials.yaml
68
103
  `,
69
104
  ];
70
105
  async run() {
@@ -98,7 +133,9 @@ Profile 'default' updated successfully at ~/.xano/credentials.yaml
98
133
  const existingProfile = credentials.profiles[profileName];
99
134
  // Check if any flags were provided
100
135
  const hasFlags = flags.account_origin || flags.instance_origin || flags.access_token ||
101
- flags.workspace || flags.branch || flags['remove-workspace'] || flags['remove-branch'];
136
+ flags.workspace || flags.branch || flags.project || flags['run-project'] || flags.run_base_url ||
137
+ flags['remove-workspace'] || flags['remove-branch'] || flags['remove-project'] ||
138
+ flags['remove-run-project'] || flags['remove-run-base-url'];
102
139
  if (!hasFlags) {
103
140
  this.error('No fields specified to update. Use at least one flag to edit the profile.');
104
141
  }
@@ -110,6 +147,9 @@ Profile 'default' updated successfully at ~/.xano/credentials.yaml
110
147
  ...(flags.access_token !== undefined && { access_token: flags.access_token }),
111
148
  ...(flags.workspace !== undefined && { workspace: flags.workspace }),
112
149
  ...(flags.branch !== undefined && { branch: flags.branch }),
150
+ ...(flags.project !== undefined && { project: flags.project }),
151
+ ...(flags['run-project'] !== undefined && { run_project: flags['run-project'] }),
152
+ ...(flags.run_base_url !== undefined && { run_base_url: flags.run_base_url }),
113
153
  };
114
154
  // Handle removal flags
115
155
  if (flags['remove-workspace']) {
@@ -118,6 +158,15 @@ Profile 'default' updated successfully at ~/.xano/credentials.yaml
118
158
  if (flags['remove-branch']) {
119
159
  delete updatedProfile.branch;
120
160
  }
161
+ if (flags['remove-project']) {
162
+ delete updatedProfile.project;
163
+ }
164
+ if (flags['remove-run-project']) {
165
+ delete updatedProfile.run_project;
166
+ }
167
+ if (flags['remove-run-base-url']) {
168
+ delete updatedProfile.run_base_url;
169
+ }
121
170
  credentials.profiles[profileName] = updatedProfile;
122
171
  // Write the updated credentials back to the file
123
172
  try {
@@ -30,6 +30,7 @@ Profile: default
30
30
  Access Token: ***...***
31
31
  Workspace: my-workspace
32
32
  Branch: main
33
+ Project: my-project
33
34
 
34
35
  Profile: production
35
36
  Account Origin: https://account.xano.com
@@ -45,6 +46,7 @@ Profile: default
45
46
  Access Token: ***...***
46
47
  Workspace: my-workspace
47
48
  Branch: main
49
+ Project: my-project
48
50
  `,
49
51
  ];
50
52
  async run() {
@@ -93,6 +95,9 @@ Profile: default
93
95
  if (profile.branch) {
94
96
  this.log(` Branch: ${profile.branch}`);
95
97
  }
98
+ if (profile.project) {
99
+ this.log(` Project: ${profile.project}`);
100
+ }
96
101
  this.log(''); // Empty line between profiles
97
102
  }
98
103
  }
@@ -0,0 +1,6 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class ProfileProject extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ run(): Promise<void>;
6
+ }
@@ -0,0 +1,54 @@
1
+ import { Command } from '@oclif/core';
2
+ import * as fs from 'node:fs';
3
+ import * as os from 'node:os';
4
+ import * as path from 'node:path';
5
+ import * as yaml from 'js-yaml';
6
+ export default class ProfileProject extends Command {
7
+ static description = 'Print the project for the default profile';
8
+ static examples = [
9
+ `$ xano profile:project
10
+ my-project-id
11
+ `,
12
+ `$ xano profile:project | pbcopy
13
+ # Copies the project to clipboard on macOS
14
+ `,
15
+ ];
16
+ async run() {
17
+ const configDir = path.join(os.homedir(), '.xano');
18
+ const credentialsPath = path.join(configDir, 'credentials.yaml');
19
+ // Check if credentials file exists
20
+ if (!fs.existsSync(credentialsPath)) {
21
+ this.error(`Credentials file not found at ${credentialsPath}. Create a profile first using 'profile:create'.`);
22
+ }
23
+ // Read credentials file
24
+ let credentials;
25
+ try {
26
+ const fileContent = fs.readFileSync(credentialsPath, 'utf8');
27
+ const parsed = yaml.load(fileContent);
28
+ if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
29
+ this.error('Credentials file has invalid format.');
30
+ }
31
+ credentials = parsed;
32
+ }
33
+ catch (error) {
34
+ this.error(`Failed to parse credentials file: ${error}`);
35
+ }
36
+ // Get the default profile name
37
+ const defaultProfileName = credentials.default;
38
+ if (!defaultProfileName) {
39
+ this.error("No default profile set. Set one using 'profile:set-default <name>'.");
40
+ }
41
+ // Check if the default profile exists
42
+ if (!(defaultProfileName in credentials.profiles)) {
43
+ this.error(`Default profile '${defaultProfileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}`);
44
+ }
45
+ const profile = credentials.profiles[defaultProfileName];
46
+ // Get and display the project
47
+ if (profile.project) {
48
+ this.log(profile.project);
49
+ }
50
+ else {
51
+ this.error(`Profile '${defaultProfileName}' does not have a project set. Set one using 'profile:edit -j <project>'.`);
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,6 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class ProfileToken extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ run(): Promise<void>;
6
+ }
@@ -0,0 +1,54 @@
1
+ import { Command } from '@oclif/core';
2
+ import * as fs from 'node:fs';
3
+ import * as os from 'node:os';
4
+ import * as path from 'node:path';
5
+ import * as yaml from 'js-yaml';
6
+ export default class ProfileToken extends Command {
7
+ static description = 'Print the access token for the default profile';
8
+ static examples = [
9
+ `$ xano profile:token
10
+ eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
11
+ `,
12
+ `$ xano profile:token | pbcopy
13
+ # Copies the token to clipboard on macOS
14
+ `,
15
+ ];
16
+ async run() {
17
+ const configDir = path.join(os.homedir(), '.xano');
18
+ const credentialsPath = path.join(configDir, 'credentials.yaml');
19
+ // Check if credentials file exists
20
+ if (!fs.existsSync(credentialsPath)) {
21
+ this.error(`Credentials file not found at ${credentialsPath}. Create a profile first using 'profile:create'.`);
22
+ }
23
+ // Read credentials file
24
+ let credentials;
25
+ try {
26
+ const fileContent = fs.readFileSync(credentialsPath, 'utf8');
27
+ const parsed = yaml.load(fileContent);
28
+ if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
29
+ this.error('Credentials file has invalid format.');
30
+ }
31
+ credentials = parsed;
32
+ }
33
+ catch (error) {
34
+ this.error(`Failed to parse credentials file: ${error}`);
35
+ }
36
+ // Get the default profile name
37
+ const defaultProfileName = credentials.default;
38
+ if (!defaultProfileName) {
39
+ this.error("No default profile set. Set one using 'profile:set-default <name>'.");
40
+ }
41
+ // Check if the default profile exists
42
+ if (!(defaultProfileName in credentials.profiles)) {
43
+ this.error(`Default profile '${defaultProfileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}`);
44
+ }
45
+ const profile = credentials.profiles[defaultProfileName];
46
+ // Get and display the access token
47
+ if (profile.access_token) {
48
+ this.log(profile.access_token);
49
+ }
50
+ else {
51
+ this.error(`Profile '${defaultProfileName}' does not have an access token.`);
52
+ }
53
+ }
54
+ }
@@ -10,6 +10,8 @@ export default class ProfileWizard extends Command {
10
10
  private fetchInstances;
11
11
  private fetchWorkspaces;
12
12
  private fetchBranches;
13
+ private fetchProjects;
14
+ private fetchRunProjects;
13
15
  private getDefaultProfileName;
14
16
  private saveProfile;
15
17
  }
@@ -4,6 +4,7 @@ import * as os from 'node:os';
4
4
  import * as path from 'node:path';
5
5
  import * as yaml from 'js-yaml';
6
6
  import inquirer from 'inquirer';
7
+ import { DEFAULT_RUN_BASE_URL } from '../../../lib/run-http-client.js';
7
8
  export default class ProfileWizard extends Command {
8
9
  static flags = {
9
10
  name: Flags.string({
@@ -96,6 +97,7 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
96
97
  // Step 5: Workspace selection
97
98
  let workspace;
98
99
  let branch;
100
+ let project;
99
101
  // Fetch workspaces from the selected instance
100
102
  this.log('');
101
103
  this.log('Fetching available workspaces...');
@@ -157,8 +159,56 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
157
159
  ]);
158
160
  branch = selectedBranch || undefined;
159
161
  }
162
+ // Step 6: Project selection
163
+ this.log('');
164
+ this.log('Fetching available projects...');
165
+ let projects = [];
166
+ try {
167
+ projects = await this.fetchProjects(accessToken, selectedInstance.origin, workspace, branch);
168
+ }
169
+ catch (error) {
170
+ this.warn(`Failed to fetch projects: ${error instanceof Error ? error.message : String(error)}`);
171
+ }
172
+ // If projects were fetched, let user select one
173
+ if (projects.length > 0) {
174
+ this.log('');
175
+ const { selectedProject } = await inquirer.prompt([
176
+ {
177
+ type: 'list',
178
+ name: 'selectedProject',
179
+ message: 'Select a project',
180
+ choices: [
181
+ { name: '(Skip project)', value: '' },
182
+ ...projects.map((proj) => {
183
+ return {
184
+ name: proj.name,
185
+ value: proj.id,
186
+ };
187
+ }),
188
+ ],
189
+ },
190
+ ]);
191
+ project = selectedProject || undefined;
192
+ }
160
193
  }
161
194
  }
195
+ // Step 7: Fetch run projects and auto-select the first one
196
+ let runProject;
197
+ this.log('');
198
+ this.log('Fetching available run projects...');
199
+ try {
200
+ const runProjects = await this.fetchRunProjects(accessToken);
201
+ if (runProjects.length > 0) {
202
+ runProject = runProjects[0].id;
203
+ this.log(`✓ Found ${runProjects.length} run project(s). Using "${runProjects[0].name}" as default.`);
204
+ }
205
+ else {
206
+ this.log('No run projects found. You can create one later with "xano run projects create".');
207
+ }
208
+ }
209
+ catch {
210
+ // Silently ignore - run_project will remain undefined
211
+ }
162
212
  // Save profile
163
213
  await this.saveProfile({
164
214
  name: profileName,
@@ -167,6 +217,8 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
167
217
  access_token: accessToken,
168
218
  workspace,
169
219
  branch,
220
+ project,
221
+ run_project: runProject,
170
222
  }, true);
171
223
  this.log('');
172
224
  this.log(`✓ Profile '${profileName}' created successfully!`);
@@ -288,6 +340,60 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
288
340
  }
289
341
  return [];
290
342
  }
343
+ async fetchProjects(accessToken, origin, workspaceId, branchId) {
344
+ const branchParam = branchId ? `?branch=${branchId}` : '';
345
+ const response = await fetch(`${origin}/api:meta/workspace/${workspaceId}/project${branchParam}`, {
346
+ method: 'GET',
347
+ headers: {
348
+ accept: 'application/json',
349
+ Authorization: `Bearer ${accessToken}`,
350
+ },
351
+ });
352
+ if (!response.ok) {
353
+ if (response.status === 401) {
354
+ throw new Error('Unauthorized. Please check your access token.');
355
+ }
356
+ throw new Error(`API request failed with status ${response.status}`);
357
+ }
358
+ const data = (await response.json());
359
+ // Transform API response to Project format
360
+ // Assuming the API returns an array or object with projects
361
+ if (Array.isArray(data)) {
362
+ return data.map((proj) => ({
363
+ id: proj.id || proj.name,
364
+ name: proj.name,
365
+ }));
366
+ }
367
+ // If it's an object, try to extract projects
368
+ if (data && typeof data === 'object') {
369
+ const projects = data.projects || data.data || [];
370
+ if (Array.isArray(projects)) {
371
+ return projects.map((proj) => ({
372
+ id: proj.id || proj.name,
373
+ name: proj.name,
374
+ }));
375
+ }
376
+ }
377
+ return [];
378
+ }
379
+ async fetchRunProjects(accessToken, runBaseUrl = DEFAULT_RUN_BASE_URL) {
380
+ const baseUrl = runBaseUrl.endsWith('/') ? runBaseUrl.slice(0, -1) : runBaseUrl;
381
+ const response = await fetch(`${baseUrl}/api:run/project`, {
382
+ method: 'GET',
383
+ headers: {
384
+ 'Content-Type': 'application/json',
385
+ Authorization: `Bearer ${accessToken}`,
386
+ },
387
+ });
388
+ if (!response.ok) {
389
+ if (response.status === 401) {
390
+ throw new Error('Unauthorized. Please check your access token.');
391
+ }
392
+ throw new Error(`API request failed with status ${response.status}`);
393
+ }
394
+ const data = (await response.json());
395
+ return Array.isArray(data) ? data : [];
396
+ }
291
397
  getDefaultProfileName() {
292
398
  try {
293
399
  const configDir = path.join(os.homedir(), '.xano');
@@ -334,6 +440,8 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
334
440
  access_token: profile.access_token,
335
441
  ...(profile.workspace && { workspace: profile.workspace }),
336
442
  ...(profile.branch && { branch: profile.branch }),
443
+ ...(profile.project && { project: profile.project }),
444
+ ...(profile.run_project && { run_project: profile.run_project }),
337
445
  };
338
446
  // Set as default if requested
339
447
  if (setAsDefault) {
@@ -0,0 +1,13 @@
1
+ import BaseRunCommand from '../../../../lib/base-run-command.js';
2
+ export default class RunEnvDelete extends BaseRunCommand {
3
+ static args: {
4
+ name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static flags: {
7
+ force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ };
10
+ static description: string;
11
+ static examples: string[];
12
+ run(): Promise<void>;
13
+ }