wizards 0.2.26 → 0.2.28
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/agents/project-assistant.js +10 -1
- package/apps/runtime/dist/authoring/guide.js +7 -3
- package/apps/runtime/dist/connectors/plugin-connections.js +248 -0
- package/apps/runtime/dist/db/migrations/tenant/0010_plugin_connection.sql +15 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0010_snapshot.json +2942 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/_journal.json +7 -0
- package/apps/runtime/dist/db/schema.js +21 -0
- package/apps/runtime/dist/engenty/connections-google/connectors/calendar.js +25 -3
- package/apps/runtime/dist/engenty/connections-microsoft/outlook.js +29 -1
- package/apps/runtime/dist/engine/input.js +10 -1
- package/apps/runtime/dist/mcp/tools.js +5 -2
- package/apps/runtime/dist/plugins/loader.js +22 -1
- package/apps/runtime/dist/plugins/registry.js +10 -0
- package/apps/runtime/dist/routes/marketplace.js +2 -1
- package/apps/runtime/dist/routes/runs.js +11 -3
- package/apps/runtime/dist/routes/studio.js +3 -1
- package/apps/runtime/dist/services/marketplace.js +167 -5
- package/apps/runtime/dist/services/runs.js +43 -15
- package/apps/runtime/dist/services/wizards.js +4 -2
- package/apps/runtime/dist/tools/plugin.js +29 -1
- package/apps/web/dist/assets/Account-CVMSZTCv.js +1 -0
- package/apps/web/dist/assets/{ChangelogDialog-CdtQWq0m.js → ChangelogDialog-3N1CgtWP.js} +1 -1
- package/apps/web/dist/assets/CreditsDialog-BW-RETl-.js +1 -0
- package/apps/web/dist/assets/EditorPage-DnuHM3nW.js +3 -0
- package/apps/web/dist/assets/{FlowPreview-IZcNrBJi.js → FlowPreview-Ds6ul7-N.js} +1 -1
- package/apps/web/dist/assets/{Harness-Cj-k02TA.js → Harness-D-QoXcSe.js} +2 -2
- package/apps/web/dist/assets/HomePage-Ch0HlwVX.js +1 -0
- package/apps/web/dist/assets/ImportWizard-DVcYcB6l.js +1 -0
- package/apps/web/dist/assets/LocalRuntime-D7bzT40S.js +1 -0
- package/apps/web/dist/assets/NewWizardPage-DvA6AX7D.js +2 -0
- package/apps/web/dist/assets/ProjectPage-BWAISSfX.js +2 -0
- package/apps/web/dist/assets/PublicRunner-D2bd95TR.js +1 -0
- package/apps/web/dist/assets/RunDrawer-DkOCgXAR.js +4 -0
- package/apps/web/dist/assets/RunnerView-DF5uW_95.js +2 -0
- package/apps/web/dist/assets/SettingsPage-CMTGFXit.js +1 -0
- package/apps/web/dist/assets/SetupPage-BaT-VqIc.js +1 -0
- package/apps/web/dist/assets/{SharePage-70niRKaO.js → SharePage-COKOsuvD.js} +1 -1
- package/apps/web/dist/assets/arrow-right-hy38IBRU.js +1 -0
- package/apps/web/dist/assets/changelog-C8wFW63z.js +1 -0
- package/apps/web/dist/assets/circle-alert-BG9P3ewL.js +1 -0
- package/apps/web/dist/assets/definition-BiUSuZN5.js +1 -0
- package/apps/web/dist/assets/esm-0rdZWuyu.js +7 -0
- package/apps/web/dist/assets/external-link-BwCtn1nW.js +1 -0
- package/apps/web/dist/assets/image-DG4q2P1H.js +1 -0
- package/apps/web/dist/assets/index-BOp2fz-2.css +1 -0
- package/apps/web/dist/assets/index-DHM9S48l.js +78 -0
- package/apps/web/dist/assets/lock-DH_BntP_.js +1 -0
- package/apps/web/dist/assets/meta-DI0ahJ_z.js +1 -0
- package/apps/web/dist/assets/pen-line-B1dsy3Sr.js +1 -0
- package/apps/web/dist/assets/refresh-cw-B9M1g3SH.js +1 -0
- package/apps/web/dist/assets/settings-menu-DOjgaq6J.js +1 -0
- package/apps/web/dist/assets/star-axQV_3EC.js +1 -0
- package/apps/web/dist/assets/surface-BE-3Z1DP.js +56 -0
- package/apps/web/dist/assets/trash-3S1_XpVa.js +1 -0
- package/apps/web/dist/assets/ui-BZ01zr3i.js +9 -0
- package/apps/web/dist/assets/upload-U0i8fAW3.js +1 -0
- package/apps/web/dist/assets/video-Db4o719f.js +1 -0
- package/apps/web/dist/assets/volume-2-6f7qN_LM.js +1 -0
- package/apps/web/dist/assets/wand-sparkles-MelvBtiK.js +1 -0
- package/apps/web/dist/index.html +4 -5
- package/apps/web/dist/mcp/flow.html +57 -57
- package/node_modules/@engenty-wizards/plugin-sdk/dist/index.d.ts +85 -0
- package/node_modules/@engenty-wizards/plugin-sdk/dist/studio.d.ts +23 -0
- package/node_modules/@engenty-wizards/shared/dist/definition.d.ts +28 -1
- package/node_modules/@engenty-wizards/shared/dist/definition.js +85 -5
- package/node_modules/@engenty-wizards/shared/dist/marketplace-search.js +4 -1
- package/node_modules/@engenty-wizards/shared/dist/marketplace.d.ts +43 -1
- package/node_modules/@engenty-wizards/shared/dist/marketplace.js +27 -0
- package/package.json +1 -1
- package/apps/web/dist/assets/Account-Cnksw5Ea.js +0 -1
- package/apps/web/dist/assets/CreditsDialog-Ti43boCN.js +0 -1
- package/apps/web/dist/assets/EditorPage-N7EzlRSj.js +0 -3
- package/apps/web/dist/assets/HomePage-bjdKkt84.js +0 -1
- package/apps/web/dist/assets/ImportWizard-DagFXYPM.js +0 -1
- package/apps/web/dist/assets/LocalRuntime-Boi_zqQJ.js +0 -1
- package/apps/web/dist/assets/NewWizardPage-DO5J8OpZ.js +0 -2
- package/apps/web/dist/assets/ProjectPage-DdG9wYsd.js +0 -2
- package/apps/web/dist/assets/PublicRunner-B4ts9rT6.js +0 -1
- package/apps/web/dist/assets/RunDrawer-LnVfCiYO.js +0 -4
- package/apps/web/dist/assets/RunnerView-DwffK7AA.js +0 -70
- package/apps/web/dist/assets/SettingsPage-Cyx_jTlp.js +0 -1
- package/apps/web/dist/assets/SetupPage-CBDD4rpC.js +0 -1
- package/apps/web/dist/assets/api-C4M6cbpW.js +0 -10
- package/apps/web/dist/assets/arrow-right-6FyN3e7D.js +0 -1
- package/apps/web/dist/assets/changelog-COyL1B5X.js +0 -1
- package/apps/web/dist/assets/check-CUpby_J9.js +0 -1
- package/apps/web/dist/assets/chevron-down-DymW-2BP.js +0 -1
- package/apps/web/dist/assets/circle-alert-D2SCwFT-.js +0 -1
- package/apps/web/dist/assets/createLucideIcon-q3MZMmnf.js +0 -1
- package/apps/web/dist/assets/definition-DoCLtZZL.js +0 -55
- package/apps/web/dist/assets/esm-CfW8Rmtq.js +0 -7
- package/apps/web/dist/assets/external-link-MAL7vJYj.js +0 -1
- package/apps/web/dist/assets/image-CC_2mJd6.js +0 -1
- package/apps/web/dist/assets/index-CJ0YJ5HC.css +0 -1
- package/apps/web/dist/assets/index-DMKXnSf-.js +0 -10
- package/apps/web/dist/assets/lock-DZL2N67v.js +0 -1
- package/apps/web/dist/assets/meta-6jZygxJq.js +0 -1
- package/apps/web/dist/assets/pen-line-NK46C_nG.js +0 -1
- package/apps/web/dist/assets/plus-Du-Jx8aI.js +0 -1
- package/apps/web/dist/assets/refresh-cw-BNtHis1x.js +0 -1
- package/apps/web/dist/assets/settings-menu-Dph4yXbO.js +0 -1
- package/apps/web/dist/assets/star-UHmZXpjA.js +0 -1
- package/apps/web/dist/assets/trash-0IafyhZB.js +0 -1
- package/apps/web/dist/assets/ui-BS348L4M.js +0 -1
- package/apps/web/dist/assets/upload-DNRENWzQ.js +0 -1
- package/apps/web/dist/assets/video-9fDPRPCv.js +0 -1
- package/apps/web/dist/assets/volume-2-DE67_rue.js +0 -1
- package/apps/web/dist/assets/wand-sparkles-IEd4jkyn.js +0 -1
|
@@ -497,3 +497,24 @@ export const marketplaceStar = sqliteTable("marketplace_star", {
|
|
|
497
497
|
entryId: text("entry_id").notNull(),
|
|
498
498
|
createdAt: createdAt(),
|
|
499
499
|
}, (t) => [primaryKey({ columns: [t.tenantId, t.entryId] })]);
|
|
500
|
+
/**
|
|
501
|
+
* An account a plugin connected for a space (`server.connections`): a calendar, a mailbox. Not
|
|
502
|
+
* a wizard's: it outlives runs and people. `data` is encrypted.
|
|
503
|
+
*/
|
|
504
|
+
export const pluginConnection = sqliteTable("plugin_connection", {
|
|
505
|
+
id: text("id").primaryKey(),
|
|
506
|
+
plugin: text("plugin").notNull(),
|
|
507
|
+
projectId: text("project_id")
|
|
508
|
+
.notNull()
|
|
509
|
+
.references(() => project.id, { onDelete: "cascade" }),
|
|
510
|
+
/** The built-in connector: `google-calendar`, `microsoft-outlook`. */
|
|
511
|
+
connector: text("connector").notNull(),
|
|
512
|
+
/** Shown to the person: the account's address. */
|
|
513
|
+
label: text("label").notNull(),
|
|
514
|
+
/** The connector's actions it was connected for. */
|
|
515
|
+
actions: text("actions", { mode: "json" }).$type().notNull(),
|
|
516
|
+
data: text("data").notNull(),
|
|
517
|
+
createdBy: text("created_by"),
|
|
518
|
+
createdAt: createdAt(),
|
|
519
|
+
updatedAt: updatedAt(),
|
|
520
|
+
}, (t) => [index("plugin_connection_space").on(t.plugin, t.projectId)]);
|
|
@@ -59,6 +59,12 @@ const attendeesField = z
|
|
|
59
59
|
.array(z.string().describe("Attendee email address."))
|
|
60
60
|
.optional()
|
|
61
61
|
.describe("Optional list of attendee email addresses.");
|
|
62
|
+
const sendUpdatesField = z
|
|
63
|
+
.enum(["all", "externalOnly", "none"])
|
|
64
|
+
.optional()
|
|
65
|
+
.describe('Whom Google e-mails about the change: "all" attendees, "externalOnly" (outside the organisation) or "none" (default).');
|
|
66
|
+
/** `?sendUpdates=` for a write, when the caller asked Google to tell the attendees. */
|
|
67
|
+
const updatesQuery = (value) => value ? `?sendUpdates=${encodeURIComponent(value)}` : "";
|
|
62
68
|
export const calendarConnector = defineConnector({
|
|
63
69
|
actions: [
|
|
64
70
|
connectorAction({
|
|
@@ -237,12 +243,13 @@ export const calendarConnector = defineConnector({
|
|
|
237
243
|
group: "write",
|
|
238
244
|
handler: async (input, ctx) => {
|
|
239
245
|
const calendarId = input.calendar_id ?? "primary";
|
|
240
|
-
const created = await googleJson(ctx, `${CALENDAR_API}/calendars/${encodeURIComponent(calendarId)}/events`, {
|
|
246
|
+
const created = await googleJson(ctx, `${CALENDAR_API}/calendars/${encodeURIComponent(calendarId)}/events${updatesQuery(input.send_updates)}`, {
|
|
241
247
|
body: JSON.stringify({
|
|
242
248
|
end: toEventDateTime(input.end, input.time_zone),
|
|
243
249
|
start: toEventDateTime(input.start, input.time_zone),
|
|
244
250
|
summary: input.summary,
|
|
245
251
|
...(input.description ? { description: input.description } : {}),
|
|
252
|
+
...(input.location ? { location: input.location } : {}),
|
|
246
253
|
...(input.attendees?.length
|
|
247
254
|
? { attendees: input.attendees.map((email) => ({ email })) }
|
|
248
255
|
: {}),
|
|
@@ -264,7 +271,12 @@ export const calendarConnector = defineConnector({
|
|
|
264
271
|
.optional()
|
|
265
272
|
.describe("Optional event description/notes."),
|
|
266
273
|
end: endField,
|
|
274
|
+
location: z
|
|
275
|
+
.string()
|
|
276
|
+
.optional()
|
|
277
|
+
.describe("Where the event takes place: an address, a phone number or a meeting link."),
|
|
267
278
|
private_properties: privatePropertiesField,
|
|
279
|
+
send_updates: sendUpdatesField,
|
|
268
280
|
start: startField,
|
|
269
281
|
summary: z.string().describe("Event title."),
|
|
270
282
|
time_zone: timeZoneField,
|
|
@@ -284,6 +296,9 @@ export const calendarConnector = defineConnector({
|
|
|
284
296
|
if (input.description !== undefined) {
|
|
285
297
|
patch.description = input.description;
|
|
286
298
|
}
|
|
299
|
+
if (input.location !== undefined) {
|
|
300
|
+
patch.location = input.location;
|
|
301
|
+
}
|
|
287
302
|
if (input.start !== undefined) {
|
|
288
303
|
patch.start = toEventDateTime(input.start, input.time_zone);
|
|
289
304
|
}
|
|
@@ -296,7 +311,7 @@ export const calendarConnector = defineConnector({
|
|
|
296
311
|
if (input.private_properties !== undefined) {
|
|
297
312
|
patch.extendedProperties = { private: input.private_properties };
|
|
298
313
|
}
|
|
299
|
-
const updated = await googleJson(ctx, `${CALENDAR_API}/calendars/${encodeURIComponent(calendarId)}/events/${encodeURIComponent(input.event_id)}`, {
|
|
314
|
+
const updated = await googleJson(ctx, `${CALENDAR_API}/calendars/${encodeURIComponent(calendarId)}/events/${encodeURIComponent(input.event_id)}${updatesQuery(input.send_updates)}`, {
|
|
300
315
|
body: JSON.stringify(patch),
|
|
301
316
|
headers: { "Content-Type": "application/json" },
|
|
302
317
|
method: "PATCH",
|
|
@@ -314,7 +329,12 @@ export const calendarConnector = defineConnector({
|
|
|
314
329
|
.describe("New event description (replaces the existing one)."),
|
|
315
330
|
end: endField.optional(),
|
|
316
331
|
event_id: z.string().describe("Calendar event id to update."),
|
|
332
|
+
location: z
|
|
333
|
+
.string()
|
|
334
|
+
.optional()
|
|
335
|
+
.describe("New location (replaces the existing one)."),
|
|
317
336
|
private_properties: privatePropertiesField,
|
|
337
|
+
send_updates: sendUpdatesField,
|
|
318
338
|
start: startField.optional(),
|
|
319
339
|
summary: z.string().optional().describe("New event title."),
|
|
320
340
|
time_zone: timeZoneField,
|
|
@@ -322,6 +342,7 @@ export const calendarConnector = defineConnector({
|
|
|
322
342
|
.refine((v) => v.attendees !== undefined ||
|
|
323
343
|
v.description !== undefined ||
|
|
324
344
|
v.end !== undefined ||
|
|
345
|
+
v.location !== undefined ||
|
|
325
346
|
v.private_properties !== undefined ||
|
|
326
347
|
v.start !== undefined ||
|
|
327
348
|
v.summary !== undefined, { message: "Provide at least one field to update." }),
|
|
@@ -333,7 +354,7 @@ export const calendarConnector = defineConnector({
|
|
|
333
354
|
group: "destructive",
|
|
334
355
|
handler: async (input, ctx) => {
|
|
335
356
|
const calendarId = input.calendar_id ?? "primary";
|
|
336
|
-
await googleFetch(ctx, `${CALENDAR_API}/calendars/${encodeURIComponent(calendarId)}/events/${encodeURIComponent(input.event_id)}`, { method: "DELETE" });
|
|
357
|
+
await googleFetch(ctx, `${CALENDAR_API}/calendars/${encodeURIComponent(calendarId)}/events/${encodeURIComponent(input.event_id)}${updatesQuery(input.send_updates)}`, { method: "DELETE" });
|
|
337
358
|
return {
|
|
338
359
|
calendar_id: calendarId,
|
|
339
360
|
deleted: true,
|
|
@@ -344,6 +365,7 @@ export const calendarConnector = defineConnector({
|
|
|
344
365
|
inputSchema: z.object({
|
|
345
366
|
calendar_id: calendarIdField,
|
|
346
367
|
event_id: z.string().describe("Calendar event id to delete."),
|
|
368
|
+
send_updates: sendUpdatesField,
|
|
347
369
|
}),
|
|
348
370
|
providerScopes: [SCOPE_EVENTS],
|
|
349
371
|
summary: "Delete a calendar event",
|
|
@@ -25,6 +25,7 @@ function mapEventSummary(e) {
|
|
|
25
25
|
isAllDay: e.isAllDay ?? false,
|
|
26
26
|
location: e.location?.displayName ?? null,
|
|
27
27
|
organizer: mapRecipient(e.organizer),
|
|
28
|
+
show_as: e.showAs ?? null,
|
|
28
29
|
start: e.start ?? null,
|
|
29
30
|
subject: e.subject ?? null,
|
|
30
31
|
};
|
|
@@ -93,7 +94,7 @@ function draftMessagePayload(input) {
|
|
|
93
94
|
toRecipients: toRecipients(input.to),
|
|
94
95
|
};
|
|
95
96
|
}
|
|
96
|
-
const EVENT_SELECT = "id,subject,start,end,location,organizer,isAllDay";
|
|
97
|
+
const EVENT_SELECT = "id,subject,start,end,location,organizer,isAllDay,showAs";
|
|
97
98
|
async function fetchEventDetail(ctx, eventId) {
|
|
98
99
|
const e = await graphJson(ctx, `/me/events/${encodeURIComponent(eventId)}?$select=${EVENT_SELECT},attendees,body,webLink`);
|
|
99
100
|
return mapEventDetail(e);
|
|
@@ -232,6 +233,10 @@ export const microsoftOutlookConnector = defineConnector({
|
|
|
232
233
|
.optional()
|
|
233
234
|
.describe("Window start (ISO 8601). Provide both start and end to query a window."),
|
|
234
235
|
top: topField,
|
|
236
|
+
all: z
|
|
237
|
+
.boolean()
|
|
238
|
+
.optional()
|
|
239
|
+
.describe("With start and end: every event of the window (up to 1000), not only the first `top`. For free/busy."),
|
|
235
240
|
}),
|
|
236
241
|
providerScopes: ["Calendars.Read"],
|
|
237
242
|
run: async (input, ctx) => {
|
|
@@ -244,6 +249,20 @@ export const microsoftOutlookConnector = defineConnector({
|
|
|
244
249
|
if (input.start && input.end) {
|
|
245
250
|
params.set("startDateTime", input.start);
|
|
246
251
|
params.set("endDateTime", input.end);
|
|
252
|
+
if (input.all) {
|
|
253
|
+
// Page through the window, 100 at a time.
|
|
254
|
+
const events = [];
|
|
255
|
+
params.set("$top", "100");
|
|
256
|
+
for (let skip = 0; skip < 1000; skip += 100) {
|
|
257
|
+
params.set("$skip", String(skip));
|
|
258
|
+
const page = await graphJson(ctx, `${calendarViewPath(input.calendar_id)}?${params.toString()}`);
|
|
259
|
+
events.push(...(page.value ?? []));
|
|
260
|
+
if ((page.value ?? []).length < 100) {
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return { events: events.map(mapEventSummary) };
|
|
265
|
+
}
|
|
247
266
|
path = `${calendarViewPath(input.calendar_id)}?${params.toString()}`;
|
|
248
267
|
}
|
|
249
268
|
const data = await graphJson(ctx, path);
|
|
@@ -320,6 +339,10 @@ export const microsoftOutlookConnector = defineConnector({
|
|
|
320
339
|
.optional()
|
|
321
340
|
.describe("Plain-text event description."),
|
|
322
341
|
calendar_id: calendarIdField,
|
|
342
|
+
location: z
|
|
343
|
+
.string()
|
|
344
|
+
.optional()
|
|
345
|
+
.describe("Where the event takes place: an address, a phone number or a meeting link."),
|
|
323
346
|
end: z
|
|
324
347
|
.string()
|
|
325
348
|
.describe("Event end, ISO 8601 date-time (e.g. 2026-07-04T15:00:00)."),
|
|
@@ -343,6 +366,7 @@ export const microsoftOutlookConnector = defineConnector({
|
|
|
343
366
|
? { body: { content: input.body_text, contentType: "Text" } }
|
|
344
367
|
: {}),
|
|
345
368
|
end: { dateTime: input.end, timeZone: input.time_zone },
|
|
369
|
+
...(input.location ? { location: { displayName: input.location } } : {}),
|
|
346
370
|
start: { dateTime: input.start, timeZone: input.time_zone },
|
|
347
371
|
subject: input.subject,
|
|
348
372
|
},
|
|
@@ -367,6 +391,7 @@ export const microsoftOutlookConnector = defineConnector({
|
|
|
367
391
|
.describe("New plain-text event description."),
|
|
368
392
|
end: z.string().optional().describe("New end, ISO 8601 date-time."),
|
|
369
393
|
event_id: z.string().describe("Graph event id to update."),
|
|
394
|
+
location: z.string().optional().describe("New location (replaces the existing one)."),
|
|
370
395
|
start: z.string().optional().describe("New start, ISO 8601 date-time."),
|
|
371
396
|
subject: z.string().optional().describe("New event subject/title."),
|
|
372
397
|
time_zone: z
|
|
@@ -392,6 +417,9 @@ export const microsoftOutlookConnector = defineConnector({
|
|
|
392
417
|
if (input.attendees !== undefined) {
|
|
393
418
|
patch.attendees = toAttendees(input.attendees);
|
|
394
419
|
}
|
|
420
|
+
if (input.location !== undefined) {
|
|
421
|
+
patch.location = { displayName: input.location };
|
|
422
|
+
}
|
|
395
423
|
if (Object.keys(patch).length === 0) {
|
|
396
424
|
throw new Error("update_event: no fields to update");
|
|
397
425
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isDecidedField, isLocationValue, MAX_FILES, shownFields, } from "@engenty-wizards/shared/definition";
|
|
1
|
+
import { isDecidedField, isLocationValue, MAX_FILES, offeredSlot, shownFields, } from "@engenty-wizards/shared/definition";
|
|
2
2
|
function empty(v) {
|
|
3
3
|
return v === undefined || v === null || v === "" || (Array.isArray(v) && v.length === 0);
|
|
4
4
|
}
|
|
@@ -20,6 +20,11 @@ function coerce(field, raw) {
|
|
|
20
20
|
return (Array.isArray(raw) ? raw : [raw])
|
|
21
21
|
.map(String)
|
|
22
22
|
.filter((v) => field.options?.includes(v));
|
|
23
|
+
// The offered time as written, also when the answer names the same moment in another offset.
|
|
24
|
+
case "slot": {
|
|
25
|
+
const v = String(raw).trim();
|
|
26
|
+
return v ? (offeredSlot(v, field.options ?? []) ?? v) : undefined;
|
|
27
|
+
}
|
|
23
28
|
case "image":
|
|
24
29
|
case "file": {
|
|
25
30
|
const ids = (Array.isArray(raw) ? raw : [raw])
|
|
@@ -153,6 +158,10 @@ export function readPageInput(step, input, context = {}) {
|
|
|
153
158
|
errors.push({ field: field.id, message: "Ungültige Auswahl" });
|
|
154
159
|
continue;
|
|
155
160
|
}
|
|
161
|
+
if (field.kind === "slot" && !empty(v) && !field.options?.includes(String(v))) {
|
|
162
|
+
errors.push({ field: field.id, message: "Dieser Termin wird nicht angeboten" });
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
156
165
|
if (field.kind === "email" && !empty(v) && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(v))) {
|
|
157
166
|
errors.push({ field: field.id, message: "Keine gültige E-Mail-Adresse" });
|
|
158
167
|
continue;
|
|
@@ -143,7 +143,7 @@ export function registerTools(server, who) {
|
|
|
143
143
|
});
|
|
144
144
|
tool("list_starters", "wizards:read", {
|
|
145
145
|
title: "List starters",
|
|
146
|
-
description: "
|
|
146
|
+
description: "Ready-made wizards to start from: first those the space's plugins bring (marked with the plugin), then the marketplace's (tweet with image, video ad, research briefing, dashboard, invoice, offer …), with what each makes and needs. Give a query to search them; without one you get the starters.",
|
|
147
147
|
input: z.object({ query: z.string().max(200).optional() }),
|
|
148
148
|
readOnly: true,
|
|
149
149
|
}, async ({ query }) => (await searchMarketplace({ q: query ?? "", lang: "de", limit: 30 })).entries
|
|
@@ -155,6 +155,9 @@ export function registerTools(server, who) {
|
|
|
155
155
|
formats: s.formats,
|
|
156
156
|
useCases: s.useCases,
|
|
157
157
|
capabilities: s.capabilities,
|
|
158
|
+
...(s.plugin ? { plugin: s.plugin.name } : {}),
|
|
159
|
+
// Not startable here: its plugins come with the Pro plan.
|
|
160
|
+
...(s.needs?.length ? { needsPro: s.needs } : {}),
|
|
158
161
|
})));
|
|
159
162
|
tool("get_starter", "wizards:read", {
|
|
160
163
|
title: "Get starter",
|
|
@@ -401,7 +404,7 @@ While status is running, get_run with waitSeconds. Hosts with MCP Apps also show
|
|
|
401
404
|
}, async ({ runId, waitSeconds }) => runReport(who.userId, runId, waitSeconds));
|
|
402
405
|
tool("answer_page", "runs:test", {
|
|
403
406
|
title: "Answer page",
|
|
404
|
-
description: "Fill the page a run waits for: field id → value (items: an array of row objects; toggle: true/false; multiselect: an array). Returns the run, after waitSeconds.",
|
|
407
|
+
description: "Fill the page a run waits for: field id → value (items: an array of row objects; toggle: true/false; multiselect: an array; slot: one of the offered times, as given). Returns the run, after waitSeconds.",
|
|
405
408
|
input: z.object({
|
|
406
409
|
runId: z.string(),
|
|
407
410
|
stepId: z.string().describe("waitingFor.page"),
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, watch } from "node:fs";
|
|
2
2
|
import { join, resolve, sep } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { PLUGIN_TOOL_NAME } from "@engenty-wizards/shared/definition";
|
|
4
|
+
import { PLUGIN_ID, PLUGIN_TOOL_NAME, wizardSchema } from "@engenty-wizards/shared/definition";
|
|
5
5
|
import { generateText, Output } from "ai";
|
|
6
6
|
import { createJiti } from "jiti";
|
|
7
7
|
import { readPage } from "../agents/website.js";
|
|
8
8
|
import { packageRoot } from "../cli/home.js";
|
|
9
|
+
import { pluginConnections } from "../connectors/plugin-connections.js";
|
|
9
10
|
import { db, onTenantOpen } from "../db/client.js";
|
|
10
11
|
import { parseDocument } from "../documents/parse.js";
|
|
11
12
|
import { env } from "../env.js";
|
|
@@ -98,6 +99,9 @@ function apiFor(record) {
|
|
|
98
99
|
}
|
|
99
100
|
record.assistantTools.set(tool.name, tool);
|
|
100
101
|
},
|
|
102
|
+
registerAssistantContext(context) {
|
|
103
|
+
record.assistantPages.push(context);
|
|
104
|
+
},
|
|
101
105
|
registerPublicRoute(route) {
|
|
102
106
|
record.publicRoutes.push(compileRoute(route));
|
|
103
107
|
},
|
|
@@ -153,6 +157,21 @@ function apiFor(record) {
|
|
|
153
157
|
object: (request.schema ? result.output : undefined),
|
|
154
158
|
};
|
|
155
159
|
},
|
|
160
|
+
connections: pluginConnections(source.id),
|
|
161
|
+
registerStarter(starter) {
|
|
162
|
+
if (!PLUGIN_ID.test(starter.id)) {
|
|
163
|
+
throw new Error(`Starter "${starter.id}": an id is lower case, digits and "-".`);
|
|
164
|
+
}
|
|
165
|
+
if (record.starters.has(starter.id)) {
|
|
166
|
+
throw new Error(`Starter "${starter.id}" is registered twice.`);
|
|
167
|
+
}
|
|
168
|
+
const parsed = wizardSchema.safeParse(starter.definition);
|
|
169
|
+
if (!parsed.success) {
|
|
170
|
+
const issue = parsed.error.issues[0];
|
|
171
|
+
throw new Error(`Starter "${starter.id}": ${issue?.path.join(".") || "definition"}: ${issue?.message}`);
|
|
172
|
+
}
|
|
173
|
+
record.starters.set(starter.id, { ...starter, definition: parsed.data });
|
|
174
|
+
},
|
|
156
175
|
onUnload(dispose) {
|
|
157
176
|
record.disposers.push(dispose);
|
|
158
177
|
},
|
|
@@ -186,8 +205,10 @@ async function load(source) {
|
|
|
186
205
|
record.tools.clear();
|
|
187
206
|
record.contexts = [];
|
|
188
207
|
record.assistantTools.clear();
|
|
208
|
+
record.assistantPages = [];
|
|
189
209
|
record.publicRoutes = [];
|
|
190
210
|
record.jobs.clear();
|
|
211
|
+
record.starters.clear();
|
|
191
212
|
record.listeners = {};
|
|
192
213
|
record.migrations = [];
|
|
193
214
|
record.error = err?.message || String(err);
|
|
@@ -17,8 +17,10 @@ export function emptyRecord(source) {
|
|
|
17
17
|
tools: new Map(),
|
|
18
18
|
contexts: [],
|
|
19
19
|
assistantTools: new Map(),
|
|
20
|
+
assistantPages: [],
|
|
20
21
|
publicRoutes: [],
|
|
21
22
|
jobs: new Map(),
|
|
23
|
+
starters: new Map(),
|
|
22
24
|
listeners: {},
|
|
23
25
|
migrations: [],
|
|
24
26
|
disposers: [],
|
|
@@ -83,6 +85,14 @@ export async function pluginToolsOf(tenantId) {
|
|
|
83
85
|
description: tool.description,
|
|
84
86
|
})));
|
|
85
87
|
}
|
|
88
|
+
/** The starters the tenant's plugins bring, in the order the plugins registered them. */
|
|
89
|
+
export async function pluginStartersOf(tenantId) {
|
|
90
|
+
return (await pluginsOf(tenantId)).flatMap((plugin) => [...plugin.starters.values()].map((starter) => ({
|
|
91
|
+
id: `${plugin.source.id}.${starter.id}`,
|
|
92
|
+
plugin,
|
|
93
|
+
starter,
|
|
94
|
+
})));
|
|
95
|
+
}
|
|
86
96
|
/** The built studio half as it is on disk now; null when the plugin has none. */
|
|
87
97
|
export function pluginAsset(record, kind) {
|
|
88
98
|
const file = record.source[kind];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CAPABILITY_IDS, INDUSTRY_IDS, ITEM_FORMAT_IDS, USE_CASE_IDS, } from "@engenty-wizards/shared/marketplace";
|
|
1
|
+
import { CAPABILITY_IDS, INDUSTRY_IDS, ITEM_FORMAT_IDS, MARKETPLACE_PLAN_IDS, USE_CASE_IDS, } from "@engenty-wizards/shared/marketplace";
|
|
2
2
|
import { Hono } from "hono";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { asLang, marketplaceDetail, searchMarketplace, starEntry, } from "../services/marketplace.js";
|
|
@@ -9,6 +9,7 @@ const searchSchema = z.object({
|
|
|
9
9
|
industry: z.enum(INDUSTRY_IDS).optional(),
|
|
10
10
|
format: z.enum(ITEM_FORMAT_IDS).optional(),
|
|
11
11
|
capability: z.enum(CAPABILITY_IDS).optional(),
|
|
12
|
+
plan: z.enum(MARKETPLACE_PLAN_IDS).optional(),
|
|
12
13
|
starred: z
|
|
13
14
|
.enum(["1", "0"])
|
|
14
15
|
.optional()
|
|
@@ -9,6 +9,7 @@ import { nanoid } from "nanoid";
|
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
import { principalOf } from "../auth/index.js";
|
|
11
11
|
import { ConnectError, connectionViews, connectWithCredentials, disconnect, finishOAuth, oauthStateRun, startOAuth, } from "../connectors/index.js";
|
|
12
|
+
import { finishPluginOAuth, pluginStateTenant } from "../connectors/plugin-connections.js";
|
|
12
13
|
import { db, schema, withTenant } from "../db/client.js";
|
|
13
14
|
import { answerAsk, pendingAsk } from "../engine/asks.js";
|
|
14
15
|
import { signalChanged, subscribe } from "../engine/events.js";
|
|
@@ -795,12 +796,19 @@ export const connectCallback = new Hono().get("/callback", async (c) => {
|
|
|
795
796
|
let message = c.req.query("error") === "exchange_failed"
|
|
796
797
|
? "Verbinden hat nicht geklappt. Bitte noch einmal versuchen."
|
|
797
798
|
: "Verbinden wurde abgebrochen.";
|
|
798
|
-
|
|
799
|
+
// A plugin's sign-in (server.connections) names its tenant; a wizard's names its run.
|
|
800
|
+
const pluginTenant = state ? pluginStateTenant(state) : null;
|
|
801
|
+
const tenant = state ? (pluginTenant ?? (await tenantOfRun(oauthStateRun(state) ?? ""))) : null;
|
|
799
802
|
const answer = ticket ? { ticket } : code ? { code } : null;
|
|
800
803
|
if (answer && state && tenant) {
|
|
801
804
|
try {
|
|
802
|
-
|
|
803
|
-
|
|
805
|
+
if (pluginTenant) {
|
|
806
|
+
await withTenant(tenant, () => finishPluginOAuth(answer, state));
|
|
807
|
+
}
|
|
808
|
+
else {
|
|
809
|
+
const done = await withTenant(tenant, () => finishOAuth(answer, state));
|
|
810
|
+
signalChanged(done.runId);
|
|
811
|
+
}
|
|
804
812
|
ok = true;
|
|
805
813
|
message = "Verbunden. Du kannst dieses Fenster schließen.";
|
|
806
814
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mkdirSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { MODEL_CLASSES, TEXT_CLASSES } from "@engenty-wizards/shared/definition";
|
|
3
|
+
import { MODEL_CLASSES, PLUGIN_ID, TEXT_CLASSES } from "@engenty-wizards/shared/definition";
|
|
4
4
|
import { CATEGORY_TYPES, whereSchema } from "@engenty-wizards/shared/knowledge";
|
|
5
5
|
import { PROJECT_FILE_KINDS } from "@engenty-wizards/shared/projects";
|
|
6
6
|
import { isCreditsRef } from "@engenty-wizards/shared/providers";
|
|
@@ -405,6 +405,7 @@ export const studio = new Hono()
|
|
|
405
405
|
.max(24)
|
|
406
406
|
.default([]),
|
|
407
407
|
part: z.enum(["info", "knowledge"]).default("info"),
|
|
408
|
+
plugin: z.string().regex(PLUGIN_ID).optional(),
|
|
408
409
|
})
|
|
409
410
|
.parse(await c.req.json());
|
|
410
411
|
if (!(await canSpend())) {
|
|
@@ -420,6 +421,7 @@ export const studio = new Hono()
|
|
|
420
421
|
message: body.message,
|
|
421
422
|
history: body.history,
|
|
422
423
|
part: body.part,
|
|
424
|
+
plugin: body.plugin,
|
|
423
425
|
signal: abort.signal,
|
|
424
426
|
onText: (delta) => {
|
|
425
427
|
void stream.writeSSE({ event: "text", data: JSON.stringify(delta) });
|