@xano/cli 0.0.95 → 1.0.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 (236) hide show
  1. package/README.md +133 -66
  2. package/dist/base-command.d.ts +41 -1
  3. package/dist/base-command.js +92 -3
  4. package/dist/commands/auth/index.d.ts +1 -0
  5. package/dist/commands/auth/index.js +16 -11
  6. package/dist/commands/branch/create/index.d.ts +4 -1
  7. package/dist/commands/branch/create/index.js +22 -21
  8. package/dist/commands/branch/delete/index.d.ts +1 -0
  9. package/dist/commands/branch/delete/index.js +1 -4
  10. package/dist/commands/branch/edit/index.d.ts +1 -0
  11. package/dist/commands/branch/edit/index.js +1 -4
  12. package/dist/commands/branch/get/index.d.ts +1 -0
  13. package/dist/commands/branch/get/index.js +1 -4
  14. package/dist/commands/branch/list/index.d.ts +2 -6
  15. package/dist/commands/branch/list/index.js +13 -17
  16. package/dist/commands/branch/set_live/index.d.ts +1 -0
  17. package/dist/commands/branch/set_live/index.js +1 -4
  18. package/dist/commands/function/create/index.d.ts +1 -0
  19. package/dist/commands/function/create/index.js +1 -2
  20. package/dist/commands/function/edit/index.d.ts +1 -0
  21. package/dist/commands/function/edit/index.js +1 -2
  22. package/dist/commands/function/get/index.d.ts +1 -0
  23. package/dist/commands/function/get/index.js +1 -4
  24. package/dist/commands/function/list/index.d.ts +1 -0
  25. package/dist/commands/function/list/index.js +1 -4
  26. package/dist/commands/platform/get/index.d.ts +1 -0
  27. package/dist/commands/platform/get/index.js +1 -4
  28. package/dist/commands/platform/list/index.d.ts +1 -0
  29. package/dist/commands/platform/list/index.js +1 -4
  30. package/dist/commands/profile/create/index.d.ts +1 -0
  31. package/dist/commands/profile/create/index.js +12 -6
  32. package/dist/commands/profile/delete/index.d.ts +1 -0
  33. package/dist/commands/profile/delete/index.js +8 -4
  34. package/dist/commands/profile/edit/index.d.ts +1 -0
  35. package/dist/commands/profile/edit/index.js +3 -6
  36. package/dist/commands/profile/get/index.d.ts +3 -0
  37. package/dist/commands/profile/get/index.js +12 -5
  38. package/dist/commands/profile/list/index.d.ts +1 -0
  39. package/dist/commands/profile/list/index.js +8 -4
  40. package/dist/commands/profile/me/index.d.ts +1 -0
  41. package/dist/commands/profile/me/index.js +22 -6
  42. package/dist/commands/profile/set/index.d.ts +3 -0
  43. package/dist/commands/profile/set/index.js +12 -6
  44. package/dist/commands/profile/token/index.d.ts +3 -0
  45. package/dist/commands/profile/token/index.js +12 -5
  46. package/dist/commands/profile/wizard/index.d.ts +1 -0
  47. package/dist/commands/profile/wizard/index.js +16 -12
  48. package/dist/commands/profile/workspace/index.d.ts +3 -0
  49. package/dist/commands/profile/workspace/index.js +12 -5
  50. package/dist/commands/profile/workspace/set/index.d.ts +1 -0
  51. package/dist/commands/profile/workspace/set/index.js +2 -4
  52. package/dist/commands/release/create/index.d.ts +4 -1
  53. package/dist/commands/release/create/index.js +12 -14
  54. package/dist/commands/release/delete/index.d.ts +1 -0
  55. package/dist/commands/release/delete/index.js +1 -4
  56. package/dist/commands/release/deploy/index.d.ts +20 -0
  57. package/dist/commands/release/deploy/index.js +137 -0
  58. package/dist/commands/release/edit/index.d.ts +1 -0
  59. package/dist/commands/release/edit/index.js +1 -4
  60. package/dist/commands/release/export/index.d.ts +1 -0
  61. package/dist/commands/release/export/index.js +1 -3
  62. package/dist/commands/release/get/index.d.ts +1 -0
  63. package/dist/commands/release/get/index.js +1 -4
  64. package/dist/commands/release/import/index.d.ts +1 -0
  65. package/dist/commands/release/import/index.js +1 -3
  66. package/dist/commands/release/list/index.d.ts +1 -0
  67. package/dist/commands/release/list/index.js +1 -4
  68. package/dist/commands/release/pull/index.d.ts +2 -3
  69. package/dist/commands/release/pull/index.js +19 -18
  70. package/dist/commands/release/push/index.d.ts +2 -3
  71. package/dist/commands/release/push/index.js +19 -22
  72. package/dist/commands/sandbox/delete/index.d.ts +13 -0
  73. package/dist/commands/sandbox/delete/index.js +71 -0
  74. package/dist/commands/sandbox/env/delete/index.d.ts +15 -0
  75. package/dist/commands/sandbox/env/delete/index.js +89 -0
  76. package/dist/commands/sandbox/env/get/index.d.ts +13 -0
  77. package/dist/commands/sandbox/env/get/index.js +65 -0
  78. package/dist/commands/sandbox/env/get_all/index.d.ts +14 -0
  79. package/dist/commands/sandbox/env/get_all/index.js +78 -0
  80. package/dist/commands/sandbox/env/list/index.d.ts +12 -0
  81. package/dist/commands/sandbox/env/list/index.js +67 -0
  82. package/dist/commands/sandbox/env/set/index.d.ts +14 -0
  83. package/dist/commands/sandbox/env/set/index.js +74 -0
  84. package/dist/commands/sandbox/env/set_all/index.d.ts +14 -0
  85. package/dist/commands/sandbox/env/set_all/index.js +86 -0
  86. package/dist/commands/sandbox/get/index.d.ts +12 -0
  87. package/dist/commands/sandbox/get/index.js +63 -0
  88. package/dist/commands/sandbox/impersonate/index.d.ts +5 -0
  89. package/dist/commands/sandbox/impersonate/index.js +5 -0
  90. package/dist/commands/sandbox/license/get/index.d.ts +14 -0
  91. package/dist/commands/sandbox/license/get/index.js +78 -0
  92. package/dist/commands/sandbox/license/set/index.d.ts +15 -0
  93. package/dist/commands/sandbox/license/set/index.js +95 -0
  94. package/dist/commands/sandbox/pull/index.d.ts +16 -0
  95. package/dist/commands/sandbox/pull/index.js +185 -0
  96. package/dist/commands/sandbox/push/index.d.ts +26 -0
  97. package/dist/commands/sandbox/push/index.js +196 -0
  98. package/dist/commands/sandbox/reset/index.d.ts +13 -0
  99. package/dist/commands/sandbox/reset/index.js +71 -0
  100. package/dist/commands/sandbox/review/index.d.ts +14 -0
  101. package/dist/commands/sandbox/review/index.js +94 -0
  102. package/dist/commands/sandbox/unit_test/list/index.d.ts +14 -0
  103. package/dist/commands/sandbox/unit_test/list/index.js +91 -0
  104. package/dist/commands/sandbox/unit_test/run/index.d.ts +15 -0
  105. package/dist/commands/sandbox/unit_test/run/index.js +79 -0
  106. package/dist/commands/sandbox/unit_test/run_all/index.d.ts +14 -0
  107. package/dist/commands/sandbox/unit_test/run_all/index.js +171 -0
  108. package/dist/commands/sandbox/workflow_test/list/index.d.ts +13 -0
  109. package/dist/commands/sandbox/workflow_test/list/index.js +84 -0
  110. package/dist/commands/sandbox/workflow_test/run/index.d.ts +18 -0
  111. package/dist/commands/sandbox/workflow_test/run/index.js +77 -0
  112. package/dist/commands/sandbox/workflow_test/run_all/index.d.ts +13 -0
  113. package/dist/commands/sandbox/workflow_test/run_all/index.js +157 -0
  114. package/dist/commands/static_host/build/create/index.d.ts +1 -0
  115. package/dist/commands/static_host/build/create/index.js +1 -3
  116. package/dist/commands/static_host/build/get/index.d.ts +1 -0
  117. package/dist/commands/static_host/build/get/index.js +1 -4
  118. package/dist/commands/static_host/build/list/index.d.ts +1 -0
  119. package/dist/commands/static_host/build/list/index.js +1 -4
  120. package/dist/commands/static_host/list/index.d.ts +1 -0
  121. package/dist/commands/static_host/list/index.js +1 -4
  122. package/dist/commands/tenant/backup/create/index.d.ts +1 -0
  123. package/dist/commands/tenant/backup/create/index.js +1 -4
  124. package/dist/commands/tenant/backup/delete/index.d.ts +1 -0
  125. package/dist/commands/tenant/backup/delete/index.js +1 -4
  126. package/dist/commands/tenant/backup/export/index.d.ts +1 -0
  127. package/dist/commands/tenant/backup/export/index.js +1 -3
  128. package/dist/commands/tenant/backup/import/index.d.ts +1 -0
  129. package/dist/commands/tenant/backup/import/index.js +1 -3
  130. package/dist/commands/tenant/backup/list/index.d.ts +1 -0
  131. package/dist/commands/tenant/backup/list/index.js +1 -4
  132. package/dist/commands/tenant/backup/restore/index.d.ts +1 -0
  133. package/dist/commands/tenant/backup/restore/index.js +1 -4
  134. package/dist/commands/tenant/cluster/create/index.d.ts +1 -0
  135. package/dist/commands/tenant/cluster/create/index.js +1 -3
  136. package/dist/commands/tenant/cluster/delete/index.d.ts +1 -0
  137. package/dist/commands/tenant/cluster/delete/index.js +1 -4
  138. package/dist/commands/tenant/cluster/edit/index.d.ts +1 -0
  139. package/dist/commands/tenant/cluster/edit/index.js +1 -4
  140. package/dist/commands/tenant/cluster/get/index.d.ts +1 -0
  141. package/dist/commands/tenant/cluster/get/index.js +1 -4
  142. package/dist/commands/tenant/cluster/license/get/index.d.ts +1 -0
  143. package/dist/commands/tenant/cluster/license/get/index.js +1 -3
  144. package/dist/commands/tenant/cluster/license/set/index.d.ts +1 -0
  145. package/dist/commands/tenant/cluster/license/set/index.js +1 -3
  146. package/dist/commands/tenant/cluster/list/index.d.ts +1 -0
  147. package/dist/commands/tenant/cluster/list/index.js +1 -4
  148. package/dist/commands/tenant/create/index.d.ts +1 -1
  149. package/dist/commands/tenant/create/index.js +1 -8
  150. package/dist/commands/tenant/delete/index.d.ts +1 -0
  151. package/dist/commands/tenant/delete/index.js +1 -4
  152. package/dist/commands/tenant/deploy_platform/index.d.ts +1 -0
  153. package/dist/commands/tenant/deploy_platform/index.js +1 -3
  154. package/dist/commands/tenant/deploy_release/index.d.ts +1 -0
  155. package/dist/commands/tenant/deploy_release/index.js +1 -4
  156. package/dist/commands/tenant/edit/index.d.ts +1 -0
  157. package/dist/commands/tenant/edit/index.js +1 -4
  158. package/dist/commands/tenant/env/delete/index.d.ts +1 -0
  159. package/dist/commands/tenant/env/delete/index.js +1 -4
  160. package/dist/commands/tenant/env/get/index.d.ts +1 -0
  161. package/dist/commands/tenant/env/get/index.js +1 -4
  162. package/dist/commands/tenant/env/get_all/index.d.ts +1 -0
  163. package/dist/commands/tenant/env/get_all/index.js +1 -3
  164. package/dist/commands/tenant/env/list/index.d.ts +1 -0
  165. package/dist/commands/tenant/env/list/index.js +1 -4
  166. package/dist/commands/tenant/env/set/index.d.ts +1 -0
  167. package/dist/commands/tenant/env/set/index.js +1 -4
  168. package/dist/commands/tenant/env/set_all/index.d.ts +1 -0
  169. package/dist/commands/tenant/env/set_all/index.js +1 -3
  170. package/dist/commands/tenant/get/index.d.ts +1 -0
  171. package/dist/commands/tenant/get/index.js +3 -6
  172. package/dist/commands/tenant/impersonate/index.d.ts +1 -0
  173. package/dist/commands/tenant/impersonate/index.js +1 -4
  174. package/dist/commands/tenant/license/get/index.d.ts +1 -0
  175. package/dist/commands/tenant/license/get/index.js +1 -3
  176. package/dist/commands/tenant/license/set/index.d.ts +1 -0
  177. package/dist/commands/tenant/license/set/index.js +1 -3
  178. package/dist/commands/tenant/list/index.d.ts +1 -0
  179. package/dist/commands/tenant/list/index.js +3 -6
  180. package/dist/commands/tenant/pull/index.d.ts +2 -3
  181. package/dist/commands/tenant/pull/index.js +20 -21
  182. package/dist/commands/tenant/push/index.d.ts +2 -22
  183. package/dist/commands/tenant/push/index.js +7 -259
  184. package/dist/commands/tenant/unit_test/list/index.d.ts +16 -0
  185. package/dist/commands/tenant/unit_test/list/index.js +115 -0
  186. package/dist/commands/tenant/unit_test/run/index.d.ts +17 -0
  187. package/dist/commands/tenant/unit_test/run/index.js +103 -0
  188. package/dist/commands/tenant/unit_test/run_all/index.d.ts +16 -0
  189. package/dist/commands/tenant/unit_test/run_all/index.js +190 -0
  190. package/dist/commands/tenant/workflow_test/list/index.d.ts +15 -0
  191. package/dist/commands/tenant/workflow_test/list/index.js +108 -0
  192. package/dist/commands/tenant/workflow_test/run/index.d.ts +20 -0
  193. package/dist/commands/tenant/workflow_test/run/index.js +101 -0
  194. package/dist/commands/tenant/workflow_test/run_all/index.d.ts +15 -0
  195. package/dist/commands/tenant/workflow_test/run_all/index.js +176 -0
  196. package/dist/commands/unit_test/list/index.d.ts +1 -0
  197. package/dist/commands/unit_test/list/index.js +1 -4
  198. package/dist/commands/unit_test/run/index.d.ts +1 -0
  199. package/dist/commands/unit_test/run/index.js +1 -4
  200. package/dist/commands/unit_test/run_all/index.d.ts +1 -0
  201. package/dist/commands/unit_test/run_all/index.js +1 -4
  202. package/dist/commands/update/index.d.ts +1 -0
  203. package/dist/commands/workflow_test/delete/index.d.ts +1 -0
  204. package/dist/commands/workflow_test/delete/index.js +1 -4
  205. package/dist/commands/workflow_test/get/index.d.ts +1 -0
  206. package/dist/commands/workflow_test/get/index.js +1 -4
  207. package/dist/commands/workflow_test/list/index.d.ts +1 -0
  208. package/dist/commands/workflow_test/list/index.js +1 -4
  209. package/dist/commands/workflow_test/run/index.d.ts +1 -0
  210. package/dist/commands/workflow_test/run/index.js +1 -4
  211. package/dist/commands/workflow_test/run_all/index.d.ts +1 -0
  212. package/dist/commands/workflow_test/run_all/index.js +1 -4
  213. package/dist/commands/workspace/create/index.d.ts +1 -0
  214. package/dist/commands/workspace/create/index.js +1 -4
  215. package/dist/commands/workspace/delete/index.d.ts +2 -6
  216. package/dist/commands/workspace/delete/index.js +17 -16
  217. package/dist/commands/workspace/edit/index.d.ts +3 -6
  218. package/dist/commands/workspace/edit/index.js +31 -25
  219. package/dist/commands/workspace/get/index.d.ts +2 -6
  220. package/dist/commands/workspace/get/index.js +23 -25
  221. package/dist/commands/workspace/git/pull/index.d.ts +2 -3
  222. package/dist/commands/workspace/git/pull/index.js +18 -17
  223. package/dist/commands/workspace/list/index.d.ts +2 -0
  224. package/dist/commands/workspace/list/index.js +15 -11
  225. package/dist/commands/workspace/pull/index.d.ts +2 -3
  226. package/dist/commands/workspace/pull/index.js +21 -24
  227. package/dist/commands/workspace/push/index.d.ts +7 -16
  228. package/dist/commands/workspace/push/index.js +85 -674
  229. package/dist/help.d.ts +2 -1
  230. package/dist/help.js +39 -1
  231. package/dist/utils/multidoc-push.d.ts +63 -0
  232. package/dist/utils/multidoc-push.js +690 -0
  233. package/dist/utils/reference-checker.d.ts +57 -0
  234. package/dist/utils/reference-checker.js +232 -0
  235. package/oclif.manifest.json +5631 -2297
  236. package/package.json +17 -2
