mulmoterminal 0.6.1 → 0.7.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 (151) hide show
  1. package/Dockerfile.sandbox +30 -0
  2. package/README.md +82 -0
  3. package/dist/assets/{abnfDiagram-VRR7QNED-6nNByj6v-BRfj7y6X.js → abnfDiagram-VRR7QNED-6nNByj6v-IGFnNkfa.js} +1 -1
  4. package/dist/assets/architecture-TIHT7OUA-CYMWc3UT-bWcDFBmU.js +1 -0
  5. package/dist/assets/{architectureDiagram-ZJ3FMSHR-Bgnyaj_i-DMTeuW7L.js → architectureDiagram-ZJ3FMSHR-Bgnyaj_i-CAxLCofF.js} +1 -1
  6. package/dist/assets/{blockDiagram-677ZJIJ3-DQ35o5E4-M9RAb7cT.js → blockDiagram-677ZJIJ3-DQ35o5E4-x3_3MCHB.js} +1 -1
  7. package/dist/assets/{c4Diagram-LMCZKHZV-ClWZeiWo-Dc-cmWP4.js → c4Diagram-LMCZKHZV-ClWZeiWo-BHgXFEHO.js} +1 -1
  8. package/dist/assets/channel-Di5rtkx0-GohkERB0.js +1 -0
  9. package/dist/assets/{chunk-32BRIVSS-CCt9wtYd-DT1SummH.js → chunk-32BRIVSS-CCt9wtYd-Cevd76M7.js} +1 -1
  10. package/dist/assets/{chunk-52WLFC77-FbBbR4uI-DcHhD_Vq.js → chunk-52WLFC77-FbBbR4uI-BiOqnP1l.js} +1 -1
  11. package/dist/assets/{chunk-C7G6YPKG-C87hlS9c-DF1Hdam6.js → chunk-C7G6YPKG-C87hlS9c-qjNSM7yE.js} +1 -1
  12. package/dist/assets/{chunk-EX3LRPZG-BqGqMXLN-D1M61uVx.js → chunk-EX3LRPZG-BqGqMXLN-DP83K0Kb.js} +1 -1
  13. package/dist/assets/{chunk-FWX5IMBZ-BkbSAAuW-EW9ZPm5p.js → chunk-FWX5IMBZ-BkbSAAuW-DDaQ5S2G.js} +2 -2
  14. package/dist/assets/{chunk-HOUHSVGY-Cxu0eDlh-DnSK7t3L.js → chunk-HOUHSVGY-Cxu0eDlh-CmLLHV-D.js} +1 -1
  15. package/dist/assets/{chunk-ICXQ74PX-hiraF_Xj-Dl3d1Q-4.js → chunk-ICXQ74PX-hiraF_Xj-xuqMpWo5.js} +1 -1
  16. package/dist/assets/{chunk-MOJQB5TN-CqxshQHA-DCZHBsfc.js → chunk-MOJQB5TN-CqxshQHA-jUnAGLd5.js} +1 -1
  17. package/dist/assets/{chunk-OGEWGWER-CjCr7ceX-BnohLmvg.js → chunk-OGEWGWER-CjCr7ceX-Be5sOa4R.js} +1 -1
  18. package/dist/assets/{chunk-PUDLZKDR-Dx6M-vz1-CqvcQ_gE.js → chunk-PUDLZKDR-Dx6M-vz1-B6N9l2sp.js} +1 -1
  19. package/dist/assets/{chunk-Q4XR5HBZ-CZd-9lTB-CHbSCh_V.js → chunk-Q4XR5HBZ-CZd-9lTB-CNc2UnbF.js} +1 -1
  20. package/dist/assets/{chunk-V7JOEXUC-rI0xlC_O-OzbWOXfF.js → chunk-V7JOEXUC-rI0xlC_O-BKwWIZFY.js} +1 -1
  21. package/dist/assets/{chunk-VAUOI2AC-DrcykVNK-D4oLEG67.js → chunk-VAUOI2AC-DrcykVNK-Um-9nuwz.js} +1 -1
  22. package/dist/assets/{chunk-VR4S4FIN-O6iF8Yvf-Dt0kQHmh.js → chunk-VR4S4FIN-O6iF8Yvf-C55cFzwq.js} +1 -1
  23. package/dist/assets/{chunk-WYO6CB5R-B83L_z6I-D3DzbPhG.js → chunk-WYO6CB5R-B83L_z6I-NmPlbhpt.js} +1 -1
  24. package/dist/assets/{chunk-ZGVPDNZ5-BpFv9JSP-DJqCdYKI.js → chunk-ZGVPDNZ5-BpFv9JSP-DQuoM4dk.js} +1 -1
  25. package/dist/assets/classDiagram-OUVF2IWQ-BgAZMSbT-GlJ3u1Wb.js +1 -0
  26. package/dist/assets/classDiagram-v2-EOCWNBFH-DxHTyui1-GlJ3u1Wb.js +1 -0
  27. package/dist/assets/cynefin-VYW2F7L2-DqA3n9nY-Dnv61JuF.js +1 -0
  28. package/dist/assets/{cynefinDiagram-TSTJHNR4-x0-0sQ15-CkaW2deB.js → cynefinDiagram-TSTJHNR4-x0-0sQ15-BjW2zyQO.js} +1 -1
  29. package/dist/assets/{dagre-VKFMJZFB-CQdfl-bx-CUTHQgzA.js → dagre-VKFMJZFB-CQdfl-bx-CiVq2pd3.js} +1 -1
  30. package/dist/assets/{diagram-FQU43EPY-BOSB6VUb-D5wdryla.js → diagram-FQU43EPY-BOSB6VUb-BviH94fe.js} +1 -1
  31. package/dist/assets/{diagram-G47NLZAW-DLXrcXsN-oCO7nvFu.js → diagram-G47NLZAW-DLXrcXsN-C9ANoKwM.js} +1 -1
  32. package/dist/assets/{diagram-NH7WQ7WH-BMQp1rkF-DyRW3B_6.js → diagram-NH7WQ7WH-BMQp1rkF-iRLoGEIF.js} +1 -1
  33. package/dist/assets/{diagram-OA4YK3LP-D1wQ0vUj-w6zTb_BH.js → diagram-OA4YK3LP-D1wQ0vUj-DApMIOOW.js} +1 -1
  34. package/dist/assets/{diagram-WEI45ONY-RR0DpF8R-6-4LvpAz.js → diagram-WEI45ONY-RR0DpF8R-5HyDvuK6.js} +1 -1
  35. package/dist/assets/{ebnfDiagram-CCIWWBDH-M123uVJ8-Bn6UdzFR.js → ebnfDiagram-CCIWWBDH-M123uVJ8-2mE0-H0-.js} +1 -1
  36. package/dist/assets/{erDiagram-Q63AITRT-BWx_-PXG-BgHXk-m7.js → erDiagram-Q63AITRT-BWx_-PXG-CY-qvEZP.js} +1 -1
  37. package/dist/assets/eventmodeling-45OFAUF4-_BVSjAXf-Bbbkffe1.js +1 -0
  38. package/dist/assets/flowDiagram-23GEKE2U-BeOc_anm-DulRGLp4.js +1 -0
  39. package/dist/assets/{ganttDiagram-NO4QXBWP-BOoJ1eTw-aZ--txIR.js → ganttDiagram-NO4QXBWP-BOoJ1eTw-DJnZnEVq.js} +1 -1
  40. package/dist/assets/gitGraph-TEB2WS4Q-CH12KLTN-BtMbgf2j.js +1 -0
  41. package/dist/assets/{gitGraphDiagram-IHSO6WYX-B2CJhk_G-B_CerNh6.js → gitGraphDiagram-IHSO6WYX-B2CJhk_G-CS2k47ZS.js} +1 -1
  42. package/dist/assets/index-B7dNL7y0.css +1 -0
  43. package/dist/assets/index-a5e0Ms7-.js +355 -0
  44. package/dist/assets/info-DKCQHKI2-Cbw3mbiK-pm3KH1hx.js +1 -0
  45. package/dist/assets/{infoDiagram-FWYZ7A6U-Mp1X3pBP-BzMa5ofJ.js → infoDiagram-FWYZ7A6U-Mp1X3pBP-DA4NgCU8.js} +1 -1
  46. package/dist/assets/{ishikawaDiagram-FXEZZL3T-BNG7tkJu-SGEBO_iN.js → ishikawaDiagram-FXEZZL3T-BNG7tkJu-DPK-CeOV.js} +1 -1
  47. package/dist/assets/{journeyDiagram-5HDEW3XC-Dbp_hY9X-BpJI0cuT.js → journeyDiagram-5HDEW3XC-Dbp_hY9X-C7-vjDGz.js} +1 -1
  48. package/dist/assets/{kanban-definition-HUTT4EX6-DSTc5u3q-BaW5f-cr.js → kanban-definition-HUTT4EX6-DSTc5u3q-DDPYyGjM.js} +1 -1
  49. package/dist/assets/{line-B1wBwzrY-Be5cCdHP.js → line-B1wBwzrY-C8LCGp0g.js} +1 -1
  50. package/dist/assets/marp-CXxH4bDL.js +3458 -0
  51. package/dist/assets/{mermaid-parser.core-DC7NPJ_M-gJ-cjH2L.js → mermaid-parser.core-DC7NPJ_M-DpdiJOd9.js} +2 -2
  52. package/dist/assets/{mermaid.core-DZM3Ha-E-BRXQiAP5.js → mermaid.core-DZM3Ha-E-qDLGiF8u.js} +3 -3
  53. package/dist/assets/{mindmap-definition-LN4V7U3C-DYtgcMsY-ogEVkYX3.js → mindmap-definition-LN4V7U3C-DYtgcMsY-X9JS6B27.js} +1 -1
  54. package/dist/assets/packet-7NZHBO7P-lwb58iYx-CrXwOyyd.js +1 -0
  55. package/dist/assets/{pegDiagram-2B236MQR-C43eIpKM-BfalDaw5.js → pegDiagram-2B236MQR-C43eIpKM-q5euCaBj.js} +1 -1
  56. package/dist/assets/pie-RZYD4A2V-B1UWb4Gu-BaiAaew5.js +1 -0
  57. package/dist/assets/{pieDiagram-ENE6RG2P-BkTqgJyR-Bvk8HOTC.js → pieDiagram-ENE6RG2P-BkTqgJyR-ee81vkeN.js} +1 -1
  58. package/dist/assets/{quadrantDiagram-ABIIQ3AL-Bm1Zjm45-DGa-3QX3.js → quadrantDiagram-ABIIQ3AL-Bm1Zjm45-CoACCyaD.js} +1 -1
  59. package/dist/assets/radar-I7S5WNFK-7CKcb_l--CFNO3OrR.js +1 -0
  60. package/dist/assets/railroad-3IZDKUUU-gCySKdnW-CdeNVhid.js +1 -0
  61. package/dist/assets/railroad-abnf-AHOZXSZD-BophH4r--Btv0ecNO.js +1 -0
  62. package/dist/assets/railroad-ebnf-EBAXGLYW-AZNjl_Zu-CH9b-tjP.js +1 -0
  63. package/dist/assets/railroad-peg-LSFZ7HO6-BSiEEyeb-C3e6kQJQ.js +1 -0
  64. package/dist/assets/{railroadDiagram-RFXS5EU6-BkfbdeAs-BurdYf-r.js → railroadDiagram-RFXS5EU6-BkfbdeAs-Dgv56gIO.js} +1 -1
  65. package/dist/assets/{requirementDiagram-TGXJPOKE-CrGTTjYg-bn54utB3.js → requirementDiagram-TGXJPOKE-CrGTTjYg-CCYvGt-t.js} +1 -1
  66. package/dist/assets/{sankeyDiagram-HTMAVEWB-rWXPf03Z-DID2TYa0.js → sankeyDiagram-HTMAVEWB-rWXPf03Z-CprB_Z8u.js} +1 -1
  67. package/dist/assets/{sequenceDiagram-DBY2YBRQ-nkJYWO2m-BZTbGdBV.js → sequenceDiagram-DBY2YBRQ-nkJYWO2m-Dq6hIypb.js} +1 -1
  68. package/dist/assets/{stateDiagram-2N3HPSRC-T4-clK8b-CvBAzjQl.js → stateDiagram-2N3HPSRC-T4-clK8b-Ch8H-wNS.js} +1 -1
  69. package/dist/assets/stateDiagram-v2-6OUMAXLB-DIp7nhRd-C9sabJiH.js +1 -0
  70. package/dist/assets/{swimlanes-5IMT3BWC-HmQNEntu-BwBS76rD.js → swimlanes-5IMT3BWC-HmQNEntu-D8pbAIlW.js} +1 -1
  71. package/dist/assets/swimlanesDiagram-G3AALYLV-BeoZhwg7-Dgqw4HBg.js +8 -0
  72. package/dist/assets/{timeline-definition-FHXFAJF6-CNc9jSTP-sKenRvwh.js → timeline-definition-FHXFAJF6-CNc9jSTP--kxf46LG.js} +1 -1
  73. package/dist/assets/treeView-QDETBFTQ-nIQcG1h9-pio3kitt.js +1 -0
  74. package/dist/assets/treemap-6X3UGDF4-BnsvC8yL-DprilSCD.js +1 -0
  75. package/dist/assets/{vennDiagram-L72KCM5P-Dr3pTJ_0-DgoLMSsY.js → vennDiagram-L72KCM5P-Dr3pTJ_0-BDmffNZb.js} +1 -1
  76. package/dist/assets/wardley-OPB4EBWU-8Odxkx6V-E_XL_tuo.js +1 -0
  77. package/dist/assets/{wardleyDiagram-EHGQE667-DSFc7ZZa-Djx4JXUU.js → wardleyDiagram-EHGQE667-DSFc7ZZa-B5tAi_lA.js} +1 -1
  78. package/dist/assets/{xychartDiagram-FW5EYKEG-BP0Nn4Pp-CtCbuXVc.js → xychartDiagram-FW5EYKEG-BP0Nn4Pp-Du36xrax.js} +1 -1
  79. package/dist/index.html +2 -2
  80. package/package.json +17 -7
  81. package/server/agents/claude.ts +10 -0
  82. package/server/agents/codex.ts +7 -0
  83. package/server/agents/registry.spec.ts +44 -0
  84. package/server/agents/registry.ts +13 -0
  85. package/server/agents/types.ts +11 -0
  86. package/server/app-config.spec.ts +93 -13
  87. package/server/app-config.ts +107 -4
  88. package/server/backends/collections.spec.ts +107 -2
  89. package/server/backends/collections.ts +540 -388
  90. package/server/backends/remoteHost/attachmentStore.spec.ts +34 -0
  91. package/server/backends/remoteHost/attachmentStore.ts +61 -0
  92. package/server/backends/remoteHost/collectionPage.ts +25 -0
  93. package/server/backends/remoteHost/firebase.ts +28 -0
  94. package/server/backends/remoteHost/getFeed.spec.ts +94 -0
  95. package/server/backends/remoteHost/handlers.spec.ts +132 -0
  96. package/server/backends/remoteHost/handlers.ts +202 -0
  97. package/server/backends/remoteHost/index.ts +98 -0
  98. package/server/backends/remoteHost/ingestAttachments.spec.ts +91 -0
  99. package/server/backends/remoteHost/ingestAttachments.ts +98 -0
  100. package/server/backends/remoteHost/onExpire.ts +51 -0
  101. package/server/backends/remoteHost/skills.spec.ts +82 -0
  102. package/server/backends/remoteHost/skills.ts +90 -0
  103. package/server/backends/remoteView.ts +296 -0
  104. package/server/backends/shortcuts.ts +3 -2
  105. package/server/backends/thumbnailStore.spec.ts +61 -0
  106. package/server/backends/thumbnailStore.ts +104 -0
  107. package/server/claude-args.spec.ts +7 -3
  108. package/server/claude-args.ts +6 -9
  109. package/server/codex-args.spec.ts +20 -0
  110. package/server/codex-args.ts +17 -0
  111. package/server/codex-session.spec.ts +142 -0
  112. package/server/codex-session.ts +121 -0
  113. package/server/config-routes.ts +58 -3
  114. package/server/files-browse.spec.ts +81 -0
  115. package/server/files-browse.ts +185 -0
  116. package/server/gh.ts +29 -0
  117. package/server/index.ts +508 -100
  118. package/server/issues.spec.ts +38 -0
  119. package/server/issues.ts +67 -0
  120. package/server/prs.spec.ts +64 -0
  121. package/server/prs.ts +93 -0
  122. package/server/sandbox.spec.ts +172 -0
  123. package/server/sandbox.ts +271 -0
  124. package/server/terminal-replay.spec.ts +41 -0
  125. package/server/terminal-replay.ts +24 -0
  126. package/server/tmux.spec.ts +43 -0
  127. package/server/tmux.ts +115 -0
  128. package/dist/assets/architecture-TIHT7OUA-CYMWc3UT--R6rq7zE.js +0 -1
  129. package/dist/assets/channel-Di5rtkx0-COh04QUU.js +0 -1
  130. package/dist/assets/classDiagram-OUVF2IWQ-BgAZMSbT-D6UdMWjU.js +0 -1
  131. package/dist/assets/classDiagram-v2-EOCWNBFH-DxHTyui1-D6UdMWjU.js +0 -1
  132. package/dist/assets/cynefin-VYW2F7L2-DqA3n9nY-DqbJKifg.js +0 -1
  133. package/dist/assets/eventmodeling-45OFAUF4-_BVSjAXf-D2SACNAL.js +0 -1
  134. package/dist/assets/flowDiagram-23GEKE2U-BeOc_anm-BGbwxw_9.js +0 -1
  135. package/dist/assets/gitGraph-TEB2WS4Q-CH12KLTN-BuzBqT4T.js +0 -1
  136. package/dist/assets/index-BQm57UKf.js +0 -317
  137. package/dist/assets/index-CByoF_Rr.css +0 -1
  138. package/dist/assets/info-DKCQHKI2-Cbw3mbiK-Bzw7yQLG.js +0 -1
  139. package/dist/assets/marp-DhAav9Gi.js +0 -3451
  140. package/dist/assets/packet-7NZHBO7P-lwb58iYx-CjzRO2M1.js +0 -1
  141. package/dist/assets/pie-RZYD4A2V-B1UWb4Gu-4RXP0X8g.js +0 -1
  142. package/dist/assets/radar-I7S5WNFK-7CKcb_l--fxir4rC5.js +0 -1
  143. package/dist/assets/railroad-3IZDKUUU-gCySKdnW-B8femwGM.js +0 -1
  144. package/dist/assets/railroad-abnf-AHOZXSZD-BophH4r--tlhAE3rL.js +0 -1
  145. package/dist/assets/railroad-ebnf-EBAXGLYW-AZNjl_Zu-D3h59v-7.js +0 -1
  146. package/dist/assets/railroad-peg-LSFZ7HO6-BSiEEyeb-Dl5i_mYn.js +0 -1
  147. package/dist/assets/stateDiagram-v2-6OUMAXLB-DIp7nhRd-xACBEDy7.js +0 -1
  148. package/dist/assets/swimlanesDiagram-G3AALYLV-BeoZhwg7-CCId37Gu.js +0 -8
  149. package/dist/assets/treeView-QDETBFTQ-nIQcG1h9-CMge-ZdT.js +0 -1
  150. package/dist/assets/treemap-6X3UGDF4-BnsvC8yL-IEow94mT.js +0 -1
  151. package/dist/assets/wardley-OPB4EBWU-8Odxkx6V-YdgYPFcC.js +0 -1
