wizards 0.4.7 → 0.5.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.
- package/README.md +45 -42
- package/apps/runtime/dist/agents/studio-assistant.js +8 -8
- package/apps/runtime/dist/app.js +7 -4
- package/apps/runtime/dist/assistant/primer.js +1 -1
- package/apps/runtime/dist/auth/index.js +2 -2
- package/apps/runtime/dist/authoring/guide.js +8 -8
- package/apps/runtime/dist/cli/account.js +31 -28
- package/apps/runtime/dist/cli/autostart.js +5 -3
- package/apps/runtime/dist/cli/clients.js +1 -1
- package/apps/runtime/dist/cli/connect.js +24 -24
- package/apps/runtime/dist/cli/features.js +6 -6
- package/apps/runtime/dist/cli/main.js +51 -39
- package/apps/runtime/dist/cli/mcp.js +5 -4
- package/apps/runtime/dist/cli/menu.js +32 -30
- package/apps/runtime/dist/cli/report.js +7 -9
- package/apps/runtime/dist/cli/setup.js +42 -38
- package/apps/runtime/dist/cli/start.js +16 -15
- package/apps/runtime/dist/cli/status.js +20 -20
- package/apps/runtime/dist/cli/ui.js +9 -2
- package/apps/runtime/dist/cli/update.js +6 -6
- package/apps/runtime/dist/cli/validate.js +1 -1
- package/apps/runtime/dist/connectors/external.js +3 -2
- package/apps/runtime/dist/connectors/index.js +5 -5
- package/apps/runtime/dist/connectors/plugin-connections.js +2 -2
- package/apps/runtime/dist/connectors/via-account.js +5 -5
- package/apps/runtime/dist/db/control-schema.js +14 -0
- package/apps/runtime/dist/db/migrations/control/0009_plugin_claim.sql +9 -0
- package/apps/runtime/dist/db/migrations/control/meta/0009_snapshot.json +561 -0
- package/apps/runtime/dist/db/migrations/control/meta/_journal.json +7 -0
- package/apps/runtime/dist/db/schema.js +9 -9
- package/apps/runtime/dist/docs/dev/index.md +16 -14
- package/apps/runtime/dist/docs/dev/plugins/connections.md +27 -24
- package/apps/runtime/dist/docs/dev/plugins/events.md +19 -17
- package/apps/runtime/dist/docs/dev/plugins/examples.md +13 -12
- package/apps/runtime/dist/docs/dev/plugins/index.md +29 -24
- package/apps/runtime/dist/docs/dev/plugins/layout.md +20 -19
- package/apps/runtime/dist/docs/dev/plugins/quick-start.md +16 -15
- package/apps/runtime/dist/docs/dev/plugins/reference.md +52 -40
- package/apps/runtime/dist/docs/dev/plugins/reloading.md +24 -23
- package/apps/runtime/dist/docs/dev/plugins/routes.md +26 -22
- package/apps/runtime/dist/docs/dev/plugins/runners.md +49 -44
- package/apps/runtime/dist/docs/dev/plugins/server.md +95 -43
- package/apps/runtime/dist/docs/dev/plugins/shipping.md +19 -16
- package/apps/runtime/dist/docs/dev/plugins/space.md +75 -69
- package/apps/runtime/dist/docs/dev/plugins/starters.md +22 -17
- package/apps/runtime/dist/docs/dev/plugins/studio.md +80 -37
- package/apps/runtime/dist/docs/dev/plugins/styling.md +44 -41
- package/apps/runtime/dist/docs/dev/plugins/tables.md +11 -11
- package/apps/runtime/dist/docs/dev/plugins/tools.md +26 -25
- package/apps/runtime/dist/docs/dev/wizard-format.md +35 -23
- package/apps/runtime/dist/docs/user/ai-apps.md +46 -43
- package/apps/runtime/dist/docs/user/assistant.md +41 -36
- package/apps/runtime/dist/docs/user/build-a-wizard.md +64 -39
- package/apps/runtime/dist/docs/user/command-line.md +47 -44
- package/apps/runtime/dist/docs/user/connectors.md +40 -35
- package/apps/runtime/dist/docs/user/create-account.md +27 -22
- package/apps/runtime/dist/docs/user/data.md +29 -29
- package/apps/runtime/dist/docs/user/first-start.md +26 -28
- package/apps/runtime/dist/docs/user/index.md +16 -17
- package/apps/runtime/dist/docs/user/install.md +28 -27
- package/apps/runtime/dist/docs/user/local-only.md +21 -17
- package/apps/runtime/dist/docs/user/models.md +92 -84
- package/apps/runtime/dist/docs/user/plugins.md +28 -23
- package/apps/runtime/dist/docs/user/run.md +51 -50
- package/apps/runtime/dist/docs/user/server.md +9 -9
- package/apps/runtime/dist/docs/user/space.md +87 -65
- package/apps/runtime/dist/docs/user/steps.md +73 -69
- package/apps/runtime/dist/docs/user/test-and-share.md +127 -105
- package/apps/runtime/dist/docs/user/troubleshooting.md +40 -38
- package/apps/runtime/dist/docs/user/wordpress.md +62 -54
- package/apps/runtime/dist/documents/parse.js +2 -2
- package/apps/runtime/dist/engenty/shims/web-ingest.js +17 -1
- package/apps/runtime/dist/engine/input.js +4 -4
- package/apps/runtime/dist/engine/push.js +2 -2
- package/apps/runtime/dist/engine/runner.js +8 -8
- package/apps/runtime/dist/engine/steps.js +4 -4
- package/apps/runtime/dist/engine/surface.js +1 -1
- package/apps/runtime/dist/film/step.js +3 -3
- package/apps/runtime/dist/harness/claude.js +1 -1
- package/apps/runtime/dist/harness/codex.js +1 -1
- package/apps/runtime/dist/harness/model.js +2 -2
- package/apps/runtime/dist/harness/terminal.js +3 -3
- package/apps/runtime/dist/mcp/handler.js +2 -2
- package/apps/runtime/dist/mcp/tools.js +9 -9
- package/apps/runtime/dist/models.js +15 -15
- package/apps/runtime/dist/plugins/claims.js +56 -0
- package/apps/runtime/dist/plugins/loader.js +2 -0
- package/apps/runtime/dist/plugins/public.js +2 -3
- package/apps/runtime/dist/plugins/runs.js +13 -8
- package/apps/runtime/dist/render/chromium.js +24 -11
- package/apps/runtime/dist/routes/api.js +16 -10
- package/apps/runtime/dist/routes/debug.js +1 -1
- package/apps/runtime/dist/routes/delivery.js +46 -0
- package/apps/runtime/dist/routes/lang.js +7 -0
- package/apps/runtime/dist/routes/marketplace.js +2 -1
- package/apps/runtime/dist/routes/plugin-public.js +2 -1
- package/apps/runtime/dist/routes/plugins.js +13 -14
- package/apps/runtime/dist/routes/runs.js +59 -49
- package/apps/runtime/dist/routes/studio.js +58 -28
- package/apps/runtime/dist/services/access.js +3 -3
- package/apps/runtime/dist/services/assistant-chats.js +1 -1
- package/apps/runtime/dist/services/brand.js +2 -2
- package/apps/runtime/dist/services/cloud.js +5 -4
- package/apps/runtime/dist/services/errors.js +2 -1
- package/apps/runtime/dist/services/knowledge-model.js +10 -10
- package/apps/runtime/dist/services/knowledge.js +19 -19
- package/apps/runtime/dist/services/marketplace.js +2 -2
- package/apps/runtime/dist/services/package-file.js +12 -9
- package/apps/runtime/dist/services/package.js +3 -3
- package/apps/runtime/dist/services/project-files.js +15 -17
- package/apps/runtime/dist/services/project-index.js +10 -10
- package/apps/runtime/dist/services/projects.js +13 -8
- package/apps/runtime/dist/services/relevance.js +3 -3
- package/apps/runtime/dist/services/runners.js +4 -3
- package/apps/runtime/dist/services/runs.js +3 -3
- package/apps/runtime/dist/services/site.js +10 -10
- package/apps/runtime/dist/services/space-categories.js +22 -22
- package/apps/runtime/dist/services/space-data.js +16 -16
- package/apps/runtime/dist/services/space-origin.js +4 -4
- package/apps/runtime/dist/services/spaces.js +10 -10
- package/apps/runtime/dist/services/wizards.js +5 -5
- package/apps/runtime/dist/tools/index.js +1 -1
- package/apps/runtime/dist/tools/net-guard.js +135 -24
- package/apps/runtime/dist/tools/pages.js +4 -4
- package/apps/runtime/dist/tools/plugin.js +1 -1
- package/apps/runtime/dist/tools/project.js +10 -10
- package/apps/runtime/dist/tools/store.js +1 -1
- package/apps/runtime/dist/widgets/step.js +1 -1
- package/apps/web/dist/assets/AboutDialog-DaYwk84X.js +1 -0
- package/apps/web/dist/assets/{ChangelogDialog-Cm25slr3.js → ChangelogDialog-CsYbEayb.js} +1 -1
- package/apps/web/dist/assets/{ChatView-BXtXWnqK.js → ChatView-BRD3fKZU.js} +1 -1
- package/apps/web/dist/assets/EditorPage-ocPlZD3E.js +3 -0
- package/apps/web/dist/assets/{FlowPreview-BsfjDVv8.js → FlowPreview-Bm7sv746.js} +1 -1
- package/apps/web/dist/assets/{Harness-B0hD5Pp7.js → Harness-DKE-YNA5.js} +1 -1
- package/apps/web/dist/assets/{NewWizardChannels-DMDp9Ao5.js → NewWizardChannels-CnWckqjO.js} +1 -1
- package/apps/web/dist/assets/{NewWizardPage-aOisxyk2.js → NewWizardPage-BXTpbHVM.js} +1 -1
- package/apps/web/dist/assets/{OnboardingPage-Czb0pOyy.js → OnboardingPage-3Rz5uOsa.js} +1 -1
- package/apps/web/dist/assets/{ProjectPage-DPCtb42K.js → ProjectPage-QdSJwm3k.js} +1 -1
- package/apps/web/dist/assets/{PublicRunner-CvyA-gkf.js → PublicRunner-DEpBer5g.js} +1 -1
- package/apps/web/dist/assets/ReadOnly-rr_RJD-a.js +1 -0
- package/apps/web/dist/assets/{RunDrawer-BCiPEz9g.js → RunDrawer-BsZt0tL9.js} +1 -1
- package/apps/web/dist/assets/{RunnerView-4z6DMzE6.js → RunnerView-BbRukEdE.js} +1 -1
- package/apps/web/dist/assets/SettingsPage-qkWsQIMN.js +1 -0
- package/apps/web/dist/assets/{SetupPage-YFoU_nXK.js → SetupPage-PzvPPLfM.js} +1 -1
- package/apps/web/dist/assets/{SharePage-BBHiJ34e.js → SharePage-7vdDWG1n.js} +1 -1
- package/apps/web/dist/assets/changelog-BFdAYD28.js +1 -0
- package/apps/web/dist/assets/{esm-hSq-jeOG.js → esm-g0kaGp8t.js} +1 -1
- package/apps/web/dist/assets/{index-CSeBGwy0.css → index-Bi2x2g7n.css} +1 -1
- package/apps/web/dist/assets/{index-DB_iwB35.js → index-BzPid1oQ.js} +4 -4
- package/apps/web/dist/assets/lock-BK9RYIft.js +1 -0
- package/apps/web/dist/assets/oss-credits-BJrR7BR1.js +1 -0
- package/apps/web/dist/assets/{phone-CeqyvwK-.js → phone-DtkY0NfU.js} +1 -1
- package/apps/web/dist/assets/{settings-menu-BK8WpcGv.js → settings-menu-7EJQRFOi.js} +1 -1
- package/apps/web/dist/assets/{ui-BbIh6SOJ.js → ui-B6S5hYPd.js} +3 -3
- package/apps/web/dist/assets/{upload-BQrAlHIG.js → upload-rYCAy7ox.js} +1 -1
- package/apps/web/dist/index.html +3 -3
- package/apps/web/dist/mcp/flow.html +4 -4
- package/bin/wizards.mjs +1 -1
- package/node_modules/@engenty-wizards/plugin-sdk/dist/index.d.ts +33 -14
- package/node_modules/@engenty-wizards/plugin-sdk/dist/studio.d.ts +3 -3
- package/node_modules/@engenty-wizards/shared/dist/definition.d.ts +690 -690
- package/node_modules/@engenty-wizards/shared/dist/definition.js +1 -1
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns.d.ts +76 -76
- package/node_modules/@engenty-wizards/shared/dist/feature-flags.d.ts +3 -3
- package/node_modules/@engenty-wizards/shared/dist/feature-flags.js +3 -3
- package/node_modules/@engenty-wizards/shared/dist/knowledge.d.ts +13 -13
- package/node_modules/@engenty-wizards/shared/dist/knowledge.js +4 -4
- package/node_modules/@engenty-wizards/shared/dist/marketplace.d.ts +4 -4
- package/node_modules/@engenty-wizards/shared/dist/projects.js +1 -1
- package/node_modules/@engenty-wizards/shared/dist/run.d.ts +48 -48
- package/node_modules/@engenty-wizards/shared/dist/run.js +48 -48
- package/node_modules/@engenty-wizards/shared/dist/space-data.d.ts +2 -2
- package/node_modules/@engenty-wizards/shared/dist/surface.d.ts +20 -20
- package/node_modules/@engenty-wizards/shared/dist/wizard-package.js +1 -1
- package/package.json +5 -4
- package/apps/web/dist/assets/AboutDialog-BtAuA44m.js +0 -1
- package/apps/web/dist/assets/EditorPage-B4jX_Ckc.js +0 -3
- package/apps/web/dist/assets/ReadOnly-DFysMEMF.js +0 -1
- package/apps/web/dist/assets/SettingsPage-BKkOfz6P.js +0 -1
- package/apps/web/dist/assets/changelog-D8WjOq7d.js +0 -1
- package/apps/web/dist/assets/circle-alert-BVj6idzu.js +0 -1
- package/apps/web/dist/assets/lock-BoWS-JhN.js +0 -1
- package/apps/web/dist/assets/oss-credits-BQQX0kvk.js +0 -1
|
@@ -31,7 +31,7 @@ export function currentRole() {
|
|
|
31
31
|
return actor.getStore() ?? "owner";
|
|
32
32
|
}
|
|
33
33
|
export const isAdminRole = (role) => role === "owner" || role === "admin";
|
|
34
|
-
export const MEMBERS_EDIT_ONLY = bilingual("Das dürfen nur Admins des Teams. Als Mitglied bearbeitest du die Wizards, die es gibt.", "Only
|
|
34
|
+
export const MEMBERS_EDIT_ONLY = bilingual("Das dürfen nur Admins des Teams. Als Mitglied bearbeitest du die Wizards, die es schon gibt.", "Only team admins can do that. As a member, you can edit the wizards that already exist.");
|
|
35
35
|
/** Refuses a member: making and deleting is for the owner and the admins of the team. */
|
|
36
36
|
export async function requireAdmin() {
|
|
37
37
|
if (syncing() || !managed || isAdminRole(currentRole())) {
|
|
@@ -52,8 +52,8 @@ export async function mayBuild() {
|
|
|
52
52
|
// An older Manage-App does not say: then it builds. One that cannot be reached: nothing changes.
|
|
53
53
|
return info ? info.limits.build !== false : false;
|
|
54
54
|
}
|
|
55
|
-
export const READ_ONLY_PROJECT = bilingual("
|
|
56
|
-
export const READ_ONLY_TENANT = bilingual("Hier
|
|
55
|
+
export const READ_ONLY_PROJECT = bilingual("Diesen Space bearbeitest du in engenty wizards auf deinem Computer, nicht hier.", "You edit this space in engenty wizards on your computer, not here.");
|
|
56
|
+
export const READ_ONLY_TENANT = bilingual("Hier laufen die Wizards, die du in engenty wizards auf deinem Computer veröffentlichst. Gebaut und bearbeitet wird dort.", "Wizards run here once you publish them in engenty wizards on your computer. You build and edit them there.");
|
|
57
57
|
/** Refuses when the tenant builds nothing here. */
|
|
58
58
|
export async function requireBuild() {
|
|
59
59
|
if (syncing() || (await mayBuild())) {
|
|
@@ -253,7 +253,7 @@ export async function readChat(user, chatId) {
|
|
|
253
253
|
where: and(mine(user), eq(schema.assistantChat.id, chatId)),
|
|
254
254
|
});
|
|
255
255
|
if (!chat) {
|
|
256
|
-
throw new ServiceError("not_found", bilingual("Diesen Chat gibt es nicht.", "
|
|
256
|
+
throw new ServiceError("not_found", bilingual("Diesen Chat gibt es nicht.", "This chat doesn’t exist."));
|
|
257
257
|
}
|
|
258
258
|
return {
|
|
259
259
|
id: chat.id,
|
|
@@ -66,14 +66,14 @@ export async function describeGradient(brand, text, current) {
|
|
|
66
66
|
answer = result.output;
|
|
67
67
|
}
|
|
68
68
|
catch {
|
|
69
|
-
throw new ServiceError("invalid", bilingual("Daraus ließ sich kein Verlauf machen. Beschreib ihn
|
|
69
|
+
throw new ServiceError("invalid", bilingual("Daraus ließ sich kein Verlauf machen. Beschreib ihn anders.", "Couldn’t make a gradient from that. Try describing it differently."));
|
|
70
70
|
}
|
|
71
71
|
const stops = answer.stops
|
|
72
72
|
.filter((s) => HEX.test(s.color))
|
|
73
73
|
.slice(0, 12)
|
|
74
74
|
.map((s) => ({ color: s.color.toLowerCase(), at: Math.min(100, Math.max(0, s.at)) }));
|
|
75
75
|
if (stops.length < 2) {
|
|
76
|
-
throw new ServiceError("invalid", bilingual("Daraus ließ sich kein Verlauf machen. Beschreib ihn
|
|
76
|
+
throw new ServiceError("invalid", bilingual("Daraus ließ sich kein Verlauf machen. Beschreib ihn anders.", "Couldn’t make a gradient from that. Try describing it differently."));
|
|
77
77
|
}
|
|
78
78
|
return {
|
|
79
79
|
name: answer.name.trim().slice(0, 80),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import { bilingual } from "@engenty-wizards/shared/run";
|
|
2
3
|
import { and, asc, eq, inArray, isNotNull, isNull, like } from "drizzle-orm";
|
|
3
4
|
import { accountToken, linkedAccount } from "../auth/account.js";
|
|
4
5
|
import { control, controlDb, db, schema } from "../db/client.js";
|
|
@@ -25,7 +26,7 @@ export async function cloudState(wizardId) {
|
|
|
25
26
|
async function call(method, path, body, timeoutMs = 30_000) {
|
|
26
27
|
const token = await accountToken();
|
|
27
28
|
if (!token) {
|
|
28
|
-
throw new ServiceError("refused", "The account is signed out.
|
|
29
|
+
throw new ServiceError("refused", bilingual("Das Konto ist abgemeldet. Melde dich neu an.", "The account is signed out. Sign in again."), {
|
|
29
30
|
reason: "signed_out",
|
|
30
31
|
});
|
|
31
32
|
}
|
|
@@ -39,7 +40,7 @@ async function call(method, path, body, timeoutMs = 30_000) {
|
|
|
39
40
|
signal: AbortSignal.timeout(timeoutMs),
|
|
40
41
|
}).catch(() => null);
|
|
41
42
|
if (!res) {
|
|
42
|
-
throw new ServiceError("refused", "The cloud
|
|
43
|
+
throw new ServiceError("refused", bilingual("Die Cloud ist gerade nicht erreichbar.", "The cloud can’t be reached right now."), {
|
|
43
44
|
reason: "unreachable",
|
|
44
45
|
});
|
|
45
46
|
}
|
|
@@ -49,7 +50,7 @@ async function call(method, path, body, timeoutMs = 30_000) {
|
|
|
49
50
|
function refusal(answer, fallback) {
|
|
50
51
|
const { error, reason, ...rest } = answer.body;
|
|
51
52
|
const message = answer.status === 401
|
|
52
|
-
? "The account is signed out.
|
|
53
|
+
? bilingual("Das Konto ist abgemeldet. Melde dich neu an.", "The account is signed out. Sign in again.")
|
|
53
54
|
: typeof error === "string" && error
|
|
54
55
|
? error
|
|
55
56
|
: fallback;
|
|
@@ -213,7 +214,7 @@ export async function syncToCloud(userId, wizardId) {
|
|
|
213
214
|
const w = await ownedWizard(userId, wizardId);
|
|
214
215
|
const version = await publishedVersion(w);
|
|
215
216
|
if (!version) {
|
|
216
|
-
throw new ServiceError("refused", "Publish the wizard first.");
|
|
217
|
+
throw new ServiceError("refused", bilingual("Veröffentliche den Wizard zuerst.", "Publish the wizard first."));
|
|
217
218
|
}
|
|
218
219
|
const before = await cloudState(w.id);
|
|
219
220
|
try {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { bilingual } from "@engenty-wizards/shared/run";
|
|
1
2
|
import { errorText, NotedError } from "../model-errors.js";
|
|
2
3
|
const STATUS = {
|
|
3
4
|
not_found: 404,
|
|
@@ -25,4 +26,4 @@ export class ServiceError extends NotedError {
|
|
|
25
26
|
return { error: errorText(this, lang), code: this.code, ...this.data };
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
|
-
export const notFound = () => new ServiceError("not_found", "
|
|
29
|
+
export const notFound = () => new ServiceError("not_found", bilingual("Das gibt es nicht oder nicht mehr.", "This doesn’t exist or was deleted."));
|
|
@@ -256,7 +256,7 @@ export async function dropConverted(fileId) {
|
|
|
256
256
|
.where(and(eq(schema.spaceTable.fileId, fileId), eq(schema.spaceTable.kept, false), isNull(schema.spaceTable.originKey)));
|
|
257
257
|
}
|
|
258
258
|
/**
|
|
259
|
-
* Reads an uploaded document into
|
|
259
|
+
* Reads an uploaded document into Knowledge: a table per sheet of a workbook, else a page — with
|
|
260
260
|
* sub-pages when it is long and has headings of its own. The file stays as the original. A file
|
|
261
261
|
* nothing can read stays a file; UnreadableDocument says why.
|
|
262
262
|
*/
|
|
@@ -386,13 +386,13 @@ export async function convertDocument(file, data) {
|
|
|
386
386
|
items: [`p:${parentId}`],
|
|
387
387
|
};
|
|
388
388
|
}
|
|
389
|
-
// ---
|
|
389
|
+
// --- Categories a model fills ---------------------------------------------------------------
|
|
390
390
|
/**
|
|
391
|
-
* Fills the space's
|
|
391
|
+
* Fills the space's categories of an item from its text, where the space has any and nothing
|
|
392
392
|
* set them yet. Values a person set are never touched. True when anything was set.
|
|
393
393
|
*/
|
|
394
394
|
export async function fillCategories(projectId, ref, text, name) {
|
|
395
|
-
// A column's
|
|
395
|
+
// A column's category comes from its cells, not from a model.
|
|
396
396
|
const categories = (await categoryRows(projectId)).filter((c) => !(c.proposed || c.tableId));
|
|
397
397
|
if (!(categories.length && text.trim())) {
|
|
398
398
|
return false;
|
|
@@ -443,9 +443,9 @@ export async function fillCategories(projectId, ref, text, name) {
|
|
|
443
443
|
const { changed } = await setItemCategories(projectId, ref, input, "model");
|
|
444
444
|
return changed;
|
|
445
445
|
}
|
|
446
|
-
// --- the
|
|
446
|
+
// --- the summary of a value -----------------------------------------------------------------
|
|
447
447
|
const SUMMARY_ITEMS = 40;
|
|
448
|
-
/** What a value's items say, for the model that writes its
|
|
448
|
+
/** What a value's items say, for the model that writes its summary. */
|
|
449
449
|
async function itemsOfValue(valueId) {
|
|
450
450
|
const rows = await db.query.spaceItemCategory.findMany({
|
|
451
451
|
where: eq(schema.spaceItemCategory.valueId, valueId),
|
|
@@ -496,7 +496,7 @@ async function itemsOfValue(valueId) {
|
|
|
496
496
|
return { items: out, total: rows.length };
|
|
497
497
|
}
|
|
498
498
|
/**
|
|
499
|
-
* Writes the
|
|
499
|
+
* Writes the summary of a value: what its items are and what they say, so "which Zuschüsse are
|
|
500
500
|
* there?" finds one page that answers it. Kept on the value and indexed like a page.
|
|
501
501
|
*/
|
|
502
502
|
export async function writeSummary(valueId) {
|
|
@@ -504,12 +504,12 @@ export async function writeSummary(valueId) {
|
|
|
504
504
|
where: eq(schema.spaceCategoryValue.id, valueId),
|
|
505
505
|
});
|
|
506
506
|
if (!value) {
|
|
507
|
-
throw new ServiceError("not_found", bilingual("Diesen Wert gibt es nicht.", "
|
|
507
|
+
throw new ServiceError("not_found", bilingual("Diesen Wert gibt es nicht.", "This value doesn’t exist."));
|
|
508
508
|
}
|
|
509
509
|
const category = await categoryRow(value.categoryId);
|
|
510
510
|
const { items, total } = await itemsOfValue(value.id);
|
|
511
511
|
if (!items.length) {
|
|
512
|
-
throw new ServiceError("invalid", "
|
|
512
|
+
throw new ServiceError("invalid", bilingual("Unter diesem Wert steht noch nichts.", "Nothing is filed under this value yet."));
|
|
513
513
|
}
|
|
514
514
|
const model = await textModel("standard");
|
|
515
515
|
const result = await generateText({
|
|
@@ -530,7 +530,7 @@ export async function writeSummary(valueId) {
|
|
|
530
530
|
queueIndex(`v:${value.id}`);
|
|
531
531
|
return summary;
|
|
532
532
|
}
|
|
533
|
-
/** After a source wrote: the values it filled that have three items or more get their
|
|
533
|
+
/** After a source wrote: the values it filled that have three items or more get their summary. */
|
|
534
534
|
export async function summarizeValues(projectId, categoryName, value) {
|
|
535
535
|
const categories = (await categoryRows(projectId)).filter((c) => c.type === "choice" &&
|
|
536
536
|
!c.proposed &&
|
|
@@ -172,7 +172,7 @@ async function convertedFiles(projectId) {
|
|
|
172
172
|
]);
|
|
173
173
|
return new Set([...pages, ...tables].map((r) => r.id));
|
|
174
174
|
}
|
|
175
|
-
/** Everything
|
|
175
|
+
/** Everything Knowledge holds: pages (sub-pages counted under them), tables, files, categories. */
|
|
176
176
|
export async function listKnowledge(userId, projectId) {
|
|
177
177
|
const project = await ownedProject(userId, projectId);
|
|
178
178
|
const [tree, tables, rowCounts, files, converted, categories, embeddings, checker] = await Promise.all([
|
|
@@ -277,7 +277,7 @@ export async function listKnowledge(userId, projectId) {
|
|
|
277
277
|
items.sort((a, b) => a.title.localeCompare(b.title, "de"));
|
|
278
278
|
return { items, categories, embeddings, checked: checker };
|
|
279
279
|
}
|
|
280
|
-
/** A value opened in the studio: its
|
|
280
|
+
/** A value opened in the studio: its summary, and the items and rows that have it. */
|
|
281
281
|
export async function valueContents(userId, valueId) {
|
|
282
282
|
const value = await db.query.spaceCategoryValue.findFirst({
|
|
283
283
|
where: eq(schema.spaceCategoryValue.id, valueId),
|
|
@@ -303,7 +303,7 @@ export async function valueContents(userId, valueId) {
|
|
|
303
303
|
...(await itemsHaving(userId, project.id, having)),
|
|
304
304
|
};
|
|
305
305
|
}
|
|
306
|
-
/** Everything that has a
|
|
306
|
+
/** Everything that has a category, whatever its value: what the category's page lists. */
|
|
307
307
|
export async function categoryContents(userId, categoryId) {
|
|
308
308
|
const category = await db.query.spaceCategory.findFirst({
|
|
309
309
|
where: eq(schema.spaceCategory.id, categoryId),
|
|
@@ -377,7 +377,7 @@ async function itemsHaving(userId, projectId, having) {
|
|
|
377
377
|
})),
|
|
378
378
|
};
|
|
379
379
|
}
|
|
380
|
-
/** What the studio shows around a page of
|
|
380
|
+
/** What the studio shows around a page of Knowledge: where it stands, its sub-pages, its origin. */
|
|
381
381
|
export async function knowledgePage(page) {
|
|
382
382
|
const tree = await pageTree(page.projectId);
|
|
383
383
|
const node = tree.get(page.id);
|
|
@@ -443,7 +443,7 @@ function sectionOf(markdown, wanted) {
|
|
|
443
443
|
return start >= 0 ? lines.slice(start).join("\n").trim() : null;
|
|
444
444
|
}
|
|
445
445
|
/**
|
|
446
|
-
* A page as a step reads it: front matter (title, path,
|
|
446
|
+
* A page as a step reads it: front matter (title, path, categories, where it came from, its
|
|
447
447
|
* original, its parent and sub-pages) and its Markdown, or one section of it. Long pages come in
|
|
448
448
|
* parts: `from` continues where the last part ended.
|
|
449
449
|
*/
|
|
@@ -472,7 +472,7 @@ export async function readPage(projectId, path, opts = {}) {
|
|
|
472
472
|
...(node?.parentId ? [`parent: ${tree.get(node.parentId)?.path}`] : []),
|
|
473
473
|
...(lines.length
|
|
474
474
|
? [
|
|
475
|
-
"
|
|
475
|
+
"categories:",
|
|
476
476
|
...lines.map((l) => ` ${l.replace(/^([^:]+): (.*)$/, (_m, k, v) => `${yamlValue(k)}: ${yamlValue(v)}`)}`),
|
|
477
477
|
]
|
|
478
478
|
: []),
|
|
@@ -586,15 +586,15 @@ export async function readTable(projectId, path, opts = {}) {
|
|
|
586
586
|
};
|
|
587
587
|
}
|
|
588
588
|
/**
|
|
589
|
-
* For project_list: without a filter, the
|
|
590
|
-
* has; with one, the items it leaves, with their paths and
|
|
589
|
+
* For project_list: without a filter, the categories with their values and how many items each
|
|
590
|
+
* has; with one, the items it leaves, with their paths and categories.
|
|
591
591
|
*/
|
|
592
592
|
export async function listForStep(projectId, where) {
|
|
593
593
|
const categories = await listCategories(projectId);
|
|
594
594
|
const taken = categories.filter((c) => !c.proposed);
|
|
595
595
|
if (!Object.keys(where ?? {}).length) {
|
|
596
596
|
return {
|
|
597
|
-
|
|
597
|
+
categories: taken.map((c) => ({
|
|
598
598
|
name: c.name,
|
|
599
599
|
type: c.type,
|
|
600
600
|
...(c.unit ? { unit: c.unit } : {}),
|
|
@@ -640,27 +640,27 @@ export async function listForStep(projectId, where) {
|
|
|
640
640
|
.map((n) => ({
|
|
641
641
|
path: n.path,
|
|
642
642
|
title: n.title,
|
|
643
|
-
|
|
643
|
+
categories: categoryLines(taken, cats.get(`p:${n.id}`)),
|
|
644
644
|
})),
|
|
645
645
|
...[...(filtered?.tables ?? [])].map((id) => ({
|
|
646
646
|
path: tPaths.get(id) ?? "",
|
|
647
647
|
title: "",
|
|
648
|
-
|
|
648
|
+
categories: categoryLines(taken, cats.get(`t:${id}`)),
|
|
649
649
|
})),
|
|
650
650
|
...[...rowTables].map(([id, n]) => ({
|
|
651
651
|
path: tPaths.get(id) ?? "",
|
|
652
652
|
title: `${n} rows match: read them with table_read and the same values as its columns`,
|
|
653
|
-
|
|
653
|
+
categories: [],
|
|
654
654
|
})),
|
|
655
655
|
...[...(filtered?.files ?? [])].map((id) => ({
|
|
656
656
|
path: fPaths.get(id) ?? "",
|
|
657
657
|
title: "",
|
|
658
|
-
|
|
658
|
+
categories: categoryLines(taken, cats.get(`f:${id}`)),
|
|
659
659
|
})),
|
|
660
660
|
];
|
|
661
661
|
return { items: items.slice(0, 200), total: items.length };
|
|
662
662
|
}
|
|
663
|
-
/** How much
|
|
663
|
+
/** How much Knowledge a space holds: what the block of the steps' instructions says. */
|
|
664
664
|
export async function knowledgeCounts(projectId) {
|
|
665
665
|
const [pages, tables, files] = await Promise.all([
|
|
666
666
|
db
|
|
@@ -679,8 +679,8 @@ export async function knowledgeCounts(projectId) {
|
|
|
679
679
|
return { pages: pages[0].n, tables: tables[0].n, files: files[0].n };
|
|
680
680
|
}
|
|
681
681
|
/**
|
|
682
|
-
* What every step of the space is told about
|
|
683
|
-
* by, the tools to find and read. Empty when
|
|
682
|
+
* What every step of the space is told about Knowledge: how much there is, the categories to filter
|
|
683
|
+
* by, the tools to find and read. Empty when Knowledge is.
|
|
684
684
|
*/
|
|
685
685
|
export async function knowledgeBlock(projectId) {
|
|
686
686
|
const counts = await knowledgeCounts(projectId);
|
|
@@ -708,14 +708,14 @@ export async function knowledgeBlock(projectId) {
|
|
|
708
708
|
return `- ${c.name} (${type})${values}`;
|
|
709
709
|
});
|
|
710
710
|
return [
|
|
711
|
-
"#
|
|
711
|
+
"# KNOWLEDGE — WHAT THE SPACE KNOWS",
|
|
712
712
|
`Ground truth the space's owner keeps for all wizards: ${what}. Use it when the task touches what it covers, and prefer it to what you remember.`,
|
|
713
713
|
...(lines.length
|
|
714
714
|
? [
|
|
715
|
-
"
|
|
715
|
+
"Categories, to filter with `where` in project_search and project_list before searching:",
|
|
716
716
|
...lines,
|
|
717
717
|
]
|
|
718
718
|
: []),
|
|
719
|
-
"Find with project_search (a question; `where` on
|
|
719
|
+
"Find with project_search (a question; `where` on categories). List with project_list. Read whole with page_read and table_read by path. A page marks the pages of its original as <!-- S. n -->: quote them when the source matters.",
|
|
720
720
|
].join("\n");
|
|
721
721
|
}
|
|
@@ -404,13 +404,13 @@ export async function marketplaceWizard(id, lang) {
|
|
|
404
404
|
return null;
|
|
405
405
|
}
|
|
406
406
|
if (!readable(found.definition)) {
|
|
407
|
-
throw new ServiceError("refused", bilingual("Diese Vorlage braucht eine neuere Version
|
|
407
|
+
throw new ServiceError("refused", bilingual("Diese Vorlage braucht eine neuere Version von engenty wizards. Aktualisiere zuerst engenty wizards.", "This template needs a newer version of engenty wizards. Update engenty wizards first."));
|
|
408
408
|
}
|
|
409
409
|
// A wizard whose plugins are not here would stop at its first step that uses them.
|
|
410
410
|
const have = await pluginIdsOf(currentTenant());
|
|
411
411
|
const needs = pluginsOf(wizardSchema.parse(found.definition)).filter((p) => !have.has(p));
|
|
412
412
|
if (needs.length) {
|
|
413
|
-
throw new ServiceError("refused", bilingual(`Diese Vorlage braucht ${needs.length === 1 ? "das Plugin" : "die Plugins"} ${needs.join(", ")}:
|
|
413
|
+
throw new ServiceError("refused", bilingual(`Diese Vorlage braucht ${needs.length === 1 ? "das Plugin" : "die Plugins"} ${needs.join(", ")}: bei Pro auf engenty.ai dabei.`, `This template needs the ${needs.length === 1 ? "plugin" : "plugins"} ${needs.join(", ")}: included with Pro on engenty.ai.`));
|
|
414
414
|
}
|
|
415
415
|
return {
|
|
416
416
|
id: found.id,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { bilingual } from "@engenty-wizards/shared/run";
|
|
1
2
|
import { manifestSchema, PACKAGE_FILES, PACKAGE_FORMAT, PACKAGE_MANIFEST, PACKAGE_MAX_BYTES, } from "@engenty-wizards/shared/wizard-package";
|
|
2
3
|
import { cleanPath, WORKSPACE_LIMITS } from "@engenty-wizards/shared/workspace";
|
|
3
4
|
import { strFromU8, unzipSync } from "fflate";
|
|
@@ -6,13 +7,13 @@ import { ServiceError } from "./errors.js";
|
|
|
6
7
|
* Reading a `.wizard` package, with nothing of a project or a database: the studio's import and
|
|
7
8
|
* `wizards validate` both start here.
|
|
8
9
|
*/
|
|
9
|
-
const invalid = (
|
|
10
|
+
const invalid = (said) => new ServiceError("invalid", said);
|
|
10
11
|
export function json(data) {
|
|
11
12
|
try {
|
|
12
13
|
return JSON.parse(strFromU8(data));
|
|
13
14
|
}
|
|
14
15
|
catch {
|
|
15
|
-
throw invalid("This
|
|
16
|
+
throw invalid(bilingual("Das ist keine Wizard-Datei: Die wizard.json darin lässt sich nicht lesen.", "This isn’t a wizard file: its wizard.json can’t be read."));
|
|
16
17
|
}
|
|
17
18
|
}
|
|
18
19
|
/** A manifest, or a bare definition: a wizard without workspace is fully told by its JSON. */
|
|
@@ -22,7 +23,7 @@ export function readManifest(raw) {
|
|
|
22
23
|
}
|
|
23
24
|
const parsed = manifestSchema.safeParse(raw);
|
|
24
25
|
if (!parsed.success) {
|
|
25
|
-
throw invalid("This wizard
|
|
26
|
+
throw invalid(bilingual("Diese Wizard-Datei stammt aus einer neueren Version oder ist beschädigt. Aktualisiere engenty wizards oder exportiere den Wizard neu.", "This wizard file comes from a newer version, or it’s damaged. Update engenty wizards or export the wizard again."));
|
|
26
27
|
}
|
|
27
28
|
return parsed.data;
|
|
28
29
|
}
|
|
@@ -45,24 +46,26 @@ export function readPackage(data) {
|
|
|
45
46
|
total += file.originalSize;
|
|
46
47
|
count += 1;
|
|
47
48
|
if (file.originalSize > WORKSPACE_LIMITS.fileBytes) {
|
|
48
|
-
throw invalid(`${file.name} is larger than 5 MB.`);
|
|
49
|
+
throw invalid(bilingual(`${file.name} ist größer als 5 MB.`, `${file.name} is larger than 5 MB.`));
|
|
49
50
|
}
|
|
50
51
|
if (count > WORKSPACE_LIMITS.files + 1 || total > PACKAGE_MAX_BYTES) {
|
|
51
|
-
throw invalid("The
|
|
52
|
+
throw invalid(bilingual("Die Wizard-Datei ist zu groß: Ein Arbeitsbereich fasst höchstens 200 Dateien und 25 MB.", "The wizard file is too large: a workspace holds at most 200 files and 25 MB."));
|
|
52
53
|
}
|
|
53
54
|
return true;
|
|
54
55
|
},
|
|
55
56
|
});
|
|
56
57
|
}
|
|
57
58
|
catch (err) {
|
|
58
|
-
throw err instanceof ServiceError
|
|
59
|
+
throw err instanceof ServiceError
|
|
60
|
+
? err
|
|
61
|
+
: invalid(bilingual("Die Wizard-Datei lässt sich nicht öffnen. Exportiere den Wizard neu und versuche es noch einmal.", "The wizard file can’t be opened. Export the wizard again and try once more."));
|
|
59
62
|
}
|
|
60
63
|
// A package unpacked and zipped again often sits in a folder of its own.
|
|
61
64
|
const manifestPath = Object.keys(entries)
|
|
62
65
|
.filter((name) => name === PACKAGE_MANIFEST || name.endsWith(`/${PACKAGE_MANIFEST}`))
|
|
63
66
|
.sort((a, b) => a.length - b.length)[0];
|
|
64
67
|
if (manifestPath === undefined) {
|
|
65
|
-
throw invalid("This
|
|
68
|
+
throw invalid(bilingual("Das ist keine Wizard-Datei: Die wizard.json fehlt.", "This isn’t a wizard file: wizard.json is missing."));
|
|
66
69
|
}
|
|
67
70
|
const root = manifestPath.slice(0, -PACKAGE_MANIFEST.length);
|
|
68
71
|
const files = Object.entries(entries)
|
|
@@ -70,12 +73,12 @@ export function readPackage(data) {
|
|
|
70
73
|
.map(([name, content]) => {
|
|
71
74
|
const path = cleanPath(name.slice(root.length + PACKAGE_FILES.length));
|
|
72
75
|
if (!path) {
|
|
73
|
-
throw invalid(
|
|
76
|
+
throw invalid(bilingual(`„${name}“ ist kein gültiger Dateipfad.`, `“${name}” isn’t a valid file path.`));
|
|
74
77
|
}
|
|
75
78
|
return { path, data: content };
|
|
76
79
|
});
|
|
77
80
|
if (files.reduce((n, f) => n + f.data.byteLength, 0) > WORKSPACE_LIMITS.totalBytes) {
|
|
78
|
-
throw invalid("The workspace in the
|
|
81
|
+
throw invalid(bilingual("Der Arbeitsbereich in der Wizard-Datei ist größer als 25 MB.", "The workspace in the wizard file is larger than 25 MB."));
|
|
79
82
|
}
|
|
80
83
|
return { manifest: readManifest(json(entries[manifestPath])), files };
|
|
81
84
|
}
|
|
@@ -12,7 +12,7 @@ import { ownedProject } from "./projects.js";
|
|
|
12
12
|
import { createWizard, deleteWizard, draftIssues, ownedWizard, parseDraft } from "./wizards.js";
|
|
13
13
|
/** A wizard as a file — the format is `@engenty-wizards/shared/wizard-package`'s. */
|
|
14
14
|
/** Where the project's pages go in a package, as Markdown the standalone runner reads inline. */
|
|
15
|
-
export const KNOWLEDGE_DIR = "
|
|
15
|
+
export const KNOWLEDGE_DIR = "knowledge/";
|
|
16
16
|
/** How much of the project's knowledge a package takes along: enough pages, never the whole space. */
|
|
17
17
|
export const KNOWLEDGE_MAX_CHARS = 2_000_000;
|
|
18
18
|
/** What one page may hold in the package (the plugin hands a file to the model up to there). */
|
|
@@ -151,8 +151,8 @@ lang = "en") {
|
|
|
151
151
|
: `Imported${name ? ` from “${name}”` : ""}: ${draft.steps.length} steps, ${files.length} ${files.length === 1 ? "file" : "files"}.`,
|
|
152
152
|
lacks.length
|
|
153
153
|
? de
|
|
154
|
-
? `Diesem
|
|
155
|
-
: `This
|
|
154
|
+
? `Diesem Space fehlt noch: ${lacks.join(", ")}. Füge es unter Einstellungen → Connectors hinzu.`
|
|
155
|
+
: `This space still needs: ${lacks.join(", ")}. Add it under Settings → Connectors.`
|
|
156
156
|
: "",
|
|
157
157
|
]
|
|
158
158
|
.filter(Boolean)
|
|
@@ -28,7 +28,7 @@ export function fileView(row) {
|
|
|
28
28
|
description: row.description,
|
|
29
29
|
source: row.source,
|
|
30
30
|
status: stale ? "failed" : row.status,
|
|
31
|
-
error: stale ? "
|
|
31
|
+
error: stale ? "Reading was interrupted. Read it in again." : row.error,
|
|
32
32
|
indexed: row.indexed,
|
|
33
33
|
pages: row.pages,
|
|
34
34
|
chars: row.chars,
|
|
@@ -82,11 +82,11 @@ export async function addProjectFile(projectId, input) {
|
|
|
82
82
|
? bilingual("Ein Logo ist ein Bild (PNG, SVG, JPEG, WebP).", "A logo is an image (PNG, SVG, JPEG, WebP).")
|
|
83
83
|
: input.kind === "pattern"
|
|
84
84
|
? bilingual("Ein Muster ist ein Bild (SVG, PNG, JPEG, WebP).", "A pattern is an image (SVG, PNG, JPEG, WebP).")
|
|
85
|
-
: bilingual("Hierher gehören Bilder, Videos, Audio, PDFs, Schriften, Lottie und 3D-Modelle. Texte zum Nachschlagen
|
|
85
|
+
: bilingual("Hierher gehören Bilder, Videos, Audio, PDFs, Schriften, Lottie und 3D-Modelle. Texte zum Nachschlagen gehören ins Wissen.", "Images, videos, audio, PDFs, fonts, Lottie and 3D models go here. Texts to look things up in go under knowledge."));
|
|
86
86
|
}
|
|
87
87
|
const limit = projectFileLimit(input.kind, mime);
|
|
88
88
|
if (!input.data.byteLength || input.data.byteLength > limit) {
|
|
89
|
-
throw new ServiceError("invalid", bilingual(`
|
|
89
|
+
throw new ServiceError("invalid", bilingual(`Wähl eine Datei bis ${Math.round(limit / 1_000_000)} MB.`, `Choose a file of ${Math.round(limit / 1_000_000)} MB or less.`));
|
|
90
90
|
}
|
|
91
91
|
const scope = and(eq(schema.projectFile.projectId, projectId), eq(schema.projectFile.kind, input.kind));
|
|
92
92
|
const [{ n, last }] = await db
|
|
@@ -94,7 +94,7 @@ export async function addProjectFile(projectId, input) {
|
|
|
94
94
|
.from(schema.projectFile)
|
|
95
95
|
.where(scope);
|
|
96
96
|
if (n >= PROJECT_LIMITS.files[input.kind]) {
|
|
97
|
-
throw new ServiceError("refused", bilingual(`
|
|
97
|
+
throw new ServiceError("refused", bilingual(`Hier haben höchstens ${PROJECT_LIMITS.files[input.kind]} Dateien Platz.`, `There’s room for at most ${PROJECT_LIMITS.files[input.kind]} files here.`));
|
|
98
98
|
}
|
|
99
99
|
const name = input.name.slice(0, 120) || "datei";
|
|
100
100
|
const ref = await saveAsset({
|
|
@@ -150,15 +150,15 @@ export async function addProjectFileFromUrl(projectId, input) {
|
|
|
150
150
|
headers: { "user-agent": "Mozilla/5.0 (compatible; engenty-wizards/0.1)" },
|
|
151
151
|
signal: AbortSignal.any([AbortSignal.timeout(60_000), ...(signal ? [signal] : [])]),
|
|
152
152
|
}).catch(() => {
|
|
153
|
-
throw new ServiceError("invalid", bilingual("Die Adresse ist nicht erreichbar.", "The address
|
|
153
|
+
throw new ServiceError("invalid", bilingual("Die Adresse ist nicht erreichbar.", "The address can’t be reached."));
|
|
154
154
|
});
|
|
155
155
|
if (!res.ok) {
|
|
156
|
-
throw new ServiceError("invalid", bilingual(`Die Adresse antwortete ${res.status}.`, `The address
|
|
156
|
+
throw new ServiceError("invalid", bilingual(`Die Adresse antwortete mit ${res.status}.`, `The address responded with ${res.status}.`));
|
|
157
157
|
}
|
|
158
158
|
const mime = (res.headers.get("content-type") ?? "").split(";")[0].trim().toLowerCase();
|
|
159
159
|
if (mime === "text/html" && kind !== "document") {
|
|
160
160
|
await res.body?.cancel();
|
|
161
|
-
throw new ServiceError("invalid", bilingual("Unter der Adresse steht eine Webseite, kein Bild.", "The address
|
|
161
|
+
throw new ServiceError("invalid", bilingual("Unter der Adresse steht eine Webseite, kein Bild.", "The address leads to a web page, not an image."), {
|
|
162
162
|
reason: "page",
|
|
163
163
|
url: res.url,
|
|
164
164
|
});
|
|
@@ -206,7 +206,7 @@ async function describeImage(row, data) {
|
|
|
206
206
|
maxOutputTokens: 300,
|
|
207
207
|
system: [
|
|
208
208
|
"You catalogue the files of a project. Describe the picture in ONE sentence of at most 25 words: what it shows, its style and colours, and what it suits (a logo on dark ground, a header image, a team photo, a product shot).",
|
|
209
|
-
"Write in the language of the project's description; if unclear,
|
|
209
|
+
"Write in the language of the project's description; if unclear, English. No preamble, no quotes.",
|
|
210
210
|
].join("\n"),
|
|
211
211
|
messages: [
|
|
212
212
|
{
|
|
@@ -237,7 +237,7 @@ async function describeDocument(row, text) {
|
|
|
237
237
|
return result.text.trim().slice(0, 600);
|
|
238
238
|
}
|
|
239
239
|
/**
|
|
240
|
-
* What a document became gets its
|
|
240
|
+
* What a document became gets its categories from a model, where the space has some: the first
|
|
241
241
|
* page or table it was read into, else the file itself.
|
|
242
242
|
*/
|
|
243
243
|
async function categorize(row, items, text) {
|
|
@@ -248,7 +248,7 @@ async function categorize(row, items, text) {
|
|
|
248
248
|
? { tableId: first.slice(2) }
|
|
249
249
|
: { fileId: row.id };
|
|
250
250
|
const changed = await fillCategories(row.projectId, ref, text || row.description, row.name).catch((err) => {
|
|
251
|
-
console.error("[project-file]
|
|
251
|
+
console.error("[project-file] categories", row.name, err);
|
|
252
252
|
return false;
|
|
253
253
|
});
|
|
254
254
|
if (changed) {
|
|
@@ -256,7 +256,7 @@ async function categorize(row, items, text) {
|
|
|
256
256
|
}
|
|
257
257
|
}
|
|
258
258
|
/**
|
|
259
|
-
* Reads a new file. A document is read into
|
|
259
|
+
* Reads a new file. A document is read into Knowledge — a page, a page with sub-pages, a table per
|
|
260
260
|
* sheet — and stays as the original behind it; one that reads into nothing stays a file, found
|
|
261
261
|
* by its name and description. A file nobody described is described.
|
|
262
262
|
*/
|
|
@@ -296,9 +296,7 @@ async function prepare(row) {
|
|
|
296
296
|
console.error("[project-file]", row.name, err);
|
|
297
297
|
await set({
|
|
298
298
|
status: "failed",
|
|
299
|
-
error: err instanceof UnreadableDocument
|
|
300
|
-
? err.message
|
|
301
|
-
: "Reading did not work. Please try again.",
|
|
299
|
+
error: err instanceof UnreadableDocument ? err.message : "Reading didn’t work. Try again.",
|
|
302
300
|
});
|
|
303
301
|
}
|
|
304
302
|
});
|
|
@@ -370,7 +368,7 @@ export async function removeProjectFiles(projectId) {
|
|
|
370
368
|
}
|
|
371
369
|
// --- what plugins keep as files ----------------------------------------------------
|
|
372
370
|
/**
|
|
373
|
-
* A plugin's file of
|
|
371
|
+
* A plugin's file of Knowledge (`server.spaceData.putFile`, or the original of a page it wrote):
|
|
374
372
|
* a document of the space under the plugin's key. The same key again replaces it. `text`: what
|
|
375
373
|
* a step reads of it; without one only its name and description are found.
|
|
376
374
|
*/
|
|
@@ -382,7 +380,7 @@ export async function putOriginFile(origin, input) {
|
|
|
382
380
|
});
|
|
383
381
|
const mime = documentMime(input.name, input.mime) || "application/octet-stream";
|
|
384
382
|
if (!input.data.byteLength || input.data.byteLength > projectFileLimit("document", mime)) {
|
|
385
|
-
throw new ServiceError("invalid",
|
|
383
|
+
throw new ServiceError("invalid", bilingual(`„${input.name}“ ist leer oder zu groß für ein Dokument.`, `“${input.name}” is empty or too large for a document.`));
|
|
386
384
|
}
|
|
387
385
|
const name = input.name.slice(0, 120) || "datei";
|
|
388
386
|
const values = {
|
|
@@ -409,7 +407,7 @@ export async function putOriginFile(origin, input) {
|
|
|
409
407
|
.from(schema.projectFile)
|
|
410
408
|
.where(and(eq(schema.projectFile.projectId, input.space), eq(schema.projectFile.kind, "document")));
|
|
411
409
|
if (n >= PROJECT_LIMITS.files.document) {
|
|
412
|
-
throw new ServiceError("refused", `A space
|
|
410
|
+
throw new ServiceError("refused", bilingual(`Ein Space hat höchstens ${PROJECT_LIMITS.files.document} Dokumente.`, `A space can have at most ${PROJECT_LIMITS.files.document} documents.`));
|
|
413
411
|
}
|
|
414
412
|
const ref = await saveAsset({ kind: "project", mime, name, data: input.data });
|
|
415
413
|
id = ref.id;
|
|
@@ -159,7 +159,7 @@ async function indexPage(pageId) {
|
|
|
159
159
|
const prefix = head(node?.titles ?? [page.title], lines);
|
|
160
160
|
const sections = sectionsOf(page.markdown);
|
|
161
161
|
const units = [];
|
|
162
|
-
// A page without text is still found by its title and
|
|
162
|
+
// A page without text is still found by its title and categories.
|
|
163
163
|
for (const section of sections.length ? sections : [{ heading: [], sheet: null, body: "" }]) {
|
|
164
164
|
// A page's own first heading is its title: the path says it already.
|
|
165
165
|
const path = section.heading[0]?.toLowerCase() === page.title.toLowerCase()
|
|
@@ -244,7 +244,7 @@ async function indexFile(fileId) {
|
|
|
244
244
|
.select({ heading: schema.projectChunk.heading, idx: schema.projectChunk.idx })
|
|
245
245
|
.from(schema.projectChunk)
|
|
246
246
|
.where(scope);
|
|
247
|
-
// Passages of a document read before
|
|
247
|
+
// Passages of a document read before Knowledge had pages stay until it is read again.
|
|
248
248
|
if (passages.some((p) => p.heading === null)) {
|
|
249
249
|
return;
|
|
250
250
|
}
|
|
@@ -279,7 +279,7 @@ async function indexValue(valueId) {
|
|
|
279
279
|
valueId: value.id,
|
|
280
280
|
heading: `${category.name}: ${value.value}`,
|
|
281
281
|
idx: i,
|
|
282
|
-
text: `${category.name}: ${value.value} —
|
|
282
|
+
text: `${category.name}: ${value.value} — Summary\n\n${part}`,
|
|
283
283
|
}));
|
|
284
284
|
await writeUnits(scope, units);
|
|
285
285
|
}
|
|
@@ -294,8 +294,8 @@ async function subPages(pageId) {
|
|
|
294
294
|
}
|
|
295
295
|
/**
|
|
296
296
|
* Brings one item's units up to date. `p:` a page, `P:` a page and its sub-pages (its title or
|
|
297
|
-
*
|
|
298
|
-
* a value's
|
|
297
|
+
* categories changed: they stand in theirs), `t:` a table's rows, `r:` one row, `f:` a file, `v:`
|
|
298
|
+
* a value's summary.
|
|
299
299
|
*/
|
|
300
300
|
export async function indexItem(key) {
|
|
301
301
|
const id = key.slice(2);
|
|
@@ -423,7 +423,7 @@ export function trigramQuery(query) {
|
|
|
423
423
|
return terms.length ? terms.join(" OR ") : null;
|
|
424
424
|
}
|
|
425
425
|
const COLUMNS = sql.raw("c.id, c.file_id, c.page_id, c.row_id, c.table_id, c.value_id, c.heading, c.sheet, c.plugin, c.ref, c.title, c.link, c.idx, c.text");
|
|
426
|
-
/** A unit of the space's own
|
|
426
|
+
/** A unit of the space's own Knowledge, or of a plugin's text while the tenant has that plugin. */
|
|
427
427
|
function searchable(plugins) {
|
|
428
428
|
return plugins.length
|
|
429
429
|
? sql `(c.plugin IS NULL OR c.plugin IN (${sql.join(plugins.map((p) => sql `${p}`), sql `, `)}))`
|
|
@@ -465,7 +465,7 @@ function itemOf(u) {
|
|
|
465
465
|
return `x:${u.plugin}:${u.ref}`;
|
|
466
466
|
}
|
|
467
467
|
/**
|
|
468
|
-
* Searches
|
|
468
|
+
* Searches Knowledge: items whose categories pass `where`, then the units the keywords, the
|
|
469
469
|
* trigrams and the vectors rank best, merged by rank (RRF), then the classifier's judgement of
|
|
470
470
|
* each of the CANDIDATES best. Gives up to `limit` items with their best section. `check: false`
|
|
471
471
|
* leaves the classifier out.
|
|
@@ -478,7 +478,7 @@ export async function searchKnowledge(projectId, query, opts = {}) {
|
|
|
478
478
|
}
|
|
479
479
|
const plugins = [...(await pluginIdsOf(currentTenant()))];
|
|
480
480
|
const inFilter = filter ? filtered(filter) : null;
|
|
481
|
-
// A filter keeps plugins' texts and
|
|
481
|
+
// A filter keeps plugins' texts and summaries out: they have no categories.
|
|
482
482
|
const where = and(sql `c.project_id = ${projectId}`, searchable(plugins), filter
|
|
483
483
|
? (inFilter ?? sql `(c.page_id IS NOT NULL OR c.row_id IS NOT NULL OR c.file_id IS NOT NULL)`)
|
|
484
484
|
: undefined);
|
|
@@ -559,7 +559,7 @@ export async function searchKnowledge(projectId, query, opts = {}) {
|
|
|
559
559
|
filtered: filter?.size ?? null,
|
|
560
560
|
};
|
|
561
561
|
}
|
|
562
|
-
/** What a step and the studio see of each unit: the item's path, title and
|
|
562
|
+
/** What a step and the studio see of each unit: the item's path, title and categories. */
|
|
563
563
|
async function describe(projectId, units) {
|
|
564
564
|
const pageIds = [...new Set(units.flatMap((u) => (u.page_id ? [u.page_id] : [])))];
|
|
565
565
|
const rowIds = [...new Set(units.flatMap((u) => (u.row_id ? [u.row_id] : [])))];
|
|
@@ -654,7 +654,7 @@ async function describe(projectId, units) {
|
|
|
654
654
|
kind: "summary",
|
|
655
655
|
open: `v:${u.value_id}`,
|
|
656
656
|
path: "",
|
|
657
|
-
title: `${category?.name}: ${value?.value} —
|
|
657
|
+
title: `${category?.name}: ${value?.value} — Summary`,
|
|
658
658
|
categories: [],
|
|
659
659
|
};
|
|
660
660
|
}
|