@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.
- package/LICENSE +661 -0
- package/README.md +762 -0
- package/dist/api-key-D9XxErQn.mjs +13 -0
- package/dist/archive-BPG5c88Y.mjs +38 -0
- package/dist/auth--Hpjwlaf.mjs +18 -0
- package/dist/body-DwU2s6Pg.mjs +19 -0
- package/dist/body-flags-7oqLhu5j.mjs +14 -0
- package/dist/branches-BbcoJXfp.mjs +41 -0
- package/dist/cancel-task-BDas45YO.mjs +29 -0
- package/dist/card-C31pGtBZ.mjs +113 -0
- package/dist/card-D4zZSPUb.mjs +19 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +61 -0
- package/dist/command-augment-D9pI9Vbh.mjs +11 -0
- package/dist/create-Bd_U1zWU.mjs +124 -0
- package/dist/create-CCzsCZMm.mjs +47 -0
- package/dist/create-CwVcoq0O.mjs +43 -0
- package/dist/create-DpnjQvPw.mjs +43 -0
- package/dist/create-_UOeEXAj.mjs +39 -0
- package/dist/create-branch-sDttBORB.mjs +54 -0
- package/dist/credentials-C0xKke5D.mjs +84 -0
- package/dist/current-task-BGt1mqaX.mjs +35 -0
- package/dist/database-4V1iiPEx.mjs +17 -0
- package/dist/database-BTX5qbSv.mjs +33 -0
- package/dist/db-Dm2u2ISJ.mjs +17 -0
- package/dist/delete-DRBTgyus.mjs +47 -0
- package/dist/delete-DUC_stoL.mjs +47 -0
- package/dist/delete-runtime-inOVw3IX.mjs +58 -0
- package/dist/delete-table-9Is631O_.mjs +47 -0
- package/dist/deprovision-BAMzZc6f.mjs +60 -0
- package/dist/dirty-CLjHbz6J.mjs +32 -0
- package/dist/docker-QWVMG2gl.mjs +605 -0
- package/dist/eid-BNhutC1U.mjs +13 -0
- package/dist/export-D2Anfu3p.mjs +97 -0
- package/dist/field-Dhs2AND3.mjs +13 -0
- package/dist/field-QwBMAWsq.mjs +76 -0
- package/dist/flag-pair-CWvvzDJ_.mjs +17 -0
- package/dist/get-2po1uv9i.mjs +35 -0
- package/dist/get-BHJA78zg.mjs +35 -0
- package/dist/get-CAPLfawI.mjs +35 -0
- package/dist/get-CAVVmdMX.mjs +49 -0
- package/dist/get-DDWpubE8.mjs +36 -0
- package/dist/get-DhIoNeOp.mjs +35 -0
- package/dist/get-qPOsuTPw.mjs +35 -0
- package/dist/has-remote-changes-DAL5jetW.mjs +63 -0
- package/dist/import-CUMxUfSF.mjs +92 -0
- package/dist/input-BNqSFl38.mjs +33 -0
- package/dist/is-dirty-B10S6MG0.mjs +35 -0
- package/dist/is-dirty-CUuq-aB6.mjs +9 -0
- package/dist/key-CyhOpgWt.mjs +12 -0
- package/dist/license-DtsGJi3l.mjs +17 -0
- package/dist/list-B8s7Qnzk.mjs +31 -0
- package/dist/list-C5MGydGU.mjs +31 -0
- package/dist/list-DeFGwhhJ.mjs +60 -0
- package/dist/list-OBx5B3gd.mjs +39 -0
- package/dist/list-Y7iGsOfE.mjs +31 -0
- package/dist/list-evtQS7jl.mjs +39 -0
- package/dist/list-qetY9OIN.mjs +31 -0
- package/dist/login-Dqw9ZtCx.mjs +172 -0
- package/dist/logout-DwYJ5OUi.mjs +74 -0
- package/dist/logs-B_lrY7Js.mjs +57 -0
- package/dist/manifest-wzEFG0JB.mjs +124 -0
- package/dist/package-t8dKf4m_.mjs +73 -0
- package/dist/parse-id-C1prc9US.mjs +12 -0
- package/dist/poll-D2sXM5rc.mjs +49 -0
- package/dist/poll-task-Byiunmaj.mjs +194 -0
- package/dist/prompt-fXeNtj0M.mjs +40 -0
- package/dist/provision-DC4_HWZD.mjs +80 -0
- package/dist/ps-1bZKIwWh.mjs +9 -0
- package/dist/ps-BiOrecEe.mjs +78 -0
- package/dist/query-BnGVGeM3.mjs +100 -0
- package/dist/remove-Bx48o-0S.mjs +62 -0
- package/dist/remove-DecoZzNd.mjs +97 -0
- package/dist/render-DlBijc5i.mjs +179 -0
- package/dist/run-D4NgvaRh.mjs +87 -0
- package/dist/runtime-DUgFfYkN.mjs +950 -0
- package/dist/search-4wKx5ug2.mjs +171 -0
- package/dist/set-BZnCRL4c.mjs +66 -0
- package/dist/set-DCjrmTFm.mjs +66 -0
- package/dist/setting-C4vQSqer.mjs +18 -0
- package/dist/setting-DM7pm7yh.mjs +55 -0
- package/dist/setup-Dqh9hN6l.mjs +70 -0
- package/dist/start-xXQypG5L.mjs +324 -0
- package/dist/stash-ZZkmW_V7.mjs +106 -0
- package/dist/status-9KAPIpX8.mjs +31 -0
- package/dist/status-DezF-PIM.mjs +63 -0
- package/dist/status-JH6BZppo.mjs +55 -0
- package/dist/stop-br-ZOnve.mjs +80 -0
- package/dist/sync-C7VOWD00.mjs +26 -0
- package/dist/table-BvAr2ixC.mjs +75 -0
- package/dist/table-D-Mb5Nvw.mjs +16 -0
- package/dist/transform-CqxZwhGs.mjs +21 -0
- package/dist/transform-DfVkUttP.mjs +137 -0
- package/dist/transform-job-DuB_OjhO.mjs +91 -0
- package/dist/transform-job-HjbqjEoP.mjs +19 -0
- package/dist/translate-DJxDVAE4.mjs +110 -0
- package/dist/update-CDtm71m2.mjs +50 -0
- package/dist/update-DYVeVjk2.mjs +76 -0
- package/dist/update-DxKlQ0hP.mjs +50 -0
- package/dist/url-DP88YHNo.mjs +53 -0
- package/dist/wait-Cj_8wu4y.mjs +52 -0
- package/dist/wait-DwZN3ZwR.mjs +19 -0
- package/dist/wait-flags-CjW4ogUJ.mjs +35 -0
- package/dist/workspace-CbwR0vX_.mjs +24 -0
- package/dist/workspace-Dr9lWU3D.mjs +72 -0
- package/dist/workspace-credentials-q5RRFMT8.mjs +139 -0
- package/package.json +62 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { getMetabaseAugment } from "./command-augment-D9pI9Vbh.mjs";
|
|
2
|
+
import { defineCommand } from "citty";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
//#region src/output/manifest.ts
|
|
6
|
+
function writeManifest(manifest) {
|
|
7
|
+
process.stdout.write(JSON.stringify(manifest, null, 2) + "\n");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/runtime/manifest.ts
|
|
12
|
+
const ManifestArg = z.object({
|
|
13
|
+
name: z.string(),
|
|
14
|
+
type: z.enum([
|
|
15
|
+
"string",
|
|
16
|
+
"boolean",
|
|
17
|
+
"positional",
|
|
18
|
+
"enum"
|
|
19
|
+
]),
|
|
20
|
+
required: z.boolean(),
|
|
21
|
+
description: z.string().optional(),
|
|
22
|
+
default: z.union([
|
|
23
|
+
z.string(),
|
|
24
|
+
z.boolean(),
|
|
25
|
+
z.number()
|
|
26
|
+
]).optional(),
|
|
27
|
+
alias: z.array(z.string()).optional(),
|
|
28
|
+
options: z.array(z.string()).optional()
|
|
29
|
+
});
|
|
30
|
+
const ManifestEntry = z.object({
|
|
31
|
+
command: z.string(),
|
|
32
|
+
description: z.string(),
|
|
33
|
+
examples: z.array(z.string()),
|
|
34
|
+
args: z.array(ManifestArg),
|
|
35
|
+
outputSchema: z.unknown().nullable()
|
|
36
|
+
});
|
|
37
|
+
const Manifest = z.object({
|
|
38
|
+
version: z.literal(1),
|
|
39
|
+
commands: z.array(ManifestEntry)
|
|
40
|
+
});
|
|
41
|
+
async function buildManifest(root) {
|
|
42
|
+
return {
|
|
43
|
+
version: 1,
|
|
44
|
+
commands: await walk(root, [])
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
async function walk(cmd, path) {
|
|
48
|
+
const meta = await resolveCitty(cmd.meta);
|
|
49
|
+
if (meta?.hidden === true) return [];
|
|
50
|
+
const subCommands = await resolveCitty(cmd.subCommands);
|
|
51
|
+
if (subCommands && Object.keys(subCommands).length > 0) {
|
|
52
|
+
const groups = await Promise.all(Object.entries(subCommands).map(async ([name, lazy]) => {
|
|
53
|
+
const sub = await resolveCitty(lazy);
|
|
54
|
+
return sub ? walk(sub, [...path, name]) : [];
|
|
55
|
+
}));
|
|
56
|
+
return groups.flat();
|
|
57
|
+
}
|
|
58
|
+
const args = await resolveCitty(cmd.args) ?? {};
|
|
59
|
+
const augment = getMetabaseAugment(cmd);
|
|
60
|
+
return [{
|
|
61
|
+
command: path.join(" "),
|
|
62
|
+
description: readDescription(meta),
|
|
63
|
+
examples: augment ? Array.from(augment.examples) : [],
|
|
64
|
+
args: convertArgs(args),
|
|
65
|
+
outputSchema: augment?.outputSchema ? z.toJSONSchema(augment.outputSchema) : null
|
|
66
|
+
}];
|
|
67
|
+
}
|
|
68
|
+
function readDescription(meta) {
|
|
69
|
+
if (meta === void 0) return "";
|
|
70
|
+
return typeof meta.description === "string" ? meta.description : "";
|
|
71
|
+
}
|
|
72
|
+
function convertArgs(args) {
|
|
73
|
+
return Object.entries(args).map(([name, def]) => convertArg(name, def));
|
|
74
|
+
}
|
|
75
|
+
function convertArg(name, def) {
|
|
76
|
+
const arg = {
|
|
77
|
+
name,
|
|
78
|
+
type: def.type ?? "string",
|
|
79
|
+
required: def.required === true && def.default === void 0
|
|
80
|
+
};
|
|
81
|
+
if (def.description) arg.description = def.description;
|
|
82
|
+
if (isPrimitiveDefault(def.default)) arg.default = def.default;
|
|
83
|
+
const alias = readAlias(def);
|
|
84
|
+
if (alias.length > 0) arg.alias = alias;
|
|
85
|
+
const options = readOptions(def);
|
|
86
|
+
if (options.length > 0) arg.options = options;
|
|
87
|
+
return arg;
|
|
88
|
+
}
|
|
89
|
+
function readOptions(def) {
|
|
90
|
+
if (!("options" in def) || !Array.isArray(def.options)) return [];
|
|
91
|
+
return [...def.options];
|
|
92
|
+
}
|
|
93
|
+
function readAlias(def) {
|
|
94
|
+
if (!("alias" in def) || def.alias === void 0) return [];
|
|
95
|
+
return Array.isArray(def.alias) ? def.alias : [def.alias];
|
|
96
|
+
}
|
|
97
|
+
function isPrimitiveDefault(value) {
|
|
98
|
+
return typeof value === "string" || typeof value === "boolean" || typeof value === "number";
|
|
99
|
+
}
|
|
100
|
+
async function resolveCitty(value) {
|
|
101
|
+
if (value === void 0) return void 0;
|
|
102
|
+
if (typeof value === "function") return value();
|
|
103
|
+
return value;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
//#endregion
|
|
107
|
+
//#region src/commands/manifest.ts
|
|
108
|
+
function createManifestCommand(root) {
|
|
109
|
+
return defineCommand({
|
|
110
|
+
meta: {
|
|
111
|
+
name: "__manifest",
|
|
112
|
+
description: "Emit machine-readable command manifest as JSON (for agents)",
|
|
113
|
+
hidden: true
|
|
114
|
+
},
|
|
115
|
+
args: {},
|
|
116
|
+
async run() {
|
|
117
|
+
const manifest = await buildManifest(root);
|
|
118
|
+
writeManifest(manifest);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
//#endregion
|
|
124
|
+
export { createManifestCommand };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
//#region package.json
|
|
2
|
+
var name = "@metabase/cli";
|
|
3
|
+
var version = "0.1.0-alpha.workspaces-commands.818a8f1";
|
|
4
|
+
var description = "Metabase CLI";
|
|
5
|
+
var license = "AGPL-3.0";
|
|
6
|
+
var repository = {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/metabase/mb-cli.git"
|
|
9
|
+
};
|
|
10
|
+
var bin = { "metabase": "./dist/cli.mjs" };
|
|
11
|
+
var files = ["dist"];
|
|
12
|
+
var type = "module";
|
|
13
|
+
var publishConfig = {
|
|
14
|
+
"access": "public",
|
|
15
|
+
"registry": "https://registry.npmjs.org/"
|
|
16
|
+
};
|
|
17
|
+
var scripts = {
|
|
18
|
+
"build": "tsdown",
|
|
19
|
+
"dev": "tsdown --watch",
|
|
20
|
+
"start": "node dist/cli.mjs",
|
|
21
|
+
"test": "vitest run --project unit",
|
|
22
|
+
"test:watch": "vitest --project unit",
|
|
23
|
+
"test:e2e": "vitest run --project e2e",
|
|
24
|
+
"test:e2e:watch": "vitest --project e2e",
|
|
25
|
+
"e2e:up": "docker compose -f tests/e2e/docker-compose.yml up -d --wait",
|
|
26
|
+
"e2e:down": "docker compose -f tests/e2e/docker-compose.yml down -v",
|
|
27
|
+
"e2e:bootstrap": "bun tests/e2e/setup/bootstrap.ts",
|
|
28
|
+
"e2e:logs": "docker compose -f tests/e2e/docker-compose.yml logs -f metabase",
|
|
29
|
+
"typecheck": "tsc --noEmit",
|
|
30
|
+
"lint": "oxlint",
|
|
31
|
+
"lint:fix": "oxlint --fix",
|
|
32
|
+
"format": "oxfmt",
|
|
33
|
+
"format:check": "oxfmt --check",
|
|
34
|
+
"prepublishOnly": "tsdown && publint"
|
|
35
|
+
};
|
|
36
|
+
var dependencies = {
|
|
37
|
+
"@clack/prompts": "^0.8.2",
|
|
38
|
+
"@napi-rs/keyring": "^1.3.0",
|
|
39
|
+
"citty": "^0.2.2",
|
|
40
|
+
"cli-table3": "^0.6.5",
|
|
41
|
+
"yaml": "^2.8.4",
|
|
42
|
+
"zod": "^4.0.0"
|
|
43
|
+
};
|
|
44
|
+
var devDependencies = {
|
|
45
|
+
"@types/node": "^22.10.0",
|
|
46
|
+
"execa": "^9.5.2",
|
|
47
|
+
"fast-check": "^4.7.0",
|
|
48
|
+
"oxfmt": "^0.47.0",
|
|
49
|
+
"oxlint": "^1.62.0",
|
|
50
|
+
"publint": "^0.3.0",
|
|
51
|
+
"tsdown": "^0.9.0",
|
|
52
|
+
"typescript": "^5.7.2",
|
|
53
|
+
"vitest": "^2.1.8"
|
|
54
|
+
};
|
|
55
|
+
var engines = { "node": ">=20.6" };
|
|
56
|
+
var package_default = {
|
|
57
|
+
name,
|
|
58
|
+
version,
|
|
59
|
+
description,
|
|
60
|
+
license,
|
|
61
|
+
repository,
|
|
62
|
+
bin,
|
|
63
|
+
files,
|
|
64
|
+
type,
|
|
65
|
+
publishConfig,
|
|
66
|
+
scripts,
|
|
67
|
+
dependencies,
|
|
68
|
+
devDependencies,
|
|
69
|
+
engines
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
//#endregion
|
|
73
|
+
export { package_default };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { TimeoutError, combineAborts, throwIfAborted } from "./runtime-DUgFfYkN.mjs";
|
|
2
|
+
import { setTimeout } from "node:timers/promises";
|
|
3
|
+
|
|
4
|
+
//#region src/runtime/poll.ts
|
|
5
|
+
const DEFAULT_INTERVAL_MS = 2e3;
|
|
6
|
+
const DEFAULT_MAX_INTERVAL_MS = 3e4;
|
|
7
|
+
const DEFAULT_TIMEOUT_MS = 6e5;
|
|
8
|
+
async function pollUntil(fn, done, opts = {}) {
|
|
9
|
+
const intervalMs = opts.intervalMs ?? DEFAULT_INTERVAL_MS;
|
|
10
|
+
const maxIntervalMs = opts.maxIntervalMs ?? DEFAULT_MAX_INTERVAL_MS;
|
|
11
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
12
|
+
const backoff = opts.backoff ?? "fixed";
|
|
13
|
+
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
14
|
+
const { combined, processSignal } = combineAborts(timeoutSignal, opts.signal);
|
|
15
|
+
let interval = intervalMs;
|
|
16
|
+
let attempts = 0;
|
|
17
|
+
while (true) {
|
|
18
|
+
throwIfAborted(processSignal);
|
|
19
|
+
if (combined.aborted) throw pollTimeout(timeoutMs, attempts);
|
|
20
|
+
let value;
|
|
21
|
+
try {
|
|
22
|
+
value = await fn(combined);
|
|
23
|
+
} catch (error) {
|
|
24
|
+
throwIfAborted(processSignal);
|
|
25
|
+
if (combined.aborted) throw pollTimeout(timeoutMs, attempts);
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
attempts += 1;
|
|
29
|
+
if (done(value)) return value;
|
|
30
|
+
try {
|
|
31
|
+
await setTimeout(interval, void 0, { signal: combined });
|
|
32
|
+
} catch (error) {
|
|
33
|
+
throwIfAborted(processSignal);
|
|
34
|
+
if (combined.aborted) throw pollTimeout(timeoutMs, attempts);
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
if (backoff === "exponential") interval = Math.min(interval * 2, maxIntervalMs);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function pollTimeout(timeoutMs, attempts) {
|
|
41
|
+
return new TimeoutError(`Polling timed out after ${timeoutMs}ms`, {
|
|
42
|
+
kind: "polling",
|
|
43
|
+
timeoutMs,
|
|
44
|
+
attempts
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
//#endregion
|
|
49
|
+
export { DEFAULT_INTERVAL_MS, DEFAULT_TIMEOUT_MS, pollUntil };
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { parseJson } from "./runtime-DUgFfYkN.mjs";
|
|
2
|
+
import { DEFAULT_INTERVAL_MS, DEFAULT_TIMEOUT_MS, pollUntil } from "./poll-D2sXM5rc.mjs";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
//#region src/domain/remote-sync.ts
|
|
6
|
+
const SyncTaskStatus = z.enum([
|
|
7
|
+
"running",
|
|
8
|
+
"successful",
|
|
9
|
+
"errored",
|
|
10
|
+
"cancelled",
|
|
11
|
+
"timed-out",
|
|
12
|
+
"conflict"
|
|
13
|
+
]);
|
|
14
|
+
const SyncTaskType = z.enum(["import", "export"]);
|
|
15
|
+
const SyncTask = z.object({
|
|
16
|
+
id: z.number().int().positive(),
|
|
17
|
+
sync_task_type: SyncTaskType,
|
|
18
|
+
status: SyncTaskStatus,
|
|
19
|
+
progress: z.number().min(0).max(1).nullable(),
|
|
20
|
+
started_at: z.string(),
|
|
21
|
+
ended_at: z.string().nullable().optional(),
|
|
22
|
+
last_progress_report_at: z.string().nullable().optional(),
|
|
23
|
+
version: z.string().nullable().optional(),
|
|
24
|
+
initiated_by: z.number().int().positive().nullable().optional(),
|
|
25
|
+
cancelled: z.boolean().nullable().optional(),
|
|
26
|
+
error_message: z.string().nullable().optional(),
|
|
27
|
+
conflicts: z.array(z.string()).nullable().optional()
|
|
28
|
+
}).loose();
|
|
29
|
+
const SyncTaskCompact = SyncTask.pick({
|
|
30
|
+
id: true,
|
|
31
|
+
sync_task_type: true,
|
|
32
|
+
status: true,
|
|
33
|
+
progress: true,
|
|
34
|
+
version: true,
|
|
35
|
+
error_message: true
|
|
36
|
+
}).strip();
|
|
37
|
+
const syncTaskView = {
|
|
38
|
+
compactPick: SyncTaskCompact,
|
|
39
|
+
tableColumns: [
|
|
40
|
+
{
|
|
41
|
+
key: "id",
|
|
42
|
+
label: "ID"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
key: "sync_task_type",
|
|
46
|
+
label: "Type"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
key: "status",
|
|
50
|
+
label: "Status"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
key: "progress",
|
|
54
|
+
label: "Progress"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
key: "version",
|
|
58
|
+
label: "Version"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
key: "error_message",
|
|
62
|
+
label: "Error"
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
};
|
|
66
|
+
const SyncDirtyItem = z.object({
|
|
67
|
+
id: z.number().int(),
|
|
68
|
+
name: z.string().nullable(),
|
|
69
|
+
model: z.string(),
|
|
70
|
+
sync_status: z.string(),
|
|
71
|
+
collection_id: z.number().int().positive().nullable().optional(),
|
|
72
|
+
description: z.string().nullable().optional(),
|
|
73
|
+
display: z.string().nullable().optional(),
|
|
74
|
+
query_type: z.string().nullable().optional(),
|
|
75
|
+
table_id: z.number().int().positive().nullable().optional(),
|
|
76
|
+
table_name: z.string().nullable().optional()
|
|
77
|
+
}).loose();
|
|
78
|
+
const SyncDirtyItemCompact = SyncDirtyItem.pick({
|
|
79
|
+
id: true,
|
|
80
|
+
name: true,
|
|
81
|
+
model: true,
|
|
82
|
+
sync_status: true,
|
|
83
|
+
collection_id: true
|
|
84
|
+
}).strip();
|
|
85
|
+
const syncDirtyItemView = {
|
|
86
|
+
compactPick: SyncDirtyItemCompact,
|
|
87
|
+
tableColumns: [
|
|
88
|
+
{
|
|
89
|
+
key: "id",
|
|
90
|
+
label: "ID"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
key: "model",
|
|
94
|
+
label: "Model"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
key: "name",
|
|
98
|
+
label: "Name"
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
key: "sync_status",
|
|
102
|
+
label: "Status"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
key: "collection_id",
|
|
106
|
+
label: "Collection"
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/commands/sync/poll-task.ts
|
|
113
|
+
const TERMINAL_STATUSES = new Set([
|
|
114
|
+
"successful",
|
|
115
|
+
"errored",
|
|
116
|
+
"cancelled",
|
|
117
|
+
"timed-out",
|
|
118
|
+
"conflict"
|
|
119
|
+
]);
|
|
120
|
+
const FAILURE_STATUSES = new Set([
|
|
121
|
+
"errored",
|
|
122
|
+
"timed-out",
|
|
123
|
+
"conflict"
|
|
124
|
+
]);
|
|
125
|
+
const REMOTE_SYNC_PATHS = {
|
|
126
|
+
currentTask: "/api/ee/remote-sync/current-task",
|
|
127
|
+
cancelTask: "/api/ee/remote-sync/current-task/cancel",
|
|
128
|
+
isDirty: "/api/ee/remote-sync/is-dirty",
|
|
129
|
+
hasRemoteChanges: "/api/ee/remote-sync/has-remote-changes",
|
|
130
|
+
dirty: "/api/ee/remote-sync/dirty",
|
|
131
|
+
import: "/api/ee/remote-sync/import",
|
|
132
|
+
export: "/api/ee/remote-sync/export",
|
|
133
|
+
stash: "/api/ee/remote-sync/stash",
|
|
134
|
+
branches: "/api/ee/remote-sync/branches",
|
|
135
|
+
createBranch: "/api/ee/remote-sync/create-branch"
|
|
136
|
+
};
|
|
137
|
+
const SyncTaskIdle = z.object({ status: z.literal("idle") });
|
|
138
|
+
const SyncTaskOrIdle = z.union([SyncTask, SyncTaskIdle]);
|
|
139
|
+
const syncTaskIdleView = {
|
|
140
|
+
compactPick: SyncTaskIdle,
|
|
141
|
+
tableColumns: [{
|
|
142
|
+
key: "status",
|
|
143
|
+
label: "Status"
|
|
144
|
+
}]
|
|
145
|
+
};
|
|
146
|
+
const pollFlags = {
|
|
147
|
+
wait: {
|
|
148
|
+
type: "boolean",
|
|
149
|
+
description: "Poll the resulting task until it reaches a terminal status",
|
|
150
|
+
default: true
|
|
151
|
+
},
|
|
152
|
+
timeout: {
|
|
153
|
+
type: "string",
|
|
154
|
+
description: "Polling timeout in ms (used with --wait)",
|
|
155
|
+
default: String(DEFAULT_TIMEOUT_MS)
|
|
156
|
+
},
|
|
157
|
+
interval: {
|
|
158
|
+
type: "string",
|
|
159
|
+
description: "Polling interval in ms (used with --wait)",
|
|
160
|
+
default: String(DEFAULT_INTERVAL_MS)
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
function isTerminal(status) {
|
|
164
|
+
return TERMINAL_STATUSES.has(status);
|
|
165
|
+
}
|
|
166
|
+
function isFailure(status) {
|
|
167
|
+
return FAILURE_STATUSES.has(status);
|
|
168
|
+
}
|
|
169
|
+
async function fetchOptionalParsed(client, path, schema) {
|
|
170
|
+
const response = await client.requestRaw(path, {
|
|
171
|
+
method: "GET",
|
|
172
|
+
expectContentType: "binary"
|
|
173
|
+
});
|
|
174
|
+
if (response.status === 204) return null;
|
|
175
|
+
const text = await response.text();
|
|
176
|
+
return parseJson(text, schema, { source: response.url });
|
|
177
|
+
}
|
|
178
|
+
async function fetchCurrentTask(client) {
|
|
179
|
+
return await fetchOptionalParsed(client, REMOTE_SYNC_PATHS.currentTask, SyncTask);
|
|
180
|
+
}
|
|
181
|
+
async function pollSyncTask(client, opts) {
|
|
182
|
+
return await pollUntil(async () => fetchCurrentTask(client), (task) => task === null || isTerminal(task.status), {
|
|
183
|
+
backoff: "exponential",
|
|
184
|
+
...opts
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
function throwIfFailedTask(final, verb) {
|
|
188
|
+
if (final === null || !isFailure(final.status)) return;
|
|
189
|
+
const detail = final.error_message ? `: ${final.error_message}` : "";
|
|
190
|
+
throw new Error(`sync ${verb} ${final.status}${detail}`);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
//#endregion
|
|
194
|
+
export { REMOTE_SYNC_PATHS, SyncDirtyItem, SyncDirtyItemCompact, SyncTask, SyncTaskOrIdle, fetchCurrentTask, fetchOptionalParsed, pollFlags, pollSyncTask, syncDirtyItemView, syncTaskIdleView, syncTaskView, throwIfFailedTask };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { AbortError, ConfigError } from "./runtime-DUgFfYkN.mjs";
|
|
2
|
+
import { confirm, isCancel, password, text } from "@clack/prompts";
|
|
3
|
+
|
|
4
|
+
//#region src/output/prompt.ts
|
|
5
|
+
async function promptText(opts) {
|
|
6
|
+
requireTty(opts.message);
|
|
7
|
+
const value = await text({
|
|
8
|
+
message: opts.message,
|
|
9
|
+
...opts.placeholder !== void 0 && { placeholder: opts.placeholder },
|
|
10
|
+
...opts.initialValue !== void 0 && { initialValue: opts.initialValue },
|
|
11
|
+
...opts.validate !== void 0 && { validate: opts.validate }
|
|
12
|
+
});
|
|
13
|
+
if (isCancel(value)) throw new AbortError();
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
async function promptPassword(opts) {
|
|
17
|
+
requireTty(opts.message);
|
|
18
|
+
const value = await password({
|
|
19
|
+
message: opts.message,
|
|
20
|
+
...opts.mask !== void 0 && { mask: opts.mask },
|
|
21
|
+
...opts.validate !== void 0 && { validate: opts.validate }
|
|
22
|
+
});
|
|
23
|
+
if (isCancel(value)) throw new AbortError();
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
async function promptConfirm(opts) {
|
|
27
|
+
requireTty(opts.message);
|
|
28
|
+
const value = await confirm({
|
|
29
|
+
message: opts.message,
|
|
30
|
+
...opts.initialValue !== void 0 && { initialValue: opts.initialValue }
|
|
31
|
+
});
|
|
32
|
+
if (isCancel(value)) throw new AbortError();
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
function requireTty(prompt) {
|
|
36
|
+
if (!process.stdin.isTTY) throw new ConfigError(`cannot prompt "${prompt}" — stdin is not a TTY`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
//#endregion
|
|
40
|
+
export { promptConfirm, promptPassword, promptText };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import "./package-t8dKf4m_.mjs";
|
|
2
|
+
import "./command-augment-D9pI9Vbh.mjs";
|
|
3
|
+
import { renderItem } from "./render-DlBijc5i.mjs";
|
|
4
|
+
import { ConfigError, connectionFlags, defineMetabaseCommand, outputFlags, parseCsv, profileFlag } from "./runtime-DUgFfYkN.mjs";
|
|
5
|
+
import "./input-BNqSFl38.mjs";
|
|
6
|
+
import { readBody } from "./body-DwU2s6Pg.mjs";
|
|
7
|
+
import { bodyInputFlags } from "./body-flags-7oqLhu5j.mjs";
|
|
8
|
+
import { parseId } from "./parse-id-C1prc9US.mjs";
|
|
9
|
+
import "./poll-D2sXM5rc.mjs";
|
|
10
|
+
import { parseWaitFlags, waitFlags } from "./wait-flags-CjW4ogUJ.mjs";
|
|
11
|
+
import { Workspace, WorkspaceProvisionInput, workspaceView } from "./workspace-Dr9lWU3D.mjs";
|
|
12
|
+
import { waitForDatabaseProvisioned } from "./wait-DwZN3ZwR.mjs";
|
|
13
|
+
|
|
14
|
+
//#region src/commands/workspace/database/provision.ts
|
|
15
|
+
var provision_default = defineMetabaseCommand({
|
|
16
|
+
meta: {
|
|
17
|
+
name: "provision",
|
|
18
|
+
description: "Provision a database into a workspace"
|
|
19
|
+
},
|
|
20
|
+
args: {
|
|
21
|
+
...outputFlags,
|
|
22
|
+
...profileFlag,
|
|
23
|
+
...connectionFlags,
|
|
24
|
+
...bodyInputFlags,
|
|
25
|
+
...waitFlags,
|
|
26
|
+
"database-id": {
|
|
27
|
+
type: "string",
|
|
28
|
+
description: "Database id (alternative to --body / --file)"
|
|
29
|
+
},
|
|
30
|
+
schemas: {
|
|
31
|
+
type: "string",
|
|
32
|
+
description: "Comma-separated input schemas (alternative to --body / --file)"
|
|
33
|
+
},
|
|
34
|
+
id: {
|
|
35
|
+
type: "positional",
|
|
36
|
+
description: "Workspace id",
|
|
37
|
+
required: true
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
outputSchema: Workspace,
|
|
41
|
+
examples: [
|
|
42
|
+
"metabase workspace database provision 1 --database-id 5 --schemas analytics,github",
|
|
43
|
+
"metabase workspace database provision 1 --database-id 5 --schemas analytics --wait",
|
|
44
|
+
"metabase workspace database provision 1 --file provision.json"
|
|
45
|
+
],
|
|
46
|
+
async run({ args, ctx, getClient }) {
|
|
47
|
+
const workspaceId = parseId(args.id);
|
|
48
|
+
const databaseIdFlag = args["database-id"];
|
|
49
|
+
const schemasFlag = args.schemas;
|
|
50
|
+
const wait = parseWaitFlags(args);
|
|
51
|
+
let body;
|
|
52
|
+
if (databaseIdFlag !== void 0 && databaseIdFlag !== "") {
|
|
53
|
+
const databaseId = parseId(databaseIdFlag, "--database-id");
|
|
54
|
+
const schemas = parseSchemas(schemasFlag);
|
|
55
|
+
body = WorkspaceProvisionInput.parse({
|
|
56
|
+
database_id: databaseId,
|
|
57
|
+
input_schemas: schemas
|
|
58
|
+
});
|
|
59
|
+
} else body = await readBody({
|
|
60
|
+
flag: args.body,
|
|
61
|
+
file: args.file
|
|
62
|
+
}, WorkspaceProvisionInput);
|
|
63
|
+
const client = await getClient();
|
|
64
|
+
const initial = await client.requestParsed(Workspace, `/api/ee/workspace-manager/${workspaceId}/database`, {
|
|
65
|
+
method: "POST",
|
|
66
|
+
body
|
|
67
|
+
});
|
|
68
|
+
const final = wait.enabled ? await waitForDatabaseProvisioned(client, workspaceId, body.database_id, wait.schedule) : initial;
|
|
69
|
+
renderItem(final, workspaceView, ctx);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
function parseSchemas(raw) {
|
|
73
|
+
if (raw === void 0 || raw === "") throw new ConfigError("--schemas is required when using --database-id");
|
|
74
|
+
const parts = parseCsv(raw);
|
|
75
|
+
if (parts.length === 0) throw new ConfigError("--schemas must contain at least one schema name");
|
|
76
|
+
return parts;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
//#endregion
|
|
80
|
+
export { provision_default as default };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import "./package-t8dKf4m_.mjs";
|
|
2
|
+
import "./command-augment-D9pI9Vbh.mjs";
|
|
3
|
+
import "./render-DlBijc5i.mjs";
|
|
4
|
+
import "./runtime-DUgFfYkN.mjs";
|
|
5
|
+
import "./poll-D2sXM5rc.mjs";
|
|
6
|
+
import "./docker-QWVMG2gl.mjs";
|
|
7
|
+
import { LocalWorkspace, LocalWorkspaceCompact, LocalWorkspaceListEnvelope, LocalWorkspaceState, localWorkspaceView, ps_default } from "./ps-BiOrecEe.mjs";
|
|
8
|
+
|
|
9
|
+
export { ps_default as default };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { renderList } from "./render-DlBijc5i.mjs";
|
|
2
|
+
import { defineMetabaseCommand, listEnvelopeSchema, localUrl, outputFlags, wrapList } from "./runtime-DUgFfYkN.mjs";
|
|
3
|
+
import { CONTAINER_STATES, checkDockerReady, listWorkspaceContainers } from "./docker-QWVMG2gl.mjs";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
|
|
6
|
+
//#region src/commands/workspace/ps.ts
|
|
7
|
+
const LocalWorkspaceState = z.enum(CONTAINER_STATES);
|
|
8
|
+
const LocalWorkspace = z.object({
|
|
9
|
+
workspace_id: z.number().int().positive(),
|
|
10
|
+
workspace_name: z.string(),
|
|
11
|
+
container_name: z.string(),
|
|
12
|
+
state: LocalWorkspaceState,
|
|
13
|
+
status: z.string(),
|
|
14
|
+
image: z.string(),
|
|
15
|
+
profile: z.string().nullable(),
|
|
16
|
+
parent_url: z.string().nullable(),
|
|
17
|
+
host_port: z.number().int().positive().nullable(),
|
|
18
|
+
url: z.string().nullable()
|
|
19
|
+
});
|
|
20
|
+
const LocalWorkspaceCompact = LocalWorkspace.pick({
|
|
21
|
+
workspace_id: true,
|
|
22
|
+
workspace_name: true,
|
|
23
|
+
state: true,
|
|
24
|
+
url: true
|
|
25
|
+
}).strip();
|
|
26
|
+
const localWorkspaceView = {
|
|
27
|
+
compactPick: LocalWorkspaceCompact,
|
|
28
|
+
tableColumns: [
|
|
29
|
+
{
|
|
30
|
+
key: "workspace_id",
|
|
31
|
+
label: "ID"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
key: "workspace_name",
|
|
35
|
+
label: "Name"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
key: "state",
|
|
39
|
+
label: "State"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
key: "url",
|
|
43
|
+
label: "URL",
|
|
44
|
+
format: (value) => typeof value === "string" ? value : "—"
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
};
|
|
48
|
+
const LocalWorkspaceListEnvelope = listEnvelopeSchema(LocalWorkspaceCompact);
|
|
49
|
+
var ps_default = defineMetabaseCommand({
|
|
50
|
+
meta: {
|
|
51
|
+
name: "ps",
|
|
52
|
+
description: "List workspaces with a local container (running or stopped)"
|
|
53
|
+
},
|
|
54
|
+
args: { ...outputFlags },
|
|
55
|
+
outputSchema: LocalWorkspaceListEnvelope,
|
|
56
|
+
examples: ["metabase workspace ps", "metabase workspace ps --json"],
|
|
57
|
+
async run({ ctx }) {
|
|
58
|
+
await checkDockerReady();
|
|
59
|
+
const summaries = await listWorkspaceContainers();
|
|
60
|
+
const items = summaries.map((summary) => ({
|
|
61
|
+
workspace_id: summary.workspaceId,
|
|
62
|
+
workspace_name: summary.workspaceName,
|
|
63
|
+
container_name: summary.name,
|
|
64
|
+
state: summary.state,
|
|
65
|
+
status: summary.status,
|
|
66
|
+
image: summary.image,
|
|
67
|
+
profile: summary.profile,
|
|
68
|
+
parent_url: summary.parentUrl,
|
|
69
|
+
host_port: summary.hostPort,
|
|
70
|
+
url: summary.hostPort !== null && summary.state === "running" ? localUrl(summary.hostPort) : null
|
|
71
|
+
}));
|
|
72
|
+
items.sort((a, b) => a.workspace_id - b.workspace_id);
|
|
73
|
+
renderList(wrapList(items), localWorkspaceView, ctx);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
//#endregion
|
|
78
|
+
export { LocalWorkspace, LocalWorkspaceCompact, LocalWorkspaceListEnvelope, LocalWorkspaceState, localWorkspaceView, ps_default };
|