@@ -0,0 +1,30 @@
1
+ # MulmoTerminal sandbox image: runs the interactive `claude` CLI inside a container so
2
+ # an untrusted workspace can't touch the host. Deliberately minimal — unlike MulmoClaude
3
+ # this image needs NO in-container MCP broker, because MulmoTerminal's GUI MCP is served
4
+ # over HTTP by the host and reached at host.docker.internal (see docs/spawn-architecture).
5
+ #
6
+ # What's inside: node (for the claude CLI), curl (the activity hooks POST to the host),
7
+ # git + ripgrep + ca-certificates (claude's built-in tools). No secrets, no host FS.
8
+ FROM node:22-slim
9
+
10
+ RUN apt-get update && apt-get install -y --no-install-recommends \
11
+ git \
12
+ curl \
13
+ ca-certificates \
14
+ ripgrep \
15
+ && rm -rf /var/lib/apt/lists/*
16
+
17
+ # The interactive agent CLI. Pin nothing here — a rebuild picks up the latest, matching
18
+ # how the host runs the newest `claude`.
19
+ RUN npm install -g @anthropic-ai/claude-code
20
+
21
+ # Run as the unprivileged `node` user (uid 1000). Phase 1 is macOS-only: Docker Desktop
22
+ # maps bind-mount ownership transparently there. The server gates the sandbox off on Linux
23
+ # (no `--user` yet → uid-1000 ownership mismatch) and Windows (host paths aren't valid
24
+ # Linux container paths), falling back to the host spawn — see Phase 5 (#202).
25
+ USER node
26
+ ENV HOME=/home/node
27
+ WORKDIR /home/node/workspace
28
+
29
+ # The host passes `claude ...` (with a rewritten --mcp-config / --settings) as the
30
+ # command, so no ENTRYPOINT/CMD baked in — the container IS one claude session.
package/README.md CHANGED
@@ -121,6 +121,62 @@ SDK; we drive the real interactive CLI and relay its TTY over the WebSocket.
121
121
 
122
122
  ---
123
123
 
124
+ ## Session persistence (tmux)
125
+
126
+ If **`tmux` is installed**, MulmoTerminal runs each Claude session and launcher inside
127
+ a tmux session, so **a server crash or restart doesn't kill your terminals** — the
128
+ processes keep running and reattach when the server comes back (like `screen`/`tmux`).
129
+ A long build, a dev server, or a mid-turn Claude session all survive `node --watch`
130
+ reloads and crashes. It uses its **own** tmux server (`-L mulmoterminal`) and config, so
131
+ it never touches your personal tmux sessions or keybindings.
132
+
133
+ **No tmux? No problem** — terminals fall back to plain (non-persistent) PTYs, exactly as
134
+ before. An explicit close (a cell's ✕) ends the tmux session; a machine reboot does not
135
+ survive (tmux itself is gone). Command-cell scripts are ephemeral and not persisted.
136
+
137
+ ---
138
+
139
+ ## Docker sandbox (experimental, single view)
140
+
141
+ Set **`MULMOTERMINAL_SANDBOX=1`** (and have Docker running) to run the **single-view**
142
+ Claude session inside a container instead of on the host, while Claude still reaches the
143
+ app's GUI MCP + activity hooks over `host.docker.internal`. The `mulmoterminal-sandbox`
144
+ image is **built automatically** on first launch from the shipped `Dockerfile.sandbox`
145
+ (~1 min, once; rebuilt only when that file changes). Override the name with
146
+ `MULMOTERMINAL_SANDBOX_IMAGE`. If the image can't be built (e.g. Docker down), the session
147
+ falls back to the host spawn — no cryptic failure.
148
+
149
+ This **contains** Claude — it can't reach the host filesystem outside the mounts, host
150
+ processes, or arbitrary host ports. It is **not full isolation**: the **workspace** and
151
+ **`~/.claude`** are bind-mounted **read-write** by design (so Claude edits your project,
152
+ and transcripts interoperate with host sessions), so those specific paths stay mutable
153
+ from inside. The sandbox is **non-persistent** (the container is
154
+ `--rm`, tied to the session), **opt-in and single-view only** — the grid keeps its host +
155
+ tmux path, and with the flag unset (or Docker unavailable) everything runs on the host
156
+ exactly as before. **macOS only** for now — on Linux (bind-mount uid ownership) and
157
+ Windows (host paths aren't valid Linux container paths) it falls back to the host spawn;
158
+ both are follow-ups. Adding arbitrary user MCP servers to the sandbox is in progress
159
+ (see #202).
160
+
161
+ **Authentication (macOS).** Claude's live login token lives in the macOS **Keychain**,
162
+ which the container can't read (mounting `~/.claude` alone isn't enough — its
163
+ `.credentials.json` is often absent or stale). On each sandbox spawn MulmoTerminal exports
164
+ the current credential to a per-session `~/.mulmoterminal/sandbox/creds-<id>.json`
165
+ (mode `0600`, removed when the session ends) and mounts it **read-only** over the
166
+ container's `~/.claude/.credentials.json`; your host `~/.claude` is never modified. If
167
+ you've never logged in on the host, run `claude` once first — otherwise the server logs a
168
+ warning and the container shows "Not logged in".
169
+
170
+ **Host credentials (opt-in).** By default the sandbox has no host credentials. To let the
171
+ sandboxed Claude use `gh`/`git`, set **`SANDBOX_MOUNT_CONFIGS=gh,gitconfig`** — a **fixed
172
+ allowlist** (you pick names, never arbitrary paths): `gh` mounts `~/.config/gh` read-only
173
+ and passes a `GH_TOKEN` (from `gh auth token`, since macOS keeps it in the Keychain), and
174
+ `gitconfig` mounts `~/.gitconfig` read-only. Set **`SANDBOX_SSH_AGENT_FORWARD=1`** to
175
+ forward the SSH agent socket (the keys never enter the container). Both are read only when
176
+ building the sandbox spawn, so they have no effect unless `MULMOTERMINAL_SANDBOX` is on.
177
+
178
+ ---
179
+
124
180
  ## Tech stack
125
181
 
126
182
  | Layer | Technology |
@@ -162,6 +218,9 @@ The Settings modal (⚙) persists per-user UI choices to `~/.mulmoterminal/confi
162
218
  | ------------ | ------- |
163
219
  | `cwdPresets` | Quick-pick directories offered when launching a terminal. |
164
220
  | `soundFile` | Absolute path to a custom **attention sound** (played when a session needs attention). Empty/unset uses the built-in synthesized chime. |
221
+ | `prRepos` | `owner/repo` entries whose open PRs/issues the cross-repo **PRs & Issues** view aggregates (via your `gh` login). |
222
+ | `launchers` | `{ label, command }` entries offered in a grid cell's launcher besides Claude — a plain shell, `codex`, any interactive command. |
223
+ | `userMcpServers` | `{ id, url }` HTTP MCP servers merged into the **single-view** Claude session's `--mcp-config` (a `localhost` URL is reached over `host.docker.internal` in the Docker sandbox). Takes effect on the next session. |
165
224
 
166
225
  **Attention sound.** The default chime is generated with the Web Audio API — **no
167
226
  audio file is bundled**, so the npm package stays light and has no media-licensing
@@ -238,6 +297,13 @@ alongside the Claude sessions. Scripts are **per-directory**: the cell reads the
238
297
  `script.json` of whatever directory you select, so different cells can offer
239
298
  different projects' scripts.
240
299
 
300
+ The same launcher also has an **or launch** row for your configured **launch commands**
301
+ — a plain interactive shell, `codex`, any command — set in Settings (⚙) → **Launch
302
+ commands** as `{ label, command }` (e.g. `Shell` → `$SHELL`, `Codex` → `codex`). Unlike
303
+ a one-shot script, a launcher runs as a **persistent terminal in the cell's directory**:
304
+ it survives grid page switches and reconnects, and its dot shows running vs. exited (it
305
+ has no Claude hooks, so no blocked/done states).
306
+
241
307
  Every running terminal's header also has a **▶ Run ▾** dropdown (next to the
242
308
  connection status), in both the single view and each grid cell — but **only when the
243
309
  open project has scripts** (no `script.json`, no button). It lists the **open
@@ -278,6 +344,22 @@ command, so the file is the allowlist of what can run.
278
344
 
279
345
  ---
280
346
 
347
+ ## Files view (browse & edit)
348
+
349
+ Every terminal header has a **📁 Files** button that opens a full-screen file explorer
350
+ rooted at **that terminal's project directory** — so after Claude says "wrote `foo.md`"
351
+ you can jump straight there to read or edit it. The left pane is a lazy-loaded directory
352
+ tree; clicking a file opens it in a **CodeMirror** editor (Markdown / JS-TS / JSON
353
+ highlighting, everything else as plain text). Markdown files get a **Preview** toggle
354
+ that renders via the server's sandboxed `…/md` HTML. **Save** (or ⌘/Ctrl-S) writes back.
355
+
356
+ All reads and writes go through `GET/PUT /api/files/browse/*?cwd=&path=`, and every
357
+ `path` is **contained within the project root** (server-side) — `..`/absolute escapes
358
+ are rejected for reads and writes alike, so editing can't reach outside the directory
359
+ the terminal is pointed at.
360
+
361
+ ---
362
+
281
363
  ## Server API specification
282
364
 
283
365
  Base URL: `http://localhost:$PORT` (default `http://localhost:34567`).
@@ -1 +1 @@
1
- import{n as e}from"./chunk-Y2CYZVJY-Bdt8pFDJ-DsF7k-Jl.js";import{f as t}from"./src-Brzfja-q-ch1VcTiG.js";import{_ as n,g as r}from"./mermaid-parser.core-DC7NPJ_M-gJ-cjH2L.js";import"./chunk-WYO6CB5R-B83L_z6I-D3DzbPhG.js";import"./chunk-VAUOI2AC-DrcykVNK-D4oLEG67.js";import{n as i,r as a,t as o}from"./chunk-MOJQB5TN-CqxshQHA-DCZHBsfc.js";import{t as s}from"./chunk-JWPE2WC7-BQ3zXr2k-DSd3Ct95.js";var c=r().RailroadAbnf.parser.LangiumParser,l=e(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=e(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=e(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=e(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=e(e=>{switch(e.$type){case`AbnfStringLiteral`:return{type:`terminal`,value:e.value};case`AbnfNumVal`:return{type:`terminal`,value:e.value};case`AbnfRuleName`:return{type:`nonterminal`,name:e.name};case`AbnfGroup`:return l(e.element);case`AbnfOptionalGroup`:return{type:`optional`,element:l(e.element)};default:throw Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=e(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=e(e=>{s(e,a),e.title&&a.setTitle(e.title),e.rules.map(e=>a.addRule(m(e)))},`populateDb`),g={parser:{parse:e(e=>{a.clear(),t.debug(`[ABNF Parser] Starting Langium parse`);let r=c.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new n(r);let i=r.value;t.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),t.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:a}},db:a,renderer:o,styles:i};export{g as diagram};
1
+ import{n as e}from"./chunk-Y2CYZVJY-Bdt8pFDJ-DsF7k-Jl.js";import{f as t}from"./src-Brzfja-q-ch1VcTiG.js";import{_ as n,g as r}from"./mermaid-parser.core-DC7NPJ_M-DpdiJOd9.js";import"./chunk-WYO6CB5R-B83L_z6I-NmPlbhpt.js";import"./chunk-VAUOI2AC-DrcykVNK-Um-9nuwz.js";import{n as i,r as a,t as o}from"./chunk-MOJQB5TN-CqxshQHA-jUnAGLd5.js";import{t as s}from"./chunk-JWPE2WC7-BQ3zXr2k-DSd3Ct95.js";var c=r().RailroadAbnf.parser.LangiumParser,l=e(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=e(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=e(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=e(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=e(e=>{switch(e.$type){case`AbnfStringLiteral`:return{type:`terminal`,value:e.value};case`AbnfNumVal`:return{type:`terminal`,value:e.value};case`AbnfRuleName`:return{type:`nonterminal`,name:e.name};case`AbnfGroup`:return l(e.element);case`AbnfOptionalGroup`:return{type:`optional`,element:l(e.element)};default:throw Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=e(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=e(e=>{s(e,a),e.title&&a.setTitle(e.title),e.rules.map(e=>a.addRule(m(e)))},`populateDb`),g={parser:{parse:e(e=>{a.clear(),t.debug(`[ABNF Parser] Starting Langium parse`);let r=c.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new n(r);let i=r.value;t.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),t.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:a}},db:a,renderer:o,styles:i};export{g as diagram};
@@ -0,0 +1 @@
1
+ import{f as e}from"./mermaid-parser.core-DC7NPJ_M-DpdiJOd9.js";export{e as createArchitectureServices};