@xano/cli 1.0.3-beta.9 → 1.0.3

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 +3 -69
  2. package/dist/base-command.d.ts +0 -27
  3. package/dist/base-command.js +46 -125
  4. package/dist/commands/auth/index.d.ts +0 -10
  5. package/dist/commands/auth/index.js +9 -146
  6. package/dist/commands/static_host/build/create/index.d.ts +1 -9
  7. package/dist/commands/static_host/build/create/index.js +4 -54
  8. package/dist/commands/static_host/build/get/index.d.ts +1 -1
  9. package/dist/commands/static_host/build/get/index.js +10 -16
  10. package/dist/utils/multidoc-push.js +94 -27
  11. package/dist/utils/reference-checker.js +2 -2
  12. package/oclif.manifest.json +2421 -3382
  13. package/package.json +3 -4
  14. package/dist/commands/static_host/build/delete/index.d.ts +0 -19
  15. package/dist/commands/static_host/build/delete/index.js +0 -114
  16. package/dist/commands/static_host/build/pull/index.d.ts +0 -52
  17. package/dist/commands/static_host/build/pull/index.js +0 -300
  18. package/dist/commands/static_host/build/push/index.d.ts +0 -23
  19. package/dist/commands/static_host/build/push/index.js +0 -225
  20. package/dist/commands/static_host/create/index.d.ts +0 -17
  21. package/dist/commands/static_host/create/index.js +0 -86
  22. package/dist/commands/static_host/deploy/index.d.ts +0 -18
  23. package/dist/commands/static_host/deploy/index.js +0 -105
  24. package/dist/commands/static_host/edit/index.d.ts +0 -23
  25. package/dist/commands/static_host/edit/index.js +0 -151
  26. package/dist/commands/static_host/get/index.d.ts +0 -18
  27. package/dist/commands/static_host/get/index.js +0 -94
  28. package/dist/commands/static_host/migrate/index.d.ts +0 -44
  29. package/dist/commands/static_host/migrate/index.js +0 -205
package/README.md CHANGED
@@ -46,42 +46,8 @@ These warnings are layer 1 of broader push-safety work; ephemeral sandbox enviro
46
46
  xano auth
47
47
  xano auth --origin https://custom.xano.com
48
48
  xano auth --insecure # Skip TLS verification (self-signed certs)
