@webship.co/workspace 1.0.0-alpha1 → 1.0.0-beta2
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} +1 -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/{plugin-loader.js → core/workspace-app/plugin-loader.js} +19 -1
- package/{plugins-page.js → core/workspace-app/plugins-page.js} +28 -2
- package/{row-layout.js → core/workspace-app/row-layout.js} +0 -1
- package/{server.js → core/workspace-app/server.js} +26 -225
- 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 +39 -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 +40 -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 +152 -0
- package/core/workspace-app/tests/step-definitions/preconditions.steps.js +100 -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 +177 -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 +153 -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/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 +74 -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 +198 -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 +417 -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/{workspace-types.js → core/workspace-app/workspace-types.js} +2 -2
- package/{workspaces.js → core/workspace-app/workspaces.js} +1 -5
- package/package.json +38 -36
- package/actions/graphs.js +0 -107
- package/actions/obsidian.js +0 -59
- package/graphs.js +0 -104
- package/obsidian.js +0 -405
- /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-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/{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
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Voices for the audio workspace: which engines this machine can use, and one text spoken by one of them.
|
|
3
|
+
|
|
4
|
+
Piper is the local voice of the voice-narration skill: free, offline, no key. ElevenLabs is offered when
|
|
5
|
+
a key is set, and what it offers follows what that key is allowed to read. Standard library only.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import argparse
|
|
9
|
+
import datetime
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
import re
|
|
13
|
+
import shutil
|
|
14
|
+
import subprocess
|
|
15
|
+
import sys
|
|
16
|
+
import tempfile
|
|
17
|
+
|
|
18
|
+
sys.dont_write_bytecode = True # importing the engine must not leave a __pycache__ in the tooling tree
|
|
19
|
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
20
|
+
import elevenlabs as el # noqa: E402
|
|
21
|
+
|
|
22
|
+
OK, ERROR, USAGE = el.OK, el.ERROR, el.USAGE
|
|
23
|
+
MODELS = [('eleven_v4', 'Eleven v4: the default'),
|
|
24
|
+
('eleven_v3', 'Eleven v3: most expressive, 70+ languages'),
|
|
25
|
+
('eleven_multilingual_v2', 'Multilingual v2: steady and lifelike, 29 languages'),
|
|
26
|
+
('eleven_flash_v2_5', 'Flash v2.5: fastest, 32 languages, half the credits'),
|
|
27
|
+
('eleven_turbo_v2_5', 'Turbo v2.5: quick and balanced, 32 languages, half the credits'),
|
|
28
|
+
('eleven_flash_v2', 'Flash v2: English only, fast, half the credits'),
|
|
29
|
+
('eleven_turbo_v2', 'Turbo v2: English only, low latency, half the credits')]
|
|
30
|
+
CATALOG_LINE = re.compile(r'^ ([ !])(\d+)\) (\S+)\s+(.*)$')
|
|
31
|
+
NOT_INSTALLED = ('Piper is not installed where this runs. Install it on the host with the voice-narration skill '
|
|
32
|
+
'(scripts/install-voice.sh); the dashboard container has none.')
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def home(*parts):
|
|
36
|
+
return os.path.join(os.path.expanduser('~'), *parts)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def piper_bin():
|
|
40
|
+
for candidate in (os.environ.get('PIPER_BIN'), home('.local', 'bin', 'piper'), shutil.which('piper')):
|
|
41
|
+
if candidate and os.access(candidate, os.X_OK):
|
|
42
|
+
return candidate
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def voice_dir():
|
|
47
|
+
return os.environ.get('PIPER_VOICE_DIR') or home('.local', 'share', 'piper-voices')
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def say_script():
|
|
51
|
+
for candidate in (home('.claude', 'skills', 'voice-narration', 'scripts', 'claude-say'), home('.local', 'bin', 'claude-say')):
|
|
52
|
+
if os.path.isfile(candidate):
|
|
53
|
+
return candidate
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def piper_voices():
|
|
58
|
+
"""The voice-narration catalog, in its numbered order, with what is installed marked."""
|
|
59
|
+
voices = []
|
|
60
|
+
script = say_script()
|
|
61
|
+
if script:
|
|
62
|
+
try:
|
|
63
|
+
listing = subprocess.run(['bash', script, 'list'], capture_output=True, text=True, timeout=20).stdout
|
|
64
|
+
except (OSError, subprocess.SubprocessError):
|
|
65
|
+
listing = ''
|
|
66
|
+
for line in listing.splitlines():
|
|
67
|
+
found = CATALOG_LINE.match(line)
|
|
68
|
+
if found:
|
|
69
|
+
voices.append({'id': found.group(3), 'number': int(found.group(2)), 'label': found.group(4).strip(),
|
|
70
|
+
'installed': os.path.isfile(os.path.join(voice_dir(), found.group(3) + '.onnx'))})
|
|
71
|
+
known = {v['id'] for v in voices}
|
|
72
|
+
try:
|
|
73
|
+
extra = sorted(n[:-5] for n in os.listdir(voice_dir()) if n.endswith('.onnx') and n[:-5] not in known)
|
|
74
|
+
except OSError:
|
|
75
|
+
extra = []
|
|
76
|
+
voices += [{'id': name, 'number': None, 'label': 'Installed', 'installed': True} for name in extra]
|
|
77
|
+
return voices
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def write_belongings(out, text, record):
|
|
81
|
+
"""What belongs to a generated file, kept beside it: the text it was made from, and how it was made."""
|
|
82
|
+
stem = os.path.splitext(out)[0]
|
|
83
|
+
record = dict(record, file=os.path.basename(out), created_utc=datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ'))
|
|
84
|
+
try:
|
|
85
|
+
el.write_atomic(stem + '.txt', (text.strip() + '\n').encode('utf-8'), True)
|
|
86
|
+
el.write_atomic(stem + '.json', (json.dumps(record, indent=2, ensure_ascii=False) + '\n').encode('utf-8'), True)
|
|
87
|
+
except OSError as error:
|
|
88
|
+
print('The audio was made, but its text and record could not be saved: %s' % error, file=sys.stderr)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def piper_info():
|
|
92
|
+
info = {'id': 'piper', 'label': 'Piper (voice-narration, local)', 'free': True, 'needs_key': False,
|
|
93
|
+
'available': False, 'reason': '', 'reason_code': '', 'voices': [], 'models': []}
|
|
94
|
+
binary = piper_bin()
|
|
95
|
+
voices = piper_voices()
|
|
96
|
+
info['voices'] = voices
|
|
97
|
+
if not binary:
|
|
98
|
+
info.update(reason=NOT_INSTALLED, reason_code='no-binary')
|
|
99
|
+
elif not any(v['installed'] for v in voices):
|
|
100
|
+
info.update(reason='Piper is installed but no voice model is in %s.' % voice_dir(), reason_code='no-voices')
|
|
101
|
+
else:
|
|
102
|
+
info['available'] = True
|
|
103
|
+
info['default_voice'] = next((v['id'] for v in voices if v['installed']), '')
|
|
104
|
+
info['formats'] = ['wav'] + (['mp3'] if shutil.which('ffmpeg') else [])
|
|
105
|
+
return info
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def elevenlabs_info():
|
|
109
|
+
info = {'id': 'elevenlabs', 'label': 'ElevenLabs', 'free': False, 'needs_key': True, 'available': False, 'reason': '',
|
|
110
|
+
'voices': [], 'models': [{'id': m, 'label': label} for m, label in MODELS],
|
|
111
|
+
'default_model': os.environ.get('ELEVENLABS_MODEL') or el.DEFAULT_MODEL,
|
|
112
|
+
'default_voice': os.environ.get('ELEVENLABS_VOICE', ''), 'formats': ['mp3'],
|
|
113
|
+
'can_read_account': False, 'can_list_voices': False}
|
|
114
|
+
try:
|
|
115
|
+
el.key()
|
|
116
|
+
except el.Stop as stop:
|
|
117
|
+
info['reason'] = str(stop)
|
|
118
|
+
return info
|
|
119
|
+
try:
|
|
120
|
+
found, why = el.account()
|
|
121
|
+
status, _, body = el.call('GET', '/v2/voices', query={'page_size': '100'})
|
|
122
|
+
except el.Stop as stop:
|
|
123
|
+
info['reason'] = str(stop)
|
|
124
|
+
return info
|
|
125
|
+
if found:
|
|
126
|
+
info.update(can_read_account=True, plan=found['tier'], remaining=found['remaining'], limit=found['limit'])
|
|
127
|
+
else:
|
|
128
|
+
info['account_note'] = why
|
|
129
|
+
if status == 200:
|
|
130
|
+
info['can_list_voices'] = True
|
|
131
|
+
info['voices'] = [{'id': v.get('voice_id'), 'label': v.get('name') or v.get('voice_id'), 'category': v.get('category'),
|
|
132
|
+
'labels': v.get('labels') or {}} for v in json.loads(body).get('voices', [])]
|
|
133
|
+
# A default given by name (Brian) is the voice whose name starts with it.
|
|
134
|
+
want = info['default_voice'].strip().lower()
|
|
135
|
+
if want and not any(v['id'] == info['default_voice'] for v in info['voices']):
|
|
136
|
+
hit = next((v for v in info['voices'] if (v['label'] or '').lower().startswith(want)), None)
|
|
137
|
+
info['default_voice'] = hit['id'] if hit else ''
|
|
138
|
+
else:
|
|
139
|
+
info['voices_note'] = el.explain(status, body)
|
|
140
|
+
# When the key may read models, any speech model it offers beyond the built-in list is added.
|
|
141
|
+
try:
|
|
142
|
+
listed, _, models = el.call('GET', '/v1/models')
|
|
143
|
+
except el.Stop:
|
|
144
|
+
listed, models = 0, '[]'
|
|
145
|
+
if listed == 200:
|
|
146
|
+
info['can_list_models'] = True
|
|
147
|
+
known = {m['id'] for m in info['models']}
|
|
148
|
+
for m in json.loads(models):
|
|
149
|
+
if m.get('can_do_text_to_speech') and m.get('model_id') and m['model_id'] not in known:
|
|
150
|
+
info['models'].append({'id': m['model_id'], 'label': m.get('name') or m['model_id']})
|
|
151
|
+
# A rejected key fails both reads with 401; a key that only lacks a scope fails with 403 and can still speak.
|
|
152
|
+
if not found and status == 401:
|
|
153
|
+
info['reason'] = 'ElevenLabs rejected the key.'
|
|
154
|
+
return info
|
|
155
|
+
info['available'] = True
|
|
156
|
+
return info
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def cmd_engines(args):
|
|
160
|
+
engines = [piper_info(), elevenlabs_info()]
|
|
161
|
+
lines = []
|
|
162
|
+
for e in engines:
|
|
163
|
+
lines.append('%s: %s' % (e['label'], 'available' if e['available'] else 'not available. ' + e['reason']))
|
|
164
|
+
if e['available']:
|
|
165
|
+
lines.append(' %d voices%s' % (len(e['voices']), '' if e['id'] == 'piper' or e['can_list_voices'] else ' (the key cannot list them; pass --voice <id>)'))
|
|
166
|
+
if e.get('remaining') is not None:
|
|
167
|
+
lines.append(' %s characters left on the %s plan' % (e['remaining'], e['plan']))
|
|
168
|
+
el.emit(args, {'engines': engines}, lines)
|
|
169
|
+
return OK
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def cmd_voices(args):
|
|
173
|
+
if args.engine == 'elevenlabs':
|
|
174
|
+
return el.main(['voices', '--limit', str(args.limit)] + (['--search', args.search] if args.search else []) + (['--json'] if args.json else []))
|
|
175
|
+
voices = piper_voices()
|
|
176
|
+
lines = ['%-3s %-24s %-9s %s' % ('#', 'voice', 'installed', 'about')] + [
|
|
177
|
+
'%-3s %-24s %-9s %s' % (v['number'] or '', v['id'], 'yes' if v['installed'] else 'no', v['label']) for v in voices]
|
|
178
|
+
el.emit(args, {'voices': voices}, lines)
|
|
179
|
+
return OK
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def read_text(path):
|
|
183
|
+
try:
|
|
184
|
+
with open(path, encoding='utf-8') as handle:
|
|
185
|
+
return handle.read().strip()
|
|
186
|
+
except OSError:
|
|
187
|
+
return ''
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def cmd_speak(args):
|
|
191
|
+
if not args.out:
|
|
192
|
+
print('Pass --out <file>.', file=sys.stderr)
|
|
193
|
+
return USAGE
|
|
194
|
+
if args.engine == 'elevenlabs':
|
|
195
|
+
argv = ['speak', '--text-file', args.text_file, '--out', args.out, '--voice', args.voice, '--model', args.model]
|
|
196
|
+
code = el.main(argv + (['--confirm'] if args.confirm else []) + (['--force'] if args.force else []) + (['--json'] if args.json else []))
|
|
197
|
+
if code == OK:
|
|
198
|
+
write_belongings(args.out, read_text(args.text_file), {'kind': 'voice', 'engine': 'elevenlabs', 'voice': args.voice, 'model': args.model})
|
|
199
|
+
return code
|
|
200
|
+
return speak_piper(args)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def speak_piper(args):
|
|
204
|
+
text = read_text(args.text_file)
|
|
205
|
+
if not text:
|
|
206
|
+
print('The text file is missing or empty: %s' % args.text_file, file=sys.stderr)
|
|
207
|
+
return USAGE
|
|
208
|
+
binary = piper_bin()
|
|
209
|
+
if not binary:
|
|
210
|
+
print(NOT_INSTALLED, file=sys.stderr)
|
|
211
|
+
return ERROR
|
|
212
|
+
installed = [v['id'] for v in piper_voices() if v['installed']]
|
|
213
|
+
voice = args.voice or (installed[0] if installed else '')
|
|
214
|
+
model = os.path.join(voice_dir(), voice + '.onnx')
|
|
215
|
+
if not voice or not os.path.isfile(model):
|
|
216
|
+
print('The Piper voice "%s" is not installed. Installed: %s.' % (voice, ', '.join(installed) or 'none'), file=sys.stderr)
|
|
217
|
+
return USAGE
|
|
218
|
+
ext = os.path.splitext(args.out)[1].lower()
|
|
219
|
+
if ext not in ('.wav', '.mp3'):
|
|
220
|
+
print('Piper writes .wav, or .mp3 when ffmpeg is installed. Use one of those for --out.', file=sys.stderr)
|
|
221
|
+
return USAGE
|
|
222
|
+
if ext == '.mp3' and not shutil.which('ffmpeg'):
|
|
223
|
+
print('ffmpeg is not installed here, so Piper cannot make an mp3. Use a .wav name.', file=sys.stderr)
|
|
224
|
+
return USAGE
|
|
225
|
+
try:
|
|
226
|
+
el.ensure_free([args.out], args.force)
|
|
227
|
+
except el.Stop as stop:
|
|
228
|
+
print(stop, file=sys.stderr)
|
|
229
|
+
return stop.code
|
|
230
|
+
wav = args.out if ext == '.wav' else args.out + '.tmp.wav'
|
|
231
|
+
try:
|
|
232
|
+
subprocess.run([binary, '--model', model, '--output_file', wav], input=text.encode('utf-8'), capture_output=True, check=True, timeout=300)
|
|
233
|
+
if ext == '.mp3':
|
|
234
|
+
subprocess.run(['ffmpeg', '-y', '-loglevel', 'error', '-i', wav, '-codec:a', 'libmp3lame', '-b:a', '128k', args.out], check=True, timeout=300)
|
|
235
|
+
except (OSError, subprocess.SubprocessError) as error:
|
|
236
|
+
print('Piper failed: %s' % getattr(error, 'stderr', error), file=sys.stderr)
|
|
237
|
+
return ERROR
|
|
238
|
+
finally:
|
|
239
|
+
if wav != args.out and os.path.exists(wav):
|
|
240
|
+
os.remove(wav)
|
|
241
|
+
write_belongings(args.out, text, {'kind': 'voice', 'engine': 'piper', 'voice': voice})
|
|
242
|
+
el.emit(args, {'engine': 'piper', 'voice': voice, 'characters': len(text), 'out': args.out},
|
|
243
|
+
['Wrote %s (voice %s, %d characters).' % (args.out, voice, len(text))])
|
|
244
|
+
return OK
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def piper_synthesize(text, voice, model, fmt):
|
|
248
|
+
"""The engine's synthesize(), spoken by Piper: one section in, mp3 bytes out."""
|
|
249
|
+
binary = piper_bin()
|
|
250
|
+
if not binary:
|
|
251
|
+
raise el.Stop(NOT_INSTALLED)
|
|
252
|
+
path = os.path.join(voice_dir(), voice + '.onnx')
|
|
253
|
+
if not os.path.isfile(path):
|
|
254
|
+
raise el.Stop('The Piper voice "%s" is not installed.' % voice, USAGE)
|
|
255
|
+
with tempfile.TemporaryDirectory() as work:
|
|
256
|
+
wav, mp3 = os.path.join(work, 's.wav'), os.path.join(work, 's.mp3')
|
|
257
|
+
try:
|
|
258
|
+
subprocess.run([binary, '--model', path, '--output_file', wav], input=text.encode('utf-8'), capture_output=True, check=True, timeout=300)
|
|
259
|
+
subprocess.run(['ffmpeg', '-y', '-loglevel', 'error', '-i', wav, '-codec:a', 'libmp3lame', '-b:a', '128k', mp3], check=True, timeout=300)
|
|
260
|
+
except (OSError, subprocess.SubprocessError) as error:
|
|
261
|
+
raise el.Stop('Piper failed: %s' % getattr(error, 'stderr', error))
|
|
262
|
+
with open(mp3, 'rb') as handle:
|
|
263
|
+
return handle.read(), {'engine': 'piper', 'voice': voice}
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def timed_script(path, video):
|
|
267
|
+
"""The script the engine reads. A transcript with no timed sections is spread over the whole video."""
|
|
268
|
+
text = read_text(path)
|
|
269
|
+
if not text:
|
|
270
|
+
raise el.Stop('The script is missing or empty: %s' % path, USAGE)
|
|
271
|
+
if any(el.HEADING.match(line) for line in text.splitlines()):
|
|
272
|
+
return path, None
|
|
273
|
+
length = el.duration(video)
|
|
274
|
+
if not length:
|
|
275
|
+
raise el.Stop('Cannot read the video duration: %s' % video)
|
|
276
|
+
lines = [re.sub(r'^\s*(?:[-*+]\s+|\d+[.)]\s+|>\s*)', '', line).strip() for line in text.splitlines()]
|
|
277
|
+
spoken = [line for line in lines if line and not line.startswith('#')]
|
|
278
|
+
if not spoken:
|
|
279
|
+
raise el.Stop('The script has nothing to say: %s' % path, USAGE)
|
|
280
|
+
work = tempfile.mkdtemp(prefix='ws-voice-script-')
|
|
281
|
+
timed = os.path.join(work, 'script.md')
|
|
282
|
+
with open(timed, 'w', encoding='utf-8') as handle:
|
|
283
|
+
handle.write('## S01 Narration (00:00.0 to %s)\n\n%s\n' % (el.mmss(length), '\n'.join('> ' + line for line in spoken)))
|
|
284
|
+
return timed, work
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def cmd_voiceover(args):
|
|
288
|
+
if args.engine == 'piper':
|
|
289
|
+
installed = [v['id'] for v in piper_voices() if v['installed']]
|
|
290
|
+
args.voice = args.voice or (installed[0] if installed else '')
|
|
291
|
+
if not piper_bin() or args.voice not in installed:
|
|
292
|
+
print(NOT_INSTALLED if not piper_bin() else 'The Piper voice "%s" is not installed. Installed: %s.' % (args.voice, ', '.join(installed) or 'none'), file=sys.stderr)
|
|
293
|
+
return USAGE if piper_bin() else ERROR
|
|
294
|
+
# Piper is local and free: no key, no credits, and the engine's section loop speaks through it.
|
|
295
|
+
el.key = lambda: ''
|
|
296
|
+
el.remaining_characters = lambda: None
|
|
297
|
+
el.synthesize = piper_synthesize
|
|
298
|
+
el.resolve_voice = lambda value: value # a Piper voice is never looked up on ElevenLabs
|
|
299
|
+
args.model = 'piper'
|
|
300
|
+
script, work = timed_script(args.script, args.video)
|
|
301
|
+
try:
|
|
302
|
+
argv = ['voiceover', '--script', script, '--video', args.video, '--out', args.out, '--voice', args.voice, '--model', args.model]
|
|
303
|
+
flags = [f for on, f in ((args.confirm, '--confirm'), (args.force, '--force'), (args.json, '--json'), (args.drop_optional, '--drop-optional')) if on]
|
|
304
|
+
return el.main(argv + flags)
|
|
305
|
+
finally:
|
|
306
|
+
if work:
|
|
307
|
+
shutil.rmtree(work, ignore_errors=True)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
SOUND_FORMAT = 'mp3_44100_128'
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def _generate(args, endpoint, body, label, plan, text):
|
|
314
|
+
"""A sound or a piece of music from ElevenLabs: plan first, send only with --confirm."""
|
|
315
|
+
el.key()
|
|
316
|
+
plan = dict(plan, remaining=el.remaining_characters(), out=args.out)
|
|
317
|
+
if not args.confirm:
|
|
318
|
+
el.emit(args, dict(plan, confirmed=False), ['Plan only, nothing was sent.'] + ['%s: %s' % kv for kv in plan.items() if kv[0] != 'remaining']
|
|
319
|
+
+ ['Credits left: %s.' % ('unknown' if plan['remaining'] is None else plan['remaining']), 'Run again with --confirm to create %s.' % args.out])
|
|
320
|
+
return el.PLAN_ONLY
|
|
321
|
+
el.ensure_free([args.out], args.force)
|
|
322
|
+
status, _, data = el.call('POST', endpoint, body=json.dumps(body).encode(), content_type='application/json', query={'output_format': SOUND_FORMAT})
|
|
323
|
+
if status != 200:
|
|
324
|
+
raise el.Stop('%s failed. %s' % (label, el.explain(status, data)))
|
|
325
|
+
el.write_atomic(args.out, data, args.force)
|
|
326
|
+
write_belongings(args.out, text, {'kind': plan['kind'], 'engine': 'elevenlabs', **{k: v for k, v in plan.items() if k not in ('kind', 'remaining', 'out', 'prompt')}})
|
|
327
|
+
el.emit(args, dict(plan, confirmed=True, bytes=len(data)), ['Wrote %s (%d bytes).' % (args.out, len(data))])
|
|
328
|
+
return OK
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def cmd_sound(args):
|
|
332
|
+
text = read_text(args.text_file)
|
|
333
|
+
if not text:
|
|
334
|
+
print('The description is missing or empty: %s' % args.text_file, file=sys.stderr)
|
|
335
|
+
return USAGE
|
|
336
|
+
if args.duration is not None and not 0.5 <= args.duration <= 30:
|
|
337
|
+
print('A sound runs from 0.5 to 30 seconds.', file=sys.stderr)
|
|
338
|
+
return USAGE
|
|
339
|
+
if not 0 <= args.influence <= 1:
|
|
340
|
+
print('--influence is between 0 and 1.', file=sys.stderr)
|
|
341
|
+
return USAGE
|
|
342
|
+
body = {'text': text, 'prompt_influence': args.influence}
|
|
343
|
+
if args.duration is not None:
|
|
344
|
+
body['duration_seconds'] = args.duration
|
|
345
|
+
if args.loop:
|
|
346
|
+
body['loop'] = True
|
|
347
|
+
return _generate(args, '/v1/sound-generation', body, 'Sound generation',
|
|
348
|
+
{'kind': 'sound', 'prompt': text[:120], 'duration': args.duration if args.duration is not None else 'chosen by ElevenLabs', 'loop': args.loop}, text)
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def cmd_music(args):
|
|
352
|
+
text = read_text(args.text_file)
|
|
353
|
+
if not text:
|
|
354
|
+
print('The description is missing or empty: %s' % args.text_file, file=sys.stderr)
|
|
355
|
+
return USAGE
|
|
356
|
+
if not 3 <= args.length <= 600:
|
|
357
|
+
print('Music runs from 3 to 600 seconds.', file=sys.stderr)
|
|
358
|
+
return USAGE
|
|
359
|
+
body = {'prompt': text, 'music_length_ms': int(round(args.length * 1000)), 'force_instrumental': bool(args.instrumental)}
|
|
360
|
+
return _generate(args, '/v1/music', body, 'Music generation',
|
|
361
|
+
{'kind': 'music', 'prompt': text[:120], 'seconds': args.length, 'instrumental': args.instrumental}, text)
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def build_parser():
|
|
365
|
+
parser = argparse.ArgumentParser(prog='voice.py', description='Voices for the audio workspace.')
|
|
366
|
+
sub = parser.add_subparsers(dest='command', required=True)
|
|
367
|
+
engines = sub.add_parser('engines', help='the engines this machine can use')
|
|
368
|
+
engines.add_argument('--json', action='store_true')
|
|
369
|
+
voices = sub.add_parser('voices', help='the voices of one engine')
|
|
370
|
+
voices.add_argument('--engine', choices=('piper', 'elevenlabs'), required=True)
|
|
371
|
+
voices.add_argument('--search', type=el.text_arg, default='')
|
|
372
|
+
voices.add_argument('--limit', type=int, default=30)
|
|
373
|
+
voices.add_argument('--json', action='store_true')
|
|
374
|
+
speak = sub.add_parser('speak', help='one text, one file')
|
|
375
|
+
speak.add_argument('--engine', choices=('piper', 'elevenlabs'), required=True)
|
|
376
|
+
speak.add_argument('--text-file', type=el.text_arg, required=True)
|
|
377
|
+
speak.add_argument('--out', type=el.text_arg, required=True)
|
|
378
|
+
speak.add_argument('--voice', type=el.text_arg, default='')
|
|
379
|
+
speak.add_argument('--model', type=el.text_arg, default=os.environ.get('ELEVENLABS_MODEL') or el.DEFAULT_MODEL)
|
|
380
|
+
speak.add_argument('--confirm', action='store_true', help='ElevenLabs only: actually send; without it only the plan is printed')
|
|
381
|
+
speak.add_argument('--force', action='store_true')
|
|
382
|
+
speak.add_argument('--json', action='store_true')
|
|
383
|
+
sound = sub.add_parser('sound', help='a sound effect from a description (ElevenLabs)')
|
|
384
|
+
sound.add_argument('--text-file', type=el.text_arg, required=True)
|
|
385
|
+
sound.add_argument('--out', type=el.text_arg, required=True)
|
|
386
|
+
sound.add_argument('--duration', type=float, default=None, help='0.5 to 30 seconds; left out, ElevenLabs chooses')
|
|
387
|
+
sound.add_argument('--loop', action='store_true', help='a sound that repeats without a seam')
|
|
388
|
+
sound.add_argument('--influence', type=float, default=0.3, help='how closely to follow the description, 0 to 1')
|
|
389
|
+
sound.add_argument('--confirm', action='store_true')
|
|
390
|
+
sound.add_argument('--force', action='store_true')
|
|
391
|
+
sound.add_argument('--json', action='store_true')
|
|
392
|
+
music = sub.add_parser('music', help='music from a description (ElevenLabs)')
|
|
393
|
+
music.add_argument('--text-file', type=el.text_arg, required=True)
|
|
394
|
+
music.add_argument('--out', type=el.text_arg, required=True)
|
|
395
|
+
music.add_argument('--length', type=float, default=30, help='3 to 600 seconds')
|
|
396
|
+
music.add_argument('--instrumental', action='store_true', help='no vocals')
|
|
397
|
+
music.add_argument('--confirm', action='store_true')
|
|
398
|
+
music.add_argument('--force', action='store_true')
|
|
399
|
+
music.add_argument('--json', action='store_true')
|
|
400
|
+
over = sub.add_parser('voiceover', help='a script, spoken over a video')
|
|
401
|
+
over.add_argument('--engine', choices=('piper', 'elevenlabs'), required=True)
|
|
402
|
+
over.add_argument('--script', type=el.text_arg, required=True)
|
|
403
|
+
over.add_argument('--video', type=el.text_arg, required=True)
|
|
404
|
+
over.add_argument('--out', type=el.text_arg, required=True)
|
|
405
|
+
over.add_argument('--voice', type=el.text_arg, default='')
|
|
406
|
+
over.add_argument('--model', type=el.text_arg, default=os.environ.get('ELEVENLABS_MODEL') or el.DEFAULT_MODEL)
|
|
407
|
+
over.add_argument('--confirm', action='store_true')
|
|
408
|
+
over.add_argument('--force', action='store_true')
|
|
409
|
+
over.add_argument('--drop-optional', action='store_true')
|
|
410
|
+
over.add_argument('--json', action='store_true')
|
|
411
|
+
return parser
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def main(argv):
|
|
415
|
+
args = build_parser().parse_args(argv)
|
|
416
|
+
if args.command in ('speak', 'voiceover') and args.engine == 'elevenlabs' and not args.voice:
|
|
417
|
+
args.voice = os.environ.get('ELEVENLABS_VOICE', '')
|
|
418
|
+
if not args.voice:
|
|
419
|
+
print('No ElevenLabs voice. Pass --voice <id>, or set voice_id in settings.yml (list them: -a voices --engine elevenlabs).', file=sys.stderr)
|
|
420
|
+
return USAGE
|
|
421
|
+
try:
|
|
422
|
+
return {'engines': cmd_engines, 'voices': cmd_voices, 'speak': cmd_speak, 'voiceover': cmd_voiceover,
|
|
423
|
+
'sound': cmd_sound, 'music': cmd_music}[args.command](args)
|
|
424
|
+
except el.Stop as stop:
|
|
425
|
+
print(stop, file=sys.stderr)
|
|
426
|
+
return stop.code
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
if __name__ == '__main__':
|
|
430
|
+
sys.exit(main(sys.argv[1:]))
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
## The host side of the Jira plugin: twg, the Teamwork Graph CLI, on this machine and signed in.
|
|
3
|
+
## Run by core/scripts/plugins.sh --install jira (--check: report only) and in full by install.sh.
|
|
4
|
+
## Offered, never automatic: twg is one OAuth session per person, not a machine-wide tool.
|
|
5
|
+
set -u
|
|
6
|
+
confirm() { local a; read -r -p "$1 [y/N] " a < /dev/tty 2>/dev/null || return 1; [[ "${a}" =~ ^[Yy] ]]; }
|
|
7
|
+
|
|
8
|
+
# Docker CREATES a missing bind-mount source as a root-owned directory. The plugin mounts ~/.local/bin
|
|
9
|
+
# (the directory, so `twg update` on the host is live in the container) and ~/.config/twg; a twg that
|
|
10
|
+
# is a DIRECTORY at ~/.local/bin/twg is such a placeholder from an older mount, and blocks installing.
|
|
11
|
+
TWG_PLACEHOLDER="${HOME}/.local/bin/twg"
|
|
12
|
+
if [ -d "${TWG_PLACEHOLDER}" ] ; then
|
|
13
|
+
if [ -z "$(ls -A "${TWG_PLACEHOLDER}" 2>/dev/null)" ] && rmdir "${TWG_PLACEHOLDER}" 2>/dev/null ; then
|
|
14
|
+
echo " ✓ removed the empty ${TWG_PLACEHOLDER/#${HOME}/~} placeholder, so twg can be installed there" ;
|
|
15
|
+
else
|
|
16
|
+
echo " ✗ ${TWG_PLACEHOLDER/#${HOME}/~} is a directory — remove it so twg can be installed there (sudo rmdir ${TWG_PLACEHOLDER})" ;
|
|
17
|
+
fi
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
# twg installs into ~/.local/bin, which is not always on PATH, so `command -v` alone
|
|
21
|
+
# would report it missing on a machine that has it. The dashboard looks for the binary
|
|
22
|
+
# at a path for the same reason.
|
|
23
|
+
TWG_BIN='';
|
|
24
|
+
for candidate in "$(command -v twg 2>/dev/null)" "${HOME}/.local/bin/twg" /usr/local/bin/twg ; do
|
|
25
|
+
[ -n "${candidate}" ] && [ -f "${candidate}" ] && [ -x "${candidate}" ] && { TWG_BIN="${candidate}"; break; }
|
|
26
|
+
done
|
|
27
|
+
if [ -n "${TWG_BIN}" ] ; then
|
|
28
|
+
# Installed is not the same as signed in, and the difference is the whole symptom:
|
|
29
|
+
# the dashboard's Jira gate is a BINARY check, so the button appears the moment the
|
|
30
|
+
# file exists and every Jira action then fails with `AUTH_REQUIRED`. Reporting only
|
|
31
|
+
# the binary sends you looking for a broken button instead of an unfinished login.
|
|
32
|
+
# Read from the CLI's own state file rather than by calling the API: a survey must
|
|
33
|
+
# not make a network round trip, and must never touch the token.
|
|
34
|
+
# Detect the ERROR shape, not a success marker. twg prints `{"projects": [...]}` on
|
|
35
|
+
# success with NO "ok" field at all — only failures carry `"ok": false` and a code — so
|
|
36
|
+
# the first version of this check (grep for `"ok": true`) reported EVERY signed-in CLI as
|
|
37
|
+
# signed out. Matching the failure is also robust to the success shape changing.
|
|
38
|
+
if ! "${TWG_BIN}" jira space list --limit 1 2>&1 | grep -qE '"ok": *false|AUTH_REQUIRED|authentication required' ; then
|
|
39
|
+
printf ' ✓ %-16s %s\n' "twg" "${TWG_BIN} — signed in" ;
|
|
40
|
+
else
|
|
41
|
+
printf ' ✗ %-16s %s\n' "twg" "${TWG_BIN} — NOT signed in: run ${TWG_BIN/#${HOME}/~} login --force" ;
|
|
42
|
+
printf ' %-18s %s\n' "" "The Jira button appears anyway (the gate is a binary check), and every" ;
|
|
43
|
+
printf ' %-18s %s\n' "" "Jira action answers 'authentication required' until you have signed in." ;
|
|
44
|
+
fi
|
|
45
|
+
else
|
|
46
|
+
printf ' ✗ %-16s %s\n' "twg" "missing — the Jira button stays hidden (teamworkgraph.com/cli)" ;
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
[ "${1:-}" == '--check' ] && exit 0 ;
|
|
51
|
+
|
|
52
|
+
# twg — the Teamwork Graph CLI, which is how the dashboard and the Jira agents read and
|
|
53
|
+
# write Jira. https://teamworkgraph.com/cli · https://developer.atlassian.com/cloud/twg-cli/
|
|
54
|
+
# It installs into ~/.local/bin and authenticates in the browser, so it is offered rather
|
|
55
|
+
# than assumed: it is one OAuth session per person, not a machine-wide tool.
|
|
56
|
+
# Its last step is a consent prompt that needs a controlling terminal — so this works
|
|
57
|
+
# when a person runs the installer and fails under -y in a pipe, where it says to pass
|
|
58
|
+
# --agree. Recording that consent is a person's to give, so it is not passed here.
|
|
59
|
+
if [ -z "${TWG_BIN}" ] && confirm "Install twg, the Teamwork Graph CLI (Jira from the terminal)?" ; then
|
|
60
|
+
# NOT `curl … | bash`, which is what Atlassian documents and which installs NOTHING
|
|
61
|
+
# here. Its last step records consent, that prompt needs a controlling terminal, and
|
|
62
|
+
# on failure the installer DELETES the binary it just downloaded:
|
|
63
|
+
#
|
|
64
|
+
# Interactive auth prompting is unavailable … without a controlling terminal.
|
|
65
|
+
# Pass --agree or --yes to record consent without an interactive prompt.
|
|
66
|
+
# Command failed: ~/.local/bin/twg consent --source direct-public-installer
|
|
67
|
+
#
|
|
68
|
+
# `--yes` alone does not help: the script forwards it to consent only when --yes,
|
|
69
|
+
# --skip-login AND --skip-skills are all set. And passing --agree from here would
|
|
70
|
+
# record a person's agreement to Atlassian's terms on their behalf, which is not
|
|
71
|
+
# this script's to give — so the release binary is fetched directly and consent is
|
|
72
|
+
# left to the first `twg login`, where a person answers it.
|
|
73
|
+
#
|
|
74
|
+
# The download is CHECKSUM-VERIFIED against the SHA256SUMS file published beside it.
|
|
75
|
+
# Fetching a 100MB binary over https and running it unverified would be worse than
|
|
76
|
+
# the pipe this replaces.
|
|
77
|
+
twg_arch='x64'; [ "$(uname -m)" == 'aarch64' ] && twg_arch='arm64' ;
|
|
78
|
+
twg_ver="$(curl -fsSL --retry 2 https://teamwork-graph.atlassian.com/cli/install 2>/dev/null \
|
|
79
|
+
| sed -n 's/^DEFAULT_VERSION="\([^"]*\)".*/\1/p' | head -1)" ;
|
|
80
|
+
if [ -z "${twg_ver}" ] ; then
|
|
81
|
+
echo " ✗ could not read the current twg version from teamwork-graph.atlassian.com — skipped." ;
|
|
82
|
+
else
|
|
83
|
+
twg_asset="twg-linux-${twg_arch}-v${twg_ver}" ;
|
|
84
|
+
twg_tmp="$(mktemp -d)" ;
|
|
85
|
+
if curl -fsSL --retry 2 -o "${twg_tmp}/twg" "https://teamwork-graph.atlassian.com/cli/${twg_asset}" \
|
|
86
|
+
&& curl -fsSL --retry 2 -o "${twg_tmp}/sums" "https://teamwork-graph.atlassian.com/cli/SHA256SUMS-v${twg_ver}" ; then
|
|
87
|
+
twg_want="$(awk -v a="${twg_asset}" '$2 == a { print $1 }' "${twg_tmp}/sums")" ;
|
|
88
|
+
twg_got="$(sha256sum "${twg_tmp}/twg" | awk '{print $1}')" ;
|
|
89
|
+
if [ -n "${twg_want}" ] && [ "${twg_want}" == "${twg_got}" ] ; then
|
|
90
|
+
mkdir -p "${HOME}/.local/bin" ;
|
|
91
|
+
install -m 0755 "${twg_tmp}/twg" "${HOME}/.local/bin/twg" ;
|
|
92
|
+
TWG_BIN="${HOME}/.local/bin/twg" ;
|
|
93
|
+
echo " ✓ twg ${twg_ver} installed to ~/.local/bin/twg (sha256 verified)" ;
|
|
94
|
+
# The mount is disabled while twg is absent, so it has to be turned back on now
|
|
95
|
+
# — Docker recreates the placeholder on every start otherwise. See host-mounts.sh.
|
|
96
|
+
true ;
|
|
97
|
+
echo "" ;
|
|
98
|
+
echo " One step is yours, because it needs a browser:" ;
|
|
99
|
+
echo " ~/.local/bin/twg login --force" ;
|
|
100
|
+
echo " It prints a URL and a code (OAuth DEVICE authorization), so the browser does" ;
|
|
101
|
+
echo " not have to be on this machine — open the URL anywhere, enter the code, and" ;
|
|
102
|
+
echo " the CLI finishes on its own. Check it with: ~/.local/bin/twg jira space list --limit 1" ;
|
|
103
|
+
echo " The dashboard shares ~/.config/twg, so it sees the session with no restart." ;
|
|
104
|
+
case ":${PATH}:" in
|
|
105
|
+
*":${HOME}/.local/bin:"*) ;;
|
|
106
|
+
*) echo " ~/.local/bin is not on your PATH — add it, or call twg by its full path." ;;
|
|
107
|
+
esac
|
|
108
|
+
else
|
|
109
|
+
echo " ✗ twg download did not match the published sha256 — NOT installed." ;
|
|
110
|
+
echo " expected ${twg_want:-<no entry for ${twg_asset}>}" ;
|
|
111
|
+
echo " got ${twg_got}" ;
|
|
112
|
+
fi
|
|
113
|
+
else
|
|
114
|
+
echo " ✗ could not download ${twg_asset} — check network access." ;
|
|
115
|
+
fi
|
|
116
|
+
rm -rf "${twg_tmp}" ;
|
|
117
|
+
fi
|
|
118
|
+
fi
|
|
119
|
+
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
argparse "$@" <<ARGEOF || exit 1
|
|
4
|
+
parser.add_argument('PROJECT_NAME',
|
|
5
|
+
help='The project directory in this workspace.')
|
|
6
|
+
parser.add_argument('-l', '--link',
|
|
7
|
+
default="_none_",
|
|
8
|
+
help='Link the project to an Upsun project ID (upsun project:set-remote). Find IDs with: upsun projects')
|
|
9
|
+
parser.add_argument('-i', '--init',
|
|
10
|
+
action='store_true',
|
|
11
|
+
default=False,
|
|
12
|
+
help='Scaffold .upsun/config.yaml with upsun project:init (interactive).')
|
|
13
|
+
parser.add_argument('-p', '--push',
|
|
14
|
+
action='store_true',
|
|
15
|
+
default=False,
|
|
16
|
+
help='Deploy the current branch with upsun push. Outward-facing: upsun asks for confirmation.')
|
|
17
|
+
parser.add_argument('-e', '--environment',
|
|
18
|
+
default="_none_",
|
|
19
|
+
help='Environment for THIS run: the --push target, and the source --pull-db/--pull-files take data from. Default: the one remembered by --select.')
|
|
20
|
+
parser.add_argument('-E', '--environments',
|
|
21
|
+
action='store_true',
|
|
22
|
+
default=False,
|
|
23
|
+
help='List the environments (branches) of the linked Upsun project.')
|
|
24
|
+
parser.add_argument('-s', '--select',
|
|
25
|
+
default="_none_",
|
|
26
|
+
help='Remember which environment this project works against, in .upsun/local/ (git-excluded). The pulls then need no flag. Checked against the real environment list.')
|
|
27
|
+
parser.add_argument('-D', '--pull-db',
|
|
28
|
+
action='store_true',
|
|
29
|
+
default=False,
|
|
30
|
+
help='Dump the selected environment\'s database and import it into DDEV. REPLACES the local database — the current one is exported to backups/ first. Client data: it lands on this machine.')
|
|
31
|
+
parser.add_argument('-F', '--pull-files',
|
|
32
|
+
action='store_true',
|
|
33
|
+
default=False,
|
|
34
|
+
help='Download the selected environment\'s public files mount into the local docroot (adds and overwrites, never deletes). Client data.')
|
|
35
|
+
parser.add_argument('-g', '--logs',
|
|
36
|
+
action='store_true',
|
|
37
|
+
default=False,
|
|
38
|
+
help='Show the most recent deploy log.')
|
|
39
|
+
parser.add_argument('-a', '--activities',
|
|
40
|
+
action='store_true',
|
|
41
|
+
default=False,
|
|
42
|
+
help='List the last 10 Upsun activities.')
|
|
43
|
+
parser.add_argument('-B', '--no-safety-backup',
|
|
44
|
+
action='store_true',
|
|
45
|
+
default=False,
|
|
46
|
+
help='Skip exporting the local database before --pull-db replaces it. The export exists because a pull that ate an afternoon of work is a bad pull, so skipping it is a choice, not a default.')
|
|
47
|
+
parser.add_argument('-f', '--force',
|
|
48
|
+
action='store_true',
|
|
49
|
+
default=False,
|
|
50
|
+
help='Allow --link to relink a project that already points at a different Upsun ID.')
|
|
51
|
+
ARGEOF
|