openmausbot 0.1.93 → 0.1.95

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 (192) hide show
  1. package/dist/assets/{DesktopViewer-CKNR3SEW.js → DesktopViewer-BwWpc4RK.js} +1 -1
  2. package/dist/assets/{abnfDiagram-O67JEVCF-Ckrm1I1X.js → abnfDiagram-O67JEVCF-jsQkIqTn.js} +1 -1
  3. package/dist/assets/{arc-BBdBuCgB.js → arc-7kei4Hsu.js} +1 -1
  4. package/dist/assets/{architectureDiagram-NJMV4G6O-CDdA3Hjs.js → architectureDiagram-NJMV4G6O-CRWDFDMq.js} +1 -1
  5. package/dist/assets/{blockDiagram-BEXU5L5S-AUrsx1YR.js → blockDiagram-BEXU5L5S-CTKW_IKs.js} +1 -1
  6. package/dist/assets/{c4Diagram-YGBWAQC7-BEqQ0Unh.js → c4Diagram-YGBWAQC7-BBTW7-6M.js} +1 -1
  7. package/dist/assets/channel-DS2NbT5x.js +1 -0
  8. package/dist/assets/{chunk-2Q5K7J3B-CEEuvd4C.js → chunk-2Q5K7J3B-M_Ox-MkD.js} +1 -1
  9. package/dist/assets/{chunk-3FUC2YCW-DyANCiHl.js → chunk-3FUC2YCW-D2YiN-Ud.js} +2 -2
  10. package/dist/assets/{chunk-5VM5RSS4-Dx_Slpw9.js → chunk-5VM5RSS4-BmVYJ0f8.js} +1 -1
  11. package/dist/assets/{chunk-GTNCS2PH-Dl7h7iiD.js → chunk-GTNCS2PH-BtiLVVnZ.js} +1 -1
  12. package/dist/assets/{chunk-GWA4HPMP-BLx07SPb.js → chunk-GWA4HPMP-DFYSb6J6.js} +1 -1
  13. package/dist/assets/{chunk-JWPE2WC7-BMxDj3n7.js → chunk-JWPE2WC7-mknKBzgV.js} +1 -1
  14. package/dist/assets/{chunk-NETBCI7D-CN66PT4H.js → chunk-NETBCI7D-DgSYGiOu.js} +1 -1
  15. package/dist/assets/{chunk-WEXAMYUT-6HwhsDKw.js → chunk-WEXAMYUT-C7ZvuguH.js} +1 -1
  16. package/dist/assets/{chunk-XXDRQBXY-gWnLt7SN.js → chunk-XXDRQBXY-CPqxINpA.js} +1 -1
  17. package/dist/assets/{classDiagram-v2-NBCMYWYE-CMS5l2ea.js → classDiagram-v2-NBCMYWYE-Cd6xfJaC.js} +1 -1
  18. package/dist/assets/{cose-bilkent-JH36ORCC-CV7sQwIG.js → cose-bilkent-JH36ORCC-BSXY5qRh.js} +1 -1
  19. package/dist/assets/{cynefin-OW5HDTMX-CSyl-7vl.js → cynefin-OW5HDTMX-D_lhEMj1.js} +1 -1
  20. package/dist/assets/{cynefinDiagram-VND7K2PF-BCLKL6az.js → cynefinDiagram-VND7K2PF-Da9FrFwk.js} +1 -1
  21. package/dist/assets/{dagre-6A5THRUB-DiaZs6S1.js → dagre-6A5THRUB-T77jMOXe.js} +1 -1
  22. package/dist/assets/{diagram-22UHCM2B-BeTtmt3I.js → diagram-22UHCM2B-Dkj96X5E.js} +1 -1
  23. package/dist/assets/{diagram-3UASUU5V-CsdBG-QQ.js → diagram-3UASUU5V-CMi9zlD_.js} +1 -1
  24. package/dist/assets/{diagram-ATOU4E4O-BczvdbR1.js → diagram-ATOU4E4O-CXK6YUfS.js} +1 -1
  25. package/dist/assets/{diagram-CDSNMT55-DHTTTMhi.js → diagram-CDSNMT55-BE7B1u9Q.js} +1 -1
  26. package/dist/assets/{diagram-MLGK6HIB-RTjHyd8z.js → diagram-MLGK6HIB-CCMQphKr.js} +1 -1
  27. package/dist/assets/{diagram-MPIPVDR6-BDenHEH_.js → diagram-MPIPVDR6-iHJ_-tGH.js} +1 -1
  28. package/dist/assets/{ebnfDiagram-ZINNZB2B-BSiRSbtL.js → ebnfDiagram-ZINNZB2B-DONsCWQ7.js} +1 -1
  29. package/dist/assets/{elk-276RUBZZ-BCAElss-.js → elk-276RUBZZ-CU_WOaXk.js} +1 -1
  30. package/dist/assets/{erDiagram-OPXOYQCR-C4_1ZPJe.js → erDiagram-OPXOYQCR-BeYwM04G.js} +1 -1
  31. package/dist/assets/{flowDiagram-KWPJA3E3-Bcb4Z75S.js → flowDiagram-KWPJA3E3-Bdw9DqEl.js} +1 -1
  32. package/dist/assets/{ganttDiagram-FUAMR5RP-BiPOzxzb.js → ganttDiagram-FUAMR5RP-C9iwq3qm.js} +1 -1
  33. package/dist/assets/{gitGraphDiagram-X574FWY7-B1wtF3ZS.js → gitGraphDiagram-X574FWY7-Dx-VWzUg.js} +1 -1
  34. package/dist/assets/{index-BkYwilkG.js → index-KygHRE3E.js} +1 -1
  35. package/dist/assets/index-NF7XCHAy.css +1 -0
  36. package/dist/assets/index-X_5ydXIE.js +610 -0
  37. package/dist/assets/{infoDiagram-VRGFBTTK-A19No71U.js → infoDiagram-VRGFBTTK-BG7uaEd4.js} +1 -1
  38. package/dist/assets/{ishikawaDiagram-OU5B5YK6-BQsqJ_cV.js → ishikawaDiagram-OU5B5YK6-D72WAXnM.js} +1 -1
  39. package/dist/assets/{journeyDiagram-ZHPQQLJL-DAO0pVan.js → journeyDiagram-ZHPQQLJL-DKH1gjYT.js} +1 -1
  40. package/dist/assets/{kanban-definition-PNTS6WVX-NepmKEe2.js → kanban-definition-PNTS6WVX-3upm9JUT.js} +1 -1
  41. package/dist/assets/{linear-bq1PTDTW.js → linear-CcR4pc0-.js} +1 -1
  42. package/dist/assets/{mermaid.core-s4LTbQle.js → mermaid.core-C6yU5FSS.js} +5 -5
  43. package/dist/assets/{mindmap-definition-NLK3R4M7-LtdbgHFH.js → mindmap-definition-NLK3R4M7-DOI2xEuv.js} +1 -1
  44. package/dist/assets/{pegDiagram-GJSIUBJH-tdbaCxiK.js → pegDiagram-GJSIUBJH-BVWJSgLd.js} +1 -1
  45. package/dist/assets/{pieDiagram-5QR66LMP-DvkbpW8l.js → pieDiagram-5QR66LMP-jtbYezhq.js} +1 -1
  46. package/dist/assets/{quadrantDiagram-O4NWA36T-Do4KaH-E.js → quadrantDiagram-O4NWA36T-g0u8mtPX.js} +1 -1
  47. package/dist/assets/{railroadDiagram-XR7U4H2S-BQWULOAs.js → railroadDiagram-XR7U4H2S-k8689Wny.js} +1 -1
  48. package/dist/assets/{requirementDiagram-PLB6GJNP-w9kei1FU.js → requirementDiagram-PLB6GJNP-DsJ22PDt.js} +1 -1
  49. package/dist/assets/{sankeyDiagram-IPEJSGJF-D2gRy0e8.js → sankeyDiagram-IPEJSGJF-CneWBRgG.js} +1 -1
  50. package/dist/assets/{sequenceDiagram-PO4LG4MO-a2sO5_Eo.js → sequenceDiagram-PO4LG4MO-DWsFCWap.js} +1 -1
  51. package/dist/assets/{sizeCapture-INFHLROL-CiPJmFEB.js → sizeCapture-INFHLROL-Bvf0YE3S.js} +1 -1
  52. package/dist/assets/{stateDiagram-v2-GCMORJYK-DAkUUlL7.js → stateDiagram-v2-GCMORJYK-Bxb3Iei8.js} +1 -1
  53. package/dist/assets/{swimlanes-2SLR337P-DANf2R8Y.js → swimlanes-2SLR337P-DLkk7W4A.js} +1 -1
  54. package/dist/assets/swimlanesDiagram-TC7HE7FX-C6bZtBXk.js +8 -0
  55. package/dist/assets/{timeline-definition-EJHVYXUP-BKpQmzrT.js → timeline-definition-EJHVYXUP-KR9bIHYC.js} +1 -1
  56. package/dist/assets/{usecaseDiagram-POWQR4AR-BtI7qLN-.js → usecaseDiagram-POWQR4AR-DfsOrj8t.js} +1 -1
  57. package/dist/assets/{vennDiagram-UO4OBE2U-gXJXrAG_.js → vennDiagram-UO4OBE2U-DRSTzG3h.js} +1 -1
  58. package/dist/assets/{wardleyDiagram-VNRHLVJA-GU8Po5-n.js → wardleyDiagram-VNRHLVJA-DtEdhPgT.js} +1 -1
  59. package/dist/assets/{xychartDiagram-PMCCYNJV-BF4J30g6.js → xychartDiagram-PMCCYNJV-B-XhTxjG.js} +1 -1
  60. package/dist/index.html +2 -2
  61. package/dist-server/cloud-home-start.js +3 -1
  62. package/dist-server/companion/src/routes.js +60 -0
  63. package/dist-server/container-mcp.js +94 -77
  64. package/dist-server/drivers/agents-proxy.js +96 -39
  65. package/dist-server/drivers/pi-mcp-extension.ts +245 -299
  66. package/dist-server/enterprise/server/index.js +3 -1
  67. package/dist-server/{browser-proxy.js → harness-mcp-proxy.js} +35 -19
  68. package/dist-server/index.js +18242 -13645
  69. package/dist-server/local-computer.js +3 -1
  70. package/dist-server/mcp-gate.js +147 -19
  71. package/dist-server/mcp-remote-proxy.js +437 -0
  72. package/dist-server/openmausbot.js +9618 -10058
  73. package/dist-server/pair-cli.js +9618 -10058
  74. package/dist-server/permission-proxy.js +65 -1
  75. package/dist-server/proxy-paths.js +3 -1
  76. package/dist-server/server/account-signin.js +0 -2
  77. package/dist-server/server/activity.js +201 -0
  78. package/dist-server/server/auto-approve.js +39 -11
  79. package/dist-server/server/automatic-recovery.js +1 -1
  80. package/dist-server/server/boat-create-idempotency.js +0 -7
  81. package/dist-server/server/boat-delete-journal.js +0 -4
  82. package/dist-server/server/bot-package.js +1 -19
  83. package/dist-server/server/browser-live.js +14 -5
  84. package/dist-server/server/browser-runtime.js +7 -2
  85. package/dist-server/server/channel-queue.js +4 -1
  86. package/dist-server/server/chief-of-staff.js +2 -3
  87. package/dist-server/server/cli.js +32 -12
  88. package/dist-server/server/cloud-backend.js +0 -4
  89. package/dist-server/server/cloud-computer-tools.js +110 -0
  90. package/dist-server/server/cloud-home.js +0 -1
  91. package/dist-server/server/cloud-lending.js +0 -3
  92. package/dist-server/server/cloud-move-http.js +9 -3
  93. package/dist-server/server/cloud-move.js +18 -0
  94. package/dist-server/server/composio.js +31 -2
  95. package/dist-server/server/computer-control.js +0 -15
  96. package/dist-server/server/computer-observation.js +0 -74
  97. package/dist-server/server/computer-wait.js +33 -13
  98. package/dist-server/server/config.js +73 -11
  99. package/dist-server/server/container-computer.js +7 -19
  100. package/dist-server/server/contracts.js +9 -0
  101. package/dist-server/server/control-client.js +4 -10
  102. package/dist-server/server/decision-log.js +9 -6
  103. package/dist-server/server/delegations.js +47 -22
  104. package/dist-server/server/drivers/acp/core.js +64 -13
  105. package/dist-server/server/drivers/acp/grok.js +208 -3
  106. package/dist-server/server/drivers/agents-call.js +70 -29
  107. package/dist-server/server/drivers/agents-catalog.js +34 -12
  108. package/dist-server/server/drivers/agents-proxy.js +8 -30
  109. package/dist-server/server/drivers/antigravity.js +1 -4
  110. package/dist-server/server/drivers/boatagent.js +25 -4
  111. package/dist-server/server/drivers/cerebras.js +3 -3
  112. package/dist-server/server/drivers/chat-mcp-tools.js +29 -19
  113. package/dist-server/server/drivers/claude.js +33 -19
  114. package/dist-server/server/drivers/codex.js +84 -10
  115. package/dist-server/server/drivers/openai-chat.js +14 -9
  116. package/dist-server/server/drivers/pi.js +48 -4
  117. package/dist-server/server/drivers/retry.js +8 -4
  118. package/dist-server/server/engine-install.js +1 -5
  119. package/dist-server/server/environment.js +3 -0
  120. package/dist-server/server/guarded-requests.js +2 -1
  121. package/dist-server/server/harness/registry.js +0 -1
  122. package/dist-server/server/{browser-proxy.js → harness-mcp-proxy.js} +40 -20
  123. package/dist-server/server/index.js +1929 -507
  124. package/dist-server/server/lending-memory.js +12 -8
  125. package/dist-server/server/live-call-controller.js +868 -0
  126. package/dist-server/server/live-call.js +216 -0
  127. package/dist-server/server/mcp-gate-config.js +43 -12
  128. package/dist-server/server/mcp-gate.js +127 -25
  129. package/dist-server/server/mcp-http.js +46 -8
  130. package/dist-server/server/mcp-remote-proxy.js +108 -0
  131. package/dist-server/server/memory-entries.js +38 -8
  132. package/dist-server/server/memory-organize.js +1 -8
  133. package/dist-server/server/memory-topics.js +4 -4
  134. package/dist-server/server/memory-upkeep.js +13 -15
  135. package/dist-server/server/message-file.js +5 -9
  136. package/dist-server/server/new-bot-defaults.js +3 -0
  137. package/dist-server/server/outbound-counts.js +79 -0
  138. package/dist-server/server/outbound-requests.js +60 -0
  139. package/dist-server/server/package-import.js +1 -1
  140. package/dist-server/server/peer-delivery.js +1 -0
  141. package/dist-server/server/peer-provenance.js +4 -2
  142. package/dist-server/server/permission-proxy.js +15 -1
  143. package/dist-server/server/plan-usage.js +797 -0
  144. package/dist-server/server/proxy-paths.js +3 -1
  145. package/dist-server/server/redact.js +2 -0
  146. package/dist-server/server/request-auth.js +3 -2
  147. package/dist-server/server/room-handoffs.js +5 -13
  148. package/dist-server/server/routes/live.js +98 -0
  149. package/dist-server/server/routine-requests.js +1 -1
  150. package/dist-server/server/routines.js +5 -1
  151. package/dist-server/server/setup-mode.js +0 -2
  152. package/dist-server/server/skill-learn.js +0 -1
  153. package/dist-server/server/skill-library.js +195 -3
  154. package/dist-server/server/skills-library-migration.js +235 -0
  155. package/dist-server/server/skills.js +66 -3
  156. package/dist-server/server/steer-queue.js +13 -1
  157. package/dist-server/server/store.js +41 -5
  158. package/dist-server/server/surface.js +74 -23
  159. package/dist-server/server/system-prompt.js +4 -4
  160. package/dist-server/server/team-backup.js +2 -1
  161. package/dist-server/server/team-memory.js +209 -0
  162. package/dist-server/server/thread-events.js +3 -3
  163. package/dist-server/server/tunnel.js +20 -1
  164. package/dist-server/server/turn-log.js +10 -0
  165. package/dist-server/server/turn-resources.js +124 -0
  166. package/dist-server/server/vps-computer.js +4 -14
  167. package/dist-server/server/workspace.js +85 -47
  168. package/dist-server/shared/activity.js +3 -0
  169. package/dist-server/shared/ask-question.js +13 -0
  170. package/dist-server/shared/bot-avatar.js +8 -0
  171. package/dist-server/shared/call-consent.js +28 -0
  172. package/dist-server/shared/connector-scopes.js +78 -0
  173. package/dist-server/shared/live-approval.js +124 -0
  174. package/dist-server/shared/live-call.js +176 -0
  175. package/dist-server/shared/outbound.js +128 -0
  176. package/dist-server/shared/skill-md.js +10 -0
  177. package/dist-server/shared/team-backup.js +3 -0
  178. package/dist-server/shared/tool-scope-support.js +25 -0
  179. package/dist-server/shared/tool-scope.js +136 -0
  180. package/dist-server/shared/tool-surface.js +3 -2
  181. package/dist-server/src/lib/thread-refs.js +406 -0
  182. package/dist-server/vps-container-mcp.js +94 -77
  183. package/dist-server/workspace-backup.worker.js +89 -100
  184. package/enterprise/server/index.js +3 -1
  185. package/package.json +1 -1
  186. package/dist/assets/channel-BU94VpxE.js +0 -1
  187. package/dist/assets/index-Biji-bfL.css +0 -1
  188. package/dist/assets/index-DZlhoES0.js +0 -597
  189. package/dist/assets/swimlanesDiagram-TC7HE7FX-CdHxG8aM.js +0 -8
  190. package/dist-server/server/drivers/chat-boat-tools.js +0 -90
  191. package/dist-server/server/drivers/grokagent.js +0 -4
  192. package/dist-server/server/openmaus-status-capsule.js +0 -329