49
- xano auth --no-browser # Headless login (no local callback server)
50
-
51
- # Pre-select instance/workspace/branch and profile name (skips the pickers)
52
- xano auth -i my-instance -w 5 -b dev -p staging
53
- xano auth --instance my-instance --workspace "My Workspace" --branch dev --profile staging
54
-
55
- # Pass "" to take a picker's default: skip workspace, use live branch, default profile name
56
- xano auth -i my-instance -w 5 -b "" -p ""
57
49
  ```
58
50
 
59
- The default flow starts a temporary callback server on `127.0.0.1` and waits
60
- for the browser to redirect back to it. On remote/SSH sessions, Docker
61
- containers, or locked-down networks where the browser can't reach the CLI's
62
- loopback address, use `--no-browser`: the CLI prints a login URL, you open it
63
- in any browser, and paste back the code it displays. No local server required.
64
-
65
- Each picker can be pre-answered with a flag: `-i/--instance` (instance name),
66
- `-w/--workspace` (workspace ID or name), `-b/--branch` (branch label), and
67
- `-p/--profile` (profile name to save). An empty value (`""`) takes the
68
- picker's default answer: `-w ""` skips workspace selection, `-b ""` skips and
69
- uses the live branch, and `-p ""` uses the default profile name. With all four
70
- set alongside `--no-browser`, the only input is pasting the code from the
71
- browser — useful for scripted or remote setups.
72
-
73
- When stdin is piped (not a TTY), `--no-browser` reads the code directly from
74
- stdin instead of prompting, so scripts and AI agents can complete the flow
75
- without an interactive terminal:
76
-
77
- ```bash
78
- echo "$CODE" | xano auth --no-browser -i my-instance -w 5 -b dev -p staging
79
- ```
80
-
81
- If you can't run `xano auth` at all, you can always create a profile manually
82
- with a Metadata API token from the Xano dashboard — see
83
- [Profiles](#profiles) below.
84
-
85
51
  ### Profiles
86
52
 
87
53
  Profiles store your Xano credentials and default workspace settings.
@@ -556,46 +522,14 @@ xano sandbox reset --force
556
522
  # List static hosts
557
523
  xano static_host list
558
524
 
559
- # Create / get / edit a static host
560
- xano static_host create marketing --description "Marketing site"
561
- xano static_host get marketing
562
- xano static_host edit marketing --name marketing-v2 --description "Updated"
525
+ # Create a build
526
+ xano static_host build create default -f ./build.zip -n "v1.0.0"
563
527
 
564
528
  # List builds
565
529
  xano static_host build list default
566
530
 
567
531
  # Get build details
568
- xano static_host build get default --build_id 52
569
-
570
- # Pull a build to disk. Defaults to the original uploaded source
571
- # (including package.json). Use --source built for the compiled/served output.
572
- xano static_host build pull default --build_id 52 # By build ID (original source)
573
- xano static_host build pull default --build_id 52 --source built # Compiled output
574
- xano static_host build pull default --latest # Latest build
575
- xano static_host build pull default --env dev # Build currently deployed to dev
576
- xano static_host build pull default --env prod -d ./prod-release
577
-
578
- # Push a build (name optional — auto-generated from the timestamp if omitted).
579
- # Accepts a directory (-d) or a zip file (-f). Defaults to the current directory.
580
- # For package.json builds, the CLI waits for the build to finish (--no-wait to skip).
581
- xano static_host build push default -d ./dist -n "v1.0.0"
582
- xano static_host build push default # current dir, auto-name
583
- xano static_host build push default -f ./build.zip -n "v1.0.0" # from zip file
584
- xano static_host build push default -n "release" --description "Production build"
585
-
586
- # Delete a build (prompts for confirmation; --force to skip)
587
- xano static_host build delete default --build_id 52
588
- xano static_host build delete default --build_id 52 --force
589
-
590
- # Deploy a build to an environment
591
- xano static_host deploy default --build_id 52 --env dev
592
- xano static_host deploy default --build_id 52 --env prod
593
-
594
- # Migrate a host to instance-managed (v2) hosting
595
- xano static_host migrate newsite # one host (both envs)
596
- xano static_host migrate newsite --env dev # one env
597
- xano static_host migrate --all # every v1 host in the workspace
598
- xano static_host migrate --all --dry-run # preview without changing anything
532
+ xano static_host build get default 52
599
533
  ```
600
534
 
601
535
  ## Global Options
@@ -103,31 +103,4 @@ export default abstract class BaseCommand extends Command {
103
103
  * Use this for all Metadata API calls to support the --verbose flag.
104
104
  */
105
105
  protected verboseFetch(url: string, options: RequestInit, verbose: boolean, authToken?: string): Promise<Response>;
106
- /**
107
- * Poll a static-host build until it reaches a terminal status (ok | error),
108
- * showing a live, ticking spinner with the current stage and elapsed time —
109
- * mirroring the UI's build progress for async (package.json) builds, which
110
- * keep running after the upload returns.
111
- *
112
- * On a TTY it renders an animated spinner via ux.action; when quiet (JSON
113
- * output) or non-interactive it falls back to plain one-line status updates.
114
- *
115
- * Returns the final status. Resolves to the last-seen status on timeout.
116
- */
117
- protected logStaticHostUrls(opts: {
118
- profile: ProfileConfig;
119
- staticHost: string;
120
- verbose: boolean;
121
- workspaceId: string;
122
- }): Promise<void>;
123
- protected waitForBuild(opts: {
124
- buildId: number | string;
125
- intervalMs?: number;
126
- profile: ProfileConfig;
127
- quiet?: boolean;
128
- staticHost: string;
129
- timeoutMs?: number;
130
- verbose: boolean;
131
- workspaceId: string;
132
- }): Promise<string>;
133
106
  }
@@ -1,13 +1,48 @@
1
- import { Command, Flags, ux } from '@oclif/core';
1
+ import { Command, Flags } from '@oclif/core';
2
2
  import * as yaml from 'js-yaml';
3
3
  import * as fs from 'node:fs';
4
4
  import * as os from 'node:os';
5
5
  import * as path from 'node:path';
6
+ import { Agent } from 'undici';
6
7
  import { checkForUpdate } from './update-check.js';
