@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.
Files changed (634) hide show
  1. package/LICENSE +339 -0
  2. package/README.md +28 -364
  3. package/core/bin/workspace.js +135 -0
  4. package/core/config/settings.labels.yml +514 -0
  5. package/core/config/settings.template.yml +292 -0
  6. package/core/config/style.yml +53 -0
  7. package/core/config/theme.default.yml +161 -0
  8. package/core/config/theme.dispatch.yml +166 -0
  9. package/core/config/theme.drupal.yml +175 -0
  10. package/core/config/theme.ember.yml +160 -0
  11. package/core/config/theme.github.yml +172 -0
  12. package/core/config/theme.gitlab.yml +168 -0
  13. package/core/config/theme.press.yml +161 -0
  14. package/core/config/theme.slate.yml +160 -0
  15. package/core/config/theme.upsun.yml +174 -0
  16. package/core/docs/architecture.svg +65 -0
  17. package/core/docs/getting-started.md +51 -0
  18. package/core/docs/plugins.md +27 -0
  19. package/core/docs/releases.md +23 -0
  20. package/core/docs/testing.md +15 -0
  21. package/core/docs/workspaces.md +25 -0
  22. package/core/scripts/args/arg-ai-items.sh +65 -0
  23. package/core/scripts/args/arg-autostart.sh +31 -0
  24. package/core/scripts/args/arg-avatar-agent.sh +28 -0
  25. package/core/scripts/args/arg-avatar-catalog.sh +12 -0
  26. package/core/scripts/args/arg-avatar-character.sh +68 -0
  27. package/core/scripts/args/arg-avatar-voice.sh +24 -0
  28. package/core/scripts/args/arg-avatar.sh +22 -0
  29. package/core/scripts/args/arg-checks.sh +36 -0
  30. package/core/scripts/args/arg-clone.sh +42 -0
  31. package/core/scripts/args/arg-commands.sh +51 -0
  32. package/core/scripts/args/arg-cucumber.sh +34 -0
  33. package/core/scripts/args/arg-distribution.sh +30 -0
  34. package/core/scripts/args/arg-drupal.sh +39 -0
  35. package/core/scripts/args/arg-drupal_cms.sh +18 -0
  36. package/core/scripts/args/arg-gitlab-ci-local.sh +25 -0
  37. package/core/scripts/args/arg-graphify.sh +36 -0
  38. package/core/scripts/args/arg-graphs.sh +12 -0
  39. package/core/scripts/args/arg-hostnames.sh +23 -0
  40. package/core/scripts/args/arg-milvus-backup.sh +8 -0
  41. package/core/scripts/args/arg-milvus-mcp.sh +12 -0
  42. package/core/scripts/args/arg-milvus.sh +32 -0
  43. package/core/scripts/args/arg-obsidian.sh +17 -0
  44. package/core/scripts/args/arg-odoo.sh +18 -0
  45. package/core/scripts/args/arg-penpot-backup.sh +8 -0
  46. package/core/scripts/args/arg-penpot-mcp.sh +15 -0
  47. package/core/scripts/args/arg-penpot-token.sh +17 -0
  48. package/core/scripts/args/arg-penpot.sh +44 -0
  49. package/core/scripts/args/arg-rag.sh +21 -0
  50. package/core/scripts/args/arg-ragify.sh +35 -0
  51. package/core/scripts/args/arg-reclaim.sh +18 -0
  52. package/core/scripts/args/arg-release.sh +21 -0
  53. package/core/scripts/args/arg-screen.sh +28 -0
  54. package/core/scripts/args/arg-site-template.sh +13 -0
  55. package/core/scripts/args/arg-spdd.sh +16 -0
  56. package/core/scripts/args/arg-specs.sh +22 -0
  57. package/core/scripts/args/arg-storage.sh +36 -0
  58. package/core/scripts/args/arg-testing.sh +36 -0
  59. package/core/scripts/args/arg-webship.sh +34 -0
  60. package/core/scripts/args/arg-webships-project.sh +22 -0
  61. package/core/scripts/args/arg-webships-site-template.sh +17 -0
  62. package/core/scripts/args/arg-webships.sh +34 -0
  63. package/core/scripts/args/arg-website-site-template.sh +17 -0
  64. package/core/scripts/args/arg-website.sh +25 -0
  65. package/core/scripts/args/arg-wordpress.sh +18 -0
  66. package/core/scripts/autostart-run.sh +162 -0
  67. package/core/scripts/autostart-stop.sh +30 -0
  68. package/core/scripts/autostart-watch.sh +43 -0
  69. package/core/scripts/autostart.sh +26 -0
  70. package/core/scripts/bootstrap-functions.sh +117 -0
  71. package/core/scripts/bootstrap-libraries.sh +10 -0
  72. package/core/scripts/bootstrap.sh +71 -0
  73. package/core/scripts/ddev-shim/ddev +122 -0
  74. package/core/scripts/docker-gid.sh +119 -0
  75. package/core/scripts/functions/arg-site-template.sh +28 -0
  76. package/core/scripts/functions/fun-ai-items-lock.sh +175 -0
  77. package/core/scripts/functions/fun-ai-items-sources.sh +298 -0
  78. package/core/scripts/functions/fun-ai-items.sh +621 -0
  79. package/core/scripts/functions/fun-ai-providers.sh +72 -0
  80. package/core/scripts/functions/fun-autostart.sh +429 -0
  81. package/core/scripts/functions/fun-avatar.sh +441 -0
  82. package/core/scripts/functions/fun-backup-item.sh +50 -0
  83. package/core/scripts/functions/fun-build-distribution.sh +226 -0
  84. package/core/scripts/functions/fun-build-site-template.sh +104 -0
  85. package/core/scripts/functions/fun-build-webships.sh +161 -0
  86. package/core/scripts/functions/fun-build-website.sh +199 -0
  87. package/core/scripts/functions/fun-checks.sh +243 -0
  88. package/core/scripts/functions/fun-clone-project.sh +289 -0
  89. package/core/scripts/functions/fun-commands.sh +521 -0
  90. package/core/scripts/functions/fun-database.sh +63 -0
  91. package/core/scripts/functions/fun-ddev-hostnames.sh +213 -0
  92. package/core/scripts/functions/fun-ddev-nodejs.sh +15 -0
  93. package/core/scripts/functions/fun-ddev-telemetry.sh +15 -0
  94. package/core/scripts/functions/fun-default-settings.sh +28 -0
  95. package/core/scripts/functions/fun-distribution-cucumber.sh +45 -0
  96. package/core/scripts/functions/fun-distribution-drupal.sh +25 -0
  97. package/core/scripts/functions/fun-distribution-drupal_cms.sh +35 -0
  98. package/core/scripts/functions/fun-distribution-webship.sh +28 -0
  99. package/core/scripts/functions/fun-distribution-webships.sh +33 -0
  100. package/core/scripts/functions/fun-distribution-website.sh +19 -0
  101. package/core/scripts/functions/fun-drush.sh +48 -0
  102. package/core/scripts/functions/fun-elevenlabs.sh +179 -0
  103. package/core/scripts/functions/fun-files.sh +19 -0
  104. package/core/scripts/functions/fun-git-worktrees.sh +142 -0
  105. package/core/scripts/functions/fun-gitlab-ci.sh +156 -0
  106. package/core/scripts/functions/fun-hostnames-cli.sh +88 -0
  107. package/core/scripts/functions/fun-milvus.sh +584 -0
  108. package/core/scripts/functions/fun-odoo.sh +121 -0
  109. package/core/scripts/functions/fun-penpot.sh +724 -0
  110. package/core/scripts/functions/fun-project-name.sh +20 -0
  111. package/core/scripts/functions/fun-rag.sh +253 -0
  112. package/core/scripts/functions/fun-ragify.sh +165 -0
  113. package/core/scripts/functions/fun-reclaim.sh +297 -0
  114. package/core/scripts/functions/fun-release.sh +159 -0
  115. package/core/scripts/functions/fun-remove-project.sh +319 -0
  116. package/core/scripts/functions/fun-screen.sh +96 -0
  117. package/core/scripts/functions/fun-spdd.sh +676 -0
  118. package/core/scripts/functions/fun-specs-share.sh +237 -0
  119. package/core/scripts/functions/fun-storage.sh +347 -0
  120. package/core/scripts/functions/fun-testing.sh +329 -0
  121. package/core/scripts/functions/fun-users.sh +87 -0
  122. package/core/scripts/functions/fun-version.sh +72 -0
  123. package/core/scripts/functions/fun-video-edit.sh +48 -0
  124. package/core/scripts/functions/fun-voice.sh +45 -0
  125. package/core/scripts/functions/fun-wordpress.sh +59 -0
  126. package/core/scripts/host-mounts.sh +161 -0
  127. package/core/scripts/install.sh +602 -0
  128. package/core/scripts/libs/__pycache__/video_edit.cpython-312.pyc +0 -0
  129. package/core/scripts/libs/argparse.sh +93 -0
  130. package/core/scripts/libs/bash-yaml.sh +52 -0
  131. package/core/scripts/libs/checks_plan.js +312 -0
  132. package/core/scripts/libs/elevenlabs.py +627 -0
  133. package/core/scripts/libs/progress-bar.sh +19 -0
  134. package/core/scripts/libs/rag_index.py +446 -0
  135. package/core/scripts/libs/video_edit.py +636 -0
  136. package/core/scripts/libs/voice.py +430 -0
  137. package/core/scripts/plugins/jira/host-install.sh +119 -0
  138. package/core/scripts/plugins/jira/host-mounts.txt +2 -0
  139. package/core/scripts/plugins/upsun/args/arg-upsun.sh +51 -0
  140. package/core/scripts/plugins/upsun/functions/fun-upsun.sh +304 -0
  141. package/core/scripts/plugins/upsun/host-install.sh +99 -0
  142. package/core/scripts/plugins/upsun/host-mounts.txt +1 -0
  143. package/core/scripts/plugins/upsun/upsun-ai.sh +133 -0
  144. package/core/scripts/plugins.sh +152 -0
  145. package/core/scripts/release.sh +69 -0
  146. package/core/scripts/storage.sh +39 -0
  147. package/core/scripts/sync-tooling.sh +246 -0
  148. package/core/scripts/tests/cmd-smoke-test.sh +109 -0
  149. package/core/scripts/trust-https.sh +257 -0
  150. package/core/workspace-app/.ddev/commands/web/test-dashboard +49 -0
  151. package/core/workspace-app/.ddev/config.yaml +400 -0
  152. package/core/workspace-app/.ddev/docker-compose.dood.yaml +45 -0
  153. package/core/workspace-app/.ddev/docker-compose.plugin-jira-twg.yaml +26 -0
  154. package/core/workspace-app/.ddev/docker-compose.plugin-upsun-upsun-cli.yaml +8 -0
  155. package/core/workspace-app/.ddev/docker-compose.screens.yaml +15 -0
  156. package/core/workspace-app/.ddev/nginx_full/nginx-site.conf +114 -0
  157. package/core/workspace-app/.ddev/web-build/Dockerfile.workspace-app +203 -0
  158. package/core/workspace-app/.ddev/web-build/ddev-shim-ddev +122 -0
  159. package/{PLUGINS.md → core/workspace-app/PLUGINS.md} +1 -1
  160. package/core/workspace-app/README.md +380 -0
  161. package/{actions → core/workspace-app/actions}/ddev.js +0 -1
  162. package/{actions → core/workspace-app/actions}/rag.js +0 -1
  163. package/{actions → core/workspace-app/actions}/testing.js +0 -1
  164. package/{assistant.js → core/workspace-app/assistant.js} +1 -10
  165. package/{git-info.js → core/workspace-app/git-info.js} +17 -1
  166. package/core/workspace-app/package.json +29 -0
  167. package/core/workspace-app/plugin-catalog.js +50 -0
  168. package/{plugin-loader.js → core/workspace-app/plugin-loader.js} +19 -1
  169. package/{plugins-page.js → core/workspace-app/plugins-page.js} +28 -2
  170. package/{row-layout.js → core/workspace-app/row-layout.js} +0 -1
  171. package/{server.js → core/workspace-app/server.js} +26 -225
  172. package/core/workspace-app/tests/features/actions-rail.feature +44 -0
  173. package/core/workspace-app/tests/features/ai-items.feature +23 -0
  174. package/core/workspace-app/tests/features/assistant-speech.feature +63 -0
  175. package/core/workspace-app/tests/features/checks.feature +39 -0
  176. package/core/workspace-app/tests/features/clone-project.feature +30 -0
  177. package/core/workspace-app/tests/features/command-authoring.feature +58 -0
  178. package/core/workspace-app/tests/features/dashboard.feature +57 -0
  179. package/core/workspace-app/tests/features/menus.feature +29 -0
  180. package/core/workspace-app/tests/features/plugins/jira/jira.feature +47 -0
  181. package/core/workspace-app/tests/features/plugins.feature +20 -0
  182. package/core/workspace-app/tests/features/project-actions.feature +40 -0
  183. package/core/workspace-app/tests/features/screens.feature +29 -0
  184. package/core/workspace-app/tests/features/settings.feature +46 -0
  185. package/core/workspace-app/tests/features/test-reports.feature +32 -0
  186. package/core/workspace-app/tests/features/theme.feature +40 -0
  187. package/core/workspace-app/tests/step-definitions/actions-post.steps.js +25 -0
  188. package/core/workspace-app/tests/step-definitions/assistant-speech.steps.js +152 -0
  189. package/core/workspace-app/tests/step-definitions/preconditions.steps.js +100 -0
  190. package/core/workspace-app/tests/unit/_isolated-config.js +31 -0
  191. package/core/workspace-app/tests/unit/accessibility.test.js +124 -0
  192. package/core/workspace-app/tests/unit/actions-rail-ux.test.js +56 -0
  193. package/core/workspace-app/tests/unit/actions-rail.test.js +51 -0
  194. package/core/workspace-app/tests/unit/ai-inventory.test.js +64 -0
  195. package/core/workspace-app/tests/unit/app-docs.test.js +64 -0
  196. package/core/workspace-app/tests/unit/assistant-avatar.test.js +110 -0
  197. package/core/workspace-app/tests/unit/assistant-cast.test.js +105 -0
  198. package/core/workspace-app/tests/unit/assistant-send-animation.test.js +15 -0
  199. package/core/workspace-app/tests/unit/assistant-suggestions.test.js +112 -0
  200. package/core/workspace-app/tests/unit/assistant-switches.test.js +53 -0
  201. package/core/workspace-app/tests/unit/assistant-views.test.js +146 -0
  202. package/core/workspace-app/tests/unit/assistant-working-indicator.test.js +49 -0
  203. package/core/workspace-app/tests/unit/assistant.test.js +61 -0
  204. package/core/workspace-app/tests/unit/audio-player.test.js +147 -0
  205. package/core/workspace-app/tests/unit/audio-voice.test.js +634 -0
  206. package/core/workspace-app/tests/unit/auth-google.test.js +180 -0
  207. package/core/workspace-app/tests/unit/autostart-watch.test.js +76 -0
  208. package/core/workspace-app/tests/unit/browsers.test.js +76 -0
  209. package/core/workspace-app/tests/unit/build-distribution.test.js +125 -0
  210. package/core/workspace-app/tests/unit/build-order.test.js +153 -0
  211. package/core/workspace-app/tests/unit/builder-args.test.js +113 -0
  212. package/core/workspace-app/tests/unit/builders.test.js +158 -0
  213. package/core/workspace-app/tests/unit/card-state-toggle.test.js +29 -0
  214. package/core/workspace-app/tests/unit/changelog.test.js +177 -0
  215. package/core/workspace-app/tests/unit/chat-thread.test.js +92 -0
  216. package/core/workspace-app/tests/unit/checks-plan.test.js +205 -0
  217. package/core/workspace-app/tests/unit/claude-args.test.js +182 -0
  218. package/core/workspace-app/tests/unit/claude-code-here.test.js +15 -0
  219. package/core/workspace-app/tests/unit/claude-resume.test.js +147 -0
  220. package/core/workspace-app/tests/unit/claude-screen-options.test.js +84 -0
  221. package/core/workspace-app/tests/unit/claude-session.test.js +41 -0
  222. package/core/workspace-app/tests/unit/command-flags.test.js +45 -0
  223. package/core/workspace-app/tests/unit/command-palette.test.js +105 -0
  224. package/core/workspace-app/tests/unit/commands-center.test.js +338 -0
  225. package/core/workspace-app/tests/unit/composer-menu.test.js +187 -0
  226. package/core/workspace-app/tests/unit/config-yaml.test.js +116 -0
  227. package/core/workspace-app/tests/unit/confirm-dialog.test.js +69 -0
  228. package/core/workspace-app/tests/unit/create-card.test.js +85 -0
  229. package/core/workspace-app/tests/unit/ddev-addons.test.js +144 -0
  230. package/core/workspace-app/tests/unit/ddev-auth-ssh.test.js +40 -0
  231. package/core/workspace-app/tests/unit/ddev-custom-commands.test.js +56 -0
  232. package/core/workspace-app/tests/unit/ddev-diagnose.test.js +57 -0
  233. package/core/workspace-app/tests/unit/ddev-domains.test.js +153 -0
  234. package/core/workspace-app/tests/unit/ddev-hostnames.test.js +143 -0
  235. package/core/workspace-app/tests/unit/ddev-import.test.js +82 -0
  236. package/core/workspace-app/tests/unit/ddev-mail.test.js +135 -0
  237. package/core/workspace-app/tests/unit/ddev-menu-and-create.test.js +28 -0
  238. package/core/workspace-app/tests/unit/ddev-mutagen.test.js +37 -0
  239. package/core/workspace-app/tests/unit/ddev-options.test.js +51 -0
  240. package/core/workspace-app/tests/unit/ddev-share.test.js +40 -0
  241. package/core/workspace-app/tests/unit/ddev-shim.test.js +57 -0
  242. package/core/workspace-app/tests/unit/ddev-snapshots.test.js +142 -0
  243. package/core/workspace-app/tests/unit/ddev-start-daemons.test.js +62 -0
  244. package/core/workspace-app/tests/unit/ddev-xdebug.test.js +41 -0
  245. package/core/workspace-app/tests/unit/deep-chat-origin.test.js +38 -0
  246. package/core/workspace-app/tests/unit/density.test.js +52 -0
  247. package/core/workspace-app/tests/unit/elevenlabs.test.js +453 -0
  248. package/core/workspace-app/tests/unit/event-log.test.js +102 -0
  249. package/core/workspace-app/tests/unit/favicon.test.js +68 -0
  250. package/core/workspace-app/tests/unit/files-browser.test.js +224 -0
  251. package/core/workspace-app/tests/unit/git-info.test.js +26 -0
  252. package/core/workspace-app/tests/unit/grid-and-list.test.js +103 -0
  253. package/core/workspace-app/tests/unit/host-tool-wrapper.test.js +18 -0
  254. package/core/workspace-app/tests/unit/html-patterns.test.js +51 -0
  255. package/core/workspace-app/tests/unit/ide-infra.test.js +28 -0
  256. package/core/workspace-app/tests/unit/images-ai.test.js +201 -0
  257. package/core/workspace-app/tests/unit/images-create.test.js +59 -0
  258. package/core/workspace-app/tests/unit/images-providers.test.js +248 -0
  259. package/core/workspace-app/tests/unit/images-svg.test.js +213 -0
  260. package/core/workspace-app/tests/unit/images-workspace.test.js +144 -0
  261. package/core/workspace-app/tests/unit/jobs-fragment.test.js +66 -0
  262. package/core/workspace-app/tests/unit/media-ux.test.js +164 -0
  263. package/core/workspace-app/tests/unit/modal-fit.test.js +87 -0
  264. package/core/workspace-app/tests/unit/modules-load.test.js +23 -0
  265. package/core/workspace-app/tests/unit/narration.test.js +186 -0
  266. package/core/workspace-app/tests/unit/navigation-editor.test.js +260 -0
  267. package/core/workspace-app/tests/unit/navigation.test.js +254 -0
  268. package/core/workspace-app/tests/unit/new-folder.test.js +121 -0
  269. package/core/workspace-app/tests/unit/new-screen-form.test.js +149 -0
  270. package/core/workspace-app/tests/unit/node-menu.test.js +161 -0
  271. package/core/workspace-app/tests/unit/notes.test.js +128 -0
  272. package/core/workspace-app/tests/unit/page-structure.test.js +40 -0
  273. package/core/workspace-app/tests/unit/page-width.test.js +59 -0
  274. package/core/workspace-app/tests/unit/php-version.test.js +90 -0
  275. package/core/workspace-app/tests/unit/plan-card.test.js +44 -0
  276. package/core/workspace-app/tests/unit/plugin-catalog.test.js +31 -0
  277. package/core/workspace-app/tests/unit/plugin-system.test.js +251 -0
  278. package/core/workspace-app/tests/unit/plugins-page.test.js +28 -0
  279. package/core/workspace-app/tests/unit/plugins.test.js +58 -0
  280. package/core/workspace-app/tests/unit/project-activity-filter.test.js +180 -0
  281. package/core/workspace-app/tests/unit/project-make.test.js +143 -0
  282. package/core/workspace-app/tests/unit/project-name.test.js +202 -0
  283. package/core/workspace-app/tests/unit/reader.test.js +62 -0
  284. package/core/workspace-app/tests/unit/release.test.js +92 -0
  285. package/core/workspace-app/tests/unit/remove-confirm.test.js +37 -0
  286. package/core/workspace-app/tests/unit/report-pdf.test.js +171 -0
  287. package/core/workspace-app/tests/unit/round-11.test.js +31 -0
  288. package/core/workspace-app/tests/unit/round-12.test.js +37 -0
  289. package/core/workspace-app/tests/unit/row-layout.test.js +585 -0
  290. package/core/workspace-app/tests/unit/row-menus-fold.test.js +29 -0
  291. package/core/workspace-app/tests/unit/rows-add-modal.test.js +68 -0
  292. package/core/workspace-app/tests/unit/screen-runner-queue.test.js +41 -0
  293. package/core/workspace-app/tests/unit/screen-window.test.js +116 -0
  294. package/core/workspace-app/tests/unit/screens-beside-running.test.js +19 -0
  295. package/core/workspace-app/tests/unit/screens-columns.test.js +79 -0
  296. package/core/workspace-app/tests/unit/screens-menu.test.js +84 -0
  297. package/core/workspace-app/tests/unit/screens-modal-overlay.test.js +14 -0
  298. package/core/workspace-app/tests/unit/screens-waiting.test.js +129 -0
  299. package/core/workspace-app/tests/unit/screens.test.js +74 -0
  300. package/core/workspace-app/tests/unit/second-storage.test.js +218 -0
  301. package/core/workspace-app/tests/unit/session-name.test.js +64 -0
  302. package/core/workspace-app/tests/unit/settings-file.test.js +173 -0
  303. package/core/workspace-app/tests/unit/settings-nested-list.test.js +54 -0
  304. package/core/workspace-app/tests/unit/settings-raw.test.js +177 -0
  305. package/core/workspace-app/tests/unit/shared-prefs.test.js +49 -0
  306. package/core/workspace-app/tests/unit/speech.test.js +290 -0
  307. package/core/workspace-app/tests/unit/static-caching.test.js +39 -0
  308. package/core/workspace-app/tests/unit/status-language.test.js +47 -0
  309. package/core/workspace-app/tests/unit/tab-modal.test.js +69 -0
  310. package/core/workspace-app/tests/unit/tab-title.test.js +37 -0
  311. package/core/workspace-app/tests/unit/template-literals.test.js +52 -0
  312. package/core/workspace-app/tests/unit/terminal-scrollback.test.js +61 -0
  313. package/core/workspace-app/tests/unit/terminal-size.test.js +103 -0
  314. package/core/workspace-app/tests/unit/terminal-style.test.js +84 -0
  315. package/core/workspace-app/tests/unit/terminal-windows.test.js +154 -0
  316. package/core/workspace-app/tests/unit/themes.test.js +453 -0
  317. package/core/workspace-app/tests/unit/tool-labels.test.js +34 -0
  318. package/core/workspace-app/tests/unit/topbar-icons.test.js +47 -0
  319. package/core/workspace-app/tests/unit/update-docker-gid.test.js +51 -0
  320. package/core/workspace-app/tests/unit/user-guide.test.js +60 -0
  321. package/core/workspace-app/tests/unit/version-check.test.js +98 -0
  322. package/core/workspace-app/tests/unit/version-link.test.js +50 -0
  323. package/core/workspace-app/tests/unit/video-borrow.test.js +198 -0
  324. package/core/workspace-app/tests/unit/video-edit-ask.test.js +93 -0
  325. package/core/workspace-app/tests/unit/video-edit-short.test.js +42 -0
  326. package/core/workspace-app/tests/unit/video-edit.test.js +417 -0
  327. package/core/workspace-app/tests/unit/video-transcript.test.js +237 -0
  328. package/core/workspace-app/tests/unit/waiting.test.js +48 -0
  329. package/core/workspace-app/tests/unit/wordpress-odoo-builders.test.js +38 -0
  330. package/core/workspace-app/tests/unit/workspace-bin.test.js +48 -0
  331. package/core/workspace-app/tests/unit/workspace-card-tools.test.js +30 -0
  332. package/core/workspace-app/tests/unit/workspace-delete.test.js +77 -0
  333. package/core/workspace-app/tests/unit/workspace-types.test.js +68 -0
  334. package/core/workspace-app/tests/unit/worktree-clone.test.js +250 -0
  335. package/{workspace-types.js → core/workspace-app/workspace-types.js} +2 -2
  336. package/{workspaces.js → core/workspace-app/workspaces.js} +1 -5
  337. package/package.json +38 -36
  338. package/actions/graphs.js +0 -107
  339. package/actions/obsidian.js +0 -59
  340. package/graphs.js +0 -104
  341. package/obsidian.js +0 -405
  342. /package/{WORKSPACES.md → core/workspace-app/WORKSPACES.md} +0 -0
  343. /package/{actions → core/workspace-app/actions}/ai-items.js +0 -0
  344. /package/{actions → core/workspace-app/actions}/audio.js +0 -0
  345. /package/{actions → core/workspace-app/actions}/build-order.js +0 -0
  346. /package/{actions → core/workspace-app/actions}/checks.js +0 -0
  347. /package/{actions → core/workspace-app/actions}/claude-session.js +0 -0
  348. /package/{actions → core/workspace-app/actions}/commands-center.js +0 -0
  349. /package/{actions → core/workspace-app/actions}/composer.js +0 -0
  350. /package/{actions → core/workspace-app/actions}/ddev-addons.js +0 -0
  351. /package/{actions → core/workspace-app/actions}/ddev-mail.js +0 -0
  352. /package/{actions → core/workspace-app/actions}/folders.js +0 -0
  353. /package/{actions → core/workspace-app/actions}/images-svg.js +0 -0
  354. /package/{actions → core/workspace-app/actions}/images.js +0 -0
  355. /package/{actions → core/workspace-app/actions}/navigation.js +0 -0
  356. /package/{actions → core/workspace-app/actions}/node.js +0 -0
  357. /package/{actions → core/workspace-app/actions}/notes.js +0 -0
  358. /package/{actions → core/workspace-app/actions}/plugins.js +0 -0
  359. /package/{actions → core/workspace-app/actions}/project-make.js +0 -0
  360. /package/{actions → core/workspace-app/actions}/row-actions.js +0 -0
  361. /package/{actions → core/workspace-app/actions}/screens.js +0 -0
  362. /package/{actions → core/workspace-app/actions}/video-edit.js +0 -0
  363. /package/{ai-inventory.js → core/workspace-app/ai-inventory.js} +0 -0
  364. /package/{ai-items.js → core/workspace-app/ai-items.js} +0 -0
  365. /package/{assistant-cast.js → core/workspace-app/assistant-cast.js} +0 -0
  366. /package/{assistant-suggestions.js → core/workspace-app/assistant-suggestions.js} +0 -0
  367. /package/{audio-voice.js → core/workspace-app/audio-voice.js} +0 -0
  368. /package/{auth-google.js → core/workspace-app/auth-google.js} +0 -0
  369. /package/{build-ai.js → core/workspace-app/build-ai.js} +0 -0
  370. /package/{build-order-page.js → core/workspace-app/build-order-page.js} +0 -0
  371. /package/{build-order.js → core/workspace-app/build-order.js} +0 -0
  372. /package/{builder-args.js → core/workspace-app/builder-args.js} +0 -0
  373. /package/{changelog.js → core/workspace-app/changelog.js} +0 -0
  374. /package/{chat-threads.js → core/workspace-app/chat-threads.js} +0 -0
  375. /package/{checks.js → core/workspace-app/checks.js} +0 -0
  376. /package/{claude-args.js → core/workspace-app/claude-args.js} +0 -0
  377. /package/{claude-sessions.js → core/workspace-app/claude-sessions.js} +0 -0
  378. /package/{command-author.js → core/workspace-app/command-author.js} +0 -0
  379. /package/{command-block.js → core/workspace-app/command-block.js} +0 -0
  380. /package/{commands → core/workspace-app/commands}/defaults.yml +0 -0
  381. /package/{commands-page.js → core/workspace-app/commands-page.js} +0 -0
  382. /package/{commands.js → core/workspace-app/commands.js} +0 -0
  383. /package/{cucumber.js → core/workspace-app/cucumber.js} +0 -0
  384. /package/{ddev-addons-page.js → core/workspace-app/ddev-addons-page.js} +0 -0
  385. /package/{ddev-addons.js → core/workspace-app/ddev-addons.js} +0 -0
  386. /package/{ddev-custom-commands.js → core/workspace-app/ddev-custom-commands.js} +0 -0
  387. /package/{ddev-domains.js → core/workspace-app/ddev-domains.js} +0 -0
  388. /package/{ddev-import.js → core/workspace-app/ddev-import.js} +0 -0
  389. /package/{ddev-mail-page.js → core/workspace-app/ddev-mail-page.js} +0 -0
  390. /package/{ddev-mail.js → core/workspace-app/ddev-mail.js} +0 -0
  391. /package/{ddev-options.js → core/workspace-app/ddev-options.js} +0 -0
  392. /package/{ddev-snapshots.js → core/workspace-app/ddev-snapshots.js} +0 -0
  393. /package/{ddev.js → core/workspace-app/ddev.js} +0 -0
  394. /package/{docs → core/workspace-app/docs}/DEVELOPER-GUIDE.md +0 -0
  395. /package/{docs → core/workspace-app/docs}/README.md +0 -0
  396. /package/{docs → core/workspace-app/docs}/REFERENCE.md +0 -0
  397. /package/{docs → core/workspace-app/docs}/USER-GUIDE.md +0 -0
  398. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-board.png +0 -0
  399. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-coffee.png +0 -0
  400. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-colours.png +0 -0
  401. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-icons.png +0 -0
  402. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-moves.png +0 -0
  403. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-pick-cookieman.png +0 -0
  404. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-settings-voices.png +0 -0
  405. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-status.png +0 -0
  406. /package/{docs → core/workspace-app/docs}/screenshots/create-card/create-card.png +0 -0
  407. /package/{docs → core/workspace-app/docs}/screenshots/create-card/start-ai.png +0 -0
  408. /package/{docs → core/workspace-app/docs}/screenshots/guide-clone-address.png +0 -0
  409. /package/{docs → core/workspace-app/docs}/screenshots/guide-clone-worktree-off.png +0 -0
  410. /package/{docs → core/workspace-app/docs}/screenshots/guide-clone-worktree-on.png +0 -0
  411. /package/{docs → core/workspace-app/docs}/screenshots/guide-ddev-menu.png +0 -0
  412. /package/{docs → core/workspace-app/docs}/screenshots/guide-domains.png +0 -0
  413. /package/{docs → core/workspace-app/docs}/screenshots/guide-home.png +0 -0
  414. /package/{docs → core/workspace-app/docs}/screenshots/guide-ide-menu.png +0 -0
  415. /package/{docs → core/workspace-app/docs}/screenshots/guide-remove-refusal.png +0 -0
  416. /package/{docs → core/workspace-app/docs}/screenshots/guide-rows-worktree.png +0 -0
  417. /package/{docs → core/workspace-app/docs}/screenshots/guide-screens.png +0 -0
  418. /package/{docs → core/workspace-app/docs}/screenshots/guide-settings.png +0 -0
  419. /package/{docs → core/workspace-app/docs}/screenshots/guide-workspace-page.png +0 -0
  420. /package/{docs → core/workspace-app/docs}/screenshots/home/card-settings.png +0 -0
  421. /package/{docs → core/workspace-app/docs}/screenshots/home/drag-reorder.png +0 -0
  422. /package/{docs → core/workspace-app/docs}/screenshots/home/edit-card.png +0 -0
  423. /package/{docs → core/workspace-app/docs}/screenshots/images/claude-code-image.png +0 -0
  424. /package/{docs → core/workspace-app/docs}/screenshots/images/edit-with-ai.png +0 -0
  425. /package/{docs → core/workspace-app/docs}/screenshots/images/gallery.png +0 -0
  426. /package/{docs → core/workspace-app/docs}/screenshots/images/generate-description.png +0 -0
  427. /package/{docs → core/workspace-app/docs}/screenshots/images/generate-plan.png +0 -0
  428. /package/{docs → core/workspace-app/docs}/screenshots/images/providers.png +0 -0
  429. /package/{docs → core/workspace-app/docs}/screenshots/images/svg-form.png +0 -0
  430. /package/{docs → core/workspace-app/docs}/screenshots/images/svg-result.png +0 -0
  431. /package/{docs → core/workspace-app/docs}/screenshots/images/upload.png +0 -0
  432. /package/{docs → core/workspace-app/docs}/screenshots/new-folder/2-form.png +0 -0
  433. /package/{docs → core/workspace-app/docs}/screenshots/new-folder/8-ddev-options.png +0 -0
  434. /package/{docs → core/workspace-app/docs}/screenshots/new-folder/files-folder.png +0 -0
  435. /package/{docs → core/workspace-app/docs}/screenshots/projects/ddev-custom-commands.png +0 -0
  436. /package/{docs → core/workspace-app/docs}/screenshots/projects/state-chip-stop.png +0 -0
  437. /package/{docs → core/workspace-app/docs}/screenshots/readme/dashboard-dark.png +0 -0
  438. /package/{docs → core/workspace-app/docs}/screenshots/readme/dashboard-light.png +0 -0
  439. /package/{docs → core/workspace-app/docs}/screenshots/readme/dev-dark.png +0 -0
  440. /package/{docs → core/workspace-app/docs}/screenshots/readme/dev-light.png +0 -0
  441. /package/{docs → core/workspace-app/docs}/screenshots/readme/docs-dark.png +0 -0
  442. /package/{docs → core/workspace-app/docs}/screenshots/readme/docs-light.png +0 -0
  443. /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/ai.svg +0 -0
  444. /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/code.svg +0 -0
  445. /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/docs.svg +0 -0
  446. /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/services.svg +0 -0
  447. /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/sites.svg +0 -0
  448. /package/{docs → core/workspace-app/docs}/screenshots/readme/test-report-dark.png +0 -0
  449. /package/{docs → core/workspace-app/docs}/screenshots/readme/test-report-light.png +0 -0
  450. /package/{docs → core/workspace-app/docs}/screenshots/readme/test-scenarios.png +0 -0
  451. /package/{docs → core/workspace-app/docs}/screenshots/readme/test-steps.png +0 -0
  452. /package/{docs → core/workspace-app/docs}/screenshots/readme/videos-dark.png +0 -0
  453. /package/{docs → core/workspace-app/docs}/screenshots/readme/videos-light.png +0 -0
  454. /package/{docs → core/workspace-app/docs}/screenshots/screens-menu.png +0 -0
  455. /package/{docs → core/workspace-app/docs}/screenshots/screens-new.png +0 -0
  456. /package/{docs → core/workspace-app/docs}/screenshots/settings/auth-google-lists.png +0 -0
  457. /package/{docs → core/workspace-app/docs}/screenshots/settings/delete-1-row.png +0 -0
  458. /package/{docs → core/workspace-app/docs}/screenshots/settings/delete-2-confirm.png +0 -0
  459. /package/{docs → core/workspace-app/docs}/screenshots/settings/delete-3-type-name.png +0 -0
  460. /package/{docs → core/workspace-app/docs}/screenshots/settings/delete-4-type-yes.png +0 -0
  461. /package/{event-log.js → core/workspace-app/event-log.js} +0 -0
  462. /package/{files-browser.js → core/workspace-app/files-browser.js} +0 -0
  463. /package/{folders.js → core/workspace-app/folders.js} +0 -0
  464. /package/{format.js → core/workspace-app/format.js} +0 -0
  465. /package/{github-auth.js → core/workspace-app/github-auth.js} +0 -0
  466. /package/{html.js → core/workspace-app/html.js} +0 -0
  467. /package/{icons.js → core/workspace-app/icons.js} +0 -0
  468. /package/{images-ai.js → core/workspace-app/images-ai.js} +0 -0
  469. /package/{images-create.js → core/workspace-app/images-create.js} +0 -0
  470. /package/{images-page.js → core/workspace-app/images-page.js} +0 -0
  471. /package/{images-records.js → core/workspace-app/images-records.js} +0 -0
  472. /package/{images-render.js → core/workspace-app/images-render.js} +0 -0
  473. /package/{images-svg-core.js → core/workspace-app/images-svg-core.js} +0 -0
  474. /package/{images-svg-sanitize.js → core/workspace-app/images-svg-sanitize.js} +0 -0
  475. /package/{images-svg.js → core/workspace-app/images-svg.js} +0 -0
  476. /package/{images.js → core/workspace-app/images.js} +0 -0
  477. /package/{jobs.js → core/workspace-app/jobs.js} +0 -0
  478. /package/{linger.js → core/workspace-app/linger.js} +0 -0
  479. /package/{lists.js → core/workspace-app/lists.js} +0 -0
  480. /package/{media-info.js → core/workspace-app/media-info.js} +0 -0
  481. /package/{navigation-page.js → core/workspace-app/navigation-page.js} +0 -0
  482. /package/{navigation.js → core/workspace-app/navigation.js} +0 -0
  483. /package/{notes.js → core/workspace-app/notes.js} +0 -0
  484. /package/{palette-index.js → core/workspace-app/palette-index.js} +0 -0
  485. /package/{playwright.config.js → core/workspace-app/playwright.config.js} +0 -0
  486. /package/{plugin-install.js → core/workspace-app/plugin-install.js} +0 -0
  487. /package/{plugin-manifest.js → core/workspace-app/plugin-manifest.js} +0 -0
  488. /package/{plugin-settings.js → core/workspace-app/plugin-settings.js} +0 -0
  489. /package/{plugins → core/workspace-app/plugins}/workspace-notes/index.js +0 -0
  490. /package/{project-make.js → core/workspace-app/project-make.js} +0 -0
  491. /package/{project-name.js → core/workspace-app/project-name.js} +0 -0
  492. /package/{public → core/workspace-app/public}/assistant-avatar-moves.js +0 -0
  493. /package/{public → core/workspace-app/public}/assistant-avatar-ui.js +0 -0
  494. /package/{public → core/workspace-app/public}/assistant-avatar.js +0 -0
  495. /package/{public → core/workspace-app/public}/avatar/druplicon.vrm +0 -0
  496. /package/{public → core/workspace-app/public}/css/base.css +0 -0
  497. /package/{public → core/workspace-app/public}/css/components/ai-assistant.css +0 -0
  498. /package/{public → core/workspace-app/public}/css/components/assistant-avatar.css +0 -0
  499. /package/{public → core/workspace-app/public}/css/components/assistant-views.css +0 -0
  500. /package/{public → core/workspace-app/public}/css/components/build-order.css +0 -0
  501. /package/{public → core/workspace-app/public}/css/components/clone-as-a-git-worktree.css +0 -0
  502. /package/{public → core/workspace-app/public}/css/components/command-palette.css +0 -0
  503. /package/{public → core/workspace-app/public}/css/components/commands-center.css +0 -0
  504. /package/{public → core/workspace-app/public}/css/components/confirm-dialog.css +0 -0
  505. /package/{public → core/workspace-app/public}/css/components/create-card.css +0 -0
  506. /package/{public → core/workspace-app/public}/css/components/ddev-menu.css +0 -0
  507. /package/{public → core/workspace-app/public}/css/components/deep-chat.css +0 -0
  508. /package/{public → core/workspace-app/public}/css/components/density.css +0 -0
  509. /package/{public → core/workspace-app/public}/css/components/floating-assistant-workspace-pages.css +0 -0
  510. /package/{public → core/workspace-app/public}/css/components/folders.css +0 -0
  511. /package/{public → core/workspace-app/public}/css/components/full-height-left-assistant-sidebar-all-pages.css +0 -0
  512. /package/{public → core/workspace-app/public}/css/components/full-height-right-actions-rail-pages-that-have-actions.css +0 -0
  513. /package/{public → core/workspace-app/public}/css/components/grid-and-list.css +0 -0
  514. /package/{public → core/workspace-app/public}/css/components/help-page.css +0 -0
  515. /package/{public → core/workspace-app/public}/css/components/hero.css +0 -0
  516. /package/{public → core/workspace-app/public}/css/components/images.css +0 -0
  517. /package/{public → core/workspace-app/public}/css/components/layout-rhythm-and-spacing-polish.css +0 -0
  518. /package/{public → core/workspace-app/public}/css/components/mic-on-any-field-that-wants-typing.css +0 -0
  519. /package/{public → core/workspace-app/public}/css/components/modal-fit.css +0 -0
  520. /package/{public → core/workspace-app/public}/css/components/page-width.css +0 -0
  521. /package/{public → core/workspace-app/public}/css/components/plugins-page.css +0 -0
  522. /package/{public → core/workspace-app/public}/css/components/project-cards.css +0 -0
  523. /package/{public → core/workspace-app/public}/css/components/project-rows.css +0 -0
  524. /package/{public → core/workspace-app/public}/css/components/reordering-the-workspace-cards.css +0 -0
  525. /package/{public → core/workspace-app/public}/css/components/responsive-home-and-the-workspace-pages.css +0 -0
  526. /package/{public → core/workspace-app/public}/css/components/running-commands-as-toasts.css +0 -0
  527. /package/{public → core/workspace-app/public}/css/components/settings-editor-core-config-yml.css +0 -0
  528. /package/{public → core/workspace-app/public}/css/components/short-results-as-a-toast-at-the-top.css +0 -0
  529. /package/{public → core/workspace-app/public}/css/components/sound-player.css +0 -0
  530. /package/{public → core/workspace-app/public}/css/components/suggest-agents-skills-commands.css +0 -0
  531. /package/{public → core/workspace-app/public}/css/components/tab-modal.css +0 -0
  532. /package/{public → core/workspace-app/public}/css/components/terminals.css +0 -0
  533. /package/{public → core/workspace-app/public}/css/components/test-report-media-one-file-per-row-paged.css +0 -0
  534. /package/{public → core/workspace-app/public}/css/components/top-toolbar-logo-breadcrumb.css +0 -0
  535. /package/{public → core/workspace-app/public}/css/components/video-edit.css +0 -0
  536. /package/{public → core/workspace-app/public}/css/components/web-ide-open-with.css +0 -0
  537. /package/{public → core/workspace-app/public}/css/components/working-indicators.css +0 -0
  538. /package/{public → core/workspace-app/public}/css/components/workspace-cards.css +0 -0
  539. /package/{public → core/workspace-app/public}/favicon.ico +0 -0
  540. /package/{public → core/workspace-app/public}/favicon.png +0 -0
  541. /package/{public → core/workspace-app/public}/htmx.min.js +0 -0
  542. /package/{public → core/workspace-app/public}/logo.png +0 -0
  543. /package/{public → core/workspace-app/public}/media-player.js +0 -0
  544. /package/{public → core/workspace-app/public}/mic.js +0 -0
  545. /package/{public → core/workspace-app/public}/narration.js +0 -0
  546. /package/{public → core/workspace-app/public}/palette.js +0 -0
  547. /package/{public → core/workspace-app/public}/sortable.js +0 -0
  548. /package/{public → core/workspace-app/public}/suggest.js +0 -0
  549. /package/{public → core/workspace-app/public}/terminal.js +0 -0
  550. /package/{public → core/workspace-app/public}/themes/default/theme.css +0 -0
  551. /package/{public → core/workspace-app/public}/themes/ember/theme.css +0 -0
  552. /package/{public → core/workspace-app/public}/themes/press/theme.css +0 -0
  553. /package/{public → core/workspace-app/public}/themes/slate/theme.css +0 -0
  554. /package/{public → core/workspace-app/public}/touch-icon.png +0 -0
  555. /package/{public → core/workspace-app/public}/ui.js +0 -0
  556. /package/{public → core/workspace-app/public}/vendor/ace/LICENSE.txt +0 -0
  557. /package/{public → core/workspace-app/public}/vendor/ace/ace.js +0 -0
  558. /package/{public → core/workspace-app/public}/vendor/ace/ext-language_tools.js +0 -0
  559. /package/{public → core/workspace-app/public}/vendor/ace/ext-searchbox.js +0 -0
  560. /package/{public → core/workspace-app/public}/vendor/ace/mode-css.js +0 -0
  561. /package/{public → core/workspace-app/public}/vendor/ace/mode-html.js +0 -0
  562. /package/{public → core/workspace-app/public}/vendor/ace/mode-javascript.js +0 -0
  563. /package/{public → core/workspace-app/public}/vendor/ace/mode-markdown.js +0 -0
  564. /package/{public → core/workspace-app/public}/vendor/ace/mode-sh.js +0 -0
  565. /package/{public → core/workspace-app/public}/vendor/ace/mode-yaml.js +0 -0
  566. /package/{public → core/workspace-app/public}/vendor/ace/theme-textmate.js +0 -0
  567. /package/{public → core/workspace-app/public}/vendor/ace/theme-tomorrow_night.js +0 -0
  568. /package/{public → core/workspace-app/public}/vendor/ace/worker-yaml.js +0 -0
  569. /package/{public → core/workspace-app/public}/vendor/deep-chat.LICENSE +0 -0
  570. /package/{public → core/workspace-app/public}/vendor/deep-chat.bundle.js +0 -0
  571. /package/{public → core/workspace-app/public}/vendor/fonts/dm-serif-text-LICENSE.txt +0 -0
  572. /package/{public → core/workspace-app/public}/vendor/fonts/dm-serif-text-latin-400-italic.woff2 +0 -0
  573. /package/{public → core/workspace-app/public}/vendor/fonts/dm-serif-text-latin-400-normal.woff2 +0 -0
  574. /package/{public → core/workspace-app/public}/vendor/fonts/figtree-LICENSE.txt +0 -0
  575. /package/{public → core/workspace-app/public}/vendor/fonts/figtree-latin-400-italic.woff2 +0 -0
  576. /package/{public → core/workspace-app/public}/vendor/fonts/figtree-latin-400-normal.woff2 +0 -0
  577. /package/{public → core/workspace-app/public}/vendor/fonts/figtree-latin-600-italic.woff2 +0 -0
  578. /package/{public → core/workspace-app/public}/vendor/fonts/figtree-latin-600-normal.woff2 +0 -0
  579. /package/{public → core/workspace-app/public}/vendor/fonts/jetbrains-mono-LICENSE.txt +0 -0
  580. /package/{public → core/workspace-app/public}/vendor/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
  581. /package/{public → core/workspace-app/public}/vendor/fonts/ubuntu-mono-LICENSE.txt +0 -0
  582. /package/{public → core/workspace-app/public}/vendor/fonts/ubuntu-mono-latin-wght-normal.woff2 +0 -0
  583. /package/{public → core/workspace-app/public}/vendor/tabler-LICENSE.txt +0 -0
  584. /package/{public → core/workspace-app/public}/vendor/tabler-sprite.svg +0 -0
  585. /package/{public → core/workspace-app/public}/vendor/three/LICENSE +0 -0
  586. /package/{public → core/workspace-app/public}/vendor/three/addons/loaders/GLTFLoader.js +0 -0
  587. /package/{public → core/workspace-app/public}/vendor/three/addons/utils/BufferGeometryUtils.js +0 -0
  588. /package/{public → core/workspace-app/public}/vendor/three/addons/utils/SkeletonUtils.js +0 -0
  589. /package/{public → core/workspace-app/public}/vendor/three/three.core.min.js +0 -0
  590. /package/{public → core/workspace-app/public}/vendor/three/three.module.min.js +0 -0
  591. /package/{public → core/workspace-app/public}/vendor/three-vrm/LICENSE +0 -0
  592. /package/{public → core/workspace-app/public}/vendor/three-vrm/three-vrm.module.min.js +0 -0
  593. /package/{public → core/workspace-app/public}/vendor/uikit-icons.min.js +0 -0
  594. /package/{public → core/workspace-app/public}/vendor/uikit.min.css +0 -0
  595. /package/{public → core/workspace-app/public}/vendor/uikit.min.js +0 -0
  596. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/bookmark-outlined.svg +0 -0
  597. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/headset.svg +0 -0
  598. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/menu.svg +0 -0
  599. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/pause.svg +0 -0
  600. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/play.svg +0 -0
  601. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/share.svg +0 -0
  602. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/sound-off.svg +0 -0
  603. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/sound-on.svg +0 -0
  604. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/stop.svg +0 -0
  605. /package/{public → core/workspace-app/public}/vendor/xterm/addon-fit.js +0 -0
  606. /package/{public → core/workspace-app/public}/vendor/xterm/addon-unicode11.js +0 -0
  607. /package/{public → core/workspace-app/public}/vendor/xterm/addon-web-links.js +0 -0
  608. /package/{public → core/workspace-app/public}/vendor/xterm/xterm.css +0 -0
  609. /package/{public → core/workspace-app/public}/vendor/xterm/xterm.js +0 -0
  610. /package/{rag.js → core/workspace-app/rag.js} +0 -0
  611. /package/{reader.js → core/workspace-app/reader.js} +0 -0
  612. /package/{repo-blobs.js → core/workspace-app/repo-blobs.js} +0 -0
  613. /package/{report-pdf.js → core/workspace-app/report-pdf.js} +0 -0
  614. /package/{screenrc → core/workspace-app/screenrc} +0 -0
  615. /package/{screens.js → core/workspace-app/screens.js} +0 -0
  616. /package/{scripts → core/workspace-app/scripts}/code-server-start.sh +0 -0
  617. /package/{scripts → core/workspace-app/scripts}/images-claude-finish.js +0 -0
  618. /package/{scripts → core/workspace-app/scripts}/screen-host.sh +0 -0
  619. /package/{scripts → core/workspace-app/scripts}/screen-runner.js +0 -0
  620. /package/{scripts → core/workspace-app/scripts}/screen-then.js +0 -0
  621. /package/{scripts → core/workspace-app/scripts}/update.sh +0 -0
  622. /package/{settings-file.js → core/workspace-app/settings-file.js} +0 -0
  623. /package/{speech.js → core/workspace-app/speech.js} +0 -0
  624. /package/{tab-modal.js → core/workspace-app/tab-modal.js} +0 -0
  625. /package/{test-reports.js → core/workspace-app/test-reports.js} +0 -0
  626. /package/{themes.js → core/workspace-app/themes.js} +0 -0
  627. /package/{transcripts.js → core/workspace-app/transcripts.js} +0 -0
  628. /package/{urls.js → core/workspace-app/urls.js} +0 -0
  629. /package/{validate.js → core/workspace-app/validate.js} +0 -0
  630. /package/{version-check.js → core/workspace-app/version-check.js} +0 -0
  631. /package/{video-edit.js → core/workspace-app/video-edit.js} +0 -0
  632. /package/{waiting.js → core/workspace-app/waiting.js} +0 -0
  633. /package/{workspace-card-tools.js → core/workspace-app/workspace-card-tools.js} +0 -0
  634. /package/{workspace-delete.js → core/workspace-app/workspace-delete.js} +0 -0
