@xano/cli 0.0.10 → 0.0.12

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.
@@ -2,7 +2,6 @@ import BaseCommand from '../../../../base-command.js';
2
2
  export default class EphemeralRunJob extends BaseCommand {
3
3
  static args: {};
4
4
  static flags: {
5
- workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
6
5
  file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
6
  stdin: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
7
  edit: import("@oclif/core/interfaces").BooleanFlag<boolean>;
@@ -14,8 +13,7 @@ export default class EphemeralRunJob extends BaseCommand {
14
13
  static examples: string[];
15
14
  run(): Promise<void>;
16
15
  private editFile;
17
- private readStdin;
18
16
  private isUrl;
19
- private fetchContent;
17
+ private readStdin;
20
18
  private loadCredentials;
21
19
  }
@@ -9,11 +9,6 @@ export default class EphemeralRunJob extends BaseCommand {
9
9
  static args = {};
10
10
  static flags = {
11
11
  ...BaseCommand.baseFlags,
12
- workspace: Flags.string({
13
- char: 'w',
14
- description: 'Workspace ID (optional if set in profile)',
15
- required: false,
16
- }),
17
12
  file: Flags.string({
18
13
  char: 'f',
19
14
  description: 'Path or URL to file containing XanoScript code',
@@ -47,32 +42,28 @@ export default class EphemeralRunJob extends BaseCommand {
47
42
  required: false,
48
43
  }),
49
44
  };
50
- static description = 'Run an ephemeral job in a workspace';
45
+ static description = 'Run an ephemeral job';
51
46
  static examples = [
52
- `$ xano ephemeral:run:job -w 1 -f script.xs
53
- Job executed successfully!
54
- ...
55
- `,
56
47
  `$ xano ephemeral:run:job -f script.xs
57
48
  Job executed successfully!
58
49
  ...
59
50
  `,
60
- `$ xano ephemeral:run:job -w 1 -f script.xs --edit
51
+ `$ xano ephemeral:run:job -f script.xs --edit
61
52
  # Opens script.xs in $EDITOR, then runs job with edited content
62
53
  Job executed successfully!
63
54
  ...
64
55
  `,
65
- `$ cat script.xs | xano ephemeral:run:job -w 1 --stdin
56
+ `$ cat script.xs | xano ephemeral:run:job --stdin
66
57
  Job executed successfully!
67
58
  ...
68
59
  `,
69
- `$ xano ephemeral:run:job -w 1 -f script.xs -o json
60
+ `$ xano ephemeral:run:job -f script.xs -o json
70
61
  {
71
62
  "job": { "id": 1, "run": { "id": 1 } },
72
63
  "result": { ... }
73
64
  }
74
65
  `,
75
- `$ xano ephemeral:run:job -w 1 -f script.xs -a args.json
66
+ `$ xano ephemeral:run:job -f script.xs -a args.json
76
67
  # Runs job with input arguments from args.json
77
68
  Job executed successfully!
78
69
  ...
@@ -97,24 +88,16 @@ Job executed successfully!
97
88
  if (!profile.access_token) {
98
89
  this.error(`Profile '${profileName}' is missing access_token`);
99
90
  }
100
- // Determine workspace_id from flag or profile
101
- let workspaceId;
102
- if (flags.workspace) {
103
- workspaceId = flags.workspace;
104
- }
105
- else if (profile.workspace) {
106
- workspaceId = profile.workspace;
107
- }
108
- else {
109
- this.error(`Workspace ID is required. Either:\n` +
110
- ` 1. Provide it as a flag: xano ephemeral:run:job -w <workspace_id>\n` +
111
- ` 2. Set it in your profile using: xano profile:edit ${profileName} -w <workspace_id>`);
112
- }
113
- // Read XanoScript content
91
+ // Read XanoScript content or use URL
114
92
  let xanoscript;
93
+ let xanoscriptUrl;
115
94
  if (flags.file) {
116
- // If edit flag is set and source is not a URL, copy to temp file and open in editor
117
- if (flags.edit && !this.isUrl(flags.file)) {
95
+ if (this.isUrl(flags.file)) {
96
+ // Pass URL directly to API
97
+ xanoscriptUrl = flags.file;
98
+ }
99
+ else if (flags.edit) {
100
+ // If edit flag is set, copy to temp file and open in editor
118
101
  const fileToRead = await this.editFile(flags.file);
119
102
  xanoscript = fs.readFileSync(fileToRead, 'utf8');
120
103
  // Clean up temp file
@@ -126,7 +109,12 @@ Job executed successfully!
126
109
  }
127
110
  }
128
111
  else {
129
- xanoscript = await this.fetchContent(flags.file);
112
+ try {
113
+ xanoscript = fs.readFileSync(flags.file, 'utf8');
114
+ }
115
+ catch (error) {
116
+ this.error(`Failed to read file '${flags.file}': ${error}`);
117
+ }
130
118
  }
131
119
  }
132
120
  else if (flags.stdin) {
@@ -141,45 +129,53 @@ Job executed successfully!
141
129
  else {
142
130
  this.error('Either --file or --stdin must be specified to provide XanoScript code');
143
131
  }
144
- // Validate xanoscript is not empty
145
- if (!xanoscript || xanoscript.trim().length === 0) {
132
+ // Validate xanoscript is not empty (only if not using URL)
133
+ if (!xanoscriptUrl && (!xanoscript || xanoscript.trim().length === 0)) {
146
134
  this.error('XanoScript content is empty');
147
135
  }
148
- // Load args from JSON file or URL if provided
136
+ // Load args from JSON file/URL if provided
149
137
  let inputArgs;
138
+ let inputArgsUrl;
150
139
  if (flags.args) {
151
- try {
152
- const argsContent = await this.fetchContent(flags.args);
153
- inputArgs = JSON.parse(argsContent);
140
+ if (this.isUrl(flags.args)) {
141
+ // Pass URL directly to API
142
+ inputArgsUrl = flags.args;
154
143
  }
155
- catch (error) {
156
- this.error(`Failed to read or parse args '${flags.args}': ${error}`);
144
+ else {
145
+ try {
146
+ const argsContent = fs.readFileSync(flags.args, 'utf8');
147
+ inputArgs = JSON.parse(argsContent);
148
+ }
149
+ catch (error) {
150
+ this.error(`Failed to read or parse args '${flags.args}': ${error}`);
151
+ }
157
152
  }
158
153
  }
159
154
  // Construct the API URL
160
- const apiUrl = `${profile.instance_origin}/api:meta/beta/workspace/${workspaceId}/ephemeral/job`;
161
- // Build request body - multipart if args provided, plain xanoscript otherwise
162
- let requestBody;
163
- let contentType;
155
+ const apiUrl = `${profile.instance_origin}/api:meta/beta/ephemeral/job`;
156
+ // Build request body
164
157
  const formData = new FormData();
165
- formData.append('doc', xanoscript);
166
- if (inputArgs) {
158
+ if (xanoscriptUrl) {
159
+ formData.append('doc', xanoscriptUrl);
160
+ }
161
+ else {
162
+ formData.append('doc', xanoscript);
163
+ }
164
+ if (inputArgsUrl) {
165
+ formData.append('args', inputArgsUrl);
166
+ }
167
+ else if (inputArgs) {
167
168
  formData.append('args', JSON.stringify(inputArgs));
168
169
  }
169
- requestBody = formData;
170
- contentType = ''; // Let fetch set the boundary
170
+ const requestBody = formData;
171
171
  // Run ephemeral job via API
172
172
  try {
173
- const headers = {
174
- 'accept': 'application/json',
175
- 'Authorization': `Bearer ${profile.access_token}`,
176
- };
177
- if (contentType) {
178
- headers['Content-Type'] = contentType;
179
- }
180
173
  const response = await fetch(apiUrl, {
181
174
  method: 'POST',
182
- headers,
175
+ headers: {
176
+ 'accept': 'application/json',
177
+ 'Authorization': `Bearer ${profile.access_token}`,
178
+ },
183
179
  body: requestBody,
184
180
  });
185
181
  if (!response.ok) {
@@ -272,6 +268,9 @@ Job executed successfully!
272
268
  }
273
269
  return tmpFile;
274
270
  }
271
+ isUrl(str) {
272
+ return str.startsWith('http://') || str.startsWith('https://');
273
+ }
275
274
  async readStdin() {
276
275
  return new Promise((resolve, reject) => {
277
276
  const chunks = [];
@@ -288,31 +287,6 @@ Job executed successfully!
288
287
  process.stdin.resume();
289
288
  });
290
289
  }
291
- isUrl(str) {
292
- return str.startsWith('http://') || str.startsWith('https://');
293
- }
294
- async fetchContent(source) {
295
- if (this.isUrl(source)) {
296
- try {
297
- const response = await fetch(source);
298
- if (!response.ok) {
299
- this.error(`Failed to fetch '${source}': ${response.status} ${response.statusText}`);
300
- }
301
- return await response.text();
302
- }
303
- catch (error) {
304
- this.error(`Failed to fetch '${source}': ${error}`);
305
- }
306
- }
307
- else {
308
- try {
309
- return fs.readFileSync(source, 'utf8');
310
- }
311
- catch (error) {
312
- this.error(`Failed to read file '${source}': ${error}`);
313
- }
314
- }
315
- }
316
290
  loadCredentials() {
317
291
  const configDir = path.join(os.homedir(), '.xano');
318
292
  const credentialsPath = path.join(configDir, 'credentials.yaml');
@@ -2,7 +2,6 @@ import BaseCommand from '../../../../base-command.js';
2
2
  export default class EphemeralRunService extends BaseCommand {
3
3
  static args: {};
4
4
  static flags: {
5
- workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
6
5
  file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
6
  stdin: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
7
  edit: import("@oclif/core/interfaces").BooleanFlag<boolean>;
@@ -13,8 +12,7 @@ export default class EphemeralRunService extends BaseCommand {
13
12
  static examples: string[];
14
13
  run(): Promise<void>;
15
14
  private editFile;
16
- private readStdin;
17
15
  private isUrl;
18
- private fetchContent;
16
+ private readStdin;
19
17
  private loadCredentials;
20
18
  }
@@ -9,11 +9,6 @@ export default class EphemeralRunService extends BaseCommand {
9
9
  static args = {};
10
10
  static flags = {
11
11
  ...BaseCommand.baseFlags,
12
- workspace: Flags.string({
13
- char: 'w',
14
- description: 'Workspace ID (optional if set in profile)',
15
- required: false,
16
- }),
17
12
  file: Flags.string({
18
13
  char: 'f',
19
14
  description: 'Path or URL to file containing XanoScript code',
@@ -42,26 +37,22 @@ export default class EphemeralRunService extends BaseCommand {
42
37
  options: ['summary', 'json'],
43
38
  }),
44
39
  };
45
- static description = 'Run an ephemeral service in a workspace';
40
+ static description = 'Run an ephemeral service';
46
41
  static examples = [
47
- `$ xano ephemeral:run:service -w 1 -f service.xs
48
- Service created successfully!
49
- ...
50
- `,
51
42
  `$ xano ephemeral:run:service -f service.xs
52
43
  Service created successfully!
53
44
  ...
54
45
  `,
55
- `$ xano ephemeral:run:service -w 1 -f service.xs --edit
46
+ `$ xano ephemeral:run:service -f service.xs --edit
56
47
  # Opens service.xs in $EDITOR, then creates service with edited content
57
48
  Service created successfully!
58
49
  ...
59
50
  `,
60
- `$ cat service.xs | xano ephemeral:run:service -w 1 --stdin
51
+ `$ cat service.xs | xano ephemeral:run:service --stdin
61
52
  Service created successfully!
62
53
  ...
63
54
  `,
64
- `$ xano ephemeral:run:service -w 1 -f service.xs -o json
55
+ `$ xano ephemeral:run:service -f service.xs -o json
65
56
  {
66
57
  "service": { "id": 1 },
67
58
  ...
@@ -87,24 +78,16 @@ Service created successfully!
87
78
  if (!profile.access_token) {
88
79
  this.error(`Profile '${profileName}' is missing access_token`);
89
80
  }
90
- // Determine workspace_id from flag or profile
91
- let workspaceId;
92
- if (flags.workspace) {
93
- workspaceId = flags.workspace;
94
- }
95
- else if (profile.workspace) {
96
- workspaceId = profile.workspace;
97
- }
98
- else {
99
- this.error(`Workspace ID is required. Either:\n` +
100
- ` 1. Provide it as a flag: xano ephemeral:run:service -w <workspace_id>\n` +
101
- ` 2. Set it in your profile using: xano profile:edit ${profileName} -w <workspace_id>`);
102
- }
103
- // Read XanoScript content
81
+ // Read XanoScript content or use URL
104
82
  let xanoscript;
83
+ let xanoscriptUrl;
105
84
  if (flags.file) {
106
- // If edit flag is set and source is not a URL, copy to temp file and open in editor
107
- if (flags.edit && !this.isUrl(flags.file)) {
85
+ if (this.isUrl(flags.file)) {
86
+ // Pass URL directly to API
87
+ xanoscriptUrl = flags.file;
88
+ }
89
+ else if (flags.edit) {
90
+ // If edit flag is set, copy to temp file and open in editor
108
91
  const fileToRead = await this.editFile(flags.file);
109
92
  xanoscript = fs.readFileSync(fileToRead, 'utf8');
110
93
  // Clean up temp file
@@ -116,7 +99,12 @@ Service created successfully!
116
99
  }
117
100
  }
118
101
  else {
119
- xanoscript = await this.fetchContent(flags.file);
102
+ try {
103
+ xanoscript = fs.readFileSync(flags.file, 'utf8');
104
+ }
105
+ catch (error) {
106
+ this.error(`Failed to read file '${flags.file}': ${error}`);
107
+ }
120
108
  }
121
109
  }
122
110
  else if (flags.stdin) {
@@ -131,22 +119,29 @@ Service created successfully!
131
119
  else {
132
120
  this.error('Either --file or --stdin must be specified to provide XanoScript code');
133
121
  }
134
- // Validate xanoscript is not empty
135
- if (!xanoscript || xanoscript.trim().length === 0) {
122
+ // Validate xanoscript is not empty (only if not using URL)
123
+ if (!xanoscriptUrl && (!xanoscript || xanoscript.trim().length === 0)) {
136
124
  this.error('XanoScript content is empty');
137
125
  }
138
126
  // Construct the API URL
139
- const apiUrl = `${profile.instance_origin}/api:meta/beta/workspace/${workspaceId}/ephemeral/service`;
127
+ const apiUrl = `${profile.instance_origin}/api:meta/beta/ephemeral/service`;
128
+ // Build request body
129
+ const formData = new FormData();
130
+ if (xanoscriptUrl) {
131
+ formData.append('doc', xanoscriptUrl);
132
+ }
133
+ else {
134
+ formData.append('doc', xanoscript);
135
+ }
140
136
  // Run ephemeral service via API
141
137
  try {
142
138
  const response = await fetch(apiUrl, {
143
139
  method: 'POST',
144
140
  headers: {
145
141
  'accept': 'application/json',
146
- 'Content-Type': 'text/x-xanoscript',
147
142
  'Authorization': `Bearer ${profile.access_token}`,
148
143
  },
149
- body: xanoscript,
144
+ body: formData,
150
145
  });
151
146
  if (!response.ok) {
152
147
  const errorText = await response.text();
@@ -182,6 +177,11 @@ Service created successfully!
182
177
  }
183
178
  }
184
179
  }
180
+ this.log('');
181
+ }
182
+ if (result.result.metadata_api) {
183
+ this.log(' Metadata API:');
184
+ this.log(` ${result.result.metadata_api.url}`);
185
185
  }
186
186
  }
187
187
  }
@@ -244,6 +244,9 @@ Service created successfully!
244
244
  }
245
245
  return tmpFile;
246
246
  }
247
+ isUrl(str) {
248
+ return str.startsWith('http://') || str.startsWith('https://');
249
+ }
247
250
  async readStdin() {
248
251
  return new Promise((resolve, reject) => {
249
252
  const chunks = [];
@@ -260,31 +263,6 @@ Service created successfully!
260
263
  process.stdin.resume();
261
264
  });
262
265
  }
263
- isUrl(str) {
264
- return str.startsWith('http://') || str.startsWith('https://');
265
- }
266
- async fetchContent(source) {
267
- if (this.isUrl(source)) {
268
- try {
269
- const response = await fetch(source);
270
- if (!response.ok) {
271
- this.error(`Failed to fetch '${source}': ${response.status} ${response.statusText}`);
272
- }
273
- return await response.text();
274
- }
275
- catch (error) {
276
- this.error(`Failed to fetch '${source}': ${error}`);
277
- }
278
- }
279
- else {
280
- try {
281
- return fs.readFileSync(source, 'utf8');
282
- }
283
- catch (error) {
284
- this.error(`Failed to read file '${source}': ${error}`);
285
- }
286
- }
287
- }
288
266
  loadCredentials() {
289
267
  const configDir = path.join(os.homedir(), '.xano');
290
268
  const credentialsPath = path.join(configDir, 'credentials.yaml');
@@ -1,7 +1,7 @@
1
- import { Command } from '@oclif/core';
2
- export default class ProfileEdit extends Command {
1
+ import BaseCommand from '../../../base-command.js';
2
+ export default class ProfileEdit extends BaseCommand {
3
3
  static args: {
4
- name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
4
+ name: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
5
5
  };
6
6
  static flags: {
7
7
  account_origin: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -11,6 +11,7 @@ export default class ProfileEdit extends Command {
11
11
  branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
12
  'remove-workspace': import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
13
  'remove-branch': import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
15
  };
15
16
  static description: string;
16
17
  static examples: string[];
@@ -1,16 +1,18 @@
1
- import { Args, Command, Flags } from '@oclif/core';
1
+ import { Args, Flags } from '@oclif/core';
2
2
  import * as fs from 'node:fs';
3
3
  import * as os from 'node:os';
4
4
  import * as path from 'node:path';
5
5
  import * as yaml from 'js-yaml';
6
- export default class ProfileEdit extends Command {
6
+ import BaseCommand from '../../../base-command.js';
7
+ export default class ProfileEdit extends BaseCommand {
7
8
  static args = {
8
9
  name: Args.string({
9
- description: 'Profile name to edit',
10
- required: true,
10
+ description: 'Profile name to edit (uses default profile if not specified)',
11
+ required: false,
11
12
  }),
12
13
  };
13
14
  static flags = {
15
+ ...BaseCommand.baseFlags,
14
16
  account_origin: Flags.string({
15
17
  char: 'a',
16
18
  description: 'Update account origin URL',
@@ -49,21 +51,26 @@ export default class ProfileEdit extends Command {
49
51
  };
50
52
  static description = 'Edit an existing profile configuration';
51
53
  static examples = [
54
+ `$ xano profile:edit --access_token new_token123
55
+ Profile 'default' updated successfully at ~/.xano/credentials.yaml
56
+ `,
52
57
  `$ xano profile:edit production --access_token new_token123
53
58
  Profile 'production' updated successfully at ~/.xano/credentials.yaml
54
59
  `,
55
60
  `$ xano profile:edit staging -i https://new-staging-instance.xano.com -t new_token456
56
61
  Profile 'staging' updated successfully at ~/.xano/credentials.yaml
57
62
  `,
58
- `$ xano profile:edit dev -w new-workspace -b new-branch
59
- Profile 'dev' updated successfully at ~/.xano/credentials.yaml
63
+ `$ xano profile:edit -b new-branch
64
+ Profile 'default' updated successfully at ~/.xano/credentials.yaml
60
65
  `,
61
- `$ xano profile:edit dev --remove-workspace
62
- Profile 'dev' updated successfully at ~/.xano/credentials.yaml
66
+ `$ xano profile:edit --remove-branch
67
+ Profile 'default' updated successfully at ~/.xano/credentials.yaml
63
68
  `,
64
69
  ];
65
70
  async run() {
66
71
  const { args, flags } = await this.parse(ProfileEdit);
72
+ // Use provided name or default profile
73
+ const profileName = args.name || this.getDefaultProfile();
67
74
  const configDir = path.join(os.homedir(), '.xano');
68
75
  const credentialsPath = path.join(configDir, 'credentials.yaml');
69
76
  // Check if credentials file exists
@@ -84,11 +91,11 @@ Profile 'dev' updated successfully at ~/.xano/credentials.yaml
84
91
  this.error(`Failed to parse credentials file: ${error}`);
85
92
  }
86
93
  // Check if profile exists
87
- if (!(args.name in credentials.profiles)) {
88
- this.error(`Profile '${args.name}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}`);
94
+ if (!(profileName in credentials.profiles)) {
95
+ this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}`);
89
96
  }
90
97
  // Get the existing profile
91
- const existingProfile = credentials.profiles[args.name];
98
+ const existingProfile = credentials.profiles[profileName];
92
99
  // Check if any flags were provided
93
100
  const hasFlags = flags.account_origin || flags.instance_origin || flags.access_token ||
94
101
  flags.workspace || flags.branch || flags['remove-workspace'] || flags['remove-branch'];
@@ -111,7 +118,7 @@ Profile 'dev' updated successfully at ~/.xano/credentials.yaml
111
118
  if (flags['remove-branch']) {
112
119
  delete updatedProfile.branch;
113
120
  }
114
- credentials.profiles[args.name] = updatedProfile;
121
+ credentials.profiles[profileName] = updatedProfile;
115
122
  // Write the updated credentials back to the file
116
123
  try {
117
124
  const yamlContent = yaml.dump(credentials, {
@@ -120,7 +127,7 @@ Profile 'dev' updated successfully at ~/.xano/credentials.yaml
120
127
  noRefs: true,
121
128
  });
122
129
  fs.writeFileSync(credentialsPath, yamlContent, 'utf8');
123
- this.log(`Profile '${args.name}' updated successfully at ${credentialsPath}`);
130
+ this.log(`Profile '${profileName}' updated successfully at ${credentialsPath}`);
124
131
  }
125
132
  catch (error) {
126
133
  this.error(`Failed to write credentials file: ${error}`);
@@ -0,0 +1,12 @@
1
+ import BaseCommand from '../../../base-command.js';
2
+ export default class ProfileMe extends BaseCommand {
3
+ static flags: {
4
+ output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
5
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
6
+ };
7
+ static description: string;
8
+ static examples: string[];
9
+ run(): Promise<void>;
10
+ private loadCredentials;
11
+ private formatKey;
12
+ }