@metabase/cli 0.1.0-alpha.workspaces-commands.818a8f1 → 0.1.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 (110) hide show
  1. package/README.md +0 -237
  2. package/dist/{archive-BPG5c88Y.mjs → archive-CsWeHXle.mjs} +4 -5
  3. package/dist/{auth--Hpjwlaf.mjs → auth-BF7IjZIH.mjs} +3 -3
  4. package/dist/{body-DwU2s6Pg.mjs → body-Dv9hQ0Qk.mjs} +2 -2
  5. package/dist/{branches-BbcoJXfp.mjs → branches-BujtceGr.mjs} +4 -5
  6. package/dist/{cancel-task-BDas45YO.mjs → cancel-task-CT2xUMRg.mjs} +4 -5
  7. package/dist/card-_Ta7zdYe.mjs +19 -0
  8. package/dist/cli.mjs +13 -17
  9. package/dist/{create-DpnjQvPw.mjs → create-B8ektf-R.mjs} +6 -7
  10. package/dist/{create-_UOeEXAj.mjs → create-CI2Cunq5.mjs} +6 -7
  11. package/dist/{create-CwVcoq0O.mjs → create-DdbU3TLX.mjs} +6 -7
  12. package/dist/{create-branch-sDttBORB.mjs → create-branch-goZBTNnr.mjs} +4 -5
  13. package/dist/{current-task-BGt1mqaX.mjs → current-task-DBjRNCFq.mjs} +4 -5
  14. package/dist/{db-Dm2u2ISJ.mjs → db-DMghzgb6.mjs} +2 -2
  15. package/dist/{delete-DRBTgyus.mjs → delete-8vGU35r3.mjs} +5 -6
  16. package/dist/{delete-DUC_stoL.mjs → delete-B27KLF5X.mjs} +5 -6
  17. package/dist/{delete-runtime-inOVw3IX.mjs → delete-runtime-Byr60cR3.mjs} +2 -4
  18. package/dist/{delete-table-9Is631O_.mjs → delete-table-BNaJ_gA4.mjs} +5 -6
  19. package/dist/{dirty-CLjHbz6J.mjs → dirty-aNUuph4I.mjs} +4 -5
  20. package/dist/{export-D2Anfu3p.mjs → export-QDkuuzSE.mjs} +5 -6
  21. package/dist/{field-Dhs2AND3.mjs → field-DaYo_90x.mjs} +1 -1
  22. package/dist/{get-DhIoNeOp.mjs → get-BGBIzMKY.mjs} +4 -5
  23. package/dist/{get-CAVVmdMX.mjs → get-COXHplHP.mjs} +4 -5
  24. package/dist/{get-CAPLfawI.mjs → get-Cl8-IauC.mjs} +4 -5
  25. package/dist/{get-DDWpubE8.mjs → get-Cwpj7lDe.mjs} +5 -6
  26. package/dist/{get-BHJA78zg.mjs → get-DI_IJvgk.mjs} +4 -5
  27. package/dist/{get-2po1uv9i.mjs → get-Dh_acl8q.mjs} +4 -5
  28. package/dist/{get-qPOsuTPw.mjs → get-i6LWOByV.mjs} +4 -5
  29. package/dist/{has-remote-changes-DAL5jetW.mjs → has-remote-changes-hjKoQuRy.mjs} +4 -5
  30. package/dist/{import-CUMxUfSF.mjs → import-HJsSKRYx.mjs} +5 -6
  31. package/dist/{input-BNqSFl38.mjs → input-Dojr-RTw.mjs} +1 -1
  32. package/dist/{is-dirty-B10S6MG0.mjs → is-dirty-1Qy7hiHB.mjs} +2 -3
  33. package/dist/is-dirty-DpKn9HJp.mjs +8 -0
  34. package/dist/{key-CyhOpgWt.mjs → key-DBxPSFwi.mjs} +1 -1
  35. package/dist/{license-DtsGJi3l.mjs → license-MoWse3ZI.mjs} +3 -3
  36. package/dist/{list-Y7iGsOfE.mjs → list-Bk6RsbJl.mjs} +3 -4
  37. package/dist/{list-DeFGwhhJ.mjs → list-C4Ajrw8f.mjs} +3 -4
  38. package/dist/{list-C5MGydGU.mjs → list-C8tdLOH5.mjs} +3 -4
  39. package/dist/{list-evtQS7jl.mjs → list-CBSBHtK-.mjs} +3 -4
  40. package/dist/{list-qetY9OIN.mjs → list-CWt3fqrZ.mjs} +3 -4
  41. package/dist/{list-OBx5B3gd.mjs → list-C_PRdL5e.mjs} +5 -6
  42. package/dist/{login-Dqw9ZtCx.mjs → login-C9WTwNn6.mjs} +4 -5
  43. package/dist/{logout-DwYJ5OUi.mjs → logout-oLszGCOg.mjs} +3 -4
  44. package/dist/{package-t8dKf4m_.mjs → package-BGfw4ZWJ.mjs} +1 -2
  45. package/dist/parse-id-BhmmfyCP.mjs +14 -0
  46. package/dist/{poll-D2sXM5rc.mjs → poll-ILanYysl.mjs} +1 -1
  47. package/dist/{poll-task-Byiunmaj.mjs → poll-task-DbpsiQhl.mjs} +2 -2
  48. package/dist/{prompt-fXeNtj0M.mjs → prompt-DpT8yAVy.mjs} +1 -1
  49. package/dist/{query-BnGVGeM3.mjs → query-PihYi-UZ.mjs} +4 -5
  50. package/dist/{remove-Bx48o-0S.mjs → remove-B2hVYn1v.mjs} +3 -4
  51. package/dist/{run-D4NgvaRh.mjs → run-C2so6Qp6.mjs} +27 -11
  52. package/dist/{runtime-DUgFfYkN.mjs → runtime-C9CEZhcn.mjs} +335 -203
  53. package/dist/{search-4wKx5ug2.mjs → search-CopOytXY.mjs} +4 -5
  54. package/dist/{set-BZnCRL4c.mjs → set-BcF7M1GQ.mjs} +4 -5
  55. package/dist/{set-DCjrmTFm.mjs → set-CbibegpA.mjs} +6 -7
  56. package/dist/{setting-C4vQSqer.mjs → setting-U3NtBMFo.mjs} +3 -3
  57. package/dist/{stash-ZZkmW_V7.mjs → stash-DOBbYozC.mjs} +5 -6
  58. package/dist/{status-DezF-PIM.mjs → status-Buf1ZbNR.mjs} +5 -6
  59. package/dist/{status-JH6BZppo.mjs → status-CUcs8XBH.mjs} +2 -3
  60. package/dist/{status-9KAPIpX8.mjs → status-D1F5XHae.mjs} +2 -3
  61. package/dist/sync-BPyGXfUk.mjs +26 -0
  62. package/dist/{table-BvAr2ixC.mjs → table-Cfk7oSvw.mjs} +1 -1
  63. package/dist/{table-D-Mb5Nvw.mjs → table-D7nJt7JO.mjs} +2 -2
  64. package/dist/transform-UbyewMxY.mjs +21 -0
  65. package/dist/transform-job-CrYkr-Ma.mjs +19 -0
  66. package/dist/{update-DxKlQ0hP.mjs → update-CL8tRbxr.mjs} +7 -8
  67. package/dist/{update-CDtm71m2.mjs → update-DU2oU2j-.mjs} +7 -8
  68. package/dist/{wait-Cj_8wu4y.mjs → wait-Bugr9eXD.mjs} +5 -6
  69. package/package.json +1 -2
  70. package/dist/api-key-D9XxErQn.mjs +0 -13
  71. package/dist/card-D4zZSPUb.mjs +0 -19
  72. package/dist/create-Bd_U1zWU.mjs +0 -124
  73. package/dist/create-CCzsCZMm.mjs +0 -47
  74. package/dist/credentials-C0xKke5D.mjs +0 -84
  75. package/dist/database-4V1iiPEx.mjs +0 -17
  76. package/dist/deprovision-BAMzZc6f.mjs +0 -60
  77. package/dist/docker-QWVMG2gl.mjs +0 -605
  78. package/dist/eid-BNhutC1U.mjs +0 -13
  79. package/dist/flag-pair-CWvvzDJ_.mjs +0 -17
  80. package/dist/is-dirty-CUuq-aB6.mjs +0 -9
  81. package/dist/list-B8s7Qnzk.mjs +0 -31
  82. package/dist/logs-B_lrY7Js.mjs +0 -57
  83. package/dist/parse-id-C1prc9US.mjs +0 -12
  84. package/dist/provision-DC4_HWZD.mjs +0 -80
  85. package/dist/ps-1bZKIwWh.mjs +0 -9
  86. package/dist/ps-BiOrecEe.mjs +0 -78
  87. package/dist/remove-DecoZzNd.mjs +0 -97
  88. package/dist/render-DlBijc5i.mjs +0 -179
  89. package/dist/setup-Dqh9hN6l.mjs +0 -70
  90. package/dist/start-xXQypG5L.mjs +0 -324
  91. package/dist/stop-br-ZOnve.mjs +0 -80
  92. package/dist/sync-C7VOWD00.mjs +0 -26
  93. package/dist/transform-CqxZwhGs.mjs +0 -21
  94. package/dist/transform-job-HjbqjEoP.mjs +0 -19
  95. package/dist/translate-DJxDVAE4.mjs +0 -110
  96. package/dist/update-DYVeVjk2.mjs +0 -76
  97. package/dist/url-DP88YHNo.mjs +0 -53
  98. package/dist/wait-DwZN3ZwR.mjs +0 -19
  99. package/dist/wait-flags-CjW4ogUJ.mjs +0 -35
  100. package/dist/workspace-CbwR0vX_.mjs +0 -24
  101. package/dist/workspace-Dr9lWU3D.mjs +0 -72
  102. package/dist/workspace-credentials-q5RRFMT8.mjs +0 -139
  103. /package/dist/{body-flags-7oqLhu5j.mjs → body-flags-BUA9XV1u.mjs} +0 -0
  104. /package/dist/{card-C31pGtBZ.mjs → card-CsXk8T6A.mjs} +0 -0
  105. /package/dist/{database-BTX5qbSv.mjs → database-PA9Goi25.mjs} +0 -0
  106. /package/dist/{field-QwBMAWsq.mjs → field-C8IVs6rp.mjs} +0 -0
  107. /package/dist/{manifest-wzEFG0JB.mjs → manifest-CAdjQYH8.mjs} +0 -0
  108. /package/dist/{setting-DM7pm7yh.mjs → setting-26ckqHAP.mjs} +0 -0
  109. /package/dist/{transform-DfVkUttP.mjs → transform-B5uRpg1G.mjs} +0 -0
  110. /package/dist/{transform-job-DuB_OjhO.mjs → transform-job-C7QXWTVE.mjs} +0 -0
