@openpalm/ui 0.11.2 → 0.11.3-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/build/.openpalm-ui-version +1 -1
- package/build/client/_app/immutable/chunks/C-sSVbJo.js +1 -0
- package/build/client/_app/immutable/chunks/C-sSVbJo.js.br +2 -0
- package/build/client/_app/immutable/chunks/C-sSVbJo.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C6H1sfn1.js +1 -0
- package/build/client/_app/immutable/chunks/C6H1sfn1.js.br +0 -0
- package/build/client/_app/immutable/chunks/C6H1sfn1.js.gz +0 -0
- package/build/client/_app/immutable/chunks/D9LRIz_I.js +5 -0
- package/build/client/_app/immutable/chunks/D9LRIz_I.js.br +0 -0
- package/build/client/_app/immutable/chunks/D9LRIz_I.js.gz +0 -0
- package/build/client/_app/immutable/chunks/MWgo-EQN.js +3 -0
- package/build/client/_app/immutable/chunks/MWgo-EQN.js.br +0 -0
- package/build/client/_app/immutable/chunks/MWgo-EQN.js.gz +0 -0
- package/build/client/_app/immutable/entry/{app.jhM1F9Lz.js → app.BKTyb7ZQ.js} +2 -2
- package/build/client/_app/immutable/entry/app.BKTyb7ZQ.js.br +0 -0
- package/build/client/_app/immutable/entry/app.BKTyb7ZQ.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.CN6C9_iJ.js +1 -0
- package/build/client/_app/immutable/entry/start.CN6C9_iJ.js.br +0 -0
- package/build/client/_app/immutable/entry/start.CN6C9_iJ.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{1.CAM0_CnR.js → 1.BAxAehbc.js} +1 -1
- package/build/client/_app/immutable/nodes/1.BAxAehbc.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.BAxAehbc.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{4.BInZ_Tt5.js → 4.Ds_EDXAF.js} +1 -1
- package/build/client/_app/immutable/nodes/4.Ds_EDXAF.js.br +0 -0
- package/build/client/_app/immutable/nodes/{4.BInZ_Tt5.js.gz → 4.Ds_EDXAF.js.gz} +0 -0
- package/build/client/_app/immutable/nodes/{5.e3eblL7D.js → 5.BKi1GIpv.js} +1 -1
- package/build/client/_app/immutable/nodes/5.BKi1GIpv.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.BKi1GIpv.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{6.D6wSITV7.js → 6.BmmaVwNn.js} +1 -1
- package/build/client/_app/immutable/nodes/6.BmmaVwNn.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.BmmaVwNn.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{7.C1W_OsSf.js → 7.AqCKkpbO.js} +1 -1
- package/build/client/_app/immutable/nodes/7.AqCKkpbO.js.br +0 -0
- package/build/client/_app/immutable/nodes/{7.C1W_OsSf.js.gz → 7.AqCKkpbO.js.gz} +0 -0
- package/build/client/_app/immutable/nodes/{8.9E6jSDDa.js → 8.CGV3MiHo.js} +1 -1
- package/build/client/_app/immutable/nodes/8.CGV3MiHo.js.br +0 -0
- package/build/client/_app/immutable/nodes/8.CGV3MiHo.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{9.CutxJ3jN.js → 9.CFQabEMi.js} +3 -3
- package/build/client/_app/immutable/nodes/9.CFQabEMi.js.br +0 -0
- package/build/client/_app/immutable/nodes/9.CFQabEMi.js.gz +0 -0
- package/build/client/_app/version.json +1 -1
- package/build/client/_app/version.json.br +1 -1
- package/build/client/_app/version.json.gz +0 -0
- package/build/server/chunks/1-CN4IyeuK.js +9 -0
- package/build/server/chunks/{1-DGZd-zFK.js.map → 1-CN4IyeuK.js.map} +1 -1
- package/build/server/chunks/4-CSC3dcY8.js +9 -0
- package/build/server/chunks/{4-D9UtUsJp.js.map → 4-CSC3dcY8.js.map} +1 -1
- package/build/server/chunks/5-kcs1qRfH.js +9 -0
- package/build/server/chunks/{5-B_4lRh2I.js.map → 5-kcs1qRfH.js.map} +1 -1
- package/build/server/chunks/{6-BLnRcxu-.js → 6-hGDLkIgU.js} +3 -3
- package/build/server/chunks/{6-BLnRcxu-.js.map → 6-hGDLkIgU.js.map} +1 -1
- package/build/server/chunks/7-CxrtjAcR.js +9 -0
- package/build/server/chunks/{7-Ce9mKDNS.js.map → 7-CxrtjAcR.js.map} +1 -1
- package/build/server/chunks/{8-DZdi94I7.js → 8-B4b9vc3y.js} +3 -3
- package/build/server/chunks/{8-DZdi94I7.js.map → 8-B4b9vc3y.js.map} +1 -1
- package/build/server/chunks/{9-DbwaI7nh.js → 9-CGVsBC0_.js} +2 -2
- package/build/server/chunks/{9-DbwaI7nh.js.map → 9-CGVsBC0_.js.map} +1 -1
- package/build/server/chunks/{Navbar-BHPXjl1A.js → Navbar-Dvgl5XIe.js} +6 -3
- package/build/server/chunks/Navbar-Dvgl5XIe.js.map +1 -0
- package/build/server/chunks/_page.svelte-B0M8mXzs.js.map +1 -1
- package/build/server/chunks/{_page.svelte-CXkNPL1p.js → _page.svelte-BJdYPF2B.js} +5 -5
- package/build/server/chunks/{_page.svelte-CXkNPL1p.js.map → _page.svelte-BJdYPF2B.js.map} +1 -1
- package/build/server/chunks/{_page.svelte-BXZROSm4.js → _page.svelte-Cb4lQ28_.js} +5 -5
- package/build/server/chunks/{_page.svelte-BXZROSm4.js.map → _page.svelte-Cb4lQ28_.js.map} +1 -1
- package/build/server/chunks/{_page.svelte-Cp6aBilY.js → _page.svelte-DBPaePBZ.js} +5 -5
- package/build/server/chunks/{_page.svelte-Cp6aBilY.js.map → _page.svelte-DBPaePBZ.js.map} +1 -1
- package/build/server/chunks/{_page.svelte-Ra8DEIk0.js → _page.svelte-Dm9-lf4l.js} +3 -3
- package/build/server/chunks/{_page.svelte-Ra8DEIk0.js.map → _page.svelte-Dm9-lf4l.js.map} +1 -1
- package/build/server/chunks/{_page.svelte-C4_2jNl6.js → _page.svelte-e0BQyOr0.js} +6 -6
- package/build/server/chunks/{_page.svelte-C4_2jNl6.js.map → _page.svelte-e0BQyOr0.js.map} +1 -1
- package/build/server/chunks/{_server.ts-OVKLVpsS.js → _server.ts-3bIOrNCW.js} +4 -4
- package/build/server/chunks/{_server.ts-OVKLVpsS.js.map → _server.ts-3bIOrNCW.js.map} +1 -1
- package/build/server/chunks/{_server.ts-IJ68pRcj.js → _server.ts-6j35W-bb.js} +5 -5
- package/build/server/chunks/{_server.ts-IJ68pRcj.js.map → _server.ts-6j35W-bb.js.map} +1 -1
- package/build/server/chunks/{_server.ts-Cix_DzOZ.js → _server.ts-7Wf3G6xc.js} +4 -4
- package/build/server/chunks/{_server.ts-Cix_DzOZ.js.map → _server.ts-7Wf3G6xc.js.map} +1 -1
- package/build/server/chunks/{_server.ts-5pMviGH3.js → _server.ts-B3DUJf6P.js} +16 -4
- package/build/server/chunks/_server.ts-B3DUJf6P.js.map +1 -0
- package/build/server/chunks/{_server.ts-DuJ8w3d8.js → _server.ts-B8V8xLOX.js} +4 -4
- package/build/server/chunks/{_server.ts-DuJ8w3d8.js.map → _server.ts-B8V8xLOX.js.map} +1 -1
- package/build/server/chunks/{_server.ts-C901uAGV.js → _server.ts-BGZDhLXh.js} +8 -7
- package/build/server/chunks/_server.ts-BGZDhLXh.js.map +1 -0
- package/build/server/chunks/{_server.ts-CyUtNvIJ.js → _server.ts-BOha2L_A.js} +4 -4
- package/build/server/chunks/{_server.ts-CyUtNvIJ.js.map → _server.ts-BOha2L_A.js.map} +1 -1
- package/build/server/chunks/{_server.ts-6KmCyX4_.js → _server.ts-BPBMdkDE.js} +6 -6
- package/build/server/chunks/{_server.ts-6KmCyX4_.js.map → _server.ts-BPBMdkDE.js.map} +1 -1
- package/build/server/chunks/{_server.ts-BJqeSuwW.js → _server.ts-BTFkcp50.js} +4 -4
- package/build/server/chunks/{_server.ts-BJqeSuwW.js.map → _server.ts-BTFkcp50.js.map} +1 -1
- package/build/server/chunks/{_server.ts-D5gUeFJE.js → _server.ts-BU9Dy6Ga.js} +5 -5
- package/build/server/chunks/{_server.ts-D5gUeFJE.js.map → _server.ts-BU9Dy6Ga.js.map} +1 -1
- package/build/server/chunks/{_server.ts-DiJOVzSl.js → _server.ts-B_PD1-BE.js} +4 -4
- package/build/server/chunks/{_server.ts-DiJOVzSl.js.map → _server.ts-B_PD1-BE.js.map} +1 -1
- package/build/server/chunks/{_server.ts-crIb-8QZ.js → _server.ts-BbzfnU5x.js} +5 -5
- package/build/server/chunks/{_server.ts-crIb-8QZ.js.map → _server.ts-BbzfnU5x.js.map} +1 -1
- package/build/server/chunks/{_server.ts-Bvp4YpaT.js → _server.ts-Bc4WPPlv.js} +6 -6
- package/build/server/chunks/{_server.ts-Bvp4YpaT.js.map → _server.ts-Bc4WPPlv.js.map} +1 -1
- package/build/server/chunks/{_server.ts-zCuG209p.js → _server.ts-BkpQZcF0.js} +4 -4
- package/build/server/chunks/{_server.ts-zCuG209p.js.map → _server.ts-BkpQZcF0.js.map} +1 -1
- package/build/server/chunks/{_server.ts-ByXfEYZ3.js → _server.ts-ByC7qKzo.js} +4 -4
- package/build/server/chunks/{_server.ts-ByXfEYZ3.js.map → _server.ts-ByC7qKzo.js.map} +1 -1
- package/build/server/chunks/{_server.ts-MVqFnsqa.js → _server.ts-C1m2Gw5U.js} +4 -4
- package/build/server/chunks/{_server.ts-MVqFnsqa.js.map → _server.ts-C1m2Gw5U.js.map} +1 -1
- package/build/server/chunks/{_server.ts-C_JYwlDL.js → _server.ts-C2-tZdFx.js} +4 -4
- package/build/server/chunks/{_server.ts-C_JYwlDL.js.map → _server.ts-C2-tZdFx.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CK0Rxw5K.js → _server.ts-C2qehtMf.js} +4 -4
- package/build/server/chunks/{_server.ts-CK0Rxw5K.js.map → _server.ts-C2qehtMf.js.map} +1 -1
- package/build/server/chunks/{_server.ts-BH3-HO5r.js → _server.ts-CF-JUFSB.js} +6 -6
- package/build/server/chunks/{_server.ts-BH3-HO5r.js.map → _server.ts-CF-JUFSB.js.map} +1 -1
- package/build/server/chunks/{_server.ts-Dhzz7tHs.js → _server.ts-CGHhMNs3.js} +5 -5
- package/build/server/chunks/{_server.ts-Dhzz7tHs.js.map → _server.ts-CGHhMNs3.js.map} +1 -1
- package/build/server/chunks/{_server.ts-o9s65uGr.js → _server.ts-CP6bZaJJ.js} +4 -4
- package/build/server/chunks/{_server.ts-o9s65uGr.js.map → _server.ts-CP6bZaJJ.js.map} +1 -1
- package/build/server/chunks/{_server.ts-RHuSC2x-.js → _server.ts-CS7JXLT-.js} +4 -4
- package/build/server/chunks/{_server.ts-RHuSC2x-.js.map → _server.ts-CS7JXLT-.js.map} +1 -1
- package/build/server/chunks/{_server.ts-DFJDEcdK.js → _server.ts-CSZYdKIz.js} +2 -2
- package/build/server/chunks/{_server.ts-DFJDEcdK.js.map → _server.ts-CSZYdKIz.js.map} +1 -1
- package/build/server/chunks/{_server.ts-B8jv4dTo.js → _server.ts-CS_9GHcK.js} +4 -4
- package/build/server/chunks/{_server.ts-B8jv4dTo.js.map → _server.ts-CS_9GHcK.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CjcbK3K2.js → _server.ts-CXZAOFJM.js} +4 -4
- package/build/server/chunks/{_server.ts-CjcbK3K2.js.map → _server.ts-CXZAOFJM.js.map} +1 -1
- package/build/server/chunks/{_server.ts-BRaaz800.js → _server.ts-ChEUjFy2.js} +4 -4
- package/build/server/chunks/{_server.ts-BRaaz800.js.map → _server.ts-ChEUjFy2.js.map} +1 -1
- package/build/server/chunks/{_server.ts-BmCX_RO-.js → _server.ts-CnU909WV.js} +5 -5
- package/build/server/chunks/{_server.ts-BmCX_RO-.js.map → _server.ts-CnU909WV.js.map} +1 -1
- package/build/server/chunks/{_server.ts-C6CQ4cXn.js → _server.ts-Cnjv0PV3.js} +4 -4
- package/build/server/chunks/{_server.ts-C6CQ4cXn.js.map → _server.ts-Cnjv0PV3.js.map} +1 -1
- package/build/server/chunks/{_server.ts-C6xE00MS.js → _server.ts-Ctm2JarS.js} +4 -4
- package/build/server/chunks/{_server.ts-C6xE00MS.js.map → _server.ts-Ctm2JarS.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CkyeWLQD.js → _server.ts-CuWvzlxI.js} +5 -5
- package/build/server/chunks/{_server.ts-CkyeWLQD.js.map → _server.ts-CuWvzlxI.js.map} +1 -1
- package/build/server/chunks/{_server.ts-c5vxACJj.js → _server.ts-D-CG_91w.js} +4 -4
- package/build/server/chunks/{_server.ts-c5vxACJj.js.map → _server.ts-D-CG_91w.js.map} +1 -1
- package/build/server/chunks/{_server.ts-XII8m8Um.js → _server.ts-D-p2Hm9n.js} +2 -2
- package/build/server/chunks/{_server.ts-XII8m8Um.js.map → _server.ts-D-p2Hm9n.js.map} +1 -1
- package/build/server/chunks/{_server.ts-BqQPs0i_.js → _server.ts-D-s-9ge-.js} +3 -3
- package/build/server/chunks/{_server.ts-BqQPs0i_.js.map → _server.ts-D-s-9ge-.js.map} +1 -1
- package/build/server/chunks/{_server.ts-D4X4auyD.js → _server.ts-D2KqxMre.js} +4 -4
- package/build/server/chunks/{_server.ts-D4X4auyD.js.map → _server.ts-D2KqxMre.js.map} +1 -1
- package/build/server/chunks/{_server.ts-D8sMZCw-.js → _server.ts-D2iTW5_-.js} +4 -4
- package/build/server/chunks/{_server.ts-D8sMZCw-.js.map → _server.ts-D2iTW5_-.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CqlfB-aA.js → _server.ts-D5p0zu-y.js} +4 -4
- package/build/server/chunks/{_server.ts-CqlfB-aA.js.map → _server.ts-D5p0zu-y.js.map} +1 -1
- package/build/server/chunks/{_server.ts-DhyOE-KP.js → _server.ts-D86uN0DZ.js} +2 -2
- package/build/server/chunks/{_server.ts-DhyOE-KP.js.map → _server.ts-D86uN0DZ.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CIcZeBAF.js → _server.ts-D8p-Zc8Z.js} +4 -4
- package/build/server/chunks/{_server.ts-CIcZeBAF.js.map → _server.ts-D8p-Zc8Z.js.map} +1 -1
- package/build/server/chunks/{_server.ts-BRAfEDvT.js → _server.ts-D9bBTzrV.js} +4 -4
- package/build/server/chunks/{_server.ts-BRAfEDvT.js.map → _server.ts-D9bBTzrV.js.map} +1 -1
- package/build/server/chunks/{_server.ts-X6kwNmZX.js → _server.ts-DJLROGuC.js} +4 -4
- package/build/server/chunks/{_server.ts-X6kwNmZX.js.map → _server.ts-DJLROGuC.js.map} +1 -1
- package/build/server/chunks/{_server.ts-BHIP51pX.js → _server.ts-DKKKZx7q.js} +4 -4
- package/build/server/chunks/{_server.ts-BHIP51pX.js.map → _server.ts-DKKKZx7q.js.map} +1 -1
- package/build/server/chunks/{_server.ts-DxDXTfuq.js → _server.ts-DP-NPkMD.js} +4 -4
- package/build/server/chunks/{_server.ts-DxDXTfuq.js.map → _server.ts-DP-NPkMD.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CK8sJWT2.js → _server.ts-D_da58Nk.js} +5 -5
- package/build/server/chunks/{_server.ts-CK8sJWT2.js.map → _server.ts-D_da58Nk.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CKf5jMDy.js → _server.ts-DaLBPART.js} +4 -4
- package/build/server/chunks/{_server.ts-CKf5jMDy.js.map → _server.ts-DaLBPART.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CrRcF8Ie.js → _server.ts-DgRjI1W8.js} +4 -4
- package/build/server/chunks/{_server.ts-CrRcF8Ie.js.map → _server.ts-DgRjI1W8.js.map} +1 -1
- package/build/server/chunks/{_server.ts-B4Atr7Pb.js → _server.ts-DjfCGqvA.js} +4 -4
- package/build/server/chunks/{_server.ts-B4Atr7Pb.js.map → _server.ts-DjfCGqvA.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CaSXmkds.js → _server.ts-Dkk8XP3m.js} +4 -4
- package/build/server/chunks/{_server.ts-CaSXmkds.js.map → _server.ts-Dkk8XP3m.js.map} +1 -1
- package/build/server/chunks/{_server.ts-C5cUQhLR.js → _server.ts-Dl07R_n2.js} +5 -5
- package/build/server/chunks/{_server.ts-C5cUQhLR.js.map → _server.ts-Dl07R_n2.js.map} +1 -1
- package/build/server/chunks/{_server.ts-C90P5trG.js → _server.ts-Do2sgBif.js} +8 -7
- package/build/server/chunks/_server.ts-Do2sgBif.js.map +1 -0
- package/build/server/chunks/{_server.ts-BHxlYhmn.js → _server.ts-DrJ5_e35.js} +2 -2
- package/build/server/chunks/{_server.ts-BHxlYhmn.js.map → _server.ts-DrJ5_e35.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CXkRiweb.js → _server.ts-Ds9OIz3V.js} +4 -4
- package/build/server/chunks/{_server.ts-CXkRiweb.js.map → _server.ts-Ds9OIz3V.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CPo93T4Q.js → _server.ts-DuMzNLkr.js} +6 -6
- package/build/server/chunks/{_server.ts-CPo93T4Q.js.map → _server.ts-DuMzNLkr.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CWhPqSHs.js → _server.ts-Dv1tbIPB.js} +4 -4
- package/build/server/chunks/{_server.ts-CWhPqSHs.js.map → _server.ts-Dv1tbIPB.js.map} +1 -1
- package/build/server/chunks/{_server.ts-DseLSghB.js → _server.ts-DvES1wiN.js} +4 -4
- package/build/server/chunks/{_server.ts-DseLSghB.js.map → _server.ts-DvES1wiN.js.map} +1 -1
- package/build/server/chunks/{_server.ts-Bwx3Gjwd.js → _server.ts-DwufWrL8.js} +4 -4
- package/build/server/chunks/{_server.ts-Bwx3Gjwd.js.map → _server.ts-DwufWrL8.js.map} +1 -1
- package/build/server/chunks/{_server.ts-DE0m1Q4L.js → _server.ts-DypIhw8E.js} +4 -4
- package/build/server/chunks/{_server.ts-DE0m1Q4L.js.map → _server.ts-DypIhw8E.js.map} +1 -1
- package/build/server/chunks/{_server.ts-BRefcV0Z.js → _server.ts-I_df3rNy.js} +2 -2
- package/build/server/chunks/{_server.ts-BRefcV0Z.js.map → _server.ts-I_df3rNy.js.map} +1 -1
- package/build/server/chunks/{_server.ts-BHZefY3G.js → _server.ts-JqtP_8OZ.js} +4 -4
- package/build/server/chunks/{_server.ts-BHZefY3G.js.map → _server.ts-JqtP_8OZ.js.map} +1 -1
- package/build/server/chunks/{_server.ts-fDATxtr3.js → _server.ts-KbEJb9iF.js} +4 -4
- package/build/server/chunks/{_server.ts-fDATxtr3.js.map → _server.ts-KbEJb9iF.js.map} +1 -1
- package/build/server/chunks/{_server.ts-DPhVV-jJ.js → _server.ts-KdUnJtGx.js} +4 -4
- package/build/server/chunks/{_server.ts-DPhVV-jJ.js.map → _server.ts-KdUnJtGx.js.map} +1 -1
- package/build/server/chunks/{_server.ts-BGUpdnPV.js → _server.ts-LnGYpCfy.js} +4 -4
- package/build/server/chunks/{_server.ts-BGUpdnPV.js.map → _server.ts-LnGYpCfy.js.map} +1 -1
- package/build/server/chunks/{_server.ts-HwlPZi53.js → _server.ts-MPuOqnb6.js} +5 -5
- package/build/server/chunks/{_server.ts-HwlPZi53.js.map → _server.ts-MPuOqnb6.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CFtUB8Hj.js → _server.ts-Q2jLnu8r.js} +4 -4
- package/build/server/chunks/{_server.ts-CFtUB8Hj.js.map → _server.ts-Q2jLnu8r.js.map} +1 -1
- package/build/server/chunks/{_server.ts-B3N5nJb3.js → _server.ts-URmO6EQw.js} +4 -4
- package/build/server/chunks/{_server.ts-B3N5nJb3.js.map → _server.ts-URmO6EQw.js.map} +1 -1
- package/build/server/chunks/{_server.ts-Dq4ZhGok.js → _server.ts-XOD7SUR4.js} +4 -4
- package/build/server/chunks/{_server.ts-Dq4ZhGok.js.map → _server.ts-XOD7SUR4.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CN5s8RLB.js → _server.ts-XQi6MDkU.js} +6 -6
- package/build/server/chunks/{_server.ts-CN5s8RLB.js.map → _server.ts-XQi6MDkU.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CnCzNcH9.js → _server.ts-lwKsNmQt.js} +4 -4
- package/build/server/chunks/{_server.ts-CnCzNcH9.js.map → _server.ts-lwKsNmQt.js.map} +1 -1
- package/build/server/chunks/{_server.ts-DDFiq1Ud.js → _server.ts-m1ayiB8O.js} +4 -4
- package/build/server/chunks/{_server.ts-DDFiq1Ud.js.map → _server.ts-m1ayiB8O.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CdM6LezY.js → _server.ts-qmijt9h5.js} +4 -4
- package/build/server/chunks/{_server.ts-CdM6LezY.js.map → _server.ts-qmijt9h5.js.map} +1 -1
- package/build/server/chunks/{_server.ts-DxDpkTu4.js → _server.ts-t7MfsyTJ.js} +4 -4
- package/build/server/chunks/{_server.ts-DxDpkTu4.js.map → _server.ts-t7MfsyTJ.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CKlHRDZS.js → _server.ts-z2KYSfCP.js} +4 -4
- package/build/server/chunks/{_server.ts-CKlHRDZS.js.map → _server.ts-z2KYSfCP.js.map} +1 -1
- package/build/server/chunks/{addon-helpers-BY7Y68Or.js → addon-helpers-xb9eIaVn.js} +2 -2
- package/build/server/chunks/{addon-helpers-BY7Y68Or.js.map → addon-helpers-xb9eIaVn.js.map} +1 -1
- package/build/server/chunks/{client-ArAeRoxk.js → client-s1A2mBfJ.js} +28 -3
- package/build/server/chunks/{client-ArAeRoxk.js.map → client-s1A2mBfJ.js.map} +1 -1
- package/build/server/chunks/{config-CjX55Ht6.js → config-0V4UPT81.js} +2 -2
- package/build/server/chunks/{config-CjX55Ht6.js.map → config-0V4UPT81.js.map} +1 -1
- package/build/server/chunks/{docker-NqrL9zHp.js → docker-CKOdzi6A.js} +2 -2
- package/build/server/chunks/{docker-NqrL9zHp.js.map → docker-CKOdzi6A.js.map} +1 -1
- package/build/server/chunks/{endpoints-Ci6CJJnc.js → endpoints-uSe3ORbp.js} +2 -2
- package/build/server/chunks/{endpoints-Ci6CJJnc.js.map → endpoints-uSe3ORbp.js.map} +1 -1
- package/build/server/chunks/{environment-Bpg55mjq.js → environment-CmkLMh24.js} +2 -2
- package/build/server/chunks/{environment-Bpg55mjq.js.map → environment-CmkLMh24.js.map} +1 -1
- package/build/server/chunks/{error.svelte-CpFwRtRX.js → error.svelte-B9_N1bPe.js} +4 -4
- package/build/server/chunks/{error.svelte-CpFwRtRX.js.map → error.svelte-B9_N1bPe.js.map} +1 -1
- package/build/server/chunks/{helpers-BqYJlSJg.js → helpers-D4ot6bJ_.js} +3 -3
- package/build/server/chunks/{helpers-BqYJlSJg.js.map → helpers-D4ot6bJ_.js.map} +1 -1
- package/build/server/chunks/{hooks.server-CRMfHyDz.js → hooks.server-CksTzt9Y.js} +5 -5
- package/build/server/chunks/{hooks.server-CRMfHyDz.js.map → hooks.server-CksTzt9Y.js.map} +1 -1
- package/build/server/chunks/{http-CghRhEH6.js → http-DWdG8RlO.js} +2 -2
- package/build/server/chunks/{http-CghRhEH6.js.map → http-DWdG8RlO.js.map} +1 -1
- package/build/server/chunks/{internal-W1HfYnMF.js → internal-BFPFXx52.js} +3 -3
- package/build/server/chunks/{internal-W1HfYnMF.js.map → internal-BFPFXx52.js.map} +1 -1
- package/build/server/chunks/{session-cookie-BGqzaS8B.js → session-cookie-CY2ZiP5t.js} +2 -2
- package/build/server/chunks/{session-cookie-BGqzaS8B.js.map → session-cookie-CY2ZiP5t.js.map} +1 -1
- package/build/server/chunks/{setup-deploy-Cg9faOlI.js → setup-deploy-CN1g3_cR.js} +2 -7
- package/build/server/chunks/setup-deploy-CN1g3_cR.js.map +1 -0
- package/build/server/chunks/{src-b3nBkyhY.js → src-DkGZ5D6n.js} +855 -855
- package/build/server/chunks/{src-b3nBkyhY.js.map → src-DkGZ5D6n.js.map} +1 -1
- package/build/server/chunks/{state-BIeW4-XD.js → state-CP5gULgb.js} +2 -2
- package/build/server/chunks/{state-BIeW4-XD.js.map → state-CP5gULgb.js.map} +1 -1
- package/build/server/index.js +2 -2
- package/build/server/manifest.js +79 -79
- package/build/server/manifest.js.map +1 -1
- package/package.json +2 -2
- package/build/client/_app/immutable/chunks/5ZqlzR7j.js +0 -3
- package/build/client/_app/immutable/chunks/5ZqlzR7j.js.br +0 -0
- package/build/client/_app/immutable/chunks/5ZqlzR7j.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Bu0cBnLr.js +0 -1
- package/build/client/_app/immutable/chunks/Bu0cBnLr.js.br +0 -2
- package/build/client/_app/immutable/chunks/Bu0cBnLr.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CEPSqAnS.js +0 -1
- package/build/client/_app/immutable/chunks/CEPSqAnS.js.br +0 -0
- package/build/client/_app/immutable/chunks/CEPSqAnS.js.gz +0 -0
- package/build/client/_app/immutable/chunks/ltAtEuyp.js +0 -5
- package/build/client/_app/immutable/chunks/ltAtEuyp.js.br +0 -0
- package/build/client/_app/immutable/chunks/ltAtEuyp.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.jhM1F9Lz.js.br +0 -0
- package/build/client/_app/immutable/entry/app.jhM1F9Lz.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.Dc1B22-x.js +0 -1
- package/build/client/_app/immutable/entry/start.Dc1B22-x.js.br +0 -1
- package/build/client/_app/immutable/entry/start.Dc1B22-x.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.CAM0_CnR.js.br +0 -1
- package/build/client/_app/immutable/nodes/1.CAM0_CnR.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.BInZ_Tt5.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.e3eblL7D.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.e3eblL7D.js.gz +0 -0
- package/build/client/_app/immutable/nodes/6.D6wSITV7.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.D6wSITV7.js.gz +0 -0
- package/build/client/_app/immutable/nodes/7.C1W_OsSf.js.br +0 -0
- package/build/client/_app/immutable/nodes/8.9E6jSDDa.js.br +0 -0
- package/build/client/_app/immutable/nodes/8.9E6jSDDa.js.gz +0 -0
- package/build/client/_app/immutable/nodes/9.CutxJ3jN.js.br +0 -0
- package/build/client/_app/immutable/nodes/9.CutxJ3jN.js.gz +0 -0
- package/build/server/chunks/1-DGZd-zFK.js +0 -9
- package/build/server/chunks/4-D9UtUsJp.js +0 -9
- package/build/server/chunks/5-B_4lRh2I.js +0 -9
- package/build/server/chunks/7-Ce9mKDNS.js +0 -9
- package/build/server/chunks/Navbar-BHPXjl1A.js.map +0 -1
- package/build/server/chunks/_server.ts-5pMviGH3.js.map +0 -1
- package/build/server/chunks/_server.ts-C901uAGV.js.map +0 -1
- package/build/server/chunks/_server.ts-C90P5trG.js.map +0 -1
- package/build/server/chunks/setup-deploy-Cg9faOlI.js.map +0 -1
|
@@ -12263,975 +12263,975 @@ function removeTaskFile(stashDir, name) {
|
|
|
12263
12263
|
function isSetupComplete(stackDir) {
|
|
12264
12264
|
return parseEnvFile(stackEnvPathFromStackDir(stackDir)).OP_SETUP_COMPLETE === "true";
|
|
12265
12265
|
}
|
|
12266
|
-
|
|
12267
|
-
|
|
12266
|
+
/** Execute docker with an argument array — no shell interpolation. */
|
|
12267
|
+
function run$1(args, cwd, timeoutMs = 12e4, envOverrides) {
|
|
12268
|
+
return new Promise((resolve) => {
|
|
12269
|
+
execFile("docker", args, {
|
|
12270
|
+
cwd,
|
|
12271
|
+
timeout: timeoutMs,
|
|
12272
|
+
env: {
|
|
12273
|
+
...process.env,
|
|
12274
|
+
...envOverrides
|
|
12275
|
+
}
|
|
12276
|
+
}, (error, stdout, stderr) => {
|
|
12277
|
+
resolve({
|
|
12278
|
+
ok: !error,
|
|
12279
|
+
stdout: stdout?.toString() ?? "",
|
|
12280
|
+
stderr: stderr?.toString() ?? "",
|
|
12281
|
+
code: error?.code ? Number(error.code) : 0
|
|
12282
|
+
});
|
|
12283
|
+
});
|
|
12284
|
+
});
|
|
12285
|
+
}
|
|
12268
12286
|
/**
|
|
12269
|
-
*
|
|
12287
|
+
* Resolve the Docker Compose project name.
|
|
12288
|
+
* Honors OP_PROJECT_NAME first for OpenPalm stacks, then COMPOSE_PROJECT_NAME.
|
|
12270
12289
|
*/
|
|
12271
|
-
function
|
|
12272
|
-
|
|
12273
|
-
for (const name of [
|
|
12274
|
-
"channels.compose.yml",
|
|
12275
|
-
"services.compose.yml",
|
|
12276
|
-
"custom.compose.yml"
|
|
12277
|
-
]) {
|
|
12278
|
-
const composePath = `${homeDir}/config/stack/${name}`;
|
|
12279
|
-
if (existsSync(composePath)) paths.push(composePath);
|
|
12280
|
-
}
|
|
12281
|
-
return paths;
|
|
12290
|
+
function resolveComposeProjectName(envOverrides = {}) {
|
|
12291
|
+
return envOverrides.OP_PROJECT_NAME?.trim() || envOverrides.COMPOSE_PROJECT_NAME?.trim() || process.env.OP_PROJECT_NAME?.trim() || process.env.COMPOSE_PROJECT_NAME?.trim() || "openpalm";
|
|
12282
12292
|
}
|
|
12283
12293
|
/**
|
|
12284
|
-
*
|
|
12285
|
-
*
|
|
12286
|
-
*
|
|
12287
|
-
*
|
|
12294
|
+
* Decide whether a running compose project (identified by its
|
|
12295
|
+
* `com.docker.compose.project.working_dir` label) is OURS — i.e. was launched
|
|
12296
|
+
* from this install's working dir. An empty/unknown label can't prove foreign,
|
|
12297
|
+
* so it counts as ours (reconcile rather than wrongly refuse a redeploy).
|
|
12298
|
+
*
|
|
12299
|
+
* Pure decision split out from detectExistingProject so the ours-vs-foreign
|
|
12300
|
+
* rule is unit-testable without a Docker daemon.
|
|
12288
12301
|
*/
|
|
12289
|
-
function
|
|
12290
|
-
|
|
12291
|
-
|
|
12292
|
-
if (configDir) {
|
|
12293
|
-
const homeDir = dirname(configDir);
|
|
12294
|
-
for (const composePath of addonComposePaths(homeDir)) try {
|
|
12295
|
-
const doc = (0, import_dist.parse)(readFileSync(composePath, "utf-8"));
|
|
12296
|
-
if (typeof doc === "object" && doc !== null) {
|
|
12297
|
-
const services = doc.services;
|
|
12298
|
-
if (typeof services === "object" && services !== null && value in services) return true;
|
|
12299
|
-
}
|
|
12300
|
-
} catch {
|
|
12301
|
-
continue;
|
|
12302
|
-
}
|
|
12303
|
-
}
|
|
12304
|
-
return false;
|
|
12302
|
+
function isProjectOurs(workingDirLabel, expectedWorkingDir) {
|
|
12303
|
+
const label = workingDirLabel.trim();
|
|
12304
|
+
return label === "" || label === expectedWorkingDir;
|
|
12305
12305
|
}
|
|
12306
|
-
//#endregion
|
|
12307
|
-
//#region ../lib/src/control-plane/provider-models.ts
|
|
12308
12306
|
/**
|
|
12309
|
-
*
|
|
12307
|
+
* Probe the Docker daemon for a running compose project that shares
|
|
12308
|
+
* `projectName`. Decides ours-vs-foreign by comparing the project's
|
|
12309
|
+
* `com.docker.compose.project.working_dir` label against `expectedWorkingDir`
|
|
12310
|
+
* (the install's OP_HOME / compose context).
|
|
12310
12311
|
*
|
|
12311
|
-
*
|
|
12312
|
-
*
|
|
12312
|
+
* Returns `{ exists:false }` on any docker error (daemon down, no permission) —
|
|
12313
|
+
* detection is best-effort and never blocks the caller; a real failure surfaces
|
|
12314
|
+
* later through composeUp.
|
|
12313
12315
|
*/
|
|
12314
|
-
|
|
12315
|
-
|
|
12316
|
-
|
|
12317
|
-
|
|
12318
|
-
|
|
12319
|
-
|
|
12320
|
-
|
|
12321
|
-
|
|
12322
|
-
|
|
12323
|
-
|
|
12316
|
+
function detectExistingProject(opts) {
|
|
12317
|
+
const none = {
|
|
12318
|
+
exists: false,
|
|
12319
|
+
isOurs: false,
|
|
12320
|
+
workingDir: ""
|
|
12321
|
+
};
|
|
12322
|
+
return new Promise((resolve) => {
|
|
12323
|
+
execFile("docker", [
|
|
12324
|
+
"ps",
|
|
12325
|
+
"-q",
|
|
12326
|
+
"--filter",
|
|
12327
|
+
`label=com.docker.compose.project=${opts.projectName}`
|
|
12328
|
+
], { timeout: 1e4 }, (err, stdout) => {
|
|
12329
|
+
if (err) return resolve(none);
|
|
12330
|
+
const ids = stdout.toString().trim().split(/\s+/).filter(Boolean);
|
|
12331
|
+
if (ids.length === 0) return resolve(none);
|
|
12332
|
+
execFile("docker", [
|
|
12333
|
+
"inspect",
|
|
12334
|
+
"--format",
|
|
12335
|
+
"{{ index .Config.Labels \"com.docker.compose.project.working_dir\" }}",
|
|
12336
|
+
ids[0]
|
|
12337
|
+
], { timeout: 1e4 }, (err2, stdout2) => {
|
|
12338
|
+
if (err2) return resolve({
|
|
12339
|
+
exists: true,
|
|
12340
|
+
isOurs: false,
|
|
12341
|
+
workingDir: ""
|
|
12342
|
+
});
|
|
12343
|
+
const workingDir = stdout2.toString().trim();
|
|
12344
|
+
resolve({
|
|
12345
|
+
exists: true,
|
|
12346
|
+
isOurs: isProjectOurs(workingDir, opts.expectedWorkingDir),
|
|
12347
|
+
workingDir
|
|
12348
|
+
});
|
|
12349
|
+
});
|
|
12350
|
+
});
|
|
12351
|
+
});
|
|
12352
|
+
}
|
|
12353
|
+
/** Check if Docker is available */
|
|
12354
|
+
async function checkDocker() {
|
|
12355
|
+
return new Promise((resolve) => {
|
|
12356
|
+
execFile("docker", [
|
|
12357
|
+
"info",
|
|
12358
|
+
"--format",
|
|
12359
|
+
"{{.ServerVersion}}"
|
|
12360
|
+
], (error, stdout, stderr) => {
|
|
12361
|
+
const stdoutStr = stdout?.toString().trim() ?? "";
|
|
12362
|
+
const stderrStr = stderr?.toString() ?? "";
|
|
12363
|
+
resolve({
|
|
12364
|
+
ok: stdoutStr.length > 0 || !error,
|
|
12365
|
+
stdout: stdoutStr,
|
|
12366
|
+
stderr: stderrStr,
|
|
12367
|
+
code: error?.code ? Number(error.code) : 0
|
|
12368
|
+
});
|
|
12369
|
+
});
|
|
12370
|
+
});
|
|
12371
|
+
}
|
|
12372
|
+
/** Check if docker compose is available */
|
|
12373
|
+
async function checkDockerCompose() {
|
|
12374
|
+
return new Promise((resolve) => {
|
|
12375
|
+
execFile("docker", ["compose", "version"], (error, stdout, stderr) => {
|
|
12376
|
+
resolve({
|
|
12377
|
+
ok: !error,
|
|
12378
|
+
stdout: stdout?.toString() ?? "",
|
|
12379
|
+
stderr: stderr?.toString() ?? "",
|
|
12380
|
+
code: error?.code ? Number(error.code) : 0
|
|
12381
|
+
});
|
|
12382
|
+
});
|
|
12383
|
+
});
|
|
12384
|
+
}
|
|
12385
|
+
/** Build common prefix: compose -f ... --project-name ... --env-file ... --profile ... */
|
|
12386
|
+
function buildComposeArgs(options) {
|
|
12387
|
+
const envOverrides = collectEnvOverrides(options.envFiles);
|
|
12388
|
+
const args = [
|
|
12389
|
+
"compose",
|
|
12390
|
+
...options.files.flatMap((f) => ["-f", f]),
|
|
12391
|
+
"--project-name",
|
|
12392
|
+
resolveComposeProjectName(envOverrides)
|
|
12393
|
+
];
|
|
12394
|
+
for (const ef of options.envFiles ?? []) if (existsSync(ef)) args.push("--env-file", ef);
|
|
12395
|
+
for (const p of options.profiles ?? []) args.push("--profile", p);
|
|
12396
|
+
return args;
|
|
12397
|
+
}
|
|
12398
|
+
/** Merge all env files into a single overrides object for process env. */
|
|
12399
|
+
function collectEnvOverrides(envFiles) {
|
|
12400
|
+
const overrides = {};
|
|
12401
|
+
for (const ef of envFiles ?? []) Object.assign(overrides, parseEnvFile(ef));
|
|
12402
|
+
return overrides;
|
|
12403
|
+
}
|
|
12324
12404
|
/**
|
|
12325
|
-
*
|
|
12326
|
-
*
|
|
12327
|
-
* - Empty input → empty string.
|
|
12328
|
-
* - `env:NAME` form → looks up `NAME` in `process.env` first, then falls back
|
|
12329
|
-
* to `knowledge/secrets/<NAME>` resolved against `stackDir`.
|
|
12330
|
-
* - Anything else → returned verbatim (treated as a literal key value).
|
|
12405
|
+
* Run `docker compose config` to validate compose file merge and variable substitution.
|
|
12406
|
+
* Must be called before any lifecycle mutation (install/apply/update).
|
|
12331
12407
|
*/
|
|
12332
|
-
function
|
|
12333
|
-
|
|
12334
|
-
|
|
12335
|
-
|
|
12336
|
-
if (process.env[varName]) return process.env[varName];
|
|
12337
|
-
return readStackRuntimeEnv(stackDir)[varName] ?? "";
|
|
12408
|
+
async function composePreflight(options) {
|
|
12409
|
+
const args = buildComposeArgs(options);
|
|
12410
|
+
args.push("config", "--quiet");
|
|
12411
|
+
return run$1(args, void 0, 3e4, collectEnvOverrides(options.envFiles));
|
|
12338
12412
|
}
|
|
12339
|
-
var HTTP_STATUS_LABELS = {
|
|
12340
|
-
401: "Invalid or missing API key",
|
|
12341
|
-
403: "Access denied — check API key permissions",
|
|
12342
|
-
404: "Endpoint not found — verify the base URL",
|
|
12343
|
-
429: "Rate limited — try again shortly",
|
|
12344
|
-
500: "Provider internal error",
|
|
12345
|
-
502: "Provider returned a bad gateway error",
|
|
12346
|
-
503: "Provider is temporarily unavailable"
|
|
12347
|
-
};
|
|
12348
12413
|
/**
|
|
12349
|
-
*
|
|
12350
|
-
*
|
|
12351
|
-
* `recoverable_error` with a structured reason otherwise. Network and timeout
|
|
12352
|
-
* failures are caught and mapped to a result rather than thrown.
|
|
12414
|
+
* Run compose config preflight validation before any mutation.
|
|
12415
|
+
* Skipped when OP_SKIP_COMPOSE_PREFLIGHT is set (tests, CI).
|
|
12353
12416
|
*/
|
|
12354
|
-
async function
|
|
12355
|
-
|
|
12356
|
-
|
|
12357
|
-
|
|
12358
|
-
|
|
12359
|
-
|
|
12360
|
-
};
|
|
12361
|
-
const
|
|
12362
|
-
|
|
12363
|
-
const url = `${(baseUrl?.trim() || PROVIDER_DEFAULT_URLS.ollama).replace(/\/+$/, "")}/api/tags`;
|
|
12364
|
-
const res = await fetch(url, { signal: AbortSignal.timeout(5e3) });
|
|
12365
|
-
if (!res.ok) return {
|
|
12366
|
-
models: [],
|
|
12367
|
-
status: "recoverable_error",
|
|
12368
|
-
reason: "provider_http",
|
|
12369
|
-
error: `Ollama API returned ${res.status}: ${HTTP_STATUS_LABELS[res.status] ?? `HTTP ${res.status}`}`
|
|
12370
|
-
};
|
|
12371
|
-
return {
|
|
12372
|
-
models: ((await res.json()).models ?? []).map((m) => m.name).sort(),
|
|
12373
|
-
status: "ok",
|
|
12374
|
-
reason: "none"
|
|
12375
|
-
};
|
|
12376
|
-
}
|
|
12377
|
-
const base = baseUrl?.trim() || PROVIDER_DEFAULT_URLS[provider] || "";
|
|
12378
|
-
if (!base) return {
|
|
12379
|
-
models: [],
|
|
12380
|
-
status: "recoverable_error",
|
|
12381
|
-
reason: "missing_base_url",
|
|
12382
|
-
error: `No base URL configured for provider "${provider}"`
|
|
12383
|
-
};
|
|
12384
|
-
const url = `${base.replace(/\/+$/, "")}/v1/models`;
|
|
12385
|
-
const headers = {};
|
|
12386
|
-
if (resolvedKey) headers["Authorization"] = `Bearer ${resolvedKey}`;
|
|
12387
|
-
const res = await fetch(url, {
|
|
12388
|
-
headers,
|
|
12389
|
-
signal: AbortSignal.timeout(5e3)
|
|
12390
|
-
});
|
|
12391
|
-
if (!res.ok) {
|
|
12392
|
-
let detail = "";
|
|
12393
|
-
try {
|
|
12394
|
-
const json = JSON.parse(await res.text());
|
|
12395
|
-
const errObj = json.error;
|
|
12396
|
-
detail = typeof errObj === "object" && errObj !== null && typeof errObj.message === "string" ? errObj.message : typeof errObj === "string" ? errObj : typeof json.message === "string" ? json.message : typeof json.detail === "string" ? json.detail : "";
|
|
12397
|
-
} catch {}
|
|
12398
|
-
return {
|
|
12399
|
-
models: [],
|
|
12400
|
-
status: "recoverable_error",
|
|
12401
|
-
reason: "provider_http",
|
|
12402
|
-
error: detail ? `Provider API returned ${res.status}: ${detail}` : `Provider API returned ${res.status}: ${HTTP_STATUS_LABELS[res.status] ?? `HTTP ${res.status}`}`
|
|
12403
|
-
};
|
|
12404
|
-
}
|
|
12405
|
-
return {
|
|
12406
|
-
models: ((await res.json()).data ?? []).map((m) => m.id).sort(),
|
|
12407
|
-
status: "ok",
|
|
12408
|
-
reason: "none"
|
|
12409
|
-
};
|
|
12410
|
-
} catch (err) {
|
|
12411
|
-
const message = err instanceof Error && err.name === "TimeoutError" ? "Request timed out after 5s" : String(err);
|
|
12412
|
-
return {
|
|
12413
|
-
models: [],
|
|
12414
|
-
status: "recoverable_error",
|
|
12415
|
-
reason: err instanceof Error && err.name === "TimeoutError" ? "timeout" : "network",
|
|
12416
|
-
error: message
|
|
12417
|
-
};
|
|
12417
|
+
async function runPreflight(options) {
|
|
12418
|
+
if (options.files.length === 0 || process.env.OP_SKIP_COMPOSE_PREFLIGHT) return;
|
|
12419
|
+
const result = await composePreflight(options);
|
|
12420
|
+
if (!result.ok) {
|
|
12421
|
+
const project = resolveComposeProjectName(collectEnvOverrides(options.envFiles));
|
|
12422
|
+
const fileArgs = options.files.map((f) => `-f ${f}`).join(" ");
|
|
12423
|
+
const envArgs = (options.envFiles ?? []).map((f) => `--env-file ${f}`).join(" ");
|
|
12424
|
+
const profileArgs = (options.profiles ?? []).map((p) => `--profile ${p}`).join(" ");
|
|
12425
|
+
throw new Error(`Compose preflight failed: ${result.stderr}\nResolved command: docker compose ${fileArgs} --project-name ${project} ${envArgs} ${profileArgs} config --quiet`);
|
|
12418
12426
|
}
|
|
12419
12427
|
}
|
|
12420
|
-
|
|
12421
|
-
|
|
12428
|
+
async function composeConfigServices(options) {
|
|
12429
|
+
const args = buildComposeArgs(options);
|
|
12430
|
+
args.push("config", "--services");
|
|
12431
|
+
const result = await run$1(args, void 0, 3e4, collectEnvOverrides(options.envFiles));
|
|
12432
|
+
if (!result.ok) return {
|
|
12433
|
+
ok: false,
|
|
12434
|
+
services: []
|
|
12435
|
+
};
|
|
12436
|
+
return {
|
|
12437
|
+
ok: true,
|
|
12438
|
+
services: result.stdout.split("\n").map((s) => s.trim()).filter(Boolean)
|
|
12439
|
+
};
|
|
12440
|
+
}
|
|
12422
12441
|
/**
|
|
12423
|
-
*
|
|
12424
|
-
*
|
|
12425
|
-
* partially written file. `mode` (e.g. 0o600) is applied on creation.
|
|
12426
|
-
*
|
|
12427
|
-
* Shared by all control-plane writers (setup, akm-sources, …) so config and
|
|
12428
|
-
* secret files are written through one audited path — never hand-rolled.
|
|
12442
|
+
* Run `docker compose up -d` with the generated compose file(s).
|
|
12443
|
+
* Pass `files` to merge multiple compose overlays (e.g. core + addon files).
|
|
12429
12444
|
*/
|
|
12430
|
-
function
|
|
12431
|
-
|
|
12432
|
-
|
|
12433
|
-
|
|
12445
|
+
async function composeUp(options) {
|
|
12446
|
+
await runPreflight(options);
|
|
12447
|
+
if (!existsSync(options.files[0])) return {
|
|
12448
|
+
ok: false,
|
|
12449
|
+
stdout: "",
|
|
12450
|
+
stderr: "Compose file not found",
|
|
12451
|
+
code: 1
|
|
12452
|
+
};
|
|
12453
|
+
const args = buildComposeArgs(options);
|
|
12454
|
+
args.push("up", "-d");
|
|
12455
|
+
if (options.forceRecreate) args.push("--force-recreate");
|
|
12456
|
+
if (options.removeOrphans) args.push("--remove-orphans");
|
|
12457
|
+
if (options.services?.length) args.push(...options.services);
|
|
12458
|
+
return run$1(args, void 0, composeUpTimeoutMs(), collectEnvOverrides(options.envFiles));
|
|
12434
12459
|
}
|
|
12435
|
-
//#endregion
|
|
12436
|
-
//#region ../lib/src/control-plane/akm-sources.ts
|
|
12437
12460
|
/**
|
|
12438
|
-
*
|
|
12439
|
-
*
|
|
12440
|
-
*
|
|
12441
|
-
*
|
|
12442
|
-
*
|
|
12443
|
-
* Each akm instance keeps its OWN primary stash, data dir, and cache. Sharing
|
|
12444
|
-
* is done purely by adding the other instance's stash as a *secondary* source
|
|
12445
|
-
* in `config.sources[]`:
|
|
12446
|
-
* - The OpenPalm/container config gains a `host-akm` source → /host-stash
|
|
12447
|
-
* (the user's personal ~/akm, bind-mounted by the host-akm.compose.yml overlay).
|
|
12448
|
-
* - The personal config gains an `openpalm` source → OP_HOME/knowledge.
|
|
12449
|
-
*
|
|
12450
|
-
* VERIFIED against akm 0.8.0 (rc.13 → stable; schema unchanged):
|
|
12451
|
-
* - `SourceConfigEntrySchema` (config-schema.ts:259) accepts
|
|
12452
|
-
* { type, path?, url?, name?, enabled?, writable?, primary?, options?, wikiName? }.
|
|
12453
|
-
* - The indexer's `resolveSourceEntries` (search-source.ts:56) ALWAYS injects the
|
|
12454
|
-
* env-resolved primary stash (AKM_STASH_DIR) as sources[0], then appends
|
|
12455
|
-
* config.sources[] deduped by path. So a secondary entry can never strand or
|
|
12456
|
-
* displace the primary — provided we NEVER set `primary:true` and NEVER set
|
|
12457
|
-
* `config.stashDir`.
|
|
12458
|
-
* - Writes resolve to the primary unless an explicit `--target` is given
|
|
12459
|
-
* (write-source.ts) and `defaultWriteTarget` is left unset, so a writable
|
|
12460
|
-
* secondary is safe by construction.
|
|
12461
|
-
*
|
|
12462
|
-
* Invariants (enforced + unit-tested):
|
|
12463
|
-
* - Only ever appends/updates a NAMED source (idempotent upsert by name).
|
|
12464
|
-
* - NEVER sets `primary`, NEVER sets `defaultWriteTarget`, NEVER sets `stashDir`.
|
|
12465
|
-
* - Atomic 0600 writes.
|
|
12466
|
-
* - The OpenPalm config is parse-tolerant (we own it: corrupt → start from {}).
|
|
12467
|
-
* - The PERSONAL config FAILS CLOSED (corrupt/unreadable → throw, never overwrite
|
|
12468
|
-
* the user's file). This asymmetry is the host-data-loss guard.
|
|
12461
|
+
* Timeout budget for `compose up`. A first install extracts multi-GB images
|
|
12462
|
+
* (voice CUDA ~7.6 GB) onto slow disks; the previous hard 5-minute cap
|
|
12463
|
+
* SIGTERM-killed the start mid-extraction and surfaced as an empty/opaque
|
|
12464
|
+
* error. Default 30 min, override with OP_COMPOSE_UP_TIMEOUT_MS. Kept bounded
|
|
12465
|
+
* (never removed) so a genuinely hung start still eventually fails.
|
|
12469
12466
|
*/
|
|
12470
|
-
|
|
12471
|
-
|
|
12472
|
-
|
|
12473
|
-
if (
|
|
12474
|
-
|
|
12475
|
-
const parsed = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
12476
|
-
return parsed && typeof parsed === "object" ? parsed : {};
|
|
12477
|
-
} catch {
|
|
12478
|
-
return {};
|
|
12479
|
-
}
|
|
12480
|
-
}
|
|
12481
|
-
function readConfigFailClosed(configPath) {
|
|
12482
|
-
if (!existsSync(configPath)) throw new Error(`Personal akm config not found at ${configPath}; refusing to create it. Run \`akm init\`/\`akm setup\` first, then enable host AKM sharing.`);
|
|
12483
|
-
let text;
|
|
12484
|
-
try {
|
|
12485
|
-
text = readFileSync(configPath, "utf-8");
|
|
12486
|
-
} catch (err) {
|
|
12487
|
-
throw new Error(`Unable to read personal akm config at ${configPath}: ${err.message}`);
|
|
12488
|
-
}
|
|
12489
|
-
let parsed;
|
|
12490
|
-
try {
|
|
12491
|
-
parsed = JSON.parse(text);
|
|
12492
|
-
} catch {
|
|
12493
|
-
throw new Error(`Personal akm config at ${configPath} is not valid JSON; refusing to overwrite it. Fix the file by hand, then retry.`);
|
|
12494
|
-
}
|
|
12495
|
-
if (!parsed || typeof parsed !== "object") throw new Error(`Personal akm config at ${configPath} is not a JSON object; refusing to overwrite it.`);
|
|
12496
|
-
return parsed;
|
|
12467
|
+
function composeUpTimeoutMs() {
|
|
12468
|
+
const raw = process.env.OP_COMPOSE_UP_TIMEOUT_MS?.trim();
|
|
12469
|
+
const parsed = raw ? Number(raw) : NaN;
|
|
12470
|
+
if (Number.isFinite(parsed) && parsed > 0) return parsed;
|
|
12471
|
+
return 30 * 6e4;
|
|
12497
12472
|
}
|
|
12498
12473
|
/**
|
|
12499
|
-
*
|
|
12500
|
-
* NEVER touches `primary`, `defaultWriteTarget`, or `stashDir`.
|
|
12474
|
+
* Run `docker compose down` to stop and remove containers.
|
|
12501
12475
|
*/
|
|
12502
|
-
function
|
|
12503
|
-
|
|
12504
|
-
|
|
12505
|
-
|
|
12506
|
-
|
|
12507
|
-
|
|
12508
|
-
|
|
12509
|
-
else sources.push(entry);
|
|
12510
|
-
return {
|
|
12511
|
-
...config,
|
|
12512
|
-
sources
|
|
12476
|
+
async function composeDown(options) {
|
|
12477
|
+
await runPreflight(options);
|
|
12478
|
+
if (!existsSync(options.files[0])) return {
|
|
12479
|
+
ok: false,
|
|
12480
|
+
stdout: "",
|
|
12481
|
+
stderr: "Compose file not found",
|
|
12482
|
+
code: 1
|
|
12513
12483
|
};
|
|
12484
|
+
const args = buildComposeArgs(options);
|
|
12485
|
+
args.push("down");
|
|
12486
|
+
if (options.removeVolumes) args.push("-v");
|
|
12487
|
+
if (options.removeOrphans) args.push("--remove-orphans");
|
|
12488
|
+
return run$1(args, void 0);
|
|
12514
12489
|
}
|
|
12515
|
-
|
|
12516
|
-
|
|
12517
|
-
|
|
12518
|
-
|
|
12519
|
-
|
|
12520
|
-
|
|
12490
|
+
/**
|
|
12491
|
+
* Restart specific services.
|
|
12492
|
+
*/
|
|
12493
|
+
async function composeRestart(services, options) {
|
|
12494
|
+
await runPreflight(options);
|
|
12495
|
+
const primaryFile = options.files[0];
|
|
12496
|
+
if (!existsSync(primaryFile)) return {
|
|
12497
|
+
ok: false,
|
|
12498
|
+
stdout: "",
|
|
12499
|
+
stderr: "Compose file not found",
|
|
12500
|
+
code: 1
|
|
12521
12501
|
};
|
|
12502
|
+
const args = buildComposeArgs(options);
|
|
12503
|
+
args.push("restart", ...services);
|
|
12504
|
+
return run$1(args, void 0);
|
|
12522
12505
|
}
|
|
12523
|
-
|
|
12524
|
-
|
|
12525
|
-
|
|
12526
|
-
function
|
|
12527
|
-
|
|
12506
|
+
/**
|
|
12507
|
+
* Stop specific services.
|
|
12508
|
+
*/
|
|
12509
|
+
async function composeStop(services, options) {
|
|
12510
|
+
await runPreflight(options);
|
|
12511
|
+
const args = buildComposeArgs(options);
|
|
12512
|
+
args.push("stop", ...services);
|
|
12513
|
+
return run$1(args, void 0);
|
|
12528
12514
|
}
|
|
12529
12515
|
/**
|
|
12530
|
-
*
|
|
12531
|
-
* secondary source. Parse-tolerant (we own this config). Writable by default so
|
|
12532
|
-
* the assistant can contribute back via an explicit `--target host-akm`.
|
|
12516
|
+
* Start specific services (must already be created).
|
|
12533
12517
|
*/
|
|
12534
|
-
function
|
|
12535
|
-
|
|
12536
|
-
const
|
|
12537
|
-
|
|
12538
|
-
|
|
12539
|
-
name: HOST_SOURCE_NAME,
|
|
12540
|
-
writable,
|
|
12541
|
-
enabled: true
|
|
12542
|
-
};
|
|
12543
|
-
assertNoPrimaryEscalation(entry);
|
|
12544
|
-
const updated = upsertSource(readConfigTolerant(configPath), entry);
|
|
12545
|
-
writeFileAtomic(configPath, JSON.stringify(updated, null, 2), 384);
|
|
12518
|
+
async function composeStart(services, options) {
|
|
12519
|
+
await runPreflight(options);
|
|
12520
|
+
const args = buildComposeArgs(options);
|
|
12521
|
+
args.push("up", "-d", ...services);
|
|
12522
|
+
return run$1(args, void 0);
|
|
12546
12523
|
}
|
|
12547
12524
|
/**
|
|
12548
|
-
*
|
|
12549
|
-
* sharing). Parse-tolerant; never touches the user's personal config (D1 —
|
|
12550
|
-
* host sharing is assistant-reads-host only). Idempotent.
|
|
12525
|
+
* Get the status of all containers in the project.
|
|
12551
12526
|
*/
|
|
12552
|
-
function
|
|
12553
|
-
const
|
|
12554
|
-
|
|
12555
|
-
|
|
12527
|
+
async function composePs(options) {
|
|
12528
|
+
const primaryFile = options.files[0];
|
|
12529
|
+
if (!existsSync(primaryFile)) return run$1([
|
|
12530
|
+
"ps",
|
|
12531
|
+
"--filter",
|
|
12532
|
+
`label=com.docker.compose.project=${resolveComposeProjectName()}`,
|
|
12533
|
+
"--format",
|
|
12534
|
+
"json"
|
|
12535
|
+
], void 0);
|
|
12536
|
+
const args = buildComposeArgs(options);
|
|
12537
|
+
args.push("ps", "--format", "json");
|
|
12538
|
+
return run$1(args, void 0);
|
|
12556
12539
|
}
|
|
12557
12540
|
/**
|
|
12558
|
-
*
|
|
12559
|
-
* config. Reads the personal config READ-ONLY; never writes back to the host.
|
|
12560
|
-
*
|
|
12561
|
-
* ADDITIVE MERGE: existing OpenPalm values ALWAYS win — the host only fills gaps.
|
|
12562
|
-
* A profile name, default selection, or embedding field that OpenPalm already has
|
|
12563
|
-
* is never overwritten; host-only profiles/fields are added. Covers the
|
|
12564
|
-
* LLM/agent/improve PROFILES (+ their `defaults.*`) and the top-level `embedding`
|
|
12565
|
-
* connection. Returns which sections actually gained values.
|
|
12566
|
-
*
|
|
12567
|
-
* Writes the canonical akm 0.8.0 shape (profiles.* + defaults.* + embedding) —
|
|
12568
|
-
* never the legacy top-level `llm` (see I-3). NEVER touches `sources`, `stashDir`,
|
|
12569
|
-
* `registries`, or `installed`.
|
|
12541
|
+
* Get logs for specific services or all services.
|
|
12570
12542
|
*/
|
|
12571
|
-
function
|
|
12572
|
-
const
|
|
12573
|
-
|
|
12574
|
-
|
|
12575
|
-
|
|
12576
|
-
|
|
12577
|
-
|
|
12578
|
-
|
|
12579
|
-
|
|
12580
|
-
|
|
12581
|
-
|
|
12582
|
-
|
|
12583
|
-
|
|
12584
|
-
"improve"
|
|
12585
|
-
]) {
|
|
12586
|
-
if (isObj(hostProfiles[ns])) {
|
|
12587
|
-
const existing = isObj(opProfiles[ns]) ? opProfiles[ns] : {};
|
|
12588
|
-
const merged = {
|
|
12589
|
-
...hostProfiles[ns],
|
|
12590
|
-
...existing
|
|
12591
|
-
};
|
|
12592
|
-
const added = Object.keys(merged).length - Object.keys(existing).length;
|
|
12593
|
-
opProfiles[ns] = merged;
|
|
12594
|
-
if (added > 0) imported.push(`profiles.${ns}`);
|
|
12595
|
-
}
|
|
12596
|
-
if (typeof hostDefaults[ns] === "string" && typeof opDefaults[ns] !== "string") {
|
|
12597
|
-
opDefaults[ns] = hostDefaults[ns];
|
|
12598
|
-
imported.push(`defaults.${ns}`);
|
|
12599
|
-
}
|
|
12600
|
-
}
|
|
12601
|
-
let embedding;
|
|
12602
|
-
if (isObj(host.embedding)) {
|
|
12603
|
-
const existing = isObj(op.embedding) ? op.embedding : {};
|
|
12604
|
-
const merged = {
|
|
12605
|
-
...host.embedding,
|
|
12606
|
-
...existing
|
|
12607
|
-
};
|
|
12608
|
-
if (Object.keys(merged).length > Object.keys(existing).length) {
|
|
12609
|
-
embedding = merged;
|
|
12610
|
-
imported.push("embedding");
|
|
12611
|
-
}
|
|
12612
|
-
}
|
|
12613
|
-
if (imported.length === 0) return { imported };
|
|
12614
|
-
const updated = {
|
|
12615
|
-
...op,
|
|
12616
|
-
profiles: opProfiles,
|
|
12617
|
-
defaults: opDefaults
|
|
12618
|
-
};
|
|
12619
|
-
if (embedding !== void 0) updated.embedding = embedding;
|
|
12620
|
-
delete updated.llm;
|
|
12621
|
-
writeFileAtomic(opPath, JSON.stringify(updated, null, 2), 384);
|
|
12622
|
-
return { imported };
|
|
12543
|
+
async function composeLogs(services, tail, options) {
|
|
12544
|
+
const args = buildComposeArgs(options);
|
|
12545
|
+
args.push("logs", "--tail", String(tail));
|
|
12546
|
+
if (options.since) args.push("--since", options.since);
|
|
12547
|
+
if (services && services.length > 0) args.push(...services);
|
|
12548
|
+
return run$1(args, void 0);
|
|
12549
|
+
}
|
|
12550
|
+
var PULL_TIMEOUT_MS = 60 * 6e4;
|
|
12551
|
+
async function composePull(options) {
|
|
12552
|
+
await runPreflight(options);
|
|
12553
|
+
const args = buildComposeArgs(options);
|
|
12554
|
+
args.push("pull");
|
|
12555
|
+
return run$1(args, void 0, PULL_TIMEOUT_MS, collectEnvOverrides(options.envFiles));
|
|
12623
12556
|
}
|
|
12624
|
-
//#endregion
|
|
12625
|
-
//#region ../lib/src/control-plane/host-akm-sharing.ts
|
|
12626
12557
|
/**
|
|
12627
|
-
*
|
|
12628
|
-
*
|
|
12629
|
-
* Simplified model (no compose overlay, no file-presence gating):
|
|
12630
|
-
*
|
|
12631
|
-
* - The assistant ALWAYS mounts `/host-stash` (core.compose.yml). When the host
|
|
12632
|
-
* has AKM, OP_HOST_AKM_STASH points at the user's personal stash (~/akm);
|
|
12633
|
-
* otherwise it is unset and compose falls back to an always-present empty dir.
|
|
12634
|
-
* - "Sharing" is purely a writable SECONDARY source entry named `host-akm` →
|
|
12635
|
-
* /host-stash in the assistant's config/akm/config.json. Adding it = enabled;
|
|
12636
|
-
* removing it = disabled. akm resolves writes to the primary unless an explicit
|
|
12637
|
-
* --target is given, and silently skips a source whose dir is empty/missing —
|
|
12638
|
-
* so a mounted-but-unconfigured /host-stash is harmless.
|
|
12639
|
-
* - Host availability is detected from the presence of the user's personal akm
|
|
12640
|
-
* CONFIG (~/.config/akm/config.json) — the real signal that akm is initialized.
|
|
12641
|
-
*
|
|
12642
|
-
* Decision D1 (2026-06-03): host sharing is assistant-reads-host ONLY by default.
|
|
12643
|
-
* We never write into the user's personal ~/.config/akm here. (Letting the host
|
|
12644
|
-
* akm see OpenPalm's knowledge is a future, explicit opt-in.)
|
|
12558
|
+
* Get resource usage stats for all containers in the project.
|
|
12645
12559
|
*/
|
|
12646
|
-
|
|
12647
|
-
|
|
12648
|
-
|
|
12649
|
-
return
|
|
12650
|
-
}
|
|
12651
|
-
/** The user's personal akm stash dir (mounted into the assistant at /host-stash). */
|
|
12652
|
-
function hostAkmStashPath() {
|
|
12653
|
-
return `${userHome()}/akm`;
|
|
12654
|
-
}
|
|
12655
|
-
/** The user's personal akm config file — its existence is our availability signal. */
|
|
12656
|
-
function hostAkmConfigPath() {
|
|
12657
|
-
return `${userHome()}/.config/akm/config.json`;
|
|
12658
|
-
}
|
|
12659
|
-
/** True when AKM is initialized on the host (personal config exists). */
|
|
12660
|
-
function isHostAkmAvailable() {
|
|
12661
|
-
return existsSync(hostAkmConfigPath());
|
|
12662
|
-
}
|
|
12663
|
-
function stackEnvPath(state) {
|
|
12664
|
-
return `${state.stashDir}/env/stack.env`;
|
|
12560
|
+
async function composeStats(options) {
|
|
12561
|
+
const args = buildComposeArgs(options);
|
|
12562
|
+
args.push("stats", "--no-stream", "--format", "json");
|
|
12563
|
+
return run$1(args, void 0);
|
|
12665
12564
|
}
|
|
12666
12565
|
/**
|
|
12667
|
-
*
|
|
12668
|
-
* (compose then uses the empty-dir fallback). Pure infrastructure — does NOT
|
|
12669
|
-
* change the source list. Idempotent; safe to call on setup and on deploy.
|
|
12566
|
+
* Get recent Docker events for the compose project.
|
|
12670
12567
|
*/
|
|
12671
|
-
function
|
|
12672
|
-
|
|
12673
|
-
|
|
12674
|
-
|
|
12675
|
-
|
|
12568
|
+
async function getDockerEvents(projectName, since = "1h") {
|
|
12569
|
+
return run$1([
|
|
12570
|
+
"events",
|
|
12571
|
+
"--filter",
|
|
12572
|
+
`label=com.docker.compose.project=${projectName}`,
|
|
12573
|
+
"--since",
|
|
12574
|
+
since,
|
|
12575
|
+
"--until",
|
|
12576
|
+
"now",
|
|
12577
|
+
"--format",
|
|
12578
|
+
"json"
|
|
12579
|
+
], void 0, 15e3);
|
|
12676
12580
|
}
|
|
12581
|
+
//#endregion
|
|
12582
|
+
//#region ../lib/src/control-plane/channels.ts
|
|
12677
12583
|
/**
|
|
12678
|
-
*
|
|
12679
|
-
* writable `host-akm` secondary source to the assistant config. Optionally import
|
|
12680
|
-
* host LLM/agent profiles (read-only). Throws if host AKM is not available.
|
|
12584
|
+
* Channel validation, discovery, and allowlist checks for the OpenPalm control plane.
|
|
12681
12585
|
*/
|
|
12682
|
-
function
|
|
12683
|
-
|
|
12684
|
-
|
|
12685
|
-
|
|
12686
|
-
|
|
12687
|
-
|
|
12688
|
-
|
|
12689
|
-
|
|
12690
|
-
|
|
12691
|
-
}
|
|
12692
|
-
return
|
|
12586
|
+
function addonComposePaths(homeDir) {
|
|
12587
|
+
const paths = [];
|
|
12588
|
+
for (const name of [
|
|
12589
|
+
"channels.compose.yml",
|
|
12590
|
+
"services.compose.yml",
|
|
12591
|
+
"custom.compose.yml"
|
|
12592
|
+
]) {
|
|
12593
|
+
const composePath = `${homeDir}/config/stack/${name}`;
|
|
12594
|
+
if (existsSync(composePath)) paths.push(composePath);
|
|
12595
|
+
}
|
|
12596
|
+
return paths;
|
|
12693
12597
|
}
|
|
12694
12598
|
/**
|
|
12695
|
-
*
|
|
12696
|
-
*
|
|
12697
|
-
* any
|
|
12599
|
+
* Check if a service name is allowed. Core services are always allowed.
|
|
12600
|
+
* Addon services are allowed if they appear as a compose service defined in
|
|
12601
|
+
* any active addon compose file. This is compose-derived: the actual compose
|
|
12602
|
+
* content is checked, not directory naming conventions.
|
|
12698
12603
|
*/
|
|
12699
|
-
function
|
|
12700
|
-
|
|
12701
|
-
|
|
12702
|
-
|
|
12703
|
-
|
|
12704
|
-
|
|
12705
|
-
|
|
12706
|
-
|
|
12707
|
-
|
|
12708
|
-
|
|
12709
|
-
|
|
12710
|
-
|
|
12711
|
-
|
|
12712
|
-
|
|
12713
|
-
const path = `${state.configDir}/akm/config.json`;
|
|
12714
|
-
if (!existsSync(path)) return false;
|
|
12715
|
-
try {
|
|
12716
|
-
const cfg = JSON.parse(readFileSync(path, "utf-8"));
|
|
12717
|
-
return Array.isArray(cfg.sources) && cfg.sources.some((s) => s?.name === "host-akm");
|
|
12718
|
-
} catch {
|
|
12719
|
-
return false;
|
|
12604
|
+
function isAllowedService(value, configDir) {
|
|
12605
|
+
if (!value || !value.trim() || value !== value.toLowerCase()) return false;
|
|
12606
|
+
if (CORE_SERVICES.includes(value)) return true;
|
|
12607
|
+
if (configDir) {
|
|
12608
|
+
const homeDir = dirname(configDir);
|
|
12609
|
+
for (const composePath of addonComposePaths(homeDir)) try {
|
|
12610
|
+
const doc = (0, import_dist.parse)(readFileSync(composePath, "utf-8"));
|
|
12611
|
+
if (typeof doc === "object" && doc !== null) {
|
|
12612
|
+
const services = doc.services;
|
|
12613
|
+
if (typeof services === "object" && services !== null && value in services) return true;
|
|
12614
|
+
}
|
|
12615
|
+
} catch {
|
|
12616
|
+
continue;
|
|
12617
|
+
}
|
|
12720
12618
|
}
|
|
12619
|
+
return false;
|
|
12721
12620
|
}
|
|
12722
12621
|
//#endregion
|
|
12723
|
-
//#region ../lib/src/control-plane/
|
|
12622
|
+
//#region ../lib/src/control-plane/provider-models.ts
|
|
12724
12623
|
/**
|
|
12725
|
-
*
|
|
12624
|
+
* Provider model discovery and API key resolution.
|
|
12726
12625
|
*
|
|
12727
|
-
*
|
|
12728
|
-
*
|
|
12729
|
-
* (or manual `openpalm rollback`), the snapshot is restored.
|
|
12626
|
+
* Used by the admin capabilities test endpoint and the CLI setup wizard
|
|
12627
|
+
* to enumerate the models a configured provider exposes.
|
|
12730
12628
|
*/
|
|
12731
|
-
/**
|
|
12732
|
-
|
|
12733
|
-
|
|
12734
|
-
|
|
12735
|
-
"
|
|
12736
|
-
"
|
|
12737
|
-
"
|
|
12738
|
-
"
|
|
12739
|
-
"
|
|
12629
|
+
/** Static model list for Anthropic (no listing API available). */
|
|
12630
|
+
var ANTHROPIC_MODELS = [
|
|
12631
|
+
"claude-opus-4-6",
|
|
12632
|
+
"claude-sonnet-4-6",
|
|
12633
|
+
"claude-opus-4-20250514",
|
|
12634
|
+
"claude-sonnet-4-20250514",
|
|
12635
|
+
"claude-haiku-4-5-20251001",
|
|
12636
|
+
"claude-3-5-sonnet-20241022",
|
|
12637
|
+
"claude-3-5-haiku-20241022"
|
|
12740
12638
|
];
|
|
12741
12639
|
/**
|
|
12742
|
-
*
|
|
12640
|
+
* Resolve an API key reference.
|
|
12641
|
+
*
|
|
12642
|
+
* - Empty input → empty string.
|
|
12643
|
+
* - `env:NAME` form → looks up `NAME` in `process.env` first, then falls back
|
|
12644
|
+
* to `knowledge/secrets/<NAME>` resolved against `stackDir`.
|
|
12645
|
+
* - Anything else → returned verbatim (treated as a literal key value).
|
|
12743
12646
|
*/
|
|
12744
|
-
function
|
|
12745
|
-
if (!
|
|
12746
|
-
|
|
12747
|
-
|
|
12647
|
+
function resolveApiKey(apiKeyRef, stackDir) {
|
|
12648
|
+
if (!apiKeyRef) return "";
|
|
12649
|
+
if (!apiKeyRef.startsWith("env:")) return apiKeyRef;
|
|
12650
|
+
const varName = apiKeyRef.slice(4);
|
|
12651
|
+
if (process.env[varName]) return process.env[varName];
|
|
12652
|
+
return readStackRuntimeEnv(stackDir)[varName] ?? "";
|
|
12748
12653
|
}
|
|
12654
|
+
var HTTP_STATUS_LABELS = {
|
|
12655
|
+
401: "Invalid or missing API key",
|
|
12656
|
+
403: "Access denied — check API key permissions",
|
|
12657
|
+
404: "Endpoint not found — verify the base URL",
|
|
12658
|
+
429: "Rate limited — try again shortly",
|
|
12659
|
+
500: "Provider internal error",
|
|
12660
|
+
502: "Provider returned a bad gateway error",
|
|
12661
|
+
503: "Provider is temporarily unavailable"
|
|
12662
|
+
};
|
|
12749
12663
|
/**
|
|
12750
|
-
*
|
|
12751
|
-
*
|
|
12664
|
+
* Enumerate available models for a provider. Returns an `ok` result with a
|
|
12665
|
+
* sorted model list when the provider responds successfully, or a
|
|
12666
|
+
* `recoverable_error` with a structured reason otherwise. Network and timeout
|
|
12667
|
+
* failures are caught and mapped to a result rather than thrown.
|
|
12752
12668
|
*/
|
|
12753
|
-
function
|
|
12754
|
-
|
|
12755
|
-
|
|
12756
|
-
|
|
12757
|
-
|
|
12758
|
-
|
|
12759
|
-
}
|
|
12760
|
-
|
|
12761
|
-
|
|
12762
|
-
|
|
12763
|
-
|
|
12764
|
-
|
|
12765
|
-
|
|
12766
|
-
|
|
12767
|
-
|
|
12768
|
-
|
|
12769
|
-
|
|
12770
|
-
|
|
12771
|
-
|
|
12772
|
-
|
|
12773
|
-
|
|
12774
|
-
|
|
12775
|
-
|
|
12776
|
-
|
|
12777
|
-
|
|
12778
|
-
|
|
12779
|
-
|
|
12780
|
-
|
|
12781
|
-
|
|
12782
|
-
|
|
12783
|
-
|
|
12784
|
-
|
|
12785
|
-
|
|
12786
|
-
|
|
12787
|
-
|
|
12788
|
-
|
|
12789
|
-
secretKey: "openpalm/mistral/api-key",
|
|
12790
|
-
envKey: "MISTRAL_API_KEY",
|
|
12791
|
-
scope: "user"
|
|
12792
|
-
},
|
|
12793
|
-
{
|
|
12794
|
-
secretKey: "openpalm/google/api-key",
|
|
12795
|
-
envKey: "GOOGLE_API_KEY",
|
|
12796
|
-
scope: "user"
|
|
12797
|
-
},
|
|
12798
|
-
{
|
|
12799
|
-
secretKey: "openpalm/together/api-key",
|
|
12800
|
-
envKey: "TOGETHER_API_KEY",
|
|
12801
|
-
scope: "user"
|
|
12802
|
-
},
|
|
12803
|
-
{
|
|
12804
|
-
secretKey: "openpalm/deepseek/api-key",
|
|
12805
|
-
envKey: "DEEPSEEK_API_KEY",
|
|
12806
|
-
scope: "user"
|
|
12807
|
-
},
|
|
12808
|
-
{
|
|
12809
|
-
secretKey: "openpalm/xai/api-key",
|
|
12810
|
-
envKey: "XAI_API_KEY",
|
|
12811
|
-
scope: "user"
|
|
12812
|
-
},
|
|
12813
|
-
{
|
|
12814
|
-
secretKey: "openpalm/huggingface/token",
|
|
12815
|
-
envKey: "HF_TOKEN",
|
|
12816
|
-
scope: "user"
|
|
12817
|
-
},
|
|
12818
|
-
{
|
|
12819
|
-
secretKey: "openpalm/mcp/api-key",
|
|
12820
|
-
envKey: "MCP_API_KEY",
|
|
12821
|
-
scope: "user"
|
|
12822
|
-
},
|
|
12823
|
-
{
|
|
12824
|
-
secretKey: "openpalm/embedding/api-key",
|
|
12825
|
-
envKey: "EMBEDDING_API_KEY",
|
|
12826
|
-
scope: "user"
|
|
12827
|
-
},
|
|
12828
|
-
{
|
|
12829
|
-
secretKey: "openpalm/lmstudio/api-key",
|
|
12830
|
-
envKey: "LMSTUDIO_API_KEY",
|
|
12831
|
-
scope: "user"
|
|
12832
|
-
},
|
|
12833
|
-
{
|
|
12834
|
-
secretKey: "openpalm/discord/bot-token",
|
|
12835
|
-
envKey: "DISCORD_BOT_TOKEN",
|
|
12836
|
-
scope: "user"
|
|
12837
|
-
},
|
|
12838
|
-
{
|
|
12839
|
-
secretKey: "openpalm/slack/bot-token",
|
|
12840
|
-
envKey: "SLACK_BOT_TOKEN",
|
|
12841
|
-
scope: "user"
|
|
12842
|
-
},
|
|
12843
|
-
{
|
|
12844
|
-
secretKey: "openpalm/slack/app-token",
|
|
12845
|
-
envKey: "SLACK_APP_TOKEN",
|
|
12846
|
-
scope: "user"
|
|
12847
|
-
},
|
|
12848
|
-
{
|
|
12849
|
-
secretKey: "openpalm/voice/stt-api-key",
|
|
12850
|
-
envKey: "STT_API_KEY",
|
|
12851
|
-
scope: "user"
|
|
12852
|
-
},
|
|
12853
|
-
{
|
|
12854
|
-
secretKey: "openpalm/voice/tts-api-key",
|
|
12855
|
-
envKey: "TTS_API_KEY",
|
|
12856
|
-
scope: "user"
|
|
12857
|
-
}
|
|
12858
|
-
];
|
|
12859
|
-
function getCoreSecretMappings(systemEnv) {
|
|
12860
|
-
const dynamicMappings = [];
|
|
12861
|
-
for (const envKey of Object.keys(systemEnv)) {
|
|
12862
|
-
const match = envKey.match(/^CHANNEL_([A-Z0-9_]+)_SECRET$/);
|
|
12863
|
-
if (!match?.[1]) continue;
|
|
12864
|
-
dynamicMappings.push({
|
|
12865
|
-
secretKey: `openpalm/channel/${match[1].toLowerCase()}/secret`,
|
|
12866
|
-
envKey,
|
|
12867
|
-
scope: "system"
|
|
12669
|
+
async function fetchProviderModels(provider, apiKeyRef, baseUrl, stackDir) {
|
|
12670
|
+
try {
|
|
12671
|
+
if (provider === "anthropic") return {
|
|
12672
|
+
models: [...ANTHROPIC_MODELS],
|
|
12673
|
+
status: "ok",
|
|
12674
|
+
reason: "provider_static"
|
|
12675
|
+
};
|
|
12676
|
+
const resolvedKey = resolveApiKey(apiKeyRef, stackDir);
|
|
12677
|
+
if (provider === "ollama") {
|
|
12678
|
+
const url = `${(baseUrl?.trim() || PROVIDER_DEFAULT_URLS.ollama).replace(/\/+$/, "")}/api/tags`;
|
|
12679
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(5e3) });
|
|
12680
|
+
if (!res.ok) return {
|
|
12681
|
+
models: [],
|
|
12682
|
+
status: "recoverable_error",
|
|
12683
|
+
reason: "provider_http",
|
|
12684
|
+
error: `Ollama API returned ${res.status}: ${HTTP_STATUS_LABELS[res.status] ?? `HTTP ${res.status}`}`
|
|
12685
|
+
};
|
|
12686
|
+
return {
|
|
12687
|
+
models: ((await res.json()).models ?? []).map((m) => m.name).sort(),
|
|
12688
|
+
status: "ok",
|
|
12689
|
+
reason: "none"
|
|
12690
|
+
};
|
|
12691
|
+
}
|
|
12692
|
+
const base = baseUrl?.trim() || PROVIDER_DEFAULT_URLS[provider] || "";
|
|
12693
|
+
if (!base) return {
|
|
12694
|
+
models: [],
|
|
12695
|
+
status: "recoverable_error",
|
|
12696
|
+
reason: "missing_base_url",
|
|
12697
|
+
error: `No base URL configured for provider "${provider}"`
|
|
12698
|
+
};
|
|
12699
|
+
const url = `${base.replace(/\/+$/, "")}/v1/models`;
|
|
12700
|
+
const headers = {};
|
|
12701
|
+
if (resolvedKey) headers["Authorization"] = `Bearer ${resolvedKey}`;
|
|
12702
|
+
const res = await fetch(url, {
|
|
12703
|
+
headers,
|
|
12704
|
+
signal: AbortSignal.timeout(5e3)
|
|
12868
12705
|
});
|
|
12706
|
+
if (!res.ok) {
|
|
12707
|
+
let detail = "";
|
|
12708
|
+
try {
|
|
12709
|
+
const json = JSON.parse(await res.text());
|
|
12710
|
+
const errObj = json.error;
|
|
12711
|
+
detail = typeof errObj === "object" && errObj !== null && typeof errObj.message === "string" ? errObj.message : typeof errObj === "string" ? errObj : typeof json.message === "string" ? json.message : typeof json.detail === "string" ? json.detail : "";
|
|
12712
|
+
} catch {}
|
|
12713
|
+
return {
|
|
12714
|
+
models: [],
|
|
12715
|
+
status: "recoverable_error",
|
|
12716
|
+
reason: "provider_http",
|
|
12717
|
+
error: detail ? `Provider API returned ${res.status}: ${detail}` : `Provider API returned ${res.status}: ${HTTP_STATUS_LABELS[res.status] ?? `HTTP ${res.status}`}`
|
|
12718
|
+
};
|
|
12719
|
+
}
|
|
12720
|
+
return {
|
|
12721
|
+
models: ((await res.json()).data ?? []).map((m) => m.id).sort(),
|
|
12722
|
+
status: "ok",
|
|
12723
|
+
reason: "none"
|
|
12724
|
+
};
|
|
12725
|
+
} catch (err) {
|
|
12726
|
+
const message = err instanceof Error && err.name === "TimeoutError" ? "Request timed out after 5s" : String(err);
|
|
12727
|
+
return {
|
|
12728
|
+
models: [],
|
|
12729
|
+
status: "recoverable_error",
|
|
12730
|
+
reason: err instanceof Error && err.name === "TimeoutError" ? "timeout" : "network",
|
|
12731
|
+
error: message
|
|
12732
|
+
};
|
|
12869
12733
|
}
|
|
12870
|
-
return [...STATIC_CORE_MAPPINGS, ...dynamicMappings];
|
|
12871
12734
|
}
|
|
12872
12735
|
//#endregion
|
|
12873
|
-
//#region ../lib/src/control-plane/
|
|
12736
|
+
//#region ../lib/src/control-plane/fs-atomic.ts
|
|
12874
12737
|
/**
|
|
12875
|
-
*
|
|
12738
|
+
* Write a file atomically: write to `${path}.tmp` then rename over the target.
|
|
12739
|
+
* The rename is atomic on the same filesystem, so readers never observe a
|
|
12740
|
+
* partially written file. `mode` (e.g. 0o600) is applied on creation.
|
|
12876
12741
|
*
|
|
12877
|
-
*
|
|
12878
|
-
*
|
|
12879
|
-
* historical schema files and external validation binary were retired in
|
|
12880
|
-
* #391; everything advisory is surfaced as a non-blocking warning. The
|
|
12881
|
-
* function never shells out and never reads schemas.
|
|
12742
|
+
* Shared by all control-plane writers (setup, akm-sources, …) so config and
|
|
12743
|
+
* secret files are written through one audited path — never hand-rolled.
|
|
12882
12744
|
*/
|
|
12883
|
-
|
|
12745
|
+
function writeFileAtomic(path, content, mode) {
|
|
12746
|
+
const tmp = `${path}.tmp`;
|
|
12747
|
+
writeFileSync(tmp, content, { mode } );
|
|
12748
|
+
renameSync(tmp, path);
|
|
12749
|
+
}
|
|
12750
|
+
//#endregion
|
|
12751
|
+
//#region ../lib/src/control-plane/akm-sources.ts
|
|
12884
12752
|
/**
|
|
12885
|
-
*
|
|
12753
|
+
* Host ↔ Assistant AKM source wiring (control-plane logic — lives in lib).
|
|
12886
12754
|
*
|
|
12887
|
-
*
|
|
12888
|
-
*
|
|
12889
|
-
* non-empty value.
|
|
12890
|
-
* 2. Every secret env key in getCoreSecretMappings() is present (key only
|
|
12891
|
-
* — blank values are warned about, never erred on, because operators
|
|
12892
|
-
* may opt out of providers they don't use).
|
|
12755
|
+
* Implements the "symmetric writable secondary" design
|
|
12756
|
+
* (docs/technical/akm-host-assistant-integration-proposal.md §8).
|
|
12893
12757
|
*
|
|
12894
|
-
*
|
|
12895
|
-
*
|
|
12758
|
+
* Each akm instance keeps its OWN primary stash, data dir, and cache. Sharing
|
|
12759
|
+
* is done purely by adding the other instance's stash as a *secondary* source
|
|
12760
|
+
* in `config.sources[]`:
|
|
12761
|
+
* - The OpenPalm/container config gains a `host-akm` source → /host-stash
|
|
12762
|
+
* (the user's personal ~/akm, bind-mounted by the host-akm.compose.yml overlay).
|
|
12763
|
+
* - The personal config gains an `openpalm` source → OP_HOME/knowledge.
|
|
12764
|
+
*
|
|
12765
|
+
* VERIFIED against akm 0.8.0 (rc.13 → stable; schema unchanged):
|
|
12766
|
+
* - `SourceConfigEntrySchema` (config-schema.ts:259) accepts
|
|
12767
|
+
* { type, path?, url?, name?, enabled?, writable?, primary?, options?, wikiName? }.
|
|
12768
|
+
* - The indexer's `resolveSourceEntries` (search-source.ts:56) ALWAYS injects the
|
|
12769
|
+
* env-resolved primary stash (AKM_STASH_DIR) as sources[0], then appends
|
|
12770
|
+
* config.sources[] deduped by path. So a secondary entry can never strand or
|
|
12771
|
+
* displace the primary — provided we NEVER set `primary:true` and NEVER set
|
|
12772
|
+
* `config.stashDir`.
|
|
12773
|
+
* - Writes resolve to the primary unless an explicit `--target` is given
|
|
12774
|
+
* (write-source.ts) and `defaultWriteTarget` is left unset, so a writable
|
|
12775
|
+
* secondary is safe by construction.
|
|
12776
|
+
*
|
|
12777
|
+
* Invariants (enforced + unit-tested):
|
|
12778
|
+
* - Only ever appends/updates a NAMED source (idempotent upsert by name).
|
|
12779
|
+
* - NEVER sets `primary`, NEVER sets `defaultWriteTarget`, NEVER sets `stashDir`.
|
|
12780
|
+
* - Atomic 0600 writes.
|
|
12781
|
+
* - The OpenPalm config is parse-tolerant (we own it: corrupt → start from {}).
|
|
12782
|
+
* - The PERSONAL config FAILS CLOSED (corrupt/unreadable → throw, never overwrite
|
|
12783
|
+
* the user's file). This asymmetry is the host-data-loss guard.
|
|
12896
12784
|
*/
|
|
12897
|
-
|
|
12898
|
-
|
|
12899
|
-
|
|
12900
|
-
|
|
12901
|
-
|
|
12902
|
-
|
|
12903
|
-
return {
|
|
12904
|
-
|
|
12905
|
-
|
|
12906
|
-
warnings
|
|
12907
|
-
};
|
|
12908
|
-
}
|
|
12909
|
-
const runtimeEnv = readStackRuntimeEnv(state.stackDir);
|
|
12910
|
-
for (const key of REQUIRED_SECRET_KEYS) {
|
|
12911
|
-
const value = runtimeEnv[key];
|
|
12912
|
-
if (!value || value.trim().length === 0) errors.push(`ERROR: required secret ${key} is missing or empty in knowledge/secrets/${key.toLowerCase()}`);
|
|
12785
|
+
/** Source entry name added to the OpenPalm/container config (points at /host-stash). */
|
|
12786
|
+
var HOST_SOURCE_NAME = "host-akm";
|
|
12787
|
+
function readConfigTolerant(configPath) {
|
|
12788
|
+
if (!existsSync(configPath)) return {};
|
|
12789
|
+
try {
|
|
12790
|
+
const parsed = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
12791
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
12792
|
+
} catch {
|
|
12793
|
+
return {};
|
|
12913
12794
|
}
|
|
12914
|
-
for (const mapping of getCoreSecretMappings(runtimeEnv)) if (!Object.prototype.hasOwnProperty.call(runtimeEnv, mapping.envKey)) warnings.push(`WARN: ${mapping.envKey} (akm ${mapping.secretKey}) is not declared in knowledge/secrets/${mapping.envKey.toLowerCase()}`);
|
|
12915
|
-
return {
|
|
12916
|
-
ok: errors.length === 0,
|
|
12917
|
-
errors,
|
|
12918
|
-
warnings
|
|
12919
|
-
};
|
|
12920
|
-
}
|
|
12921
|
-
/** Execute docker with an argument array — no shell interpolation. */
|
|
12922
|
-
function run$1(args, cwd, timeoutMs = 12e4, envOverrides) {
|
|
12923
|
-
return new Promise((resolve) => {
|
|
12924
|
-
execFile("docker", args, {
|
|
12925
|
-
cwd,
|
|
12926
|
-
timeout: timeoutMs,
|
|
12927
|
-
env: {
|
|
12928
|
-
...process.env,
|
|
12929
|
-
...envOverrides
|
|
12930
|
-
}
|
|
12931
|
-
}, (error, stdout, stderr) => {
|
|
12932
|
-
resolve({
|
|
12933
|
-
ok: !error,
|
|
12934
|
-
stdout: stdout?.toString() ?? "",
|
|
12935
|
-
stderr: stderr?.toString() ?? "",
|
|
12936
|
-
code: error?.code ? Number(error.code) : 0
|
|
12937
|
-
});
|
|
12938
|
-
});
|
|
12939
|
-
});
|
|
12940
|
-
}
|
|
12941
|
-
/**
|
|
12942
|
-
* Resolve the Docker Compose project name.
|
|
12943
|
-
* Honors OP_PROJECT_NAME first for OpenPalm stacks, then COMPOSE_PROJECT_NAME.
|
|
12944
|
-
*/
|
|
12945
|
-
function resolveComposeProjectName(envOverrides = {}) {
|
|
12946
|
-
return envOverrides.OP_PROJECT_NAME?.trim() || envOverrides.COMPOSE_PROJECT_NAME?.trim() || process.env.OP_PROJECT_NAME?.trim() || process.env.COMPOSE_PROJECT_NAME?.trim() || "openpalm";
|
|
12947
12795
|
}
|
|
12948
|
-
|
|
12949
|
-
|
|
12950
|
-
|
|
12951
|
-
|
|
12952
|
-
|
|
12953
|
-
|
|
12954
|
-
|
|
12955
|
-
|
|
12956
|
-
|
|
12957
|
-
|
|
12958
|
-
|
|
12959
|
-
|
|
12796
|
+
function readConfigFailClosed(configPath) {
|
|
12797
|
+
if (!existsSync(configPath)) throw new Error(`Personal akm config not found at ${configPath}; refusing to create it. Run \`akm init\`/\`akm setup\` first, then enable host AKM sharing.`);
|
|
12798
|
+
let text;
|
|
12799
|
+
try {
|
|
12800
|
+
text = readFileSync(configPath, "utf-8");
|
|
12801
|
+
} catch (err) {
|
|
12802
|
+
throw new Error(`Unable to read personal akm config at ${configPath}: ${err.message}`);
|
|
12803
|
+
}
|
|
12804
|
+
let parsed;
|
|
12805
|
+
try {
|
|
12806
|
+
parsed = JSON.parse(text);
|
|
12807
|
+
} catch {
|
|
12808
|
+
throw new Error(`Personal akm config at ${configPath} is not valid JSON; refusing to overwrite it. Fix the file by hand, then retry.`);
|
|
12809
|
+
}
|
|
12810
|
+
if (!parsed || typeof parsed !== "object") throw new Error(`Personal akm config at ${configPath} is not a JSON object; refusing to overwrite it.`);
|
|
12811
|
+
return parsed;
|
|
12960
12812
|
}
|
|
12961
12813
|
/**
|
|
12962
|
-
*
|
|
12963
|
-
*
|
|
12964
|
-
* `com.docker.compose.project.working_dir` label against `expectedWorkingDir`
|
|
12965
|
-
* (the install's OP_HOME / compose context).
|
|
12966
|
-
*
|
|
12967
|
-
* Returns `{ exists:false }` on any docker error (daemon down, no permission) —
|
|
12968
|
-
* detection is best-effort and never blocks the caller; a real failure surfaces
|
|
12969
|
-
* later through composeUp.
|
|
12814
|
+
* Upsert a named filesystem source into `config.sources[]` by name. Idempotent.
|
|
12815
|
+
* NEVER touches `primary`, `defaultWriteTarget`, or `stashDir`.
|
|
12970
12816
|
*/
|
|
12971
|
-
function
|
|
12972
|
-
const
|
|
12973
|
-
|
|
12974
|
-
|
|
12975
|
-
|
|
12817
|
+
function upsertSource(config, entry) {
|
|
12818
|
+
const sources = Array.isArray(config.sources) ? [...config.sources] : [];
|
|
12819
|
+
const idx = sources.findIndex((s) => s && typeof s === "object" && s.name === entry.name);
|
|
12820
|
+
if (idx >= 0) sources[idx] = {
|
|
12821
|
+
...sources[idx],
|
|
12822
|
+
...entry
|
|
12823
|
+
};
|
|
12824
|
+
else sources.push(entry);
|
|
12825
|
+
return {
|
|
12826
|
+
...config,
|
|
12827
|
+
sources
|
|
12976
12828
|
};
|
|
12977
|
-
return new Promise((resolve) => {
|
|
12978
|
-
execFile("docker", [
|
|
12979
|
-
"ps",
|
|
12980
|
-
"-q",
|
|
12981
|
-
"--filter",
|
|
12982
|
-
`label=com.docker.compose.project=${opts.projectName}`
|
|
12983
|
-
], { timeout: 1e4 }, (err, stdout) => {
|
|
12984
|
-
if (err) return resolve(none);
|
|
12985
|
-
const ids = stdout.toString().trim().split(/\s+/).filter(Boolean);
|
|
12986
|
-
if (ids.length === 0) return resolve(none);
|
|
12987
|
-
execFile("docker", [
|
|
12988
|
-
"inspect",
|
|
12989
|
-
"--format",
|
|
12990
|
-
"{{ index .Config.Labels \"com.docker.compose.project.working_dir\" }}",
|
|
12991
|
-
ids[0]
|
|
12992
|
-
], { timeout: 1e4 }, (err2, stdout2) => {
|
|
12993
|
-
if (err2) return resolve({
|
|
12994
|
-
exists: true,
|
|
12995
|
-
isOurs: false,
|
|
12996
|
-
workingDir: ""
|
|
12997
|
-
});
|
|
12998
|
-
const workingDir = stdout2.toString().trim();
|
|
12999
|
-
resolve({
|
|
13000
|
-
exists: true,
|
|
13001
|
-
isOurs: isProjectOurs(workingDir, opts.expectedWorkingDir),
|
|
13002
|
-
workingDir
|
|
13003
|
-
});
|
|
13004
|
-
});
|
|
13005
|
-
});
|
|
13006
|
-
});
|
|
13007
|
-
}
|
|
13008
|
-
/** Check if Docker is available */
|
|
13009
|
-
async function checkDocker() {
|
|
13010
|
-
return new Promise((resolve) => {
|
|
13011
|
-
execFile("docker", [
|
|
13012
|
-
"info",
|
|
13013
|
-
"--format",
|
|
13014
|
-
"{{.ServerVersion}}"
|
|
13015
|
-
], (error, stdout, stderr) => {
|
|
13016
|
-
const stdoutStr = stdout?.toString().trim() ?? "";
|
|
13017
|
-
const stderrStr = stderr?.toString() ?? "";
|
|
13018
|
-
resolve({
|
|
13019
|
-
ok: stdoutStr.length > 0 || !error,
|
|
13020
|
-
stdout: stdoutStr,
|
|
13021
|
-
stderr: stderrStr,
|
|
13022
|
-
code: error?.code ? Number(error.code) : 0
|
|
13023
|
-
});
|
|
13024
|
-
});
|
|
13025
|
-
});
|
|
13026
|
-
}
|
|
13027
|
-
/** Check if docker compose is available */
|
|
13028
|
-
async function checkDockerCompose() {
|
|
13029
|
-
return new Promise((resolve) => {
|
|
13030
|
-
execFile("docker", ["compose", "version"], (error, stdout, stderr) => {
|
|
13031
|
-
resolve({
|
|
13032
|
-
ok: !error,
|
|
13033
|
-
stdout: stdout?.toString() ?? "",
|
|
13034
|
-
stderr: stderr?.toString() ?? "",
|
|
13035
|
-
code: error?.code ? Number(error.code) : 0
|
|
13036
|
-
});
|
|
13037
|
-
});
|
|
13038
|
-
});
|
|
13039
12829
|
}
|
|
13040
|
-
|
|
13041
|
-
|
|
13042
|
-
const
|
|
13043
|
-
|
|
13044
|
-
|
|
13045
|
-
|
|
13046
|
-
|
|
13047
|
-
resolveComposeProjectName(envOverrides)
|
|
13048
|
-
];
|
|
13049
|
-
for (const ef of options.envFiles ?? []) if (existsSync(ef)) args.push("--env-file", ef);
|
|
13050
|
-
for (const p of options.profiles ?? []) args.push("--profile", p);
|
|
13051
|
-
return args;
|
|
12830
|
+
function removeSource(config, name) {
|
|
12831
|
+
if (!Array.isArray(config.sources)) return config;
|
|
12832
|
+
const sources = config.sources.filter((s) => !(s && typeof s === "object" && s.name === name));
|
|
12833
|
+
return {
|
|
12834
|
+
...config,
|
|
12835
|
+
sources
|
|
12836
|
+
};
|
|
13052
12837
|
}
|
|
13053
|
-
|
|
13054
|
-
|
|
13055
|
-
|
|
13056
|
-
|
|
13057
|
-
return
|
|
12838
|
+
function assertNoPrimaryEscalation(entry) {
|
|
12839
|
+
if (entry.primary !== void 0) throw new Error("akm-sources: refusing to write a source entry carrying `primary`.");
|
|
12840
|
+
}
|
|
12841
|
+
function openpalmConfigPath(state) {
|
|
12842
|
+
return join(state.configDir, "akm", "config.json");
|
|
13058
12843
|
}
|
|
13059
12844
|
/**
|
|
13060
|
-
*
|
|
13061
|
-
*
|
|
12845
|
+
* Container/OpenPalm side: add the personal stash (mounted at /host-stash) as a
|
|
12846
|
+
* secondary source. Parse-tolerant (we own this config). Writable by default so
|
|
12847
|
+
* the assistant can contribute back via an explicit `--target host-akm`.
|
|
13062
12848
|
*/
|
|
13063
|
-
|
|
13064
|
-
const
|
|
13065
|
-
|
|
13066
|
-
|
|
12849
|
+
function addHostStashToOpenpalmConfig(state, writable = true) {
|
|
12850
|
+
const configPath = openpalmConfigPath(state);
|
|
12851
|
+
const entry = {
|
|
12852
|
+
type: "filesystem",
|
|
12853
|
+
path: "/host-stash",
|
|
12854
|
+
name: HOST_SOURCE_NAME,
|
|
12855
|
+
writable,
|
|
12856
|
+
enabled: true
|
|
12857
|
+
};
|
|
12858
|
+
assertNoPrimaryEscalation(entry);
|
|
12859
|
+
const updated = upsertSource(readConfigTolerant(configPath), entry);
|
|
12860
|
+
writeFileAtomic(configPath, JSON.stringify(updated, null, 2), 384);
|
|
13067
12861
|
}
|
|
13068
12862
|
/**
|
|
13069
|
-
*
|
|
13070
|
-
*
|
|
12863
|
+
* Remove the `host-akm` secondary source from the assistant config (disable
|
|
12864
|
+
* sharing). Parse-tolerant; never touches the user's personal config (D1 —
|
|
12865
|
+
* host sharing is assistant-reads-host only). Idempotent.
|
|
13071
12866
|
*/
|
|
13072
|
-
|
|
13073
|
-
|
|
13074
|
-
const
|
|
13075
|
-
|
|
13076
|
-
const project = resolveComposeProjectName(collectEnvOverrides(options.envFiles));
|
|
13077
|
-
const fileArgs = options.files.map((f) => `-f ${f}`).join(" ");
|
|
13078
|
-
const envArgs = (options.envFiles ?? []).map((f) => `--env-file ${f}`).join(" ");
|
|
13079
|
-
const profileArgs = (options.profiles ?? []).map((p) => `--profile ${p}`).join(" ");
|
|
13080
|
-
throw new Error(`Compose preflight failed: ${result.stderr}\nResolved command: docker compose ${fileArgs} --project-name ${project} ${envArgs} ${profileArgs} config --quiet`);
|
|
13081
|
-
}
|
|
12867
|
+
function removeHostAkmSource(state) {
|
|
12868
|
+
const opPath = openpalmConfigPath(state);
|
|
12869
|
+
const opConfig = readConfigTolerant(opPath);
|
|
12870
|
+
writeFileAtomic(opPath, JSON.stringify(removeSource(opConfig, HOST_SOURCE_NAME), null, 2), 384);
|
|
13082
12871
|
}
|
|
13083
|
-
|
|
13084
|
-
|
|
13085
|
-
|
|
13086
|
-
|
|
13087
|
-
|
|
13088
|
-
|
|
13089
|
-
|
|
13090
|
-
|
|
13091
|
-
|
|
13092
|
-
|
|
13093
|
-
|
|
12872
|
+
/**
|
|
12873
|
+
* Read-only snapshot import of the host's reusable akm config into the OpenPalm
|
|
12874
|
+
* config. Reads the personal config READ-ONLY; never writes back to the host.
|
|
12875
|
+
*
|
|
12876
|
+
* ADDITIVE MERGE: existing OpenPalm values ALWAYS win — the host only fills gaps.
|
|
12877
|
+
* A profile name, default selection, or embedding field that OpenPalm already has
|
|
12878
|
+
* is never overwritten; host-only profiles/fields are added. Covers the
|
|
12879
|
+
* LLM/agent/improve PROFILES (+ their `defaults.*`) and the top-level `embedding`
|
|
12880
|
+
* connection. Returns which sections actually gained values.
|
|
12881
|
+
*
|
|
12882
|
+
* Writes the canonical akm 0.8.0 shape (profiles.* + defaults.* + embedding) —
|
|
12883
|
+
* never the legacy top-level `llm` (see I-3). NEVER touches `sources`, `stashDir`,
|
|
12884
|
+
* `registries`, or `installed`.
|
|
12885
|
+
*/
|
|
12886
|
+
function importHostProfiles(state, hostConfigPath) {
|
|
12887
|
+
const host = readConfigFailClosed(hostConfigPath);
|
|
12888
|
+
const hostProfiles = host.profiles ?? {};
|
|
12889
|
+
const hostDefaults = host.defaults ?? {};
|
|
12890
|
+
const opPath = openpalmConfigPath(state);
|
|
12891
|
+
const op = readConfigTolerant(opPath);
|
|
12892
|
+
const opProfiles = op.profiles ?? {};
|
|
12893
|
+
const opDefaults = op.defaults ?? {};
|
|
12894
|
+
const imported = [];
|
|
12895
|
+
const isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
12896
|
+
for (const ns of [
|
|
12897
|
+
"llm",
|
|
12898
|
+
"agent",
|
|
12899
|
+
"improve"
|
|
12900
|
+
]) {
|
|
12901
|
+
if (isObj(hostProfiles[ns])) {
|
|
12902
|
+
const existing = isObj(opProfiles[ns]) ? opProfiles[ns] : {};
|
|
12903
|
+
const merged = {
|
|
12904
|
+
...hostProfiles[ns],
|
|
12905
|
+
...existing
|
|
12906
|
+
};
|
|
12907
|
+
const added = Object.keys(merged).length - Object.keys(existing).length;
|
|
12908
|
+
opProfiles[ns] = merged;
|
|
12909
|
+
if (added > 0) imported.push(`profiles.${ns}`);
|
|
12910
|
+
}
|
|
12911
|
+
if (typeof hostDefaults[ns] === "string" && typeof opDefaults[ns] !== "string") {
|
|
12912
|
+
opDefaults[ns] = hostDefaults[ns];
|
|
12913
|
+
imported.push(`defaults.${ns}`);
|
|
12914
|
+
}
|
|
12915
|
+
}
|
|
12916
|
+
let embedding;
|
|
12917
|
+
if (isObj(host.embedding)) {
|
|
12918
|
+
const existing = isObj(op.embedding) ? op.embedding : {};
|
|
12919
|
+
const merged = {
|
|
12920
|
+
...host.embedding,
|
|
12921
|
+
...existing
|
|
12922
|
+
};
|
|
12923
|
+
if (Object.keys(merged).length > Object.keys(existing).length) {
|
|
12924
|
+
embedding = merged;
|
|
12925
|
+
imported.push("embedding");
|
|
12926
|
+
}
|
|
12927
|
+
}
|
|
12928
|
+
if (imported.length === 0) return { imported };
|
|
12929
|
+
const updated = {
|
|
12930
|
+
...op,
|
|
12931
|
+
profiles: opProfiles,
|
|
12932
|
+
defaults: opDefaults
|
|
13094
12933
|
};
|
|
12934
|
+
if (embedding !== void 0) updated.embedding = embedding;
|
|
12935
|
+
delete updated.llm;
|
|
12936
|
+
writeFileAtomic(opPath, JSON.stringify(updated, null, 2), 384);
|
|
12937
|
+
return { imported };
|
|
13095
12938
|
}
|
|
12939
|
+
//#endregion
|
|
12940
|
+
//#region ../lib/src/control-plane/host-akm-sharing.ts
|
|
13096
12941
|
/**
|
|
13097
|
-
*
|
|
13098
|
-
*
|
|
12942
|
+
* Host AKM sharing (control-plane logic — lives in lib).
|
|
12943
|
+
*
|
|
12944
|
+
* Simplified model (no compose overlay, no file-presence gating):
|
|
12945
|
+
*
|
|
12946
|
+
* - The assistant ALWAYS mounts `/host-stash` (core.compose.yml). When the host
|
|
12947
|
+
* has AKM, OP_HOST_AKM_STASH points at the user's personal stash (~/akm);
|
|
12948
|
+
* otherwise it is unset and compose falls back to an always-present empty dir.
|
|
12949
|
+
* - "Sharing" is purely a writable SECONDARY source entry named `host-akm` →
|
|
12950
|
+
* /host-stash in the assistant's config/akm/config.json. Adding it = enabled;
|
|
12951
|
+
* removing it = disabled. akm resolves writes to the primary unless an explicit
|
|
12952
|
+
* --target is given, and silently skips a source whose dir is empty/missing —
|
|
12953
|
+
* so a mounted-but-unconfigured /host-stash is harmless.
|
|
12954
|
+
* - Host availability is detected from the presence of the user's personal akm
|
|
12955
|
+
* CONFIG (~/.config/akm/config.json) — the real signal that akm is initialized.
|
|
12956
|
+
*
|
|
12957
|
+
* Decision D1 (2026-06-03): host sharing is assistant-reads-host ONLY by default.
|
|
12958
|
+
* We never write into the user's personal ~/.config/akm here. (Letting the host
|
|
12959
|
+
* akm see OpenPalm's knowledge is a future, explicit opt-in.)
|
|
13099
12960
|
*/
|
|
13100
|
-
|
|
13101
|
-
|
|
13102
|
-
|
|
13103
|
-
|
|
13104
|
-
|
|
13105
|
-
|
|
13106
|
-
|
|
13107
|
-
}
|
|
13108
|
-
|
|
13109
|
-
|
|
13110
|
-
|
|
13111
|
-
|
|
13112
|
-
|
|
13113
|
-
|
|
12961
|
+
var logger$5 = createLogger("host-akm-sharing");
|
|
12962
|
+
var ENV_KEY = "OP_HOST_AKM_STASH";
|
|
12963
|
+
function userHome() {
|
|
12964
|
+
return process.env.HOME ?? process.env.USERPROFILE ?? homedir();
|
|
12965
|
+
}
|
|
12966
|
+
/** The user's personal akm stash dir (mounted into the assistant at /host-stash). */
|
|
12967
|
+
function hostAkmStashPath() {
|
|
12968
|
+
return `${userHome()}/akm`;
|
|
12969
|
+
}
|
|
12970
|
+
/** The user's personal akm config file — its existence is our availability signal. */
|
|
12971
|
+
function hostAkmConfigPath() {
|
|
12972
|
+
return `${userHome()}/.config/akm/config.json`;
|
|
12973
|
+
}
|
|
12974
|
+
/** True when AKM is initialized on the host (personal config exists). */
|
|
12975
|
+
function isHostAkmAvailable() {
|
|
12976
|
+
return existsSync(hostAkmConfigPath());
|
|
12977
|
+
}
|
|
12978
|
+
function stackEnvPath(state) {
|
|
12979
|
+
return `${state.stashDir}/env/stack.env`;
|
|
13114
12980
|
}
|
|
13115
12981
|
/**
|
|
13116
|
-
*
|
|
13117
|
-
* (
|
|
13118
|
-
*
|
|
13119
|
-
* error. Default 30 min, override with OP_COMPOSE_UP_TIMEOUT_MS. Kept bounded
|
|
13120
|
-
* (never removed) so a genuinely hung start still eventually fails.
|
|
12982
|
+
* Point OP_HOST_AKM_STASH at the host stash when AKM is available, else unset it
|
|
12983
|
+
* (compose then uses the empty-dir fallback). Pure infrastructure — does NOT
|
|
12984
|
+
* change the source list. Idempotent; safe to call on setup and on deploy.
|
|
13121
12985
|
*/
|
|
13122
|
-
function
|
|
13123
|
-
const
|
|
13124
|
-
const
|
|
13125
|
-
|
|
13126
|
-
|
|
12986
|
+
function ensureHostStashEnv(state) {
|
|
12987
|
+
const path = stackEnvPath(state);
|
|
12988
|
+
const existing = existsSync(path) ? readFileSync(path, "utf-8") : "";
|
|
12989
|
+
const updated = isHostAkmAvailable() ? mergeEnvContent(existing, { [ENV_KEY]: hostAkmStashPath() }) : removeEnvKey(existing, ENV_KEY);
|
|
12990
|
+
if (updated !== existing) writeFileAtomic(path, updated, 384);
|
|
13127
12991
|
}
|
|
13128
12992
|
/**
|
|
13129
|
-
*
|
|
12993
|
+
* Enable host AKM sharing: ensure OP_HOST_AKM_STASH points at ~/akm and add the
|
|
12994
|
+
* writable `host-akm` secondary source to the assistant config. Optionally import
|
|
12995
|
+
* host LLM/agent profiles (read-only). Throws if host AKM is not available.
|
|
13130
12996
|
*/
|
|
13131
|
-
|
|
13132
|
-
|
|
13133
|
-
|
|
13134
|
-
|
|
13135
|
-
|
|
13136
|
-
|
|
13137
|
-
|
|
13138
|
-
|
|
13139
|
-
|
|
13140
|
-
|
|
13141
|
-
|
|
13142
|
-
if (options.removeOrphans) args.push("--remove-orphans");
|
|
13143
|
-
return run$1(args, void 0);
|
|
12997
|
+
function enableHostAkmSharing(state, opts = {}) {
|
|
12998
|
+
if (!isHostAkmAvailable()) throw new Error(`Host AKM is not available (no ${hostAkmConfigPath()}). Run \`akm init\` on the host first.`);
|
|
12999
|
+
ensureHostStashEnv(state);
|
|
13000
|
+
addHostStashToOpenpalmConfig(state, opts.writable ?? true);
|
|
13001
|
+
let profilesImported = [];
|
|
13002
|
+
if (opts.importProfiles) profilesImported = importHostProfiles(state, hostAkmConfigPath()).imported;
|
|
13003
|
+
logger$5.info("host akm sharing enabled", {
|
|
13004
|
+
hostStashPath: hostAkmStashPath(),
|
|
13005
|
+
profilesImported
|
|
13006
|
+
});
|
|
13007
|
+
return { profilesImported };
|
|
13144
13008
|
}
|
|
13145
13009
|
/**
|
|
13146
|
-
*
|
|
13010
|
+
* Disable host AKM sharing: remove the `host-akm` secondary source from the
|
|
13011
|
+
* assistant config. Leaves the (harmless) mount and env in place; never deletes
|
|
13012
|
+
* any stash content.
|
|
13147
13013
|
*/
|
|
13148
|
-
|
|
13149
|
-
|
|
13150
|
-
|
|
13151
|
-
|
|
13152
|
-
|
|
13153
|
-
|
|
13154
|
-
|
|
13155
|
-
|
|
13156
|
-
|
|
13157
|
-
|
|
13158
|
-
|
|
13159
|
-
|
|
13014
|
+
function disableHostAkmSharing(state) {
|
|
13015
|
+
removeHostAkmSource(state);
|
|
13016
|
+
logger$5.info("host akm sharing disabled");
|
|
13017
|
+
}
|
|
13018
|
+
/** Report availability + whether the host-akm source is currently configured. */
|
|
13019
|
+
function getHostAkmSharingStatus(state) {
|
|
13020
|
+
const available = isHostAkmAvailable();
|
|
13021
|
+
return {
|
|
13022
|
+
available,
|
|
13023
|
+
enabled: openpalmHasHostSource(state),
|
|
13024
|
+
hostStashPath: available ? hostAkmStashPath() : null
|
|
13025
|
+
};
|
|
13160
13026
|
}
|
|
13161
|
-
|
|
13162
|
-
|
|
13163
|
-
|
|
13164
|
-
|
|
13165
|
-
|
|
13166
|
-
|
|
13167
|
-
|
|
13168
|
-
|
|
13027
|
+
function openpalmHasHostSource(state) {
|
|
13028
|
+
const path = `${state.configDir}/akm/config.json`;
|
|
13029
|
+
if (!existsSync(path)) return false;
|
|
13030
|
+
try {
|
|
13031
|
+
const cfg = JSON.parse(readFileSync(path, "utf-8"));
|
|
13032
|
+
return Array.isArray(cfg.sources) && cfg.sources.some((s) => s?.name === "host-akm");
|
|
13033
|
+
} catch {
|
|
13034
|
+
return false;
|
|
13035
|
+
}
|
|
13169
13036
|
}
|
|
13037
|
+
//#endregion
|
|
13038
|
+
//#region ../lib/src/control-plane/rollback.ts
|
|
13170
13039
|
/**
|
|
13171
|
-
*
|
|
13040
|
+
* Snapshot-based rollback for the OpenPalm control plane.
|
|
13041
|
+
*
|
|
13042
|
+
* Before writing validated changes to live paths, the current state
|
|
13043
|
+
* is snapshotted to OP_HOME/data/rollback/. On deploy failure
|
|
13044
|
+
* (or manual `openpalm rollback`), the snapshot is restored.
|
|
13172
13045
|
*/
|
|
13173
|
-
|
|
13174
|
-
|
|
13175
|
-
|
|
13176
|
-
|
|
13177
|
-
|
|
13178
|
-
|
|
13046
|
+
/** Files that are tracked for rollback (relative to homeDir).
|
|
13047
|
+
* Only config/ system files are included — user-editable config files
|
|
13048
|
+
* are never overwritten by lifecycle operations. */
|
|
13049
|
+
var SNAPSHOT_FILES = [
|
|
13050
|
+
"knowledge/env/stack.env",
|
|
13051
|
+
"config/stack/services.compose.yml",
|
|
13052
|
+
"config/stack/channels.compose.yml",
|
|
13053
|
+
"config/stack/custom.compose.yml",
|
|
13054
|
+
"knowledge/secrets/auth.json"
|
|
13055
|
+
];
|
|
13179
13056
|
/**
|
|
13180
|
-
*
|
|
13057
|
+
* Copy a file if it exists, creating parent directories as needed.
|
|
13181
13058
|
*/
|
|
13182
|
-
|
|
13183
|
-
|
|
13184
|
-
|
|
13185
|
-
|
|
13186
|
-
"--filter",
|
|
13187
|
-
`label=com.docker.compose.project=${resolveComposeProjectName()}`,
|
|
13188
|
-
"--format",
|
|
13189
|
-
"json"
|
|
13190
|
-
], void 0);
|
|
13191
|
-
const args = buildComposeArgs(options);
|
|
13192
|
-
args.push("ps", "--format", "json");
|
|
13193
|
-
return run$1(args, void 0);
|
|
13059
|
+
function safeCopy(src, dest) {
|
|
13060
|
+
if (!existsSync(src)) return;
|
|
13061
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
13062
|
+
copyFileSync(src, dest);
|
|
13194
13063
|
}
|
|
13195
13064
|
/**
|
|
13196
|
-
*
|
|
13065
|
+
* Save the current live configuration files to the rollback directory.
|
|
13066
|
+
* Also snapshots stack/core.compose.yml.
|
|
13197
13067
|
*/
|
|
13198
|
-
|
|
13199
|
-
const
|
|
13200
|
-
|
|
13201
|
-
|
|
13202
|
-
|
|
13203
|
-
|
|
13068
|
+
function snapshotCurrentState(state) {
|
|
13069
|
+
const rollbackDir = resolveRollbackDir();
|
|
13070
|
+
mkdirSync(rollbackDir, { recursive: true });
|
|
13071
|
+
for (const rel of SNAPSHOT_FILES) safeCopy(join(state.homeDir, rel), join(rollbackDir, rel));
|
|
13072
|
+
safeCopy(join(state.homeDir, "config/stack/core.compose.yml"), join(rollbackDir, "config/stack/core.compose.yml"));
|
|
13073
|
+
writeFileSync(join(rollbackDir, ".snapshot-ts"), (/* @__PURE__ */ new Date()).toISOString() + "\n");
|
|
13204
13074
|
}
|
|
13205
|
-
|
|
13206
|
-
|
|
13207
|
-
|
|
13208
|
-
|
|
13209
|
-
|
|
13210
|
-
|
|
13075
|
+
//#endregion
|
|
13076
|
+
//#region ../lib/src/control-plane/secret-mappings.ts
|
|
13077
|
+
var STATIC_CORE_MAPPINGS = [
|
|
13078
|
+
{
|
|
13079
|
+
secretKey: "openpalm/ui-login-password",
|
|
13080
|
+
envKey: "OP_UI_LOGIN_PASSWORD",
|
|
13081
|
+
scope: "system"
|
|
13082
|
+
},
|
|
13083
|
+
{
|
|
13084
|
+
secretKey: "openpalm/opencode/server-password",
|
|
13085
|
+
envKey: "OP_OPENCODE_PASSWORD",
|
|
13086
|
+
scope: "system"
|
|
13087
|
+
},
|
|
13088
|
+
{
|
|
13089
|
+
secretKey: "openpalm/openai/api-key",
|
|
13090
|
+
envKey: "OPENAI_API_KEY",
|
|
13091
|
+
scope: "user"
|
|
13092
|
+
},
|
|
13093
|
+
{
|
|
13094
|
+
secretKey: "openpalm/anthropic/api-key",
|
|
13095
|
+
envKey: "ANTHROPIC_API_KEY",
|
|
13096
|
+
scope: "user"
|
|
13097
|
+
},
|
|
13098
|
+
{
|
|
13099
|
+
secretKey: "openpalm/groq/api-key",
|
|
13100
|
+
envKey: "GROQ_API_KEY",
|
|
13101
|
+
scope: "user"
|
|
13102
|
+
},
|
|
13103
|
+
{
|
|
13104
|
+
secretKey: "openpalm/mistral/api-key",
|
|
13105
|
+
envKey: "MISTRAL_API_KEY",
|
|
13106
|
+
scope: "user"
|
|
13107
|
+
},
|
|
13108
|
+
{
|
|
13109
|
+
secretKey: "openpalm/google/api-key",
|
|
13110
|
+
envKey: "GOOGLE_API_KEY",
|
|
13111
|
+
scope: "user"
|
|
13112
|
+
},
|
|
13113
|
+
{
|
|
13114
|
+
secretKey: "openpalm/together/api-key",
|
|
13115
|
+
envKey: "TOGETHER_API_KEY",
|
|
13116
|
+
scope: "user"
|
|
13117
|
+
},
|
|
13118
|
+
{
|
|
13119
|
+
secretKey: "openpalm/deepseek/api-key",
|
|
13120
|
+
envKey: "DEEPSEEK_API_KEY",
|
|
13121
|
+
scope: "user"
|
|
13122
|
+
},
|
|
13123
|
+
{
|
|
13124
|
+
secretKey: "openpalm/xai/api-key",
|
|
13125
|
+
envKey: "XAI_API_KEY",
|
|
13126
|
+
scope: "user"
|
|
13127
|
+
},
|
|
13128
|
+
{
|
|
13129
|
+
secretKey: "openpalm/huggingface/token",
|
|
13130
|
+
envKey: "HF_TOKEN",
|
|
13131
|
+
scope: "user"
|
|
13132
|
+
},
|
|
13133
|
+
{
|
|
13134
|
+
secretKey: "openpalm/mcp/api-key",
|
|
13135
|
+
envKey: "MCP_API_KEY",
|
|
13136
|
+
scope: "user"
|
|
13137
|
+
},
|
|
13138
|
+
{
|
|
13139
|
+
secretKey: "openpalm/embedding/api-key",
|
|
13140
|
+
envKey: "EMBEDDING_API_KEY",
|
|
13141
|
+
scope: "user"
|
|
13142
|
+
},
|
|
13143
|
+
{
|
|
13144
|
+
secretKey: "openpalm/lmstudio/api-key",
|
|
13145
|
+
envKey: "LMSTUDIO_API_KEY",
|
|
13146
|
+
scope: "user"
|
|
13147
|
+
},
|
|
13148
|
+
{
|
|
13149
|
+
secretKey: "openpalm/discord/bot-token",
|
|
13150
|
+
envKey: "DISCORD_BOT_TOKEN",
|
|
13151
|
+
scope: "user"
|
|
13152
|
+
},
|
|
13153
|
+
{
|
|
13154
|
+
secretKey: "openpalm/slack/bot-token",
|
|
13155
|
+
envKey: "SLACK_BOT_TOKEN",
|
|
13156
|
+
scope: "user"
|
|
13157
|
+
},
|
|
13158
|
+
{
|
|
13159
|
+
secretKey: "openpalm/slack/app-token",
|
|
13160
|
+
envKey: "SLACK_APP_TOKEN",
|
|
13161
|
+
scope: "user"
|
|
13162
|
+
},
|
|
13163
|
+
{
|
|
13164
|
+
secretKey: "openpalm/voice/stt-api-key",
|
|
13165
|
+
envKey: "STT_API_KEY",
|
|
13166
|
+
scope: "user"
|
|
13167
|
+
},
|
|
13168
|
+
{
|
|
13169
|
+
secretKey: "openpalm/voice/tts-api-key",
|
|
13170
|
+
envKey: "TTS_API_KEY",
|
|
13171
|
+
scope: "user"
|
|
13172
|
+
}
|
|
13173
|
+
];
|
|
13174
|
+
function getCoreSecretMappings(systemEnv) {
|
|
13175
|
+
const dynamicMappings = [];
|
|
13176
|
+
for (const envKey of Object.keys(systemEnv)) {
|
|
13177
|
+
const match = envKey.match(/^CHANNEL_([A-Z0-9_]+)_SECRET$/);
|
|
13178
|
+
if (!match?.[1]) continue;
|
|
13179
|
+
dynamicMappings.push({
|
|
13180
|
+
secretKey: `openpalm/channel/${match[1].toLowerCase()}/secret`,
|
|
13181
|
+
envKey,
|
|
13182
|
+
scope: "system"
|
|
13183
|
+
});
|
|
13184
|
+
}
|
|
13185
|
+
return [...STATIC_CORE_MAPPINGS, ...dynamicMappings];
|
|
13211
13186
|
}
|
|
13187
|
+
//#endregion
|
|
13188
|
+
//#region ../lib/src/control-plane/validate.ts
|
|
13212
13189
|
/**
|
|
13213
|
-
*
|
|
13190
|
+
* Runtime configuration validation for the OpenPalm control plane.
|
|
13191
|
+
*
|
|
13192
|
+
* Validation is a presence check on the canonical env keys we expect in
|
|
13193
|
+
* the live config/stack files. The
|
|
13194
|
+
* historical schema files and external validation binary were retired in
|
|
13195
|
+
* #391; everything advisory is surfaced as a non-blocking warning. The
|
|
13196
|
+
* function never shells out and never reads schemas.
|
|
13214
13197
|
*/
|
|
13215
|
-
|
|
13216
|
-
const args = buildComposeArgs(options);
|
|
13217
|
-
args.push("stats", "--no-stream", "--format", "json");
|
|
13218
|
-
return run$1(args, void 0);
|
|
13219
|
-
}
|
|
13198
|
+
var REQUIRED_SECRET_KEYS = ["OP_UI_LOGIN_PASSWORD"];
|
|
13220
13199
|
/**
|
|
13221
|
-
*
|
|
13200
|
+
* Validate the live configuration files.
|
|
13201
|
+
*
|
|
13202
|
+
* Checks:
|
|
13203
|
+
* 1. knowledge/env/stack.env exists and carries every required key with a
|
|
13204
|
+
* non-empty value.
|
|
13205
|
+
* 2. Every secret env key in getCoreSecretMappings() is present (key only
|
|
13206
|
+
* — blank values are warned about, never erred on, because operators
|
|
13207
|
+
* may opt out of providers they don't use).
|
|
13208
|
+
*
|
|
13209
|
+
* Errors fail the result. Warnings do not. The function never reads
|
|
13210
|
+
* schema files and never spawns subprocesses.
|
|
13222
13211
|
*/
|
|
13223
|
-
async function
|
|
13224
|
-
|
|
13225
|
-
|
|
13226
|
-
|
|
13227
|
-
|
|
13228
|
-
|
|
13229
|
-
|
|
13230
|
-
|
|
13231
|
-
|
|
13232
|
-
|
|
13233
|
-
|
|
13234
|
-
|
|
13212
|
+
async function validateProposedState(state) {
|
|
13213
|
+
const errors = [];
|
|
13214
|
+
const warnings = [];
|
|
13215
|
+
const stackEnvPath = `${state.stashDir}/env/stack.env`;
|
|
13216
|
+
if (!existsSync(stackEnvPath)) {
|
|
13217
|
+
errors.push(`ERROR: stack env file missing at ${stackEnvPath}`);
|
|
13218
|
+
return {
|
|
13219
|
+
ok: false,
|
|
13220
|
+
errors,
|
|
13221
|
+
warnings
|
|
13222
|
+
};
|
|
13223
|
+
}
|
|
13224
|
+
const runtimeEnv = readStackRuntimeEnv(state.stackDir);
|
|
13225
|
+
for (const key of REQUIRED_SECRET_KEYS) {
|
|
13226
|
+
const value = runtimeEnv[key];
|
|
13227
|
+
if (!value || value.trim().length === 0) errors.push(`ERROR: required secret ${key} is missing or empty in knowledge/secrets/${key.toLowerCase()}`);
|
|
13228
|
+
}
|
|
13229
|
+
for (const mapping of getCoreSecretMappings(runtimeEnv)) if (!Object.prototype.hasOwnProperty.call(runtimeEnv, mapping.envKey)) warnings.push(`WARN: ${mapping.envKey} (akm ${mapping.secretKey}) is not declared in knowledge/secrets/${mapping.envKey.toLowerCase()}`);
|
|
13230
|
+
return {
|
|
13231
|
+
ok: errors.length === 0,
|
|
13232
|
+
errors,
|
|
13233
|
+
warnings
|
|
13234
|
+
};
|
|
13235
13235
|
}
|
|
13236
13236
|
//#endregion
|
|
13237
13237
|
//#region ../lib/src/control-plane/compose-args.ts
|
|
@@ -14878,4 +14878,4 @@ function importHostOpenCode(state, options = {}) {
|
|
|
14878
14878
|
}
|
|
14879
14879
|
|
|
14880
14880
|
export { hostAkmStashPath as $, AKM_USER_ENV_REF as A, createState as B, CORE_SERVICES as C, deleteUserEnvKey as D, detectExistingProject as E, detectGpu as F, detectHostOpenCode as G, detectLocalProviders as H, disableHostAkmSharing as I, enableHostAkmSharing as J, ensureAkmUserEnv as K, ensureHomeDirs as L, MigrationError as M, ensureMigrated as N, ensureOpenCodeConfig as O, PROVIDER_KEY_MAP as P, ensureOpenCodeSystemConfig as Q, ensureSecrets as R, executeAutomation as S, fetchProviderModels as T, getAddonProfileAvailability as U, getAddonProfileSelection as V, getAddonProfiles as W, getAddonServiceNames as X, getDockerEvents as Y, getHostAkmSharingStatus as Z, getRegistryAddonConfig as _, addonProfileId as a, importHostOpenCode as a0, isAllowedService as a1, isHostAkmAvailable as a2, isSetupComplete as a3, listAvailableAddonIds as a4, listEnabledAddonIds as a5, listSecretFiles as a6, loadAutomations as a7, parseComposeStderr as a8, parseEnvFile as a9, writeTaskFile as aA, writeUserEnvKey as aB, writeVoiceVars as aC, performSetup as aa, performUpgrade as ab, readAutomationLogs as ac, readSecret as ad, readSecretFile as ae, readStackEnv as af, readStackRuntimeEnv as ag, readStackSecretEnv as ah, readTaskFile as ai, readUserEnvFile as aj, recommendSetup as ak, removeSecretFile as al, removeTaskFile as am, resolveComposeProjectName as an, resolveDataDir as ao, resolveRuntimeFiles as ap, resolveStackDir as aq, seedUiBuild as ar, setAddonEnabled as as, setAddonProfileSelection as at, summarizeComposeStderr as au, validateProposedState as av, writeFileAtomic as aw, writeRuntimeFiles as ax, writeSecretFile as ay, writeStackSecretEnv as az, annotateAddonProfileAvailability as b, applyInstall as c, applyTagChange as d, applyUninstall as e, applyUpdate as f, assertSafeSecretFilename as g, assertSafeTaskFilename as h, authJsonPath as i, buildAkmEnv as j, buildComposeOptions as k, buildManagedServices as l, checkDocker as m, checkDockerCompose as n, composeDown as o, composeLogs as p, composePreflight as q, composePs as r, composePull as s, composeRestart as t, composeStart as u, composeStats as v, composeStop as w, composeUp as x, createLogger as y, createOpenCodeClient as z };
|
|
14881
|
-
//# sourceMappingURL=src-
|
|
14881
|
+
//# sourceMappingURL=src-DkGZ5D6n.js.map
|