@rubytech/create-maxy-code 0.1.40 → 0.1.43

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 (172) hide show
  1. package/package.json +2 -2
  2. package/payload/platform/lib/mcp-stderr-tee/package.json +7 -0
  3. package/payload/platform/plugins/admin/hooks/turn-completed-graph-write.sh +142 -52
  4. package/payload/platform/plugins/docs/references/deployment.md +6 -0
  5. package/payload/platform/plugins/docs/references/platform.md +18 -4
  6. package/payload/platform/plugins/docs/references/plugins-guide.md +2 -0
  7. package/payload/platform/scripts/check-no-esm-require.mjs +132 -0
  8. package/payload/platform/scripts/smoke-boot-services.sh +175 -0
  9. package/payload/platform/services/claude-session-manager/dist/config.d.ts +17 -0
  10. package/payload/platform/services/claude-session-manager/dist/config.d.ts.map +1 -1
  11. package/payload/platform/services/claude-session-manager/dist/config.js +31 -0
  12. package/payload/platform/services/claude-session-manager/dist/config.js.map +1 -1
  13. package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts +63 -0
  14. package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts.map +1 -0
  15. package/payload/platform/services/claude-session-manager/dist/fs-watcher.js +514 -0
  16. package/payload/platform/services/claude-session-manager/dist/fs-watcher.js.map +1 -0
  17. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +3 -1
  18. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  19. package/payload/platform/services/claude-session-manager/dist/http-server.js +364 -204
  20. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  21. package/payload/platform/services/claude-session-manager/dist/index.js +38 -11
  22. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  23. package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts +2 -0
  24. package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts.map +1 -1
  25. package/payload/platform/services/claude-session-manager/dist/jsonl-path.js +11 -6
  26. package/payload/platform/services/claude-session-manager/dist/jsonl-path.js.map +1 -1
  27. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +37 -44
  28. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  29. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +136 -159
  30. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  31. package/payload/platform/services/claude-session-manager/dist/reaper.d.ts +24 -0
  32. package/payload/platform/services/claude-session-manager/dist/reaper.d.ts.map +1 -0
  33. package/payload/platform/services/claude-session-manager/dist/reaper.js +114 -0
  34. package/payload/platform/services/claude-session-manager/dist/reaper.js.map +1 -0
  35. package/payload/platform/services/claude-session-manager/dist/session-store.d.ts +16 -41
  36. package/payload/platform/services/claude-session-manager/dist/session-store.d.ts.map +1 -1
  37. package/payload/platform/services/claude-session-manager/dist/session-store.js +29 -68
  38. package/payload/platform/services/claude-session-manager/dist/session-store.js.map +1 -1
  39. package/payload/premium-plugins/real-agent/lib/mcp-stderr-tee/dist/index.d.ts +51 -0
  40. package/payload/premium-plugins/real-agent/lib/mcp-stderr-tee/dist/index.d.ts.map +1 -0
  41. package/payload/premium-plugins/real-agent/lib/mcp-stderr-tee/dist/index.js +196 -0
  42. package/payload/premium-plugins/real-agent/lib/mcp-stderr-tee/dist/index.js.map +1 -0
  43. package/payload/premium-plugins/real-agent/lib/mcp-stderr-tee/package.json +7 -0
  44. package/payload/server/public/assets/{admin-DO9W2D9g.js → admin-CzJDs5FQ.js} +24 -24
  45. package/payload/server/public/assets/{arc-DMDAZHAN.js → arc-aUiRP9AS.js} +1 -1
  46. package/payload/server/public/assets/architecture-YZFGNWBL--v-pJPNp.js +1 -0
  47. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-BbLtk_k6.js → architectureDiagram-Q4EWVU46-DJvcnhtT.js} +1 -1
  48. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-B9IgcgeJ.js → blockDiagram-DXYQGD6D-_vt7n9ql.js} +1 -1
  49. package/payload/server/public/assets/{brand-DQrOw38P.css → brand-DNSV7-vn.css} +1 -1
  50. package/payload/server/public/assets/{brand-Bky-hMVw.js → brand-FZ59T8pI.js} +1 -1
  51. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-BrvPS09f.js → c4Diagram-AHTNJAMY-CdEo9QqN.js} +1 -1
  52. package/payload/server/public/assets/channel-DzmCBQ_9.js +1 -0
  53. package/payload/server/public/assets/{chunk-2KRD3SAO-Di4bO8ir.js → chunk-2KRD3SAO-BK3470lx.js} +1 -1
  54. package/payload/server/public/assets/chunk-336JU56O-JmuLOuir.js +2 -0
  55. package/payload/server/public/assets/chunk-426QAEUC-10f8pXpz.js +1 -0
  56. package/payload/server/public/assets/{chunk-4BX2VUAB-DyEhFk-Z.js → chunk-4BX2VUAB-BOvVdJLf.js} +1 -1
  57. package/payload/server/public/assets/{chunk-4TB4RGXK-D7_xc8xf.js → chunk-4TB4RGXK-DajMd0ug.js} +1 -1
  58. package/payload/server/public/assets/{chunk-55IACEB6-BRJOZLpU.js → chunk-55IACEB6-BwZyF7vR.js} +1 -1
  59. package/payload/server/public/assets/{chunk-5FUZZQ4R-CdCSpb8j.js → chunk-5FUZZQ4R-B4A-XUHI.js} +1 -1
  60. package/payload/server/public/assets/{chunk-5PVQY5BW-BVgnD9QR.js → chunk-5PVQY5BW-BNM3rT14.js} +1 -1
  61. package/payload/server/public/assets/{chunk-67CJDMHE-BG6-9r6c.js → chunk-67CJDMHE-D5bhMrtY.js} +1 -1
  62. package/payload/server/public/assets/{chunk-7N4EOEYR-BvMbitX7.js → chunk-7N4EOEYR-Si7Lgrwc.js} +1 -1
  63. package/payload/server/public/assets/{chunk-AA7GKIK3-CE8mGBD5.js → chunk-AA7GKIK3-DMuHtDqO.js} +1 -1
  64. package/payload/server/public/assets/{chunk-BSJP7CBP-DP7LTBll.js → chunk-BSJP7CBP-L79XKVcb.js} +1 -1
  65. package/payload/server/public/assets/{chunk-CIAEETIT-VfnIdN-h.js → chunk-CIAEETIT-C0O7Upmg.js} +1 -1
  66. package/payload/server/public/assets/{chunk-EDXVE4YY-CYrT1Tom.js → chunk-EDXVE4YY-atMwO81K.js} +1 -1
  67. package/payload/server/public/assets/{chunk-ENJZ2VHE-B70d0jN7.js → chunk-ENJZ2VHE-BUbZv2Nz.js} +1 -1
  68. package/payload/server/public/assets/{chunk-FMBD7UC4-w-yBZsN2.js → chunk-FMBD7UC4-C_E43NFJ.js} +1 -1
  69. package/payload/server/public/assets/{chunk-FOC6F5B3-f9cFywhd.js → chunk-FOC6F5B3-D9lWWHAu.js} +1 -1
  70. package/payload/server/public/assets/{chunk-ICPOFSXX-BcLouRbd.js → chunk-ICPOFSXX-BMF-eWHb.js} +2 -2
  71. package/payload/server/public/assets/{chunk-K5T4RW27-B_ZUrFUq.js → chunk-K5T4RW27-DuhsNH4c.js} +1 -1
  72. package/payload/server/public/assets/{chunk-KGLVRYIC-CcWTvRlI.js → chunk-KGLVRYIC-B4-A1Abi.js} +1 -1
  73. package/payload/server/public/assets/{chunk-LIHQZDEY-D-5-peQw.js → chunk-LIHQZDEY-BxqgHRgT.js} +1 -1
  74. package/payload/server/public/assets/{chunk-ORNJ4GCN-B4Z5L25I.js → chunk-ORNJ4GCN-DEYQ5WaJ.js} +1 -1
  75. package/payload/server/public/assets/{chunk-OYMX7WX6-C2ja2fPj.js → chunk-OYMX7WX6-BEMDmPTI.js} +1 -1
  76. package/payload/server/public/assets/chunk-QZHKN3VN-Bd-GrQM6.js +1 -0
  77. package/payload/server/public/assets/{chunk-U2HBQHQK-CCjoTpL6.js → chunk-U2HBQHQK-DOM7Y4f1.js} +1 -1
  78. package/payload/server/public/assets/{chunk-X2U36JSP-QIVkp-XR.js → chunk-X2U36JSP-C_uWyW6Y.js} +1 -1
  79. package/payload/server/public/assets/{chunk-XPW4576I-m1Y_r88I.js → chunk-XPW4576I-CBfZXZDB.js} +1 -1
  80. package/payload/server/public/assets/{chunk-YZCP3GAM-qD9rebhx.js → chunk-YZCP3GAM-DQbgl47k.js} +1 -1
  81. package/payload/server/public/assets/{chunk-ZZ45TVLE-C0J_f05H.js → chunk-ZZ45TVLE-C44DEAgq.js} +1 -1
  82. package/payload/server/public/assets/classDiagram-6PBFFD2Q-DVoOmecn.js +1 -0
  83. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-B0r3hpf9.js +1 -0
  84. package/payload/server/public/assets/clone-Dde30M8J.js +1 -0
  85. package/payload/server/public/assets/{cose-bilkent-S5V4N54A-BhtgY3T7.js → cose-bilkent-S5V4N54A-Br2gjtEO.js} +1 -1
  86. package/payload/server/public/assets/{dagre-KV5264BT-C8lVFO5U.js → dagre-KV5264BT-CE3meBlZ.js} +1 -1
  87. package/payload/server/public/assets/{dagre-B3cA7jId.js → dagre-lNEvWLDX.js} +1 -1
  88. package/payload/server/public/assets/data-DW6QAnMD.js +1 -0
  89. package/payload/server/public/assets/{device-url-actions-qu2-XiHc.js → device-url-actions-a6Bd-MRk.js} +1 -1
  90. package/payload/server/public/assets/{diagram-5BDNPKRD-BYkcTelK.js → diagram-5BDNPKRD-BqR72jWu.js} +1 -1
  91. package/payload/server/public/assets/{diagram-G4DWMVQ6-B6f_WR4j.js → diagram-G4DWMVQ6-DOjamNAP.js} +1 -1
  92. package/payload/server/public/assets/{diagram-MMDJMWI5-ALaKcN6w.js → diagram-MMDJMWI5-DAUB-aPu.js} +1 -1
  93. package/payload/server/public/assets/{diagram-TYMM5635-CkVg3crF.js → diagram-TYMM5635-DpToW00F.js} +1 -1
  94. package/payload/server/public/assets/{erDiagram-SMLLAGMA-DszlTGIQ.js → erDiagram-SMLLAGMA-C-p2ze2T.js} +1 -1
  95. package/payload/server/public/assets/{flatten-BsWEYbBB.js → flatten-Bo6YRmWl.js} +1 -1
  96. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-C9-FqSlk.js → flowDiagram-DWJPFMVM-B9SS6a8F.js} +1 -1
  97. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-faJU-5Zy.js → ganttDiagram-T4ZO3ILL-DnXZX0Gp.js} +1 -1
  98. package/payload/server/public/assets/gitGraph-7Q5UKJZL-CI0s_tqn.js +1 -0
  99. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-Cck8_tiG.js → gitGraphDiagram-UUTBAWPF-CtQ0FzPg.js} +1 -1
  100. package/payload/server/public/assets/graph-D13IJB3w.js +1 -0
  101. package/payload/server/public/assets/graph-labels-CI9WmEiE.js +1 -0
  102. package/payload/server/public/assets/{graphlib-CYRxeBXj.js → graphlib-nUaDKzkp.js} +1 -1
  103. package/payload/server/public/assets/info-OMHHGYJF-g3gYW7Qm.js +1 -0
  104. package/payload/server/public/assets/infoDiagram-42DDH7IO-c2wWbEaX.js +2 -0
  105. package/payload/server/public/assets/{isEmpty-BWl67LAZ.js → isEmpty-D6Kr-M1M.js} +1 -1
  106. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-nSIaVXdw.js → ishikawaDiagram-UXIWVN3A-BBOgnjrU.js} +1 -1
  107. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-DuKq7d3g.js → journeyDiagram-VCZTEJTY-DzIkOZor.js} +1 -1
  108. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-Bt5xqHfY.js → kanban-definition-6JOO6SKY-NoAdbB_E.js} +1 -1
  109. package/payload/server/public/assets/{line-BgV8qCP_.js → line-BRK1p00q.js} +1 -1
  110. package/payload/server/public/assets/{linear-0O14Y6uf.js → linear-DOh_6k2k.js} +1 -1
  111. package/payload/server/public/assets/{mermaid-parser.core-DQzl__pY.js → mermaid-parser.core-Bm0OvYTL.js} +2 -2
  112. package/payload/server/public/assets/{mermaid.core-BAPEOjwh.js → mermaid.core-CTznq93v.js} +3 -3
  113. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-CmMBrI79.js → mindmap-definition-QFDTVHPH-BOQ1enWi.js} +1 -1
  114. package/payload/server/public/assets/{ordinal-krseTxxN.js → ordinal-BDi6f4xk.js} +1 -1
  115. package/payload/server/public/assets/packet-4T2RLAQJ-CT0TB9HI.js +1 -0
  116. package/payload/server/public/assets/{page-BoDdtGsH.js → page-BLNJpxTT.js} +2 -2
  117. package/payload/server/public/assets/{page-D_NCNi5j.js → page-Dn3gASQM.js} +1 -1
  118. package/payload/server/public/assets/pie-ZZUOXDRM-CXLe7TFF.js +1 -0
  119. package/payload/server/public/assets/{pieDiagram-DEJITSTG-BhsR9k6j.js → pieDiagram-DEJITSTG-2S-XHkal.js} +1 -1
  120. package/payload/server/public/assets/{public-DCcGKIMW.js → public-D0z7BALL.js} +5 -5
  121. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-DSKV6d_A.js → quadrantDiagram-34T5L4WZ-CncZqXXr.js} +1 -1
  122. package/payload/server/public/assets/radar-PYXPWWZC-DnPLBl-D.js +1 -0
  123. package/payload/server/public/assets/{reduce-tk-xY6Fv.js → reduce-CGi9ik8i.js} +1 -1
  124. package/payload/server/public/assets/{requirementDiagram-MS252O5E-BccM48AI.js → requirementDiagram-MS252O5E-CPeWs9fs.js} +1 -1
  125. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-C0pxiQfn.js → sankeyDiagram-XADWPNL6-A4_669ey.js} +1 -1
  126. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-BVsUIJsW.js → sequenceDiagram-FGHM5R23-DD-qEurY.js} +1 -1
  127. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-lqYQZ-F1.js → stateDiagram-FHFEXIEX-CdO6Mw_T.js} +1 -1
  128. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-D28bJZnf.js +1 -0
  129. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-D-LxYOHq.js → timeline-definition-GMOUNBTQ-JH65fN1V.js} +1 -1
  130. package/payload/server/public/assets/treeView-SZITEDCU-C3cb7Xwe.js +1 -0
  131. package/payload/server/public/assets/treemap-W4RFUUIX-Dc7G3Bgm.js +1 -0
  132. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-BNHzvNba.js → vennDiagram-DHZGUBPP-CJLoNrBL.js} +1 -1
  133. package/payload/server/public/assets/wardley-RL74JXVD-DtgibWAt.js +1 -0
  134. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-BfXR9l5s.js → wardleyDiagram-NUSXRM2D-BgM9eEf7.js} +1 -1
  135. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-CL7mVEbT.js → xychartDiagram-5P7HB3ND-DCmesmc9.js} +1 -1
  136. package/payload/server/public/data.html +5 -5
  137. package/payload/server/public/graph.html +5 -6
  138. package/payload/server/public/index.html +7 -8
  139. package/payload/server/public/public.html +4 -5
  140. package/payload/server/server.js +94 -4
  141. package/payload/server/public/assets/Checkbox-6lLDKzMP.js +0 -1
  142. package/payload/server/public/assets/architecture-YZFGNWBL-COhEvUpo.js +0 -1
  143. package/payload/server/public/assets/channel-Bmc1qlCD.js +0 -1
  144. package/payload/server/public/assets/chunk-336JU56O-DXOr0ZnC.js +0 -2
  145. package/payload/server/public/assets/chunk-426QAEUC-BFW4zwrV.js +0 -1
  146. package/payload/server/public/assets/chunk-QZHKN3VN-CWh_0JsP.js +0 -1
  147. package/payload/server/public/assets/classDiagram-6PBFFD2Q-RVZorqSk.js +0 -1
  148. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BEZem5bl.js +0 -1
  149. package/payload/server/public/assets/clone-B6eXIhiR.js +0 -1
  150. package/payload/server/public/assets/data-BaSmBQ7U.js +0 -1
  151. package/payload/server/public/assets/gitGraph-7Q5UKJZL-DeTNsAO0.js +0 -1
  152. package/payload/server/public/assets/graph-labels-COhLbJD0.js +0 -1
  153. package/payload/server/public/assets/graph-qg6fkRyC.js +0 -1
  154. package/payload/server/public/assets/info-OMHHGYJF-DJJ9GlS6.js +0 -1
  155. package/payload/server/public/assets/infoDiagram-42DDH7IO-CLjEjhJX.js +0 -2
  156. package/payload/server/public/assets/packet-4T2RLAQJ-CGbvGkvF.js +0 -1
  157. package/payload/server/public/assets/pie-ZZUOXDRM-BZy8rjFn.js +0 -1
  158. package/payload/server/public/assets/radar-PYXPWWZC-DcfWIVXr.js +0 -1
  159. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-D2eHThhC.js +0 -1
  160. package/payload/server/public/assets/treeView-SZITEDCU-3WugwVdj.js +0 -1
  161. package/payload/server/public/assets/treemap-W4RFUUIX-Cf5mDLlu.js +0 -1
  162. package/payload/server/public/assets/wardley-RL74JXVD-Bv4md4b3.js +0 -1
  163. /package/payload/server/public/assets/{_baseFor-BHtDrjIo.js → _baseFor-Cam2PbSt.js} +0 -0
  164. /package/payload/server/public/assets/{array-DetWRiSa.js → array-DYRGGQae.js} +0 -0
  165. /package/payload/server/public/assets/{cytoscape.esm-C9yNhe1u.js → cytoscape.esm-nWsJMTNI.js} +0 -0
  166. /package/payload/server/public/assets/{defaultLocale-_WRwicXn.js → defaultLocale-Du1XY3Dp.js} +0 -0
  167. /package/payload/server/public/assets/{dist-Bd4S37oi.js → dist-BzAsli7o.js} +0 -0
  168. /package/payload/server/public/assets/{init-sTEcj9YX.js → init-B5BXBRcm.js} +0 -0
  169. /package/payload/server/public/assets/{katex-s61Rgv6l.js → katex-HOUACuRw.js} +0 -0
  170. /package/payload/server/public/assets/{path-B0Ik7Tu9.js → path-CNO468J-.js} +0 -0
  171. /package/payload/server/public/assets/{rough.esm-DKRO8IF-.js → rough.esm-DRO6hWPh.js} +0 -0
  172. /package/payload/server/public/assets/{src-B6XdH6xq.js → src-CWiyyVfn.js} +0 -0
