@xano/cli 1.2.0 → 1.3.0

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 (116) hide show
  1. package/README.md +112 -3
  2. package/dist/base-command.d.ts +43 -36
  3. package/dist/base-command.js +223 -160
  4. package/dist/commands/auth/index.d.ts +1 -1
  5. package/dist/commands/auth/index.js +7 -7
  6. package/dist/commands/branch/create/index.d.ts +1 -1
  7. package/dist/commands/branch/create/index.js +1 -1
  8. package/dist/commands/ephemeral/create/index.d.ts +18 -0
  9. package/dist/commands/ephemeral/create/index.js +103 -0
  10. package/dist/commands/ephemeral/delete/index.d.ts +18 -0
  11. package/dist/commands/ephemeral/delete/index.js +99 -0
  12. package/dist/commands/ephemeral/edit/index.d.ts +18 -0
  13. package/dist/commands/ephemeral/edit/index.js +109 -0
  14. package/dist/commands/ephemeral/get/index.d.ts +21 -0
  15. package/dist/commands/ephemeral/get/index.js +125 -0
  16. package/dist/commands/ephemeral/impersonate/index.d.ts +20 -0
  17. package/dist/commands/ephemeral/impersonate/index.js +120 -0
  18. package/dist/commands/ephemeral/list/index.d.ts +19 -0
  19. package/dist/commands/ephemeral/list/index.js +123 -0
  20. package/dist/commands/ephemeral/pull/index.d.ts +19 -0
  21. package/dist/commands/ephemeral/pull/index.js +163 -0
  22. package/dist/commands/ephemeral/push/index.d.ts +35 -0
  23. package/dist/commands/ephemeral/push/index.js +223 -0
  24. package/dist/commands/ephemeral/static_host/build/delete/index.d.ts +20 -0
  25. package/dist/commands/ephemeral/static_host/build/delete/index.js +121 -0
  26. package/dist/commands/ephemeral/static_host/build/get/index.d.ts +18 -0
  27. package/dist/commands/ephemeral/static_host/build/get/index.js +137 -0
  28. package/dist/commands/ephemeral/static_host/build/list/index.d.ts +18 -0
  29. package/dist/commands/ephemeral/static_host/build/list/index.js +137 -0
  30. package/dist/commands/ephemeral/static_host/build/pull/index.d.ts +53 -0
  31. package/dist/commands/ephemeral/static_host/build/pull/index.js +308 -0
  32. package/dist/commands/ephemeral/static_host/build/push/index.d.ts +24 -0
  33. package/dist/commands/ephemeral/static_host/build/push/index.js +250 -0
  34. package/dist/commands/ephemeral/static_host/create/index.d.ts +18 -0
  35. package/dist/commands/ephemeral/static_host/create/index.js +92 -0
  36. package/dist/commands/ephemeral/static_host/deploy/index.d.ts +19 -0
  37. package/dist/commands/ephemeral/static_host/deploy/index.js +112 -0
  38. package/dist/commands/ephemeral/static_host/edit/index.d.ts +24 -0
  39. package/dist/commands/ephemeral/static_host/edit/index.js +158 -0
  40. package/dist/commands/ephemeral/static_host/get/index.d.ts +19 -0
  41. package/dist/commands/ephemeral/static_host/get/index.js +101 -0
  42. package/dist/commands/ephemeral/static_host/list/index.d.ts +17 -0
  43. package/dist/commands/ephemeral/static_host/list/index.js +132 -0
  44. package/dist/commands/flatten/index.d.ts +34 -0
  45. package/dist/commands/flatten/index.js +140 -0
  46. package/dist/commands/function/create/index.js +1 -1
  47. package/dist/commands/function/edit/index.js +26 -32
  48. package/dist/commands/function/get/index.js +2 -3
  49. package/dist/commands/function/run/index.d.ts +1 -0
  50. package/dist/commands/function/run/index.js +19 -6
  51. package/dist/commands/profile/create/index.d.ts +1 -1
  52. package/dist/commands/profile/create/index.js +7 -7
  53. package/dist/commands/profile/me/index.js +1 -1
  54. package/dist/commands/profile/use/index.js +0 -4
  55. package/dist/commands/profile/wizard/index.d.ts +1 -1
  56. package/dist/commands/profile/wizard/index.js +12 -10
  57. package/dist/commands/profile/workspace/set/index.js +1 -1
  58. package/dist/commands/release/edit/index.js +2 -2
  59. package/dist/commands/release/export/index.js +1 -2
  60. package/dist/commands/release/import/index.js +1 -1
  61. package/dist/commands/release/pull/index.d.ts +0 -6
  62. package/dist/commands/release/pull/index.js +13 -99
  63. package/dist/commands/release/push/index.js +2 -2
  64. package/dist/commands/sandbox/env/get_all/index.js +1 -1
  65. package/dist/commands/sandbox/env/set_all/index.js +1 -1
  66. package/dist/commands/sandbox/get/index.js +1 -1
  67. package/dist/commands/sandbox/license/get/index.js +1 -1
  68. package/dist/commands/sandbox/license/set/index.js +1 -1
  69. package/dist/commands/sandbox/pull/index.d.ts +0 -1
  70. package/dist/commands/sandbox/pull/index.js +13 -81
  71. package/dist/commands/sandbox/push/index.d.ts +1 -1
  72. package/dist/commands/sandbox/push/index.js +5 -5
  73. package/dist/commands/static_host/build/create/index.d.ts +1 -1
  74. package/dist/commands/static_host/build/create/index.js +2 -2
  75. package/dist/commands/static_host/build/pull/index.js +1 -1
  76. package/dist/commands/static_host/build/push/index.js +1 -1
  77. package/dist/commands/static_host/edit/index.js +1 -1
  78. package/dist/commands/tenant/backup/export/index.js +1 -2
  79. package/dist/commands/tenant/backup/import/index.js +1 -1
  80. package/dist/commands/tenant/cluster/create/index.js +1 -1
  81. package/dist/commands/tenant/cluster/license/get/index.js +1 -1
  82. package/dist/commands/tenant/cluster/license/set/index.js +1 -1
  83. package/dist/commands/tenant/create/index.d.ts +1 -1
  84. package/dist/commands/tenant/create/index.js +6 -6
  85. package/dist/commands/tenant/deploy_platform/index.js +1 -1
  86. package/dist/commands/tenant/edit/index.js +7 -7
  87. package/dist/commands/tenant/env/get_all/index.js +1 -1
  88. package/dist/commands/tenant/env/set_all/index.js +1 -1
  89. package/dist/commands/tenant/impersonate/index.d.ts +1 -1
  90. package/dist/commands/tenant/impersonate/index.js +14 -14
  91. package/dist/commands/tenant/license/get/index.js +1 -1
  92. package/dist/commands/tenant/license/set/index.js +1 -1
  93. package/dist/commands/tenant/pull/index.d.ts +0 -6
  94. package/dist/commands/tenant/pull/index.js +14 -100
  95. package/dist/commands/workspace/git/pull/index.d.ts +4 -4
  96. package/dist/commands/workspace/git/pull/index.js +158 -97
  97. package/dist/commands/workspace/pull/index.d.ts +1 -7
  98. package/dist/commands/workspace/pull/index.js +17 -103
  99. package/dist/help.js +4 -2
  100. package/dist/update-check.d.ts +1 -1
  101. package/dist/update-check.js +1 -1
  102. package/dist/utils/document-parser.d.ts +116 -0
  103. package/dist/utils/document-parser.js +311 -4
  104. package/dist/utils/flatten.d.ts +36 -0
  105. package/dist/utils/flatten.js +68 -0
  106. package/dist/utils/knowledge-sync.d.ts +1 -0
  107. package/dist/utils/knowledge-sync.js +11 -6
  108. package/dist/utils/microservice-wait.d.ts +53 -0
  109. package/dist/utils/microservice-wait.js +84 -0
  110. package/dist/utils/multidoc-push.d.ts +88 -0
  111. package/dist/utils/multidoc-push.js +378 -15
  112. package/dist/utils/paging.d.ts +2 -1
  113. package/dist/utils/paging.js +0 -1
  114. package/dist/utils/reference-checker.js +14 -3
  115. package/oclif.manifest.json +5264 -3170
  116. package/package.json +10 -1
