wizards 0.4.7 → 0.5.1

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 (183) hide show
  1. package/README.md +45 -42
  2. package/apps/runtime/dist/agents/studio-assistant.js +8 -8
  3. package/apps/runtime/dist/app.js +7 -4
  4. package/apps/runtime/dist/assistant/primer.js +1 -1
  5. package/apps/runtime/dist/auth/index.js +2 -2
  6. package/apps/runtime/dist/authoring/guide.js +8 -8
  7. package/apps/runtime/dist/cli/account.js +31 -28
  8. package/apps/runtime/dist/cli/autostart.js +5 -3
  9. package/apps/runtime/dist/cli/clients.js +1 -1
  10. package/apps/runtime/dist/cli/connect.js +24 -24
  11. package/apps/runtime/dist/cli/features.js +6 -6
  12. package/apps/runtime/dist/cli/main.js +51 -39
  13. package/apps/runtime/dist/cli/mcp.js +5 -4
  14. package/apps/runtime/dist/cli/menu.js +32 -30
  15. package/apps/runtime/dist/cli/report.js +7 -9
  16. package/apps/runtime/dist/cli/setup.js +42 -38
  17. package/apps/runtime/dist/cli/start.js +16 -15
  18. package/apps/runtime/dist/cli/status.js +20 -20
  19. package/apps/runtime/dist/cli/ui.js +9 -2
  20. package/apps/runtime/dist/cli/update.js +6 -6
  21. package/apps/runtime/dist/cli/validate.js +1 -1
  22. package/apps/runtime/dist/connectors/external.js +3 -2
  23. package/apps/runtime/dist/connectors/index.js +5 -5
  24. package/apps/runtime/dist/connectors/plugin-connections.js +2 -2
  25. package/apps/runtime/dist/connectors/via-account.js +5 -5
  26. package/apps/runtime/dist/db/control-schema.js +14 -0
  27. package/apps/runtime/dist/db/migrations/control/0009_plugin_claim.sql +9 -0
  28. package/apps/runtime/dist/db/migrations/control/meta/0009_snapshot.json +561 -0
  29. package/apps/runtime/dist/db/migrations/control/meta/_journal.json +7 -0
  30. package/apps/runtime/dist/db/schema.js +9 -9
  31. package/apps/runtime/dist/docs/dev/index.md +16 -14
  32. package/apps/runtime/dist/docs/dev/plugins/connections.md +27 -24
  33. package/apps/runtime/dist/docs/dev/plugins/events.md +19 -17
  34. package/apps/runtime/dist/docs/dev/plugins/examples.md +13 -12
  35. package/apps/runtime/dist/docs/dev/plugins/index.md +29 -24
  36. package/apps/runtime/dist/docs/dev/plugins/layout.md +20 -19
  37. package/apps/runtime/dist/docs/dev/plugins/quick-start.md +16 -15
  38. package/apps/runtime/dist/docs/dev/plugins/reference.md +52 -40
  39. package/apps/runtime/dist/docs/dev/plugins/reloading.md +24 -23
  40. package/apps/runtime/dist/docs/dev/plugins/routes.md +26 -22
  41. package/apps/runtime/dist/docs/dev/plugins/runners.md +49 -44
  42. package/apps/runtime/dist/docs/dev/plugins/server.md +95 -43
  43. package/apps/runtime/dist/docs/dev/plugins/shipping.md +19 -16
  44. package/apps/runtime/dist/docs/dev/plugins/space.md +75 -69
  45. package/apps/runtime/dist/docs/dev/plugins/starters.md +22 -17
  46. package/apps/runtime/dist/docs/dev/plugins/studio.md +80 -37
  47. package/apps/runtime/dist/docs/dev/plugins/styling.md +44 -41
  48. package/apps/runtime/dist/docs/dev/plugins/tables.md +11 -11
  49. package/apps/runtime/dist/docs/dev/plugins/tools.md +26 -25
  50. package/apps/runtime/dist/docs/dev/wizard-format.md +35 -23
  51. package/apps/runtime/dist/docs/user/ai-apps.md +46 -43
  52. package/apps/runtime/dist/docs/user/assistant.md +41 -36
  53. package/apps/runtime/dist/docs/user/build-a-wizard.md +64 -39
  54. package/apps/runtime/dist/docs/user/command-line.md +47 -44
  55. package/apps/runtime/dist/docs/user/connectors.md +40 -35
  56. package/apps/runtime/dist/docs/user/create-account.md +27 -22
  57. package/apps/runtime/dist/docs/user/data.md +29 -29
  58. package/apps/runtime/dist/docs/user/first-start.md +26 -28
  59. package/apps/runtime/dist/docs/user/index.md +16 -17
  60. package/apps/runtime/dist/docs/user/install.md +28 -27
  61. package/apps/runtime/dist/docs/user/local-only.md +21 -17
  62. package/apps/runtime/dist/docs/user/models.md +92 -84
  63. package/apps/runtime/dist/docs/user/plugins.md +28 -23
  64. package/apps/runtime/dist/docs/user/run.md +51 -50
  65. package/apps/runtime/dist/docs/user/server.md +9 -9
  66. package/apps/runtime/dist/docs/user/space.md +87 -65
  67. package/apps/runtime/dist/docs/user/steps.md +73 -69
  68. package/apps/runtime/dist/docs/user/test-and-share.md +127 -105
  69. package/apps/runtime/dist/docs/user/troubleshooting.md +40 -38
  70. package/apps/runtime/dist/docs/user/wordpress.md +62 -54
  71. package/apps/runtime/dist/documents/parse.js +2 -2
  72. package/apps/runtime/dist/engenty/shims/web-ingest.js +17 -1
  73. package/apps/runtime/dist/engine/input.js +4 -4
  74. package/apps/runtime/dist/engine/push.js +2 -2
  75. package/apps/runtime/dist/engine/runner.js +8 -8
  76. package/apps/runtime/dist/engine/steps.js +4 -4
  77. package/apps/runtime/dist/engine/surface.js +1 -1
  78. package/apps/runtime/dist/film/step.js +3 -3
  79. package/apps/runtime/dist/harness/claude.js +1 -1
  80. package/apps/runtime/dist/harness/codex.js +1 -1
  81. package/apps/runtime/dist/harness/model.js +2 -2
  82. package/apps/runtime/dist/harness/terminal.js +3 -3
  83. package/apps/runtime/dist/mcp/handler.js +2 -2
  84. package/apps/runtime/dist/mcp/tools.js +9 -9
  85. package/apps/runtime/dist/models.js +15 -15
  86. package/apps/runtime/dist/plugins/claims.js +56 -0
  87. package/apps/runtime/dist/plugins/loader.js +2 -0
  88. package/apps/runtime/dist/plugins/public.js +2 -3
  89. package/apps/runtime/dist/plugins/runs.js +13 -8
  90. package/apps/runtime/dist/render/chromium.js +24 -11
  91. package/apps/runtime/dist/routes/api.js +16 -10
  92. package/apps/runtime/dist/routes/debug.js +1 -1
  93. package/apps/runtime/dist/routes/delivery.js +46 -0
  94. package/apps/runtime/dist/routes/lang.js +7 -0
  95. package/apps/runtime/dist/routes/marketplace.js +2 -1
  96. package/apps/runtime/dist/routes/plugin-public.js +2 -1
  97. package/apps/runtime/dist/routes/plugins.js +13 -14
  98. package/apps/runtime/dist/routes/runs.js +59 -49
  99. package/apps/runtime/dist/routes/studio.js +58 -28
  100. package/apps/runtime/dist/services/access.js +3 -3
  101. package/apps/runtime/dist/services/assistant-chats.js +1 -1
  102. package/apps/runtime/dist/services/brand.js +2 -2
  103. package/apps/runtime/dist/services/cloud.js +5 -4
  104. package/apps/runtime/dist/services/errors.js +2 -1
  105. package/apps/runtime/dist/services/knowledge-model.js +10 -10
  106. package/apps/runtime/dist/services/knowledge.js +19 -19
  107. package/apps/runtime/dist/services/marketplace.js +2 -2
  108. package/apps/runtime/dist/services/package-file.js +12 -9
  109. package/apps/runtime/dist/services/package.js +3 -3
  110. package/apps/runtime/dist/services/project-files.js +15 -17
  111. package/apps/runtime/dist/services/project-index.js +10 -10
  112. package/apps/runtime/dist/services/projects.js +13 -8
  113. package/apps/runtime/dist/services/relevance.js +3 -3
  114. package/apps/runtime/dist/services/runners.js +4 -3
  115. package/apps/runtime/dist/services/runs.js +3 -3
  116. package/apps/runtime/dist/services/site.js +10 -10
  117. package/apps/runtime/dist/services/space-categories.js +22 -22
  118. package/apps/runtime/dist/services/space-data.js +16 -16
  119. package/apps/runtime/dist/services/space-origin.js +4 -4
  120. package/apps/runtime/dist/services/spaces.js +10 -10
  121. package/apps/runtime/dist/services/wizards.js +5 -5
  122. package/apps/runtime/dist/tools/index.js +1 -1
  123. package/apps/runtime/dist/tools/net-guard.js +135 -24
  124. package/apps/runtime/dist/tools/pages.js +4 -4
  125. package/apps/runtime/dist/tools/plugin.js +1 -1
  126. package/apps/runtime/dist/tools/project.js +10 -10
  127. package/apps/runtime/dist/tools/store.js +1 -1
  128. package/apps/runtime/dist/widgets/step.js +1 -1
  129. package/apps/web/dist/assets/AboutDialog-DaYwk84X.js +1 -0
  130. package/apps/web/dist/assets/{ChangelogDialog-Cm25slr3.js → ChangelogDialog-CsYbEayb.js} +1 -1
  131. package/apps/web/dist/assets/{ChatView-BXtXWnqK.js → ChatView-BRD3fKZU.js} +1 -1
  132. package/apps/web/dist/assets/EditorPage-ocPlZD3E.js +3 -0
  133. package/apps/web/dist/assets/{FlowPreview-BsfjDVv8.js → FlowPreview-Bm7sv746.js} +1 -1
  134. package/apps/web/dist/assets/{Harness-B0hD5Pp7.js → Harness-DKE-YNA5.js} +1 -1
  135. package/apps/web/dist/assets/{NewWizardChannels-DMDp9Ao5.js → NewWizardChannels-CnWckqjO.js} +1 -1
  136. package/apps/web/dist/assets/{NewWizardPage-aOisxyk2.js → NewWizardPage-BXTpbHVM.js} +1 -1
  137. package/apps/web/dist/assets/{OnboardingPage-Czb0pOyy.js → OnboardingPage-3Rz5uOsa.js} +1 -1
  138. package/apps/web/dist/assets/{ProjectPage-DPCtb42K.js → ProjectPage-QdSJwm3k.js} +1 -1
  139. package/apps/web/dist/assets/{PublicRunner-CvyA-gkf.js → PublicRunner-DEpBer5g.js} +1 -1
  140. package/apps/web/dist/assets/ReadOnly-rr_RJD-a.js +1 -0
  141. package/apps/web/dist/assets/{RunDrawer-BCiPEz9g.js → RunDrawer-BsZt0tL9.js} +1 -1
  142. package/apps/web/dist/assets/{RunnerView-4z6DMzE6.js → RunnerView-BbRukEdE.js} +1 -1
  143. package/apps/web/dist/assets/SettingsPage-qkWsQIMN.js +1 -0
  144. package/apps/web/dist/assets/{SetupPage-YFoU_nXK.js → SetupPage-PzvPPLfM.js} +1 -1
  145. package/apps/web/dist/assets/{SharePage-BBHiJ34e.js → SharePage-7vdDWG1n.js} +1 -1
  146. package/apps/web/dist/assets/changelog-BFdAYD28.js +1 -0
  147. package/apps/web/dist/assets/{esm-hSq-jeOG.js → esm-g0kaGp8t.js} +1 -1
  148. package/apps/web/dist/assets/{index-CSeBGwy0.css → index-Bi2x2g7n.css} +1 -1
  149. package/apps/web/dist/assets/{index-DB_iwB35.js → index-BzPid1oQ.js} +4 -4
  150. package/apps/web/dist/assets/lock-BK9RYIft.js +1 -0
  151. package/apps/web/dist/assets/oss-credits-BJrR7BR1.js +1 -0
  152. package/apps/web/dist/assets/{phone-CeqyvwK-.js → phone-DtkY0NfU.js} +1 -1
  153. package/apps/web/dist/assets/{settings-menu-BK8WpcGv.js → settings-menu-7EJQRFOi.js} +1 -1
  154. package/apps/web/dist/assets/{ui-BbIh6SOJ.js → ui-B6S5hYPd.js} +3 -3
  155. package/apps/web/dist/assets/{upload-BQrAlHIG.js → upload-rYCAy7ox.js} +1 -1
  156. package/apps/web/dist/index.html +3 -3
  157. package/apps/web/dist/mcp/flow.html +4 -4
  158. package/bin/wizards.mjs +1 -1
  159. package/node_modules/@engenty-wizards/plugin-sdk/dist/index.d.ts +33 -14
  160. package/node_modules/@engenty-wizards/plugin-sdk/dist/studio.d.ts +3 -3
  161. package/node_modules/@engenty-wizards/shared/dist/definition.d.ts +690 -690
  162. package/node_modules/@engenty-wizards/shared/dist/definition.js +1 -1
  163. package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns.d.ts +76 -76
  164. package/node_modules/@engenty-wizards/shared/dist/feature-flags.d.ts +3 -3
  165. package/node_modules/@engenty-wizards/shared/dist/feature-flags.js +3 -3
  166. package/node_modules/@engenty-wizards/shared/dist/knowledge.d.ts +13 -13
  167. package/node_modules/@engenty-wizards/shared/dist/knowledge.js +4 -4
  168. package/node_modules/@engenty-wizards/shared/dist/marketplace.d.ts +4 -4
  169. package/node_modules/@engenty-wizards/shared/dist/projects.js +1 -1
  170. package/node_modules/@engenty-wizards/shared/dist/run.d.ts +48 -48
  171. package/node_modules/@engenty-wizards/shared/dist/run.js +48 -48
  172. package/node_modules/@engenty-wizards/shared/dist/space-data.d.ts +2 -2
  173. package/node_modules/@engenty-wizards/shared/dist/surface.d.ts +20 -20
  174. package/node_modules/@engenty-wizards/shared/dist/wizard-package.js +1 -1
  175. package/package.json +5 -4
  176. package/apps/web/dist/assets/AboutDialog-BtAuA44m.js +0 -1
  177. package/apps/web/dist/assets/EditorPage-B4jX_Ckc.js +0 -3
  178. package/apps/web/dist/assets/ReadOnly-DFysMEMF.js +0 -1
  179. package/apps/web/dist/assets/SettingsPage-BKkOfz6P.js +0 -1
  180. package/apps/web/dist/assets/changelog-D8WjOq7d.js +0 -1
  181. package/apps/web/dist/assets/circle-alert-BVj6idzu.js +0 -1
  182. package/apps/web/dist/assets/lock-BoWS-JhN.js +0 -1
  183. package/apps/web/dist/assets/oss-credits-BQQX0kvk.js +0 -1
