@metabase/cli 0.1.0-alpha.workspaces-commands.818a8f1

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 (107) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +762 -0
  3. package/dist/api-key-D9XxErQn.mjs +13 -0
  4. package/dist/archive-BPG5c88Y.mjs +38 -0
  5. package/dist/auth--Hpjwlaf.mjs +18 -0
  6. package/dist/body-DwU2s6Pg.mjs +19 -0
  7. package/dist/body-flags-7oqLhu5j.mjs +14 -0
  8. package/dist/branches-BbcoJXfp.mjs +41 -0
  9. package/dist/cancel-task-BDas45YO.mjs +29 -0
  10. package/dist/card-C31pGtBZ.mjs +113 -0
  11. package/dist/card-D4zZSPUb.mjs +19 -0
  12. package/dist/cli.d.mts +1 -0
  13. package/dist/cli.mjs +61 -0
  14. package/dist/command-augment-D9pI9Vbh.mjs +11 -0
  15. package/dist/create-Bd_U1zWU.mjs +124 -0
  16. package/dist/create-CCzsCZMm.mjs +47 -0
  17. package/dist/create-CwVcoq0O.mjs +43 -0
  18. package/dist/create-DpnjQvPw.mjs +43 -0
  19. package/dist/create-_UOeEXAj.mjs +39 -0
  20. package/dist/create-branch-sDttBORB.mjs +54 -0
  21. package/dist/credentials-C0xKke5D.mjs +84 -0
  22. package/dist/current-task-BGt1mqaX.mjs +35 -0
  23. package/dist/database-4V1iiPEx.mjs +17 -0
  24. package/dist/database-BTX5qbSv.mjs +33 -0
  25. package/dist/db-Dm2u2ISJ.mjs +17 -0
  26. package/dist/delete-DRBTgyus.mjs +47 -0
  27. package/dist/delete-DUC_stoL.mjs +47 -0
  28. package/dist/delete-runtime-inOVw3IX.mjs +58 -0
  29. package/dist/delete-table-9Is631O_.mjs +47 -0
  30. package/dist/deprovision-BAMzZc6f.mjs +60 -0
  31. package/dist/dirty-CLjHbz6J.mjs +32 -0
  32. package/dist/docker-QWVMG2gl.mjs +605 -0
  33. package/dist/eid-BNhutC1U.mjs +13 -0
  34. package/dist/export-D2Anfu3p.mjs +97 -0
  35. package/dist/field-Dhs2AND3.mjs +13 -0
  36. package/dist/field-QwBMAWsq.mjs +76 -0
  37. package/dist/flag-pair-CWvvzDJ_.mjs +17 -0
  38. package/dist/get-2po1uv9i.mjs +35 -0
  39. package/dist/get-BHJA78zg.mjs +35 -0
  40. package/dist/get-CAPLfawI.mjs +35 -0
  41. package/dist/get-CAVVmdMX.mjs +49 -0
  42. package/dist/get-DDWpubE8.mjs +36 -0
  43. package/dist/get-DhIoNeOp.mjs +35 -0
  44. package/dist/get-qPOsuTPw.mjs +35 -0
  45. package/dist/has-remote-changes-DAL5jetW.mjs +63 -0
  46. package/dist/import-CUMxUfSF.mjs +92 -0
  47. package/dist/input-BNqSFl38.mjs +33 -0
  48. package/dist/is-dirty-B10S6MG0.mjs +35 -0
  49. package/dist/is-dirty-CUuq-aB6.mjs +9 -0
  50. package/dist/key-CyhOpgWt.mjs +12 -0
  51. package/dist/license-DtsGJi3l.mjs +17 -0
  52. package/dist/list-B8s7Qnzk.mjs +31 -0
  53. package/dist/list-C5MGydGU.mjs +31 -0
  54. package/dist/list-DeFGwhhJ.mjs +60 -0
  55. package/dist/list-OBx5B3gd.mjs +39 -0
  56. package/dist/list-Y7iGsOfE.mjs +31 -0
  57. package/dist/list-evtQS7jl.mjs +39 -0
  58. package/dist/list-qetY9OIN.mjs +31 -0
  59. package/dist/login-Dqw9ZtCx.mjs +172 -0
  60. package/dist/logout-DwYJ5OUi.mjs +74 -0
  61. package/dist/logs-B_lrY7Js.mjs +57 -0
  62. package/dist/manifest-wzEFG0JB.mjs +124 -0
  63. package/dist/package-t8dKf4m_.mjs +73 -0
  64. package/dist/parse-id-C1prc9US.mjs +12 -0
  65. package/dist/poll-D2sXM5rc.mjs +49 -0
  66. package/dist/poll-task-Byiunmaj.mjs +194 -0
  67. package/dist/prompt-fXeNtj0M.mjs +40 -0
  68. package/dist/provision-DC4_HWZD.mjs +80 -0
  69. package/dist/ps-1bZKIwWh.mjs +9 -0
  70. package/dist/ps-BiOrecEe.mjs +78 -0
  71. package/dist/query-BnGVGeM3.mjs +100 -0
  72. package/dist/remove-Bx48o-0S.mjs +62 -0
  73. package/dist/remove-DecoZzNd.mjs +97 -0
  74. package/dist/render-DlBijc5i.mjs +179 -0
  75. package/dist/run-D4NgvaRh.mjs +87 -0
  76. package/dist/runtime-DUgFfYkN.mjs +950 -0
  77. package/dist/search-4wKx5ug2.mjs +171 -0
  78. package/dist/set-BZnCRL4c.mjs +66 -0
  79. package/dist/set-DCjrmTFm.mjs +66 -0
  80. package/dist/setting-C4vQSqer.mjs +18 -0
  81. package/dist/setting-DM7pm7yh.mjs +55 -0
  82. package/dist/setup-Dqh9hN6l.mjs +70 -0
  83. package/dist/start-xXQypG5L.mjs +324 -0
  84. package/dist/stash-ZZkmW_V7.mjs +106 -0
  85. package/dist/status-9KAPIpX8.mjs +31 -0
  86. package/dist/status-DezF-PIM.mjs +63 -0
  87. package/dist/status-JH6BZppo.mjs +55 -0
  88. package/dist/stop-br-ZOnve.mjs +80 -0
  89. package/dist/sync-C7VOWD00.mjs +26 -0
  90. package/dist/table-BvAr2ixC.mjs +75 -0
  91. package/dist/table-D-Mb5Nvw.mjs +16 -0
  92. package/dist/transform-CqxZwhGs.mjs +21 -0
  93. package/dist/transform-DfVkUttP.mjs +137 -0
  94. package/dist/transform-job-DuB_OjhO.mjs +91 -0
  95. package/dist/transform-job-HjbqjEoP.mjs +19 -0
  96. package/dist/translate-DJxDVAE4.mjs +110 -0
  97. package/dist/update-CDtm71m2.mjs +50 -0
  98. package/dist/update-DYVeVjk2.mjs +76 -0
  99. package/dist/update-DxKlQ0hP.mjs +50 -0
  100. package/dist/url-DP88YHNo.mjs +53 -0
  101. package/dist/wait-Cj_8wu4y.mjs +52 -0
  102. package/dist/wait-DwZN3ZwR.mjs +19 -0
  103. package/dist/wait-flags-CjW4ogUJ.mjs +35 -0
  104. package/dist/workspace-CbwR0vX_.mjs +24 -0
  105. package/dist/workspace-Dr9lWU3D.mjs +72 -0
  106. package/dist/workspace-credentials-q5RRFMT8.mjs +139 -0
  107. package/package.json +62 -0