@@ -1,63 +1,38 @@
1
1
  import type { PtyHandle, Role, Channel } from './types.js';
2
2
  export interface StoredSession {
3
- /** URL bridge suffix captured from the /remote-control URL. Stable for
4
- * the row's entire alive lifetime — never null, never replaced. Surfaced
5
- * as the row's `sessionId` on every wire response so the UI mounts its
6
- * pane against this identifier from t=0 (URL capture). */
3
+ /** Intrinsic Claude session id (UUID). Stable for the row's lifetime. */
4
+ sessionId: string;
5
+ /** "session_<suffix>" form from the PID file. Stable, never null after
6
+ * /spawn returns (the spawn promise resolves on PID-file event). */
7
+ bridgeSessionId: string;
8
+ /** Suffix-only convenience field (no "session_" prefix). */
7
9
  bridgeSuffix: string;
8
- /** Intrinsic claude session id — the JSONL basename Claude Code wrote.
9
- * Null between URL capture and the first turn the operator types (claude
10
- * defers JSONL write to the first turn). Filled when the jsonl-observer
11
- * fires; thereafter equals the intrinsic id forever. */
12
- claudeSessionId: string | null;
13
10
  pid: number;
14
- url: string;
11
+ /** URL from the `/remote-control` banner. Null until url-capture fires;
12
+ * may stay null forever if the banner never lands. The UI tolerates
13
+ * null. */
14
+ url: string | null;
15
15
  startedAt: string;
16
16
  senderId: string;
17
17
  role: Role;
18
18
  channel: Channel;
19
- /** Path of the JSONL Claude Code wrote, observed from disk by the
20
- * jsonl-observer. Null until promotion fires (mirrors claudeSessionId). */
19
+ /** Set on the JSONL CREATE event the http-server subscribes to. Null
20
+ * until the operator types the first turn. */
21
21
  jsonlPath: string | null;
22
22
  pty: PtyHandle;
23
23
  exited: boolean;
24
- /** Background spawns (per-turn database-operator recorder, future
25
- * out-of-band specialists) set `hidden: true` at /spawn time. /list
26
- * filters them out by default so they never appear in the sidebar,
27
- * the operator URL, or the artefact pane; /list?includeHidden=1
28
- * surfaces them for diagnostics. Defaults to false. */
29
24
  hidden: boolean;
30
- /** Specialist agent name passed as `--agent <name>` to claude when the
31
- * spawn should land inside a specific specialist (database-operator,
32
- * etc.). Null on user-facing spawns. */
33
25
  specialist: string | null;
34
26
  }
