@webship.co/workspace 1.0.0-beta1 → 1.0.0-rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (632) 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} +9 -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/{plugins-page.js → core/workspace-app/plugins-page.js} +28 -2
  169. package/{server.js → core/workspace-app/server.js} +18 -0
  170. package/core/workspace-app/tests/features/actions-rail.feature +44 -0
  171. package/core/workspace-app/tests/features/ai-items.feature +23 -0
  172. package/core/workspace-app/tests/features/assistant-speech.feature +63 -0
  173. package/core/workspace-app/tests/features/checks.feature +40 -0
  174. package/core/workspace-app/tests/features/clone-project.feature +30 -0
  175. package/core/workspace-app/tests/features/command-authoring.feature +58 -0
  176. package/core/workspace-app/tests/features/dashboard.feature +57 -0
  177. package/core/workspace-app/tests/features/menus.feature +29 -0
  178. package/core/workspace-app/tests/features/plugins/jira/jira.feature +47 -0
  179. package/core/workspace-app/tests/features/plugins.feature +20 -0
  180. package/core/workspace-app/tests/features/project-actions.feature +41 -0
  181. package/core/workspace-app/tests/features/screens.feature +29 -0
  182. package/core/workspace-app/tests/features/settings.feature +46 -0
  183. package/core/workspace-app/tests/features/test-reports.feature +32 -0
  184. package/core/workspace-app/tests/features/theme.feature +40 -0
  185. package/core/workspace-app/tests/step-definitions/actions-post.steps.js +25 -0
  186. package/core/workspace-app/tests/step-definitions/assistant-speech.steps.js +157 -0
  187. package/core/workspace-app/tests/step-definitions/clone-project.steps.js +47 -0
  188. package/core/workspace-app/tests/step-definitions/preconditions.steps.js +168 -0
  189. package/core/workspace-app/tests/unit/_isolated-config.js +31 -0
  190. package/core/workspace-app/tests/unit/accessibility.test.js +124 -0
  191. package/core/workspace-app/tests/unit/actions-rail-ux.test.js +56 -0
  192. package/core/workspace-app/tests/unit/actions-rail.test.js +51 -0
  193. package/core/workspace-app/tests/unit/ai-inventory.test.js +64 -0
  194. package/core/workspace-app/tests/unit/app-docs.test.js +64 -0
  195. package/core/workspace-app/tests/unit/assistant-avatar.test.js +110 -0
  196. package/core/workspace-app/tests/unit/assistant-cast.test.js +105 -0
  197. package/core/workspace-app/tests/unit/assistant-send-animation.test.js +15 -0
  198. package/core/workspace-app/tests/unit/assistant-suggestions.test.js +112 -0
  199. package/core/workspace-app/tests/unit/assistant-switches.test.js +53 -0
  200. package/core/workspace-app/tests/unit/assistant-views.test.js +146 -0
  201. package/core/workspace-app/tests/unit/assistant-working-indicator.test.js +49 -0
  202. package/core/workspace-app/tests/unit/assistant.test.js +61 -0
  203. package/core/workspace-app/tests/unit/audio-player.test.js +147 -0
  204. package/core/workspace-app/tests/unit/audio-voice.test.js +634 -0
  205. package/core/workspace-app/tests/unit/auth-google.test.js +180 -0
  206. package/core/workspace-app/tests/unit/autostart-watch.test.js +76 -0
  207. package/core/workspace-app/tests/unit/browsers.test.js +76 -0
  208. package/core/workspace-app/tests/unit/build-distribution.test.js +125 -0
  209. package/core/workspace-app/tests/unit/build-order.test.js +153 -0
  210. package/core/workspace-app/tests/unit/builder-args.test.js +113 -0
  211. package/core/workspace-app/tests/unit/builders.test.js +158 -0
  212. package/core/workspace-app/tests/unit/card-state-toggle.test.js +29 -0
  213. package/core/workspace-app/tests/unit/changelog.test.js +223 -0
  214. package/core/workspace-app/tests/unit/chat-thread.test.js +92 -0
  215. package/core/workspace-app/tests/unit/checks-plan.test.js +205 -0
  216. package/core/workspace-app/tests/unit/claude-args.test.js +182 -0
  217. package/core/workspace-app/tests/unit/claude-code-here.test.js +15 -0
  218. package/core/workspace-app/tests/unit/claude-resume.test.js +147 -0
  219. package/core/workspace-app/tests/unit/claude-screen-options.test.js +84 -0
  220. package/core/workspace-app/tests/unit/claude-session.test.js +41 -0
  221. package/core/workspace-app/tests/unit/command-flags.test.js +45 -0
  222. package/core/workspace-app/tests/unit/command-palette.test.js +105 -0
  223. package/core/workspace-app/tests/unit/commands-center.test.js +338 -0
  224. package/core/workspace-app/tests/unit/composer-menu.test.js +187 -0
  225. package/core/workspace-app/tests/unit/config-yaml.test.js +116 -0
  226. package/core/workspace-app/tests/unit/confirm-dialog.test.js +69 -0
  227. package/core/workspace-app/tests/unit/create-card.test.js +85 -0
  228. package/core/workspace-app/tests/unit/ddev-addons.test.js +144 -0
  229. package/core/workspace-app/tests/unit/ddev-auth-ssh.test.js +40 -0
  230. package/core/workspace-app/tests/unit/ddev-custom-commands.test.js +56 -0
  231. package/core/workspace-app/tests/unit/ddev-diagnose.test.js +57 -0
  232. package/core/workspace-app/tests/unit/ddev-domains.test.js +179 -0
  233. package/core/workspace-app/tests/unit/ddev-hostnames.test.js +143 -0
  234. package/core/workspace-app/tests/unit/ddev-import.test.js +82 -0
  235. package/core/workspace-app/tests/unit/ddev-mail.test.js +135 -0
  236. package/core/workspace-app/tests/unit/ddev-menu-and-create.test.js +28 -0
  237. package/core/workspace-app/tests/unit/ddev-mutagen.test.js +37 -0
  238. package/core/workspace-app/tests/unit/ddev-options.test.js +51 -0
  239. package/core/workspace-app/tests/unit/ddev-share.test.js +40 -0
  240. package/core/workspace-app/tests/unit/ddev-shim.test.js +57 -0
  241. package/core/workspace-app/tests/unit/ddev-snapshots.test.js +142 -0
  242. package/core/workspace-app/tests/unit/ddev-start-daemons.test.js +62 -0
  243. package/core/workspace-app/tests/unit/ddev-xdebug.test.js +41 -0
  244. package/core/workspace-app/tests/unit/deep-chat-origin.test.js +38 -0
  245. package/core/workspace-app/tests/unit/density.test.js +52 -0
  246. package/core/workspace-app/tests/unit/elevenlabs.test.js +453 -0
  247. package/core/workspace-app/tests/unit/event-log.test.js +102 -0
  248. package/core/workspace-app/tests/unit/favicon.test.js +68 -0
  249. package/core/workspace-app/tests/unit/files-browser.test.js +224 -0
  250. package/core/workspace-app/tests/unit/git-info.test.js +26 -0
  251. package/core/workspace-app/tests/unit/grid-and-list.test.js +103 -0
  252. package/core/workspace-app/tests/unit/host-tool-wrapper.test.js +18 -0
  253. package/core/workspace-app/tests/unit/html-patterns.test.js +51 -0
  254. package/core/workspace-app/tests/unit/ide-infra.test.js +28 -0
  255. package/core/workspace-app/tests/unit/images-ai.test.js +201 -0
  256. package/core/workspace-app/tests/unit/images-create.test.js +59 -0
  257. package/core/workspace-app/tests/unit/images-providers.test.js +248 -0
  258. package/core/workspace-app/tests/unit/images-svg.test.js +213 -0
  259. package/core/workspace-app/tests/unit/images-workspace.test.js +144 -0
  260. package/core/workspace-app/tests/unit/jobs-fragment.test.js +66 -0
  261. package/core/workspace-app/tests/unit/media-ux.test.js +164 -0
  262. package/core/workspace-app/tests/unit/modal-fit.test.js +87 -0
  263. package/core/workspace-app/tests/unit/modules-load.test.js +23 -0
  264. package/core/workspace-app/tests/unit/narration.test.js +186 -0
  265. package/core/workspace-app/tests/unit/navigation-editor.test.js +260 -0
  266. package/core/workspace-app/tests/unit/navigation.test.js +254 -0
  267. package/core/workspace-app/tests/unit/new-folder.test.js +121 -0
  268. package/core/workspace-app/tests/unit/new-screen-form.test.js +149 -0
  269. package/core/workspace-app/tests/unit/node-menu.test.js +161 -0
  270. package/core/workspace-app/tests/unit/notes.test.js +128 -0
  271. package/core/workspace-app/tests/unit/page-structure.test.js +40 -0
  272. package/core/workspace-app/tests/unit/page-width.test.js +59 -0
  273. package/core/workspace-app/tests/unit/php-version.test.js +90 -0
  274. package/core/workspace-app/tests/unit/plan-card.test.js +44 -0
  275. package/core/workspace-app/tests/unit/plugin-catalog.test.js +31 -0
  276. package/core/workspace-app/tests/unit/plugin-system.test.js +251 -0
  277. package/core/workspace-app/tests/unit/plugins-page.test.js +28 -0
  278. package/core/workspace-app/tests/unit/plugins.test.js +58 -0
  279. package/core/workspace-app/tests/unit/preconditions.test.js +21 -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 +93 -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 +220 -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 +436 -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/package.json +38 -36
  336. /package/{WORKSPACES.md → core/workspace-app/WORKSPACES.md} +0 -0
  337. /package/{actions → core/workspace-app/actions}/ai-items.js +0 -0
  338. /package/{actions → core/workspace-app/actions}/audio.js +0 -0
  339. /package/{actions → core/workspace-app/actions}/build-order.js +0 -0
  340. /package/{actions → core/workspace-app/actions}/checks.js +0 -0
  341. /package/{actions → core/workspace-app/actions}/claude-session.js +0 -0
  342. /package/{actions → core/workspace-app/actions}/commands-center.js +0 -0
  343. /package/{actions → core/workspace-app/actions}/composer.js +0 -0
  344. /package/{actions → core/workspace-app/actions}/ddev-addons.js +0 -0
  345. /package/{actions → core/workspace-app/actions}/ddev-mail.js +0 -0
  346. /package/{actions → core/workspace-app/actions}/folders.js +0 -0
  347. /package/{actions → core/workspace-app/actions}/images-svg.js +0 -0
  348. /package/{actions → core/workspace-app/actions}/images.js +0 -0
  349. /package/{actions → core/workspace-app/actions}/navigation.js +0 -0
  350. /package/{actions → core/workspace-app/actions}/node.js +0 -0
  351. /package/{actions → core/workspace-app/actions}/notes.js +0 -0
  352. /package/{actions → core/workspace-app/actions}/plugins.js +0 -0
  353. /package/{actions → core/workspace-app/actions}/project-make.js +0 -0
  354. /package/{actions → core/workspace-app/actions}/row-actions.js +0 -0
  355. /package/{actions → core/workspace-app/actions}/screens.js +0 -0
  356. /package/{actions → core/workspace-app/actions}/video-edit.js +0 -0
  357. /package/{ai-inventory.js → core/workspace-app/ai-inventory.js} +0 -0
  358. /package/{ai-items.js → core/workspace-app/ai-items.js} +0 -0
  359. /package/{assistant-cast.js → core/workspace-app/assistant-cast.js} +0 -0
  360. /package/{assistant-suggestions.js → core/workspace-app/assistant-suggestions.js} +0 -0
  361. /package/{audio-voice.js → core/workspace-app/audio-voice.js} +0 -0
  362. /package/{auth-google.js → core/workspace-app/auth-google.js} +0 -0
  363. /package/{build-ai.js → core/workspace-app/build-ai.js} +0 -0
  364. /package/{build-order-page.js → core/workspace-app/build-order-page.js} +0 -0
  365. /package/{build-order.js → core/workspace-app/build-order.js} +0 -0
  366. /package/{builder-args.js → core/workspace-app/builder-args.js} +0 -0
  367. /package/{changelog.js → core/workspace-app/changelog.js} +0 -0
  368. /package/{chat-threads.js → core/workspace-app/chat-threads.js} +0 -0
  369. /package/{checks.js → core/workspace-app/checks.js} +0 -0
  370. /package/{claude-args.js → core/workspace-app/claude-args.js} +0 -0
  371. /package/{claude-sessions.js → core/workspace-app/claude-sessions.js} +0 -0
  372. /package/{command-author.js → core/workspace-app/command-author.js} +0 -0
  373. /package/{command-block.js → core/workspace-app/command-block.js} +0 -0
  374. /package/{commands → core/workspace-app/commands}/defaults.yml +0 -0
  375. /package/{commands-page.js → core/workspace-app/commands-page.js} +0 -0
  376. /package/{commands.js → core/workspace-app/commands.js} +0 -0
  377. /package/{cucumber.js → core/workspace-app/cucumber.js} +0 -0
  378. /package/{ddev-addons-page.js → core/workspace-app/ddev-addons-page.js} +0 -0
  379. /package/{ddev-addons.js → core/workspace-app/ddev-addons.js} +0 -0
  380. /package/{ddev-custom-commands.js → core/workspace-app/ddev-custom-commands.js} +0 -0
  381. /package/{ddev-domains.js → core/workspace-app/ddev-domains.js} +0 -0
  382. /package/{ddev-import.js → core/workspace-app/ddev-import.js} +0 -0
  383. /package/{ddev-mail-page.js → core/workspace-app/ddev-mail-page.js} +0 -0
  384. /package/{ddev-mail.js → core/workspace-app/ddev-mail.js} +0 -0
  385. /package/{ddev-options.js → core/workspace-app/ddev-options.js} +0 -0
  386. /package/{ddev-snapshots.js → core/workspace-app/ddev-snapshots.js} +0 -0
  387. /package/{ddev.js → core/workspace-app/ddev.js} +0 -0
  388. /package/{docs → core/workspace-app/docs}/DEVELOPER-GUIDE.md +0 -0
  389. /package/{docs → core/workspace-app/docs}/README.md +0 -0
  390. /package/{docs → core/workspace-app/docs}/REFERENCE.md +0 -0
  391. /package/{docs → core/workspace-app/docs}/USER-GUIDE.md +0 -0
  392. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-board.png +0 -0
  393. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-coffee.png +0 -0
  394. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-colours.png +0 -0
  395. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-icons.png +0 -0
  396. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-moves.png +0 -0
  397. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-pick-cookieman.png +0 -0
  398. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-settings-voices.png +0 -0
  399. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-status.png +0 -0
  400. /package/{docs → core/workspace-app/docs}/screenshots/create-card/create-card.png +0 -0
  401. /package/{docs → core/workspace-app/docs}/screenshots/create-card/start-ai.png +0 -0
  402. /package/{docs → core/workspace-app/docs}/screenshots/guide-clone-address.png +0 -0
  403. /package/{docs → core/workspace-app/docs}/screenshots/guide-clone-worktree-off.png +0 -0
  404. /package/{docs → core/workspace-app/docs}/screenshots/guide-clone-worktree-on.png +0 -0
  405. /package/{docs → core/workspace-app/docs}/screenshots/guide-ddev-menu.png +0 -0
  406. /package/{docs → core/workspace-app/docs}/screenshots/guide-domains.png +0 -0
  407. /package/{docs → core/workspace-app/docs}/screenshots/guide-home.png +0 -0
  408. /package/{docs → core/workspace-app/docs}/screenshots/guide-ide-menu.png +0 -0
  409. /package/{docs → core/workspace-app/docs}/screenshots/guide-remove-refusal.png +0 -0
  410. /package/{docs → core/workspace-app/docs}/screenshots/guide-rows-worktree.png +0 -0
  411. /package/{docs → core/workspace-app/docs}/screenshots/guide-screens.png +0 -0
  412. /package/{docs → core/workspace-app/docs}/screenshots/guide-settings.png +0 -0
  413. /package/{docs → core/workspace-app/docs}/screenshots/guide-workspace-page.png +0 -0
  414. /package/{docs → core/workspace-app/docs}/screenshots/home/card-settings.png +0 -0
  415. /package/{docs → core/workspace-app/docs}/screenshots/home/drag-reorder.png +0 -0
  416. /package/{docs → core/workspace-app/docs}/screenshots/home/edit-card.png +0 -0
  417. /package/{docs → core/workspace-app/docs}/screenshots/images/claude-code-image.png +0 -0
  418. /package/{docs → core/workspace-app/docs}/screenshots/images/edit-with-ai.png +0 -0
  419. /package/{docs → core/workspace-app/docs}/screenshots/images/gallery.png +0 -0
  420. /package/{docs → core/workspace-app/docs}/screenshots/images/generate-description.png +0 -0
  421. /package/{docs → core/workspace-app/docs}/screenshots/images/generate-plan.png +0 -0
  422. /package/{docs → core/workspace-app/docs}/screenshots/images/providers.png +0 -0
  423. /package/{docs → core/workspace-app/docs}/screenshots/images/svg-form.png +0 -0
  424. /package/{docs → core/workspace-app/docs}/screenshots/images/svg-result.png +0 -0
  425. /package/{docs → core/workspace-app/docs}/screenshots/images/upload.png +0 -0
  426. /package/{docs → core/workspace-app/docs}/screenshots/new-folder/2-form.png +0 -0
  427. /package/{docs → core/workspace-app/docs}/screenshots/new-folder/8-ddev-options.png +0 -0
  428. /package/{docs → core/workspace-app/docs}/screenshots/new-folder/files-folder.png +0 -0
  429. /package/{docs → core/workspace-app/docs}/screenshots/projects/ddev-custom-commands.png +0 -0
  430. /package/{docs → core/workspace-app/docs}/screenshots/projects/state-chip-stop.png +0 -0
  431. /package/{docs → core/workspace-app/docs}/screenshots/readme/dashboard-dark.png +0 -0
  432. /package/{docs → core/workspace-app/docs}/screenshots/readme/dashboard-light.png +0 -0
  433. /package/{docs → core/workspace-app/docs}/screenshots/readme/dev-dark.png +0 -0
  434. /package/{docs → core/workspace-app/docs}/screenshots/readme/dev-light.png +0 -0
  435. /package/{docs → core/workspace-app/docs}/screenshots/readme/docs-dark.png +0 -0
  436. /package/{docs → core/workspace-app/docs}/screenshots/readme/docs-light.png +0 -0
  437. /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/ai.svg +0 -0
  438. /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/code.svg +0 -0
  439. /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/docs.svg +0 -0
  440. /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/services.svg +0 -0
  441. /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/sites.svg +0 -0
  442. /package/{docs → core/workspace-app/docs}/screenshots/readme/test-report-dark.png +0 -0
  443. /package/{docs → core/workspace-app/docs}/screenshots/readme/test-report-light.png +0 -0
  444. /package/{docs → core/workspace-app/docs}/screenshots/readme/test-scenarios.png +0 -0
  445. /package/{docs → core/workspace-app/docs}/screenshots/readme/test-steps.png +0 -0
  446. /package/{docs → core/workspace-app/docs}/screenshots/readme/videos-dark.png +0 -0
  447. /package/{docs → core/workspace-app/docs}/screenshots/readme/videos-light.png +0 -0
  448. /package/{docs → core/workspace-app/docs}/screenshots/screens-menu.png +0 -0
  449. /package/{docs → core/workspace-app/docs}/screenshots/screens-new.png +0 -0
  450. /package/{docs → core/workspace-app/docs}/screenshots/settings/auth-google-lists.png +0 -0
  451. /package/{docs → core/workspace-app/docs}/screenshots/settings/delete-1-row.png +0 -0
  452. /package/{docs → core/workspace-app/docs}/screenshots/settings/delete-2-confirm.png +0 -0
  453. /package/{docs → core/workspace-app/docs}/screenshots/settings/delete-3-type-name.png +0 -0
  454. /package/{docs → core/workspace-app/docs}/screenshots/settings/delete-4-type-yes.png +0 -0
  455. /package/{event-log.js → core/workspace-app/event-log.js} +0 -0
  456. /package/{files-browser.js → core/workspace-app/files-browser.js} +0 -0
  457. /package/{folders.js → core/workspace-app/folders.js} +0 -0
  458. /package/{format.js → core/workspace-app/format.js} +0 -0
  459. /package/{github-auth.js → core/workspace-app/github-auth.js} +0 -0
  460. /package/{html.js → core/workspace-app/html.js} +0 -0
  461. /package/{icons.js → core/workspace-app/icons.js} +0 -0
  462. /package/{images-ai.js → core/workspace-app/images-ai.js} +0 -0
  463. /package/{images-create.js → core/workspace-app/images-create.js} +0 -0
  464. /package/{images-page.js → core/workspace-app/images-page.js} +0 -0
  465. /package/{images-records.js → core/workspace-app/images-records.js} +0 -0
  466. /package/{images-render.js → core/workspace-app/images-render.js} +0 -0
  467. /package/{images-svg-core.js → core/workspace-app/images-svg-core.js} +0 -0
  468. /package/{images-svg-sanitize.js → core/workspace-app/images-svg-sanitize.js} +0 -0
  469. /package/{images-svg.js → core/workspace-app/images-svg.js} +0 -0
  470. /package/{images.js → core/workspace-app/images.js} +0 -0
  471. /package/{jobs.js → core/workspace-app/jobs.js} +0 -0
  472. /package/{linger.js → core/workspace-app/linger.js} +0 -0
  473. /package/{lists.js → core/workspace-app/lists.js} +0 -0
  474. /package/{media-info.js → core/workspace-app/media-info.js} +0 -0
  475. /package/{navigation-page.js → core/workspace-app/navigation-page.js} +0 -0
  476. /package/{navigation.js → core/workspace-app/navigation.js} +0 -0
  477. /package/{notes.js → core/workspace-app/notes.js} +0 -0
  478. /package/{palette-index.js → core/workspace-app/palette-index.js} +0 -0
  479. /package/{playwright.config.js → core/workspace-app/playwright.config.js} +0 -0
  480. /package/{plugin-install.js → core/workspace-app/plugin-install.js} +0 -0
  481. /package/{plugin-loader.js → core/workspace-app/plugin-loader.js} +0 -0
  482. /package/{plugin-manifest.js → core/workspace-app/plugin-manifest.js} +0 -0
  483. /package/{plugin-settings.js → core/workspace-app/plugin-settings.js} +0 -0
  484. /package/{plugins → core/workspace-app/plugins}/workspace-notes/index.js +0 -0
  485. /package/{project-make.js → core/workspace-app/project-make.js} +0 -0
  486. /package/{project-name.js → core/workspace-app/project-name.js} +0 -0
  487. /package/{public → core/workspace-app/public}/assistant-avatar-moves.js +0 -0
  488. /package/{public → core/workspace-app/public}/assistant-avatar-ui.js +0 -0
  489. /package/{public → core/workspace-app/public}/assistant-avatar.js +0 -0
  490. /package/{public → core/workspace-app/public}/avatar/druplicon.vrm +0 -0
  491. /package/{public → core/workspace-app/public}/css/base.css +0 -0
  492. /package/{public → core/workspace-app/public}/css/components/ai-assistant.css +0 -0
  493. /package/{public → core/workspace-app/public}/css/components/assistant-avatar.css +0 -0
  494. /package/{public → core/workspace-app/public}/css/components/assistant-views.css +0 -0
  495. /package/{public → core/workspace-app/public}/css/components/build-order.css +0 -0
  496. /package/{public → core/workspace-app/public}/css/components/clone-as-a-git-worktree.css +0 -0
  497. /package/{public → core/workspace-app/public}/css/components/command-palette.css +0 -0
  498. /package/{public → core/workspace-app/public}/css/components/commands-center.css +0 -0
  499. /package/{public → core/workspace-app/public}/css/components/confirm-dialog.css +0 -0
  500. /package/{public → core/workspace-app/public}/css/components/create-card.css +0 -0
  501. /package/{public → core/workspace-app/public}/css/components/ddev-menu.css +0 -0
  502. /package/{public → core/workspace-app/public}/css/components/deep-chat.css +0 -0
  503. /package/{public → core/workspace-app/public}/css/components/density.css +0 -0
  504. /package/{public → core/workspace-app/public}/css/components/floating-assistant-workspace-pages.css +0 -0
  505. /package/{public → core/workspace-app/public}/css/components/folders.css +0 -0
  506. /package/{public → core/workspace-app/public}/css/components/full-height-left-assistant-sidebar-all-pages.css +0 -0
  507. /package/{public → core/workspace-app/public}/css/components/full-height-right-actions-rail-pages-that-have-actions.css +0 -0
  508. /package/{public → core/workspace-app/public}/css/components/grid-and-list.css +0 -0
  509. /package/{public → core/workspace-app/public}/css/components/help-page.css +0 -0
  510. /package/{public → core/workspace-app/public}/css/components/hero.css +0 -0
  511. /package/{public → core/workspace-app/public}/css/components/images.css +0 -0
  512. /package/{public → core/workspace-app/public}/css/components/layout-rhythm-and-spacing-polish.css +0 -0
  513. /package/{public → core/workspace-app/public}/css/components/mic-on-any-field-that-wants-typing.css +0 -0
  514. /package/{public → core/workspace-app/public}/css/components/modal-fit.css +0 -0
  515. /package/{public → core/workspace-app/public}/css/components/page-width.css +0 -0
  516. /package/{public → core/workspace-app/public}/css/components/plugins-page.css +0 -0
  517. /package/{public → core/workspace-app/public}/css/components/project-cards.css +0 -0
  518. /package/{public → core/workspace-app/public}/css/components/project-rows.css +0 -0
  519. /package/{public → core/workspace-app/public}/css/components/reordering-the-workspace-cards.css +0 -0
  520. /package/{public → core/workspace-app/public}/css/components/responsive-home-and-the-workspace-pages.css +0 -0
  521. /package/{public → core/workspace-app/public}/css/components/running-commands-as-toasts.css +0 -0
  522. /package/{public → core/workspace-app/public}/css/components/settings-editor-core-config-yml.css +0 -0
  523. /package/{public → core/workspace-app/public}/css/components/short-results-as-a-toast-at-the-top.css +0 -0
  524. /package/{public → core/workspace-app/public}/css/components/sound-player.css +0 -0
  525. /package/{public → core/workspace-app/public}/css/components/suggest-agents-skills-commands.css +0 -0
  526. /package/{public → core/workspace-app/public}/css/components/tab-modal.css +0 -0
  527. /package/{public → core/workspace-app/public}/css/components/terminals.css +0 -0
  528. /package/{public → core/workspace-app/public}/css/components/test-report-media-one-file-per-row-paged.css +0 -0
  529. /package/{public → core/workspace-app/public}/css/components/top-toolbar-logo-breadcrumb.css +0 -0
  530. /package/{public → core/workspace-app/public}/css/components/video-edit.css +0 -0
  531. /package/{public → core/workspace-app/public}/css/components/web-ide-open-with.css +0 -0
  532. /package/{public → core/workspace-app/public}/css/components/working-indicators.css +0 -0
  533. /package/{public → core/workspace-app/public}/css/components/workspace-cards.css +0 -0
  534. /package/{public → core/workspace-app/public}/favicon.ico +0 -0
  535. /package/{public → core/workspace-app/public}/favicon.png +0 -0
  536. /package/{public → core/workspace-app/public}/htmx.min.js +0 -0
  537. /package/{public → core/workspace-app/public}/logo.png +0 -0
  538. /package/{public → core/workspace-app/public}/media-player.js +0 -0
  539. /package/{public → core/workspace-app/public}/mic.js +0 -0
  540. /package/{public → core/workspace-app/public}/narration.js +0 -0
  541. /package/{public → core/workspace-app/public}/palette.js +0 -0
  542. /package/{public → core/workspace-app/public}/sortable.js +0 -0
  543. /package/{public → core/workspace-app/public}/suggest.js +0 -0
  544. /package/{public → core/workspace-app/public}/terminal.js +0 -0
  545. /package/{public → core/workspace-app/public}/themes/default/theme.css +0 -0
  546. /package/{public → core/workspace-app/public}/themes/ember/theme.css +0 -0
  547. /package/{public → core/workspace-app/public}/themes/press/theme.css +0 -0
  548. /package/{public → core/workspace-app/public}/themes/slate/theme.css +0 -0
  549. /package/{public → core/workspace-app/public}/touch-icon.png +0 -0
  550. /package/{public → core/workspace-app/public}/ui.js +0 -0
  551. /package/{public → core/workspace-app/public}/vendor/ace/LICENSE.txt +0 -0
  552. /package/{public → core/workspace-app/public}/vendor/ace/ace.js +0 -0
  553. /package/{public → core/workspace-app/public}/vendor/ace/ext-language_tools.js +0 -0
  554. /package/{public → core/workspace-app/public}/vendor/ace/ext-searchbox.js +0 -0
  555. /package/{public → core/workspace-app/public}/vendor/ace/mode-css.js +0 -0
  556. /package/{public → core/workspace-app/public}/vendor/ace/mode-html.js +0 -0
  557. /package/{public → core/workspace-app/public}/vendor/ace/mode-javascript.js +0 -0
  558. /package/{public → core/workspace-app/public}/vendor/ace/mode-markdown.js +0 -0
  559. /package/{public → core/workspace-app/public}/vendor/ace/mode-sh.js +0 -0
  560. /package/{public → core/workspace-app/public}/vendor/ace/mode-yaml.js +0 -0
  561. /package/{public → core/workspace-app/public}/vendor/ace/theme-textmate.js +0 -0
  562. /package/{public → core/workspace-app/public}/vendor/ace/theme-tomorrow_night.js +0 -0
  563. /package/{public → core/workspace-app/public}/vendor/ace/worker-yaml.js +0 -0
  564. /package/{public → core/workspace-app/public}/vendor/deep-chat.LICENSE +0 -0
  565. /package/{public → core/workspace-app/public}/vendor/deep-chat.bundle.js +0 -0
  566. /package/{public → core/workspace-app/public}/vendor/fonts/dm-serif-text-LICENSE.txt +0 -0
  567. /package/{public → core/workspace-app/public}/vendor/fonts/dm-serif-text-latin-400-italic.woff2 +0 -0
  568. /package/{public → core/workspace-app/public}/vendor/fonts/dm-serif-text-latin-400-normal.woff2 +0 -0
  569. /package/{public → core/workspace-app/public}/vendor/fonts/figtree-LICENSE.txt +0 -0
  570. /package/{public → core/workspace-app/public}/vendor/fonts/figtree-latin-400-italic.woff2 +0 -0
  571. /package/{public → core/workspace-app/public}/vendor/fonts/figtree-latin-400-normal.woff2 +0 -0
  572. /package/{public → core/workspace-app/public}/vendor/fonts/figtree-latin-600-italic.woff2 +0 -0
  573. /package/{public → core/workspace-app/public}/vendor/fonts/figtree-latin-600-normal.woff2 +0 -0
  574. /package/{public → core/workspace-app/public}/vendor/fonts/jetbrains-mono-LICENSE.txt +0 -0
  575. /package/{public → core/workspace-app/public}/vendor/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
  576. /package/{public → core/workspace-app/public}/vendor/fonts/ubuntu-mono-LICENSE.txt +0 -0
  577. /package/{public → core/workspace-app/public}/vendor/fonts/ubuntu-mono-latin-wght-normal.woff2 +0 -0
  578. /package/{public → core/workspace-app/public}/vendor/tabler-LICENSE.txt +0 -0
  579. /package/{public → core/workspace-app/public}/vendor/tabler-sprite.svg +0 -0
  580. /package/{public → core/workspace-app/public}/vendor/three/LICENSE +0 -0
  581. /package/{public → core/workspace-app/public}/vendor/three/addons/loaders/GLTFLoader.js +0 -0
  582. /package/{public → core/workspace-app/public}/vendor/three/addons/utils/BufferGeometryUtils.js +0 -0
  583. /package/{public → core/workspace-app/public}/vendor/three/addons/utils/SkeletonUtils.js +0 -0
  584. /package/{public → core/workspace-app/public}/vendor/three/three.core.min.js +0 -0
  585. /package/{public → core/workspace-app/public}/vendor/three/three.module.min.js +0 -0
  586. /package/{public → core/workspace-app/public}/vendor/three-vrm/LICENSE +0 -0
  587. /package/{public → core/workspace-app/public}/vendor/three-vrm/three-vrm.module.min.js +0 -0
  588. /package/{public → core/workspace-app/public}/vendor/uikit-icons.min.js +0 -0
  589. /package/{public → core/workspace-app/public}/vendor/uikit.min.css +0 -0
  590. /package/{public → core/workspace-app/public}/vendor/uikit.min.js +0 -0
  591. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/bookmark-outlined.svg +0 -0
  592. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/headset.svg +0 -0
  593. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/menu.svg +0 -0
  594. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/pause.svg +0 -0
  595. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/play.svg +0 -0
  596. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/share.svg +0 -0
  597. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/sound-off.svg +0 -0
  598. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/sound-on.svg +0 -0
  599. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/stop.svg +0 -0
  600. /package/{public → core/workspace-app/public}/vendor/xterm/addon-fit.js +0 -0
  601. /package/{public → core/workspace-app/public}/vendor/xterm/addon-unicode11.js +0 -0
  602. /package/{public → core/workspace-app/public}/vendor/xterm/addon-web-links.js +0 -0
  603. /package/{public → core/workspace-app/public}/vendor/xterm/xterm.css +0 -0
  604. /package/{public → core/workspace-app/public}/vendor/xterm/xterm.js +0 -0
  605. /package/{rag.js → core/workspace-app/rag.js} +0 -0
  606. /package/{reader.js → core/workspace-app/reader.js} +0 -0
  607. /package/{repo-blobs.js → core/workspace-app/repo-blobs.js} +0 -0
  608. /package/{report-pdf.js → core/workspace-app/report-pdf.js} +0 -0
  609. /package/{row-layout.js → core/workspace-app/row-layout.js} +0 -0
  610. /package/{screenrc → core/workspace-app/screenrc} +0 -0
  611. /package/{screens.js → core/workspace-app/screens.js} +0 -0
  612. /package/{scripts → core/workspace-app/scripts}/code-server-start.sh +0 -0
  613. /package/{scripts → core/workspace-app/scripts}/images-claude-finish.js +0 -0
  614. /package/{scripts → core/workspace-app/scripts}/screen-host.sh +0 -0
  615. /package/{scripts → core/workspace-app/scripts}/screen-runner.js +0 -0
  616. /package/{scripts → core/workspace-app/scripts}/screen-then.js +0 -0
  617. /package/{scripts → core/workspace-app/scripts}/update.sh +0 -0
  618. /package/{settings-file.js → core/workspace-app/settings-file.js} +0 -0
  619. /package/{speech.js → core/workspace-app/speech.js} +0 -0
  620. /package/{tab-modal.js → core/workspace-app/tab-modal.js} +0 -0
  621. /package/{test-reports.js → core/workspace-app/test-reports.js} +0 -0
  622. /package/{themes.js → core/workspace-app/themes.js} +0 -0
  623. /package/{transcripts.js → core/workspace-app/transcripts.js} +0 -0
  624. /package/{urls.js → core/workspace-app/urls.js} +0 -0
  625. /package/{validate.js → core/workspace-app/validate.js} +0 -0
  626. /package/{version-check.js → core/workspace-app/version-check.js} +0 -0
  627. /package/{video-edit.js → core/workspace-app/video-edit.js} +0 -0
  628. /package/{waiting.js → core/workspace-app/waiting.js} +0 -0
  629. /package/{workspace-card-tools.js → core/workspace-app/workspace-card-tools.js} +0 -0
  630. /package/{workspace-delete.js → core/workspace-app/workspace-delete.js} +0 -0
  631. /package/{workspace-types.js → core/workspace-app/workspace-types.js} +0 -0
  632. /package/{workspaces.js → core/workspace-app/workspaces.js} +0 -0
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # AI provider keys for the sites this workspace builds.
4
+ #
5
+ # settings.yml carries an `ai_providers:` map (openai, anthropic, gemini, …)
6
+ # that parse_yaml flattens into $ai_providers_<provider>_api_key. The committed
7
+ # file ships placeholders; a developer's real keys live only in their own
8
+ # checkout. These helpers push those keys into an installed Drupal site that
9
+ # carries the AI stack (Drupal CMS AI, Webship AI), so "build me a Drupal CMS
10
+ # site with AI working" is one command instead of console clicking.
11
+
12
+ # A value counts as configured only when it is non-empty and not a placeholder.
13
+ function ai_provider_key_is_real() {
14
+ case "$1" in
15
+ ''|CHANGE_ME|sk-change-this) return 1 ;;
16
+ *) return 0 ;;
17
+ esac
18
+ }
19
+
20
+ # Store one provider key on the installed site and point the AI provider
21
+ # module at it. Uses the Key module (how Drupal CMS AI stores secrets):
22
+ # a `key` config entity holds the value, and ai_provider_<name>.settings
23
+ # references it by id. Runs inside the project's DDEV containers only.
24
+ #
25
+ # set_one_ai_provider_key <provider> <api_key>
26
+ function set_one_ai_provider_key() {
27
+ local provider="$1";
28
+ local api_key="$2";
29
+ local module="ai_provider_${provider}";
30
+
31
+ if ! ddev drush pm:list --field=name --status=enabled 2>/dev/null | grep -qx "${module}" ; then
32
+ echo " ${module} is not enabled — skipping the ${provider} key." ;
33
+ return 0 ;
34
+ fi
35
+
36
+ # The Key entity. The value travels on STDIN, so it never appears in a
37
+ # command line, shell history, or process list.
38
+ printf '%s' "${api_key}" | ddev drush php:eval "
39
+ \$value = trim(file_get_contents('php://stdin'));
40
+ \$storage = \Drupal::entityTypeManager()->getStorage('key');
41
+ \$id = '${provider}_api_key';
42
+ \$key = \$storage->load(\$id) ?: \$storage->create(['id' => \$id, 'label' => '${provider} API key', 'key_type' => 'authentication']);
43
+ \$key->set('key_provider', 'config');
44
+ \$key->set('key_provider_settings', ['key_value' => \$value]);
45
+ \$key->save();
46
+ " || return 1 ;
47
+
48
+ ddev drush -y config:set "${module}.settings" api_key "${provider}_api_key" || return 1 ;
49
+ echo " ${provider}: key stored and wired to ${module}." ;
50
+ }
51
+
52
+ # Wire every configured provider into the current project's site. Call it
53
+ # after install, from the project directory (like enable_*_extra_components).
54
+ # Quietly does nothing when the site has no AI stack or no real keys are set.
55
+ function set_ai_provider_keys() {
56
+ if ! ddev drush pm:list --field=name --status=enabled 2>/dev/null | grep -qx "key" ; then
57
+ echo "No Key module on this site — skipping AI provider keys." ;
58
+ return 0 ;
59
+ fi
60
+
61
+ echo "Wiring AI provider keys from settings.yml (placeholders are skipped).";
62
+
63
+ if ai_provider_key_is_real "${ai_providers_openai_api_key}" ; then
64
+ set_one_ai_provider_key "openai" "${ai_providers_openai_api_key}" ;
65
+ fi
66
+ if ai_provider_key_is_real "${ai_providers_anthropic_api_key}" ; then
67
+ set_one_ai_provider_key "anthropic" "${ai_providers_anthropic_api_key}" ;
68
+ fi
69
+ if ai_provider_key_is_real "${ai_providers_gemini_api_key}" ; then
70
+ set_one_ai_provider_key "gemini" "${ai_providers_gemini_api_key}" ;
71
+ fi
72
+ }
@@ -0,0 +1,429 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # The dashboard's own hostname, and getting it back after a reboot.
4
+ #
5
+ # Two problems that look like one. A DDEV project's containers carry `restart: no`, so nothing brings
6
+ # the dashboard back when the machine boots or when Docker restarts — `ddev describe` says `exited`
7
+ # and every workspace URL answers 404 from a router with nothing behind it. And a project answers only
8
+ # on the names DDEV knows about, so a name of your own choosing has to be given to it.
9
+ #
10
+ # WHY NOT A DOCKER RESTART POLICY. Setting `restart: unless-stopped` on the containers looks like the
11
+ # smaller fix and is the wrong one: DDEV rebuilds its containers on `ddev start` and pushes fresh
12
+ # router config each time, so a container resurrected behind DDEV's back comes up without a router
13
+ # entry and answers nothing. The supported way in is `ddev start`, so that is what runs at boot.
14
+ #
15
+ # A **user** service, not a system one. The projects live under $HOME, the Docker socket is reached
16
+ # with this user's group membership, and `ddev` reads ~/.ddev. A root unit would start them as the
17
+ # wrong user and strand every volume. Lingering (`loginctl enable-linger`) is what makes a user
18
+ # service run at BOOT rather than at first login — without it the dashboard would only appear once
19
+ # somebody signs in, which defeats the point.
20
+
21
+ readonly AUTOSTART_UNIT_NAME="webship-workspace.service";
22
+ readonly AUTOSTART_UNIT_DIR="${HOME}/.config/systemd/user";
23
+ readonly AUTOSTART_RUNNER="${WORKSPACE_SCRIPTS}/autostart-run.sh";
24
+ readonly AUTOSTART_RECORDER="${WORKSPACE_SCRIPTS}/autostart-stop.sh";
25
+ readonly AUTOSTART_WATCHER="${WORKSPACE_SCRIPTS}/autostart-watch.sh";
26
+ readonly AUTOSTART_WATCH_NAME="webship-workspace-watch";
27
+
28
+ # The dashboard's own DDEV project. Everything else in the tree is a site; this one is the way in.
29
+ function autostart_app_path() {
30
+ printf '%s' "${WORKSPACE_PATH}/workspace-app";
31
+ }
32
+
33
+ # Which projects the boot service starts, resolved. Asks the runner rather than re-deriving it: the
34
+ # setting is a small vocabulary (`workspace`, `running`, `all`, or literal names) and a second copy of
35
+ # that logic here would drift from the one that actually runs at boot.
36
+ function autostart_projects() {
37
+ bash "${AUTOSTART_RUNNER}" --list 2>/dev/null ;
38
+ }
39
+
40
+ # The one name the dashboard answers on, as `<project>.<tld>`.
41
+ #
42
+ # `hub_domain`, the key the app ALREADY uses — workspaces.js reads it for every URL it builds, and
43
+ # urls.js derives the whole scheme from it. A second key here would be the same fact stored twice:
44
+ # set one, and the dashboard's links and DDEV's routing would disagree about where the site is. So
45
+ # this command reads and writes that one, and its job is to make DDEV agree with it.
46
+ function autostart_domain() {
47
+ printf '%s' "${hub_domain:-workspace.ddev.site}";
48
+ }
49
+
50
+ # Rewrite one `key: value` line in the dashboard block of the settings file, in place.
51
+ #
52
+ # In place, and only that line: this file holds the real account password and the API keys on a live
53
+ # machine, it is never synced or committed, and a regenerating writer would reformat around secrets it
54
+ # has no business touching. One sed on one key is the whole edit.
55
+ function autostart_settings_write() {
56
+ local key="$1" value="$2" file="${WORKSPACE_CONFIG}/settings.yml";
57
+ [ -f "${file}" ] || { echo "No ${file}."; return 1 ; }
58
+
59
+ # Two shapes, because the two keys live at different levels: hub_domain is top-level (the app has
60
+ # always read it there), autostart is nested under dashboard:.
61
+ #
62
+ # Written UNQUOTED and at two spaces, because that is what every other value in this file does.
63
+ # parse_yaml strips quotes either way, so quoting was never a correctness question — it just left
64
+ # one block looking unlike the file it lives in, which is how a hand-edit later gets it wrong.
65
+ case "${key}" in
66
+ hub_domain)
67
+ if grep -qE "^${key}:" "${file}" ; then
68
+ # `#` as the sed delimiter: `|` and `/` both appear in things people paste here.
69
+ sed -i -E "s#^${key}:.*#${key}: ${value}#" "${file}" ;
70
+ else
71
+ printf '\n# The base domain every dashboard URL is built from, and what DDEV routes.\n# Kept in step with DDEV by core/scripts/autostart.sh.\n%s: %s\n' "${key}" "${value}" >> "${file}" ;
72
+ fi
73
+ ;;
74
+ *)
75
+ if grep -qE "^ ${key}:" "${file}" ; then
76
+ sed -i -E "s#^ ${key}:.*# ${key}: ${value}#" "${file}" ;
77
+ elif grep -qE '^dashboard:' "${file}" ; then
78
+ sed -i -E "/^dashboard:/a\\ ${key}: ${value}" "${file}" ;
79
+ else
80
+ printf '\ndashboard:\n %s: %s\n' "${key}" "${value}" >> "${file}" ;
81
+ fi
82
+ ;;
83
+ esac
84
+ }
85
+
86
+ # Does this TLD answer for names nobody has put in /etc/hosts?
87
+ #
88
+ # This is the whole question, and it is why the default is ddev.site rather than something branded.
89
+ # `*.ddev.site` is wildcard DNS pointing at 127.0.0.1 AT ANY DEPTH — `x.dev.workspace.ddev.site`
90
+ # resolves with an empty hosts file — which is the only reason this tree's nested scheme
91
+ # (<project>.<workspace>.workspace.<tld>) works at all.
92
+ #
93
+ # A TLD without that has to be written into /etc/hosts, and **/etc/hosts cannot hold wildcards**. Every
94
+ # nested name would need its own line, DDEV would ask for sudo on every start, and each new project
95
+ # would silently 404 until someone added it by hand. So a TLD that does not resolve is refused rather
96
+ # than half-applied, and --force is there for the case where you run your own wildcard DNS.
97
+ function autostart_tld_resolves() {
98
+ local tld="$1" probe ;
99
+ probe="ddev-probe-$$.${tld}";
100
+ getent hosts "${probe}" > /dev/null 2>&1 ;
101
+ }
102
+
103
+ # Hand the name to DDEV rather than editing any config.yaml. `ddev config global --project-tld` is the
104
+ # supported writer and it sets the DEFAULT FOR EVERY PROJECT — the dashboard, every workspace page
105
+ # under it, and each project's own <project>.<tld> — which is what makes this one field rather than
106
+ # forty. Hand-editing that YAML is how a project stops parsing.
107
+ function autostart_apply_domain() {
108
+ local app ; app="$(autostart_app_path)";
109
+ local domain ; domain="$(autostart_domain)";
110
+ local want_name want_tld current_name ;
111
+
112
+ [ -d "${app}/.ddev" ] || { echo "No DDEV project at ${app}."; return 1 ; }
113
+
114
+ want_name="${domain%%.*}";
115
+ want_tld="${domain#*.}";
116
+ current_name="$(grep -E '^name:' "${app}/.ddev/config.yaml" | head -1 | sed -E 's/^name:[[:space:]]*//')";
117
+
118
+ if [ -z "${want_tld}" ] || [ "${want_tld}" == "${domain}" ] ; then
119
+ echo " '${domain}' has no TLD — it should look like workspace.ddev.site." ;
120
+ return 1 ;
121
+ fi
122
+
123
+ # A rename is not a domain change: DDEV keys its volumes, containers and router entries on the
124
+ # project name, so renaming strands the old database volume and builds a fresh empty one.
125
+ if [ "${want_name}" != "${current_name}" ] ; then
126
+ if [ "${FORCE}" == 'yes' ] ; then
127
+ echo " renaming the project ${current_name} -> ${want_name} (--force)." ;
128
+ echo " the old volumes stay behind; find them later with cmd-tools-reclaim.sh." ;
129
+ else
130
+ echo " '${domain}' would rename the project ${current_name} -> ${want_name}." ;
131
+ echo " That strands its database volume and builds an empty one, so it is refused here." ;
132
+ echo " Keep the first label as '${current_name}', or pass --force if you meant it." ;
133
+ return 1 ;
134
+ fi
135
+ fi
136
+
137
+ if ! autostart_tld_resolves "${want_tld}" ; then
138
+ if [ "${FORCE}" == 'yes' ] ; then
139
+ echo " '${want_tld}' has no wildcard DNS — going ahead anyway (--force)." ;
140
+ echo " DDEV will ask for sudo and write /etc/hosts, and nested names will NOT work." ;
141
+ else
142
+ echo " '${want_tld}' does not resolve: a name under it answers nothing." ;
143
+ echo "" ;
144
+ echo " ddev.site is wildcard DNS to 127.0.0.1 at any depth, which is what makes" ;
145
+ echo " <project>.<workspace>.workspace.<tld> work with an empty /etc/hosts." ;
146
+ echo " /etc/hosts cannot hold wildcards, so a TLD without its own DNS needs one line" ;
147
+ echo " per hostname, added by hand, with sudo, forever." ;
148
+ echo "" ;
149
+ echo " Point *.${want_tld} at 127.0.0.1 in your DNS first, then pass --force." ;
150
+ return 1 ;
151
+ fi
152
+ fi
153
+
154
+ echo " applying: ${want_name}.${want_tld} (project '${want_name}', TLD '${want_tld}')" ;
155
+
156
+ # GLOBAL, not per-project, and that is the whole point of setting it here.
157
+ #
158
+ # Every project in this tree inherits `project-tld` from DDEV's global config — none of them carry
159
+ # their own — so one global setting moves the dashboard, every workspace page under it, and every
160
+ # project's canonical <project>.<tld> together. A per-project override on the dashboard instead
161
+ # would move workspace.<tld> and leave all 40-odd sites answering on ddev.site, with the dashboard's
162
+ # own Launch buttons pointing at hostnames that no longer exist.
163
+ ddev config global --project-tld="${want_tld}" > /dev/null || return 1 ;
164
+
165
+ # A per-project override beats the global one, so name the projects that will not follow rather
166
+ # than leaving someone to wonder why their site did not move.
167
+ local overrides ;
168
+ overrides="$(find "${WORKSPACE_ROOT}" -mindepth 2 -maxdepth 4 -name config.yaml -path '*/.ddev/*' 2>/dev/null \
169
+ | xargs grep -l '^project_tld' 2>/dev/null | sed 's|/.ddev/config.yaml||; s|.*/||' | paste -sd' ' -)";
170
+ [ -n "${overrides}" ] && echo " note: these set their own project_tld and will NOT follow: ${overrides}" ;
171
+
172
+ # Laptop or hub. On ddev.site the router binds 127.0.0.1 and that is correct — the sites are for
173
+ # the person sitting here. A real TLD means this is a shared workspace on a Docker host or a
174
+ # dedicated server, where binding to loopback makes it reachable by nobody: the port is open, the
175
+ # DNS resolves, and every request from outside the box times out with nothing in any log. So the
176
+ # bind is widened to match, and said out loud, because it is the one change here that exposes
177
+ # something to the network.
178
+ if [ "${want_tld}" != 'ddev.site' ] ; then
179
+ if [ "$(ddev config global 2>/dev/null | grep -c 'router-bind-all-interfaces=true')" -eq 0 ] ; then
180
+ echo "" ;
181
+ echo " '${want_tld}' is a real domain, so this is a shared hub rather than a laptop." ;
182
+ echo " The router is bound to 127.0.0.1 and nothing outside this machine can reach it." ;
183
+ if [ "${FORCE}" == 'yes' ] ; then
184
+ ddev config global --router-bind-all-interfaces=true > /dev/null 2>&1 \
185
+ && echo " opened the router to all interfaces (--force)." ;
186
+ echo " EVERY DDEV project on this machine is now reachable from the network." ;
187
+ echo " Put it behind a firewall or a reverse proxy, and remember the sites are dev sites." ;
188
+ else
189
+ echo " Open it deliberately, once you have a firewall in front:" ;
190
+ echo " ddev config global --router-bind-all-interfaces=true" ;
191
+ echo " (or re-run this with --force). Left closed for now — the domain change still applied." ;
192
+ fi
193
+ echo "" ;
194
+ fi
195
+ fi
196
+
197
+ # A TLD only reaches the router after a restart — router config is pushed on start.
198
+ echo " restarting the dashboard so the router learns it…" ;
199
+ ( cd "${app}" && ddev restart > /dev/null 2>&1 ) || {
200
+ echo " ddev restart failed. If it asked for a sudo password, run it yourself:" ;
201
+ echo " cd ${app} && ddev restart" ;
202
+ return 1 ;
203
+ }
204
+
205
+ # Every other running project still answers on the OLD name until it restarts too: its containers
206
+ # were started against the previous TLD and the router holds no entry for the new one. Listed
207
+ # rather than done silently — restarting a site mid-build is how a half-written database happens,
208
+ # and there can be forty of them.
209
+ local stale ;
210
+ stale="$(ddev list --json-output 2>/dev/null \
211
+ | jq -r --arg n "${want_name}" '.raw[]? | select(.status=="running") | select(.name != $n) | .name' 2>/dev/null \
212
+ | paste -sd' ' -)";
213
+ if [ -n "${stale}" ] ; then
214
+ echo "" ;
215
+ echo " Running, and still on the old TLD until they restart:" ;
216
+ printf ' %s\n' ${stale} ;
217
+ if [ "${RESTART_ALL}" == 'yes' ] ; then
218
+ echo " restarting them (--restart-all)…" ;
219
+ local one root ;
220
+ for one in ${stale} ; do
221
+ root="$(ddev list --json-output 2>/dev/null \
222
+ | jq -r --arg n "${one}" '.raw[]? | select(.name==$n) | .approot' 2>/dev/null | head -1)";
223
+ [ -d "${root}" ] || continue ;
224
+ ( cd "${root}" && ddev restart > /dev/null 2>&1 ) \
225
+ && echo " restarted ${one}" || echo " FAILED ${one}" ;
226
+ done
227
+ else
228
+ echo " Restart them when nothing is mid-build:" ;
229
+ echo " bash core/scripts/autostart.sh --apply --restart-all" ;
230
+ fi
231
+ fi
232
+ return 0 ;
233
+ }
234
+
235
+ function autostart_write_unit() {
236
+ mkdir -p "${AUTOSTART_UNIT_DIR}" ;
237
+
238
+ # Type=oneshot + RemainAfterExit: `ddev start` returns when the containers are up, and the unit
239
+ # should then read as active rather than dead. The long timeout is not padding — a first start after
240
+ # an update rebuilds images, and systemd killing that halfway leaves a broken project.
241
+ cat > "${AUTOSTART_UNIT_DIR}/${AUTOSTART_UNIT_NAME}" <<UNITEOF
242
+ [Unit]
243
+ Description=Webship Workspace — start the dashboard and its projects
244
+ Documentation=https://github.com/webship/workspace
245
+ After=network-online.target
246
+ Wants=network-online.target
247
+ # The window StartLimitBurst counts failures IN, and it belongs HERE in [Unit] — systemd accepts
248
+ # StartLimitBurst under [Service] too for compatibility, but silently ignored StartLimitIntervalSec
249
+ # there ("Unknown key name", ddev-workspace ran on the default 10s window for a day before this was
250
+ # caught). The window has to be wider than RestartSec * StartLimitBurst below, or the burst limit
251
+ # can never be reached and this restarts forever: 3 tries at 20s apart span 60s, so 150s leaves room
252
+ # for that plus the first attempt's own startup time. Below this, a genuinely broken start (bad
253
+ # config, a Docker daemon that never recovers) retries forever rather than failing after three
254
+ # tries — silently rebuilding project images on every attempt, which is the same failure shape as
255
+ # the disk filling up from unrelated builds.
256
+ StartLimitIntervalSec=150
257
+ StartLimitBurst=3
258
+
259
+ [Service]
260
+ Type=oneshot
261
+ RemainAfterExit=yes
262
+ ExecStart=/bin/bash ${AUTOSTART_RUNNER}
263
+ # Records what was running, so 'autostart: running' has something to restore. RemainAfterExit means
264
+ # this fires on shutdown and reboot rather than the instant ExecStart returns.
265
+ ExecStop=/bin/bash ${AUTOSTART_RECORDER}
266
+ TimeoutStartSec=900
267
+ TimeoutStopSec=60
268
+ # Docker may not be accepting connections the instant the user session starts.
269
+ Restart=on-failure
270
+ RestartSec=20
271
+
272
+ [Install]
273
+ WantedBy=default.target
274
+ UNITEOF
275
+ echo " wrote ${AUTOSTART_UNIT_DIR}/${AUTOSTART_UNIT_NAME}" ;
276
+ }
277
+
278
+ # The boot service runs once. A Docker restart AFTER login stops every container and nothing starts them
279
+ # again; this timer looks every two minutes and runs the same start script when Docker has restarted.
280
+ function autostart_write_watch() {
281
+ mkdir -p "${AUTOSTART_UNIT_DIR}" ;
282
+ cat > "${AUTOSTART_UNIT_DIR}/${AUTOSTART_WATCH_NAME}.service" <<UNITEOF
283
+ [Unit]
284
+ Description=Webship Workspace — start the dashboard again after Docker restarts
285
+ Documentation=https://github.com/webship/workspace
286
+
287
+ [Service]
288
+ Type=oneshot
289
+ ExecStart=/bin/bash ${AUTOSTART_WATCHER}
290
+ TimeoutStartSec=900
291
+ UNITEOF
292
+ cat > "${AUTOSTART_UNIT_DIR}/${AUTOSTART_WATCH_NAME}.timer" <<UNITEOF
293
+ [Unit]
294
+ Description=Webship Workspace — look for a Docker restart every two minutes
295
+
296
+ [Timer]
297
+ OnBootSec=3min
298
+ OnUnitActiveSec=2min
299
+ AccuracySec=15s
300
+
301
+ [Install]
302
+ WantedBy=timers.target
303
+ UNITEOF
304
+ echo " wrote ${AUTOSTART_UNIT_DIR}/${AUTOSTART_WATCH_NAME}.{service,timer}" ;
305
+ }
306
+
307
+ function autostart_enable() {
308
+ command -v systemctl > /dev/null 2>&1 || { echo "No systemctl here — nothing to install."; return 1 ; }
309
+
310
+ autostart_write_unit ;
311
+ systemctl --user daemon-reload ;
312
+ systemctl --user enable "${AUTOSTART_UNIT_NAME}" > /dev/null 2>&1 ;
313
+ autostart_write_watch ;
314
+ systemctl --user daemon-reload ;
315
+ systemctl --user enable --now "${AUTOSTART_WATCH_NAME}.timer" > /dev/null 2>&1 \
316
+ && echo " watching for a Docker restart (every two minutes)" ;
317
+
318
+ # Lingering is the difference between "starts at boot" and "starts when Rajab logs in". Without it
319
+ # the dashboard is missing until someone signs in, which is exactly the problem being solved.
320
+ if command -v loginctl > /dev/null 2>&1 ; then
321
+ if [ "$(loginctl show-user "${USER}" -p Linger --value 2>/dev/null)" != 'yes' ] ; then
322
+ echo " enabling lingering so it starts at BOOT, not at login (needs sudo once)…" ;
323
+ sudo loginctl enable-linger "${USER}" 2>/dev/null \
324
+ && echo " lingering on" \
325
+ || { echo " could not enable lingering — run this once yourself:" ;
326
+ echo " sudo loginctl enable-linger ${USER}" ;
327
+ echo " until then the dashboard starts at login rather than at boot." ; }
328
+ else
329
+ echo " lingering already on" ;
330
+ fi
331
+ fi
332
+ echo " enabled — it will run on the next boot. Prove it now with --now." ;
333
+ }
334
+
335
+ function autostart_disable() {
336
+ command -v systemctl > /dev/null 2>&1 || return 0 ;
337
+ systemctl --user disable --now "${AUTOSTART_UNIT_NAME}" > /dev/null 2>&1 ;
338
+ systemctl --user disable --now "${AUTOSTART_WATCH_NAME}.timer" > /dev/null 2>&1 ;
339
+ rm -f "${AUTOSTART_UNIT_DIR}/${AUTOSTART_UNIT_NAME}" "${AUTOSTART_UNIT_DIR}/${AUTOSTART_WATCH_NAME}.service" "${AUTOSTART_UNIT_DIR}/${AUTOSTART_WATCH_NAME}.timer" ;
340
+ systemctl --user daemon-reload ;
341
+ echo " removed. Projects are not touched; they keep running." ;
342
+ echo " Lingering was left on — turn it off with: sudo loginctl disable-linger ${USER}" ;
343
+ }
344
+
345
+ function autostart_status() {
346
+ local app ; app="$(autostart_app_path)";
347
+ local unit_state linger ;
348
+
349
+ unit_state="$(systemctl --user is-enabled "${AUTOSTART_UNIT_NAME}" 2>/dev/null)";
350
+ case "${unit_state}" in ''|not-found) unit_state='not installed' ;; esac ;
351
+ linger="$(loginctl show-user "${USER}" -p Linger --value 2>/dev/null || echo '?')";
352
+
353
+ echo "" ;
354
+ echo "── Autostart ────────────────────────────────────────────────────────" ;
355
+ printf " boot service : %s\n" "${unit_state}" ;
356
+ printf " lingering : %s%s\n" "${linger}" "$([ "${linger}" == 'yes' ] && echo '' || echo ' (without it, starts at login not at boot)')" ;
357
+ printf " setting : %s\n" "${dashboard_autostart:-workspace}" ;
358
+ printf " will start : %s\n" "$(autostart_projects | paste -sd' ' -)" ;
359
+ local domain tld ; domain="$(autostart_domain)" ; tld="${domain#*.}" ;
360
+ printf " domain : %s\n" "${domain}" ;
361
+ local global_tld ; global_tld="$(ddev config global 2>/dev/null | sed -n 's/^project-tld=//p')" ;
362
+ if [ "${global_tld}" == "${tld}" ] ; then
363
+ printf " DDEV TLD : %s — global, so every project inherits it\n" "${global_tld}" ;
364
+ else
365
+ printf " DDEV TLD : %s in DDEV vs %s in settings — run --apply to line them up\n" "${global_tld:-unset}" "${tld}" ;
366
+ fi
367
+ if autostart_tld_resolves "${tld}" ; then
368
+ printf " TLD DNS : *.%s resolves on its own — no /etc/hosts needed\n" "${tld}" ;
369
+ else
370
+ printf " TLD DNS : *.%s does NOT resolve — every name needs an /etc/hosts line\n" "${tld}" ;
371
+ fi
372
+ if [ "${tld}" == 'ddev.site' ] ; then
373
+ printf " reachable : this machine only (ddev.site, router on 127.0.0.1)\n" ;
374
+ elif ddev config global 2>/dev/null | grep -q 'router-bind-all-interfaces=true' ; then
375
+ printf " reachable : from the network — shared hub, router on all interfaces\n" ;
376
+ else
377
+ printf " reachable : this machine only — a real TLD but the router is still on 127.0.0.1\n" ;
378
+ printf " open it with: ddev config global --router-bind-all-interfaces=true\n" ;
379
+ fi
380
+ echo "" ;
381
+ echo " DDEV sees:" ;
382
+ ( cd "${app}" 2>/dev/null && ddev describe 2>/dev/null \
383
+ | awk -F'│' '/web|Project:/ {gsub(/^[ \t]+|[ \t]+$/,"",$2); if ($2 != "") printf " %s\n", $2}' | head -3 ) \
384
+ || echo " (no project at ${app})" ;
385
+ echo "" ;
386
+ echo " Every URL the dashboard answers on:" ;
387
+ ( cd "${app}" 2>/dev/null && ddev describe -j 2>/dev/null \
388
+ | jq -r '.raw.urls[]?' 2>/dev/null | grep '^https' | sed 's/^/ /' | head -12 ) ;
389
+ echo "" ;
390
+ }
391
+
392
+ function autostart_command() {
393
+ local changed=0 ;
394
+
395
+ if [ ! "${DOMAIN}" == '_none_' ] ; then
396
+ # Hold it in the variable the apply step reads, but do NOT write the file yet — a refused domain
397
+ # must not end up recorded as the truth. The write happens below, only once DDEV took it.
398
+ hub_domain="${DOMAIN}";
399
+ changed=1 ;
400
+ fi
401
+
402
+ [ "${APPLY}" == 'yes' ] && changed=1 ;
403
+
404
+ if [ "${changed}" -eq 1 ] ; then
405
+ echo "" ;
406
+ echo "→ applying the domain to the dashboard project" ;
407
+ autostart_apply_domain || { echo " nothing was changed, in DDEV or in the settings." ; return 1 ; }
408
+ if [ ! "${DOMAIN}" == '_none_' ] ; then
409
+ autostart_settings_write 'hub_domain' "${DOMAIN}" || return 1 ;
410
+ echo " domain is now: ${DOMAIN}" ;
411
+ fi
412
+ echo " done" ;
413
+ fi
414
+
415
+ if [ "${ENABLE}" == 'yes' ] ; then
416
+ echo "" ; echo "→ installing the boot service" ; autostart_enable || return 1 ;
417
+ fi
418
+
419
+ if [ "${DISABLE}" == 'yes' ] ; then
420
+ echo "" ; echo "→ removing the boot service" ; autostart_disable ;
421
+ fi
422
+
423
+ if [ "${NOW}" == 'yes' ] ; then
424
+ echo "" ; echo "→ running exactly what the boot service runs" ;
425
+ bash "${AUTOSTART_RUNNER}" || return 1 ;
426
+ fi
427
+
428
+ autostart_status ;
429
+ }