@@ -0,0 +1,100 @@
1
+ import "./package-t8dKf4m_.mjs";
2
+ import "./command-augment-D9pI9Vbh.mjs";
3
+ import { renderItem } from "./render-DlBijc5i.mjs";
4
+ import { ConfigError, connectionFlags, defineMetabaseCommand, outputFlags, parseJson, profileFlag } from "./runtime-DUgFfYkN.mjs";
5
+ import { parseId } from "./parse-id-C1prc9US.mjs";
6
+ import { CardQueryResult, cardQueryView } from "./card-C31pGtBZ.mjs";
7
+ import { z } from "zod";
8
+ import { Writable } from "node:stream";
9
+
10
+ //#region src/output/stream.ts
11
+ async function pipeToStdout(stream) {
12
+ await stream.pipeTo(Writable.toWeb(process.stdout));
13
+ }
14
+
15
+ //#endregion
16
+ //#region src/commands/card/query.ts
17
+ const ExportFormat = z.enum(["csv", "xlsx"]);
18
+ const QueryParameters = z.array(z.unknown());
19
+ var query_default = defineMetabaseCommand({
20
+ meta: {
21
+ name: "query",
22
+ description: "Run a saved card and return results (json envelope, CSV, or XLSX)"
23
+ },
24
+ args: {
25
+ ...outputFlags,
26
+ ...profileFlag,
27
+ ...connectionFlags,
28
+ id: {
29
+ type: "positional",
30
+ description: "Card id",
31
+ required: true
32
+ },
33
+ "export-format": {
34
+ type: "string",
35
+ description: `Bypass JSON envelope and stream raw export: ${ExportFormat.options.join(" | ")}`
36
+ },
37
+ parameters: {
38
+ type: "string",
39
+ description: "JSON array of Metabase parameter objects to pass with the query"
40
+ },
41
+ limit: {
42
+ type: "string",
43
+ description: "Cap rows kept in the JSON envelope (no effect on csv/xlsx exports)"
44
+ }
45
+ },
46
+ outputSchema: CardQueryResult,
47
+ examples: [
48
+ "metabase card query 1",
49
+ "metabase card query 1 --json --limit 20",
50
+ "metabase card query 1 --export-format csv > results.csv",
51
+ "metabase card query 1 --parameters '[{\"type\":\"category\",\"value\":\"A\",\"target\":[\"variable\",[\"template-tag\",\"c\"]]}]'"
52
+ ],
53
+ async run({ args, ctx, getClient }) {
54
+ const id = parseId(args.id);
55
+ const parameters = parseParameters(args.parameters);
56
+ const client = await getClient();
57
+ const exportFormatRaw = args["export-format"];
58
+ if (exportFormatRaw !== void 0 && exportFormatRaw !== "") {
59
+ const exportFormat = parseExportFormat(exportFormatRaw);
60
+ await streamExport(client, id, exportFormat, parameters);
61
+ return;
62
+ }
63
+ const result = await client.requestParsed(CardQueryResult, `/api/card/${id}/query`, {
64
+ method: "POST",
65
+ body: { parameters }
66
+ });
67
+ const limit = args.limit === void 0 || args.limit === "" ? null : parseId(args.limit, "limit");
68
+ renderItem(applyLimit(result, limit), cardQueryView, ctx);
69
+ }
70
+ });
71
+ function parseParameters(raw) {
72
+ if (raw === void 0 || raw === "") return [];
73
+ return parseJson(raw, QueryParameters, { source: "--parameters" });
74
+ }
75
+ function parseExportFormat(raw) {
76
+ const result = ExportFormat.safeParse(raw);
77
+ if (!result.success) throw new ConfigError(`invalid --export-format: "${raw}" (expected: ${ExportFormat.options.join(", ")})`);
78
+ return result.data;
79
+ }
80
+ function applyLimit(result, limit) {
81
+ if (limit === null || result.status !== "completed" || result.data.rows.length <= limit) return result;
82
+ return {
83
+ ...result,
84
+ data: {
85
+ ...result.data,
86
+ rows: result.data.rows.slice(0, limit)
87
+ }
88
+ };
89
+ }
90
+ async function streamExport(client, id, format, parameters) {
91
+ const body = new URLSearchParams({ parameters: JSON.stringify(parameters) });
92
+ const stream = await client.requestStream(`/api/card/${id}/query/${format}`, {
93
+ method: "POST",
94
+ body
95
+ });
96
+ await pipeToStdout(stream);
97
+ }
98
+
99
+ //#endregion
100
+ export { query_default as default };
@@ -0,0 +1,62 @@
1
+ import "./package-t8dKf4m_.mjs";
2
+ import "./command-augment-D9pI9Vbh.mjs";
3
+ import { renderItem } from "./render-DlBijc5i.mjs";
4
+ import { ConfigError, clearLicense, defineMetabaseCommand, outputFlags } from "./runtime-DUgFfYkN.mjs";
5
+ import { promptConfirm } from "./prompt-fXeNtj0M.mjs";
6
+ import { z } from "zod";
7
+
8
+ //#region src/commands/license/remove.ts
9
+ const LicenseRemoveResult = z.object({
10
+ removed: z.boolean(),
11
+ aborted: z.boolean()
12
+ });
13
+ const licenseRemoveView = {
14
+ compactPick: LicenseRemoveResult,
15
+ tableColumns: [{
16
+ key: "removed",
17
+ label: "Removed"
18
+ }, {
19
+ key: "aborted",
20
+ label: "Aborted"
21
+ }]
22
+ };
23
+ var remove_default = defineMetabaseCommand({
24
+ meta: {
25
+ name: "remove",
26
+ description: "Remove the stored license token"
27
+ },
28
+ args: {
29
+ ...outputFlags,
30
+ yes: {
31
+ type: "boolean",
32
+ description: "Skip confirmation",
33
+ default: false
34
+ }
35
+ },
36
+ outputSchema: LicenseRemoveResult,
37
+ examples: ["metabase license remove --yes"],
38
+ async run({ args, ctx }) {
39
+ if (!args.yes) {
40
+ if (!process.stdin.isTTY) throw new ConfigError("--yes required to remove license non-interactively");
41
+ const ok = await promptConfirm({
42
+ message: "Remove stored license token?",
43
+ initialValue: false
44
+ });
45
+ if (!ok) {
46
+ renderItem({
47
+ removed: false,
48
+ aborted: true
49
+ }, licenseRemoveView, ctx);
50
+ return;
51
+ }
52
+ }
53
+ const removed = await clearLicense();
54
+ renderItem({
55
+ removed,
56
+ aborted: false
57
+ }, licenseRemoveView, ctx);
58
+ }
59
+ });
60
+
61
+ //#endregion
62
+ export { remove_default as default };
@@ -0,0 +1,97 @@
1
+ import "./package-t8dKf4m_.mjs";
2
+ import "./command-augment-D9pI9Vbh.mjs";
3
+ import { renderItem } from "./render-DlBijc5i.mjs";
4
+ import { defineMetabaseCommand, outputFlags } from "./runtime-DUgFfYkN.mjs";
5
+ import { parseId } from "./parse-id-C1prc9US.mjs";
6
+ import { promptConfirm } from "./prompt-fXeNtj0M.mjs";
7
+ import "./poll-D2sXM5rc.mjs";
8
+ import { checkDockerReady, containerNameFor, removeContainer, removeVolume, volumeNameFor } from "./docker-QWVMG2gl.mjs";
9
+ import { z } from "zod";
10
+
11
+ //#region src/commands/workspace/remove.ts
12
+ const RemoveResult = z.object({
13
+ workspace_id: z.number().int().positive(),
14
+ container_name: z.string(),
15
+ volume_name: z.string(),
16
+ removed_container: z.boolean(),
17
+ removed_volume: z.boolean()
18
+ });
19
+ const removeResultView = {
20
+ compactPick: RemoveResult.pick({
21
+ workspace_id: true,
22
+ removed_container: true,
23
+ removed_volume: true
24
+ }).strip(),
25
+ tableColumns: [
26
+ {
27
+ key: "workspace_id",
28
+ label: "ID"
29
+ },
30
+ {
31
+ key: "container_name",
32
+ label: "Container"
33
+ },
34
+ {
35
+ key: "volume_name",
36
+ label: "Volume"
37
+ },
38
+ {
39
+ key: "removed_container",
40
+ label: "Removed Container"
41
+ },
42
+ {
43
+ key: "removed_volume",
44
+ label: "Removed Volume"
45
+ }
46
+ ]
47
+ };
48
+ var remove_default = defineMetabaseCommand({
49
+ meta: {
50
+ name: "remove",
51
+ description: "Stop and remove the local container + app-db volume (does not affect remote)"
52
+ },
53
+ args: {
54
+ ...outputFlags,
55
+ id: {
56
+ type: "positional",
57
+ description: "Workspace id",
58
+ required: true
59
+ },
60
+ "keep-volume": {
61
+ type: "boolean",
62
+ description: "Keep the workspace's app-db volume (faster restart, app-db survives)",
63
+ default: false
64
+ },
65
+ yes: {
66
+ type: "boolean",
67
+ description: "Skip the confirmation prompt",
68
+ default: false
69
+ }
70
+ },
71
+ outputSchema: RemoveResult,
72
+ examples: ["metabase workspace remove 1 --yes", "metabase workspace remove 1 --keep-volume --yes"],
73
+ async run({ args, ctx }) {
74
+ const workspaceId = parseId(args.id);
75
+ const containerName = containerNameFor(workspaceId);
76
+ const volumeName = volumeNameFor(workspaceId);
77
+ const shouldRemoveVolume = args["keep-volume"] !== true;
78
+ await checkDockerReady();
79
+ if (!args.yes) {
80
+ const confirmed = await promptConfirm({ message: shouldRemoveVolume ? `Remove container ${containerName} and its app-db volume ${volumeName}?` : `Remove container ${containerName}? (volume ${volumeName} will be kept)` });
81
+ if (!confirmed) return;
82
+ }
83
+ const removedContainer = await removeContainer(containerName);
84
+ const removedVolume = shouldRemoveVolume ? await removeVolume(volumeName) : false;
85
+ const result = {
86
+ workspace_id: workspaceId,
87
+ container_name: containerName,
88
+ volume_name: volumeName,
89
+ removed_container: removedContainer,
90
+ removed_volume: removedVolume
91
+ };
92
+ renderItem(result, removeResultView, ctx);
93
+ }
94
+ });
95
+
96
+ //#endregion
97
+ export { remove_default as default };
@@ -0,0 +1,179 @@
1
+ import { ConfigError } from "./runtime-DUgFfYkN.mjs";
2
+ import Table from "cli-table3";
3
+
4
+ //#region src/output/cap.ts
5
+ function capListEnvelope(envelope, maxBytes) {
6
+ if (maxBytes <= 0) return envelope;
7
+ const fullBytes = jsonByteLength(envelope);
8
+ if (fullBytes <= maxBytes) return envelope;
9
+ let lo = 0;
10
+ let hi = envelope.data.length;
11
+ while (lo < hi) {
12
+ const mid = Math.ceil((lo + hi) / 2);
13
+ if (jsonByteLength(truncate(envelope, mid, fullBytes)) <= maxBytes) lo = mid;
14
+ else hi = mid - 1;
15
+ }
16
+ return truncate(envelope, lo, fullBytes);
17
+ }
18
+ function truncate(envelope, count, originalBytes) {
19
+ return {
20
+ ...envelope,
21
+ data: envelope.data.slice(0, count),
22
+ returned: count,
23
+ truncated: {
24
+ reason: "max_bytes",
25
+ bytes: originalBytes
26
+ }
27
+ };
28
+ }
29
+ function jsonByteLength(value) {
30
+ return Buffer.byteLength(JSON.stringify(value), "utf8");
31
+ }
32
+
33
+ //#endregion
34
+ //#region src/output/notice.ts
35
+ function warn(message) {
36
+ process.stderr.write(message + "\n");
37
+ }
38
+ function listTruncationNotice(bytes) {
39
+ return `… cut at ${bytes} bytes; rerun with --max-bytes 0`;
40
+ }
41
+ function itemOversizeNotice(bytes) {
42
+ return `… item is ${bytes} bytes (exceeds --max-bytes); narrow with --fields, or pass --max-bytes 0`;
43
+ }
44
+
45
+ //#endregion
46
+ //#region src/output/projection.ts
47
+ function applyProjection(value, view, full, fields) {
48
+ if (fields !== void 0) {
49
+ if (fields.length === 0) throw new ConfigError("--fields requires at least one path");
50
+ return projectFields(value, fields);
51
+ }
52
+ if (full) return value;
53
+ const parsed = view.compactPick.safeParse(value);
54
+ if (parsed.success) return parsed.data;
55
+ throw new ConfigError(`compact projection failed: ${parsed.error.message}`);
56
+ }
57
+ function projectFields(value, fields) {
58
+ const out = {};
59
+ for (const path of fields) {
60
+ if (path.length === 0) throw new ConfigError(`empty field path`);
61
+ const parts = path.split(".");
62
+ if (parts.some((part) => part.length === 0)) throw new ConfigError(`invalid field path: "${path}"`);
63
+ setPath(out, parts, pickPath(value, parts));
64
+ }
65
+ return out;
66
+ }
67
+ function pickPath(value, parts) {
68
+ let cursor = value;
69
+ for (const part of parts) {
70
+ if (!isPlainObject(cursor) || !Object.hasOwn(cursor, part)) throw new ConfigError(`unknown field path: "${parts.join(".")}"`);
71
+ cursor = Reflect.get(cursor, part);
72
+ }
73
+ return cursor;
74
+ }
75
+ function setPath(target, parts, value) {
76
+ let cursor = target;
77
+ const lastIndex = parts.length - 1;
78
+ for (const [index, part] of parts.entries()) {
79
+ if (index === lastIndex) {
80
+ cursor[part] = value;
81
+ return;
82
+ }
83
+ const existing = cursor[part];
84
+ if (isPlainObject(existing)) cursor = existing;
85
+ else {
86
+ const next = {};
87
+ cursor[part] = next;
88
+ cursor = next;
89
+ }
90
+ }
91
+ }
92
+ function isPlainObject(value) {
93
+ return typeof value === "object" && value !== null && !Array.isArray(value);
94
+ }
95
+
96
+ //#endregion
97
+ //#region src/output/table.ts
98
+ function renderTable(rows, columns) {
99
+ const head = columns.map((column) => column.label ?? column.key);
100
+ const widths = columns.map((column) => column.width ?? null);
101
+ const hasWidth = widths.some((width) => width !== null);
102
+ const table = new Table(hasWidth ? {
103
+ head,
104
+ colWidths: widths
105
+ } : { head });
106
+ for (const row of rows) table.push(columns.map((column) => formatCell(row, column)));
107
+ return table.toString();
108
+ }
109
+ function formatCell(row, column) {
110
+ const value = row[column.key];
111
+ if (column.format !== void 0) return column.format(value);
112
+ return formatScalar(value);
113
+ }
114
+ function formatScalar(value) {
115
+ if (value === null || value === void 0) return "";
116
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return String(value);
117
+ return JSON.stringify(value);
118
+ }
119
+
120
+ //#endregion
121
+ //#region src/output/render.ts
122
+ function renderItem(item, view, opts) {
123
+ const projected = applyProjection(item, view, opts.full, opts.fields);
124
+ const body = renderItemBody(item, view, projected, opts) + "\n";
125
+ process.stdout.write(body);
126
+ emitItemOversizeNotice(body, opts.maxBytes);
127
+ }
128
+ function renderList(envelope, view, opts) {
129
+ if (opts.format === "json" || opts.fields !== void 0) {
130
+ renderJsonEnvelope(envelope, view, opts);
131
+ return;
132
+ }
133
+ if (envelope.data.length === 0) {
134
+ process.stdout.write("(no results)\n");
135
+ return;
136
+ }
137
+ const capped = capListEnvelope(envelope, opts.maxBytes);
138
+ process.stdout.write(renderTable(capped.data, view.tableColumns) + "\n");
139
+ if (capped.truncated !== void 0) warn(listTruncationNotice(capped.truncated.bytes));
140
+ }
141
+ function renderJsonEnvelope(envelope, view, opts) {
142
+ const projectedItems = envelope.data.map((item) => applyProjection(item, view, opts.full, opts.fields));
143
+ const projectedEnvelope = {
144
+ ...envelope,
145
+ data: projectedItems
146
+ };
147
+ const capped = capListEnvelope(projectedEnvelope, opts.maxBytes);
148
+ process.stdout.write(JSON.stringify(capped, null, 2) + "\n");
149
+ if (capped.truncated !== void 0) warn(listTruncationNotice(capped.truncated.bytes));
150
+ }
151
+ function renderItemBody(item, view, projected, opts) {
152
+ if (opts.format === "json" || opts.fields !== void 0) return JSON.stringify(projected, null, 2);
153
+ if (!opts.full) return renderKeyValueLines(columnPairs(item, view.tableColumns));
154
+ return renderKeyValueLines(objectPairs(projected));
155
+ }
156
+ function columnPairs(item, columns) {
157
+ return columns.map((column) => [column.label ?? column.key, formatCell(item, column)]);
158
+ }
159
+ function objectPairs(value) {
160
+ if (!isPlainObject(value)) {
161
+ const scalar = formatScalar(value);
162
+ return scalar === "" ? [] : [["", scalar]];
163
+ }
164
+ return Object.entries(value).map(([key, raw]) => [key, formatScalar(raw)]);
165
+ }
166
+ function renderKeyValueLines(pairs) {
167
+ if (pairs.length === 0) return "";
168
+ const padding = Math.max(...pairs.map(([label]) => label.length));
169
+ return pairs.map(([label, value]) => `${label.padEnd(padding)} ${value}`).join("\n");
170
+ }
171
+ function emitItemOversizeNotice(body, maxBytes) {
172
+ if (maxBytes <= 0) return;
173
+ const bytes = Buffer.byteLength(body, "utf8");
174
+ if (bytes <= maxBytes) return;
175
+ warn(itemOversizeNotice(bytes));
176
+ }
177
+
178
+ //#endregion
179
+ export { renderItem, renderList, warn };
@@ -0,0 +1,87 @@
1
+ import "./package-t8dKf4m_.mjs";
2
+ import "./command-augment-D9pI9Vbh.mjs";
3
+ import { renderItem } from "./render-DlBijc5i.mjs";
4
+ import { connectionFlags, defineMetabaseCommand, outputFlags, profileFlag } from "./runtime-DUgFfYkN.mjs";
5
+ import { parseId } from "./parse-id-C1prc9US.mjs";
6
+ import { pollUntil } from "./poll-D2sXM5rc.mjs";
7
+ import { TransformRun } from "./transform-DfVkUttP.mjs";
8
+ import { parseWaitFlags, waitFlags } from "./wait-flags-CjW4ogUJ.mjs";
9
+ import { z } from "zod";
10
+
11
+ //#region src/commands/transform/run.ts
12
+ const RUN_TERMINAL_STATUSES = new Set([
13
+ "succeeded",
14
+ "failed",
15
+ "timeout",
16
+ "canceled"
17
+ ]);
18
+ const RUN_FAILURE_STATUSES = new Set([
19
+ "failed",
20
+ "timeout",
21
+ "canceled"
22
+ ]);
23
+ const TransformRunKickoff = z.object({
24
+ message: z.string(),
25
+ run_id: z.number().int().positive().nullable()
26
+ });
27
+ const TransformRunResult = z.object({
28
+ message: z.string(),
29
+ run_id: z.number().int().positive().nullable(),
30
+ final: TransformRun.nullable().optional()
31
+ });
32
+ const transformRunView = {
33
+ compactPick: TransformRunResult,
34
+ tableColumns: [{
35
+ key: "run_id",
36
+ label: "Run ID"
37
+ }, {
38
+ key: "message",
39
+ label: "Message"
40
+ }]
41
+ };
42
+ var run_default = defineMetabaseCommand({
43
+ meta: {
44
+ name: "run",
45
+ description: "Trigger a transform run by id"
46
+ },
47
+ args: {
48
+ ...outputFlags,
49
+ ...profileFlag,
50
+ ...connectionFlags,
51
+ ...waitFlags,
52
+ id: {
53
+ type: "positional",
54
+ description: "Transform id",
55
+ required: true
56
+ }
57
+ },
58
+ outputSchema: TransformRunResult,
59
+ examples: ["metabase transform run 1", "metabase transform run 1 --wait --json"],
60
+ async run({ args, ctx, getClient }) {
61
+ const id = parseId(args.id);
62
+ const wait = parseWaitFlags(args);
63
+ const client = await getClient();
64
+ const kickoff = await client.requestParsed(TransformRunKickoff, `/api/transform/${id}/run`, { method: "POST" });
65
+ if (!wait.enabled || kickoff.run_id === null) {
66
+ renderItem({
67
+ message: kickoff.message,
68
+ run_id: kickoff.run_id
69
+ }, transformRunView, ctx);
70
+ return;
71
+ }
72
+ const runId = kickoff.run_id;
73
+ const final = await pollUntil(async () => client.requestParsed(TransformRun, `/api/transform/run/${runId}`), (run) => RUN_TERMINAL_STATUSES.has(run.status), wait.schedule);
74
+ renderItem({
75
+ message: kickoff.message,
76
+ run_id: runId,
77
+ final
78
+ }, transformRunView, ctx);
79
+ if (RUN_FAILURE_STATUSES.has(final.status)) {
80
+ const detail = final.message ? `: ${final.message}` : "";
81
+ throw new Error(`transform run ${runId} ${final.status}${detail}`);
82
+ }
83
+ }
84
+ });
85
+
86
+ //#endregion
87
+ export { run_default as default };