@metabase/cli 0.1.0 → 0.1.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 (187) hide show
  1. package/README.md +972 -57
  2. package/dist/add-collection--zwkmE1S.mjs +11 -0
  3. package/dist/add-collection-B1qe0D1U.mjs +54 -0
  4. package/dist/api-key-gzCbKDjL.mjs +13 -0
  5. package/dist/archive-CitmlD1e.mjs +39 -0
  6. package/dist/{archive-CsWeHXle.mjs → archive-CnhWegtR.mjs} +7 -4
  7. package/dist/archive-DQjBOXnx.mjs +44 -0
  8. package/dist/archive-Ni8-lQ1Y.mjs +44 -0
  9. package/dist/auth-BPjsrFxM.mjs +19 -0
  10. package/dist/{body-Dv9hQ0Qk.mjs → body-DRBgxS6-.mjs} +3 -2
  11. package/dist/{branches-BujtceGr.mjs → branches-C5Jcw8wu.mjs} +8 -6
  12. package/dist/cancel-Ca3r7Y6v.mjs +56 -0
  13. package/dist/{cancel-task-CT2xUMRg.mjs → cancel-task-C1-8vDKS.mjs} +9 -7
  14. package/dist/card-BGAy3eIb.mjs +20 -0
  15. package/dist/{card-CsXk8T6A.mjs → card-CAEZWixN.mjs} +34 -15
  16. package/dist/cards-CILfMPUP.mjs +37 -0
  17. package/dist/cli.mjs +33 -14
  18. package/dist/collection-B3sPXRLs.mjs +163 -0
  19. package/dist/collection-D8cnCB98.mjs +19 -0
  20. package/dist/create-3Z6rm-4O.mjs +44 -0
  21. package/dist/create-BsY5RrVY.mjs +44 -0
  22. package/dist/create-C4OCclBD.mjs +48 -0
  23. package/dist/create-COsD7Vzm.mjs +48 -0
  24. package/dist/create-CP8ou91U.mjs +125 -0
  25. package/dist/create-CeIi_QLj.mjs +66 -0
  26. package/dist/create-CqNw6PmR.mjs +50 -0
  27. package/dist/create-DE_5NrFy.mjs +48 -0
  28. package/dist/{create-B8ektf-R.mjs → create-MEhhhgMC.mjs} +8 -6
  29. package/dist/create-QxDmleKJ.mjs +48 -0
  30. package/dist/{create-branch-goZBTNnr.mjs → create-branch-CKMYaAHk.mjs} +9 -7
  31. package/dist/credentials-CwRKvdP2.mjs +85 -0
  32. package/dist/{current-task-DBjRNCFq.mjs → current-task-Dutjys16.mjs} +9 -7
  33. package/dist/dashboard-B4fVp392.mjs +20 -0
  34. package/dist/dashboard-CnMD04PQ.mjs +163 -0
  35. package/dist/database-BMTb0CzV.mjs +17 -0
  36. package/dist/database-Dvkfy3JM.mjs +51 -0
  37. package/dist/db-ACuuaEok.mjs +22 -0
  38. package/dist/{delete-8vGU35r3.mjs → delete-BMQZuVXZ.mjs} +7 -5
  39. package/dist/{delete-B27KLF5X.mjs → delete-BvcA4jPj.mjs} +7 -5
  40. package/dist/{delete-runtime-Byr60cR3.mjs → delete-runtime-BMzvfj_B.mjs} +4 -4
  41. package/dist/{delete-table-BNaJ_gA4.mjs → delete-table-DUPjHKk4.mjs} +7 -5
  42. package/dist/deprovision-Bsc1S15j.mjs +61 -0
  43. package/dist/{dirty-aNUuph4I.mjs → dirty-CXcdoUhY.mjs} +8 -6
  44. package/dist/docker-D-ieBsP7.mjs +612 -0
  45. package/dist/eid-pvOsEMPZ.mjs +13 -0
  46. package/dist/{export-QDkuuzSE.mjs → export-BjGhLEOi.mjs} +30 -23
  47. package/dist/field-BI2bt8e9.mjs +18 -0
  48. package/dist/field-DciLbuv-.mjs +276 -0
  49. package/dist/fields-Do8HHm_T.mjs +38 -0
  50. package/dist/flag-pair-DtR1AiBQ.mjs +17 -0
  51. package/dist/{get-BGBIzMKY.mjs → get-BGFGWkH0.mjs} +6 -4
  52. package/dist/get-BmE_VHdl.mjs +36 -0
  53. package/dist/{get-DI_IJvgk.mjs → get-C7sshmqF.mjs} +6 -4
  54. package/dist/get-CObKBj2J.mjs +36 -0
  55. package/dist/get-Cq5U_Eep.mjs +40 -0
  56. package/dist/get-D4GUJBiX.mjs +41 -0
  57. package/dist/{get-COXHplHP.mjs → get-DFrsi77F.mjs} +7 -5
  58. package/dist/get-DczxeETg.mjs +53 -0
  59. package/dist/{get-Cl8-IauC.mjs → get-DeQa3ThJ.mjs} +7 -4
  60. package/dist/get-DhZ_dGUb.mjs +36 -0
  61. package/dist/{get-i6LWOByV.mjs → get-DzCVafyO.mjs} +6 -4
  62. package/dist/get-YCnVqq-z.mjs +49 -0
  63. package/dist/get-run-CTyW29s3.mjs +36 -0
  64. package/dist/git-sync-BOmT8HEU.mjs +28 -0
  65. package/dist/{has-remote-changes-hjKoQuRy.mjs → has-remote-changes-xX8vMVsX.mjs} +8 -6
  66. package/dist/{import-HJsSKRYx.mjs → import-CaAUNtXz.mjs} +11 -9
  67. package/dist/{input-Dojr-RTw.mjs → input-ikCiip6x.mjs} +2 -1
  68. package/dist/is-dirty-CPu-xqkW.mjs +10 -0
  69. package/dist/{is-dirty-1Qy7hiHB.mjs → is-dirty-mgxEwEk4.mjs} +5 -4
  70. package/dist/items-Cg67tdto.mjs +77 -0
  71. package/dist/{key-DBxPSFwi.mjs → key-NDEARu2L.mjs} +1 -1
  72. package/dist/{license-MoWse3ZI.mjs → license-CwKzVMD0.mjs} +3 -3
  73. package/dist/list-BqdNQ1nU.mjs +47 -0
  74. package/dist/list-BwGdD45N.mjs +32 -0
  75. package/dist/list-CfOVsAZz.mjs +55 -0
  76. package/dist/list-CpyNn1Zn.mjs +32 -0
  77. package/dist/list-CwwOoGLK.mjs +40 -0
  78. package/dist/{list-C_PRdL5e.mjs → list-DD8CQx8l.mjs} +7 -5
  79. package/dist/{list-Bk6RsbJl.mjs → list-DL-RWpIE.mjs} +5 -3
  80. package/dist/list-DLlq3FyS.mjs +61 -0
  81. package/dist/list-DdQ4jmUQ.mjs +52 -0
  82. package/dist/{list-C4Ajrw8f.mjs → list-DshbLoqR.mjs} +6 -3
  83. package/dist/{list-C8tdLOH5.mjs → list-DzTMpoBs.mjs} +5 -3
  84. package/dist/list-JgRtCzz3.mjs +32 -0
  85. package/dist/{list-CWt3fqrZ.mjs → list-WzgJcwB5.mjs} +5 -3
  86. package/dist/{login-C9WTwNn6.mjs → login-DJnmR2wX.mjs} +14 -5
  87. package/dist/{logout-oLszGCOg.mjs → logout-BMe_1Zp8.mjs} +7 -6
  88. package/dist/logs-CQxKJ3HG.mjs +58 -0
  89. package/dist/{manifest-CAdjQYH8.mjs → manifest-Dv5B9Blc.mjs} +3 -7
  90. package/dist/measure-BEQfnLdN.mjs +67 -0
  91. package/dist/measure-BGyYbtqO.mjs +19 -0
  92. package/dist/metadata-CLIALntn.mjs +37 -0
  93. package/dist/metadata-T-fNUWg_.mjs +38 -0
  94. package/dist/{package-BGfw4ZWJ.mjs → package-DBsS7a5x.mjs} +7 -1
  95. package/dist/paginate-CTSfuYiF.mjs +49 -0
  96. package/dist/parse-id-BUOZQqjp.mjs +12 -0
  97. package/dist/parse-ref-DGvh4aDn.mjs +17 -0
  98. package/dist/parse-schemas-BnW4T1_I.mjs +12 -0
  99. package/dist/{poll-ILanYysl.mjs → poll-DMmmZWvi.mjs} +2 -1
  100. package/dist/{poll-task-DbpsiQhl.mjs → poll-task-2Ckiwp8U.mjs} +8 -7
  101. package/dist/predicates-DiIiS3k7.mjs +153 -0
  102. package/dist/preflight-CC_g6EWU.mjs +91 -0
  103. package/dist/{prompt-DpT8yAVy.mjs → prompt-Bf3DQ-qE.mjs} +1 -1
  104. package/dist/provision-BUgWJWAV.mjs +77 -0
  105. package/dist/ps-BUNHygf-.mjs +10 -0
  106. package/dist/ps-Yv0JjLVN.mjs +78 -0
  107. package/dist/{query-PihYi-UZ.mjs → query-CzfbuG8a.mjs} +38 -13
  108. package/dist/query-UIebHmbT.mjs +90 -0
  109. package/dist/remove-BAUbcwuF.mjs +98 -0
  110. package/dist/{remove-B2hVYn1v.mjs → remove-CN2PNGTR.mjs} +6 -5
  111. package/dist/remove-collection-C6NxEh53.mjs +38 -0
  112. package/dist/render-DXv-D6fU.mjs +182 -0
  113. package/dist/rescan-values-CcB4F9qa.mjs +43 -0
  114. package/dist/revision-message-flag-CWQbKhdl.mjs +11 -0
  115. package/dist/{run-C2so6Qp6.mjs → run-BjXZtu_6.mjs} +27 -36
  116. package/dist/runs-CXx7l1NY.mjs +54 -0
  117. package/dist/{runtime-C9CEZhcn.mjs → runtime-D7jihh81.mjs} +425 -442
  118. package/dist/schema-tables-BCJT2DM_.mjs +45 -0
  119. package/dist/schemas-DlNpbn4H.mjs +47 -0
  120. package/dist/{search-CopOytXY.mjs → search-Dt-6mdHZ.mjs} +6 -19
  121. package/dist/segment-BMrUBz94.mjs +70 -0
  122. package/dist/segment-C52QNnSs.mjs +19 -0
  123. package/dist/{set-BcF7M1GQ.mjs → set-DCESWpi3.mjs} +6 -4
  124. package/dist/{set-CbibegpA.mjs → set-L7cuHjVZ.mjs} +8 -6
  125. package/dist/{setting-U3NtBMFo.mjs → setting-DysGAuYS.mjs} +3 -3
  126. package/dist/setup-_ypJDPAY.mjs +71 -0
  127. package/dist/snippet-Dw0Sjzkr.mjs +64 -0
  128. package/dist/snippet-vb3G9R8a.mjs +19 -0
  129. package/dist/start-BokXnb0V.mjs +350 -0
  130. package/dist/{stash-DOBbYozC.mjs → stash-CaGX6PfX.mjs} +9 -7
  131. package/dist/{status-Buf1ZbNR.mjs → status-BaX9vedb.mjs} +10 -8
  132. package/dist/{status-CUcs8XBH.mjs → status-CyecXzN4.mjs} +4 -2
  133. package/dist/{status-D1F5XHae.mjs → status-RpVyPEty.mjs} +4 -2
  134. package/dist/stop-BRuF_Cg1.mjs +81 -0
  135. package/dist/summary-CpEOiOlZ.mjs +41 -0
  136. package/dist/sync-schema-4Cl4h8Jn.mjs +43 -0
  137. package/dist/table-BeMWuvzO.mjs +19 -0
  138. package/dist/{table-Cfk7oSvw.mjs → table-jljEqZ0R.mjs} +22 -9
  139. package/dist/transform-DwRc-w6y.mjs +24 -0
  140. package/dist/{transform-B5uRpg1G.mjs → transform-IEX4Mx3X.mjs} +56 -2
  141. package/dist/transform-job-BigWrctt.mjs +19 -0
  142. package/dist/{transform-job-C7QXWTVE.mjs → transform-job-Csr86muI.mjs} +7 -0
  143. package/dist/translate-DqLlXXUx.mjs +111 -0
  144. package/dist/tree-BT24nkLM.mjs +32 -0
  145. package/dist/update-BCXKQi2n.mjs +52 -0
  146. package/dist/{update-CL8tRbxr.mjs → update-BXbLmC2b.mjs} +9 -7
  147. package/dist/update-C1Frz9GR.mjs +52 -0
  148. package/dist/update-C5goGhNr.mjs +56 -0
  149. package/dist/update-CCOyB0iT.mjs +73 -0
  150. package/dist/update-D04NMueX.mjs +59 -0
  151. package/dist/update-D6WVtNV1.mjs +57 -0
  152. package/dist/update-DFR46LsB.mjs +56 -0
  153. package/dist/update-DyLItrpV.mjs +56 -0
  154. package/dist/update-dashcard-av0_PYeg.mjs +71 -0
  155. package/dist/update-mrgvQF4i.mjs +51 -0
  156. package/dist/url-x4wn_l3k.mjs +54 -0
  157. package/dist/uuid-BZHbti8B.mjs +47 -0
  158. package/dist/validate-DCYx6jdL.mjs +1496 -0
  159. package/dist/validate-query-B07oGG4K.mjs +37 -0
  160. package/dist/values-Be6i0Fs9.mjs +36 -0
  161. package/dist/{wait-Bugr9eXD.mjs → wait-BMqQD8k_.mjs} +10 -8
  162. package/dist/wait-CWizX_sR.mjs +19 -0
  163. package/dist/wait-flags-DO3ar2tf.mjs +35 -0
  164. package/dist/workspace-CG1xyJ86.mjs +24 -0
  165. package/dist/workspace-DVuqKJGG.mjs +72 -0
  166. package/dist/workspace-credentials-B6BL-X0d.mjs +139 -0
  167. package/package.json +7 -1
  168. package/dist/auth-BF7IjZIH.mjs +0 -18
  169. package/dist/card-_Ta7zdYe.mjs +0 -19
  170. package/dist/create-CI2Cunq5.mjs +0 -38
  171. package/dist/create-DdbU3TLX.mjs +0 -42
  172. package/dist/database-PA9Goi25.mjs +0 -33
  173. package/dist/db-DMghzgb6.mjs +0 -17
  174. package/dist/field-C8IVs6rp.mjs +0 -76
  175. package/dist/field-DaYo_90x.mjs +0 -13
  176. package/dist/get-Cwpj7lDe.mjs +0 -35
  177. package/dist/get-Dh_acl8q.mjs +0 -34
  178. package/dist/is-dirty-DpKn9HJp.mjs +0 -8
  179. package/dist/list-CBSBHtK-.mjs +0 -38
  180. package/dist/parse-id-BhmmfyCP.mjs +0 -14
  181. package/dist/sync-BPyGXfUk.mjs +0 -26
  182. package/dist/table-D7nJt7JO.mjs +0 -16
  183. package/dist/transform-UbyewMxY.mjs +0 -21
  184. package/dist/transform-job-CrYkr-Ma.mjs +0 -19
  185. package/dist/update-DU2oU2j-.mjs +0 -49
  186. /package/dist/{body-flags-BUA9XV1u.mjs → body-flags-BK7J6Daz.mjs} +0 -0
  187. /package/dist/{setting-26ckqHAP.mjs → setting-CTaAeMci.mjs} +0 -0
