@xano/cli 0.0.95-beta.3 → 0.0.95

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 (75) hide show
  1. package/README.md +2 -27
  2. package/dist/base-command.d.ts +0 -24
  3. package/dist/base-command.js +0 -37
  4. package/dist/commands/tenant/create/index.d.ts +3 -1
  5. package/dist/commands/tenant/create/index.js +28 -6
  6. package/dist/commands/tenant/get/index.js +2 -2
  7. package/dist/commands/tenant/list/index.js +2 -2
  8. package/dist/commands/tenant/push/index.js +34 -0
  9. package/dist/commands/workspace/push/index.js +0 -26
  10. package/dist/help.d.ts +1 -2
  11. package/dist/help.js +1 -39
  12. package/oclif.manifest.json +2059 -4455
  13. package/package.json +1 -16
  14. package/dist/commands/release/deploy/index.d.ts +0 -17
  15. package/dist/commands/release/deploy/index.js +0 -107
  16. package/dist/commands/sandbox/env/delete/index.d.ts +0 -14
  17. package/dist/commands/sandbox/env/delete/index.js +0 -89
  18. package/dist/commands/sandbox/env/get/index.d.ts +0 -12
  19. package/dist/commands/sandbox/env/get/index.js +0 -65
  20. package/dist/commands/sandbox/env/get_all/index.d.ts +0 -13
  21. package/dist/commands/sandbox/env/get_all/index.js +0 -78
  22. package/dist/commands/sandbox/env/list/index.d.ts +0 -11
  23. package/dist/commands/sandbox/env/list/index.js +0 -67
  24. package/dist/commands/sandbox/env/set/index.d.ts +0 -13
  25. package/dist/commands/sandbox/env/set/index.js +0 -74
  26. package/dist/commands/sandbox/env/set_all/index.d.ts +0 -13
  27. package/dist/commands/sandbox/env/set_all/index.js +0 -86
  28. package/dist/commands/sandbox/get/index.d.ts +0 -11
  29. package/dist/commands/sandbox/get/index.js +0 -48
  30. package/dist/commands/sandbox/impersonate/index.d.ts +0 -5
  31. package/dist/commands/sandbox/impersonate/index.js +0 -5
  32. package/dist/commands/sandbox/license/get/index.d.ts +0 -13
  33. package/dist/commands/sandbox/license/get/index.js +0 -78
  34. package/dist/commands/sandbox/license/set/index.d.ts +0 -14
  35. package/dist/commands/sandbox/license/set/index.js +0 -95
  36. package/dist/commands/sandbox/pull/index.d.ts +0 -17
  37. package/dist/commands/sandbox/pull/index.js +0 -182
  38. package/dist/commands/sandbox/push/index.d.ts +0 -18
  39. package/dist/commands/sandbox/push/index.js +0 -143
  40. package/dist/commands/sandbox/reset/index.d.ts +0 -12
  41. package/dist/commands/sandbox/reset/index.js +0 -71
  42. package/dist/commands/sandbox/review/index.d.ts +0 -13
  43. package/dist/commands/sandbox/review/index.js +0 -94
  44. package/dist/commands/sandbox/unit_test/list/index.d.ts +0 -13
  45. package/dist/commands/sandbox/unit_test/list/index.js +0 -91
  46. package/dist/commands/sandbox/unit_test/run/index.d.ts +0 -14
  47. package/dist/commands/sandbox/unit_test/run/index.js +0 -79
  48. package/dist/commands/sandbox/unit_test/run_all/index.d.ts +0 -13
  49. package/dist/commands/sandbox/unit_test/run_all/index.js +0 -169
  50. package/dist/commands/sandbox/workflow_test/delete/index.d.ts +0 -17
  51. package/dist/commands/sandbox/workflow_test/delete/index.js +0 -61
  52. package/dist/commands/sandbox/workflow_test/get/index.d.ts +0 -17
  53. package/dist/commands/sandbox/workflow_test/get/index.js +0 -60
  54. package/dist/commands/sandbox/workflow_test/list/index.d.ts +0 -12
  55. package/dist/commands/sandbox/workflow_test/list/index.js +0 -84
  56. package/dist/commands/sandbox/workflow_test/run/index.d.ts +0 -17
  57. package/dist/commands/sandbox/workflow_test/run/index.js +0 -77
  58. package/dist/commands/sandbox/workflow_test/run_all/index.d.ts +0 -12
  59. package/dist/commands/sandbox/workflow_test/run_all/index.js +0 -155
  60. package/dist/commands/tenant/unit_test/list/index.d.ts +0 -15
  61. package/dist/commands/tenant/unit_test/list/index.js +0 -140
  62. package/dist/commands/tenant/unit_test/run/index.d.ts +0 -16
  63. package/dist/commands/tenant/unit_test/run/index.js +0 -128
  64. package/dist/commands/tenant/unit_test/run_all/index.d.ts +0 -15
  65. package/dist/commands/tenant/unit_test/run_all/index.js +0 -215
  66. package/dist/commands/tenant/workflow_test/delete/index.d.ts +0 -19
  67. package/dist/commands/tenant/workflow_test/delete/index.js +0 -110
  68. package/dist/commands/tenant/workflow_test/get/index.d.ts +0 -19
  69. package/dist/commands/tenant/workflow_test/get/index.js +0 -112
  70. package/dist/commands/tenant/workflow_test/list/index.d.ts +0 -14
  71. package/dist/commands/tenant/workflow_test/list/index.js +0 -133
  72. package/dist/commands/tenant/workflow_test/run/index.d.ts +0 -19
  73. package/dist/commands/tenant/workflow_test/run/index.js +0 -126
  74. package/dist/commands/tenant/workflow_test/run_all/index.d.ts +0 -14
  75. package/dist/commands/tenant/workflow_test/run_all/index.js +0 -201
