@webship.co/workspace 1.0.0-beta1 → 1.0.0-rc1
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 +339 -0
- package/README.md +28 -364
- package/core/bin/workspace.js +135 -0
- package/core/config/settings.labels.yml +514 -0
- package/core/config/settings.template.yml +292 -0
- package/core/config/style.yml +53 -0
- package/core/config/theme.default.yml +161 -0
- package/core/config/theme.dispatch.yml +166 -0
- package/core/config/theme.drupal.yml +175 -0
- package/core/config/theme.ember.yml +160 -0
- package/core/config/theme.github.yml +172 -0
- package/core/config/theme.gitlab.yml +168 -0
- package/core/config/theme.press.yml +161 -0
- package/core/config/theme.slate.yml +160 -0
- package/core/config/theme.upsun.yml +174 -0
- package/core/docs/architecture.svg +65 -0
- package/core/docs/getting-started.md +51 -0
- package/core/docs/plugins.md +27 -0
- package/core/docs/releases.md +23 -0
- package/core/docs/testing.md +15 -0
- package/core/docs/workspaces.md +25 -0
- package/core/scripts/args/arg-ai-items.sh +65 -0
- package/core/scripts/args/arg-autostart.sh +31 -0
- package/core/scripts/args/arg-avatar-agent.sh +28 -0
- package/core/scripts/args/arg-avatar-catalog.sh +12 -0
- package/core/scripts/args/arg-avatar-character.sh +68 -0
- package/core/scripts/args/arg-avatar-voice.sh +24 -0
- package/core/scripts/args/arg-avatar.sh +22 -0
- package/core/scripts/args/arg-checks.sh +36 -0
- package/core/scripts/args/arg-clone.sh +42 -0
- package/core/scripts/args/arg-commands.sh +51 -0
- package/core/scripts/args/arg-cucumber.sh +34 -0
- package/core/scripts/args/arg-distribution.sh +30 -0
- package/core/scripts/args/arg-drupal.sh +39 -0
- package/core/scripts/args/arg-drupal_cms.sh +18 -0
- package/core/scripts/args/arg-gitlab-ci-local.sh +25 -0
- package/core/scripts/args/arg-graphify.sh +36 -0
- package/core/scripts/args/arg-graphs.sh +12 -0
- package/core/scripts/args/arg-hostnames.sh +23 -0
- package/core/scripts/args/arg-milvus-backup.sh +8 -0
- package/core/scripts/args/arg-milvus-mcp.sh +12 -0
- package/core/scripts/args/arg-milvus.sh +32 -0
- package/core/scripts/args/arg-obsidian.sh +17 -0
- package/core/scripts/args/arg-odoo.sh +18 -0
- package/core/scripts/args/arg-penpot-backup.sh +8 -0
- package/core/scripts/args/arg-penpot-mcp.sh +15 -0
- package/core/scripts/args/arg-penpot-token.sh +17 -0
- package/core/scripts/args/arg-penpot.sh +44 -0
- package/core/scripts/args/arg-rag.sh +21 -0
- package/core/scripts/args/arg-ragify.sh +35 -0
- package/core/scripts/args/arg-reclaim.sh +18 -0
- package/core/scripts/args/arg-release.sh +21 -0
- package/core/scripts/args/arg-screen.sh +28 -0
- package/core/scripts/args/arg-site-template.sh +13 -0
- package/core/scripts/args/arg-spdd.sh +16 -0
- package/core/scripts/args/arg-specs.sh +22 -0
- package/core/scripts/args/arg-storage.sh +36 -0
- package/core/scripts/args/arg-testing.sh +36 -0
- package/core/scripts/args/arg-webship.sh +34 -0
- package/core/scripts/args/arg-webships-project.sh +22 -0
- package/core/scripts/args/arg-webships-site-template.sh +17 -0
- package/core/scripts/args/arg-webships.sh +34 -0
- package/core/scripts/args/arg-website-site-template.sh +17 -0
- package/core/scripts/args/arg-website.sh +25 -0
- package/core/scripts/args/arg-wordpress.sh +18 -0
- package/core/scripts/autostart-run.sh +162 -0
- package/core/scripts/autostart-stop.sh +30 -0
- package/core/scripts/autostart-watch.sh +43 -0
- package/core/scripts/autostart.sh +26 -0
- package/core/scripts/bootstrap-functions.sh +117 -0
- package/core/scripts/bootstrap-libraries.sh +10 -0
- package/core/scripts/bootstrap.sh +71 -0
- package/core/scripts/ddev-shim/ddev +122 -0
- package/core/scripts/docker-gid.sh +119 -0
- package/core/scripts/functions/arg-site-template.sh +28 -0
- package/core/scripts/functions/fun-ai-items-lock.sh +175 -0
- package/core/scripts/functions/fun-ai-items-sources.sh +298 -0
- package/core/scripts/functions/fun-ai-items.sh +621 -0
- package/core/scripts/functions/fun-ai-providers.sh +72 -0
- package/core/scripts/functions/fun-autostart.sh +429 -0
- package/core/scripts/functions/fun-avatar.sh +441 -0
- package/core/scripts/functions/fun-backup-item.sh +50 -0
- package/core/scripts/functions/fun-build-distribution.sh +226 -0
- package/core/scripts/functions/fun-build-site-template.sh +104 -0
- package/core/scripts/functions/fun-build-webships.sh +161 -0
- package/core/scripts/functions/fun-build-website.sh +199 -0
- package/core/scripts/functions/fun-checks.sh +243 -0
- package/core/scripts/functions/fun-clone-project.sh +289 -0
- package/core/scripts/functions/fun-commands.sh +521 -0
- package/core/scripts/functions/fun-database.sh +63 -0
- package/core/scripts/functions/fun-ddev-hostnames.sh +213 -0
- package/core/scripts/functions/fun-ddev-nodejs.sh +15 -0
- package/core/scripts/functions/fun-ddev-telemetry.sh +15 -0
- package/core/scripts/functions/fun-default-settings.sh +28 -0
- package/core/scripts/functions/fun-distribution-cucumber.sh +45 -0
- package/core/scripts/functions/fun-distribution-drupal.sh +25 -0
- package/core/scripts/functions/fun-distribution-drupal_cms.sh +35 -0
- package/core/scripts/functions/fun-distribution-webship.sh +28 -0
- package/core/scripts/functions/fun-distribution-webships.sh +33 -0
- package/core/scripts/functions/fun-distribution-website.sh +19 -0
- package/core/scripts/functions/fun-drush.sh +48 -0
- package/core/scripts/functions/fun-elevenlabs.sh +179 -0
- package/core/scripts/functions/fun-files.sh +19 -0
- package/core/scripts/functions/fun-git-worktrees.sh +142 -0
- package/core/scripts/functions/fun-gitlab-ci.sh +156 -0
- package/core/scripts/functions/fun-hostnames-cli.sh +88 -0
- package/core/scripts/functions/fun-milvus.sh +584 -0
- package/core/scripts/functions/fun-odoo.sh +121 -0
- package/core/scripts/functions/fun-penpot.sh +724 -0
- package/core/scripts/functions/fun-project-name.sh +20 -0
- package/core/scripts/functions/fun-rag.sh +253 -0
- package/core/scripts/functions/fun-ragify.sh +165 -0
- package/core/scripts/functions/fun-reclaim.sh +297 -0
- package/core/scripts/functions/fun-release.sh +159 -0
- package/core/scripts/functions/fun-remove-project.sh +319 -0
- package/core/scripts/functions/fun-screen.sh +96 -0
- package/core/scripts/functions/fun-spdd.sh +676 -0
- package/core/scripts/functions/fun-specs-share.sh +237 -0
- package/core/scripts/functions/fun-storage.sh +347 -0
- package/core/scripts/functions/fun-testing.sh +329 -0
- package/core/scripts/functions/fun-users.sh +87 -0
- package/core/scripts/functions/fun-version.sh +72 -0
- package/core/scripts/functions/fun-video-edit.sh +48 -0
- package/core/scripts/functions/fun-voice.sh +45 -0
- package/core/scripts/functions/fun-wordpress.sh +59 -0
- package/core/scripts/host-mounts.sh +161 -0
- package/core/scripts/install.sh +602 -0
- package/core/scripts/libs/__pycache__/video_edit.cpython-312.pyc +0 -0
- package/core/scripts/libs/argparse.sh +93 -0
- package/core/scripts/libs/bash-yaml.sh +52 -0
- package/core/scripts/libs/checks_plan.js +312 -0
- package/core/scripts/libs/elevenlabs.py +627 -0
- package/core/scripts/libs/progress-bar.sh +19 -0
- package/core/scripts/libs/rag_index.py +446 -0
- package/core/scripts/libs/video_edit.py +636 -0
- package/core/scripts/libs/voice.py +430 -0
- package/core/scripts/plugins/jira/host-install.sh +119 -0
- package/core/scripts/plugins/jira/host-mounts.txt +2 -0
- package/core/scripts/plugins/upsun/args/arg-upsun.sh +51 -0
- package/core/scripts/plugins/upsun/functions/fun-upsun.sh +304 -0
- package/core/scripts/plugins/upsun/host-install.sh +99 -0
- package/core/scripts/plugins/upsun/host-mounts.txt +1 -0
- package/core/scripts/plugins/upsun/upsun-ai.sh +133 -0
- package/core/scripts/plugins.sh +152 -0
- package/core/scripts/release.sh +69 -0
- package/core/scripts/storage.sh +39 -0
- package/core/scripts/sync-tooling.sh +246 -0
- package/core/scripts/tests/cmd-smoke-test.sh +109 -0
- package/core/scripts/trust-https.sh +257 -0
- package/core/workspace-app/.ddev/commands/web/test-dashboard +49 -0
- package/core/workspace-app/.ddev/config.yaml +400 -0
- package/core/workspace-app/.ddev/docker-compose.dood.yaml +45 -0
- package/core/workspace-app/.ddev/docker-compose.plugin-jira-twg.yaml +26 -0
- package/core/workspace-app/.ddev/docker-compose.plugin-upsun-upsun-cli.yaml +8 -0
- package/core/workspace-app/.ddev/docker-compose.screens.yaml +15 -0
- package/core/workspace-app/.ddev/nginx_full/nginx-site.conf +114 -0
- package/core/workspace-app/.ddev/web-build/Dockerfile.workspace-app +203 -0
- package/core/workspace-app/.ddev/web-build/ddev-shim-ddev +122 -0
- package/{PLUGINS.md → core/workspace-app/PLUGINS.md} +9 -1
- package/core/workspace-app/README.md +380 -0
- package/{actions → core/workspace-app/actions}/ddev.js +0 -1
- package/{actions → core/workspace-app/actions}/rag.js +0 -1
- package/{actions → core/workspace-app/actions}/testing.js +0 -1
- package/{assistant.js → core/workspace-app/assistant.js} +1 -10
- package/{git-info.js → core/workspace-app/git-info.js} +17 -1
- package/core/workspace-app/package.json +29 -0
- package/core/workspace-app/plugin-catalog.js +50 -0
- package/{plugins-page.js → core/workspace-app/plugins-page.js} +28 -2
- package/{server.js → core/workspace-app/server.js} +18 -0
- package/core/workspace-app/tests/features/actions-rail.feature +44 -0
- package/core/workspace-app/tests/features/ai-items.feature +23 -0
- package/core/workspace-app/tests/features/assistant-speech.feature +63 -0
- package/core/workspace-app/tests/features/checks.feature +40 -0
- package/core/workspace-app/tests/features/clone-project.feature +30 -0
- package/core/workspace-app/tests/features/command-authoring.feature +58 -0
- package/core/workspace-app/tests/features/dashboard.feature +57 -0
- package/core/workspace-app/tests/features/menus.feature +29 -0
- package/core/workspace-app/tests/features/plugins/jira/jira.feature +47 -0
- package/core/workspace-app/tests/features/plugins.feature +20 -0
- package/core/workspace-app/tests/features/project-actions.feature +41 -0
- package/core/workspace-app/tests/features/screens.feature +29 -0
- package/core/workspace-app/tests/features/settings.feature +46 -0
- package/core/workspace-app/tests/features/test-reports.feature +32 -0
- package/core/workspace-app/tests/features/theme.feature +40 -0
- package/core/workspace-app/tests/step-definitions/actions-post.steps.js +25 -0
- package/core/workspace-app/tests/step-definitions/assistant-speech.steps.js +157 -0
- package/core/workspace-app/tests/step-definitions/clone-project.steps.js +47 -0
- package/core/workspace-app/tests/step-definitions/preconditions.steps.js +168 -0
- package/core/workspace-app/tests/unit/_isolated-config.js +31 -0
- package/core/workspace-app/tests/unit/accessibility.test.js +124 -0
- package/core/workspace-app/tests/unit/actions-rail-ux.test.js +56 -0
- package/core/workspace-app/tests/unit/actions-rail.test.js +51 -0
- package/core/workspace-app/tests/unit/ai-inventory.test.js +64 -0
- package/core/workspace-app/tests/unit/app-docs.test.js +64 -0
- package/core/workspace-app/tests/unit/assistant-avatar.test.js +110 -0
- package/core/workspace-app/tests/unit/assistant-cast.test.js +105 -0
- package/core/workspace-app/tests/unit/assistant-send-animation.test.js +15 -0
- package/core/workspace-app/tests/unit/assistant-suggestions.test.js +112 -0
- package/core/workspace-app/tests/unit/assistant-switches.test.js +53 -0
- package/core/workspace-app/tests/unit/assistant-views.test.js +146 -0
- package/core/workspace-app/tests/unit/assistant-working-indicator.test.js +49 -0
- package/core/workspace-app/tests/unit/assistant.test.js +61 -0
- package/core/workspace-app/tests/unit/audio-player.test.js +147 -0
- package/core/workspace-app/tests/unit/audio-voice.test.js +634 -0
- package/core/workspace-app/tests/unit/auth-google.test.js +180 -0
- package/core/workspace-app/tests/unit/autostart-watch.test.js +76 -0
- package/core/workspace-app/tests/unit/browsers.test.js +76 -0
- package/core/workspace-app/tests/unit/build-distribution.test.js +125 -0
- package/core/workspace-app/tests/unit/build-order.test.js +153 -0
- package/core/workspace-app/tests/unit/builder-args.test.js +113 -0
- package/core/workspace-app/tests/unit/builders.test.js +158 -0
- package/core/workspace-app/tests/unit/card-state-toggle.test.js +29 -0
- package/core/workspace-app/tests/unit/changelog.test.js +223 -0
- package/core/workspace-app/tests/unit/chat-thread.test.js +92 -0
- package/core/workspace-app/tests/unit/checks-plan.test.js +205 -0
- package/core/workspace-app/tests/unit/claude-args.test.js +182 -0
- package/core/workspace-app/tests/unit/claude-code-here.test.js +15 -0
- package/core/workspace-app/tests/unit/claude-resume.test.js +147 -0
- package/core/workspace-app/tests/unit/claude-screen-options.test.js +84 -0
- package/core/workspace-app/tests/unit/claude-session.test.js +41 -0
- package/core/workspace-app/tests/unit/command-flags.test.js +45 -0
- package/core/workspace-app/tests/unit/command-palette.test.js +105 -0
- package/core/workspace-app/tests/unit/commands-center.test.js +338 -0
- package/core/workspace-app/tests/unit/composer-menu.test.js +187 -0
- package/core/workspace-app/tests/unit/config-yaml.test.js +116 -0
- package/core/workspace-app/tests/unit/confirm-dialog.test.js +69 -0
- package/core/workspace-app/tests/unit/create-card.test.js +85 -0
- package/core/workspace-app/tests/unit/ddev-addons.test.js +144 -0
- package/core/workspace-app/tests/unit/ddev-auth-ssh.test.js +40 -0
- package/core/workspace-app/tests/unit/ddev-custom-commands.test.js +56 -0
- package/core/workspace-app/tests/unit/ddev-diagnose.test.js +57 -0
- package/core/workspace-app/tests/unit/ddev-domains.test.js +179 -0
- package/core/workspace-app/tests/unit/ddev-hostnames.test.js +143 -0
- package/core/workspace-app/tests/unit/ddev-import.test.js +82 -0
- package/core/workspace-app/tests/unit/ddev-mail.test.js +135 -0
- package/core/workspace-app/tests/unit/ddev-menu-and-create.test.js +28 -0
- package/core/workspace-app/tests/unit/ddev-mutagen.test.js +37 -0
- package/core/workspace-app/tests/unit/ddev-options.test.js +51 -0
- package/core/workspace-app/tests/unit/ddev-share.test.js +40 -0
- package/core/workspace-app/tests/unit/ddev-shim.test.js +57 -0
- package/core/workspace-app/tests/unit/ddev-snapshots.test.js +142 -0
- package/core/workspace-app/tests/unit/ddev-start-daemons.test.js +62 -0
- package/core/workspace-app/tests/unit/ddev-xdebug.test.js +41 -0
- package/core/workspace-app/tests/unit/deep-chat-origin.test.js +38 -0
- package/core/workspace-app/tests/unit/density.test.js +52 -0
- package/core/workspace-app/tests/unit/elevenlabs.test.js +453 -0
- package/core/workspace-app/tests/unit/event-log.test.js +102 -0
- package/core/workspace-app/tests/unit/favicon.test.js +68 -0
- package/core/workspace-app/tests/unit/files-browser.test.js +224 -0
- package/core/workspace-app/tests/unit/git-info.test.js +26 -0
- package/core/workspace-app/tests/unit/grid-and-list.test.js +103 -0
- package/core/workspace-app/tests/unit/host-tool-wrapper.test.js +18 -0
- package/core/workspace-app/tests/unit/html-patterns.test.js +51 -0
- package/core/workspace-app/tests/unit/ide-infra.test.js +28 -0
- package/core/workspace-app/tests/unit/images-ai.test.js +201 -0
- package/core/workspace-app/tests/unit/images-create.test.js +59 -0
- package/core/workspace-app/tests/unit/images-providers.test.js +248 -0
- package/core/workspace-app/tests/unit/images-svg.test.js +213 -0
- package/core/workspace-app/tests/unit/images-workspace.test.js +144 -0
- package/core/workspace-app/tests/unit/jobs-fragment.test.js +66 -0
- package/core/workspace-app/tests/unit/media-ux.test.js +164 -0
- package/core/workspace-app/tests/unit/modal-fit.test.js +87 -0
- package/core/workspace-app/tests/unit/modules-load.test.js +23 -0
- package/core/workspace-app/tests/unit/narration.test.js +186 -0
- package/core/workspace-app/tests/unit/navigation-editor.test.js +260 -0
- package/core/workspace-app/tests/unit/navigation.test.js +254 -0
- package/core/workspace-app/tests/unit/new-folder.test.js +121 -0
- package/core/workspace-app/tests/unit/new-screen-form.test.js +149 -0
- package/core/workspace-app/tests/unit/node-menu.test.js +161 -0
- package/core/workspace-app/tests/unit/notes.test.js +128 -0
- package/core/workspace-app/tests/unit/page-structure.test.js +40 -0
- package/core/workspace-app/tests/unit/page-width.test.js +59 -0
- package/core/workspace-app/tests/unit/php-version.test.js +90 -0
- package/core/workspace-app/tests/unit/plan-card.test.js +44 -0
- package/core/workspace-app/tests/unit/plugin-catalog.test.js +31 -0
- package/core/workspace-app/tests/unit/plugin-system.test.js +251 -0
- package/core/workspace-app/tests/unit/plugins-page.test.js +28 -0
- package/core/workspace-app/tests/unit/plugins.test.js +58 -0
- package/core/workspace-app/tests/unit/preconditions.test.js +21 -0
- package/core/workspace-app/tests/unit/project-activity-filter.test.js +180 -0
- package/core/workspace-app/tests/unit/project-make.test.js +143 -0
- package/core/workspace-app/tests/unit/project-name.test.js +202 -0
- package/core/workspace-app/tests/unit/reader.test.js +62 -0
- package/core/workspace-app/tests/unit/release.test.js +92 -0
- package/core/workspace-app/tests/unit/remove-confirm.test.js +37 -0
- package/core/workspace-app/tests/unit/report-pdf.test.js +171 -0
- package/core/workspace-app/tests/unit/round-11.test.js +31 -0
- package/core/workspace-app/tests/unit/round-12.test.js +37 -0
- package/core/workspace-app/tests/unit/row-layout.test.js +585 -0
- package/core/workspace-app/tests/unit/row-menus-fold.test.js +29 -0
- package/core/workspace-app/tests/unit/rows-add-modal.test.js +68 -0
- package/core/workspace-app/tests/unit/screen-runner-queue.test.js +41 -0
- package/core/workspace-app/tests/unit/screen-window.test.js +116 -0
- package/core/workspace-app/tests/unit/screens-beside-running.test.js +19 -0
- package/core/workspace-app/tests/unit/screens-columns.test.js +79 -0
- package/core/workspace-app/tests/unit/screens-menu.test.js +84 -0
- package/core/workspace-app/tests/unit/screens-modal-overlay.test.js +14 -0
- package/core/workspace-app/tests/unit/screens-waiting.test.js +129 -0
- package/core/workspace-app/tests/unit/screens.test.js +93 -0
- package/core/workspace-app/tests/unit/second-storage.test.js +218 -0
- package/core/workspace-app/tests/unit/session-name.test.js +64 -0
- package/core/workspace-app/tests/unit/settings-file.test.js +173 -0
- package/core/workspace-app/tests/unit/settings-nested-list.test.js +54 -0
- package/core/workspace-app/tests/unit/settings-raw.test.js +177 -0
- package/core/workspace-app/tests/unit/shared-prefs.test.js +49 -0
- package/core/workspace-app/tests/unit/speech.test.js +290 -0
- package/core/workspace-app/tests/unit/static-caching.test.js +39 -0
- package/core/workspace-app/tests/unit/status-language.test.js +47 -0
- package/core/workspace-app/tests/unit/tab-modal.test.js +69 -0
- package/core/workspace-app/tests/unit/tab-title.test.js +37 -0
- package/core/workspace-app/tests/unit/template-literals.test.js +52 -0
- package/core/workspace-app/tests/unit/terminal-scrollback.test.js +61 -0
- package/core/workspace-app/tests/unit/terminal-size.test.js +103 -0
- package/core/workspace-app/tests/unit/terminal-style.test.js +84 -0
- package/core/workspace-app/tests/unit/terminal-windows.test.js +154 -0
- package/core/workspace-app/tests/unit/themes.test.js +453 -0
- package/core/workspace-app/tests/unit/tool-labels.test.js +34 -0
- package/core/workspace-app/tests/unit/topbar-icons.test.js +47 -0
- package/core/workspace-app/tests/unit/update-docker-gid.test.js +51 -0
- package/core/workspace-app/tests/unit/user-guide.test.js +60 -0
- package/core/workspace-app/tests/unit/version-check.test.js +98 -0
- package/core/workspace-app/tests/unit/version-link.test.js +50 -0
- package/core/workspace-app/tests/unit/video-borrow.test.js +220 -0
- package/core/workspace-app/tests/unit/video-edit-ask.test.js +93 -0
- package/core/workspace-app/tests/unit/video-edit-short.test.js +42 -0
- package/core/workspace-app/tests/unit/video-edit.test.js +436 -0
- package/core/workspace-app/tests/unit/video-transcript.test.js +237 -0
- package/core/workspace-app/tests/unit/waiting.test.js +48 -0
- package/core/workspace-app/tests/unit/wordpress-odoo-builders.test.js +38 -0
- package/core/workspace-app/tests/unit/workspace-bin.test.js +48 -0
- package/core/workspace-app/tests/unit/workspace-card-tools.test.js +30 -0
- package/core/workspace-app/tests/unit/workspace-delete.test.js +77 -0
- package/core/workspace-app/tests/unit/workspace-types.test.js +68 -0
- package/core/workspace-app/tests/unit/worktree-clone.test.js +250 -0
- package/package.json +38 -36
- /package/{WORKSPACES.md → core/workspace-app/WORKSPACES.md} +0 -0
- /package/{actions → core/workspace-app/actions}/ai-items.js +0 -0
- /package/{actions → core/workspace-app/actions}/audio.js +0 -0
- /package/{actions → core/workspace-app/actions}/build-order.js +0 -0
- /package/{actions → core/workspace-app/actions}/checks.js +0 -0
- /package/{actions → core/workspace-app/actions}/claude-session.js +0 -0
- /package/{actions → core/workspace-app/actions}/commands-center.js +0 -0
- /package/{actions → core/workspace-app/actions}/composer.js +0 -0
- /package/{actions → core/workspace-app/actions}/ddev-addons.js +0 -0
- /package/{actions → core/workspace-app/actions}/ddev-mail.js +0 -0
- /package/{actions → core/workspace-app/actions}/folders.js +0 -0
- /package/{actions → core/workspace-app/actions}/images-svg.js +0 -0
- /package/{actions → core/workspace-app/actions}/images.js +0 -0
- /package/{actions → core/workspace-app/actions}/navigation.js +0 -0
- /package/{actions → core/workspace-app/actions}/node.js +0 -0
- /package/{actions → core/workspace-app/actions}/notes.js +0 -0
- /package/{actions → core/workspace-app/actions}/plugins.js +0 -0
- /package/{actions → core/workspace-app/actions}/project-make.js +0 -0
- /package/{actions → core/workspace-app/actions}/row-actions.js +0 -0
- /package/{actions → core/workspace-app/actions}/screens.js +0 -0
- /package/{actions → core/workspace-app/actions}/video-edit.js +0 -0
- /package/{ai-inventory.js → core/workspace-app/ai-inventory.js} +0 -0
- /package/{ai-items.js → core/workspace-app/ai-items.js} +0 -0
- /package/{assistant-cast.js → core/workspace-app/assistant-cast.js} +0 -0
- /package/{assistant-suggestions.js → core/workspace-app/assistant-suggestions.js} +0 -0
- /package/{audio-voice.js → core/workspace-app/audio-voice.js} +0 -0
- /package/{auth-google.js → core/workspace-app/auth-google.js} +0 -0
- /package/{build-ai.js → core/workspace-app/build-ai.js} +0 -0
- /package/{build-order-page.js → core/workspace-app/build-order-page.js} +0 -0
- /package/{build-order.js → core/workspace-app/build-order.js} +0 -0
- /package/{builder-args.js → core/workspace-app/builder-args.js} +0 -0
- /package/{changelog.js → core/workspace-app/changelog.js} +0 -0
- /package/{chat-threads.js → core/workspace-app/chat-threads.js} +0 -0
- /package/{checks.js → core/workspace-app/checks.js} +0 -0
- /package/{claude-args.js → core/workspace-app/claude-args.js} +0 -0
- /package/{claude-sessions.js → core/workspace-app/claude-sessions.js} +0 -0
- /package/{command-author.js → core/workspace-app/command-author.js} +0 -0
- /package/{command-block.js → core/workspace-app/command-block.js} +0 -0
- /package/{commands → core/workspace-app/commands}/defaults.yml +0 -0
- /package/{commands-page.js → core/workspace-app/commands-page.js} +0 -0
- /package/{commands.js → core/workspace-app/commands.js} +0 -0
- /package/{cucumber.js → core/workspace-app/cucumber.js} +0 -0
- /package/{ddev-addons-page.js → core/workspace-app/ddev-addons-page.js} +0 -0
- /package/{ddev-addons.js → core/workspace-app/ddev-addons.js} +0 -0
- /package/{ddev-custom-commands.js → core/workspace-app/ddev-custom-commands.js} +0 -0
- /package/{ddev-domains.js → core/workspace-app/ddev-domains.js} +0 -0
- /package/{ddev-import.js → core/workspace-app/ddev-import.js} +0 -0
- /package/{ddev-mail-page.js → core/workspace-app/ddev-mail-page.js} +0 -0
- /package/{ddev-mail.js → core/workspace-app/ddev-mail.js} +0 -0
- /package/{ddev-options.js → core/workspace-app/ddev-options.js} +0 -0
- /package/{ddev-snapshots.js → core/workspace-app/ddev-snapshots.js} +0 -0
- /package/{ddev.js → core/workspace-app/ddev.js} +0 -0
- /package/{docs → core/workspace-app/docs}/DEVELOPER-GUIDE.md +0 -0
- /package/{docs → core/workspace-app/docs}/README.md +0 -0
- /package/{docs → core/workspace-app/docs}/REFERENCE.md +0 -0
- /package/{docs → core/workspace-app/docs}/USER-GUIDE.md +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-board.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-coffee.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-colours.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-icons.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-moves.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-pick-cookieman.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-settings-voices.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-status.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/create-card/create-card.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/create-card/start-ai.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/guide-clone-address.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/guide-clone-worktree-off.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/guide-clone-worktree-on.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/guide-ddev-menu.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/guide-domains.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/guide-home.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/guide-ide-menu.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/guide-remove-refusal.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/guide-rows-worktree.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/guide-screens.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/guide-settings.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/guide-workspace-page.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/home/card-settings.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/home/drag-reorder.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/home/edit-card.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/images/claude-code-image.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/images/edit-with-ai.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/images/gallery.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/images/generate-description.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/images/generate-plan.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/images/providers.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/images/svg-form.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/images/svg-result.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/images/upload.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/new-folder/2-form.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/new-folder/8-ddev-options.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/new-folder/files-folder.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/projects/ddev-custom-commands.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/projects/state-chip-stop.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/dashboard-dark.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/dashboard-light.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/dev-dark.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/dev-light.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/docs-dark.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/docs-light.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/ai.svg +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/code.svg +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/docs.svg +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/services.svg +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/sites.svg +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/test-report-dark.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/test-report-light.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/test-scenarios.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/test-steps.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/videos-dark.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/videos-light.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/screens-menu.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/screens-new.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/settings/auth-google-lists.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/settings/delete-1-row.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/settings/delete-2-confirm.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/settings/delete-3-type-name.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/settings/delete-4-type-yes.png +0 -0
- /package/{event-log.js → core/workspace-app/event-log.js} +0 -0
- /package/{files-browser.js → core/workspace-app/files-browser.js} +0 -0
- /package/{folders.js → core/workspace-app/folders.js} +0 -0
- /package/{format.js → core/workspace-app/format.js} +0 -0
- /package/{github-auth.js → core/workspace-app/github-auth.js} +0 -0
- /package/{html.js → core/workspace-app/html.js} +0 -0
- /package/{icons.js → core/workspace-app/icons.js} +0 -0
- /package/{images-ai.js → core/workspace-app/images-ai.js} +0 -0
- /package/{images-create.js → core/workspace-app/images-create.js} +0 -0
- /package/{images-page.js → core/workspace-app/images-page.js} +0 -0
- /package/{images-records.js → core/workspace-app/images-records.js} +0 -0
- /package/{images-render.js → core/workspace-app/images-render.js} +0 -0
- /package/{images-svg-core.js → core/workspace-app/images-svg-core.js} +0 -0
- /package/{images-svg-sanitize.js → core/workspace-app/images-svg-sanitize.js} +0 -0
- /package/{images-svg.js → core/workspace-app/images-svg.js} +0 -0
- /package/{images.js → core/workspace-app/images.js} +0 -0
- /package/{jobs.js → core/workspace-app/jobs.js} +0 -0
- /package/{linger.js → core/workspace-app/linger.js} +0 -0
- /package/{lists.js → core/workspace-app/lists.js} +0 -0
- /package/{media-info.js → core/workspace-app/media-info.js} +0 -0
- /package/{navigation-page.js → core/workspace-app/navigation-page.js} +0 -0
- /package/{navigation.js → core/workspace-app/navigation.js} +0 -0
- /package/{notes.js → core/workspace-app/notes.js} +0 -0
- /package/{palette-index.js → core/workspace-app/palette-index.js} +0 -0
- /package/{playwright.config.js → core/workspace-app/playwright.config.js} +0 -0
- /package/{plugin-install.js → core/workspace-app/plugin-install.js} +0 -0
- /package/{plugin-loader.js → core/workspace-app/plugin-loader.js} +0 -0
- /package/{plugin-manifest.js → core/workspace-app/plugin-manifest.js} +0 -0
- /package/{plugin-settings.js → core/workspace-app/plugin-settings.js} +0 -0
- /package/{plugins → core/workspace-app/plugins}/workspace-notes/index.js +0 -0
- /package/{project-make.js → core/workspace-app/project-make.js} +0 -0
- /package/{project-name.js → core/workspace-app/project-name.js} +0 -0
- /package/{public → core/workspace-app/public}/assistant-avatar-moves.js +0 -0
- /package/{public → core/workspace-app/public}/assistant-avatar-ui.js +0 -0
- /package/{public → core/workspace-app/public}/assistant-avatar.js +0 -0
- /package/{public → core/workspace-app/public}/avatar/druplicon.vrm +0 -0
- /package/{public → core/workspace-app/public}/css/base.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/ai-assistant.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/assistant-avatar.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/assistant-views.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/build-order.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/clone-as-a-git-worktree.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/command-palette.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/commands-center.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/confirm-dialog.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/create-card.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/ddev-menu.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/deep-chat.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/density.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/floating-assistant-workspace-pages.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/folders.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/full-height-left-assistant-sidebar-all-pages.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/full-height-right-actions-rail-pages-that-have-actions.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/grid-and-list.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/help-page.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/hero.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/images.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/layout-rhythm-and-spacing-polish.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/mic-on-any-field-that-wants-typing.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/modal-fit.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/page-width.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/plugins-page.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/project-cards.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/project-rows.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/reordering-the-workspace-cards.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/responsive-home-and-the-workspace-pages.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/running-commands-as-toasts.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/settings-editor-core-config-yml.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/short-results-as-a-toast-at-the-top.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/sound-player.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/suggest-agents-skills-commands.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/tab-modal.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/terminals.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/test-report-media-one-file-per-row-paged.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/top-toolbar-logo-breadcrumb.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/video-edit.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/web-ide-open-with.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/working-indicators.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/workspace-cards.css +0 -0
- /package/{public → core/workspace-app/public}/favicon.ico +0 -0
- /package/{public → core/workspace-app/public}/favicon.png +0 -0
- /package/{public → core/workspace-app/public}/htmx.min.js +0 -0
- /package/{public → core/workspace-app/public}/logo.png +0 -0
- /package/{public → core/workspace-app/public}/media-player.js +0 -0
- /package/{public → core/workspace-app/public}/mic.js +0 -0
- /package/{public → core/workspace-app/public}/narration.js +0 -0
- /package/{public → core/workspace-app/public}/palette.js +0 -0
- /package/{public → core/workspace-app/public}/sortable.js +0 -0
- /package/{public → core/workspace-app/public}/suggest.js +0 -0
- /package/{public → core/workspace-app/public}/terminal.js +0 -0
- /package/{public → core/workspace-app/public}/themes/default/theme.css +0 -0
- /package/{public → core/workspace-app/public}/themes/ember/theme.css +0 -0
- /package/{public → core/workspace-app/public}/themes/press/theme.css +0 -0
- /package/{public → core/workspace-app/public}/themes/slate/theme.css +0 -0
- /package/{public → core/workspace-app/public}/touch-icon.png +0 -0
- /package/{public → core/workspace-app/public}/ui.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/ace/LICENSE.txt +0 -0
- /package/{public → core/workspace-app/public}/vendor/ace/ace.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/ace/ext-language_tools.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/ace/ext-searchbox.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/ace/mode-css.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/ace/mode-html.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/ace/mode-javascript.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/ace/mode-markdown.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/ace/mode-sh.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/ace/mode-yaml.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/ace/theme-textmate.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/ace/theme-tomorrow_night.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/ace/worker-yaml.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/deep-chat.LICENSE +0 -0
- /package/{public → core/workspace-app/public}/vendor/deep-chat.bundle.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/fonts/dm-serif-text-LICENSE.txt +0 -0
- /package/{public → core/workspace-app/public}/vendor/fonts/dm-serif-text-latin-400-italic.woff2 +0 -0
- /package/{public → core/workspace-app/public}/vendor/fonts/dm-serif-text-latin-400-normal.woff2 +0 -0
- /package/{public → core/workspace-app/public}/vendor/fonts/figtree-LICENSE.txt +0 -0
- /package/{public → core/workspace-app/public}/vendor/fonts/figtree-latin-400-italic.woff2 +0 -0
- /package/{public → core/workspace-app/public}/vendor/fonts/figtree-latin-400-normal.woff2 +0 -0
- /package/{public → core/workspace-app/public}/vendor/fonts/figtree-latin-600-italic.woff2 +0 -0
- /package/{public → core/workspace-app/public}/vendor/fonts/figtree-latin-600-normal.woff2 +0 -0
- /package/{public → core/workspace-app/public}/vendor/fonts/jetbrains-mono-LICENSE.txt +0 -0
- /package/{public → core/workspace-app/public}/vendor/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
- /package/{public → core/workspace-app/public}/vendor/fonts/ubuntu-mono-LICENSE.txt +0 -0
- /package/{public → core/workspace-app/public}/vendor/fonts/ubuntu-mono-latin-wght-normal.woff2 +0 -0
- /package/{public → core/workspace-app/public}/vendor/tabler-LICENSE.txt +0 -0
- /package/{public → core/workspace-app/public}/vendor/tabler-sprite.svg +0 -0
- /package/{public → core/workspace-app/public}/vendor/three/LICENSE +0 -0
- /package/{public → core/workspace-app/public}/vendor/three/addons/loaders/GLTFLoader.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/three/addons/utils/BufferGeometryUtils.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/three/addons/utils/SkeletonUtils.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/three/three.core.min.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/three/three.module.min.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/three-vrm/LICENSE +0 -0
- /package/{public → core/workspace-app/public}/vendor/three-vrm/three-vrm.module.min.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/uikit-icons.min.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/uikit.min.css +0 -0
- /package/{public → core/workspace-app/public}/vendor/uikit.min.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/bookmark-outlined.svg +0 -0
- /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/headset.svg +0 -0
- /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/menu.svg +0 -0
- /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/pause.svg +0 -0
- /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/play.svg +0 -0
- /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/share.svg +0 -0
- /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/sound-off.svg +0 -0
- /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/sound-on.svg +0 -0
- /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/stop.svg +0 -0
- /package/{public → core/workspace-app/public}/vendor/xterm/addon-fit.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/xterm/addon-unicode11.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/xterm/addon-web-links.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/xterm/xterm.css +0 -0
- /package/{public → core/workspace-app/public}/vendor/xterm/xterm.js +0 -0
- /package/{rag.js → core/workspace-app/rag.js} +0 -0
- /package/{reader.js → core/workspace-app/reader.js} +0 -0
- /package/{repo-blobs.js → core/workspace-app/repo-blobs.js} +0 -0
- /package/{report-pdf.js → core/workspace-app/report-pdf.js} +0 -0
- /package/{row-layout.js → core/workspace-app/row-layout.js} +0 -0
- /package/{screenrc → core/workspace-app/screenrc} +0 -0
- /package/{screens.js → core/workspace-app/screens.js} +0 -0
- /package/{scripts → core/workspace-app/scripts}/code-server-start.sh +0 -0
- /package/{scripts → core/workspace-app/scripts}/images-claude-finish.js +0 -0
- /package/{scripts → core/workspace-app/scripts}/screen-host.sh +0 -0
- /package/{scripts → core/workspace-app/scripts}/screen-runner.js +0 -0
- /package/{scripts → core/workspace-app/scripts}/screen-then.js +0 -0
- /package/{scripts → core/workspace-app/scripts}/update.sh +0 -0
- /package/{settings-file.js → core/workspace-app/settings-file.js} +0 -0
- /package/{speech.js → core/workspace-app/speech.js} +0 -0
- /package/{tab-modal.js → core/workspace-app/tab-modal.js} +0 -0
- /package/{test-reports.js → core/workspace-app/test-reports.js} +0 -0
- /package/{themes.js → core/workspace-app/themes.js} +0 -0
- /package/{transcripts.js → core/workspace-app/transcripts.js} +0 -0
- /package/{urls.js → core/workspace-app/urls.js} +0 -0
- /package/{validate.js → core/workspace-app/validate.js} +0 -0
- /package/{version-check.js → core/workspace-app/version-check.js} +0 -0
- /package/{video-edit.js → core/workspace-app/video-edit.js} +0 -0
- /package/{waiting.js → core/workspace-app/waiting.js} +0 -0
- /package/{workspace-card-tools.js → core/workspace-app/workspace-card-tools.js} +0 -0
- /package/{workspace-delete.js → core/workspace-app/workspace-delete.js} +0 -0
- /package/{workspace-types.js → core/workspace-app/workspace-types.js} +0 -0
- /package/{workspaces.js → core/workspace-app/workspaces.js} +0 -0
|
@@ -0,0 +1,724 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# Penpot, self-hosted inside DDEV.
|
|
4
|
+
#
|
|
5
|
+
# The whole Penpot stack runs as DDEV custom compose services on the project's
|
|
6
|
+
# `default` network, so they resolve each other by service name, and DDEV's own
|
|
7
|
+
# web container (nginx) reverse-proxies the site to `penpot-frontend:8080`. DDEV
|
|
8
|
+
# owns the router and the TLS certificate, which is what makes
|
|
9
|
+
# https://<project>.ddev.site work with no host ports and no standalone
|
|
10
|
+
# docker-compose stack to keep in sync.
|
|
11
|
+
#
|
|
12
|
+
# Browser ──https──▶ ddev-router ──▶ ddev-<project>-web (nginx reverse proxy)
|
|
13
|
+
# └─▶ penpot-frontend:8080
|
|
14
|
+
# penpot-frontend ─▶ penpot-backend ─▶ penpot-postgres (15)
|
|
15
|
+
# │ └──────────▶ penpot-valkey (redis)
|
|
16
|
+
# └─▶ penpot-exporter penpot-mailcatch (SMTP catcher)
|
|
17
|
+
#
|
|
18
|
+
# Reference: products/ai_penpot/docs/penpot-on-ddev.md, which this command
|
|
19
|
+
# automates end to end — it used to be a page of copy-paste.
|
|
20
|
+
#
|
|
21
|
+
# The project is NOT a Drupal site: it is `--project-type=php` with settings
|
|
22
|
+
# management disabled and no docroot, because nothing in it is served from disk.
|
|
23
|
+
|
|
24
|
+
# The last-resort flag set, used only when the workspace settings carry none.
|
|
25
|
+
# `enable-plugins` is what the Penpot MCP needs, and
|
|
26
|
+
# `enable-access-tokens` is what the AI Penpot Drupal module needs — without the
|
|
27
|
+
# latter a token is silently ignored (requests resolve to Anonymous, not a 401),
|
|
28
|
+
# which is a bad afternoon. Registration is open and email verification off, so
|
|
29
|
+
# registering logs you straight in on a local instance.
|
|
30
|
+
PENPOT_DEFAULT_FLAGS="disable-email-verification enable-smtp enable-prepl-server disable-secure-session-cookies enable-registration enable-plugins enable-access-tokens";
|
|
31
|
+
|
|
32
|
+
# parse_yaml keeps a value's own quotes, so `version: "2.16"` reaches the shell as the
|
|
33
|
+
# five-character string `"2.16"` — which lands in an image tag as `penpotapp/frontend:"2.16"`
|
|
34
|
+
# and, worse, makes `secret_key: ""` a NON-empty string, so every `${x:-fallback}` default
|
|
35
|
+
# silently stops working. Strip one layer of matching quotes from anything read from settings.
|
|
36
|
+
function penpot_setting() {
|
|
37
|
+
local value="$1";
|
|
38
|
+
case "${value}" in
|
|
39
|
+
\"*\") value="${value#\"}"; value="${value%\"}" ;;
|
|
40
|
+
\'*\') value="${value#\'}"; value="${value%\'}" ;;
|
|
41
|
+
esac
|
|
42
|
+
printf '%s' "${value}";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# Write .ddev/docker-compose.penpot.yaml for one project.
|
|
46
|
+
#
|
|
47
|
+
# $1 the project directory, $2 the public URI, $3 PENPOT_FLAGS, $4 the secret key,
|
|
48
|
+
# $5 the host port for the mail catcher
|
|
49
|
+
function penpot_write_compose() {
|
|
50
|
+
local project_dir="$1" public_uri="$2" flags="$3" secret_key="$4" mail_port="$5";
|
|
51
|
+
|
|
52
|
+
mkdir -p "${project_dir}/.ddev" ;
|
|
53
|
+
|
|
54
|
+
cat > "${project_dir}/.ddev/docker-compose.penpot.yaml" <<COMPOSEEOF
|
|
55
|
+
## Penpot self-hosted, orchestrated by DDEV.
|
|
56
|
+
## Written by cmd-penpot-project.sh — re-run that command rather than editing by hand.
|
|
57
|
+
## The DDEV web container (nginx) reverse-proxies ${public_uri} -> penpot-frontend:8080.
|
|
58
|
+
## Every service is on the DDEV project's 'default' network, so they resolve each other by name.
|
|
59
|
+
|
|
60
|
+
volumes:
|
|
61
|
+
penpot_postgres_v15:
|
|
62
|
+
penpot_assets:
|
|
63
|
+
|
|
64
|
+
services:
|
|
65
|
+
penpot-frontend:
|
|
66
|
+
container_name: ddev-\${DDEV_SITENAME}-penpot-frontend
|
|
67
|
+
image: "penpotapp/frontend:\${PENPOT_VERSION:-${PENPOT_VERSION}}"
|
|
68
|
+
restart: "no"
|
|
69
|
+
labels:
|
|
70
|
+
com.ddev.site-name: \${DDEV_SITENAME}
|
|
71
|
+
com.ddev.approot: \${DDEV_APPROOT}
|
|
72
|
+
volumes:
|
|
73
|
+
- penpot_assets:/opt/data/assets
|
|
74
|
+
depends_on:
|
|
75
|
+
- penpot-backend
|
|
76
|
+
- penpot-exporter
|
|
77
|
+
environment:
|
|
78
|
+
PENPOT_FLAGS: ${flags}
|
|
79
|
+
PENPOT_PUBLIC_URI: ${public_uri}
|
|
80
|
+
PENPOT_HTTP_SERVER_MAX_BODY_SIZE: "367001600"
|
|
81
|
+
PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE: "367001600"
|
|
82
|
+
|
|
83
|
+
penpot-backend:
|
|
84
|
+
container_name: ddev-\${DDEV_SITENAME}-penpot-backend
|
|
85
|
+
image: "penpotapp/backend:\${PENPOT_VERSION:-${PENPOT_VERSION}}"
|
|
86
|
+
restart: "no"
|
|
87
|
+
labels:
|
|
88
|
+
com.ddev.site-name: \${DDEV_SITENAME}
|
|
89
|
+
com.ddev.approot: \${DDEV_APPROOT}
|
|
90
|
+
volumes:
|
|
91
|
+
- penpot_assets:/opt/data/assets
|
|
92
|
+
depends_on:
|
|
93
|
+
penpot-postgres:
|
|
94
|
+
condition: service_healthy
|
|
95
|
+
penpot-valkey:
|
|
96
|
+
condition: service_healthy
|
|
97
|
+
environment:
|
|
98
|
+
PENPOT_FLAGS: ${flags}
|
|
99
|
+
PENPOT_PUBLIC_URI: ${public_uri}
|
|
100
|
+
PENPOT_SECRET_KEY: ${secret_key}
|
|
101
|
+
PENPOT_HTTP_SERVER_MAX_BODY_SIZE: "367001600"
|
|
102
|
+
PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE: "367001600"
|
|
103
|
+
PENPOT_DATABASE_URI: postgresql://penpot-postgres/penpot
|
|
104
|
+
PENPOT_DATABASE_USERNAME: penpot
|
|
105
|
+
PENPOT_DATABASE_PASSWORD: penpot
|
|
106
|
+
PENPOT_REDIS_URI: redis://penpot-valkey/0
|
|
107
|
+
PENPOT_OBJECTS_STORAGE_BACKEND: fs
|
|
108
|
+
PENPOT_OBJECTS_STORAGE_FS_DIRECTORY: /opt/data/assets
|
|
109
|
+
PENPOT_TELEMETRY_ENABLED: "false"
|
|
110
|
+
PENPOT_SMTP_DEFAULT_FROM: no-reply@example.com
|
|
111
|
+
PENPOT_SMTP_DEFAULT_REPLY_TO: no-reply@example.com
|
|
112
|
+
PENPOT_SMTP_HOST: penpot-mailcatch
|
|
113
|
+
PENPOT_SMTP_PORT: "1025"
|
|
114
|
+
PENPOT_SMTP_TLS: "false"
|
|
115
|
+
PENPOT_SMTP_SSL: "false"
|
|
116
|
+
|
|
117
|
+
penpot-exporter:
|
|
118
|
+
container_name: ddev-\${DDEV_SITENAME}-penpot-exporter
|
|
119
|
+
image: "penpotapp/exporter:\${PENPOT_VERSION:-${PENPOT_VERSION}}"
|
|
120
|
+
restart: "no"
|
|
121
|
+
labels:
|
|
122
|
+
com.ddev.site-name: \${DDEV_SITENAME}
|
|
123
|
+
com.ddev.approot: \${DDEV_APPROOT}
|
|
124
|
+
depends_on:
|
|
125
|
+
penpot-valkey:
|
|
126
|
+
condition: service_healthy
|
|
127
|
+
environment:
|
|
128
|
+
PENPOT_SECRET_KEY: ${secret_key}
|
|
129
|
+
## The exporter talks to the frontend container directly, inside the network.
|
|
130
|
+
PENPOT_PUBLIC_URI: http://penpot-frontend:8080
|
|
131
|
+
PENPOT_REDIS_URI: redis://penpot-valkey/0
|
|
132
|
+
|
|
133
|
+
penpot-postgres:
|
|
134
|
+
container_name: ddev-\${DDEV_SITENAME}-penpot-postgres
|
|
135
|
+
image: "postgres:15"
|
|
136
|
+
restart: "no"
|
|
137
|
+
stop_signal: SIGINT
|
|
138
|
+
labels:
|
|
139
|
+
com.ddev.site-name: \${DDEV_SITENAME}
|
|
140
|
+
com.ddev.approot: \${DDEV_APPROOT}
|
|
141
|
+
healthcheck:
|
|
142
|
+
test: ["CMD-SHELL", "pg_isready -U penpot"]
|
|
143
|
+
interval: 2s
|
|
144
|
+
timeout: 10s
|
|
145
|
+
retries: 5
|
|
146
|
+
start_period: 2s
|
|
147
|
+
volumes:
|
|
148
|
+
- penpot_postgres_v15:/var/lib/postgresql/data
|
|
149
|
+
environment:
|
|
150
|
+
POSTGRES_INITDB_ARGS: "--data-checksums"
|
|
151
|
+
POSTGRES_DB: penpot
|
|
152
|
+
POSTGRES_USER: penpot
|
|
153
|
+
POSTGRES_PASSWORD: penpot
|
|
154
|
+
|
|
155
|
+
penpot-valkey:
|
|
156
|
+
container_name: ddev-\${DDEV_SITENAME}-penpot-valkey
|
|
157
|
+
image: "valkey/valkey:8.1"
|
|
158
|
+
restart: "no"
|
|
159
|
+
labels:
|
|
160
|
+
com.ddev.site-name: \${DDEV_SITENAME}
|
|
161
|
+
com.ddev.approot: \${DDEV_APPROOT}
|
|
162
|
+
healthcheck:
|
|
163
|
+
test: ["CMD-SHELL", "valkey-cli ping | grep PONG"]
|
|
164
|
+
interval: 1s
|
|
165
|
+
timeout: 3s
|
|
166
|
+
retries: 5
|
|
167
|
+
start_period: 3s
|
|
168
|
+
|
|
169
|
+
penpot-mailcatch:
|
|
170
|
+
container_name: ddev-\${DDEV_SITENAME}-penpot-mailcatch
|
|
171
|
+
image: "sj26/mailcatcher:latest"
|
|
172
|
+
restart: "no"
|
|
173
|
+
labels:
|
|
174
|
+
com.ddev.site-name: \${DDEV_SITENAME}
|
|
175
|
+
com.ddev.approot: \${DDEV_APPROOT}
|
|
176
|
+
environment:
|
|
177
|
+
- VIRTUAL_HOST=\$DDEV_HOSTNAME
|
|
178
|
+
- HTTP_EXPOSE=${mail_port}:1080
|
|
179
|
+
- HTTPS_EXPOSE=$((mail_port + 363)):1080
|
|
180
|
+
COMPOSEEOF
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
# Write the nginx site config that turns the DDEV web container into the reverse
|
|
184
|
+
# proxy. A full replacement (nginx_full), because none of the default PHP site
|
|
185
|
+
# config applies to a project that serves no files.
|
|
186
|
+
function penpot_write_nginx() {
|
|
187
|
+
local project_dir="$1";
|
|
188
|
+
|
|
189
|
+
mkdir -p "${project_dir}/.ddev/nginx_full" ;
|
|
190
|
+
|
|
191
|
+
cat > "${project_dir}/.ddev/nginx_full/nginx-site.conf" <<'NGINXEOF'
|
|
192
|
+
# DDEV web container nginx: reverse-proxy everything to the Penpot frontend container.
|
|
193
|
+
# Full replacement of the default site config (nginx_full).
|
|
194
|
+
# Written by cmd-penpot-project.sh — re-run that command rather than editing by hand.
|
|
195
|
+
|
|
196
|
+
server {
|
|
197
|
+
listen 80 default_server;
|
|
198
|
+
listen 443 ssl default_server;
|
|
199
|
+
|
|
200
|
+
root /var/www/html;
|
|
201
|
+
|
|
202
|
+
ssl_certificate /etc/ssl/certs/master.crt;
|
|
203
|
+
ssl_certificate_key /etc/ssl/certs/master.key;
|
|
204
|
+
|
|
205
|
+
include /etc/nginx/monitoring.conf;
|
|
206
|
+
|
|
207
|
+
# Penpot handles large asset/media uploads.
|
|
208
|
+
client_max_body_size 400M;
|
|
209
|
+
|
|
210
|
+
# Real IP from the DDEV router.
|
|
211
|
+
include /etc/nginx/common.d/*.conf;
|
|
212
|
+
|
|
213
|
+
# The Penpot MCP server runs INSIDE this project's web container network namespace
|
|
214
|
+
# (cmd-penpot-mcp.sh starts it with --network container:), so its three ports are on this
|
|
215
|
+
# container's loopback and are proxied onto the instance's own https origin. That is the only
|
|
216
|
+
# arrangement that works here: the servers bind 127.0.0.1 (so no container can reach them
|
|
217
|
+
# across the network), the editor page is https (so an http://localhost manifest is mixed
|
|
218
|
+
# content and Penpot refuses it: "The plugin doesn't exist or the URL is not correct"), and
|
|
219
|
+
# this machine's firewall drops container-to-host traffic (so a socat relay on the host is
|
|
220
|
+
# unreachable too). Same origin, https and wss, nothing published on the host.
|
|
221
|
+
#
|
|
222
|
+
# The upstreams are [::1], not 127.0.0.1: these servers bind IPv6 loopback ONLY, so an IPv4
|
|
223
|
+
# upstream is refused and nginx answers 502 while the servers are plainly running and logging.
|
|
224
|
+
location /mcp-plugin/ {
|
|
225
|
+
proxy_pass http://[::1]:4400/;
|
|
226
|
+
proxy_http_version 1.1;
|
|
227
|
+
# Vite's preview server refuses an unknown Host with 403 ("This host is not allowed"), so
|
|
228
|
+
# the upstream keeps its own name and the browser-facing origin stays this instance.
|
|
229
|
+
proxy_set_header Host localhost:4400;
|
|
230
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
231
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
232
|
+
proxy_set_header Connection "upgrade";
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
location /mcp-bridge {
|
|
236
|
+
proxy_pass http://[::1]:4402;
|
|
237
|
+
proxy_http_version 1.1;
|
|
238
|
+
proxy_set_header Host $host;
|
|
239
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
240
|
+
proxy_set_header Connection "upgrade";
|
|
241
|
+
proxy_read_timeout 3600s;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
location /mcp {
|
|
245
|
+
proxy_pass http://[::1]:4401;
|
|
246
|
+
proxy_http_version 1.1;
|
|
247
|
+
proxy_set_header Host $host;
|
|
248
|
+
proxy_set_header Connection "";
|
|
249
|
+
proxy_buffering off;
|
|
250
|
+
proxy_read_timeout 3600s;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
location / {
|
|
254
|
+
proxy_pass http://penpot-frontend:8080;
|
|
255
|
+
proxy_http_version 1.1;
|
|
256
|
+
|
|
257
|
+
proxy_set_header Host $host;
|
|
258
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
259
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
260
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
261
|
+
proxy_set_header X-Forwarded-Host $host;
|
|
262
|
+
|
|
263
|
+
# WebSocket support (Penpot's live notifications).
|
|
264
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
265
|
+
proxy_set_header Connection "upgrade";
|
|
266
|
+
|
|
267
|
+
proxy_connect_timeout 75s;
|
|
268
|
+
proxy_read_timeout 3600s;
|
|
269
|
+
proxy_send_timeout 3600s;
|
|
270
|
+
proxy_buffering off;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
include /etc/nginx/server.d/*.conf;
|
|
274
|
+
}
|
|
275
|
+
NGINXEOF
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
# Register the first account over Penpot's RPC API, so the instance is usable without a
|
|
279
|
+
# browser detour. Two calls: prepare-register-profile returns a signed token (it takes the
|
|
280
|
+
# full name too — leaving it out fails validation there, not at the second step), then
|
|
281
|
+
# register-profile creates the profile and its default project.
|
|
282
|
+
#
|
|
283
|
+
# An existing email is not a failure: `email-already-exists` is what a rebuild that kept its
|
|
284
|
+
# volumes reports, and the account it names is the one you already use.
|
|
285
|
+
#
|
|
286
|
+
# $1 base url, $2 fullname, $3 email, $4 password
|
|
287
|
+
function penpot_register_first_account() {
|
|
288
|
+
local base_url="$1" fullname="$2" email="$3" password="$4";
|
|
289
|
+
local prepared token registered;
|
|
290
|
+
|
|
291
|
+
# The backend answers before it is ready to register, so wait for the API itself.
|
|
292
|
+
local waited=0;
|
|
293
|
+
until curl -sk -o /dev/null --max-time 5 "${base_url}/api/rpc/command/get-profile" -X POST -H 'Accept: application/json' -d '{}' ; do
|
|
294
|
+
sleep 3 ; waited=$((waited + 3));
|
|
295
|
+
if [ "${waited}" -ge 120 ] ; then
|
|
296
|
+
echo "The Penpot API did not answer within 2 minutes — register in the browser instead.";
|
|
297
|
+
return 1 ;
|
|
298
|
+
fi
|
|
299
|
+
done
|
|
300
|
+
|
|
301
|
+
prepared=$(curl -sk -X POST "${base_url}/api/rpc/command/prepare-register-profile" \
|
|
302
|
+
-H 'Accept: application/json' -H 'Content-Type: application/json' -H "Origin: ${base_url}" \
|
|
303
|
+
--data-binary @- <<JSONEOF
|
|
304
|
+
{"email": "${email}", "password": "${password}", "fullname": "${fullname}"}
|
|
305
|
+
JSONEOF
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
if printf '%s' "${prepared}" | grep -q 'email-already-exists' ; then
|
|
309
|
+
echo "${email} is already registered on this instance — keeping it.";
|
|
310
|
+
return 0 ;
|
|
311
|
+
fi
|
|
312
|
+
|
|
313
|
+
token=$(printf '%s' "${prepared}" | sed -n 's/.*"token":"\([^"]*\)".*/\1/p');
|
|
314
|
+
if [ -z "${token}" ] ; then
|
|
315
|
+
echo "Could not prepare the registration:";
|
|
316
|
+
printf ' %s\n' "${prepared}";
|
|
317
|
+
return 1 ;
|
|
318
|
+
fi
|
|
319
|
+
|
|
320
|
+
registered=$(curl -sk -X POST "${base_url}/api/rpc/command/register-profile" \
|
|
321
|
+
-H 'Accept: application/json' -H 'Content-Type: application/json' -H "Origin: ${base_url}" \
|
|
322
|
+
--data-binary @- <<JSONEOF
|
|
323
|
+
{"token": "${token}", "fullname": "${fullname}", "acceptTermsAndPrivacy": true, "acceptNewsletterSubscription": false}
|
|
324
|
+
JSONEOF
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
if printf '%s' "${registered}" | grep -q '"isActive":true' ; then
|
|
328
|
+
echo "Registered ${fullname} <${email}> and its default project.";
|
|
329
|
+
return 0 ;
|
|
330
|
+
fi
|
|
331
|
+
|
|
332
|
+
echo "Registration did not complete:";
|
|
333
|
+
printf ' %s\n' "${registered}";
|
|
334
|
+
return 1 ;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
# The next free host port from $1 upwards, as seen by docker. Several Penpot instances is the
|
|
338
|
+
# normal case — one per design system — and each mail catcher is published on the host, so a
|
|
339
|
+
# fixed 1080 would make the second instance fail to start with "port is already in use".
|
|
340
|
+
# The pair 1080/1443 keeps its shape: the HTTPS port is always the HTTP one plus 363.
|
|
341
|
+
function penpot_free_mail_port() {
|
|
342
|
+
local port="$1" taken;
|
|
343
|
+
taken="$(docker ps --format '{{.Ports}}' 2>/dev/null)";
|
|
344
|
+
while printf '%s' "${taken}" | grep -q ":${port}->" || printf '%s' "${taken}" | grep -q ":$((port + 363))->" ; do
|
|
345
|
+
port=$((port + 1));
|
|
346
|
+
if [ "${port}" -gt 1179 ] ; then
|
|
347
|
+
echo "1080" ; return 0 ; # give up scanning and let ddev report the conflict
|
|
348
|
+
fi
|
|
349
|
+
done
|
|
350
|
+
printf '%s' "${port}";
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
# Build (or rebuild) a Penpot instance in the calling workspace folder.
|
|
354
|
+
#
|
|
355
|
+
# The calling cmd-*.sh sets nothing but the workspace: everything else comes from
|
|
356
|
+
# the arguments in args/arg-penpot.sh.
|
|
357
|
+
function build_penpot() {
|
|
358
|
+
|
|
359
|
+
# The workspace settings hold the defaults; an argument that was actually given wins. The
|
|
360
|
+
# sentinel is what tells the two apart — argparse cannot report "not given" on its own.
|
|
361
|
+
if [ "${PROJECT_NAME}" == '_settings_' ] ; then PROJECT_NAME="$(penpot_setting "${penpot_project}")"; PROJECT_NAME="${PROJECT_NAME:-penpot}"; fi
|
|
362
|
+
if [ "${PENPOT_VERSION}" == '_settings_' ] ; then PENPOT_VERSION="$(penpot_setting "${penpot_version}")"; PENPOT_VERSION="${PENPOT_VERSION:-2.16}"; fi
|
|
363
|
+
if [ "${FLAGS}" == '_settings_' ] ; then FLAGS="$(penpot_setting "${penpot_flags}")"; FLAGS="${FLAGS:-${PENPOT_DEFAULT_FLAGS}}"; fi
|
|
364
|
+
if [ "${SECRET_KEY}" == '_settings_' ] ; then SECRET_KEY="$(penpot_setting "${penpot_secret_key}")"; fi
|
|
365
|
+
# The account falls back to the same `account:` block the Drupal installs use, so a machine
|
|
366
|
+
# states its developer identity once.
|
|
367
|
+
if [ "${FULLNAME}" == '_settings_' ] ; then FULLNAME="$(penpot_setting "${penpot_account_fullname}")"; FULLNAME="${FULLNAME:-$(penpot_setting "${account_name}")}"; fi
|
|
368
|
+
if [ "${EMAIL}" == '_settings_' ] ; then EMAIL="$(penpot_setting "${penpot_account_email}")"; EMAIL="${EMAIL:-$(penpot_setting "${account_mail}")}"; fi
|
|
369
|
+
if [ "${PASSWORD}" == '_settings_' ] ; then PASSWORD="$(penpot_setting "${penpot_account_password}")"; PASSWORD="${PASSWORD:-$(penpot_setting "${account_pass}")}"; fi
|
|
370
|
+
|
|
371
|
+
local base_url="https://${PROJECT_NAME}.ddev.site";
|
|
372
|
+
local hub_url="https://${PROJECT_NAME}.${doc_name}.workspace.ddev.site";
|
|
373
|
+
local project_dir="${WORKSPACE_ROOT}/${doc_name}/${PROJECT_NAME}";
|
|
374
|
+
local flags secret_key mail_port public_uri other_url;
|
|
375
|
+
|
|
376
|
+
flags="${FLAGS}";
|
|
377
|
+
|
|
378
|
+
# A per-project key, not a literal shared by every instance the tooling builds:
|
|
379
|
+
# the session cookies of one instance must not be forgeable from another's
|
|
380
|
+
# committed config. Derived, so a rebuild of the same project keeps its
|
|
381
|
+
# sessions valid.
|
|
382
|
+
secret_key="${SECRET_KEY}";
|
|
383
|
+
if [ -z "${secret_key}" ] ; then
|
|
384
|
+
secret_key="$(printf 'penpot-%s-%s' "${PROJECT_NAME}" "$(hostname)" | sha256sum | cut -c1-48)";
|
|
385
|
+
fi
|
|
386
|
+
|
|
387
|
+
# Penpot's frontend fetches its API from PENPOT_PUBLIC_URI, so the instance works on exactly
|
|
388
|
+
# ONE origin: opened on any other host the API calls are cross-origin and CORS blocks every
|
|
389
|
+
# one of them, which shows up as a blank page rather than an error. The hub hostname is the
|
|
390
|
+
# default because that is the address the workspace hands out — one Penpot per design system,
|
|
391
|
+
# penpot-website_starter.designs… beside penpot-haven.designs… — and the canonical
|
|
392
|
+
# <project>.ddev.site stays useful for the RPC API, where there is no browser to enforce CORS.
|
|
393
|
+
public_uri="${PUBLIC_URI}";
|
|
394
|
+
if [ "${public_uri}" == '_settings_' ] ; then
|
|
395
|
+
public_uri="$(penpot_setting "${penpot_public_uri}")";
|
|
396
|
+
public_uri="${public_uri:-hub}";
|
|
397
|
+
fi
|
|
398
|
+
case "${public_uri}" in
|
|
399
|
+
hub) public_uri="${hub_url}"; other_url="${base_url}" ;;
|
|
400
|
+
canonical) public_uri="${base_url}"; other_url="${hub_url}" ;;
|
|
401
|
+
*) other_url="" ;;
|
|
402
|
+
esac
|
|
403
|
+
|
|
404
|
+
mail_port="${MAIL_PORT}";
|
|
405
|
+
if [ "${mail_port}" == '_settings_' ] ; then
|
|
406
|
+
mail_port="$(penpot_setting "${penpot_mail_port}")";
|
|
407
|
+
mail_port="${mail_port:-1080}";
|
|
408
|
+
fi
|
|
409
|
+
mail_port="$(penpot_free_mail_port "${mail_port}")";
|
|
410
|
+
|
|
411
|
+
cd ${WORKSPACE_ROOT}/${doc_name} || exit 1 ;
|
|
412
|
+
|
|
413
|
+
if [ -d "${PROJECT_NAME}" ] ; then
|
|
414
|
+
if [ "${FORCE}" != 'yes' ] ; then
|
|
415
|
+
echo "";
|
|
416
|
+
echo "${PROJECT_NAME} already exists in ${WORKSPACE_ROOT}/${doc_name}.";
|
|
417
|
+
echo " Start it with: cd ${project_dir} && ddev start";
|
|
418
|
+
echo " Rebuild it with: bash cmd-penpot-project.sh ${PROJECT_NAME} --force";
|
|
419
|
+
echo " (--force deletes the Penpot volumes too, so the designs in it go with them.)";
|
|
420
|
+
exit 1 ;
|
|
421
|
+
fi
|
|
422
|
+
echo "Removing the existing ${PROJECT_NAME} instance, its database and its assets.";
|
|
423
|
+
# -O removes the project's volumes, which for this project means the Penpot
|
|
424
|
+
# Postgres data and the uploaded assets: the designs are IN there.
|
|
425
|
+
(cd "${project_dir}" && ddev delete -y -O) ;
|
|
426
|
+
rm -rf "${project_dir}" ;
|
|
427
|
+
fi
|
|
428
|
+
|
|
429
|
+
mkdir -p "${project_dir}" ;
|
|
430
|
+
cd "${project_dir}" || exit 1 ;
|
|
431
|
+
|
|
432
|
+
# No docroot and no settings management: this project serves no files of its
|
|
433
|
+
# own, it only proxies.
|
|
434
|
+
ddev config --project-name=${PROJECT_NAME} --project-type=php --docroot="" --disable-settings-management ;
|
|
435
|
+
|
|
436
|
+
penpot_write_compose "${project_dir}" "${public_uri}" "${flags}" "${secret_key}" "${mail_port}" ;
|
|
437
|
+
penpot_write_nginx "${project_dir}" ;
|
|
438
|
+
|
|
439
|
+
echo "";
|
|
440
|
+
echo "Starting Penpot ${PENPOT_VERSION} — the first run pulls the images, which takes a few minutes.";
|
|
441
|
+
ddev start -y ;
|
|
442
|
+
|
|
443
|
+
if [ "${NO_REGISTER}" == 'yes' ] ; then
|
|
444
|
+
echo "";
|
|
445
|
+
echo "Skipping registration — register the first account at ${base_url}/#/auth/register.";
|
|
446
|
+
else
|
|
447
|
+
echo "";
|
|
448
|
+
echo "Registering the first account.";
|
|
449
|
+
penpot_register_first_account "${public_uri}" "${FULLNAME}" "${EMAIL}" "${PASSWORD}" ;
|
|
450
|
+
fi
|
|
451
|
+
|
|
452
|
+
echo "";
|
|
453
|
+
echo "*---------------------------------------------------------------------*";
|
|
454
|
+
echo " Penpot ${PENPOT_VERSION} is ready — open it here:";
|
|
455
|
+
echo " ${public_uri}";
|
|
456
|
+
if [ -n "${other_url}" ] ; then
|
|
457
|
+
echo "";
|
|
458
|
+
echo " ${other_url} reaches the same instance for the RPC API, but NOT in a browser:";
|
|
459
|
+
echo " Penpot serves one origin, and from the other one CORS blocks every API call.";
|
|
460
|
+
fi
|
|
461
|
+
echo "";
|
|
462
|
+
if [ "${NO_REGISTER}" == 'yes' ] ; then
|
|
463
|
+
echo " Register the first account (it logs you straight in, no email step):";
|
|
464
|
+
echo " ${public_uri}/#/auth/register";
|
|
465
|
+
else
|
|
466
|
+
echo " Log in at ${public_uri}/#/auth/login";
|
|
467
|
+
echo " ${EMAIL}";
|
|
468
|
+
fi
|
|
469
|
+
echo "";
|
|
470
|
+
echo " Mail sent by this instance is caught, not delivered:";
|
|
471
|
+
echo " https://${PROJECT_NAME}.ddev.site:$((mail_port + 363))";
|
|
472
|
+
echo "";
|
|
473
|
+
echo " Flags: ${flags}";
|
|
474
|
+
echo " Design agents: enable-plugins is on, so Ctrl+Alt+P loads the Penpot MCP plugin.";
|
|
475
|
+
echo " Drupal AI Penpot: enable-access-tokens is on, so an access token authenticates.";
|
|
476
|
+
echo " The full integration walkthrough is in products/ai_penpot/docs/penpot-on-ddev.md.";
|
|
477
|
+
echo "*---------------------------------------------------------------------*";
|
|
478
|
+
|
|
479
|
+
if [ "${LAUNCH}" == 'yes' ] ; then
|
|
480
|
+
ddev launch ;
|
|
481
|
+
fi
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
# ---------------------------------------------------------------------------
|
|
485
|
+
# Per-instance tools. Each resolves its defaults from the workspace settings
|
|
486
|
+
# (`penpot:` in workspace.designs.settings.yml), so the commands take a project
|
|
487
|
+
# name and nothing else in the normal case.
|
|
488
|
+
# ---------------------------------------------------------------------------
|
|
489
|
+
|
|
490
|
+
# The project a tool acts on: the argument, else penpot.project from the settings.
|
|
491
|
+
function penpot_resolve_project() {
|
|
492
|
+
local given="$1" name;
|
|
493
|
+
if [ -n "${given}" ] && [ "${given}" != '_settings_' ] ; then
|
|
494
|
+
printf '%s' "${given}" ; return 0 ;
|
|
495
|
+
fi
|
|
496
|
+
name="$(penpot_setting "${penpot_project}")";
|
|
497
|
+
printf '%s' "${name:-penpot}";
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
# The one origin an instance serves, resolved the same way build_penpot resolves it — a tool
|
|
501
|
+
# that talked to the other host would be CORS-blocked in a browser and inconsistent here.
|
|
502
|
+
function penpot_resolve_url() {
|
|
503
|
+
local project="$1" setting;
|
|
504
|
+
setting="$(penpot_setting "${penpot_public_uri}")";
|
|
505
|
+
case "${setting:-hub}" in
|
|
506
|
+
hub) printf 'https://%s.%s.workspace.ddev.site' "${project}" "${doc_name}" ;;
|
|
507
|
+
canonical) printf 'https://%s.ddev.site' "${project}" ;;
|
|
508
|
+
*) printf '%s' "${setting}" ;;
|
|
509
|
+
esac
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
# Refuse politely rather than erroring deep inside docker.
|
|
513
|
+
function penpot_require_running() {
|
|
514
|
+
local project="$1";
|
|
515
|
+
if ! docker ps --format '{{.Names}}' | grep -qx "ddev-${project}-penpot-backend" ; then
|
|
516
|
+
echo "Penpot ${project} is not running.";
|
|
517
|
+
echo " Start it: cd ${WORKSPACE_ROOT}/${doc_name}/${project} && ddev start";
|
|
518
|
+
return 1 ;
|
|
519
|
+
fi
|
|
520
|
+
return 0 ;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
# Back up one instance: the Postgres database AND the asset volume. Either alone is useless —
|
|
524
|
+
# a design is rows in the database plus the files those rows point at.
|
|
525
|
+
function penpot_backup() {
|
|
526
|
+
local project stamp out db_file assets_file;
|
|
527
|
+
project="$(penpot_resolve_project "${PROJECT_NAME}")";
|
|
528
|
+
penpot_require_running "${project}" || return 1 ;
|
|
529
|
+
|
|
530
|
+
stamp="$(date +%Y-%m-%d-%H%M%S)";
|
|
531
|
+
out="${backups}/${doc_name}/${project}";
|
|
532
|
+
mkdir -p "${out}" ;
|
|
533
|
+
db_file="${out}/${project}-penpot-db-${stamp}.sql.gz";
|
|
534
|
+
assets_file="${out}/${project}-penpot-assets-${stamp}.tar.gz";
|
|
535
|
+
|
|
536
|
+
echo "Dumping the Penpot database of ${project}.";
|
|
537
|
+
if ! docker exec "ddev-${project}-penpot-postgres" pg_dump -U penpot penpot | gzip > "${db_file}" ; then
|
|
538
|
+
echo "The database dump failed — removing the partial file.";
|
|
539
|
+
rm -f "${db_file}" ;
|
|
540
|
+
return 1 ;
|
|
541
|
+
fi
|
|
542
|
+
|
|
543
|
+
echo "Archiving the Penpot assets of ${project}.";
|
|
544
|
+
# The assets live in a named volume, not in the project directory, so they are read through a
|
|
545
|
+
# throwaway container rather than from the host.
|
|
546
|
+
# --user, or the archive lands owned by root: a backup the user cannot delete is a bad backup.
|
|
547
|
+
if ! docker run --rm --user "$(id -u):$(id -g)" \
|
|
548
|
+
-v "ddev-${project}_penpot_assets:/assets:ro" -v "${out}:/out" \
|
|
549
|
+
busybox tar czf "/out/$(basename "${assets_file}")" -C /assets . ; then
|
|
550
|
+
echo "The asset archive failed — removing the partial file.";
|
|
551
|
+
rm -f "${assets_file}" ;
|
|
552
|
+
return 1 ;
|
|
553
|
+
fi
|
|
554
|
+
|
|
555
|
+
echo "";
|
|
556
|
+
echo "*---------------------------------------------------------------------*";
|
|
557
|
+
echo " Backed up ${project}:";
|
|
558
|
+
echo " ${db_file}";
|
|
559
|
+
echo " ${assets_file}";
|
|
560
|
+
echo " Restore the database with:";
|
|
561
|
+
echo " gunzip -c <dump> | docker exec -i ddev-${project}-penpot-postgres psql -U penpot penpot";
|
|
562
|
+
echo "*---------------------------------------------------------------------*";
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
# Create a Penpot access token — what the Drupal AI Penpot module authenticates with.
|
|
566
|
+
#
|
|
567
|
+
# The token is printed ONCE and nowhere else: it is a credential, so it is not written to a
|
|
568
|
+
# file, not echoed into a command line, and not left in the backups folder.
|
|
569
|
+
function penpot_token() {
|
|
570
|
+
local project url jar name fullname email password prepared token;
|
|
571
|
+
project="$(penpot_resolve_project "${PROJECT_NAME}")";
|
|
572
|
+
penpot_require_running "${project}" || return 1 ;
|
|
573
|
+
url="$(penpot_resolve_url "${project}")";
|
|
574
|
+
|
|
575
|
+
email="${EMAIL}";
|
|
576
|
+
if [ "${email}" == '_settings_' ] ; then
|
|
577
|
+
email="$(penpot_setting "${penpot_account_email}")";
|
|
578
|
+
email="${email:-$(penpot_setting "${account_mail}")}";
|
|
579
|
+
fi
|
|
580
|
+
password="${PASSWORD}";
|
|
581
|
+
if [ "${password}" == '_settings_' ] ; then
|
|
582
|
+
password="$(penpot_setting "${penpot_account_password}")";
|
|
583
|
+
password="${password:-$(penpot_setting "${account_pass}")}";
|
|
584
|
+
fi
|
|
585
|
+
name="${TOKEN_NAME}";
|
|
586
|
+
|
|
587
|
+
jar="$(mktemp)";
|
|
588
|
+
if ! curl -sk -c "${jar}" -X POST "${url}/api/rpc/command/login-with-password" \
|
|
589
|
+
-H 'Accept: application/json' -H 'Content-Type: application/json' -H "Origin: ${url}" \
|
|
590
|
+
--data-binary @- >/dev/null <<JSONEOF
|
|
591
|
+
{"email": "${email}", "password": "${password}"}
|
|
592
|
+
JSONEOF
|
|
593
|
+
then
|
|
594
|
+
rm -f "${jar}" ;
|
|
595
|
+
echo "Could not log in to ${url} as ${email}.";
|
|
596
|
+
return 1 ;
|
|
597
|
+
fi
|
|
598
|
+
|
|
599
|
+
prepared=$(curl -sk -b "${jar}" -X POST "${url}/api/rpc/command/create-access-token" \
|
|
600
|
+
-H 'Accept: application/json' -H 'Content-Type: application/json' -H "Origin: ${url}" \
|
|
601
|
+
--data-binary @- <<JSONEOF
|
|
602
|
+
{"name": "${name}"}
|
|
603
|
+
JSONEOF
|
|
604
|
+
);
|
|
605
|
+
rm -f "${jar}" ;
|
|
606
|
+
|
|
607
|
+
token=$(printf '%s' "${prepared}" | sed -n 's/.*"token":"\([^"]*\)".*/\1/p');
|
|
608
|
+
if [ -z "${token}" ] ; then
|
|
609
|
+
echo "Could not create the access token:";
|
|
610
|
+
printf ' %s\n' "${prepared}";
|
|
611
|
+
return 1 ;
|
|
612
|
+
fi
|
|
613
|
+
|
|
614
|
+
echo "";
|
|
615
|
+
echo "*---------------------------------------------------------------------*";
|
|
616
|
+
echo " Access token \"${name}\" for ${email} on ${url}";
|
|
617
|
+
echo "";
|
|
618
|
+
echo " ${token}";
|
|
619
|
+
echo "";
|
|
620
|
+
echo " Shown once and stored nowhere. Paste it into the Drupal site's penpot Key at";
|
|
621
|
+
echo " /admin/config/system/keys, with the instance URL ${url}.";
|
|
622
|
+
echo " It needs enable-access-tokens in PENPOT_FLAGS, which this workspace sets by default —";
|
|
623
|
+
echo " without that flag a token is ignored silently rather than refused.";
|
|
624
|
+
echo "*---------------------------------------------------------------------*";
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
# The Penpot MCP server: a node:24 container on the host network, plus the one-line Claude Code
|
|
628
|
+
# registration. Two processes are needed for the MCP to reach a design and only one of them is
|
|
629
|
+
# here: the other is a browser tab holding the plugin open, which no script can do for you.
|
|
630
|
+
function penpot_mcp() {
|
|
631
|
+
local project url package image port plugin_port bridge_port work container public_host;
|
|
632
|
+
project="$(penpot_resolve_project "${PROJECT_NAME}")";
|
|
633
|
+
url="$(penpot_resolve_url "${project}")";
|
|
634
|
+
package="$(penpot_setting "${penpot_mcp_package}")"; package="${package:-@penpot/mcp@stable}";
|
|
635
|
+
image="$(penpot_setting "${penpot_mcp_image}")"; image="${image:-node:24}";
|
|
636
|
+
port="$(penpot_setting "${penpot_mcp_port}")"; port="${port:-4401}";
|
|
637
|
+
plugin_port="$(penpot_setting "${penpot_mcp_plugin_port}")"; plugin_port="${plugin_port:-4400}";
|
|
638
|
+
bridge_port="$(penpot_setting "${penpot_mcp_bridge_port}")"; bridge_port="${bridge_port:-4402}";
|
|
639
|
+
container="penpot-mcp-${project}";
|
|
640
|
+
public_host="$(printf '%s' "$(penpot_resolve_url "${project}")" | sed 's#^https\?://##')";
|
|
641
|
+
work="${WORKSPACE_ROOT}/${doc_name}/${project}/.penpot-mcp";
|
|
642
|
+
|
|
643
|
+
case "${MCP_ACTION}" in
|
|
644
|
+
status)
|
|
645
|
+
if docker ps --format '{{.Names}}' | grep -qx "${container}" ; then
|
|
646
|
+
echo "${container} is running.";
|
|
647
|
+
curl -sk -o /dev/null -w " MCP endpoint ${url}/mcp → HTTP %{http_code} (406 on a plain GET is correct)\n" "${url}/mcp" ;
|
|
648
|
+
curl -sk -o /dev/null -w " plugin manifest ${url}/mcp-plugin/manifest.json → HTTP %{http_code}\n" "${url}/mcp-plugin/manifest.json" ;
|
|
649
|
+
else
|
|
650
|
+
echo "${container} is not running. Start it with --start.";
|
|
651
|
+
fi
|
|
652
|
+
claude mcp list 2>/dev/null | grep -i penpot || echo " Not registered in Claude Code yet — run with --register.";
|
|
653
|
+
;;
|
|
654
|
+
stop)
|
|
655
|
+
docker rm -f "${container}" >/dev/null 2>&1 && echo "Stopped ${container}." || echo "${container} was not running.";
|
|
656
|
+
;;
|
|
657
|
+
register)
|
|
658
|
+
claude mcp add --scope user --transport http "penpot-${project}" "${url}/mcp" \
|
|
659
|
+
&& echo "Registered penpot-${project} in Claude Code. Open a NEW session to see its tools." ;
|
|
660
|
+
;;
|
|
661
|
+
start)
|
|
662
|
+
penpot_require_running "${project}" || return 1 ;
|
|
663
|
+
mkdir -p "${work}" ;
|
|
664
|
+
cd "${work}" || return 1 ;
|
|
665
|
+
if [ ! -d package ] ; then
|
|
666
|
+
echo "Fetching ${package}.";
|
|
667
|
+
npm pack "${package}" >/dev/null || return 1 ;
|
|
668
|
+
tar xzf penpot-mcp-*.tgz || return 1 ;
|
|
669
|
+
# pnpm's supply-chain gate blocks esbuild and sharp from running their install scripts,
|
|
670
|
+
# and @penpot/mcp cannot build without them. pnpm 11 has moved the answer from
|
|
671
|
+
# `onlyBuiltDependencies` to `allowBuilds` — given only the old key it writes an
|
|
672
|
+
# `allowBuilds:` stub reading "set this to true or false" and fails with
|
|
673
|
+
# ERR_PNPM_IGNORED_BUILDS anyway. Both keys go in, so either version is satisfied.
|
|
674
|
+
cat >> package/pnpm-workspace.yaml <<'YAMLEOF'
|
|
675
|
+
|
|
676
|
+
onlyBuiltDependencies:
|
|
677
|
+
- esbuild
|
|
678
|
+
- sharp
|
|
679
|
+
|
|
680
|
+
allowBuilds:
|
|
681
|
+
esbuild: true
|
|
682
|
+
sharp: true
|
|
683
|
+
YAMLEOF
|
|
684
|
+
fi
|
|
685
|
+
docker rm -f "${container}" >/dev/null 2>&1 ;
|
|
686
|
+
# pnpm comes from a /tmp prefix rather than corepack: `corepack enable` symlinks into
|
|
687
|
+
# /usr/local/bin, which a non-root container user cannot write (EACCES on pnpm.js).
|
|
688
|
+
# As the host user, or the install writes a node_modules tree the user cannot delete —
|
|
689
|
+
# `rm -rf` on the work directory then fails with Permission denied on every binary. pnpm and
|
|
690
|
+
# corepack need a writable HOME, which /tmp gives them inside the container.
|
|
691
|
+
# The web container's network namespace, NOT the host's: the MCP servers bind 127.0.0.1, and
|
|
692
|
+
# sharing the namespace is what lets this instance's nginx proxy them onto its https origin.
|
|
693
|
+
# It also means the container has to be started AFTER the project and restarted with it — a
|
|
694
|
+
# `ddev restart` destroys the namespace and leaves the routes answering 502.
|
|
695
|
+
docker run -d --name "${container}" --network "container:ddev-${project}-web" \
|
|
696
|
+
--user "$(id -u):$(id -g)" -e HOME=/tmp \
|
|
697
|
+
-e CI=false -e "PENPOT_MCP_WEBSOCKET_URL=wss://${public_host}/mcp-bridge" \
|
|
698
|
+
-v "${work}/package:/pkg" -w /pkg "${image}" \
|
|
699
|
+
sh -c 'set -e; export PATH=/tmp/npm-global/bin:$PATH; \
|
|
700
|
+
npm install -g --prefix /tmp/npm-global pnpm@11.13.0 >/dev/null; \
|
|
701
|
+
pnpm rebuild esbuild sharp; pnpm -r run build; pnpm -r --parallel run start' >/dev/null || return 1 ;
|
|
702
|
+
echo "";
|
|
703
|
+
echo "*---------------------------------------------------------------------*";
|
|
704
|
+
echo " ${container} is starting — the first build takes a couple of minutes.";
|
|
705
|
+
echo " docker logs -f ${container}";
|
|
706
|
+
echo "";
|
|
707
|
+
echo " MCP endpoint ${url}/mcp";
|
|
708
|
+
echo " plugin manifest ${url}/mcp-plugin/manifest.json (same origin — an http://localhost";
|
|
709
|
+
echo " manifest is mixed content on an https editor page and is refused)";
|
|
710
|
+
echo " WS bridge wss://${public_host}/mcp-bridge";
|
|
711
|
+
echo "";
|
|
712
|
+
echo " Then, and this part no script can do:";
|
|
713
|
+
echo " 1. bash cmd-penpot-mcp.sh ${project} -a register";
|
|
714
|
+
echo " 2. In a Penpot editor tab on ${url}: Ctrl+Alt+P, paste the manifest URL,";
|
|
715
|
+
echo " INSTALL, OPEN, CONNECT MCP SERVER — and leave that tab open. It holds the";
|
|
716
|
+
echo " bridge; close it and the tools stop reaching the design.";
|
|
717
|
+
echo "*---------------------------------------------------------------------*";
|
|
718
|
+
;;
|
|
719
|
+
*)
|
|
720
|
+
echo "Pick one of --start, --stop, --status or --register.";
|
|
721
|
+
return 1 ;
|
|
722
|
+
;;
|
|
723
|
+
esac
|
|
724
|
+
}
|