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
package/src/files.ts
CHANGED
|
@@ -72,3 +72,60 @@ export async function copyFolderRecursive(srcFolder: string, destFolder: string)
|
|
|
72
72
|
export async function deleteFolder(dirPath: string): Promise<void> {
|
|
73
73
|
await fs.rm(dirPath, { recursive: true });
|
|
74
74
|
}
|
|
75
|
+
|
|
76
|
+
// --- Multi-folder helpers ---
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Search folders in order, return the full path to the first existing match
|
|
80
|
+
* for the given filename.
|
|
81
|
+
*/
|
|
82
|
+
export async function findFileInFolders(folders: string[], filename: string): Promise<string | undefined> {
|
|
83
|
+
for (const folder of folders) {
|
|
84
|
+
const candidate = path.join(folder, filename);
|
|
85
|
+
if (await checkIfExists(candidate)) {
|
|
86
|
+
return candidate;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Merge file listings from multiple folders. First folder takes priority on
|
|
94
|
+
* name collisions (earlier occurrence wins).
|
|
95
|
+
*/
|
|
96
|
+
export async function listFilesFromFolders(folders: string[]): Promise<string[]> {
|
|
97
|
+
const seen = new Set<string>();
|
|
98
|
+
const result: string[] = [];
|
|
99
|
+
for (const folder of folders) {
|
|
100
|
+
if (!await checkIfExists(folder)) continue;
|
|
101
|
+
const files = await listFiles(folder);
|
|
102
|
+
for (const f of files) {
|
|
103
|
+
if (!seen.has(f)) {
|
|
104
|
+
seen.add(f);
|
|
105
|
+
result.push(f);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Copy files from multiple source folders into a single destination.
|
|
114
|
+
* First folder takes priority on duplicate filenames (copy is skipped
|
|
115
|
+
* if the file already exists in dest from an earlier folder).
|
|
116
|
+
*/
|
|
117
|
+
export async function copyFilesFromFolders(folders: string[], destFolder: string): Promise<void> {
|
|
118
|
+
await ensureFolderExists(destFolder);
|
|
119
|
+
const copied = new Set<string>();
|
|
120
|
+
for (const folder of folders) {
|
|
121
|
+
if (!await checkIfExists(folder)) continue;
|
|
122
|
+
const files = await fs.readdir(folder);
|
|
123
|
+
for (const file of files) {
|
|
124
|
+
if (copied.has(file)) continue;
|
|
125
|
+
copied.add(file);
|
|
126
|
+
const srcPath = path.join(folder, file);
|
|
127
|
+
const destPath = path.join(destFolder, file);
|
|
128
|
+
await fs.copyFile(srcPath, destPath);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
package/src/index.ts
CHANGED
package/src/init.ts
CHANGED
|
@@ -1,29 +1,67 @@
|
|
|
1
1
|
import * as fs from 'fs/promises';
|
|
2
2
|
import path from "path";
|
|
3
|
-
import { checkIfExists, copyFile,
|
|
4
|
-
import { PAGE_VERSION } from "./pages";
|
|
3
|
+
import { checkIfExists, copyFile, copyFolderRecursive, deleteFile, ensureFolderExists, findFileInFolders, listFiles, listFolders, saveFile } from "./files";
|
|
4
|
+
import { PAGE_VERSION, getRequiredPages } from "./pages";
|
|
5
5
|
import { DefaultSettings } from "./settings";
|
|
6
|
-
import {
|
|
6
|
+
import { Customizer } from './customizer';
|
|
7
7
|
|
|
8
8
|
export interface SynthOSConfig {
|
|
9
|
+
localFolder: string;
|
|
9
10
|
pagesFolder: string;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
requiredPagesFolders: string[];
|
|
12
|
+
defaultPagesFolders: string[];
|
|
13
|
+
defaultScriptsFolders: string[];
|
|
14
|
+
defaultThemesFolders: string[];
|
|
15
|
+
staticFilesFolders: string[];
|
|
16
|
+
serviceConnectorsFolders: string[];
|
|
17
|
+
requiredPages: string[];
|
|
15
18
|
debug: boolean;
|
|
16
19
|
debugPageUpdates: boolean;
|
|
17
20
|
}
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Resolve a folder array from a Customizer getter: replace the `'default'`
|
|
24
|
+
* sentinel with the built-in SynthOS path.
|
|
25
|
+
*/
|
|
26
|
+
function resolveFolders(folders: string[], builtInPath: string): string[] {
|
|
27
|
+
return folders.map(f => f === 'default' ? builtInPath : f);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function createConfig(
|
|
31
|
+
pagesFolder = '.synthos',
|
|
32
|
+
options?: { debug?: boolean; debugPageUpdates?: boolean },
|
|
33
|
+
customizer?: Customizer
|
|
34
|
+
): Promise<SynthOSConfig> {
|
|
35
|
+
const requiredPagesFolders = resolveFolders(
|
|
36
|
+
customizer?.requiredPagesFolders ?? ['default'],
|
|
37
|
+
path.join(__dirname, '../required-pages')
|
|
38
|
+
);
|
|
39
|
+
const requiredPages = await getRequiredPages(requiredPagesFolders);
|
|
20
40
|
return {
|
|
41
|
+
localFolder: pagesFolder,
|
|
21
42
|
pagesFolder: path.join(process.cwd(), pagesFolder),
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
43
|
+
requiredPagesFolders,
|
|
44
|
+
defaultPagesFolders: resolveFolders(
|
|
45
|
+
customizer?.defaultPagesFolders ?? ['default'],
|
|
46
|
+
path.join(__dirname, '../default-pages')
|
|
47
|
+
),
|
|
48
|
+
defaultScriptsFolders: resolveFolders(
|
|
49
|
+
customizer?.defaultScriptsFolders ?? ['default'],
|
|
50
|
+
path.join(__dirname, '../default-scripts')
|
|
51
|
+
),
|
|
52
|
+
defaultThemesFolders: resolveFolders(
|
|
53
|
+
customizer?.defaultThemesFolders ?? ['default'],
|
|
54
|
+
path.join(__dirname, '../default-themes')
|
|
55
|
+
),
|
|
56
|
+
staticFilesFolders: resolveFolders(
|
|
57
|
+
customizer?.staticFilesFolders ?? ['default'],
|
|
58
|
+
path.join(__dirname, '../static-files')
|
|
59
|
+
),
|
|
60
|
+
serviceConnectorsFolders: resolveFolders(
|
|
61
|
+
customizer?.serviceConnectorsFolders ?? ['default'],
|
|
62
|
+
path.join(__dirname, '../service-connectors')
|
|
63
|
+
),
|
|
64
|
+
requiredPages,
|
|
27
65
|
debug: options?.debug ?? false,
|
|
28
66
|
debugPageUpdates: options?.debugPageUpdates ?? false
|
|
29
67
|
};
|
|
@@ -36,7 +74,7 @@ export async function init(config: SynthOSConfig, includeDefaultPages: boolean =
|
|
|
36
74
|
return false;
|
|
37
75
|
}
|
|
38
76
|
|
|
39
|
-
console.log(`Initializing .
|
|
77
|
+
console.log(`Initializing ${config.localFolder} folder...`);
|
|
40
78
|
|
|
41
79
|
// Create pages folder
|
|
42
80
|
await ensureFolderExists(config.pagesFolder);
|
|
@@ -47,37 +85,29 @@ export async function init(config: SynthOSConfig, includeDefaultPages: boolean =
|
|
|
47
85
|
await saveFile(path.join(config.pagesFolder, 'settings.json.example'), JSON.stringify(DefaultSettings, null, 4));
|
|
48
86
|
|
|
49
87
|
// Setup default scripts
|
|
50
|
-
console.log(`Copying default scripts to .
|
|
88
|
+
console.log(`Copying default scripts to ${config.localFolder} folder...`);
|
|
51
89
|
const scriptsFolder = path.join(config.pagesFolder, 'scripts');
|
|
52
90
|
await ensureFolderExists(scriptsFolder);
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
await saveFile(path.join(scriptsFolder, 'example.sh'), '#!/bin/bash\n\n# This is an example script\n\n# You can run this script using the following command:\n# sh .synthos/scripts/example.sh\n\n# This script will print "Hello, World!" to the console\n\necho "Hello, World!"\n');
|
|
70
|
-
|
|
71
|
-
// Setup default themes
|
|
72
|
-
console.log(`Copying default themes to .synthos folder...`);
|
|
73
|
-
const themesFolder = path.join(config.pagesFolder, 'themes');
|
|
74
|
-
await ensureFolderExists(themesFolder);
|
|
75
|
-
await copyFiles(config.defaultThemesFolder, themesFolder);
|
|
91
|
+
const scriptFilename = ({
|
|
92
|
+
win32: 'windows-terminal.json',
|
|
93
|
+
darwin: 'mac-terminal.json',
|
|
94
|
+
android: 'android-terminal.json',
|
|
95
|
+
} as Record<string, string>)[process.platform] ?? 'linux-terminal.json';
|
|
96
|
+
const scriptSrc = await findFileInFolders(config.defaultScriptsFolders, scriptFilename);
|
|
97
|
+
if (scriptSrc) {
|
|
98
|
+
await copyFile(scriptSrc, scriptsFolder);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
await saveFile(path.join(scriptsFolder, 'example.sh'), `#!/bin/bash\n\n# This is an example script\n\n# You can run this script using the following command:\n# sh ${config.localFolder}/scripts/example.sh\n\n# This script will print "Hello, World!" to the console\n\necho "Hello, World!"\n`);
|
|
102
|
+
|
|
103
|
+
// Create empty themes folder — default themes are served directly from
|
|
104
|
+
// defaultThemesFolders; users can add custom themes here.
|
|
105
|
+
await ensureFolderExists(path.join(config.pagesFolder, 'themes'));
|
|
76
106
|
|
|
77
107
|
// Copy pages
|
|
78
108
|
if (includeDefaultPages) {
|
|
79
|
-
console.log(`Copying default pages to .
|
|
80
|
-
await copyDefaultPages(config.
|
|
109
|
+
console.log(`Copying default pages to ${config.localFolder} folder...`);
|
|
110
|
+
await copyDefaultPages(config.defaultPagesFolders, config.pagesFolder);
|
|
81
111
|
}
|
|
82
112
|
|
|
83
113
|
return true;
|
|
@@ -87,57 +117,23 @@ async function repairMissingFolders(config: SynthOSConfig): Promise<void> {
|
|
|
87
117
|
// Rebuild scripts folder from defaults if missing
|
|
88
118
|
const scriptsFolder = path.join(config.pagesFolder, 'scripts');
|
|
89
119
|
if (!await checkIfExists(scriptsFolder)) {
|
|
90
|
-
console.log(`Restoring default scripts to .
|
|
120
|
+
console.log(`Restoring default scripts to ${config.localFolder} folder...`);
|
|
91
121
|
await ensureFolderExists(scriptsFolder);
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
await copyFile(path.join(config.defaultScriptsFolder, 'android-terminal.json'), scriptsFolder);
|
|
101
|
-
break;
|
|
102
|
-
case 'linux':
|
|
103
|
-
default:
|
|
104
|
-
await copyFile(path.join(config.defaultScriptsFolder, 'linux-terminal.json'), scriptsFolder);
|
|
105
|
-
break;
|
|
122
|
+
const scriptFilename = ({
|
|
123
|
+
win32: 'windows-terminal.json',
|
|
124
|
+
darwin: 'mac-terminal.json',
|
|
125
|
+
android: 'android-terminal.json',
|
|
126
|
+
} as Record<string, string>)[process.platform] ?? 'linux-terminal.json';
|
|
127
|
+
const scriptSrc = await findFileInFolders(config.defaultScriptsFolders, scriptFilename);
|
|
128
|
+
if (scriptSrc) {
|
|
129
|
+
await copyFile(scriptSrc, scriptsFolder);
|
|
106
130
|
}
|
|
107
|
-
await saveFile(path.join(scriptsFolder, 'example.sh'),
|
|
131
|
+
await saveFile(path.join(scriptsFolder, 'example.sh'), `#!/bin/bash\n\n# This is an example script\n\n# You can run this script using the following command:\n# sh ${config.localFolder}/scripts/example.sh\n\n# This script will print "Hello, World!" to the console\n\necho "Hello, World!"\n`);
|
|
108
132
|
}
|
|
109
133
|
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
console.log(`Restoring default themes to .synthos folder...`);
|
|
114
|
-
await ensureFolderExists(themesFolder);
|
|
115
|
-
await copyFiles(config.defaultThemesFolder, themesFolder);
|
|
116
|
-
} else {
|
|
117
|
-
// Upgrade outdated themes — copy newer versioned CSS from defaults
|
|
118
|
-
const outdated = await getOutdatedThemes(config);
|
|
119
|
-
if (outdated.length > 0) {
|
|
120
|
-
console.log(`Upgrading ${outdated.length} theme(s): ${outdated.join(', ')}...`);
|
|
121
|
-
const defaultFiles = await listFiles(config.defaultThemesFolder);
|
|
122
|
-
for (const themeName of outdated) {
|
|
123
|
-
// Remove old versioned CSS files for this theme
|
|
124
|
-
const localFiles = await listFiles(themesFolder);
|
|
125
|
-
for (const f of localFiles) {
|
|
126
|
-
const parsed = parseThemeFilename(f);
|
|
127
|
-
if (parsed && parsed.name === themeName) {
|
|
128
|
-
await deleteFile(path.join(themesFolder, f));
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
// Copy the new versioned CSS from defaults
|
|
132
|
-
for (const f of defaultFiles) {
|
|
133
|
-
const parsed = parseThemeFilename(f);
|
|
134
|
-
if (parsed && parsed.name === themeName) {
|
|
135
|
-
await copyFile(path.join(config.defaultThemesFolder, f), themesFolder);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
134
|
+
// Ensure themes folder exists — default themes are served directly from
|
|
135
|
+
// defaultThemesFolders; this folder is for user-added custom themes only.
|
|
136
|
+
await ensureFolderExists(path.join(config.pagesFolder, 'themes'));
|
|
141
137
|
|
|
142
138
|
// Ensure pages/ subfolder exists
|
|
143
139
|
const pagesSubdir = path.join(config.pagesFolder, 'pages');
|
|
@@ -145,15 +141,15 @@ async function repairMissingFolders(config: SynthOSConfig): Promise<void> {
|
|
|
145
141
|
// No pages folder and no flat files — rebuild from defaults
|
|
146
142
|
const htmlFiles = (await listFiles(config.pagesFolder)).filter(f => f.endsWith('.html'));
|
|
147
143
|
if (htmlFiles.length === 0) {
|
|
148
|
-
console.log(`Restoring default pages to .
|
|
149
|
-
await copyDefaultPages(config.
|
|
144
|
+
console.log(`Restoring default pages to ${config.localFolder}/pages/ folder...`);
|
|
145
|
+
await copyDefaultPages(config.defaultPagesFolders, config.pagesFolder);
|
|
150
146
|
} else {
|
|
151
147
|
await ensureFolderExists(pagesSubdir);
|
|
152
148
|
}
|
|
153
149
|
}
|
|
154
150
|
|
|
155
151
|
// Migrate any stray flat .html files from root into pages/<name>/
|
|
156
|
-
await migrateFlatPages(config.pagesFolder);
|
|
152
|
+
await migrateFlatPages(config.pagesFolder, config.localFolder);
|
|
157
153
|
}
|
|
158
154
|
|
|
159
155
|
function toTitleCase(name: string): string {
|
|
@@ -164,12 +160,12 @@ function toTitleCase(name: string): string {
|
|
|
164
160
|
.replace(/\b\w/g, c => c.toUpperCase());
|
|
165
161
|
}
|
|
166
162
|
|
|
167
|
-
async function migrateFlatPages(pagesFolder: string): Promise<void> {
|
|
163
|
+
async function migrateFlatPages(pagesFolder: string, localFolder: string): Promise<void> {
|
|
168
164
|
const pagesSubdir = path.join(pagesFolder, 'pages');
|
|
169
165
|
const htmlFiles = (await listFiles(pagesFolder)).filter(f => f.endsWith('.html'));
|
|
170
166
|
if (htmlFiles.length === 0) return;
|
|
171
167
|
|
|
172
|
-
console.log(`Migrating ${htmlFiles.length} page(s) to
|
|
168
|
+
console.log(`Migrating ${htmlFiles.length} page(s) to ${localFolder}/pages/ folder...`);
|
|
173
169
|
await ensureFolderExists(pagesSubdir);
|
|
174
170
|
const now = new Date().toISOString();
|
|
175
171
|
|
|
@@ -200,40 +196,58 @@ async function migrateFlatPages(pagesFolder: string): Promise<void> {
|
|
|
200
196
|
}
|
|
201
197
|
}
|
|
202
198
|
|
|
203
|
-
async function copyDefaultPages(
|
|
199
|
+
async function copyDefaultPages(srcFolders: string[], destFolder: string): Promise<void> {
|
|
204
200
|
const pagesDir = path.join(destFolder, 'pages');
|
|
205
201
|
await ensureFolderExists(pagesDir);
|
|
206
|
-
const files = await fs.readdir(srcFolder);
|
|
207
202
|
const now = new Date().toISOString();
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
await
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
203
|
+
const seen = new Set<string>();
|
|
204
|
+
|
|
205
|
+
for (const srcFolder of srcFolders) {
|
|
206
|
+
if (!await checkIfExists(srcFolder)) continue;
|
|
207
|
+
const dirs = await listFolders(srcFolder);
|
|
208
|
+
for (const dir of dirs) {
|
|
209
|
+
const srcPageDir = path.join(srcFolder, dir);
|
|
210
|
+
if (!await checkIfExists(path.join(srcPageDir, 'page.html'))) continue;
|
|
211
|
+
if (seen.has(dir)) continue; // first folder wins
|
|
212
|
+
seen.add(dir);
|
|
213
|
+
|
|
214
|
+
const pageFolder = path.join(pagesDir, dir);
|
|
215
|
+
await ensureFolderExists(pageFolder);
|
|
216
|
+
await fs.copyFile(path.join(srcPageDir, 'page.html'), path.join(pageFolder, 'page.html'));
|
|
217
|
+
|
|
218
|
+
// Read companion page.json metadata from source folder, fall back to defaults
|
|
219
|
+
let metadata: Record<string, unknown> = {};
|
|
220
|
+
const jsonPath = path.join(srcPageDir, 'page.json');
|
|
221
|
+
if (await checkIfExists(jsonPath)) {
|
|
222
|
+
try {
|
|
223
|
+
const raw = await fs.readFile(jsonPath, 'utf-8');
|
|
224
|
+
metadata = JSON.parse(raw);
|
|
225
|
+
} catch {
|
|
226
|
+
// use defaults
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const fullMetadata = {
|
|
230
|
+
title: typeof metadata.title === 'string' ? metadata.title : '',
|
|
231
|
+
categories: Array.isArray(metadata.categories) ? metadata.categories : [],
|
|
232
|
+
pinned: typeof metadata.pinned === 'boolean' ? metadata.pinned : false,
|
|
233
|
+
showInAll: typeof metadata.showInAll === 'boolean' ? metadata.showInAll : true,
|
|
234
|
+
createdDate: now,
|
|
235
|
+
lastModified: now,
|
|
236
|
+
pageVersion: typeof metadata.pageVersion === 'number' ? metadata.pageVersion
|
|
237
|
+
: typeof metadata.uxVersion === 'number' ? metadata.uxVersion : PAGE_VERSION,
|
|
238
|
+
mode: metadata.mode === 'locked' ? 'locked' : 'unlocked',
|
|
239
|
+
};
|
|
240
|
+
await saveFile(path.join(pageFolder, 'page.json'), JSON.stringify(fullMetadata, null, 4));
|
|
241
|
+
|
|
242
|
+
// Copy data subfolders (anything that isn't page.html/page.json)
|
|
243
|
+
const subEntries = await fs.readdir(srcPageDir, { withFileTypes: true });
|
|
244
|
+
for (const entry of subEntries) {
|
|
245
|
+
if (!entry.isDirectory()) continue;
|
|
246
|
+
await copyFolderRecursive(
|
|
247
|
+
path.join(srcPageDir, entry.name),
|
|
248
|
+
path.join(pageFolder, entry.name)
|
|
249
|
+
);
|
|
224
250
|
}
|
|
225
251
|
}
|
|
226
|
-
const fullMetadata = {
|
|
227
|
-
title: typeof metadata.title === 'string' ? metadata.title : '',
|
|
228
|
-
categories: Array.isArray(metadata.categories) ? metadata.categories : [],
|
|
229
|
-
pinned: typeof metadata.pinned === 'boolean' ? metadata.pinned : false,
|
|
230
|
-
showInAll: typeof metadata.showInAll === 'boolean' ? metadata.showInAll : true,
|
|
231
|
-
createdDate: now,
|
|
232
|
-
lastModified: now,
|
|
233
|
-
pageVersion: typeof metadata.pageVersion === 'number' ? metadata.pageVersion
|
|
234
|
-
: typeof metadata.uxVersion === 'number' ? metadata.uxVersion : PAGE_VERSION,
|
|
235
|
-
mode: metadata.mode === 'locked' ? 'locked' : 'unlocked',
|
|
236
|
-
};
|
|
237
|
-
await saveFile(path.join(pageFolder, 'page.json'), JSON.stringify(fullMetadata, null, 4));
|
|
238
252
|
}
|
|
239
253
|
}
|
package/src/migrations.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { deduplicateInlineScripts } from './service/transformPage';
|
|
|
10
10
|
*/
|
|
11
11
|
const migrations: Record<number, (html: string, completePrompt: completePrompt) => Promise<string>> = {
|
|
12
12
|
1: migrateV1toV2,
|
|
13
|
+
2: migrateV2toV3,
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
/**
|
|
@@ -36,7 +37,6 @@ const SHARED_CSS_SELECTORS = [
|
|
|
36
37
|
'.chat-panel', '.chat-header', '.chat-messages',
|
|
37
38
|
'.chat-message', '.chat-message p', '.chat-message p strong', '.chat-message p code',
|
|
38
39
|
'.chat-message strong', '.chat-message pre', '.chat-message code', '.chat-message a',
|
|
39
|
-
'.link-group', '.link-group a', '.link-group a:hover',
|
|
40
40
|
'form',
|
|
41
41
|
'.chat-input', '.chat-input:focus', '.chat-input::placeholder', '.chat-input:disabled',
|
|
42
42
|
'.chat-submit', '.chat-submit:hover', '.chat-submit:active', '.chat-submit:disabled',
|
|
@@ -80,14 +80,8 @@ const DEFAULT_CHAT_PANEL = `
|
|
|
80
80
|
<div class="chat-messages" id="chatMessages">
|
|
81
81
|
<div class="chat-message"><p>Welcome! How can I help you?</p></div>
|
|
82
82
|
</div>
|
|
83
|
-
<div class="link-group">
|
|
84
|
-
<a href="#" id="saveLink">Save</a>
|
|
85
|
-
<a href="/pages" id="pagesLink">Pages</a>
|
|
86
|
-
<a href="#" id="resetLink">Reset</a>
|
|
87
|
-
</div>
|
|
88
83
|
<form action="/" method="POST" id="chatForm">
|
|
89
|
-
<
|
|
90
|
-
<button type="submit" class="chat-submit">Send</button>
|
|
84
|
+
<textarea class="chat-input" id="chatInput" name="message" rows="2" placeholder="Type a message..."></textarea>
|
|
91
85
|
</form>
|
|
92
86
|
</div>`;
|
|
93
87
|
|
|
@@ -125,6 +119,33 @@ async function migrateV1toV2(html: string, completePrompt: completePrompt): Prom
|
|
|
125
119
|
return migrated;
|
|
126
120
|
}
|
|
127
121
|
|
|
122
|
+
/**
|
|
123
|
+
* v2 -> v3: Cheerio-based migration (no LLM).
|
|
124
|
+
* - Removes .link-group div
|
|
125
|
+
* - Converts chat <input> to <textarea>
|
|
126
|
+
* - Removes .chat-submit button (v3 creates it dynamically)
|
|
127
|
+
*/
|
|
128
|
+
async function migrateV2toV3(html: string, _completePrompt: completePrompt): Promise<string> {
|
|
129
|
+
const $ = cheerio.load(html, { decodeEntities: false });
|
|
130
|
+
|
|
131
|
+
// 1. Remove .link-group div and its children
|
|
132
|
+
$('.link-group').remove();
|
|
133
|
+
|
|
134
|
+
// 2. Replace <input type="text" id="chatInput" ...> with <textarea>
|
|
135
|
+
const chatInput = $('input#chatInput, input.chat-input');
|
|
136
|
+
if (chatInput.length > 0) {
|
|
137
|
+
chatInput.replaceWith(
|
|
138
|
+
'<textarea class="chat-input" id="chatInput" name="message" rows="2" placeholder="Type a message..."></textarea>'
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// 3. Remove .chat-submit button inside #chatForm (or anywhere)
|
|
143
|
+
$('button.chat-submit').remove();
|
|
144
|
+
|
|
145
|
+
// Run through postProcessV2 to ensure structural integrity
|
|
146
|
+
return postProcessV2($.html());
|
|
147
|
+
}
|
|
148
|
+
|
|
128
149
|
/**
|
|
129
150
|
* Cheerio-based post-processing to verify the LLM output meets v2 requirements.
|
|
130
151
|
* Uses the original HTML as a fallback source for critical elements.
|
|
@@ -146,8 +167,7 @@ export function postProcessV2(html: string, originalHtml?: string): string {
|
|
|
146
167
|
// Append default form
|
|
147
168
|
$('.chat-panel').append(`
|
|
148
169
|
<form action="/" method="POST" id="chatForm">
|
|
149
|
-
<
|
|
150
|
-
<button type="submit" class="chat-submit">Send</button>
|
|
170
|
+
<textarea class="chat-input" id="chatInput" name="message" rows="2" placeholder="Type a message..."></textarea>
|
|
151
171
|
</form>`);
|
|
152
172
|
}
|
|
153
173
|
} else {
|
package/src/models/anthropic.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Anthropic from '@anthropic-ai/sdk';
|
|
2
|
-
import { AgentCompletion, completePrompt, PromptCompletionArgs, RequestError } from './types';
|
|
2
|
+
import { AgentCompletion, completePrompt, PromptCompletionArgs, RequestError, isMultimodalContent } from './types';
|
|
3
3
|
|
|
4
4
|
export interface AnthropicArgs {
|
|
5
5
|
apiKey: string;
|
|
@@ -14,25 +14,44 @@ export interface AnthropicArgs {
|
|
|
14
14
|
* Pure function — no SDK dependency.
|
|
15
15
|
*/
|
|
16
16
|
export function buildAnthropicRequest(args: PromptCompletionArgs, defaultTemp: number): {
|
|
17
|
-
messages: { role: string; content: string }[];
|
|
18
|
-
system: string | undefined;
|
|
17
|
+
messages: { role: string; content: string | Anthropic.ContentBlockParam[] }[];
|
|
18
|
+
system: string | Anthropic.TextBlockParam[] | undefined;
|
|
19
19
|
temperature: number;
|
|
20
|
+
outputConfig?: Anthropic.OutputConfig;
|
|
20
21
|
} {
|
|
21
22
|
const reqTemp = args.temperature ?? defaultTemp;
|
|
22
23
|
|
|
23
|
-
const messages: { role: string; content: string }[] = [];
|
|
24
|
+
const messages: { role: string; content: string | Anthropic.ContentBlockParam[] }[] = [];
|
|
24
25
|
if (args.history) {
|
|
25
26
|
for (const msg of args.history) {
|
|
26
27
|
messages.push({ role: msg.role, content: msg.content });
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
// Build user content — multimodal when ContentBlock[] is provided
|
|
32
|
+
const promptContent = args.prompt.content;
|
|
33
|
+
let userContent: string | Anthropic.ContentBlockParam[];
|
|
34
|
+
if (isMultimodalContent(promptContent)) {
|
|
35
|
+
userContent = promptContent.map(block => {
|
|
36
|
+
if (block.type === 'text') {
|
|
37
|
+
return { type: 'text' as const, text: block.text };
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
type: 'image' as const,
|
|
41
|
+
source: { type: 'base64' as const, media_type: block.mediaType as 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp', data: block.data },
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
} else {
|
|
45
|
+
userContent = promptContent;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Structured output via output_config is incompatible with prefilling
|
|
49
|
+
const useJsonPrefill = !args.outputSchema && (args.jsonMode || args.jsonSchema);
|
|
31
50
|
if (useJsonPrefill) {
|
|
32
|
-
messages.push({ role: 'user', content:
|
|
51
|
+
messages.push({ role: 'user', content: userContent });
|
|
33
52
|
messages.push({ role: 'assistant', content: '{' });
|
|
34
53
|
} else {
|
|
35
|
-
messages.push({ role: 'user', content:
|
|
54
|
+
messages.push({ role: 'user', content: userContent });
|
|
36
55
|
}
|
|
37
56
|
|
|
38
57
|
let system = args.system?.content;
|
|
@@ -41,7 +60,17 @@ export function buildAnthropicRequest(args: PromptCompletionArgs, defaultTemp: n
|
|
|
41
60
|
system = system ? system + schemaInstruction : schemaInstruction;
|
|
42
61
|
}
|
|
43
62
|
|
|
44
|
-
|
|
63
|
+
// Wrap system content with cache_control for prompt caching
|
|
64
|
+
const finalSystem: string | Anthropic.TextBlockParam[] | undefined = (system && args.cacheSystem)
|
|
65
|
+
? [{ type: 'text' as const, text: system, cache_control: { type: 'ephemeral' as const } }]
|
|
66
|
+
: system;
|
|
67
|
+
|
|
68
|
+
// Structured output config for constrained decoding
|
|
69
|
+
const outputConfig: Anthropic.OutputConfig | undefined = args.outputSchema
|
|
70
|
+
? { format: { type: 'json_schema', schema: args.outputSchema } }
|
|
71
|
+
: undefined;
|
|
72
|
+
|
|
73
|
+
return { messages, system: finalSystem, temperature: reqTemp, outputConfig };
|
|
45
74
|
}
|
|
46
75
|
|
|
47
76
|
export function anthropic(args: AnthropicArgs): completePrompt {
|
|
@@ -50,9 +79,9 @@ export function anthropic(args: AnthropicArgs): completePrompt {
|
|
|
50
79
|
const client = new Anthropic({ apiKey, baseURL, maxRetries });
|
|
51
80
|
|
|
52
81
|
return async (completionArgs: PromptCompletionArgs): Promise<AgentCompletion<string>> => {
|
|
53
|
-
const { messages, system: systemContent, temperature: reqTemp } = buildAnthropicRequest(completionArgs, temperature);
|
|
82
|
+
const { messages, system: systemContent, temperature: reqTemp, outputConfig } = buildAnthropicRequest(completionArgs, temperature);
|
|
54
83
|
|
|
55
|
-
const useJsonPrefill = completionArgs.jsonMode || completionArgs.jsonSchema;
|
|
84
|
+
const useJsonPrefill = !completionArgs.outputSchema && (completionArgs.jsonMode || completionArgs.jsonSchema);
|
|
56
85
|
|
|
57
86
|
try {
|
|
58
87
|
const stream = await client.messages.create({
|
|
@@ -62,6 +91,7 @@ export function anthropic(args: AnthropicArgs): completePrompt {
|
|
|
62
91
|
system: systemContent,
|
|
63
92
|
messages: messages as Anthropic.MessageParam[],
|
|
64
93
|
stream: true,
|
|
94
|
+
...(outputConfig && { output_config: outputConfig }),
|
|
65
95
|
});
|
|
66
96
|
|
|
67
97
|
let text = '';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
|
-
import { AgentCompletion, completePrompt, PromptCompletionArgs, RequestError } from './types';
|
|
2
|
+
import { AgentCompletion, completePrompt, PromptCompletionArgs, RequestError, isMultimodalContent } from './types';
|
|
3
3
|
|
|
4
4
|
export interface FireworksAIArgs {
|
|
5
5
|
apiKey: string;
|
|
@@ -83,7 +83,14 @@ export function buildFireworksRequest(args: PromptCompletionArgs, defaultTemp: n
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
// Strip images — FireworksAI has no vision support; keep text only
|
|
87
|
+
const promptContent = args.prompt.content;
|
|
88
|
+
let userContent: string;
|
|
89
|
+
if (isMultimodalContent(promptContent)) {
|
|
90
|
+
userContent = promptContent.filter(b => b.type === 'text').map(b => (b as { text: string }).text).join('\n');
|
|
91
|
+
} else {
|
|
92
|
+
userContent = promptContent;
|
|
93
|
+
}
|
|
87
94
|
if (useJson) {
|
|
88
95
|
userContent += '\n\nRespond with valid JSON only. No markdown fences.';
|
|
89
96
|
}
|
package/src/models/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { ProviderName, ProviderConfig, ModelEntry, Provider, SystemMessage, UserMessage, Message, AgentCompletion, completePrompt, PromptCompletionArgs, AgentArgs, RequestError } from './types';
|
|
1
|
+
export { ProviderName, ProviderConfig, ModelEntry, Provider, SystemMessage, UserMessage, Message, AgentCompletion, completePrompt, PromptCompletionArgs, AgentArgs, RequestError, TextBlock, ImageBlock, ContentBlock, MessageContent, isMultimodalContent } from './types';
|
|
2
2
|
export { AnthropicProvider, OpenAIProvider, PROVIDERS, getProvider, detectProvider } from './providers';
|
|
3
3
|
export { anthropic, AnthropicArgs, buildAnthropicRequest } from './anthropic';
|
|
4
4
|
export { openai, OpenaiArgs, buildOpenAIRequest } from './openai';
|