package/README.md CHANGED
@@ -25,7 +25,7 @@ npm install -g @xano/cli
25
25
 
26
26
  3. Pull a workspace to local files:
27
27
  ```bash
28
- xano workspace pull ./my-workspace
28
+ xano workspace pull
29
29
  ```
30
30
 
31
31
  ## Commands
@@ -92,70 +92,76 @@ xano profile delete myprofile --force
92
92
  xano workspace list
93
93
 
94
94
  # Get workspace details
95
- xano workspace get <workspace_id>
95
+ xano workspace get -w <workspace_id>
96
96
 
97
97
  # Create a workspace
98
98
  xano workspace create my-workspace
99
99
  xano workspace create my-workspace -d "My application workspace"
100
100
 
101
101
  # Edit a workspace
102
- xano workspace edit <workspace_id> --name "new-name" -d "Updated description"
103
- xano workspace edit <workspace_id> --swagger # Enable swagger docs
104
- xano workspace edit <workspace_id> --no-swagger # Disable swagger docs
105
- xano workspace edit <workspace_id> --require-token # Require token for docs
102
+ xano workspace edit -w <workspace_id> --name "new-name" -d "Updated description"
103
+ xano workspace edit -w <workspace_id> --swagger # Enable swagger docs
104
+ xano workspace edit -w <workspace_id> --no-swagger # Disable swagger docs
105
+ xano workspace edit -w <workspace_id> --require-token # Require token for docs
106
106
 
