wizards 0.1.11 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/README.md +3 -0
  2. package/apps/runtime/dist/agents/architect.js +6 -4
  3. package/apps/runtime/dist/app.js +38 -4
  4. package/apps/runtime/dist/auth/local-ticket.js +8 -5
  5. package/apps/runtime/dist/authoring/guide.js +10 -2
  6. package/apps/runtime/dist/cli/connect.js +531 -0
  7. package/apps/runtime/dist/cli/environment.js +8 -1
  8. package/apps/runtime/dist/cli/home.js +1 -0
  9. package/apps/runtime/dist/cli/main.js +17 -0
  10. package/apps/runtime/dist/cli/mcp.js +175 -0
  11. package/apps/runtime/dist/cli/setup.js +49 -0
  12. package/apps/runtime/dist/db/client.js +13 -0
  13. package/apps/runtime/dist/engine/runner.js +3 -0
  14. package/apps/runtime/dist/env.js +18 -4
  15. package/apps/runtime/dist/index.js +3 -0
  16. package/apps/runtime/dist/mcp/auth.js +26 -0
  17. package/apps/runtime/dist/mcp/flow-app.js +94 -0
  18. package/apps/runtime/dist/mcp/handler.js +15 -5
  19. package/apps/runtime/dist/mcp/seen.js +65 -0
  20. package/apps/runtime/dist/mcp/tools.js +158 -5
  21. package/apps/runtime/dist/plugins/discovery.js +144 -0
  22. package/apps/runtime/dist/plugins/events.js +40 -0
  23. package/apps/runtime/dist/plugins/loader.js +277 -0
  24. package/apps/runtime/dist/plugins/migrations.js +69 -0
  25. package/apps/runtime/dist/plugins/registry.js +105 -0
  26. package/apps/runtime/dist/routes/plugins.js +160 -0
  27. package/apps/runtime/dist/routes/runs.js +7 -3
  28. package/apps/runtime/dist/routes/studio.js +20 -0
  29. package/apps/runtime/dist/secrets/signing.js +21 -3
  30. package/apps/runtime/dist/services/runs.js +190 -28
  31. package/apps/runtime/dist/services/wizards.js +30 -4
  32. package/apps/runtime/dist/tools/index.js +2 -0
  33. package/apps/runtime/dist/tools/plugin.js +43 -0
  34. package/apps/web/dist/assets/ChangelogDialog-Co_9fs61.js +1 -0
  35. package/apps/web/dist/assets/ChatPanel-Br3nYkUc.js +1 -0
  36. package/apps/web/dist/assets/CreditsDialog-D3fZRqXH.js +1 -0
  37. package/apps/web/dist/assets/EditorPage-BSjQC6FE.js +2 -0
  38. package/apps/web/dist/assets/FlowPreview-CjUK5046.js +1 -0
  39. package/apps/web/dist/assets/HomePage-8v4HqIbP.js +1 -0
  40. package/apps/web/dist/assets/ImportWizard-DiV62Uno.js +1 -0
  41. package/apps/web/dist/assets/{LocalRuntime-DKqEzptp.js → LocalRuntime-CTyLGC98.js} +2 -2
  42. package/apps/web/dist/assets/NewWizardPage-BBL2yenw.js +2 -0
  43. package/apps/web/dist/assets/PublicRunner-BwtoEVlE.js +1 -0
  44. package/apps/web/dist/assets/RunnerView-DOizlTad.js +2 -0
  45. package/apps/web/dist/assets/SettingsPage-CBDv04YS.js +2 -0
  46. package/apps/web/dist/assets/SetupPage-CfXTOaIm.js +1 -0
  47. package/apps/web/dist/assets/SharePage-CgByqrAF.js +1 -0
  48. package/apps/web/dist/assets/api-KMi35npD.js +10 -0
  49. package/apps/web/dist/assets/{arrow-left-UpjOBfQp.js → arrow-left-hTN1VoH1.js} +1 -1
  50. package/apps/web/dist/assets/{arrow-right-CucQycEy.js → arrow-right-6FyN3e7D.js} +1 -1
  51. package/apps/web/dist/assets/changelog-03JgixQA.js +1 -0
  52. package/apps/web/dist/assets/check-CUpby_J9.js +1 -0
  53. package/apps/web/dist/assets/{createLucideIcon-DBu2Ovxj.js → createLucideIcon-q3MZMmnf.js} +1 -1
  54. package/apps/web/dist/assets/definition-DndaH5Ow.js +55 -0
  55. package/apps/web/dist/assets/engenty-Cm8yZLON.js +1 -0
  56. package/apps/web/dist/assets/esm-rJY59Ixx.js +7 -0
  57. package/apps/web/dist/assets/{external-link-CRg6DVdO.js → external-link-MAL7vJYj.js} +1 -1
  58. package/apps/web/dist/assets/{fluffy-engenty-CJ3wCQgT.js → fluffy-engenty-D7rFrNcJ.js} +2 -2
  59. package/apps/web/dist/assets/index-B9YVjvVY.css +1 -0
  60. package/apps/web/dist/assets/index-BmpnE7IO.js +10 -0
  61. package/apps/web/dist/assets/jsx-runtime-D3jfb0Ew.js +1 -0
  62. package/apps/web/dist/assets/meta-4R1Lnr25.js +1 -0
  63. package/apps/web/dist/assets/{mic-CeqQ89Dj.js → mic-Bvz4mA2j.js} +1 -1
  64. package/apps/web/dist/assets/oss-credits-roB62QJu.js +1 -0
  65. package/apps/web/dist/assets/{outputs-CHe6KRLc.js → outputs-COE_nvAw.js} +2 -2
  66. package/apps/web/dist/assets/plug-CLjyCoHB.js +1 -0
  67. package/apps/web/dist/assets/{plus-HA210wXA.js → plus-Du-Jx8aI.js} +1 -1
  68. package/apps/web/dist/assets/react-dom-DenslgJm.js +1 -0
  69. package/apps/web/dist/assets/session-B6-6VomQ.js +1 -0
  70. package/apps/web/dist/assets/type-Bj9H3eOK.js +1 -0
  71. package/apps/web/dist/assets/ui-Cs6lpsmj.js +1 -0
  72. package/apps/web/dist/assets/{upload-DsgLfQ3U.js → upload--J5bZhEM.js} +1 -1
  73. package/apps/web/dist/assets/{video-Dk1fuTDl.js → video-CjgmvRDk.js} +1 -1
  74. package/apps/web/dist/assets/{wand-sparkles-Bf97fs8T.js → wand-sparkles-CKxArWPY.js} +1 -1
  75. package/apps/web/dist/index.html +10 -7
  76. package/apps/web/dist/mcp/flow.html +748 -0
  77. package/modules/README.md +15 -0
  78. package/node_modules/@engenty-wizards/plugin-sdk/dist/index.d.ts +144 -0
  79. package/node_modules/@engenty-wizards/plugin-sdk/dist/index.js +4 -0
  80. package/node_modules/@engenty-wizards/plugin-sdk/dist/studio.d.ts +80 -0
  81. package/node_modules/@engenty-wizards/plugin-sdk/dist/studio.js +4 -0
  82. package/node_modules/@engenty-wizards/plugin-sdk/package.json +10 -0
  83. package/node_modules/@engenty-wizards/shared/dist/definition.d.ts +18 -6
  84. package/node_modules/@engenty-wizards/shared/dist/definition.js +22 -1
  85. package/package.json +6 -1
  86. package/plugin/.claude-plugin/plugin.json +1 -1
  87. package/plugin/skills/use-wizard/SKILL.md +18 -0
  88. package/apps/web/dist/assets/ChangelogDialog-DURK75KD.js +0 -1
  89. package/apps/web/dist/assets/ChatPanel-DZhZJZCP.js +0 -1
  90. package/apps/web/dist/assets/CreditsDialog-CLUOVBLG.js +0 -1
  91. package/apps/web/dist/assets/EditorPage-PGCEQdiC.js +0 -2
  92. package/apps/web/dist/assets/FlowPreview-CqJUNW13.js +0 -1
  93. package/apps/web/dist/assets/HomePage-CRkDMNM2.js +0 -1
  94. package/apps/web/dist/assets/ImportWizard-DX1YtFPv.js +0 -1
  95. package/apps/web/dist/assets/NewWizardPage-BPJrYi2m.js +0 -2
  96. package/apps/web/dist/assets/PublicRunner-B3T244Ry.js +0 -1
  97. package/apps/web/dist/assets/RunnerView-DgYaOHIH.js +0 -2
  98. package/apps/web/dist/assets/SettingsPage-LBVqEwXs.js +0 -2
  99. package/apps/web/dist/assets/SetupPage-DAyJs-hK.js +0 -1
  100. package/apps/web/dist/assets/SharePage-oKGavxJt.js +0 -1
  101. package/apps/web/dist/assets/api-DbPn6Dge.js +0 -10
  102. package/apps/web/dist/assets/changelog-BJkYErYt.js +0 -1
  103. package/apps/web/dist/assets/check-Bjx41xbB.js +0 -1
  104. package/apps/web/dist/assets/definition-cy7uvY5L.js +0 -55
  105. package/apps/web/dist/assets/engenty-CWIFpgKq.js +0 -1
  106. package/apps/web/dist/assets/esm-ZHEGGS6s.js +0 -7
  107. package/apps/web/dist/assets/index-BRy5sz4a.js +0 -10
  108. package/apps/web/dist/assets/index-CQ_q9iMO.css +0 -1
  109. package/apps/web/dist/assets/jsx-runtime-Dk72oS4N.js +0 -1
  110. package/apps/web/dist/assets/meta-2YMHa2hT.js +0 -1
  111. package/apps/web/dist/assets/oss-credits-cPuZUFHD.js +0 -1
  112. package/apps/web/dist/assets/type-BzYgVWKL.js +0 -1
  113. package/apps/web/dist/assets/ui-tO4ZXbfh.js +0 -1
