synthos 0.8.0 → 0.10.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 (368) hide show
  1. package/README.md +1 -1
  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 +1803 -0
  25. package/default-pages/{neon_asteroids.json → neon_asteroids/page.json} +3 -3
  26. package/default-pages/{oregon_trail.html → oregon_trail/page.html} +16 -30
  27. package/default-pages/{oregon_trail.json → oregon_trail/page.json} +2 -2
  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_page.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} +15 -12
  33. package/default-pages/{solar_explorer.json → solar_explorer/page.json} +2 -2
  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_page.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.html → us_map/page.html} +193 -192
  39. package/default-pages/{us_map.json → us_map/page.json} +12 -12
  40. package/default-pages/{us_map_1850.html → us_map_1850/page.html} +326 -325
  41. package/default-pages/{us_map_1850.json → us_map_1850/page.json} +12 -12
  42. package/default-pages/{western_cities_1850.html → western_cities_1850/page.html} +527 -526
  43. package/default-pages/{western_cities_1850.json → western_cities_1850/page.json} +12 -12
  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.v2.css +110 -0
  57. package/default-themes/nebula-dawn.v3.css +199 -0
  58. package/default-themes/nebula-dusk.v2.css +104 -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/index.d.ts +1 -1
  65. package/dist/agents/index.d.ts.map +1 -1
  66. package/dist/agents/index.js +2 -1
  67. package/dist/agents/index.js.map +1 -1
  68. package/dist/agents/openclaw/gatewayManager.d.ts +4 -0
  69. package/dist/agents/openclaw/gatewayManager.d.ts.map +1 -1
  70. package/dist/agents/openclaw/gatewayManager.js +27 -11
  71. package/dist/agents/openclaw/gatewayManager.js.map +1 -1
  72. package/dist/agents/openclaw/openclawProvider.d.ts.map +1 -1
  73. package/dist/agents/openclaw/openclawProvider.js +2 -4
  74. package/dist/agents/openclaw/openclawProvider.js.map +1 -1
  75. package/dist/agents/openclaw/sshTunnelManager.d.ts +2 -0
  76. package/dist/agents/openclaw/sshTunnelManager.d.ts.map +1 -1
  77. package/dist/agents/openclaw/sshTunnelManager.js +31 -12
  78. package/dist/agents/openclaw/sshTunnelManager.js.map +1 -1
  79. package/dist/builders/anthropic.d.ts +31 -0
  80. package/dist/builders/anthropic.d.ts.map +1 -0
  81. package/dist/builders/anthropic.js +227 -0
  82. package/dist/builders/anthropic.js.map +1 -0
  83. package/dist/builders/fireworksai.d.ts +9 -0
  84. package/dist/builders/fireworksai.d.ts.map +1 -0
  85. package/dist/builders/fireworksai.js +57 -0
  86. package/dist/builders/fireworksai.js.map +1 -0
  87. package/dist/builders/index.d.ts +13 -0
  88. package/dist/builders/index.d.ts.map +1 -0
  89. package/dist/builders/index.js +31 -0
  90. package/dist/builders/index.js.map +1 -0
  91. package/dist/builders/openai.d.ts +8 -0
  92. package/dist/builders/openai.d.ts.map +1 -0
  93. package/dist/builders/openai.js +87 -0
  94. package/dist/builders/openai.js.map +1 -0
  95. package/dist/builders/types.d.ts +54 -0
  96. package/dist/builders/types.d.ts.map +1 -0
  97. package/dist/builders/types.js +211 -0
  98. package/dist/builders/types.js.map +1 -0
  99. package/dist/connectors/index.d.ts +1 -1
  100. package/dist/connectors/index.d.ts.map +1 -1
  101. package/dist/connectors/index.js +3 -2
  102. package/dist/connectors/index.js.map +1 -1
  103. package/dist/connectors/registry.d.ts +2 -1
  104. package/dist/connectors/registry.d.ts.map +1 -1
  105. package/dist/connectors/registry.js +31 -8
  106. package/dist/connectors/registry.js.map +1 -1
  107. package/dist/customizer/Customizer.d.ts +62 -0
  108. package/dist/customizer/Customizer.d.ts.map +1 -0
  109. package/dist/customizer/Customizer.js +134 -0
  110. package/dist/customizer/Customizer.js.map +1 -0
  111. package/dist/customizer/index.d.ts +4 -0
  112. package/dist/customizer/index.d.ts.map +1 -0
  113. package/dist/customizer/index.js +9 -0
  114. package/dist/customizer/index.js.map +1 -0
  115. package/dist/files.d.ts +16 -0
  116. package/dist/files.d.ts.map +1 -1
  117. package/dist/files.js +60 -1
  118. package/dist/files.js.map +1 -1
  119. package/dist/index.d.ts +2 -0
  120. package/dist/index.d.ts.map +1 -1
  121. package/dist/index.js +2 -0
  122. package/dist/index.js.map +1 -1
  123. package/dist/init.d.ts +12 -6
  124. package/dist/init.d.ts.map +1 -1
  125. package/dist/init.js +150 -133
  126. package/dist/init.js.map +1 -1
  127. package/dist/migrations.d.ts.map +1 -1
  128. package/dist/migrations.js +23 -10
  129. package/dist/migrations.js.map +1 -1
  130. package/dist/models/anthropic.d.ts +4 -2
  131. package/dist/models/anthropic.d.ts.map +1 -1
  132. package/dist/models/anthropic.js +33 -6
  133. package/dist/models/anthropic.js.map +1 -1
  134. package/dist/models/fireworksai.d.ts.map +1 -1
  135. package/dist/models/fireworksai.js +9 -1
  136. package/dist/models/fireworksai.js.map +1 -1
  137. package/dist/models/index.d.ts +1 -1
  138. package/dist/models/index.d.ts.map +1 -1
  139. package/dist/models/index.js +2 -1
  140. package/dist/models/index.js.map +1 -1
  141. package/dist/models/openai.d.ts +1 -1
  142. package/dist/models/openai.d.ts.map +1 -1
  143. package/dist/models/openai.js +24 -3
  144. package/dist/models/openai.js.map +1 -1
  145. package/dist/models/types.d.ts +20 -1
  146. package/dist/models/types.d.ts.map +1 -1
  147. package/dist/models/types.js +6 -1
  148. package/dist/models/types.js.map +1 -1
  149. package/dist/pages.d.ts +34 -10
  150. package/dist/pages.d.ts.map +1 -1
  151. package/dist/pages.js +229 -79
  152. package/dist/pages.js.map +1 -1
  153. package/dist/service/createCompletePrompt.d.ts +2 -1
  154. package/dist/service/createCompletePrompt.d.ts.map +1 -1
  155. package/dist/service/createCompletePrompt.js +2 -2
  156. package/dist/service/createCompletePrompt.js.map +1 -1
  157. package/dist/service/requiresSettings.d.ts +2 -1
  158. package/dist/service/requiresSettings.d.ts.map +1 -1
  159. package/dist/service/requiresSettings.js +3 -3
  160. package/dist/service/requiresSettings.js.map +1 -1
  161. package/dist/service/server.d.ts +2 -1
  162. package/dist/service/server.d.ts.map +1 -1
  163. package/dist/service/server.js +37 -8
  164. package/dist/service/server.js.map +1 -1
  165. package/dist/service/transformPage.d.ts +47 -20
  166. package/dist/service/transformPage.d.ts.map +1 -1
  167. package/dist/service/transformPage.js +514 -293
  168. package/dist/service/transformPage.js.map +1 -1
  169. package/dist/service/useAgentRoutes.d.ts +2 -1
  170. package/dist/service/useAgentRoutes.d.ts.map +1 -1
  171. package/dist/service/useAgentRoutes.js +17 -14
  172. package/dist/service/useAgentRoutes.js.map +1 -1
  173. package/dist/service/useApiRoutes.d.ts +2 -1
  174. package/dist/service/useApiRoutes.d.ts.map +1 -1
  175. package/dist/service/useApiRoutes.js +287 -172
  176. package/dist/service/useApiRoutes.js.map +1 -1
  177. package/dist/service/useConnectorRoutes.js +17 -17
  178. package/dist/service/useConnectorRoutes.js.map +1 -1
  179. package/dist/service/useDataRoutes.d.ts.map +1 -1
  180. package/dist/service/useDataRoutes.js +13 -10
  181. package/dist/service/useDataRoutes.js.map +1 -1
  182. package/dist/service/useFileRoutes.d.ts +4 -0
  183. package/dist/service/useFileRoutes.d.ts.map +1 -0
  184. package/dist/service/useFileRoutes.js +122 -0
  185. package/dist/service/useFileRoutes.js.map +1 -0
  186. package/dist/service/usePageRoutes.d.ts +2 -1
  187. package/dist/service/usePageRoutes.d.ts.map +1 -1
  188. package/dist/service/usePageRoutes.js +671 -74
  189. package/dist/service/usePageRoutes.js.map +1 -1
  190. package/dist/service/useSharedDataRoutes.d.ts +4 -0
  191. package/dist/service/useSharedDataRoutes.d.ts.map +1 -0
  192. package/dist/service/useSharedDataRoutes.js +107 -0
  193. package/dist/service/useSharedDataRoutes.js.map +1 -0
  194. package/dist/service/useSharedFileRoutes.d.ts +4 -0
  195. package/dist/service/useSharedFileRoutes.d.ts.map +1 -0
  196. package/dist/service/useSharedFileRoutes.js +121 -0
  197. package/dist/service/useSharedFileRoutes.js.map +1 -0
  198. package/dist/settings.d.ts +5 -3
  199. package/dist/settings.d.ts.map +1 -1
  200. package/dist/settings.js +12 -10
  201. package/dist/settings.js.map +1 -1
  202. package/dist/storage/FsStorageProvider.d.ts +25 -0
  203. package/dist/storage/FsStorageProvider.d.ts.map +1 -0
  204. package/dist/storage/FsStorageProvider.js +103 -0
  205. package/dist/storage/FsStorageProvider.js.map +1 -0
  206. package/dist/storage/StorageProvider.d.ts +31 -0
  207. package/dist/storage/StorageProvider.d.ts.map +1 -0
  208. package/dist/storage/StorageProvider.js +3 -0
  209. package/dist/storage/StorageProvider.js.map +1 -0
  210. package/dist/storage/index.d.ts +3 -0
  211. package/dist/storage/index.d.ts.map +1 -0
  212. package/dist/storage/index.js +6 -0
  213. package/dist/storage/index.js.map +1 -0
  214. package/dist/synthos-cli.d.ts.map +1 -1
  215. package/dist/synthos-cli.js +4 -3
  216. package/dist/synthos-cli.js.map +1 -1
  217. package/dist/themes.d.ts +1 -0
  218. package/dist/themes.d.ts.map +1 -1
  219. package/dist/themes.js +65 -28
  220. package/dist/themes.js.map +1 -1
  221. package/migration-rules/v1-to-v2.md +193 -0
  222. package/migration-rules/v2-to-v3.md +481 -0
  223. package/package.json +11 -10
  224. package/required-pages/builder/page.html +43 -0
  225. package/required-pages/builder/page.json +10 -0
  226. package/required-pages/{pages.html → pages/page.html} +238 -233
  227. package/required-pages/pages/page.json +10 -0
  228. package/required-pages/{settings.html → settings/page.html} +389 -275
  229. package/required-pages/settings/page.json +10 -0
  230. package/required-pages/synthos_apis/page.html +846 -0
  231. package/required-pages/synthos_apis/page.json +10 -0
  232. package/required-pages/{synthos_scripts.html → synthos_scripts/page.html} +13 -11
  233. package/required-pages/synthos_scripts/page.json +10 -0
  234. package/src/agents/index.ts +1 -1
  235. package/src/agents/openclaw/gatewayManager.ts +22 -11
  236. package/src/agents/openclaw/openclawProvider.ts +2 -4
  237. package/src/agents/openclaw/sshTunnelManager.ts +19 -11
  238. package/src/builders/anthropic.ts +283 -0
  239. package/src/builders/fireworksai.ts +59 -0
  240. package/src/builders/index.ts +33 -0
  241. package/src/builders/openai.ts +89 -0
  242. package/src/builders/types.ts +261 -0
  243. package/src/connectors/index.ts +1 -1
  244. package/src/connectors/registry.ts +28 -8
  245. package/src/customizer/Customizer.ts +163 -0
  246. package/src/customizer/index.ts +5 -0
  247. package/src/files.ts +57 -0
  248. package/src/index.ts +3 -1
  249. package/src/init.ts +195 -145
  250. package/src/migrations.ts +30 -10
  251. package/src/models/anthropic.ts +40 -10
  252. package/src/models/fireworksai.ts +9 -2
  253. package/src/models/index.ts +1 -1
  254. package/src/models/openai.ts +26 -6
  255. package/src/models/types.ts +31 -1
  256. package/src/pages.ts +230 -77
  257. package/src/service/createCompletePrompt.ts +3 -2
  258. package/src/service/requiresSettings.ts +4 -3
  259. package/src/service/server.ts +36 -9
  260. package/src/service/transformPage.ts +557 -326
  261. package/src/service/useAgentRoutes.ts +19 -14
  262. package/src/service/useApiRoutes.ts +208 -84
  263. package/src/service/useConnectorRoutes.ts +18 -18
  264. package/src/service/useDataRoutes.ts +13 -10
  265. package/src/service/useFileRoutes.ts +128 -0
  266. package/src/service/usePageRoutes.ts +730 -81
  267. package/src/service/useSharedDataRoutes.ts +109 -0
  268. package/src/service/useSharedFileRoutes.ts +127 -0
  269. package/src/settings.ts +14 -10
  270. package/src/storage/FsStorageProvider.ts +87 -0
  271. package/src/storage/StorageProvider.ts +34 -0
  272. package/src/storage/index.ts +2 -0
  273. package/src/synthos-cli.ts +4 -3
  274. package/src/themes.ts +64 -27
  275. package/static-files/favicon.svg +12 -0
  276. package/static-files/fluentlm-instructions.llmd +868 -0
  277. package/static-files/fluentlm-instructions.md +1595 -0
  278. package/static-files/fluentlm.css +4844 -0
  279. package/static-files/fluentlm.js +3602 -0
  280. package/static-files/fluentlm.min.css +1 -0
  281. package/static-files/fluentlm.min.js +1 -0
  282. package/{page-scripts/helpers-v2.js → static-files/helpers.v3.js} +82 -0
  283. package/static-files/page.v3.js +1290 -0
  284. package/static-files/recommended-frameworks.llmd +81 -0
  285. package/static-files/recommended-frameworks.md +137 -0
  286. package/static-files/retro-game.js +877 -0
  287. package/static-files/shell.css +797 -0
  288. package/static-files/theme-dark.css +169 -0
  289. package/static-files/theme-light.css +169 -0
  290. package/tests/builders.spec.ts +139 -0
  291. package/tests/pages.spec.ts +54 -84
  292. package/tests/transformPage.spec.ts +299 -360
  293. package/default-pages/application.html +0 -40
  294. package/default-pages/application.json +0 -1
  295. package/default-pages/json_tools.json +0 -1
  296. package/default-pages/my_notes.html +0 -33
  297. package/default-pages/neon_asteroids.html +0 -77
  298. package/default-pages/sidebar_page.json +0 -1
  299. package/default-pages/solar_tutorial.json +0 -1
  300. package/default-pages/two-panel_page.json +0 -1
  301. package/dist/service/useGatewayRoutes.d.ts +0 -4
  302. package/dist/service/useGatewayRoutes.d.ts.map +0 -1
  303. package/dist/service/useGatewayRoutes.js +0 -168
  304. package/dist/service/useGatewayRoutes.js.map +0 -1
  305. package/page-scripts/page-v2.js +0 -656
  306. package/required-pages/builder.html +0 -48
  307. package/required-pages/builder.json +0 -1
  308. package/required-pages/pages.json +0 -1
  309. package/required-pages/settings.json +0 -1
  310. package/required-pages/synthos_apis.html +0 -327
  311. package/required-pages/synthos_apis.json +0 -1
  312. package/required-pages/synthos_scripts.json +0 -1
  313. package/src/connectors/airtable/connector.json +0 -27
  314. package/src/connectors/alpha-vantage/connector.json +0 -26
  315. package/src/connectors/brave-search/connector.json +0 -26
  316. package/src/connectors/cloudinary/connector.json +0 -27
  317. package/src/connectors/deepl/connector.json +0 -28
  318. package/src/connectors/elevenlabs/connector.json +0 -30
  319. package/src/connectors/giphy/connector.json +0 -27
  320. package/src/connectors/github/connector.json +0 -29
  321. package/src/connectors/huggingface/connector.json +0 -27
  322. package/src/connectors/imgur/connector.json +0 -29
  323. package/src/connectors/instagram/connector.json +0 -43
  324. package/src/connectors/jira/connector.json +0 -28
  325. package/src/connectors/mapbox/connector.json +0 -26
  326. package/src/connectors/nasa/connector.json +0 -27
  327. package/src/connectors/newsapi/connector.json +0 -27
  328. package/src/connectors/notion/connector.json +0 -28
  329. package/src/connectors/open-exchange-rates/connector.json +0 -27
  330. package/src/connectors/openweathermap/connector.json +0 -26
  331. package/src/connectors/pexels/connector.json +0 -27
  332. package/src/connectors/resend/connector.json +0 -29
  333. package/src/connectors/rss2json/connector.json +0 -27
  334. package/src/connectors/sendgrid/connector.json +0 -27
  335. package/src/connectors/spoonacular/connector.json +0 -28
  336. package/src/connectors/stability-ai/connector.json +0 -27
  337. package/src/connectors/twilio/connector.json +0 -28
  338. package/src/connectors/unsplash/connector.json +0 -27
  339. package/src/connectors/wolfram-alpha/connector.json +0 -26
  340. package/src/connectors/youtube-data/connector.json +0 -30
  341. /package/{dist/connectors → service-connectors}/airtable/connector.json +0 -0
  342. /package/{dist/connectors → service-connectors}/alpha-vantage/connector.json +0 -0
  343. /package/{dist/connectors → service-connectors}/brave-search/connector.json +0 -0
  344. /package/{dist/connectors → service-connectors}/cloudinary/connector.json +0 -0
  345. /package/{dist/connectors → service-connectors}/deepl/connector.json +0 -0
  346. /package/{dist/connectors → service-connectors}/elevenlabs/connector.json +0 -0
  347. /package/{dist/connectors → service-connectors}/giphy/connector.json +0 -0
  348. /package/{dist/connectors → service-connectors}/github/connector.json +0 -0
  349. /package/{dist/connectors → service-connectors}/huggingface/connector.json +0 -0
  350. /package/{dist/connectors → service-connectors}/imgur/connector.json +0 -0
  351. /package/{dist/connectors → service-connectors}/instagram/connector.json +0 -0
  352. /package/{dist/connectors → service-connectors}/jira/connector.json +0 -0
  353. /package/{dist/connectors → service-connectors}/mapbox/connector.json +0 -0
  354. /package/{dist/connectors → service-connectors}/nasa/connector.json +0 -0
  355. /package/{dist/connectors → service-connectors}/newsapi/connector.json +0 -0
  356. /package/{dist/connectors → service-connectors}/notion/connector.json +0 -0
  357. /package/{dist/connectors → service-connectors}/open-exchange-rates/connector.json +0 -0
  358. /package/{dist/connectors → service-connectors}/openweathermap/connector.json +0 -0
  359. /package/{dist/connectors → service-connectors}/pexels/connector.json +0 -0
  360. /package/{dist/connectors → service-connectors}/resend/connector.json +0 -0
  361. /package/{dist/connectors → service-connectors}/rss2json/connector.json +0 -0
  362. /package/{dist/connectors → service-connectors}/sendgrid/connector.json +0 -0
  363. /package/{dist/connectors → service-connectors}/spoonacular/connector.json +0 -0
  364. /package/{dist/connectors → service-connectors}/stability-ai/connector.json +0 -0
  365. /package/{dist/connectors → service-connectors}/twilio/connector.json +0 -0
  366. /package/{dist/connectors → service-connectors}/unsplash/connector.json +0 -0
  367. /package/{dist/connectors → service-connectors}/wolfram-alpha/connector.json +0 -0
  368. /package/{dist/connectors → service-connectors}/youtube-data/connector.json +0 -0
