wizards 0.2.16 → 0.2.18

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 (90) hide show
  1. package/apps/runtime/dist/agents/project-assistant.js +20 -2
  2. package/apps/runtime/dist/authoring/guide.js +6 -3
  3. package/apps/runtime/dist/credits/estimate.js +14 -8
  4. package/apps/runtime/dist/db/migrations/tenant/0008_run_event_note.sql +1 -0
  5. package/apps/runtime/dist/db/migrations/tenant/0009_knowledge.sql +104 -0
  6. package/apps/runtime/dist/db/migrations/tenant/meta/0008_snapshot.json +2019 -0
  7. package/apps/runtime/dist/db/migrations/tenant/meta/0009_snapshot.json +2837 -0
  8. package/apps/runtime/dist/db/migrations/tenant/meta/_journal.json +14 -0
  9. package/apps/runtime/dist/db/schema.js +162 -8
  10. package/apps/runtime/dist/engine/events.js +14 -2
  11. package/apps/runtime/dist/engine/prepare.js +5 -2
  12. package/apps/runtime/dist/engine/requirements.js +108 -0
  13. package/apps/runtime/dist/engine/runner.js +2 -2
  14. package/apps/runtime/dist/engine/steps.js +21 -21
  15. package/apps/runtime/dist/engine/template.js +13 -10
  16. package/apps/runtime/dist/env.js +2 -0
  17. package/apps/runtime/dist/files/storage.js +15 -0
  18. package/apps/runtime/dist/models.js +38 -0
  19. package/apps/runtime/dist/plugins/loader.js +2 -0
  20. package/apps/runtime/dist/routes/studio.js +149 -8
  21. package/apps/runtime/dist/services/cloud.js +59 -1
  22. package/apps/runtime/dist/services/knowledge-model.js +558 -0
  23. package/apps/runtime/dist/services/knowledge.js +721 -0
  24. package/apps/runtime/dist/services/marketplace.js +2 -2
  25. package/apps/runtime/dist/services/project-files.js +114 -6
  26. package/apps/runtime/dist/services/project-index.js +632 -94
  27. package/apps/runtime/dist/services/relevance.js +90 -0
  28. package/apps/runtime/dist/services/space-categories.js +882 -0
  29. package/apps/runtime/dist/services/space-data.js +466 -38
  30. package/apps/runtime/dist/services/space-origin.js +164 -0
  31. package/apps/runtime/dist/services/spaces.js +155 -1
  32. package/apps/runtime/dist/tools/browser.js +12 -6
  33. package/apps/runtime/dist/tools/index.js +12 -6
  34. package/apps/runtime/dist/tools/mail.js +5 -2
  35. package/apps/runtime/dist/tools/pages.js +28 -23
  36. package/apps/runtime/dist/tools/project.js +132 -39
  37. package/apps/runtime/dist/tools/store.js +4 -4
  38. package/apps/runtime/dist/widgets/step.js +10 -4
  39. package/apps/web/dist/assets/{Account-BE_W101C.js → Account-Cp1cgqjV.js} +1 -1
  40. package/apps/web/dist/assets/{ChangelogDialog-DjKQJAos.js → ChangelogDialog-Chke1qUe.js} +1 -1
  41. package/apps/web/dist/assets/ChatPanel-DWrwewr6.js +1 -0
  42. package/apps/web/dist/assets/CreditsDialog-Ckema8Ss.js +1 -0
  43. package/apps/web/dist/assets/EditorPage-XX6pfHk5.js +2 -0
  44. package/apps/web/dist/assets/{FlowPreview-DK4RZqTW.js → FlowPreview-DS545n78.js} +1 -1
  45. package/apps/web/dist/assets/{Harness-DcI94f1U.js → Harness-H3yXP-2L.js} +2 -2
  46. package/apps/web/dist/assets/{HomePage-BBNFgryu.js → HomePage-Bds84ShK.js} +1 -1
  47. package/apps/web/dist/assets/{ImportWizard-BoYqovdl.js → ImportWizard-0_NkA8aS.js} +1 -1
  48. package/apps/web/dist/assets/LocalRuntime-DnABzTOe.js +1 -0
  49. package/apps/web/dist/assets/{NewWizardPage-tKV2S4eK.js → NewWizardPage-BqP75V9K.js} +2 -2
  50. package/apps/web/dist/assets/ProjectPage-DbOvwOdZ.js +5 -0
  51. package/apps/web/dist/assets/PublicRunner-CtZ7vxl_.js +1 -0
  52. package/apps/web/dist/assets/RunnerView-xV5bWclu.js +70 -0
  53. package/apps/web/dist/assets/SettingsPage-DKt55zFh.js +1 -0
  54. package/apps/web/dist/assets/{SetupPage-qxHvlf87.js → SetupPage-c6mMD9zN.js} +1 -1
  55. package/apps/web/dist/assets/SharePage-DTKIbMj-.js +1 -0
  56. package/apps/web/dist/assets/{api-t9_w1XNe.js → api-CVG-xDEn.js} +2 -2
  57. package/apps/web/dist/assets/changelog-CXXT0zsp.js +1 -0
  58. package/apps/web/dist/assets/{esm-D0YVMTdo.js → esm-DLM3jdbm.js} +1 -1
  59. package/apps/web/dist/assets/index-1gmaxDSk.js +10 -0
  60. package/apps/web/dist/assets/index-Bwyehu0k.css +1 -0
  61. package/apps/web/dist/assets/{meta-BJHYbMh7.js → meta-m947x6uG.js} +1 -1
  62. package/apps/web/dist/assets/{settings-menu-BqqUAvIY.js → settings-menu-DUY6__C3.js} +1 -1
  63. package/apps/web/dist/assets/ui-BPY2pIh0.js +1 -0
  64. package/apps/web/dist/index.html +4 -4
  65. package/apps/web/dist/mcp/flow.html +54 -54
  66. package/node_modules/@engenty-wizards/plugin-sdk/dist/index.d.ts +164 -0
  67. package/node_modules/@engenty-wizards/shared/dist/definition.d.ts +10 -0
  68. package/node_modules/@engenty-wizards/shared/dist/definition.js +25 -0
  69. package/node_modules/@engenty-wizards/shared/dist/knowledge.d.ts +199 -0
  70. package/node_modules/@engenty-wizards/shared/dist/knowledge.js +68 -0
  71. package/node_modules/@engenty-wizards/shared/dist/projects.d.ts +1 -16
  72. package/node_modules/@engenty-wizards/shared/dist/projects.js +1 -1
  73. package/node_modules/@engenty-wizards/shared/dist/run.d.ts +149 -0
  74. package/node_modules/@engenty-wizards/shared/dist/run.js +77 -1
  75. package/node_modules/@engenty-wizards/shared/dist/space-data.d.ts +20 -0
  76. package/node_modules/@engenty-wizards/shared/dist/space-data.js +2 -1
  77. package/package.json +1 -1
  78. package/apps/web/dist/assets/ChatPanel-BWrcDs4s.js +0 -1
  79. package/apps/web/dist/assets/CreditsDialog-DUjR5xkl.js +0 -1
  80. package/apps/web/dist/assets/EditorPage-CrOqikP2.js +0 -2
  81. package/apps/web/dist/assets/LocalRuntime-BD8zGN8w.js +0 -1
  82. package/apps/web/dist/assets/ProjectPage-voR0hvzy.js +0 -4
  83. package/apps/web/dist/assets/PublicRunner-CYZE_hSi.js +0 -1
  84. package/apps/web/dist/assets/RunnerView-D_C7hn81.js +0 -70
  85. package/apps/web/dist/assets/SettingsPage-DguoPF_3.js +0 -1
  86. package/apps/web/dist/assets/SharePage-Cf3i7KB-.js +0 -1
  87. package/apps/web/dist/assets/changelog-kz0DmHY4.js +0 -1
  88. package/apps/web/dist/assets/index-BPpKz65x.css +0 -1
  89. package/apps/web/dist/assets/index-D5w3fW1p.js +0 -10
  90. package/apps/web/dist/assets/ui-BHXO_e0e.js +0 -1
