@xano/sdk 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 (336) hide show
  1. package/CHANGELOG.md +788 -0
  2. package/LICENSE +21 -0
  3. package/README.md +541 -0
  4. package/dist/.build-fingerprint +1 -0
  5. package/dist/agent-file-refresh-ZEMK33AX.js +38 -0
  6. package/dist/answer-shape-K67TAAPY.js +33 -0
  7. package/dist/backend-dir-2U53CIAL.js +39 -0
  8. package/dist/backend-slot-7QXW2QYC.js +28 -0
  9. package/dist/bin.d.ts +1 -0
  10. package/dist/bin.js +255 -0
  11. package/dist/branch-NCRNWQBQ.js +61 -0
  12. package/dist/branch-commands-ACIRCEQJ.js +79 -0
  13. package/dist/bundle.d.ts +223 -0
  14. package/dist/bundle.js +153 -0
  15. package/dist/capture-WAXPD7DR.js +41 -0
  16. package/dist/chunk-27GWNP2I.js +23 -0
  17. package/dist/chunk-2CMJZDCL.js +177 -0
  18. package/dist/chunk-2EZ3ZJZZ.js +302 -0
  19. package/dist/chunk-2GNFDZA2.js +390 -0
  20. package/dist/chunk-2MNLRIOX.js +11 -0
  21. package/dist/chunk-2TFRR6JV.js +104 -0
  22. package/dist/chunk-2ZUEE4EK.js +408 -0
  23. package/dist/chunk-36JUQZVQ.js +22 -0
  24. package/dist/chunk-3DRXWLJ5.js +83 -0
  25. package/dist/chunk-3JDU4VRL.js +137 -0
  26. package/dist/chunk-3KZCLN4Z.js +241 -0
  27. package/dist/chunk-3LDULPRE.js +168 -0
  28. package/dist/chunk-3M4LFYNQ.js +111 -0
  29. package/dist/chunk-3XI5CTUE.js +22 -0
  30. package/dist/chunk-3YW23UFH.js +50 -0
  31. package/dist/chunk-45UIT5T2.js +862 -0
  32. package/dist/chunk-4BW2SG2N.js +46 -0
  33. package/dist/chunk-4KHEMC32.js +79 -0
  34. package/dist/chunk-4POWHT2K.js +321 -0
  35. package/dist/chunk-4Q2GN7NW.js +192 -0
  36. package/dist/chunk-5A2PLKZB.js +70 -0
  37. package/dist/chunk-5FBYXK5P.js +159 -0
  38. package/dist/chunk-5HVXPTU4.js +323 -0
  39. package/dist/chunk-5QU2P5CA.js +304 -0
  40. package/dist/chunk-5V5XU32D.js +191 -0
  41. package/dist/chunk-5ZSO4T2G.js +10 -0
  42. package/dist/chunk-622HUCZT.js +10 -0
  43. package/dist/chunk-65HWOMGV.js +177 -0
  44. package/dist/chunk-6ELFRN3P.js +365 -0
  45. package/dist/chunk-6HO3NCSJ.js +1055 -0
  46. package/dist/chunk-6IUMKYWI.js +457 -0
  47. package/dist/chunk-6KEB3C3U.js +65 -0
  48. package/dist/chunk-6MR5TI5V.js +987 -0
  49. package/dist/chunk-6NNTNXN7.js +157 -0
  50. package/dist/chunk-6PBR37LN.js +97 -0
  51. package/dist/chunk-6QXLMMT3.js +155 -0
  52. package/dist/chunk-6ZGJCBZS.js +224 -0
  53. package/dist/chunk-73IVBKMA.js +250 -0
  54. package/dist/chunk-7G3CTU7I.js +286 -0
  55. package/dist/chunk-A3UDOZB6.js +222 -0
  56. package/dist/chunk-A6E77GDW.js +85 -0
  57. package/dist/chunk-AHJWCYQO.js +660 -0
  58. package/dist/chunk-AKNCFVKZ.js +36 -0
  59. package/dist/chunk-AM4FKL4J.js +237 -0
  60. package/dist/chunk-AN6BL47T.js +26 -0
  61. package/dist/chunk-AOSYVMUS.js +147 -0
  62. package/dist/chunk-APWXY5SL.js +182 -0
  63. package/dist/chunk-AWEIIR2U.js +218 -0
  64. package/dist/chunk-AWQ7BQTQ.js +73 -0
  65. package/dist/chunk-BAUZOWGX.js +393 -0
  66. package/dist/chunk-BHACRS6R.js +940 -0
  67. package/dist/chunk-BWPNT55T.js +680 -0
  68. package/dist/chunk-C6RIMRU4.js +187 -0
  69. package/dist/chunk-CGVQZXYG.js +263 -0
  70. package/dist/chunk-CIMQZRRE.js +162 -0
  71. package/dist/chunk-CJHQKJU4.js +113 -0
  72. package/dist/chunk-CTMO46PK.js +1897 -0
  73. package/dist/chunk-D53J55C5.js +106 -0
  74. package/dist/chunk-D7VGG3WS.js +90 -0
  75. package/dist/chunk-DAD3ONIS.js +245 -0
  76. package/dist/chunk-DGWHAVRG.js +162 -0
  77. package/dist/chunk-DX4QR4B2.js +483 -0
  78. package/dist/chunk-EHP3WPEG.js +21 -0
  79. package/dist/chunk-ELP2HMXC.js +432 -0
  80. package/dist/chunk-ERVIASGL.js +21 -0
  81. package/dist/chunk-ET4Y4XCB.js +865 -0
  82. package/dist/chunk-ETO6LGLD.js +4750 -0
  83. package/dist/chunk-EUUUMY7J.js +24 -0
  84. package/dist/chunk-FQJ2TTHP.js +218 -0
  85. package/dist/chunk-FQOF7E2E.js +133 -0
  86. package/dist/chunk-FWD7AL6N.js +21 -0
  87. package/dist/chunk-FXFV3KU6.js +138 -0
  88. package/dist/chunk-FXRQOWTE.js +171 -0
  89. package/dist/chunk-G554NZNI.js +210 -0
  90. package/dist/chunk-GSGDWJB5.js +13 -0
  91. package/dist/chunk-H2V3N23D.js +66 -0
  92. package/dist/chunk-H5C7MBCR.js +159 -0
  93. package/dist/chunk-HHN734BU.js +371 -0
  94. package/dist/chunk-HRVBLCVJ.js +243 -0
  95. package/dist/chunk-HVRUQXGD.js +1264 -0
  96. package/dist/chunk-I4F5TCJH.js +130 -0
  97. package/dist/chunk-IDOTCMDV.js +57 -0
  98. package/dist/chunk-IGOLHKWF.js +427 -0
  99. package/dist/chunk-JGE7CAWB.js +148 -0
  100. package/dist/chunk-JTUKLCXS.js +92 -0
  101. package/dist/chunk-JZGVVJSI.js +66 -0
  102. package/dist/chunk-K4WX6UUU.js +923 -0
  103. package/dist/chunk-K5BBGTYZ.js +217 -0
  104. package/dist/chunk-K6RXBGUB.js +427 -0
  105. package/dist/chunk-KTSDSD36.js +165 -0
  106. package/dist/chunk-L5JYN3NM.js +1038 -0
  107. package/dist/chunk-LIEOKXKD.js +354 -0
  108. package/dist/chunk-LLNZGEFK.js +51 -0
  109. package/dist/chunk-LPECSJ2J.js +55 -0
  110. package/dist/chunk-LQCMKAJ7.js +413 -0
  111. package/dist/chunk-LRN4ZJCQ.js +1145 -0
  112. package/dist/chunk-MJ4ZVHKN.js +17 -0
  113. package/dist/chunk-MXHSQOC4.js +3061 -0
  114. package/dist/chunk-N5GKCWE5.js +367 -0
  115. package/dist/chunk-NA3PXMEF.js +10 -0
  116. package/dist/chunk-NAQDMEHV.js +76 -0
  117. package/dist/chunk-NWKXM5RD.js +109 -0
  118. package/dist/chunk-NZOWWHU5.js +196 -0
  119. package/dist/chunk-OHX6MIUZ.js +184 -0
  120. package/dist/chunk-OOXWYAA4.js +15 -0
  121. package/dist/chunk-OPHB5ORL.js +1990 -0
  122. package/dist/chunk-OXA6PSZT.js +1725 -0
  123. package/dist/chunk-P2HAGF6H.js +73 -0
  124. package/dist/chunk-PTHVMXZI.js +3036 -0
  125. package/dist/chunk-PZ5AY32C.js +10 -0
  126. package/dist/chunk-Q325JKYB.js +189 -0
  127. package/dist/chunk-Q7DGTQ5G.js +255 -0
  128. package/dist/chunk-QCNYHBL7.js +601 -0
  129. package/dist/chunk-QO6BVGS5.js +145 -0
  130. package/dist/chunk-QXTKSOFI.js +76 -0
  131. package/dist/chunk-ROY4YAEQ.js +66 -0
  132. package/dist/chunk-RTJRZFFY.js +447 -0
  133. package/dist/chunk-SAVJUS2X.js +128 -0
  134. package/dist/chunk-SFLHVCKX.js +173 -0
  135. package/dist/chunk-STSFNCY4.js +127 -0
  136. package/dist/chunk-SWBF2LTK.js +170 -0
  137. package/dist/chunk-T5KELPAR.js +109 -0
  138. package/dist/chunk-TFFGOFVZ.js +1481 -0
  139. package/dist/chunk-TLHIFK4M.js +256 -0
  140. package/dist/chunk-TLQE7ZN3.js +156 -0
  141. package/dist/chunk-TPHWJ7UG.js +1981 -0
  142. package/dist/chunk-TRJCALW3.js +291 -0
  143. package/dist/chunk-TTEIYD5P.js +133 -0
  144. package/dist/chunk-U64O2MXY.js +407 -0
  145. package/dist/chunk-UCQ2VUTG.js +630 -0
  146. package/dist/chunk-UTI5G6OJ.js +44 -0
  147. package/dist/chunk-UVHDMMIP.js +188 -0
  148. package/dist/chunk-UZ6QQWJT.js +120 -0
  149. package/dist/chunk-VAX3UZOY.js +686 -0
  150. package/dist/chunk-W3HBUKFO.js +57 -0
  151. package/dist/chunk-WKQLGKCL.js +2823 -0
  152. package/dist/chunk-WQFNASK4.js +560 -0
  153. package/dist/chunk-WRBKLYSJ.js +224 -0
  154. package/dist/chunk-WTPZRR4I.js +17 -0
  155. package/dist/chunk-WZDJ2LLX.js +2385 -0
  156. package/dist/chunk-X2NL4KUO.js +14 -0
  157. package/dist/chunk-X4DVXBFY.js +38 -0
  158. package/dist/chunk-X677DMZM.js +7050 -0
  159. package/dist/chunk-XBKMWY7F.js +612 -0
  160. package/dist/chunk-XNCP4KYO.js +78 -0
  161. package/dist/chunk-XVBGLISP.js +35 -0
  162. package/dist/chunk-XW6KISBB.js +1192 -0
  163. package/dist/chunk-YGZHHAAB.js +8156 -0
  164. package/dist/chunk-YHEFQUQB.js +186 -0
  165. package/dist/chunk-YLDALTWZ.js +653 -0
  166. package/dist/chunk-YU4HSQW7.js +229 -0
  167. package/dist/chunk-ZDPP6RKB.js +27 -0
  168. package/dist/chunk-ZFDZKNGG.js +194 -0
  169. package/dist/chunk-ZIY7RNUT.js +49 -0
  170. package/dist/chunk-ZYJP6BOO.js +30 -0
  171. package/dist/cli.d.ts +1111 -0
  172. package/dist/cli.js +165 -0
  173. package/dist/codegen-command-VR2GWR6C.js +173 -0
  174. package/dist/codegen.d.ts +115 -0
  175. package/dist/codegen.js +19 -0
  176. package/dist/commands-GJMJ4XYZ.js +76 -0
  177. package/dist/completion-ZQLO6PB6.js +738 -0
  178. package/dist/config-MBYGCAFK.js +136 -0
  179. package/dist/deploy-command-JVZPWHMK.js +180 -0
  180. package/dist/deploy-source-3W45CXMP.js +81 -0
  181. package/dist/dev-env-sync-HN44K4D5.js +188 -0
  182. package/dist/diagnostics-UEAAHWVF.js +30 -0
  183. package/dist/display-name-hint-ABBPVQIR.js +46 -0
  184. package/dist/emit-CyhBDxli.d.ts +7007 -0
  185. package/dist/env-example-refresh-ET5HMMDW.js +126 -0
  186. package/dist/env-pull-command-TNMNB3CV.js +323 -0
  187. package/dist/env-target-O4HRKTGZ.js +40 -0
  188. package/dist/env-var-command-ZIRDCAZA.js +427 -0
  189. package/dist/ephemeral-P7F3PMAD.js +54 -0
  190. package/dist/ephemeral-command-CLJJKOFT.js +592 -0
  191. package/dist/ephemeral-state-PYCBSOGX.js +61 -0
  192. package/dist/file-lock-PT74GWYW.js +10 -0
  193. package/dist/generate-command-AYEOLA26.js +510 -0
  194. package/dist/gitignore-3SJ3WNRG.js +51 -0
  195. package/dist/guards-4EBNCZDC.js +162 -0
  196. package/dist/hosted-file-PSHJB3HX.js +28 -0
  197. package/dist/impersonate-command-5JAS3WSV.js +152 -0
  198. package/dist/import-EQSFU6JC.js +19 -0
  199. package/dist/index.d.ts +5470 -0
  200. package/dist/index.js +371 -0
  201. package/dist/init-command-X2FR4UAK.js +125 -0
  202. package/dist/init-modules-RY7UHAOL.js +50 -0
  203. package/dist/init-web-M4YZSZK6.js +42 -0
  204. package/dist/internal.d.ts +179 -0
  205. package/dist/internal.js +2634 -0
  206. package/dist/io-RC56QSN5.js +26 -0
  207. package/dist/keep-data-merge-7MPLVAVI.js +1119 -0
  208. package/dist/lambda-config-reconcile-OUZ4OYAB.js +116 -0
  209. package/dist/landed-753YLJ5H.js +40 -0
  210. package/dist/landing-record-SU52SKXU.js +61 -0
  211. package/dist/live-diff-YONW2VI3.js +65 -0
  212. package/dist/local-engine-choice-CH5X3W7E.js +79 -0
  213. package/dist/local-engine-command-Z3OL7VOD.js +766 -0
  214. package/dist/local-engine-config-5U3SC434.js +39 -0
  215. package/dist/local-engine-deploy-PCQ4SNPB.js +39 -0
  216. package/dist/local-engine-process-EHTE5LLU.js +63 -0
  217. package/dist/local-engine-state-A4FTRFAF.js +32 -0
  218. package/dist/local-engine-update-check-6L645VBL.js +162 -0
  219. package/dist/lock-MRRHW5GS.js +87 -0
  220. package/dist/lock-commands-2QG25HL7.js +963 -0
  221. package/dist/login-command-QPYRWOXZ.js +538 -0
  222. package/dist/logout-command-WOIQLDP6.js +56 -0
  223. package/dist/loop-ALIZLOSX.js +191 -0
  224. package/dist/marketplace-command-K5YL42NO.js +977 -0
  225. package/dist/marketplace-resolve-LY32JES5.js +40 -0
  226. package/dist/meta-client-YXGPW53C.js +244 -0
  227. package/dist/module-manifest-JFGHU2TC.js +38 -0
  228. package/dist/near-key.d.ts +13 -0
  229. package/dist/near-key.js +8 -0
  230. package/dist/node.d.ts +95 -0
  231. package/dist/node.js +969 -0
  232. package/dist/operation-outcome-TZSESNC7.js +51 -0
  233. package/dist/output-target-AL3I44GG.js +47 -0
  234. package/dist/package-manager-PRAHUSCN.js +34 -0
  235. package/dist/plan-disclosure-6G4MLZKD.js +114 -0
  236. package/dist/plugin-contributions-WRNY5ABG.js +28 -0
  237. package/dist/plugin-prompt-4VA3B4FF.js +20 -0
  238. package/dist/plugin-questions-JTSYA74Q.js +24 -0
  239. package/dist/plugin.d.ts +396 -0
  240. package/dist/plugin.js +1 -0
  241. package/dist/preflight-command-2T5Z36MI.js +321 -0
  242. package/dist/profile-command-AH3PIM4X.js +800 -0
  243. package/dist/profile-pointer-GRWJU3YD.js +31 -0
  244. package/dist/profile-select-74TRDEPD.js +23 -0
  245. package/dist/project-dependency-KSH63ODT.js +35 -0
  246. package/dist/project-sdk-REPPOIBD.js +24 -0
  247. package/dist/promote-command-PCWT5N6S.js +140 -0
  248. package/dist/prompt-II3F3LND.js +27 -0
  249. package/dist/publish-command-TTDUUJI5.js +414 -0
  250. package/dist/pull-command-VBO7C4S3.js +161 -0
  251. package/dist/pull-superseded-GJWLG7XO.js +20 -0
  252. package/dist/release-command-N4ZPKMHR.js +146 -0
  253. package/dist/release-ns-command-3KFDMZ6G.js +1745 -0
  254. package/dist/response-HlXahv4K.d.ts +925 -0
  255. package/dist/retry-command-AS5EVREX.js +34 -0
  256. package/dist/routes-manifest-SNWUVBT6.js +43 -0
  257. package/dist/runtime-XETHVF33.js +14 -0
  258. package/dist/scaffold.d.ts +1911 -0
  259. package/dist/scaffold.js +107 -0
  260. package/dist/secrets-file-LVZD6IIM.js +56 -0
  261. package/dist/secrets-fill-command-TTCPSVXN.js +218 -0
  262. package/dist/seed-VU7C3QYS.js +46 -0
  263. package/dist/sent-writes-N7X4OVXT.js +22 -0
  264. package/dist/source-resolve-S2XA3XZF.js +83 -0
  265. package/dist/static-host-B2Y6AG3W.js +352 -0
  266. package/dist/status-command-RUDXTF7I.js +554 -0
  267. package/dist/store-6VNCD4D7.js +20 -0
  268. package/dist/store-ARM6VMO5.js +98 -0
  269. package/dist/store-DuYfzKlW.d.ts +3144 -0
  270. package/dist/suggest-6FOOYKHG.js +12 -0
  271. package/dist/sveltekit.d.ts +154 -0
  272. package/dist/sveltekit.js +109 -0
  273. package/dist/sync-contributions-UCEROO26.js +415 -0
  274. package/dist/table-ENAVMITR.js +42 -0
  275. package/dist/tables-command-GUPXXSB5.js +156 -0
  276. package/dist/tenant-IIHPOHGP.js +42 -0
  277. package/dist/tenant-command-D5HGI2X4.js +965 -0
  278. package/dist/test-command-TIKZMDPZ.js +476 -0
  279. package/dist/tests-BGGIOK3L.js +26 -0
  280. package/dist/token-NICJUTMT.js +72 -0
  281. package/dist/toolchain-hooks-GHKLUJOH.js +96 -0
  282. package/dist/toolchain-modules-5EAZUONB.js +24 -0
  283. package/dist/tracked-backend-EIBUMDZA.js +80 -0
  284. package/dist/update-check-2W2NLDTC.js +136 -0
  285. package/dist/upgrade-command-QHNFWM7O.js +531 -0
  286. package/dist/verify-rollout-DRCK5TDD.js +61 -0
  287. package/dist/whoami-command-STGINTXH.js +51 -0
  288. package/dist/workspace-2CLHU2LK.js +93 -0
  289. package/dist/workspace-binding-56PIIPCP.js +54 -0
  290. package/dist/workspace-command-JEXQETJD.js +535 -0
  291. package/dist/workspace-env-F2RFMSC5.js +70 -0
  292. package/dist/workspace-export-OJSH2HIW.js +21 -0
  293. package/dist/writable-VA7OISSK.js +25 -0
  294. package/dist/xano-ICFUSPFR.js +42 -0
  295. package/dist/xanosdk-import-GN42TO2B.js +56 -0
  296. package/dist/xanosdk-project-S6DXJONQ.js +41 -0
  297. package/dist/xdo-CxDr2Q1C.d.ts +1371 -0
  298. package/guides/README.md +33 -0
  299. package/guides/authoring.md +921 -0
  300. package/guides/cli.md +401 -0
  301. package/guides/codegen.md +177 -0
  302. package/guides/codes.md +404 -0
  303. package/guides/coverage.md +74 -0
  304. package/guides/deploying.md +1525 -0
  305. package/guides/environment.md +400 -0
  306. package/guides/marketplace.md +415 -0
  307. package/guides/module-contract.md +558 -0
  308. package/guides/object-kinds.md +433 -0
  309. package/guides/project-structure.md +43 -0
  310. package/guides/scaffold.md +351 -0
  311. package/guides/typed-frontend.md +242 -0
  312. package/llms/client.md +63 -0
  313. package/llms/errors.md +56 -0
  314. package/llms/fields.md +176 -0
  315. package/llms/filters.md +183 -0
  316. package/llms/kinds-agent-mcp.md +30 -0
  317. package/llms/kinds-core.md +100 -0
  318. package/llms/kinds-knowledge.md +20 -0
  319. package/llms/kinds-realtime.md +76 -0
  320. package/llms/lambda.md +94 -0
  321. package/llms/legacy.md +44 -0
  322. package/llms/lock.md +40 -0
  323. package/llms/object-kinds.md +34 -0
  324. package/llms/statements-calls.md +62 -0
  325. package/llms/statements-catalog.md +320 -0
  326. package/llms/statements-data.md +66 -0
  327. package/llms/statements-runtime.md +68 -0
  328. package/llms/tests.md +59 -0
  329. package/llms/toolchain.md +16 -0
  330. package/llms/triggers.md +36 -0
  331. package/llms/values.md +29 -0
  332. package/llms-full.txt +2085 -0
  333. package/llms.txt +493 -0
  334. package/manifest.json +11224 -0
  335. package/package.json +162 -0
  336. package/types/lambda-globals.d.ts +129 -0
