@engenty/wizards-runtime 0.0.0-stage → 0.7.5
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/LICENSE +110 -0
- package/README.md +214 -2
- package/apps/runtime/dist/agents/architect.js +444 -0
- package/apps/runtime/dist/agents/studio-assistant.js +883 -0
- package/apps/runtime/dist/agents/subscription.js +207 -0
- package/apps/runtime/dist/agents/thought.js +21 -0
- package/apps/runtime/dist/agents/website.js +480 -0
- package/apps/runtime/dist/app.js +359 -0
- package/apps/runtime/dist/apple/wizards-apple +0 -0
- package/apps/runtime/dist/assistant/browser.js +26 -0
- package/apps/runtime/dist/assistant/docs.js +158 -0
- package/apps/runtime/dist/assistant/knows.js +353 -0
- package/apps/runtime/dist/assistant/primer.js +26 -0
- package/apps/runtime/dist/assistant/runs.js +24 -0
- package/apps/runtime/dist/auth/account.js +175 -0
- package/apps/runtime/dist/auth/device.js +112 -0
- package/apps/runtime/dist/auth/index.js +358 -0
- package/apps/runtime/dist/auth/keys.js +55 -0
- package/apps/runtime/dist/auth/local-ticket.js +26 -0
- package/apps/runtime/dist/authoring/example.js +74 -0
- package/apps/runtime/dist/authoring/guide.js +433 -0
- package/apps/runtime/dist/authoring/ops.js +117 -0
- package/apps/runtime/dist/browser/fast/action-space.js +160 -0
- package/apps/runtime/dist/browser/fast/classifier.js +91 -0
- package/apps/runtime/dist/browser/fast/decide.js +171 -0
- package/apps/runtime/dist/browser/fast/driver.js +141 -0
- package/apps/runtime/dist/browser/fast/instructions.js +33 -0
- package/apps/runtime/dist/browser/fast/run.js +249 -0
- package/apps/runtime/dist/browser/fast/span-picker.js +153 -0
- package/apps/runtime/dist/browser/fast/text-helper.js +178 -0
- package/apps/runtime/dist/browser/snapshot.js +335 -0
- package/apps/runtime/dist/cli/account.js +385 -0
- package/apps/runtime/dist/cli/autostart.js +182 -0
- package/apps/runtime/dist/cli/autoupdate.js +221 -0
- package/apps/runtime/dist/cli/clients.js +44 -0
- package/apps/runtime/dist/cli/connect.js +540 -0
- package/apps/runtime/dist/cli/environment.js +93 -0
- package/apps/runtime/dist/cli/exec.js +45 -0
- package/apps/runtime/dist/cli/features.js +50 -0
- package/apps/runtime/dist/cli/home.js +88 -0
- package/apps/runtime/dist/cli/install-env.js +33 -0
- package/apps/runtime/dist/cli/machine.js +107 -0
- package/apps/runtime/dist/cli/main.js +222 -0
- package/apps/runtime/dist/cli/mcp.js +136 -0
- package/apps/runtime/dist/cli/menu.js +202 -0
- package/apps/runtime/dist/cli/processes.js +72 -0
- package/apps/runtime/dist/cli/report.js +66 -0
- package/apps/runtime/dist/cli/setup.js +335 -0
- package/apps/runtime/dist/cli/start.js +276 -0
- package/apps/runtime/dist/cli/status.js +203 -0
- package/apps/runtime/dist/cli/ui.js +59 -0
- package/apps/runtime/dist/cli/uninstall-main.js +20 -0
- package/apps/runtime/dist/cli/uninstall.js +231 -0
- package/apps/runtime/dist/cli/update.js +86 -0
- package/apps/runtime/dist/cli/validate.js +75 -0
- package/apps/runtime/dist/cli/versions.js +123 -0
- package/apps/runtime/dist/connectors/builtin.js +86 -0
- package/apps/runtime/dist/connectors/external.js +409 -0
- package/apps/runtime/dist/connectors/gmail.js +107 -0
- package/apps/runtime/dist/connectors/imap.js +261 -0
- package/apps/runtime/dist/connectors/index.js +325 -0
- package/apps/runtime/dist/connectors/mail.js +72 -0
- package/apps/runtime/dist/connectors/outlook.js +90 -0
- package/apps/runtime/dist/connectors/plugin-connections.js +343 -0
- package/apps/runtime/dist/connectors/via-account.js +65 -0
- package/apps/runtime/dist/credits/credits.js +100 -0
- package/apps/runtime/dist/credits/estimate.js +152 -0
- package/apps/runtime/dist/db/adopt.js +149 -0
- package/apps/runtime/dist/db/client.js +173 -0
- package/apps/runtime/dist/db/control-schema.js +133 -0
- package/apps/runtime/dist/db/migrations/control/0000_last_absorbing_man.sql +47 -0
- package/apps/runtime/dist/db/migrations/control/0001_gorgeous_nicolaos.sql +41 -0
- package/apps/runtime/dist/db/migrations/control/0002_secret_penance.sql +1 -0
- package/apps/runtime/dist/db/migrations/control/0003_nasty_mordo.sql +3 -0
- package/apps/runtime/dist/db/migrations/control/0004_breezy_eddie_brock.sql +6 -0
- package/apps/runtime/dist/db/migrations/control/0005_chilly_oracle.sql +1 -0
- package/apps/runtime/dist/db/migrations/control/0006_sleepy_mojo.sql +1 -0
- package/apps/runtime/dist/db/migrations/control/0007_common_the_hand.sql +15 -0
- package/apps/runtime/dist/db/migrations/control/0008_runner.sql +8 -0
- package/apps/runtime/dist/db/migrations/control/0009_plugin_claim.sql +9 -0
- package/apps/runtime/dist/db/migrations/control/0010_tan_snowbird.sql +6 -0
- package/apps/runtime/dist/db/migrations/control/meta/0000_snapshot.json +311 -0
- package/apps/runtime/dist/db/migrations/control/meta/0001_snapshot.json +599 -0
- package/apps/runtime/dist/db/migrations/control/meta/0002_snapshot.json +607 -0
- package/apps/runtime/dist/db/migrations/control/meta/0003_snapshot.json +311 -0
- package/apps/runtime/dist/db/migrations/control/meta/0004_snapshot.json +350 -0
- package/apps/runtime/dist/db/migrations/control/meta/0005_snapshot.json +358 -0
- package/apps/runtime/dist/db/migrations/control/meta/0006_snapshot.json +365 -0
- package/apps/runtime/dist/db/migrations/control/meta/0007_snapshot.json +460 -0
- package/apps/runtime/dist/db/migrations/control/meta/0008_snapshot.json +505 -0
- package/apps/runtime/dist/db/migrations/control/meta/0009_snapshot.json +561 -0
- package/apps/runtime/dist/db/migrations/control/meta/0010_snapshot.json +600 -0
- package/apps/runtime/dist/db/migrations/control/meta/_journal.json +83 -0
- package/apps/runtime/dist/db/migrations/tenant/0000_eminent_freak.sql +188 -0
- package/apps/runtime/dist/db/migrations/tenant/0001_salty_master_chief.sql +1 -0
- package/apps/runtime/dist/db/migrations/tenant/0002_cloudy_firebird.sql +1 -0
- package/apps/runtime/dist/db/migrations/tenant/0003_brown_warbound.sql +52 -0
- package/apps/runtime/dist/db/migrations/tenant/0004_romantic_nocturne.sql +6 -0
- package/apps/runtime/dist/db/migrations/tenant/0005_reflective_forgotten_one.sql +2 -0
- package/apps/runtime/dist/db/migrations/tenant/0006_hard_war_machine.sql +30 -0
- package/apps/runtime/dist/db/migrations/tenant/0007_space_data.sql +43 -0
- package/apps/runtime/dist/db/migrations/tenant/0008_run_event_note.sql +1 -0
- package/apps/runtime/dist/db/migrations/tenant/0009_knowledge.sql +104 -0
- package/apps/runtime/dist/db/migrations/tenant/0010_plugin_connection.sql +15 -0
- package/apps/runtime/dist/db/migrations/tenant/0011_runners.sql +2 -0
- package/apps/runtime/dist/db/migrations/tenant/0012_assistant_chat.sql +40 -0
- package/apps/runtime/dist/db/migrations/tenant/0013_run_origin.sql +1 -0
- package/apps/runtime/dist/db/migrations/tenant/0014_run_answer.sql +2 -0
- package/apps/runtime/dist/db/migrations/tenant/0015_device_calls.sql +13 -0
- package/apps/runtime/dist/db/migrations/tenant/0016_even_trish_tilby.sql +12 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0000_snapshot.json +1323 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0001_snapshot.json +1330 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0002_snapshot.json +1337 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0003_snapshot.json +1605 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0004_snapshot.json +1645 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0005_snapshot.json +1659 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0006_snapshot.json +1696 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0007_snapshot.json +2012 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0008_snapshot.json +2019 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0009_snapshot.json +2837 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0010_snapshot.json +2942 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0011_snapshot.json +2956 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0012_snapshot.json +3132 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0013_snapshot.json +3139 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0014_snapshot.json +3154 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0015_snapshot.json +3243 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0016_snapshot.json +3326 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/_journal.json +125 -0
- package/apps/runtime/dist/db/schema.js +602 -0
- package/apps/runtime/dist/debug.js +196 -0
- package/apps/runtime/dist/docs/dev/index.md +41 -0
- package/apps/runtime/dist/docs/dev/meta.json +7 -0
- package/apps/runtime/dist/docs/dev/plugins/connections.md +74 -0
- package/apps/runtime/dist/docs/dev/plugins/events.md +89 -0
- package/apps/runtime/dist/docs/dev/plugins/examples.md +101 -0
- package/apps/runtime/dist/docs/dev/plugins/index.md +78 -0
- package/apps/runtime/dist/docs/dev/plugins/layout.md +90 -0
- package/apps/runtime/dist/docs/dev/plugins/meta.json +28 -0
- package/apps/runtime/dist/docs/dev/plugins/quick-start.md +94 -0
- package/apps/runtime/dist/docs/dev/plugins/reference.md +348 -0
- package/apps/runtime/dist/docs/dev/plugins/reloading.md +76 -0
- package/apps/runtime/dist/docs/dev/plugins/routes.md +185 -0
- package/apps/runtime/dist/docs/dev/plugins/runners.md +230 -0
- package/apps/runtime/dist/docs/dev/plugins/server.md +281 -0
- package/apps/runtime/dist/docs/dev/plugins/shipping.md +79 -0
- package/apps/runtime/dist/docs/dev/plugins/space.md +297 -0
- package/apps/runtime/dist/docs/dev/plugins/starters.md +70 -0
- package/apps/runtime/dist/docs/dev/plugins/studio.md +363 -0
- package/apps/runtime/dist/docs/dev/plugins/styling.md +129 -0
- package/apps/runtime/dist/docs/dev/plugins/tables.md +104 -0
- package/apps/runtime/dist/docs/dev/plugins/tools.md +139 -0
- package/apps/runtime/dist/docs/dev/wizard-format.md +125 -0
- package/apps/runtime/dist/docs/meta.json +3 -0
- package/apps/runtime/dist/docs/user/ai-apps.md +112 -0
- package/apps/runtime/dist/docs/user/assistant.md +77 -0
- package/apps/runtime/dist/docs/user/build-a-wizard.md +116 -0
- package/apps/runtime/dist/docs/user/command-line.md +83 -0
- package/apps/runtime/dist/docs/user/connectors.md +90 -0
- package/apps/runtime/dist/docs/user/create-account.md +52 -0
- package/apps/runtime/dist/docs/user/data.md +66 -0
- package/apps/runtime/dist/docs/user/first-start.md +60 -0
- package/apps/runtime/dist/docs/user/index.md +49 -0
- package/apps/runtime/dist/docs/user/install.md +123 -0
- package/apps/runtime/dist/docs/user/local-only.md +42 -0
- package/apps/runtime/dist/docs/user/meta.json +32 -0
- package/apps/runtime/dist/docs/user/models.md +156 -0
- package/apps/runtime/dist/docs/user/plugins.md +71 -0
- package/apps/runtime/dist/docs/user/run.md +98 -0
- package/apps/runtime/dist/docs/user/server.md +52 -0
- package/apps/runtime/dist/docs/user/space.md +126 -0
- package/apps/runtime/dist/docs/user/steps.md +220 -0
- package/apps/runtime/dist/docs/user/test-and-share.md +224 -0
- package/apps/runtime/dist/docs/user/troubleshooting.md +67 -0
- package/apps/runtime/dist/docs/user/wordpress.md +121 -0
- package/apps/runtime/dist/documents/parse.js +246 -0
- package/apps/runtime/dist/engenty/connections-external/build-connector.js +193 -0
- package/apps/runtime/dist/engenty/connections-external/errors.js +11 -0
- package/apps/runtime/dist/engenty/connections-external/import-service.js +270 -0
- package/apps/runtime/dist/engenty/connections-external/importer/classify.js +34 -0
- package/apps/runtime/dist/engenty/connections-external/importer/map-auth.js +185 -0
- package/apps/runtime/dist/engenty/connections-external/importer/normalize-mcp.js +63 -0
- package/apps/runtime/dist/engenty/connections-external/importer/normalize-openapi.js +130 -0
- package/apps/runtime/dist/engenty/connections-external/invoke/http-invoker.js +143 -0
- package/apps/runtime/dist/engenty/connections-external/invoke/mcp-client.js +143 -0
- package/apps/runtime/dist/engenty/connections-external/net/guarded-fetch.js +119 -0
- package/apps/runtime/dist/engenty/connections-external/oauth-dcr.js +88 -0
- package/apps/runtime/dist/engenty/connections-external/registry-client.js +336 -0
- package/apps/runtime/dist/engenty/connections-external/registry-source.js +82 -0
- package/apps/runtime/dist/engenty/connections-external/types.js +7 -0
- package/apps/runtime/dist/engenty/connections-github/connector.js +209 -0
- package/apps/runtime/dist/engenty/connections-google/connectors/calendar.js +381 -0
- package/apps/runtime/dist/engenty/connections-google/connectors/contacts.js +94 -0
- package/apps/runtime/dist/engenty/connections-google/connectors/drive.js +290 -0
- package/apps/runtime/dist/engenty/connections-google/connectors/gmail.js +567 -0
- package/apps/runtime/dist/engenty/connections-google/definitions.js +11 -0
- package/apps/runtime/dist/engenty/connections-google/shared.js +73 -0
- package/apps/runtime/dist/engenty/connections-hubspot/connector.js +140 -0
- package/apps/runtime/dist/engenty/connections-microsoft/action.js +17 -0
- package/apps/runtime/dist/engenty/connections-microsoft/graph.js +114 -0
- package/apps/runtime/dist/engenty/connections-microsoft/onedrive.js +286 -0
- package/apps/runtime/dist/engenty/connections-microsoft/outlook.js +492 -0
- package/apps/runtime/dist/engenty/connections-s3/s3.js +328 -0
- package/apps/runtime/dist/engenty/connections-sdk/files-capability.js +86 -0
- package/apps/runtime/dist/engenty/connections-sdk/oauth2.js +150 -0
- package/apps/runtime/dist/engenty/connections-sdk/registry.js +89 -0
- package/apps/runtime/dist/engenty/connections-sdk/storage-capability.js +84 -0
- package/apps/runtime/dist/engenty/connections-sdk/types.js +30 -0
- package/apps/runtime/dist/engenty/connections-slack/connector.js +360 -0
- package/apps/runtime/dist/engenty/csv-import/csv-matrix.js +178 -0
- package/apps/runtime/dist/engenty/csv-import/import-sources.js +87 -0
- package/apps/runtime/dist/engenty/csv-import/parse-csv.js +129 -0
- package/apps/runtime/dist/engenty/csv-import/serialize-csv.js +20 -0
- package/apps/runtime/dist/engenty/csv-import/types.js +1 -0
- package/apps/runtime/dist/engenty/csv-import/xlsx-workbook.js +159 -0
- package/apps/runtime/dist/engenty/doc-converter/interface.js +9 -0
- package/apps/runtime/dist/engenty/doc-converter/page-break.js +92 -0
- package/apps/runtime/dist/engenty/doc-converter/providers/local/index.js +72 -0
- package/apps/runtime/dist/engenty/document-scanner/schemas/classifier.js +30 -0
- package/apps/runtime/dist/engenty/document-scanner/schemas/index.js +4 -0
- package/apps/runtime/dist/engenty/document-scanner/schemas/invoice.js +73 -0
- package/apps/runtime/dist/engenty/document-scanner/schemas/receipt.js +63 -0
- package/apps/runtime/dist/engenty/document-scanner/schemas/shared.js +40 -0
- package/apps/runtime/dist/engenty/search-index/chunks.js +164 -0
- package/apps/runtime/dist/engenty/search-index/contracts.js +9 -0
- package/apps/runtime/dist/engenty/shims/connections-sdk.js +39 -0
- package/apps/runtime/dist/engenty/shims/plugin-sdk.js +1 -0
- package/apps/runtime/dist/engenty/shims/web-ingest.js +19 -0
- package/apps/runtime/dist/engenty/web-ingest/ssrf.js +277 -0
- package/apps/runtime/dist/engine/asks.js +82 -0
- package/apps/runtime/dist/engine/decide.js +82 -0
- package/apps/runtime/dist/engine/device.js +194 -0
- package/apps/runtime/dist/engine/events.js +128 -0
- package/apps/runtime/dist/engine/input.js +208 -0
- package/apps/runtime/dist/engine/owner.js +32 -0
- package/apps/runtime/dist/engine/prepare.js +90 -0
- package/apps/runtime/dist/engine/push.js +58 -0
- package/apps/runtime/dist/engine/requirements.js +355 -0
- package/apps/runtime/dist/engine/resources.js +198 -0
- package/apps/runtime/dist/engine/runner.js +970 -0
- package/apps/runtime/dist/engine/steps.js +1000 -0
- package/apps/runtime/dist/engine/surface.js +91 -0
- package/apps/runtime/dist/engine/template.js +240 -0
- package/apps/runtime/dist/engine/types.js +4 -0
- package/apps/runtime/dist/env.js +295 -0
- package/apps/runtime/dist/features.js +123 -0
- package/apps/runtime/dist/files/blobs.js +22 -0
- package/apps/runtime/dist/files/objects.js +86 -0
- package/apps/runtime/dist/files/storage.js +120 -0
- package/apps/runtime/dist/files/web-images.js +101 -0
- package/apps/runtime/dist/film/project.js +257 -0
- package/apps/runtime/dist/film/skills.js +91 -0
- package/apps/runtime/dist/film/sounds.js +122 -0
- package/apps/runtime/dist/film/step.js +289 -0
- package/apps/runtime/dist/film/tools.js +99 -0
- package/apps/runtime/dist/geocode.js +58 -0
- package/apps/runtime/dist/harness/antigravity.js +129 -0
- package/apps/runtime/dist/harness/apple.js +270 -0
- package/apps/runtime/dist/harness/claude.js +147 -0
- package/apps/runtime/dist/harness/codex.js +229 -0
- package/apps/runtime/dist/harness/cursor.js +93 -0
- package/apps/runtime/dist/harness/env.js +124 -0
- package/apps/runtime/dist/harness/index.js +95 -0
- package/apps/runtime/dist/harness/model.js +279 -0
- package/apps/runtime/dist/harness/prompt.js +121 -0
- package/apps/runtime/dist/harness/terminal.js +194 -0
- package/apps/runtime/dist/harness/types.js +7 -0
- package/apps/runtime/dist/index.js +113 -0
- package/apps/runtime/dist/limits.js +81 -0
- package/apps/runtime/dist/link-preview.js +60 -0
- package/apps/runtime/dist/log.js +106 -0
- package/apps/runtime/dist/manage.js +249 -0
- package/apps/runtime/dist/mcp/auth.js +161 -0
- package/apps/runtime/dist/mcp/bridge.js +59 -0
- package/apps/runtime/dist/mcp/flow-app.js +97 -0
- package/apps/runtime/dist/mcp/handler.js +54 -0
- package/apps/runtime/dist/mcp/scopes.js +14 -0
- package/apps/runtime/dist/mcp/seen.js +65 -0
- package/apps/runtime/dist/mcp/tools.js +572 -0
- package/apps/runtime/dist/media/conversation.js +167 -0
- package/apps/runtime/dist/media/ffmpeg.js +121 -0
- package/apps/runtime/dist/media/generate.js +120 -0
- package/apps/runtime/dist/media/marking.js +121 -0
- package/apps/runtime/dist/media/transcribe.js +198 -0
- package/apps/runtime/dist/model-errors.js +42 -0
- package/apps/runtime/dist/models.js +1164 -0
- package/apps/runtime/dist/plugin.js +61 -0
- package/apps/runtime/dist/plugins/channels.js +78 -0
- package/apps/runtime/dist/plugins/claims.js +56 -0
- package/apps/runtime/dist/plugins/discovery.js +155 -0
- package/apps/runtime/dist/plugins/events.js +63 -0
- package/apps/runtime/dist/plugins/jobs.js +64 -0
- package/apps/runtime/dist/plugins/loader.js +508 -0
- package/apps/runtime/dist/plugins/migrations.js +69 -0
- package/apps/runtime/dist/plugins/pro-keys.js +34 -0
- package/apps/runtime/dist/plugins/pro-state.js +83 -0
- package/apps/runtime/dist/plugins/pro.js +310 -0
- package/apps/runtime/dist/plugins/public.js +44 -0
- package/apps/runtime/dist/plugins/registry.js +122 -0
- package/apps/runtime/dist/plugins/runs.js +210 -0
- package/apps/runtime/dist/render/browser-path.js +54 -0
- package/apps/runtime/dist/render/chromium.js +98 -0
- package/apps/runtime/dist/render/convert.js +158 -0
- package/apps/runtime/dist/render/downloads.js +299 -0
- package/apps/runtime/dist/render/guard.js +25 -0
- package/apps/runtime/dist/render/shaped.js +137 -0
- package/apps/runtime/dist/routes/api.js +440 -0
- package/apps/runtime/dist/routes/debug.js +42 -0
- package/apps/runtime/dist/routes/delivery.js +90 -0
- package/apps/runtime/dist/routes/internal.js +81 -0
- package/apps/runtime/dist/routes/lang.js +25 -0
- package/apps/runtime/dist/routes/marketplace.js +43 -0
- package/apps/runtime/dist/routes/plugin-public.js +113 -0
- package/apps/runtime/dist/routes/plugins.js +212 -0
- package/apps/runtime/dist/routes/runs.js +1422 -0
- package/apps/runtime/dist/routes/studio.js +1629 -0
- package/apps/runtime/dist/routes/wizard-stream.js +30 -0
- package/apps/runtime/dist/running.js +39 -0
- package/apps/runtime/dist/sandbox/docker.js +75 -0
- package/apps/runtime/dist/sandbox/index.js +23 -0
- package/apps/runtime/dist/sandbox/types.js +1 -0
- package/apps/runtime/dist/secrets/crypto.js +28 -0
- package/apps/runtime/dist/secrets/signing.js +52 -0
- package/apps/runtime/dist/secrets/vault.js +59 -0
- package/apps/runtime/dist/services/access.js +88 -0
- package/apps/runtime/dist/services/api.js +248 -0
- package/apps/runtime/dist/services/architect.js +95 -0
- package/apps/runtime/dist/services/assistant-chats.js +311 -0
- package/apps/runtime/dist/services/brand.js +85 -0
- package/apps/runtime/dist/services/cloud.js +456 -0
- package/apps/runtime/dist/services/draft-events.js +27 -0
- package/apps/runtime/dist/services/engenty-shapes.js +71 -0
- package/apps/runtime/dist/services/errors.js +29 -0
- package/apps/runtime/dist/services/files.js +178 -0
- package/apps/runtime/dist/services/knowledge-model.js +559 -0
- package/apps/runtime/dist/services/knowledge.js +721 -0
- package/apps/runtime/dist/services/links.js +16 -0
- package/apps/runtime/dist/services/marketplace.js +492 -0
- package/apps/runtime/dist/services/onboarding.js +152 -0
- package/apps/runtime/dist/services/package-file.js +82 -0
- package/apps/runtime/dist/services/package.js +170 -0
- package/apps/runtime/dist/services/profile.js +51 -0
- package/apps/runtime/dist/services/project-files.js +439 -0
- package/apps/runtime/dist/services/project-index.js +707 -0
- package/apps/runtime/dist/services/projects.js +387 -0
- package/apps/runtime/dist/services/relevance.js +58 -0
- package/apps/runtime/dist/services/runners.js +178 -0
- package/apps/runtime/dist/services/runs.js +524 -0
- package/apps/runtime/dist/services/shares.js +130 -0
- package/apps/runtime/dist/services/site.js +270 -0
- package/apps/runtime/dist/services/space-categories.js +861 -0
- package/apps/runtime/dist/services/space-data.js +792 -0
- package/apps/runtime/dist/services/space-origin.js +207 -0
- package/apps/runtime/dist/services/spaces.js +656 -0
- package/apps/runtime/dist/services/translations.js +306 -0
- package/apps/runtime/dist/services/widgets.js +34 -0
- package/apps/runtime/dist/services/wizard-icon.js +157 -0
- package/apps/runtime/dist/services/wizards.js +420 -0
- package/apps/runtime/dist/settings.js +16 -0
- package/apps/runtime/dist/store/errors.js +4 -0
- package/apps/runtime/dist/store/index.js +299 -0
- package/apps/runtime/dist/store/shared-lists.js +123 -0
- package/apps/runtime/dist/tenants/control.js +238 -0
- package/apps/runtime/dist/tenants/tenant.js +26 -0
- package/apps/runtime/dist/texts.js +207 -0
- package/apps/runtime/dist/tools/browser.js +703 -0
- package/apps/runtime/dist/tools/connector.js +204 -0
- package/apps/runtime/dist/tools/index.js +291 -0
- package/apps/runtime/dist/tools/mail.js +104 -0
- package/apps/runtime/dist/tools/net-guard.js +170 -0
- package/apps/runtime/dist/tools/pages.js +81 -0
- package/apps/runtime/dist/tools/plugin.js +180 -0
- package/apps/runtime/dist/tools/project.js +154 -0
- package/apps/runtime/dist/tools/shared.js +69 -0
- package/apps/runtime/dist/tools/store.js +238 -0
- package/apps/runtime/dist/update.js +113 -0
- package/apps/runtime/dist/widgets/bundle.js +99 -0
- package/apps/runtime/dist/widgets/render.js +329 -0
- package/apps/runtime/dist/widgets/runtime.js +108 -0
- package/apps/runtime/dist/widgets/script.js +30 -0
- package/apps/runtime/dist/widgets/step.js +223 -0
- package/apps/web/dist/ai-labels/ai-black.svg +20 -0
- package/apps/web/dist/ai-labels/ai-generated-black.svg +31 -0
- package/apps/web/dist/ai-labels/ai-generated-white.svg +32 -0
- package/apps/web/dist/ai-labels/ai-modified-black.svg +30 -0
- package/apps/web/dist/ai-labels/ai-modified-white.svg +31 -0
- package/apps/web/dist/ai-labels/ai-white.svg +21 -0
- package/apps/web/dist/assets/AboutDialog-D4mvc-zu.js +1 -0
- package/apps/web/dist/assets/ChangelogDialog-DLqpJsF6.js +1 -0
- package/apps/web/dist/assets/ChatView-3laypRcE.js +10 -0
- package/apps/web/dist/assets/EditorPage-BalA5U2Y.js +3 -0
- package/apps/web/dist/assets/FlowPreview-CusP7ci9.js +1 -0
- package/apps/web/dist/assets/Harness-BrP-ENHg.css +1 -0
- package/apps/web/dist/assets/Harness-iYLtOLaK.js +16 -0
- package/apps/web/dist/assets/NewWizardChannels-CbwJ9A56.js +1 -0
- package/apps/web/dist/assets/NewWizardPage-BOBYiB9i.js +7 -0
- package/apps/web/dist/assets/OnboardingPage-CUwCiVIr.js +1 -0
- package/apps/web/dist/assets/ProjectPage-Bb16MdYG.js +1 -0
- package/apps/web/dist/assets/PublicRunner-BCt7Q6C-.js +1 -0
- package/apps/web/dist/assets/ReadOnly-kIHX2TEy.js +1 -0
- package/apps/web/dist/assets/RunDrawer-hZ9oeSXu.js +4 -0
- package/apps/web/dist/assets/RunnerView-DUS9fPmx.js +1 -0
- package/apps/web/dist/assets/SettingsPage-BzwR50fi.js +1 -0
- package/apps/web/dist/assets/SetupPage-gAp7rPxp.js +1 -0
- package/apps/web/dist/assets/SharePage-DqZWHva_.js +1 -0
- package/apps/web/dist/assets/changelog-DugmCNSf.js +1 -0
- package/apps/web/dist/assets/engenty-eXZ-EAwd.js +1 -0
- package/apps/web/dist/assets/esm-DXeMu8Lb.js +7 -0
- package/apps/web/dist/assets/fluffy-engenty-ClRou7yu.js +456 -0
- package/apps/web/dist/assets/index-AfUa8xKD.css +1 -0
- package/apps/web/dist/assets/index-C5Sm7tTR.js +14 -0
- package/apps/web/dist/assets/jsx-runtime-BNakU3Ej.js +1 -0
- package/apps/web/dist/assets/lock-R_CfU0Xo.js +1 -0
- package/apps/web/dist/assets/oss-credits-cZ9NUaLZ.js +1 -0
- package/apps/web/dist/assets/pdfmake-Cx9scTbD.js +228 -0
- package/apps/web/dist/assets/phone-CgrIS4_-.js +1 -0
- package/apps/web/dist/assets/preload-helper-CNEI7N0N.js +1 -0
- package/apps/web/dist/assets/qr-scanner-worker.min-CJaXo3yu.js +98 -0
- package/apps/web/dist/assets/qr-scanner.min-C_UI0gH-.js +1 -0
- package/apps/web/dist/assets/rolldown-runtime-hePW80VL.js +1 -0
- package/apps/web/dist/assets/settings-menu-2IMOOlLl.js +1 -0
- package/apps/web/dist/assets/ui-dzGvc3xk.js +137 -0
- package/apps/web/dist/assets/upload-BSuTilFF.js +1 -0
- package/apps/web/dist/assets/vfs_fonts-DllDSYt1.js +1 -0
- package/apps/web/dist/embed.js +266 -0
- package/apps/web/dist/favicon.svg +1 -0
- package/apps/web/dist/icons/apple-touch-icon.png +0 -0
- package/apps/web/dist/icons/icon-192.png +0 -0
- package/apps/web/dist/icons/icon-512.png +0 -0
- package/apps/web/dist/icons/icon-maskable-512.png +0 -0
- package/apps/web/dist/index.html +48 -0
- package/apps/web/dist/install.sh +577 -0
- package/apps/web/dist/manifest.webmanifest +21 -0
- package/apps/web/dist/mcp/flow.html +980 -0
- package/apps/web/dist/sw.js +15 -0
- package/bin/wizards.mjs +288 -0
- package/modules/README.md +15 -0
- package/node_modules/@engenty/wizards/dist/plugin/index.d.ts +1150 -0
- package/node_modules/@engenty/wizards/dist/plugin/index.js +4 -0
- package/node_modules/@engenty/wizards/dist/plugin/studio.d.ts +322 -0
- package/node_modules/@engenty/wizards/dist/plugin/studio.js +4 -0
- package/node_modules/@engenty/wizards/dist/plugin/thread.d.ts +174 -0
- package/node_modules/@engenty/wizards/dist/plugin/thread.js +821 -0
- package/node_modules/@engenty/wizards/package.json +11 -0
- package/node_modules/@engenty-wizards/shared/dist/assistant.d.ts +174 -0
- package/node_modules/@engenty-wizards/shared/dist/assistant.js +65 -0
- package/node_modules/@engenty-wizards/shared/dist/changes.d.ts +22 -0
- package/node_modules/@engenty-wizards/shared/dist/changes.js +61 -0
- package/node_modules/@engenty-wizards/shared/dist/connectors.d.ts +77 -0
- package/node_modules/@engenty-wizards/shared/dist/connectors.js +2 -0
- package/node_modules/@engenty-wizards/shared/dist/decision.d.ts +76 -0
- package/node_modules/@engenty-wizards/shared/dist/decision.js +147 -0
- package/node_modules/@engenty-wizards/shared/dist/definition.d.ts +2922 -0
- package/node_modules/@engenty-wizards/shared/dist/definition.js +1920 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-format.d.ts +5 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-format.js +132 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-value.d.ts +11 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-value.js +209 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns.d.ts +532 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns.js +217 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/index.d.ts +3 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/index.js +3 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-lexical.d.ts +14 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-lexical.js +166 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-rank.d.ts +33 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-rank.js +99 -0
- package/node_modules/@engenty-wizards/shared/dist/feature-flags.d.ts +78 -0
- package/node_modules/@engenty-wizards/shared/dist/feature-flags.js +70 -0
- package/node_modules/@engenty-wizards/shared/dist/features.d.ts +41 -0
- package/node_modules/@engenty-wizards/shared/dist/features.js +89 -0
- package/node_modules/@engenty-wizards/shared/dist/fit.d.ts +71 -0
- package/node_modules/@engenty-wizards/shared/dist/fit.js +382 -0
- package/node_modules/@engenty-wizards/shared/dist/json-schema.d.ts +14 -0
- package/node_modules/@engenty-wizards/shared/dist/json-schema.js +66 -0
- package/node_modules/@engenty-wizards/shared/dist/knowledge.d.ts +199 -0
- package/node_modules/@engenty-wizards/shared/dist/knowledge.js +68 -0
- package/node_modules/@engenty-wizards/shared/dist/marketplace-entry.d.ts +35 -0
- package/node_modules/@engenty-wizards/shared/dist/marketplace-entry.js +107 -0
- package/node_modules/@engenty-wizards/shared/dist/marketplace-search.d.ts +20 -0
- package/node_modules/@engenty-wizards/shared/dist/marketplace-search.js +136 -0
- package/node_modules/@engenty-wizards/shared/dist/marketplace.d.ts +227 -0
- package/node_modules/@engenty-wizards/shared/dist/marketplace.js +354 -0
- package/node_modules/@engenty-wizards/shared/dist/merge.d.ts +7 -0
- package/node_modules/@engenty-wizards/shared/dist/merge.js +40 -0
- package/node_modules/@engenty-wizards/shared/dist/numbers.d.ts +24 -0
- package/node_modules/@engenty-wizards/shared/dist/numbers.js +54 -0
- package/node_modules/@engenty-wizards/shared/dist/projects.d.ts +224 -0
- package/node_modules/@engenty-wizards/shared/dist/projects.js +319 -0
- package/node_modules/@engenty-wizards/shared/dist/providers.d.ts +101 -0
- package/node_modules/@engenty-wizards/shared/dist/providers.js +390 -0
- package/node_modules/@engenty-wizards/shared/dist/run.d.ts +680 -0
- package/node_modules/@engenty-wizards/shared/dist/run.js +620 -0
- package/node_modules/@engenty-wizards/shared/dist/runner-texts.d.ts +546 -0
- package/node_modules/@engenty-wizards/shared/dist/runner-texts.js +523 -0
- package/node_modules/@engenty-wizards/shared/dist/runners.d.ts +93 -0
- package/node_modules/@engenty-wizards/shared/dist/runners.js +146 -0
- package/node_modules/@engenty-wizards/shared/dist/shape.d.ts +102 -0
- package/node_modules/@engenty-wizards/shared/dist/shape.js +318 -0
- package/node_modules/@engenty-wizards/shared/dist/space-data.d.ts +89 -0
- package/node_modules/@engenty-wizards/shared/dist/space-data.js +47 -0
- package/node_modules/@engenty-wizards/shared/dist/store.d.ts +117 -0
- package/node_modules/@engenty-wizards/shared/dist/store.js +81 -0
- package/node_modules/@engenty-wizards/shared/dist/surface.d.ts +115 -0
- package/node_modules/@engenty-wizards/shared/dist/surface.js +442 -0
- package/node_modules/@engenty-wizards/shared/dist/texts.d.ts +45 -0
- package/node_modules/@engenty-wizards/shared/dist/texts.js +54 -0
- package/node_modules/@engenty-wizards/shared/dist/translation.d.ts +23 -0
- package/node_modules/@engenty-wizards/shared/dist/translation.js +154 -0
- package/node_modules/@engenty-wizards/shared/dist/wizard-package.d.ts +48 -0
- package/node_modules/@engenty-wizards/shared/dist/wizard-package.js +49 -0
- package/node_modules/@engenty-wizards/shared/dist/workspace.d.ts +16 -0
- package/node_modules/@engenty-wizards/shared/dist/workspace.js +51 -0
- package/node_modules/@engenty-wizards/shared/package.json +10 -0
- package/package.json +77 -4
- package/plugin/.claude-plugin/plugin.json +9 -0
- package/plugin/.mcp.json +8 -0
- package/plugin/commands/wizard.md +8 -0
- package/plugin/skills/build-wizard/SKILL.md +27 -0
- package/plugin/skills/use-wizard/SKILL.md +18 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { eq } from "drizzle-orm";
|
|
2
|
+
import { db, schema } from "../db/client.js";
|
|
3
|
+
import { dropLinks, dropRuns } from "../tenants/control.js";
|
|
4
|
+
/** Takes a wizard's public tokens and its runs out of the control database. */
|
|
5
|
+
export async function forgetWizardLinks(wizardId) {
|
|
6
|
+
const w = await db.query.wizard.findFirst({ where: eq(schema.wizard.id, wizardId) });
|
|
7
|
+
const runs = await db
|
|
8
|
+
.select({ id: schema.run.id, shareToken: schema.run.shareToken })
|
|
9
|
+
.from(schema.run)
|
|
10
|
+
.where(eq(schema.run.wizardId, wizardId));
|
|
11
|
+
await dropLinks([
|
|
12
|
+
...(w ? [w.shareToken] : []),
|
|
13
|
+
...runs.map((r) => r.shareToken).filter((t) => Boolean(t)),
|
|
14
|
+
]);
|
|
15
|
+
await dropRuns(runs.map((r) => r.id));
|
|
16
|
+
}
|
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { statSync } from "node:fs";
|
|
3
|
+
import { DEFINITION_VERSION, readable, wizardSchema, } from "@engenty-wizards/shared/definition";
|
|
4
|
+
import { capabilitiesOf, costTierOf, effortOf, formatsOf, MARKETPLACE_LANGS, pluginsOf, } from "@engenty-wizards/shared/marketplace";
|
|
5
|
+
import { wizardOutline } from "@engenty-wizards/shared/marketplace-entry";
|
|
6
|
+
import { searchEntries } from "@engenty-wizards/shared/marketplace-search";
|
|
7
|
+
import { note } from "@engenty-wizards/shared/run";
|
|
8
|
+
import { and, eq, inArray, notInArray } from "drizzle-orm";
|
|
9
|
+
import { formulaEstimate } from "../credits/estimate.js";
|
|
10
|
+
import { control, controlDb, db, schema, withTenant } from "../db/client.js";
|
|
11
|
+
import { missingCapabilities, optionalCapabilities, stepsWithoutModel, } from "../engine/requirements.js";
|
|
12
|
+
import { env } from "../env.js";
|
|
13
|
+
import { managed } from "../manage.js";
|
|
14
|
+
import { classCatalog, imageCostUsd, tokenCostUsd, videoCostUsd, WEB_SEARCH_COST_USD, } from "../models.js";
|
|
15
|
+
import { pluginIdsOf, pluginStartersOf } from "../plugins/registry.js";
|
|
16
|
+
import { readSetting, writeSetting } from "../settings.js";
|
|
17
|
+
import { currentTenant, LOCAL_TENANT } from "../tenants/tenant.js";
|
|
18
|
+
import { ServiceError } from "./errors.js";
|
|
19
|
+
/**
|
|
20
|
+
* The marketplace is an app of its own; this runtime is one of its clients
|
|
21
|
+
* (docs/marketplace-contract.md). It searches the marketplace live and starts wizards from its
|
|
22
|
+
* entries. Its starters and what people starred are kept in the control database, so the list
|
|
23
|
+
* and starting a wizard work without a connection too.
|
|
24
|
+
*
|
|
25
|
+
* Beside them stand the starters the tenant's plugins bring (docs/content/dev/plugins/starters.md),
|
|
26
|
+
* listed first and never fetched. They are known as `<plugin id>.<starter id>`; a marketplace
|
|
27
|
+
* entry's id has no dot, so the two never meet.
|
|
28
|
+
*/
|
|
29
|
+
export const asLang = (raw) => MARKETPLACE_LANGS.includes(raw ?? "") ? raw : "en";
|
|
30
|
+
/** A search waits this long for the marketplace; then it searches what is kept here. */
|
|
31
|
+
const TIMEOUT_MS = 5000;
|
|
32
|
+
/** One call to the marketplace's API; null without an answer, or with an error. */
|
|
33
|
+
async function call(path, init) {
|
|
34
|
+
if (!env.marketplace.url) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const res = await fetch(`${env.marketplace.url}/api/v1${path}`, {
|
|
39
|
+
...init,
|
|
40
|
+
signal: AbortSignal.timeout(TIMEOUT_MS),
|
|
41
|
+
});
|
|
42
|
+
if (!res.ok) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
return res.status === 204 ? {} : (await res.json());
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// --- stars: a person's own, in the tenant's database ------------------------------------
|
|
52
|
+
async function starredIds() {
|
|
53
|
+
const rows = await db
|
|
54
|
+
.select({ id: schema.marketplaceStar.entryId })
|
|
55
|
+
.from(schema.marketplaceStar)
|
|
56
|
+
.orderBy(schema.marketplaceStar.createdAt);
|
|
57
|
+
return rows.map((r) => r.id);
|
|
58
|
+
}
|
|
59
|
+
/** Stars an entry or takes the star away; a starred entry is kept for offline use. */
|
|
60
|
+
export async function starEntry(tenantId, entryId, on) {
|
|
61
|
+
if (on) {
|
|
62
|
+
await db.insert(schema.marketplaceStar).values({ tenantId, entryId }).onConflictDoNothing();
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
await db
|
|
66
|
+
.delete(schema.marketplaceStar)
|
|
67
|
+
.where(and(eq(schema.marketplaceStar.tenantId, tenantId), eq(schema.marketplaceStar.entryId, entryId)));
|
|
68
|
+
}
|
|
69
|
+
// What is kept follows at once, beside the request.
|
|
70
|
+
void syncMarketplace(true).catch(() => undefined);
|
|
71
|
+
}
|
|
72
|
+
// --- what is kept for offline use --------------------------------------------------------
|
|
73
|
+
async function kept(ids) {
|
|
74
|
+
const rows = await controlDb
|
|
75
|
+
.select({ entry: control.marketplaceCache.entry })
|
|
76
|
+
.from(control.marketplaceCache)
|
|
77
|
+
.where(ids ? inArray(control.marketplaceCache.id, ids) : undefined);
|
|
78
|
+
return rows.map((r) => r.entry);
|
|
79
|
+
}
|
|
80
|
+
/** The words of a kept entry in a language: a translation where it has one, else its own. */
|
|
81
|
+
function textIn(e, lang) {
|
|
82
|
+
return e.texts.find((t) => t.language === lang) ?? e.texts.find((t) => t.language === e.language);
|
|
83
|
+
}
|
|
84
|
+
function keptSummary(e, lang) {
|
|
85
|
+
const { texts: _texts, files: _files, ...summary } = e;
|
|
86
|
+
const text = textIn(e, lang);
|
|
87
|
+
return text
|
|
88
|
+
? { ...summary, language: text.language, title: text.title, pitch: text.pitch }
|
|
89
|
+
: summary;
|
|
90
|
+
}
|
|
91
|
+
function keptItem(e, lang) {
|
|
92
|
+
const text = textIn(e, lang);
|
|
93
|
+
return text ? { ...keptSummary(e, lang), definition: text.definition, files: e.files } : null;
|
|
94
|
+
}
|
|
95
|
+
// --- the plugins' starters ------------------------------------------------------------------
|
|
96
|
+
/** One of the starters the tenant's plugins bring, by its id; undefined for any other id. */
|
|
97
|
+
async function pluginStarter(id) {
|
|
98
|
+
if (!id.includes(".")) {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
return (await pluginStartersOf(currentTenant())).find((s) => s.id === id);
|
|
102
|
+
}
|
|
103
|
+
/** A plugin's starter as the marketplace lists an entry, read from its definition. */
|
|
104
|
+
function pluginSummary({ id, plugin, starter }) {
|
|
105
|
+
const definition = starter.definition;
|
|
106
|
+
const { name, version } = plugin.source.manifest;
|
|
107
|
+
const capabilities = capabilitiesOf(definition);
|
|
108
|
+
const changed = statSync(plugin.source.entry ?? plugin.source.root, { throwIfNoEntry: false });
|
|
109
|
+
return {
|
|
110
|
+
id,
|
|
111
|
+
revision: 0,
|
|
112
|
+
hash: createHash("sha256")
|
|
113
|
+
.update(`${version}\n${JSON.stringify(definition)}`)
|
|
114
|
+
.digest("hex")
|
|
115
|
+
.slice(0, 16),
|
|
116
|
+
updatedAt: (changed?.mtime ?? new Date()).toISOString(),
|
|
117
|
+
starter: true,
|
|
118
|
+
language: starter.language ?? "en",
|
|
119
|
+
title: starter.title,
|
|
120
|
+
pitch: starter.description,
|
|
121
|
+
// Found by the plugin's name as well: "Termine" finds the appointment plugin's starters.
|
|
122
|
+
terms: [name],
|
|
123
|
+
avatar: definition.avatar,
|
|
124
|
+
formats: formatsOf(definition),
|
|
125
|
+
industries: ["any"],
|
|
126
|
+
useCases: [],
|
|
127
|
+
capabilities,
|
|
128
|
+
effort: effortOf(definition),
|
|
129
|
+
costTier: costTierOf(capabilities),
|
|
130
|
+
steps: definition.steps.length,
|
|
131
|
+
version: definition.version,
|
|
132
|
+
plugins: [plugin.source.id],
|
|
133
|
+
plan: "pro",
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/** A plugin's starter as this app shows it: never starred, and marked with its plugin. */
|
|
137
|
+
function pluginEntry(found, unavailable) {
|
|
138
|
+
const { id, name } = found.plugin.source.manifest;
|
|
139
|
+
return {
|
|
140
|
+
...asEntry(pluginSummary(found), new Set(), unavailable, optionalOf(found.starter.definition)),
|
|
141
|
+
plugin: { id, name },
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/** Two searches' counts per option, added up. */
|
|
145
|
+
function addFacets(a, b) {
|
|
146
|
+
const add = (x = {}, y = {}) => {
|
|
147
|
+
const out = { ...x };
|
|
148
|
+
for (const [key, n] of Object.entries(y)) {
|
|
149
|
+
out[key] = (out[key] ?? 0) + n;
|
|
150
|
+
}
|
|
151
|
+
return out;
|
|
152
|
+
};
|
|
153
|
+
return {
|
|
154
|
+
useCase: add(a.useCase, b.useCase),
|
|
155
|
+
industry: add(a.industry, b.industry),
|
|
156
|
+
format: add(a.format, b.format),
|
|
157
|
+
capability: add(a.capability, b.capability),
|
|
158
|
+
plan: add(a.plan, b.plan),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* The marketplace's entries as this tenant sees them: an entry that stands for a starter of a
|
|
163
|
+
* plugin it has is left out (the plugin's own is listed), and the others say which plugins they
|
|
164
|
+
* need that it does not have.
|
|
165
|
+
*/
|
|
166
|
+
async function withPlugins(entries) {
|
|
167
|
+
if (!entries.some((e) => e.plugins?.length || e.pluginStarter)) {
|
|
168
|
+
return entries;
|
|
169
|
+
}
|
|
170
|
+
const tenant = currentTenant();
|
|
171
|
+
const [have, starters] = await Promise.all([
|
|
172
|
+
pluginIdsOf(tenant),
|
|
173
|
+
pluginStartersOf(tenant).then((list) => new Set(list.map((s) => s.id))),
|
|
174
|
+
]);
|
|
175
|
+
return entries.flatMap((e) => {
|
|
176
|
+
if (e.pluginStarter && starters.has(e.pluginStarter)) {
|
|
177
|
+
return [];
|
|
178
|
+
}
|
|
179
|
+
const needs = (e.plugins ?? []).filter((id) => !have.has(id));
|
|
180
|
+
return [needs.length ? { ...e, needs } : e];
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
// --- searching -----------------------------------------------------------------------------
|
|
184
|
+
/** A summary as this app shows it: whether it can read and run it, and whether it is starred. */
|
|
185
|
+
function asEntry(summary, starred, unavailable, optional = new Set()) {
|
|
186
|
+
const lacking = summary.capabilities.filter((c) => unavailable.has(c));
|
|
187
|
+
return {
|
|
188
|
+
...summary,
|
|
189
|
+
credits: null,
|
|
190
|
+
usable: summary.version <= DEFINITION_VERSION,
|
|
191
|
+
starred: starred.has(summary.id),
|
|
192
|
+
missing: lacking.filter((c) => !optional.has(c)),
|
|
193
|
+
optional: lacking.filter((c) => optional.has(c)),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
/** What only some paths of a wizard need; nothing where it cannot be read. */
|
|
197
|
+
function optionalOf(definition) {
|
|
198
|
+
return readable(definition)
|
|
199
|
+
? optionalCapabilities(wizardSchema.parse(definition))
|
|
200
|
+
: new Set();
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Per entry, what only some of its paths need, read from the wizards kept here (the starters
|
|
204
|
+
* and what is starred); an entry not kept counts everything it needs as needed.
|
|
205
|
+
*/
|
|
206
|
+
async function keptOptional(ids, lang, unavailable) {
|
|
207
|
+
const out = new Map();
|
|
208
|
+
if (!(unavailable.size && ids.length)) {
|
|
209
|
+
return out;
|
|
210
|
+
}
|
|
211
|
+
for (const e of await kept(ids)) {
|
|
212
|
+
const text = textIn(e, lang);
|
|
213
|
+
if (text) {
|
|
214
|
+
out.set(e.id, optionalOf(text.definition));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return out;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Searches the tenant's plugins' starters and the marketplace, in that order: a page holds the
|
|
221
|
+
* plugins' starters the words and filters find, then the marketplace's entries.
|
|
222
|
+
*/
|
|
223
|
+
export async function searchMarketplace(search) {
|
|
224
|
+
const { q = "", starred, limit = 60, offset = 0, lang: _lang, ...filters } = search;
|
|
225
|
+
const own = starred ? [] : await pluginStartersOf(currentTenant());
|
|
226
|
+
if (!own.length) {
|
|
227
|
+
return searchListed(search);
|
|
228
|
+
}
|
|
229
|
+
const byId = new Map(own.map((s) => [s.id, s]));
|
|
230
|
+
const found = searchEntries(own.map(pluginSummary), q, filters, { limit: own.length });
|
|
231
|
+
const shown = found.entries.slice(offset, offset + limit);
|
|
232
|
+
// The marketplace fills what is left of the page, and counts from where its entries begin.
|
|
233
|
+
const room = limit - shown.length;
|
|
234
|
+
const listed = await searchListed({
|
|
235
|
+
...search,
|
|
236
|
+
limit: Math.max(1, room),
|
|
237
|
+
offset: Math.max(0, offset - found.total),
|
|
238
|
+
});
|
|
239
|
+
const unavailable = new Set(listed.unavailable);
|
|
240
|
+
return {
|
|
241
|
+
...listed,
|
|
242
|
+
entries: [
|
|
243
|
+
...shown.flatMap((e) => {
|
|
244
|
+
const entry = byId.get(e.id);
|
|
245
|
+
return entry ? [pluginEntry(entry, unavailable)] : [];
|
|
246
|
+
}),
|
|
247
|
+
...listed.entries.slice(0, room),
|
|
248
|
+
],
|
|
249
|
+
total: found.total + listed.total,
|
|
250
|
+
all: own.length + listed.all,
|
|
251
|
+
facets: addFacets(listed.facets, found.facets),
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* The gallery for someone not signed in (the studio's start page for guests): what the
|
|
256
|
+
* marketplace lists, without a tenant's stars and plugins. An entry that needs a plugin says so.
|
|
257
|
+
*/
|
|
258
|
+
export function searchMarketplaceAsGuest(search) {
|
|
259
|
+
return searchListed(search, true);
|
|
260
|
+
}
|
|
261
|
+
/** Searches the marketplace; without an answer, what is kept here, scored the same way. */
|
|
262
|
+
async function searchListed(search, guest = false) {
|
|
263
|
+
const { q = "", lang, starred: onlyStarred, limit = 60, offset = 0, ...filters } = search;
|
|
264
|
+
const stars = guest ? [] : await starredIds();
|
|
265
|
+
const starred = new Set(stars);
|
|
266
|
+
const unavailable = guest ? new Set() : await missingCapabilities();
|
|
267
|
+
const plugged = (entries) => guest
|
|
268
|
+
? entries.map((e) => (e.plugins?.length ? { ...e, needs: e.plugins } : e))
|
|
269
|
+
: withPlugins(entries);
|
|
270
|
+
const params = new URLSearchParams({ lang, limit: String(limit), offset: String(offset) });
|
|
271
|
+
if (onlyStarred) {
|
|
272
|
+
params.set("ids", stars.join(","));
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
if (q.trim()) {
|
|
276
|
+
params.set("q", q.trim());
|
|
277
|
+
}
|
|
278
|
+
for (const [key, value] of Object.entries(filters)) {
|
|
279
|
+
if (value) {
|
|
280
|
+
params.set(key, value);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
const remote = onlyStarred && !stars.length
|
|
285
|
+
? { entries: [], total: 0, all: 0, facets: { useCase: {}, industry: {}, format: {} } }
|
|
286
|
+
: await call(`/entries?${params}`);
|
|
287
|
+
if (remote) {
|
|
288
|
+
const optional = await keptOptional(remote.entries.map((e) => e.id), lang, unavailable);
|
|
289
|
+
return {
|
|
290
|
+
...remote,
|
|
291
|
+
entries: await plugged(remote.entries.map((e) => asEntry(e, starred, unavailable, optional.get(e.id)))),
|
|
292
|
+
offline: false,
|
|
293
|
+
unavailable: [...unavailable],
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
const local = (await kept(onlyStarred ? stars : undefined)).map((e) => keptSummary(e, lang));
|
|
297
|
+
const page = onlyStarred
|
|
298
|
+
? searchEntries(local, "", {}, { limit, offset })
|
|
299
|
+
: searchEntries(local, q, filters, { limit, offset });
|
|
300
|
+
const optional = await keptOptional(page.entries.map((e) => e.id), lang, unavailable);
|
|
301
|
+
return {
|
|
302
|
+
...page,
|
|
303
|
+
entries: await plugged(page.entries.map((e) => asEntry(e, starred, unavailable, optional.get(e.id)))),
|
|
304
|
+
offline: true,
|
|
305
|
+
unavailable: [...unavailable],
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
// --- one entry -----------------------------------------------------------------------------
|
|
309
|
+
/** An entry with its wizard: from the marketplace, else from what is kept here. */
|
|
310
|
+
async function fetchItem(id, lang) {
|
|
311
|
+
const remote = await call(`/entries/${encodeURIComponent(id)}?lang=${encodeURIComponent(lang)}`);
|
|
312
|
+
if (remote) {
|
|
313
|
+
return remote;
|
|
314
|
+
}
|
|
315
|
+
const [local] = await kept([id]);
|
|
316
|
+
return local ? keptItem(local, lang) : null;
|
|
317
|
+
}
|
|
318
|
+
const CREDITS_PER_USD = 100;
|
|
319
|
+
/**
|
|
320
|
+
* Prices for a runtime that has no gateway to ask: what the models each class is bound to by
|
|
321
|
+
* default cost at their list price, in credits. An estimate needs a number even where a run is
|
|
322
|
+
* paid for another way.
|
|
323
|
+
*/
|
|
324
|
+
function listPrices() {
|
|
325
|
+
const text = (ref) => ({
|
|
326
|
+
model: ref,
|
|
327
|
+
kind: "text",
|
|
328
|
+
inputCreditsPerMTok: tokenCostUsd(ref, { inputTokens: 1e6 }) * CREDITS_PER_USD,
|
|
329
|
+
outputCreditsPerMTok: tokenCostUsd(ref, { outputTokens: 1e6 }) * CREDITS_PER_USD,
|
|
330
|
+
});
|
|
331
|
+
const m = env.models;
|
|
332
|
+
return {
|
|
333
|
+
markup: 1,
|
|
334
|
+
webSearchCredits: WEB_SEARCH_COST_USD * CREDITS_PER_USD,
|
|
335
|
+
classes: {
|
|
336
|
+
classifier: text(m.classifier),
|
|
337
|
+
standard: text(m.standard),
|
|
338
|
+
high: text(m.high),
|
|
339
|
+
highest: text(m.highest),
|
|
340
|
+
image: {
|
|
341
|
+
model: m.image,
|
|
342
|
+
kind: "image",
|
|
343
|
+
creditsPerImage: imageCostUsd(m.image) * CREDITS_PER_USD,
|
|
344
|
+
},
|
|
345
|
+
video: {
|
|
346
|
+
model: m.video,
|
|
347
|
+
kind: "video",
|
|
348
|
+
creditsPerSecond: videoCostUsd(m.video, 1) * CREDITS_PER_USD,
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
/** What a run costs: at the gateway's prices where there is one, else at list prices. */
|
|
354
|
+
async function priced(definition) {
|
|
355
|
+
const catalog = await classCatalog().catch(() => null);
|
|
356
|
+
const price = formulaEstimate(definition, catalog ?? listPrices(), await stepsWithoutModel(definition));
|
|
357
|
+
return { credits: Math.round(price.credits), high: price.high };
|
|
358
|
+
}
|
|
359
|
+
/** An entry in a language with its wizard read against this app; null where there is none. */
|
|
360
|
+
export async function marketplaceDetail(id, lang) {
|
|
361
|
+
const own = await pluginStarter(id);
|
|
362
|
+
if (own) {
|
|
363
|
+
const definition = own.starter.definition;
|
|
364
|
+
return {
|
|
365
|
+
...pluginEntry(own, await missingCapabilities()),
|
|
366
|
+
credits: await priced(definition),
|
|
367
|
+
...wizardOutline(definition, Object.keys(own.starter.files ?? {}), lang),
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
const found = await fetchItem(id, lang);
|
|
371
|
+
if (!found) {
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
const { definition, files, ...summary } = found;
|
|
375
|
+
const entry = asEntry(summary, new Set(await starredIds()), await missingCapabilities(), optionalOf(definition));
|
|
376
|
+
const parsed = readable(definition) ? wizardSchema.parse(definition) : null;
|
|
377
|
+
if (!parsed) {
|
|
378
|
+
return {
|
|
379
|
+
...entry,
|
|
380
|
+
usable: false,
|
|
381
|
+
description: "",
|
|
382
|
+
outline: [],
|
|
383
|
+
results: [],
|
|
384
|
+
files: Object.keys(files),
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
return {
|
|
388
|
+
...entry,
|
|
389
|
+
credits: await priced(parsed),
|
|
390
|
+
...wizardOutline(parsed, Object.keys(files), lang),
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
/** An entry's wizard and workspace in a language: what a wizard is made from. */
|
|
394
|
+
export async function marketplaceWizard(id, lang) {
|
|
395
|
+
const own = await pluginStarter(id);
|
|
396
|
+
if (own) {
|
|
397
|
+
// A plugin's starter is in its one language, whatever is asked for.
|
|
398
|
+
const { starter, plugin } = own;
|
|
399
|
+
return {
|
|
400
|
+
id,
|
|
401
|
+
revision: null,
|
|
402
|
+
plugin: plugin.source.id,
|
|
403
|
+
title: starter.title,
|
|
404
|
+
language: starter.language ?? "en",
|
|
405
|
+
definition: structuredClone(starter.definition),
|
|
406
|
+
files: Object.fromEntries(Object.entries(starter.files ?? {}).map(([path, data]) => [
|
|
407
|
+
path,
|
|
408
|
+
typeof data === "string" ? Buffer.from(data, "utf8") : Buffer.from(data),
|
|
409
|
+
])),
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
const found = await fetchItem(id, lang);
|
|
413
|
+
if (!found) {
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
if (!readable(found.definition)) {
|
|
417
|
+
throw new ServiceError("refused", note("templateNeedsNewerVersion"));
|
|
418
|
+
}
|
|
419
|
+
// A wizard whose plugins are not here would stop at its first step that uses them.
|
|
420
|
+
const have = await pluginIdsOf(currentTenant());
|
|
421
|
+
const needs = pluginsOf(wizardSchema.parse(found.definition)).filter((p) => !have.has(p));
|
|
422
|
+
if (needs.length) {
|
|
423
|
+
throw new ServiceError("refused", note("templateNeedsPlugins", { count: needs.length, plugins: needs }));
|
|
424
|
+
}
|
|
425
|
+
return {
|
|
426
|
+
id: found.id,
|
|
427
|
+
revision: found.revision,
|
|
428
|
+
title: found.title,
|
|
429
|
+
language: found.language,
|
|
430
|
+
definition: wizardSchema.parse(found.definition),
|
|
431
|
+
files: Object.fromEntries(Object.entries(found.files).map(([path, data]) => [path, Buffer.from(data, "base64")])),
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
/** Tells the marketplace a wizard was made from an entry. */
|
|
435
|
+
export async function countInstall(id) {
|
|
436
|
+
await call(`/entries/${encodeURIComponent(id)}/installs`, { method: "POST" });
|
|
437
|
+
}
|
|
438
|
+
// --- keeping the starters and the starred ones ----------------------------------------------
|
|
439
|
+
// Not the key the copying into the database used before: the first look after an update is
|
|
440
|
+
// not held back.
|
|
441
|
+
const SYNCED = "marketplace:kept";
|
|
442
|
+
const SYNC_EVERY_MS = 3600_000;
|
|
443
|
+
let syncing = null;
|
|
444
|
+
/** The stars that count for what is kept: a runtime that runs alone has one person's. */
|
|
445
|
+
async function keptStars() {
|
|
446
|
+
return managed ? [] : withTenant(LOCAL_TENANT, starredIds);
|
|
447
|
+
}
|
|
448
|
+
async function takeOver() {
|
|
449
|
+
const list = await call(`/sync?ids=${encodeURIComponent((await keptStars()).join(","))}`);
|
|
450
|
+
if (!list?.items) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
const have = await controlDb
|
|
454
|
+
.select({ id: control.marketplaceCache.id, hash: control.marketplaceCache.hash })
|
|
455
|
+
.from(control.marketplaceCache);
|
|
456
|
+
for (const item of list.items) {
|
|
457
|
+
if (have.some((h) => h.id === item.id && h.hash === item.hash)) {
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
const entry = await call(`/entries/${encodeURIComponent(item.id)}/export`);
|
|
461
|
+
if (!entry) {
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
const values = { id: entry.id, hash: entry.hash, entry, fetchedAt: new Date() };
|
|
465
|
+
await controlDb
|
|
466
|
+
.insert(control.marketplaceCache)
|
|
467
|
+
.values(values)
|
|
468
|
+
.onConflictDoUpdate({ target: control.marketplaceCache.id, set: values });
|
|
469
|
+
}
|
|
470
|
+
// What the marketplace no longer lists here is not kept either.
|
|
471
|
+
await controlDb.delete(control.marketplaceCache).where(notInArray(control.marketplaceCache.id, list.items.map((i) => i.id)));
|
|
472
|
+
await writeSetting(SYNCED, Date.now());
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Keeps the marketplace's starters and the starred entries, by hash: an entry is fetched again
|
|
476
|
+
* only when it changed. At most once an hour unless forced.
|
|
477
|
+
*/
|
|
478
|
+
export async function syncMarketplace(force = false) {
|
|
479
|
+
if (!env.marketplace.url) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
const last = await readSetting(SYNCED);
|
|
483
|
+
if (!force && last && Date.now() - last < SYNC_EVERY_MS) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
syncing ??= takeOver()
|
|
487
|
+
.catch((err) => console.error("[marketplace]", err.message))
|
|
488
|
+
.finally(() => {
|
|
489
|
+
syncing = null;
|
|
490
|
+
});
|
|
491
|
+
await syncing;
|
|
492
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { readWebsite } from "../agents/website.js";
|
|
2
|
+
import { managed } from "../manage.js";
|
|
3
|
+
import { readSetting, writeSetting } from "../settings.js";
|
|
4
|
+
import { isAdminRole } from "./access.js";
|
|
5
|
+
import { addProjectFile, projectFiles } from "./project-files.js";
|
|
6
|
+
import { listProjects, updateProject } from "./projects.js";
|
|
7
|
+
/**
|
|
8
|
+
* The first visit of a team's studio in the cloud: a short wizard asks who the team is, reads
|
|
9
|
+
* its website into the space, takes first documents and explains the space. It shows once, to
|
|
10
|
+
* the people who may change the team, and never to a team that already has wizards; the mark
|
|
11
|
+
* that it is done sits in the control database under the tenant.
|
|
12
|
+
*/
|
|
13
|
+
const key = (tenantId) => `onboarding:${tenantId}`;
|
|
14
|
+
export async function needsOnboarding(user) {
|
|
15
|
+
if (!managed || !isAdminRole(user.role)) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
if (await readSetting(key(user.tenantId))) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
const projects = await listProjects(user.id);
|
|
22
|
+
return projects.every((p) => p.wizardCount === 0);
|
|
23
|
+
}
|
|
24
|
+
export async function finishOnboarding(tenantId) {
|
|
25
|
+
await writeSetting(key(tenantId), { doneAt: new Date().toISOString() });
|
|
26
|
+
}
|
|
27
|
+
/** A hex colour as `#rrggbb`, or null when the value is no plain colour. */
|
|
28
|
+
function hex(value) {
|
|
29
|
+
const v = value.trim().toLowerCase();
|
|
30
|
+
const six = v.match(/^#([0-9a-f]{6})\b/);
|
|
31
|
+
if (six) {
|
|
32
|
+
return `#${six[1]}`;
|
|
33
|
+
}
|
|
34
|
+
const three = v.match(/^#([0-9a-f]{3})\b/);
|
|
35
|
+
if (three) {
|
|
36
|
+
return `#${[...three[1]].map((c) => c + c).join("")}`;
|
|
37
|
+
}
|
|
38
|
+
const rgb = v.match(/^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/);
|
|
39
|
+
if (rgb) {
|
|
40
|
+
const part = (n) => Math.min(255, Number(n)).toString(16).padStart(2, "0");
|
|
41
|
+
return `#${part(rgb[1])}${part(rgb[2])}${part(rgb[3])}`;
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
/** Greys and near-whites say nothing about a brand. */
|
|
46
|
+
function telling(color) {
|
|
47
|
+
const r = Number.parseInt(color.slice(1, 3), 16);
|
|
48
|
+
const g = Number.parseInt(color.slice(3, 5), 16);
|
|
49
|
+
const b = Number.parseInt(color.slice(5, 7), 16);
|
|
50
|
+
const spread = Math.max(r, g, b) - Math.min(r, g, b);
|
|
51
|
+
const light = (r + g + b) / 3;
|
|
52
|
+
return spread > 24 && light > 24 && light < 235;
|
|
53
|
+
}
|
|
54
|
+
/** A page title is often "Company – Slogan" or "Home | Company": the company's part of it. */
|
|
55
|
+
export function companyOf(title) {
|
|
56
|
+
const parts = title
|
|
57
|
+
.split(/\s+[–—|·-]\s+|:\s+/)
|
|
58
|
+
.map((p) => p.trim())
|
|
59
|
+
.filter(Boolean);
|
|
60
|
+
if (parts.length < 2) {
|
|
61
|
+
return title.trim().slice(0, 120);
|
|
62
|
+
}
|
|
63
|
+
const generic = /^(home|start|startseite|willkommen|welcome)$/i;
|
|
64
|
+
const named = parts.filter((p) => !generic.test(p));
|
|
65
|
+
// The shortest telling part is the name; a slogan is longer.
|
|
66
|
+
return (named.sort((a, b) => a.length - b.length)[0] ?? parts[0]).slice(0, 120);
|
|
67
|
+
}
|
|
68
|
+
/** The first paragraph of the page's text that reads like prose, as the about text. */
|
|
69
|
+
export function aboutOf(description, text) {
|
|
70
|
+
if (description.trim().length >= 40) {
|
|
71
|
+
return description.trim().slice(0, 600);
|
|
72
|
+
}
|
|
73
|
+
const paragraph = text
|
|
74
|
+
.split(/\n{2,}/)
|
|
75
|
+
.map((p) => p.replace(/^[#>*\-\s]+/, "").trim())
|
|
76
|
+
.find((p) => p.length >= 80 && !p.startsWith("[") && !/^!\[/.test(p));
|
|
77
|
+
return (paragraph ?? description.trim()).slice(0, 600);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Up to three brand colours out of what the site declares and uses: the theme colour first, then
|
|
81
|
+
* the stylesheet's named colours, then the most used ones; greys and near-whites left out.
|
|
82
|
+
*/
|
|
83
|
+
export function brandColors(brand) {
|
|
84
|
+
const candidates = [
|
|
85
|
+
...(brand.themeColor ? [{ name: "Brand", value: brand.themeColor }] : []),
|
|
86
|
+
...brand.variables,
|
|
87
|
+
...brand.frequent.map((value, i) => ({ name: `Colour ${i + 1}`, value })),
|
|
88
|
+
];
|
|
89
|
+
const colors = [];
|
|
90
|
+
for (const c of candidates) {
|
|
91
|
+
const value = hex(c.value);
|
|
92
|
+
if (value && telling(value) && !colors.some((x) => x.value === value)) {
|
|
93
|
+
colors.push({ name: c.name.replace(/^--/, "").slice(0, 120) || "Colour", value });
|
|
94
|
+
}
|
|
95
|
+
if (colors.length === 3) {
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return colors;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Reads a website into the space's Info & brand: the company's name, what it does, up to three
|
|
103
|
+
* of its colours and, where the space has none yet, its logo. What the assistant does on
|
|
104
|
+
* request, done once at the start without a model.
|
|
105
|
+
*/
|
|
106
|
+
export async function applyWebsite(userId, projectId, url) {
|
|
107
|
+
const reading = await readWebsite(url, AbortSignal.timeout(30_000));
|
|
108
|
+
const name = companyOf(reading.title || new URL(reading.url).hostname);
|
|
109
|
+
const about = aboutOf(reading.description, reading.text);
|
|
110
|
+
const colors = brandColors(reading.brand);
|
|
111
|
+
await updateProject(userId, projectId, {
|
|
112
|
+
brand: {
|
|
113
|
+
name,
|
|
114
|
+
...(about ? { about } : {}),
|
|
115
|
+
...(colors.length ? { colors } : {}),
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
let logo = false;
|
|
119
|
+
const first = reading.brand.logos[0];
|
|
120
|
+
if (first && (await projectFiles(projectId, "logo")).length === 0) {
|
|
121
|
+
logo = await fetchLogo(projectId, first.url, first.hint);
|
|
122
|
+
}
|
|
123
|
+
return { name, about: Boolean(about), colors: colors.length, logo };
|
|
124
|
+
}
|
|
125
|
+
const LOGO_BYTES = 5 * 1024 * 1024;
|
|
126
|
+
async function fetchLogo(projectId, url, hint) {
|
|
127
|
+
try {
|
|
128
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(15_000) });
|
|
129
|
+
const mime = res.headers.get("content-type")?.split(";")[0].trim().toLowerCase() ?? "";
|
|
130
|
+
if (!res.ok || !mime.startsWith("image/")) {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
const data = new Uint8Array(await res.arrayBuffer());
|
|
134
|
+
if (data.byteLength === 0 || data.byteLength > LOGO_BYTES) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
const name = decodeURIComponent(new URL(url).pathname.split("/").pop() || "logo");
|
|
138
|
+
await addProjectFile(projectId, {
|
|
139
|
+
kind: "logo",
|
|
140
|
+
name,
|
|
141
|
+
mime,
|
|
142
|
+
data,
|
|
143
|
+
description: hint || undefined,
|
|
144
|
+
source: url,
|
|
145
|
+
});
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// A logo that cannot be fetched is no loss: the person adds one under Space → Logos.
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
}
|