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
@@ -10,12 +10,12 @@ import { runLogged } from "./exec.js";
10
10
  import { command, installedByScript, isCheckout, packageVersion, readInstallNote, writeInstallNote, } from "./home.js";
11
11
  import { CLIENTS, detectClients, findChrome, findFfmpeg, findOnPath, suggestedClient, vendorApp, } from "./machine.js";
12
12
  import { runningRuntime, settings } from "./start.js";
13
- import { badge, busy, cyan, dim, tilde } from "./ui.js";
13
+ import { badge, busy, cmd, cyan, dim, tilde } from "./ui.js";
14
14
  /** The studio as an app of its own: Chrome (or Edge) installs it from the page. */
15
- const CHROME_APP = "Tip: in Chrome or Edge the studio installs as an app, in a window of its own with its own icon. The studio offers it at the top; or use the install icon in the address bar.";
15
+ const CHROME_APP = "Tip: in Chrome or Edge you can install the studio as an app, with its own window and icon. Use the offer at the top of the studio, or the install icon in the address bar.";
16
16
  function cancelled(value) {
17
17
  if (p.isCancel(value)) {
18
- p.cancel(`Setup stopped. Run it again with \`${command()} setup\`.`);
18
+ p.cancel(`Setup stopped. To run it again: ${cmd("setup")}`);
19
19
  return true;
20
20
  }
21
21
  return false;
@@ -29,19 +29,19 @@ async function install(client, paths) {
29
29
  task.success(`${client.name} ${installed.version ?? ""} installed`.trim());
30
30
  return true;
31
31
  }
32
- task.error(`${client.name} was not installed${code === 0 ? ": its command is not on the PATH" : ""}. ${tail.split("\n").pop() ?? ""}`);
32
+ task.error(`${client.name} didn’t install${code === 0 ? ": its command isn’t on the PATH" : ""}. ${tail.split("\n").pop() ?? ""}`);
33
33
  return false;
34
34
  }
35
35
  async function clientsStep(paths, options, interactive) {
36
- const spinner = busy("Looking for AI clients on this machine");
36
+ const spinner = busy("Looking for AI clients on this computer…");
37
37
  const found = await detectClients(paths);
38
38
  const installed = found.filter((state) => state.path);
39
39
  const missing = found.filter((state) => !state.path);
40
40
  spinner.stop(installed.length
41
- ? `Thinks with: ${installed.map((s) => `${s.client.name} ${s.version}`).join(", ")}`
42
- : "No AI client on this machine yet");
41
+ ? `Found AI clients: ${installed.map((s) => `${s.client.name} ${s.version}`).join(", ")}.`
42
+ : "No AI client on this computer yet.");
43
43
  if (!installed.length) {
44
- p.log.message(dim("engenty wizards thinks with one, on the subscription you already have."));
44
+ p.log.message(dim("engenty wizards uses an AI client to think, on a subscription you already have."));
45
45
  }
46
46
  let wanted = missing.filter((s) => options.clients.includes(s.client.id));