package/guides/cli.md ADDED
@@ -0,0 +1,401 @@
1
+ # CLI
2
+
3
+ Every `xanosdk` command, shell completion, and what the CLI prints when something fails.
4
+
5
+ The examples spell the bare `xanosdk` of a global install (`npm i -g @xano/sdk`). In a project
6
+ with `@xano/sdk` installed, run them as `npx xanosdk …`; anywhere else, `npx @xano/sdk …` (see
7
+ [Commands the CLI prints](#commands-the-cli-prints)).
8
+
9
+ ```bash
10
+ xanosdk init my-app # scaffold a full project (frontend/ + xano/)
11
+ xanosdk init my-app --framework svelte # SvelteKit instead of the default React
12
+ xanosdk init my-app --no-agents-md --no-install # skip AGENTS.md and npm install
13
+ xanosdk init my-app --theme zinc-blue --dark toggle --icons tabler # the look
14
+ xanosdk init my-app --marketplace @xano-sdk/auth,@xano-sdk/vector # install add-ons AND register them
15
+
16
+ xanosdk marketplace list # every published add-on (no login needed)
17
+ xanosdk marketplace search auth # narrow by keyword
18
+ xanosdk marketplace details @xano-sdk/auth # what it installs + the registration to copy
19
+ xanosdk marketplace details @xano-sdk/auth --prompt # …as a prompt for a coding agent
20
+ xanosdk marketplace install @xano-sdk/auth # add an add-on to the project you're in (and configure it)
21
+ xanosdk marketplace reinstall <toolchain-package> # re-ask a toolchain module its questions
22
+ xanosdk marketplace remove <toolchain-package> # uninstall it and drop what it contributed
23
+
24
+ xanosdk export ./xano/index.ts # bundle to stdout
25
+ xanosdk export ./xano/index.ts --out ws.json
26
+ xanosdk compile ./xano/functions/get-user.ts # a single function's JSON
27
+ xanosdk routes ./xano/index.ts # each query's verb + resolved api:<canonical>/<name> (`paths` is an alias)
28
+
29
+ xanosdk export ./xano/index.ts --strict # CI: fail the build on any warning, don't just print it
30
+ xanosdk export ./xano/index.ts --no-lock # build with no xano.lock (one is written by default; refused once one exists)
31
+ xanosdk export ./xano/index.ts --frozen-lock # CI guard: fail if the export would change the lock, or if the lock carries an entry no object matches
32
+ xanosdk export ./xano/index.ts --check --strict # the same guard, writing nothing (no bundle, no lock) — needs no .env or .secrets.json; the scaffold's xano:check
33
+ xanosdk lock rename --entry=xano/index.ts table users members # move a lock entry after renaming in code
34
+ xanosdk lock prune ./xano/index.ts --yes # drop lock entries nothing exports anymore
35
+ xanosdk lock prune --identity-only --entry=xano/index.ts --yes dbo:notes # …or drop named keys without running the workspace
36
+ xanosdk lock import live-export.json --entry=xano/index.ts --yes # seed the lock from a live engine export
37
+
38
+ xanosdk login # OAuth sign-in (once) — pick the instance + workspace at consent
39
+ xanosdk login --paste # …when the browser can't reach this machine's 127.0.0.1 (remote shell, container, Codespace)
40
+ xanosdk login --scope "<list>" # request these OAuth scopes instead of the built-in set
41
+ xanosdk login --profile staging # sign in as a second named credential, leaving the others alone
42
+ xanosdk <command> --profile staging # act as that profile for one run (-p is the short form)
43
+ xanosdk workspace details # which instance/workspace am I bound to, and via which credential?
44
+ xanosdk deploy ./xano/index.ts # compile + import into a live ephemeral (the dev loop) → URL
45
+ xanosdk deploy ./xano/index.ts --static ./frontend/dist # also deploy a static frontend (onto the ephemeral)
46
+ xanosdk deploy ./xano/index.ts --static ./frontend/dist --static-env PK=pk_live_1 # + extra public config
47
+ xanosdk deploy --bundle ws.json # deploy an already-exported bundle
48
+ xanosdk deploy ./xano/index.ts --open # …and open the deployed URL in your browser
49
+ xanosdk deploy ./xano/index.ts --expires-hours 8 # the ephemeral's TTL when this deploy creates it: 1–24 hours (default: 1)
50
+ xanosdk deploy ./xano/index.ts --keep-data --static ./frontend/dist --static-host docs # publish to a named static host — merge only: a replace deletes it, and a deploy creates only `default` (create `docs` in the dashboard first)
51
+ xanosdk deploy ./xano/index.ts --static ./frontend/dist --static-routing spa # override URL resolution, spa|multipage (inferred from the bundle; rarely needed)
52
+ xanosdk deploy ./xano/index.ts --keep-data # redeploy but keep table rows (merge instead of replace; seeds aren't re-written)
53
+ xanosdk env set STRIPE_KEY --to workspace --yes # set ONE env var on a running backend, value piped on stdin (no deploy; never printed)
54
+ xanosdk env unset STRIPE_KEY --to local-engine # clear one (--to workspace|ephemeral[:<name>]|local-engine[:<name>]|tenant:<name>, default: last deployed)
55
+ xanosdk publish ./frontend/dist # publish a built frontend alone — no compile, no backend import (default: last deployed)
56
+ xanosdk publish ./frontend/dist --to ephemeral # …after a local-engine deploy: publish does not reach a local engine
57
+ xanosdk deploy ./xano/index.ts --local-engine --static ./frontend/dist # the engine serves the build at http://<prefix>.localhost:<port>
58
+ xanosdk publish ./frontend/dist --to workspace --branch v2 --yes # …onto the workspace, refused unless branch v2 is live
59
+ xanosdk deploy ./xano/index.ts --local-engine # deploy to a local engine on this machine instead of an ephemeral (runs the version pinned in package.json)
60
+ xanosdk deploy ./xano/index.ts --local-engine=~/Downloads/engine.tar.gz # override: run this archive, URL or version (v0.1.8), never touching the pin
61
+ XANOSDK_LOCAL_ENGINE_OVERRIDE=v0.1.8 npm run xano:deploy:local-engine # the same override for every deploy in the shell
62
+ xanosdk local-engine list # local engines on this machine, marking the ones xanosdk started
63
+ xanosdk local-engine token # print its meta API bearer: XANO_META_TOKEN=$(xanosdk local-engine token)
64
+ xanosdk local-engine stop <name> # stop one (--all: every one xanosdk started, and what a crashed one left running)
65
+ xanosdk local-engine update # move this project's pinned engine to the latest (--version <v>: that one) and restart it — commit package.json
66
+ xanosdk local-engine cache list # each cached engine version, its size, and the engines running on it
67
+ xanosdk local-engine cache clear # remove every cached engine and its unpacked runtime (--version <v>: just that one); running ones are stopped first
68
+ xanosdk ephemeral list # list your ephemeral environments (--all-workspaces spans every workspace; --json: { ephemerals: [...] })
69
+ xanosdk ephemeral get <name> # base URL, state, and expiry for one (<name> = the tenant name, e.g. ewap-8wz9-9e13, NOT the display name — `ephemeral list` shows it in bold)
70
+ xanosdk ephemeral delete <name> --yes # destroy one
71
+ xanosdk tables # the last-deployed backend's tables: id, guid, name (for release create --seed)
72
+ xanosdk tables tenant:acme # …or name one
73
+ xanosdk test run-all # run the tests the last-deployed backend carries (exits 5 on a failure)
74
+ xanosdk test run-all --on local-engine # …or name one (--on ephemeral:<name>|tenant:<name>|workspace)
75
+ xanosdk test list # list the tests a backend carries, without running any
76
+ xanosdk test run-all --kind unit # only one family (unit|workflow; default: both) — also on test list
77
+ xanosdk test run-all --concurrency 4 # run 4 at once (default: 1); tests share the backend's database, so only when yours don't share state
78
+ xanosdk impersonate # open what this project last deployed to in the builder (--guest = read-only; --url-only prints the URL; the URL is a session — treat it as a credential)
79
+ xanosdk impersonate ephemeral:<tenant> # …or name one: local-engine[:<name>] or tenant:<name> (open your workspace from the Xano dashboard)
80
+ xanosdk deploy ./xano/index.ts --test # THE RELEASE FLOW, step 1: stand it up and run its tests
81
+ xanosdk release create v1 # …2: cut a release from the ephemeral this project last deployed to (after a local-engine deploy: --from ephemeral)
82
+ xanosdk release create v1 --from ephemeral:e4f2-9ab1 --description "what shipped" # …from a named source
83
+ xanosdk release create v1 --from workspace --branch staging # …or from a branch of your workspace (default: what it serves)
84
+ xanosdk release list # every release in this workspace, newest first, a description cut to one line (--json: { releases: [...] }, each with the origin show names)
85
+ xanosdk release show v1 # one release: when it was cut, from what (a cut from an environment records it in the description), what it carries
86
+ # --json origin: {kind:"branch",label} | {kind:"environment",name,type} | {kind:"copy",source:{host,workspaceId,release},from?:<branch or environment>} (transferred in) | null
87
+ xanosdk release export v1 # download it as a XanoScript multidoc (workspace block named "v1")
88
+ xanosdk release delete v1 --yes # remove one (a tenant may be running it — there is no undo)
89
+ xanosdk release transfer v1 --to-profile prod # copy a release to another workspace/instance, checked by content hash
90
+ xanosdk promote v1 # …3: land it in your workspace, as a new branch
91
+ xanosdk promote v1 --branch staging # …on a named branch instead of one named after the release (a taken label exits 2, SDK_BRANCH_TAKEN)
92
+ # (a label is letters, digits, _ . - and starts with a letter, digit or _; surrounding spaces are trimmed)
93
+ xanosdk tenant list # every tenant under this workspace (--json: { tenants: [...] })
94
+ xanosdk tenant get acme # base URL and state for one
95
+ xanosdk tenant deploy acme v1 # …or 3: land it on a customer tenant instead
96
+ xanosdk tenant delete acme --yes # destroy one (by its NAME — a display name fails with exit 8 and names the tenant it belongs to)
97
+ xanosdk pull release:v1 # refresh xano/ from a release: rewrites decoded files, keeps and names files you added, lists and confirms (also workspace, ephemeral[:<name>], local-engine, tenant:<name>)
98
+ xanosdk pull release:v1 --yes # …skip the confirmation and the dirty-tree refusal (CI)
99
+ xanosdk pull workspace --backend-dir ./backend # …when this project keeps its backend somewhere other than xano/ and nothing on disk says so (also on env pull)
100
+ xanosdk generate release:v1 --out ./backend # write ONLY the decoded xano/ tree, no project around it (any source above, or a bundle path; --force replaces the files an earlier generate wrote, asks first when that loses your edits (--yes answers), and keeps and lists any others)
101
+ xanosdk deploy ./xano/index.ts --to workspace --dry-run # ALTERNATIVE to the release flow: preview a merge of the local build
102
+ xanosdk deploy ./xano/index.ts --to workspace # …send it: add + update, never delete, never touch table data. Leaves no release to re-land
103
+ xanosdk deploy ./xano/index.ts --to workspace --prune # …also delete objects this project LANDED here and no longer defines (per xano.lock's `landed` record; commit it)
104
+ xanosdk deploy ./xano/index.ts --to workspace --branch staging # …land on a NEW branch instead of live (created; an existing label is refused, exit 2; stages LOGIC only — schema and workspace settings are shared, so not applied from a branch)
105
+ xanosdk deploy ./xano/index.ts --to workspace --branch staging --set-live # …and set it live the moment the import succeeds
106
+ xanosdk deploy ./xano/index.ts --to tenant:acme # …or merge into a customer tenant
107
+ xanosdk ephemeral export <name> # export a DEPLOYED ephemeral as a JSON bundle → .xano/exports/<name>.json
108
+ xanosdk ephemeral export <name> --format multidoc --name backend # …or as XanoScript → .xano/exports/backend.xs (--path picks elsewhere; a secret-bearing file git would commit is added to .gitignore)
109
+ xanosdk ephemeral export <name> --format multidoc --path - # …stream the multidoc to stdout (deploy first)
110
+
111
+ xanosdk workspace details # which workspace your token is scoped to (instance, id, name)
112
+ xanosdk workspace export --path ws.json # your REAL workspace as a JSON bundle (`--path -` streams to stdout)
113
+ xanosdk workspace diff ./xano/index.ts --branch staging # did my release land? which objects on the branch differ from your compile, are missing, or are unexpected (default: live); exits 2 when anything declared differs or is missing
114
+ xanosdk workspace branch list # its branches, and which one is live
115
+ xanosdk workspace branch set-live staging # promote a branch (also the way back after --backup-branch)
116
+ xanosdk workspace branch delete staging --yes # remove a non-live branch (a label padded with spaces is found by the trimmed one; one not there exits 0, alreadyGone; the live branch exits 2, SDK_BRANCH_LIVE)
117
+ xanosdk init my-app --from workspace # …or as a runnable project (the pull direction)
118
+ xanosdk init my-app --from ephemeral:<tenant> # same, from an ephemeral (the env is named inside the flag)
119
+ xanosdk init my-app --from ./ws.json # …or from a bundle already on disk (offline, no auth)
120
+
121
+ xanosdk status # who am I, which workspace, and the env this project last deployed to
122
+ xanosdk whoami # print the scoped user + instance base URL (pretty on a TTY, JSON when piped)
123
+ xanosdk profile list # every stored credential profile, with the default and the active one marked
124
+ xanosdk profile show [name] # what one profile addresses — never its token
125
+ xanosdk profile use <name> # pin THIS PROJECT to a profile (writes a committed xano.profile.json)
126
+ xanosdk profile set-default <name> # set the profile THIS MACHINE falls back to
127
+ xanosdk profile add <name> --instance <url> --workspace-id <n> # store a meta API token (prompts, or reads it piped on stdin; checked before storing)
128
+ xanosdk profile delete <name> # revoke that session and remove the profile (xano.profile.json is kept, as logout keeps it)
129
+ xanosdk logout # revoke the active profile and remove it (--all for every profile)
130
+ # Both ask first (--yes off a terminal) when the profile is the one xano.profile.json pins, or the default while others stay.
131
+ xanosdk version # print the installed @xano/sdk version
132
+ xanosdk upgrade --check # is a newer @xano/sdk published? exits 7 if so, 0 if current
133
+ xanosdk upgrade # install it: globally (npm i -g) for a global CLI; in a project, into the block that lists it (--save-prod or --save-dev)
134
+ xanosdk help # grouped command reference (also the no-arg default)
135
+ xanosdk <command> --help # that command's usage, subcommands, and flags (`xanosdk deploy --help`)
136
+ xanosdk <noun> <verb> --help # scoped to one verb (`xanosdk workspace export --help`)
137
+ xanosdk <command> --json # force JSON on stdout (otherwise: whenever stdout isn't a terminal)
138
+ xanosdk <command> --no-refresh # don't refresh the managed block in AGENTS.md (a build that writes nothing outside its output)
139
+ xanosdk completion zsh # shell completion script (also bash, fish) — see below
140
+
141
+ xanosdk preflight ./xano/index.ts # import into a live instance, diff each object back (needs auth + a throwaway tenant)
142
+ xanosdk preflight ./xano/index.ts --runtime # also run each deployed function on the engine
143
+ xanosdk preflight ./xano/index.ts --capture # write the fetched JSON as fixture candidates
144
+ ```
145
+
146
+ The `marketplace` family has a guide of its own: [The marketplace](marketplace.md) for what
147
+ each verb does to a project, and [The module contract](module-contract.md) for writing an
148
+ add-on.
149
+
150
+ ## Naming a backend
151
+
152
+ Every command that reads, writes or opens a running backend names it with one grammar:
153
+
154
+ | Spelling | What it is |
155
+ |---|---|
156
+ | `workspace` | Your real workspace — the one your login is scoped to |
157
+ | `ephemeral` / `ephemeral:<name>` | The ephemeral this project deployed to, or a named one — by its name (`e4f2-9ab1`, from `xanosdk ephemeral list`), not its display name |
158
+ | `local-engine` / `local-engine:<name>` | The engine this project deployed to, or a named one from `xanosdk local-engine list`. Needs no Xano account |
159
+ | `tenant:<name>` | A tenant on your instance |
160
+ | `release:<name>` | A stored release — where a command reads one (`deploy`, `pull`, `generate`, `init --from`) |
161
+ | a bundle path | Where a command takes a file (`deploy`, `generate`, `init --from`) |
162
+
163
+ The flag follows what the command does with it: **reads take `--from`** (`env pull`, `release
164
+ create`, `init`), **writes take `--to`** (`env set`, `env unset`, `publish`, `deploy`), `test`
165
+ takes **`--on`**, and a command whose subject IS the backend takes it positionally (`deploy`,
166
+ `pull`, `generate`, `tables`, `impersonate`).
167
+
168
+ **Leave it off and a command follows the backend this project last deployed to** — recorded
169
+ in `.xano/deployed.json` by an ephemeral or local-engine deploy, and never by a deploy to your
170
+ workspace or a tenant, so a bare command cannot reach something real unasked. With no record,
171
+ it falls back to the ephemeral recorded under your current credential, then to the engine
172
+ recorded for this directory, and otherwise refuses naming the flag, `xanosdk deploy` and
173
+ `xanosdk deploy --local-engine`.
174
+ `deploy` is the exception: bare, it compiles the project and stands it up on an ephemeral
175
+ unless `--to` or `--local-engine` says otherwise, so it writes that record rather than
176
+ reading it. `generate` and `init --from` need a backend named.
177
+
178
+ A command that cannot serve a backend refuses it, whether you typed it or it came from the
179
+ record, with the reason and the spellings it does take: `publish` refuses a local engine (no
180
+ static host — pass `--to ephemeral`), and so does `release create` (the cut runs on the
181
+ instance — pass `--from ephemeral`). A write to your workspace or a tenant confirms first,
182
+ or needs `--yes` off a terminal; an ephemeral or local engine never asks.
183
+
184
+ ## Bundle key names vs. the names everything else uses
185
+
186
+ A bundle's `payload` arrays are keyed by the engine's **storage** name, which for five kinds
187
+ is not the name the SDK, the CLI, or a release plan uses for the same thing. Scripting against
188
+ a bundle means translating:
189
+
190
+ | You author / the plan reports | `payload` array |
191
+ |---|---|
192
+ | `table` | `payload.dbo` |
193
+ | `api_group` | `payload.app` |
194
+ | `agent` | `payload.toolset` |
195
+ | `mcp_server` | `payload.toolset` |
196
+ | `toolset` | `payload.tool` |
197
+ | `realtime_channel` | `payload.channel` |
198
+ | `realtime_message` | `payload.message` |
199
+
200
+ Everything else (`query`, `function`, `task`, `trigger`, `middleware`, `microservice`,
201
+ `addon`, `workflow_test`, `workspace`) is keyed the same on both sides.
202
+
203
+ Two traps worth naming. A release plan reports operations as `{"type": "table", …}` while the
204
+ bundle it came from stores that object under `payload.dbo` — so a table that released
205
+ correctly looks *missing* if you go looking for `payload.table`. And `agent` and `mcp_server`
206
+ both land in `payload.toolset`, while the kind actually named `toolset` lands in
207
+ `payload.tool`; matching on the word alone will pick the wrong array.
208
+
209
+ This is also the format `xanosdk lock import` reads, so both directions are user-facing.
210
+
211
+
212
+ **Build warnings, and `--strict`.** `export`/`deploy` print a warning (a `!` line) for the
213
+ shapes that ship clean and then do the wrong thing — a `bulk.update` zero-filling the columns
214
+ an item omits, an `ignoreEmpty` on an operand that's already empty, a `ref()` no `as` binds,
215
+ a filter name the engine can't resolve, a `s.switch` case that falls through into the next
216
+ one, a request-time timestamp filter on a `where` operand. Each stays a warning because each
217
+ has a legitimate use. Nothing fails on a message nobody reads, though, so pass `--strict` in CI and in
218
+ unattended agent builds: every warning becomes a hard failure and the exit code carries it —
219
+ the CLI's own too (an unsupplied documentation token or env name, a `xano.lock` entry that
220
+ matches no object), and a db step bound to no table (`table: null`), which a pulled tree may
221
+ carry and which does nothing when run. When a shape is meant — a fixture that pins a hazard on
222
+ purpose — `diagnostics: { allow: ["<code>"] }` on that def accepts that warning for it alone, and
223
+ `--strict` still fails everything else. Every def kind takes it, and it takes any warning code the
224
+ def raised (never an error); the advisory warnings name the form in their own message. Some lines are deliberately
225
+ exempt, because each describes a choice you made rather than a defect in the build: a `--no-lock`
226
+ build says so as information, and a bundle written to stdout that carries env values or a
227
+ documentation token says so on stderr — `--strict` passes both.
228
+ The programmatic equivalents are `emitBundle(app, { strict: true })` and
229
+ `app.export({ strict: true })`. The bundle bytes are identical either way.
230
+
231
+ **Warnings under `--json`.** Every `!` line a run prints on stderr is also in the `--json`
232
+ document it writes, on every command, as `warnings: [{ code, message }]`, and a failure
233
+ document carries the same list as `error.details.warnings`. `code` is stable and dotted, so
234
+ match on it rather than on the sentence. A build diagnostic keeps its own code
235
+ (`stack.env-undeclared`), and the export's own warnings take theirs (`workspace-env.undeclared`,
236
+ `workspace-env.unsupplied`, `lock.orphan`, `lock.dropped-entry`, `lock.ceded-canonical`,
237
+ `filter.unresolvable`, `doc-token.orphan`, `doc-token.cleared`, `doc-token.unsupplied`) — these, and only these, are
238
+ what `--strict` fails on. Every other code is a NOTICE about this run, and `--strict` passes it:
239
+ `secrets.cleartext-export`, `credential.readable-file`, `credential.unpinned-profile`,
240
+ `credential.shadowed-global`, `credential.env-overrides-pin`, `profile.local-shadows-pin`, `static-env.secret-like`, `publish.unbuilt-source`, and a code
241
+ dotted by area on each other line — among them `deploy.tenant-write`, `deploy.destructive`,
242
+ `plan.nothing-matched`, `plan.workspace-rename`, `plan.column-drop`, `release.no-env`,
243
+ `release.drift`, `promote.server-release`, `promote.seed-rows`, `pull.no-git`,
244
+ `pull.kept-files`, `pull.unverified`, `tenant.not-found`,
245
+ `release.not-found`. `message` is the line stderr printed, with any fix-up commands printed
246
+ under it (a lock orphan's `lock rename` / `lock prune`) appended one per line.
247
+ An export that fails its checks — `--strict` promoting a warning included, whichever
248
+ layer raised it — is `SDK_EXPORT_INVALID`, with its checks in `error.details.diagnostics` as
249
+ `{ severity, code, message }` and every warning the run printed beside them in
250
+ `error.details.warnings`.
251
+
252
+ **Naming the backend under `--json`.** Every document that names a backend — a write's
253
+ `destination`, a read's `kind`/`name` (`tables`, `test`), `status`, `ephemeral get`,
254
+ `workspace details`, `impersonate`, and each `tenant list`/`ephemeral list` row — also carries
255
+ `selector`, the spelling the next command's backend slot takes (`"ephemeral:e4f2-9ab1"`,
256
+ `"tenant:acme"`, `"local-engine:<name>"`, `"workspace"`; `ephemeral get|delete|export` and
257
+ `tenant get|deploy|delete` take it as well as the bare name), and `workspaceId`, the numeric workspace it acts on (for an ephemeral or tenant,
258
+ the workspace it lives under). The fields each command already had stay. `export --out --json`
259
+ carries `written: true`, as `export --check` carries `written: false`. `help --json` lists each
260
+ flag as `{ flag, summary, name, short?, arg?, values?, examples?, description }` — `flag` the spec
261
+ string, `values` its whole closed set, `examples` a few values of a flag that takes others too
262
+ (`--to tenant:<name>`, `--theme <url>`).
263
+
264
+ ## Shell completion
265
+
266
+ `xanosdk completion <bash|zsh|fish>` prints a completion script covering every command, verb, flag,
267
+ and closed value set (`--on workspace|ephemeral|local-engine`, `--format json|multidoc`, `--icons lucide|tabler|phosphor`).
268
+ A flag's value completes as what it takes: its closed set, file names for a path (`--config`,
269
+ `--secrets-file`), directories for a directory (`--backend-dir`), stored profile names for
270
+ `--profile`/`--to-profile`, and nothing for a free-form value (`--name`, `--expires-hours`).
271
+ `xanosdk help <Tab>` completes command names, then verbs.
272
+ It is generated from the CLI's own command table, so it never drifts from what the CLI accepts — but it
273
+ is baked at generation time, so re-run it after upgrading.
274
+
275
+ ```bash
276
+ # zsh
277
+ xanosdk completion zsh > "${fpath[1]}/_xanosdk" # then restart your shell
278
+
279
+ # bash
280
+ xanosdk completion bash > ~/.xanosdk-completion.bash
281
+ echo 'source ~/.xanosdk-completion.bash' >> ~/.bashrc
282
+
283
+ # fish
284
+ xanosdk completion fish > ~/.config/fish/completions/xanosdk.fish
285
+ ```
286
+
287
+ After a command succeeds the CLI checks npm (at most once an hour, cached in
288
+ `~/.xanosdk/update-check.json`) and prints a one-line nudge to **stderr** when a newer
289
+ `@xano/sdk` is published — never to stdout, so piped bundles stay clean. The suggested
290
+ command adapts to how you installed it: `npm i -g @xano/sdk@latest` for a global install,
291
+ `xanosdk upgrade` when it's a project dependency (it keeps the SDK in the `package.json` block
292
+ that lists it and restores the scaffolded range, which a raw `npm i` would not). The check is best-effort and bounded (a slow or offline
293
+ registry never delays a command), and stays silent under CI or when stderr isn't a terminal.
294
+ Opt out with `XANOSDK_NO_UPDATE_CHECK=1` (or the conventional `NO_UPDATE_NOTIFIER=1`).
295
+
296
+ `xanosdk upgrade` is the same question asked on purpose, and it answers under all the
297
+ conditions the nudge stays quiet for — CI, a piped stderr, the opt-out variables — reading the
298
+ registry live rather than serving the hourly cache. `--check` reports without installing and
299
+ exits **7** when a newer version is published, **0** when you are current or ahead of the
300
+ latest published version (a local build; `status: "ahead"`, and a bare `upgrade` installs
301
+ nothing), so a pipeline can branch on it:
302
+
303
+ ```bash
304
+ xanosdk upgrade --check || echo "an upgrade is waiting"
305
+ xanosdk upgrade --check | jq -r .latest # piped stdout is JSON already
306
+ ```
307
+
308
+ A registry it cannot reach is an error (exit 1), never a quiet "you are up to date". Without
309
+ `--check` it installs the exact version the check found (never npm's `latest` tag, which npm
310
+ resolves from a cache that lags a fresh release) and reports the version it actually installed
311
+ as `installed`. The install matches how this CLI is installed (`npm i -g` for a global one; for a
312
+ project, `--save-prod` when `@xano/sdk` is in `dependencies`, as a scaffold puts it, else
313
+ `--save-dev`) — and for a project-local install it
314
+ then restores the `@xano/sdk` range your project was scaffolded with (npm rewrites it to a
315
+ caret) and restamps the managed block in your
316
+ agent files so the guidance matches the version you now have. Set `XANOSDK_INSTALL_MODE` to
317
+ `global` or `local` to override the detection. Run through `npx` (or `pnpm dlx`, `yarn dlx`, `bunx`)
318
+ outside a project, it installs nothing (`status: "not-installed"`): that copy is temporary,
319
+ and `npx @xano/sdk@latest` already runs the newest release.
320
+
321
+ ## Checking what an installed SDK can do
322
+
323
+ A tool that drives the CLI, such as an editor extension or a CI step, should check that the
324
+ installed SDK has the commands it needs before it starts. **The compatibility contract is the
325
+ installed version plus the `cli` array in its `manifest.json`.** There is no capability
326
+ endpoint and no protocol number. The manifest is a file inside the package, so reading it
327
+ needs no sign-in and no network. It comes from the same registry that parses the
328
+ command line, so it cannot list a command the CLI does not accept.
329
+
330
+ Each `cli` entry carries a `command` (`"promote"`, `"release create"`, `"publish"`) and its
331
+ `flags`. Check for the commands and flags you will actually run:
332
+
333
+ ```bash
334
+ M=node_modules/@xano/sdk/manifest.json
335
+ jq -r .version "$M" # e.g. 1.0.3
336
+ jq -e '.cli[] | select(.command=="promote")' "$M" >/dev/null # exit 0: present
337
+ jq -e '.cli[] | select(.command=="publish") | .flags[]
338
+ | select(.flag | startswith("--release"))' "$M" >/dev/null # a flag, not just the command
339
+ ```
340
+
341
+ ```ts
342
+ import manifest from "@xano/sdk/manifest.json" with { type: "json" };
343
+ const has = (cmd: string) => manifest.cli.some((c) => c.command === cmd);
344
+ ```
345
+
346
+ Read the manifest from the install whose `xanosdk` you will run: a project dependency and a
347
+ global install can be different versions.
348
+
349
+ - **Check each operation, not the whole feature.** A backend-only promotion needs `promote`,
350
+ and it should not be refused because `publish` is missing. Gate each command on its own entry.
351
+ - **When something is missing, name the command.** `This workflow needs \`xanosdk publish\`,
352
+ which the installed @xano/sdk (1.0.x) does not have. Run \`xanosdk upgrade\`.` is accurate. "This SDK has
353
+ no release support" is not, when `release create` and `promote` are right there.
354
+ - **The manifest describes the package, not the target.** Whether a particular workspace or
355
+ instance will accept an operation is decided when the command runs. The command's own
356
+ failure is the answer to that, not a check you make in advance.
357
+
358
+ ## Commands the CLI prints
359
+
360
+ Every command the CLI prints for you to run next (a remedy, a rerun, a hint, `--help`) is spelled
361
+ for where it runs, in the text and in `--json` fields such as `rerun` alike: plain `xanosdk …` from a
362
+ global install; otherwise `npx xanosdk …` in a project with `@xano/sdk` installed, and
363
+ `npx @xano/sdk …` anywhere else (a `cd <dir> && …` is spelled for `<dir>`). Paths in it read from
364
+ where you typed — under `npm run`, the directory you ran npm in. Files it writes to disk (a
365
+ scaffold's README, `AGENTS.md`, `.env.example`) always say `npx xanosdk …`.
366
+
367
+ ## When something fails
368
+
369
+ Failures are written for the person reading the terminal. A request the instance refused
370
+ prints the server's own sentence on one line (`deploy failed (403 Forbidden): Access denied
371
+ for this workspace.`) rather than the whole JSON envelope; a request that never arrived names
372
+ what it could not reach and why (`workspace list could not reach https://…: fetch failed
373
+ (ECONNRESET)`), and a request that timed out says so separately, because "slow" and
374
+ "unreachable" call for different next steps. Set `XANOSDK_DEBUG=1` to append the untouched
375
+ response body underneath — nothing is discarded, only folded away.
376
+
377
+ A malformed invocation fails before any work happens: a missing or misspelled entry file, an
378
+ argument the command has nowhere to put (`xanosdk export --lock xano.lock` — that flag takes
379
+ its value attached, as `--lock=xano.lock`), or a missing credential, each answered with the
380
+ command's usage block instead of a stack trace. `deploy` and `release` check that you are
381
+ signed in *before* compiling, so a lapsed session costs you a message, not a build.
382
+
383
+ Emitters that write to disk (and the programmatic CLI) are Node-only — import them from
384
+ `@xano/sdk/node`. The string emitters (`emit`, `emitBundle`) stay on the browser-safe
385
+ `@xano/sdk` entry.
386
+
387
+ ```ts
388
+ import { emitBundle } from "@xano/sdk"; // pure string — browser-safe
389
+ import { writeBundle } from "@xano/sdk/node"; // writes a file — Node only
390
+ ```
391
+
392
+ **Four entry points, and only the first is the authoring API.** `@xano/sdk` is what you
393
+ define a workspace with; `@xano/sdk/node` adds the filesystem half; `@xano/sdk/codegen`
394
+ is what a generated tree imports; and `@xano/sdk/internal` holds the compiler machinery —
395
+ the per-kind encoders, the kind and statement registries, the bundle serializer, the
396
+ `xano.lock` model. Nothing on `/internal` is needed to author anything, and it is kept off
397
+ the root so an agent scanning the package's exports sees the surface rather than the guts.
398
+
399
+ `emit`, `emitBundle` and the `@xano/sdk/node` writers run the same build-time checks, including seed validation of a literal
400
+ `seed: [...]` array. A **deferred** seed (a thunk, or `seedFile()`) needs an await or the
401
+ filesystem, so it is materialised and checked only by `xanosdk export` / `xanosdk deploy`.
@@ -0,0 +1,177 @@
1
+ # Pulling an existing workspace
2
+
3
+ What `xanosdk init --from` writes, how faithful the pull is, and how to read its report.
4
+
5
+ ## Running it
6
+
7
+ A pull is `init` with `xano/` filled from an existing backend instead of the empty starter,
8
+ so it is the scaffold command with one extra flag:
9
+
10
+ ```bash
11
+ xanosdk init my-app --from workspace # your real workspace (the one your login is scoped to)
12
+ xanosdk init my-app --from ephemeral:my-env # a named ephemeral environment
13
+ xanosdk init my-app --from local-engine:<name> # a running local engine (`local-engine list`) — no login
14
+ xanosdk init my-app --from ./ws.json # a bundle already on disk — offline, no login
15
+ ```
16
+
17
+ `--from` takes any backend in [the CLI's one grammar](cli.md#naming-a-backend) —
18
+ `workspace`, `ephemeral[:<name>]`, `local-engine[:<name>]`, `tenant:<name>`, `release:<name>` —
19
+ or a bundle path. Unlike most commands it has no default: a pull names its source.
20
+
21
+ Everything else about the project is unchanged: `--framework`, the theme flags, `--no-agents-md` and
22
+ `--web` mean the same thing here as they do without `--from`. The three flags that describe
23
+ the pull itself — `--report`, `--skip-roundtrip` and `--branch` — are refused without it rather
24
+ than silently ignored.
25
+
26
+ ### Just the tree: `generate`
27
+
28
+ `xanosdk generate <source>` writes the same decoded tree `init --from` puts in `xano/`, and nothing
29
+ else — no `package.json`, no frontend, no round-trip check. Use it to drop a backend into a repo
30
+ that already has its own shell, or to read one.
31
+
32
+ ```bash
33
+ xanosdk generate workspace # → ./xano
34
+ xanosdk generate release:v1 --out ./backend # a stored release
35
+ xanosdk generate ephemeral:my-env # also tenant:<name>, local-engine[:<name>]
36
+ xanosdk generate ./ws.json # a bundle on disk — offline, no login
37
+ xanosdk generate workspace --branch staging # --branch applies to `workspace` only
38
+ ```
39
+
40
+ The output directory must be new, empty, or a tree an earlier decode wrote — `generate` and
41
+ `init --from` both leave `.xanosdk-codegen.json` in it, and that marker is the proof. Replacing a tree
42
+ needs `--force`, and keeps its `.env` and `.secrets.json`; a non-empty directory
43
+ without the marker, or the working directory itself, is refused even with `--force`. Env var names
44
+ land in `.env.example`; the values a bundle carries land in the tree's `.env` — owner-only and
45
+ gitignored, as `init --from` writes them — unless `--no-secrets` declines them, and an existing
46
+ `.env` is kept. `--json` names them under `env` (names only). `xano.lock` is reconciled the way `pull` does it:
47
+ an entry survives only when the source still carries an object with that name and identity, so
48
+ generating a different backend into a tree cannot carry its old identities forward.
49
+
50
+ ### Pulling a branch
51
+
52
+ `--branch <label>` reads that branch instead of the live one:
53
+
54
+ ```bash
55
+ xanosdk init review-staging --from workspace --branch staging
56
+ xanosdk pull workspace --branch staging # refresh that tree from the same branch
57
+ xanosdk workspace export --branch staging --path - # the raw bundle, same selector
58
+ ```
59
+
60
+ Only logic differs. Tables and microservices are shared by every branch, so the schema in the
61
+ tree is the same one live has — a branch pull changes the APIs, functions and tasks around it.
62
+
63
+ Three things are refused rather than quietly worked around, because each would otherwise hand
64
+ back a plausible tree built from something other than what was asked for:
65
+
66
+ - **A label the workspace does not have**, answered with the labels it does have.
67
+ - **An instance too old to honor the selector.** Such an instance accepts the branch, ignores
68
+ it, and returns the live branch with a success status — so the SDK asks it about a branch
69
+ that cannot exist first, and refuses to read at all if it gets an archive back instead of a
70
+ refusal.
71
+ - **`--branch` on a source with no branches to choose between** — an ephemeral carries the
72
+ logic it was deployed with, and a bundle file is one archive already exported from one
73
+ branch. Re-export, or deploy from the branch you want, instead.
74
+
75
+ ## The generated tree
76
+
77
+ Inside, `xano/` is shaped the way the workspace is: one directory per kind, with each
78
+ object under its parent — queries under the API group that owns them, triggers under
79
+ what they fire on. Each table gets its own `table/<name>.ts`, settings sit in `xano/workspace.ts`,
80
+ `_shared.ts` holds anything else referenced from more than one file, and `xano/README.md`
81
+ lists anything that did not translate cleanly.
82
+ Object identities (`guid`) are preserved, so cross-references stay intact. A statement
83
+ this SDK does not model yet round-trips verbatim rather than breaking the pull.
84
+
85
+ Pulled objects are authored the same way you would write them by hand — `table({...})`,
86
+ `query({...})`, `defineFunction({...})` — so the generated tree keeps its types. A pulled
87
+ table's columns still check on `fieldName`/`output`/`sortBy`, `InferInput<typeof q>` still
88
+ resolves a pulled query's payload, and a pulled agent still types `s.ai.agent.run`.
89
+
90
+ A pull states what the source workspace actually holds and leaves out what the SDK would
91
+ put back anyway. A table's `primary(id)` / `created_at` / `gin(xdo)` indexes are the
92
+ engine's standard set, so only the indexes someone created are listed. A trigger comes back
93
+ through the factory that built it (`tableTrigger`, `realtimeTrigger`, …) rather than a bare
94
+ `satisfies TriggerDef`, which keeps its typed stack handle; the two realtime types that bind
95
+ a def handle are the exception, since a stored trigger carries two guids with no way to know
96
+ they agree. And two objects that reference each other — a pair of tables joined both ways,
97
+ two functions that call each other — can't both be declared first, so the second reference
98
+ is a `{name, guid}` const hoisted to the top of the file (`const OrdersRef = {…}`) instead of
99
+ an import that would close a cycle. Only the guid is ever read, so it binds exactly.
100
+
101
+ `xano/README.md` also lists objects that were **already empty in the source** — an
102
+ endpoint someone created and never filled in pulls as a def with no `stack`, which looks
103
+ identical to a decode that gave up. The report is what tells the two apart.
104
+
105
+ A few options exist only so a pull can be *faithful*, and reading them in generated code
106
+ is the only time you should see them: `table: null` / `fn: null` (a statement whose target
107
+ was deleted or never bound), `merge` / `hidden` on a field, `paging: { enabled }` on a
108
+ query, `c.blank(tag)` (the editor's unconfigured value box — **not** a zero or an
109
+ empty collection; the engine reads `""` and `"0"` differently, so tidying one into the
110
+ other changes what the workspace stores), and `c.null("const:obj")` (the object-typed null
111
+ a `db.*` statement's `@meta` slot carries — different stored bytes from `c.obj(null)`,
112
+ which is the blank object, though both evaluate to null). They describe what the source workspace actually
113
+ stored — a pulled `table: null` is a defect to fix upstream, not a shape to copy — and
114
+ each carries that warning at the call site. A blank binding also reports, because a
115
+ statement wired to a table or function that no longer exists is worth seeing even though
116
+ it round-trips exactly.
117
+
118
+ ## Verification, and the decode report
119
+
120
+ Then it checks its own work: the project it just wrote is loaded, exported, and diffed
121
+ against the workspace it came from. A mismatch names the object and fails the command
122
+ (`--skip-roundtrip` opts out). So "it compiled" and "it means the same thing" are separate
123
+ claims, and you get both.
124
+
125
+ A tree that passes gets its `xano/xano.lock` from that same export, so the lock is committed
126
+ with the first commit rather than written by the first deploy. As with a plain `init`, the
127
+ project is pinned in `xano.profile.json` to the profile the pull ran under. Under `--json` the
128
+ command answers with the project — `dir`, `name`, `pinnedProfile`, `lock`, `verified` — and
129
+ the findings under `report`.
130
+
131
+ The findings above are printed either way. Verification runs after decoding is finished,
132
+ so whether it passes, disagrees, or cannot run at all, the report describing the decode is
133
+ rendered first — and a tree that was written but does not re-export exits **2**, distinct
134
+ from the **1** you get when nothing was written at all. A tree that fails verification is
135
+ reported as unverified rather than ready, and the deploy next steps are withheld: the check
136
+ exists precisely because re-exporting that tree does not reproduce the workspace it came
137
+ from.
138
+
139
+ **A source that carries one object twice.** An export can repeat a stored object — the same
140
+ guid appearing on several records, differing only in generated editor ids. Those are merged
141
+ into one definition keeping the original guid, and the merge is reported as a notice, so a
142
+ tree holding fewer objects than the archive has records is explained rather than merely
143
+ smaller. Records that share a guid but genuinely differ are refused before anything is
144
+ written (exit **1**), naming both records and the fields they disagree on: one guid cannot
145
+ describe two objects, and renaming one or pinning a fresh guid would repoint every
146
+ reference that resolves through it.
147
+
148
+ **Reading the report.** It opens with a headline (`27 distinct issues across 424 findings;
149
+ 3 need your attention`) and splits into three sections, most actionable first: *problems in
150
+ your workspace*, *things Xano SDK could not model*, and the things stated only so the output
151
+ is not ambiguous. That split is the question a reader actually has — a `raw()` passthrough
152
+ is ours to close, a lambda reading an unbound name is theirs to fix, and an empty object is
153
+ neither. Findings that repeat the same sentence across objects collapse to one line with a
154
+ count and a collapsed object list, and each names the generated file it landed in.
155
+ `--report full` prints every site instead; `--report json` prints the findings as data (piped
156
+ or under `--json`, as the `report` field of the command's one JSON document), and the same data is written into `xano/.xanosdk-codegen.json` on every pull, so parity is
157
+ trackable release over release and gateable in CI without scraping output.
158
+
159
+ Re-pulling is a real workflow: a second `init --from` into the same directory refreshes
160
+ `xano/` and leaves the rest of the project — your `package.json`, your `frontend/` —
161
+ exactly as you left it. No `--force` needed, because the tree carries a marker saying it
162
+ was decoded. It and `xanosdk pull` share one planner: before any write it lists the files it
163
+ will delete and the files it will rewrite — telling a file you edited since the last decode
164
+ from one only the source changed — refuses a `xano/` git reports as dirty, and asks; without
165
+ a terminal, `--yes` answers. It removes only files the previous decode wrote; one you added
166
+ beside them is kept and named. `xano.lock` is reconciled against the source (dropped entries
167
+ are listed), then updated from the new tree, so `export --check` passes after it. `--force`
168
+ over a `xano/` no decode wrote follows the same rules.
169
+
170
+ > ⚠️ **`xano/` is your source once pulled — commit it.** A refresh overwrites the files it
171
+ > decodes, so commit before one (a directory that isn't a previous pull still needs `--force`).
172
+ > It carries schema only — no table rows or stored files.
173
+ > `xanosdk deploy` sends it to a disposable ephemeral environment as a *full replace*;
174
+ > `xanosdk promote <release>` or `xanosdk deploy --to workspace` merges it into your real
175
+ > workspace. Workspace env vars are declared by name only — their values never enter the tree.
176
+
177
+ ---