@engenty/wizards-runtime 0.7.8 → 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.
- package/README.md +2 -1
- package/apps/runtime/dist/app.js +2 -2
- package/apps/runtime/dist/cli/autostart.js +90 -7
- package/apps/runtime/dist/cli/autoupdate.js +18 -5
- package/apps/runtime/dist/cli/clients.js +25 -6
- package/apps/runtime/dist/cli/connect.js +7 -2
- package/apps/runtime/dist/cli/environment.js +33 -2
- package/apps/runtime/dist/cli/exec.js +38 -1
- package/apps/runtime/dist/cli/home.js +15 -0
- package/apps/runtime/dist/cli/machine.js +18 -8
- package/apps/runtime/dist/cli/processes.js +23 -3
- package/apps/runtime/dist/cli/setup.js +11 -1
- package/apps/runtime/dist/cli/start.js +33 -7
- package/apps/runtime/dist/cli/uninstall.js +100 -6
- package/apps/runtime/dist/cli/update.js +28 -12
- package/apps/runtime/dist/cli/versions.js +16 -4
- package/apps/runtime/dist/clients.js +118 -0
- package/apps/runtime/dist/db/client.js +37 -3
- package/apps/runtime/dist/db/migrations/tenant/0017_run_client.sql +1 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0017_snapshot.json +3333 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/_journal.json +7 -0
- package/apps/runtime/dist/db/schema.js +2 -0
- package/apps/runtime/dist/docs/dev/api.md +39 -0
- package/apps/runtime/dist/docs/dev/index.md +1 -0
- package/apps/runtime/dist/docs/dev/integration/openui.md +5 -3
- package/apps/runtime/dist/docs/dev/meta.json +1 -1
- package/apps/runtime/dist/docs/user/ai-apps.md +14 -3
- package/apps/runtime/dist/docs/user/install.md +18 -8
- package/apps/runtime/dist/engine/runner.js +1 -0
- package/apps/runtime/dist/harness/env.js +41 -8
- package/apps/runtime/dist/harness/index.js +10 -1
- package/apps/runtime/dist/harness/model.js +1 -1
- package/apps/runtime/dist/index.js +13 -4
- package/apps/runtime/dist/plugin.js +24 -8
- package/apps/runtime/dist/render/browser-path.js +43 -11
- package/apps/runtime/dist/routes/runs.js +9 -0
- package/apps/runtime/dist/update.js +4 -0
- package/apps/web/dist/assets/AboutDialog-B2LuZ_mC.js +1 -0
- package/apps/web/dist/assets/ChangelogDialog-B0lS0BdL.js +1 -0
- package/apps/web/dist/assets/ChatView-BcPDcU2d.js +10 -0
- package/apps/web/dist/assets/EditorPage-erduYKNM.js +4 -0
- package/apps/web/dist/assets/{FlowPreview-Dx8AjXvE.js → FlowPreview-oX_Emm7u.js} +1 -1
- package/apps/web/dist/assets/{Harness-NvIb36TG.js → Harness-Dv9ln5xB.js} +2 -2
- package/apps/web/dist/assets/{NewWizardChannels-B2YgtJlt.js → NewWizardChannels-JveYyh-U.js} +1 -1
- package/apps/web/dist/assets/NewWizardPage-Dfk-WOhX.js +7 -0
- package/apps/web/dist/assets/OnboardingPage-CT9dL4ke.js +1 -0
- package/apps/web/dist/assets/ProjectPage-5_Wsk9Hv.js +1 -0
- package/apps/web/dist/assets/PublicRunner-Cw1WzTze.js +1 -0
- package/apps/web/dist/assets/{ReadOnly-CGNHlgUT.js → ReadOnly-S9vGVQHr.js} +1 -1
- package/apps/web/dist/assets/RunDrawer-O4rEFCL8.js +4 -0
- package/apps/web/dist/assets/RunnerView-BW1h4x63.js +1 -0
- package/apps/web/dist/assets/SettingsPage-DmhN7OTy.js +1 -0
- package/apps/web/dist/assets/SetupPage-BlWd7Uln.js +1 -0
- package/apps/web/dist/assets/{SharePage-hG7Z6SLy.js → SharePage-ktqm-jWI.js} +1 -1
- package/apps/web/dist/assets/changelog-FJJS0Tpt.js +1 -0
- package/apps/web/dist/assets/engenty-WpMi3xwq.js +1 -0
- package/apps/web/dist/assets/{esm-DVk3RJij.js → esm-C-ZWeuhU.js} +1 -1
- package/apps/web/dist/assets/{fluffy-engenty-ClRou7yu.js → fluffy-engenty-DPNHytrz.js} +135 -16
- package/apps/web/dist/assets/goose-helmet-Cvc7KjUg.bin +0 -0
- package/apps/web/dist/assets/goose-helmet-livery-CZD9u0XS.webp +0 -0
- package/apps/web/dist/assets/index-C4aHMcg9.css +1 -0
- package/apps/web/dist/assets/index-xH9uN8Nh.js +14 -0
- package/apps/web/dist/assets/oss-credits-Bc05Qv5H.js +1 -0
- package/apps/web/dist/assets/{phone-CXXHXYnA.js → phone-BjmhIKuT.js} +1 -1
- package/apps/web/dist/assets/{settings-menu-DXzZCIMx.js → settings-menu-Dpkp3f56.js} +1 -1
- package/apps/web/dist/assets/{ui-DQ4L2npj.js → ui-DMmT-cSO.js} +4 -4
- package/apps/web/dist/assets/{upload-BnYHIfu5.js → upload-JX9BcMTq.js} +1 -1
- package/apps/web/dist/assets/{wrench-BrUJ7SV6.js → wrench-BSxnPs2_.js} +1 -1
- package/apps/web/dist/index.html +4 -4
- package/apps/web/dist/install.ps1 +307 -0
- package/apps/web/dist/mcp/flow.html +141 -22
- package/bin/wizards.mjs +59 -35
- package/node_modules/@engenty/wizards/package.json +1 -4
- package/node_modules/@engenty-wizards/shared/dist/claude-plugin.d.ts +7 -0
- package/node_modules/@engenty-wizards/shared/dist/claude-plugin.js +10 -0
- package/node_modules/@engenty-wizards/shared/dist/run.d.ts +2 -0
- package/node_modules/@engenty-wizards/shared/dist/run.js +4 -0
- package/package.json +2 -5
- package/plugin/.claude-plugin/plugin.json +28 -6
- package/plugin/README.md +58 -0
- package/plugin/hooks/engenty.ts +140 -0
- package/plugin/hooks/hooks.json +1 -0
- package/plugin/hooks/pane.test.tsx +177 -0
- package/plugin/hooks/register.tsx +396 -0
- package/plugin/types/index.d.ts +44 -0
- package/apps/web/dist/assets/AboutDialog-ByZyHXDj.js +0 -1
- package/apps/web/dist/assets/ChangelogDialog-QHYP0jAv.js +0 -1
- package/apps/web/dist/assets/ChatView-DupTtI8K.js +0 -10
- package/apps/web/dist/assets/EditorPage-k6qouLeS.js +0 -4
- package/apps/web/dist/assets/NewWizardPage-BgLY6SL6.js +0 -7
- package/apps/web/dist/assets/OnboardingPage-CxhwefJU.js +0 -1
- package/apps/web/dist/assets/ProjectPage-6ceSSS7W.js +0 -1
- package/apps/web/dist/assets/PublicRunner-6lQvReDx.js +0 -1
- package/apps/web/dist/assets/RunDrawer-TVGNnWyx.js +0 -4
- package/apps/web/dist/assets/RunnerView-DInSK0VZ.js +0 -1
- package/apps/web/dist/assets/SettingsPage-D3k5pXN5.js +0 -1
- package/apps/web/dist/assets/SetupPage-Bj5frdzY.js +0 -1
- package/apps/web/dist/assets/changelog-CO9vKjV0.js +0 -1
- package/apps/web/dist/assets/engenty-eXZ-EAwd.js +0 -1
- package/apps/web/dist/assets/index-DfaoPjNW.css +0 -1
- package/apps/web/dist/assets/index-DlDsr9Lc.js +0 -14
- package/apps/web/dist/assets/oss-credits-X2JPiMOR.js +0 -1
- package/node_modules/@engenty/wizards/dist/openui/Wizard.d.ts +0 -20
- package/node_modules/@engenty/wizards/dist/openui/Wizard.js +0 -62
- package/node_modules/@engenty/wizards/dist/openui/index.d.ts +0 -9
- package/node_modules/@engenty/wizards/dist/openui/index.js +0 -9
- package/node_modules/@engenty/wizards/dist/openui/message.d.ts +0 -11
- package/node_modules/@engenty/wizards/dist/openui/message.js +0 -69
- package/node_modules/@engenty/wizards/dist/openui/run.d.ts +0 -8
- package/node_modules/@engenty/wizards/dist/openui/run.js +0 -137
- package/node_modules/@engenty/wizards/dist/openui/server.d.ts +0 -37
- package/node_modules/@engenty/wizards/dist/openui/server.js +0 -77
- package/plugin/.mcp.json +0 -8
|
@@ -127,6 +127,8 @@ export const run = sqliteTable("run", {
|
|
|
127
127
|
* (`data-page` of the embed tag, else the page's own address); null on the wizard's link.
|
|
128
128
|
*/
|
|
129
129
|
origin: text("origin"),
|
|
130
|
+
/** The client that started it over the public API (`x-wizards-client`): web, app, cli, or a registered id. */
|
|
131
|
+
client: text("client"),
|
|
130
132
|
status: text("status", {
|
|
131
133
|
enum: ["waiting_input", "running", "done", "failed", "cancelled"],
|
|
132
134
|
}).notNull(),
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: The API
|
|
3
|
+
description: What a program may rely on when it talks to a runtime, and what is private.
|
|
4
|
+
version: 0.7.7
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
A runtime answers HTTP under `/api`. Three parts are a contract: they are versioned, documented
|
|
8
|
+
and change with notice. Everything else is the studio’s own and changes without notice.
|
|
9
|
+
|
|
10
|
+
## What a program may rely on
|
|
11
|
+
|
|
12
|
+
| Part | For | Signed in as |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| `/api/v1/…` | programs that build and publish wizards: the spaces of a local install, importing a wizard | an access token of the account, with scopes (`wizards:read`, `wizards:write`, `wizards:publish`, `runs:test`); see [the Manage-App contract](https://github.com/engenty/engenty-wizards/blob/main/docs/manage-contract.md) |
|
|
15
|
+
| `/api/mcp` | AI apps: every tool of [the MCP server](../user/ai-apps.md) | the same tokens, or the local ticket `wizards mcp` carries |
|
|
16
|
+
| The public run API: `/api/public/wizards/:token`, `/api/public/wizards/:token/runs`, `/api/public/codes/:code`, `/api/runs/:id/…` | running a published wizard by its link: what the runner in a browser does | nobody; a visitor cookie, or the `x-wizards-visitor` header where a cookie does not survive |
|
|
17
|
+
|
|
18
|
+
## Private
|
|
19
|
+
|
|
20
|
+
`/api/studio/…`, `/api/auth/…` and `/api/internal/…` belong to the studio, the sign-in and the
|
|
21
|
+
Manage-App. They change with every release and are not meant for other programs. A program that
|
|
22
|
+
needs something only they have asks for it on `/api/v1`.
|
|
23
|
+
|
|
24
|
+
## Who you are: `x-wizards-client`
|
|
25
|
+
|
|
26
|
+
Every start of a run over the public API names the client that starts it, in the
|
|
27
|
+
`x-wizards-client` header: `web` is the runner in a browser, `app` the mobile app, `cli` the
|
|
28
|
+
command line. A client of your own sends its own id: lower-case letters, digits and hyphens.
|
|
29
|
+
The run keeps the id.
|
|
30
|
+
|
|
31
|
+
The id is no secret and proves nothing. It carries the terms a client runs under:
|
|
32
|
+
|
|
33
|
+
| Runtime | Does with the id |
|
|
34
|
+
|---|---|
|
|
35
|
+
| One that runs alone (your computer, your server) | Keeps it on the run. Nothing is refused: the API is yours |
|
|
36
|
+
| engenty.ai | Asks its list of clients: a registered id runs under its limits, an unknown one under the public limits, a revoked one not at all. The [terms of the hosted API](https://engenty.ai/api-terms) say how a client registers and what it may do |
|
|
37
|
+
|
|
38
|
+
An answer of `403` with `code: "client_refused"` means the id may not start runs there;
|
|
39
|
+
`429` with `code: "client_limit"` means it started too many this hour.
|
|
@@ -15,6 +15,7 @@ code, and the runtime loads it from its files while it runs.
|
|
|
15
15
|
| [Quick start](./plugins/quick-start.md) | A tool for AI steps in one file that works in two minutes |
|
|
16
16
|
| [Reference](./plugins/reference.md) | Every call, type, address and setting |
|
|
17
17
|
| [Examples](./plugins/examples.md) | Two plugins to read and copy |
|
|
18
|
+
| [The API](./api.md) | What a program may rely on, what is private, and how a client names itself |
|
|
18
19
|
|
|
19
20
|
## Wizards as files
|
|
20
21
|
|
|
@@ -11,9 +11,11 @@ author defined. `@engenty/wizards/openui` puts the two together: the chat decide
|
|
|
11
11
|
fits and what it already knows, the wizard then runs inside the conversation, and the chat
|
|
12
12
|
hears the result.
|
|
13
13
|
|
|
14
|
-
It is part of `@engenty/wizards`, the package for code outside engenty wizards
|
|
15
|
-
(
|
|
16
|
-
|
|
14
|
+
It is part of `@engenty/wizards`, the package on npm for code outside engenty wizards
|
|
15
|
+
(`npm install @engenty/wizards`;
|
|
16
|
+
[`packages/wizards/src/openui`](https://github.com/engenty/wizards/tree/main/packages/wizards/src/openui)
|
|
17
|
+
in the repository, under FSL-1.1-MIT; the runner it bundles is MIT). A whole chat to try it with
|
|
18
|
+
is `examples/openui-chat`.
|
|
17
19
|
|
|
18
20
|
## What OpenUI is
|
|
19
21
|
|
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
"description": "Extend engenty wizards with plugins: tools for AI steps, routes with their own tables, pages in the studio.",
|
|
4
4
|
"root": true,
|
|
5
5
|
"icon": "SquareTerminal",
|
|
6
|
-
"pages": ["index", "wizard-format", "plugins", "integration"]
|
|
6
|
+
"pages": ["index", "wizard-format", "plugins", "integration", "api"]
|
|
7
7
|
}
|
|
@@ -103,10 +103,21 @@ a minute, even with a token it already holds.
|
|
|
103
103
|
|
|
104
104
|
## The Claude Code plugin
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
Claude Code can also install a plugin. It adds the `/wizard` command, the skills to build and
|
|
107
|
+
use wizards, and `/wizards`, a pane beside the chat that lists your wizards and opens one in the
|
|
108
|
+
app’s browser pane. From GitHub, for the `engenty-wizards` server your AI apps already have:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
claude plugin marketplace add engenty/wizards
|
|
112
|
+
claude plugin install wizards@engenty
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
If engenty wizards has a public https address, it serves the plugin with its server bundled:
|
|
108
116
|
|
|
109
117
|
```bash
|
|
110
118
|
claude plugin marketplace add <your address>/api/claude-plugin/marketplace.json
|
|
111
|
-
claude plugin install
|
|
119
|
+
claude plugin install wizards@<your-host-with-dashes> # wizards.example.com → wizards-example-com
|
|
112
120
|
```
|
|
121
|
+
|
|
122
|
+
`claude plugin update wizards` brings the next version. In Claude Code’s auto mode the pane’s own
|
|
123
|
+
calls need allow rules; the plugin’s README names them.
|
|
@@ -1,31 +1,41 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Install
|
|
3
|
-
description: One command on macOS or
|
|
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
|
|
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
|
-
|
|
22
|
-
|
|
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`.
|
|
27
|
-
|
|
28
|
-
|
|
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
|
|
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
|
|
|
@@ -174,6 +174,7 @@ export async function createRun(input) {
|
|
|
174
174
|
origin: input.origin ?? null,
|
|
175
175
|
lang: input.lang ?? null,
|
|
176
176
|
unattended: input.unattended ?? false,
|
|
177
|
+
client: input.client ?? null,
|
|
177
178
|
status: first.type === "page" ? "waiting_input" : "running",
|
|
178
179
|
cursor: first.id,
|
|
179
180
|
state,
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { runFile } from "../cli/exec.js";
|
|
4
4
|
import { layout } from "../cli/home.js";
|
|
5
|
-
export const run =
|
|
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
|
|
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
|
-
/**
|
|
77
|
-
|
|
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,5 +1,6 @@
|
|
|
1
1
|
import { serve } from "@hono/node-server";
|
|
2
2
|
import { localEntryUrl, purgeSessions } from "./auth/index.js";
|
|
3
|
+
import { startClientChecks } from "./clients.js";
|
|
3
4
|
import { adoptLegacyData } from "./db/adopt.js";
|
|
4
5
|
import { knownTenants, migrateAllTenants, migrateControlDb, openTenantDb, withTenant, } from "./db/client.js";
|
|
5
6
|
import { startRunner, stopRunner } from "./engine/runner.js";
|
|
@@ -20,6 +21,9 @@ import { LOCAL_TENANT } from "./tenants/tenant.js";
|
|
|
20
21
|
import { onRestart, RESTART_EXIT, startUpdateChecks } from "./update.js";
|
|
21
22
|
// Before anything prints: the last lines stay in memory for a look from afar (debug.ts).
|
|
22
23
|
hookConsole();
|
|
24
|
+
// A background job that failed (a dropped connection to a remote database) is logged; the
|
|
25
|
+
// server keeps running for every other request instead of ending with it.
|
|
26
|
+
process.on("unhandledRejection", (err) => console.error("[unhandled]", err));
|
|
23
27
|
await migrateControlDb();
|
|
24
28
|
// What the linked account's plan confirmed last: the Pro modules it includes load with the rest.
|
|
25
29
|
await loadEntitlement();
|
|
@@ -28,9 +32,13 @@ await loadPlugins();
|
|
|
28
32
|
startJobs();
|
|
29
33
|
if (managed) {
|
|
30
34
|
// Every tenant database is brought to this release's schema; a new one migrates when first opened.
|
|
31
|
-
void migrateAllTenants()
|
|
35
|
+
void migrateAllTenants()
|
|
36
|
+
.then(({ migrated, failed }) => {
|
|
32
37
|
console.log(`tenant databases: ${migrated} current${failed.length ? `, failed: ${failed}` : ""}`);
|
|
33
|
-
})
|
|
38
|
+
})
|
|
39
|
+
.catch((err) => console.error("[migrate]", err));
|
|
40
|
+
// Which clients may start runs over the public API, and how often: the Manage-App's list.
|
|
41
|
+
startClientChecks();
|
|
34
42
|
}
|
|
35
43
|
else {
|
|
36
44
|
await adoptLegacyData();
|
|
@@ -43,8 +51,9 @@ else {
|
|
|
43
51
|
startUpdateChecks();
|
|
44
52
|
}
|
|
45
53
|
// The marketplace's starters and the starred entries, kept for offline use; checked hourly.
|
|
46
|
-
void syncMarketplace();
|
|
47
|
-
|
|
54
|
+
const marketplace = () => void syncMarketplace().catch((err) => console.error("[marketplace]", err.message));
|
|
55
|
+
marketplace();
|
|
56
|
+
setInterval(marketplace, 3600_000).unref();
|
|
48
57
|
const { default: app } = await import("./app.js");
|
|
49
58
|
/** Behind a path (APP_URL ends in /w) the proxy may leave the prefix on; the routes know none. */
|
|
50
59
|
function withoutBasePath(request) {
|
|
@@ -3,19 +3,24 @@ import { existsSync } from "node:fs";
|
|
|
3
3
|
import { readdir, readFile } from "node:fs/promises";
|
|
4
4
|
import { dirname, join, relative, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { marketplaceNameOf } from "@engenty-wizards/shared/claude-plugin";
|
|
6
7
|
import { zipSync } from "fflate";
|
|
7
8
|
import { env } from "./env.js";
|
|
8
9
|
/**
|
|
9
|
-
* The Claude Code plugin for this deployment: the `plugin/` folder
|
|
10
|
-
*
|
|
11
|
-
*
|
|
10
|
+
* The Claude Code plugin for this deployment: the `plugin/` folder, served as a marketplace
|
|
11
|
+
* (`url` source) whose one plugin is a zip (`archive` source), with this server's address as its
|
|
12
|
+
* homepage, the package's version, and an `.mcp.json` that bundles this server. So every
|
|
13
|
+
* deployment hands out its own plugin and no domain is baked into the repo, while the folder in
|
|
14
|
+
* git is a valid plugin as it is: installed from GitHub, it uses the `engenty-wizards` server the
|
|
15
|
+
* person configured.
|
|
12
16
|
*/
|
|
13
17
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
14
18
|
/** The folder that holds `plugin/` and the root package.json: three levels above this file. */
|
|
15
19
|
const base = resolve(here, "../../..");
|
|
16
20
|
const root = existsSync(join(base, "plugin")) ? base : undefined;
|
|
17
|
-
export const PLUGIN_NAME = "
|
|
18
|
-
|
|
21
|
+
export const PLUGIN_NAME = "wizards";
|
|
22
|
+
/** This deployment's marketplace, named after its host; `engenty` is the GitHub one. */
|
|
23
|
+
export const MARKETPLACE_NAME = marketplaceNameOf(env.appUrl);
|
|
19
24
|
export const pluginBase = `${env.appUrl}/api/claude-plugin`;
|
|
20
25
|
async function filesUnder(dir) {
|
|
21
26
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
@@ -32,12 +37,23 @@ export function pluginArchive() {
|
|
|
32
37
|
const dir = join(root, "plugin");
|
|
33
38
|
const files = {};
|
|
34
39
|
for (const path of (await filesUnder(dir)).sort()) {
|
|
35
|
-
const
|
|
40
|
+
const name = relative(dir, path);
|
|
41
|
+
let text = (await readFile(path, "utf8"))
|
|
36
42
|
.replaceAll("{{APP_URL}}", env.appUrl)
|
|
37
43
|
.replaceAll("{{VERSION}}", version);
|
|
44
|
+
if (name === ".claude-plugin/plugin.json") {
|
|
45
|
+
const manifest = JSON.parse(text);
|
|
46
|
+
manifest.version = version;
|
|
47
|
+
manifest.homepage = env.appUrl;
|
|
48
|
+
text = `${JSON.stringify(manifest, null, 2)}\n`;
|
|
49
|
+
}
|
|
38
50
|
// Fixed timestamps keep the archive, and so its sha256, stable across restarts.
|
|
39
|
-
files[
|
|
51
|
+
files[name] = new TextEncoder().encode(text);
|
|
40
52
|
}
|
|
53
|
+
const mcp = {
|
|
54
|
+
mcpServers: { "engenty-wizards": { type: "http", url: `${env.appUrl}/api/mcp` } },
|
|
55
|
+
};
|
|
56
|
+
files[".mcp.json"] = new TextEncoder().encode(`${JSON.stringify(mcp, null, 2)}\n`);
|
|
41
57
|
const zip = zipSync(files, { level: 9, mtime: new Date("2026-01-01T00:00:00Z") });
|
|
42
58
|
return { zip, sha256: createHash("sha256").update(zip).digest("hex"), version };
|
|
43
59
|
})();
|
|
@@ -48,7 +64,7 @@ export async function pluginMarketplace() {
|
|
|
48
64
|
return {
|
|
49
65
|
name: MARKETPLACE_NAME,
|
|
50
66
|
owner: { name: "engenty" },
|
|
51
|
-
description: "engenty wizards for Claude Code",
|
|
67
|
+
description: "engenty wizards for Claude Code, from this deployment",
|
|
52
68
|
plugins: [
|
|
53
69
|
{
|
|
54
70
|
name: PLUGIN_NAME,
|
|
@@ -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
|
-
/**
|
|
5
|
-
|
|
6
|
-
|
|
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
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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) {
|
|
@@ -11,6 +11,7 @@ import { nanoid } from "nanoid";
|
|
|
11
11
|
import { z } from "zod";
|
|
12
12
|
import { principalOf } from "../auth/index.js";
|
|
13
13
|
import { MARK_FOCUSED_PERSONAL } from "../browser/snapshot.js";
|
|
14
|
+
import { clientOf, clientRefused } from "../clients.js";
|
|
14
15
|
import { ConnectError, connectionViews, connectWithCredentials, disconnect, finishOAuth, oauthStateLang, oauthStateRun, startOAuth, } from "../connectors/index.js";
|
|
15
16
|
import { finishPluginOAuth, pluginStateTenant } from "../connectors/plugin-connections.js";
|
|
16
17
|
import { db, schema, withTenant } from "../db/client.js";
|
|
@@ -362,6 +363,13 @@ export const publicRoutes = new Hono()
|
|
|
362
363
|
if (!(await verifyTurnstile(body.turnstileToken, ip))) {
|
|
363
364
|
return refuse(c, { code: "confirmHuman" }, 403);
|
|
364
365
|
}
|
|
366
|
+
// The client that starts it (docs/content/dev/api.md): on a managed runtime the Manage-App's
|
|
367
|
+
// list says which may, and how often an hour.
|
|
368
|
+
const client = clientOf(c);
|
|
369
|
+
const refused = clientRefused(client);
|
|
370
|
+
if (refused) {
|
|
371
|
+
return c.json({ error: noteText({ code: refused.note }, requestLang(c)), code: refused.code }, refused.status);
|
|
372
|
+
}
|
|
365
373
|
const vid = visitorId(c, true, body.embedded);
|
|
366
374
|
const ipHash = hashIp(ip);
|
|
367
375
|
if (await visitorOverLimit(vid, ipHash)) {
|
|
@@ -385,6 +393,7 @@ export const publicRoutes = new Hono()
|
|
|
385
393
|
(await storedTranslation(w.id, body.lang))
|
|
386
394
|
? body.lang
|
|
387
395
|
: null,
|
|
396
|
+
client,
|
|
388
397
|
});
|
|
389
398
|
return c.json({ runId });
|
|
390
399
|
}
|
|
@@ -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";
|
|
@@ -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{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};
|
|
@@ -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{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};
|