@@ -600,6 +600,44 @@ export async function embeddingModel() {
600
600
  ? { model: gatewayClient(access, {}).embeddingModel("wizards/embedding"), ref: bound.model }
601
601
  : null;
602
602
  }
603
+ /**
604
+ * The way to TypeSafe's Jev, a classifier that answers typed questions with probabilities and
605
+ * writes no text: the credits on a Manage-App's runtime; alone, an AI Gateway key, a TypeSafe
606
+ * key, or the linked account's credits. Null when there is none.
607
+ */
608
+ export async function systemOneAccess() {
609
+ const cfg = await config();
610
+ const viaCredits = (access) => ({
611
+ url: `${access.baseUrl}/v4/ai/systemone`,
612
+ headers: {
613
+ authorization: `Bearer ${access.token}`,
614
+ // Booked under the classifier class; its evaluation model answers.
615
+ "ai-model-id": "wizards/classifier",
616
+ ...(access.tenant ? { "x-wizards-tenant": access.tenant } : {}),
617
+ },
618
+ model: null,
619
+ });
620
+ if (managed) {
621
+ const access = await creditAccess(cfg, true).catch(() => null);
622
+ return access ? viaCredits(access) : null;
623
+ }
624
+ if (cfg.keys.gateway) {
625
+ return {
626
+ url: "https://ai-gateway.vercel.sh/typesafe/v1/systemone",
627
+ headers: { authorization: `Bearer ${cfg.keys.gateway}` },
628
+ model: "typesafe-ai/jev",
629
+ };
630
+ }
631
+ if (env.typesafeKey) {
632
+ return {
633
+ url: "https://api.typesafe.ai/v1/systemone",
634
+ headers: { authorization: `Bearer ${env.typesafeKey}` },
635
+ model: "jev-latest",
636
+ };
637
+ }
638
+ const access = await creditAccess(cfg, false).catch(() => null);
639
+ return access ? viaCredits(access) : null;
640
+ }
603
641
  /** Search tools an AI Gateway runs itself; they travel in the request, so any gateway client names them. */
