@xano/cli 0.0.13 → 0.0.15

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 (62) hide show
  1. package/README.md +97 -12
  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 +4 -0
  5. package/dist/commands/profile/edit/index.js +37 -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 +1 -0
  12. package/dist/commands/profile/wizard/index.js +70 -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/lib/base-run-command.d.ts +41 -0
  54. package/dist/lib/base-run-command.js +73 -0
  55. package/dist/lib/run-http-client.d.ts +58 -0
  56. package/dist/lib/run-http-client.js +136 -0
  57. package/dist/lib/run-types.d.ts +226 -0
  58. package/dist/lib/run-types.js +5 -0
  59. package/oclif.manifest.json +1423 -306
  60. package/package.json +1 -1
  61. package/dist/commands/ephemeral/run/job/index.js +0 -311
  62. package/dist/commands/ephemeral/run/service/index.js +0 -287
package/README.md CHANGED
@@ -23,16 +23,16 @@ 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
38
  # Create a profile interactively
@@ -41,6 +41,9 @@ xano profile:wizard
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,7 +52,8 @@ 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
53
57
 
54
58
  # Delete a profile
55
59
  xano profile:delete myprofile
@@ -84,18 +88,99 @@ xano function:edit 145 -f new.xs # Update from file
84
88
  xano function:edit 145 --publish # Publish after editing
85
89
  ```
86
90
 
87
- ### Ephemeral Jobs & Services
91
+ ### Xano Run
92
+
93
+ Execute XanoScript code and manage projects, sessions, environment variables, and secrets.
94
+
95
+ #### Executing Code
96
+
97
+ ```bash
98
+ # Execute XanoScript (job or service)
99
+ xano run exec -f script.xs
100
+ xano run exec -f https://example.com/script.xs # From URL
101
+ xano run exec -f script.xs -a args.json # With input arguments (file)
102
+ xano run exec -f script.xs -a https://ex.com/args.json # With input arguments (URL)
103
+ xano run exec -f script.xs --edit # Edit in $EDITOR first
104
+ xano run exec -f script.xs --env API_KEY=secret # With env overrides
105
+ cat script.xs | xano run exec --stdin # From stdin
106
+
107
+ # Get document info (type, inputs, env vars)
108
+ xano run info -f script.xs
109
+ ```
110
+
111
+ #### Projects
112
+
113
+ ```bash
114
+ # List projects
115
+ xano run projects list
116
+
117
+ # Create a project
118
+ xano run projects create -n "My Project"
119
+ xano run projects create -n "My Project" -d "Description"
120
+
121
+ # Update a project
122
+ xano run projects update <project-id> -n "New Name"
123
+ xano run projects update <project-id> -d "New description"
124
+
125
+ # Delete a project
126
+ xano run projects delete <project-id>
127
+ xano run projects delete <project-id> --force # Skip confirmation
128
+ ```
129
+
130
+ #### Sessions
131
+
132
+ ```bash
133
+ # List sessions
134
+ xano run sessions list
135
+
136
+ # Get session details
137
+ xano run sessions get <session-id>
138
+
139
+ # Start/stop a session
140
+ xano run sessions start <session-id>
141
+ xano run sessions stop <session-id>
142
+
143
+ # Delete a session
144
+ xano run sessions delete <session-id>
145
+ xano run sessions delete <session-id> --force # Skip confirmation
146
+
147
+ # Get sink data for a completed session
148
+ xano run sink get <session-id>
149
+ ```
150
+
151
+ #### Environment Variables
88
152
 
89
- Run XanoScript code without creating permanent resources.
153
+ ```bash
154
+ # List environment variable keys
155
+ xano run env list
156
+
157
+ # Set an environment variable
158
+ xano run env set API_KEY my-secret-key
159
+
160
+ # Get an environment variable value
161
+ xano run env get API_KEY
162
+
163
+ # Delete an environment variable
164
+ xano run env delete API_KEY
165
+ xano run env delete API_KEY --force # Skip confirmation
166
+ ```
167
+
168
+ #### Secrets
90
169
 
91
170
  ```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
171
+ # List secrets
172
+ xano run secrets list
173
+
174
+ # Set a secret
175
+ xano run secrets set docker-registry -t dockerconfigjson -v '{"auths":{...}}' -r ghcr.io
176
+ xano run secrets set service-key -t service-account-token -v 'token-value'
177
+
178
+ # Get a secret value
179
+ xano run secrets get docker-registry
96
180
 