7
8
  import { applyLocalOverrides, findLocalProfilePath, formatLocalProfileBanner, parseLocalProfile, resolveProfileSelection, } from './utils/local-config.js';
8
9
  export function buildUserAgent(version) {
9
10
  return `xano-cli/${version} (${process.platform}; ${process.arch}) node/${process.version}`;
10
11
  }
12
+ /**
13
+ * Default per-request timeout for Metadata API calls, in milliseconds (15 min).
14
+ *
15
+ * Node's built-in fetch (undici) defaults to a 300s `headersTimeout` and throws
16
+ * UND_ERR_HEADERS_TIMEOUT when a slow endpoint (e.g. a large multidoc push) takes
17
+ * longer than that to produce response headers — even when the server, ingress
18
+ * (3600s), and load balancer would happily wait. We replace that hidden 300s
19
+ * ceiling with a single, generous bound applied to every request. Override with
20
+ * the XANO_CLI_REQUEST_TIMEOUT_MS env var; set it to 0 to disable the timeout.
21
+ */
22
+ const DEFAULT_REQUEST_TIMEOUT_MS = 15 * 60 * 1000;
23
+ function resolveRequestTimeoutMs(env = process.env) {
24
+ const raw = env.XANO_CLI_REQUEST_TIMEOUT_MS;
25
+ if (raw === undefined || raw === '')
26
+ return DEFAULT_REQUEST_TIMEOUT_MS;
27
+ const parsed = Number(raw);
28
+ if (!Number.isFinite(parsed) || parsed < 0)
29
+ return DEFAULT_REQUEST_TIMEOUT_MS;
30
+ return parsed;
31
+ }
32
+ /**
33
+ * Lazily-built undici dispatcher whose header/body inactivity timeouts match our
34
+ * request timeout, so undici's internal 300s default never fires first. Built
35
+ * once and reused across requests. `timeout === 0` means "no bound".
36
+ */
37
+ let sharedDispatcher;
38
+ function getRequestDispatcher(timeoutMs) {
39
+ if (timeoutMs === 0) {
40
+ sharedDispatcher ??= new Agent({ bodyTimeout: 0, headersTimeout: 0 });
41
+ return sharedDispatcher;
42
+ }
43
+ sharedDispatcher ??= new Agent({ bodyTimeout: timeoutMs, headersTimeout: timeoutMs });
44
+ return sharedDispatcher;
45
+ }
11
46
  /**
12
47
  * Resolve the credentials file path from flag, env var, or default.
13
48
  * Checks (in order): explicit configPath arg, XANO_CONFIG env var, ~/.xano/credentials.yaml
@@ -288,7 +323,16 @@ export default class BaseCommand extends Command {
288
323
  'User-Agent': buildUserAgent(this.config.version),
289
324
  ...options.headers,
290
325
  };
291
- const fetchOptions = { ...options, headers };
326
+ const timeoutMs = resolveRequestTimeoutMs();
327
+ const fetchOptions = {
328
+ ...options,
329
+ // undici dispatcher: lifts the hidden 300s headers/body timeout to our bound
330
+ dispatcher: getRequestDispatcher(timeoutMs),
331
+ headers,
332
+ // belt-and-suspenders hard ceiling that also covers DNS/connect stalls;
333
+ // surfaces as a clean AbortError (see describeNetworkError). 0 = no bound.
334
+ ...(timeoutMs > 0 && !options.signal ? { signal: AbortSignal.timeout(timeoutMs) } : {}),
335
+ };
292
336
  const contentType = headers['Content-Type'] || 'application/json';
293
337
  if (verbose) {
294
338
  this.log('');
@@ -314,127 +358,4 @@ export default class BaseCommand extends Command {
314
358
  }
315
359
  return response;
316
360
  }
317
- /**
318
- * Poll a static-host build until it reaches a terminal status (ok | error),
319
- * showing a live, ticking spinner with the current stage and elapsed time —
320
- * mirroring the UI's build progress for async (package.json) builds, which
321
- * keep running after the upload returns.
322
- *
323
- * On a TTY it renders an animated spinner via ux.action; when quiet (JSON
324
- * output) or non-interactive it falls back to plain one-line status updates.
325
- *
326
- * Returns the final status. Resolves to the last-seen status on timeout.
327
- */
328
- async logStaticHostUrls(opts) {
329
- const { profile, staticHost, verbose, workspaceId } = opts;
330
- const url = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/static_host/${staticHost}`;
331
- try {
332
- const response = await this.verboseFetch(url, {
333
- headers: { accept: 'application/json', Authorization: `Bearer ${profile.access_token}` },
334
- method: 'GET',
335
- }, verbose, profile.access_token);
336
- if (!response.ok)
337
- return;
338
- const host = (await response.json());
339
- if (host.dev?.default_url)
340
- this.log(`Dev URL: ${host.dev.default_url}`);
341
- if (host.dev?.custom_url)
342
- this.log(`Dev Custom URL: ${host.dev.custom_url}`);
343
- if (host.prod?.default_url)
344
- this.log(`Prod URL: ${host.prod.default_url}`);
345
- if (host.prod?.custom_url)
346
- this.log(`Prod Custom URL: ${host.prod.custom_url}`);
347
- }
348
- catch {
349
- // Non-fatal — the build succeeded, we just can't show the URL.
350
- }
351
- }
352
- async waitForBuild(opts) {
353
- const { buildId, profile, quiet, staticHost, verbose, workspaceId } = opts;
354
- const intervalMs = opts.intervalMs ?? 2000;
355
- const timeoutMs = opts.timeoutMs ?? 600_000; // 10 min
356
- const terminal = new Set(['error', 'ok']);
357
- const url = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/static_host/${staticHost}/build/${buildId}`;
358
- // Spinner only on an interactive TTY and when not emitting JSON. Verbose mode
359
- // also disables it (the spinner would interleave with request/response logs).
360
- const animate = Boolean(process.stdout.isTTY) && !quiet && !verbose;
361
- const start = Date.now();
362
- const elapsed = () => Math.round((Date.now() - start) / 1000);
363
- let stage = 'pending';
364
- let ticker;
365
- // Reflect the current stage: live spinner status on a TTY, else a plain line.
366
- const render = () => {
367
- if (animate) {
368
- ux.action.status = `${stageLabel(stage)} (${elapsed()}s)`;
369
- }
370
- else if (!quiet && !terminal.has(stage)) {
371
- this.log(`Build status: ${stage}`);
372
- }
373
- };
374
- // Stop the spinner/ticker and emit a final line.
375
- const conclude = (message) => {
376
- if (ticker)
377
- clearInterval(ticker);
378
- if (animate) {
379
- ux.action.stop(message);
380
- }
381
- else if (!quiet) {
382
- this.log(message);
383
- }
384
- };
385
- if (animate) {
386
- ux.action.start('Building', stageLabel(stage));
387
- // Re-render every 120ms so the elapsed counter ticks even between polls.
388
- ticker = setInterval(render, 120);
389
- }
390
- else if (!quiet) {
391
- this.log(`Build status: ${stage}`);
392
- }
393
- /* eslint-disable no-await-in-loop */
394
- while (Date.now() - start < timeoutMs) {
395
- const response = await this.verboseFetch(url, {
396
- headers: { accept: 'application/json', Authorization: `Bearer ${profile.access_token}` },
397
- method: 'GET',
398
- }, verbose, profile.access_token);
399
- if (response.ok) {
400
- const build = (await response.json());
401
- const status = build.status ?? 'pending';
402
- if (status !== stage) {
403
- stage = status;
404
- render();
405
- }
406
- if (terminal.has(status)) {
407
- const took = `${elapsed()}s`;
408
- conclude(status === 'ok' ? `done in ${took}` : `failed after ${took}`);
409
- return status;
410
- }
411
- }
412
- await new Promise((resolve) => {
413
- setTimeout(resolve, intervalMs);
414
- });
415
- }
416
- /* eslint-enable no-await-in-loop */
417
- conclude(`stopped waiting after ${Math.round(timeoutMs / 1000)}s (last status: ${stage || 'unknown'})`);
418
- return stage;
419
- }
420
- }
421
- /** Human-friendly label for a build status stage. */
422
- function stageLabel(status) {
423
- switch (status) {
424
- case 'building': {
425
- return 'Installing & building (npm)';
426
- }
427
- case 'ok': {
428
- return 'Finishing';
429
- }
430
- case 'pending': {
431
- return 'Queued';
432
- }
433
- case 'publishing': {
434
- return 'Publishing files';
435
- }
436
- default: {
437
- return status;
438
- }
439
- }
440
361
  }
