@metabase/cli 0.1.4 → 0.1.6
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/.claude-plugin/marketplace.json +19 -0
- package/README.md +147 -101
- package/dist/{add-collection-CffaBB-Y.mjs → add-collection-BU8r3r2M.mjs} +9 -4
- package/dist/add-collection-C0w6ACQF.mjs +11 -0
- package/dist/{archive-uJrslh9r.mjs → archive-BNinrUak.mjs} +9 -8
- package/dist/{archive-GdGm7l2e.mjs → archive-C1enZgKV.mjs} +8 -7
- package/dist/archive-CDA0KxL8.mjs +40 -0
- package/dist/{archive-BAcEXbT9.mjs → archive-CRhiBpPJ.mjs} +9 -8
- package/dist/{archive-B_B3MQp0.mjs → archive-DMPS8Kih.mjs} +9 -8
- package/dist/archive-lWgqiFAt.mjs +40 -0
- package/dist/auth-CzXb_zB2.mjs +19 -0
- package/dist/{body-D6dHGjMT.mjs → body-DjdFxjpg.mjs} +4 -4
- package/dist/{branches-Bpe40fEd.mjs → branches-B1WRfG7-.mjs} +11 -7
- package/dist/{cancel-BWTY6oYI.mjs → cancel-Dl_Ho056.mjs} +7 -6
- package/dist/{cancel-task--BfiAXfS.mjs → cancel-task-CdigdCaO.mjs} +11 -7
- package/dist/capabilities-7e9MgquN.mjs +29 -0
- package/dist/card-DP4rfoOi.mjs +21 -0
- package/dist/{card-CQxvHeyP.mjs → card-DlCAaAPq.mjs} +1 -1
- package/dist/{cards-CVlFJxYh.mjs → cards-BGiJS675.mjs} +8 -7
- package/dist/cli.mjs +267 -44
- package/dist/collection-tY18ezvn.mjs +21 -0
- package/dist/{predicates-CGO17Q15.mjs → command-augment-BH9qgQ5u.mjs} +66 -14
- package/dist/create-BNiva__H.mjs +52 -0
- package/dist/{create-izE3EKCt.mjs → create-BTcpaop_.mjs} +9 -8
- package/dist/{create-BykvNpSA.mjs → create-BYlIju0b.mjs} +14 -12
- package/dist/{create-Bu-YhIDL.mjs → create-Be_0Vier.mjs} +10 -9
- package/dist/{create-DYoc9IXW.mjs → create-CHF313Qg.mjs} +13 -9
- package/dist/{create-Cz3_Wxdt.mjs → create-CwGtmwqm.mjs} +14 -12
- package/dist/{create-DP8RrLDi.mjs → create-CzzrbL0u.mjs} +10 -9
- package/dist/{create-BzElku2l.mjs → create-DGth_uOp.mjs} +14 -12
- package/dist/{create-branch-B49UQyCK.mjs → create-branch-DKZkoQ64.mjs} +11 -7
- package/dist/{create-DQVdMT2Y.mjs → create-dhxPxfF3.mjs} +16 -14
- package/dist/{credentials-xKSoP6eh.mjs → credentials-dzeq7ckm.mjs} +12 -10
- package/dist/{current-task-DweHmjlk.mjs → current-task-CCRzm0_7.mjs} +11 -7
- package/dist/dashboard-ChM_Tu0l.mjs +22 -0
- package/dist/{dashboard-CnMD04PQ.mjs → dashboard-FY5UzJ_Z.mjs} +2 -1
- package/dist/{database-BNlvldUL.mjs → database-CIXwHKjK.mjs} +3 -3
- package/dist/{database-vvig8k4x.mjs → database-lH-B3G1I.mjs} +1 -1
- package/dist/db-DrQn_i3W.mjs +22 -0
- package/dist/{remove-B3ZEqBF7.mjs → delete-CM3jnAeQ.mjs} +21 -20
- package/dist/{delete-DojHmKeM.mjs → delete-Dimc-2y8.mjs} +9 -8
- package/dist/{delete-DIz9Tgz5.mjs → delete-ZjnV35OJ.mjs} +9 -8
- package/dist/{delete-runtime-BkAdygbs.mjs → delete-runtime-B6RQo_pw.mjs} +5 -3
- package/dist/{delete-table-DjN8E3sd.mjs → delete-table-agZJpivt.mjs} +9 -8
- package/dist/{deprovision-_HDcBApz.mjs → deprovision-CwxcIT3k.mjs} +16 -12
- package/dist/{dirty-Co8V0SZ3.mjs → dirty-D4d0yHqj.mjs} +11 -7
- package/dist/{docker-D9sC_37H.mjs → docker-Oq80q3tu.mjs} +4 -4
- package/dist/{translate-CG_Ka0dO.mjs → eid-BXzaQh0o.mjs} +37 -22
- package/dist/error-C9S6PN3-.mjs +190 -0
- package/dist/{export-CVMFxoo1.mjs → export-DTygoXBP.mjs} +17 -16
- package/dist/field-Z6Pcxf4n.mjs +19 -0
- package/dist/{fields-Coha7vKv.mjs → fields-CoQi99gv.mjs} +9 -8
- package/dist/{get-DXv2FkA7.mjs → get-Bzys7vgp.mjs} +8 -7
- package/dist/{get-bNtA7vWe.mjs → get-C2p383Qc.mjs} +8 -7
- package/dist/{get-Br6WayZv.mjs → get-C3HdQ91a.mjs} +8 -7
- package/dist/{get-BOtKerj8.mjs → get-CP3Z3NiH.mjs} +9 -8
- package/dist/{get-BSKoL8ek.mjs → get-C_w1kvN3.mjs} +9 -8
- package/dist/{get-Be6EFh94.mjs → get-CzuzeKSe.mjs} +10 -9
- package/dist/{get-BVTz9B_H.mjs → get-D3SbEQSE.mjs} +10 -9
- package/dist/get-DFxZXaKz.mjs +79 -0
- package/dist/{get-DZrV7v9d.mjs → get-DQTZG_NP.mjs} +8 -7
- package/dist/{get-CJwzbVjc.mjs → get-DSWFjy7O.mjs} +8 -7
- package/dist/{get-BxzCKVC6.mjs → get-Ddr0XLh7.mjs} +8 -7
- package/dist/{get-AOvWo48B.mjs → get-Hc93A0Yz.mjs} +8 -7
- package/dist/{get-C_6K7MSW.mjs → get-lb7q3JYs.mjs} +7 -6
- package/dist/get-run-B7sKdaDU.mjs +38 -0
- package/dist/git-sync-CiGAad76.mjs +28 -0
- package/dist/{has-remote-changes-D6xgsuUr.mjs → has-remote-changes-BY10-nnE.mjs} +11 -7
- package/dist/{import-Dv0ORSNw.mjs → import-CiMz4Wz-.mjs} +17 -16
- package/dist/{input-BQ-BZA8h.mjs → input-cMSEqISy.mjs} +7 -4
- package/dist/{is-dirty-WNi8a6O9.mjs → is-dirty-BZOaryxT.mjs} +9 -4
- package/dist/is-dirty-Ume4oV0j.mjs +10 -0
- package/dist/{items-CTcAMknV.mjs → items-BWfvkY-J.mjs} +9 -8
- package/dist/key-C2XG394c.mjs +17 -0
- package/dist/license-Dxarh-gG.mjs +17 -0
- package/dist/{list-FXuSCYpa.mjs → list--OYdUTtu.mjs} +7 -6
- package/dist/{list-8oVMvlLV.mjs → list-2j7GsXsl.mjs} +7 -6
- package/dist/{list-xQmtQPSl.mjs → list-BI4zr8LW.mjs} +10 -8
- package/dist/{list-DhWG5jiW.mjs → list-Brgh-Z2v.mjs} +8 -6
- package/dist/{list-DSs0Q78i.mjs → list-C3hfovHv.mjs} +7 -6
- package/dist/{list-DvUjMQze.mjs → list-CL7eCOQE.mjs} +7 -6
- package/dist/list-Clz5igWg.mjs +44 -0
- package/dist/list-D4sFiqX8.mjs +173 -0
- package/dist/{list-BxdXvGTK.mjs → list-DXH7TlkU.mjs} +9 -7
- package/dist/{list-CocYwmnI.mjs → list-DZ8fNUoQ.mjs} +9 -8
- package/dist/{list-DjhZU-FY.mjs → list-SOG0whQ-.mjs} +7 -6
- package/dist/{list-DI7K3K6k.mjs → list-d58BprgJ.mjs} +7 -6
- package/dist/{list-NiwCL_1X.mjs → list-sD5N3fGk.mjs} +9 -8
- package/dist/{list-CbJeP0Z6.mjs → list-zSO0DMw-.mjs} +10 -6
- package/dist/{login-SXsSH0I1.mjs → login-Bm2AnCez.mjs} +65 -80
- package/dist/{logout-bgOXjxbN.mjs → logout-BlyRJODO.mjs} +8 -7
- package/dist/{logs-BnwVbFuD.mjs → logs-CywPikkL.mjs} +9 -8
- package/dist/{manifest-CGM7XNLC.mjs → manifest-BBR46KFM.mjs} +15 -15
- package/dist/measure-C44EK_xt.mjs +20 -0
- package/dist/{measure-BEQfnLdN.mjs → measure-ClESGxIb.mjs} +2 -2
- package/dist/{metadata-Bu2HOmuX.mjs → metadata-B8ZSF9LA.mjs} +10 -9
- package/dist/{metadata-B0WZT3Yb.mjs → metadata-DqiI2q9q.mjs} +9 -8
- package/dist/parse-enum-CrEWOhuY.mjs +11 -0
- package/dist/{parse-id-B3B-0hUA.mjs → parse-id-lk_K-CEF.mjs} +1 -1
- package/dist/{parse-ref-D1yeDOn8.mjs → parse-ref-BiETXmvm.mjs} +1 -1
- package/dist/{parse-schemas-DgtVLikM.mjs → parse-schemas-BqUdWUwq.mjs} +2 -2
- package/dist/path-AEtZ3mBq.mjs +58 -0
- package/dist/{poll-BCnrcUVf.mjs → poll-DHKDpCiq.mjs} +2 -2
- package/dist/{poll-task-0b1V6G-8.mjs → poll-task-Cooi0lQV.mjs} +3 -20
- package/dist/{preflight-5ACaYnDp.mjs → preflight-aXV5LyDs.mjs} +4 -4
- package/dist/{process-FjsqDwKo.mjs → process-C7V8LJ-j.mjs} +1 -1
- package/dist/{prompt-DgDNy_Pc.mjs → prompt-CFKoys7k.mjs} +3 -1
- package/dist/{provision-29Zt62Ft.mjs → provision-UWcNDoDe.mjs} +29 -24
- package/dist/{ps-BMFiRCi4.mjs → ps-CJU0EbrC.mjs} +5 -3
- package/dist/ps-DEroLgbI.mjs +11 -0
- package/dist/{query-DxA353Hy.mjs → query-AaKzYnTY.mjs} +9 -8
- package/dist/{query-aba8MEe_.mjs → query-BlsVNZpD.mjs} +15 -13
- package/dist/{remove-BfgU_CQi.mjs → remove-BFWun0e8.mjs} +9 -8
- package/dist/{remove-collection-Brv72xUe.mjs → remove-collection-CoCmrrQs.mjs} +13 -9
- package/dist/{render-DuoDUTVL.mjs → render-CfznwleY.mjs} +15 -17
- package/dist/render-OQn3iRsI.mjs +32 -0
- package/dist/{rescan-values-DIAdjoq7.mjs → rescan-values-C0FDsjT7.mjs} +10 -9
- package/dist/{run-CgXRo0hD.mjs → run-B4Wn43zm.mjs} +10 -9
- package/dist/{runs-DtLRw6xg.mjs → runs-Bbaszr18.mjs} +9 -8
- package/dist/{runtime-Br8L4NPm.mjs → runtime-Dmv5VtUK.mjs} +657 -428
- package/dist/{schema-tables-DiKMY6lx.mjs → schema-tables-CaWinbuK.mjs} +9 -8
- package/dist/{schemas-Bvr8cOzo.mjs → schemas-DUgGpAyB.mjs} +7 -6
- package/dist/{search-BT_TCcTd.mjs → search-BLrBXLUk.mjs} +12 -16
- package/dist/segment-B3Uwwcsm.mjs +20 -0
- package/dist/{set-DtG0KH6P.mjs → set-B8cUbRLD.mjs} +13 -12
- package/dist/{set-CAIkXlPy.mjs → set-DfGsta5O.mjs} +11 -10
- package/dist/{setting-BDOi5fk_.mjs → setting-D2p2MA7f.mjs} +3 -3
- package/dist/{setup-LjTvvlJy.mjs → setup-C9ikBRw_.mjs} +9 -8
- package/dist/skills-CUHIcQS6.mjs +18 -0
- package/dist/skills-CiN1OQ8W.mjs +191 -0
- package/dist/snippet-B7D0uWlz.mjs +20 -0
- package/dist/{start-CXKt0Q7A.mjs → start-3PX3ahjT.mjs} +68 -36
- package/dist/{stash-dRw1UEwg.mjs → stash-EIDcSvpF.mjs} +17 -16
- package/dist/{status-C2niMfrQ.mjs → status-95ElRAu9.mjs} +12 -8
- package/dist/status-B0_MiZEf.mjs +100 -0
- package/dist/status-CEplmC44.mjs +34 -0
- package/dist/{stop-BdedYfwU.mjs → stop-CQ0XGrN8.mjs} +11 -10
- package/dist/{summary-BPDA4K99.mjs → summary-C12LiEuJ.mjs} +8 -7
- package/dist/{sync-schema-D95LLRpf.mjs → sync-schema-Ba8M3DiX.mjs} +10 -9
- package/dist/{table-B-PYcgGb.mjs → table-C7a5V6Zn.mjs} +1 -1
- package/dist/table-e6h8SLVX.mjs +20 -0
- package/dist/transform-BMYh1lsC.mjs +25 -0
- package/dist/transform-job-Cm7z5TfH.mjs +20 -0
- package/dist/{transform-job-Csr86muI.mjs → transform-job-DeTDPMxt.mjs} +1 -1
- package/dist/{tree-DazZT7dR.mjs → tree-Des2ZG9d.mjs} +6 -5
- package/dist/{update-DE6kjV-f.mjs → update-Bx54nWEI.mjs} +17 -15
- package/dist/{update-bW-i6gjZ.mjs → update-CyIZdbIQ.mjs} +11 -10
- package/dist/{update-djgvzO3K.mjs → update-DBi5U8zb.mjs} +16 -14
- package/dist/{update-CJSDB6S8.mjs → update-DHZubok3.mjs} +18 -14
- package/dist/{update-BBfvArCx.mjs → update-DSgceARZ.mjs} +11 -10
- package/dist/{update-DSWZSfpw.mjs → update-DzAN4SPj.mjs} +15 -13
- package/dist/{update-WyRKlQPh.mjs → update-F6DmZncY.mjs} +11 -10
- package/dist/{update-DTIWJxob.mjs → update-_QfgNa53.mjs} +12 -11
- package/dist/{update-dashcard-BhD5x__K.mjs → update-dashcard-wpSjv4M7.mjs} +11 -10
- package/dist/{update-9kVyE3BJ.mjs → update-mYVnoYNV.mjs} +15 -13
- package/dist/{update-659eQR1L.mjs → update-njHe3j-s.mjs} +15 -13
- package/dist/{upgrade-D58rvXHM.mjs → upgrade-iAuvhX-W.mjs} +9 -8
- package/dist/{url-DKkSu2D8.mjs → url-DWaT6WIZ.mjs} +11 -10
- package/dist/{uuid-BF20B59s.mjs → uuid-CMKnS8-z.mjs} +8 -6
- package/dist/{validate-CB0bu50i.mjs → validate-dPEOnOf8.mjs} +2 -1
- package/dist/{validate-query-CavIA0Q2.mjs → validate-query-Cw6WE5Y8.mjs} +3 -3
- package/dist/{values-DyjmpcbT.mjs → values-BfSTAbzc.mjs} +8 -7
- package/dist/verify-D5YtTqqp.mjs +79 -0
- package/dist/{wait-CeUPCgdc.mjs → wait-8yV9_WIo.mjs} +2 -2
- package/dist/{wait-DhkTaV6E.mjs → wait-Bv3Tsnv4.mjs} +12 -8
- package/dist/{wait-flags-BR-yqe7y.mjs → wait-flags-Dzq9BGQY.mjs} +20 -9
- package/dist/workspace-CKLZrR7l.mjs +26 -0
- package/dist/{workspace-credentials-Cctumbru.mjs → workspace-credentials-BXpABsNZ.mjs} +2 -41
- package/dist/yaml-YTQiYJ9s.mjs +43 -0
- package/package.json +6 -2
- package/skill-data/core/SKILL.md +177 -0
- package/skill-data/git-sync/SKILL.md +196 -0
- package/skill-data/mbql/SKILL.md +156 -0
- package/skill-data/mbql/references/operators.md +253 -0
- package/skill-data/transform/SKILL.md +197 -0
- package/skill-data/viz/SKILL.md +137 -0
- package/skill-data/viz/references/settings.md +312 -0
- package/skill-data/workspace/SKILL.md +390 -0
- package/skills/metabase-cli/SKILL.md +21 -0
- package/dist/add-collection-CPL1njYZ.mjs +0 -11
- package/dist/api-key-9p1UPnXn.mjs +0 -13
- package/dist/auth-N4w5xtwW.mjs +0 -19
- package/dist/card-4rZRb5bc.mjs +0 -20
- package/dist/collection-Cp_B02I4.mjs +0 -19
- package/dist/command-augment-D9pI9Vbh.mjs +0 -11
- package/dist/create-doyv3SxU.mjs +0 -50
- package/dist/create-ov-De5dO.mjs +0 -125
- package/dist/dashboard-BYBiA-IG.mjs +0 -20
- package/dist/db-CObVU22j.mjs +0 -22
- package/dist/eid-Cr5r-t9B.mjs +0 -13
- package/dist/field-CbljasCH.mjs +0 -18
- package/dist/flag-pair-Fmcdkrfx.mjs +0 -17
- package/dist/get-run-CSrXHDGS.mjs +0 -36
- package/dist/git-sync-BGkS8o5b.mjs +0 -28
- package/dist/is-dirty-BOZ4xz92.mjs +0 -10
- package/dist/key-CCJdVWKc.mjs +0 -12
- package/dist/license-DLLTpFvP.mjs +0 -17
- package/dist/list-BNzdnE1c.mjs +0 -55
- package/dist/measure-B54VtKym.mjs +0 -19
- package/dist/package-D-aVYFKM.mjs +0 -80
- package/dist/ps-C5FOLwL2.mjs +0 -11
- package/dist/segment-C2ui5dSd.mjs +0 -19
- package/dist/snippet-BcgVYsoR.mjs +0 -19
- package/dist/status-BEONmJWv.mjs +0 -32
- package/dist/status-BWep0PFe.mjs +0 -56
- package/dist/table-lCNGbvej.mjs +0 -19
- package/dist/transform-BGAm1s4f.mjs +0 -24
- package/dist/transform-job-cNTJ30pm.mjs +0 -19
- package/dist/workspace-DtcBldk0.mjs +0 -24
- /package/dist/{body-flags-BK7J6Daz.mjs → body-flags-D7q87Btw.mjs} +0 -0
- /package/dist/{field-B3gvaqpK.mjs → field-yomXlkvl.mjs} +0 -0
- /package/dist/{paginate-CTSfuYiF.mjs → paginate-Dfm9eO9A.mjs} +0 -0
- /package/dist/{revision-message-flag-oyq2xrDU.mjs → revision-message-flag-WmsIzUOM.mjs} +0 -0
- /package/dist/{segment-BMrUBz94.mjs → segment-Be2v4ilr.mjs} +0 -0
- /package/dist/{setting-CTaAeMci.mjs → setting-oL97SNeO.mjs} +0 -0
- /package/dist/{snippet-Dw0Sjzkr.mjs → snippet-COggaWxx.mjs} +0 -0
- /package/dist/{transform-IEX4Mx3X.mjs → transform-GTW3G-01.mjs} +0 -0
- /package/dist/{workspace-C5q4nbpY.mjs → workspace-BBXJczJK.mjs} +0 -0
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: workspace
|
|
3
|
+
description: Enterprise workspace lifecycle for `mb` — create, provision databases, start (with Remote Sync wiring + branch guard), save child credentials as a profile, diagnose. Load when the user touches `mb workspace …` — "spin up a workspace", "provision a database", "start a local Metabase against my prod", "save the child's API key", "diagnose a workspace that won't start", or anything Enterprise workspaces.
|
|
4
|
+
allowed-tools: Read, Write, Edit, Bash, AskUserQuestion
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Workspaces (Enterprise)
|
|
8
|
+
|
|
9
|
+
A **workspace** is a child Metabase instance bound to a parent's databases. Local lifecycle is `mb workspace <verb>`; the parent is reached via a profile (the parent's profile — typically `prod` / `staging`). Each provisioned database gets a per-workspace isolation schema on the warehouse, and the QP rewrites references from canonical names (`public.foo`) to that isolation schema (`mb__isolation_<hash>_<ws-id>.foo`) on the fly. Cards, transforms, and queries authored in the workspace target canonical names; the rewrite is invisible to the author.
|
|
10
|
+
|
|
11
|
+
This skill covers the full lifecycle. The general flag conventions, auth setup, and output flags live in the `core` skill; load that first (`mb skills get core`).
|
|
12
|
+
|
|
13
|
+
## Always ask about Remote Sync before starting
|
|
14
|
+
|
|
15
|
+
Before running `mb workspace start`, **ask the user how they want Remote Sync wired**. The bind mount is set at container-create time — you cannot add it later without a recreate, so this decision belongs at start time. Use `AskUserQuestion` with three options:
|
|
16
|
+
|
|
17
|
+
> "How should I wire Remote Sync for this workspace?"
|
|
18
|
+
>
|
|
19
|
+
> 1. **Current directory** — bind-mount the directory you're running Claude from (`pwd`) as `file:///mnt/repo` and set the workspace to remote-sync against it (read-write). Pick this when the conversation is happening inside the sync repo.
|
|
20
|
+
> 2. **Custom path** — you specify a different host directory; same wiring as option 1.
|
|
21
|
+
> 3. **No sync** — start the workspace without a repo bind mount; you can configure remote-sync against a remote URL later via `setting set`.
|
|
22
|
+
|
|
23
|
+
Default-suggest option 1 if the current working directory looks like a git repo (a `.git/` is present). Otherwise default-suggest option 3 and let the user volunteer a path.
|
|
24
|
+
|
|
25
|
+
Map the answer to flags on `workspace start`:
|
|
26
|
+
|
|
27
|
+
| Choice | Flags to add to `workspace start` |
|
|
28
|
+
| ----------------- | ----------------------------------------------------------------- |
|
|
29
|
+
| Current directory | `--repo "$(pwd)"` |
|
|
30
|
+
| Custom path | `--repo <path>` |
|
|
31
|
+
| No sync | (omit `--repo` — no bind mount, no remote-sync settings injected) |
|
|
32
|
+
|
|
33
|
+
The `--repo` flag (a) bind-mounts the host path into the container at `/mnt/repo`, and (b) injects three settings into the workspace's config.yml at boot: `remote-sync-url=file:///mnt/repo`, `remote-sync-branch=<HEAD>`, `remote-sync-type=read-write`. The branch defaults to the current branch of the host repo (read via `git -C <path> symbolic-ref --short HEAD`); override with `--repo-branch <name>`. Switch to read-only with `--repo-mode read-only` (also makes the bind mount read-only).
|
|
34
|
+
|
|
35
|
+
Do not skip this question — silently picking "no sync" loses the user's repo context, and silently picking "current directory" pushes work into a repo they didn't intend.
|
|
36
|
+
|
|
37
|
+
## Branch guard before `--repo`
|
|
38
|
+
|
|
39
|
+
When the user picks a `--repo` option (current dir or custom path), check the host's branch before `workspace start`. `--repo` reads `git -C <path> symbolic-ref --short HEAD` and injects it as the workspace's `remote-sync-branch` setting; that branch then becomes the default target for every subsequent `git-sync import` and `git-sync export`. If the host is on `main` (or `master`), every export commits straight to it — usually not what the user wants for ephemeral workspace work.
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
HOST_BRANCH=$(git -C <repo-path> symbolic-ref --short HEAD)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
If `HOST_BRANCH` is `main` or `master`, ask the user via `AskUserQuestion`:
|
|
46
|
+
|
|
47
|
+
> "The host repo is on `<branch>` — the workspace will track and export to that branch by default. Switch to a feature branch first?"
|
|
48
|
+
>
|
|
49
|
+
> 1. **Create + checkout a feature branch on the host** — agent suggests a name (e.g., `agent/<task>`); run `git -C <repo-path> checkout -b <name>` then proceed with `workspace start --repo …` so the workspace tracks `<name>`.
|
|
50
|
+
> 2. **Pin the workspace to a specific branch** — pass `--repo-branch <name>` on `workspace start` to override host HEAD. The branch must exist **locally** in the bind-mounted host repo before `workspace start` (create it first with `git -C <repo-path> branch <name>` or `git -C <repo-path> checkout -b <name>`); it does **not** need to exist on `origin`. Local-only branches are fine — the workspace never pushes, and the remote side gets created on the user's first `git push` later.
|
|
51
|
+
> 3. **Proceed on `main`/`master`** — explicitly accepted; downstream `git-sync export` will commit to that branch unless overridden per-call.
|
|
52
|
+
|
|
53
|
+
Skip this question only when the user's instructions already named the branch (e.g., they explicitly asked to work against `main`). The same guard applies later at `git-sync export` time — see the `git-sync` skill, "Branch guard".
|
|
54
|
+
|
|
55
|
+
## Quick start (copy-pasteable, end-to-end)
|
|
56
|
+
|
|
57
|
+
When a parent profile + license are in place, this whole sequence runs in one go. Replace the four shell vars; pick whether to bind-mount a sync repo with `REPO_FLAGS` per the question above.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
PARENT=<parent> # e.g. prod — the parent profile name
|
|
61
|
+
WS_NAME=<ws-name> # e.g. my_nice_ws — also reused as the child profile name
|
|
62
|
+
DB_ID=<db-id> # parent database id from `mb database list --profile $PARENT --json`
|
|
63
|
+
SCHEMAS=<schema1,schema2> # comma-separated; no "all" wildcard
|
|
64
|
+
REPO_FLAGS=(--repo "$(pwd)") # OR (--repo /path/to/sync-repo) OR () for no sync
|
|
65
|
+
|
|
66
|
+
# 0. Branch guard (only when REPO_FLAGS is non-empty). If the host repo is on
|
|
67
|
+
# main/master, ask the user before continuing — see "Branch guard before --repo"
|
|
68
|
+
# above. Skip when REPO_FLAGS is () (no sync = no branch).
|
|
69
|
+
if [ ${#REPO_FLAGS[@]} -gt 0 ]; then
|
|
70
|
+
HOST_BRANCH=$(git -C "$(pwd)" symbolic-ref --short HEAD)
|
|
71
|
+
case "$HOST_BRANCH" in main|master) ;; # ask user; not auto-resolvable
|
|
72
|
+
esac
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
# 1. Create empty workspace, capture id
|
|
76
|
+
WS_ID=$(mb workspace create --name "$WS_NAME" --profile "$PARENT" --json | jq -r '.id')
|
|
77
|
+
|
|
78
|
+
# 2. Provision a database into it (blocks on :provisioned)
|
|
79
|
+
mb workspace database provision "$WS_ID" "$DB_ID" \
|
|
80
|
+
--schemas "$SCHEMAS" \
|
|
81
|
+
--wait \
|
|
82
|
+
--profile "$PARENT"
|
|
83
|
+
|
|
84
|
+
# 3. Start the child container, block on state=running.
|
|
85
|
+
# With REPO_FLAGS set, the child boots already wired to the local repo:
|
|
86
|
+
# bind-mounted at /mnt/repo, remote-sync-url=file:///mnt/repo, branch from HEAD.
|
|
87
|
+
mb workspace start "$WS_ID" --wait --profile "$PARENT" "${REPO_FLAGS[@]}"
|
|
88
|
+
|
|
89
|
+
# 4. Save the child's API key as its own profile (use the workspace name as profile name).
|
|
90
|
+
# This is the documented exception to "the agent doesn't run auth login" — the child
|
|
91
|
+
# key was minted by the parent the human authorized, and reading it via
|
|
92
|
+
# `workspace credentials` is the supported path.
|
|
93
|
+
WS_URL=$(mb workspace url "$WS_ID" --json | jq -r '.url')
|
|
94
|
+
WS_API_KEY=$(mb workspace credentials "$WS_ID" --json | jq -r '.api_key')
|
|
95
|
+
printf '%s' "$WS_API_KEY" | mb auth login \
|
|
96
|
+
--url "$WS_URL" \
|
|
97
|
+
--profile "$WS_NAME" \
|
|
98
|
+
--json
|
|
99
|
+
|
|
100
|
+
# 5. Smoke test: list child databases
|
|
101
|
+
mb database list --profile "$WS_NAME" --json
|
|
102
|
+
|
|
103
|
+
# 6. (If REPO_FLAGS was set) Verify sync is wired:
|
|
104
|
+
mb setting get remote-sync-url --profile "$WS_NAME" --json # → "file:///mnt/repo"
|
|
105
|
+
mb git-sync status --profile "$WS_NAME" --json # → branch, dirty, current task
|
|
106
|
+
|
|
107
|
+
# 7. (If REPO_FLAGS was set) Apply the repo to the fresh workspace. The container's
|
|
108
|
+
# boot-time auto-import usually handles this — the step-6 `git-sync status` shows
|
|
109
|
+
# whether it landed. If `current_task` is not a successful `import` for the host
|
|
110
|
+
# branch, run an explicit import. The status-check + retry-then-force guard lives
|
|
111
|
+
# in the git-sync skill, "First import on a fresh workspace". Skipping the import is
|
|
112
|
+
# *not* safe — without it the instance has none of the repo content and edits diverge.
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
After step 5, drive the child via `mb <verb> --profile $WS_NAME` for everything (cards, transforms, queries, …). To author a transform on the workspace, load the `transform` skill (`mb skills get transform`). To use the sync flow (import host commits, export instance changes), load the `git-sync` skill (`mb skills get git-sync`).
|
|
116
|
+
|
|
117
|
+
## Setup (steps in order)
|
|
118
|
+
|
|
119
|
+
### 1. Parent profile
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
mb auth status --profile <parent> --json
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
If a profile is missing or expired, **stop and ask the operator** to run, themselves:
|
|
126
|
+
|
|
127
|
+
> Please run `mb auth login --url <parent-base-url> --profile <parent>` from your terminal and tell me the profile name when you're done.
|
|
128
|
+
|
|
129
|
+
Don't run `auth login` for them and don't suggest a URL — they pick. Verify with `mb auth status --profile <parent> --json` once they confirm. If multiple parent profiles exist and the user hasn't named one, use `AskUserQuestion` to disambiguate.
|
|
130
|
+
|
|
131
|
+
### 2. License
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
mb workspace license status --json
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
If `present: false`, ask the operator to run, themselves:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
echo "<your-token>" | mb workspace license set
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
A workspace child cannot start without a parent license — it inherits feature gates from the parent.
|
|
144
|
+
|
|
145
|
+
### 3. Find or create a workspace
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
mb workspace list --profile <parent> --json
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
- Empty → create one (below).
|
|
152
|
+
- One workspace → use its `id`. Surface name + id to the user.
|
|
153
|
+
- Multiple → `AskUserQuestion`.
|
|
154
|
+
|
|
155
|
+
Create:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
mb workspace create --name "<descriptive-name>" --profile <parent> --json
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Note the returned `id`. The workspace is empty; you must provision at least one database before `start` will succeed.
|
|
162
|
+
|
|
163
|
+
### 4. Provision databases
|
|
164
|
+
|
|
165
|
+
A workspace needs at least one provisioned database. Source databases come from the parent.
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
mb database list --profile <parent> --json
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
For each source database, decide which schemas to expose. Enumerate the schemas the parent already syncs for that database:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
mb table list --db-id <db-id> --profile <parent> --json \
|
|
175
|
+
| jq -r '[.data[].schema] | unique | .[]'
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Provision (one db per call; `--schemas` is required, no "all" wildcard):
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
mb workspace database provision <ws-id> <db-id> \
|
|
182
|
+
--schemas <schema1>,<schema2> \
|
|
183
|
+
--wait \
|
|
184
|
+
--profile <parent>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
`--wait` blocks until status is `provisioned`. Repeat per source database.
|
|
188
|
+
|
|
189
|
+
Verify all are ready:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
mb workspace list --profile <parent> --full --json \
|
|
193
|
+
| jq '.data[] | select(.id==<ws-id>) | .databases'
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Every entry's `status` must be `provisioned`.
|
|
197
|
+
|
|
198
|
+
## Start
|
|
199
|
+
|
|
200
|
+
Before running `start`, ask the user about Remote Sync (see "Always ask about Remote Sync before starting" at the top of this file). The bind mount is decided at container-create time and cannot be added later without recreate.
|
|
201
|
+
|
|
202
|
+
### Pick a free port up front
|
|
203
|
+
|
|
204
|
+
Despite the `--port` flag's "auto-shifts up if taken" hint, in practice `workspace start` fails with `docker start failed for metabase-workspace-<id>` when the host port is occupied — typically by a stale workspace container from a prior session. **List local containers first** and pass an explicit free `--port`:
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
mb workspace ps # → currently-running workspace containers + their host ports
|
|
208
|
+
docker ps --filter "name=metabase-workspace" \
|
|
209
|
+
--format "{{.Names}}\t{{.Ports}}\t{{.Status}}" # also surfaces stopped containers
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
If 3000 is taken, pass e.g. `--port 3322`. The child's URL in `workspace credentials` and `workspace url` reflects the chosen port automatically.
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
# No sync:
|
|
216
|
+
mb workspace start <ws-id> --wait --profile <parent>
|
|
217
|
+
|
|
218
|
+
# With sync against the current directory:
|
|
219
|
+
mb workspace start <ws-id> --repo "$(pwd)" --wait --profile <parent>
|
|
220
|
+
|
|
221
|
+
# With sync against a custom path, branch override, read-only:
|
|
222
|
+
mb workspace start <ws-id> --repo /path/to/repo --repo-branch dev --repo-mode read-only --wait --profile <parent>
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
`--wait` blocks until `state: "running"`. Don't omit it for interactive bring-up — without it the next step (saving credentials as a child profile) races the container's HTTP listener and you'll get spurious connection errors.
|
|
226
|
+
|
|
227
|
+
- `--port <n>` — host port (default 3000; **does not** auto-shift reliably — pass an explicit free port if 3000 might be taken).
|
|
228
|
+
- `--wait` — block until `/api/health` reports ready before returning.
|
|
229
|
+
- `--no-pull` — skip `docker pull` (image already present).
|
|
230
|
+
- `--no-metadata` — skip the warehouse metadata export.
|
|
231
|
+
- `--force` — recreate even if a container for this workspace exists. Preserves the app db.
|
|
232
|
+
- `--timeout <ms>` — per-phase readiness deadline (default 240000). Covers the post-create config-consumption wait, (with `--wait`) the `/api/health` probe, and (with `--metadata`) the metadata-import status poll on the child. Bump if the first cold boot exceeds the default — image pull + JVM startup can stretch on slow disks/networks.
|
|
233
|
+
- `--repo <host-path>` — bind-mount a host directory at `/mnt/repo` and inject `remote-sync-url=file:///mnt/repo` into config.yml.
|
|
234
|
+
- `--repo-branch <name>` — `remote-sync-branch` value. Default: current branch of the host repo (`git symbolic-ref --short HEAD`).
|
|
235
|
+
- `--repo-mode <mode>` — `read-write` (default) or `read-only`. Also flips the bind mount's mount mode.
|
|
236
|
+
|
|
237
|
+
**Notes on `--repo`:**
|
|
238
|
+
|
|
239
|
+
- `--repo` is honored only on container create. To change the mount on an existing container you must `start --force` (which recreates), passing `--repo` again. The app db volume persists, so users/sessions/saved questions survive.
|
|
240
|
+
- The host path must be a directory and must already exist. The CLI does not create or initialize a git repo for you.
|
|
241
|
+
- For `--repo-branch` auto-detection, the path needs to be a git repo (a `.git/` ancestor); otherwise pass `--repo-branch` explicitly.
|
|
242
|
+
- The `--repo-branch` value must name a branch that already exists **locally** in the host repo. Local-only branches (never pushed to `origin`) are fine — the workspace operates against the bind-mounted working tree, never pushes anywhere itself, and the remote side is created on the user's first `git push` later. If the branch doesn't exist locally yet, create it before `workspace start`: `git -C <repo-path> branch <name>` (or `checkout -b <name>` if you also want to switch HEAD).
|
|
243
|
+
- File-permission gotcha (Linux only): the Metabase container runs as uid 2000 by default; the host directory must be writable by that uid for `git-sync export` to succeed. macOS Docker Desktop / OrbStack / Colima handle this via their file-sharing layer.
|
|
244
|
+
|
|
245
|
+
## Interact with a running workspace
|
|
246
|
+
|
|
247
|
+
`url` and `credentials` both return JSON envelopes. Extract fields with `jq`:
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
mb workspace url <ws-id> --json
|
|
251
|
+
# → {"workspace_id": ..., "url": "http://localhost:3000"}
|
|
252
|
+
|
|
253
|
+
mb workspace credentials <ws-id> --json
|
|
254
|
+
# → {"email": ..., "password": ..., "api_key": ...}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Save the child's API key as its own named profile. **Always pipe the key on stdin** (the CLI rejects `--api-key "$VAR"`).
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
WS_URL=$(mb workspace url <ws-id> --json | jq -r '.url')
|
|
261
|
+
WS_API_KEY=$(mb workspace credentials <ws-id> --json | jq -r '.api_key')
|
|
262
|
+
printf '%s' "$WS_API_KEY" | mb auth login \
|
|
263
|
+
--url "$WS_URL" \
|
|
264
|
+
--profile <ws-name> \
|
|
265
|
+
--json
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Convention: use the workspace name as the profile name (`my_nice_ws` workspace → `my_nice_ws` profile). Then drive the child with the same CLI verbs:
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
mb database list --profile <ws-name> --json
|
|
272
|
+
mb card list --profile <ws-name> --json
|
|
273
|
+
mb transform list --profile <ws-name> --json
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
To create and run a transform in the workspace, load the `transform` skill. The `<db-id-in-child>` referenced there comes from `mb database list --profile <ws-name> --json` — the child re-numbers databases independently of the parent.
|
|
277
|
+
|
|
278
|
+
## Open the UI
|
|
279
|
+
|
|
280
|
+
```
|
|
281
|
+
http://localhost:<port> # default 3000; honors `--port` from `workspace start`
|
|
282
|
+
http://localhost:<port>/admin/transforms/<transform-id>
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Log in with the **admin email + password** from `workspace credentials` (the API key authenticates as a synthetic api-key user, not as the admin — many UI screens hide content from the api-key user).
|
|
286
|
+
|
|
287
|
+
**Don't open the URL before `state: "running"`** — the Metabase setup wizard will hijack it and create a fresh app db, bypassing the workspace bring-up.
|
|
288
|
+
|
|
289
|
+
## Lifecycle
|
|
290
|
+
|
|
291
|
+
| User intent | Command |
|
|
292
|
+
| --------------------------------- | -------------------------------------------------------------- |
|
|
293
|
+
| List local workspace containers | `mb workspace ps` |
|
|
294
|
+
| Tail logs | `mb workspace logs <ws-id> --tail 200` |
|
|
295
|
+
| Follow logs | `mb workspace logs <ws-id> --follow` |
|
|
296
|
+
| Read admin email/password/API key | `mb workspace credentials <ws-id> --json` |
|
|
297
|
+
| Stop (preserves app db) | `mb workspace stop <ws-id>` |
|
|
298
|
+
| Restart | `mb workspace start <ws-id> --force --wait --profile <parent>` |
|
|
299
|
+
| Remove container + app db | `mb workspace delete <ws-id> --yes` |
|
|
300
|
+
| Remove container, keep app db | `mb workspace delete <ws-id> --keep-volume --yes` |
|
|
301
|
+
|
|
302
|
+
The supported restart path is `stop` + `start --force` (or `start --force` directly). The app db volume persists across `stop`/`start` cycles, so users/sessions/saved questions survive. `delete`, `start --force`, and `stop` are destructive enough to confirm before running unless the user explicitly asked for them.
|
|
303
|
+
|
|
304
|
+
## Diagnose
|
|
305
|
+
|
|
306
|
+
Pick the symptom.
|
|
307
|
+
|
|
308
|
+
### `start` succeeds but the database isn't visible in the UI
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
mb workspace logs <ws-id> --tail 300 | grep -iE "advanced-config|workspace|error"
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
| Log signal | Cause | Fix |
|
|
315
|
+
| ---------------------------------------------------------------- | ----------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
|
|
316
|
+
| `Spec assertion failed ... :input ... :output` | Parent emits keys the child's spec doesn't accept (server-side). | File against the parent. Not a CLI issue. |
|
|
317
|
+
| `Connection refused` / `unknown host` against the warehouse host | Container can't reach the source DB. | Source DB credentials configured on the parent use a host that doesn't resolve from inside docker. Use a routable hostname. |
|
|
318
|
+
| `Invalid token` / `License expired` | EE license bad or unset on the parent (forwarded into the child). | Re-set on the parent: `mb workspace license set` (operator pastes). |
|
|
319
|
+
|
|
320
|
+
### `workspace credentials` returns values that don't authenticate
|
|
321
|
+
|
|
322
|
+
Symptom: right after `workspace start`, the API key returned by `mb workspace credentials <ws-id>` is rejected by the child (`Unauthenticated` on `/api/user/current`, or `Invalid or unauthorized API key` from `mb auth login --skip-verify` followed by any verb). The admin password from the same response also fails (`did not match stored password`). The values inside the container's `/mw-config/credentials.json` match what the parent reports, but the child's app db has different state.
|
|
323
|
+
|
|
324
|
+
This is a parent↔child credential drift bug — the parent's record for the workspace can desync from the child's app db, especially after a rapid `start` → `start --force` sequence on the same port. **`start --force` alone does not fix it** (the volume persists across the recreate; the api-key already exists from the prior init and the new credentials.json is ignored).
|
|
325
|
+
|
|
326
|
+
Recovery (works reliably):
|
|
327
|
+
|
|
328
|
+
```bash
|
|
329
|
+
mb workspace delete <ws-id> --yes # destroys container + volume; keeps parent record + provisioned dbs
|
|
330
|
+
mb workspace start <ws-id> --port <fresh-port> --wait --profile <parent> # different port from the bad attempt
|
|
331
|
+
mb workspace credentials <ws-id> --json | jq -r '.api_key' \
|
|
332
|
+
| xargs -I{} curl -s -H "x-api-key: {}" http://localhost:<fresh-port>/api/user/current # smoke check
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Why "different port": empirically, restarting on the same port after the drifted attempt can cling to the same broken state; switching ports forces a clean parent-side handoff. If you must reuse the original port, `workspace delete --yes` plus a brief pause (a few seconds) before `start` increases the success rate.
|
|
336
|
+
|
|
337
|
+
`workspace delete --yes` is destructive — it drops the container _and_ the app db volume — but in the bring-up window (before any user content has been imported) there's nothing to lose. The provisioned-database records on the parent survive the delete and don't need to be re-created.
|
|
338
|
+
|
|
339
|
+
### Container exited shortly after `start`
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
mb workspace ps
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
`Exited (137)` → OOM. Bump Docker host memory to ≥ 6 GB.
|
|
346
|
+
|
|
347
|
+
- Colima: `colima stop && colima start --memory 6 --cpu 2`
|
|
348
|
+
- Docker Desktop: Settings → Resources → Memory.
|
|
349
|
+
|
|
350
|
+
Then `mb workspace start <ws-id> --force --wait --profile <parent>`.
|
|
351
|
+
|
|
352
|
+
### `Endpoint not found — is this a Metabase instance?`
|
|
353
|
+
|
|
354
|
+
The parent doesn't expose `/api/ee/workspace-manager/*`. Either:
|
|
355
|
+
|
|
356
|
+
- Parent is OSS (no EE).
|
|
357
|
+
- Parent has no license, or license lacks the workspace feature.
|
|
358
|
+
- Parent is on a Metabase version that predates workspaces.
|
|
359
|
+
|
|
360
|
+
Confirm the URL points at the right instance with `mb auth status --profile <parent> --json`. If the URL is correct, the parent simply lacks the workspace feature — pick a different instance.
|
|
361
|
+
|
|
362
|
+
### `workspace has no databases — provision at least one before starting`
|
|
363
|
+
|
|
364
|
+
`mb workspace list --profile <parent> --full --json` will show the workspace with `databases: []`. Run a `provision` (step 4) and retry.
|
|
365
|
+
|
|
366
|
+
### `workspace ... is not ready: database X=provisioning`
|
|
367
|
+
|
|
368
|
+
Provisioning is async on the parent. Re-run the original `provision` with `--wait`, or poll:
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
mb workspace list --profile <parent> --full --json \
|
|
372
|
+
| jq '.data[] | select(.id==<ws-id>) | .databases[] | {database_id, status}'
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Workspace UI demands the setup wizard
|
|
376
|
+
|
|
377
|
+
You opened the URL before health passed and walked through the wizard, which created a fresh app db and bypassed the workspace bring-up. `mb workspace delete <ws-id> --yes` then `start --wait` again. Don't open the URL before `state: "running"`.
|
|
378
|
+
|
|
379
|
+
### `git status` on the host shows confusing "staged changes" after `git-sync export`
|
|
380
|
+
|
|
381
|
+
The in-container exporter writes the new commit object directly into the bind-mounted `.git/` and advances HEAD, but does not update the host's working tree or index. The host then shows the export's content as "Changes to be committed" reverting to the prior commit — display artifact, not a real revert. The non-destructive realignment is `git -C <repo> restore --staged --worktree .` (only touches paths that disagree with HEAD; refuses on unmerged paths; does not move HEAD). See the `git-sync` skill, "Working-tree drift on `--repo` bind-mount workspaces" for the full decision tree (when to stash first, when `reset --hard` is acceptable).
|
|
382
|
+
|
|
383
|
+
## Don't (workspace-specific)
|
|
384
|
+
|
|
385
|
+
- Don't run raw `docker` commands against the workspace container — use the `mb workspace` subcommands. They wrap the right labels, volumes, network, and lifecycle hooks.
|
|
386
|
+
- Don't open the workspace URL before `state: "running"` — the setup wizard will hijack it.
|
|
387
|
+
- Don't try to share an API key across workspaces — each child mints its own. Save credentials per-workspace under a profile named after the workspace.
|
|
388
|
+
- Don't write the workspace's isolation schema (`mb__isolation_<hash>_<ws-id>`) into transform/card SQL or `target.schema`. Author against the **canonical** schema (e.g. `public`); the QP rewrites at execution time. Hard-coding the isolation prefix breaks portability across workspaces and bypasses the rewrite contract.
|
|
389
|
+
- Don't run `workspace start` without first asking the user about Remote Sync (current dir / custom path / no sync). The bind mount is set at create time; "I'll add it after start" is not supported.
|
|
390
|
+
- Don't run `workspace start --repo <path>` when the host repo is on `main`/`master` without first asking the user (see "Branch guard before `--repo`"). The host's HEAD becomes the workspace's `remote-sync-branch`, so every subsequent export targets `main` by default.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: metabase-cli
|
|
3
|
+
description: Drive a Metabase instance from the terminal via the `mb` CLI — auth, databases, cards, dashboards, transforms, queries, search, git-sync, Enterprise workspaces. Discovery entry; load the full guide with `mb skills get core`.
|
|
4
|
+
allowed-tools: Bash, Read, Write, Edit, AskUserQuestion
|
|
5
|
+
hidden: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# metabase-cli
|
|
9
|
+
|
|
10
|
+
The official Metabase CLI (`mb`) drives a Metabase instance over its REST API.
|
|
11
|
+
|
|
12
|
+
Install: `npm i -g @metabase/cli`
|
|
13
|
+
|
|
14
|
+
## Start here
|
|
15
|
+
|
|
16
|
+
Before running any `mb` command, load the workflow content from the CLI:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
mb skills get core # auth, flag conventions, every command group
|
|
20
|
+
mb skills list # everything available on the installed version
|
|
21
|
+
```
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import "./package-D-aVYFKM.mjs";
|
|
2
|
-
import "./command-augment-D9pI9Vbh.mjs";
|
|
3
|
-
import "./render-DuoDUTVL.mjs";
|
|
4
|
-
import "./predicates-CGO17Q15.mjs";
|
|
5
|
-
import "./runtime-Br8L4NPm.mjs";
|
|
6
|
-
import "./parse-id-B3B-0hUA.mjs";
|
|
7
|
-
import "./poll-task-0b1V6G-8.mjs";
|
|
8
|
-
import "./poll-BCnrcUVf.mjs";
|
|
9
|
-
import { SyncSettingsUpdateResult, add_collection_default, setCollectionRemoteSynced, syncSettingsUpdateView } from "./add-collection-CffaBB-Y.mjs";
|
|
10
|
-
|
|
11
|
-
export { add_collection_default as default };
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { defineCommand } from "citty";
|
|
2
|
-
|
|
3
|
-
//#region src/commands/api-key/index.ts
|
|
4
|
-
var api_key_default = defineCommand({
|
|
5
|
-
meta: {
|
|
6
|
-
name: "api-key",
|
|
7
|
-
description: "Manage Metabase API keys"
|
|
8
|
-
},
|
|
9
|
-
subCommands: { create: () => import("./create-ov-De5dO.mjs").then((mod) => mod.default) }
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
//#endregion
|
|
13
|
-
export { api_key_default as default };
|
package/dist/auth-N4w5xtwW.mjs
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { defineCommand } from "citty";
|
|
2
|
-
|
|
3
|
-
//#region src/commands/auth/index.ts
|
|
4
|
-
var auth_default = defineCommand({
|
|
5
|
-
meta: {
|
|
6
|
-
name: "auth",
|
|
7
|
-
description: "Authenticate against a Metabase instance"
|
|
8
|
-
},
|
|
9
|
-
default: "login",
|
|
10
|
-
subCommands: {
|
|
11
|
-
login: () => import("./login-SXsSH0I1.mjs").then((m) => m.default),
|
|
12
|
-
status: () => import("./status-BWep0PFe.mjs").then((m) => m.default),
|
|
13
|
-
list: () => import("./list-BNzdnE1c.mjs").then((m) => m.default),
|
|
14
|
-
logout: () => import("./logout-bgOXjxbN.mjs").then((m) => m.default)
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
//#endregion
|
|
19
|
-
export { auth_default as default };
|
package/dist/card-4rZRb5bc.mjs
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { defineCommand } from "citty";
|
|
2
|
-
|
|
3
|
-
//#region src/commands/card/index.ts
|
|
4
|
-
var card_default = defineCommand({
|
|
5
|
-
meta: {
|
|
6
|
-
name: "card",
|
|
7
|
-
description: "Manage Metabase cards (questions, models, metrics)"
|
|
8
|
-
},
|
|
9
|
-
subCommands: {
|
|
10
|
-
list: () => import("./list-xQmtQPSl.mjs").then((mod) => mod.default),
|
|
11
|
-
get: () => import("./get-BOtKerj8.mjs").then((mod) => mod.default),
|
|
12
|
-
query: () => import("./query-DxA353Hy.mjs").then((mod) => mod.default),
|
|
13
|
-
create: () => import("./create-doyv3SxU.mjs").then((mod) => mod.default),
|
|
14
|
-
update: () => import("./update-djgvzO3K.mjs").then((mod) => mod.default),
|
|
15
|
-
archive: () => import("./archive-uJrslh9r.mjs").then((mod) => mod.default)
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
//#endregion
|
|
20
|
-
export { card_default as default };
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { defineCommand } from "citty";
|
|
2
|
-
|
|
3
|
-
//#region src/commands/collection/index.ts
|
|
4
|
-
var collection_default = defineCommand({
|
|
5
|
-
meta: {
|
|
6
|
-
name: "collection",
|
|
7
|
-
description: "Browse Metabase collections"
|
|
8
|
-
},
|
|
9
|
-
subCommands: {
|
|
10
|
-
list: () => import("./list-DhWG5jiW.mjs").then((mod) => mod.default),
|
|
11
|
-
get: () => import("./get-C_6K7MSW.mjs").then((mod) => mod.default),
|
|
12
|
-
items: () => import("./items-CTcAMknV.mjs").then((mod) => mod.default),
|
|
13
|
-
tree: () => import("./tree-DazZT7dR.mjs").then((mod) => mod.default),
|
|
14
|
-
create: () => import("./create-izE3EKCt.mjs").then((mod) => mod.default)
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
//#endregion
|
|
19
|
-
export { collection_default as default };
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
//#region src/runtime/command-augment.ts
|
|
2
|
-
const augments = new WeakMap();
|
|
3
|
-
function setMetabaseAugment(cmd, augment) {
|
|
4
|
-
augments.set(cmd, augment);
|
|
5
|
-
}
|
|
6
|
-
function getMetabaseAugment(cmd) {
|
|
7
|
-
return augments.get(cmd) ?? null;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
//#endregion
|
|
11
|
-
export { getMetabaseAugment, setMetabaseAugment };
|
package/dist/create-doyv3SxU.mjs
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import "./package-D-aVYFKM.mjs";
|
|
2
|
-
import "./command-augment-D9pI9Vbh.mjs";
|
|
3
|
-
import { renderItem } from "./render-DuoDUTVL.mjs";
|
|
4
|
-
import "./predicates-CGO17Q15.mjs";
|
|
5
|
-
import "./input-BQ-BZA8h.mjs";
|
|
6
|
-
import { connectionFlags, defineMetabaseCommand, outputFlags, profileFlag } from "./runtime-Br8L4NPm.mjs";
|
|
7
|
-
import { readBody } from "./body-D6dHGjMT.mjs";
|
|
8
|
-
import { bodyInputFlags } from "./body-flags-BK7J6Daz.mjs";
|
|
9
|
-
import "./field-B3gvaqpK.mjs";
|
|
10
|
-
import { Card, CardCreateInput, cardView } from "./card-CQxvHeyP.mjs";
|
|
11
|
-
import "./validate-CB0bu50i.mjs";
|
|
12
|
-
import { CARD_DATASET_QUERY_LABELS, preflightMbql5Query, skipValidateFlag } from "./validate-query-CavIA0Q2.mjs";
|
|
13
|
-
|
|
14
|
-
//#region src/commands/card/create.ts
|
|
15
|
-
var create_default = defineMetabaseCommand({
|
|
16
|
-
meta: {
|
|
17
|
-
name: "create",
|
|
18
|
-
description: "Create a card from a JSON spec; if dataset_query is MBQL 5 (lib/type: mbql/query) it is pre-flight-validated against the same schema as `mb query` (see `mb query --print-schema`)"
|
|
19
|
-
},
|
|
20
|
-
args: {
|
|
21
|
-
...outputFlags,
|
|
22
|
-
...profileFlag,
|
|
23
|
-
...connectionFlags,
|
|
24
|
-
...bodyInputFlags,
|
|
25
|
-
...skipValidateFlag
|
|
26
|
-
},
|
|
27
|
-
outputSchema: Card,
|
|
28
|
-
examples: [
|
|
29
|
-
"cat card.json | mb card create",
|
|
30
|
-
"mb card create --file card.json",
|
|
31
|
-
"mb card create --body '{\"name\":\"x\",\"display\":\"table\",\"dataset_query\":{...},\"visualization_settings\":{}}'",
|
|
32
|
-
"mb card create --file card.json --skip-validate"
|
|
33
|
-
],
|
|
34
|
-
async run({ args, ctx, getClient }) {
|
|
35
|
-
const body = await readBody({
|
|
36
|
-
flag: args.body,
|
|
37
|
-
file: args.file
|
|
38
|
-
}, CardCreateInput);
|
|
39
|
-
preflightMbql5Query(body.dataset_query, CARD_DATASET_QUERY_LABELS, { skip: args["skip-validate"] === true });
|
|
40
|
-
const client = await getClient();
|
|
41
|
-
const created = await client.requestParsed(Card, "/api/card", {
|
|
42
|
-
method: "POST",
|
|
43
|
-
body
|
|
44
|
-
});
|
|
45
|
-
renderItem(created, cardView, ctx);
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
//#endregion
|
|
50
|
-
export { create_default as default };
|