wizards 0.5.11 → 0.5.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/runtime/dist/browser/fast/action-space.js +0 -2
- package/apps/runtime/dist/cli/mcp.js +2 -15
- package/apps/runtime/dist/cli/report.js +2 -15
- package/apps/runtime/dist/cli/start.js +28 -14
- package/apps/runtime/dist/cli/status.js +1 -1
- package/apps/runtime/dist/credits/credits.js +0 -3
- package/apps/runtime/dist/db/client.js +0 -7
- package/apps/runtime/dist/docs/user/build-a-wizard.md +1 -1
- package/apps/runtime/dist/engine/runner.js +12 -7
- package/apps/runtime/dist/harness/index.js +0 -4
- package/apps/runtime/dist/manage.js +7 -7
- package/apps/runtime/dist/plugins/pro.js +0 -6
- package/apps/runtime/dist/render/convert.js +0 -3
- package/apps/runtime/dist/routes/studio.js +42 -6
- package/apps/runtime/dist/services/api.js +8 -6
- package/apps/runtime/dist/store/index.js +0 -11
- package/apps/runtime/dist/texts.js +45 -39
- package/apps/web/dist/assets/{AboutDialog-L1hcshAX.js → AboutDialog-DvkE9J4h.js} +1 -1
- package/apps/web/dist/assets/{ChangelogDialog-DZLYVOkl.js → ChangelogDialog-BZ8i3wt_.js} +1 -1
- package/apps/web/dist/assets/{ChatView-GNHi9ZnZ.js → ChatView-DIDIR0mb.js} +1 -1
- package/apps/web/dist/assets/{EditorPage-COJunH8K.js → EditorPage-BYysznX3.js} +1 -1
- package/apps/web/dist/assets/{FlowPreview-CTEJi5TQ.js → FlowPreview-DG23aI89.js} +1 -1
- package/apps/web/dist/assets/{Harness-CApAHvJE.js → Harness-CFh04TZ3.js} +1 -1
- package/apps/web/dist/assets/{NewWizardChannels-tst10rkV.js → NewWizardChannels-Bmo5yRCv.js} +1 -1
- package/apps/web/dist/assets/{NewWizardPage-BAKaUY1g.js → NewWizardPage-4n9-ZmHt.js} +1 -1
- package/apps/web/dist/assets/OnboardingPage-DpZv9qEO.js +1 -0
- package/apps/web/dist/assets/{ProjectPage-DaerYm8A.js → ProjectPage-IQb9RFlm.js} +1 -1
- package/apps/web/dist/assets/{PublicRunner-Hms0Vi88.js → PublicRunner-Da0klKj-.js} +1 -1
- package/apps/web/dist/assets/{ReadOnly-Bb8khbuH.js → ReadOnly-C_jD7KjW.js} +1 -1
- package/apps/web/dist/assets/{RunDrawer-DWaC_ryo.js → RunDrawer-FwLK67Yn.js} +1 -1
- package/apps/web/dist/assets/{RunnerView-D5BQ99ah.js → RunnerView-BBuphCkK.js} +1 -1
- package/apps/web/dist/assets/{SettingsPage-CQX6fKsa.js → SettingsPage-CZx52Ubk.js} +1 -1
- package/apps/web/dist/assets/{SetupPage-BZkbpN_V.js → SetupPage-B-o7VhbI.js} +1 -1
- package/apps/web/dist/assets/{SharePage-Bb-BIvAX.js → SharePage-k2OMGOWU.js} +1 -1
- package/apps/web/dist/assets/changelog-9oSWVuP-.js +1 -0
- package/apps/web/dist/assets/{esm-pbWES-Cu.js → esm-Dzqe3TT4.js} +1 -1
- package/apps/web/dist/assets/{index-BVWLhiGX.js → index-93fPRTxr.js} +4 -4
- package/apps/web/dist/assets/index-DaKjB676.css +1 -0
- package/apps/web/dist/assets/{lock-C6LaSCq_.js → lock-a8rDnyg5.js} +1 -1
- package/apps/web/dist/assets/{phone-BUWpzNWn.js → phone-CitA6oZn.js} +1 -1
- package/apps/web/dist/assets/{settings-menu-yO8b8LjQ.js → settings-menu-COgM8CgB.js} +1 -1
- package/apps/web/dist/assets/{ui-i5saQjmD.js → ui-CeZNNuaK.js} +1 -1
- package/apps/web/dist/assets/{upload-D89fyY2-.js → upload-CcKFwoPd.js} +1 -1
- package/apps/web/dist/index.html +3 -3
- package/apps/web/dist/mcp/flow.html +2 -2
- package/node_modules/@engenty-wizards/plugin-sdk/dist/thread.js +34 -34
- package/node_modules/@engenty-wizards/shared/dist/definition.d.ts +0 -6
- package/node_modules/@engenty-wizards/shared/dist/definition.js +0 -10
- package/node_modules/@engenty-wizards/shared/dist/features.d.ts +0 -1
- package/node_modules/@engenty-wizards/shared/dist/features.js +0 -1
- package/node_modules/@engenty-wizards/shared/dist/marketplace.d.ts +0 -5
- package/node_modules/@engenty-wizards/shared/dist/marketplace.js +0 -5
- package/node_modules/@engenty-wizards/shared/dist/providers.d.ts +1 -1
- package/node_modules/@engenty-wizards/shared/dist/providers.js +1 -1
- package/node_modules/@engenty-wizards/shared/dist/run.js +1 -1
- package/package.json +1 -1
- package/apps/web/dist/assets/OnboardingPage-Di8AuZj_.js +0 -1
- package/apps/web/dist/assets/changelog-BUHje4CT.js +0 -1
- package/apps/web/dist/assets/index-DmQEcSlY.css +0 -1
|
@@ -7,8 +7,6 @@
|
|
|
7
7
|
// the answer is validated against exactly the ids that were offered. Ported
|
|
8
8
|
// from engenty-pro's fast loop.
|
|
9
9
|
import { NEXT_ACTION, TARGET } from "./instructions.js";
|
|
10
|
-
/** What the classifier may pick besides an element operation. */
|
|
11
|
-
export const CONTROL_OPERATIONS = ["DONE", "BLOCKED"];
|
|
12
10
|
/** The operation head's id for "interact with one element". */
|
|
13
11
|
export const ACT = "ACT";
|
|
14
12
|
/** The single target head's key in the questions and the answers. */
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
1
|
import { join } from "node:path";
|
|
3
2
|
import { createInterface } from "node:readline";
|
|
4
3
|
import { LOCAL_CLIENT_HEADER, LOCAL_MCP_HEADER, mintLocalTicket } from "../auth/local-ticket.js";
|
|
5
|
-
import { runningRuntime, settings, startInBackground } from "./start.js";
|
|
4
|
+
import { readSecret, runningRuntime, settings, startInBackground } from "./start.js";
|
|
6
5
|
import { tilde } from "./ui.js";
|
|
7
6
|
/** The JSON-RPC messages of a response: a JSON body (one or a batch) or a server-sent stream. */
|
|
8
7
|
export function messagesOf(contentType, body) {
|
|
@@ -26,18 +25,6 @@ export function messagesOf(contentType, body) {
|
|
|
26
25
|
const parsed = JSON.parse(body);
|
|
27
26
|
return Array.isArray(parsed) ? parsed : [parsed];
|
|
28
27
|
}
|
|
29
|
-
function readSecret(paths, dataDir) {
|
|
30
|
-
const given = process.env.APP_SECRET?.trim() || settings(paths).fileEnv.APP_SECRET?.trim();
|
|
31
|
-
if (given) {
|
|
32
|
-
return given;
|
|
33
|
-
}
|
|
34
|
-
try {
|
|
35
|
-
return readFileSync(join(dataDir, "secret"), "utf8").trim();
|
|
36
|
-
}
|
|
37
|
-
catch {
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
28
|
export async function mcp(paths) {
|
|
42
29
|
const { dataDir } = settings(paths);
|
|
43
30
|
let running = null;
|
|
@@ -63,7 +50,7 @@ export async function mcp(paths) {
|
|
|
63
50
|
};
|
|
64
51
|
const post = async (message) => {
|
|
65
52
|
const at = await runtime();
|
|
66
|
-
const secret = readSecret(paths
|
|
53
|
+
const secret = readSecret(paths);
|
|
67
54
|
if (!secret) {
|
|
68
55
|
throw new Error("No secret in the data folder yet. Start engenty wizards once first.");
|
|
69
56
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
1
|
import { join } from "node:path";
|
|
3
2
|
import { LOCAL_MCP_HEADER, mintLocalTicket } from "../auth/local-ticket.js";
|
|
4
|
-
import { runningRuntime, settings } from "./start.js";
|
|
3
|
+
import { readSecret, runningRuntime, settings } from "./start.js";
|
|
5
4
|
import { bad, cmd, dim, ok, tilde } from "./ui.js";
|
|
6
5
|
/**
|
|
7
6
|
* `wizards logs` shows the last lines the running runtime printed; `wizards report` sends its
|
|
@@ -9,25 +8,13 @@ import { bad, cmd, dim, ok, tilde } from "./ui.js";
|
|
|
9
8
|
* (docs/plans/remote-debug.md). Both talk to the running process, signed with the data folder's
|
|
10
9
|
* secret as `wizards mcp` does; neither sends anything by itself.
|
|
11
10
|
*/
|
|
12
|
-
function readSecret(paths, dataDir) {
|
|
13
|
-
const given = process.env.APP_SECRET?.trim() || settings(paths).fileEnv.APP_SECRET?.trim();
|
|
14
|
-
if (given) {
|
|
15
|
-
return given;
|
|
16
|
-
}
|
|
17
|
-
try {
|
|
18
|
-
return readFileSync(join(dataDir, "secret"), "utf8").trim();
|
|
19
|
-
}
|
|
20
|
-
catch {
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
11
|
async function ask(paths, method, path) {
|
|
25
12
|
const { dataDir } = settings(paths);
|
|
26
13
|
const running = await runningRuntime(dataDir);
|
|
27
14
|
if (!running) {
|
|
28
15
|
return `engenty wizards isn’t running. To start it: ${cmd("start")}`;
|
|
29
16
|
}
|
|
30
|
-
const secret = readSecret(paths
|
|
17
|
+
const secret = readSecret(paths);
|
|
31
18
|
if (!secret) {
|
|
32
19
|
return `Couldn’t read the data folder’s secret (${tilde(join(dataDir, "secret"))}).`;
|
|
33
20
|
}
|
|
@@ -23,6 +23,25 @@ export function settings(paths, env = process.env) {
|
|
|
23
23
|
appUrl: value("APP_URL").replace(/\/$/, ""),
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* The secret the runtime this command starts signs with, read the way it reads it (env.ts): its
|
|
28
|
+
* APP_SECRET, else the one it keeps in the data folder; null before it made one. The runtime
|
|
29
|
+
* never sees the terminal's APP_SECRET (cli/environment.ts), only its folder's `.env.local` and
|
|
30
|
+
* `.env`, so the terminal's is left out here too: a ticket signed with it would be refused.
|
|
31
|
+
*/
|
|
32
|
+
export function readSecret(paths, env = process.env) {
|
|
33
|
+
const { fileEnv, dataDir } = settings(paths, env);
|
|
34
|
+
const given = readEnvFile(join(paths.home, ".env.local")).APP_SECRET?.trim() || fileEnv.APP_SECRET?.trim();
|
|
35
|
+
if (given) {
|
|
36
|
+
return given;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
return readFileSync(join(dataDir, "secret"), "utf8").trim() || null;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
26
45
|
export async function healthy(port) {
|
|
27
46
|
try {
|
|
28
47
|
const response = await fetch(`http://127.0.0.1:${port}/api/health`, {
|
|
@@ -55,16 +74,11 @@ export async function runningRuntime(dataDir) {
|
|
|
55
74
|
const running = readRunning(dataDir);
|
|
56
75
|
return running && (await healthy(running.port)) ? running : null;
|
|
57
76
|
}
|
|
58
|
-
/** A link that lets this browser into a running runtime: a ticket signed with the
|
|
59
|
-
export function entryUrl(running,
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
// APP_SECRET is set elsewhere: the browser gets in if it was let in before.
|
|
66
|
-
return `${running.url}/`;
|
|
67
|
-
}
|
|
77
|
+
/** A link that lets this browser into a running runtime: a ticket signed with the runtime's secret. */
|
|
78
|
+
export function entryUrl(running, paths, env = process.env) {
|
|
79
|
+
const secret = readSecret(paths, env);
|
|
80
|
+
// No secret to be read here: the browser gets in if it was let in before.
|
|
81
|
+
return secret ? `${running.url}/api/local/enter?t=${mintLocalTicket(secret)}` : `${running.url}/`;
|
|
68
82
|
}
|
|
69
83
|
/** Opens an address in the person's browser; false when this machine has none to open it with. */
|
|
70
84
|
export function openBrowser(url) {
|
|
@@ -145,7 +159,7 @@ export async function start(paths, options) {
|
|
|
145
159
|
mkdirSync(paths.logs, { recursive: true });
|
|
146
160
|
const running = await runningRuntime(dataDir);
|
|
147
161
|
if (running) {
|
|
148
|
-
show(running.url, entryUrl(running,
|
|
162
|
+
show(running.url, entryUrl(running, paths), options.open);
|
|
149
163
|
console.log(dim(` It was already running. To stop it: ${cmd("stop")}`));
|
|
150
164
|
return 0;
|
|
151
165
|
}
|
|
@@ -155,7 +169,7 @@ export async function start(paths, options) {
|
|
|
155
169
|
console.error(`engenty wizards didn’t start. Its log is in ${tilde(join(paths.logs, "runtime.log"))}.`);
|
|
156
170
|
return 1;
|
|
157
171
|
}
|
|
158
|
-
show(started.url, entryUrl(started,
|
|
172
|
+
show(started.url, entryUrl(started, paths), options.open);
|
|
159
173
|
console.log(dim(` It’s running in the background. To stop it: ${cmd("stop")}`));
|
|
160
174
|
console.log(dim(` Its log is in ${tilde(join(paths.logs, "runtime.log"))}.`));
|
|
161
175
|
return 0;
|
|
@@ -224,7 +238,7 @@ export async function studio(paths) {
|
|
|
224
238
|
const { dataDir } = settings(paths);
|
|
225
239
|
const running = await runningRuntime(dataDir);
|
|
226
240
|
if (running) {
|
|
227
|
-
show(running.url, entryUrl(running,
|
|
241
|
+
show(running.url, entryUrl(running, paths), true);
|
|
228
242
|
return 0;
|
|
229
243
|
}
|
|
230
244
|
return start(paths, { open: true, background: true });
|
|
@@ -237,7 +251,7 @@ export async function open(paths, print) {
|
|
|
237
251
|
console.error(`engenty wizards isn’t running. To start it: ${cmd("start")}`);
|
|
238
252
|
return 1;
|
|
239
253
|
}
|
|
240
|
-
const entry = entryUrl(running,
|
|
254
|
+
const entry = entryUrl(running, paths);
|
|
241
255
|
if (print || !openBrowser(entry)) {
|
|
242
256
|
console.log(entry);
|
|
243
257
|
}
|
|
@@ -135,7 +135,7 @@ export async function restart(paths) {
|
|
|
135
135
|
}
|
|
136
136
|
console.log(`${ok} ${running ? "Restarted" : "Started"}: ${cyan(started.url)} ${dim(`pid ${started.pid}, in the background`)}`);
|
|
137
137
|
if (!running) {
|
|
138
|
-
console.log(` Open the studio: ${cyan(entryUrl(started,
|
|
138
|
+
console.log(` Open the studio: ${cyan(entryUrl(started, paths))}`);
|
|
139
139
|
}
|
|
140
140
|
return 0;
|
|
141
141
|
}
|
|
@@ -11,9 +11,6 @@ export const MICROS_PER_CREDIT = 1_000_000;
|
|
|
11
11
|
export function usdToMicros(usd) {
|
|
12
12
|
return Math.ceil(usd * 100 * MICROS_PER_CREDIT);
|
|
13
13
|
}
|
|
14
|
-
export function microsToCredits(micros) {
|
|
15
|
-
return Math.round(micros / MICROS_PER_CREDIT);
|
|
16
|
-
}
|
|
17
14
|
/** At least one credit left — enough to start something. */
|
|
18
15
|
export async function canSpend() {
|
|
19
16
|
if (!managed) {
|
|
@@ -92,13 +92,6 @@ export function tenantDb(id) {
|
|
|
92
92
|
}
|
|
93
93
|
return db;
|
|
94
94
|
}
|
|
95
|
-
/** Records where the Manage-App created the tenant's database, before it is first opened. */
|
|
96
|
-
export async function registerTenant(id, dbUrl) {
|
|
97
|
-
await controlDb
|
|
98
|
-
.insert(control.tenant)
|
|
99
|
-
.values({ id, dbUrl })
|
|
100
|
-
.onConflictDoUpdate({ target: control.tenant.id, set: { dbUrl } });
|
|
101
|
-
}
|
|
102
95
|
const ready = new Map();
|
|
103
96
|
/** Closes and forgets an open tenant database (the tenant was deleted or moved). */
|
|
104
97
|
export function closeTenantDb(id) {
|
|
@@ -52,7 +52,7 @@ You can change everything afterwards in the chat.
|
|
|
52
52
|
| The diagram, left | The steps as a flow with their branches. Click a step to open it |
|
|
53
53
|
| Chat | Say what should change. The assistant edits the flow and tells you what it did |
|
|
54
54
|
| Steps | One step at a time: its texts, fields, tools and model class. Move it up or down, or delete it |
|
|
55
|
-
| Files |
|
|
55
|
+
| Files | Files the wizard uses on every run, like widget code, libraries or price lists. Upload your own or let the assistant write them |
|
|
56
56
|
| Memory | What the wizard knows across all its runs: the notes that AI steps with the “Pages” tool read and add to, and the tables of its shared lists |
|
|
57
57
|
| Runs | The test runs and live runs of this wizard |
|
|
58
58
|
|
|
@@ -59,7 +59,11 @@ async function concurrencyLimit(tenant) {
|
|
|
59
59
|
if (!managed) {
|
|
60
60
|
return env.limits.concurrentRuns;
|
|
61
61
|
}
|
|
62
|
-
|
|
62
|
+
// Out of reach, the Manage-App doesn't hold the line up: the runtime's own limit counts.
|
|
63
|
+
const info = await tenantInfo(tenant).catch((err) => {
|
|
64
|
+
console.warn(`[line] limit of tenant ${tenant}:`, err);
|
|
65
|
+
return null;
|
|
66
|
+
});
|
|
63
67
|
return info?.limits.concurrentRuns || env.limits.concurrentRuns;
|
|
64
68
|
}
|
|
65
69
|
async function loadRun(runId) {
|
|
@@ -206,14 +210,14 @@ async function take(runId, tenant) {
|
|
|
206
210
|
active.set(runId, abort);
|
|
207
211
|
// The heartbeat keeps the claim and hears of a cancel the web wrote while a step works.
|
|
208
212
|
const beat = setInterval(() => {
|
|
209
|
-
void heartbeatRuns([runId]).catch(() =>
|
|
213
|
+
void heartbeatRuns([runId]).catch((err) => console.warn(`[run ${runId}] heartbeat:`, err));
|
|
210
214
|
void withTenant(tenant, () => loadRun(runId))
|
|
211
215
|
.then((run) => {
|
|
212
216
|
if (run && run.status !== "running") {
|
|
213
217
|
abort.abort();
|
|
214
218
|
}
|
|
215
219
|
})
|
|
216
|
-
.catch(() =>
|
|
220
|
+
.catch((err) => console.warn(`[run ${runId}] cancel check:`, err));
|
|
217
221
|
}, HEARTBEAT_MS);
|
|
218
222
|
beat.unref();
|
|
219
223
|
await withTenant(tenant, () => drive(runId, abort.signal))
|
|
@@ -223,17 +227,18 @@ async function take(runId, tenant) {
|
|
|
223
227
|
const run = await loadRun(runId);
|
|
224
228
|
const lang = run ? wizardLang(run.definition) : "en";
|
|
225
229
|
await updateRun(runId, { status: "failed", error: saidText(friendly(err), lang) });
|
|
226
|
-
}).catch(() =>
|
|
230
|
+
}).catch((e) => console.error(`[run ${runId}] marking it failed:`, e));
|
|
227
231
|
})
|
|
228
232
|
.finally(async () => {
|
|
229
233
|
clearInterval(beat);
|
|
230
234
|
active.delete(runId);
|
|
231
|
-
|
|
235
|
+
// What fails here leaves the rest of the cleanup to run.
|
|
236
|
+
await withTenant(tenant, () => releaseResources(runId)).catch((err) => console.error(`[run ${runId}] releasing its resources:`, err));
|
|
232
237
|
// A run the shutdown stopped goes back in line for the next runner; the rest is done for now.
|
|
233
238
|
if (!stopping) {
|
|
234
|
-
await restRun(runId).catch(() =>
|
|
239
|
+
await restRun(runId).catch((err) => console.error(`[run ${runId}] rest:`, err));
|
|
235
240
|
}
|
|
236
|
-
withTenant(tenant, () => signalChanged(runId));
|
|
241
|
+
void withTenant(tenant, () => signalChanged(runId)).catch((err) => console.error(`[run ${runId}] signal:`, err));
|
|
237
242
|
// A slot came free: the next run of the line need not wait for the poll.
|
|
238
243
|
void lookAtLine();
|
|
239
244
|
});
|
|
@@ -65,10 +65,6 @@ export async function detectHarness(id, force = false) {
|
|
|
65
65
|
export function detectHarnesses(force = false) {
|
|
66
66
|
return Promise.all(HARNESSES.map((h) => detectHarness(h.id, force)));
|
|
67
67
|
}
|
|
68
|
-
/** The clients installed on this machine. */
|
|
69
|
-
export async function installedHarnesses() {
|
|
70
|
-
return (await detectHarnesses()).filter((h) => h.version !== null);
|
|
71
|
-
}
|
|
72
68
|
/**
|
|
73
69
|
* What the inline terminal runs to install a client: the command the terminal setup runs, shown
|
|
74
70
|
* first. A client from npm goes into the install's own prefix with the npm of its own Node.
|
|
@@ -153,6 +153,13 @@ export async function renameTenant(id, name) {
|
|
|
153
153
|
tenants.set(id, { at: Date.now(), value });
|
|
154
154
|
return value;
|
|
155
155
|
}
|
|
156
|
+
export function saveTeamProfile(tenantId, userId, lang, profile) {
|
|
157
|
+
return call("PUT", `/v1/tenants/${encodeURIComponent(tenantId)}/profile`, {
|
|
158
|
+
...profile,
|
|
159
|
+
userId,
|
|
160
|
+
lang,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
156
163
|
/** The Manage-App mails the install command and the first steps to one of the team's people. */
|
|
157
164
|
export function installMail(email, lang) {
|
|
158
165
|
return call("POST", "/v1/install-mail", { email, lang });
|
|
@@ -184,9 +191,6 @@ export async function publicPlans(base) {
|
|
|
184
191
|
}
|
|
185
192
|
return (await res.json());
|
|
186
193
|
}
|
|
187
|
-
export function bookUsage(input) {
|
|
188
|
-
return call("POST", "/v1/usage", input);
|
|
189
|
-
}
|
|
190
194
|
const channels = new Map();
|
|
191
195
|
/** The tenant's numbers, cached for a minute. */
|
|
192
196
|
export async function tenantChannels(id) {
|
|
@@ -198,10 +202,6 @@ export async function tenantChannels(id) {
|
|
|
198
202
|
channels.set(id, { at: Date.now(), value });
|
|
199
203
|
return value;
|
|
200
204
|
}
|
|
201
|
-
/** Forgets what the Manage-App said of the tenant's numbers: the next `tenantChannels` asks. */
|
|
202
|
-
export function forgetChannels(id) {
|
|
203
|
-
channels.delete(id);
|
|
204
|
-
}
|
|
205
205
|
/** A person of the tenant asks for its number; what the Manage-App answers with is the request. */
|
|
206
206
|
export async function requestChannelNumber(id, input) {
|
|
207
207
|
channels.delete(id);
|
|
@@ -146,9 +146,6 @@ export async function tablesToXlsx(tables) {
|
|
|
146
146
|
}
|
|
147
147
|
return new Uint8Array(await wb.xlsx.writeBuffer());
|
|
148
148
|
}
|
|
149
|
-
export function tableToXlsx(rows) {
|
|
150
|
-
return tablesToXlsx([{ name: "Data", rows }]);
|
|
151
|
-
}
|
|
152
149
|
export function jsonToMarkdown(value) {
|
|
153
150
|
const rows = firstTable(value);
|
|
154
151
|
if (rows?.length) {
|
|
@@ -31,7 +31,7 @@ import { appleStatus } from "../harness/apple.js";
|
|
|
31
31
|
import { freshAuth, loginEnv } from "../harness/env.js";
|
|
32
32
|
import { detectHarness, detectHarnesses, HARNESS_IDS, harness, installSpec, } from "../harness/index.js";
|
|
33
33
|
import { closeTerminal, openTerminal, resizeTerminal, subscribeTerminal, writeTerminal, } from "../harness/terminal.js";
|
|
34
|
-
import { installMail, managed, renameTenant, tenantInfo, tenantMembers, userInfo, } from "../manage.js";
|
|
34
|
+
import { installMail, managed, renameTenant, saveTeamProfile, tenantInfo, tenantMembers, userInfo, } from "../manage.js";
|
|
35
35
|
import { seenApps } from "../mcp/seen.js";
|
|
36
36
|
import { generateImageMedia, generateSpeechMedia, generateVideoMedia } from "../media/generate.js";
|
|
37
37
|
import { transcribeAudio } from "../media/transcribe.js";
|
|
@@ -1266,11 +1266,21 @@ export const studio = new Hono()
|
|
|
1266
1266
|
}, 400);
|
|
1267
1267
|
}
|
|
1268
1268
|
// Own keys and bindings to a provider's model are a feature of the team's plan.
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1269
|
+
// When the Manage-App can't say what the plan holds, own keys wait: nothing is taken unchecked.
|
|
1270
|
+
const own = Object.values(input.keys ?? {}).some((key) => key?.trim()) ||
|
|
1271
|
+
Object.values(input.bindings ?? {}).some((ref) => ref && !isCreditsRef(ref));
|
|
1272
|
+
if (managed && own) {
|
|
1273
|
+
const info = await tenantInfo(c.get("user").tenantId).catch((err) => {
|
|
1274
|
+
console.error("[models] plan of the tenant:", err);
|
|
1275
|
+
return null;
|
|
1276
|
+
});
|
|
1277
|
+
if (!info) {
|
|
1278
|
+
return c.json({
|
|
1279
|
+
error: say(c, "planUnavailable"),
|
|
1280
|
+
code: "manage_unavailable",
|
|
1281
|
+
}, 503);
|
|
1282
|
+
}
|
|
1283
|
+
if (!feature("ownKeys", info.features)) {
|
|
1274
1284
|
return c.json({
|
|
1275
1285
|
error: info.plan?.name
|
|
1276
1286
|
? say(c, "ownKeysNotInPlan", { plan: info.plan.name })
|
|
@@ -1470,6 +1480,32 @@ export const studio = new Hono()
|
|
|
1470
1480
|
const { name } = z.object({ name: z.string().trim().min(1).max(80) }).parse(await c.req.json());
|
|
1471
1481
|
const info = await renameTenant(c.get("user").tenantId, name);
|
|
1472
1482
|
return c.json({ name: info.name });
|
|
1483
|
+
})
|
|
1484
|
+
// What the team uses engenty for, and the questions that fit: kept at the Manage-App.
|
|
1485
|
+
.put("/team/profile", async (c) => {
|
|
1486
|
+
if (!managed) {
|
|
1487
|
+
return c.json({
|
|
1488
|
+
error: say(c, "onlyAvailableCloud"),
|
|
1489
|
+
code: "not_managed",
|
|
1490
|
+
}, 404);
|
|
1491
|
+
}
|
|
1492
|
+
await requireAdmin();
|
|
1493
|
+
const field = (max) => z.string().trim().max(max).nullable().optional();
|
|
1494
|
+
const profile = z
|
|
1495
|
+
.object({
|
|
1496
|
+
kind: z.enum(["personal", "club", "small", "corp"]),
|
|
1497
|
+
size: field(40),
|
|
1498
|
+
sector: field(120),
|
|
1499
|
+
role: field(120),
|
|
1500
|
+
need: field(2000),
|
|
1501
|
+
legalName: field(200),
|
|
1502
|
+
phone: field(40),
|
|
1503
|
+
website: field(400),
|
|
1504
|
+
})
|
|
1505
|
+
.parse(await c.req.json());
|
|
1506
|
+
const user = c.get("user");
|
|
1507
|
+
await saveTeamProfile(user.tenantId, user.id, requestLang(c) === "de" ? "de" : "en", profile);
|
|
1508
|
+
return c.json({ ok: true });
|
|
1473
1509
|
})
|
|
1474
1510
|
.post("/onboarding/done", async (c) => {
|
|
1475
1511
|
if (!managed) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { wizardLang } from "@engenty-wizards/shared/definition";
|
|
1
|
+
import { wizardLang, } from "@engenty-wizards/shared/definition";
|
|
2
2
|
import { standaloneEntry } from "@engenty-wizards/shared/fit";
|
|
3
3
|
import { pluginsOf } from "@engenty-wizards/shared/marketplace";
|
|
4
4
|
import { noteText } from "@engenty-wizards/shared/run";
|
|
@@ -9,6 +9,7 @@ import { env } from "../env.js";
|
|
|
9
9
|
import { wizardUnavailable } from "../limits.js";
|
|
10
10
|
import { codeOf } from "../tenants/control.js";
|
|
11
11
|
import { currentTenant } from "../tenants/tenant.js";
|
|
12
|
+
import { serverText } from "../texts.js";
|
|
12
13
|
import { mayBuild, projectWritable, READ_ONLY_TENANT } from "./access.js";
|
|
13
14
|
import { mainLogoId } from "./brand.js";
|
|
14
15
|
import { notFound, ServiceError } from "./errors.js";
|
|
@@ -111,20 +112,20 @@ export async function apiWizards(userId, spaceId) {
|
|
|
111
112
|
};
|
|
112
113
|
}));
|
|
113
114
|
}
|
|
114
|
-
/** A value as one line of text: what the person typed or picked. */
|
|
115
|
-
function answerText(field, value) {
|
|
115
|
+
/** A value as one line of text, in the wizard's language: what the person typed or picked. */
|
|
116
|
+
function answerText(field, value, lang) {
|
|
116
117
|
if (value === undefined || value === null || value === "") {
|
|
117
118
|
return "";
|
|
118
119
|
}
|
|
119
120
|
if (typeof value === "boolean") {
|
|
120
|
-
return value ? "
|
|
121
|
+
return serverText(lang, value ? "yes" : "no");
|
|
121
122
|
}
|
|
122
123
|
if (typeof value === "string" || typeof value === "number") {
|
|
123
124
|
return String(value);
|
|
124
125
|
}
|
|
125
126
|
if (Array.isArray(value)) {
|
|
126
127
|
if (["file", "image", "audio", "signature"].includes(field.kind)) {
|
|
127
|
-
return
|
|
128
|
+
return serverText(lang, "fileCount", { count: value.length });
|
|
128
129
|
}
|
|
129
130
|
return value
|
|
130
131
|
.map((v) => (typeof v === "object" && v ? Object.values(v).join(" ") : String(v)))
|
|
@@ -140,12 +141,13 @@ function answerText(field, value) {
|
|
|
140
141
|
/** What the person answered on the run's pages, by the fields' labels, in the order asked. */
|
|
141
142
|
function answersOf(run) {
|
|
142
143
|
const answers = [];
|
|
144
|
+
const lang = wizardLang(run.definition);
|
|
143
145
|
for (const step of run.definition.steps) {
|
|
144
146
|
if (step.type !== "page") {
|
|
145
147
|
continue;
|
|
146
148
|
}
|
|
147
149
|
for (const field of step.fields) {
|
|
148
|
-
const value = answerText(field, run.state.values[field.id]);
|
|
150
|
+
const value = answerText(field, run.state.values[field.id], lang);
|
|
149
151
|
if (value) {
|
|
150
152
|
answers.push({ field: field.id, label: field.label, kind: field.kind, value });
|
|
151
153
|
}
|
|
@@ -238,17 +238,6 @@ export async function writeStoreFile(scope, rawPath, content, meta = {}) {
|
|
|
238
238
|
updatedAt: updatedAt.toISOString(),
|
|
239
239
|
};
|
|
240
240
|
}
|
|
241
|
-
export async function deleteStoreFile(scope, rawPath) {
|
|
242
|
-
const path = cleanPath(rawPath);
|
|
243
|
-
if (!path) {
|
|
244
|
-
return false;
|
|
245
|
-
}
|
|
246
|
-
const gone = await db
|
|
247
|
-
.delete(schema.storeFile)
|
|
248
|
-
.where(and(ownFiles(scope), eq(schema.storeFile.path, path)))
|
|
249
|
-
.returning({ path: schema.storeFile.path });
|
|
250
|
-
return gone.length > 0;
|
|
251
|
-
}
|
|
252
241
|
// --- secrets -----------------------------------------------------------------
|
|
253
242
|
const ownSecrets = (scope) => and(eq(schema.storeSecret.wizardId, scope.wizardId), eq(schema.storeSecret.holder, scope.holder));
|
|
254
243
|
export async function getSecret(scope, slot) {
|
|
@@ -12,38 +12,40 @@ const EN = {
|
|
|
12
12
|
imported: "Imported: {steps}, {files}.",
|
|
13
13
|
stepCount: { one: "{count} step", other: "{count} steps" },
|
|
14
14
|
fileCount: { one: "{count} file", other: "{count} files" },
|
|
15
|
+
yes: "Yes",
|
|
16
|
+
no: "No",
|
|
15
17
|
spaceStillNeeds: "This space still needs: {needs}. Add it under Settings → Connectors.",
|
|
16
18
|
notFinished: "I have not quite finished. Write “continue” and I will carry on right there.",
|
|
17
19
|
done: "Done.",
|
|
18
20
|
notUnderstood: "I did not understand that – could you put it differently?",
|
|
19
|
-
siteTakingOver: "Taking over what the website says
|
|
20
|
-
siteWritingPages: "Writing the pages into Knowledge
|
|
21
|
-
siteReading: "Reading the details
|
|
21
|
+
siteTakingOver: "Taking over what the website says…",
|
|
22
|
+
siteWritingPages: "Writing the pages into Knowledge…",
|
|
23
|
+
siteReading: "Reading the details…",
|
|
22
24
|
siteFailed: "The website could not be taken over.",
|
|
23
25
|
faqQuestion: "Question",
|
|
24
26
|
faqAnswer: "Answer",
|
|
25
27
|
tableTitle: "Table",
|
|
26
28
|
newWizard: "New wizard",
|
|
27
|
-
newWizardStart: "Let
|
|
29
|
+
newWizardStart: "Let’s start",
|
|
28
30
|
newWizardAsk: "What is it about?",
|
|
29
31
|
newWizardDone: "Done",
|
|
30
32
|
templateSetUp: "I have set up the template “{title}” for you. Test it at the top right – or tell me what should be different.",
|
|
31
|
-
imapHint: "Gmail, iCloud, GMX and web.de need an app password (in your account
|
|
32
|
-
doingSaveLogo: "Fetching the logo
|
|
33
|
-
doingSaveFile: "Fetching a file
|
|
34
|
-
doingSetLogoSections: "Grouping the logos
|
|
35
|
-
doingSetAssetSections: "Grouping the assets
|
|
36
|
-
doingSetTypography: "Entering the fonts
|
|
37
|
-
doingSetGradients: "Entering the gradients
|
|
38
|
-
doingSetRules: "Entering rules
|
|
39
|
-
doingSetProfile: "Writing title and description
|
|
40
|
-
doingSetFacts: "Entering facts
|
|
41
|
-
doingSetColors: "Entering the colours
|
|
42
|
-
doingLookAtFile: "Looking at a file
|
|
43
|
-
doingDescribeFile: "Describing a file
|
|
44
|
-
doingWebSearch: "Searching the web
|
|
45
|
-
doingReadSite: "Reading {host}
|
|
46
|
-
doingReadWebsite: "Reading the website
|
|
33
|
+
imapHint: "Gmail, iCloud, GMX and web.de need an app password (in your account’s security settings), not your normal password.",
|
|
34
|
+
doingSaveLogo: "Fetching the logo…",
|
|
35
|
+
doingSaveFile: "Fetching a file…",
|
|
36
|
+
doingSetLogoSections: "Grouping the logos…",
|
|
37
|
+
doingSetAssetSections: "Grouping the assets…",
|
|
38
|
+
doingSetTypography: "Entering the fonts…",
|
|
39
|
+
doingSetGradients: "Entering the gradients…",
|
|
40
|
+
doingSetRules: "Entering rules…",
|
|
41
|
+
doingSetProfile: "Writing title and description…",
|
|
42
|
+
doingSetFacts: "Entering facts…",
|
|
43
|
+
doingSetColors: "Entering the colours…",
|
|
44
|
+
doingLookAtFile: "Looking at a file…",
|
|
45
|
+
doingDescribeFile: "Describing a file…",
|
|
46
|
+
doingWebSearch: "Searching the web…",
|
|
47
|
+
doingReadSite: "Reading {host}…",
|
|
48
|
+
doingReadWebsite: "Reading the website…",
|
|
47
49
|
doingGuide: "Reading the building guide",
|
|
48
50
|
doingTemplate: "Looking at a template",
|
|
49
51
|
doingReadWizard: "Reading the wizard",
|
|
@@ -53,24 +55,24 @@ const EN = {
|
|
|
53
55
|
doingTestWizard: "Testing the wizard",
|
|
54
56
|
doingFindService: "Looking for a service",
|
|
55
57
|
doingWork: "Working",
|
|
56
|
-
doingFlow: "Building the flow
|
|
57
|
-
doingWriteFile: "Writing {path}
|
|
58
|
-
doingWriteAFile: "Writing a file
|
|
59
|
-
doingDownload: "Downloading data
|
|
60
|
-
doingReadWeb: "Reading the web
|
|
61
|
-
doingTestWidget: "Testing the widget
|
|
62
|
-
doingPrepare: "Preparing {output}
|
|
63
|
-
doingCalculate: "Calculating
|
|
64
|
-
doingLookFiles: "Looking at the files
|
|
65
|
-
doingTidyFiles: "Tidying up files
|
|
66
|
-
doingReadGuide: "Reading the guide
|
|
67
|
-
doingAuthoringGuide: "Reading the authoring guide
|
|
68
|
-
doingGallery: "Searching the gallery
|
|
69
|
-
doingPlans: "Looking up the plans
|
|
70
|
-
doingAccount: "Looking up the account
|
|
71
|
-
doingUsage: "Looking up the usage
|
|
72
|
-
doingModels: "Looking up the models
|
|
73
|
-
doingEarlierChats: "Searching earlier chats
|
|
58
|
+
doingFlow: "Building the flow…",
|
|
59
|
+
doingWriteFile: "Writing {path}…",
|
|
60
|
+
doingWriteAFile: "Writing a file…",
|
|
61
|
+
doingDownload: "Downloading data…",
|
|
62
|
+
doingReadWeb: "Reading the web…",
|
|
63
|
+
doingTestWidget: "Testing the widget…",
|
|
64
|
+
doingPrepare: "Preparing {output}…",
|
|
65
|
+
doingCalculate: "Calculating…",
|
|
66
|
+
doingLookFiles: "Looking at the files…",
|
|
67
|
+
doingTidyFiles: "Tidying up files…",
|
|
68
|
+
doingReadGuide: "Reading the guide…",
|
|
69
|
+
doingAuthoringGuide: "Reading the authoring guide…",
|
|
70
|
+
doingGallery: "Searching the gallery…",
|
|
71
|
+
doingPlans: "Looking up the plans…",
|
|
72
|
+
doingAccount: "Looking up the account…",
|
|
73
|
+
doingUsage: "Looking up the usage…",
|
|
74
|
+
doingModels: "Looking up the models…",
|
|
75
|
+
doingEarlierChats: "Searching earlier chats…",
|
|
74
76
|
somethingWrong: "Something went wrong. Try again.",
|
|
75
77
|
firstSpaceCloud: "My wizards",
|
|
76
78
|
firstSpaceLocal: "My computer",
|
|
@@ -95,6 +97,7 @@ const EN = {
|
|
|
95
97
|
notFound: "This doesn’t exist or was deleted.",
|
|
96
98
|
ownKeysNotInPlan: "Your own API keys aren’t part of the {plan} plan.",
|
|
97
99
|
ownKeysNotInTeamPlan: "Your own API keys aren’t part of the team’s plan.",
|
|
100
|
+
planUnavailable: "The team’s plan can’t be checked right now. Try again later.",
|
|
98
101
|
};
|
|
99
102
|
const DE = {
|
|
100
103
|
keyUnknown: "Der Anbieter kennt diesen API-Key nicht.",
|
|
@@ -104,6 +107,8 @@ const DE = {
|
|
|
104
107
|
imported: "Importiert: {steps}, {files}.",
|
|
105
108
|
stepCount: { one: "{count} Schritt", other: "{count} Schritte" },
|
|
106
109
|
fileCount: { one: "{count} Datei", other: "{count} Dateien" },
|
|
110
|
+
yes: "Ja",
|
|
111
|
+
no: "Nein",
|
|
107
112
|
spaceStillNeeds: "Diesem Space fehlt noch: {needs}. Füge es unter Einstellungen → Connectors hinzu.",
|
|
108
113
|
notFinished: "Ich bin noch nicht ganz fertig geworden. Schreib „weiter“, dann mache ich genau dort weiter.",
|
|
109
114
|
done: "Erledigt.",
|
|
@@ -116,7 +121,7 @@ const DE = {
|
|
|
116
121
|
faqAnswer: "Antwort",
|
|
117
122
|
tableTitle: "Tabelle",
|
|
118
123
|
newWizard: "Neuer Wizard",
|
|
119
|
-
newWizardStart: "Los geht
|
|
124
|
+
newWizardStart: "Los geht’s",
|
|
120
125
|
newWizardAsk: "Worum geht es?",
|
|
121
126
|
newWizardDone: "Fertig",
|
|
122
127
|
templateSetUp: "Ich habe die Vorlage „{title}“ für dich angelegt. Teste sie rechts oben – oder sag mir, was anders sein soll.",
|
|
@@ -187,6 +192,7 @@ const DE = {
|
|
|
187
192
|
notFound: "Das gibt es nicht oder nicht mehr.",
|
|
188
193
|
ownKeysNotInPlan: "Eigene API-Keys gibt es nicht im Paket {plan}.",
|
|
189
194
|
ownKeysNotInTeamPlan: "Eigene API-Keys gibt es nicht im Paket des Teams.",
|
|
195
|
+
planUnavailable: "Das Paket des Teams lässt sich gerade nicht prüfen. Versuch es später noch einmal.",
|
|
190
196
|
};
|
|
191
197
|
export const SERVER_TEXTS = { en: EN, de: DE };
|
|
192
198
|
/** A line of the server's table in a language; English where the language lacks it. */
|