@webship.co/workspace 0.0.0-stage → 1.0.0-alpha1

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 (311) hide show
  1. package/PLUGINS.md +249 -0
  2. package/README.md +379 -2
  3. package/WORKSPACES.md +69 -0
  4. package/actions/ai-items.js +293 -0
  5. package/actions/audio.js +48 -0
  6. package/actions/build-order.js +79 -0
  7. package/actions/checks.js +95 -0
  8. package/actions/claude-session.js +104 -0
  9. package/actions/commands-center.js +258 -0
  10. package/actions/composer.js +267 -0
  11. package/actions/ddev-addons.js +90 -0
  12. package/actions/ddev-mail.js +80 -0
  13. package/actions/ddev.js +237 -0
  14. package/actions/folders.js +165 -0
  15. package/actions/graphs.js +107 -0
  16. package/actions/images-svg.js +33 -0
  17. package/actions/images.js +145 -0
  18. package/actions/navigation.js +190 -0
  19. package/actions/node.js +222 -0
  20. package/actions/notes.js +55 -0
  21. package/actions/obsidian.js +59 -0
  22. package/actions/plugins.js +36 -0
  23. package/actions/project-make.js +28 -0
  24. package/actions/rag.js +202 -0
  25. package/actions/row-actions.js +262 -0
  26. package/actions/screens.js +270 -0
  27. package/actions/testing.js +121 -0
  28. package/actions/video-edit.js +54 -0
  29. package/ai-inventory.js +182 -0
  30. package/ai-items.js +202 -0
  31. package/assistant-cast.js +176 -0
  32. package/assistant-suggestions.js +91 -0
  33. package/assistant.js +484 -0
  34. package/audio-voice.js +528 -0
  35. package/auth-google.js +260 -0
  36. package/build-ai.js +52 -0
  37. package/build-order-page.js +191 -0
  38. package/build-order.js +313 -0
  39. package/builder-args.js +117 -0
  40. package/changelog.js +302 -0
  41. package/chat-threads.js +134 -0
  42. package/checks.js +105 -0
  43. package/claude-args.js +282 -0
  44. package/claude-sessions.js +129 -0
  45. package/command-author.js +117 -0
  46. package/command-block.js +20 -0
  47. package/commands/defaults.yml +159 -0
  48. package/commands-page.js +460 -0
  49. package/commands.js +568 -0
  50. package/cucumber.js +43 -0
  51. package/ddev-addons-page.js +124 -0
  52. package/ddev-addons.js +154 -0
  53. package/ddev-custom-commands.js +96 -0
  54. package/ddev-domains.js +84 -0
  55. package/ddev-import.js +93 -0
  56. package/ddev-mail-page.js +167 -0
  57. package/ddev-mail.js +110 -0
  58. package/ddev-options.js +145 -0
  59. package/ddev-snapshots.js +126 -0
  60. package/ddev.js +145 -0
  61. package/docs/DEVELOPER-GUIDE.md +224 -0
  62. package/docs/README.md +252 -0
  63. package/docs/REFERENCE.md +684 -0
  64. package/docs/USER-GUIDE.md +697 -0
  65. package/docs/screenshots/assistant/avatar-board.png +0 -0
  66. package/docs/screenshots/assistant/avatar-coffee.png +0 -0
  67. package/docs/screenshots/assistant/avatar-colours.png +0 -0
  68. package/docs/screenshots/assistant/avatar-icons.png +0 -0
  69. package/docs/screenshots/assistant/avatar-moves.png +0 -0
  70. package/docs/screenshots/assistant/avatar-pick-cookieman.png +0 -0
  71. package/docs/screenshots/assistant/avatar-settings-voices.png +0 -0
  72. package/docs/screenshots/assistant/avatar-status.png +0 -0
  73. package/docs/screenshots/create-card/create-card.png +0 -0
  74. package/docs/screenshots/create-card/start-ai.png +0 -0
  75. package/docs/screenshots/guide-clone-address.png +0 -0
  76. package/docs/screenshots/guide-clone-worktree-off.png +0 -0
  77. package/docs/screenshots/guide-clone-worktree-on.png +0 -0
  78. package/docs/screenshots/guide-ddev-menu.png +0 -0
  79. package/docs/screenshots/guide-domains.png +0 -0
  80. package/docs/screenshots/guide-home.png +0 -0
  81. package/docs/screenshots/guide-ide-menu.png +0 -0
  82. package/docs/screenshots/guide-remove-refusal.png +0 -0
  83. package/docs/screenshots/guide-rows-worktree.png +0 -0
  84. package/docs/screenshots/guide-screens.png +0 -0
  85. package/docs/screenshots/guide-settings.png +0 -0
  86. package/docs/screenshots/guide-workspace-page.png +0 -0
  87. package/docs/screenshots/home/card-settings.png +0 -0
  88. package/docs/screenshots/home/drag-reorder.png +0 -0
  89. package/docs/screenshots/home/edit-card.png +0 -0
  90. package/docs/screenshots/images/claude-code-image.png +0 -0
  91. package/docs/screenshots/images/edit-with-ai.png +0 -0
  92. package/docs/screenshots/images/gallery.png +0 -0
  93. package/docs/screenshots/images/generate-description.png +0 -0
  94. package/docs/screenshots/images/generate-plan.png +0 -0
  95. package/docs/screenshots/images/providers.png +0 -0
  96. package/docs/screenshots/images/svg-form.png +0 -0
  97. package/docs/screenshots/images/svg-result.png +0 -0
  98. package/docs/screenshots/images/upload.png +0 -0
  99. package/docs/screenshots/new-folder/2-form.png +0 -0
  100. package/docs/screenshots/new-folder/8-ddev-options.png +0 -0
  101. package/docs/screenshots/new-folder/files-folder.png +0 -0
  102. package/docs/screenshots/projects/ddev-custom-commands.png +0 -0
  103. package/docs/screenshots/projects/state-chip-stop.png +0 -0
  104. package/docs/screenshots/readme/dashboard-dark.png +0 -0
  105. package/docs/screenshots/readme/dashboard-light.png +0 -0
  106. package/docs/screenshots/readme/dev-dark.png +0 -0
  107. package/docs/screenshots/readme/dev-light.png +0 -0
  108. package/docs/screenshots/readme/docs-dark.png +0 -0
  109. package/docs/screenshots/readme/docs-light.png +0 -0
  110. package/docs/screenshots/readme/icons/ai.svg +4 -0
  111. package/docs/screenshots/readme/icons/code.svg +4 -0
  112. package/docs/screenshots/readme/icons/docs.svg +4 -0
  113. package/docs/screenshots/readme/icons/services.svg +4 -0
  114. package/docs/screenshots/readme/icons/sites.svg +4 -0
  115. package/docs/screenshots/readme/test-report-dark.png +0 -0
  116. package/docs/screenshots/readme/test-report-light.png +0 -0
  117. package/docs/screenshots/readme/test-scenarios.png +0 -0
  118. package/docs/screenshots/readme/test-steps.png +0 -0
  119. package/docs/screenshots/readme/videos-dark.png +0 -0
  120. package/docs/screenshots/readme/videos-light.png +0 -0
  121. package/docs/screenshots/screens-menu.png +0 -0
  122. package/docs/screenshots/screens-new.png +0 -0
  123. package/docs/screenshots/settings/auth-google-lists.png +0 -0
  124. package/docs/screenshots/settings/delete-1-row.png +0 -0
  125. package/docs/screenshots/settings/delete-2-confirm.png +0 -0
  126. package/docs/screenshots/settings/delete-3-type-name.png +0 -0
  127. package/docs/screenshots/settings/delete-4-type-yes.png +0 -0
  128. package/event-log.js +59 -0
  129. package/files-browser.js +247 -0
  130. package/folders.js +121 -0
  131. package/format.js +73 -0
  132. package/git-info.js +81 -0
  133. package/github-auth.js +62 -0
  134. package/graphs.js +104 -0
  135. package/html.js +10 -0
  136. package/icons.js +83 -0
  137. package/images-ai.js +447 -0
  138. package/images-create.js +226 -0
  139. package/images-page.js +417 -0
  140. package/images-records.js +39 -0
  141. package/images-render.js +36 -0
  142. package/images-svg-core.js +214 -0
  143. package/images-svg-sanitize.js +188 -0
  144. package/images-svg.js +241 -0
  145. package/images.js +270 -0
  146. package/jobs.js +430 -0
  147. package/linger.js +21 -0
  148. package/lists.js +292 -0
  149. package/media-info.js +136 -0
  150. package/navigation-page.js +264 -0
  151. package/navigation.js +394 -0
  152. package/notes.js +0 -0
  153. package/obsidian.js +405 -0
  154. package/package.json +47 -4
  155. package/palette-index.js +46 -0
  156. package/playwright.config.js +23 -0
  157. package/plugin-install.js +239 -0
  158. package/plugin-loader.js +448 -0
  159. package/plugin-manifest.js +83 -0
  160. package/plugin-settings.js +44 -0
  161. package/plugins/workspace-notes/index.js +59 -0
  162. package/plugins-page.js +103 -0
  163. package/project-make.js +241 -0
  164. package/project-name.js +210 -0
  165. package/public/assistant-avatar-moves.js +646 -0
  166. package/public/assistant-avatar-ui.js +1223 -0
  167. package/public/assistant-avatar.js +530 -0
  168. package/public/avatar/druplicon.vrm +0 -0
  169. package/public/css/base.css +260 -0
  170. package/public/css/components/ai-assistant.css +263 -0
  171. package/public/css/components/assistant-avatar.css +279 -0
  172. package/public/css/components/assistant-views.css +198 -0
  173. package/public/css/components/build-order.css +74 -0
  174. package/public/css/components/clone-as-a-git-worktree.css +30 -0
  175. package/public/css/components/command-palette.css +61 -0
  176. package/public/css/components/commands-center.css +139 -0
  177. package/public/css/components/confirm-dialog.css +40 -0
  178. package/public/css/components/create-card.css +8 -0
  179. package/public/css/components/ddev-menu.css +23 -0
  180. package/public/css/components/deep-chat.css +17 -0
  181. package/public/css/components/density.css +11 -0
  182. package/public/css/components/floating-assistant-workspace-pages.css +55 -0
  183. package/public/css/components/folders.css +5 -0
  184. package/public/css/components/full-height-left-assistant-sidebar-all-pages.css +39 -0
  185. package/public/css/components/full-height-right-actions-rail-pages-that-have-actions.css +267 -0
  186. package/public/css/components/grid-and-list.css +203 -0
  187. package/public/css/components/help-page.css +36 -0
  188. package/public/css/components/hero.css +16 -0
  189. package/public/css/components/images.css +70 -0
  190. package/public/css/components/layout-rhythm-and-spacing-polish.css +139 -0
  191. package/public/css/components/mic-on-any-field-that-wants-typing.css +416 -0
  192. package/public/css/components/modal-fit.css +113 -0
  193. package/public/css/components/page-width.css +23 -0
  194. package/public/css/components/plugins-page.css +9 -0
  195. package/public/css/components/project-cards.css +256 -0
  196. package/public/css/components/project-rows.css +390 -0
  197. package/public/css/components/reordering-the-workspace-cards.css +85 -0
  198. package/public/css/components/responsive-home-and-the-workspace-pages.css +457 -0
  199. package/public/css/components/running-commands-as-toasts.css +327 -0
  200. package/public/css/components/settings-editor-core-config-yml.css +828 -0
  201. package/public/css/components/short-results-as-a-toast-at-the-top.css +203 -0
  202. package/public/css/components/sound-player.css +84 -0
  203. package/public/css/components/suggest-agents-skills-commands.css +112 -0
  204. package/public/css/components/tab-modal.css +142 -0
  205. package/public/css/components/terminals.css +244 -0
  206. package/public/css/components/test-report-media-one-file-per-row-paged.css +144 -0
  207. package/public/css/components/top-toolbar-logo-breadcrumb.css +107 -0
  208. package/public/css/components/video-edit.css +6 -0
  209. package/public/css/components/web-ide-open-with.css +23 -0
  210. package/public/css/components/working-indicators.css +158 -0
  211. package/public/css/components/workspace-cards.css +79 -0
  212. package/public/favicon.ico +0 -0
  213. package/public/favicon.png +0 -0
  214. package/public/htmx.min.js +1 -0
  215. package/public/logo.png +0 -0
  216. package/public/media-player.js +215 -0
  217. package/public/mic.js +255 -0
  218. package/public/narration.js +110 -0
  219. package/public/palette.js +278 -0
  220. package/public/sortable.js +251 -0
  221. package/public/suggest.js +337 -0
  222. package/public/terminal.js +903 -0
  223. package/public/themes/default/theme.css +267 -0
  224. package/public/themes/ember/theme.css +180 -0
  225. package/public/themes/press/theme.css +290 -0
  226. package/public/themes/slate/theme.css +182 -0
  227. package/public/touch-icon.png +0 -0
  228. package/public/ui.js +2721 -0
  229. package/public/vendor/ace/LICENSE.txt +24 -0
  230. package/public/vendor/ace/ace.js +23 -0
  231. package/public/vendor/ace/ext-language_tools.js +8 -0
  232. package/public/vendor/ace/ext-searchbox.js +8 -0
  233. package/public/vendor/ace/mode-css.js +8 -0
  234. package/public/vendor/ace/mode-html.js +8 -0
  235. package/public/vendor/ace/mode-javascript.js +8 -0
  236. package/public/vendor/ace/mode-markdown.js +8 -0
  237. package/public/vendor/ace/mode-sh.js +8 -0
  238. package/public/vendor/ace/mode-yaml.js +8 -0
  239. package/public/vendor/ace/theme-textmate.js +8 -0
  240. package/public/vendor/ace/theme-tomorrow_night.js +8 -0
  241. package/public/vendor/ace/worker-yaml.js +1 -0
  242. package/public/vendor/deep-chat.LICENSE +21 -0
  243. package/public/vendor/deep-chat.bundle.js +1 -0
  244. package/public/vendor/fonts/dm-serif-text-LICENSE.txt +93 -0
  245. package/public/vendor/fonts/dm-serif-text-latin-400-italic.woff2 +0 -0
  246. package/public/vendor/fonts/dm-serif-text-latin-400-normal.woff2 +0 -0
  247. package/public/vendor/fonts/figtree-LICENSE.txt +93 -0
  248. package/public/vendor/fonts/figtree-latin-400-italic.woff2 +0 -0
  249. package/public/vendor/fonts/figtree-latin-400-normal.woff2 +0 -0
  250. package/public/vendor/fonts/figtree-latin-600-italic.woff2 +0 -0
  251. package/public/vendor/fonts/figtree-latin-600-normal.woff2 +0 -0
  252. package/public/vendor/fonts/jetbrains-mono-LICENSE.txt +93 -0
  253. package/public/vendor/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
  254. package/public/vendor/fonts/ubuntu-mono-LICENSE.txt +98 -0
  255. package/public/vendor/fonts/ubuntu-mono-latin-wght-normal.woff2 +0 -0
  256. package/public/vendor/tabler-LICENSE.txt +21 -0
  257. package/public/vendor/tabler-sprite.svg +1 -0
  258. package/public/vendor/three/LICENSE +21 -0
  259. package/public/vendor/three/addons/loaders/GLTFLoader.js +4860 -0
  260. package/public/vendor/three/addons/utils/BufferGeometryUtils.js +1501 -0
  261. package/public/vendor/three/addons/utils/SkeletonUtils.js +496 -0
  262. package/public/vendor/three/three.core.min.js +6 -0
  263. package/public/vendor/three/three.module.min.js +6 -0
  264. package/public/vendor/three-vrm/LICENSE +22 -0
  265. package/public/vendor/three-vrm/three-vrm.module.min.js +983 -0
  266. package/public/vendor/uikit-icons.min.js +1 -0
  267. package/public/vendor/uikit.min.css +1 -0
  268. package/public/vendor/uikit.min.js +1 -0
  269. package/public/vendor/webship-icons/ui/bookmark-outlined.svg +1 -0
  270. package/public/vendor/webship-icons/ui/headset.svg +1 -0
  271. package/public/vendor/webship-icons/ui/menu.svg +1 -0
  272. package/public/vendor/webship-icons/ui/pause.svg +1 -0
  273. package/public/vendor/webship-icons/ui/play.svg +1 -0
  274. package/public/vendor/webship-icons/ui/share.svg +1 -0
  275. package/public/vendor/webship-icons/ui/sound-off.svg +1 -0
  276. package/public/vendor/webship-icons/ui/sound-on.svg +1 -0
  277. package/public/vendor/webship-icons/ui/stop.svg +1 -0
  278. package/public/vendor/xterm/addon-fit.js +2 -0
  279. package/public/vendor/xterm/addon-unicode11.js +2 -0
  280. package/public/vendor/xterm/addon-web-links.js +2 -0
  281. package/public/vendor/xterm/xterm.css +218 -0
  282. package/public/vendor/xterm/xterm.js +2 -0
  283. package/rag.js +169 -0
  284. package/reader.js +132 -0
  285. package/repo-blobs.js +66 -0
  286. package/report-pdf.js +337 -0
  287. package/row-layout.js +1440 -0
  288. package/screenrc +37 -0
  289. package/screens.js +1063 -0
  290. package/scripts/code-server-start.sh +190 -0
  291. package/scripts/images-claude-finish.js +107 -0
  292. package/scripts/screen-host.sh +114 -0
  293. package/scripts/screen-runner.js +362 -0
  294. package/scripts/screen-then.js +47 -0
  295. package/scripts/update.sh +350 -0
  296. package/server.js +8140 -0
  297. package/settings-file.js +423 -0
  298. package/speech.js +297 -0
  299. package/tab-modal.js +75 -0
  300. package/test-reports.js +260 -0
  301. package/themes.js +381 -0
  302. package/transcripts.js +168 -0
  303. package/urls.js +13 -0
  304. package/validate.js +18 -0
  305. package/version-check.js +94 -0
  306. package/video-edit.js +588 -0
  307. package/waiting.js +85 -0
  308. package/workspace-card-tools.js +41 -0
  309. package/workspace-delete.js +139 -0
  310. package/workspace-types.js +85 -0
  311. package/workspaces.js +557 -0
