@xano/cli 0.0.17 → 0.0.19

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
@@ -25,7 +25,7 @@ npm install -g @xano/cli
25
25
 
26
26
  3. Execute XanoScript code:
27
27
  ```bash
28
- xano run exec -f script.xs
28
+ xano run exec script.xs
29
29
  ```
30
30
 
31
31
  ## Commands
@@ -54,8 +54,6 @@ xano profile:set-default myprofile
54
54
  # Edit a profile
55
55
  xano profile:edit myprofile -w 123 # Set default workspace
56
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
59
57
 
60
58
  # Delete a profile
61
59
  xano profile:delete myprofile
@@ -100,18 +98,21 @@ Execute XanoScript code and manage projects, sessions, environment variables, an
100
98
 
101
99
  ```bash
102
100
  # 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
101
+ xano run exec script.xs # Single file
102
+ xano run exec ./my-workspace # Directory (multidoc from .xs files)
103
+ xano run exec https://example.com/script.xs # From URL
104
+ xano run exec script.xs -a args.json # With input arguments (file)
105
+ xano run exec script.xs -a https://ex.com/args.json # With input arguments (URL)
106
+ xano run exec script.xs --edit # Edit in $EDITOR first
107
+ xano run exec script.xs --env API_KEY=secret # With env overrides
109
108
  cat script.xs | xano run exec --stdin # From stdin
110
109
 
111
110
  # Get document info (type, inputs, env vars)
112
111
  xano run info -f script.xs
113
112
  ```
114
113
 
114
+ When a directory is provided, all `.xs` files are collected recursively and combined into a multidoc (joined with `---` separators), similar to `xano workspace push`.
115
+
115
116
  #### Projects
116
117
 
117
118
  ```bash
@@ -225,7 +226,7 @@ profiles:
225
226
  access_token: <token>
226
227
  workspace: <workspace_id>
227
228
  branch: <branch_id>
228
- run_project: <run_project_id> # Used by xano run commands
229
+ project: <project_id>
229
230
  default: default
230
231
  ```
231
232
 
@@ -1,6 +1,8 @@
1
1
  import BaseRunCommand from '../../../lib/base-run-command.js';
