@xano/cli 0.0.2
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 +1200 -0
- package/bin/dev.cmd +3 -0
- package/bin/dev.js +5 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +5 -0
- package/dist/base-command.d.ts +11 -0
- package/dist/base-command.js +40 -0
- package/dist/commands/ephemeral/run/job/index.d.ts +19 -0
- package/dist/commands/ephemeral/run/job/index.js +318 -0
- package/dist/commands/ephemeral/run/service/index.d.ts +18 -0
- package/dist/commands/ephemeral/run/service/index.js +286 -0
- package/dist/commands/function/create/index.d.ts +18 -0
- package/dist/commands/function/create/index.js +280 -0
- package/dist/commands/function/edit/index.d.ts +24 -0
- package/dist/commands/function/edit/index.js +482 -0
- package/dist/commands/function/get/index.d.ts +18 -0
- package/dist/commands/function/get/index.js +279 -0
- package/dist/commands/function/list/index.d.ts +19 -0
- package/dist/commands/function/list/index.js +208 -0
- package/dist/commands/profile/create/index.d.ts +17 -0
- package/dist/commands/profile/create/index.js +123 -0
- package/dist/commands/profile/delete/index.d.ts +14 -0
- package/dist/commands/profile/delete/index.js +124 -0
- package/dist/commands/profile/edit/index.d.ts +18 -0
- package/dist/commands/profile/edit/index.js +129 -0
- package/dist/commands/profile/get-default/index.d.ts +6 -0
- package/dist/commands/profile/get-default/index.js +44 -0
- package/dist/commands/profile/list/index.d.ts +10 -0
- package/dist/commands/profile/list/index.js +115 -0
- package/dist/commands/profile/set-default/index.d.ts +9 -0
- package/dist/commands/profile/set-default/index.js +63 -0
- package/dist/commands/profile/wizard/index.d.ts +15 -0
- package/dist/commands/profile/wizard/index.js +350 -0
- package/dist/commands/static_host/build/create/index.d.ts +18 -0
- package/dist/commands/static_host/build/create/index.js +194 -0
- package/dist/commands/static_host/build/get/index.d.ts +16 -0
- package/dist/commands/static_host/build/get/index.js +165 -0
- package/dist/commands/static_host/build/list/index.d.ts +17 -0
- package/dist/commands/static_host/build/list/index.js +192 -0
- package/dist/commands/static_host/list/index.d.ts +15 -0
- package/dist/commands/static_host/list/index.js +187 -0
- package/dist/commands/workspace/list/index.d.ts +11 -0
- package/dist/commands/workspace/list/index.js +154 -0
- package/dist/help.d.ts +20 -0
- package/dist/help.js +26 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/oclif.manifest.json +1370 -0
- package/package.json +79 -0
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import { execSync } from 'node:child_process';
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import * as os from 'node:os';
|
|
5
|
+
import * as path from 'node:path';
|
|
6
|
+
import * as yaml from 'js-yaml';
|
|
7
|
+
import inquirer from 'inquirer';
|
|
8
|
+
import BaseCommand from '../../../base-command.js';
|
|
9
|
+
export default class FunctionEdit extends BaseCommand {
|
|
10
|
+
static args = {
|
|
11
|
+
function_id: Args.string({
|
|
12
|
+
description: 'Function ID to edit',
|
|
13
|
+
required: false,
|
|
14
|
+
}),
|
|
15
|
+
};
|
|
16
|
+
static flags = {
|
|
17
|
+
...BaseCommand.baseFlags,
|
|
18
|
+
workspace: Flags.string({
|
|
19
|
+
char: 'w',
|
|
20
|
+
description: 'Workspace ID (optional if set in profile)',
|
|
21
|
+
required: false,
|
|
22
|
+
}),
|
|
23
|
+
file: Flags.string({
|
|
24
|
+
char: 'f',
|
|
25
|
+
description: 'Path to file containing XanoScript code',
|
|
26
|
+
required: false,
|
|
27
|
+
exclusive: ['stdin'],
|
|
28
|
+
}),
|
|
29
|
+
stdin: Flags.boolean({
|
|
30
|
+
char: 's',
|
|
31
|
+
description: 'Read XanoScript code from stdin',
|
|
32
|
+
required: false,
|
|
33
|
+
default: false,
|
|
34
|
+
exclusive: ['file'],
|
|
35
|
+
}),
|
|
36
|
+
edit: Flags.boolean({
|
|
37
|
+
char: 'e',
|
|
38
|
+
description: 'Open file in editor before updating function (requires --file)',
|
|
39
|
+
required: false,
|
|
40
|
+
default: false,
|
|
41
|
+
}),
|
|
42
|
+
publish: Flags.boolean({
|
|
43
|
+
description: 'Publish the function after editing',
|
|
44
|
+
required: false,
|
|
45
|
+
default: true,
|
|
46
|
+
}),
|
|
47
|
+
output: Flags.string({
|
|
48
|
+
char: 'o',
|
|
49
|
+
description: 'Output format',
|
|
50
|
+
required: false,
|
|
51
|
+
default: 'summary',
|
|
52
|
+
options: ['summary', 'json'],
|
|
53
|
+
}),
|
|
54
|
+
};
|
|
55
|
+
static description = 'Edit a function in a workspace';
|
|
56
|
+
static examples = [
|
|
57
|
+
`$ xano function:edit 163
|
|
58
|
+
# Fetches the function code and opens it in $EDITOR for editing
|
|
59
|
+
Function updated successfully!
|
|
60
|
+
ID: 163
|
|
61
|
+
Name: my_function
|
|
62
|
+
`,
|
|
63
|
+
`$ xano function:edit
|
|
64
|
+
# Prompts for function, fetches the code and opens it in $EDITOR for editing
|
|
65
|
+
Select a function to edit:
|
|
66
|
+
❯ my_function (ID: 163) - Sample function
|
|
67
|
+
another-func (ID: 164)
|
|
68
|
+
`,
|
|
69
|
+
`$ xano function:edit 163 -f function.xs
|
|
70
|
+
Function updated successfully!
|
|
71
|
+
ID: 163
|
|
72
|
+
Name: my_function
|
|
73
|
+
`,
|
|
74
|
+
`$ xano function:edit 163 -w 40 -f function.xs
|
|
75
|
+
Function updated successfully!
|
|
76
|
+
ID: 163
|
|
77
|
+
Name: my_function
|
|
78
|
+
`,
|
|
79
|
+
`$ xano function:edit -f function.xs
|
|
80
|
+
Select a function to edit:
|
|
81
|
+
❯ my_function (ID: 163) - Sample function
|
|
82
|
+
another-func (ID: 164)
|
|
83
|
+
`,
|
|
84
|
+
`$ xano function:edit 163 -f function.xs --edit
|
|
85
|
+
# Opens function.xs in $EDITOR, then updates function with edited content
|
|
86
|
+
Function updated successfully!
|
|
87
|
+
ID: 163
|
|
88
|
+
Name: my_function
|
|
89
|
+
`,
|
|
90
|
+
`$ cat function.xs | xano function:edit 163 --stdin
|
|
91
|
+
Function updated successfully!
|
|
92
|
+
ID: 163
|
|
93
|
+
Name: my_function
|
|
94
|
+
`,
|
|
95
|
+
`$ xano function:edit 163 -f function.xs -o json
|
|
96
|
+
{
|
|
97
|
+
"id": 163,
|
|
98
|
+
"name": "my_function",
|
|
99
|
+
...
|
|
100
|
+
}
|
|
101
|
+
`,
|
|
102
|
+
];
|
|
103
|
+
async run() {
|
|
104
|
+
const { args, flags } = await this.parse(FunctionEdit);
|
|
105
|
+
// Get profile name (default or from flag/env)
|
|
106
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
107
|
+
// Load credentials
|
|
108
|
+
const credentials = this.loadCredentials();
|
|
109
|
+
// Get the profile configuration
|
|
110
|
+
if (!(profileName in credentials.profiles)) {
|
|
111
|
+
this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
|
|
112
|
+
`Create a profile using 'xano profile:create'`);
|
|
113
|
+
}
|
|
114
|
+
const profile = credentials.profiles[profileName];
|
|
115
|
+
// Validate required fields
|
|
116
|
+
if (!profile.instance_origin) {
|
|
117
|
+
this.error(`Profile '${profileName}' is missing instance_origin`);
|
|
118
|
+
}
|
|
119
|
+
if (!profile.access_token) {
|
|
120
|
+
this.error(`Profile '${profileName}' is missing access_token`);
|
|
121
|
+
}
|
|
122
|
+
// Validate flag combinations
|
|
123
|
+
if (flags.edit && !flags.file) {
|
|
124
|
+
this.error('The --edit flag requires --file to be specified');
|
|
125
|
+
}
|
|
126
|
+
// Determine workspace_id from flag or profile
|
|
127
|
+
let workspaceId;
|
|
128
|
+
if (flags.workspace) {
|
|
129
|
+
workspaceId = flags.workspace;
|
|
130
|
+
}
|
|
131
|
+
else if (profile.workspace) {
|
|
132
|
+
workspaceId = profile.workspace;
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
this.error(`Workspace ID is required. Either:\n` +
|
|
136
|
+
` 1. Provide it as a flag: xano function:edit [function_id] -w <workspace_id>\n` +
|
|
137
|
+
` 2. Set it in your profile using: xano profile:edit ${profileName} -w <workspace_id>`);
|
|
138
|
+
}
|
|
139
|
+
// If function_id is not provided, prompt user to select from list
|
|
140
|
+
let functionId;
|
|
141
|
+
if (args.function_id) {
|
|
142
|
+
functionId = args.function_id;
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
functionId = await this.promptForFunctionId(profile, workspaceId);
|
|
146
|
+
}
|
|
147
|
+
// Read XanoScript content
|
|
148
|
+
let xanoscript;
|
|
149
|
+
if (flags.file) {
|
|
150
|
+
// Read from file
|
|
151
|
+
let fileToRead = flags.file;
|
|
152
|
+
// If edit flag is set, copy to temp file and open in editor
|
|
153
|
+
if (flags.edit) {
|
|
154
|
+
fileToRead = await this.editFile(flags.file);
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
xanoscript = fs.readFileSync(fileToRead, 'utf8');
|
|
158
|
+
// Clean up temp file if it was created
|
|
159
|
+
if (flags.edit && fileToRead !== flags.file) {
|
|
160
|
+
try {
|
|
161
|
+
fs.unlinkSync(fileToRead);
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
// Ignore cleanup errors
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
this.error(`Failed to read file '${fileToRead}': ${error}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
else if (flags.stdin) {
|
|
173
|
+
// Read from stdin
|
|
174
|
+
try {
|
|
175
|
+
xanoscript = await this.readStdin();
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
this.error(`Failed to read from stdin: ${error}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
// Default: Fetch from API and open in editor
|
|
183
|
+
try {
|
|
184
|
+
xanoscript = await this.fetchFunctionCode(profile, workspaceId, functionId);
|
|
185
|
+
// Automatically open in editor when fetching
|
|
186
|
+
xanoscript = await this.editFunctionContent(xanoscript);
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
this.error(`Failed to fetch function: ${error}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// Validate xanoscript is not empty
|
|
193
|
+
if (!xanoscript || xanoscript.trim().length === 0) {
|
|
194
|
+
this.error('XanoScript content is empty');
|
|
195
|
+
}
|
|
196
|
+
// Construct the API URL
|
|
197
|
+
const queryParams = new URLSearchParams({
|
|
198
|
+
publish: flags.publish ? 'true' : 'false',
|
|
199
|
+
include_xanoscript: 'false',
|
|
200
|
+
});
|
|
201
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/function/${functionId}?${queryParams.toString()}`;
|
|
202
|
+
// Update function via API
|
|
203
|
+
try {
|
|
204
|
+
const response = await fetch(apiUrl, {
|
|
205
|
+
method: 'PUT',
|
|
206
|
+
headers: {
|
|
207
|
+
'accept': 'application/json',
|
|
208
|
+
'Content-Type': 'text/x-xanoscript',
|
|
209
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
210
|
+
},
|
|
211
|
+
body: xanoscript,
|
|
212
|
+
});
|
|
213
|
+
if (!response.ok) {
|
|
214
|
+
const errorText = await response.text();
|
|
215
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
216
|
+
}
|
|
217
|
+
const result = await response.json();
|
|
218
|
+
// Validate response
|
|
219
|
+
if (!result || typeof result !== 'object') {
|
|
220
|
+
this.error('Unexpected API response format');
|
|
221
|
+
}
|
|
222
|
+
// Output results
|
|
223
|
+
if (flags.output === 'json') {
|
|
224
|
+
this.log(JSON.stringify(result, null, 2));
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
// summary format
|
|
228
|
+
this.log('Function updated successfully!');
|
|
229
|
+
this.log(`ID: ${result.id}`);
|
|
230
|
+
this.log(`Name: ${result.name}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
catch (error) {
|
|
234
|
+
if (error instanceof Error) {
|
|
235
|
+
this.error(`Failed to update function: ${error.message}`);
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
this.error(`Failed to update function: ${String(error)}`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
async fetchFunctionCode(profile, workspaceId, functionId) {
|
|
243
|
+
const queryParams = new URLSearchParams({
|
|
244
|
+
include_xanoscript: 'true',
|
|
245
|
+
});
|
|
246
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/function/${functionId}?${queryParams.toString()}`;
|
|
247
|
+
try {
|
|
248
|
+
const response = await fetch(apiUrl, {
|
|
249
|
+
method: 'GET',
|
|
250
|
+
headers: {
|
|
251
|
+
'accept': 'application/json',
|
|
252
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
if (!response.ok) {
|
|
256
|
+
const errorText = await response.text();
|
|
257
|
+
throw new Error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
258
|
+
}
|
|
259
|
+
const result = await response.json();
|
|
260
|
+
// Handle xanoscript as an object with status and value
|
|
261
|
+
if (result.xanoscript) {
|
|
262
|
+
if (result.xanoscript.status === 'ok' && result.xanoscript.value !== undefined) {
|
|
263
|
+
return result.xanoscript.value;
|
|
264
|
+
}
|
|
265
|
+
else if (typeof result.xanoscript === 'string') {
|
|
266
|
+
return result.xanoscript;
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
throw new Error(`Invalid xanoscript format: ${JSON.stringify(result.xanoscript)}`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return '';
|
|
273
|
+
}
|
|
274
|
+
catch (error) {
|
|
275
|
+
throw error;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
async editFunctionContent(xanoscript) {
|
|
279
|
+
// Get the EDITOR environment variable
|
|
280
|
+
const editor = process.env.EDITOR || process.env.VISUAL;
|
|
281
|
+
if (!editor) {
|
|
282
|
+
throw new Error('No editor configured. Please set the EDITOR or VISUAL environment variable. Example: export EDITOR=vim');
|
|
283
|
+
}
|
|
284
|
+
// Validate editor executable exists
|
|
285
|
+
try {
|
|
286
|
+
execSync(`which ${editor.split(' ')[0]}`, { stdio: 'ignore' });
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
throw new Error(`Editor '${editor}' not found. Please set EDITOR to a valid editor. Example: export EDITOR=vim`);
|
|
290
|
+
}
|
|
291
|
+
// Create a temporary file with .xs extension
|
|
292
|
+
const tmpFile = path.join(os.tmpdir(), `xano-edit-${Date.now()}.xs`);
|
|
293
|
+
// Write content to temp file
|
|
294
|
+
try {
|
|
295
|
+
fs.writeFileSync(tmpFile, xanoscript, 'utf8');
|
|
296
|
+
}
|
|
297
|
+
catch (error) {
|
|
298
|
+
throw new Error(`Failed to create temporary file: ${error}`);
|
|
299
|
+
}
|
|
300
|
+
// Open the editor
|
|
301
|
+
try {
|
|
302
|
+
execSync(`${editor} "${tmpFile}"`, { stdio: 'inherit' });
|
|
303
|
+
}
|
|
304
|
+
catch (error) {
|
|
305
|
+
// Clean up temp file
|
|
306
|
+
try {
|
|
307
|
+
fs.unlinkSync(tmpFile);
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
// Ignore cleanup errors
|
|
311
|
+
}
|
|
312
|
+
throw new Error(`Editor exited with an error: ${error}`);
|
|
313
|
+
}
|
|
314
|
+
// Read the edited content
|
|
315
|
+
try {
|
|
316
|
+
const editedContent = fs.readFileSync(tmpFile, 'utf8');
|
|
317
|
+
// Clean up temp file
|
|
318
|
+
try {
|
|
319
|
+
fs.unlinkSync(tmpFile);
|
|
320
|
+
}
|
|
321
|
+
catch {
|
|
322
|
+
// Ignore cleanup errors
|
|
323
|
+
}
|
|
324
|
+
return editedContent;
|
|
325
|
+
}
|
|
326
|
+
catch (error) {
|
|
327
|
+
throw new Error(`Failed to read edited file: ${error}`);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
async editFile(filePath) {
|
|
331
|
+
// Get the EDITOR environment variable
|
|
332
|
+
const editor = process.env.EDITOR || process.env.VISUAL;
|
|
333
|
+
if (!editor) {
|
|
334
|
+
this.error('No editor configured. Please set the EDITOR or VISUAL environment variable.\n' +
|
|
335
|
+
'Example: export EDITOR=vim');
|
|
336
|
+
}
|
|
337
|
+
// Validate editor executable exists
|
|
338
|
+
try {
|
|
339
|
+
execSync(`which ${editor.split(' ')[0]}`, { stdio: 'ignore' });
|
|
340
|
+
}
|
|
341
|
+
catch {
|
|
342
|
+
this.error(`Editor '${editor}' not found. Please set EDITOR to a valid editor.\n` +
|
|
343
|
+
'Example: export EDITOR=vim');
|
|
344
|
+
}
|
|
345
|
+
// Read the original file
|
|
346
|
+
let originalContent;
|
|
347
|
+
try {
|
|
348
|
+
originalContent = fs.readFileSync(filePath, 'utf8');
|
|
349
|
+
}
|
|
350
|
+
catch (error) {
|
|
351
|
+
this.error(`Failed to read file '${filePath}': ${error}`);
|
|
352
|
+
}
|
|
353
|
+
// Create a temporary file with the same extension
|
|
354
|
+
const ext = path.extname(filePath);
|
|
355
|
+
const tmpFile = path.join(os.tmpdir(), `xano-edit-${Date.now()}${ext}`);
|
|
356
|
+
// Copy content to temp file
|
|
357
|
+
try {
|
|
358
|
+
fs.writeFileSync(tmpFile, originalContent, 'utf8');
|
|
359
|
+
}
|
|
360
|
+
catch (error) {
|
|
361
|
+
this.error(`Failed to create temporary file: ${error}`);
|
|
362
|
+
}
|
|
363
|
+
// Open the editor
|
|
364
|
+
try {
|
|
365
|
+
execSync(`${editor} ${tmpFile}`, { stdio: 'inherit' });
|
|
366
|
+
}
|
|
367
|
+
catch (error) {
|
|
368
|
+
// Clean up temp file
|
|
369
|
+
try {
|
|
370
|
+
fs.unlinkSync(tmpFile);
|
|
371
|
+
}
|
|
372
|
+
catch {
|
|
373
|
+
// Ignore cleanup errors
|
|
374
|
+
}
|
|
375
|
+
this.error(`Editor exited with an error: ${error}`);
|
|
376
|
+
}
|
|
377
|
+
return tmpFile;
|
|
378
|
+
}
|
|
379
|
+
async readStdin() {
|
|
380
|
+
return new Promise((resolve, reject) => {
|
|
381
|
+
const chunks = [];
|
|
382
|
+
process.stdin.on('data', (chunk) => {
|
|
383
|
+
chunks.push(chunk);
|
|
384
|
+
});
|
|
385
|
+
process.stdin.on('end', () => {
|
|
386
|
+
resolve(Buffer.concat(chunks).toString('utf8'));
|
|
387
|
+
});
|
|
388
|
+
process.stdin.on('error', (error) => {
|
|
389
|
+
reject(error);
|
|
390
|
+
});
|
|
391
|
+
// Resume stdin if it was paused
|
|
392
|
+
process.stdin.resume();
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
async promptForFunctionId(profile, workspaceId) {
|
|
396
|
+
try {
|
|
397
|
+
// Fetch list of functions
|
|
398
|
+
const queryParams = new URLSearchParams({
|
|
399
|
+
include_draft: 'false',
|
|
400
|
+
include_xanoscript: 'false',
|
|
401
|
+
page: '1',
|
|
402
|
+
per_page: '50',
|
|
403
|
+
sort: 'created_at',
|
|
404
|
+
order: 'desc',
|
|
405
|
+
});
|
|
406
|
+
const listUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/function?${queryParams.toString()}`;
|
|
407
|
+
const response = await fetch(listUrl, {
|
|
408
|
+
method: 'GET',
|
|
409
|
+
headers: {
|
|
410
|
+
'accept': 'application/json',
|
|
411
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
412
|
+
},
|
|
413
|
+
});
|
|
414
|
+
if (!response.ok) {
|
|
415
|
+
const errorText = await response.text();
|
|
416
|
+
this.error(`Failed to fetch function list: ${response.status} ${response.statusText}\n${errorText}`);
|
|
417
|
+
}
|
|
418
|
+
const data = await response.json();
|
|
419
|
+
// Handle different response formats
|
|
420
|
+
let functions;
|
|
421
|
+
if (Array.isArray(data)) {
|
|
422
|
+
functions = data;
|
|
423
|
+
}
|
|
424
|
+
else if (data && typeof data === 'object' && 'functions' in data && Array.isArray(data.functions)) {
|
|
425
|
+
functions = data.functions;
|
|
426
|
+
}
|
|
427
|
+
else if (data && typeof data === 'object' && 'items' in data && Array.isArray(data.items)) {
|
|
428
|
+
functions = data.items;
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
this.error('Unexpected API response format');
|
|
432
|
+
}
|
|
433
|
+
if (functions.length === 0) {
|
|
434
|
+
this.error('No functions found in workspace');
|
|
435
|
+
}
|
|
436
|
+
// Create choices for inquirer
|
|
437
|
+
const choices = functions.map(func => ({
|
|
438
|
+
name: `${func.name} (ID: ${func.id})${func.description ? ` - ${func.description}` : ''}`,
|
|
439
|
+
value: func.id.toString(),
|
|
440
|
+
}));
|
|
441
|
+
// Prompt user to select a function
|
|
442
|
+
const answer = await inquirer.prompt([
|
|
443
|
+
{
|
|
444
|
+
type: 'list',
|
|
445
|
+
name: 'functionId',
|
|
446
|
+
message: 'Select a function to edit:',
|
|
447
|
+
choices,
|
|
448
|
+
},
|
|
449
|
+
]);
|
|
450
|
+
return answer.functionId;
|
|
451
|
+
}
|
|
452
|
+
catch (error) {
|
|
453
|
+
if (error instanceof Error) {
|
|
454
|
+
this.error(`Failed to prompt for function: ${error.message}`);
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
this.error(`Failed to prompt for function: ${String(error)}`);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
loadCredentials() {
|
|
462
|
+
const configDir = path.join(os.homedir(), '.xano');
|
|
463
|
+
const credentialsPath = path.join(configDir, 'credentials.yaml');
|
|
464
|
+
// Check if credentials file exists
|
|
465
|
+
if (!fs.existsSync(credentialsPath)) {
|
|
466
|
+
this.error(`Credentials file not found at ${credentialsPath}\n` +
|
|
467
|
+
`Create a profile using 'xano profile:create'`);
|
|
468
|
+
}
|
|
469
|
+
// Read credentials file
|
|
470
|
+
try {
|
|
471
|
+
const fileContent = fs.readFileSync(credentialsPath, 'utf8');
|
|
472
|
+
const parsed = yaml.load(fileContent);
|
|
473
|
+
if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
|
|
474
|
+
this.error('Credentials file has invalid format.');
|
|
475
|
+
}
|
|
476
|
+
return parsed;
|
|
477
|
+
}
|
|
478
|
+
catch (error) {
|
|
479
|
+
this.error(`Failed to parse credentials file: ${error}`);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import BaseCommand from '../../../base-command.js';
|
|
2
|
+
export default class FunctionGet extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
function_id: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static flags: {
|
|
7
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
include_draft: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
include_xanoscript: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
};
|
|
13
|
+
static description: string;
|
|
14
|
+
static examples: string[];
|
|
15
|
+
run(): Promise<void>;
|
|
16
|
+
private promptForFunctionId;
|
|
17
|
+
private loadCredentials;
|
|
18
|
+
}
|