@xano/cli 0.0.16 → 0.0.18
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 +11 -10
- package/dist/commands/profile/edit/index.d.ts +0 -2
- package/dist/commands/profile/edit/index.js +3 -16
- package/dist/commands/profile/wizard/index.js +6 -6
- package/dist/commands/run/exec/index.d.ts +11 -1
- package/dist/commands/run/exec/index.js +75 -17
- package/dist/lib/base-run-command.d.ts +0 -1
- package/dist/lib/base-run-command.js +4 -6
- package/oclif.manifest.json +599 -607
- package/package.json +1 -1
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
|
|
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
|
|
104
|
-
xano run exec -
|
|
105
|
-
xano run exec
|
|
106
|
-
xano run exec
|
|
107
|
-
xano run exec
|
|
108
|
-
xano run exec
|
|
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
|
-
|
|
229
|
+
project: <project_id>
|
|
229
230
|
default: default
|
|
230
231
|
```
|
|
231
232
|
|
|
@@ -10,11 +10,9 @@ export default class ProfileEdit extends BaseCommand {
|
|
|
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
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>;
|
|
14
13
|
'remove-workspace': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
14
|
'remove-branch': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
15
|
'remove-project': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
|
-
'remove-run-project': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
18
16
|
run_base_url: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
17
|
'remove-run-base-url': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
20
18
|
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -40,11 +40,7 @@ export default class ProfileEdit extends BaseCommand {
|
|
|
40
40
|
}),
|
|
41
41
|
project: Flags.string({
|
|
42
42
|
char: 'j',
|
|
43
|
-
description: 'Update project
|
|
44
|
-
required: false,
|
|
45
|
-
}),
|
|
46
|
-
'run-project': Flags.string({
|
|
47
|
-
description: 'Update run project ID (for xano run commands)',
|
|
43
|
+
description: 'Update project ID',
|
|
48
44
|
required: false,
|
|
49
45
|
}),
|
|
50
46
|
'remove-workspace': Flags.boolean({
|
|
@@ -62,11 +58,6 @@ export default class ProfileEdit extends BaseCommand {
|
|
|
62
58
|
required: false,
|
|
63
59
|
default: false,
|
|
64
60
|
}),
|
|
65
|
-
'remove-run-project': Flags.boolean({
|
|
66
|
-
description: 'Remove run project from profile',
|
|
67
|
-
required: false,
|
|
68
|
-
default: false,
|
|
69
|
-
}),
|
|
70
61
|
run_base_url: Flags.string({
|
|
71
62
|
char: 'r',
|
|
72
63
|
description: 'Update Xano Run API base URL',
|
|
@@ -133,9 +124,9 @@ Profile 'default' updated successfully at ~/.xano/credentials.yaml
|
|
|
133
124
|
const existingProfile = credentials.profiles[profileName];
|
|
134
125
|
// Check if any flags were provided
|
|
135
126
|
const hasFlags = flags.account_origin || flags.instance_origin || flags.access_token ||
|
|
136
|
-
flags.workspace || flags.branch || flags.project || flags
|
|
127
|
+
flags.workspace || flags.branch || flags.project || flags.run_base_url ||
|
|
137
128
|
flags['remove-workspace'] || flags['remove-branch'] || flags['remove-project'] ||
|
|
138
|
-
flags['remove-run-
|
|
129
|
+
flags['remove-run-base-url'];
|
|
139
130
|
if (!hasFlags) {
|
|
140
131
|
this.error('No fields specified to update. Use at least one flag to edit the profile.');
|
|
141
132
|
}
|
|
@@ -148,7 +139,6 @@ Profile 'default' updated successfully at ~/.xano/credentials.yaml
|
|
|
148
139
|
...(flags.workspace !== undefined && { workspace: flags.workspace }),
|
|
149
140
|
...(flags.branch !== undefined && { branch: flags.branch }),
|
|
150
141
|
...(flags.project !== undefined && { project: flags.project }),
|
|
151
|
-
...(flags['run-project'] !== undefined && { run_project: flags['run-project'] }),
|
|
152
142
|
...(flags.run_base_url !== undefined && { run_base_url: flags.run_base_url }),
|
|
153
143
|
};
|
|
154
144
|
// Handle removal flags
|
|
@@ -161,9 +151,6 @@ Profile 'default' updated successfully at ~/.xano/credentials.yaml
|
|
|
161
151
|
if (flags['remove-project']) {
|
|
162
152
|
delete updatedProfile.project;
|
|
163
153
|
}
|
|
164
|
-
if (flags['remove-run-project']) {
|
|
165
|
-
delete updatedProfile.run_project;
|
|
166
|
-
}
|
|
167
154
|
if (flags['remove-run-base-url']) {
|
|
168
155
|
delete updatedProfile.run_base_url;
|
|
169
156
|
}
|
|
@@ -192,14 +192,16 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
|
|
|
192
192
|
}
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
|
-
// Step 7: Fetch run projects and auto-select the first one
|
|
196
|
-
let runProject;
|
|
195
|
+
// Step 7: Fetch run projects and auto-select the first one if no project was selected
|
|
197
196
|
this.log('');
|
|
198
197
|
this.log('Fetching available run projects...');
|
|
199
198
|
try {
|
|
200
199
|
const runProjects = await this.fetchRunProjects(accessToken);
|
|
201
200
|
if (runProjects.length > 0) {
|
|
202
|
-
|
|
201
|
+
// Use run project if no metadata project was selected
|
|
202
|
+
if (!project) {
|
|
203
|
+
project = runProjects[0].id;
|
|
204
|
+
}
|
|
203
205
|
this.log(`✓ Found ${runProjects.length} run project(s). Using "${runProjects[0].name}" as default.`);
|
|
204
206
|
}
|
|
205
207
|
else {
|
|
@@ -207,7 +209,7 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
|
|
|
207
209
|
}
|
|
208
210
|
}
|
|
209
211
|
catch {
|
|
210
|
-
// Silently ignore -
|
|
212
|
+
// Silently ignore - project will remain undefined
|
|
211
213
|
}
|
|
212
214
|
// Save profile
|
|
213
215
|
await this.saveProfile({
|
|
@@ -218,7 +220,6 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
|
|
|
218
220
|
workspace,
|
|
219
221
|
branch,
|
|
220
222
|
project,
|
|
221
|
-
run_project: runProject,
|
|
222
223
|
}, true);
|
|
223
224
|
this.log('');
|
|
224
225
|
this.log(`✓ Profile '${profileName}' created successfully!`);
|
|
@@ -441,7 +442,6 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
|
|
|
441
442
|
...(profile.workspace && { workspace: profile.workspace }),
|
|
442
443
|
...(profile.branch && { branch: profile.branch }),
|
|
443
444
|
...(profile.project && { project: profile.project }),
|
|
444
|
-
...(profile.run_project && { run_project: profile.run_project }),
|
|
445
445
|
};
|
|
446
446
|
// Set as default if requested
|
|
447
447
|
if (setAsDefault) {
|
|
@@ -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
|
}),
|
|
@@ -48,11 +53,16 @@ export default class RunExec extends BaseRunCommand {
|
|
|
48
53
|
};
|
|
49
54
|
static description = 'Execute XanoScript code (job or service)';
|
|
50
55
|
static examples = [
|
|
51
|
-
`$ xano run exec
|
|
56
|
+
`$ xano run exec script.xs
|
|
52
57
|
Executed successfully!
|
|
53
58
|
...
|
|
54
59
|
`,
|
|
55
|
-
`$ xano run exec -
|
|
60
|
+
`$ xano run exec ./my-workspace
|
|
61
|
+
# Executes all .xs files in directory as multidoc
|
|
62
|
+
Executed successfully!
|
|
63
|
+
...
|
|
64
|
+
`,
|
|
65
|
+
`$ xano run exec script.xs --edit
|
|
56
66
|
# Opens script.xs in $EDITOR, then executes
|
|
57
67
|
Executed successfully!
|
|
58
68
|
...
|
|
@@ -61,45 +71,51 @@ Executed successfully!
|
|
|
61
71
|
Executed successfully!
|
|
62
72
|
...
|
|
63
73
|
`,
|
|
64
|
-
`$ xano run exec
|
|
74
|
+
`$ xano run exec script.xs -o json
|
|
65
75
|
{
|
|
66
76
|
"run": { ... }
|
|
67
77
|
}
|
|
68
78
|
`,
|
|
69
|
-
`$ xano run exec
|
|
79
|
+
`$ xano run exec script.xs -a args.json
|
|
70
80
|
# Executes with input arguments from args.json
|
|
71
81
|
Executed successfully!
|
|
72
82
|
...
|
|
73
83
|
`,
|
|
74
|
-
`$ xano run exec
|
|
84
|
+
`$ xano run exec script.xs --env API_KEY=secret --env DEBUG=true
|
|
75
85
|
# Executes with environment variable overrides
|
|
76
86
|
Executed successfully!
|
|
77
87
|
...
|
|
78
88
|
`,
|
|
79
89
|
];
|
|
80
90
|
async run() {
|
|
81
|
-
const { flags } = await this.parse(RunExec);
|
|
91
|
+
const { args, flags } = await this.parse(RunExec);
|
|
82
92
|
// Initialize with project required
|
|
83
93
|
await this.initRunCommandWithProject(flags.profile);
|
|
84
94
|
// Read XanoScript content
|
|
85
95
|
let xanoscript;
|
|
86
|
-
|
|
87
|
-
|
|
96
|
+
// Determine input source: path argument, --file flag, or --stdin
|
|
97
|
+
const inputPath = args.path || flags.file;
|
|
98
|
+
if (inputPath) {
|
|
99
|
+
if (this.isUrl(inputPath)) {
|
|
88
100
|
// Fetch URL content
|
|
89
101
|
try {
|
|
90
|
-
const response = await fetch(
|
|
102
|
+
const response = await fetch(inputPath);
|
|
91
103
|
if (!response.ok) {
|
|
92
104
|
this.error(`Failed to fetch URL: ${response.status} ${response.statusText}`);
|
|
93
105
|
}
|
|
94
106
|
xanoscript = await response.text();
|
|
95
107
|
}
|
|
96
108
|
catch (error) {
|
|
97
|
-
this.error(`Failed to fetch URL '${
|
|
109
|
+
this.error(`Failed to fetch URL '${inputPath}': ${error}`);
|
|
98
110
|
}
|
|
99
111
|
}
|
|
112
|
+
else if (fs.existsSync(inputPath) && fs.statSync(inputPath).isDirectory()) {
|
|
113
|
+
// Handle directory - collect .xs files and create multidoc
|
|
114
|
+
xanoscript = this.loadMultidocFromDirectory(inputPath);
|
|
115
|
+
}
|
|
100
116
|
else if (flags.edit) {
|
|
101
117
|
// If edit flag is set, copy to temp file and open in editor
|
|
102
|
-
const fileToRead = await this.editFile(
|
|
118
|
+
const fileToRead = await this.editFile(inputPath);
|
|
103
119
|
xanoscript = fs.readFileSync(fileToRead, 'utf8');
|
|
104
120
|
// Clean up temp file
|
|
105
121
|
try {
|
|
@@ -111,10 +127,10 @@ Executed successfully!
|
|
|
111
127
|
}
|
|
112
128
|
else {
|
|
113
129
|
try {
|
|
114
|
-
xanoscript = fs.readFileSync(
|
|
130
|
+
xanoscript = fs.readFileSync(inputPath, 'utf8');
|
|
115
131
|
}
|
|
116
132
|
catch (error) {
|
|
117
|
-
this.error(`Failed to read file '${
|
|
133
|
+
this.error(`Failed to read file '${inputPath}': ${error}`);
|
|
118
134
|
}
|
|
119
135
|
}
|
|
120
136
|
}
|
|
@@ -127,7 +143,7 @@ Executed successfully!
|
|
|
127
143
|
}
|
|
128
144
|
}
|
|
129
145
|
else {
|
|
130
|
-
this.error('Either --file or --stdin must be specified to provide XanoScript code');
|
|
146
|
+
this.error('Either a path argument, --file, or --stdin must be specified to provide XanoScript code');
|
|
131
147
|
}
|
|
132
148
|
// Validate xanoscript is not empty
|
|
133
149
|
if (!xanoscript || xanoscript.trim().length === 0) {
|
|
@@ -202,6 +218,48 @@ Executed successfully!
|
|
|
202
218
|
}
|
|
203
219
|
}
|
|
204
220
|
}
|
|
221
|
+
/**
|
|
222
|
+
* Load all .xs files from a directory and combine them into a multidoc.
|
|
223
|
+
*/
|
|
224
|
+
loadMultidocFromDirectory(dir) {
|
|
225
|
+
const resolvedDir = path.resolve(dir);
|
|
226
|
+
if (!fs.existsSync(resolvedDir)) {
|
|
227
|
+
this.error(`Directory not found: ${resolvedDir}`);
|
|
228
|
+
}
|
|
229
|
+
const files = this.collectFiles(resolvedDir);
|
|
230
|
+
if (files.length === 0) {
|
|
231
|
+
this.error(`No .xs files found in ${dir}`);
|
|
232
|
+
}
|
|
233
|
+
// Read each file and join with --- separator
|
|
234
|
+
const documents = [];
|
|
235
|
+
for (const filePath of files) {
|
|
236
|
+
const content = fs.readFileSync(filePath, 'utf8').trim();
|
|
237
|
+
if (content) {
|
|
238
|
+
documents.push(content);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (documents.length === 0) {
|
|
242
|
+
this.error(`All .xs files in ${dir} are empty`);
|
|
243
|
+
}
|
|
244
|
+
return documents.join('\n---\n');
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Recursively collect all .xs files from a directory, sorted for deterministic ordering.
|
|
248
|
+
*/
|
|
249
|
+
collectFiles(dir) {
|
|
250
|
+
const files = [];
|
|
251
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
252
|
+
for (const entry of entries) {
|
|
253
|
+
const fullPath = path.join(dir, entry.name);
|
|
254
|
+
if (entry.isDirectory()) {
|
|
255
|
+
files.push(...this.collectFiles(fullPath));
|
|
256
|
+
}
|
|
257
|
+
else if (entry.isFile() && entry.name.endsWith('.xs')) {
|
|
258
|
+
files.push(fullPath);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return files.sort();
|
|
262
|
+
}
|
|
205
263
|
outputSummary(result) {
|
|
206
264
|
this.log('Executed successfully!');
|
|
207
265
|
this.log('');
|
|
@@ -26,12 +26,10 @@ export default class BaseRunCommand extends BaseCommand {
|
|
|
26
26
|
this.error(`Profile '${this.profileName}' is missing access_token`);
|
|
27
27
|
}
|
|
28
28
|
const baseUrl = this.profile.run_base_url || DEFAULT_RUN_BASE_URL;
|
|
29
|
-
// Use run_project if available, fall back to project for backward compatibility
|
|
30
|
-
const projectId = this.profile.run_project || this.profile.project;
|
|
31
29
|
this.httpClient = new RunHttpClient({
|
|
32
30
|
baseUrl,
|
|
33
31
|
authToken: this.profile.access_token,
|
|
34
|
-
projectId,
|
|
32
|
+
projectId: this.profile.project,
|
|
35
33
|
});
|
|
36
34
|
}
|
|
37
35
|
/**
|
|
@@ -39,9 +37,9 @@ export default class BaseRunCommand extends BaseCommand {
|
|
|
39
37
|
*/
|
|
40
38
|
async initRunCommandWithProject(profileFlag) {
|
|
41
39
|
await this.initRunCommand(profileFlag);
|
|
42
|
-
if (!this.profile.
|
|
43
|
-
this.error(`Profile '${this.profileName}' is missing
|
|
44
|
-
`Run 'xano profile:wizard' to set up your profile or use 'xano profile:edit --
|
|
40
|
+
if (!this.profile.project) {
|
|
41
|
+
this.error(`Profile '${this.profileName}' is missing project. ` +
|
|
42
|
+
`Run 'xano profile:wizard' to set up your profile or use 'xano profile:edit --project <project-id>'`);
|
|
45
43
|
}
|
|
46
44
|
}
|
|
47
45
|
/**
|