@openpalm/ui 0.11.0-rc.2
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 +129 -0
- package/build/.openpalm-ui-version +1 -0
- package/build/client/_app/immutable/assets/0.BK7X4bPu.css +1 -0
- package/build/client/_app/immutable/assets/0.BK7X4bPu.css.br +0 -0
- package/build/client/_app/immutable/assets/0.BK7X4bPu.css.gz +0 -0
- package/build/client/_app/immutable/assets/4.D9MwgTDo.css +1 -0
- package/build/client/_app/immutable/assets/4.D9MwgTDo.css.br +0 -0
- package/build/client/_app/immutable/assets/4.D9MwgTDo.css.gz +0 -0
- package/build/client/_app/immutable/assets/5.Cj0Mk9Gn.css +1 -0
- package/build/client/_app/immutable/assets/5.Cj0Mk9Gn.css.br +0 -0
- package/build/client/_app/immutable/assets/5.Cj0Mk9Gn.css.gz +0 -0
- package/build/client/_app/immutable/assets/6.DJenXolA.css +1 -0
- package/build/client/_app/immutable/assets/6.DJenXolA.css.br +0 -0
- package/build/client/_app/immutable/assets/6.DJenXolA.css.gz +0 -0
- package/build/client/_app/immutable/assets/7.BHUsSUvX.css +1 -0
- package/build/client/_app/immutable/assets/7.BHUsSUvX.css.br +0 -0
- package/build/client/_app/immutable/assets/7.BHUsSUvX.css.gz +0 -0
- package/build/client/_app/immutable/assets/8.DjrHy5wu.css +1 -0
- package/build/client/_app/immutable/assets/8.DjrHy5wu.css.br +0 -0
- package/build/client/_app/immutable/assets/8.DjrHy5wu.css.gz +0 -0
- package/build/client/_app/immutable/assets/AuthGate.BtJV-xXj.css +1 -0
- package/build/client/_app/immutable/assets/AuthGate.BtJV-xXj.css.br +0 -0
- package/build/client/_app/immutable/assets/AuthGate.BtJV-xXj.css.gz +0 -0
- package/build/client/_app/immutable/assets/VoiceProfileSelector.Dp7tWdKe.css +1 -0
- package/build/client/_app/immutable/assets/VoiceProfileSelector.Dp7tWdKe.css.br +0 -0
- package/build/client/_app/immutable/assets/VoiceProfileSelector.Dp7tWdKe.css.gz +0 -0
- package/build/client/_app/immutable/chunks/BuFlayix.js +1 -0
- package/build/client/_app/immutable/chunks/BuFlayix.js.br +0 -0
- package/build/client/_app/immutable/chunks/BuFlayix.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BwTF6U35.js +3 -0
- package/build/client/_app/immutable/chunks/BwTF6U35.js.br +0 -0
- package/build/client/_app/immutable/chunks/BwTF6U35.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C8qMxft1.js +1 -0
- package/build/client/_app/immutable/chunks/C8qMxft1.js.br +0 -0
- package/build/client/_app/immutable/chunks/C8qMxft1.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Cb_i8bV9.js +1 -0
- package/build/client/_app/immutable/chunks/Cb_i8bV9.js.br +0 -0
- package/build/client/_app/immutable/chunks/Cb_i8bV9.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CzXGhlNF.js +1 -0
- package/build/client/_app/immutable/chunks/CzXGhlNF.js.br +0 -0
- package/build/client/_app/immutable/chunks/CzXGhlNF.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DYskO3Sk.js +5 -0
- package/build/client/_app/immutable/chunks/DYskO3Sk.js.br +0 -0
- package/build/client/_app/immutable/chunks/DYskO3Sk.js.gz +0 -0
- package/build/client/_app/immutable/chunks/ak2ISc8B.js +1 -0
- package/build/client/_app/immutable/chunks/ak2ISc8B.js.br +0 -0
- package/build/client/_app/immutable/chunks/ak2ISc8B.js.gz +0 -0
- package/build/client/_app/immutable/chunks/kNaey6uv.js +1 -0
- package/build/client/_app/immutable/chunks/kNaey6uv.js.br +0 -0
- package/build/client/_app/immutable/chunks/kNaey6uv.js.gz +0 -0
- package/build/client/_app/immutable/chunks/os2NZ37U.js +1 -0
- package/build/client/_app/immutable/chunks/os2NZ37U.js.br +1 -0
- package/build/client/_app/immutable/chunks/os2NZ37U.js.gz +0 -0
- package/build/client/_app/immutable/chunks/xihTtKlq.js +1 -0
- package/build/client/_app/immutable/chunks/xihTtKlq.js.br +1 -0
- package/build/client/_app/immutable/chunks/xihTtKlq.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.C_eSlUyi.js +2 -0
- package/build/client/_app/immutable/entry/app.C_eSlUyi.js.br +0 -0
- package/build/client/_app/immutable/entry/app.C_eSlUyi.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.BAta_Cih.js +1 -0
- package/build/client/_app/immutable/entry/start.BAta_Cih.js.br +0 -0
- package/build/client/_app/immutable/entry/start.BAta_Cih.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.Bi_of0So.js +1 -0
- package/build/client/_app/immutable/nodes/0.Bi_of0So.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.Bi_of0So.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.qjZsjpWC.js +1 -0
- package/build/client/_app/immutable/nodes/1.qjZsjpWC.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.qjZsjpWC.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.ojh8oE7F.js +1 -0
- package/build/client/_app/immutable/nodes/2.ojh8oE7F.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.ojh8oE7F.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.DeAC3yVJ.js +1 -0
- package/build/client/_app/immutable/nodes/3.DeAC3yVJ.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.DeAC3yVJ.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.BYtA2xx-.js +19 -0
- package/build/client/_app/immutable/nodes/4.BYtA2xx-.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.BYtA2xx-.js.gz +0 -0
- package/build/client/_app/immutable/nodes/5.ChSoAFFd.js +4 -0
- package/build/client/_app/immutable/nodes/5.ChSoAFFd.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.ChSoAFFd.js.gz +0 -0
- package/build/client/_app/immutable/nodes/6.BRG0vYmH.js +1 -0
- package/build/client/_app/immutable/nodes/6.BRG0vYmH.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.BRG0vYmH.js.gz +0 -0
- package/build/client/_app/immutable/nodes/7.BVvy1f_p.js +15 -0
- package/build/client/_app/immutable/nodes/7.BVvy1f_p.js.br +0 -0
- package/build/client/_app/immutable/nodes/7.BVvy1f_p.js.gz +0 -0
- package/build/client/_app/immutable/nodes/8.Cb4MWMah.js +2 -0
- package/build/client/_app/immutable/nodes/8.Cb4MWMah.js.br +0 -0
- package/build/client/_app/immutable/nodes/8.Cb4MWMah.js.gz +0 -0
- package/build/client/_app/version.json +1 -0
- package/build/client/_app/version.json.br +0 -0
- package/build/client/_app/version.json.gz +0 -0
- package/build/client/banner.png +0 -0
- package/build/client/fu-128.png +0 -0
- package/build/client/fu.png +0 -0
- package/build/client/logo-128.png +0 -0
- package/build/client/logo.png +0 -0
- package/build/client/setup/wizard.css +1639 -0
- package/build/client/setup/wizard.css.br +0 -0
- package/build/client/setup/wizard.css.gz +0 -0
- package/build/client/wizard-128.png +0 -0
- package/build/client/wizard.png +0 -0
- package/build/env.js +94 -0
- package/build/handler.js +1494 -0
- package/build/index.js +345 -0
- package/build/server/chunks/0-Dk6RRhi2.js +9 -0
- package/build/server/chunks/0-Dk6RRhi2.js.map +1 -0
- package/build/server/chunks/1-CWcY3PRs.js +9 -0
- package/build/server/chunks/1-CWcY3PRs.js.map +1 -0
- package/build/server/chunks/2-C2y4ydWU.js +9 -0
- package/build/server/chunks/2-C2y4ydWU.js.map +1 -0
- package/build/server/chunks/3-BgeGdVvT.js +23 -0
- package/build/server/chunks/3-BgeGdVvT.js.map +1 -0
- package/build/server/chunks/4-DeAt_mYc.js +9 -0
- package/build/server/chunks/4-DeAt_mYc.js.map +1 -0
- package/build/server/chunks/5-qTXiNPEM.js +9 -0
- package/build/server/chunks/5-qTXiNPEM.js.map +1 -0
- package/build/server/chunks/6-C444Mr69.js +9 -0
- package/build/server/chunks/6-C444Mr69.js.map +1 -0
- package/build/server/chunks/7-Dg_sVly5.js +9 -0
- package/build/server/chunks/7-Dg_sVly5.js.map +1 -0
- package/build/server/chunks/8-CF5kn4AY.js +9 -0
- package/build/server/chunks/8-CF5kn4AY.js.map +1 -0
- package/build/server/chunks/AuthGate-keg7G6rX.js +707 -0
- package/build/server/chunks/AuthGate-keg7G6rX.js.map +1 -0
- package/build/server/chunks/_helpers-B_lb4-jB.js +74 -0
- package/build/server/chunks/_helpers-B_lb4-jB.js.map +1 -0
- package/build/server/chunks/_layout.svelte-B93aaRP_.js +11 -0
- package/build/server/chunks/_layout.svelte-B93aaRP_.js.map +1 -0
- package/build/server/chunks/_layout.svelte-BNWepgTx.js +58 -0
- package/build/server/chunks/_layout.svelte-BNWepgTx.js.map +1 -0
- package/build/server/chunks/_page.svelte-BP2EdQl3.js +5 -0
- package/build/server/chunks/_page.svelte-BP2EdQl3.js.map +1 -0
- package/build/server/chunks/_page.svelte-BdYSYjmj.js +67 -0
- package/build/server/chunks/_page.svelte-BdYSYjmj.js.map +1 -0
- package/build/server/chunks/_page.svelte-BiKY57wg.js +533 -0
- package/build/server/chunks/_page.svelte-BiKY57wg.js.map +1 -0
- package/build/server/chunks/_page.svelte-CtX_8pMq.js +5528 -0
- package/build/server/chunks/_page.svelte-CtX_8pMq.js.map +1 -0
- package/build/server/chunks/_page.svelte-D0gMlmzQ.js +104 -0
- package/build/server/chunks/_page.svelte-D0gMlmzQ.js.map +1 -0
- package/build/server/chunks/_page.svelte-DwMiUmq6.js +107 -0
- package/build/server/chunks/_page.svelte-DwMiUmq6.js.map +1 -0
- package/build/server/chunks/_server.ts-0FuXkx70.js +56 -0
- package/build/server/chunks/_server.ts-0FuXkx70.js.map +1 -0
- package/build/server/chunks/_server.ts-33LuQvHu.js +19 -0
- package/build/server/chunks/_server.ts-33LuQvHu.js.map +1 -0
- package/build/server/chunks/_server.ts-5Q0J3F4-.js +55 -0
- package/build/server/chunks/_server.ts-5Q0J3F4-.js.map +1 -0
- package/build/server/chunks/_server.ts-AJk0xBLJ.js +46 -0
- package/build/server/chunks/_server.ts-AJk0xBLJ.js.map +1 -0
- package/build/server/chunks/_server.ts-AQe1PIYE.js +68 -0
- package/build/server/chunks/_server.ts-AQe1PIYE.js.map +1 -0
- package/build/server/chunks/_server.ts-B0N2Nj7L.js +44 -0
- package/build/server/chunks/_server.ts-B0N2Nj7L.js.map +1 -0
- package/build/server/chunks/_server.ts-BDBJ6fxs.js +67 -0
- package/build/server/chunks/_server.ts-BDBJ6fxs.js.map +1 -0
- package/build/server/chunks/_server.ts-BEeaI665.js +79 -0
- package/build/server/chunks/_server.ts-BEeaI665.js.map +1 -0
- package/build/server/chunks/_server.ts-BM4QuGQf.js +53 -0
- package/build/server/chunks/_server.ts-BM4QuGQf.js.map +1 -0
- package/build/server/chunks/_server.ts-BSIZMZVn.js +50 -0
- package/build/server/chunks/_server.ts-BSIZMZVn.js.map +1 -0
- package/build/server/chunks/_server.ts-BV-6Mum8.js +54 -0
- package/build/server/chunks/_server.ts-BV-6Mum8.js.map +1 -0
- package/build/server/chunks/_server.ts-BdnJF23z.js +44 -0
- package/build/server/chunks/_server.ts-BdnJF23z.js.map +1 -0
- package/build/server/chunks/_server.ts-Bfy4RFhe.js +41 -0
- package/build/server/chunks/_server.ts-Bfy4RFhe.js.map +1 -0
- package/build/server/chunks/_server.ts-Bhh33s6N.js +149 -0
- package/build/server/chunks/_server.ts-Bhh33s6N.js.map +1 -0
- package/build/server/chunks/_server.ts-BiSuK8jN.js +41 -0
- package/build/server/chunks/_server.ts-BiSuK8jN.js.map +1 -0
- package/build/server/chunks/_server.ts-BjiyoKWK.js +41 -0
- package/build/server/chunks/_server.ts-BjiyoKWK.js.map +1 -0
- package/build/server/chunks/_server.ts-BnEeEd9g.js +57 -0
- package/build/server/chunks/_server.ts-BnEeEd9g.js.map +1 -0
- package/build/server/chunks/_server.ts-Bq2dkWvj.js +33 -0
- package/build/server/chunks/_server.ts-Bq2dkWvj.js.map +1 -0
- package/build/server/chunks/_server.ts-Br16AqpO.js +82 -0
- package/build/server/chunks/_server.ts-Br16AqpO.js.map +1 -0
- package/build/server/chunks/_server.ts-BsZrddQT.js +200 -0
- package/build/server/chunks/_server.ts-BsZrddQT.js.map +1 -0
- package/build/server/chunks/_server.ts-Btbzy89u.js +65 -0
- package/build/server/chunks/_server.ts-Btbzy89u.js.map +1 -0
- package/build/server/chunks/_server.ts-BudQlGOx.js +70 -0
- package/build/server/chunks/_server.ts-BudQlGOx.js.map +1 -0
- package/build/server/chunks/_server.ts-BxPYakCb.js +59 -0
- package/build/server/chunks/_server.ts-BxPYakCb.js.map +1 -0
- package/build/server/chunks/_server.ts-ByyRcwFy.js +79 -0
- package/build/server/chunks/_server.ts-ByyRcwFy.js.map +1 -0
- package/build/server/chunks/_server.ts-C7rtn3mH.js +103 -0
- package/build/server/chunks/_server.ts-C7rtn3mH.js.map +1 -0
- package/build/server/chunks/_server.ts-C88qd-ju.js +46 -0
- package/build/server/chunks/_server.ts-C88qd-ju.js.map +1 -0
- package/build/server/chunks/_server.ts-CD6LeslU.js +55 -0
- package/build/server/chunks/_server.ts-CD6LeslU.js.map +1 -0
- package/build/server/chunks/_server.ts-CDlWxA5S.js +60 -0
- package/build/server/chunks/_server.ts-CDlWxA5S.js.map +1 -0
- package/build/server/chunks/_server.ts-CGqrfNlv.js +514 -0
- package/build/server/chunks/_server.ts-CGqrfNlv.js.map +1 -0
- package/build/server/chunks/_server.ts-CGwYZUuw.js +759 -0
- package/build/server/chunks/_server.ts-CGwYZUuw.js.map +1 -0
- package/build/server/chunks/_server.ts-CHVs0yya.js +54 -0
- package/build/server/chunks/_server.ts-CHVs0yya.js.map +1 -0
- package/build/server/chunks/_server.ts-CMEY6oQe.js +121 -0
- package/build/server/chunks/_server.ts-CMEY6oQe.js.map +1 -0
- package/build/server/chunks/_server.ts-CS73tiTY.js +63 -0
- package/build/server/chunks/_server.ts-CS73tiTY.js.map +1 -0
- package/build/server/chunks/_server.ts-CWwgmo8z.js +33 -0
- package/build/server/chunks/_server.ts-CWwgmo8z.js.map +1 -0
- package/build/server/chunks/_server.ts-CXzZeGlQ.js +55 -0
- package/build/server/chunks/_server.ts-CXzZeGlQ.js.map +1 -0
- package/build/server/chunks/_server.ts-C_kseo_y.js +72 -0
- package/build/server/chunks/_server.ts-C_kseo_y.js.map +1 -0
- package/build/server/chunks/_server.ts-Cbk-UThV.js +73 -0
- package/build/server/chunks/_server.ts-Cbk-UThV.js.map +1 -0
- package/build/server/chunks/_server.ts-CboRRTK1.js +139 -0
- package/build/server/chunks/_server.ts-CboRRTK1.js.map +1 -0
- package/build/server/chunks/_server.ts-CgD8-EFl.js +52 -0
- package/build/server/chunks/_server.ts-CgD8-EFl.js.map +1 -0
- package/build/server/chunks/_server.ts-Ci-q0BiD.js +79 -0
- package/build/server/chunks/_server.ts-Ci-q0BiD.js.map +1 -0
- package/build/server/chunks/_server.ts-Cn7ahHlk.js +44 -0
- package/build/server/chunks/_server.ts-Cn7ahHlk.js.map +1 -0
- package/build/server/chunks/_server.ts-Cy36-qGT.js +60 -0
- package/build/server/chunks/_server.ts-Cy36-qGT.js.map +1 -0
- package/build/server/chunks/_server.ts-CyFZ9OZQ.js +97 -0
- package/build/server/chunks/_server.ts-CyFZ9OZQ.js.map +1 -0
- package/build/server/chunks/_server.ts-D-UAqhor.js +33 -0
- package/build/server/chunks/_server.ts-D-UAqhor.js.map +1 -0
- package/build/server/chunks/_server.ts-D1cb1hmF.js +90 -0
- package/build/server/chunks/_server.ts-D1cb1hmF.js.map +1 -0
- package/build/server/chunks/_server.ts-D33gvU5B.js +65 -0
- package/build/server/chunks/_server.ts-D33gvU5B.js.map +1 -0
- package/build/server/chunks/_server.ts-DDwiNChJ.js +88 -0
- package/build/server/chunks/_server.ts-DDwiNChJ.js.map +1 -0
- package/build/server/chunks/_server.ts-DEnxvwcJ.js +46 -0
- package/build/server/chunks/_server.ts-DEnxvwcJ.js.map +1 -0
- package/build/server/chunks/_server.ts-DHXmcRrP.js +41 -0
- package/build/server/chunks/_server.ts-DHXmcRrP.js.map +1 -0
- package/build/server/chunks/_server.ts-DQxY-pHZ.js +64 -0
- package/build/server/chunks/_server.ts-DQxY-pHZ.js.map +1 -0
- package/build/server/chunks/_server.ts-DXNJ2LJu.js +83 -0
- package/build/server/chunks/_server.ts-DXNJ2LJu.js.map +1 -0
- package/build/server/chunks/_server.ts-Dbly1Fe-.js +96 -0
- package/build/server/chunks/_server.ts-Dbly1Fe-.js.map +1 -0
- package/build/server/chunks/_server.ts-DffjWMNC.js +53 -0
- package/build/server/chunks/_server.ts-DffjWMNC.js.map +1 -0
- package/build/server/chunks/_server.ts-Di-8UJYw.js +40 -0
- package/build/server/chunks/_server.ts-Di-8UJYw.js.map +1 -0
- package/build/server/chunks/_server.ts-DsEzCr9k.js +109 -0
- package/build/server/chunks/_server.ts-DsEzCr9k.js.map +1 -0
- package/build/server/chunks/_server.ts-DwXd92Ow.js +63 -0
- package/build/server/chunks/_server.ts-DwXd92Ow.js.map +1 -0
- package/build/server/chunks/_server.ts-DwjjO7L5.js +41 -0
- package/build/server/chunks/_server.ts-DwjjO7L5.js.map +1 -0
- package/build/server/chunks/_server.ts-DyEbcQS0.js +62 -0
- package/build/server/chunks/_server.ts-DyEbcQS0.js.map +1 -0
- package/build/server/chunks/_server.ts-HxcM6xQA.js +70 -0
- package/build/server/chunks/_server.ts-HxcM6xQA.js.map +1 -0
- package/build/server/chunks/_server.ts-JItoNB1Z.js +57 -0
- package/build/server/chunks/_server.ts-JItoNB1Z.js.map +1 -0
- package/build/server/chunks/_server.ts-KpT_vKKo.js +77 -0
- package/build/server/chunks/_server.ts-KpT_vKKo.js.map +1 -0
- package/build/server/chunks/_server.ts-WbY7XmtT.js +157 -0
- package/build/server/chunks/_server.ts-WbY7XmtT.js.map +1 -0
- package/build/server/chunks/_server.ts-Yy0BWsSd.js +70 -0
- package/build/server/chunks/_server.ts-Yy0BWsSd.js.map +1 -0
- package/build/server/chunks/_server.ts-b9yNYKW6.js +49 -0
- package/build/server/chunks/_server.ts-b9yNYKW6.js.map +1 -0
- package/build/server/chunks/_server.ts-bAJXjaCc.js +42 -0
- package/build/server/chunks/_server.ts-bAJXjaCc.js.map +1 -0
- package/build/server/chunks/_server.ts-cwRl-tZ-.js +40 -0
- package/build/server/chunks/_server.ts-cwRl-tZ-.js.map +1 -0
- package/build/server/chunks/_server.ts-hMTx0DC3.js +159 -0
- package/build/server/chunks/_server.ts-hMTx0DC3.js.map +1 -0
- package/build/server/chunks/_server.ts-l_HoZ2_I.js +163 -0
- package/build/server/chunks/_server.ts-l_HoZ2_I.js.map +1 -0
- package/build/server/chunks/_server.ts-uLRX2uy0.js +107 -0
- package/build/server/chunks/_server.ts-uLRX2uy0.js.map +1 -0
- package/build/server/chunks/_server.ts-uWfb47S6.js +204 -0
- package/build/server/chunks/_server.ts-uWfb47S6.js.map +1 -0
- package/build/server/chunks/addon-helpers-Bnj3aKJ2.js +49 -0
- package/build/server/chunks/addon-helpers-Bnj3aKJ2.js.map +1 -0
- package/build/server/chunks/chunk-CLZ62Ad-.js +18 -0
- package/build/server/chunks/chunk-CLZ62Ad-.js.map +1 -0
- package/build/server/chunks/coercion-TNFJisCC.js +34 -0
- package/build/server/chunks/coercion-TNFJisCC.js.map +1 -0
- package/build/server/chunks/config-CEYPfvQh.js +121 -0
- package/build/server/chunks/config-CEYPfvQh.js.map +1 -0
- package/build/server/chunks/dev-DjANv7AF.js +4389 -0
- package/build/server/chunks/dev-DjANv7AF.js.map +1 -0
- package/build/server/chunks/docker-sGFTjRfy.js +20 -0
- package/build/server/chunks/docker-sGFTjRfy.js.map +1 -0
- package/build/server/chunks/endpoints-L7Wjvq44.js +340 -0
- package/build/server/chunks/endpoints-L7Wjvq44.js.map +1 -0
- package/build/server/chunks/environment-BnT49Gi3.js +36 -0
- package/build/server/chunks/environment-BnT49Gi3.js.map +1 -0
- package/build/server/chunks/error.svelte-PJnhZZCy.js +16 -0
- package/build/server/chunks/error.svelte-PJnhZZCy.js.map +1 -0
- package/build/server/chunks/exports-D1quPX8S.js +124 -0
- package/build/server/chunks/exports-D1quPX8S.js.map +1 -0
- package/build/server/chunks/helpers-5jd3ccud.js +226 -0
- package/build/server/chunks/helpers-5jd3ccud.js.map +1 -0
- package/build/server/chunks/hooks.server-Al9-eO5K.js +82 -0
- package/build/server/chunks/hooks.server-Al9-eO5K.js.map +1 -0
- package/build/server/chunks/http-D5xo_m60.js +31 -0
- package/build/server/chunks/http-D5xo_m60.js.map +1 -0
- package/build/server/chunks/internal-CJBOAkaQ.js +1544 -0
- package/build/server/chunks/internal-CJBOAkaQ.js.map +1 -0
- package/build/server/chunks/serial-queue-D9FEpYVv.js +22 -0
- package/build/server/chunks/serial-queue-D9FEpYVv.js.map +1 -0
- package/build/server/chunks/setup-deploy-UKTSVAC0.js +529 -0
- package/build/server/chunks/setup-deploy-UKTSVAC0.js.map +1 -0
- package/build/server/chunks/src-FpMyngcw.js +14064 -0
- package/build/server/chunks/src-FpMyngcw.js.map +1 -0
- package/build/server/chunks/state-BI-lJPhE.js +203 -0
- package/build/server/chunks/state-BI-lJPhE.js.map +1 -0
- package/build/server/chunks/utils-BSRjJDrZ.js +150 -0
- package/build/server/chunks/utils-BSRjJDrZ.js.map +1 -0
- package/build/server/chunks/voice-state.svelte-CQp8u0bn.js +437 -0
- package/build/server/chunks/voice-state.svelte-CQp8u0bn.js.map +1 -0
- package/build/server/index.js +4359 -0
- package/build/server/index.js.map +1 -0
- package/build/server/manifest.js +570 -0
- package/build/server/manifest.js.map +1 -0
- package/build/shims.js +32 -0
- package/package.json +61 -0
|
@@ -0,0 +1,759 @@
|
|
|
1
|
+
import { aa as readStackEnv, b as annotateAddonProfileAvailability, R as getAddonProfiles, Q as getAddonProfileSelection, a as addonProfileId, aw as writeVoiceVars, a0 as listEnabledAddonIds, w as composeStop, k as buildComposeOptions, an as setAddonProfileSelection, am as setAddonEnabled, P as getAddonProfileAvailability, x as composeUp, a3 as parseComposeStderr } from './src-FpMyngcw.js';
|
|
2
|
+
import { b as getState } from './endpoints-L7Wjvq44.js';
|
|
3
|
+
import { d as getRequestId, r as requireAdmin, h as jsonResponse, e as errorResponse } from './helpers-5jd3ccud.js';
|
|
4
|
+
import { w as withSerialQueue } from './serial-queue-D9FEpYVv.js';
|
|
5
|
+
import { existsSync, writeFileSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { execFile } from 'node:child_process';
|
|
8
|
+
import { connect } from 'node:net';
|
|
9
|
+
import './chunk-CLZ62Ad-.js';
|
|
10
|
+
import 'node:module';
|
|
11
|
+
import 'node:os';
|
|
12
|
+
import 'buffer';
|
|
13
|
+
import 'node:url';
|
|
14
|
+
import 'node:crypto';
|
|
15
|
+
import 'events';
|
|
16
|
+
import 'fs';
|
|
17
|
+
import 'node:events';
|
|
18
|
+
import 'node:stream';
|
|
19
|
+
import 'node:string_decoder';
|
|
20
|
+
import 'path';
|
|
21
|
+
import 'assert';
|
|
22
|
+
import 'zlib';
|
|
23
|
+
import 'node:assert';
|
|
24
|
+
import 'node:fs/promises';
|
|
25
|
+
|
|
26
|
+
//#region src/lib/server/voice-errors.ts
|
|
27
|
+
/**
|
|
28
|
+
* Translate raw docker / compose stderr into operator-actionable copy.
|
|
29
|
+
*
|
|
30
|
+
* Lives in $lib/server so the route can import it without violating
|
|
31
|
+
* SvelteKit's +server.ts export rules (handlers + `_`-prefixed only).
|
|
32
|
+
*
|
|
33
|
+
* Pattern matches are intentionally case-insensitive and tolerant of
|
|
34
|
+
* compose-CLI prefix decoration. Order matters: more specific patterns
|
|
35
|
+
* first.
|
|
36
|
+
*/
|
|
37
|
+
function translateDockerError(stderr) {
|
|
38
|
+
const raw = (stderr ?? "").trim();
|
|
39
|
+
if (!raw) return "Docker reported an unknown error (no stderr).";
|
|
40
|
+
if (/spawn .*docker.*ENOENT|no such file or directory.*docker|docker:\s*command not found/i.test(raw)) return "Docker isn't installed on this host. Install Docker Engine or Docker Desktop, then retry.";
|
|
41
|
+
if (/pull access denied|manifest unknown|repository does not exist|not found: manifest unknown/i.test(raw)) return "The voice image for this profile isn't published yet. Try the CPU profile.";
|
|
42
|
+
if (/failed to resolve reference|dial tcp.*lookup .+docker\.io|i\/o timeout.*registry|connection refused.*registry/i.test(raw)) return "Can't reach Docker Hub. Check your internet connection, corporate firewall, or configure a registry mirror.";
|
|
43
|
+
if (/no space left on device|insufficient.*disk space|ENOSPC|write.*disk full/i.test(raw)) return "Out of disk space. Free space and try again, or run `docker image prune -a` to reclaim unused images.";
|
|
44
|
+
if (/port is already allocated|bind.*address already in use|address already in use|failed to bind host port/i.test(raw)) return "Port 8880 is already in use on this host. Free it or change the host port (set OP_VOICE_PORT_HOST).";
|
|
45
|
+
if (/unknown[^\n]*runtime[^\n]*nvidia|runtime\s+"nvidia"\s+not\s+found|nvidia.*runtime.*not[^a-z]+(found|registered)/i.test(raw)) return "The NVIDIA Docker runtime isn't registered on this machine. Try the CPU profile, or install nvidia-container-toolkit.";
|
|
46
|
+
if (/invoking the NVIDIA Container Runtime Hook/i.test(raw)) return "Docker is in CDI mode but no CDI spec is registered. Try the CPU profile.";
|
|
47
|
+
return raw.length > 300 ? raw.slice(0, 297) + "…" : raw;
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region src/routes/admin/voice/+server.ts
|
|
51
|
+
/**
|
|
52
|
+
* GET /admin/voice — Return current TTS/STT env vars from stack.env plus
|
|
53
|
+
* an `availability` block (best-effort reachability of
|
|
54
|
+
* the configured remote endpoints).
|
|
55
|
+
* PUT /admin/voice — Write TTS/STT env vars to stack.env. Auto-enables
|
|
56
|
+
* the openpalm-voice addon, brings the chosen profile
|
|
57
|
+
* up, waits for /health, and translates Docker errors
|
|
58
|
+
* to operator-actionable copy.
|
|
59
|
+
*/
|
|
60
|
+
var VOICE_ADDON = "voice";
|
|
61
|
+
var VOICE_PROBE_TIMEOUT_MS = 18e4;
|
|
62
|
+
var VOICE_PROBE_INTERVAL_MS = 1e3;
|
|
63
|
+
var JOB_RETAIN_MS = 5 * 6e4;
|
|
64
|
+
var activeJobs = /* @__PURE__ */ new Map();
|
|
65
|
+
function setJob(addon, patch) {
|
|
66
|
+
const existing = activeJobs.get(addon);
|
|
67
|
+
const next = existing ? {
|
|
68
|
+
...existing,
|
|
69
|
+
...patch
|
|
70
|
+
} : {
|
|
71
|
+
state: "pulling",
|
|
72
|
+
steps: [],
|
|
73
|
+
startedAt: Date.now(),
|
|
74
|
+
...patch
|
|
75
|
+
};
|
|
76
|
+
activeJobs.set(addon, next);
|
|
77
|
+
return next;
|
|
78
|
+
}
|
|
79
|
+
function getActiveJob(addon) {
|
|
80
|
+
const job = activeJobs.get(addon);
|
|
81
|
+
if (!job) return void 0;
|
|
82
|
+
if (Date.now() - (job.finishedAt ?? job.startedAt) > JOB_RETAIN_MS) {
|
|
83
|
+
activeJobs.delete(addon);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
return job;
|
|
87
|
+
}
|
|
88
|
+
var REACHABILITY_TIMEOUT_MS = 1500;
|
|
89
|
+
var PORT_PROBE_TIMEOUT_MS = 750;
|
|
90
|
+
async function probeReachable(baseURL) {
|
|
91
|
+
if (!baseURL) return false;
|
|
92
|
+
const url = baseURL.replace(/\/+$/, "") + "/v1/models";
|
|
93
|
+
try {
|
|
94
|
+
return (await fetch(url, {
|
|
95
|
+
method: "GET",
|
|
96
|
+
signal: AbortSignal.timeout(REACHABILITY_TIMEOUT_MS)
|
|
97
|
+
})).status < 500;
|
|
98
|
+
} catch {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Pick the best profile for this host. Prefers the first available GPU
|
|
104
|
+
* profile (anything that isn't the canonical CPU profile) so operators with NVIDIA/AMD hardware
|
|
105
|
+
* get the accelerated variant auto-selected. Falls back to the labelled
|
|
106
|
+
* default, then first available, then first profile.
|
|
107
|
+
*/
|
|
108
|
+
function resolveDefaultProfile(profiles) {
|
|
109
|
+
if (profiles.length === 0) return null;
|
|
110
|
+
const availableGpu = profiles.find((p) => p.id !== addonProfileId(VOICE_ADDON, "cpu") && p.available !== false);
|
|
111
|
+
if (availableGpu) return availableGpu.id;
|
|
112
|
+
const labelledDefault = profiles.find((p) => p.default);
|
|
113
|
+
if (labelledDefault && labelledDefault.available !== false) return labelledDefault.id;
|
|
114
|
+
const firstAvailable = profiles.find((p) => p.available !== false);
|
|
115
|
+
if (firstAvailable) return firstAvailable.id;
|
|
116
|
+
return profiles[0].id;
|
|
117
|
+
}
|
|
118
|
+
var GET = async (event) => {
|
|
119
|
+
const requestId = getRequestId(event);
|
|
120
|
+
const authError = requireAdmin(event, requestId);
|
|
121
|
+
if (authError) return authError;
|
|
122
|
+
const state = getState();
|
|
123
|
+
const env = readStackEnv(state.stackDir);
|
|
124
|
+
const ttsBaseURL = env["OP_TTS_BASE_URL"] ?? "";
|
|
125
|
+
const sttBaseURL = env["OP_STT_BASE_URL"] ?? "";
|
|
126
|
+
const profiles = await annotateAddonProfileAvailability(getAddonProfiles(state.homeDir, VOICE_ADDON));
|
|
127
|
+
const selectedProfile = getAddonProfileSelection(state.stackDir, VOICE_ADDON) ?? resolveDefaultProfile(profiles);
|
|
128
|
+
const [sttReachable, ttsReachable] = await Promise.all([probeReachable(sttBaseURL), probeReachable(ttsBaseURL)]);
|
|
129
|
+
return jsonResponse(200, {
|
|
130
|
+
tts: {
|
|
131
|
+
enabled: true,
|
|
132
|
+
engine: env["OP_TTS_ENGINE"] ?? "",
|
|
133
|
+
provider: env["OP_TTS_PROVIDER"] ?? "",
|
|
134
|
+
baseURL: ttsBaseURL,
|
|
135
|
+
model: env["OP_TTS_MODEL"] ?? "",
|
|
136
|
+
voice: env["OP_TTS_VOICE"] ?? ""
|
|
137
|
+
},
|
|
138
|
+
stt: {
|
|
139
|
+
enabled: true,
|
|
140
|
+
engine: env["OP_STT_ENGINE"] ?? "",
|
|
141
|
+
provider: env["OP_STT_PROVIDER"] ?? "",
|
|
142
|
+
baseURL: sttBaseURL,
|
|
143
|
+
model: env["OP_STT_MODEL"] ?? "",
|
|
144
|
+
language: env["OP_STT_LANGUAGE"] ?? ""
|
|
145
|
+
},
|
|
146
|
+
availability: {
|
|
147
|
+
stt: {
|
|
148
|
+
remoteConfigured: Boolean(sttBaseURL),
|
|
149
|
+
remoteReachable: sttReachable
|
|
150
|
+
},
|
|
151
|
+
tts: {
|
|
152
|
+
remoteConfigured: Boolean(ttsBaseURL),
|
|
153
|
+
remoteReachable: ttsReachable
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
addon: {
|
|
157
|
+
profiles,
|
|
158
|
+
selectedProfile,
|
|
159
|
+
...getActiveJob(VOICE_ADDON) ? { activeJob: getActiveJob(VOICE_ADDON) } : {}
|
|
160
|
+
}
|
|
161
|
+
}, requestId);
|
|
162
|
+
};
|
|
163
|
+
function readSection(raw, kind) {
|
|
164
|
+
if (!raw || typeof raw !== "object") return null;
|
|
165
|
+
const section = {
|
|
166
|
+
enabled: raw.enabled !== false,
|
|
167
|
+
engine: typeof raw.engine === "string" ? raw.engine : void 0,
|
|
168
|
+
provider: typeof raw.provider === "string" ? raw.provider : void 0,
|
|
169
|
+
baseURL: typeof raw.baseURL === "string" ? raw.baseURL : void 0,
|
|
170
|
+
model: typeof raw.model === "string" ? raw.model : void 0
|
|
171
|
+
};
|
|
172
|
+
if (kind === "tts" && typeof raw.voice === "string") section.voice = raw.voice;
|
|
173
|
+
if (kind === "stt" && typeof raw.language === "string") section.language = raw.language;
|
|
174
|
+
return section;
|
|
175
|
+
}
|
|
176
|
+
function voiceHostPort() {
|
|
177
|
+
const raw = process.env.OP_VOICE_PORT_HOST?.trim();
|
|
178
|
+
const n = raw ? Number(raw) : NaN;
|
|
179
|
+
return Number.isFinite(n) && n > 0 ? n : 8880;
|
|
180
|
+
}
|
|
181
|
+
function openpalmVoiceBaseURL() {
|
|
182
|
+
return `http://127.0.0.1:${voiceHostPort()}`;
|
|
183
|
+
}
|
|
184
|
+
var OPENPALM_VOICE_TTS_MODEL = "kokoro";
|
|
185
|
+
var OPENPALM_VOICE_STT_MODEL = "whisper-1";
|
|
186
|
+
var OPENPALM_VOICE_DEFAULT_VOICE = "bf_isabella";
|
|
187
|
+
/**
|
|
188
|
+
* For `engine === 'openpalm-voice'`, fill in baseURL/model with the addon's
|
|
189
|
+
* preset values when the user didn't provide them. This is the auto-config
|
|
190
|
+
* that makes "select OpenPalm Voice → Save" Just Work as long as the addon
|
|
191
|
+
* is enabled. The user can still override (e.g. point at a different
|
|
192
|
+
* voice host on the LAN).
|
|
193
|
+
*/
|
|
194
|
+
function applyOpenPalmVoicePreset(section, kind) {
|
|
195
|
+
if (section.engine !== "openpalm-voice") return;
|
|
196
|
+
if (!section.baseURL || !section.baseURL.trim()) section.baseURL = openpalmVoiceBaseURL();
|
|
197
|
+
if (!section.model || !section.model.trim()) section.model = kind === "tts" ? OPENPALM_VOICE_TTS_MODEL : OPENPALM_VOICE_STT_MODEL;
|
|
198
|
+
if (kind === "tts" && (!section.voice || !section.voice.trim())) section.voice = OPENPALM_VOICE_DEFAULT_VOICE;
|
|
199
|
+
}
|
|
200
|
+
function validateSection(section, kind) {
|
|
201
|
+
if (!section || !section.engine) return null;
|
|
202
|
+
if (section.engine === "browser" || section.engine === "browser-stt" || section.engine === "browser-tts") return null;
|
|
203
|
+
if (section.engine.startsWith("skip-")) return null;
|
|
204
|
+
if (!section.baseURL || !section.baseURL.trim()) return `Remote ${kind.toUpperCase()} requires an endpoint URL.`;
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
function execFileNoThrow(cmd, args, timeoutMs) {
|
|
208
|
+
return new Promise((resolve) => {
|
|
209
|
+
execFile(cmd, args, { timeout: timeoutMs }, (error, stdout, stderr) => {
|
|
210
|
+
let mergedStderr = stderr?.toString() ?? "";
|
|
211
|
+
const code = error?.code;
|
|
212
|
+
if (code && !mergedStderr) if (code === "ENOENT") mergedStderr = `spawn ${cmd} ENOENT: command not found`;
|
|
213
|
+
else mergedStderr = `spawn ${cmd} ${code}`;
|
|
214
|
+
resolve({
|
|
215
|
+
ok: !error,
|
|
216
|
+
stdout: stdout?.toString() ?? "",
|
|
217
|
+
stderr: mergedStderr
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* True when the local docker daemon already has the named image cached.
|
|
224
|
+
* `docker image inspect` exits 0 only when the image is present locally.
|
|
225
|
+
*/
|
|
226
|
+
async function dockerImagePresent(imageRef) {
|
|
227
|
+
if (!imageRef) return true;
|
|
228
|
+
return (await execFileNoThrow("docker", [
|
|
229
|
+
"image",
|
|
230
|
+
"inspect",
|
|
231
|
+
imageRef
|
|
232
|
+
], 5e3)).ok;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Heuristic: image tags that include `-cu121` / `-rocm6` / `-cpu` are the
|
|
236
|
+
* multi-GB voice images. Show the "this may take a few minutes" toast for
|
|
237
|
+
* first pulls so the operator knows the upcoming compose-up isn't stuck.
|
|
238
|
+
*/
|
|
239
|
+
function isLargeImageTag(imageRef) {
|
|
240
|
+
return /(-cu\d+|-rocm\d+|-cpu)(\s|$|@|\b)/i.test(imageRef);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Read the resolved image for a service from the merged compose config.
|
|
244
|
+
* Best-effort — returns "" on any failure so callers can skip the pre-pull
|
|
245
|
+
* check rather than blocking save.
|
|
246
|
+
*/
|
|
247
|
+
async function resolveServiceImage(composeFiles, service) {
|
|
248
|
+
const args = ["compose"];
|
|
249
|
+
for (const f of composeFiles) args.push("-f", f);
|
|
250
|
+
args.push("--project-name", resolveProjectName(), "config", "--format", "json");
|
|
251
|
+
const res = await execFileNoThrow("docker", args, 15e3);
|
|
252
|
+
if (!res.ok) return "";
|
|
253
|
+
try {
|
|
254
|
+
return JSON.parse(res.stdout).services?.[service]?.image ?? "";
|
|
255
|
+
} catch {
|
|
256
|
+
return "";
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
function resolveProjectName() {
|
|
260
|
+
return process.env.OP_PROJECT_NAME?.trim() || process.env.COMPOSE_PROJECT_NAME?.trim() || "openpalm";
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Probe a TCP port on 127.0.0.1. Resolves true when the connect succeeds
|
|
264
|
+
* within PORT_PROBE_TIMEOUT_MS — meaning something is already listening.
|
|
265
|
+
*/
|
|
266
|
+
function isPortListening(port) {
|
|
267
|
+
return new Promise((resolve) => {
|
|
268
|
+
const socket = connect({
|
|
269
|
+
host: "127.0.0.1",
|
|
270
|
+
port
|
|
271
|
+
});
|
|
272
|
+
let done = false;
|
|
273
|
+
const finish = (listening) => {
|
|
274
|
+
if (done) return;
|
|
275
|
+
done = true;
|
|
276
|
+
try {
|
|
277
|
+
socket.destroy();
|
|
278
|
+
} catch {}
|
|
279
|
+
resolve(listening);
|
|
280
|
+
};
|
|
281
|
+
socket.setTimeout(PORT_PROBE_TIMEOUT_MS, () => finish(false));
|
|
282
|
+
socket.once("connect", () => finish(true));
|
|
283
|
+
socket.once("error", () => finish(false));
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* True when a docker container whose name matches openpalm-voice* is
|
|
288
|
+
* already running and presumably owns the host port. Used by the port
|
|
289
|
+
* pre-flight to avoid false positives when our own voice container is
|
|
290
|
+
* the listener.
|
|
291
|
+
*/
|
|
292
|
+
async function ourVoiceContainerRunning() {
|
|
293
|
+
const res = await execFileNoThrow("docker", [
|
|
294
|
+
"ps",
|
|
295
|
+
"--filter",
|
|
296
|
+
"name=openpalm-voice",
|
|
297
|
+
"--format",
|
|
298
|
+
"{{.Names}}"
|
|
299
|
+
], 5e3);
|
|
300
|
+
if (!res.ok) return false;
|
|
301
|
+
return res.stdout.trim().length > 0;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Read the Docker healthcheck state of a container.
|
|
305
|
+
* Returns "starting" while compose's start_period grace window is in
|
|
306
|
+
* effect; "healthy" / "unhealthy" / "none" / "" otherwise.
|
|
307
|
+
*/
|
|
308
|
+
async function readContainerHealthStatus(containerNamePrefix) {
|
|
309
|
+
const name = (await execFileNoThrow("docker", [
|
|
310
|
+
"ps",
|
|
311
|
+
"--filter",
|
|
312
|
+
`name=${containerNamePrefix}`,
|
|
313
|
+
"--format",
|
|
314
|
+
"{{.Names}}"
|
|
315
|
+
], 5e3)).stdout.split("\n").map((s) => s.trim()).find(Boolean);
|
|
316
|
+
if (!name) return "";
|
|
317
|
+
return (await execFileNoThrow("docker", [
|
|
318
|
+
"inspect",
|
|
319
|
+
name,
|
|
320
|
+
"--format",
|
|
321
|
+
"{{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}"
|
|
322
|
+
], 5e3)).stdout.trim();
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Write a sibling compose overlay that switches `voice-cuda` from the
|
|
326
|
+
* legacy `runtime: nvidia` form to the CDI `driver: cdi` form. Caller
|
|
327
|
+
* includes it in the composeUp file list ONLY when the host probe
|
|
328
|
+
* indicates the runtime is missing but a CDI spec exists.
|
|
329
|
+
*
|
|
330
|
+
* The canonical compose.yml stays as the runtime-nvidia form (the case
|
|
331
|
+
* that needs no manual setup beyond installing nvidia-container-toolkit).
|
|
332
|
+
*
|
|
333
|
+
* Returns the absolute path of the overlay, or null when there is no
|
|
334
|
+
* enabled voice addon directory to write into.
|
|
335
|
+
*/
|
|
336
|
+
function writeCdiOverlayIfNeeded(homeDir) {
|
|
337
|
+
const addonDir = join(homeDir, "config", "stack", "addons", VOICE_ADDON);
|
|
338
|
+
if (!existsSync(addonDir)) return null;
|
|
339
|
+
const overlayPath = join(addonDir, "compose.cdi.yml");
|
|
340
|
+
writeFileSync(overlayPath, [
|
|
341
|
+
"# Generated overlay — switches voice-cuda from runtime:nvidia to CDI.",
|
|
342
|
+
"# Applied only when the host probe shows the legacy NVIDIA runtime is",
|
|
343
|
+
"# missing but /etc/cdi/nvidia.yaml is present.",
|
|
344
|
+
"services:",
|
|
345
|
+
" voice-cuda:",
|
|
346
|
+
" runtime: \"\"",
|
|
347
|
+
" deploy:",
|
|
348
|
+
" resources:",
|
|
349
|
+
" reservations:",
|
|
350
|
+
" devices:",
|
|
351
|
+
" - driver: cdi",
|
|
352
|
+
" device_ids:",
|
|
353
|
+
" - nvidia.com/gpu=all",
|
|
354
|
+
""
|
|
355
|
+
].join("\n"));
|
|
356
|
+
return overlayPath;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Detect rootless Docker. The compose `user: "${OP_UID:-1000}:${OP_GID:-1000}"`
|
|
360
|
+
* directive bakes the host UID into the container — but on a rootless
|
|
361
|
+
* daemon the bind-mount UID inside the container is subuid-remapped, so
|
|
362
|
+
* the resulting container UID has no write permission against
|
|
363
|
+
* `${OP_HOME}/data/voice/models`. Removing the `user:` directive lets
|
|
364
|
+
* Docker pick whatever UID the rootless mapping translates to inside the
|
|
365
|
+
* user namespace, which DOES have write access to the bind-mount.
|
|
366
|
+
*
|
|
367
|
+
* `docker info` is the authoritative source: rootless daemons advertise
|
|
368
|
+
* `SecurityOptions: ... name=rootless` and `CgroupDriver: ... rootless`.
|
|
369
|
+
* We accept either signal.
|
|
370
|
+
*/
|
|
371
|
+
async function detectRootlessDocker() {
|
|
372
|
+
const res = await execFileNoThrow("docker", [
|
|
373
|
+
"info",
|
|
374
|
+
"--format",
|
|
375
|
+
"{{json .}}"
|
|
376
|
+
], 5e3);
|
|
377
|
+
if (!res.ok || !res.stdout) return false;
|
|
378
|
+
try {
|
|
379
|
+
const parsed = JSON.parse(res.stdout);
|
|
380
|
+
if ((Array.isArray(parsed.SecurityOptions) ? parsed.SecurityOptions.map((s) => String(s)) : []).some((s) => /name=rootless/i.test(s))) return true;
|
|
381
|
+
if (typeof parsed.CgroupDriver === "string" && /rootless/i.test(parsed.CgroupDriver)) return true;
|
|
382
|
+
return false;
|
|
383
|
+
} catch {
|
|
384
|
+
return /name=rootless|cgroup\s*driver:.*rootless/i.test(res.stdout);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Write a sibling overlay that drops the `user:` directive from each
|
|
389
|
+
* voice service. Mirrors writeCdiOverlayIfNeeded: caller includes the
|
|
390
|
+
* returned path in composeUp's file list. Returns null when there is
|
|
391
|
+
* no enabled voice addon directory to write into (so the file list
|
|
392
|
+
* stays valid and Docker doesn't blow up on a missing -f arg).
|
|
393
|
+
*/
|
|
394
|
+
function writeRootlessOverlayIfNeeded(homeDir) {
|
|
395
|
+
const addonDir = join(homeDir, "config", "stack", "addons", VOICE_ADDON);
|
|
396
|
+
if (!existsSync(addonDir)) return null;
|
|
397
|
+
const overlayPath = join(addonDir, "compose.rootless.yml");
|
|
398
|
+
writeFileSync(overlayPath, [
|
|
399
|
+
"# Generated overlay — removes the `user:` directive from voice services.",
|
|
400
|
+
"# Applied only when `docker info` reports a rootless daemon. On rootless",
|
|
401
|
+
"# Docker the compose-baked UID has no write access to the bind-mounted",
|
|
402
|
+
"# state directory; letting Docker pick the namespaced UID restores it.",
|
|
403
|
+
"services:",
|
|
404
|
+
" voice:",
|
|
405
|
+
" user: null",
|
|
406
|
+
" voice-cuda:",
|
|
407
|
+
" user: null",
|
|
408
|
+
" voice-rocm:",
|
|
409
|
+
" user: null",
|
|
410
|
+
""
|
|
411
|
+
].join("\n"));
|
|
412
|
+
return overlayPath;
|
|
413
|
+
}
|
|
414
|
+
var PUT = (event) => {
|
|
415
|
+
return withSerialQueue("admin:voice:put", () => handlePut(event));
|
|
416
|
+
};
|
|
417
|
+
async function handlePut(event) {
|
|
418
|
+
const requestId = getRequestId(event);
|
|
419
|
+
const authError = requireAdmin(event, requestId);
|
|
420
|
+
if (authError) return authError;
|
|
421
|
+
const state = getState();
|
|
422
|
+
let body;
|
|
423
|
+
try {
|
|
424
|
+
body = await event.request.json();
|
|
425
|
+
} catch {
|
|
426
|
+
return errorResponse(400, "Bad Request", "Invalid JSON body", {}, requestId);
|
|
427
|
+
}
|
|
428
|
+
if (!body || typeof body !== "object") return errorResponse(400, "Bad Request", "Body must be an object", {}, requestId);
|
|
429
|
+
const b = body;
|
|
430
|
+
const ttsSection = readSection(b.tts, "tts");
|
|
431
|
+
const sttSection = readSection(b.stt, "stt");
|
|
432
|
+
if (ttsSection) applyOpenPalmVoicePreset(ttsSection, "tts");
|
|
433
|
+
if (sttSection) applyOpenPalmVoicePreset(sttSection, "stt");
|
|
434
|
+
const ttsErr = validateSection(ttsSection, "tts");
|
|
435
|
+
if (ttsErr) return errorResponse(400, "invalid_tts", ttsErr, {}, requestId);
|
|
436
|
+
const sttErr = validateSection(sttSection, "stt");
|
|
437
|
+
if (sttErr) return errorResponse(400, "invalid_stt", sttErr, {}, requestId);
|
|
438
|
+
const config = {};
|
|
439
|
+
if (ttsSection) config.tts = {
|
|
440
|
+
enabled: ttsSection.enabled,
|
|
441
|
+
engine: ttsSection.engine,
|
|
442
|
+
provider: ttsSection.provider,
|
|
443
|
+
baseURL: ttsSection.baseURL,
|
|
444
|
+
model: ttsSection.model,
|
|
445
|
+
voice: ttsSection.voice
|
|
446
|
+
};
|
|
447
|
+
if (sttSection) config.stt = {
|
|
448
|
+
enabled: sttSection.enabled,
|
|
449
|
+
engine: sttSection.engine,
|
|
450
|
+
provider: sttSection.provider,
|
|
451
|
+
baseURL: sttSection.baseURL,
|
|
452
|
+
model: sttSection.model,
|
|
453
|
+
language: sttSection.language
|
|
454
|
+
};
|
|
455
|
+
writeVoiceVars(config, state.stackDir);
|
|
456
|
+
if (!(ttsSection?.engine === "openpalm-voice" || sttSection?.engine === "openpalm-voice")) {
|
|
457
|
+
if (listEnabledAddonIds(state.homeDir).includes(VOICE_ADDON)) try {
|
|
458
|
+
const voiceServiceNames = getAddonProfiles(state.homeDir, VOICE_ADDON).flatMap((p) => p.services);
|
|
459
|
+
const unique = Array.from(new Set(voiceServiceNames));
|
|
460
|
+
if (unique.length > 0) await composeStop(unique, buildComposeOptions(state));
|
|
461
|
+
} catch (e) {
|
|
462
|
+
console.warn("[voice] composeStop on disengage failed:", e);
|
|
463
|
+
}
|
|
464
|
+
return jsonResponse(200, { ok: true }, requestId);
|
|
465
|
+
}
|
|
466
|
+
const availableProfiles = await annotateAddonProfileAvailability(getAddonProfiles(state.homeDir, VOICE_ADDON));
|
|
467
|
+
const requestedProfile = typeof b.profile === "string" ? b.profile.trim() : "";
|
|
468
|
+
let activeProfile = null;
|
|
469
|
+
if (requestedProfile) {
|
|
470
|
+
if (!availableProfiles.some((p) => p.id === requestedProfile)) return errorResponse(400, "invalid_profile", `Unknown voice profile "${requestedProfile}". Available: ${availableProfiles.map((p) => p.id).join(", ") || "(none)"}`, {}, requestId);
|
|
471
|
+
activeProfile = requestedProfile;
|
|
472
|
+
setAddonProfileSelection(state.stackDir, VOICE_ADDON, activeProfile);
|
|
473
|
+
} else activeProfile = getAddonProfileSelection(state.stackDir, VOICE_ADDON) ?? resolveDefaultProfile(availableProfiles);
|
|
474
|
+
const wasAlreadyEnabled = listEnabledAddonIds(state.homeDir).includes(VOICE_ADDON);
|
|
475
|
+
const steps = [];
|
|
476
|
+
if (!wasAlreadyEnabled) try {
|
|
477
|
+
setAddonEnabled(state.homeDir, state.stackDir, VOICE_ADDON, true, state);
|
|
478
|
+
steps.push({
|
|
479
|
+
step: "enable",
|
|
480
|
+
ok: true
|
|
481
|
+
});
|
|
482
|
+
} catch (e) {
|
|
483
|
+
const detail = e instanceof Error ? e.message : String(e);
|
|
484
|
+
steps.push({
|
|
485
|
+
step: "enable",
|
|
486
|
+
ok: false,
|
|
487
|
+
detail
|
|
488
|
+
});
|
|
489
|
+
return jsonResponse(502, {
|
|
490
|
+
ok: false,
|
|
491
|
+
voiceAddon: {
|
|
492
|
+
wasAlreadyEnabled,
|
|
493
|
+
steps,
|
|
494
|
+
error: `Could not enable voice addon: ${detail}`
|
|
495
|
+
}
|
|
496
|
+
}, requestId);
|
|
497
|
+
}
|
|
498
|
+
else steps.push({
|
|
499
|
+
step: "enable",
|
|
500
|
+
ok: true,
|
|
501
|
+
detail: "already enabled"
|
|
502
|
+
});
|
|
503
|
+
const hostPort = voiceHostPort();
|
|
504
|
+
const inVitest = !!process.env.VITEST;
|
|
505
|
+
if (inVitest ? false : await isPortListening(hostPort)) {
|
|
506
|
+
if (!await ourVoiceContainerRunning()) {
|
|
507
|
+
const msg = translateDockerError(`Bind for 127.0.0.1:${hostPort} failed: port is already allocated`);
|
|
508
|
+
steps.push({
|
|
509
|
+
step: "port-check",
|
|
510
|
+
ok: false,
|
|
511
|
+
detail: msg
|
|
512
|
+
});
|
|
513
|
+
return jsonResponse(502, {
|
|
514
|
+
ok: false,
|
|
515
|
+
voiceAddon: {
|
|
516
|
+
wasAlreadyEnabled,
|
|
517
|
+
steps,
|
|
518
|
+
error: msg
|
|
519
|
+
}
|
|
520
|
+
}, requestId);
|
|
521
|
+
}
|
|
522
|
+
steps.push({
|
|
523
|
+
step: "port-check",
|
|
524
|
+
ok: true,
|
|
525
|
+
detail: "our container is the listener"
|
|
526
|
+
});
|
|
527
|
+
} else steps.push({
|
|
528
|
+
step: "port-check",
|
|
529
|
+
ok: true
|
|
530
|
+
});
|
|
531
|
+
const profileServices = activeProfile ? availableProfiles.find((p) => p.id === activeProfile)?.services ?? [] : [];
|
|
532
|
+
const services = profileServices.length > 0 ? profileServices : [VOICE_ADDON];
|
|
533
|
+
const composeFilesBase = buildComposeOptions(state).files;
|
|
534
|
+
const primaryService = services[0];
|
|
535
|
+
let backgroundPull = false;
|
|
536
|
+
if (primaryService && !inVitest) {
|
|
537
|
+
const imageRef = await resolveServiceImage(composeFilesBase, primaryService);
|
|
538
|
+
if (imageRef && isLargeImageTag(imageRef)) {
|
|
539
|
+
if (!await dockerImagePresent(imageRef)) {
|
|
540
|
+
backgroundPull = true;
|
|
541
|
+
steps.push({
|
|
542
|
+
step: "pulling",
|
|
543
|
+
ok: true,
|
|
544
|
+
detail: "first-time download — several minutes for several GB"
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
const extraFiles = [];
|
|
550
|
+
const cdiFallbackSupported = process.platform !== "win32";
|
|
551
|
+
if (activeProfile === addonProfileId(VOICE_ADDON, "cuda") && !inVitest && cdiFallbackSupported) {
|
|
552
|
+
const runtimeMissing = (await getAddonProfileAvailability({ id: addonProfileId(VOICE_ADDON, "cuda") })).available === false || !await dockerHasNvidiaRuntime();
|
|
553
|
+
const cdiSpecPresent = existsSync("/etc/cdi/nvidia.yaml");
|
|
554
|
+
if (runtimeMissing && cdiSpecPresent) {
|
|
555
|
+
const overlay = writeCdiOverlayIfNeeded(state.homeDir);
|
|
556
|
+
if (overlay) {
|
|
557
|
+
extraFiles.push(overlay);
|
|
558
|
+
steps.push({
|
|
559
|
+
step: "cdi-fallback",
|
|
560
|
+
ok: true,
|
|
561
|
+
detail: "using CDI device reservation"
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
if (!inVitest) try {
|
|
567
|
+
if (await detectRootlessDocker()) {
|
|
568
|
+
const overlay = writeRootlessOverlayIfNeeded(state.homeDir);
|
|
569
|
+
if (overlay) {
|
|
570
|
+
extraFiles.push(overlay);
|
|
571
|
+
steps.push({
|
|
572
|
+
step: "rootless-fallback",
|
|
573
|
+
ok: true,
|
|
574
|
+
detail: "dropping user: directive for rootless Docker"
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
} catch (e) {
|
|
579
|
+
console.warn("[voice] rootless detection failed:", e);
|
|
580
|
+
}
|
|
581
|
+
if (backgroundPull) {
|
|
582
|
+
setJob(VOICE_ADDON, {
|
|
583
|
+
state: "pulling",
|
|
584
|
+
steps: [...steps],
|
|
585
|
+
startedAt: Date.now(),
|
|
586
|
+
profile: activeProfile ?? void 0,
|
|
587
|
+
finishedAt: void 0,
|
|
588
|
+
error: void 0
|
|
589
|
+
});
|
|
590
|
+
runBringUpJob({
|
|
591
|
+
state,
|
|
592
|
+
services,
|
|
593
|
+
activeProfile,
|
|
594
|
+
extraFiles,
|
|
595
|
+
availableProfiles,
|
|
596
|
+
baseSteps: [...steps]
|
|
597
|
+
});
|
|
598
|
+
return jsonResponse(202, {
|
|
599
|
+
ok: true,
|
|
600
|
+
voiceAddon: {
|
|
601
|
+
wasAlreadyEnabled,
|
|
602
|
+
status: "pulling",
|
|
603
|
+
steps,
|
|
604
|
+
message: "Voice image is downloading in the background (~2–8 GB). Poll GET /admin/voice for progress; UI auto-refreshes."
|
|
605
|
+
}
|
|
606
|
+
}, requestId);
|
|
607
|
+
}
|
|
608
|
+
const outcome = await runBringUp({
|
|
609
|
+
state,
|
|
610
|
+
services,
|
|
611
|
+
activeProfile,
|
|
612
|
+
extraFiles,
|
|
613
|
+
availableProfiles,
|
|
614
|
+
steps
|
|
615
|
+
});
|
|
616
|
+
if (!outcome.composeOk) return jsonResponse(502, {
|
|
617
|
+
ok: false,
|
|
618
|
+
voiceAddon: {
|
|
619
|
+
wasAlreadyEnabled,
|
|
620
|
+
steps: outcome.steps,
|
|
621
|
+
error: `Voice addon failed to start: ${outcome.composeErr ?? "unknown error"}`
|
|
622
|
+
}
|
|
623
|
+
}, requestId);
|
|
624
|
+
return jsonResponse(outcome.healthy || outcome.warming ? 200 : 502, {
|
|
625
|
+
ok: outcome.healthy || outcome.warming,
|
|
626
|
+
voiceAddon: {
|
|
627
|
+
wasAlreadyEnabled,
|
|
628
|
+
steps: outcome.steps,
|
|
629
|
+
...outcome.warming ? { warming: true } : {},
|
|
630
|
+
...outcome.healthy || outcome.warming ? {} : { error: "Voice addon is starting but did not become healthy in time." }
|
|
631
|
+
}
|
|
632
|
+
}, requestId);
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Inline composeStop-other-profiles + composeUp + /health poll. Returns
|
|
636
|
+
* the terminal state. Pushed `steps` get mutated in place so the caller
|
|
637
|
+
* (sync or background) can read progress as it happens.
|
|
638
|
+
*/
|
|
639
|
+
async function runBringUp(input) {
|
|
640
|
+
const { state, services, activeProfile, extraFiles, availableProfiles, steps } = input;
|
|
641
|
+
let composeOk = true;
|
|
642
|
+
let composeErr;
|
|
643
|
+
try {
|
|
644
|
+
const otherProfileServices = availableProfiles.filter((p) => p.id !== activeProfile).flatMap((p) => p.services).filter((svc) => !services.includes(svc));
|
|
645
|
+
if (otherProfileServices.length > 0) try {
|
|
646
|
+
await composeStop(otherProfileServices, buildComposeOptions(state));
|
|
647
|
+
} catch (e) {
|
|
648
|
+
console.warn("[voice] composeStop other profiles failed:", e);
|
|
649
|
+
}
|
|
650
|
+
const baseOpts = buildComposeOptions(state);
|
|
651
|
+
const result = await composeUp({
|
|
652
|
+
...baseOpts,
|
|
653
|
+
files: [...baseOpts.files, ...extraFiles],
|
|
654
|
+
services,
|
|
655
|
+
forceRecreate: true,
|
|
656
|
+
...activeProfile ? { profiles: [activeProfile] } : {}
|
|
657
|
+
});
|
|
658
|
+
composeOk = result.ok;
|
|
659
|
+
if (!result.ok) composeErr = translateDockerError(parseComposeStderr(result.stderr).find((f) => services.includes(f.service))?.reason ?? result.stderr ?? `compose up exited ${result.code}`);
|
|
660
|
+
} catch (e) {
|
|
661
|
+
composeOk = false;
|
|
662
|
+
composeErr = translateDockerError(e instanceof Error ? e.message : String(e));
|
|
663
|
+
}
|
|
664
|
+
steps.push({
|
|
665
|
+
step: "compose-up",
|
|
666
|
+
ok: composeOk,
|
|
667
|
+
...composeErr ? { detail: composeErr.slice(0, 500) } : {}
|
|
668
|
+
});
|
|
669
|
+
if (!composeOk) return {
|
|
670
|
+
composeOk,
|
|
671
|
+
composeErr,
|
|
672
|
+
healthy: false,
|
|
673
|
+
warming: false,
|
|
674
|
+
steps
|
|
675
|
+
};
|
|
676
|
+
const probeUrl = `${openpalmVoiceBaseURL()}/health`;
|
|
677
|
+
const deadline = Date.now() + VOICE_PROBE_TIMEOUT_MS;
|
|
678
|
+
let healthy = false;
|
|
679
|
+
while (Date.now() < deadline) {
|
|
680
|
+
try {
|
|
681
|
+
if ((await fetch(probeUrl, { signal: AbortSignal.timeout(1500) })).ok) {
|
|
682
|
+
healthy = true;
|
|
683
|
+
break;
|
|
684
|
+
}
|
|
685
|
+
} catch {}
|
|
686
|
+
await new Promise((r) => setTimeout(r, VOICE_PROBE_INTERVAL_MS));
|
|
687
|
+
}
|
|
688
|
+
let warming = false;
|
|
689
|
+
if (!healthy) try {
|
|
690
|
+
if (await readContainerHealthStatus("openpalm-voice") === "starting") warming = true;
|
|
691
|
+
} catch {}
|
|
692
|
+
steps.push({
|
|
693
|
+
step: "healthy",
|
|
694
|
+
ok: healthy || warming,
|
|
695
|
+
...healthy ? {} : warming ? { detail: "still warming up — refresh in a moment" } : { detail: `did not respond at ${probeUrl} within ${VOICE_PROBE_TIMEOUT_MS / 1e3}s` }
|
|
696
|
+
});
|
|
697
|
+
return {
|
|
698
|
+
composeOk,
|
|
699
|
+
healthy,
|
|
700
|
+
warming,
|
|
701
|
+
steps
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Background variant: runs runBringUp and persists state transitions
|
|
706
|
+
* into the activeJobs map. Returns nothing — the UI polls GET
|
|
707
|
+
* /admin/voice to observe completion.
|
|
708
|
+
*/
|
|
709
|
+
async function runBringUpJob(input) {
|
|
710
|
+
const steps = [...input.baseSteps];
|
|
711
|
+
try {
|
|
712
|
+
setJob(VOICE_ADDON, {
|
|
713
|
+
state: "starting",
|
|
714
|
+
steps
|
|
715
|
+
});
|
|
716
|
+
const outcome = await runBringUp({
|
|
717
|
+
...input,
|
|
718
|
+
steps
|
|
719
|
+
});
|
|
720
|
+
if (!outcome.composeOk) {
|
|
721
|
+
setJob(VOICE_ADDON, {
|
|
722
|
+
state: "error",
|
|
723
|
+
steps: outcome.steps,
|
|
724
|
+
error: `Voice addon failed to start: ${outcome.composeErr ?? "unknown error"}`,
|
|
725
|
+
finishedAt: Date.now()
|
|
726
|
+
});
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
setJob(VOICE_ADDON, {
|
|
730
|
+
state: outcome.healthy ? "healthy" : outcome.warming ? "starting" : "error",
|
|
731
|
+
steps: outcome.steps,
|
|
732
|
+
...outcome.healthy || outcome.warming ? { error: void 0 } : { error: "Voice addon is starting but did not become healthy in time." },
|
|
733
|
+
finishedAt: Date.now()
|
|
734
|
+
});
|
|
735
|
+
} catch (e) {
|
|
736
|
+
setJob(VOICE_ADDON, {
|
|
737
|
+
state: "error",
|
|
738
|
+
steps,
|
|
739
|
+
error: e instanceof Error ? e.message : String(e),
|
|
740
|
+
finishedAt: Date.now()
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Lightweight wrapper around `docker info` to check whether the
|
|
746
|
+
* `nvidia` runtime is registered. Used as a second signal alongside the
|
|
747
|
+
* cached canonical CUDA profile availability result.
|
|
748
|
+
*/
|
|
749
|
+
async function dockerHasNvidiaRuntime() {
|
|
750
|
+
const res = await execFileNoThrow("docker", [
|
|
751
|
+
"info",
|
|
752
|
+
"--format",
|
|
753
|
+
"{{json .Runtimes}}"
|
|
754
|
+
], 2e3);
|
|
755
|
+
return res.ok && res.stdout.includes("\"nvidia\"");
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
export { GET, PUT };
|
|
759
|
+
//# sourceMappingURL=_server.ts-CGwYZUuw.js.map
|