@youweichen/pi-harness 0.0.0-stage → 1.1.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 (456) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +74 -2
  3. package/bin/pi-harness.mjs +1819 -0
  4. package/deploy/com.youweichen.pi-harness.plist +48 -0
  5. package/deploy/nginx-subpath.conf +88 -0
  6. package/deploy/pi-harness-task.xml +71 -0
  7. package/deploy/pi-harness.service +31 -0
  8. package/dist/server/agent-service.js +3131 -0
  9. package/dist/server/app-version.js +19 -0
  10. package/dist/server/attachments.js +526 -0
  11. package/dist/server/auth-file.js +61 -0
  12. package/dist/server/bg-servers.js +154 -0
  13. package/dist/server/client-eviction.js +19 -0
  14. package/dist/server/client-id.js +9 -0
  15. package/dist/server/client-state.js +202 -0
  16. package/dist/server/codemode-image-routes.js +36 -0
  17. package/dist/server/codemode-presentation.js +19 -0
  18. package/dist/server/component-updates.js +135 -0
  19. package/dist/server/context-breakdown.js +56 -0
  20. package/dist/server/control-socket.js +178 -0
  21. package/dist/server/conversation.js +65 -0
  22. package/dist/server/dir-browser.js +77 -0
  23. package/dist/server/editor-snapshot.js +38 -0
  24. package/dist/server/ensure-bash.js +80 -0
  25. package/dist/server/extension-display.js +23 -0
  26. package/dist/server/extensions-catalog.js +64 -0
  27. package/dist/server/extensions-model.js +46 -0
  28. package/dist/server/extensions-routes.js +255 -0
  29. package/dist/server/extensions-worker.js +188 -0
  30. package/dist/server/files-service.js +787 -0
  31. package/dist/server/index.js +1033 -0
  32. package/dist/server/markdown-images.js +42 -0
  33. package/dist/server/model-admin.js +801 -0
  34. package/dist/server/model-config-merge.js +32 -0
  35. package/dist/server/native-mcp-config.js +103 -0
  36. package/dist/server/native-mcp-presentation.js +12 -0
  37. package/dist/server/native-prompt-template.js +12 -0
  38. package/dist/server/native-tools.js +32 -0
  39. package/dist/server/node-agent.js +262 -0
  40. package/dist/server/node-sources.js +142 -0
  41. package/dist/server/node-workbench.js +729 -0
  42. package/dist/server/origin-policy.js +50 -0
  43. package/dist/server/patch-node-pty.js +96 -0
  44. package/dist/server/pi-environment.js +203 -0
  45. package/dist/server/plan/extension.js +74 -0
  46. package/dist/server/plan/progress.js +90 -0
  47. package/dist/server/plan/settings.js +41 -0
  48. package/dist/server/plan/state.js +123 -0
  49. package/dist/server/plugin-facilities.js +304 -0
  50. package/dist/server/plugin-updater.js +245 -0
  51. package/dist/server/plugins.js +712 -0
  52. package/dist/server/private-file.js +28 -0
  53. package/dist/server/process-utils.js +112 -0
  54. package/dist/server/prompt-delivery.js +80 -0
  55. package/dist/server/protocol-version.js +11 -0
  56. package/dist/server/protocol.js +6 -0
  57. package/dist/server/provider-auth.js +99 -0
  58. package/dist/server/query-cache.js +45 -0
  59. package/dist/server/recovery-state.js +37 -0
  60. package/dist/server/scm.js +459 -0
  61. package/dist/server/security-headers.js +24 -0
  62. package/dist/server/serialize.js +254 -0
  63. package/dist/server/session-file-read.js +225 -0
  64. package/dist/server/session-record-worker.js +21 -0
  65. package/dist/server/session-reload.js +13 -0
  66. package/dist/server/session-tree-controller.js +250 -0
  67. package/dist/server/session-tree.js +191 -0
  68. package/dist/server/settings-service.js +53 -0
  69. package/dist/server/skills-service.js +87 -0
  70. package/dist/server/slash-commands.js +286 -0
  71. package/dist/server/sqlite-preview.js +58 -0
  72. package/dist/server/sqlite-query.js +67 -0
  73. package/dist/server/sqlite-worker.js +12 -0
  74. package/dist/server/system-prompt-files.js +158 -0
  75. package/dist/server/system-prompt-routes.js +40 -0
  76. package/dist/server/system-prompt-view.js +49 -0
  77. package/dist/server/task-progress.js +233 -0
  78. package/dist/server/terminals.js +833 -0
  79. package/dist/server/text-sniff.js +269 -0
  80. package/dist/server/thinking-timing.js +91 -0
  81. package/dist/server/token-auth.js +37 -0
  82. package/dist/server/tool-output.js +97 -0
  83. package/dist/server/tool-text-incidents.js +82 -0
  84. package/dist/server/tool-text.js +67 -0
  85. package/dist/server/uploads.js +112 -0
  86. package/dist/server/user-attachments.js +96 -0
  87. package/dist/server/webui-context.js +257 -0
  88. package/dist/server/wiki-links.js +136 -0
  89. package/dist/server/wiki-pdf-worker.js +23 -0
  90. package/dist/server/wiki-pdf.js +36 -0
  91. package/dist/server/wiki-routes.js +184 -0
  92. package/dist/server/wiki-service.js +524 -0
  93. package/electron/main.mjs +607 -0
  94. package/extensions/webui.ts +192 -0
  95. package/package.json +122 -4
  96. package/web/dist/apple-touch-icon.png +0 -0
  97. package/web/dist/assets/NodeWorkbench-BiP2ktQP.js +3 -0
  98. package/web/dist/assets/TerminalPanel-CKaSUsN-.js +2 -0
  99. package/web/dist/assets/abnfDiagram-VCTEODGH-CnwRJDRL.js +1 -0
  100. package/web/dist/assets/arc-CG4Q7-W_.js +1 -0
  101. package/web/dist/assets/architectureDiagram-5GKGNRK7-BrGqX_Rg.js +36 -0
  102. package/web/dist/assets/blockDiagram-I7D4REHJ-C2t0PjZT.js +129 -0
  103. package/web/dist/assets/c4Diagram-7LVT6UL2-CadrcgGL.js +38 -0
  104. package/web/dist/assets/channel-C27Jizb6.js +1 -0
  105. package/web/dist/assets/chunk-2Q5K7J3B-CC3V2ITZ.js +1 -0
  106. package/web/dist/assets/chunk-5VM5RSS4-DqKSbGmE.js +15 -0
  107. package/web/dist/assets/chunk-F27PBJKO-_CDr3JbY.js +1 -0
  108. package/web/dist/assets/chunk-IMKFNOWR-BIR4H4fX.js +231 -0
  109. package/web/dist/assets/chunk-JWPE2WC7-CrsdBGXT.js +1 -0
  110. package/web/dist/assets/chunk-POPQ4Y6H-C6q9kQmf.js +1 -0
  111. package/web/dist/assets/chunk-SVP7TREG-l2XXTr2v.js +88 -0
  112. package/web/dist/assets/chunk-TICWLB2K-29I_mZgB.js +206 -0
  113. package/web/dist/assets/chunk-XXDRQBXY-htV9FBqi.js +1 -0
  114. package/web/dist/assets/classDiagram-ZZMXUADV-DYTBrAEP.js +1 -0
  115. package/web/dist/assets/classDiagram-v2-VYDZK3BY-DYTBrAEP.js +1 -0
  116. package/web/dist/assets/cose-bilkent-JH36ORCC-Cop-ihyx.js +1 -0
  117. package/web/dist/assets/cynefin-OW5HDTMX-IEk8qBRq.js +178 -0
  118. package/web/dist/assets/cynefinDiagram-5FMLGOSQ-Bmzt6Qb5.js +62 -0
  119. package/web/dist/assets/cytoscape.esm-Bch-eiPH.js +331 -0
  120. package/web/dist/assets/dagre-GXQ25YYZ-DiXTJYYU.js +4 -0
  121. package/web/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
  122. package/web/dist/assets/diagram-S7CK7UJ4-BT7CLuNk.js +30 -0
  123. package/web/dist/assets/diagram-UQ7AKVKN-BZZ3nTCO.js +41 -0
  124. package/web/dist/assets/diagram-VSXAHHWV-Dal8LeZh.js +3 -0
  125. package/web/dist/assets/diagram-VX7I27RA-D6V9FfNA.js +24 -0
  126. package/web/dist/assets/diagram-Z3DM3KII-KUAIcFiD.js +24 -0
  127. package/web/dist/assets/ebnfDiagram-PWID7BFC-s0FEPZrV.js +1 -0
  128. package/web/dist/assets/erDiagram-RLTQ6QDP-CiIQPFCD.js +99 -0
  129. package/web/dist/assets/flowDiagram-HODETNUW-CvG4MYWn.js +168 -0
  130. package/web/dist/assets/ganttDiagram-EL5Y4UJY-CUf115ii.js +292 -0
  131. package/web/dist/assets/gitGraphDiagram-WWUBYQGX-o3K1r6fL.js +106 -0
  132. package/web/dist/assets/index-C3vDFSEV.css +10 -0
  133. package/web/dist/assets/index-C5or_GAq.js +276 -0
  134. package/web/dist/assets/infoDiagram-27XIBGKW-BxiBeQ8E.js +2 -0
  135. package/web/dist/assets/init-Gi6I4Gst.js +1 -0
  136. package/web/dist/assets/ishikawaDiagram-5VMMS53U-B1ONuCkH.js +70 -0
  137. package/web/dist/assets/journeyDiagram-3NMN7TZE-DwmuoXcX.js +139 -0
  138. package/web/dist/assets/kanban-definition-UXKFOSKX-pMKHubxH.js +89 -0
  139. package/web/dist/assets/katex-C5jXJg4s.js +257 -0
  140. package/web/dist/assets/layout-CEwfjQR0.js +1 -0
  141. package/web/dist/assets/linear-D22Gdbt1.js +1 -0
  142. package/web/dist/assets/markdown-BhJ_LInD.js +51 -0
  143. package/web/dist/assets/mermaid.core-CO2nrD04.js +312 -0
  144. package/web/dist/assets/mindmap-definition-YA3MSWOX-9MQn9ZAp.js +96 -0
  145. package/web/dist/assets/ordinal-Cboi1Yqb.js +1 -0
  146. package/web/dist/assets/pegDiagram-XKGWAZYB-Dy6fLjM-.js +1 -0
  147. package/web/dist/assets/pieDiagram-E7YTZNPT-EDLez6nA.js +39 -0
  148. package/web/dist/assets/quadrantDiagram-AXDQQJYC-Cgcd9mvy.js +7 -0
  149. package/web/dist/assets/railroadDiagram-O6MQD6OU-B4Td0tjj.js +1 -0
  150. package/web/dist/assets/react-BddUxV-9.js +24 -0
  151. package/web/dist/assets/requirementDiagram-BXWQKSXE-aF2QNZ78.js +84 -0
  152. package/web/dist/assets/sankeyDiagram-P5KCCOFB-DgTYwYMa.js +40 -0
  153. package/web/dist/assets/sequenceDiagram-WJ2MYXX4-EUcddWUc.js +162 -0
  154. package/web/dist/assets/sizeCapture-INFHLROL-CXHOv9Sx.js +1 -0
  155. package/web/dist/assets/stateDiagram-D77RDMKH-B-xlgfaK.js +1 -0
  156. package/web/dist/assets/stateDiagram-v2-MP3YSRHH-kwAwAlWX.js +1 -0
  157. package/web/dist/assets/swimlanes-42K2YHIH-BtFBOCDh.js +1 -0
  158. package/web/dist/assets/swimlanesDiagram-VR7AAH4N-D5j5Cj3S.js +8 -0
  159. package/web/dist/assets/timeline-definition-24CTP7MA-CZlQY6pb.js +120 -0
  160. package/web/dist/assets/vennDiagram-4TSXK5OY-Cn61dJvL.js +34 -0
  161. package/web/dist/assets/wardleyDiagram-VM6X3IG4-fiMNrWpr.js +78 -0
  162. package/web/dist/assets/xterm-6GBZ9nXN.css +32 -0
  163. package/web/dist/assets/xterm-D1D2FVe3.js +38 -0
  164. package/web/dist/assets/xychartDiagram-S5SC5T6Z-CuzM_sof.js +7 -0
  165. package/web/dist/brand-mark.svg +5 -0
  166. package/web/dist/favicon-32.png +0 -0
  167. package/web/dist/favicon.svg +5 -0
  168. package/web/dist/fonts/WIKI-FONTS.md +5 -0
  169. package/web/dist/fonts/ibm-plex-mono/ibm-plex-mono-latin-400-normal.woff2 +0 -0
  170. package/web/dist/fonts/ibm-plex-mono/ibm-plex-mono-latin-500-normal.woff2 +0 -0
  171. package/web/dist/fonts/ibm-plex-mono/ibm-plex-mono-latin-600-normal.woff2 +0 -0
  172. package/web/dist/fonts/ibm-plex-mono/ibm-plex-mono-latin-ext-400-normal.woff2 +0 -0
  173. package/web/dist/fonts/ibm-plex-mono/ibm-plex-mono-latin-ext-500-normal.woff2 +0 -0
  174. package/web/dist/fonts/ibm-plex-mono/ibm-plex-mono-latin-ext-600-normal.woff2 +0 -0
  175. package/web/dist/fonts/ibm-plex-sans/LICENSE +93 -0
  176. package/web/dist/fonts/ibm-plex-sans/ibm-plex-sans-latin-400-normal.woff2 +0 -0
  177. package/web/dist/fonts/ibm-plex-sans/ibm-plex-sans-latin-500-normal.woff2 +0 -0
  178. package/web/dist/fonts/ibm-plex-sans/ibm-plex-sans-latin-600-normal.woff2 +0 -0
  179. package/web/dist/fonts/ibm-plex-sans/ibm-plex-sans-latin-ext-400-normal.woff2 +0 -0
  180. package/web/dist/fonts/ibm-plex-sans/ibm-plex-sans-latin-ext-500-normal.woff2 +0 -0
  181. package/web/dist/fonts/ibm-plex-sans/ibm-plex-sans-latin-ext-600-normal.woff2 +0 -0
  182. package/web/dist/fonts/inter/inter-latin-400-normal.woff2 +0 -0
  183. package/web/dist/fonts/inter/inter-latin-500-normal.woff2 +0 -0
  184. package/web/dist/fonts/inter/inter-latin-600-normal.woff2 +0 -0
  185. package/web/dist/fonts/inter/inter-latin-700-normal.woff2 +0 -0
  186. package/web/dist/fonts/inter/inter-latin-800-normal.woff2 +0 -0
  187. package/web/dist/fonts/inter/inter-latin-ext-400-normal.woff2 +0 -0
  188. package/web/dist/fonts/inter/inter-latin-ext-500-normal.woff2 +0 -0
  189. package/web/dist/fonts/inter/inter-latin-ext-600-normal.woff2 +0 -0
  190. package/web/dist/fonts/inter/inter-latin-ext-700-normal.woff2 +0 -0
  191. package/web/dist/fonts/inter/inter-latin-ext-800-normal.woff2 +0 -0
  192. package/web/dist/fonts/jetbrains-mono/LICENSE +93 -0
  193. package/web/dist/fonts/jetbrains-mono/jetbrains-mono-latin-400-normal.woff2 +0 -0
  194. package/web/dist/fonts/jetbrains-mono/jetbrains-mono-latin-500-normal.woff2 +0 -0
  195. package/web/dist/fonts/jetbrains-mono/jetbrains-mono-latin-600-normal.woff2 +0 -0
  196. package/web/dist/fonts/jetbrains-mono/jetbrains-mono-latin-ext-400-normal.woff2 +0 -0
  197. package/web/dist/fonts/jetbrains-mono/jetbrains-mono-latin-ext-500-normal.woff2 +0 -0
  198. package/web/dist/fonts/jetbrains-mono/jetbrains-mono-latin-ext-600-normal.woff2 +0 -0
  199. package/web/dist/fonts/noto-serif-sc/LICENSE +94 -0
  200. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-0.woff2 +0 -0
  201. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-1.woff2 +0 -0
  202. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-10.woff2 +0 -0
  203. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-100.woff2 +0 -0
  204. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-11.woff2 +0 -0
  205. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-12.woff2 +0 -0
  206. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-13.woff2 +0 -0
  207. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-14.woff2 +0 -0
  208. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-15.woff2 +0 -0
  209. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-16.woff2 +0 -0
  210. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-17.woff2 +0 -0
  211. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-18.woff2 +0 -0
  212. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-19.woff2 +0 -0
  213. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-2.woff2 +0 -0
  214. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-20.woff2 +0 -0
  215. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-21.woff2 +0 -0
  216. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-22.woff2 +0 -0
  217. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-23.woff2 +0 -0
  218. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-24.woff2 +0 -0
  219. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-25.woff2 +0 -0
  220. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-26.woff2 +0 -0
  221. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-27.woff2 +0 -0
  222. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-28.woff2 +0 -0
  223. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-29.woff2 +0 -0
  224. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-3.woff2 +0 -0
  225. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-30.woff2 +0 -0
  226. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-31.woff2 +0 -0
  227. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-32.woff2 +0 -0
  228. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-33.woff2 +0 -0
  229. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-34.woff2 +0 -0
  230. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-35.woff2 +0 -0
  231. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-36.woff2 +0 -0
  232. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-37.woff2 +0 -0
  233. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-38.woff2 +0 -0
  234. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-39.woff2 +0 -0
  235. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-4.woff2 +0 -0
  236. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-40.woff2 +0 -0
  237. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-41.woff2 +0 -0
  238. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-42.woff2 +0 -0
  239. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-43.woff2 +0 -0
  240. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-44.woff2 +0 -0
  241. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-45.woff2 +0 -0
  242. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-46.woff2 +0 -0
  243. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-47.woff2 +0 -0
  244. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-48.woff2 +0 -0
  245. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-49.woff2 +0 -0
  246. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-5.woff2 +0 -0
  247. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-50.woff2 +0 -0
  248. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-51.woff2 +0 -0
  249. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-52.woff2 +0 -0
  250. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-53.woff2 +0 -0
  251. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-54.woff2 +0 -0
  252. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-55.woff2 +0 -0
  253. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-56.woff2 +0 -0
  254. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-57.woff2 +0 -0
  255. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-58.woff2 +0 -0
  256. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-59.woff2 +0 -0
  257. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-6.woff2 +0 -0
  258. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-60.woff2 +0 -0
  259. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-61.woff2 +0 -0
  260. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-62.woff2 +0 -0
  261. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-63.woff2 +0 -0
  262. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-64.woff2 +0 -0
  263. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-65.woff2 +0 -0
  264. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-66.woff2 +0 -0
  265. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-67.woff2 +0 -0
  266. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-68.woff2 +0 -0
  267. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-69.woff2 +0 -0
  268. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-7.woff2 +0 -0
  269. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-70.woff2 +0 -0
  270. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-71.woff2 +0 -0
  271. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-72.woff2 +0 -0
  272. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-73.woff2 +0 -0
  273. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-74.woff2 +0 -0
  274. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-75.woff2 +0 -0
  275. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-76.woff2 +0 -0
  276. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-77.woff2 +0 -0
  277. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-78.woff2 +0 -0
  278. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-79.woff2 +0 -0
  279. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-8.woff2 +0 -0
  280. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-80.woff2 +0 -0
  281. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-81.woff2 +0 -0
  282. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-82.woff2 +0 -0
  283. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-83.woff2 +0 -0
  284. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-84.woff2 +0 -0
  285. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-85.woff2 +0 -0
  286. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-86.woff2 +0 -0
  287. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-87.woff2 +0 -0
  288. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-88.woff2 +0 -0
  289. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-89.woff2 +0 -0
  290. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-9.woff2 +0 -0
  291. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-90.woff2 +0 -0
  292. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-91.woff2 +0 -0
  293. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-92.woff2 +0 -0
  294. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-93.woff2 +0 -0
  295. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-94.woff2 +0 -0
  296. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-95.woff2 +0 -0
  297. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-96.woff2 +0 -0
  298. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-97.woff2 +0 -0
  299. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-98.woff2 +0 -0
  300. package/web/dist/fonts/noto-serif-sc/noto-serif-sc-500-99.woff2 +0 -0
  301. package/web/dist/fonts/source-serif-4/LICENSE +93 -0
  302. package/web/dist/fonts/source-serif-4/source-serif-4-500-0.woff2 +0 -0
  303. package/web/dist/fonts/source-serif-4/source-serif-4-500-1.woff2 +0 -0
  304. package/web/dist/fonts/source-serif-4/source-serif-4-500-2.woff2 +0 -0
  305. package/web/dist/fonts/source-serif-4/source-serif-4-500-3.woff2 +0 -0
  306. package/web/dist/fonts/source-serif-4/source-serif-4-500-4.woff2 +0 -0
  307. package/web/dist/fonts/source-serif-4/source-serif-4-500-5.woff2 +0 -0
  308. package/web/dist/icon/1a-mark.svg +1 -0
  309. package/web/dist/icon.ico +0 -0
  310. package/web/dist/index.html +18 -0
  311. package/web/public/apple-touch-icon.png +0 -0
  312. package/web/public/brand-mark.svg +5 -0
  313. package/web/public/favicon-32.png +0 -0
  314. package/web/public/favicon.svg +5 -0
  315. package/web/public/fonts/WIKI-FONTS.md +5 -0
  316. package/web/public/fonts/ibm-plex-mono/ibm-plex-mono-latin-400-normal.woff2 +0 -0
  317. package/web/public/fonts/ibm-plex-mono/ibm-plex-mono-latin-500-normal.woff2 +0 -0
  318. package/web/public/fonts/ibm-plex-mono/ibm-plex-mono-latin-600-normal.woff2 +0 -0
  319. package/web/public/fonts/ibm-plex-mono/ibm-plex-mono-latin-ext-400-normal.woff2 +0 -0
  320. package/web/public/fonts/ibm-plex-mono/ibm-plex-mono-latin-ext-500-normal.woff2 +0 -0
  321. package/web/public/fonts/ibm-plex-mono/ibm-plex-mono-latin-ext-600-normal.woff2 +0 -0
  322. package/web/public/fonts/ibm-plex-sans/LICENSE +93 -0
  323. package/web/public/fonts/ibm-plex-sans/ibm-plex-sans-latin-400-normal.woff2 +0 -0
  324. package/web/public/fonts/ibm-plex-sans/ibm-plex-sans-latin-500-normal.woff2 +0 -0
  325. package/web/public/fonts/ibm-plex-sans/ibm-plex-sans-latin-600-normal.woff2 +0 -0
  326. package/web/public/fonts/ibm-plex-sans/ibm-plex-sans-latin-ext-400-normal.woff2 +0 -0
  327. package/web/public/fonts/ibm-plex-sans/ibm-plex-sans-latin-ext-500-normal.woff2 +0 -0
  328. package/web/public/fonts/ibm-plex-sans/ibm-plex-sans-latin-ext-600-normal.woff2 +0 -0
  329. package/web/public/fonts/inter/inter-latin-400-normal.woff2 +0 -0
  330. package/web/public/fonts/inter/inter-latin-500-normal.woff2 +0 -0
  331. package/web/public/fonts/inter/inter-latin-600-normal.woff2 +0 -0
  332. package/web/public/fonts/inter/inter-latin-700-normal.woff2 +0 -0
  333. package/web/public/fonts/inter/inter-latin-800-normal.woff2 +0 -0
  334. package/web/public/fonts/inter/inter-latin-ext-400-normal.woff2 +0 -0
  335. package/web/public/fonts/inter/inter-latin-ext-500-normal.woff2 +0 -0
  336. package/web/public/fonts/inter/inter-latin-ext-600-normal.woff2 +0 -0
  337. package/web/public/fonts/inter/inter-latin-ext-700-normal.woff2 +0 -0
  338. package/web/public/fonts/inter/inter-latin-ext-800-normal.woff2 +0 -0
  339. package/web/public/fonts/jetbrains-mono/LICENSE +93 -0
  340. package/web/public/fonts/jetbrains-mono/jetbrains-mono-latin-400-normal.woff2 +0 -0
  341. package/web/public/fonts/jetbrains-mono/jetbrains-mono-latin-500-normal.woff2 +0 -0
  342. package/web/public/fonts/jetbrains-mono/jetbrains-mono-latin-600-normal.woff2 +0 -0
  343. package/web/public/fonts/jetbrains-mono/jetbrains-mono-latin-ext-400-normal.woff2 +0 -0
  344. package/web/public/fonts/jetbrains-mono/jetbrains-mono-latin-ext-500-normal.woff2 +0 -0
  345. package/web/public/fonts/jetbrains-mono/jetbrains-mono-latin-ext-600-normal.woff2 +0 -0
  346. package/web/public/fonts/noto-serif-sc/LICENSE +94 -0
  347. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-0.woff2 +0 -0
  348. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-1.woff2 +0 -0
  349. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-10.woff2 +0 -0
  350. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-100.woff2 +0 -0
  351. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-11.woff2 +0 -0
  352. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-12.woff2 +0 -0
  353. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-13.woff2 +0 -0
  354. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-14.woff2 +0 -0
  355. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-15.woff2 +0 -0
  356. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-16.woff2 +0 -0
  357. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-17.woff2 +0 -0
  358. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-18.woff2 +0 -0
  359. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-19.woff2 +0 -0
  360. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-2.woff2 +0 -0
  361. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-20.woff2 +0 -0
  362. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-21.woff2 +0 -0
  363. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-22.woff2 +0 -0
  364. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-23.woff2 +0 -0
  365. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-24.woff2 +0 -0
  366. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-25.woff2 +0 -0
  367. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-26.woff2 +0 -0
  368. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-27.woff2 +0 -0
  369. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-28.woff2 +0 -0
  370. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-29.woff2 +0 -0
  371. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-3.woff2 +0 -0
  372. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-30.woff2 +0 -0
  373. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-31.woff2 +0 -0
  374. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-32.woff2 +0 -0
  375. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-33.woff2 +0 -0
  376. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-34.woff2 +0 -0
  377. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-35.woff2 +0 -0
  378. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-36.woff2 +0 -0
  379. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-37.woff2 +0 -0
  380. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-38.woff2 +0 -0
  381. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-39.woff2 +0 -0
  382. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-4.woff2 +0 -0
  383. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-40.woff2 +0 -0
  384. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-41.woff2 +0 -0
  385. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-42.woff2 +0 -0
  386. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-43.woff2 +0 -0
  387. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-44.woff2 +0 -0
  388. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-45.woff2 +0 -0
  389. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-46.woff2 +0 -0
  390. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-47.woff2 +0 -0
  391. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-48.woff2 +0 -0
  392. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-49.woff2 +0 -0
  393. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-5.woff2 +0 -0
  394. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-50.woff2 +0 -0
  395. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-51.woff2 +0 -0
  396. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-52.woff2 +0 -0
  397. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-53.woff2 +0 -0
  398. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-54.woff2 +0 -0
  399. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-55.woff2 +0 -0
  400. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-56.woff2 +0 -0
  401. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-57.woff2 +0 -0
  402. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-58.woff2 +0 -0
  403. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-59.woff2 +0 -0
  404. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-6.woff2 +0 -0
  405. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-60.woff2 +0 -0
  406. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-61.woff2 +0 -0
  407. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-62.woff2 +0 -0
  408. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-63.woff2 +0 -0
  409. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-64.woff2 +0 -0
  410. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-65.woff2 +0 -0
  411. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-66.woff2 +0 -0
  412. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-67.woff2 +0 -0
  413. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-68.woff2 +0 -0
  414. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-69.woff2 +0 -0
  415. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-7.woff2 +0 -0
  416. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-70.woff2 +0 -0
  417. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-71.woff2 +0 -0
  418. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-72.woff2 +0 -0
  419. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-73.woff2 +0 -0
  420. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-74.woff2 +0 -0
  421. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-75.woff2 +0 -0
  422. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-76.woff2 +0 -0
  423. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-77.woff2 +0 -0
  424. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-78.woff2 +0 -0
  425. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-79.woff2 +0 -0
  426. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-8.woff2 +0 -0
  427. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-80.woff2 +0 -0
  428. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-81.woff2 +0 -0
  429. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-82.woff2 +0 -0
  430. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-83.woff2 +0 -0
  431. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-84.woff2 +0 -0
  432. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-85.woff2 +0 -0
  433. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-86.woff2 +0 -0
  434. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-87.woff2 +0 -0
  435. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-88.woff2 +0 -0
  436. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-89.woff2 +0 -0
  437. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-9.woff2 +0 -0
  438. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-90.woff2 +0 -0
  439. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-91.woff2 +0 -0
  440. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-92.woff2 +0 -0
  441. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-93.woff2 +0 -0
  442. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-94.woff2 +0 -0
  443. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-95.woff2 +0 -0
  444. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-96.woff2 +0 -0
  445. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-97.woff2 +0 -0
  446. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-98.woff2 +0 -0
  447. package/web/public/fonts/noto-serif-sc/noto-serif-sc-500-99.woff2 +0 -0
  448. package/web/public/fonts/source-serif-4/LICENSE +93 -0
  449. package/web/public/fonts/source-serif-4/source-serif-4-500-0.woff2 +0 -0
  450. package/web/public/fonts/source-serif-4/source-serif-4-500-1.woff2 +0 -0
  451. package/web/public/fonts/source-serif-4/source-serif-4-500-2.woff2 +0 -0
  452. package/web/public/fonts/source-serif-4/source-serif-4-500-3.woff2 +0 -0
  453. package/web/public/fonts/source-serif-4/source-serif-4-500-4.woff2 +0 -0
  454. package/web/public/fonts/source-serif-4/source-serif-4-500-5.woff2 +0 -0
  455. package/web/public/icon/1a-mark.svg +1 -0
  456. package/web/public/icon.ico +0 -0
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Origin / Host admission policy for HTTP and WebSocket requests.
3
+ *
4
+ * Pure (no env, no I/O) so it can be unit-tested; server/index.ts builds one
5
+ * instance from the environment.
6
+ */
7
+ const LOOPBACK_BIND_HOSTS = new Set(["127.0.0.1", "localhost", "::1", "[::1]"]);
8
+ /** URL#hostname spelling of the loopback names a local browser can use. */
9
+ const LOOPBACK_HOSTNAMES = ["localhost", "127.0.0.1", "[::1]"];
10
+ /** "host" or "host:port" → { hostname, port }. */
11
+ export function parseAuthority(a) {
12
+ try {
13
+ const u = new URL(`http://${a}`);
14
+ return { hostname: u.hostname.toLowerCase(), port: u.port || "80" };
15
+ }
16
+ catch {
17
+ return { hostname: "", port: "" };
18
+ }
19
+ }
20
+ /**
21
+ * Effective Host allowlist. An explicit list always wins. When the server is
22
+ * bound to loopback only, default to loopback hostnames: otherwise a DNS
23
+ * rebinding page (evil.com → 127.0.0.1) passes the same-authority Origin/Host
24
+ * check and gets full control of the agent. Non-loopback binds (LAN, Docker,
25
+ * reverse proxy) have no safe default and keep the old behavior.
26
+ */
27
+ export function effectiveAllowHosts(bindHost, allowHosts) {
28
+ if (allowHosts.length > 0)
29
+ return allowHosts;
30
+ return LOOPBACK_BIND_HOSTS.has(bindHost.toLowerCase()) ? LOOPBACK_HOSTNAMES : [];
31
+ }
32
+ export function createOriginPolicy(options) {
33
+ const allowHosts = effectiveAllowHosts(options.bindHost, options.allowHosts);
34
+ return (headers) => {
35
+ const host = parseAuthority((headers.host ?? "").toLowerCase());
36
+ if (allowHosts.length > 0 && !allowHosts.includes(host.hostname))
37
+ return false;
38
+ const origin = headers.origin;
39
+ if (!origin)
40
+ return true; // non-browser client
41
+ const o = origin.toLowerCase();
42
+ if (options.allowOrigins.includes(o))
43
+ return true;
44
+ if (o === "null")
45
+ return false; // file:// pages etc. are not trusted
46
+ const ori = parseAuthority(o.replace(/^[a-z]+:\/\//, ""));
47
+ // Same host:port only; other ports on the same host are different origins.
48
+ return ori.hostname === host.hostname && ori.port === host.port;
49
+ };
50
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * node-pty compatibility patches for Node `--watch` and Windows ConPTY.
3
+ *
4
+ * Node's `--watch` mode (the dev script runs `node --watch --import tsx`)
5
+ * pushes `watch:require` / `watch:import` messages over the IPC channels of
6
+ * spawned worker threads AND forked child processes to track module
7
+ * dependencies. node-pty 1.1.0 does not recognize these and mishandles them in
8
+ * two places:
9
+ *
10
+ * · windowsConoutConnection.js — logs every unknown worker message via
11
+ * `console.warn`, producing hundreds of "Unexpected ConoutWorkerMessage"
12
+ * lines per terminal (the SCM panel's hidden query PTY triggers it on every
13
+ * git-view open).
14
+ * · windowsPtyAgent.js — the kill path's `_getConsoleProcessList()` treats ANY
15
+ * message from the forked console-list agent as its reply; a watch message
16
+ * has no `consoleProcessList` property, so `message.consoleProcessList` is
17
+ * undefined and the `.forEach` on it crashes the server.
18
+ *
19
+ * Both are benign protocol noise: the ConPTY worker only ever sends its READY
20
+ * sentinel and the agent only sends `{ consoleProcessList }` — both of which
21
+ * node-pty handles. These patches make node-pty ignore unrelated IPC traffic.
22
+ * The console-list helper also needs to report an empty list when a shell has
23
+ * already exited: node-pty 1.1.0 lets AttachConsole throw at module scope,
24
+ * producing an uncaught stack trace and making its parent wait five seconds.
25
+ *
26
+ * IMPORTANT: this module MUST be imported before node-pty (see terminals.ts) —
27
+ * it rewrites the installed copies on disk so the subsequently-compiled
28
+ * node-pty modules contain the fixed handlers. Best-effort and idempotent
29
+ * (checks before rewriting), mirroring the spawn-helper chmod self-heal; a
30
+ * read-only node_modules or a different node-pty version just skips the patch
31
+ * (the runtime console.warn filter in terminals.ts still guards the flood).
32
+ */
33
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
34
+ import { createRequire } from "node:module";
35
+ import { dirname, join } from "node:path";
36
+ const require = createRequire(import.meta.url);
37
+ /** Absolute path of the installed node-pty package root, or null. */
38
+ function ptyPackageDir() {
39
+ try {
40
+ // require.resolve("node-pty") → <pkg>/lib/index.js → package root is two up.
41
+ return dirname(dirname(require.resolve("node-pty")));
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ }
47
+ /** Idempotently replace one code fragment in a node-pty source file. */
48
+ function patchFile(pkgDir, rel, find, replace) {
49
+ try {
50
+ const file = join(pkgDir, rel);
51
+ if (!existsSync(file))
52
+ return;
53
+ const src = readFileSync(file, "utf8");
54
+ if (src.includes(replace))
55
+ return; // already patched
56
+ if (!src.includes(find))
57
+ return; // unexpected content — leave alone
58
+ writeFileSync(file, src.replace(find, replace), "utf8");
59
+ }
60
+ catch {
61
+ // best-effort — the runtime console.warn filter still guards the flood
62
+ }
63
+ }
64
+ function applyPatches() {
65
+ const pkgDir = ptyPackageDir();
66
+ if (!pkgDir)
67
+ return;
68
+ // 1. ConoutConnection: ignore unknown worker messages instead of warning.
69
+ patchFile(pkgDir, "lib/windowsConoutConnection.js", "console.warn('Unexpected ConoutWorkerMessage', message);", "break; // pi-harness: ignore Node --watch worker messages");
70
+ // 2. windowsPtyAgent kill path: only accept a real console-list reply
71
+ // (watch messages arrive first on the fork channel and carry no
72
+ // consoleProcessList — previously resolved undefined and crashed).
73
+ patchFile(pkgDir, "lib/windowsPtyAgent.js", "agent.on('message', function (message) {\n clearTimeout(timeout);\n resolve(message.consoleProcessList);\n });", [
74
+ " agent.on('message', function (message) {",
75
+ " // pi-harness: ignore Node --watch messages on the fork channel",
76
+ " if (message && Array.isArray(message.consoleProcessList)) {",
77
+ " clearTimeout(timeout);",
78
+ " resolve(message.consoleProcessList);",
79
+ " }",
80
+ " });",
81
+ ].join("\n"));
82
+ // 3. A shell may exit before the console-list helper attaches. Always reply
83
+ // to the parent so a normal terminal close cannot print an uncaught error
84
+ // or hold its cleanup path for the full five-second fallback timeout.
85
+ patchFile(pkgDir, "lib/conpty_console_list_agent.js", "var consoleProcessList = getConsoleProcessList(shellPid);\nprocess.send({ consoleProcessList: consoleProcessList });", [
86
+ "try {",
87
+ " var consoleProcessList = getConsoleProcessList(shellPid);",
88
+ " process.send({ consoleProcessList: consoleProcessList });",
89
+ "} catch (error) {",
90
+ " if (!error || error.message !== 'AttachConsole failed') throw error;",
91
+ " // pi-harness: the console may already be gone during ConPTY teardown",
92
+ " process.send({ consoleProcessList: [] });",
93
+ "}",
94
+ ].join("\n"));
95
+ }
96
+ applyPatches();
@@ -0,0 +1,203 @@
1
+ import { spawn, spawnSync } from "node:child_process";
2
+ import { existsSync, mkdirSync, readFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { appVersion } from "./app-version.js";
5
+ /**
6
+ * Whether the pi agent config looks ready: the agent dir exists and
7
+ * auth.json has at least one provider credential. Cached for 2s.
8
+ */
9
+ export class PiConfigProbe {
10
+ agentDir;
11
+ cache = null;
12
+ constructor(agentDir) {
13
+ this.agentDir = agentDir;
14
+ }
15
+ isConfigured() {
16
+ const now = Date.now();
17
+ const cached = this.cache;
18
+ if (cached && now - cached.at < 2000)
19
+ return cached.configured;
20
+ let configured = false;
21
+ try {
22
+ const authPath = join(this.agentDir, "auth.json");
23
+ if (existsSync(authPath)) {
24
+ const data = JSON.parse(readFileSync(authPath, "utf8"));
25
+ configured =
26
+ typeof data === "object" &&
27
+ data !== null &&
28
+ Object.keys(data).length > 0;
29
+ }
30
+ }
31
+ catch {
32
+ configured = false;
33
+ }
34
+ this.cache = { at: now, configured };
35
+ return configured;
36
+ }
37
+ invalidate() {
38
+ this.cache = null;
39
+ }
40
+ }
41
+ /**
42
+ * Whether the pi CLI binary is installed and runnable (`pi --version`
43
+ * probe). Cached machine-wide (same binary for every client) for 10s —
44
+ * the check is only rerun after install or when the cache expires.
45
+ */
46
+ let piCliProbe = null;
47
+ const PI_CLI_PROBE_TTL_MS = 10_000;
48
+ export function isPiCliInstalled() {
49
+ const now = Date.now();
50
+ const cached = piCliProbe;
51
+ if (cached && now - cached.at < PI_CLI_PROBE_TTL_MS)
52
+ return cached.installed;
53
+ let installed = false;
54
+ try {
55
+ const res = spawnSync("pi", ["--version"], {
56
+ timeout: 5000,
57
+ stdio: "ignore",
58
+ // Windows: `pi` resolves to a pi.cmd shim — spawnSync can only
59
+ // exec those through a shell (else ENOENT).
60
+ shell: process.platform === "win32",
61
+ });
62
+ installed = !res.error && res.status === 0;
63
+ }
64
+ catch {
65
+ installed = false;
66
+ }
67
+ piCliProbe = { at: now, installed };
68
+ return installed;
69
+ }
70
+ function invalidatePiCliProbe() {
71
+ piCliProbe = null;
72
+ }
73
+ /**
74
+ * Run a command async, collecting stdout+stderr; kills on timeout.
75
+ * Never throws / never crashes the server: spawn errors (ENOENT etc.)
76
+ * resolve with code -1 so callers can report them as notices.
77
+ */
78
+ function runAsync(cmd, args, timeoutMs, cwd) {
79
+ return new Promise((resolve) => {
80
+ let p;
81
+ try {
82
+ p = spawn(cmd, args, {
83
+ ...(cwd ? { cwd } : {}),
84
+ stdio: ["ignore", "pipe", "pipe"],
85
+ // Windows: npm and friends are .cmd shims — Node can only exec
86
+ // them through the shell (otherwise spawn npm → ENOENT).
87
+ shell: process.platform === "win32",
88
+ });
89
+ }
90
+ catch (err) {
91
+ resolve({ code: -1, out: String(err) });
92
+ return;
93
+ }
94
+ let out = "";
95
+ let settled = false;
96
+ const done = (code, text) => {
97
+ if (settled)
98
+ return;
99
+ settled = true;
100
+ clearTimeout(t);
101
+ resolve({ code, out: text ?? out });
102
+ };
103
+ const t = setTimeout(() => p.kill(), timeoutMs);
104
+ p.stdout?.on("data", (d) => (out += d.toString()));
105
+ p.stderr?.on("data", (d) => (out += d.toString()));
106
+ p.on("error", (err) => done(-1, String(err)));
107
+ p.on("close", (code) => done(code));
108
+ });
109
+ }
110
+ /** Simple numeric semver compare: >0 means a newer than b. */
111
+ export function compareVersions(a, b) {
112
+ const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
113
+ const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
114
+ for (let i = 0; i < 3; i++) {
115
+ const x = pa[i] ?? 0;
116
+ const y = pb[i] ?? 0;
117
+ if (x !== y)
118
+ return x - y;
119
+ }
120
+ return 0;
121
+ }
122
+ /** Ask the npm registry for the latest pi-harness version and report it. */
123
+ export async function checkUpdate(emit) {
124
+ const current = appVersion();
125
+ try {
126
+ // Fetch the full package doc (not /latest): it carries the per-version
127
+ // publish timestamps so the UI can hint when a version was JUST
128
+ // published and the registry/CDN caches may not have caught up yet.
129
+ const res = await fetch("https://registry.npmjs.org/@youweichen%2fpi-harness", {
130
+ signal: AbortSignal.timeout(8_000),
131
+ });
132
+ if (!res.ok)
133
+ throw new Error(`HTTP ${res.status}`);
134
+ const data = (await res.json());
135
+ const latest = data["dist-tags"]?.latest ?? null;
136
+ const latestPublishedAt = latest && data.time ? (data.time[latest] ?? null) : null;
137
+ const upToDate = latest === null || compareVersions(current, latest) >= 0;
138
+ emit({
139
+ type: "update_status",
140
+ current,
141
+ latest,
142
+ latestPublishedAt,
143
+ upToDate,
144
+ });
145
+ }
146
+ catch (err) {
147
+ emit({
148
+ type: "update_status",
149
+ current,
150
+ latest: null,
151
+ latestPublishedAt: null,
152
+ upToDate: false,
153
+ error: `检查更新失败:${err.message}`,
154
+ });
155
+ }
156
+ }
157
+ /**
158
+ * Auto-install the pi agent: ensure the config dir exists and install the
159
+ * pi CLI globally (npm i -g). Auth is configured afterwards via the API key
160
+ * form or by running `pi` in a terminal. Callers should re-send a snapshot
161
+ * afterwards so the UI re-probes the CLI.
162
+ */
163
+ export async function installPiAgent(agentDir, emit) {
164
+ try {
165
+ mkdirSync(agentDir, { recursive: true });
166
+ emit({
167
+ type: "notice",
168
+ level: "info",
169
+ text: "正在安装 pi agent CLI(npm i -g @earendil-works/pi-coding-agent)…",
170
+ });
171
+ const { code, out } = await runAsync("npm", ["i", "-g", "@earendil-works/pi-coding-agent"], 180_000);
172
+ if (code === 0) {
173
+ emit({
174
+ type: "notice",
175
+ level: "info",
176
+ text: "✅ pi agent CLI 安装完成。填入 API 密钥即可开始,或在终端运行 pi 完成登录。",
177
+ });
178
+ emit({ type: "install_result", ok: true, detail: "" });
179
+ }
180
+ else {
181
+ emit({
182
+ type: "notice",
183
+ level: "error",
184
+ text: `pi agent 安装失败(${code ?? "timeout"}):${out.slice(0, 400)}`,
185
+ });
186
+ emit({
187
+ type: "install_result",
188
+ ok: false,
189
+ detail: out.slice(0, 600),
190
+ });
191
+ }
192
+ }
193
+ catch (err) {
194
+ emit({
195
+ type: "notice",
196
+ level: "error",
197
+ text: `pi agent 安装失败:${err.message}`,
198
+ });
199
+ }
200
+ // The CLI may just have landed on PATH (or the install may have failed) —
201
+ // drop the probe cache so the next snapshot re-checks.
202
+ invalidatePiCliProbe();
203
+ }
@@ -0,0 +1,74 @@
1
+ import { Type } from "typebox";
2
+ import { editableState, latestPlan, snapshotFromDetails, transition } from "./state.js";
3
+ const string = (maxLength) => Type.String({ minLength: 1, maxLength });
4
+ export const planParameters = Type.Object({
5
+ action: Type.Union([Type.Literal("create"), Type.Literal("update")]),
6
+ planId: Type.Optional(string(128)), expectedRevision: Type.Optional(Type.Integer({ minimum: 1 })),
7
+ title: string(80), status: Type.Union([Type.Literal("active"), Type.Literal("completed"), Type.Literal("cancelled"), Type.Literal("failed")]),
8
+ steps: Type.Array(Type.Object({ id: string(64), title: string(80), detail: Type.Optional(string(500)) }), { minItems: 1, maxItems: 16, description: "Required for BOTH create and update, including completion. Send every step with its id, title and detail; never omit unchanged steps." }),
9
+ currentStepId: Type.Union([string(64), Type.Null()], { description: "Current step for active plans; terminal statuses clear it automatically." }), completedStepIds: Type.Array(string(64), { maxItems: 16, uniqueItems: true, description: "Completed step IDs. Explicit status completed completes all submitted steps; cancelled/failed preserve this list." }),
10
+ completionCriteria: string(240), changeSummary: Type.Optional(string(240)),
11
+ });
12
+ export function restorePlanContext(messages, branch) {
13
+ const latest = latestPlan(branch);
14
+ if (!latest || latest.snapshot.status !== "active")
15
+ return messages;
16
+ const { snapshot, toolCallId } = latest;
17
+ const visibleResult = messages.some(message => message.role === "toolResult" && message.toolName === "plan" && message.toolCallId === toolCallId && !message.isError && JSON.stringify(snapshotFromDetails(message.details)) === JSON.stringify(snapshot));
18
+ const visibleCall = messages.some(message => message.role === "assistant" && message.content.some(block => {
19
+ if (block.type !== "toolCall" || block.name !== "plan" || block.id !== toolCallId)
20
+ return false;
21
+ try {
22
+ const args = block.arguments;
23
+ const identity = args.action === "create" ? snapshot.revision === 1 : args.action === "update" && args.planId === snapshot.planId && args.expectedRevision === snapshot.revision - 1;
24
+ return identity && JSON.stringify(editableState(args)) === JSON.stringify(editableState(snapshot));
25
+ }
26
+ catch {
27
+ return false;
28
+ }
29
+ }));
30
+ const lastUser = messages.findLastIndex(message => message.role === "user");
31
+ const lastPlanResult = messages.slice(lastUser + 1).findLast(message => message.role === "toolResult" && message.toolName === "plan");
32
+ const failedCall = lastPlanResult?.role === "toolResult" && lastPlanResult.isError;
33
+ if (visibleResult && visibleCall && !failedCall)
34
+ return messages;
35
+ const content = `${failedCall ? "Plan call failed; the saved plan below is unchanged. To correct the call, submit the full state including steps, even if unchanged or marking completion. Use the saved planId and expectedRevision. This reminder does not authorize continuing work.\n" : ""}Historical plan background, not authorization to continue. Judge relevance to the current request yourself. For related, already authorized implementation, update this plan with its current identity and revision and continue without requesting separate plan approval. For unrelated multi-step work, create a new plan. Ordinary questions do not require a plan. Follow user/skill instructions and waiting requirements; this tool grants no extra permissions.\n${JSON.stringify({ action: "update", planId: snapshot.planId, expectedRevision: snapshot.revision, ...editableState(snapshot) })}`;
36
+ const reminder = { role: "custom", customType: "pi-harness:plan-background", content, display: false, timestamp: 0 };
37
+ const summary = messages.findLastIndex(message => message.role === "compactionSummary");
38
+ const user = messages.findLastIndex(message => message.role === "user");
39
+ let at = summary >= 0 ? summary + 1 : user >= 0 ? user : 0;
40
+ // A user interruption may occur inside an outstanding call/result group.
41
+ // Keep that entire group together when selecting the insertion boundary.
42
+ const open = new Map();
43
+ for (let index = 0; index < at; index++) {
44
+ const message = messages[index];
45
+ if (message.role === "assistant")
46
+ for (const block of message.content)
47
+ if (block.type === "toolCall")
48
+ open.set(block.id, index);
49
+ if (message.role === "toolResult")
50
+ open.delete(message.toolCallId);
51
+ }
52
+ if (open.size)
53
+ at = Math.min(at, ...open.values());
54
+ return [...messages.slice(0, at), reminder, ...messages.slice(at)];
55
+ }
56
+ export const createPlanExtension = () => pi => {
57
+ pi.registerTool({
58
+ name: "plan", label: "Plan", description: "Create or update a complete, versioned implementation plan on this session branch. Every call requires title, status, steps, currentStepId, completedStepIds and completionCriteria, even when completing or only changing status. Updates also require the latest planId and expectedRevision. This is full replacement, not a partial patch. Explicit completed status completes all submitted steps and clears currentStepId; cancelled/failed only clear currentStepId. Ended plans require create.",
59
+ exposure: "model-only", executionMode: "sequential", defaultActive: false,
60
+ annotations: { readOnlyHint: true, openWorldHint: false },
61
+ promptSnippet: "Record and update a multi-step implementation plan.",
62
+ promptGuidelines: ["For multi-step implementation, create or update a relevant plan before execution and update the current step before starting it. For already authorized work, proceed without requesting separate plan approval. Submit the full state each time. Wait for create to return its identity before updating. Follow user and skill instructions, including waiting; a plan does not grant authorization. Ordinary questions need no plan. When work is merged into another step or becomes unnecessary, update or remove the superseded step and explain the change instead of leaving a stale pending item. Never mark unfinished work completed. Mark completion explicitly; never infer it from a stopped run."],
63
+ parameters: planParameters,
64
+ async execute(_id, args, _signal, _update, ctx) {
65
+ const snapshot = transition(args, latestPlan(ctx.sessionManager.getBranch())?.snapshot);
66
+ return { content: [{ type: "text", text: `Plan ${snapshot.planId}, revision ${snapshot.revision}: ${snapshot.status}.` }], details: { planSnapshot: snapshot } };
67
+ },
68
+ });
69
+ pi.on("context", (event, ctx) => {
70
+ if (!pi.getActiveTools().includes("plan") || !["builtin:pi-harness-plan", "<inline:pi-harness-plan>"].includes(pi.getAllTools().find(tool => tool.name === "plan")?.sourceInfo.path ?? ""))
71
+ return;
72
+ return { messages: restorePlanContext(event.messages, ctx.sessionManager.getBranch()) };
73
+ });
74
+ };
@@ -0,0 +1,90 @@
1
+ import { isToolTextContinuation } from "../tool-text.js";
2
+ import { latestPlan } from "./state.js";
3
+ const histories = new WeakMap();
4
+ export function taskHistoryFromSession(manager, serialize) {
5
+ const leaf = manager.getLeafId();
6
+ const previous = histories.get(manager);
7
+ if (previous?.leaf === leaf)
8
+ return previous.messages;
9
+ const branch = manager.getBranch();
10
+ const messages = latestPlan(branch) ? branch.flatMap(entry => {
11
+ if (entry.type !== "message")
12
+ return [];
13
+ const message = serialize(entry.message, entry.id);
14
+ return message ? [{ ...message, entryId: entry.id }] : [];
15
+ }) : undefined;
16
+ histories.set(manager, { leaf, messages });
17
+ return messages;
18
+ }
19
+ /** Replay assistant call order, pairing results before the following call. */
20
+ export function planFromTranscript(messages) {
21
+ const results = new Map(messages.filter(message => message.role === "toolResult" && message.toolCallId).map(message => [message.toolCallId, message]));
22
+ let latest;
23
+ let confirmed = false;
24
+ let changes = [];
25
+ let changeSummary;
26
+ const tools = new Map();
27
+ const removed = new Map();
28
+ const times = new Map();
29
+ for (const [index, message] of messages.entries()) {
30
+ if (message.role === "user") {
31
+ if (!isToolTextContinuation(messages, index))
32
+ confirmed = false;
33
+ continue;
34
+ }
35
+ if (message.role !== "assistant")
36
+ continue;
37
+ for (const part of message.content) {
38
+ if (part.type !== "toolCall")
39
+ continue;
40
+ const call = part;
41
+ if (call.name !== "plan") {
42
+ if (confirmed && latest?.status === "active" && latest.currentStepId)
43
+ tools.set(latest.currentStepId, [...(tools.get(latest.currentStepId) ?? []), call.id]);
44
+ continue;
45
+ }
46
+ const result = results.get(call.id);
47
+ const next = !result?.isError ? result?.planSnapshot : undefined;
48
+ if (!next)
49
+ continue;
50
+ if (next.planId !== latest?.planId) {
51
+ tools.clear();
52
+ removed.clear();
53
+ times.clear();
54
+ changes = [];
55
+ changeSummary = undefined;
56
+ }
57
+ else
58
+ for (const step of latest.steps)
59
+ if (!next.steps.some(item => item.id === step.id))
60
+ removed.set(step.id, step);
61
+ for (const step of next.steps) {
62
+ removed.delete(step.id);
63
+ const time = times.get(step.id) ?? {};
64
+ if (next.currentStepId === step.id && time.startedAt === undefined)
65
+ time.startedAt = result?.timestamp;
66
+ if (next.completedStepIds.includes(step.id) && time.endedAt === undefined)
67
+ time.endedAt = result?.timestamp;
68
+ times.set(step.id, time);
69
+ }
70
+ const structuralChanges = next.changes.flatMap(change => ["added", "removed", "updated"].includes(change.kind) && "title" in change ? [{ kind: change.kind, title: change.title, position: change.position }] : []);
71
+ if (structuralChanges.length)
72
+ changes = structuralChanges;
73
+ if (next.changeSummary)
74
+ changeSummary = next.changeSummary;
75
+ latest = next;
76
+ confirmed = true;
77
+ }
78
+ }
79
+ if (!latest)
80
+ return { known: false };
81
+ if (!confirmed && latest.status !== "active")
82
+ return { known: true };
83
+ return { known: true, plan: {
84
+ source: "plan", origin: latest.planId, revision: latest.revision, status: latest.status, awaitingConfirmation: !confirmed,
85
+ title: latest.title, completionCriteria: latest.completionCriteria, changeSummary,
86
+ added: latest.changes.filter(change => change.kind === "added").length, removed: removed.size,
87
+ changes,
88
+ items: [...latest.steps.map(step => ({ ...step, status: latest.completedStepIds.includes(step.id) ? "done" : confirmed && step.id === latest.currentStepId ? "running" : "pending", toolCallIds: tools.get(step.id) ?? [], ...times.get(step.id) })), ...[...removed.values()].map(step => ({ ...step, status: "removed", toolCallIds: tools.get(step.id) ?? [], ...times.get(step.id) }))],
89
+ } };
90
+ }
@@ -0,0 +1,41 @@
1
+ import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join, resolve } from "node:path";
4
+ /** One service instance preference. Sessions only hold their effective loadout. */
5
+ export class PlanSettings {
6
+ path;
7
+ enabled = true;
8
+ constructor(dataDir = resolve(process.env.PI_WEB_DATA_DIR ?? join(homedir(), ".pi-web"))) {
9
+ this.path = join(dataDir, "plan-settings.json");
10
+ try {
11
+ const saved = JSON.parse(readFileSync(this.path, "utf8"));
12
+ if (typeof saved.enabled === "boolean")
13
+ this.enabled = saved.enabled;
14
+ }
15
+ catch { /* Missing or invalid preference: default on. */ }
16
+ }
17
+ set(enabled) {
18
+ mkdirSync(dirname(this.path), { recursive: true });
19
+ const temporary = `${this.path}.${process.pid}.tmp`;
20
+ writeFileSync(temporary, JSON.stringify({ enabled }) + "\n", { mode: 0o600 });
21
+ renameSync(temporary, this.path);
22
+ this.enabled = enabled;
23
+ }
24
+ state(session) {
25
+ const tool = session.getAllTools().find(tool => tool.name === "plan");
26
+ const available = !!tool && ["builtin:pi-harness-plan", "<inline:pi-harness-plan>"].includes(tool.sourceInfo.path);
27
+ const effective = available && session.getActiveToolNames().includes("plan");
28
+ return { enabled: this.enabled, available, effective, pending: available && effective !== this.enabled, ...(!available ? { reason: tool ? "conflict" : "missing" } : {}) };
29
+ }
30
+ coordinate(session, writable) {
31
+ const state = this.state(session);
32
+ if (!state.available || !state.pending || !session.isIdle || !writable)
33
+ return;
34
+ const active = session.getActiveToolNames().filter(name => name !== "plan");
35
+ if (this.enabled)
36
+ active.push("plan");
37
+ session.setActiveToolsByName(active);
38
+ }
39
+ }
40
+ let shared;
41
+ export const planSettings = () => shared ??= new PlanSettings();