wizards 0.6.2 → 0.7.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 (86) hide show
  1. package/README.md +6 -2
  2. package/apps/runtime/dist/agents/architect.js +7 -29
  3. package/apps/runtime/dist/agents/studio-assistant.js +7 -24
  4. package/apps/runtime/dist/cli/autostart.js +58 -12
  5. package/apps/runtime/dist/cli/autoupdate.js +221 -0
  6. package/apps/runtime/dist/cli/environment.js +5 -0
  7. package/apps/runtime/dist/cli/main.js +9 -2
  8. package/apps/runtime/dist/cli/menu.js +2 -2
  9. package/apps/runtime/dist/cli/processes.js +72 -0
  10. package/apps/runtime/dist/cli/setup.js +9 -4
  11. package/apps/runtime/dist/cli/start.js +18 -1
  12. package/apps/runtime/dist/cli/status.js +51 -17
  13. package/apps/runtime/dist/cli/uninstall-main.js +20 -0
  14. package/apps/runtime/dist/cli/uninstall.js +231 -0
  15. package/apps/runtime/dist/cli/update.js +25 -2
  16. package/apps/runtime/dist/cli/versions.js +106 -0
  17. package/apps/runtime/dist/docs/user/command-line.md +2 -1
  18. package/apps/runtime/dist/docs/user/data.md +1 -1
  19. package/apps/runtime/dist/docs/user/install.md +29 -6
  20. package/apps/runtime/dist/engine/input.js +4 -5
  21. package/apps/runtime/dist/engine/steps.js +3 -3
  22. package/apps/runtime/dist/engine/template.js +8 -13
  23. package/apps/runtime/dist/env.js +3 -20
  24. package/apps/runtime/dist/features.js +2 -2
  25. package/apps/runtime/dist/index.js +3 -1
  26. package/apps/runtime/dist/manage.js +7 -0
  27. package/apps/runtime/dist/models.js +12 -0
  28. package/apps/runtime/dist/sandbox/docker.js +0 -2
  29. package/apps/runtime/dist/sandbox/index.js +3 -10
  30. package/apps/runtime/dist/services/space-categories.js +3 -25
  31. package/apps/runtime/dist/tools/browser.js +2 -14
  32. package/apps/runtime/dist/tools/index.js +1 -19
  33. package/apps/runtime/dist/tools/mail.js +1 -4
  34. package/apps/runtime/dist/tools/shared.js +30 -3
  35. package/apps/runtime/dist/update.js +10 -19
  36. package/apps/web/dist/assets/AboutDialog-CUVmdhzk.js +1 -0
  37. package/apps/web/dist/assets/{ChangelogDialog-BRiSYyIn.js → ChangelogDialog-DcQqFbiW.js} +1 -1
  38. package/apps/web/dist/assets/ChatView-DTPWjzWF.js +10 -0
  39. package/apps/web/dist/assets/EditorPage-SJHtft7P.js +3 -0
  40. package/apps/web/dist/assets/{FlowPreview-CUOEzoeJ.js → FlowPreview-DvpjuQdC.js} +1 -1
  41. package/apps/web/dist/assets/{Harness-D3kTVsF5.js → Harness-D6fezoWO.js} +2 -2
  42. package/apps/web/dist/assets/{NewWizardChannels-C3PGe_Zd.js → NewWizardChannels-BHeutZ7V.js} +1 -1
  43. package/apps/web/dist/assets/NewWizardPage-CzBxaPhH.js +7 -0
  44. package/apps/web/dist/assets/{OnboardingPage-pOPB5TVZ.js → OnboardingPage-DbR4N2Ms.js} +1 -1
  45. package/apps/web/dist/assets/ProjectPage-DG6_E7kA.js +1 -0
  46. package/apps/web/dist/assets/PublicRunner-19tV_8TR.js +1 -0
  47. package/apps/web/dist/assets/{ReadOnly-CCxZvYrT.js → ReadOnly-BMXFtKs0.js} +1 -1
  48. package/apps/web/dist/assets/RunDrawer-DqwLjFJb.js +4 -0
  49. package/apps/web/dist/assets/{RunnerView-pKik3sdE.js → RunnerView-DRc4J-rg.js} +1 -1
  50. package/apps/web/dist/assets/SettingsPage-CCypPS9L.js +1 -0
  51. package/apps/web/dist/assets/SetupPage-D2Y2rev4.js +1 -0
  52. package/apps/web/dist/assets/{SharePage-CZQqp5JM.js → SharePage-DB5n6Oug.js} +1 -1
  53. package/apps/web/dist/assets/changelog-BDhd7tjO.js +1 -0
  54. package/apps/web/dist/assets/{esm-D4pSRQgT.js → esm-6TNBQNCD.js} +1 -1
  55. package/apps/web/dist/assets/index-mBRI7ZQp.js +14 -0
  56. package/apps/web/dist/assets/{lock-DYnHCyRg.js → lock-HLZib0u9.js} +1 -1
  57. package/apps/web/dist/assets/{phone-DJawY1RF.js → phone-DsxbIcWi.js} +1 -1
  58. package/apps/web/dist/assets/settings-menu-BMMkIVP1.js +1 -0
  59. package/apps/web/dist/assets/ui-Brg2qM1A.js +137 -0
  60. package/apps/web/dist/assets/{upload-DL6NRa4h.js → upload-BaGy0ppG.js} +1 -1
  61. package/apps/web/dist/index.html +2 -2
  62. package/apps/web/dist/install.sh +29 -11
  63. package/apps/web/dist/mcp/flow.html +11 -11
  64. package/bin/wizards.mjs +17 -3
  65. package/node_modules/@engenty-wizards/shared/dist/definition.js +6 -17
  66. package/node_modules/@engenty-wizards/shared/dist/features.d.ts +6 -0
  67. package/node_modules/@engenty-wizards/shared/dist/features.js +12 -0
  68. package/node_modules/@engenty-wizards/shared/dist/numbers.d.ts +24 -0
  69. package/node_modules/@engenty-wizards/shared/dist/numbers.js +54 -0
  70. package/node_modules/@engenty-wizards/shared/dist/store.d.ts +7 -0
  71. package/node_modules/@engenty-wizards/shared/dist/store.js +28 -0
  72. package/package.json +1 -1
  73. package/apps/runtime/dist/sandbox/agentos.js +0 -191
  74. package/apps/web/dist/assets/AboutDialog-BrfbtjmR.js +0 -1
  75. package/apps/web/dist/assets/ChatView-_dYvuuUY.js +0 -10
  76. package/apps/web/dist/assets/EditorPage-CkRjTRMg.js +0 -3
  77. package/apps/web/dist/assets/NewWizardPage-B3ol4K43.js +0 -7
  78. package/apps/web/dist/assets/ProjectPage-Boyptzz_.js +0 -1
  79. package/apps/web/dist/assets/PublicRunner-DcETj9_E.js +0 -1
  80. package/apps/web/dist/assets/RunDrawer-hwruVTDG.js +0 -4
  81. package/apps/web/dist/assets/SettingsPage-BOe6NgFg.js +0 -1
  82. package/apps/web/dist/assets/SetupPage-81QxPnfE.js +0 -1
  83. package/apps/web/dist/assets/changelog-CtyrKHAc.js +0 -1
  84. package/apps/web/dist/assets/index-DnIvQFIg.js +0 -14
  85. package/apps/web/dist/assets/settings-menu-B_-_EAmK.js +0 -1
  86. package/apps/web/dist/assets/ui-rh8TaWvT.js +0 -137
@@ -18,13 +18,14 @@ version: 0.5.0
18
18
  | `wizards doctor` | Shows the status and checks for problems |