package/README.md CHANGED
@@ -46,16 +46,16 @@ The Metadata API pages unevenly, so `list` commands differ in what they expose.
46
46
  | Commands | Flags | Footer |
47
47
  |---|---|---|
48
48
  | `function list`, `unit_test list`, `workflow_test list` (plus `sandbox` / `tenant` variants) | `--page`, `--per_page` | `Page 2 · 50 shown · next: --page 3` |
49
- | `static_host list`, `static_host build list` | `--page` only | `Page 2 · 100 shown · 340 total` |
49
+ | `static_host list`, `static_host build list` (plus `ephemeral` variants) | `--page` only | `Page 2 · 100 shown · 340 total` |
50
50
  | `branch list`, `workspace list`, `tenant snapshot list`, `sandbox env list`, `tenant env list` | none | `12 branches` |
51
- | `tenant list`, `release list`, `platform list`, `tenant cluster list` | none | none |
51
+ | `tenant list`, `release list`, `platform list`, `tenant cluster list`, `ephemeral list` | none | none |
52
52
  | `tenant backup list` | `--page` only (pre-existing) | none |
53
53
 
54
54
  Notes:
55
55
 
56
56
  - **`--per_page` is only offered where the endpoint accepts it.** Static-host endpoints hardcode 100 items per page server-side, so those commands take `--page` alone. `xano static_host list --per_page 10` previously parsed but did nothing; it is now rejected rather than silently ignored.
