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
|
@@ -8,22 +8,24 @@
|
|
|
8
8
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js"></script>
|
|
9
9
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/14.1.1/marked.min.js"></script>
|
|
10
10
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/11.1.0/mermaid.min.js"></script>
|
|
11
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
|
|
11
12
|
<script id="page-info" src="/api/page-info.js?page=scripts"></script>
|
|
12
13
|
</head>
|
|
13
14
|
<body>
|
|
15
|
+
<div class="shell-toolbar" data-locked="true">
|
|
16
|
+
<button class="shell-toolbar-btn" id="builderToggle" aria-label="Page Builder" data-locked="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M7 18.5H6.2c-1.77 0-3.2-1.43-3.2-3.2V7.7C3 5.93 4.43 4.5 6.2 4.5h11.6c1.77 0 3.2 1.43 3.2 3.2v7.6c0 1.77-1.43 3.2-3.2 3.2H12l-4.2 3.2c-.5.38-1.2.02-1.2-.6V18.5Z" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><circle cx="8.5" cy="11.5" r="1" fill="currentColor"/><circle cx="12" cy="11.5" r="1" fill="currentColor"/><circle cx="15.5" cy="11.5" r="1" fill="currentColor"/></svg></button>
|
|
17
|
+
<button class="shell-toolbar-btn" id="pagesBtn" aria-label="View All Pages" data-locked="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none"><rect x="3" y="3" width="11" height="12" rx="1.5" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="M6 7.5h5M6 10h3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><rect x="18" y="3" width="11" height="12" rx="1.5" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="M21 7.5h5M21 10h3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><rect x="3" y="18" width="11" height="12" rx="1.5" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="M6 22.5h5M6 25h3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><rect x="18" y="18" width="11" height="12" rx="1.5" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="M21 22.5h5M21 25h3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg></button>
|
|
18
|
+
<button class="shell-toolbar-btn" id="saveBtn" aria-label="Save Page" data-locked="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2Z" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="M17 21v-8H7v8" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="M7 3v5h8" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/></svg></button>
|
|
19
|
+
<div class="shell-toolbar-spacer" data-locked="true"></div>
|
|
20
|
+
<button class="shell-toolbar-btn" id="settingsBtn" aria-label="Settings" data-locked="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z" stroke="currentColor" stroke-width="1.8"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1Z" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg></button>
|
|
21
|
+
</div>
|
|
14
22
|
<div class="chat-panel" data-locked="true">
|
|
15
|
-
<div class="chat-header" data-locked="true">
|
|
23
|
+
<div class="chat-header" data-locked="true"><span>Page Builder</span><button class="chat-header-close" id="builderClose" aria-label="Close builder" data-locked="true">×</button></div>
|
|
16
24
|
<div class="chat-messages" id="chatMessages" data-locked="true">
|
|
17
25
|
<div class="chat-message"><p><strong>SynthOS:</strong> Add or modify scripts that can be executed using the <code>/api/scripts/:id</code> API.</p></div>
|
|
18
|
-
</div>
|
|
19
|
-
<div class="link-group" data-locked="true">
|
|
20
|
-
<a href="#" id="saveLink" data-locked="true">Save</a>
|
|
21
|
-
<a href="/pages" id="pagesLink" data-locked="true">Pages</a>
|
|
22
|
-
<a href="#" id="resetLink" data-locked="true">Reset</a>
|
|
23
|
-
</div>
|
|
26
|
+
</div>
|
|
24
27
|
<form action="/" method="POST" id="chatForm" data-locked="true">
|
|
25
|
-
<
|
|
26
|
-
<button type="submit" class="chat-submit" data-locked="true">Send</button>
|
|
28
|
+
<textarea class="chat-input" id="chatInput" name="message" rows="2" placeholder="Type a message..." data-locked="true"></textarea>
|
|
27
29
|
</form>
|
|
28
30
|
</div>
|
|
29
31
|
<div class="viewer-panel" id="viewerPanel">
|
|
@@ -82,6 +84,6 @@
|
|
|
82
84
|
<div id="instructions" style="display: none;" data-locked="true"></div>
|
|
83
85
|
<div id="thoughts" style="display: none;" data-locked="true"></div>
|
|
84
86
|
<script id="script-editor">const scriptList=document.getElementById("scriptList"),scriptId=document.getElementById("scriptId"),scriptType=document.getElementById("scriptType"),scriptCommand=document.getElementById("scriptCommand"),scriptDescription=document.getElementById("scriptDescription"),scriptVariables=document.getElementById("scriptVariables"),addScriptBtn=document.querySelector(".add-script-btn"),saveScriptBtn=document.querySelector(".save-script-btn"),deleteScriptBtn=document.querySelector(".delete-script-btn"),placeholderMessage=document.getElementById("placeholderMessage"),commandError=document.getElementById("commandError");let isNewScript=!1;function showScriptDetails(){placeholderMessage.style.display="none",scriptId.style.display="block",scriptType.style.display="block",scriptCommand.style.display="block",scriptDescription.style.display="block",scriptVariables.style.display="block",saveScriptBtn.style.display="block",deleteScriptBtn.style.display=isNewScript?"none":"block"}function hideScriptDetails(){placeholderMessage.style.display="block",scriptId.style.display="none",scriptType.style.display="none",scriptCommand.style.display="none",scriptDescription.style.display="none",scriptVariables.style.display="none",saveScriptBtn.style.display="none",deleteScriptBtn.style.display="none",commandError.style.display="none"}function formatId(id){return id.toLowerCase().replace(/\s+/g,"-")}function loadScripts(){fetch("/api/data/scripts/scripts").then(response=>response.json()).then(scripts=>{scriptList.innerHTML="",scripts.forEach(script=>{const li=document.createElement("li");li.textContent=script.id,li.dataset.id=script.id,li.dataset.type=script.type,li.dataset.command=script.command,li.dataset.description=script.description||"",li.dataset.variables=script.variables||"",scriptList.appendChild(li)})}).catch(error=>console.error("Error loading scripts:",error))}function saveScript(script){fetch("/api/data/scripts/scripts").then(response=>response.json()).then(scripts=>{scripts.find(s=>s.id===script.id)&&isNewScript?confirm("A script with this ID already exists. Do you want to overwrite it?")&&performSave(script):performSave(script)}).catch(error=>console.error("Error checking existing scripts:",error))}function performSave(script){fetch("/api/data/scripts/scripts",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(script)}).then(response=>response.json()).then(savedScript=>{loadScripts(),isNewScript=!1,setTimeout(()=>{const newScriptElement=document.querySelector(`[data-id="${savedScript.id}"]`);newScriptElement&&newScriptElement.click()},100)}).catch(error=>console.error("Error saving script:",error))}function deleteScript(id){confirm("Are you sure you want to delete this script?")&&fetch(`/api/data/scripts/scripts/${id}`,{method:"DELETE"}).then(response=>response.json()).then(result=>{result.success?(loadScripts(),hideScriptDetails()):console.error("Failed to delete script.")}).catch(error=>console.error("Error deleting script:",error))}scriptList.addEventListener("click",e=>{"LI"===e.target.tagName&&(document.querySelectorAll(".script-list li").forEach(li=>li.classList.remove("active")),e.target.classList.add("active"),showScriptDetails(),scriptId.value=e.target.dataset.id,scriptType.value=e.target.dataset.type||"command",scriptCommand.value=e.target.dataset.command||"",scriptDescription.value=e.target.dataset.description||"",scriptVariables.value=e.target.dataset.variables||"",commandError.style.display="none",isNewScript=!1,deleteScriptBtn.style.display="block")}),addScriptBtn.addEventListener("click",()=>{const newId=formatId(`script-${Date.now()}`);showScriptDetails(),scriptId.value=newId,scriptType.value="command",scriptCommand.value="",scriptDescription.value="",scriptVariables.value="",commandError.style.display="none",document.querySelectorAll(".script-list li").forEach(li=>li.classList.remove("active")),isNewScript=!0,deleteScriptBtn.style.display="none"}),saveScriptBtn.addEventListener("click",()=>{scriptCommand.value.trim()?saveScript({id:formatId(scriptId.value),type:scriptType.value,command:scriptCommand.value,description:scriptDescription.value.trim()||void 0,variables:scriptVariables.value.trim()||void 0}):commandError.style.display="block"}),deleteScriptBtn.addEventListener("click",()=>{const id=scriptId.value;id&&deleteScript(id)}),scriptId.addEventListener("input",e=>{e.target.value=formatId(e.target.value)}),scriptCommand.addEventListener("input",()=>{commandError.style.display="none"}),loadScripts(),hideScriptDetails();</script>
|
|
85
|
-
<script id="page-helpers" src="/api/page-helpers.js?v=
|
|
86
|
-
<script id="page-script" src="/api/page-script.js?v=
|
|
87
|
+
<script id="page-helpers" src="/api/page-helpers.js?v=3" data-locked="true"></script>
|
|
88
|
+
<script id="page-script" src="/api/page-script.js?v=3" data-locked="true"></script>
|
|
87
89
|
</body></html>
|
package/src/agents/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { AgentConfig, AgentResponse, AgentEvent, AgentProvider, Attachment, ChatMessage } from './types';
|
|
2
2
|
export { a2aProvider } from './a2a/a2aProvider';
|
|
3
3
|
export { openclawProvider } from './openclaw/openclawProvider';
|
|
4
|
-
export { connectAgent, disconnectAgent, getAgentStatus } from './openclaw/gatewayManager';
|
|
4
|
+
export { connectAgent, disconnectAgent, getAgentStatus, setOpenClawDebug } from './openclaw/gatewayManager';
|
|
5
5
|
export { startTunnel, stopTunnel, getTunnelStatus } from './openclaw/sshTunnelManager';
|
|
6
6
|
export { discoverA2AAgent, discoverOpenClawAgent } from './discovery';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { startTunnel, stopTunnel, SshTunnelConfig } from './sshTunnelManager';
|
|
1
|
+
import { startTunnel, stopTunnel, setSshTunnelDebug, SshTunnelConfig } from './sshTunnelManager';
|
|
2
2
|
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
// Types
|
|
@@ -13,6 +13,7 @@ export interface GatewayConfig {
|
|
|
13
13
|
enabled: boolean;
|
|
14
14
|
role: 'operator';
|
|
15
15
|
scopes: string[];
|
|
16
|
+
productName?: string;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
interface PendingRequest {
|
|
@@ -51,6 +52,14 @@ const BASE_RECONNECT_DELAY_MS = 1_000;
|
|
|
51
52
|
// Module-level connection pool
|
|
52
53
|
// ---------------------------------------------------------------------------
|
|
53
54
|
|
|
55
|
+
let _debug = false;
|
|
56
|
+
|
|
57
|
+
/** Enable or disable OpenClaw + SSH tunnel debug logging. */
|
|
58
|
+
export function setOpenClawDebug(enabled: boolean): void {
|
|
59
|
+
_debug = enabled;
|
|
60
|
+
setSshTunnelDebug(enabled);
|
|
61
|
+
}
|
|
62
|
+
|
|
54
63
|
const connections = new Map<string, GatewayConnection>();
|
|
55
64
|
|
|
56
65
|
let _nextId = 1;
|
|
@@ -161,7 +170,7 @@ export async function connect(config: GatewayConfig): Promise<GatewayConnection>
|
|
|
161
170
|
ws.addEventListener('error', () => {
|
|
162
171
|
// The close event will fire after this, so we don't settle here.
|
|
163
172
|
// Just log for diagnostics.
|
|
164
|
-
console.error(`[OpenClaw] WebSocket error on gateway ${config.name}`);
|
|
173
|
+
if (_debug) console.error(`[OpenClaw] WebSocket error on gateway ${config.name}`);
|
|
165
174
|
});
|
|
166
175
|
} catch (err) {
|
|
167
176
|
settle(err instanceof Error ? err : new Error(String(err)));
|
|
@@ -241,7 +250,7 @@ export function request(conn: GatewayConnection, method: string, params?: unknow
|
|
|
241
250
|
msg.params = params;
|
|
242
251
|
}
|
|
243
252
|
|
|
244
|
-
console.log(`[OpenClaw] → req: ${id} ${method}`);
|
|
253
|
+
if (_debug) console.log(`[OpenClaw] → req: ${id} ${method}`);
|
|
245
254
|
conn.ws.send(JSON.stringify(msg));
|
|
246
255
|
});
|
|
247
256
|
}
|
|
@@ -290,6 +299,7 @@ export async function connectAgent(agent: {
|
|
|
290
299
|
url: string;
|
|
291
300
|
token: string;
|
|
292
301
|
sshTunnel?: { enabled: boolean; command: string; password: string };
|
|
302
|
+
productName?: string;
|
|
293
303
|
}): Promise<GatewayConnection> {
|
|
294
304
|
// Start SSH tunnel first if configured
|
|
295
305
|
if (agent.sshTunnel?.enabled && agent.sshTunnel.command) {
|
|
@@ -308,6 +318,7 @@ export async function connectAgent(agent: {
|
|
|
308
318
|
enabled: true,
|
|
309
319
|
role: 'operator',
|
|
310
320
|
scopes: ['operator.read', 'operator.write', 'operator.approvals'],
|
|
321
|
+
productName: agent.productName,
|
|
311
322
|
};
|
|
312
323
|
return connect(gwConfig);
|
|
313
324
|
}
|
|
@@ -366,7 +377,7 @@ function handleMessage(
|
|
|
366
377
|
// Skip noisy per-token agent events
|
|
367
378
|
if (eventName === 'agent') {
|
|
368
379
|
// Still dispatch to listeners below, just don't log
|
|
369
|
-
} else {
|
|
380
|
+
} else if (_debug) {
|
|
370
381
|
console.log(`[OpenClaw] ← event: ${eventName}`);
|
|
371
382
|
if (eventName === 'chat') {
|
|
372
383
|
console.log(`[OpenClaw] ← ${eventName} payload:\n${JSON.stringify(msg.payload, null, 2)}`);
|
|
@@ -383,7 +394,7 @@ function handleMessage(
|
|
|
383
394
|
if (listeners) {
|
|
384
395
|
for (const listener of listeners) {
|
|
385
396
|
try { listener(msg.payload); } catch (err) {
|
|
386
|
-
console.error(`[OpenClaw] Event listener error (${eventName}):`, err);
|
|
397
|
+
if (_debug) console.error(`[OpenClaw] Event listener error (${eventName}):`, err);
|
|
387
398
|
}
|
|
388
399
|
}
|
|
389
400
|
}
|
|
@@ -392,7 +403,7 @@ function handleMessage(
|
|
|
392
403
|
if (wildcardListeners) {
|
|
393
404
|
for (const listener of wildcardListeners) {
|
|
394
405
|
try { listener(msg); } catch (err) {
|
|
395
|
-
console.error(`[OpenClaw] Wildcard listener error:`, err);
|
|
406
|
+
if (_debug) console.error(`[OpenClaw] Wildcard listener error:`, err);
|
|
396
407
|
}
|
|
397
408
|
}
|
|
398
409
|
}
|
|
@@ -407,10 +418,10 @@ function handleMessage(
|
|
|
407
418
|
const error = msg.error as Record<string, unknown> | string | undefined;
|
|
408
419
|
|
|
409
420
|
const payloadType = (payload as Record<string, unknown>).type as string | undefined;
|
|
410
|
-
console.log(`[OpenClaw] ← res: ${id} ${ok ? (payloadType ?? 'ok') : 'ERROR'}`);
|
|
421
|
+
if (_debug) console.log(`[OpenClaw] ← res: ${id} ${ok ? (payloadType ?? 'ok') : 'ERROR'}`);
|
|
411
422
|
|
|
412
423
|
if (!ok || error) {
|
|
413
|
-
console.error(`[OpenClaw] ← error (id: ${id}):\n${JSON.stringify(msg, null, 2)}`);
|
|
424
|
+
if (_debug) console.error(`[OpenClaw] ← error (id: ${id}):\n${JSON.stringify(msg, null, 2)}`);
|
|
414
425
|
}
|
|
415
426
|
|
|
416
427
|
// Check if this is the hello-ok response to our connect request
|
|
@@ -502,7 +513,7 @@ function sendConnectRequest(conn: GatewayConnection): void {
|
|
|
502
513
|
permissions: {},
|
|
503
514
|
auth: { token: conn.config.token },
|
|
504
515
|
locale: 'en-US',
|
|
505
|
-
userAgent: 'SynthOS/1.0
|
|
516
|
+
userAgent: `${conn.config.productName ?? 'SynthOS'}/1.0`,
|
|
506
517
|
},
|
|
507
518
|
};
|
|
508
519
|
|
|
@@ -510,7 +521,7 @@ function sendConnectRequest(conn: GatewayConnection): void {
|
|
|
510
521
|
conn.pendingRequests.set(id, {
|
|
511
522
|
resolve: () => { /* handled in handleMessage via hello-ok check */ },
|
|
512
523
|
reject: (err: Error) => {
|
|
513
|
-
console.error(`[OpenClaw] Connect request failed for ${conn.config.name}: ${err.message}`);
|
|
524
|
+
if (_debug) console.error(`[OpenClaw] Connect request failed for ${conn.config.name}: ${err.message}`);
|
|
514
525
|
},
|
|
515
526
|
timer: setTimeout(() => {
|
|
516
527
|
conn.pendingRequests.delete(id);
|
|
@@ -553,7 +564,7 @@ function scheduleReconnect(conn: GatewayConnection): void {
|
|
|
553
564
|
try {
|
|
554
565
|
await connect(conn.config);
|
|
555
566
|
} catch (err) {
|
|
556
|
-
console.error(`[OpenClaw] Reconnect failed for ${conn.config.name}:`, err instanceof Error ? err.message : err);
|
|
567
|
+
if (_debug) console.error(`[OpenClaw] Reconnect failed for ${conn.config.name}:`, err instanceof Error ? err.message : err);
|
|
557
568
|
}
|
|
558
569
|
}, delay);
|
|
559
570
|
}
|
|
@@ -248,14 +248,12 @@ export const openclawProvider: AgentProvider = {
|
|
|
248
248
|
async abortChat(agent: AgentConfig): Promise<void> {
|
|
249
249
|
const conn = await getConnection(agent);
|
|
250
250
|
const sessionKey = await resolveSessionKey(agent, conn);
|
|
251
|
-
|
|
252
|
-
console.log('[OpenClaw] ← raw chat.abort:', JSON.stringify(result, null, 2));
|
|
251
|
+
await request(conn, 'chat.abort', { sessionKey });
|
|
253
252
|
},
|
|
254
253
|
|
|
255
254
|
async clearSession(agent: AgentConfig): Promise<void> {
|
|
256
255
|
const conn = await getConnection(agent);
|
|
257
256
|
const sessionKey = await resolveSessionKey(agent, conn);
|
|
258
|
-
|
|
259
|
-
console.log('[OpenClaw] ← raw sessions.reset:', JSON.stringify(result, null, 2));
|
|
257
|
+
await request(conn, 'sessions.reset', { sessionKey });
|
|
260
258
|
},
|
|
261
259
|
};
|
|
@@ -37,6 +37,13 @@ const MAX_RECONNECT_DELAY_MS = 120_000;
|
|
|
37
37
|
|
|
38
38
|
const IS_WINDOWS = process.platform === 'win32';
|
|
39
39
|
|
|
40
|
+
let _debug = false;
|
|
41
|
+
|
|
42
|
+
/** Enable or disable SSH tunnel debug logging. */
|
|
43
|
+
export function setSshTunnelDebug(enabled: boolean): void {
|
|
44
|
+
_debug = enabled;
|
|
45
|
+
}
|
|
46
|
+
|
|
40
47
|
// ---------------------------------------------------------------------------
|
|
41
48
|
// Shared askpass script — reads password from an env var, never touches disk
|
|
42
49
|
// with the actual secret. One script is created per process lifetime and
|
|
@@ -144,7 +151,7 @@ export function stopTunnel(agentId: string): void {
|
|
|
144
151
|
entry.running = false;
|
|
145
152
|
entry.reconnecting = false;
|
|
146
153
|
tunnels.delete(agentId);
|
|
147
|
-
console.log(`[SSH Tunnel] Stopped tunnel for agent "${agentId}"`);
|
|
154
|
+
if (_debug) console.log(`[SSH Tunnel] Stopped tunnel for agent "${agentId}"`);
|
|
148
155
|
}
|
|
149
156
|
|
|
150
157
|
/**
|
|
@@ -206,7 +213,7 @@ function probePort(port: number, timeoutMs: number = 1000): Promise<boolean> {
|
|
|
206
213
|
*/
|
|
207
214
|
function spawnTunnel(entry: TunnelEntry): Promise<void> {
|
|
208
215
|
return new Promise<void>((resolve, reject) => {
|
|
209
|
-
console.log(`[SSH Tunnel] Starting tunnel for agent "${entry.agentId}": ${entry.config.command}`);
|
|
216
|
+
if (_debug) console.log(`[SSH Tunnel] Starting tunnel for agent "${entry.agentId}": ${entry.config.command}`);
|
|
210
217
|
|
|
211
218
|
let settled = false;
|
|
212
219
|
|
|
@@ -247,7 +254,7 @@ function spawnTunnel(entry: TunnelEntry): Promise<void> {
|
|
|
247
254
|
env.DISPLAY = env.DISPLAY || 'dummy:0';
|
|
248
255
|
}
|
|
249
256
|
|
|
250
|
-
console.log(`[SSH Tunnel] Spawning: ${cmd} ${args.join(' ')}`);
|
|
257
|
+
if (_debug) console.log(`[SSH Tunnel] Spawning: ${cmd} ${args.join(' ')}`);
|
|
251
258
|
|
|
252
259
|
const child = spawn(cmd, args, {
|
|
253
260
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
@@ -260,6 +267,7 @@ function spawnTunnel(entry: TunnelEntry): Promise<void> {
|
|
|
260
267
|
|
|
261
268
|
// Log all output for diagnostics
|
|
262
269
|
const handleOutput = (source: string) => (data: Buffer) => {
|
|
270
|
+
if (!_debug) return;
|
|
263
271
|
const text = data.toString().trim();
|
|
264
272
|
if (text) {
|
|
265
273
|
console.log(`[SSH Tunnel] [${entry.agentId}] ${source}: ${text}`);
|
|
@@ -270,7 +278,7 @@ function spawnTunnel(entry: TunnelEntry): Promise<void> {
|
|
|
270
278
|
child.stderr?.on('data', handleOutput('stderr'));
|
|
271
279
|
|
|
272
280
|
child.on('error', (err) => {
|
|
273
|
-
console.error(`[SSH Tunnel] Spawn error for agent "${entry.agentId}":`, err.message);
|
|
281
|
+
if (_debug) console.error(`[SSH Tunnel] Spawn error for agent "${entry.agentId}":`, err.message);
|
|
274
282
|
entry.running = false;
|
|
275
283
|
entry.process = null;
|
|
276
284
|
settle(err);
|
|
@@ -281,7 +289,7 @@ function spawnTunnel(entry: TunnelEntry): Promise<void> {
|
|
|
281
289
|
entry.running = false;
|
|
282
290
|
entry.process = null;
|
|
283
291
|
|
|
284
|
-
console.log(`[SSH Tunnel] Process exited for agent "${entry.agentId}" (code=${code}, signal=${signal})`);
|
|
292
|
+
if (_debug) console.log(`[SSH Tunnel] Process exited for agent "${entry.agentId}" (code=${code}, signal=${signal})`);
|
|
285
293
|
|
|
286
294
|
// If we never settled (tunnel never came up), reject
|
|
287
295
|
settle(new Error(`SSH tunnel exited before becoming ready (code=${code})`));
|
|
@@ -303,7 +311,7 @@ function spawnTunnel(entry: TunnelEntry): Promise<void> {
|
|
|
303
311
|
entry.running = true;
|
|
304
312
|
entry.reconnecting = false;
|
|
305
313
|
entry.reconnectAttempts = 0;
|
|
306
|
-
console.log(`[SSH Tunnel] Tunnel ready (delay heuristic) for agent "${entry.agentId}"`);
|
|
314
|
+
if (_debug) console.log(`[SSH Tunnel] Tunnel ready (delay heuristic) for agent "${entry.agentId}"`);
|
|
307
315
|
settle();
|
|
308
316
|
}
|
|
309
317
|
}, TUNNEL_READY_PROBE_DELAY_MS);
|
|
@@ -336,7 +344,7 @@ function waitForPort(entry: TunnelEntry, port: number, settle: (err?: Error) =>
|
|
|
336
344
|
entry.running = true;
|
|
337
345
|
entry.reconnecting = false;
|
|
338
346
|
entry.reconnectAttempts = 0;
|
|
339
|
-
console.log(`[SSH Tunnel] Tunnel ready (port ${port} open) for agent "${entry.agentId}"`);
|
|
347
|
+
if (_debug) console.log(`[SSH Tunnel] Tunnel ready (port ${port} open) for agent "${entry.agentId}"`);
|
|
340
348
|
settle();
|
|
341
349
|
} else if (attempts >= maxAttempts) {
|
|
342
350
|
clearInterval(timer);
|
|
@@ -344,7 +352,7 @@ function waitForPort(entry: TunnelEntry, port: number, settle: (err?: Error) =>
|
|
|
344
352
|
entry.running = true;
|
|
345
353
|
entry.reconnecting = false;
|
|
346
354
|
entry.reconnectAttempts = 0;
|
|
347
|
-
console.log(`[SSH Tunnel] Tunnel assumed ready (port probe timed out) for agent "${entry.agentId}"`);
|
|
355
|
+
if (_debug) console.log(`[SSH Tunnel] Tunnel assumed ready (port probe timed out) for agent "${entry.agentId}"`);
|
|
348
356
|
settle();
|
|
349
357
|
}
|
|
350
358
|
}, intervalMs);
|
|
@@ -364,7 +372,7 @@ function scheduleReconnect(entry: TunnelEntry): void {
|
|
|
364
372
|
MAX_RECONNECT_DELAY_MS
|
|
365
373
|
);
|
|
366
374
|
|
|
367
|
-
console.log(`[SSH Tunnel] Scheduling reconnect for agent "${entry.agentId}" in ${delay}ms (attempt ${entry.reconnectAttempts})`);
|
|
375
|
+
if (_debug) console.log(`[SSH Tunnel] Scheduling reconnect for agent "${entry.agentId}" in ${delay}ms (attempt ${entry.reconnectAttempts})`);
|
|
368
376
|
|
|
369
377
|
entry.reconnectTimer = setTimeout(async () => {
|
|
370
378
|
entry.reconnectTimer = null;
|
|
@@ -372,9 +380,9 @@ function scheduleReconnect(entry: TunnelEntry): void {
|
|
|
372
380
|
|
|
373
381
|
try {
|
|
374
382
|
await spawnTunnel(entry);
|
|
375
|
-
console.log(`[SSH Tunnel] Reconnected tunnel for agent "${entry.agentId}"`);
|
|
383
|
+
if (_debug) console.log(`[SSH Tunnel] Reconnected tunnel for agent "${entry.agentId}"`);
|
|
376
384
|
} catch (err) {
|
|
377
|
-
console.error(`[SSH Tunnel] Reconnect failed for agent "${entry.agentId}":`,
|
|
385
|
+
if (_debug) console.error(`[SSH Tunnel] Reconnect failed for agent "${entry.agentId}":`,
|
|
378
386
|
err instanceof Error ? err.message : err);
|
|
379
387
|
// Schedule another attempt
|
|
380
388
|
if (!entry.intentionalStop) {
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { anthropic as createAnthropicModel, completePrompt, ContentBlock } from '../models';
|
|
2
|
+
import { parseChangeList, getTransformInstr } from '../service/transformPage';
|
|
3
|
+
import { Attachment, Builder, BuilderResult, CHANGE_OPS_SCHEMA, CHANGE_OPS_SCHEMA_NO_RANGED, ContextSection } from './types';
|
|
4
|
+
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Builder options — passed from the route handler
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
export interface AnthropicBuilderOptions {
|
|
10
|
+
apiKey?: string;
|
|
11
|
+
model?: string;
|
|
12
|
+
/** Optional wrapper applied to any internally-created model (e.g. for debug logging). */
|
|
13
|
+
wrapModel?: (model: completePrompt) => completePrompt;
|
|
14
|
+
/** When true, this is the first edit to a saved page (v0→v1) — forces Opus + no ranged writes. */
|
|
15
|
+
isFirstEdit?: boolean;
|
|
16
|
+
/** When true, retry the last edit — forces Opus + no ranged writes, skips classification. */
|
|
17
|
+
tryAgain?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Request classification
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
export type Classification = 'question' | 'easy-change' | 'medium-change' | 'hard-change' | 're-write';
|
|
25
|
+
|
|
26
|
+
export interface ClassifyResult {
|
|
27
|
+
classification: Classification;
|
|
28
|
+
/** When classification is "question", this contains the answer text. */
|
|
29
|
+
answer?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const CLASSIFIER_SYSTEM_PROMPT = `You classify user messages for a web page builder. Default to a change request. Only classify as "question" when the user is purely asking for information with zero implication that anything should change.
|
|
33
|
+
|
|
34
|
+
<DECISION_RULES>
|
|
35
|
+
Step 1 — Is this a pure information question with zero change implication?
|
|
36
|
+
Examples: "What color is the header?", "How many sections are there?", "What font is the title using?"
|
|
37
|
+
Yes, and there is absolutely no suggestion that anything should change → "question"
|
|
38
|
+
No → go to step 2.
|
|
39
|
+
|
|
40
|
+
Step 2 — Classify complexity:
|
|
41
|
+
"easy-change": Text edits, color/style tweaks, toggling visibility, adding/removing a single element, minor CSS changes.
|
|
42
|
+
"medium-change": Modifying existing JS logic, updating event handlers, changing multiple related styles, adding a small feature to existing functionality, updating existing behavior.
|
|
43
|
+
"hard-change": Complex new features, significant new JS, games, animations, restructuring layout, forms with validation, multi-step work.
|
|
44
|
+
"re-write": Complete page overhaul, fundamental restructure, "start over", "rebuild", "redo the whole page".
|
|
45
|
+
|
|
46
|
+
<OUTPUT_FORMAT>
|
|
47
|
+
Return only JSON. No other text.
|
|
48
|
+
- Change: { "classification": "easy-change" } or { "classification": "medium-change" } or { "classification": "hard-change" } or { "classification": "re-write" }
|
|
49
|
+
- Question: { "classification": "question", "answer": "<brief answer>" }`;
|
|
50
|
+
|
|
51
|
+
export async function classifyRequest(
|
|
52
|
+
apiKey: string,
|
|
53
|
+
pageHtml: string,
|
|
54
|
+
userMessage: string
|
|
55
|
+
): Promise<ClassifyResult> {
|
|
56
|
+
try {
|
|
57
|
+
const sonnet = createAnthropicModel({ apiKey, model: 'claude-sonnet-4-5' });
|
|
58
|
+
const result = await sonnet({
|
|
59
|
+
system: { role: 'system', content: CLASSIFIER_SYSTEM_PROMPT },
|
|
60
|
+
prompt: { role: 'user', content: `<PAGE_HTML>\n${pageHtml}\n\n<USER_MESSAGE>\n${userMessage}` },
|
|
61
|
+
jsonMode: true,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
if (!result.completed || !result.value) {
|
|
65
|
+
return { classification: 'hard-change' };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const parsed = JSON.parse(result.value);
|
|
69
|
+
const c = parsed.classification;
|
|
70
|
+
if (c === 'question') {
|
|
71
|
+
return { classification: 'question', answer: typeof parsed.answer === 'string' ? parsed.answer : '' };
|
|
72
|
+
}
|
|
73
|
+
if (c === 'easy-change' || c === 'medium-change' || c === 'hard-change' || c === 're-write') {
|
|
74
|
+
return { classification: c };
|
|
75
|
+
}
|
|
76
|
+
return { classification: 'hard-change' };
|
|
77
|
+
} catch {
|
|
78
|
+
return { classification: 'hard-change' };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
// Anthropic builder factory
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Create an Anthropic-tuned builder.
|
|
88
|
+
*
|
|
89
|
+
* @param complete The completePrompt function (already configured with API key / model).
|
|
90
|
+
* @param userInstructions Optional user-level instructions from settings.
|
|
91
|
+
* @param productName Product name for branding in prompts (defaults to 'SynthOS').
|
|
92
|
+
* @param options Optional API key and model name for classifier routing.
|
|
93
|
+
*/
|
|
94
|
+
export function createAnthropicBuilder(
|
|
95
|
+
complete: completePrompt,
|
|
96
|
+
userInstructions?: string,
|
|
97
|
+
productName?: string,
|
|
98
|
+
options?: AnthropicBuilderOptions
|
|
99
|
+
): Builder {
|
|
100
|
+
const name = productName ?? 'SynthOS';
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
async run(currentPage, additionalSections, userMessage, newBuild, attachments?): Promise<BuilderResult> {
|
|
104
|
+
try {
|
|
105
|
+
const isOpus = options?.model?.startsWith('claude-opus-');
|
|
106
|
+
const noRanged = CHANGE_OPS_SCHEMA_NO_RANGED;
|
|
107
|
+
|
|
108
|
+
// Non-Opus models or missing apiKey: existing behavior
|
|
109
|
+
if (!isOpus || !options?.apiKey) {
|
|
110
|
+
return buildWithModel(complete, currentPage, additionalSections, userMessage, userInstructions, name, attachments);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Console errors bypass classification — always route to Opus with no ranged writes
|
|
114
|
+
if (userMessage.includes('CONSOLE_ERRORS:')) {
|
|
115
|
+
console.log('classifyRequest: console errors detected → routing to ' + options.model! + ' (no ranged)');
|
|
116
|
+
return buildWithModel(complete, currentPage, additionalSections, userMessage, userInstructions, name, attachments, noRanged);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// New builds always use Opus with no ranged writes
|
|
120
|
+
if (newBuild) {
|
|
121
|
+
console.log('classifyRequest: new build → routing to ' + options.model! + ' (no ranged)');
|
|
122
|
+
return buildWithModel(complete, currentPage, additionalSections, userMessage, userInstructions, name, attachments, noRanged);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// First edit to a saved page (v0→v1) — force Opus with no ranged writes
|
|
126
|
+
if (options?.isFirstEdit) {
|
|
127
|
+
console.log('classifyRequest: first edit (v0→v1) → routing to ' + options.model! + ' (no ranged)');
|
|
128
|
+
return buildWithModel(complete, currentPage, additionalSections, userMessage, userInstructions, name, attachments, noRanged);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Try again — force Opus with no ranged writes, skip classification
|
|
132
|
+
if (options?.tryAgain) {
|
|
133
|
+
console.log('classifyRequest: try again → routing to claude-opus-4-6 (no ranged)');
|
|
134
|
+
let opus: completePrompt = createAnthropicModel({ apiKey: options.apiKey!, model: 'claude-opus-4-6' });
|
|
135
|
+
if (options.wrapModel) opus = options.wrapModel(opus);
|
|
136
|
+
return buildWithModel(opus, currentPage, additionalSections, userMessage, userInstructions, name, attachments, noRanged);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Classify the request using Sonnet
|
|
140
|
+
const classifyResult = await classifyRequest(options.apiKey, currentPage.content, userMessage);
|
|
141
|
+
console.log(`classifyRequest: "${classifyResult.classification}" → routing to ${routeLabel(classifyResult.classification, options.model!)}`);
|
|
142
|
+
|
|
143
|
+
// Questions — answer was already provided by the classifier
|
|
144
|
+
if (classifyResult.classification === 'question') {
|
|
145
|
+
return { kind: 'reply', text: classifyResult.answer ?? '' };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Easy changes use Sonnet with default schema (ranged allowed)
|
|
149
|
+
if (classifyResult.classification === 'easy-change') {
|
|
150
|
+
let sonnet: completePrompt = createAnthropicModel({ apiKey: options.apiKey, model: 'claude-sonnet-4-5' });
|
|
151
|
+
if (options.wrapModel) sonnet = options.wrapModel(sonnet);
|
|
152
|
+
return buildWithModel(sonnet, currentPage, additionalSections, userMessage, userInstructions, name, attachments);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Medium changes use Sonnet with no ranged writes
|
|
156
|
+
if (classifyResult.classification === 'medium-change') {
|
|
157
|
+
let sonnet: completePrompt = createAnthropicModel({ apiKey: options.apiKey, model: 'claude-sonnet-4-5' });
|
|
158
|
+
if (options.wrapModel) sonnet = options.wrapModel(sonnet);
|
|
159
|
+
return buildWithModel(sonnet, currentPage, additionalSections, userMessage, userInstructions, name, attachments, noRanged);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Hard changes and re-writes use Opus with no ranged writes
|
|
163
|
+
return buildWithModel(complete, currentPage, additionalSections, userMessage, userInstructions, name, attachments, noRanged);
|
|
164
|
+
} catch (err: unknown) {
|
|
165
|
+
return { kind: 'error', error: err instanceof Error ? err : new Error(String(err)) };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ---------------------------------------------------------------------------
|
|
172
|
+
// Build flow — shared prompt construction + model call + parsing
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
|
|
175
|
+
export async function buildWithModel(
|
|
176
|
+
model: completePrompt,
|
|
177
|
+
currentPage: ContextSection,
|
|
178
|
+
additionalSections: ContextSection[],
|
|
179
|
+
userMessage: string,
|
|
180
|
+
userInstructions: string | undefined,
|
|
181
|
+
productName: string,
|
|
182
|
+
attachments?: Attachment[],
|
|
183
|
+
outputSchema?: Record<string, unknown>
|
|
184
|
+
): Promise<BuilderResult> {
|
|
185
|
+
// -- System message: all static content (cacheable) --
|
|
186
|
+
const systemParts: string[] = [];
|
|
187
|
+
for (const section of additionalSections) {
|
|
188
|
+
if (section.content) {
|
|
189
|
+
systemParts.push(`${section.title}\n${section.content}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const instructionParts: string[] = [];
|
|
194
|
+
if (userInstructions?.trim()) {
|
|
195
|
+
instructionParts.push(userInstructions);
|
|
196
|
+
}
|
|
197
|
+
for (const section of additionalSections) {
|
|
198
|
+
if (section.instructions?.trim()) {
|
|
199
|
+
instructionParts.push(section.instructions);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
instructionParts.push(getTransformInstr(productName));
|
|
203
|
+
const instructions = instructionParts.filter(s => s.trim() !== '').join('\n');
|
|
204
|
+
systemParts.push(`<INSTRUCTIONS>\n${instructions}`);
|
|
205
|
+
|
|
206
|
+
const systemContent = systemParts.join('\n\n');
|
|
207
|
+
|
|
208
|
+
// -- User message: dynamic content only (current page + user message) --
|
|
209
|
+
const promptText = `${currentPage.title}\n${currentPage.content}\n\n<USER_MESSAGE>\n${userMessage}`;
|
|
210
|
+
|
|
211
|
+
// Build prompt content — multimodal when image attachments are present
|
|
212
|
+
const imageAttachments = (attachments ?? []).filter(a => a.mediaType.startsWith('image/'));
|
|
213
|
+
let promptContent: string | ContentBlock[];
|
|
214
|
+
if (imageAttachments.length > 0) {
|
|
215
|
+
const blocks: ContentBlock[] = [{ type: 'text', text: promptText }];
|
|
216
|
+
for (const att of imageAttachments) {
|
|
217
|
+
blocks.push({ type: 'image', mediaType: att.mediaType, data: att.data });
|
|
218
|
+
}
|
|
219
|
+
promptContent = blocks;
|
|
220
|
+
} else {
|
|
221
|
+
promptContent = promptText;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// -- Call model --
|
|
225
|
+
const result = await model({
|
|
226
|
+
system: { role: 'system', content: systemContent },
|
|
227
|
+
prompt: { role: 'user', content: promptContent },
|
|
228
|
+
cacheSystem: true,
|
|
229
|
+
outputSchema: outputSchema ?? CHANGE_OPS_SCHEMA,
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
if (!result.completed) {
|
|
233
|
+
return { kind: 'error', error: result.error ?? new Error('Model call failed') };
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// -- Parse response --
|
|
237
|
+
return parseBuilderResponse(result.value!);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ---------------------------------------------------------------------------
|
|
241
|
+
// Route label for console logging
|
|
242
|
+
// ---------------------------------------------------------------------------
|
|
243
|
+
|
|
244
|
+
function routeLabel(classification: Classification, configuredModel: string): string {
|
|
245
|
+
if (classification === 'question') return 'classifier (answered inline)';
|
|
246
|
+
if (classification === 'easy-change') return 'claude-sonnet-4-5';
|
|
247
|
+
if (classification === 'medium-change') return 'claude-sonnet-4-5 (no ranged)';
|
|
248
|
+
if (classification === 're-write') return configuredModel + ' (re-write)';
|
|
249
|
+
return configuredModel + ' (no ranged)';
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ---------------------------------------------------------------------------
|
|
253
|
+
// Response parsing — shared across builders
|
|
254
|
+
// ---------------------------------------------------------------------------
|
|
255
|
+
|
|
256
|
+
export function parseBuilderResponse(raw: string): BuilderResult {
|
|
257
|
+
// Try parsing as a JSON object with a kind discriminator
|
|
258
|
+
try {
|
|
259
|
+
const parsed = JSON.parse(raw);
|
|
260
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
261
|
+
if (parsed.kind === 'transforms' && Array.isArray(parsed.changes)) {
|
|
262
|
+
return { kind: 'transforms', changes: parsed.changes };
|
|
263
|
+
}
|
|
264
|
+
if (parsed.kind === 'reply' && typeof parsed.text === 'string') {
|
|
265
|
+
return { kind: 'reply', text: parsed.text };
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
// Bare array — backward compat
|
|
269
|
+
if (Array.isArray(parsed)) {
|
|
270
|
+
return { kind: 'transforms', changes: parsed };
|
|
271
|
+
}
|
|
272
|
+
} catch {
|
|
273
|
+
// fall through to parseChangeList extraction
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Fall back to extracting a JSON array from the response text
|
|
277
|
+
try {
|
|
278
|
+
const changes = parseChangeList(raw);
|
|
279
|
+
return { kind: 'transforms', changes };
|
|
280
|
+
} catch {
|
|
281
|
+
return { kind: 'error', error: new Error('Failed to parse model response as JSON') };
|
|
282
|
+
}
|
|
283
|
+
}
|