wizards 0.3.7 → 0.4.0

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 (72) hide show
  1. package/README.md +28 -14
  2. package/apps/runtime/dist/agents/studio-assistant.js +79 -7
  3. package/apps/runtime/dist/assistant/runs.js +24 -0
  4. package/apps/runtime/dist/auth/account.js +36 -4
  5. package/apps/runtime/dist/auth/device.js +101 -0
  6. package/apps/runtime/dist/auth/index.js +4 -4
  7. package/apps/runtime/dist/cli/account.js +289 -0
  8. package/apps/runtime/dist/cli/install-env.js +33 -0
  9. package/apps/runtime/dist/cli/main.js +17 -3
  10. package/apps/runtime/dist/cli/menu.js +19 -5
  11. package/apps/runtime/dist/cli/setup.js +9 -16
  12. package/apps/runtime/dist/cli/status.js +4 -1
  13. package/apps/runtime/dist/cli/ui.js +14 -0
  14. package/apps/runtime/dist/docs/user/assistant.md +25 -7
  15. package/apps/runtime/dist/docs/user/command-line.md +3 -2
  16. package/apps/runtime/dist/docs/user/create-account.md +47 -0
  17. package/apps/runtime/dist/docs/user/first-start.md +9 -1
  18. package/apps/runtime/dist/docs/user/index.md +16 -12
  19. package/apps/runtime/dist/docs/user/install.md +3 -1
  20. package/apps/runtime/dist/docs/user/local-only.md +38 -0
  21. package/apps/runtime/dist/docs/user/meta.json +3 -1
  22. package/apps/runtime/dist/docs/user/models.md +3 -2
  23. package/apps/runtime/dist/env.js +5 -0
  24. package/apps/runtime/dist/manage.js +16 -4
  25. package/apps/runtime/dist/models.js +6 -0
  26. package/apps/runtime/dist/plugins/pro.js +16 -16
  27. package/apps/runtime/dist/routes/studio.js +140 -40
  28. package/apps/runtime/dist/services/assistant-chats.js +49 -6
  29. package/apps/runtime/dist/services/cloud.js +10 -10
  30. package/apps/runtime/dist/services/onboarding.js +152 -0
  31. package/apps/web/dist/assets/{AboutDialog-C0dN86zd.js → AboutDialog-CPQiN7H9.js} +1 -1
  32. package/apps/web/dist/assets/{ChangelogDialog-Cv81t7tj.js → ChangelogDialog-DynvQuuR.js} +1 -1
  33. package/apps/web/dist/assets/ChatView-CKo_9IZD.js +10 -0
  34. package/apps/web/dist/assets/EditorPage-CDIJXC4T.js +3 -0
  35. package/apps/web/dist/assets/{FlowPreview-D4dp1MWS.js → FlowPreview-BZLnG7GV.js} +1 -1
  36. package/apps/web/dist/assets/{Harness-v7r0e6vK.js → Harness-CNh3nbB_.js} +2 -2
  37. package/apps/web/dist/assets/{NewWizardChannels-Be-7VhBi.js → NewWizardChannels-C1Ss9UMY.js} +1 -1
  38. package/apps/web/dist/assets/{NewWizardPage-C8hPvrK8.js → NewWizardPage-2qLAbvDL.js} +2 -2
  39. package/apps/web/dist/assets/OnboardingPage-C1FJwEsT.js +1 -0
  40. package/apps/web/dist/assets/ProjectPage-CGuLUuOs.js +1 -0
  41. package/apps/web/dist/assets/{PublicRunner-DeCpVfZP.js → PublicRunner-BKw1lOW4.js} +1 -1
  42. package/apps/web/dist/assets/{ReadOnly-B-UCgnqW.js → ReadOnly-DTjv2Jzm.js} +1 -1
  43. package/apps/web/dist/assets/RunDrawer-D1I5xqVf.js +4 -0
  44. package/apps/web/dist/assets/RunnerView-BPPmlv9z.js +1 -0
  45. package/apps/web/dist/assets/SettingsPage-zbomQfGy.js +1 -0
  46. package/apps/web/dist/assets/SetupPage-D3F7M6Pz.js +1 -0
  47. package/apps/web/dist/assets/{SharePage-B778xUFo.js → SharePage-BY49IJgT.js} +1 -1
  48. package/apps/web/dist/assets/{changelog-DCJo2mbx.js → changelog-DYZC2E6q.js} +1 -1
  49. package/apps/web/dist/assets/{circle-alert-DLvC9ePT.js → circle-alert-CVOH4wAv.js} +1 -1
  50. package/apps/web/dist/assets/{esm-BJskogLD.js → esm-BfXdeWaI.js} +1 -1
  51. package/apps/web/dist/assets/index-B9xVkr2X.css +1 -0
  52. package/apps/web/dist/assets/index-BH0PYgov.js +15 -0
  53. package/apps/web/dist/assets/{lock-DR7NakPn.js → lock-DHBh62LA.js} +1 -1
  54. package/apps/web/dist/assets/{phone-BORh3jfv.js → phone-Bt1cyWpY.js} +1 -1
  55. package/apps/web/dist/assets/{settings-menu-DV5D91TD.js → settings-menu-ace1vI3i.js} +1 -1
  56. package/apps/web/dist/assets/{ui-BOCXzzHi.js → ui-D2SqIZeQ.js} +2 -2
  57. package/apps/web/dist/assets/{upload-B3yv8gsk.js → upload-CCEq88l4.js} +1 -1
  58. package/apps/web/dist/index.html +3 -3
  59. package/apps/web/dist/install.sh +17 -7
  60. package/apps/web/dist/mcp/flow.html +3 -3
  61. package/node_modules/@engenty-wizards/shared/dist/assistant.d.ts +62 -1
  62. package/node_modules/@engenty-wizards/shared/dist/assistant.js +10 -0
  63. package/package.json +1 -1
  64. package/apps/web/dist/assets/ChatView-uogJ3nht.js +0 -10
  65. package/apps/web/dist/assets/EditorPage-n8YuTh6j.js +0 -3
  66. package/apps/web/dist/assets/ProjectPage-B686ArYy.js +0 -1
  67. package/apps/web/dist/assets/RunDrawer-C6pzi8AJ.js +0 -4
  68. package/apps/web/dist/assets/RunnerView-Deenymq0.js +0 -1
  69. package/apps/web/dist/assets/SettingsPage-CK6L0yEe.js +0 -1
  70. package/apps/web/dist/assets/SetupPage-9CH3qXdH.js +0 -1
  71. package/apps/web/dist/assets/index-D6hUM8NU.css +0 -1
  72. package/apps/web/dist/assets/index-Df6UxElH.js +0 -12
