@particle-academy/agent-integrations 0.28.2 → 0.30.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 (213) hide show
  1. package/README.md +38 -0
  2. package/dist/bridges/artboard.d.cts +3 -3
  3. package/dist/bridges/artboard.d.ts +3 -3
  4. package/dist/bridges/catalog.d.cts +3 -3
  5. package/dist/bridges/catalog.d.ts +3 -3
  6. package/dist/bridges/charts.d.cts +3 -3
  7. package/dist/bridges/charts.d.ts +3 -3
  8. package/dist/bridges/cms.d.cts +3 -3
  9. package/dist/bridges/cms.d.ts +3 -3
  10. package/dist/bridges/code.d.cts +3 -3
  11. package/dist/bridges/code.d.ts +3 -3
  12. package/dist/bridges/doc.d.cts +3 -3
  13. package/dist/bridges/doc.d.ts +3 -3
  14. package/dist/bridges/features.d.cts +3 -3
  15. package/dist/bridges/features.d.ts +3 -3
  16. package/dist/bridges/files.d.cts +23 -4
  17. package/dist/bridges/files.d.ts +23 -4
  18. package/dist/bridges/flow.d.cts +3 -3
  19. package/dist/bridges/flow.d.ts +3 -3
  20. package/dist/bridges/forms.d.cts +25 -4
  21. package/dist/bridges/forms.d.ts +25 -4
  22. package/dist/bridges/map.d.cts +3 -3
  23. package/dist/bridges/map.d.ts +3 -3
  24. package/dist/bridges/scene.d.cts +3 -3
  25. package/dist/bridges/scene.d.ts +3 -3
  26. package/dist/bridges/screens.d.cts +3 -3
  27. package/dist/bridges/screens.d.ts +3 -3
  28. package/dist/bridges/sheets.d.cts +3 -3
  29. package/dist/bridges/sheets.d.ts +3 -3
  30. package/dist/bridges/slides.d.cts +3 -3
  31. package/dist/bridges/slides.d.ts +3 -3
  32. package/dist/bridges/terminal.d.cts +24 -4
  33. package/dist/bridges/terminal.d.ts +24 -4
  34. package/dist/bridges/tui.d.cts +45 -0
  35. package/dist/bridges/tui.d.ts +45 -0
  36. package/dist/bridges/whiteboard.d.cts +3 -3
  37. package/dist/bridges/whiteboard.d.ts +3 -3
  38. package/dist/bridges-artboard.cjs +1 -1
  39. package/dist/bridges-artboard.cjs.map +1 -1
  40. package/dist/bridges-artboard.js +3 -3
  41. package/dist/bridges-catalog.cjs +1 -1
  42. package/dist/bridges-catalog.cjs.map +1 -1
  43. package/dist/bridges-catalog.js +4 -4
  44. package/dist/bridges-charts.cjs +1 -1
  45. package/dist/bridges-charts.cjs.map +1 -1
  46. package/dist/bridges-charts.js +4 -4
  47. package/dist/bridges-cms.cjs +1 -1
  48. package/dist/bridges-cms.cjs.map +1 -1
  49. package/dist/bridges-cms.js +5 -5
  50. package/dist/bridges-code.cjs +38 -3
  51. package/dist/bridges-code.cjs.map +1 -1
  52. package/dist/bridges-code.js +4 -4
  53. package/dist/bridges-doc.cjs +1 -1
  54. package/dist/bridges-doc.cjs.map +1 -1
  55. package/dist/bridges-doc.js +4 -4
  56. package/dist/bridges-features.cjs +1 -1
  57. package/dist/bridges-features.cjs.map +1 -1
  58. package/dist/bridges-features.js +4 -4
  59. package/dist/bridges-files.cjs +29 -8
  60. package/dist/bridges-files.cjs.map +1 -1
  61. package/dist/bridges-files.js +4 -4
  62. package/dist/bridges-flow.cjs +1 -1
  63. package/dist/bridges-flow.cjs.map +1 -1
  64. package/dist/bridges-flow.js +3 -3
  65. package/dist/bridges-forms.cjs +12 -2
  66. package/dist/bridges-forms.cjs.map +1 -1
  67. package/dist/bridges-forms.js +4 -4
  68. package/dist/bridges-map.cjs +1 -1
  69. package/dist/bridges-map.cjs.map +1 -1
  70. package/dist/bridges-map.js +4 -4
  71. package/dist/bridges-scene.cjs +1 -1
  72. package/dist/bridges-scene.cjs.map +1 -1
  73. package/dist/bridges-scene.js +4 -4
  74. package/dist/bridges-screens.cjs +1 -1
  75. package/dist/bridges-screens.cjs.map +1 -1
  76. package/dist/bridges-screens.js +4 -4
  77. package/dist/bridges-sheets.cjs +1 -1
  78. package/dist/bridges-sheets.cjs.map +1 -1
  79. package/dist/bridges-sheets.js +4 -4
  80. package/dist/bridges-slides.cjs +1 -1
  81. package/dist/bridges-slides.cjs.map +1 -1
  82. package/dist/bridges-slides.js +4 -4
  83. package/dist/bridges-terminal.cjs +70 -45
  84. package/dist/bridges-terminal.cjs.map +1 -1
  85. package/dist/bridges-terminal.js +4 -4
  86. package/dist/bridges-tui.cjs +114 -0
  87. package/dist/bridges-tui.cjs.map +1 -0
  88. package/dist/bridges-tui.js +7 -0
  89. package/dist/bridges-tui.js.map +1 -0
  90. package/dist/bridges-whiteboard.cjs +1 -1
  91. package/dist/bridges-whiteboard.cjs.map +1 -1
  92. package/dist/bridges-whiteboard.js +4 -4
  93. package/dist/{chunk-AAMQOXZC.js → chunk-2U5JB6ZA.js} +5 -5
  94. package/dist/{chunk-AAMQOXZC.js.map → chunk-2U5JB6ZA.js.map} +1 -1
  95. package/dist/{chunk-ULJL53DL.js → chunk-2VENWGLZ.js} +3 -3
  96. package/dist/chunk-2VENWGLZ.js.map +1 -0
  97. package/dist/{chunk-66WOHZZJ.js → chunk-6YUBWQTS.js} +5 -5
  98. package/dist/{chunk-66WOHZZJ.js.map → chunk-6YUBWQTS.js.map} +1 -1
  99. package/dist/{chunk-ZX6UQURN.js → chunk-BHQJJ2YG.js} +5 -5
  100. package/dist/{chunk-ZX6UQURN.js.map → chunk-BHQJJ2YG.js.map} +1 -1
  101. package/dist/{chunk-GHY3PBPN.js → chunk-BT7HM5I6.js} +3 -3
  102. package/dist/{chunk-GHY3PBPN.js.map → chunk-BT7HM5I6.js.map} +1 -1
  103. package/dist/{chunk-YGD2VTUM.js → chunk-CA7GIJJC.js} +5 -5
  104. package/dist/{chunk-YGD2VTUM.js.map → chunk-CA7GIJJC.js.map} +1 -1
  105. package/dist/{chunk-KJ5AOOV7.js → chunk-CT3LRNH7.js} +3 -3
  106. package/dist/{chunk-KJ5AOOV7.js.map → chunk-CT3LRNH7.js.map} +1 -1
  107. package/dist/{chunk-4KAIV6OD.js → chunk-DF3PLL2E.js} +10 -5
  108. package/dist/chunk-DF3PLL2E.js.map +1 -0
  109. package/dist/{chunk-A57CRGEK.js → chunk-DKSR4R4O.js} +3 -3
  110. package/dist/{chunk-A57CRGEK.js.map → chunk-DKSR4R4O.js.map} +1 -1
  111. package/dist/{chunk-AROVENCE.js → chunk-G5EZNWXJ.js} +5 -5
  112. package/dist/{chunk-AROVENCE.js.map → chunk-G5EZNWXJ.js.map} +1 -1
  113. package/dist/{chunk-OIX2ANFS.js → chunk-GRHPEK6M.js} +45 -9
  114. package/dist/chunk-GRHPEK6M.js.map +1 -0
  115. package/dist/{chunk-XMMCSQ4I.js → chunk-HRHC5GM5.js} +33 -13
  116. package/dist/chunk-HRHC5GM5.js.map +1 -0
  117. package/dist/{chunk-5KLUYF4D.js → chunk-HSI5RVF4.js} +3 -3
  118. package/dist/{chunk-5KLUYF4D.js.map → chunk-HSI5RVF4.js.map} +1 -1
  119. package/dist/{chunk-FF5F526J.js → chunk-LQZKRBWR.js} +5 -5
  120. package/dist/{chunk-FF5F526J.js.map → chunk-LQZKRBWR.js.map} +1 -1
  121. package/dist/chunk-MFMBR6LN.js +100 -0
  122. package/dist/chunk-MFMBR6LN.js.map +1 -0
  123. package/dist/{chunk-KMSRG5KH.js → chunk-N6RLSLOF.js} +7 -7
  124. package/dist/chunk-N6RLSLOF.js.map +1 -0
  125. package/dist/{chunk-VFKO2VUR.js → chunk-OAFJ53X3.js} +5 -5
  126. package/dist/{chunk-VFKO2VUR.js.map → chunk-OAFJ53X3.js.map} +1 -1
  127. package/dist/chunk-PMI4C5S4.js +44 -0
  128. package/dist/chunk-PMI4C5S4.js.map +1 -0
  129. package/dist/{chunk-QDD4WHIV.js → chunk-PR4SXFZH.js} +5 -5
  130. package/dist/{chunk-QDD4WHIV.js.map → chunk-PR4SXFZH.js.map} +1 -1
  131. package/dist/{chunk-NN3ELTVO.js → chunk-RIXYCFDO.js} +42 -7
  132. package/dist/chunk-RIXYCFDO.js.map +1 -0
  133. package/dist/{chunk-PCW2EK5G.js → chunk-USQGA672.js} +16 -6
  134. package/dist/chunk-USQGA672.js.map +1 -0
  135. package/dist/{chunk-HWVVS6HE.js → chunk-UZHNKUER.js} +74 -49
  136. package/dist/chunk-UZHNKUER.js.map +1 -0
  137. package/dist/{chunk-QRDVJW6Q.js → chunk-WCKSDFTM.js} +5 -5
  138. package/dist/{chunk-QRDVJW6Q.js.map → chunk-WCKSDFTM.js.map} +1 -1
  139. package/dist/{chunk-AAD3Z5E7.js → chunk-ZNGTC4CO.js} +5 -5
  140. package/dist/{chunk-AAD3Z5E7.js.map → chunk-ZNGTC4CO.js.map} +1 -1
  141. package/dist/components/SharedWhiteboard/index.d.cts +1 -1
  142. package/dist/components/SharedWhiteboard/index.d.ts +1 -1
  143. package/dist/components-shared-whiteboard.cjs +10 -6
  144. package/dist/components-shared-whiteboard.cjs.map +1 -1
  145. package/dist/components-shared-whiteboard.js +6 -6
  146. package/dist/heuristics.js +1 -1
  147. package/dist/human-plus/file-store.d.cts +17 -0
  148. package/dist/human-plus/file-store.d.ts +17 -0
  149. package/dist/human-plus/index.d.cts +61 -0
  150. package/dist/human-plus/index.d.ts +61 -0
  151. package/dist/human-plus-node.cjs +121 -0
  152. package/dist/human-plus-node.cjs.map +1 -0
  153. package/dist/human-plus-node.js +76 -0
  154. package/dist/human-plus-node.js.map +1 -0
  155. package/dist/human-plus.cjs +46 -0
  156. package/dist/human-plus.cjs.map +1 -0
  157. package/dist/human-plus.js +3 -0
  158. package/dist/human-plus.js.map +1 -0
  159. package/dist/index.cjs +337 -99
  160. package/dist/index.cjs.map +1 -1
  161. package/dist/index.d.cts +11 -9
  162. package/dist/index.d.ts +11 -9
  163. package/dist/index.js +35 -27
  164. package/dist/index.js.map +1 -1
  165. package/dist/mcp/index.d.cts +5 -5
  166. package/dist/mcp/index.d.ts +5 -5
  167. package/dist/mcp.cjs +8 -3
  168. package/dist/mcp.cjs.map +1 -1
  169. package/dist/mcp.js +1 -1
  170. package/dist/presence/index.d.cts +8 -1
  171. package/dist/presence/index.d.ts +8 -1
  172. package/dist/presence.cjs +1 -1
  173. package/dist/presence.cjs.map +1 -1
  174. package/dist/presence.js +1 -1
  175. package/dist/relay-server/index.d.cts +20 -0
  176. package/dist/relay-server/index.d.ts +20 -0
  177. package/dist/relay-server-cli.cjs +53 -9
  178. package/dist/relay-server-cli.cjs.map +1 -1
  179. package/dist/relay-server-cli.js +11 -3
  180. package/dist/relay-server-cli.js.map +1 -1
  181. package/dist/relay-server.cjs +43 -7
  182. package/dist/relay-server.cjs.map +1 -1
  183. package/dist/relay-server.js +1 -1
  184. package/dist/{server-FFZpYPyM.d.ts → server-CDX643Kz.d.ts} +4 -2
  185. package/dist/{server-Dr3j48CU.d.cts → server-DDD3IZ56.d.cts} +4 -2
  186. package/dist/sharing/index.d.cts +4 -4
  187. package/dist/sharing/index.d.ts +4 -4
  188. package/dist/sharing.cjs +5 -5
  189. package/dist/sharing.cjs.map +1 -1
  190. package/dist/sharing.js +1 -1
  191. package/dist/sheets-adapter.d.cts +3 -3
  192. package/dist/sheets-adapter.d.ts +3 -3
  193. package/dist/{token-CrJF76oH.d.cts → token-BZ8doMam.d.cts} +15 -2
  194. package/dist/{token-CrJF76oH.d.ts → token-BZ8doMam.d.ts} +15 -2
  195. package/dist/{tool-host-C_Vh17J_.d.ts → tool-host-CWTOJEm2.d.ts} +1 -1
  196. package/dist/{tool-host-IDecPrF1.d.cts → tool-host-EuYHYSSH.d.cts} +1 -1
  197. package/dist/{types-CBoQhR8L.d.ts → types-BKD6uf86.d.ts} +1 -1
  198. package/dist/{types-BDI_g5UG.d.ts → types-C58ZXs4K.d.cts} +6 -2
  199. package/dist/{types-BDI_g5UG.d.cts → types-C58ZXs4K.d.ts} +6 -2
  200. package/dist/{types-B0rmWyoR.d.cts → types-NFGP769G.d.cts} +1 -1
  201. package/dist/undo/index.d.cts +2 -2
  202. package/dist/undo/index.d.ts +2 -2
  203. package/dist/undo.cjs.map +1 -1
  204. package/dist/undo.js +3 -3
  205. package/package.json +13 -1
  206. package/dist/chunk-4KAIV6OD.js.map +0 -1
  207. package/dist/chunk-HWVVS6HE.js.map +0 -1
  208. package/dist/chunk-KMSRG5KH.js.map +0 -1
  209. package/dist/chunk-NN3ELTVO.js.map +0 -1
  210. package/dist/chunk-OIX2ANFS.js.map +0 -1
  211. package/dist/chunk-PCW2EK5G.js.map +0 -1
  212. package/dist/chunk-ULJL53DL.js.map +0 -1
  213. package/dist/chunk-XMMCSQ4I.js.map +0 -1
