wizards 0.4.2 → 0.4.4
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/apps/runtime/dist/docs/dev/plugins/reference.md +5 -1
- package/apps/runtime/dist/docs/dev/plugins/routes.md +35 -1
- package/apps/runtime/dist/docs/dev/plugins/server.md +27 -1
- package/apps/runtime/dist/manage.js +29 -0
- package/apps/runtime/dist/media/conversation.js +7 -1
- package/apps/runtime/dist/plugins/channels.js +77 -0
- package/apps/runtime/dist/plugins/loader.js +9 -1
- package/apps/runtime/dist/plugins/public.js +16 -2
- package/apps/runtime/dist/plugins/registry.js +1 -0
- package/apps/runtime/dist/routes/plugin-public.js +65 -12
- package/apps/web/dist/assets/{AboutDialog-CH1rQdia.js → AboutDialog-nLn3vZAN.js} +1 -1
- package/apps/web/dist/assets/{ChangelogDialog-78MQLHfA.js → ChangelogDialog-Cbx3mN2p.js} +1 -1
- package/apps/web/dist/assets/{ChatView-DLJfy69U.js → ChatView-1VuHE2Q7.js} +2 -2
- package/apps/web/dist/assets/{EditorPage-BYrZokVk.js → EditorPage-Cp3jqQsk.js} +1 -1
- package/apps/web/dist/assets/{FlowPreview-xIWPdOCV.js → FlowPreview-Bvj3R8g4.js} +1 -1
- package/apps/web/dist/assets/{Harness-CsDhNLSW.js → Harness-D-Z3MbYq.js} +1 -1
- package/apps/web/dist/assets/{NewWizardChannels-JB3V6DCL.js → NewWizardChannels-DCF7JoL0.js} +1 -1
- package/apps/web/dist/assets/{NewWizardPage-Bkh-hjSe.js → NewWizardPage-BTlgq7yN.js} +1 -1
- package/apps/web/dist/assets/{OnboardingPage-Diw1ILjC.js → OnboardingPage-CdoAfnI2.js} +1 -1
- package/apps/web/dist/assets/{ProjectPage-FgoZDzPI.js → ProjectPage-CoPSdDO9.js} +1 -1
- package/apps/web/dist/assets/{PublicRunner-ClT6B7t5.js → PublicRunner-CHwK3X75.js} +1 -1
- package/apps/web/dist/assets/{RunDrawer-DAwTEyjM.js → RunDrawer-CmGuqp0p.js} +1 -1
- package/apps/web/dist/assets/{RunnerView-B_tJroFM.js → RunnerView-DiaQiDbw.js} +1 -1
- package/apps/web/dist/assets/{SettingsPage-BsMN-XQs.js → SettingsPage-NzmFrJXb.js} +1 -1
- package/apps/web/dist/assets/{SetupPage-C7FuIpSt.js → SetupPage-8mBuezdY.js} +1 -1
- package/apps/web/dist/assets/{SharePage-CbFe83L9.js → SharePage-C-T5fkp1.js} +1 -1
- package/apps/web/dist/assets/changelog-CsxPWKJ-.js +1 -0
- package/apps/web/dist/assets/{esm-BhIeKBJY.js → esm-CnR8rDPi.js} +1 -1
- package/apps/web/dist/assets/index-D57rKyGN.js +14 -0
- package/apps/web/dist/assets/{settings-menu-C5Y8SOhG.js → settings-menu-D-UqnL1p.js} +1 -1
- package/apps/web/dist/index.html +1 -1
- package/node_modules/@engenty-wizards/plugin-sdk/dist/index.d.ts +94 -0
- package/package.json +1 -1
- package/apps/web/dist/assets/changelog-mYclku73.js +0 -1
- package/apps/web/dist/assets/index-fflYi8P1.js +0 -14
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Reference
|
|
3
3
|
description: Every call, type, address and setting of the plugin framework on one page.
|
|
4
|
-
version: 0.3
|
|
4
|
+
version: 0.4.3
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## Server half
|
|
@@ -40,6 +40,10 @@ The default export is a `WizardsPluginFactory`: `(wizards: WizardsPluginApi) =>
|
|
|
40
40
|
| `registerHttpRoute(route: PluginRoute)` | [Routes](./routes.md) |
|
|
41
41
|
| `registerPublicRoute(route: PluginPublicRoute)` | [Routes](./routes.md#public-routes) |
|
|
42
42
|
| `publicUrl(path): Promise<string>` | The full address of a public route for the current tenant |
|
|
43
|
+
| `publicRef(): Promise<string>` | [Routes](./routes.md#shared-routes). The part of `publicUrl` that names the current tenant |
|
|
44
|
+
| `registerSharedRoute(route: PluginSharedRoute)` | [Routes](./routes.md#shared-routes). One route for every tenant; `handler({ request, path, params, query, inTenant })` |
|
|
45
|
+
| `sharedUrl(path): string` | The full address of a shared route, the same for every tenant |
|
|
46
|
+
| `channels.get()`, `.request({ userId, country, wish?, lang })`, `.withdraw()`, `.book({ kind, quantity, key, runId? })` | [Numbers of the Manage-App](./server.md#numbers-of-the-manage-app). `get`: `{ number, request, shared }` or `null` without a Manage-App |
|
|
43
47
|
| `registerMigrations(folder: string)` | [Tables](./tables.md). The folder is relative to the plugin's |
|
|
44
48
|
| `on(event, listener)` | [Events](./events.md). `event`: `"run.done"`, `"run.failed"`, `"run.cancelled"`, `"space.created"`, `"space.updated"`, `"space.deleted"`, `"space.file.ready"`, `"space.file.removed"` |
|
|
45
49
|
| `every(name, everyMs, handler)` | [Jobs](./server.md#jobs). Per tenant that has the plugin, at most once per `everyMs` (a minute at least). `handler({ tenantId, lastRun, signal })` |
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Routes
|
|
3
3
|
description: HTTP routes below the plugin's own address — signed in, inside the person's tenant.
|
|
4
|
-
version: 0.3
|
|
4
|
+
version: 0.4.3
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
A plugin's routes live below `/api/studio/plugins/<id>`, and for programs with a key below
|
|
@@ -130,6 +130,40 @@ const address = await server.publicUrl(`/signal/${source.token}`);
|
|
|
130
130
|
- A body is taken up to 4 MB. Answers and errors work as for the plugin's other routes.
|
|
131
131
|
- A tenant without the plugin, or a suspended one, answers `404`.
|
|
132
132
|
|
|
133
|
+
## Shared routes
|
|
134
|
+
|
|
135
|
+
Some services post to one webhook for everyone: one account of the runtime's, one app. A shared
|
|
136
|
+
route has one address for all tenants, and the call itself says which tenant it is for, with
|
|
137
|
+
the ref the plugin handed the service before:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
// Handed to the service with the call it is about to make: names the tenant.
|
|
141
|
+
const ref = await server.publicRef();
|
|
142
|
+
|
|
143
|
+
server.registerSharedRoute({
|
|
144
|
+
method: "POST",
|
|
145
|
+
path: "/incoming",
|
|
146
|
+
handler: async ({ request, inTenant }) => {
|
|
147
|
+
const raw = await request.text();
|
|
148
|
+
if (!signedByTheService(raw, request.headers)) {
|
|
149
|
+
throw Object.assign(new Error("Bad signature."), { status: 401, code: "bad_signature" });
|
|
150
|
+
}
|
|
151
|
+
const event = JSON.parse(raw);
|
|
152
|
+
return inTenant(event.ref, () => pickUp(event));
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// The address to enter at the service, once: the same for every tenant.
|
|
157
|
+
server.sharedUrl("/incoming");
|
|
158
|
+
// https://…/api/public/plugins/<id>/shared/incoming
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
- The handler runs outside any tenant: check the service's signature, then `inTenant`.
|
|
162
|
+
- `inTenant(ref, work)` runs `work` inside the tenant of that ref, as a public route of the
|
|
163
|
+
tenant would run. A ref no tenant has, a suspended tenant and one without the plugin throw
|
|
164
|
+
`404` / `not_found`.
|
|
165
|
+
- `publicRef()` is the part of `publicUrl` that names the tenant; it is the same every time.
|
|
166
|
+
|
|
133
167
|
## From the studio half
|
|
134
168
|
|
|
135
169
|
`studio.api` calls the plugin's own routes and gives the parsed JSON:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: The server half
|
|
3
3
|
description: The function a plugin exports, what it is handed, and the rules it runs under.
|
|
4
|
-
version: 0.3
|
|
4
|
+
version: 0.4.3
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
The server half is a file whose default export is a function. The runtime calls it once when the
|
|
@@ -43,6 +43,8 @@ export default definePlugin((wizards) => {
|
|
|
43
43
|
| `index.put(entry)`, `index.remove(space, key?)` | Texts in the space's search index | [The space](./space.md#the-search-index) |
|
|
44
44
|
| `registerHttpRoute(route)` | A route below `/api/studio/plugins/<id>` | [Routes](./routes.md) |
|
|
45
45
|
| `registerPublicRoute(route)`, `publicUrl(path)` | A route anyone may call, and its address | [Routes](./routes.md#public-routes) |
|
|
46
|
+
| `registerSharedRoute(route)`, `sharedUrl(path)`, `publicRef()` | One route for every tenant, and the ref that names one | [Routes](./routes.md#shared-routes) |
|
|
47
|
+
| `channels.get()`, `request`, `withdraw`, `book` | The numbers the Manage-App gives the tenant | below |
|
|
46
48
|
| `registerRunner(runner)` | A door a wizard runs through, beside the page and the chat | [Runners](./runners.md) |
|
|
47
49
|
| `runs.start(…)`, `report`, `answerPage`, … | Drives runs from the plugin's own door | [Runners](./runners.md#drive-a-run) |
|
|
48
50
|
| `speech.conversation()` | The tenant's realtime speech model, for a call | [Runners](./runners.md#a-call) |
|
|
@@ -85,6 +87,30 @@ Either way a plugin never names a tenant:
|
|
|
85
87
|
- State kept in a variable of the module is shared by all tenants. Keep per-tenant state in a
|
|
86
88
|
table.
|
|
87
89
|
|
|
90
|
+
## Numbers of the Manage-App
|
|
91
|
+
|
|
92
|
+
On engenty.ai a team gets its number for texts and calls from engenty: it asks for one, an
|
|
93
|
+
admin assigns it. `server.channels` reads it for the current tenant and books what it carries;
|
|
94
|
+
the prices are the Manage-App's ([manage contract](https://github.com/engenty/wizards/blob/main/docs/manage-contract.md#numbers-of-a-tenant)).
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
const channels = await server.channels.get();
|
|
98
|
+
// { number: { number: "+43…", country: "AT", kinds: ["sms", "call"], providerRef: "PN…" } | null,
|
|
99
|
+
// request: { id, country, wish, createdAt } | null,
|
|
100
|
+
// shared: [{ number, country, kinds }] }
|
|
101
|
+
|
|
102
|
+
await server.channels.request({ userId: request.user.id, country: "AT", lang: "de" });
|
|
103
|
+
await server.channels.book({ kind: "sms", quantity: segments, key: messageSid, runId });
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
- `get` is kept for a minute; `request` and `withdraw` forget it.
|
|
107
|
+
- `request` throws `403` / `plan` on a plan without a number and `409` / `has_number` when
|
|
108
|
+
the team has one. Asking again answers the open request.
|
|
109
|
+
- `book`: `sms` by segment, sent or received, `call` by started minute. The same `key` books
|
|
110
|
+
once.
|
|
111
|
+
- A runtime without a Manage-App has no numbers: `get` gives `null`, `request` and `withdraw`
|
|
112
|
+
throw `404` / `not_managed`, `book` books nothing.
|
|
113
|
+
|
|
88
114
|
## Asking a model
|
|
89
115
|
|
|
90
116
|
`server.generate` asks one of the tenant's models, the same ones its wizards run on, and is paid
|
|
@@ -187,6 +187,35 @@ export async function publicPlans(base) {
|
|
|
187
187
|
export function bookUsage(input) {
|
|
188
188
|
return call("POST", "/v1/usage", input);
|
|
189
189
|
}
|
|
190
|
+
const channels = new Map();
|
|
191
|
+
/** The tenant's numbers, cached for a minute. */
|
|
192
|
+
export async function tenantChannels(id) {
|
|
193
|
+
const hit = channels.get(id);
|
|
194
|
+
if (hit && Date.now() - hit.at < 60_000) {
|
|
195
|
+
return hit.value;
|
|
196
|
+
}
|
|
197
|
+
const value = await call("GET", `/v1/tenants/${encodeURIComponent(id)}/channels`);
|
|
198
|
+
channels.set(id, { at: Date.now(), value });
|
|
199
|
+
return value;
|
|
200
|
+
}
|
|
201
|
+
/** Forgets what the Manage-App said of the tenant's numbers: the next `tenantChannels` asks. */
|
|
202
|
+
export function forgetChannels(id) {
|
|
203
|
+
channels.delete(id);
|
|
204
|
+
}
|
|
205
|
+
/** A person of the tenant asks for its number; what the Manage-App answers with is the request. */
|
|
206
|
+
export async function requestChannelNumber(id, input) {
|
|
207
|
+
channels.delete(id);
|
|
208
|
+
return call("POST", `/v1/tenants/${encodeURIComponent(id)}/channels/requests`, input);
|
|
209
|
+
}
|
|
210
|
+
/** The tenant takes its open request back. */
|
|
211
|
+
export async function withdrawChannelRequest(id) {
|
|
212
|
+
channels.delete(id);
|
|
213
|
+
return call("DELETE", `/v1/tenants/${encodeURIComponent(id)}/channels/requests`);
|
|
214
|
+
}
|
|
215
|
+
/** What a tenant's channel used, priced by the Manage-App: texts by segment, calls by minute. */
|
|
216
|
+
export function bookChannelUsage(id, input) {
|
|
217
|
+
return call("POST", `/v1/tenants/${encodeURIComponent(id)}/channels/usage`, input);
|
|
218
|
+
}
|
|
190
219
|
/** A push to the mobile app on one device; the Manage-App holds the APNs and FCM keys. */
|
|
191
220
|
export function pushToDevice(input) {
|
|
192
221
|
return call("POST", "/v1/push", input);
|
|
@@ -150,7 +150,13 @@ export async function mintConversation(view) {
|
|
|
150
150
|
tool_choice: "auto",
|
|
151
151
|
audio: {
|
|
152
152
|
// What the person said, written down: the call screen shows it as captions.
|
|
153
|
-
input: {
|
|
153
|
+
input: {
|
|
154
|
+
transcription: { model: "gpt-4o-mini-transcribe" },
|
|
155
|
+
// Phones and laptops play the wizard's voice through their own speakers, which the mic
|
|
156
|
+
// picks up: far-field filtering and a higher bar for speech keep it from interrupting itself.
|
|
157
|
+
noise_reduction: { type: "far_field" },
|
|
158
|
+
turn_detection: { type: "server_vad", threshold: 0.7, silence_duration_ms: 600 },
|
|
159
|
+
},
|
|
154
160
|
output: { voice: VOICES[view.lang] },
|
|
155
161
|
},
|
|
156
162
|
},
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { bookChannelUsage, ManageError, managed, requestChannelNumber, tenantChannels, withdrawChannelRequest, } from "../manage.js";
|
|
2
|
+
import { currentTenant } from "../tenants/tenant.js";
|
|
3
|
+
/**
|
|
4
|
+
* `server.channels`: the numbers the Manage-App gives the current tenant
|
|
5
|
+
* (docs/manage-contract.md, "Numbers of a tenant"). Without a Manage-App there are none.
|
|
6
|
+
*/
|
|
7
|
+
/** A refusal of the Manage-App as a plugin passes it on: its status and its code. */
|
|
8
|
+
const asked = (work) => work.catch((err) => {
|
|
9
|
+
throw err instanceof ManageError
|
|
10
|
+
? Object.assign(new Error(err.message), { status: err.status, code: err.code })
|
|
11
|
+
: err;
|
|
12
|
+
});
|
|
13
|
+
const unmanaged = () => Object.assign(new Error("This runtime has no Manage-App, so it has no numbers."), {
|
|
14
|
+
status: 404,
|
|
15
|
+
code: "not_managed",
|
|
16
|
+
});
|
|
17
|
+
export const channelsApi = {
|
|
18
|
+
async get() {
|
|
19
|
+
if (!managed) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
const c = await asked(tenantChannels(currentTenant()));
|
|
23
|
+
return {
|
|
24
|
+
number: c.number
|
|
25
|
+
? {
|
|
26
|
+
number: c.number.number,
|
|
27
|
+
country: c.number.country,
|
|
28
|
+
kinds: c.number.kinds,
|
|
29
|
+
providerRef: c.number.providerRef ?? null,
|
|
30
|
+
}
|
|
31
|
+
: null,
|
|
32
|
+
request: c.request
|
|
33
|
+
? {
|
|
34
|
+
id: c.request.id,
|
|
35
|
+
country: c.request.country,
|
|
36
|
+
wish: c.request.wish,
|
|
37
|
+
createdAt: c.request.createdAt,
|
|
38
|
+
}
|
|
39
|
+
: null,
|
|
40
|
+
shared: c.shared,
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
async request(input) {
|
|
44
|
+
if (!managed) {
|
|
45
|
+
throw unmanaged();
|
|
46
|
+
}
|
|
47
|
+
const { request } = await asked(requestChannelNumber(currentTenant(), {
|
|
48
|
+
userId: input.userId,
|
|
49
|
+
country: input.country.toUpperCase(),
|
|
50
|
+
wish: input.wish ?? null,
|
|
51
|
+
lang: input.lang,
|
|
52
|
+
}));
|
|
53
|
+
return {
|
|
54
|
+
id: request.id,
|
|
55
|
+
country: request.country,
|
|
56
|
+
wish: request.wish,
|
|
57
|
+
createdAt: request.createdAt,
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
async withdraw() {
|
|
61
|
+
if (!managed) {
|
|
62
|
+
throw unmanaged();
|
|
63
|
+
}
|
|
64
|
+
return (await asked(withdrawChannelRequest(currentTenant()))).withdrawn;
|
|
65
|
+
},
|
|
66
|
+
async book(input) {
|
|
67
|
+
if (!managed || input.quantity <= 0) {
|
|
68
|
+
return { credits: 0 };
|
|
69
|
+
}
|
|
70
|
+
return asked(bookChannelUsage(currentTenant(), {
|
|
71
|
+
kind: input.kind,
|
|
72
|
+
quantity: input.quantity,
|
|
73
|
+
idempotencyKey: input.key,
|
|
74
|
+
...(input.runId ? { runId: input.runId } : {}),
|
|
75
|
+
}));
|
|
76
|
+
},
|
|
77
|
+
};
|
|
@@ -20,10 +20,11 @@ import { seal, unseal } from "../secrets/crypto.js";
|
|
|
20
20
|
import { putIndexEntry, removeIndexEntries } from "../services/project-index.js";
|
|
21
21
|
import { originData } from "../services/space-origin.js";
|
|
22
22
|
import { safeFetch } from "../tools/net-guard.js";
|
|
23
|
+
import { channelsApi } from "./channels.js";
|
|
23
24
|
import { discoverPlugins } from "./discovery.js";
|
|
24
25
|
import { migrateOpenTenants, migratePlugins } from "./migrations.js";
|
|
25
26
|
import { proModulesDir, proSources } from "./pro-state.js";
|
|
26
|
-
import { publicUrlOf } from "./public.js";
|
|
27
|
+
import { publicRefOf, publicUrlOf, sharedUrlOf } from "./public.js";
|
|
27
28
|
import { announceChange, compileRoute, dropPlugin, emptyRecord, loadedPlugin, loadedPlugins, setPlugin, } from "./registry.js";
|
|
28
29
|
import { runsApiFor } from "./runs.js";
|
|
29
30
|
/**
|
|
@@ -159,6 +160,12 @@ function apiFor(record) {
|
|
|
159
160
|
});
|
|
160
161
|
},
|
|
161
162
|
publicUrl: (path) => publicUrlOf(source.id, path),
|
|
163
|
+
publicRef: () => publicRefOf(source.id),
|
|
164
|
+
registerSharedRoute(route) {
|
|
165
|
+
record.sharedRoutes.push(compileRoute(route));
|
|
166
|
+
},
|
|
167
|
+
sharedUrl: (path) => sharedUrlOf(source.id, path),
|
|
168
|
+
channels: channelsApi,
|
|
162
169
|
runs: runsApiFor(source.id),
|
|
163
170
|
speech: {
|
|
164
171
|
async conversation() {
|
|
@@ -307,6 +314,7 @@ async function load(source) {
|
|
|
307
314
|
record.assistantTools.clear();
|
|
308
315
|
record.assistantPages = [];
|
|
309
316
|
record.publicRoutes = [];
|
|
317
|
+
record.sharedRoutes = [];
|
|
310
318
|
record.runners.clear();
|
|
311
319
|
record.jobs.clear();
|
|
312
320
|
record.starters.clear();
|
|
@@ -6,9 +6,24 @@ import { currentTenant } from "../tenants/tenant.js";
|
|
|
6
6
|
/**
|
|
7
7
|
* Public addresses of plugins (`server.publicUrl`): `/api/public/plugins/<plugin>/<ref>/…`. The
|
|
8
8
|
* ref names the tenant without giving its id away; one per tenant and plugin, kept for good.
|
|
9
|
+
* A shared route (`server.sharedUrl`) is `/api/public/plugins/<plugin>/shared/…` for every
|
|
10
|
+
* tenant: a ref is 24 characters, so it is never `shared`.
|
|
9
11
|
*/
|
|
12
|
+
export const SHARED = "shared";
|
|
13
|
+
const rest = (path) => {
|
|
14
|
+
const p = path.startsWith("/") ? path : `/${path}`;
|
|
15
|
+
return p === "/" ? "" : p;
|
|
16
|
+
};
|
|
10
17
|
/** The full address of a plugin's public route for the current tenant. */
|
|
11
18
|
export async function publicUrlOf(plugin, path) {
|
|
19
|
+
return `${env.publicUrl}/api/public/plugins/${plugin}/${await publicRefOf(plugin)}${rest(path)}`;
|
|
20
|
+
}
|
|
21
|
+
/** The full address of a plugin's shared route. */
|
|
22
|
+
export function sharedUrlOf(plugin, path) {
|
|
23
|
+
return `${env.publicUrl}/api/public/plugins/${plugin}/${SHARED}${rest(path)}`;
|
|
24
|
+
}
|
|
25
|
+
/** The ref of the current tenant's public address for a plugin, made the first time. */
|
|
26
|
+
export async function publicRefOf(plugin) {
|
|
12
27
|
const tenantId = currentTenant();
|
|
13
28
|
const where = and(eq(control.pluginAddress.tenantId, tenantId), eq(control.pluginAddress.plugin, plugin));
|
|
14
29
|
let row = await controlDb.query.pluginAddress.findFirst({ where });
|
|
@@ -19,8 +34,7 @@ export async function publicUrlOf(plugin, path) {
|
|
|
19
34
|
.onConflictDoNothing();
|
|
20
35
|
row = (await controlDb.query.pluginAddress.findFirst({ where }));
|
|
21
36
|
}
|
|
22
|
-
|
|
23
|
-
return `${env.publicUrl}/api/public/plugins/${plugin}/${row.ref}${rest === "/" ? "" : rest}`;
|
|
37
|
+
return row.ref;
|
|
24
38
|
}
|
|
25
39
|
/** The tenant a plugin's public address belongs to. */
|
|
26
40
|
export async function tenantOfAddress(plugin, ref) {
|
|
@@ -1,26 +1,82 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
2
|
import { withTenant } from "../db/client.js";
|
|
3
|
-
import { tenantOfAddress } from "../plugins/public.js";
|
|
3
|
+
import { SHARED, tenantOfAddress } from "../plugins/public.js";
|
|
4
4
|
import { loadedPlugin, pluginIdsOf } from "../plugins/registry.js";
|
|
5
5
|
import { tenantStatus } from "../tenants/control.js";
|
|
6
6
|
import { answerOf } from "./plugins.js";
|
|
7
7
|
/**
|
|
8
8
|
* Plugins' public routes (`server.registerPublicRoute`), below `/api/public/plugins/<id>/<ref>`.
|
|
9
9
|
* Nobody is signed in: the ref names the tenant, and the plugin checks a secret of its own.
|
|
10
|
+
* Shared routes (`server.registerSharedRoute`) are below `/api/public/plugins/<id>/shared`, for
|
|
11
|
+
* every tenant; their handler enters a tenant by the ref the call carries.
|
|
10
12
|
*/
|
|
11
13
|
const notFound = (c) => c.json({ error: "not found", code: "not_found" }, 404);
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
14
|
+
/** What a handler threw, as the answer: its status and code, or a 500. */
|
|
15
|
+
function failed(c, id, what, err) {
|
|
16
|
+
const answer = answerOf(err);
|
|
17
|
+
if (answer) {
|
|
18
|
+
return c.json(answer.body, answer.status);
|
|
19
|
+
}
|
|
20
|
+
console.error(`[plugin ${id}] ${what}:`, err);
|
|
21
|
+
return c.json({ error: "The plugin failed.", code: "plugin_failed" }, 500);
|
|
22
|
+
}
|
|
23
|
+
/** The tenant of a ref, when it is active and has the plugin. */
|
|
24
|
+
async function tenantWithPlugin(id, ref) {
|
|
15
25
|
const tenantId = await tenantOfAddress(id, ref);
|
|
16
26
|
if (!tenantId || (await tenantStatus(tenantId)) !== "active") {
|
|
17
|
-
return
|
|
27
|
+
return null;
|
|
18
28
|
}
|
|
19
|
-
|
|
29
|
+
return (await pluginIdsOf(tenantId)).has(id) ? tenantId : null;
|
|
30
|
+
}
|
|
31
|
+
async function shared(c) {
|
|
32
|
+
const id = c.req.param("id") ?? "";
|
|
33
|
+
const plugin = loadedPlugin(id);
|
|
20
34
|
if (!plugin) {
|
|
21
35
|
return notFound(c);
|
|
22
36
|
}
|
|
23
37
|
const url = new URL(c.req.url);
|
|
38
|
+
const marker = `/plugins/${id}/${SHARED}`;
|
|
39
|
+
const path = url.pathname.slice(url.pathname.indexOf(marker) + marker.length);
|
|
40
|
+
for (const { route, pattern, params } of plugin.sharedRoutes) {
|
|
41
|
+
const match = route.method === c.req.method ? pattern.exec(path) : null;
|
|
42
|
+
if (!match) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const request = {
|
|
46
|
+
request: c.req.raw,
|
|
47
|
+
path: path || "/",
|
|
48
|
+
params: Object.fromEntries(params.map((name, i) => [name, decodeURIComponent(match[i + 1])])),
|
|
49
|
+
query: url.searchParams,
|
|
50
|
+
async inTenant(ref, work) {
|
|
51
|
+
const tenantId = await tenantWithPlugin(id, ref);
|
|
52
|
+
if (!tenantId) {
|
|
53
|
+
throw Object.assign(new Error("No tenant has this address."), {
|
|
54
|
+
status: 404,
|
|
55
|
+
code: "not_found",
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return withTenant(tenantId, async () => await work());
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
try {
|
|
62
|
+
const result = await route.handler(request);
|
|
63
|
+
return result instanceof Response ? result : c.json(result ?? null);
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
return failed(c, id, `shared ${route.method} ${path}`, err);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return notFound(c);
|
|
70
|
+
}
|
|
71
|
+
async function dispatch(c) {
|
|
72
|
+
const id = c.req.param("id") ?? "";
|
|
73
|
+
const ref = c.req.param("ref") ?? "";
|
|
74
|
+
const tenantId = await tenantWithPlugin(id, ref);
|
|
75
|
+
const plugin = tenantId ? loadedPlugin(id) : undefined;
|
|
76
|
+
if (!tenantId || !plugin) {
|
|
77
|
+
return notFound(c);
|
|
78
|
+
}
|
|
79
|
+
const url = new URL(c.req.url);
|
|
24
80
|
const marker = `/plugins/${id}/${ref}`;
|
|
25
81
|
const path = url.pathname.slice(url.pathname.indexOf(marker) + marker.length);
|
|
26
82
|
for (const { route, pattern, params } of plugin.publicRoutes) {
|
|
@@ -44,16 +100,13 @@ async function dispatch(c) {
|
|
|
44
100
|
return result instanceof Response ? result : c.json(result ?? null);
|
|
45
101
|
}
|
|
46
102
|
catch (err) {
|
|
47
|
-
|
|
48
|
-
if (answer) {
|
|
49
|
-
return c.json(answer.body, answer.status);
|
|
50
|
-
}
|
|
51
|
-
console.error(`[plugin ${id}] public ${route.method} ${path}:`, err);
|
|
52
|
-
return c.json({ error: "The plugin failed.", code: "plugin_failed" }, 500);
|
|
103
|
+
return failed(c, id, `public ${route.method} ${path}`, err);
|
|
53
104
|
}
|
|
54
105
|
}
|
|
55
106
|
return notFound(c);
|
|
56
107
|
}
|
|
57
108
|
export const publicPluginRoutes = new Hono()
|
|
109
|
+
.all(`/:id/${SHARED}`, shared)
|
|
110
|
+
.all(`/:id/${SHARED}/*`, shared)
|
|
58
111
|
.all("/:id/:ref", dispatch)
|
|
59
112
|
.all("/:id/:ref/*", dispatch);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as e}from"./rolldown-runtime-hePW80VL.js";import{n as t,t as n}from"./jsx-runtime-BNakU3Ej.js";import{$t as r,An as i,Fn as a,Gt as o,Ht as s,Ut as c,Wt as l,Xt as u,an as d,rn as f,sn as p,tn as m}from"./ui-DTuixH4y.js";import{t as h}from"./preload-helper-CNEI7N0N.js";import{$ as g,H as _,V as v,gt as y}from"./index-fflYi8P1.js";import{c as b,f as x}from"./settings-menu-C5Y8SOhG.js";import{ReleaseNotes as S,loadReleases as C,releaseDate as w,releaseLabel as T}from"./ChangelogDialog-78MQLHfA.js";var E=e(t(),1),D=[{name:`engenty wizards`,description:{de:`KI-Wizards aus einer Beschreibung, mit einem Link geteilt.`,en:`AI wizards built from a description, shared with a link.`},homepage:`https://github.com/engenty/wizards`,license:`FSL-1.1-MIT`},{name:`engenty`,description:{de:`Konnektoren, Datentabellen, Suche und die Engenties stammen aus dem engenty-Framework.`,en:`Connectors, data tables, search and the engenties come from the engenty framework.`},homepage:`https://github.com/engenty/engenty`,license:`FSL-1.1-MIT`}],O=[{name:`Mastra`,description:{de:`Agent-Framework hinter den KI-Schritten.`,en:`Agent framework behind the AI steps.`},homepage:`https://github.com/mastra-ai/mastra`,license:`Apache-2.0`},{name:`AI SDK`,description:{de:`Modell-Aufrufe für Text, Bild, Video und Sprache.`,en:`Model calls for text, image, video and speech.`},homepage:`https://github.com/vercel/ai`,license:`Apache-2.0`},{name:`Model Context Protocol`,description:{de:`MCP-Server zum Bauen von Wizards, MCP-Client für Werkzeuge der Schritte.`,en:`MCP server for building wizards, MCP client for the steps' tools.`},homepage:`https://github.com/modelcontextprotocol/typescript-sdk`,license:`MIT`},{name:`Hono`,description:{de:`HTTP-API der Runtime.`,en:`The runtime's HTTP API.`},homepage:`https://github.com/honojs/hono`,license:`MIT`},{name:`Drizzle ORM`,description:{de:`Schema und Migrationen der Datenbanken.`,en:`Schema and migrations of the databases.`},homepage:`https://github.com/drizzle-team/drizzle-orm`,license:`Apache-2.0`},{name:`libSQL`,description:{de:`Eine Datenbank je Mandant, lokal als Datei oder bei Turso.`,en:`One database per tenant, a local file or at Turso.`},homepage:`https://github.com/tursodatabase/libsql`,license:`MIT`},{name:`Playwright`,description:{de:`Browser-Schritte sowie PDF- und PNG-Export.`,en:`Browser steps and the PDF and PNG export.`},homepage:`https://github.com/microsoft/playwright`,license:`Apache-2.0`},{name:`agentOS`,description:{de:`Sandbox für Code-Schritte ohne Docker.`,en:`Sandbox for code steps without Docker.`},homepage:`https://github.com/rivet-dev/agentos`,license:`Apache-2.0`},{name:`React`,description:{de:`UI-Bibliothek der Web-App.`,en:`UI library of the web app.`},homepage:`https://github.com/facebook/react`,license:`MIT`},{name:`Vite`,description:{de:`Build-Werkzeug der Web-App.`,en:`Build tool of the web app.`},homepage:`https://github.com/vitejs/vite`,license:`MIT`},{name:`Tailwind CSS`,description:{de:`Styling der Oberfläche.`,en:`Styling of the interface.`},homepage:`https://github.com/tailwindlabs/tailwindcss`,license:`MIT`},{name:`React Flow`,description:{de:`Das Diagramm der Schritte im Editor.`,en:`The diagram of steps in the editor.`},homepage:`https://github.com/xyflow/xyflow`,license:`MIT`},{name:`TanStack Query`,description:{de:`Server-Daten in der Web-App.`,en:`Server data in the web app.`},homepage:`https://github.com/TanStack/query`,license:`MIT`},{name:`xterm.js`,description:{de:`Das Terminal auf der Seite, in dem sich KI-Clients anmelden.`,en:`The terminal on the page where AI clients sign in.`},homepage:`https://github.com/xtermjs/xterm.js`,license:`MIT`},{name:`Lucide`,description:{de:`Icons.`,en:`Icons.`},homepage:`https://github.com/lucide-icons/lucide`,license:`ISC`},{name:`Geist & Geist Mono`,description:{de:`Schrift der Oberfläche.`,en:`Typeface of the interface.`},homepage:`https://github.com/vercel/geist-font`,license:`OFL-1.1`},{name:`Space Grotesk`,description:{de:`Schrift der Überschriften.`,en:`Typeface of the headings.`},homepage:`https://github.com/floriankarsten/space-grotesk`,license:`OFL-1.1`},{name:`git-cliff`,description:{de:`Erzeugt den Changelog aus den Commits.`,en:`Generates the changelog from the commits.`},homepage:`https://github.com/orhun/git-cliff`,license:`MIT OR Apache-2.0`}],k=e(n(),1),A=(e,...t)=>t.some(t=>t?.toLowerCase().includes(e));function j({name:e,homepage:t,version:n,license:r,description:a}){return(0,k.jsxs)(`li`,{className:`py-2.5`,children:[(0,k.jsxs)(`div`,{className:`flex flex-wrap items-baseline gap-x-2.5 gap-y-0.5`,children:[t?(0,k.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,k.jsx)(i,{"aria-hidden":!0,className:`size-3 text-ink-4`})]}):(0,k.jsx)(`span`,{className:`font-medium text-[0.875rem] text-ink`,children:e}),n?(0,k.jsx)(`span`,{className:`text-[0.75rem] text-ink-3 tabular-nums`,children:n}):null,(0,k.jsx)(`span`,{className:`text-[0.75rem] text-ink-3`,children:r})]}),a?(0,k.jsx)(`p`,{className:`mt-0.5 text-[0.8125rem] text-ink-3`,children:a}):null]})}function M({title:e,hint:t,children:n}){return(0,k.jsxs)(`section`,{className:`mt-6`,children:[(0,k.jsx)(`h3`,{className:`border-border-soft border-b pb-2 font-display font-semibold text-[1rem]`,children:e}),t?(0,k.jsx)(`p`,{className:`mt-2 text-[0.8125rem] text-ink-3`,children:t}):null,n]})}function N({credits:e}){return(0,k.jsx)(`ul`,{className:`divide-y divide-border-soft`,children:e.map(e=>(0,k.jsx)(j,{name:e.name,homepage:e.homepage,license:e.license,description:e.description[f]},e.name))})}function P({label:e,packages:t}){return(0,k.jsxs)(`div`,{className:`mt-4`,children:[(0,k.jsx)(`h4`,{className:`font-medium text-[0.75rem] text-ink-3 tracking-wide`,children:e}),(0,k.jsx)(`ul`,{className:`divide-y divide-border-soft`,children:t.map(e=>(0,k.jsx)(j,{...e},e.name))})]})}function F({active:e}){let[t,n]=(0,E.useState)(null),[r,i]=(0,E.useState)(``);(0,E.useEffect)(()=>{if(!e||t)return;let r=!1;return h(()=>import(`./oss-credits-BQQX0kvk.js`).then(e=>{r||n(e.default)}),[]),()=>{r=!0}},[e,t]),(0,E.useEffect)(()=>{e||i(``)},[e]);let a=r.trim().toLowerCase(),s=e=>a?e.filter(e=>A(a,e.name,e.license,e.description.de,e.description.en)):e,l=e=>a?e.filter(e=>A(a,e.name,e.license,e.version)):e,f=s(D),p=s(O),m=t?[{id:`shared`,label:d(`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=f.length===0&&p.length===0&&m.length===0;return(0,k.jsxs)(k.Fragment,{children:[(0,k.jsx)(`p`,{className:`mb-4 text-[0.875rem] text-ink-3`,children:d(`about.creditsHint`)}),(0,k.jsx)(o,{type:`search`,value:r,placeholder:d(`about.search`),onChange:e=>i(e.target.value)}),f.length>0?(0,k.jsx)(M,{title:d(`about.creditsProduct`),children:(0,k.jsx)(N,{credits:f})}):null,p.length>0?(0,k.jsx)(M,{title:d(`about.creditsBuiltWith`),children:(0,k.jsx)(N,{credits:p})}):null,t===null?(0,k.jsx)(`div`,{className:`flex justify-center py-10 text-ink-3`,children:(0,k.jsx)(u,{})}):m.length>0?(0,k.jsx)(M,{title:d(`about.creditsDeps`),hint:a?void 0:d(`about.creditsDepsHint`),children:m.map(e=>(0,k.jsx)(P,{label:e.label,packages:e.packages},e.id))}):null,t!==null&&g?(0,k.jsx)(c,{children:d(`about.noMatches`)}):null]})}var I=[`new`,`earlier`,`credits`],L=e=>d(e===`new`?`about.tab.new`:e===`earlier`?`about.tab.earlier`:`about.credits`),R=e=>e===1?d(`about.change`):d(`about.changes`,{n:e});function z({open:e,onClose:t,me:n}){let i=n?.mode===`local`,{data:o,latest:c}=_(i&&e),[f,h]=(0,E.useState)(null),[D,O]=(0,E.useState)(`new`),A=x(I.indexOf(D)),j=o?.newer&&c?c:g;(0,E.useEffect)(()=>{if(!e)return;let t=!1;return h(null),C(j===`0.4.2`?void 0:j).then(e=>{t||h(e)}),()=>{t=!0}},[e,j]);let M=f?.find(e=>e.version===`v${j}`)??f?.[0]??null,N=f?.filter(e=>e!==M)??[],P=i?o?.kind??null:`cloud`;return(0,k.jsxs)(s,{open:e,onClose:t,wide:!0,height:`tall`,children:[(0,k.jsx)(`div`,{className:`-mt-2 -mr-2 sticky top-0 z-10 flex h-0 justify-end`,children:(0,k.jsx)(l,{label:d(`common.close`),onClick:t,children:(0,k.jsx)(p,{className:`size-4`})})}),(0,k.jsxs)(`div`,{className:`flex flex-col items-center pt-4 text-center`,children:[(0,k.jsx)(y,{size:96}),(0,k.jsxs)(`div`,{className:`mt-4 font-display font-semibold text-[1.5rem] tracking-tight`,children:[`engenty`,(0,k.jsx)(`span`,{className:`text-ember`,children:`.`}),(0,k.jsx)(`span`,{className:`ml-1.5 font-normal text-ink-3`,children:`wizards`})]}),(0,k.jsxs)(`div`,{className:`mt-1 text-[0.9375rem] text-ink-3 tabular-nums`,children:[d(`about.version`,{v:g}),P?` · ${d(`about.kind.${P}`)}`:``]})]}),i?(0,k.jsx)(`div`,{className:`mt-5`,children:(0,k.jsx)(v,{enabled:e})}):null,(0,k.jsx)(`div`,{className:`mt-6 flex justify-center`,children:(0,k.jsx)(r,{value:D,onChange:O,options:I.map(e=>({id:e,label:L(e)})),label:d(`about.title`)})}),(0,k.jsxs)(`div`,{className:m(`mt-5`,b(A)),children:[D===`new`?f===null?(0,k.jsx)(`div`,{className:`flex justify-center py-6 text-ink-3`,children:(0,k.jsx)(u,{})}):M?(0,k.jsxs)(k.Fragment,{children:[(0,k.jsxs)(`h3`,{className:`flex items-baseline gap-3 border-border-soft border-b pb-2 font-display font-semibold text-[1rem]`,children:[T(M),(0,k.jsx)(`span`,{className:`font-normal font-sans text-[0.8125rem] text-ink-3 tabular-nums`,children:w(M)})]}),(0,k.jsx)(`div`,{className:`mt-3`,children:(0,k.jsx)(S,{release:M})})]}):(0,k.jsx)(`p`,{className:`text-[0.875rem] text-ink-3`,children:d(`about.changelogEmpty`)}):null,D===`earlier`?(0,k.jsx)(`div`,{className:`flex flex-col divide-y divide-border-soft`,children:N.map(e=>(0,k.jsxs)(`details`,{className:`group/r py-2`,children:[(0,k.jsxs)(`summary`,{className:`flex cursor-pointer list-none items-baseline gap-3 text-[0.875rem] [&::-webkit-details-marker]:hidden`,children:[(0,k.jsx)(a,{className:`size-3.5 shrink-0 self-center text-ink-4 transition group-open/r:rotate-90`}),(0,k.jsx)(`span`,{className:`font-medium text-ink tabular-nums`,children:T(e)}),(0,k.jsx)(`span`,{className:`text-[0.8125rem] text-ink-3 tabular-nums`,children:w(e)}),(0,k.jsx)(`span`,{className:`ml-auto text-[0.8125rem] text-ink-4`,children:R(e.commits.length)})]}),(0,k.jsx)(`div`,{className:`mt-2 pl-6`,children:(0,k.jsx)(S,{release:e})})]},e.version??`unreleased`))}):null,D===`credits`?(0,k.jsx)(F,{active:!0}):null]},D)]})}export{z 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{$t as r,An as i,Fn as a,Gt as o,Ht as s,Ut as c,Wt as l,Xt as u,an as d,rn as f,sn as p,tn as m}from"./ui-DTuixH4y.js";import{t as h}from"./preload-helper-CNEI7N0N.js";import{$ as g,H as _,V as v,gt as y}from"./index-D57rKyGN.js";import{c as b,f as x}from"./settings-menu-D-UqnL1p.js";import{ReleaseNotes as S,loadReleases as C,releaseDate as w,releaseLabel as T}from"./ChangelogDialog-Cbx3mN2p.js";var E=e(t(),1),D=[{name:`engenty wizards`,description:{de:`KI-Wizards aus einer Beschreibung, mit einem Link geteilt.`,en:`AI wizards built from a description, shared with a link.`},homepage:`https://github.com/engenty/wizards`,license:`FSL-1.1-MIT`},{name:`engenty`,description:{de:`Konnektoren, Datentabellen, Suche und die Engenties stammen aus dem engenty-Framework.`,en:`Connectors, data tables, search and the engenties come from the engenty framework.`},homepage:`https://github.com/engenty/engenty`,license:`FSL-1.1-MIT`}],O=[{name:`Mastra`,description:{de:`Agent-Framework hinter den KI-Schritten.`,en:`Agent framework behind the AI steps.`},homepage:`https://github.com/mastra-ai/mastra`,license:`Apache-2.0`},{name:`AI SDK`,description:{de:`Modell-Aufrufe für Text, Bild, Video und Sprache.`,en:`Model calls for text, image, video and speech.`},homepage:`https://github.com/vercel/ai`,license:`Apache-2.0`},{name:`Model Context Protocol`,description:{de:`MCP-Server zum Bauen von Wizards, MCP-Client für Werkzeuge der Schritte.`,en:`MCP server for building wizards, MCP client for the steps' tools.`},homepage:`https://github.com/modelcontextprotocol/typescript-sdk`,license:`MIT`},{name:`Hono`,description:{de:`HTTP-API der Runtime.`,en:`The runtime's HTTP API.`},homepage:`https://github.com/honojs/hono`,license:`MIT`},{name:`Drizzle ORM`,description:{de:`Schema und Migrationen der Datenbanken.`,en:`Schema and migrations of the databases.`},homepage:`https://github.com/drizzle-team/drizzle-orm`,license:`Apache-2.0`},{name:`libSQL`,description:{de:`Eine Datenbank je Mandant, lokal als Datei oder bei Turso.`,en:`One database per tenant, a local file or at Turso.`},homepage:`https://github.com/tursodatabase/libsql`,license:`MIT`},{name:`Playwright`,description:{de:`Browser-Schritte sowie PDF- und PNG-Export.`,en:`Browser steps and the PDF and PNG export.`},homepage:`https://github.com/microsoft/playwright`,license:`Apache-2.0`},{name:`agentOS`,description:{de:`Sandbox für Code-Schritte ohne Docker.`,en:`Sandbox for code steps without Docker.`},homepage:`https://github.com/rivet-dev/agentos`,license:`Apache-2.0`},{name:`React`,description:{de:`UI-Bibliothek der Web-App.`,en:`UI library of the web app.`},homepage:`https://github.com/facebook/react`,license:`MIT`},{name:`Vite`,description:{de:`Build-Werkzeug der Web-App.`,en:`Build tool of the web app.`},homepage:`https://github.com/vitejs/vite`,license:`MIT`},{name:`Tailwind CSS`,description:{de:`Styling der Oberfläche.`,en:`Styling of the interface.`},homepage:`https://github.com/tailwindlabs/tailwindcss`,license:`MIT`},{name:`React Flow`,description:{de:`Das Diagramm der Schritte im Editor.`,en:`The diagram of steps in the editor.`},homepage:`https://github.com/xyflow/xyflow`,license:`MIT`},{name:`TanStack Query`,description:{de:`Server-Daten in der Web-App.`,en:`Server data in the web app.`},homepage:`https://github.com/TanStack/query`,license:`MIT`},{name:`xterm.js`,description:{de:`Das Terminal auf der Seite, in dem sich KI-Clients anmelden.`,en:`The terminal on the page where AI clients sign in.`},homepage:`https://github.com/xtermjs/xterm.js`,license:`MIT`},{name:`Lucide`,description:{de:`Icons.`,en:`Icons.`},homepage:`https://github.com/lucide-icons/lucide`,license:`ISC`},{name:`Geist & Geist Mono`,description:{de:`Schrift der Oberfläche.`,en:`Typeface of the interface.`},homepage:`https://github.com/vercel/geist-font`,license:`OFL-1.1`},{name:`Space Grotesk`,description:{de:`Schrift der Überschriften.`,en:`Typeface of the headings.`},homepage:`https://github.com/floriankarsten/space-grotesk`,license:`OFL-1.1`},{name:`git-cliff`,description:{de:`Erzeugt den Changelog aus den Commits.`,en:`Generates the changelog from the commits.`},homepage:`https://github.com/orhun/git-cliff`,license:`MIT OR Apache-2.0`}],k=e(n(),1),A=(e,...t)=>t.some(t=>t?.toLowerCase().includes(e));function j({name:e,homepage:t,version:n,license:r,description:a}){return(0,k.jsxs)(`li`,{className:`py-2.5`,children:[(0,k.jsxs)(`div`,{className:`flex flex-wrap items-baseline gap-x-2.5 gap-y-0.5`,children:[t?(0,k.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,k.jsx)(i,{"aria-hidden":!0,className:`size-3 text-ink-4`})]}):(0,k.jsx)(`span`,{className:`font-medium text-[0.875rem] text-ink`,children:e}),n?(0,k.jsx)(`span`,{className:`text-[0.75rem] text-ink-3 tabular-nums`,children:n}):null,(0,k.jsx)(`span`,{className:`text-[0.75rem] text-ink-3`,children:r})]}),a?(0,k.jsx)(`p`,{className:`mt-0.5 text-[0.8125rem] text-ink-3`,children:a}):null]})}function M({title:e,hint:t,children:n}){return(0,k.jsxs)(`section`,{className:`mt-6`,children:[(0,k.jsx)(`h3`,{className:`border-border-soft border-b pb-2 font-display font-semibold text-[1rem]`,children:e}),t?(0,k.jsx)(`p`,{className:`mt-2 text-[0.8125rem] text-ink-3`,children:t}):null,n]})}function N({credits:e}){return(0,k.jsx)(`ul`,{className:`divide-y divide-border-soft`,children:e.map(e=>(0,k.jsx)(j,{name:e.name,homepage:e.homepage,license:e.license,description:e.description[f]},e.name))})}function P({label:e,packages:t}){return(0,k.jsxs)(`div`,{className:`mt-4`,children:[(0,k.jsx)(`h4`,{className:`font-medium text-[0.75rem] text-ink-3 tracking-wide`,children:e}),(0,k.jsx)(`ul`,{className:`divide-y divide-border-soft`,children:t.map(e=>(0,k.jsx)(j,{...e},e.name))})]})}function F({active:e}){let[t,n]=(0,E.useState)(null),[r,i]=(0,E.useState)(``);(0,E.useEffect)(()=>{if(!e||t)return;let r=!1;return h(()=>import(`./oss-credits-BQQX0kvk.js`).then(e=>{r||n(e.default)}),[]),()=>{r=!0}},[e,t]),(0,E.useEffect)(()=>{e||i(``)},[e]);let a=r.trim().toLowerCase(),s=e=>a?e.filter(e=>A(a,e.name,e.license,e.description.de,e.description.en)):e,l=e=>a?e.filter(e=>A(a,e.name,e.license,e.version)):e,f=s(D),p=s(O),m=t?[{id:`shared`,label:d(`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=f.length===0&&p.length===0&&m.length===0;return(0,k.jsxs)(k.Fragment,{children:[(0,k.jsx)(`p`,{className:`mb-4 text-[0.875rem] text-ink-3`,children:d(`about.creditsHint`)}),(0,k.jsx)(o,{type:`search`,value:r,placeholder:d(`about.search`),onChange:e=>i(e.target.value)}),f.length>0?(0,k.jsx)(M,{title:d(`about.creditsProduct`),children:(0,k.jsx)(N,{credits:f})}):null,p.length>0?(0,k.jsx)(M,{title:d(`about.creditsBuiltWith`),children:(0,k.jsx)(N,{credits:p})}):null,t===null?(0,k.jsx)(`div`,{className:`flex justify-center py-10 text-ink-3`,children:(0,k.jsx)(u,{})}):m.length>0?(0,k.jsx)(M,{title:d(`about.creditsDeps`),hint:a?void 0:d(`about.creditsDepsHint`),children:m.map(e=>(0,k.jsx)(P,{label:e.label,packages:e.packages},e.id))}):null,t!==null&&g?(0,k.jsx)(c,{children:d(`about.noMatches`)}):null]})}var I=[`new`,`earlier`,`credits`],L=e=>d(e===`new`?`about.tab.new`:e===`earlier`?`about.tab.earlier`:`about.credits`),R=e=>e===1?d(`about.change`):d(`about.changes`,{n:e});function z({open:e,onClose:t,me:n}){let i=n?.mode===`local`,{data:o,latest:c}=_(i&&e),[f,h]=(0,E.useState)(null),[D,O]=(0,E.useState)(`new`),A=x(I.indexOf(D)),j=o?.newer&&c?c:g;(0,E.useEffect)(()=>{if(!e)return;let t=!1;return h(null),C(j===`0.4.4`?void 0:j).then(e=>{t||h(e)}),()=>{t=!0}},[e,j]);let M=f?.find(e=>e.version===`v${j}`)??f?.[0]??null,N=f?.filter(e=>e!==M)??[],P=i?o?.kind??null:`cloud`;return(0,k.jsxs)(s,{open:e,onClose:t,wide:!0,height:`tall`,children:[(0,k.jsx)(`div`,{className:`-mt-2 -mr-2 sticky top-0 z-10 flex h-0 justify-end`,children:(0,k.jsx)(l,{label:d(`common.close`),onClick:t,children:(0,k.jsx)(p,{className:`size-4`})})}),(0,k.jsxs)(`div`,{className:`flex flex-col items-center pt-4 text-center`,children:[(0,k.jsx)(y,{size:96}),(0,k.jsxs)(`div`,{className:`mt-4 font-display font-semibold text-[1.5rem] tracking-tight`,children:[`engenty`,(0,k.jsx)(`span`,{className:`text-ember`,children:`.`}),(0,k.jsx)(`span`,{className:`ml-1.5 font-normal text-ink-3`,children:`wizards`})]}),(0,k.jsxs)(`div`,{className:`mt-1 text-[0.9375rem] text-ink-3 tabular-nums`,children:[d(`about.version`,{v:g}),P?` · ${d(`about.kind.${P}`)}`:``]})]}),i?(0,k.jsx)(`div`,{className:`mt-5`,children:(0,k.jsx)(v,{enabled:e})}):null,(0,k.jsx)(`div`,{className:`mt-6 flex justify-center`,children:(0,k.jsx)(r,{value:D,onChange:O,options:I.map(e=>({id:e,label:L(e)})),label:d(`about.title`)})}),(0,k.jsxs)(`div`,{className:m(`mt-5`,b(A)),children:[D===`new`?f===null?(0,k.jsx)(`div`,{className:`flex justify-center py-6 text-ink-3`,children:(0,k.jsx)(u,{})}):M?(0,k.jsxs)(k.Fragment,{children:[(0,k.jsxs)(`h3`,{className:`flex items-baseline gap-3 border-border-soft border-b pb-2 font-display font-semibold text-[1rem]`,children:[T(M),(0,k.jsx)(`span`,{className:`font-normal font-sans text-[0.8125rem] text-ink-3 tabular-nums`,children:w(M)})]}),(0,k.jsx)(`div`,{className:`mt-3`,children:(0,k.jsx)(S,{release:M})})]}):(0,k.jsx)(`p`,{className:`text-[0.875rem] text-ink-3`,children:d(`about.changelogEmpty`)}):null,D===`earlier`?(0,k.jsx)(`div`,{className:`flex flex-col divide-y divide-border-soft`,children:N.map(e=>(0,k.jsxs)(`details`,{className:`group/r py-2`,children:[(0,k.jsxs)(`summary`,{className:`flex cursor-pointer list-none items-baseline gap-3 text-[0.875rem] [&::-webkit-details-marker]:hidden`,children:[(0,k.jsx)(a,{className:`size-3.5 shrink-0 self-center text-ink-4 transition group-open/r:rotate-90`}),(0,k.jsx)(`span`,{className:`font-medium text-ink tabular-nums`,children:T(e)}),(0,k.jsx)(`span`,{className:`text-[0.8125rem] text-ink-3 tabular-nums`,children:w(e)}),(0,k.jsx)(`span`,{className:`ml-auto text-[0.8125rem] text-ink-4`,children:R(e.commits.length)})]}),(0,k.jsx)(`div`,{className:`mt-2 pl-6`,children:(0,k.jsx)(S,{release:e})})]},e.version??`unreleased`))}):null,D===`credits`?(0,k.jsx)(F,{active:!0}):null]},D)]})}export{z 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{Gt as r,Ht as i,Ut as a,Xt as o,an as s,tn as c}from"./ui-DTuixH4y.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/,``):s(`about.unreleased`),m=e=>e.timestamp?new Date(e.timestamp*1e3).toISOString().slice(0,10):``,h=e=>e.charAt(0).toUpperCase()+e.slice(1);function g(e,t){let n=t.trim().toLowerCase();if(!n)return e;let r=[];for(let t of e){if(`${p(t)} ${m(t)}`.toLowerCase().includes(n)){r.push(t);continue}let e=t.commits.filter(e=>`${f(e).label} ${e.scope??``} ${e.message}`.toLowerCase().includes(n));e.length>0&&r.push({...t,commits:e})}return r}async function _(e){if(e)try{let t=await fetch(`https://raw.githubusercontent.com/engenty/engenty-wizards/v${e}/apps/web/src/about/changelog.json`,{signal:AbortSignal.timeout(5e3)});if(t.ok)return await t.json()}catch{}return(await l(async()=>{let{default:e}=await import(`./changelog-
|
|
1
|
+
import{r as e}from"./rolldown-runtime-hePW80VL.js";import{n as t,t as n}from"./jsx-runtime-BNakU3Ej.js";import{Gt as r,Ht as i,Ut as a,Xt as o,an as s,tn as c}from"./ui-DTuixH4y.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/,``):s(`about.unreleased`),m=e=>e.timestamp?new Date(e.timestamp*1e3).toISOString().slice(0,10):``,h=e=>e.charAt(0).toUpperCase()+e.slice(1);function g(e,t){let n=t.trim().toLowerCase();if(!n)return e;let r=[];for(let t of e){if(`${p(t)} ${m(t)}`.toLowerCase().includes(n)){r.push(t);continue}let e=t.commits.filter(e=>`${f(e).label} ${e.scope??``} ${e.message}`.toLowerCase().includes(n));e.length>0&&r.push({...t,commits:e})}return r}async function _(e){if(e)try{let t=await fetch(`https://raw.githubusercontent.com/engenty/engenty-wizards/v${e}/apps/web/src/about/changelog.json`,{signal:AbortSignal.timeout(5e3)});if(t.ok)return await t.json()}catch{}return(await l(async()=>{let{default:e}=await import(`./changelog-CsxPWKJ-.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:c(`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:s(`about.breaking`)}):null]})]},e.id)})})}function y({open:e,onClose:t,version:n}){let[c,l]=(0,u.useState)(null),[f,h]=(0,u.useState)(``);(0,u.useEffect)(()=>{if(!e||c)return;let t=!1;return _(n).then(e=>{t||l(e)}),()=>{t=!0}},[e,c,n]),(0,u.useEffect)(()=>{e||h(``)},[e]);let y=c?g(c,f):[];return(0,d.jsxs)(i,{open:e,onClose:t,title:s(`about.changelog`),wide:!0,height:`tall`,children:[(0,d.jsx)(`p`,{className:`-mt-3 mb-4 text-[0.875rem] text-ink-3`,children:s(`about.changelogHint`)}),c===null?(0,d.jsx)(`div`,{className:`flex justify-center py-10 text-ink-3`,children:(0,d.jsx)(o,{})}):c.length===0?(0,d.jsx)(a,{children:s(`about.changelogEmpty`)}):(0,d.jsxs)(d.Fragment,{children:[(0,d.jsx)(r,{type:`search`,value:f,placeholder:s(`about.search`),onChange:e=>h(e.target.value)}),y.length===0?(0,d.jsx)(a,{children:s(`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};
|