@xano/cli 0.0.10 → 0.0.11

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.
@@ -14,8 +14,7 @@ export default class EphemeralRunJob extends BaseCommand {
14
14
  static examples: string[];
15
15
  run(): Promise<void>;
16
16
  private editFile;
17
- private readStdin;
18
17
  private isUrl;
19
- private fetchContent;
18
+ private readStdin;
20
19
  private loadCredentials;
21
20
  }
@@ -110,11 +110,16 @@ Job executed successfully!
110
110
  ` 1. Provide it as a flag: xano ephemeral:run:job -w <workspace_id>\n` +
111
111
  ` 2. Set it in your profile using: xano profile:edit ${profileName} -w <workspace_id>`);
112
112
  }
113
- // Read XanoScript content
113
+ // Read XanoScript content or use URL
114
114
  let xanoscript;
115
+ let xanoscriptUrl;
115
116
  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)) {
117
+ if (this.isUrl(flags.file)) {
118
+ // Pass URL directly to API
119
+ xanoscriptUrl = flags.file;
120
+ }
121
+ else if (flags.edit) {
122
+ // If edit flag is set, copy to temp file and open in editor
118
123
  const fileToRead = await this.editFile(flags.file);
119
124
  xanoscript = fs.readFileSync(fileToRead, 'utf8');
120
125
  // Clean up temp file
@@ -126,7 +131,12 @@ Job executed successfully!
126
131
  }
127
132
  }
128
133
  else {
129
- xanoscript = await this.fetchContent(flags.file);
134
+ try {
135
+ xanoscript = fs.readFileSync(flags.file, 'utf8');
136
+ }
137
+ catch (error) {
138
+ this.error(`Failed to read file '${flags.file}': ${error}`);
139
+ }
130
140
  }
131
141
  }
132
142
  else if (flags.stdin) {
@@ -141,45 +151,53 @@ Job executed successfully!
141
151
  else {
142
152
  this.error('Either --file or --stdin must be specified to provide XanoScript code');
143
153
  }
144
- // Validate xanoscript is not empty
145
- if (!xanoscript || xanoscript.trim().length === 0) {
154
+ // Validate xanoscript is not empty (only if not using URL)
155
+ if (!xanoscriptUrl && (!xanoscript || xanoscript.trim().length === 0)) {
146
156
  this.error('XanoScript content is empty');
147
157
  }
148
- // Load args from JSON file or URL if provided
158
+ // Load args from JSON file/URL if provided
149
159
  let inputArgs;
160
+ let inputArgsUrl;
150
161
  if (flags.args) {
151
- try {
152
- const argsContent = await this.fetchContent(flags.args);
153
- inputArgs = JSON.parse(argsContent);
162
+ if (this.isUrl(flags.args)) {
163
+ // Pass URL directly to API
164
+ inputArgsUrl = flags.args;
154
165
  }
155
- catch (error) {
156
- this.error(`Failed to read or parse args '${flags.args}': ${error}`);
166
+ else {
167
+ try {
168
+ const argsContent = fs.readFileSync(flags.args, 'utf8');
169
+ inputArgs = JSON.parse(argsContent);
170
+ }
171
+ catch (error) {
172
+ this.error(`Failed to read or parse args '${flags.args}': ${error}`);
173
+ }
157
174
  }
158
175
  }
159
176
  // Construct the API URL
160
177
  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;
178
+ // Build request body
164
179
  const formData = new FormData();
165
- formData.append('doc', xanoscript);
166
- if (inputArgs) {
180
+ if (xanoscriptUrl) {
181
+ formData.append('doc', xanoscriptUrl);
182
+ }
183
+ else {
184
+ formData.append('doc', xanoscript);
185
+ }
186
+ if (inputArgsUrl) {
187
+ formData.append('args', inputArgsUrl);
188
+ }
189
+ else if (inputArgs) {
167
190
  formData.append('args', JSON.stringify(inputArgs));
168
191
  }
169
- requestBody = formData;
170
- contentType = ''; // Let fetch set the boundary
192
+ const requestBody = formData;
171
193
  // Run ephemeral job via API
172
194
  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
195
  const response = await fetch(apiUrl, {
181
196
  method: 'POST',
182
- headers,
197
+ headers: {
198
+ 'accept': 'application/json',
199
+ 'Authorization': `Bearer ${profile.access_token}`,
200
+ },
183
201
  body: requestBody,
184
202
  });
185
203
  if (!response.ok) {
@@ -272,6 +290,9 @@ Job executed successfully!
272
290
  }
273
291
  return tmpFile;
274
292
  }
293
+ isUrl(str) {
294
+ return str.startsWith('http://') || str.startsWith('https://');
295
+ }
275
296
  async readStdin() {
276
297
  return new Promise((resolve, reject) => {
277
298
  const chunks = [];
@@ -288,31 +309,6 @@ Job executed successfully!
288
309
  process.stdin.resume();
289
310
  });
290
311
  }
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
312
  loadCredentials() {
317
313
  const configDir = path.join(os.homedir(), '.xano');
318
314
  const credentialsPath = path.join(configDir, 'credentials.yaml');
@@ -13,8 +13,7 @@ export default class EphemeralRunService extends BaseCommand {
13
13
  static examples: string[];
14
14
  run(): Promise<void>;
15
15
  private editFile;
16
- private readStdin;
17
16
  private isUrl;
18
- private fetchContent;
17
+ private readStdin;
19
18
  private loadCredentials;
20
19
  }
@@ -100,11 +100,16 @@ Service created successfully!
100
100
  ` 1. Provide it as a flag: xano ephemeral:run:service -w <workspace_id>\n` +
101
101
  ` 2. Set it in your profile using: xano profile:edit ${profileName} -w <workspace_id>`);
102
102
  }
103
- // Read XanoScript content
103
+ // Read XanoScript content or use URL
104
104
  let xanoscript;
105
+ let xanoscriptUrl;
105
106
  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)) {
107
+ if (this.isUrl(flags.file)) {
108
+ // Pass URL directly to API
109
+ xanoscriptUrl = flags.file;
110
+ }
111
+ else if (flags.edit) {
112
+ // If edit flag is set, copy to temp file and open in editor
108
113
  const fileToRead = await this.editFile(flags.file);
109
114
  xanoscript = fs.readFileSync(fileToRead, 'utf8');
110
115
  // Clean up temp file
@@ -116,7 +121,12 @@ Service created successfully!
116
121
  }
117
122
  }
118
123
  else {
119
- xanoscript = await this.fetchContent(flags.file);
124
+ try {
125
+ xanoscript = fs.readFileSync(flags.file, 'utf8');
126
+ }
127
+ catch (error) {
128
+ this.error(`Failed to read file '${flags.file}': ${error}`);
129
+ }
120
130
  }
121
131
  }