@@ -2,7 +2,7 @@ import { Application } from 'express';
2
2
  import { SynthOSConfig } from '../init';
3
3
  import { loadSettings, saveSettings } from '../settings';
4
4
  import {
5
- CONNECTOR_REGISTRY,
5
+ getConnectorRegistry,
6
6
  ConnectorSummary,
7
7
  ConnectorDetail,
8
8
  ConnectorCallRequest,
@@ -15,13 +15,13 @@ export function useConnectorRoutes(config: SynthOSConfig, app: Application): voi
15
15
  // Also handles POST /api/connectors — Proxy call (see below)
16
16
  app.get('/api/connectors', async (req, res) => {
17
17
  try {
18
- const settings = await loadSettings(config.pagesFolder);
18
+ const settings = await loadSettings(config);
19
19
  const connectors = settings.connectors ?? {};
20
20
 
21
21
  const categoryFilter = req.query.category as string | undefined;
22
22
  const idFilter = req.query.id as string | undefined;
23
23
 
24
- const list: ConnectorSummary[] = CONNECTOR_REGISTRY
24
+ const list: ConnectorSummary[] = getConnectorRegistry(config.serviceConnectorsFolders)
25
25
  .filter(def => {
26
26
  if (categoryFilter && def.category !== categoryFilter) return false;
27
27
  if (idFilter && def.id !== idFilter) return false;
@@ -54,13 +54,13 @@ export function useConnectorRoutes(config: SynthOSConfig, app: Application): voi
54
54
  app.get('/api/connectors/:id', async (req, res) => {
55
55
  try {
56
56
  const { id } = req.params;
57
- const def = CONNECTOR_REGISTRY.find(d => d.id === id);
57
+ const def = getConnectorRegistry(config.serviceConnectorsFolders).find(d => d.id === id);
58
58
  if (!def) {
59
59
  res.status(404).json({ error: `Connector "${id}" not found` });
60
60
  return;
61
61
  }
62
62
 
63
- const settings = await loadSettings(config.pagesFolder);
63
+ const settings = await loadSettings(config);
64
64
  const cfg = (settings.connectors ?? {})[id];
65
65
  const isOAuth = def.authStrategy === 'oauth2';
66
66
  const oauthCfg = cfg as ConnectorOAuthConfig | undefined;
@@ -88,13 +88,13 @@ export function useConnectorRoutes(config: SynthOSConfig, app: Application): voi
88
88
  app.post('/api/connectors/:id', async (req, res) => {
89
89
  try {
90
90
  const { id } = req.params;
91
- const def = CONNECTOR_REGISTRY.find(d => d.id === id);
91
+ const def = getConnectorRegistry(config.serviceConnectorsFolders).find(d => d.id === id);
92
92
  if (!def) {
93
93
  res.status(404).json({ error: `Connector "${id}" not found` });
94
94
  return;
95
95
  }
96
96
 
97
- const settings = await loadSettings(config.pagesFolder);
97
+ const settings = await loadSettings(config);
98
98
  const existing = settings.connectors ?? {};
99
99
 
100
100
  if (def.authStrategy === 'oauth2') {
@@ -121,7 +121,7 @@ export function useConnectorRoutes(config: SynthOSConfig, app: Application): voi
121
121
  }
122
122
 
123
123
  const updated = { ...existing, [id]: entry };
124
- await saveSettings(config.pagesFolder, { connectors: updated });
124
+ await saveSettings(config, { connectors: updated });
125
125
  } else {
126
126
  const { apiKey, enabled } = req.body;
127
127
  const resolvedKey = (typeof apiKey === 'string' && apiKey.length > 0)
@@ -132,7 +132,7 @@ export function useConnectorRoutes(config: SynthOSConfig, app: Application): voi
132
132
  ...existing,
133
133
  [id]: { apiKey: resolvedKey, enabled: !!enabled }
134
134
  };
135
- await saveSettings(config.pagesFolder, { connectors: updated });
135
+ await saveSettings(config, { connectors: updated });
136
136
  }
137
137
 
138
138
  res.json({ saved: true });
@@ -146,13 +146,13 @@ export function useConnectorRoutes(config: SynthOSConfig, app: Application): voi
146
146
  app.delete('/api/connectors/:id', async (req, res) => {
147
147
  try {
148
148
  const { id } = req.params;
149
- const settings = await loadSettings(config.pagesFolder);
149
+ const settings = await loadSettings(config);
150
150
  const existing = settings.connectors ?? {};
151
151
 
152
152
  const updated = { ...existing };
153
153
  delete updated[id];
154
154
 
155
- await saveSettings(config.pagesFolder, { connectors: updated });
155
+ await saveSettings(config, { connectors: updated });
156
156
  res.json({ deleted: true });
157
157
  } catch (err: unknown) {
158
158
  console.error(err);
@@ -164,13 +164,13 @@ export function useConnectorRoutes(config: SynthOSConfig, app: Application): voi
164
164
  app.get('/api/connectors/:id/authorize', async (req, res) => {
165
165
  try {
166
166
  const { id } = req.params;
167
- const def = CONNECTOR_REGISTRY.find(d => d.id === id);
167
+ const def = getConnectorRegistry(config.serviceConnectorsFolders).find(d => d.id === id);
168
168
  if (!def || def.authStrategy !== 'oauth2') {
169
169
  res.status(400).json({ error: `Connector "${id}" is not an OAuth2 connector` });
170
170
  return;
171
171
  }
172
172
 
173
- const settings = await loadSettings(config.pagesFolder);
173
+ const settings = await loadSettings(config);
174
174
  const cfg = (settings.connectors ?? {})[id] as ConnectorOAuthConfig | undefined;
175
175
  if (!cfg?.clientId || !cfg?.clientSecret) {
176
176
  res.status(400).json({ error: 'Client ID and Client Secret must be saved before authorizing' });
@@ -214,13 +214,13 @@ export function useConnectorRoutes(config: SynthOSConfig, app: Application): voi
214
214
  const state = JSON.parse(stateRaw) as { connector: string };
215
215
  const connectorId = state.connector;
216
216
 
217
- const def = CONNECTOR_REGISTRY.find(d => d.id === connectorId);
217
+ const def = getConnectorRegistry(config.serviceConnectorsFolders).find(d => d.id === connectorId);
218
218
  if (!def || def.authStrategy !== 'oauth2') {
219
219
  res.status(400).json({ error: `Unknown OAuth2 connector: ${connectorId}` });
220
220
  return;
221
221
  }
222
222
 
223
- const settings = await loadSettings(config.pagesFolder);
223
+ const settings = await loadSettings(config);
224
224
  const cfg = (settings.connectors ?? {})[connectorId] as ConnectorOAuthConfig | undefined;
225
225
  if (!cfg?.clientId || !cfg?.clientSecret) {
226
226
  res.status(400).json({ error: 'Client credentials not found' });
@@ -307,7 +307,7 @@ export function useConnectorRoutes(config: SynthOSConfig, app: Application): voi
307
307
  enabled: true
308
308
  }
309
309
  };
310
- await saveSettings(config.pagesFolder, { connectors: updated });
310
+ await saveSettings(config, { connectors: updated });
311
311
 
312
312
  res.redirect(`/settings?tab=connectors&connected=${encodeURIComponent(connectorId)}`);
313
313
  } catch (err: unknown) {
@@ -326,13 +326,13 @@ export function useConnectorRoutes(config: SynthOSConfig, app: Application): voi
326
326
  return;
327
327
  }
328
328
 
329
- const def = CONNECTOR_REGISTRY.find(d => d.id === request.connector);
329
+ const def = getConnectorRegistry(config.serviceConnectorsFolders).find(d => d.id === request.connector);
330
330
  if (!def) {
331
331
  res.status(404).json({ error: `Connector "${request.connector}" not found` });
332
332
  return;
333
333
  }
334
334
 
335
- const settings = await loadSettings(config.pagesFolder);
335
+ const settings = await loadSettings(config);
336
336
  const cfg = (settings.connectors ?? {})[request.connector];
337
337
 
338
338
  if (def.authStrategy === 'oauth2') {
@@ -1,6 +1,5 @@
1
1
  import { Application, Response } from 'express';
2
2
  import { SynthOSConfig } from "../init";
3
- import { checkIfExists, deleteFile, ensureFolderExists, listFiles, loadFile, saveFile } from "../files";
4
3
  import path from "path";
5
4
  import { v4 } from "uuid";
6
5
  import { clearCachedScripts } from '../scripts';
@@ -17,19 +16,20 @@ export function useDataRoutes(config: SynthOSConfig, app: Application): void {
17
16
  // ---------------------------------------------------------------------------
18
17
 
19
18
  async function handleList(config: SynthOSConfig, page: string, table: string, query: Record<string, any>, res: Response): Promise<void> {
19
+ const sp = config.storageProvider;
20
20
  const folder = tableFolder(config, page, table);
21
- if (!(await checkIfExists(folder))) {
21
+ if (!(await sp.checkIfExists(folder))) {
22
22
  res.status(404).json({ error: 'table_not_found', page, table });
23
23
  return;
24
24
  }
25
25
 
26
- const ids = (await listFiles(folder)).filter(f => f.endsWith('.json')).map(f => f.replace('.json', ''));
26
+ const ids = (await sp.listFiles(folder)).filter(f => f.endsWith('.json')).map(f => f.replace('.json', ''));
27
27
 
28
28
  const rows: Record<string, any>[] = [];
29
29
  for (const id of ids) {
30
30
  const file = recordFile(folder, id);
31
31
  try {
32
- const row = JSON.parse(await loadFile(file));
32
+ const row = JSON.parse(await sp.loadFile(file));
33
33
  row.id = id;
34
34
  rows.push(row);
35
35
  } catch (err: unknown) {
@@ -49,15 +49,16 @@ async function handleList(config: SynthOSConfig, page: string, table: string, qu
49
49
  }
50
50
 
51
51
  async function handleGet(config: SynthOSConfig, page: string, table: string, id: string, res: Response): Promise<void> {
52
+ const sp = config.storageProvider;
52
53
  const folder = tableFolder(config, page, table);
53
- if (!(await checkIfExists(folder))) {
54
+ if (!(await sp.checkIfExists(folder))) {
54
55
  res.status(404).json({ error: 'table_not_found', page, table });
55
56
  return;
56
57
  }
57
58
 
58
59
  const file = recordFile(folder, id);
59
60
  try {
60
- const row = JSON.parse(await loadFile(file));
61
+ const row = JSON.parse(await sp.loadFile(file));
61
62
  row.id = id;
62
63
  res.json(row);
63
64
  } catch (err: unknown) {
@@ -66,13 +67,14 @@ async function handleGet(config: SynthOSConfig, page: string, table: string, id:
66
67
  }
67
68
 
68
69
  async function handleUpsert(config: SynthOSConfig, page: string, table: string, body: any, res: Response): Promise<void> {
70
+ const sp = config.storageProvider;
69
71
  const id = body.id ?? v4();
70
72
  const folder = tableFolder(config, page, table);
71
73
  const file = recordFile(folder, id);
72
74
  try {
73
75
  const row = { ...body, id };
74
- await ensureFolderExists(folder);
75
- await saveFile(file, JSON.stringify(row, null, 4));
76
+ await sp.ensureFolderExists(folder);
77
+ await sp.saveFile(file, JSON.stringify(row, null, 4));
76
78
  if (table === 'scripts') {
77
79
  clearCachedScripts();
78
80
  }
@@ -84,11 +86,12 @@ async function handleUpsert(config: SynthOSConfig, page: string, table: string,
84
86
  }
85
87
 
86
88
  async function handleDelete(config: SynthOSConfig, page: string, table: string, id: string, res: Response): Promise<void> {
89
+ const sp = config.storageProvider;
87
90
  const folder = tableFolder(config, page, table);
88
91
  const file = recordFile(folder, id);
89
92
  try {
90
- if (await checkIfExists(file)) {
91
- await deleteFile(file);
93
+ if (await sp.checkIfExists(file)) {
94
+ await sp.deleteFile(file);
92
95
  if (table === 'scripts') {
93
96
  clearCachedScripts();
94
97
  }
@@ -0,0 +1,128 @@
1
+ import { Application } from 'express';
2
+ import express from 'express';
3
+ import path from 'path';
4
+ import { SynthOSConfig } from '../init';
5
+
6
+ export function useFileRoutes(config: SynthOSConfig, app: Application): void {
7
+ const sp = config.storageProvider;
8
+
9
+ // List files in a page's files/ folder
10
+ app.get('/api/files/:page', async (req, res) => {
11
+ try {
12
+ const folder = filesFolder(config, req.params.page);
13
+ if (!(await sp.checkIfExists(folder))) {
14
+ res.json({ files: [] });
15
+ return;
16
+ }
17
+
18
+ const entries = await sp.listFiles(folder);
19
+ const files: { name: string; size: number }[] = [];
20
+ for (const entry of entries) {
21
+ const stat = await sp.stat(path.join(folder, entry));
22
+ if (stat.isFile) {
23
+ files.push({ name: entry, size: stat.size });
24
+ }
25
+ }
26
+ res.json({ files });
27
+ } catch (err: unknown) {
28
+ console.error(err);
29
+ res.status(500).json({ error: (err as Error).message });
30
+ }
31
+ });
32
+
33
+ // Download/serve a specific file
34
+ app.get('/api/files/:page/:filename', async (req, res) => {
35
+ try {
36
+ const filePath = safeFilePath(config, req.params.page, req.params.filename);
37
+ if (!filePath) {
38
+ res.status(400).json({ error: 'Invalid filename' });
39
+ return;
40
+ }
41
+
42
+ if (!(await sp.checkIfExists(filePath))) {
43
+ res.status(404).json({ error: 'File not found' });
44
+ return;
45
+ }
46
+
47
+ res.type(path.extname(req.params.filename));
48
+ sp.createReadStream(filePath).pipe(res);
49
+ } catch (err: unknown) {
50
+ console.error(err);
51
+ res.status(500).json({ error: (err as Error).message });
52
+ }
53
+ });
54
+
55
+ // Upload a file (raw body + x-filename header)
56
+ app.post('/api/files/:page', express.raw({ type: '*/*', limit: '50mb' }), async (req, res) => {
57
+ try {
58
+ const filename = req.headers['x-filename'] as string | undefined;
59
+ if (!filename || filename.trim().length === 0) {
60
+ res.status(400).json({ error: 'x-filename header is required' });
61
+ return;
62
+ }
63
+
64
+ const filePath = safeFilePath(config, req.params.page, filename);
65
+ if (!filePath) {
66
+ res.status(400).json({ error: 'Invalid filename' });
67
+ return;
68
+ }
69
+
70
+ const folder = filesFolder(config, req.params.page);
71
+ await sp.ensureFolderExists(folder);
72
+ await sp.saveBuffer(filePath, req.body as Buffer);
73
+
74
+ const stat = await sp.stat(filePath);
75
+ res.status(201).json({ name: filename, size: stat.size });
76
+ } catch (err: unknown) {
77
+ console.error(err);
78
+ res.status(500).json({ error: (err as Error).message });
79
+ }
80
+ });
81
+
82
+ // Delete a file
83
+ app.delete('/api/files/:page/:filename', async (req, res) => {
84
+ try {
85
+ const filePath = safeFilePath(config, req.params.page, req.params.filename);
86
+ if (!filePath) {
87
+ res.status(400).json({ error: 'Invalid filename' });
88
+ return;
89
+ }
90
+
91
+ if (!(await sp.checkIfExists(filePath))) {
92
+ res.status(404).json({ error: 'File not found' });
93
+ return;
94
+ }
95
+
96
+ await sp.deleteFile(filePath);
97
+ res.json({ deleted: true });
98
+ } catch (err: unknown) {
99
+ console.error(err);
100
+ res.status(500).json({ error: (err as Error).message });
101
+ }
102
+ });
103
+ }
104
+
105
+ // ---------------------------------------------------------------------------
106
+ // Helpers
107
+ // ---------------------------------------------------------------------------
108
+
109
+ function filesFolder(config: SynthOSConfig, page: string): string {
110
+ return path.join(config.pagesFolder, 'pages', page, 'files');
111
+ }
112
+
113
+ /**
114
+ * Resolve a filename inside the page's files/ folder with path-traversal protection.
115
+ * Returns the absolute path if safe, or null if the filename is invalid.
116
+ */
117
+ function safeFilePath(config: SynthOSConfig, page: string, filename: string): string | null {
118
+ // Reject obviously bad filenames
119
+ if (!filename || filename.includes('..') || filename.includes('/') || filename.includes('\\')) {
120
+ return null;
121
+ }
122
+ const folder = filesFolder(config, page);
123
+ const resolved = path.resolve(folder, filename);
124
+ if (!resolved.startsWith(path.resolve(folder))) {
125
+ return null;
126
+ }
127
+ return resolved;
128
+ }