107
107
  # Delete a workspace (confirmation required)
108
- xano workspace delete <workspace_id>
109
- xano workspace delete <workspace_id> --force
110
-
111
- # Pull workspace to local files
112
- xano workspace pull ./my-workspace
113
- xano workspace pull ./my-workspace -b dev # Specific branch
114
- xano workspace pull ./my-workspace --env --records # Include env vars and table records
115
- xano workspace pull ./my-workspace --draft # Include draft changes
116
-
117
- # Push local files to workspace (only changed files by default)
118
- xano workspace push ./my-workspace
119
- xano workspace push ./my-workspace -b dev
120
- xano workspace push ./my-workspace --sync # Full push send all files, not just changed ones
121
- xano workspace push ./my-workspace --sync --delete # Full push + delete remote objects not included
122
- xano workspace push ./my-workspace --dry-run # Preview changes without pushing
123
- xano workspace push ./my-workspace --records # Include table records
124
- xano workspace push ./my-workspace --env # Include environment variables
125
- xano workspace push ./my-workspace --truncate # Truncate tables before import
126
- xano workspace push ./my-workspace --no-transaction # Disable database transaction wrapping
127
- xano workspace push ./my-workspace --no-guids # Skip writing GUIDs back to local files
128
- xano workspace push ./my-workspace --force # Skip preview and confirmation (for CI/CD)
129
- xano workspace push ./my-workspace -i "function/*" # Push only matching files
130
- xano workspace push ./my-workspace -e "table/*" # Push all files except tables
131
- xano workspace push ./my-workspace -i "function/*" -e "**/test*" # Include functions, exclude tests
132
-
133
- # Pull from a git repository to local files
134
- xano workspace git pull ./output -r https://github.com/owner/repo
135
- xano workspace git pull ./output -r https://github.com/owner/repo -b main
136
- xano workspace git pull ./output -r https://github.com/owner/repo/tree/main/path/to/dir
137
- xano workspace git pull ./output -r https://github.com/owner/repo/blob/main/file.xs
138
- xano workspace git pull ./output -r git@github.com:owner/repo.git
139
- xano workspace git pull ./output -r https://gitlab.com/owner/repo/-/tree/master/path
140
- xano workspace git pull ./output -r https://github.com/owner/private-repo -t ghp_xxx
141
- xano workspace git pull ./output -r https://github.com/owner/repo --path subdir
108
+ xano workspace delete -w <workspace_id>
109
+ xano workspace delete -w <workspace_id> --force
110
+
111
+ # Pull workspace to local files (defaults to current directory)
112
+ xano workspace pull
113
+ xano workspace pull -d ./my-workspace # Specify output directory
114
+ xano workspace pull -b dev # Specific branch
115
+ xano workspace pull --env --records # Include env vars and table records
116
+ xano workspace pull --draft # Include draft changes
117
+
118
+ # Push local files to workspace (defaults to current directory, only changed files)
119
+ xano workspace push
120
+ xano workspace push -d ./my-workspace # Push from a specific directory
121
+ xano workspace push -b dev
122
+ xano workspace push --sync # Full push send all files, not just changed ones
123
+ xano workspace push --sync --delete # Full push + delete remote objects not included
124
+ xano workspace push --dry-run # Preview changes without pushing
125
+ xano workspace push --records # Include table records
126
+ xano workspace push --env # Include environment variables
127
+ xano workspace push --truncate # Truncate tables before import
128
+ xano workspace push --no-transaction # Disable database transaction wrapping
129
+ xano workspace push --no-guids # Skip writing GUIDs back to local files
130
+ xano workspace push --force # Skip preview and confirmation (for CI/CD)
131
+ xano workspace push -i "function/*" # Push only matching files
132
+ xano workspace push -e "table/*" # Push all files except tables
133
+ xano workspace push -i "function/*" -e "**/test*" # Include functions, exclude tests
134
+
135
+ # Pull from a git repository to local files (defaults to current directory)
136
+ xano workspace git pull -r https://github.com/owner/repo
137
+ xano workspace git pull -d ./output -r https://github.com/owner/repo
138
+ xano workspace git pull -r https://github.com/owner/repo -b main
139
+ xano workspace git pull -r https://github.com/owner/repo/tree/main/path/to/dir
140
+ xano workspace git pull -r https://github.com/owner/repo/blob/main/file.xs
141
+ xano workspace git pull -r git@github.com:owner/repo.git
142
+ xano workspace git pull -r https://gitlab.com/owner/repo/-/tree/master/path
143
+ xano workspace git pull -r https://github.com/owner/private-repo -t ghp_xxx
144
+ xano workspace git pull -r https://github.com/owner/repo --path subdir
142
145
  ```
143
146
 
144
147
  ### Branches
145
148
 
146
149
  All branch commands use **branch labels** (e.g., `v1`, `dev`), not IDs.
147
150
 
151
+ The `v1` branch is the default branch and always exists. It cannot be created, edited, or deleted.
152
+
148
153
  ```bash