package/README.md CHANGED
@@ -10,32 +10,28 @@
10
10
  Let AI do the work.
11
11
  </p>
12
12
 
13
- <p align="center">
14
- <img src="docs/assets/setup.png" alt="The first start of engenty wizards: choose the engine it thinks with — Codex, Claude Code, Gemini CLI, Cursor Agent or your own keys">
15
- </p>
16
-
17
13
  ## What it does
18
14
 
19
- - **Describe it, get a wizard.** Say in plain words what you do again and again — a post with
20
- an image, a research briefing, an invoice, a quote, a damage report — and engenty builds a
15
+ - **Describe it, get a wizard.** Say in plain words what you do again and again – a post with
16
+ an image, a research briefing, an invoice, a quote, a damage report – and engenty builds a
21
17
  wizard for it: pages that ask what it needs, AI steps that do the work.
22
18
  - **AI steps do the work.** They research the web, write, draw images, render video, read PDFs,
23
- scans and mails, build documents and dashboards, and write into other services over MCP, HTTP
19
+ scans and emails, build documents and dashboards, and write into other services over MCP, HTTP
24
20
  or a browser. Anything that changes an account asks first.
25
21
  - **Share it with a link.** Anyone can run a wizard in the browser, on a phone too: camera,
26
22
  code scan, voice notes, location and a drawn signature included. Results download as PDF,