19
19
  | `wizards logs [n]` | Prints the last lines engenty wizards printed while running (100, or `n` up to 2000). Keys and tokens are taken out |
20
20
  | `wizards report` | Sends the status, the model settings (never a key) and the last 500 log lines to your [account](./models.md#account), so support can see what went wrong. Nothing is sent unless you run it. The command names the report’s ID. If this computer isn’t signed in, it tells you to sign in under Settings → Account or with `wizards account sign-in` |
21
- | `wizards stop` | Stops it. Your data is kept |
21
+ | `wizards stop` | Stops it, with any other copy of it still running in the background. Your data is kept. AI apps that use your wizards start it again when you use one there |
22
22
  | `wizards restart` | Stops it and starts it again; after an update, that is the new version. If it isn’t running, it starts in the background |
23
23
  | `wizards update` | Installs the latest version |
24
24
  | `wizards autostart on` / `off` | Starts it when you log in, or stops doing so. It is a LaunchAgent on macOS and a systemd user service on Linux |
25
25
  | `wizards connect [app …]` | Adds your wizards to your [AI apps](./ai-apps.md): every app found, or the ones you name |
26
26
  | `wizards disconnect [app …]` | Takes them out again |
27
27
  | `wizards mcp` | Runs the MCP server over stdio. AI apps start it; you don’t |
28
+ | `wizards uninstall` | Takes engenty wizards off this computer. It shows what it will remove first and asks about your data on its own; your data is kept unless you choose to delete it. `--dry-run` only shows the plan, `--yes` asks nothing and keeps your data, `--yes --delete-data` deletes it too. See [Remove](./install.md#remove) |
28
29
  | `wizards validate <path …>` | Checks wizards without the studio: `.wizard` files, unpacked wizard files, or a wizard’s JSON. `--json` prints the problems as JSON. A space’s connectors and plugins are checked when you import it |
29
30
 
30
31
  | Option | What it does |
@@ -27,7 +27,7 @@ Keychain (as `engenty-wizards`). On other systems they are stored encrypted in t
27
27
  |---|---|
28
28
  | Back up | Stop engenty wizards (`wizards stop`) and copy the folder |
29
29
  | Move to another computer | Install engenty wizards there, stop it and put the folder in place. On a Mac, enter your API keys and sign in to your account again: the Keychain doesn’t move |
30
- | Start over | Delete the folder. On a Mac, the keys in the Keychain stay and are used again. To remove them, delete the `engenty-wizards` entries in Keychain Access |
30
+ | Start over | Run `wizards uninstall`, choose to delete your data, then install again. On a Mac, the keys in the Keychain stay and are used again. To remove them, delete the `engenty-wizards` entries in Keychain Access |
31
31
  | Keep it elsewhere | Set `ENGENTY_HOME` to another folder. engenty wizards then keeps everything in its `wizards` folder there |
32
32
 
33
33
  After an update, engenty wizards brings your data up to date the next time it starts.
@@ -71,6 +71,19 @@ the right of the address bar.
71
71
 
72
72
  ## Update
73
73
 
74
+ engenty wizards updates itself: it looks for a new version when it starts and once a day,
75
+ downloads it in the background and checks it, and switches to it at the next start (a restart or
76
+ the next login), never while a wizard runs. The version before stays beside it. `wizards status`
77
+ and the studio then say “Updated to …” with what changed. To turn it off:
78
+
79
+ ```bash
80
+ wizards autoupdate off
81
+ ```
82
+
83
+ or set `WIZARDS_DISABLE_AUTOUPDATE=1` (in the terminal or in `~/.engenty/wizards/.env`). In CI,
84
+ and for an install set up without a terminal, it is off unless you turn it on with `wizards
85
+ autoupdate on`. `npx wizards` never updates itself. To update now:
86
+
74
87
  ```bash
75
88
  wizards update
76
89
  ```
@@ -88,11 +101,21 @@ brought up to date at the next start.
88
101
  ## Remove
89
102
 
90
103
  ```bash
91
- wizards stop
92
- wizards autostart off
93
- wizards disconnect
104
+ wizards uninstall
94
105
  ```
95
106
 
96
- `wizards disconnect` takes your wizards out of your AI apps again. Then delete `~/.engenty/wizards`
97
- and `~/.local/bin/wizards`. Your wizards and results are in `~/.engenty/wizards/data`:
98
- [back that folder up](./data.md) first if you want to keep them.
107
+ First it shows what it will remove: your wizards in your AI apps, starting at login, everything of
108
+ engenty wizards that is running, the program in `~/.engenty/wizards` and the command
109
+ `~/.local/bin/wizards`. Your data in `~/.engenty/wizards/data` is kept unless you say to delete
110
+ it, and installing again picks it up. [Back it up](./data.md) first if you delete it. At the end
111
+ it lists anything it couldn’t remove.
112
+
113
+ If the `wizards` command no longer runs, `npx wizards uninstall` does the same.
114
+
115
+ | Option | What it does |
116
+ |---|---|
117
+ | `--dry-run` | Only shows what it would remove |
118
+ | `--yes` | Removes everything without asking and keeps your data |
119
+ | `--yes --delete-data` | Deletes your data as well |
120
+
121
+ Without a terminal, it asks for `--yes`.
@@ -1,4 +1,5 @@
1
1
  import { isDecidedField, isLang, isLocationValue, MAX_FILES, offeredSlot, shownFields, } from "@engenty-wizards/shared/definition";
2
+ import { readNumber } from "@engenty-wizards/shared/numbers";
2
3
  import { note, saidText } from "@engenty-wizards/shared/run";
3
4
  /** The field errors in a language, for whoever reads them. */
4
5
  export function fieldErrorsIn(errors, lang) {
@@ -13,11 +14,10 @@ function coerce(field, raw) {
13
14
  }
14
15
  switch (field.kind) {
15
16
  case "number": {
16
- if (raw === "") {
17
+ if (String(raw).trim() === "") {
17
18
  return undefined;
18
19
  }
19
- const n = Number(String(raw).replace(",", "."));
20
- return Number.isFinite(n) ? n : Number.NaN;
20
+ return readNumber(raw) ?? Number.NaN;
21
21
  }
22
22
  case "toggle":
23
23
  return raw === true || raw === "true" || raw === "on";
@@ -62,8 +62,7 @@ function coerce(field, raw) {
62
62
  if (c.kind === "text") {
63
63
  return [c.id, String(v ?? "").slice(0, 2000)];
64
64
  }
65
- const n = Number(String(v ?? "").replace(",", "."));
66
- return [c.id, Number.isFinite(n) ? n : 0];
65
+ return [c.id, readNumber(v) ?? 0];
67
66
  })))
68
67
  .filter((r) => Object.values(r).some((v) => v !== "" && v !== 0))
69
68
  .slice(0, 200);
@@ -11,7 +11,7 @@ import { snapshotHtmlImages } from "../files/web-images.js";
11
11
  import { runFilmStep } from "../film/step.js";
12
12
  import { generateImageMedia, generateSpeechMedia, generateVideoMedia, } from "../media/generate.js";
13
13
  import { markMedia } from "../media/marking.js";
14
- import { attachTools, costOf, isHarnessVendor, textModel } from "../models.js";
14
+ import { agentModel, attachTools, costOf, isHarnessVendor, textModel, totalUsage, } from "../models.js";
15
15
  import { knowledgeBlock } from "../services/knowledge.js";
16
16
  import { buildStepTools } from "../tools/index.js";
17
17
  import { callPluginTool, spaceContextOf } from "../tools/plugin.js";
@@ -536,7 +536,7 @@ export async function runAgentStep(step, ctx) {
536
536
  id: `step-${step.id}`,
537
537
  name: step.title,
538
538
  instructions: system,
539
- model: resolved.model,
539
+ model: agentModel(resolved),
540
540
  tools,
541
541
  });
542
542
  // Working through a mailbox or a row of portals takes many small tool calls.
@@ -564,7 +564,7 @@ export async function runAgentStep(step, ctx) {
564
564
  const result = step.output.format === "json"
565
565
  ? await streamWithProgress(agent, message, { maxSteps, abortSignal: ctx.signal }, step, ctx)
566
566
  : await agent.generate(message, { maxSteps, abortSignal: ctx.signal });
567
- await ctx.chargeUsd(costOf(resolved, result.totalUsage ?? result.usage));
567
+ await ctx.chargeUsd(costOf(resolved, await totalUsage(result)));
568
568
  // `result.text` strings together what the model said between tool calls ("I open the page …").
569
569
  // A written result is its last message; structuring keeps everything, numbers may sit anywhere.
570
570
  const closing = String(result.steps?.at?.(-1)?.text ?? "").trim();
@@ -1,15 +1,8 @@
1
1
  import { allFields, isAudioValue, isLocationValue, itemsTotals, localeOf, locationText, wizardLang, } from "@engenty-wizards/shared/definition";
2
+ import { formatMoney } from "@engenty-wizards/shared/numbers";
2
3
  import { fontLicenseWords, gradientCss, textStyleLine, } from "@engenty-wizards/shared/projects";
3
4
  import { listAsMarkdown } from "@engenty-wizards/shared/store";
4
5
  const TEMPLATE_RE = /\{\{\s*([^}]+?)\s*\}\}/g;
5
- function money(n, currency, locale) {
6
- try {
7
- return new Intl.NumberFormat(locale, { style: "currency", currency }).format(n);
8
- }
9
- catch {
10
- return `${n.toFixed(2)} ${currency}`;
11
- }
12
- }
13
6
  /** Line items as a Markdown table with their totals — what a model needs to write a document. */
14
7
  export function itemsAsMarkdown(field, value, values, lang = "en") {
15
8
  const locale = localeOf(lang);
@@ -21,13 +14,15 @@ export function itemsAsMarkdown(field, value, values, lang = "en") {
21
14
  const head = `| ${cols.map((c) => c.label).join(" | ")} | Amount |\n|${cols.map(() => " --- ").join("|")}| ---: |`;
22
15
  const body = totals.rows.map((r) => `| ${cols
23
16
  .map((c) => c.kind === "money"
24
- ? money(Number(r[c.id]) || 0, totals.currency, locale)
17
+ ? formatMoney(Number(r[c.id]) || 0, totals.currency, locale)
25
18
  : String(r[c.id] ?? ""))
26
- .join(" | ")} | ${money(r.amount, totals.currency, locale)} |`);
19
+ .join(" | ")} | ${formatMoney(r.amount, totals.currency, locale)} |`);
27
20
  const sums = [
28
- `Net: ${money(totals.net, totals.currency, locale)}`,
29
- totals.vatRate ? `VAT ${totals.vatRate}%: ${money(totals.vat, totals.currency, locale)}` : null,
30
- `Total: ${money(totals.gross, totals.currency, locale)}`,
21
+ `Net: ${formatMoney(totals.net, totals.currency, locale)}`,
22
+ totals.vatRate
23
+ ? `VAT ${totals.vatRate}%: ${formatMoney(totals.vat, totals.currency, locale)}`
24
+ : null,
25
+ `Total: ${formatMoney(totals.gross, totals.currency, locale)}`,
31
26
  ].filter(Boolean);
32
27
  return [head, ...body, "", ...sums].join("\n");
33
28
  }
@@ -23,8 +23,7 @@ function loadEnvFiles() {
23
23
  loadEnvFiles();
24
24
  const str = (key, fallback = "") => process.env[key]?.trim() || fallback;
25
25
  /**
26
- * Without `SANDBOX`: Docker when the sandbox image is on this machine, else agentOS when its
27
- * packages are installed (a checkout has them, the image and the desktop app do not), else none.
26
+ * Without `SANDBOX`: Docker when the sandbox image is on this machine, else none.
28
27
  * The image is not published, so a fresh install would otherwise offer a tool that cannot start.
29
28
  */
30
29
  function sandboxEngine(image) {
@@ -33,12 +32,9 @@ function sandboxEngine(image) {
33
32
  return "off";
34
33
  }
35
34
  if (engine) {
36
- return engine === "agentos" ? "agentos" : "docker";
37
- }
38
- if (dockerHasImage(image)) {
39
35
  return "docker";
40
36
  }
41
- return agentOsInstalled() ? "agentos" : "off";
37
+ return dockerHasImage(image) ? "docker" : "off";
42
38
  }
43
39
  function dockerHasImage(image) {
44
40
  try {
@@ -49,19 +45,6 @@ function dockerHasImage(image) {
49
45
  return false;
50
46
  }
51
47
  }
52
- /** agentOS ships its sidecar for macOS and Linux on arm64 and x64 only. */
53
- function agentOsInstalled() {
54
- if (!["darwin", "linux"].includes(process.platform) || !["arm64", "x64"].includes(process.arch)) {
55
- return false;
56
- }
57
- try {
58
- import.meta.resolve("@rivet-dev/agentos-core");
59
- return true;
60
- }
61
- catch {
62
- return false;
63
- }
64
- }
65
48
  /** A comma-separated list. */
66
49
  const list = (key) => str(key)
67
50
  .split(",")
@@ -234,7 +217,7 @@ export const env = {
234
217
  : "/usr/bin/chromium")),
235
218
  /** Encodes widget animations to MP4; without it widgets offer no video. */
236
219
  ffmpegPath: str("FFMPEG_PATH", "ffmpeg"),
237
- /** What runs an agent's shell and code: a Docker container, an agentOS VM, or nothing. */
220
+ /** What runs an agent's shell and code: a Docker container or nothing. */
238
221
  sandbox: sandboxEngine(sandboxImage),
239
222
  sandboxImage,
240
223
  /** Assets and workspace files in an S3-compatible bucket (R2); empty = the data folder. */
@@ -1,6 +1,6 @@
1
1
  import { mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
- import { declaredByPlugin, defaultFeatures, FEATURES, parseFeatures, resolveFeatures, } from "@engenty-wizards/shared/features";
3
+ import { declaredByPlugin, defaultFeatures, FEATURES, featureOn, parseFeatures, resolveFeatures, } from "@engenty-wizards/shared/features";
4
4
  import { accountOverview } from "./auth/account.js";
5
5
  import { isCheckout, layout, packageRoot } from "./cli/home.js";
6
6
  import { env } from "./env.js";
@@ -61,7 +61,7 @@ export function features(remote) {
61
61
  return resolveFeatures(defaultFeatures(declaredFeatures()), envFeatures(), remote, fileFeatures());
62
62
  }
63
63
  export function feature(name, remote) {
64
- return features(remote)[name] === true;
64
+ return featureOn(features(remote), name);
65
65
  }
66
66
  let accountCache = null;
67
67
  /**
@@ -17,7 +17,7 @@ import { purgeExpiredRuns } from "./services/shares.js";
17
17
  import { purgeUnusedStores } from "./store/index.js";
18
18
  import { forgetRunner, registerRunner } from "./tenants/control.js";
19
19
  import { LOCAL_TENANT } from "./tenants/tenant.js";
20
- import { onRestart, RESTART_EXIT } from "./update.js";
20
+ import { onRestart, RESTART_EXIT, startUpdateChecks } from "./update.js";
21
21
  // Before anything prints: the last lines stay in memory for a look from afar (debug.ts).
22
22
  hookConsole();
23
23
  await migrateControlDb();
@@ -39,6 +39,8 @@ else {
39
39
  void loadCatalog();
40
40
  void checkModelsAtStart().catch(() => undefined);
41
41
  startProChecks();
42
+ // A newer release is downloaded beside this one and used from the next start.
43
+ startUpdateChecks();
42
44
  }
43
45
  // The marketplace's starters and the starred entries, kept for offline use; checked hourly.
44
46
  void syncMarketplace();
@@ -202,6 +202,13 @@ export async function tenantChannels(id) {
202
202
  channels.set(id, { at: Date.now(), value });
203
203
  return value;
204
204
  }
205
+ /**
206
+ * Forgets what the Manage-App said of the tenant's numbers: the next `tenantChannels` asks.
207
+ * The runtime tests of the Manage-App's channel plugins (sms, calls, whatsapp) call it.
208
+ */
209
+ export function forgetChannels(id) {
210
+ channels.delete(id);
211
+ }
205
212
  /** A person of the tenant asks for its number; what the Manage-App answers with is the request. */
206
213
  export async function requestChannelNumber(id, input) {
207
214
  channels.delete(id);
@@ -1042,6 +1042,18 @@ export function usageOf(u) {
1042
1042
  cachedInputTokens: u?.cachedInputTokens ?? u?.inputTokenDetails?.cacheReadTokens ?? 0,
1043
1043
  };
1044
1044
  }
1045
+ /** A class's model as Mastra's `Agent` takes it: the AI SDK's model, typed by Mastra's own copy of it. */
1046
+ export function agentModel(resolved) {
1047
+ return resolved.model;
1048
+ }
1049
+ /**
1050
+ * What an agent's call used over all its steps: `totalUsage` where Mastra's result has it, else
1051
+ * `usage`. Takes a stream's promises and a finished call's values alike.
1052
+ */
1053
+ export async function totalUsage(out) {
1054
+ const result = out;
1055
+ return (await result.totalUsage) ?? (await result.usage);
1056
+ }
1045
1057
  /** Provider cost of a text call a runtime that runs alone made; the gateway books the others itself. */
1046
1058
  export function costOf(resolved, usage) {
1047
1059
  // An installed client answers on its subscription: nothing to book.
@@ -6,8 +6,6 @@ const WRITE_CHUNK = 48 * 1024;
6
6
  /** What the engenty-sandbox image carries. The container sits on Docker's default bridge. */
7
7
  export const DOCKER_CAPABILITIES = {
8
8
  commands: ["bash", "python3", "pip", "uv", "node", "npm", "bun", "jq", "curl", "git", "unzip"],
9
- python: "shell",
10
- pythonPackages: "install what you need with pip or uv",
11
9
  network: "open internet",
12
10
  uses: "calculations, charts or file conversion",
13
11
  workdir: WORKDIR,
@@ -1,27 +1,20 @@
1
1
  import { env } from "../env.js";
2
- import { AGENTOS_CAPABILITIES, AgentOsSandbox } from "./agentos.js";
3
2
  import { DOCKER_CAPABILITIES, DockerRunSandbox } from "./docker.js";
4
3
  /** What this server's sandbox runs, or null when it is switched off. */
5
4
  export function sandboxCapabilities() {
6
5
  if (env.sandbox === "off") {
7
6
  return null;
8
7
  }
9
- return env.sandbox === "agentos" ? AGENTOS_CAPABILITIES : DOCKER_CAPABILITIES;
8
+ return DOCKER_CAPABILITIES;
10
9
  }
11
10
  export function createSandbox(runId) {
12
11
  if (env.sandbox === "off") {
13
12
  throw new Error("The sandbox is switched off on this server.");
14
13
  }
15
- return env.sandbox === "agentos"
16
- ? new AgentOsSandbox()
17
- : new DockerRunSandbox(runId, env.sandboxImage);
14
+ return new DockerRunSandbox(runId, env.sandboxImage);
18
15
  }
19
16
  export function shellToolDescription(caps) {
20
- const python = caps.python === "tool" ? " Python is not a shell command here: use run_python." : "";
21
- return `Run a shell command in a private Linux sandbox (${caps.commands.join(", ")}). Working directory ${caps.workdir}. Network: ${caps.network}.${python}`;
22
- }
23
- export function pythonToolDescription(caps) {
24
- return `Run Python code in the sandbox (${caps.pythonPackages}). Working directory ${caps.workdir}, shared with run_command and export_file.`;
17
+ return `Run a shell command in a private Linux sandbox (${caps.commands.join(", ")}). Working directory ${caps.workdir}. Network: ${caps.network}.`;
25
18
  }
26
19
  /** The authoring guide's line about the sandbox tool. */
27
20
  export function sandboxGuideLine() {
@@ -1,4 +1,5 @@
1
1
  import { CATEGORY_TYPES, dayOf, } from "@engenty-wizards/shared/knowledge";
2
+ import { readNumber } from "@engenty-wizards/shared/numbers";
2
3
  import { note } from "@engenty-wizards/shared/run";
3
4
  import { and, asc, eq, inArray, isNotNull, or, sql } from "drizzle-orm";
4
5
  import { nanoid } from "nanoid";
@@ -42,32 +43,9 @@ function refWhere(ref) {
42
43
  return eq(t.fileId, ref.fileId);
43
44
  }
44
45
  // --- typed values ---------------------------------------------------------------
45
- /** A number as people write it: `38 h`, `20.000 €`, `1,5`, `1.234,50`. */
46
+ /** A number with what stands around it, as `readNumber` reads it: `38 h`, `19 %`, `1,5`. */
46
47
  export function numberOf(value) {
47
- if (typeof value === "number") {
48
- return Number.isFinite(value) ? value : null;
49
- }
50
- if (typeof value !== "string") {
51
- return null;
52
- }
53
- let s = value.replace(/[^\d,.-]/g, "");
54
- if (!/\d/.test(s)) {
55
- return null;
56
- }
57
- if (s.includes(",") && s.includes(".")) {
58
- s =
59
- s.lastIndexOf(",") > s.lastIndexOf(".")
60
- ? s.replace(/\./g, "").replace(",", ".")
61
- : s.replace(/,/g, "");
62
- }
63
- else if (s.includes(",")) {
64
- s = /^-?\d{1,3}(,\d{3}){2,}$/.test(s) ? s.replace(/,/g, "") : s.replace(",", ".");
65
- }
66
- else if (/^-?\d{1,3}(\.\d{3})+$/.test(s)) {
67
- s = s.replace(/\./g, "");
68
- }
69
- const n = Number(s);
70
- return Number.isFinite(n) ? n : null;
48
+ return readNumber(value, { units: true });
71
49
  }
72
50
  /** A day, or a day and a time, as milliseconds (UTC, as written: no time zone is applied). */
73
51
  export function dateOf(value) {
@@ -9,7 +9,7 @@ import { nodeScript, PAGE_SCRIPT, settleScript, targetScript, } from "../browser
9
9
  import { env } from "../env.js";
10
10
  import { costOf, textModel } from "../models.js";
11
11
  import { assertPublicUrl } from "./net-guard.js";
12
- import { attempt } from "./shared.js";
12
+ import { attempt, imageOutput } from "./shared.js";
13
13
  /** The page as an agent step reads it; retried while the page is between two documents. */
14
14
  export async function readPage(page) {
15
15
  for (let attempt = 0; attempt < 5; attempt++) {
@@ -473,19 +473,7 @@ export async function browserTools(ctx, assets, files) {
473
473
  }
474
474
  return { url: p.url(), saved, screenshot: Buffer.from(png).toString("base64") };
475
475
  }),
476
- toModelOutput: (out) => {
477
- if (!out?.screenshot) {
478
- return { type: "json", value: out };
479
- }
480
- const { screenshot, ...rest } = out;
481
- return {
482
- type: "content",
483
- value: [
484
- { type: "text", text: JSON.stringify(rest) },
485
- { type: "image-data", data: screenshot, mediaType: "image/png" },
486
- ],
487
- };
488
- },
476
+ toModelOutput: imageOutput("screenshot"),
489
477
  });
490
478
  const handOver = async (input) => {
491
479
  const p = await page();
@@ -8,7 +8,7 @@ import { generateImageMedia } from "../media/generate.js";
8
8
  import { markMedia } from "../media/marking.js";
9
9
  import { gatewayTools, isHarnessVendor } from "../models.js";
10
10
  import { htmlToMarkdown } from "../render/convert.js";
11
- import { pythonToolDescription, sandboxCapabilities, shellToolDescription, } from "../sandbox/index.js";
11
+ import { sandboxCapabilities, shellToolDescription } from "../sandbox/index.js";
12
12
  import { snapshotFile } from "../services/files.js";
13
13
  import { hasStoreFiles } from "../store/index.js";
14
14
  import { browserTools } from "./browser.js";
@@ -183,24 +183,6 @@ export async function buildStepTools(step, ctx, resolved, uploads) {
183
183
  .exec(command, { timeoutMs: timeoutMs(timeoutSeconds) }));
184
184
  },
185
185
  });
186
- if (sandbox.python === "tool") {
187
- tools.run_python = createTool({
188
- id: "run_python",
189
- description: pythonToolDescription(sandbox),
190
- inputSchema: z.object({
191
- code: z.string(),
192
- packages: z.array(z.string()).optional().describe("PyPI packages the code imports"),
193
- timeoutSeconds: z.number().optional(),
194
- }),
195
- execute: async ({ code, packages, timeoutSeconds }) => {
196
- await ctx.emit("tool", { code: "python" });
197
- return report(await ctx.resources.sandboxHandle().runPython(code, {
198
- packages,
199
- timeoutMs: timeoutMs(timeoutSeconds),
200
- }));
201
- },
202
- });
203
- }
204
186
  tools.export_file = createTool({
205
187
  id: "export_file",
206
188
  description: "Hand a file from the sandbox to the person as a result (e.g. a chart PNG, a CSV).",
@@ -2,7 +2,7 @@ import { createTool } from "@mastra/core/tools";
2
2
  import { z } from "zod";
3
3
  import { connectionContext } from "../connectors/index.js";
4
4
  import { mailAction, mailSearchInput, } from "../connectors/mail.js";
5
- import { htmlToPdf } from "../render/convert.js";
5
+ import { escapeHtml, htmlToPdf } from "../render/convert.js";
6
6
  import { guardHtml } from "../render/guard.js";
7
7
  import { attempt, clip } from "./shared.js";
8
8
  const NOT_CONNECTED = "The person has not connected a mailbox. Go on without mail and say in your result that the mailbox is not connected.";
@@ -102,6 +102,3 @@ export function mailTools(step, ctx, files) {
102
102
  });
103
103
  return tools;
104
104
  }
105
- function escapeHtml(s) {
106
- return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
107
- }
@@ -1,17 +1,44 @@
1
+ import { ServiceError } from "../services/errors.js";
1
2
  import { writeStoreFile } from "../store/index.js";
2
3
  export const TEXT_LIMIT = 8000;
3
4
  export function clip(s, limit = TEXT_LIMIT) {
4
5
  return s.length > limit ? `${s.slice(0, limit)}\n…[truncated ${s.length - limit} chars]` : s;
5
6
  }
6
- /** A tool failure is something the model reads and reacts to, never a crashed step. */
7
- export async function attempt(run) {
7
+ /**
8
+ * A tool failure is something the model reads and reacts to, never a crashed step or turn. A
9
+ * service's refusal brings its data along (`issues`, `fields`, `column`), so the model can fix
10
+ * what it sent. The message is clipped to `limit` characters (400): a site or an API can answer
11
+ * with a whole page; the studio's agents, whose failures are the runtime's own, pass `Infinity`.
12
+ */
13
+ export async function attempt(run, { limit = 400 } = {}) {
8
14
  try {
9
15
  return await run();
10
16
  }
11
17
  catch (err) {
12
- return { error: String(err?.message ?? err).slice(0, 400) };
18
+ const error = String(err?.message ?? err).slice(0, limit);
19
+ return err instanceof ServiceError ? { error, ...err.data } : { error };
13
20
  }
14
21
  }
22
+ /**
23
+ * A tool's `toModelOutput` for a result that carries a picture: the base64 under `key` goes to the
24
+ * model as an image (of `mime`, else PNG), the rest as text beside it – as JSON, or as `text`
25
+ * makes it. A result without the picture, a failure among them, goes as JSON.
26
+ */
27
+ export function imageOutput(key, text = (rest) => JSON.stringify(rest)) {
28
+ return (out) => {
29
+ if (!out || out.error || typeof out[key] !== "string" || !out[key]) {
30
+ return { type: "json", value: out };
31
+ }
32
+ const { [key]: data, mime, ...rest } = out;
33
+ return {
34
+ type: "content",
35
+ value: [
36
+ { type: "text", text: text(rest) },
37
+ { type: "image-data", data, mediaType: mime ?? "image/png" },
38
+ ],
39
+ };
40
+ };
41
+ }
15
42
  /**
16
43
  * Files a step collects go two places: the wizard's store (kept for next time) and the step's
17
44
  * result (this run's download). One keeper per step, so a file saved twice is one result.
@@ -1,7 +1,9 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { mkdirSync, openSync } from "node:fs";
3
3
  import { join } from "node:path";
4
+ import { autoUpdatePolicy, isNewer, scheduleUpdateChecks, updateNotice, } from "./cli/autoupdate.js";
4
5
  import { installedByScript, isCheckout, layout, packageRoot, packageVersion } from "./cli/home.js";
6
+ export { isNewer };
5
7
  const RELEASES_URL = process.env.ENGENTY_WIZARDS_RELEASES_URL?.trim() ||
6
8
  "https://api.github.com/repos/engenty/wizards/releases/latest";
7
9
  /** How long an answer from GitHub holds, found or not, before the runtime asks again. */
@@ -23,25 +25,6 @@ export function onRestart(shutdown) {
23
25
  function restarts() {
24
26
  return process.env[RESTARTS] === "1" && restart !== null;
25
27
  }
26
- /** `0.2.0` or `v0.2.0` as numbers; anything after a `-` (a pre-release) is ignored. */
27
- function numbers(version) {
28
- return version
29
- .replace(/^v/, "")
30
- .split("-")[0]
31
- .split(".")
32
- .map((part) => Number.parseInt(part, 10) || 0);
33
- }
34
- export function isNewer(latest, current) {
35
- const a = numbers(latest);
36
- const b = numbers(current);
37
- for (let i = 0; i < Math.max(a.length, b.length); i++) {
38
- const diff = (a[i] ?? 0) - (b[i] ?? 0);
39
- if (diff !== 0) {
40
- return diff > 0;
41
- }
42
- }
43
- return false;
44
- }
45
28
  const enabled = () => !process.env.ENGENTY_WIZARDS_NO_UPDATE_CHECK;
46
29
  /** The newest release GitHub knows; `fresh` asks again although the last answer still holds. */
47
30
  async function newestRelease(fresh = false) {
@@ -78,6 +61,8 @@ export async function updateStatus(fresh = false) {
78
61
  const current = packageVersion();
79
62
  const { latest, url } = await newestRelease(fresh);
80
63
  const kind = installKind();
64
+ const paths = layout();
65
+ const notice = kind === "script" ? updateNotice(paths, current) : { updated: null, pending: null };
81
66
  return {
82
67
  current,
83
68
  latest,
@@ -89,8 +74,14 @@ export async function updateStatus(fresh = false) {
89
74
  applying,
90
75
  checkedAt: cached?.at ?? null,
91
76
  enabled: enabled(),
77
+ ...notice,
92
78
  };
93
79
  }
80
+ const autoPolicy = () => autoUpdatePolicy(layout(), { installed: installKind() === "script" });
81
+ /** The runtime looks for an update when it starts and once a day; it switches nothing itself. */
82
+ export function startUpdateChecks() {
83
+ scheduleUpdateChecks(layout(), autoPolicy);
84
+ }
94
85
  /**
95
86
  * Runs `wizards update` in the background, the same steps as in a terminal. Afterwards a
96
87
  * runtime started by the command restarts into the new version; any other keeps running the old
@@ -0,0 +1 @@
1
+ import{r as e}from"./rolldown-runtime-hePW80VL.js";import{n as t,t as n}from"./jsx-runtime-BNakU3Ej.js";import{$t as r,Ft as i,Hn as a,It as o,Kt as s,Nt as c,Pt as l,Vt as u,Wt as d,fr as f,vr as p}from"./ui-Brg2qM1A.js";import{t as m}from"./preload-helper-CNEI7N0N.js";import{G as h,U as g,W as _,xt as v}from"./index-mBRI7ZQp.js";import{c as y,f as b}from"./settings-menu-BMMkIVP1.js";import{ReleaseNotes as x,loadReleases as S,releaseDate as C,releaseLabel as w}from"./ChangelogDialog-DcQqFbiW.js";var T=e(t(),1),E=[{name:`engenty wizards`,description:`credit.engenty-wizards`,homepage:`https://github.com/engenty/wizards`,license:`FSL-1.1-MIT`},{name:`engenty`,description:`credit.engenty`,homepage:`https://github.com/engenty/engenty`,license:`FSL-1.1-MIT`}],D=[{name:`Mastra`,description:`credit.mastra`,homepage:`https://github.com/mastra-ai/mastra`,license:`Apache-2.0`},{name:`AI SDK`,description:`credit.ai-sdk`,homepage:`https://github.com/vercel/ai`,license:`Apache-2.0`},{name:`Model Context Protocol`,description:`credit.model-context-protocol`,homepage:`https://github.com/modelcontextprotocol/typescript-sdk`,license:`MIT`},{name:`Hono`,description:`credit.hono`,homepage:`https://github.com/honojs/hono`,license:`MIT`},{name:`Drizzle ORM`,description:`credit.drizzle-orm`,homepage:`https://github.com/drizzle-team/drizzle-orm`,license:`Apache-2.0`},{name:`libSQL`,description:`credit.libsql`,homepage:`https://github.com/tursodatabase/libsql`,license:`MIT`},{name:`Playwright`,description:`credit.playwright`,homepage:`https://github.com/microsoft/playwright`,license:`Apache-2.0`},{name:`React`,description:`credit.react`,homepage:`https://github.com/facebook/react`,license:`MIT`},{name:`Vite`,description:`credit.vite`,homepage:`https://github.com/vitejs/vite`,license:`MIT`},{name:`Tailwind CSS`,description:`credit.tailwind-css`,homepage:`https://github.com/tailwindlabs/tailwindcss`,license:`MIT`},{name:`React Flow`,description:`credit.react-flow`,homepage:`https://github.com/xyflow/xyflow`,license:`MIT`},{name:`TanStack Query`,description:`credit.tanstack-query`,homepage:`https://github.com/TanStack/query`,license:`MIT`},{name:`xterm.js`,description:`credit.xterm-js`,homepage:`https://github.com/xtermjs/xterm.js`,license:`MIT`},{name:`Lucide`,description:`credit.lucide`,homepage:`https://github.com/lucide-icons/lucide`,license:`ISC`},{name:`Geist & Geist Mono`,description:`credit.geist-geist-mono`,homepage:`https://github.com/vercel/geist-font`,license:`OFL-1.1`},{name:`Space Grotesk`,description:`credit.space-grotesk`,homepage:`https://github.com/floriankarsten/space-grotesk`,license:`OFL-1.1`},{name:`git-cliff`,description:`credit.git-cliff`,homepage:`https://github.com/orhun/git-cliff`,license:`MIT OR Apache-2.0`}],O=e(n(),1),k=(e,...t)=>t.some(t=>t?.toLowerCase().includes(e));function A({name:e,homepage:t,version:n,license:r,description:i}){return(0,O.jsxs)(`li`,{className:`py-2.5`,children:[(0,O.jsxs)(`div`,{className:`flex flex-wrap items-baseline gap-x-2.5 gap-y-0.5`,children:[t?(0,O.jsxs)(`a`,{href:t,target:`_blank`,rel:`noreferrer`,className:`inline-flex items-center gap-1 font-medium text-[0.875rem] text-ink underline-offset-4 hover:underline`,children:[e,(0,O.jsx)(f,{"aria-hidden":!0,className:`size-3 text-ink-4`})]}):(0,O.jsx)(`span`,{className:`font-medium text-[0.875rem] text-ink`,children:e}),n?(0,O.jsx)(`span`,{className:`text-[0.75rem] text-ink-3 tabular-nums`,children:n}):null,(0,O.jsx)(`span`,{className:`text-[0.75rem] text-ink-3`,children:r})]}),i?(0,O.jsx)(`p`,{className:`mt-0.5 text-[0.8125rem] text-ink-3`,children:i}):null]})}function j({title:e,hint:t,children:n}){return(0,O.jsxs)(`section`,{className:`mt-6`,children:[(0,O.jsx)(`h3`,{className:`border-border-soft border-b pb-2 font-display font-semibold text-[1rem]`,children:e}),t?(0,O.jsx)(`p`,{className:`mt-2 text-[0.8125rem] text-ink-3`,children:t}):null,n]})}function M({credits:e}){return(0,O.jsx)(`ul`,{className:`divide-y divide-border-soft`,children:e.map(e=>(0,O.jsx)(A,{name:e.name,homepage:e.homepage,license:e.license,description:r(e.description)},e.name))})}function N({label:e,packages:t}){return(0,O.jsxs)(`div`,{className:`mt-4`,children:[(0,O.jsx)(`h4`,{className:`font-medium text-[0.75rem] text-ink-3 tracking-wide`,children:e}),(0,O.jsx)(`ul`,{className:`divide-y divide-border-soft`,children:t.map(e=>(0,O.jsx)(A,{...e},e.name))})]})}function P({active:e}){let[t,n]=(0,T.useState)(null),[i,a]=(0,T.useState)(``);(0,T.useEffect)(()=>{if(!e||t)return;let r=!1;return m(()=>import(`./oss-credits-BJrR7BR1.js`).then(e=>{r||n(e.default)}),[]),()=>{r=!0}},[e,t]),(0,T.useEffect)(()=>{e||a(``)},[e]);let s=i.trim().toLowerCase(),c=e=>s?e.filter(e=>k(s,e.name,e.license,r(e.description))):e,d=e=>s?e.filter(e=>k(s,e.name,e.license,e.version)):e,f=c(E),p=c(D),h=t?[{id:`shared`,label:r(`about.creditsShared`),packages:d(t.shared)},...t.groups.map(e=>({id:e.id,label:e.label,packages:d(e.packages)}))].filter(e=>e.packages.length>0):[],g=f.length===0&&p.length===0&&h.length===0;return(0,O.jsxs)(O.Fragment,{children:[(0,O.jsx)(`p`,{className:`mb-4 text-[0.875rem] text-ink-3`,children:r(`about.creditsHint`)}),(0,O.jsx)(o,{type:`search`,value:i,placeholder:r(`about.search`),onChange:e=>a(e.target.value)}),f.length>0?(0,O.jsx)(j,{title:r(`about.creditsProduct`),children:(0,O.jsx)(M,{credits:f})}):null,p.length>0?(0,O.jsx)(j,{title:r(`about.creditsBuiltWith`),children:(0,O.jsx)(M,{credits:p})}):null,t===null?(0,O.jsx)(`div`,{className:`flex justify-center py-10 text-ink-3`,children:(0,O.jsx)(u,{})}):h.length>0?(0,O.jsx)(j,{title:r(`about.creditsDeps`),hint:s?void 0:r(`about.creditsDepsHint`),children:h.map(e=>(0,O.jsx)(N,{label:e.label,packages:e.packages},e.id))}):null,t!==null&&g?(0,O.jsx)(l,{children:r(`about.noMatches`)}):null]})}var F=[`new`,`earlier`,`credits`],I=e=>r(e===`new`?`about.tab.new`:e===`earlier`?`about.tab.earlier`:`about.credits`),L=e=>e===1?r(`about.change`):r(`about.changes`,{n:e});function R({open:e,onClose:t,me:n}){let o=n?.mode===`local`,{data:l,latest:f}=h(o&&e),[m,E]=(0,T.useState)(null),[D,k]=(0,T.useState)(`new`),A=b(F.indexOf(D)),j=l?.newer&&f?f:_;(0,T.useEffect)(()=>{if(!e)return;let t=!1;return E(null),S(j===`0.7.0`?void 0:j).then(e=>{t||E(e)}),()=>{t=!0}},[e,j]);let M=m?.find(e=>e.version===`v${j}`)??m?.[0]??null,N=m?.filter(e=>e!==M)??[],R=o?l?.kind??null:`cloud`;return(0,O.jsxs)(c,{open:e,onClose:t,wide:!0,height:`tall`,children:[(0,O.jsx)(`div`,{className:`-mt-2 -mr-2 sticky top-0 z-10 flex h-0 justify-end`,children:(0,O.jsx)(i,{label:r(`common.close`),onClick:t,children:(0,O.jsx)(a,{className:`size-4`})})}),(0,O.jsxs)(`div`,{className:`flex flex-col items-center pt-4 text-center`,children:[(0,O.jsx)(v,{size:96}),(0,O.jsxs)(`div`,{className:`mt-4 font-display font-semibold text-[1.5rem] tracking-tight`,children:[`engenty`,(0,O.jsx)(`span`,{className:`text-ember`,children:`.`}),(0,O.jsx)(`span`,{className:`ml-1.5 font-normal text-ink-3`,children:`wizards`})]}),(0,O.jsxs)(`div`,{className:`mt-1 text-[0.9375rem] text-ink-3 tabular-nums`,children:[r(`about.version`,{v:_}),R?` · ${r(`about.kind.${R}`)}`:``]})]}),o?(0,O.jsx)(`div`,{className:`mt-5`,children:(0,O.jsx)(g,{enabled:e})}):null,(0,O.jsx)(`div`,{className:`mt-6 flex justify-center`,children:(0,O.jsx)(d,{value:D,onChange:k,options:F.map(e=>({id:e,label:I(e)})),label:r(`about.title`)})}),(0,O.jsxs)(`div`,{className:s(`mt-5`,y(A)),children:[D===`new`?m===null?(0,O.jsx)(`div`,{className:`flex justify-center py-6 text-ink-3`,children:(0,O.jsx)(u,{})}):M?(0,O.jsxs)(O.Fragment,{children:[(0,O.jsxs)(`h3`,{className:`flex items-baseline gap-3 border-border-soft border-b pb-2 font-display font-semibold text-[1rem]`,children:[w(M),(0,O.jsx)(`span`,{className:`font-normal font-sans text-[0.8125rem] text-ink-3 tabular-nums`,children:C(M)})]}),(0,O.jsx)(`div`,{className:`mt-3`,children:(0,O.jsx)(x,{release:M})})]}):(0,O.jsx)(`p`,{className:`text-[0.875rem] text-ink-3`,children:r(`about.changelogEmpty`)}):null,D===`earlier`?(0,O.jsx)(`div`,{className:`flex flex-col divide-y divide-border-soft`,children:N.map(e=>(0,O.jsxs)(`details`,{className:`group/r py-2`,children:[(0,O.jsxs)(`summary`,{className:`flex cursor-pointer list-none items-baseline gap-3 text-[0.875rem] [&::-webkit-details-marker]:hidden`,children:[(0,O.jsx)(p,{className:`size-3.5 shrink-0 self-center text-ink-4 transition group-open/r:rotate-90`}),(0,O.jsx)(`span`,{className:`font-medium text-ink tabular-nums`,children:w(e)}),(0,O.jsx)(`span`,{className:`text-[0.8125rem] text-ink-3 tabular-nums`,children:C(e)}),(0,O.jsx)(`span`,{className:`ml-auto text-[0.8125rem] text-ink-4`,children:L(e.commits.length)})]}),(0,O.jsx)(`div`,{className:`mt-2 pl-6`,children:(0,O.jsx)(x,{release:e})})]},e.version??`unreleased`))}):null,D===`credits`?(0,O.jsx)(P,{active:!0}):null]},D)]})}export{R as AboutDialog};
@@ -1 +1 @@
1
- import{r as e}from"./rolldown-runtime-hePW80VL.js";import{n as t,t as n}from"./jsx-runtime-BNakU3Ej.js";import{$t as r,It as i,Kt as a,Nt as o,Pt as s,Vt as c}from"./ui-rh8TaWvT.js";import{t as l}from"./preload-helper-CNEI7N0N.js";var u=e(t(),1),d=e(n(),1);function f(e){switch(e.group){case`Added`:return{label:`NEW`,dot:`bg-moss`};case`Fixed`:return{label:`FIXED`,dot:`bg-amber`};case`Changed`:return{label:`CHANGED`,dot:`bg-cobalt`};case`Performance`:return{label:`PERFORMANCE`,dot:`bg-ember`};case`Deploy`:return{label:`DEPLOY`,dot:`bg-ink-4`};case`Docs`:return{label:`DOCS`,dot:`bg-ink-4`};default:return{label:`OTHER`,dot:`bg-ink-4`}}}var p=e=>e.version?e.version.replace(/^v/,``):r(`about.unreleased`),m=e=>e.timestamp?new Date(e.timestamp*1e3).toISOString().slice(0,10):``,h=e=>e.charAt(0).toUpperCase()+e.slice(1);function g(e,t){let n=t.trim().toLowerCase();if(!n)return e;let r=[];for(let t of e){if(`${p(t)} ${m(t)}`.toLowerCase().includes(n)){r.push(t);continue}let e=t.commits.filter(e=>`${f(e).label} ${e.scope??``} ${e.message}`.toLowerCase().includes(n));e.length>0&&r.push({...t,commits:e})}return r}async function _(e){if(e)try{let t=await fetch(`https://raw.githubusercontent.com/engenty/engenty-wizards/v${e}/apps/web/src/about/changelog.json`,{signal:AbortSignal.timeout(5e3)});if(t.ok)return await t.json()}catch{}return(await l(async()=>{let{default:e}=await import(`./changelog-CtyrKHAc.js`);return{default:e}},[])).default}function v({release:e}){return(0,d.jsx)(`ul`,{className:`flex flex-col gap-2`,children:e.commits.map(e=>{let t=f(e);return(0,d.jsxs)(`li`,{className:`flex items-start gap-2.5 text-[0.875rem]`,children:[(0,d.jsx)(`span`,{className:a(`mt-[7px] size-1.5 shrink-0 rounded-full`,t.dot)}),(0,d.jsxs)(`span`,{className:`min-w-0 text-ink-2`,children:[(0,d.jsxs)(`span`,{className:`font-semibold text-[0.75rem] text-ink tracking-wide`,children:[t.label,e.scope?` [${e.scope}]`:``]}),` `,h(e.message),e.breaking?(0,d.jsx)(`span`,{className:`ml-1.5 rounded-full bg-rose-tint px-1.5 py-0.5 font-medium text-[0.75rem] text-rose`,children:r(`about.breaking`)}):null]})]},e.id)})})}function y({open:e,onClose:t,version:n}){let[a,l]=(0,u.useState)(null),[f,h]=(0,u.useState)(``);(0,u.useEffect)(()=>{if(!e||a)return;let t=!1;return _(n).then(e=>{t||l(e)}),()=>{t=!0}},[e,a,n]),(0,u.useEffect)(()=>{e||h(``)},[e]);let y=a?g(a,f):[];return(0,d.jsxs)(o,{open:e,onClose:t,title:r(`about.changelog`),wide:!0,height:`tall`,children:[(0,d.jsx)(`p`,{className:`-mt-3 mb-4 text-[0.875rem] text-ink-3`,children:r(`about.changelogHint`)}),a===null?(0,d.jsx)(`div`,{className:`flex justify-center py-10 text-ink-3`,children:(0,d.jsx)(c,{})}):a.length===0?(0,d.jsx)(s,{children:r(`about.changelogEmpty`)}):(0,d.jsxs)(d.Fragment,{children:[(0,d.jsx)(i,{type:`search`,value:f,placeholder:r(`about.search`),onChange:e=>h(e.target.value)}),y.length===0?(0,d.jsx)(s,{children:r(`about.noMatches`)}):null,y.map(e=>(0,d.jsxs)(`section`,{className:`mt-6`,children:[(0,d.jsxs)(`h3`,{className:`flex items-baseline gap-3 border-border-soft border-b pb-2 font-display font-semibold text-[1rem]`,children:[p(e),(0,d.jsx)(`span`,{className:`font-normal font-sans text-[0.8125rem] text-ink-3 tabular-nums`,children:m(e)})]}),(0,d.jsx)(`div`,{className:`mt-3`,children:(0,d.jsx)(v,{release:e})})]},e.version??`unreleased`))]})]})}export{y as ChangelogDialog,v as ReleaseNotes,_ as loadReleases,m as releaseDate,p as releaseLabel};
1
+ import{r as e}from"./rolldown-runtime-hePW80VL.js";import{n as t,t as n}from"./jsx-runtime-BNakU3Ej.js";import{$t as r,It as i,Kt as a,Nt as o,Pt as s,Vt as c}from"./ui-Brg2qM1A.js";import{t as l}from"./preload-helper-CNEI7N0N.js";var u=e(t(),1),d=e(n(),1);function f(e){switch(e.group){case`Added`:return{label:`NEW`,dot:`bg-moss`};case`Fixed`:return{label:`FIXED`,dot:`bg-amber`};case`Changed`:return{label:`CHANGED`,dot:`bg-cobalt`};case`Performance`:return{label:`PERFORMANCE`,dot:`bg-ember`};case`Deploy`:return{label:`DEPLOY`,dot:`bg-ink-4`};case`Docs`:return{label:`DOCS`,dot:`bg-ink-4`};default:return{label:`OTHER`,dot:`bg-ink-4`}}}var p=e=>e.version?e.version.replace(/^v/,``):r(`about.unreleased`),m=e=>e.timestamp?new Date(e.timestamp*1e3).toISOString().slice(0,10):``,h=e=>e.charAt(0).toUpperCase()+e.slice(1);function g(e,t){let n=t.trim().toLowerCase();if(!n)return e;let r=[];for(let t of e){if(`${p(t)} ${m(t)}`.toLowerCase().includes(n)){r.push(t);continue}let e=t.commits.filter(e=>`${f(e).label} ${e.scope??``} ${e.message}`.toLowerCase().includes(n));e.length>0&&r.push({...t,commits:e})}return r}async function _(e){if(e)try{let t=await fetch(`https://raw.githubusercontent.com/engenty/engenty-wizards/v${e}/apps/web/src/about/changelog.json`,{signal:AbortSignal.timeout(5e3)});if(t.ok)return await t.json()}catch{}return(await l(async()=>{let{default:e}=await import(`./changelog-BDhd7tjO.js`);return{default:e}},[])).default}function v({release:e}){return(0,d.jsx)(`ul`,{className:`flex flex-col gap-2`,children:e.commits.map(e=>{let t=f(e);return(0,d.jsxs)(`li`,{className:`flex items-start gap-2.5 text-[0.875rem]`,children:[(0,d.jsx)(`span`,{className:a(`mt-[7px] size-1.5 shrink-0 rounded-full`,t.dot)}),(0,d.jsxs)(`span`,{className:`min-w-0 text-ink-2`,children:[(0,d.jsxs)(`span`,{className:`font-semibold text-[0.75rem] text-ink tracking-wide`,children:[t.label,e.scope?` [${e.scope}]`:``]}),` `,h(e.message),e.breaking?(0,d.jsx)(`span`,{className:`ml-1.5 rounded-full bg-rose-tint px-1.5 py-0.5 font-medium text-[0.75rem] text-rose`,children:r(`about.breaking`)}):null]})]},e.id)})})}function y({open:e,onClose:t,version:n}){let[a,l]=(0,u.useState)(null),[f,h]=(0,u.useState)(``);(0,u.useEffect)(()=>{if(!e||a)return;let t=!1;return _(n).then(e=>{t||l(e)}),()=>{t=!0}},[e,a,n]),(0,u.useEffect)(()=>{e||h(``)},[e]);let y=a?g(a,f):[];return(0,d.jsxs)(o,{open:e,onClose:t,title:r(`about.changelog`),wide:!0,height:`tall`,children:[(0,d.jsx)(`p`,{className:`-mt-3 mb-4 text-[0.875rem] text-ink-3`,children:r(`about.changelogHint`)}),a===null?(0,d.jsx)(`div`,{className:`flex justify-center py-10 text-ink-3`,children:(0,d.jsx)(c,{})}):a.length===0?(0,d.jsx)(s,{children:r(`about.changelogEmpty`)}):(0,d.jsxs)(d.Fragment,{children:[(0,d.jsx)(i,{type:`search`,value:f,placeholder:r(`about.search`),onChange:e=>h(e.target.value)}),y.length===0?(0,d.jsx)(s,{children:r(`about.noMatches`)}):null,y.map(e=>(0,d.jsxs)(`section`,{className:`mt-6`,children:[(0,d.jsxs)(`h3`,{className:`flex items-baseline gap-3 border-border-soft border-b pb-2 font-display font-semibold text-[1rem]`,children:[p(e),(0,d.jsx)(`span`,{className:`font-normal font-sans text-[0.8125rem] text-ink-3 tabular-nums`,children:m(e)})]}),(0,d.jsx)(`div`,{className:`mt-3`,children:(0,d.jsx)(v,{release:e})})]},e.version??`unreleased`))]})]})}export{y as ChangelogDialog,v as ReleaseNotes,_ as loadReleases,m as releaseDate,p as releaseLabel};