@engenty/wizards-runtime 0.7.9 → 0.7.10

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 (46) hide show
  1. package/README.md +2 -1
  2. package/apps/runtime/dist/app.js +2 -2
  3. package/apps/runtime/dist/cli/autostart.js +90 -7
  4. package/apps/runtime/dist/cli/autoupdate.js +18 -5
  5. package/apps/runtime/dist/cli/clients.js +25 -6
  6. package/apps/runtime/dist/cli/connect.js +7 -2
  7. package/apps/runtime/dist/cli/environment.js +33 -2
  8. package/apps/runtime/dist/cli/exec.js +38 -1
  9. package/apps/runtime/dist/cli/home.js +15 -0
  10. package/apps/runtime/dist/cli/machine.js +18 -8
  11. package/apps/runtime/dist/cli/processes.js +23 -3
  12. package/apps/runtime/dist/cli/setup.js +11 -1
  13. package/apps/runtime/dist/cli/start.js +33 -7
  14. package/apps/runtime/dist/cli/uninstall.js +100 -6
  15. package/apps/runtime/dist/cli/update.js +28 -12
  16. package/apps/runtime/dist/cli/versions.js +16 -4
  17. package/apps/runtime/dist/docs/user/install.md +18 -8
  18. package/apps/runtime/dist/harness/env.js +41 -8
  19. package/apps/runtime/dist/harness/index.js +10 -1
  20. package/apps/runtime/dist/harness/model.js +1 -1
  21. package/apps/runtime/dist/render/browser-path.js +43 -11
  22. package/apps/runtime/dist/update.js +4 -0
  23. package/apps/web/dist/assets/{AboutDialog-CShc0mRo.js → AboutDialog-B2LuZ_mC.js} +1 -1
  24. package/apps/web/dist/assets/{ChangelogDialog-C4dVSwtN.js → ChangelogDialog-B0lS0BdL.js} +1 -1
  25. package/apps/web/dist/assets/{ChatView-KkOboyOo.js → ChatView-BcPDcU2d.js} +1 -1
  26. package/apps/web/dist/assets/{EditorPage-lQ3YgsFT.js → EditorPage-erduYKNM.js} +1 -1
  27. package/apps/web/dist/assets/{Harness-D6mgLWTb.js → Harness-Dv9ln5xB.js} +1 -1
  28. package/apps/web/dist/assets/{NewWizardChannels-CPlh_37J.js → NewWizardChannels-JveYyh-U.js} +1 -1
  29. package/apps/web/dist/assets/{NewWizardPage-D0FVJtXK.js → NewWizardPage-Dfk-WOhX.js} +1 -1
  30. package/apps/web/dist/assets/{OnboardingPage-BXPOMrQR.js → OnboardingPage-CT9dL4ke.js} +1 -1
  31. package/apps/web/dist/assets/{ProjectPage-DroTprqs.js → ProjectPage-5_Wsk9Hv.js} +1 -1
  32. package/apps/web/dist/assets/{PublicRunner-j3qoKjQQ.js → PublicRunner-Cw1WzTze.js} +1 -1
  33. package/apps/web/dist/assets/{ReadOnly-CKgSQ2z1.js → ReadOnly-S9vGVQHr.js} +1 -1
  34. package/apps/web/dist/assets/{RunDrawer-CJZ_ISAd.js → RunDrawer-O4rEFCL8.js} +1 -1
  35. package/apps/web/dist/assets/{SettingsPage-BHZ0Z-og.js → SettingsPage-DmhN7OTy.js} +1 -1
  36. package/apps/web/dist/assets/{SetupPage-CDkYkjJX.js → SetupPage-BlWd7Uln.js} +1 -1
  37. package/apps/web/dist/assets/{SharePage-BlVjVjHG.js → SharePage-ktqm-jWI.js} +1 -1
  38. package/apps/web/dist/assets/changelog-FJJS0Tpt.js +1 -0
  39. package/apps/web/dist/assets/{index-C61HAltK.js → index-xH9uN8Nh.js} +3 -3
  40. package/apps/web/dist/assets/{oss-credits-qGhcbqiM.js → oss-credits-Bc05Qv5H.js} +1 -1
  41. package/apps/web/dist/index.html +1 -1
  42. package/apps/web/dist/install.ps1 +307 -0
  43. package/bin/wizards.mjs +59 -35
  44. package/package.json +2 -5
  45. package/plugin/.claude-plugin/plugin.json +1 -1
  46. package/apps/web/dist/assets/changelog-Be0i5Jjw.js +0 -1
@@ -1,13 +1,19 @@
1
- import { spawnSync } from "node:child_process";
1
+ import { execFileSync, spawn, spawnSync } from "node:child_process";
2
2
  import { existsSync, lstatSync, readdirSync, readlinkSync, rmdirSync, rmSync } from "node:fs";
3
3
  import { homedir } from "node:os";
4
4
  import { dirname, join, resolve, sep } from "node:path";
5
5
  import * as p from "@clack/prompts";
6
- import { LOGIN_LABEL, present, removeUnit, UNIT, unloadLoginItem } from "./autostart.js";
6
+ import { LOGIN_LABEL, present, removeUnit, removeWindowsLoginItem, UNIT, unloadLoginItem, windowsLoginItem, } from "./autostart.js";
7
7
  import { apps as allApps } from "./connect.js";
8
8
  import { findWizardsProcesses, stopProcesses } from "./processes.js";
9
9
  import { settings } from "./start.js";
10
10
  import { bad, cyan, dim, no, ok } from "./ui.js";