2
2
  export default class RunExec extends BaseRunCommand {
3
- static args: {};
3
+ static args: {
4
+ path: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
5
+ };
4
6
  static flags: {
5
7
  file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
6
8
  stdin: import("@oclif/core/interfaces").BooleanFlag<boolean>;
@@ -13,6 +15,14 @@ export default class RunExec extends BaseRunCommand {
13
15
  static description: string;
14
16
  static examples: string[];
15
17
  run(): Promise<void>;
18
+ /**
19
+ * Load all .xs files from a directory and combine them into a multidoc.
20
+ */
21
+ private loadMultidocFromDirectory;
22
+ /**
23
+ * Recursively collect all .xs files from a directory, sorted for deterministic ordering.
24
+ */
25
+ private collectFiles;
16
26
  private outputSummary;
17
27
  private editFile;
18
28
  private isUrl;
@@ -1,16 +1,21 @@
1
- import { Flags } from '@oclif/core';
1
+ import { Args, Flags } from '@oclif/core';
2
2
  import { execSync } from 'node:child_process';
3
3
  import * as fs from 'node:fs';
4
4
  import * as os from 'node:os';
5
5
  import * as path from 'node:path';
6
6
  import BaseRunCommand from '../../../lib/base-run-command.js';
7
7
  export default class RunExec extends BaseRunCommand {
8
- static args = {};
8
+ static args = {
9
+ path: Args.string({
10
+ description: 'Path to file or directory containing XanoScript code (directory creates multidoc from .xs files)',
11
+ required: false,
12
+ }),
13
+ };
9
14
  static flags = {
10
15
  ...BaseRunCommand.baseFlags,
11
16
  file: Flags.string({
12
17
  char: 'f',
13
- description: 'Path or URL to file containing XanoScript code',
18
+ description: 'Path or URL to file containing XanoScript code (deprecated: use path argument instead)',
14
19
  required: false,
15
20
  exclusive: ['stdin'],
16
21
  }),
@@ -23,10 +28,9 @@ export default class RunExec extends BaseRunCommand {
23
28
  }),
24
29
  edit: Flags.boolean({
25
30
  char: 'e',
26
- description: 'Open file in editor before running (requires --file)',
31
+ description: 'Open file in editor before running (requires path argument or --file)',
27
32
  required: false,
28
33
  default: false,
29
- dependsOn: ['file'],
30
34
  }),
31
35
  output: Flags.string({
32
36
  char: 'o',
@@ -48,11 +52,16 @@ export default class RunExec extends BaseRunCommand {
48
52
  };
49
53
  static description = 'Execute XanoScript code (job or service)';
50
54
  static examples = [
51
- `$ xano run exec -f script.xs
55
+ `$ xano run exec script.xs
56
+ Executed successfully!
57
+ ...
58
+ `,
59
+ `$ xano run exec ./my-workspace
60
+ # Executes all .xs files in directory as multidoc
52
61
  Executed successfully!
53
62
  ...
54
63
  `,
55
- `$ xano run exec -f script.xs --edit
64
+ `$ xano run exec script.xs --edit
56
65
  # Opens script.xs in $EDITOR, then executes
57
66
  Executed successfully!
58
67
  ...
@@ -61,45 +70,63 @@ Executed successfully!
61
70
  Executed successfully!
62
71
  ...
63
72
  `,
64
- `$ xano run exec -f script.xs -o json
73
+ `$ xano run exec script.xs -o json
65
74
  {
66
75
  "run": { ... }
67
76
  }
68
77
  `,
69
- `$ xano run exec -f script.xs -a args.json
78
+ `$ xano run exec script.xs -a args.json
70
79
  # Executes with input arguments from args.json
71
80
  Executed successfully!
72
81
  ...
73
82
  `,
74
- `$ xano run exec -f script.xs --env API_KEY=secret --env DEBUG=true
83
+ `$ xano run exec script.xs --env API_KEY=secret --env DEBUG=true
75
84
  # Executes with environment variable overrides
76
85
  Executed successfully!
77
86
  ...
78
87
  `,
79
88
  ];
80
89
  async run() {
81
- const { flags } = await this.parse(RunExec);
90
+ const { args, flags } = await this.parse(RunExec);
82
91
  // Initialize with project required
83
92
  await this.initRunCommandWithProject(flags.profile);
93
+ // Determine input source: path argument, --file flag, or --stdin
94
+ const inputPath = args.path || flags.file;
95
+ // Validate --edit flag requirements
96
+ if (flags.edit) {
97
+ if (!inputPath) {
98
+ this.error('--edit requires a file path (either path argument or --file flag)');
99
+ }
100
+ if (this.isUrl(inputPath)) {
101
+ this.error('--edit cannot be used with URLs');
102
+ }
103
+ if (fs.existsSync(inputPath) && fs.statSync(inputPath).isDirectory()) {
104
+ this.error('--edit cannot be used with directories');
105
+ }
106
+ }
84
107
  // Read XanoScript content
85
108
  let xanoscript;
86
- if (flags.file) {
87
- if (this.isUrl(flags.file)) {
109
+ if (inputPath) {
110
+ if (this.isUrl(inputPath)) {
88
111
  // Fetch URL content
89
112
  try {
90
- const response = await fetch(flags.file);
113
+ const response = await fetch(inputPath);
91
114
  if (!response.ok) {
92
115
  this.error(`Failed to fetch URL: ${response.status} ${response.statusText}`);
93
116
  }
94
117
  xanoscript = await response.text();
95
118
  }
96
119
  catch (error) {
97
- this.error(`Failed to fetch URL '${flags.file}': ${error}`);
120
+ this.error(`Failed to fetch URL '${inputPath}': ${error}`);
98
121
  }
99
122
  }
123
+ else if (fs.existsSync(inputPath) && fs.statSync(inputPath).isDirectory()) {
124
+ // Handle directory - collect .xs files and create multidoc
125
+ xanoscript = this.loadMultidocFromDirectory(inputPath);
126
+ }
100
127
  else if (flags.edit) {
101
128
  // If edit flag is set, copy to temp file and open in editor
102
- const fileToRead = await this.editFile(flags.file);
129
+ const fileToRead = await this.editFile(inputPath);
103
130
  xanoscript = fs.readFileSync(fileToRead, 'utf8');
104
131
  // Clean up temp file
105
132
  try {
@@ -111,10 +138,10 @@ Executed successfully!
111
138
  }
112
139
  else {
113
140
  try {
114
- xanoscript = fs.readFileSync(flags.file, 'utf8');
141
+ xanoscript = fs.readFileSync(inputPath, 'utf8');
115
142
  }
116
143
  catch (error) {
117
- this.error(`Failed to read file '${flags.file}': ${error}`);
144
+ this.error(`Failed to read file '${inputPath}': ${error}`);
118
145
  }
119
146
  }
120
147
  }
@@ -127,7 +154,7 @@ Executed successfully!
127
154
  }
128
155
  }
129
156
  else {
130
- this.error('Either --file or --stdin must be specified to provide XanoScript code');
157
+ this.error('Either a path argument, --file, or --stdin must be specified to provide XanoScript code');
131
158
  }
132
159
  // Validate xanoscript is not empty
133
160
  if (!xanoscript || xanoscript.trim().length === 0) {
@@ -202,6 +229,48 @@ Executed successfully!
202
229
  }
203
230
  }
204
231
  }
232
+ /**
233
+ * Load all .xs files from a directory and combine them into a multidoc.
234
+ */
235
+ loadMultidocFromDirectory(dir) {
236
+ const resolvedDir = path.resolve(dir);
237
+ if (!fs.existsSync(resolvedDir)) {
238
+ this.error(`Directory not found: ${resolvedDir}`);
239
+ }
240
+ const files = this.collectFiles(resolvedDir);
241
+ if (files.length === 0) {
242
+ this.error(`No .xs files found in ${dir}`);
243
+ }
244
+ // Read each file and join with --- separator
245
+ const documents = [];
246
+ for (const filePath of files) {
247
+ const content = fs.readFileSync(filePath, 'utf8').trim();
248
+ if (content) {
249
+ documents.push(content);
250
+ }
251
+ }
252
+ if (documents.length === 0) {
253
+ this.error(`All .xs files in ${dir} are empty`);
254
+ }
255
+ return documents.join('\n---\n');
256
+ }
257
+ /**
258
+ * Recursively collect all .xs files from a directory, sorted for deterministic ordering.
259
+ */
260
+ collectFiles(dir) {
261
+ const files = [];
262
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
263
+ for (const entry of entries) {
264
+ const fullPath = path.join(dir, entry.name);
265
+ if (entry.isDirectory()) {
266
+ files.push(...this.collectFiles(fullPath));
267
+ }
268
+ else if (entry.isFile() && entry.name.endsWith('.xs')) {
269
+ files.push(fullPath);
270
+ }
271
+ }
272
+ return files.sort();
273
+ }
205
274
  outputSummary(result) {
206
275
  this.log('Executed successfully!');
207
276
  this.log('');