@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.
Files changed (29) hide show
  1. package/README.md +28 -3
  2. package/dist/base-command.d.ts +2 -0
  3. package/dist/base-command.js +10 -4
  4. package/dist/commands/ephemeral/pull/index.d.ts +3 -1
  5. package/dist/commands/ephemeral/pull/index.js +15 -14
  6. package/dist/commands/ephemeral/push/index.d.ts +3 -1
  7. package/dist/commands/ephemeral/push/index.js +17 -16
  8. package/dist/commands/ephemeral/static_host/build/delete/index.d.ts +20 -0
  9. package/dist/commands/ephemeral/static_host/build/delete/index.js +121 -0
  10. package/dist/commands/ephemeral/static_host/build/get/index.d.ts +18 -0
  11. package/dist/commands/ephemeral/static_host/build/get/index.js +137 -0
  12. package/dist/commands/ephemeral/static_host/build/list/index.d.ts +19 -0
  13. package/dist/commands/ephemeral/static_host/build/list/index.js +158 -0
  14. package/dist/commands/ephemeral/static_host/build/pull/index.d.ts +53 -0
  15. package/dist/commands/ephemeral/static_host/build/pull/index.js +308 -0
  16. package/dist/commands/ephemeral/static_host/build/push/index.d.ts +24 -0
  17. package/dist/commands/ephemeral/static_host/build/push/index.js +250 -0
  18. package/dist/commands/ephemeral/static_host/create/index.d.ts +18 -0
  19. package/dist/commands/ephemeral/static_host/create/index.js +92 -0
  20. package/dist/commands/ephemeral/static_host/deploy/index.d.ts +19 -0
  21. package/dist/commands/ephemeral/static_host/deploy/index.js +112 -0
  22. package/dist/commands/ephemeral/static_host/edit/index.d.ts +24 -0
  23. package/dist/commands/ephemeral/static_host/edit/index.js +158 -0
  24. package/dist/commands/ephemeral/static_host/get/index.d.ts +19 -0
  25. package/dist/commands/ephemeral/static_host/get/index.js +101 -0
  26. package/dist/commands/ephemeral/static_host/list/index.d.ts +18 -0
  27. package/dist/commands/ephemeral/static_host/list/index.js +152 -0
  28. package/oclif.manifest.json +3932 -2741
  29. 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 -t <tenant_name> -d ./my-ephemeral
591
- xano ephemeral push -t <tenant_name> -d ./my-ephemeral --dry-run # preview first
592
- xano ephemeral push -t <tenant_name> -d ./my-ephemeral
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.
@@ -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;
@@ -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
- const url = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/static_host/${staticHost}`;
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
- const url = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/static_host/${staticHost}/build/${buildId}`;
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 -t e4f2-9ab1-xyz1
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 -t e4f2-9ab1-xyz1
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 -t e4f2-9ab1-xyz1 -w 40
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 -t e4f2-9ab1-xyz1 --profile production --env --records
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 -t e4f2-9ab1-xyz1 --draft`,
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 -t <tenant_name> -w <workspace_id>\n` +
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 = flags.tenant;
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 -t e4f2-9ab1-xyz1
15
+ `$ xano ephemeral push e4f2-9ab1-xyz1
10
16
  Push from current directory (default partial mode)
11
17
  `,
12
- `$ xano ephemeral push -t e4f2-9ab1-xyz1 -d ./my-workspace
18
+ `$ xano ephemeral push e4f2-9ab1-xyz1 -d ./my-workspace
13
19
  Push from a specific directory
14
20
  `,
15
- `$ xano ephemeral push -t e4f2-9ab1-xyz1 --sync
21
+ `$ xano ephemeral push e4f2-9ab1-xyz1 --sync
16
22
  Push all files to the ephemeral tenant
17
23
  `,
18
- `$ xano ephemeral push -t e4f2-9ab1-xyz1 --sync --delete
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 -t e4f2-9ab1-xyz1 --dry-run
27
+ `$ xano ephemeral push e4f2-9ab1-xyz1 --dry-run
22
28
  Preview changes without pushing
23
29
  `,
24
- `$ xano ephemeral push -t e4f2-9ab1-xyz1 --force
30
+ `$ xano ephemeral push e4f2-9ab1-xyz1 --force
25
31
  Skip preview and push immediately
26
32
  `,
27
- `$ xano ephemeral push -t e4f2-9ab1-xyz1 --records --env`,
28
- `$ xano ephemeral push -t e4f2-9ab1-xyz1 --truncate`,
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 = flags.tenant;
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
+ }