@pasko70/pibo 2.0.0 → 2.2.0
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/dist/agent-runtime/capabilities.js +68 -0
- package/dist/agent-runtime/context-build.js +13 -5
- package/dist/agent-runtime/portable-history.js +526 -0
- package/dist/agent-runtime/profile-validation.js +13 -7
- package/dist/agent-runtime/resource-files.js +26 -10
- package/dist/agent-runtime/resource-service.js +216 -13
- package/dist/agent-runtimes/codex-native/adapter.js +38 -1
- package/dist/agent-runtimes/codex-native/auth.js +24 -3
- package/dist/agent-runtimes/codex-native/portable-history.js +73 -0
- package/dist/agent-runtimes/codex-native/process.js +38 -27
- package/dist/agent-runtimes/codex-native/resource-delivery.js +39 -16
- package/dist/agent-runtimes/codex-native/turn.js +68 -5
- package/dist/agent-runtimes/omp/adapter.js +616 -0
- package/dist/agent-runtimes/omp/auth.js +109 -0
- package/dist/agent-runtimes/omp/client.js +460 -0
- package/dist/agent-runtimes/omp/config.js +188 -0
- package/dist/agent-runtimes/omp/history.js +108 -0
- package/dist/agent-runtimes/omp/host-tools.js +201 -0
- package/dist/agent-runtimes/omp/models.js +86 -0
- package/dist/agent-runtimes/omp/process.js +299 -0
- package/dist/agent-runtimes/omp/protocol-types.js +17 -0
- package/dist/agent-runtimes/omp/resource-delivery.js +228 -0
- package/dist/agent-runtimes/omp/thread.js +141 -0
- package/dist/agent-runtimes/omp/turn.js +325 -0
- package/dist/agent-runtimes/pi/adapter.js +32 -1
- package/dist/agent-runtimes/pi/auth.js +34 -29
- package/dist/agent-runtimes/pi/credentials.js +43 -0
- package/dist/agent-runtimes/pi/model-catalog.js +7 -3
- package/dist/agent-runtimes/pi/portable-history.js +99 -0
- package/dist/agent-runtimes/pi/routed-session.js +1 -1
- package/dist/agent-runtimes/pi/runtime.js +31 -15
- package/dist/apps/chat/agent-profiles.js +1 -0
- package/dist/apps/chat/agent-store.js +20 -7
- package/dist/apps/chat/chat-files.js +19 -6
- package/dist/apps/chat/chat-request-normalizers.js +13 -0
- package/dist/apps/chat/web-app.js +55 -6
- package/dist/apps/chat-ui/assets/{apl-14BqhLYK.js → apl-gfMMiTNf.js} +1 -1
- package/dist/apps/chat-ui/assets/asciiarmor-C_eisQCp.js +1 -0
- package/dist/apps/{context-files-ui/assets/asn1-Bg4FbdCg.js → chat-ui/assets/asn1-DCl_8MCW.js} +1 -1
- package/dist/apps/{context-files-ui/assets/asterisk-B2MO-X4d.js → chat-ui/assets/asterisk-BEn0Cwpx.js} +1 -1
- package/dist/apps/chat-ui/assets/clike-D_6L3tnO.js +1 -0
- package/dist/apps/chat-ui/assets/cmake-84nbDH0I.js +1 -0
- package/dist/apps/chat-ui/assets/cobol-DHLQUbN7.js +1 -0
- package/dist/apps/chat-ui/assets/coffeescript-De_zI4J0.js +1 -0
- package/dist/apps/chat-ui/assets/commonlisp-DgAKBe0r.js +1 -0
- package/dist/apps/chat-ui/assets/crystal-Bsdphhtm.js +1 -0
- package/dist/apps/chat-ui/assets/css-CnaYoQib.js +1 -0
- package/dist/apps/chat-ui/assets/{d-TPqSVaHM.js → d-DWkQyNnU.js} +1 -1
- package/dist/apps/{context-files-ui/assets/dist-DxEGebCR.js → chat-ui/assets/dist-BB8loWTa.js} +1 -1
- package/dist/apps/chat-ui/assets/dist-BJ-TIk3N.js +1 -0
- package/dist/apps/chat-ui/assets/dist-CBPDkWd4.js +1 -0
- package/dist/apps/chat-ui/assets/dist-CHInEWhr.js +1 -0
- package/dist/apps/chat-ui/assets/dist-CqM4On9D.js +13 -0
- package/dist/apps/chat-ui/assets/dist-CswnpKik.js +1 -0
- package/dist/apps/{context-files-ui/assets/dist-B7gXtTZC.js → chat-ui/assets/dist-CwS16H7k.js} +1 -1
- package/dist/apps/chat-ui/assets/dist-DHvUPfrF.js +1 -0
- package/dist/apps/chat-ui/assets/dist-DLJCGrGQ.js +1 -0
- package/dist/apps/chat-ui/assets/{dist-DLMjd26T.js → dist-DMiglryg.js} +1 -1
- package/dist/apps/chat-ui/assets/dist-DTeLDijr.js +9 -0
- package/dist/apps/chat-ui/assets/{dist-DKoyMSNr.js → dist-DpF08Yd7.js} +1 -1
- package/dist/apps/chat-ui/assets/dist-LCx98fA_.js +11 -0
- package/dist/apps/chat-ui/assets/{dist-DotDq_kD.js → dist-RKTRnzGb.js} +1 -1
- package/dist/apps/chat-ui/assets/dist-S3zJGntM.js +1 -0
- package/dist/apps/{context-files-ui/assets/dist-BkUONE3N.js → chat-ui/assets/dist-XVYGiB-c.js} +1 -1
- package/dist/apps/chat-ui/assets/dist-eddbbGyw.js +1 -0
- package/dist/apps/chat-ui/assets/dist-fPhZsAUX.js +1 -0
- package/dist/apps/chat-ui/assets/dist-mpRGeo_R.js +2 -0
- package/dist/apps/chat-ui/assets/{dist-sZgJkWZb.js → dist-nU3BVpFi.js} +1 -1
- package/dist/apps/chat-ui/assets/{dockerfile-CJb2sekW.js → dockerfile-DJkHQgkl.js} +1 -1
- package/dist/apps/chat-ui/assets/{dylan-08Azy1NK.js → dylan-BCDoL_-p.js} +1 -1
- package/dist/apps/chat-ui/assets/{ecl-dhxjHL8y.js → ecl-BVIeWmwY.js} +1 -1
- package/dist/apps/{context-files-ui/assets/erlang-BD5LkVkR.js → chat-ui/assets/erlang-BV0c4Hdn.js} +1 -1
- package/dist/apps/chat-ui/assets/{factor-B2EMQZkP.js → factor-LKH5gN8L.js} +1 -1
- package/dist/apps/chat-ui/assets/groovy-CUpZ7dEl.js +1 -0
- package/dist/apps/chat-ui/assets/{haxe-BJVKtprM.js → haxe-DjVOVvNP.js} +1 -1
- package/dist/apps/chat-ui/assets/index-CQuka3bs.js +226 -0
- package/dist/apps/chat-ui/assets/index-DUXboQnI.css +1 -0
- package/dist/apps/chat-ui/assets/javascript-DoL9Wb-Q.js +1 -0
- package/dist/apps/chat-ui/assets/{lua-BxuTHaF7.js → lua-CiA1ziua.js} +1 -1
- package/dist/apps/chat-ui/assets/{mumps-Ds_ZaumL.js → mumps-BSzj6Xyz.js} +1 -1
- package/dist/apps/{context-files-ui/assets/nsis-DOo3SJmd.js → chat-ui/assets/nsis-CfSuhXs5.js} +1 -1
- package/dist/apps/chat-ui/assets/{ntriples-DLHGTvHs.js → ntriples-B9h1VB_g.js} +1 -1
- package/dist/apps/chat-ui/assets/perl-BkSkiI9m.js +1 -0
- package/dist/apps/chat-ui/assets/{powershell-B4IPkVxR.js → powershell-BcIto6Sf.js} +1 -1
- package/dist/apps/{context-files-ui/assets/pug-QU72BPAa.js → chat-ui/assets/pug-Gsm0M45u.js} +1 -1
- package/dist/apps/chat-ui/assets/puppet-BZyAS7I4.js +1 -0
- package/dist/apps/chat-ui/assets/python-B_1Jd8HM.js +1 -0
- package/dist/apps/chat-ui/assets/r-CxDmU1I4.js +1 -0
- package/dist/apps/chat-ui/assets/ruby-BjXV7-Qa.js +1 -0
- package/dist/apps/chat-ui/assets/scheme-D9qYN0V5.js +1 -0
- package/dist/apps/chat-ui/assets/{shell-BjiMLAaM.js → shell-DwuoZtxw.js} +1 -1
- package/dist/apps/chat-ui/assets/{simple-mode-DnMbQuKW.js → simple-mode-DXiLM1-G.js} +1 -1
- package/dist/apps/chat-ui/assets/sql-DuQMFDvy.js +1 -0
- package/dist/apps/chat-ui/assets/{stex-BCRT8IJ3.js → stex-DNzd62Q_.js} +1 -1
- package/dist/apps/chat-ui/assets/{stylus-CARoeVE9.js → stylus-BOVgKnng.js} +1 -1
- package/dist/apps/chat-ui/assets/textile-C4jVoHG1.js +1 -0
- package/dist/apps/chat-ui/assets/tiki-AK6FwT8q.js +1 -0
- package/dist/apps/chat-ui/assets/toml-BPTmHmyx.js +1 -0
- package/dist/apps/chat-ui/assets/{ttcn-GvRgs2AT.js → ttcn-PPSTk1ow.js} +1 -1
- package/dist/apps/chat-ui/assets/{ttcn-cfg-CGCXz7Vo.js → ttcn-cfg-2b4Gb5sE.js} +1 -1
- package/dist/apps/chat-ui/assets/turtle-Cx3rYX2g.js +1 -0
- package/dist/apps/chat-ui/assets/{verilog-DVCHmufx.js → verilog-7LoJAqYT.js} +1 -1
- package/dist/apps/chat-ui/assets/vhdl-kS471soi.js +1 -0
- package/dist/apps/{context-files-ui/assets/xquery-5f0hn7cg.js → chat-ui/assets/xquery-BKJx4mf9.js} +1 -1
- package/dist/apps/chat-ui/assets/z80-Co3PbHdE.js +1 -0
- package/dist/apps/chat-ui/index.html +5 -3
- package/dist/apps/chat-vscode-web/assets/index-CF1-9PKU.css +2 -0
- package/dist/apps/chat-vscode-web/assets/index-Dp1TzeRL.js +41 -0
- package/dist/apps/chat-vscode-web/index.html +2 -2
- package/dist/apps/context-files-ui/assets/{apl-14BqhLYK.js → apl-gfMMiTNf.js} +1 -1
- package/dist/apps/context-files-ui/assets/asciiarmor-C_eisQCp.js +1 -0
- package/dist/apps/{chat-ui/assets/asn1-Bg4FbdCg.js → context-files-ui/assets/asn1-DCl_8MCW.js} +1 -1
- package/dist/apps/{chat-ui/assets/asterisk-B2MO-X4d.js → context-files-ui/assets/asterisk-BEn0Cwpx.js} +1 -1
- package/dist/apps/context-files-ui/assets/clike-D_6L3tnO.js +1 -0
- package/dist/apps/context-files-ui/assets/cmake-84nbDH0I.js +1 -0
- package/dist/apps/context-files-ui/assets/cobol-DHLQUbN7.js +1 -0
- package/dist/apps/context-files-ui/assets/coffeescript-De_zI4J0.js +1 -0
- package/dist/apps/context-files-ui/assets/commonlisp-DgAKBe0r.js +1 -0
- package/dist/apps/context-files-ui/assets/crystal-Bsdphhtm.js +1 -0
- package/dist/apps/context-files-ui/assets/css-CnaYoQib.js +1 -0
- package/dist/apps/context-files-ui/assets/{d-TPqSVaHM.js → d-DWkQyNnU.js} +1 -1
- package/dist/apps/{chat-ui/assets/dist-C6LPDFXU.js → context-files-ui/assets/dist-BAqbgxUS.js} +1 -1
- package/dist/apps/context-files-ui/assets/dist-BJ-TIk3N.js +1 -0
- package/dist/apps/context-files-ui/assets/dist-BcauuFwf.js +1 -0
- package/dist/apps/context-files-ui/assets/dist-BecMXs79.js +1 -0
- package/dist/apps/context-files-ui/assets/{dist-C-Jm45P4.js → dist-Ci2NSlf1.js} +1 -1
- package/dist/apps/context-files-ui/assets/dist-CqM4On9D.js +13 -0
- package/dist/apps/context-files-ui/assets/dist-CswnpKik.js +1 -0
- package/dist/apps/{chat-ui/assets/dist-I26VB0QO.js → context-files-ui/assets/dist-D-xpsbZI.js} +1 -1
- package/dist/apps/context-files-ui/assets/dist-DHvUPfrF.js +1 -0
- package/dist/apps/context-files-ui/assets/dist-DLJCGrGQ.js +1 -0
- package/dist/apps/context-files-ui/assets/{dist-DLMjd26T.js → dist-DMiglryg.js} +1 -1
- package/dist/apps/context-files-ui/assets/dist-DTeLDijr.js +9 -0
- package/dist/apps/context-files-ui/assets/{dist-DKoyMSNr.js → dist-DpF08Yd7.js} +1 -1
- package/dist/apps/context-files-ui/assets/dist-LCx98fA_.js +11 -0
- package/dist/apps/context-files-ui/assets/{dist-DotDq_kD.js → dist-RKTRnzGb.js} +1 -1
- package/dist/apps/context-files-ui/assets/dist-S3zJGntM.js +1 -0
- package/dist/apps/{chat-ui/assets/dist-BkUONE3N.js → context-files-ui/assets/dist-XVYGiB-c.js} +1 -1
- package/dist/apps/context-files-ui/assets/dist-eddbbGyw.js +1 -0
- package/dist/apps/context-files-ui/assets/dist-fPhZsAUX.js +1 -0
- package/dist/apps/context-files-ui/assets/dist-mpRGeo_R.js +2 -0
- package/dist/apps/context-files-ui/assets/{dockerfile-CJb2sekW.js → dockerfile-DJkHQgkl.js} +1 -1
- package/dist/apps/context-files-ui/assets/{dylan-08Azy1NK.js → dylan-BCDoL_-p.js} +1 -1
- package/dist/apps/context-files-ui/assets/{ecl-dhxjHL8y.js → ecl-BVIeWmwY.js} +1 -1
- package/dist/apps/{chat-ui/assets/erlang-BD5LkVkR.js → context-files-ui/assets/erlang-BV0c4Hdn.js} +1 -1
- package/dist/apps/context-files-ui/assets/{factor-B2EMQZkP.js → factor-LKH5gN8L.js} +1 -1
- package/dist/apps/context-files-ui/assets/groovy-CUpZ7dEl.js +1 -0
- package/dist/apps/context-files-ui/assets/{haxe-BJVKtprM.js → haxe-DjVOVvNP.js} +1 -1
- package/dist/apps/context-files-ui/assets/index-CkWXlBNn.js +191 -0
- package/dist/apps/context-files-ui/assets/{index-Cs4XQITe.css → index-D8TsUizd.css} +1 -1
- package/dist/apps/context-files-ui/assets/javascript-DoL9Wb-Q.js +1 -0
- package/dist/apps/context-files-ui/assets/{lua-BxuTHaF7.js → lua-CiA1ziua.js} +1 -1
- package/dist/apps/context-files-ui/assets/{mumps-Ds_ZaumL.js → mumps-BSzj6Xyz.js} +1 -1
- package/dist/apps/{chat-ui/assets/nsis-DOo3SJmd.js → context-files-ui/assets/nsis-CfSuhXs5.js} +1 -1
- package/dist/apps/context-files-ui/assets/{ntriples-DLHGTvHs.js → ntriples-B9h1VB_g.js} +1 -1
- package/dist/apps/context-files-ui/assets/perl-BkSkiI9m.js +1 -0
- package/dist/apps/context-files-ui/assets/{powershell-B4IPkVxR.js → powershell-BcIto6Sf.js} +1 -1
- package/dist/apps/{chat-ui/assets/pug-QU72BPAa.js → context-files-ui/assets/pug-Gsm0M45u.js} +1 -1
- package/dist/apps/context-files-ui/assets/puppet-BZyAS7I4.js +1 -0
- package/dist/apps/context-files-ui/assets/python-B_1Jd8HM.js +1 -0
- package/dist/apps/context-files-ui/assets/r-CxDmU1I4.js +1 -0
- package/dist/apps/context-files-ui/assets/ruby-BjXV7-Qa.js +1 -0
- package/dist/apps/context-files-ui/assets/scheme-D9qYN0V5.js +1 -0
- package/dist/apps/context-files-ui/assets/{shell-BjiMLAaM.js → shell-DwuoZtxw.js} +1 -1
- package/dist/apps/context-files-ui/assets/{simple-mode-DnMbQuKW.js → simple-mode-DXiLM1-G.js} +1 -1
- package/dist/apps/context-files-ui/assets/sql-DuQMFDvy.js +1 -0
- package/dist/apps/context-files-ui/assets/{stex-BCRT8IJ3.js → stex-DNzd62Q_.js} +1 -1
- package/dist/apps/context-files-ui/assets/{stylus-CARoeVE9.js → stylus-BOVgKnng.js} +1 -1
- package/dist/apps/context-files-ui/assets/textile-C4jVoHG1.js +1 -0
- package/dist/apps/context-files-ui/assets/tiki-AK6FwT8q.js +1 -0
- package/dist/apps/context-files-ui/assets/toml-BPTmHmyx.js +1 -0
- package/dist/apps/context-files-ui/assets/{ttcn-GvRgs2AT.js → ttcn-PPSTk1ow.js} +1 -1
- package/dist/apps/context-files-ui/assets/{ttcn-cfg-CGCXz7Vo.js → ttcn-cfg-2b4Gb5sE.js} +1 -1
- package/dist/apps/context-files-ui/assets/turtle-Cx3rYX2g.js +1 -0
- package/dist/apps/context-files-ui/assets/{verilog-DVCHmufx.js → verilog-7LoJAqYT.js} +1 -1
- package/dist/apps/context-files-ui/assets/vhdl-kS471soi.js +1 -0
- package/dist/apps/{chat-ui/assets/xquery-5f0hn7cg.js → context-files-ui/assets/xquery-BKJx4mf9.js} +1 -1
- package/dist/apps/context-files-ui/assets/z80-Co3PbHdE.js +1 -0
- package/dist/apps/context-files-ui/index.html +5 -3
- package/dist/apps/vscode-artifacts/latest.vsix +0 -0
- package/dist/apps/vscode-artifacts/pibo-vscode-ext-2.2.0.vsix +0 -0
- package/dist/auth/better-auth.js +223 -30
- package/dist/auth/cli.js +10 -1
- package/dist/auth/machine-keys.js +8 -4
- package/dist/auth/openai-codex-usage.js +4 -4
- package/dist/cli-session/fakeSessionSource.js +2 -2
- package/dist/cli-session/localSessionSource.js +2 -2
- package/dist/cli.js +3 -1
- package/dist/config/config.js +9 -4
- package/dist/core/compaction-prompt.js +3 -1
- package/dist/core/context-build.js +1 -0
- package/dist/core/pibo-home.js +24 -1
- package/dist/core/private-path.js +146 -0
- package/dist/core/profiles.js +8 -0
- package/dist/core/sensitive-data-redaction.js +36 -0
- package/dist/core/session-model.js +1 -0
- package/dist/core/session-router.js +219 -29
- package/dist/data/pibo-store.js +5 -1
- package/dist/gateway/server.js +9 -0
- package/dist/index.js +1 -1
- package/dist/plugins/builtin.js +4 -2
- package/dist/plugins/context-files.js +11 -0
- package/dist/plugins/omp.js +57 -0
- package/dist/plugins/registry.js +1 -0
- package/dist/providers/openai-gpt56.js +0 -11
- package/dist/reliability/store.js +5 -1
- package/dist/resources/lifecycle.js +13 -3
- package/dist/runs/resource-isolation.js +159 -8
- package/dist/runs/tools.js +44 -1
- package/dist/sessions/pibo-data-store.js +3 -0
- package/dist/tools/codex-browser-node-repl.js +4 -2
- package/dist/tools/codex-browser.js +1 -1
- package/dist/tools/codex-image-generation.js +4 -4
- package/dist/tools/runtime/node-backend.js +4 -1
- package/dist/tools/runtime/python-backend.js +4 -1
- package/dist/web-annotations/api.js +22 -2
- package/npm-shrinkwrap.json +16707 -0
- package/package.json +19 -14
- package/dist/apps/chat-ui/assets/asciiarmor-WLvjqI7d.js +0 -1
- package/dist/apps/chat-ui/assets/clike-CjmcUpt6.js +0 -1
- package/dist/apps/chat-ui/assets/cmake-i2VDNaRq.js +0 -1
- package/dist/apps/chat-ui/assets/cobol-BwO6DF08.js +0 -1
- package/dist/apps/chat-ui/assets/coffeescript-BtivDvRz.js +0 -1
- package/dist/apps/chat-ui/assets/commonlisp-DFkEQdLP.js +0 -1
- package/dist/apps/chat-ui/assets/crystal-CU0NkcVL.js +0 -1
- package/dist/apps/chat-ui/assets/css-CbMZAQ1W.js +0 -1
- package/dist/apps/chat-ui/assets/dist-9v2ZXT6V.js +0 -11
- package/dist/apps/chat-ui/assets/dist-B1LVAytl.js +0 -9
- package/dist/apps/chat-ui/assets/dist-B5PTVnox.js +0 -1
- package/dist/apps/chat-ui/assets/dist-B5ZAfwf5.js +0 -1
- package/dist/apps/chat-ui/assets/dist-D1KyNDg5.js +0 -13
- package/dist/apps/chat-ui/assets/dist-DVtXxfHc.js +0 -1
- package/dist/apps/chat-ui/assets/dist-DW4Qrfbd.js +0 -2
- package/dist/apps/chat-ui/assets/dist-DmbsHdMC.js +0 -1
- package/dist/apps/chat-ui/assets/dist-DyvwuSFO.js +0 -1
- package/dist/apps/chat-ui/assets/dist-QQuDy4KA.js +0 -1
- package/dist/apps/chat-ui/assets/dist-RtHbJRPV.js +0 -1
- package/dist/apps/chat-ui/assets/groovy-Cmduloe1.js +0 -1
- package/dist/apps/chat-ui/assets/index-BPbNvcZe.css +0 -1
- package/dist/apps/chat-ui/assets/index-CIOf2uL2.js +0 -237
- package/dist/apps/chat-ui/assets/javascript-C5pDVaoP.js +0 -1
- package/dist/apps/chat-ui/assets/perl-D6YJObi_.js +0 -1
- package/dist/apps/chat-ui/assets/puppet-B_0GFuyD.js +0 -1
- package/dist/apps/chat-ui/assets/python-Cs7sNzy_.js +0 -1
- package/dist/apps/chat-ui/assets/r-CN5s4f3o.js +0 -1
- package/dist/apps/chat-ui/assets/ruby-BVPradRk.js +0 -1
- package/dist/apps/chat-ui/assets/scheme-CvNgHAnh.js +0 -1
- package/dist/apps/chat-ui/assets/sql-DYI2GwJR.js +0 -1
- package/dist/apps/chat-ui/assets/textile-Yg_UWn-t.js +0 -1
- package/dist/apps/chat-ui/assets/tiki-CC_ic8g0.js +0 -1
- package/dist/apps/chat-ui/assets/toml-B7gGzVkJ.js +0 -1
- package/dist/apps/chat-ui/assets/turtle-CwIpz2yy.js +0 -1
- package/dist/apps/chat-ui/assets/vhdl-BGDk4f1d.js +0 -1
- package/dist/apps/chat-ui/assets/z80-B7Dfb4UO.js +0 -1
- package/dist/apps/chat-vscode-web/assets/index-1ivRyjFs.js +0 -41
- package/dist/apps/chat-vscode-web/assets/index-Dupu1DzQ.css +0 -2
- package/dist/apps/context-files-ui/assets/asciiarmor-WLvjqI7d.js +0 -1
- package/dist/apps/context-files-ui/assets/clike-CjmcUpt6.js +0 -1
- package/dist/apps/context-files-ui/assets/cmake-i2VDNaRq.js +0 -1
- package/dist/apps/context-files-ui/assets/cobol-BwO6DF08.js +0 -1
- package/dist/apps/context-files-ui/assets/coffeescript-BtivDvRz.js +0 -1
- package/dist/apps/context-files-ui/assets/commonlisp-DFkEQdLP.js +0 -1
- package/dist/apps/context-files-ui/assets/crystal-CU0NkcVL.js +0 -1
- package/dist/apps/context-files-ui/assets/css-CbMZAQ1W.js +0 -1
- package/dist/apps/context-files-ui/assets/dist-B5PTVnox.js +0 -1
- package/dist/apps/context-files-ui/assets/dist-B5ZAfwf5.js +0 -1
- package/dist/apps/context-files-ui/assets/dist-BD262Ouw.js +0 -1
- package/dist/apps/context-files-ui/assets/dist-BKuGdcnX.js +0 -9
- package/dist/apps/context-files-ui/assets/dist-CHdx1Jri.js +0 -1
- package/dist/apps/context-files-ui/assets/dist-CULMH-fB.js +0 -11
- package/dist/apps/context-files-ui/assets/dist-Cjq6RWEt.js +0 -1
- package/dist/apps/context-files-ui/assets/dist-CntqFySz.js +0 -1
- package/dist/apps/context-files-ui/assets/dist-D1KyNDg5.js +0 -13
- package/dist/apps/context-files-ui/assets/dist-QQuDy4KA.js +0 -1
- package/dist/apps/context-files-ui/assets/dist-kPuQud7t.js +0 -2
- package/dist/apps/context-files-ui/assets/groovy-Cmduloe1.js +0 -1
- package/dist/apps/context-files-ui/assets/index-DZWsbuvg.js +0 -202
- package/dist/apps/context-files-ui/assets/javascript-C5pDVaoP.js +0 -1
- package/dist/apps/context-files-ui/assets/perl-D6YJObi_.js +0 -1
- package/dist/apps/context-files-ui/assets/puppet-B_0GFuyD.js +0 -1
- package/dist/apps/context-files-ui/assets/python-Cs7sNzy_.js +0 -1
- package/dist/apps/context-files-ui/assets/r-CN5s4f3o.js +0 -1
- package/dist/apps/context-files-ui/assets/ruby-BVPradRk.js +0 -1
- package/dist/apps/context-files-ui/assets/scheme-CvNgHAnh.js +0 -1
- package/dist/apps/context-files-ui/assets/sql-DYI2GwJR.js +0 -1
- package/dist/apps/context-files-ui/assets/textile-Yg_UWn-t.js +0 -1
- package/dist/apps/context-files-ui/assets/tiki-CC_ic8g0.js +0 -1
- package/dist/apps/context-files-ui/assets/toml-B7gGzVkJ.js +0 -1
- package/dist/apps/context-files-ui/assets/turtle-CwIpz2yy.js +0 -1
- package/dist/apps/context-files-ui/assets/vhdl-BGDk4f1d.js +0 -1
- package/dist/apps/context-files-ui/assets/z80-B7Dfb4UO.js +0 -1
- package/dist/apps/vscode-artifacts/pibo-vscode-ext-2.0.0.vsix +0 -0
- /package/dist/apps/chat-ui/assets/{brainfuck-CV9kufud.js → brainfuck-D5EjA2JK.js} +0 -0
- /package/dist/apps/chat-ui/assets/{clojure-De3QGfx6.js → clojure-BflJGmDX.js} +0 -0
- /package/dist/apps/chat-ui/assets/{cypher-Dke5QOzv.js → cypher-p9eYesGn.js} +0 -0
- /package/dist/apps/chat-ui/assets/{diff-B365LSoZ.js → diff-ChtP43wD.js} +0 -0
- /package/dist/apps/chat-ui/assets/{dtd-CBMymJ7O.js → dtd-DTF72YNO.js} +0 -0
- /package/dist/apps/chat-ui/assets/{ebnf-Dkum_1J8.js → ebnf-D4c0_ac3.js} +0 -0
- /package/dist/apps/chat-ui/assets/{eiffel-C446m6dI.js → eiffel-BEjRio4Q.js} +0 -0
- /package/dist/apps/chat-ui/assets/{elm-po1E9de0.js → elm-Cshzl8qu.js} +0 -0
- /package/dist/apps/chat-ui/assets/{fcl-DkEOamHd.js → fcl-ClOhbFR7.js} +0 -0
- /package/dist/apps/chat-ui/assets/{forth-dCg2_HVB.js → forth-Cezjo90N.js} +0 -0
- /package/dist/apps/chat-ui/assets/{fortran-3ZpXLFh1.js → fortran-Bt6PBEDR.js} +0 -0
- /package/dist/apps/chat-ui/assets/{gas-DMmxLZRI.js → gas-BHEdbvp9.js} +0 -0
- /package/dist/apps/chat-ui/assets/{gherkin-DUSFg2R6.js → gherkin-oBAE_ms0.js} +0 -0
- /package/dist/apps/chat-ui/assets/{haskell-Cyr255R9.js → haskell-BRsoo5mP.js} +0 -0
- /package/dist/apps/chat-ui/assets/{http-DszHFpc-.js → http-CLVgA2GD.js} +0 -0
- /package/dist/apps/chat-ui/assets/{idl-Cati9Nrv.js → idl-BUZw3wgd.js} +0 -0
- /package/dist/apps/chat-ui/assets/{julia-DlqY1cTU.js → julia-CQfgDRfP.js} +0 -0
- /package/dist/apps/chat-ui/assets/{livescript-C6p8-2W1.js → livescript-BXxG0Yva.js} +0 -0
- /package/dist/apps/chat-ui/assets/{mathematica-nDHZUMJT.js → mathematica-u8YMmbU0.js} +0 -0
- /package/dist/apps/chat-ui/assets/{mbox-DAaR0Hcz.js → mbox-BEMVcqjs.js} +0 -0
- /package/dist/apps/chat-ui/assets/{mirc-CCA0FRfl.js → mirc-C9zpzAZU.js} +0 -0
- /package/dist/apps/chat-ui/assets/{mllike-Cw3Iccsb.js → mllike-B45xgp2S.js} +0 -0
- /package/dist/apps/chat-ui/assets/{modelica-Czx80fNR.js → modelica-CqHI_q-D.js} +0 -0
- /package/dist/apps/chat-ui/assets/{mscgen-BmfuTbzG.js → mscgen-qgSLujhx.js} +0 -0
- /package/dist/apps/chat-ui/assets/{nginx-D9M2PQ4U.js → nginx-BMaDbqW3.js} +0 -0
- /package/dist/apps/chat-ui/assets/{octave-8jj96JiC.js → octave-D2Q8cUp1.js} +0 -0
- /package/dist/apps/chat-ui/assets/{oz-BmMq7iC4.js → oz-BwTct_5T.js} +0 -0
- /package/dist/apps/chat-ui/assets/{pascal-BgMr6XpT.js → pascal-Dp_KdFgX.js} +0 -0
- /package/dist/apps/chat-ui/assets/{pig-nx7uchs8.js → pig-AF4Hwhju.js} +0 -0
- /package/dist/apps/chat-ui/assets/{properties-CoavQdLC.js → properties-C357ku5U.js} +0 -0
- /package/dist/apps/chat-ui/assets/{protobuf-D5EWhcmc.js → protobuf-D1ij_kNL.js} +0 -0
- /package/dist/apps/chat-ui/assets/{q-BIB9pPtI.js → q-B9NhS7L_.js} +0 -0
- /package/dist/apps/chat-ui/assets/{rpm-Ct_Ck8mQ.js → rpm-gpDK5sbt.js} +0 -0
- /package/dist/apps/chat-ui/assets/{sas-DN4j74IF.js → sas-C2OyD2Y6.js} +0 -0
- /package/dist/apps/chat-ui/assets/{sieve-C2U1O5Z4.js → sieve-BAxa3IjF.js} +0 -0
- /package/dist/apps/chat-ui/assets/{smalltalk-DkYwYufV.js → smalltalk-BLp5-jwC.js} +0 -0
- /package/dist/apps/chat-ui/assets/{solr-BopnSD-v.js → solr-BBopId9w.js} +0 -0
- /package/dist/apps/chat-ui/assets/{sparql-Cpg3YQfd.js → sparql-Dz-mlCAC.js} +0 -0
- /package/dist/apps/chat-ui/assets/{spreadsheet-kRxtVt9J.js → spreadsheet-CTIncYxj.js} +0 -0
- /package/dist/apps/chat-ui/assets/{swift-DKNaLAK6.js → swift-DKB6_1j6.js} +0 -0
- /package/dist/apps/chat-ui/assets/{tcl-CQyXkgMH.js → tcl-BGfruO1u.js} +0 -0
- /package/dist/apps/chat-ui/assets/{tiddlywiki-dqVbkfX4.js → tiddlywiki-VlUiK86J.js} +0 -0
- /package/dist/apps/chat-ui/assets/{troff-Bq3z6oAu.js → troff-C2dAgh7P.js} +0 -0
- /package/dist/apps/chat-ui/assets/{vb-BmcHUM2C.js → vb-BthdM_4N.js} +0 -0
- /package/dist/apps/chat-ui/assets/{vbscript-Cs9dylre.js → vbscript-D9f6rSsU.js} +0 -0
- /package/dist/apps/chat-ui/assets/{velocity-CBWU0VCy.js → velocity-CqftRsjg.js} +0 -0
- /package/dist/apps/chat-ui/assets/{webidl-egXQEWlC.js → webidl-D5jiJvOU.js} +0 -0
- /package/dist/apps/chat-ui/assets/{yacas-DNs9XkmQ.js → yacas-CXXgtw0p.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{brainfuck-CV9kufud.js → brainfuck-D5EjA2JK.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{clojure-De3QGfx6.js → clojure-BflJGmDX.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{cypher-Dke5QOzv.js → cypher-p9eYesGn.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{diff-B365LSoZ.js → diff-ChtP43wD.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{dtd-CBMymJ7O.js → dtd-DTF72YNO.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{ebnf-Dkum_1J8.js → ebnf-D4c0_ac3.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{eiffel-C446m6dI.js → eiffel-BEjRio4Q.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{elm-po1E9de0.js → elm-Cshzl8qu.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{fcl-DkEOamHd.js → fcl-ClOhbFR7.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{forth-dCg2_HVB.js → forth-Cezjo90N.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{fortran-3ZpXLFh1.js → fortran-Bt6PBEDR.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{gas-DMmxLZRI.js → gas-BHEdbvp9.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{gherkin-DUSFg2R6.js → gherkin-oBAE_ms0.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{haskell-Cyr255R9.js → haskell-BRsoo5mP.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{http-DszHFpc-.js → http-CLVgA2GD.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{idl-Cati9Nrv.js → idl-BUZw3wgd.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{julia-DlqY1cTU.js → julia-CQfgDRfP.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{livescript-C6p8-2W1.js → livescript-BXxG0Yva.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{mathematica-nDHZUMJT.js → mathematica-u8YMmbU0.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{mbox-DAaR0Hcz.js → mbox-BEMVcqjs.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{mirc-CCA0FRfl.js → mirc-C9zpzAZU.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{mllike-Cw3Iccsb.js → mllike-B45xgp2S.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{modelica-Czx80fNR.js → modelica-CqHI_q-D.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{mscgen-BmfuTbzG.js → mscgen-qgSLujhx.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{nginx-D9M2PQ4U.js → nginx-BMaDbqW3.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{octave-8jj96JiC.js → octave-D2Q8cUp1.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{oz-BmMq7iC4.js → oz-BwTct_5T.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{pascal-BgMr6XpT.js → pascal-Dp_KdFgX.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{pig-nx7uchs8.js → pig-AF4Hwhju.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{properties-CoavQdLC.js → properties-C357ku5U.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{protobuf-D5EWhcmc.js → protobuf-D1ij_kNL.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{q-BIB9pPtI.js → q-B9NhS7L_.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{rpm-Ct_Ck8mQ.js → rpm-gpDK5sbt.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{sas-DN4j74IF.js → sas-C2OyD2Y6.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{sieve-C2U1O5Z4.js → sieve-BAxa3IjF.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{smalltalk-DkYwYufV.js → smalltalk-BLp5-jwC.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{solr-BopnSD-v.js → solr-BBopId9w.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{sparql-Cpg3YQfd.js → sparql-Dz-mlCAC.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{spreadsheet-kRxtVt9J.js → spreadsheet-CTIncYxj.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{swift-DKNaLAK6.js → swift-DKB6_1j6.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{tcl-CQyXkgMH.js → tcl-BGfruO1u.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{tiddlywiki-dqVbkfX4.js → tiddlywiki-VlUiK86J.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{troff-Bq3z6oAu.js → troff-C2dAgh7P.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{vb-BmcHUM2C.js → vb-BthdM_4N.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{vbscript-Cs9dylre.js → vbscript-D9f6rSsU.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{velocity-CBWU0VCy.js → velocity-CqftRsjg.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{webidl-egXQEWlC.js → webidl-D5jiJvOU.js} +0 -0
- /package/dist/apps/context-files-ui/assets/{yacas-DNs9XkmQ.js → yacas-CXXgtw0p.js} +0 -0
package/dist/auth/better-auth.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { mkdirSync } from "node:fs";
|
|
1
|
+
import { copyFileSync, existsSync, mkdirSync, rmSync } from "node:fs";
|
|
2
2
|
import { dirname, resolve } from "node:path";
|
|
3
|
-
import { DatabaseSync } from "node:sqlite";
|
|
3
|
+
import { backup, DatabaseSync } from "node:sqlite";
|
|
4
4
|
import { betterAuth } from "better-auth";
|
|
5
5
|
import { getMigrations } from "better-auth/db/migration";
|
|
6
6
|
import { bearer } from "better-auth/plugins";
|
|
7
7
|
import { loadPiboConfig } from "../config/config.js";
|
|
8
|
-
import { piboHomePath } from "../core/pibo-home.js";
|
|
8
|
+
import { ensurePrivatePiboHomeForPath, piboHomePath } from "../core/pibo-home.js";
|
|
9
|
+
import { protectPrivateFileSync } from "../core/private-path.js";
|
|
9
10
|
import { createMachineKeyAuthenticator } from "./machine-keys.js";
|
|
10
11
|
import { createMachineSessionManager } from "./machine-session.js";
|
|
11
12
|
import { createForbiddenAuthError, createUnauthenticatedError } from "./types.js";
|
|
@@ -42,10 +43,167 @@ function createAllowedEmailSet(emails) {
|
|
|
42
43
|
}
|
|
43
44
|
function createDatabase(path) {
|
|
44
45
|
const resolvedPath = path === ":memory:" ? path : resolve(path);
|
|
45
|
-
if (resolvedPath
|
|
46
|
-
|
|
46
|
+
if (resolvedPath === ":memory:")
|
|
47
|
+
return new DatabaseSync(resolvedPath);
|
|
48
|
+
ensurePrivatePiboHomeForPath(resolvedPath);
|
|
49
|
+
mkdirSync(dirname(resolvedPath), { recursive: true });
|
|
50
|
+
const database = new DatabaseSync(resolvedPath);
|
|
51
|
+
protectPrivateFileSync(resolvedPath);
|
|
52
|
+
return database;
|
|
53
|
+
}
|
|
54
|
+
const BETTER_AUTH_SQLITE_TABLES = new Set(["user", "session", "account", "verification"]);
|
|
55
|
+
function quoteSqlIdentifier(value) {
|
|
56
|
+
return `"${value.replaceAll('"', '""')}"`;
|
|
57
|
+
}
|
|
58
|
+
function sqliteColumnType(field) {
|
|
59
|
+
if (Array.isArray(field.type))
|
|
60
|
+
return "text";
|
|
61
|
+
switch (field.type) {
|
|
62
|
+
case "string":
|
|
63
|
+
case "json":
|
|
64
|
+
case "string[]":
|
|
65
|
+
case "number[]":
|
|
66
|
+
return "text";
|
|
67
|
+
case "number":
|
|
68
|
+
case "boolean":
|
|
69
|
+
return "integer";
|
|
70
|
+
case "date":
|
|
71
|
+
return "date";
|
|
72
|
+
default:
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function sqliteDefaultLiteral(value) {
|
|
77
|
+
if (typeof value === "boolean")
|
|
78
|
+
return value ? "1" : "0";
|
|
79
|
+
if (typeof value === "number") {
|
|
80
|
+
if (!Number.isFinite(value))
|
|
81
|
+
throw new Error("Better Auth migration default must be finite");
|
|
82
|
+
return String(value);
|
|
83
|
+
}
|
|
84
|
+
return `'${value.replaceAll("'", "''")}'`;
|
|
85
|
+
}
|
|
86
|
+
function safeRequiredColumnDefault(fieldName, field, migrationTimestamp) {
|
|
87
|
+
if (field.required === false || field.references || field.unique)
|
|
88
|
+
return undefined;
|
|
89
|
+
const declaredDefault = field.defaultValue;
|
|
90
|
+
if (typeof declaredDefault === "string" || typeof declaredDefault === "boolean")
|
|
91
|
+
return declaredDefault;
|
|
92
|
+
if (typeof declaredDefault === "number" && Number.isFinite(declaredDefault))
|
|
93
|
+
return declaredDefault;
|
|
94
|
+
if (declaredDefault instanceof Date && Number.isFinite(declaredDefault.getTime()))
|
|
95
|
+
return declaredDefault.toISOString();
|
|
96
|
+
if ((fieldName === "createdAt" || fieldName === "updatedAt") && field.type === "date") {
|
|
97
|
+
return migrationTimestamp;
|
|
98
|
+
}
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
function tableRowCount(database, table) {
|
|
102
|
+
const row = database.prepare(`SELECT COUNT(*) AS count FROM ${quoteSqlIdentifier(table)}`).get();
|
|
103
|
+
return Number(row.count);
|
|
104
|
+
}
|
|
105
|
+
function repairSafeRequiredColumns(database, pendingAdditions) {
|
|
106
|
+
const migrationTimestamp = new Date().toISOString();
|
|
107
|
+
const statements = [];
|
|
108
|
+
const unsafeColumns = [];
|
|
109
|
+
for (const addition of pendingAdditions) {
|
|
110
|
+
if (tableRowCount(database, addition.table) === 0)
|
|
111
|
+
continue;
|
|
112
|
+
for (const [fieldName, field] of Object.entries(addition.fields)) {
|
|
113
|
+
if (field.required === false)
|
|
114
|
+
continue;
|
|
115
|
+
const columnType = sqliteColumnType(field);
|
|
116
|
+
const defaultValue = safeRequiredColumnDefault(fieldName, field, migrationTimestamp);
|
|
117
|
+
if (!columnType || defaultValue === undefined) {
|
|
118
|
+
unsafeColumns.push(`${addition.table}.${fieldName}`);
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
statements.push(`ALTER TABLE ${quoteSqlIdentifier(addition.table)} ADD COLUMN ${quoteSqlIdentifier(fieldName)} ${columnType} NOT NULL DEFAULT ${sqliteDefaultLiteral(defaultValue)}`);
|
|
122
|
+
if (field.index) {
|
|
123
|
+
const indexName = `${addition.table}_${fieldName}_idx`;
|
|
124
|
+
statements.push(`CREATE INDEX IF NOT EXISTS ${quoteSqlIdentifier(indexName)} ON ${quoteSqlIdentifier(addition.table)} (${quoteSqlIdentifier(fieldName)})`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (unsafeColumns.length > 0 || statements.length === 0)
|
|
129
|
+
return unsafeColumns;
|
|
130
|
+
database.exec("BEGIN IMMEDIATE");
|
|
131
|
+
try {
|
|
132
|
+
for (const statement of statements)
|
|
133
|
+
database.exec(statement);
|
|
134
|
+
database.exec("COMMIT");
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
database.exec("ROLLBACK");
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
function isSqliteRequiredColumnMigrationError(error) {
|
|
143
|
+
return error instanceof Error && error.message.includes("Cannot add a NOT NULL column with default value NULL");
|
|
144
|
+
}
|
|
145
|
+
function nextRecoveryBackupPath(databasePath) {
|
|
146
|
+
const timestamp = new Date().toISOString().replaceAll(":", "-").replaceAll(".", "-");
|
|
147
|
+
const base = `${databasePath}.pibo-auth-recovery-${timestamp}`;
|
|
148
|
+
for (let attempt = 0; attempt < 100; attempt += 1) {
|
|
149
|
+
const suffix = attempt === 0 ? "" : `-${attempt + 1}`;
|
|
150
|
+
const candidate = `${base}${suffix}.sqlite`;
|
|
151
|
+
if (!existsSync(candidate))
|
|
152
|
+
return candidate;
|
|
153
|
+
}
|
|
154
|
+
throw new Error(`Could not allocate a unique Better Auth recovery backup beside ${databasePath}`);
|
|
155
|
+
}
|
|
156
|
+
function removeSqliteDatabaseFiles(databasePath) {
|
|
157
|
+
for (const path of [databasePath, `${databasePath}-wal`, `${databasePath}-shm`, `${databasePath}-journal`]) {
|
|
158
|
+
rmSync(path, { force: true });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function makePrivateFile(path) {
|
|
162
|
+
protectPrivateFileSync(path, { force: true });
|
|
163
|
+
}
|
|
164
|
+
function assertAuthOnlyRecoveryDatabase(database) {
|
|
165
|
+
const tables = database.prepare(`
|
|
166
|
+
SELECT name
|
|
167
|
+
FROM sqlite_master
|
|
168
|
+
WHERE type = 'table' AND name NOT LIKE 'sqlite_%'
|
|
169
|
+
`).all();
|
|
170
|
+
if (tables.some((table) => !BETTER_AUTH_SQLITE_TABLES.has(table.name))) {
|
|
171
|
+
throw new Error("Better Auth SQLite recovery refused to replace a database that contains non-auth tables.");
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/** @internal Exported for deterministic recovery failure injection. */
|
|
175
|
+
export async function recoverBetterAuthSqliteDatabase(input) {
|
|
176
|
+
if (input.databasePath === ":memory:") {
|
|
177
|
+
throw new Error("Better Auth SQLite schema requires recovery, but an in-memory database cannot be preserved");
|
|
178
|
+
}
|
|
179
|
+
assertAuthOnlyRecoveryDatabase(input.failedRuntime.database);
|
|
180
|
+
const backupPath = nextRecoveryBackupPath(input.databasePath);
|
|
181
|
+
await backup(input.failedRuntime.database, backupPath);
|
|
182
|
+
makePrivateFile(backupPath);
|
|
183
|
+
input.failedRuntime.database.close();
|
|
184
|
+
removeSqliteDatabaseFiles(input.databasePath);
|
|
185
|
+
let replacement;
|
|
186
|
+
try {
|
|
187
|
+
replacement = input.createRuntime();
|
|
188
|
+
await input.migrateRuntime(replacement);
|
|
189
|
+
makePrivateFile(input.databasePath);
|
|
190
|
+
return { runtime: replacement, backupPath };
|
|
191
|
+
}
|
|
192
|
+
catch (recoveryError) {
|
|
193
|
+
try {
|
|
194
|
+
replacement?.database.close();
|
|
195
|
+
}
|
|
196
|
+
catch { }
|
|
197
|
+
removeSqliteDatabaseFiles(input.databasePath);
|
|
198
|
+
try {
|
|
199
|
+
copyFileSync(backupPath, input.databasePath);
|
|
200
|
+
makePrivateFile(input.databasePath);
|
|
201
|
+
}
|
|
202
|
+
catch (restoreError) {
|
|
203
|
+
throw new AggregateError([recoveryError, restoreError], `Pibo could not create a fresh Better Auth database or restore the original. The protected recovery backup remains at "${backupPath}".`);
|
|
204
|
+
}
|
|
205
|
+
throw new Error(`Pibo could not create a fresh Better Auth database. The original was restored and the protected recovery backup remains at "${backupPath}".`, { cause: recoveryError });
|
|
47
206
|
}
|
|
48
|
-
return new DatabaseSync(resolvedPath);
|
|
49
207
|
}
|
|
50
208
|
function requiredAllowedEmails(options, configAllowedEmails) {
|
|
51
209
|
const allowedEmails = options.allowedEmails !== undefined
|
|
@@ -68,28 +226,50 @@ export function createBetterAuthService(options = {}) {
|
|
|
68
226
|
const allowedEmails = requiredAllowedEmails(options, authConfig?.allowedEmails);
|
|
69
227
|
const machineKeys = createMachineKeyAuthenticator(options.machineKeyStorePath ?? authConfig?.machineKeyStorePath);
|
|
70
228
|
const machineSessions = createMachineSessionManager({ secret, machineKeys });
|
|
71
|
-
const
|
|
229
|
+
const configuredDatabasePath = options.databasePath ?? authConfig?.databasePath ?? piboHomePath("auth.sqlite");
|
|
230
|
+
const databasePath = configuredDatabasePath === ":memory:" ? configuredDatabasePath : resolve(configuredDatabasePath);
|
|
72
231
|
const trustedOrigins = options.trustedOrigins ?? authConfig?.trustedOrigins;
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
clientSecret: googleClientSecret,
|
|
87
|
-
prompt: "select_account",
|
|
232
|
+
const createRuntime = () => {
|
|
233
|
+
const database = createDatabase(databasePath);
|
|
234
|
+
if (databasePath !== ":memory:")
|
|
235
|
+
makePrivateFile(databasePath);
|
|
236
|
+
const authOptions = {
|
|
237
|
+
appName: "Pibo",
|
|
238
|
+
baseURL,
|
|
239
|
+
secret,
|
|
240
|
+
database,
|
|
241
|
+
trustedOrigins: createTrustedOrigins(baseURL, trustedOrigins),
|
|
242
|
+
session: {
|
|
243
|
+
expiresIn: SESSION_EXPIRES_IN_SECONDS,
|
|
244
|
+
updateAge: SESSION_UPDATE_AGE_SECONDS,
|
|
88
245
|
},
|
|
89
|
-
|
|
90
|
-
|
|
246
|
+
socialProviders: {
|
|
247
|
+
google: {
|
|
248
|
+
clientId: googleClientId,
|
|
249
|
+
clientSecret: googleClientSecret,
|
|
250
|
+
prompt: "select_account",
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
plugins: [bearer()],
|
|
254
|
+
};
|
|
255
|
+
return { database, authOptions, auth: betterAuth(authOptions) };
|
|
256
|
+
};
|
|
257
|
+
let runtime = createRuntime();
|
|
258
|
+
const recoverAuthDatabase = async (failedRuntime) => {
|
|
259
|
+
const recovered = await recoverBetterAuthSqliteDatabase({
|
|
260
|
+
databasePath,
|
|
261
|
+
failedRuntime,
|
|
262
|
+
createRuntime,
|
|
263
|
+
migrateRuntime: async (replacement) => {
|
|
264
|
+
const migrations = await getMigrations(replacement.authOptions);
|
|
265
|
+
await migrations.runMigrations();
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
console.warn(`[pibo] Better Auth SQLite schema was incompatible with a safe in-place migration. `
|
|
269
|
+
+ `Pibo preserved a protected backup at "${recovered.backupPath}", created a fresh authentication database, `
|
|
270
|
+
+ "and reset existing browser sessions. Sign in again.");
|
|
271
|
+
return recovered.runtime;
|
|
91
272
|
};
|
|
92
|
-
const auth = betterAuth(authOptions);
|
|
93
273
|
const requireAllowedMachineSession = (session) => {
|
|
94
274
|
const email = session.identity.email?.toLowerCase();
|
|
95
275
|
if (!email || !allowedEmails.has(email))
|
|
@@ -99,17 +279,30 @@ export function createBetterAuthService(options = {}) {
|
|
|
99
279
|
return {
|
|
100
280
|
name: "better-auth",
|
|
101
281
|
async start() {
|
|
102
|
-
|
|
103
|
-
|
|
282
|
+
let migrations = await getMigrations(runtime.authOptions);
|
|
283
|
+
const unsafeColumns = repairSafeRequiredColumns(runtime.database, migrations.toBeAdded);
|
|
284
|
+
if (unsafeColumns.length > 0) {
|
|
285
|
+
runtime = await recoverAuthDatabase(runtime);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
migrations = await getMigrations(runtime.authOptions);
|
|
289
|
+
try {
|
|
290
|
+
await migrations.runMigrations();
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
if (!isSqliteRequiredColumnMigrationError(error))
|
|
294
|
+
throw error;
|
|
295
|
+
runtime = await recoverAuthDatabase(runtime);
|
|
296
|
+
}
|
|
104
297
|
},
|
|
105
298
|
stop() {
|
|
106
|
-
database.close();
|
|
299
|
+
runtime.database.close();
|
|
107
300
|
},
|
|
108
301
|
async getSession(headers) {
|
|
109
302
|
const machineSession = machineKeys.getSession(headers) ?? machineSessions.getSession(headers);
|
|
110
303
|
if (machineSession)
|
|
111
304
|
return requireAllowedMachineSession(machineSession);
|
|
112
|
-
const session = await auth.api.getSession({ headers });
|
|
305
|
+
const session = await runtime.auth.api.getSession({ headers });
|
|
113
306
|
if (!session)
|
|
114
307
|
return undefined;
|
|
115
308
|
const user = session.user;
|
|
@@ -172,7 +365,7 @@ export function createBetterAuthService(options = {}) {
|
|
|
172
365
|
},
|
|
173
366
|
});
|
|
174
367
|
}
|
|
175
|
-
return auth.handler(request);
|
|
368
|
+
return runtime.auth.handler(request);
|
|
176
369
|
},
|
|
177
370
|
};
|
|
178
371
|
}
|
package/dist/auth/cli.js
CHANGED
|
@@ -4,6 +4,7 @@ import { dirname, resolve } from "node:path";
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { loadPiboConfig } from "../config/config.js";
|
|
6
6
|
import { piboHomePath } from "../core/pibo-home.js";
|
|
7
|
+
import { protectPrivateFileSync } from "../core/private-path.js";
|
|
7
8
|
import { generateMachineKey, getDefaultMachineKeyStorePath, importMachineKeyRecord, listMachineKeys, parseMachineKeyRecord, revokeMachineKey, } from "./machine-keys.js";
|
|
8
9
|
function printJson(value) {
|
|
9
10
|
console.log(JSON.stringify(value, null, 2));
|
|
@@ -103,7 +104,15 @@ function requireNewOutputPath(path, label) {
|
|
|
103
104
|
}
|
|
104
105
|
function writePrivateNewFile(path, content) {
|
|
105
106
|
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
106
|
-
|
|
107
|
+
try {
|
|
108
|
+
writeFileSync(path, content, { encoding: "utf8", mode: 0o600, flag: "wx" });
|
|
109
|
+
protectPrivateFileSync(path, { force: true });
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
if (existsSync(path))
|
|
113
|
+
unlinkSync(path);
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
107
116
|
}
|
|
108
117
|
function parseExpiresAt(value) {
|
|
109
118
|
if (value === undefined)
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
|
|
2
|
-
import {
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
3
3
|
import { dirname, resolve } from "node:path";
|
|
4
|
-
import { piboHomePath } from "../core/pibo-home.js";
|
|
4
|
+
import { ensurePrivatePiboHomeForPath, piboHomePath } from "../core/pibo-home.js";
|
|
5
|
+
import { protectPrivateFileSync } from "../core/private-path.js";
|
|
5
6
|
export const PIBO_MACHINE_KEY_HEADER = "x-pibo-machine-key";
|
|
6
7
|
export const MACHINE_KEY_STORE_VERSION = 1;
|
|
7
8
|
const MACHINE_KEY_ID_PATTERN = /^[a-f0-9]{16}$/;
|
|
@@ -108,8 +109,10 @@ function parseMachineKeyStore(value) {
|
|
|
108
109
|
return { version: MACHINE_KEY_STORE_VERSION, keys };
|
|
109
110
|
}
|
|
110
111
|
function assertPrivateStoreFile(path) {
|
|
111
|
-
if (process.platform === "win32")
|
|
112
|
+
if (process.platform === "win32") {
|
|
113
|
+
protectPrivateFileSync(path, { force: true });
|
|
112
114
|
return;
|
|
115
|
+
}
|
|
113
116
|
const stat = statSync(path);
|
|
114
117
|
if ((stat.mode & 0o077) !== 0) {
|
|
115
118
|
throw new Error(`Machine-key store must not be accessible by group or other users: ${path}`);
|
|
@@ -128,12 +131,13 @@ export function readMachineKeyStore(path = getDefaultMachineKeyStorePath()) {
|
|
|
128
131
|
}
|
|
129
132
|
function writeMachineKeyStore(store, path) {
|
|
130
133
|
const resolvedPath = resolve(path);
|
|
134
|
+
ensurePrivatePiboHomeForPath(resolvedPath);
|
|
131
135
|
mkdirSync(dirname(resolvedPath), { recursive: true, mode: 0o700 });
|
|
132
136
|
const temporaryPath = `${resolvedPath}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
|
|
133
137
|
try {
|
|
134
138
|
writeFileSync(temporaryPath, `${JSON.stringify(store, null, 2)}\n`, { encoding: "utf8", mode: 0o600, flag: "wx" });
|
|
135
139
|
renameSync(temporaryPath, resolvedPath);
|
|
136
|
-
|
|
140
|
+
protectPrivateFileSync(resolvedPath, { force: true });
|
|
137
141
|
}
|
|
138
142
|
finally {
|
|
139
143
|
if (existsSync(temporaryPath))
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readPiCredential, resolvePiProviderAuth, } from "../agent-runtimes/pi/credentials.js";
|
|
2
2
|
const OPENAI_CODEX_PROVIDER = "openai-codex";
|
|
3
3
|
const OPENAI_CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
|
|
4
4
|
const OPENAI_JWT_CLAIM_PATH = "https://api.openai.com/auth";
|
|
@@ -124,11 +124,11 @@ function normalizeUsagePayload(payload) {
|
|
|
124
124
|
export async function getOpenAiCodexProviderUsageForActiveModel(activeModel) {
|
|
125
125
|
if (activeModel?.provider !== OPENAI_CODEX_PROVIDER)
|
|
126
126
|
return undefined;
|
|
127
|
-
const
|
|
128
|
-
const credential = authStorage.get(OPENAI_CODEX_PROVIDER);
|
|
127
|
+
const credential = await readPiCredential(OPENAI_CODEX_PROVIDER);
|
|
129
128
|
if (credential?.type !== "oauth")
|
|
130
129
|
return undefined;
|
|
131
|
-
const
|
|
130
|
+
const resolvedAuth = await resolvePiProviderAuth(OPENAI_CODEX_PROVIDER);
|
|
131
|
+
const accessToken = resolvedAuth?.auth.apiKey;
|
|
132
132
|
if (!accessToken)
|
|
133
133
|
return undefined;
|
|
134
134
|
const headers = {
|
|
@@ -774,10 +774,10 @@ function fakeUploadResult(args) {
|
|
|
774
774
|
if (!target)
|
|
775
775
|
return {
|
|
776
776
|
supported: false,
|
|
777
|
-
unsupportedReason: "Usage: /upload <path>. Browser file picker upload is Web-only; copy files to ~/.pibo/uploads from the shell.",
|
|
777
|
+
unsupportedReason: "Usage: /upload <path>. Browser file picker upload is Web-only; copy files to $PIBO_HOME/uploads (default ~/.pibo/uploads) from the shell.",
|
|
778
778
|
};
|
|
779
779
|
return {
|
|
780
|
-
message: `Terminal upload for ${target}: copy the file to ~/.pibo/uploads or use Web /upload. The CLI did not copy file contents.`,
|
|
780
|
+
message: `Terminal upload for ${target}: copy the file to $PIBO_HOME/uploads (default ~/.pibo/uploads) or use Web /upload. The CLI did not copy file contents.`,
|
|
781
781
|
};
|
|
782
782
|
}
|
|
783
783
|
function sessionIdFromActionResult(value) {
|
|
@@ -1113,10 +1113,10 @@ function terminalUploadResult(args) {
|
|
|
1113
1113
|
if (!target)
|
|
1114
1114
|
return {
|
|
1115
1115
|
supported: false,
|
|
1116
|
-
unsupportedReason: "Usage: /upload <path>. Browser file picker upload is Web-only; from SSH, copy files with scp/rsync or place them under ~/.pibo/uploads.",
|
|
1116
|
+
unsupportedReason: "Usage: /upload <path>. Browser file picker upload is Web-only; from SSH, copy files with scp/rsync or place them under $PIBO_HOME/uploads (default ~/.pibo/uploads).",
|
|
1117
1117
|
};
|
|
1118
1118
|
return {
|
|
1119
|
-
message: `Terminal upload for ${target}: copy or move the file to ~/.pibo/uploads, or use Web /upload for browser file selection. The CLI did not copy file contents or echo secrets.`,
|
|
1119
|
+
message: `Terminal upload for ${target}: copy or move the file to $PIBO_HOME/uploads (default ~/.pibo/uploads), or use Web /upload for browser file selection. The CLI did not copy file contents or echo secrets.`,
|
|
1120
1120
|
};
|
|
1121
1121
|
}
|
|
1122
1122
|
function sessionMetadataResult(session, defaultRoomId, roomTitle) {
|
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import { PIBO_CONFIG_KEYS, getDefaultPiboConfigPath, deletePiboConfigValue, getDisplayPiboConfigValue, loadPiboConfig, redactPiboConfig, savePiboConfig, setPiboConfigValue, } from "./config/config.js";
|
|
4
4
|
import { parsePiboThinkingLevel } from "./core/thinking.js";
|
|
5
|
-
import { piboHomePath } from "./core/pibo-home.js";
|
|
5
|
+
import { ensurePrivatePiboHome, piboHomePath } from "./core/pibo-home.js";
|
|
6
6
|
async function createCliProfile(profileName) {
|
|
7
7
|
const { createDefaultPiboPluginRegistry, createGatewayProducerPiboProfile, createPiboProfileFromRegistryOrDefault } = await import("./plugins/builtin.js");
|
|
8
8
|
if (profileName === "gateway-producer" || profileName === "pibo-gateway-producer") {
|
|
@@ -80,6 +80,8 @@ export async function runPiboCli(argv = process.argv) {
|
|
|
80
80
|
printPiboVersion();
|
|
81
81
|
return;
|
|
82
82
|
}
|
|
83
|
+
if (argv.length > 2)
|
|
84
|
+
ensurePrivatePiboHome();
|
|
83
85
|
if (argv[2] === "auth") {
|
|
84
86
|
const { runAuthCli } = await import("./auth/cli.js");
|
|
85
87
|
await runAuthCli([argv[0] ?? "node", "pibo auth", ...argv.slice(3)]);
|
package/dist/config/config.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname, resolve } from "node:path";
|
|
3
|
-
import { piboHomePath } from "../core/pibo-home.js";
|
|
3
|
+
import { ensurePrivatePiboHomeForPath, piboHomePath } from "../core/pibo-home.js";
|
|
4
|
+
import { protectPrivateFileSync } from "../core/private-path.js";
|
|
4
5
|
export const DEFAULT_PIBO_CONFIG_PATH = "config.json";
|
|
5
6
|
export function getDefaultPiboConfigPath() {
|
|
6
7
|
return piboHomePath(DEFAULT_PIBO_CONFIG_PATH);
|
|
@@ -104,13 +105,17 @@ export function loadPiboConfig(path) {
|
|
|
104
105
|
const resolvedPath = resolve(path ?? getDefaultPiboConfigPath());
|
|
105
106
|
if (!existsSync(resolvedPath))
|
|
106
107
|
return {};
|
|
107
|
-
|
|
108
|
+
protectPrivateFileSync(resolvedPath);
|
|
109
|
+
const source = readFileSync(resolvedPath, "utf-8").replace(/^\uFEFF/, "");
|
|
110
|
+
const parsed = JSON.parse(source);
|
|
108
111
|
return assertObject(parsed);
|
|
109
112
|
}
|
|
110
113
|
export function savePiboConfig(config, path) {
|
|
111
114
|
const resolvedPath = resolve(path ?? getDefaultPiboConfigPath());
|
|
112
|
-
|
|
113
|
-
|
|
115
|
+
ensurePrivatePiboHomeForPath(resolvedPath);
|
|
116
|
+
mkdirSync(dirname(resolvedPath), { recursive: true, mode: 0o700 });
|
|
117
|
+
writeFileSync(resolvedPath, `${JSON.stringify(config, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
118
|
+
protectPrivateFileSync(resolvedPath, { force: true });
|
|
114
119
|
}
|
|
115
120
|
export function getPiboConfigValue(config, key) {
|
|
116
121
|
getKeyDefinition(key);
|
|
@@ -275,7 +275,9 @@ export function createPiboCompactionPromptExtension(options = {}) {
|
|
|
275
275
|
spec: await readActivePiboCompactionPromptSpec(ctx.cwd),
|
|
276
276
|
model: ctx.model,
|
|
277
277
|
apiKey: auth.apiKey,
|
|
278
|
-
headers: auth.headers
|
|
278
|
+
headers: auth.headers
|
|
279
|
+
? Object.fromEntries(Object.entries(auth.headers).filter((entry) => typeof entry[1] === "string"))
|
|
280
|
+
: undefined,
|
|
279
281
|
customInstructions: event.customInstructions,
|
|
280
282
|
signal: event.signal,
|
|
281
283
|
thinkingLevel: sessionContext.thinkingLevel,
|
|
@@ -180,6 +180,7 @@ function withoutRequestedModels(profile) {
|
|
|
180
180
|
builtinTools: profile.builtinTools,
|
|
181
181
|
builtinToolNames: profile.builtinToolNames,
|
|
182
182
|
autoContextFiles: profile.autoContextFiles,
|
|
183
|
+
nativeSubagents: profile.nativeSubagents,
|
|
183
184
|
toolPackages: profile.toolPackages,
|
|
184
185
|
});
|
|
185
186
|
}
|
package/dist/core/pibo-home.js
CHANGED
|
@@ -1,8 +1,31 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, statSync } from "node:fs";
|
|
1
2
|
import { homedir } from "node:os";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
+
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
4
|
+
import { protectPrivateDirectorySync } from "./private-path.js";
|
|
3
5
|
export function getPiboHome() {
|
|
4
6
|
return process.env.PIBO_HOME || join(homedir(), ".pibo");
|
|
5
7
|
}
|
|
6
8
|
export function piboHomePath(...segments) {
|
|
7
9
|
return join(getPiboHome(), ...segments);
|
|
8
10
|
}
|
|
11
|
+
export function ensurePrivatePiboHome(path = getPiboHome()) {
|
|
12
|
+
const resolvedPath = resolve(path);
|
|
13
|
+
if (existsSync(resolvedPath) && !statSync(resolvedPath).isDirectory()) {
|
|
14
|
+
throw new Error(`Pibo Home must be a directory: ${resolvedPath}`);
|
|
15
|
+
}
|
|
16
|
+
mkdirSync(resolvedPath, { recursive: true, mode: 0o700 });
|
|
17
|
+
protectPrivateDirectorySync(resolvedPath);
|
|
18
|
+
return resolvedPath;
|
|
19
|
+
}
|
|
20
|
+
export function ensurePrivatePiboHomeForPath(path) {
|
|
21
|
+
if (path === ":memory:")
|
|
22
|
+
return false;
|
|
23
|
+
const home = resolve(getPiboHome());
|
|
24
|
+
const target = resolve(path);
|
|
25
|
+
const relativePath = relative(home, target);
|
|
26
|
+
const outsideHome = relativePath === ".." || relativePath.startsWith(`..${sep}`) || isAbsolute(relativePath);
|
|
27
|
+
if (outsideHome)
|
|
28
|
+
return false;
|
|
29
|
+
ensurePrivatePiboHome(home);
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { chmodSync, lstatSync, readdirSync, statSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
const protectedWindowsPaths = new Set();
|
|
5
|
+
const WINDOWS_PRIVATE_ACL_SCRIPT = `
|
|
6
|
+
$ErrorActionPreference = 'Stop'
|
|
7
|
+
$ProgressPreference = 'SilentlyContinue'
|
|
8
|
+
$entries = @((ConvertFrom-Json -InputObject $env:PIBO_PRIVATE_PATHS_JSON))
|
|
9
|
+
$current = [System.Security.Principal.WindowsIdentity]::GetCurrent().User
|
|
10
|
+
$system = New-Object System.Security.Principal.SecurityIdentifier('S-1-5-18')
|
|
11
|
+
$administrators = New-Object System.Security.Principal.SecurityIdentifier('S-1-5-32-544')
|
|
12
|
+
$allowed = @($current.Value, $system.Value, $administrators.Value)
|
|
13
|
+
$inheritBoth = [System.Security.AccessControl.InheritanceFlags]([int][System.Security.AccessControl.InheritanceFlags]::ContainerInherit -bor [int][System.Security.AccessControl.InheritanceFlags]::ObjectInherit)
|
|
14
|
+
foreach ($entry in $entries) {
|
|
15
|
+
$path = [System.IO.Path]::GetFullPath([string]$entry.path)
|
|
16
|
+
$kind = [string]$entry.kind
|
|
17
|
+
$sections = [System.Security.AccessControl.AccessControlSections]([int][System.Security.AccessControl.AccessControlSections]::Access -bor [int][System.Security.AccessControl.AccessControlSections]::Owner)
|
|
18
|
+
if ($kind -eq 'directory') {
|
|
19
|
+
if (-not [System.IO.Directory]::Exists($path)) { throw "Private directory does not exist" }
|
|
20
|
+
$security = [System.IO.Directory]::GetAccessControl($path, $sections)
|
|
21
|
+
$inheritance = $inheritBoth
|
|
22
|
+
} elseif ($kind -eq 'file') {
|
|
23
|
+
if (-not [System.IO.File]::Exists($path)) { throw "Private file does not exist" }
|
|
24
|
+
$security = [System.IO.File]::GetAccessControl($path, $sections)
|
|
25
|
+
$inheritance = [System.Security.AccessControl.InheritanceFlags]::None
|
|
26
|
+
} else {
|
|
27
|
+
throw "Unsupported private path kind"
|
|
28
|
+
}
|
|
29
|
+
if ($security.GetOwner([System.Security.Principal.SecurityIdentifier]).Value -ne $current.Value) { throw "Private path owner is not the current user" }
|
|
30
|
+
$security.SetAccessRuleProtection($true, $false)
|
|
31
|
+
foreach ($rule in @($security.GetAccessRules($true, $false, [System.Security.Principal.SecurityIdentifier]))) {
|
|
32
|
+
$security.RemoveAccessRuleAll($rule)
|
|
33
|
+
}
|
|
34
|
+
foreach ($sid in @($current, $system, $administrators)) {
|
|
35
|
+
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
|
|
36
|
+
$sid,
|
|
37
|
+
[System.Security.AccessControl.FileSystemRights]::FullControl,
|
|
38
|
+
$inheritance,
|
|
39
|
+
[System.Security.AccessControl.PropagationFlags]::None,
|
|
40
|
+
[System.Security.AccessControl.AccessControlType]::Allow
|
|
41
|
+
)
|
|
42
|
+
$security.AddAccessRule($rule) | Out-Null
|
|
43
|
+
}
|
|
44
|
+
if ($kind -eq 'directory') {
|
|
45
|
+
[System.IO.Directory]::SetAccessControl($path, $security)
|
|
46
|
+
} else {
|
|
47
|
+
[System.IO.File]::SetAccessControl($path, $security)
|
|
48
|
+
}
|
|
49
|
+
$applied = Get-Acl -LiteralPath $path
|
|
50
|
+
if (-not $applied.AreAccessRulesProtected) { throw "Private path still inherits access rules" }
|
|
51
|
+
if ($applied.GetOwner([System.Security.Principal.SecurityIdentifier]).Value -ne $current.Value) { throw "Private path owner is not the current user" }
|
|
52
|
+
$seen = @{}
|
|
53
|
+
foreach ($rule in $applied.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier])) {
|
|
54
|
+
$sid = $rule.IdentityReference.Value
|
|
55
|
+
if ($allowed -notcontains $sid) { throw "Private path grants access to an unexpected principal" }
|
|
56
|
+
if ($rule.AccessControlType -ne [System.Security.AccessControl.AccessControlType]::Allow) { throw "Private path contains a deny rule" }
|
|
57
|
+
if (($rule.FileSystemRights -band [System.Security.AccessControl.FileSystemRights]::FullControl) -ne [System.Security.AccessControl.FileSystemRights]::FullControl) { throw "Private path does not grant required full control" }
|
|
58
|
+
$seen[$sid] = $true
|
|
59
|
+
}
|
|
60
|
+
foreach ($sid in $allowed) {
|
|
61
|
+
if (-not $seen.ContainsKey($sid)) { throw "Private path is missing a required principal" }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
`;
|
|
65
|
+
const WINDOWS_PRIVATE_ACL_COMMAND = Buffer.from(WINDOWS_PRIVATE_ACL_SCRIPT, "utf16le").toString("base64");
|
|
66
|
+
function protectWindowsPathBatchSync(paths) {
|
|
67
|
+
const result = spawnSync("powershell.exe", ["-NoLogo", "-NoProfile", "-NonInteractive", "-EncodedCommand", WINDOWS_PRIVATE_ACL_COMMAND], {
|
|
68
|
+
encoding: "utf8",
|
|
69
|
+
env: { ...process.env, PIBO_PRIVATE_PATHS_JSON: JSON.stringify(paths) },
|
|
70
|
+
timeout: 15_000,
|
|
71
|
+
windowsHide: true,
|
|
72
|
+
});
|
|
73
|
+
if (result.error || result.status !== 0) {
|
|
74
|
+
const reason = result.error instanceof Error && result.error.message
|
|
75
|
+
? ` (${result.error.message})`
|
|
76
|
+
: "";
|
|
77
|
+
throw new Error(`Could not apply a private Windows ACL${reason}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function protectWindowsPathsSync(paths) {
|
|
81
|
+
let batch = [];
|
|
82
|
+
let batchCharacters = 2;
|
|
83
|
+
for (const descriptor of paths) {
|
|
84
|
+
const characters = JSON.stringify(descriptor).length + 1;
|
|
85
|
+
if (batch.length > 0 && batchCharacters + characters > 24_000) {
|
|
86
|
+
protectWindowsPathBatchSync(batch);
|
|
87
|
+
batch = [];
|
|
88
|
+
batchCharacters = 2;
|
|
89
|
+
}
|
|
90
|
+
batch.push(descriptor);
|
|
91
|
+
batchCharacters += characters;
|
|
92
|
+
}
|
|
93
|
+
if (batch.length > 0)
|
|
94
|
+
protectWindowsPathBatchSync(batch);
|
|
95
|
+
}
|
|
96
|
+
function protectPosixPathSync(path, kind) {
|
|
97
|
+
const expectedMode = kind === "directory" ? 0o700 : 0o600;
|
|
98
|
+
chmodSync(path, expectedMode);
|
|
99
|
+
if ((statSync(path).mode & 0o077) !== 0) {
|
|
100
|
+
throw new Error(`Private ${kind} must not be accessible by group or other users: ${path}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
export function protectPrivatePathsSync(paths, options = {}) {
|
|
104
|
+
if (paths.length === 0)
|
|
105
|
+
return;
|
|
106
|
+
const normalized = paths.map((descriptor) => ({ ...descriptor, path: resolve(descriptor.path) }));
|
|
107
|
+
if (process.platform === "win32") {
|
|
108
|
+
const pending = options.force
|
|
109
|
+
? normalized
|
|
110
|
+
: normalized.filter((descriptor) => !protectedWindowsPaths.has(`${descriptor.kind}:${descriptor.path.toLowerCase()}`));
|
|
111
|
+
if (pending.length === 0)
|
|
112
|
+
return;
|
|
113
|
+
protectWindowsPathsSync(pending);
|
|
114
|
+
for (const descriptor of pending)
|
|
115
|
+
protectedWindowsPaths.add(`${descriptor.kind}:${descriptor.path.toLowerCase()}`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
for (const descriptor of normalized)
|
|
119
|
+
protectPosixPathSync(descriptor.path, descriptor.kind);
|
|
120
|
+
}
|
|
121
|
+
export function protectPrivateDirectorySync(path, options) {
|
|
122
|
+
protectPrivatePathsSync([{ path, kind: "directory" }], options);
|
|
123
|
+
}
|
|
124
|
+
export function protectPrivateFileSync(path, options) {
|
|
125
|
+
protectPrivatePathsSync([{ path, kind: "file" }], options);
|
|
126
|
+
}
|
|
127
|
+
export function protectPrivateTreeSync(root) {
|
|
128
|
+
if (process.platform !== "win32")
|
|
129
|
+
return;
|
|
130
|
+
const paths = [];
|
|
131
|
+
const visit = (path) => {
|
|
132
|
+
const metadata = lstatSync(path);
|
|
133
|
+
if (metadata.isSymbolicLink())
|
|
134
|
+
throw new Error(`Private path tree contains a symbolic link: ${path}`);
|
|
135
|
+
if (metadata.isDirectory()) {
|
|
136
|
+
paths.push({ path, kind: "directory" });
|
|
137
|
+
for (const entry of readdirSync(path))
|
|
138
|
+
visit(resolve(path, entry));
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (metadata.isFile())
|
|
142
|
+
paths.push({ path, kind: "file" });
|
|
143
|
+
};
|
|
144
|
+
visit(resolve(root));
|
|
145
|
+
protectPrivatePathsSync(paths, { force: true });
|
|
146
|
+
}
|