@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,237 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
################################################################################
|
|
4
|
+
## Sharing the specs that are worth sharing.
|
|
5
|
+
################################################################################
|
|
6
|
+
## SPDD's own claim is that "successful patterns naturally accumulate into a reusable prompt
|
|
7
|
+
## library" and that each iteration "starts from accumulated assets". Accumulation does not happen
|
|
8
|
+
## by itself: a template improved on one machine, in one workspace, improves nothing for anybody
|
|
9
|
+
## else. Agents, skills and prompts already solve this — one shared repository, fetched with
|
|
10
|
+
## `git archive` from the fetched remote branch, pulled into the workspace folder to read and edit.
|
|
11
|
+
## This is the same mechanism for the same reason.
|
|
12
|
+
##
|
|
13
|
+
## **Only two kinds of spec are shared, and the exclusion is the design.**
|
|
14
|
+
##
|
|
15
|
+
## _defaults/ the templates every spec starts from, and the review checklist.
|
|
16
|
+
## */00-constitution.md the general spec of a product: what it is, who for, what it must
|
|
17
|
+
## never become. Written once, revised rarely, useful to everyone who
|
|
18
|
+
## touches that product.
|
|
19
|
+
##
|
|
20
|
+
## A NUMBERED spec — `001-some-change/` — is about one change, in one tree, at one moment. It is
|
|
21
|
+
## the most valuable artifact locally and pure noise shared: nobody else's repository has that
|
|
22
|
+
## change, and the analysis it contains was true of a commit they are not on. Sharing those would
|
|
23
|
+
## turn the shared repository into a landfill of other people's finished work, and the templates
|
|
24
|
+
## nobody could find in it would stop being pulled. So they stay local.
|
|
25
|
+
##
|
|
26
|
+
## Nothing here pushes. Promotion to the shared repository goes through the issue and MR/PR path
|
|
27
|
+
## like every other change, because a template everybody inherits deserves a review.
|
|
28
|
+
################################################################################
|
|
29
|
+
|
|
30
|
+
# What is shared, as a predicate. One place, so the listing, the diff and the pull cannot disagree
|
|
31
|
+
# about what "a shared spec" means.
|
|
32
|
+
function specs_is_shared_path() {
|
|
33
|
+
local rel="$1";
|
|
34
|
+
case "${rel}" in
|
|
35
|
+
_defaults/*) return 0 ;;
|
|
36
|
+
*/00-constitution.md) return 0 ;;
|
|
37
|
+
*) return 1 ;;
|
|
38
|
+
esac
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function specs_root() { echo "${WORKSPACE_ROOT}/specs"; }
|
|
42
|
+
|
|
43
|
+
# Where the shared specs live in the shared repository. A top level `specs/`, beside `prompts/` and
|
|
44
|
+
# `.claude/`, because a spec is not a thing Claude Code loads — it is a thing people read.
|
|
45
|
+
function specs_repo_path() { echo "specs"; }
|
|
46
|
+
|
|
47
|
+
# The checkout the repository is read from, kept between runs. Gitignored and hidden from the
|
|
48
|
+
# workspace listing, the way the AI-items checkout and the commands mirror are.
|
|
49
|
+
function specs_checkout() { echo "$(specs_root)/dev-ai-agents"; }
|
|
50
|
+
|
|
51
|
+
# Every shared spec in this workspace folder, as paths relative to specs/.
|
|
52
|
+
function specs_local_shared() {
|
|
53
|
+
local root; root="$(specs_root)";
|
|
54
|
+
[ -d "${root}" ] || return 0 ;
|
|
55
|
+
( cd "${root}" && find . -name '*.md' -o -name '*.yml' ) 2>/dev/null \
|
|
56
|
+
| sed 's|^\./||' | sort | while read -r rel ; do
|
|
57
|
+
specs_is_shared_path "${rel}" && echo "${rel}" ;
|
|
58
|
+
done
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# Refresh the checkout and answer with the remote branch to read from. Prints nothing on success
|
|
62
|
+
# beyond the one line saying what it did, because this runs inside every other verb.
|
|
63
|
+
function specs_fetch_repo() {
|
|
64
|
+
local checkout; checkout="$(specs_checkout)";
|
|
65
|
+
ensure_gh_cli || return 1 ;
|
|
66
|
+
ensure_git_github_auth ;
|
|
67
|
+
if [ -d "${checkout}/.git" ] ; then
|
|
68
|
+
git -C "${checkout}" fetch --quiet origin || return 1 ;
|
|
69
|
+
else
|
|
70
|
+
echo "Cloning ${AI_ITEMS_REPO}…" >&2 ;
|
|
71
|
+
git clone --quiet "https://github.com/${AI_ITEMS_REPO}.git" "${checkout}" || {
|
|
72
|
+
echo "Could not clone ${AI_ITEMS_REPO} — check gh auth and repository access." >&2 ;
|
|
73
|
+
return 1 ;
|
|
74
|
+
}
|
|
75
|
+
fi
|
|
76
|
+
local remote_branch;
|
|
77
|
+
remote_branch=$(git -C "${checkout}" symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null) ;
|
|
78
|
+
echo "${remote_branch:-origin/${AI_ITEMS_REF:-main}}" ;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# The shared specs as the repository has them, extracted to a temporary directory. Read from the
|
|
82
|
+
# FETCHED REMOTE BRANCH with git archive, never from the checkout's working tree: that clone may
|
|
83
|
+
# sit on any branch and hold anybody's work in progress.
|
|
84
|
+
function specs_remote_tree() {
|
|
85
|
+
local branch="$1" tmp;
|
|
86
|
+
tmp="$(mktemp -d)" || return 1 ;
|
|
87
|
+
# Both halves quiet: with no specs/ in the branch, git archive fails and tar then complains that
|
|
88
|
+
# an empty stream is not an archive. That pair of errors is the NORMAL answer the first time
|
|
89
|
+
# anybody runs this — the shared repository does not carry specs until somebody proposes one —
|
|
90
|
+
# and printing it makes a working command look broken.
|
|
91
|
+
if ! git -C "$(specs_checkout)" archive "${branch}" "$(specs_repo_path)" 2>/dev/null | tar -x -C "${tmp}" 2>/dev/null ; then
|
|
92
|
+
rm -rf "${tmp}" ;
|
|
93
|
+
return 1 ;
|
|
94
|
+
fi
|
|
95
|
+
echo "${tmp}" ;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
# What the shared repository has.
|
|
99
|
+
function specs_list_remote() {
|
|
100
|
+
local branch tmp base;
|
|
101
|
+
branch="$(specs_fetch_repo)" || return 1 ;
|
|
102
|
+
tmp="$(specs_remote_tree "${branch}")" || {
|
|
103
|
+
echo "Nothing at $(specs_repo_path)/ in ${branch} — the shared repository carries no specs yet." ;
|
|
104
|
+
return 0 ;
|
|
105
|
+
}
|
|
106
|
+
base="${tmp}/$(specs_repo_path)";
|
|
107
|
+
echo "";
|
|
108
|
+
echo "Shared specs in ${AI_ITEMS_REPO} (${branch#origin/})";
|
|
109
|
+
echo "";
|
|
110
|
+
( cd "${base}" && find . -type f \( -name '*.md' -o -name '*.yml' \) | sed 's|^\./||' | sort ) \
|
|
111
|
+
| while read -r rel ; do printf ' %s\n' "${rel}" ; done
|
|
112
|
+
echo "";
|
|
113
|
+
rm -rf "${tmp}" ;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
# What is here, what is shared, and where the two differ. The answer people actually want, and the
|
|
117
|
+
# one that decides whether to pull.
|
|
118
|
+
function specs_status() {
|
|
119
|
+
local branch tmp base root rel local_file state;
|
|
120
|
+
root="$(specs_root)";
|
|
121
|
+
echo "";
|
|
122
|
+
echo "Shared specs — ${root}";
|
|
123
|
+
echo "";
|
|
124
|
+
branch="$(specs_fetch_repo)" || { echo " (the shared repository could not be read; showing local only)" ; branch='' ; }
|
|
125
|
+
if [ -n "${branch}" ] ; then
|
|
126
|
+
tmp="$(specs_remote_tree "${branch}")" || tmp='' ;
|
|
127
|
+
base="${tmp}/$(specs_repo_path)";
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
printf ' %-52s %s\n' "SPEC" "STATE" ;
|
|
131
|
+
specs_local_shared | while read -r rel ; do
|
|
132
|
+
local_file="${root}/${rel}";
|
|
133
|
+
if [ -z "${branch}" ] || [ -z "${tmp}" ] || [ ! -f "${base}/${rel}" ] ; then
|
|
134
|
+
state="local only" ;
|
|
135
|
+
elif cmp -s "${local_file}" "${base}/${rel}" ; then
|
|
136
|
+
state="same as shared" ;
|
|
137
|
+
else
|
|
138
|
+
state="DIFFERS from shared" ;
|
|
139
|
+
fi
|
|
140
|
+
printf ' %-52s %s\n' "${rel}" "${state}" ;
|
|
141
|
+
done
|
|
142
|
+
|
|
143
|
+
# And what the repository has that this machine does not — the reason to run --pull.
|
|
144
|
+
if [ -n "${tmp}" ] && [ -d "${base}" ] ; then
|
|
145
|
+
( cd "${base}" && find . -type f \( -name '*.md' -o -name '*.yml' \) | sed 's|^\./||' | sort ) \
|
|
146
|
+
| while read -r rel ; do
|
|
147
|
+
[ -f "${root}/${rel}" ] || printf ' %-52s %s\n' "${rel}" "in the repository only" ;
|
|
148
|
+
done
|
|
149
|
+
fi
|
|
150
|
+
echo "";
|
|
151
|
+
echo " --pull to take the shared copy, --diff to see what changed first." ;
|
|
152
|
+
echo "";
|
|
153
|
+
[ -n "${tmp}" ] && rm -rf "${tmp}" ;
|
|
154
|
+
return 0 ;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
# What would change if this were pulled. Shown before it is taken, never after: a template is read
|
|
158
|
+
# by every future spec, and overwriting one unseen is how a local improvement disappears.
|
|
159
|
+
function specs_diff() {
|
|
160
|
+
local want="$1" branch tmp base root rel shown=0;
|
|
161
|
+
root="$(specs_root)";
|
|
162
|
+
branch="$(specs_fetch_repo)" || return 1 ;
|
|
163
|
+
tmp="$(specs_remote_tree "${branch}")" || { echo "The shared repository carries no specs yet." ; return 0 ; }
|
|
164
|
+
base="${tmp}/$(specs_repo_path)";
|
|
165
|
+
|
|
166
|
+
( cd "${base}" && find . -type f \( -name '*.md' -o -name '*.yml' \) | sed 's|^\./||' | sort ) \
|
|
167
|
+
| while read -r rel ; do
|
|
168
|
+
[ "${want}" == '_all_' ] || [ -z "${want}" ] || case "${rel}" in *"${want}"*) : ;; *) continue ;; esac
|
|
169
|
+
if [ ! -f "${root}/${rel}" ] ; then
|
|
170
|
+
echo "=== ${rel} — not here yet" ;
|
|
171
|
+
elif ! cmp -s "${root}/${rel}" "${base}/${rel}" ; then
|
|
172
|
+
echo "=== ${rel}" ;
|
|
173
|
+
diff -u "${root}/${rel}" "${base}/${rel}" | sed '1,2d' ;
|
|
174
|
+
fi
|
|
175
|
+
done
|
|
176
|
+
shown=$? ;
|
|
177
|
+
rm -rf "${tmp}" ;
|
|
178
|
+
return 0 ;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
# Take the shared copy. A local file that differs is BACKED UP first, with the path printed: a pull
|
|
182
|
+
# can move you backwards — the shared branch does not carry work still in review — and a template
|
|
183
|
+
# somebody improved this morning must not vanish because they ran a sync this afternoon.
|
|
184
|
+
function specs_pull() {
|
|
185
|
+
local want="$1" branch tmp base root rel dest backup stamp pulled=0;
|
|
186
|
+
root="$(specs_root)";
|
|
187
|
+
branch="$(specs_fetch_repo)" || return 1 ;
|
|
188
|
+
tmp="$(specs_remote_tree "${branch}")" || { echo "The shared repository carries no specs yet — nothing to pull." ; return 0 ; }
|
|
189
|
+
base="${tmp}/$(specs_repo_path)";
|
|
190
|
+
stamp="$(date +%Y-%m-%d-%H%M)";
|
|
191
|
+
|
|
192
|
+
while read -r rel ; do
|
|
193
|
+
[ -n "${rel}" ] || continue ;
|
|
194
|
+
[ "${want}" == '_all_' ] || [ -z "${want}" ] || case "${rel}" in *"${want}"*) : ;; *) continue ;; esac
|
|
195
|
+
specs_is_shared_path "${rel}" || continue ;
|
|
196
|
+
dest="${root}/${rel}";
|
|
197
|
+
if [ -f "${dest}" ] && cmp -s "${dest}" "${base}/${rel}" ; then continue ; fi
|
|
198
|
+
if [ -f "${dest}" ] ; then
|
|
199
|
+
backup="${WORKSPACE_ROOT}/backups/specs/${stamp}/${rel}";
|
|
200
|
+
mkdir -p "$(dirname "${backup}")" ;
|
|
201
|
+
cp "${dest}" "${backup}" ;
|
|
202
|
+
echo " kept your copy at ${backup}" ;
|
|
203
|
+
fi
|
|
204
|
+
mkdir -p "$(dirname "${dest}")" ;
|
|
205
|
+
cp "${base}/${rel}" "${dest}" ;
|
|
206
|
+
echo " pulled ${rel}" ;
|
|
207
|
+
pulled=$((pulled + 1)) ;
|
|
208
|
+
done < <( cd "${base}" && find . -type f \( -name '*.md' -o -name '*.yml' \) | sed 's|^\./||' | sort )
|
|
209
|
+
|
|
210
|
+
rm -rf "${tmp}" ;
|
|
211
|
+
echo "" ;
|
|
212
|
+
if [ "${pulled}" -eq 0 ] ; then
|
|
213
|
+
echo "Nothing to pull — everything shared is already what you have." ;
|
|
214
|
+
else
|
|
215
|
+
echo "${pulled} file(s) pulled from ${AI_ITEMS_REPO}." ;
|
|
216
|
+
fi
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
# Promoting a local improvement back to everybody. Deliberately NOT automated: a template is
|
|
220
|
+
# inherited by every future spec in every workspace, so it goes through the same issue-and-PR path
|
|
221
|
+
# as any other change. This prints exactly what to do, and creates nothing.
|
|
222
|
+
function specs_propose() {
|
|
223
|
+
local root; root="$(specs_root)";
|
|
224
|
+
echo "";
|
|
225
|
+
echo "Sharing a spec back to ${AI_ITEMS_REPO}";
|
|
226
|
+
echo "";
|
|
227
|
+
echo " Nothing here pushes. A template or a constitution is inherited by everyone who pulls it,"
|
|
228
|
+
echo " so it is proposed and reviewed like any other change:";
|
|
229
|
+
echo "";
|
|
230
|
+
echo " 1. git -C $(specs_checkout) checkout -b <branch>";
|
|
231
|
+
echo " 2. copy your file into $(specs_checkout)/$(specs_repo_path)/<same relative path>";
|
|
232
|
+
echo " 3. commit, push, and open the PR on ${AI_ITEMS_REPO}";
|
|
233
|
+
echo "";
|
|
234
|
+
echo " What is shared: _defaults/ and every 00-constitution.md. A numbered spec is about one";
|
|
235
|
+
echo " change in one tree and is not shared — see fun-specs-share.sh for why.";
|
|
236
|
+
echo "";
|
|
237
|
+
}
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# Second storage: whole workspace folders kept on another disk, each one a symlink from the tree.
|
|
4
|
+
#
|
|
5
|
+
# `storage.second` in settings.yml names the folder on that disk that holds them. A moved workspace is
|
|
6
|
+
# `<root>/<name> -> <storage>/<name>`, so every script and the dashboard keep using `<root>/<name>`.
|
|
7
|
+
#
|
|
8
|
+
# Three rules hold throughout:
|
|
9
|
+
# * nothing is deleted without a flag that says so. A move keeps `<name>.pre-move`, a move back keeps
|
|
10
|
+
# the copy on the drive;
|
|
11
|
+
# * a copy is trusted only after a checksum compare finds no difference;
|
|
12
|
+
# * a workspace that holds DDEV projects is refused. DDEV bind-mounts a project's folder into its
|
|
13
|
+
# containers, and that has not been verified across a symlink onto another disk.
|
|
14
|
+
|
|
15
|
+
STORAGE_RSYNC_FLAGS="${STORAGE_RSYNC_FLAGS:--aHAX}";
|
|
16
|
+
STORAGE_COMPOSE_NAME="docker-compose.second-storage.yaml";
|
|
17
|
+
|
|
18
|
+
# The configured path, one layer of quotes stripped (parse_yaml keeps them), no trailing slash.
|
|
19
|
+
function storage_path() {
|
|
20
|
+
local p="${storage_second:-}" ;
|
|
21
|
+
p="${p#\"}" ; p="${p%\"}" ; p="${p#\'}" ; p="${p%\'}" ;
|
|
22
|
+
case "${p}" in ''|CHANGE_ME|none|off) return 0 ;; esac
|
|
23
|
+
printf '%s' "${p%/}" ;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function storage_say() { [ "${QUIET:-}" == 'yes' ] || echo "$@" ; }
|
|
27
|
+
|
|
28
|
+
function storage_is_workspace() {
|
|
29
|
+
local w ;
|
|
30
|
+
for w in "${workspaces[@]}" ; do [ "${w}" == "$1" ] && return 0 ; done
|
|
31
|
+
return 1 ;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# local | second | elsewhere | broken | missing
|
|
35
|
+
# second a symlink into the configured storage path that resolves
|
|
36
|
+
# elsewhere a symlink that resolves, but not into the configured path
|
|
37
|
+
# broken a symlink whose target is not there: the drive is not mounted, or it moved
|
|
38
|
+
function storage_state() {
|
|
39
|
+
local link="${WORKSPACE_ROOT}/$1" storage target ;
|
|
40
|
+
storage="$(storage_path)" ;
|
|
41
|
+
if [ -L "${link}" ] ; then
|
|
42
|
+
target="$(readlink "${link}")" ;
|
|
43
|
+
[ -d "${link}" ] || { echo 'broken' ; return 0 ; } ;
|
|
44
|
+
if [ -n "${storage}" ] && [ "${target%/}" == "${storage}/$1" ] ; then echo 'second' ; else echo 'elsewhere' ; fi
|
|
45
|
+
elif [ -d "${link}" ] ; then
|
|
46
|
+
echo 'local' ;
|
|
47
|
+
else
|
|
48
|
+
echo 'missing' ;
|
|
49
|
+
fi
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# Any DDEV project inside, at the depths projects live at (a project, or a project inside a clone).
|
|
53
|
+
function storage_has_ddev() {
|
|
54
|
+
[ -n "$(find "$1" -mindepth 2 -maxdepth 4 -type f -path '*/.ddev/config.yaml' -print -quit 2>/dev/null)" ] ;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function storage_avail_bytes() { df -B1 --output=avail "$1" 2>/dev/null | tail -1 | tr -d ' ' ; }
|
|
58
|
+
function storage_size_bytes() { du -sb "$1" 2>/dev/null | cut -f1 ; }
|
|
59
|
+
function storage_human() { numfmt --to=iec --suffix=B "${1:-0}" 2>/dev/null || echo "${1:-0} bytes" ; }
|
|
60
|
+
|
|
61
|
+
# True when both paths are on one filesystem, where a move frees nothing.
|
|
62
|
+
function storage_same_fs() {
|
|
63
|
+
[ "$(stat -c %d "$1" 2>/dev/null)" == "$(stat -c %d "$2" 2>/dev/null)" ] ;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# The storage path exists and is on its own filesystem.
|
|
67
|
+
function storage_mounted() {
|
|
68
|
+
local storage ; storage="$(storage_path)" ;
|
|
69
|
+
[ -n "${storage}" ] && [ -d "${storage}" ] && ! storage_same_fs "${storage}" "${WORKSPACE_ROOT}" ;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# Room for size plus a margin: 5%, at least 1 GiB.
|
|
73
|
+
function storage_has_room() {
|
|
74
|
+
local size="$1" dest="$2" avail margin ;
|
|
75
|
+
avail="$(storage_avail_bytes "${dest}")" ;
|
|
76
|
+
margin=$(( size / 20 )) ; [ "${margin}" -lt 1073741824 ] && margin=1073741824 ;
|
|
77
|
+
[ -n "${avail}" ] && [ "${avail}" -ge $(( size + margin )) ] ;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
# Lines that differ between two trees, by checksum. Empty means identical.
|
|
81
|
+
function storage_diff() {
|
|
82
|
+
rsync ${STORAGE_RSYNC_FLAGS} -nc --delete --itemize-changes "$1/" "$2/" 2>&1 ;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function storage_copy() {
|
|
86
|
+
rsync ${STORAGE_RSYNC_FLAGS} --delete "$1/" "$2/" ;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
# Copy, then compare. Prints why and returns 1 on any difference.
|
|
90
|
+
function storage_copy_verified() {
|
|
91
|
+
local from="$1" to="$2" diff ;
|
|
92
|
+
storage_say " copying ${from} → ${to}" ;
|
|
93
|
+
if ! storage_copy "${from}" "${to}" ; then
|
|
94
|
+
echo " ✗ rsync failed. Nothing was swapped; ${from} is untouched." ; return 1 ;
|
|
95
|
+
fi
|
|
96
|
+
storage_say " verifying by checksum" ;
|
|
97
|
+
diff="$(storage_diff "${from}" "${to}")" ;
|
|
98
|
+
if [ -n "${diff}" ] ; then
|
|
99
|
+
echo " ✗ The copy differs from the source ($(printf '%s\n' "${diff}" | wc -l) lines). Nothing was swapped:" ;
|
|
100
|
+
printf '%s\n' "${diff}" | head -10 | sed 's/^/ /' ;
|
|
101
|
+
return 1 ;
|
|
102
|
+
fi
|
|
103
|
+
storage_say " ✓ identical" ;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
# The mount the drive needs in the dashboard container, one host path per line, at the same path.
|
|
107
|
+
# Under /run/media/<user> the PARENT is mounted, because udisks picks the folder name at mount time
|
|
108
|
+
# (`workspace`, then `workspace1` when a stale `workspace` is in the way), and a bind of the parent with
|
|
109
|
+
# rslave also sees a drive mounted after the dashboard started.
|
|
110
|
+
function storage_mount_sources() {
|
|
111
|
+
local storage link target ;
|
|
112
|
+
storage="$(storage_path)" ;
|
|
113
|
+
{
|
|
114
|
+
[ -n "${storage}" ] && printf '%s\n' "${storage}" ;
|
|
115
|
+
# Links that still point somewhere else (an old path) need their target too, or they break inside.
|
|
116
|
+
for link in "${WORKSPACE_ROOT}"/* ; do
|
|
117
|
+
[ -L "${link}" ] || continue ;
|
|
118
|
+
target="$(readlink "${link}")" ;
|
|
119
|
+
case "${target}" in /*) printf '%s\n' "$(dirname "${target}")" ;; esac
|
|
120
|
+
done
|
|
121
|
+
} | while read -r p ; do
|
|
122
|
+
case "${p}" in
|
|
123
|
+
"${WORKSPACE_ROOT}"|"${WORKSPACE_ROOT}"/*) ;;
|
|
124
|
+
/run/media/*/*) printf '%s\n' "$(printf '%s' "${p}" | cut -d/ -f1-4)" ;;
|
|
125
|
+
*) printf '%s\n' "${p}" ;;
|
|
126
|
+
esac
|
|
127
|
+
done | sort -u ;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function storage_compose() {
|
|
131
|
+
local sources src ;
|
|
132
|
+
sources="$(storage_mount_sources)" ;
|
|
133
|
+
[ -n "${sources}" ] || return 0 ;
|
|
134
|
+
echo "# Generated by core/scripts/storage.sh --mounts from storage.second in settings.yml." ;
|
|
135
|
+
echo "# This machine only: never committed, and rewritten by every run. Edit the setting, not this file." ;
|
|
136
|
+
echo "# The second storage, at the same path as on the host, so the workspace symlinks resolve in here." ;
|
|
137
|
+
echo "services:" ;
|
|
138
|
+
echo " web:" ;
|
|
139
|
+
echo " volumes:" ;
|
|
140
|
+
while read -r src ; do
|
|
141
|
+
case "${src}" in
|
|
142
|
+
/run/media/*) echo " - ${src}:${src}:rslave" ;;
|
|
143
|
+
*) echo " - ${src}:${src}" ;;
|
|
144
|
+
esac
|
|
145
|
+
done <<< "${sources}" ;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function storage_compose_file() { echo "${WORKSPACE_ROOT}/core/workspace-app/.ddev/${STORAGE_COMPOSE_NAME}" ; }
|
|
149
|
+
|
|
150
|
+
# Write (or remove) the dashboard's override. Says a restart is needed only when it changed.
|
|
151
|
+
function storage_write_mounts() {
|
|
152
|
+
local file new old ;
|
|
153
|
+
file="$(storage_compose_file)" ;
|
|
154
|
+
[ -d "$(dirname "${file}")" ] || { echo " ✗ No dashboard at $(dirname "$(dirname "${file}")")." ; return 1 ; } ;
|
|
155
|
+
new="$(storage_compose)" ;
|
|
156
|
+
old="$(cat "${file}" 2>/dev/null)" ;
|
|
157
|
+
if [ "${new}" == "${old}" ] ; then
|
|
158
|
+
storage_say " ✓ dashboard mounts up to date${new:+ (${file##*/})}" ;
|
|
159
|
+
return 0 ;
|
|
160
|
+
fi
|
|
161
|
+
if [ -z "${new}" ] ; then
|
|
162
|
+
rm -f "${file}" ;
|
|
163
|
+
echo " ✓ removed ${file##*/}: no second storage is set or linked" ;
|
|
164
|
+
else
|
|
165
|
+
printf '%s\n' "${new}" > "${file}" ;
|
|
166
|
+
echo " ✓ wrote ${file#${WORKSPACE_ROOT}/}:" ;
|
|
167
|
+
grep '^ - ' "${file}" | sed 's/^ - / /' ;
|
|
168
|
+
fi
|
|
169
|
+
echo " The dashboard picks it up on its next restart:" ;
|
|
170
|
+
echo " cd ${WORKSPACE_ROOT}/core/workspace-app && ddev restart" ;
|
|
171
|
+
echo " Check no build job is running first; a restart orphans it." ;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
# The stable fix for a udisks mount whose path can change: an fstab line by UUID to a fixed point.
|
|
175
|
+
function storage_fstab_hint() {
|
|
176
|
+
local storage mnt uuid fstype label point stale ;
|
|
177
|
+
storage="$(storage_path)" ;
|
|
178
|
+
case "${storage}" in /run/media/*) ;; *) return 0 ;; esac
|
|
179
|
+
[ -d "${storage}" ] || return 0 ;
|
|
180
|
+
mnt="$(findmnt -no TARGET -T "${storage}" 2>/dev/null)" ;
|
|
181
|
+
uuid="$(findmnt -no UUID -T "${storage}" 2>/dev/null)" ;
|
|
182
|
+
fstype="$(findmnt -no FSTYPE -T "${storage}" 2>/dev/null)" ;
|
|
183
|
+
label="$(findmnt -no LABEL -T "${storage}" 2>/dev/null)" ;
|
|
184
|
+
[ -n "${uuid}" ] || return 0 ;
|
|
185
|
+
point="${STORAGE_MOUNT_POINT:-/mnt/workspace-storage}" ;
|
|
186
|
+
# udisks names the folder after the label and adds a digit when that name is taken.
|
|
187
|
+
stale="$(dirname "${mnt}")/${label}" ;
|
|
188
|
+
echo "" ;
|
|
189
|
+
echo " ! ${mnt} is a udisks automount. Its path is picked at mount time (a leftover" ;
|
|
190
|
+
echo " folder named after the label makes udisks add a digit), so after a reboot every" ;
|
|
191
|
+
echo " workspace link can break. The stable fix, run by you (this tool never uses sudo):" ;
|
|
192
|
+
echo " (cd ${WORKSPACE_ROOT}/core/workspace-app && ddev stop) # it holds the mount open" ;
|
|
193
|
+
echo " sudo mkdir -p ${point}" ;
|
|
194
|
+
echo " echo 'UUID=${uuid} ${point} ${fstype:-auto} defaults,nofail,x-systemd.device-timeout=10s 0 2' | sudo tee -a /etc/fstab" ;
|
|
195
|
+
echo " sudo systemctl daemon-reload && sudo umount ${mnt} && sudo mount ${point}" ;
|
|
196
|
+
if [ "${stale}" != "${mnt}" ] && [ -d "${stale}" ] && [ -z "$(ls -A "${stale}" 2>/dev/null)" ] ; then
|
|
197
|
+
echo " sudo rmdir ${stale} # the empty leftover that pushed it to ${mnt##*/}" ;
|
|
198
|
+
fi
|
|
199
|
+
echo " then set storage.second to ${point}${storage#${mnt}} and run:" ;
|
|
200
|
+
echo " bash ${WORKSPACE_SCRIPTS}/storage.sh --relink --mounts" ;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function storage_status() {
|
|
204
|
+
local storage w state target size free_local free_second ;
|
|
205
|
+
storage="$(storage_path)" ;
|
|
206
|
+
echo "Second storage: ${storage:-not set (storage.second in settings.yml)}" ;
|
|
207
|
+
if [ -n "${storage}" ] ; then
|
|
208
|
+
if storage_mounted ; then
|
|
209
|
+
free_second="$(storage_avail_bytes "${storage}")" ;
|
|
210
|
+
echo " mounted: $(findmnt -no TARGET,SOURCE,FSTYPE -T "${storage}" 2>/dev/null | tr -s ' ')" ;
|
|
211
|
+
echo " free: $(storage_human "${free_second}")" ;
|
|
212
|
+
elif [ -d "${storage}" ] ; then
|
|
213
|
+
echo " ✗ ${storage} is on the same filesystem as the workspace: the drive is not mounted there." ;
|
|
214
|
+
else
|
|
215
|
+
echo " ✗ ${storage} does not exist: the drive is not mounted." ;
|
|
216
|
+
fi
|
|
217
|
+
fi
|
|
218
|
+
free_local="$(storage_avail_bytes "${WORKSPACE_ROOT}")" ;
|
|
219
|
+
echo "Workspace: ${WORKSPACE_ROOT}" ;
|
|
220
|
+
echo " free: $(storage_human "${free_local}")" ;
|
|
221
|
+
echo "" ;
|
|
222
|
+
for w in "${workspaces[@]}" ; do
|
|
223
|
+
state="$(storage_state "${w}")" ;
|
|
224
|
+
target="" ;
|
|
225
|
+
[ -L "${WORKSPACE_ROOT}/${w}" ] && target=" → $(readlink "${WORKSPACE_ROOT}/${w}")" ;
|
|
226
|
+
case "${state}" in
|
|
227
|
+
local) printf ' %-16s local\n' "${w}" ;;
|
|
228
|
+
second) printf ' %-16s second storage%s\n' "${w}" "${target}" ;;
|
|
229
|
+
elsewhere) printf ' %-16s ! linked elsewhere%s\n' "${w}" "${target}" ;;
|
|
230
|
+
broken) printf ' %-16s ✗ broken link%s\n' "${w}" "${target}" ;;
|
|
231
|
+
missing) printf ' %-16s (no folder)\n' "${w}" ;;
|
|
232
|
+
esac
|
|
233
|
+
if [ -d "${WORKSPACE_ROOT}/${w}.pre-move" ] ; then
|
|
234
|
+
size="$(storage_size_bytes "${WORKSPACE_ROOT}/${w}.pre-move")" ;
|
|
235
|
+
printf ' %-16s %s.pre-move kept locally, %s (--remove-pre-move %s frees it)\n' "" "${w}" "$(storage_human "${size}")" "${w}" ;
|
|
236
|
+
fi
|
|
237
|
+
done
|
|
238
|
+
if [ -n "$(storage_mount_sources)" ] && [ ! -f "$(storage_compose_file)" ] ; then
|
|
239
|
+
echo "" ;
|
|
240
|
+
echo " ! The dashboard does not mount the second storage yet: run --mounts." ;
|
|
241
|
+
fi
|
|
242
|
+
storage_fstab_hint ;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function storage_move() {
|
|
246
|
+
local w="$1" storage src dest size ;
|
|
247
|
+
storage="$(storage_path)" ; src="${WORKSPACE_ROOT}/${w}" ; dest="${storage}/${w}" ;
|
|
248
|
+
storage_is_workspace "${w}" || { echo " ✗ ${w} is not a workspace in settings.yml." ; return 1 ; } ;
|
|
249
|
+
[ -n "${storage}" ] || { echo " ✗ Set storage.second in settings.yml first." ; return 1 ; } ;
|
|
250
|
+
case "${storage}/" in "${WORKSPACE_ROOT}/"*) echo " ✗ ${storage} is inside the workspace." ; return 1 ;; esac
|
|
251
|
+
storage_mounted || { echo " ✗ ${storage} is not mounted on its own filesystem." ; return 1 ; } ;
|
|
252
|
+
[ -w "${storage}" ] || { echo " ✗ ${storage} is not writable by $(id -un)." ; return 1 ; } ;
|
|
253
|
+
[ "$(storage_state "${w}")" == 'local' ] || { echo " ✗ ${w} is not a local folder ($(storage_state "${w}"))." ; return 1 ; } ;
|
|
254
|
+
if storage_has_ddev "${src}" ; then
|
|
255
|
+
echo " ✗ ${w} holds DDEV projects. DDEV bind-mounts each project into its containers, and that" ;
|
|
256
|
+
echo " is not verified across a symlink onto another disk. Back them up or remove them first." ;
|
|
257
|
+
return 1 ;
|
|
258
|
+
fi
|
|
259
|
+
[ -e "${src}.pre-move" ] && { echo " ✗ ${src}.pre-move already exists. Remove it or move it aside first." ; return 1 ; } ;
|
|
260
|
+
if [ -e "${dest}" ] && [ -n "$(ls -A "${dest}" 2>/dev/null)" ] ; then
|
|
261
|
+
echo " ✗ ${dest} already holds files. Nothing on the second storage is overwritten." ; return 1 ;
|
|
262
|
+
fi
|
|
263
|
+
size="$(storage_size_bytes "${src}")" ;
|
|
264
|
+
if ! storage_has_room "${size}" "${storage}" ; then
|
|
265
|
+
echo " ✗ Not enough room: ${w} is $(storage_human "${size}"), ${storage} has $(storage_human "$(storage_avail_bytes "${storage}")") free (5%, at least 1 GiB, is kept spare)." ;
|
|
266
|
+
return 1 ;
|
|
267
|
+
fi
|
|
268
|
+
echo "Moving ${w} ($(storage_human "${size}")) to ${dest}" ;
|
|
269
|
+
if [ "${DRY_RUN:-}" == 'yes' ] ; then echo " dry run: nothing copied." ; return 0 ; fi
|
|
270
|
+
echo " Nothing should write into ${w} until this finishes." ;
|
|
271
|
+
mkdir -p "${dest}" || return 1 ;
|
|
272
|
+
storage_copy_verified "${src}" "${dest}" || return 1 ;
|
|
273
|
+
mv "${src}" "${src}.pre-move" || return 1 ;
|
|
274
|
+
if ! ln -s "${dest}" "${src}" ; then
|
|
275
|
+
mv "${src}.pre-move" "${src}" ; echo " ✗ Could not make the link; ${w} is back where it was." ; return 1 ;
|
|
276
|
+
fi
|
|
277
|
+
echo " ✓ ${src} → ${dest}" ;
|
|
278
|
+
echo " The original is kept as ${src}.pre-move. When you are happy, free it with:" ;
|
|
279
|
+
echo " bash ${WORKSPACE_SCRIPTS}/storage.sh --remove-pre-move ${w}" ;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function storage_back() {
|
|
283
|
+
local w="$1" src target stage size ;
|
|
284
|
+
src="${WORKSPACE_ROOT}/${w}" ;
|
|
285
|
+
storage_is_workspace "${w}" || { echo " ✗ ${w} is not a workspace in settings.yml." ; return 1 ; } ;
|
|
286
|
+
case "$(storage_state "${w}")" in
|
|
287
|
+
second|elsewhere) ;;
|
|
288
|
+
broken) echo " ✗ ${w} links to $(readlink "${src}"), which is not there. Mount the drive first." ; return 1 ;;
|
|
289
|
+
*) echo " ✗ ${w} is not on second storage." ; return 1 ;;
|
|
290
|
+
esac
|
|
291
|
+
target="$(readlink -f "${src}")" ;
|
|
292
|
+
# A kept pre-move copy is the cheapest place to start: rsync only sends what changed since.
|
|
293
|
+
stage="${src}.pre-move" ;
|
|
294
|
+
[ -d "${stage}" ] || stage="${WORKSPACE_ROOT}/.${w}.moving-back" ;
|
|
295
|
+
size="$(storage_size_bytes "${target}")" ;
|
|
296
|
+
if ! storage_has_room "${size}" "${WORKSPACE_ROOT}" ; then
|
|
297
|
+
echo " ✗ Not enough room: ${w} is $(storage_human "${size}"), the workspace disk has $(storage_human "$(storage_avail_bytes "${WORKSPACE_ROOT}")") free." ;
|
|
298
|
+
return 1 ;
|
|
299
|
+
fi
|
|
300
|
+
echo "Moving ${w} ($(storage_human "${size}")) back from ${target}" ;
|
|
301
|
+
if [ "${DRY_RUN:-}" == 'yes' ] ; then echo " dry run: nothing copied." ; return 0 ; fi
|
|
302
|
+
mkdir -p "${stage}" || return 1 ;
|
|
303
|
+
storage_copy_verified "${target}" "${stage}" || return 1 ;
|
|
304
|
+
unlink "${src}" || return 1 ;
|
|
305
|
+
if ! mv "${stage}" "${src}" ; then
|
|
306
|
+
ln -s "${target}" "${src}" ; echo " ✗ Could not put the folder in place; the link is restored." ; return 1 ;
|
|
307
|
+
fi
|
|
308
|
+
echo " ✓ ${src} is a local folder again." ;
|
|
309
|
+
echo " The copy on the second storage is kept: ${target}" ;
|
|
310
|
+
echo " Delete it by hand when you no longer want it; this tool never deletes on the drive." ;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function storage_remove_pre_move() {
|
|
314
|
+
local w="$1" kept="${WORKSPACE_ROOT}/$1.pre-move" ;
|
|
315
|
+
[ -d "${kept}" ] || { echo " ✗ There is no ${kept}." ; return 1 ; } ;
|
|
316
|
+
case "$(storage_state "${w}")" in
|
|
317
|
+
second|elsewhere) ;;
|
|
318
|
+
*) echo " ✗ ${w} is not a working link to second storage, so ${kept} may be the only copy. Kept." ; return 1 ;;
|
|
319
|
+
esac
|
|
320
|
+
[ -n "$(ls -A "${WORKSPACE_ROOT}/${w}/" 2>/dev/null)" ] || { echo " ✗ ${w} on second storage is empty. Kept ${kept}." ; return 1 ; } ;
|
|
321
|
+
if [ "${DRY_RUN:-}" == 'yes' ] ; then echo " dry run: would remove ${kept}." ; return 0 ; fi
|
|
322
|
+
rm -rf "${kept}" && echo " ✓ removed ${kept}" ;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
# Point links at the configured path when the same folder is there (the drive got a new mount point).
|
|
326
|
+
function storage_relink() {
|
|
327
|
+
local storage w link target changed=0 ;
|
|
328
|
+
storage="$(storage_path)" ;
|
|
329
|
+
[ -n "${storage}" ] || { echo " ✗ Set storage.second in settings.yml first." ; return 1 ; } ;
|
|
330
|
+
for w in "${workspaces[@]}" ; do
|
|
331
|
+
link="${WORKSPACE_ROOT}/${w}" ;
|
|
332
|
+
[ -L "${link}" ] || continue ;
|
|
333
|
+
target="$(readlink "${link}")" ;
|
|
334
|
+
[ "${target%/}" == "${storage}/${w}" ] && continue ;
|
|
335
|
+
if [ -d "${storage}/${w}" ] && [ -n "$(ls -A "${storage}/${w}" 2>/dev/null)" ] ; then
|
|
336
|
+
if [ "${DRY_RUN:-}" == 'yes' ] ; then
|
|
337
|
+
echo " would relink ${w}: ${target} → ${storage}/${w}" ;
|
|
338
|
+
else
|
|
339
|
+
ln -sfn "${storage}/${w}" "${link}" && echo " ✓ ${w}: ${target} → ${storage}/${w}" ;
|
|
340
|
+
fi
|
|
341
|
+
changed=$((changed+1)) ;
|
|
342
|
+
else
|
|
343
|
+
echo " ! ${w} links to ${target}; ${storage}/${w} is not there, so it is left alone." ;
|
|
344
|
+
fi
|
|
345
|
+
done
|
|
346
|
+
[ "${changed}" -gt 0 ] || storage_say " ✓ every link already points at ${storage}" ;
|
|
347
|
+
}
|