@metabase/cli 0.2.2 → 0.3.0-alpha.extract-api-client.e9b2dd5
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/README.md +4 -1902
- package/dist/add-collection-B3Ty8SzB.mjs +36 -0
- package/dist/alert-DT-ee7yf.mjs +23 -0
- package/dist/{alerts-CRGMX4eB.mjs → alerts-Bf2RH_p_.mjs} +11 -17
- package/dist/{append-lL0IaI6U.mjs → append-DFzQRPMd.mjs} +7 -12
- package/dist/{archive-DihHU042.mjs → archive-4tmm946K.mjs} +8 -17
- package/dist/{archive-C9mJGN6J.mjs → archive-B6eXiyER.mjs} +7 -13
- package/dist/{archive-D3DErepm.mjs → archive-BxNPHxOY.mjs} +7 -13
- package/dist/{archive-DyPsHSFb.mjs → archive-ByvA49Kz.mjs} +8 -13
- package/dist/{archive-ExcEma7q.mjs → archive-C7w49Grr.mjs} +7 -16
- package/dist/{archive-Baz4OVDd.mjs → archive-Cd29QC0c.mjs} +7 -13
- package/dist/{archive-CUGRnyRV.mjs → archive-ChORH5JH.mjs} +7 -13
- package/dist/{archive-hKPQXgXa.mjs → archive-CwAdlv63.mjs} +7 -12
- package/dist/{archive-D8YrImIU.mjs → archive-Dr9Xnx_Z.mjs} +7 -15
- package/dist/{archive-BUFc1me2.mjs → archive-gid9Y09v.mjs} +8 -17
- package/dist/{archive-3rLXY0nS.mjs → archive-lQqs13Uz.mjs} +7 -13
- package/dist/auth-Dltxua89.mjs +19 -0
- package/dist/{body-DXJ2v3GY.mjs → body-Cur4NJt9.mjs} +5 -8
- package/dist/{body-flags-Cbv-Ba6W.mjs → body-flags-AqKpN01Y.mjs} +1 -2
- package/dist/{branches-ByCzTuiv.mjs → branches-DchlyU0i.mjs} +7 -13
- package/dist/{cancel-DvehNbDD.mjs → cancel-BMQa5R1Z.mjs} +5 -12
- package/dist/{cancel-task-BM8ADd3Y.mjs → cancel-task-C5CU1pxU.mjs} +6 -10
- package/dist/card-CHI2bmds.mjs +24 -0
- package/dist/{card-BFRRyTGs.mjs → card-DZxRmver.mjs} +21 -48
- package/dist/card-zyUkr2nS.mjs +44 -0
- package/dist/cards-SccWvRQD.mjs +34 -0
- package/dist/cli.mjs +664 -85
- package/dist/client-BqhzKdLM.mjs +2194 -0
- package/dist/collection-CGoVLvcD.mjs +58 -0
- package/dist/collection-CY0AB99E.mjs +20 -0
- package/dist/{collection-BrrjbPLN.mjs → collection-CfulNRBG.mjs} +16 -61
- package/dist/{collection-namespace-8i_CvWsg.mjs → collection-namespace-GwydWBF3.mjs} +3 -5
- package/dist/{create-D6bXJlBS.mjs → create-BNQ_DytP.mjs} +8 -15
- package/dist/{create-CtIOvInN.mjs → create-BOLwWt8Q.mjs} +9 -17
- package/dist/{create--_4GZUNY.mjs → create-Beh-2uoN.mjs} +8 -16
- package/dist/{create-BnNRvoyz.mjs → create-C79vErX0.mjs} +9 -20
- package/dist/{create-D57tRDrg.mjs → create-CAQV2g6V.mjs} +9 -17
- package/dist/{create-CHK9cJcK.mjs → create-CAWytBE5.mjs} +9 -17
- package/dist/{create-BynNiG1N.mjs → create-CCh_LyW9.mjs} +8 -15
- package/dist/{create-Cy57naL0.mjs → create-CD008jdE.mjs} +13 -28
- package/dist/{create-DOpa2TmH.mjs → create-CHQx7jq4.mjs} +9 -17
- package/dist/{create-COI80Veo.mjs → create-DF4QtgDL.mjs} +8 -16
- package/dist/{create-yK1-fuX5.mjs → create-DJmXXQoq.mjs} +10 -18
- package/dist/{create-1O1UiTVt.mjs → create-DV-Ln5Mo.mjs} +8 -15
- package/dist/create-branch-CYo6jZth.mjs +35 -0
- package/dist/{create-t7z8Eb4T.mjs → create-i14Wv3Tj.mjs} +8 -15
- package/dist/{create-qzjhJFP8.mjs → create-tQbnuz-I.mjs} +8 -15
- package/dist/{create-DAj8pyfQ.mjs → create-zeveFdnd.mjs} +6 -11
- package/dist/{cron-93wjhtrP.mjs → cron-LKRffEC-.mjs} +2 -4
- package/dist/{csv-yoFA1hhv.mjs → csv-B2Jgb1GM.mjs} +7 -19
- package/dist/{current-task-D8Q1uFmk.mjs → current-task-MpBFGttd.mjs} +7 -12
- package/dist/{dashboard-BSCXTX4H.mjs → dashboard-BCpSq_G-.mjs} +9 -60
- package/dist/dashboard-BHJBoaG7.mjs +33 -0
- package/dist/dashboard-CsIFFpzK.mjs +58 -0
- package/dist/database-B19qlLKZ.mjs +38 -0
- package/dist/database-C8G6ZKG0.mjs +26 -0
- package/dist/db-DsfojyeH.mjs +24 -0
- package/dist/{delete-B8dui6O6.mjs → delete-B8e01xAG.mjs} +6 -11
- package/dist/{delete-CA7IjA5J.mjs → delete-BApOjQvl.mjs} +6 -11
- package/dist/{delete-Dcu3iDkg.mjs → delete-Ck0q4Dzp.mjs} +6 -11
- package/dist/{delete-BwPgIWV8.mjs → delete-DFmX7jTp.mjs} +6 -11
- package/dist/{delete-CawUDFud.mjs → delete-Ob06PPie.mjs} +6 -11
- package/dist/{delete-runtime-00-R9iJm.mjs → delete-runtime-D74s6vhu.mjs} +6 -13
- package/dist/{delete-table-DS0b3i0k.mjs → delete-table-DuyhGrvA.mjs} +6 -11
- package/dist/{dependencies-BQVZylqV.mjs → dependencies-Bj3QrGIm.mjs} +10 -13
- package/dist/dirty-DNYoM6Bp.mjs +31 -0
- package/dist/document-BbskZPTT.mjs +19 -0
- package/dist/{document-C7IR6T8c.mjs → document-Cd2mmqCp.mjs} +5 -33
- package/dist/document-D6OybtP2.mjs +29 -0
- package/dist/{eid-DcCAs-RP.mjs → eid-WTZmDdsr.mjs} +12 -54
- package/dist/eid-translation-zEHtr5Oc.mjs +35 -0
- package/dist/{embedding-B0x9CPkU.mjs → embedding-i-KqpefC.mjs} +2 -4
- package/dist/events-Bn7jh5lP.mjs +39 -0
- package/dist/export-BjfoZRDJ.mjs +66 -0
- package/dist/field-BPsNa-TO.mjs +80 -0
- package/dist/{field-RYBTW2KS.mjs → field-Cr2DPUG9.mjs} +2 -81
- package/dist/field-Zau6Hc6X.mjs +18 -0
- package/dist/fields-CBBa-Prp.mjs +34 -0
- package/dist/{get-vGqyjGSW.mjs → get-00lyLv6R.mjs} +7 -11
- package/dist/{get-Cvkt6GcQ.mjs → get-B7sMgsFs.mjs} +7 -11
- package/dist/get-B_xdF1kq.mjs +31 -0
- package/dist/{get-DhS4y0K9.mjs → get-ByBOq30r.mjs} +7 -11
- package/dist/get-Bz99dAxK.mjs +35 -0
- package/dist/get-C2-Q3iQ5.mjs +31 -0
- package/dist/get-CDJFVvhj.mjs +35 -0
- package/dist/get-CDMGbgrh.mjs +31 -0
- package/dist/get-CnbUCzDM.mjs +31 -0
- package/dist/{get-CJYv3CfV.mjs → get-CvI--9S_.mjs} +16 -12
- package/dist/get-DRvsATp-.mjs +31 -0
- package/dist/{get-Db8YNxId.mjs → get-DY03Azx9.mjs} +6 -11
- package/dist/{get-4rA8pbRu.mjs → get-DYO1kywc.mjs} +7 -13
- package/dist/{get-DZRB5S37.mjs → get-D_h-G9bn.mjs} +7 -11
- package/dist/get-DdsoHaKH.mjs +31 -0
- package/dist/{get-DQ0pb4tT.mjs → get-DkWO0EI0.mjs} +7 -11
- package/dist/{get-DXiL39gi.mjs → get-Dmj9Ygz1.mjs} +7 -17
- package/dist/{get-CvXOGyFj.mjs → get-DmpB6J9g.mjs} +7 -12
- package/dist/{get-B2vvnMpl.mjs → get-Dn9ZiSZr.mjs} +8 -16
- package/dist/{get-run-BF5ZBE8o.mjs → get-run-Cnux9ln0.mjs} +7 -11
- package/dist/git-sync-BDnxPrqU.mjs +104 -0
- package/dist/git-sync-DxD_ng48.mjs +136 -0
- package/dist/git-sync-MQQYRYLg.mjs +28 -0
- package/dist/{group-DfTvzGz-.mjs → group-DBM87IgC.mjs} +2 -4
- package/dist/has-remote-changes-C684Redh.mjs +35 -0
- package/dist/import-Rmmttboo.mjs +56 -0
- package/dist/{input-BpXlmJ5J.mjs → input-DH9i67x_.mjs} +6 -11
- package/dist/{is-dirty-4kIyD4oP.mjs → is-dirty-DYeJYhBz.mjs} +4 -8
- package/dist/{items-BXH0f1r6.mjs → items-CQlPXBXE.mjs} +13 -29
- package/dist/key-BEhqIzIQ.mjs +10 -0
- package/dist/{library-BFZYsBcz.mjs → library-Bsm5KsFF.mjs} +6 -9
- package/dist/library-ClLgrAXy.mjs +19 -0
- package/dist/library-DfYAT2Ep.mjs +21 -0
- package/dist/{list-By1yYVGq.mjs → list-B4hJuMhA.mjs} +11 -27
- package/dist/{list-oRyXsJXs.mjs → list-B8nAGwP4.mjs} +10 -12
- package/dist/list-BOET6zhP.mjs +36 -0
- package/dist/list-BP3MPq-M.mjs +36 -0
- package/dist/list-BpFp9Wre.mjs +28 -0
- package/dist/list-BtkWNtvY.mjs +34 -0
- package/dist/list-BtmsNOEn.mjs +28 -0
- package/dist/list-BzVTHOoH.mjs +28 -0
- package/dist/list-C5Zs8nrb.mjs +28 -0
- package/dist/list-CKK-s3Ty.mjs +28 -0
- package/dist/{list-SiSfX4wl.mjs → list-CMpoc2TN.mjs} +10 -13
- package/dist/list-CNUiF8Z7.mjs +28 -0
- package/dist/list-CQDprvW0.mjs +39 -0
- package/dist/{list-w61Uovt2.mjs → list-CQwOgIlj.mjs} +10 -24
- package/dist/list-DE9FM957.mjs +28 -0
- package/dist/{list-CG3ec9BZ.mjs → list-DF2rvoO1.mjs} +22 -14
- package/dist/list-DMKDsBRd.mjs +38 -0
- package/dist/{list-Bar8uQRC.mjs → list-UhpKG6_m.mjs} +15 -15
- package/dist/{login-zUCMDLC8.mjs → login-CGHeSjlP.mjs} +47 -52
- package/dist/{logout-BFh7QHx6.mjs → logout-DAxOlbv4.mjs} +7 -12
- package/dist/measure-4P0bOlZ3.mjs +29 -0
- package/dist/measure-BsYv0p26.mjs +22 -0
- package/dist/{measure-DH3ow2ko.mjs → measure-CtfjqJFP.mjs} +3 -30
- package/dist/network-error-CEb9ymDW.mjs +423 -0
- package/dist/{notification-BR5wehqg.mjs → notification-DaA4bBHW.mjs} +3 -66
- package/dist/notification-Fcq1fIPw.mjs +67 -0
- package/dist/{parameter-hCQ80jnc.mjs → parameter-DUKR8NXo.mjs} +2 -11
- package/dist/{parameter-values-BYDFzxNY.mjs → parameter-values-CJTdqd8c.mjs} +17 -11
- package/dist/{parse-enum-D1zTYr_i.mjs → parse-enum-D0Jfm_Bz.mjs} +2 -4
- package/dist/{parse-id-waEVK-uR.mjs → parse-id-C_jmKFaa.mjs} +2 -4
- package/dist/{parse-ref-C3DUXwci.mjs → parse-ref-ibKVB0J4.mjs} +3 -5
- package/dist/{path-Bvs9IR4k.mjs → path-B5gHvrYL.mjs} +8 -9
- package/dist/{poll-rqLkEJVP.mjs → poll-75SvT7Zv.mjs} +11 -15
- package/dist/predicates-Bkm2IoeX.mjs +170 -0
- package/dist/preflight-YpPQo-sa.mjs +29 -0
- package/dist/{process-BxAUv3Wx.mjs → process-DBOOrmET.mjs} +4 -6
- package/dist/{publish-C1y_vGGw.mjs → publish-DijrxaCW.mjs} +29 -24
- package/dist/{pulse-C8q469KD.mjs → pulse-BmsDenXm.mjs} +3 -56
- package/dist/pulse-DhG6I9mI.mjs +57 -0
- package/dist/{query-B9LlGOZI.mjs → query-BbqECzmF.mjs} +20 -44
- package/dist/{query-DpVWKW-y.mjs → query-Dc__-KJH.mjs} +13 -24
- package/dist/{query-result-Bvh89K4f.mjs → query-result-DBDwk8TS.mjs} +4 -8
- package/dist/remove-collection-BOnxYvhj.mjs +36 -0
- package/dist/{render-rgZBe3rO.mjs → render-CI93iP0I.mjs} +9 -11
- package/dist/{replace-sHtPBNnA.mjs → replace-Cq4CxVa-.mjs} +7 -12
- package/dist/rescan-values-Dr_j416g.mjs +31 -0
- package/dist/revision-message-flag-8TCHYJV6.mjs +8 -0
- package/dist/run-C3DkS6NP.mjs +82 -0
- package/dist/{run-Ce_gPVcg.mjs → run-DdGJ544X.mjs} +8 -18
- package/dist/runs-CUNWiidL.mjs +38 -0
- package/dist/runtime-DiKyBxOw.mjs +1007 -0
- package/dist/{schema-tables-D91DWM5l.mjs → schema-tables-CV386fnC.mjs} +10 -14
- package/dist/{schemas-OVYRgvz9.mjs → schemas-DM-UqZBQ.mjs} +8 -12
- package/dist/search-DFBwUc6d.mjs +44 -0
- package/dist/{search-msFNbxU6.mjs → search-DmGScXs8.mjs} +14 -73
- package/dist/segment-BzUGM2Fr.mjs +22 -0
- package/dist/segment-C_gl5ag6.mjs +29 -0
- package/dist/{segment-cwIOOF0v.mjs → segment-OsrbUDzw.mjs} +3 -30
- package/dist/{selectors-Bsyhe1zP.mjs → selectors-DGDn0Hto.mjs} +4 -6
- package/dist/{send-BeFpVrwD.mjs → send-CVxxEpDB.mjs} +5 -16
- package/dist/{set-jRnCLhvc.mjs → set-DFolTjbd.mjs} +10 -20
- package/dist/{set-active-CFWtIWzX.mjs → set-active-BD8L1lPX.mjs} +7 -18
- package/dist/setting-CwRYgHYN.mjs +35 -0
- package/dist/setting-DTiLCW8g.mjs +22 -0
- package/dist/setting-DyhaeICs.mjs +17 -0
- package/dist/setup-BbDQN8fe.mjs +44 -0
- package/dist/setup-CO7QV-ao.mjs +21 -0
- package/dist/signal-CMd0EFUa.mjs +16 -0
- package/dist/{skills-CxqkjkdQ.mjs → skills--vK6J-e4.mjs} +4 -6
- package/dist/{skills-DlFbbaoS.mjs → skills-DTCrMcix.mjs} +13 -19
- package/dist/{snippet-1xYckIwc.mjs → snippet-CyKAgIZl.mjs} +3 -32
- package/dist/snippet-DRwKb2x7.mjs +19 -0
- package/dist/snippet-oJLaYW9V.mjs +29 -0
- package/dist/stash-D0S4JmAz.mjs +61 -0
- package/dist/{status-DCIOvsc6.mjs → status-BhswxJL1.mjs} +23 -23
- package/dist/{status-BMEdl-_G.mjs → status-hL1am-HV.mjs} +6 -8
- package/dist/{subscription-BOOBR5Ir.mjs → subscription-_d5fB45F.mjs} +7 -10
- package/dist/{subscriptions-C_1AQpZF.mjs → subscriptions-s4TF6Ax3.mjs} +11 -16
- package/dist/{summary-D1fKnwE3.mjs → summary-BytbDz6e.mjs} +1 -2
- package/dist/summary-CsPvcUwW.mjs +31 -0
- package/dist/sync-schema-DaZ_MV-u.mjs +35 -0
- package/dist/sync-task-BACcrs0i.mjs +37 -0
- package/dist/{table-fDvuvqoU.mjs → table-CeeTJgQb.mjs} +4 -38
- package/dist/table-CsIJNPFf.mjs +37 -0
- package/dist/table-DYoC1kPn.mjs +18 -0
- package/dist/{timeline-BNwnJ3g9.mjs → timeline-CCSU2qOS.mjs} +2 -66
- package/dist/timeline-event-CNQdCTDg.mjs +19 -0
- package/dist/timeline-uDCmwGFT.mjs +66 -0
- package/dist/timeline-xlDbEKWg.mjs +21 -0
- package/dist/transform-Bs0EWijQ.mjs +28 -0
- package/dist/{transform-BkSkCvpx.mjs → transform-D3QX0jxA.mjs} +28 -74
- package/dist/transform-DoTyyXAu.mjs +74 -0
- package/dist/transform-job-50GZHhKH.mjs +37 -0
- package/dist/transform-job-BcLChRup.mjs +22 -0
- package/dist/{transform-job-sJmW_En3.mjs → transform-job-DRrK28IH.mjs} +11 -38
- package/dist/{transform-tag-qTjNVfTW.mjs → transform-tag-CcElGp3C.mjs} +2 -21
- package/dist/transform-tag-Dmb4u3-Q.mjs +21 -0
- package/dist/transform-tag-_tgwlsdB.mjs +18 -0
- package/dist/{transforms-Cosz-Vq3.mjs → transforms-oU4zizbO.mjs} +10 -13
- package/dist/{tree-BkXtw0c4.mjs → tree-CH-f3bjP.mjs} +6 -10
- package/dist/{unpublish-BlGj7omM.mjs → unpublish-BUl9RU5C.mjs} +7 -18
- package/dist/{update-DpIzJ8Ts.mjs → update-BCS5FxKr.mjs} +11 -19
- package/dist/{update-hoCyoR-u.mjs → update-BZPllQoR.mjs} +9 -16
- package/dist/{update-CXlhrcy8.mjs → update-BiFDBcEH.mjs} +9 -16
- package/dist/{update-Cbsgvopz.mjs → update-BzMDeZiU.mjs} +9 -15
- package/dist/{update-CTWsiwme.mjs → update-C4YJQFDS.mjs} +9 -16
- package/dist/{update-CzrbMLZq.mjs → update-CHNSUHK4.mjs} +9 -16
- package/dist/{update-CsYFWkVd.mjs → update-CO4m4wH3.mjs} +10 -21
- package/dist/{update-DDVuyquf.mjs → update-CVPAwJaH.mjs} +10 -18
- package/dist/{update-C9K1QnOo.mjs → update-CmDTljK-.mjs} +9 -17
- package/dist/{update-DrvAdi-O.mjs → update-CvOO8TED.mjs} +9 -17
- package/dist/{update-Tw0kARao.mjs → update-D-NE60AV.mjs} +9 -16
- package/dist/{update-ZJ5T-SBW.mjs → update-Nfi44RrE.mjs} +10 -18
- package/dist/{update-C-ChEcI9.mjs → update-bD_Jh5-s.mjs} +10 -16
- package/dist/{update-CuKHukZ6.mjs → update-cYkvbqK8.mjs} +9 -16
- package/dist/update-dashcard-C3TuYO_k.mjs +50 -0
- package/dist/{update-BHhEtxwC.mjs → update-tX9mKBN_.mjs} +10 -21
- package/dist/{upgrade-D0HiyTxw.mjs → upgrade-DXMV4AyP.mjs} +37 -44
- package/dist/upload-CH632qW6.mjs +46 -0
- package/dist/upload-DAtxBBms.mjs +13 -0
- package/dist/upload-Dey_8rLf.mjs +13 -0
- package/dist/{uuid-D7NcSTj8.mjs → uuid-BJkDWj9p.mjs} +5 -7
- package/dist/validate-BWchyW0V.mjs +1469 -0
- package/dist/{validate-query-Di0VKMF-.mjs → validate-query-DaGYSmpQ.mjs} +4 -5
- package/dist/{values-OBiEVCVH.mjs → values-Dxf1JEoA.mjs} +8 -13
- package/dist/verify-K96rItG5.mjs +89 -0
- package/dist/{wait-B9qNlcTe.mjs → wait-DoMDH226.mjs} +10 -18
- package/dist/{wait-flags-D0FEgH6k.mjs → wait-flags-DWEF9tX4.mjs} +14 -11
- package/dist/window-BkvrP0gB.mjs +64 -0
- package/package.json +5 -35
- package/skill-data/core/SKILL.md +25 -22
- package/skill-data/dashboard/SKILL.md +6 -2
- package/skill-data/document/SKILL.md +2 -2
- package/skill-data/transform/SKILL.md +1 -1
- package/.claude-plugin/marketplace.json +0 -19
- package/dist/add-collection-B-Pf12HY.mjs +0 -60
- package/dist/add-collection-B2PdVMRy.mjs +0 -9
- package/dist/alert-DGcECcDb.mjs +0 -26
- package/dist/auth-B8Yb4ZaN.mjs +0 -22
- package/dist/capabilities-1HTHeDa4.mjs +0 -684
- package/dist/card-BT7ce7uO.mjs +0 -27
- package/dist/cards-Bz6anDNX.mjs +0 -38
- package/dist/cli.d.mts +0 -1
- package/dist/collection-ahFSJT-e.mjs +0 -23
- package/dist/command-augment-D9pI9Vbh.mjs +0 -11
- package/dist/create-branch-BwqFHYcZ.mjs +0 -57
- package/dist/dashboard-C__PiWrv.mjs +0 -36
- package/dist/database-B-xwx5LA.mjs +0 -61
- package/dist/db-BiLWl1J9.mjs +0 -27
- package/dist/dirty-BqdcrfUz.mjs +0 -35
- package/dist/document-C9ykHYOG.mjs +0 -22
- package/dist/events-C9PqRUCO.mjs +0 -44
- package/dist/export-CdKR5nPT.mjs +0 -103
- package/dist/field-P7aJaet9.mjs +0 -21
- package/dist/fields-D2czTLKq.mjs +0 -37
- package/dist/get-75Vdyusm.mjs +0 -49
- package/dist/get-B6fSPCfp.mjs +0 -35
- package/dist/get-BbKC0fTx.mjs +0 -35
- package/dist/get-BfFKIfpX.mjs +0 -36
- package/dist/get-Bpw05IUm.mjs +0 -38
- package/dist/get-CaIYg15K.mjs +0 -41
- package/dist/get-DXydDRVU.mjs +0 -35
- package/dist/get-Dy5f54pV.mjs +0 -35
- package/dist/git-sync-BzievbUz.mjs +0 -31
- package/dist/has-remote-changes-CjDHphUH.mjs +0 -67
- package/dist/import-DzA5Ty9_.mjs +0 -94
- package/dist/is-dirty-Cv0bdKkX.mjs +0 -8
- package/dist/key-B9AAqwjq.mjs +0 -17
- package/dist/list-AyGEaws8.mjs +0 -8
- package/dist/list-BiAVv9jX.mjs +0 -31
- package/dist/list-Bm1PlZEV.mjs +0 -31
- package/dist/list-BtgjcC2w.mjs +0 -31
- package/dist/list-CLgkOcmY.mjs +0 -39
- package/dist/list-CSnpbFML.mjs +0 -31
- package/dist/list-Cn-VruXD.mjs +0 -8
- package/dist/list-D7mMcRG5.mjs +0 -49
- package/dist/list-DOhVPh95.mjs +0 -39
- package/dist/list-DSHteJOl.mjs +0 -32
- package/dist/list-Dl81pkVs.mjs +0 -53
- package/dist/list-DwMcwucR.mjs +0 -31
- package/dist/list-IxaPXKXu.mjs +0 -31
- package/dist/list-hKRQUcGB.mjs +0 -39
- package/dist/listing-1G8iFfZe.mjs +0 -10
- package/dist/measure-DNldT89z.mjs +0 -25
- package/dist/paginate-DuDXvUPr.mjs +0 -49
- package/dist/patch-BpjI9BOz.mjs +0 -20
- package/dist/patch-DrMVqf34.mjs +0 -36
- package/dist/poll-task-CiB1o1wG.mjs +0 -194
- package/dist/preflight-DJOBTWVq.mjs +0 -90
- package/dist/prompt-DmxSLNrO.mjs +0 -52
- package/dist/remove-collection-DYQibG6d.mjs +0 -42
- package/dist/requests-Pc0u7PQS.mjs +0 -96
- package/dist/rescan-values-CuPGOzKI.mjs +0 -40
- package/dist/resolve-BiL5scF0.mjs +0 -91
- package/dist/revision-message-flag-B4BxzDXJ.mjs +0 -11
- package/dist/run-D6jyZ0nv.mjs +0 -135
- package/dist/runs-DRxrpAkU.mjs +0 -53
- package/dist/runtime-CPIHg_Zg.mjs +0 -1699
- package/dist/segment-BIKVTq_n.mjs +0 -25
- package/dist/setting-CApPg0Xr.mjs +0 -20
- package/dist/setting-CDEoJYO0.mjs +0 -55
- package/dist/setup-CruHFQJz.mjs +0 -71
- package/dist/snippet-DdoeL-7N.mjs +0 -22
- package/dist/stash-CR-ESWiX.mjs +0 -108
- package/dist/summary-WUjzNITU.mjs +0 -40
- package/dist/sync-schema-Cq5LzHHc.mjs +0 -54
- package/dist/sync-scope-Dh1wBlrg.mjs +0 -55
- package/dist/table-BK56VDw8.mjs +0 -21
- package/dist/timeline-Dl5pm5tN.mjs +0 -24
- package/dist/timeline-event-5JAeGVSI.mjs +0 -22
- package/dist/transform-ATK6rJBl.mjs +0 -31
- package/dist/transform-job-BezBE6Y8.mjs +0 -25
- package/dist/transform-tag-GqbcG_6_.mjs +0 -21
- package/dist/update-dashcard-Dnfa8_aJ.mjs +0 -73
- package/dist/upload-WT6SywFa.mjs +0 -16
- package/dist/validate-BoY75_M3.mjs +0 -1496
- package/dist/verify-VEB7TBQ_.mjs +0 -79
|
@@ -0,0 +1,1007 @@
|
|
|
1
|
+
import { d as ValidationError, f as errorMessage, i as ConfigError, l as TimeoutError, o as MetabaseError, p as isFileNotFoundError } from "./predicates-Bkm2IoeX.mjs";
|
|
2
|
+
import { B as normalizeFlag, D as consumeLegacyEnvWarnings, E as ENV_URL, O as readEnv, S as ENV_DISABLE_KEYRING, T as ENV_SKIP_PREFLIGHT, V as toAliasArray, a as reportError, b as warn, i as mergeCapabilities, j as version, k as setMetabaseAugment, n as BASELINE_CAPABILITIES, r as checkCapabilities, t as interruptSignal, x as ENV_API_KEY, z as flagConsumesValue } from "./cli.mjs";
|
|
3
|
+
import { _ as parseJsonResult, a as isRetryableStatus, c as normalizeUrl, f as isOAuthExpired, m as JSON_CONTENT_TYPE, o as assertEndpointOrigin, p as oauthCredentialFromTokens, r as HttpError, t as buildNetworkError } from "./network-error-CEb9ymDW.mjs";
|
|
4
|
+
import { defineCommand } from "citty";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { parse } from "semver";
|
|
7
|
+
import { Entry } from "@napi-rs/keyring";
|
|
8
|
+
import { promises } from "node:fs";
|
|
9
|
+
import { dirname, join } from "node:path";
|
|
10
|
+
import { homedir } from "node:os";
|
|
11
|
+
//#region src/commands/parse-integer.ts
|
|
12
|
+
const INTEGER_PATTERN = /^-?\d+$/;
|
|
13
|
+
function parseInteger(value, options) {
|
|
14
|
+
const trimmed = value.trim();
|
|
15
|
+
if (!INTEGER_PATTERN.test(trimmed)) throw new ConfigError(`invalid ${options.name}: "${value}" (expected integer)`);
|
|
16
|
+
const parsed = Number.parseInt(trimmed, 10);
|
|
17
|
+
if (parsed < options.min) throw new ConfigError(`invalid ${options.name}: ${parsed} (must be ≥ ${options.min})`);
|
|
18
|
+
return parsed;
|
|
19
|
+
}
|
|
20
|
+
function parseOptionalInteger(value, options) {
|
|
21
|
+
if (value === void 0) return null;
|
|
22
|
+
return parseInteger(value, options);
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region ../client/src/version/preflight-error.ts
|
|
26
|
+
var CapabilityError = class extends MetabaseError {
|
|
27
|
+
category = "capability";
|
|
28
|
+
isRetryable = false;
|
|
29
|
+
developerDetail;
|
|
30
|
+
constructor(failure) {
|
|
31
|
+
super(failure.detail);
|
|
32
|
+
this.name = "CapabilityError";
|
|
33
|
+
this.developerDetail = failure;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region ../client/src/domain/session-properties.ts
|
|
38
|
+
const ServerVersion = z.object({
|
|
39
|
+
tag: z.string(),
|
|
40
|
+
date: z.string().optional(),
|
|
41
|
+
hash: z.string().optional()
|
|
42
|
+
}).loose();
|
|
43
|
+
const TokenFeatures = z.record(z.string(), z.boolean());
|
|
44
|
+
const SessionProperties = z.object({
|
|
45
|
+
version: ServerVersion,
|
|
46
|
+
"token-features": TokenFeatures.optional()
|
|
47
|
+
}).loose();
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region ../client/src/version/tag.ts
|
|
50
|
+
const ParsedVersion = z.object({
|
|
51
|
+
tag: z.string(),
|
|
52
|
+
major: z.number().int().nonnegative(),
|
|
53
|
+
patch: z.number().int().nonnegative()
|
|
54
|
+
});
|
|
55
|
+
const DEV_BUILD_SUFFIX = "-SNAPSHOT";
|
|
56
|
+
function tryParseTag(tag) {
|
|
57
|
+
if (tag.endsWith(DEV_BUILD_SUFFIX)) return null;
|
|
58
|
+
const parsed = parse(tag);
|
|
59
|
+
if (parsed === null || parsed.major !== 0 && parsed.major !== 1) return null;
|
|
60
|
+
return {
|
|
61
|
+
tag,
|
|
62
|
+
major: parsed.minor,
|
|
63
|
+
patch: parsed.patch
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/core/paths.ts
|
|
68
|
+
const APP_DIR_NAME = "metabase-cli";
|
|
69
|
+
function configDir() {
|
|
70
|
+
if (process.platform === "win32") return join(process.env["APPDATA"] ?? join(homedir(), "AppData", "Roaming"), APP_DIR_NAME);
|
|
71
|
+
return join(process.env["XDG_CONFIG_HOME"] ?? join(homedir(), ".config"), APP_DIR_NAME);
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region src/core/auth/profile-record.ts
|
|
75
|
+
const ProbedUser = z.object({
|
|
76
|
+
id: z.number().int(),
|
|
77
|
+
name: z.string(),
|
|
78
|
+
isAdmin: z.boolean()
|
|
79
|
+
});
|
|
80
|
+
const ProfileLastProbe = z.object({
|
|
81
|
+
at: z.iso.datetime(),
|
|
82
|
+
version: ParsedVersion.nullable(),
|
|
83
|
+
tokenFeatures: TokenFeatures.nullable(),
|
|
84
|
+
user: ProbedUser
|
|
85
|
+
});
|
|
86
|
+
const ProfileFailureKind = z.enum([
|
|
87
|
+
"auth",
|
|
88
|
+
"network",
|
|
89
|
+
"server"
|
|
90
|
+
]);
|
|
91
|
+
const ProfileLastFailure = z.object({
|
|
92
|
+
at: z.iso.datetime(),
|
|
93
|
+
kind: ProfileFailureKind,
|
|
94
|
+
reason: z.string()
|
|
95
|
+
});
|
|
96
|
+
const ProfileOAuth = z.object({
|
|
97
|
+
accessToken: z.string().nullable(),
|
|
98
|
+
refreshToken: z.string().nullable(),
|
|
99
|
+
expiresAt: z.iso.datetime(),
|
|
100
|
+
clientId: z.string()
|
|
101
|
+
}).loose();
|
|
102
|
+
const ProfileAuthMethod = z.enum(["oauth", "apiKey"]);
|
|
103
|
+
const ProfileRecord = z.object({
|
|
104
|
+
name: z.string(),
|
|
105
|
+
url: z.string(),
|
|
106
|
+
apiKey: z.string().nullable(),
|
|
107
|
+
oauth: ProfileOAuth.nullable().default(null),
|
|
108
|
+
lastProbe: ProfileLastProbe.nullable(),
|
|
109
|
+
lastFailure: ProfileLastFailure.nullable()
|
|
110
|
+
}).loose();
|
|
111
|
+
const ProfilesFile = z.object({ profiles: z.array(ProfileRecord) }).loose();
|
|
112
|
+
function profileAuthMethod(record) {
|
|
113
|
+
return record.oauth !== null ? "oauth" : "apiKey";
|
|
114
|
+
}
|
|
115
|
+
//#endregion
|
|
116
|
+
//#region src/core/auth/storage.ts
|
|
117
|
+
const KEYRING_SERVICE = "metabase-cli";
|
|
118
|
+
const PROFILES_FILE = "profiles.json";
|
|
119
|
+
const LEGACY_CREDENTIALS_FILE = "credentials.json";
|
|
120
|
+
const LEGACY_REJECTIONS_FILE = "rejections.json";
|
|
121
|
+
const PROFILES_FILE_MODE = 384;
|
|
122
|
+
const PROFILES_DIR_MODE = 448;
|
|
123
|
+
const DEFAULT_PROFILE = "default";
|
|
124
|
+
const LEGACY_STORAGE_NOTICE = "Old profile storage detected and ignored; re-run `mb auth login` for each profile.";
|
|
125
|
+
const KEYCHAIN_RESIDUAL_NOTICE = "warning: could not remove one or more secrets from the OS keychain; a stored token may remain — remove it manually or retry.";
|
|
126
|
+
const account = {
|
|
127
|
+
profileApiKey: (profile) => `profile:${profile}:apiKey`,
|
|
128
|
+
profileOAuthAccess: (profile) => `profile:${profile}:oauthAccess`,
|
|
129
|
+
profileOAuthRefresh: (profile) => `profile:${profile}:oauthRefresh`
|
|
130
|
+
};
|
|
131
|
+
let legacyWarningPending = false;
|
|
132
|
+
let keychainResidualPending = false;
|
|
133
|
+
let keyringDowngradeNotice = null;
|
|
134
|
+
function profilesFilePath() {
|
|
135
|
+
return join(configDir(), PROFILES_FILE);
|
|
136
|
+
}
|
|
137
|
+
function consumeLegacyStorageWarning() {
|
|
138
|
+
if (!legacyWarningPending) return null;
|
|
139
|
+
legacyWarningPending = false;
|
|
140
|
+
return LEGACY_STORAGE_NOTICE;
|
|
141
|
+
}
|
|
142
|
+
function consumeKeychainResidualWarning() {
|
|
143
|
+
if (!keychainResidualPending) return null;
|
|
144
|
+
keychainResidualPending = false;
|
|
145
|
+
return KEYCHAIN_RESIDUAL_NOTICE;
|
|
146
|
+
}
|
|
147
|
+
function consumeKeyringDowngradeWarning() {
|
|
148
|
+
const message = keyringDowngradeNotice;
|
|
149
|
+
keyringDowngradeNotice = null;
|
|
150
|
+
return message;
|
|
151
|
+
}
|
|
152
|
+
function keyringEnabled() {
|
|
153
|
+
return readEnv(ENV_DISABLE_KEYRING) !== "1";
|
|
154
|
+
}
|
|
155
|
+
function trySetKeyring(key, value) {
|
|
156
|
+
if (!keyringEnabled()) return false;
|
|
157
|
+
try {
|
|
158
|
+
new Entry(KEYRING_SERVICE, key).setPassword(value);
|
|
159
|
+
return true;
|
|
160
|
+
} catch {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function tryReadKeyring(key) {
|
|
165
|
+
if (!keyringEnabled()) return;
|
|
166
|
+
try {
|
|
167
|
+
return new Entry(KEYRING_SERVICE, key).getPassword();
|
|
168
|
+
} catch {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function removeKeyringEntry(key) {
|
|
173
|
+
if (!keyringEnabled()) return "skipped";
|
|
174
|
+
try {
|
|
175
|
+
return new Entry(KEYRING_SERVICE, key).deletePassword() ? "removed" : "absent";
|
|
176
|
+
} catch {
|
|
177
|
+
return "failed";
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
async function readProfilesFile() {
|
|
181
|
+
const path = profilesFilePath();
|
|
182
|
+
let raw;
|
|
183
|
+
try {
|
|
184
|
+
raw = await promises.readFile(path, "utf8");
|
|
185
|
+
} catch (error) {
|
|
186
|
+
if (isFileNotFoundError(error)) {
|
|
187
|
+
await detectLegacyArtifacts();
|
|
188
|
+
return { profiles: [] };
|
|
189
|
+
}
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
const parsed = parseJsonResult(raw, ProfilesFile, { source: path });
|
|
193
|
+
if (parsed.ok) return parsed.value;
|
|
194
|
+
if (parsed.error instanceof ValidationError) {
|
|
195
|
+
legacyWarningPending = true;
|
|
196
|
+
return { profiles: [] };
|
|
197
|
+
}
|
|
198
|
+
throw parsed.error;
|
|
199
|
+
}
|
|
200
|
+
async function detectLegacyArtifacts() {
|
|
201
|
+
const legacyCredentials = join(configDir(), LEGACY_CREDENTIALS_FILE);
|
|
202
|
+
const legacyRejections = join(configDir(), LEGACY_REJECTIONS_FILE);
|
|
203
|
+
const [credentialsExists, rejectionsExists] = await Promise.all([fileExists(legacyCredentials), fileExists(legacyRejections)]);
|
|
204
|
+
if (credentialsExists || rejectionsExists) legacyWarningPending = true;
|
|
205
|
+
}
|
|
206
|
+
async function fileExists(path) {
|
|
207
|
+
try {
|
|
208
|
+
await promises.access(path);
|
|
209
|
+
return true;
|
|
210
|
+
} catch {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
async function writeProfilesFile(file) {
|
|
215
|
+
const path = profilesFilePath();
|
|
216
|
+
if (file.profiles.length === 0) {
|
|
217
|
+
await promises.unlink(path).catch(() => void 0);
|
|
218
|
+
await cleanupLegacyFiles();
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
await promises.mkdir(dirname(path), {
|
|
222
|
+
recursive: true,
|
|
223
|
+
mode: PROFILES_DIR_MODE
|
|
224
|
+
});
|
|
225
|
+
const tmpPath = `${path}.${process.pid}.tmp`;
|
|
226
|
+
await promises.writeFile(tmpPath, JSON.stringify(file, null, 2) + "\n", { mode: PROFILES_FILE_MODE });
|
|
227
|
+
if (process.platform !== "win32") await promises.chmod(tmpPath, PROFILES_FILE_MODE);
|
|
228
|
+
try {
|
|
229
|
+
await promises.rename(tmpPath, path);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
await promises.unlink(tmpPath).catch(() => void 0);
|
|
232
|
+
throw error;
|
|
233
|
+
}
|
|
234
|
+
await cleanupLegacyFiles();
|
|
235
|
+
}
|
|
236
|
+
async function cleanupLegacyFiles() {
|
|
237
|
+
await Promise.all([promises.unlink(join(configDir(), LEGACY_CREDENTIALS_FILE)).catch(() => void 0), promises.unlink(join(configDir(), LEGACY_REJECTIONS_FILE)).catch(() => void 0)]);
|
|
238
|
+
}
|
|
239
|
+
function findRecord(file, name) {
|
|
240
|
+
return file.profiles.find((entry) => entry.name === name) ?? null;
|
|
241
|
+
}
|
|
242
|
+
function sideIsKeyringBacked(record, side) {
|
|
243
|
+
return side === "oauth" ? record.oauth !== null && record.oauth.accessToken === null : record.oauth === null && record.apiKey === null;
|
|
244
|
+
}
|
|
245
|
+
function flagResidualIfUnconfirmed(existing, cleared, removals) {
|
|
246
|
+
if (existing === null || !removals.includes("failed")) return;
|
|
247
|
+
if (sideIsKeyringBacked(existing, cleared)) keychainResidualPending = true;
|
|
248
|
+
}
|
|
249
|
+
function fileLocation(key) {
|
|
250
|
+
return {
|
|
251
|
+
backend: "file",
|
|
252
|
+
path: profilesFilePath(),
|
|
253
|
+
account: key,
|
|
254
|
+
reason: keyringEnabled() ? "unavailable" : "disabled"
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
function keyringFallbackWarning(location) {
|
|
258
|
+
return `warning: ${location.reason === "disabled" ? `OS keychain disabled via ${ENV_DISABLE_KEYRING}` : "OS keychain unavailable"}; credentials stored as plaintext at ${location.path}`;
|
|
259
|
+
}
|
|
260
|
+
function persistSecret(key, value) {
|
|
261
|
+
if (trySetKeyring(key, value)) return {
|
|
262
|
+
backend: "keyring",
|
|
263
|
+
service: KEYRING_SERVICE,
|
|
264
|
+
account: key
|
|
265
|
+
};
|
|
266
|
+
removeKeyringEntry(key);
|
|
267
|
+
return fileLocation(key);
|
|
268
|
+
}
|
|
269
|
+
function resolveSecret(key, inline) {
|
|
270
|
+
if (inline !== null) return inline;
|
|
271
|
+
return tryReadKeyring(key) ?? null;
|
|
272
|
+
}
|
|
273
|
+
async function readProfileCredential(name = DEFAULT_PROFILE) {
|
|
274
|
+
const record = findRecord(await readProfilesFile(), name);
|
|
275
|
+
if (record === null) return null;
|
|
276
|
+
return resolveRecordCredential(record);
|
|
277
|
+
}
|
|
278
|
+
function resolveRecordCredential(record) {
|
|
279
|
+
if (record.oauth !== null) {
|
|
280
|
+
const accessToken = resolveSecret(account.profileOAuthAccess(record.name), record.oauth.accessToken);
|
|
281
|
+
const refreshToken = resolveSecret(account.profileOAuthRefresh(record.name), record.oauth.refreshToken);
|
|
282
|
+
if (accessToken === null || refreshToken === null) return null;
|
|
283
|
+
return {
|
|
284
|
+
url: record.url,
|
|
285
|
+
credential: {
|
|
286
|
+
kind: "oauth",
|
|
287
|
+
accessToken,
|
|
288
|
+
refreshToken,
|
|
289
|
+
expiresAt: record.oauth.expiresAt,
|
|
290
|
+
clientId: record.oauth.clientId
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
const apiKey = resolveSecret(account.profileApiKey(record.name), record.apiKey);
|
|
295
|
+
if (apiKey === null) return null;
|
|
296
|
+
return {
|
|
297
|
+
url: record.url,
|
|
298
|
+
credential: {
|
|
299
|
+
kind: "apiKey",
|
|
300
|
+
apiKey
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
async function readProfileRecord(name = DEFAULT_PROFILE) {
|
|
305
|
+
return findRecord(await readProfilesFile(), name);
|
|
306
|
+
}
|
|
307
|
+
async function listProfileRecords() {
|
|
308
|
+
return (await readProfilesFile()).profiles;
|
|
309
|
+
}
|
|
310
|
+
async function upsertRecord(file, name, updated) {
|
|
311
|
+
const profiles = findRecord(file, name) !== null ? file.profiles.map((entry) => entry.name === name ? updated : entry) : [...file.profiles, updated];
|
|
312
|
+
await writeProfilesFile({
|
|
313
|
+
...file,
|
|
314
|
+
profiles
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
async function writeProfile(profile, name = DEFAULT_PROFILE) {
|
|
318
|
+
const location = persistSecret(account.profileApiKey(name), profile.apiKey);
|
|
319
|
+
const inlineApiKey = location.backend === "file" ? profile.apiKey : null;
|
|
320
|
+
const file = await readProfilesFile();
|
|
321
|
+
const existing = findRecord(file, name);
|
|
322
|
+
flagResidualIfUnconfirmed(existing, "oauth", [removeKeyringEntry(account.profileOAuthAccess(name)), removeKeyringEntry(account.profileOAuthRefresh(name))]);
|
|
323
|
+
await upsertRecord(file, name, existing === null ? {
|
|
324
|
+
name,
|
|
325
|
+
url: profile.url,
|
|
326
|
+
apiKey: inlineApiKey,
|
|
327
|
+
oauth: null,
|
|
328
|
+
lastProbe: null,
|
|
329
|
+
lastFailure: null
|
|
330
|
+
} : {
|
|
331
|
+
...existing,
|
|
332
|
+
url: profile.url,
|
|
333
|
+
apiKey: inlineApiKey,
|
|
334
|
+
oauth: null
|
|
335
|
+
});
|
|
336
|
+
return location;
|
|
337
|
+
}
|
|
338
|
+
async function writeOAuthProfile(url, credential, name = DEFAULT_PROFILE) {
|
|
339
|
+
const accessKey = account.profileOAuthAccess(name);
|
|
340
|
+
const refreshKey = account.profileOAuthRefresh(name);
|
|
341
|
+
const accessLocation = persistSecret(accessKey, credential.accessToken);
|
|
342
|
+
const refreshLocation = persistSecret(refreshKey, credential.refreshToken);
|
|
343
|
+
const onFile = accessLocation.backend === "file" || refreshLocation.backend === "file";
|
|
344
|
+
const oauth = {
|
|
345
|
+
accessToken: onFile ? credential.accessToken : null,
|
|
346
|
+
refreshToken: onFile ? credential.refreshToken : null,
|
|
347
|
+
expiresAt: credential.expiresAt,
|
|
348
|
+
clientId: credential.clientId
|
|
349
|
+
};
|
|
350
|
+
const file = await readProfilesFile();
|
|
351
|
+
const existing = findRecord(file, name);
|
|
352
|
+
if (onFile && existing !== null && sideIsKeyringBacked(existing, "oauth")) keyringDowngradeNotice = keyringFallbackWarning(fileLocation(accessKey));
|
|
353
|
+
flagResidualIfUnconfirmed(existing, "apiKey", [removeKeyringEntry(account.profileApiKey(name))]);
|
|
354
|
+
await upsertRecord(file, name, existing === null ? {
|
|
355
|
+
name,
|
|
356
|
+
url,
|
|
357
|
+
apiKey: null,
|
|
358
|
+
oauth,
|
|
359
|
+
lastProbe: null,
|
|
360
|
+
lastFailure: null
|
|
361
|
+
} : {
|
|
362
|
+
...existing,
|
|
363
|
+
url,
|
|
364
|
+
apiKey: null,
|
|
365
|
+
oauth
|
|
366
|
+
});
|
|
367
|
+
return onFile ? fileLocation(accessKey) : {
|
|
368
|
+
backend: "keyring",
|
|
369
|
+
service: KEYRING_SERVICE,
|
|
370
|
+
account: accessKey
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
async function writeProbeResult(name, input) {
|
|
374
|
+
const probe = ProfileLastProbe.parse({
|
|
375
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
376
|
+
version: input.server.version,
|
|
377
|
+
tokenFeatures: input.server.tokenFeatures,
|
|
378
|
+
user: input.user
|
|
379
|
+
});
|
|
380
|
+
const file = await readProfilesFile();
|
|
381
|
+
if (findRecord(file, name) === null) return null;
|
|
382
|
+
const profiles = file.profiles.map((entry) => entry.name === name ? {
|
|
383
|
+
...entry,
|
|
384
|
+
lastProbe: probe,
|
|
385
|
+
lastFailure: null
|
|
386
|
+
} : entry);
|
|
387
|
+
await writeProfilesFile({
|
|
388
|
+
...file,
|
|
389
|
+
profiles
|
|
390
|
+
});
|
|
391
|
+
return probe;
|
|
392
|
+
}
|
|
393
|
+
async function writeProbeFailure(name, input) {
|
|
394
|
+
const failure = ProfileLastFailure.parse({
|
|
395
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
396
|
+
kind: input.kind,
|
|
397
|
+
reason: input.reason
|
|
398
|
+
});
|
|
399
|
+
const file = await readProfilesFile();
|
|
400
|
+
if (findRecord(file, name) === null) return null;
|
|
401
|
+
const profiles = file.profiles.map((entry) => entry.name === name ? {
|
|
402
|
+
...entry,
|
|
403
|
+
lastFailure: failure
|
|
404
|
+
} : entry);
|
|
405
|
+
await writeProfilesFile({
|
|
406
|
+
...file,
|
|
407
|
+
profiles
|
|
408
|
+
});
|
|
409
|
+
return failure;
|
|
410
|
+
}
|
|
411
|
+
async function clearProfile(name = DEFAULT_PROFILE) {
|
|
412
|
+
const file = await readProfilesFile();
|
|
413
|
+
const existing = findRecord(file, name);
|
|
414
|
+
const removals = [
|
|
415
|
+
removeKeyringEntry(account.profileApiKey(name)),
|
|
416
|
+
removeKeyringEntry(account.profileOAuthAccess(name)),
|
|
417
|
+
removeKeyringEntry(account.profileOAuthRefresh(name))
|
|
418
|
+
];
|
|
419
|
+
if (existing !== null) flagResidualIfUnconfirmed(existing, existing.oauth !== null ? "oauth" : "apiKey", removals);
|
|
420
|
+
if (existing === null) return false;
|
|
421
|
+
await writeProfilesFile({
|
|
422
|
+
...file,
|
|
423
|
+
profiles: file.profiles.filter((entry) => entry.name !== name)
|
|
424
|
+
});
|
|
425
|
+
return true;
|
|
426
|
+
}
|
|
427
|
+
//#endregion
|
|
428
|
+
//#region ../client/src/http/oauth.ts
|
|
429
|
+
const DISCOVERY_PATH = "/.well-known/oauth-authorization-server";
|
|
430
|
+
const FORM_CONTENT_TYPE = "application/x-www-form-urlencoded";
|
|
431
|
+
const OAUTH_TIMEOUT_MS = 3e4;
|
|
432
|
+
async function oauthFetch(request) {
|
|
433
|
+
const timeoutSignal = AbortSignal.timeout(OAUTH_TIMEOUT_MS);
|
|
434
|
+
try {
|
|
435
|
+
return await fetch(request.url, {
|
|
436
|
+
method: request.method,
|
|
437
|
+
headers: {
|
|
438
|
+
...request.headers,
|
|
439
|
+
"user-agent": request.userAgent
|
|
440
|
+
},
|
|
441
|
+
...request.body !== void 0 ? { body: request.body } : {},
|
|
442
|
+
signal: timeoutSignal
|
|
443
|
+
});
|
|
444
|
+
} catch (error) {
|
|
445
|
+
if (timeoutSignal.aborted) throw new TimeoutError(`OAuth request timed out after ${OAUTH_TIMEOUT_MS}ms`, {
|
|
446
|
+
kind: "http",
|
|
447
|
+
method: request.method,
|
|
448
|
+
url: request.url,
|
|
449
|
+
timeoutMs: OAUTH_TIMEOUT_MS
|
|
450
|
+
});
|
|
451
|
+
throw buildNetworkError(error, request.method, request.url);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
const OAUTH_SCOPE = "mb:full";
|
|
455
|
+
const OAuthServerMetadata = z.object({
|
|
456
|
+
issuer: z.string(),
|
|
457
|
+
authorization_endpoint: z.string(),
|
|
458
|
+
token_endpoint: z.string(),
|
|
459
|
+
registration_endpoint: z.string().optional(),
|
|
460
|
+
revocation_endpoint: z.string().optional(),
|
|
461
|
+
scopes_supported: z.array(z.string()).optional()
|
|
462
|
+
}).loose();
|
|
463
|
+
const RegisteredClient = z.object({ client_id: z.string() }).loose();
|
|
464
|
+
const OAuthTokens = z.object({
|
|
465
|
+
access_token: z.string(),
|
|
466
|
+
token_type: z.string(),
|
|
467
|
+
expires_in: z.number().int().optional(),
|
|
468
|
+
refresh_token: z.string().optional()
|
|
469
|
+
}).loose();
|
|
470
|
+
const OAuthErrorBody = z.object({
|
|
471
|
+
error: z.string(),
|
|
472
|
+
error_description: z.string().optional()
|
|
473
|
+
}).loose();
|
|
474
|
+
async function readJson(response, schema, source) {
|
|
475
|
+
let raw;
|
|
476
|
+
try {
|
|
477
|
+
raw = await response.json();
|
|
478
|
+
} catch (error) {
|
|
479
|
+
throw new ConfigError(`${source}: invalid JSON response (${errorMessage(error)})`);
|
|
480
|
+
}
|
|
481
|
+
const parsed = schema.safeParse(raw);
|
|
482
|
+
if (!parsed.success) throw new ConfigError(`${source}: unexpected response shape`);
|
|
483
|
+
return parsed.data;
|
|
484
|
+
}
|
|
485
|
+
async function failure(response, source, method, url) {
|
|
486
|
+
const rawBody = await response.text().catch(() => "");
|
|
487
|
+
if (isRetryableStatus(response.status)) return new HttpError({
|
|
488
|
+
status: response.status,
|
|
489
|
+
statusText: response.statusText,
|
|
490
|
+
method,
|
|
491
|
+
url,
|
|
492
|
+
responseHeaders: response.headers,
|
|
493
|
+
rawBody
|
|
494
|
+
});
|
|
495
|
+
const parsed = parseJsonResult(rawBody, OAuthErrorBody);
|
|
496
|
+
if (parsed.ok) {
|
|
497
|
+
const detail = parsed.value.error_description ?? parsed.value.error;
|
|
498
|
+
return new ConfigError(`${source} failed (${response.status}): ${detail}`);
|
|
499
|
+
}
|
|
500
|
+
return new ConfigError(`${source} failed (${response.status})`);
|
|
501
|
+
}
|
|
502
|
+
async function postForm(url, userAgent, params, schema, source) {
|
|
503
|
+
const response = await oauthFetch({
|
|
504
|
+
url,
|
|
505
|
+
method: "POST",
|
|
506
|
+
userAgent,
|
|
507
|
+
headers: {
|
|
508
|
+
"content-type": FORM_CONTENT_TYPE,
|
|
509
|
+
accept: JSON_CONTENT_TYPE
|
|
510
|
+
},
|
|
511
|
+
body: new URLSearchParams(params)
|
|
512
|
+
});
|
|
513
|
+
if (!response.ok) throw await failure(response, source, "POST", url);
|
|
514
|
+
return readJson(response, schema, source);
|
|
515
|
+
}
|
|
516
|
+
const OAUTH_UNSUPPORTED_MESSAGE = "this Metabase does not support OAuth login (requires Metabase v63 or newer)";
|
|
517
|
+
async function tryDiscoverMetadata(baseUrl, userAgent) {
|
|
518
|
+
const response = await oauthFetch({
|
|
519
|
+
url: `${baseUrl}${DISCOVERY_PATH}`,
|
|
520
|
+
method: "GET",
|
|
521
|
+
userAgent,
|
|
522
|
+
headers: { accept: JSON_CONTENT_TYPE }
|
|
523
|
+
});
|
|
524
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
525
|
+
if (!response.ok || !contentType.includes("json")) {
|
|
526
|
+
await response.body?.cancel().catch(() => void 0);
|
|
527
|
+
return null;
|
|
528
|
+
}
|
|
529
|
+
const metadata = await readJson(response, OAuthServerMetadata, "OAuth discovery");
|
|
530
|
+
const scopes = metadata.scopes_supported;
|
|
531
|
+
if (scopes !== void 0 && !scopes.includes("mb:full")) return null;
|
|
532
|
+
assertEndpointOrigin(metadata.issuer, baseUrl, "issuer");
|
|
533
|
+
assertEndpointOrigin(metadata.authorization_endpoint, baseUrl, "authorization endpoint");
|
|
534
|
+
assertEndpointOrigin(metadata.token_endpoint, baseUrl, "token endpoint");
|
|
535
|
+
if (metadata.registration_endpoint !== void 0) assertEndpointOrigin(metadata.registration_endpoint, baseUrl, "registration endpoint");
|
|
536
|
+
if (metadata.revocation_endpoint !== void 0) assertEndpointOrigin(metadata.revocation_endpoint, baseUrl, "revocation endpoint");
|
|
537
|
+
return metadata;
|
|
538
|
+
}
|
|
539
|
+
async function discoverMetadata(baseUrl, userAgent) {
|
|
540
|
+
const metadata = await tryDiscoverMetadata(baseUrl, userAgent);
|
|
541
|
+
if (metadata === null) throw new ConfigError(OAUTH_UNSUPPORTED_MESSAGE);
|
|
542
|
+
return metadata;
|
|
543
|
+
}
|
|
544
|
+
async function registerClient(input) {
|
|
545
|
+
const response = await oauthFetch({
|
|
546
|
+
url: input.registrationEndpoint,
|
|
547
|
+
method: "POST",
|
|
548
|
+
userAgent: input.userAgent,
|
|
549
|
+
headers: {
|
|
550
|
+
"content-type": JSON_CONTENT_TYPE,
|
|
551
|
+
accept: JSON_CONTENT_TYPE
|
|
552
|
+
},
|
|
553
|
+
body: JSON.stringify({
|
|
554
|
+
application_type: "native",
|
|
555
|
+
client_name: input.clientName,
|
|
556
|
+
redirect_uris: [input.redirectUri],
|
|
557
|
+
grant_types: ["authorization_code", "refresh_token"],
|
|
558
|
+
response_types: ["code"],
|
|
559
|
+
token_endpoint_auth_method: "none",
|
|
560
|
+
scope: OAUTH_SCOPE
|
|
561
|
+
})
|
|
562
|
+
});
|
|
563
|
+
if (!response.ok) throw await failure(response, "OAuth client registration", "POST", input.registrationEndpoint);
|
|
564
|
+
return readJson(response, RegisteredClient, "OAuth client registration");
|
|
565
|
+
}
|
|
566
|
+
function exchangeCode(input) {
|
|
567
|
+
return postForm(input.tokenEndpoint, input.userAgent, {
|
|
568
|
+
grant_type: "authorization_code",
|
|
569
|
+
code: input.code,
|
|
570
|
+
redirect_uri: input.redirectUri,
|
|
571
|
+
client_id: input.clientId,
|
|
572
|
+
code_verifier: input.codeVerifier
|
|
573
|
+
}, OAuthTokens, "OAuth token exchange");
|
|
574
|
+
}
|
|
575
|
+
function refreshTokens(input) {
|
|
576
|
+
return postForm(input.tokenEndpoint, input.userAgent, {
|
|
577
|
+
grant_type: "refresh_token",
|
|
578
|
+
refresh_token: input.refreshToken,
|
|
579
|
+
client_id: input.clientId
|
|
580
|
+
}, OAuthTokens, "OAuth token refresh");
|
|
581
|
+
}
|
|
582
|
+
async function revokeToken(input) {
|
|
583
|
+
const response = await oauthFetch({
|
|
584
|
+
url: input.revocationEndpoint,
|
|
585
|
+
method: "POST",
|
|
586
|
+
userAgent: input.userAgent,
|
|
587
|
+
headers: { "content-type": FORM_CONTENT_TYPE },
|
|
588
|
+
body: new URLSearchParams({
|
|
589
|
+
token: input.token,
|
|
590
|
+
client_id: input.clientId
|
|
591
|
+
})
|
|
592
|
+
});
|
|
593
|
+
if (!response.ok) throw await failure(response, "OAuth token revocation", "POST", input.revocationEndpoint);
|
|
594
|
+
await response.body?.cancel().catch(() => void 0);
|
|
595
|
+
}
|
|
596
|
+
//#endregion
|
|
597
|
+
//#region ../client/src/auth/oauth-session.ts
|
|
598
|
+
async function refreshOAuthCredential(baseUrl, credential, nowMs, userAgent) {
|
|
599
|
+
const tokens = await refreshTokens({
|
|
600
|
+
tokenEndpoint: (await discoverMetadata(baseUrl, userAgent)).token_endpoint,
|
|
601
|
+
refreshToken: credential.refreshToken,
|
|
602
|
+
clientId: credential.clientId,
|
|
603
|
+
userAgent
|
|
604
|
+
});
|
|
605
|
+
return oauthCredentialFromTokens(tokens, tokens.refresh_token ?? credential.refreshToken, credential.clientId, nowMs);
|
|
606
|
+
}
|
|
607
|
+
async function revokeOAuthCredential(baseUrl, credential, userAgent) {
|
|
608
|
+
const revocationEndpoint = (await discoverMetadata(baseUrl, userAgent)).revocation_endpoint;
|
|
609
|
+
if (revocationEndpoint === void 0) return false;
|
|
610
|
+
await revokeToken({
|
|
611
|
+
revocationEndpoint,
|
|
612
|
+
token: credential.refreshToken,
|
|
613
|
+
clientId: credential.clientId,
|
|
614
|
+
userAgent
|
|
615
|
+
});
|
|
616
|
+
await revokeToken({
|
|
617
|
+
revocationEndpoint,
|
|
618
|
+
token: credential.accessToken,
|
|
619
|
+
clientId: credential.clientId,
|
|
620
|
+
userAgent
|
|
621
|
+
});
|
|
622
|
+
return true;
|
|
623
|
+
}
|
|
624
|
+
//#endregion
|
|
625
|
+
//#region src/core/user-agent.ts
|
|
626
|
+
const USER_AGENT = `metabase-cli/${version}`;
|
|
627
|
+
const OAUTH_CLIENT_NAME = "Metabase CLI";
|
|
628
|
+
//#endregion
|
|
629
|
+
//#region src/core/config.ts
|
|
630
|
+
function isPreflightSkipped() {
|
|
631
|
+
return readEnv(ENV_SKIP_PREFLIGHT) === "1";
|
|
632
|
+
}
|
|
633
|
+
function resolveProfileName(profileFlag) {
|
|
634
|
+
return explicitProfileName(profileFlag) ?? "default";
|
|
635
|
+
}
|
|
636
|
+
function explicitProfileName(profileFlag) {
|
|
637
|
+
return profileFlag || readEnv("MB_PROFILE") || null;
|
|
638
|
+
}
|
|
639
|
+
function readEnvCredentials() {
|
|
640
|
+
return {
|
|
641
|
+
url: readEnv("MB_URL") ?? null,
|
|
642
|
+
apiKey: readEnv("MB_API_KEY") ?? null
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
async function resolveConfig(flags) {
|
|
646
|
+
const profile = resolveProfileName(flags.profile);
|
|
647
|
+
const env = readEnvCredentials();
|
|
648
|
+
const hasUrl = Boolean(flags.url ?? env.url);
|
|
649
|
+
const hasKey = Boolean(flags.apiKey ?? env.apiKey);
|
|
650
|
+
const stored = hasUrl && hasKey ? null : await readProfileCredential(profile);
|
|
651
|
+
const url = resolveUrl(flags.url, env.url, stored?.url);
|
|
652
|
+
const credential = resolveCredential(flags.apiKey, env.apiKey, stored?.credential);
|
|
653
|
+
if (url === null || credential === null) throw new ConfigError(`Not authenticated for profile "${profile}". Run \`mb auth login\`, set ${ENV_URL}/${ENV_API_KEY}, or pass --url/--api-key.${await failureHintForProfile(profile)}`);
|
|
654
|
+
const normalizedUrl = normalizeUrl(url.value);
|
|
655
|
+
assertOAuthUrlMatchesIssuer(credential.credential, stored?.url, normalizedUrl, profile);
|
|
656
|
+
return {
|
|
657
|
+
url: normalizedUrl,
|
|
658
|
+
credential: await ensureFreshCredential(profile, credential.credential, stored?.url),
|
|
659
|
+
profile,
|
|
660
|
+
source: url.source === credential.source ? url.source : "mixed"
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
function assertOAuthUrlMatchesIssuer(credential, issuerUrl, requestUrl, profile) {
|
|
664
|
+
if (credential.kind !== "oauth" || issuerUrl === void 0) return;
|
|
665
|
+
const issuer = normalizeUrl(issuerUrl);
|
|
666
|
+
if (issuer === requestUrl) return;
|
|
667
|
+
throw new ConfigError(`profile "${profile}" is a browser-login (OAuth) profile bound to ${issuer}, but the request URL is ${requestUrl}. Drop --url/${ENV_URL} to use the profile's own URL, or run \`mb auth login --url ${requestUrl}\` to authenticate there.`);
|
|
668
|
+
}
|
|
669
|
+
async function ensureFreshCredential(profile, credential, issuerUrl) {
|
|
670
|
+
if (credential.kind !== "oauth" || issuerUrl === void 0) return credential;
|
|
671
|
+
if (!isOAuthExpired(credential, Date.now())) return credential;
|
|
672
|
+
let refreshed;
|
|
673
|
+
try {
|
|
674
|
+
refreshed = await refreshOAuthCredential(issuerUrl, credential, Date.now(), USER_AGENT);
|
|
675
|
+
} catch {
|
|
676
|
+
return credential;
|
|
677
|
+
}
|
|
678
|
+
await persistRefreshed(profile, issuerUrl, refreshed);
|
|
679
|
+
return refreshed;
|
|
680
|
+
}
|
|
681
|
+
async function refreshAndPersist(profile, issuerUrl, credential) {
|
|
682
|
+
const refreshed = await refreshOAuthCredential(issuerUrl, credential, Date.now(), USER_AGENT);
|
|
683
|
+
await persistRefreshed(profile, issuerUrl, refreshed);
|
|
684
|
+
return refreshed;
|
|
685
|
+
}
|
|
686
|
+
async function persistRefreshed(profile, issuerUrl, refreshed) {
|
|
687
|
+
await writeOAuthProfile(issuerUrl, refreshed, profile);
|
|
688
|
+
}
|
|
689
|
+
function createCredentialRefresher(profile) {
|
|
690
|
+
return async () => {
|
|
691
|
+
const stored = await readProfileCredential(profile);
|
|
692
|
+
if (stored === null || stored.credential.kind !== "oauth") return null;
|
|
693
|
+
try {
|
|
694
|
+
return await refreshAndPersist(profile, stored.url, stored.credential);
|
|
695
|
+
} catch (error) {
|
|
696
|
+
if (error instanceof ConfigError) throw new ConfigError(`${error.message} — run \`mb auth login --profile ${profile}\` to log in again`);
|
|
697
|
+
throw error;
|
|
698
|
+
}
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
async function failureHintForProfile(profile) {
|
|
702
|
+
const record = await readProfileRecord(profile);
|
|
703
|
+
if (record === null || record.lastFailure === null) return "";
|
|
704
|
+
if (record.lastProbe !== null && record.lastProbe.at >= record.lastFailure.at) return "";
|
|
705
|
+
return ` profile "${profile}" last verify failed: ${record.lastFailure.reason}. Run \`mb auth login --profile ${profile}\` to update the token.`;
|
|
706
|
+
}
|
|
707
|
+
function resolveUrl(flag, env, stored) {
|
|
708
|
+
if (flag) return {
|
|
709
|
+
value: flag,
|
|
710
|
+
source: "flag"
|
|
711
|
+
};
|
|
712
|
+
if (env) return {
|
|
713
|
+
value: env,
|
|
714
|
+
source: "env"
|
|
715
|
+
};
|
|
716
|
+
if (stored) return {
|
|
717
|
+
value: stored,
|
|
718
|
+
source: "stored"
|
|
719
|
+
};
|
|
720
|
+
return null;
|
|
721
|
+
}
|
|
722
|
+
function resolveCredential(flagKey, envKey, stored) {
|
|
723
|
+
if (flagKey) return {
|
|
724
|
+
credential: {
|
|
725
|
+
kind: "apiKey",
|
|
726
|
+
apiKey: flagKey
|
|
727
|
+
},
|
|
728
|
+
source: "flag"
|
|
729
|
+
};
|
|
730
|
+
if (envKey) return {
|
|
731
|
+
credential: {
|
|
732
|
+
kind: "apiKey",
|
|
733
|
+
apiKey: envKey
|
|
734
|
+
},
|
|
735
|
+
source: "env"
|
|
736
|
+
};
|
|
737
|
+
if (stored) return {
|
|
738
|
+
credential: stored,
|
|
739
|
+
source: "stored"
|
|
740
|
+
};
|
|
741
|
+
return null;
|
|
742
|
+
}
|
|
743
|
+
//#endregion
|
|
744
|
+
//#region src/output/format.ts
|
|
745
|
+
function resolveFormat({ json, format, isTty }) {
|
|
746
|
+
const explicit = format !== void 0 && format !== "auto" ? format : null;
|
|
747
|
+
if (explicit !== null && explicit !== "json" && explicit !== "text") throw new ConfigError(`invalid --format value: "${explicit}" (expected: auto, json, text)`);
|
|
748
|
+
if (json && explicit !== null && explicit !== "json") throw new ConfigError(`--json conflicts with --format ${explicit}`);
|
|
749
|
+
if (json) return "json";
|
|
750
|
+
if (explicit !== null) return explicit;
|
|
751
|
+
return isTty ? "text" : "json";
|
|
752
|
+
}
|
|
753
|
+
//#endregion
|
|
754
|
+
//#region src/runtime/csv.ts
|
|
755
|
+
function parseCsv(raw) {
|
|
756
|
+
return raw.split(",").map((part) => part.trim()).filter((part) => part.length > 0);
|
|
757
|
+
}
|
|
758
|
+
function parseEnumCsv(raw, schema, flagName) {
|
|
759
|
+
if (raw === void 0 || raw === "") return;
|
|
760
|
+
const parts = parseCsv(raw);
|
|
761
|
+
if (parts.length === 0) return;
|
|
762
|
+
const accepted = [];
|
|
763
|
+
const rejected = [];
|
|
764
|
+
for (const part of parts) {
|
|
765
|
+
const result = schema.safeParse(part);
|
|
766
|
+
if (result.success) accepted.push(result.data);
|
|
767
|
+
else rejected.push(part);
|
|
768
|
+
}
|
|
769
|
+
if (rejected.length > 0) {
|
|
770
|
+
const allowed = Object.values(schema.enum).join(", ");
|
|
771
|
+
throw new ConfigError(`invalid ${flagName} value: ${rejected.join(", ")} (expected one of: ${allowed})`);
|
|
772
|
+
}
|
|
773
|
+
return accepted;
|
|
774
|
+
}
|
|
775
|
+
function parseEnum(raw, schema, flagName) {
|
|
776
|
+
if (raw === void 0 || raw === "") return;
|
|
777
|
+
const result = schema.safeParse(raw);
|
|
778
|
+
if (!result.success) throw new ConfigError(`invalid ${flagName} value: "${raw}" (expected one of: ${Object.values(schema.enum).join(", ")})`);
|
|
779
|
+
return result.data;
|
|
780
|
+
}
|
|
781
|
+
//#endregion
|
|
782
|
+
//#region src/commands/context.ts
|
|
783
|
+
function resolveOutputFormat(args, options = {}) {
|
|
784
|
+
return resolveFormat({
|
|
785
|
+
json: args.json,
|
|
786
|
+
format: args.format,
|
|
787
|
+
isTty: options.isTty ?? Boolean(process.stdout.isTTY)
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
function resolveCommonFlags(args, options = {}) {
|
|
791
|
+
const fields = parseFields(args.fields);
|
|
792
|
+
const full = args.full === true;
|
|
793
|
+
if (full && fields !== void 0) throw new ConfigError("--full conflicts with --fields (use one or neither)");
|
|
794
|
+
return {
|
|
795
|
+
format: resolveOutputFormat(args, options),
|
|
796
|
+
full,
|
|
797
|
+
fields,
|
|
798
|
+
maxBytes: parseMaxBytes(args.maxBytes),
|
|
799
|
+
range: parseRange(args.limit, args.offset),
|
|
800
|
+
url: args.url,
|
|
801
|
+
apiKey: args.apiKey,
|
|
802
|
+
profile: args.profile,
|
|
803
|
+
skipPreflight: args.skipPreflight === true
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
function parseFields(value) {
|
|
807
|
+
if (value === void 0 || value === "") return;
|
|
808
|
+
const parts = parseCsv(value);
|
|
809
|
+
return parts.length > 0 ? parts : void 0;
|
|
810
|
+
}
|
|
811
|
+
function parseMaxBytes(value) {
|
|
812
|
+
return parseInteger(value ?? String(24576), {
|
|
813
|
+
name: "--max-bytes",
|
|
814
|
+
min: 0
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
function parseRange(limit, offset) {
|
|
818
|
+
const parsedLimit = parseOptionalInteger(limit, {
|
|
819
|
+
name: "--limit",
|
|
820
|
+
min: 1
|
|
821
|
+
});
|
|
822
|
+
return {
|
|
823
|
+
offset: parseInteger(offset ?? "0", {
|
|
824
|
+
name: "--offset",
|
|
825
|
+
min: 0
|
|
826
|
+
}),
|
|
827
|
+
limit: parsedLimit === null ? void 0 : parsedLimit
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
//#endregion
|
|
831
|
+
//#region src/commands/known-flags.ts
|
|
832
|
+
const ARGUMENT_SEPARATOR = "--";
|
|
833
|
+
const NEGATION_PREFIX = "no-";
|
|
834
|
+
const BUILTIN_FLAGS = [
|
|
835
|
+
"help",
|
|
836
|
+
"h",
|
|
837
|
+
"version",
|
|
838
|
+
"v"
|
|
839
|
+
];
|
|
840
|
+
function assertKnownFlags(rawArgs, argsDef) {
|
|
841
|
+
const allowed = allowedFlagKeys(argsDef);
|
|
842
|
+
let index = 0;
|
|
843
|
+
while (index < rawArgs.length) {
|
|
844
|
+
const token = rawArgs[index];
|
|
845
|
+
if (token === void 0 || token === ARGUMENT_SEPARATOR) return;
|
|
846
|
+
if (!isFlagToken(token)) {
|
|
847
|
+
index += 1;
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
850
|
+
if (!flagCandidates(token).some((candidate) => allowed.has(candidate))) throw new ConfigError(`unknown flag: ${displayFlag(token)}`);
|
|
851
|
+
index += flagConsumesValue(token, argsDef) ? 2 : 1;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
function allowedFlagKeys(argsDef) {
|
|
855
|
+
const keys = new Set(BUILTIN_FLAGS.map(normalizeFlag));
|
|
856
|
+
for (const [name, def] of Object.entries(argsDef)) {
|
|
857
|
+
keys.add(normalizeFlag(name));
|
|
858
|
+
if ("alias" in def) for (const alias of toAliasArray(def.alias)) keys.add(normalizeFlag(alias));
|
|
859
|
+
}
|
|
860
|
+
return keys;
|
|
861
|
+
}
|
|
862
|
+
function isFlagToken(token) {
|
|
863
|
+
return token.startsWith("-") && token !== "-";
|
|
864
|
+
}
|
|
865
|
+
function displayFlag(token) {
|
|
866
|
+
const equals = token.indexOf("=");
|
|
867
|
+
return equals === -1 ? token : token.slice(0, equals);
|
|
868
|
+
}
|
|
869
|
+
function flagCandidates(token) {
|
|
870
|
+
const name = displayFlag(token).replace(/^-+/, "");
|
|
871
|
+
const candidates = [normalizeFlag(name)];
|
|
872
|
+
if (name.startsWith(NEGATION_PREFIX)) candidates.push(normalizeFlag(name.slice(3)));
|
|
873
|
+
return candidates;
|
|
874
|
+
}
|
|
875
|
+
//#endregion
|
|
876
|
+
//#region src/commands/runtime.ts
|
|
877
|
+
function defineMetabaseCommand(def) {
|
|
878
|
+
const required = def.capabilities === null ? null : mergeCapabilities(def.capabilities);
|
|
879
|
+
const cmd = defineCommand({
|
|
880
|
+
meta: def.meta,
|
|
881
|
+
args: def.args,
|
|
882
|
+
async run({ args, rawArgs }) {
|
|
883
|
+
const commonArgs = pickCommonArgs(args);
|
|
884
|
+
let reportFormat;
|
|
885
|
+
try {
|
|
886
|
+
reportFormat = resolveOutputFormat(commonArgs);
|
|
887
|
+
const ctx = resolveCommonFlags(commonArgs);
|
|
888
|
+
assertKnownFlags(rawArgs, def.args);
|
|
889
|
+
let cachedConfig = null;
|
|
890
|
+
let cachedClient = null;
|
|
891
|
+
let cachedServerInfo = null;
|
|
892
|
+
const getResolvedConfig = async () => {
|
|
893
|
+
if (cachedConfig === null) cachedConfig = await resolveConfig(buildConfigFlags(ctx));
|
|
894
|
+
return cachedConfig;
|
|
895
|
+
};
|
|
896
|
+
const getServerInfo = () => {
|
|
897
|
+
if (cachedServerInfo === null) cachedServerInfo = loadServerInfo(getResolvedConfig);
|
|
898
|
+
return cachedServerInfo;
|
|
899
|
+
};
|
|
900
|
+
const rawGetClient = async () => {
|
|
901
|
+
if (cachedClient === null) {
|
|
902
|
+
const resolved = await getResolvedConfig();
|
|
903
|
+
const { createClient } = await import("./client-BqhzKdLM.mjs").then((n) => n.t);
|
|
904
|
+
cachedClient = createClient({
|
|
905
|
+
url: resolved.url,
|
|
906
|
+
credential: resolved.credential
|
|
907
|
+
}, {
|
|
908
|
+
userAgent: USER_AGENT,
|
|
909
|
+
getServerTag: async () => (await getServerInfo())?.version?.tag ?? null,
|
|
910
|
+
refreshCredential: createCredentialRefresher(resolved.profile),
|
|
911
|
+
signal: interruptSignal
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
return cachedClient;
|
|
915
|
+
};
|
|
916
|
+
const enforcePreflight = createPreflightEnforcer(required, getServerInfo, ctx.skipPreflight);
|
|
917
|
+
const getClient = async () => {
|
|
918
|
+
const client = await rawGetClient();
|
|
919
|
+
await enforcePreflight();
|
|
920
|
+
return client;
|
|
921
|
+
};
|
|
922
|
+
try {
|
|
923
|
+
await def.run({
|
|
924
|
+
args,
|
|
925
|
+
ctx,
|
|
926
|
+
getClient,
|
|
927
|
+
getResolvedConfig,
|
|
928
|
+
getServerInfo
|
|
929
|
+
});
|
|
930
|
+
} finally {
|
|
931
|
+
emitPendingWarnings();
|
|
932
|
+
}
|
|
933
|
+
} catch (error) {
|
|
934
|
+
reportError(error, reportFormat);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
});
|
|
938
|
+
setMetabaseAugment(cmd, {
|
|
939
|
+
examples: def.examples ?? [],
|
|
940
|
+
details: def.details ? def.details : null,
|
|
941
|
+
skills: def.skills ?? [],
|
|
942
|
+
inputSchema: def.inputSchema ?? null,
|
|
943
|
+
outputSchema: def.outputSchema ?? null,
|
|
944
|
+
capabilities: required
|
|
945
|
+
});
|
|
946
|
+
return cmd;
|
|
947
|
+
}
|
|
948
|
+
function emitPendingWarnings() {
|
|
949
|
+
for (const message of consumeLegacyEnvWarnings()) warn(message);
|
|
950
|
+
const legacy = consumeLegacyStorageWarning();
|
|
951
|
+
if (legacy !== null) warn(legacy);
|
|
952
|
+
const downgrade = consumeKeyringDowngradeWarning();
|
|
953
|
+
if (downgrade !== null) warn(downgrade);
|
|
954
|
+
}
|
|
955
|
+
async function loadServerInfo(getResolvedConfig) {
|
|
956
|
+
const record = await readProfileRecord((await getResolvedConfig()).profile);
|
|
957
|
+
if (record === null || record.lastProbe === null) return null;
|
|
958
|
+
return {
|
|
959
|
+
version: record.lastProbe.version,
|
|
960
|
+
tokenFeatures: record.lastProbe.tokenFeatures
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
const NO_OP_ENFORCER = async () => {};
|
|
964
|
+
const NO_PROBE_DATA_WARNING = "Could not detect Metabase server version. Proceeding without preflight check; failures may produce confusing errors. Run `mb auth list` (or `mb auth login`) to populate the version cache.";
|
|
965
|
+
function createPreflightEnforcer(required, getServerInfo, skip) {
|
|
966
|
+
if (required === null || skip || isPreflightSkipped() || isBaseline(required)) return NO_OP_ENFORCER;
|
|
967
|
+
let done = false;
|
|
968
|
+
return async () => {
|
|
969
|
+
if (done) return;
|
|
970
|
+
done = true;
|
|
971
|
+
const info = await getServerInfo();
|
|
972
|
+
if (info === null) {
|
|
973
|
+
warn(NO_PROBE_DATA_WARNING);
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
976
|
+
const failure = checkCapabilities(info, required);
|
|
977
|
+
if (failure === null || failure.reason === "unknown-version") return;
|
|
978
|
+
throw new CapabilityError(failure);
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
function isBaseline(caps) {
|
|
982
|
+
return caps.minVersion === BASELINE_CAPABILITIES.minVersion && caps.tokenFeature === void 0;
|
|
983
|
+
}
|
|
984
|
+
function pickCommonArgs(args) {
|
|
985
|
+
const out = {};
|
|
986
|
+
if (typeof args["format"] === "string") out.format = args["format"];
|
|
987
|
+
if (typeof args["json"] === "boolean") out.json = args["json"];
|
|
988
|
+
if (typeof args["full"] === "boolean") out.full = args["full"];
|
|
989
|
+
if (typeof args["fields"] === "string") out.fields = args["fields"];
|
|
990
|
+
if (typeof args["maxBytes"] === "string") out.maxBytes = args["maxBytes"];
|
|
991
|
+
if (typeof args["limit"] === "string") out.limit = args["limit"];
|
|
992
|
+
if (typeof args["offset"] === "string") out.offset = args["offset"];
|
|
993
|
+
if (typeof args["profile"] === "string") out.profile = args["profile"];
|
|
994
|
+
if (typeof args["url"] === "string") out.url = args["url"];
|
|
995
|
+
if (typeof args["apiKey"] === "string") out.apiKey = args["apiKey"];
|
|
996
|
+
if (typeof args["skipPreflight"] === "boolean") out.skipPreflight = args["skipPreflight"];
|
|
997
|
+
return out;
|
|
998
|
+
}
|
|
999
|
+
function buildConfigFlags(ctx) {
|
|
1000
|
+
const flags = {};
|
|
1001
|
+
if (ctx.profile !== void 0) flags.profile = ctx.profile;
|
|
1002
|
+
if (ctx.url !== void 0) flags.url = ctx.url;
|
|
1003
|
+
if (ctx.apiKey !== void 0) flags.apiKey = ctx.apiKey;
|
|
1004
|
+
return flags;
|
|
1005
|
+
}
|
|
1006
|
+
//#endregion
|
|
1007
|
+
export { ProbedUser as A, readProfileCredential as C, writeProbeFailure as D, writeOAuthProfile as E, tryParseTag as F, SessionProperties as I, TokenFeatures as L, ProfileLastFailure as M, profileAuthMethod as N, writeProbeResult as O, ParsedVersion as P, parseInteger as R, listProfileRecords as S, resolveRecordCredential as T, DEFAULT_PROFILE as _, createCredentialRefresher as a, consumeKeyringDowngradeWarning as b, resolveProfileName as c, revokeOAuthCredential as d, OAUTH_SCOPE as f, tryDiscoverMetadata as g, registerClient as h, parseEnumCsv as i, ProfileAuthMethod as j, writeProfile as k, OAUTH_CLIENT_NAME as l, exchangeCode as m, parseCsv as n, explicitProfileName as o, discoverMetadata as p, parseEnum as r, readEnvCredentials as s, defineMetabaseCommand as t, USER_AGENT as u, clearProfile as v, readProfileRecord as w, keyringFallbackWarning as x, consumeKeychainResidualWarning as y, parseOptionalInteger as z };
|