jishushell 0.4.24 → 0.5.15

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 (281) hide show
  1. package/INSTALL-NOTICE +11 -0
  2. package/apps/anythingllm-container.yaml +287 -0
  3. package/apps/browserless-chromium-container.yaml +90 -0
  4. package/apps/filebrowser-container.yaml +163 -0
  5. package/apps/hermes-container.yaml +36 -2
  6. package/apps/ollama-binary.yaml +91 -90
  7. package/apps/ollama-cpu-container.yaml +8 -1
  8. package/apps/ollama-with-hollama-binary.yaml +91 -90
  9. package/apps/openclaw-binary.yaml +38 -1
  10. package/apps/openclaw-container.yaml +45 -2
  11. package/apps/openclaw-with-ollama-container.yaml +11 -2
  12. package/apps/openclaw-with-searxng-container.yaml +26 -2
  13. package/apps/openwebui-container.yaml +45 -1
  14. package/apps/playwright-container.yaml +7 -1
  15. package/apps/searxng-container.yaml +58 -7
  16. package/apps/weknora-container.yaml +471 -0
  17. package/dist/cli/app.js +79 -9
  18. package/dist/cli/app.js.map +1 -1
  19. package/dist/cli/doctor.d.ts +12 -12
  20. package/dist/cli/doctor.js +242 -55
  21. package/dist/cli/doctor.js.map +1 -1
  22. package/dist/cli/llm.d.ts +4 -3
  23. package/dist/cli/llm.js +4 -3
  24. package/dist/cli/llm.js.map +1 -1
  25. package/dist/cli/panel.d.ts +6 -5
  26. package/dist/cli/panel.js +10 -9
  27. package/dist/cli/panel.js.map +1 -1
  28. package/dist/config.d.ts +19 -0
  29. package/dist/config.js +99 -1
  30. package/dist/config.js.map +1 -1
  31. package/dist/control.d.ts +7 -6
  32. package/dist/control.js +7 -6
  33. package/dist/control.js.map +1 -1
  34. package/dist/install.js +3 -3
  35. package/dist/install.js.map +1 -1
  36. package/dist/routes/agent-apps.d.ts +1 -1
  37. package/dist/routes/agent-apps.js +1 -1
  38. package/dist/routes/apps.js +44 -11
  39. package/dist/routes/apps.js.map +1 -1
  40. package/dist/routes/auth.js +5 -2
  41. package/dist/routes/auth.js.map +1 -1
  42. package/dist/routes/backup.js +64 -11
  43. package/dist/routes/backup.js.map +1 -1
  44. package/dist/routes/external-mounts.d.ts +17 -0
  45. package/dist/routes/external-mounts.js +73 -0
  46. package/dist/routes/external-mounts.js.map +1 -0
  47. package/dist/routes/file-mounts.d.ts +13 -0
  48. package/dist/routes/file-mounts.js +90 -0
  49. package/dist/routes/file-mounts.js.map +1 -0
  50. package/dist/routes/files-organize.d.ts +28 -0
  51. package/dist/routes/files-organize.js +167 -0
  52. package/dist/routes/files-organize.js.map +1 -0
  53. package/dist/routes/files.d.ts +31 -0
  54. package/dist/routes/files.js +321 -0
  55. package/dist/routes/files.js.map +1 -0
  56. package/dist/routes/instances.js +826 -17
  57. package/dist/routes/instances.js.map +1 -1
  58. package/dist/routes/internal.d.ts +2 -0
  59. package/dist/routes/internal.js +59 -0
  60. package/dist/routes/internal.js.map +1 -0
  61. package/dist/routes/llm.js +24 -35
  62. package/dist/routes/llm.js.map +1 -1
  63. package/dist/routes/setup.js +10 -10
  64. package/dist/routes/setup.js.map +1 -1
  65. package/dist/routes/system.js +1 -1
  66. package/dist/routes/system.js.map +1 -1
  67. package/dist/routes/webdav.d.ts +17 -0
  68. package/dist/routes/webdav.js +114 -0
  69. package/dist/routes/webdav.js.map +1 -0
  70. package/dist/server.d.ts +9 -0
  71. package/dist/server.js +751 -20
  72. package/dist/server.js.map +1 -1
  73. package/dist/services/agent-apps/catalog.js +4 -3
  74. package/dist/services/agent-apps/catalog.js.map +1 -1
  75. package/dist/services/agent-apps/index.d.ts +1 -1
  76. package/dist/services/agent-apps/index.js +1 -1
  77. package/dist/services/agent-apps/installers/adapter.d.ts +1 -1
  78. package/dist/services/agent-apps/installers/adapter.js +1 -1
  79. package/dist/services/agent-apps/installers/shell-script.d.ts +1 -1
  80. package/dist/services/agent-apps/installers/shell-script.js +3 -3
  81. package/dist/services/agent-apps/installers/shell-script.js.map +1 -1
  82. package/dist/services/agent-apps/types.d.ts +2 -2
  83. package/dist/services/agent-apps/types.js +1 -1
  84. package/dist/services/app/app-compiler.d.ts +1 -1
  85. package/dist/services/app/app-compiler.js +5 -5
  86. package/dist/services/app/app-compiler.js.map +1 -1
  87. package/dist/services/app/app-manager.d.ts +25 -1
  88. package/dist/services/app/app-manager.js +829 -150
  89. package/dist/services/app/app-manager.js.map +1 -1
  90. package/dist/services/app/custom-manager.js.map +1 -1
  91. package/dist/services/app/hermes-agent-manager.js +7 -4
  92. package/dist/services/app/hermes-agent-manager.js.map +1 -1
  93. package/dist/services/app/ollama-manager.js +1 -1
  94. package/dist/services/app/ollama-manager.js.map +1 -1
  95. package/dist/services/app/openclaw-manager.js +20 -3
  96. package/dist/services/app/openclaw-manager.js.map +1 -1
  97. package/dist/services/app/platform-transform.d.ts +32 -0
  98. package/dist/services/app/platform-transform.js +65 -0
  99. package/dist/services/app/platform-transform.js.map +1 -0
  100. package/dist/services/app/provide-resolver.d.ts +29 -0
  101. package/dist/services/app/provide-resolver.js +112 -0
  102. package/dist/services/app/provide-resolver.js.map +1 -0
  103. package/dist/services/app-passwords.d.ts +61 -0
  104. package/dist/services/app-passwords.js +173 -0
  105. package/dist/services/app-passwords.js.map +1 -0
  106. package/dist/services/backup-manager.d.ts +11 -0
  107. package/dist/services/backup-manager.js +177 -4
  108. package/dist/services/backup-manager.js.map +1 -1
  109. package/dist/services/capability-endpoint-validator.d.ts +41 -0
  110. package/dist/services/capability-endpoint-validator.js +104 -0
  111. package/dist/services/capability-endpoint-validator.js.map +1 -0
  112. package/dist/services/capability-health.d.ts +16 -0
  113. package/dist/services/capability-health.js +121 -0
  114. package/dist/services/capability-health.js.map +1 -0
  115. package/dist/services/capability-registry.d.ts +106 -0
  116. package/dist/services/capability-registry.js +313 -0
  117. package/dist/services/capability-registry.js.map +1 -0
  118. package/dist/services/connection-apply.d.ts +91 -0
  119. package/dist/services/connection-apply.js +475 -0
  120. package/dist/services/connection-apply.js.map +1 -0
  121. package/dist/services/connection-resolver.d.ts +65 -0
  122. package/dist/services/connection-resolver.js +281 -0
  123. package/dist/services/connection-resolver.js.map +1 -0
  124. package/dist/services/connection-transactor.d.ts +39 -0
  125. package/dist/services/connection-transactor.js +351 -0
  126. package/dist/services/connection-transactor.js.map +1 -0
  127. package/dist/services/external-mounts.d.ts +40 -0
  128. package/dist/services/external-mounts.js +187 -0
  129. package/dist/services/external-mounts.js.map +1 -0
  130. package/dist/services/files-manager.d.ts +252 -0
  131. package/dist/services/files-manager.js +1075 -0
  132. package/dist/services/files-manager.js.map +1 -0
  133. package/dist/services/files-mounts.d.ts +42 -0
  134. package/dist/services/files-mounts.js +207 -0
  135. package/dist/services/files-mounts.js.map +1 -0
  136. package/dist/services/instance-manager.d.ts +13 -0
  137. package/dist/services/instance-manager.js +138 -46
  138. package/dist/services/instance-manager.js.map +1 -1
  139. package/dist/services/llm-proxy/index.d.ts +16 -2
  140. package/dist/services/llm-proxy/index.js +48 -44
  141. package/dist/services/llm-proxy/index.js.map +1 -1
  142. package/dist/services/llm-proxy/probe.d.ts +6 -0
  143. package/dist/services/llm-proxy/probe.js +85 -0
  144. package/dist/services/llm-proxy/probe.js.map +1 -0
  145. package/dist/services/llm-proxy/ssrf.d.ts +1 -0
  146. package/dist/services/llm-proxy/ssrf.js +24 -9
  147. package/dist/services/llm-proxy/ssrf.js.map +1 -1
  148. package/dist/services/nomad-manager.d.ts +4 -0
  149. package/dist/services/nomad-manager.js +428 -35
  150. package/dist/services/nomad-manager.js.map +1 -1
  151. package/dist/services/organize/applier.d.ts +46 -0
  152. package/dist/services/organize/applier.js +218 -0
  153. package/dist/services/organize/applier.js.map +1 -0
  154. package/dist/services/organize/rules.d.ts +57 -0
  155. package/dist/services/organize/rules.js +286 -0
  156. package/dist/services/organize/rules.js.map +1 -0
  157. package/dist/services/organize/scanner.d.ts +50 -0
  158. package/dist/services/organize/scanner.js +366 -0
  159. package/dist/services/organize/scanner.js.map +1 -0
  160. package/dist/services/organize/store.d.ts +14 -0
  161. package/dist/services/organize/store.js +82 -0
  162. package/dist/services/organize/store.js.map +1 -0
  163. package/dist/services/panel-manager.js +20 -1
  164. package/dist/services/panel-manager.js.map +1 -1
  165. package/dist/services/process-manager.js +4 -3
  166. package/dist/services/process-manager.js.map +1 -1
  167. package/dist/services/runtime/adapters/hermes.d.ts +30 -1
  168. package/dist/services/runtime/adapters/hermes.js +219 -6
  169. package/dist/services/runtime/adapters/hermes.js.map +1 -1
  170. package/dist/services/runtime/adapters/openclaw-mcporter.d.ts +45 -0
  171. package/dist/services/runtime/adapters/openclaw-mcporter.js +108 -0
  172. package/dist/services/runtime/adapters/openclaw-mcporter.js.map +1 -0
  173. package/dist/services/runtime/adapters/openclaw-routes.d.ts +8 -2
  174. package/dist/services/runtime/adapters/openclaw-routes.js +68 -0
  175. package/dist/services/runtime/adapters/openclaw-routes.js.map +1 -1
  176. package/dist/services/runtime/adapters/openclaw.d.ts +177 -0
  177. package/dist/services/runtime/adapters/openclaw.js +1171 -11
  178. package/dist/services/runtime/adapters/openclaw.js.map +1 -1
  179. package/dist/services/runtime/instance.d.ts +1 -1
  180. package/dist/services/runtime/instance.js +1 -1
  181. package/dist/services/runtime/instance.js.map +1 -1
  182. package/dist/services/runtime/mcp-shims/anythingllm-shim.d.ts +46 -0
  183. package/dist/services/runtime/mcp-shims/anythingllm-shim.js +281 -0
  184. package/dist/services/runtime/mcp-shims/anythingllm-shim.js.map +1 -0
  185. package/dist/services/runtime/mcp-shims/drive-shim.d.ts +54 -0
  186. package/dist/services/runtime/mcp-shims/drive-shim.js +489 -0
  187. package/dist/services/runtime/mcp-shims/drive-shim.js.map +1 -0
  188. package/dist/services/runtime/mcp-shims/firewall.d.ts +26 -0
  189. package/dist/services/runtime/mcp-shims/firewall.js +129 -0
  190. package/dist/services/runtime/mcp-shims/firewall.js.map +1 -0
  191. package/dist/services/runtime/mcp-shims/searxng-shim.d.ts +27 -0
  192. package/dist/services/runtime/mcp-shims/searxng-shim.js +125 -0
  193. package/dist/services/runtime/mcp-shims/searxng-shim.js.map +1 -0
  194. package/dist/services/runtime/mcp-shims/write-mcp-entry.d.ts +83 -0
  195. package/dist/services/runtime/mcp-shims/write-mcp-entry.js +127 -0
  196. package/dist/services/runtime/mcp-shims/write-mcp-entry.js.map +1 -0
  197. package/dist/services/runtime/migrations.d.ts +8 -0
  198. package/dist/services/runtime/migrations.js +100 -0
  199. package/dist/services/runtime/migrations.js.map +1 -1
  200. package/dist/services/runtime/types.d.ts +46 -0
  201. package/dist/services/setup-manager.js +99 -24
  202. package/dist/services/setup-manager.js.map +1 -1
  203. package/dist/services/suggestions.d.ts +27 -0
  204. package/dist/services/suggestions.js +133 -0
  205. package/dist/services/suggestions.js.map +1 -0
  206. package/dist/services/task-registry.js +4 -2
  207. package/dist/services/task-registry.js.map +1 -1
  208. package/dist/services/telemetry/device-fingerprint.d.ts +1 -1
  209. package/dist/services/telemetry/device-fingerprint.js +1 -1
  210. package/dist/services/types-shim.d.ts +16 -0
  211. package/dist/services/types-shim.js +2 -0
  212. package/dist/services/types-shim.js.map +1 -0
  213. package/dist/services/webdav/server.d.ts +24 -0
  214. package/dist/services/webdav/server.js +420 -0
  215. package/dist/services/webdav/server.js.map +1 -0
  216. package/dist/services/webdav/xml-builder.d.ts +73 -0
  217. package/dist/services/webdav/xml-builder.js +156 -0
  218. package/dist/services/webdav/xml-builder.js.map +1 -0
  219. package/dist/services/workspace-builder.d.ts +29 -0
  220. package/dist/services/workspace-builder.js +188 -0
  221. package/dist/services/workspace-builder.js.map +1 -0
  222. package/dist/types.d.ts +231 -1
  223. package/dist/utils/instance-lock.d.ts +22 -0
  224. package/dist/utils/instance-lock.js +48 -0
  225. package/dist/utils/instance-lock.js.map +1 -0
  226. package/dist/utils/path-locks.d.ts +30 -0
  227. package/dist/utils/path-locks.js +63 -0
  228. package/dist/utils/path-locks.js.map +1 -0
  229. package/dist/utils/path-safety.d.ts +41 -0
  230. package/dist/utils/path-safety.js +119 -0
  231. package/dist/utils/path-safety.js.map +1 -0
  232. package/dist/utils/safe-json.js +55 -22
  233. package/dist/utils/safe-json.js.map +1 -1
  234. package/dist/utils/safe-write.d.ts +24 -0
  235. package/dist/utils/safe-write.js +82 -0
  236. package/dist/utils/safe-write.js.map +1 -0
  237. package/install/jishu-install.sh +323 -27
  238. package/install/jishu-uninstall.sh +353 -20
  239. package/package.json +18 -1
  240. package/public/assets/Dashboard-BdWPtroF.js +1 -0
  241. package/public/assets/{HermesChatPanel-mFSureyc.js → HermesChatPanel-B_2HlVBQ.js} +1 -1
  242. package/public/assets/HermesConfigForm-DVlhg3WV.js +4 -0
  243. package/public/assets/{InitPassword-CVA8wQA6.js → InitPassword-D7glTExX.js} +1 -1
  244. package/public/assets/InstanceDetail-CxSy2cpe.js +92 -0
  245. package/public/assets/{Login-BWsZH2mu.js → Login-Cfr5c2sv.js} +1 -1
  246. package/public/assets/NewInstance-BIYDmJis.js +1 -0
  247. package/public/assets/ProviderRecommendations-BuRnvRcI.js +1 -0
  248. package/public/assets/Settings-Cc-tYBil.js +1 -0
  249. package/public/assets/Setup-lGZEk5jq.js +1 -0
  250. package/public/assets/{WeixinLoginPanel-CnjR8xMu.js → WeixinLoginPanel-CoGqzxeV.js} +2 -2
  251. package/public/assets/index-87IJXG-w.css +1 -0
  252. package/public/assets/index-BZc5zH7u.js +19 -0
  253. package/public/assets/providers-DtNXh9JD.js +1 -0
  254. package/public/assets/registry-BWnkJgZ1.js +2 -0
  255. package/public/assets/{usePolling-Do5Erqm_.js → usePolling-CwwT9KrC.js} +1 -1
  256. package/public/assets/{vendor-i18n-ucpM0OR0.js → vendor-i18n-y9V7Sfuu.js} +1 -1
  257. package/public/assets/{vendor-react-Bk1hRGiY.js → vendor-react-BWrEVJVb.js} +6 -6
  258. package/public/index.html +4 -4
  259. package/scripts/check-app-spec.mjs +457 -0
  260. package/scripts/check-i18n.mjs +154 -0
  261. package/scripts/check-new-file-tests.mjs +230 -0
  262. package/scripts/check-quarantine-expiry.mjs +105 -0
  263. package/scripts/perf/README.md +49 -0
  264. package/scripts/perf/auth.js +99 -0
  265. package/scripts/perf/config.js +63 -0
  266. package/scripts/perf/instances.js +143 -0
  267. package/scripts/perf/proxy.js +96 -0
  268. package/scripts/run.sh +4 -4
  269. package/scripts/smoke/files-w1.sh +142 -0
  270. package/scripts/smoke-backend.mjs +122 -0
  271. package/scripts/smoke-post-publish.mjs +346 -0
  272. package/public/assets/Dashboard-B-JoOjBQ.js +0 -1
  273. package/public/assets/HermesConfigForm-DvR05LK1.js +0 -4
  274. package/public/assets/InstanceDetail-DcZW2QGO.js +0 -91
  275. package/public/assets/NewInstance-BCIrAd86.js +0 -1
  276. package/public/assets/Settings-xkDcduFz.js +0 -1
  277. package/public/assets/Setup-Cfuwj4gV.js +0 -1
  278. package/public/assets/index-CPhVFEsx.css +0 -1
  279. package/public/assets/index-DQsM6Joa.js +0 -19
  280. package/public/assets/providers-V-vwrExZ.js +0 -1
  281. package/public/assets/registry-B4UFJdpA.js +0 -2
