synthos 0.7.2 → 0.9.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 (380) hide show
  1. package/README.md +215 -65
  2. package/default-pages/application/page.html +42 -0
  3. package/default-pages/application/page.json +10 -0
  4. package/default-pages/elevenlabs_effects_studio/page.html +1363 -0
  5. package/default-pages/elevenlabs_effects_studio/page.json +11 -0
  6. package/default-pages/elevenlabs_voice_studio/page.html +801 -0
  7. package/default-pages/elevenlabs_voice_studio/page.json +11 -0
  8. package/default-pages/{json_tools.html → json_tools/page.html} +13 -11
  9. package/default-pages/json_tools/page.json +10 -0
  10. package/default-pages/my_notes/notes/a1b2c3d4-e5f6-7890-abcd-ef1234567890.json +5 -0
  11. package/default-pages/my_notes/page.html +132 -0
  12. package/default-pages/{my_notes.json → my_notes/page.json} +2 -2
  13. package/default-pages/neon_asteroids/files/Ambient_Space.mp3 +0 -0
  14. package/default-pages/neon_asteroids/files/Ambient_Space2.mp3 +0 -0
  15. package/default-pages/neon_asteroids/files/Ambient_Space3.mp3 +0 -0
  16. package/default-pages/neon_asteroids/files/Asteroid_Explosion.mp3 +0 -0
  17. package/default-pages/neon_asteroids/files/Hyperspace_Jump.mp3 +0 -0
  18. package/default-pages/neon_asteroids/files/Laser_Fire.mp3 +0 -0
  19. package/default-pages/neon_asteroids/files/Menu_Navigate.mp3 +0 -0
  20. package/default-pages/neon_asteroids/files/Power_Up_Collect.mp3 +0 -0
  21. package/default-pages/neon_asteroids/files/Saucer_Alert.mp3 +0 -0
  22. package/default-pages/neon_asteroids/files/Ship_Thrust.mp3 +0 -0
  23. package/default-pages/neon_asteroids/files/effects.json +74 -0
  24. package/default-pages/neon_asteroids/page.html +1822 -0
  25. package/default-pages/{neon_asteroids.json → neon_asteroids/page.json} +3 -3
  26. package/default-pages/oregon_trail/page.html +323 -0
  27. package/default-pages/oregon_trail/page.json +12 -0
  28. package/default-pages/retro_game_starter/page.html +1308 -0
  29. package/default-pages/retro_game_starter/page.json +12 -0
  30. package/default-pages/{sidebar_builder.html → sidebar_page/page.html} +12 -10
  31. package/default-pages/sidebar_page/page.json +10 -0
  32. package/default-pages/{solar_explorer.html → solar_explorer/page.html} +24 -29
  33. package/default-pages/{solar_explorer.json → solar_explorer/page.json} +4 -4
  34. package/default-pages/{solar_tutorial.html → solar_tutorial/page.html} +12 -10
  35. package/default-pages/solar_tutorial/page.json +10 -0
  36. package/default-pages/{two-panel_builder.html → two-panel_page/page.html} +13 -11
  37. package/default-pages/two-panel_page/page.json +10 -0
  38. package/default-pages/us_map/page.html +193 -0
  39. package/default-pages/us_map/page.json +12 -0
  40. package/default-pages/us_map_1850/page.html +326 -0
  41. package/default-pages/us_map_1850/page.json +12 -0
  42. package/default-pages/western_cities_1850/page.html +527 -0
  43. package/default-pages/western_cities_1850/page.json +12 -0
  44. package/default-themes/aurora-dawn.json +19 -0
  45. package/default-themes/aurora-dawn.v3.css +198 -0
  46. package/default-themes/aurora-dusk.json +19 -0
  47. package/default-themes/aurora-dusk.v3.css +200 -0
  48. package/default-themes/cosmos-dawn.json +19 -0
  49. package/default-themes/cosmos-dawn.v3.css +198 -0
  50. package/default-themes/cosmos-dusk.json +19 -0
  51. package/default-themes/cosmos-dusk.v3.css +200 -0
  52. package/default-themes/high-contrast-dark.json +19 -0
  53. package/default-themes/high-contrast-dark.v3.css +200 -0
  54. package/default-themes/high-contrast-light.json +19 -0
  55. package/default-themes/high-contrast-light.v3.css +198 -0
  56. package/default-themes/{nebula-dawn.css → nebula-dawn.v2.css} +134 -0
  57. package/default-themes/nebula-dawn.v3.css +199 -0
  58. package/default-themes/{nebula-dusk.css → nebula-dusk.v2.css} +128 -0
  59. package/default-themes/nebula-dusk.v3.css +201 -0
  60. package/default-themes/solar-flare-dawn.json +19 -0
  61. package/default-themes/solar-flare-dawn.v3.css +198 -0
  62. package/default-themes/solar-flare-dusk.json +19 -0
  63. package/default-themes/solar-flare-dusk.v3.css +200 -0
  64. package/dist/agents/a2a/a2aProvider.d.ts.map +1 -0
  65. package/dist/agents/a2a/a2aProvider.js +126 -0
  66. package/dist/agents/a2a/a2aProvider.js.map +1 -0
  67. package/dist/agents/discovery.d.ts.map +1 -0
  68. package/dist/agents/discovery.js +52 -0
  69. package/dist/agents/discovery.js.map +1 -0
  70. package/dist/agents/index.d.ts +7 -0
  71. package/dist/agents/index.d.ts.map +1 -0
  72. package/dist/agents/index.js +20 -0
  73. package/dist/agents/index.js.map +1 -0
  74. package/dist/agents/openclaw/gatewayManager.d.ts +117 -0
  75. package/dist/agents/openclaw/gatewayManager.d.ts.map +1 -0
  76. package/dist/agents/openclaw/gatewayManager.js +486 -0
  77. package/dist/agents/openclaw/gatewayManager.js.map +1 -0
  78. package/dist/agents/openclaw/openclawProvider.d.ts.map +1 -0
  79. package/dist/agents/openclaw/openclawProvider.js +237 -0
  80. package/dist/agents/openclaw/openclawProvider.js.map +1 -0
  81. package/dist/agents/openclaw/sshTunnelManager.d.ts +25 -0
  82. package/dist/agents/openclaw/sshTunnelManager.d.ts.map +1 -0
  83. package/dist/agents/openclaw/sshTunnelManager.js +359 -0
  84. package/dist/agents/openclaw/sshTunnelManager.js.map +1 -0
  85. package/dist/agents/types.d.ts.map +1 -0
  86. package/dist/agents/types.js +6 -0
  87. package/dist/agents/types.js.map +1 -0
  88. package/dist/builders/anthropic.d.ts +31 -0
  89. package/dist/builders/anthropic.d.ts.map +1 -0
  90. package/dist/builders/anthropic.js +227 -0
  91. package/dist/builders/anthropic.js.map +1 -0
  92. package/dist/builders/fireworksai.d.ts +9 -0
  93. package/dist/builders/fireworksai.d.ts.map +1 -0
  94. package/dist/builders/fireworksai.js +57 -0
  95. package/dist/builders/fireworksai.js.map +1 -0
  96. package/dist/builders/index.d.ts +13 -0
  97. package/dist/builders/index.d.ts.map +1 -0
  98. package/dist/builders/index.js +31 -0
  99. package/dist/builders/index.js.map +1 -0
  100. package/dist/builders/openai.d.ts +8 -0
  101. package/dist/builders/openai.d.ts.map +1 -0
  102. package/dist/builders/openai.js +87 -0
  103. package/dist/builders/openai.js.map +1 -0
  104. package/dist/builders/types.d.ts +54 -0
  105. package/dist/builders/types.d.ts.map +1 -0
  106. package/dist/builders/types.js +211 -0
  107. package/dist/builders/types.js.map +1 -0
  108. package/dist/connectors/index.d.ts.map +1 -1
  109. package/dist/connectors/index.js +3 -2
  110. package/dist/connectors/index.js.map +1 -1
  111. package/dist/connectors/registry.d.ts +2 -1
  112. package/dist/connectors/registry.d.ts.map +1 -1
  113. package/dist/connectors/registry.js +65 -96
  114. package/dist/connectors/registry.js.map +1 -1
  115. package/dist/connectors/types.d.ts.map +1 -1
  116. package/dist/customizer/Customizer.d.ts +57 -0
  117. package/dist/customizer/Customizer.d.ts.map +1 -0
  118. package/dist/customizer/Customizer.js +124 -0
  119. package/dist/customizer/Customizer.js.map +1 -0
  120. package/dist/customizer/index.d.ts.map +1 -0
  121. package/dist/customizer/index.js +9 -0
  122. package/dist/customizer/index.js.map +1 -0
  123. package/dist/files.d.ts +17 -0
  124. package/dist/files.d.ts.map +1 -1
  125. package/dist/files.js +75 -1
  126. package/dist/files.js.map +1 -1
  127. package/dist/index.d.ts.map +1 -1
  128. package/dist/index.js +1 -0
  129. package/dist/index.js.map +1 -1
  130. package/dist/init.d.ts +10 -6
  131. package/dist/init.d.ts.map +1 -1
  132. package/dist/init.js +97 -86
  133. package/dist/init.js.map +1 -1
  134. package/dist/migrations.d.ts.map +1 -1
  135. package/dist/migrations.js +142 -145
  136. package/dist/migrations.js.map +1 -1
  137. package/dist/models/anthropic.d.ts +24 -0
  138. package/dist/models/anthropic.d.ts.map +1 -0
  139. package/dist/models/anthropic.js +103 -0
  140. package/dist/models/anthropic.js.map +1 -0
  141. package/dist/models/chainOfThought.d.ts.map +1 -0
  142. package/dist/models/chainOfThought.js +45 -0
  143. package/dist/models/chainOfThought.js.map +1 -0
  144. package/dist/models/fireworksai.d.ts.map +1 -0
  145. package/dist/models/fireworksai.js +141 -0
  146. package/dist/models/fireworksai.js.map +1 -0
  147. package/dist/models/index.d.ts +7 -1
  148. package/dist/models/index.d.ts.map +1 -1
  149. package/dist/models/index.js +20 -1
  150. package/dist/models/index.js.map +1 -1
  151. package/dist/models/logCompletePrompt.d.ts.map +1 -0
  152. package/dist/models/logCompletePrompt.js +23 -0
  153. package/dist/models/logCompletePrompt.js.map +1 -0
  154. package/dist/models/openai.d.ts +24 -0
  155. package/dist/models/openai.d.ts.map +1 -0
  156. package/dist/models/openai.js +101 -0
  157. package/dist/models/openai.js.map +1 -0
  158. package/dist/models/providers.d.ts.map +1 -1
  159. package/dist/models/providers.js +12 -4
  160. package/dist/models/providers.js.map +1 -1
  161. package/dist/models/types.d.ts +53 -2
  162. package/dist/models/types.d.ts.map +1 -1
  163. package/dist/models/types.js +21 -0
  164. package/dist/models/types.js.map +1 -1
  165. package/dist/models/utils.d.ts.map +1 -0
  166. package/dist/models/utils.js +21 -0
  167. package/dist/models/utils.js.map +1 -0
  168. package/dist/pages.d.ts +30 -7
  169. package/dist/pages.d.ts.map +1 -1
  170. package/dist/pages.js +177 -55
  171. package/dist/pages.js.map +1 -1
  172. package/dist/scripts.d.ts.map +1 -1
  173. package/dist/scripts.js +4 -3
  174. package/dist/scripts.js.map +1 -1
  175. package/dist/service/createCompletePrompt.d.ts.map +1 -1
  176. package/dist/service/createCompletePrompt.js +9 -6
  177. package/dist/service/createCompletePrompt.js.map +1 -1
  178. package/dist/service/generateImage.d.ts.map +1 -1
  179. package/dist/service/generateImage.js +3 -3
  180. package/dist/service/generateImage.js.map +1 -1
  181. package/dist/service/server.d.ts.map +1 -1
  182. package/dist/service/server.js +39 -7
  183. package/dist/service/server.js.map +1 -1
  184. package/dist/service/transformPage.d.ts +47 -18
  185. package/dist/service/transformPage.d.ts.map +1 -1
  186. package/dist/service/transformPage.js +559 -270
  187. package/dist/service/transformPage.js.map +1 -1
  188. package/dist/service/useAgentRoutes.d.ts +5 -0
  189. package/dist/service/useAgentRoutes.d.ts.map +1 -0
  190. package/dist/service/useAgentRoutes.js +392 -0
  191. package/dist/service/useAgentRoutes.js.map +1 -0
  192. package/dist/service/useApiRoutes.d.ts.map +1 -1
  193. package/dist/service/useApiRoutes.js +380 -138
  194. package/dist/service/useApiRoutes.js.map +1 -1
  195. package/dist/service/useConnectorRoutes.d.ts.map +1 -1
  196. package/dist/service/useConnectorRoutes.js +20 -9
  197. package/dist/service/useConnectorRoutes.js.map +1 -1
  198. package/dist/service/useFileRoutes.d.ts +4 -0
  199. package/dist/service/useFileRoutes.d.ts.map +1 -0
  200. package/dist/service/useFileRoutes.js +122 -0
  201. package/dist/service/useFileRoutes.js.map +1 -0
  202. package/dist/service/usePageRoutes.d.ts.map +1 -1
  203. package/dist/service/usePageRoutes.js +660 -68
  204. package/dist/service/usePageRoutes.js.map +1 -1
  205. package/dist/service/useSharedDataRoutes.d.ts +4 -0
  206. package/dist/service/useSharedDataRoutes.d.ts.map +1 -0
  207. package/dist/service/useSharedDataRoutes.js +104 -0
  208. package/dist/service/useSharedDataRoutes.js.map +1 -0
  209. package/dist/service/useSharedFileRoutes.d.ts +4 -0
  210. package/dist/service/useSharedFileRoutes.d.ts.map +1 -0
  211. package/dist/service/useSharedFileRoutes.js +121 -0
  212. package/dist/service/useSharedFileRoutes.js.map +1 -0
  213. package/dist/settings.d.ts +3 -1
  214. package/dist/settings.d.ts.map +1 -1
  215. package/dist/settings.js +5 -8
  216. package/dist/settings.js.map +1 -1
  217. package/dist/synthos-cli.d.ts.map +1 -1
  218. package/dist/synthos-cli.js +4 -3
  219. package/dist/synthos-cli.js.map +1 -1
  220. package/dist/themes.d.ts +15 -0
  221. package/dist/themes.d.ts.map +1 -1
  222. package/dist/themes.js +106 -20
  223. package/dist/themes.js.map +1 -1
  224. package/migration-rules/v1-to-v2.md +193 -0
  225. package/migration-rules/v2-to-v3.md +481 -0
  226. package/package.json +15 -11
  227. package/required-pages/builder/page.html +43 -0
  228. package/required-pages/builder/page.json +10 -0
  229. package/required-pages/pages/page.html +924 -0
  230. package/required-pages/pages/page.json +10 -0
  231. package/required-pages/settings/page.html +1753 -0
  232. package/required-pages/settings/page.json +10 -0
  233. package/required-pages/synthos_apis/page.html +846 -0
  234. package/required-pages/synthos_apis/page.json +10 -0
  235. package/required-pages/{synthos_scripts.html → synthos_scripts/page.html} +13 -11
  236. package/required-pages/synthos_scripts/page.json +10 -0
  237. package/service-connectors/airtable/connector.json +27 -0
  238. package/service-connectors/alpha-vantage/connector.json +26 -0
  239. package/service-connectors/brave-search/connector.json +26 -0
  240. package/service-connectors/cloudinary/connector.json +27 -0
  241. package/service-connectors/deepl/connector.json +28 -0
  242. package/service-connectors/elevenlabs/connector.json +30 -0
  243. package/service-connectors/giphy/connector.json +27 -0
  244. package/service-connectors/github/connector.json +29 -0
  245. package/service-connectors/huggingface/connector.json +27 -0
  246. package/service-connectors/imgur/connector.json +29 -0
  247. package/service-connectors/instagram/connector.json +43 -0
  248. package/service-connectors/jira/connector.json +28 -0
  249. package/service-connectors/mapbox/connector.json +26 -0
  250. package/service-connectors/nasa/connector.json +27 -0
  251. package/service-connectors/newsapi/connector.json +27 -0
  252. package/service-connectors/notion/connector.json +28 -0
  253. package/service-connectors/open-exchange-rates/connector.json +27 -0
  254. package/service-connectors/openweathermap/connector.json +26 -0
  255. package/service-connectors/pexels/connector.json +27 -0
  256. package/service-connectors/resend/connector.json +29 -0
  257. package/service-connectors/rss2json/connector.json +27 -0
  258. package/service-connectors/sendgrid/connector.json +27 -0
  259. package/service-connectors/spoonacular/connector.json +28 -0
  260. package/service-connectors/stability-ai/connector.json +27 -0
  261. package/service-connectors/twilio/connector.json +28 -0
  262. package/service-connectors/unsplash/connector.json +27 -0
  263. package/service-connectors/wolfram-alpha/connector.json +26 -0
  264. package/service-connectors/youtube-data/connector.json +30 -0
  265. package/src/agents/a2a/a2aProvider.ts +110 -0
  266. package/src/agents/discovery.ts +74 -0
  267. package/src/agents/index.ts +6 -0
  268. package/src/agents/openclaw/gatewayManager.ts +570 -0
  269. package/src/agents/openclaw/openclawProvider.ts +259 -0
  270. package/src/agents/openclaw/sshTunnelManager.ts +393 -0
  271. package/src/agents/types.ts +82 -0
  272. package/src/builders/anthropic.ts +283 -0
  273. package/src/builders/fireworksai.ts +59 -0
  274. package/src/builders/index.ts +33 -0
  275. package/src/builders/openai.ts +89 -0
  276. package/src/builders/types.ts +261 -0
  277. package/src/connectors/index.ts +3 -1
  278. package/src/connectors/registry.ts +40 -96
  279. package/src/connectors/types.ts +25 -0
  280. package/src/customizer/Customizer.ts +151 -0
  281. package/src/customizer/index.ts +5 -0
  282. package/src/files.ts +71 -0
  283. package/src/index.ts +2 -1
  284. package/src/init.ts +138 -97
  285. package/src/migrations.ts +148 -145
  286. package/src/models/anthropic.ts +119 -0
  287. package/src/models/chainOfThought.ts +56 -0
  288. package/src/models/fireworksai.ts +143 -0
  289. package/src/models/index.ts +7 -1
  290. package/src/models/logCompletePrompt.ts +25 -0
  291. package/src/models/openai.ts +110 -0
  292. package/src/models/providers.ts +12 -3
  293. package/src/models/types.ts +97 -2
  294. package/src/models/utils.ts +16 -0
  295. package/src/pages.ts +176 -54
  296. package/src/scripts.ts +2 -2
  297. package/src/service/createCompletePrompt.ts +3 -1
  298. package/src/service/generateImage.ts +2 -2
  299. package/src/service/server.ts +39 -8
  300. package/src/service/transformPage.ts +605 -301
  301. package/src/service/useAgentRoutes.ts +428 -0
  302. package/src/service/useApiRoutes.ts +309 -45
  303. package/src/service/useConnectorRoutes.ts +21 -10
  304. package/src/service/useFileRoutes.ts +127 -0
  305. package/src/service/usePageRoutes.ts +736 -75
  306. package/src/service/useSharedDataRoutes.ts +106 -0
  307. package/src/service/useSharedFileRoutes.ts +126 -0
  308. package/src/settings.ts +8 -10
  309. package/src/synthos-cli.ts +4 -3
  310. package/src/themes.ts +103 -20
  311. package/static-files/favicon.svg +12 -0
  312. package/static-files/fluentlm-instructions.llmd +868 -0
  313. package/static-files/fluentlm-instructions.md +1595 -0
  314. package/static-files/fluentlm.css +4844 -0
  315. package/static-files/fluentlm.js +3602 -0
  316. package/static-files/fluentlm.min.css +1 -0
  317. package/static-files/fluentlm.min.js +1 -0
  318. package/static-files/helpers.v3.js +304 -0
  319. package/static-files/page.v3.js +1290 -0
  320. package/static-files/recommended-frameworks.llmd +81 -0
  321. package/static-files/recommended-frameworks.md +137 -0
  322. package/static-files/retro-game.js +877 -0
  323. package/static-files/shell.css +797 -0
  324. package/static-files/theme-dark.css +169 -0
  325. package/static-files/theme-light.css +169 -0
  326. package/tests/anthropic.spec.ts +84 -0
  327. package/tests/builders.spec.ts +139 -0
  328. package/tests/chainOfThought.spec.ts +108 -0
  329. package/tests/ensureScripts.spec.ts +82 -0
  330. package/tests/files.spec.ts +233 -0
  331. package/tests/fireworksai.spec.ts +92 -0
  332. package/tests/logCompletePrompt.spec.ts +74 -0
  333. package/tests/migrations.spec.ts +79 -1
  334. package/tests/openai.spec.ts +71 -0
  335. package/tests/pages.spec.ts +226 -1
  336. package/tests/providers.spec.ts +144 -0
  337. package/tests/scripts.spec.ts +209 -0
  338. package/tests/transformPage.spec.ts +456 -0
  339. package/tests/types.spec.ts +23 -0
  340. package/default-pages/app_builder.html +0 -40
  341. package/default-pages/app_builder.json +0 -1
  342. package/default-pages/json_tools.json +0 -1
  343. package/default-pages/my_notes.html +0 -33
  344. package/default-pages/neon_asteroids.html +0 -77
  345. package/default-pages/sidebar_builder.json +0 -1
  346. package/default-pages/solar_tutorial.json +0 -1
  347. package/default-pages/two-panel_builder.json +0 -1
  348. package/dist/connectors/index.d.ts +0 -3
  349. package/dist/connectors/types.d.ts +0 -61
  350. package/dist/index.d.ts +0 -7
  351. package/dist/migrations.d.ts +0 -11
  352. package/dist/models/providers.d.ts +0 -7
  353. package/dist/scripts.d.ts +0 -14
  354. package/dist/service/createCompletePrompt.d.ts +0 -5
  355. package/dist/service/debugLog.d.ts +0 -11
  356. package/dist/service/generateImage.d.ts +0 -32
  357. package/dist/service/index.d.ts +0 -8
  358. package/dist/service/modelInstructions.d.ts +0 -7
  359. package/dist/service/requiresSettings.d.ts +0 -3
  360. package/dist/service/server.d.ts +0 -4
  361. package/dist/service/useApiRoutes.d.ts +0 -4
  362. package/dist/service/useConnectorRoutes.d.ts +0 -4
  363. package/dist/service/useDataRoutes.d.ts +0 -4
  364. package/dist/service/usePageRoutes.d.ts +0 -5
  365. package/dist/synthos-cli.d.ts +0 -2
  366. package/images/home.png +0 -0
  367. package/images/page-management.png +0 -0
  368. package/images/settings.png +0 -0
  369. package/images/synthos-square.png +0 -0
  370. package/page-scripts/helpers-v2.js +0 -121
  371. package/page-scripts/page-v2.js +0 -615
  372. package/required-pages/builder.html +0 -74
  373. package/required-pages/builder.json +0 -1
  374. package/required-pages/pages.html +0 -196
  375. package/required-pages/pages.json +0 -1
  376. package/required-pages/settings.html +0 -841
  377. package/required-pages/settings.json +0 -1
  378. package/required-pages/synthos_apis.html +0 -272
  379. package/required-pages/synthos_apis.json +0 -1
  380. package/required-pages/synthos_scripts.json +0 -1