35
27
  export declare class SessionStore {
36
- /** Keyed by the row's stable surface id (bridge suffix). Lookup-by-bridge
37
- * is the alive-row contract. */
38
- private readonly byBridge;
39
- /** Filled at promotion time so /list, /meta, /log routes resolve the
40
- * intrinsic id to the same StoredSession the bridge id resolves to. */
41
- private readonly byClaude;
42
- /** Register a freshly-spawned session (claudeSessionId null, jsonlPath
43
- * null). The row becomes visible to /list immediately. */
44
- addPending(s: StoredSession): void;
45
- /** Resolve by either the bridge suffix or the intrinsic claudeSessionId.
46
- * Returns the same StoredSession in either case once promotion has
47
- * fired. */
48
- get(id: string): StoredSession | undefined;
49
- /** Remove by either id. Drops both index entries. */
50
- remove(id: string): StoredSession | undefined;
28
+ private readonly bySessionId;
29
+ add(s: StoredSession): void;
30
+ get(sessionId: string): StoredSession | undefined;
31
+ remove(sessionId: string): StoredSession | undefined;
51
32
  listBySender(senderId: string, opts?: {
52
33
  includeHidden?: boolean;
53
34
  }): StoredSession[];
54
35
  all(): StoredSession[];
55
36
  size(): number;
56
- /** Bind a pending row to its newly-observed `claudeSessionId` + JSONL
57
- * path. Idempotent: a second call on an already-promoted row updates
58
- * nothing. Returns the StoredSession on success, null when no row
59
- * exists for the bridge suffix (operator already deleted it, or the
60
- * PTY exited before observation fired). */
61
- promote(bridgeSuffix: string, claudeSessionId: string, jsonlPath: string): StoredSession | null;
62
37
  }