package/dist/mcp.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/mcp/types.ts","../src/mcp/tool-host.ts","../src/mcp/server.ts","../src/mcp/transports/in-process.ts","../src/mcp/transports/relay.ts"],"names":[],"mappings":"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","file":"mcp.cjs","sourcesContent":["/**\n * Minimal MCP (Model Context Protocol) types — covers the subset this\n * package implements: initialize, tools/list, tools/call, plus JSON-RPC.\n *\n * Aligned with the public MCP spec but trimmed to v0.1 needs. See\n * https://spec.modelcontextprotocol.io/ for the full surface.\n */\n\nexport type JsonValue =\n | string\n | number\n | boolean\n | null\n | { [key: string]: JsonValue }\n | JsonValue[];\n\nexport type JsonObject = { [key: string]: JsonValue };\n\nexport type JsonRpcId = string | number | null;\n\nexport type JsonRpcRequest = {\n jsonrpc: \"2.0\";\n id: JsonRpcId;\n method: string;\n params?: JsonObject;\n};\n\nexport type JsonRpcNotification = {\n jsonrpc: \"2.0\";\n method: string;\n params?: JsonObject;\n};\n\nexport type JsonRpcSuccess = {\n jsonrpc: \"2.0\";\n id: JsonRpcId;\n result: JsonValue;\n};\n\nexport type JsonRpcError = {\n jsonrpc: \"2.0\";\n id: JsonRpcId;\n error: { code: number; message: string; data?: JsonValue };\n};\n\nexport type JsonRpcMessage = JsonRpcRequest | JsonRpcNotification | JsonRpcSuccess | JsonRpcError;\n\nexport const JSONRPC_PARSE_ERROR = -32700;\nexport const JSONRPC_INVALID_REQUEST = -32600;\nexport const JSONRPC_METHOD_NOT_FOUND = -32601;\nexport const JSONRPC_INVALID_PARAMS = -32602;\nexport const JSONRPC_INTERNAL_ERROR = -32603;\n\nexport type ServerCapabilities = {\n tools?: { listChanged?: boolean };\n resources?: { listChanged?: boolean; subscribe?: boolean };\n prompts?: { listChanged?: boolean };\n logging?: Record<string, never>;\n};\n\nexport type ServerInfo = {\n name: string;\n version: string;\n title?: string;\n};\n\nexport type InitializeResult = {\n protocolVersion: string;\n capabilities: ServerCapabilities;\n serverInfo: ServerInfo;\n instructions?: string;\n};\n\nexport type ToolInputSchema = {\n type: \"object\";\n properties?: Record<string, JsonValue>;\n required?: string[];\n additionalProperties?: boolean;\n};\n\nexport type ToolDefinition = {\n name: string;\n title?: string;\n description?: string;\n inputSchema: ToolInputSchema;\n};\n\nexport type ContentBlock =\n | { type: \"text\"; text: string }\n | { type: \"image\"; data: string; mimeType: string }\n | { type: \"resource\"; resource: { uri: string; text?: string; mimeType?: string } };\n\nexport type CallToolResult = {\n content: ContentBlock[];\n isError?: boolean;\n /** Structured tool output — non-spec but useful for typed bridges. */\n structuredContent?: JsonValue;\n};\n\n/** Handler signature for a tool registered on the MicroMcpServer. */\nexport type ToolHandler = (args: JsonObject) => Promise<CallToolResult> | CallToolResult;\n\n/** Internal record kept by the server. */\nexport type RegisteredTool = {\n definition: ToolDefinition;\n handler: ToolHandler;\n};\n\nexport const MCP_PROTOCOL_VERSION = \"2025-06-18\";\n","import type {\n CallToolResult,\n JsonObject,\n RegisteredTool,\n ToolDefinition,\n ToolHandler,\n} from \"./types\";\n\n/**\n * ToolHost — the minimal surface a bridge needs to register its tools.\n *\n * Bridges (whiteboard, flow, sheets, code, charts, screens, scene, form)\n * speak only through this interface. The MCP server implements it\n * alongside its transport / JSON-RPC duties, and the standalone\n * {@link ToolRegistry} implements it for in-process agents that don't\n * need any of MCP's wire framing.\n *\n * Net effect: every bridge works equally well behind an MCP server\n * (browser ↔ external agents over SSE) or behind a plain registry\n * (in-process agent calling `host.callTool(\"sheet_set_cell\", { … })`).\n */\nexport interface ToolHost {\n /** Register a tool. Returns a disposer that unregisters it. */\n registerTool(definition: ToolDefinition, handler: ToolHandler): () => void;\n\n /** Look up an already-registered tool's definition + handler, or null. */\n getTool(name: string): RegisteredTool | null;\n\n /** Snapshot of all currently-registered tool definitions. */\n listTools(): ToolDefinition[];\n\n /**\n * Invoke a registered tool directly, bypassing any transport layer.\n * Throws if no tool is registered under `name`.\n *\n * This is the single entry point in-process agents use to drive the\n * surface — no JSON-RPC framing, no transport plumbing.\n */\n callTool(name: string, args?: JsonObject): Promise<CallToolResult>;\n}\n\n/**\n * Standalone in-memory ToolHost. Use this when no MCP server is needed —\n * e.g. an in-process agent that just wants to register the same bridges\n * and call them directly.\n *\n * Example:\n *\n * const host = new ToolRegistry();\n * registerSheetsBridge(host, { adapter });\n * const result = await host.callTool(\"sheet_set_cell\", { address: \"B3\", value: 42 });\n *\n * Pair with a MicroMcpServer in the same app to expose the same tools\n * to remote agents over SSE while in-process agents still get\n * zero-overhead direct calls.\n */\nexport class ToolRegistry implements ToolHost {\n protected readonly tools = new Map<string, RegisteredTool>();\n\n registerTool(definition: ToolDefinition, handler: ToolHandler): () => void {\n this.tools.set(definition.name, { definition, handler });\n this.onToolsChanged();\n return () => {\n if (this.tools.delete(definition.name)) this.onToolsChanged();\n };\n }\n\n getTool(name: string): RegisteredTool | null {\n return this.tools.get(name) ?? null;\n }\n\n listTools(): ToolDefinition[] {\n return Array.from(this.tools.values()).map((t) => t.definition);\n }\n\n async callTool(name: string, args: JsonObject = {}): Promise<CallToolResult> {\n const tool = this.tools.get(name);\n if (!tool) {\n throw new Error(`Unknown tool: ${name}`);\n }\n return tool.handler(args);\n }\n\n /**\n * Hook for subclasses (e.g. MicroMcpServer) to notify subscribers\n * when the tool catalog changes. Default no-op.\n */\n protected onToolsChanged(): void {}\n}\n","import {\n type CallToolResult,\n type JsonObject,\n type JsonRpcMessage,\n type JsonRpcRequest,\n type JsonRpcId,\n type RegisteredTool,\n type ServerCapabilities,\n type ServerInfo,\n type ToolDefinition,\n type ToolHandler,\n JSONRPC_INTERNAL_ERROR,\n JSONRPC_INVALID_PARAMS,\n JSONRPC_METHOD_NOT_FOUND,\n MCP_PROTOCOL_VERSION,\n} from \"./types\";\nimport { ToolRegistry } from \"./tool-host\";\n\nexport type McpServerOptions = {\n info: ServerInfo;\n /** Defaults to { tools: { listChanged: true } } */\n capabilities?: ServerCapabilities;\n /** Free-text instructions surfaced to clients during initialize. */\n instructions?: string;\n};\n\nexport type Transport = {\n /** Called by the server when it has a message to deliver to the client. */\n send: (message: JsonRpcMessage) => void;\n /** Called by the server when it's torn down so the transport can clean up. */\n close?: () => void;\n};\n\n/**\n * MicroMcpServer — protocol-level MCP server, transport-agnostic.\n *\n * Use it like:\n *\n * const server = new MicroMcpServer({ info: { name: \"session\", version: \"0.1\" } });\n * server.registerTool({ name: \"...\", inputSchema: { type: \"object\" } }, async (args) => ({...}));\n * const transport = new InProcessTransport();\n * server.attach(transport);\n * transport.deliver({ ... }); // client → server frames\n *\n * The same server can serve multiple transports (e.g. an in-process agent\n * AND a relayed external client) by attaching each one.\n */\nexport class MicroMcpServer extends ToolRegistry {\n private transports = new Set<Transport>();\n private notifyListChangedScheduled = false;\n\n readonly info: ServerInfo;\n readonly capabilities: ServerCapabilities;\n readonly instructions?: string;\n\n constructor(options: McpServerOptions) {\n super();\n this.info = options.info;\n this.capabilities = options.capabilities ?? { tools: { listChanged: true } };\n this.instructions = options.instructions;\n }\n\n attach(transport: Transport): () => void {\n this.transports.add(transport);\n return () => this.detach(transport);\n }\n\n detach(transport: Transport): void {\n if (this.transports.delete(transport)) {\n transport.close?.();\n }\n }\n\n unregisterTool(name: string): void {\n if (this.tools.delete(name)) {\n this.scheduleListChangedNotification();\n }\n }\n\n protected onToolsChanged(): void {\n this.scheduleListChangedNotification();\n }\n\n /**\n * Receive a JSON-RPC frame from a client (called by the transport).\n * The transport is responsible for sending the response back.\n */\n async receive(transport: Transport, message: JsonRpcMessage): Promise<void> {\n if (!(\"method\" in message)) return; // It's a response, not a request — ignore.\n\n const isNotification = !(\"id\" in message);\n if (isNotification) {\n // Notifications are fire-and-forget. We ignore unknown methods.\n return;\n }\n\n const request = message as JsonRpcRequest;\n try {\n const result = await this.handle(request);\n transport.send({ jsonrpc: \"2.0\", id: request.id, result });\n } catch (err) {\n transport.send({\n jsonrpc: \"2.0\",\n id: request.id,\n error: this.toRpcError(err),\n });\n }\n }\n\n private async handle(request: JsonRpcRequest): Promise<any> {\n const { method, params } = request;\n switch (method) {\n case \"initialize\":\n return {\n protocolVersion: MCP_PROTOCOL_VERSION,\n capabilities: this.capabilities,\n serverInfo: this.info,\n ...(this.instructions ? { instructions: this.instructions } : {}),\n };\n\n case \"tools/list\":\n return { tools: this.listTools() };\n\n case \"tools/call\": {\n const name = params?.name;\n const args = (params?.arguments ?? {}) as JsonObject;\n if (typeof name !== \"string\") {\n throw rpcError(JSONRPC_INVALID_PARAMS, \"tools/call requires `name`\");\n }\n const tool = this.tools.get(name);\n if (!tool) {\n throw rpcError(JSONRPC_METHOD_NOT_FOUND, `Unknown tool: ${name}`);\n }\n const result = await tool.handler(args);\n return result satisfies CallToolResult;\n }\n\n case \"ping\":\n return {};\n\n default:\n throw rpcError(JSONRPC_METHOD_NOT_FOUND, `Unsupported method: ${method}`);\n }\n }\n\n private scheduleListChangedNotification(): void {\n if (this.notifyListChangedScheduled) return;\n this.notifyListChangedScheduled = true;\n queueMicrotask(() => {\n this.notifyListChangedScheduled = false;\n this.broadcast({ jsonrpc: \"2.0\", method: \"notifications/tools/list_changed\" });\n });\n }\n\n private broadcast(message: JsonRpcMessage): void {\n for (const t of this.transports) t.send(message);\n }\n\n private toRpcError(err: unknown): { code: number; message: string; data?: any } {\n if (err && typeof err === \"object\" && \"code\" in err && \"message\" in err) {\n return err as any;\n }\n return {\n code: JSONRPC_INTERNAL_ERROR,\n message: err instanceof Error ? err.message : String(err),\n };\n }\n}\n\nexport function rpcError(code: number, message: string, data?: any) {\n return { code, message, ...(data !== undefined ? { data } : {}) };\n}\n\n/**\n * Helper to build a CallToolResult from a string or structured value.\n */\nexport function textResult(text: string, structured?: any): CallToolResult {\n return {\n content: [{ type: \"text\", text }],\n ...(structured !== undefined ? { structuredContent: structured } : {}),\n };\n}\n\nexport function errorResult(text: string): CallToolResult {\n return { content: [{ type: \"text\", text }], isError: true };\n}\n\n// Internal helper so the JsonRpcId import isn't dropped by tsup\ntype _KeepIdImport = JsonRpcId;\n","import type { JsonRpcMessage } from \"../types\";\nimport type { MicroMcpServer, Transport } from \"../server\";\n\n/**\n * InProcessTransport — direct function-call wiring between an in-page MCP\n * client (e.g. an embedded chat agent) and a MicroMcpServer running in\n * the same JS context. No serialization, no network.\n *\n * Usage:\n *\n * const t = new InProcessTransport();\n * server.attach(t);\n * t.onServerMessage((msg) => { ... }); // client subscribes\n * t.send({ jsonrpc: \"2.0\", id: 1, method: \"tools/list\" }); // client → server\n */\nexport class InProcessTransport implements Transport {\n private server?: MicroMcpServer;\n private listeners = new Set<(msg: JsonRpcMessage) => void>();\n\n /** Bind to a server. Called from the client's setup, not directly. */\n bindServer(server: MicroMcpServer): void {\n this.server = server;\n }\n\n /** Server → client (delivered to subscribed listeners). */\n send(message: JsonRpcMessage): void {\n for (const l of this.listeners) l(message);\n }\n\n /** Client → server. Awaitable so callers can flush. */\n async deliver(message: JsonRpcMessage): Promise<void> {\n if (!this.server) throw new Error(\"InProcessTransport has no bound server\");\n await this.server.receive(this, message);\n }\n\n /** Subscribe to messages the server pushes to this client. */\n onServerMessage(listener: (msg: JsonRpcMessage) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n close(): void {\n this.listeners.clear();\n }\n}\n\n/**\n * Convenience: create a server-attached in-process transport in one call.\n */\nexport function attachInProcess(server: MicroMcpServer): InProcessTransport {\n const transport = new InProcessTransport();\n transport.bindServer(server);\n server.attach(transport);\n return transport;\n}\n","import type { JsonRpcMessage } from \"../types\";\nimport type { MicroMcpServer, Transport } from \"../server\";\n\n/**\n * RelayTransport — wraps any duplex JSON-frame channel (e.g. a Reverb\n * websocket private channel, a WebRTC data channel) so external agents\n * can talk to a browser-side MicroMcpServer.\n *\n * The host app owns the actual channel. This class only handles framing\n * (JSON.stringify / JSON.parse) and the server contract.\n *\n * Channel contract:\n * - host calls `transport.deliverFromRemote(payload)` with each frame\n * it receives from the remote agent\n * - host implements `sendToRemote(frame)` so the transport can deliver\n * server → client frames outward\n *\n * See docs/relay-protocol.md for the wire format.\n */\nexport type RelayChannel = {\n sendToRemote: (frame: JsonRpcMessage) => void;\n /** Optional: notify the channel that the server is gone. */\n onClose?: () => void;\n};\n\nexport class RelayTransport implements Transport {\n private server?: MicroMcpServer;\n constructor(private channel: RelayChannel) {}\n\n bindServer(server: MicroMcpServer): void {\n this.server = server;\n }\n\n send(message: JsonRpcMessage): void {\n this.channel.sendToRemote(message);\n }\n\n /**\n * Host calls this with each frame received from the remote agent. Accepts\n * either a parsed object or a raw JSON string.\n */\n async deliverFromRemote(payload: JsonRpcMessage | string): Promise<void> {\n if (!this.server) throw new Error(\"RelayTransport has no bound server\");\n const message = typeof payload === \"string\"\n ? (JSON.parse(payload) as JsonRpcMessage)\n : payload;\n await this.server.receive(this, message);\n }\n\n close(): void {\n this.channel.onClose?.();\n }\n}\n\n/**\n * Convenience wiring. Returns the bound transport.\n */\nexport function attachRelay(server: MicroMcpServer, channel: RelayChannel): RelayTransport {\n const transport = new RelayTransport(channel);\n transport.bindServer(server);\n server.attach(transport);\n return transport;\n}\n"]}
1
+ {"version":3,"sources":["../src/mcp/types.ts","../src/mcp/tool-host.ts","../src/mcp/server.ts","../src/mcp/transports/in-process.ts","../src/mcp/transports/relay.ts"],"names":[],"mappings":"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","file":"mcp.cjs","sourcesContent":["/**\n * Minimal MCP (Model Context Protocol) types — covers the subset this\n * package implements: initialize, tools/list, tools/call, plus JSON-RPC.\n *\n * Aligned with the public MCP spec but trimmed to v0.1 needs. See\n * https://spec.modelcontextprotocol.io/ for the full surface.\n */\n\nexport type JsonValue =\n | string\n | number\n | boolean\n | null\n | { [key: string]: JsonValue }\n | JsonValue[];\n\nexport type JsonObject = { [key: string]: JsonValue };\n\nexport type JsonRpcId = string | number | null;\n\nexport type JsonRpcRequest = {\n jsonrpc: \"2.0\";\n id: JsonRpcId;\n method: string;\n params?: JsonObject;\n};\n\nexport type JsonRpcNotification = {\n jsonrpc: \"2.0\";\n method: string;\n params?: JsonObject;\n};\n\nexport type JsonRpcSuccess = {\n jsonrpc: \"2.0\";\n id: JsonRpcId;\n result: JsonValue;\n};\n\nexport type JsonRpcError = {\n jsonrpc: \"2.0\";\n id: JsonRpcId;\n error: { code: number; message: string; data?: JsonValue };\n};\n\nexport type JsonRpcMessage = JsonRpcRequest | JsonRpcNotification | JsonRpcSuccess | JsonRpcError;\n\nexport const JSONRPC_PARSE_ERROR = -32700;\nexport const JSONRPC_INVALID_REQUEST = -32600;\nexport const JSONRPC_METHOD_NOT_FOUND = -32601;\nexport const JSONRPC_INVALID_PARAMS = -32602;\nexport const JSONRPC_INTERNAL_ERROR = -32603;\n\nexport type ServerCapabilities = {\n tools?: { listChanged?: boolean };\n resources?: { listChanged?: boolean; subscribe?: boolean };\n prompts?: { listChanged?: boolean };\n logging?: Record<string, never>;\n experimental?: Record<string, JsonValue>;\n};\n\nexport type ServerInfo = {\n name: string;\n version: string;\n title?: string;\n};\n\nexport type InitializeResult = {\n protocolVersion: string;\n capabilities: ServerCapabilities;\n serverInfo: ServerInfo;\n instructions?: string;\n};\n\nexport type ToolInputSchema = {\n type: \"object\";\n properties?: Record<string, JsonValue>;\n required?: string[];\n additionalProperties?: boolean;\n};\n\nexport type ToolDefinition = {\n name: string;\n title?: string;\n description?: string;\n inputSchema: ToolInputSchema;\n};\n\nexport type ContentBlock =\n | { type: \"text\"; text: string }\n | { type: \"image\"; data: string; mimeType: string }\n | { type: \"resource\"; resource: { uri: string; text?: string; mimeType?: string } };\n\nexport type CallToolResult = {\n content: ContentBlock[];\n isError?: boolean;\n /** Structured tool output — non-spec but useful for typed bridges. */\n structuredContent?: JsonValue;\n};\n\n/** Handler signature for a tool registered on the MicroMcpServer. */\nexport type ToolCallContext = { transport?: unknown };\nexport type ToolHandler = (args: JsonObject, context?: ToolCallContext) => Promise<CallToolResult> | CallToolResult;\n\n/** Internal record kept by the server. */\nexport type RegisteredTool = {\n definition: ToolDefinition;\n handler: ToolHandler;\n};\n\nexport const MCP_PROTOCOL_VERSION = \"2025-06-18\";\n","import type {\n CallToolResult,\n JsonObject,\n RegisteredTool,\n ToolDefinition,\n ToolHandler,\n} from \"./types\";\n\n/**\n * ToolHost — the minimal surface a bridge needs to register its tools.\n *\n * Bridges (whiteboard, flow, sheets, code, charts, screens, scene, form)\n * speak only through this interface. The MCP server implements it\n * alongside its transport / JSON-RPC duties, and the standalone\n * {@link ToolRegistry} implements it for in-process agents that don't\n * need any of MCP's wire framing.\n *\n * Net effect: every bridge works equally well behind an MCP server\n * (browser ↔ external agents over SSE) or behind a plain registry\n * (in-process agent calling `host.callTool(\"sheet_set_cell\", { … })`).\n */\nexport interface ToolHost {\n /** Register a tool. Returns a disposer that unregisters it. */\n registerTool(definition: ToolDefinition, handler: ToolHandler): () => void;\n\n /** Look up an already-registered tool's definition + handler, or null. */\n getTool(name: string): RegisteredTool | null;\n\n /** Snapshot of all currently-registered tool definitions. */\n listTools(): ToolDefinition[];\n\n /**\n * Invoke a registered tool directly, bypassing any transport layer.\n * Throws if no tool is registered under `name`.\n *\n * This is the single entry point in-process agents use to drive the\n * surface — no JSON-RPC framing, no transport plumbing.\n */\n callTool(name: string, args?: JsonObject): Promise<CallToolResult>;\n}\n\n/**\n * Standalone in-memory ToolHost. Use this when no MCP server is needed —\n * e.g. an in-process agent that just wants to register the same bridges\n * and call them directly.\n *\n * Example:\n *\n * const host = new ToolRegistry();\n * registerSheetsBridge(host, { adapter });\n * const result = await host.callTool(\"sheet_set_cell\", { address: \"B3\", value: 42 });\n *\n * Pair with a MicroMcpServer in the same app to expose the same tools\n * to remote agents over SSE while in-process agents still get\n * zero-overhead direct calls.\n */\nexport class ToolRegistry implements ToolHost {\n protected readonly tools = new Map<string, RegisteredTool>();\n\n registerTool(definition: ToolDefinition, handler: ToolHandler): () => void {\n this.tools.set(definition.name, { definition, handler });\n this.onToolsChanged();\n return () => {\n if (this.tools.delete(definition.name)) this.onToolsChanged();\n };\n }\n\n getTool(name: string): RegisteredTool | null {\n return this.tools.get(name) ?? null;\n }\n\n listTools(): ToolDefinition[] {\n return Array.from(this.tools.values()).map((t) => t.definition);\n }\n\n async callTool(name: string, args: JsonObject = {}): Promise<CallToolResult> {\n const tool = this.tools.get(name);\n if (!tool) {\n throw new Error(`Unknown tool: ${name}`);\n }\n return tool.handler(args);\n }\n\n /**\n * Hook for subclasses (e.g. MicroMcpServer) to notify subscribers\n * when the tool catalog changes. Default no-op.\n */\n protected onToolsChanged(): void {}\n}\n","import {\n type CallToolResult,\n type JsonObject,\n type JsonRpcMessage,\n type JsonRpcRequest,\n type JsonRpcId,\n type RegisteredTool,\n type ServerCapabilities,\n type ServerInfo,\n type ToolDefinition,\n type ToolHandler,\n JSONRPC_INTERNAL_ERROR,\n JSONRPC_INVALID_PARAMS,\n JSONRPC_METHOD_NOT_FOUND,\n MCP_PROTOCOL_VERSION,\n} from \"./types\";\nimport { ToolRegistry } from \"./tool-host\";\n\nexport type McpServerOptions = {\n info: ServerInfo;\n /** Defaults to { tools: { listChanged: true } } */\n capabilities?: ServerCapabilities;\n /** Free-text instructions surfaced to clients during initialize. */\n instructions?: string;\n};\n\nexport type Transport = {\n /** Called by the server when it has a message to deliver to the client. */\n send: (message: JsonRpcMessage) => void;\n /** Called by the server when it's torn down so the transport can clean up. */\n close?: () => void;\n};\n\n/**\n * MicroMcpServer — protocol-level MCP server, transport-agnostic.\n *\n * Use it like:\n *\n * const server = new MicroMcpServer({ info: { name: \"session\", version: \"0.1\" } });\n * server.registerTool({ name: \"...\", inputSchema: { type: \"object\" } }, async (args) => ({...}));\n * const transport = new InProcessTransport();\n * server.attach(transport);\n * transport.deliver({ ... }); // client → server frames\n *\n * The same server can serve multiple transports (e.g. an in-process agent\n * AND a relayed external client) by attaching each one.\n */\nexport class MicroMcpServer extends ToolRegistry {\n private transports = new Set<Transport>();\n private notifyListChangedScheduled = false;\n\n readonly info: ServerInfo;\n readonly capabilities: ServerCapabilities;\n readonly instructions?: string;\n\n constructor(options: McpServerOptions) {\n super();\n this.info = options.info;\n this.capabilities = options.capabilities ?? { tools: { listChanged: true } };\n this.instructions = options.instructions;\n }\n\n attach(transport: Transport): () => void {\n this.transports.add(transport);\n return () => this.detach(transport);\n }\n\n detach(transport: Transport): void {\n if (this.transports.delete(transport)) {\n transport.close?.();\n }\n }\n\n unregisterTool(name: string): void {\n if (this.tools.delete(name)) {\n this.scheduleListChangedNotification();\n }\n }\n\n protected onToolsChanged(): void {\n this.scheduleListChangedNotification();\n }\n\n /**\n * Receive a JSON-RPC frame from a client (called by the transport).\n * The transport is responsible for sending the response back.\n */\n async receive(transport: Transport, message: JsonRpcMessage): Promise<void> {\n if (!(\"method\" in message)) return; // It's a response, not a request — ignore.\n\n const isNotification = !(\"id\" in message);\n if (isNotification) {\n // Notifications are fire-and-forget. We ignore unknown methods.\n return;\n }\n\n const request = message as JsonRpcRequest;\n try {\n const result = await this.handle(request, transport);\n transport.send({ jsonrpc: \"2.0\", id: request.id, result });\n } catch (err) {\n transport.send({\n jsonrpc: \"2.0\",\n id: request.id,\n error: this.toRpcError(err),\n });\n }\n }\n\n private async handle(request: JsonRpcRequest, transport: Transport): Promise<any> {\n const { method, params } = request;\n switch (method) {\n case \"initialize\":\n return {\n protocolVersion: MCP_PROTOCOL_VERSION,\n capabilities: this.capabilities,\n serverInfo: this.info,\n ...(this.instructions ? { instructions: this.instructions } : {}),\n };\n\n case \"tools/list\":\n return { tools: this.listTools() };\n\n case \"tools/call\": {\n const name = params?.name;\n const args = (params?.arguments ?? {}) as JsonObject;\n if (typeof name !== \"string\") {\n throw rpcError(JSONRPC_INVALID_PARAMS, \"tools/call requires `name`\");\n }\n const tool = this.tools.get(name);\n if (!tool) {\n throw rpcError(JSONRPC_METHOD_NOT_FOUND, `Unknown tool: ${name}`);\n }\n const result = await tool.handler(args, { transport });\n return result satisfies CallToolResult;\n }\n\n case \"ping\":\n return {};\n\n default:\n throw rpcError(JSONRPC_METHOD_NOT_FOUND, `Unsupported method: ${method}`);\n }\n }\n\n private scheduleListChangedNotification(): void {\n if (this.notifyListChangedScheduled) return;\n this.notifyListChangedScheduled = true;\n queueMicrotask(() => {\n this.notifyListChangedScheduled = false;\n this.broadcast({ jsonrpc: \"2.0\", method: \"notifications/tools/list_changed\" });\n });\n }\n\n /** Send an application notification to every client, or one attached transport. */\n notify(message: JsonRpcMessage, transport?: Transport): void {\n if (transport) transport.send(message);\n else this.broadcast(message);\n }\n\n private broadcast(message: JsonRpcMessage): void {\n for (const t of this.transports) t.send(message);\n }\n\n private toRpcError(err: unknown): { code: number; message: string; data?: any } {\n if (err && typeof err === \"object\" && \"code\" in err && \"message\" in err) {\n return err as any;\n }\n return {\n code: JSONRPC_INTERNAL_ERROR,\n message: err instanceof Error ? err.message : String(err),\n };\n }\n}\n\nexport function rpcError(code: number, message: string, data?: any) {\n return { code, message, ...(data !== undefined ? { data } : {}) };\n}\n\n/**\n * Helper to build a CallToolResult from a string or structured value.\n */\nexport function textResult(text: string, structured?: any): CallToolResult {\n return {\n content: [{ type: \"text\", text }],\n ...(structured !== undefined ? { structuredContent: structured } : {}),\n };\n}\n\nexport function errorResult(text: string): CallToolResult {\n return { content: [{ type: \"text\", text }], isError: true };\n}\n\n// Internal helper so the JsonRpcId import isn't dropped by tsup\ntype _KeepIdImport = JsonRpcId;\n","import type { JsonRpcMessage } from \"../types\";\nimport type { MicroMcpServer, Transport } from \"../server\";\n\n/**\n * InProcessTransport — direct function-call wiring between an in-page MCP\n * client (e.g. an embedded chat agent) and a MicroMcpServer running in\n * the same JS context. No serialization, no network.\n *\n * Usage:\n *\n * const t = new InProcessTransport();\n * server.attach(t);\n * t.onServerMessage((msg) => { ... }); // client subscribes\n * t.send({ jsonrpc: \"2.0\", id: 1, method: \"tools/list\" }); // client → server\n */\nexport class InProcessTransport implements Transport {\n private server?: MicroMcpServer;\n private listeners = new Set<(msg: JsonRpcMessage) => void>();\n\n /** Bind to a server. Called from the client's setup, not directly. */\n bindServer(server: MicroMcpServer): void {\n this.server = server;\n }\n\n /** Server → client (delivered to subscribed listeners). */\n send(message: JsonRpcMessage): void {\n for (const l of this.listeners) l(message);\n }\n\n /** Client → server. Awaitable so callers can flush. */\n async deliver(message: JsonRpcMessage): Promise<void> {\n if (!this.server) throw new Error(\"InProcessTransport has no bound server\");\n await this.server.receive(this, message);\n }\n\n /** Subscribe to messages the server pushes to this client. */\n onServerMessage(listener: (msg: JsonRpcMessage) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n close(): void {\n this.listeners.clear();\n }\n}\n\n/**\n * Convenience: create a server-attached in-process transport in one call.\n */\nexport function attachInProcess(server: MicroMcpServer): InProcessTransport {\n const transport = new InProcessTransport();\n transport.bindServer(server);\n server.attach(transport);\n return transport;\n}\n","import type { JsonRpcMessage } from \"../types\";\nimport type { MicroMcpServer, Transport } from \"../server\";\n\n/**\n * RelayTransport — wraps any duplex JSON-frame channel (e.g. a Reverb\n * websocket private channel, a WebRTC data channel) so external agents\n * can talk to a browser-side MicroMcpServer.\n *\n * The host app owns the actual channel. This class only handles framing\n * (JSON.stringify / JSON.parse) and the server contract.\n *\n * Channel contract:\n * - host calls `transport.deliverFromRemote(payload)` with each frame\n * it receives from the remote agent\n * - host implements `sendToRemote(frame)` so the transport can deliver\n * server → client frames outward\n *\n * See docs/relay-protocol.md for the wire format.\n */\nexport type RelayChannel = {\n sendToRemote: (frame: JsonRpcMessage) => void;\n /** Optional: notify the channel that the server is gone. */\n onClose?: () => void;\n};\n\nexport class RelayTransport implements Transport {\n private server?: MicroMcpServer;\n constructor(private channel: RelayChannel) {}\n\n bindServer(server: MicroMcpServer): void {\n this.server = server;\n }\n\n send(message: JsonRpcMessage): void {\n this.channel.sendToRemote(message);\n }\n\n /**\n * Host calls this with each frame received from the remote agent. Accepts\n * either a parsed object or a raw JSON string.\n */\n async deliverFromRemote(payload: JsonRpcMessage | string): Promise<void> {\n if (!this.server) throw new Error(\"RelayTransport has no bound server\");\n const message = typeof payload === \"string\"\n ? (JSON.parse(payload) as JsonRpcMessage)\n : payload;\n await this.server.receive(this, message);\n }\n\n close(): void {\n this.channel.onClose?.();\n }\n}\n\n/**\n * Convenience wiring. Returns the bound transport.\n */\nexport function attachRelay(server: MicroMcpServer, channel: RelayChannel): RelayTransport {\n const transport = new RelayTransport(channel);\n transport.bindServer(server);\n server.attach(transport);\n return transport;\n}\n"]}
package/dist/mcp.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { RelayTransport, attachRelay } from './chunk-G6N2TQVO.js';
2
2
  export { InProcessTransport, attachInProcess } from './chunk-AFUULW5E.js';
