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
package/README.md CHANGED
@@ -177,6 +177,9 @@ More settings: [.env.example](.env.example).
177
177
  Development server, checks, how it is built, the managed mode:
178
178
  [docs/development.md](docs/development.md).
179
179
 
180
+ Plugins add tools for steps, routes with their own tables and pages in the studio, without
181
+ changing the runtime: [docs/plugins.md](docs/plugins.md).
182
+
180
183
  ## License
181
184
 
182
185
  [FSL-1.1-MIT](LICENSE): use it for yourself and your company, run it, change it — just don't
@@ -3,20 +3,22 @@ import { isTextMime } from "@engenty-wizards/shared/workspace";
3
3
  import { Agent } from "@mastra/core/agent";
4
4
  import { createTool } from "@mastra/core/tools";
5
5
  import { z } from "zod";
6
- import { connectorsLine, exampleWizard, mcpServersLine, PRINCIPLES, SCHEMA_DOC, WIDGET_GUIDE, } from "../authoring/guide.js";
6
+ import { connectorsLine, exampleWizard, mcpServersLine, PRINCIPLES, pluginToolsLine, SCHEMA_DOC, WIDGET_GUIDE, } from "../authoring/guide.js";
7
7
  import { wizardOpSchema } from "../authoring/ops.js";
8
8
  import { importFromRegistry, listConnectors, searchRegistry } from "../connectors/external.js";
9
9
  import { attachTools, costOf, gatewayTools, textModel } from "../models.js";
10
+ import { pluginToolsOf } from "../plugins/registry.js";
10
11
  import { htmlToMarkdown } from "../render/convert.js";
11
12
  import { ServiceError } from "../services/errors.js";
12
13
  import { deleteFile, listFiles, readFileText, writeFile } from "../services/files.js";
13
14
  import { checkDraftWidget } from "../services/widgets.js";
14
15
  import { editWizard, ownedWizard, writeDraft } from "../services/wizards.js";
16
+ import { currentTenant } from "../tenants/tenant.js";
15
17
  import { assertPublicUrl, safeFetch } from "../tools/net-guard.js";
16
18
  import { runScript } from "../widgets/script.js";
17
19
  import { thoughtLine } from "./thought.js";
18
20
  const MAX_STEPS = 45;
