@pasko70/pibo 1.0.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.
Files changed (351) hide show
  1. package/.codex/skills/pi-agent-harness/SKILL.md +319 -0
  2. package/.codex/skills/pi-agent-harness/agents/openai.yaml +4 -0
  3. package/README.md +318 -0
  4. package/context/codex-base-prompt.md +144 -0
  5. package/context/pibo-compaction-prompt.md +100 -0
  6. package/context/pibo-system-prompt.md +17 -0
  7. package/dist/apps/chat/agent-profiles.js +26 -0
  8. package/dist/apps/chat/agent-store.js +336 -0
  9. package/dist/apps/chat/event-log.js +307 -0
  10. package/dist/apps/chat/read-model.js +218 -0
  11. package/dist/apps/chat/rooms.js +321 -0
  12. package/dist/apps/chat/session-metadata.js +14 -0
  13. package/dist/apps/chat/stream.js +122 -0
  14. package/dist/apps/chat/trace.js +1182 -0
  15. package/dist/apps/chat/web-app.js +2569 -0
  16. package/dist/apps/chat-ui/assets/apl-BxEMbctQ.js +1 -0
  17. package/dist/apps/chat-ui/assets/asciiarmor-eFjeGRuZ.js +1 -0
  18. package/dist/apps/chat-ui/assets/asn1-Cfx-0pt9.js +1 -0
  19. package/dist/apps/chat-ui/assets/asterisk-B5BHTsBr.js +1 -0
  20. package/dist/apps/chat-ui/assets/brainfuck-CAA2xofr.js +1 -0
  21. package/dist/apps/chat-ui/assets/clike-BoDCOLzN.js +1 -0
  22. package/dist/apps/chat-ui/assets/clojure-kVFSWX0o.js +1 -0
  23. package/dist/apps/chat-ui/assets/cmake-BUPAhezI.js +1 -0
  24. package/dist/apps/chat-ui/assets/cobol-BfgHZgup.js +1 -0
  25. package/dist/apps/chat-ui/assets/coffeescript-G1Pe_1AZ.js +1 -0
  26. package/dist/apps/chat-ui/assets/commonlisp-B3N0CjHn.js +1 -0
  27. package/dist/apps/chat-ui/assets/crystal-C15ndaGM.js +1 -0
  28. package/dist/apps/chat-ui/assets/css-BqOeyZOV.js +1 -0
  29. package/dist/apps/chat-ui/assets/cypher-KjIKv-wW.js +1 -0
  30. package/dist/apps/chat-ui/assets/d-BlT5AEC-.js +1 -0
  31. package/dist/apps/chat-ui/assets/diff-oF98tbqj.js +1 -0
  32. package/dist/apps/chat-ui/assets/dist--55mTWk3.js +1 -0
  33. package/dist/apps/chat-ui/assets/dist-B5rcjVCj.js +1 -0
  34. package/dist/apps/chat-ui/assets/dist-BPv5MdlL.js +1 -0
  35. package/dist/apps/chat-ui/assets/dist-BZ39rB1W.js +1 -0
  36. package/dist/apps/chat-ui/assets/dist-BsedZGJo.js +1 -0
  37. package/dist/apps/chat-ui/assets/dist-BvO6SpaM.js +9 -0
  38. package/dist/apps/chat-ui/assets/dist-C5YJOwhH.js +1 -0
  39. package/dist/apps/chat-ui/assets/dist-C5oXtFSP.js +1 -0
  40. package/dist/apps/chat-ui/assets/dist-CG_gRSwv.js +23 -0
  41. package/dist/apps/chat-ui/assets/dist-CKM5j-iy.js +1 -0
  42. package/dist/apps/chat-ui/assets/dist-CUp9us2q.js +1 -0
  43. package/dist/apps/chat-ui/assets/dist-CZ9T4Cly.js +1 -0
  44. package/dist/apps/chat-ui/assets/dist-D4HhiFWB.js +6 -0
  45. package/dist/apps/chat-ui/assets/dist-DHE-yBLH.js +1 -0
  46. package/dist/apps/chat-ui/assets/dist-DSKQpPaz.js +1 -0
  47. package/dist/apps/chat-ui/assets/dist-L1B-KWZO.js +1 -0
  48. package/dist/apps/chat-ui/assets/dist-SVPsM5Oi.js +13 -0
  49. package/dist/apps/chat-ui/assets/dist-Zqt9kNG9.js +1 -0
  50. package/dist/apps/chat-ui/assets/dist-_DPv0-QF.js +1 -0
  51. package/dist/apps/chat-ui/assets/dist-_oM7g0eh.js +11 -0
  52. package/dist/apps/chat-ui/assets/dist-laadbMWg.js +1 -0
  53. package/dist/apps/chat-ui/assets/dist-nFui3f-r.js +2 -0
  54. package/dist/apps/chat-ui/assets/dist-tGfufz3b.js +1 -0
  55. package/dist/apps/chat-ui/assets/dockerfile-CUaqzcqp.js +1 -0
  56. package/dist/apps/chat-ui/assets/dtd-VCA9yr61.js +1 -0
  57. package/dist/apps/chat-ui/assets/dylan-O-V5Dw5W.js +1 -0
  58. package/dist/apps/chat-ui/assets/ebnf-SPmdBkvT.js +1 -0
  59. package/dist/apps/chat-ui/assets/ecl-ChfCttfI.js +1 -0
  60. package/dist/apps/chat-ui/assets/eiffel-cB9EbjeN.js +1 -0
  61. package/dist/apps/chat-ui/assets/elm-BTiVflBU.js +1 -0
  62. package/dist/apps/chat-ui/assets/erlang-egf0TXFm.js +1 -0
  63. package/dist/apps/chat-ui/assets/factor-C2tAxNob.js +1 -0
  64. package/dist/apps/chat-ui/assets/fcl-BiCjh1tj.js +1 -0
  65. package/dist/apps/chat-ui/assets/forth-C4kDGW6u.js +1 -0
  66. package/dist/apps/chat-ui/assets/fortran-DzkLaPnd.js +1 -0
  67. package/dist/apps/chat-ui/assets/gas-DMOxskQF.js +1 -0
  68. package/dist/apps/chat-ui/assets/gherkin-BPxBpp-k.js +1 -0
  69. package/dist/apps/chat-ui/assets/groovy-CkEnc0ZY.js +1 -0
  70. package/dist/apps/chat-ui/assets/haskell-Dadpo9-w.js +1 -0
  71. package/dist/apps/chat-ui/assets/haxe-CDNZL9MW.js +1 -0
  72. package/dist/apps/chat-ui/assets/http-jy9Zxxt3.js +1 -0
  73. package/dist/apps/chat-ui/assets/idl-CGSB6iiy.js +1 -0
  74. package/dist/apps/chat-ui/assets/index-BdcqTugD.js +151 -0
  75. package/dist/apps/chat-ui/assets/index-Dk-opWl3.css +1 -0
  76. package/dist/apps/chat-ui/assets/javascript-DSlzRUd8.js +1 -0
  77. package/dist/apps/chat-ui/assets/julia-DTiF9v6R.js +1 -0
  78. package/dist/apps/chat-ui/assets/livescript-Ci8Xxna6.js +1 -0
  79. package/dist/apps/chat-ui/assets/lua-eVxIrt9t.js +1 -0
  80. package/dist/apps/chat-ui/assets/mathematica-Da3grj36.js +1 -0
  81. package/dist/apps/chat-ui/assets/mbox-BWBMSafM.js +1 -0
  82. package/dist/apps/chat-ui/assets/mirc-CtHu9S1B.js +1 -0
  83. package/dist/apps/chat-ui/assets/mllike-wTFlPWXS.js +1 -0
  84. package/dist/apps/chat-ui/assets/modelica-cKbQ0UXu.js +1 -0
  85. package/dist/apps/chat-ui/assets/mscgen-C5pXQCMK.js +1 -0
  86. package/dist/apps/chat-ui/assets/mumps-CKBHkOQq.js +1 -0
  87. package/dist/apps/chat-ui/assets/nginx-B00AiQel.js +1 -0
  88. package/dist/apps/chat-ui/assets/nsis-SMDwKltR.js +1 -0
  89. package/dist/apps/chat-ui/assets/ntriples-B_36EBV0.js +1 -0
  90. package/dist/apps/chat-ui/assets/octave-BXPjzAif.js +1 -0
  91. package/dist/apps/chat-ui/assets/oz-Dedpat1M.js +1 -0
  92. package/dist/apps/chat-ui/assets/pascal-CMcUg72K.js +1 -0
  93. package/dist/apps/chat-ui/assets/perl-DoVTbnzt.js +1 -0
  94. package/dist/apps/chat-ui/assets/pig-CULXT1YV.js +1 -0
  95. package/dist/apps/chat-ui/assets/powershell-Cv5Uhf6b.js +1 -0
  96. package/dist/apps/chat-ui/assets/properties-CQ1C2P-M.js +1 -0
  97. package/dist/apps/chat-ui/assets/protobuf-SRlvzrl6.js +1 -0
  98. package/dist/apps/chat-ui/assets/pug-Bo3wfL-f.js +1 -0
  99. package/dist/apps/chat-ui/assets/puppet-ClSqCGj6.js +1 -0
  100. package/dist/apps/chat-ui/assets/python-C2_X1GpX.js +1 -0
  101. package/dist/apps/chat-ui/assets/q-B_6Gdyzl.js +1 -0
  102. package/dist/apps/chat-ui/assets/r-BD07NGED.js +1 -0
  103. package/dist/apps/chat-ui/assets/rolldown-runtime-S-ySWqyJ.js +1 -0
  104. package/dist/apps/chat-ui/assets/rpm-CRu5Z2Jl.js +1 -0
  105. package/dist/apps/chat-ui/assets/ruby-dZPnOz8h.js +1 -0
  106. package/dist/apps/chat-ui/assets/sas-S2t8_sDI.js +1 -0
  107. package/dist/apps/chat-ui/assets/scheme-WcyFWLW2.js +1 -0
  108. package/dist/apps/chat-ui/assets/shell-DUelnYSh.js +1 -0
  109. package/dist/apps/chat-ui/assets/sieve-jDjlVhNh.js +1 -0
  110. package/dist/apps/chat-ui/assets/simple-mode-CL04yRVp.js +1 -0
  111. package/dist/apps/chat-ui/assets/smalltalk-ddWDYy10.js +1 -0
  112. package/dist/apps/chat-ui/assets/solr-C7rQhLuW.js +1 -0
  113. package/dist/apps/chat-ui/assets/sparql--fPCCgVd.js +1 -0
  114. package/dist/apps/chat-ui/assets/spreadsheet-DjthqD5G.js +1 -0
  115. package/dist/apps/chat-ui/assets/sql-D_dieYbr.js +1 -0
  116. package/dist/apps/chat-ui/assets/stex-rHcFYcA0.js +1 -0
  117. package/dist/apps/chat-ui/assets/stylus-DxF70RNr.js +1 -0
  118. package/dist/apps/chat-ui/assets/swift-T0hDoOb-.js +1 -0
  119. package/dist/apps/chat-ui/assets/tcl-Dlzy1mPk.js +1 -0
  120. package/dist/apps/chat-ui/assets/textile-Dy5rFZe5.js +1 -0
  121. package/dist/apps/chat-ui/assets/tiddlywiki-DQP1OymS.js +1 -0
  122. package/dist/apps/chat-ui/assets/tiki-0vOj3DB2.js +1 -0
  123. package/dist/apps/chat-ui/assets/toml-DEltN0lI.js +1 -0
  124. package/dist/apps/chat-ui/assets/troff-CRbBpEqY.js +1 -0
  125. package/dist/apps/chat-ui/assets/ttcn-M5q1nLKg.js +1 -0
  126. package/dist/apps/chat-ui/assets/ttcn-cfg-Rmw9NWq9.js +1 -0
  127. package/dist/apps/chat-ui/assets/turtle-BCDWhJK0.js +1 -0
  128. package/dist/apps/chat-ui/assets/vb-QQ7LGlOz.js +1 -0
  129. package/dist/apps/chat-ui/assets/vbscript-BiSEdUzz.js +1 -0
  130. package/dist/apps/chat-ui/assets/velocity-CzMg5_6w.js +1 -0
  131. package/dist/apps/chat-ui/assets/verilog-BJOO83gk.js +1 -0
  132. package/dist/apps/chat-ui/assets/vhdl-ed4zAZAA.js +1 -0
  133. package/dist/apps/chat-ui/assets/webidl-Ddy6hbV6.js +1 -0
  134. package/dist/apps/chat-ui/assets/xquery-Bw1zwhh6.js +1 -0
  135. package/dist/apps/chat-ui/assets/yacas-DZdTXy3s.js +1 -0
  136. package/dist/apps/chat-ui/assets/z80-DMG02_YV.js +1 -0
  137. package/dist/apps/chat-ui/index.html +15 -0
  138. package/dist/apps/context-files-ui/assets/apl-14BqhLYK.js +1 -0
  139. package/dist/apps/context-files-ui/assets/asciiarmor-WLvjqI7d.js +1 -0
  140. package/dist/apps/context-files-ui/assets/asn1-Bg4FbdCg.js +1 -0
  141. package/dist/apps/context-files-ui/assets/asterisk-B2MO-X4d.js +1 -0
  142. package/dist/apps/context-files-ui/assets/brainfuck-CV9kufud.js +1 -0
  143. package/dist/apps/context-files-ui/assets/clike-CjmcUpt6.js +1 -0
  144. package/dist/apps/context-files-ui/assets/clojure-De3QGfx6.js +1 -0
  145. package/dist/apps/context-files-ui/assets/cmake-i2VDNaRq.js +1 -0
  146. package/dist/apps/context-files-ui/assets/cobol-BwO6DF08.js +1 -0
  147. package/dist/apps/context-files-ui/assets/coffeescript-BtivDvRz.js +1 -0
  148. package/dist/apps/context-files-ui/assets/commonlisp-DFkEQdLP.js +1 -0
  149. package/dist/apps/context-files-ui/assets/crystal-CU0NkcVL.js +1 -0
  150. package/dist/apps/context-files-ui/assets/css-CbMZAQ1W.js +1 -0
  151. package/dist/apps/context-files-ui/assets/cypher-Dke5QOzv.js +1 -0
  152. package/dist/apps/context-files-ui/assets/d-TPqSVaHM.js +1 -0
  153. package/dist/apps/context-files-ui/assets/diff-B365LSoZ.js +1 -0
  154. package/dist/apps/context-files-ui/assets/dist-B5PTVnox.js +1 -0
  155. package/dist/apps/context-files-ui/assets/dist-B5ZAfwf5.js +1 -0
  156. package/dist/apps/context-files-ui/assets/dist-BJW3917N.js +11 -0
  157. package/dist/apps/context-files-ui/assets/dist-BkUONE3N.js +1 -0
  158. package/dist/apps/context-files-ui/assets/dist-CKQF1zEu.js +1 -0
  159. package/dist/apps/context-files-ui/assets/dist-CwbH_3RN.js +1 -0
  160. package/dist/apps/context-files-ui/assets/dist-CxRLRmMx.js +2 -0
  161. package/dist/apps/context-files-ui/assets/dist-D1KyNDg5.js +13 -0
  162. package/dist/apps/context-files-ui/assets/dist-DFi2YGux.js +1 -0
  163. package/dist/apps/context-files-ui/assets/dist-DKoyMSNr.js +1 -0
  164. package/dist/apps/context-files-ui/assets/dist-DLMjd26T.js +1 -0
  165. package/dist/apps/context-files-ui/assets/dist-DQXzt2Ng.js +1 -0
  166. package/dist/apps/context-files-ui/assets/dist-DotDq_kD.js +1 -0
  167. package/dist/apps/context-files-ui/assets/dist-DyfSeBMu.js +1 -0
  168. package/dist/apps/context-files-ui/assets/dist-MyZgX9K2.js +9 -0
  169. package/dist/apps/context-files-ui/assets/dist-QQuDy4KA.js +1 -0
  170. package/dist/apps/context-files-ui/assets/dist-YwR9dpb-.js +1 -0
  171. package/dist/apps/context-files-ui/assets/dist-dTBsFYAl.js +1 -0
  172. package/dist/apps/context-files-ui/assets/dockerfile-CJb2sekW.js +1 -0
  173. package/dist/apps/context-files-ui/assets/dtd-CBMymJ7O.js +1 -0
  174. package/dist/apps/context-files-ui/assets/dylan-08Azy1NK.js +1 -0
  175. package/dist/apps/context-files-ui/assets/ebnf-Dkum_1J8.js +1 -0
  176. package/dist/apps/context-files-ui/assets/ecl-dhxjHL8y.js +1 -0
  177. package/dist/apps/context-files-ui/assets/eiffel-C446m6dI.js +1 -0
  178. package/dist/apps/context-files-ui/assets/elm-po1E9de0.js +1 -0
  179. package/dist/apps/context-files-ui/assets/erlang-BD5LkVkR.js +1 -0
  180. package/dist/apps/context-files-ui/assets/factor-B2EMQZkP.js +1 -0
  181. package/dist/apps/context-files-ui/assets/fcl-DkEOamHd.js +1 -0
  182. package/dist/apps/context-files-ui/assets/forth-dCg2_HVB.js +1 -0
  183. package/dist/apps/context-files-ui/assets/fortran-3ZpXLFh1.js +1 -0
  184. package/dist/apps/context-files-ui/assets/gas-DMmxLZRI.js +1 -0
  185. package/dist/apps/context-files-ui/assets/gherkin-DUSFg2R6.js +1 -0
  186. package/dist/apps/context-files-ui/assets/groovy-Cmduloe1.js +1 -0
  187. package/dist/apps/context-files-ui/assets/haskell-Cyr255R9.js +1 -0
  188. package/dist/apps/context-files-ui/assets/haxe-BJVKtprM.js +1 -0
  189. package/dist/apps/context-files-ui/assets/http-DszHFpc-.js +1 -0
  190. package/dist/apps/context-files-ui/assets/idl-Cati9Nrv.js +1 -0
  191. package/dist/apps/context-files-ui/assets/index-BwRDoHMC.css +1 -0
  192. package/dist/apps/context-files-ui/assets/index-GwvjXytt.js +202 -0
  193. package/dist/apps/context-files-ui/assets/javascript-C5pDVaoP.js +1 -0
  194. package/dist/apps/context-files-ui/assets/julia-DlqY1cTU.js +1 -0
  195. package/dist/apps/context-files-ui/assets/livescript-C6p8-2W1.js +1 -0
  196. package/dist/apps/context-files-ui/assets/lua-BxuTHaF7.js +1 -0
  197. package/dist/apps/context-files-ui/assets/mathematica-nDHZUMJT.js +1 -0
  198. package/dist/apps/context-files-ui/assets/mbox-DAaR0Hcz.js +1 -0
  199. package/dist/apps/context-files-ui/assets/mirc-CCA0FRfl.js +1 -0
  200. package/dist/apps/context-files-ui/assets/mllike-Cw3Iccsb.js +1 -0
  201. package/dist/apps/context-files-ui/assets/modelica-Czx80fNR.js +1 -0
  202. package/dist/apps/context-files-ui/assets/mscgen-BmfuTbzG.js +1 -0
  203. package/dist/apps/context-files-ui/assets/mumps-Ds_ZaumL.js +1 -0
  204. package/dist/apps/context-files-ui/assets/nginx-D9M2PQ4U.js +1 -0
  205. package/dist/apps/context-files-ui/assets/nsis-DOo3SJmd.js +1 -0
  206. package/dist/apps/context-files-ui/assets/ntriples-DLHGTvHs.js +1 -0
  207. package/dist/apps/context-files-ui/assets/octave-8jj96JiC.js +1 -0
  208. package/dist/apps/context-files-ui/assets/oz-BmMq7iC4.js +1 -0
  209. package/dist/apps/context-files-ui/assets/pascal-BgMr6XpT.js +1 -0
  210. package/dist/apps/context-files-ui/assets/perl-D6YJObi_.js +1 -0
  211. package/dist/apps/context-files-ui/assets/pig-nx7uchs8.js +1 -0
  212. package/dist/apps/context-files-ui/assets/powershell-B4IPkVxR.js +1 -0
  213. package/dist/apps/context-files-ui/assets/properties-CoavQdLC.js +1 -0
  214. package/dist/apps/context-files-ui/assets/protobuf-D5EWhcmc.js +1 -0
  215. package/dist/apps/context-files-ui/assets/pug-QU72BPAa.js +1 -0
  216. package/dist/apps/context-files-ui/assets/puppet-B_0GFuyD.js +1 -0
  217. package/dist/apps/context-files-ui/assets/python-Cs7sNzy_.js +1 -0
  218. package/dist/apps/context-files-ui/assets/q-BIB9pPtI.js +1 -0
  219. package/dist/apps/context-files-ui/assets/r-CN5s4f3o.js +1 -0
  220. package/dist/apps/context-files-ui/assets/rpm-Ct_Ck8mQ.js +1 -0
  221. package/dist/apps/context-files-ui/assets/ruby-BVPradRk.js +1 -0
  222. package/dist/apps/context-files-ui/assets/sas-DN4j74IF.js +1 -0
  223. package/dist/apps/context-files-ui/assets/scheme-CvNgHAnh.js +1 -0
  224. package/dist/apps/context-files-ui/assets/shell-BjiMLAaM.js +1 -0
  225. package/dist/apps/context-files-ui/assets/sieve-C2U1O5Z4.js +1 -0
  226. package/dist/apps/context-files-ui/assets/simple-mode-DnMbQuKW.js +1 -0
  227. package/dist/apps/context-files-ui/assets/smalltalk-DkYwYufV.js +1 -0
  228. package/dist/apps/context-files-ui/assets/solr-BopnSD-v.js +1 -0
  229. package/dist/apps/context-files-ui/assets/sparql-Cpg3YQfd.js +1 -0
  230. package/dist/apps/context-files-ui/assets/spreadsheet-kRxtVt9J.js +1 -0
  231. package/dist/apps/context-files-ui/assets/sql-DYI2GwJR.js +1 -0
  232. package/dist/apps/context-files-ui/assets/stex-BCRT8IJ3.js +1 -0
  233. package/dist/apps/context-files-ui/assets/stylus-CARoeVE9.js +1 -0
  234. package/dist/apps/context-files-ui/assets/swift-DKNaLAK6.js +1 -0
  235. package/dist/apps/context-files-ui/assets/tcl-CQyXkgMH.js +1 -0
  236. package/dist/apps/context-files-ui/assets/textile-Yg_UWn-t.js +1 -0
  237. package/dist/apps/context-files-ui/assets/tiddlywiki-dqVbkfX4.js +1 -0
  238. package/dist/apps/context-files-ui/assets/tiki-CC_ic8g0.js +1 -0
  239. package/dist/apps/context-files-ui/assets/toml-B7gGzVkJ.js +1 -0
  240. package/dist/apps/context-files-ui/assets/troff-Bq3z6oAu.js +1 -0
  241. package/dist/apps/context-files-ui/assets/ttcn-GvRgs2AT.js +1 -0
  242. package/dist/apps/context-files-ui/assets/ttcn-cfg-CGCXz7Vo.js +1 -0
  243. package/dist/apps/context-files-ui/assets/turtle-CwIpz2yy.js +1 -0
  244. package/dist/apps/context-files-ui/assets/vb-BmcHUM2C.js +1 -0
  245. package/dist/apps/context-files-ui/assets/vbscript-Cs9dylre.js +1 -0
  246. package/dist/apps/context-files-ui/assets/velocity-CBWU0VCy.js +1 -0
  247. package/dist/apps/context-files-ui/assets/verilog-DVCHmufx.js +1 -0
  248. package/dist/apps/context-files-ui/assets/vhdl-BGDk4f1d.js +1 -0
  249. package/dist/apps/context-files-ui/assets/webidl-egXQEWlC.js +1 -0
  250. package/dist/apps/context-files-ui/assets/xquery-5f0hn7cg.js +1 -0
  251. package/dist/apps/context-files-ui/assets/yacas-DNs9XkmQ.js +1 -0
  252. package/dist/apps/context-files-ui/assets/z80-B7Dfb4UO.js +1 -0
  253. package/dist/apps/context-files-ui/index.html +14 -0
  254. package/dist/auth/better-auth.js +129 -0
  255. package/dist/auth/types.js +14 -0
  256. package/dist/bin/pibo.js +3 -0
  257. package/dist/channels/types.js +1 -0
  258. package/dist/cli-errors.js +18 -0
  259. package/dist/cli.js +264 -0
  260. package/dist/config/config.js +167 -0
  261. package/dist/core/base-prompt.js +104 -0
  262. package/dist/core/codex-compat.js +44 -0
  263. package/dist/core/compaction-prompt.js +258 -0
  264. package/dist/core/events.js +1 -0
  265. package/dist/core/profiles.js +138 -0
  266. package/dist/core/routed-session.js +538 -0
  267. package/dist/core/runtime.js +323 -0
  268. package/dist/core/session-router.js +437 -0
  269. package/dist/core/system-prompt-template.js +56 -0
  270. package/dist/core/thinking.js +9 -0
  271. package/dist/core/workspace.js +5 -0
  272. package/dist/debug/events.js +92 -0
  273. package/dist/debug/index.js +631 -0
  274. package/dist/debug/session.js +200 -0
  275. package/dist/debug/sql.js +238 -0
  276. package/dist/debug/stores.js +49 -0
  277. package/dist/debug/trace.js +245 -0
  278. package/dist/env.js +32 -0
  279. package/dist/gateway/client.js +200 -0
  280. package/dist/gateway/protocol.js +46 -0
  281. package/dist/gateway/request.js +146 -0
  282. package/dist/gateway/server.js +221 -0
  283. package/dist/gateway/tool.js +69 -0
  284. package/dist/gateway/web.js +80 -0
  285. package/dist/index.js +23 -0
  286. package/dist/local/client.js +77 -0
  287. package/dist/local/extension.js +548 -0
  288. package/dist/local/tui.js +25 -0
  289. package/dist/mcp/agent-context.js +123 -0
  290. package/dist/mcp/client.js +332 -0
  291. package/dist/mcp/commands/call.js +146 -0
  292. package/dist/mcp/commands/grep.js +145 -0
  293. package/dist/mcp/commands/info.js +71 -0
  294. package/dist/mcp/commands/list.js +94 -0
  295. package/dist/mcp/config-command.js +217 -0
  296. package/dist/mcp/config.js +440 -0
  297. package/dist/mcp/daemon-client.js +266 -0
  298. package/dist/mcp/daemon.js +327 -0
  299. package/dist/mcp/errors.js +287 -0
  300. package/dist/mcp/index.js +431 -0
  301. package/dist/mcp/output.js +171 -0
  302. package/dist/mcp/python-runtime.js +131 -0
  303. package/dist/mcp/registry.js +116 -0
  304. package/dist/mcp/version.js +5 -0
  305. package/dist/pi-packages/cli.js +92 -0
  306. package/dist/pi-packages/installer.js +70 -0
  307. package/dist/pi-packages/metadata.js +231 -0
  308. package/dist/pi-packages/runtime.js +61 -0
  309. package/dist/pi-packages/store.js +175 -0
  310. package/dist/pi-packages/types.js +1 -0
  311. package/dist/plugins/better-auth.js +11 -0
  312. package/dist/plugins/builtin.js +249 -0
  313. package/dist/plugins/chat-web.js +11 -0
  314. package/dist/plugins/codex-compat.js +72 -0
  315. package/dist/plugins/context-files-store.js +354 -0
  316. package/dist/plugins/context-files.js +999 -0
  317. package/dist/plugins/core-tools.js +94 -0
  318. package/dist/plugins/example.js +67 -0
  319. package/dist/plugins/registry.js +368 -0
  320. package/dist/plugins/remote-agent.js +9 -0
  321. package/dist/plugins/types.js +1 -0
  322. package/dist/plugins/web.js +11 -0
  323. package/dist/reliability/store.js +759 -0
  324. package/dist/remote/channel.js +176 -0
  325. package/dist/remote/client.js +92 -0
  326. package/dist/remote/examples/tui-controller.js +263 -0
  327. package/dist/remote/protocol.js +55 -0
  328. package/dist/remote/session-client.js +189 -0
  329. package/dist/runs/registry.js +323 -0
  330. package/dist/runs/tools.js +185 -0
  331. package/dist/sessions/bindings.js +41 -0
  332. package/dist/sessions/sqlite-store.js +154 -0
  333. package/dist/sessions/store.js +125 -0
  334. package/dist/shared/trace-order.js +83 -0
  335. package/dist/subagents/tool.js +63 -0
  336. package/dist/tools/browser-use-cdp.js +207 -0
  337. package/dist/tools/browser-use-leases.js +338 -0
  338. package/dist/tools/browser-use-wrapper.js +338 -0
  339. package/dist/tools/codex-compat.js +85 -0
  340. package/dist/tools/desktop-env.js +90 -0
  341. package/dist/tools/guides.js +276 -0
  342. package/dist/tools/index.js +366 -0
  343. package/dist/tools/python-runtime.js +154 -0
  344. package/dist/tools/registry.js +107 -0
  345. package/dist/tools/web-search.js +137 -0
  346. package/dist/web/auth.js +10 -0
  347. package/dist/web/channel.js +151 -0
  348. package/dist/web/http.js +110 -0
  349. package/dist/web/types.js +1 -0
  350. package/package.json +74 -0
  351. package/src/mcp/LICENSE.mcp-cli +21 -0