package/README.md CHANGED
@@ -235,11 +235,6 @@ xano release pull ./my-release -r v1.0 --env --records
235
235
  xano release push ./my-release -n "v2.0"
236
236
  xano release push ./my-release -n "v2.0" --hotfix -d "Critical fix"
237
237
  xano release push ./my-release -n "v2.0" --no-records --no-env
238
-
239
- # Deploy a release to its workspace as a new branch
240
- xano release deploy "v1.0"
241
- xano release deploy "v1.0" --branch "restore-v1" --no-set_live
242
- xano release deploy "v1.0" -w 40 -o json
243
238
  ```
244
239
 
245
240
  ### Platforms
@@ -307,7 +302,8 @@ xano tenant get <tenant_name>
307
302
 
308
303
  # Create a tenant
309
304
  xano tenant create "My Tenant"
310
- xano tenant create "My Tenant" -d "Description" --cluster_id 1 --platform_id 5
305
+ xano tenant create "My Tenant" -d "Description" --type tier2 --cluster_id 1 --platform_id 5
306
+ xano tenant create "My Tenant" --type tier2 --cluster_id 1 --license ./license.yaml
311
307
 
312
308
  # Edit a tenant
313
309
  xano tenant edit <tenant_name> --display "New Name" -d "New description"
@@ -445,27 +441,6 @@ xano tenant cluster license set <cluster_id>
445
441
  xano tenant cluster license set <cluster_id> --file ./kubeconfig.yaml
446
442
  ```
447
443
 
448
- ### Sandbox
449
-
450
- Manage your sandbox tenant. Each user has a single sandbox tenant that is auto-provisioned on first use.
451
-
452
- ```bash
453
- # Get your sandbox tenant (creates if needed)
454
- xano sandbox get
455
- xano sandbox get -o json
456
-
457
- # Push/pull workspace data
458
- xano sandbox push ./my-workspace
459
- xano sandbox pull ./my-tenant
460
-
461
- # Impersonate (open in browser)
462
- xano sandbox impersonate
463
-
464
- # Reset all workspace data
465
- xano sandbox reset
466
- xano sandbox reset --force
467
- ```
468
-
469
444
  ### Static Hosts
470
445
 
