@valbuild/server 0.95.0 → 0.96.0
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/dist/valbuild-server.cjs.dev.js +404 -10
- package/dist/valbuild-server.cjs.prod.js +404 -10
- package/dist/valbuild-server.esm.js +404 -10
- package/package.json +4 -4
|
@@ -2610,6 +2610,76 @@ class ValOpsFS extends ValOps {
|
|
|
2610
2610
|
async onInit() {
|
|
2611
2611
|
// do nothing
|
|
2612
2612
|
}
|
|
2613
|
+
async getPresignedAuthNonce(project, corsOrigin, auth) {
|
|
2614
|
+
const authHeader = "pat" in auth ? {
|
|
2615
|
+
"x-val-pat": auth.pat
|
|
2616
|
+
} : {
|
|
2617
|
+
Authorization: `Bearer ${auth.apiKey}`
|
|
2618
|
+
};
|
|
2619
|
+
try {
|
|
2620
|
+
const res = await fetch(`${this.contentUrl}/v1/${project}/presigned-auth-nonce`, {
|
|
2621
|
+
method: "POST",
|
|
2622
|
+
headers: {
|
|
2623
|
+
...authHeader,
|
|
2624
|
+
"Content-Type": "application/json"
|
|
2625
|
+
},
|
|
2626
|
+
body: JSON.stringify({
|
|
2627
|
+
corsOrigin
|
|
2628
|
+
})
|
|
2629
|
+
});
|
|
2630
|
+
if (res.ok) {
|
|
2631
|
+
const json = await res.json();
|
|
2632
|
+
const parsed = zod.z.object({
|
|
2633
|
+
nonce: zod.z.string(),
|
|
2634
|
+
expiresAt: zod.z.string()
|
|
2635
|
+
}).safeParse(json);
|
|
2636
|
+
if (parsed.success) {
|
|
2637
|
+
return {
|
|
2638
|
+
status: "success",
|
|
2639
|
+
data: {
|
|
2640
|
+
nonce: parsed.data.nonce,
|
|
2641
|
+
baseUrl: `${this.contentUrl}/v1/${project}`
|
|
2642
|
+
}
|
|
2643
|
+
};
|
|
2644
|
+
}
|
|
2645
|
+
console.error("Could not parse presigned auth nonce response. Error: " + zodValidationError.fromError(parsed.error));
|
|
2646
|
+
return {
|
|
2647
|
+
status: "error",
|
|
2648
|
+
statusCode: 500,
|
|
2649
|
+
error: {
|
|
2650
|
+
message: "Could not get presigned auth nonce. The response from the content host was not in the expected format."
|
|
2651
|
+
}
|
|
2652
|
+
};
|
|
2653
|
+
}
|
|
2654
|
+
if (res.status === 401) {
|
|
2655
|
+
return {
|
|
2656
|
+
status: "error",
|
|
2657
|
+
statusCode: 401,
|
|
2658
|
+
error: {
|
|
2659
|
+
message: "Could not get presigned auth nonce. The local PAT was rejected by the content host. Try re-running `val login`."
|
|
2660
|
+
}
|
|
2661
|
+
};
|
|
2662
|
+
}
|
|
2663
|
+
const unknownErrorMessage = `Could not get presigned auth nonce. HTTP error: ${res.status} ${res.statusText}`;
|
|
2664
|
+
console.error(unknownErrorMessage);
|
|
2665
|
+
return {
|
|
2666
|
+
status: "error",
|
|
2667
|
+
statusCode: 500,
|
|
2668
|
+
error: {
|
|
2669
|
+
message: unknownErrorMessage
|
|
2670
|
+
}
|
|
2671
|
+
};
|
|
2672
|
+
} catch (e) {
|
|
2673
|
+
console.error("Could not get presigned auth nonce (connection error?):", e);
|
|
2674
|
+
return {
|
|
2675
|
+
status: "error",
|
|
2676
|
+
statusCode: 500,
|
|
2677
|
+
error: {
|
|
2678
|
+
message: `Could not get presigned auth nonce. Error: ${e instanceof Error ? e.message : JSON.stringify(e)}`
|
|
2679
|
+
}
|
|
2680
|
+
};
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2613
2683
|
async getCommitSummary() {
|
|
2614
2684
|
return {
|
|
2615
2685
|
error: {
|
|
@@ -3931,7 +4001,7 @@ class ValOpsHttp extends ValOps {
|
|
|
3931
4001
|
return {
|
|
3932
4002
|
status: "error",
|
|
3933
4003
|
error: {
|
|
3934
|
-
message: "Could not get nonce." + message
|
|
4004
|
+
message: "Could not get nonce. " + message
|
|
3935
4005
|
}
|
|
3936
4006
|
};
|
|
3937
4007
|
}
|
|
@@ -4678,6 +4748,14 @@ function hasRemoteFileSchema(schema) {
|
|
|
4678
4748
|
|
|
4679
4749
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
4680
4750
|
const ValServer = (valModules, options, callbacks) => {
|
|
4751
|
+
const AIContentBlock = zod.z.union([zod.z.object({
|
|
4752
|
+
type: zod.z.literal("text"),
|
|
4753
|
+
text: zod.z.string()
|
|
4754
|
+
}), zod.z.object({
|
|
4755
|
+
type: zod.z.literal("image_url"),
|
|
4756
|
+
url: zod.z.string()
|
|
4757
|
+
})]);
|
|
4758
|
+
const AIMessageContent = zod.z.union([zod.z.string(), zod.z.array(AIContentBlock)]);
|
|
4681
4759
|
const ProfilesResponse = zod.z.object({
|
|
4682
4760
|
profiles: zod.z.array(zod.z.object({
|
|
4683
4761
|
profileId: zod.z.string(),
|
|
@@ -5475,15 +5553,38 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
5475
5553
|
};
|
|
5476
5554
|
}
|
|
5477
5555
|
if (serverOps instanceof ValOpsFS) {
|
|
5478
|
-
// In FS mode
|
|
5479
|
-
//
|
|
5480
|
-
//
|
|
5556
|
+
// In FS mode patch-file uploads are buffered through this server (no remote round-trip),
|
|
5557
|
+
// so baseUrl points at /api/val/upload. AI image uploads, however, go straight to the
|
|
5558
|
+
// content host — we resolve a contentBaseUrl + a PAT-issued nonce here so the browser
|
|
5559
|
+
// can POST directly without exposing the PAT.
|
|
5481
5560
|
const host = `/api/val`;
|
|
5561
|
+
let contentBaseUrl = null;
|
|
5562
|
+
let contentAuthNonce = null;
|
|
5563
|
+
if (!options.project) {
|
|
5564
|
+
console.warn("Direct content-host uploads (AI images) disabled: no `project` set in val.config (and VAL_PROJECT env var is not set).");
|
|
5565
|
+
} else {
|
|
5566
|
+
const authDataRes = await getRemoteFileAuth();
|
|
5567
|
+
if (authDataRes.status !== 200) {
|
|
5568
|
+
console.warn("Direct content-host uploads (AI images) disabled: " + authDataRes.json.message);
|
|
5569
|
+
} else {
|
|
5570
|
+
const corsOrigin = "*"; // TODO: add cors origin
|
|
5571
|
+
const presignedAuthNonce = await serverOps.getPresignedAuthNonce(options.project, corsOrigin, authDataRes.json.remoteFileAuth);
|
|
5572
|
+
if (presignedAuthNonce.status === "success") {
|
|
5573
|
+
contentBaseUrl = presignedAuthNonce.data.baseUrl;
|
|
5574
|
+
contentAuthNonce = presignedAuthNonce.data.nonce;
|
|
5575
|
+
} else {
|
|
5576
|
+
console.warn("Direct content-host uploads (AI images) disabled: " + presignedAuthNonce.error.message);
|
|
5577
|
+
}
|
|
5578
|
+
}
|
|
5579
|
+
}
|
|
5482
5580
|
return {
|
|
5483
5581
|
status: 200,
|
|
5484
5582
|
json: {
|
|
5485
5583
|
nonce: null,
|
|
5486
|
-
baseUrl: `${host}/upload
|
|
5584
|
+
baseUrl: `${host}/upload`,
|
|
5585
|
+
// NOTE: this is the /upload/patches endpoint - the client will add /patches/:patchId/files to this and post to it
|
|
5586
|
+
contentBaseUrl,
|
|
5587
|
+
contentAuthNonce
|
|
5487
5588
|
}
|
|
5488
5589
|
};
|
|
5489
5590
|
}
|
|
@@ -5509,7 +5610,9 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
5509
5610
|
status: 200,
|
|
5510
5611
|
json: {
|
|
5511
5612
|
nonce: presignedAuthNonce.data.nonce,
|
|
5512
|
-
baseUrl: presignedAuthNonce.data.baseUrl
|
|
5613
|
+
baseUrl: presignedAuthNonce.data.baseUrl,
|
|
5614
|
+
contentBaseUrl: presignedAuthNonce.data.baseUrl,
|
|
5615
|
+
contentAuthNonce: presignedAuthNonce.data.nonce
|
|
5513
5616
|
}
|
|
5514
5617
|
};
|
|
5515
5618
|
}
|
|
@@ -6179,7 +6282,7 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
6179
6282
|
}
|
|
6180
6283
|
if (!options.project) {
|
|
6181
6284
|
return {
|
|
6182
|
-
status:
|
|
6285
|
+
status: 401,
|
|
6183
6286
|
json: {
|
|
6184
6287
|
message: "Project is not configured"
|
|
6185
6288
|
}
|
|
@@ -6188,7 +6291,7 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
6188
6291
|
const authDataRes = await getRemoteFileAuth();
|
|
6189
6292
|
if (authDataRes.status !== 200) {
|
|
6190
6293
|
return {
|
|
6191
|
-
status:
|
|
6294
|
+
status: 401,
|
|
6192
6295
|
json: {
|
|
6193
6296
|
message: authDataRes.json.message
|
|
6194
6297
|
}
|
|
@@ -6481,14 +6584,19 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
6481
6584
|
const SessionMessagesResponse = zod.z.object({
|
|
6482
6585
|
messages: zod.z.array(zod.z.object({
|
|
6483
6586
|
role: zod.z.string(),
|
|
6484
|
-
content:
|
|
6587
|
+
content: AIMessageContent
|
|
6485
6588
|
})),
|
|
6486
6589
|
nextCursor: zod.z.object({
|
|
6487
6590
|
updatedAt: zod.z.string(),
|
|
6488
6591
|
id: zod.z.string()
|
|
6489
6592
|
}).nullable().optional()
|
|
6490
6593
|
});
|
|
6491
|
-
const
|
|
6594
|
+
const params = new URLSearchParams();
|
|
6595
|
+
if (req.query.limit) params.set("limit", req.query.limit);
|
|
6596
|
+
if (req.query.cursor_updatedAt) params.set("cursor_updatedAt", req.query.cursor_updatedAt);
|
|
6597
|
+
if (req.query.cursor_id) params.set("cursor_id", req.query.cursor_id);
|
|
6598
|
+
const qs = params.toString();
|
|
6599
|
+
const upstreamUrl = `${options.valContentUrl}/v1/${options.project}/ai/sessions/${encodeURIComponent(sessionId)}/messages` + (qs ? `?${qs}` : "");
|
|
6492
6600
|
const upstreamRes = await fetch(upstreamUrl, {
|
|
6493
6601
|
headers
|
|
6494
6602
|
});
|
|
@@ -6539,6 +6647,292 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
6539
6647
|
});
|
|
6540
6648
|
}
|
|
6541
6649
|
},
|
|
6650
|
+
"/ai/session-image-to-patch-file": {
|
|
6651
|
+
POST: async req => {
|
|
6652
|
+
const cookies = req.cookies;
|
|
6653
|
+
const auth = getAuth(cookies);
|
|
6654
|
+
if (auth.error) {
|
|
6655
|
+
return {
|
|
6656
|
+
status: 401,
|
|
6657
|
+
json: {
|
|
6658
|
+
message: auth.error
|
|
6659
|
+
}
|
|
6660
|
+
};
|
|
6661
|
+
}
|
|
6662
|
+
if (!options.project) {
|
|
6663
|
+
return {
|
|
6664
|
+
status: 500,
|
|
6665
|
+
json: {
|
|
6666
|
+
message: "Project is not configured"
|
|
6667
|
+
}
|
|
6668
|
+
};
|
|
6669
|
+
}
|
|
6670
|
+
const authDataRes = await getRemoteFileAuth();
|
|
6671
|
+
if (authDataRes.status !== 200) {
|
|
6672
|
+
return {
|
|
6673
|
+
status: 500,
|
|
6674
|
+
json: {
|
|
6675
|
+
message: authDataRes.json.message
|
|
6676
|
+
}
|
|
6677
|
+
};
|
|
6678
|
+
}
|
|
6679
|
+
const authData = authDataRes.json.remoteFileAuth;
|
|
6680
|
+
let headers;
|
|
6681
|
+
if (serverOps instanceof ValOpsFS) {
|
|
6682
|
+
headers = getProfileAuthHeaders(authData, null, "application/json");
|
|
6683
|
+
} else {
|
|
6684
|
+
if (!("id" in auth) || !auth.id) {
|
|
6685
|
+
return {
|
|
6686
|
+
status: 401,
|
|
6687
|
+
json: {
|
|
6688
|
+
message: "Unauthorized"
|
|
6689
|
+
}
|
|
6690
|
+
};
|
|
6691
|
+
}
|
|
6692
|
+
headers = getProfileAuthHeaders(authData, {
|
|
6693
|
+
sub: auth.id
|
|
6694
|
+
}, "application/json");
|
|
6695
|
+
}
|
|
6696
|
+
const execFetch = async () => {
|
|
6697
|
+
try {
|
|
6698
|
+
const upstreamUrl = `${options.valContentUrl}/v1/${options.project}/patches/${encodeURIComponent(req.body.patchId)}/files/from-session-file`;
|
|
6699
|
+
const upstreamRes = await fetch(upstreamUrl, {
|
|
6700
|
+
method: "POST",
|
|
6701
|
+
headers,
|
|
6702
|
+
body: JSON.stringify({
|
|
6703
|
+
files: req.body.files.map(f => ({
|
|
6704
|
+
filePath: f.filePath,
|
|
6705
|
+
key: f.key,
|
|
6706
|
+
...(f.isRemote !== undefined ? {
|
|
6707
|
+
isRemote: f.isRemote
|
|
6708
|
+
} : {})
|
|
6709
|
+
}))
|
|
6710
|
+
})
|
|
6711
|
+
});
|
|
6712
|
+
if (!upstreamRes.ok) {
|
|
6713
|
+
var _upstreamJson;
|
|
6714
|
+
const text = await upstreamRes.text();
|
|
6715
|
+
let upstreamJson = null;
|
|
6716
|
+
try {
|
|
6717
|
+
upstreamJson = JSON.parse(text);
|
|
6718
|
+
} catch {
|
|
6719
|
+
upstreamJson = null;
|
|
6720
|
+
}
|
|
6721
|
+
if (upstreamRes.status === 400 && upstreamJson) {
|
|
6722
|
+
return {
|
|
6723
|
+
status: 400,
|
|
6724
|
+
json: {
|
|
6725
|
+
message: upstreamJson.message ?? `AI session image to patch failed: ${text}`,
|
|
6726
|
+
...(upstreamJson.details ? {
|
|
6727
|
+
details: upstreamJson.details
|
|
6728
|
+
} : {})
|
|
6729
|
+
}
|
|
6730
|
+
};
|
|
6731
|
+
}
|
|
6732
|
+
return {
|
|
6733
|
+
status: 500,
|
|
6734
|
+
json: {
|
|
6735
|
+
message: `AI session image to patch failed: ${upstreamRes.status} ${((_upstreamJson = upstreamJson) === null || _upstreamJson === void 0 ? void 0 : _upstreamJson.message) ?? text}`
|
|
6736
|
+
}
|
|
6737
|
+
};
|
|
6738
|
+
}
|
|
6739
|
+
const rawUpstreamJson = await upstreamRes.json();
|
|
6740
|
+
const UpstreamResponse = zod.z.object({
|
|
6741
|
+
patchId: zod.z.string(),
|
|
6742
|
+
files: zod.z.array(zod.z.object({
|
|
6743
|
+
filePath: zod.z.string(),
|
|
6744
|
+
metadata: zod.z.object({
|
|
6745
|
+
width: zod.z.number(),
|
|
6746
|
+
height: zod.z.number(),
|
|
6747
|
+
mimeType: zod.z.string()
|
|
6748
|
+
})
|
|
6749
|
+
}))
|
|
6750
|
+
});
|
|
6751
|
+
const json = UpstreamResponse.safeParse(rawUpstreamJson);
|
|
6752
|
+
if (!json.success) {
|
|
6753
|
+
return {
|
|
6754
|
+
status: 500,
|
|
6755
|
+
json: {
|
|
6756
|
+
message: "Could not parse AI session image patch response: " + zodValidationError.fromError(json.error).toString()
|
|
6757
|
+
}
|
|
6758
|
+
};
|
|
6759
|
+
}
|
|
6760
|
+
if (json.data.patchId !== req.body.patchId) {
|
|
6761
|
+
return {
|
|
6762
|
+
status: 500,
|
|
6763
|
+
json: {
|
|
6764
|
+
message: `AI session image to patch upstream returned mismatched patchId: expected '${req.body.patchId}', got '${json.data.patchId}'`
|
|
6765
|
+
}
|
|
6766
|
+
};
|
|
6767
|
+
}
|
|
6768
|
+
if (serverOps instanceof ValOpsFS) {
|
|
6769
|
+
// Mirror the binaries from the content host into local patch
|
|
6770
|
+
// storage so /files?patch_id=... can serve them. Match upstream
|
|
6771
|
+
// entries to client-provided keys by filePath.
|
|
6772
|
+
const keysByFilePath = new Map(req.body.files.map(f => [f.filePath, f.key]));
|
|
6773
|
+
for (const file of json.data.files) {
|
|
6774
|
+
const sessionKey = keysByFilePath.get(file.filePath);
|
|
6775
|
+
if (!sessionKey) {
|
|
6776
|
+
return {
|
|
6777
|
+
status: 500,
|
|
6778
|
+
json: {
|
|
6779
|
+
message: `AI session image to patch: upstream returned file '${file.filePath}' which was not in the request`
|
|
6780
|
+
}
|
|
6781
|
+
};
|
|
6782
|
+
}
|
|
6783
|
+
const downloadUrl = `${options.valContentUrl}/v1/${options.project}/ai/images?key=${encodeURIComponent(sessionKey)}`;
|
|
6784
|
+
let binaryRes;
|
|
6785
|
+
try {
|
|
6786
|
+
binaryRes = await fetch(downloadUrl, {
|
|
6787
|
+
headers
|
|
6788
|
+
});
|
|
6789
|
+
} catch (err) {
|
|
6790
|
+
return {
|
|
6791
|
+
status: 500,
|
|
6792
|
+
json: {
|
|
6793
|
+
message: `Could not download AI session image '${file.filePath}' from content host: ${err instanceof Error ? err.message : String(err)}`
|
|
6794
|
+
}
|
|
6795
|
+
};
|
|
6796
|
+
}
|
|
6797
|
+
if (!binaryRes.ok) {
|
|
6798
|
+
return {
|
|
6799
|
+
status: 500,
|
|
6800
|
+
json: {
|
|
6801
|
+
message: `Could not download AI session image '${file.filePath}' from content host: ${binaryRes.status} ${binaryRes.statusText}`
|
|
6802
|
+
}
|
|
6803
|
+
};
|
|
6804
|
+
}
|
|
6805
|
+
const arrayBuffer = await binaryRes.arrayBuffer();
|
|
6806
|
+
const base64 = Buffer.from(arrayBuffer).toString("base64");
|
|
6807
|
+
const dataUrl = `data:${file.metadata.mimeType};base64,${base64}`;
|
|
6808
|
+
const type = file.metadata.mimeType.startsWith("image/") ? "image" : "file";
|
|
6809
|
+
const saveRes = await serverOps.saveBase64EncodedBinaryFileFromPatch(file.filePath, req.body.parentRef, req.body.patchId, dataUrl, type, file.metadata);
|
|
6810
|
+
if (saveRes.error) {
|
|
6811
|
+
return {
|
|
6812
|
+
status: 500,
|
|
6813
|
+
json: {
|
|
6814
|
+
message: `Could not save AI session image '${file.filePath}' to local patch storage: ${saveRes.error.message}`
|
|
6815
|
+
}
|
|
6816
|
+
};
|
|
6817
|
+
}
|
|
6818
|
+
}
|
|
6819
|
+
}
|
|
6820
|
+
return {
|
|
6821
|
+
status: 200,
|
|
6822
|
+
json: {
|
|
6823
|
+
patchId: req.body.patchId,
|
|
6824
|
+
files: json.data.files
|
|
6825
|
+
}
|
|
6826
|
+
};
|
|
6827
|
+
} catch (err) {
|
|
6828
|
+
return {
|
|
6829
|
+
status: 500,
|
|
6830
|
+
json: {
|
|
6831
|
+
message: err instanceof Error ? err.message : "AI session image to patch error"
|
|
6832
|
+
}
|
|
6833
|
+
};
|
|
6834
|
+
}
|
|
6835
|
+
};
|
|
6836
|
+
return execFetch();
|
|
6837
|
+
}
|
|
6838
|
+
},
|
|
6839
|
+
"/ai/images": {
|
|
6840
|
+
PATCH: async req => {
|
|
6841
|
+
const cookies = req.cookies;
|
|
6842
|
+
const auth = getAuth(cookies);
|
|
6843
|
+
if (auth.error) {
|
|
6844
|
+
return {
|
|
6845
|
+
status: 401,
|
|
6846
|
+
json: {
|
|
6847
|
+
message: auth.error
|
|
6848
|
+
}
|
|
6849
|
+
};
|
|
6850
|
+
}
|
|
6851
|
+
if (!options.project) {
|
|
6852
|
+
return {
|
|
6853
|
+
status: 500,
|
|
6854
|
+
json: {
|
|
6855
|
+
message: "Project is not configured"
|
|
6856
|
+
}
|
|
6857
|
+
};
|
|
6858
|
+
}
|
|
6859
|
+
const authDataRes = await getRemoteFileAuth();
|
|
6860
|
+
if (authDataRes.status !== 200) {
|
|
6861
|
+
return {
|
|
6862
|
+
status: 500,
|
|
6863
|
+
json: {
|
|
6864
|
+
message: authDataRes.json.message
|
|
6865
|
+
}
|
|
6866
|
+
};
|
|
6867
|
+
}
|
|
6868
|
+
const authData = authDataRes.json.remoteFileAuth;
|
|
6869
|
+
let headers;
|
|
6870
|
+
if (serverOps instanceof ValOpsFS) {
|
|
6871
|
+
headers = getProfileAuthHeaders(authData, null, "application/json");
|
|
6872
|
+
} else {
|
|
6873
|
+
if (!("id" in auth) || !auth.id) {
|
|
6874
|
+
return {
|
|
6875
|
+
status: 401,
|
|
6876
|
+
json: {
|
|
6877
|
+
message: "Unauthorized"
|
|
6878
|
+
}
|
|
6879
|
+
};
|
|
6880
|
+
}
|
|
6881
|
+
headers = getProfileAuthHeaders(authData, {
|
|
6882
|
+
sub: auth.id
|
|
6883
|
+
}, "application/json");
|
|
6884
|
+
}
|
|
6885
|
+
const execFetch = async () => {
|
|
6886
|
+
try {
|
|
6887
|
+
const upstreamUrl = `${options.valContentUrl}/v1/${options.project}/ai/images`;
|
|
6888
|
+
const upstreamRes = await fetch(upstreamUrl, {
|
|
6889
|
+
method: "PATCH",
|
|
6890
|
+
headers,
|
|
6891
|
+
body: JSON.stringify({
|
|
6892
|
+
key: req.body.key,
|
|
6893
|
+
metadata: req.body.metadata,
|
|
6894
|
+
contentType: req.body.contentType
|
|
6895
|
+
})
|
|
6896
|
+
});
|
|
6897
|
+
if (!upstreamRes.ok) {
|
|
6898
|
+
const text = await upstreamRes.text();
|
|
6899
|
+
return {
|
|
6900
|
+
status: 500,
|
|
6901
|
+
json: {
|
|
6902
|
+
message: `AI images patch failed: ${upstreamRes.status} ${text}`
|
|
6903
|
+
}
|
|
6904
|
+
};
|
|
6905
|
+
}
|
|
6906
|
+
const UpstreamResponse = zod.z.object({
|
|
6907
|
+
key: zod.z.string()
|
|
6908
|
+
});
|
|
6909
|
+
const json = UpstreamResponse.safeParse(await upstreamRes.json());
|
|
6910
|
+
if (!json.success) {
|
|
6911
|
+
return {
|
|
6912
|
+
status: 500,
|
|
6913
|
+
json: {
|
|
6914
|
+
message: "Could not parse AI images patch response: " + zodValidationError.fromError(json.error).toString()
|
|
6915
|
+
}
|
|
6916
|
+
};
|
|
6917
|
+
}
|
|
6918
|
+
return {
|
|
6919
|
+
status: 200,
|
|
6920
|
+
json: {
|
|
6921
|
+
key: json.data.key
|
|
6922
|
+
}
|
|
6923
|
+
};
|
|
6924
|
+
} catch (err) {
|
|
6925
|
+
return {
|
|
6926
|
+
status: 500,
|
|
6927
|
+
json: {
|
|
6928
|
+
message: err instanceof Error ? err.message : "AI images patch error"
|
|
6929
|
+
}
|
|
6930
|
+
};
|
|
6931
|
+
}
|
|
6932
|
+
};
|
|
6933
|
+
return execFetch();
|
|
6934
|
+
}
|
|
6935
|
+
},
|
|
6542
6936
|
//#region files
|
|
6543
6937
|
"/files": {
|
|
6544
6938
|
GET: async req => {
|