@valbuild/server 0.94.0 → 0.95.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.
|
@@ -48,7 +48,7 @@ export declare const ENABLE_COOKIE_VALUE: {
|
|
|
48
48
|
readonly sameSite: "lax";
|
|
49
49
|
};
|
|
50
50
|
};
|
|
51
|
-
export declare function bufferToReadableStream(buffer: Buffer):
|
|
51
|
+
export declare function bufferToReadableStream(buffer: Buffer): ReadableStream<any>;
|
|
52
52
|
export declare function getRedirectUrl(query: {
|
|
53
53
|
redirect_to?: string | undefined;
|
|
54
54
|
}, overrideHost: string | undefined): string | {
|
|
@@ -2405,8 +2405,8 @@ class ValOps {
|
|
|
2405
2405
|
}
|
|
2406
2406
|
|
|
2407
2407
|
// #region createPatch
|
|
2408
|
-
async createPatch(path, patch, patchId, parentRef, authorId) {
|
|
2409
|
-
const saveRes = await this.saveSourceFilePatch(path, patch, patchId, parentRef, authorId);
|
|
2408
|
+
async createPatch(path, patch, patchId, parentRef, sessionId, authorId) {
|
|
2409
|
+
const saveRes = await this.saveSourceFilePatch(path, patch, patchId, parentRef, authorId, sessionId);
|
|
2410
2410
|
if (fp.result.isErr(saveRes)) {
|
|
2411
2411
|
console.error(`Could not save source patch at path: '${path}'. Error: ${saveRes.error.errorType === "other" ? saveRes.error.message : saveRes.error.errorType}`);
|
|
2412
2412
|
if (saveRes.error.errorType === "patch-head-conflict") {
|
|
@@ -2798,11 +2798,17 @@ class ValOpsFS extends ValOps {
|
|
|
2798
2798
|
parentPatchId: dir
|
|
2799
2799
|
});
|
|
2800
2800
|
} else {
|
|
2801
|
-
|
|
2801
|
+
const patchId = parsedPatch.data.patchId;
|
|
2802
|
+
if (includes && includes.length > 0 && !includes.includes(patchId)) {
|
|
2802
2803
|
return;
|
|
2803
2804
|
}
|
|
2804
|
-
patches[
|
|
2805
|
-
|
|
2805
|
+
patches[patchId] = {
|
|
2806
|
+
path: parsedPatch.data.path,
|
|
2807
|
+
patch: parsedPatch.data.patch,
|
|
2808
|
+
parentRef: parsedPatch.data.parentRef,
|
|
2809
|
+
baseSha: parsedPatch.data.baseSha,
|
|
2810
|
+
createdAt: parsedPatch.data.createdAt,
|
|
2811
|
+
authorId: parsedPatch.data.authorId,
|
|
2806
2812
|
appliedAt: null
|
|
2807
2813
|
};
|
|
2808
2814
|
}
|
|
@@ -2973,7 +2979,7 @@ class ValOpsFS extends ValOps {
|
|
|
2973
2979
|
};
|
|
2974
2980
|
}
|
|
2975
2981
|
}
|
|
2976
|
-
async saveSourceFilePatch(path, patch, patchId, parentRef, authorId) {
|
|
2982
|
+
async saveSourceFilePatch(path, patch, patchId, parentRef, authorId, sessionId) {
|
|
2977
2983
|
const patchDir = this.getParentPatchIdFromParentRef(parentRef);
|
|
2978
2984
|
try {
|
|
2979
2985
|
const baseSha = await this.getBaseSha();
|
|
@@ -2983,6 +2989,7 @@ class ValOpsFS extends ValOps {
|
|
|
2983
2989
|
parentRef,
|
|
2984
2990
|
path,
|
|
2985
2991
|
authorId,
|
|
2992
|
+
sessionId,
|
|
2986
2993
|
baseSha,
|
|
2987
2994
|
coreVersion: core.Internal.VERSION.core,
|
|
2988
2995
|
createdAt: new Date().toISOString()
|
|
@@ -3421,12 +3428,6 @@ class ValOpsFS extends ValOps {
|
|
|
3421
3428
|
return fp.result.ok(Object.fromEntries(Object.entries(patches.patches).map(([patchId, value]) => [patchId, this.getParentPatchIdFromParentRef(value.parentRef)])));
|
|
3422
3429
|
}
|
|
3423
3430
|
|
|
3424
|
-
// #region profiles
|
|
3425
|
-
async getProfiles() {
|
|
3426
|
-
// We do not have profiles in FS mode
|
|
3427
|
-
return [];
|
|
3428
|
-
}
|
|
3429
|
-
|
|
3430
3431
|
// #region fs file path helpers
|
|
3431
3432
|
getPatchesDir() {
|
|
3432
3433
|
return path__namespace["default"].join(this.rootDir, ValOpsFS.VAL_DIR, "patches");
|
|
@@ -3535,7 +3536,9 @@ const FSPatch = zod.z.object({
|
|
|
3535
3536
|
parentRef: internal.ParentRef,
|
|
3536
3537
|
authorId: zod.z.string().refine(p => true).nullable(),
|
|
3537
3538
|
createdAt: zod.z.string().datetime(),
|
|
3538
|
-
coreVersion: zod.z.string().nullable()
|
|
3539
|
+
coreVersion: zod.z.string().nullable(),
|
|
3540
|
+
// TODO: use this to check if patch is compatible with current core version?
|
|
3541
|
+
sessionId: zod.z.string().nullable()
|
|
3539
3542
|
});
|
|
3540
3543
|
const FSPatchBase = zod.z.object({
|
|
3541
3544
|
baseSha: zod.z.string().refine(p => true),
|
|
@@ -3637,17 +3640,6 @@ const CommitResponse = zod.z.object({
|
|
|
3637
3640
|
commit: CommitSha,
|
|
3638
3641
|
branch: zod.z.string()
|
|
3639
3642
|
});
|
|
3640
|
-
const ProfilesResponse = zod.z.object({
|
|
3641
|
-
profiles: zod.z.array(zod.z.object({
|
|
3642
|
-
profileId: zod.z.string(),
|
|
3643
|
-
fullName: zod.z.string(),
|
|
3644
|
-
email: zod.z.string().optional(),
|
|
3645
|
-
// TODO: make this required once this can be guaranteed
|
|
3646
|
-
avatar: zod.z.object({
|
|
3647
|
-
url: zod.z.string()
|
|
3648
|
-
}).nullable()
|
|
3649
|
-
}))
|
|
3650
|
-
});
|
|
3651
3643
|
const NonceResponse = zod.z.object({
|
|
3652
3644
|
nonce: zod.z.string(),
|
|
3653
3645
|
url: zod.z.string()
|
|
@@ -4098,7 +4090,7 @@ class ValOpsHttp extends ValOps {
|
|
|
4098
4090
|
};
|
|
4099
4091
|
}
|
|
4100
4092
|
}
|
|
4101
|
-
async saveSourceFilePatch(path, patch, patchId, parentRef, authorId) {
|
|
4093
|
+
async saveSourceFilePatch(path, patch, patchId, parentRef, authorId, sessionId) {
|
|
4102
4094
|
const baseSha = await this.getBaseSha();
|
|
4103
4095
|
return fetch(`${this.contentUrl}/v1/${this.project}/patches`, {
|
|
4104
4096
|
method: "POST",
|
|
@@ -4110,6 +4102,7 @@ class ValOpsHttp extends ValOps {
|
|
|
4110
4102
|
path,
|
|
4111
4103
|
patch,
|
|
4112
4104
|
authorId,
|
|
4105
|
+
sessionId,
|
|
4113
4106
|
patchId,
|
|
4114
4107
|
parentPatchId: parentRef.type === "patch" ? parentRef.patchId : null,
|
|
4115
4108
|
baseSha,
|
|
@@ -4533,31 +4526,6 @@ class ValOpsHttp extends ValOps {
|
|
|
4533
4526
|
};
|
|
4534
4527
|
}
|
|
4535
4528
|
}
|
|
4536
|
-
|
|
4537
|
-
// #region profiles
|
|
4538
|
-
async getProfiles() {
|
|
4539
|
-
var _res$headers$get6;
|
|
4540
|
-
const res = await fetch(`${this.contentUrl}/v1/${this.project}/profiles`, {
|
|
4541
|
-
headers: {
|
|
4542
|
-
...this.authHeaders,
|
|
4543
|
-
"Content-Type": "application/json"
|
|
4544
|
-
}
|
|
4545
|
-
});
|
|
4546
|
-
if (res.ok) {
|
|
4547
|
-
const parsed = ProfilesResponse.safeParse(await res.json());
|
|
4548
|
-
if (parsed.error) {
|
|
4549
|
-
console.error("Could not parse profiles response", parsed.error);
|
|
4550
|
-
throw Error(`Could not get profiles from remote server: wrong format. You might need to upgrade Val.`);
|
|
4551
|
-
}
|
|
4552
|
-
return parsed.data.profiles;
|
|
4553
|
-
}
|
|
4554
|
-
if ((_res$headers$get6 = res.headers.get("Content-Type")) !== null && _res$headers$get6 !== void 0 && _res$headers$get6.includes("application/json")) {
|
|
4555
|
-
const json = await res.json();
|
|
4556
|
-
const message = internal.getErrorMessageFromUnknownJson(json, "Unknown error");
|
|
4557
|
-
throw Error(`Could not get profiles (status: ${res.status}): ${message}`);
|
|
4558
|
-
}
|
|
4559
|
-
throw Error(`Could not get profiles. Got status: ${res.status}`);
|
|
4560
|
-
}
|
|
4561
4529
|
}
|
|
4562
4530
|
|
|
4563
4531
|
const host = process.env.VAL_CONTENT_URL || core.DEFAULT_CONTENT_HOST;
|
|
@@ -4710,6 +4678,16 @@ function hasRemoteFileSchema(schema) {
|
|
|
4710
4678
|
|
|
4711
4679
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
4712
4680
|
const ValServer = (valModules, options, callbacks) => {
|
|
4681
|
+
const ProfilesResponse = zod.z.object({
|
|
4682
|
+
profiles: zod.z.array(zod.z.object({
|
|
4683
|
+
profileId: zod.z.string(),
|
|
4684
|
+
fullName: zod.z.string(),
|
|
4685
|
+
email: zod.z.string().optional(),
|
|
4686
|
+
avatar: zod.z.object({
|
|
4687
|
+
url: zod.z.string()
|
|
4688
|
+
}).nullable()
|
|
4689
|
+
}))
|
|
4690
|
+
});
|
|
4713
4691
|
let serverOps;
|
|
4714
4692
|
if (options.mode === "fs") {
|
|
4715
4693
|
serverOps = new ValOpsFS(options.valContentUrl, options.cwd, valModules, {
|
|
@@ -5559,10 +5537,11 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
5559
5537
|
}
|
|
5560
5538
|
const patches = req.body.patches;
|
|
5561
5539
|
let parentRef = req.body.parentRef;
|
|
5540
|
+
const sessionId = req.body.sessionId ?? null;
|
|
5562
5541
|
const authorId = "id" in auth ? auth.id : null;
|
|
5563
5542
|
const newPatchIds = [];
|
|
5564
5543
|
for (const patch of patches) {
|
|
5565
|
-
const createPatchRes = await serverOps.createPatch(patch.path, patch.patch, patch.patchId, parentRef, authorId);
|
|
5544
|
+
const createPatchRes = await serverOps.createPatch(patch.path, patch.patch, patch.patchId, parentRef, sessionId, authorId);
|
|
5566
5545
|
if (fp.result.isErr(createPatchRes)) {
|
|
5567
5546
|
if (createPatchRes.error.errorType === "patch-head-conflict") {
|
|
5568
5547
|
return {
|
|
@@ -5923,13 +5902,87 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
5923
5902
|
}
|
|
5924
5903
|
};
|
|
5925
5904
|
}
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5905
|
+
if (!options.project) {
|
|
5906
|
+
return {
|
|
5907
|
+
status: 500,
|
|
5908
|
+
json: {
|
|
5909
|
+
message: "Project is not configured"
|
|
5910
|
+
}
|
|
5911
|
+
};
|
|
5912
|
+
}
|
|
5913
|
+
const authDataRes = await getRemoteFileAuth();
|
|
5914
|
+
if (authDataRes.status !== 200) {
|
|
5915
|
+
if (serverOps instanceof ValOpsFS && authDataRes.json.errorCode === "pat-error") {
|
|
5916
|
+
return {
|
|
5917
|
+
status: 200,
|
|
5918
|
+
json: {
|
|
5919
|
+
profiles: []
|
|
5920
|
+
}
|
|
5921
|
+
};
|
|
5922
|
+
}
|
|
5923
|
+
return {
|
|
5924
|
+
status: 500,
|
|
5925
|
+
json: {
|
|
5926
|
+
message: authDataRes.json.message
|
|
5927
|
+
}
|
|
5928
|
+
};
|
|
5929
|
+
}
|
|
5930
|
+
const authData = authDataRes.json.remoteFileAuth;
|
|
5931
|
+
const execFetch = async headers => {
|
|
5932
|
+
try {
|
|
5933
|
+
const upstreamUrl = `${options.valContentUrl}/v1/${options.project}/profiles`;
|
|
5934
|
+
const upstreamRes = await fetch(upstreamUrl, {
|
|
5935
|
+
method: "GET",
|
|
5936
|
+
headers
|
|
5937
|
+
});
|
|
5938
|
+
if (!upstreamRes.ok) {
|
|
5939
|
+
const text = await upstreamRes.text();
|
|
5940
|
+
const isAuthError = upstreamRes.status === 401 || upstreamRes.status === 403;
|
|
5941
|
+
return {
|
|
5942
|
+
status: isAuthError ? 401 : 500,
|
|
5943
|
+
json: {
|
|
5944
|
+
message: isAuthError ? `Profile authentication failed: ${upstreamRes.status} ${text}` : `Profiles failed: ${upstreamRes.status} ${text}`
|
|
5945
|
+
}
|
|
5946
|
+
};
|
|
5947
|
+
}
|
|
5948
|
+
const parseRes = ProfilesResponse.safeParse(await upstreamRes.json());
|
|
5949
|
+
if (!parseRes.success) {
|
|
5950
|
+
return {
|
|
5951
|
+
status: 500,
|
|
5952
|
+
json: {
|
|
5953
|
+
message: "Could not parse profiles response: " + zodValidationError.fromError(parseRes.error).toString()
|
|
5954
|
+
}
|
|
5955
|
+
};
|
|
5956
|
+
}
|
|
5957
|
+
return {
|
|
5958
|
+
status: 200,
|
|
5959
|
+
json: {
|
|
5960
|
+
profiles: parseRes.data.profiles
|
|
5961
|
+
}
|
|
5962
|
+
};
|
|
5963
|
+
} catch (err) {
|
|
5964
|
+
return {
|
|
5965
|
+
status: 500,
|
|
5966
|
+
json: {
|
|
5967
|
+
message: err instanceof Error ? err.message : "Profiles request failed"
|
|
5968
|
+
}
|
|
5969
|
+
};
|
|
5931
5970
|
}
|
|
5932
5971
|
};
|
|
5972
|
+
if (serverOps instanceof ValOpsFS) {
|
|
5973
|
+
return execFetch(getProfileAuthHeaders(authData, null, "application/json"));
|
|
5974
|
+
}
|
|
5975
|
+
if (!options.valSecret) {
|
|
5976
|
+
return {
|
|
5977
|
+
status: 500,
|
|
5978
|
+
json: {
|
|
5979
|
+
message: "Secret is not configured"
|
|
5980
|
+
}
|
|
5981
|
+
};
|
|
5982
|
+
}
|
|
5983
|
+
return withAuth(options.valSecret, cookies, "profiles", data => {
|
|
5984
|
+
return execFetch(getProfileAuthHeaders(authData, data, "application/json"));
|
|
5985
|
+
});
|
|
5933
5986
|
}
|
|
5934
5987
|
},
|
|
5935
5988
|
"/commit-summary": {
|
|
@@ -6111,6 +6164,381 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
6111
6164
|
}
|
|
6112
6165
|
}
|
|
6113
6166
|
},
|
|
6167
|
+
//#region ai proxy
|
|
6168
|
+
"/ai/initialize": {
|
|
6169
|
+
POST: async req => {
|
|
6170
|
+
const cookies = req.cookies;
|
|
6171
|
+
const auth = getAuth(cookies);
|
|
6172
|
+
if (auth.error) {
|
|
6173
|
+
return {
|
|
6174
|
+
status: 401,
|
|
6175
|
+
json: {
|
|
6176
|
+
message: auth.error
|
|
6177
|
+
}
|
|
6178
|
+
};
|
|
6179
|
+
}
|
|
6180
|
+
if (!options.project) {
|
|
6181
|
+
return {
|
|
6182
|
+
status: 500,
|
|
6183
|
+
json: {
|
|
6184
|
+
message: "Project is not configured"
|
|
6185
|
+
}
|
|
6186
|
+
};
|
|
6187
|
+
}
|
|
6188
|
+
const authDataRes = await getRemoteFileAuth();
|
|
6189
|
+
if (authDataRes.status !== 200) {
|
|
6190
|
+
return {
|
|
6191
|
+
status: 500,
|
|
6192
|
+
json: {
|
|
6193
|
+
message: authDataRes.json.message
|
|
6194
|
+
}
|
|
6195
|
+
};
|
|
6196
|
+
}
|
|
6197
|
+
const authData = authDataRes.json.remoteFileAuth;
|
|
6198
|
+
const execFetch = async headers => {
|
|
6199
|
+
try {
|
|
6200
|
+
const upstreamUrl = `${options.valContentUrl}/v1/${options.project}/ai/initialize`;
|
|
6201
|
+
const upstreamRes = await fetch(upstreamUrl, {
|
|
6202
|
+
method: "POST",
|
|
6203
|
+
headers,
|
|
6204
|
+
body: JSON.stringify({})
|
|
6205
|
+
});
|
|
6206
|
+
if (!upstreamRes.ok) {
|
|
6207
|
+
const text = await upstreamRes.text();
|
|
6208
|
+
return {
|
|
6209
|
+
status: 500,
|
|
6210
|
+
json: {
|
|
6211
|
+
message: `AI initialize failed: ${upstreamRes.status} ${text}`
|
|
6212
|
+
}
|
|
6213
|
+
};
|
|
6214
|
+
}
|
|
6215
|
+
const json = await upstreamRes.json();
|
|
6216
|
+
const wsUrl = options.valContentUrl.replace(/^https:/, "wss:").replace(/^http:/, "ws:") + `/v1/${options.project}/ai/connect`;
|
|
6217
|
+
return {
|
|
6218
|
+
status: 200,
|
|
6219
|
+
json: {
|
|
6220
|
+
nonce: json.nonce,
|
|
6221
|
+
wsUrl
|
|
6222
|
+
}
|
|
6223
|
+
};
|
|
6224
|
+
} catch (err) {
|
|
6225
|
+
return {
|
|
6226
|
+
status: 500,
|
|
6227
|
+
json: {
|
|
6228
|
+
message: err instanceof Error ? err.message : "AI initialize error"
|
|
6229
|
+
}
|
|
6230
|
+
};
|
|
6231
|
+
}
|
|
6232
|
+
};
|
|
6233
|
+
if (serverOps instanceof ValOpsFS) {
|
|
6234
|
+
return execFetch(getProfileAuthHeaders(authData, null, "application/json"));
|
|
6235
|
+
}
|
|
6236
|
+
if (!options.valSecret) {
|
|
6237
|
+
return {
|
|
6238
|
+
status: 500,
|
|
6239
|
+
json: {
|
|
6240
|
+
message: "Secret is not configured"
|
|
6241
|
+
}
|
|
6242
|
+
};
|
|
6243
|
+
}
|
|
6244
|
+
return withAuth(options.valSecret, cookies, "ai/initialize", data => {
|
|
6245
|
+
return execFetch(getProfileAuthHeaders(authData, data, "application/json"));
|
|
6246
|
+
});
|
|
6247
|
+
}
|
|
6248
|
+
},
|
|
6249
|
+
"/ai/sessions": {
|
|
6250
|
+
GET: async req => {
|
|
6251
|
+
const cookies = req.cookies;
|
|
6252
|
+
const auth = getAuth(cookies);
|
|
6253
|
+
if (auth.error) {
|
|
6254
|
+
return {
|
|
6255
|
+
status: 401,
|
|
6256
|
+
json: {
|
|
6257
|
+
message: auth.error
|
|
6258
|
+
}
|
|
6259
|
+
};
|
|
6260
|
+
}
|
|
6261
|
+
if (!options.project) {
|
|
6262
|
+
return {
|
|
6263
|
+
status: 500,
|
|
6264
|
+
json: {
|
|
6265
|
+
message: "Project is not configured"
|
|
6266
|
+
}
|
|
6267
|
+
};
|
|
6268
|
+
}
|
|
6269
|
+
const authDataRes = await getRemoteFileAuth();
|
|
6270
|
+
if (authDataRes.status !== 200) {
|
|
6271
|
+
return {
|
|
6272
|
+
status: 500,
|
|
6273
|
+
json: {
|
|
6274
|
+
message: authDataRes.json.message
|
|
6275
|
+
}
|
|
6276
|
+
};
|
|
6277
|
+
}
|
|
6278
|
+
const authData = authDataRes.json.remoteFileAuth;
|
|
6279
|
+
const execFetch = async headers => {
|
|
6280
|
+
try {
|
|
6281
|
+
const SessionsResponse = zod.z.object({
|
|
6282
|
+
sessions: zod.z.array(zod.z.object({
|
|
6283
|
+
id: zod.z.string(),
|
|
6284
|
+
name: zod.z.string().nullable(),
|
|
6285
|
+
createdAt: zod.z.string(),
|
|
6286
|
+
updatedAt: zod.z.string()
|
|
6287
|
+
})),
|
|
6288
|
+
nextCursor: zod.z.object({
|
|
6289
|
+
updatedAt: zod.z.string(),
|
|
6290
|
+
id: zod.z.string()
|
|
6291
|
+
}).nullable().optional()
|
|
6292
|
+
});
|
|
6293
|
+
const params = new URLSearchParams();
|
|
6294
|
+
if (req.query.limit) params.set("limit", req.query.limit);
|
|
6295
|
+
if (req.query.cursor_updatedAt) {
|
|
6296
|
+
params.set("cursor_updatedAt", req.query.cursor_updatedAt);
|
|
6297
|
+
}
|
|
6298
|
+
if (req.query.cursor_id) {
|
|
6299
|
+
params.set("cursor_id", req.query.cursor_id);
|
|
6300
|
+
}
|
|
6301
|
+
const qs = params.toString();
|
|
6302
|
+
const upstreamUrl = `${options.valContentUrl}/v1/${options.project}/ai/sessions${qs ? `?${qs}` : ""}`;
|
|
6303
|
+
const upstreamRes = await fetch(upstreamUrl, {
|
|
6304
|
+
headers
|
|
6305
|
+
});
|
|
6306
|
+
if (!upstreamRes.ok) {
|
|
6307
|
+
const text = await upstreamRes.text();
|
|
6308
|
+
return {
|
|
6309
|
+
status: 500,
|
|
6310
|
+
json: {
|
|
6311
|
+
message: `AI sessions failed: ${upstreamRes.status} ${text}`
|
|
6312
|
+
}
|
|
6313
|
+
};
|
|
6314
|
+
}
|
|
6315
|
+
const json = SessionsResponse.safeParse(await upstreamRes.json());
|
|
6316
|
+
if (!json.success) {
|
|
6317
|
+
return {
|
|
6318
|
+
status: 500,
|
|
6319
|
+
json: {
|
|
6320
|
+
message: "Could not parse AI sessions response: " + zodValidationError.fromError(json.error).toString()
|
|
6321
|
+
}
|
|
6322
|
+
};
|
|
6323
|
+
}
|
|
6324
|
+
return {
|
|
6325
|
+
status: 200,
|
|
6326
|
+
json: json.data
|
|
6327
|
+
};
|
|
6328
|
+
} catch (err) {
|
|
6329
|
+
return {
|
|
6330
|
+
status: 500,
|
|
6331
|
+
json: {
|
|
6332
|
+
message: err instanceof Error ? err.message : "AI sessions error"
|
|
6333
|
+
}
|
|
6334
|
+
};
|
|
6335
|
+
}
|
|
6336
|
+
};
|
|
6337
|
+
if (serverOps instanceof ValOpsFS) {
|
|
6338
|
+
return execFetch(getProfileAuthHeaders(authData, null, "application/json"));
|
|
6339
|
+
}
|
|
6340
|
+
if (!options.valSecret) {
|
|
6341
|
+
return {
|
|
6342
|
+
status: 500,
|
|
6343
|
+
json: {
|
|
6344
|
+
message: "Secret is not configured"
|
|
6345
|
+
}
|
|
6346
|
+
};
|
|
6347
|
+
}
|
|
6348
|
+
return withAuth(options.valSecret, cookies, "ai/sessions", data => {
|
|
6349
|
+
return execFetch(getProfileAuthHeaders(authData, data, "application/json"));
|
|
6350
|
+
});
|
|
6351
|
+
},
|
|
6352
|
+
PATCH: async req => {
|
|
6353
|
+
const cookies = req.cookies;
|
|
6354
|
+
const auth = getAuth(cookies);
|
|
6355
|
+
if (auth.error) {
|
|
6356
|
+
return {
|
|
6357
|
+
status: 401,
|
|
6358
|
+
json: {
|
|
6359
|
+
message: auth.error
|
|
6360
|
+
}
|
|
6361
|
+
};
|
|
6362
|
+
}
|
|
6363
|
+
if (!options.project) {
|
|
6364
|
+
return {
|
|
6365
|
+
status: 500,
|
|
6366
|
+
json: {
|
|
6367
|
+
message: "Project is not configured"
|
|
6368
|
+
}
|
|
6369
|
+
};
|
|
6370
|
+
}
|
|
6371
|
+
const pathParts = (req.path || "").split("/").filter(Boolean);
|
|
6372
|
+
const sessionId = pathParts[0];
|
|
6373
|
+
if (!sessionId) {
|
|
6374
|
+
return {
|
|
6375
|
+
status: 500,
|
|
6376
|
+
json: {
|
|
6377
|
+
message: "Missing sessionId in path"
|
|
6378
|
+
}
|
|
6379
|
+
};
|
|
6380
|
+
}
|
|
6381
|
+
const authDataRes = await getRemoteFileAuth();
|
|
6382
|
+
if (authDataRes.status !== 200) {
|
|
6383
|
+
return {
|
|
6384
|
+
status: 500,
|
|
6385
|
+
json: {
|
|
6386
|
+
message: authDataRes.json.message
|
|
6387
|
+
}
|
|
6388
|
+
};
|
|
6389
|
+
}
|
|
6390
|
+
const authData = authDataRes.json.remoteFileAuth;
|
|
6391
|
+
const execFetch = async headers => {
|
|
6392
|
+
try {
|
|
6393
|
+
const upstreamUrl = `${options.valContentUrl}/v1/${options.project}/ai/sessions/${encodeURIComponent(sessionId)}`;
|
|
6394
|
+
const upstreamRes = await fetch(upstreamUrl, {
|
|
6395
|
+
method: "PATCH",
|
|
6396
|
+
headers,
|
|
6397
|
+
body: JSON.stringify({
|
|
6398
|
+
name: req.body.name
|
|
6399
|
+
})
|
|
6400
|
+
});
|
|
6401
|
+
if (!upstreamRes.ok) {
|
|
6402
|
+
const text = await upstreamRes.text();
|
|
6403
|
+
return {
|
|
6404
|
+
status: 500,
|
|
6405
|
+
json: {
|
|
6406
|
+
message: `AI session rename failed: ${upstreamRes.status} ${text}`
|
|
6407
|
+
}
|
|
6408
|
+
};
|
|
6409
|
+
}
|
|
6410
|
+
return {
|
|
6411
|
+
status: 200,
|
|
6412
|
+
json: {}
|
|
6413
|
+
};
|
|
6414
|
+
} catch (err) {
|
|
6415
|
+
return {
|
|
6416
|
+
status: 500,
|
|
6417
|
+
json: {
|
|
6418
|
+
message: err instanceof Error ? err.message : "AI session rename error"
|
|
6419
|
+
}
|
|
6420
|
+
};
|
|
6421
|
+
}
|
|
6422
|
+
};
|
|
6423
|
+
if (serverOps instanceof ValOpsFS) {
|
|
6424
|
+
return execFetch(getProfileAuthHeaders(authData, null, "application/json"));
|
|
6425
|
+
}
|
|
6426
|
+
if (!options.valSecret) {
|
|
6427
|
+
return {
|
|
6428
|
+
status: 500,
|
|
6429
|
+
json: {
|
|
6430
|
+
message: "Secret is not configured"
|
|
6431
|
+
}
|
|
6432
|
+
};
|
|
6433
|
+
}
|
|
6434
|
+
return withAuth(options.valSecret, cookies, "ai/sessions/rename", data => {
|
|
6435
|
+
return execFetch(getProfileAuthHeaders(authData, data, "application/json"));
|
|
6436
|
+
});
|
|
6437
|
+
}
|
|
6438
|
+
},
|
|
6439
|
+
"/ai/messages": {
|
|
6440
|
+
GET: async req => {
|
|
6441
|
+
const cookies = req.cookies;
|
|
6442
|
+
const auth = getAuth(cookies);
|
|
6443
|
+
if (auth.error) {
|
|
6444
|
+
return {
|
|
6445
|
+
status: 401,
|
|
6446
|
+
json: {
|
|
6447
|
+
message: auth.error
|
|
6448
|
+
}
|
|
6449
|
+
};
|
|
6450
|
+
}
|
|
6451
|
+
if (!options.project) {
|
|
6452
|
+
return {
|
|
6453
|
+
status: 500,
|
|
6454
|
+
json: {
|
|
6455
|
+
message: "Project is not configured"
|
|
6456
|
+
}
|
|
6457
|
+
};
|
|
6458
|
+
}
|
|
6459
|
+
const pathParts = (req.path || "").split("/").filter(Boolean);
|
|
6460
|
+
const sessionId = pathParts[0];
|
|
6461
|
+
if (!sessionId) {
|
|
6462
|
+
return {
|
|
6463
|
+
status: 500,
|
|
6464
|
+
json: {
|
|
6465
|
+
message: "Missing sessionId in path"
|
|
6466
|
+
}
|
|
6467
|
+
};
|
|
6468
|
+
}
|
|
6469
|
+
const authDataRes = await getRemoteFileAuth();
|
|
6470
|
+
if (authDataRes.status !== 200) {
|
|
6471
|
+
return {
|
|
6472
|
+
status: 500,
|
|
6473
|
+
json: {
|
|
6474
|
+
message: authDataRes.json.message
|
|
6475
|
+
}
|
|
6476
|
+
};
|
|
6477
|
+
}
|
|
6478
|
+
const authData = authDataRes.json.remoteFileAuth;
|
|
6479
|
+
const execFetch = async headers => {
|
|
6480
|
+
try {
|
|
6481
|
+
const SessionMessagesResponse = zod.z.object({
|
|
6482
|
+
messages: zod.z.array(zod.z.object({
|
|
6483
|
+
role: zod.z.string(),
|
|
6484
|
+
content: zod.z.string()
|
|
6485
|
+
})),
|
|
6486
|
+
nextCursor: zod.z.object({
|
|
6487
|
+
updatedAt: zod.z.string(),
|
|
6488
|
+
id: zod.z.string()
|
|
6489
|
+
}).nullable().optional()
|
|
6490
|
+
});
|
|
6491
|
+
const upstreamUrl = `${options.valContentUrl}/v1/${options.project}/ai/sessions/${encodeURIComponent(sessionId)}/messages`;
|
|
6492
|
+
const upstreamRes = await fetch(upstreamUrl, {
|
|
6493
|
+
headers
|
|
6494
|
+
});
|
|
6495
|
+
if (!upstreamRes.ok) {
|
|
6496
|
+
const text = await upstreamRes.text();
|
|
6497
|
+
return {
|
|
6498
|
+
status: 500,
|
|
6499
|
+
json: {
|
|
6500
|
+
message: `AI session messages failed: ${upstreamRes.status} ${text}`
|
|
6501
|
+
}
|
|
6502
|
+
};
|
|
6503
|
+
}
|
|
6504
|
+
const json = SessionMessagesResponse.safeParse(await upstreamRes.json());
|
|
6505
|
+
if (!json.success) {
|
|
6506
|
+
return {
|
|
6507
|
+
status: 500,
|
|
6508
|
+
json: {
|
|
6509
|
+
message: "Could not parse AI session messages response: " + zodValidationError.fromError(json.error).toString()
|
|
6510
|
+
}
|
|
6511
|
+
};
|
|
6512
|
+
}
|
|
6513
|
+
return {
|
|
6514
|
+
status: 200,
|
|
6515
|
+
json: json.data
|
|
6516
|
+
};
|
|
6517
|
+
} catch (err) {
|
|
6518
|
+
return {
|
|
6519
|
+
status: 500,
|
|
6520
|
+
json: {
|
|
6521
|
+
message: err instanceof Error ? err.message : "AI session messages error"
|
|
6522
|
+
}
|
|
6523
|
+
};
|
|
6524
|
+
}
|
|
6525
|
+
};
|
|
6526
|
+
if (serverOps instanceof ValOpsFS) {
|
|
6527
|
+
return execFetch(getProfileAuthHeaders(authData, null, "application/json"));
|
|
6528
|
+
}
|
|
6529
|
+
if (!options.valSecret) {
|
|
6530
|
+
return {
|
|
6531
|
+
status: 500,
|
|
6532
|
+
json: {
|
|
6533
|
+
message: "Secret is not configured"
|
|
6534
|
+
}
|
|
6535
|
+
};
|
|
6536
|
+
}
|
|
6537
|
+
return withAuth(options.valSecret, cookies, "ai/sessions/messages", data => {
|
|
6538
|
+
return execFetch(getProfileAuthHeaders(authData, data, "application/json"));
|
|
6539
|
+
});
|
|
6540
|
+
}
|
|
6541
|
+
},
|
|
6114
6542
|
//#region files
|
|
6115
6543
|
"/files": {
|
|
6116
6544
|
GET: async req => {
|
|
@@ -6332,6 +6760,21 @@ async function withAuth(secret, cookies, errorMessageType, handler) {
|
|
|
6332
6760
|
};
|
|
6333
6761
|
}
|
|
6334
6762
|
}
|
|
6763
|
+
function getProfileAuthHeaders(auth, data, type) {
|
|
6764
|
+
if ("pat" in auth) {
|
|
6765
|
+
return {
|
|
6766
|
+
"x-val-pat": auth.pat,
|
|
6767
|
+
"Content-Type": type
|
|
6768
|
+
};
|
|
6769
|
+
}
|
|
6770
|
+
if ("apiKey" in auth && data) {
|
|
6771
|
+
return {
|
|
6772
|
+
...getAuthHeaders(auth.apiKey, type),
|
|
6773
|
+
"x-val-profile-id": data.sub
|
|
6774
|
+
};
|
|
6775
|
+
}
|
|
6776
|
+
throw new Error("Invalid auth");
|
|
6777
|
+
}
|
|
6335
6778
|
function getAuthHeaders(token, type) {
|
|
6336
6779
|
if (!type) {
|
|
6337
6780
|
return {
|