471
446
  ```bash
@@ -14,16 +14,6 @@ export interface CredentialsFile {
14
14
  };
15
15
  }
16
16
  export declare function buildUserAgent(version: string): string;
17
- export interface SandboxTenant {
18
- created_at?: string;
19
- description?: string;
20
- display?: string;
21
- ephemeral?: boolean;
22
- id: number;
23
- name: string;
24
- state?: string;
25
- xano_domain?: string;
26
- }
27
17
  export default abstract class BaseCommand extends Command {
28
18
  static baseFlags: {
29
19
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -48,20 +38,6 @@ export default abstract class BaseCommand extends Command {
48
38
  * Load and parse the credentials file. Returns null if the file doesn't exist.
49
39
  */
50
40
  protected loadCredentialsFile(): CredentialsFile | null;
51
- /**
52
- * Get or create the singleton sandbox environment for the authenticated user.
53
- * Returns the sandbox object (existing or newly created).
54
- */
55
- protected getOrCreateSandbox(profile: ProfileConfig, verbose: boolean): Promise<SandboxTenant>;
56
- /**
57
- * Resolve profile from flags, validating instance_origin and access_token exist.
58
- */
59
- protected resolveProfile(flags: {
60
- profile?: string;
61
- }): {
62
- profile: ProfileConfig;
63
- profileName: string;
64
- };
65
41
  /**
66
42
  * Make an HTTP request with optional verbose logging.
67
43
  * Use this for all Metadata API calls to support the --verbose flag.
@@ -103,43 +103,6 @@ export default class BaseCommand extends Command {
103
103
  }
104
104
  return null;
105
105
  }
106
- /**
107
- * Get or create the singleton sandbox environment for the authenticated user.
108
- * Returns the sandbox object (existing or newly created).
109
- */
110
- async getOrCreateSandbox(profile, verbose) {
111
- const apiUrl = `${profile.instance_origin}/api:meta/sandbox/me`;
112
- const response = await this.verboseFetch(apiUrl, {
113
- headers: {
114
- accept: 'application/json',
115
- Authorization: `Bearer ${profile.access_token}`,
116
- },
117
- method: 'GET',
118
- }, verbose, profile.access_token);
119
- if (!response.ok) {
120
- const errorText = await response.text();
121
- this.error(`Failed to get sandbox environment: ${response.status} ${response.statusText}\n${errorText}`);
122
- }
123
- return (await response.json());
124
- }
125
- /**
126
- * Resolve profile from flags, validating instance_origin and access_token exist.
127
- */
128
- resolveProfile(flags) {
129
- const profileName = flags.profile || this.getDefaultProfile();
130
- const credentials = this.loadCredentialsFile();
131
- if (!credentials || !(profileName in credentials.profiles)) {
132
- this.error(`Profile '${profileName}' not found.\n` + `Create a profile using 'xano profile create'`);
133
- }
134
- const profile = credentials.profiles[profileName];
135
- if (!profile.instance_origin) {
136
- this.error(`Profile '${profileName}' is missing instance_origin`);
137
- }
138
- if (!profile.access_token) {
139
- this.error(`Profile '${profileName}' is missing access_token`);
140
- }
141
- return { profile, profileName };
142
- }
143
106
  /**
144
107
  * Make an HTTP request with optional verbose logging.
145
108
  * Use this for all Metadata API calls to support the --verbose flag.
@@ -9,10 +9,12 @@ export default class TenantCreate extends BaseCommand {
9
9
  cluster_id: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
10
  description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
11
  domain: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ ephemeral: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
13
  ingress: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
- license: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
14
+ license: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
15
  output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
15
16
  platform_id: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
+ type: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
16
18
  tasks: import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
19
  workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
20
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -16,7 +16,8 @@ export default class TenantCreate extends BaseCommand {
16
16
  `$ xano tenant create "Production"
17
17
  Created tenant: Production (production) - ID: 42
