synthos 0.8.0 → 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.
- package/README.md +1 -1
- package/default-pages/application/page.html +42 -0
- package/default-pages/application/page.json +10 -0
- package/default-pages/elevenlabs_effects_studio/page.html +1363 -0
- package/default-pages/elevenlabs_effects_studio/page.json +11 -0
- package/default-pages/elevenlabs_voice_studio/page.html +801 -0
- package/default-pages/elevenlabs_voice_studio/page.json +11 -0
- package/default-pages/{json_tools.html → json_tools/page.html} +13 -11
- package/default-pages/json_tools/page.json +10 -0
- package/default-pages/my_notes/notes/a1b2c3d4-e5f6-7890-abcd-ef1234567890.json +5 -0
- package/default-pages/my_notes/page.html +132 -0
- package/default-pages/{my_notes.json → my_notes/page.json} +2 -2
- package/default-pages/neon_asteroids/files/Ambient_Space.mp3 +0 -0
- package/default-pages/neon_asteroids/files/Ambient_Space2.mp3 +0 -0
- package/default-pages/neon_asteroids/files/Ambient_Space3.mp3 +0 -0
- package/default-pages/neon_asteroids/files/Asteroid_Explosion.mp3 +0 -0
- package/default-pages/neon_asteroids/files/Hyperspace_Jump.mp3 +0 -0
- package/default-pages/neon_asteroids/files/Laser_Fire.mp3 +0 -0
- package/default-pages/neon_asteroids/files/Menu_Navigate.mp3 +0 -0
- package/default-pages/neon_asteroids/files/Power_Up_Collect.mp3 +0 -0
- package/default-pages/neon_asteroids/files/Saucer_Alert.mp3 +0 -0
- package/default-pages/neon_asteroids/files/Ship_Thrust.mp3 +0 -0
- package/default-pages/neon_asteroids/files/effects.json +74 -0
- package/default-pages/neon_asteroids/page.html +1822 -0
- package/default-pages/{neon_asteroids.json → neon_asteroids/page.json} +3 -3
- package/default-pages/{oregon_trail.html → oregon_trail/page.html} +14 -12
- package/default-pages/{oregon_trail.json → oregon_trail/page.json} +2 -2
- package/default-pages/retro_game_starter/page.html +1308 -0
- package/default-pages/retro_game_starter/page.json +12 -0
- package/default-pages/{sidebar_page.html → sidebar_page/page.html} +12 -10
- package/default-pages/sidebar_page/page.json +10 -0
- package/default-pages/{solar_explorer.html → solar_explorer/page.html} +14 -11
- package/default-pages/{solar_explorer.json → solar_explorer/page.json} +2 -2
- package/default-pages/{solar_tutorial.html → solar_tutorial/page.html} +12 -10
- package/default-pages/solar_tutorial/page.json +10 -0
- package/default-pages/{two-panel_page.html → two-panel_page/page.html} +13 -11
- package/default-pages/two-panel_page/page.json +10 -0
- package/default-pages/{us_map.html → us_map/page.html} +193 -192
- package/default-pages/{us_map.json → us_map/page.json} +12 -12
- package/default-pages/{us_map_1850.html → us_map_1850/page.html} +326 -325
- package/default-pages/{us_map_1850.json → us_map_1850/page.json} +12 -12
- package/default-pages/{western_cities_1850.html → western_cities_1850/page.html} +527 -526
- package/default-pages/{western_cities_1850.json → western_cities_1850/page.json} +12 -12
- package/default-themes/aurora-dawn.json +19 -0
- package/default-themes/aurora-dawn.v3.css +198 -0
- package/default-themes/aurora-dusk.json +19 -0
- package/default-themes/aurora-dusk.v3.css +200 -0
- package/default-themes/cosmos-dawn.json +19 -0
- package/default-themes/cosmos-dawn.v3.css +198 -0
- package/default-themes/cosmos-dusk.json +19 -0
- package/default-themes/cosmos-dusk.v3.css +200 -0
- package/default-themes/high-contrast-dark.json +19 -0
- package/default-themes/high-contrast-dark.v3.css +200 -0
- package/default-themes/high-contrast-light.json +19 -0
- package/default-themes/high-contrast-light.v3.css +198 -0
- package/default-themes/nebula-dawn.v2.css +110 -0
- package/default-themes/nebula-dawn.v3.css +199 -0
- package/default-themes/nebula-dusk.v2.css +104 -0
- package/default-themes/nebula-dusk.v3.css +201 -0
- package/default-themes/solar-flare-dawn.json +19 -0
- package/default-themes/solar-flare-dawn.v3.css +198 -0
- package/default-themes/solar-flare-dusk.json +19 -0
- package/default-themes/solar-flare-dusk.v3.css +200 -0
- package/dist/agents/index.d.ts +1 -1
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +2 -1
- package/dist/agents/index.js.map +1 -1
- package/dist/agents/openclaw/gatewayManager.d.ts +4 -0
- package/dist/agents/openclaw/gatewayManager.d.ts.map +1 -1
- package/dist/agents/openclaw/gatewayManager.js +27 -11
- package/dist/agents/openclaw/gatewayManager.js.map +1 -1
- package/dist/agents/openclaw/openclawProvider.d.ts.map +1 -1
- package/dist/agents/openclaw/openclawProvider.js +2 -4
- package/dist/agents/openclaw/openclawProvider.js.map +1 -1
- package/dist/agents/openclaw/sshTunnelManager.d.ts +2 -0
- package/dist/agents/openclaw/sshTunnelManager.d.ts.map +1 -1
- package/dist/agents/openclaw/sshTunnelManager.js +31 -12
- package/dist/agents/openclaw/sshTunnelManager.js.map +1 -1
- package/dist/builders/anthropic.d.ts +31 -0
- package/dist/builders/anthropic.d.ts.map +1 -0
- package/dist/builders/anthropic.js +227 -0
- package/dist/builders/anthropic.js.map +1 -0
- package/dist/builders/fireworksai.d.ts +9 -0
- package/dist/builders/fireworksai.d.ts.map +1 -0
- package/dist/builders/fireworksai.js +57 -0
- package/dist/builders/fireworksai.js.map +1 -0
- package/dist/builders/index.d.ts +13 -0
- package/dist/builders/index.d.ts.map +1 -0
- package/dist/builders/index.js +31 -0
- package/dist/builders/index.js.map +1 -0
- package/dist/builders/openai.d.ts +8 -0
- package/dist/builders/openai.d.ts.map +1 -0
- package/dist/builders/openai.js +87 -0
- package/dist/builders/openai.js.map +1 -0
- package/dist/builders/types.d.ts +54 -0
- package/dist/builders/types.d.ts.map +1 -0
- package/dist/builders/types.js +211 -0
- package/dist/builders/types.js.map +1 -0
- package/dist/connectors/index.d.ts.map +1 -1
- package/dist/connectors/index.js +3 -2
- package/dist/connectors/index.js.map +1 -1
- package/dist/connectors/registry.d.ts +2 -1
- package/dist/connectors/registry.d.ts.map +1 -1
- package/dist/connectors/registry.js +31 -8
- package/dist/connectors/registry.js.map +1 -1
- package/dist/customizer/Customizer.d.ts +57 -0
- package/dist/customizer/Customizer.d.ts.map +1 -0
- package/dist/customizer/Customizer.js +124 -0
- package/dist/customizer/Customizer.js.map +1 -0
- package/dist/customizer/index.d.ts.map +1 -0
- package/dist/customizer/index.js +9 -0
- package/dist/customizer/index.js.map +1 -0
- package/dist/files.d.ts +16 -0
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +60 -1
- package/dist/files.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +10 -6
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +96 -113
- package/dist/init.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +23 -10
- package/dist/migrations.js.map +1 -1
- package/dist/models/anthropic.d.ts +4 -2
- package/dist/models/anthropic.d.ts.map +1 -1
- package/dist/models/anthropic.js +33 -6
- package/dist/models/anthropic.js.map +1 -1
- package/dist/models/fireworksai.d.ts.map +1 -1
- package/dist/models/fireworksai.js +9 -1
- package/dist/models/fireworksai.js.map +1 -1
- package/dist/models/index.d.ts +1 -1
- package/dist/models/index.d.ts.map +1 -1
- package/dist/models/index.js +2 -1
- package/dist/models/index.js.map +1 -1
- package/dist/models/openai.d.ts +1 -1
- package/dist/models/openai.d.ts.map +1 -1
- package/dist/models/openai.js +24 -3
- package/dist/models/openai.js.map +1 -1
- package/dist/models/types.d.ts +20 -1
- package/dist/models/types.d.ts.map +1 -1
- package/dist/models/types.js +6 -1
- package/dist/models/types.js.map +1 -1
- package/dist/pages.d.ts +30 -7
- package/dist/pages.d.ts.map +1 -1
- package/dist/pages.js +177 -55
- package/dist/pages.js.map +1 -1
- package/dist/service/server.d.ts.map +1 -1
- package/dist/service/server.js +37 -8
- package/dist/service/server.js.map +1 -1
- package/dist/service/transformPage.d.ts +47 -20
- package/dist/service/transformPage.d.ts.map +1 -1
- package/dist/service/transformPage.js +514 -293
- package/dist/service/transformPage.js.map +1 -1
- package/dist/service/useAgentRoutes.d.ts +2 -1
- package/dist/service/useAgentRoutes.d.ts.map +1 -1
- package/dist/service/useAgentRoutes.js +5 -2
- package/dist/service/useAgentRoutes.js.map +1 -1
- package/dist/service/useApiRoutes.d.ts.map +1 -1
- package/dist/service/useApiRoutes.js +237 -136
- package/dist/service/useApiRoutes.js.map +1 -1
- package/dist/service/useConnectorRoutes.js +6 -6
- package/dist/service/useConnectorRoutes.js.map +1 -1
- package/dist/service/useFileRoutes.d.ts +4 -0
- package/dist/service/useFileRoutes.d.ts.map +1 -0
- package/dist/service/useFileRoutes.js +122 -0
- package/dist/service/useFileRoutes.js.map +1 -0
- package/dist/service/usePageRoutes.d.ts.map +1 -1
- package/dist/service/usePageRoutes.js +648 -67
- package/dist/service/usePageRoutes.js.map +1 -1
- package/dist/service/useSharedDataRoutes.d.ts +4 -0
- package/dist/service/useSharedDataRoutes.d.ts.map +1 -0
- package/dist/service/useSharedDataRoutes.js +104 -0
- package/dist/service/useSharedDataRoutes.js.map +1 -0
- package/dist/service/useSharedFileRoutes.d.ts +4 -0
- package/dist/service/useSharedFileRoutes.d.ts.map +1 -0
- package/dist/service/useSharedFileRoutes.js +121 -0
- package/dist/service/useSharedFileRoutes.js.map +1 -0
- package/dist/settings.d.ts +1 -0
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +1 -0
- package/dist/settings.js.map +1 -1
- package/dist/synthos-cli.d.ts.map +1 -1
- package/dist/synthos-cli.js +4 -3
- package/dist/synthos-cli.js.map +1 -1
- package/dist/themes.d.ts +1 -0
- package/dist/themes.d.ts.map +1 -1
- package/dist/themes.js +28 -15
- package/dist/themes.js.map +1 -1
- package/migration-rules/v1-to-v2.md +193 -0
- package/migration-rules/v2-to-v3.md +481 -0
- package/package.json +11 -10
- package/required-pages/builder/page.html +43 -0
- package/required-pages/builder/page.json +10 -0
- package/required-pages/{pages.html → pages/page.html} +238 -233
- package/required-pages/pages/page.json +10 -0
- package/required-pages/{settings.html → settings/page.html} +389 -275
- package/required-pages/settings/page.json +10 -0
- package/required-pages/synthos_apis/page.html +846 -0
- package/required-pages/synthos_apis/page.json +10 -0
- package/required-pages/{synthos_scripts.html → synthos_scripts/page.html} +13 -11
- package/required-pages/synthos_scripts/page.json +10 -0
- package/src/agents/index.ts +1 -1
- package/src/agents/openclaw/gatewayManager.ts +22 -11
- package/src/agents/openclaw/openclawProvider.ts +2 -4
- package/src/agents/openclaw/sshTunnelManager.ts +19 -11
- package/src/builders/anthropic.ts +283 -0
- package/src/builders/fireworksai.ts +59 -0
- package/src/builders/index.ts +33 -0
- package/src/builders/openai.ts +89 -0
- package/src/builders/types.ts +261 -0
- package/src/connectors/index.ts +1 -1
- package/src/connectors/registry.ts +28 -8
- package/src/customizer/Customizer.ts +151 -0
- package/src/customizer/index.ts +5 -0
- package/src/files.ts +57 -0
- package/src/index.ts +2 -1
- package/src/init.ts +137 -123
- package/src/migrations.ts +30 -10
- package/src/models/anthropic.ts +40 -10
- package/src/models/fireworksai.ts +9 -2
- package/src/models/index.ts +1 -1
- package/src/models/openai.ts +26 -6
- package/src/models/types.ts +31 -1
- package/src/pages.ts +176 -54
- package/src/service/server.ts +36 -9
- package/src/service/transformPage.ts +557 -326
- package/src/service/useAgentRoutes.ts +7 -2
- package/src/service/useApiRoutes.ts +150 -41
- package/src/service/useConnectorRoutes.ts +7 -7
- package/src/service/useFileRoutes.ts +127 -0
- package/src/service/usePageRoutes.ts +720 -73
- package/src/service/useSharedDataRoutes.ts +106 -0
- package/src/service/useSharedFileRoutes.ts +126 -0
- package/src/settings.ts +2 -0
- package/src/synthos-cli.ts +4 -3
- package/src/themes.ts +25 -14
- package/static-files/favicon.svg +12 -0
- package/static-files/fluentlm-instructions.llmd +868 -0
- package/static-files/fluentlm-instructions.md +1595 -0
- package/static-files/fluentlm.css +4844 -0
- package/static-files/fluentlm.js +3602 -0
- package/static-files/fluentlm.min.css +1 -0
- package/static-files/fluentlm.min.js +1 -0
- package/{page-scripts/helpers-v2.js → static-files/helpers.v3.js} +82 -0
- package/static-files/page.v3.js +1290 -0
- package/static-files/recommended-frameworks.llmd +81 -0
- package/static-files/recommended-frameworks.md +137 -0
- package/static-files/retro-game.js +877 -0
- package/static-files/shell.css +797 -0
- package/static-files/theme-dark.css +169 -0
- package/static-files/theme-light.css +169 -0
- package/tests/builders.spec.ts +139 -0
- package/tests/pages.spec.ts +8 -8
- package/tests/transformPage.spec.ts +299 -360
- package/default-pages/application.html +0 -40
- package/default-pages/application.json +0 -1
- package/default-pages/json_tools.json +0 -1
- package/default-pages/my_notes.html +0 -33
- package/default-pages/neon_asteroids.html +0 -77
- package/default-pages/sidebar_page.json +0 -1
- package/default-pages/solar_tutorial.json +0 -1
- package/default-pages/two-panel_page.json +0 -1
- package/dist/agents/a2a/a2aProvider.d.ts +0 -3
- package/dist/agents/discovery.d.ts +0 -30
- package/dist/agents/openclaw/openclawProvider.d.ts +0 -3
- package/dist/agents/types.d.ts +0 -64
- package/dist/connectors/index.d.ts +0 -3
- package/dist/connectors/types.d.ts +0 -84
- package/dist/index.d.ts +0 -7
- package/dist/migrations.d.ts +0 -12
- package/dist/models/chainOfThought.d.ts +0 -12
- package/dist/models/fireworksai.d.ts +0 -30
- package/dist/models/logCompletePrompt.d.ts +0 -3
- package/dist/models/providers.d.ts +0 -8
- package/dist/models/utils.d.ts +0 -6
- package/dist/scripts.d.ts +0 -15
- package/dist/service/createCompletePrompt.d.ts +0 -5
- package/dist/service/debugLog.d.ts +0 -11
- package/dist/service/generateImage.d.ts +0 -32
- package/dist/service/index.d.ts +0 -8
- package/dist/service/modelInstructions.d.ts +0 -7
- package/dist/service/requiresSettings.d.ts +0 -3
- package/dist/service/server.d.ts +0 -4
- package/dist/service/useApiRoutes.d.ts +0 -4
- package/dist/service/useConnectorRoutes.d.ts +0 -4
- package/dist/service/useDataRoutes.d.ts +0 -4
- package/dist/service/useGatewayRoutes.d.ts +0 -4
- package/dist/service/useGatewayRoutes.d.ts.map +0 -1
- package/dist/service/useGatewayRoutes.js +0 -168
- package/dist/service/useGatewayRoutes.js.map +0 -1
- package/dist/service/usePageRoutes.d.ts +0 -5
- package/dist/synthos-cli.d.ts +0 -2
- package/page-scripts/page-v2.js +0 -656
- package/required-pages/builder.html +0 -48
- package/required-pages/builder.json +0 -1
- package/required-pages/pages.json +0 -1
- package/required-pages/settings.json +0 -1
- package/required-pages/synthos_apis.html +0 -327
- package/required-pages/synthos_apis.json +0 -1
- package/required-pages/synthos_scripts.json +0 -1
- package/src/connectors/airtable/connector.json +0 -27
- package/src/connectors/alpha-vantage/connector.json +0 -26
- package/src/connectors/brave-search/connector.json +0 -26
- package/src/connectors/cloudinary/connector.json +0 -27
- package/src/connectors/deepl/connector.json +0 -28
- package/src/connectors/elevenlabs/connector.json +0 -30
- package/src/connectors/giphy/connector.json +0 -27
- package/src/connectors/github/connector.json +0 -29
- package/src/connectors/huggingface/connector.json +0 -27
- package/src/connectors/imgur/connector.json +0 -29
- package/src/connectors/instagram/connector.json +0 -43
- package/src/connectors/jira/connector.json +0 -28
- package/src/connectors/mapbox/connector.json +0 -26
- package/src/connectors/nasa/connector.json +0 -27
- package/src/connectors/newsapi/connector.json +0 -27
- package/src/connectors/notion/connector.json +0 -28
- package/src/connectors/open-exchange-rates/connector.json +0 -27
- package/src/connectors/openweathermap/connector.json +0 -26
- package/src/connectors/pexels/connector.json +0 -27
- package/src/connectors/resend/connector.json +0 -29
- package/src/connectors/rss2json/connector.json +0 -27
- package/src/connectors/sendgrid/connector.json +0 -27
- package/src/connectors/spoonacular/connector.json +0 -28
- package/src/connectors/stability-ai/connector.json +0 -27
- package/src/connectors/twilio/connector.json +0 -28
- package/src/connectors/unsplash/connector.json +0 -27
- package/src/connectors/wolfram-alpha/connector.json +0 -26
- package/src/connectors/youtube-data/connector.json +0 -30
- /package/{dist/connectors → service-connectors}/airtable/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/alpha-vantage/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/brave-search/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/cloudinary/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/deepl/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/elevenlabs/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/giphy/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/github/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/huggingface/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/imgur/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/instagram/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/jira/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/mapbox/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/nasa/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/newsapi/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/notion/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/open-exchange-rates/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/openweathermap/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/pexels/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/resend/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/rss2json/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/sendgrid/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/spoonacular/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/stability-ai/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/twilio/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/unsplash/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/wolfram-alpha/connector.json +0 -0
- /package/{dist/connectors → service-connectors}/youtube-data/connector.json +0 -0
|
@@ -31,10 +31,10 @@ const SERVICE_REGISTRY = [
|
|
|
31
31
|
exclusive: 'search'
|
|
32
32
|
}
|
|
33
33
|
];
|
|
34
|
-
function useApiRoutes(config, app) {
|
|
34
|
+
function useApiRoutes(config, app, customizer) {
|
|
35
35
|
// List pages
|
|
36
36
|
app.get('/api/pages', async (req, res) => {
|
|
37
|
-
const pages = await (0, pages_1.listPages)(config.pagesFolder, config.
|
|
37
|
+
const pages = await (0, pages_1.listPages)(config.pagesFolder, config.requiredPagesFolders);
|
|
38
38
|
res.json(pages);
|
|
39
39
|
});
|
|
40
40
|
// Import a page from a zip file
|
|
@@ -122,7 +122,7 @@ function useApiRoutes(config, app) {
|
|
|
122
122
|
app.get('/api/pages/:name', async (req, res) => {
|
|
123
123
|
try {
|
|
124
124
|
const { name } = req.params;
|
|
125
|
-
const metadata = await (0, pages_1.loadPageMetadata)(config.pagesFolder, name, config.
|
|
125
|
+
const metadata = await (0, pages_1.loadPageMetadata)(config.pagesFolder, name, config.requiredPagesFolders);
|
|
126
126
|
if (metadata) {
|
|
127
127
|
res.json(metadata);
|
|
128
128
|
}
|
|
@@ -172,7 +172,7 @@ function useApiRoutes(config, app) {
|
|
|
172
172
|
return;
|
|
173
173
|
}
|
|
174
174
|
// Load existing metadata (or defaults)
|
|
175
|
-
const existing = await (0, pages_1.loadPageMetadata)(config.pagesFolder, name, config.
|
|
175
|
+
const existing = await (0, pages_1.loadPageMetadata)(config.pagesFolder, name, config.requiredPagesFolders);
|
|
176
176
|
const metadata = {
|
|
177
177
|
title: existing?.title ?? '',
|
|
178
178
|
categories: existing?.categories ?? [],
|
|
@@ -200,7 +200,12 @@ function useApiRoutes(config, app) {
|
|
|
200
200
|
if (metadata.mode !== 'locked') {
|
|
201
201
|
const userPagePath = path_1.default.join(config.pagesFolder, 'pages', name, 'page.html');
|
|
202
202
|
if (!(await (0, files_1.checkIfExists)(userPagePath))) {
|
|
203
|
-
|
|
203
|
+
let html;
|
|
204
|
+
for (const folder of config.requiredPagesFolders) {
|
|
205
|
+
html = await (0, pages_1.loadPageState)(folder, name);
|
|
206
|
+
if (html)
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
204
209
|
if (html) {
|
|
205
210
|
await (0, pages_1.savePageState)(config.pagesFolder, name, html);
|
|
206
211
|
}
|
|
@@ -224,7 +229,7 @@ function useApiRoutes(config, app) {
|
|
|
224
229
|
return;
|
|
225
230
|
}
|
|
226
231
|
// Load existing metadata (user override → fallback .json → defaults)
|
|
227
|
-
let metadata = await (0, pages_1.loadPageMetadata)(config.pagesFolder, name, config.
|
|
232
|
+
let metadata = await (0, pages_1.loadPageMetadata)(config.pagesFolder, name, config.requiredPagesFolders);
|
|
228
233
|
if (!metadata) {
|
|
229
234
|
metadata = {
|
|
230
235
|
title: '',
|
|
@@ -251,7 +256,7 @@ function useApiRoutes(config, app) {
|
|
|
251
256
|
try {
|
|
252
257
|
const { name } = req.params;
|
|
253
258
|
// Cannot delete required pages
|
|
254
|
-
if (
|
|
259
|
+
if (config.requiredPages.includes(name)) {
|
|
255
260
|
res.status(400).json({ error: `Cannot delete required page "${name}"` });
|
|
256
261
|
return;
|
|
257
262
|
}
|
|
@@ -271,11 +276,52 @@ function useApiRoutes(config, app) {
|
|
|
271
276
|
res.status(500).send(err.message);
|
|
272
277
|
}
|
|
273
278
|
});
|
|
279
|
+
// Discover what a page contains (tables + files)
|
|
280
|
+
app.get('/api/pages/:name/contents', async (req, res) => {
|
|
281
|
+
try {
|
|
282
|
+
const { name } = req.params;
|
|
283
|
+
// Resolve page folder: user pages first, then required pages
|
|
284
|
+
let pageFolder;
|
|
285
|
+
const userFolder = path_1.default.join(config.pagesFolder, 'pages', name);
|
|
286
|
+
if (await (0, files_1.checkIfExists)(path_1.default.join(userFolder, 'page.html'))) {
|
|
287
|
+
pageFolder = userFolder;
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
for (const folder of config.requiredPagesFolders) {
|
|
291
|
+
const candidate = path_1.default.join(folder, name);
|
|
292
|
+
if (await (0, files_1.checkIfExists)(path_1.default.join(candidate, 'page.html'))) {
|
|
293
|
+
pageFolder = candidate;
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
if (!pageFolder) {
|
|
299
|
+
res.status(404).json({ error: `Page "${name}" not found` });
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
// List subdirectories, filtering out non-table entries
|
|
303
|
+
const EXCLUDED = new Set(['files']);
|
|
304
|
+
const subdirs = await (0, files_1.listFolders)(pageFolder);
|
|
305
|
+
const tables = subdirs.filter(d => !EXCLUDED.has(d));
|
|
306
|
+
// Check if files/ exists and has entries
|
|
307
|
+
const filesDir = path_1.default.join(pageFolder, 'files');
|
|
308
|
+
let hasFiles = false;
|
|
309
|
+
if (await (0, files_1.checkIfExists)(filesDir)) {
|
|
310
|
+
const entries = await promises_1.default.readdir(filesDir);
|
|
311
|
+
hasFiles = entries.length > 0;
|
|
312
|
+
}
|
|
313
|
+
res.json({ tables, hasFiles });
|
|
314
|
+
}
|
|
315
|
+
catch (err) {
|
|
316
|
+
console.error(err);
|
|
317
|
+
res.status(500).json({ error: err.message });
|
|
318
|
+
}
|
|
319
|
+
});
|
|
274
320
|
// Copy a page to a new name
|
|
275
321
|
app.post('/api/pages/:name/copy', async (req, res) => {
|
|
276
322
|
try {
|
|
277
323
|
const sourceName = req.params.name;
|
|
278
|
-
const { name: targetName, title, categories } = req.body;
|
|
324
|
+
const { name: targetName, title, categories, copyTables, copyFiles } = req.body;
|
|
279
325
|
// Validate target name
|
|
280
326
|
if (!targetName || typeof targetName !== 'string') {
|
|
281
327
|
res.status(400).json({ error: 'name is required' });
|
|
@@ -293,10 +339,17 @@ function useApiRoutes(config, app) {
|
|
|
293
339
|
// Check source exists (user pages → required pages)
|
|
294
340
|
const sourceFolderPath = path_1.default.join(config.pagesFolder, 'pages', sourceName, 'page.html');
|
|
295
341
|
const sourceFlatPath = path_1.default.join(config.pagesFolder, `${sourceName}.html`);
|
|
296
|
-
|
|
342
|
+
let sourceRequiredPath;
|
|
343
|
+
for (const folder of config.requiredPagesFolders) {
|
|
344
|
+
const candidate = path_1.default.join(folder, sourceName, 'page.html');
|
|
345
|
+
if (await (0, files_1.checkIfExists)(candidate)) {
|
|
346
|
+
sourceRequiredPath = candidate;
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
297
350
|
const sourceExists = await (0, files_1.checkIfExists)(sourceFolderPath)
|
|
298
351
|
|| await (0, files_1.checkIfExists)(sourceFlatPath)
|
|
299
|
-
||
|
|
352
|
+
|| !!sourceRequiredPath;
|
|
300
353
|
if (!sourceExists) {
|
|
301
354
|
res.status(404).json({ error: `Source page "${sourceName}" not found` });
|
|
302
355
|
return;
|
|
@@ -308,7 +361,10 @@ function useApiRoutes(config, app) {
|
|
|
308
361
|
res.status(409).json({ error: `Page "${targetName}" already exists` });
|
|
309
362
|
return;
|
|
310
363
|
}
|
|
311
|
-
await (0, pages_1.copyPage)(config.pagesFolder, sourceName, targetName, typeof title === 'string' ? title : '', Array.isArray(categories) ? categories : [], config.
|
|
364
|
+
await (0, pages_1.copyPage)(config.pagesFolder, sourceName, targetName, typeof title === 'string' ? title : '', Array.isArray(categories) ? categories : [], config.requiredPagesFolders, {
|
|
365
|
+
copyTables: copyTables === true,
|
|
366
|
+
copyFiles: copyFiles !== false, // default true
|
|
367
|
+
});
|
|
312
368
|
// Return the new page metadata
|
|
313
369
|
const metadata = await (0, pages_1.loadPageMetadata)(config.pagesFolder, targetName);
|
|
314
370
|
res.status(201).json({ name: targetName, ...metadata });
|
|
@@ -380,15 +436,17 @@ function useApiRoutes(config, app) {
|
|
|
380
436
|
});
|
|
381
437
|
});
|
|
382
438
|
// Brainstorm endpoint
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
439
|
+
if (!customizer || customizer.isEnabled('brainstorm'))
|
|
440
|
+
app.post('/api/brainstorm', async (req, res) => {
|
|
441
|
+
await (0, requiresSettings_1.requiresSettings)(res, config.pagesFolder, async (settings) => {
|
|
442
|
+
const { context, messages } = req.body;
|
|
443
|
+
const completePrompt = await (0, createCompletePrompt_1.createCompletePrompt)(config.pagesFolder, 'chat');
|
|
444
|
+
const productName = customizer?.productName ?? 'SynthOS';
|
|
445
|
+
const system = {
|
|
446
|
+
role: 'system',
|
|
447
|
+
content: `You are a creative brainstorming assistant for ${productName}, a tool that builds pages through conversation.
|
|
448
|
+
${productName} is like a WIKI for vibe coding. Each page has a chat panel and a viewer panel. They are vibe coding what's displayed in that viewer panel. They can then save that as a page.
|
|
449
|
+
The user is brainstorming — exploring ideas before building. Be concise, creative, and collaborative.
|
|
392
450
|
They may say that they want to build an app or page that does XYZ but they're talking about what they expect to see in the viewer panel.
|
|
393
451
|
The goal is to help them generate a prompt for the builder that captures their vision, along with suggestions for next steps.
|
|
394
452
|
Suggest concrete approaches when you can, not complex visions for some ellaborate app.
|
|
@@ -399,74 +457,75 @@ ${context}
|
|
|
399
457
|
|
|
400
458
|
<INSTRUCTIONS>
|
|
401
459
|
Look at the <CHAT_HISTORY> and if it's empty it's the start of a new idea. Simply greet them and ask them what they're thinking of building. Suggestions could be help me decide, etc.
|
|
402
|
-
If you see a conversation between
|
|
460
|
+
If you see a conversation between ${productName} and the User. Asses what they're building and ask them what they'd like help with. Maybe offer a few good next steps.
|
|
403
461
|
|
|
404
|
-
|
|
462
|
+
${productName} exposes table storage and chat completion api's that every page can use. If the user wants to store something or use AI, your prompt should suggest using table storage or make llm calls.
|
|
405
463
|
|
|
406
464
|
You MUST return your response as a JSON object with exactly these fields:
|
|
407
465
|
{
|
|
408
466
|
"response": "Your conversational reply — explanations, options, suggestions. Markdown OK.",
|
|
409
|
-
"prompt": "A clean, actionable instruction ready to paste into
|
|
467
|
+
"prompt": "A clean, actionable instruction ready to paste into ${productName} chat to build what was discussed. Update this each exchange to reflect the latest brainstorm state.",
|
|
410
468
|
"suggestions": ["Short clickable option A", "Short clickable option B", "Short clickable option C"]
|
|
411
469
|
}
|
|
412
470
|
|
|
413
471
|
suggestions — 2-4 short phrases the user can click to continue the conversation. These are next-step options: directions to explore, questions to answer, or choices to make. Keep each under 60 characters. Always provide suggestions.
|
|
414
472
|
|
|
415
473
|
Return ONLY the JSON object.`
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
474
|
+
};
|
|
475
|
+
// Format multi-turn conversation into a single prompt
|
|
476
|
+
const formatted = messages.map(m => `${m.role === 'user' ? 'User' : 'Assistant'}: ${m.content}`).join('\n\n');
|
|
477
|
+
const prompt = { role: 'user', content: formatted };
|
|
478
|
+
const result = await completePrompt({ prompt, system, jsonMode: true });
|
|
479
|
+
if (result.completed) {
|
|
480
|
+
let response = result.value || '';
|
|
481
|
+
let brainstormPrompt = '';
|
|
482
|
+
let suggestions = [];
|
|
483
|
+
// jsonMode returns an already-parsed object from agentm-core
|
|
484
|
+
const parsed = (typeof result.value === 'object' && result.value !== null)
|
|
485
|
+
? result.value
|
|
486
|
+
: (() => { try {
|
|
487
|
+
return JSON.parse(result.value);
|
|
488
|
+
}
|
|
489
|
+
catch {
|
|
490
|
+
return null;
|
|
491
|
+
} })();
|
|
492
|
+
if (parsed) {
|
|
493
|
+
if (typeof parsed.response === 'string')
|
|
494
|
+
response = parsed.response;
|
|
495
|
+
if (typeof parsed.prompt === 'string')
|
|
496
|
+
brainstormPrompt = parsed.prompt;
|
|
497
|
+
if (Array.isArray(parsed.suggestions)) {
|
|
498
|
+
suggestions = parsed.suggestions.filter((s) => typeof s === 'string');
|
|
499
|
+
}
|
|
441
500
|
}
|
|
501
|
+
res.json({ response, prompt: brainstormPrompt, suggestions });
|
|
442
502
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
}
|
|
503
|
+
else {
|
|
504
|
+
console.error(result.error);
|
|
505
|
+
res.status(500).send(result.error?.message);
|
|
506
|
+
}
|
|
507
|
+
});
|
|
449
508
|
});
|
|
450
|
-
});
|
|
451
509
|
// Define a route for running configured scripts
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
510
|
+
if (!customizer || customizer.isEnabled('scripts'))
|
|
511
|
+
app.post('/api/scripts/:id', async (req, res) => {
|
|
512
|
+
await (0, requiresSettings_1.requiresSettings)(res, config.pagesFolder, async (settings) => {
|
|
513
|
+
const { id } = req.params;
|
|
514
|
+
const pagesFolder = config.pagesFolder;
|
|
515
|
+
const scriptId = id;
|
|
516
|
+
const response = await (0, scripts_1.executeScript)({ pagesFolder, scriptId, variables: req.body });
|
|
517
|
+
if (response.completed) {
|
|
518
|
+
// Return the response as text
|
|
519
|
+
const value = (response.value?.output ?? (response.value?.errors ?? []).join('\n')).trim();
|
|
520
|
+
res.set('Content-Type', 'text/plain');
|
|
521
|
+
res.send(value);
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
console.error(response.error);
|
|
525
|
+
res.status(500).send(response.error);
|
|
526
|
+
}
|
|
527
|
+
});
|
|
468
528
|
});
|
|
469
|
-
});
|
|
470
529
|
// Return theme info as a self-executing JS script
|
|
471
530
|
app.get('/api/theme-info.js', async (req, res) => {
|
|
472
531
|
try {
|
|
@@ -477,7 +536,13 @@ Return ONLY the JSON object.`
|
|
|
477
536
|
res.status(404).send(`// Theme info for "${themeName}" not found`);
|
|
478
537
|
return;
|
|
479
538
|
}
|
|
480
|
-
const
|
|
539
|
+
const themeVersion = await (0, themes_1.loadThemeVersion)(themeName, config);
|
|
540
|
+
const payload = { ...info, name: themeName, version: themeVersion };
|
|
541
|
+
let js = `window.themeInfo=${JSON.stringify(payload)};document.documentElement.classList.add(window.themeInfo.mode+"-mode");`;
|
|
542
|
+
if (themeVersion >= 3) {
|
|
543
|
+
js += `document.documentElement.classList.add(${JSON.stringify(themeName)});`;
|
|
544
|
+
}
|
|
545
|
+
js += `document.documentElement.setAttribute("data-toolbar",${JSON.stringify(settings.toolbarPosition || 'left')});`;
|
|
481
546
|
res.set('Content-Type', 'application/javascript');
|
|
482
547
|
res.send(js);
|
|
483
548
|
}
|
|
@@ -494,21 +559,17 @@ Return ONLY the JSON object.`
|
|
|
494
559
|
res.status(400).send('// Missing page query parameter');
|
|
495
560
|
return;
|
|
496
561
|
}
|
|
497
|
-
const metadata = await (0, pages_1.loadPageMetadata)(config.pagesFolder, page, config.
|
|
562
|
+
const metadata = await (0, pages_1.loadPageMetadata)(config.pagesFolder, page, config.requiredPagesFolders);
|
|
498
563
|
const mode = metadata?.mode ?? 'unlocked';
|
|
499
564
|
const title = metadata?.title ?? '';
|
|
500
565
|
const categories = metadata?.categories ?? [];
|
|
501
|
-
const
|
|
566
|
+
const isRequiredPage = config.requiredPages.includes(page);
|
|
567
|
+
const info = JSON.stringify({ name: page, mode, latestPageVersion: pages_1.PAGE_VERSION, title, categories, isRequiredPage });
|
|
502
568
|
const js = [
|
|
503
569
|
`window.pageInfo=${info};`,
|
|
504
570
|
`if(window.pageInfo.mode==="locked"){`,
|
|
505
571
|
`document.addEventListener("DOMContentLoaded",function(){`,
|
|
506
572
|
`var f=document.getElementById("chatForm");if(f)f.style.display="none";`,
|
|
507
|
-
`var s=document.getElementById("saveLink");if(s)s.textContent="Copy";`,
|
|
508
|
-
`var r=document.getElementById("resetLink");if(r){`,
|
|
509
|
-
`var c=r.cloneNode(true);c.textContent="Reload";`,
|
|
510
|
-
`c.addEventListener("click",function(e){e.preventDefault();window.location.href=window.location.pathname;});`,
|
|
511
|
-
`r.parentNode.replaceChild(c,r);}`,
|
|
512
573
|
`});`,
|
|
513
574
|
`}`,
|
|
514
575
|
].join('');
|
|
@@ -558,8 +619,8 @@ Return ONLY the JSON object.`
|
|
|
558
619
|
res.status(400).send('// Invalid version parameter');
|
|
559
620
|
return;
|
|
560
621
|
}
|
|
561
|
-
const scriptPath =
|
|
562
|
-
if (!
|
|
622
|
+
const scriptPath = await (0, files_1.findFileInFolders)(config.staticFilesFolders, `page.v${v}.js`);
|
|
623
|
+
if (!scriptPath) {
|
|
563
624
|
res.status(404).send(`// page-v${v}.js not found`);
|
|
564
625
|
return;
|
|
565
626
|
}
|
|
@@ -581,8 +642,8 @@ Return ONLY the JSON object.`
|
|
|
581
642
|
res.status(400).send('// Invalid version parameter');
|
|
582
643
|
return;
|
|
583
644
|
}
|
|
584
|
-
const scriptPath =
|
|
585
|
-
if (!
|
|
645
|
+
const scriptPath = await (0, files_1.findFileInFolders)(config.staticFilesFolders, `helpers.v${v}.js`);
|
|
646
|
+
if (!scriptPath) {
|
|
586
647
|
res.status(404).send(`// helpers-v${v}.js not found`);
|
|
587
648
|
return;
|
|
588
649
|
}
|
|
@@ -657,57 +718,58 @@ Return ONLY the JSON object.`
|
|
|
657
718
|
// -----------------------------------------------------------------------
|
|
658
719
|
// Web Search (Brave Search API)
|
|
659
720
|
// -----------------------------------------------------------------------
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
const settings = await (0, settings_1.loadSettings)(config.pagesFolder);
|
|
668
|
-
const braveConfig = settings.connectors?.['brave-search'] ?? settings.services?.['brave-search'];
|
|
669
|
-
if (!braveConfig || !braveConfig.enabled || !braveConfig.apiKey) {
|
|
670
|
-
res.status(400).json({ error: 'Brave Search is not configured or not enabled. Add your API key in Settings > Services.' });
|
|
671
|
-
return;
|
|
672
|
-
}
|
|
673
|
-
const params = new URLSearchParams({ q: query });
|
|
674
|
-
if (count)
|
|
675
|
-
params.set('count', String(Math.min(Number(count) || 5, 20)));
|
|
676
|
-
if (country)
|
|
677
|
-
params.set('country', country);
|
|
678
|
-
if (freshness)
|
|
679
|
-
params.set('freshness', freshness);
|
|
680
|
-
const response = await fetch(`https://api.search.brave.com/res/v1/web/search?${params.toString()}`, {
|
|
681
|
-
headers: {
|
|
682
|
-
'Accept': 'application/json',
|
|
683
|
-
'Accept-Encoding': 'gzip',
|
|
684
|
-
'X-Subscription-Token': braveConfig.apiKey
|
|
721
|
+
if (!customizer || customizer.isEnabled('search'))
|
|
722
|
+
app.post('/api/search/web', async (req, res) => {
|
|
723
|
+
try {
|
|
724
|
+
const { query, count, country, freshness } = req.body;
|
|
725
|
+
if (!query || typeof query !== 'string') {
|
|
726
|
+
res.status(400).json({ error: 'query is required' });
|
|
727
|
+
return;
|
|
685
728
|
}
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
729
|
+
const settings = await (0, settings_1.loadSettings)(config.pagesFolder);
|
|
730
|
+
const braveConfig = settings.connectors?.['brave-search'] ?? settings.services?.['brave-search'];
|
|
731
|
+
if (!braveConfig || !braveConfig.enabled || !braveConfig.apiKey) {
|
|
732
|
+
res.status(400).json({ error: 'Brave Search is not configured or not enabled. Add your API key in Settings > Services.' });
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
const params = new URLSearchParams({ q: query });
|
|
736
|
+
if (count)
|
|
737
|
+
params.set('count', String(Math.min(Number(count) || 5, 20)));
|
|
738
|
+
if (country)
|
|
739
|
+
params.set('country', country);
|
|
740
|
+
if (freshness)
|
|
741
|
+
params.set('freshness', freshness);
|
|
742
|
+
const response = await fetch(`https://api.search.brave.com/res/v1/web/search?${params.toString()}`, {
|
|
743
|
+
headers: {
|
|
744
|
+
'Accept': 'application/json',
|
|
745
|
+
'Accept-Encoding': 'gzip',
|
|
746
|
+
'X-Subscription-Token': braveConfig.apiKey
|
|
747
|
+
}
|
|
748
|
+
});
|
|
749
|
+
if (!response.ok) {
|
|
750
|
+
const text = await response.text();
|
|
751
|
+
res.status(response.status).json({ error: `Brave Search API error: ${text}` });
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
const data = await response.json();
|
|
755
|
+
const results = (data.web?.results ?? []).map(r => ({
|
|
756
|
+
title: r.title,
|
|
757
|
+
url: r.url,
|
|
758
|
+
description: r.description
|
|
759
|
+
}));
|
|
760
|
+
res.json({ results });
|
|
761
|
+
}
|
|
762
|
+
catch (err) {
|
|
763
|
+
console.error(err);
|
|
764
|
+
res.status(500).json({ error: err.message });
|
|
691
765
|
}
|
|
692
|
-
|
|
693
|
-
const results = (data.web?.results ?? []).map(r => ({
|
|
694
|
-
title: r.title,
|
|
695
|
-
url: r.url,
|
|
696
|
-
description: r.description
|
|
697
|
-
}));
|
|
698
|
-
res.json({ results });
|
|
699
|
-
}
|
|
700
|
-
catch (err) {
|
|
701
|
-
console.error(err);
|
|
702
|
-
res.status(500).json({ error: err.message });
|
|
703
|
-
}
|
|
704
|
-
});
|
|
766
|
+
});
|
|
705
767
|
// Upgrade a page to the latest version
|
|
706
768
|
app.post('/api/pages/:name/upgrade', async (req, res) => {
|
|
707
769
|
try {
|
|
708
770
|
const { name } = req.params;
|
|
709
771
|
// Load current metadata
|
|
710
|
-
const metadata = await (0, pages_1.loadPageMetadata)(config.pagesFolder, name, config.
|
|
772
|
+
const metadata = await (0, pages_1.loadPageMetadata)(config.pagesFolder, name, config.requiredPagesFolders);
|
|
711
773
|
if (!metadata) {
|
|
712
774
|
res.status(404).json({ error: `Page "${name}" not found` });
|
|
713
775
|
return;
|
|
@@ -730,17 +792,19 @@ Return ONLY the JSON object.`
|
|
|
730
792
|
await (0, pages_1.savePageState)(config.pagesFolder, name, migratedHtml);
|
|
731
793
|
// Backup original page to .migrated/ before overwriting
|
|
732
794
|
const migratedFolder = path_1.default.join(config.pagesFolder, '.migrated');
|
|
733
|
-
// Handle legacy flat file (
|
|
795
|
+
// Handle legacy flat file (<localFolder>/pagename.html)
|
|
734
796
|
const flatPath = path_1.default.join(config.pagesFolder, `${name}.html`);
|
|
735
797
|
if (await (0, files_1.checkIfExists)(flatPath)) {
|
|
736
798
|
await (0, files_1.copyFile)(flatPath, migratedFolder);
|
|
737
799
|
await (0, files_1.deleteFile)(flatPath);
|
|
738
800
|
}
|
|
739
|
-
// Handle folder-based page (
|
|
801
|
+
// Handle folder-based page (<localFolder>/pages/name/)
|
|
740
802
|
const folderPath = path_1.default.join(config.pagesFolder, 'pages', name);
|
|
741
803
|
if (await (0, files_1.checkIfExists)(folderPath)) {
|
|
742
804
|
await (0, files_1.copyFolderRecursive)(folderPath, path_1.default.join(migratedFolder, name));
|
|
743
805
|
}
|
|
806
|
+
// Clear stale version files (undo snapshots from the old page version)
|
|
807
|
+
await (0, pages_1.clearVersions)(config.pagesFolder, name);
|
|
744
808
|
// Update metadata
|
|
745
809
|
metadata.pageVersion = pages_1.PAGE_VERSION;
|
|
746
810
|
metadata.lastModified = new Date().toISOString();
|
|
@@ -758,20 +822,26 @@ Return ONLY the JSON object.`
|
|
|
758
822
|
const { name } = req.params;
|
|
759
823
|
// Try user pages folder first, then required pages
|
|
760
824
|
const userPageDir = path_1.default.join(config.pagesFolder, 'pages', name);
|
|
761
|
-
|
|
825
|
+
let requiredPageDir;
|
|
826
|
+
for (const folder of config.requiredPagesFolders) {
|
|
827
|
+
if (await (0, files_1.checkIfExists)(path_1.default.join(folder, name, 'page.html'))) {
|
|
828
|
+
requiredPageDir = path_1.default.join(folder, name);
|
|
829
|
+
break;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
762
832
|
let sourceDir = null;
|
|
763
833
|
if (await (0, files_1.checkIfExists)(path_1.default.join(userPageDir, 'page.html'))) {
|
|
764
834
|
sourceDir = userPageDir;
|
|
765
835
|
}
|
|
766
|
-
else if (
|
|
836
|
+
else if (requiredPageDir) {
|
|
767
837
|
// For required pages, create a temp-like zip with just the HTML
|
|
768
838
|
const zip = new adm_zip_1.default();
|
|
769
|
-
const html = await (0, files_1.loadFile)(
|
|
839
|
+
const html = await (0, files_1.loadFile)(path_1.default.join(requiredPageDir, 'page.html'));
|
|
770
840
|
zip.addFile(`${name}/page.html`, Buffer.from(html, 'utf-8'));
|
|
771
841
|
// Include page.json if it exists
|
|
772
|
-
const
|
|
773
|
-
if (await (0, files_1.checkIfExists)(
|
|
774
|
-
const meta = await (0, files_1.loadFile)(
|
|
842
|
+
const metaPath = path_1.default.join(requiredPageDir, 'page.json');
|
|
843
|
+
if (await (0, files_1.checkIfExists)(metaPath)) {
|
|
844
|
+
const meta = await (0, files_1.loadFile)(metaPath);
|
|
775
845
|
zip.addFile(`${name}/page.json`, Buffer.from(meta, 'utf-8'));
|
|
776
846
|
}
|
|
777
847
|
const zipBuffer = zip.toBuffer();
|
|
@@ -797,6 +867,37 @@ Return ONLY the JSON object.`
|
|
|
797
867
|
res.status(500).json({ error: err.message });
|
|
798
868
|
}
|
|
799
869
|
});
|
|
870
|
+
// Ask a question about a page (with full page HTML context)
|
|
871
|
+
app.post('/api/pages/:name/ask', async (req, res) => {
|
|
872
|
+
await (0, requiresSettings_1.requiresSettings)(res, config.pagesFolder, async (settings) => {
|
|
873
|
+
const { name } = req.params;
|
|
874
|
+
const { question } = req.body;
|
|
875
|
+
if (typeof question !== 'string' || !question.trim()) {
|
|
876
|
+
res.status(400).json({ error: 'question is required' });
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
// Load the page HTML
|
|
880
|
+
const html = await (0, usePageRoutes_1.loadPageWithFallback)(name, config, false);
|
|
881
|
+
if (!html) {
|
|
882
|
+
res.status(404).json({ error: `Page "${name}" not found` });
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
// Create completion (uses 'chat' model, not 'builder')
|
|
886
|
+
const complete = await (0, createCompletePrompt_1.createCompletePrompt)(config.pagesFolder, 'chat', req.body.model);
|
|
887
|
+
const system = {
|
|
888
|
+
role: 'system',
|
|
889
|
+
content: `You are a helpful assistant. The user will ask questions about a web page. Answer based on the page content provided.\n\n<PAGE_HTML>\n${html}`
|
|
890
|
+
};
|
|
891
|
+
const prompt = { role: 'user', content: question };
|
|
892
|
+
const result = await complete({ system, prompt });
|
|
893
|
+
if (result.completed) {
|
|
894
|
+
res.json({ answer: result.value });
|
|
895
|
+
}
|
|
896
|
+
else {
|
|
897
|
+
res.status(500).json({ error: result.error?.message || 'Completion failed' });
|
|
898
|
+
}
|
|
899
|
+
});
|
|
900
|
+
});
|
|
800
901
|
}
|
|
801
902
|
exports.useApiRoutes = useApiRoutes;
|
|
802
903
|
//# sourceMappingURL=useApiRoutes.js.map
|