mulmoterminal 1.7.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -4
- package/bin/mulmoterminal.js +22 -34
- package/bin/npx-cache-hint.d.ts +2 -0
- package/bin/npx-cache-hint.js +69 -0
- package/bin/wait-ready.d.ts +26 -0
- package/bin/wait-ready.js +61 -0
- package/common/modelPresets.ts +11 -6
- package/common/terminalSubmit.ts +70 -0
- package/dist/assets/{abnfDiagram-VRR7QNED-RjiYivmv-0ZB3Q7Mv.js → abnfDiagram-VRR7QNED-RjiYivmv-Dv9Yctvh.js} +1 -1
- package/dist/assets/architecture-TIHT7OUA-0NsD2xW1-D3TA8fTd.js +1 -0
- package/dist/assets/{architectureDiagram-ZJ3FMSHR-3nWA91tG-BGEQ26kW.js → architectureDiagram-ZJ3FMSHR-3nWA91tG-BfLpJ-Hj.js} +1 -1
- package/dist/assets/{blockDiagram-677ZJIJ3-BPuAJQRW-CfG6VjIt.js → blockDiagram-677ZJIJ3-BPuAJQRW-mrEl8u2o.js} +1 -1
- package/dist/assets/{c4Diagram-LMCZKHZV-C0LAqQso-5Y3QAEOO.js → c4Diagram-LMCZKHZV-C0LAqQso-CccnvKxn.js} +1 -1
- package/dist/assets/channel-7wUqSdoX-DzBsOdzu.js +1 -0
- package/dist/assets/{chunk-32BRIVSS-BRrYpgtb-LKWk21XQ.js → chunk-32BRIVSS-BRrYpgtb-z64_xEfL.js} +1 -1
- package/dist/assets/{chunk-52WLFC77-BJ-ss3Xr-DQ0c_tel.js → chunk-52WLFC77-BJ-ss3Xr-BbTx44vc.js} +1 -1
- package/dist/assets/{chunk-C7G6YPKG-BZEucKEL-D1ScG-A9.js → chunk-C7G6YPKG-BZEucKEL-UIcrTcAw.js} +1 -1
- package/dist/assets/{chunk-EX3LRPZG-DLS6FBN1-CM3AIHWh.js → chunk-EX3LRPZG-DLS6FBN1-EqsJFk0Y.js} +1 -1
- package/dist/assets/{chunk-FWX5IMBZ-DHLSFw1H-C7tdhIeD.js → chunk-FWX5IMBZ-DHLSFw1H-BHANfZHz.js} +2 -2
- package/dist/assets/{chunk-HOUHSVGY-Bhlt8hXJ-Cz_lCkPW.js → chunk-HOUHSVGY-Bhlt8hXJ-s4l_kLKK.js} +1 -1
- package/dist/assets/{chunk-ICXQ74PX-DwgHBX_g-BiaXcLCM.js → chunk-ICXQ74PX-DwgHBX_g-Cwv0aNBS.js} +1 -1
- package/dist/assets/{chunk-MOJQB5TN-CMZRaeqt-DynKnij8.js → chunk-MOJQB5TN-CMZRaeqt-Dv_IeGiN.js} +1 -1
- package/dist/assets/{chunk-OGEWGWER-8Qy4a8b5-BHp0IyoO.js → chunk-OGEWGWER-8Qy4a8b5-C_128Gk1.js} +1 -1
- package/dist/assets/{chunk-PUDLZKDR-DcrWQRYh-CgXHo6E0.js → chunk-PUDLZKDR-DcrWQRYh-SIXDzNuv.js} +1 -1
- package/dist/assets/{chunk-Q4XR5HBZ-ZXVGkG8Z-BEB0ANC3.js → chunk-Q4XR5HBZ-ZXVGkG8Z-C6CR9UX-.js} +1 -1
- package/dist/assets/{chunk-V7JOEXUC-DmGdheTX-Ct5XDuM1.js → chunk-V7JOEXUC-DmGdheTX-N9ND9AI0.js} +1 -1
- package/dist/assets/{chunk-VAUOI2AC-CS9QJ4yz-BkjFKKFo.js → chunk-VAUOI2AC-CS9QJ4yz-C8yjyNAb.js} +1 -1
- package/dist/assets/{chunk-VR4S4FIN-C6a91eNY-Cvguj9hE.js → chunk-VR4S4FIN-C6a91eNY-BaQ02h-J.js} +1 -1
- package/dist/assets/{chunk-WYO6CB5R-BlzOfotS-Dm5-lJPj.js → chunk-WYO6CB5R-BlzOfotS-BuYOtRW-.js} +1 -1
- package/dist/assets/{chunk-ZGVPDNZ5-BYwxNFTK-hzHMTH8r.js → chunk-ZGVPDNZ5-BYwxNFTK-BCkOnDkO.js} +1 -1
- package/dist/assets/classDiagram-OUVF2IWQ-BYlRi6YQ-DbagYY9o.js +1 -0
- package/dist/assets/classDiagram-v2-EOCWNBFH-BCHaYHxj-DbagYY9o.js +1 -0
- package/dist/assets/cynefin-VYW2F7L2-CGYr2F6h-UB5w6uC7.js +1 -0
- package/dist/assets/{cynefinDiagram-TSTJHNR4-DHA9iPo--9Vfx1RL5.js → cynefinDiagram-TSTJHNR4-DHA9iPo--D5F9W2o2.js} +1 -1
- package/dist/assets/{dagre-VKFMJZFB--oJKqXBZ-DVMiDNUa.js → dagre-VKFMJZFB--oJKqXBZ-88RUfNlU.js} +1 -1
- package/dist/assets/{diagram-FQU43EPY-CcJCB9bG-Dw8uXi5J.js → diagram-FQU43EPY-CcJCB9bG-jbLemh0e.js} +1 -1
- package/dist/assets/{diagram-G47NLZAW-C_o-WGG1-BiirKYwU.js → diagram-G47NLZAW-C_o-WGG1-xlNez2pC.js} +1 -1
- package/dist/assets/{diagram-NH7WQ7WH-CXJCYvY--CxJ-W_AV.js → diagram-NH7WQ7WH-CXJCYvY--C3CqFZaV.js} +1 -1
- package/dist/assets/{diagram-OA4YK3LP-BMzeJ87A-C66U-WpA.js → diagram-OA4YK3LP-BMzeJ87A-sCnQ29Ip.js} +1 -1
- package/dist/assets/{diagram-WEI45ONY-D_93NKqo-C5wgOP3m.js → diagram-WEI45ONY-D_93NKqo-DSd8lYcR.js} +1 -1
- package/dist/assets/{ebnfDiagram-CCIWWBDH-CVai1Ii9-XxRZOagG.js → ebnfDiagram-CCIWWBDH-CVai1Ii9-DL5PrRfS.js} +1 -1
- package/dist/assets/{erDiagram-Q63AITRT-CiABnA0s-C3SFUgS_.js → erDiagram-Q63AITRT-CiABnA0s-rpAyy6Aw.js} +1 -1
- package/dist/assets/eventmodeling-45OFAUF4-CWmqhm3a-DkBecorT.js +1 -0
- package/dist/assets/flowDiagram-23GEKE2U-DKSB5AiY-CXJxjn_t.js +1 -0
- package/dist/assets/{ganttDiagram-NO4QXBWP-DQZvdFo1-CZAkzl2_.js → ganttDiagram-NO4QXBWP-DQZvdFo1-C7F2aPpB.js} +1 -1
- package/dist/assets/gitGraph-TEB2WS4Q-BR9qPwdN-Du_E4hcC.js +1 -0
- package/dist/assets/{gitGraphDiagram-IHSO6WYX-C2ovBouh-CXNXoPSF.js → gitGraphDiagram-IHSO6WYX-C2ovBouh-C_2xuNvU.js} +1 -1
- package/dist/assets/index-D3DaJte4.css +1 -0
- package/dist/assets/index-DKufSRgm.js +617 -0
- package/dist/assets/info-DKCQHKI2-Dplx5kMp-BUCYnVUr.js +1 -0
- package/dist/assets/{infoDiagram-FWYZ7A6U-7UnoB5AP-GVMnr0TB.js → infoDiagram-FWYZ7A6U-7UnoB5AP-BDJn_gZ2.js} +1 -1
- package/dist/assets/{ishikawaDiagram-FXEZZL3T-ByUDM_N2-BmKgPmF9.js → ishikawaDiagram-FXEZZL3T-ByUDM_N2-84Ld9x5-.js} +1 -1
- package/dist/assets/{journeyDiagram-5HDEW3XC-c5xIah9o-BOoOb2Up.js → journeyDiagram-5HDEW3XC-c5xIah9o-BKkQO5ur.js} +1 -1
- package/dist/assets/{kanban-definition-HUTT4EX6-Cnt6loYD-CV1DfLpD.js → kanban-definition-HUTT4EX6-Cnt6loYD-BsG3BxVw.js} +1 -1
- package/dist/assets/{lib-BfLKLNAW.js → lib-zuZr9fkI.js} +1 -1
- package/dist/assets/{line-D7ziSjKi-qjBgm7Wg.js → line-D7ziSjKi-C5w4gPUQ.js} +1 -1
- package/dist/assets/{marp-BPQ2jYa4.js → marp-FzmYUP9b.js} +1 -1
- package/dist/assets/{mermaid-parser.core-DxEa8E3F-bFNCoAMV.js → mermaid-parser.core-DxEa8E3F-DjFZ0x4N.js} +2 -2
- package/dist/assets/{mermaid.core-V0OYwIz3-bDvzm251.js → mermaid.core-V0OYwIz3-1iJt6Fje.js} +3 -3
- package/dist/assets/{mindmap-definition-LN4V7U3C-CexN3O6L-BRp2Mb--.js → mindmap-definition-LN4V7U3C-CexN3O6L-DP9kmAzP.js} +1 -1
- package/dist/assets/packet-7NZHBO7P-CQI3flND-ewcXElnr.js +1 -0
- package/dist/assets/{pegDiagram-2B236MQR-BiM4G0cW--kWTOoR2.js → pegDiagram-2B236MQR-BiM4G0cW-BlnrD6NT.js} +1 -1
- package/dist/assets/pie-RZYD4A2V-CnmKE2Wa-CCyUxET4.js +1 -0
- package/dist/assets/{pieDiagram-ENE6RG2P-B_fBBS-2-BOFV4ngY.js → pieDiagram-ENE6RG2P-B_fBBS-2-BKuZUm01.js} +1 -1
- package/dist/assets/{quadrantDiagram-ABIIQ3AL-BigGVxCR-jyaO71dB.js → quadrantDiagram-ABIIQ3AL-BigGVxCR-DkfLj9Xh.js} +1 -1
- package/dist/assets/radar-I7S5WNFK-us6x-Z9R-DrKUISae.js +1 -0
- package/dist/assets/railroad-3IZDKUUU-BKdz6pdN-CFKVwjnX.js +1 -0
- package/dist/assets/railroad-abnf-AHOZXSZD-m_CSSIfd-jmHmUIiI.js +1 -0
- package/dist/assets/railroad-ebnf-EBAXGLYW-Nu3nAG7C-xTNqkilU.js +1 -0
- package/dist/assets/railroad-peg-LSFZ7HO6--Y3mD4R_-BTE-hywQ.js +1 -0
- package/dist/assets/{railroadDiagram-RFXS5EU6--lNIlG64-D8DkHqgs.js → railroadDiagram-RFXS5EU6--lNIlG64-BEkxjf_N.js} +1 -1
- package/dist/assets/{requirementDiagram-TGXJPOKE-D-B0Y5Wu-d5Jr6_2i.js → requirementDiagram-TGXJPOKE-D-B0Y5Wu-CJBVdYgb.js} +1 -1
- package/dist/assets/{sankeyDiagram-HTMAVEWB-DlutaXDv-VGEBaos9.js → sankeyDiagram-HTMAVEWB-DlutaXDv-CFbAwuwF.js} +1 -1
- package/dist/assets/{sequenceDiagram-DBY2YBRQ-BOLgtggH-hpEall7s.js → sequenceDiagram-DBY2YBRQ-BOLgtggH-DLEygrdq.js} +1 -1
- package/dist/assets/{stateDiagram-2N3HPSRC-jf_dXUEU-CDm7zgbz.js → stateDiagram-2N3HPSRC-jf_dXUEU-Cd5481-w.js} +1 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-CuzgCL2v-BP3lZGTn.js +1 -0
- package/dist/assets/{swimlanes-5IMT3BWC-CYjtALQH-Bn3Yx5s4.js → swimlanes-5IMT3BWC-CYjtALQH-Cp0_qORx.js} +1 -1
- package/dist/assets/swimlanesDiagram-G3AALYLV-3bmSBtKs-BqvZyp1I.js +8 -0
- package/dist/assets/{timeline-definition-FHXFAJF6-DYJ4oUm8-CtWavdRU.js → timeline-definition-FHXFAJF6-DYJ4oUm8-BR9TSleK.js} +1 -1
- package/dist/assets/treeView-QDETBFTQ-D27HWG6Q-DBPuc9zP.js +1 -0
- package/dist/assets/treemap-6X3UGDF4-CmGIU3H5-opVOIgkg.js +1 -0
- package/dist/assets/{vennDiagram-L72KCM5P-CdKAHoek-D-tozz5H.js → vennDiagram-L72KCM5P-CdKAHoek-B1GbPicN.js} +1 -1
- package/dist/assets/wardley-OPB4EBWU-CbHWsb2k-oryHi9la.js +1 -0
- package/dist/assets/{wardleyDiagram-EHGQE667-BF6c4_CW-Bd3huzRu.js → wardleyDiagram-EHGQE667-BF6c4_CW-CS4IOktG.js} +1 -1
- package/dist/assets/{xychartDiagram-FW5EYKEG-CGiKngj7-PiOfLnci.js → xychartDiagram-FW5EYKEG-CGiKngj7-WkBklR-a.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +3 -3
- package/server/backends/byte-range.ts +18 -8
- package/server/backends/collections.ts +7 -1
- package/server/backends/files.ts +42 -23
- package/server/backends/html.ts +2 -2
- package/server/backends/image-gen.ts +13 -2
- package/server/backends/mutateStatus.ts +9 -0
- package/server/backends/remoteHost/attachment-path.ts +4 -1
- package/server/backends/remoteHost/attachmentStore.spec.ts +14 -1
- package/server/backends/remoteHost/attachmentStore.ts +3 -1
- package/server/backends/remoteHost/getFeed.spec.ts +2 -1
- package/server/backends/remoteHost/handlers.spec.ts +70 -4
- package/server/backends/remoteHost/handlers.ts +31 -8
- package/server/backends/remoteHost/index.ts +1 -0
- package/server/backends/remoteHost/ingestAttachments.spec.ts +11 -7
- package/server/backends/remoteHost/ingestAttachments.ts +29 -19
- package/server/backends/remoteHost/skills.ts +4 -2
- package/server/backends/remoteHost/stagedStorageIds.ts +5 -2
- package/server/backends/remoteHost/terminalInput.spec.ts +41 -0
- package/server/backends/remoteHost/terminalInput.ts +7 -1
- package/server/backends/remoteHost/terminalScreen.ts +6 -2
- package/server/backends/streamFile.ts +26 -0
- package/server/backends/thumbnailStore.ts +1 -1
- package/server/backends/worklog.ts +5 -5
- package/server/config/app-config.ts +15 -0
- package/server/config/config-routes.ts +7 -0
- package/server/config/config-schema.ts +6 -1
- package/server/config/header-config.ts +11 -4
- package/server/config/header-context.ts +10 -3
- package/server/files/files-browse.ts +1 -49
- package/server/files/pathContainment.ts +79 -0
- package/server/git/pr-for-branch.ts +10 -5
- package/server/git/spawn-collect.ts +18 -9
- package/server/git/worktree-diff.ts +16 -3
- package/server/git/worktree-pr.ts +17 -1
- package/server/git/worktrees.ts +21 -5
- package/server/index.ts +19 -4
- package/server/infra/plugins-registry.ts +6 -2
- package/server/infra/tmux-routes.ts +17 -1
- package/server/routes/app-routes.ts +7 -4
- package/server/routes/plugin-narration.ts +4 -2
- package/dist/assets/architecture-TIHT7OUA-0NsD2xW1-9X3_cRqd.js +0 -1
- package/dist/assets/channel-7wUqSdoX-vfZkO_uL.js +0 -1
- package/dist/assets/classDiagram-OUVF2IWQ-BYlRi6YQ-CeGPKz7I.js +0 -1
- package/dist/assets/classDiagram-v2-EOCWNBFH-BCHaYHxj-CeGPKz7I.js +0 -1
- package/dist/assets/cynefin-VYW2F7L2-CGYr2F6h-0kc9OM8m.js +0 -1
- package/dist/assets/eventmodeling-45OFAUF4-CWmqhm3a-CjeZJqe8.js +0 -1
- package/dist/assets/flowDiagram-23GEKE2U-DKSB5AiY-B6tkM4yH.js +0 -1
- package/dist/assets/gitGraph-TEB2WS4Q-BR9qPwdN-BnYaIjat.js +0 -1
- package/dist/assets/index-C-I-KZAf.js +0 -617
- package/dist/assets/index-Cue6y3NP.css +0 -1
- package/dist/assets/info-DKCQHKI2-Dplx5kMp-DYTQnnkg.js +0 -1
- package/dist/assets/packet-7NZHBO7P-CQI3flND-TB9IxewR.js +0 -1
- package/dist/assets/pie-RZYD4A2V-CnmKE2Wa-DfFUYw37.js +0 -1
- package/dist/assets/radar-I7S5WNFK-us6x-Z9R-CfuJM-7B.js +0 -1
- package/dist/assets/railroad-3IZDKUUU-BKdz6pdN-d15bc0qE.js +0 -1
- package/dist/assets/railroad-abnf-AHOZXSZD-m_CSSIfd-CyjJaPJJ.js +0 -1
- package/dist/assets/railroad-ebnf-EBAXGLYW-Nu3nAG7C-CWODrAAC.js +0 -1
- package/dist/assets/railroad-peg-LSFZ7HO6--Y3mD4R_-uM99LygW.js +0 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-CuzgCL2v-BJkHeYpz.js +0 -1
- package/dist/assets/swimlanesDiagram-G3AALYLV-3bmSBtKs-Dq42c4u7.js +0 -8
- package/dist/assets/treeView-QDETBFTQ-D27HWG6Q-DVQpaIF5.js +0 -1
- package/dist/assets/treemap-6X3UGDF4-CmGIU3H5-DpXrJ_Vl.js +0 -1
- package/dist/assets/wardley-OPB4EBWU-CbHWsb2k-DS8lznjT.js +0 -1
package/README.md
CHANGED
|
@@ -410,13 +410,16 @@ The Settings modal (⚙) persists per-user UI choices to `~/.mulmoterminal/confi
|
|
|
410
410
|
| `pushEnabled` | `true` to send a **Web Push** to your registered devices when a background task finishes. Off by default; only sends while the **RemoteHost** channel is connected (see below). |
|
|
411
411
|
| `worklogEnabled` | `true` to run the built-in **dev worklog** batch (see below). Off by default (each run spawns an LLM session, so it costs tokens). |
|
|
412
412
|
| `worklogIntervalHours` | Worklog cadence in hours (default `6`, clamped to `1`–`168`). |
|
|
413
|
+
| `terminalSubmit` | Which bytes Claude reads as **submit** vs **newline**: `"cr"` (default — Enter submits, Shift+Enter makes a newline) or `"esc-cr"` (for a Claude Code rebound the other way). Applies to the keyboard **and** the phone remote-view submit, for **Claude sessions only** (shell/codex keep plain Enter). See the [Configuration guide](https://receptron.github.io/mulmoterminal/guide/en/config.html#terminal-submit). |
|
|
413
414
|
|
|
414
415
|
#### Header buttons
|
|
415
416
|
|
|
416
417
|
Each terminal header shows configurable **action buttons**. Omitting `buttons` (globally or per-dir)
|
|
417
|
-
keeps the built-in
|
|
418
|
-
|
|
419
|
-
|
|
418
|
+
keeps the built-in **starter set**: a file-path picker (📎), an OS file-manager reveal (📂), an in-app
|
|
419
|
+
file explorer (📁), a new terminal here (🖥), this branch's PR (🔗, git repos, only when a PR exists),
|
|
420
|
+
and open-on-GitHub (🌐, git repos). Setting `buttons` (at either level) **replaces the whole default
|
|
421
|
+
set** with your list (it is not merged on top), so listing your own — even a **shorter** one — is how
|
|
422
|
+
you drop, reorder, or swap them.
|
|
420
423
|
A button has an `id`, `label`, and a `run` of `"shell"` (run a command), `"input"` (send text to the
|
|
421
424
|
agent), or `"open"`. An `open` button targets one of `url` / `reveal` (OS file manager) / `files`
|
|
422
425
|
(in-app explorer) / `view` (a built-in overlay) / `terminal` (a dir → a new cell running `$SHELL`,
|
|
@@ -793,7 +796,9 @@ Favorited collections get their own toolbar buttons.
|
|
|
793
796
|
- **Themes** — four terminal palettes (midnight / nord / daylight / solarized), your pick
|
|
794
797
|
remembered; a project's `.mulmoterminal.json` can override per directory.
|
|
795
798
|
- **Editing niceties** — **Shift+Enter** inserts a newline in the prompt, and on macOS
|
|
796
|
-
**Option** is treated as Meta so Claude's Alt-key bindings work.
|
|
799
|
+
**Option** is treated as Meta so Claude's Alt-key bindings work. If your Claude Code is
|
|
800
|
+
rebound so Enter and Shift+Enter behave backwards, flip them with
|
|
801
|
+
[`terminalSubmit`](https://receptron.github.io/mulmoterminal/guide/en/config.html#terminal-submit).
|
|
797
802
|
|
|
798
803
|
---
|
|
799
804
|
|
package/bin/mulmoterminal.js
CHANGED
|
@@ -7,13 +7,14 @@
|
|
|
7
7
|
|
|
8
8
|
import { execSync, spawn } from "node:child_process";
|
|
9
9
|
import { existsSync, statSync } from "node:fs";
|
|
10
|
-
import { get as httpGet } from "node:http";
|
|
11
10
|
import { createRequire } from "node:module";
|
|
12
11
|
import { createServer } from "node:net";
|
|
13
12
|
import { dirname, join, resolve } from "node:path";
|
|
14
13
|
import { createInterface } from "node:readline";
|
|
15
14
|
import { fileURLToPath } from "node:url";
|
|
16
15
|
import { computeUpdateNotice, isUpdateCheckDisabled } from "./update-check.js";
|
|
16
|
+
import { detectNpxCacheDir, npxCacheHintLines } from "./npx-cache-hint.js";
|
|
17
|
+
import { waitUntilReady } from "./wait-ready.js";
|
|
17
18
|
import {
|
|
18
19
|
chooseCwd,
|
|
19
20
|
parsePortArg,
|
|
@@ -30,10 +31,12 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
30
31
|
const PKG_DIR = join(__dirname, "..");
|
|
31
32
|
const SERVER_ENTRY = join(PKG_DIR, "server", "index.ts");
|
|
32
33
|
const DEFAULT_PORT = 34567;
|
|
33
|
-
const READY_TIMEOUT_MS = 15_000;
|
|
34
34
|
// Server exit code meaning "port taken at bind time" — keep in sync with
|
|
35
35
|
// server/index.ts (PORT_IN_USE_EXIT_CODE).
|
|
36
36
|
const PORT_IN_USE_EXIT_CODE = 75;
|
|
37
|
+
// Only the end of stderr matters for the crash diagnosis; a long-lived server can log
|
|
38
|
+
// arbitrarily much before dying, so the tail is bounded.
|
|
39
|
+
const STDERR_TAIL_MAX_BYTES = 64 * 1024;
|
|
37
40
|
|
|
38
41
|
// Single source of truth: read the version from the shipped package.json so
|
|
39
42
|
// `--version` never drifts from the published version.
|
|
@@ -170,36 +173,6 @@ function isPortFree(port) {
|
|
|
170
173
|
});
|
|
171
174
|
}
|
|
172
175
|
|
|
173
|
-
// Poll the server until it answers, then call onReady; give up after the timeout
|
|
174
|
-
// so the launcher never hangs on a crash loop. Returns a cancel function — a
|
|
175
|
-
// raced/abandoned attempt stops polling so it can't fire a stale banner.
|
|
176
|
-
function waitUntilReady(port, onReady) {
|
|
177
|
-
const startedAt = Date.now();
|
|
178
|
-
let timer = null;
|
|
179
|
-
let cancelled = false;
|
|
180
|
-
const attempt = () => {
|
|
181
|
-
if (cancelled) return;
|
|
182
|
-
const req = httpGet({ host: "127.0.0.1", port, path: "/", timeout: 1000 }, (res) => {
|
|
183
|
-
res.resume();
|
|
184
|
-
if (!cancelled) onReady();
|
|
185
|
-
});
|
|
186
|
-
req.on("error", retry);
|
|
187
|
-
req.on("timeout", () => {
|
|
188
|
-
req.destroy();
|
|
189
|
-
retry();
|
|
190
|
-
});
|
|
191
|
-
};
|
|
192
|
-
const retry = () => {
|
|
193
|
-
if (cancelled || Date.now() - startedAt > READY_TIMEOUT_MS) return;
|
|
194
|
-
timer = setTimeout(attempt, 300);
|
|
195
|
-
};
|
|
196
|
-
attempt();
|
|
197
|
-
return () => {
|
|
198
|
-
cancelled = true;
|
|
199
|
-
if (timer) clearTimeout(timer);
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
|
|
203
176
|
function printReadyBanner(url) {
|
|
204
177
|
const bar = "\x1b[32m" + "─".repeat(48) + "\x1b[0m";
|
|
205
178
|
console.log(`\n${bar}`);
|
|
@@ -272,10 +245,18 @@ async function choosePort(requested, explicit) {
|
|
|
272
245
|
function runServer(port, noOpen, cwd, onChild) {
|
|
273
246
|
return new Promise((resolveExit) => {
|
|
274
247
|
log(`Starting MulmoTerminal on port ${port}...`);
|
|
248
|
+
// stderr is piped (and passed through) so a fatal boot error can be inspected once the
|
|
249
|
+
// child closes — a half-unpacked npx cache entry crashes here with ERR_MODULE_NOT_FOUND,
|
|
250
|
+
// and without reading stderr the launcher cannot tell that from a real bug.
|
|
275
251
|
const server = spawn(process.execPath, ["--import", "tsx", SERVER_ENTRY], {
|
|
276
252
|
cwd: PKG_DIR,
|
|
277
253
|
env: { ...process.env, NODE_ENV: "production", PORT: String(port), CLAUDE_CWD: cwd },
|
|
278
|
-
stdio: "inherit",
|
|
254
|
+
stdio: ["inherit", "inherit", "pipe"],
|
|
255
|
+
});
|
|
256
|
+
let stderrTail = "";
|
|
257
|
+
server.stderr.on("data", (chunk) => {
|
|
258
|
+
process.stderr.write(chunk);
|
|
259
|
+
stderrTail = (stderrTail + chunk.toString()).slice(-STDERR_TAIL_MAX_BYTES);
|
|
279
260
|
});
|
|
280
261
|
onChild(server);
|
|
281
262
|
|
|
@@ -292,7 +273,10 @@ function runServer(port, noOpen, cwd, onChild) {
|
|
|
292
273
|
}
|
|
293
274
|
});
|
|
294
275
|
|
|
295
|
-
|
|
276
|
+
// `close`, not `exit`: it fires only once the piped stderr has fully drained, so the
|
|
277
|
+
// whole crash output — including a trailing `_npx/<hash>` line that can arrive after
|
|
278
|
+
// `exit` — is in `stderrTail` before we inspect it.
|
|
279
|
+
server.on("close", (code) => {
|
|
296
280
|
cancelReady();
|
|
297
281
|
// Exit code 75 means this child failed to bind (EADDRINUSE) and never
|
|
298
282
|
// served — always retriable, regardless of what a probe to the port saw
|
|
@@ -301,6 +285,10 @@ function runServer(port, noOpen, cwd, onChild) {
|
|
|
301
285
|
resolveExit();
|
|
302
286
|
return;
|
|
303
287
|
}
|
|
288
|
+
if (code !== 0) {
|
|
289
|
+
const cacheDir = detectNpxCacheDir(stderrTail);
|
|
290
|
+
if (cacheDir) npxCacheHintLines(cacheDir, process.platform).forEach((line) => error(line));
|
|
291
|
+
}
|
|
304
292
|
process.exit(code ?? 1);
|
|
305
293
|
});
|
|
306
294
|
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Turning a confusing crash into a one-line fix. When an `npx mulmoterminal` install is
|
|
2
|
+
// aborted mid-unpack (npm's npx lock race — `ECOMPROMISED: Lock compromised`), the next run
|
|
3
|
+
// reuses the half-unpacked `~/.npm/_npx/<hash>/node_modules` and dies at boot with
|
|
4
|
+
// ERR_MODULE_NOT_FOUND for some transitive dependency. That reads as a broken package, but
|
|
5
|
+
// the published tarball is fine — the local npx cache entry is corrupted, and removing it
|
|
6
|
+
// is the whole fix. Nothing here reads the filesystem; the caller feeds it stderr text.
|
|
7
|
+
|
|
8
|
+
const MISSING_MODULE = /ERR_MODULE_NOT_FOUND|Cannot find (?:package|module)/;
|
|
9
|
+
|
|
10
|
+
// The cache entry itself: `<sep>_npx<sep><hash>` followed by another separator. Anchored on
|
|
11
|
+
// the literal `_npx` rather than on a leading `[^\s'"]*` wildcard — that prefix can also match
|
|
12
|
+
// separators, which makes the match ambiguous and the regex backtrack super-linearly on a long
|
|
13
|
+
// stack trace. The directory part before it is recovered by scanning, not by matching.
|
|
14
|
+
const NPX_ENTRY = /[/\\]_npx[/\\][0-9a-f]+(?=[/\\])/;
|
|
15
|
+
|
|
16
|
+
// What ends the path token a stack trace embeds: whitespace, or the quotes Node wraps
|
|
17
|
+
// specifiers in.
|
|
18
|
+
const PATH_BOUNDARIES = [" ", "\t", "\n", "\r", "'", '"'];
|
|
19
|
+
|
|
20
|
+
// Where the path token containing the entry begins: just past the last boundary before it.
|
|
21
|
+
const pathStartIndex = (textBefore) => Math.max(-1, ...PATH_BOUNDARIES.map((ch) => textBefore.lastIndexOf(ch))) + 1;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The corrupted npx cache entry a failed boot points at, or null.
|
|
25
|
+
* Fires only when BOTH signals are present — a module-resolution error and a path inside
|
|
26
|
+
* an `_npx/<hash>` cache entry — so an ordinary crash, or a resolution error in a normal
|
|
27
|
+
* install, never triggers the hint.
|
|
28
|
+
*/
|
|
29
|
+
export function detectNpxCacheDir(stderrText) {
|
|
30
|
+
if (typeof stderrText !== "string" || !MISSING_MODULE.test(stderrText)) return null;
|
|
31
|
+
const match = NPX_ENTRY.exec(stderrText);
|
|
32
|
+
if (!match) return null;
|
|
33
|
+
return stderrText.slice(pathStartIndex(stderrText.slice(0, match.index)), match.index + match[0].length);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Shell-quote the cache dir so a path containing quote characters (a home dir with an
|
|
37
|
+
// apostrophe on POSIX is the realistic case) produces a command that is still valid — and
|
|
38
|
+
// safe — to copy-paste, rather than one that breaks or runs something unintended.
|
|
39
|
+
|
|
40
|
+
// POSIX single-quoting: wrap in '…' and turn every embedded ' into '\'' (close, escaped
|
|
41
|
+
// quote, reopen). Nothing inside single quotes is special, so this is injection-proof.
|
|
42
|
+
const posixQuote = (s) => `'${s.replace(/'/g, "'\\''")}'`;
|
|
43
|
+
|
|
44
|
+
// PowerShell single-quoting: '…' with each ' doubled. Single quotes suppress $-interpolation
|
|
45
|
+
// (a double-quoted path containing $ would otherwise expand a variable), so this is safe.
|
|
46
|
+
const powershellQuote = (s) => `'${s.replace(/'/g, "''")}'`;
|
|
47
|
+
|
|
48
|
+
// cmd has no way to escape a " inside "…", but " is an illegal character in a Windows path,
|
|
49
|
+
// so double-quoting a real cache dir is safe; it protects the spaces that do occur.
|
|
50
|
+
const cmdQuote = (s) => `"${s}"`;
|
|
51
|
+
|
|
52
|
+
// The delete command differs per platform, and a wrong one is worse than none: `rm -rf` is not
|
|
53
|
+
// a command on Windows, and cmd's `rmdir /s /q` is a syntax error in PowerShell (where `rmdir`
|
|
54
|
+
// aliases Remove-Item), so Windows gets both of its shells spelled out.
|
|
55
|
+
const removalCommands = (cacheDir, platform) =>
|
|
56
|
+
platform === "win32"
|
|
57
|
+
? [` cmd: rmdir /s /q ${cmdQuote(cacheDir)}`, ` PowerShell: Remove-Item -Recurse -Force ${powershellQuote(cacheDir)}`]
|
|
58
|
+
: [` rm -rf ${posixQuote(cacheDir)}`];
|
|
59
|
+
|
|
60
|
+
/** The recovery instructions shown under the crash, one string per line. */
|
|
61
|
+
export function npxCacheHintLines(cacheDir, platform) {
|
|
62
|
+
return [
|
|
63
|
+
"This looks like a corrupted npx cache, not a MulmoTerminal bug.",
|
|
64
|
+
'An interrupted `npx` install (npm\'s "Lock compromised" race) can leave this entry half-unpacked:',
|
|
65
|
+
` ${cacheDir}`,
|
|
66
|
+
"Remove it and run the same command again:",
|
|
67
|
+
...removalCommands(cacheDir, platform),
|
|
68
|
+
];
|
|
69
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Only the surface probeOnce actually touches — narrower than http.ClientRequest /
|
|
2
|
+
// IncomingMessage on purpose, so a test fake and the real node:http both satisfy it.
|
|
3
|
+
export interface ProbeRequest {
|
|
4
|
+
on(event: "error" | "timeout", listener: (...args: unknown[]) => void): unknown;
|
|
5
|
+
destroy(): void;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface ProbeResponse {
|
|
9
|
+
resume(): void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type ProbeGet = (opts: { host: string; port: number; path: string; timeout: number }, cb: (res: ProbeResponse) => void) => ProbeRequest;
|
|
13
|
+
|
|
14
|
+
export declare function probeOnce(get: ProbeGet, port: number, timeoutMs?: number): Promise<"ready" | "retry">;
|
|
15
|
+
|
|
16
|
+
export declare function waitUntilReady(
|
|
17
|
+
port: number,
|
|
18
|
+
onReady: () => void,
|
|
19
|
+
deps?: {
|
|
20
|
+
get?: ProbeGet;
|
|
21
|
+
now?: () => number;
|
|
22
|
+
timeoutMs?: number;
|
|
23
|
+
intervalMs?: number;
|
|
24
|
+
readyTimeoutMs?: number;
|
|
25
|
+
},
|
|
26
|
+
): () => void;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Polling the launched server until it answers `/`, then firing onReady exactly once.
|
|
2
|
+
// Split out of mulmoterminal.js so the retry state machine is testable without a socket.
|
|
3
|
+
|
|
4
|
+
import { get as httpGet } from "node:http";
|
|
5
|
+
|
|
6
|
+
const PROBE_TIMEOUT_MS = 1000;
|
|
7
|
+
const RETRY_INTERVAL_MS = 300;
|
|
8
|
+
const DEFAULT_READY_TIMEOUT_MS = 15_000;
|
|
9
|
+
|
|
10
|
+
// One probe of the server: resolves "ready" if it answers, else "retry". It NEVER
|
|
11
|
+
// rejects, and yields exactly ONE outcome per call — the crucial guarantee. Destroying a
|
|
12
|
+
// timed-out request itself emits an 'error', so a single timeout would otherwise trigger
|
|
13
|
+
// both the timeout handler AND the error handler; each schedules a retry, the poll loop
|
|
14
|
+
// forks in two, and every fork fires onReady when the server finally answers — printing
|
|
15
|
+
// the banner and opening the browser several times. The `settled` latch collapses each
|
|
16
|
+
// request to one outcome.
|
|
17
|
+
export function probeOnce(get, port, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
18
|
+
return new Promise((resolve) => {
|
|
19
|
+
let settled = false;
|
|
20
|
+
const done = (outcome) => {
|
|
21
|
+
if (settled) return;
|
|
22
|
+
settled = true;
|
|
23
|
+
resolve(outcome);
|
|
24
|
+
};
|
|
25
|
+
const req = get({ host: "127.0.0.1", port, path: "/", timeout: timeoutMs }, (res) => {
|
|
26
|
+
res.resume();
|
|
27
|
+
done("ready");
|
|
28
|
+
});
|
|
29
|
+
req.on("error", () => done("retry"));
|
|
30
|
+
req.on("timeout", () => {
|
|
31
|
+
req.destroy();
|
|
32
|
+
done("retry");
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Poll until the server answers, then call onReady once; give up after readyTimeoutMs so
|
|
38
|
+
// the launcher never hangs on a crash loop. Returns a cancel function — a raced/abandoned
|
|
39
|
+
// attempt stops polling so it can't fire a stale banner. `deps` is injectable for tests.
|
|
40
|
+
export function waitUntilReady(port, onReady, deps = {}) {
|
|
41
|
+
const { get = httpGet, now = Date.now, timeoutMs = PROBE_TIMEOUT_MS, intervalMs = RETRY_INTERVAL_MS, readyTimeoutMs = DEFAULT_READY_TIMEOUT_MS } = deps;
|
|
42
|
+
const startedAt = now();
|
|
43
|
+
let cancelled = false;
|
|
44
|
+
let timer = null;
|
|
45
|
+
const loop = async () => {
|
|
46
|
+
if (cancelled) return;
|
|
47
|
+
const outcome = await probeOnce(get, port, timeoutMs);
|
|
48
|
+
if (cancelled) return;
|
|
49
|
+
if (outcome === "ready") {
|
|
50
|
+
onReady();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (now() - startedAt > readyTimeoutMs) return;
|
|
54
|
+
timer = setTimeout(loop, intervalMs);
|
|
55
|
+
};
|
|
56
|
+
loop();
|
|
57
|
+
return () => {
|
|
58
|
+
cancelled = true;
|
|
59
|
+
if (timer) clearTimeout(timer);
|
|
60
|
+
};
|
|
61
|
+
}
|
package/common/modelPresets.ts
CHANGED
|
@@ -197,7 +197,7 @@ export const MODEL_PRESETS: readonly ModelPreset[] = [
|
|
|
197
197
|
provider: "openrouter",
|
|
198
198
|
id: "nvidia/nemotron-3-ultra-550b-a55b",
|
|
199
199
|
label: "Nemotron 3 Ultra 550B",
|
|
200
|
-
contextLength: 512_288
|
|
200
|
+
contextLength: 524_288, // 512 KiB = 512 * 1024; was 512_288 (a typo)
|
|
201
201
|
pricePerMTok: { input: 0.6, output: 3.6 },
|
|
202
202
|
trials: measured(3, 3, 13),
|
|
203
203
|
},
|
|
@@ -300,14 +300,19 @@ export const MODEL_PRESETS: readonly ModelPreset[] = [
|
|
|
300
300
|
|
|
301
301
|
// Presets for one provider, plus whatever the user added to that provider's `models`.
|
|
302
302
|
// A user entry with the same id as a preset keeps the preset's measured numbers rather
|
|
303
|
-
// than appearing twice
|
|
303
|
+
// than appearing twice — and two user entries for the same id (even differing only in
|
|
304
|
+
// case) collapse to one, so a dropdown never shows the same model twice.
|
|
304
305
|
export function presetsForProvider(providerId: string, userModels: readonly string[] = []): ModelPreset[] {
|
|
305
306
|
const presets = MODEL_PRESETS.filter((preset) => preset.provider === providerId);
|
|
306
307
|
// Match `presetFor` (modelOption.ts), which compares ids case-insensitively: a user
|
|
307
308
|
// entry that only differs from a preset in case is the same model, not a new one.
|
|
308
|
-
const
|
|
309
|
-
const added: ModelPreset[] =
|
|
310
|
-
|
|
311
|
-
|
|
309
|
+
const seen = new Set(presets.map((preset) => preset.id.toLowerCase()));
|
|
310
|
+
const added: ModelPreset[] = [];
|
|
311
|
+
for (const id of userModels) {
|
|
312
|
+
const key = id.toLowerCase();
|
|
313
|
+
if (seen.has(key)) continue; // already a preset OR an earlier user entry
|
|
314
|
+
seen.add(key);
|
|
315
|
+
added.push({ provider: providerId, id, label: id, contextLength: 0, pricePerMTok: { input: 0, output: 0 }, trials: { status: "unmeasured" } });
|
|
316
|
+
}
|
|
312
317
|
return [...presets, ...added];
|
|
313
318
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// How the host's Claude Code / TUI maps received bytes to "submit this prompt" vs
|
|
2
|
+
// "insert a newline". The TUI decides from the bytes, and that mapping is environment-
|
|
3
|
+
// dependent (Claude Code lets a user rebind it), so the SAME fact has to reach two
|
|
4
|
+
// unrelated places: the browser's xterm key handler and the phone remote-view's submit.
|
|
5
|
+
// One value, consumed by both, keeps them from drifting apart.
|
|
6
|
+
//
|
|
7
|
+
// "cr" — the standard binding: CR (\r) submits, ESC+CR (\x1b\r, = Alt/Meta+Enter)
|
|
8
|
+
// makes a newline. This is MulmoTerminal's default and unchanged behaviour.
|
|
9
|
+
// "esc-cr" — the reversed binding some users configure: ESC+CR submits, CR makes a
|
|
10
|
+
// newline.
|
|
11
|
+
export const TERMINAL_SUBMIT_MODES = ["cr", "esc-cr"] as const;
|
|
12
|
+
export type TerminalSubmitMode = (typeof TERMINAL_SUBMIT_MODES)[number];
|
|
13
|
+
export const DEFAULT_TERMINAL_SUBMIT_MODE: TerminalSubmitMode = "cr";
|
|
14
|
+
|
|
15
|
+
export const isTerminalSubmitMode = (value: unknown): value is TerminalSubmitMode =>
|
|
16
|
+
typeof value === "string" && (TERMINAL_SUBMIT_MODES as readonly string[]).includes(value);
|
|
17
|
+
|
|
18
|
+
// CR on its own. ESC+CR is what a terminal emits for Alt/Meta+Enter.
|
|
19
|
+
const CR = "\r";
|
|
20
|
+
const ESC_CR = "\x1b\r";
|
|
21
|
+
|
|
22
|
+
// The byte(s) the host reads as "submit this prompt" in the given mode.
|
|
23
|
+
export const submitSequence = (mode: TerminalSubmitMode): string => (mode === "esc-cr" ? ESC_CR : CR);
|
|
24
|
+
// The byte(s) it reads as "insert a newline" — the other one.
|
|
25
|
+
export const newlineSequence = (mode: TerminalSubmitMode): string => (mode === "esc-cr" ? CR : ESC_CR);
|
|
26
|
+
|
|
27
|
+
// The submit byte(s) for a specific session. `terminalSubmit` describes the user's CLAUDE
|
|
28
|
+
// binding, so it applies ONLY to Claude sessions; every other agent (shell, codex, a
|
|
29
|
+
// one-shot command) keeps plain CR — a reversed setting must never rewrite a shell's Enter,
|
|
30
|
+
// where ESC+CR is Alt+Enter, not submit.
|
|
31
|
+
export const submitSequenceForAgent = (agent: string | undefined, mode: TerminalSubmitMode): string => (agent === "claude" ? submitSequence(mode) : CR);
|
|
32
|
+
|
|
33
|
+
// The structural shape of a keydown the override needs. A real DOM KeyboardEvent satisfies
|
|
34
|
+
// it, and so does a plain test object — no DOM dependency, so this stays testable and shared.
|
|
35
|
+
export interface EnterKeyEvent {
|
|
36
|
+
type: string;
|
|
37
|
+
key: string;
|
|
38
|
+
shiftKey: boolean;
|
|
39
|
+
altKey: boolean;
|
|
40
|
+
ctrlKey: boolean;
|
|
41
|
+
metaKey: boolean;
|
|
42
|
+
isComposing?: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// The bytes to emit for an Enter-family keydown (the caller then suppresses xterm's own
|
|
46
|
+
// \r and sends these instead), or null to let xterm handle the key natively.
|
|
47
|
+
//
|
|
48
|
+
// The SEMANTICS are the same in both modes — a bare Enter SUBMITS, Shift/Alt(Option)+Enter
|
|
49
|
+
// make a NEWLINE — only which bytes carry each meaning differs, because that is the
|
|
50
|
+
// environment fact `mode` encodes.
|
|
51
|
+
//
|
|
52
|
+
// In "cr" mode only Shift+Enter is overridden, exactly as before the setting existed: a
|
|
53
|
+
// bare Enter falls through to xterm's native \r (submit) and Option+Enter to
|
|
54
|
+
// macOptionIsMeta's ESC+CR (newline). Leaving the bare-Enter path native keeps IME
|
|
55
|
+
// candidate-confirm and the hottest key untouched in the default configuration.
|
|
56
|
+
export const enterKeyOverride = (mode: TerminalSubmitMode, e: EnterKeyEvent): string | null => {
|
|
57
|
+
if (e.type !== "keydown" || e.key !== "Enter") return null;
|
|
58
|
+
// IME composition (Japanese and other CJK input): this Enter confirms a candidate — it
|
|
59
|
+
// is never the user's submit/newline, so never intercept it.
|
|
60
|
+
if (e.isComposing) return null;
|
|
61
|
+
const bare = !e.shiftKey && !e.altKey && !e.ctrlKey && !e.metaKey;
|
|
62
|
+
const shiftOnly = e.shiftKey && !e.altKey && !e.ctrlKey && !e.metaKey;
|
|
63
|
+
const altHeld = e.altKey && !e.ctrlKey && !e.metaKey; // Option/Alt (with or without Shift)
|
|
64
|
+
if (mode === "cr") return shiftOnly ? newlineSequence(mode) : null;
|
|
65
|
+
// "esc-cr": a bare Enter must emit the ESC+CR that submits here; Shift/Alt+Enter must
|
|
66
|
+
// emit the CR that makes a newline (the Alt case also overrides macOptionIsMeta's ESC+CR).
|
|
67
|
+
if (bare) return submitSequence(mode);
|
|
68
|
+
if (shiftOnly || altHeld) return newlineSequence(mode);
|
|
69
|
+
return null;
|
|
70
|
+
};
|
|
@@ -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-DxEa8E3F-
|
|
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-DxEa8E3F-DjFZ0x4N.js";import"./chunk-WYO6CB5R-BlzOfotS-BuYOtRW-.js";import"./chunk-VAUOI2AC-CS9QJ4yz-C8yjyNAb.js";import{n as i,r as a,t as o}from"./chunk-MOJQB5TN-CMZRaeqt-Dv_IeGiN.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-DxEa8E3F-DjFZ0x4N.js";export{e as createArchitectureServices};
|