122
132
  else if (flags.stdin) {
@@ -131,22 +141,29 @@ Service created successfully!
131
141
  else {
132
142
  this.error('Either --file or --stdin must be specified to provide XanoScript code');
133
143
  }
134
- // Validate xanoscript is not empty
135
- if (!xanoscript || xanoscript.trim().length === 0) {
144
+ // Validate xanoscript is not empty (only if not using URL)
145
+ if (!xanoscriptUrl && (!xanoscript || xanoscript.trim().length === 0)) {
136
146
  this.error('XanoScript content is empty');
137
147
  }
138
148
  // Construct the API URL
139
149
  const apiUrl = `${profile.instance_origin}/api:meta/beta/workspace/${workspaceId}/ephemeral/service`;
150
+ // Build request body
151
+ const formData = new FormData();
152
+ if (xanoscriptUrl) {
153
+ formData.append('doc', xanoscriptUrl);
154
+ }
155
+ else {
156
+ formData.append('doc', xanoscript);
157
+ }
140
158
  // Run ephemeral service via API
141
159
  try {
142
160
  const response = await fetch(apiUrl, {
143
161
  method: 'POST',
144
162
  headers: {
145
163
  'accept': 'application/json',
146
- 'Content-Type': 'text/x-xanoscript',
147
164
  'Authorization': `Bearer ${profile.access_token}`,
148
165
  },
149
- body: xanoscript,
166
+ body: formData,
150
167
  });
151
168
  if (!response.ok) {
152
169
  const errorText = await response.text();
@@ -182,6 +199,11 @@ Service created successfully!
182
199
  }
183
200
  }
184
201
  }
202
+ this.log('');
203
+ }
204
+ if (result.result.metadata_api) {
205
+ this.log(' Metadata API:');
206
+ this.log(` ${result.result.metadata_api.url}`);
185
207
  }
186
208
  }
187
209
  }
@@ -244,6 +266,9 @@ Service created successfully!
244
266
  }
245
267
  return tmpFile;
246
268
  }
269
+ isUrl(str) {
270
+ return str.startsWith('http://') || str.startsWith('https://');
271
+ }
247
272
  async readStdin() {
248
273
  return new Promise((resolve, reject) => {
249
274
  const chunks = [];
@@ -260,31 +285,6 @@ Service created successfully!
260
285
  process.stdin.resume();
261
286
  });
262
287
  }
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
288
  loadCredentials() {
289
289
  const configDir = path.join(os.homedir(), '.xano');
290
290
  const credentialsPath = path.join(configDir, 'credentials.yaml');