@@ -1,15 +1,15 @@
1
1
  import { formatsFor } from "@engenty-wizards/shared/definition";
2
- import { and, count, desc, eq, inArray } from "drizzle-orm";
2
+ import { and, count, desc, eq, inArray, or } from "drizzle-orm";
3
3
  import { canSpend, MICROS_PER_CREDIT } from "../credits/credits.js";
4
4
  import { db, schema } from "../db/client.js";
5
- import { unattended } from "../engine/asks.js";
5
+ import { answerAsk, unattended } from "../engine/asks.js";
6
6
  import { emitEvent, recentEvents, subscribe } from "../engine/events.js";
7
- import { createRun, RunInputError, reviewStep, submitPage } from "../engine/runner.js";
7
+ import { cancel, createRun, goBack, NoCreditsError, RunConflict, RunInputError, retry, reviewStep, submitPage, } from "../engine/runner.js";
8
8
  import { ModelUnavailableError } from "../model-errors.js";
9
9
  import { signedUrl } from "../secrets/signing.js";
10
10
  import { currentTenant } from "../tenants/tenant.js";
11
11
  import { notFound, ServiceError } from "./errors.js";
12
- import { ownedWizard, requireClean, studioUrl } from "./wizards.js";
12
+ import { ownedWizard, requireClean, shareUrl, studioUrl } from "./wizards.js";
13
13
  /** Test runs an MCP client started with answers: pages are filled and reviews accepted for it. */
14
14
  const autopilots = new Set();