57
57
  - **All paged commands default `--per_page` to 50.** The test-list commands previously requested 10000 internally so nothing was ever cut off; now that the footer and the JSON envelope both report position, a page that stops at 50 is visible rather than silent. Raise it (up to 10000) when you want everything in one call.
58
- - **The last group has no CLI paging.** Those endpoints page server-side but return a plain array with no page or total metadata, so the CLI cannot tell you where you are. Rather than show a page number with no context — or infer "more available" from a full page, which is wrong exactly when the result count is a multiple of the page size — these commands are left as-is. **They return at most 25–50 items** (50 for tenants, 25 for releases, platforms, and tenant backups). If you have more than that, query the Metadata API directly until those endpoints return paging metadata.
58
+ - **The last group has no CLI paging.** Those endpoints page server-side but return a plain array with no page or total metadata, so the CLI cannot tell you where you are. Rather than show a page number with no context — or infer "more available" from a full page, which is wrong exactly when the result count is a multiple of the page size — these commands are left as-is. **They return at most 25–50 items** (50 for tenants and ephemerals, 25 for releases, platforms, and tenant backups). If you have more than that, query the Metadata API directly until those endpoints return paging metadata.
59
59
  - **`--output json` returns an envelope, not a bare array.** Every list command emits `{items, ...}` using the Metadata API's own field names — `curPage`, `nextPage`, `prevPage`, `itemsTotal` — each included only when the endpoint actually reports it. `perPage` is the one field the CLI adds, since the API never echoes it back and it is otherwise invisible at its default. This is a **breaking change** for scripts that parsed the previous bare array — read `.items` instead.
60
60
 
61
61
  ```jsonc
@@ -229,6 +229,51 @@ xano workspace git pull -r https://github.com/owner/private-repo -t ghp_xxx
229
229
  xano workspace git pull -r https://github.com/owner/repo --path subdir
230
230
  ```
231
231
 
