@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,165 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as os from 'node:os';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import * as yaml from 'js-yaml';
|
|
6
|
+
import BaseCommand from '../../../../base-command.js';
|
|
7
|
+
export default class StaticHostBuildGet extends BaseCommand {
|
|
8
|
+
static args = {
|
|
9
|
+
static_host: Args.string({
|
|
10
|
+
description: 'Static Host name',
|
|
11
|
+
required: true,
|
|
12
|
+
}),
|
|
13
|
+
build_id: Args.string({
|
|
14
|
+
description: 'Build ID',
|
|
15
|
+
required: true,
|
|
16
|
+
}),
|
|
17
|
+
};
|
|
18
|
+
static flags = {
|
|
19
|
+
...BaseCommand.baseFlags,
|
|
20
|
+
workspace: Flags.string({
|
|
21
|
+
char: 'w',
|
|
22
|
+
description: 'Workspace ID (optional if set in profile)',
|
|
23
|
+
required: false,
|
|
24
|
+
}),
|
|
25
|
+
output: Flags.string({
|
|
26
|
+
char: 'o',
|
|
27
|
+
description: 'Output format',
|
|
28
|
+
required: false,
|
|
29
|
+
default: 'summary',
|
|
30
|
+
options: ['summary', 'json'],
|
|
31
|
+
}),
|
|
32
|
+
};
|
|
33
|
+
static description = 'Get details of a specific build for a static host';
|
|
34
|
+
static examples = [
|
|
35
|
+
`$ xano static_host:build:get default 52
|
|
36
|
+
Build Details:
|
|
37
|
+
ID: 52
|
|
38
|
+
Name: v1.0.0
|
|
39
|
+
Status: completed
|
|
40
|
+
`,
|
|
41
|
+
`$ xano static_host:build:get default 52 -w 40
|
|
42
|
+
Build Details:
|
|
43
|
+
ID: 52
|
|
44
|
+
Name: v1.0.0
|
|
45
|
+
Status: completed
|
|
46
|
+
`,
|
|
47
|
+
`$ xano static_host:build:get myhost 123 --profile production
|
|
48
|
+
Build Details:
|
|
49
|
+
ID: 123
|
|
50
|
+
Name: production-build
|
|
51
|
+
`,
|
|
52
|
+
`$ xano static_host:build:get default 52 -o json
|
|
53
|
+
{
|
|
54
|
+
"id": 52,
|
|
55
|
+
"name": "v1.0.0",
|
|
56
|
+
"status": "completed"
|
|
57
|
+
}
|
|
58
|
+
`,
|
|
59
|
+
];
|
|
60
|
+
async run() {
|
|
61
|
+
const { args, flags } = await this.parse(StaticHostBuildGet);
|
|
62
|
+
// Get profile name (default or from flag/env)
|
|
63
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
64
|
+
// Load credentials
|
|
65
|
+
const credentials = this.loadCredentials();
|
|
66
|
+
// Get the profile configuration
|
|
67
|
+
if (!(profileName in credentials.profiles)) {
|
|
68
|
+
this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
|
|
69
|
+
`Create a profile using 'xano profile:create'`);
|
|
70
|
+
}
|
|
71
|
+
const profile = credentials.profiles[profileName];
|
|
72
|
+
// Validate required fields
|
|
73
|
+
if (!profile.instance_origin) {
|
|
74
|
+
this.error(`Profile '${profileName}' is missing instance_origin`);
|
|
75
|
+
}
|
|
76
|
+
if (!profile.access_token) {
|
|
77
|
+
this.error(`Profile '${profileName}' is missing access_token`);
|
|
78
|
+
}
|
|
79
|
+
// Determine workspace_id from flag or profile
|
|
80
|
+
let workspaceId;
|
|
81
|
+
if (flags.workspace) {
|
|
82
|
+
workspaceId = flags.workspace;
|
|
83
|
+
}
|
|
84
|
+
else if (profile.workspace) {
|
|
85
|
+
workspaceId = profile.workspace;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
this.error(`Workspace ID is required. Either:\n` +
|
|
89
|
+
` 1. Provide it as a flag: xano static_host:build:get <static_host> <build_id> -w <workspace_id>\n` +
|
|
90
|
+
` 2. Set it in your profile using: xano profile:edit ${profileName} -w <workspace_id>`);
|
|
91
|
+
}
|
|
92
|
+
// Construct the API URL
|
|
93
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/static_host/${args.static_host}/build/${args.build_id}`;
|
|
94
|
+
// Fetch build from the API
|
|
95
|
+
try {
|
|
96
|
+
const response = await fetch(apiUrl, {
|
|
97
|
+
method: 'GET',
|
|
98
|
+
headers: {
|
|
99
|
+
'accept': 'application/json',
|
|
100
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
if (!response.ok) {
|
|
104
|
+
const errorText = await response.text();
|
|
105
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
106
|
+
}
|
|
107
|
+
const build = await response.json();
|
|
108
|
+
// Validate response
|
|
109
|
+
if (!build || typeof build !== 'object') {
|
|
110
|
+
this.error('Unexpected API response format');
|
|
111
|
+
}
|
|
112
|
+
// Output results
|
|
113
|
+
if (flags.output === 'json') {
|
|
114
|
+
this.log(JSON.stringify(build, null, 2));
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
// summary format
|
|
118
|
+
this.log('Build Details:');
|
|
119
|
+
this.log(`ID: ${build.id}`);
|
|
120
|
+
this.log(`Name: ${build.name}`);
|
|
121
|
+
if (build.description) {
|
|
122
|
+
this.log(`Description: ${build.description}`);
|
|
123
|
+
}
|
|
124
|
+
if (build.status) {
|
|
125
|
+
this.log(`Status: ${build.status}`);
|
|
126
|
+
}
|
|
127
|
+
if (build.created_at) {
|
|
128
|
+
this.log(`Created: ${build.created_at}`);
|
|
129
|
+
}
|
|
130
|
+
if (build.updated_at) {
|
|
131
|
+
this.log(`Updated: ${build.updated_at}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
if (error instanceof Error) {
|
|
137
|
+
this.error(`Failed to fetch build: ${error.message}`);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
this.error(`Failed to fetch build: ${String(error)}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
loadCredentials() {
|
|
145
|
+
const configDir = path.join(os.homedir(), '.xano');
|
|
146
|
+
const credentialsPath = path.join(configDir, 'credentials.yaml');
|
|
147
|
+
// Check if credentials file exists
|
|
148
|
+
if (!fs.existsSync(credentialsPath)) {
|
|
149
|
+
this.error(`Credentials file not found at ${credentialsPath}\n` +
|
|
150
|
+
`Create a profile using 'xano profile:create'`);
|
|
151
|
+
}
|
|
152
|
+
// Read credentials file
|
|
153
|
+
try {
|
|
154
|
+
const fileContent = fs.readFileSync(credentialsPath, 'utf8');
|
|
155
|
+
const parsed = yaml.load(fileContent);
|
|
156
|
+
if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
|
|
157
|
+
this.error('Credentials file has invalid format.');
|
|
158
|
+
}
|
|
159
|
+
return parsed;
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
this.error(`Failed to parse credentials file: ${error}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import BaseCommand from '../../../../base-command.js';
|
|
2
|
+
export default class StaticHostBuildList extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
static_host: import("@oclif/core/interfaces").Arg<string, 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
|
+
page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
per_page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
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 loadCredentials;
|
|
17
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as os from 'node:os';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import * as yaml from 'js-yaml';
|
|
6
|
+
import BaseCommand from '../../../../base-command.js';
|
|
7
|
+
export default class StaticHostBuildList extends BaseCommand {
|
|
8
|
+
static args = {
|
|
9
|
+
static_host: Args.string({
|
|
10
|
+
description: 'Static Host name',
|
|
11
|
+
required: true,
|
|
12
|
+
}),
|
|
13
|
+
};
|
|
14
|
+
static flags = {
|
|
15
|
+
...BaseCommand.baseFlags,
|
|
16
|
+
workspace: Flags.string({
|
|
17
|
+
char: 'w',
|
|
18
|
+
description: 'Workspace ID (optional if set in profile)',
|
|
19
|
+
required: false,
|
|
20
|
+
}),
|
|
21
|
+
output: Flags.string({
|
|
22
|
+
char: 'o',
|
|
23
|
+
description: 'Output format',
|
|
24
|
+
required: false,
|
|
25
|
+
default: 'summary',
|
|
26
|
+
options: ['summary', 'json'],
|
|
27
|
+
}),
|
|
28
|
+
page: Flags.integer({
|
|
29
|
+
description: 'Page number for pagination',
|
|
30
|
+
required: false,
|
|
31
|
+
default: 1,
|
|
32
|
+
}),
|
|
33
|
+
per_page: Flags.integer({
|
|
34
|
+
description: 'Number of results per page',
|
|
35
|
+
required: false,
|
|
36
|
+
default: 50,
|
|
37
|
+
}),
|
|
38
|
+
};
|
|
39
|
+
static description = 'List all builds for a static host';
|
|
40
|
+
static examples = [
|
|
41
|
+
`$ xano static_host:build:list default -w 40
|
|
42
|
+
Available builds:
|
|
43
|
+
- v1.0.0 (ID: 1) - Status: completed
|
|
44
|
+
- v1.0.1 (ID: 2) - Status: pending
|
|
45
|
+
`,
|
|
46
|
+
`$ xano static_host:build:list myhost --profile production
|
|
47
|
+
Available builds:
|
|
48
|
+
- production (ID: 1) - Status: completed
|
|
49
|
+
- staging (ID: 2) - Status: completed
|
|
50
|
+
`,
|
|
51
|
+
`$ xano static_host:build:list default -w 40 --output json
|
|
52
|
+
[
|
|
53
|
+
{
|
|
54
|
+
"id": 1,
|
|
55
|
+
"name": "v1.0.0",
|
|
56
|
+
"status": "completed"
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
`,
|
|
60
|
+
`$ xano static_host:build:list default -p staging -o json --page 2
|
|
61
|
+
[
|
|
62
|
+
{
|
|
63
|
+
"id": 3,
|
|
64
|
+
"name": "v1.0.2"
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
`,
|
|
68
|
+
];
|
|
69
|
+
async run() {
|
|
70
|
+
const { args, flags } = await this.parse(StaticHostBuildList);
|
|
71
|
+
// Get profile name (default or from flag/env)
|
|
72
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
73
|
+
// Load credentials
|
|
74
|
+
const credentials = this.loadCredentials();
|
|
75
|
+
// Get the profile configuration
|
|
76
|
+
if (!(profileName in credentials.profiles)) {
|
|
77
|
+
this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
|
|
78
|
+
`Create a profile using 'xano profile:create'`);
|
|
79
|
+
}
|
|
80
|
+
const profile = credentials.profiles[profileName];
|
|
81
|
+
// Validate required fields
|
|
82
|
+
if (!profile.instance_origin) {
|
|
83
|
+
this.error(`Profile '${profileName}' is missing instance_origin`);
|
|
84
|
+
}
|
|
85
|
+
if (!profile.access_token) {
|
|
86
|
+
this.error(`Profile '${profileName}' is missing access_token`);
|
|
87
|
+
}
|
|
88
|
+
// Determine workspace_id from flag or profile
|
|
89
|
+
let workspaceId;
|
|
90
|
+
if (flags.workspace) {
|
|
91
|
+
workspaceId = flags.workspace;
|
|
92
|
+
}
|
|
93
|
+
else if (profile.workspace) {
|
|
94
|
+
workspaceId = profile.workspace;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
this.error(`Workspace ID is required. Either:\n` +
|
|
98
|
+
` 1. Provide it as a flag: xano static_host:build:list <static_host> -w <workspace_id>\n` +
|
|
99
|
+
` 2. Set it in your profile using: xano profile:edit ${profileName} -w <workspace_id>`);
|
|
100
|
+
}
|
|
101
|
+
// Build query parameters
|
|
102
|
+
const queryParams = new URLSearchParams({
|
|
103
|
+
page: flags.page.toString(),
|
|
104
|
+
});
|
|
105
|
+
// Only add per_page if it's not the default value
|
|
106
|
+
if (flags.per_page !== 50) {
|
|
107
|
+
queryParams.append('per_page', flags.per_page.toString());
|
|
108
|
+
}
|
|
109
|
+
// Construct the API URL
|
|
110
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/static_host/${args.static_host}/build?${queryParams.toString()}`;
|
|
111
|
+
// Fetch builds from the API
|
|
112
|
+
try {
|
|
113
|
+
const response = await fetch(apiUrl, {
|
|
114
|
+
method: 'GET',
|
|
115
|
+
headers: {
|
|
116
|
+
'accept': 'application/json',
|
|
117
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
const errorText = await response.text();
|
|
122
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
123
|
+
}
|
|
124
|
+
const data = await response.json();
|
|
125
|
+
// Handle different response formats
|
|
126
|
+
let builds;
|
|
127
|
+
if (Array.isArray(data)) {
|
|
128
|
+
builds = data;
|
|
129
|
+
}
|
|
130
|
+
else if (data && typeof data === 'object' && 'builds' in data && Array.isArray(data.builds)) {
|
|
131
|
+
builds = data.builds;
|
|
132
|
+
}
|
|
133
|
+
else if (data && typeof data === 'object' && 'items' in data && Array.isArray(data.items)) {
|
|
134
|
+
builds = data.items;
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
this.error('Unexpected API response format');
|
|
138
|
+
}
|
|
139
|
+
// Output results
|
|
140
|
+
if (flags.output === 'json') {
|
|
141
|
+
this.log(JSON.stringify(builds, null, 2));
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
// summary format
|
|
145
|
+
if (builds.length === 0) {
|
|
146
|
+
this.log('No builds found');
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
this.log('Available builds:');
|
|
150
|
+
for (const build of builds) {
|
|
151
|
+
if (build.id !== undefined) {
|
|
152
|
+
const statusInfo = build.status ? ` - Status: ${build.status}` : '';
|
|
153
|
+
this.log(` - ${build.name} (ID: ${build.id})${statusInfo}`);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
this.log(` - ${build.name}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
if (error instanceof Error) {
|
|
164
|
+
this.error(`Failed to fetch builds: ${error.message}`);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
this.error(`Failed to fetch builds: ${String(error)}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
loadCredentials() {
|
|
172
|
+
const configDir = path.join(os.homedir(), '.xano');
|
|
173
|
+
const credentialsPath = path.join(configDir, 'credentials.yaml');
|
|
174
|
+
// Check if credentials file exists
|
|
175
|
+
if (!fs.existsSync(credentialsPath)) {
|
|
176
|
+
this.error(`Credentials file not found at ${credentialsPath}\n` +
|
|
177
|
+
`Create a profile using 'xano profile:create'`);
|
|
178
|
+
}
|
|
179
|
+
// Read credentials file
|
|
180
|
+
try {
|
|
181
|
+
const fileContent = fs.readFileSync(credentialsPath, 'utf8');
|
|
182
|
+
const parsed = yaml.load(fileContent);
|
|
183
|
+
if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
|
|
184
|
+
this.error('Credentials file has invalid format.');
|
|
185
|
+
}
|
|
186
|
+
return parsed;
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
this.error(`Failed to parse credentials file: ${error}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import BaseCommand from '../../../base-command.js';
|
|
2
|
+
export default class StaticHostList extends BaseCommand {
|
|
3
|
+
static args: {};
|
|
4
|
+
static flags: {
|
|
5
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
6
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
per_page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
};
|
|
11
|
+
static description: string;
|
|
12
|
+
static examples: string[];
|
|
13
|
+
run(): Promise<void>;
|
|
14
|
+
private loadCredentials;
|
|
15
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as os from 'node:os';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import * as yaml from 'js-yaml';
|
|
6
|
+
import BaseCommand from '../../../base-command.js';
|
|
7
|
+
export default class StaticHostList extends BaseCommand {
|
|
8
|
+
static args = {};
|
|
9
|
+
static flags = {
|
|
10
|
+
...BaseCommand.baseFlags,
|
|
11
|
+
workspace: Flags.string({
|
|
12
|
+
char: 'w',
|
|
13
|
+
description: 'Workspace ID (optional if set in profile)',
|
|
14
|
+
required: false,
|
|
15
|
+
}),
|
|
16
|
+
output: Flags.string({
|
|
17
|
+
char: 'o',
|
|
18
|
+
description: 'Output format',
|
|
19
|
+
required: false,
|
|
20
|
+
default: 'summary',
|
|
21
|
+
options: ['summary', 'json'],
|
|
22
|
+
}),
|
|
23
|
+
page: Flags.integer({
|
|
24
|
+
description: 'Page number for pagination',
|
|
25
|
+
required: false,
|
|
26
|
+
default: 1,
|
|
27
|
+
}),
|
|
28
|
+
per_page: Flags.integer({
|
|
29
|
+
description: 'Number of results per page',
|
|
30
|
+
required: false,
|
|
31
|
+
default: 50,
|
|
32
|
+
}),
|
|
33
|
+
};
|
|
34
|
+
static description = 'List all static hosts in a workspace from the Xano Metadata API';
|
|
35
|
+
static examples = [
|
|
36
|
+
`$ xano static_host:list -w 40
|
|
37
|
+
Available static hosts:
|
|
38
|
+
- my-static-host (ID: 1)
|
|
39
|
+
- another-host (ID: 2)
|
|
40
|
+
`,
|
|
41
|
+
`$ xano static_host:list --profile production
|
|
42
|
+
Available static hosts:
|
|
43
|
+
- my-static-host (ID: 1)
|
|
44
|
+
- another-host (ID: 2)
|
|
45
|
+
`,
|
|
46
|
+
`$ xano static_host:list -w 40 --output json
|
|
47
|
+
[
|
|
48
|
+
{
|
|
49
|
+
"id": 1,
|
|
50
|
+
"name": "my-static-host",
|
|
51
|
+
"domain": "example.com"
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
`,
|
|
55
|
+
`$ xano static_host:list -p staging -o json --page 2
|
|
56
|
+
[
|
|
57
|
+
{
|
|
58
|
+
"id": 3,
|
|
59
|
+
"name": "static-host-3"
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
`,
|
|
63
|
+
];
|
|
64
|
+
async run() {
|
|
65
|
+
const { flags } = await this.parse(StaticHostList);
|
|
66
|
+
// Get profile name (default or from flag/env)
|
|
67
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
68
|
+
// Load credentials
|
|
69
|
+
const credentials = this.loadCredentials();
|
|
70
|
+
// Get the profile configuration
|
|
71
|
+
if (!(profileName in credentials.profiles)) {
|
|
72
|
+
this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
|
|
73
|
+
`Create a profile using 'xano profile:create'`);
|
|
74
|
+
}
|
|
75
|
+
const profile = credentials.profiles[profileName];
|
|
76
|
+
// Validate required fields
|
|
77
|
+
if (!profile.instance_origin) {
|
|
78
|
+
this.error(`Profile '${profileName}' is missing instance_origin`);
|
|
79
|
+
}
|
|
80
|
+
if (!profile.access_token) {
|
|
81
|
+
this.error(`Profile '${profileName}' is missing access_token`);
|
|
82
|
+
}
|
|
83
|
+
// Determine workspace_id from flag or profile
|
|
84
|
+
let workspaceId;
|
|
85
|
+
if (flags.workspace) {
|
|
86
|
+
workspaceId = flags.workspace;
|
|
87
|
+
}
|
|
88
|
+
else if (profile.workspace) {
|
|
89
|
+
workspaceId = profile.workspace;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
this.error(`Workspace ID is required. Either:\n` +
|
|
93
|
+
` 1. Provide it as a flag: xano static_host:list -w <workspace_id>\n` +
|
|
94
|
+
` 2. Set it in your profile using: xano profile:edit ${profileName} -w <workspace_id>`);
|
|
95
|
+
}
|
|
96
|
+
// Build query parameters
|
|
97
|
+
const queryParams = new URLSearchParams({
|
|
98
|
+
page: flags.page.toString(),
|
|
99
|
+
});
|
|
100
|
+
// Only add per_page if it's not the default value
|
|
101
|
+
if (flags.per_page !== 50) {
|
|
102
|
+
queryParams.append('per_page', flags.per_page.toString());
|
|
103
|
+
}
|
|
104
|
+
// Construct the API URL
|
|
105
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/static_host?${queryParams.toString()}`;
|
|
106
|
+
// Fetch static hosts from the API
|
|
107
|
+
try {
|
|
108
|
+
const response = await fetch(apiUrl, {
|
|
109
|
+
method: 'GET',
|
|
110
|
+
headers: {
|
|
111
|
+
'accept': 'application/json',
|
|
112
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
if (!response.ok) {
|
|
116
|
+
const errorText = await response.text();
|
|
117
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
118
|
+
}
|
|
119
|
+
const data = await response.json();
|
|
120
|
+
// Handle different response formats
|
|
121
|
+
let staticHosts;
|
|
122
|
+
if (Array.isArray(data)) {
|
|
123
|
+
staticHosts = data;
|
|
124
|
+
}
|
|
125
|
+
else if (data && typeof data === 'object' && 'static_hosts' in data && Array.isArray(data.static_hosts)) {
|
|
126
|
+
staticHosts = data.static_hosts;
|
|
127
|
+
}
|
|
128
|
+
else if (data && typeof data === 'object' && 'items' in data && Array.isArray(data.items)) {
|
|
129
|
+
staticHosts = data.items;
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
this.error('Unexpected API response format');
|
|
133
|
+
}
|
|
134
|
+
// Output results
|
|
135
|
+
if (flags.output === 'json') {
|
|
136
|
+
this.log(JSON.stringify(staticHosts, null, 2));
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
// summary format
|
|
140
|
+
if (staticHosts.length === 0) {
|
|
141
|
+
this.log('No static hosts found');
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
this.log('Available static hosts:');
|
|
145
|
+
for (const host of staticHosts) {
|
|
146
|
+
if (host.id !== undefined) {
|
|
147
|
+
const domainInfo = host.domain ? ` - ${host.domain}` : '';
|
|
148
|
+
this.log(` - ${host.name} (ID: ${host.id})${domainInfo}`);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
this.log(` - ${host.name}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
if (error instanceof Error) {
|
|
159
|
+
this.error(`Failed to fetch static hosts: ${error.message}`);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
this.error(`Failed to fetch static hosts: ${String(error)}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
loadCredentials() {
|
|
167
|
+
const configDir = path.join(os.homedir(), '.xano');
|
|
168
|
+
const credentialsPath = path.join(configDir, 'credentials.yaml');
|
|
169
|
+
// Check if credentials file exists
|
|
170
|
+
if (!fs.existsSync(credentialsPath)) {
|
|
171
|
+
this.error(`Credentials file not found at ${credentialsPath}\n` +
|
|
172
|
+
`Create a profile using 'xano profile:create'`);
|
|
173
|
+
}
|
|
174
|
+
// Read credentials file
|
|
175
|
+
try {
|
|
176
|
+
const fileContent = fs.readFileSync(credentialsPath, 'utf8');
|
|
177
|
+
const parsed = yaml.load(fileContent);
|
|
178
|
+
if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
|
|
179
|
+
this.error('Credentials file has invalid format.');
|
|
180
|
+
}
|
|
181
|
+
return parsed;
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
this.error(`Failed to parse credentials file: ${error}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import BaseCommand from '../../../base-command.js';
|
|
2
|
+
export default class WorkspaceList 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
|
+
}
|