@@ -0,0 +1,27 @@
1
+ /**
2
+ * SearXNG MCP shim — exported as a string for runtime drop into the
3
+ * Hermes container's agent-home.
4
+ *
5
+ * Why a shim instead of upstream `mcp-searxng`?
6
+ *
7
+ * The upstream `mcp-searxng` npm package's tool description and parameter
8
+ * surface (`Performs a web search using the SearXNG API, ideal for general
9
+ * queries, news, articles, and online content. Use this for broad
10
+ * information gathering, recent events, or when you need diverse web
11
+ * sources.` + `time_range: day|month|year` + `language` slot) acts as a
12
+ * prompt injection that nudges some LLMs (observed: MiniMax-M2.7) to:
13
+ * - inject the current date (or a hallucinated past date) into the query,
14
+ * - translate the query to English,
15
+ * - auto-fill `time_range=day`.
16
+ *
17
+ * OpenClaw's built-in `searxng` web-search tool exposes a deliberately
18
+ * minimal description (`Search the web using a self-hosted SearXNG
19
+ * instance. Returns titles, URLs, and snippets.`) and the same model
20
+ * leaves the user query untouched. We mirror OpenClaw's surface here so
21
+ * Hermes' MCP-bound search behaves identically to OpenClaw's plugin path.
22
+ *
23
+ * The shim is pure stdio MCP (JSON-RPC 2.0 over NDJSON) implemented
24
+ * against Node built-ins — no npm install needed inside the runtime.
25
+ */
26
+ export declare const SEARXNG_MCP_SHIM_VERSION = "0.1.0";
27
+ export declare const SEARXNG_MCP_SHIM_SOURCE = "#!/usr/bin/env node\n// jishushell-searxng-shim \u2014 generated, do not edit by hand.\n// Source: src/services/runtime/mcp-shims/searxng-shim.ts\nimport { createInterface } from \"node:readline\";\n\nconst SEARXNG_URL = (process.env.SEARXNG_URL || \"\").replace(/\\/+$/, \"\");\n\nconst TOOL = {\n name: \"searxng_web_search\",\n description: \"Search the web using a self-hosted SearXNG instance. Returns titles, URLs, and snippets.\",\n inputSchema: {\n type: \"object\",\n properties: {\n query: { type: \"string\", description: \"Search query string.\" },\n count: { type: \"number\", description: \"Number of results to return (1-10).\", minimum: 1, maximum: 10 },\n categories: { type: \"string\", description: \"Optional comma-separated categories such as general, news, or science.\" },\n language: { type: \"string\", description: \"Optional language code such as en, de, or fr.\" }\n },\n required: [\"query\"]\n }\n};\n\nfunction send(msg) { process.stdout.write(JSON.stringify(msg) + \"\\n\"); }\nfunction reply(id, result) { if (id != null) send({ jsonrpc: \"2.0\", id, result }); }\nfunction replyError(id, code, message) { if (id != null) send({ jsonrpc: \"2.0\", id, error: { code, message } }); }\n\nasync function callSearch(args) {\n if (!SEARXNG_URL) throw new Error(\"SEARXNG_URL env not set\");\n const query = String(args?.query ?? \"\").trim();\n if (!query) throw new Error(\"query is required\");\n const count = Math.max(1, Math.min(10, Number(args?.count) || 5));\n const url = new URL(\"/search\", SEARXNG_URL);\n url.searchParams.set(\"q\", query);\n url.searchParams.set(\"format\", \"json\");\n if (args?.categories) url.searchParams.set(\"categories\", String(args.categories));\n if (args?.language) url.searchParams.set(\"language\", String(args.language));\n const ctrl = new AbortController();\n const t = setTimeout(() => ctrl.abort(), 20000);\n let resp;\n try {\n resp = await fetch(url.toString(), {\n method: \"GET\",\n headers: { Accept: \"application/json\" },\n signal: ctrl.signal\n });\n } finally {\n clearTimeout(t);\n }\n if (!resp.ok) throw new Error(\"SearXNG \" + resp.status + \" \" + resp.statusText);\n const body = await resp.json();\n const raw = Array.isArray(body?.results) ? body.results : [];\n const results = raw.slice(0, count).map((r) => ({\n title: typeof r?.title === \"string\" ? r.title : \"\",\n url: typeof r?.url === \"string\" ? r.url : \"\",\n snippet: typeof r?.content === \"string\" ? r.content : \"\"\n }));\n return { query, count: results.length, results };\n}\n\nasync function handle(req) {\n const { id, method, params } = req || {};\n try {\n if (method === \"initialize\") {\n reply(id, {\n protocolVersion: \"2024-11-05\",\n capabilities: { tools: {} },\n serverInfo: { name: \"jishushell-searxng-shim\", version: \"0.1.0\" }\n });\n } else if (method === \"notifications/initialized\") {\n // notification, no reply\n } else if (method === \"tools/list\") {\n reply(id, { tools: [TOOL] });\n } else if (method === \"tools/call\") {\n if (params?.name !== TOOL.name) throw new Error(\"unknown tool: \" + params?.name);\n const data = await callSearch(params?.arguments ?? {});\n reply(id, { content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }] });\n } else if (method === \"ping\") {\n reply(id, {});\n } else {\n replyError(id, -32601, \"method not found: \" + method);\n }\n } catch (e) {\n replyError(id, -32000, (e && e.message) ? e.message : String(e));\n }\n}\n\nconst rl = createInterface({ input: process.stdin });\nrl.on(\"line\", (line) => {\n const t = line.trim();\n if (!t) return;\n let req;\n try { req = JSON.parse(t); } catch { return; }\n handle(req);\n});\n\nprocess.on(\"SIGTERM\", () => process.exit(0));\nprocess.on(\"SIGINT\", () => process.exit(0));\n";
@@ -0,0 +1,125 @@
1
+ /**
2
+ * SearXNG MCP shim — exported as a string for runtime drop into the
3
+ * Hermes container's agent-home.
4
+ *
5
+ * Why a shim instead of upstream `mcp-searxng`?
6
+ *
7
+ * The upstream `mcp-searxng` npm package's tool description and parameter
8
+ * surface (`Performs a web search using the SearXNG API, ideal for general
9
+ * queries, news, articles, and online content. Use this for broad
10
+ * information gathering, recent events, or when you need diverse web
11
+ * sources.` + `time_range: day|month|year` + `language` slot) acts as a
12
+ * prompt injection that nudges some LLMs (observed: MiniMax-M2.7) to:
13
+ * - inject the current date (or a hallucinated past date) into the query,
14
+ * - translate the query to English,
15
+ * - auto-fill `time_range=day`.
16
+ *
17
+ * OpenClaw's built-in `searxng` web-search tool exposes a deliberately
18
+ * minimal description (`Search the web using a self-hosted SearXNG
19
+ * instance. Returns titles, URLs, and snippets.`) and the same model
20
+ * leaves the user query untouched. We mirror OpenClaw's surface here so
21
+ * Hermes' MCP-bound search behaves identically to OpenClaw's plugin path.
22
+ *
23
+ * The shim is pure stdio MCP (JSON-RPC 2.0 over NDJSON) implemented
24
+ * against Node built-ins — no npm install needed inside the runtime.
25
+ */
26
+ export const SEARXNG_MCP_SHIM_VERSION = "0.1.0";
27
+ export const SEARXNG_MCP_SHIM_SOURCE = `#!/usr/bin/env node
28
+ // jishushell-searxng-shim — generated, do not edit by hand.
29
+ // Source: src/services/runtime/mcp-shims/searxng-shim.ts
30
+ import { createInterface } from "node:readline";
31
+
32
+ const SEARXNG_URL = (process.env.SEARXNG_URL || "").replace(/\\/+$/, "");
33
+
34
+ const TOOL = {
35
+ name: "searxng_web_search",
36
+ description: "Search the web using a self-hosted SearXNG instance. Returns titles, URLs, and snippets.",
37
+ inputSchema: {
38
+ type: "object",
39
+ properties: {
40
+ query: { type: "string", description: "Search query string." },
41
+ count: { type: "number", description: "Number of results to return (1-10).", minimum: 1, maximum: 10 },
42
+ categories: { type: "string", description: "Optional comma-separated categories such as general, news, or science." },
43
+ language: { type: "string", description: "Optional language code such as en, de, or fr." }
44
+ },
45
+ required: ["query"]
46
+ }
47
+ };
48
+
49
+ function send(msg) { process.stdout.write(JSON.stringify(msg) + "\\n"); }
50
+ function reply(id, result) { if (id != null) send({ jsonrpc: "2.0", id, result }); }
51
+ function replyError(id, code, message) { if (id != null) send({ jsonrpc: "2.0", id, error: { code, message } }); }
52
+
53
+ async function callSearch(args) {
54
+ if (!SEARXNG_URL) throw new Error("SEARXNG_URL env not set");
55
+ const query = String(args?.query ?? "").trim();
56
+ if (!query) throw new Error("query is required");
57
+ const count = Math.max(1, Math.min(10, Number(args?.count) || 5));
58
+ const url = new URL("/search", SEARXNG_URL);
59
+ url.searchParams.set("q", query);
60
+ url.searchParams.set("format", "json");
61
+ if (args?.categories) url.searchParams.set("categories", String(args.categories));
62
+ if (args?.language) url.searchParams.set("language", String(args.language));
63
+ const ctrl = new AbortController();
64
+ const t = setTimeout(() => ctrl.abort(), 20000);
65
+ let resp;
66
+ try {
67
+ resp = await fetch(url.toString(), {
68
+ method: "GET",
69
+ headers: { Accept: "application/json" },
70
+ signal: ctrl.signal
71
+ });
72
+ } finally {
73
+ clearTimeout(t);
74
+ }
75
+ if (!resp.ok) throw new Error("SearXNG " + resp.status + " " + resp.statusText);
76
+ const body = await resp.json();
77
+ const raw = Array.isArray(body?.results) ? body.results : [];
78
+ const results = raw.slice(0, count).map((r) => ({
79
+ title: typeof r?.title === "string" ? r.title : "",
80
+ url: typeof r?.url === "string" ? r.url : "",
81
+ snippet: typeof r?.content === "string" ? r.content : ""
82
+ }));
83
+ return { query, count: results.length, results };
84
+ }
85
+
86
+ async function handle(req) {
87
+ const { id, method, params } = req || {};
88
+ try {
89
+ if (method === "initialize") {
90
+ reply(id, {
91
+ protocolVersion: "2024-11-05",
92
+ capabilities: { tools: {} },
93
+ serverInfo: { name: "jishushell-searxng-shim", version: "${SEARXNG_MCP_SHIM_VERSION}" }
94
+ });
95
+ } else if (method === "notifications/initialized") {
96
+ // notification, no reply
97
+ } else if (method === "tools/list") {
98
+ reply(id, { tools: [TOOL] });
99
+ } else if (method === "tools/call") {
100
+ if (params?.name !== TOOL.name) throw new Error("unknown tool: " + params?.name);
101
+ const data = await callSearch(params?.arguments ?? {});
102
+ reply(id, { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] });
103
+ } else if (method === "ping") {
104
+ reply(id, {});
105
+ } else {
106
+ replyError(id, -32601, "method not found: " + method);
107
+ }
108
+ } catch (e) {
109
+ replyError(id, -32000, (e && e.message) ? e.message : String(e));
110
+ }
111
+ }
112
+
113
+ const rl = createInterface({ input: process.stdin });
114
+ rl.on("line", (line) => {
115
+ const t = line.trim();
116
+ if (!t) return;
117
+ let req;
118
+ try { req = JSON.parse(t); } catch { return; }
119
+ handle(req);
120
+ });
121
+
122
+ process.on("SIGTERM", () => process.exit(0));
123
+ process.on("SIGINT", () => process.exit(0));
124
+ `;
125
+ //# sourceMappingURL=searxng-shim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"searxng-shim.js","sourceRoot":"","sources":["../../../../src/services/runtime/mcp-shims/searxng-shim.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,MAAM,CAAC,MAAM,wBAAwB,GAAG,OAAO,CAAC;AAEhD,MAAM,CAAC,MAAM,uBAAuB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mEAkE4B,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+B1F,CAAC"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * writeMcpEntry — adapter helper that materializes the MCP firewall
3
+ * (see firewall.ts) into an instance's agent-home and returns the
4
+ * `mcp_servers.<X>` block the adapter should write into its own config.
5
+ *
6
+ * Called by adapters from `applyConnectionEnv` (PR 8 / docs §17.3.3).
7
+ *
8
+ * Side effects on the host filesystem:
9
+ * - <agentHomeDir>/__mcp_shims__/<capabilityId>/firewall.mjs
10
+ * - <agentHomeDir>/__mcp_shims__/<capabilityId>/config.json
11
+ * - chown both to serviceUser if provided (best-effort)
12
+ *
13
+ * Pure return value (the adapter applies it to its own config schema):
14
+ * {
15
+ * command: "node",
16
+ * args: ["<containerHomeDir>/__mcp_shims__/<id>/firewall.mjs",
17
+ * "--config",
18
+ * "<containerHomeDir>/__mcp_shims__/<id>/config.json"],
19
+ * env: {} // upstream env is encoded in config.json — keep adapter env clean
20
+ * }
21
+ *
22
+ * The adapter is responsible for:
23
+ * - Adding `enabled: true` (or whatever flag its config schema needs).
24
+ * - Picking the `mcp_servers` map key (typically capabilityId).
25
+ */
26
+ import type { ToolSchema } from "../../../types.js";
27
+ export interface WriteMcpEntryArgs {
28
+ /** Host-side path where the agent's home directory lives. */
29
+ agentHomeDir: string;
30
+ /**
31
+ * Container-side equivalent of agentHomeDir — used to compose the
32
+ * absolute path the agent process will exec. For Hermes that's
33
+ * `/opt/data` (HERMES_CONTAINER_HOME).
34
+ */
35
+ containerHomeDir: string;
36
+ /** Capability ID (e.g. "search-searxng"); also used as the on-disk subdir. */
37
+ capabilityId: string;
38
+ /** Canonical tool surface — copied into config.json verbatim. */
39
+ toolSchema: ToolSchema;
40
+ /**
41
+ * Resolved upstream env for this binding — values are already substituted
42
+ * from connection-resolver outputs (no `${...}` placeholders left).
43
+ */
44
+ upstreamEnv: Record<string, string>;
45
+ /** Best-effort chown so the in-container process can read the files. */
46
+ serviceUser?: {
47
+ uid: number;
48
+ gid: number;
49
+ };
50
+ }
51
+ export interface WriteMcpEntryResult {
52
+ /** Command to put under `mcp_servers.<key>.command`. */
53
+ command: string;
54
+ /** Args to put under `mcp_servers.<key>.args`. */
55
+ args: string[];
56
+ /**
57
+ * Env to put under `mcp_servers.<key>.env`. Always empty — upstream
58
+ * env is funneled through firewall config.json so adapter config
59
+ * stays minimal. Adapters should still write this key so any prior
60
+ * env entries get cleared on overwrite.
61
+ */
62
+ env: Record<string, string>;
63
+ }
64
+ export declare function writeMcpEntry(args: WriteMcpEntryArgs): WriteMcpEntryResult;
65
+ /**
66
+ * Resolve `${SLOT}` placeholders in an `env_template` against a flat
67
+ * env map (typically the env that connection-resolver returned).
68
+ *
69
+ * Suffix forms:
70
+ * ${NAME_ORIGIN} → parsed URL with /search stripped, returns
71
+ * scheme://host:port (no path, no trailing slash).
72
+ * ${NAME} → verbatim env value.
73
+ *
74
+ * Note: this returns the host:port as the registry resolved at the time
75
+ * the consumer's PUT /connections was processed. To keep this fresh
76
+ * across host IP changes, the framework re-runs adapter.applyConnectionEnv
77
+ * before each instance start (see nomad-manager.phaseRefreshConnections,
78
+ * §17/PR 9). That re-render path passes a fresh env map here.
79
+ *
80
+ * Unresolved placeholders are left as-is (caller can decide whether to
81
+ * fail or pass through).
82
+ */
83
+ export declare function resolveEnvTemplate(template: Record<string, string> | undefined, envSource: Record<string, string>): Record<string, string>;
@@ -0,0 +1,127 @@
1
+ /**
2
+ * writeMcpEntry — adapter helper that materializes the MCP firewall
3
+ * (see firewall.ts) into an instance's agent-home and returns the
4
+ * `mcp_servers.<X>` block the adapter should write into its own config.
5
+ *
6
+ * Called by adapters from `applyConnectionEnv` (PR 8 / docs §17.3.3).
7
+ *
8
+ * Side effects on the host filesystem:
9
+ * - <agentHomeDir>/__mcp_shims__/<capabilityId>/firewall.mjs
10
+ * - <agentHomeDir>/__mcp_shims__/<capabilityId>/config.json
11
+ * - chown both to serviceUser if provided (best-effort)
12
+ *
13
+ * Pure return value (the adapter applies it to its own config schema):
14
+ * {
15
+ * command: "node",
16
+ * args: ["<containerHomeDir>/__mcp_shims__/<id>/firewall.mjs",
17
+ * "--config",
18
+ * "<containerHomeDir>/__mcp_shims__/<id>/config.json"],
19
+ * env: {} // upstream env is encoded in config.json — keep adapter env clean
20
+ * }
21
+ *
22
+ * The adapter is responsible for:
23
+ * - Adding `enabled: true` (or whatever flag its config schema needs).
24
+ * - Picking the `mcp_servers` map key (typically capabilityId).
25
+ */
26
+ import { chownSync, mkdirSync, writeFileSync } from "fs";
27
+ import { dirname, join, posix } from "path";
28
+ import { MCP_FIREWALL_SOURCE } from "./firewall.js";
29
+ const SHIM_ROOT = "__mcp_shims__";
30
+ const FIREWALL_FILENAME = "firewall.mjs";
31
+ const CONFIG_FILENAME = "config.json";
32
+ export function writeMcpEntry(args) {
33
+ const { agentHomeDir, containerHomeDir, capabilityId, toolSchema, upstreamEnv, serviceUser, } = args;
34
+ const hostShimDir = join(agentHomeDir, SHIM_ROOT, capabilityId);
35
+ const hostFirewall = join(hostShimDir, FIREWALL_FILENAME);
36
+ const hostConfig = join(hostShimDir, CONFIG_FILENAME);
37
+ mkdirSync(hostShimDir, { recursive: true });
38
+ // Drop firewall source. Mode 0o755 so the in-container service user
39
+ // (which may not match host UID via every chown path) can still read+exec.
40
+ writeFileSync(hostFirewall, MCP_FIREWALL_SOURCE, { mode: 0o755 });
41
+ // Build firewall config
42
+ const config = {
43
+ capability_id: capabilityId,
44
+ tool_schema: {
45
+ name: toolSchema.name,
46
+ description: toolSchema.description,
47
+ // Stored under `inputSchema` so firewall doesn't have to alias.
48
+ inputSchema: toolSchema.parameters,
49
+ },
50
+ upstream: {
51
+ command: toolSchema.upstream.command,
52
+ args: toolSchema.upstream.args ?? [],
53
+ env: upstreamEnv,
54
+ },
55
+ wrap_outputs: toolSchema.wrap_outputs !== false,
56
+ };
57
+ writeFileSync(hostConfig, JSON.stringify(config, null, 2), { mode: 0o644 });
58
+ if (serviceUser) {
59
+ try {
60
+ chownSync(dirname(hostFirewall), serviceUser.uid, serviceUser.gid);
61
+ chownSync(hostFirewall, serviceUser.uid, serviceUser.gid);
62
+ chownSync(hostConfig, serviceUser.uid, serviceUser.gid);
63
+ }
64
+ catch {
65
+ /* best-effort — files are world-readable */
66
+ }
67
+ }
68
+ // Build container-side absolute path. Always POSIX-style — container
69
+ // OSes are Linux even when host is not.
70
+ const containerShimDir = posix.join(containerHomeDir, SHIM_ROOT, capabilityId);
71
+ return {
72
+ command: "node",
73
+ args: [
74
+ posix.join(containerShimDir, FIREWALL_FILENAME),
75
+ "--config",
76
+ posix.join(containerShimDir, CONFIG_FILENAME),
77
+ ],
78
+ env: {},
79
+ };
80
+ }
81
+ /**
82
+ * Resolve `${SLOT}` placeholders in an `env_template` against a flat
83
+ * env map (typically the env that connection-resolver returned).
84
+ *
85
+ * Suffix forms:
86
+ * ${NAME_ORIGIN} → parsed URL with /search stripped, returns
87
+ * scheme://host:port (no path, no trailing slash).
88
+ * ${NAME} → verbatim env value.
89
+ *
90
+ * Note: this returns the host:port as the registry resolved at the time
91
+ * the consumer's PUT /connections was processed. To keep this fresh
92
+ * across host IP changes, the framework re-runs adapter.applyConnectionEnv
93
+ * before each instance start (see nomad-manager.phaseRefreshConnections,
94
+ * §17/PR 9). That re-render path passes a fresh env map here.
95
+ *
96
+ * Unresolved placeholders are left as-is (caller can decide whether to
97
+ * fail or pass through).
98
+ */
99
+ export function resolveEnvTemplate(template, envSource) {
100
+ if (!template)
101
+ return {};
102
+ const out = {};
103
+ for (const [key, raw] of Object.entries(template)) {
104
+ out[key] = raw.replace(/\$\{([A-Z0-9_]+)\}/g, (_m, name) => {
105
+ if (name.endsWith("_ORIGIN")) {
106
+ const baseName = name.slice(0, -"_ORIGIN".length);
107
+ const baseVal = envSource[baseName];
108
+ if (typeof baseVal === "string" && baseVal) {
109
+ try {
110
+ const u = new URL(baseVal);
111
+ if (u.pathname === "/search" || u.pathname === "/search/")
112
+ u.pathname = "";
113
+ return u.toString().replace(/\/$/, "");
114
+ }
115
+ catch {
116
+ return baseVal;
117
+ }
118
+ }
119
+ return _m;
120
+ }
121
+ const val = envSource[name];
122
+ return typeof val === "string" ? val : _m;
123
+ });
124
+ }
125
+ return out;
126
+ }
127
+ //# sourceMappingURL=write-mcp-entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write-mcp-entry.js","sourceRoot":"","sources":["../../../../src/services/runtime/mcp-shims/write-mcp-entry.ts"],"names":[],"mappings":"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"}
@@ -12,4 +12,12 @@
12
12
  *