15
15
  export async function startTestRun(userId, wizardId, autopilot) {
@@ -95,9 +95,15 @@ function fly(runId, answers, acceptReviews) {
95
95
  const timeout = setTimeout(land, 30 * 60_000);
96
96
  void step();
97
97
  }
98
- async function ownedTestRun(_userId, runId) {
98
+ /**
99
+ * A run the caller may see: any test run of their tenant, or a run they started themselves
100
+ * from an MCP client (run_wizard). `test` narrows it to test runs.
101
+ */
102
+ async function ownedRun(userId, runId, only) {
99
103
  const run = await db.query.run.findFirst({
100
- where: and(eq(schema.run.id, runId), eq(schema.run.tenantId, currentTenant()), eq(schema.run.mode, "test")),
104
+ where: and(eq(schema.run.id, runId), eq(schema.run.tenantId, currentTenant()), only === "test"
105
+ ? eq(schema.run.mode, "test")
106
+ : or(eq(schema.run.mode, "test"), eq(schema.run.userId, userId))),
101
107
  });
102
108
  if (!run) {
103
109
  throw notFound();
@@ -108,9 +114,9 @@ async function ownedTestRun(_userId, runId) {
108
114
  function moving(run) {
109
115
  return run.status === "running" || (run.status === "waiting_input" && autopilots.has(run.id));
110
116
  }
111
- async function waitWhileMoving(userId, runId, seconds) {
117
+ async function waitWhileMoving(userId, runId, seconds, only) {
112
118
  const deadline = Date.now() + seconds * 1000;
113
- let run = await ownedTestRun(userId, runId);
119
+ let run = await ownedRun(userId, runId, only);
114
120
  while (moving(run) && Date.now() < deadline) {
115
121
  await new Promise((resolve) => {
116
122
  const unsubscribe = subscribe(runId, () => {
@@ -123,7 +129,7 @@ async function waitWhileMoving(userId, runId, seconds) {
123
129
  resolve();
124
130
  }, deadline - Date.now());
125
131
  });
126
- run = await ownedTestRun(userId, runId);
132
+ run = await ownedRun(userId, runId, only);
127
133
  }
128
134
  return run;
129
135
  }
@@ -139,9 +145,88 @@ function deliverableFormats(run, step) {
139
145
  .find((d) => d.from === step.id);
140
146
  return listed ? listed.formats.filter((f) => possible.includes(f)) : [];
141
147
  }
142
- /** What a test run did so far, for a client without the studio: outputs, links, cost, errors. */
143
- export async function testRunReport(userId, runId, waitSeconds = 0) {
144
- const run = await waitWhileMoving(userId, runId, waitSeconds);
148
+ /** Field kinds an MCP client can answer with plain values; the others need the run page. */
149
+ const CHAT_KINDS = new Set([
150
+ "text",
151
+ "textarea",
152
+ "number",
153
+ "select",
154
+ "multiselect",
155
+ "date",
156
+ "email",
157
+ "url",
158
+ "toggle",
159
+ "color",
160
+ "items",
161
+ "location",
162
+ ]);
163
+ /** Where the person can go on in a browser: the run page of a live run, the studio for a test. */
164
+ /** The live run's page — the wizard itself, never the studio. A test run has none. */
165
+ async function browserUrl(run) {
166
+ if (run.mode === "test") {
167
+ return null;
168
+ }
169
+ const w = await db.query.wizard.findFirst({ where: eq(schema.wizard.id, run.wizardId) });
170
+ return w ? `${shareUrl(w.shareToken)}/${run.id}` : null;
171
+ }
172
+ function waitingFor(run, current) {
173
+ if (run.status === "running" && run.ask) {
174
+ const ask = run.ask;
175
+ return ask.kind === "confirm"
176
+ ? {
177
+ ask: ask.id,
178
+ kind: ask.kind,
179
+ reason: ask.reason,
180
+ service: ask.service,
181
+ action: ask.action.summary,
182
+ input: clip(ask.input, 1500),
183
+ hint: "Ask the person, then answer_ask with allow or skip.",
184
+ }
185
+ : {
186
+ ask: ask.id,
187
+ kind: ask.kind,
188
+ reason: ask.reason,
189
+ site: ask.site.host,
190
+ hint: "A sign-in: the person types it on the run page (browserUrl).",
191
+ };
192
+ }
193
+ if (run.status !== "waiting_input" || !current) {
194
+ return undefined;
195
+ }
196
+ if (current.type === "page") {
197
+ return {
198
+ page: current.id,
199
+ title: current.title,
200
+ fields: current.fields.map((f) => ({
201
+ id: f.id,
202
+ label: f.label,
203
+ kind: f.kind,
204
+ required: Boolean(f.required),
205
+ options: f.options,
206
+ placeholder: f.placeholder,
207
+ help: f.help,
208
+ default: f.default,
209
+ value: run.state.values[f.id],
210
+ columns: f.columns,
211
+ // A file, a recording or a signature comes from the person's device: the run page.
212
+ inChat: CHAT_KINDS.has(f.kind),
213
+ })),
214
+ };
215
+ }
216
+ if (current.type === "review") {
217
+ return {
218
+ review: current.id,
219
+ title: current.title,
220
+ show: current.show,
221
+ editable: Boolean(current.edit),
222
+ hint: "Show the person the outputs, then review_step: accept, or regenerate one with a note.",
223
+ };
224
+ }
225
+ return undefined;
226
+ }
227
+ /** What a run did so far, for a client without the run page: outputs, links, cost, errors. */
228
+ export async function runReport(userId, runId, waitSeconds = 0, only) {
229
+ const run = await waitWhileMoving(userId, runId, waitSeconds, only);
145
230
  const current = run.definition.steps.find((s) => s.id === run.cursor) ?? null;
146
231
  const base = `/api/runs/${run.id}`;
147
232
  const outputs = run.definition.steps.flatMap((step) => {
@@ -167,25 +252,19 @@ export async function testRunReport(userId, runId, waitSeconds = 0) {
167
252
  },
168
253
  ];
169
254
  });
170
- const waitingFor = run.status === "waiting_input" && current?.type === "page"
171
- ? {
172
- page: current.id,
173
- fields: current.fields.map((f) => ({
174
- id: f.id,
175
- label: f.label,
176
- kind: f.kind,
177
- required: Boolean(f.required),
178
- options: f.options,
179
- })),
180
- }
181
- : run.status === "waiting_input" && current?.type === "review"
182
- ? { review: current.id, hint: "Accept or regenerate in the studio." }
183
- : undefined;
184
255
  return {
185
256
  runId: run.id,
257
+ wizardId: run.wizardId,
258
+ mode: run.mode,
186
259
  status: moving(run) ? "running" : run.status,
187
260
  step: current ? { id: current.id, type: current.type, title: current.title } : null,
188
- waitingFor,
261
+ /** Steps behind the run: answered pages and reviews, every step with a result, the end. */
262
+ passed: run.definition.steps
263
+ .filter((s) => run.state.history.includes(s.id) ||
264
+ run.state.outputs[s.id] ||
265
+ (run.status === "done" && s.id === run.cursor))
266
+ .map((s) => s.id),
267
+ waitingFor: waitingFor(run, current),
189
268
  error: run.error,
190
269
  credits: Math.ceil(run.costMicros / MICROS_PER_CREDIT),
191
270
  events: (await recentEvents(run.id, 0, 15)).map((e) => ({
@@ -194,9 +273,92 @@ export async function testRunReport(userId, runId, waitSeconds = 0) {
194
273
  message: e.message,
195
274
  })),
196
275
  outputs,
197
- studioUrl: studioUrl(run.wizardId),
276
+ browserUrl: await browserUrl(run),
277
+ ...(run.mode === "test" ? { studioUrl: studioUrl(run.wizardId) } : {}),
198
278
  };
199
279
  }
280
+ export const testRunReport = (userId, runId, waitSeconds = 0) => runReport(userId, runId, waitSeconds, "test");
281
+ /** A command the run refused, as a tool error the client can act on. */
282
+ function refusedInput(err) {
283
+ if (err instanceof RunInputError) {
284
+ throw new ServiceError("invalid", err.message, { fields: err.errors });
285
+ }
286
+ if (err instanceof RunConflict) {
287
+ throw new ServiceError("refused", err.message);
288
+ }
289
+ throw err;
290
+ }
291
+ /**
292
+ * Runs the published version of a wizard for the admin, from their own AI client: a live run
293
+ * that is theirs — no visitor, no share link needed. With answers the first page is filled.
294
+ */
295
+ export async function startRun(userId, wizardId, answers = {}) {
296
+ const w = await ownedWizard(userId, wizardId);
297
+ if (w.publishedVersion === null) {
298
+ throw new ServiceError("refused", "This wizard is not published yet. Publish it, or try the draft with start_test_run.");
299
+ }
300
+ const version = await db.query.wizardVersion.findFirst({
301
+ where: and(eq(schema.wizardVersion.wizardId, w.id), eq(schema.wizardVersion.version, w.publishedVersion)),
302
+ });
303
+ if (!version) {
304
+ throw notFound();
305
+ }
306
+ if (!(await canSpend())) {
307
+ throw new ServiceError("no_credits", "Dein Guthaben ist aufgebraucht.");
308
+ }
309
+ const runId = await createRun({
310
+ wizardId: w.id,
311
+ definition: version.definition,
312
+ files: version.files,
313
+ version: w.publishedVersion,
314
+ mode: "live",
315
+ userId,
316
+ }).catch((err) => {
317
+ if (err instanceof ModelUnavailableError) {
318
+ throw new ServiceError("refused", err.message);
319
+ }
320
+ if (err instanceof NoCreditsError) {
321
+ throw new ServiceError("no_credits", "Dein Guthaben ist aufgebraucht.");
322
+ }
323
+ throw err;
324
+ });
325
+ const first = version.definition.steps[0];
326
+ if (first?.type !== "page" || !Object.keys(answers).length) {
327
+ return { runId, refused: null };
328
+ }
329
+ const values = Object.fromEntries(first.fields.filter((f) => f.id in answers).map((f) => [f.id, answers[f.id]]));
330
+ // The run is there either way; what did not fit is answered again with answer_page.
331
+ try {
332
+ await submitPage(runId, first.id, values);
333
+ return { runId, refused: null };
334
+ }
335
+ catch (err) {
336
+ if (err instanceof RunInputError) {
337
+ return { runId, refused: err.errors };
338
+ }
339
+ throw err;
340
+ }
341
+ }
342
+ export async function answerRunPage(userId, runId, stepId, values) {
343
+ await ownedRun(userId, runId);
344
+ await submitPage(runId, stepId, values).catch(refusedInput);
345
+ }
346
+ export async function reviewRun(userId, runId, stepId, action) {
347
+ await ownedRun(userId, runId);
348
+ await reviewStep(runId, stepId, action).catch(refusedInput);
349
+ }
350
+ export async function controlRun(userId, runId, action) {
351
+ await ownedRun(userId, runId);
352
+ const command = { back: goBack, retry, cancel }[action];
353
+ await command(runId).catch(refusedInput);
354
+ }
355
+ /** Answers what a running step asks: allow a change in a connected account, or skip it. */
356
+ export async function answerRunAsk(userId, runId, askId, answer) {
357
+ await ownedRun(userId, runId);
358
+ if (!answerAsk(runId, askId, answer === "allow" ? { type: "done" } : { type: "skip" })) {
359
+ throw new ServiceError("refused", "This question is no longer open.");
360
+ }
361
+ }
200
362
  export async function listRuns(userId, wizardId) {
201
363
  const w = await ownedWizard(userId, wizardId);
202
364
  const rows = await db.query.run.findMany({
@@ -1,10 +1,11 @@
1
- import { validateWizard, wizardSchema, } from "@engenty-wizards/shared/definition";
1
+ import { pluginToolOf, validateWizard, wizardSchema, } from "@engenty-wizards/shared/definition";
2
2
  import { and, desc, eq, sql } from "drizzle-orm";
3
3
  import { nanoid } from "nanoid";
4
4
  import { applyOps, OpError } from "../authoring/ops.js";
5
5
  import { listConnectors } from "../connectors/external.js";
6
6
  import { db, schema } from "../db/client.js";
7
7
  import { env } from "../env.js";
8
+ import { pluginToolsOf } from "../plugins/registry.js";
8
9
  import { dropLinks, putLink } from "../tenants/control.js";
9
10
  import { currentTenant } from "../tenants/tenant.js";
10
11
  import { changedSteps, emitDraftChanged } from "./draft-events.js";
@@ -64,7 +65,7 @@ export async function listWizards(_userId, projectId) {
64
65
  });
65
66
  return rows.map(wizardSummary);
66
67
  }
67
- async function publishedVersion(w) {
68
+ export async function publishedVersion(w) {
68
69
  if (w.publishedVersion === null) {
69
70
  return null;
70
71
  }
@@ -98,9 +99,34 @@ async function connectorIssues(projectId, draft) {
98
99
  }
99
100
  return issues;
100
101
  }
101
- /** Everything wrong with a draft: its structure, its workspace files, its project's connectors. */
102
+ /** Tools of plugins the draft's steps list that this tenant does not have. */
103
+ async function pluginIssues(draft) {
104
+ const issues = [];
105
+ let known = null;
106
+ for (const step of draft.steps) {
107
+ for (const id of step.type === "agent" ? step.tools : []) {
108
+ const named = pluginToolOf(id);
109
+ if (!named) {
110
+ continue;
111
+ }
112
+ known ??= new Set((await pluginToolsOf(currentTenant())).map((tool) => tool.id));
113
+ if (!known.has(id)) {
114
+ issues.push({
115
+ stepId: step.id,
116
+ message: `Step "${step.id}": tool "${id}" is not available here — the plugin "${named.plugin}" is not installed or has no such tool.`,
117
+ });
118
+ }
119
+ }
120
+ }
121
+ return issues;
122
+ }
123
+ /** Everything wrong with a draft: its structure, its workspace files, its project's connectors and plugins. */
102
124
  async function checkDraft(projectId, draft, files) {
103
- return [...validateWizard(draft, files), ...(await connectorIssues(projectId, draft))];
125
+ return [
126
+ ...validateWizard(draft, files),
127
+ ...(await connectorIssues(projectId, draft)),
128
+ ...(await pluginIssues(draft)),
129
+ ];
104
130
  }
105
131
  /** Validator issues of the draft, checked against its workspace and the project's connectors. */
106
132
  export async function draftIssues(w) {
@@ -13,6 +13,7 @@ import { browserTools } from "./browser.js";
13
13
  import { connectorTools } from "./connector.js";
14
14
  import { mailTools } from "./mail.js";
15
15
  import { assertPublicUrl, safeFetch } from "./net-guard.js";
16
+ import { pluginTools } from "./plugin.js";
16
17
  import { projectTools } from "./project.js";
17
18
  import { clip, FileKeeper } from "./shared.js";
18
19
  import { storeTools } from "./store.js";
@@ -95,6 +96,7 @@ export async function buildStepTools(step, ctx, resolved, uploads) {
95
96
  Object.assign(tools, projectTools(ctx));
96
97
  Object.assign(tools, mailTools(step, ctx, files));
97
98
  Object.assign(tools, await connectorTools(step, ctx, files));
99
+ Object.assign(tools, await pluginTools(step, ctx));
98
100
  const sandbox = sandboxCapabilities();
99
101
  if (allowed.has("sandbox") && sandbox) {
100
102
  const timeoutMs = (seconds) => Math.min((seconds ?? 120) * 1000, 600_000);
@@ -0,0 +1,43 @@
1
+ import { pluginToolOf } from "@engenty-wizards/shared/definition";
2
+ import { createTool } from "@mastra/core/tools";
3
+ import { StepError } from "../engine/types.js";
4
+ import { loadedPlugin, pluginIdsOf } from "../plugins/registry.js";
5
+ import { attempt } from "./shared.js";
6
+ /** The model's name of a plugin's tool: `<plugin>_<tool>`, since a tool name takes no dot. */
7
+ export function pluginToolName(plugin, tool) {
8
+ return `${plugin.replaceAll("-", "_")}_${tool}`;
9
+ }
10
+ /**
11
+ * The tools of plugins a step allowlists as `<plugin>.<tool>`. A step that names a tool this
12
+ * tenant does not have fails: the wizard was written for a runtime with that plugin.
13
+ */
14
+ export async function pluginTools(step, ctx) {
15
+ const wanted = step.tools.map(pluginToolOf).filter((named) => named !== null);
16
+ if (!wanted.length) {
17
+ return {};
18
+ }
19
+ const tools = {};
20
+ const has = await pluginIdsOf(ctx.tenantId);
21
+ for (const { plugin, tool } of wanted) {
22
+ const found = has.has(plugin) ? loadedPlugin(plugin)?.tools.get(tool) : undefined;
23
+ if (!found) {
24
+ throw new StepError(`Dieser Schritt braucht das Werkzeug „${plugin}.${tool}“. Das Plugin „${plugin}“ ist hier nicht installiert.`);
25
+ }
26
+ const id = pluginToolName(plugin, tool);
27
+ tools[id] = createTool({
28
+ id,
29
+ description: found.description,
30
+ inputSchema: found.inputSchema,
31
+ execute: (input) => attempt(async () => found.execute(input, {
32
+ runId: ctx.runId,
33
+ stepId: ctx.stepId,
34
+ tenantId: ctx.tenantId,
35
+ project: { id: ctx.project.id, name: ctx.project.name },
36
+ wizard: { title: ctx.def.title },
37
+ signal: ctx.signal,
38
+ emit: (message) => ctx.emit("tool", message),
39
+ })),
40
+ });
41
+ }
42
+ return tools;
43
+ }
@@ -0,0 +1 @@
1
+ import{a as e,n as t,t as n}from"./jsx-runtime-D3jfb0Ew.js";import{t as r}from"./preload-helper-CNEI7N0N.js";import{a as i,d as a,h as o,i as s,s as c,y as l}from"./ui-Cs6lpsmj.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/,``):l(`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}function _({open:e,onClose:t}){let[n,_]=(0,u.useState)(null),[v,y]=(0,u.useState)(``);(0,u.useEffect)(()=>{if(!e||n)return;let t=!1;return r(()=>import(`./changelog-03JgixQA.js`).then(e=>{t||_(e.default)}),[]),()=>{t=!0}},[e,n]),(0,u.useEffect)(()=>{e||y(``)},[e]);let b=n?g(n,v):[];return(0,d.jsxs)(s,{open:e,onClose:t,title:l(`about.changelog`),wide:!0,children:[(0,d.jsx)(`p`,{className:`-mt-3 mb-4 text-[14px] text-ink-3`,children:l(`about.changelogHint`)}),n===null?(0,d.jsx)(`div`,{className:`flex justify-center py-10 text-ink-3`,children:(0,d.jsx)(a,{})}):n.length===0?(0,d.jsx)(i,{children:l(`about.changelogEmpty`)}):(0,d.jsxs)(d.Fragment,{children:[(0,d.jsx)(c,{type:`search`,value:v,placeholder:l(`about.search`),onChange:e=>y(e.target.value)}),b.length===0?(0,d.jsx)(i,{children:l(`about.noMatches`)}):null,b.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-[16px]`,children:[p(e),(0,d.jsx)(`span`,{className:`font-normal font-sans text-[13px] text-ink-3 tabular-nums`,children:m(e)})]}),(0,d.jsx)(`ul`,{className:`mt-3 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-[14px]`,children:[(0,d.jsx)(`span`,{className:o(`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-[12px] 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-[12px] text-rose`,children:l(`about.breaking`)}):null]})]},e.id)})})]},e.version??`unreleased`))]})]})}export{_ as ChangelogDialog};
@@ -0,0 +1 @@
1
+ import{a as e,n as t,t as n}from"./jsx-runtime-D3jfb0Ew.js";import{c as r,d as i,i as a,n as o,nt as s}from"./api-KMi35npD.js";import{t as c}from"./createLucideIcon-q3MZMmnf.js";import{t as l}from"./plug-CLjyCoHB.js";import{S as u,g as d,h as f,r as p,v as m}from"./outputs-COE_nvAw.js";import{r as h}from"./wand-sparkles-CKxArWPY.js";import{t as g}from"./plus-Du-Jx8aI.js";import{t as _}from"./mic-Bvz4mA2j.js";import{_ as v,d as y,h as b,o as x,y as S}from"./ui-Cs6lpsmj.js";import{l as C}from"./index-BmpnE7IO.js";var w={name:`chevron-up`,size:24,node:[[`path`,{d:`m18 15-6-6-6 6`,key:`153udz`}]]};w.node;var T=c(w),E=e(t(),1),D=!1;function O(){if(!(typeof window>`u`||D))return window.SpeechRecognition??window.webkitSpeechRecognition}var k=typeof navigator<`u`&&!!navigator.mediaDevices?.getUserMedia&&typeof MediaRecorder<`u`;function A(e,t){let n=e.trimEnd(),r=t.trim();return n&&r?`${n} ${r}`:n||r}function j(e,t){let[n,r]=(0,E.useState)(!1),[i,a]=(0,E.useState)(!1),[s,c]=(0,E.useState)(null),l=(0,E.useRef)(e);l.current=e;let u=(0,E.useRef)(t);u.current=t;let d=(0,E.useRef)(null);(0,E.useEffect)(()=>()=>d.current?.stop(),[]);let f=async()=>{let e=l.current,t;try{t=await navigator.mediaDevices.getUserMedia({audio:!0})}catch{c(`denied`);return}let n=[],i=new MediaRecorder(t);i.ondataavailable=e=>e.data.size&&n.push(e.data),i.onstop=async()=>{for(let e of t.getTracks())e.stop();d.current=null,r(!1);let s=(i.mimeType||`audio/webm`).split(`;`)[0],l=new File(n,`dictation`,{type:s});if(l.size){a(!0);try{let{text:t}=await o.upload(`/api/studio/transcribe`,l);t.trim()&&u.current(A(e,t))}catch(e){c(e.message)}finally{a(!1)}}},d.current={stop:()=>i.state!==`inactive`&&i.stop()},i.start(),r(!0)},p=e=>{let t=l.current,n=``,i=new e;i.continuous=!0,i.interimResults=!0,i.lang=v===`de`?`de-DE`:`en-US`,i.onresult=e=>{let r=``;for(let t=Math.max(0,e.resultIndex);t<e.results.length;t++){let i=e.results[t],a=i[0]?.transcript??``;i.isFinal?n=A(n,a):r+=a}u.current(A(t,A(n,r)))},i.onerror=e=>{if(e.error!==`aborted`&&e.error!==`no-speech`){if(e.error===`not-allowed`){c(`denied`);return}D=!0,k&&!n?(i.onend=null,d.current=null,f()):c(e.error)}},i.onend=()=>{d.current=null,r(!1)},d.current={stop:()=>i.stop()},i.start(),r(!0)};return{supported:!!O()||k,listening:n,processing:i,error:s,toggle:()=>{if(i)return;if(d.current){d.current.stop();return}c(null);let e=O();e?p(e):f()}}}var M=e(n(),1),N=/\.(png|jpe?g|gif|webp|avif|svg)$/i;function P(e){let t=e.filter(e=>e.type.startsWith(`image/`));return t.length?Object.fromEntries(t.map(e=>[e.name,URL.createObjectURL(e)])):void 0}function F(e){let t=e.match(/(?:^|\n\n)(?:Angehängt|Attached): ([^\n]+)$/);return t?{text:e.slice(0,t.index).trim(),names:t[1].split(`, `).filter(Boolean)}:{text:e,names:[]}}function I(e,t){if(e.name&&e.name!==`image.png`)return e;let n=e.type.split(`/`)[1]?.replace(`jpeg`,`jpg`)||`png`,r=new Date().toISOString().slice(0,19).replace(/[-:]/g,``).replace(`T`,`-`);return new File([e],`paste-${r}${t?`-${t+1}`:``}.${n}`,{type:e.type})}function L({url:e,label:t,children:n}){return(0,M.jsxs)(`div`,{className:`group relative`,title:t,children:[(0,M.jsx)(`img`,{src:e,alt:t,width:64,height:64,className:`size-16 rounded-lg bg-paper-2 object-cover ring-1 ring-border`}),n]})}function R({names:e,previews:t,fileUrl:n}){let r=e.filter(e=>t?.[e]||n&&N.test(e)),i=e.filter(e=>!r.includes(e));return(0,M.jsxs)(`div`,{className:`flex flex-col items-end gap-1.5`,children:[r.map(e=>{let r=t?.[e]??n?.(e)??``;return(0,M.jsx)(`a`,{href:r,target:`_blank`,rel:`noreferrer`,title:e,children:(0,M.jsx)(`img`,{src:r,alt:e,className:`max-h-60 w-auto max-w-full rounded-xl bg-paper-2 object-contain ring-1 ring-border`})},e)}),i.length?(0,M.jsx)(`div`,{className:`flex flex-wrap justify-end gap-1.5`,children:i.map(e=>(0,M.jsxs)(`span`,{className:`inline-flex max-w-full items-center gap-1.5 rounded-full bg-paper-2 px-2.5 py-1 text-[12px] text-ink-2`,children:[(0,M.jsx)(m,{className:`size-3 shrink-0 text-ink-4`}),(0,M.jsx)(`span`,{className:`truncate`,children:e})]},e))}):null]})}function z(e,t){let n=s(),[i,o]=(0,E.useState)([]),[c,l]=(0,E.useState)(`idle`),[u,d]=(0,E.useState)(null),[f,p]=(0,E.useState)(null),[m,h]=(0,E.useState)(null),[g,_]=(0,E.useState)(null),v=(0,E.useRef)(null);return(0,E.useEffect)(()=>{if(e){if(v.current!==e.id){v.current=e.id,o(e.messages);return}c===`idle`&&o(t=>e.messages.length>=t.length?e.messages:t)}},[e,c]),{messages:i,phase:c,activity:f,thought:m,startedAt:g,error:u,send:async(i,s=[])=>{if(!e||!(i.trim()||s.length)||c!==`idle`)return!1;if(d(null),_(Date.now()),h(null),s.length){l(`thinking`);try{for(let t of s){let n=await fetch(r(`/api/studio/wizards/${e.id}/files/${encodeURIComponent(t.name)}`),{method:`PUT`,credentials:`include`,headers:{"content-type":t.type||`application/octet-stream`},body:t});if(!n.ok){let e=await n.json().catch(()=>({}));throw Error(e.error??n.statusText)}}}catch(t){return d(t.message),l(`idle`),_(null),await n.invalidateQueries({queryKey:[`wizard`,e.id]}),!1}let t=`${S(`editor.attached`)}: ${s.map(e=>e.name).join(`, `)}`;i=i.trim()?`${i}\n\n${t}`:t}let u=`p-${Date.now()}`;o(e=>[...e,{id:`u-${Date.now()}`,role:`user`,content:i,previews:P(s)},{id:u,role:`assistant`,content:``,pending:!0}]),l(`thinking`),p(null);let f=!1;try{await a(`/api/studio/wizards/${e.id}/chat`,{message:i},(e,n)=>{if(e===`text`){let e=JSON.parse(n);o(t=>t.map(t=>t.id===u?{...t,content:t.content+e}:t))}else if(e===`activity`)p(JSON.parse(n));else if(e===`thought`)h(JSON.parse(n));else if(e===`building`)l(`building`);else if(e===`done`){f=!0;let e=JSON.parse(n);o(t=>t.map(t=>t.id===u?{...t,content:e.reply,changed:e.changed,pending:!1}:t)),e.changed&&t(e.draft,e.revision)}else e===`error`&&(f=!0,d(JSON.parse(n).message))}),f||d(S(`editor.chatBroken`))}catch(e){d(e.message)}finally{p(null),h(null),_(null),o(e=>e.filter(e=>!(e.id===u&&!e.content)).map(e=>({...e,pending:!1}))),l(`idle`),await n.invalidateQueries({queryKey:[`wizard`,e.id]}),await n.invalidateQueries({queryKey:[`me`]})}return!0},fileUrl:t=>r(`/api/studio/wizards/${e?.id}/files/${encodeURIComponent(t)}`)}}function B(e){let[t,n]=(0,E.useState)(()=>Date.now());return(0,E.useEffect)(()=>{if(e===null)return;n(Date.now());let t=setInterval(()=>n(Date.now()),1e3);return()=>clearInterval(t)},[e]),e===null?null:Math.max(0,Math.floor((t-e)/1e3))}function V(e){return e<60?`${e} s`:`${Math.floor(e/60)}:${String(e%60).padStart(2,`0`)} min`}function H({chat:e,thought:t,className:n,spinner:r=`size-3.5`}){let i=B(e.startedAt);return(0,M.jsxs)(`div`,{className:b(`min-w-0`,n),children:[(0,M.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2`,children:[(0,M.jsx)(y,{className:b(`shrink-0`,r)}),(0,M.jsx)(`span`,{className:`min-w-0 truncate`,children:e.activity??(e.phase===`building`?S(`editor.building`):S(`editor.thinking`))}),i===null?null:(0,M.jsx)(`span`,{className:`shrink-0 text-ink-4 tabular-nums`,children:V(i)})]}),t&&e.thought?(0,M.jsx)(`p`,{className:`mt-0.5 truncate pl-5.5 text-[12px] text-ink-4`,children:e.thought}):null]})}function U({message:e,fileUrl:t}){let{text:n,names:r}=F(e.content);return(0,M.jsxs)(`div`,{className:`ml-8 flex flex-col items-end gap-1.5 self-end`,children:[r.length?(0,M.jsx)(R,{names:r,previews:e.previews,fileUrl:t}):null,n?(0,M.jsx)(`div`,{className:`rounded-xl rounded-br-md bg-paper-2 px-4 py-2.5 text-[14px] leading-relaxed`,children:(0,M.jsx)(`span`,{className:`whitespace-pre-wrap`,children:n})}):null]})}function W({chat:e,avatar:t,hello:n=S(`editor.chatHello`),placeholder:r=S(`editor.composer`),changedLabel:a=`Ablauf aktualisiert`,compact:o,intro:s}){let[c,v]=(0,E.useState)(!1),[w,D]=(0,E.useState)(``),[O,k]=(0,E.useState)([]),A=(0,E.useRef)(null),N=(0,E.useRef)(null),P=(0,E.useRef)(null),F=j(w,D);(0,E.useEffect)(()=>{A.current?.scrollTo({top:A.current.scrollHeight,behavior:`smooth`})},[e.messages,e.phase]),(0,E.useEffect)(()=>{let e=N.current;e&&(e.style.height=`auto`,e.style.height=`${Math.min(e.scrollHeight,200)}px`)},[w]);let R=(w.trim()||O.length>0)&&e.phase===`idle`,z=e=>k(t=>[...t.filter(t=>!e.some(e=>e.name===t.name)),...e]),[B,V]=(0,E.useState)(new Map);(0,E.useEffect)(()=>{let e=new Map(O.filter(e=>e.type.startsWith(`image/`)).map(e=>[e,URL.createObjectURL(e)]));return V(e),()=>{for(let t of e.values())URL.revokeObjectURL(t)}},[O]);let W=async()=>{if(!R)return;F.listening&&F.toggle();let t={text:w,files:O};D(``),k([]),await e.send(t.text.trim(),t.files)||(D(t.text),k(t.files))},G=!o||c,K=e.messages.at(-1);return(0,M.jsxs)(`div`,{className:`flex h-full min-h-0 flex-col`,children:[o&&(K||e.error)?(0,M.jsxs)(`button`,{type:`button`,"aria-expanded":c,"aria-label":S(`editor.thread`),onClick:()=>v(e=>!e),className:`flex items-center gap-2 px-4 pt-2.5 pb-0.5 text-left text-[13px] text-ink-3 transition hover:text-ink`,children:[!(e.error||c)&&K?.pending?(0,M.jsx)(H,{chat:e,className:`flex-1`}):(0,M.jsx)(`span`,{className:b(`min-w-0 flex-1 truncate`,e.error&&`text-rose`),children:e.error??(c?S(`editor.thread`):K?.content)}),c?(0,M.jsx)(h,{className:`size-4 shrink-0`}):(0,M.jsx)(T,{className:`size-4 shrink-0`})]}):null,(0,M.jsxs)(`div`,{ref:A,className:b(`min-h-0 flex-1 overflow-y-auto px-5 py-5`,!G&&`hidden`),children:[e.messages.length===0?s??(0,M.jsxs)(`div`,{className:`flex items-start gap-3`,children:[(0,M.jsx)(i,{kind:t,size:32,interactive:!1}),(0,M.jsx)(`p`,{className:`pt-1 text-[14px] text-ink-2 leading-relaxed`,children:n})]}):null,(0,M.jsx)(`div`,{className:`flex flex-col gap-4`,children:e.messages.map(n=>n.role===`user`?(0,M.jsx)(U,{message:n,fileUrl:e.fileUrl},n.id):(0,M.jsxs)(`div`,{className:`flex items-start gap-3`,children:[(0,M.jsx)(`div`,{className:`shrink-0`,children:(0,M.jsx)(i,{kind:t,size:28,interactive:!1})}),(0,M.jsxs)(`div`,{className:`min-w-0 pt-0.5 text-[14px]`,children:[n.source===`mcp`?(0,M.jsxs)(`div`,{className:`mb-1 inline-flex items-center gap-1.5 text-[12px] text-ink-3`,children:[(0,M.jsx)(l,{className:`size-3`}),` `,S(`editor.viaClient`,{client:n.client??`MCP`})]}):null,n.content?(0,M.jsx)(p,{text:n.content,className:`text-[14px]`}):null,n.pending?(0,M.jsx)(H,{chat:e,thought:!0,className:`mt-1 text-[13px] text-ink-3`}):null,n.changed?(0,M.jsxs)(`div`,{className:`mt-2 inline-flex items-center gap-1.5 rounded-full bg-ember-tint px-2.5 py-0.5 text-[12px] text-ember-strong`,children:[(0,M.jsx)(d,{className:`size-3`}),` `,a]}):null]})]},n.id))}),e.error?(0,M.jsx)(`div`,{className:`mt-4 rounded-lg bg-rose-tint px-3 py-2 text-[13px] text-rose`,children:e.error}):null]}),(0,M.jsxs)(`div`,{className:o?`p-2`:`p-3`,children:[(0,M.jsxs)(`div`,{className:`rounded-xl bg-card p-1.5 shadow-soft ring-1 ring-border focus-within:ring-focus`,children:[B.size?(0,M.jsx)(`div`,{className:`flex flex-wrap gap-2 px-1 pt-1 pb-1.5`,children:O.map((e,t)=>{let n=B.get(e);return n?(0,M.jsx)(L,{url:n,label:e.name,children:(0,M.jsx)(`button`,{type:`button`,"aria-label":S(`editor.detach`),onClick:()=>k(e=>e.filter((e,n)=>n!==t)),className:`-top-1.5 -right-1.5 absolute flex size-5 items-center justify-center rounded-full bg-ink text-paper opacity-0 shadow-soft transition focus-visible:opacity-100 group-hover:opacity-100`,children:(0,M.jsx)(C,{className:`size-3`})})},`${e.name}-${t}`):null})}):null,O.some(e=>!B.has(e))?(0,M.jsx)(`ul`,{className:`flex flex-wrap gap-1.5 px-1 pt-1 pb-1.5`,children:O.map((e,t)=>B.has(e)?null:(0,M.jsxs)(`li`,{className:`inline-flex max-w-full items-center gap-1.5 rounded-full bg-paper-2 py-1 pr-1 pl-2.5 text-[12px] text-ink-2`,children:[(0,M.jsx)(m,{className:`size-3 shrink-0 text-ink-4`}),(0,M.jsx)(`span`,{className:`truncate`,children:e.name}),(0,M.jsx)(`button`,{type:`button`,"aria-label":S(`editor.detach`),onClick:()=>k(e=>e.filter((e,n)=>n!==t)),className:`inline-flex size-5 shrink-0 items-center justify-center rounded-full text-ink-3 hover:bg-paper-3 hover:text-ink`,children:(0,M.jsx)(C,{className:`size-3`})})]},`${e.name}-${t}`))}):null,(0,M.jsxs)(`div`,{className:`flex items-end gap-1`,children:[(0,M.jsx)(x,{label:S(`editor.attach`),onClick:()=>P.current?.click(),children:(0,M.jsx)(g,{className:`size-4`})}),(0,M.jsx)(`input`,{ref:P,type:`file`,multiple:!0,hidden:!0,onChange:e=>{let t=Array.from(e.target.files??[]);e.target.value=``,z(t)}}),(0,M.jsx)(`textarea`,{ref:N,rows:1,value:w,onChange:e=>D(e.target.value),onPaste:e=>{let t=Array.from(e.clipboardData.files);t.length&&(e.preventDefault(),z(t.map(I)))},onKeyDown:e=>{e.key===`Enter`&&!e.shiftKey&&(e.preventDefault(),W())},placeholder:F.listening?S(`editor.listening`):r,className:`max-h-[200px] min-h-[40px] flex-1 resize-none bg-transparent px-1.5 py-2 text-[14px] outline-none placeholder:text-ink-4`}),F.supported?(0,M.jsx)(x,{label:F.processing?S(`editor.transcribing`):F.listening?S(`editor.voiceStop`):S(`editor.voice`),"aria-pressed":F.listening,disabled:F.processing,onClick:F.toggle,className:b(F.listening&&`bg-rose-tint text-rose hover:bg-rose-tint hover:text-rose`),children:F.processing?(0,M.jsx)(y,{className:`size-4`}):F.listening?(0,M.jsx)(f,{className:`size-3.5 animate-pulse-dot fill-current`}):(0,M.jsx)(_,{className:`size-4`})}):null,(0,M.jsx)(`button`,{type:`button`,onClick:()=>void W(),disabled:!R,className:b(`inline-flex size-9 shrink-0 items-center justify-center rounded-full transition`,R?`bg-primary text-primary-foreground`:`bg-paper-2 text-ink-4`),"aria-label":`Send`,children:(0,M.jsx)(u,{className:`size-4`})})]})]}),F.error?(0,M.jsx)(`p`,{className:`mt-2 px-1 text-[12px] text-rose`,children:F.error===`denied`?S(`editor.micDenied`):F.error}):null]})]})}export{z as i,H as n,P as r,W as t};
@@ -0,0 +1 @@
1
+ import{a as e,n as t,t as n}from"./jsx-runtime-D3jfb0Ew.js";import{t as r}from"./preload-helper-CNEI7N0N.js";import{t as i}from"./external-link-MAL7vJYj.js";import{_ as a,a as o,d as s,i as c,s as l,y as u}from"./ui-Cs6lpsmj.js";var d=e(t(),1),f=[{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/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`}],p=[{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`}],m=e(n(),1),h=(e,...t)=>t.some(t=>t?.toLowerCase().includes(e));function g({name:e,homepage:t,version:n,license:r,description:a}){return(0,m.jsxs)(`li`,{className:`py-2.5`,children:[(0,m.jsxs)(`div`,{className:`flex flex-wrap items-baseline gap-x-2.5 gap-y-0.5`,children:[t?(0,m.jsxs)(`a`,{href:t,target:`_blank`,rel:`noreferrer`,className:`inline-flex items-center gap-1 font-medium text-[14px] text-ink underline-offset-4 hover:underline`,children:[e,(0,m.jsx)(i,{"aria-hidden":!0,className:`size-3 text-ink-4`})]}):(0,m.jsx)(`span`,{className:`font-medium text-[14px] text-ink`,children:e}),n?(0,m.jsx)(`span`,{className:`text-[12px] text-ink-3 tabular-nums`,children:n}):null,(0,m.jsx)(`span`,{className:`text-[12px] text-ink-3`,children:r})]}),a?(0,m.jsx)(`p`,{className:`mt-0.5 text-[13px] text-ink-3`,children:a}):null]})}function _({title:e,hint:t,children:n}){return(0,m.jsxs)(`section`,{className:`mt-6`,children:[(0,m.jsx)(`h3`,{className:`border-border-soft border-b pb-2 font-display font-semibold text-[16px]`,children:e}),t?(0,m.jsx)(`p`,{className:`mt-2 text-[13px] text-ink-3`,children:t}):null,n]})}function v({credits:e}){return(0,m.jsx)(`ul`,{className:`divide-y divide-border-soft`,children:e.map(e=>(0,m.jsx)(g,{name:e.name,homepage:e.homepage,license:e.license,description:e.description[a]},e.name))})}function y({label:e,packages:t}){return(0,m.jsxs)(`div`,{className:`mt-4`,children:[(0,m.jsx)(`h4`,{className:`font-medium text-[12px] text-ink-3 tracking-wide`,children:e}),(0,m.jsx)(`ul`,{className:`divide-y divide-border-soft`,children:t.map(e=>(0,m.jsx)(g,{...e},e.name))})]})}function b({open:e,onClose:t}){let[n,i]=(0,d.useState)(null),[a,g]=(0,d.useState)(``);(0,d.useEffect)(()=>{if(!e||n)return;let t=!1;return r(()=>import(`./oss-credits-roB62QJu.js`).then(e=>{t||i(e.default)}),[]),()=>{t=!0}},[e,n]),(0,d.useEffect)(()=>{e||g(``)},[e]);let b=a.trim().toLowerCase(),x=e=>b?e.filter(e=>h(b,e.name,e.license,e.description.de,e.description.en)):e,S=e=>b?e.filter(e=>h(b,e.name,e.license,e.version)):e,C=x(f),w=x(p),T=n?[{id:`shared`,label:u(`about.creditsShared`),packages:S(n.shared)},...n.groups.map(e=>({id:e.id,label:e.label,packages:S(e.packages)}))].filter(e=>e.packages.length>0):[],E=C.length===0&&w.length===0&&T.length===0;return(0,m.jsxs)(c,{open:e,onClose:t,title:u(`about.credits`),wide:!0,children:[(0,m.jsx)(`p`,{className:`-mt-3 mb-4 text-[14px] text-ink-3`,children:u(`about.creditsHint`)}),(0,m.jsx)(l,{type:`search`,value:a,placeholder:u(`about.search`),onChange:e=>g(e.target.value)}),C.length>0?(0,m.jsx)(_,{title:u(`about.creditsProduct`),children:(0,m.jsx)(v,{credits:C})}):null,w.length>0?(0,m.jsx)(_,{title:u(`about.creditsBuiltWith`),children:(0,m.jsx)(v,{credits:w})}):null,n===null?(0,m.jsx)(`div`,{className:`flex justify-center py-10 text-ink-3`,children:(0,m.jsx)(s,{})}):T.length>0?(0,m.jsx)(_,{title:u(`about.creditsDeps`),hint:b?void 0:u(`about.creditsDepsHint`),children:T.map(e=>(0,m.jsx)(y,{label:e.label,packages:e.packages},e.id))}):null,n!==null&&E?(0,m.jsx)(o,{children:u(`about.noMatches`)}):null]})}export{b as CreditsDialog};