@@ -3,26 +3,16 @@ export default class Auth extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static flags: {
6
- branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
6
  config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
7
  insecure: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
- instance: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
- 'no-browser': import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
8
  origin: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
- profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
- workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
9
  };
15
10
  run(): Promise<void>;
16
11
  private getHeaders;
17
12
  private fetchBranches;
18
13
  private fetchInstances;
19
14
  private fetchWorkspaces;
20
- private promptForToken;
21
15
  private promptProfileName;
22
- private readTokenFromStdin;
23
- private resolveBranch;
24
- private resolveInstance;
25
- private resolveWorkspace;
26
16
  private saveProfile;
27
17
  private selectBranch;
28
18
  private selectInstance;
@@ -1,3 +1,4 @@
1
+ import { ExitPromptError } from '@inquirer/core';
1
2
  import { Command, Flags } from '@oclif/core';
2
3
  import inquirer from 'inquirer';
3
4
  import * as yaml from 'js-yaml';
@@ -19,21 +20,8 @@ Authenticated as John Doe (john@example.com)
19
20
  Profile 'default' created successfully!`,
20
21
  `$ xano auth --origin https://custom.xano.com
21
22
  Opening browser for Xano login at https://custom.xano.com...`,
22
- `$ xano auth --no-browser
23
- To authenticate, open the following URL in any browser:
24
- https://app.xano.com/login?dest=cli&display=code
25
- ? Paste the code shown in your browser: ****`,
26
- `$ xano auth --no-browser --instance my-instance --workspace 5 --branch dev --profile staging
27
- (non-interactive: only the pasted code is prompted for)`,
28
- `$ echo "$CODE" | xano auth --no-browser --instance my-instance --workspace 5 --branch dev --profile staging
29
- (fully scripted: the code is read from piped stdin, no prompt at all)`,
30
23
  ];