97
- # Run a service (starts API endpoints)
98
- xano ephemeral:run:service -f service.xs
181
+ # Delete a secret
182
+ xano run secrets delete docker-registry
183
+ xano run secrets delete docker-registry --force # Skip confirmation
99
184
  ```
100
185
 
101
186
  ### Static Hosts
@@ -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,12 @@ 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>;
12
13
  'remove-workspace': import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
14
  'remove-branch': import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
+ 'remove-project': import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
+ run_base_url: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
+ 'remove-run-base-url': import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
18
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
19
  };
16
20
  static description: string;
@@ -38,6 +38,11 @@ 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
+ }),
41
46
  'remove-workspace': Flags.boolean({
42
47
  description: 'Remove workspace from profile',
43
48
  required: false,
@@ -48,6 +53,21 @@ export default class ProfileEdit extends BaseCommand {
48
53
  required: false,
49
54
  default: false,
50
55
  }),
56
+ 'remove-project': Flags.boolean({
57
+ description: 'Remove project from profile',
58
+ required: false,
59
+ default: false,
60
+ }),
61
+ run_base_url: Flags.string({
62
+ char: 'r',
63
+ description: 'Update Xano Run API base URL',
64
+ required: false,
65
+ }),
66
+ 'remove-run-base-url': Flags.boolean({
67
+ description: 'Remove run_base_url from profile (use default)',
68
+ required: false,
69
+ default: false,
70
+ }),
51
71
  };
52
72
  static description = 'Edit an existing profile configuration';
53
73
  static examples = [
@@ -65,6 +85,12 @@ Profile 'default' updated successfully at ~/.xano/credentials.yaml
65
85
  `,
66
86
  `$ xano profile:edit --remove-branch
67
87
  Profile 'default' updated successfully at ~/.xano/credentials.yaml
88
+ `,
89
+ `$ xano profile:edit -j my-project
90
+ Profile 'default' updated successfully at ~/.xano/credentials.yaml
91
+ `,
92
+ `$ xano profile:edit --remove-project
93
+ Profile 'default' updated successfully at ~/.xano/credentials.yaml
68
94
  `,
69
95
  ];
70
96
  async run() {
@@ -98,7 +124,9 @@ Profile 'default' updated successfully at ~/.xano/credentials.yaml
98
124
  const existingProfile = credentials.profiles[profileName];
99
125
  // Check if any flags were provided
100
126
  const hasFlags = flags.account_origin || flags.instance_origin || flags.access_token ||
101
- flags.workspace || flags.branch || flags['remove-workspace'] || flags['remove-branch'];
127
+ flags.workspace || flags.branch || flags.project || flags.run_base_url ||
128
+ flags['remove-workspace'] || flags['remove-branch'] || flags['remove-project'] ||
129
+ flags['remove-run-base-url'];
102
130
  if (!hasFlags) {
103
131
  this.error('No fields specified to update. Use at least one flag to edit the profile.');
104
132
  }
@@ -110,6 +138,8 @@ Profile 'default' updated successfully at ~/.xano/credentials.yaml
110
138
  ...(flags.access_token !== undefined && { access_token: flags.access_token }),
111
139
  ...(flags.workspace !== undefined && { workspace: flags.workspace }),
112
140
  ...(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 }),
113
143
  };
114
144
  // Handle removal flags
115
145
  if (flags['remove-workspace']) {
@@ -118,6 +148,12 @@ Profile 'default' updated successfully at ~/.xano/credentials.yaml
118
148
  if (flags['remove-branch']) {
119
149
  delete updatedProfile.branch;
120
150
  }
151
+ if (flags['remove-project']) {
152
+ delete updatedProfile.project;
153
+ }
154
+ if (flags['remove-run-base-url']) {
155
+ delete updatedProfile.run_base_url;
156
+ }
121
157
  credentials.profiles[profileName] = updatedProfile;
122
158
  // Write the updated credentials back to the file
123
159
  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,7 @@ export default class ProfileWizard extends Command {
10
10
  private fetchInstances;
11
11
  private fetchWorkspaces;
12
12
  private fetchBranches;
13
+ private fetchProjects;
13
14
  private getDefaultProfileName;
14
15
  private saveProfile;
15
16
  }
@@ -96,6 +96,7 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
96
96
  // Step 5: Workspace selection
97
97
  let workspace;
98
98
  let branch;
99
+ let project;
99
100
  // Fetch workspaces from the selected instance
100
101
  this.log('');
101
102
  this.log('Fetching available workspaces...');
@@ -157,6 +158,37 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
157
158
  ]);
158
159
  branch = selectedBranch || undefined;
159
160
  }
161
+ // Step 6: Project selection
162
+ this.log('');
163
+ this.log('Fetching available projects...');
164
+ let projects = [];
165
+ try {
166
+ projects = await this.fetchProjects(accessToken, selectedInstance.origin, workspace, branch);
167
+ }
168
+ catch (error) {
169
+ this.warn(`Failed to fetch projects: ${error instanceof Error ? error.message : String(error)}`);
170
+ }
171
+ // If projects were fetched, let user select one
172
+ if (projects.length > 0) {
173
+ this.log('');
174
+ const { selectedProject } = await inquirer.prompt([
175
+ {
176
+ type: 'list',
177
+ name: 'selectedProject',
178
+ message: 'Select a project',
179
+ choices: [
180
+ { name: '(Skip project)', value: '' },
181
+ ...projects.map((proj) => {
182
+ return {
183
+ name: proj.name,
184
+ value: proj.id,
185
+ };
186
+ }),
187
+ ],
188
+ },
189
+ ]);
190
+ project = selectedProject || undefined;
191
+ }
160
192
  }