@@ -0,0 +1,440 @@
1
+ /**
2
+ * MCP-CLI Configuration Types and Loader
3
+ */
4
+ import { createHash } from 'node:crypto';
5
+ import { existsSync } from 'node:fs';
6
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
7
+ import { homedir } from 'node:os';
8
+ import { dirname, join, resolve } from 'node:path';
9
+ import { ErrorCode, configInvalidJsonError, configMissingFieldError, configNotFoundError, configSearchError, formatCliError, serverNotFoundError, } from './errors.js';
10
+ export const DEFAULT_MCP_CONFIG_FILE = 'mcp_servers.json';
11
+ // ============================================================================
12
+ // Tool Filtering
13
+ // ============================================================================
14
+ /**
15
+ * Simple glob pattern matcher for tool names
16
+ * Supports * (any characters) and ? (single character)
17
+ */
18
+ function matchesPattern(name, pattern) {
19
+ // Convert glob pattern to regex
20
+ const regexPattern = pattern
21
+ .replace(/[.+^${}()|[\]\\]/g, '\\$&') // Escape special regex chars
22
+ .replace(/\*/g, '.*') // * matches any characters
23
+ .replace(/\?/g, '.'); // ? matches single character
24
+ return new RegExp(`^${regexPattern}$`, 'i').test(name);
25
+ }
26
+ /**
27
+ * Check if a tool name matches any of the given patterns
28
+ */
29
+ function matchesAnyPattern(name, patterns) {
30
+ return patterns.some((pattern) => matchesPattern(name, pattern));
31
+ }
32
+ /**
33
+ * Filter tools based on allowedTools and disabledTools configuration
34
+ *
35
+ * Rules:
36
+ * - If allowedTools is specified, only tools matching those patterns are available
37
+ * - If disabledTools is specified, tools matching those patterns are excluded
38
+ * - disabledTools takes precedence over allowedTools
39
+ *
40
+ * @param tools - Array of tools with name property
41
+ * @param config - Server config with optional allowedTools/disabledTools
42
+ * @returns Filtered array of tools
43
+ */
44
+ export function filterTools(tools, config) {
45
+ const { allowedTools, disabledTools } = config;
46
+ return tools.filter((tool) => {
47
+ // First check if tool is in disabledTools (takes precedence)
48
+ if (disabledTools && disabledTools.length > 0) {
49
+ if (matchesAnyPattern(tool.name, disabledTools)) {
50
+ return false;
51
+ }
52
+ }
53
+ // Then check if allowedTools is specified
54
+ if (allowedTools && allowedTools.length > 0) {
55
+ return matchesAnyPattern(tool.name, allowedTools);
56
+ }
57
+ // No filtering specified, allow all
58
+ return true;
59
+ });
60
+ }
61
+ /**
62
+ * Check if a specific tool is allowed by the config
63
+ *
64
+ * @param toolName - Name of the tool to check
65
+ * @param config - Server config with optional allowedTools/disabledTools
66
+ * @returns true if tool is allowed, false otherwise
67
+ */
68
+ export function isToolAllowed(toolName, config) {
69
+ const { allowedTools, disabledTools } = config;
70
+ // First check if tool is in disabledTools (takes precedence)
71
+ if (disabledTools && disabledTools.length > 0) {
72
+ if (matchesAnyPattern(toolName, disabledTools)) {
73
+ return false;
74
+ }
75
+ }
76
+ // Then check if allowedTools is specified
77
+ if (allowedTools && allowedTools.length > 0) {
78
+ return matchesAnyPattern(toolName, allowedTools);
79
+ }
80
+ // No filtering specified, allow all
81
+ return true;
82
+ }
83
+ /**
84
+ * Check if a server config is HTTP-based
85
+ */
86
+ export function isHttpServer(config) {
87
+ return 'url' in config;
88
+ }
89
+ /**
90
+ * Check if a server config is stdio-based
91
+ */
92
+ export function isStdioServer(config) {
93
+ return 'command' in config;
94
+ }
95
+ // ============================================================================
96
+ // Environment Variables & Runtime Configuration
97
+ // ============================================================================
98
+ /**
99
+ * Default configuration values - centralized to avoid inline magic numbers
100
+ */
101
+ export const DEFAULT_TIMEOUT_SECONDS = 1800; // 30 minutes
102
+ export const DEFAULT_TIMEOUT_MS = DEFAULT_TIMEOUT_SECONDS * 1000;
103
+ export const DEFAULT_CONCURRENCY = 5;
104
+ export const DEFAULT_MAX_RETRIES = 3;
105
+ export const DEFAULT_RETRY_DELAY_MS = 1000; // 1 second base delay
106
+ export const DEFAULT_DAEMON_TIMEOUT_SECONDS = 60; // 60 seconds idle timeout
107
+ export const DEFAULT_DAEMON_REQUEST_TIMEOUT_SECONDS = 60;
108
+ /**
109
+ * Debug logging utility - only logs when MCP_DEBUG is set
110
+ */
111
+ export function debug(message) {
112
+ if (process.env.MCP_DEBUG) {
113
+ console.error(`[pibo mcp] ${message}`);
114
+ }
115
+ }
116
+ /**
117
+ * Get configured timeout in milliseconds
118
+ * @env MCP_TIMEOUT - timeout in seconds (default: 1800 = 30 minutes)
119
+ */
120
+ export function getTimeoutMs() {
121
+ const envTimeout = process.env.MCP_TIMEOUT;
122
+ if (envTimeout) {
123
+ const seconds = Number.parseInt(envTimeout, 10);
124
+ if (!Number.isNaN(seconds) && seconds > 0) {
125
+ return seconds * 1000;
126
+ }
127
+ }
128
+ return DEFAULT_TIMEOUT_MS;
129
+ }
130
+ /**
131
+ * Get concurrency limit for parallel server connections
132
+ * @env MCP_CONCURRENCY - max parallel connections (default: 5)
133
+ */
134
+ export function getConcurrencyLimit() {
135
+ const envConcurrency = process.env.MCP_CONCURRENCY;
136
+ if (envConcurrency) {
137
+ const limit = Number.parseInt(envConcurrency, 10);
138
+ if (!Number.isNaN(limit) && limit > 0) {
139
+ return limit;
140
+ }
141
+ }
142
+ return DEFAULT_CONCURRENCY;
143
+ }
144
+ /**
145
+ * Get max retry attempts for transient failures
146
+ * @env MCP_MAX_RETRIES - max retry attempts (default: 3, use 0 to disable retries)
147
+ */
148
+ export function getMaxRetries() {
149
+ const envRetries = process.env.MCP_MAX_RETRIES;
150
+ if (envRetries) {
151
+ const retries = Number.parseInt(envRetries, 10);
152
+ if (!Number.isNaN(retries) && retries >= 0) {
153
+ return retries;
154
+ }
155
+ }
156
+ return DEFAULT_MAX_RETRIES;
157
+ }
158
+ /**
159
+ * Get base delay for retry backoff in milliseconds
160
+ * @env MCP_RETRY_DELAY - base delay in milliseconds (default: 1000)
161
+ */
162
+ export function getRetryDelayMs() {
163
+ const envDelay = process.env.MCP_RETRY_DELAY;
164
+ if (envDelay) {
165
+ const delay = Number.parseInt(envDelay, 10);
166
+ if (!Number.isNaN(delay) && delay > 0) {
167
+ return delay;
168
+ }
169
+ }
170
+ return DEFAULT_RETRY_DELAY_MS;
171
+ }
172
+ // ============================================================================
173
+ // Daemon Configuration
174
+ // ============================================================================
175
+ /**
176
+ * Check if daemon mode is enabled
177
+ * @env MCP_NO_DAEMON - set to "1" to disable daemon, force fresh connections
178
+ */
179
+ export function isDaemonEnabled() {
180
+ return process.env.MCP_NO_DAEMON !== '1';
181
+ }
182
+ /**
183
+ * Get daemon idle timeout in milliseconds
184
+ * @env MCP_DAEMON_TIMEOUT - timeout in seconds (default: 60)
185
+ */
186
+ export function getDaemonTimeoutMs() {
187
+ const envTimeout = process.env.MCP_DAEMON_TIMEOUT;
188
+ if (envTimeout) {
189
+ const seconds = Number.parseInt(envTimeout, 10);
190
+ if (!Number.isNaN(seconds) && seconds > 0) {
191
+ return seconds * 1000;
192
+ }
193
+ }
194
+ return DEFAULT_DAEMON_TIMEOUT_SECONDS * 1000;
195
+ }
196
+ /**
197
+ * Get daemon request timeout in milliseconds
198
+ * @env MCP_DAEMON_REQUEST_TIMEOUT - timeout in seconds (default: 60)
199
+ */
200
+ export function getDaemonRequestTimeoutMs() {
201
+ const envTimeout = process.env.MCP_DAEMON_REQUEST_TIMEOUT;
202
+ if (envTimeout) {
203
+ const seconds = Number.parseInt(envTimeout, 10);
204
+ if (!Number.isNaN(seconds) && seconds > 0) {
205
+ return seconds * 1000;
206
+ }
207
+ }
208
+ return DEFAULT_DAEMON_REQUEST_TIMEOUT_SECONDS * 1000;
209
+ }
210
+ /**
211
+ * Get the socket directory for daemon connections
212
+ * Uses platform-appropriate temp directory
213
+ */
214
+ export function getSocketDir() {
215
+ const uid = process.getuid?.() ?? 'unknown';
216
+ // macOS uses /var/folders which is auto-cleaned, Linux uses /tmp
217
+ const base = process.platform === 'darwin' ? '/tmp' : '/tmp';
218
+ return join(base, `mcp-cli-${uid}`);
219
+ }
220
+ /**
221
+ * Get socket path for a specific server
222
+ */
223
+ export function getSocketPath(serverName) {
224
+ return join(getSocketDir(), `${serverName}.sock`);
225
+ }
226
+ /**
227
+ * Get PID file path for a specific server daemon
228
+ */
229
+ export function getPidPath(serverName) {
230
+ return join(getSocketDir(), `${serverName}.pid`);
231
+ }
232
+ /**
233
+ * Generate a hash of server config for stale detection
234
+ * Returns consistent hash for identical configs
235
+ */
236
+ export function getConfigHash(config) {
237
+ const str = JSON.stringify(config, Object.keys(config).sort());
238
+ return createHash('sha256').update(str).digest('hex').slice(0, 16);
239
+ }
240
+ /**
241
+ * Check if strict environment variable mode is enabled
242
+ * @env MCP_STRICT_ENV - set to "false" to warn instead of error (default: true)
243
+ */
244
+ function isStrictEnvMode() {
245
+ const value = process.env.MCP_STRICT_ENV?.toLowerCase();
246
+ return value !== 'false' && value !== '0';
247
+ }
248
+ /**
249
+ * Substitute environment variables in a string
250
+ * Supports ${VAR_NAME} syntax
251
+ *
252
+ * By default (strict mode), throws an error when referenced env var is not set.
253
+ * Set MCP_STRICT_ENV=false to warn instead of error.
254
+ */
255
+ function substituteEnvVars(value) {
256
+ const missingVars = [];
257
+ const result = value.replace(/\$\{([^}]+)\}/g, (match, varName) => {
258
+ const envValue = process.env[varName];
259
+ if (envValue === undefined) {
260
+ missingVars.push(varName);
261
+ return '';
262
+ }
263
+ return envValue;
264
+ });
265
+ if (missingVars.length > 0) {
266
+ const varList = missingVars.map((v) => `\${${v}}`).join(', ');
267
+ const message = `Missing environment variable${missingVars.length > 1 ? 's' : ''}: ${varList}`;
268
+ if (isStrictEnvMode()) {
269
+ throw new Error(formatCliError({
270
+ code: ErrorCode.CLIENT_ERROR,
271
+ type: 'MISSING_ENV_VAR',
272
+ message: message,
273
+ details: 'Referenced in config but not set in environment',
274
+ suggestion: `Set the variable(s) before running: export ${missingVars[0]}="value" or set MCP_STRICT_ENV=false to use empty values`,
275
+ }));
276
+ }
277
+ // Non-strict mode: warn but continue
278
+ console.error(`[pibo mcp] Warning: ${message}`);
279
+ }
280
+ return result;
281
+ }
282
+ /**
283
+ * Recursively substitute environment variables in an object
284
+ */
285
+ function substituteEnvVarsInObject(obj) {
286
+ if (typeof obj === 'string') {
287
+ return substituteEnvVars(obj);
288
+ }
289
+ if (Array.isArray(obj)) {
290
+ return obj.map(substituteEnvVarsInObject);
291
+ }
292
+ if (obj && typeof obj === 'object') {
293
+ const result = {};
294
+ for (const [key, value] of Object.entries(obj)) {
295
+ result[key] = substituteEnvVarsInObject(value);
296
+ }
297
+ return result;
298
+ }
299
+ return obj;
300
+ }
301
+ /**
302
+ * Get default config search paths
303
+ */
304
+ export function getDefaultConfigPaths() {
305
+ const paths = [];
306
+ const home = homedir();
307
+ // Current directory
308
+ paths.push(resolve(DEFAULT_MCP_CONFIG_FILE));
309
+ // Home directory variants
310
+ paths.push(join(home, '.mcp_servers.json'));
311
+ paths.push(join(home, '.config', 'mcp', 'mcp_servers.json'));
312
+ return paths;
313
+ }
314
+ export function getPreferredConfigPath(explicitPath) {
315
+ if (explicitPath) {
316
+ return resolve(explicitPath);
317
+ }
318
+ if (process.env.MCP_CONFIG_PATH) {
319
+ return resolve(process.env.MCP_CONFIG_PATH);
320
+ }
321
+ return resolve(DEFAULT_MCP_CONFIG_FILE);
322
+ }
323
+ export function findConfigPath(explicitPath) {
324
+ if (explicitPath) {
325
+ const resolvedPath = resolve(explicitPath);
326
+ return existsSync(resolvedPath) ? resolvedPath : undefined;
327
+ }
328
+ if (process.env.MCP_CONFIG_PATH) {
329
+ const resolvedPath = resolve(process.env.MCP_CONFIG_PATH);
330
+ return existsSync(resolvedPath) ? resolvedPath : undefined;
331
+ }
332
+ return getDefaultConfigPaths().find((path) => existsSync(path));
333
+ }
334
+ export async function ensureConfigExists(explicitPath) {
335
+ const existingPath = findConfigPath(explicitPath);
336
+ if (existingPath) {
337
+ return existingPath;
338
+ }
339
+ const configPath = getPreferredConfigPath(explicitPath);
340
+ await mkdir(dirname(configPath), { recursive: true });
341
+ await writeFile(configPath, `${JSON.stringify({ mcpServers: {} }, null, 2)}\n`);
342
+ return configPath;
343
+ }
344
+ /**
345
+ * Load and parse MCP servers configuration
346
+ */
347
+ export async function loadConfig(explicitPath) {
348
+ let configPath;
349
+ // Check explicit path from argument or environment
350
+ if (explicitPath) {
351
+ configPath = resolve(explicitPath);
352
+ }
353
+ else if (process.env.MCP_CONFIG_PATH) {
354
+ configPath = resolve(process.env.MCP_CONFIG_PATH);
355
+ }
356
+ // If explicit path provided, it must exist
357
+ if (configPath) {
358
+ if (!existsSync(configPath)) {
359
+ throw new Error(formatCliError(configNotFoundError(configPath)));
360
+ }
361
+ }
362
+ else {
363
+ // Search default paths
364
+ const searchPaths = getDefaultConfigPaths();
365
+ for (const path of searchPaths) {
366
+ if (existsSync(path)) {
367
+ configPath = path;
368
+ break;
369
+ }
370
+ }
371
+ if (!configPath) {
372
+ throw new Error(formatCliError(configSearchError()));
373
+ }
374
+ }
375
+ // Read and parse config
376
+ const content = await readFile(configPath, 'utf-8');
377
+ let config;
378
+ try {
379
+ config = JSON.parse(content);
380
+ }
381
+ catch (e) {
382
+ throw new Error(formatCliError(configInvalidJsonError(configPath, e.message)));
383
+ }
384
+ // Validate structure
385
+ if (!config.mcpServers || typeof config.mcpServers !== 'object') {
386
+ throw new Error(formatCliError(configMissingFieldError(configPath)));
387
+ }
388
+ // Validate individual server configs
389
+ for (const [serverName, serverConfig] of Object.entries(config.mcpServers)) {
390
+ if (!serverConfig || typeof serverConfig !== 'object') {
391
+ throw new Error(formatCliError({
392
+ code: ErrorCode.CLIENT_ERROR,
393
+ type: 'CONFIG_INVALID_SERVER',
394
+ message: `Invalid server configuration for "${serverName}"`,
395
+ details: 'Server config must be an object',
396
+ suggestion: `Use { "command": "..." } for stdio or { "url": "..." } for HTTP`,
397
+ }));
398
+ }
399
+ const hasCommand = 'command' in serverConfig;
400
+ const hasUrl = 'url' in serverConfig;
401
+ if (!hasCommand && !hasUrl) {
402
+ throw new Error(formatCliError({
403
+ code: ErrorCode.CLIENT_ERROR,
404
+ type: 'CONFIG_INVALID_SERVER',
405
+ message: `Server "${serverName}" missing required field`,
406
+ details: `Must have either "command" (for stdio) or "url" (for HTTP)`,
407
+ suggestion: `Add "command": "npx ..." for local servers or "url": "https://..." for remote servers`,
408
+ }));
409
+ }
410
+ if (hasCommand && hasUrl) {
411
+ throw new Error(formatCliError({
412
+ code: ErrorCode.CLIENT_ERROR,
413
+ type: 'CONFIG_INVALID_SERVER',
414
+ message: `Server "${serverName}" has both "command" and "url"`,
415
+ details: 'A server must be either stdio (command) or HTTP (url), not both',
416
+ suggestion: `Remove one of "command" or "url"`,
417
+ }));
418
+ }
419
+ }
420
+ // Substitute environment variables
421
+ config = substituteEnvVarsInObject(config);
422
+ return config;
423
+ }
424
+ /**
425
+ * Get a specific server config by name
426
+ */
427
+ export function getServerConfig(config, serverName) {
428
+ const server = config.mcpServers[serverName];
429
+ if (!server) {
430
+ const available = Object.keys(config.mcpServers);
431
+ throw new Error(formatCliError(serverNotFoundError(serverName, available)));
432
+ }
433
+ return server;
434
+ }
435
+ /**
436
+ * List all server names
437
+ */
438
+ export function listServerNames(config) {
439
+ return Object.keys(config.mcpServers);
440
+ }
@@ -0,0 +1,266 @@
1
+ /**
2
+ * MCP-CLI Daemon Client - IPC client for communicating with daemon workers
3
+ *
4
+ * Handles spawning daemons, detecting stale connections, and forwarding requests.
5
+ */
6
+ import { spawn } from 'node:child_process';
7
+ import { existsSync } from 'node:fs';
8
+ import { readdir } from 'node:fs/promises';
9
+ import { createConnection } from 'node:net';
10
+ import { dirname, extname, join } from 'node:path';
11
+ import { fileURLToPath } from 'node:url';
12
+ import { debug, getConfigHash, getDaemonRequestTimeoutMs, getSocketDir, getSocketPath, } from './config.js';
13
+ import { isProcessRunning, killProcess, readPidFile, removePidFile, removeSocketFile, } from './daemon.js';
14
+ /**
15
+ * Generate a unique request ID
16
+ */
17
+ function generateRequestId() {
18
+ return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
19
+ }
20
+ /**
21
+ * Send a request to the daemon and wait for response
22
+ */
23
+ async function sendRequest(socketPath, request) {
24
+ return new Promise((resolve, reject) => {
25
+ let settled = false;
26
+ let responseText = '';
27
+ const settle = (fn) => {
28
+ if (settled)
29
+ return;
30
+ settled = true;
31
+ clearTimeout(timeoutId);
32
+ fn();
33
+ };
34
+ const socket = createConnection(socketPath, () => {
35
+ socket.write(JSON.stringify(request));
36
+ });
37
+ socket.on('data', (data) => {
38
+ responseText += data.toString();
39
+ if (responseText.includes('\n')) {
40
+ settle(() => {
41
+ try {
42
+ resolve(JSON.parse(responseText.trim()));
43
+ }
44
+ catch {
45
+ reject(new Error('Invalid response from daemon'));
46
+ }
47
+ finally {
48
+ socket.end();
49
+ }
50
+ });
51
+ }
52
+ });
53
+ socket.on('end', () => {
54
+ if (settled)
55
+ return;
56
+ settle(() => {
57
+ try {
58
+ resolve(JSON.parse(responseText.trim()));
59
+ }
60
+ catch {
61
+ reject(new Error('Invalid response from daemon'));
62
+ }
63
+ });
64
+ });
65
+ socket.on('error', (error) => {
66
+ settle(() => reject(error));
67
+ });
68
+ const timeoutId = setTimeout(() => {
69
+ socket.destroy();
70
+ settle(() => reject(new Error('Daemon request timeout')));
71
+ }, getDaemonRequestTimeoutMs());
72
+ });
73
+ }
74
+ /**
75
+ * Check if daemon is running and has matching config
76
+ */
77
+ function isDaemonValid(serverName, config) {
78
+ const socketPath = getSocketPath(serverName);
79
+ const pidInfo = readPidFile(serverName);
80
+ // No PID file = no daemon
81
+ if (!pidInfo) {
82
+ debug(`[daemon-client] No PID file for ${serverName}`);
83
+ return false;
84
+ }
85
+ // Check if process is actually running
86
+ if (!isProcessRunning(pidInfo.pid)) {
87
+ debug(`[daemon-client] Process ${pidInfo.pid} not running, cleaning up`);
88
+ removePidFile(serverName);
89
+ removeSocketFile(serverName);
90
+ return false;
91
+ }
92
+ // Check if config matches
93
+ const currentHash = getConfigHash(config);
94
+ if (pidInfo.configHash !== currentHash) {
95
+ debug(`[daemon-client] Config hash mismatch for ${serverName}, killing old daemon`);
96
+ killProcess(pidInfo.pid);
97
+ removePidFile(serverName);
98
+ removeSocketFile(serverName);
99
+ return false;
100
+ }
101
+ // Check if socket exists
102
+ if (!existsSync(socketPath)) {
103
+ debug(`[daemon-client] Socket missing for ${serverName}, cleaning up`);
104
+ killProcess(pidInfo.pid);
105
+ removePidFile(serverName);
106
+ return false;
107
+ }
108
+ return true;
109
+ }
110
+ /**
111
+ * Spawn a new daemon process for a server
112
+ */
113
+ async function spawnDaemon(serverName, config) {
114
+ debug(`[daemon-client] Spawning daemon for ${serverName}`);
115
+ // Find the daemon script next to this module. This is .ts under tsx and .js after build.
116
+ const modulePath = fileURLToPath(import.meta.url);
117
+ const daemonScript = join(dirname(modulePath), `daemon${extname(modulePath)}`);
118
+ const configJson = JSON.stringify(config);
119
+ // Spawn detached process
120
+ const proc = spawn(process.execPath, [
121
+ ...process.execArgv,
122
+ daemonScript,
123
+ '--daemon',
124
+ serverName,
125
+ configJson,
126
+ ], {
127
+ detached: true,
128
+ stdio: ['ignore', 'pipe', 'pipe'],
129
+ env: { ...process.env },
130
+ });
131
+ // Wait for daemon to signal readiness or fail
132
+ return new Promise((resolve) => {
133
+ let resolved = false;
134
+ proc.stdout?.on('data', (data) => {
135
+ const text = data.toString();
136
+ if (text.includes('DAEMON_READY') && !resolved) {
137
+ resolved = true;
138
+ // Don't await the process, let it run in the background.
139
+ proc.stdout?.unref?.();
140
+ proc.stderr?.unref?.();
141
+ proc.unref();
142
+ resolve(true);
143
+ }
144
+ });
145
+ // Timeout after 5 seconds (fast fallback to direct connection)
146
+ setTimeout(() => {
147
+ if (!resolved) {
148
+ resolved = true;
149
+ debug(`[daemon-client] Daemon spawn timeout for ${serverName}`);
150
+ resolve(false);
151
+ }
152
+ }, 5000);
153
+ // Check for early exit
154
+ proc.on('exit', (code) => {
155
+ if (!resolved && code !== 0) {
156
+ resolved = true;
157
+ debug(`[daemon-client] Daemon exited with code ${code}`);
158
+ resolve(false);
159
+ }
160
+ });
161
+ });
162
+ }
163
+ /**
164
+ * Get or create a daemon connection for a server
165
+ * Returns null if daemon mode fails (caller should fallback to direct connection)
166
+ */
167
+ export async function getDaemonConnection(serverName, config) {
168
+ const socketPath = getSocketPath(serverName);
169
+ // Check if valid daemon exists
170
+ if (!isDaemonValid(serverName, config)) {
171
+ // Spawn new daemon
172
+ const spawned = await spawnDaemon(serverName, config);
173
+ if (!spawned) {
174
+ debug(`[daemon-client] Failed to spawn daemon for ${serverName}`);
175
+ return null;
176
+ }
177
+ // Wait a bit for socket to be ready
178
+ await new Promise((r) => setTimeout(r, 100));
179
+ }
180
+ // Verify socket exists
181
+ if (!existsSync(socketPath)) {
182
+ debug(`[daemon-client] Socket not found after spawn for ${serverName}`);
183
+ return null;
184
+ }
185
+ // Test connection with ping
186
+ try {
187
+ const pingResponse = await sendRequest(socketPath, {
188
+ id: generateRequestId(),
189
+ type: 'ping',
190
+ });
191
+ if (!pingResponse.success) {
192
+ debug(`[daemon-client] Ping failed for ${serverName}`);
193
+ return null;
194
+ }
195
+ }
196
+ catch (error) {
197
+ debug(`[daemon-client] Connection test failed for ${serverName}: ${error.message}`);
198
+ return null;
199
+ }
200
+ debug(`[daemon-client] Connected to daemon for ${serverName}`);
201
+ // Return connection interface
202
+ return {
203
+ serverName,
204
+ async listTools() {
205
+ const response = await sendRequest(socketPath, {
206
+ id: generateRequestId(),
207
+ type: 'listTools',
208
+ });
209
+ if (!response.success) {
210
+ throw new Error(response.error?.message ?? 'listTools failed');
211
+ }
212
+ return response.data;
213
+ },
214
+ async callTool(toolName, args) {
215
+ const response = await sendRequest(socketPath, {
216
+ id: generateRequestId(),
217
+ type: 'callTool',
218
+ toolName,
219
+ args,
220
+ });
221
+ if (!response.success) {
222
+ throw new Error(response.error?.message ?? 'callTool failed');
223
+ }
224
+ return response.data;
225
+ },
226
+ async getInstructions() {
227
+ const response = await sendRequest(socketPath, {
228
+ id: generateRequestId(),
229
+ type: 'getInstructions',
230
+ });
231
+ if (!response.success) {
232
+ throw new Error(response.error?.message ?? 'getInstructions failed');
233
+ }
234
+ return response.data;
235
+ },
236
+ async close() {
237
+ // Just disconnect, don't tell daemon to close (let it idle timeout)
238
+ debug(`[daemon-client] Disconnecting from ${serverName} daemon`);
239
+ },
240
+ };
241
+ }
242
+ /**
243
+ * Clean up any orphaned daemon processes and sockets
244
+ * Call this on CLI startup
245
+ */
246
+ export async function cleanupOrphanedDaemons() {
247
+ const socketDir = getSocketDir();
248
+ if (!existsSync(socketDir)) {
249
+ return;
250
+ }
251
+ try {
252
+ const files = (await readdir(socketDir)).filter((file) => file.endsWith('.pid'));
253
+ for (const file of files) {
254
+ const serverName = file.replace('.pid', '');
255
+ const pidInfo = readPidFile(serverName);
256
+ if (pidInfo && !isProcessRunning(pidInfo.pid)) {
257
+ debug(`[daemon-client] Cleaning up orphaned daemon: ${serverName}`);
258
+ removePidFile(serverName);
259
+ removeSocketFile(serverName);
260
+ }
261
+ }
262
+ }
263
+ catch {
264
+ // Ignore errors during cleanup scan
265
+ }
266
+ }