13
13
  * This file is framework-level and must never import adapter code.
14
14
  */
15
+ import type { AppSpec } from "../../types.js";
15
16
  export declare function backfillInstanceMeta<T extends Record<string, any>>(raw: T | null | undefined): T | null;
17
+ /**
18
+ * Return a synthetic capability-only spec for a legacy instance based on
19
+ * its agentType. Returns null when the agent type has no known template
20
+ * (e.g. raw `custom` agents). Cached per-agentType — templates ship with
21
+ * the panel and don't change at runtime.
22
+ */
23
+ export declare function loadCapabilitySpecForLegacyInstance(meta: Record<string, any> | null): AppSpec | null;
@@ -12,6 +12,10 @@
12
12
  *
13
13
  * This file is framework-level and must never import adapter code.
14
14
  */
15
+ import { existsSync, readFileSync } from "fs";
16
+ import { join } from "path";
17
+ import { fileURLToPath } from "url";
18
+ import { parse } from "yaml";
15
19
  import { resolveAgentType } from "./instance.js";
16
20
  export function backfillInstanceMeta(raw) {
17
21
  if (!raw)
@@ -22,4 +26,100 @@ export function backfillInstanceMeta(raw) {
22
26
  mut.agentType = agentType;
23
27
  return raw;
24
28
  }
29
+ // ── Capability discovery for legacy instances ──────────────────────────────
30
+ //
31
+ // PR 1+2 introduced the capability registry / app-spec contract. Legacy
32
+ // hermes/openclaw instances created via the old "新建实例" flow only carry
33
+ // `instance.json` — they have no `app-spec.yaml`, no `manifest.json`, and
34
+ // no `app_id`. The Connections endpoint then early-returns
35
+ // `{requires:[], connections:{}, pending:[]}` and the UI shows "此应用没
36
+ // 有需要绑定的能力。" even though every hermes/openclaw runtime *should*
37
+ // expose the standard llm/search/browser/mcp slots.
38
+ //
39
+ // We deliberately do NOT write any files for these instances:
40
+ //
41
+ // - The shipped templates (`apps/openclaw-container.yaml`,
42
+ // `apps/hermes-container.yaml`) describe a *container* runtime
43
+ // (image / args / ports). Legacy instances may run as raw_exec on a
44
+ // local binary instead, with their real runtime in
45
+ // `instance.json#runtime`. Persisting the container spec would
46
+ // cause `nomad-manager.startAppJob(spec, ...)` to rebuild the job
47
+ // from the wrong runtime shape on next start, losing volume mounts
48
+ // and per-instance customisation.
49
+ //
50
+ // - The Connections feature only needs `spec.requires` (and a stable
51
+ // `spec.id`) to populate the UI. We can synthesize that in-memory
52
+ // from the matching template every time the Connections route is
53
+ // hit. There is no state to drift, nothing to migrate, nothing to
54
+ // undo.
55
+ //
56
+ // The synthetic spec returned by `loadCapabilitySpecForLegacyInstance`
57
+ // is therefore pruned to capability metadata only. Service routes
58
+ // (start / stop / restart) keep going through the legacy
59
+ // `svc.startInstance` path because `meta.app_id` stays unset — the
60
+ // runtime continues to come from `instance.json`.
61
+ const TEMPLATE_BY_AGENT_TYPE = {
62
+ hermes: "hermes-container.yaml",
63
+ openclaw: "openclaw-container.yaml",
64
+ };
65
+ function resolveTemplatePath(templateName) {
66
+ const candidates = [
67
+ fileURLToPath(new URL(`../../../apps/${templateName}`, import.meta.url)),
68
+ join(process.cwd(), "apps", templateName),
69
+ ];
70
+ for (const candidate of candidates) {
71
+ if (existsSync(candidate))
72
+ return candidate;
73
+ }
74
+ return null;
75
+ }
76
+ const specCache = new Map();
77
+ /**
78
+ * Return a synthetic capability-only spec for a legacy instance based on
79
+ * its agentType. Returns null when the agent type has no known template
80
+ * (e.g. raw `custom` agents). Cached per-agentType — templates ship with
81
+ * the panel and don't change at runtime.
82
+ */
83
+ export function loadCapabilitySpecForLegacyInstance(meta) {
84
+ if (!meta)
85
+ return null;
86
+ const agentType = typeof meta.agentType === "string" && meta.agentType
87
+ ? meta.agentType
88
+ : resolveAgentType(meta);
89
+ const templateName = TEMPLATE_BY_AGENT_TYPE[agentType];
90
+ if (!templateName)
91
+ return null;
92
+ if (specCache.has(agentType)) {
93
+ return specCache.get(agentType) ?? null;
94
+ }
95
+ const templatePath = resolveTemplatePath(templateName);
96
+ if (!templatePath) {
97
+ specCache.set(agentType, null);
98
+ return null;
99
+ }
100
+ try {
101
+ const raw = parse(readFileSync(templatePath, "utf-8"));
102
+ // Strip runtime — Connections only needs id/name/requires/provides.
103
+ // Leaving `tasks` in would tempt callers to (re)build a nomad job
104
+ // from a spec that doesn't match the instance's actual runtime.
105
+ const synthetic = {
106
+ id: raw.id,
107
+ name: raw.name,
108
+ version: raw.version,
109
+ agentType: raw.agentType,
110
+ description: raw.description,
111
+ singleInstance: raw.singleInstance,
112
+ tasks: [],
113
+ provides: raw.provides ?? [],
114
+ requires: raw.requires ?? [],
115
+ };
116
+ specCache.set(agentType, synthetic);
117
+ return synthetic;
118
+ }
119
+ catch (e) {
120
+ console.warn(`[migration] cannot load capability template '${templateName}': ${e.message}`);
121
+ specCache.set(agentType, null);
122
+ return null;
123
+ }
124
+ }
25
125
  //# sourceMappingURL=migrations.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../../src/services/runtime/migrations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjD,MAAM,UAAU,oBAAoB,CAClC,GAAyB;IAEzB,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,GAAG,GAAG,GAA0B,CAAC;IACvC,IAAI,GAAG,CAAC,SAAS,KAAK,SAAS;QAAE,GAAG,CAAC,SAAS,GAAG,SAAS,CAAC;IAC3D,OAAO,GAAG,CAAC;AACb,CAAC"}
1
+ {"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../../src/services/runtime/migrations.ts"],"names":[],"mappings":"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"}
@@ -55,6 +55,37 @@ export interface RuntimeSpec {
55
55
  ports?: RuntimePortSpec[];
56
56
  volumes?: RuntimeVolumeSpec[];
57
57
  health?: RuntimeHealthSpec | null;
58
+ /**
59
+ * Per-instance mounts of subtrees from `~/.jishushell/files/`.
60
+ *
61
+ * Each entry exposes one user file-tree subdirectory inside the
62
+ * container under a stable alias, optionally read-only.
63
+ *
64
+ * - Docker mode: enforced by Linux mount options (ro is real)
65
+ * - raw_exec / process mode: configuration only — the agent runs as
66
+ * the panel user and can ignore the ro flag (see vision G8)
67
+ *
68
+ * The structural defaults and built-in `agent-data/{id}` mount are
69
+ * applied by `services/instance-manager.ts` at create time.
70
+ */
71
+ fileMounts?: FileMount[];
72
+ }
73
+ /**
74
+ * One subtree of `~/.jishushell/files/` exposed to a single instance.
75
+ *
76
+ * - `path` is files/-relative; validated by path-safety
77
+ * - `mode` is the mount mode (ro/rw)
78
+ * - `alias` controls how the mount appears inside the container's
79
+ * workspace:
80
+ * - non-empty string → workspace/{alias} symlink to this subtree
81
+ * - "" or null → workspace IS this subtree (root mount;
82
+ * exclusive — at most one per instance)
83
+ * - undefined → defaults to basename(path)
84
+ */
85
+ export interface FileMount {
86
+ path: string;
87
+ mode: "ro" | "rw";
88
+ alias?: string | null;
58
89
  }