63
38
  //# sourceMappingURL=session-store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../src/session-store.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAE1D,MAAM,WAAW,aAAa;IAC5B;;;+DAG2D;IAC3D,YAAY,EAAE,MAAM,CAAA;IACpB;;;6DAGyD;IACzD,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,IAAI,CAAA;IACV,OAAO,EAAE,OAAO,CAAA;IAChB;gFAC4E;IAC5E,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,GAAG,EAAE,SAAS,CAAA;IACd,MAAM,EAAE,OAAO,CAAA;IACf;;;;4DAIwD;IACxD,MAAM,EAAE,OAAO,CAAA;IACf;;6CAEyC;IACzC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAED,qBAAa,YAAY;IACvB;qCACiC;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmC;IAC5D;4EACwE;IACxE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmC;IAE5D;+DAC2D;IAC3D,UAAU,CAAC,CAAC,EAAE,aAAa,GAAG,IAAI;IAIlC;;iBAEa;IACb,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAI1C,qDAAqD;IACrD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAQ7C,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,aAAa,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,aAAa,EAAE;IAWvF,GAAG,IAAI,aAAa,EAAE;IAItB,IAAI,IAAI,MAAM;IAId;;;;gDAI4C;IAC5C,OAAO,CACL,YAAY,EAAE,MAAM,EACpB,eAAe,EAAE,MAAM,EACvB,SAAS,EAAE,MAAM,GAChB,aAAa,GAAG,IAAI;CASxB"}