@@ -0,0 +1,47 @@
1
+ ---
2
+ title: Create an account
3
+ description: Your team, its space and your wizards live in your account at engenty.ai. Sign up with an invitation code, answer four questions, build.
4
+ version: 0.3.4
5
+ ---
6
+
7
+ engenty wizards is in a closed beta: a new account needs an invitation code. "Get started" on
8
+ [engenty.ai](https://engenty.ai) takes your e-mail and puts you on the list; the code comes by
9
+ mail as soon as a seat is free. With a code, the same step opens the sign-up.
10
+
11
+ ## Sign up
12
+
13
+ 1. Type the code at [account.engenty.ai/sign-up](https://account.engenty.ai/sign-up). The link
14
+ in the invitation mail has it filled in.
15
+ 2. Sign in with Google, GitHub, or a link by mail. There is no password.
16
+ 3. The studio opens at [engenty.ai/studio](https://engenty.ai/studio).
17
+
18
+ Your account is a **team**: it holds your credits, your spaces and, on the Team plan, your
19
+ colleagues. It starts with credits, some of which end on a date; the account pages at
20
+ [account.engenty.ai](https://account.engenty.ai) show them.
21
+
22
+ ## The first visit
23
+
24
+ The studio asks four things, one per page. Each but the first can be skipped and changed later.
25
+
26
+ | Page | Asks | Goes to |
27
+ |---|---|---|
28
+ | Who you are | What your team is called: your company, your club, or just your name | The team's name at account.engenty.ai |
29
+ | About you & your brand | Your website. engenty reads your name, what you do, your logo and colours from it; or you write a line about yourself | [Space → Info & brand](./space.md#info--brand) |
30
+ | Knowledge | Documents the wizards may look things up in: price lists, your terms, an FAQ | [Space → Knowledge](./space.md#knowledge) |
31
+ | Your space | Nothing: it explains the space that holds all of this, "Meine Wizards" | — |
32
+
33
+ Then "Build your first wizard" opens the studio's start: describe a task in a sentence, or pick
34
+ a template. See [Build a wizard](./build-a-wizard.md).
35
+
36
+ ## What the cloud does
37
+
38
+ - Wizards built here run behind a link that works day and night, on the team's credits.
39
+ - Share links, the tag for your website, QR codes and the channels of [Test and share](./test-and-share.md) work as on an install.
40
+ - What the wizards think with is the team's: [Models and account](./models.md).
41
+
42
+ ## Also on your computer
43
+
44
+ The same account works on your own computer: [install](./install.md) engenty wizards there and
45
+ sign in during the setup. It thinks with your Claude, ChatGPT, Gemini or Cursor subscription,
46
+ reads your local files and browses for you; what you publish there runs in the cloud too, behind
47
+ the same links. "Also on your computer" on the studio's start page mails you the command.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  title: First start
3
3
  description: Choose what engenty wizards thinks with. The studio opens once a test call works.
4
- version: 0.3.2
4
+ version: 0.3.4
5
5
  ---
6
6
 
7
7
  The first page of the studio is the setup, "Choose your Engine". It asks one thing: what should
@@ -9,10 +9,18 @@ engenty think with?
9
9
 
10
10
  ![The setup: choose the engine, an installed AI client or your own keys](../../assets/setup.png)
11
11
 
12
+ ## Your account
13
+
14
+ Above the engines stands your account: signed in during the [install](./install.md), it shows
15
+ your name, your team and your credits, and your team's space is on this computer. Not signed in
16
+ yet, "Sign in" opens your browser for it; an install that is [local only](./local-only.md)
17
+ shows nothing here.
18
+
12
19
  ## Choose your engine
13
20
 
14
21
  | Engine | Runs on | Needs |
15
22
  |---|---|---|
23
+ | engenty credits | Your account's credits, like in the cloud | An account signed in. Shown only then |
16
24
  | Claude Code | Your Claude subscription | The client installed and signed in |
17
25
  | Codex | Your ChatGPT subscription | The client installed and signed in |
18
26
  | Gemini CLI | Your Google account | The client installed and signed in |
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  title: User Guide
3
3
  description: Install engenty wizards, build a wizard, run and share it.
4
- version: 0.3.2
4
+ version: 0.3.4
5
5
  ---
6
6
 
7
7
  engenty wizards turns something you do again and again into a wizard: pages that ask what it
@@ -17,30 +17,34 @@ briefing, an invoice, a quote, a damage report.
17
17
  | **Describe it** | Say in plain words what the wizard should do. The assistant builds the flow; you see it as a diagram and change any step. |
18
18
  | **Let AI steps work** | They research the web, write, draw images, render video, read PDFs, scans and mails, build documents and dashboards, and write into other services. Anything that changes an account asks first. |
19
19
  | **Share a link** | Anyone runs the wizard in a browser, on a phone too. Results download as PDF, Word, HTML, Markdown, PNG, MP4, MP3, CSV, Excel or JSON, or go out as a link. |
20
- | **Keep it on your machine** | It thinks with Codex, Claude Code, Gemini CLI or Cursor Agent on your own subscription, with your own keys, or with a local model. Wizards and results stay in a folder on your computer. |
20
+ | **In the cloud, or on your computer** | Sign up and build in the browser; links run on your team's credits. Or install it on your computer: it thinks with Codex, Claude Code, Gemini CLI or Cursor Agent on your own subscription, with your own keys, or with a local model, and your files stay with you. |
21
21
 
22
22
  ## Where to start
23
23
 
24
- 1. [Install](./install.md) it: one command on macOS or Linux.
25
- 2. At the [first start](./first-start.md), choose what it thinks with.
26
- 3. [Build a wizard](./build-a-wizard.md) from a sentence or a template.
27
- 4. [Test and share](./test-and-share.md) it.
24
+ 1. [Create an account](./create-account.md) at engenty.ai, with an invitation code.
25
+ 2. [Build a wizard](./build-a-wizard.md) from a sentence or a template.
26
+ 3. [Test and share](./test-and-share.md) it.
27
+ 4. Also on your computer: [install](./install.md) it there, signed in to the same account, and at the [first start](./first-start.md) choose what it thinks with.
28
28
  5. Use it from your [AI apps](./ai-apps.md): Claude Desktop, Cursor, Codex and others.
29
29
 
30
30
  ## Words used here
31
31
 
32
32
  | Word | Means |
33
33
  |---|---|
34
- | Studio | Where you build wizards: `/studio` of your install |
34
+ | Account | You, signed in at engenty.ai with Google, GitHub or a link by mail |
35
+ | Team | What the sign-up creates: it holds your credits, your spaces and, on the Team plan, your colleagues |
36
+ | Space | What all your wizards know and produce: name, logo, colours, facts, documents, data, results. A team's first one is "Meine Wizards" |
37
+ | Studio | Where you build wizards: engenty.ai/studio, or `/studio` of your install |
35
38
  | Wizard | A flow of steps a person walks through on a link |
36
39
  | Step | One part of the flow: a page, an AI step, a review, the result |
37
40
  | Run | One person going through a wizard once |
38
- | Space | What all your wizards know: name, logo, colours, facts, documents |
39
41
  | Model class | The kind of model a step needs. Which model serves a class is a setting, not part of the wizard |
40
42
 
41
43
  ## Two places it runs
42
44
 
43
- This guide describes the install on your own computer: there you build. engenty.ai is the cloud:
44
- with an account, what you publish on your computer also runs there, behind a link that works
45
- while your computer is off, on the account's credits. The studio at engenty.ai shows those
46
- wizards and their runs and changes nothing ([Test and share](./test-and-share.md#in-the-cloud)).
45
+ Your team, its space and your wizards live in your account. engenty.ai is the cloud: you build
46
+ there in the browser, and links run day and night on the team's credits. Your computer can join:
47
+ [install](./install.md) engenty wizards there, signed in to the same account, and it thinks with
48
+ the AI clients and keys on that machine; what you publish there runs in the cloud as well
49
+ ([Test and share](./test-and-share.md#in-the-cloud)). An install without an account is
50
+ [local only](./local-only.md).
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  title: Install
3
3
  description: One command on macOS or Linux. It brings its own Node.js and asks for no password.
4
- version: 0.3.2
4
+ version: 0.3.4
5
5
  ---
6
6
 
7
7
  ## Install
@@ -18,6 +18,7 @@ command into `~/.local/bin` and then walks you through the rest:
18
18
 
19
19
  | Step | What it does |
20
20
  |---|---|
21
+ | Your engenty account | Shows a code and opens the browser for the sign-in at account.engenty.ai (no account yet? the page takes an invitation code). Signed in, your team's space comes down to this computer, and what you publish here runs in the cloud too. "Later" skips it: `wizards account sign-in` does it any time. `--local` leaves the account out altogether: [Local only](./local-only.md) |
21
22
  | Something to think with | Looks for Codex, Claude Code, Gemini CLI and Cursor Agent and offers to install the ones that are missing. It proposes the one whose app (ChatGPT, Claude, Cursor) is on your Mac first, as that subscription is most likely yours already. You can also use an API key, [Ollama](https://ollama.com) or, on a Mac with Apple silicon and macOS 26, Apple Intelligence: the studio's [first page](./first-start.md) offers them |
22
23
  | A browser and ffmpeg | Names the browser for PDF and PNG exports and steps on websites: the first of Chrome, Edge and Chromium it finds (on a Mac also Brave). The app uses that one; [`CHROME_PATH`](./command-line.md#settings) picks another. Says whether there is ffmpeg for MP4 videos of animated widgets; with Homebrew it offers to install it |
23
24
  | Start at login (optional) | engenty wizards starts in the background when you log in, so links and AI clients always reach it. A LaunchAgent on a Mac, a systemd user service on Linux |
@@ -37,6 +38,7 @@ You install once; from then on you use the `wizards` command.
37
38
  | `wizards stop` | Stops it. Your data is kept |
38
39
  | `wizards status` | Whether it runs, and where |
39
40
  | `wizards restart` | Stops it and starts it again |
41
+ | `wizards account` | How the install stands to your account; `sign-in` and `sign-out` |
40
42
  | `wizards` | A menu with all of it |
41
43
 
42
44
  The studio is at
@@ -0,0 +1,38 @@
1
+ ---
2
+ title: Local only
3
+ description: An install without an account. Nothing leaves the computer; links answer on it only. The flag, what it means, and how to connect an account later.
4
+ version: 0.3.4
5
+ ---
6
+
7
+ An install normally signs in to your account during the setup: your team's space comes down to
8
+ the computer, and what you publish runs in the cloud too. An install can also stay alone.
9
+
10
+ ## The flag
11
+
12
+ ```bash
13
+ curl -fsSL https://engenty.ai/install.sh | bash -s -- --local
14
+ ```
15
+
16
+ `--local` skips the account step of the setup and switches the account off for this install.
17
+ On an install that is there already: `wizards setup --local`.
18
+
19
+ ## What it means
20
+
21
+ - No account, nothing leaves this computer. Wizards, the space and results stay in
22
+ `~/.engenty/wizards/data` ([Your data](./data.md)).
23
+ - Links answer on this computer only: `http://localhost:24368/w/…`. For others to open them,
24
+ run it [on a server](./server.md) of your own.
25
+ - It thinks with your AI clients, your own keys, Ollama or Apple Intelligence
26
+ ([First start](./first-start.md)). Without an account, video and voice need a key of your own.
27
+ - The studio shows no account surfaces: no sign-in offer, no credits, no cloud copy. The feature
28
+ switch `account` is off in the install's `.features` file; `wizards features` lists it.
29
+
30
+ ## Connect an account later
31
+
32
+ ```bash
33
+ wizards account sign-in
34
+ ```
35
+
36
+ The command shows a code and opens the browser for the sign-in; afterwards the install is
37
+ connected, and the switch is on again. `wizards account` says how the install stands,
38
+ `wizards account sign-out` disconnects it. Settings → Account in the studio does the same.
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "title": "User Guide",
3
- "description": "Install engenty wizards, build a wizard, run and share it.",
3
+ "description": "Create an account, build a wizard, run and share it, install it on your computer.",
4
4
  "root": true,
5
5
  "icon": "LifeBuoy",
6
6
  "pages": [
7
7
  "index",
8
8
  "---Start---",
9
+ "create-account",
9
10
  "install",
10
11
  "first-start",
11
12
  "---Wizards---",
@@ -24,6 +25,7 @@
24
25
  "---Your install---",
25
26
  "command-line",
26
27
  "data",
28
+ "local-only",
27
29
  "server",
28
30
  "troubleshooting"
29
31
  ]
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  title: Models and account
3
3
  description: Who does what your wizards ask for — an installed AI client, your own API keys, a local model, or credits.
4
- version: 0.3.2
4
+ version: 0.3.4
5
5
  ---
6
6
 
7
7
  Settings → Models decides who does what your wizards ask for. On your computer it is yours; on
@@ -125,7 +125,8 @@ Text & reasoning) picks what builds your wizards in the editor's conversation:
125
125
 
126
126
  ## Account
127
127
 
128
- An account is optional. With it you
128
+ An install normally signs in to your account during the setup ([Install](./install.md)); one
129
+ without an account is [local only](./local-only.md). With an account you
129
130
 
130
131
  - use credits instead of your own keys (the editor's note about missing models says so where
131
132
  text, image or video models are missing),
@@ -152,6 +152,11 @@ export const env = {
152
152
  /** Encrypts connected accounts and kept browser sessions; falls back to the app secret. */
153
153
  storeKey: str("STORE_ENC_KEY"),
154
154
  devLogin: process.env.NODE_ENV !== "production" && str("DEV_LOGIN") === "1",
155
+ /**
156
+ * `1`: the studio assistant's developer mode may be switched on here (steps, tools, prompt,
157
+ * the turns' events). A managed runtime offers it to the platform's admins instead.
158
+ */
159
+ assistantDev: str("ASSISTANT_DEV") === "1",
155
160
  /**
156
161
  * The Manage-App this runtime belongs to. Set = people sign in there, tenants come from its
157
162
  * tokens and model calls go through its gateway. Empty = the runtime runs alone with one tenant.
@@ -110,15 +110,15 @@ export function tenantMembers(id) {
110
110
  return call("GET", `/v1/tenants/${encodeURIComponent(id)}/members`);
111
111
  }
112
112
  const users = new Map();
113
- /** A person's own feature switches, as an admin set them at the Manage-App; cached a minute. */
114
- export async function userFeatures(userId) {
113
+ /** What the Manage-App says of a person beyond their token; cached a minute. */
114
+ export async function userInfo(userId) {
115
115
  const hit = users.get(userId);
116
116
  if (hit && Date.now() - hit.at < 60_000) {
117
117
  return hit.value;
118
118
  }
119
119
  const value = await call("GET", `/v1/users/${encodeURIComponent(userId)}`)
120
- .then((u) => u.features ?? {})
121
- .catch(() => hit?.value ?? {});
120
+ .then((u) => ({ features: u.features ?? {}, admin: u.admin === true }))
121
+ .catch(() => hit?.value ?? { features: {}, admin: false });
122
122
  users.set(userId, { at: Date.now(), value });
123
123
  return value;
124
124
  }
@@ -145,6 +145,18 @@ export async function tenantInfo(id, fresh = false) {
145
145
  export function forgetTenant(id) {
146
146
  tenants.delete(id);
147
147
  }
148
+ /** Renames the team at the Manage-App; what it answers is the tenant as it stands now. */
149
+ export async function renameTenant(id, name) {
150
+ const value = await call("PATCH", `/v1/tenants/${encodeURIComponent(id)}`, {
151
+ name,
152
+ });
153
+ tenants.set(id, { at: Date.now(), value });
154
+ return value;
155
+ }
156
+ /** The Manage-App mails the install command and the first steps to one of the team's people. */
157
+ export function installMail(email, lang) {
158
+ return call("POST", "/v1/install-mail", { email, lang });
159
+ }
148
160
  export function verifyKey(key) {
149
161
  return call("POST", "/v1/keys/verify", { key });
150
162
  }
@@ -1013,6 +1013,12 @@ function priceOf(ref) {
1013
1013
  return (catalog.get(provider === null || provider === "gateway" ? id : `${provider}/${id}`)?.pricing ??
1014
1014
  {});
1015
1015
  }
1016
+ /** A model's context window in tokens, as the AI Gateway's catalog gives it; null where unknown. */
1017
+ export function contextWindowOf(ref) {
1018
+ const { provider, id } = parse(ref);
1019
+ const entry = catalog.get(provider === null || provider === "gateway" ? id : `${provider}/${id}`);
1020
+ return typeof entry?.context_window === "number" ? entry.context_window : null;
1021
+ }
1016
1022
  const n = (v, fallback) => {
1017
1023
  const x = Number(v);
1018
1024
  return Number.isFinite(x) ? x : fallback;
@@ -84,7 +84,7 @@ async function call(path, token) {
84
84
  signal: AbortSignal.timeout(60_000),
85
85
  }).catch(() => null);
86
86
  if (!res) {
87
- throw new ProModuleError("Dein engenty-Konto ist gerade nicht erreichbar.", 502, "offline");
87
+ throw new ProModuleError("Your engenty account is not reachable right now.", 502, "offline");
88
88
  }
89
89
  return res;
90
90
  }
@@ -109,19 +109,19 @@ export async function refreshPro() {
109
109
  try {
110
110
  const token = await accountToken();
111
111
  if (!token) {
112
- throw new ProModuleError("Dein engenty-Konto ist gerade nicht erreichbar.", 502, "offline");
112
+ throw new ProModuleError("Your engenty account is not reachable right now.", 502, "offline");
113
113
  }
114
114
  const res = await call("", token);
115
115
  if (res.status === 401) {
116
- throw new ProModuleError("Das engenty-Konto nimmt diese Anmeldung nicht mehr an.", 403);
116
+ throw new ProModuleError("The engenty account no longer accepts this sign-in.", 403);
117
117
  }
118
118
  if (!res.ok) {
119
- throw new ProModuleError(`Das Konto antwortet nicht wie erwartet (${res.status}).`, 502);
119
+ throw new ProModuleError(`The account did not answer as expected (${res.status}).`, 502);
120
120
  }
121
121
  const answer = (await res.json());
122
122
  const claims = await verifySigned(answer.entitlement?.token ?? "", [AUDIENCE], env.local.accountUrl);
123
123
  if (!claims || claims.sub !== account.tenantId || !Array.isArray(claims.modules)) {
124
- throw new ProModuleError("Die Bestätigung des Pakets ließ sich nicht prüfen.", 502);
124
+ throw new ProModuleError("The package's confirmation could not be checked.", 502);
125
125
  }
126
126
  listing = answer;
127
127
  lastError = null;
@@ -177,7 +177,7 @@ function filesOf(zip) {
177
177
  files = unzipSync(zip);
178
178
  }
179
179
  catch {
180
- throw new ProModuleError("Das Paket ist beschädigt.", 502);
180
+ throw new ProModuleError("The package is damaged.", 502);
181
181
  }
182
182
  const out = {};
183
183
  for (const [name, data] of Object.entries(files)) {
@@ -188,7 +188,7 @@ function filesOf(zip) {
188
188
  const safe = parts.every((p) => p && p !== "." && p !== ".." && !p.includes("\\"));
189
189
  const allowed = name === MANIFEST_FILE || PARTS.some((part) => name.startsWith(part));
190
190
  if (!(safe && allowed)) {
191
- throw new ProModuleError(`Das Paket enthält eine unerlaubte Datei: ${name}`, 502);
191
+ throw new ProModuleError(`The package holds a file it may not: ${name}`, 502);
192
192
  }
193
193
  out[name] = data;
194
194
  }
@@ -197,28 +197,28 @@ function filesOf(zip) {
197
197
  /** Fetches one module of the plan, checks it and puts it in place; then it loads. */
198
198
  export async function installPro(id) {
199
199
  if (managed) {
200
- throw new ProModuleError("Hier nicht verfügbar.", 404, "not_found");
200
+ throw new ProModuleError("Not available here.", 404, "not_found");
201
201
  }
202
202
  const token = await accountToken();
203
203
  if (!token) {
204
- throw new ProModuleError("Verbinde zuerst dein engenty-Konto (Einstellungen → Konto).", 409);
204
+ throw new ProModuleError("Sign in to your engenty account first (Settings → Account).", 409);
205
205
  }
206
206
  if (!listing) {
207
207
  await refreshPro();
208
208
  }
209
209
  const entry = listing?.modules.find((m) => m.id === id);
210
210
  if (!entry) {
211
- throw new ProModuleError(`Für ${runtimeTag()} gibt es dieses Modul nicht.`, 404, "not_found");
211
+ throw new ProModuleError(`There is no build of this module for ${runtimeTag()}.`, 404, "not_found");
212
212
  }
213
213
  if (!entry.included) {
214
- throw new ProModuleError("Dieses Modul ist nicht Teil deines Pakets.", 403, "plan");
214
+ throw new ProModuleError("This module is not part of your plan.", 403, "plan");
215
215
  }
216
216
  const res = await call(`/${encodeURIComponent(id)}/package`, token);
217
217
  if (!res.ok) {
218
- throw new ProModuleError(`Das Modul ließ sich nicht laden (${res.status}).`, 502);
218
+ throw new ProModuleError(`The module could not be fetched (${res.status}).`, 502);
219
219
  }
220
220
  if (Number(res.headers.get("content-length") ?? 0) > MAX_PACKAGE) {
221
- throw new ProModuleError("Das Paket ist zu groß.", 502);
221
+ throw new ProModuleError("The package is too large.", 502);
222
222
  }
223
223
  const zip = new Uint8Array(await res.arrayBuffer());
224
224
  const sha256 = createHash("sha256").update(zip).digest("hex");
@@ -231,12 +231,12 @@ export async function installPro(id) {
231
231
  signature: entry.signature,
232
232
  });
233
233
  if (zip.length > MAX_PACKAGE || !genuine) {
234
- throw new ProModuleError("Das Paket ist nicht von engenty signiert und wurde verworfen.", 502);
234
+ throw new ProModuleError("The package is not signed by engenty and was discarded.", 502);
235
235
  }
236
236
  const files = filesOf(zip);
237
237
  const manifest = JSON.parse(new TextDecoder().decode(files[MANIFEST_FILE] ?? new Uint8Array()));
238
238
  if (manifest?.id !== id) {
239
- throw new ProModuleError("Das Paket passt nicht zu diesem Modul.", 502);
239
+ throw new ProModuleError("The package does not belong to this module.", 502);
240
240
  }
241
241
  const dir = proModulesDir();
242
242
  const target = join(dir, id);
@@ -270,7 +270,7 @@ export async function installPro(id) {
270
270
  /** Removes an installed module; its tables and what is in them stay. */
271
271
  export async function removePro(id) {
272
272
  if (managed || !(await installedIds()).includes(id)) {
273
- throw new ProModuleError("Dieses Modul ist nicht installiert.", 404, "not_found");
273
+ throw new ProModuleError("This module is not installed.", 404, "not_found");
274
274
  }
275
275
  await rm(join(proModulesDir(), id), { recursive: true, force: true });
276
276
  await findNewPlugins();