27
- Word, HTML, Markdown, PNG, MP4, CSV, Excel or JSON, or go out as a link.
28
- - **Runs on your machine, on your subscription.** It thinks with Codex, Claude Code, Gemini CLI
23
+ Word, HTML, Markdown, PNG, MP4, MP3, CSV, Excel or JSON, or go out as a link.
24
+ - **Runs on your machine, on your subscription.** It uses Codex, Claude Code, Gemini CLI
29
25
  or Cursor Agent when one is installed and signed in, so no API key is needed. Or bring a key
30
26
  (Vercel AI Gateway, OpenAI, Anthropic), or a local model through Ollama. Your wizards and
31
27
  results stay in a folder on your machine.
32
28
 
33
29
  ## How it looks
34
30
 
35
- The studio: a wizard's steps as a flow with its branches, next to the conversation that changes them.
31
+ The studio: a wizard’s steps as a flow with its branches, next to the conversation that changes them.
36
32
 
37
33
  <p align="center">
38
- <img src="docs/assets/studio.png" alt="The studio of engenty wizards: the flow of a wizard that collects a month's receipts for bookkeeping — two pages of questions, AI steps that branch to the inbox and to customer portals, a review and the result — with the conversation beside it">
34
+ <img src="docs/assets/studio.png" alt="The studio of engenty wizards: the flow of a wizard that collects a month's receipts for bookkeeping – two pages of questions, AI steps that branch to the inbox and to customer portals, a review and the result – with the conversation beside it">
39
35
  </p>
40
36
 
41
37
  A wizard in use: it asks on one page, then on the next, and ends with the result to download or share.
@@ -46,14 +42,14 @@ A wizard in use: it asks on one page, then on the next, and ends with the result
46
42
 
47
43
  ## Get started
48
44
 