3
- export { JSONRPC_INTERNAL_ERROR, JSONRPC_INVALID_PARAMS, JSONRPC_INVALID_REQUEST, JSONRPC_METHOD_NOT_FOUND, JSONRPC_PARSE_ERROR, MCP_PROTOCOL_VERSION, MicroMcpServer, errorResult, rpcError, textResult } from './chunk-4KAIV6OD.js';
3
+ export { JSONRPC_INTERNAL_ERROR, JSONRPC_INVALID_PARAMS, JSONRPC_INVALID_REQUEST, JSONRPC_METHOD_NOT_FOUND, JSONRPC_PARSE_ERROR, MCP_PROTOCOL_VERSION, MicroMcpServer, errorResult, rpcError, textResult } from './chunk-DF3PLL2E.js';
4
4
  //# sourceMappingURL=mcp.js.map
5
5
  //# sourceMappingURL=mcp.js.map
@@ -1,6 +1,6 @@
1
1
  import { AutoTarget, ActivityFilter, AutoActivityEvent } from '@particle-academy/fancy-auto-common';
2
2
  export { ActivityFilter, AutoActivityEvent as AgentActivityEvent, AutoActivityListener as AgentActivityListener, AutoTarget as AgentTarget, AutoTargetKind as AgentTargetKind, emitActivity, onActivity, readActivityHistory, resetActivityRegistry } from '@particle-academy/fancy-auto-common';
