@xano/cli 0.0.95 → 1.0.1

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 +5562 -2228
  236. package/package.json +19 -3
@@ -0,0 +1,185 @@
1
+ import { Flags } from '@oclif/core';
2
+ import snakeCase from 'lodash.snakecase';
3
+ import BaseCommand from '../../../base-command.js';
4
+ import { buildApiGroupFolderResolver, parseDocument } from '../../../utils/document-parser.js';
5
+ import * as fs from 'node:fs';
6
+ import * as path from 'node:path';
7
+ export default class SandboxPull extends BaseCommand {
8
+ static description = 'Pull documents from your sandbox environment and split into individual files';
9
+ static examples = [
10
+ `$ xano sandbox pull
11
+ Pulled 42 documents from sandbox environment to current directory
12
+ `,
13
+ `$ xano sandbox pull -d ./my-sandbox
14
+ Pulled 42 documents from sandbox environment to ./my-sandbox
15
+ `,
16
+ `$ xano sandbox pull --env --records`,
17
+ ];
18
+ static flags = {
19
+ ...BaseCommand.baseFlags,
20
+ directory: Flags.string({
21
+ char: 'd',
22
+ default: '.',
23
+ description: 'Output directory for pulled documents (defaults to current directory)',
24
+ required: false,
25
+ }),
26
+ draft: Flags.boolean({
27
+ default: false,
28
+ description: 'Include draft versions',
29
+ required: false,
30
+ }),
31
+ env: Flags.boolean({
32
+ default: false,
33
+ description: 'Include environment variables',
34
+ required: false,
35
+ }),
36
+ records: Flags.boolean({
37
+ default: false,
38
+ description: 'Include records',
39
+ required: false,
40
+ }),
41
+ };
42
+ async run() {
43
+ const { flags } = await this.parse(SandboxPull);
44
+ const { profile } = this.resolveProfile(flags);
45
+ const queryParams = new URLSearchParams({
46
+ env: flags.env.toString(),
47
+ include_draft: flags.draft.toString(),
48
+ records: flags.records.toString(),
49
+ });
50
+ const apiUrl = `${profile.instance_origin}/api:meta/sandbox/multidoc?${queryParams.toString()}`;
51
+ let responseText;
52
+ try {
53
+ const response = await this.verboseFetch(apiUrl, {
54
+ headers: {
55
+ accept: 'application/json',
56
+ Authorization: `Bearer ${profile.access_token}`,
57
+ },
58
+ method: 'GET',
59
+ }, flags.verbose, profile.access_token);
60
+ if (!response.ok) {
61
+ const message = await this.parseApiError(response, 'API request failed');
62
+ this.error(message);
63
+ }
64
+ responseText = await response.text();
65
+ }
66
+ catch (error) {
67
+ if (error instanceof Error && 'oclif' in error)
68
+ throw error;
69
+ if (error instanceof Error) {
70
+ this.error(`Failed to fetch multidoc: ${error.message}`);
71
+ }
72
+ else {
73
+ this.error(`Failed to fetch multidoc: ${String(error)}`);
74
+ }
75
+ }
76
+ const rawDocuments = responseText.split('\n---\n');
77
+ const documents = [];
78
+ for (const raw of rawDocuments) {
79
+ const trimmed = raw.trim();
80
+ if (!trimmed) {
81
+ continue;
82
+ }
83
+ const parsed = parseDocument(trimmed);
84
+ if (parsed) {
85
+ documents.push(parsed);
86
+ }
87
+ }
88
+ if (documents.length === 0) {
89
+ this.log('No documents found in response');
90
+ return;
91
+ }
92
+ const outputDir = path.resolve(flags.directory);
93
+ fs.mkdirSync(outputDir, { recursive: true });
94
+ const getApiGroupFolder = buildApiGroupFolderResolver(documents, snakeCase);
95
+ const filenameCounters = new Map();
96
+ let writtenCount = 0;
97
+ for (const doc of documents) {
98
+ let typeDir;
99
+ let baseName;
100
+ if (doc.type === 'workspace') {
101
+ typeDir = path.join(outputDir, 'workspace');
102
+ baseName = this.sanitizeFilename(doc.name);
103
+ }
104
+ else if (doc.type === 'workspace_trigger') {
105
+ typeDir = path.join(outputDir, 'workspace', 'trigger');
106
+ baseName = this.sanitizeFilename(doc.name);
107
+ }
108
+ else if (doc.type === 'agent') {
109
+ typeDir = path.join(outputDir, 'ai', 'agent');
110
+ baseName = this.sanitizeFilename(doc.name);
111
+ }
112
+ else if (doc.type === 'mcp_server') {
113
+ typeDir = path.join(outputDir, 'ai', 'mcp_server');
114
+ baseName = this.sanitizeFilename(doc.name);
115
+ }
116
+ else if (doc.type === 'tool') {
117
+ typeDir = path.join(outputDir, 'ai', 'tool');
118
+ baseName = this.sanitizeFilename(doc.name);
119
+ }
120
+ else if (doc.type === 'agent_trigger') {
121
+ typeDir = path.join(outputDir, 'ai', 'agent', 'trigger');
122
+ baseName = this.sanitizeFilename(doc.name);
123
+ }
124
+ else if (doc.type === 'mcp_server_trigger') {
125
+ typeDir = path.join(outputDir, 'ai', 'mcp_server', 'trigger');
126
+ baseName = this.sanitizeFilename(doc.name);
127
+ }
128
+ else if (doc.type === 'table_trigger') {
129
+ typeDir = path.join(outputDir, 'table', 'trigger');
130
+ baseName = this.sanitizeFilename(doc.name);
131
+ }
132
+ else if (doc.type === 'realtime_channel') {
133
+ typeDir = path.join(outputDir, 'realtime', 'channel');
134
+ baseName = this.sanitizeFilename(doc.name);
135
+ }
136
+ else if (doc.type === 'realtime_trigger') {
137
+ typeDir = path.join(outputDir, 'realtime', 'trigger');
138
+ baseName = this.sanitizeFilename(doc.name);
139
+ }
140
+ else if (doc.type === 'api_group') {
141
+ const groupFolder = getApiGroupFolder(doc.name);
142
+ typeDir = path.join(outputDir, 'api', groupFolder);
143
+ baseName = this.sanitizeFilename(doc.name);
144
+ }
145
+ else if (doc.type === 'query' && doc.apiGroup) {
146
+ const groupFolder = getApiGroupFolder(doc.apiGroup);
147
+ const nameParts = doc.name.split('/');
148
+ const leafName = nameParts.pop();
149
+ const folderParts = nameParts.map((part) => snakeCase(part));
150
+ typeDir = path.join(outputDir, 'api', groupFolder, ...folderParts);
151
+ baseName = this.sanitizeFilename(leafName);
152
+ if (doc.verb) {
153
+ baseName = `${baseName}_${doc.verb}`;
154
+ }
155
+ }
156
+ else {
157
+ const nameParts = doc.name.split('/');
158
+ const leafName = nameParts.pop();
159
+ const folderParts = nameParts.map((part) => snakeCase(part));
160
+ typeDir = path.join(outputDir, doc.type, ...folderParts);
161
+ baseName = this.sanitizeFilename(leafName);
162
+ if (doc.verb) {
163
+ baseName = `${baseName}_${doc.verb}`;
164
+ }
165
+ }
166
+ fs.mkdirSync(typeDir, { recursive: true });
167
+ const dirKey = path.relative(outputDir, typeDir);
168
+ if (!filenameCounters.has(dirKey)) {
169
+ filenameCounters.set(dirKey, new Map());
170
+ }
171
+ const typeCounters = filenameCounters.get(dirKey);
172
+ const count = typeCounters.get(baseName) || 0;
173
+ typeCounters.set(baseName, count + 1);
174
+ let filename;
175
+ filename = count === 0 ? `${baseName}.xs` : `${baseName}_${count + 1}.xs`;
176
+ const filePath = path.join(typeDir, filename);
177
+ fs.writeFileSync(filePath, doc.content, 'utf8');
178
+ writtenCount++;
179
+ }
180
+ this.log(`Pulled ${writtenCount} documents from sandbox environment to ${flags.directory}`);
181
+ }
182
+ sanitizeFilename(name) {
183
+ return snakeCase(name.replaceAll('"', ''));
184
+ }
185
+ }
@@ -0,0 +1,26 @@
1
+ import BaseCommand from '../../../base-command.js';
2
+ export default class SandboxPush extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ directory: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ delete: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ 'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ env: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ exclude: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ guids: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
+ include: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ records: import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
+ review: import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
+ sync: import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
+ transaction: import("@oclif/core/interfaces").BooleanFlag<boolean>;
18
+ truncate: import("@oclif/core/interfaces").BooleanFlag<boolean>;
19
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
20
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
21
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
22
+ };
23
+ run(): Promise<void>;
24
+ private openReview;
25
+ private getFrontendUrl;
26
+ }
@@ -0,0 +1,196 @@
1
+ import { Flags } from '@oclif/core';
2
+ import * as fs from 'node:fs';
3
+ import { resolve } from 'node:path';
4
+ import open from 'open';
5
+ import BaseCommand from '../../../base-command.js';
6
+ import { executePush } from '../../../utils/multidoc-push.js';
7
+ export default class SandboxPush extends BaseCommand {
8
+ static description = 'Push local documents to your sandbox environment via multidoc import. By default, only changed files are pushed (partial mode). Use --sync to push all files. Shows a preview of changes before pushing unless --force is specified. Use --dry-run to preview only.';
9
+ static examples = [
10
+ `$ xano sandbox push
11
+ Push from current directory (default partial mode)
12
+ `,
13
+ `$ xano sandbox push -d ./my-workspace
14
+ Push from a specific directory
15
+ `,
16
+ `$ xano sandbox push --sync
17
+ Push all files to the sandbox
18
+ `,
19
+ `$ xano sandbox push --sync --delete
20
+ Push all files and delete remote objects not included
21
+ `,
22
+ `$ xano sandbox push --dry-run
23
+ Preview changes without pushing
24
+ `,
25
+ `$ xano sandbox push --force
26
+ Skip preview and push immediately
27
+ `,
28
+ `$ xano sandbox push --records --env`,
29
+ `$ xano sandbox push --truncate`,
30
+ `$ xano sandbox push -i "**/func*"
31
+ Push only files matching the glob pattern
32
+ `,
33
+ `$ xano sandbox push -i "function/*" -i "table/*"
34
+ Push files matching multiple patterns
35
+ `,
36
+ `$ xano sandbox push -e "table/*"
37
+ Push all files except tables
38
+ `,
39
+ `$ xano sandbox push --review
40
+ Push and open sandbox review in the browser
41
+ `,
42
+ ];
43
+ static flags = {
44
+ ...BaseCommand.baseFlags,
45
+ directory: Flags.string({
46
+ char: 'd',
47
+ default: '.',
48
+ description: 'Directory containing documents to push (defaults to current directory)',
49
+ required: false,
50
+ }),
51
+ delete: Flags.boolean({
52
+ default: false,
53
+ description: 'Delete sandbox objects not included in the push (requires --sync)',
54
+ required: false,
55
+ }),
56
+ 'dry-run': Flags.boolean({
57
+ default: false,
58
+ description: 'Show preview of changes without pushing (exit after preview)',
59
+ required: false,
60
+ }),
61
+ env: Flags.boolean({
62
+ default: false,
63
+ description: 'Include environment variables in import',
64
+ required: false,
65
+ }),
66
+ exclude: Flags.string({
67
+ char: 'e',
68
+ description: 'Glob pattern to exclude files (e.g. "table/*", "**/test*"). Matched against relative paths from the push directory.',
69
+ multiple: true,
70
+ required: false,
71
+ }),
72
+ force: Flags.boolean({
73
+ default: false,
74
+ description: 'Skip preview and confirmation prompt (for CI/CD pipelines)',
75
+ required: false,
76
+ }),
77
+ guids: Flags.boolean({
78
+ allowNo: true,
79
+ default: true,
80
+ description: 'Write server-assigned GUIDs back to local files (use --no-guids to skip)',
81
+ required: false,
82
+ }),
83
+ include: Flags.string({
84
+ char: 'i',
85
+ description: 'Glob pattern to include files (e.g. "**/func*", "table/*.xs"). Matched against relative paths from the push directory.',
86
+ multiple: true,
87
+ required: false,
88
+ }),
89
+ records: Flags.boolean({
90
+ default: false,
91
+ description: 'Include records in import',
92
+ required: false,
93
+ }),
94
+ review: Flags.boolean({
95
+ default: false,
96
+ description: 'Open sandbox review in the browser after pushing',
97
+ required: false,
98
+ }),
99
+ sync: Flags.boolean({
100
+ default: false,
101
+ description: 'Full push — send all files, not just changed ones. Required for --delete.',
102
+ required: false,
103
+ }),
104
+ transaction: Flags.boolean({
105
+ allowNo: true,
106
+ default: true,
107
+ description: 'Wrap import in a database transaction (use --no-transaction for debugging purposes)',
108
+ required: false,
109
+ }),
110
+ truncate: Flags.boolean({
111
+ default: false,
112
+ description: 'Truncate all table records before importing',
113
+ required: false,
114
+ }),
115
+ };
116
+ async run() {
117
+ const { flags } = await this.parse(SandboxPush);
118
+ const { profile } = this.resolveProfile(flags);
119
+ const inputDir = resolve(flags.directory);
120
+ if (!fs.existsSync(inputDir)) {
121
+ this.error(`Directory not found: ${inputDir}`);
122
+ }
123
+ if (!fs.statSync(inputDir).isDirectory()) {
124
+ this.error(`Not a directory: ${inputDir}`);
125
+ }
126
+ const baseUrl = `${profile.instance_origin}/api:meta/sandbox`;
127
+ const target = {
128
+ buildDryRunUrl: (params) => `${baseUrl}/multidoc/dry-run?${params.toString()}`,
129
+ buildPushUrl: (params) => `${baseUrl}/multidoc?${params.toString()}`,
130
+ cliVersion: this.config.version,
131
+ instanceOrigin: profile.instance_origin,
132
+ label: 'sandbox environment',
133
+ supportsBranches: false,
134
+ supportsPartial: true,
135
+ };
136
+ const pushFlags = {
137
+ delete: flags.delete,
138
+ 'dry-run': flags['dry-run'],
139
+ env: flags.env,
140
+ exclude: flags.exclude,
141
+ force: flags.force,
142
+ guids: flags.guids,
143
+ include: flags.include,
144
+ records: flags.records,
145
+ sync: flags.sync,
146
+ transaction: flags.transaction,
147
+ truncate: flags.truncate,
148
+ verbose: flags.verbose,
149
+ };
150
+ await executePush({
151
+ accessToken: profile.access_token,
152
+ branch: '',
153
+ command: this,
154
+ inputDir,
155
+ verboseFetch: this.verboseFetch.bind(this),
156
+ }, target, pushFlags);
157
+ if (flags.review) {
158
+ await this.openReview(profile.instance_origin, profile.access_token, flags.verbose);
159
+ }
160
+ }
161
+ async openReview(instanceOrigin, accessToken, verbose) {
162
+ const response = await this.verboseFetch(`${instanceOrigin}/api:meta/sandbox/impersonate`, {
163
+ headers: {
164
+ accept: 'application/json',
165
+ Authorization: `Bearer ${accessToken}`,
166
+ },
167
+ method: 'GET',
168
+ }, verbose, accessToken);
169
+ if (!response.ok) {
170
+ const message = await this.parseApiError(response, 'Failed to open sandbox review');
171
+ this.error(message);
172
+ }
173
+ const result = (await response.json());
174
+ if (!result._ti) {
175
+ this.error('No one-time token returned from impersonate API');
176
+ }
177
+ const frontendUrl = this.getFrontendUrl(instanceOrigin);
178
+ const params = new URLSearchParams({ _ti: result._ti });
179
+ const reviewUrl = `${frontendUrl}/impersonate?${params.toString()}`;
180
+ this.log('Opening sandbox review...');
181
+ await open(reviewUrl);
182
+ }
183
+ getFrontendUrl(instanceOrigin) {
184
+ try {
185
+ const url = new URL(instanceOrigin);
186
+ if (url.hostname === 'localhost' || url.hostname === '127.0.0.1') {
187
+ url.port = '4200';
188
+ return url.origin;
189
+ }
190
+ }
191
+ catch {
192
+ // fall through
193
+ }
194
+ return instanceOrigin;
195
+ }
196
+ }
@@ -0,0 +1,13 @@
1
+ import BaseCommand from '../../../base-command.js';
2
+ export default class SandboxReset extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ run(): Promise<void>;
12
+ private confirm;
13
+ }
@@ -0,0 +1,71 @@
1
+ import { Flags } from '@oclif/core';
2
+ import BaseCommand from '../../../base-command.js';
3
+ export default class SandboxReset extends BaseCommand {
4
+ static description = 'Reset your sandbox environment (clears all workspace data and drafts)';
5
+ static examples = [
6
+ `$ xano sandbox reset
7
+ Are you sure you want to reset your sandbox environment? All workspace data and drafts will be cleared. (y/N) y
8
+ Sandbox environment has been reset.
9
+ `,
10
+ `$ xano sandbox reset --force`,
11
+ ];
12
+ static flags = {
13
+ ...BaseCommand.baseFlags,
14
+ force: Flags.boolean({
15
+ char: 'f',
16
+ default: false,
17
+ description: 'Skip confirmation prompt',
18
+ required: false,
19
+ }),
20
+ };
21
+ async run() {
22
+ const { flags } = await this.parse(SandboxReset);
23
+ const { profile } = this.resolveProfile(flags);
24
+ if (!flags.force) {
25
+ const confirmed = await this.confirm(`Are you sure you want to reset your sandbox environment? All workspace data and drafts will be cleared.`);
26
+ if (!confirmed) {
27
+ this.log('Reset cancelled.');
28
+ return;
29
+ }
30
+ }
31
+ const apiUrl = `${profile.instance_origin}/api:meta/sandbox/reset`;
32
+ try {
33
+ const response = await this.verboseFetch(apiUrl, {
34
+ headers: {
35
+ accept: 'application/json',
36
+ Authorization: `Bearer ${profile.access_token}`,
37
+ 'Content-Type': 'application/json',
38
+ },
39
+ method: 'POST',
40
+ }, flags.verbose, profile.access_token);
41
+ if (!response.ok) {
42
+ const message = await this.parseApiError(response, 'API request failed');
43
+ this.error(message);
44
+ }
45
+ this.log('Sandbox environment has been reset.');
46
+ }
47
+ catch (error) {
48
+ if (error instanceof Error && 'oclif' in error)
49
+ throw error;
50
+ if (error instanceof Error) {
51
+ this.error(`Failed to reset sandbox environment: ${error.message}`);
52
+ }
53
+ else {
54
+ this.error(`Failed to reset sandbox environment: ${String(error)}`);
55
+ }
56
+ }
57
+ }
58
+ async confirm(message) {
59
+ const readline = await import('node:readline');
60
+ const rl = readline.createInterface({
61
+ input: process.stdin,
62
+ output: process.stdout,
63
+ });
64
+ return new Promise((resolve) => {
65
+ rl.question(`${message} (y/N) `, (answer) => {
66
+ rl.close();
67
+ resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
68
+ });
69
+ });
70
+ }
71
+ }
@@ -0,0 +1,14 @@
1
+ import BaseCommand from '../../../base-command.js';
2
+ export default class SandboxReview extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ 'url-only': import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ };
12
+ run(): Promise<void>;
13
+ private getFrontendUrl;
14
+ }
@@ -0,0 +1,94 @@
1
+ import { Flags } from '@oclif/core';
2
+ import open from 'open';
3
+ import BaseCommand from '../../../base-command.js';
4
+ export default class SandboxReview extends BaseCommand {
5
+ static description = 'Open your sandbox environment in the browser to review and promote changes';
6
+ static examples = [
7
+ `$ xano sandbox review
8
+ Opening browser...
9
+ Review session started!
10
+ `,
11
+ `$ xano sandbox review -u`,
12
+ `$ xano sandbox review -o json`,
13
+ ];
14
+ static flags = {
15
+ ...BaseCommand.baseFlags,
16
+ output: Flags.string({
17
+ char: 'o',
18
+ default: 'summary',
19
+ description: 'Output format',
20
+ options: ['summary', 'json'],
21
+ required: false,
22
+ }),
23
+ 'url-only': Flags.boolean({
24
+ char: 'u',
25
+ default: false,
26
+ description: 'Print the URL without opening the browser',
27
+ required: false,
28
+ }),
29
+ };
30
+ async run() {
31
+ const { flags } = await this.parse(SandboxReview);
32
+ const { profile } = this.resolveProfile(flags);
33
+ const apiUrl = `${profile.instance_origin}/api:meta/sandbox/impersonate`;
34
+ try {
35
+ const response = await this.verboseFetch(apiUrl, {
36
+ headers: {
37
+ accept: 'application/json',
38
+ Authorization: `Bearer ${profile.access_token}`,
39
+ },
40
+ method: 'GET',
41
+ }, flags.verbose, profile.access_token);
42
+ if (!response.ok) {
43
+ const message = await this.parseApiError(response, 'API request failed');
44
+ this.error(message);
45
+ }
46
+ const result = (await response.json());
47
+ if (!result._ti) {
48
+ this.error('No one-time token returned from impersonate API');
49
+ }
50
+ if (flags.output === 'json') {
51
+ this.log(JSON.stringify(result, null, 2));
52
+ }
53
+ else {
54
+ const frontendUrl = this.getFrontendUrl(profile.instance_origin);
55
+ const params = new URLSearchParams({
56
+ _ti: result._ti,
57
+ });
58
+ const impersonateUrl = `${frontendUrl}/impersonate?${params.toString()}`;
59
+ if (flags['url-only']) {
60
+ this.log(impersonateUrl);
61
+ }
62
+ else {
63
+ this.log('Opening browser...');
64
+ await open(impersonateUrl);
65
+ this.log('Review session started!');
66
+ }
67
+ }
68
+ process.exit(0);
69
+ }
70
+ catch (error) {
71
+ if (error instanceof Error && 'oclif' in error)
72
+ throw error;
73
+ if (error instanceof Error) {
74
+ this.error(`Failed to open sandbox review: ${error.message}`);
75
+ }
76
+ else {
77
+ this.error(`Failed to open sandbox review: ${String(error)}`);
78
+ }
79
+ }
80
+ }
81
+ getFrontendUrl(instanceOrigin) {
82
+ try {
83
+ const url = new URL(instanceOrigin);
84
+ if (url.hostname === 'localhost' || url.hostname === '127.0.0.1') {
85
+ url.port = '4200';
86
+ return url.origin;
87
+ }
88
+ }
89
+ catch {
90
+ // fall through
91
+ }
92
+ return instanceOrigin;
93
+ }
94
+ }
@@ -0,0 +1,14 @@
1
+ import BaseCommand from '../../../../base-command.js';
2
+ export default class SandboxUnitTestList extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ 'obj-type': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ };
13
+ run(): Promise<void>;
14
+ }