@xano/cli 1.0.4-beta.3 → 1.0.4
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 +4 -51
- package/dist/base-command.d.ts +0 -27
- package/dist/base-command.js +1 -124
- package/dist/commands/auth/index.d.ts +1 -45
- package/dist/commands/auth/index.js +29 -197
- package/dist/commands/{tenant/snapshot/list → knowledge/pull}/index.d.ts +4 -6
- package/dist/commands/knowledge/pull/index.js +86 -0
- package/dist/commands/knowledge/push/index.d.ts +20 -0
- package/dist/commands/knowledge/push/index.js +126 -0
- package/dist/commands/static_host/build/create/index.d.ts +1 -9
- package/dist/commands/static_host/build/create/index.js +4 -54
- package/dist/commands/static_host/build/get/index.d.ts +1 -1
- package/dist/commands/static_host/build/get/index.js +10 -16
- package/dist/utils/knowledge-sync.d.ts +108 -0
- package/dist/utils/knowledge-sync.js +380 -0
- package/dist/utils/multidoc-push.js +17 -21
- package/dist/utils/reference-checker.js +2 -2
- package/oclif.manifest.json +2951 -4086
- package/package.json +1 -3
- package/dist/commands/static_host/build/delete/index.d.ts +0 -19
- package/dist/commands/static_host/build/delete/index.js +0 -114
- package/dist/commands/static_host/build/pull/index.d.ts +0 -52
- package/dist/commands/static_host/build/pull/index.js +0 -300
- package/dist/commands/static_host/build/push/index.d.ts +0 -23
- package/dist/commands/static_host/build/push/index.js +0 -225
- package/dist/commands/static_host/create/index.d.ts +0 -17
- package/dist/commands/static_host/create/index.js +0 -86
- package/dist/commands/static_host/deploy/index.d.ts +0 -18
- package/dist/commands/static_host/deploy/index.js +0 -105
- package/dist/commands/static_host/edit/index.d.ts +0 -23
- package/dist/commands/static_host/edit/index.js +0 -151
- package/dist/commands/static_host/get/index.d.ts +0 -18
- package/dist/commands/static_host/get/index.js +0 -94
- package/dist/commands/static_host/migrate/index.d.ts +0 -44
- package/dist/commands/static_host/migrate/index.js +0 -205
- package/dist/commands/tenant/snapshot/create/index.d.ts +0 -17
- package/dist/commands/tenant/snapshot/create/index.js +0 -78
- package/dist/commands/tenant/snapshot/delete/index.d.ts +0 -19
- package/dist/commands/tenant/snapshot/delete/index.js +0 -102
- package/dist/commands/tenant/snapshot/list/index.js +0 -96
- package/dist/commands/tenant/snapshot/swap/index.d.ts +0 -19
- package/dist/commands/tenant/snapshot/swap/index.js +0 -103
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
import { Args, Flags, ux } from '@oclif/core';
|
|
2
|
-
import archiver from 'archiver';
|
|
3
|
-
import * as fs from 'node:fs';
|
|
4
|
-
import * as path from 'node:path';
|
|
5
|
-
import BaseCommand from '../../../../base-command.js';
|
|
6
|
-
import { generateBuildName } from '../create/index.js';
|
|
7
|
-
export default class StaticHostBuildPush extends BaseCommand {
|
|
8
|
-
static args = {
|
|
9
|
-
static_host: Args.string({
|
|
10
|
-
description: 'Static Host name',
|
|
11
|
-
required: true,
|
|
12
|
-
}),
|
|
13
|
-
};
|
|
14
|
-
static description = 'Push a directory or zip file as a new static host build';
|
|
15
|
-
static examples = [
|
|
16
|
-
`$ xano static_host build push default -d ./dist -n "v1.0.0"
|
|
17
|
-
Pushed 15 files as build "v1.0.0"
|
|
18
|
-
ID: 123
|
|
19
|
-
`,
|
|
20
|
-
`$ xano static_host build push default
|
|
21
|
-
Pushed 8 files as build "20260531-143022"
|
|
22
|
-
`,
|
|
23
|
-
`$ xano static_host build push default -f ./build.zip -n "v1.0.0"
|
|
24
|
-
Pushed build.zip as build "v1.0.0"
|
|
25
|
-
ID: 124
|
|
26
|
-
`,
|
|
27
|
-
`$ xano static_host build push myhost -n "production" --description "Production build" -w 40
|
|
28
|
-
Pushed 22 files as build "production"
|
|
29
|
-
ID: 125
|
|
30
|
-
`,
|
|
31
|
-
];
|
|
32
|
-
static flags = {
|
|
33
|
-
...BaseCommand.baseFlags,
|
|
34
|
-
description: Flags.string({
|
|
35
|
-
description: 'Build description',
|
|
36
|
-
required: false,
|
|
37
|
-
}),
|
|
38
|
-
directory: Flags.string({
|
|
39
|
-
char: 'd',
|
|
40
|
-
description: 'Directory to push (defaults to current directory)',
|
|
41
|
-
exclusive: ['file'],
|
|
42
|
-
required: false,
|
|
43
|
-
}),
|
|
44
|
-
file: Flags.string({
|
|
45
|
-
char: 'f',
|
|
46
|
-
description: 'Path to a zip file to upload (alternative to -d)',
|
|
47
|
-
exclusive: ['directory'],
|
|
48
|
-
required: false,
|
|
49
|
-
}),
|
|
50
|
-
name: Flags.string({
|
|
51
|
-
char: 'n',
|
|
52
|
-
description: 'Build name (auto-generated from the current timestamp if omitted)',
|
|
53
|
-
required: false,
|
|
54
|
-
}),
|
|
55
|
-
'no-wait': Flags.boolean({
|
|
56
|
-
default: false,
|
|
57
|
-
description: 'Return immediately after upload instead of waiting for the build to finish',
|
|
58
|
-
required: false,
|
|
59
|
-
}),
|
|
60
|
-
output: Flags.string({
|
|
61
|
-
char: 'o',
|
|
62
|
-
default: 'summary',
|
|
63
|
-
description: 'Output format',
|
|
64
|
-
options: ['summary', 'json'],
|
|
65
|
-
required: false,
|
|
66
|
-
}),
|
|
67
|
-
workspace: Flags.string({
|
|
68
|
-
char: 'w',
|
|
69
|
-
description: 'Workspace ID (optional if set in profile)',
|
|
70
|
-
required: false,
|
|
71
|
-
}),
|
|
72
|
-
};
|
|
73
|
-
async run() {
|
|
74
|
-
const { args, flags } = await this.parse(StaticHostBuildPush);
|
|
75
|
-
const { profile, profileName } = this.resolveProfile(flags);
|
|
76
|
-
let workspaceId;
|
|
77
|
-
if (flags.workspace) {
|
|
78
|
-
workspaceId = flags.workspace;
|
|
79
|
-
}
|
|
80
|
-
else if (profile.workspace) {
|
|
81
|
-
workspaceId = profile.workspace;
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
this.error(`Workspace ID is required. Either:\n` +
|
|
85
|
-
` 1. Provide it as a flag: xano static_host build push <static_host> -n <name> -w <workspace_id>\n` +
|
|
86
|
-
` 2. Set it in your profile using: xano profile edit ${profileName} -w <workspace_id>`);
|
|
87
|
-
}
|
|
88
|
-
const animate = Boolean(process.stdout.isTTY) && flags.output !== 'json' && !flags.verbose;
|
|
89
|
-
const buildName = flags.name ?? generateBuildName();
|
|
90
|
-
let zipBuffer;
|
|
91
|
-
let fileCount = 0;
|
|
92
|
-
let fileName;
|
|
93
|
-
if (flags.file) {
|
|
94
|
-
const filePath = path.resolve(flags.file);
|
|
95
|
-
if (!fs.existsSync(filePath)) {
|
|
96
|
-
this.error(`File not found: ${filePath}`);
|
|
97
|
-
}
|
|
98
|
-
const fileStats = fs.statSync(filePath);
|
|
99
|
-
if (!fileStats.isFile()) {
|
|
100
|
-
this.error(`Path is not a file: ${filePath}`);
|
|
101
|
-
}
|
|
102
|
-
fileName = path.basename(filePath);
|
|
103
|
-
if (animate)
|
|
104
|
-
ux.action.start('Uploading', fileName);
|
|
105
|
-
zipBuffer = fs.readFileSync(filePath);
|
|
106
|
-
}
|
|
107
|
-
else {
|
|
108
|
-
const sourceDir = path.resolve(flags.directory ?? '.');
|
|
109
|
-
if (!fs.existsSync(sourceDir)) {
|
|
110
|
-
this.error(`Directory not found: ${sourceDir}`);
|
|
111
|
-
}
|
|
112
|
-
const dirStats = fs.statSync(sourceDir);
|
|
113
|
-
if (!dirStats.isDirectory()) {
|
|
114
|
-
this.error(`Path is not a directory: ${sourceDir}`);
|
|
115
|
-
}
|
|
116
|
-
fileCount = this.countFiles(sourceDir);
|
|
117
|
-
if (animate)
|
|
118
|
-
ux.action.start('Packaging', `${fileCount} files`);
|
|
119
|
-
zipBuffer = await this.createZipBuffer(sourceDir);
|
|
120
|
-
if (animate) {
|
|
121
|
-
ux.action.stop(`${fileCount} files (${(zipBuffer.length / (1024 * 1024)).toFixed(1)} MB)`);
|
|
122
|
-
ux.action.start('Uploading');
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
const sizeMB = (zipBuffer.length / (1024 * 1024)).toFixed(1);
|
|
126
|
-
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/static_host/${args.static_host}/build`;
|
|
127
|
-
const formData = new globalThis.FormData();
|
|
128
|
-
const blob = new Blob([new Uint8Array(zipBuffer)], { type: 'application/zip' });
|
|
129
|
-
formData.append('file', blob, fileName ?? 'build.zip');
|
|
130
|
-
formData.append('name', buildName);
|
|
131
|
-
if (flags.description) {
|
|
132
|
-
formData.append('description', flags.description);
|
|
133
|
-
}
|
|
134
|
-
try {
|
|
135
|
-
const response = await this.verboseFetch(apiUrl, {
|
|
136
|
-
body: formData,
|
|
137
|
-
headers: {
|
|
138
|
-
'accept': 'application/json',
|
|
139
|
-
'Authorization': `Bearer ${profile.access_token}`,
|
|
140
|
-
},
|
|
141
|
-
method: 'POST',
|
|
142
|
-
}, flags.verbose, profile.access_token);
|
|
143
|
-
if (animate) {
|
|
144
|
-
ux.action.stop('done');
|
|
145
|
-
}
|
|
146
|
-
if (!response.ok) {
|
|
147
|
-
const errorText = await response.text();
|
|
148
|
-
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
149
|
-
}
|
|
150
|
-
const result = await response.json();
|
|
151
|
-
if (!result || typeof result !== 'object') {
|
|
152
|
-
this.error('Unexpected API response format');
|
|
153
|
-
}
|
|
154
|
-
if (flags.output === 'json') {
|
|
155
|
-
this.log(JSON.stringify(result, null, 2));
|
|
156
|
-
}
|
|
157
|
-
else {
|
|
158
|
-
this.log(fileName
|
|
159
|
-
? `Pushed ${fileName} as build "${buildName}" (${sizeMB} MB)`
|
|
160
|
-
: `Pushed ${fileCount} files as build "${buildName}" (${sizeMB} MB)`);
|
|
161
|
-
this.log(`ID: ${result.id}`);
|
|
162
|
-
if (result.status) {
|
|
163
|
-
this.log(`Status: ${result.status}`);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
// Async (package.json) builds keep running after upload. Unless --no-wait,
|
|
167
|
-
// poll until the build finishes so the CLI mirrors the UI's progress.
|
|
168
|
-
const inProgress = result.status !== undefined && !['error', 'ok'].includes(result.status);
|
|
169
|
-
if (inProgress && !flags['no-wait']) {
|
|
170
|
-
const finalStatus = await this.waitForBuild({
|
|
171
|
-
buildId: result.id,
|
|
172
|
-
profile,
|
|
173
|
-
quiet: flags.output === 'json',
|
|
174
|
-
staticHost: args.static_host,
|
|
175
|
-
verbose: flags.verbose,
|
|
176
|
-
workspaceId,
|
|
177
|
-
});
|
|
178
|
-
if (finalStatus === 'error') {
|
|
179
|
-
this.error(`Build ${result.id} failed (status: error). Check the build log with: xano static_host build get ${args.static_host} --build_id ${result.id}`);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
if (flags.output !== 'json') {
|
|
183
|
-
await this.logStaticHostUrls({ profile, staticHost: args.static_host, verbose: flags.verbose, workspaceId });
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
catch (error) {
|
|
187
|
-
if (error instanceof Error) {
|
|
188
|
-
this.error(`Failed to push build: ${error.message}`);
|
|
189
|
-
}
|
|
190
|
-
else {
|
|
191
|
-
this.error(`Failed to push build: ${String(error)}`);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
countFiles(dir) {
|
|
196
|
-
let count = 0;
|
|
197
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
198
|
-
for (const entry of entries) {
|
|
199
|
-
if (entry.isDirectory()) {
|
|
200
|
-
count += this.countFiles(path.join(dir, entry.name));
|
|
201
|
-
}
|
|
202
|
-
else if (entry.isFile()) {
|
|
203
|
-
count++;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
return count;
|
|
207
|
-
}
|
|
208
|
-
async createZipBuffer(sourceDir) {
|
|
209
|
-
return new Promise((resolve, reject) => {
|
|
210
|
-
const chunks = [];
|
|
211
|
-
const archive = archiver('zip', { zlib: { level: 9 } });
|
|
212
|
-
archive.on('data', (chunk) => {
|
|
213
|
-
chunks.push(chunk);
|
|
214
|
-
});
|
|
215
|
-
archive.on('end', () => {
|
|
216
|
-
resolve(Buffer.concat(chunks));
|
|
217
|
-
});
|
|
218
|
-
archive.on('error', (err) => {
|
|
219
|
-
reject(err);
|
|
220
|
-
});
|
|
221
|
-
archive.directory(sourceDir, false);
|
|
222
|
-
archive.finalize();
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import BaseCommand from '../../../base-command.js';
|
|
2
|
-
export default class StaticHostCreate extends BaseCommand {
|
|
3
|
-
static args: {
|
|
4
|
-
name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
-
};
|
|
6
|
-
static description: string;
|
|
7
|
-
static examples: string[];
|
|
8
|
-
static flags: {
|
|
9
|
-
description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
-
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
-
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
-
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
-
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
-
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
|
-
};
|
|
16
|
-
run(): Promise<void>;
|
|
17
|
-
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { Args, Flags } from '@oclif/core';
|
|
2
|
-
import BaseCommand from '../../../base-command.js';
|
|
3
|
-
export default class StaticHostCreate extends BaseCommand {
|
|
4
|
-
static args = {
|
|
5
|
-
name: Args.string({
|
|
6
|
-
description: 'Name for the new static host',
|
|
7
|
-
required: true,
|
|
8
|
-
}),
|
|
9
|
-
};
|
|
10
|
-
static description = 'Create a new static host in the workspace';
|
|
11
|
-
static examples = [
|
|
12
|
-
`$ xano static_host create marketing
|
|
13
|
-
Created static host 'marketing' (ID: 7)
|
|
14
|
-
`,
|
|
15
|
-
`$ xano static_host create marketing --description "Marketing site" -w 40
|
|
16
|
-
Created static host 'marketing' (ID: 7)
|
|
17
|
-
`,
|
|
18
|
-
`$ xano static_host create marketing -o json`,
|
|
19
|
-
];
|
|
20
|
-
static flags = {
|
|
21
|
-
...BaseCommand.baseFlags,
|
|
22
|
-
description: Flags.string({
|
|
23
|
-
description: 'Description for the static host',
|
|
24
|
-
required: false,
|
|
25
|
-
}),
|
|
26
|
-
output: Flags.string({
|
|
27
|
-
char: 'o',
|
|
28
|
-
default: 'summary',
|
|
29
|
-
description: 'Output format',
|
|
30
|
-
options: ['summary', 'json'],
|
|
31
|
-
required: false,
|
|
32
|
-
}),
|
|
33
|
-
workspace: Flags.string({
|
|
34
|
-
char: 'w',
|
|
35
|
-
description: 'Workspace ID (optional if set in profile)',
|
|
36
|
-
required: false,
|
|
37
|
-
}),
|
|
38
|
-
};
|
|
39
|
-
async run() {
|
|
40
|
-
const { args, flags } = await this.parse(StaticHostCreate);
|
|
41
|
-
const { profile, profileName } = this.resolveProfile(flags);
|
|
42
|
-
let workspaceId;
|
|
43
|
-
if (flags.workspace) {
|
|
44
|
-
workspaceId = flags.workspace;
|
|
45
|
-
}
|
|
46
|
-
else if (profile.workspace) {
|
|
47
|
-
workspaceId = profile.workspace;
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
this.error(`Workspace ID is required. Either:\n` +
|
|
51
|
-
` 1. Provide it as a flag: xano static_host create <name> -w <workspace_id>\n` +
|
|
52
|
-
` 2. Set it in your profile using: xano profile edit ${profileName} -w <workspace_id>`);
|
|
53
|
-
}
|
|
54
|
-
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/static_host`;
|
|
55
|
-
try {
|
|
56
|
-
const response = await this.verboseFetch(apiUrl, {
|
|
57
|
-
body: JSON.stringify({ description: flags.description ?? '', name: args.name }),
|
|
58
|
-
headers: {
|
|
59
|
-
accept: 'application/json',
|
|
60
|
-
Authorization: `Bearer ${profile.access_token}`,
|
|
61
|
-
'Content-Type': 'application/json',
|
|
62
|
-
},
|
|
63
|
-
method: 'POST',
|
|
64
|
-
}, flags.verbose, profile.access_token);
|
|
65
|
-
if (!response.ok) {
|
|
66
|
-
const message = await this.parseApiError(response, `Failed to create static host '${args.name}'`);
|
|
67
|
-
this.error(message);
|
|
68
|
-
}
|
|
69
|
-
const host = (await response.json());
|
|
70
|
-
if (flags.output === 'json') {
|
|
71
|
-
this.log(JSON.stringify(host, null, 2));
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
this.log(`Created static host '${host.name}' (ID: ${host.id})`);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
catch (error) {
|
|
78
|
-
if (error instanceof Error) {
|
|
79
|
-
this.error(`Failed to create static host: ${error.message}`);
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
this.error(`Failed to create static host: ${String(error)}`);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import BaseCommand from '../../../base-command.js';
|
|
2
|
-
export default class StaticHostDeploy extends BaseCommand {
|
|
3
|
-
static args: {
|
|
4
|
-
static_host: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
-
};
|
|
6
|
-
static description: string;
|
|
7
|
-
static examples: string[];
|
|
8
|
-
static flags: {
|
|
9
|
-
build_id: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
-
env: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
-
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
-
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
-
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
-
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
-
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
|
-
};
|
|
17
|
-
run(): Promise<void>;
|
|
18
|
-
}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { Args, Flags } from '@oclif/core';
|
|
2
|
-
import BaseCommand from '../../../base-command.js';
|
|
3
|
-
export default class StaticHostDeploy extends BaseCommand {
|
|
4
|
-
static args = {
|
|
5
|
-
static_host: Args.string({
|
|
6
|
-
description: 'Static Host name',
|
|
7
|
-
required: true,
|
|
8
|
-
}),
|
|
9
|
-
};
|
|
10
|
-
static description = 'Deploy a static host build to an environment';
|
|
11
|
-
static examples = [
|
|
12
|
-
`$ xano static_host deploy default --build_id 52 --env dev
|
|
13
|
-
Deployed build 52 to dev
|
|
14
|
-
URL: https://x1234-abcd.static.xano.io
|
|
15
|
-
`,
|
|
16
|
-
`$ xano static_host deploy default --build_id 52 --env prod
|
|
17
|
-
Deployed build 52 to prod
|
|
18
|
-
URL: https://x1234-abcd.static.xano.io
|
|
19
|
-
`,
|
|
20
|
-
`$ xano static_host deploy myhost --build_id 123 --env dev -w 40
|
|
21
|
-
Deployed build 123 to dev
|
|
22
|
-
`,
|
|
23
|
-
`$ xano static_host deploy default --build_id 52 --env prod -o json`,
|
|
24
|
-
];
|
|
25
|
-
static flags = {
|
|
26
|
-
...BaseCommand.baseFlags,
|
|
27
|
-
build_id: Flags.string({
|
|
28
|
-
description: 'Build ID to deploy',
|
|
29
|
-
required: true,
|
|
30
|
-
}),
|
|
31
|
-
env: Flags.string({
|
|
32
|
-
description: 'Target environment',
|
|
33
|
-
options: ['dev', 'prod'],
|
|
34
|
-
required: true,
|
|
35
|
-
}),
|
|
36
|
-
output: Flags.string({
|
|
37
|
-
char: 'o',
|
|
38
|
-
default: 'summary',
|
|
39
|
-
description: 'Output format',
|
|
40
|
-
options: ['summary', 'json'],
|
|
41
|
-
required: false,
|
|
42
|
-
}),
|
|
43
|
-
workspace: Flags.string({
|
|
44
|
-
char: 'w',
|
|
45
|
-
description: 'Workspace ID (optional if set in profile)',
|
|
46
|
-
required: false,
|
|
47
|
-
}),
|
|
48
|
-
};
|
|
49
|
-
async run() {
|
|
50
|
-
const { args, flags } = await this.parse(StaticHostDeploy);
|
|
51
|
-
const { profile, profileName } = this.resolveProfile(flags);
|
|
52
|
-
let workspaceId;
|
|
53
|
-
if (flags.workspace) {
|
|
54
|
-
workspaceId = flags.workspace;
|
|
55
|
-
}
|
|
56
|
-
else if (profile.workspace) {
|
|
57
|
-
workspaceId = profile.workspace;
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
this.error(`Workspace ID is required. Either:\n` +
|
|
61
|
-
` 1. Provide it as a flag: xano static_host deploy <static_host> --build_id <id> --env <env> -w <workspace_id>\n` +
|
|
62
|
-
` 2. Set it in your profile using: xano profile edit ${profileName} -w <workspace_id>`);
|
|
63
|
-
}
|
|
64
|
-
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/static_host/${args.static_host}/build/${flags.build_id}/env`;
|
|
65
|
-
try {
|
|
66
|
-
const response = await this.verboseFetch(apiUrl, {
|
|
67
|
-
body: JSON.stringify({ env: flags.env }),
|
|
68
|
-
headers: {
|
|
69
|
-
'accept': 'application/json',
|
|
70
|
-
'Authorization': `Bearer ${profile.access_token}`,
|
|
71
|
-
'Content-Type': 'application/json',
|
|
72
|
-
},
|
|
73
|
-
method: 'POST',
|
|
74
|
-
}, flags.verbose, profile.access_token);
|
|
75
|
-
if (!response.ok) {
|
|
76
|
-
const errorText = await response.text();
|
|
77
|
-
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
78
|
-
}
|
|
79
|
-
const result = await response.json();
|
|
80
|
-
if (!result || typeof result !== 'object') {
|
|
81
|
-
this.error('Unexpected API response format');
|
|
82
|
-
}
|
|
83
|
-
if (flags.output === 'json') {
|
|
84
|
-
this.log(JSON.stringify(result, null, 2));
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
this.log(`Deployed build ${flags.build_id} to ${flags.env}`);
|
|
88
|
-
if (result.default_url) {
|
|
89
|
-
this.log(`URL: ${result.default_url}`);
|
|
90
|
-
}
|
|
91
|
-
if (result.custom_url) {
|
|
92
|
-
this.log(`Custom URL: ${result.custom_url}`);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
catch (error) {
|
|
97
|
-
if (error instanceof Error) {
|
|
98
|
-
this.error(`Failed to deploy build: ${error.message}`);
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
this.error(`Failed to deploy build: ${String(error)}`);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import BaseCommand from '../../../base-command.js';
|
|
2
|
-
export default class StaticHostEdit extends BaseCommand {
|
|
3
|
-
static args: {
|
|
4
|
-
static_host: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
-
};
|
|
6
|
-
static description: string;
|
|
7
|
-
static examples: string[];
|
|
8
|
-
static flags: {
|
|
9
|
-
description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
-
'git-private-key-file': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
-
'git-public-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
-
'git-repo': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
-
name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
-
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
-
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
-
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
|
-
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
|
-
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
19
|
-
};
|
|
20
|
-
run(): Promise<void>;
|
|
21
|
-
/** Fetch the current host record so unspecified fields are preserved on edit. */
|
|
22
|
-
private fetchHost;
|
|
23
|
-
}
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import { Args, Flags } from '@oclif/core';
|
|
2
|
-
import * as fs from 'node:fs';
|
|
3
|
-
import * as path from 'node:path';
|
|
4
|
-
import BaseCommand from '../../../base-command.js';
|
|
5
|
-
export default class StaticHostEdit extends BaseCommand {
|
|
6
|
-
static args = {
|
|
7
|
-
static_host: Args.string({
|
|
8
|
-
description: 'Static Host name to edit',
|
|
9
|
-
required: true,
|
|
10
|
-
}),
|
|
11
|
-
};
|
|
12
|
-
static description = 'Update a static host\'s name, description, or git configuration';
|
|
13
|
-
static examples = [
|
|
14
|
-
`$ xano static_host edit newsite --description "Marketing site"
|
|
15
|
-
Updated static host 'newsite'
|
|
16
|
-
`,
|
|
17
|
-
`$ xano static_host edit newsite --name newsite-v2
|
|
18
|
-
Updated static host 'newsite' (renamed to 'newsite-v2')
|
|
19
|
-
`,
|
|
20
|
-
`$ xano static_host edit newsite --git-repo git@github.com:me/site.git --git-private-key-file ./deploy_key
|
|
21
|
-
Updated static host 'newsite'
|
|
22
|
-
`,
|
|
23
|
-
];
|
|
24
|
-
static flags = {
|
|
25
|
-
...BaseCommand.baseFlags,
|
|
26
|
-
description: Flags.string({
|
|
27
|
-
description: 'New description for the static host',
|
|
28
|
-
required: false,
|
|
29
|
-
}),
|
|
30
|
-
'git-private-key-file': Flags.string({
|
|
31
|
-
description: 'Path to a file containing the git SSH private key (read from disk; never passed on the command line)',
|
|
32
|
-
required: false,
|
|
33
|
-
}),
|
|
34
|
-
'git-public-key': Flags.string({
|
|
35
|
-
description: 'Git SSH public key',
|
|
36
|
-
required: false,
|
|
37
|
-
}),
|
|
38
|
-
'git-repo': Flags.string({
|
|
39
|
-
description: 'Git repository URL (e.g. git@github.com:org/repo.git)',
|
|
40
|
-
required: false,
|
|
41
|
-
}),
|
|
42
|
-
name: Flags.string({
|
|
43
|
-
description: 'New name for the static host (renaming changes the deployed hostname)',
|
|
44
|
-
required: false,
|
|
45
|
-
}),
|
|
46
|
-
output: Flags.string({
|
|
47
|
-
char: 'o',
|
|
48
|
-
default: 'summary',
|
|
49
|
-
description: 'Output format',
|
|
50
|
-
options: ['summary', 'json'],
|
|
51
|
-
required: false,
|
|
52
|
-
}),
|
|
53
|
-
workspace: Flags.string({
|
|
54
|
-
char: 'w',
|
|
55
|
-
description: 'Workspace ID (optional if set in profile)',
|
|
56
|
-
required: false,
|
|
57
|
-
}),
|
|
58
|
-
};
|
|
59
|
-
async run() {
|
|
60
|
-
const { args, flags } = await this.parse(StaticHostEdit);
|
|
61
|
-
const hasGitFlag = Boolean(flags['git-repo'] || flags['git-private-key-file'] || flags['git-public-key']);
|
|
62
|
-
if (!flags.name && flags.description === undefined && !hasGitFlag) {
|
|
63
|
-
this.error('Nothing to update. Provide at least one of --name, --description, or a --git-* flag.');
|
|
64
|
-
}
|
|
65
|
-
const { profile, profileName } = this.resolveProfile(flags);
|
|
66
|
-
let workspaceId;
|
|
67
|
-
if (flags.workspace) {
|
|
68
|
-
workspaceId = flags.workspace;
|
|
69
|
-
}
|
|
70
|
-
else if (profile.workspace) {
|
|
71
|
-
workspaceId = profile.workspace;
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
this.error(`Workspace ID is required. Either:\n` +
|
|
75
|
-
` 1. Provide it as a flag: xano static_host edit <static_host> --name <name> -w <workspace_id>\n` +
|
|
76
|
-
` 2. Set it in your profile using: xano profile edit ${profileName} -w <workspace_id>`);
|
|
77
|
-
}
|
|
78
|
-
// The edit endpoint requires `name`, and merges git as a whole object —
|
|
79
|
-
// so fetch the current record first and only override the fields the user set.
|
|
80
|
-
const current = await this.fetchHost(profile, workspaceId, args.static_host, flags.verbose);
|
|
81
|
-
const git = { ...current.git };
|
|
82
|
-
if (flags['git-repo'] !== undefined)
|
|
83
|
-
git.repo = flags['git-repo'];
|
|
84
|
-
if (flags['git-public-key'] !== undefined)
|
|
85
|
-
git.public_key = flags['git-public-key'];
|
|
86
|
-
if (flags['git-private-key-file'] !== undefined) {
|
|
87
|
-
const keyPath = path.resolve(flags['git-private-key-file']);
|
|
88
|
-
if (!fs.existsSync(keyPath)) {
|
|
89
|
-
this.error(`Private key file not found: ${keyPath}`);
|
|
90
|
-
}
|
|
91
|
-
git.private_key = fs.readFileSync(keyPath, 'utf8').trim();
|
|
92
|
-
}
|
|
93
|
-
const body = {
|
|
94
|
-
description: flags.description ?? current.description ?? '',
|
|
95
|
-
git: {
|
|
96
|
-
private_key: git.private_key ?? '',
|
|
97
|
-
public_key: git.public_key ?? '',
|
|
98
|
-
repo: git.repo ?? '',
|
|
99
|
-
},
|
|
100
|
-
name: flags.name ?? current.name,
|
|
101
|
-
};
|
|
102
|
-
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/static_host/${args.static_host}/edit`;
|
|
103
|
-
try {
|
|
104
|
-
const response = await this.verboseFetch(apiUrl, {
|
|
105
|
-
body: JSON.stringify(body),
|
|
106
|
-
headers: {
|
|
107
|
-
accept: 'application/json',
|
|
108
|
-
Authorization: `Bearer ${profile.access_token}`,
|
|
109
|
-
'Content-Type': 'application/json',
|
|
110
|
-
},
|
|
111
|
-
method: 'POST',
|
|
112
|
-
}, flags.verbose, profile.access_token);
|
|
113
|
-
if (!response.ok) {
|
|
114
|
-
const message = await this.parseApiError(response, `Failed to update static host '${args.static_host}'`);
|
|
115
|
-
this.error(message);
|
|
116
|
-
}
|
|
117
|
-
const updated = (await response.json());
|
|
118
|
-
if (flags.output === 'json') {
|
|
119
|
-
this.log(JSON.stringify(updated, null, 2));
|
|
120
|
-
}
|
|
121
|
-
else {
|
|
122
|
-
const renamed = flags.name && flags.name !== args.static_host ? ` (renamed to '${flags.name}')` : '';
|
|
123
|
-
this.log(`Updated static host '${args.static_host}'${renamed}`);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
catch (error) {
|
|
127
|
-
if (error instanceof Error) {
|
|
128
|
-
this.error(`Failed to update static host: ${error.message}`);
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
this.error(`Failed to update static host: ${String(error)}`);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
/** Fetch the current host record so unspecified fields are preserved on edit. */
|
|
136
|
-
async fetchHost(profile, workspaceId, staticHost, verbose) {
|
|
137
|
-
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/static_host/${staticHost}`;
|
|
138
|
-
const response = await this.verboseFetch(apiUrl, {
|
|
139
|
-
headers: {
|
|
140
|
-
accept: 'application/json',
|
|
141
|
-
Authorization: `Bearer ${profile.access_token}`,
|
|
142
|
-
},
|
|
143
|
-
method: 'GET',
|
|
144
|
-
}, verbose, profile.access_token);
|
|
145
|
-
if (!response.ok) {
|
|
146
|
-
const message = await this.parseApiError(response, `Static host '${staticHost}' not found`);
|
|
147
|
-
this.error(message);
|
|
148
|
-
}
|
|
149
|
-
return (await response.json());
|
|
150
|
-
}
|
|
151
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import BaseCommand from '../../../base-command.js';
|
|
2
|
-
export default class StaticHostGet extends BaseCommand {
|
|
3
|
-
static args: {
|
|
4
|
-
static_host: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
-
};
|
|
6
|
-
static description: string;
|
|
7
|
-
static examples: string[];
|
|
8
|
-
static flags: {
|
|
9
|
-
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
-
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
-
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
-
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
-
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
-
};
|
|
15
|
-
run(): Promise<void>;
|
|
16
|
-
/** Print a one-line summary for a dev/prod env if it has been deployed. */
|
|
17
|
-
private logEnv;
|
|
18
|
-
}
|