1
+ {"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../src/session-store.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAE1D,MAAM,WAAW,aAAa;IAC5B,yEAAyE;IACzE,SAAS,EAAE,MAAM,CAAA;IACjB;yEACqE;IACrE,eAAe,EAAE,MAAM,CAAA;IACvB,4DAA4D;IAC5D,YAAY,EAAE,MAAM,CAAA;IACpB,GAAG,EAAE,MAAM,CAAA;IACX;;gBAEY;IACZ,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,IAAI,CAAA;IACV,OAAO,EAAE,OAAO,CAAA;IAChB;mDAC+C;IAC/C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,GAAG,EAAE,SAAS,CAAA;IACd,MAAM,EAAE,OAAO,CAAA;IACf,MAAM,EAAE,OAAO,CAAA;IACf,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAmC;IAE/D,GAAG,CAAC,CAAC,EAAE,aAAa,GAAG,IAAI;IAI3B,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAIjD,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAOpD,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,aAAa,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,aAAa,EAAE;IAWvF,GAAG,IAAI,aAAa,EAAE;IAItB,IAAI,IAAI,MAAM;CAGf"}
@@ -1,64 +1,41 @@
1
- // In-memory session store. One identifier per Claude process, but the
2
- // spawn-time window between PTY creation and Claude Code writing its first
3
- // JSONL line is now unbounded: claude does not write the JSONL until the
4
- // operator types the first turn (verified Pi 2.1.143 and Mac 2.1.128).
1
+ // In-memory PTY-handle store. The canonical row index now lives in the
2
+ // fs-watcher (sessions/<pid>.json + projects/<slug>/<sid>.jsonl). This
3
+ // store only tracks the live PTY handles the manager itself spawned
4
+ // the watcher cannot expose those (they aren't on disk). Lookups by
5
+ // alternative identifiers (bridgeSuffix, pid, bridgeSessionId) go through
6
+ // the watcher first to resolve to the canonical sessionId; the store is
7
+ // then keyed solely on sessionId.
5
8
  //