59
90
  export interface CapabilityProfile {
60
91
  gateway: {
@@ -354,6 +385,21 @@ export interface RuntimeAdapter {
354
385
  }>;
355
386
  /** Return the list of other instance IDs that share this one's agent-home. */
356
387
  findInstancesSharingHome?(instanceId: string): string[];
388
+ /**
389
+ * Translate a flat env map (`{SEARCH_API_BASE_URL, OPENAI_API_BASE_URL,
390
+ * MCP_SERVERS, ...}`) — produced by `connection-resolver` from the
391
+ * user's persisted bindings — into adapter-native config writes
392
+ * (Hermes' `mcp_servers` in config.yaml, OpenClaw's
393
+ * `plugins.entries.searxng.config.webSearch.baseUrl` in openclaw.json,
394
+ * etc.). Called both at PUT /connections time AND on every instance
395
+ * start (PR 9 phaseRefreshConnections), so host IP changes /
396
+ * provider redeployments propagate without a manual re-bind.
397
+ *
398
+ * Adapters are free to no-op for env keys they don't recognize.
399
+ * Failures should be logged but NOT thrown — start should proceed
400
+ * with whatever config is on disk.
401
+ */
402
+ applyConnectionEnv?(instanceId: string, env: Record<string, string>): Promise<void>;
357
403
  /** Resolve the canonical agent binary path (OpenClaw only). */
358
404
  resolveBin?(): string;
359
405
  /** Resolve the instance's on-disk config file path. */