18
18
  `,
19
- `$ xano tenant create "Staging" --description "Staging env" --cluster_id 1 --platform_id 1 --license tier2 -o json`,
19
+ `$ xano tenant create "Staging" --description "Staging env" --cluster_id 1 --platform_id 1 --type tier2 -o json`,
20
+ `$ xano tenant create "Staging" --type tier2 --cluster_id 1 --license ./license.yaml`,
20
21
  ];
21
22
  static flags = {
22
23
  ...BaseCommand.baseFlags,
@@ -33,15 +34,18 @@ Created tenant: Production (production) - ID: 42
33
34
  description: 'Custom domain for the tenant',
34
35
  required: false,
35
36
  }),
37
+ ephemeral: Flags.boolean({
38
+ default: false,
39
+ description: 'Mark tenant as ephemeral (allows push operations)',
40
+ }),
36
41
  ingress: Flags.boolean({
37
42
  allowNo: true,
38
43
  default: true,
39
44
  description: 'Enable ingress',
40
45
  }),
41
46
  license: Flags.string({
42
- default: 'tier1',
43
- description: 'License tier',
44
- options: ['tier1', 'tier2', 'tier3'],
47
+ char: 'l',
48
+ description: 'Path to a license override file to apply during creation',
45
49
  required: false,
46
50
  }),
47
51
  output: Flags.string({
@@ -55,6 +59,12 @@ Created tenant: Production (production) - ID: 42
55
59
  description: 'Platform ID to use',
56
60
  required: false,
57
61
  }),
62
+ type: Flags.string({
63
+ default: 'tier1',
64
+ description: 'Tenant type',
65
+ options: ['tier1', 'tier2', 'tier3'],
66
+ required: false,
67
+ }),
58
68
  tasks: Flags.boolean({
59
69
  allowNo: true,
60
70
  default: true,
@@ -87,8 +97,9 @@ Created tenant: Production (production) - ID: 42
87
97
  }
88
98
  const body = {
89
99
  display: args.display,
100
+ ephemeral: flags.ephemeral,
90
101
  ingress: flags.ingress,
91
- license: flags.license,
102
+ license: flags.type,
92
103
  tag: [],
93
104
  tasks: flags.tasks,
94
105
  };
@@ -102,7 +113,16 @@ Created tenant: Production (production) - ID: 42
102
113
  body.platform_id = flags.platform_id;
103
114
  if (flags.domain)
104
115
  body.domain = flags.domain;
105
- if (flags.license === 'tier2' || flags.license === 'tier3' || flags.cluster_id) {
116
+ if (flags.license) {
117
+ const licensePath = path.resolve(flags.license);
118
+ if (!fs.existsSync(licensePath)) {
119
+ this.error(`License file not found: ${licensePath}`);
120
+ }
121
+ const licenseContent = fs.readFileSync(licensePath, 'utf8');
122
+ body.license_overrides = yaml.load(licenseContent);
123
+ }
124
+ const effectiveType = flags.cluster_id ? 'tier3' : flags.type;
125
+ if (effectiveType === 'tier2' || effectiveType === 'tier3') {
106
126
  this.warn('This may take a few minutes. Please be patient.');
107
127
  }
108
128
  const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/tenant`;
@@ -129,6 +149,8 @@ Created tenant: Production (production) - ID: 42
129
149
  if (tenant.state) {
130
150
  this.log(` State: ${tenant.state}`);
131
151
  }
152
+ if (flags.license)
153
+ this.log(` License: applied`);
132
154
  }
133
155
  }
