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
@@ -22,15 +22,22 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  __setModuleDefault(result, mod);
23
23
  return result;
24
24
  };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
25
28
  Object.defineProperty(exports, "__esModule", { value: true });
26
29
  exports.loadPageWithFallback = exports.usePageRoutes = void 0;
27
30
  const pages_1 = require("../pages");
28
31
  const settings_1 = require("../settings");
29
32
  const transformPage_1 = require("./transformPage");
30
- const modelInstructions_1 = require("./modelInstructions");
31
33
  const createCompletePrompt_1 = require("./createCompletePrompt");
32
34
  const debugLog_1 = require("./debugLog");
33
35
  const themes_1 = require("../themes");
36
+ const builders_1 = require("../builders");
37
+ const connectors_1 = require("../connectors");
38
+ const scripts_1 = require("../scripts");
39
+ const path_1 = __importDefault(require("path"));
40
+ const files_1 = require("../files");
34
41
  const cheerio = __importStar(require("cheerio"));
35
42
  /**
36
43
  * Required CDN imports that must be present on every v2 page.
@@ -38,6 +45,7 @@ const cheerio = __importStar(require("cheerio"));
38
45
  */
39
46
  const REQUIRED_IMPORTS = [
40
47
  { selector: 'script[src*="marked"]', src: 'https://cdnjs.cloudflare.com/ajax/libs/marked/14.1.1/marked.min.js' },
48
+ { selector: 'script[src*="html2canvas"]', src: 'https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js' },
41
49
  ];