149
154
  # List branches
150
155
  xano branch list
156
+ xano branch list -w <workspace_id>
151
157
 
152
158
  # Get branch details
153
159
  xano branch get <branch_label>
154
160
 
155
161
  # Create a branch
156
- xano branch create --label dev
157
- xano branch create -l feature-auth -s dev -d "Auth feature"
158
- xano branch create -l staging --color "#ebc346"
162
+ xano branch create dev
163
+ xano branch create feature-auth -s dev -d "Auth feature"
164
+ xano branch create staging --color "#ebc346"
159
165
 
160
166
  # Edit a branch
161
167
  xano branch edit <branch_label> --label "new-label"
@@ -209,9 +215,9 @@ xano release list
209
215
  xano release get <release_name>
210
216
 
211
217
  # Create a release
212
- xano release create --name "v1.0" --branch main
213
- xano release create --name "v1.1-hotfix" --branch main --hotfix
214
- xano release create --name "v1.0" --branch main --table-ids 1,2,3
218
+ xano release create "v1.0" --branch main
219
+ xano release create "v1.1-hotfix" --branch main --hotfix
220
+ xano release create "v1.0" --branch main --table-ids 1,2,3
215
221
 
216
222
  # Edit a release
217
223
  xano release edit <release_name> --name "v1.0-final" -d "Updated description"