31
24
  static flags = {
32
- branch: Flags.string({
33
- char: 'b',
34
- description: 'Pre-select a branch by label (skips the branch picker); pass "" to skip and use the live branch',
35
- required: false,
36
- }),
37
25
  config: Flags.string({
38
26
  char: 'c',
39
27
  description: 'Path to credentials file (default: ~/.xano/credentials.yaml)',
@@ -45,30 +33,11 @@ To authenticate, open the following URL in any browser:
45
33
  default: false,
46
34
  description: 'Skip TLS certificate verification (for self-signed certificates)',
47
35
  }),
48
- instance: Flags.string({
49
- char: 'i',
50
- description: 'Pre-select an instance by name (skips the instance picker)',
51
- required: false,
52
- }),
53
- 'no-browser': Flags.boolean({
54
- default: false,
55
- description: 'Headless login: print a URL and paste back the code shown in the browser, instead of starting a local callback server (use on remote/SSH/Docker hosts where 127.0.0.1 is not reachable from the browser)',
56
- }),
57
36
  origin: Flags.string({
58
37
  char: 'o',
59
38
  default: 'https://app.xano.com',
60
39
  description: 'Xano account origin URL',
61
40
  }),
62
- profile: Flags.string({
63
- char: 'p',
64
- description: 'Profile name to save (skips the profile name prompt); pass "" to use the default name',
65
- required: false,
66
- }),
67
- workspace: Flags.string({
68
- char: 'w',
69
- description: 'Pre-select a workspace by ID or name (skips the workspace picker); pass "" to skip workspace',
70
- required: false,
71
- }),
72
41
  };