232
+ **One workspace document per tree.** A push directory must contain at most one
233
+ `workspace/*.xs` document. The server applies the first workspace document it
234
+ receives to the workspace being pushed into and silently discards the rest,
235
+ without checking that it describes that workspace — so a second one renames the
236
+ destination to a foreign name while leaving its content untouched. `workspace push`
237
+ and `sandbox push` refuse a tree carrying more than one, naming the offending
238
+ files; there is no `--force` override, because only you know which workspace the
239
+ tree is meant to be.
240
+
241
+ Trees pick up a second one easily: `pull` names the file after the workspace
242
+ itself (`workspace/{name}.xs`), so pulling a different workspace into the same
243
+ directory *adds* a file rather than overwriting, and renaming a workspace leaves
244
+ the old-name file behind. Delete the stale ones, keeping the single document that
245
+ matches your target workspace.
246
+
247
+ ### Flatten
248
+
249
+ A hand-authored `.xs` file can hold several documents joined by `---`. That is
250
+ convenient to edit but unsupported by `push`: the partial-diff filter and GUID
251
+ writeback both parse only the first document in a file, so such a bundle
252
+ silently pushes nothing (partial mode) or corrupts GUIDs (full mode). `push`
253
+ refuses these files and offers to flatten them.
254
+
255
+ `xano flatten` performs that split on demand, producing the same
256
+ one-document-per-file tree `pull` would have written. It is purely local — no
257
+ profile, token, or network — and deletes the original bundle once the split
258
+ succeeds, unless you pass `--keep-source`.
259
+
260
+ ```bash
261
+ # Split a bundle in place into per-document files (removes the original)
262
+ xano flatten secret/pdf-micro/multidoc.xs
263
+
264
+ # Write the split files somewhere else instead of alongside the bundle
265
+ xano flatten ./bundle.xs -o ./workspace
266
+
267
+ # Preview the resulting layout without writing anything
268
+ xano flatten secret/pdf-micro/multidoc.xs --dry-run
269
+
270
+ # Flatten every multi-doc .xs under a directory, keeping the originals
271
+ xano flatten ./dir-of-bundles --keep-source
272
+
273
+ # Overwrite existing destination files instead of erroring
274
+ xano flatten ./bundle.xs --force
275
+ ```
276
+
232
277
  ### Knowledge
233
278
 
234
279
  Knowledge items are user-authored docs and skills (e.g. `CLAUDE.md`, `AGENTS.md`, runbooks)
@@ -323,6 +368,7 @@ xano function run <name> --data email=jo@x.com --data age:=30 --data active:=tru
323
368
  xano function run <name> --json @payload.json --data env=staging # base payload + override
324
369
  echo '{"email":"jo@x.com"}' | xano function run <name> --stdin -o json | jq .result
325
370
  xano function run <name> --branch dev --logs # run on a branch, show execution logs
371
+ xano function run <name> --datasource test # run against the 'test' data source
326
372
  ```
327
373
 
328
374
  Input flexibility for `function run` (assembled into one JSON `input` object):
@@ -335,6 +381,11 @@ Input flexibility for `function run` (assembled into one JSON `input` object):
335
381
  | `--json '<inline>'` / `--json @file.json` / `--json -` | a base JSON object (stdin with `-`) |
336
382
  | `--stdin` | read the JSON object from stdin (same as `--json -`) |
337
383
 
384
+ `--datasource <label>` runs the function against a non-live data source by sending the
385
+ `X-Data-Source` header, so table reads and writes hit that data source's tables. Omit it
386
+ to run against `live`; an unknown label is rejected by the server with
387
+ `Invalid data source.`
388
+
338
389
  Merge order is JSON base first, then `--data` overrides. Missing required inputs are
339
390
  prompted for on an interactive terminal; in a non-TTY (CI) context the command fails
340
391
  listing them. Output defaults to the raw `result` (`-o json`); the exit code is non-zero
@@ -694,6 +745,64 @@ xano tenant cluster license set <cluster_id>
694
745
  xano tenant cluster license set <cluster_id> --file ./kubeconfig.yaml
695
746
  ```
696
747
 