161
193
  }
162
194
  // Save profile
@@ -167,6 +199,7 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
167
199
  access_token: accessToken,
168
200
  workspace,
169
201
  branch,
202
+ project,
170
203
  }, true);
171
204
  this.log('');
172
205
  this.log(`✓ Profile '${profileName}' created successfully!`);
@@ -288,6 +321,42 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
288
321
  }
289
322
  return [];
290
323
  }
324
+ async fetchProjects(accessToken, origin, workspaceId, branchId) {
325
+ const branchParam = branchId ? `?branch=${branchId}` : '';
326
+ const response = await fetch(`${origin}/api:meta/workspace/${workspaceId}/project${branchParam}`, {
327
+ method: 'GET',
328
+ headers: {
329
+ accept: 'application/json',
330
+ Authorization: `Bearer ${accessToken}`,
331
+ },
332
+ });
333
+ if (!response.ok) {
334
+ if (response.status === 401) {
335
+ throw new Error('Unauthorized. Please check your access token.');
336
+ }
337
+ throw new Error(`API request failed with status ${response.status}`);
338
+ }
339
+ const data = (await response.json());
340
+ // Transform API response to Project format
341
+ // Assuming the API returns an array or object with projects
342
+ if (Array.isArray(data)) {
343
+ return data.map((proj) => ({
344
+ id: proj.id || proj.name,
345
+ name: proj.name,
346
+ }));
347
+ }
348
+ // If it's an object, try to extract projects
349
+ if (data && typeof data === 'object') {
350
+ const projects = data.projects || data.data || [];
351
+ if (Array.isArray(projects)) {
352
+ return projects.map((proj) => ({
353
+ id: proj.id || proj.name,
354
+ name: proj.name,
355
+ }));
356
+ }
357
+ }
358
+ return [];
359
+ }
291
360
  getDefaultProfileName() {
292
361
  try {
293
362
  const configDir = path.join(os.homedir(), '.xano');
@@ -334,6 +403,7 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
334
403
  access_token: profile.access_token,
335
404
  ...(profile.workspace && { workspace: profile.workspace }),
336
405
  ...(profile.branch && { branch: profile.branch }),
406
+ ...(profile.project && { project: profile.project }),
337
407
  };
338
408
  // Set as default if requested
339
409
  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
+ }
@@ -0,0 +1,65 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import BaseRunCommand from '../../../../lib/base-run-command.js';
3
+ export default class RunEnvDelete extends BaseRunCommand {
4
+ static args = {
5
+ name: Args.string({
6
+ description: 'Environment variable name',
7
+ required: true,
8
+ }),
9
+ };
10
+ static flags = {
11
+ ...BaseRunCommand.baseFlags,
12
+ force: Flags.boolean({
13
+ char: 'f',
14
+ description: 'Skip confirmation prompt',
15
+ required: false,
16
+ default: false,
17
+ }),
18
+ };
19
+ static description = 'Delete an environment variable';
20
+ static examples = [
21
+ `$ xano run env delete API_KEY
22
+ Are you sure you want to delete environment variable 'API_KEY'? (y/N)
23
+ Environment variable 'API_KEY' deleted successfully!
24
+ `,
25
+ `$ xano run env delete API_KEY --force
26
+ Environment variable 'API_KEY' deleted successfully!
27
+ `,
28
+ ];
29
+ async run() {
30
+ const { args, flags } = await this.parse(RunEnvDelete);
31
+ // Initialize with project required
32
+ await this.initRunCommandWithProject(flags.profile);
33
+ // Confirm deletion unless --force is used
34
+ if (!flags.force) {
35
+ const readline = await import('node:readline');
36
+ const rl = readline.createInterface({
37
+ input: process.stdin,
38
+ output: process.stdout,
39
+ });
40
+ const confirmed = await new Promise((resolve) => {
41
+ rl.question(`Are you sure you want to delete environment variable '${args.name}'? (y/N) `, (answer) => {
42
+ rl.close();
43
+ resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
44
+ });
45
+ });
46
+ if (!confirmed) {
47
+ this.log('Deletion cancelled.');
48
+ return;
49
+ }
50
+ }
51
+ try {
52
+ const url = this.httpClient.buildProjectUrl('/env');
53
+ await this.httpClient.delete(url, { name: args.name });
54
+ this.log(`Environment variable '${args.name}' deleted successfully!`);
55
+ }
56
+ catch (error) {
57
+ if (error instanceof Error) {
58
+ this.error(`Failed to delete environment variable: ${error.message}`);
59
+ }
60
+ else {
61
+ this.error(`Failed to delete environment variable: ${String(error)}`);
62
+ }
63
+ }
64
+ }
65
+ }
@@ -0,0 +1,13 @@
1
+ import BaseRunCommand from '../../../../lib/base-run-command.js';
2
+ export default class RunEnvGet extends BaseRunCommand {
3
+ static args: {
4
+ name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static flags: {
7
+ output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
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
+ }