49
- Create an account at [engenty.ai](https://engenty.ai): it is in a closed beta, so "Get started"
50
- takes your e-mail for an invitation, or your invitation code. Signed in, the studio asks who
45
+ Create an account at [engenty.ai](https://engenty.ai): it is in a closed beta, so “Get started”
46
+ takes your email for an invitation, or your invitation code. Signed in, the studio asks who
51
47
  your team is, reads your website into your space, takes first documents, and you build your
52
- first wizard in the browser. Links run day and night on the team's credits.
48
+ first wizard in the browser. Links run day and night on the team’s credits.
53
49
 
54
50
  ## Install it on your computer
55
51
 
56
- The same account on your own machine: it thinks with your Claude, ChatGPT, Gemini or Cursor
52
+ The same account on your own machine: it runs on your Claude, ChatGPT, Gemini or Cursor
57
53
  subscription, reads your local files and browses for you, and what you publish keeps running in
58
54
  the cloud behind the same links. On macOS or Linux, in a terminal:
59
55
 
@@ -65,19 +61,23 @@ It needs nothing but `curl` and `tar` and asks for no password. It brings its ow
65
61
  Node you already have is left alone), installs engenty wizards into `~/.engenty/wizards` and
66
62
  then walks you through the rest:
67
63
 
68
- - **Your engenty account.** A code and the browser for the sign-in; your team's space comes
69
- down to this computer. "Later" skips it, `wizards account sign-in` does it any time.
64
+ - **Your engenty account.** “Sign in now?”: a code and the browser for the sign-in; what you
65
+ publish on this computer then runs in the cloud too. Say no, and `wizards account sign-in`
66
+ does it any time.
70
67
  `--local` leaves the account out: nothing leaves the computer, links answer on it only
71
68
  ([local only](https://engenty.ai/docs/user/local-only)).
72
- - **Something to think with.** It looks for Codex, Claude Code, Gemini CLI and Cursor Agent and
73
- offers to install one if there is none — first the one whose app (Claude, ChatGPT, Cursor) is
69
+ - **An AI client.** It looks for Codex, Claude Code, Gemini CLI and Cursor Agent and
70
+ offers to install the missing ones – first the one whose app (Claude, ChatGPT, Cursor) is
74
71
  on your Mac, as its subscription is most likely yours already. The first page of the studio
75
- installs them too. Or use your account's credits, an API key or [Ollama](https://ollama.com).
76
- - **Google Chrome and ffmpeg** (optional): Chrome for PDF and PNG exports and for steps that use
77
- a browser, ffmpeg for MP4 videos of animated widgets.
72
+ installs them too. Or use your account’s credits, an API key, [Ollama](https://ollama.com) or,
73
+ on a Mac with Apple silicon and macOS 26, Apple Intelligence.
74
+ - **A browser and ffmpeg** (optional): Chrome, Edge or Chromium (on a Mac also Brave) for PDF
75
+ and PNG exports and for steps that use a browser, ffmpeg for MP4 videos of animated widgets.
78
76
  - **Start at login** (optional): engenty wizards starts in the background when you log in (a
79
77
  LaunchAgent on a Mac, a systemd user service on Linux). Links and AI clients then always
80
78
  reach it. `wizards autostart on` or `off` switches it later.
79
+ - **Your AI apps** (optional): adds your wizards to the AI apps it finds, such as Claude
80
+ Desktop, Cursor or Codex. `wizards connect` and `wizards disconnect` do it later.
81
81
 
82
82
  It starts nothing. Afterwards:
83
83
 
@@ -86,6 +86,7 @@ wizards start
86
86
  ```
87
87
 
88
88
  starts it and opens the studio; it runs while that terminal is open, Ctrl-C stops it.
89
+ `wizards start -s` starts it in the background instead.
89
90
  `wizards` opens a menu with all of it, `wizards status` says what is installed and what runs,
90
91
  `wizards setup` runs the guided setup again, and `wizards --help` lists the rest.
91
92
 
@@ -99,29 +100,30 @@ You need Node.js 24.11 or newer and pnpm 10 (`corepack enable`).
99
100
 
100
101
  ```bash
101
102
  git clone https://github.com/engenty/wizards.git
102
- cd engenty-wizards
103
+ cd wizards
103
104
  pnpm install
104
105
  pnpm build
105
106
  pnpm wizards
106
107
  ```
107
108
 
108
- pnpm may list "Ignored build scripts" — that's expected, nothing to do.
109
+ pnpm may list “Ignored build scripts” – that’s expected, nothing to do.
109
110
 
110
111
  Code steps (shell, Node, Python) run in a sandbox when Docker and the sandbox image are on the
111
112
  machine; a checkout brings its own sandbox, the installed version has none yet.
112
113
 
113
114
  ## First start
114
115
 
115
- The setup shows your account at the top and asks what engenty should think with. Pick an AI
116
- client that is installed on your computer — engenty uses your existing subscription and signs
117
- it in for you in a terminal right on the page — your account's credits, or enter your own keys.
116
+ The setup shows your account at the top and asks how the AI should run. Pick an AI
117
+ client that is installed on your computer – engenty uses your existing subscription and signs
118
+ it in for you in a terminal right on the page – your account’s credits, Apple Intelligence on a
119
+ Mac that has it, or your own keys.
118
120
  It stays until a test call works, then the studio opens.
119
121
 
120
- Change it later under Settings.
122
+ Change it later under Settings → Models.
121
123
 
122
124
  ## Using it
123
125
 
124
- - **Make a wizard:** describe it in the studio's chat, or start from one of the starters.
126
+ - **Make a wizard:** describe it in the studio’s chat, or start from one of the templates.
125
127
  You see the steps as a diagram and can change any of them.
126
128
  - **Run and share:** try it yourself, then share the link. Shared results are kept for 7 days
127
129
  for people without an account.
@@ -129,8 +131,9 @@ Change it later under Settings.
129
131
  GitHub, HubSpot and S3 are built in; any other service can be added from its API description
130
132
  or MCP server through the [integrations.sh](https://integrations.sh) registry. Services that
131
133
  sign in with OAuth need a few settings first: [Connectors](docs/development.md#connectors).
132
- - **Build from Claude Code or Codex:** Settings → "Build with Claude Code & co." creates a key
133
- and shows the command to add engenty wizards to your AI client.
134
+ - **Build from Claude Code or Codex:** Settings → Integrate adds engenty wizards to your AI
135
+ apps (Claude Desktop, Claude Code, Codex, Cursor and others), or `wizards connect` from a
136
+ terminal.
134
137
 
135
138
  Your machine only answers on `localhost`. To let others open shared wizards, run it on a server.
136
139
 
@@ -149,12 +152,12 @@ Back up the folder; delete it to start over. Settings go into `~/.engenty/wizard
149
152
  wizards update
150
153
  ```
151
154
 
152
- This runs the installer again without its questions. The studio says when a newer release is
153
- out and can run the same update from its footer.
155
+ This runs the installer again without its questions. The studio says in a banner at the top
156
+ when a newer release is out and can run the same update from there or from its About dialog.
154
157
  From source: `git pull`, `pnpm install`, `pnpm build`. Your data is brought up to date at the
155
158
  next start.
156
159
 
157
- To remove engenty wizards, run `wizards autostart off`, then delete `~/.engenty/wizards` (your data is in its `data` folder) and
160
+ To remove engenty wizards, run `wizards stop`, `wizards autostart off` and `wizards disconnect`, then delete `~/.engenty/wizards` (your data is in its `data` folder) and
158
161
  `~/.local/bin/wizards`.
159
162
 
160
163
  ## Run it on a server
@@ -163,24 +166,24 @@ With Docker, behind an https proxy (Caddy, Traefik, Coolify):
163
166
 
164
167
  ```bash
165
168
  git clone https://github.com/engenty/wizards.git
166
- cd engenty-wizards
169
+ cd wizards
167
170
  cp .env.example .env # set APP_URL to your https address and add a model key
168
171
  docker compose up -d --build
169
172
  docker compose logs # shows the link to open once
170
173
  ```
171
174
 
172
- Point the proxy at port 8891. On a server, engenty thinks with your keys or Ollama; the
173
- AI clients' subscriptions live on your own computer. The code sandbox is off in this image.
175
+ Point the proxy at port 8891. On a server, engenty uses your keys or Ollama; the
176
+ AI clients’ subscriptions live on your own computer. The code sandbox is off in this image.
174
177
 
175
178
  ## Troubleshooting
176
179
 
177
180
  | | |
178
181
  |---|---|
179
- | "This link is no longer valid" | The link works once. `wizards open` gives this browser a new one. |
182
+ | “This link is no longer valid” | The link works once. `wizards open` gives this browser a new one. |
180
183
  | `wizards`: command not found | Open a new terminal, or run `~/.local/bin/wizards`. |
181
184
  | You open it under another address | Set `APP_URL` in `~/.engenty/wizards/.env` to the address you open. |
182
- | An AI client shows as not signed in | Sign it in on the setup page, or in your own terminal (e.g. `claude`, `codex login`), then "Check again". |
183
- | PDF or PNG export fails | Install Google Chrome, or set `CHROME_PATH` to Chrome or Chromium. |
185
+ | An AI client shows as not signed in | Sign it in on the setup page, or in your own terminal (e.g. `claude`, `codex login`), then “Check again”. |
186
+ | PDF or PNG export fails | Install Chrome, Edge or Chromium (on a Mac also Brave), or set `CHROME_PATH` to one. |
184
187
  | Port 24368 is taken | The next free port is used. To fix one, set `API_PORT` in `~/.engenty/wizards/.env`. |
185
188
  | Something else | `wizards doctor` checks the install. |
186
189
 
@@ -194,12 +197,12 @@ Development server, checks, how it is built, the managed mode:
194
197
  Plugins add tools for steps, routes with their own tables and pages in the studio, without
195
198
  changing the runtime: [docs/content/dev/plugins](docs/content/dev/plugins/index.md).
196
199
 
197
- The documentation — a user guide and the developer docs — is in [docs/content](docs/content)
200
+ The documentation – a user guide and the developer docs – is in [docs/content](docs/content)
198
201
  and is a site of its own: `pnpm dev:docs`.
199
202
 
200
203
  ## License
201
204
 
202
- [FSL-1.1-MIT](LICENSE): use it for yourself and your company, run it, change it — just don't
205
+ [FSL-1.1-MIT](LICENSE): use it for yourself and your company, run it, change it – just don’t
203
206
  offer it to others as a competing product or service. Each release becomes MIT two years
204
207
  after it is published. [COPYRIGHTS.md](COPYRIGHTS.md) names the owner, the marks, what is not ours and the
205
208
  third-party software with its licences.
@@ -24,7 +24,7 @@ export function signalText(taken) {
24
24
  const MAX_STEPS = 24;
25
25
  const SYSTEM = `You are Goose, the assistant of the engenty wizards studio. You stand on every page of the studio except the wizard editor, which has a chat of its own: the space's pages, the settings, the plugins' pages. You help the person who works here understand the app, set it up and find their way.
26
26
 
27
- You talk to the person. Answer in their language, briefly and warmly: a few sentences, a short list where it helps. Never mention JSON, ids, keys or tool names. Call it a space (German „Space“), never a project: the tools say project, the app says space.
27
+ You talk to the person. Answer in their language, briefly and warmly: a few sentences, a short list where it helps. Never mention JSON, ids, keys or tool names. Call it a space, never a project: the tools say project, the app says space. Name the studio's parts and pages as the studio shows them in the person's language.
28
28
 
29
29
  ${PRIMER}
30
30
 
@@ -42,9 +42,9 @@ THE PAGE
42
42
  - fill_field never saves: say that the person checks and saves. You never press buttons for the person.
43
43
 
44
44
  THE SPACE
45
- - Where a space's tools are there, you also fill the space in: what every wizard of the space draws on — who they are (title, about), the brand (logos, colours), assets (images, graphics, videos), documents, and facts (label → value: address, VAT id, phone, opening hours — anything). The space as it is now comes with every message then; you change it ONLY through tools.
46
- - A website: read_website it first. You get the page's text, colours from its stylesheet, logo candidates and the links to imprint / contact / about. Read those pages too (at most four more) for address, VAT id, register number, phone, email, bank details, who runs it.
47
- - Facts: one fact per thing, a short label in the person's language ("Adresse", "UID", "Telefon"), the value on one line where it fits. Only what a source or the person states — never guess, never invent. Keep the facts that are there; a fact with a known label is updated.
45
+ - Where a space's tools are there, you also fill the space in: what every wizard of the space draws on — who they are (title, about), the brand (logos, colours), assets (images, graphics, videos), documents, and facts (label → value: address, VAT ID, phone, opening hours — anything). The space as it is now comes with every message then; you change it ONLY through tools.
46
+ - A website: read_website it first. You get the page's text, colours from its stylesheet, logo candidates and the links to imprint / contact / about. Read those pages too (at most four more) for address, VAT ID, register number, phone, email, bank details, who runs it.
47
+ - Facts: one fact per thing, a short label in the person's language ("Adresse", "USt-IdNr.", "Telefon"), the value on one line where it fits. Only what a source or the person states — never guess, never invent. Keep the facts that are there; a fact with a known label is updated.
48
48
  - About: two to five sentences — what they do, for whom, what sets them apart, the tone they write in. Rewrite it only when you learned something new or the person asks.
49
49
  - Colours: two to six, the main one first, each with a name that says what it is for ("Primär", "Hintergrund", "Text"). Take them from the stylesheet's variables or its most used colours, never from photos. set_colors replaces the list: pass all colours that should remain. Colours in no section are the standard ones; the first of them is the accent. Colour sections are optional like logo sections: only for a further palette a brand guide or the person names for its own use ("Illustration – drawings and charts"), never for the standard colours. A brand's gradients (set_gradients, as CSS) only where a brand guide or the site's stylesheet has them; patterns and background fills are pictures the person uploads.
50
50
  - Logos: save the best candidate (an SVG or a large PNG rather than a favicon); a second one only if it truly differs (for dark ground, a signet). A few images that show the business (header, team, products) may be saved as assets — a handful, never everything.
@@ -52,7 +52,7 @@ THE SPACE
52
52
  - Typography: the brand's fonts (set_typography), each with what it is for ("Headings", "Body text") and the weights it uses — from the website's stylesheet (read_website lists its fonts) or a brand guide; system and generic faces are no brand fonts. A font's licence and what it may be used for (web, documents, print, images, video) only where a brand guide, the font's own page or the person says so; the wizards keep to it. A type scale (text styles: name, size, weight, line height, letter spacing) only when a brand guide gives one.
53
53
  - Asset sections are optional too: tabs of one kind each ("Icons", "Background images"), made with set_asset_sections only when the space holds enough assets of clearly different kinds or the person asks; a handful of pictures stay in the general tab.
54
54
  - Files the person attached are stored already; the message names them. look_at_file shows you a picture or a document's text. Give a file a description with describe_file when it has none or the person says what it is. Take facts from a document when asked.
55
- - Wissen (German „Wissen“) is what the wizards' steps look things up in: pages, tables and files, kept apart by typed Kategorien. Documents the person attaches are read into Wissen by themselves: say so; do not copy their text into facts unless asked. A website, a sitemap or a folder whose entries should go into Wissen is a source: where a tool proposes sources, use it (the person takes the proposal on its card); where there is none, say that sources need the Sources plugin.
55
+ - Knowledge is what the wizards' steps look things up in: pages, tables and files, kept apart by typed categories. Documents the person attaches are read into Knowledge by themselves: say so; do not copy their text into facts unless asked. A website, a sitemap or a folder whose entries should go into Knowledge is a source: where a tool proposes sources, use it (the person takes the proposal on its card); where there is none, say that sources need the Sources plugin.
56
56
  - Talking from another page than Info & brand, change title, about, logos, colours and facts only when the person asks.
57
57
  - A space that is read-only (synced from a local install, or a team that builds elsewhere) has no tools: say where it is changed.
58
58
 
@@ -113,9 +113,9 @@ async function projectState(userId, projectId) {
113
113
  const files = await projectFiles(projectId);
114
114
  const categories = await categoryRows(projectId);
115
115
  return {
116
- wissen: {
116
+ knowledge: {
117
117
  ...(await knowledgeCounts(projectId)),
118
- kategorien: categories.map((c) => `${c.name} (${c.type}${c.proposed ? ", proposed" : ""})`),
118
+ categories: categories.map((c) => `${c.name} (${c.type}${c.proposed ? ", proposed" : ""})`),
119
119
  },
120
120
  title: p.brand.name ?? "",
121
121
  about: p.brand.about ?? "",
@@ -799,7 +799,7 @@ export async function runStudioAssistant(input) {
799
799
  ? `The space "${project.name}" is read-only here: nothing of it can be changed in this studio.`
800
800
  : "No space is open.",
801
801
  part === "knowledge"
802
- ? "The person talks to you from Wissen: they want the wizards to know something."
802
+ ? "The person talks to you from Knowledge: they want the wizards to know something."
803
803
  : "",
804
804
  space && !(brandOn.brandSections && brandOn.brandRules && brandOn.typography)
805
805
  ? `Not in this studio (no tools for it; never offer it): ${[
@@ -23,7 +23,7 @@ import { PLUGIN_NAME, pluginArchive, pluginMarketplace } from "./plugin.js";
23
23
  import { apiRoutes } from "./routes/api.js";
24
24
  import { browserReport, localDebugRoutes } from "./routes/debug.js";
25
25
  import { internalRoutes } from "./routes/internal.js";
26
- import { inLang, requestLang } from "./routes/lang.js";
26
+ import { inLang, notFoundAnswer, requestLang } from "./routes/lang.js";
27
27
  import { marketplaceStudio } from "./routes/marketplace.js";
28
28
  import { publicPluginRoutes } from "./routes/plugin-public.js";
29
29
  import { pluginRoutes } from "./routes/plugins.js";
@@ -67,7 +67,7 @@ const ticketedRun = /^\/api\/runs\/([^/]+)(\/|$)/;
67
67
  app.use("*", async (c, next) => {
68
68
  if (!managed && !hostAllowed(c.req.header("host") ?? new URL(c.req.url).host)) {
69
69
  // Says where it does answer: the studio opened under another name shows the way there.
70
- return c.json({ error: "Forbidden host", code: "host", appUrl: env.appUrl }, 403);
70
+ return c.json({ error: "This address isn’t allowed.", code: "host", appUrl: env.appUrl }, 403);
71
71
  }
72
72
  const origin = c.req.header("origin");
73
73
  const path = c.req.path.startsWith(`${basePath}/`)
@@ -201,7 +201,10 @@ if (serves.studio) {
201
201
  app.use("/api/studio/*", async (c, next) => {
202
202
  const user = await principalOf(c);
203
203
  if (!user) {
204
- return c.json({ error: "unauthorized" }, 401);
204
+ return c.json({
205
+ error: inLang(c, "Du bist nicht angemeldet.", "You’re not signed in."),
206
+ code: "unauthorized",
207
+ }, 401);
205
208
  }
206
209
  if ((await tenantStatus(user.tenantId)) === "suspended") {
207
210
  return c.json({
@@ -257,7 +260,7 @@ if (serves.studio) {
257
260
  return c.body(new Uint8Array(zip), 200, { "content-type": "application/zip" });
258
261
  });
259
262
  }
260
- app.all("/api/*", (c) => c.json({ error: "not found" }, 404));
263
+ app.all("/api/*", (c) => c.json(notFoundAnswer(c), 404));
261
264
  /**
262
265
  * `/w/<token>/<runner>`: a plugin's page runner, where the tenant has it and the wizard offers
263
266
  * it; otherwise the next handler (the app's page, which knows `steps`, `chat` and run ids).
@@ -10,7 +10,7 @@ Words
10
10
  - Studio: where wizards are built. Home lists the space's wizards; the editor (/edit/<id>) shows a wizard's flow as a diagram with its own chat (the architect) that builds and changes it.
11
11
  - Wizard: a flow of steps a person walks through on a link. Step kinds: a page (fields to fill in), an AI step (an agent with tools: web search, reading files, writing documents, images, video, pages, other services), a review, and the result.
12
12
  - Run: one person going through a wizard once. Results lists every run that reached its result.
13
- - Space: what all wizards of a team know. Its page has four parts: Info & brand (title, about, logos, colours, assets, facts), Wissen / Knowledge (pages, tables and files the AI steps look things up in, kept apart by typed Kategorien), Data (tables and pages the wizards keep) and Results (finished runs).
13
+ - Space: what all wizards of a team know. Its page has four parts: Info & brand (title, about, logos, colours, assets, facts), Knowledge (pages, tables and files the AI steps look things up in, kept apart by typed categories), Data (tables and pages the wizards keep) and Results (finished runs).
14
14
  - Model class: the kind of model a step needs (Classifier, Standard, High, Highest for text; Image, Video, Speech, Audio). Which model serves a class is a setting (Settings › Models), not part of the wizard.
15
15
 
16
16
  Thinking: an installed AI client on the person's subscription (Claude Code, Codex, Gemini CLI, Cursor Agent), own API keys (OpenAI, Anthropic, Google, fal.ai, ElevenLabs, Replicate, Vercel AI Gateway), a local model (Ollama), or engenty credits from an account. Settings › Models shows which way serves each class and what is missing.
@@ -184,7 +184,7 @@ export const authRoutes = new Hono()
184
184
  usedTickets.set(ticket, expires);
185
185
  }
186
186
  else if (managed || !accessKey || !sameValue(given, accessKey)) {
187
- return c.text("This link is no longer valid. Start the app again.", 403);
187
+ return c.text("This link is no longer valid. Run wizards open to get a new one.", 403);
188
188
  }
189
189
  else {
190
190
  accessKey = null;
@@ -285,7 +285,7 @@ export const authRoutes = new Hono()
285
285
  }
286
286
  catch (err) {
287
287
  console.error("[account]", err);
288
- return c.html(closePage("The sign-in did not work. Please try again."), 400);
288
+ return c.html(closePage("The sign-in didn’t work. Try again."), 400);
289
289
  }
290
290
  }
291
291
  const state = unseal(getCookie(c, STATE_COOKIE) ?? "");
@@ -24,7 +24,7 @@ type List = {
24
24
  key?: columnId // rows are matched on this column: saving a row with a known key updates that row
25
25
  shared?: true // ONE list for every run (leads, sign-ups, bookings, a log) instead of one per person.
26
26
  // The person running the wizard never sees it: no list field, review or deliverable of it;
27
- // the admin sees and edits it in the studio under Space → Daten
27
+ // the admin sees and edits it in the studio under Space → Data
28
28
  check?: { file?: columnId, status?: columnId }
29
29
  // a review that shows the list lets the person go through it ROW BY ROW on a split screen:
30
30
  // "file" = a text column holding the path of a kept file (PDF, photo), previewed beside the row;
@@ -151,10 +151,10 @@ type AgentStep = {
151
151
  // Every agent step also has, without listing them: list_read / list_write (the wizard's lists), files_list,
152
152
  // read_document (PDF, scans and photos, Word, Excel, CSV, saved mails) and scan_documents (invoice / receipt fields, many at once)
153
153
  // — on an installed client only where there is something to read: tools or connections, uploads, lists or kept files;
154
- // and where the space has Wissen: project_search (a question, "where" on its typed Kategorien, hits checked for relevance),
155
- // project_list (by Kategorien), page_read / table_read / project_document (an item whole, by its path).
154
+ // and where the space has Knowledge: project_search (a question, "where" on its typed categories, hits checked for relevance),
155
+ // project_list (by categories), page_read / table_read / project_document (an item whole, by its path).
156
156
  // "pages" brings: page_read / page_write — Markdown pages of the space the wizard keeps for every run (notes, a log
157
- // that grows with append), shown to the admin under Space → Daten; page_read also reads Wissen by path.
157
+ // that grows with append), shown to the admin under Space → Data; page_read also reads Knowledge by path.
158
158
  // "browser" brings: browser_open/read/click/type/select/scroll/press/back/wait, browser_screenshot (the model looks at the page),
159
159
  // browser_run_fast (a decision model clicks through a page's mechanical part, where one is reached),
160
160
  // browser_request_credentials (the PERSON types a login into the page — the model never sees it; sign-ins can be remembered),
@@ -243,9 +243,9 @@ type ResultStep = { type: "result", message?: string, deliverables: { from: step
243
243
 
244
244
  Templates (in instructions/prompt): {{fieldId}} · {{steps.stepId}} (whole output) · {{steps.stepId.key}} (json key)
245
245
  · {{itemsField}} (line items as a table WITH computed net/VAT/total) · {{itemsField.net|vat|gross}} · {{today}}
246
- · the project (its settings): {{brand.name}} {{brand.about}} {{brand.colors}} · {{facts.key}} (one fact: address, VAT id, …) · {{facts}} (all facts)
247
- — every AI step already knows them, name one only where the text must carry it; agent steps also search the space's Wissen
248
- and are told its Kategorien: say in a step's instructions which Kategorie to filter by when the task names one
246
+ · the project (its settings): {{brand.name}} {{brand.about}} {{brand.colors}} · {{facts.key}} (one fact: address, VAT ID, …) · {{facts}} (all facts)
247
+ — every AI step already knows them, name one only where the text must carry it; agent steps also search the space's Knowledge
248
+ and are told its categories: say in a step's instructions which category to filter by when the task names one
249
249
  · {{lists.listId}} (the stored list as a table, as it is when the step starts) · {{fileField}} (the names of the uploads, for read_document)
250
250
  · {{locationField}} {{locationField.lat|lng|accuracy|label|map}} · {{audioField}} (the transcript) · {{signatureField}} (asset://ID)
251
251
  A template may only use fields asked and steps run EARLIER.`;
@@ -261,7 +261,7 @@ How good wizards look:
261
261
  - Writing into other systems (CRM, database, spreadsheet, website): an agent step with the matching mcp server, http, or browser tool, preceded by a review step.
262
262
  ${sandboxGuideLine()}
263
263
  - Remembering between runs ("merke dir", "führe eine Liste"): declare a list and let an agent step save into it with list_write; a later run reads it as {{lists.id}} or list_read. Give it a key column so known rows are updated, not duplicated. Never make the person re-upload last time's result.
264
- - Collecting from everyone who runs the wizard (requests, leads, sign-ups, feedback): a list with "shared": true, saved with list_write. It is the admin's, under Space → Daten; never show one person's rows to another. Longer text for every run (a running summary, a log): the "pages" tool.
264
+ - Collecting from everyone who runs the wizard (requests, leads, sign-ups, feedback): a list with "shared": true, saved with list_write. It is the admin's, under Space → Data; never show one person's rows to another. Longer text for every run (a running summary, a log): the "pages" tool.
265
265
  - The person's mail: one connection of kind "mail", a page field of kind "connection" early in the wizard, and the agent step lists it in "connections". Do not ask for the provider or the address in other fields — the connect field does that.
266
266
  - Working in a service for the person (read their Notion, create an issue, look up a customer): import the service as a connector, declare a connection with "connector", put a page field of kind "connection" before the step, and list the connection in the step's "connections". Prefer a connector over the browser whenever the service has one.
267
267
  - Logins on websites: never ask for passwords in page fields. An agent step with the browser tool asks the person at the moment it meets the login (browser_request_credentials); write that into its instructions, and that it must go on without the site when the person skips.
@@ -4,9 +4,9 @@ import { DeviceSignInError, requestDeviceCode, waitForDeviceSignIn } from "../au
4
4
  import { migrateControlDb } from "../db/client.js";
5
5
  import { env } from "../env.js";
6
6
  import { featuresFile, fileFeatures, writeFeature } from "../features.js";
7
- import { command, readInstallNote, writeInstallNote } from "./home.js";
7
+ import { readInstallNote, writeInstallNote } from "./home.js";
8
8
  import { openBrowser, runningRuntime, settings } from "./start.js";
9
- import { bold, busy, cyan, dim, tilde } from "./ui.js";
9
+ import { bold, busy, cmd, cyan, dim, tilde } from "./ui.js";
10
10
  /**
11
11
  * The account of this install, from the terminal. `wizards account` says where it stands,
12
12
  * `sign-in` runs the device flow (auth/device.ts), `sign-out` ends the link the way the
@@ -73,7 +73,7 @@ export function described(account, team, credits = true) {
73
73
  export function describeAccount(state) {
74
74
  switch (state.kind) {
75
75
  case "local":
76
- return "local only";
76
+ return "only on this computer, no account";
77
77
  case "none":
78
78
  return "not signed in";
79
79
  default:
@@ -86,16 +86,18 @@ const host = () => new URL(env.local.accountUrl).host.replace(/^account\./, "");
86
86
  async function status() {
87
87
  const state = await accountState();
88
88
  if (state.kind === "local") {
89
- console.log(`Local only (no account). ${dim(`\`${command()} account sign-in\` connects one.`)}`);
89
+ console.log(`Only on this computer, no account. ${dim(`To connect one: ${cmd("account sign-in")}`)}`);
90
90
  return 0;
91
91
  }
92
92
  if (state.kind === "none") {
93
- console.log(`Not signed in. ${dim(`\`${command()} account sign-in\``)}`);
93
+ console.log(`Not signed in. ${dim(`To sign in: ${cmd("account sign-in")}`)}`);
94
94
  return 0;
95
95
  }
96
96
  const overview = await accountOverview();
97
97
  const team = overview?.tenant ?? state.team;
98
- console.log(`Signed in as ${described(state.account, team)} · ${host()}${overview ? "" : dim(` (${host()} did not answer${team ? "; this is from last time" : ""})`)}`);
98
+ console.log(`Signed in as ${described(state.account, team)} · ${host()}${overview
99
+ ? ""
100
+ : dim(` (${host()} didn’t respond${team ? ", so this is from last time" : ""})`)}`);
99
101
  return 0;
100
102
  }
101
103
  /**
@@ -112,21 +114,21 @@ async function deviceSignIn() {
112
114
  return null;
113
115
  }
114
116
  const link = code.verificationUriComplete ?? code.verificationUri;
115
- p.log.message(`Open ${cyan(link)} and type ${bold(code.userCode)}`);
117
+ p.log.message(`Open ${cyan(link)} and enter the code ${bold(code.userCode)}.`);
116
118
  if (openBrowser(link)) {
117
119
  p.log.message(dim("The page opens in your browser."));
118
120
  }
119
- const spinner = busy("Waiting for you to allow it in the browser");
121
+ const spinner = busy("Waiting for you to confirm in the browser…");
120
122
  try {
121
123
  const account = await waitForDeviceSignIn(code);
122
124
  const overview = await accountOverview();
123
- spinner.stop(`Signed in as ${described(account, overview?.tenant ?? null)}`);
125
+ spinner.stop(`Signed in as ${described(account, overview?.tenant ?? null)}.`);
124
126
  return account;
125
127
  }
126
128
  catch (err) {
127
129
  spinner.fail(err instanceof DeviceSignInError
128
130
  ? err.message
129
- : `The sign-in did not work. ${err.message}`);
131
+ : `The sign-in didn’t work. ${err.message}`);
130
132
  return null;
131
133
  }
132
134
  }
@@ -139,29 +141,28 @@ async function pickUp(paths, interactive, why) {
139
141
  if (!(await runningRuntime(settings(paths).dataDir))) {
140
142
  return;
141
143
  }
142
- const how = `${command()} restart`;
143
144
  if (!interactive) {
144
- p.log.info(`The studio that runs ${why} after ${cyan(how)}.`);
145
+ p.log.info(`Run ${cmd("restart")} so ${why}.`);
145
146
  return;
146
147
  }
147
148
  const answer = await p.confirm({
148
- message: `Restart the studio that runs, so it ${why}?`,
149
+ message: `Restart engenty wizards now, so ${why}?`,
149
150
  initialValue: true,
150
151
  });
151
152
  if (p.isCancel(answer) || !answer) {
152
- p.log.info(dim(`Later: ${how}`));
153
+ p.log.info(dim(`To do it later: ${cmd("restart")}`));
153
154
  return;
154
155
  }
155
156
  // status.ts shows the account; loaded here when needed, so neither module waits on the other.
156
157
  const { restart } = await import("./status.js");
157
158
  await restart(paths);
158
159
  }
159
- const SIGNED_IN = "fetches the Pro modules of your plan and sends what is published to the cloud";
160
+ const SIGNED_IN = "your plan’s Pro modules load and your published wizards arrive in the cloud";
160
161
  async function signIn(paths, interactive) {
161
162
  await ready();
162
163
  const linked = await linkedAccount();
163
164
  if (linked) {
164
- p.log.info(`Already signed in as ${described(linked, await knownTeam(), false)}. ${dim(`To switch: \`${command()} account sign-out\` first.`)}`);
165
+ p.log.info(`Already signed in as ${described(linked, await knownTeam(), false)}. ${dim(`To switch accounts, run ${cmd("account sign-out")} first.`)}`);
165
166
  return 0;
166
167
  }
167
168
  const account = await deviceSignIn();
@@ -176,10 +177,12 @@ async function signIn(paths, interactive) {
176
177
  async function signOut(paths, interactive) {
177
178
  const state = await accountState();
178
179
  if (state.kind !== "linked") {
179
- console.log(state.kind === "local" ? "Local only: there is no account to sign out." : "Not signed in.");
180
+ console.log(state.kind === "local"
181
+ ? "No account is connected, so there’s nothing to sign out of."
182
+ : "Not signed in.");
180
183
  return 0;
181
184
  }
182
- const spinner = busy(`Signing out ${state.account.name || state.account.email}`);
185
+ const spinner = busy(`Signing out ${state.account.name || state.account.email}…`);
183
186
  await unlink();
184
187
  // The same as the studio's sign-out (DELETE /api/studio/account): what was sent to the cloud
185
188
  // is forgotten, the entitlement goes with the account's plan, and models that ran on its
@@ -197,7 +200,7 @@ async function signOut(paths, interactive) {
197
200
  await models.saveLocalModels({ source: "own" });
198
201
  }
199
202
  spinner.stop("Signed out. Your wizards and results stay on this computer.");
200
- await pickUp(paths, interactive, "lets go of the account");
203
+ await pickUp(paths, interactive, "it disconnects from the account");
201
204
  return 0;
202
205
  }
203
206
  /** `wizards account [sign-in|sign-out]`. */
@@ -215,7 +218,7 @@ export async function accountCommand(paths, value, options) {
215
218
  case "logout":
216
219
  return signOut(paths, interactive);
217
220
  default:
218
- console.error(`${command()} account [sign-in|sign-out]`);
221
+ console.error(`Usage: ${cmd("account [sign-in|sign-out]")}`);
219
222
  return 2;
220
223
  }
221
224
  }
@@ -250,37 +253,37 @@ export async function accountMenu(paths) {
250
253
  export async function accountStep(paths, options, interactive) {
251
254
  if (options.local) {
252
255
  localOnly(paths);
253
- p.log.warn(`Local only. No account, nothing leaves this computer. Wizards, the space and results stay in ${tilde(settings(paths).dataDir)}. Links answer on this computer only.`);
254
- p.log.message(dim(`Connect an account any time: ${command()} account`));
256
+ p.log.warn(`No account: nothing leaves this computer. Your wizards, your space and results stay in ${tilde(settings(paths).dataDir)}. Links work only on this computer.`);
257
+ p.log.message(dim(`To connect an account any time: ${cmd("account sign-in")}`));
255
258
  return;
256
259
  }
257
260
  const state = await accountState();
258
261
  if (state.kind === "local") {
259
- p.log.info(`Local only (no account). ${dim(`\`${command()} account sign-in\` connects one.`)}`);
262
+ p.log.info(`Only on this computer, no account. ${dim(`To connect one: ${cmd("account sign-in")}`)}`);
260
263
  return;
261
264
  }
262
265
  if (state.kind === "linked") {
263
266
  const overview = await accountOverview();
264
- p.log.success(`Signed in as ${described(state.account, overview?.tenant ?? state.team)}`);
267
+ p.log.success(`Signed in as ${described(state.account, overview?.tenant ?? state.team)}.`);
265
268
  return;
266
269
  }
267
270
  p.note(INTRO, "Your engenty account");
268
271
  if (!interactive) {
269
- p.log.info(dim(`Sign in later: ${command()} account sign-in`));
272
+ p.log.info(dim(`To sign in later: ${cmd("account sign-in")}`));
270
273
  return;
271
274
  }
272
275
  const answer = await p.confirm({ message: "Sign in now?", initialValue: true });
273
276
  if (p.isCancel(answer)) {
274
- p.cancel(`Setup stopped. Run it again with \`${command()} setup\`.`);
277
+ p.cancel(`Setup stopped. To run it again: ${cmd("setup")}`);
275
278
  return "cancelled";
276
279
  }
277
280
  if (!answer) {
278
- p.log.info(dim(`Later: ${command()} account`));
281
+ p.log.info(dim(`To sign in later: ${cmd("account sign-in")}`));
279
282
  return;
280
283
  }
281
284
  const account = await deviceSignIn();
282
285
  if (!account) {
283
- p.log.info(dim(`Later: ${command()} account sign-in`));
286
+ p.log.info(dim(`To sign in later: ${cmd("account sign-in")}`));
284
287
  return;
285
288
  }
286
289
  joinedAccount(paths);
@@ -82,16 +82,18 @@ async function systemdHere() {
82
82
  }
83
83
  export async function autostartState(paths) {
84
84
  if (process.platform !== "darwin" && process.platform !== "linux") {
85
- return { unavailable: `not on ${process.platform}` };
85
+ return {
86
+ unavailable: process.platform === "win32" ? "not on Windows" : `not on ${process.platform}`,
87
+ };
86
88
  }
87
89
  if (!installedByScript(paths)) {
88
- return { unavailable: "only for an install made by install.sh" };
90
+ return { unavailable: "only for installs made with the install script" };
89
91
  }
90
92
  if (process.platform === "darwin") {
91
93
  return { on: existsSync(LOGIN_ITEM), how: MAC_HOW };
92
94
  }
93
95
  if (!(await systemdHere())) {
94
- return { unavailable: "this Linux has no systemd user session" };
96
+ return { unavailable: "this system has no systemd user session" };
95
97
  }
96
98
  try {
97
99
  await run("systemctl", ["--user", "is-enabled", "--quiet", UNIT], { timeout: 10_000 });
@@ -28,7 +28,7 @@ export function installClient(client, paths, onLine) {
28
28
  }
29
29
  const npm = findOnPath("npm", [dirname(process.execPath), process.env.PATH ?? ""].join(delimiter));
30
30
  if (!npm) {
31
- return Promise.resolve({ code: 127, tail: "npm was not found next to this Node." });
31
+ return Promise.resolve({ code: 127, tail: "npm wasn’t found next to this Node.js." });
32
32
  }
33
33
  mkdirSync(paths.clients, { recursive: true });
34
34
  return runLogged(npm, [