@@ -0,0 +1,190 @@
1
+ #!/usr/bin/env bash
2
+
3
+ ################################################################################
4
+ ## Start the Web IDE (code-server), with defaults that suit this workspace.
5
+ ################################################################################
6
+ ## Its own script rather than a long line in .ddev/config.yaml, because it has to
7
+ ## do one thing before exec-ing: seed the user settings on a machine that has
8
+ ## none yet.
9
+ ##
10
+ ## WORKSPACE TRUST IS OFF. VS Code opens every new folder in Restricted Mode and
11
+ ## asks "Do you trust the authors of the files in this folder?", and until it is
12
+ ## answered the terminal refuses to start at all:
13
+ ##
14
+ ## Creating a terminal process requires executing code
15
+ ##
16
+ ## That prompt exists for code you downloaded. Here every folder is a project on
17
+ ## this machine, opened from a dashboard that already offers a real shell at
18
+ ## /terminal/<session> — so the prompt protects nothing and costs a click plus a
19
+ ## confused "why can't I open a terminal" on every project. Turning it off is a
20
+ ## deliberate decision recorded here, not an oversight; see the security note in
21
+ ## core/workspace-app/README.md.
22
+ ##
23
+ ## AGENTS AND SKILLS ARE ALREADY THERE. This runs in the dashboard's container, which
24
+ ## bind-mounts ~/.claude — so the `claude` CLI in an IDE terminal loads the same 86
25
+ ## agents, 42 skills and 6 commands a session on the host would, plus the folder's own
26
+ ## CLAUDE.md and AGENTS.md, because Claude Code reads those from the working directory
27
+ ## up. Nothing needs installing per project. The only thing missing was DISCOVERABILITY,
28
+ ## so the seeded settings add "Claude Code" terminal profiles: the terminal dropdown in
29
+ ## the IDE offers them by name instead of the person having to know to type `claude`.
30
+ ##
31
+ ## WATCHERS ARE THE OTHER DRUPAL PROBLEM. One built Drupal project is ~21,000
32
+ ## directories, 11,800 of them in vendor/, web/core/ and node_modules/ — and the kernel's
33
+ ## fs.inotify.max_user_watches is 65,536 SHARED ACROSS EVERYTHING on the machine. Open two
34
+ ## projects and the editor gives up with "Unable to watch for file changes", which means it
35
+ ## silently stops noticing edits made outside it (a composer install, a git checkout, an
36
+ ## agent). files.watcherExclude drops those three trees — 55% of the directories per
37
+ ## project — and needs no root. Raising the sysctl does need root, so install.sh reports it
38
+ ## rather than this script doing it behind anyone's back.
39
+ ##
40
+ ## The PHP file associations matter on Drupal: .module, .install, .theme and .engine are
41
+ ## PHP, and without this the editor treats them as plain text — no syntax highlighting
42
+ ## and no outline in exactly the files a Drupal developer opens most.
43
+ ##
44
+ ## Values are written in the CURRENT form VS Code expects: it migrates deprecated ones
45
+ ## and REWRITES the file, so `"extensions.autoUpdate": false` came back as `"off"` on the
46
+ ## first run. Harmless, but a file that changes itself the moment it is written makes the
47
+ ## next person wonder which of the two is authoritative.
48
+ ##
49
+ ## The settings file is only written when it does NOT exist. It lives in the
50
+ ## workspace tree (gitignored) so a person's own settings, keybindings and
51
+ ## extensions survive an image rebuild — and must not be overwritten by a restart.
52
+ ################################################################################
53
+
54
+ set -u
55
+
56
+ : "${WORKSPACE_ROOT:=${HOME}/workspace}"
57
+
58
+ STATE="${WORKSPACE_ROOT}/.code-server"
59
+ USER_DIR="${STATE}/data/User"
60
+ SETTINGS="${USER_DIR}/settings.json"
61
+
62
+ mkdir -p "${USER_DIR}" "${STATE}/extensions"
63
+
64
+ if [ ! -f "${SETTINGS}" ] ; then
65
+ cat > "${SETTINGS}" <<'JSON'
66
+ {
67
+ "security.workspace.trust.enabled": false,
68
+ "telemetry.telemetryLevel": "off",
69
+ "update.mode": "none",
70
+ "extensions.autoUpdate": "off",
71
+ "workbench.startupEditor": "none",
72
+ "files.autoSave": "off",
73
+ "editor.renderWhitespace": "boundary",
74
+ "editor.rulers": [80, 120],
75
+ "files.trimTrailingWhitespace": true,
76
+ "files.insertFinalNewline": true,
77
+ "files.exclude": {
78
+ "**/.git": true,
79
+ "**/node_modules": true,
80
+ "**/vendor": true
81
+ },
82
+ "search.exclude": {
83
+ "**/node_modules": true,
84
+ "**/vendor": true,
85
+ "**/web/core": true,
86
+ "**/web/modules/contrib": true
87
+ },
88
+ "files.watcherExclude": {
89
+ "**/.git/objects/**": true,
90
+ "**/.git/subtree-cache/**": true,
91
+ "**/node_modules/**": true,
92
+ "**/vendor/**": true,
93
+ "**/web/core/**": true,
94
+ "**/web/modules/contrib/**": true,
95
+ "**/web/themes/contrib/**": true,
96
+ "**/web/libraries/**": true,
97
+ "**/graphify-out/**": true,
98
+ "**/.ddev/**": true
99
+ },
100
+ "terminal.integrated.defaultProfile.linux": "bash",
101
+ "terminal.integrated.profiles.linux": {
102
+ "bash": { "path": "bash", "icon": "terminal-bash" },
103
+ "Claude Code": {
104
+ "path": "claude",
105
+ "icon": "sparkle",
106
+ "overrideName": true
107
+ },
108
+ "Claude Code (accept edits)": {
109
+ "path": "claude",
110
+ "args": ["--permission-mode", "acceptEdits"],
111
+ "icon": "sparkle"
112
+ }
113
+ },
114
+ "files.associations": {
115
+ "*.theme": "php",
116
+ "*.module": "php",
117
+ "*.install": "php",
118
+ "*.profile": "php",
119
+ "*.engine": "php",
120
+ "*.inc": "php"
121
+ }
122
+ }
123
+ JSON
124
+ echo "code-server: seeded default settings at ${SETTINGS}"
125
+ fi
126
+
127
+ ################################################################################
128
+ ## Claude Code IN the editor, but only if it is configured for use.
129
+ ################################################################################
130
+ ## The official extension (Anthropic.claude-code) is published on Open VSX, which is
131
+ ## the registry code-server queries, so it installs with no marketplace switch and no
132
+ ## .vsix juggling.
133
+ ##
134
+ ## GATED on Claude Code actually being usable here, which means two things and not one:
135
+ ## the `claude` CLI on PATH (the extension drives it) and a credentials file in
136
+ ## ~/.claude, which the container bind-mounts from the host. Installing it on a machine
137
+ ## where Claude Code has never been logged in would put a panel in the editor whose only
138
+ ## behaviour is to ask for a login that belongs on the host — so on that machine the IDE
139
+ ## keeps the terminal profiles and nothing else, which is the honest state.
140
+ ##
141
+ ## Installed ONCE, when missing: the download is a few MB and doing it on every restart
142
+ ## would add that to every `ddev restart`. Bounded by a timeout and never fatal — an
143
+ ## editor that will not start because a registry was slow is a worse outcome than an
144
+ ## editor without one extension.
145
+ CLAUDE_EXT='Anthropic.claude-code'
146
+
147
+ function claude_is_configured() {
148
+ command -v claude > /dev/null 2>&1 || return 1
149
+ # Either shape counts: the credentials file, or a subscription/API key in the env.
150
+ [ -s "${HOME}/.claude/.credentials.json" ] && return 0
151
+ [ -n "${ANTHROPIC_API_KEY:-}" ] && return 0
152
+ return 1
153
+ }
154
+
155
+ function ext_installed() {
156
+ [ -d "${STATE}/extensions" ] || return 1
157
+ ls -d "${STATE}/extensions/"*"${CLAUDE_EXT,,}"* > /dev/null 2>&1
158
+ }
159
+
160
+ if claude_is_configured ; then
161
+ if ext_installed ; then
162
+ echo "code-server: ${CLAUDE_EXT} already installed"
163
+ else
164
+ echo "code-server: Claude Code is configured here — installing ${CLAUDE_EXT}…"
165
+ if timeout 180 code-server \
166
+ --user-data-dir "${STATE}/data" \
167
+ --extensions-dir "${STATE}/extensions" \
168
+ --install-extension "${CLAUDE_EXT}" ; then
169
+ echo "code-server: ${CLAUDE_EXT} installed"
170
+ else
171
+ echo "code-server: could not install ${CLAUDE_EXT} (registry unreachable?) — the" \
172
+ "Claude Code terminal profiles still work. Retry: ddev restart"
173
+ fi
174
+ fi
175
+ else
176
+ echo "code-server: Claude Code is not configured on this machine (no ~/.claude/.credentials.json)" \
177
+ "— skipping the editor extension. Log in on the HOST with \`claude\`, then: ddev restart"
178
+ fi
179
+
180
+ # Loopback only: the ide.* vhost in .ddev/nginx_full/nginx-site.conf is the one way
181
+ # in, and that is the whole access control. Never bind 0.0.0.0 here — this process
182
+ # has a terminal and write access to the entire workspace tree.
183
+ exec code-server \
184
+ --bind-addr 127.0.0.1:8443 \
185
+ --auth none \
186
+ --disable-telemetry \
187
+ --disable-update-check \
188
+ --app-name 'Webship Workspace IDE' \
189
+ --user-data-dir "${STATE}/data" \
190
+ --extensions-dir "${STATE}/extensions"
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * The fixed step after Claude Code has drawn an image: sanitise the SVG, render its PNG with headless Chrome,
4
+ * and write the records. Runs on the host, in the screen session, with the job file the dashboard wrote.
5
+ * Usage: node images-claude-finish.js <job.json>
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+
11
+ const { sanitizeSvg, removedSummary } = require('../images-svg-sanitize');
12
+ const { writeRecords } = require('../images-records');
13
+ const { renderPng } = require('../images-render');
14
+
15
+ /** The summary and alt text Claude wrote, or empty ones. The file is removed either way. */
16
+ function words(file) {
17
+ let out = {};
18
+ try { out = JSON.parse(fs.readFileSync(file, 'utf8')); } catch (_) { /* missing or not JSON */ }
19
+ fs.rmSync(file, { force: true });
20
+ return { summary: String(out.summary || '').trim().slice(0, 600), alt: String(out.alt || '').trim().slice(0, 125) };
21
+ }
22
+
23
+ /** Generate SVG: the same checks as the API path; a refused drawing goes back to Claude with the reason. */
24
+ function finishSvg(job, jobFile, svgPath) {
25
+ const core = require('../images-svg-core');
26
+ const done = core.finish(fs.readFileSync(svgPath, 'utf8'), { style: job.style, motion: job.motion });
27
+ job.tried = (job.tried || 0) + 1;
28
+ if (!done.ok) {
29
+ fs.rmSync(svgPath, { force: true });
30
+ const promptFile = jobFile.replace(/\.job\.json$/, '.txt');
31
+ if (job.tried < (job.attempts || 1)) {
32
+ fs.writeFileSync(jobFile, JSON.stringify(job), { mode: 0o600 });
33
+ fs.appendFileSync(promptFile, `\n\nYour last drawing was refused. ${done.why} Write ${job.name}.svg again.`);
34
+ console.error(`Attempt ${job.tried} refused: ${done.why} Drawing again.`);
35
+ return 3;
36
+ }
37
+ console.error(`No usable drawing after ${job.tried} attempt${job.tried === 1 ? '' : 's'}: ${done.why}`);
38
+ for (const f of [jobFile, promptFile, path.join(job.dir, `${job.name}.claude.json`)]) fs.rmSync(f, { force: true });
39
+ return 2;
40
+ }
41
+ fs.writeFileSync(svgPath, done.svg);
42
+ if (done.note) console.log(`Sanitised: removed ${done.note}.`);
43
+ const said = words(path.join(job.dir, `${job.name}.claude.json`));
44
+ let png = '';
45
+ if (job.png && renderPng(svgPath, svgPath.replace(/\.svg$/, '.png'), job.pixels || [960, 400])) png = `${job.name}.png`;
46
+ const styles = core.STYLES;
47
+ writeRecords(svgPath, { prompt: job.prompt, style: styles[job.style], provider: 'claude_code_cli', model: 'Claude Code (your login)',
48
+ attempts: job.tried, palette: job.palette, size: (job.pixels || []).join('x'), created: job.created, png: png || undefined,
49
+ motion: job.style === 'draw' ? job.motion : undefined, sanitised: done.note || undefined, cost: 'no API charge: your own Claude Code login',
50
+ summary: said.summary, alt: said.alt, described_by: said.summary || said.alt ? 'Claude Code, while drawing' : undefined });
51
+ for (const f of [jobFile, jobFile.replace(/\.job\.json$/, '.txt')]) fs.rmSync(f, { force: true });
52
+ console.log(`Done: ${svgPath}${png ? ` and ${png}` : ''}, with its records.`);
53
+ return 0;
54
+ }
55
+
56
+ function main(jobFile) {
57
+ const job = JSON.parse(fs.readFileSync(jobFile, 'utf8'));
58
+ const svgPath = path.join(job.dir, `${job.name}.svg`);
59
+ if (!fs.existsSync(svgPath)) {
60
+ console.error(`Claude Code did not write ${job.name}.svg.`);
61
+ return 2;
62
+ }
63
+ if (job.kind === 'svg') return finishSvg(job, jobFile, svgPath);
64
+ const clean = sanitizeSvg(fs.readFileSync(svgPath, 'utf8'));
65
+ if (!clean.svg) {
66
+ console.error(`${job.name}.svg holds no <svg> drawing; it was left as written.`);
67
+ return 2;
68
+ }
69
+ fs.writeFileSync(svgPath, clean.svg);
70
+ const note = removedSummary(clean);
71
+ if (note) console.log(`Sanitised: removed ${note}.`);
72
+ const said = words(path.join(job.dir, `${job.name}.claude.json`));
73
+ const [w, h] = job.pixels || [1024, 1024];
74
+ const base = {
75
+ prompt: job.kind === 'edit' ? undefined : job.prompt,
76
+ edit_prompt: job.kind === 'edit' ? job.prompt : undefined,
77
+ source: job.source,
78
+ provider: 'claude_code_cli',
79
+ model: 'Claude Code (your login)',
80
+ size: `${w}x${h}`,
81
+ created: job.created || new Date().toISOString(),
82
+ cost: 'no API charge: your own Claude Code login',
83
+ described_by: said.summary || said.alt ? 'Claude Code, while drawing' : undefined,
84
+ summary: said.summary,
85
+ alt: said.alt,
86
+ sanitised: note || undefined,
87
+ };
88
+ let png = '';
89
+ if (job.png) {
90
+ const pngPath = path.join(job.dir, `${job.name}.png`);
91
+ if (renderPng(svgPath, pngPath, [w, h])) {
92
+ png = `${job.name}.png`;
93
+ fs.writeFileSync(`${pngPath}.json`, `${JSON.stringify({ ...JSON.parse(JSON.stringify(base)), from: `${job.name}.svg` }, null, 2)}\n`);
94
+ console.log(`Rendered ${png}.`);
95
+ } else {
96
+ console.error('No PNG: Chrome is not on this machine, or it could not render the SVG. The SVG is kept.');
97
+ }
98
+ }
99
+ writeRecords(svgPath, { ...base, png: png || undefined });
100
+ for (const f of [jobFile, jobFile.replace(/\.job\.json$/, '.txt')]) fs.rmSync(f, { force: true });
101
+ console.log(`Done: ${path.join(job.dir, `${job.name}.svg`)}${png ? ` and ${png}` : ''}, with its records.`);
102
+ return 0;
103
+ }
104
+
105
+ if (require.main === module) process.exit(main(process.argv[2]));
106
+
107
+ module.exports = { main };
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # The host screen runner for the Webship Workspace dashboard.
4
+ #
5
+ # bash core/workspace-app/scripts/screen-host.sh --start start it in the background
6
+ # bash core/workspace-app/scripts/screen-host.sh --status is it up, and what is running here
7
+ # bash core/workspace-app/scripts/screen-host.sh --stop stop it (sessions keep running)
8
+ # bash core/workspace-app/scripts/screen-host.sh --run run in the foreground
9
+ #
10
+ # Screen sessions belong on this machine, not inside the dashboard's container:
11
+ # the display, the browser an agent drives, ~/.claude and your own terminal are
12
+ # all here, so `screen -r <session>` should just work. The dashboard runs in the
13
+ # container and cannot start a host process, so it asks this runner instead.
14
+ #
15
+ # Deliberately SELF-CONTAINED, like scripts/update.sh beside it: plain bash, no
16
+ # workspace bootstrap. It runs on the host, before and independently of anything
17
+ # the container provides, and it locates the workspace from its own path.
18
+
19
+ set -u
20
+
21
+ APP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
22
+ ROOT="$(cd "${APP_DIR}/../.." && pwd)"
23
+ STORE="${ROOT}/.screens"
24
+ RUNNER="${APP_DIR}/scripts/screen-runner.js"
25
+ PIDFILE="${STORE}/.runner-pid"
26
+ LOGFILE="${STORE}/.runner.log"
27
+ PORT="${SCREEN_RUNNER_PORT:-7681}"
28
+ ACTION=""
29
+
30
+ while [ $# -gt 0 ]; do
31
+ case "$1" in
32
+ -s|--start) ACTION=start; shift ;;
33
+ -r|--run) ACTION=run; shift ;;
34
+ -t|--status) ACTION=status; shift ;;
35
+ -x|--stop) ACTION=stop; shift ;;
36
+ -p|--port) PORT="${2:-7681}"; shift 2 ;;
37
+ -h|--help) sed -n '3,20p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'; exit 0 ;;
38
+ *) echo "Unknown option: $1 (see --help)"; exit 1 ;;
39
+ esac
40
+ done
41
+
42
+ mkdir -p "${STORE}"
43
+
44
+ runner_pid() {
45
+ [ -f "${PIDFILE}" ] || return 1
46
+ local p; p="$(cat "${PIDFILE}" 2>/dev/null)"
47
+ [ -n "${p}" ] && kill -0 "${p}" 2>/dev/null && echo "${p}"
48
+ }
49
+
50
+ case "${ACTION}" in
51
+ stop)
52
+ if p="$(runner_pid)"; then
53
+ kill "${p}" && rm -f "${PIDFILE}"
54
+ echo "Stopped the runner (pid ${p}). The sessions it started keep running."
55
+ else
56
+ echo "The runner is not running."
57
+ fi
58
+ exit 0 ;;
59
+ status)
60
+ if p="$(runner_pid)"; then
61
+ echo "Running — pid ${p}, port $(cat "${STORE}/.runner-port" 2>/dev/null || echo '?')"
62
+ else
63
+ echo "Not running. Start it with: bash ${BASH_SOURCE[0]} --start"
64
+ fi
65
+ echo ""
66
+ echo "Sessions on $(hostname):"
67
+ if command -v screen > /dev/null 2>&1; then
68
+ screen -ls 2>/dev/null | sed -n 's/^\s\+/ /p' || true
69
+ else
70
+ echo " (screen is not installed here — sudo apt install -y screen)"
71
+ fi
72
+ exit 0 ;;
73
+ esac
74
+
75
+ if ! command -v screen > /dev/null 2>&1; then
76
+ echo "screen is not installed on this machine — the runner would have nothing to drive."
77
+ echo "Install it with: sudo apt install -y screen (or: bash ${ROOT}/core/scripts/install.sh)"
78
+ exit 1
79
+ fi
80
+ if ! command -v node > /dev/null 2>&1; then
81
+ echo "node is not installed on this machine — the runner needs it."
82
+ echo "Install it with: bash ${ROOT}/core/scripts/install.sh"
83
+ exit 1
84
+ fi
85
+
86
+ if p="$(runner_pid)"; then
87
+ echo "Already running — pid ${p}."
88
+ exit 0
89
+ fi
90
+
91
+ if [ "${ACTION}" = "run" ]; then
92
+ SCREEN_RUNNER_PORT="${PORT}" WORKSPACE_ROOT="${ROOT}" exec node "${RUNNER}"
93
+ fi
94
+
95
+ if [ "${ACTION}" = "start" ]; then
96
+ SCREEN_RUNNER_PORT="${PORT}" WORKSPACE_ROOT="${ROOT}" nohup node "${RUNNER}" >> "${LOGFILE}" 2>&1 &
97
+ echo $! > "${PIDFILE}"
98
+ sleep 1
99
+ if p="$(runner_pid)"; then
100
+ echo "Started the screen runner — pid ${p}, port ${PORT}."
101
+ echo " log: ${LOGFILE}"
102
+ echo " status: bash ${BASH_SOURCE[0]} --status"
103
+ echo ""
104
+ echo "The dashboard now starts sessions HERE, so 'screen -r <session>' works in this terminal"
105
+ echo "and a browser an agent drives opens on your desktop."
106
+ else
107
+ echo "It did not stay up. Its log:"; tail -n 20 "${LOGFILE}"
108
+ exit 1
109
+ fi
110
+ exit 0
111
+ fi
112
+
113
+ echo "Nothing to do: give --start, --run, --status or --stop."
114
+ exit 1