3
- import { C as CallToolResult } from '../types-BDI_g5UG.cjs';
3
+ import { C as CallToolResult } from '../types-C58ZXs4K.cjs';
4
4
 
5
5
  type ActivityAgent = {
6
6
  id: string;
@@ -52,6 +52,13 @@ declare function wrapToolWithActivity<TArgs = Record<string, unknown>>(handler:
52
52
  resolveTarget?: ActivityTargetResolver<TArgs>;
53
53
  /** Optional ttl override. */
54
54
  ttlMs?: number;
55
+ /**
56
+ * Build the `meta` broadcast to all relay peers. Defaults to the tool's full
57
+ * `structuredContent`. Override to REDACT sensitive fields before fan-out —
58
+ * e.g. the terminal bridge strips raw command/keystroke bytes so a command
59
+ * line (or its secrets) is never broadcast verbatim to every peer.
60
+ */
61
+ buildMeta?: (result: CallToolResult) => Record<string, unknown> | undefined;
55
62
  }): ToolHandler<TArgs>;
56
63
 
57
64
  /**
@@ -1,6 +1,6 @@
1
1
  import { AutoTarget, ActivityFilter, AutoActivityEvent } from '@particle-academy/fancy-auto-common';
2
2
  export { ActivityFilter, AutoActivityEvent as AgentActivityEvent, AutoActivityListener as AgentActivityListener, AutoTarget as AgentTarget, AutoTargetKind as AgentTargetKind, emitActivity, onActivity, readActivityHistory, resetActivityRegistry } from '@particle-academy/fancy-auto-common';
3
- import { C as CallToolResult } from '../types-BDI_g5UG.js';
3
+ import { C as CallToolResult } from '../types-C58ZXs4K.js';
4
4
 
5
5
  type ActivityAgent = {
6
6
  id: string;
@@ -52,6 +52,13 @@ declare function wrapToolWithActivity<TArgs = Record<string, unknown>>(handler:
52
52
  resolveTarget?: ActivityTargetResolver<TArgs>;
53
53
  /** Optional ttl override. */
