@xano/cli 1.0.6-beta.0 → 1.0.6-beta.1
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 +28 -3
- package/dist/base-command.d.ts +2 -0
- package/dist/base-command.js +10 -4
- package/dist/commands/ephemeral/pull/index.d.ts +3 -1
- package/dist/commands/ephemeral/pull/index.js +15 -14
- package/dist/commands/ephemeral/push/index.d.ts +3 -1
- package/dist/commands/ephemeral/push/index.js +17 -16
- package/dist/commands/ephemeral/static_host/build/delete/index.d.ts +20 -0
- package/dist/commands/ephemeral/static_host/build/delete/index.js +121 -0
- package/dist/commands/ephemeral/static_host/build/get/index.d.ts +18 -0
- package/dist/commands/ephemeral/static_host/build/get/index.js +137 -0
- package/dist/commands/ephemeral/static_host/build/list/index.d.ts +19 -0
- package/dist/commands/ephemeral/static_host/build/list/index.js +158 -0
- package/dist/commands/ephemeral/static_host/build/pull/index.d.ts +53 -0
- package/dist/commands/ephemeral/static_host/build/pull/index.js +308 -0
- package/dist/commands/ephemeral/static_host/build/push/index.d.ts +24 -0
- package/dist/commands/ephemeral/static_host/build/push/index.js +250 -0
- package/dist/commands/ephemeral/static_host/create/index.d.ts +18 -0
- package/dist/commands/ephemeral/static_host/create/index.js +92 -0
- package/dist/commands/ephemeral/static_host/deploy/index.d.ts +19 -0
- package/dist/commands/ephemeral/static_host/deploy/index.js +112 -0
- package/dist/commands/ephemeral/static_host/edit/index.d.ts +24 -0
- package/dist/commands/ephemeral/static_host/edit/index.js +158 -0
- package/dist/commands/ephemeral/static_host/get/index.d.ts +19 -0
- package/dist/commands/ephemeral/static_host/get/index.js +101 -0
- package/dist/commands/ephemeral/static_host/list/index.d.ts +18 -0
- package/dist/commands/ephemeral/static_host/list/index.js +152 -0
- package/oclif.manifest.json +3932 -2741
- package/package.json +7 -1
package/README.md
CHANGED
|
@@ -587,15 +587,40 @@ xano ephemeral edit <tenant_name> --display "New Name" -d "New description"
|
|
|
587
587
|
xano ephemeral delete <tenant_name> --force
|
|
588
588
|
|
|
589
589
|
# Pull to / push from local files (multidoc)
|
|
590
|
-
xano ephemeral pull
|
|
591
|
-
xano ephemeral push
|
|
592
|
-
xano ephemeral push
|
|
590
|
+
xano ephemeral pull <tenant_name> -d ./my-ephemeral
|
|
591
|
+
xano ephemeral push <tenant_name> -d ./my-ephemeral --dry-run # preview first
|
|
592
|
+
xano ephemeral push <tenant_name> -d ./my-ephemeral
|
|
593
593
|
|
|
594
594
|
# Open in the browser (or print the URL)
|
|
595
595
|
xano ephemeral impersonate <tenant_name>
|
|
596
596
|
xano ephemeral impersonate <tenant_name> --url-only
|
|
597
597
|
```
|
|
598
598
|
|
|
599
|
+
#### Static hosting for an ephemeral tenant
|
|
600
|
+
|
|
601
|
+
An ephemeral tenant can host static sites, scoped to that tenant. These commands mirror
|
|
602
|
+
`xano static_host *` but take the tenant name as the first argument (the tenant's static
|
|
603
|
+
hosting is isolated inside the tenant's own database).
|
|
604
|
+
|
|
605
|
+
```bash
|
|
606
|
+
# List / create / inspect a tenant's static hosts
|
|
607
|
+
xano ephemeral static_host list <tenant_name>
|
|
608
|
+
xano ephemeral static_host create <tenant_name> --name marketing --description "Marketing site"
|
|
609
|
+
xano ephemeral static_host get <tenant_name> -H marketing
|
|
610
|
+
xano ephemeral static_host edit <tenant_name> -H marketing --description "Updated"
|
|
611
|
+
|
|
612
|
+
# Builds: push a directory, list, inspect, deploy to an env, pull, delete
|
|
613
|
+
xano ephemeral static_host build push <tenant_name> -H default -f ./site
|
|
614
|
+
xano ephemeral static_host build list <tenant_name> -H default
|
|
615
|
+
xano ephemeral static_host build get <tenant_name> -H default --build_id 52
|
|
616
|
+
xano ephemeral static_host deploy <tenant_name> -H default --build_id 52 --env prod
|
|
617
|
+
xano ephemeral static_host build pull <tenant_name> -H default --latest
|
|
618
|
+
xano ephemeral static_host build delete <tenant_name> -H default --build_id 52
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
> Static hosting is currently available for **local** tenants. Remote (tier2/tier3)
|
|
622
|
+
> tenants are not yet supported and will return an error.
|
|
623
|
+
|
|
599
624
|
### Sandbox
|
|
600
625
|
|
|
601
626
|
Manage your sandbox tenant. Each user has a single sandbox tenant that is auto-provisioned on first use.
|
package/dist/base-command.d.ts
CHANGED
|
@@ -117,6 +117,7 @@ export default abstract class BaseCommand extends Command {
|
|
|
117
117
|
protected logStaticHostUrls(opts: {
|
|
118
118
|
profile: ProfileConfig;
|
|
119
119
|
staticHost: string;
|
|
120
|
+
tenantName?: string;
|
|
120
121
|
verbose: boolean;
|
|
121
122
|
workspaceId: string;
|
|
122
123
|
}): Promise<void>;
|
|
@@ -126,6 +127,7 @@ export default abstract class BaseCommand extends Command {
|
|
|
126
127
|
profile: ProfileConfig;
|
|
127
128
|
quiet?: boolean;
|
|
128
129
|
staticHost: string;
|
|
130
|
+
tenantName?: string;
|
|
129
131
|
timeoutMs?: number;
|
|
130
132
|
verbose: boolean;
|
|
131
133
|
workspaceId: string;
|
package/dist/base-command.js
CHANGED
|
@@ -370,8 +370,11 @@ export default class BaseCommand extends Command {
|
|
|
370
370
|
* Returns the final status. Resolves to the last-seen status on timeout.
|
|
371
371
|
*/
|
|
372
372
|
async logStaticHostUrls(opts) {
|
|
373
|
-
const { profile, staticHost, verbose, workspaceId } = opts;
|
|
374
|
-
|
|
373
|
+
const { profile, staticHost, tenantName, verbose, workspaceId } = opts;
|
|
374
|
+
// When a tenant is scoped, target the tenant's static hosting; otherwise the
|
|
375
|
+
// workspace-level static host (default, unchanged behavior).
|
|
376
|
+
const scope = tenantName ? `/tenant/${tenantName}` : '';
|
|
377
|
+
const url = `${profile.instance_origin}/api:meta/workspace/${workspaceId}${scope}/static_host/${staticHost}`;
|
|
375
378
|
try {
|
|
376
379
|
const response = await this.verboseFetch(url, {
|
|
377
380
|
headers: { accept: 'application/json', Authorization: `Bearer ${profile.access_token}` },
|
|
@@ -394,11 +397,14 @@ export default class BaseCommand extends Command {
|
|
|
394
397
|
}
|
|
395
398
|
}
|
|
396
399
|
async waitForBuild(opts) {
|
|
397
|
-
const { buildId, profile, quiet, staticHost, verbose, workspaceId } = opts;
|
|
400
|
+
const { buildId, profile, quiet, staticHost, tenantName, verbose, workspaceId } = opts;
|
|
398
401
|
const intervalMs = opts.intervalMs ?? 2000;
|
|
399
402
|
const timeoutMs = opts.timeoutMs ?? 600_000; // 10 min
|
|
400
403
|
const terminal = new Set(['error', 'ok']);
|
|
401
|
-
|
|
404
|
+
// When a tenant is scoped, target the tenant's static hosting; otherwise the
|
|
405
|
+
// workspace-level static host (default, unchanged behavior).
|
|
406
|
+
const scope = tenantName ? `/tenant/${tenantName}` : '';
|
|
407
|
+
const url = `${profile.instance_origin}/api:meta/workspace/${workspaceId}${scope}/static_host/${staticHost}/build/${buildId}`;
|
|
402
408
|
// Spinner only on an interactive TTY and when not emitting JSON. Verbose mode
|
|
403
409
|
// also disables it (the spinner would interleave with request/response logs).
|
|
404
410
|
const animate = Boolean(process.stdout.isTTY) && !quiet && !verbose;
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import BaseCommand from '../../../base-command.js';
|
|
2
2
|
export default class EphemeralPull extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
tenant_name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
3
6
|
static description: string;
|
|
4
7
|
static examples: string[];
|
|
5
8
|
static flags: {
|
|
@@ -7,7 +10,6 @@ export default class EphemeralPull extends BaseCommand {
|
|
|
7
10
|
draft: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
11
|
env: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
12
|
records: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
-
tenant: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
13
|
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
14
|
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
15
|
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -1,25 +1,31 @@
|
|
|
1
|
-
import { Flags } from '@oclif/core';
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
3
|
import * as path from 'node:path';
|
|
4
4
|
import snakeCase from 'lodash.snakecase';
|
|
5
5
|
import BaseCommand from '../../../base-command.js';
|
|
6
6
|
import { buildApiGroupFolderResolver, parseDocument } from '../../../utils/document-parser.js';
|
|
7
7
|
export default class EphemeralPull extends BaseCommand {
|
|
8
|
+
static args = {
|
|
9
|
+
tenant_name: Args.string({
|
|
10
|
+
description: 'Ephemeral tenant name to pull from',
|
|
11
|
+
required: true,
|
|
12
|
+
}),
|
|
13
|
+
};
|
|
8
14
|
static description = 'Pull an ephemeral tenant multidoc from the Xano Metadata API and split into individual files';
|
|
9
15
|
static examples = [
|
|
10
|
-
`$ xano ephemeral pull
|
|
16
|
+
`$ xano ephemeral pull e4f2-9ab1-xyz1
|
|
11
17
|
Pulled 42 documents from tenant e4f2-9ab1-xyz1 to current directory
|
|
12
18
|
`,
|
|
13
|
-
`$ xano ephemeral pull -d ./my-tenant
|
|
19
|
+
`$ xano ephemeral pull e4f2-9ab1-xyz1 -d ./my-tenant
|
|
14
20
|
Pulled 42 documents from tenant e4f2-9ab1-xyz1 to ./my-tenant
|
|
15
21
|
`,
|
|
16
|
-
`$ xano ephemeral pull -d ./output -
|
|
22
|
+
`$ xano ephemeral pull e4f2-9ab1-xyz1 -d ./output -w 40
|
|
17
23
|
Pulled 15 documents from tenant e4f2-9ab1-xyz1 to ./output
|
|
18
24
|
`,
|
|
19
|
-
`$ xano ephemeral pull
|
|
25
|
+
`$ xano ephemeral pull e4f2-9ab1-xyz1 --profile production --env --records
|
|
20
26
|
Pulled 58 documents from tenant e4f2-9ab1-xyz1
|
|
21
27
|
`,
|
|
22
|
-
`$ xano ephemeral pull
|
|
28
|
+
`$ xano ephemeral pull e4f2-9ab1-xyz1 --draft`,
|
|
23
29
|
];
|
|
24
30
|
static flags = {
|
|
25
31
|
...BaseCommand.baseFlags,
|
|
@@ -44,11 +50,6 @@ Pulled 58 documents from tenant e4f2-9ab1-xyz1
|
|
|
44
50
|
description: 'Include records',
|
|
45
51
|
required: false,
|
|
46
52
|
}),
|
|
47
|
-
tenant: Flags.string({
|
|
48
|
-
char: 't',
|
|
49
|
-
description: 'Ephemeral tenant name to pull from',
|
|
50
|
-
required: true,
|
|
51
|
-
}),
|
|
52
53
|
workspace: Flags.string({
|
|
53
54
|
char: 'w',
|
|
54
55
|
description: 'Workspace ID (optional if set in profile)',
|
|
@@ -56,7 +57,7 @@ Pulled 58 documents from tenant e4f2-9ab1-xyz1
|
|
|
56
57
|
}),
|
|
57
58
|
};
|
|
58
59
|
async run() {
|
|
59
|
-
const { flags } = await this.parse(EphemeralPull);
|
|
60
|
+
const { args, flags } = await this.parse(EphemeralPull);
|
|
60
61
|
const { profileName, profile } = this.resolveProfile(flags);
|
|
61
62
|
// Determine workspace_id from flag or profile
|
|
62
63
|
let workspaceId;
|
|
@@ -68,10 +69,10 @@ Pulled 58 documents from tenant e4f2-9ab1-xyz1
|
|
|
68
69
|
}
|
|
69
70
|
else {
|
|
70
71
|
this.error(`Workspace ID is required. Either:\n` +
|
|
71
|
-
` 1. Provide it as a flag: xano ephemeral pull
|
|
72
|
+
` 1. Provide it as a flag: xano ephemeral pull <tenant_name> -w <workspace_id>\n` +
|
|
72
73
|
` 2. Set it in your profile using: xano profile:edit ${profileName} -w <workspace_id>`);
|
|
73
74
|
}
|
|
74
|
-
const tenantName =
|
|
75
|
+
const tenantName = args.tenant_name;
|
|
75
76
|
// Build query parameters
|
|
76
77
|
const queryParams = new URLSearchParams({
|
|
77
78
|
env: flags.env.toString(),
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import BaseCommand from '../../../base-command.js';
|
|
2
2
|
export default class EphemeralPush extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
tenant_name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
3
6
|
static description: string;
|
|
4
7
|
static examples: string[];
|
|
5
8
|
static flags: {
|
|
@@ -11,7 +14,6 @@ export default class EphemeralPush extends BaseCommand {
|
|
|
11
14
|
guids: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
15
|
records: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
16
|
sync: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
-
tenant: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
17
|
transaction: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
18
|
truncate: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
19
|
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -1,31 +1,37 @@
|
|
|
1
|
-
import { Flags } from '@oclif/core';
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
3
|
import { resolve } from 'node:path';
|
|
4
4
|
import BaseCommand from '../../../base-command.js';
|
|
5
5
|
import { executePush } from '../../../utils/multidoc-push.js';
|
|
6
6
|
export default class EphemeralPush extends BaseCommand {
|
|
7
|
+
static args = {
|
|
8
|
+
tenant_name: Args.string({
|
|
9
|
+
description: 'Ephemeral tenant name to push to',
|
|
10
|
+
required: true,
|
|
11
|
+
}),
|
|
12
|
+
};
|
|
7
13
|
static description = '[IMPORTANT] ALWAYS run --dry-run first and show the user the output before pushing. Push local documents to an ephemeral tenant via multidoc import. By default, only changed files are pushed (partial mode). Use --sync to push all files. Shows a preview of changes before pushing unless --force is specified. Use --dry-run to preview only.';
|
|
8
14
|
static examples = [
|
|
9
|
-
`$ xano ephemeral push
|
|
15
|
+
`$ xano ephemeral push e4f2-9ab1-xyz1
|
|
10
16
|
Push from current directory (default partial mode)
|
|
11
17
|
`,
|
|
12
|
-
`$ xano ephemeral push
|
|
18
|
+
`$ xano ephemeral push e4f2-9ab1-xyz1 -d ./my-workspace
|
|
13
19
|
Push from a specific directory
|
|
14
20
|
`,
|
|
15
|
-
`$ xano ephemeral push
|
|
21
|
+
`$ xano ephemeral push e4f2-9ab1-xyz1 --sync
|
|
16
22
|
Push all files to the ephemeral tenant
|
|
17
23
|
`,
|
|
18
|
-
`$ xano ephemeral push
|
|
24
|
+
`$ xano ephemeral push e4f2-9ab1-xyz1 --sync --delete
|
|
19
25
|
Push all files and delete remote objects not included
|
|
20
26
|
`,
|
|
21
|
-
`$ xano ephemeral push
|
|
27
|
+
`$ xano ephemeral push e4f2-9ab1-xyz1 --dry-run
|
|
22
28
|
Preview changes without pushing
|
|
23
29
|
`,
|
|
24
|
-
`$ xano ephemeral push
|
|
30
|
+
`$ xano ephemeral push e4f2-9ab1-xyz1 --force
|
|
25
31
|
Skip preview and push immediately
|
|
26
32
|
`,
|
|
27
|
-
`$ xano ephemeral push
|
|
28
|
-
`$ xano ephemeral push
|
|
33
|
+
`$ xano ephemeral push e4f2-9ab1-xyz1 --records --env`,
|
|
34
|
+
`$ xano ephemeral push e4f2-9ab1-xyz1 --truncate`,
|
|
29
35
|
];
|
|
30
36
|
static flags = {
|
|
31
37
|
...BaseCommand.baseFlags,
|
|
@@ -71,11 +77,6 @@ Skip preview and push immediately
|
|
|
71
77
|
description: 'Full push — send all files, not just changed ones. Required for --delete.',
|
|
72
78
|
required: false,
|
|
73
79
|
}),
|
|
74
|
-
tenant: Flags.string({
|
|
75
|
-
char: 't',
|
|
76
|
-
description: 'Ephemeral tenant name to push to',
|
|
77
|
-
required: true,
|
|
78
|
-
}),
|
|
79
80
|
transaction: Flags.boolean({
|
|
80
81
|
allowNo: true,
|
|
81
82
|
default: true,
|
|
@@ -94,7 +95,7 @@ Skip preview and push immediately
|
|
|
94
95
|
}),
|
|
95
96
|
};
|
|
96
97
|
async run() {
|
|
97
|
-
const { flags } = await this.parse(EphemeralPush);
|
|
98
|
+
const { args, flags } = await this.parse(EphemeralPush);
|
|
98
99
|
const { profile } = this.resolveProfile(flags);
|
|
99
100
|
const workspaceId = flags.workspace || profile.workspace;
|
|
100
101
|
if (!workspaceId) {
|
|
@@ -107,7 +108,7 @@ Skip preview and push immediately
|
|
|
107
108
|
if (!fs.statSync(inputDir).isDirectory()) {
|
|
108
109
|
this.error(`Not a directory: ${inputDir}`);
|
|
109
110
|
}
|
|
110
|
-
const tenantName =
|
|
111
|
+
const tenantName = args.tenant_name;
|
|
111
112
|
const baseUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/tenant/${tenantName}`;
|
|
112
113
|
const target = {
|
|
113
114
|
buildDryRunUrl: (params) => `${baseUrl}/multidoc/dry-run?${params.toString()}`,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import BaseCommand from '../../../../../base-command.js';
|
|
2
|
+
export default class EphemeralStaticHostBuildDelete extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
tenant_name: 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
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
'static-host': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
|
+
};
|
|
18
|
+
run(): Promise<void>;
|
|
19
|
+
private confirm;
|
|
20
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import BaseCommand from '../../../../../base-command.js';
|
|
3
|
+
export default class EphemeralStaticHostBuildDelete extends BaseCommand {
|
|
4
|
+
static args = {
|
|
5
|
+
tenant_name: Args.string({
|
|
6
|
+
description: 'Ephemeral tenant name',
|
|
7
|
+
required: true,
|
|
8
|
+
}),
|
|
9
|
+
};
|
|
10
|
+
static description = 'Delete a static host build for an ephemeral tenant permanently. This action cannot be undone.';
|
|
11
|
+
static examples = [
|
|
12
|
+
`$ xano ephemeral static_host build delete e4f2-9ab1-xyz1 --static-host default --build_id 52
|
|
13
|
+
Are you sure you want to delete build 52 from static host 'default'? This action cannot be undone. (y/N) y
|
|
14
|
+
Deleted build 52 from static host 'default'
|
|
15
|
+
`,
|
|
16
|
+
`$ xano ephemeral static_host build delete e4f2-9ab1-xyz1 -H default --build_id 52 --force
|
|
17
|
+
Deleted build 52 from static host 'default'
|
|
18
|
+
`,
|
|
19
|
+
`$ xano ephemeral static_host build delete e4f2-9ab1-xyz1 -H myhost --build_id 123 -w 40 -f
|
|
20
|
+
Deleted build 123 from static host 'myhost'
|
|
21
|
+
`,
|
|
22
|
+
`$ xano ephemeral static_host build delete e4f2-9ab1-xyz1 -H default --build_id 52 -f -o json`,
|
|
23
|
+
];
|
|
24
|
+
static flags = {
|
|
25
|
+
...BaseCommand.baseFlags,
|
|
26
|
+
build_id: Flags.string({
|
|
27
|
+
description: 'Build ID to delete',
|
|
28
|
+
required: true,
|
|
29
|
+
}),
|
|
30
|
+
force: Flags.boolean({
|
|
31
|
+
char: 'f',
|
|
32
|
+
default: false,
|
|
33
|
+
description: '[CRITICAL] NEVER run without explicit user confirmation. Skips the confirmation prompt.',
|
|
34
|
+
required: false,
|
|
35
|
+
}),
|
|
36
|
+
output: Flags.string({
|
|
37
|
+
char: 'o',
|
|
38
|
+
default: 'summary',
|
|
39
|
+
description: 'Output format',
|
|
40
|
+
options: ['summary', 'json'],
|
|
41
|
+
required: false,
|
|
42
|
+
}),
|
|
43
|
+
'static-host': Flags.string({
|
|
44
|
+
char: 'H',
|
|
45
|
+
description: 'Static host name',
|
|
46
|
+
required: true,
|
|
47
|
+
}),
|
|
48
|
+
workspace: Flags.string({
|
|
49
|
+
char: 'w',
|
|
50
|
+
description: 'Workspace ID (optional if set in profile)',
|
|
51
|
+
required: false,
|
|
52
|
+
}),
|
|
53
|
+
};
|
|
54
|
+
async run() {
|
|
55
|
+
const { args, flags } = await this.parse(EphemeralStaticHostBuildDelete);
|
|
56
|
+
const { profile, profileName } = this.resolveProfile(flags);
|
|
57
|
+
const tenantName = args.tenant_name;
|
|
58
|
+
const staticHost = flags['static-host'];
|
|
59
|
+
// Determine workspace_id from flag or profile
|
|
60
|
+
let workspaceId;
|
|
61
|
+
if (flags.workspace) {
|
|
62
|
+
workspaceId = flags.workspace;
|
|
63
|
+
}
|
|
64
|
+
else if (profile.workspace) {
|
|
65
|
+
workspaceId = profile.workspace;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
this.error(`Workspace ID is required. Either:\n` +
|
|
69
|
+
` 1. Provide it as a flag: xano ephemeral static_host build delete <tenant_name> --static-host <static_host> --build_id <id> -w <workspace_id>\n` +
|
|
70
|
+
` 2. Set it in your profile using: xano profile edit ${profileName} -w <workspace_id>`);
|
|
71
|
+
}
|
|
72
|
+
if (!flags.force) {
|
|
73
|
+
const confirmed = await this.confirm(`Are you sure you want to delete build ${flags.build_id} from static host '${staticHost}'? This action cannot be undone.`);
|
|
74
|
+
if (!confirmed) {
|
|
75
|
+
this.log('Deletion cancelled.');
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/tenant/${tenantName}/static_host/${staticHost}/build/${flags.build_id}`;
|
|
80
|
+
try {
|
|
81
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
82
|
+
headers: {
|
|
83
|
+
'accept': 'application/json',
|
|
84
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
85
|
+
},
|
|
86
|
+
method: 'DELETE',
|
|
87
|
+
}, flags.verbose, profile.access_token);
|
|
88
|
+
if (!response.ok) {
|
|
89
|
+
const errorText = await response.text();
|
|
90
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
91
|
+
}
|
|
92
|
+
if (flags.output === 'json') {
|
|
93
|
+
this.log(JSON.stringify({ build_id: flags.build_id, deleted: true, static_host: staticHost }, null, 2));
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
this.log(`Deleted build ${flags.build_id} from static host '${staticHost}'`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
if (error instanceof Error) {
|
|
101
|
+
this.error(`Failed to delete build: ${error.message}`);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
this.error(`Failed to delete build: ${String(error)}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async confirm(message) {
|
|
109
|
+
const readline = await import('node:readline');
|
|
110
|
+
const rl = readline.createInterface({
|
|
111
|
+
input: process.stdin,
|
|
112
|
+
output: process.stdout,
|
|
113
|
+
});
|
|
114
|
+
return new Promise((resolve) => {
|
|
115
|
+
rl.question(`${message} (y/N) `, (answer) => {
|
|
116
|
+
rl.close();
|
|
117
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import BaseCommand from '../../../../../base-command.js';
|
|
2
|
+
export default class EphemeralStaticHostBuildGet extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
tenant_name: 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
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
'static-host': 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
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import BaseCommand from '../../../../../base-command.js';
|
|
3
|
+
export default class EphemeralStaticHostBuildGet extends BaseCommand {
|
|
4
|
+
static args = {
|
|
5
|
+
tenant_name: Args.string({
|
|
6
|
+
description: 'Ephemeral tenant name',
|
|
7
|
+
required: true,
|
|
8
|
+
}),
|
|
9
|
+
};
|
|
10
|
+
static description = 'Get details of a specific build for a static host of an ephemeral tenant';
|
|
11
|
+
static examples = [
|
|
12
|
+
`$ xano ephemeral static_host build get e4f2-9ab1-xyz1 --static-host default --build_id 52
|
|
13
|
+
Build Details:
|
|
14
|
+
ID: 52
|
|
15
|
+
Name: v1.0.0
|
|
16
|
+
Status: completed
|
|
17
|
+
`,
|
|
18
|
+
`$ xano ephemeral static_host build get e4f2-9ab1-xyz1 -H default --build_id 52 -w 40
|
|
19
|
+
Build Details:
|
|
20
|
+
ID: 52
|
|
21
|
+
Name: v1.0.0
|
|
22
|
+
Status: completed
|
|
23
|
+
`,
|
|
24
|
+
`$ xano ephemeral static_host build get e4f2-9ab1-xyz1 -H myhost --build_id 123 --profile production
|
|
25
|
+
Build Details:
|
|
26
|
+
ID: 123
|
|
27
|
+
Name: production-build
|
|
28
|
+
`,
|
|
29
|
+
`$ xano ephemeral static_host build get e4f2-9ab1-xyz1 -H default --build_id 52 -o json
|
|
30
|
+
{
|
|
31
|
+
"id": 52,
|
|
32
|
+
"name": "v1.0.0",
|
|
33
|
+
"status": "completed"
|
|
34
|
+
}
|
|
35
|
+
`,
|
|
36
|
+
];
|
|
37
|
+
static flags = {
|
|
38
|
+
...BaseCommand.baseFlags,
|
|
39
|
+
build_id: Flags.string({
|
|
40
|
+
description: 'Build ID',
|
|
41
|
+
required: true,
|
|
42
|
+
}),
|
|
43
|
+
output: Flags.string({
|
|
44
|
+
char: 'o',
|
|
45
|
+
default: 'summary',
|
|
46
|
+
description: 'Output format',
|
|
47
|
+
options: ['summary', 'json'],
|
|
48
|
+
required: false,
|
|
49
|
+
}),
|
|
50
|
+
'static-host': Flags.string({
|
|
51
|
+
char: 'H',
|
|
52
|
+
description: 'Static host name',
|
|
53
|
+
required: true,
|
|
54
|
+
}),
|
|
55
|
+
workspace: Flags.string({
|
|
56
|
+
char: 'w',
|
|
57
|
+
description: 'Workspace ID (optional if set in profile)',
|
|
58
|
+
required: false,
|
|
59
|
+
}),
|
|
60
|
+
};
|
|
61
|
+
async run() {
|
|
62
|
+
const { args, flags } = await this.parse(EphemeralStaticHostBuildGet);
|
|
63
|
+
const { profile, profileName } = this.resolveProfile(flags);
|
|
64
|
+
const tenantName = args.tenant_name;
|
|
65
|
+
const staticHost = flags['static-host'];
|
|
66
|
+
// Determine workspace_id from flag or profile
|
|
67
|
+
let workspaceId;
|
|
68
|
+
if (flags.workspace) {
|
|
69
|
+
workspaceId = flags.workspace;
|
|
70
|
+
}
|
|
71
|
+
else if (profile.workspace) {
|
|
72
|
+
workspaceId = profile.workspace;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
this.error(`Workspace ID is required. Either:\n` +
|
|
76
|
+
` 1. Provide it as a flag: xano ephemeral static_host build get <tenant_name> --static-host <static_host> --build_id <id> -w <workspace_id>\n` +
|
|
77
|
+
` 2. Set it in your profile using: xano profile:edit ${profileName} -w <workspace_id>`);
|
|
78
|
+
}
|
|
79
|
+
// Construct the API URL
|
|
80
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/tenant/${tenantName}/static_host/${staticHost}/build/${flags.build_id}`;
|
|
81
|
+
// Fetch build from the API
|
|
82
|
+
try {
|
|
83
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
84
|
+
headers: {
|
|
85
|
+
'accept': 'application/json',
|
|
86
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
87
|
+
},
|
|
88
|
+
method: 'GET',
|
|
89
|
+
}, flags.verbose, profile.access_token);
|
|
90
|
+
if (!response.ok) {
|
|
91
|
+
const errorText = await response.text();
|
|
92
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
93
|
+
}
|
|
94
|
+
const build = await response.json();
|
|
95
|
+
// Validate response
|
|
96
|
+
if (!build || typeof build !== 'object') {
|
|
97
|
+
this.error('Unexpected API response format');
|
|
98
|
+
}
|
|
99
|
+
// Output results
|
|
100
|
+
if (flags.output === 'json') {
|
|
101
|
+
this.log(JSON.stringify(build, null, 2));
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// summary format
|
|
105
|
+
this.log('Build Details:');
|
|
106
|
+
this.log(`ID: ${build.id}`);
|
|
107
|
+
this.log(`Name: ${build.name}`);
|
|
108
|
+
if (build.description) {
|
|
109
|
+
this.log(`Description: ${build.description}`);
|
|
110
|
+
}
|
|
111
|
+
if (build.status) {
|
|
112
|
+
this.log(`Status: ${build.status}`);
|
|
113
|
+
}
|
|
114
|
+
if (typeof build.file_count === 'number') {
|
|
115
|
+
this.log(`Files: ${build.file_count}`);
|
|
116
|
+
}
|
|
117
|
+
if (typeof build.file_bytes === 'number') {
|
|
118
|
+
this.log(`Size: ${build.file_bytes} bytes`);
|
|
119
|
+
}
|
|
120
|
+
if (build.created_at) {
|
|
121
|
+
this.log(`Created: ${build.created_at}`);
|
|
122
|
+
}
|
|
123
|
+
if (build.updated_at) {
|
|
124
|
+
this.log(`Updated: ${build.updated_at}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
if (error instanceof Error) {
|
|
130
|
+
this.error(`Failed to fetch build: ${error.message}`);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
this.error(`Failed to fetch build: ${String(error)}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import BaseCommand from '../../../../../base-command.js';
|
|
2
|
+
export default class EphemeralStaticHostBuildList extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
tenant_name: 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
|
+
page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
per_page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
'static-host': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
|
+
};
|
|
18
|
+
run(): Promise<void>;
|
|
19
|
+
}
|