748
+ ### Ephemeral Tenants
749
+
750
+ Manage ephemeral tenants — short-lived, auto-expiring tenants scoped to a workspace (default TTL 1h, max 24h). Unlike a sandbox, an ephemeral tenant requires a workspace.
751
+
752
+ ```bash
753
+ # List ephemeral tenants in the current workspace
754
+ xano ephemeral list
755
+ xano ephemeral list -w 5
756
+
757
+ # List ephemeral tenants across every workspace you can access
758
+ xano ephemeral list --global
759
+
760
+ # Create an ephemeral tenant (workspace required)
761
+ xano ephemeral create "PR preview"
762
+ xano ephemeral create "Demo" --expires-hours 24 -w 5
763
+ xano ephemeral create "Load test" -d "overnight soak" --expires-hours 24
764
+
765
+ # Get / edit / delete
766
+ xano ephemeral get <tenant_name>
767
+ xano ephemeral edit <tenant_name> --display "New Name" -d "New description"
768
+ xano ephemeral delete <tenant_name> --force
769
+
770
+ # Pull to / push from local files (multidoc)
771
+ xano ephemeral pull <tenant_name> -d ./my-ephemeral
772
+ xano ephemeral push <tenant_name> -d ./my-ephemeral --dry-run # preview first
773
+ xano ephemeral push <tenant_name> -d ./my-ephemeral
774
+
775
+ # Open in the browser (or print the URL)
776
+ xano ephemeral impersonate <tenant_name>
777
+ xano ephemeral impersonate <tenant_name> --url-only
778
+ xano ephemeral impersonate <tenant_name> --guest # read-only session (browse only)
779
+ ```
780
+
781
+ #### Static hosting for an ephemeral tenant
782
+
783
+ An ephemeral tenant can host static sites, scoped to that tenant. These commands mirror
784
+ `xano static_host *` but take the tenant name as the first argument (the tenant's static
785
+ hosting is isolated inside the tenant's own database).
786
+
787
+ ```bash
788
+ # List / create / inspect a tenant's static hosts
789
+ xano ephemeral static_host list <tenant_name>
790
+ xano ephemeral static_host create <tenant_name> --name marketing --description "Marketing site"
791
+ xano ephemeral static_host get <tenant_name> -H marketing
792
+ xano ephemeral static_host edit <tenant_name> -H marketing --description "Updated"
793
+
794
+ # Builds: push a directory, list, inspect, deploy to an env, pull, delete
795
+ xano ephemeral static_host build push <tenant_name> -H default -f ./site
796
+ xano ephemeral static_host build list <tenant_name> -H default
797
+ xano ephemeral static_host build get <tenant_name> -H default --build_id 52
798
+ xano ephemeral static_host deploy <tenant_name> -H default --build_id 52 --env prod
799
+ xano ephemeral static_host build pull <tenant_name> -H default --latest
800
+ xano ephemeral static_host build delete <tenant_name> -H default --build_id 52
801
+ ```
802
+
803
+ > Static hosting is currently available for **local** tenants. Remote (tier2/tier3)
804
+ > tenants are not yet supported and will return an error.
805
+
697
806
  ### Sandbox
698
807
 
699
808
  Manage your sandbox tenant. Each user has a single sandbox tenant that is auto-provisioned on first use.
@@ -15,6 +15,11 @@ export interface CredentialsFile {
15
15
  };
16
16
  }
17
17
  export declare function buildUserAgent(version: string): string;