@@ -0,0 +1,40 @@
1
+ /**
2
+ * SSH keys into the agent container every project borrows them from.
3
+ *
4
+ * Without it, a `composer install` that needs a private repository fails at the first package nobody
5
+ * can read — and the fix is a command in a terminal that nothing in the dashboard mentions.
6
+ *
7
+ * It is also the ONE verb here that needs a person at the keyboard: `ssh-add` asks for the key's
8
+ * passphrase. So it runs in a screen session, which this dashboard can already open a terminal onto,
9
+ * and the menu item says to go and answer it. As a fire-and-forget button it would look like a
10
+ * command that silently did nothing.
11
+ */
12
+
13
+ const { test } = require('node:test');
14
+ const assert = require('node:assert');
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+
18
+ const APP = path.join(__dirname, '../..');
19
+ const ddev = fs.readFileSync(path.join(APP, 'ddev.js'), 'utf8');
20
+ const actions = fs.readFileSync(path.join(APP, 'actions/ddev.js'), 'utf8');
21
+ const server = fs.readFileSync(path.join(APP, 'server.js'), 'utf8');
22
+
23
+ test('it runs where somebody can answer it', () => {
24
+ assert.match(ddev, /'\/actions\/ddev-auth-ssh':[^\n]*args: \['auth', 'ssh'\]/);
25
+ assert.match(ddev, /'\/actions\/ddev-auth-ssh':[^\n]*inScreen: true/,
26
+ 'a prompt in a detached job is a prompt nobody can answer');
27
+ assert.match(actions, /'\/actions\/ddev-auth-ssh': ddev_verb,/);
28
+ // It is not tied to one project being up: the agent container is global, and a key added from any
29
+ // project is a key every project has.
30
+ assert.ok(!/'\/actions\/ddev-auth-ssh':[^\n]*needsRunning: true/.test(ddev));
31
+ });
32
+
33
+ test('the item says a passphrase is waiting, and where', () => {
34
+ const item = server.slice(server.indexOf('Add my SSH keys'), server.indexOf('Add my SSH keys') + 200);
35
+ assert.match(server, /then answer the passphrase in Screens/);
36
+ assert.ok(item.length > 0);
37
+ // The row already raises a hand for a session that is waiting for input, so the two features meet:
38
+ // this is the one action that will reliably raise it.
39
+ assert.match(server, /title="Add your SSH keys to the agent container every project borrows them from/);
40
+ });
@@ -0,0 +1,56 @@
1
+ 'use strict';
2
+ const test = require('node:test');
3
+ const assert = require('node:assert');
4
+ const fs = require('node:fs');
5
+ const os = require('node:os');
6
+ const path = require('node:path');
7
+
8
+ const { customCommands, menuGroupsHtml } = require('../../ddev-custom-commands');
9
+ const composerSrc = fs.readFileSync(path.join(__dirname, '../../actions/composer.js'), 'utf8');
10
+
11
+ function project({ type = 'drupal', lock = true } = {}) {
12
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ddev-cmds-'));
13
+ fs.mkdirSync(path.join(dir, '.ddev', 'commands', 'web', 'libs'), { recursive: true });
14
+ fs.mkdirSync(path.join(dir, '.ddev', 'commands', 'host'), { recursive: true });
15
+ fs.writeFileSync(path.join(dir, '.ddev', 'config.yaml'), `name: x\ntype: ${type}\n`);
16
+ const cmd = (where, name, body, mode = 0o755) => fs.writeFileSync(path.join(dir, '.ddev', 'commands', where, name), body, { mode });
17
+ cmd('web', 'add-testing-users', '#!/bin/bash\n## Description: Add testing users\n## Usage: add-testing-users\n## Aliases: webship:add-testing-users\n');
18
+ cmd('web', 'helper.sh', 'echo sourced\n', 0o644);
19
+ cmd('web', 'README.txt', 'docs\n');
20
+ cmd('web', 'solrtail.example', '#!/bin/bash\n## Description: example\n');
21
+ cmd('web', 'wp-only', '#!/bin/bash\n## Description: WordPress only\n## ProjectTypes: wordpress\n');
22
+ cmd('host', 'open-ide', '#!/bin/bash\n## Description: Open the IDE\n');
23
+ fs.writeFileSync(path.join(dir, 'composer.json'), JSON.stringify({ scripts: { 'post-install-cmd': 'x', lint: 'phpcs' }, 'scripts-aliases': { lint: ['cs'] } }));
24
+ if (lock) fs.writeFileSync(path.join(dir, 'composer.lock'), '{}');
25
+ fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify({ scripts: { build: 'vite build', postinstall: 'x' } }));
26
+ return dir;
27
+ }
28
+
29
+ test('every service folder is read, helpers and examples are not commands, ProjectTypes is honoured', () => {
30
+ const list = customCommands(project());
31
+ assert.deepStrictEqual(list.map((c) => `${c.service}/${c.name}`), ['web/add-testing-users', 'host/open-ide']);
32
+ assert.deepStrictEqual(list[0].aliases, ['webship:add-testing-users']);
33
+ assert.strictEqual(customCommands(project({ type: 'wordpress' })).length, 3);
34
+ });
35
+
36
+ test('the DDEV menu gets three folding groups: custom commands, composer.json and package.json scripts', () => {
37
+ const html = menuGroupsHtml({ key: 'dev', project: 'p', projectPath: project(), running: true });
38
+ for (const h of ['custom commands', 'composer.json scripts', 'package.json scripts']) assert.match(html, new RegExp(`class="uk-nav-header" data-group="${h}"`));
39
+ assert.match(html, /hx-post="\/actions\/ddev-project-command"[^>]*"command":"add-testing-users"/);
40
+ assert.match(html, /title="Add testing users — ddev add-testing-users — aliases: webship:add-testing-users">/);
41
+ assert.match(html, /hx-post="\/actions\/composer-script"[^>]*"script":"lint"/);
42
+ assert.match(html, /lint <span class="uk-text-meta">· cs/);
43
+ assert.match(html, /hx-post="\/actions\/node-script"[^>]*"script":"build"/);
44
+ assert.ok(!/post-install-cmd|postinstall/.test(html), 'hooks run on their own');
45
+ });
46
+
47
+ test('a stopped project offers host commands and says container ones need a start', () => {
48
+ const html = menuGroupsHtml({ key: 'dev', project: 'p', projectPath: project(), running: false });
49
+ assert.match(html, /"command":"open-ide"/);
50
+ assert.match(html, /ddev add-testing-users · start first/);
51
+ });
52
+
53
+ test('composer.json scripts are listed only once composer.lock is there, and the route allows them for a DDEV folder', () => {
54
+ assert.ok(!/composer\.json scripts/.test(menuGroupsHtml({ key: 'dev', project: 'p', projectPath: project({ lock: false }), running: true })));
55
+ assert.match(composerSrc, /const installedDdev = \(dir\) => isDdevProject\(dir\) && fs\.existsSync\(path\.join\(dir, 'composer\.lock'\)\);/);
56
+ });
@@ -0,0 +1,57 @@
1
+ /**
2
+ * The four questions worth asking before guessing at a broken project.
3
+ *
4
+ * Docker and the network, the certificate the browser will not trust, the ports something else is
5
+ * holding, and whether the database volume on disk is the engine and version the config asks for.
6
+ * DDEV answers all four and the dashboard offered none of them, so the moment you need them is the
7
+ * moment you go looking for a command name.
8
+ *
9
+ * All four READ. None of them changes anything, which is why they are one group and why none is
10
+ * behind the confirm dialog — a diagnostic you hesitate to run is not a diagnostic.
11
+ */
12
+
13
+ const { test } = require('node:test');
14
+ const assert = require('node:assert');
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+
18
+ const APP = path.join(__dirname, '../..');
19
+ const ddev = fs.readFileSync(path.join(APP, 'ddev.js'), 'utf8');
20
+ const actions = fs.readFileSync(path.join(APP, 'actions/ddev.js'), 'utf8');
21
+ const server = fs.readFileSync(path.join(APP, 'server.js'), 'utf8');
22
+
23
+ const FOUR = {
24
+ 'ddev-diagnose': ['utility', 'diagnose'],
25
+ 'ddev-tls-diagnose': ['utility', 'tls-diagnose'],
26
+ 'ddev-port-diagnose': ['utility', 'port-diagnose'],
27
+ 'ddev-db-match': ['utility', 'check-db-match'],
28
+ };
29
+
30
+ test('each is the CLI command it claims to be', () => {
31
+ // The `utility` namespace, as `ddev utility --help` lists it on v1.25 — not the older `ddev debug`.
32
+ for (const [action, args] of Object.entries(FOUR)) {
33
+ const line = ddev.match(new RegExp(`'/actions/${action}':[^\\n]*`))[0];
34
+ assert.ok(line.includes(`args: ['${args[0]}', '${args[1]}']`), `${action} runs ${args.join(' ')}`);
35
+ assert.match(actions, new RegExp(`'/actions/${action}': ddev_verb,`), action);
36
+ }
37
+ });
38
+
39
+ test('none of them is destructive, and none needs the project running', () => {
40
+ // They read. And the moment you want them most is when the project will NOT start, so gating them
41
+ // on a running project would take them away exactly then.
42
+ for (const action of Object.keys(FOUR)) {
43
+ const line = ddev.match(new RegExp(`'/actions/${action}':[^\\n]*`))[0];
44
+ assert.ok(!line.includes('destructive'), `${action} is not destructive`);
45
+ assert.ok(!line.includes('needsRunning'), `${action} answers even when the project will not start`);
46
+ }
47
+ });
48
+
49
+ test('the menu says what each one answers, not what it is called', () => {
50
+ // "Diagnose the ports" is a question somebody has; `ddev utility port-diagnose` is a command name
51
+ // they would have to already know.
52
+ assert.match(server, /Diagnose — Docker, network, HTTPS/);
53
+ assert.match(server, /Diagnose the certificate/);
54
+ assert.match(server, /Diagnose the ports/);
55
+ assert.match(server, /Does the database match the config\?/);
56
+ assert.match(server, /What else on this machine is holding the ports DDEV needs/);
57
+ });
@@ -0,0 +1,153 @@
1
+ /**
2
+ * The addresses a DDEV project answers at.
3
+ *
4
+ * The bug these were written for: the dashboard read `additional_hostnames` and nothing else, so a
5
+ * project with a Storybook showed no extra domains at all — Webship writes its Storybook address
6
+ * into `additional_fqdns`, which is a different key with different rules. Seven projects in the
7
+ * tree had a Storybook the dashboard never mentioned.
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ const test = require('node:test');
13
+ const assert = require('node:assert');
14
+ const fs = require('fs');
15
+ const os = require('os');
16
+ const path = require('path');
17
+
18
+ const { ddevHostnames, ddevExtraDomains } = require('../../ddev-domains');
19
+ const { ROOT } = require('../../workspaces');
20
+
21
+ /** A throwaway project directory holding one .ddev/config.yaml. */
22
+ function project(yaml) {
23
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ddev-domains-'));
24
+ fs.mkdirSync(path.join(dir, '.ddev'), { recursive: true });
25
+ fs.writeFileSync(path.join(dir, '.ddev', 'config.yaml'), yaml);
26
+ return dir;
27
+ }
28
+
29
+ /* ---------------- the two keys ---------------- */
30
+
31
+ test('a label takes the TLD, a fqdn does not', () => {
32
+ // The whole distinction. Folding a fqdn into the label list would give
33
+ // storybook.blog.ddev.site.ddev.site, which resolves to nothing.
34
+ const dir = project(`name: blog
35
+ project_tld: ddev.site
36
+ additional_hostnames:
37
+ - blog-dev
38
+ additional_fqdns:
39
+ - storybook.blog.ddev.site
40
+ `);
41
+ assert.deepEqual(ddevExtraDomains(dir).map((d) => d.host),
42
+ ['blog-dev.ddev.site', 'storybook.blog.ddev.site']);
43
+ });
44
+
45
+ test('a custom project_tld reaches the labels and leaves the fqdns alone', () => {
46
+ const dir = project(`name: blog
47
+ project_tld: example.test
48
+ additional_hostnames:
49
+ - alias
50
+ additional_fqdns:
51
+ - storybook.blog.ddev.site
52
+ `);
53
+ assert.deepEqual(ddevExtraDomains(dir).map((d) => d.host),
54
+ ['alias.example.test', 'storybook.blog.ddev.site']);
55
+ });
56
+
57
+ test('both keys are read in flow style as well as block style', () => {
58
+ const dir = project(`name: blog
59
+ additional_hostnames: [one, "two"]
60
+ additional_fqdns: ['storybook.blog.ddev.site']
61
+ `);
62
+ assert.deepEqual(ddevHostnames(dir).list, ['one', 'two']);
63
+ assert.deepEqual(ddevHostnames(dir).fqdns, ['storybook.blog.ddev.site']);
64
+ });
65
+
66
+ test('the two lists stay apart, because one of them is compared against labels', () => {
67
+ // hostnameOwner compares `list` entries as LABELS. A fqdn in there would be compared against a
68
+ // label and never match, which is a collision check that silently stops checking.
69
+ const dir = project(`name: blog
70
+ additional_hostnames:
71
+ - alias
72
+ additional_fqdns:
73
+ - storybook.blog.ddev.site
74
+ `);
75
+ const h = ddevHostnames(dir);
76
+ assert.deepEqual(h.list, ['alias']);
77
+ assert.deepEqual(h.fqdns, ['storybook.blog.ddev.site']);
78
+ });
79
+
80
+ /* ---------------- what must NOT be read ---------------- */
81
+
82
+ test("DDEV's own commented-out examples are not hostnames", () => {
83
+ // A stock config.yaml ships these. A parser that reads them hands every project in the tree two
84
+ // hostnames it never had, and the collision check then reports conflicts that do not exist.
85
+ const dir = project(`name: blog
86
+ # additional_hostnames:
87
+ # - somename
88
+ # - someothername
89
+ # additional_fqdns:
90
+ # - example.com
91
+ `);
92
+ assert.deepEqual(ddevExtraDomains(dir), []);
93
+ });
94
+
95
+ test('a folder that is not a DDEV project answers nothing rather than throwing', () => {
96
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'not-ddev-'));
97
+ assert.deepEqual(ddevExtraDomains(dir), []);
98
+ assert.deepEqual(ddevHostnames(dir), { list: [], fqdns: [], tld: 'ddev.site' });
99
+ });
100
+
101
+ test('an empty list is empty, not a list holding nothing', () => {
102
+ const dir = project(`name: blog
103
+ additional_hostnames: []
104
+ additional_fqdns:
105
+ - storybook.blog.ddev.site
106
+ `);
107
+ assert.deepEqual(ddevExtraDomains(dir).map((d) => d.host), ['storybook.blog.ddev.site']);
108
+ });
109
+
110
+ test('the same address declared twice is listed once', () => {
111
+ const dir = project(`name: blog
112
+ additional_fqdns:
113
+ - storybook.blog.ddev.site
114
+ - storybook.blog.ddev.site
115
+ `);
116
+ assert.equal(ddevExtraDomains(dir).length, 1);
117
+ });
118
+
119
+ /* ---------------- which one is Storybook ---------------- */
120
+
121
+ test('Storybook is marked, and only Storybook', () => {
122
+ const dir = project(`name: blog
123
+ additional_fqdns:
124
+ - storybook.blog.ddev.site
125
+ - mail.blog.ddev.site
126
+ - my-storybook-notes.blog.ddev.site
127
+ `);
128
+ const got = ddevExtraDomains(dir);
129
+ assert.deepEqual(got.map((d) => d.storybook), [true, false, false],
130
+ 'only a host whose FIRST label is storybook is the component library');
131
+ });
132
+
133
+ /* ---------------- against the real tree ---------------- */
134
+
135
+ test('the projects in this tree that ship a Storybook are found', () => {
136
+ // The regression in one line: before this parsed additional_fqdns, every one of these was absent.
137
+ const { loadWorkspaces } = require('../../workspaces');
138
+ const found = [];
139
+ for (const w of Object.values(loadWorkspaces())) {
140
+ if (!w.dir) continue;
141
+ let names = [];
142
+ try {
143
+ names = fs.readdirSync(w.dir, { withFileTypes: true })
144
+ .filter((e) => e.isDirectory() && !e.name.startsWith('.')).map((e) => e.name);
145
+ } catch (_) { continue; }
146
+ for (const n of names) {
147
+ if (ddevExtraDomains(path.join(w.dir, n)).some((d) => d.storybook)) found.push(`${w.key}/${n}`);
148
+ }
149
+ }
150
+ assert.ok(found.length > 0,
151
+ 'no project in the tree declares a Storybook domain — either the tree changed or the fqdn key stopped being read');
152
+ for (const p of found) assert.ok(p.includes('/'), p);
153
+ });
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Extra hostnames for a project.
3
+ *
4
+ * The feature is "the same site at another address", and the whole design rests on
5
+ * ADDING rather than renaming — so what is asserted here is the parsing (which the
6
+ * dashboard and the shell each do separately, and must agree on), the validation,
7
+ * and the collision check, which is the part that prevents a router conflict
8
+ * nobody would be able to see from one project's page.
9
+ */
10
+
11
+ const assert = require('node:assert');
12
+ const { test } = require('node:test');
13
+ const fs = require('fs');
14
+ const os = require('os');
15
+ const path = require('path');
16
+ const { execFileSync, spawnSync } = require('child_process');
17
+
18
+ const ROOT = path.join(__dirname, '..', '..', '..', '..');
19
+ const FUN = path.join(ROOT, 'core', 'scripts', 'functions', 'fun-ddev-hostnames.sh');
20
+ const CLI = path.join(ROOT, 'core', 'scripts', 'functions', 'fun-hostnames-cli.sh');
21
+ const ARGS = path.join(ROOT, 'core', 'scripts', 'args', 'arg-hostnames.sh');
22
+
23
+ function sh(snippet) {
24
+ return execFileSync('bash', ['-c', `source ${JSON.stringify(FUN)} ; ${snippet}`], { encoding: 'utf8' }).trim();
25
+ }
26
+
27
+ /** A project directory with the given config.yaml body. */
28
+ function project(body) {
29
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'hn-'));
30
+ fs.mkdirSync(path.join(tmp, '.ddev'), { recursive: true });
31
+ fs.writeFileSync(path.join(tmp, '.ddev', 'config.yaml'), body);
32
+ return tmp;
33
+ }
34
+
35
+ test('the shell files are valid', () => {
36
+ for (const f of [FUN, CLI, ARGS]) assert.strictEqual(spawnSync('bash', ['-n', f]).status, 0, f);
37
+ });
38
+
39
+ test('additional_hostnames is read in both legal YAML shapes', () => {
40
+ const block = project('name: p\nadditional_hostnames:\n - one\n - "two"\nphp_version: "8.3"\n');
41
+ const inline = project('name: p\nadditional_hostnames: [one, "two"]\n');
42
+ const none = project('name: p\ndocroot: web\n');
43
+ try {
44
+ assert.deepStrictEqual(sh(`ddev_hostnames_list ${JSON.stringify(block)}`).split('\n'), ['one', 'two']);
45
+ assert.deepStrictEqual(sh(`ddev_hostnames_list ${JSON.stringify(inline)}`).split('\n'), ['one', 'two']);
46
+ assert.strictEqual(sh(`ddev_hostnames_list ${JSON.stringify(none)}`), '');
47
+ } finally { [block, inline, none].forEach((d) => fs.rmSync(d, { recursive: true, force: true })); }
48
+ });
49
+
50
+ test('DDEV’s own commented-out examples are not read as real hostnames', () => {
51
+ // config.yaml ships with `# additional_hostnames:` and `# - somename` in its
52
+ // comments. Reading those would hand every project two hostnames it never had.
53
+ const d = project('name: p\n# additional_hostnames:\n# - somename\n# - someothername\n');
54
+ try {
55
+ assert.strictEqual(sh(`ddev_hostnames_list ${JSON.stringify(d)}`), '');
56
+ } finally { fs.rmSync(d, { recursive: true, force: true }); }
57
+ });
58
+
59
+ test('a hostname is a LABEL: no dots, no empty, no leading hyphen', () => {
60
+ for (const good of ['staging', 'blog2-dev', 'a', 'x1-2-3']) {
61
+ assert.strictEqual(sh(`ddev_hostname_valid ${JSON.stringify(good)} && echo yes || echo no`), 'yes', good);
62
+ }
63
+ // A dot would silently produce name.example.com.<tld>, which resolves to nothing.
64
+ for (const bad of ['a.b', 'has space', '-lead', 'trail-', '']) {
65
+ assert.strictEqual(sh(`ddev_hostname_valid ${JSON.stringify(bad)} && echo yes || echo no`), 'no', bad);
66
+ }
67
+ });
68
+
69
+ test('the workspace hostname is <project>-<workspace>, with no trailing junk', () => {
70
+ assert.strictEqual(sh('ddev_workspace_hostname test3 dev'), 'test3-dev');
71
+ assert.strictEqual(sh('ddev_workspace_hostname blog2 site-templates'), 'blog2-site-templates');
72
+ // `tr -c` complements the newline as well, which is how ddev_project_name grew a
73
+ // trailing hyphen; the \n is in the kept set here and this asserts it stays.
74
+ assert.ok(!sh('ddev_workspace_hostname test3 dev').endsWith('-'), 'no trailing hyphen');
75
+ assert.strictEqual(sh('ddev_workspace_hostname "" dev'), '');
76
+ });
77
+
78
+ test('the dashboard reads hostnames the same way the shell does', () => {
79
+ // This used to match patterns in server.js, because server.js cannot be required without starting
80
+ // the server. The parsing now lives in ddev-domains.js, so the behaviour is asserted by CALLING it
81
+ // — which is how a missing `additional_fqdns` would have been caught the first time.
82
+ const { ddevHostnames } = require('../../ddev-domains');
83
+ const os = require('os');
84
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ddev-hostnames-'));
85
+ fs.mkdirSync(path.join(dir, '.ddev'), { recursive: true });
86
+ fs.writeFileSync(path.join(dir, '.ddev', 'config.yaml'), `name: blog
87
+ project_tld: example.test
88
+ additional_hostnames: [one, "two"]
89
+ additional_fqdns:
90
+ - storybook.blog.ddev.site
91
+ `);
92
+ const h = ddevHostnames(dir);
93
+ assert.deepEqual(h.list, ['one', 'two'], 'the inline [a, b] shape must be handled');
94
+ assert.deepEqual(h.fqdns, ['storybook.blog.ddev.site']);
95
+ assert.strictEqual(h.tld, 'example.test');
96
+
97
+ // The important one: no second writer. DDEV owns config.yaml, and the dashboard shells out to
98
+ // cmd-tools-hostnames.sh so the button and the CLI cannot disagree about the collision check.
99
+ const mod = fs.readFileSync(path.join(__dirname, '..', '..', 'ddev-domains.js'), 'utf8');
100
+ assert.ok(!/writeFileSync/.test(mod), 'the dashboard must not write config.yaml itself');
101
+ const src = fs.readFileSync(path.join(__dirname, '..', '..', 'server.js'), 'utf8');
102
+ assert.match(src, /cmd-tools-hostnames\.sh/);
103
+ });
104
+
105
+ test('the five site workspaces carry the command; the code-only ones do not', () => {
106
+ for (const w of ['dev', 'test', 'sandboxes', 'demos', 'projects']) {
107
+ assert.ok(fs.existsSync(path.join(ROOT, w, 'cmd-tools-hostnames.sh')), `${w} is missing it`);
108
+ }
109
+ for (const w of ['docs', 'videos', 'specs', 'products']) {
110
+ if (!fs.existsSync(path.join(ROOT, w))) continue;
111
+ assert.ok(!fs.existsSync(path.join(ROOT, w, 'cmd-tools-hostnames.sh')),
112
+ `${w} builds no sites and should not carry it`);
113
+ }
114
+ });
115
+
116
+ test('clone takes the address options, and --hostname is not the name it uses', () => {
117
+ const args = fs.readFileSync(path.join(ROOT, 'core', 'scripts', 'args', 'arg-clone.sh'), 'utf8');
118
+ assert.match(args, /'--extra-hostname'/);
119
+ assert.match(args, /'--with-workspace-hostname'/);
120
+ // HOSTNAME is a bash builtin; argparse assigns the dest as a shell variable, so
121
+ // --hostname would clobber it inside every clone.
122
+ assert.ok(!/'--hostname'/.test(args), 'must not use --hostname: it would clobber $HOSTNAME');
123
+ });
124
+
125
+ test('a clone name is checked against folders elsewhere and booked addresses', () => {
126
+ const fun = fs.readFileSync(path.join(ROOT, 'core', 'scripts', 'functions', 'fun-clone-project.sh'), 'utf8');
127
+ // `ddev list` is not enough on its own: an unregistered folder is invisible to
128
+ // it, and an additional hostname is a claim on an address it says nothing about.
129
+ assert.match(fun, /A folder called '\$\{target_name\}' already exists in this tree/);
130
+ assert.match(fun, /ddev_hostname_owner "\$\{target_name\}"/);
131
+ const src = fs.readFileSync(path.join(__dirname, '..', '..', 'server.js'), 'utf8');
132
+ assert.match(src, /function hostnameOwner/);
133
+ assert.match(src, /already answered by/);
134
+ assert.match(src, /a folder called \$\{esc\(to\)\} already exists in/);
135
+ });
136
+
137
+ test('hostnameOwner skips the project asking, or every project owns its own name', () => {
138
+ const src = fs.readFileSync(path.join(__dirname, '..', '..', 'server.js'), 'utf8');
139
+ const fn = src.slice(src.indexOf('function hostnameOwner'), src.indexOf('function hostnameOwner') + 1200);
140
+ assert.match(fn, /skipDir && path\.resolve\(dir\) === path\.resolve\(skipDir\)/);
141
+ const sh = fs.readFileSync(FUN, 'utf8');
142
+ assert.match(sh.slice(sh.indexOf('function ddev_hostname_owner')), /skip_real/);
143
+ });
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Putting a database back: the other half of Export the database.
3
+ *
4
+ * The row could dump a database into the workspace's backups folder and never put one back, so every
5
+ * dump it wrote was a one-way trip. Two things make a naive version of this dangerous, and both are
6
+ * why the module exists: that folder also holds whole-project tarballs, which `ddev import-db` would
7
+ * accept as if they were SQL; and importing replaces every table in a database that is often the only
8
+ * copy of an afternoon's work.
9
+ */
10
+
11
+ const { test } = require('node:test');
12
+ const assert = require('node:assert');
13
+ const fs = require('fs');
14
+ const os = require('os');
15
+ const path = require('path');
16
+
17
+ const imp = require('../../ddev-import');
18
+
19
+ const APP = path.join(__dirname, '../..');
20
+ const actions = fs.readFileSync(path.join(APP, 'actions/ddev.js'), 'utf8');
21
+
22
+ function backups(files) {
23
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ws-dump-'));
24
+ for (const f of files) fs.writeFileSync(path.join(dir, f), 'x');
25
+ return dir;
26
+ }
27
+
28
+ test('a dump is listed; a whole-project archive is not', () => {
29
+ // `ddev import-db` accepts .tar.gz, .tgz and .zip — and in this folder those are archives of an
30
+ // entire project. Offering one would hand a tarball of a site to the database.
31
+ const dir = backups([
32
+ 'ruverify1--db--2026-09-23-10-12.sql.gz',
33
+ 'dev---vp1107lock--2026-09-09_01-02-41-db.sql.gz',
34
+ 'dev---vp1107lock--2026-09-09_01-02-41.tar.gz',
35
+ 'dev---v11x00b1canvasicon--2026-09-13_17-45-34.tar.gz',
36
+ 'notes.txt',
37
+ '.hidden.sql',
38
+ ]);
39
+ const names = imp.listDumps(dir).map((d) => d.name);
40
+ assert.deepEqual(names.sort(), [
41
+ 'dev---vp1107lock--2026-09-09_01-02-41-db.sql.gz',
42
+ 'ruverify1--db--2026-09-23-10-12.sql.gz',
43
+ ]);
44
+ for (const ext of ['a.sql', 'b.sql.gz', 'c.sql.bz2', 'd.sql.xz', 'e.mysql', 'f.mysql.gz']) {
45
+ assert.ok(imp.DUMP_RE.test(ext), ext);
46
+ }
47
+ for (const no of ['x.tar.gz', 'x.tgz', 'x.zip', 'x.txt', 'x.sql.zip']) {
48
+ assert.equal(imp.DUMP_RE.test(no), false, no);
49
+ }
50
+ });
51
+
52
+ test('the dump is named by basename, and matched against what is there', () => {
53
+ const dir = backups(['real--db--2026.sql.gz']);
54
+ assert.ok(imp.findDump(dir, 'real--db--2026.sql.gz'));
55
+ assert.equal(imp.findDump(dir, 'invented.sql.gz'), null);
56
+ // A name carrying a directory is refused before anything is compared, let alone joined.
57
+ for (const bad of ['../../../etc/passwd', 'sub/real--db--2026.sql.gz', '/etc/passwd', '']) {
58
+ assert.equal(imp.findDump(dir, bad), null, bad);
59
+ }
60
+ });
61
+
62
+ test('a snapshot is taken first, in the same line', () => {
63
+ // `&&`, not `;` — nothing is imported if the snapshot fails. The same shape `drush sql:drop`
64
+ // already uses before it drops. Verified live: the import left `before-import-2026-09-23-10-12`
65
+ // behind, and said so.
66
+ const fn = actions.slice(actions.indexOf('async function ddev_import_db'));
67
+ assert.match(fn, /ddev snapshot --name before-import-\$\{stamp\}`\s*\n\s*\+ ` && ddev import-db --file/);
68
+ assert.match(fn, /The database as it was is the snapshot before-import-/);
69
+ assert.match(fn, /if \(form\.confirm !== 'yes'\)/, 'and it is confirmed');
70
+ assert.match(fn, /ddevImport\.findDump\(backupsDir, form\.dump\)/, 'against the workspace\'s own folder');
71
+ });
72
+
73
+ test('the panel says what importing costs, and what it will not offer', () => {
74
+ const html = imp.importPanelHtml({
75
+ workspace: 'test', project: 'demo', backupsDir: backups(['a--db--1.sql.gz']), backupsLabel: '~/workspace/backups/test',
76
+ });
77
+ assert.match(html, /Importing <strong>replaces<\/strong>/);
78
+ assert.match(html, /A snapshot is taken first/);
79
+ assert.match(html, /Whole-project archives/);
80
+ assert.match(html, /hx-trigger="confirmed-remove"/);
81
+ assert.match(html, /~\/workspace\/backups\/test/, 'and where it is looking');
82
+ });