134
156
  catch (error) {
@@ -99,8 +99,8 @@ Tenant: My Tenant (my-tenant)
99
99
  this.log(` Tasks: ${tenant.tasks}`);
100
100
  if (tenant.ingress !== undefined)
101
101
  this.log(` Ingress: ${tenant.ingress}`);
102
- if (tenant.type)
103
- this.log(` Type: ${tenant.type}`);
102
+ if (tenant.ephemeral)
103
+ this.log(` Ephemeral: ${tenant.ephemeral}`);
104
104
  if (tenant.deployed_at) {
105
105
  const d = new Date(tenant.deployed_at);
106
106
  const deployedDate = Number.isNaN(d.getTime()) ? tenant.deployed_at : d.toISOString().split('T')[0];
@@ -92,8 +92,8 @@ Tenants in workspace 5:
92
92
  for (const tenant of tenants) {
93
93
  const state = tenant.state ? ` [${tenant.state}]` : '';
94
94
  const license = tenant.license ? ` - ${tenant.license}` : '';
95
- const typeLabel = tenant.type && tenant.type !== 'standard' ? ` [${tenant.type}]` : '';
96
- this.log(` - ${tenant.display || tenant.name} (${tenant.name})${state}${license}${typeLabel}`);
95
+ const ephemeral = tenant.ephemeral ? ' [ephemeral]' : '';
96
+ this.log(` - ${tenant.display || tenant.name} (${tenant.name})${state}${license}${ephemeral}`);
97
97
  if (tenant.cluster?.name)
98
98
  this.log(` Cluster: ${tenant.cluster.name}`);
99
99
  const releaseName = typeof tenant.release === 'string' ? tenant.release : tenant.release?.name;
@@ -100,6 +100,40 @@ Truncate all table records before importing
100
100
  ` 2. Set it in your profile using: xano profile:edit ${profileName} -w <workspace_id>`);
101
101
  }
102
102
  const tenantName = flags.tenant;
103
+ // Fetch tenant details and verify it's ephemeral
104
+ const tenantApiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/tenant/${tenantName}`;
105
+ try {
106
+ const tenantResponse = await this.verboseFetch(tenantApiUrl, {
107
+ headers: {
108
+ accept: 'application/json',
109
+ Authorization: `Bearer ${profile.access_token}`,
110
+ },
111
+ method: 'GET',
112
+ }, flags.verbose, profile.access_token);
113
+ if (!tenantResponse.ok) {
114
+ const errorText = await tenantResponse.text();
115
+ this.error(`Failed to fetch tenant '${tenantName}' (${tenantResponse.status}): ${errorText}`);
116
+ }
117
+ const tenantData = (await tenantResponse.json());
118
+ if (!tenantData.ephemeral) {
119
+ this.error(`Tenant '${tenantName}' is not ephemeral. Push is only allowed for ephemeral tenants.\n` +
120
+ `Create an ephemeral tenant with: xano tenant create "name" --ephemeral`);
121
+ }
122
+ }
123
+ catch (error) {
124
+ if (error instanceof Error && error.message.includes('is not ephemeral')) {
125
+ throw error;
126
+ }
127
+ if (error instanceof Error && error.message.includes('Failed to fetch tenant')) {
128
+ throw error;
129
+ }
130
+ if (error instanceof Error) {
131
+ this.error(`Failed to verify tenant: ${error.message}`);
132
+ }
133
+ else {
134
+ this.error(`Failed to verify tenant: ${String(error)}`);
135
+ }
136
+ }
103
137
  // Resolve the input directory
104
138
  const inputDir = path.resolve(args.directory);
105
139
  if (!fs.existsSync(inputDir)) {
@@ -282,24 +282,6 @@ Push functions but exclude test files
282
282
  }
283
283
  else {
284
284
  const errorText = await dryRunResponse.text();
285
- // Check if push is disabled on this workspace
286
- try {
287
- const errorJson = JSON.parse(errorText);
288
- if (errorJson.message?.includes('Push is disabled')) {
289
- this.log('');
290
- this.log(ux.colorize('red', ux.colorize('bold', 'Direct push to this workspace is disabled.')));
291
- this.log(ux.colorize('dim', 'To apply changes to the workspace, use the sandbox review flow:'));
292
- this.log(` ${ux.colorize('cyan', 'xano sandbox push')} ${ux.colorize('dim', '— push changes to your sandbox')}`);
293
- this.log(` ${ux.colorize('cyan', 'xano sandbox review')} ${ux.colorize('dim', '— edit any logic, inspect the snapshot diff, and promote changes to the workspace')}`);
294
- this.log('');
295
- this.log(ux.colorize('dim', 'To enable direct push, go to Workspace Settings → CLI → Allow Direct Workspace Push.'));
296
- this.log('');
297
- return;
298
- }
299
- }
300
- catch {
301
- // Not JSON, fall through
302
- }
303
285
  this.warn(`Push preview failed (${dryRunResponse.status}). Skipping preview.`);
304
286
  if (flags.verbose) {
305
287
  this.log(ux.colorize('dim', errorText));
@@ -483,14 +465,6 @@ Push functions but exclude test files
483
465
  if (errorJson.payload?.param) {
484
466
  errorMessage += `\n Parameter: ${errorJson.payload.param}`;
485
467
  }
486
- // Provide clear guidance when push is disabled
487
- if (errorJson.message?.includes('Push is disabled')) {
488
- this.error(`Push is disabled for this workspace.\n\n` +
489
- `To enable, go to Workspace Settings and turn on "Allow Push".\n\n` +
490
- `Alternatively, use sandbox commands:\n` +
491
- ` xano sandbox push ${args.directory}\n` +
492
- ` xano sandbox impersonate`);
493
- }
494
468
  }
495
469
  catch {
496
470
  errorMessage += `\n${errorText}`;
package/dist/help.d.ts CHANGED
@@ -12,10 +12,9 @@ declare class CustomCommandHelp extends BaseCommandHelp {
12
12
  protected flagHelpLabel(flag: Command.Flag.Any, showOptions?: boolean): string;
13
13
  }
14
14
  /**
15
- * Custom Help class that injects promoted commands into the COMMANDS list
15
+ * Custom Help class that uses CustomCommandHelp
16
16
  */
17
17
  export default class Help extends BaseHelp {
18
18
  protected CommandHelpClass: typeof CustomCommandHelp;
19
- formatCommands(commands: Command.Loadable[]): string;
20
19
  }
21
20
  export {};
package/dist/help.js CHANGED
@@ -1,15 +1,5 @@
1
1
  import { Help as BaseHelp } from '@oclif/core';
2
2
  import { CommandHelp as BaseCommandHelp } from '@oclif/core/help';
3
- /**
4
- * Extra commands to include in the top-level COMMANDS list.
5
- * These are nested commands promoted for discoverability.
6
- */
7
- const PROMOTED_COMMANDS = [
8
- { description: 'Create a new workspace', label: 'workspace create' },
9
- { description: 'List workspaces', label: 'workspace list' },
10
- { description: 'Pull a workspace to local files', label: 'workspace pull' },
11
- { description: 'Push local documents to a workspace', label: 'workspace push' },
12
- ];
13
3
  /**
14
4
  * Custom CommandHelp class that extends the default to display environment variables
15
5
  * alongside flag descriptions
@@ -29,36 +19,8 @@ class CustomCommandHelp extends BaseCommandHelp {
29
19
  }
30
20
  }
31
21
  /**
32
- * Custom Help class that injects promoted commands into the COMMANDS list
22
+ * Custom Help class that uses CustomCommandHelp
33
23
  */
34
24
  export default class Help extends BaseHelp {
35
25
  CommandHelpClass = CustomCommandHelp;
36
- formatCommands(commands) {
37
- if (commands.length === 0 && PROMOTED_COMMANDS.length === 0)
38
- return '';
39
- // Check before IDs are mutated: root help has top-level commands (no colons)
40
- const isRootHelp = commands.some((c) => !c.id.includes(':'));
41
- const entries = commands
42
- .filter((c) => (this.opts.hideAliasesFromRoot ? !c.aliases?.includes(c.id) : true))
43
- .filter((c) => c.id !== 'plugins')
44
- .map((c) => {
45
- if (this.config.topicSeparator !== ':')
46
- c.id = c.id.replaceAll(':', this.config.topicSeparator);
47
- const summary = this.summary(c);
48
- return [c.id, summary ? summary.replace(/\u001B\[\d+m/g, '') : ''];
49
- });
50
- // Only add promoted commands at the root level, not within a specific topic
51
- if (isRootHelp) {
52
- for (const promoted of PROMOTED_COMMANDS) {
53
- entries.push([promoted.label, promoted.description]);
54
- }
55
- }
56
- entries.sort((a, b) => a[0].localeCompare(b[0]));
57
- const body = this.renderList(entries, {
58
- indentation: 2,
59
- spacer: '\n',
60
- stripAnsi: this.opts.stripAnsi,
61
- });
62
- return this.section('COMMANDS', body + `\n\n\x1b[2mSee xano <topic> --help for all commands in a topic.\x1b[0m`);
63
- }
64
26
  }