@@ -1,605 +0,0 @@
1
- import { ConfigError, errorMessage, isNotFoundError, parseJson } from "./runtime-DUgFfYkN.mjs";
2
- import { pollUntil } from "./poll-D2sXM5rc.mjs";
3
- import { z } from "zod";
4
- import { spawn } from "node:child_process";
5
-
6
- //#region src/runtime/process.ts
7
- var ProcessNotFoundError = class extends Error {
8
- command;
9
- constructor(command) {
10
- super(`command not found: ${command}`);
11
- this.name = "ProcessNotFoundError";
12
- this.command = command;
13
- }
14
- };
15
- var ProcessTimeoutError = class extends Error {
16
- command;
17
- timeoutMs;
18
- constructor(command, timeoutMs) {
19
- super(`command timed out after ${timeoutMs}ms: ${command}`);
20
- this.name = "ProcessTimeoutError";
21
- this.command = command;
22
- this.timeoutMs = timeoutMs;
23
- }
24
- };
25
- function spawnAndCollect(command, args, options) {
26
- const timeoutSignal = options.timeoutMs !== void 0 && options.timeoutMs > 0 ? AbortSignal.timeout(options.timeoutMs) : void 0;
27
- return new Promise((resolve, reject) => {
28
- const child = spawn(command, args, {
29
- stdio: [
30
- "pipe",
31
- "pipe",
32
- "pipe"
33
- ],
34
- env: options.env ?? process.env,
35
- ...options.cwd !== void 0 ? { cwd: options.cwd } : {},
36
- ...timeoutSignal !== void 0 ? {
37
- signal: timeoutSignal,
38
- killSignal: "SIGKILL"
39
- } : {}
40
- });
41
- const stdoutChunks = [];
42
- let stderr = "";
43
- child.stdout.on("data", (chunk) => {
44
- stdoutChunks.push(chunk);
45
- });
46
- child.stderr.on("data", (chunk) => {
47
- stderr += chunk.toString("utf8");
48
- });
49
- child.on("error", (error) => {
50
- if (isNotFoundError(error)) {
51
- reject(new ProcessNotFoundError(command));
52
- return;
53
- }
54
- if (timeoutSignal?.aborted) {
55
- reject(new ProcessTimeoutError(command, options.timeoutMs ?? 0));
56
- return;
57
- }
58
- reject(error);
59
- });
60
- child.on("close", (code) => {
61
- if (timeoutSignal?.aborted) {
62
- reject(new ProcessTimeoutError(command, options.timeoutMs ?? 0));
63
- return;
64
- }
65
- const stdoutBuffer = Buffer.concat(stdoutChunks);
66
- resolve({
67
- stdout: new Uint8Array(stdoutBuffer.buffer, stdoutBuffer.byteOffset, stdoutBuffer.length),
68
- stderr,
69
- exitCode: code
70
- });
71
- });
72
- if (options.stdin === void 0) child.stdin.end();
73
- else child.stdin.end(options.stdin);
74
- });
75
- }
76
- const stdoutDecoder = new TextDecoder("utf-8");
77
- async function runProcess(command, args, options = {}) {
78
- const result = await spawnAndCollect(command, args, options);
79
- return {
80
- stdout: stdoutDecoder.decode(result.stdout),
81
- stderr: result.stderr,
82
- exitCode: result.exitCode
83
- };
84
- }
85
- function runProcessBinary(command, args, options = {}) {
86
- return spawnAndCollect(command, args, options);
87
- }
88
- function streamProcess(command, args) {
89
- return new Promise((resolve, reject) => {
90
- const child = spawn(command, args, { stdio: "inherit" });
91
- child.on("error", (error) => {
92
- if (isNotFoundError(error)) {
93
- reject(new ProcessNotFoundError(command));
94
- return;
95
- }
96
- reject(error);
97
- });
98
- child.on("close", (code) => resolve(code));
99
- });
100
- }
101
-
102
- //#endregion
103
- //#region src/runtime/tar.ts
104
- const BLOCK_SIZE = 512;
105
- const REGULAR_FILE_MODE = 420;
106
- const DIR_MODE = 493;
107
- const NAME_FIELD_LENGTH = 100;
108
- const TYPE_FLAG_REGULAR = "0";
109
- const TYPE_FLAG_DIRECTORY = "5";
110
- const textEncoder = new TextEncoder();
111
- function toBytes(content) {
112
- return typeof content === "string" ? textEncoder.encode(content) : content;
113
- }
114
- function writeOctal(target, offset, length, value) {
115
- const digits = length - 1;
116
- const octal = Math.trunc(value).toString(8).padStart(digits, "0");
117
- if (octal.length > digits) throw new Error(`tar value ${value} exceeds octal field width ${digits}`);
118
- for (let i = 0; i < digits; i++) target[offset + i] = octal.charCodeAt(i);
119
- target[offset + length - 1] = 0;
120
- }
121
- function writeString(target, offset, length, value) {
122
- const bytes = textEncoder.encode(value);
123
- if (bytes.length > length) throw new Error(`tar string field of length ${length} cannot hold ${bytes.length} bytes`);
124
- target.set(bytes, offset);
125
- }
126
- function writeHeader(out, offset, name, size, mode, mtime, typeFlag) {
127
- if (textEncoder.encode(name).length > NAME_FIELD_LENGTH) throw new Error(`tar entry name exceeds ${NAME_FIELD_LENGTH} bytes: ${name}`);
128
- writeString(out, offset, NAME_FIELD_LENGTH, name);
129
- writeOctal(out, offset + 100, 8, mode & 4095);
130
- writeOctal(out, offset + 108, 8, 0);
131
- writeOctal(out, offset + 116, 8, 0);
132
- writeOctal(out, offset + 124, 12, size);
133
- writeOctal(out, offset + 136, 12, mtime);
134
- for (let i = 148; i < 156; i++) out[offset + i] = 32;
135
- out[offset + 156] = typeFlag.charCodeAt(0);
136
- writeString(out, offset + 257, 6, "ustar");
137
- out[offset + 263] = 48;
138
- out[offset + 264] = 48;
139
- let sum = 0;
140
- for (const byte of out.subarray(offset, offset + BLOCK_SIZE)) sum += byte;
141
- const sumOctal = sum.toString(8).padStart(6, "0");
142
- for (let i = 0; i < 6; i++) out[offset + 148 + i] = sumOctal.charCodeAt(i);
143
- out[offset + 154] = 0;
144
- out[offset + 155] = 32;
145
- }
146
- function paddedSize(size) {
147
- const remainder = size % BLOCK_SIZE;
148
- return remainder === 0 ? 0 : BLOCK_SIZE - remainder;
149
- }
150
- function resolveEntry(entry, fallbackMtime) {
151
- if (entry.type === "directory") {
152
- const name = entry.name.endsWith("/") ? entry.name : `${entry.name}/`;
153
- return {
154
- name,
155
- mode: entry.mode ?? DIR_MODE,
156
- mtime: entry.mtime ?? fallbackMtime,
157
- typeFlag: TYPE_FLAG_DIRECTORY,
158
- content: null
159
- };
160
- }
161
- return {
162
- name: entry.name,
163
- mode: entry.mode ?? REGULAR_FILE_MODE,
164
- mtime: entry.mtime ?? fallbackMtime,
165
- typeFlag: TYPE_FLAG_REGULAR,
166
- content: toBytes(entry.content)
167
- };
168
- }
169
- var TarParseError = class extends Error {
170
- constructor(message) {
171
- super(message);
172
- this.name = "TarParseError";
173
- }
174
- };
175
- const SIZE_FIELD_OFFSET = 124;
176
- const SIZE_FIELD_LENGTH = 12;
177
- const textDecoder = new TextDecoder("utf-8");
178
- function readNullTerminatedString(buffer, offset, length) {
179
- const slice = buffer.subarray(offset, offset + length);
180
- const nul = slice.indexOf(0);
181
- const end = nul === -1 ? slice.length : nul;
182
- return textDecoder.decode(slice.subarray(0, end));
183
- }
184
- function extractSingleFileFromTar(tar, expectedNameSuffix) {
185
- if (tar.length < BLOCK_SIZE) throw new TarParseError(`tar is shorter than one block: ${tar.length} bytes`);
186
- const nameField = readNullTerminatedString(tar, 0, NAME_FIELD_LENGTH);
187
- if (!nameField.endsWith(expectedNameSuffix)) throw new TarParseError(`unexpected tar entry ${JSON.stringify(nameField)}, expected to end with ${JSON.stringify(expectedNameSuffix)}`);
188
- const sizeField = readNullTerminatedString(tar, SIZE_FIELD_OFFSET, SIZE_FIELD_LENGTH).trim();
189
- const size = Number.parseInt(sizeField, 8);
190
- if (!Number.isFinite(size) || size < 0) throw new TarParseError(`tar header has invalid size field: ${JSON.stringify(sizeField)}`);
191
- if (tar.length < BLOCK_SIZE + size) throw new TarParseError(`tar truncated: header reports ${size} content bytes but only ${tar.length - BLOCK_SIZE} bytes follow`);
192
- return tar.subarray(BLOCK_SIZE, BLOCK_SIZE + size);
193
- }
194
- function buildTar(entries) {
195
- const fallbackMtime = Math.floor(Date.now() / 1e3);
196
- const resolved = entries.map((entry) => resolveEntry(entry, fallbackMtime));
197
- let total = BLOCK_SIZE * 2;
198
- for (const entry of resolved) {
199
- total += BLOCK_SIZE;
200
- if (entry.content !== null) total += entry.content.length + paddedSize(entry.content.length);
201
- }
202
- const out = new Uint8Array(total);
203
- let offset = 0;
204
- for (const entry of resolved) {
205
- const size = entry.content?.length ?? 0;
206
- writeHeader(out, offset, entry.name, size, entry.mode, entry.mtime, entry.typeFlag);
207
- offset += BLOCK_SIZE;
208
- if (entry.content !== null) {
209
- out.set(entry.content, offset);
210
- offset += entry.content.length + paddedSize(entry.content.length);
211
- }
212
- }
213
- return out;
214
- }
215
-
216
- //#endregion
217
- //#region src/core/docker.ts
218
- const DOCKER_BIN = "docker";
219
- const CONTAINER_NAME_PREFIX = "metabase-workspace-";
220
- const VOLUME_NAME_SUFFIX = "-appdb";
221
- const LABEL_ID = "com.metabase.workspace.id";
222
- const LABEL_NAME = "com.metabase.workspace.name";
223
- const LABEL_PROFILE = "com.metabase.workspace.profile";
224
- const LABEL_PARENT = "com.metabase.workspace.parent";
225
- const LABEL_IMAGE = "com.metabase.workspace.image";
226
- const LABEL_HOST_PORT = "com.metabase.workspace.host-port";
227
- const WORKSPACE_CONTAINER_PORT = 3e3;
228
- const CONTAINER_CONFIG_DIR = "/mw-config";
229
- const CONTAINER_CONFIG_DIR_BASENAME = CONTAINER_CONFIG_DIR.replace(/^\//, "");
230
- const CONTAINER_APP_DB_DIR = "/metabase-app-db";
231
- const CONTAINER_REPO_DIR = "/mnt/repo";
232
- const CONFIG_FILENAME = "config.yml";
233
- const METADATA_FILENAME = "metadata.json";
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 Error {
254
- exitCode;
255
- stderr;
256
- constructor(message, exitCode, stderr) {
257
- super(message);
258
- this.name = "DockerError";
259
- this.exitCode = exitCode;
260
- this.stderr = stderr;
261
- }
262
- };
263
- var DockerNotInstalledError = class extends Error {
264
- constructor() {
265
- super("docker is not installed or not on PATH — install Docker Desktop / OrbStack / Colima and retry");
266
- this.name = "DockerNotInstalledError";
267
- }
268
- };
269
- var DockerNotRunningError = class extends Error {
270
- stderr;
271
- constructor(stderr) {
272
- super("docker is installed but the daemon is not responding — start Docker and retry");
273
- this.name = "DockerNotRunningError";
274
- this.stderr = stderr;
275
- }
276
- };
277
- const ContainerSummarySchema = z.object({
278
- ID: z.string(),
279
- Names: z.string(),
280
- State: z.string(),
281
- Status: z.string(),
282
- Image: z.string(),
283
- Labels: z.string(),
284
- Ports: z.string()
285
- });
286
- function containerNameFor(workspaceId) {
287
- return `${CONTAINER_NAME_PREFIX}${workspaceId}`;
288
- }
289
- function volumeNameFor(workspaceId) {
290
- return `${CONTAINER_NAME_PREFIX}${workspaceId}${VOLUME_NAME_SUFFIX}`;
291
- }
292
- async function dockerExec(args, options = {}) {
293
- try {
294
- return await runProcess(DOCKER_BIN, args, options);
295
- } catch (error) {
296
- if (error instanceof ProcessNotFoundError) throw new DockerNotInstalledError();
297
- throw error;
298
- }
299
- }
300
- async function runDocker(args, failureMessage, options = {}) {
301
- const { ignorePattern,...execOptions } = options;
302
- const result = await dockerExec(args, execOptions);
303
- if (result.exitCode === 0) return result;
304
- if (ignorePattern?.test(result.stderr)) return result;
305
- throw new DockerError(failureMessage, result.exitCode, result.stderr);
306
- }
307
- async function checkDockerReady() {
308
- let result;
309
- try {
310
- result = await runProcess(DOCKER_BIN, [
311
- "version",
312
- "--format",
313
- "{{.Server.Version}}"
314
- ]);
315
- } catch (error) {
316
- if (error instanceof ProcessNotFoundError) throw new DockerNotInstalledError();
317
- throw error;
318
- }
319
- if (result.exitCode !== 0) throw new DockerNotRunningError(result.stderr);
320
- }
321
- async function pullImage(image) {
322
- const code = await streamProcess(DOCKER_BIN, ["pull", image]);
323
- if (code !== 0) throw new DockerError(`docker pull ${image} failed`, code, "");
324
- }
325
- async function containerLifecycleStatus(containerName) {
326
- const result = await runDocker([
327
- "ps",
328
- "-a",
329
- "--filter",
330
- `name=^${containerName}$`,
331
- "--format",
332
- "{{.State}}"
333
- ], "docker ps failed");
334
- const trimmed = result.stdout.trim();
335
- if (trimmed.length === 0) return "missing";
336
- return parseContainerState(trimmed);
337
- }
338
- function parseContainerState(raw) {
339
- const lower = raw.toLowerCase();
340
- for (const known of CONTAINER_STATES) if (known === lower) return known;
341
- throw new DockerError(`unknown docker container state: ${JSON.stringify(raw)}`, null, "");
342
- }
343
- async function runWorkspaceContainer(spec) {
344
- const containerName = containerNameFor(spec.workspaceId);
345
- await createContainer({
346
- containerName,
347
- image: spec.image,
348
- port: {
349
- hostPort: spec.hostPort,
350
- containerPort: WORKSPACE_CONTAINER_PORT
351
- },
352
- namedVolumes: [{
353
- volume: volumeNameFor(spec.workspaceId),
354
- container: CONTAINER_APP_DB_DIR
355
- }],
356
- bindMounts: spec.bindMounts,
357
- envVars: workspaceContainerEnv(spec),
358
- labels: workspaceContainerLabels(spec)
359
- });
360
- try {
361
- await copyTarToContainer(containerName, "/", buildBootBundleTar(spec));
362
- await startContainer(containerName);
363
- } catch (error) {
364
- await removeContainer(containerName).catch(() => void 0);
365
- throw error;
366
- }
367
- }
368
- async function scrubContainerConfig(workspaceId) {
369
- const containerName = containerNameFor(workspaceId);
370
- await runDocker([
371
- "exec",
372
- containerName,
373
- "rm",
374
- "-f",
375
- `${CONTAINER_CONFIG_DIR}/${CONFIG_FILENAME}`
376
- ], `docker exec rm config.yml failed for ${containerName}`);
377
- }
378
- async function waitForConfigConsumed(workspaceId, timeoutMs) {
379
- const containerName = containerNameFor(workspaceId);
380
- await pollUntil(async () => {
381
- const result = await dockerExec([
382
- "logs",
383
- "--tail",
384
- String(CONFIG_CONSUMED_LOG_LINES),
385
- containerName
386
- ]);
387
- const haystack = `${result.stdout}\n${result.stderr}`;
388
- if (haystack.includes(INIT_FAILED_MARKER)) {
389
- const tail = haystack.split("\n").slice(-INIT_FAILED_TAIL_LINES).join("\n");
390
- throw new DockerError(`workspace ${workspaceId} container failed Metabase initialization`, null, tail);
391
- }
392
- return haystack.includes(CONFIG_CONSUMED_MARKER);
393
- }, (consumed) => consumed, {
394
- intervalMs: CONFIG_CONSUMED_INTERVAL_MS,
395
- maxIntervalMs: CONFIG_CONSUMED_MAX_INTERVAL_MS,
396
- backoff: "exponential",
397
- timeoutMs
398
- });
399
- }
400
- async function readContainerCredentialsFile(workspaceId) {
401
- const containerName = containerNameFor(workspaceId);
402
- const result = await runProcessBinary(DOCKER_BIN, [
403
- "cp",
404
- `${containerName}:${CONTAINER_CONFIG_DIR}/${CREDENTIALS_FILENAME}`,
405
- "-"
406
- ]);
407
- if (result.exitCode !== 0) {
408
- if (NO_SUCH_CONTAINER_PATTERN.test(result.stderr)) throw new DockerError(`no container for workspace ${workspaceId}`, result.exitCode, result.stderr);
409
- throw new DockerError(`docker cp ${CREDENTIALS_FILENAME} from ${containerName} failed`, result.exitCode, result.stderr);
410
- }
411
- return extractSingleFileFromTar(result.stdout, CREDENTIALS_FILENAME);
412
- }
413
- function buildBootBundleTar(spec) {
414
- const entries = [
415
- {
416
- type: "directory",
417
- name: CONTAINER_CONFIG_DIR_BASENAME
418
- },
419
- {
420
- type: "file",
421
- name: `${CONTAINER_CONFIG_DIR_BASENAME}/${CONFIG_FILENAME}`,
422
- content: spec.configYaml,
423
- mode: BUNDLE_FILE_MODE
424
- },
425
- {
426
- type: "file",
427
- name: `${CONTAINER_CONFIG_DIR_BASENAME}/${CREDENTIALS_FILENAME}`,
428
- content: spec.credentialsJson,
429
- mode: BUNDLE_FILE_MODE
430
- }
431
- ];
432
- if (spec.metadataJson !== null) entries.push({
433
- type: "file",
434
- name: `${CONTAINER_CONFIG_DIR_BASENAME}/${METADATA_FILENAME}`,
435
- content: spec.metadataJson,
436
- mode: BUNDLE_FILE_MODE
437
- });
438
- return buildTar(entries);
439
- }
440
- function workspaceContainerLabels(spec) {
441
- return {
442
- [LABEL_ID]: String(spec.workspaceId),
443
- [LABEL_NAME]: spec.workspaceName,
444
- [LABEL_PROFILE]: spec.profile,
445
- [LABEL_PARENT]: spec.parentUrl,
446
- [LABEL_IMAGE]: spec.image,
447
- [LABEL_HOST_PORT]: String(spec.hostPort)
448
- };
449
- }
450
- function workspaceContainerEnv(spec) {
451
- const env = {
452
- MB_CONFIG_FILE_PATH: `${CONTAINER_CONFIG_DIR}/${CONFIG_FILENAME}`,
453
- MB_PREMIUM_EMBEDDING_TOKEN: spec.licenseToken,
454
- MB_DB_FILE: `${CONTAINER_APP_DB_DIR}/metabase.db`,
455
- JAVA_OPTS: "-Xmx2g"
456
- };
457
- if (spec.metadataJson !== null) env["MB_DATABASE_METADATA_PATH"] = `${CONTAINER_CONFIG_DIR}/${METADATA_FILENAME}`;
458
- return env;
459
- }
460
- async function createContainer(options) {
461
- const args = [
462
- "create",
463
- "--name",
464
- options.containerName,
465
- "-p",
466
- `${options.port.hostPort}:${options.port.containerPort}`
467
- ];
468
- for (const [key, value] of Object.entries(options.labels)) args.push("--label", `${key}=${value}`);
469
- for (const mount of options.namedVolumes) args.push("-v", `${mount.volume}:${mount.container}`);
470
- for (const bind of options.bindMounts) args.push("-v", `${bind.hostPath}:${bind.containerPath}:${bind.readOnly ? "ro" : "rw"}`);
471
- for (const key of Object.keys(options.envVars)) args.push("-e", key);
472
- args.push(options.image);
473
- const env = {
474
- ...process.env,
475
- ...options.envVars
476
- };
477
- await runDocker(args, `docker create failed for ${options.containerName}`, { env });
478
- }
479
- async function copyTarToContainer(containerName, destPath, tarBytes) {
480
- await runDocker([
481
- "cp",
482
- "-",
483
- `${containerName}:${destPath}`
484
- ], `docker cp into ${containerName}:${destPath} failed`, { stdin: tarBytes });
485
- }
486
- async function startContainer(containerName) {
487
- await runDocker(["start", containerName], `docker start failed for ${containerName}`);
488
- }
489
- async function stopContainer(containerName) {
490
- await runDocker(["stop", containerName], `docker stop ${containerName} failed`, { ignorePattern: NO_SUCH_CONTAINER_PATTERN });
491
- }
492
- async function removeContainer(containerName) {
493
- const result = await runDocker([
494
- "rm",
495
- "-f",
496
- containerName
497
- ], `docker rm ${containerName} failed`, { ignorePattern: NO_SUCH_CONTAINER_PATTERN });
498
- return result.exitCode === 0;
499
- }
500
- async function removeVolume(volumeName) {
501
- const result = await runDocker([
502
- "volume",
503
- "rm",
504
- volumeName
505
- ], `docker volume rm ${volumeName} failed`, { ignorePattern: NO_SUCH_VOLUME_PATTERN });
506
- return result.exitCode === 0;
507
- }
508
- async function listWorkspaceContainers() {
509
- const result = await runDocker([
510
- "ps",
511
- "-a",
512
- "--filter",
513
- `label=${LABEL_ID}`,
514
- "--format",
515
- "{{json .}}"
516
- ], "docker ps failed");
517
- return parseContainerLines(result.stdout);
518
- }
519
- async function inspectWorkspaceContainer(containerName) {
520
- const result = await runDocker([
521
- "ps",
522
- "-a",
523
- "--filter",
524
- `name=^${containerName}$`,
525
- "--filter",
526
- `label=${LABEL_ID}`,
527
- "--format",
528
- "{{json .}}"
529
- ], "docker ps failed");
530
- const summaries = parseContainerLines(result.stdout);
531
- return summaries[0] ?? null;
532
- }
533
- async function requireWorkspaceContainerLocation(workspaceId) {
534
- const containerName = containerNameFor(workspaceId);
535
- const summary = await inspectWorkspaceContainer(containerName);
536
- if (summary === null) throw new ConfigError(`no container for workspace ${workspaceId} — run \`metabase workspace start ${workspaceId}\` first`);
537
- if (summary.hostPort === null) throw new ConfigError(`container ${containerName} is missing the host-port label — likely created by a different tool`);
538
- return {
539
- containerName,
540
- hostPort: summary.hostPort
541
- };
542
- }
543
- function streamLogs(containerName, options) {
544
- const args = [
545
- "logs",
546
- "--tail",
547
- String(options.tail)
548
- ];
549
- if (options.follow) args.push("--follow");
550
- args.push(containerName);
551
- return streamProcess(DOCKER_BIN, args);
552
- }
553
- function parseContainerLines(stdout) {
554
- const lines = stdout.split("\n").filter((line) => line.trim().length > 0);
555
- const summaries = [];
556
- for (const line of lines) {
557
- const summary = parseContainerLine(line);
558
- if (summary !== null) summaries.push(summary);
559
- }
560
- return summaries;
561
- }
562
- function parseContainerLine(line) {
563
- let raw;
564
- try {
565
- raw = parseJson(line, z.unknown(), { source: "docker" });
566
- } catch (error) {
567
- throw new DockerError(`could not parse docker output: ${errorMessage(error)}`, null, line);
568
- }
569
- const parsed = ContainerSummarySchema.safeParse(raw);
570
- if (!parsed.success) return null;
571
- const labels = parseLabels(parsed.data.Labels);
572
- const idLabel = labels[LABEL_ID];
573
- const nameLabel = labels[LABEL_NAME];
574
- if (idLabel === void 0 || nameLabel === void 0) return null;
575
- const idNum = Number.parseInt(idLabel, 10);
576
- if (!Number.isFinite(idNum) || idNum < 1) return null;
577
- const portLabel = labels[LABEL_HOST_PORT];
578
- const portNum = portLabel !== void 0 ? Number.parseInt(portLabel, 10) : Number.NaN;
579
- return {
580
- containerId: parsed.data.ID,
581
- name: parsed.data.Names,
582
- state: parseContainerState(parsed.data.State),
583
- status: parsed.data.Status,
584
- image: parsed.data.Image,
585
- workspaceId: idNum,
586
- workspaceName: nameLabel,
587
- profile: labels[LABEL_PROFILE] ?? null,
588
- parentUrl: labels[LABEL_PARENT] ?? null,
589
- hostPort: Number.isFinite(portNum) ? portNum : null
590
- };
591
- }
592
- function parseLabels(raw) {
593
- const out = {};
594
- for (const pair of raw.split(",")) {
595
- const eq = pair.indexOf("=");
596
- if (eq === -1) continue;
597
- const key = pair.slice(0, eq);
598
- const value = pair.slice(eq + 1);
599
- if (key.length > 0) out[key] = value;
600
- }
601
- return out;
602
- }
603
-
604
- //#endregion
605
- export { CONTAINER_REPO_DIR, CONTAINER_STATES, checkDockerReady, containerLifecycleStatus, containerNameFor, listWorkspaceContainers, pullImage, readContainerCredentialsFile, removeContainer, removeVolume, requireWorkspaceContainerLocation, runProcess, runWorkspaceContainer, scrubContainerConfig, stopContainer, streamLogs, volumeNameFor, waitForConfigConsumed };
@@ -1,13 +0,0 @@
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-DJxDVAE4.mjs").then((mod) => mod.default) }
10
- });
11
-
12
- //#endregion
13
- export { eid_default as default };
@@ -1,17 +0,0 @@
1
- import { ConfigError } from "./runtime-DUgFfYkN.mjs";
2
-
3
- //#region src/commands/flag-pair.ts
4
- function requireBothOrNeither(first, second) {
5
- const firstSet = first.value !== void 0 && first.value !== "";
6
- const secondSet = second.value !== void 0 && second.value !== "";
7
- if (!firstSet && !secondSet) return null;
8
- if (!firstSet) throw new ConfigError(`${first.name} is required when using ${second.name}`);
9
- if (!secondSet) throw new ConfigError(`${second.name} is required when using ${first.name}`);
10
- return {
11
- first: first.value,
12
- second: second.value
13
- };
14
- }
15
-
16
- //#endregion
17
- export { requireBothOrNeither };
@@ -1,9 +0,0 @@
1
- import "./package-t8dKf4m_.mjs";
2
- import "./command-augment-D9pI9Vbh.mjs";
3
- import "./render-DlBijc5i.mjs";
4
- import "./runtime-DUgFfYkN.mjs";
5
- import "./poll-task-Byiunmaj.mjs";
6
- import "./poll-D2sXM5rc.mjs";
7
- import { IsDirtyResult, is_dirty_default } from "./is-dirty-B10S6MG0.mjs";
8
-
9
- export { is_dirty_default as default };
@@ -1,31 +0,0 @@
1
- import "./package-t8dKf4m_.mjs";
2
- import "./command-augment-D9pI9Vbh.mjs";
3
- import { renderList } from "./render-DlBijc5i.mjs";
4
- import { connectionFlags, defineMetabaseCommand, listEnvelopeSchema, outputFlags, profileFlag, wrapList } from "./runtime-DUgFfYkN.mjs";
5
- import { Workspace, WorkspaceCompact, workspaceView } from "./workspace-Dr9lWU3D.mjs";
6
- import { z } from "zod";
7
-
8
- //#region src/commands/workspace/list.ts
9
- const WorkspaceApiList = z.array(Workspace);
10
- const WorkspaceListEnvelope = listEnvelopeSchema(WorkspaceCompact);
11
- var list_default = defineMetabaseCommand({
12
- meta: {
13
- name: "list",
14
- description: "List workspaces"
15
- },
16
- args: {
17
- ...outputFlags,
18
- ...profileFlag,
19
- ...connectionFlags
20
- },
21
- outputSchema: WorkspaceListEnvelope,
22
- examples: ["metabase workspace list", "metabase workspace list --json"],
23
- async run({ ctx, getClient }) {
24
- const client = await getClient();
25
- const items = await client.requestParsed(WorkspaceApiList, "/api/ee/workspace-manager");
26
- renderList(wrapList(items), workspaceView, ctx);
27
- }
28
- });
29
-
30
- //#endregion
31
- export { list_default as default };