@@ -0,0 +1,428 @@
1
+ import { Application } from 'express';
2
+ import { SynthOSConfig } from '../init';
3
+ import { loadSettings, saveSettings } from '../settings';
4
+ import {
5
+ AgentConfig,
6
+ AgentProvider,
7
+ a2aProvider,
8
+ openclawProvider,
9
+ discoverA2AAgent,
10
+ discoverOpenClawAgent,
11
+ connectAgent,
12
+ disconnectAgent,
13
+ getAgentStatus,
14
+ getTunnelStatus,
15
+ setOpenClawDebug,
16
+ } from '../agents';
17
+ import { v4 as uuidv4 } from 'uuid';
18
+ import { Customizer } from '../customizer';
19
+
20
+ export function useAgentRoutes(config: SynthOSConfig, app: Application, customizer?: Customizer): void {
21
+ // Enable OpenClaw debug logging only when --debug is passed
22
+ setOpenClawDebug(config.debug);
23
+
24
+ /** Strip the token and sshTunnel.password fields, add connection/tunnel status for agent responses. */
25
+ function toClientAgent(agent: AgentConfig): Record<string, unknown> {
26
+ const { token: _, sshTunnel, ...rest } = agent;
27
+ const status = agent.provider === 'openclaw' ? getAgentStatus(agent.id) : undefined;
28
+ const tunnelStatus = sshTunnel?.enabled ? getTunnelStatus(agent.id) : undefined;
29
+ // Expose sshTunnel config without the password
30
+ const sshTunnelClient = sshTunnel ? { enabled: sshTunnel.enabled, command: sshTunnel.command } : undefined;
31
+ return {
32
+ ...rest,
33
+ ...(sshTunnelClient ? { sshTunnel: sshTunnelClient } : {}),
34
+ ...(status ? { connected: status.connected && status.authenticated } : {}),
35
+ ...(tunnelStatus ? { tunnelStatus } : {}),
36
+ };
37
+ }
38
+
39
+ /** Try to connect an OpenClaw agent (fire-and-forget, logs errors). */
40
+ function tryConnectAgent(agent: AgentConfig): void {
41
+ if (agent.provider !== 'openclaw' || !agent.token || !agent.enabled) return;
42
+ connectAgent({
43
+ id: agent.id,
44
+ name: agent.name,
45
+ url: agent.url,
46
+ token: agent.token,
47
+ sshTunnel: agent.sshTunnel,
48
+ productName: customizer?.productName,
49
+ })
50
+ .then(() => console.log(`[Agents] Auto-connected OpenClaw agent "${agent.name}"`))
51
+ .catch(err => console.warn(`[Agents] Auto-connect failed for "${agent.name}": ${err instanceof Error ? err.message : err}`));
52
+ }
53
+
54
+ // Auto-connect all enabled OpenClaw agents on startup
55
+ (async () => {
56
+ try {
57
+ const settings = await loadSettings(config.pagesFolder);
58
+ for (const agent of settings.agents ?? []) {
59
+ tryConnectAgent(agent);
60
+ }
61
+ } catch (err) {
62
+ console.warn('[Agents] Failed to auto-connect agents on startup:', err);
63
+ }
64
+ })();
65
+
66
+ // GET /api/agents — List configured agents (with optional filters)
67
+ app.get('/api/agents', async (req, res) => {
68
+ try {
69
+ const settings = await loadSettings(config.pagesFolder);
70
+ let agents = settings.agents ?? [];
71
+
72
+ // Filter by enabled
73
+ if (req.query.enabled === 'true') {
74
+ agents = agents.filter(a => a.enabled);
75
+ }
76
+
77
+ // Filter by provider
78
+ if (typeof req.query.provider === 'string') {
79
+ agents = agents.filter(a => a.provider === req.query.provider);
80
+ }
81
+
82
+ res.json(agents.map(toClientAgent));
83
+ } catch (err: unknown) {
84
+ console.error(err);
85
+ res.status(500).json({ error: (err as Error).message });
86
+ }
87
+ });
88
+
89
+ // POST /api/agents/discover — Discover agent by URL + type
90
+ app.post('/api/agents/discover', async (req, res) => {
91
+ try {
92
+ const { url, type, token } = req.body;
93
+ if (!url || typeof url !== 'string') {
94
+ res.status(400).json({ error: 'url is required' });
95
+ return;
96
+ }
97
+
98
+ if (type === 'openclaw') {
99
+ if (!token || typeof token !== 'string') {
100
+ res.status(400).json({ error: 'token is required for OpenClaw discovery' });
101
+ return;
102
+ }
103
+ const result = await discoverOpenClawAgent(url, token);
104
+ res.json(result);
105
+ } else {
106
+ // Default to A2A
107
+ const card = await discoverA2AAgent(url);
108
+ res.json(card);
109
+ }
110
+ } catch (err: unknown) {
111
+ console.error(err);
112
+ res.status(502).json({ error: `Failed to discover agent: ${(err as Error).message}` });
113
+ }
114
+ });
115
+
116
+ // POST /api/agents — Upsert agent config
117
+ app.post('/api/agents', async (req, res) => {
118
+ try {
119
+ const body = req.body as Partial<AgentConfig>;
120
+ if (!body.url || !body.name) {
121
+ res.status(400).json({ error: 'url and name are required' });
122
+ return;
123
+ }
124
+
125
+ const settings = await loadSettings(config.pagesFolder);
126
+ const agents = settings.agents ? [...settings.agents] : [];
127
+
128
+ const agentConfig: AgentConfig = {
129
+ id: body.id || uuidv4(),
130
+ url: body.url,
131
+ name: body.name,
132
+ description: body.description || '',
133
+ iconUrl: body.iconUrl,
134
+ enabled: body.enabled ?? true,
135
+ provider: body.provider ?? 'a2a',
136
+ token: body.token,
137
+ sessionKey: body.sessionKey,
138
+ capabilities: body.capabilities,
139
+ skills: body.skills,
140
+ sshTunnel: body.sshTunnel,
141
+ };
142
+
143
+ // Upsert: replace if same id exists, otherwise append
144
+ const idx = agents.findIndex(a => a.id === agentConfig.id);
145
+ if (idx !== -1) {
146
+ // Preserve existing token/sessionKey if not provided in update
147
+ if (!agentConfig.token && agents[idx].token) {
148
+ agentConfig.token = agents[idx].token;
149
+ }
150
+ if (!agentConfig.sessionKey && agents[idx].sessionKey) {
151
+ agentConfig.sessionKey = agents[idx].sessionKey;
152
+ }
153
+ // Preserve existing sshTunnel if not provided in update
154
+ if (!agentConfig.sshTunnel && agents[idx].sshTunnel) {
155
+ agentConfig.sshTunnel = agents[idx].sshTunnel;
156
+ }
157
+ agents[idx] = agentConfig;
158
+ } else {
159
+ agents.push(agentConfig);
160
+ }
161
+
162
+ await saveSettings(config.pagesFolder, { agents });
163
+
164
+ // Auto-connect OpenClaw agents after save
165
+ tryConnectAgent(agentConfig);
166
+
167
+ res.json(toClientAgent(agentConfig));
168
+ } catch (err: unknown) {
169
+ console.error(err);
170
+ res.status(500).json({ error: (err as Error).message });
171
+ }
172
+ });
173
+
174
+ // PATCH /api/agents/:id — Toggle enabled/disabled or partial update
175
+ app.patch('/api/agents/:id', async (req, res) => {
176
+ try {
177
+ const { id } = req.params;
178
+ const settings = await loadSettings(config.pagesFolder);
179
+ const agents = settings.agents ? [...settings.agents] : [];
180
+ const idx = agents.findIndex(a => a.id === id);
181
+ if (idx === -1) {
182
+ res.status(404).json({ error: `Agent "${id}" not found` });
183
+ return;
184
+ }
185
+
186
+ const body = req.body as Partial<AgentConfig>;
187
+ if (typeof body.enabled === 'boolean') agents[idx].enabled = body.enabled;
188
+ if (typeof body.name === 'string') agents[idx].name = body.name;
189
+ if (typeof body.description === 'string') agents[idx].description = body.description;
190
+
191
+ await saveSettings(config.pagesFolder, { agents });
192
+
193
+ // Connect or disconnect based on enabled state
194
+ if (agents[idx].provider === 'openclaw') {
195
+ if (agents[idx].enabled) {
196
+ tryConnectAgent(agents[idx]);
197
+ } else {
198
+ disconnectAgent(agents[idx].id);
199
+ }
200
+ }
201
+
202
+ res.json(toClientAgent(agents[idx]));
203
+ } catch (err: unknown) {
204
+ console.error(err);
205
+ res.status(500).json({ error: (err as Error).message });
206
+ }
207
+ });
208
+
209
+ // DELETE /api/agents/:id — Remove agent
210
+ app.delete('/api/agents/:id', async (req, res) => {
211
+ try {
212
+ const { id } = req.params;
213
+ const settings = await loadSettings(config.pagesFolder);
214
+ const agent = (settings.agents ?? []).find(a => a.id === id);
215
+
216
+ // Disconnect if OpenClaw
217
+ if (agent?.provider === 'openclaw') {
218
+ disconnectAgent(id);
219
+ }
220
+
221
+ const agents = (settings.agents ?? []).filter(a => a.id !== id);
222
+ await saveSettings(config.pagesFolder, { agents });
223
+ res.json({ deleted: true });
224
+ } catch (err: unknown) {
225
+ console.error(err);
226
+ res.status(500).json({ error: (err as Error).message });
227
+ }
228
+ });
229
+
230
+ // POST /api/agents/:id/connect — Manually trigger WebSocket connection
231
+ app.post('/api/agents/:id/connect', async (req, res) => {
232
+ try {
233
+ const { id } = req.params;
234
+ const settings = await loadSettings(config.pagesFolder);
235
+ const agent = (settings.agents ?? []).find(a => a.id === id);
236
+ if (!agent) {
237
+ res.status(404).json({ error: `Agent "${id}" not found` });
238
+ return;
239
+ }
240
+ if (agent.provider !== 'openclaw') {
241
+ res.status(400).json({ error: 'Only OpenClaw agents support WebSocket connections' });
242
+ return;
243
+ }
244
+ if (!agent.token) {
245
+ res.status(400).json({ error: 'Agent has no token configured' });
246
+ return;
247
+ }
248
+
249
+ await connectAgent({ id: agent.id, name: agent.name, url: agent.url, token: agent.token, sshTunnel: agent.sshTunnel, productName: customizer?.productName });
250
+ const status = getAgentStatus(agent.id);
251
+ res.json({ connected: status.connected, authenticated: status.authenticated });
252
+ } catch (err: unknown) {
253
+ console.error(err);
254
+ res.status(500).json({ error: (err as Error).message });
255
+ }
256
+ });
257
+
258
+ // POST /api/agents/:id/disconnect — Disconnect WebSocket
259
+ app.post('/api/agents/:id/disconnect', async (req, res) => {
260
+ try {
261
+ const { id } = req.params;
262
+ disconnectAgent(id);
263
+ res.json({ disconnected: true });
264
+ } catch (err: unknown) {
265
+ console.error(err);
266
+ res.status(500).json({ error: (err as Error).message });
267
+ }
268
+ });
269
+
270
+ // POST /api/agents/:id/send — Send message to agent (dispatches by provider)
271
+ app.post('/api/agents/:id/send', async (req, res) => {
272
+ try {
273
+ const { id } = req.params;
274
+ const { message, attachments } = req.body;
275
+ if (!message || typeof message !== 'string') {
276
+ res.status(400).json({ error: 'message is required' });
277
+ return;
278
+ }
279
+
280
+ const settings = await loadSettings(config.pagesFolder);
281
+ const agent = (settings.agents ?? []).find(a => a.id === id);
282
+ if (!agent) {
283
+ res.status(404).json({ error: `Agent "${id}" not found` });
284
+ return;
285
+ }
286
+
287
+ if (!agent.enabled) {
288
+ res.status(400).json({ error: `Agent "${agent.name}" is disabled` });
289
+ return;
290
+ }
291
+
292
+ // Dispatch to the correct provider
293
+ const provider = agent.provider === 'openclaw' ? openclawProvider : a2aProvider;
294
+ const result = await provider.send(agent, message, attachments);
295
+ res.json(result);
296
+ } catch (err: unknown) {
297
+ console.error(err);
298
+ res.status(500).json({ error: (err as Error).message });
299
+ }
300
+ });
301
+
302
+ // -----------------------------------------------------------------------
303
+ // Helper: load agent + resolve provider, return 404/400 on failure
304
+ // -----------------------------------------------------------------------
305
+ async function withAgent(
306
+ id: string,
307
+ res: import('express').Response,
308
+ fn: (agent: AgentConfig, provider: AgentProvider) => Promise<void>,
309
+ ): Promise<void> {
310
+ const settings = await loadSettings(config.pagesFolder);
311
+ const agent = (settings.agents ?? []).find(a => a.id === id);
312
+ if (!agent) {
313
+ res.status(404).json({ error: `Agent "${id}" not found` });
314
+ return;
315
+ }
316
+ if (!agent.enabled) {
317
+ res.status(400).json({ error: `Agent "${agent.name}" is disabled` });
318
+ return;
319
+ }
320
+ const provider: AgentProvider = agent.provider === 'openclaw' ? openclawProvider : a2aProvider;
321
+ await fn(agent, provider);
322
+ }
323
+
324
+ // -----------------------------------------------------------------------
325
+ // Chat lifecycle routes (Phase 1)
326
+ // -----------------------------------------------------------------------
327
+
328
+ // POST /api/agents/:id/chat/history — Get chat history
329
+ app.post('/api/agents/:id/chat/history', async (req, res) => {
330
+ try {
331
+ await withAgent(req.params.id, res, async (agent, provider) => {
332
+ if (!provider.getHistory) {
333
+ res.status(501).json({ error: 'Operation not supported by this agent provider' });
334
+ return;
335
+ }
336
+ const messages = await provider.getHistory(agent);
337
+ res.json({ messages });
338
+ });
339
+ } catch (err: unknown) {
340
+ console.error(err);
341
+ res.status(500).json({ error: (err as Error).message });
342
+ }
343
+ });
344
+
345
+ // POST /api/agents/:id/chat/abort — Abort in-flight chat
346
+ app.post('/api/agents/:id/chat/abort', async (req, res) => {
347
+ try {
348
+ await withAgent(req.params.id, res, async (agent, provider) => {
349
+ if (!provider.abortChat) {
350
+ res.status(501).json({ error: 'Operation not supported by this agent provider' });
351
+ return;
352
+ }
353
+ await provider.abortChat(agent);
354
+ res.json({ ok: true });
355
+ });
356
+ } catch (err: unknown) {
357
+ console.error(err);
358
+ res.status(500).json({ error: (err as Error).message });
359
+ }
360
+ });
361
+
362
+ // POST /api/agents/:id/chat/clear — Clear/reset chat session
363
+ app.post('/api/agents/:id/chat/clear', async (req, res) => {
364
+ try {
365
+ await withAgent(req.params.id, res, async (agent, provider) => {
366
+ if (!provider.clearSession) {
367
+ res.status(501).json({ error: 'Operation not supported by this agent provider' });
368
+ return;
369
+ }
370
+ await provider.clearSession(agent);
371
+ res.json({ ok: true });
372
+ });
373
+ } catch (err: unknown) {
374
+ console.error(err);
375
+ res.status(500).json({ error: (err as Error).message });
376
+ }
377
+ });
378
+
379
+ // POST /api/agents/:id/stream — SSE streaming endpoint
380
+ app.post('/api/agents/:id/stream', async (req, res) => {
381
+ try {
382
+ const { id } = req.params;
383
+ const { message, attachments } = req.body;
384
+ if (!message || typeof message !== 'string') {
385
+ res.status(400).json({ error: 'message is required' });
386
+ return;
387
+ }
388
+
389
+ const settings = await loadSettings(config.pagesFolder);
390
+ const agent = (settings.agents ?? []).find(a => a.id === id);
391
+ if (!agent) {
392
+ res.status(404).json({ error: `Agent "${id}" not found` });
393
+ return;
394
+ }
395
+
396
+ if (!agent.enabled) {
397
+ res.status(400).json({ error: `Agent "${agent.name}" is disabled` });
398
+ return;
399
+ }
400
+
401
+ // Set up SSE headers
402
+ res.setHeader('Content-Type', 'text/event-stream');
403
+ res.setHeader('Cache-Control', 'no-cache');
404
+ res.setHeader('Connection', 'keep-alive');
405
+ res.flushHeaders();
406
+
407
+ // Dispatch to the correct provider's streaming method
408
+ const provider = agent.provider === 'openclaw' ? openclawProvider : a2aProvider;
409
+
410
+ for await (const event of provider.sendStream(agent, message, attachments)) {
411
+ res.write(`data: ${JSON.stringify(event)}\n\n`);
412
+ if (event.kind === 'done' || event.kind === 'error') break;
413
+ }
414
+
415
+ res.write('data: [DONE]\n\n');
416
+ res.end();
417
+ } catch (err: unknown) {
418
+ console.error(err);
419
+ // If headers already sent, just end the stream
420
+ if (res.headersSent) {
421
+ res.write(`data: ${JSON.stringify({ kind: 'error', data: (err as Error).message })}\n\n`);
422
+ res.end();
423
+ } else {
424
+ res.status(500).json({ error: (err as Error).message });
425
+ }
426
+ }
427
+ });
428
+ }