@@ -0,0 +1,612 @@
1
+ import { ConfigError, MetabaseError, errorMessage, isNotFoundError } from "./predicates-DiIiS3k7.mjs";
2
+ import { parseJson } from "./runtime-D7jihh81.mjs";
3
+ import { pollUntil } from "./poll-DMmmZWvi.mjs";
4
+ import { z } from "zod";
5
+ import { spawn } from "node:child_process";
6
+
7
+ //#region src/runtime/process.ts
8
+ var ProcessNotFoundError = class extends Error {
9
+ command;
10
+ constructor(command) {
11
+ super(`command not found: ${command}`);
12
+ this.name = "ProcessNotFoundError";
13
+ this.command = command;
14
+ }
15
+ };
16
+ var ProcessTimeoutError = class extends Error {
17
+ command;
18
+ timeoutMs;
19
+ constructor(command, timeoutMs) {
20
+ super(`command timed out after ${timeoutMs}ms: ${command}`);
21
+ this.name = "ProcessTimeoutError";
22
+ this.command = command;
23
+ this.timeoutMs = timeoutMs;
24
+ }
25
+ };
26
+ function spawnAndCollect(command, args, options) {
27
+ const timeoutSignal = options.timeoutMs !== void 0 && options.timeoutMs > 0 ? AbortSignal.timeout(options.timeoutMs) : void 0;
28
+ return new Promise((resolve, reject) => {
29
+ const child = spawn(command, args, {
30
+ stdio: [
31
+ "pipe",
32
+ "pipe",
33
+ "pipe"
34
+ ],
35
+ env: options.env ?? process.env,
36
+ ...options.cwd !== void 0 ? { cwd: options.cwd } : {},
37
+ ...timeoutSignal !== void 0 ? {
38
+ signal: timeoutSignal,
39
+ killSignal: "SIGKILL"
40
+ } : {}
41
+ });
42
+ const stdoutChunks = [];
43
+ let stderr = "";
44
+ child.stdout.on("data", (chunk) => {
45
+ stdoutChunks.push(chunk);
46
+ });
47
+ child.stderr.on("data", (chunk) => {
48
+ stderr += chunk.toString("utf8");
49
+ });
50
+ child.on("error", (error) => {
51
+ if (isNotFoundError(error)) {
52
+ reject(new ProcessNotFoundError(command));
53
+ return;
54
+ }
55
+ if (timeoutSignal?.aborted) {
56
+ reject(new ProcessTimeoutError(command, options.timeoutMs ?? 0));
57
+ return;
58
+ }
59
+ reject(error);
60
+ });
61
+ child.on("close", (code) => {
62
+ if (timeoutSignal?.aborted) {
63
+ reject(new ProcessTimeoutError(command, options.timeoutMs ?? 0));
64
+ return;
65
+ }
66
+ const stdoutBuffer = Buffer.concat(stdoutChunks);
67
+ resolve({
68
+ stdout: new Uint8Array(stdoutBuffer.buffer, stdoutBuffer.byteOffset, stdoutBuffer.length),
69
+ stderr,
70
+ exitCode: code
71
+ });
72
+ });
73
+ if (options.stdin === void 0) child.stdin.end();
74
+ else child.stdin.end(options.stdin);
75
+ });
76
+ }
77
+ const stdoutDecoder = new TextDecoder("utf-8");
78
+ async function runProcess(command, args, options = {}) {
79
+ const result = await spawnAndCollect(command, args, options);
80
+ return {
81
+ stdout: stdoutDecoder.decode(result.stdout),
82
+ stderr: result.stderr,
83
+ exitCode: result.exitCode
84
+ };
85
+ }
86
+ function runProcessBinary(command, args, options = {}) {
87
+ return spawnAndCollect(command, args, options);
88
+ }
89
+ function streamProcess(command, args) {
90
+ return new Promise((resolve, reject) => {
91
+ const child = spawn(command, args, { stdio: "inherit" });
92
+ child.on("error", (error) => {
93
+ if (isNotFoundError(error)) {
94
+ reject(new ProcessNotFoundError(command));
95
+ return;
96
+ }
97
+ reject(error);
98
+ });
99
+ child.on("close", (code) => resolve(code));
100
+ });
101
+ }
102
+
103
+ //#endregion
104
+ //#region src/runtime/tar.ts
105
+ const BLOCK_SIZE = 512;
106
+ const REGULAR_FILE_MODE = 420;
107
+ const DIR_MODE = 493;
108
+ const NAME_FIELD_LENGTH = 100;
109
+ const TYPE_FLAG_REGULAR = "0";
110
+ const TYPE_FLAG_DIRECTORY = "5";
111
+ const textEncoder = new TextEncoder();
112
+ function toBytes(content) {
113
+ return typeof content === "string" ? textEncoder.encode(content) : content;
114
+ }
115
+ function writeOctal(target, offset, length, value) {
116
+ const digits = length - 1;
117
+ const octal = Math.trunc(value).toString(8).padStart(digits, "0");
118
+ if (octal.length > digits) throw new Error(`tar value ${value} exceeds octal field width ${digits}`);
119
+ for (let i = 0; i < digits; i++) target[offset + i] = octal.charCodeAt(i);
120
+ target[offset + length - 1] = 0;
121
+ }
122
+ function writeString(target, offset, length, value) {
123
+ const bytes = textEncoder.encode(value);
124
+ if (bytes.length > length) throw new Error(`tar string field of length ${length} cannot hold ${bytes.length} bytes`);
125
+ target.set(bytes, offset);
126
+ }
127
+ function writeHeader(out, offset, name, size, mode, mtime, typeFlag) {
128
+ if (textEncoder.encode(name).length > NAME_FIELD_LENGTH) throw new Error(`tar entry name exceeds ${NAME_FIELD_LENGTH} bytes: ${name}`);
129
+ writeString(out, offset, NAME_FIELD_LENGTH, name);
130
+ writeOctal(out, offset + 100, 8, mode & 4095);
131
+ writeOctal(out, offset + 108, 8, 0);
132
+ writeOctal(out, offset + 116, 8, 0);
133
+ writeOctal(out, offset + 124, 12, size);
134
+ writeOctal(out, offset + 136, 12, mtime);
135
+ for (let i = 148; i < 156; i++) out[offset + i] = 32;
136
+ out[offset + 156] = typeFlag.charCodeAt(0);
137
+ writeString(out, offset + 257, 6, "ustar");
138
+ out[offset + 263] = 48;
139
+ out[offset + 264] = 48;
140
+ let sum = 0;
141
+ for (const byte of out.subarray(offset, offset + BLOCK_SIZE)) sum += byte;
142
+ const sumOctal = sum.toString(8).padStart(6, "0");
143
+ for (let i = 0; i < 6; i++) out[offset + 148 + i] = sumOctal.charCodeAt(i);
144
+ out[offset + 154] = 0;
145
+ out[offset + 155] = 32;
146
+ }
147
+ function paddedSize(size) {
148
+ const remainder = size % BLOCK_SIZE;
149
+ return remainder === 0 ? 0 : BLOCK_SIZE - remainder;
150
+ }
151
+ function resolveEntry(entry, fallbackMtime) {
152
+ if (entry.type === "directory") {
153
+ const name = entry.name.endsWith("/") ? entry.name : `${entry.name}/`;
154
+ return {
155
+ name,
156
+ mode: entry.mode ?? DIR_MODE,
157
+ mtime: entry.mtime ?? fallbackMtime,
158
+ typeFlag: TYPE_FLAG_DIRECTORY,
159
+ content: null
160
+ };
161
+ }
162
+ return {
163
+ name: entry.name,
164
+ mode: entry.mode ?? REGULAR_FILE_MODE,
165
+ mtime: entry.mtime ?? fallbackMtime,
166
+ typeFlag: TYPE_FLAG_REGULAR,
167
+ content: toBytes(entry.content)
168
+ };
169
+ }
170
+ var TarParseError = class extends Error {
171
+ constructor(message) {
172
+ super(message);
173
+ this.name = "TarParseError";
174
+ }
175
+ };
176
+ const SIZE_FIELD_OFFSET = 124;
177
+ const SIZE_FIELD_LENGTH = 12;
178
+ const textDecoder = new TextDecoder("utf-8");
179
+ function readNullTerminatedString(buffer, offset, length) {
180
+ const slice = buffer.subarray(offset, offset + length);
181
+ const nul = slice.indexOf(0);
182
+ const end = nul === -1 ? slice.length : nul;
183
+ return textDecoder.decode(slice.subarray(0, end));
184
+ }
185
+ function extractSingleFileFromTar(tar, expectedNameSuffix) {
186
+ if (tar.length < BLOCK_SIZE) throw new TarParseError(`tar is shorter than one block: ${tar.length} bytes`);
187
+ const nameField = readNullTerminatedString(tar, 0, NAME_FIELD_LENGTH);
188
+ if (!nameField.endsWith(expectedNameSuffix)) throw new TarParseError(`unexpected tar entry ${JSON.stringify(nameField)}, expected to end with ${JSON.stringify(expectedNameSuffix)}`);
189
+ const sizeField = readNullTerminatedString(tar, SIZE_FIELD_OFFSET, SIZE_FIELD_LENGTH).trim();
190
+ const size = Number.parseInt(sizeField, 8);
191
+ if (!Number.isFinite(size) || size < 0) throw new TarParseError(`tar header has invalid size field: ${JSON.stringify(sizeField)}`);
192
+ if (tar.length < BLOCK_SIZE + size) throw new TarParseError(`tar truncated: header reports ${size} content bytes but only ${tar.length - BLOCK_SIZE} bytes follow`);
193
+ return tar.subarray(BLOCK_SIZE, BLOCK_SIZE + size);
194
+ }
195
+ function buildTar(entries) {
196
+ const fallbackMtime = Math.floor(Date.now() / 1e3);
197
+ const resolved = entries.map((entry) => resolveEntry(entry, fallbackMtime));
198
+ let total = BLOCK_SIZE * 2;
199
+ for (const entry of resolved) {
200
+ total += BLOCK_SIZE;
201
+ if (entry.content !== null) total += entry.content.length + paddedSize(entry.content.length);
202
+ }
203
+ const out = new Uint8Array(total);
204
+ let offset = 0;
205
+ for (const entry of resolved) {
206
+ const size = entry.content?.length ?? 0;
207
+ writeHeader(out, offset, entry.name, size, entry.mode, entry.mtime, entry.typeFlag);
208
+ offset += BLOCK_SIZE;
209
+ if (entry.content !== null) {
210
+ out.set(entry.content, offset);
211
+ offset += entry.content.length + paddedSize(entry.content.length);
212
+ }
213
+ }
214
+ return out;
215
+ }
216
+
217
+ //#endregion
218
+ //#region src/core/docker.ts
219
+ const DOCKER_BIN = "docker";
220
+ const CONTAINER_NAME_PREFIX = "metabase-workspace-";
221
+ const VOLUME_NAME_SUFFIX = "-appdb";
222
+ const LABEL_ID = "com.metabase.workspace.id";
223
+ const LABEL_NAME = "com.metabase.workspace.name";
224
+ const LABEL_PROFILE = "com.metabase.workspace.profile";
225
+ const LABEL_PARENT = "com.metabase.workspace.parent";
226
+ const LABEL_IMAGE = "com.metabase.workspace.image";
227
+ const LABEL_HOST_PORT = "com.metabase.workspace.host-port";
228
+ const WORKSPACE_CONTAINER_PORT = 3e3;
229
+ const CONTAINER_CONFIG_DIR = "/mw-config";
230
+ const CONTAINER_CONFIG_DIR_BASENAME = CONTAINER_CONFIG_DIR.replace(/^\//, "");
231
+ const CONTAINER_APP_DB_DIR = "/metabase-app-db";
232
+ const CONTAINER_REPO_DIR = "/mnt/repo";
233
+ const CONFIG_FILENAME = "config.yml";
234
+ const CREDENTIALS_FILENAME = "credentials.json";
235
+ const CONFIG_CONSUMED_MARKER = "Loaded workspace";
236
+ const INIT_FAILED_MARKER = "Metabase Initialization FAILED";
237
+ const CONFIG_CONSUMED_LOG_LINES = 500;
238
+ const CONFIG_CONSUMED_INTERVAL_MS = 1e3;
239
+ const CONFIG_CONSUMED_MAX_INTERVAL_MS = 3e3;
240
+ const INIT_FAILED_TAIL_LINES = 25;
241
+ const BUNDLE_FILE_MODE = 420;
242
+ const NO_SUCH_CONTAINER_PATTERN = /no such container/i;
243
+ const NO_SUCH_VOLUME_PATTERN = /no such volume/i;
244
+ const CONTAINER_STATES = [
245
+ "running",
246
+ "exited",
247
+ "created",
248
+ "paused",
249
+ "restarting",
250
+ "removing",
251
+ "dead"
252
+ ];
253
+ var DockerError = class extends MetabaseError {
254
+ category = "docker";
255
+ isRetryable = false;
256
+ exitCode = 1;
257
+ developerDetail;
258
+ stderr;
259
+ constructor(message, dockerExitCode, stderr) {
260
+ super(message);
261
+ this.name = "DockerError";
262
+ this.developerDetail = {
263
+ dockerExitCode,
264
+ stderr
265
+ };
266
+ this.stderr = stderr;
267
+ }
268
+ get userMessage() {
269
+ const trimmed = this.stderr.trim();
270
+ if (trimmed === "") return this.message;
271
+ return `${this.message}\n${indentLines(trimmed)}`;
272
+ }
273
+ };
274
+ function indentLines(text) {
275
+ return text.split("\n").map((line) => ` ${line}`).join("\n");
276
+ }
277
+ var DockerNotInstalledError = class extends Error {
278
+ constructor() {
279
+ super("docker is not installed or not on PATH — install Docker Desktop / OrbStack / Colima and retry");
280
+ this.name = "DockerNotInstalledError";
281
+ }
282
+ };
283
+ var DockerNotRunningError = class extends Error {
284
+ stderr;
285
+ constructor(stderr) {
286
+ super("docker is installed but the daemon is not responding — start Docker and retry");
287
+ this.name = "DockerNotRunningError";
288
+ this.stderr = stderr;
289
+ }
290
+ };
291
+ const ContainerSummarySchema = z.object({
292
+ ID: z.string(),
293
+ Names: z.string(),
294
+ State: z.string(),
295
+ Status: z.string(),
296
+ Image: z.string(),
297
+ Labels: z.string(),
298
+ Ports: z.string()
299
+ });
300
+ function containerNameFor(workspaceId) {
301
+ return `${CONTAINER_NAME_PREFIX}${workspaceId}`;
302
+ }
303
+ function volumeNameFor(workspaceId) {
304
+ return `${CONTAINER_NAME_PREFIX}${workspaceId}${VOLUME_NAME_SUFFIX}`;
305
+ }
306
+ async function dockerExec(args, options = {}) {
307
+ try {
308
+ return await runProcess(DOCKER_BIN, args, options);
309
+ } catch (error) {
310
+ if (error instanceof ProcessNotFoundError) throw new DockerNotInstalledError();
311
+ throw error;
312
+ }
313
+ }
314
+ async function runDocker(args, failureMessage, options = {}) {
315
+ const { ignorePattern,...execOptions } = options;
316
+ const result = await dockerExec(args, execOptions);
317
+ if (result.exitCode === 0) return result;
318
+ if (ignorePattern?.test(result.stderr)) return result;
319
+ throw new DockerError(failureMessage, result.exitCode, result.stderr);
320
+ }
321
+ async function checkDockerReady() {
322
+ let result;
323
+ try {
324
+ result = await runProcess(DOCKER_BIN, [
325
+ "version",
326
+ "--format",
327
+ "{{.Server.Version}}"
328
+ ]);
329
+ } catch (error) {
330
+ if (error instanceof ProcessNotFoundError) throw new DockerNotInstalledError();
331
+ throw error;
332
+ }
333
+ if (result.exitCode !== 0) throw new DockerNotRunningError(result.stderr);
334
+ }
335
+ async function pullImage(image) {
336
+ const code = await streamProcess(DOCKER_BIN, ["pull", image]);
337
+ if (code !== 0) throw new DockerError(`docker pull ${image} failed`, code, "");
338
+ }
339
+ async function containerLifecycleStatus(containerName) {
340
+ const result = await runDocker([
341
+ "ps",
342
+ "-a",
343
+ "--filter",
344
+ `name=^${containerName}$`,
345
+ "--format",
346
+ "{{.State}}"
347
+ ], "docker ps failed");
348
+ const trimmed = result.stdout.trim();
349
+ if (trimmed.length === 0) return "missing";
350
+ return parseContainerState(trimmed);
351
+ }
352
+ function parseContainerState(raw) {
353
+ const lower = raw.toLowerCase();
354
+ for (const known of CONTAINER_STATES) if (known === lower) return known;
355
+ throw new DockerError(`unknown docker container state: ${JSON.stringify(raw)}`, null, "");
356
+ }
357
+ async function runWorkspaceContainer(spec) {
358
+ const containerName = containerNameFor(spec.workspaceId);
359
+ await createContainer({
360
+ containerName,
361
+ image: spec.image,
362
+ port: {
363
+ hostPort: spec.hostPort,
364
+ containerPort: WORKSPACE_CONTAINER_PORT
365
+ },
366
+ namedVolumes: [{
367
+ volume: volumeNameFor(spec.workspaceId),
368
+ container: CONTAINER_APP_DB_DIR
369
+ }],
370
+ bindMounts: spec.bindMounts,
371
+ envVars: workspaceContainerEnv(spec),
372
+ labels: workspaceContainerLabels(spec)
373
+ });
374
+ try {
375
+ await copyTarToContainer(containerName, "/", buildBootBundleTar(spec));
376
+ await startContainer(containerName);
377
+ } catch (error) {
378
+ await removeContainer(containerName).catch(() => void 0);
379
+ throw error;
380
+ }
381
+ }
382
+ async function scrubContainerConfig(workspaceId) {
383
+ const containerName = containerNameFor(workspaceId);
384
+ await runDocker([
385
+ "exec",
386
+ containerName,
387
+ "rm",
388
+ "-f",
389
+ `${CONTAINER_CONFIG_DIR}/${CONFIG_FILENAME}`
390
+ ], `docker exec rm config.yml failed for ${containerName}`);
391
+ }
392
+ async function waitForConfigConsumed(workspaceId, timeoutMs) {
393
+ const containerName = containerNameFor(workspaceId);
394
+ await pollUntil(async () => {
395
+ const result = await dockerExec([
396
+ "logs",
397
+ "--tail",
398
+ String(CONFIG_CONSUMED_LOG_LINES),
399
+ containerName
400
+ ]);
401
+ const haystack = `${result.stdout}\n${result.stderr}`;
402
+ if (haystack.includes(INIT_FAILED_MARKER)) {
403
+ const tail = haystack.split("\n").slice(-INIT_FAILED_TAIL_LINES).join("\n");
404
+ throw new DockerError(`workspace ${workspaceId} container failed Metabase initialization`, null, tail);
405
+ }
406
+ return haystack.includes(CONFIG_CONSUMED_MARKER);
407
+ }, (consumed) => consumed, {
408
+ intervalMs: CONFIG_CONSUMED_INTERVAL_MS,
409
+ maxIntervalMs: CONFIG_CONSUMED_MAX_INTERVAL_MS,
410
+ backoff: "exponential",
411
+ timeoutMs
412
+ });
413
+ }
414
+ async function readContainerCredentialsFile(workspaceId) {
415
+ const containerName = containerNameFor(workspaceId);
416
+ const result = await runProcessBinary(DOCKER_BIN, [
417
+ "cp",
418
+ `${containerName}:${CONTAINER_CONFIG_DIR}/${CREDENTIALS_FILENAME}`,
419
+ "-"
420
+ ]);
421
+ if (result.exitCode !== 0) {
422
+ if (NO_SUCH_CONTAINER_PATTERN.test(result.stderr)) throw new DockerError(`no container for workspace ${workspaceId}`, result.exitCode, result.stderr);
423
+ throw new DockerError(`docker cp ${CREDENTIALS_FILENAME} from ${containerName} failed`, result.exitCode, result.stderr);
424
+ }
425
+ return extractSingleFileFromTar(result.stdout, CREDENTIALS_FILENAME);
426
+ }
427
+ function buildBootBundleTar(spec) {
428
+ const entries = [
429
+ {
430
+ type: "directory",
431
+ name: CONTAINER_CONFIG_DIR_BASENAME
432
+ },
433
+ {
434
+ type: "file",
435
+ name: `${CONTAINER_CONFIG_DIR_BASENAME}/${CONFIG_FILENAME}`,
436
+ content: spec.configYaml,
437
+ mode: BUNDLE_FILE_MODE
438
+ },
439
+ {
440
+ type: "file",
441
+ name: `${CONTAINER_CONFIG_DIR_BASENAME}/${CREDENTIALS_FILENAME}`,
442
+ content: spec.credentialsJson,
443
+ mode: BUNDLE_FILE_MODE
444
+ }
445
+ ];
446
+ return buildTar(entries);
447
+ }
448
+ function workspaceContainerLabels(spec) {
449
+ return {
450
+ [LABEL_ID]: String(spec.workspaceId),
451
+ [LABEL_NAME]: spec.workspaceName,
452
+ [LABEL_PROFILE]: spec.profile,
453
+ [LABEL_PARENT]: spec.parentUrl,
454
+ [LABEL_IMAGE]: spec.image,
455
+ [LABEL_HOST_PORT]: String(spec.hostPort)
456
+ };
457
+ }
458
+ function workspaceContainerEnv(spec) {
459
+ return {
460
+ MB_CONFIG_FILE_PATH: `${CONTAINER_CONFIG_DIR}/${CONFIG_FILENAME}`,
461
+ MB_PREMIUM_EMBEDDING_TOKEN: spec.licenseToken,
462
+ MB_DB_FILE: `${CONTAINER_APP_DB_DIR}/metabase.db`,
463
+ MB_DISABLE_SCHEDULER: "true",
464
+ JAVA_OPTS: "-Xmx2g"
465
+ };
466
+ }
467
+ async function createContainer(options) {
468
+ const args = [
469
+ "create",
470
+ "--name",
471
+ options.containerName,
472
+ "-p",
473
+ `${options.port.hostPort}:${options.port.containerPort}`
474
+ ];
475
+ for (const [key, value] of Object.entries(options.labels)) args.push("--label", `${key}=${value}`);
476
+ for (const mount of options.namedVolumes) args.push("-v", `${mount.volume}:${mount.container}`);
477
+ for (const bind of options.bindMounts) args.push("-v", `${bind.hostPath}:${bind.containerPath}:${bind.readOnly ? "ro" : "rw"}`);
478
+ for (const key of Object.keys(options.envVars)) args.push("-e", key);
479
+ args.push(options.image);
480
+ const env = {
481
+ ...process.env,
482
+ ...options.envVars
483
+ };
484
+ await runDocker(args, `docker create failed for ${options.containerName}`, { env });
485
+ }
486
+ async function copyTarToContainer(containerName, destPath, tarBytes) {
487
+ await runDocker([
488
+ "cp",
489
+ "-",
490
+ `${containerName}:${destPath}`
491
+ ], `docker cp into ${containerName}:${destPath} failed`, { stdin: tarBytes });
492
+ }
493
+ async function startContainer(containerName) {
494
+ await runDocker(["start", containerName], `docker start failed for ${containerName}`);
495
+ }
496
+ async function stopContainer(containerName) {
497
+ await runDocker(["stop", containerName], `docker stop ${containerName} failed`, { ignorePattern: NO_SUCH_CONTAINER_PATTERN });
498
+ }
499
+ async function removeContainer(containerName) {
500
+ const result = await runDocker([
501
+ "rm",
502
+ "-f",
503
+ containerName
504
+ ], `docker rm ${containerName} failed`, { ignorePattern: NO_SUCH_CONTAINER_PATTERN });
505
+ return result.exitCode === 0;
506
+ }
507
+ async function removeVolume(volumeName) {
508
+ const result = await runDocker([
509
+ "volume",
510
+ "rm",
511
+ volumeName
512
+ ], `docker volume rm ${volumeName} failed`, { ignorePattern: NO_SUCH_VOLUME_PATTERN });
513
+ return result.exitCode === 0;
514
+ }
515
+ async function listWorkspaceContainers() {
516
+ const result = await runDocker([
517
+ "ps",
518
+ "-a",
519
+ "--filter",
520
+ `label=${LABEL_ID}`,
521
+ "--format",
522
+ "{{json .}}"
523
+ ], "docker ps failed");
524
+ return parseContainerLines(result.stdout);
525
+ }
526
+ async function inspectWorkspaceContainer(containerName) {
527
+ const result = await runDocker([
528
+ "ps",
529
+ "-a",
530
+ "--filter",
531
+ `name=^${containerName}$`,
532
+ "--filter",
533
+ `label=${LABEL_ID}`,
534
+ "--format",
535
+ "{{json .}}"
536
+ ], "docker ps failed");
537
+ const summaries = parseContainerLines(result.stdout);
538
+ return summaries[0] ?? null;
539
+ }
540
+ async function requireWorkspaceContainerLocation(workspaceId) {
541
+ const containerName = containerNameFor(workspaceId);
542
+ const summary = await inspectWorkspaceContainer(containerName);
543
+ if (summary === null) throw new ConfigError(`no container for workspace ${workspaceId} — run \`metabase workspace start ${workspaceId}\` first`);
544
+ if (summary.hostPort === null) throw new ConfigError(`container ${containerName} is missing the host-port label — likely created by a different tool`);
545
+ return {
546
+ containerName,
547
+ hostPort: summary.hostPort
548
+ };
549
+ }
550
+ function streamLogs(containerName, options) {
551
+ const args = [
552
+ "logs",
553
+ "--tail",
554
+ String(options.tail)
555
+ ];
556
+ if (options.follow) args.push("--follow");
557
+ args.push(containerName);
558
+ return streamProcess(DOCKER_BIN, args);
559
+ }
560
+ function parseContainerLines(stdout) {
561
+ const lines = stdout.split("\n").filter((line) => line.trim().length > 0);
562
+ const summaries = [];
563
+ for (const line of lines) {
564
+ const summary = parseContainerLine(line);
565
+ if (summary !== null) summaries.push(summary);
566
+ }
567
+ return summaries;
568
+ }
569
+ function parseContainerLine(line) {
570
+ let raw;
571
+ try {
572
+ raw = parseJson(line, z.unknown(), { source: "docker" });
573
+ } catch (error) {
574
+ throw new DockerError(`could not parse docker output: ${errorMessage(error)}`, null, line);
575
+ }
576
+ const parsed = ContainerSummarySchema.safeParse(raw);
577
+ if (!parsed.success) return null;
578
+ const labels = parseLabels(parsed.data.Labels);
579
+ const idLabel = labels[LABEL_ID];
580
+ const nameLabel = labels[LABEL_NAME];
581
+ if (idLabel === void 0 || nameLabel === void 0) return null;
582
+ const idNum = Number.parseInt(idLabel, 10);
583
+ if (!Number.isFinite(idNum) || idNum < 1) return null;
584
+ const portLabel = labels[LABEL_HOST_PORT];
585
+ const portNum = portLabel !== void 0 ? Number.parseInt(portLabel, 10) : Number.NaN;
586
+ return {
587
+ containerId: parsed.data.ID,
588
+ name: parsed.data.Names,
589
+ state: parseContainerState(parsed.data.State),
590
+ status: parsed.data.Status,
591
+ image: parsed.data.Image,
592
+ workspaceId: idNum,
593
+ workspaceName: nameLabel,
594
+ profile: labels[LABEL_PROFILE] ?? null,
595
+ parentUrl: labels[LABEL_PARENT] ?? null,
596
+ hostPort: Number.isFinite(portNum) ? portNum : null
597
+ };
598
+ }
599
+ function parseLabels(raw) {
600
+ const out = {};
601
+ for (const pair of raw.split(",")) {
602
+ const eq = pair.indexOf("=");
603
+ if (eq === -1) continue;
604
+ const key = pair.slice(0, eq);
605
+ const value = pair.slice(eq + 1);
606
+ if (key.length > 0) out[key] = value;
607
+ }
608
+ return out;
609
+ }
610
+
611
+ //#endregion
612
+ export { CONTAINER_REPO_DIR, CONTAINER_STATES, checkDockerReady, containerLifecycleStatus, containerNameFor, listWorkspaceContainers, pullImage, readContainerCredentialsFile, removeContainer, removeVolume, requireWorkspaceContainerLocation, runProcess, runWorkspaceContainer, scrubContainerConfig, stopContainer, streamLogs, volumeNameFor, waitForConfigConsumed };
@@ -0,0 +1,13 @@
1
+ import { defineCommand } from "citty";
2
+
3
+ //#region src/commands/eid/index.ts
4
+ var eid_default = defineCommand({
5
+ meta: {
6
+ name: "eid",
7
+ description: "Translate Metabase entity ids (string EIDs) to numeric ids"
8
+ },
9
+ subCommands: { translate: () => import("./translate-DqLlXXUx.mjs").then((mod) => mod.default) }
10
+ });
11
+
12
+ //#endregion
13
+ export { eid_default as default };
@@ -1,12 +1,14 @@
1
- import "./package-BGfw4ZWJ.mjs";
1
+ import "./package-DBsS7a5x.mjs";
2
2
  import "./command-augment-D9pI9Vbh.mjs";
3
- import { connectionFlags, defineMetabaseCommand, outputFlags, profileFlag, renderItem } from "./runtime-C9CEZhcn.mjs";
4
- import { parseId } from "./parse-id-BhmmfyCP.mjs";
5
- import { REMOTE_SYNC_PATHS, SyncTask, pollFlags, pollSyncTask, throwIfFailedTask } from "./poll-task-DbpsiQhl.mjs";
6
- import "./poll-ILanYysl.mjs";
3
+ import { renderItem, warn } from "./render-DXv-D6fU.mjs";
4
+ import "./predicates-DiIiS3k7.mjs";
5
+ import { connectionFlags, defineMetabaseCommand, outputFlags, profileFlag } from "./runtime-D7jihh81.mjs";
6
+ import { parseId } from "./parse-id-BUOZQqjp.mjs";
7
+ import { REMOTE_SYNC_PATHS, SyncTask, pollFlags, pollSyncTask, throwIfFailedTask } from "./poll-task-2Ckiwp8U.mjs";
8
+ import "./poll-DMmmZWvi.mjs";
7
9
  import { z } from "zod";
8
10
 
9
- //#region src/commands/sync/export.ts
11
+ //#region src/commands/git-sync/export.ts
10
12
  const SyncExportKickoff = z.object({
11
13
  message: z.string(),
12
14
  task_id: z.number().int().positive()
@@ -54,9 +56,9 @@ var export_default = defineMetabaseCommand({
54
56
  },
55
57
  outputSchema: SyncExportResult,
56
58
  examples: [
57
- "metabase sync export -m \"update dashboards\"",
58
- "metabase sync export --branch main --json",
59
- "metabase sync export --no-wait"
59
+ "metabase git-sync export -m \"update dashboards\"",
60
+ "metabase git-sync export --branch main --json",
61
+ "metabase git-sync export --no-wait"
60
62
  ],
61
63
  async run({ args, ctx, getClient }) {
62
64
  const timeoutMs = parseId(args.timeout, "timeout");
@@ -71,26 +73,31 @@ var export_default = defineMetabaseCommand({
71
73
  body
72
74
  });
73
75
  if (!args.wait) {
74
- const result$1 = {
76
+ const result = {
75
77
  message: kickoff.message,
76
78
  task_id: kickoff.task_id
77
79
  };
78
- renderItem(result$1, syncExportView, ctx);
79
- return;
80
+ renderItem(result, syncExportView, ctx);
81
+ } else {
82
+ const final = await pollSyncTask(client, {
83
+ timeoutMs,
84
+ intervalMs
85
+ });
86
+ const result = {
87
+ message: kickoff.message,
88
+ task_id: kickoff.task_id,
89
+ final
90
+ };
91
+ renderItem(result, syncExportView, ctx);
92
+ throwIfFailedTask(final, "export");
80
93
  }
81
- const final = await pollSyncTask(client, {
82
- timeoutMs,
83
- intervalMs
84
- });
85
- const result = {
86
- message: kickoff.message,
87
- task_id: kickoff.task_id,
88
- final
89
- };
90
- renderItem(result, syncExportView, ctx);
91
- throwIfFailedTask(final, "export");
94
+ emitRealignHint(ctx);
92
95
  }
93
96
  });
97
+ function emitRealignHint(ctx) {
98
+ if (ctx.format !== "text") return;
99
+ warn("\nNote: if exporting to a host-bound repo, realign the host working tree with:\n git -C <repo-path> restore --staged --worktree .");
100
+ }
94
101
 
95
102
  //#endregion
96
103
  export { export_default as default };