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,106 @@
1
+ import { Application, Response } from 'express';
2
+ import { SynthOSConfig } from "../init";
3
+ import { checkIfExists, deleteFile, ensureFolderExists, listFiles, loadFile, saveFile } from "../files";
4
+ import path from "path";
5
+ import { v4 } from "uuid";
6
+
7
+ export function useSharedDataRoutes(config: SynthOSConfig, app: Application): void {
8
+ app.get('/api/shared/data/:table', (req, res) => handleList(config, req.params.table, req.query, res));
9
+ app.get('/api/shared/data/:table/:id', (req, res) => handleGet(config, req.params.table, req.params.id, res));
10
+ app.post('/api/shared/data/:table', (req, res) => handleUpsert(config, req.params.table, req.body, res));
11
+ app.delete('/api/shared/data/:table/:id', (req, res) => handleDelete(config, req.params.table, req.params.id, res));
12
+ }
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // Route handlers
16
+ // ---------------------------------------------------------------------------
17
+
18
+ async function handleList(config: SynthOSConfig, table: string, query: Record<string, any>, res: Response): Promise<void> {
19
+ const folder = sharedTableFolder(config, table);
20
+ if (!(await checkIfExists(folder))) {
21
+ res.status(404).json({ error: 'table_not_found', table });
22
+ return;
23
+ }
24
+
25
+ const ids = (await listFiles(folder)).filter(f => f.endsWith('.json')).map(f => f.replace('.json', ''));
26
+
27
+ const rows: Record<string, any>[] = [];
28
+ for (const id of ids) {
29
+ const file = recordFile(folder, id);
30
+ try {
31
+ const row = JSON.parse(await loadFile(file));
32
+ row.id = id;
33
+ rows.push(row);
34
+ } catch (err: unknown) {
35
+ console.error(err);
36
+ }
37
+ }
38
+
39
+ // Paginate when limit is provided
40
+ const limitParam = typeof query.limit === 'string' ? parseInt(query.limit, 10) : NaN;
41
+ if (!isNaN(limitParam) && limitParam > 0) {
42
+ const offset = Math.max(0, typeof query.offset === 'string' ? parseInt(query.offset, 10) || 0 : 0);
43
+ const items = rows.slice(offset, offset + limitParam);
44
+ res.json({ items, total: rows.length, offset, limit: limitParam, hasMore: offset + limitParam < rows.length });
45
+ } else {
46
+ res.json(rows);
47
+ }
48
+ }
49
+
50
+ async function handleGet(config: SynthOSConfig, table: string, id: string, res: Response): Promise<void> {
51
+ const folder = sharedTableFolder(config, table);
52
+ if (!(await checkIfExists(folder))) {
53
+ res.status(404).json({ error: 'table_not_found', table });
54
+ return;
55
+ }
56
+
57
+ const file = recordFile(folder, id);
58
+ try {
59
+ const row = JSON.parse(await loadFile(file));
60
+ row.id = id;
61
+ res.json(row);
62
+ } catch (err: unknown) {
63
+ res.json({});
64
+ }
65
+ }
66
+
67
+ async function handleUpsert(config: SynthOSConfig, table: string, body: any, res: Response): Promise<void> {
68
+ const id = body.id ?? v4();
69
+ const folder = sharedTableFolder(config, table);
70
+ const file = recordFile(folder, id);
71
+ try {
72
+ const row = { ...body, id };
73
+ await ensureFolderExists(folder);
74
+ await saveFile(file, JSON.stringify(row, null, 4));
75
+ res.json(row);
76
+ } catch (err: unknown) {
77
+ console.error(err);
78
+ res.status(500).send((err as Error).message);
79
+ }
80
+ }
81
+
82
+ async function handleDelete(config: SynthOSConfig, table: string, id: string, res: Response): Promise<void> {
83
+ const folder = sharedTableFolder(config, table);
84
+ const file = recordFile(folder, id);
85
+ try {
86
+ if (await checkIfExists(file)) {
87
+ await deleteFile(file);
88
+ }
89
+ res.json({ success: true });
90
+ } catch (err: unknown) {
91
+ console.error(err);
92
+ res.status(500).send((err as Error).message);
93
+ }
94
+ }
95
+
96
+ // ---------------------------------------------------------------------------
97
+ // Helpers
98
+ // ---------------------------------------------------------------------------
99
+
100
+ function sharedTableFolder(config: SynthOSConfig, table: string): string {
101
+ return path.join(config.pagesFolder, 'shared', table);
102
+ }
103
+
104
+ function recordFile(folder: string, id: string): string {
105
+ return path.join(folder, `${id}.json`);
106
+ }
@@ -0,0 +1,126 @@
1
+ import { Application } from 'express';
2
+ import express from 'express';
3
+ import fs from 'fs/promises';
4
+ import path from 'path';
5
+ import { SynthOSConfig } from '../init';
6
+ import { checkIfExists, ensureFolderExists } from '../files';
7
+
8
+ export function useSharedFileRoutes(config: SynthOSConfig, app: Application): void {
9
+ // List files in the shared files folder
10
+ app.get('/api/shared/files', async (req, res) => {
11
+ try {
12
+ const folder = sharedFilesFolder(config);
13
+ if (!(await checkIfExists(folder))) {
14
+ res.json({ files: [] });
15
+ return;
16
+ }
17
+
18
+ const entries = await fs.readdir(folder);
19
+ const files: { name: string; size: number }[] = [];
20
+ for (const entry of entries) {
21
+ const stat = await fs.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 shared file
34
+ app.get('/api/shared/files/:filename', async (req, res) => {
35
+ try {
36
+ const filePath = safeFilePath(config, req.params.filename);
37
+ if (!filePath) {
38
+ res.status(400).json({ error: 'Invalid filename' });
39
+ return;
40
+ }
41
+
42
+ if (!(await checkIfExists(filePath))) {
43
+ res.status(404).json({ error: 'File not found' });
44
+ return;
45
+ }
46
+
47
+ res.sendFile(filePath);
48
+ } catch (err: unknown) {
49
+ console.error(err);
50
+ res.status(500).json({ error: (err as Error).message });
51
+ }
52
+ });
53
+
54
+ // Upload a shared file (raw body + x-filename header)
55
+ app.post('/api/shared/files', express.raw({ type: '*/*', limit: '50mb' }), async (req, res) => {
56
+ try {
57
+ const filename = req.headers['x-filename'] as string | undefined;
58
+ if (!filename || filename.trim().length === 0) {
59
+ res.status(400).json({ error: 'x-filename header is required' });
60
+ return;
61
+ }
62
+
63
+ const filePath = safeFilePath(config, filename);
64
+ if (!filePath) {
65
+ res.status(400).json({ error: 'Invalid filename' });
66
+ return;
67
+ }
68
+
69
+ const folder = sharedFilesFolder(config);
70
+ await ensureFolderExists(folder);
71
+ await fs.writeFile(filePath, req.body as Buffer);
72
+
73
+ const stat = await fs.stat(filePath);
74
+ res.status(201).json({ name: filename, size: stat.size });
75
+ } catch (err: unknown) {
76
+ console.error(err);
77
+ res.status(500).json({ error: (err as Error).message });
78
+ }
79
+ });
80
+
81
+ // Delete a shared file
82
+ app.delete('/api/shared/files/:filename', async (req, res) => {
83
+ try {
84
+ const filePath = safeFilePath(config, req.params.filename);
85
+ if (!filePath) {
86
+ res.status(400).json({ error: 'Invalid filename' });
87
+ return;
88
+ }
89
+
90
+ if (!(await checkIfExists(filePath))) {
91
+ res.status(404).json({ error: 'File not found' });
92
+ return;
93
+ }
94
+
95
+ await fs.unlink(filePath);
96
+ res.json({ deleted: true });
97
+ } catch (err: unknown) {
98
+ console.error(err);
99
+ res.status(500).json({ error: (err as Error).message });
100
+ }
101
+ });
102
+ }
103
+
104
+ // ---------------------------------------------------------------------------
105
+ // Helpers
106
+ // ---------------------------------------------------------------------------
107
+
108
+ function sharedFilesFolder(config: SynthOSConfig): string {
109
+ return path.join(config.pagesFolder, 'shared', 'files');
110
+ }
111
+
112
+ /**
113
+ * Resolve a filename inside the shared files folder with path-traversal protection.
114
+ * Returns the absolute path if safe, or null if the filename is invalid.
115
+ */
116
+ function safeFilePath(config: SynthOSConfig, filename: string): string | null {
117
+ if (!filename || filename.includes('..') || filename.includes('/') || filename.includes('\\')) {
118
+ return null;
119
+ }
120
+ const folder = sharedFilesFolder(config);
121
+ const resolved = path.resolve(folder, filename);
122
+ if (!resolved.startsWith(path.resolve(folder))) {
123
+ return null;
124
+ }
125
+ return resolved;
126
+ }
package/src/settings.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import {checkIfExists, loadFile, saveFile} from './files';
2
2
  import path from 'path';
3
3
  import { ModelEntry, ProviderName, detectProvider } from './models';
4
+ import { AgentConfig } from './agents';
4
5
 
5
6
  let _settings: Partial<SettingsV2>|undefined;
6
7
 
@@ -20,7 +21,6 @@ export type { ModelEntry } from './models';
20
21
  export interface SettingsV1 {
21
22
  serviceApiKey: string;
22
23
  model: string;
23
- maxTokens: number;
24
24
  imageQuality: 'standard' | 'hd';
25
25
  instructions?: string;
26
26
  logCompletions?: boolean;
@@ -38,6 +38,8 @@ export interface SettingsV2 {
38
38
  features: string[];
39
39
  services?: ServicesConfig;
40
40
  connectors?: ServicesConfig;
41
+ agents?: AgentConfig[];
42
+ toolbarPosition?: 'left' | 'right';
41
43
  }
42
44
 
43
45
  export const DefaultSettings: SettingsV2 = {
@@ -47,7 +49,7 @@ export const DefaultSettings: SettingsV2 = {
47
49
  {
48
50
  use: 'builder',
49
51
  provider: 'Anthropic',
50
- configuration: { apiKey: '', model: '', maxTokens: 32000 },
52
+ configuration: { apiKey: '', model: '' },
51
53
  imageQuality: 'standard',
52
54
  instructions: '',
53
55
  logCompletions: false,
@@ -55,7 +57,7 @@ export const DefaultSettings: SettingsV2 = {
55
57
  {
56
58
  use: 'chat',
57
59
  provider: 'Anthropic',
58
- configuration: { apiKey: '', model: '', maxTokens: 32000 },
60
+ configuration: { apiKey: '', model: '' },
59
61
  imageQuality: 'standard',
60
62
  instructions: '',
61
63
  logCompletions: false,
@@ -63,7 +65,9 @@ export const DefaultSettings: SettingsV2 = {
63
65
  ],
64
66
  features: [],
65
67
  services: {},
66
- connectors: {}
68
+ connectors: {},
69
+ agents: [],
70
+ toolbarPosition: 'left',
67
71
  };
68
72
 
69
73
  /**
@@ -100,7 +104,6 @@ function migrateV1toV2(raw: Record<string, unknown>): SettingsV2 {
100
104
  configuration: {
101
105
  apiKey: v1.serviceApiKey ?? '',
102
106
  model,
103
- maxTokens: v1.maxTokens ?? 32000,
104
107
  },
105
108
  imageQuality: v1.imageQuality ?? 'standard',
106
109
  instructions: v1.instructions ?? '',
@@ -112,7 +115,6 @@ function migrateV1toV2(raw: Record<string, unknown>): SettingsV2 {
112
115
  configuration: {
113
116
  apiKey: v1.serviceApiKey ?? '',
114
117
  model: chatModel,
115
- maxTokens: v1.maxTokens ?? 32000,
116
118
  },
117
119
  imageQuality: v1.imageQuality ?? 'standard',
118
120
  instructions: v1.instructions ?? '',
@@ -133,10 +135,6 @@ export async function hasConfiguredSettings(folder: string): Promise<boolean> {
133
135
  if (typeof builder.configuration.model !== 'string' || builder.configuration.model.length == 0) {
134
136
  return false;
135
137
  }
136
- if (typeof builder.configuration.maxTokens !== 'number' || builder.configuration.maxTokens <= 0) {
137
- return false;
138
- }
139
-
140
138
  return true;
141
139
  }
142
140
 
@@ -2,6 +2,7 @@ import yargs from "yargs";
2
2
  import { hideBin } from "yargs/helpers";
3
3
  import { server } from "./service";
4
4
  import { createConfig, init } from "./init";
5
+ import { customizer } from "./customizer";
5
6
 
6
7
  const dynamicImport = new Function('specifier', `return import(specifier)`);
7
8
 
@@ -16,7 +17,7 @@ export async function run() {
16
17
  default: 4242
17
18
  })
18
19
  .option('pages', {
19
- describe: `Include default pages when initializing a new .synthos folder.`,
20
+ describe: `Include default pages when initializing a new local folder.`,
20
21
  type: 'boolean',
21
22
  default: true
22
23
  })
@@ -32,9 +33,9 @@ export async function run() {
32
33
  })
33
34
  .demandOption([]);
34
35
  }, async (args) => {
35
- const config = createConfig('.synthos', { debug: args.debug, debugPageUpdates: args.debugPageUpdates });
36
+ const config = await createConfig(customizer.localFolder, { debug: args.debug, debugPageUpdates: args.debugPageUpdates }, customizer);
36
37
  await init(config, args.pages);
37
- await server(config).listen(args.port, async () => {
38
+ await server(config, customizer).listen(args.port, async () => {
38
39
  console.log(`SynthOS server is running on http://localhost:${args.port}`);
39
40
 
40
41
  // Open using default browser
package/src/themes.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import path from 'path';
2
- import { checkIfExists, listFiles, loadFile } from './files';
2
+ import { checkIfExists, findFileInFolders, listFiles, listFilesFromFolders, loadFile } from './files';
3
3
  import { SynthOSConfig } from './init';
4
4
 
5
+ export const THEME_VERSION = 2;
6
+
5
7
  export interface ThemeInfo {
6
8
  mode: 'light' | 'dark';
7
9
  colors: Record<string, string>;
@@ -11,6 +13,49 @@ function userThemesFolder(config: SynthOSConfig): string {
11
13
  return path.join(config.pagesFolder, 'themes');
12
14
  }
13
15
 
16
+ /**
17
+ * Extract the base theme name and version from a CSS filename.
18
+ * e.g. "nebula-dusk.v2.css" → { name: "nebula-dusk", version: 2 }
19
+ * "nebula-dusk.css" → { name: "nebula-dusk", version: 1 }
20
+ */
21
+ export function parseThemeFilename(filename: string): { name: string; version: number } | undefined {
22
+ if (!filename.endsWith('.css')) return undefined;
23
+ const versionedMatch = filename.match(/^(.+)\.v(\d+)\.css$/);
24
+ if (versionedMatch) {
25
+ return { name: versionedMatch[1], version: parseInt(versionedMatch[2], 10) };
26
+ }
27
+ return { name: filename.replace(/\.css$/, ''), version: 1 };
28
+ }
29
+
30
+ /**
31
+ * Find the CSS file for a theme by name in a folder.
32
+ * Prefers the highest-versioned file (e.g. name.v2.css over name.css).
33
+ */
34
+ async function findThemeCssFile(folder: string, name: string): Promise<{ path: string; version: number } | undefined> {
35
+ if (!await checkIfExists(folder)) return undefined;
36
+ const files = await listFiles(folder);
37
+ let best: { path: string; version: number } | undefined;
38
+ for (const f of files) {
39
+ const parsed = parseThemeFilename(f);
40
+ if (parsed && parsed.name === name) {
41
+ if (!best || parsed.version > best.version) {
42
+ best = { path: path.join(folder, f), version: parsed.version };
43
+ }
44
+ }
45
+ }
46
+ return best;
47
+ }
48
+
49
+ export async function loadThemeVersion(name: string, config: SynthOSConfig): Promise<number> {
50
+ const local = await findThemeCssFile(userThemesFolder(config), name);
51
+ if (local) return local.version;
52
+ for (const folder of config.defaultThemesFolders) {
53
+ const def = await findThemeCssFile(folder, name);
54
+ if (def) return def.version;
55
+ }
56
+ return 1;
57
+ }
58
+
14
59
  export async function loadThemeInfo(name: string, config: SynthOSConfig): Promise<ThemeInfo | undefined> {
15
60
  // Check user's local themes first, then fall back to package defaults
16
61
  const localPath = path.join(userThemesFolder(config), `${name}.json`);
@@ -19,8 +64,8 @@ export async function loadThemeInfo(name: string, config: SynthOSConfig): Promis
19
64
  return raw ? JSON.parse(raw) : undefined;
20
65
  }
21
66
 
22
- const defaultPath = path.join(config.defaultThemesFolder, `${name}.json`);
23
- if (await checkIfExists(defaultPath)) {
67
+ const defaultPath = await findFileInFolders(config.defaultThemesFolders, `${name}.json`);
68
+ if (defaultPath) {
24
69
  const raw = await loadFile(defaultPath);
25
70
  return raw ? JSON.parse(raw) : undefined;
26
71
  }
@@ -30,14 +75,17 @@ export async function loadThemeInfo(name: string, config: SynthOSConfig): Promis
30
75
 
31
76
  export async function loadTheme(name: string, config: SynthOSConfig): Promise<string | undefined> {
32
77
  // Check user's local themes first, then fall back to package defaults
33
- const localPath = path.join(userThemesFolder(config), `${name}.css`);
34
- if (await checkIfExists(localPath)) {
35
- return await loadFile(localPath);
78
+ const local = await findThemeCssFile(userThemesFolder(config), name);
79
+ if (local) {
80
+ return await loadFile(local.path);
36
81
  }
37
82
 
38
- const defaultPath = path.join(config.defaultThemesFolder, `${name}.css`);
39
- if (await checkIfExists(defaultPath)) {
40
- return await loadFile(defaultPath);
83
+ // Search all default theme folders
84
+ for (const folder of config.defaultThemesFolders) {
85
+ const def = await findThemeCssFile(folder, name);
86
+ if (def) {
87
+ return await loadFile(def.path);
88
+ }
41
89
  }
42
90
 
43
91
  return undefined;
@@ -51,21 +99,56 @@ export async function listThemes(config: SynthOSConfig): Promise<string[]> {
51
99
  if (await checkIfExists(localFolder)) {
52
100
  const files = await listFiles(localFolder);
53
101
  for (const f of files) {
54
- if (f.endsWith('.css')) {
55
- names.add(f.replace(/\.css$/, ''));
56
- }
102
+ const parsed = parseThemeFilename(f);
103
+ if (parsed) names.add(parsed.name);
57
104
  }
58
105
  }
59
106
 
60
- // Collect from package defaults
61
- if (await checkIfExists(config.defaultThemesFolder)) {
62
- const files = await listFiles(config.defaultThemesFolder);
63
- for (const f of files) {
64
- if (f.endsWith('.css')) {
65
- names.add(f.replace(/\.css$/, ''));
66
- }
67
- }
107
+ // Collect from all default theme folders
108
+ const defaultFiles = await listFilesFromFolders(config.defaultThemesFolders);
109
+ for (const f of defaultFiles) {
110
+ const parsed = parseThemeFilename(f);
111
+ if (parsed) names.add(parsed.name);
68
112
  }
69
113
 
70
114
  return Array.from(names).sort();
71
115
  }
116
+
117
+ /**
118
+ * Compare local theme versions against defaults and return themes that need upgrading.
119
+ */
120
+ export async function getOutdatedThemes(config: SynthOSConfig): Promise<string[]> {
121
+ const localFolder = userThemesFolder(config);
122
+ if (!await checkIfExists(localFolder)) return [];
123
+
124
+ const defaultFiles = await listFilesFromFolders(config.defaultThemesFolders);
125
+ const localFiles = await listFiles(localFolder);
126
+
127
+ // Build maps: theme name → highest version
128
+ const defaultVersions = new Map<string, number>();
129
+ for (const f of defaultFiles) {
130
+ const parsed = parseThemeFilename(f);
131
+ if (parsed) {
132
+ const cur = defaultVersions.get(parsed.name) ?? 0;
133
+ if (parsed.version > cur) defaultVersions.set(parsed.name, parsed.version);
134
+ }
135
+ }
136
+
137
+ const localVersions = new Map<string, number>();
138
+ for (const f of localFiles) {
139
+ const parsed = parseThemeFilename(f);
140
+ if (parsed) {
141
+ const cur = localVersions.get(parsed.name) ?? 0;
142
+ if (parsed.version > cur) localVersions.set(parsed.name, parsed.version);
143
+ }
144
+ }
145
+
146
+ const outdated: string[] = [];
147
+ for (const [name, defVer] of defaultVersions) {
148
+ const localVer = localVersions.get(name) ?? 0;
149
+ if (localVer < defVer) {
150
+ outdated.push(name);
151
+ }
152
+ }
153
+ return outdated;
154
+ }
@@ -0,0 +1,12 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
+ <defs>
3
+ <linearGradient id="g" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" stop-color="#a855f7"/>
5
+ <stop offset="100%" stop-color="#3b82f6"/>
6
+ </linearGradient>
7
+ </defs>
8
+ <rect width="32" height="32" rx="7" fill="#0f0f13"/>
9
+ <path d="M9 11 C9 8.8 10.8 7 13 7 L20 7 C22.2 7 24 8.8 24 11 C24 13.2 22.2 15 20 15 L13 15 C10.8 15 9 16.8 9 19 C9 21.2 10.8 23 13 23 L20 23" stroke="url(#g)" stroke-width="3" stroke-linecap="round" fill="none"/>
10
+ <circle cx="9" cy="11" r="2" fill="#a855f7"/>
11
+ <circle cx="23" cy="23" r="2" fill="#3b82f6"/>
12
+ </svg>