wizards 0.5.0 → 0.5.2
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 +3 -3
- package/apps/runtime/dist/app.js +7 -4
- package/apps/runtime/dist/auth/index.js +2 -2
- package/apps/runtime/dist/authoring/guide.js +1 -1
- 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/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 +2 -2
- 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 +15 -9
- 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 +53 -23
- 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 +2 -2
- 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 +2 -2
- package/apps/runtime/dist/services/project-files.js +10 -12
- package/apps/runtime/dist/services/projects.js +13 -8
- 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 +2 -2
- package/apps/runtime/dist/services/space-categories.js +4 -4
- package/apps/runtime/dist/services/space-data.js +13 -13
- package/apps/runtime/dist/services/space-origin.js +1 -1
- package/apps/runtime/dist/services/spaces.js +7 -7
- package/apps/runtime/dist/services/wizards.js +5 -5
- package/apps/runtime/dist/tools/net-guard.js +135 -24
- package/apps/runtime/dist/tools/plugin.js +1 -1
- package/apps/runtime/dist/tools/store.js +1 -1
- package/apps/runtime/dist/widgets/step.js +1 -1
- package/apps/web/dist/assets/AboutDialog-hRgrS60N.js +1 -0
- package/apps/web/dist/assets/{ChangelogDialog-BXpkgatF.js → ChangelogDialog-BUSpLhC6.js} +1 -1
- package/apps/web/dist/assets/{ChatView-t7z44uFN.js → ChatView-DO7P5Vv_.js} +1 -1
- package/apps/web/dist/assets/EditorPage-BabV35Bj.js +3 -0
- package/apps/web/dist/assets/{FlowPreview-WpS8DCPl.js → FlowPreview-CKrwLPN0.js} +1 -1
- package/apps/web/dist/assets/{Harness-DL7Bse40.js → Harness-o8IIQXZp.js} +1 -1
- package/apps/web/dist/assets/{NewWizardChannels-Crt6PzH2.js → NewWizardChannels-DqU79Myj.js} +1 -1
- package/apps/web/dist/assets/{NewWizardPage-COiVmNsl.js → NewWizardPage-D1UCvmDp.js} +1 -1
- package/apps/web/dist/assets/{OnboardingPage-BwluEqXq.js → OnboardingPage-C70bqPnq.js} +1 -1
- package/apps/web/dist/assets/{ProjectPage-BIRU_AlN.js → ProjectPage-BOLvmQWZ.js} +1 -1
- package/apps/web/dist/assets/{PublicRunner-BxMb4gEt.js → PublicRunner-B0PGyHI9.js} +1 -1
- package/apps/web/dist/assets/{ReadOnly-D9r5ze5S.js → ReadOnly-B0mJBD1q.js} +1 -1
- package/apps/web/dist/assets/{RunDrawer-DmJl2QOJ.js → RunDrawer-BsnsIJ0Y.js} +1 -1
- package/apps/web/dist/assets/{RunnerView-CF_1rC-7.js → RunnerView-B56i3nKO.js} +1 -1
- package/apps/web/dist/assets/SettingsPage-BvfRUqb2.js +1 -0
- package/apps/web/dist/assets/{SetupPage-BGKtbS7j.js → SetupPage-BvAFPu1J.js} +1 -1
- package/apps/web/dist/assets/{SharePage-C4MqXKDA.js → SharePage-hV5pYrO2.js} +1 -1
- package/apps/web/dist/assets/changelog-BftXQzUA.js +1 -0
- package/apps/web/dist/assets/{esm-CsY8TkyE.js → esm-DfapVSy6.js} +1 -1
- package/apps/web/dist/assets/{index-BjQvGQp_.js → index-BuFrRzvG.js} +4 -4
- package/apps/web/dist/assets/{index-Bi2x2g7n.css → index-DSbQiZY8.css} +1 -1
- package/apps/web/dist/assets/{lock-CI6TpWL-.js → lock-B-tJwE4p.js} +1 -1
- package/apps/web/dist/assets/oss-credits-BJrR7BR1.js +1 -0
- package/apps/web/dist/assets/{phone-BrtQz-fG.js → phone-DRHxY0l_.js} +1 -1
- package/apps/web/dist/assets/{settings-menu-BtEKL72n.js → settings-menu-OPrJTW8a.js} +1 -1
- package/apps/web/dist/assets/{ui-4IaTD7LB.js → ui-DftgEA2k.js} +3 -3
- package/apps/web/dist/assets/{upload-CWJAtPRo.js → upload-B73coFNw.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 +21 -2
- 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/package.json +5 -4
- package/apps/web/dist/assets/AboutDialog-A_ILyeue.js +0 -1
- package/apps/web/dist/assets/EditorPage-4qxNJgKr.js +0 -3
- package/apps/web/dist/assets/SettingsPage-3xSInE-A.js +0 -1
- package/apps/web/dist/assets/changelog-CM_GdIVI.js +0 -1
- package/apps/web/dist/assets/oss-credits-BQQX0kvk.js +0 -1
|
@@ -38,11 +38,13 @@ import { sharedRun, shareImage, shareRun, shareView, unshareRun } from "../servi
|
|
|
38
38
|
import { isIconFile, wizardAvatarSvg, wizardIcon } from "../services/wizard-icon.js";
|
|
39
39
|
import { clearStore, deleteRows, listRows, listSecrets, readStoreFile, StoreError, saveRows, scopeOf, storeFiles, updateRow, } from "../store/index.js";
|
|
40
40
|
import { tenantOfLink, tenantOfRun, tenantStatus, tokenOfCode, visitorOverLimit, } from "../tenants/control.js";
|
|
41
|
-
import { byteRange, uploadLimit, wizardManifest } from "./delivery.js";
|
|
42
|
-
import { requestLang } from "./lang.js";
|
|
41
|
+
import { bareMime, byteRange, inlineSafe, uploadLimit, uploadMime, wizardManifest, } from "./delivery.js";
|
|
42
|
+
import { inLang, notFoundAnswer, requestLang } from "./lang.js";
|
|
43
43
|
const VISITOR_COOKIE = "wz_vid";
|
|
44
44
|
/** Kept files a review shows inline. */
|
|
45
45
|
const INLINE_TYPES = new Set(["application/pdf", "image/png", "image/jpeg", "image/webp"]);
|
|
46
|
+
/** Assets whose bytes and type someone outside the wizard chose: uploads and collected files. */
|
|
47
|
+
const FOREIGN_KINDS = new Set(["upload", "upload-image", "file"]);
|
|
46
48
|
export function clientIp(c) {
|
|
47
49
|
const fwd = c.req.header("x-forwarded-for")?.split(",")[0]?.trim();
|
|
48
50
|
if (fwd) {
|
|
@@ -148,7 +150,7 @@ function tenantFrom(find) {
|
|
|
148
150
|
return async (c, next) => {
|
|
149
151
|
const tenant = await find(c);
|
|
150
152
|
if (!tenant) {
|
|
151
|
-
return c.json(
|
|
153
|
+
return c.json(notFoundAnswer(c), 404);
|
|
152
154
|
}
|
|
153
155
|
if ((await tenantStatus(tenant)) === "suspended") {
|
|
154
156
|
return refuse(c, { code: "wizardUnavailable" }, 403);
|
|
@@ -187,7 +189,7 @@ async function pageRunner(c, rest) {
|
|
|
187
189
|
where: eq(schema.wizard.shareToken, c.req.param("token") ?? ""),
|
|
188
190
|
});
|
|
189
191
|
if (!w || w.publishedVersion === null) {
|
|
190
|
-
return c.json(
|
|
192
|
+
return c.json(notFoundAnswer(c), 404);
|
|
191
193
|
}
|
|
192
194
|
const version = await db.query.wizardVersion.findFirst({
|
|
193
195
|
where: and(eq(schema.wizardVersion.wizardId, w.id), eq(schema.wizardVersion.version, w.publishedVersion)),
|
|
@@ -195,19 +197,22 @@ async function pageRunner(c, rest) {
|
|
|
195
197
|
const served = version
|
|
196
198
|
? await servePageRunner(w, version.definition, c.req.param("runner") ?? "", rest, c.req.raw)
|
|
197
199
|
: null;
|
|
198
|
-
return served ?? c.json(
|
|
200
|
+
return served ?? c.json(notFoundAnswer(c), 404);
|
|
199
201
|
}
|
|
200
202
|
export const publicRoutes = new Hono()
|
|
201
203
|
// A wizard's ID, typed into the mobile app: answers its share token.
|
|
202
204
|
.get("/codes/:code", async (c) => {
|
|
203
205
|
const ip = clientIp(c);
|
|
204
206
|
if (codeLookupBlocked(ip)) {
|
|
205
|
-
return c.json({
|
|
207
|
+
return c.json({
|
|
208
|
+
error: inLang(c, "Zu viele Versuche. Warte kurz und versuch es noch einmal.", "Too many tries. Wait a moment and try again."),
|
|
209
|
+
code: "too_many_tries",
|
|
210
|
+
}, 429);
|
|
206
211
|
}
|
|
207
212
|
const found = await tokenOfCode(c.req.param("code"));
|
|
208
213
|
if (!found || (await tenantStatus(found.tenantId)) === "suspended") {
|
|
209
214
|
codeMissed(ip);
|
|
210
|
-
return c.json(
|
|
215
|
+
return c.json(notFoundAnswer(c), 404);
|
|
211
216
|
}
|
|
212
217
|
return c.json({ token: found.token, url: `${env.publicUrl}/w/${found.token}` });
|
|
213
218
|
})
|
|
@@ -219,7 +224,7 @@ export const publicRoutes = new Hono()
|
|
|
219
224
|
where: eq(schema.wizard.shareToken, c.req.param("token")),
|
|
220
225
|
});
|
|
221
226
|
if (!w || w.publishedVersion === null) {
|
|
222
|
-
return c.json(
|
|
227
|
+
return c.json(notFoundAnswer(c), 404);
|
|
223
228
|
}
|
|
224
229
|
const version = await db.query.wizardVersion.findFirst({
|
|
225
230
|
where: and(eq(schema.wizardVersion.wizardId, w.id), eq(schema.wizardVersion.version, w.publishedVersion)),
|
|
@@ -245,7 +250,7 @@ export const publicRoutes = new Hono()
|
|
|
245
250
|
where: eq(schema.wizard.shareToken, c.req.param("token")),
|
|
246
251
|
});
|
|
247
252
|
if (!w || w.publishedVersion === null) {
|
|
248
|
-
return c.json(
|
|
253
|
+
return c.json(notFoundAnswer(c), 404);
|
|
249
254
|
}
|
|
250
255
|
const reason = await wizardUnavailable(w);
|
|
251
256
|
if (reason) {
|
|
@@ -372,7 +377,7 @@ export const runRoutes = new Hono()
|
|
|
372
377
|
.get("/:id", async (c) => {
|
|
373
378
|
const run = await accessibleRun(c, c.req.param("id"));
|
|
374
379
|
if (!run) {
|
|
375
|
-
return c.json(
|
|
380
|
+
return c.json(notFoundAnswer(c), 404);
|
|
376
381
|
}
|
|
377
382
|
return c.json(await viewOf(run));
|
|
378
383
|
})
|
|
@@ -380,7 +385,7 @@ export const runRoutes = new Hono()
|
|
|
380
385
|
const id = c.req.param("id");
|
|
381
386
|
const first = await accessibleRun(c, id);
|
|
382
387
|
if (!first) {
|
|
383
|
-
return c.json(
|
|
388
|
+
return c.json(notFoundAnswer(c), 404);
|
|
384
389
|
}
|
|
385
390
|
return streamSSE(c, async (stream) => {
|
|
386
391
|
let pending = false;
|
|
@@ -432,7 +437,7 @@ export const runRoutes = new Hono()
|
|
|
432
437
|
.post("/:id/device", async (c) => {
|
|
433
438
|
const run = await accessibleRun(c, c.req.param("id"));
|
|
434
439
|
if (!run) {
|
|
435
|
-
return c.json(
|
|
440
|
+
return c.json(notFoundAnswer(c), 404);
|
|
436
441
|
}
|
|
437
442
|
const offer = z
|
|
438
443
|
.object({
|
|
@@ -451,7 +456,7 @@ export const runRoutes = new Hono()
|
|
|
451
456
|
.post("/:id/device/:reqId", async (c) => {
|
|
452
457
|
const run = await accessibleRun(c, c.req.param("id"));
|
|
453
458
|
if (!run) {
|
|
454
|
-
return c.json(
|
|
459
|
+
return c.json(notFoundAnswer(c), 404);
|
|
455
460
|
}
|
|
456
461
|
const answer = z
|
|
457
462
|
.object({ text: z.string().max(200_000).optional(), error: z.string().max(500).optional() })
|
|
@@ -461,7 +466,7 @@ export const runRoutes = new Hono()
|
|
|
461
466
|
.post("/:id/pages/:stepId", async (c) => {
|
|
462
467
|
const run = await accessibleRun(c, c.req.param("id"));
|
|
463
468
|
if (!run) {
|
|
464
|
-
return c.json(
|
|
469
|
+
return c.json(notFoundAnswer(c), 404);
|
|
465
470
|
}
|
|
466
471
|
const { values } = z
|
|
467
472
|
.object({ values: z.record(z.string(), z.unknown()) })
|
|
@@ -477,7 +482,7 @@ export const runRoutes = new Hono()
|
|
|
477
482
|
.post("/:id/reviews/:stepId", async (c) => {
|
|
478
483
|
const run = await accessibleRun(c, c.req.param("id"));
|
|
479
484
|
if (!run) {
|
|
480
|
-
return c.json(
|
|
485
|
+
return c.json(notFoundAnswer(c), 404);
|
|
481
486
|
}
|
|
482
487
|
const action = z
|
|
483
488
|
.discriminatedUnion("type", [
|
|
@@ -501,7 +506,7 @@ export const runRoutes = new Hono()
|
|
|
501
506
|
.post("/:id/back", async (c) => {
|
|
502
507
|
const run = await accessibleRun(c, c.req.param("id"));
|
|
503
508
|
if (!run) {
|
|
504
|
-
return c.json(
|
|
509
|
+
return c.json(notFoundAnswer(c), 404);
|
|
505
510
|
}
|
|
506
511
|
try {
|
|
507
512
|
await goBack(run.id);
|
|
@@ -514,7 +519,7 @@ export const runRoutes = new Hono()
|
|
|
514
519
|
.post("/:id/retry", async (c) => {
|
|
515
520
|
const run = await accessibleRun(c, c.req.param("id"));
|
|
516
521
|
if (!run) {
|
|
517
|
-
return c.json(
|
|
522
|
+
return c.json(notFoundAnswer(c), 404);
|
|
518
523
|
}
|
|
519
524
|
try {
|
|
520
525
|
await retry(run.id);
|
|
@@ -527,7 +532,7 @@ export const runRoutes = new Hono()
|
|
|
527
532
|
.post("/:id/cancel", async (c) => {
|
|
528
533
|
const run = await accessibleRun(c, c.req.param("id"));
|
|
529
534
|
if (!run) {
|
|
530
|
-
return c.json(
|
|
535
|
+
return c.json(notFoundAnswer(c), 404);
|
|
531
536
|
}
|
|
532
537
|
await cancel(run.id);
|
|
533
538
|
return c.json({ ok: true });
|
|
@@ -536,7 +541,7 @@ export const runRoutes = new Hono()
|
|
|
536
541
|
.post("/:id/talk", async (c) => {
|
|
537
542
|
const run = await accessibleRun(c, c.req.param("id"));
|
|
538
543
|
if (!run) {
|
|
539
|
-
return c.json(
|
|
544
|
+
return c.json(notFoundAnswer(c), 404);
|
|
540
545
|
}
|
|
541
546
|
if (run.status === "done" || run.status === "cancelled" || run.status === "failed") {
|
|
542
547
|
return refuse(c, { code: "runOver" }, 409);
|
|
@@ -563,7 +568,7 @@ export const runRoutes = new Hono()
|
|
|
563
568
|
.post("/:id/transcribe", async (c) => {
|
|
564
569
|
const run = await accessibleRun(c, c.req.param("id"));
|
|
565
570
|
if (!run) {
|
|
566
|
-
return c.json(
|
|
571
|
+
return c.json(notFoundAnswer(c), 404);
|
|
567
572
|
}
|
|
568
573
|
if (run.status === "done" || run.status === "cancelled" || run.status === "failed") {
|
|
569
574
|
return refuse(c, { code: "runOver" }, 409);
|
|
@@ -597,12 +602,14 @@ export const runRoutes = new Hono()
|
|
|
597
602
|
.post("/:id/uploads", async (c) => {
|
|
598
603
|
const run = await accessibleRun(c, c.req.param("id"));
|
|
599
604
|
if (!run) {
|
|
600
|
-
return c.json(
|
|
605
|
+
return c.json(notFoundAnswer(c), 404);
|
|
601
606
|
}
|
|
602
607
|
const form = await c.req.formData();
|
|
603
608
|
const file = form.get("file");
|
|
604
|
-
// A recorder names its type with codecs ("audio/webm;codecs=opus"); the bare type is kept
|
|
605
|
-
|
|
609
|
+
// A recorder names its type with codecs ("audio/webm;codecs=opus"); the bare type is kept,
|
|
610
|
+
// and only when it is one a wizard takes. The browser's word is the sender's: anything else
|
|
611
|
+
// is kept as plain bytes, so it can never come back as a page on this origin.
|
|
612
|
+
const mime = uploadMime(file instanceof File ? file.type : "");
|
|
606
613
|
const limit = uploadLimit(mime);
|
|
607
614
|
if (!(file instanceof File) || file.size > limit) {
|
|
608
615
|
return refuse(c, { code: "fileTooLarge", params: { mb: Math.round(limit / 1_000_000) } }, 400);
|
|
@@ -610,7 +617,7 @@ export const runRoutes = new Hono()
|
|
|
610
617
|
const ref = await saveAsset({
|
|
611
618
|
runId: run.id,
|
|
612
619
|
kind: mime.startsWith("image/") ? "upload-image" : "upload",
|
|
613
|
-
mime
|
|
620
|
+
mime,
|
|
614
621
|
name: file.name.slice(0, 120),
|
|
615
622
|
data: new Uint8Array(await file.arrayBuffer()),
|
|
616
623
|
});
|
|
@@ -620,7 +627,7 @@ export const runRoutes = new Hono()
|
|
|
620
627
|
.post("/:id/geocode", async (c) => {
|
|
621
628
|
const run = await accessibleRun(c, c.req.param("id"));
|
|
622
629
|
if (!run) {
|
|
623
|
-
return c.json(
|
|
630
|
+
return c.json(notFoundAnswer(c), 404);
|
|
624
631
|
}
|
|
625
632
|
const { lat, lng, language } = z
|
|
626
633
|
.object({
|
|
@@ -649,7 +656,7 @@ export const runRoutes = new Hono()
|
|
|
649
656
|
.post("/:id/ask/:askId", async (c) => {
|
|
650
657
|
const run = await accessibleRun(c, c.req.param("id"));
|
|
651
658
|
if (!run) {
|
|
652
|
-
return c.json(
|
|
659
|
+
return c.json(notFoundAnswer(c), 404);
|
|
653
660
|
}
|
|
654
661
|
const answer = z
|
|
655
662
|
.discriminatedUnion("type", [
|
|
@@ -673,7 +680,7 @@ export const runRoutes = new Hono()
|
|
|
673
680
|
const resources = run?.status === "running" ? liveResources(run.id) : null;
|
|
674
681
|
if (!resources?.openPage()) {
|
|
675
682
|
// The run may work in another process: its runner answers.
|
|
676
|
-
return (run && (await fromOwner(c, run.id))) || c.json(
|
|
683
|
+
return (run && (await fromOwner(c, run.id))) || c.json(notFoundAnswer(c), 404);
|
|
677
684
|
}
|
|
678
685
|
return c.json({ seat: resources.seatHolder() });
|
|
679
686
|
})
|
|
@@ -831,7 +838,7 @@ export const runRoutes = new Hono()
|
|
|
831
838
|
const run = await accessibleRun(c, c.req.param("id"));
|
|
832
839
|
const connection = run?.definition.connections?.find((x) => x.id === c.req.param("connectionId"));
|
|
833
840
|
if (!run || !connection) {
|
|
834
|
-
return c.json(
|
|
841
|
+
return c.json(notFoundAnswer(c), 404);
|
|
835
842
|
}
|
|
836
843
|
try {
|
|
837
844
|
const url = await startOAuth(scopeOf(run), await projectIdOf(run), run.id, connection, c.req.param("connectorId"), requestLang(c));
|
|
@@ -848,7 +855,7 @@ export const runRoutes = new Hono()
|
|
|
848
855
|
const run = await accessibleRun(c, c.req.param("id"));
|
|
849
856
|
const connection = run?.definition.connections?.find((x) => x.id === c.req.param("connectionId"));
|
|
850
857
|
if (!run || !connection) {
|
|
851
|
-
return c.json(
|
|
858
|
+
return c.json(notFoundAnswer(c), 404);
|
|
852
859
|
}
|
|
853
860
|
const { values } = z
|
|
854
861
|
.object({ values: z.record(z.string(), z.string().max(500)) })
|
|
@@ -868,7 +875,7 @@ export const runRoutes = new Hono()
|
|
|
868
875
|
.delete("/:id/connections/:connectionId", async (c) => {
|
|
869
876
|
const run = await accessibleRun(c, c.req.param("id"));
|
|
870
877
|
if (!run) {
|
|
871
|
-
return c.json(
|
|
878
|
+
return c.json(notFoundAnswer(c), 404);
|
|
872
879
|
}
|
|
873
880
|
await disconnect(scopeOf(run), c.req.param("connectionId"));
|
|
874
881
|
signalChanged(run.id);
|
|
@@ -880,7 +887,7 @@ export const runRoutes = new Hono()
|
|
|
880
887
|
// A shared list is every run's: the person never sees it.
|
|
881
888
|
const def = run?.definition.lists?.find((l) => l.id === c.req.param("list") && !l.shared);
|
|
882
889
|
if (!run || !def) {
|
|
883
|
-
return c.json(
|
|
890
|
+
return c.json(notFoundAnswer(c), 404);
|
|
884
891
|
}
|
|
885
892
|
const { cells } = z
|
|
886
893
|
.object({ cells: z.record(z.string(), z.unknown()) })
|
|
@@ -899,14 +906,14 @@ export const runRoutes = new Hono()
|
|
|
899
906
|
// A shared list is every run's: the person never sees it.
|
|
900
907
|
const def = run?.definition.lists?.find((l) => l.id === c.req.param("list") && !l.shared);
|
|
901
908
|
if (!run || !def) {
|
|
902
|
-
return c.json(
|
|
909
|
+
return c.json(notFoundAnswer(c), 404);
|
|
903
910
|
}
|
|
904
911
|
const { cells } = z
|
|
905
912
|
.object({ cells: z.record(z.string(), z.unknown()) })
|
|
906
913
|
.parse(await c.req.json());
|
|
907
914
|
try {
|
|
908
915
|
if (!(await updateRow(scopeOf(run), def, c.req.param("rowId"), cells))) {
|
|
909
|
-
return c.json(
|
|
916
|
+
return c.json(notFoundAnswer(c), 404);
|
|
910
917
|
}
|
|
911
918
|
}
|
|
912
919
|
catch (err) {
|
|
@@ -920,7 +927,7 @@ export const runRoutes = new Hono()
|
|
|
920
927
|
// A shared list is every run's: the person never sees it.
|
|
921
928
|
const def = run?.definition.lists?.find((l) => l.id === c.req.param("list") && !l.shared);
|
|
922
929
|
if (!run || !def) {
|
|
923
|
-
return c.json(
|
|
930
|
+
return c.json(notFoundAnswer(c), 404);
|
|
924
931
|
}
|
|
925
932
|
await deleteRows(scopeOf(run), def, [c.req.param("rowId")]);
|
|
926
933
|
signalChanged(run.id);
|
|
@@ -944,7 +951,7 @@ export const runRoutes = new Hono()
|
|
|
944
951
|
.get("/:id/store", async (c) => {
|
|
945
952
|
const run = await accessibleRun(c, c.req.param("id"));
|
|
946
953
|
if (!run) {
|
|
947
|
-
return c.json(
|
|
954
|
+
return c.json(notFoundAnswer(c), 404);
|
|
948
955
|
}
|
|
949
956
|
const scope = scopeOf(run);
|
|
950
957
|
const lists = await Promise.all((run.definition.lists ?? [])
|
|
@@ -989,7 +996,7 @@ export const runRoutes = new Hono()
|
|
|
989
996
|
.delete("/:id/store", async (c) => {
|
|
990
997
|
const run = await accessibleRun(c, c.req.param("id"));
|
|
991
998
|
if (!run) {
|
|
992
|
-
return c.json(
|
|
999
|
+
return c.json(notFoundAnswer(c), 404);
|
|
993
1000
|
}
|
|
994
1001
|
await clearStore(scopeOf(run));
|
|
995
1002
|
signalChanged(run.id);
|
|
@@ -999,7 +1006,7 @@ export const runRoutes = new Hono()
|
|
|
999
1006
|
.post("/:id/notify", async (c) => {
|
|
1000
1007
|
const run = await accessibleRun(c, c.req.param("id"));
|
|
1001
1008
|
if (!run) {
|
|
1002
|
-
return c.json(
|
|
1009
|
+
return c.json(notFoundAnswer(c), 404);
|
|
1003
1010
|
}
|
|
1004
1011
|
const body = z
|
|
1005
1012
|
.object({
|
|
@@ -1014,7 +1021,7 @@ export const runRoutes = new Hono()
|
|
|
1014
1021
|
.delete("/:id/notify", async (c) => {
|
|
1015
1022
|
const run = await accessibleRun(c, c.req.param("id"));
|
|
1016
1023
|
if (!run) {
|
|
1017
|
-
return c.json(
|
|
1024
|
+
return c.json(notFoundAnswer(c), 404);
|
|
1018
1025
|
}
|
|
1019
1026
|
await setPushDevice(run.id, null);
|
|
1020
1027
|
return c.json({ ok: true });
|
|
@@ -1022,7 +1029,7 @@ export const runRoutes = new Hono()
|
|
|
1022
1029
|
.post("/:id/share", async (c) => {
|
|
1023
1030
|
const run = await accessibleRun(c, c.req.param("id"));
|
|
1024
1031
|
if (!run) {
|
|
1025
|
-
return c.json(
|
|
1032
|
+
return c.json(notFoundAnswer(c), 404);
|
|
1026
1033
|
}
|
|
1027
1034
|
try {
|
|
1028
1035
|
return c.json(await shareRun(run));
|
|
@@ -1037,7 +1044,7 @@ export const runRoutes = new Hono()
|
|
|
1037
1044
|
.delete("/:id/share", async (c) => {
|
|
1038
1045
|
const run = await accessibleRun(c, c.req.param("id"));
|
|
1039
1046
|
if (!run) {
|
|
1040
|
-
return c.json(
|
|
1047
|
+
return c.json(notFoundAnswer(c), 404);
|
|
1041
1048
|
}
|
|
1042
1049
|
await unshareRun(run);
|
|
1043
1050
|
return c.json({ ok: true });
|
|
@@ -1108,22 +1115,25 @@ async function serveAsset(c, run, assetId) {
|
|
|
1108
1115
|
if (!found || found.row.runId !== run.id) {
|
|
1109
1116
|
return c.notFound();
|
|
1110
1117
|
}
|
|
1118
|
+
// The stored type is sent only as a bare `type/subtype`: "text/html,x" reads as HTML to a browser.
|
|
1119
|
+
const mime = bareMime(found.row.mime) ?? "application/octet-stream";
|
|
1120
|
+
// What a person or a plugin uploaded, and files a step collected from mail and the web.
|
|
1121
|
+
const foreign = FOREIGN_KINDS.has(found.row.kind);
|
|
1111
1122
|
const headers = {
|
|
1112
|
-
"content-type":
|
|
1123
|
+
"content-type": mime,
|
|
1113
1124
|
"cache-control": "private, max-age=3600",
|
|
1114
1125
|
"x-content-type-options": "nosniff",
|
|
1126
|
+
// Nothing opened on its own runs scripts with this origin (an SVG, an XML document, a page).
|
|
1127
|
+
// A PDF is left to the browser's own viewer, which a sandbox would keep from starting.
|
|
1128
|
+
...(mime === "application/pdf" ? {} : { "content-security-policy": "sandbox" }),
|
|
1115
1129
|
};
|
|
1116
|
-
//
|
|
1117
|
-
if (
|
|
1130
|
+
// Foreign files are downloads, never pages of ours: only photos, sound, film and PDFs show in place.
|
|
1131
|
+
if (foreign && !inlineSafe(mime)) {
|
|
1118
1132
|
headers["content-disposition"] =
|
|
1119
1133
|
`attachment; filename="${found.row.name.replace(/["\\\r\n]/g, "")}"`;
|
|
1120
1134
|
}
|
|
1121
|
-
// An SVG or XML document opened on its own could run scripts with this origin.
|
|
1122
|
-
if (/svg|xml/.test(found.row.mime)) {
|
|
1123
|
-
headers["content-security-policy"] = "sandbox";
|
|
1124
|
-
}
|
|
1125
1135
|
// Generated HTML runs its scripts in an opaque origin and can reach nothing.
|
|
1126
|
-
if (
|
|
1136
|
+
if (mime === "text/html" && !foreign) {
|
|
1127
1137
|
const html = await stepHtml({
|
|
1128
1138
|
assets: [{ id: assetId, kind: found.row.kind, mime: found.row.mime, name: "" }],
|
|
1129
1139
|
at: "",
|
|
@@ -1132,7 +1142,7 @@ async function serveAsset(c, run, assetId) {
|
|
|
1132
1142
|
headers["content-security-policy"] = HTML_RESPONSE_CSP;
|
|
1133
1143
|
return c.body(new TextEncoder().encode(html ?? ""), 200, headers);
|
|
1134
1144
|
}
|
|
1135
|
-
if (/^(audio|video)\//.test(
|
|
1145
|
+
if (/^(audio|video)\//.test(mime)) {
|
|
1136
1146
|
headers["accept-ranges"] = "bytes";
|
|
1137
1147
|
const size = found.data.byteLength;
|
|
1138
1148
|
const range = byteRange(c.req.header("range"), size);
|
|
@@ -281,7 +281,10 @@ export const studio = new Hono()
|
|
|
281
281
|
// The team as the Manage-App keeps it: its people, who is invited, the places the plan gives.
|
|
282
282
|
.get("/team", async (c) => {
|
|
283
283
|
if (!managed) {
|
|
284
|
-
return c.json({
|
|
284
|
+
return c.json({
|
|
285
|
+
error: inLang(c, "Das gibt es nur in der Cloud.", "This is only available in the cloud."),
|
|
286
|
+
code: "not_managed",
|
|
287
|
+
}, 404);
|
|
285
288
|
}
|
|
286
289
|
const user = c.get("user");
|
|
287
290
|
const [info, team] = await Promise.all([
|
|
@@ -300,11 +303,17 @@ export const studio = new Hono()
|
|
|
300
303
|
// A runtime that runs alone says when a newer release is out and can run the update itself;
|
|
301
304
|
// `?fresh=1` asks GitHub now instead of within the hour.
|
|
302
305
|
.get("/update", async (c) => managed
|
|
303
|
-
? c.json({
|
|
306
|
+
? c.json({
|
|
307
|
+
error: inLang(c, "Das gibt es nur auf deinem Computer.", "This is only available on your computer."),
|
|
308
|
+
code: "not_local",
|
|
309
|
+
}, 404)
|
|
304
310
|
: c.json(await updateStatus(c.req.query("fresh") === "1")))
|
|
305
311
|
.post("/update", async (c) => {
|
|
306
312
|
if (managed) {
|
|
307
|
-
return c.json({
|
|
313
|
+
return c.json({
|
|
314
|
+
error: inLang(c, "Das gibt es nur auf deinem Computer.", "This is only available on your computer."),
|
|
315
|
+
code: "not_local",
|
|
316
|
+
}, 404);
|
|
308
317
|
}
|
|
309
318
|
applyUpdate();
|
|
310
319
|
return c.json(await updateStatus());
|
|
@@ -333,7 +342,7 @@ export const studio = new Hono()
|
|
|
333
342
|
.post("/assistant/speak", async (c) => {
|
|
334
343
|
const { text } = z.object({ text: z.string().min(1).max(4000) }).parse(await c.req.json());
|
|
335
344
|
if ((await classWays()).speech.kind === "none") {
|
|
336
|
-
throw new ServiceError("refused", bilingual("Hier
|
|
345
|
+
throw new ServiceError("refused", bilingual("Hier ist kein Sprachmodell eingerichtet.", "No language model is set up here."));
|
|
337
346
|
}
|
|
338
347
|
if (!(await canSpend())) {
|
|
339
348
|
throw new ServiceError("no_credits", bilingual("Dein Guthaben ist aufgebraucht.", "Your credits are used up."));
|
|
@@ -472,8 +481,8 @@ export const studio = new Hono()
|
|
|
472
481
|
message: err instanceof ServiceError || err instanceof ModelUnavailableError
|
|
473
482
|
? errorText(err, body.lang)
|
|
474
483
|
: body.lang === "de"
|
|
475
|
-
? "Da ist etwas schiefgelaufen.
|
|
476
|
-
: "Something went wrong.
|
|
484
|
+
? "Da ist etwas schiefgelaufen. Versuch es noch einmal."
|
|
485
|
+
: "Something went wrong. Try again.",
|
|
477
486
|
});
|
|
478
487
|
}
|
|
479
488
|
endRun(runId);
|
|
@@ -483,7 +492,7 @@ export const studio = new Hono()
|
|
|
483
492
|
})
|
|
484
493
|
// --- projects --------------------------------------------------------------
|
|
485
494
|
.get("/projects", async (c) => {
|
|
486
|
-
const projects = await listProjects(c.get("user").id);
|
|
495
|
+
const projects = await listProjects(c.get("user").id, requestLang(c));
|
|
487
496
|
const build = await mayBuild();
|
|
488
497
|
return c.json(projects.map((p) => ({
|
|
489
498
|
id: p.id,
|
|
@@ -521,7 +530,7 @@ export const studio = new Hono()
|
|
|
521
530
|
const kind = z.enum(PROJECT_FILE_KINDS).parse(c.req.query("kind"));
|
|
522
531
|
const file = (await c.req.formData()).get("file");
|
|
523
532
|
if (!(file instanceof File)) {
|
|
524
|
-
throw new ServiceError("invalid", bilingual("
|
|
533
|
+
throw new ServiceError("invalid", bilingual("Wähl eine Datei.", "Choose a file."));
|
|
525
534
|
}
|
|
526
535
|
const row = await addProjectFile(project.id, {
|
|
527
536
|
kind,
|
|
@@ -554,7 +563,7 @@ export const studio = new Hono()
|
|
|
554
563
|
await ownedProject(c.get("user").id, c.req.param("id"));
|
|
555
564
|
const { url } = z.object({ url: z.string().min(1).max(2000) }).parse(await c.req.json());
|
|
556
565
|
const colors = await scanColors(url).catch(() => {
|
|
557
|
-
throw new ServiceError("invalid", bilingual("Die Seite ist nicht erreichbar.", "The page
|
|
566
|
+
throw new ServiceError("invalid", bilingual("Die Seite ist nicht erreichbar.", "The page can’t be reached."));
|
|
558
567
|
});
|
|
559
568
|
return c.json({ colors });
|
|
560
569
|
})
|
|
@@ -588,7 +597,7 @@ export const studio = new Hono()
|
|
|
588
597
|
await ownedProject(c.get("user").id, c.req.param("id"));
|
|
589
598
|
const { url } = z.object({ url: z.string().min(1).max(2000) }).parse(await c.req.json());
|
|
590
599
|
const fonts = await scanFonts(url).catch(() => {
|
|
591
|
-
throw new ServiceError("invalid", bilingual("Die Seite ist nicht erreichbar.", "The page
|
|
600
|
+
throw new ServiceError("invalid", bilingual("Die Seite ist nicht erreichbar.", "The page can’t be reached."));
|
|
592
601
|
});
|
|
593
602
|
return c.json({ fonts });
|
|
594
603
|
})
|
|
@@ -830,7 +839,7 @@ export const studio = new Hono()
|
|
|
830
839
|
? { fileId: id }
|
|
831
840
|
: null;
|
|
832
841
|
if (!ref || key[1] !== ":") {
|
|
833
|
-
throw new ServiceError("invalid", "Unbekannter Eintrag.");
|
|
842
|
+
throw new ServiceError("invalid", bilingual("Unbekannter Eintrag.", "Unknown entry."));
|
|
834
843
|
}
|
|
835
844
|
const { values } = z
|
|
836
845
|
.object({ values: z.array(z.union([z.string().max(300), z.number(), z.boolean()])).max(50) })
|
|
@@ -944,7 +953,7 @@ export const studio = new Hono()
|
|
|
944
953
|
.post("/projects/:id/wizards/import", async (c) => {
|
|
945
954
|
const file = (await c.req.formData()).get("file");
|
|
946
955
|
if (!(file instanceof File) || !file.size || file.size > PACKAGE_MAX_BYTES) {
|
|
947
|
-
throw new ServiceError("invalid", bilingual(`
|
|
956
|
+
throw new ServiceError("invalid", bilingual(`Wähl eine Wizard-Datei (${PACKAGE_EXTENSION}) bis 30 MB.`, `Choose a wizard file (${PACKAGE_EXTENSION}) of 30 MB or less.`));
|
|
948
957
|
}
|
|
949
958
|
return c.json(await importWizard(c.get("user").id, c.req.param("id"), new Uint8Array(await file.arrayBuffer()), file.name, requestLang(c)));
|
|
950
959
|
})
|
|
@@ -1137,7 +1146,7 @@ export const studio = new Hono()
|
|
|
1137
1146
|
data: JSON.stringify({
|
|
1138
1147
|
message: err instanceof ServiceError
|
|
1139
1148
|
? errorText(err, requestLang(c))
|
|
1140
|
-
: inLang(c, "Da ist etwas schiefgelaufen.
|
|
1149
|
+
: inLang(c, "Da ist etwas schiefgelaufen. Versuch es noch einmal.", "Something went wrong. Try again."),
|
|
1141
1150
|
}),
|
|
1142
1151
|
});
|
|
1143
1152
|
}
|
|
@@ -1147,7 +1156,7 @@ export const studio = new Hono()
|
|
|
1147
1156
|
.post("/transcribe", async (c) => {
|
|
1148
1157
|
const file = (await c.req.formData()).get("file");
|
|
1149
1158
|
if (!(file instanceof File) || !file.size || file.size > 10_000_000) {
|
|
1150
|
-
throw new ServiceError("invalid", bilingual("
|
|
1159
|
+
throw new ServiceError("invalid", bilingual("Die Aufnahme darf höchstens 10 MB groß sein.", "The recording can be 10 MB at most."));
|
|
1151
1160
|
}
|
|
1152
1161
|
if (!(await canSpend())) {
|
|
1153
1162
|
throw new ServiceError("no_credits", bilingual("Dein Guthaben ist aufgebraucht.", "Your credits are used up."));
|
|
@@ -1221,7 +1230,7 @@ export const studio = new Hono()
|
|
|
1221
1230
|
const { connect } = z.object({ connect: z.boolean() }).parse(await c.req.json());
|
|
1222
1231
|
const [done] = await connectApps(layout(), [c.req.param("id")], !connect);
|
|
1223
1232
|
if (!done) {
|
|
1224
|
-
return c.json({ error: "Unknown app" }, 404);
|
|
1233
|
+
return c.json({ error: inLang(c, "Diese App gibt es nicht.", "Unknown app."), code: "not_found" }, 404);
|
|
1225
1234
|
}
|
|
1226
1235
|
// Not done is an answer too: the app's file is no plain JSON, here is what to paste.
|
|
1227
1236
|
return c.json(done.outcome);
|
|
@@ -1241,7 +1250,10 @@ export const studio = new Hono()
|
|
|
1241
1250
|
.get("/models", async (c) => c.json(await modelSettings(requestLang(c))))
|
|
1242
1251
|
.put("/models", async (c) => {
|
|
1243
1252
|
if (!mayChangeModels(c.get("user"))) {
|
|
1244
|
-
return c.json({
|
|
1253
|
+
return c.json({
|
|
1254
|
+
error: inLang(c, "Nur Admins des Teams ändern seine Modelle.", "Only team admins can change its models."),
|
|
1255
|
+
code: "forbidden",
|
|
1256
|
+
}, 403);
|
|
1245
1257
|
}
|
|
1246
1258
|
const input = modelsInput.parse(await c.req.json());
|
|
1247
1259
|
// In the cloud nothing runs on a machine's client or local model.
|
|
@@ -1249,7 +1261,10 @@ export const studio = new Hono()
|
|
|
1249
1261
|
(input.source ||
|
|
1250
1262
|
input.ollamaUrl ||
|
|
1251
1263
|
Object.values(input.bindings ?? {}).some((ref) => ref && !cloudBinding(ref)))) {
|
|
1252
|
-
return c.json({
|
|
1264
|
+
return c.json({
|
|
1265
|
+
error: inLang(c, "Das gibt es nicht in der Cloud.", "This isn’t available in the cloud."),
|
|
1266
|
+
code: "not_local",
|
|
1267
|
+
}, 400);
|
|
1253
1268
|
}
|
|
1254
1269
|
// Own keys and bindings to a provider's model are a feature of the team's plan.
|
|
1255
1270
|
if (managed) {
|
|
@@ -1258,7 +1273,7 @@ export const studio = new Hono()
|
|
|
1258
1273
|
Object.values(input.bindings ?? {}).some((ref) => ref && !isCreditsRef(ref));
|
|
1259
1274
|
if (own && info && !feature("ownKeys", info.features)) {
|
|
1260
1275
|
return c.json({
|
|
1261
|
-
error: inLang(c, `Eigene API
|
|
1276
|
+
error: inLang(c, `Eigene API-Keys gibt es nicht im Paket ${info.plan?.name ?? "des Teams"}.`, `Your own API keys aren’t part of the ${info.plan?.name ?? "team’s"} plan.`),
|
|
1262
1277
|
code: "plan",
|
|
1263
1278
|
}, 403);
|
|
1264
1279
|
}
|
|
@@ -1274,7 +1289,10 @@ export const studio = new Hono()
|
|
|
1274
1289
|
.parse(await c.req.json().catch(() => ({})));
|
|
1275
1290
|
const value = key?.trim() || (await storedKey(provider));
|
|
1276
1291
|
if (!value) {
|
|
1277
|
-
return c.json({
|
|
1292
|
+
return c.json({
|
|
1293
|
+
ok: false,
|
|
1294
|
+
message: inLang(c, "Kein API-Key hinterlegt.", "No API key is set."),
|
|
1295
|
+
});
|
|
1278
1296
|
}
|
|
1279
1297
|
return c.json(await checkKey(provider, value, requestLang(c)));
|
|
1280
1298
|
})
|
|
@@ -1282,7 +1300,10 @@ export const studio = new Hono()
|
|
|
1282
1300
|
// clip comes back to look at, a voice note comes back as text. Paid like any call.
|
|
1283
1301
|
.post("/models/test", async (c) => {
|
|
1284
1302
|
if (!mayChangeModels(c.get("user"))) {
|
|
1285
|
-
return c.json({
|
|
1303
|
+
return c.json({
|
|
1304
|
+
error: inLang(c, "Nur Admins des Teams testen seine Modelle.", "Only team admins can test its models."),
|
|
1305
|
+
code: "forbidden",
|
|
1306
|
+
}, 403);
|
|
1286
1307
|
}
|
|
1287
1308
|
const { cls, lang } = z
|
|
1288
1309
|
.object({ cls: z.enum(MODEL_CLASSES), lang: z.enum(["de", "en"]).default("en") })
|
|
@@ -1439,7 +1460,10 @@ export const studio = new Hono()
|
|
|
1439
1460
|
// --- the first visit of a cloud studio: who the team is, its website, first documents -----
|
|
1440
1461
|
.put("/team", async (c) => {
|
|
1441
1462
|
if (!managed) {
|
|
1442
|
-
return c.json({
|
|
1463
|
+
return c.json({
|
|
1464
|
+
error: inLang(c, "Das gibt es nur in der Cloud.", "This is only available in the cloud."),
|
|
1465
|
+
code: "not_managed",
|
|
1466
|
+
}, 404);
|
|
1443
1467
|
}
|
|
1444
1468
|
await requireAdmin();
|
|
1445
1469
|
const { name } = z.object({ name: z.string().trim().min(1).max(80) }).parse(await c.req.json());
|
|
@@ -1448,7 +1472,10 @@ export const studio = new Hono()
|
|
|
1448
1472
|
})
|
|
1449
1473
|
.post("/onboarding/done", async (c) => {
|
|
1450
1474
|
if (!managed) {
|
|
1451
|
-
return c.json({
|
|
1475
|
+
return c.json({
|
|
1476
|
+
error: inLang(c, "Das gibt es nur in der Cloud.", "This is only available in the cloud."),
|
|
1477
|
+
code: "not_managed",
|
|
1478
|
+
}, 404);
|
|
1452
1479
|
}
|
|
1453
1480
|
await finishOnboarding(c.get("user").tenantId);
|
|
1454
1481
|
return c.json({ ok: true });
|
|
@@ -1462,7 +1489,10 @@ export const studio = new Hono()
|
|
|
1462
1489
|
// The install command and the first steps, mailed to the signed-in person by the Manage-App.
|
|
1463
1490
|
.post("/install-mail", async (c) => {
|
|
1464
1491
|
if (!managed) {
|
|
1465
|
-
return c.json({
|
|
1492
|
+
return c.json({
|
|
1493
|
+
error: inLang(c, "Das gibt es nur in der Cloud.", "This is only available in the cloud."),
|
|
1494
|
+
code: "not_managed",
|
|
1495
|
+
}, 404);
|
|
1466
1496
|
}
|
|
1467
1497
|
const { lang } = z
|
|
1468
1498
|
.object({ lang: z.enum(["de", "en"]).default("en") })
|
|
@@ -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),
|