@xano/cli 0.0.65 → 0.0.67
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 +29 -0
- package/dist/base-command.d.ts +25 -0
- package/dist/base-command.js +53 -11
- package/dist/commands/auth/index.d.ts +2 -0
- package/dist/commands/auth/index.js +44 -22
- package/dist/commands/function/edit/index.js +17 -18
- package/dist/commands/function/get/index.js +11 -11
- package/dist/commands/profile/create/index.d.ts +1 -0
- package/dist/commands/profile/create/index.js +10 -0
- package/dist/commands/profile/edit/index.d.ts +2 -0
- package/dist/commands/profile/edit/index.js +23 -1
- package/dist/commands/profile/list/index.js +3 -0
- package/dist/commands/profile/wizard/index.d.ts +2 -0
- package/dist/commands/profile/wizard/index.js +23 -12
- package/dist/commands/profile/workspace/set/index.d.ts +11 -0
- package/dist/commands/profile/workspace/set/index.js +87 -0
- package/dist/commands/release/export/index.js +14 -13
- package/dist/commands/tenant/backup/export/index.js +4 -2
- package/dist/commands/workspace/git/pull/index.js +2 -2
- package/oclif.manifest.json +1368 -1281
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,6 +36,7 @@ npm install -g @xano/cli
|
|
|
36
36
|
# Interactive browser-based authentication
|
|
37
37
|
xano auth
|
|
38
38
|
xano auth --origin https://custom.xano.com
|
|
39
|
+
xano auth --insecure # Skip TLS verification (self-signed certs)
|
|
39
40
|
```
|
|
40
41
|
|
|
41
42
|
### Profiles
|
|
@@ -48,6 +49,7 @@ xano profile wizard
|
|
|
48
49
|
|
|
49
50
|
# Create a profile manually
|
|
50
51
|
xano profile create myprofile -i https://instance.xano.com -t <access_token>
|
|
52
|
+
xano profile create myprofile -i https://self-signed.example.com -t <token> --insecure
|
|
51
53
|
|
|
52
54
|
# List profiles
|
|
53
55
|
xano profile list
|
|
@@ -59,6 +61,8 @@ xano profile set myprofile
|
|
|
59
61
|
|
|
60
62
|
# Edit a profile
|
|
61
63
|
xano profile edit myprofile -w 123
|
|
64
|
+
xano profile edit myprofile --insecure # Enable insecure mode (self-signed certs)
|
|
65
|
+
xano profile edit myprofile --remove-insecure # Disable insecure mode
|
|
62
66
|
xano profile edit myprofile --remove-branch # Remove branch from profile
|
|
63
67
|
|
|
64
68
|
# Get current user info
|
|
@@ -70,6 +74,10 @@ xano profile token
|
|
|
70
74
|
# Print workspace ID (useful for piping)
|
|
71
75
|
xano profile workspace
|
|
72
76
|
|
|
77
|
+
# Interactively change the workspace on a profile
|
|
78
|
+
xano profile workspace set
|
|
79
|
+
xano profile workspace set -p production
|
|
80
|
+
|
|
73
81
|
# Delete a profile
|
|
74
82
|
xano profile delete myprofile
|
|
75
83
|
```
|
|
@@ -467,9 +475,30 @@ profiles:
|
|
|
467
475
|
access_token: <token>
|
|
468
476
|
workspace: <workspace_id>
|
|
469
477
|
branch: <branch_id>
|
|
478
|
+
self-hosted:
|
|
479
|
+
instance_origin: https://self-signed.example.com
|
|
480
|
+
access_token: <token>
|
|
481
|
+
insecure: true
|
|
470
482
|
default: default
|
|
471
483
|
```
|
|
472
484
|
|
|
485
|
+
### Self-Signed Certificates
|
|
486
|
+
|
|
487
|
+
For environments using self-signed TLS certificates, use the `--insecure` (`-k`) flag to skip certificate verification:
|
|
488
|
+
|
|
489
|
+
```bash
|
|
490
|
+
# During authentication
|
|
491
|
+
xano auth --insecure
|
|
492
|
+
|
|
493
|
+
# When creating a profile
|
|
494
|
+
xano profile create myprofile -i https://self-signed.example.com -t <token> -k
|
|
495
|
+
|
|
496
|
+
# Add to an existing profile
|
|
497
|
+
xano profile edit myprofile --insecure
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
When a profile has `insecure: true`, all commands using that profile will automatically skip TLS certificate verification. A warning is displayed when insecure mode is active.
|
|
501
|
+
|
|
473
502
|
### Update
|
|
474
503
|
|
|
475
504
|
```bash
|
package/dist/base-command.d.ts
CHANGED
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
|
+
export interface ProfileConfig {
|
|
3
|
+
access_token: string;
|
|
4
|
+
account_origin?: string;
|
|
5
|
+
branch?: string;
|
|
6
|
+
insecure?: boolean;
|
|
7
|
+
instance_origin: string;
|
|
8
|
+
workspace?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface CredentialsFile {
|
|
11
|
+
default?: string;
|
|
12
|
+
profiles: {
|
|
13
|
+
[key: string]: ProfileConfig;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export declare function buildUserAgent(version: string): string;
|
|
2
17
|
export default abstract class BaseCommand extends Command {
|
|
3
18
|
static baseFlags: {
|
|
4
19
|
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -8,8 +23,18 @@ export default abstract class BaseCommand extends Command {
|
|
|
8
23
|
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
24
|
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
25
|
};
|
|
26
|
+
init(): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Apply insecure TLS mode if the active profile has insecure: true.
|
|
29
|
+
* Sets NODE_TLS_REJECT_UNAUTHORIZED=0 so all fetch() calls skip cert verification.
|
|
30
|
+
*/
|
|
31
|
+
protected applyInsecureFromProfile(): void;
|
|
11
32
|
protected getDefaultProfile(): string;
|
|
12
33
|
protected getProfile(): string | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Load and parse the credentials file. Returns null if the file doesn't exist.
|
|
36
|
+
*/
|
|
37
|
+
protected loadCredentialsFile(): CredentialsFile | null;
|
|
13
38
|
/**
|
|
14
39
|
* Make an HTTP request with optional verbose logging.
|
|
15
40
|
* Use this for all Metadata API calls to support the --verbose flag.
|
package/dist/base-command.js
CHANGED
|
@@ -3,6 +3,9 @@ import * as yaml from 'js-yaml';
|
|
|
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
|
+
export function buildUserAgent(version) {
|
|
7
|
+
return `xano-cli/${version} (${process.platform}; ${process.arch}) node/${process.version}`;
|
|
8
|
+
}
|
|
6
9
|
export default class BaseCommand extends Command {
|
|
7
10
|
static baseFlags = {
|
|
8
11
|
profile: Flags.string({
|
|
@@ -21,18 +24,36 @@ export default class BaseCommand extends Command {
|
|
|
21
24
|
};
|
|
22
25
|
// Override the flags property to include baseFlags
|
|
23
26
|
static flags = BaseCommand.baseFlags;
|
|
27
|
+
async init() {
|
|
28
|
+
await super.init();
|
|
29
|
+
this.applyInsecureFromProfile();
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Apply insecure TLS mode if the active profile has insecure: true.
|
|
33
|
+
* Sets NODE_TLS_REJECT_UNAUTHORIZED=0 so all fetch() calls skip cert verification.
|
|
34
|
+
*/
|
|
35
|
+
applyInsecureFromProfile() {
|
|
36
|
+
try {
|
|
37
|
+
const profileName = this.flags?.profile || this.getDefaultProfile();
|
|
38
|
+
const credentials = this.loadCredentialsFile();
|
|
39
|
+
if (!credentials)
|
|
40
|
+
return;
|
|
41
|
+
const profile = credentials.profiles[profileName];
|
|
42
|
+
if (profile?.insecure) {
|
|
43
|
+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
44
|
+
this.warn('TLS certificate verification is disabled for this profile (insecure mode)');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// Don't fail the command if we can't read the profile for insecure check
|
|
49
|
+
}
|
|
50
|
+
}
|
|
24
51
|
// Helper method to get the default profile from credentials file
|
|
25
52
|
getDefaultProfile() {
|
|
26
53
|
try {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return 'default';
|
|
31
|
-
}
|
|
32
|
-
const fileContent = fs.readFileSync(credentialsPath, 'utf8');
|
|
33
|
-
const parsed = yaml.load(fileContent);
|
|
34
|
-
if (parsed && typeof parsed === 'object' && 'default' in parsed && parsed.default) {
|
|
35
|
-
return parsed.default;
|
|
54
|
+
const credentials = this.loadCredentialsFile();
|
|
55
|
+
if (credentials?.default) {
|
|
56
|
+
return credentials.default;
|
|
36
57
|
}
|
|
37
58
|
return 'default';
|
|
38
59
|
}
|
|
@@ -44,13 +65,34 @@ export default class BaseCommand extends Command {
|
|
|
44
65
|
getProfile() {
|
|
45
66
|
return this.flags?.profile;
|
|
46
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Load and parse the credentials file. Returns null if the file doesn't exist.
|
|
70
|
+
*/
|
|
71
|
+
loadCredentialsFile() {
|
|
72
|
+
const configDir = path.join(os.homedir(), '.xano');
|
|
73
|
+
const credentialsPath = path.join(configDir, 'credentials.yaml');
|
|
74
|
+
if (!fs.existsSync(credentialsPath)) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
const fileContent = fs.readFileSync(credentialsPath, 'utf8');
|
|
78
|
+
const parsed = yaml.load(fileContent);
|
|
79
|
+
if (parsed && typeof parsed === 'object' && 'profiles' in parsed) {
|
|
80
|
+
return parsed;
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
47
84
|
/**
|
|
48
85
|
* Make an HTTP request with optional verbose logging.
|
|
49
86
|
* Use this for all Metadata API calls to support the --verbose flag.
|
|
50
87
|
*/
|
|
51
88
|
async verboseFetch(url, options, verbose, authToken) {
|
|
52
89
|
const method = options.method || 'GET';
|
|
53
|
-
const
|
|
90
|
+
const headers = {
|
|
91
|
+
'User-Agent': buildUserAgent(this.config.version),
|
|
92
|
+
...options.headers,
|
|
93
|
+
};
|
|
94
|
+
const fetchOptions = { ...options, headers };
|
|
95
|
+
const contentType = headers['Content-Type'] || 'application/json';
|
|
54
96
|
if (verbose) {
|
|
55
97
|
this.log('');
|
|
56
98
|
this.log('─'.repeat(60));
|
|
@@ -66,7 +108,7 @@ export default class BaseCommand extends Command {
|
|
|
66
108
|
}
|
|
67
109
|
}
|
|
68
110
|
const startTime = Date.now();
|
|
69
|
-
const response = await fetch(url,
|
|
111
|
+
const response = await fetch(url, fetchOptions);
|
|
70
112
|
const elapsed = Date.now() - startTime;
|
|
71
113
|
if (verbose) {
|
|
72
114
|
this.log(`← ${response.status} ${response.statusText} (${elapsed}ms)`);
|
|
@@ -3,9 +3,11 @@ export default class Auth extends Command {
|
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static flags: {
|
|
6
|
+
insecure: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
6
7
|
origin: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
8
|
};
|
|
8
9
|
run(): Promise<void>;
|
|
10
|
+
private getHeaders;
|
|
9
11
|
private fetchBranches;
|
|
10
12
|
private fetchInstances;
|
|
11
13
|
private fetchWorkspaces;
|
|
@@ -7,6 +7,7 @@ import * as http from 'node:http';
|
|
|
7
7
|
import * as os from 'node:os';
|
|
8
8
|
import { join } from 'node:path';
|
|
9
9
|
import open from 'open';
|
|
10
|
+
import { buildUserAgent } from '../../base-command.js';
|
|
10
11
|
const AUTH_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
|
|
11
12
|
export default class Auth extends Command {
|
|
12
13
|
static description = 'Authenticate with Xano via browser login';
|
|
@@ -22,6 +23,11 @@ Profile 'default' created successfully!`,
|
|
|
22
23
|
Opening browser for Xano login at https://custom.xano.com...`,
|
|
23
24
|
];
|
|
24
25
|
static flags = {
|
|
26
|
+
insecure: Flags.boolean({
|
|
27
|
+
char: 'k',
|
|
28
|
+
default: false,
|
|
29
|
+
description: 'Skip TLS certificate verification (for self-signed certificates)',
|
|
30
|
+
}),
|
|
25
31
|
origin: Flags.string({
|
|
26
32
|
char: 'o',
|
|
27
33
|
default: 'https://app.xano.com',
|
|
@@ -30,23 +36,42 @@ Opening browser for Xano login at https://custom.xano.com...`,
|
|
|
30
36
|
};
|
|
31
37
|
async run() {
|
|
32
38
|
const { flags } = await this.parse(Auth);
|
|
39
|
+
if (flags.insecure) {
|
|
40
|
+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
41
|
+
this.warn('TLS certificate verification is disabled (insecure mode)');
|
|
42
|
+
}
|
|
33
43
|
try {
|
|
34
44
|
// Step 1: Get token via browser auth
|
|
35
45
|
this.log('Starting authentication flow...');
|
|
36
46
|
const token = await this.startAuthServer(flags.origin);
|
|
47
|
+
this.log(`Received token: ${token}`);
|
|
37
48
|
// Step 2: Validate token and get user info
|
|
38
49
|
this.log('');
|
|
39
50
|
this.log('Validating authentication...');
|
|
40
51
|
const user = await this.validateToken(token, flags.origin);
|
|
41
52
|
this.log(`Authenticated as ${user.name} (${user.email})`);
|
|
42
53
|
// Step 3: Fetch and select instance
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
// Self-hosted instances (non-default origin) don't have /api:meta/instance
|
|
55
|
+
// The origin itself IS the instance
|
|
56
|
+
const isSelfHosted = !/^https:\/\/app\.(.*\.)?xano\.com$/.test(flags.origin);
|
|
57
|
+
let instance;
|
|
58
|
+
if (isSelfHosted) {
|
|
59
|
+
instance = {
|
|
60
|
+
display: flags.origin,
|
|
61
|
+
id: 'self-hosted',
|
|
62
|
+
name: 'self-hosted',
|
|
63
|
+
origin: flags.origin,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
this.log('');
|
|
68
|
+
this.log('Fetching available instances...');
|
|
69
|
+
const instances = await this.fetchInstances(token, flags.origin);
|
|
70
|
+
if (instances.length === 0) {
|
|
71
|
+
this.error('No instances found. Please check your account.');
|
|
72
|
+
}
|
|
73
|
+
instance = await this.selectInstance(instances);
|
|
48
74
|
}
|
|
49
|
-
const instance = await this.selectInstance(instances);
|
|
50
75
|
// Step 4: Workspace selection
|
|
51
76
|
let workspace;
|
|
52
77
|
let branch;
|
|
@@ -73,6 +98,7 @@ Opening browser for Xano login at https://custom.xano.com...`,
|
|
|
73
98
|
access_token: token,
|
|
74
99
|
account_origin: flags.origin,
|
|
75
100
|
branch,
|
|
101
|
+
...(flags.insecure && { insecure: true }),
|
|
76
102
|
instance_origin: instance.origin,
|
|
77
103
|
name: profileName,
|
|
78
104
|
workspace: workspace?.id,
|
|
@@ -90,13 +116,17 @@ Opening browser for Xano login at https://custom.xano.com...`,
|
|
|
90
116
|
throw error;
|
|
91
117
|
}
|
|
92
118
|
}
|
|
119
|
+
getHeaders(accessToken) {
|
|
120
|
+
return {
|
|
121
|
+
'User-Agent': buildUserAgent(this.config.version),
|
|
122
|
+
accept: 'application/json',
|
|
123
|
+
...(accessToken && { Authorization: `Bearer ${accessToken}` }),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
93
126
|
async fetchBranches(accessToken, origin, workspaceId) {
|
|
94
127
|
try {
|
|
95
128
|
const response = await fetch(`${origin}/api:meta/workspace/${workspaceId}/branch`, {
|
|
96
|
-
headers:
|
|
97
|
-
accept: 'application/json',
|
|
98
|
-
Authorization: `Bearer ${accessToken}`,
|
|
99
|
-
},
|
|
129
|
+
headers: this.getHeaders(accessToken),
|
|
100
130
|
method: 'GET',
|
|
101
131
|
});
|
|
102
132
|
if (!response.ok) {
|
|
@@ -117,10 +147,7 @@ Opening browser for Xano login at https://custom.xano.com...`,
|
|
|
117
147
|
}
|
|
118
148
|
async fetchInstances(accessToken, origin) {
|
|
119
149
|
const response = await fetch(`${origin}/api:meta/instance`, {
|
|
120
|
-
headers:
|
|
121
|
-
accept: 'application/json',
|
|
122
|
-
Authorization: `Bearer ${accessToken}`,
|
|
123
|
-
},
|
|
150
|
+
headers: this.getHeaders(accessToken),
|
|
124
151
|
method: 'GET',
|
|
125
152
|
});
|
|
126
153
|
if (!response.ok) {
|
|
@@ -143,10 +170,7 @@ Opening browser for Xano login at https://custom.xano.com...`,
|
|
|
143
170
|
async fetchWorkspaces(accessToken, origin) {
|
|
144
171
|
try {
|
|
145
172
|
const response = await fetch(`${origin}/api:meta/workspace`, {
|
|
146
|
-
headers:
|
|
147
|
-
accept: 'application/json',
|
|
148
|
-
Authorization: `Bearer ${accessToken}`,
|
|
149
|
-
},
|
|
173
|
+
headers: this.getHeaders(accessToken),
|
|
150
174
|
method: 'GET',
|
|
151
175
|
});
|
|
152
176
|
if (!response.ok) {
|
|
@@ -211,6 +235,7 @@ Opening browser for Xano login at https://custom.xano.com...`,
|
|
|
211
235
|
instance_origin: profile.instance_origin,
|
|
212
236
|
...(profile.workspace && { workspace: profile.workspace }),
|
|
213
237
|
...(profile.branch && { branch: profile.branch }),
|
|
238
|
+
...(profile.insecure && { insecure: true }),
|
|
214
239
|
};
|
|
215
240
|
// Set as default profile
|
|
216
241
|
credentials.default = profile.name;
|
|
@@ -464,10 +489,7 @@ Opening browser for Xano login at https://custom.xano.com...`,
|
|
|
464
489
|
}
|
|
465
490
|
async validateToken(token, origin) {
|
|
466
491
|
const response = await fetch(`${origin}/api:meta/auth/me`, {
|
|
467
|
-
headers:
|
|
468
|
-
accept: 'application/json',
|
|
469
|
-
Authorization: `Bearer ${token}`,
|
|
470
|
-
},
|
|
492
|
+
headers: this.getHeaders(token),
|
|
471
493
|
method: 'GET',
|
|
472
494
|
});
|
|
473
495
|
if (!response.ok) {
|
|
@@ -5,7 +5,7 @@ import { execSync } from 'node:child_process';
|
|
|
5
5
|
import * as fs from 'node:fs';
|
|
6
6
|
import * as os from 'node:os';
|
|
7
7
|
import * as path from 'node:path';
|
|
8
|
-
import BaseCommand from '../../../base-command.js';
|
|
8
|
+
import BaseCommand, { buildUserAgent } from '../../../base-command.js';
|
|
9
9
|
export default class FunctionEdit extends BaseCommand {
|
|
10
10
|
static args = {
|
|
11
11
|
function_id: Args.string({
|
|
@@ -138,7 +138,7 @@ Name: my_function
|
|
|
138
138
|
}
|
|
139
139
|
// If function_id is not provided, prompt user to select from list
|
|
140
140
|
let functionId;
|
|
141
|
-
functionId = args.function_id ? args.function_id :
|
|
141
|
+
functionId = args.function_id ? args.function_id : await this.promptForFunctionId(profile, workspaceId);
|
|
142
142
|
// Read XanoScript content
|
|
143
143
|
let xanoscript;
|
|
144
144
|
if (flags.file) {
|
|
@@ -199,8 +199,8 @@ Name: my_function
|
|
|
199
199
|
const response = await this.verboseFetch(apiUrl, {
|
|
200
200
|
body: xanoscript,
|
|
201
201
|
headers: {
|
|
202
|
-
|
|
203
|
-
|
|
202
|
+
accept: 'application/json',
|
|
203
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
204
204
|
'Content-Type': 'text/x-xanoscript',
|
|
205
205
|
},
|
|
206
206
|
method: 'PUT',
|
|
@@ -209,7 +209,7 @@ Name: my_function
|
|
|
209
209
|
const errorText = await response.text();
|
|
210
210
|
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
211
211
|
}
|
|
212
|
-
const result = await response.json();
|
|
212
|
+
const result = (await response.json());
|
|
213
213
|
// Validate response
|
|
214
214
|
if (!result || typeof result !== 'object') {
|
|
215
215
|
this.error('Unexpected API response format');
|
|
@@ -238,16 +238,14 @@ Name: my_function
|
|
|
238
238
|
// Get the EDITOR environment variable
|
|
239
239
|
const editor = process.env.EDITOR || process.env.VISUAL;
|
|
240
240
|
if (!editor) {
|
|
241
|
-
this.error('No editor configured. Please set the EDITOR or VISUAL environment variable.\n' +
|
|
242
|
-
'Example: export EDITOR=vim');
|
|
241
|
+
this.error('No editor configured. Please set the EDITOR or VISUAL environment variable.\n' + 'Example: export EDITOR=vim');
|
|
243
242
|
}
|
|
244
243
|
// Validate editor executable exists
|
|
245
244
|
try {
|
|
246
245
|
execSync(`which ${editor.split(' ')[0]}`, { stdio: 'ignore' });
|
|
247
246
|
}
|
|
248
247
|
catch {
|
|
249
|
-
this.error(`Editor '${editor}' not found. Please set EDITOR to a valid editor.\n` +
|
|
250
|
-
'Example: export EDITOR=vim');
|
|
248
|
+
this.error(`Editor '${editor}' not found. Please set EDITOR to a valid editor.\n` + 'Example: export EDITOR=vim');
|
|
251
249
|
}
|
|
252
250
|
// Read the original file
|
|
253
251
|
let originalContent;
|
|
@@ -343,8 +341,9 @@ Name: my_function
|
|
|
343
341
|
try {
|
|
344
342
|
const response = await fetch(apiUrl, {
|
|
345
343
|
headers: {
|
|
346
|
-
'
|
|
347
|
-
'
|
|
344
|
+
'User-Agent': buildUserAgent(this.config.version),
|
|
345
|
+
accept: 'application/json',
|
|
346
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
348
347
|
},
|
|
349
348
|
method: 'GET',
|
|
350
349
|
});
|
|
@@ -352,7 +351,7 @@ Name: my_function
|
|
|
352
351
|
const errorText = await response.text();
|
|
353
352
|
throw new Error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
354
353
|
}
|
|
355
|
-
const result = await response.json();
|
|
354
|
+
const result = (await response.json());
|
|
356
355
|
// Handle xanoscript as an object with status and value
|
|
357
356
|
if (result.xanoscript) {
|
|
358
357
|
if (result.xanoscript.status === 'ok' && result.xanoscript.value !== undefined) {
|
|
@@ -374,8 +373,7 @@ Name: my_function
|
|
|
374
373
|
const credentialsPath = path.join(configDir, 'credentials.yaml');
|
|
375
374
|
// Check if credentials file exists
|
|
376
375
|
if (!fs.existsSync(credentialsPath)) {
|
|
377
|
-
this.error(`Credentials file not found at ${credentialsPath}\n` +
|
|
378
|
-
`Create a profile using 'xano profile:create'`);
|
|
376
|
+
this.error(`Credentials file not found at ${credentialsPath}\n` + `Create a profile using 'xano profile:create'`);
|
|
379
377
|
}
|
|
380
378
|
// Read credentials file
|
|
381
379
|
try {
|
|
@@ -404,8 +402,9 @@ Name: my_function
|
|
|
404
402
|
const listUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/function?${queryParams.toString()}`;
|
|
405
403
|
const response = await fetch(listUrl, {
|
|
406
404
|
headers: {
|
|
407
|
-
'
|
|
408
|
-
'
|
|
405
|
+
'User-Agent': buildUserAgent(this.config.version),
|
|
406
|
+
accept: 'application/json',
|
|
407
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
409
408
|
},
|
|
410
409
|
method: 'GET',
|
|
411
410
|
});
|
|
@@ -413,7 +412,7 @@ Name: my_function
|
|
|
413
412
|
const errorText = await response.text();
|
|
414
413
|
this.error(`Failed to fetch function list: ${response.status} ${response.statusText}\n${errorText}`);
|
|
415
414
|
}
|
|
416
|
-
const data = await response.json();
|
|
415
|
+
const data = (await response.json());
|
|
417
416
|
// Handle different response formats
|
|
418
417
|
let functions;
|
|
419
418
|
if (Array.isArray(data)) {
|
|
@@ -432,7 +431,7 @@ Name: my_function
|
|
|
432
431
|
this.error('No functions found in workspace');
|
|
433
432
|
}
|
|
434
433
|
// Create choices for inquirer
|
|
435
|
-
const choices = functions.map(func => ({
|
|
434
|
+
const choices = functions.map((func) => ({
|
|
436
435
|
name: `${func.name} (ID: ${func.id})${func.description ? ` - ${func.description}` : ''}`,
|
|
437
436
|
value: func.id.toString(),
|
|
438
437
|
}));
|
|
@@ -4,7 +4,7 @@ import * as yaml from 'js-yaml';
|
|
|
4
4
|
import * as fs from 'node:fs';
|
|
5
5
|
import * as os from 'node:os';
|
|
6
6
|
import * as path from 'node:path';
|
|
7
|
-
import BaseCommand from '../../../base-command.js';
|
|
7
|
+
import BaseCommand, { buildUserAgent } from '../../../base-command.js';
|
|
8
8
|
export default class FunctionGet extends BaseCommand {
|
|
9
9
|
static args = {
|
|
10
10
|
function_id: Args.string({
|
|
@@ -110,7 +110,7 @@ function yo {
|
|
|
110
110
|
}
|
|
111
111
|
// If function_id is not provided, prompt user to select from list
|
|
112
112
|
let functionId;
|
|
113
|
-
functionId = args.function_id ? args.function_id :
|
|
113
|
+
functionId = args.function_id ? args.function_id : await this.promptForFunctionId(profile, workspaceId);
|
|
114
114
|
// Build query parameters
|
|
115
115
|
// Automatically set include_xanoscript to true if output format is xs
|
|
116
116
|
const includeXanoscript = flags.output === 'xs' ? true : flags.include_xanoscript;
|
|
@@ -124,8 +124,8 @@ function yo {
|
|
|
124
124
|
try {
|
|
125
125
|
const response = await this.verboseFetch(apiUrl, {
|
|
126
126
|
headers: {
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
accept: 'application/json',
|
|
128
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
129
129
|
},
|
|
130
130
|
method: 'GET',
|
|
131
131
|
}, flags.verbose, profile.access_token);
|
|
@@ -133,7 +133,7 @@ function yo {
|
|
|
133
133
|
const errorText = await response.text();
|
|
134
134
|
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
135
135
|
}
|
|
136
|
-
const func = await response.json();
|
|
136
|
+
const func = (await response.json());
|
|
137
137
|
// Validate response is an object
|
|
138
138
|
if (!func || typeof func !== 'object') {
|
|
139
139
|
this.error('Unexpected API response format: expected a function object');
|
|
@@ -189,8 +189,7 @@ function yo {
|
|
|
189
189
|
const credentialsPath = path.join(configDir, 'credentials.yaml');
|
|
190
190
|
// Check if credentials file exists
|
|
191
191
|
if (!fs.existsSync(credentialsPath)) {
|
|
192
|
-
this.error(`Credentials file not found at ${credentialsPath}\n` +
|
|
193
|
-
`Create a profile using 'xano profile:create'`);
|
|
192
|
+
this.error(`Credentials file not found at ${credentialsPath}\n` + `Create a profile using 'xano profile:create'`);
|
|
194
193
|
}
|
|
195
194
|
// Read credentials file
|
|
196
195
|
try {
|
|
@@ -219,8 +218,9 @@ function yo {
|
|
|
219
218
|
const listUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/function?${queryParams.toString()}`;
|
|
220
219
|
const response = await fetch(listUrl, {
|
|
221
220
|
headers: {
|
|
222
|
-
'
|
|
223
|
-
'
|
|
221
|
+
'User-Agent': buildUserAgent(this.config.version),
|
|
222
|
+
accept: 'application/json',
|
|
223
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
224
224
|
},
|
|
225
225
|
method: 'GET',
|
|
226
226
|
});
|
|
@@ -228,7 +228,7 @@ function yo {
|
|
|
228
228
|
const errorText = await response.text();
|
|
229
229
|
this.error(`Failed to fetch function list: ${response.status} ${response.statusText}\n${errorText}`);
|
|
230
230
|
}
|
|
231
|
-
const data = await response.json();
|
|
231
|
+
const data = (await response.json());
|
|
232
232
|
// Handle different response formats
|
|
233
233
|
let functions;
|
|
234
234
|
if (Array.isArray(data)) {
|
|
@@ -247,7 +247,7 @@ function yo {
|
|
|
247
247
|
this.error('No functions found in workspace');
|
|
248
248
|
}
|
|
249
249
|
// Create choices for inquirer
|
|
250
|
-
const choices = functions.map(func => ({
|
|
250
|
+
const choices = functions.map((func) => ({
|
|
251
251
|
name: `${func.name} (ID: ${func.id})${func.description ? ` - ${func.description}` : ''}`,
|
|
252
252
|
value: func.id.toString(),
|
|
253
253
|
}));
|
|
@@ -10,6 +10,7 @@ export default class ProfileCreate extends Command {
|
|
|
10
10
|
account_origin: 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
|
default: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
insecure: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
14
|
instance_origin: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
15
|
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
16
|
};
|
|
@@ -24,6 +24,9 @@ Profile 'dev' created successfully at ~/.xano/credentials.yaml
|
|
|
24
24
|
`$ xano profile:create production --account_origin https://account.xano.com --instance_origin https://instance.xano.com --access_token token123 --default
|
|
25
25
|
Profile 'production' created successfully at ~/.xano/credentials.yaml
|
|
26
26
|
Default profile set to 'production'
|
|
27
|
+
`,
|
|
28
|
+
`$ xano profile:create selfhosted -i https://self-signed.example.com -t token123 --insecure
|
|
29
|
+
Profile 'selfhosted' created successfully at ~/.xano/credentials.yaml
|
|
27
30
|
`,
|
|
28
31
|
];
|
|
29
32
|
static flags = {
|
|
@@ -47,6 +50,12 @@ Default profile set to 'production'
|
|
|
47
50
|
description: 'Set this profile as the default',
|
|
48
51
|
required: false,
|
|
49
52
|
}),
|
|
53
|
+
insecure: Flags.boolean({
|
|
54
|
+
char: 'k',
|
|
55
|
+
default: false,
|
|
56
|
+
description: 'Skip TLS certificate verification (for self-signed certificates)',
|
|
57
|
+
required: false,
|
|
58
|
+
}),
|
|
50
59
|
instance_origin: Flags.string({
|
|
51
60
|
char: 'i',
|
|
52
61
|
description: 'Instance origin URL',
|
|
@@ -93,6 +102,7 @@ Default profile set to 'production'
|
|
|
93
102
|
instance_origin: flags.instance_origin,
|
|
94
103
|
...(flags.workspace && { workspace: flags.workspace }),
|
|
95
104
|
...(flags.branch && { branch: flags.branch }),
|
|
105
|
+
...(flags.insecure && { insecure: true }),
|
|
96
106
|
};
|
|
97
107
|
// Set default if flag is provided
|
|
98
108
|
if (flags.default) {
|
|
@@ -9,8 +9,10 @@ export default class ProfileEdit extends BaseCommand {
|
|
|
9
9
|
access_token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
10
|
account_origin: 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
|
+
insecure: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
13
|
instance_origin: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
14
|
'remove-branch': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
|
+
'remove-insecure': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
16
|
'remove-workspace': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
17
|
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
18
|
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|