54
54
  ttlMs?: number;
55
+ /**
56
+ * Build the `meta` broadcast to all relay peers. Defaults to the tool's full
57
+ * `structuredContent`. Override to REDACT sensitive fields before fan-out —
58
+ * e.g. the terminal bridge strips raw command/keystroke bytes so a command
59
+ * line (or its secrets) is never broadcast verbatim to every peer.
60
+ */
61
+ buildMeta?: (result: CallToolResult) => Record<string, unknown> | undefined;
55
62
  }): ToolHandler<TArgs>;
56
63
 
57
64
  /**
package/dist/presence.cjs CHANGED
@@ -24,7 +24,7 @@ function wrapToolWithActivity(handler, options) {
24
24
  target: { ...target, kind: target.kind ?? options.kind, screenId: target.screenId ?? options.screenId },
25
25
  action: options.toolName,
26
26
  timestamp: Date.now(),
27
- meta: extractMeta(result),
27
+ meta: options.buildMeta ? options.buildMeta(result) : extractMeta(result),
28
28
  ttlMs: options.ttlMs
29
29
  });
30
30
  return result;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/presence/wrap-tool-with-activity.ts","../src/presence/use-agent-activity.ts"],"names":["emitActivity","useState","readActivityHistory","useEffect","onActivity"],"mappings":"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","file":"presence.cjs","sourcesContent":["import type { CallToolResult } from \"../mcp/types\";\nimport { emitActivity } from \"./registry\";\nimport type { AgentTarget } from \"./types\";\n\nexport type ActivityAgent = { id: string; name?: string; color?: string };\n\nexport type ActivityResolverContext<TArgs = Record<string, unknown>> = {\n /** Tool name as registered. */\n toolName: string;\n /** Arguments the tool was called with. */\n args: TArgs;\n /** The CallToolResult the underlying handler produced. */\n result: CallToolResult;\n};\n\n/**\n * Resolves an `AgentTarget` for an executed tool. Bridges declare one of\n * these per registration so the wrapper knows which surface / element /\n * screen the activity belongs to.\n *\n * The resolver runs AFTER the tool handler so it can inspect the result\n * (e.g. read a newly-created item id from `structuredContent`).\n */\nexport type ActivityTargetResolver<TArgs = Record<string, unknown>> = (\n ctx: ActivityResolverContext<TArgs>,\n) => AgentTarget | null;\n\nexport type ToolHandler<TArgs = Record<string, unknown>> = (\n args: TArgs,\n) => Promise<CallToolResult> | CallToolResult;\n\n/**\n * wrapToolWithActivity — decorate a bridge tool handler so every successful\n * call emits an `AgentActivityEvent`. Returns a new handler with the same shape.\n *\n * Usage in a bridge:\n *\n * server.registerTool(\n * definition,\n * wrapToolWithActivity(\n * handler,\n * { agent, kind: \"whiteboard\", resolveTarget: ({ args }) => ({\n * kind: \"whiteboard\", elementId: args.id as string,\n * }) },\n * ),\n * );\n */\nexport function wrapToolWithActivity<TArgs = Record<string, unknown>>(\n handler: ToolHandler<TArgs>,\n options: {\n toolName: string;\n agent: ActivityAgent;\n /** Optional fancy-screens screen id this bridge is scoped to. */\n screenId?: string;\n /** Default target kind if the resolver returns one without `kind`. */\n kind: AgentTarget[\"kind\"];\n /** Per-call resolver. Return `null` to skip emitting (e.g. for read-only tools). */\n resolveTarget?: ActivityTargetResolver<TArgs>;\n /** Optional ttl override. */\n ttlMs?: number;\n },\n): ToolHandler<TArgs> {\n return async (args) => {\n const result = await handler(args);\n if (result.isError) return result;\n\n let target: AgentTarget | null;\n if (options.resolveTarget) {\n target = options.resolveTarget({ toolName: options.toolName, args, result });\n } else {\n target = { kind: options.kind, screenId: options.screenId };\n }\n if (!target) return result;\n\n emitActivity({\n agentId: options.agent.id,\n agentName: options.agent.name,\n agentColor: options.agent.color,\n target: { ...target, kind: target.kind ?? options.kind, screenId: target.screenId ?? options.screenId },\n action: options.toolName,\n timestamp: Date.now(),\n meta: extractMeta(result),\n ttlMs: options.ttlMs,\n });\n return result;\n };\n}\n\nfunction extractMeta(result: CallToolResult): Record<string, unknown> | undefined {\n const sc = result.structuredContent;\n if (sc && typeof sc === \"object\" && !Array.isArray(sc)) {\n return sc as Record<string, unknown>;\n }\n return undefined;\n}\n","import { useEffect, useState } from \"react\";\nimport { onActivity, readActivityHistory } from \"./registry\";\nimport type { ActivityFilter, AgentActivityEvent } from \"./types\";\n\n/**\n * useAgentActivity — React subscription to the in-process activity stream.\n *\n * Returns:\n * - `events`: capped scrollback of recent events matching the filter\n * - `latest`: the most recent event (handy for transient highlights)\n */\nexport function useAgentActivity(\n filter?: ActivityFilter,\n options: { capacity?: number } = {},\n): { events: AgentActivityEvent[]; latest: AgentActivityEvent | null } {\n const cap = options.capacity ?? 50;\n const [events, setEvents] = useState<AgentActivityEvent[]>(() => readActivityHistory(filter).slice(-cap));\n\n useEffect(() => {\n setEvents(readActivityHistory(filter).slice(-cap));\n return onActivity((event) => {\n setEvents((prev) => {\n const next = prev.length >= cap ? prev.slice(prev.length - cap + 1) : prev.slice();\n next.push(event);\n return next;\n });\n }, filter);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [filter?.agentId, filter?.screenId, filter?.kind, cap]);\n\n return { events, latest: events.length > 0 ? events[events.length - 1] : null };\n}\n\n/**\n * useAgentActivityForScreen — convenience wrapper that filters by screen id.\n * Drives \"agent is here\" badges in fancy-screens-based shells.\n */\nexport function useAgentActivityForScreen(\n screenId: string,\n options: { capacity?: number } = {},\n): { events: AgentActivityEvent[]; latest: AgentActivityEvent | null; isAgentActive: boolean } {\n const { events, latest } = useAgentActivity({ screenId }, options);\n const fadeAfter = latest?.ttlMs ?? 1500;\n const [isAgentActive, setActive] = useState(false);\n\n useEffect(() => {\n if (!latest) {\n setActive(false);\n return;\n }\n setActive(true);\n const timer = setTimeout(() => setActive(false), fadeAfter);\n return () => clearTimeout(timer);\n }, [latest, fadeAfter]);\n\n return { events, latest, isAgentActive };\n}\n"]}
1
+ {"version":3,"sources":["../src/presence/wrap-tool-with-activity.ts","../src/presence/use-agent-activity.ts"],"names":["emitActivity","useState","readActivityHistory","useEffect","onActivity"],"mappings":"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","file":"presence.cjs","sourcesContent":["import type { CallToolResult } from \"../mcp/types\";\nimport { emitActivity } from \"./registry\";\nimport type { AgentTarget } from \"./types\";\n\nexport type ActivityAgent = { id: string; name?: string; color?: string };\n\nexport type ActivityResolverContext<TArgs = Record<string, unknown>> = {\n /** Tool name as registered. */\n toolName: string;\n /** Arguments the tool was called with. */\n args: TArgs;\n /** The CallToolResult the underlying handler produced. */\n result: CallToolResult;\n};\n\n/**\n * Resolves an `AgentTarget` for an executed tool. Bridges declare one of\n * these per registration so the wrapper knows which surface / element /\n * screen the activity belongs to.\n *\n * The resolver runs AFTER the tool handler so it can inspect the result\n * (e.g. read a newly-created item id from `structuredContent`).\n */\nexport type ActivityTargetResolver<TArgs = Record<string, unknown>> = (\n ctx: ActivityResolverContext<TArgs>,\n) => AgentTarget | null;\n\nexport type ToolHandler<TArgs = Record<string, unknown>> = (\n args: TArgs,\n) => Promise<CallToolResult> | CallToolResult;\n\n/**\n * wrapToolWithActivity — decorate a bridge tool handler so every successful\n * call emits an `AgentActivityEvent`. Returns a new handler with the same shape.\n *\n * Usage in a bridge:\n *\n * server.registerTool(\n * definition,\n * wrapToolWithActivity(\n * handler,\n * { agent, kind: \"whiteboard\", resolveTarget: ({ args }) => ({\n * kind: \"whiteboard\", elementId: args.id as string,\n * }) },\n * ),\n * );\n */\nexport function wrapToolWithActivity<TArgs = Record<string, unknown>>(\n handler: ToolHandler<TArgs>,\n options: {\n toolName: string;\n agent: ActivityAgent;\n /** Optional fancy-screens screen id this bridge is scoped to. */\n screenId?: string;\n /** Default target kind if the resolver returns one without `kind`. */\n kind: AgentTarget[\"kind\"];\n /** Per-call resolver. Return `null` to skip emitting (e.g. for read-only tools). */\n resolveTarget?: ActivityTargetResolver<TArgs>;\n /** Optional ttl override. */\n ttlMs?: number;\n /**\n * Build the `meta` broadcast to all relay peers. Defaults to the tool's full\n * `structuredContent`. Override to REDACT sensitive fields before fan-out —\n * e.g. the terminal bridge strips raw command/keystroke bytes so a command\n * line (or its secrets) is never broadcast verbatim to every peer.\n */\n buildMeta?: (result: CallToolResult) => Record<string, unknown> | undefined;\n },\n): ToolHandler<TArgs> {\n return async (args) => {\n const result = await handler(args);\n if (result.isError) return result;\n\n let target: AgentTarget | null;\n if (options.resolveTarget) {\n target = options.resolveTarget({ toolName: options.toolName, args, result });\n } else {\n target = { kind: options.kind, screenId: options.screenId };\n }\n if (!target) return result;\n\n emitActivity({\n agentId: options.agent.id,\n agentName: options.agent.name,\n agentColor: options.agent.color,\n target: { ...target, kind: target.kind ?? options.kind, screenId: target.screenId ?? options.screenId },\n action: options.toolName,\n timestamp: Date.now(),\n meta: options.buildMeta ? options.buildMeta(result) : extractMeta(result),\n ttlMs: options.ttlMs,\n });\n return result;\n };\n}\n\nfunction extractMeta(result: CallToolResult): Record<string, unknown> | undefined {\n const sc = result.structuredContent;\n if (sc && typeof sc === \"object\" && !Array.isArray(sc)) {\n return sc as Record<string, unknown>;\n }\n return undefined;\n}\n","import { useEffect, useState } from \"react\";\nimport { onActivity, readActivityHistory } from \"./registry\";\nimport type { ActivityFilter, AgentActivityEvent } from \"./types\";\n\n/**\n * useAgentActivity — React subscription to the in-process activity stream.\n *\n * Returns:\n * - `events`: capped scrollback of recent events matching the filter\n * - `latest`: the most recent event (handy for transient highlights)\n */\nexport function useAgentActivity(\n filter?: ActivityFilter,\n options: { capacity?: number } = {},\n): { events: AgentActivityEvent[]; latest: AgentActivityEvent | null } {\n const cap = options.capacity ?? 50;\n const [events, setEvents] = useState<AgentActivityEvent[]>(() => readActivityHistory(filter).slice(-cap));\n\n useEffect(() => {\n setEvents(readActivityHistory(filter).slice(-cap));\n return onActivity((event) => {\n setEvents((prev) => {\n const next = prev.length >= cap ? prev.slice(prev.length - cap + 1) : prev.slice();\n next.push(event);\n return next;\n });\n }, filter);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [filter?.agentId, filter?.screenId, filter?.kind, cap]);\n\n return { events, latest: events.length > 0 ? events[events.length - 1] : null };\n}\n\n/**\n * useAgentActivityForScreen — convenience wrapper that filters by screen id.\n * Drives \"agent is here\" badges in fancy-screens-based shells.\n */\nexport function useAgentActivityForScreen(\n screenId: string,\n options: { capacity?: number } = {},\n): { events: AgentActivityEvent[]; latest: AgentActivityEvent | null; isAgentActive: boolean } {\n const { events, latest } = useAgentActivity({ screenId }, options);\n const fadeAfter = latest?.ttlMs ?? 1500;\n const [isAgentActive, setActive] = useState(false);\n\n useEffect(() => {\n if (!latest) {\n setActive(false);\n return;\n }\n setActive(true);\n const timer = setTimeout(() => setActive(false), fadeAfter);\n return () => clearTimeout(timer);\n }, [latest, fadeAfter]);\n\n return { events, latest, isAgentActive };\n}\n"]}
package/dist/presence.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { useAgentActivity, useAgentActivityForScreen } from './chunk-UCKJAUBY.js';
2
- export { wrapToolWithActivity } from './chunk-ULJL53DL.js';
2
+ export { wrapToolWithActivity } from './chunk-2VENWGLZ.js';
3
3
  export { emitActivity, onActivity, readActivityHistory, resetActivityRegistry } from './chunk-C3TYI5TJ.js';
4
4
  //# sourceMappingURL=presence.js.map
5
5
  //# sourceMappingURL=presence.js.map
@@ -77,7 +77,18 @@ declare class RelayBroker {
77
77
  subscribe(id: string, token: string, direction: Direction): SubscribeResult;
78
78
  private getDirSubs;
79
79
  private fanOut;
80
+ /**
81
+ * Validate that a peer-supplied payload is a well-formed JSON-RPC 2.0 frame
82
+ * (or batch) before it is fanned out. Previously this was a substring match on
83
+ * `"jsonrpc"`, which let any token holder inject spoofed frames — a forged
84
+ * `notifications/peer_joined` / `peer_left` (broker-reserved presence control),
85
+ * a fake `notifications/agent_activity {source:"user"}` ("human took control"),
86
+ * or a forged response with a matching id. We now parse + shape-check, and
87
+ * reject the broker-reserved control methods (the broker emits those itself,
88
+ * never a peer).
89
+ */
80
90
  private isFrame;
91
+ private isValidFrame;
81
92
  private reap;
82
93
  }
83
94
  type SubscribeResult = {
@@ -114,6 +125,15 @@ type NodeRelayOptions = RelayBrokerOptions & {
114
125
  /** Comma-separated origins (or `*`) for CORS. Default `*` — relays
115
126
  * are typically called cross-origin from the demo host. */
116
127
  corsAllowOrigin?: string;
128
+ /**
129
+ * Strict browser-origin allow-list. When set, only these Origins get a
130
+ * matching `Access-Control-Allow-Origin` (the request's own Origin is
131
+ * reflected, never `*`), which blunts DNS-rebinding + hostile cross-origin
132
+ * pages from reading relay responses. Recommended for any browser-facing
133
+ * deployment. When unset, `corsAllowOrigin` (default `*`) is used and the
134
+ * session token is the only auth — pair that with a loopback bind.
135
+ */
136
+ allowedOrigins?: string[];
117
137
  };
118
138
  type NodeHandler = (req: IncomingMessage, res: ServerResponse) => unknown | Promise<unknown>;
119
139
  type NodeRelay = {
@@ -77,7 +77,18 @@ declare class RelayBroker {
77
77
  subscribe(id: string, token: string, direction: Direction): SubscribeResult;
78
78
  private getDirSubs;
79
79
  private fanOut;
80
+ /**
81
+ * Validate that a peer-supplied payload is a well-formed JSON-RPC 2.0 frame
82
+ * (or batch) before it is fanned out. Previously this was a substring match on
83
+ * `"jsonrpc"`, which let any token holder inject spoofed frames — a forged
84
+ * `notifications/peer_joined` / `peer_left` (broker-reserved presence control),
85
+ * a fake `notifications/agent_activity {source:"user"}` ("human took control"),
86
+ * or a forged response with a matching id. We now parse + shape-check, and
87
+ * reject the broker-reserved control methods (the broker emits those itself,
88
+ * never a peer).
89
+ */
80
90
  private isFrame;
91
+ private isValidFrame;
81
92
  private reap;
82
93
  }
83
94
  type SubscribeResult = {
@@ -114,6 +125,15 @@ type NodeRelayOptions = RelayBrokerOptions & {
114
125
  /** Comma-separated origins (or `*`) for CORS. Default `*` — relays
115
126
  * are typically called cross-origin from the demo host. */
116
127
  corsAllowOrigin?: string;
128
+ /**
129
+ * Strict browser-origin allow-list. When set, only these Origins get a
130
+ * matching `Access-Control-Allow-Origin` (the request's own Origin is
131
+ * reflected, never `*`), which blunts DNS-rebinding + hostile cross-origin
132
+ * pages from reading relay responses. Recommended for any browser-facing
133
+ * deployment. When unset, `corsAllowOrigin` (default `*`) is used and the
134
+ * session token is the only auth — pair that with a loopback bind.
135
+ */
136
+ allowedOrigins?: string[];
117
137
  };
118
138
  type NodeHandler = (req: IncomingMessage, res: ServerResponse) => unknown | Promise<unknown>;
119
139
  type NodeRelay = {
@@ -172,8 +172,37 @@ var RelayBroker = class {
172
172
  sub.resolveNext?.(sub.queue.shift() ?? null);
173
173
  }
174
174
  }
175
+ /**
176
+ * Validate that a peer-supplied payload is a well-formed JSON-RPC 2.0 frame
177
+ * (or batch) before it is fanned out. Previously this was a substring match on
178
+ * `"jsonrpc"`, which let any token holder inject spoofed frames — a forged
179
+ * `notifications/peer_joined` / `peer_left` (broker-reserved presence control),
180
+ * a fake `notifications/agent_activity {source:"user"}` ("human took control"),
181
+ * or a forged response with a matching id. We now parse + shape-check, and
182
+ * reject the broker-reserved control methods (the broker emits those itself,
183
+ * never a peer).
184
+ */
175
185
  isFrame(payload) {
176
- return payload.length > 0 && payload.includes('"jsonrpc"');
186
+ if (payload.length === 0) return false;
187
+ let parsed;
188
+ try {
189
+ parsed = JSON.parse(payload);
190
+ } catch {
191
+ return false;
192
+ }
193
+ const frames = Array.isArray(parsed) ? parsed : [parsed];
194
+ return frames.length > 0 && frames.every((f) => this.isValidFrame(f));
195
+ }
196
+ isValidFrame(f) {
197
+ if (!f || typeof f !== "object" || Array.isArray(f)) return false;
198
+ const o = f;
199
+ if (o.jsonrpc !== "2.0") return false;
200
+ const method = typeof o.method === "string" ? o.method : void 0;
201
+ if (method === "notifications/peer_joined" || method === "notifications/peer_left") return false;
202
+ if (method !== void 0) return true;
203
+ const hasResult = "result" in o;
204
+ const hasError = "error" in o;
205
+ return "id" in o && hasResult !== hasError;
177
206
  }
178
207
  reap() {
179
208
  const cutoff = Date.now() - this.ttlMs;
@@ -202,10 +231,17 @@ function createNodeRelay(opts = {}) {
202
231
  const broker = new RelayBroker(opts);
203
232
  const prefix = (opts.pathPrefix ?? "").replace(/\/$/, "");
204
233
  const cors = opts.corsAllowOrigin ?? "*";
205
- function setCorsHeaders(res) {
206
- res.setHeader("access-control-allow-origin", cors);
234
+ const allowedOrigins = opts.allowedOrigins;
235
+ function setCorsHeaders(res, req) {
236
+ if (allowedOrigins && allowedOrigins.length) {
237
+ const origin = req?.headers.origin;
238
+ res.setHeader("access-control-allow-origin", origin && allowedOrigins.includes(origin) ? origin : "null");
239
+ res.setHeader("vary", "origin");
240
+ } else {
241
+ res.setHeader("access-control-allow-origin", cors);
242
+ }
207
243
  res.setHeader("access-control-allow-methods", "GET, POST, OPTIONS");
208
- res.setHeader("access-control-allow-headers", "content-type, x-csrf-token, accept");
244
+ res.setHeader("access-control-allow-headers", "content-type, x-csrf-token, accept, authorization");
209
245
  res.setHeader("access-control-max-age", "86400");
210
246
  }
211
247
  function readBody(req) {
@@ -241,7 +277,7 @@ function createNodeRelay(opts = {}) {
241
277
  return u.pathname;
242
278
  }
243
279
  const register = async (req, res) => {
244
- setCorsHeaders(res);
280
+ setCorsHeaders(res, req);
245
281
  if (req.method === "OPTIONS") {
246
282
  res.statusCode = 204;
247
283
  return res.end();
@@ -269,7 +305,7 @@ function createNodeRelay(opts = {}) {
269
305
  };
270
306
  function makeSessionHandler(direction) {
271
307
  return async (req, res) => {
272
- setCorsHeaders(res);
308
+ setCorsHeaders(res, req);
273
309
  if (req.method === "OPTIONS") {
274
310
  res.statusCode = 204;
275
311
  return res.end();
@@ -296,7 +332,7 @@ function createNodeRelay(opts = {}) {
296
332
  const outbox = makeSessionHandler("outbound");
297
333
  const unregister = makeSessionHandler("unregister");
298
334
  const events = async (req, res) => {
299
- setCorsHeaders(res);
335
+ setCorsHeaders(res, req);
300
336
  if (req.method === "OPTIONS") {
301
337
  res.statusCode = 204;
302
338
  return res.end();
@@ -390,7 +426,7 @@ function flush(res) {
390
426
  async function main() {
391
427
  const argv = process.argv.slice(2);
392
428
  let port = Number(process.env.PORT ?? 8787);
393
- let host = process.env.HOST ?? "0.0.0.0";
429
+ let host = process.env.HOST ?? "127.0.0.1";
394
430
  let prefix = process.env.PREFIX ?? "";
395
431
  let ttlMs = Number(process.env.TTL_MS ?? 4 * 60 * 60 * 1e3);
396
432
  let cors = process.env.CORS_ALLOW_ORIGIN ?? "*";
@@ -438,6 +474,13 @@ async function main() {
438
474
  }
439
475
  relay.handler(req, res);
440
476
  });
477
+ const isLoopback = host === "127.0.0.1" || host === "::1" || host === "localhost";
478
+ if (!isLoopback) {
479
+ process.stderr.write(
480
+ `[relay] WARNING: binding ${host} exposes the relay on non-loopback interfaces. register is unauthenticated and a leaked session token grants terminal_run. Only do this behind an authenticating proxy / trusted network.
481
+ `
482
+ );
483
+ }
441
484
  server.listen(port, host, () => {
442
485
  process.stdout.write(
443
486
  `[relay] listening on http://${host}:${port}${prefix || ""} (ttl=${Math.round(ttlMs / 1e3)}s, cors=${cors})
@@ -459,7 +502,8 @@ Usage: agent-integrations-relay [options]
459
502
 
460
503
  Options:
461
504
  --port <n> Listen port (env: PORT). Default 8787.
462
- --host <addr> Bind address (env: HOST). Default 0.0.0.0.
505
+ --host <addr> Bind address (env: HOST). Default 127.0.0.1 (loopback).
506
+ Pass 0.0.0.0 to expose on all interfaces (prints a warning).
463
507
  --prefix <path> URL path prefix (env: PREFIX). Default "".
464
508
  --ttl-ms <n> Session TTL ms (env: TTL_MS). Default 14_400_000.
465
509
  --cors <origin> CORS Access-Control-Allow-Origin (env: CORS_ALLOW_ORIGIN). Default "*".