@@ -227,14 +233,22 @@ xano release import --file ./my-release.tar.gz
227
233
  xano release delete <release_name>
228
234
  xano release delete <release_name> --force
229
235
 
230
- # Pull release to local files
231
- xano release pull ./my-release -r v1.0
232
- xano release pull ./my-release -r v1.0 --env --records
233
-
234
- # Push local files as a new release
235
- xano release push ./my-release -n "v2.0"
236
- xano release push ./my-release -n "v2.0" --hotfix -d "Critical fix"
237
- xano release push ./my-release -n "v2.0" --no-records --no-env
236
+ # Pull release to local files (defaults to current directory)
237
+ xano release pull -r v1.0
238
+ xano release pull -d ./my-release -r v1.0
239
+ xano release pull -r v1.0 --env --records
240
+
241
+ # Push local files as a new release (defaults to current directory)
242
+ xano release push -n "v2.0"
243
+ xano release push -d ./my-release -n "v2.0"
244
+ xano release push -n "v2.0" --hotfix --description "Critical fix"
245
+ xano release push -n "v2.0" --no-records --no-env
246
+
247
+ # Deploy a release to its workspace as a new branch (confirmation required)
248
+ xano release deploy "v1.0"
249
+ xano release deploy "v1.0" --force
250
+ xano release deploy "v1.0" --branch "restore-v1" --no-set_live
251
+ xano release deploy "v1.0" -w 40 -o json --force
238
252
  ```
239
253
 
240
254
  ### Platforms
@@ -329,17 +343,19 @@ xano tenant impersonate <tenant_name> -o json
329
343
  #### Pull / Push
330
344
 
331
345
  ```bash
332
- # Pull tenant to local files
333
- xano tenant pull ./my-tenant -t <tenant_name>
334
- xano tenant pull ./my-tenant -t <tenant_name> --env --records
335
- xano tenant pull ./my-tenant -t <tenant_name> --draft
336
-
337
- # Push local files to tenant
338
- xano tenant push ./my-tenant -t <tenant_name>
339
- xano tenant push ./my-tenant -t <tenant_name> --records # Include table records
340
- xano tenant push ./my-tenant -t <tenant_name> --env # Include environment variables
341
- xano tenant push ./my-tenant -t <tenant_name> --truncate
342
- xano tenant push ./my-tenant -t <tenant_name> --no-transaction # Disable transaction wrapping
346
+ # Pull tenant to local files (defaults to current directory)
347
+ xano tenant pull -t <tenant_name>
348
+ xano tenant pull -d ./my-tenant -t <tenant_name>
349
+ xano tenant pull -t <tenant_name> --env --records
350
+ xano tenant pull -t <tenant_name> --draft
351
+
352
+ # Push local files to tenant (defaults to current directory)
353
+ xano tenant push -t <tenant_name>
354
+ xano tenant push -d ./my-tenant -t <tenant_name>
355
+ xano tenant push -t <tenant_name> --records # Include table records
356
+ xano tenant push -t <tenant_name> --env # Include environment variables
357
+ xano tenant push -t <tenant_name> --truncate
358
+ xano tenant push -t <tenant_name> --no-transaction # Disable transaction wrapping
343
359
  ```
344
360
 
345
361
  #### Deployments
@@ -441,6 +457,45 @@ xano tenant cluster license set <cluster_id>
441
457
  xano tenant cluster license set <cluster_id> --file ./kubeconfig.yaml
442
458
  ```
443
459
 
460
+ ### Sandbox
461
+
462
+ Manage your sandbox tenant. Each user has a single sandbox tenant that is auto-provisioned on first use.
463
+
464
+ ```bash
465
+ # Get your sandbox tenant (creates if needed)
466
+ xano sandbox get
467
+ xano sandbox get -o json
468
+
469
+ # Pull sandbox to local files (defaults to current directory)
470
+ xano sandbox pull
471
+ xano sandbox pull -d ./my-sandbox
472
+ xano sandbox pull --env --records
473
+
474
+ # Push local files to sandbox (defaults to current directory, only changed files)
475
+ xano sandbox push
476
+ xano sandbox push -d ./my-workspace # Push from a specific directory
477
+ xano sandbox push --sync # Full push — send all files
478
+ xano sandbox push --sync --delete # Full push + delete remote objects not included
479
+ xano sandbox push --dry-run # Preview changes without pushing
480
+ xano sandbox push --records --env # Include records and environment variables
481
+ xano sandbox push --truncate # Truncate tables before import
482
+ xano sandbox push --no-guids # Skip writing GUIDs back to local files
483
+ xano sandbox push --force # Skip preview and confirmation
484
+ xano sandbox push -i "function/*" # Push only matching files
485
+ xano sandbox push -e "table/*" # Push all files except tables
486
+ xano sandbox push --review # Push and open sandbox review in the browser
487
+
488
+ # Review (open in browser)
489
+ xano sandbox review
490
+
491
+ # Impersonate (open in browser)
492
+ xano sandbox impersonate
493
+
494
+ # Reset all workspace data
495
+ xano sandbox reset
496
+ xano sandbox reset --force
497
+ ```
498
+
444
499
  ### Static Hosts
