@jxsuite/server 4.2.0 → 4.2.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/package.json +11 -11
- package/src/ai-api.ts +65 -17
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jxsuite/server",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.2",
|
|
4
4
|
"description": "Jx development server with live reload, server-side proxy, and studio integration",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -42,21 +42,21 @@
|
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@jxsuite/catalog": "^0.1.0",
|
|
44
44
|
"@jxsuite/collab": "^0.9.0",
|
|
45
|
-
"@jxsuite/compiler": "^4.0.
|
|
46
|
-
"@jxsuite/create": "^1.3.
|
|
47
|
-
"@jxsuite/import": "^0.40.
|
|
48
|
-
"@jxsuite/protocol": "^2.3.
|
|
49
|
-
"@jxsuite/runtime": "^4.0.
|
|
45
|
+
"@jxsuite/compiler": "^4.0.1",
|
|
46
|
+
"@jxsuite/create": "^1.3.11",
|
|
47
|
+
"@jxsuite/import": "^0.40.3",
|
|
48
|
+
"@jxsuite/protocol": "^2.3.1",
|
|
49
|
+
"@jxsuite/runtime": "^4.0.1",
|
|
50
50
|
"@jxsuite/schema": "^2.2.0",
|
|
51
|
-
"@jxsuite/site": "^2.0.
|
|
52
|
-
"@jxsuite/starters": "^1.8.
|
|
51
|
+
"@jxsuite/site": "^2.0.1",
|
|
52
|
+
"@jxsuite/starters": "^1.8.1",
|
|
53
53
|
"chokidar": "^5.0.0",
|
|
54
|
-
"kysely": "^0.29.
|
|
55
|
-
"zod": "^4.6.
|
|
54
|
+
"kysely": "^0.29.6",
|
|
55
|
+
"zod": "^4.6.5"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@jxsuite/auth": "^0.6.0",
|
|
59
59
|
"@jxsuite/connector": "^0.6.0",
|
|
60
|
-
"@jxsuite/parser": "^1.8.
|
|
60
|
+
"@jxsuite/parser": "^1.8.1"
|
|
61
61
|
}
|
|
62
62
|
}
|
package/src/ai-api.ts
CHANGED
|
@@ -150,6 +150,48 @@ function writeSSE(controller: ReadableStreamDefaultController, event: unknown):
|
|
|
150
150
|
controller.enqueue(new TextEncoder().encode(`data: ${data}\n\n`));
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
/**
|
|
154
|
+
* Extract a human-readable message from an upstream error body, falling back to the raw body (or
|
|
155
|
+
* `fallback` when the body is empty).
|
|
156
|
+
*
|
|
157
|
+
* OpenAI-compatible providers return `{ error: "..." }` or `{ error: { message: "..." } }`.
|
|
158
|
+
* Cloudflare's own REST API — what a BYOK base URL pointed at Workers AI actually returns — uses a
|
|
159
|
+
* different envelope, `{ success: false, errors: [{ code, message }] }`, with no top-level `error`
|
|
160
|
+
* key at all. Without this branch that shape parses as valid JSON with nothing matched, and the
|
|
161
|
+
* caller was left showing the whole raw body (e.g. the full `{"success":false,"errors":[...]}`
|
|
162
|
+
* blob) instead of the human sentence inside it.
|
|
163
|
+
*/
|
|
164
|
+
function extractUpstreamErrorMessage(rawBody: string, fallback: string): string {
|
|
165
|
+
if (!rawBody) {
|
|
166
|
+
return fallback;
|
|
167
|
+
}
|
|
168
|
+
try {
|
|
169
|
+
const { error, errors } = JSON.parse(rawBody) as {
|
|
170
|
+
error?: string | { message?: string };
|
|
171
|
+
errors?: { code?: number; message?: string }[];
|
|
172
|
+
};
|
|
173
|
+
if (typeof error === "string") {
|
|
174
|
+
return error;
|
|
175
|
+
}
|
|
176
|
+
if (error) {
|
|
177
|
+
const { message } = error;
|
|
178
|
+
if (message) {
|
|
179
|
+
return message;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const [first] = errors ?? [];
|
|
183
|
+
if (first) {
|
|
184
|
+
const { message } = first;
|
|
185
|
+
if (message) {
|
|
186
|
+
return message;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
} catch {
|
|
190
|
+
/* Not JSON — use the raw body. */
|
|
191
|
+
}
|
|
192
|
+
return rawBody;
|
|
193
|
+
}
|
|
194
|
+
|
|
153
195
|
/**
|
|
154
196
|
* Write a failure response for the non-streaming endpoints.
|
|
155
197
|
*
|
|
@@ -265,20 +307,7 @@ export async function handleChat(req: Request): Promise<Response> {
|
|
|
265
307
|
} catch {
|
|
266
308
|
/* Ignore */
|
|
267
309
|
}
|
|
268
|
-
|
|
269
|
-
// While some compatible providers return { error: "..." }). Extract a clean message
|
|
270
|
-
// Instead of embedding the raw JSON in the error text.
|
|
271
|
-
let cleanMessage = errorBody || response.statusText;
|
|
272
|
-
try {
|
|
273
|
-
const parsed = JSON.parse(errorBody) as { error?: string | { message?: string } };
|
|
274
|
-
if (typeof parsed.error === "string") {
|
|
275
|
-
cleanMessage = parsed.error;
|
|
276
|
-
} else if (parsed.error?.message) {
|
|
277
|
-
cleanMessage = parsed.error.message;
|
|
278
|
-
}
|
|
279
|
-
} catch {
|
|
280
|
-
/* Not JSON — use the raw body. */
|
|
281
|
-
}
|
|
310
|
+
const cleanMessage = extractUpstreamErrorMessage(errorBody, response.statusText);
|
|
282
311
|
writeSSE(controller, {
|
|
283
312
|
code: String(response.status),
|
|
284
313
|
message: cleanMessage,
|
|
@@ -499,9 +528,22 @@ export async function handleModels(req: Request): Promise<Response> {
|
|
|
499
528
|
|
|
500
529
|
if (!upstreamResp.ok) {
|
|
501
530
|
// Upstream failed — return defaults with configured flag so user can still try.
|
|
531
|
+
let errorBody = "";
|
|
532
|
+
try {
|
|
533
|
+
errorBody = await upstreamResp.text();
|
|
534
|
+
} catch {
|
|
535
|
+
/* Ignore */
|
|
536
|
+
}
|
|
537
|
+
const upstreamMessage = extractUpstreamErrorMessage(errorBody, upstreamResp.statusText);
|
|
502
538
|
const defaults = [{ id: "gpt-4o", name: "GPT-4o", contextWindow: 128_000 }];
|
|
503
539
|
return Response.json(
|
|
504
|
-
{
|
|
540
|
+
{
|
|
541
|
+
models: defaults,
|
|
542
|
+
configured: true,
|
|
543
|
+
managed: false,
|
|
544
|
+
upstreamError: upstreamResp.status,
|
|
545
|
+
upstreamMessage,
|
|
546
|
+
},
|
|
505
547
|
{
|
|
506
548
|
headers: { "Content-Type": "application/json" },
|
|
507
549
|
},
|
|
@@ -529,11 +571,17 @@ export async function handleModels(req: Request): Promise<Response> {
|
|
|
529
571
|
headers: { "Content-Type": "application/json" },
|
|
530
572
|
},
|
|
531
573
|
);
|
|
532
|
-
} catch {
|
|
574
|
+
} catch (error) {
|
|
533
575
|
// Network error → return defaults.
|
|
534
576
|
const defaults = [{ id: "gpt-4o", name: "GPT-4o", contextWindow: 128_000 }];
|
|
535
577
|
return Response.json(
|
|
536
|
-
{
|
|
578
|
+
{
|
|
579
|
+
models: defaults,
|
|
580
|
+
configured: true,
|
|
581
|
+
managed: false,
|
|
582
|
+
upstreamError: "network",
|
|
583
|
+
upstreamMessage: (error as Error).message,
|
|
584
|
+
},
|
|
537
585
|
{
|
|
538
586
|
headers: { "Content-Type": "application/json" },
|
|
539
587
|
},
|