47
47
  if (interactive && missing.length && !wanted.length) {
@@ -50,8 +50,8 @@ async function clientsStep(paths, options, interactive) {
50
50
  const offered = [...missing].sort((a, b) => Number(!vendorApp(a.client)) - Number(!vendorApp(b.client)));
51
51
  const answer = await p.multiselect({
52
52
  message: installed.length
53
- ? "Install another AI client? (space to pick, enter to go on)"
54
- : "Which one shall I install?",
53
+ ? "Install another AI client? (space to choose, enter to continue)"
54
+ : "Which AI client should be installed?",
55
55
  options: offered.map(({ client }) => {
56
56
  const app = vendorApp(client);
57
57
  return {
@@ -79,10 +79,10 @@ async function clientsStep(paths, options, interactive) {
79
79
  any = (await install(client, paths)) || any;
80
80
  }
81
81
  if (any) {
82
- p.log.info("You sign the client in on the first page of the studio.");
82
+ p.log.info("Sign in to the AI client on the first page of the studio.");
83
83
  }
84
84
  else {
85
- p.log.warn(`No AI client yet: the first page of the studio installs one, or takes an API key (Vercel AI Gateway, OpenAI, Anthropic) or a local model (Ollama).\n${dim(`Or here, later: ${CLIENTS.map((c) => c.name).join(", ")} with \`${command()} setup\`.`)}`);
85
+ p.log.warn(`No AI client yet. The first page of the studio can install one, or you can use an API key (Vercel AI Gateway, OpenAI, Anthropic) or a local model (Ollama).\n${dim(`To install ${CLIENTS.map((c) => c.name).join(", ")} here later: ${cmd("setup")}`)}`);
86
86
  }
87
87
  return any;
88
88
  }
@@ -92,7 +92,7 @@ async function toolsStep(interactive) {
92
92
  p.log.success(`Browser for PDF and PNG: ${dim(tilde(chrome))}`);
93
93
  }
94
94
  else {
95
- p.log.warn("No Chrome, Edge or Chromium: PDF and PNG exports and browser steps need one.\nhttps://www.google.com/chrome");
95
+ p.log.warn("No Chrome, Edge or Chromium found. PDF and PNG exports and browser steps need one.\nhttps://www.google.com/chrome");
96
96
  }
97
97
  const ffmpeg = await findFfmpeg();
98
98
  if (ffmpeg.path) {
@@ -101,7 +101,7 @@ async function toolsStep(interactive) {
101
101
  }
102
102
  const brew = findOnPath("brew", process.env.PATH ?? "");
103
103
  if (!interactive || !brew) {
104
- p.log.warn(`No ffmpeg: MP4 videos of animated widgets need it.${brew ? `\n${dim("brew install ffmpeg")}` : ""}`);
104
+ p.log.warn(`No ffmpeg found. MP4 videos of animated widgets need it.${brew ? `\n${dim(`To install it: ${cyan("brew install ffmpeg")}`)}` : ""}`);
105
105
  return;
106
106
  }
107
107
  const answer = await p.confirm({
@@ -120,7 +120,7 @@ async function toolsStep(interactive) {
120
120
  task.success("ffmpeg installed");
121
121
  }
122
122
  else {
123
- task.error(`ffmpeg was not installed. ${tail.split("\n").pop() ?? ""}`);
123
+ task.error(`ffmpeg didn’t install. ${tail.split("\n").pop() ?? ""}`);
124
124
  }
125
125
  }
126
126
  }
@@ -138,18 +138,18 @@ async function autostartStep(paths, interactive) {
138
138
  return;
139
139
  }
140
140
  if (!interactive) {
141
- p.log.info(dim(`To start it at login: \`${command()} autostart on\``));
141
+ p.log.info(dim(`To start it when you log in: ${cmd("autostart on")}`));
142
142
  return;
143
143
  }
144
144
  const answer = await p.confirm({
145
- message: "Start engenty wizards when you log in? It keeps running in the background, so links and AI clients always reach it.",
145
+ message: "Start engenty wizards when you log in? It keeps running in the background, so links and your AI apps can always reach it.",
146
146
  initialValue: true,
147
147
  });
148
148
  if (cancelled(answer)) {
149
149
  return "cancelled";
150
150
  }
151
151
  if (!answer) {
152
- p.log.info(dim(`Later: \`${command()} autostart on\``));
152
+ p.log.info(dim(`To do it later: ${cmd("autostart on")}`));
153
153
  return;
154
154
  }
155
155
  try {
@@ -158,11 +158,11 @@ async function autostartStep(paths, interactive) {
158
158
  p.log.success(`Starts at login: ${dim(after.how)}`);
159
159
  }
160
160
  else {
161
- p.log.warn("Starting at login could not be switched on.");
161
+ p.log.warn("Couldn’t turn on start at login.");
162
162
  }
163
163
  }
164
164
  catch (error) {
165
- p.log.warn(`Starting at login could not be switched on. ${error.message}`);
165
+ p.log.warn(`Couldn’t turn on start at login. ${error.message}`);
166
166
  }
167
167
  }
168
168
  /**
@@ -180,15 +180,15 @@ async function appsStep(paths, interactive) {
180
180
  return;
181
181
  }
182
182
  if (!interactive) {
183
- p.log.info(dim(`To use your wizards in ${offered.map((s) => s.name).join(", ")}: \`${command()} connect\``));
183
+ p.log.info(dim(`To use your wizards in ${offered.map((s) => s.name).join(", ")}: ${cmd("connect")}`));
184
184
  return;
185
185
  }
186
186
  const answer = await p.multiselect({
187
- message: "Use your wizards in these AI apps? (space to pick, enter to go on)",
187
+ message: "Use your wizards in these AI apps? (space to choose, enter to continue)",
188
188
  options: offered.map((s) => ({
189
189
  value: s.id,
190
190
  label: s.name,
191
- hint: s.widgets ? "shows runs as a widget" : "as text",
191
+ hint: s.widgets ? "shows runs as a widget" : "shows runs as text",
192
192
  })),
193
193
  initialValues: offered.map((s) => s.id),
194
194
  required: false,
@@ -197,12 +197,12 @@ async function appsStep(paths, interactive) {
197
197
  return "cancelled";
198
198
  }
199
199
  if (!answer.length) {
200
- p.log.info(dim(`Later: \`${command()} connect\``));
200
+ p.log.info(dim(`To do it later: ${cmd("connect")}`));
201
201
  return;
202
202
  }
203
203
  for (const { app, outcome } of await connectApps(paths, answer)) {
204
204
  if (outcome.ok) {
205
- p.log.success(`${app.name}: ${dim(tilde(outcome.message))}`);
205
+ p.log.success(`${app.name}: ${dim(outcome.message)}`);
206
206
  }
207
207
  else {
208
208
  p.log.warn(`${app.name}: ${outcome.message}${outcome.snippet ? `\n${outcome.snippet}` : ""}`);
@@ -234,16 +234,16 @@ async function pathStep(interactive) {
234
234
  }
235
235
  const { file, line } = pathLine();
236
236
  if (existsSync(file) && readFileSync(file, "utf8").includes(".local/bin")) {
237
- p.log.info(`Open a new terminal to use the \`wizards\` command.`);
237
+ p.log.info(`Open a new terminal to use the ${cyan("wizards")} command.`);
238
238
  return;
239
239
  }
240
- const hint = `Until then: ${tilde(join(local, "wizards"))}`;
240
+ const hint = `Until then, use ${cyan(tilde(join(local, "wizards")))}`;
241
241
  if (!interactive) {
242
- p.log.warn(`~/.local/bin is not on your PATH. Add to ${tilde(file)}:\n${line}\n${dim(hint)}`);
242
+ p.log.warn(`~/.local/bin isn’t on your PATH. Add this to ${tilde(file)}:\n${line}\n${dim(hint)}`);
243
243
  return;
244
244
  }
245
245
  const answer = await p.confirm({
246
- message: `The \`wizards\` command is in ~/.local/bin, which is not on your PATH. Add it in ${tilde(file)}?`,
246
+ message: `The wizards command is in ~/.local/bin, which isn’t on your PATH. Add it in ${tilde(file)}?`,
247
247
  initialValue: true,
248
248
  });
249
249
  if (cancelled(answer)) {
@@ -251,7 +251,7 @@ async function pathStep(interactive) {
251
251
  }
252
252
  if (answer) {
253
253
  appendFileSync(file, `\n# engenty wizards\n${line}\n`);
254
- p.log.success(`Added to ${tilde(file)}. It works in a new terminal.\n${dim(hint)}`);
254
+ p.log.success(`Added to ${tilde(file)}. Open a new terminal to use it.\n${dim(hint)}`);
255
255
  }
256
256
  else {
257
257
  p.log.info(hint);
@@ -266,8 +266,12 @@ async function howToUse(paths) {
266
266
  const wizards = command();
267
267
  const row = (what, cmd, note = "") => `${what.padEnd(9)}${cyan(cmd.padEnd(wizards.length + 12))}${note ? dim(note) : ""}`;
268
268
  p.log.message([
269
- running ? `It runs now: ${cyan(running.url)}` : "It does not run yet. Nothing was started.",
270
- "unavailable" in atLogin || !atLogin.on ? "" : dim("It starts by itself when you log in."),
269
+ running
270
+ ? `engenty wizards is running: ${cyan(running.url)}`
271
+ : "engenty wizards isn’t running yet.",
272
+ "unavailable" in atLogin || !atLogin.on
273
+ ? ""
274
+ : dim("It starts automatically when you log in."),
271
275
  ]
272
276
  .filter(Boolean)
273
277
  .join("\n"));
@@ -275,15 +279,15 @@ async function howToUse(paths) {
275
279
  "From now on, in a terminal:",
276
280
  "",
277
281
  row("Start", `${wizards} start`, "runs in this terminal; Ctrl-C stops it"),
278
- row("", `${wizards} start -s`, "runs as a service; the terminal stays free"),
279
- row("Studio", `${wizards} studio`, "opens the studio; starts it first if needed"),
282
+ row("", `${wizards} start -s`, "runs in the background; the terminal stays free"),
283
+ row("Studio", `${wizards} studio`, "opens the studio, starting it first if needed"),
280
284
  row("Stop", `${wizards} stop`, "your data is kept"),
281
- row("Check", `${wizards} status`, "what runs and where"),
285
+ row("Check", `${wizards} status`, "shows what’s running and where"),
282
286
  row("Restart", `${wizards} restart`),
283
- row("All", wizards, "a menu with all of it"),
287
+ row("Menu", wizards, "everything in one menu"),
284
288
  "",
285
- dim("No need to run the installer again: it is installed. To update: ") +
286
- cyan(`${wizards} update`),
289
+ dim("It’s installed, so there’s no need to run the installer again. To update: ") +
290
+ cmd("update"),
287
291
  ].join("\n"));
288
292
  }
289
293
  /**
@@ -3,13 +3,13 @@ import { randomBytes } from "node:crypto";
3
3
  import { existsSync, mkdirSync, openSync, readFileSync } from "node:fs";
4
4
  import { createServer } from "node:net";
5
5
  import { join, resolve } from "node:path";
6
- import { styleText } from "node:util";
7
6
  import { mintLocalTicket } from "../auth/local-ticket.js";
8
7
  import { readRunning } from "../running.js";
9
8
  import { RESTART_EXIT } from "../update.js";
10
9
  import { runtimeEnv } from "./environment.js";
11
- import { command, packageRoot, readEnvFile } from "./home.js";
10
+ import { packageRoot, readEnvFile } from "./home.js";
12
11
  import { findOnPath } from "./machine.js";
12
+ import { cmd, cyan, dim, live, tilde } from "./ui.js";
13
13
  const DEFAULT_PORT = 24368;
14
14
  const START_TIMEOUT_MS = 60_000;
15
15
  /** The settings this install starts with: its `.env`, overridden by the terminal's variables. */
@@ -87,13 +87,13 @@ export function openBrowser(url) {
87
87
  function show(url, entry, open) {
88
88
  const opened = open && openBrowser(entry);
89
89
  console.log("");
90
- console.log(` ${styleText("green", "●")} engenty wizards is running: ${styleText("cyan", url)}`);
90
+ console.log(` ${live} engenty wizards is running: ${cyan(url)}`);
91
91
  if (opened) {
92
- console.log(styleText("dim", " The studio opens in your browser."));
92
+ console.log(dim(" The studio opens in your browser."));
93
93
  }
94
94
  else {
95
- console.log(` Open the studio: ${entry}`);
96
- console.log(styleText("dim", " The link lets one browser in and works once."));
95
+ console.log(` Open the studio: ${cyan(entry)}`);
96
+ console.log(dim(" The link signs in one browser and works only once."));
97
97
  }
98
98
  }
99
99
  /** The command of this install, as it is on disk now: `wizards <args>`. */
@@ -146,22 +146,23 @@ export async function start(paths, options) {
146
146
  const running = await runningRuntime(dataDir);
147
147
  if (running) {
148
148
  show(running.url, entryUrl(running, dataDir), options.open);
149
- console.log(styleText("dim", ` It was already running; stop it with \`${command()} stop\`.`));
149
+ console.log(dim(` It was already running. To stop it: ${cmd("stop")}`));
150
150
  return 0;
151
151
  }
152
152
  if (options.background) {
153
153
  const started = await startInBackground(paths);
154
154
  if (!started) {
155
- console.error(`engenty wizards did not start; see ${join(paths.logs, "runtime.log")}.`);
155
+ console.error(`engenty wizards didn’t start. Its log is in ${tilde(join(paths.logs, "runtime.log"))}.`);
156
156
  return 1;
157
157
  }
158
158
  show(started.url, entryUrl(started, dataDir), options.open);
159
- console.log(styleText("dim", ` It runs in the background; stop it with \`${command()} stop\`. Its log: ${join(paths.logs, "runtime.log")}`));
159
+ console.log(dim(` It’s running in the background. To stop it: ${cmd("stop")}`));
160
+ console.log(dim(` Its log is in ${tilde(join(paths.logs, "runtime.log"))}.`));
160
161
  return 0;
161
162
  }
162
163
  const port = wanted ?? (await freePort());
163
164
  if (!wanted && port !== DEFAULT_PORT) {
164
- console.log(styleText("dim", ` Port ${DEFAULT_PORT} is taken, using ${port}.`));
165
+ console.log(dim(` Port ${DEFAULT_PORT} is taken, so it uses ${port}.`));
165
166
  }
166
167
  const accessKey = randomBytes(24).toString("hex");
167
168
  const url = appUrl || `http://localhost:${port}`;
@@ -178,7 +179,7 @@ export async function start(paths, options) {
178
179
  let exited = null;
179
180
  const ended = new Promise((done) => {
180
181
  child.on("error", (error) => {
181
- console.error(`The runtime did not start: ${error.message}`);
182
+ console.error(`engenty wizards didn’t start: ${error.message}`);
182
183
  exited = 1;
183
184
  done(1);
184
185
  });
@@ -194,7 +195,7 @@ export async function start(paths, options) {
194
195
  while (exited === null && !(await healthy(port))) {
195
196
  if (Date.now() - began > START_TIMEOUT_MS) {
196
197
  child.kill("SIGTERM");
197
- console.error("The runtime did not answer within 60 seconds.");
198
+ console.error("engenty wizards didn’t respond within 60 seconds.");
198
199
  return 1;
199
200
  }
200
201
  await new Promise((wait) => setTimeout(wait, 150));
@@ -203,7 +204,7 @@ export async function start(paths, options) {
203
204
  return exited || 1;
204
205
  }
205
206
  show(url, `${url}/api/local/enter?k=${accessKey}`, options.open);
206
- console.log(styleText("dim", " Stop it with Ctrl-C."));
207
+ console.log(dim(" To stop it, press Ctrl-C."));
207
208
  console.log("");
208
209
  const code = await ended;
209
210
  if (code !== RESTART_EXIT) {
@@ -211,7 +212,7 @@ export async function start(paths, options) {
211
212
  }
212
213
  // Updated or `wizards restart`: the command as it is on disk now starts it again, in this
213
214
  // terminal or under the login item, which keeps waiting on this process.
214
- console.log(styleText("dim", " Starting it again."));
215
+ console.log(dim(" Restarting…"));
215
216
  current = spawnCommand(paths, ["start", "--no-open"], { stdio: "inherit" });
216
217
  return new Promise((done) => {
217
218
  current.on("error", () => done(1));
@@ -233,7 +234,7 @@ export async function open(paths, print) {
233
234
  const { dataDir } = settings(paths);
234
235
  const running = await runningRuntime(dataDir);
235
236
  if (!running) {
236
- console.error(`engenty wizards is not running. Start it with \`${command()} start\`.`);
237
+ console.error(`engenty wizards isn’t running. To start it: ${cmd("start")}`);
237
238
  return 1;
238
239
  }
239
240
  const entry = entryUrl(running, dataDir);
@@ -5,7 +5,7 @@ import { autostartState, setAutostart } from "./autostart.js";
5
5
  import { installedByScript, isCheckout, packageRoot, packageVersion } from "./home.js";
6
6
  import { detectClients, findChrome, findFfmpeg, nodeIsCurrent } from "./machine.js";
7
7
  import { entryUrl, runningRuntime, settings, startInBackground } from "./start.js";
8
- import { bad, badge, cyan, dim, no, ok, tilde } from "./ui.js";
8
+ import { bad, badge, cmd, cyan, dim, no, ok, tilde } from "./ui.js";
9
9
  /**
10
10
  * What is installed and what runs. With `checks` (`doctor`) it also says what is wrong and
11
11
  * returns 1 when something is.
@@ -28,24 +28,24 @@ export async function status(paths, checks) {
28
28
  row("Data", tilde(dataDir));
29
29
  row("Node", `${process.version} ${dim(tilde(process.execPath))}`);
30
30
  row("Running", running
31
- ? `${ok} ${cyan(running.url)} ${dim(`pid ${running.pid}, since ${running.startedAt}`)}`
31
+ ? `${ok} ${cyan(running.url)} ${dim(`pid ${running.pid}, since ${new Date(running.startedAt).toLocaleString()}`)}`
32
32
  : `${no} not running`);
33
33
  if (!("unavailable" in atLogin)) {
34
- row("At login", atLogin.on ? `${ok} starts ${dim(`(${atLogin.how})`)}` : `${no} does not start`);
34
+ row("At login", atLogin.on ? `${ok} starts ${dim(`(${atLogin.how})`)}` : `${no} doesn’t start`);
35
35
  }
36
36
  row("Account", `${account.kind === "linked" ? ok : no} ${describeAccount(account)}`);
37
37
  for (const { client, path, version } of clients) {
38
38
  row(client.name.split(" ")[0], path ? `${ok} ${version} ${dim(tilde(path))}` : `${no} not installed`);
39
39
  }
40
- row("Browser", chrome ? `${ok} ${dim(tilde(chrome))}` : `${no} none for PDF and PNG`);
41
- row("ffmpeg", ffmpeg.path ? `${ok} ${ffmpeg.version ?? ""}` : `${no} none for MP4`);
40
+ row("Browser", chrome ? `${ok} ${dim(tilde(chrome))}` : `${no} not found (needed for PDF and PNG)`);
41
+ row("ffmpeg", ffmpeg.path ? `${ok} ${ffmpeg.version ?? ""}` : `${no} not found (needed for MP4)`);
42
42
  console.log("");
43
43
  if (!checks) {
44
44
  return 0;
45
45
  }
46
46
  const problems = [];
47
47
  if (!nodeIsCurrent()) {
48
- problems.push(`Node ${process.version} is too old: 24.11 or newer is needed.`);
48
+ problems.push(`Node ${process.version} is too old. engenty wizards needs 24.11 or newer.`);
49
49
  }
50
50
  for (const part of ["apps/runtime/dist/index.js", "apps/web/dist/index.html", "plugin"]) {
51
51
  if (!existsSync(join(packageRoot, part))) {
@@ -57,16 +57,16 @@ export async function status(paths, checks) {
57
57
  accessSync(paths.home, constants.W_OK);
58
58
  }
59
59
  catch {
60
- problems.push(`${tilde(paths.home)} cannot be written to.`);
60
+ problems.push(`${tilde(paths.home)} can’t be written to.`);
61
61
  }
62
62
  if (!clients.some((state) => state.path)) {
63
- console.log(` ${no} No AI client: the studio needs an API key, Ollama or Apple Intelligence (a Mac) instead. ${dim("`setup` installs one.")}`);
63
+ console.log(` ${no} No AI client. The studio needs an API key, Ollama or Apple Intelligence (Mac only) instead. ${dim(`To install one: ${cmd("setup")}`)}`);
64
64
  }
65
65
  for (const problem of problems) {
66
66
  console.log(` ${bad} ${problem}`);
67
67
  }
68
68
  if (!problems.length) {
69
- console.log(` ${ok} Nothing wrong with this install.`);
69
+ console.log(` ${ok} No problems found.`);
70
70
  }
71
71
  console.log("");
72
72
  return problems.length ? 1 : 0;
@@ -89,7 +89,7 @@ export async function stop(paths) {
89
89
  const { dataDir } = settings(paths);
90
90
  const running = await runningRuntime(dataDir);
91
91
  if (!running) {
92
- console.log("engenty wizards is not running.");
92
+ console.log("engenty wizards isn’t running.");
93
93
  return 0;
94
94
  }
95
95
  process.kill(running.pid, "SIGTERM");
@@ -97,7 +97,7 @@ export async function stop(paths) {
97
97
  console.log(`Stopped (pid ${running.pid}). Your data is kept.`);
98
98
  return 0;
99
99
  }
100
- console.error(`The runtime (pid ${running.pid}) did not stop within 8 seconds.`);
100
+ console.error(`engenty wizards (pid ${running.pid}) didn’t stop within 8 seconds.`);
101
101
  return 1;
102
102
  }
103
103
  /**
@@ -118,31 +118,31 @@ export async function restart(paths) {
118
118
  return 0;
119
119
  }
120
120
  }
121
- console.error("engenty wizards did not start again within a minute.");
121
+ console.error("engenty wizards didn’t restart within a minute.");
122
122
  return 1;
123
123
  }
124
124
  if (running) {
125
125
  process.kill(running.pid, "SIGTERM");
126
126
  if (!(await ended(running.pid, 8000))) {
127
- console.error(`The runtime (pid ${running.pid}) did not stop within 8 seconds.`);
127
+ console.error(`engenty wizards (pid ${running.pid}) didn’t stop within 8 seconds.`);
128
128
  return 1;
129
129
  }
130
130
  }
131
131
  const started = await startInBackground(paths);
132
132
  if (!started) {
133
- console.error(`engenty wizards did not start; see ${join(paths.logs, "runtime.log")}.`);
133
+ console.error(`engenty wizards didn’t start. Its log is in ${tilde(join(paths.logs, "runtime.log"))}.`);
134
134
  return 1;
135
135
  }
136
136
  console.log(`${ok} ${running ? "Restarted" : "Started"}: ${cyan(started.url)} ${dim(`pid ${started.pid}, in the background`)}`);
137
137
  if (!running) {
138
- console.log(` Open the studio: ${entryUrl(started, dataDir)}`);
138
+ console.log(` Open the studio: ${cyan(entryUrl(started, dataDir))}`);
139
139
  }
140
140
  return 0;
141
141
  }
142
142
  /** `autostart [on|off]`: whether it starts at login, and the switch. */
143
143
  export async function autostart(paths, value) {
144
144
  if (value && value !== "on" && value !== "off") {
145
- console.error("autostart takes on or off.");
145
+ console.error(`Use ${cmd("autostart on")} or ${cmd("autostart off")}.`);
146
146
  return 2;
147
147
  }
148
148
  let state = await autostartState(paths);
@@ -151,19 +151,19 @@ export async function autostart(paths, value) {
151
151
  state = await setAutostart(paths, value === "on");
152
152
  }
153
153
  catch (error) {
154
- console.error(`Starting at login could not be switched ${value}: ${error.message}`);
154
+ console.error(`Couldn’t turn start at login ${value}: ${error.message}`);
155
155
  return 1;
156
156
  }
157
157
  }
158
158
  if ("unavailable" in state) {
159
- console.error(`Starting at login is not possible here: ${state.unavailable}.`);
159
+ console.error(`Start at login isn’t available here: ${state.unavailable}.`);
160
160
  return value ? 1 : 0;
161
161
  }
162
162
  console.log(state.on
163
163
  ? `${ok} engenty wizards starts at login: ${state.how}.`
164
- : `${no} engenty wizards does not start at login. ${dim("`autostart on` switches it on.")}`);
164
+ : `${no} engenty wizards doesn’t start at login. ${dim(`To turn it on: ${cmd("autostart on")}`)}`);
165
165
  if (state.on && process.platform === "linux") {
166
- console.log(dim(" Before anybody logs in, too: loginctl enable-linger"));
166
+ console.log(dim(` To start it even before anyone logs in: ${cyan("loginctl enable-linger")}`));
167
167
  }
168
168
  return value && state.on !== (value === "on") ? 1 : 0;
169
169
  }
@@ -1,6 +1,7 @@
1
1
  import { homedir } from "node:os";
2
2
  import { styleText } from "node:util";
3
3
  import * as p from "@clack/prompts";
4
+ import { command } from "./home.js";
4
5
  const colours = Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
5
6
  /** The name on engenty's orange. */
6
7
  export const badge = (text) => colours ? `\x1b[48;2;224;83;27m\x1b[97m\x1b[1m ${text} \x1b[0m` : text;
@@ -10,15 +11,21 @@ export const cyan = (text) => styleText("cyan", text);
10
11
  export const ok = styleText("green", "✓");
11
12
  export const no = styleText("yellow", "–");
12
13
  export const bad = styleText("red", "✗");
14
+ export const live = styleText("green", "●");
13
15
  /** A path as the person would write it. */
14
16
  export const tilde = (path) => path.startsWith(homedir()) ? `~${path.slice(homedir().length)}` : path;
15
- /** A spinner on a terminal; where nobody watches, only what came of it. */
17
+ /** A command as the person types it, with this install's prefix: `cmd("stop")` → wizards stop. */
18
+ export const cmd = (args = "") => cyan(args ? `${command()} ${args}` : command());
19
+ /**
20
+ * A spinner on a terminal; where nobody watches, only what came of it. The message ends in "…"
21
+ * ("Checking status…"); clack animates its own dots in its place.
22
+ */
16
23
  export function busy(message) {
17
24
  if (!process.stdout.isTTY) {
18
25
  return { stop: (text) => p.log.step(text), fail: (text) => p.log.warn(text) };
19
26
  }
20
27
  const spinner = p.spinner();
21
- spinner.start(message);
28
+ spinner.start(message.replace(/…$/, ""));
22
29
  return {
23
30
  stop: (text) => spinner.stop(text),
24
31
  fail: (text) => spinner.error(text),
@@ -1,10 +1,10 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
3
  import { join } from "node:path";
4
- import { command, installedByScript, isCheckout } from "./home.js";
4
+ import { installedByScript, isCheckout } from "./home.js";
5
5
  import { runningRuntime, settings } from "./start.js";
6
6
  import { restart } from "./status.js";
7
- import { cyan } from "./ui.js";
7
+ import { cmd, cyan } from "./ui.js";
8
8
  /** The installer this install was made with; it also updates one. */
9
9
  const INSTALLER = "https://engenty.ai/install.sh";
10
10
  async function installer(into) {
@@ -16,7 +16,7 @@ async function installer(into) {
16
16
  const text = response.ok ? await response.text() : "";
17
17
  // The address answers with the studio's page while a server has no installer yet.
18
18
  if (!text.startsWith("#!")) {
19
- throw new Error(`${source} did not answer with the installer.`);
19
+ throw new Error(`${source} didn’t return the installer.`);
20
20
  }
21
21
  const file = join(into, "install.sh");
22
22
  writeFileSync(file, text, { mode: 0o700 });
@@ -30,8 +30,8 @@ async function installer(into) {
30
30
  export async function update(paths, options = {}) {
31
31
  if (!installedByScript(paths)) {
32
32
  console.log(isCheckout()
33
- ? `This copy is a checkout. The newest version: ${cyan("git pull && pnpm install && pnpm build")}`
34
- : `This copy runs from npm. The newest version: ${cyan("npx wizards@latest")}`);
33
+ ? `This is a source checkout. To update: ${cyan("git pull && pnpm install && pnpm build")}`
34
+ : `This copy runs from npm. To update: ${cyan("npx wizards@latest")}`);
35
35
  return 0;
36
36
  }
37
37
  const tmp = mkdtempSync(join(paths.home, "update-"));
@@ -57,7 +57,7 @@ export async function update(paths, options = {}) {
57
57
  if (options.restart) {
58
58
  return restart(paths);
59
59
  }
60
- console.log(` The runtime that is running is still the old one. Restart it: ${cyan(`${command()} restart`)}`);
60
+ console.log(` Installed. Restart engenty wizards to use the new version: ${cmd("restart")}`);
61
61
  }
62
62
  return 0;
63
63
  }
@@ -51,7 +51,7 @@ export function validatePath(path) {
51
51
  }
52
52
  export function validateCommand(paths, asJson) {
53
53
  if (!paths.length) {
54
- console.error("Name a .wizard file, an unpacked package or a wizard's JSON.");
54
+ console.error("Give a path: a .wizard file, an unpacked package or a wizard JSON file.");
55
55
  return 2;
56
56
  }
57
57
  const results = paths.map(validatePath);
@@ -1,3 +1,4 @@
1
+ import { bilingual } from "@engenty-wizards/shared/run";
1
2
  import { and, asc, eq } from "drizzle-orm";
2
3
  import { z } from "zod";
3
4
  import { db, schema } from "../db/client.js";
@@ -274,7 +275,7 @@ export async function importConnector(userId, projectId, input) {
274
275
  await requireWritableProject(projectId);
275
276
  await requireAdmin();
276
277
  if (await load(projectId, input.id)) {
277
- throw new ServiceError("invalid", `Connector "${input.id}" already exists in this project.`);
278
+ throw new ServiceError("invalid", bilingual(`Den Connector „${input.id}“ gibt es in diesem Space schon.`, `The connector “${input.id}” already exists in this space.`));
278
279
  }
279
280
  try {
280
281
  const { discover, surface, prepared } = await prepare(input);
@@ -351,7 +352,7 @@ export async function refreshConnector(projectId, id, accessToken) {
351
352
  await requireWritableProject(projectId);
352
353
  const record = await load(projectId, id);
353
354
  if (!record) {
354
- throw new ServiceError("not_found", "No such connector.");
355
+ throw new ServiceError("not_found", bilingual("Diesen Connector gibt es nicht.", "This connector doesn’t exist."));
355
356
  }
356
357
  try {
357
358
  const { surface } = record.registry_surface_slug
@@ -170,7 +170,7 @@ export function oauthStateRun(rawState) {
170
170
  export async function finishOAuth(answer, rawState) {
171
171
  const state = unseal(rawState);
172
172
  if (!state || state.exp < Date.now()) {
173
- throw new Error("The sign-in has expired. Please connect again.");
173
+ throw new Error("The sign-in has expired. Connect again.");
174
174
  }
175
175
  const run = await db.query.run.findFirst({ where: eq(schema.run.id, state.runId) });
176
176
  const connection = run?.definition.connections?.find((c) => c.id === state.connectionId);
@@ -244,16 +244,16 @@ function connectFailure(err) {
244
244
  const host = e.imapHost ? ` (${e.imapHost})` : "";
245
245
  if (e.authenticationFailed) {
246
246
  const answer = e.responseText?.slice(0, 160);
247
- return bilingual(`Anmeldung abgelehnt${host}. Stimmen Adresse und (App-)Passwort? Bei Gmail und Google Workspace braucht es ein App-Passwort und aktiviertes IMAP.${answer ? ` Antwort des Servers: ${answer}` : ""}`, `Sign-in refused${host}. Are the address and (app) password right? Gmail and Google Workspace need an app password and IMAP switched on.${answer ? ` The server answered: ${answer}` : ""}`);
247
+ return bilingual(`Anmeldung abgelehnt${host}. Stimmen Adresse und (App-)Passwort? Gmail und Google Workspace brauchen ein App-Passwort und eingeschaltetes IMAP.${answer ? ` Antwort des Servers: ${answer}` : ""}`, `The sign-in was refused${host}. Are the address and (app) password right? Gmail and Google Workspace need an app password and IMAP turned on.${answer ? ` The server said: ${answer}` : ""}`);
248
248
  }
249
249
  if (e.code === "ENOTFOUND") {
250
- return bilingual(`Den Mail-Server${host} gibt es nicht. Bitte den IMAP-Server eintragen, bei Gmail und Google Workspace imap.gmail.com.`, `The mail server${host} does not exist. Please enter the IMAP server; for Gmail and Google Workspace it is imap.gmail.com.`);
250
+ return bilingual(`Den Mail-Server${host} gibt es nicht. Trag den IMAP-Server ein, bei Gmail und Google Workspace imap.gmail.com.`, `The mail server${host} doesn’t exist. Enter the IMAP server. For Gmail and Google Workspace, it’s imap.gmail.com.`);
251
251
  }
252
252
  if (e.code === "ETIMEDOUT" || e.code === "ETIMEOUT" || e.code === "ECONNREFUSED") {
253
- return bilingual(`Der Mail-Server${host} antwortet nicht. Bitte IMAP-Server und Port prüfen.`, `The mail server${host} does not answer. Please check the IMAP server and port.`);
253
+ return bilingual(`Der Mail-Server${host} antwortet nicht. Prüf IMAP-Server und Port.`, `The mail server${host} isn’t responding. Check the IMAP server and port.`);
254
254
  }
255
255
  const detail = String(e.message ?? err).slice(0, 200);
256
- return bilingual(`Verbinden hat nicht geklappt${host}: ${detail}`, `Connecting did not work${host}: ${detail}`);
256
+ return bilingual(`Verbinden hat nicht geklappt${host}: ${detail}`, `Connecting didn’t work${host}: ${detail}`);
257
257
  }
258
258
  export function disconnect(scope, connectionId) {
259
259
  return deleteSecret(scope, slot(connectionId));
@@ -37,7 +37,7 @@ async function spaceExists(space) {
37
37
  columns: { id: true },
38
38
  });
39
39
  if (!project) {
40
- throw new Error(`There is no space "${space}".`);
40
+ throw new Error(`There’s no space “${space}”.`);
41
41
  }
42
42
  }
43
43
  /** Keeps a connection: the same account for the same space again replaces the first one. */
@@ -101,7 +101,7 @@ export function pluginStateTenant(rawState) {
101
101
  export async function finishPluginOAuth(answer, rawState) {
102
102
  const state = unseal(rawState);
103
103
  if (!state?.plugin || state.exp < Date.now()) {
104
- throw new Error("The sign-in has expired. Please connect again.");
104
+ throw new Error("The sign-in has expired. Connect again.");
105
105
  }
106
106
  const connector = oauthConnector(state.connectorId);
107
107
  if (connector.auth.kind !== "oauth2") {