445
500
 
446
501
  ```bash
@@ -463,6 +518,7 @@ All commands support these options:
463
518
 
464
519
  | Flag | Description |
465
520
  |------|-------------|
521
+ | `-c, --config` | Path to credentials file (or set `XANO_CONFIG` env var). Default: `~/.xano/credentials.yaml` |
466
522
  | `-p, --profile` | Profile to use (or set `XANO_PROFILE` env var) |
467
523
  | `-w, --workspace` | Workspace ID (overrides profile default) |
468
524
  | `-o, --output` | Output format: `summary` (default) or `json` |
@@ -483,7 +539,18 @@ This will show:
483
539
 
484
540
  ## Configuration
485
541
 
486
- Profiles are stored in `~/.xano/credentials.yaml`:
542
+ Profiles are stored in `~/.xano/credentials.yaml` by default. You can use a different credentials file with:
543
+
544
+ ```bash
545
+ # Via flag
546
+ xano profile list -c /path/to/other-credentials.yaml
547
+
548
+ # Via environment variable
549
+ export XANO_CONFIG=/path/to/other-credentials.yaml
550
+ xano workspace list
551
+ ```
552
+
553
+ ### Credentials File Format
487
554
 
488
555
  ```yaml
489
556
  profiles:
@@ -14,12 +14,30 @@ 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
+ sandbox_expires_at?: string | number;
25
+ state?: string;
26
+ xano_domain?: string;
27
+ }
28
+ /**
29
+ * Resolve the credentials file path from flag, env var, or default.
30
+ * Checks (in order): explicit configPath arg, XANO_CONFIG env var, ~/.xano/credentials.yaml
31
+ */
32
+ export declare function resolveCredentialsPath(configPath?: string): string;
17
33
  export default abstract class BaseCommand extends Command {
18
34
  static baseFlags: {
35
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
19
36
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
20
37
  verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
21
38
  };
22
39
  static flags: {
40
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
23
41
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
24
42
  verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
25
43
  };
@@ -35,9 +53,31 @@ export default abstract class BaseCommand extends Command {
35
53
  protected getDefaultProfile(): string;
36
54
  protected getProfile(): string | undefined;
37
55
  /**
38
- * Load and parse the credentials file. Returns null if the file doesn't exist.
56
+ * Get the resolved credentials file path, respecting --config flag and XANO_CONFIG env var.
57
+ * Reads -c/--config from process.argv directly because oclif doesn't set this.flags
58
+ * from parsed results — the static flags property is the flag definition, not parsed values.
39
59
  */
60
+ protected getCredentialsPath(): string;
40
61
  protected loadCredentialsFile(): CredentialsFile | null;
62
+ /**
63
+ * Get or create the singleton sandbox environment for the authenticated user.
64
+ * Returns the sandbox object (existing or newly created).
65
+ */
66
+ protected getOrCreateSandbox(profile: ProfileConfig, verbose: boolean): Promise<SandboxTenant>;
67
+ /**
68
+ * Resolve profile from flags, validating instance_origin and access_token exist.
69
+ */
70
+ protected resolveProfile(flags: {
71
+ profile?: string;
72
+ }): {
73
+ profile: ProfileConfig;
74
+ profileName: string;
75
+ };
76
+ /**
77
+ * Parse an API error response and return a clean error message.
78
+ * Extracts the message from JSON responses and adds context for common errors.
79
+ */
80
+ protected parseApiError(response: Response, fallbackPrefix: string): Promise<string>;
41
81
  /**
42
82
  * Make an HTTP request with optional verbose logging.
43
83
  * Use this for all Metadata API calls to support the --verbose flag.
@@ -7,8 +7,25 @@ import { checkForUpdate } from './update-check.js';
7
7
  export function buildUserAgent(version) {
8
8
  return `xano-cli/${version} (${process.platform}; ${process.arch}) node/${process.version}`;
9
9
  }
10
+ /**
11
+ * Resolve the credentials file path from flag, env var, or default.
12
+ * Checks (in order): explicit configPath arg, XANO_CONFIG env var, ~/.xano/credentials.yaml
13
+ */
14
+ export function resolveCredentialsPath(configPath) {
15
+ const explicit = configPath || process.env.XANO_CONFIG;
16
+ if (explicit) {
17
+ return path.resolve(explicit);
18
+ }
19
+ return path.join(os.homedir(), '.xano', 'credentials.yaml');
20
+ }
10
21
  export default class BaseCommand extends Command {
11
22
  static baseFlags = {
23
+ config: Flags.string({
24
+ char: 'c',
25
+ description: 'Path to credentials file (default: ~/.xano/credentials.yaml)',
26
+ env: 'XANO_CONFIG',
27
+ required: false,
28
+ }),
12
29
  profile: Flags.string({
13
30
  char: 'p',
14
31
  description: 'Profile to use (uses default profile if not specified)',
@@ -88,11 +105,22 @@ export default class BaseCommand extends Command {
88
105
  return this.flags?.profile;
89
106
  }
90
107
  /**
91
- * Load and parse the credentials file. Returns null if the file doesn't exist.
108
+ * Get the resolved credentials file path, respecting --config flag and XANO_CONFIG env var.
109
+ * Reads -c/--config from process.argv directly because oclif doesn't set this.flags
110
+ * from parsed results — the static flags property is the flag definition, not parsed values.
92
111
  */
112
+ getCredentialsPath() {
113
+ const args = process.argv;
114
+ for (let i = 0; i < args.length; i++) {
115
+ if ((args[i] === '--config' || args[i] === '-c') && args[i + 1])
116
+ return resolveCredentialsPath(args[i + 1]);
117
+ if (args[i]?.startsWith('--config='))
118
+ return resolveCredentialsPath(args[i].slice('--config='.length));
119
+ }
120
+ return resolveCredentialsPath();
121
+ }
93
122
  loadCredentialsFile() {
94
- const configDir = path.join(os.homedir(), '.xano');
95
- const credentialsPath = path.join(configDir, 'credentials.yaml');
123
+ const credentialsPath = this.getCredentialsPath();
96
124
  if (!fs.existsSync(credentialsPath)) {
97
125
  return null;
98
126
  }
@@ -103,6 +131,67 @@ export default class BaseCommand extends Command {
103
131
  }
104
132
  return null;
105
133
  }
134
+ /**
135
+ * Get or create the singleton sandbox environment for the authenticated user.
136
+ * Returns the sandbox object (existing or newly created).
137
+ */
138
+ async getOrCreateSandbox(profile, verbose) {
139
+ const apiUrl = `${profile.instance_origin}/api:meta/sandbox/me`;
140
+ const response = await this.verboseFetch(apiUrl, {
141
+ headers: {
142
+ accept: 'application/json',
143
+ Authorization: `Bearer ${profile.access_token}`,
144
+ },
145
+ method: 'GET',
146
+ }, verbose, profile.access_token);
147
+ if (!response.ok) {
148
+ const message = await this.parseApiError(response, 'Failed to get sandbox environment');
149
+ this.error(message);
150
+ }
151
+ return (await response.json());
152
+ }
153
+ /**
154
+ * Resolve profile from flags, validating instance_origin and access_token exist.
155
+ */
156
+ resolveProfile(flags) {
157
+ const profileName = flags.profile || this.getDefaultProfile();
158
+ const credentials = this.loadCredentialsFile();
159
+ if (!credentials || !(profileName in credentials.profiles)) {
160
+ this.error(`Profile '${profileName}' not found.\n` + `Create a profile using 'xano profile create'`);
161
+ }
162
+ const profile = credentials.profiles[profileName];
163
+ if (!profile.instance_origin) {
164
+ this.error(`Profile '${profileName}' is missing instance_origin`);
165
+ }
166
+ if (!profile.access_token) {
167
+ this.error(`Profile '${profileName}' is missing access_token`);
168
+ }
169
+ return { profile, profileName };
170
+ }
171
+ /**
172
+ * Parse an API error response and return a clean error message.
173
+ * Extracts the message from JSON responses and adds context for common errors.
174
+ */
175
+ async parseApiError(response, fallbackPrefix) {
176
+ const errorText = await response.text();
177
+ let message = `${fallbackPrefix} (${response.status})`;
178
+ try {
179
+ const errorJson = JSON.parse(errorText);
180
+ if (errorJson.message) {
181
+ message = errorJson.message;
182
+ }
183
+ }
184
+ catch {
185
+ if (errorText) {
186
+ message += `\n${errorText}`;
187
+ }
188
+ }
189
+ // Provide guidance when sandbox access is denied (free plan restriction)
190
+ if (response.status === 500 && message === 'Access Denied.') {
191
+ message = 'Sandbox is not available on the Free plan. Upgrade your plan to use sandbox features.';
192
+ }
193
+ return message;
194
+ }
106
195
  /**
107
196
  * Make an HTTP request with optional verbose logging.
108
197
  * Use this for all Metadata API calls to support the --verbose flag.
@@ -3,6 +3,7 @@ export default class Auth extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static flags: {
6
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
6
7
  insecure: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
8
  origin: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
8
9
  };
@@ -4,10 +4,9 @@ import inquirer from 'inquirer';
4
4
  import * as yaml from 'js-yaml';
5
5
  import * as fs from 'node:fs';
6
6
  import * as http from 'node:http';
7
- import * as os from 'node:os';
8
- import { join } from 'node:path';
7
+ import { dirname } from 'node:path';
9
8
  import open from 'open';
10
- import { buildUserAgent } from '../../base-command.js';
9
+ import { buildUserAgent, resolveCredentialsPath } from '../../base-command.js';
11
10
  const AUTH_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
12
11
  export default class Auth extends Command {
13
12
  static description = 'Authenticate with Xano via browser login';
@@ -23,6 +22,12 @@ Profile 'default' created successfully!`,
23
22
  Opening browser for Xano login at https://custom.xano.com...`,
24
23
  ];
25
24
  static flags = {
25
+ config: Flags.string({
26
+ char: 'c',
27
+ description: 'Path to credentials file (default: ~/.xano/credentials.yaml)',
28
+ env: 'XANO_CONFIG',
29
+ required: false,
30
+ }),
26
31
  insecure: Flags.boolean({
27
32
  char: 'k',
28
33
  default: false,
@@ -101,7 +106,7 @@ Opening browser for Xano login at https://custom.xano.com...`,
101
106
  instance_origin: instance.origin,
102
107
  name: profileName,
103
108
  workspace: workspace?.id,
104
- });
109
+ }, flags.config);
105
110
  this.log('');
106
111
  this.log(`Profile '${profileName}' created successfully!`);
107
112
  // Ensure clean exit (the open() call can keep the event loop alive)
@@ -206,12 +211,12 @@ Opening browser for Xano login at https://custom.xano.com...`,
206
211
  ]);
207
212
  return profileName.trim() || 'default';
208
213
  }
209
- async saveProfile(profile) {
210
- const configDir = join(os.homedir(), '.xano');
211
- const credentialsPath = join(configDir, 'credentials.yaml');
212
- // Ensure the .xano directory exists
213
- if (!fs.existsSync(configDir)) {
214
- fs.mkdirSync(configDir, { recursive: true });
214
+ async saveProfile(profile, configPath) {
215
+ const credentialsPath = resolveCredentialsPath(configPath);
216
+ const credDir = dirname(credentialsPath);
217
+ // Ensure the directory exists
218
+ if (!fs.existsSync(credDir)) {
219
+ fs.mkdirSync(credDir, { recursive: true });
215
220
  }
216
221
  // Read existing credentials file or create new structure
217
222
  let credentials = { profiles: {} };
@@ -283,7 +288,7 @@ Opening browser for Xano login at https://custom.xano.com...`,
283
288
  choices: [
284
289
  { name: '(Skip workspace)', value: '' },
285
290
  ...workspaces.map((ws) => ({
286
- name: ws.name,
291
+ name: `${ws.name} (${ws.id})`,
287
292
  value: ws.id,
288
293
  })),
289
294
  ],
@@ -1,14 +1,17 @@
1
1
  import BaseCommand from '../../../base-command.js';
2
2
  export default class BranchCreate extends BaseCommand {
3
3
  static description: string;
4
+ static args: {
5
+ label: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
6
+ };
4
7
  static examples: string[];
5
8
  static flags: {
6
9
  color: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
10
  description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
- label: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
11
  output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
12
  source: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
13
  workspace: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
15
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
16
  verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
17
  };
@@ -1,22 +1,26 @@
1
- import { Flags } from '@oclif/core';
1
+ import { Args, Flags } from '@oclif/core';
2
2
  import * as yaml from 'js-yaml';
3
3
  import * as fs from 'node:fs';
4
- import * as os from 'node:os';
5
- import * as path from 'node:path';
6
4
  import BaseCommand from '../../../base-command.js';
7
5
  export default class BranchCreate extends BaseCommand {
8
6
  static description = 'Create a new branch by cloning from an existing branch';
7
+ static args = {
8
+ label: Args.string({
9
+ description: 'Label for the new branch',
10
+ required: true,
11
+ }),
12
+ };
9
13
  static examples = [
10
- `$ xano branch create --label dev
14
+ `$ xano branch create dev
11
15
  Created branch: dev
12
16
  Cloned from: v1
13
17
  `,
14
- `$ xano branch create -l feature-auth -s dev -d "Authentication feature"
18
+ `$ xano branch create feature-auth -s dev -d "Authentication feature"
15
19
  Created branch: feature-auth
16
20
  Cloned from: dev
17
21
  Description: Authentication feature
18
22
  `,
19
- `$ xano branch create --label staging --color "#ebc346" --output json
23
+ `$ xano branch create staging --color "#ebc346" --output json
20
24
  {
21
25
  "created_at": "2024-02-11T10:00:00Z",
22
26
  "label": "staging",
@@ -37,11 +41,6 @@ Created branch: feature-auth
37
41
  description: 'Description for the new branch',
38
42
  required: false,
39
43
  }),
40
- label: Flags.string({
41
- char: 'l',
42
- description: 'Label for the new branch',
43
- required: true,
44
- }),
45
44
  output: Flags.string({
46
45
  char: 'o',
47
46
  default: 'summary',
@@ -62,7 +61,7 @@ Created branch: feature-auth
62
61
  }),
63
62
  };
64
63
  async run() {
65
- const { flags } = await this.parse(BranchCreate);
64
+ const { args, flags } = await this.parse(BranchCreate);
66
65
  // Get profile name (default or from flag/env)
67
66
  const profileName = flags.profile || this.getDefaultProfile();
68
67
  // Load credentials
@@ -84,11 +83,15 @@ Created branch: feature-auth
84
83
  const workspaceId = flags.workspace || profile.workspace;
85
84
  if (!workspaceId) {
86
85
  this.error('No workspace ID provided. Either use --workspace flag or set one in your profile.\n' +
87
- 'Usage: xano branch create --label <label> [--workspace <workspace_id>]');
86
+ 'Usage: xano branch create <label> [--workspace <workspace_id>]');
87
+ }
88
+ // Validate reserved branch names
89
+ if (args.label.toLowerCase() === 'v1') {
90
+ this.error("Cannot create a branch named 'v1'. This is the default branch and always exists.");
88
91
  }
89
92
  // Build request body
90
93
  const body = {
91
- label: flags.label,
94
+ label: args.label,
92
95
  source_branch: flags.source,
93
96
  };
94
97
  if (flags.description) {
@@ -104,8 +107,8 @@ Created branch: feature-auth
104
107
  const response = await this.verboseFetch(apiUrl, {
105
108
  body: JSON.stringify(body),
106
109
  headers: {
107
- 'accept': 'application/json',
108
- 'Authorization': `Bearer ${profile.access_token}`,
110
+ accept: 'application/json',
111
+ Authorization: `Bearer ${profile.access_token}`,
109
112
  'Content-Type': 'application/json',
110
113
  },
111
114
  method: 'POST',
@@ -114,7 +117,7 @@ Created branch: feature-auth
114
117
  const errorText = await response.text();
115
118
  this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
116
119
  }
117
- const branch = await response.json();
120
+ const branch = (await response.json());
118
121
  // Output results
119
122
  if (flags.output === 'json') {
120
123
  this.log(JSON.stringify(branch, null, 2));
@@ -141,12 +144,10 @@ Created branch: feature-auth
141
144
  }
142
145
  }
143
146
  loadCredentials() {
144
- const configDir = path.join(os.homedir(), '.xano');
145
- const credentialsPath = path.join(configDir, 'credentials.yaml');
147
+ const credentialsPath = this.getCredentialsPath();
146
148
  // Check if credentials file exists
147
149
  if (!fs.existsSync(credentialsPath)) {
148
- this.error(`Credentials file not found at ${credentialsPath}\n` +
149
- `Create a profile using 'xano profile create'`);
150
+ this.error(`Credentials file not found at ${credentialsPath}\n` + `Create a profile using 'xano profile create'`);
150
151
  }
151
152
  // Read credentials file
152
153
  try {