42
50
  /**
43
51
  * Uses cheerio to ensure every required import is present in the page's <head>.
@@ -106,61 +114,399 @@ function injectPageScript(html, pageVersion) {
106
114
  }
107
115
  return html + '\n' + tag;
108
116
  }
109
- function usePageRoutes(config, app) {
117
+ /**
118
+ * Wrap each inline <script> body in an IIFE so that top-level const/let
119
+ * declarations become function-scoped. This prevents "Identifier … has
120
+ * already been declared" errors when the page is reloaded via
121
+ * document.write() (which reuses the Window's global lexical environment).
122
+ *
123
+ * Skips: external scripts (src=), JSON data blocks, already-wrapped IIFEs,
124
+ * and empty bodies.
125
+ */
126
+ function wrapInlineScriptsInIIFE(html) {
127
+ return html.replace(/<script([^>]*)>([\s\S]*?)<\/script>/gi, (match, attrs, body) => {
128
+ if (/\bsrc\s*=/i.test(attrs))
129
+ return match;
130
+ if (/\btype\s*=\s*["']application\/json["']/i.test(attrs))
131
+ return match;
132
+ const trimmed = body.trim();
133
+ if (!trimmed)
134
+ return match;
135
+ // Already wrapped — don't double-wrap
136
+ if (trimmed.startsWith('(function(){') || trimmed.startsWith('(function ()'))
137
+ return match;
138
+ return `<script${attrs}>(function(){${body}})();</script>`;
139
+ });
140
+ }
141
+ /**
142
+ * Move any external <script src="..."> tags found in <body> up to the end
143
+ * of <head>, preserving their relative order. Library scripts (d3, Chart.js,
144
+ * marked, etc.) only define globals and don't touch the DOM, so executing
145
+ * them in <head> is safe and guarantees they are available before any inline
146
+ * <body> scripts run. This also eliminates browser "parser-blocking cross
147
+ * site script invoked via document.write" warnings.
148
+ */
149
+ function hoistExternalScriptsToHead(html) {
150
+ const $ = cheerio.load(html, { decodeEntities: false });
151
+ const hoisted = [];
152
+ $('body script[src]').each((_, el) => {
153
+ const script = $(el);
154
+ // Don't move data-locked system scripts (page-script, page-helpers, etc.)
155
+ if (script.attr('data-locked') !== undefined)
156
+ return;
157
+ hoisted.push($.html(script));
158
+ script.remove();
159
+ });
160
+ if (hoisted.length > 0) {
161
+ $('head').append(hoisted.join('\n') + '\n');
162
+ }
163
+ return $.html();
164
+ }
165
+ /**
166
+ * Inline error-capture script injected as the first child of <head> so it
167
+ * registers window.onerror / unhandledrejection *before* any page scripts run.
168
+ * Stripped before page transformation so the LLM never sees it.
169
+ */
170
+ const ERROR_CAPTURE_ID = 'synthos-error-capture';
171
+ const ERROR_CAPTURE_SCRIPT = `<script id="${ERROR_CAPTURE_ID}">
172
+ (function(){
173
+ var E=window.__synthOSErrors=[];
174
+ window.onerror=function(m,s,l,c,e){
175
+ var entry=m+' at '+(s||'?')+':'+(l||'?')+':'+(c||'?');
176
+ if(e&&e.stack)entry+='\\n'+e.stack;
177
+ E.push(entry);showErr();return false;
178
+ };
179
+ window.addEventListener('unhandledrejection',function(ev){
180
+ var r=ev.reason;
181
+ E.push('Unhandled rejection: '+(r&&r.stack?r.stack:String(r)));showErr();
182
+ });
183
+ function showErr(){
184
+ var cm=document.getElementById('chatMessages');if(!cm)return;
185
+ if(showErr._p)return;showErr._p=true;
186
+ setTimeout(function(){
187
+ showErr._p=false;
188
+ var d=document.createElement('div');d.className='chat-message';
189
+ var p=document.createElement('p');
190
+ var pn=(window.pageInfo&&window.pageInfo.productName)||'SynthOS';
191
+ p.innerHTML='<strong>'+pn+':</strong> I noticed a JavaScript error on this page. '+
192
+ '<a href="#" style="color:var(--accent-primary,#a78bfa);text-decoration:underline;cursor:pointer" '+
193
+ 'onclick="(function(e){e.preventDefault();var ci=document.getElementById(\\'chatInput\\');'+
194
+ 'var f=document.getElementById(\\'chatForm\\');if(!ci||!f)return;'+
195
+ 'ci.value=\\'Fix the following JavaScript errors on this page:\\\\n\\\\nCONSOLE_ERRORS:\\\\n\\'+window.__synthOSErrors.join(\\'\\\\n---\\\\n\\');'+
196
+ 'window.__synthOSErrors=[];f.requestSubmit?f.requestSubmit():f.submit();})(event)">'+
197
+ 'Let me try to fix it</a>';
198
+ d.appendChild(p);cm.appendChild(d);
199
+ cm.scrollTo({top:cm.scrollHeight,behavior:'smooth'});
200
+ },500);
201
+ }
202
+ })();
203
+ </script>`;
204
+ function injectErrorCapture(html, pageVersion) {
205
+ if (pageVersion < 2)
206
+ return html;
207
+ if (html.includes(`id="${ERROR_CAPTURE_ID}"`))
208
+ return html;
209
+ const $ = cheerio.load(html, { decodeEntities: false });
210
+ $('head').prepend(ERROR_CAPTURE_SCRIPT + '\n');
211
+ return $.html();
212
+ }
213
+ /**
214
+ * Inject shell.css (always) and FluentLM base CSS/JS (v3 themes only).
215
+ * For v3 themes also adds the theme name class to <html> so scoped rules apply.
216
+ */
217
+ function injectShellAssets(html, themeName, themeVersion, toolbarPosition) {
218
+ const $ = cheerio.load(html, { decodeEntities: false });
219
+ const themeLink = $('link#theme-css');
220
+ // Favicon
221
+ if ($('link#synthos-favicon').length === 0) {
222
+ $('head').prepend('<link id="synthos-favicon" rel="icon" type="image/svg+xml" href="/static/favicon.svg">\n');
223
+ }
224
+ // shell.css — always injected (provides toolbar + layout chrome)
225
+ if ($('link#shell-css').length === 0) {
226
+ const shellLink = '<link id="shell-css" rel="stylesheet" href="/static/shell.css">';
227
+ if (themeLink.length > 0) {
228
+ themeLink.before(shellLink + '\n');
229
+ }
230
+ else {
231
+ $('head').append(shellLink + '\n');
232
+ }
233
+ }
234
+ // FluentLM assets — only for v3 themes
235
+ if (themeVersion >= 3) {
236
+ // Add theme name class to <html> (e.g. "nebula-dusk")
237
+ $('html').addClass(themeName);
238
+ // Inject CSS: fluentlm.min.css before shell.css (load order matters)
239
+ if ($('link#fluentlm-css').length === 0) {
240
+ const fluentLink = '<link id="fluentlm-css" rel="stylesheet" href="/static/fluentlm.min.css">';
241
+ const shellCss = $('link#shell-css');
242
+ if (shellCss.length > 0) {
243
+ shellCss.before(fluentLink + '\n');
244
+ }
245
+ else if (themeLink.length > 0) {
246
+ themeLink.before(fluentLink + '\n');
247
+ }
248
+ else {
249
+ $('head').append(fluentLink + '\n');
250
+ }
251
+ }
252
+ // Inject FluentLM JS before </body>
253
+ if ($('script#fluentlm-js').length === 0) {
254
+ const fluentScript = '<script id="fluentlm-js" src="/static/fluentlm.min.js"></script>';
255
+ $('body').append(fluentScript + '\n');
256
+ }
257
+ }
258
+ $('html').attr('data-toolbar', toolbarPosition || 'left');
259
+ return $.html();
260
+ }
261
+ // ---------------------------------------------------------------------------
262
+ // Context section builders — assemble ContextSections from enabled features
263
+ // ---------------------------------------------------------------------------
264
+ function buildContextSection() {
265
+ const now = new Date();
266
+ const dateTime = now.toLocaleString('en-US', {
267
+ weekday: 'long', year: 'numeric', month: 'long', day: 'numeric',
268
+ hour: 'numeric', minute: '2-digit', hour12: true,
269
+ });
270
+ return {
271
+ title: '<CONTEXT>',
272
+ content: `Current date and time: ${dateTime}`,
273
+ instructions: '',
274
+ };
275
+ }
276
+ function buildServerApisSection(customizer) {
277
+ const content = customizer ? (0, transformPage_1.buildRouteHints)(customizer) : transformPage_1.serverAPIs;
278
+ return {
279
+ title: '<SERVER_APIS>',
280
+ content: content.replace(/^<SERVER_APIS>\n?/, ''),
281
+ instructions: 'provides a list of available server APIs and helper functions you can call from injected scripts. Use synthos.* helpers instead of raw fetch().',
282
+ };
283
+ }
284
+ async function buildServerScriptsSection(pagesFolder) {
285
+ const scripts = await (0, scripts_1.listScripts)(pagesFolder);
286
+ return {
287
+ title: '<SERVER_SCRIPTS>',
288
+ content: scripts || '',
289
+ instructions: 'provides a list of available scripts callable via synthos.scripts.run(id, variables).',
290
+ };
291
+ }
292
+ function buildConnectorsSection(configuredConnectors) {
293
+ if (!configuredConnectors)
294
+ return undefined;
295
+ const entries = Object.entries(configuredConnectors)
296
+ .filter(([, cfg]) => cfg.enabled && cfg.apiKey);
297
+ if (entries.length === 0)
298
+ return undefined;
299
+ const blocks = entries.map(([id, cfg]) => {
300
+ const def = (0, connectors_1.getConnectorRegistry)().find(d => d.id === id);
301
+ if (!def)
302
+ return `- ${id}`;
303
+ let block = `- ${def.name} (id: "${id}", category: ${def.category})\n Base URL: ${def.baseUrl}`;
304
+ if (def.hints) {
305
+ block += `\n Usage:\n${def.hints.split('\n').map(l => ' ' + l).join('\n')}`;
306
+ }
307
+ // Append dynamic OAuth context
308
+ if (def.authStrategy === 'oauth2') {
309
+ const oauthCfg = cfg;
310
+ block += '\n Auth: The proxy attaches the access token automatically. Do NOT pass access_token in body or query params.';
311
+ if (oauthCfg.userId) {
312
+ block += `\n User ID: ${oauthCfg.userId} — use this directly in API paths (e.g. /${oauthCfg.userId}/media).`;
313
+ }
314
+ else {
315
+ block += '\n User ID: Not yet resolved. Call GET /me/accounts to discover it, then GET /{page-id}?fields=instagram_business_account to get the IG user ID.';
316
+ }
317
+ }
318
+ return block;
319
+ });
320
+ const content = `The user has configured and enabled these connectors:\n${blocks.join('\n\n')}\n\nYou may use synthos.connectors.call(connector, method, path, opts) to call them.\nIMPORTANT: Before making any connector call, ALWAYS check that the connector is configured first using synthos.connectors.list(). If the connector is not configured, show the user a friendly message with a link to the Settings > Connectors page (/settings?tab=connectors) so they can set it up.\nDo NOT hardcode API keys. The connector proxy attaches authentication automatically.`;
321
+ return {
322
+ title: '<CONFIGURED_CONNECTORS>',
323
+ content,
324
+ instructions: '',
325
+ };
326
+ }
327
+ function buildAgentsSection(configuredAgents) {
328
+ const enabledAgents = (configuredAgents ?? []).filter(a => a.enabled);
329
+ if (enabledAgents.length === 0)
330
+ return undefined;
331
+ const agentBlocks = enabledAgents.map(a => {
332
+ let block = `- ${a.name} (id: "${a.id}", provider: ${a.provider})`;
333
+ block += `\n Description: ${a.description}`;
334
+ if (a.capabilities?.streaming) {
335
+ block += `\n Supports streaming: yes`;
336
+ }
337
+ if (a.skills && a.skills.length > 0) {
338
+ const skillList = a.skills.map(s => ` - ${s.name}: ${s.description}`).join('\n');
339
+ block += `\n Skills:\n${skillList}`;
340
+ }
341
+ return block;
342
+ });
343
+ return {
344
+ title: '<CONFIGURED_AGENTS>',
345
+ content: `The user has configured these agents:\n\n${agentBlocks.join('\n\n')}\n\n${transformPage_1.AGENT_API_REFERENCE}`,
346
+ instructions: '',
347
+ };
348
+ }
349
+ function buildThemeSection(themeInfo) {
350
+ let content = '';
351
+ if (themeInfo) {
352
+ const { mode, colors } = themeInfo;
353
+ const colorList = Object.entries(colors)
354
+ .map(([name, value]) => ` --${name}: ${value}`)
355
+ .join('\n');
356
+ content = `Mode: ${mode}\nCSS custom properties (use instead of hardcoded values):\n${colorList}\n\nShared shell classes (pre-styled by theme, do not redefine):\n .chat-panel — Left sidebar container (30% width)\n .chat-header — Chat panel title bar\n .chat-messages — Scrollable message container\n .chat-message — Individual message wrapper\n .chat-input — Message text input\n .chat-submit — Send button\n .viewer-panel — Right content area (70% width)\n .loading-overlay — Full-screen loading overlay\n .spinner — Animated loading spinner\n .modal-overlay — Full-screen modal backdrop (position:fixed, z-index:2000, backdrop-filter:blur). Add class "show" to display.\n .modal-content — Centered modal container\n .modal-header — Gradient header bar\n .modal-body — Modal content area\n .modal-footer — Bottom action bar (flex, space-between)\n .modal-footer-right — Right-aligned button group\n\nModals and popups: ALWAYS use the theme\'s .modal-overlay class for any modal or popup overlay. Do NOT create custom overlay classes with position:fixed and z-index. Structure:\n <div class="modal-overlay" id="myModal">\n <div class="modal-content">\n <div class="modal-header">Title</div>\n <div class="modal-body">Content</div>\n <div class="modal-footer"><div class="modal-footer-right"><button>OK</button></div></div>\n </div>\n </div>\nShow/hide by toggling the "show" class: el.classList.add(\'show\') / el.classList.remove(\'show\'). This ensures correct z-index layering above the chat toggle and other UI elements.\n\nPage title bars: To align with the chat header, apply these styles:\n min-height: var(--header-min-height);\n padding: var(--header-padding-vertical) var(--header-padding-horizontal);\n line-height: var(--header-line-height);\n display: flex; align-items: center; justify-content: center; box-sizing: border-box;\n\nFull-viewer mode: For games, animations, or full-screen content, add class "full-viewer" to the viewer-panel element to remove its padding.\n\nChat panel behaviours (auto-injected via page script — do NOT recreate in page code):\n The server injects page-v2.js after transformation. It provides:\n - Form submit handler: sets action to window.location.pathname, shows #loadingOverlay, disables inputs\n - Chat scroll to bottom (#chatMessages)\n - Chat toggle button (.chat-toggle) — created dynamically if not in markup\n - .chat-input-wrapper — wraps #chatInput with a brainstorm icon button\n - Brainstorm modal (#brainstormModal) — LLM-powered brainstorm UI, created dynamically\n - Focus management — keeps keyboard input directed to #chatInput\n\n Do NOT:\n - Create your own form submit handler, toggle button, or input wrapper\n - Modify or replace .chat-panel, .chat-header, #chatForm, or .chat-toggle\n - INSERT new <script> blocks that duplicate existing ones — when fixing JavaScript, UPDATE or REPLACE the existing script's nodeId instead. Always give inline scripts a unique id attribute.\n - Set the form action attribute (page-v2.js sets it dynamically)\n - Include these CSS rules (in the theme): #loadingOverlay position, .chat-submit:disabled, .chat-input:disabled\n\n To add chat messages: use insert with parentId of #chatMessages and position "append".\n #chatMessages is the only unlocked element inside .chat-panel.\n\nThe <html> element has class "${mode}-mode". Always add .light-mode CSS overrides for any page-specific styles so the page works in both light and dark themes, unless the user has explicitly requested a very specific color scheme.`;
357
+ }
358
+ return {
359
+ title: '<THEME>',
360
+ content,
361
+ instructions: 'provides details on the current theme\'s color scheme and shared shell classes to help you generate theme-aware pages that fit seamlessly into the user experience.',
362
+ };
363
+ }
364
+ const LLMD_READING_GUIDE = `Content below is LLMD v0.2 — a compressed, token-optimized format. Read it as follows:
365
+
366
+ Line types (each non-empty line starts with exactly one prefix, or none for prose):
367
+
368
+ - @name — scope. Sets the current topic. All following lines belong to this scope
369
+ until the next @. Hierarchy is flattened: @Auth after @API means separate scopes,
370
+ not nested. Reconstruct context from scope names.
371
+ - :k=v k2=v2 — attributes. Key-value facts about the current scope. ¦ (broken bar,
372
+ U+00A6) separates multiple values (e.g., methods=oauth2¦apikey). Multiple pairs
373
+ may appear on one line, space-separated. Parse each pair by splitting on the
374
+ first = (keys never contain =).
375
+ - plain text (no prefix) — prose about the current scope.
376
+ - -item — list item. Nested depth uses dots: -. child, -.. grandchild.
377
+ - →Node — relation. Current scope depends on Node. ←Node is reverse. =Node is
378
+ equivalence. Trailing ? means optional (e.g., →Cache?).
379
+ - ::lang followed by <<<...>>> — literal block. Code or data preserved exactly,
380
+ not compressed.
381
+ - ~k=v — file metadata. Optional, appears at top of file.
382
+
383
+ Reserved meta-attributes (compiler-generated, prefixed with _):
384
+
385
+ - :_col=<header> — column header for a 2-column property table.
386
+ - :_cols=c1¦c2¦c3 — column headers for a multi-column table.
387
+ - :_pfx=<prefix> — common prefix extracted from subsequent keys. Prepend it to
388
+ restore full key names (e.g., :_pfx=flm-text-- then :secondary=... means the
389
+ full key is flm-text--secondary).
390
+
391
+ Compression artifacts (content may be shortened — infer original phrasing):
392
+
393
+ - Common words (the, a, is, are, of, etc.) may be removed from prose and list items.
394
+ - Long phrases replaced with short forms (e.g., "in order to" → "to",
395
+ "application programming interface" → "API", "specification" → "spec").
396
+ - Units shortened (e.g., "1000 requests per minute" → "1000/m",
397
+ "seconds" → "s", "megabytes" → "MB").
398
+ - Boolean values compressed (Yes/No → Y/N, true/false → T/F,
399
+ enabled/disabled → Y/N).
400
+ - Trailing periods stripped from prose and list items.
401
+ - Negation (no, not, never) and modals (must, should, may, always) are always
402
+ preserved.`;
403
+ function buildLlmdReadingGuideSection() {
404
+ return {
405
+ title: '<LLMD_READING_GUIDE>',
406
+ content: LLMD_READING_GUIDE,
407
+ instructions: '',
408
+ };
409
+ }
410
+ async function buildFluentLMSection(config) {
411
+ const filePath = await (0, files_1.findFileInFolders)(config.staticFilesFolders, 'fluentlm-instructions.md');
412
+ if (!filePath)
413
+ return undefined;
414
+ try {
415
+ const content = await (0, files_1.loadFile)(filePath);
416
+ return {
417
+ title: '<FLUENTLM_COMPONENTS>',
418
+ content,
419
+ instructions: `<FLUENTLM_COMPONENTS> is the component library available on every page. You MUST use FluentLM components instead of writing custom HTML/CSS for standard UI elements.
420
+ REQUIRED: Use flm-button for buttons, flm-textfield for inputs, flm-dropdown for selects, flm-dialog/flm-panel/flm-modal for overlays, flm-pivot for tabs, flm-nav for navigation, flm-toggle for switches, flm-card for cards, flm-callout for tooltips, flm-messagebar for alerts, and all other components listed in <FLUENTLM_COMPONENTS>.
421
+ FORBIDDEN: Do NOT create custom CSS classes for buttons (e.g. .my-btn, .okr-btn), inputs, modals, cards, tabs, dropdowns, or any UI element that has a FluentLM equivalent. Do NOT use raw <button>, <input>, or <select> elements without FluentLM classes.
422
+ Apply FluentLM utility classes (flm-text--secondary, flm-stack, etc.) for layout and typography instead of custom CSS where possible.`,
423
+ };
424
+ }
425
+ catch {
426
+ return undefined;
427
+ }
428
+ }
429
+ async function buildRecommendedFrameworksSection(config) {
430
+ const filePath = await (0, files_1.findFileInFolders)(config.staticFilesFolders, 'recommended-frameworks.llmd');
431
+ if (!filePath)
432
+ return undefined;
433
+ try {
434
+ const content = await (0, files_1.loadFile)(filePath);
435
+ return {
436
+ title: '<RECOMMENDED_FRAMEWORKS>',
437
+ content,
438
+ instructions: 'lists recommended third-party frameworks with CDN URLs. When a page needs a framework from this list, load it via <script> (or <link> for CSS) tags at the end of the <head> block. Always use the version shown in the CDN URL (it is the latest approved version).',
439
+ };
440
+ }
441
+ catch {
442
+ return undefined;
443
+ }
444
+ }
445
+ function buildMessageFormatSection(productName) {
446
+ return {
447
+ title: '<MESSAGE_FORMAT>',
448
+ content: `<div class="chat-message"><p><strong>{${productName}: | User:}</strong> {message contents}</p></div>`,
449
+ instructions: 'provides the HTML structure for chat messages in the chat panel.',
450
+ };
451
+ }
452
+ function usePageRoutes(config, app, customizer) {
110
453
  // Redirect / to /home page
111
454
  app.get('/', (req, res) => res.redirect(HOME_PAGE_ROUTE));
112
455
  // Page retrieval
113
456
  app.get('/:page', async (req, res) => {
114
457
  // Redirect if settings not configured
115
458
  const { page } = req.params;
116
- const isConfigured = await (0, settings_1.hasConfiguredSettings)(config.pagesFolder);
459
+ const isConfigured = await (0, settings_1.hasConfiguredSettings)(config);
117
460
  if (!isConfigured && page !== 'settings') {
118
461
  res.redirect('/settings?firstRun=1');
119
462
  return;
120
463
  }
121
- // Ensure page exists
122
- const pageState = await loadPageWithFallback(page, config, false);
464
+ // Ensure page exists — force fresh disk read for required pages
465
+ const isRequiredPage = config.requiredPages.includes(page);
466
+ const pageState = await loadPageWithFallback(page, config, isRequiredPage);
123
467
  if (!pageState) {
124
468
  res.status(404).send(PAGE_NOT_FOUND);
125
469
  return;
126
470
  }
127
471
  // Load page metadata for version-based script injection
128
- const metadata = await (0, pages_1.loadPageMetadata)(config.pagesFolder, page, config.requiredPagesFolder);
472
+ const metadata = await (0, pages_1.loadPageMetadata)(config, page, config.requiredPagesFolders);
129
473
  const pageVersion = metadata?.pageVersion ?? 0;
130
- // Block outdated pages (redirect to /pages so user sees upgrade UI)
131
- if (pageVersion < pages_1.PAGE_VERSION && !pages_1.REQUIRED_PAGES.includes(page)) {
132
- res.redirect('/pages');
474
+ // Block outdated pages (redirect to tabs list so user sees upgrade UI)
475
+ if (pageVersion < pages_1.PAGE_VERSION && !config.requiredPages.includes(page)) {
476
+ res.redirect(customizer?.tabsListRoute ?? '/pages');
133
477
  return;
134
478
  }
479
+ // Load settings to determine theme version for FluentLM base injection
480
+ const settings = await (0, settings_1.loadSettings)(config);
481
+ const themeName = settings.theme ?? 'nebula-dusk';
482
+ const themeVersion = await (0, themes_1.loadThemeVersion)(themeName, config);
135
483
  let html = ensureRequiredImports(pageState, pageVersion);
484
+ html = injectErrorCapture(html, pageVersion);
485
+ html = injectShellAssets(html, themeName, themeVersion, settings.toolbarPosition);
136
486
  html = injectPageInfoScript(html, page);
137
487
  html = injectPageHelpers(html, pageVersion);
138
488
  html = injectPageScript(html, pageVersion);
139
- res.send(html);
140
- });
141
- // Page reset
142
- app.get('/:page/reset', async (req, res) => {
143
- // Redirect if settings not configured
144
- const { page } = req.params;
145
- const isConfigured = await (0, settings_1.hasConfiguredSettings)(config.pagesFolder);
146
- if (!isConfigured) {
147
- res.redirect('/settings?firstRun=1');
148
- return;
489
+ // Inject version meta tag so undo/try-again links appear on page load
490
+ {
491
+ const latestVersion = await (0, pages_1.getLatestVersion)(config, page);
492
+ if (latestVersion > 0) {
493
+ html = html.replace('</head>', `<meta name="synthos-version" content="${latestVersion}">\n</head>`);
494
+ }
149
495
  }
150
- // Ensure page exists
151
- const pageState = await loadPageWithFallback(page, config, true);
152
- if (!pageState) {
153
- res.status(404).send(PAGE_NOT_FOUND);
154
- return;
496
+ html = hoistExternalScriptsToHead(html);
497
+ // Replace branding for white-label forks
498
+ const productName = customizer?.productName ?? 'SynthOS';
499
+ if (productName !== 'SynthOS') {
500
+ html = html.replace(/synthos/gi, productName);
155
501
  }
156
- res.redirect(`/${page}`);
502
+ res.send(html);
157
503
  });
158
504
  // Page save
159
505
  app.post('/:page/save', async (req, res) => {
160
506
  try {
161
507
  // Redirect if settings not configured
162
508
  const { page } = req.params;
163
- const isConfigured = await (0, settings_1.hasConfiguredSettings)(config.pagesFolder);
509
+ const isConfigured = await (0, settings_1.hasConfiguredSettings)(config);
164
510
  if (!isConfigured) {
165
511
  res.status(400).json({ error: 'Settings not configured' });
166
512
  return;
@@ -192,22 +538,25 @@ function usePageRoutes(config, app) {
192
538
  res.status(404).json({ error: PAGE_NOT_FOUND });
193
539
  return;
194
540
  }
195
- // If greeting is provided, process with cheerio
196
- if (greeting && typeof greeting === 'string' && greeting.trim().length > 0) {
541
+ // Always trim chat to the first message on save and remove undo links
542
+ {
197
543
  const $ = cheerio.load(pageState);
198
544
  const messages = $('#chatMessages .chat-message');
199
- // Keep only the first message, remove the rest
200
545
  messages.slice(1).remove();
201
- // Update the greeting text in the first message
202
- const firstP = messages.first().find('p');
203
- const strong = firstP.find('strong');
204
- if (strong.length) {
205
- firstP.html('<strong>Synthos:</strong> ' + greeting.trim());
546
+ // Remove any undo links
547
+ $('#chatMessages .synthos-undo-link').remove();
548
+ // Update greeting text if provided
549
+ if (greeting && typeof greeting === 'string' && greeting.trim().length > 0) {
550
+ const firstP = messages.first().find('p');
551
+ const strong = firstP.find('strong');
552
+ if (strong.length) {
553
+ firstP.html('<strong>Synthos:</strong> ' + greeting.trim());
554
+ }
206
555
  }
207
556
  pageState = $.html();
208
557
  }
209
558
  // Inject save-line marker at the end of chat messages (skip for locked pages)
210
- const sourceMetadata = await (0, pages_1.loadPageMetadata)(config.pagesFolder, page, config.requiredPagesFolder);
559
+ const sourceMetadata = await (0, pages_1.loadPageMetadata)(config, page, config.requiredPagesFolders);
211
560
  if (sourceMetadata?.mode !== 'locked') {
212
561
  const $ = cheerio.load(pageState);
213
562
  // Remove any existing save-line first
@@ -217,9 +566,33 @@ function usePageRoutes(config, app) {
217
566
  pageState = $.html();
218
567
  }
219
568
  // Save as new page
220
- await (0, pages_1.savePageState)(config.pagesFolder, saveAs, pageState, title, categories);
569
+ await (0, pages_1.savePageState)(config, saveAs, pageState, title, categories);
570
+ // Copy files (sound effects, etc.) from source page when saving as a different name
571
+ if (page !== saveAs) {
572
+ // Check source page's files/ dir in user pages, then fallback folders
573
+ let sourceFilesDir;
574
+ const userFilesDir = path_1.default.join(config.pagesFolder, 'pages', page, 'files');
575
+ if (await config.storageProvider.checkIfExists(userFilesDir)) {
576
+ sourceFilesDir = userFilesDir;
577
+ }
578
+ else {
579
+ for (const folder of config.requiredPagesFolders) {
580
+ const candidate = path_1.default.join(folder, page, 'files');
581
+ if (await (0, files_1.checkIfExists)(candidate)) {
582
+ sourceFilesDir = candidate;
583
+ break;
584
+ }
585
+ }
586
+ }
587
+ if (sourceFilesDir) {
588
+ const targetFilesDir = path_1.default.join(config.pagesFolder, 'pages', saveAs, 'files');
589
+ await config.storageProvider.copyFolderRecursive(sourceFilesDir, targetFilesDir);
590
+ }
591
+ }
592
+ // Clear version files after saving (fresh baseline)
593
+ await (0, pages_1.clearVersions)(config, saveAs);
221
594
  // Also update metadata with categories (in case page.json already existed)
222
- await (0, pages_1.savePageMetadata)(config.pagesFolder, saveAs, {
595
+ await (0, pages_1.savePageMetadata)(config, saveAs, {
223
596
  title,
224
597
  categories,
225
598
  pinned: false,
@@ -236,12 +609,73 @@ function usePageRoutes(config, app) {
236
609
  res.status(500).json({ error: err.message });
237
610
  }
238
611
  });
612
+ // Page undo — roll back to the previous version
613
+ app.post('/:page/undo', async (req, res) => {
614
+ try {
615
+ const { page } = req.params;
616
+ const sp = config.storageProvider;
617
+ const cv = await (0, pages_1.getLatestVersion)(config, page);
618
+ if (cv <= 0) {
619
+ res.status(400).send('Nothing to undo');
620
+ return;
621
+ }
622
+ // Delete the current version file
623
+ const pageFolder = path_1.default.join(config.pagesFolder, 'pages', page);
624
+ const versionFile = path_1.default.join(pageFolder, `page.v${cv}.html`);
625
+ if (await sp.checkIfExists(versionFile)) {
626
+ await sp.deleteFile(versionFile);
627
+ }
628
+ // Load previous version (v{cv-1}.html, or page.html if rolling back to v0)
629
+ const prevVersion = cv - 1;
630
+ let previousHtml;
631
+ if (prevVersion > 0) {
632
+ previousHtml = await (0, pages_1.loadPageVersion)(config, page, prevVersion);
633
+ }
634
+ else {
635
+ // v0 = the saved page.html baseline (user folder → required folders fallback)
636
+ previousHtml = await loadPageWithFallback(page, config, true);
637
+ }
638
+ if (!previousHtml) {
639
+ res.status(500).send('Could not load previous version');
640
+ return;
641
+ }
642
+ // Inject shell assets (same as GET handler)
643
+ const settings = await (0, settings_1.loadSettings)(config);
644
+ const metadata = await (0, pages_1.loadPageMetadata)(config, page, config.requiredPagesFolders);
645
+ const pv = metadata?.pageVersion ?? 0;
646
+ const themeName = settings.theme ?? 'nebula-dusk';
647
+ const themeVersion = await (0, themes_1.loadThemeVersion)(themeName, config);
648
+ let out = ensureRequiredImports(previousHtml, pv);
649
+ out = injectErrorCapture(out, pv);
650
+ out = injectShellAssets(out, themeName, themeVersion, settings.toolbarPosition);
651
+ out = injectPageInfoScript(out, page);
652
+ out = injectPageHelpers(out, pv);
653
+ out = injectPageScript(out, pv);
654
+ // Inject version meta tag if still have versions
655
+ if (prevVersion > 0) {
656
+ out = out.replace('</head>', `<meta name="synthos-version" content="${prevVersion}">\n</head>`);
657
+ }
658
+ // Same hardening as POST handler (undo also loads via document.write)
659
+ out = hoistExternalScriptsToHead(out);
660
+ out = wrapInlineScriptsInIIFE(out);
661
+ // Replace branding for white-label forks
662
+ const productName = customizer?.productName ?? 'SynthOS';
663
+ if (productName !== 'SynthOS') {
664
+ out = out.replace(/synthos/gi, productName);
665
+ }
666
+ res.send(out);
667
+ }
668
+ catch (err) {
669
+ console.error(err);
670
+ res.status(500).send(err.message);
671
+ }
672
+ });
239
673
  // Page transformation
240
674
  app.post('/:page', async (req, res) => {
241
675
  try {
242
676
  // Ensure settings configured
243
677
  const { page } = req.params;
244
- const isConfigured = await (0, settings_1.hasConfiguredSettings)(config.pagesFolder);
678
+ const isConfigured = await (0, settings_1.hasConfiguredSettings)(config);
245
679
  if (!isConfigured) {
246
680
  res.status(400).send('Settings not configured');
247
681
  return;
@@ -252,71 +686,225 @@ function usePageRoutes(config, app) {
252
686
  res.status(404).send(PAGE_NOT_FOUND);
253
687
  return;
254
688
  }
689
+ // Reject modifications to locked pages
690
+ const lockMetadata = await (0, pages_1.loadPageMetadata)(config, page, config.requiredPagesFolders);
691
+ if (lockMetadata?.mode === 'locked') {
692
+ res.status(403).send('This page is locked and cannot be modified');
693
+ return;
694
+ }
255
695
  // Get required and optional parameters
256
696
  const { message } = req.body; // Extract the message from the request body
257
697
  if (typeof message !== 'string') {
258
698
  res.status(400).send('Invalid or missing message parameter');
259
699
  return;
260
700
  }
701
+ // Extract and validate optional attachments
702
+ let attachments;
703
+ if (Array.isArray(req.body.attachments)) {
704
+ attachments = [];
705
+ for (const att of req.body.attachments) {
706
+ if (att && typeof att.mediaType === 'string' && att.mediaType.startsWith('image/') && typeof att.data === 'string') {
707
+ attachments.push({ mediaType: att.mediaType, data: att.data, name: typeof att.name === 'string' ? att.name : undefined });
708
+ }
709
+ }
710
+ if (attachments.length === 0)
711
+ attachments = undefined;
712
+ }
261
713
  // Create model instance
262
- const innerCompletePrompt = await (0, createCompletePrompt_1.createCompletePrompt)(config.pagesFolder, 'builder', req.body.model);
714
+ const innerCompletePrompt = await (0, createCompletePrompt_1.createCompletePrompt)(config, 'builder', req.body.model);
263
715
  const debugVerbose = config.debugPageUpdates;
264
716
  let inputChars = 0;
265
717
  let outputChars = 0;
266
- const completePrompt = async (args) => {
267
- if (debugVerbose) {
268
- console.log((0, debugLog_1.green)((0, debugLog_1.dim)('\n ===== PAGE UPDATE REQUEST =====')));
269
- console.log((0, debugLog_1.green)(` SYSTEM:\n${args.system?.content}`));
270
- console.log((0, debugLog_1.green)(`\n PROMPT:\n${args.prompt.content}`));
271
- }
272
- inputChars += (args.system?.content?.length ?? 0) + (args.prompt.content?.length ?? 0);
273
- const result = await innerCompletePrompt(args);
274
- if (result.completed) {
275
- outputChars += result.value?.length ?? 0;
276
- }
277
- if (debugVerbose) {
278
- console.log((0, debugLog_1.green)((0, debugLog_1.dim)('\n ----- PAGE UPDATE RESPONSE -----')));
718
+ const wrapModel = (inner) => {
719
+ return async (args) => {
720
+ if (debugVerbose) {
721
+ console.log((0, debugLog_1.green)((0, debugLog_1.dim)('\n ===== PAGE UPDATE REQUEST =====')));
722
+ console.log((0, debugLog_1.green)(` SYSTEM:\n${args.system?.content}`));
723
+ console.log((0, debugLog_1.green)(`\n PROMPT:\n${args.prompt.content}`));
724
+ }
725
+ inputChars += (args.system?.content?.length ?? 0) + (args.prompt.content?.length ?? 0);
726
+ const result = await inner(args);
279
727
  if (result.completed) {
280
- console.log((0, debugLog_1.green)(` RESPONSE:\n${result.value}`));
728
+ outputChars += result.value?.length ?? 0;
281
729
  }
282
- else {
283
- console.log((0, debugLog_1.red)(` ERROR: ${result.error?.message}`));
730
+ if (debugVerbose) {
731
+ console.log((0, debugLog_1.green)((0, debugLog_1.dim)('\n ----- PAGE UPDATE RESPONSE -----')));
732
+ if (result.completed) {
733
+ console.log((0, debugLog_1.green)(` RESPONSE:\n${result.value}`));
734
+ }
735
+ else {
736
+ console.log((0, debugLog_1.red)(` ERROR: ${result.error?.message}`));
737
+ }
738
+ console.log((0, debugLog_1.green)((0, debugLog_1.dim)(' ================================\n')));
284
739
  }
285
- console.log((0, debugLog_1.green)((0, debugLog_1.dim)(' ================================\n')));
286
- }
287
- return result;
740
+ return result;
741
+ };
288
742
  };
289
- // Transform and cache updated page
743
+ const wrappedCompletePrompt = wrapModel(innerCompletePrompt);
744
+ // Load settings and build context
290
745
  const pagesFolder = config.pagesFolder;
291
- const settings = await (0, settings_1.loadSettings)(config.pagesFolder);
292
- const builder = (0, settings_1.getModelEntry)(settings, 'builder');
293
- const { instructions } = builder;
746
+ const settings = await (0, settings_1.loadSettings)(config);
747
+ const entry = (0, settings_1.getModelEntry)(settings, 'builder');
748
+ const { instructions } = entry;
749
+ const productName = customizer?.productName ?? 'SynthOS';
750
+ // Build context sections from enabled features
751
+ const featureSections = [];
752
+ // CONTEXT section — always first so the model knows the current date/time
753
+ featureSections.push(buildContextSection());
754
+ // LLMD_READING_GUIDE section (first — tells model how to read compressed content)
755
+ featureSections.push(buildLlmdReadingGuideSection());
756
+ // SERVER_APIS section
757
+ featureSections.push(buildServerApisSection(customizer));
758
+ // SERVER_SCRIPTS section
759
+ featureSections.push(await buildServerScriptsSection(pagesFolder));
760
+ // CONFIGURED_CONNECTORS section
761
+ const connectorsSection = buildConnectorsSection(settings.connectors);
762
+ if (connectorsSection)
763
+ featureSections.push(connectorsSection);
764
+ // CONFIGURED_AGENTS section
765
+ const agentsSection = buildAgentsSection(settings.agents);
766
+ if (agentsSection)
767
+ featureSections.push(agentsSection);
768
+ // THEME section
294
769
  const theme = settings.theme;
295
770
  const themeInfo = await (0, themes_1.loadThemeInfo)(theme ?? 'nebula-dusk', config);
296
- const modelInstructions = (0, modelInstructions_1.getModelInstructions)(builder.provider);
297
- const configuredConnectors = settings.connectors;
298
- const configuredAgents = settings.agents;
299
- const result = await (0, transformPage_1.transformPage)({ pagesFolder, pageState, message, instructions, modelInstructions, completePrompt, themeInfo, configuredConnectors, configuredAgents });
771
+ featureSections.push(buildThemeSection(themeInfo));
772
+ // FLUENTLM_COMPONENTS section
773
+ const fluentLMSection = await buildFluentLMSection(config);
774
+ if (fluentLMSection)
775
+ featureSections.push(fluentLMSection);
776
+ // RECOMMENDED_FRAMEWORKS section
777
+ const frameworksSection = await buildRecommendedFrameworksSection(config);
778
+ if (frameworksSection)
779
+ featureSections.push(frameworksSection);
780
+ // MESSAGE_FORMAT section
781
+ featureSections.push(buildMessageFormatSection(productName));
782
+ // Custom transform instructions as a section (backward compat)
783
+ const customTransformInstructions = customizer ? customizer.getTransformInstructions() : undefined;
784
+ if (customTransformInstructions && customTransformInstructions.length > 0) {
785
+ featureSections.push({
786
+ title: '<CUSTOM_INSTRUCTIONS>',
787
+ content: customTransformInstructions.join('\n'),
788
+ instructions: '',
789
+ });
790
+ }
791
+ // Custom context sections from Customizer (appended last)
792
+ if (customizer) {
793
+ featureSections.push(...customizer.getContextSections());
794
+ }
795
+ // Detect first edit (v0→v1) for saved pages
796
+ const isRequiredPage = config.requiredPages.includes(page);
797
+ const pageFolder = path_1.default.join(pagesFolder, 'pages', page);
798
+ const pageFileExists = await config.storageProvider.checkIfExists(path_1.default.join(pageFolder, 'page.html'));
799
+ let currentVersion = await (0, pages_1.getLatestVersion)(config, page);
800
+ const isFirstEdit = !isRequiredPage && pageFileExists && currentVersion === 0;
801
+ // Try again — roll back to previous version before re-running the transform
802
+ const tryAgain = req.body.tryAgain === true;
803
+ let transformInput = pageState;
804
+ if (tryAgain && currentVersion > 0) {
805
+ // Delete current version file
806
+ const versionFile = path_1.default.join(pageFolder, `page.v${currentVersion}.html`);
807
+ if (await config.storageProvider.checkIfExists(versionFile)) {
808
+ await config.storageProvider.deleteFile(versionFile);
809
+ }
810
+ // Load previous version
811
+ const prevVersion = currentVersion - 1;
812
+ if (prevVersion > 0) {
813
+ transformInput = await (0, pages_1.loadPageVersion)(config, page, prevVersion) ?? pageState;
814
+ }
815
+ else {
816
+ transformInput = await loadPageWithFallback(page, config, true) ?? pageState;
817
+ }
818
+ currentVersion = prevVersion;
819
+ }
820
+ // Create builder
821
+ const builder = (0, builders_1.createBuilder)(entry.provider, wrappedCompletePrompt, instructions, productName, {
822
+ apiKey: entry.configuration.apiKey,
823
+ model: entry.configuration.model,
824
+ wrapModel,
825
+ isFirstEdit,
826
+ tryAgain,
827
+ });
828
+ // Inject save-line before transform if not already present, so new
829
+ // messages appended by the LLM land after the marker.
830
+ {
831
+ const $pre = cheerio.load(transformInput, { decodeEntities: false });
832
+ if ($pre('#chatMessages').length > 0 && $pre('#chatMessages .save-line').length === 0) {
833
+ $pre('#chatMessages').append('<div class="save-line" data-locked="true"><span class="save-line-label">Saved</span></div>');
834
+ transformInput = $pre.html();
835
+ }
836
+ }
837
+ // Transform page
838
+ const result = await (0, transformPage_1.transformPage)({
839
+ pageState: transformInput,
840
+ message,
841
+ instructions,
842
+ builder,
843
+ additionalSections: featureSections,
844
+ isBuilder: page === 'builder',
845
+ productName,
846
+ attachments,
847
+ });
300
848
  if (result.completed) {
301
- const { html, changeCount } = result.value;
849
+ let { html, changeCount } = result.value;
302
850
  if (config.debug) {
303
851
  const inTokens = (0, debugLog_1.estimateTokens)(inputChars).toLocaleString();
304
852
  const outTokens = (0, debugLog_1.estimateTokens)(outputChars).toLocaleString();
305
853
  console.log(` page: ${page} | message: ${message.length} chars | changes: ${changeCount} ops | ~${inTokens} in / ~${outTokens} out tokens`);
306
854
  }
307
- (0, pages_1.updatePageState)(page, html);
855
+ // Handle 0-ops from transforms: model returned no page changes.
856
+ // Append the user's message + a "try again" prompt with undo link.
857
+ // Still save as a version so undo logic stays simple.
858
+ // changeCount -1 means error/reply path already handled messaging.
859
+ if (changeCount === 0) {
860
+ const $ = cheerio.load(pageState, { decodeEntities: false });
861
+ const chatMessages = $('#chatMessages');
862
+ if (chatMessages.length > 0) {
863
+ const escapedMsg = message.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
864
+ chatMessages.append(`<div class="chat-message"><p><strong>User:</strong> ${escapedMsg}</p></div>`);
865
+ const tryAgainLink = `<a href="#" style="color:var(--themePrimary);text-decoration:underline;cursor:pointer" `
866
+ + `onclick="(function(e){e.preventDefault();var ci=document.getElementById('chatInput');if(ci){ci.focus();ci.value='';}})(event)">try again</a>`;
867
+ const undoLink = `<a href="#" style="color:var(--themePrimary);text-decoration:underline;cursor:pointer" `
868
+ + `onclick="(function(e){e.preventDefault();var o=document.getElementById('loadingOverlay');if(o)o.style.display='flex';`
869
+ + `fetch(window.location.pathname+'/undo',{method:'POST',headers:{'Content-Type':'application/json'},body:'{}'})`
870
+ + `.then(function(r){return r.text()}).then(function(h){document.open();document.write(h);document.close()})`
871
+ + `.catch(function(){if(o)o.style.display='none'});})(event)">undo</a>`;
872
+ chatMessages.append(`<div class="chat-message"><p><strong>${productName}:</strong> Sorry, I wasn\u2019t able to make changes for that request. Please ${tryAgainLink} or ${undoLink}.</p></div>`);
873
+ }
874
+ html = $.html();
875
+ }
876
+ // Save version snapshot (working state for all pages, undo support for saved pages)
877
+ const nextVersion = currentVersion + 1;
878
+ await (0, pages_1.savePageVersion)(config, page, nextVersion, html);
308
879
  // Update lastModified timestamp in page metadata
309
- const metadata = await (0, pages_1.loadPageMetadata)(pagesFolder, page, config.requiredPagesFolder);
880
+ const metadata = await (0, pages_1.loadPageMetadata)(config, page, config.requiredPagesFolders);
310
881
  if (metadata) {
311
882
  metadata.lastModified = new Date().toISOString();
312
- await (0, pages_1.savePageMetadata)(pagesFolder, page, metadata);
883
+ await (0, pages_1.savePageMetadata)(config, page, metadata);
313
884
  }
314
885
  // Inject required imports and page scripts (same as GET)
315
886
  const pv = metadata?.pageVersion ?? 0;
887
+ const themeName = settings.theme ?? 'nebula-dusk';
888
+ const themeVersion = await (0, themes_1.loadThemeVersion)(themeName, config);
316
889
  let out = ensureRequiredImports(html, pv);
890
+ out = injectErrorCapture(out, pv);
891
+ out = injectShellAssets(out, themeName, themeVersion, settings.toolbarPosition);
317
892
  out = injectPageInfoScript(out, page);
318
893
  out = injectPageHelpers(out, pv);
319
894
  out = injectPageScript(out, pv);
895
+ // Inject version meta tag for client-side undo support
896
+ if (nextVersion > 0) {
897
+ out = out.replace('</head>', `<meta name="synthos-version" content="${nextVersion}">\n</head>`);
898
+ }
899
+ // Hoist external scripts to <head> so libs load before
900
+ // inline body scripts, then wrap inlines in IIFEs to
901
+ // avoid const/let redeclaration via document.write().
902
+ out = hoistExternalScriptsToHead(out);
903
+ out = wrapInlineScriptsInIIFE(out);
904
+ // Replace branding for white-label forks
905
+ if (productName !== 'SynthOS') {
906
+ out = out.replace(/synthos/gi, productName);
907
+ }
320
908
  res.send(out);
321
909
  }
322
910
  else {
@@ -336,13 +924,22 @@ function usePageRoutes(config, app) {
336
924
  }
337
925
  exports.usePageRoutes = usePageRoutes;
338
926
  async function loadPageWithFallback(page, config, reset) {
927
+ if (reset) {
928
+ // Clear working-state versions so we get the fresh template
929
+ await (0, pages_1.clearVersions)(config, page);
930
+ }
339
931
  // Try primary pages folder first
340
- const pageState = await (0, pages_1.loadPageState)(config.pagesFolder, page, reset);
341
- if (pageState) {
932
+ const pageState = await (0, pages_1.loadPageState)(config, page);
933
+ if (pageState)
342
934
  return pageState;
935
+ // Try fallback pages folders (package content, always local fs)
936
+ for (const folder of config.requiredPagesFolders) {
937
+ const candidate = path_1.default.join(folder, page, 'page.html');
938
+ if (await (0, files_1.checkIfExists)(candidate)) {
939
+ return await (0, files_1.loadFile)(candidate);
940
+ }
343
941
  }
344
- // Try fallback pages folder second
345
- return (0, pages_1.loadPageState)(config.requiredPagesFolder, page, reset);
942
+ return undefined;
346
943
  }
347
944
  exports.loadPageWithFallback = loadPageWithFallback;
348
945
  //# sourceMappingURL=usePageRoutes.js.map