@@ -13,7 +13,8 @@ function resolveProxy(relative) {
13
13
  return existsSync(source) ? source : join(SERVER_ROOT, `${relative}.js`);
14
14
  }
15
15
  var SPAWNED_PROXIES = {
16
- browser: resolveProxy("browser-proxy"),
16
+ // The built-in browser and the cloud computer: argv[2] picks which.
17
+ harnessMcp: resolveProxy("harness-mcp-proxy"),
17
18
  localComputer: resolveProxy("local-computer-proxy"),
18
19
  permission: resolveProxy("permission-proxy"),
19
20
  containerMcp: resolveProxy("container-mcp"),
@@ -22,6 +23,7 @@ var SPAWNED_PROXIES = {
22
23
  dweb: resolveProxy("drivers/dweb-proxy"),
23
24
  connectors: resolveProxy("connector-proxy"),
24
25
  mcpGate: resolveProxy("mcp-gate"),
26
+ mcpRemote: resolveProxy("mcp-remote-proxy"),
25
27
  phone: resolveProxy("drivers/phone-proxy"),
26
28
  hook: resolveProxy("hooks/omb-hook"),
27
29
  // Loaded by the external `pi` process via `-e`, not by this server — but
@@ -4,6 +4,60 @@ import { mkdirSync, readdirSync as readdirSync2, rmSync, statSync as statSync2,
4
4
  import { join as join2 } from "node:path";
5
5
  import { createInterface } from "node:readline";
6
6
 
7
+ // shared/tool-scope.ts
8
+ var SERVER_NAME = /^[a-z][a-z0-9_-]{0,31}$/;
9
+ var CONTROL_CHARACTER = /[\x00-\x1f\x7f]/;
10
+ var MAX_SELECTORS = 256;
11
+ var MAX_SELECTOR_LENGTH = 1024;
12
+ function validName(name) {
13
+ return typeof name === "string" && name.trim().length > 0 && name.length <= MAX_SELECTOR_LENGTH && !CONTROL_CHARACTER.test(name) && !name.includes("*");
14
+ }
15
+ function selectorIdentity(selector) {
16
+ if (selector.length > MAX_SELECTOR_LENGTH || CONTROL_CHARACTER.test(selector)) return;
17
+ if (selector.startsWith("native:")) {
18
+ const name = selector.slice(7);
19
+ if (name === "*" || validName(name)) return { kind: "native", name };
20
+ } else if (selector.startsWith("mcp:")) {
21
+ const separator = selector.indexOf(":", 4);
22
+ if (separator < 0) return;
23
+ const server = selector.slice(4, separator);
24
+ const name = selector.slice(separator + 1);
25
+ if (SERVER_NAME.test(server) && (name === "*" || validName(name))) {
26
+ return { kind: "mcp", server, name };
27
+ }
28
+ }
29
+ }
30
+ function parseToolScope(value) {
31
+ if (value === void 0) return { ok: true, scope: void 0 };
32
+ const invalid = { ok: false, error: "Tool selection must contain only valid allow and deny lists." };
33
+ if (!value || typeof value !== "object" || Array.isArray(value)) return invalid;
34
+ const prototype = Object.getPrototypeOf(value);
35
+ if (prototype !== Object.prototype && prototype !== null) return invalid;
36
+ const scope2 = {};
37
+ for (const key of Reflect.ownKeys(value)) {
38
+ if (key !== "allow" && key !== "deny") return invalid;
39
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
40
+ if (!descriptor || !("value" in descriptor)) return invalid;
41
+ const list = descriptor.value;
42
+ if (!Array.isArray(list) || list.length > MAX_SELECTORS) return invalid;
43
+ if (!Array.from(list).every((entry) => typeof entry === "string" && selectorIdentity(entry))) return invalid;
44
+ scope2[key] = [...new Set(list)];
45
+ }
46
+ return { ok: true, scope: scope2 };
47
+ }
48
+ function matches(selector, tool) {
49
+ const selected = selectorIdentity(selector);
50
+ return selected?.kind === tool.kind && (selected.kind !== "mcp" || tool.kind === "mcp" && selected.server === tool.server) && (selected.name === "*" || selected.name === tool.name);
51
+ }
52
+ function permits(scope2, tool) {
53
+ if (!validName(tool.name) || tool.kind === "mcp" && !SERVER_NAME.test(tool.server)) return false;
54
+ return (scope2?.allow === void 0 || scope2.allow.some((selector) => matches(selector, tool))) && !scope2?.deny?.some((selector) => matches(selector, tool));
55
+ }
56
+ function allowsTool(scope2, tool) {
57
+ const parsed = parseToolScope(scope2);
58
+ return parsed.ok && permits(parsed.scope, tool);
59
+ }
60
+
7
61
  // server/env-path.ts
8
62
  import { execFile } from "node:child_process";
9
63
  import { closeSync, existsSync, openSync, readFileSync, readSync, statSync, readdirSync } from "node:fs";
@@ -447,21 +501,60 @@ async function stopCliTree(child2, pid, timeoutMs) {
447
501
  }
448
502
 
449
503
  // server/mcp-gate.ts
450
- var NAME = process.env.OMB_GATE_NAME || "mcp";
451
- var SPILL_DIR = process.env.OMB_GATE_SPILL_DIR || "";
452
- var BUDGET = Number(process.env.OMB_GATE_BUDGET) > 0 ? Number(process.env.OMB_GATE_BUDGET) : DEFAULT_RESULT_BUDGET;
504
+ function gateEnvironment() {
505
+ if (process.argv.length === 2) return process.env;
506
+ try {
507
+ const key = process.argv[3];
508
+ if (process.argv.length !== 4 || process.argv[2] !== "--config-env" || !key || !/^OMB_GATE_CONFIG_[a-f0-9]{64}$/.test(key)) throw new Error();
509
+ const value = JSON.parse(process.env[key] ?? "");
510
+ if (!value || typeof value !== "object" || Array.isArray(value) || !["OMB_GATE_NAME", "OMB_GATE_UPSTREAM", "OMB_GATE_TOOL_SCOPE"].every((name) => typeof value[name] === "string") || !Object.entries(value).every(([name, setting]) => GATE_ENV_KEYS.includes(name) && typeof setting === "string")) throw new Error();
511
+ return value;
512
+ } catch {
513
+ process.stderr.write("mcp-gate: invalid private configuration\n");
514
+ process.exit(1);
515
+ }
516
+ }
517
+ var GATE_ENV_KEYS = ["OMB_GATE_NAME", "OMB_GATE_SPILL_DIR", "OMB_GATE_BUDGET", "OMB_GATE_UPSTREAM", "OMB_GATE_SPILL_HINT", "OMB_GATE_TOOL_SCOPE"];
518
+ var gateEnv = gateEnvironment();
519
+ var NAME = gateEnv.OMB_GATE_NAME || "mcp";
520
+ var SPILL_DIR = gateEnv.OMB_GATE_SPILL_DIR || "";
521
+ var rawBudget = Number(gateEnv.OMB_GATE_BUDGET);
522
+ var BUDGET = Number.isFinite(rawBudget) && rawBudget >= 0 ? rawBudget : DEFAULT_RESULT_BUDGET;
453
523
  var SPILL_MAX_AGE_MS = 24 * 60 * 6e4;
454
- var SPILL_HINT = process.env.OMB_GATE_SPILL_HINT === "1";
455
- var GATE_ENV_KEYS = ["OMB_GATE_NAME", "OMB_GATE_SPILL_DIR", "OMB_GATE_BUDGET", "OMB_GATE_UPSTREAM", "OMB_GATE_SPILL_HINT"];
524
+ var SPILL_HINT = gateEnv.OMB_GATE_SPILL_HINT === "1";
456
525
  function fail(message) {
457
526
  process.stderr.write(`mcp-gate(${NAME}): ${message}
458
527
  `);
459
528
  process.exit(1);
460
529
  }
530
+ function toolScope() {
531
+ const raw = gateEnv.OMB_GATE_TOOL_SCOPE;
532
+ if (raw === void 0) return;
533
+ let value;
534
+ try {
535
+ value = JSON.parse(raw);
536
+ } catch {
537
+ fail("invalid tool selection JSON");
538
+ }
539
+ const parsed = parseToolScope(value);
540
+ if (!parsed.ok || parsed.scope === void 0) fail("invalid tool selection; check the bot's Access settings");
541
+ if (!/^[a-z][a-z0-9_-]{0,31}$/.test(NAME)) fail("invalid MCP server identity for tool selection");
542
+ return parsed.scope;
543
+ }
544
+ function isRecord(value) {
545
+ return !!value && typeof value === "object" && !Array.isArray(value);
546
+ }
547
+ function requestKey(id) {
548
+ return typeof id === "string" || typeof id === "number" && Number.isFinite(id) ? `${typeof id}:${id}` : void 0;
549
+ }
550
+ function rejectRequest(id, code, message) {
551
+ process.stdout.write(`${JSON.stringify({ jsonrpc: "2.0", id: requestKey(id) ? id : null, error: { code, message } })}
552
+ `);
553
+ }
461
554
  function upstreamSpec() {
462
555
  let parsed;
463
556
  try {
464
- parsed = JSON.parse(process.env.OMB_GATE_UPSTREAM ?? "");
557
+ parsed = JSON.parse(gateEnv.OMB_GATE_UPSTREAM ?? "");
465
558
  } catch {
466
559
  fail("OMB_GATE_UPSTREAM is not valid JSON");
467
560
  }
@@ -503,6 +596,7 @@ function spill(tool, text) {
503
596
  }
504
597
  }
505
598
  function trimCallResult(result, tool) {
599
+ if (BUDGET === 0) return false;
506
600
  const content = result.content;
507
601
  if (!Array.isArray(content)) return false;
508
602
  const textBlocks = content.filter(
@@ -525,10 +619,11 @@ function trimCallResult(result, tool) {
525
619
  }
526
620
  return trimmed;
527
621
  }
622
+ var scope = toolScope();
528
623
  var spec = upstreamSpec();
529
624
  if (SPILL_DIR) sweepSpill(SPILL_DIR);
530
625
  var childEnv = { ...process.env, ...spec.env };
531
- for (const key of GATE_ENV_KEYS) delete childEnv[key];
626
+ for (const key of Object.keys(childEnv)) if (key.startsWith("OMB_GATE_")) delete childEnv[key];
532
627
  var resolved = resolveCliSpawn(spec.command, spec.args ?? []);
533
628
  var child = spawn2(resolved.command, resolved.args, {
534
629
  stdio: ["pipe", "pipe", "pipe"],
@@ -560,14 +655,35 @@ child.on("error", (error) => {
560
655
  child.stderr.pipe(process.stderr);
561
656
  var pending = /* @__PURE__ */ new Map();
562
657
  createInterface({ input: process.stdin }).on("line", (line) => {
658
+ let message;
563
659
  if (line.trim()) {
564
660
  try {
565
- const message = JSON.parse(line);
566
- if (message.method === "tools/call" && message.id !== void 0 && message.id !== null) {
567
- const params = message.params;
568
- pending.set(String(message.id), typeof params?.name === "string" ? params.name : "tool");
569
- }
661
+ const parsed = JSON.parse(line);
662
+ if (isRecord(parsed)) message = parsed;
570
663
  } catch {
664
+ if (scope) return rejectRequest(null, -32700, "Invalid JSON-RPC frame");
665
+ }
666
+ if (scope && (!message || message.jsonrpc !== "2.0")) return rejectRequest(message?.id, -32600, "Invalid JSON-RPC frame");
667
+ }
668
+ const id = requestKey(message?.id);
669
+ const params = isRecord(message?.params) ? message.params : void 0;
670
+ if (scope && message?.method === "tools/call") {
671
+ if (!id || typeof params?.name !== "string" || params.arguments !== void 0 && !isRecord(params.arguments)) {
672
+ return rejectRequest(message.id, -32602, "Invalid tool call");
673
+ }
674
+ if (!allowsTool(scope, { kind: "mcp", server: NAME, name: params.name })) {
675
+ return rejectRequest(message.id, -32602, "Tool selection excludes this tool. Check the bot's Access settings.");
676
+ }
677
+ }
678
+ if (scope && message?.method === "tools/list" && !id) {
679
+ return rejectRequest(message.id, -32600, "Tool listing needs a request ID");
680
+ }
681
+ if (id && typeof message?.method === "string") {
682
+ if (scope && pending.has(id)) fail("duplicate request ID on a restricted connection");
683
+ if (message.method === "tools/call") {
684
+ pending.set(id, { kind: "call", tool: typeof params?.name === "string" ? params.name : "tool" });
685
+ } else if (scope) {
686
+ pending.set(id, { kind: message.method === "tools/list" ? "list" : "other" });
571
687
  }
572
688
  }
573
689
  child.stdin.write(`${line}
@@ -578,24 +694,36 @@ createInterface({ input: child.stdout }).on("line", (line) => {
578
694
  if (!line.trim()) return;
579
695
  let message;
580
696
  try {
581
- message = JSON.parse(line);
697
+ const parsed = JSON.parse(line);
698
+ if (isRecord(parsed)) message = parsed;
582
699
  } catch {
700
+ if (scope) fail("unreadable upstream frame on a restricted connection");
701
+ }
702
+ if (!message || scope && message.jsonrpc !== "2.0") {
703
+ if (scope) fail("invalid upstream frame on a restricted connection");
583
704
  process.stdout.write(`${line}
584
705
  `);
585
706
  return;
586
707
  }
587
- const id = message.id === void 0 || message.id === null ? void 0 : String(message.id);
588
- const tool = id === void 0 ? void 0 : pending.get(id);
708
+ const id = typeof message.method === "string" ? void 0 : requestKey(message.id);
709
+ const request = id === void 0 ? void 0 : pending.get(id);
710
+ if (scope && id !== void 0 && !request) fail("unrequested upstream response on a restricted connection");
589
711
  if (id !== void 0) pending.delete(id);
590
712
  const result = message.result;
591
- if (tool && result && typeof result === "object" && !Array.isArray(result)) {
713
+ if (scope && request?.kind === "list" && message.error === void 0) {
714
+ if (!isRecord(result) || !Array.isArray(result.tools) || !result.tools.every((tool) => isRecord(tool) && typeof tool.name === "string" && tool.name.trim().length > 0) || result.nextCursor !== void 0 && typeof result.nextCursor !== "string") {
715
+ return rejectRequest(message.id, -32603, "Invalid tool catalog on a restricted connection");
716
+ }
717
+ result.tools = result.tools.filter((tool) => allowsTool(scope, { kind: "mcp", server: NAME, name: tool.name }));
718
+ }
719
+ if (request?.kind === "call" && isRecord(result)) {
592
720
  try {
593
- if (trimCallResult(result, tool)) {
594
- process.stderr.write(`mcp-gate(${NAME}): trimmed ${tool} to ${BUDGET} chars
721
+ if (trimCallResult(result, request.tool)) {
722
+ process.stderr.write(`mcp-gate(${NAME}): trimmed ${request.tool} to ${BUDGET} chars
595
723
  `);
596
724
  }
597
725
  } catch (error) {
598
- process.stderr.write(`mcp-gate(${NAME}): could not trim ${tool}: ${String(error)}
726
+ process.stderr.write(`mcp-gate(${NAME}): could not trim ${request.tool}: ${String(error)}
599
727
  `);
600
728
  process.stdout.write(`${line}
601
729
  `);
@@ -0,0 +1,437 @@
1
+ // server/mcp-remote-proxy.ts
2
+ import { createInterface } from "node:readline";
3
+
4
+ // server/mcp-http.ts
5
+ var McpHttpError = class extends Error {
6
+ kind;
7
+ status;
8
+ /** The server's `WWW-Authenticate` challenge on a 401: where to sign in. */
9
+ wwwAuthenticate;
10
+ constructor(kind, message, status, wwwAuthenticate) {
11
+ super(message);
12
+ this.name = "McpHttpError";
13
+ this.kind = kind;
14
+ this.status = status;
15
+ this.wwwAuthenticate = wwwAuthenticate;
16
+ }
17
+ };
18
+ var MAX_BODY_BYTES = 1048576;
19
+ var PROTOCOL_VERSION = "2025-06-18";
20
+ function isRecord(value) {
21
+ return value !== null && typeof value === "object" && !Array.isArray(value);
22
+ }
23
+ function remoteMcpSpec(value) {
24
+ if (!isRecord(value) || value.type !== "http" && value.type !== "sse" || typeof value.url !== "string") return;
25
+ try {
26
+ const url = new URL(value.url);
27
+ if (url.protocol !== "http:" && url.protocol !== "https:" || url.username || url.password) return;
28
+ const headers = value.headers ?? {};
29
+ if (!isRecord(headers) || !Object.values(headers).every((header) => typeof header === "string")) return;
30
+ new Headers(headers);
31
+ return { type: value.type, url: value.url, headers };
32
+ } catch {
33
+ return;
34
+ }
35
+ }
36
+ function unwrap(message) {
37
+ if (message.error !== void 0) {
38
+ const detail = isRecord(message.error) && typeof message.error.message === "string" ? message.error.message : "request failed";
39
+ throw new McpHttpError("protocol", `MCP error: ${detail}`);
40
+ }
41
+ return message.result;
42
+ }
43
+ function parseMessage(text) {
44
+ try {
45
+ const parsed = JSON.parse(text);
46
+ if (Array.isArray(parsed)) return parsed.filter(isRecord);
47
+ return isRecord(parsed) ? parsed : null;
48
+ } catch {
49
+ return null;
50
+ }
51
+ }
52
+ function parseSseBlock(block) {
53
+ let event = "message";
54
+ const data = [];
55
+ for (const line of block.split("\n")) {
56
+ if (!line || line.startsWith(":")) continue;
57
+ const colon = line.indexOf(":");
58
+ const field = colon === -1 ? line : line.slice(0, colon);
59
+ let value = colon === -1 ? "" : line.slice(colon + 1);
60
+ if (value.startsWith(" ")) value = value.slice(1);
61
+ if (field === "event") event = value;
62
+ else if (field === "data") data.push(value);
63
+ }
64
+ return data.length ? { event, data: data.join("\n") } : null;
65
+ }
66
+ async function readSse(response, signal, onEvent, maxBytes = MAX_BODY_BYTES) {
67
+ const reader = response.body?.getReader();
68
+ if (!reader) throw new McpHttpError("protocol", "empty event stream");
69
+ const decoder = new TextDecoder();
70
+ let buffer = "";
71
+ let bytes = 0;
72
+ const onAbort = () => void reader.cancel().catch(() => {
73
+ });
74
+ signal.addEventListener("abort", onAbort, { once: true });
75
+ try {
76
+ for (; ; ) {
77
+ const { value, done } = await reader.read();
78
+ if (done) return;
79
+ bytes += value.byteLength;
80
+ if (bytes > maxBytes) throw new McpHttpError("protocol", "event stream too large");
81
+ buffer += decoder.decode(value, { stream: true }).replace(/\r\n?/g, "\n");
82
+ let separator;
83
+ while ((separator = buffer.indexOf("\n\n")) !== -1) {
84
+ const block = buffer.slice(0, separator);
85
+ buffer = buffer.slice(separator + 2);
86
+ const event = parseSseBlock(block);
87
+ if (event && onEvent(event) === "stop") {
88
+ await reader.cancel().catch(() => {
89
+ });
90
+ return;
91
+ }
92
+ }
93
+ }
94
+ } finally {
95
+ signal.removeEventListener("abort", onAbort);
96
+ }
97
+ }
98
+ async function readBounded(response, maxBytes = MAX_BODY_BYTES) {
99
+ const reader = response.body?.getReader();
100
+ if (!reader) return "";
101
+ const decoder = new TextDecoder();
102
+ let text = "";
103
+ let bytes = 0;
104
+ for (; ; ) {
105
+ const { value, done } = await reader.read();
106
+ if (done) return text + decoder.decode();
107
+ bytes += value.byteLength;
108
+ if (bytes > maxBytes) {
109
+ await reader.cancel().catch(() => {
110
+ });
111
+ throw new McpHttpError("protocol", "response too large");
112
+ }
113
+ text += decoder.decode(value, { stream: true });
114
+ }
115
+ }
116
+ function drain(response) {
117
+ void response.body?.cancel().catch(() => {
118
+ });
119
+ }
120
+ var RemoteMcpClient = class {
121
+ target;
122
+ fetchImpl;
123
+ maxBytes;
124
+ onNotification;
125
+ sessionId = null;
126
+ protocolVersion = null;
127
+ nextId = 1;
128
+ closed = false;
129
+ /** Legacy SSE: one long GET stream carries every response. */
130
+ pending = /* @__PURE__ */ new Map();
131
+ sseEndpoint = null;
132
+ sseAbort = new AbortController();
133
+ constructor(target, options = {}) {
134
+ this.target = target;
135
+ this.fetchImpl = options.fetch ?? fetch;
136
+ this.maxBytes = options.maxBytes ?? MAX_BODY_BYTES;
137
+ this.onNotification = options.onNotification;
138
+ }
139
+ /** The MCP handshake: initialize, then the initialized notification. */
140
+ async initialize(clientName, signal) {
141
+ const result = await this.request("initialize", {
142
+ protocolVersion: PROTOCOL_VERSION,
143
+ capabilities: {},
144
+ clientInfo: { name: clientName, version: "1" }
145
+ }, signal);
146
+ if (isRecord(result) && typeof result.protocolVersion === "string") this.protocolVersion = result.protocolVersion;
147
+ await this.notify("notifications/initialized", void 0, signal);
148
+ return result;
149
+ }
150
+ request(method, params, signal) {
151
+ if (this.closed) return Promise.reject(new McpHttpError("protocol", "client closed"));
152
+ if (signal.aborted) return Promise.reject(new McpHttpError("network", "aborted"));
153
+ const id = this.nextId++;
154
+ const frame = { jsonrpc: "2.0", id, method, ...params === void 0 ? {} : { params } };
155
+ const onAbort = () => {
156
+ void this.notify("notifications/cancelled", { requestId: id, reason: "Request cancelled" }, AbortSignal.timeout(2e3)).catch(() => {
157
+ });
158
+ };
159
+ signal.addEventListener("abort", onAbort, { once: true });
160
+ const reply = this.target.type === "sse" ? this.sseRequest(id, frame, signal) : this.streamableRequest(id, frame, signal);
161
+ return reply.finally(() => signal.removeEventListener("abort", onAbort));
162
+ }
163
+ async notify(method, params, signal) {
164
+ if (this.closed) throw new McpHttpError("protocol", "client closed");
165
+ const frame = { jsonrpc: "2.0", method, ...params === void 0 ? {} : { params } };
166
+ if (this.target.type === "sse") {
167
+ await this.ssePost(frame, signal);
168
+ return;
169
+ }
170
+ const response = await this.post(this.target.url, frame, signal);
171
+ drain(response);
172
+ }
173
+ async close() {
174
+ if (this.closed) return;
175
+ this.closed = true;
176
+ this.sseAbort.abort();
177
+ for (const entry of this.pending.values()) entry.reject(new McpHttpError("network", "client closed"));
178
+ this.pending.clear();
179
+ if (this.sessionId && this.target.type !== "sse") {
180
+ try {
181
+ const headers = this.headers();
182
+ headers.set("mcp-session-id", this.sessionId);
183
+ const response = await this.fetchImpl(this.target.url, { method: "DELETE", headers, signal: AbortSignal.timeout(2e3) });
184
+ drain(response);
185
+ } catch {
186
+ }
187
+ }
188
+ }
189
+ headers() {
190
+ const headers = new Headers();
191
+ for (const [name, value] of Object.entries(this.target.headers)) headers.set(name, value);
192
+ headers.set("accept", "application/json, text/event-stream");
193
+ if (this.sessionId) headers.set("mcp-session-id", this.sessionId);
194
+ if (this.protocolVersion) headers.set("mcp-protocol-version", this.protocolVersion);
195
+ return headers;
196
+ }
197
+ async post(url, frame, signal) {
198
+ const headers = this.headers();
199
+ headers.set("content-type", "application/json");
200
+ let response;
201
+ try {
202
+ response = await this.fetchImpl(url, { method: "POST", headers, body: JSON.stringify(frame), signal });
203
+ } catch (error2) {
204
+ if (signal.aborted) throw error2;
205
+ throw new McpHttpError("network", "request failed");
206
+ }
207
+ if (!response.ok) {
208
+ drain(response);
209
+ throw new McpHttpError("status", `HTTP ${response.status}`, response.status, response.headers.get("www-authenticate") ?? void 0);
210
+ }
211
+ const session = response.headers.get("mcp-session-id");
212
+ if (session) this.sessionId = session;
213
+ return response;
214
+ }
215
+ // ── streamable HTTP: each request is one POST; the answer is JSON or a
216
+ // short event stream that carries the response among server messages ──
217
+ async streamableRequest(id, frame, signal) {
218
+ const response = await this.post(this.target.url, frame, signal);
219
+ const type = (response.headers.get("content-type") ?? "").toLowerCase();
220
+ if (type.startsWith("application/json")) {
221
+ const parsed = parseMessage(await readBounded(response, this.maxBytes));
222
+ this.deliverNotifications(parsed);
223
+ const message = (Array.isArray(parsed) ? parsed : parsed ? [parsed] : []).find((entry) => entry.id === id);
224
+ if (!message) throw new McpHttpError("protocol", "response did not answer the request");
225
+ return unwrap(message);
226
+ }
227
+ if (type.startsWith("text/event-stream")) {
228
+ let found;
229
+ await readSse(response, signal, (event) => {
230
+ if (event.event !== "message") return void 0;
231
+ const parsed = parseMessage(event.data);
232
+ this.deliverNotifications(parsed);
233
+ const message = (Array.isArray(parsed) ? parsed : parsed ? [parsed] : []).find((entry) => entry.id === id);
234
+ if (!message) return void 0;
235
+ found = message;
236
+ return "stop";
237
+ }, this.maxBytes);
238
+ if (signal.aborted) throw new McpHttpError("network", "aborted");
239
+ if (!found) throw new McpHttpError("protocol", "event stream ended without the response");
240
+ return unwrap(found);
241
+ }
242
+ drain(response);
243
+ throw new McpHttpError("protocol", "unexpected content type");
244
+ }
245
+ // ── legacy SSE: a GET opens the stream, its first event names where to
246
+ // POST, and every response comes back over the stream ──
247
+ openSseStream(signal) {
248
+ if (this.sseEndpoint) return this.sseEndpoint;
249
+ this.sseEndpoint = new Promise((resolveEndpoint, rejectEndpoint) => {
250
+ let settled = false;
251
+ const fail = (error2) => {
252
+ if (!settled) {
253
+ settled = true;
254
+ rejectEndpoint(error2);
255
+ }
256
+ for (const entry of this.pending.values()) entry.reject(error2);
257
+ this.pending.clear();
258
+ };
259
+ const headers = this.headers();
260
+ headers.set("accept", "text/event-stream");
261
+ const opening = AbortSignal.any([signal, this.sseAbort.signal]);
262
+ this.fetchImpl(this.target.url, { method: "GET", headers, signal: opening }).then(async (response) => {
263
+ if (!response.ok) {
264
+ drain(response);
265
+ throw new McpHttpError("status", `HTTP ${response.status}`, response.status, response.headers.get("www-authenticate") ?? void 0);
266
+ }
267
+ await readSse(response, this.sseAbort.signal, (event) => {
268
+ if (event.event === "endpoint") {
269
+ if (!settled) {
270
+ settled = true;
271
+ try {
272
+ resolveEndpoint(new URL(event.data, this.target.url).toString());
273
+ } catch {
274
+ rejectEndpoint(new McpHttpError("protocol", "invalid endpoint event"));
275
+ }
276
+ }
277
+ return void 0;
278
+ }
279
+ if (event.event !== "message") return void 0;
280
+ const parsed = parseMessage(event.data);
281
+ this.deliverNotifications(parsed);
282
+ for (const message of Array.isArray(parsed) ? parsed : parsed ? [parsed] : []) {
283
+ const entry = typeof message.id === "number" ? this.pending.get(message.id) : void 0;
284
+ if (!entry) continue;
285
+ this.pending.delete(message.id);
286
+ try {
287
+ entry.resolve(unwrap(message));
288
+ } catch (error2) {
289
+ entry.reject(error2 instanceof Error ? error2 : new McpHttpError("protocol", String(error2)));
290
+ }
291
+ }
292
+ return void 0;
293
+ }, this.maxBytes);
294
+ fail(new McpHttpError("network", "event stream ended"));
295
+ }).catch((error2) => {
296
+ fail(error2 instanceof McpHttpError ? error2 : new McpHttpError("network", "event stream failed"));
297
+ });
298
+ });
299
+ return this.sseEndpoint;
300
+ }
301
+ async ssePost(frame, signal) {
302
+ const endpoint = await this.openSseStream(signal);
303
+ const response = await this.post(endpoint, frame, signal);
304
+ drain(response);
305
+ }
306
+ sseRequest(id, frame, signal) {
307
+ return new Promise((resolve, reject) => {
308
+ const onAbort = () => {
309
+ this.pending.delete(id);
310
+ reject(new McpHttpError("network", "aborted"));
311
+ };
312
+ signal.addEventListener("abort", onAbort, { once: true });
313
+ this.pending.set(id, {
314
+ resolve: (value) => {
315
+ signal.removeEventListener("abort", onAbort);
316
+ resolve(value);
317
+ },
318
+ reject: (error2) => {
319
+ signal.removeEventListener("abort", onAbort);
320
+ reject(error2);
321
+ }
322
+ });
323
+ this.ssePost(frame, signal).catch((error2) => {
324
+ this.pending.delete(id);
325
+ signal.removeEventListener("abort", onAbort);
326
+ reject(error2 instanceof Error ? error2 : new McpHttpError("network", String(error2)));
327
+ });
328
+ });
329
+ }
330
+ deliverNotifications(parsed) {
331
+ for (const message of Array.isArray(parsed) ? parsed : parsed ? [parsed] : []) {
332
+ if (typeof message.method === "string" && message.id === void 0) this.onNotification?.(message);
333
+ }
334
+ }
335
+ };
336
+
337
+ // server/mcp-remote-proxy.ts
338
+ function isRecord2(value) {
339
+ return !!value && typeof value === "object" && !Array.isArray(value);
340
+ }
341
+ function requestKey(id) {
342
+ return typeof id === "string" || typeof id === "number" && Number.isFinite(id) ? `${typeof id}:${id}` : void 0;
343
+ }
344
+ function send(frame) {
345
+ process.stdout.write(`${JSON.stringify(frame)}
346
+ `);
347
+ }
348
+ function error(id, code, message) {
349
+ send({ jsonrpc: "2.0", id: requestKey(id) ? id : null, error: { code, message } });
350
+ }
351
+ var spec;
352
+ try {
353
+ spec = remoteMcpSpec(JSON.parse(process.env.OMB_REMOTE_MCP_SERVER ?? ""));
354
+ } catch {
355
+ }
356
+ delete process.env.OMB_REMOTE_MCP_SERVER;
357
+ if (!spec) {
358
+ process.stderr.write("mcp-remote-proxy: invalid remote MCP configuration\n");
359
+ process.exit(1);
360
+ }
361
+ var client = new RemoteMcpClient(spec, { maxBytes: 32 * 1024 * 1024, onNotification: send });
362
+ var pending = /* @__PURE__ */ new Map();
363
+ var tasks = /* @__PURE__ */ new Set();
364
+ var initialized = false;
365
+ var closing = false;
366
+ async function handle(message) {
367
+ const id = requestKey(message.id);
368
+ if (typeof message.method !== "string" || message.jsonrpc !== "2.0") {
369
+ error(message.id, -32600, "Invalid JSON-RPC request");
370
+ return;
371
+ }
372
+ if (message.method === "notifications/cancelled") {
373
+ const key = isRecord2(message.params) ? requestKey(message.params.requestId) : void 0;
374
+ if (key) pending.get(key)?.abort();
375
+ return;
376
+ }
377
+ if (!id) {
378
+ if (message.method === "notifications/initialized" && initialized) return;
379
+ try {
380
+ await client.notify(message.method, message.params, AbortSignal.timeout(3e4));
381
+ } catch {
382
+ }
383
+ return;
384
+ }
385
+ if (pending.has(id)) {
386
+ error(message.id, -32600, "Duplicate request ID");
387
+ return;
388
+ }
389
+ const controller = new AbortController();
390
+ pending.set(id, controller);
391
+ const signal = AbortSignal.any([controller.signal, AbortSignal.timeout(message.method === "tools/call" ? 12e4 : 3e4)]);
392
+ try {
393
+ const result = message.method === "initialize" ? await client.initialize("OpenMausBot tool proxy", signal) : await client.request(message.method, message.params, signal);
394
+ if (message.method === "initialize") initialized = true;
395
+ send({ jsonrpc: "2.0", id: message.id, result });
396
+ } catch {
397
+ error(message.id, signal.aborted ? -32800 : -32603, signal.aborted ? "MCP request cancelled" : "Remote MCP request failed");
398
+ } finally {
399
+ pending.delete(id);
400
+ }
401
+ }
402
+ var input = createInterface({ input: process.stdin });
403
+ input.on("line", (line) => {
404
+ if (closing || !line.trim()) return;
405
+ let value;
406
+ try {
407
+ value = JSON.parse(line);
408
+ } catch {
409
+ error(null, -32700, "Invalid JSON-RPC frame");
410
+ return;
411
+ }
412
+ if (!isRecord2(value)) {
413
+ error(null, -32600, "Invalid JSON-RPC frame");
414
+ return;
415
+ }
416
+ const task = handle(value);
417
+ tasks.add(task);
418
+ void task.finally(() => tasks.delete(task));
419
+ });
420
+ async function shutdown() {
421
+ if (closing) return;
422
+ closing = true;
423
+ input.close();
424
+ for (const controller of pending.values()) controller.abort();
425
+ await Promise.allSettled(tasks);
426
+ await client.close();
427
+ process.exit(0);
428
+ }
429
+ input.on("close", () => {
430
+ void shutdown();
431
+ });
432
+ process.on("SIGTERM", () => {
433
+ void shutdown();
434
+ });
435
+ process.on("SIGINT", () => {
436
+ void shutdown();
437
+ });