11
+ /** The user's own PATH from the registry, through PowerShell. */
12
+ function userPath() {
13
+ const out = execFileSync("powershell.exe", ["-NoProfile", "-Command", '[Environment]::GetEnvironmentVariable("Path", "User")'], { encoding: "utf8", windowsHide: true });
14
+ return out.trim().split(";").filter(Boolean);
15
+ }
16
+ const sameDir = (a, b) => a.replace(/[\\/]+$/, "").toLowerCase() === b.replace(/[\\/]+$/, "").toLowerCase();
11
17
  export const realSystem = () => ({
12
18
  userHome: homedir(),
13
19
  platform: process.platform,
@@ -18,6 +24,56 @@ export const realSystem = () => ({
18
24
  stop: (pids) => stopProcesses(pids),
19
25
  unloadLoginItem,
20
26
  removeUnit,
27
+ windowsLoginItem,
28
+ removeWindowsLoginItem,
29
+ onUserPath: (dir) => {
30
+ try {
31
+ return userPath().some((entry) => sameDir(entry, dir));
32
+ }
33
+ catch {
34
+ return false;
35
+ }
36
+ },
37
+ removeFromUserPath: (dir) => {
38
+ const kept = userPath().filter((entry) => !sameDir(entry, dir));
39
+ const value = kept.join(";").replaceAll("'", "''");
40
+ execFileSync("powershell.exe", [
41
+ "-NoProfile",
42
+ "-Command",
43
+ `[Environment]::SetEnvironmentVariable("Path", '${value}', "User")`,
44
+ ], { windowsHide: true });
45
+ },
46
+ deleteAfterExit: (paths, ifEmpty) => {
47
+ // cmd.exe waits a moment for this process to end, then deletes what it held. Should it end
48
+ // with this terminal's session (an ssh session ends what it started), the next login does it.
49
+ const runOnce = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce";
50
+ const removals = [
51
+ ...paths.map((path) => `rmdir /s /q "${path}" 2>nul & del /f /q "${path}" 2>nul`),
52
+ // Without /s: a folder that still holds the data the person kept stays.
53
+ ...ifEmpty.map((dir) => `rmdir "${dir}" 2>nul`),
54
+ ];
55
+ const command = `cmd.exe /d /c ${removals.join(" & ")}`;
56
+ try {
57
+ execFileSync("reg", ["add", runOnce, "/v", "engenty-wizards-uninstall", "/d", command, "/f"], { windowsHide: true, stdio: "ignore" });
58
+ }
59
+ catch {
60
+ // The command below still runs.
61
+ }
62
+ // Tried every second for a minute, as this process may still take a moment to end. Done
63
+ // here, the entry goes too: a reinstall before the next login must not lose its files.
64
+ const gone = paths.map((path) => `if not exist "${path}"`).join(" ");
65
+ const now = [
66
+ `for /l %i in (1,1,60) do @(ping -n 2 127.0.0.1 >nul & ${removals.join(" & ")})`,
67
+ `${gone} reg delete ${runOnce} /v engenty-wizards-uninstall /f >nul 2>&1`,
68
+ ].join(" & ");
69
+ // As Node passes a shell command to cmd.exe: whole, in quotes, read as it is.
70
+ spawn("cmd.exe", ["/d", "/s", "/c", `"${now}"`], {
71
+ detached: true,
72
+ stdio: "ignore",
73
+ windowsHide: true,
74
+ windowsVerbatimArguments: true,
75
+ }).unref();
76
+ },
21
77
  });
22
78
  /** Parts of the install folder that are the person's: kept or deleted with the data. */
23
79
  const WITH_DATA = [".env", ".env.local", "plugins"];
@@ -62,6 +118,9 @@ export async function makePlan(paths, system) {
62
118
  // Can't look: count it as there, so the uninstall tries and says so.
63
119
  loginItem = item;
64
120
  }
121
+ if (system.platform === "win32") {
122
+ loginItem = await system.windowsLoginItem();
123
+ }
65
124
  const { dataDir } = settings(paths, system.env);
66
125
  const program = [];
67
126
  const data = existsSync(dataDir) ? [dataDir] : [];
@@ -80,6 +139,7 @@ export async function makePlan(paths, system) {
80
139
  processes: system.processes(paths.home),
81
140
  program,
82
141
  links: ourLinks(paths.home, system),
142
+ pathEntry: system.platform === "win32" && system.onUserPath(paths.bin) ? paths.bin : null,
83
143
  data,
84
144
  };
85
145
  }
@@ -104,6 +164,7 @@ export async function uninstall(paths, options, system = realSystem()) {
104
164
  ...(plan.processes.length ? [`Stop ${processes(plan.processes.length)}`] : []),
105
165
  ...(plan.program.length ? [`Delete the program in ${t(plan.home)}${size(plan.home)}`] : []),
106
166
  ...plan.links.map((link) => `Delete the command ${t(link)}`),
167
+ ...(plan.pathEntry ? [`Take ${t(plan.pathEntry)} off your PATH`] : []),
107
168
  ];
108
169
  if (!steps.length && !plan.data.length) {
109
170
  console.log(`${ok} engenty wizards isn’t installed here.`);
@@ -153,7 +214,10 @@ export async function uninstall(paths, options, system = realSystem()) {
153
214
  }
154
215
  if (plan.loginItem) {
155
216
  try {
156
- if (system.platform === "darwin") {
217
+ if (system.platform === "win32") {
218
+ await system.removeWindowsLoginItem();
219
+ }
220
+ else if (system.platform === "darwin") {
157
221
  await system.unloadLoginItem();
158
222
  rmSync(plan.loginItem, { force: true });
159
223
  }
@@ -173,14 +237,37 @@ export async function uninstall(paths, options, system = realSystem()) {
173
237
  console.log(`${ok} Stopped ${processes(running.length)}`);
174
238
  }
175
239
  }
176
- for (const path of [...plan.program, ...plan.links, ...(deleteData ? plan.data : [])]) {
240
+ // Looked at again now that nothing runs: what a process wrote meanwhile (the runtime's first
241
+ // update check writes update.json) goes too.
242
+ const now = await makePlan(paths, system);
243
+ // Windows keeps the files of a running program: the Node this command runs on, if it is the
244
+ // install's own, goes once the command has ended.
245
+ const later = [];
246
+ for (const path of [...now.program, ...now.links, ...(deleteData ? now.data : [])]) {
177
247
  try {
178
248
  rmSync(path, { recursive: true, force: true });
179
249
  }
180
250
  catch (error) {
181
- failed(`Couldn’t delete ${t(path)}`, error);
251
+ if (system.platform === "win32") {
252
+ later.push(path);
253
+ }
254
+ else {
255
+ failed(`Couldn’t delete ${t(path)}`, error);
256
+ }
182
257
  }
183
258
  }
259
+ if (plan.pathEntry) {
260
+ try {
261
+ system.removeFromUserPath(plan.pathEntry);
262
+ console.log(`${ok} Took ${t(plan.pathEntry)} off your PATH`);
263
+ }
264
+ catch (error) {
265
+ failed("The PATH", error);
266
+ }
267
+ }
268
+ if (later.length) {
269
+ console.log(`${ok} ${later.map(t).join(", ")} go${later.length === 1 ? "es" : ""} once this command has ended`);
270
+ }
184
271
  for (const dir of [plan.home, join(system.userHome, ".engenty")]) {
185
272
  try {
186
273
  rmdirSync(dir);
@@ -195,8 +282,15 @@ export async function uninstall(paths, options, system = realSystem()) {
195
282
  ...after.apps.map((app) => `${app.name} still lists the wizards: ${cyan(`npx wizards disconnect ${app.id}`)}`),
196
283
  ...(after.loginItem ? [t(after.loginItem)] : []),
197
284
  ...after.processes.map((proc) => `process ${proc.pid}: ${cyan(`kill -9 ${proc.pid}`)}`),
198
- ...[...after.program, ...after.links, ...(deleteData ? after.data : [])].map(t),
285
+ ...(after.pathEntry ? [`${t(after.pathEntry)} on your PATH`] : []),
286
+ ...[...after.program, ...after.links, ...(deleteData ? after.data : [])]
287
+ .filter((path) => !later.includes(path))
288
+ .map(t),
199
289
  ];
290
+ // Started after the second look, which would count it among the install's processes.
291
+ if (later.length) {
292
+ system.deleteAfterExit(later, [plan.home, join(system.userHome, ".engenty")]);
293
+ }
200
294
  if (!deleteData && after.data.length) {
201
295
  console.log(`${ok} Kept your data: ${after.data.map(t).join(", ")}`);
202
296
  }
@@ -3,12 +3,13 @@ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  import { autoUpdatePolicy, noteUpdated } from "./autoupdate.js";
5
5
  import { installedByScript, isCheckout, readInstallNote, writeInstallNote, } from "./home.js";
6
- import { runningRuntime, settings } from "./start.js";
7
- import { restart } from "./status.js";
6
+ import { runningRuntime, settings, startInBackground } from "./start.js";
7
+ import { restart, stop } from "./status.js";
8
8
  import { cmd, cyan, no, ok } from "./ui.js";
9
9
  import { currentVersion } from "./versions.js";
10
+ const windows = process.platform === "win32";
10
11
  /** The installer this install was made with; it also updates one. */
11
- const INSTALLER = "https://engenty.ai/install.sh";
12
+ const INSTALLER = windows ? "https://engenty.ai/install.ps1" : "https://engenty.ai/install.sh";
12
13
  async function installer(into) {
13
14
  const source = process.env.ENGENTY_WIZARDS_INSTALLER?.trim() || INSTALLER;
14
15
  if (!/^https?:\/\//.test(source)) {
@@ -17,13 +18,24 @@ async function installer(into) {
17
18
  const response = await fetch(source);
18
19
  const text = response.ok ? await response.text() : "";
19
20
  // The address answers with the studio's page while a server has no installer yet.
20
- if (!text.startsWith("#!")) {
21
+ if (!text.startsWith(windows ? "# engenty wizards" : "#!")) {
21
22
  throw new Error(`${source} didn’t return the installer.`);
22
23
  }
23
- const file = join(into, "install.sh");
24
+ const file = join(into, windows ? "install.ps1" : "install.sh");
24
25
  writeFileSync(file, text, { mode: 0o700 });
25
26
  return file;
26
27
  }
28
+ /** The installer, without its questions. */
29
+ function runInstaller(script) {
30
+ const [command, args] = windows
31
+ ? ["powershell.exe", ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", script, "-NoSetup"]]
32
+ : ["bash", [script, "--no-setup"]];
33
+ return new Promise((done) => {
34
+ const child = spawn(command, args, { stdio: "inherit" });
35
+ child.on("error", () => done(127));
36
+ child.on("exit", (exit) => done(exit ?? 1));
37
+ });
38
+ }
27
39
  /**
28
40
  * Brings an install made by install.sh to the newest version by running the installer again
29
41
  * without its questions: Node, the runtime and the command. With `restart`, a runtime that runs
@@ -37,14 +49,15 @@ export async function update(paths, options = {}) {
37
49
  return 0;
38
50
  }
39
51
  const before = currentVersion(paths);
52
+ // Windows keeps the files of a running program locked, its native modules among them: the
53
+ // runtime stops for the update and starts again after it, as the new version.
54
+ const stopped = windows && (await runningRuntime(settings(paths).dataDir)) !== null;
55
+ if (stopped && (await stop(paths)) !== 0) {
56
+ return 1;
57
+ }
40
58
  const tmp = mkdtempSync(join(paths.home, "update-"));
41
59
  try {
42
- const script = await installer(tmp);
43
- const code = await new Promise((done) => {
44
- const child = spawn("bash", [script, "--no-setup"], { stdio: "inherit" });
45
- child.on("error", () => done(127));
46
- child.on("exit", (exit) => done(exit ?? 1));
47
- });
60
+ const code = await runInstaller(await installer(tmp));
48
61
  if (code !== 0) {
49
62
  return code;
50
63
  }
@@ -55,12 +68,15 @@ export async function update(paths, options = {}) {
55
68
  }
56
69
  finally {
57
70
  rmSync(tmp, { recursive: true, force: true });
71
+ if (stopped) {
72
+ await startInBackground(paths);
73
+ }
58
74
  }
59
75
  const after = currentVersion(paths);
60
76
  if (before && after) {
61
77
  noteUpdated(paths, before, after);
62
78
  }
63
- if (await runningRuntime(settings(paths).dataDir)) {
79
+ if (!stopped && (await runningRuntime(settings(paths).dataDir))) {
64
80
  if (options.restart) {
65
81
  return restart(paths);
66
82
  }
@@ -1,5 +1,5 @@
1
1
  import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, readlinkSync, renameSync, rmSync, symlinkSync, } from "node:fs";
2
- import { join } from "node:path";
2
+ import { dirname, join, resolve } from "node:path";
3
3
  /**
4
4
  * The runtimes of an install made by install.sh, one folder per version, side by side:
5
5
  *
@@ -8,6 +8,8 @@ import { join } from "node:path";
8
8
  * runtime/current → <version>, the one `bin/wizards` starts
9
9
  * runtime/node_modules → current/node_modules, for commands written before the versions
10
10
  *
11
+ * On Windows the links are junctions, which need no administrator (install.ps1 makes them too).
12
+ *
11
13
  * Up to 0.6.2 `runtime/` itself was the prefix; `switchTo` moves such an install into its
12
14
  * version folder first. A process keeps the files of the version it started with.
13
15
  */
@@ -48,7 +50,8 @@ const isLink = (path) => {
48
50
  /** The version `bin/wizards` starts; null without a runtime. */
49
51
  export function currentVersion(paths) {
50
52
  if (isLink(currentLink(paths))) {
51
- return versionIn(join(paths.runtime, readlinkSync(currentLink(paths))));
53
+ // A junction names its target in full; a link here names the version folder alone.
54
+ return versionIn(resolve(paths.runtime, readlinkSync(currentLink(paths))));
52
55
  }
53
56
  return versionIn(paths.runtime);
54
57
  }
@@ -63,8 +66,16 @@ export function installedVersions(paths) {
63
66
  return [];
64
67
  }
65
68
  }
66
- /** Points `link` at `target` in one step: a new link renamed over the old one. */
69
+ /**
70
+ * Points `link` at `target` in one step: a new link renamed over the old one. On Windows a
71
+ * junction, with its target in full, which takes the old one's place.
72
+ */
67
73
  function link(target, path) {
74
+ if (process.platform === "win32") {
75
+ rmSync(path, { force: true });
76
+ symlinkSync(resolve(dirname(path), target), path, "junction");
77
+ return;
78
+ }
68
79
  const tmp = `${path}.${process.pid}.tmp`;
69
80
  rmSync(tmp, { force: true });
70
81
  symlinkSync(target, tmp);
@@ -115,7 +126,8 @@ export function promote(paths, staged, version) {
115
126
  }
116
127
  // As install.sh leaves it: a command of 0.7.4 or older finds the runtime under its old name.
117
128
  const legacy = join(staged, "node_modules", LEGACY_PACKAGE);
118
- if (!existsSync(legacy)) {
129
+ // Windows has no command of 0.7.4 or older.
130
+ if (!existsSync(legacy) && process.platform !== "win32") {
119
131
  symlinkSync(RUNTIME_PACKAGE, legacy);
120
132
  }
121
133
  rmSync(runtimeDir(paths, version), { recursive: true, force: true });
@@ -1,31 +1,41 @@
1
1
  ---
2
2
  title: Install
3
- description: One command on macOS or Linux. It brings its own Node.js and asks for no password.
3
+ description: One command on macOS, Linux or Windows. It brings its own Node.js and asks for no password.
4
4
  version: 0.5.0
5
5
  ---
6
6
 
7
7
  ## Install
8
8
 
9
- In a terminal, on macOS or Linux (arm64 or x64; on Windows, in WSL):
9
+ In a terminal, on macOS or Linux (arm64 or x64):
10
10
 
11
11
  ```bash
12
12
  curl -fsSL https://engenty.ai/install.sh | bash
13
13
  ```
14
14
 
15
+ On Windows 10 or 11, in PowerShell:
16
+
17
+ ```powershell
18
+ irm https://engenty.ai/install.ps1 | iex
19
+ ```
20
+
15
21
  Or, with Node.js 20 or newer on the machine:
16
22
 
17
23
  ```bash
18
24
  npx wizards
19
25
  ```
20
26
 
21
- Both do the same. `npx wizards` downloads only a small launcher (no dependencies) and runs the
22
- installer it brings along, then the setup, then the
27
+ All three do the same. On Windows, if PowerShell refuses `npx` (“running scripts is disabled
28
+ on this system”), type `npx.cmd wizards`. `npx wizards` downloads only a small launcher (no
29
+ dependencies) and runs the installer it brings along, then the setup, then the
23
30
  `wizards` menu, where “Start as service” starts it and opens the studio. Run again, it hands over
24
31
  to the installed `wizards`, and brings an older install up to its own version first.
25
32
 
26
- The installer needs nothing but `curl` and `tar`. It brings its own Node.js (a Node you already
27
- have is left alone), installs engenty wizards into `~/.engenty/wizards` (about 650 MB), links the
28
- `wizards` command into `~/.local/bin` and then walks you through the rest:
33
+ The installer needs nothing but `curl` and `tar`. On Windows it needs nothing, except Microsoft’s
34
+ Visual C++ runtime, which many programs bring along: where it is missing, the installer installs
35
+ it, and Windows asks once whether it may. It brings its own Node.js
36
+ (a Node you already have is left alone), installs engenty wizards into `~/.engenty/wizards`
37
+ (about 650 MB), links the `wizards` command into `~/.local/bin` (on Windows it puts
38
+ `~\.engenty\wizards\bin` on your PATH) and then walks you through the rest:
29
39
 
30
40
  | Step | What it does |
31
41
  |---|---|
@@ -33,7 +43,7 @@ have is left alone), installs engenty wizards into `~/.engenty/wizards` (about 6
33
43
  | An AI client | Looks for Codex, Claude Code, Antigravity and Cursor Agent and offers to install the ones that are missing. It puts first the one whose app (ChatGPT, Claude, Antigravity, Cursor) is on your Mac, as you most likely have that subscription already. You can also use your account’s credits, 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 |
34
44
  | 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). engenty wizards uses that one; [`CHROME_PATH`](./command-line.md#settings) picks another. It also says whether ffmpeg is there for MP4 videos of animated widgets, and with Homebrew it offers to install it |
35
45
  | A decision model (optional) | Offers to install [Ollama](https://ollama.com) if it isn’t there and to download Cloudflare’s Clef-flash into it (about 11 GB). The wizards then make their decisions, such as which branch to take, on your computer, without a key or credits. On a Mac the Ollama app goes into Applications and, when it first starts, asks once for your Mac’s password to set up its `ollama` command (cancel works too); on Linux Ollama’s own installer runs, installs `zstd` if it is missing, and asks for your password |
36
- | Start at login (optional) | engenty wizards starts in the background when you log in, so links and AI apps can always reach it. It is a LaunchAgent on a Mac and a systemd user service on Linux |
46
+ | Start at login (optional) | engenty wizards starts in the background when you log in, so links and AI apps can always reach it. It is a LaunchAgent on a Mac, a systemd user service on Linux and a scheduled task on Windows (a shortcut in the Startup folder where Windows allows no task) |
37
47
  | Your AI apps (optional) | Offers to add your wizards to the [AI apps](./ai-apps.md) it finds, such as Claude Desktop, Cursor or Codex |
38
48
  | The command | If `~/.local/bin` is not on your PATH, it offers to add it in your shell’s settings file |
39
49
 
@@ -1,12 +1,17 @@
1
- import { execFile, spawn } from "node:child_process";
1
+ import { spawn } from "node:child_process";
2
2
  import { join } from "node:path";
3
- import { promisify } from "node:util";
3
+ import { runFile } from "../cli/exec.js";
4
4
  import { layout } from "../cli/home.js";
5
- export const run = promisify(execFile);
5
+ export const run = runFile;
6
6
  let shellEnv = null;
7
7
  /** The person's login shell, asked once: a desktop app and a bare dev server start without its variables. */
8
8
  export function loginShellEnv() {
9
9
  shellEnv ??= new Promise((done) => {
10
+ // Windows has no login shell to ask: the runtime's own environment is the person's.
11
+ if (process.platform === "win32") {
12
+ done({});
13
+ return;
14
+ }
10
15
  const shell = process.env.SHELL || "/bin/zsh";
11
16
  // In a session of its own, away from the terminal `wizards` runs in: an interactive
12
17
  // shell takes over that terminal for its job control and keeps it when it ends, and Ctrl-C
@@ -48,7 +53,7 @@ export function loginShellEnv() {
48
53
  */
49
54
  export function pickEnv(shell, own, passThrough,
50
55
  /** Folders before the shell's PATH: the clients the setup installed. */
51
- first = []) {
56
+ first = [], platform = process.platform) {
52
57
  const picked = {};
53
58
  const source = Object.keys(shell).length ? shell : own;
54
59
  for (const [key, value] of Object.entries(source)) {
@@ -56,16 +61,31 @@ first = []) {
56
61
  picked[key] = value;
57
62
  }
58
63
  }
59
- const path = [...first, ...(shell.PATH ?? "").split(":"), ...(own.PATH ?? "").split(":")].filter(Boolean);
64
+ const windows = platform === "win32";
65
+ if (windows) {
66
+ for (const [key, value] of Object.entries(own)) {
67
+ if (WINDOWS_BASE.test(key) && value) {
68
+ picked[key] = value;
69
+ }
70
+ }
71
+ }
72
+ const sep = windows ? ";" : ":";
73
+ const ownPath = own.PATH ?? own.Path ?? "";
74
+ const path = [...first, ...(shell.PATH ?? "").split(sep), ...ownPath.split(sep)].filter(Boolean);
60
75
  return {
61
76
  ...picked,
62
- PATH: [...new Set(path)].join(":"),
77
+ PATH: [...new Set(path)].join(sep),
63
78
  HOME: own.HOME,
64
79
  USER: own.USER,
65
80
  LANG: "en_US.UTF-8",
66
81
  TERM: "dumb",
67
82
  };
68
83
  }
84
+ /**
85
+ * What a Windows program needs of the environment to start at all: the system's folders, the
86
+ * user's folders, and how commands are found and run (a .cmd needs ComSpec).
87
+ */
88
+ const WINDOWS_BASE = /^(SystemRoot|SystemDrive|windir|ComSpec|PATHEXT|TEMP|TMP|USERPROFILE|USERNAME|USERDOMAIN|HOMEDRIVE|HOMEPATH|APPDATA|LOCALAPPDATA|ProgramData|ProgramFiles|ProgramFiles\(x86\)|ProgramW6432|CommonProgramFiles|CommonProgramFiles\(x86\)|CommonProgramW6432|COMPUTERNAME|OS|PROCESSOR_ARCHITECTURE|NUMBER_OF_PROCESSORS)$/i;
69
89
  export function stripKeys(env, keyVars) {
70
90
  const bare = { ...env };
71
91
  for (const key of keyVars) {
@@ -73,8 +93,21 @@ export function stripKeys(env, keyVars) {
73
93
  }
74
94
  return bare;
75
95
  }
76
- /** Where the setup installs clients from npm, whoever started this runtime. */
77
- const installed = () => [join(layout().clients, "bin")];
96
+ /**
97
+ * Where the setup installs clients, whoever started this runtime: npm's prefix (on Windows the
98
+ * prefix itself), and on Windows where Claude Code and Cursor Agent put themselves.
99
+ */
100
+ const installed = () => {
101
+ if (process.platform !== "win32") {
102
+ return [join(layout().clients, "bin")];
103
+ }
104
+ const { USERPROFILE: home, LOCALAPPDATA: local } = process.env;
105
+ return [
106
+ layout().clients,
107
+ ...(home ? [join(home, ".local", "bin")] : []),
108
+ ...(local ? [join(local, "cursor-agent"), join(local, "agy", "bin")] : []),
109
+ ];
110
+ };
78
111
  const resolved = new Map();
79
112
  /**
80
113
  * The environment a client runs in, decided once: the subscription sign-in comes first — it is
@@ -1,5 +1,5 @@
1
1
  import { mkdirSync } from "node:fs";
2
- import { installCommand } from "../cli/clients.js";
2
+ import { installCommand, POWERSHELL } from "../cli/clients.js";
3
3
  import { inherited, runtimePath } from "../cli/environment.js";
4
4
  import { layout } from "../cli/home.js";
5
5
  import { CLIENTS, vendorApp } from "../cli/machine.js";
@@ -77,6 +77,15 @@ export async function installSpec(id) {
77
77
  const paths = layout();
78
78
  mkdirSync(paths.clients, { recursive: true });
79
79
  const shell = await loginShellEnv();
80
+ if (process.platform === "win32") {
81
+ const [bin, ...args] = POWERSHELL;
82
+ const command = installCommand(client, paths);
83
+ return {
84
+ bin,
85
+ args: [...args, `Write-Host "$ ${command.replaceAll('"', '`"')}\`n"; ${command}`],
86
+ env: { ...inherited(process.env), PATH: runtimePath(paths, process.env) },
87
+ };
88
+ }
80
89
  return {
81
90
  bin: "bash",
82
91
  args: [
@@ -1,7 +1,7 @@
1
- import { spawn } from "node:child_process";
2
1
  import { mkdirSync, rmSync, writeFileSync } from "node:fs";
3
2
  import { join } from "node:path";
4
3
  import { note } from "@engenty-wizards/shared/run";
4
+ import spawn from "cross-spawn";
5
5
  import { nanoid } from "nanoid";
6
6
  import { env } from "../env.js";
7
7
  import { openBridge } from "../mcp/bridge.js";
@@ -1,19 +1,33 @@
1
1
  import { accessSync, constants, existsSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
- import { delimiter, join } from "node:path";
4
- /** The first executable called `name` in the folders of `path` (a PATH value). */
5
- export function findOnPath(name, path) {
6
- for (const dir of path.split(delimiter)) {
3
+ import { delimiter, join, win32 } from "node:path";
4
+ /**
5
+ * The first executable called `name` in the folders of `path` (a PATH value). On Windows a
6
+ * command is a file with one of the endings in PATHEXT: `codex` is `codex.cmd`, `claude` is
7
+ * `claude.exe`.
8
+ */
9
+ export function findOnPath(name, path, platform = process.platform, pathext = process.env.PATHEXT) {
10
+ const windows = platform === "win32";
11
+ const endings = windows
12
+ ? ["", ...(pathext || ".COM;.EXE;.BAT;.CMD").toLowerCase().split(";").filter(Boolean)]
13
+ : [""];
14
+ for (const dir of path.split(windows ? ";" : delimiter)) {
7
15
  if (!dir) {
8
16
  continue;
9
17
  }
10
- const file = join(dir, name);
11
- try {
12
- accessSync(file, constants.X_OK);
13
- return file;
14
- }
15
- catch {
16
- // not here
18
+ for (const ending of endings) {
19
+ // A file without an ending is no command on Windows, unless the name brought one.
20
+ if (windows && !ending && !/\.\w+$/.test(name)) {
21
+ continue;
22
+ }
23
+ const file = join(dir, name + ending);
24
+ try {
25
+ accessSync(file, windows ? constants.F_OK : constants.X_OK);
26
+ return file;
27
+ }
28
+ catch {
29
+ // not here
30
+ }
17
31
  }
18
32
  }
19
33
  return null;
@@ -44,6 +58,24 @@ export function findChrome(env = process.env) {
44
58
  }
45
59
  return null;
46
60
  }
61
+ if (process.platform === "win32") {
62
+ const apps = [
63
+ "Google\\Chrome\\Application\\chrome.exe",
64
+ "Microsoft\\Edge\\Application\\msedge.exe",
65
+ "Chromium\\Application\\chrome.exe",
66
+ "BraveSoftware\\Brave-Browser\\Application\\brave.exe",
67
+ ];
68
+ const roots = [env.PROGRAMFILES, env["PROGRAMFILES(X86)"], env.LOCALAPPDATA].filter(Boolean);
69
+ for (const app of apps) {
70
+ for (const root of roots) {
71
+ const file = win32.join(root, app);
72
+ if (existsSync(file)) {
73
+ return file;
74
+ }
75
+ }
76
+ }
77
+ return null;
78
+ }
47
79
  for (const name of ["google-chrome", "chromium", "chromium-browser", "microsoft-edge"]) {
48
80
  const file = findOnPath(name, env.PATH ?? "");
49
81
  if (file) {
@@ -98,6 +98,10 @@ export function applyUpdate() {
98
98
  const child = spawn(process.execPath, [join(packageRoot, "bin/wizards.mjs"), "update"], {
99
99
  cwd: paths.home,
100
100
  stdio: ["ignore", log, log],
101
+ // On Windows the update stops this runtime for its locked files, and a child that is not
102
+ // detached ends with its parent there; the update starts the new version itself.
103
+ detached: process.platform === "win32",
104
+ windowsHide: true,
101
105
  });
102
106
  child.on("error", () => {
103
107
  applying = "failed";
@@ -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{Bt as r,Cr as i,Ht as a,Qt as o,Vt as s,Xt as c,_r as l,an as u,qn as d,qt as f,zt as p}from"./ui-DMmT-cSO.js";import{t as m}from"./preload-helper-CNEI7N0N.js";import{Ot as h,Q as g,X as _,Z as v}from"./index-C61HAltK.js";import{c as y,f as b}from"./settings-menu-Dpkp3f56.js";import{ReleaseNotes as x,loadReleases as S,releaseDate as C,releaseLabel as w}from"./ChangelogDialog-C4dVSwtN.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)(l,{"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:u(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,o]=(0,T.useState)(``);(0,T.useEffect)(()=>{if(!e||t)return;let r=!1;return m(()=>import(`./oss-credits-qGhcbqiM.js`).then(e=>{r||n(e.default)}),[]),()=>{r=!0}},[e,t]),(0,T.useEffect)(()=>{e||o(``)},[e]);let s=i.trim().toLowerCase(),c=e=>s?e.filter(e=>k(s,e.name,e.license,u(e.description))):e,l=e=>s?e.filter(e=>k(s,e.name,e.license,e.version)):e,d=c(E),p=c(D),h=t?[{id:`shared`,label:u(`about.creditsShared`),packages:l(t.shared)},...t.groups.map(e=>({id:e.id,label:e.label,packages:l(e.packages)}))].filter(e=>e.packages.length>0):[],g=d.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:u(`about.creditsHint`)}),(0,O.jsx)(a,{type:`search`,value:i,placeholder:u(`about.search`),onChange:e=>o(e.target.value)}),d.length>0?(0,O.jsx)(j,{title:u(`about.creditsProduct`),children:(0,O.jsx)(M,{credits:d})}):null,p.length>0?(0,O.jsx)(j,{title:u(`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)(f,{})}):h.length>0?(0,O.jsx)(j,{title:u(`about.creditsDeps`),hint:s?void 0:u(`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)(r,{children:u(`about.noMatches`)}):null]})}var F=[`new`,`earlier`,`credits`],I=e=>u(e===`new`?`about.tab.new`:e===`earlier`?`about.tab.earlier`:`about.credits`),L=e=>e===1?u(`about.change`):u(`about.changes`,{n:e});function R({open:e,onClose:t,me:n}){let r=n?.mode===`local`,{data:a,latest:l}=g(r&&e),[m,E]=(0,T.useState)(null),[D,k]=(0,T.useState)(`new`),A=b(F.indexOf(D)),j=a?.newer&&l?l:v;(0,T.useEffect)(()=>{if(!e)return;let t=!1;return E(null),S(j===`0.7.9`?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=r?a?.kind??null:`cloud`;return(0,O.jsxs)(p,{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)(s,{label:u(`common.close`),onClick:t,children:(0,O.jsx)(d,{className:`size-4`})})}),(0,O.jsxs)(`div`,{className:`flex flex-col items-center pt-4 text-center`,children:[(0,O.jsx)(h,{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:[u(`about.version`,{v}),R?` · ${u(`about.kind.${R}`)}`:``]})]}),r?(0,O.jsx)(`div`,{className:`mt-5`,children:(0,O.jsx)(_,{enabled:e})}):null,(0,O.jsx)(`div`,{className:`mt-6 flex justify-center`,children:(0,O.jsx)(c,{value:D,onChange:k,options:F.map(e=>({id:e,label:I(e)})),label:u(`about.title`)})}),(0,O.jsxs)(`div`,{className:o(`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)(f,{})}):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:u(`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)(i,{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
+ import{r as e}from"./rolldown-runtime-hePW80VL.js";import{n as t,t as n}from"./jsx-runtime-BNakU3Ej.js";import{Bt as r,Cr as i,Ht as a,Qt as o,Vt as s,Xt as c,_r as l,an as u,qn as d,qt as f,zt as p}from"./ui-DMmT-cSO.js";import{t as m}from"./preload-helper-CNEI7N0N.js";import{Ot as h,Q as g,X as _,Z as v}from"./index-xH9uN8Nh.js";import{c as y,f as b}from"./settings-menu-Dpkp3f56.js";import{ReleaseNotes as x,loadReleases as S,releaseDate as C,releaseLabel as w}from"./ChangelogDialog-B0lS0BdL.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)(l,{"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:u(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,o]=(0,T.useState)(``);(0,T.useEffect)(()=>{if(!e||t)return;let r=!1;return m(()=>import(`./oss-credits-Bc05Qv5H.js`).then(e=>{r||n(e.default)}),[]),()=>{r=!0}},[e,t]),(0,T.useEffect)(()=>{e||o(``)},[e]);let s=i.trim().toLowerCase(),c=e=>s?e.filter(e=>k(s,e.name,e.license,u(e.description))):e,l=e=>s?e.filter(e=>k(s,e.name,e.license,e.version)):e,d=c(E),p=c(D),h=t?[{id:`shared`,label:u(`about.creditsShared`),packages:l(t.shared)},...t.groups.map(e=>({id:e.id,label:e.label,packages:l(e.packages)}))].filter(e=>e.packages.length>0):[],g=d.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:u(`about.creditsHint`)}),(0,O.jsx)(a,{type:`search`,value:i,placeholder:u(`about.search`),onChange:e=>o(e.target.value)}),d.length>0?(0,O.jsx)(j,{title:u(`about.creditsProduct`),children:(0,O.jsx)(M,{credits:d})}):null,p.length>0?(0,O.jsx)(j,{title:u(`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)(f,{})}):h.length>0?(0,O.jsx)(j,{title:u(`about.creditsDeps`),hint:s?void 0:u(`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)(r,{children:u(`about.noMatches`)}):null]})}var F=[`new`,`earlier`,`credits`],I=e=>u(e===`new`?`about.tab.new`:e===`earlier`?`about.tab.earlier`:`about.credits`),L=e=>e===1?u(`about.change`):u(`about.changes`,{n:e});function R({open:e,onClose:t,me:n}){let r=n?.mode===`local`,{data:a,latest:l}=g(r&&e),[m,E]=(0,T.useState)(null),[D,k]=(0,T.useState)(`new`),A=b(F.indexOf(D)),j=a?.newer&&l?l:v;(0,T.useEffect)(()=>{if(!e)return;let t=!1;return E(null),S(j===`0.7.10`?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=r?a?.kind??null:`cloud`;return(0,O.jsxs)(p,{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)(s,{label:u(`common.close`),onClick:t,children:(0,O.jsx)(d,{className:`size-4`})})}),(0,O.jsxs)(`div`,{className:`flex flex-col items-center pt-4 text-center`,children:[(0,O.jsx)(h,{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:[u(`about.version`,{v}),R?` · ${u(`about.kind.${R}`)}`:``]})]}),r?(0,O.jsx)(`div`,{className:`mt-5`,children:(0,O.jsx)(_,{enabled:e})}):null,(0,O.jsx)(`div`,{className:`mt-6 flex justify-center`,children:(0,O.jsx)(c,{value:D,onChange:k,options:F.map(e=>({id:e,label:I(e)})),label:u(`about.title`)})}),(0,O.jsxs)(`div`,{className:o(`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)(f,{})}):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:u(`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)(i,{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{Bt as r,Ht as i,Qt as a,an as o,qt as s,zt as c}from"./ui-DMmT-cSO.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/,``):o(`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/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-Be0i5Jjw.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:o(`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)(c,{open:e,onClose:t,title:o(`about.changelog`),wide:!0,height:`tall`,children:[(0,d.jsx)(`p`,{className:`-mt-3 mb-4 text-[0.875rem] text-ink-3`,children:o(`about.changelogHint`)}),a===null?(0,d.jsx)(`div`,{className:`flex justify-center py-10 text-ink-3`,children:(0,d.jsx)(s,{})}):a.length===0?(0,d.jsx)(r,{children:o(`about.changelogEmpty`)}):(0,d.jsxs)(d.Fragment,{children:[(0,d.jsx)(i,{type:`search`,value:f,placeholder:o(`about.search`),onChange:e=>h(e.target.value)}),y.length===0?(0,d.jsx)(r,{children:o(`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{Bt as r,Ht as i,Qt as a,an as o,qt as s,zt as c}from"./ui-DMmT-cSO.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/,``):o(`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/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-FJJS0Tpt.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:o(`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)(c,{open:e,onClose:t,title:o(`about.changelog`),wide:!0,height:`tall`,children:[(0,d.jsx)(`p`,{className:`-mt-3 mb-4 text-[0.875rem] text-ink-3`,children:o(`about.changelogHint`)}),a===null?(0,d.jsx)(`div`,{className:`flex justify-center py-10 text-ink-3`,children:(0,d.jsx)(s,{})}):a.length===0?(0,d.jsx)(r,{children:o(`about.changelogEmpty`)}):(0,d.jsxs)(d.Fragment,{children:[(0,d.jsx)(i,{type:`search`,value:f,placeholder:o(`about.search`),onChange:e=>h(e.target.value)}),y.length===0?(0,d.jsx)(r,{children:o(`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};