604
642
  export const gatewayTools = createGateway({ apiKey: "unused" }).tools;
605
643
  /**
@@ -11,6 +11,7 @@ import { parseDocument } from "../documents/parse.js";
11
11
  import { env } from "../env.js";
12
12
  import { ModelUnavailableError, textModel } from "../models.js";
13
13
  import { putIndexEntry, removeIndexEntries } from "../services/project-index.js";
14
+ import { originData } from "../services/space-origin.js";
14
15
  import { safeFetch } from "../tools/net-guard.js";
15
16
  import { discoverPlugins } from "./discovery.js";
16
17
  import { migrateOpenTenants, migratePlugins } from "./migrations.js";
@@ -112,6 +113,7 @@ function apiFor(record) {
112
113
  put: (entry) => putIndexEntry(source.id, entry),
113
114
  remove: (space, key) => removeIndexEntries(source.id, space, key),
114
115
  },
116
+ spaceData: originData(source.id),
115
117
  web: {
116
118
  fetch: (url, init) => safeFetch(url, init),
117
119
  read: (url, options) => readPage(url, options?.signal),
@@ -1,6 +1,7 @@
1
1
  import { mkdirSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { MODEL_CLASSES, TEXT_CLASSES } from "@engenty-wizards/shared/definition";
4
+ import { CATEGORY_TYPES, whereSchema } from "@engenty-wizards/shared/knowledge";
4
5
  import { PROJECT_FILE_KINDS } from "@engenty-wizards/shared/projects";
5
6
  import { isCreditsRef } from "@engenty-wizards/shared/providers";
6
7
  import { generateText } from "ai";
@@ -16,7 +17,7 @@ import { layout } from "../cli/home.js";
16
17
  import { connectorImportSchema, importConnector, listConnectors, previewSource, refreshConnector, registryService, removeConnector, searchRegistry, } from "../connectors/external.js";
17
18
  import { balanceCredits, canSpend } from "../credits/credits.js";
18
19
  import { estimateRun } from "../credits/estimate.js";
19
- import { missingModels } from "../engine/requirements.js";
20
+ import { missingModels, stepsWithoutModel } from "../engine/requirements.js";
20
21
  import { env } from "../env.js";
21
22
  import { freshAuth, loginEnv } from "../harness/env.js";
22
23
  import { detectHarness, detectHarnesses, HARNESS_IDS, harness, installSpec, } from "../harness/index.js";
@@ -30,15 +31,18 @@ import { HTML_RESPONSE_CSP } from "../render/guard.js";
30
31
  import { mayBuild, projectWritable, requireWritable } from "../services/access.js";
31
32
  import { architectTurn } from "../services/architect.js";
32
33
  import { checkForCloud, cloudLinked, cloudSpaces, cloudState, forgetCloud, publishAndSync, pushSharing, removeFromCloud, replaceCloudSpaces, rotateInCloud, syncToCloud, } from "../services/cloud.js";
33
- import { ServiceError } from "../services/errors.js";
34
+ import { notFound, ServiceError } from "../services/errors.js";
34
35
  import { deleteFile, listFiles, readFile, writeFile } from "../services/files.js";
36
+ import { categoryContents, listKnowledge, resolvePath, valueContents, } from "../services/knowledge.js";
37
+ import { writeSummary } from "../services/knowledge-model.js";
35
38
  import { exportWizard, importWizard, PACKAGE_EXTENSION, PACKAGE_MAX_BYTES, PACKAGE_MIME, } from "../services/package.js";
36
39
  import { profileSchema, saveProfile, userProfile } from "../services/profile.js";
37
40
  import { addProjectFile, fileView, orderProjectFiles, projectFile, projectFileContent, projectFiles, reindexProjectFile, removeProjectFile, updateProjectFile, } from "../services/project-files.js";
38
- import { documentText, searchProject } from "../services/project-index.js";
41
+ import { documentText, queueIndex, searchKnowledge } from "../services/project-index.js";
39
42
  import { createProject, deleteProject, listProjects, maskedServers, ownedProject, projectLimit, projectPatchSchema, updateProject, } from "../services/projects.js";
40
43
  import { listResults, listRuns, startTestRun } from "../services/runs.js";
41
- import { addTableRow, createPage, createTable, deletePage, deleteTable, deleteTableRows, getPage, getTable, listSpaceData, updatePage, updateTable, updateTableRow, } from "../services/space-data.js";
44
+ import { addCategoryValue, categoryRow, categoryValue, deleteCategory, deleteCategoryValue, mergeCategoryValues, orderCategoryValues, putCategory, renameCategoryValue, setItemCategory, updateCategory, } from "../services/space-categories.js";
45
+ import { addTableRow, createPage, createTable, deletePage, deleteTable, deleteTableRows, getPage, getTable, listSpaceData, orderSubPages, updatePage, updateTable, updateTableRow, } from "../services/space-data.js";
42
46
  import { draftWidgetPreview } from "../services/widgets.js";
43
47
  import { createWizard, deleteWizard, duplicateWizard, listWizards, ownedWizard, rotateShareLink, updateWizardSettings, wizardMessages, wizardState, writableWizard, writeDraft, } from "../services/wizards.js";
44
48
  import { readSetting, writeSetting } from "../settings.js";
@@ -344,11 +348,13 @@ export const studio = new Hono()
344
348
  const text = await documentText(row);
345
349
  return c.json({ text: text.slice(0, limit), chars: text.length });
346
350
  })
347
- // What the document index finds for a question: the passages an agent step would get.
351
+ // What a search of Wissen finds for a question: the items an agent step would get.
348
352
  .post("/projects/:id/search", async (c) => {
349
353
  const project = await ownedProject(c.get("user").id, c.req.param("id"));
350
- const { query } = z.object({ query: z.string().min(1).max(400) }).parse(await c.req.json());
351
- return c.json({ hits: await searchProject(project.id, query, 6) });
354
+ const body = z
355
+ .object({ query: z.string().min(1).max(400), where: whereSchema.optional() })
356
+ .parse(await c.req.json());
357
+ return c.json(await searchKnowledge(project.id, body.query, { where: body.where }));
352
358
  })
353
359
  // The assistant fills the project in: from a description, a website, the files given.
354
360
  .post("/projects/:id/assist", async (c) => {
@@ -362,6 +368,7 @@ export const studio = new Hono()
362
368
  .array(z.object({ role: z.enum(["user", "assistant"]), content: z.string().max(8000) }))
363
369
  .max(24)
364
370
  .default([]),
371
+ part: z.enum(["info", "knowledge"]).default("info"),
365
372
  })
366
373
  .parse(await c.req.json());
367
374
  if (!(await canSpend())) {
@@ -376,6 +383,7 @@ export const studio = new Hono()
376
383
  projectId: project.id,
377
384
  message: body.message,
378
385
  history: body.history,
386
+ part: body.part,
379
387
  signal: abort.signal,
380
388
  onText: (delta) => {
381
389
  void stream.writeSSE({ event: "text", data: JSON.stringify(delta) });
@@ -466,6 +474,128 @@ export const studio = new Hono()
466
474
  mode: query.mode,
467
475
  days: query.days,
468
476
  }));
477
+ })
478
+ // --- Wissen: its items and Kategorien ---------------------------------------
479
+ .get("/projects/:id/knowledge", async (c) => c.json(await listKnowledge(c.get("user").id, c.req.param("id"))))
480
+ // What a link in a page names (`pages/bgb/p-281`): the studio opens it.
481
+ .get("/projects/:id/resolve", async (c) => {
482
+ const project = await ownedProject(c.get("user").id, c.req.param("id"));
483
+ const found = await resolvePath(project.id, z.string().max(600).parse(c.req.query("path")));
484
+ if (!found) {
485
+ throw notFound();
486
+ }
487
+ return c.json(found);
488
+ })
489
+ .post("/projects/:id/categories", async (c) => {
490
+ const project = await ownedProject(c.get("user").id, c.req.param("id"));
491
+ await requireWritable(project);
492
+ const body = z
493
+ .object({
494
+ name: z.string().min(1).max(80),
495
+ type: z.enum(CATEGORY_TYPES),
496
+ unit: z.string().max(20).nullish(),
497
+ multiple: z.boolean().optional(),
498
+ ordered: z.boolean().optional(),
499
+ hint: z.string().max(120).nullish(),
500
+ values: z.array(z.string().max(200)).max(200).optional(),
501
+ tableId: z.string().nullish(),
502
+ columnId: z.string().nullish(),
503
+ })
504
+ .parse(await c.req.json());
505
+ const row = await putCategory(project.id, body);
506
+ return c.json({ id: row.id });
507
+ })
508
+ .patch("/categories/:id", async (c) => {
509
+ const row = await categoryRow(c.req.param("id"));
510
+ await ownedProject(c.get("user").id, row.projectId);
511
+ const body = z
512
+ .object({
513
+ name: z.string().min(1).max(80).optional(),
514
+ unit: z.string().max(20).nullish(),
515
+ multiple: z.boolean().optional(),
516
+ ordered: z.boolean().optional(),
517
+ hint: z.string().max(120).nullish(),
518
+ proposed: z.literal(false).optional(),
519
+ position: z.number().int().min(0).optional(),
520
+ })
521
+ .parse(await c.req.json());
522
+ await updateCategory(row.id, body);
523
+ return c.json({ ok: true });
524
+ })
525
+ .delete("/categories/:id", async (c) => {
526
+ const row = await categoryRow(c.req.param("id"));
527
+ await ownedProject(c.get("user").id, row.projectId);
528
+ await deleteCategory(row.id);
529
+ return c.json({ ok: true });
530
+ })
531
+ .post("/categories/:id/values", async (c) => {
532
+ const row = await categoryRow(c.req.param("id"));
533
+ await ownedProject(c.get("user").id, row.projectId);
534
+ const { value } = z.object({ value: z.string().min(1).max(200) }).parse(await c.req.json());
535
+ return c.json(await addCategoryValue(row.id, value));
536
+ })
537
+ .put("/categories/:id/values/order", async (c) => {
538
+ const row = await categoryRow(c.req.param("id"));
539
+ await ownedProject(c.get("user").id, row.projectId);
540
+ const { ids } = z.object({ ids: z.array(z.string()).max(500) }).parse(await c.req.json());
541
+ await orderCategoryValues(row.id, ids);
542
+ return c.json({ ok: true });
543
+ })
544
+ .get("/values/:id", async (c) => c.json(await valueContents(c.get("user").id, c.req.param("id"))))
545
+ // Everything that has a Kategorie: the entries its page lists.
546
+ .get("/categories/:id/items", async (c) => c.json(await categoryContents(c.get("user").id, c.req.param("id"))))
547
+ .patch("/values/:id", async (c) => {
548
+ const { category } = await categoryValue(c.req.param("id"));
549
+ await ownedProject(c.get("user").id, category.projectId);
550
+ const { value } = z.object({ value: z.string().min(1).max(200) }).parse(await c.req.json());
551
+ await renameCategoryValue(c.req.param("id"), value);
552
+ return c.json({ ok: true });
553
+ })
554
+ .post("/values/:id/merge", async (c) => {
555
+ const { category } = await categoryValue(c.req.param("id"));
556
+ await ownedProject(c.get("user").id, category.projectId);
557
+ const { into } = z.object({ into: z.string() }).parse(await c.req.json());
558
+ await mergeCategoryValues(c.req.param("id"), into);
559
+ return c.json({ ok: true });
560
+ })
561
+ .delete("/values/:id", async (c) => {
562
+ const { category } = await categoryValue(c.req.param("id"));
563
+ await ownedProject(c.get("user").id, category.projectId);
564
+ await deleteCategoryValue(c.req.param("id"));
565
+ return c.json({ ok: true });
566
+ })
567
+ // The Übersicht of a value: a model writes it from the value's items.
568
+ .post("/values/:id/summary", async (c) => {
569
+ const { category } = await categoryValue(c.req.param("id"));
570
+ const project = await ownedProject(c.get("user").id, category.projectId);
571
+ await requireWritable(project);
572
+ return c.json({ summary: await writeSummary(c.req.param("id")) });
573
+ })
574
+ // A Kategorie a person sets on an item: `p:<page>`, `t:<table>`, `r:<row>`, `f:<file>`.
575
+ .put("/projects/:id/items/:key/categories/:categoryId", async (c) => {
576
+ const project = await ownedProject(c.get("user").id, c.req.param("id"));
577
+ await requireWritable(project);
578
+ const key = c.req.param("key");
579
+ const id = key.slice(2);
580
+ const ref = key[0] === "p"
581
+ ? { pageId: id }
582
+ : key[0] === "t"
583
+ ? { tableId: id }
584
+ : key[0] === "r"
585
+ ? { rowId: id }
586
+ : key[0] === "f"
587
+ ? { fileId: id }
588
+ : null;
589
+ if (!ref || key[1] !== ":") {
590
+ throw new ServiceError("invalid", "Unbekannter Eintrag.");
591
+ }
592
+ const { values } = z
593
+ .object({ values: z.array(z.union([z.string().max(300), z.number(), z.boolean()])).max(50) })
594
+ .parse(await c.req.json());
595
+ if (await setItemCategory(project.id, ref, c.req.param("categoryId"), values)) {
596
+ queueIndex(key[0] === "p" ? `P:${id}` : key);
597
+ }
598
+ return c.json({ ok: true });
469
599
  })
470
600
  // --- the space's tables and pages --------------------------------------------
471
601
  .get("/projects/:id/data", async (c) => c.json(await listSpaceData(c.get("user").id, c.req.param("id"))))
@@ -475,6 +605,7 @@ export const studio = new Hono()
475
605
  title: z.string().max(120),
476
606
  wizardId: z.string().nullish(),
477
607
  columns: z.unknown().optional(),
608
+ format: z.enum(["faq"]).nullish(),
478
609
  })
479
610
  .parse(await c.req.json());
480
611
  return c.json(await createTable(c.get("user").id, c.req.param("id"), body));
@@ -486,6 +617,8 @@ export const studio = new Hono()
486
617
  title: z.string().max(120).optional(),
487
618
  wizardId: z.string().nullish(),
488
619
  columns: z.unknown().optional(),
620
+ format: z.enum(["faq"]).nullish(),
621
+ review: z.null().optional(),
489
622
  })
490
623
  .parse(await c.req.json());
491
624
  await updateTable(c.get("user").id, c.req.param("id"), body);
@@ -515,6 +648,7 @@ export const studio = new Hono()
515
648
  .object({
516
649
  title: z.string().max(200),
517
650
  wizardId: z.string().nullish(),
651
+ parentId: z.string().nullish(),
518
652
  markdown: z.string().optional(),
519
653
  })
520
654
  .parse(await c.req.json());
@@ -526,11 +660,18 @@ export const studio = new Hono()
526
660
  .object({
527
661
  title: z.string().max(200).optional(),
528
662
  wizardId: z.string().nullish(),
663
+ parentId: z.string().nullish(),
529
664
  markdown: z.string().optional(),
665
+ review: z.null().optional(),
530
666
  })
531
667
  .parse(await c.req.json());
532
668
  await updatePage(c.get("user").id, c.req.param("id"), body);
533
669
  return c.json({ ok: true });
670
+ })
671
+ .put("/pages/:id/order", async (c) => {
672
+ const { ids } = z.object({ ids: z.array(z.string()).max(5000) }).parse(await c.req.json());
673
+ await orderSubPages(c.get("user").id, c.req.param("id"), ids);
674
+ return c.json({ ok: true });
534
675
  })
535
676
  .delete("/pages/:id", async (c) => {
536
677
  await deletePage(c.get("user").id, c.req.param("id"));
@@ -639,7 +780,7 @@ export const studio = new Hono()
639
780
  // What a run of the draft is expected to cost, per step and in total.
640
781
  .get("/wizards/:id/estimate", async (c) => {
641
782
  const w = await ownedWizard(c.get("user").id, c.req.param("id"));
642
- return c.json(await estimateRun(w.id, null, w.draft));
783
+ return c.json(await estimateRun(w.id, null, w.draft, await stepsWithoutModel(w.draft)));
643
784
  })
644
785
  // The models the draft needs that this runtime cannot serve, by class, with the steps that call them.
645
786
  .get("/wizards/:id/models", async (c) => {
@@ -112,14 +112,72 @@ async function spaceDataOf(projectId) {
112
112
  where: own(schema.spacePage),
113
113
  orderBy: [asc(schema.spacePage.id)],
114
114
  });
115
+ const categories = await db.query.spaceCategory.findMany({
116
+ where: and(eq(schema.spaceCategory.projectId, projectId), eq(schema.spaceCategory.proposed, false)),
117
+ orderBy: [asc(schema.spaceCategory.position), asc(schema.spaceCategory.id)],
118
+ });
119
+ const values = categories.length
120
+ ? await db.query.spaceCategoryValue.findMany({
121
+ where: inArray(schema.spaceCategoryValue.categoryId, categories.map((c) => c.id)),
122
+ orderBy: [asc(schema.spaceCategoryValue.position), asc(schema.spaceCategoryValue.id)],
123
+ })
124
+ : [];
125
+ const assignments = categories.length
126
+ ? await db.query.spaceItemCategory.findMany({
127
+ where: and(eq(schema.spaceItemCategory.projectId, projectId), isNull(schema.spaceItemCategory.fileId)),
128
+ orderBy: [asc(schema.spaceItemCategory.id)],
129
+ })
130
+ : [];
131
+ const tableIds = new Set(tables.map((t) => t.id));
132
+ const rowIds = new Set(rows.map((r) => r.id));
133
+ const pageIds = new Set(pages.map((p) => p.id));
115
134
  return {
116
135
  tables: tables.map((t) => ({
117
136
  id: t.id,
118
137
  title: t.title,
138
+ format: t.format,
139
+ originLabel: t.originLabel,
119
140
  columns: t.columns,
120
141
  rows: rows.filter((r) => r.tableId === t.id).map((r) => ({ id: r.id, cells: r.cells })),
121
142
  })),
122
- pages: pages.map((p) => ({ id: p.id, title: p.title, markdown: p.markdown })),
143
+ pages: pages.map((p) => ({
144
+ id: p.id,
145
+ title: p.title,
146
+ markdown: p.markdown,
147
+ parentId: p.parentId,
148
+ position: p.position,
149
+ originLabel: p.originLabel,
150
+ })),
151
+ categories: categories.map((c) => ({
152
+ id: c.id,
153
+ name: c.name,
154
+ type: c.type,
155
+ unit: c.unit,
156
+ multiple: c.multiple,
157
+ ordered: c.ordered,
158
+ hint: c.hint,
159
+ tableId: c.tableId && tableIds.has(c.tableId) ? c.tableId : null,
160
+ columnId: c.tableId && tableIds.has(c.tableId) ? c.columnId : null,
161
+ position: c.position,
162
+ values: values
163
+ .filter((v) => v.categoryId === c.id)
164
+ .map((v) => ({ id: v.id, value: v.value, position: v.position, summary: v.summary })),
165
+ })),
166
+ // Only what goes along: a page, a table or a row of it.
167
+ assignments: assignments
168
+ .filter((a) => (a.pageId && pageIds.has(a.pageId)) ||
169
+ (a.tableId && tableIds.has(a.tableId)) ||
170
+ (a.rowId && rowIds.has(a.rowId)))
171
+ .map((a) => ({
172
+ categoryId: a.categoryId,
173
+ item: a.pageId ? `p:${a.pageId}` : a.tableId ? `t:${a.tableId}` : `r:${a.rowId}`,
174
+ valueId: a.valueId,
175
+ text: a.text,
176
+ num: a.num,
177
+ at: a.at,
178
+ bool: a.bool,
179
+ by: a.by,
180
+ })),
123
181
  };
124
182
  }
125
183
  /**