18
+ /**
19
+ * True when `error` is the "foreign dispatcher rejected by built-in fetch"
20
+ * failure (Node 26+). The real code lives on `error.cause.code`.
21
+ */
22
+ export declare function isInvalidDispatcherError(error: unknown): boolean;
18
23
  export interface SandboxTenant {
19
24
  created_at?: string;
20
25
  description?: string;
@@ -22,7 +27,7 @@ export interface SandboxTenant {
22
27
  ephemeral?: boolean;
23
28
  id: number;
24
29
  name: string;
25
- sandbox_expires_at?: string | number;
30
+ sandbox_expires_at?: number | string;
26
31
  state?: string;
27
32
  xano_domain?: string;
28
33
  }
@@ -52,57 +57,28 @@ export default abstract class BaseCommand extends Command {
52
57
  config: LocalProfileConfig;
53
58
  path: string;
54
59
  };
55
- protected updateNotice: string | null;
56
- init(): Promise<void>;
57
- /**
58
- * Find and parse the nearest project-local profile.yaml, unless an explicit
59
- * -p/XANO_PROFILE was given (in which case the local file is ignored).
60
- */
61
- private loadLocalProfile;
62
- /** Print the one-line target banner when a local profile.yaml is in effect. */
63
- private maybePrintLocalProfileBanner;
64
- finally(_: Error | undefined): Promise<void>;
65
- private isJsonOutput;
60
+ protected updateNotice: null | string;
66
61
  /**
67
62
  * Apply insecure TLS mode if the active profile has insecure: true.
68
63
  * Sets NODE_TLS_REJECT_UNAUTHORIZED=0 so all fetch() calls skip cert verification.
69
64
  */
70
65
  protected applyInsecureFromProfile(): void;
71
- protected getDefaultProfile(): string;
72
- protected getProfile(): string | undefined;
66
+ finally(_: Error | undefined): Promise<void>;
73
67
  /**
74
68
  * Get the resolved credentials file path, respecting --config flag and XANO_CONFIG env var.
75
69
  * Reads -c/--config from process.argv directly because oclif doesn't set this.flags
76
70
  * from parsed results — the static flags property is the flag definition, not parsed values.
77
71
  */
78
72
  protected getCredentialsPath(): string;
79
- protected loadCredentialsFile(): CredentialsFile | null;
73
+ protected getDefaultProfile(): string;
80
74
  /**
81
75
  * Get or create the singleton sandbox environment for the authenticated user.
82
76
  * Returns the sandbox object (existing or newly created).
83
77
  */
84
78
  protected getOrCreateSandbox(profile: ProfileConfig, verbose: boolean): Promise<SandboxTenant>;
85
- /**
86
- * Resolve the profile from flags and any project-local profile.yaml,
87
- * validating instance_origin and access_token exist.
88
- * Precedence: -p/XANO_PROFILE > profile.yaml > credentials default.
89
- */
90
- protected resolveProfile(flags: {
91
- profile?: string;
92
- }): {
93
- profile: ProfileConfig;
94
- profileName: string;
95
- };
96
- /**
97
- * Parse an API error response and return a clean error message.
98
- * Extracts the message from JSON responses and adds context for common errors.
99
- */
100
- protected parseApiError(response: Response, fallbackPrefix: string): Promise<string>;
101
- /**
102
- * Make an HTTP request with optional verbose logging.
103
- * Use this for all Metadata API calls to support the --verbose flag.
104
- */
105
- protected verboseFetch(url: string, options: RequestInit, verbose: boolean, authToken?: string): Promise<Response>;
79
+ protected getProfile(): string | undefined;
80
+ init(): Promise<void>;
81
+ protected loadCredentialsFile(): CredentialsFile | null;
106
82
  /**
107
83
  * Poll a static-host build until it reaches a terminal status (ok | error),
108
84
  * showing a live, ticking spinner with the current stage and elapsed time —
@@ -117,17 +93,48 @@ export default abstract class BaseCommand extends Command {
117
93
  protected logStaticHostUrls(opts: {
118
94
  profile: ProfileConfig;
119
95
  staticHost: string;
96
+ tenantName?: string;
120
97
  verbose: boolean;
121
98
  workspaceId: string;
122
99
  }): Promise<void>;
100
+ /**
101
+ * Parse an API error response and return a clean error message.
102
+ * Extracts the message from JSON responses and adds context for common errors.
103
+ */
104
+ protected parseApiError(response: Response, fallbackPrefix: string): Promise<string>;
105
+ /**
106
+ * Resolve the profile from flags and any project-local profile.yaml,
107
+ * validating instance_origin and access_token exist.
108
+ * Precedence: -p/XANO_PROFILE > profile.yaml > credentials default.
109
+ */
110
+ protected resolveProfile(flags: {
111
+ profile?: string;
112
+ }): {
113
+ profile: ProfileConfig;
114
+ profileName: string;
115
+ };
116
+ /**
117
+ * Make an HTTP request with optional verbose logging.
118
+ * Use this for all Metadata API calls to support the --verbose flag.
119
+ */
120
+ protected verboseFetch(url: string, options: RequestInit, verbose: boolean, authToken?: string): Promise<Response>;
123
121
  protected waitForBuild(opts: {
124
122
  buildId: number | string;
125
123
  intervalMs?: number;
126
124
  profile: ProfileConfig;
127
125
  quiet?: boolean;
128
126
  staticHost: string;
127
+ tenantName?: string;
129
128
  timeoutMs?: number;
130
129
  verbose: boolean;
131
130
  workspaceId: string;
132
131
  }): Promise<string>;
132
+ private isJsonOutput;
133
+ /**
134
+ * Find and parse the nearest project-local profile.yaml, unless an explicit
135
+ * -p/XANO_PROFILE was given (in which case the local file is ignored).
136
+ */
137
+ private loadLocalProfile;
138
+ /** Print the one-line target banner when a local profile.yaml is in effect. */
139
+ private maybePrintLocalProfileBanner;
133
140
  }