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.
- 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 +1803 -0
- package/default-pages/{neon_asteroids.json → neon_asteroids/page.json} +3 -3
- package/default-pages/{oregon_trail.html → oregon_trail/page.html} +16 -30
- 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} +15 -12
- 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 +1 -1
- 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 +62 -0
- package/dist/customizer/Customizer.d.ts.map +1 -0
- package/dist/customizer/Customizer.js +134 -0
- package/dist/customizer/Customizer.js.map +1 -0
- package/dist/customizer/index.d.ts +4 -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 +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +12 -6
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +150 -133
- 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 +34 -10
- package/dist/pages.d.ts.map +1 -1
- package/dist/pages.js +229 -79
- package/dist/pages.js.map +1 -1
- package/dist/service/createCompletePrompt.d.ts +2 -1
- package/dist/service/createCompletePrompt.d.ts.map +1 -1
- package/dist/service/createCompletePrompt.js +2 -2
- package/dist/service/createCompletePrompt.js.map +1 -1
- package/dist/service/requiresSettings.d.ts +2 -1
- package/dist/service/requiresSettings.d.ts.map +1 -1
- package/dist/service/requiresSettings.js +3 -3
- package/dist/service/requiresSettings.js.map +1 -1
- package/dist/service/server.d.ts +2 -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 +17 -14
- package/dist/service/useAgentRoutes.js.map +1 -1
- package/dist/service/useApiRoutes.d.ts +2 -1
- package/dist/service/useApiRoutes.d.ts.map +1 -1
- package/dist/service/useApiRoutes.js +287 -172
- package/dist/service/useApiRoutes.js.map +1 -1
- package/dist/service/useConnectorRoutes.js +17 -17
- package/dist/service/useConnectorRoutes.js.map +1 -1
- package/dist/service/useDataRoutes.d.ts.map +1 -1
- package/dist/service/useDataRoutes.js +13 -10
- package/dist/service/useDataRoutes.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 +2 -1
- package/dist/service/usePageRoutes.d.ts.map +1 -1
- package/dist/service/usePageRoutes.js +671 -74
- 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 +107 -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 +5 -3
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +12 -10
- package/dist/settings.js.map +1 -1
- package/dist/storage/FsStorageProvider.d.ts +25 -0
- package/dist/storage/FsStorageProvider.d.ts.map +1 -0
- package/dist/storage/FsStorageProvider.js +103 -0
- package/dist/storage/FsStorageProvider.js.map +1 -0
- package/dist/storage/StorageProvider.d.ts +31 -0
- package/dist/storage/StorageProvider.d.ts.map +1 -0
- package/dist/storage/StorageProvider.js +3 -0
- package/dist/storage/StorageProvider.js.map +1 -0
- package/dist/storage/index.d.ts +3 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +6 -0
- package/dist/storage/index.js.map +1 -0
- 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 +65 -28
- 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 +163 -0
- package/src/customizer/index.ts +5 -0
- package/src/files.ts +57 -0
- package/src/index.ts +3 -1
- package/src/init.ts +195 -145
- 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 +230 -77
- package/src/service/createCompletePrompt.ts +3 -2
- package/src/service/requiresSettings.ts +4 -3
- package/src/service/server.ts +36 -9
- package/src/service/transformPage.ts +557 -326
- package/src/service/useAgentRoutes.ts +19 -14
- package/src/service/useApiRoutes.ts +208 -84
- package/src/service/useConnectorRoutes.ts +18 -18
- package/src/service/useDataRoutes.ts +13 -10
- package/src/service/useFileRoutes.ts +128 -0
- package/src/service/usePageRoutes.ts +730 -81
- package/src/service/useSharedDataRoutes.ts +109 -0
- package/src/service/useSharedFileRoutes.ts +127 -0
- package/src/settings.ts +14 -10
- package/src/storage/FsStorageProvider.ts +87 -0
- package/src/storage/StorageProvider.ts +34 -0
- package/src/storage/index.ts +2 -0
- package/src/synthos-cli.ts +4 -3
- package/src/themes.ts +64 -27
- 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 +54 -84
- 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/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/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
|
@@ -5,7 +5,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.useApiRoutes = void 0;
|
|
7
7
|
const path_1 = __importDefault(require("path"));
|
|
8
|
-
const promises_1 = __importDefault(require("fs/promises"));
|
|
9
8
|
const adm_zip_1 = __importDefault(require("adm-zip"));
|
|
10
9
|
const pages_1 = require("../pages");
|
|
11
10
|
const files_1 = require("../files");
|
|
@@ -31,10 +30,11 @@ const SERVICE_REGISTRY = [
|
|
|
31
30
|
exclusive: 'search'
|
|
32
31
|
}
|
|
33
32
|
];
|
|
34
|
-
function useApiRoutes(config, app) {
|
|
33
|
+
function useApiRoutes(config, app, customizer) {
|
|
34
|
+
const sp = config.storageProvider;
|
|
35
35
|
// List pages
|
|
36
36
|
app.get('/api/pages', async (req, res) => {
|
|
37
|
-
const pages = await (0, pages_1.listPages)(config
|
|
37
|
+
const pages = await (0, pages_1.listPages)(config, config.requiredPagesFolders);
|
|
38
38
|
res.json(pages);
|
|
39
39
|
});
|
|
40
40
|
// Import a page from a zip file
|
|
@@ -75,12 +75,12 @@ function useApiRoutes(config, app) {
|
|
|
75
75
|
// Auto-append _1, _2, etc. on name conflicts
|
|
76
76
|
let finalName = pageName;
|
|
77
77
|
let suffix = 0;
|
|
78
|
-
while (await
|
|
78
|
+
while (await sp.checkIfExists(path_1.default.join(config.pagesFolder, 'pages', finalName))) {
|
|
79
79
|
suffix++;
|
|
80
80
|
finalName = `${pageName}_${suffix}`;
|
|
81
81
|
}
|
|
82
82
|
const targetDir = path_1.default.join(config.pagesFolder, 'pages', finalName);
|
|
83
|
-
await
|
|
83
|
+
await sp.ensureFolderExists(targetDir);
|
|
84
84
|
// Extract entries with path traversal protection
|
|
85
85
|
for (const entry of entries) {
|
|
86
86
|
if (entry.isDirectory)
|
|
@@ -94,12 +94,12 @@ function useApiRoutes(config, app) {
|
|
|
94
94
|
// Path traversal — skip this entry
|
|
95
95
|
continue;
|
|
96
96
|
}
|
|
97
|
-
await
|
|
98
|
-
await
|
|
97
|
+
await sp.ensureFolderExists(path_1.default.dirname(resolvedPath));
|
|
98
|
+
await sp.saveBuffer(resolvedPath, entry.getData());
|
|
99
99
|
}
|
|
100
100
|
// Update metadata: set createdDate and lastModified to now
|
|
101
101
|
const now = new Date().toISOString();
|
|
102
|
-
const existingMeta = await (0, pages_1.loadPageMetadata)(config
|
|
102
|
+
const existingMeta = await (0, pages_1.loadPageMetadata)(config, finalName);
|
|
103
103
|
const metadata = {
|
|
104
104
|
title: existingMeta?.title ?? '',
|
|
105
105
|
categories: existingMeta?.categories ?? [],
|
|
@@ -110,7 +110,7 @@ function useApiRoutes(config, app) {
|
|
|
110
110
|
pageVersion: existingMeta?.pageVersion ?? pages_1.PAGE_VERSION,
|
|
111
111
|
mode: existingMeta?.mode ?? 'unlocked',
|
|
112
112
|
};
|
|
113
|
-
await (0, pages_1.savePageMetadata)(config
|
|
113
|
+
await (0, pages_1.savePageMetadata)(config, finalName, metadata);
|
|
114
114
|
res.status(201).json({ name: finalName, title: metadata.title });
|
|
115
115
|
}
|
|
116
116
|
catch (err) {
|
|
@@ -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
|
|
125
|
+
const metadata = await (0, pages_1.loadPageMetadata)(config, 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
|
|
175
|
+
const existing = await (0, pages_1.loadPageMetadata)(config, name, config.requiredPagesFolders);
|
|
176
176
|
const metadata = {
|
|
177
177
|
title: existing?.title ?? '',
|
|
178
178
|
categories: existing?.categories ?? [],
|
|
@@ -199,14 +199,22 @@ function useApiRoutes(config, app) {
|
|
|
199
199
|
// Promote required page to user folder if being unlocked/designed
|
|
200
200
|
if (metadata.mode !== 'locked') {
|
|
201
201
|
const userPagePath = path_1.default.join(config.pagesFolder, 'pages', name, 'page.html');
|
|
202
|
-
if (!(await
|
|
203
|
-
|
|
202
|
+
if (!(await sp.checkIfExists(userPagePath))) {
|
|
203
|
+
// Read from required pages (package content, always local fs)
|
|
204
|
+
let html;
|
|
205
|
+
for (const folder of config.requiredPagesFolders) {
|
|
206
|
+
const candidate = path_1.default.join(folder, name, 'page.html');
|
|
207
|
+
if (await (0, files_1.checkIfExists)(candidate)) {
|
|
208
|
+
html = await (0, files_1.loadFile)(candidate);
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
204
212
|
if (html) {
|
|
205
|
-
await (0, pages_1.savePageState)(config
|
|
213
|
+
await (0, pages_1.savePageState)(config, name, html);
|
|
206
214
|
}
|
|
207
215
|
}
|
|
208
216
|
}
|
|
209
|
-
await (0, pages_1.savePageMetadata)(config
|
|
217
|
+
await (0, pages_1.savePageMetadata)(config, name, metadata);
|
|
210
218
|
res.json(metadata);
|
|
211
219
|
}
|
|
212
220
|
catch (err) {
|
|
@@ -224,7 +232,7 @@ function useApiRoutes(config, app) {
|
|
|
224
232
|
return;
|
|
225
233
|
}
|
|
226
234
|
// Load existing metadata (user override → fallback .json → defaults)
|
|
227
|
-
let metadata = await (0, pages_1.loadPageMetadata)(config
|
|
235
|
+
let metadata = await (0, pages_1.loadPageMetadata)(config, name, config.requiredPagesFolders);
|
|
228
236
|
if (!metadata) {
|
|
229
237
|
metadata = {
|
|
230
238
|
title: '',
|
|
@@ -238,7 +246,7 @@ function useApiRoutes(config, app) {
|
|
|
238
246
|
};
|
|
239
247
|
}
|
|
240
248
|
metadata.pinned = pinned;
|
|
241
|
-
await (0, pages_1.savePageMetadata)(config
|
|
249
|
+
await (0, pages_1.savePageMetadata)(config, name, metadata);
|
|
242
250
|
res.json(metadata);
|
|
243
251
|
}
|
|
244
252
|
catch (err) {
|
|
@@ -251,19 +259,19 @@ function useApiRoutes(config, app) {
|
|
|
251
259
|
try {
|
|
252
260
|
const { name } = req.params;
|
|
253
261
|
// Cannot delete required pages
|
|
254
|
-
if (
|
|
262
|
+
if (config.requiredPages.includes(name)) {
|
|
255
263
|
res.status(400).json({ error: `Cannot delete required page "${name}"` });
|
|
256
264
|
return;
|
|
257
265
|
}
|
|
258
266
|
// Check if page exists (folder-based or legacy flat file)
|
|
259
267
|
const folderPath = path_1.default.join(config.pagesFolder, 'pages', name, 'page.html');
|
|
260
268
|
const flatPath = path_1.default.join(config.pagesFolder, `${name}.html`);
|
|
261
|
-
const exists = await
|
|
269
|
+
const exists = await sp.checkIfExists(folderPath) || await sp.checkIfExists(flatPath);
|
|
262
270
|
if (!exists) {
|
|
263
271
|
res.status(404).json({ error: `Page "${name}" not found` });
|
|
264
272
|
return;
|
|
265
273
|
}
|
|
266
|
-
await (0, pages_1.deletePage)(config
|
|
274
|
+
await (0, pages_1.deletePage)(config, name);
|
|
267
275
|
res.json({ deleted: true });
|
|
268
276
|
}
|
|
269
277
|
catch (err) {
|
|
@@ -271,11 +279,61 @@ function useApiRoutes(config, app) {
|
|
|
271
279
|
res.status(500).send(err.message);
|
|
272
280
|
}
|
|
273
281
|
});
|
|
282
|
+
// Discover what a page contains (tables + files)
|
|
283
|
+
app.get('/api/pages/:name/contents', async (req, res) => {
|
|
284
|
+
try {
|
|
285
|
+
const { name } = req.params;
|
|
286
|
+
// Resolve page folder: user pages first, then required pages
|
|
287
|
+
let pageFolder;
|
|
288
|
+
const userFolder = path_1.default.join(config.pagesFolder, 'pages', name);
|
|
289
|
+
if (await sp.checkIfExists(path_1.default.join(userFolder, 'page.html'))) {
|
|
290
|
+
pageFolder = userFolder;
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
for (const folder of config.requiredPagesFolders) {
|
|
294
|
+
const candidate = path_1.default.join(folder, name);
|
|
295
|
+
if (await (0, files_1.checkIfExists)(path_1.default.join(candidate, 'page.html'))) {
|
|
296
|
+
pageFolder = candidate;
|
|
297
|
+
break;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (!pageFolder) {
|
|
302
|
+
res.status(404).json({ error: `Page "${name}" not found` });
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
// List subdirectories, filtering out non-table entries
|
|
306
|
+
const EXCLUDED = new Set(['files']);
|
|
307
|
+
// Use provider for user folder, local fs for required pages
|
|
308
|
+
const isUserFolder = pageFolder === userFolder;
|
|
309
|
+
const subdirs = isUserFolder
|
|
310
|
+
? await sp.listFolders(pageFolder)
|
|
311
|
+
: await (0, files_1.listFolders)(pageFolder);
|
|
312
|
+
const tables = subdirs.filter(d => !EXCLUDED.has(d));
|
|
313
|
+
// Check if files/ exists and has entries
|
|
314
|
+
const filesDir = path_1.default.join(pageFolder, 'files');
|
|
315
|
+
let hasFiles = false;
|
|
316
|
+
const filesDirExists = isUserFolder
|
|
317
|
+
? await sp.checkIfExists(filesDir)
|
|
318
|
+
: await (0, files_1.checkIfExists)(filesDir);
|
|
319
|
+
if (filesDirExists) {
|
|
320
|
+
const entries = isUserFolder
|
|
321
|
+
? await sp.listFiles(filesDir)
|
|
322
|
+
: await (0, files_1.listFiles)(filesDir);
|
|
323
|
+
hasFiles = entries.length > 0;
|
|
324
|
+
}
|
|
325
|
+
res.json({ tables, hasFiles });
|
|
326
|
+
}
|
|
327
|
+
catch (err) {
|
|
328
|
+
console.error(err);
|
|
329
|
+
res.status(500).json({ error: err.message });
|
|
330
|
+
}
|
|
331
|
+
});
|
|
274
332
|
// Copy a page to a new name
|
|
275
333
|
app.post('/api/pages/:name/copy', async (req, res) => {
|
|
276
334
|
try {
|
|
277
335
|
const sourceName = req.params.name;
|
|
278
|
-
const { name: targetName, title, categories } = req.body;
|
|
336
|
+
const { name: targetName, title, categories, copyTables, copyFiles } = req.body;
|
|
279
337
|
// Validate target name
|
|
280
338
|
if (!targetName || typeof targetName !== 'string') {
|
|
281
339
|
res.status(400).json({ error: 'name is required' });
|
|
@@ -293,10 +351,17 @@ function useApiRoutes(config, app) {
|
|
|
293
351
|
// Check source exists (user pages → required pages)
|
|
294
352
|
const sourceFolderPath = path_1.default.join(config.pagesFolder, 'pages', sourceName, 'page.html');
|
|
295
353
|
const sourceFlatPath = path_1.default.join(config.pagesFolder, `${sourceName}.html`);
|
|
296
|
-
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
354
|
+
let sourceRequiredPath;
|
|
355
|
+
for (const folder of config.requiredPagesFolders) {
|
|
356
|
+
const candidate = path_1.default.join(folder, sourceName, 'page.html');
|
|
357
|
+
if (await (0, files_1.checkIfExists)(candidate)) {
|
|
358
|
+
sourceRequiredPath = candidate;
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
const sourceExists = await sp.checkIfExists(sourceFolderPath)
|
|
363
|
+
|| await sp.checkIfExists(sourceFlatPath)
|
|
364
|
+
|| !!sourceRequiredPath;
|
|
300
365
|
if (!sourceExists) {
|
|
301
366
|
res.status(404).json({ error: `Source page "${sourceName}" not found` });
|
|
302
367
|
return;
|
|
@@ -304,13 +369,16 @@ function useApiRoutes(config, app) {
|
|
|
304
369
|
// Check target doesn't already exist
|
|
305
370
|
const targetFolderPath = path_1.default.join(config.pagesFolder, 'pages', targetName, 'page.html');
|
|
306
371
|
const targetFlatPath = path_1.default.join(config.pagesFolder, `${targetName}.html`);
|
|
307
|
-
if (await
|
|
372
|
+
if (await sp.checkIfExists(targetFolderPath) || await sp.checkIfExists(targetFlatPath)) {
|
|
308
373
|
res.status(409).json({ error: `Page "${targetName}" already exists` });
|
|
309
374
|
return;
|
|
310
375
|
}
|
|
311
|
-
await (0, pages_1.copyPage)(config
|
|
376
|
+
await (0, pages_1.copyPage)(config, sourceName, targetName, typeof title === 'string' ? title : '', Array.isArray(categories) ? categories : [], config.requiredPagesFolders, {
|
|
377
|
+
copyTables: copyTables === true,
|
|
378
|
+
copyFiles: copyFiles !== false, // default true
|
|
379
|
+
});
|
|
312
380
|
// Return the new page metadata
|
|
313
|
-
const metadata = await (0, pages_1.loadPageMetadata)(config
|
|
381
|
+
const metadata = await (0, pages_1.loadPageMetadata)(config, targetName);
|
|
314
382
|
res.status(201).json({ name: targetName, ...metadata });
|
|
315
383
|
}
|
|
316
384
|
catch (err) {
|
|
@@ -320,7 +388,7 @@ function useApiRoutes(config, app) {
|
|
|
320
388
|
});
|
|
321
389
|
// Define a route to return settings
|
|
322
390
|
app.get('/api/settings', async (req, res) => {
|
|
323
|
-
const settings = await (0, settings_1.loadSettings)(config
|
|
391
|
+
const settings = await (0, settings_1.loadSettings)(config);
|
|
324
392
|
const providers = createCompletePrompt_1.PROVIDERS.map(p => ({ name: p.name, builderModels: p.builderModels, chatModels: p.chatModels }));
|
|
325
393
|
res.json({ ...settings, providers });
|
|
326
394
|
});
|
|
@@ -339,7 +407,7 @@ function useApiRoutes(config, app) {
|
|
|
339
407
|
}
|
|
340
408
|
}
|
|
341
409
|
// Save settings
|
|
342
|
-
await (0, settings_1.saveSettings)(config
|
|
410
|
+
await (0, settings_1.saveSettings)(config, settings);
|
|
343
411
|
res.redirect('/builder');
|
|
344
412
|
}
|
|
345
413
|
catch (err) {
|
|
@@ -349,7 +417,7 @@ function useApiRoutes(config, app) {
|
|
|
349
417
|
});
|
|
350
418
|
// Define a route to generate an image
|
|
351
419
|
app.post('/api/generate/image', async (req, res) => {
|
|
352
|
-
await (0, requiresSettings_1.requiresSettings)(res, config
|
|
420
|
+
await (0, requiresSettings_1.requiresSettings)(res, config, async (settings) => {
|
|
353
421
|
const { prompt, shape, style } = req.body;
|
|
354
422
|
const builder = (0, settings_1.getModelEntry)(settings, 'builder');
|
|
355
423
|
const { configuration, imageQuality, provider } = builder;
|
|
@@ -366,9 +434,9 @@ function useApiRoutes(config, app) {
|
|
|
366
434
|
});
|
|
367
435
|
// Define a route to generate a completion using chain-of-thought
|
|
368
436
|
app.post('/api/generate/completion', async (req, res) => {
|
|
369
|
-
await (0, requiresSettings_1.requiresSettings)(res, config
|
|
437
|
+
await (0, requiresSettings_1.requiresSettings)(res, config, async (settings) => {
|
|
370
438
|
const { prompt, temperature } = req.body;
|
|
371
|
-
const completePrompt = await (0, createCompletePrompt_1.createCompletePrompt)(config
|
|
439
|
+
const completePrompt = await (0, createCompletePrompt_1.createCompletePrompt)(config, 'chat', req.body.model);
|
|
372
440
|
const response = await (0, models_1.chainOfThought)({ question: prompt, temperature, completePrompt });
|
|
373
441
|
if (response.completed) {
|
|
374
442
|
res.json(response.value ?? {});
|
|
@@ -380,15 +448,17 @@ function useApiRoutes(config, app) {
|
|
|
380
448
|
});
|
|
381
449
|
});
|
|
382
450
|
// Brainstorm endpoint
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
451
|
+
if (!customizer || customizer.isEnabled('brainstorm'))
|
|
452
|
+
app.post('/api/brainstorm', async (req, res) => {
|
|
453
|
+
await (0, requiresSettings_1.requiresSettings)(res, config, async (settings) => {
|
|
454
|
+
const { context, messages } = req.body;
|
|
455
|
+
const completePrompt = await (0, createCompletePrompt_1.createCompletePrompt)(config, 'chat');
|
|
456
|
+
const productName = customizer?.productName ?? 'SynthOS';
|
|
457
|
+
const system = {
|
|
458
|
+
role: 'system',
|
|
459
|
+
content: `You are a creative brainstorming assistant for ${productName}, a tool that builds pages through conversation.
|
|
460
|
+
${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.
|
|
461
|
+
The user is brainstorming — exploring ideas before building. Be concise, creative, and collaborative.
|
|
392
462
|
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
463
|
The goal is to help them generate a prompt for the builder that captures their vision, along with suggestions for next steps.
|
|
394
464
|
Suggest concrete approaches when you can, not complex visions for some ellaborate app.
|
|
@@ -399,85 +469,91 @@ ${context}
|
|
|
399
469
|
|
|
400
470
|
<INSTRUCTIONS>
|
|
401
471
|
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
|
|
472
|
+
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
473
|
|
|
404
|
-
|
|
474
|
+
${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
475
|
|
|
406
476
|
You MUST return your response as a JSON object with exactly these fields:
|
|
407
477
|
{
|
|
408
478
|
"response": "Your conversational reply — explanations, options, suggestions. Markdown OK.",
|
|
409
|
-
"prompt": "A clean, actionable instruction ready to paste into
|
|
479
|
+
"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
480
|
"suggestions": ["Short clickable option A", "Short clickable option B", "Short clickable option C"]
|
|
411
481
|
}
|
|
412
482
|
|
|
413
483
|
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
484
|
|
|
415
485
|
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
|
-
|
|
486
|
+
};
|
|
487
|
+
// Format multi-turn conversation into a single prompt
|
|
488
|
+
const formatted = messages.map(m => `${m.role === 'user' ? 'User' : 'Assistant'}: ${m.content}`).join('\n\n');
|
|
489
|
+
const prompt = { role: 'user', content: formatted };
|
|
490
|
+
const result = await completePrompt({ prompt, system, jsonMode: true });
|
|
491
|
+
if (result.completed) {
|
|
492
|
+
let response = result.value || '';
|
|
493
|
+
let brainstormPrompt = '';
|
|
494
|
+
let suggestions = [];
|
|
495
|
+
// jsonMode returns an already-parsed object from agentm-core
|
|
496
|
+
const parsed = (typeof result.value === 'object' && result.value !== null)
|
|
497
|
+
? result.value
|
|
498
|
+
: (() => { try {
|
|
499
|
+
return JSON.parse(result.value);
|
|
500
|
+
}
|
|
501
|
+
catch {
|
|
502
|
+
return null;
|
|
503
|
+
} })();
|
|
504
|
+
if (parsed) {
|
|
505
|
+
if (typeof parsed.response === 'string')
|
|
506
|
+
response = parsed.response;
|
|
507
|
+
if (typeof parsed.prompt === 'string')
|
|
508
|
+
brainstormPrompt = parsed.prompt;
|
|
509
|
+
if (Array.isArray(parsed.suggestions)) {
|
|
510
|
+
suggestions = parsed.suggestions.filter((s) => typeof s === 'string');
|
|
511
|
+
}
|
|
441
512
|
}
|
|
513
|
+
res.json({ response, prompt: brainstormPrompt, suggestions });
|
|
442
514
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
}
|
|
515
|
+
else {
|
|
516
|
+
console.error(result.error);
|
|
517
|
+
res.status(500).send(result.error?.message);
|
|
518
|
+
}
|
|
519
|
+
});
|
|
449
520
|
});
|
|
450
|
-
});
|
|
451
521
|
// Define a route for running configured scripts
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
522
|
+
if (!customizer || customizer.isEnabled('scripts'))
|
|
523
|
+
app.post('/api/scripts/:id', async (req, res) => {
|
|
524
|
+
await (0, requiresSettings_1.requiresSettings)(res, config, async (settings) => {
|
|
525
|
+
const { id } = req.params;
|
|
526
|
+
const scriptId = id;
|
|
527
|
+
const response = await (0, scripts_1.executeScript)({ pagesFolder: config.pagesFolder, scriptId, variables: req.body });
|
|
528
|
+
if (response.completed) {
|
|
529
|
+
// Return the response as text
|
|
530
|
+
const value = (response.value?.output ?? (response.value?.errors ?? []).join('\n')).trim();
|
|
531
|
+
res.set('Content-Type', 'text/plain');
|
|
532
|
+
res.send(value);
|
|
533
|
+
}
|
|
534
|
+
else {
|
|
535
|
+
console.error(response.error);
|
|
536
|
+
res.status(500).send(response.error);
|
|
537
|
+
}
|
|
538
|
+
});
|
|
468
539
|
});
|
|
469
|
-
});
|
|
470
540
|
// Return theme info as a self-executing JS script
|
|
471
541
|
app.get('/api/theme-info.js', async (req, res) => {
|
|
472
542
|
try {
|
|
473
|
-
const settings = await (0, settings_1.loadSettings)(config
|
|
543
|
+
const settings = await (0, settings_1.loadSettings)(config);
|
|
474
544
|
const themeName = settings.theme ?? 'nebula-dusk';
|
|
475
545
|
const info = await (0, themes_1.loadThemeInfo)(themeName, config);
|
|
476
546
|
if (!info) {
|
|
477
547
|
res.status(404).send(`// Theme info for "${themeName}" not found`);
|
|
478
548
|
return;
|
|
479
549
|
}
|
|
480
|
-
const
|
|
550
|
+
const themeVersion = await (0, themes_1.loadThemeVersion)(themeName, config);
|
|
551
|
+
const payload = { ...info, name: themeName, version: themeVersion };
|
|
552
|
+
let js = `window.themeInfo=${JSON.stringify(payload)};document.documentElement.classList.add(window.themeInfo.mode+"-mode");`;
|
|
553
|
+
if (themeVersion >= 3) {
|
|
554
|
+
js += `document.documentElement.classList.add(${JSON.stringify(themeName)});`;
|
|
555
|
+
}
|
|
556
|
+
js += `document.documentElement.setAttribute("data-toolbar",${JSON.stringify(settings.toolbarPosition || 'left')});`;
|
|
481
557
|
res.set('Content-Type', 'application/javascript');
|
|
482
558
|
res.send(js);
|
|
483
559
|
}
|
|
@@ -494,21 +570,18 @@ Return ONLY the JSON object.`
|
|
|
494
570
|
res.status(400).send('// Missing page query parameter');
|
|
495
571
|
return;
|
|
496
572
|
}
|
|
497
|
-
const metadata = await (0, pages_1.loadPageMetadata)(config
|
|
573
|
+
const metadata = await (0, pages_1.loadPageMetadata)(config, page, config.requiredPagesFolders);
|
|
498
574
|
const mode = metadata?.mode ?? 'unlocked';
|
|
499
575
|
const title = metadata?.title ?? '';
|
|
500
576
|
const categories = metadata?.categories ?? [];
|
|
501
|
-
const
|
|
577
|
+
const isRequiredPage = config.requiredPages.includes(page);
|
|
578
|
+
const productName = customizer?.productName ?? 'SynthOS';
|
|
579
|
+
const info = JSON.stringify({ name: page, mode, latestPageVersion: pages_1.PAGE_VERSION, title, categories, isRequiredPage, productName });
|
|
502
580
|
const js = [
|
|
503
581
|
`window.pageInfo=${info};`,
|
|
504
582
|
`if(window.pageInfo.mode==="locked"){`,
|
|
505
583
|
`document.addEventListener("DOMContentLoaded",function(){`,
|
|
506
584
|
`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
585
|
`});`,
|
|
513
586
|
`}`,
|
|
514
587
|
].join('');
|
|
@@ -524,7 +597,7 @@ Return ONLY the JSON object.`
|
|
|
524
597
|
// Return the current theme as CSS
|
|
525
598
|
app.get('/api/theme.css', async (req, res) => {
|
|
526
599
|
try {
|
|
527
|
-
const settings = await (0, settings_1.loadSettings)(config
|
|
600
|
+
const settings = await (0, settings_1.loadSettings)(config);
|
|
528
601
|
const themeName = settings.theme ?? 'nebula-dusk';
|
|
529
602
|
const css = await (0, themes_1.loadTheme)(themeName, config);
|
|
530
603
|
if (!css) {
|
|
@@ -558,8 +631,8 @@ Return ONLY the JSON object.`
|
|
|
558
631
|
res.status(400).send('// Invalid version parameter');
|
|
559
632
|
return;
|
|
560
633
|
}
|
|
561
|
-
const scriptPath =
|
|
562
|
-
if (!
|
|
634
|
+
const scriptPath = await (0, files_1.findFileInFolders)(config.staticFilesFolders, `page.v${v}.js`);
|
|
635
|
+
if (!scriptPath) {
|
|
563
636
|
res.status(404).send(`// page-v${v}.js not found`);
|
|
564
637
|
return;
|
|
565
638
|
}
|
|
@@ -581,8 +654,8 @@ Return ONLY the JSON object.`
|
|
|
581
654
|
res.status(400).send('// Invalid version parameter');
|
|
582
655
|
return;
|
|
583
656
|
}
|
|
584
|
-
const scriptPath =
|
|
585
|
-
if (!
|
|
657
|
+
const scriptPath = await (0, files_1.findFileInFolders)(config.staticFilesFolders, `helpers.v${v}.js`);
|
|
658
|
+
if (!scriptPath) {
|
|
586
659
|
res.status(404).send(`// helpers-v${v}.js not found`);
|
|
587
660
|
return;
|
|
588
661
|
}
|
|
@@ -606,7 +679,7 @@ Return ONLY the JSON object.`
|
|
|
606
679
|
// Return user's configured services (API keys masked)
|
|
607
680
|
app.get('/api/services', async (_req, res) => {
|
|
608
681
|
try {
|
|
609
|
-
const settings = await (0, settings_1.loadSettings)(config
|
|
682
|
+
const settings = await (0, settings_1.loadSettings)(config);
|
|
610
683
|
const services = settings.services ?? {};
|
|
611
684
|
const masked = {};
|
|
612
685
|
for (const [id, cfg] of Object.entries(services)) {
|
|
@@ -626,7 +699,7 @@ Return ONLY the JSON object.`
|
|
|
626
699
|
app.post('/api/services', async (req, res) => {
|
|
627
700
|
try {
|
|
628
701
|
const incoming = req.body;
|
|
629
|
-
const settings = await (0, settings_1.loadSettings)(config
|
|
702
|
+
const settings = await (0, settings_1.loadSettings)(config);
|
|
630
703
|
const existing = settings.services ?? {};
|
|
631
704
|
// Build merged config — empty apiKey means "keep existing"
|
|
632
705
|
const merged = {};
|
|
@@ -646,7 +719,7 @@ Return ONLY the JSON object.`
|
|
|
646
719
|
}
|
|
647
720
|
}
|
|
648
721
|
}
|
|
649
|
-
await (0, settings_1.saveSettings)(config
|
|
722
|
+
await (0, settings_1.saveSettings)(config, { services: merged });
|
|
650
723
|
res.json({ saved: true });
|
|
651
724
|
}
|
|
652
725
|
catch (err) {
|
|
@@ -657,57 +730,58 @@ Return ONLY the JSON object.`
|
|
|
657
730
|
// -----------------------------------------------------------------------
|
|
658
731
|
// Web Search (Brave Search API)
|
|
659
732
|
// -----------------------------------------------------------------------
|
|
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
|
|
733
|
+
if (!customizer || customizer.isEnabled('search'))
|
|
734
|
+
app.post('/api/search/web', async (req, res) => {
|
|
735
|
+
try {
|
|
736
|
+
const { query, count, country, freshness } = req.body;
|
|
737
|
+
if (!query || typeof query !== 'string') {
|
|
738
|
+
res.status(400).json({ error: 'query is required' });
|
|
739
|
+
return;
|
|
685
740
|
}
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
741
|
+
const settings = await (0, settings_1.loadSettings)(config);
|
|
742
|
+
const braveConfig = settings.connectors?.['brave-search'] ?? settings.services?.['brave-search'];
|
|
743
|
+
if (!braveConfig || !braveConfig.enabled || !braveConfig.apiKey) {
|
|
744
|
+
res.status(400).json({ error: 'Brave Search is not configured or not enabled. Add your API key in Settings > Services.' });
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
const params = new URLSearchParams({ q: query });
|
|
748
|
+
if (count)
|
|
749
|
+
params.set('count', String(Math.min(Number(count) || 5, 20)));
|
|
750
|
+
if (country)
|
|
751
|
+
params.set('country', country);
|
|
752
|
+
if (freshness)
|
|
753
|
+
params.set('freshness', freshness);
|
|
754
|
+
const response = await fetch(`https://api.search.brave.com/res/v1/web/search?${params.toString()}`, {
|
|
755
|
+
headers: {
|
|
756
|
+
'Accept': 'application/json',
|
|
757
|
+
'Accept-Encoding': 'gzip',
|
|
758
|
+
'X-Subscription-Token': braveConfig.apiKey
|
|
759
|
+
}
|
|
760
|
+
});
|
|
761
|
+
if (!response.ok) {
|
|
762
|
+
const text = await response.text();
|
|
763
|
+
res.status(response.status).json({ error: `Brave Search API error: ${text}` });
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
const data = await response.json();
|
|
767
|
+
const results = (data.web?.results ?? []).map(r => ({
|
|
768
|
+
title: r.title,
|
|
769
|
+
url: r.url,
|
|
770
|
+
description: r.description
|
|
771
|
+
}));
|
|
772
|
+
res.json({ results });
|
|
773
|
+
}
|
|
774
|
+
catch (err) {
|
|
775
|
+
console.error(err);
|
|
776
|
+
res.status(500).json({ error: err.message });
|
|
691
777
|
}
|
|
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
|
-
});
|
|
778
|
+
});
|
|
705
779
|
// Upgrade a page to the latest version
|
|
706
780
|
app.post('/api/pages/:name/upgrade', async (req, res) => {
|
|
707
781
|
try {
|
|
708
782
|
const { name } = req.params;
|
|
709
783
|
// Load current metadata
|
|
710
|
-
const metadata = await (0, pages_1.loadPageMetadata)(config
|
|
784
|
+
const metadata = await (0, pages_1.loadPageMetadata)(config, name, config.requiredPagesFolders);
|
|
711
785
|
if (!metadata) {
|
|
712
786
|
res.status(404).json({ error: `Page "${name}" not found` });
|
|
713
787
|
return;
|
|
@@ -724,27 +798,31 @@ Return ONLY the JSON object.`
|
|
|
724
798
|
return;
|
|
725
799
|
}
|
|
726
800
|
// Run LLM-based migration
|
|
727
|
-
const completePrompt = await (0, createCompletePrompt_1.createCompletePrompt)(config
|
|
801
|
+
const completePrompt = await (0, createCompletePrompt_1.createCompletePrompt)(config, 'builder');
|
|
728
802
|
const migratedHtml = await (0, migrations_1.migratePage)(html, currentVersion, pages_1.PAGE_VERSION, completePrompt);
|
|
729
803
|
// Save upgraded HTML to v2 folder structure
|
|
730
|
-
await (0, pages_1.savePageState)(config
|
|
804
|
+
await (0, pages_1.savePageState)(config, name, migratedHtml);
|
|
731
805
|
// Backup original page to .migrated/ before overwriting
|
|
732
806
|
const migratedFolder = path_1.default.join(config.pagesFolder, '.migrated');
|
|
733
|
-
// Handle legacy flat file (
|
|
807
|
+
// Handle legacy flat file (<localFolder>/pagename.html)
|
|
734
808
|
const flatPath = path_1.default.join(config.pagesFolder, `${name}.html`);
|
|
735
|
-
if (await
|
|
736
|
-
await
|
|
737
|
-
await
|
|
809
|
+
if (await sp.checkIfExists(flatPath)) {
|
|
810
|
+
await sp.ensureFolderExists(migratedFolder);
|
|
811
|
+
const flatData = await sp.loadBuffer(flatPath);
|
|
812
|
+
await sp.saveBuffer(path_1.default.join(migratedFolder, `${name}.html`), flatData);
|
|
813
|
+
await sp.deleteFile(flatPath);
|
|
738
814
|
}
|
|
739
|
-
// Handle folder-based page (
|
|
815
|
+
// Handle folder-based page (<localFolder>/pages/name/)
|
|
740
816
|
const folderPath = path_1.default.join(config.pagesFolder, 'pages', name);
|
|
741
|
-
if (await
|
|
742
|
-
await
|
|
817
|
+
if (await sp.checkIfExists(folderPath)) {
|
|
818
|
+
await sp.copyFolderRecursive(folderPath, path_1.default.join(migratedFolder, name));
|
|
743
819
|
}
|
|
820
|
+
// Clear stale version files (undo snapshots from the old page version)
|
|
821
|
+
await (0, pages_1.clearVersions)(config, name);
|
|
744
822
|
// Update metadata
|
|
745
823
|
metadata.pageVersion = pages_1.PAGE_VERSION;
|
|
746
824
|
metadata.lastModified = new Date().toISOString();
|
|
747
|
-
await (0, pages_1.savePageMetadata)(config
|
|
825
|
+
await (0, pages_1.savePageMetadata)(config, name, metadata);
|
|
748
826
|
res.json({ upgraded: true, fromVersion: currentVersion, toVersion: pages_1.PAGE_VERSION });
|
|
749
827
|
}
|
|
750
828
|
catch (err) {
|
|
@@ -758,20 +836,26 @@ Return ONLY the JSON object.`
|
|
|
758
836
|
const { name } = req.params;
|
|
759
837
|
// Try user pages folder first, then required pages
|
|
760
838
|
const userPageDir = path_1.default.join(config.pagesFolder, 'pages', name);
|
|
761
|
-
|
|
839
|
+
let requiredPageDir;
|
|
840
|
+
for (const folder of config.requiredPagesFolders) {
|
|
841
|
+
if (await (0, files_1.checkIfExists)(path_1.default.join(folder, name, 'page.html'))) { // package content, local fs
|
|
842
|
+
requiredPageDir = path_1.default.join(folder, name);
|
|
843
|
+
break;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
762
846
|
let sourceDir = null;
|
|
763
|
-
if (await
|
|
847
|
+
if (await sp.checkIfExists(path_1.default.join(userPageDir, 'page.html'))) {
|
|
764
848
|
sourceDir = userPageDir;
|
|
765
849
|
}
|
|
766
|
-
else if (
|
|
850
|
+
else if (requiredPageDir) {
|
|
767
851
|
// For required pages, create a temp-like zip with just the HTML
|
|
768
852
|
const zip = new adm_zip_1.default();
|
|
769
|
-
const html = await (0, files_1.loadFile)(
|
|
853
|
+
const html = await (0, files_1.loadFile)(path_1.default.join(requiredPageDir, 'page.html'));
|
|
770
854
|
zip.addFile(`${name}/page.html`, Buffer.from(html, 'utf-8'));
|
|
771
855
|
// Include page.json if it exists
|
|
772
|
-
const
|
|
773
|
-
if (await (0, files_1.checkIfExists)(
|
|
774
|
-
const meta = await (0, files_1.loadFile)(
|
|
856
|
+
const metaPath = path_1.default.join(requiredPageDir, 'page.json');
|
|
857
|
+
if (await (0, files_1.checkIfExists)(metaPath)) {
|
|
858
|
+
const meta = await (0, files_1.loadFile)(metaPath);
|
|
775
859
|
zip.addFile(`${name}/page.json`, Buffer.from(meta, 'utf-8'));
|
|
776
860
|
}
|
|
777
861
|
const zipBuffer = zip.toBuffer();
|
|
@@ -797,6 +881,37 @@ Return ONLY the JSON object.`
|
|
|
797
881
|
res.status(500).json({ error: err.message });
|
|
798
882
|
}
|
|
799
883
|
});
|
|
884
|
+
// Ask a question about a page (with full page HTML context)
|
|
885
|
+
app.post('/api/pages/:name/ask', async (req, res) => {
|
|
886
|
+
await (0, requiresSettings_1.requiresSettings)(res, config, async (settings) => {
|
|
887
|
+
const { name } = req.params;
|
|
888
|
+
const { question } = req.body;
|
|
889
|
+
if (typeof question !== 'string' || !question.trim()) {
|
|
890
|
+
res.status(400).json({ error: 'question is required' });
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
// Load the page HTML
|
|
894
|
+
const html = await (0, usePageRoutes_1.loadPageWithFallback)(name, config, false);
|
|
895
|
+
if (!html) {
|
|
896
|
+
res.status(404).json({ error: `Page "${name}" not found` });
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
// Create completion (uses 'chat' model, not 'builder')
|
|
900
|
+
const complete = await (0, createCompletePrompt_1.createCompletePrompt)(config, 'chat', req.body.model);
|
|
901
|
+
const system = {
|
|
902
|
+
role: 'system',
|
|
903
|
+
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}`
|
|
904
|
+
};
|
|
905
|
+
const prompt = { role: 'user', content: question };
|
|
906
|
+
const result = await complete({ system, prompt });
|
|
907
|
+
if (result.completed) {
|
|
908
|
+
res.json({ answer: result.value });
|
|
909
|
+
}
|
|
910
|
+
else {
|
|
911
|
+
res.status(500).json({ error: result.error?.message || 'Completion failed' });
|
|
912
|
+
}
|
|
913
|
+
});
|
|
914
|
+
});
|
|
800
915
|
}
|
|
801
916
|
exports.useApiRoutes = useApiRoutes;
|
|
802
917
|
//# sourceMappingURL=useApiRoutes.js.map
|