6
- // Lifecycle spans the full PTY lifetime:
7
- //
8
- // pending — created when /spawn captures the /remote-control URL.
9
- // The row is keyed by the URL `bridgeSuffix` (the substring
10
- // after `session_` in the URL). `claudeSessionId` is null;
11
- // `jsonlPath` is null. The /spawn response returns at this
12
- // point, so the operator sees a usable row inside ~2 s even
13
- // when they never type.
14
- //
15
- // promoted — fires later (seconds or minutes, gated on operator typing)
16
- // when `jsonl-observer` matches a JSONL on disk whose head
17
- // bytes carry the bridge suffix. `claudeSessionId` is filled
18
- // with the intrinsic JSONL basename, `jsonlPath` is set, and
19
- // the same StoredSession is also registered under the
20
- // intrinsic id so GET routes resolve either id to the same
21
- // row.
22
- //
23
- // ended — operator deletes the row, the PTY exits, or the manager
24
- // restarts. Promoted rows stay enumerable from disk via the
25
- // JSONL transcript. Pending rows that never promoted have no
26
- // JSONL and disappear cleanly on delete.
27
- //
28
- // The store mints zero ids of its own. The bridge suffix is claude's, the
29
- // intrinsic id is claude's. The manager only observes them.
9
+ // Lifecycle:
10
+ // - pty-spawner awaits the PID-file CREATE event from the watcher
11
+ // before returning a /spawn response. The store row is registered at
12
+ // that point with sessionId known and bridgeSessionId carried from
13
+ // the PID file. There is no "pre-promotion" pending state — by the
14
+ // time /spawn returns, sessionId is final.
15
+ // - URL capture continues in parallel; the row's `url` field is
16
+ // mutated in place when the URL banner fires (may arrive before or
17
+ // after the PID-file event; both orderings are safe).
18
+ // - JSONL appears on first operator turn; the watcher fills the row's
19
+ // jsonlPath via direct mutation from the http-server's subscriber.
30
20
  export class SessionStore {
31
- /** Keyed by the row's stable surface id (bridge suffix). Lookup-by-bridge
32
- * is the alive-row contract. */
33
- byBridge = new Map();
34
- /** Filled at promotion time so /list, /meta, /log routes resolve the
35
- * intrinsic id to the same StoredSession the bridge id resolves to. */
36
- byClaude = new Map();
37
- /** Register a freshly-spawned session (claudeSessionId null, jsonlPath
38
- * null). The row becomes visible to /list immediately. */
39
- addPending(s) {
40
- this.byBridge.set(s.bridgeSuffix, s);
21
+ bySessionId = new Map();
22
+ add(s) {
23
+ this.bySessionId.set(s.sessionId, s);
41
24
  }
42
- /** Resolve by either the bridge suffix or the intrinsic claudeSessionId.
43
- * Returns the same StoredSession in either case once promotion has
44
- * fired. */
45
- get(id) {
46
- return this.byBridge.get(id) ?? this.byClaude.get(id);
25
+ get(sessionId) {
26
+ return this.bySessionId.get(sessionId);
47
27
  }
48
- /** Remove by either id. Drops both index entries. */
49
- remove(id) {
50
- const s = this.get(id);
28
+ remove(sessionId) {
29
+ const s = this.bySessionId.get(sessionId);
51
30
  if (!s)
52
31
  return undefined;
53
- this.byBridge.delete(s.bridgeSuffix);
54
- if (s.claudeSessionId !== null)
55
- this.byClaude.delete(s.claudeSessionId);
32
+ this.bySessionId.delete(sessionId);
56
33
  return s;
57
34
  }
58
35
  listBySender(senderId, opts = {}) {
59
36
  const includeHidden = opts.includeHidden === true;
60
37
  const out = [];
61
- for (const s of this.byBridge.values()) {
38
+ for (const s of this.bySessionId.values()) {
62
39
  if (s.senderId !== senderId)
63
40
  continue;
64
41
  if (!includeHidden && s.hidden)
@@ -68,26 +45,10 @@ export class SessionStore {
68
45
  return out;
69
46
  }
70
47
  all() {
71
- return [...this.byBridge.values()];
48
+ return [...this.bySessionId.values()];
72
49
  }
73
50
  size() {
74
- return this.byBridge.size;
75
- }
76
- /** Bind a pending row to its newly-observed `claudeSessionId` + JSONL
77
- * path. Idempotent: a second call on an already-promoted row updates
78
- * nothing. Returns the StoredSession on success, null when no row
79
- * exists for the bridge suffix (operator already deleted it, or the
80
- * PTY exited before observation fired). */
81
- promote(bridgeSuffix, claudeSessionId, jsonlPath) {
82
- const s = this.byBridge.get(bridgeSuffix);
83
- if (!s)
84
- return null;
85
- if (s.claudeSessionId !== null)
86
- return s;
87
- s.claudeSessionId = claudeSessionId;
88
- s.jsonlPath = jsonlPath;
89
- this.byClaude.set(claudeSessionId, s);
90
- return s;
51
+ return this.bySessionId.size;
91
52
  }
92
53
  }
93
54
  //# sourceMappingURL=session-store.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-store.js","sourceRoot":"","sources":["../src/session-store.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,2EAA2E;AAC3E,yEAAyE;AACzE,uEAAuE;AACvE,EAAE;AACF,yCAAyC;AACzC,EAAE;AACF,sEAAsE;AACtE,0EAA0E;AAC1E,yEAAyE;AACzE,yEAAyE;AACzE,0EAA0E;AAC1E,sCAAsC;AACtC,EAAE;AACF,2EAA2E;AAC3E,yEAAyE;AACzE,2EAA2E;AAC3E,2EAA2E;AAC3E,oEAAoE;AACpE,yEAAyE;AACzE,qBAAqB;AACrB,EAAE;AACF,wEAAwE;AACxE,0EAA0E;AAC1E,2EAA2E;AAC3E,uDAAuD;AACvD,EAAE;AACF,0EAA0E;AAC1E,4DAA4D;AAsC5D,MAAM,OAAO,YAAY;IACvB;qCACiC;IAChB,QAAQ,GAAG,IAAI,GAAG,EAAyB,CAAA;IAC5D;4EACwE;IACvD,QAAQ,GAAG,IAAI,GAAG,EAAyB,CAAA;IAE5D;+DAC2D;IAC3D,UAAU,CAAC,CAAgB;QACzB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAAA;IACtC,CAAC;IAED;;iBAEa;IACb,GAAG,CAAC,EAAU;QACZ,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACvD,CAAC;IAED,qDAAqD;IACrD,MAAM,CAAC,EAAU;QACf,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACtB,IAAI,CAAC,CAAC;YAAE,OAAO,SAAS,CAAA;QACxB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAA;QACpC,IAAI,CAAC,CAAC,eAAe,KAAK,IAAI;YAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAA;QACvE,OAAO,CAAC,CAAA;IACV,CAAC;IAED,YAAY,CAAC,QAAgB,EAAE,OAAoC,EAAE;QACnE,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,KAAK,IAAI,CAAA;QACjD,MAAM,GAAG,GAAoB,EAAE,CAAA;QAC/B,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YACvC,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ;gBAAE,SAAQ;YACrC,IAAI,CAAC,aAAa,IAAI,CAAC,CAAC,MAAM;gBAAE,SAAQ;YACxC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACb,CAAC;QACD,OAAO,GAAG,CAAA;IACZ,CAAC;IAED,GAAG;QACD,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAA;IACpC,CAAC;IAED,IAAI;QACF,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAA;IAC3B,CAAC;IAED;;;;gDAI4C;IAC5C,OAAO,CACL,YAAoB,EACpB,eAAuB,EACvB,SAAiB;QAEjB,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;QACzC,IAAI,CAAC,CAAC;YAAE,OAAO,IAAI,CAAA;QACnB,IAAI,CAAC,CAAC,eAAe,KAAK,IAAI;YAAE,OAAO,CAAC,CAAA;QACxC,CAAC,CAAC,eAAe,GAAG,eAAe,CAAA;QACnC,CAAC,CAAC,SAAS,GAAG,SAAS,CAAA;QACvB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,eAAe,EAAE,CAAC,CAAC,CAAA;QACrC,OAAO,CAAC,CAAA;IACV,CAAC;CACF"}
1
+ {"version":3,"file":"session-store.js","sourceRoot":"","sources":["../src/session-store.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,uEAAuE;AACvE,sEAAsE;AACtE,oEAAoE;AACpE,0EAA0E;AAC1E,wEAAwE;AACxE,kCAAkC;AAClC,EAAE;AACF,aAAa;AACb,oEAAoE;AACpE,yEAAyE;AACzE,uEAAuE;AACvE,uEAAuE;AACvE,+CAA+C;AAC/C,kEAAkE;AAClE,uEAAuE;AACvE,0DAA0D;AAC1D,wEAAwE;AACxE,uEAAuE;AA8BvE,MAAM,OAAO,YAAY;IACN,WAAW,GAAG,IAAI,GAAG,EAAyB,CAAA;IAE/D,GAAG,CAAC,CAAgB;QAClB,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAA;IACtC,CAAC;IAED,GAAG,CAAC,SAAiB;QACnB,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;IACxC,CAAC;IAED,MAAM,CAAC,SAAiB;QACtB,MAAM,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QACzC,IAAI,CAAC,CAAC;YAAE,OAAO,SAAS,CAAA;QACxB,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;QAClC,OAAO,CAAC,CAAA;IACV,CAAC;IAED,YAAY,CAAC,QAAgB,EAAE,OAAoC,EAAE;QACnE,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,KAAK,IAAI,CAAA;QACjD,MAAM,GAAG,GAAoB,EAAE,CAAA;QAC/B,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1C,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ;gBAAE,SAAQ;YACrC,IAAI,CAAC,aAAa,IAAI,CAAC,CAAC,MAAM;gBAAE,SAAQ;YACxC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACb,CAAC;QACD,OAAO,GAAG,CAAA;IACZ,CAAC;IAED,GAAG;QACD,OAAO,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAA;IACvC,CAAC;IAED,IAAI;QACF,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAA;IAC9B,CAAC;CACF"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * MCP server stderr tee — dual-destination log capture.
3
+ *
4
+ * Claude Code spawns MCP servers as child processes and consumes their stderr
5
+ * internally. The platform cannot intercept at the spawn level. Each server
6
+ * must tee its own stderr so diagnostic output is retrievable.
7
+ *
8
+ * ┌─────────────────────────┐
9
+ * │ MCP server process │
10
+ * │ │
11
+ * │ console.error("[x] ..") │
12
+ * │ │ │
13
+ * │ ▼ │
14
+ * │ patched stderr.write() │
15
+ * │ │ │
16
+ * │ ├──► original stderr (consumed by Claude Code — opaque)
17
+ * │ │
18
+ * │ ├──► mcp-{name}-stderr-{YYYY-MM-DD}.log (raw chunks, per-plugin)
19
+ * │ │
20
+ * │ └──► $STREAM_LOG_PATH (per-line, prefixed)
21
+ * │ "[<iso>] [mcp:{name}] <line>"
22
+ * └─────────────────────────┘
23
+ *
24
+ * $STREAM_LOG_PATH is set by the spawner (`getMcpServers` in `claude-agent.ts`)
25
+ * to the per-conversation stream log file. The MCP server code itself knows
26
+ * nothing about conversations — it just trusts the spawner's path. This is
27
+ * the scope boundary introduced: the tee is attached per spawn,
28
+ * not per MCP-server process lifetime. Servers spawned for conversation A
29
+ * write to conversation A's file; servers for B write to B's file.
30
+ *
31
+ * Every decision is logged via `[mcp-tee-*]` markers on both the target file
32
+ * and the original stderr so an investigator can confirm from the stream log
33
+ * which tees were wired up, which were skipped, and why.
34
+ */
35
+ /**
36
+ * Patch process.stderr.write to tee to:
37
+ * 1. Per-server raw log (`mcp-<serverName>-stderr-<date>.log` under LOG_DIR).
38
+ * 2. The per-conversation stream log at `STREAM_LOG_PATH` — per-line, prefixed.
39
+ * 3. The original stderr (consumed by Claude Code) — always preserved.
40
+ *
41
+ * LOG_DIR absent → skip per-server raw file (dev mode / tool discovery).
42
+ * STREAM_LOG_PATH absent → skip stream-log destination (older spawner or
43
+ * standalone invocation). In both absent cases, stderr works unchanged —
44
+ * a `[mcp-tee-skip]` marker is written to original stderr so the skip is
45
+ * visible to journalctl-level readers.
46
+ *
47
+ * Safe to call once at MCP server module load. Refused on second call to
48
+ * prevent stacking patches.
49
+ */
50
+ export declare function initStderrTee(serverName: string): void;
51
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAeH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CA8ItD"}
@@ -0,0 +1,196 @@
1
+ "use strict";
2
+ /**
3
+ * MCP server stderr tee — dual-destination log capture.
4
+ *
5
+ * Claude Code spawns MCP servers as child processes and consumes their stderr
6
+ * internally. The platform cannot intercept at the spawn level. Each server
7
+ * must tee its own stderr so diagnostic output is retrievable.
8
+ *
9
+ * ┌─────────────────────────┐
10
+ * │ MCP server process │
11
+ * │ │
12
+ * │ console.error("[x] ..") │
13
+ * │ │ │
14
+ * │ ▼ │
15
+ * │ patched stderr.write() │
16
+ * │ │ │
17
+ * │ ├──► original stderr (consumed by Claude Code — opaque)
18
+ * │ │
19
+ * │ ├──► mcp-{name}-stderr-{YYYY-MM-DD}.log (raw chunks, per-plugin)
20
+ * │ │
21
+ * │ └──► $STREAM_LOG_PATH (per-line, prefixed)
22
+ * │ "[<iso>] [mcp:{name}] <line>"
23
+ * └─────────────────────────┘
24
+ *
25
+ * $STREAM_LOG_PATH is set by the spawner (`getMcpServers` in `claude-agent.ts`)
26
+ * to the per-conversation stream log file. The MCP server code itself knows
27
+ * nothing about conversations — it just trusts the spawner's path. This is
28
+ * the scope boundary introduced: the tee is attached per spawn,
29
+ * not per MCP-server process lifetime. Servers spawned for conversation A
30
+ * write to conversation A's file; servers for B write to B's file.
31
+ *
32
+ * Every decision is logged via `[mcp-tee-*]` markers on both the target file
33
+ * and the original stderr so an investigator can confirm from the stream log
34
+ * which tees were wired up, which were skipped, and why.
35
+ */
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.initStderrTee = initStderrTee;
38
+ const node_fs_1 = require("node:fs");
39
+ const node_path_1 = require("node:path");
40
+ const node_string_decoder_1 = require("node:string_decoder");
41
+ // Marker on process.stderr.write so repeat calls to initStderrTee() are
42
+ // detected at runtime, not just warned about in the docstring. A second
43
+ // call would stack patches and double-log every chunk.
44
+ const INIT_MARKER = Symbol.for("maxy.mcpStderrTee.installed");
45
+ /**
46
+ * Patch process.stderr.write to tee to:
47
+ * 1. Per-server raw log (`mcp-<serverName>-stderr-<date>.log` under LOG_DIR).
48
+ * 2. The per-conversation stream log at `STREAM_LOG_PATH` — per-line, prefixed.
49
+ * 3. The original stderr (consumed by Claude Code) — always preserved.
50
+ *
51
+ * LOG_DIR absent → skip per-server raw file (dev mode / tool discovery).
52
+ * STREAM_LOG_PATH absent → skip stream-log destination (older spawner or
53
+ * standalone invocation). In both absent cases, stderr works unchanged —
54
+ * a `[mcp-tee-skip]` marker is written to original stderr so the skip is
55
+ * visible to journalctl-level readers.
56
+ *
57
+ * Safe to call once at MCP server module load. Refused on second call to
58
+ * prevent stacking patches.
59
+ */
60
+ function initStderrTee(serverName) {
61
+ const logDir = process.env.LOG_DIR;
62
+ const streamLogPath = process.env.STREAM_LOG_PATH;
63
+ // Refuse repeat patches — stacking them would double-log every chunk
64
+ // and corrupt the re-entrancy guard (originalWrite would capture the
65
+ // already-patched function on the second call).
66
+ if (process.stderr.write[INIT_MARKER])
67
+ return;
68
+ // Keep a direct reference to the unpatched writer. All diagnostic output
69
+ // from inside this module MUST go through originalWrite to avoid
70
+ // re-entering the patched writer (which would recurse on a tee failure).
71
+ const originalWrite = process.stderr.write.bind(process.stderr);
72
+ const tsPrefix = () => `[${new Date().toISOString()}]`;
73
+ const skipTee = (reason, destination) => {
74
+ originalWrite(`${tsPrefix()} [platform] [mcp-tee-skip] server=${serverName} destination=${destination} reason=${JSON.stringify(reason)}\n`);
75
+ };
76
+ // --- Destination 1: per-server raw file (optional, existing behaviour)
77
+ let perServerStream;
78
+ if (logDir) {
79
+ try {
80
+ (0, node_fs_1.mkdirSync)(logDir, { recursive: true });
81
+ const date = new Date().toISOString().slice(0, 10);
82
+ perServerStream = (0, node_fs_1.createWriteStream)((0, node_path_1.resolve)(logDir, `mcp-${serverName}-stderr-${date}.log`), { flags: "a" });
83
+ perServerStream.on("error", (err) => {
84
+ originalWrite(`${tsPrefix()} [platform] [mcp-tee-error] server=${serverName} destination=per-server reason=${JSON.stringify(err.message)}\n`);
85
+ });
86
+ }
87
+ catch (err) {
88
+ const msg = err instanceof Error ? err.message : String(err);
89
+ skipTee(msg, "per-server");
90
+ perServerStream = undefined;
91
+ }
92
+ }
93
+ else {
94
+ skipTee("LOG_DIR not set", "per-server");
95
+ }
96
+ // --- Destination 2: per-conversation stream log
97
+ let streamLogStream;
98
+ if (streamLogPath) {
99
+ try {
100
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(streamLogPath), { recursive: true });
101
+ const s = (0, node_fs_1.createWriteStream)(streamLogPath, { flags: "a" });
102
+ s.on("error", (err) => {
103
+ originalWrite(`${tsPrefix()} [platform] [mcp-tee-error] server=${serverName} destination=stream-log reason=${JSON.stringify(err.message)}\n`);
104
+ });
105
+ streamLogStream = s;
106
+ // Attach marker lands in the stream log itself so investigators can
107
+ // confirm per-conversation the tee was wired up.
108
+ s.write(`${tsPrefix()} [platform] [mcp-tee-attach] server=${serverName} streamLogPath=${streamLogPath}\n`);
109
+ }
110
+ catch (err) {
111
+ const msg = err instanceof Error ? err.message : String(err);
112
+ skipTee(msg, "stream-log");
113
+ streamLogStream = undefined;
114
+ }
115
+ }
116
+ else {
117
+ skipTee("STREAM_LOG_PATH not set", "stream-log");
118
+ }
119
+ // If neither tee target is available, leave stderr untouched — patching
120
+ // the writer to a no-op tee would still consume event-loop cycles on
121
+ // every write for zero observability gain.
122
+ if (!perServerStream && !streamLogStream) {
123
+ return;
124
+ }
125
+ // Line buffer — accumulates characters written to stderr so complete
126
+ // newline-terminated lines can be prefixed and emitted to the stream log.
127
+ // Partial chunks are held until a newline arrives (or beforeExit flushes).
128
+ let lineBuffer = "";
129
+ // StringDecoder buffers trailing UTF-8 continuation bytes across chunks.
130
+ // Without it, a multi-byte codepoint that straddles a write boundary would
131
+ // render as two U+FFFD replacement characters in the stream log. Only
132
+ // matters when a caller writes Uint8Array chunks directly — console.error
133
+ // produces strings — but plugins piping child-process stderr can hit this.
134
+ const utf8 = new node_string_decoder_1.StringDecoder("utf8");
135
+ const emitCompleteLinesToStreamLog = (chunk) => {
136
+ if (!streamLogStream)
137
+ return;
138
+ lineBuffer += chunk;
139
+ let newlineIndex;
140
+ // eslint-disable-next-line no-cond-assign
141
+ while ((newlineIndex = lineBuffer.indexOf("\n")) !== -1) {
142
+ const line = lineBuffer.slice(0, newlineIndex);
143
+ lineBuffer = lineBuffer.slice(newlineIndex + 1);
144
+ if (line.length === 0)
145
+ continue; // skip blank lines
146
+ if (streamLogStream.destroyed || streamLogStream.writableEnded)
147
+ continue;
148
+ streamLogStream.write(`${tsPrefix()} [mcp:${serverName}] ${line}\n`);
149
+ }
150
+ };
151
+ process.stderr.write = (chunk, ...args) => {
152
+ // 1. Per-server raw file — behaviour (no prefix).
153
+ if (perServerStream && !perServerStream.destroyed) {
154
+ perServerStream.write(chunk);
155
+ }
156
+ // 2. Stream log — per-line, prefixed.
157
+ if (streamLogStream) {
158
+ try {
159
+ const text = typeof chunk === "string"
160
+ ? chunk
161
+ : utf8.write(Buffer.from(chunk));
162
+ emitCompleteLinesToStreamLog(text);
163
+ }
164
+ catch (err) {
165
+ const msg = err instanceof Error ? err.message : String(err);
166
+ originalWrite(`${tsPrefix()} [platform] [mcp-tee-emit-error] server=${serverName} reason=${JSON.stringify(msg)}\n`);
167
+ }
168
+ }
169
+ // 3. Original stderr — Claude Code still gets what it's always got.
170
+ return originalWrite(chunk, ...args);
171
+ };
172
+ // Mark the patch so a second initStderrTee() call is refused at the top.
173
+ process.stderr.write[INIT_MARKER] = true;
174
+ // Flush any trailing unterminated segment on graceful event-loop drain.
175
+ // `beforeExit` can fire repeatedly: each async write to `streamLogStream`
176
+ // schedules I/O that keeps the event loop alive, which drains, which fires
177
+ // `beforeExit` again. With N MCP servers attached, the handler can emit
178
+ // thousands of detach lines per second. The `detached` flag gates the
179
+ // drain+detach emission to a single invocation; subsequent firings are
180
+ // no-ops. Buffer flushing also runs only once — partial-line state at true
181
+ // process exit doesn't change across re-fires.
182
+ let detached = false;
183
+ process.on("beforeExit", () => {
184
+ if (detached)
185
+ return;
186
+ detached = true;
187
+ if (streamLogStream && !streamLogStream.destroyed && !streamLogStream.writableEnded) {
188
+ if (lineBuffer.length > 0) {
189
+ streamLogStream.write(`${tsPrefix()} [mcp:${serverName}] ${lineBuffer}\n`);
190
+ }
191
+ streamLogStream.write(`${tsPrefix()} [platform] [mcp-tee-detach] server=${serverName} reason=process-before-exit\n`);
192
+ }
193
+ lineBuffer = "";
194
+ });
195
+ }
196
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "@maxy/mcp-stderr-tee",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "commonjs",
6
+ "main": "dist/index.js"
7
+ }