19
- function systemPrompt(mcp, connectors) {
21
+ function systemPrompt(mcp, connectors, pluginTools) {
20
22
  return `You design wizards for "engenty wizards": a page-by-page flow an end user walks through, where AI steps research, write, draw images, render video, build documents, dashboards and interactive widgets, or write into other systems.
21
23
 
22
24
  You talk to the ADMIN who builds the wizard. Answer in the admin's language, briefly and warmly. Never mention JSON, ids, schemas, files or templates to them — talk about pages, questions, steps, the widget and results.
@@ -27,7 +29,7 @@ ${WIDGET_GUIDE}
27
29
 
28
30
  ${mcpServersLine(mcp)}
29
31
 
30
- ${connectorsLine(connectors)}
32
+ ${connectorsLine(connectors)}${pluginToolsLine(pluginTools)}
31
33
 
32
34
  Example of a complete wizard:
33
35
  \`\`\`json
@@ -341,7 +343,7 @@ export async function runArchitect(input) {
341
343
  // The system prompt is long and identical on every turn: cache it.
342
344
  instructions: {
343
345
  role: "system",
344
- content: systemPrompt(input.mcpServers, input.connectors),
346
+ content: systemPrompt(input.mcpServers, input.connectors, await pluginToolsOf(currentTenant())),
345
347
  providerOptions: { anthropic: { cacheControl: { type: "ephemeral" } } },
346
348
  },
347
349
  model: resolved.model,
@@ -18,9 +18,11 @@ import { PLUGIN_NAME, pluginArchive, pluginMarketplace } from "./plugin.js";
18
18
  import { apiRoutes } from "./routes/api.js";
19
19
  import { internalRoutes } from "./routes/internal.js";
20
20
  import { marketplaceStudio } from "./routes/marketplace.js";
21
+ import { pluginRoutes } from "./routes/plugins.js";
21
22
  import { connectCallback, publicRoutes, runRoutes, shareRoutes } from "./routes/runs.js";
22
23
  import { studio } from "./routes/studio.js";
23
24
  import { wizardStream } from "./routes/wizard-stream.js";
25
+ import { runTicketValid } from "./secrets/signing.js";
24
26
  import { ServiceError } from "./services/errors.js";
25
27
  import { tenantOfLink, tenantStatus } from "./tenants/control.js";
26
28
  const app = new Hono();
@@ -44,14 +46,45 @@ app.onError((err, c) => {
44
46
  const ownHosts = new Set([new URL(env.appUrl).host, ...env.allowedHosts]);
45
47
  const loopback = /^(localhost|127\.0\.0\.1|\[::1\])(:\d+)?$/;
46
48
  const hostAllowed = (host) => ownHosts.has(host) || loopback.test(host);
49
+ /**
50
+ * The widget an AI app shows (MCP Apps) runs on the host's origin and reaches one run with its
51
+ * run ticket: those requests may come from anywhere, the ticket is what lets them in.
52
+ */
53
+ const ticketedRun = /^\/api\/runs\/([^/]+)(\/|$)/;
47
54
  app.use("*", async (c, next) => {
48
- if (managed) {
49
- return next();
50
- }
51
- if (!hostAllowed(c.req.header("host") ?? new URL(c.req.url).host)) {
55
+ if (!managed && !hostAllowed(c.req.header("host") ?? new URL(c.req.url).host)) {
52
56
  return c.text("Forbidden host", 403);
53
57
  }
54
58
  const origin = c.req.header("origin");
59
+ const path = c.req.path.startsWith(`${basePath}/`)
60
+ ? c.req.path.slice(basePath.length)
61
+ : c.req.path;
62
+ const runId = ticketedRun.exec(path)?.[1];
63
+ if (origin && runId && runTicketValid(runId, c.req.query("rt"))) {
64
+ const cors = {
65
+ "access-control-allow-origin": origin,
66
+ "access-control-allow-credentials": "true",
67
+ vary: "Origin",
68
+ };
69
+ if (c.req.method === "OPTIONS") {
70
+ return c.body(null, 204, {
71
+ ...cors,
72
+ "access-control-allow-methods": "GET, POST, PUT, PATCH, DELETE",
73
+ "access-control-allow-headers": "content-type",
74
+ // A page on the internet asking a runtime on this computer (Private Network Access).
75
+ "access-control-allow-private-network": "true",
76
+ "access-control-max-age": "600",
77
+ });
78
+ }
79
+ await next();
80
+ for (const [name, value] of Object.entries(cors)) {
81
+ c.res.headers.set(name, value);
82
+ }
83
+ return;
84
+ }
85
+ if (managed) {
86
+ return next();
87
+ }
55
88
  const safe = c.req.method === "GET" || c.req.method === "HEAD" || c.req.method === "OPTIONS";
56
89
  // MCP clients and scripts send no Origin; a browser always does on a cross-origin write.
57
90
  if (!safe && origin && !hostAllowed(new URL(origin).host)) {
@@ -99,6 +132,7 @@ app.on(["GET", "POST", "DELETE"], "/api/mcp", (c) => mcp(c.req.raw));
99
132
  app.on(["GET", "POST", "DELETE"], "/api/mcp/bridge/:token", (c) => bridgeRequest(c.req.param("token"), c.req.raw));
100
133
  app.route("/api/studio/wizards", wizardStream);
101
134
  app.route("/api/studio/marketplace", marketplaceStudio);
135
+ app.route("/api/studio/plugins", pluginRoutes);
102
136
  app.route("/api/studio", studio);
103
137
  app.route("/api/v1", apiRoutes);
104
138
  app.route("/api/internal", internalRoutes);
@@ -5,19 +5,22 @@ import { createHmac, timingSafeEqual } from "node:crypto";
5
5
  * nothing new — it lets the command line and the desktop app enter a runtime that already runs.
6
6
  */
7
7
  const TTL_MS = 60_000;
8
- const sign = (secret, expires) => createHmac("sha256", secret).update(`local-enter.${expires}`).digest("hex");
9
- export function mintLocalTicket(secret, now = Date.now()) {
8
+ /** The header `wizards mcp` signs its requests with, and the one naming its client. */
9
+ export const LOCAL_MCP_HEADER = "x-engenty-local";
10
+ export const LOCAL_CLIENT_HEADER = "x-engenty-client";
11
+ const sign = (secret, expires, purpose) => createHmac("sha256", secret).update(`${purpose}.${expires}`).digest("hex");
12
+ export function mintLocalTicket(secret, now = Date.now(), purpose = "local-enter") {
10
13
  const expires = now + TTL_MS;
11
- return `${expires}.${sign(secret, expires)}`;
14
+ return `${expires}.${sign(secret, expires, purpose)}`;
12
15
  }
13
16
  /** When a ticket runs out, or null when it is not one of ours or already ran out. */
14
- export function localTicketExpiry(secret, ticket, now = Date.now()) {
17
+ export function localTicketExpiry(secret, ticket, now = Date.now(), purpose = "local-enter") {
15
18
  const [head, signature] = ticket.split(".");
16
19
  const expires = Number(head);
17
20
  if (!signature || !Number.isSafeInteger(expires) || expires <= now || expires > now + TTL_MS) {
18
21
  return null;
19
22
  }
20
23
  const given = Buffer.from(signature);
21
- const expected = Buffer.from(sign(secret, expires));
24
+ const expected = Buffer.from(sign(secret, expires, purpose));
22
25
  return given.length === expected.length && timingSafeEqual(given, expected) ? expires : null;
23
26
  }
@@ -218,6 +218,14 @@ export function connectorsLine(connectors) {
218
218
  });
219
219
  return `Connectors a wizard of this project can use (built in, or imported from the integrations registry; find more with find_connectors + import_connector):\n${lines.join("\n")}`;
220
220
  }
221
+ /** The tools this app's plugins add; nothing where there are none. */
222
+ export function pluginToolsLine(tools) {
223
+ if (!tools.length) {
224
+ return "";
225
+ }
226
+ const lines = tools.map((t) => `- ${t.id} — ${t.description}`);
227
+ return `\n\nTools of this app's plugins. An agent step lists one in "tools" by its full id, next to the built-in ones; a wizard that uses one runs only where that plugin is installed:\n${lines.join("\n")}`;
228
+ }
221
229
  export function mcpServersLine(mcp) {
222
230
  return `Project MCP servers available to agent steps: ${mcp.length ? mcp.map((m) => `${m.id} (${m.name})`).join(", ") : "none — if the admin wants to write into an external system that needs one, build the step with http or browser, and tell them they can connect a server in the project settings"}.`;
223
231
  }
@@ -225,7 +233,7 @@ export function exampleWizard() {
225
233
  return JSON.stringify(EXAMPLE_WIZARD, null, 2);
226
234
  }
227
235
  /** The guide an MCP client reads before it writes a wizard. */
228
- export function authoringGuide(mcp, connectors = []) {
236
+ export function authoringGuide(mcp, connectors = [], pluginTools = []) {
229
237
  return `engenty wizards: a wizard is a page-by-page flow an end user walks through on a shared link. Pages ask questions; AI steps research, write, draw images, render video, build documents and dashboards, or write into other systems. You author the wizard as JSON; the person who asked you is the ADMIN, the people who later open the link are END USERS.
230
238
 
231
239
  ${SCHEMA_DOC}
@@ -234,7 +242,7 @@ ${WIDGET_GUIDE}
234
242
 
235
243
  ${mcpServersLine(mcp)}
236
244
 
237
- ${connectorsLine(connectors)}
245
+ ${connectorsLine(connectors)}${pluginToolsLine(pluginTools)}
238
246
 
239
247
  Models: a step names the KIND of model it needs, never a model. Pick the cheapest class that does the job:
240
248
  - "classifier": routing, yes/no, picking from options, pulling a few values out of text.