73
42
  async run() {
74
43
  const { flags } = await this.parse(Auth);
@@ -79,9 +48,7 @@ To authenticate, open the following URL in any browser:
79
48
  try {
80
49
  // Step 1: Get token via browser auth
81
50
  this.log('Starting authentication flow...');
82
- const token = flags['no-browser']
83
- ? await this.promptForToken(flags.origin)
84
- : await this.startAuthServer(flags.origin);
51
+ const token = await this.startAuthServer(flags.origin);
85
52
  // Step 2: Validate token and get user info
86
53
  this.log('');
87
54
  this.log('Validating authentication...');
@@ -93,9 +60,6 @@ To authenticate, open the following URL in any browser:
93
60
  const isSelfHosted = !/^https:\/\/app\.(.*\.)?xano\.com$/.test(flags.origin);
94
61
  let instance;
95
62
  if (isSelfHosted) {
96
- if (flags.instance) {
97
- this.warn('Ignoring --instance: the origin itself is the instance for self-hosted Xano.');
98
- }
99
63
  instance = {
100
64
  display: flags.origin,
101
65
  id: 'self-hosted',
@@ -110,7 +74,7 @@ To authenticate, open the following URL in any browser:
110
74
  if (instances.length === 0) {
111
75
  this.error('No instances found. Please check your account.');
112
76
  }
113
- instance = await this.resolveInstance(instances, flags.instance);
77
+ instance = await this.selectInstance(instances);
114
78
  }
115
79
  // Step 4: Workspace selection
116
80
  let workspace;
@@ -119,25 +83,20 @@ To authenticate, open the following URL in any browser:
119
83
  this.log('Fetching available workspaces...');
120
84
  const workspaces = await this.fetchWorkspaces(token, instance.origin);
121
85
  if (workspaces.length > 0) {
122
- workspace = await this.resolveWorkspace(workspaces, flags.workspace);
86
+ workspace = await this.selectWorkspace(workspaces);
123
87
  if (workspace) {
124
88
  // Step 5: Branch selection
125
89
  this.log('');
126
90
  this.log('Fetching available branches...');
127
91
  const branches = await this.fetchBranches(token, instance.origin, workspace.id);
128
- branch = await this.resolveBranch(branches, flags.branch);
92
+ if (branches.length > 1) {
93
+ branch = await this.selectBranch(branches);
94
+ }
129
95
  }
130
96
  }
131
- else if (flags.workspace) {
132
- this.error(`Workspace '${flags.workspace}' not found: no workspaces are available on this instance.`);
133
- }
134
- if (flags.branch && !workspace) {
135
- this.warn('Ignoring --branch: no workspace selected.');
136
- }
137
97
  // Step 6: Profile name
138
98
  this.log('');
139
- // An empty --profile value means "use the default name" (same as accepting the prompt's default)
140
- const profileName = flags.profile === undefined ? await this.promptProfileName() : flags.profile.trim() || 'default';
99
+ const profileName = await this.promptProfileName();
141
100
  // Step 7: Save profile
142
101
  await this.saveProfile({
143
102
  access_token: token,
@@ -154,10 +113,7 @@ To authenticate, open the following URL in any browser:
154
113
  process.exit(0);
155
114
  }
156
115
  catch (error) {
157
- // Ctrl+C at an inquirer prompt throws ExitPromptError. Match on the name
158
- // rather than `instanceof`: inquirer bundles its own copy of
159
- // @inquirer/core, so the thrown class won't match an imported one.
160
- if (error?.name === 'ExitPromptError') {
116
+ if (error instanceof ExitPromptError) {
161
117
  this.log('Authentication cancelled.');
162
118
  return;
163
119
  }
@@ -240,45 +196,6 @@ To authenticate, open the following URL in any browser:
240
196
  return [];
241
197
  }
242
198
  }
243
- async promptForToken(origin) {
244
- // Headless flow: no local callback server. The login page, when opened
245
- // without a `callback` param, renders the access token on screen for the
246
- // user to copy. We point the browser there (best-effort) and prompt for
247
- // the pasted code.
248
- const authUrl = `${origin}/login?dest=cli&display=code`;
249
- this.log('To authenticate, open the following URL in any browser:');
250
- this.log('');
251
- this.log(` ${authUrl}`);
252
- this.log('');
253
- // Piped (non-TTY) stdin: read the code directly instead of prompting, so
254
- // scripts and agents can do `echo $CODE | xano auth --no-browser ...`.
255
- // The masked inquirer prompt below requires an interactive terminal.
256
- if (!process.stdin.isTTY) {
257
- const piped = await this.readTokenFromStdin();
258
- if (!piped) {
259
- this.error('No code received on stdin. Pipe the code shown in the browser, e.g. `echo "$CODE" | xano auth --no-browser ...`');
260
- }
261
- this.log('Read code from stdin.');
262
- return piped;
263
- }
264
- try {
265
- await open(authUrl);
266
- }
267
- catch {
268
- // Best-effort only; the URL is already printed above for manual use.
269
- }
270
- const { token } = await inquirer.prompt([
271
- {
272
- message: 'Paste the code shown in your browser',
273
- name: 'token',
274
- type: 'password',
275
- validate(input) {
276
- return input.trim() === '' ? 'A code is required' : true;
277
- },
278
- },
279
- ]);
280
- return token.trim();
281
- }
282
199
  async promptProfileName() {
283
200
  const { profileName } = await inquirer.prompt([
284
201
  {
@@ -297,60 +214,6 @@ To authenticate, open the following URL in any browser:
297
214
  ]);
298
215
  return profileName.trim() || 'default';
299
216
  }
300
- readTokenFromStdin() {
301
- return new Promise((resolve) => {
302
- let data = '';
303
- process.stdin.setEncoding('utf8');
304
- process.stdin.on('data', (chunk) => {
305
- data += chunk;
306
- });
307
- process.stdin.on('end', () => resolve(data.trim()));
308
- process.stdin.on('error', () => resolve(data.trim()));
309
- });
310
- }
311
- async resolveBranch(branches, flagValue) {
312
- if (flagValue !== undefined) {
313
- // An empty value means "skip and use live branch" (same as the picker's skip option)
314
- if (flagValue.trim() === '') {
315
- this.log('Using live branch');
316
- return undefined;
317
- }
318
- const match = branches.find((br) => br.label === flagValue || br.id === flagValue);
319
- if (!match) {
320
- this.error(`Branch '${flagValue}' not found. Available branches: ${branches.map((br) => br.label).join(', ')}`);
321
- }
322
- this.log(`Using branch: ${match.label}`);
323
- return match.id;
324
- }
325
- return branches.length > 1 ? this.selectBranch(branches) : undefined;
326
- }
327
- async resolveInstance(instances, flagValue) {
328
- if (flagValue) {
329
- const match = instances.find((inst) => inst.name === flagValue || inst.id === flagValue);
330
- if (!match) {
331
- this.error(`Instance '${flagValue}' not found. Available instances: ${instances.map((inst) => inst.name).join(', ')}`);
332
- }
333
- this.log(`Using instance: ${match.name} (${match.display})`);
334
- return match;
335
- }
336
- return this.selectInstance(instances);
337
- }
338
- async resolveWorkspace(workspaces, flagValue) {
339
- if (flagValue !== undefined) {
340
- // An empty value means "skip workspace" (same as the picker's skip option)
341
- if (flagValue.trim() === '') {
342
- this.log('Skipping workspace selection');
343
- return undefined;
344
- }
345
- const match = workspaces.find((ws) => String(ws.id) === flagValue || ws.name === flagValue);
346
- if (!match) {
347
- this.error(`Workspace '${flagValue}' not found. Available workspaces: ${workspaces.map((ws) => `${ws.name} (${ws.id})`).join(', ')}`);
348
- }
349
- this.log(`Using workspace: ${match.name} (${match.id})`);
350
- return match;
351
- }
352
- return this.selectWorkspace(workspaces);
353
- }
354
217
  async saveProfile(profile, configPath) {
355
218
  const credentialsPath = resolveCredentialsPath(configPath);
356
219
  const credDir = dirname(credentialsPath);
@@ -1,12 +1,5 @@
1
1
  import BaseCommand from '../../../../base-command.js';
2
- /**
3
- * Generate a default build name from a compact timestamp: `YYYYMMDD-HHmmss`
4
- * (e.g. `20260531-143022`). Sortable, distinct down to the second, and uses
5
- * local time so it lines up with when the user ran the command.
6
- */
7
- export declare function generateBuildName(date?: Date): string;
8
2
  export default class StaticHostBuildCreate extends BaseCommand {
9
- static hidden: boolean;
10
3
  static args: {
11
4
  static_host: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
12
5
  };
@@ -15,8 +8,7 @@ export default class StaticHostBuildCreate extends BaseCommand {
15
8
  static flags: {
16
9
  description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
10
  file: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
18
- name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
19
- 'no-wait': import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ name: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
20
12
  output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
21
13
  workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
22
14
  config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;