@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.
|
@@ -2374,8 +2374,8 @@ class ValOps {
|
|
|
2374
2374
|
}
|
|
2375
2375
|
|
|
2376
2376
|
// #region createPatch
|
|
2377
|
-
async createPatch(path, patch, patchId, parentRef, authorId) {
|
|
2378
|
-
const saveRes = await this.saveSourceFilePatch(path, patch, patchId, parentRef, authorId);
|
|
2377
|
+
async createPatch(path, patch, patchId, parentRef, sessionId, authorId) {
|
|
2378
|
+
const saveRes = await this.saveSourceFilePatch(path, patch, patchId, parentRef, authorId, sessionId);
|
|
2379
2379
|
if (result.isErr(saveRes)) {
|
|
2380
2380
|
console.error(`Could not save source patch at path: '${path}'. Error: ${saveRes.error.errorType === "other" ? saveRes.error.message : saveRes.error.errorType}`);
|
|
2381
2381
|
if (saveRes.error.errorType === "patch-head-conflict") {
|
|
@@ -2767,11 +2767,17 @@ class ValOpsFS extends ValOps {
|
|
|
2767
2767
|
parentPatchId: dir
|
|
2768
2768
|
});
|
|
2769
2769
|
} else {
|
|
2770
|
-
|
|
2770
|
+
const patchId = parsedPatch.data.patchId;
|
|
2771
|
+
if (includes && includes.length > 0 && !includes.includes(patchId)) {
|
|
2771
2772
|
return;
|
|
2772
2773
|
}
|
|
2773
|
-
patches[
|
|
2774
|
-
|
|
2774
|
+
patches[patchId] = {
|
|
2775
|
+
path: parsedPatch.data.path,
|
|
2776
|
+
patch: parsedPatch.data.patch,
|
|
2777
|
+
parentRef: parsedPatch.data.parentRef,
|
|
2778
|
+
baseSha: parsedPatch.data.baseSha,
|
|
2779
|
+
createdAt: parsedPatch.data.createdAt,
|
|
2780
|
+
authorId: parsedPatch.data.authorId,
|
|
2775
2781
|
appliedAt: null
|
|
2776
2782
|
};
|
|
2777
2783
|
}
|
|
@@ -2942,7 +2948,7 @@ class ValOpsFS extends ValOps {
|
|
|
2942
2948
|
};
|
|
2943
2949
|
}
|
|
2944
2950
|
}
|
|
2945
|
-
async saveSourceFilePatch(path, patch, patchId, parentRef, authorId) {
|
|
2951
|
+
async saveSourceFilePatch(path, patch, patchId, parentRef, authorId, sessionId) {
|
|
2946
2952
|
const patchDir = this.getParentPatchIdFromParentRef(parentRef);
|
|
2947
2953
|
try {
|
|
2948
2954
|
const baseSha = await this.getBaseSha();
|
|
@@ -2952,6 +2958,7 @@ class ValOpsFS extends ValOps {
|
|
|
2952
2958
|
parentRef,
|
|
2953
2959
|
path,
|
|
2954
2960
|
authorId,
|
|
2961
|
+
sessionId,
|
|
2955
2962
|
baseSha,
|
|
2956
2963
|
coreVersion: Internal.VERSION.core,
|
|
2957
2964
|
createdAt: new Date().toISOString()
|
|
@@ -3390,12 +3397,6 @@ class ValOpsFS extends ValOps {
|
|
|
3390
3397
|
return result.ok(Object.fromEntries(Object.entries(patches.patches).map(([patchId, value]) => [patchId, this.getParentPatchIdFromParentRef(value.parentRef)])));
|
|
3391
3398
|
}
|
|
3392
3399
|
|
|
3393
|
-
// #region profiles
|
|
3394
|
-
async getProfiles() {
|
|
3395
|
-
// We do not have profiles in FS mode
|
|
3396
|
-
return [];
|
|
3397
|
-
}
|
|
3398
|
-
|
|
3399
3400
|
// #region fs file path helpers
|
|
3400
3401
|
getPatchesDir() {
|
|
3401
3402
|
return path__default.join(this.rootDir, ValOpsFS.VAL_DIR, "patches");
|
|
@@ -3504,7 +3505,9 @@ const FSPatch = z.object({
|
|
|
3504
3505
|
parentRef: ParentRef,
|
|
3505
3506
|
authorId: z.string().refine(p => true).nullable(),
|
|
3506
3507
|
createdAt: z.string().datetime(),
|
|
3507
|
-
coreVersion: z.string().nullable()
|
|
3508
|
+
coreVersion: z.string().nullable(),
|
|
3509
|
+
// TODO: use this to check if patch is compatible with current core version?
|
|
3510
|
+
sessionId: z.string().nullable()
|
|
3508
3511
|
});
|
|
3509
3512
|
const FSPatchBase = z.object({
|
|
3510
3513
|
baseSha: z.string().refine(p => true),
|
|
@@ -3606,17 +3609,6 @@ const CommitResponse = z.object({
|
|
|
3606
3609
|
commit: CommitSha,
|
|
3607
3610
|
branch: z.string()
|
|
3608
3611
|
});
|
|
3609
|
-
const ProfilesResponse = z.object({
|
|
3610
|
-
profiles: z.array(z.object({
|
|
3611
|
-
profileId: z.string(),
|
|
3612
|
-
fullName: z.string(),
|
|
3613
|
-
email: z.string().optional(),
|
|
3614
|
-
// TODO: make this required once this can be guaranteed
|
|
3615
|
-
avatar: z.object({
|
|
3616
|
-
url: z.string()
|
|
3617
|
-
}).nullable()
|
|
3618
|
-
}))
|
|
3619
|
-
});
|
|
3620
3612
|
const NonceResponse = z.object({
|
|
3621
3613
|
nonce: z.string(),
|
|
3622
3614
|
url: z.string()
|
|
@@ -4067,7 +4059,7 @@ class ValOpsHttp extends ValOps {
|
|
|
4067
4059
|
};
|
|
4068
4060
|
}
|
|
4069
4061
|
}
|
|
4070
|
-
async saveSourceFilePatch(path, patch, patchId, parentRef, authorId) {
|
|
4062
|
+
async saveSourceFilePatch(path, patch, patchId, parentRef, authorId, sessionId) {
|
|
4071
4063
|
const baseSha = await this.getBaseSha();
|
|
4072
4064
|
return fetch(`${this.contentUrl}/v1/${this.project}/patches`, {
|
|
4073
4065
|
method: "POST",
|
|
@@ -4079,6 +4071,7 @@ class ValOpsHttp extends ValOps {
|
|
|
4079
4071
|
path,
|
|
4080
4072
|
patch,
|
|
4081
4073
|
authorId,
|
|
4074
|
+
sessionId,
|
|
4082
4075
|
patchId,
|
|
4083
4076
|
parentPatchId: parentRef.type === "patch" ? parentRef.patchId : null,
|
|
4084
4077
|
baseSha,
|
|
@@ -4502,31 +4495,6 @@ class ValOpsHttp extends ValOps {
|
|
|
4502
4495
|
};
|
|
4503
4496
|
}
|
|
4504
4497
|
}
|
|
4505
|
-
|
|
4506
|
-
// #region profiles
|
|
4507
|
-
async getProfiles() {
|
|
4508
|
-
var _res$headers$get6;
|
|
4509
|
-
const res = await fetch(`${this.contentUrl}/v1/${this.project}/profiles`, {
|
|
4510
|
-
headers: {
|
|
4511
|
-
...this.authHeaders,
|
|
4512
|
-
"Content-Type": "application/json"
|
|
4513
|
-
}
|
|
4514
|
-
});
|
|
4515
|
-
if (res.ok) {
|
|
4516
|
-
const parsed = ProfilesResponse.safeParse(await res.json());
|
|
4517
|
-
if (parsed.error) {
|
|
4518
|
-
console.error("Could not parse profiles response", parsed.error);
|
|
4519
|
-
throw Error(`Could not get profiles from remote server: wrong format. You might need to upgrade Val.`);
|
|
4520
|
-
}
|
|
4521
|
-
return parsed.data.profiles;
|
|
4522
|
-
}
|
|
4523
|
-
if ((_res$headers$get6 = res.headers.get("Content-Type")) !== null && _res$headers$get6 !== void 0 && _res$headers$get6.includes("application/json")) {
|
|
4524
|
-
const json = await res.json();
|
|
4525
|
-
const message = getErrorMessageFromUnknownJson(json, "Unknown error");
|
|
4526
|
-
throw Error(`Could not get profiles (status: ${res.status}): ${message}`);
|
|
4527
|
-
}
|
|
4528
|
-
throw Error(`Could not get profiles. Got status: ${res.status}`);
|
|
4529
|
-
}
|
|
4530
4498
|
}
|
|
4531
4499
|
|
|
4532
4500
|
const host = process.env.VAL_CONTENT_URL || DEFAULT_CONTENT_HOST;
|
|
@@ -4679,6 +4647,16 @@ function hasRemoteFileSchema(schema) {
|
|
|
4679
4647
|
|
|
4680
4648
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
4681
4649
|
const ValServer = (valModules, options, callbacks) => {
|
|
4650
|
+
const ProfilesResponse = z.object({
|
|
4651
|
+
profiles: z.array(z.object({
|
|
4652
|
+
profileId: z.string(),
|
|
4653
|
+
fullName: z.string(),
|
|
4654
|
+
email: z.string().optional(),
|
|
4655
|
+
avatar: z.object({
|
|
4656
|
+
url: z.string()
|
|
4657
|
+
}).nullable()
|
|
4658
|
+
}))
|
|
4659
|
+
});
|
|
4682
4660
|
let serverOps;
|
|
4683
4661
|
if (options.mode === "fs") {
|
|
4684
4662
|
serverOps = new ValOpsFS(options.valContentUrl, options.cwd, valModules, {
|
|
@@ -5528,10 +5506,11 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
5528
5506
|
}
|
|
5529
5507
|
const patches = req.body.patches;
|
|
5530
5508
|
let parentRef = req.body.parentRef;
|
|
5509
|
+
const sessionId = req.body.sessionId ?? null;
|
|
5531
5510
|
const authorId = "id" in auth ? auth.id : null;
|
|
5532
5511
|
const newPatchIds = [];
|
|
5533
5512
|
for (const patch of patches) {
|
|
5534
|
-
const createPatchRes = await serverOps.createPatch(patch.path, patch.patch, patch.patchId, parentRef, authorId);
|
|
5513
|
+
const createPatchRes = await serverOps.createPatch(patch.path, patch.patch, patch.patchId, parentRef, sessionId, authorId);
|
|
5535
5514
|
if (result.isErr(createPatchRes)) {
|
|
5536
5515
|
if (createPatchRes.error.errorType === "patch-head-conflict") {
|
|
5537
5516
|
return {
|
|
@@ -5892,13 +5871,87 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
5892
5871
|
}
|
|
5893
5872
|
};
|
|
5894
5873
|
}
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5874
|
+
if (!options.project) {
|
|
5875
|
+
return {
|
|
5876
|
+
status: 500,
|
|
5877
|
+
json: {
|
|
5878
|
+
message: "Project is not configured"
|
|
5879
|
+
}
|
|
5880
|
+
};
|
|
5881
|
+
}
|
|
5882
|
+
const authDataRes = await getRemoteFileAuth();
|
|
5883
|
+
if (authDataRes.status !== 200) {
|
|
5884
|
+
if (serverOps instanceof ValOpsFS && authDataRes.json.errorCode === "pat-error") {
|
|
5885
|
+
return {
|
|
5886
|
+
status: 200,
|
|
5887
|
+
json: {
|
|
5888
|
+
profiles: []
|
|
5889
|
+
}
|
|
5890
|
+
};
|
|
5891
|
+
}
|
|
5892
|
+
return {
|
|
5893
|
+
status: 500,
|
|
5894
|
+
json: {
|
|
5895
|
+
message: authDataRes.json.message
|
|
5896
|
+
}
|
|
5897
|
+
};
|
|
5898
|
+
}
|
|
5899
|
+
const authData = authDataRes.json.remoteFileAuth;
|
|
5900
|
+
const execFetch = async headers => {
|
|
5901
|
+
try {
|
|
5902
|
+
const upstreamUrl = `${options.valContentUrl}/v1/${options.project}/profiles`;
|
|
5903
|
+
const upstreamRes = await fetch(upstreamUrl, {
|
|
5904
|
+
method: "GET",
|
|
5905
|
+
headers
|
|
5906
|
+
});
|
|
5907
|
+
if (!upstreamRes.ok) {
|
|
5908
|
+
const text = await upstreamRes.text();
|
|
5909
|
+
const isAuthError = upstreamRes.status === 401 || upstreamRes.status === 403;
|
|
5910
|
+
return {
|
|
5911
|
+
status: isAuthError ? 401 : 500,
|
|
5912
|
+
json: {
|
|
5913
|
+
message: isAuthError ? `Profile authentication failed: ${upstreamRes.status} ${text}` : `Profiles failed: ${upstreamRes.status} ${text}`
|
|
5914
|
+
}
|
|
5915
|
+
};
|
|
5916
|
+
}
|
|
5917
|
+
const parseRes = ProfilesResponse.safeParse(await upstreamRes.json());
|
|
5918
|
+
if (!parseRes.success) {
|
|
5919
|
+
return {
|
|
5920
|
+
status: 500,
|
|
5921
|
+
json: {
|
|
5922
|
+
message: "Could not parse profiles response: " + fromError(parseRes.error).toString()
|
|
5923
|
+
}
|
|
5924
|
+
};
|
|
5925
|
+
}
|
|
5926
|
+
return {
|
|
5927
|
+
status: 200,
|
|
5928
|
+
json: {
|
|
5929
|
+
profiles: parseRes.data.profiles
|
|
5930
|
+
}
|
|
5931
|
+
};
|
|
5932
|
+
} catch (err) {
|
|
5933
|
+
return {
|
|
5934
|
+
status: 500,
|
|
5935
|
+
json: {
|
|
5936
|
+
message: err instanceof Error ? err.message : "Profiles request failed"
|
|
5937
|
+
}
|
|
5938
|
+
};
|
|
5900
5939
|
}
|
|
5901
5940
|
};
|
|
5941
|
+
if (serverOps instanceof ValOpsFS) {
|
|
5942
|
+
return execFetch(getProfileAuthHeaders(authData, null, "application/json"));
|
|
5943
|
+
}
|
|
5944
|
+
if (!options.valSecret) {
|
|
5945
|
+
return {
|
|
5946
|
+
status: 500,
|
|
5947
|
+
json: {
|
|
5948
|
+
message: "Secret is not configured"
|
|
5949
|
+
}
|
|
5950
|
+
};
|
|
5951
|
+
}
|
|
5952
|
+
return withAuth(options.valSecret, cookies, "profiles", data => {
|
|
5953
|
+
return execFetch(getProfileAuthHeaders(authData, data, "application/json"));
|
|
5954
|
+
});
|
|
5902
5955
|
}
|
|
5903
5956
|
},
|
|
5904
5957
|
"/commit-summary": {
|
|
@@ -6080,6 +6133,381 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
6080
6133
|
}
|
|
6081
6134
|
}
|
|
6082
6135
|
},
|
|
6136
|
+
//#region ai proxy
|
|
6137
|
+
"/ai/initialize": {
|
|
6138
|
+
POST: async req => {
|
|
6139
|
+
const cookies = req.cookies;
|
|
6140
|
+
const auth = getAuth(cookies);
|
|
6141
|
+
if (auth.error) {
|
|
6142
|
+
return {
|
|
6143
|
+
status: 401,
|
|
6144
|
+
json: {
|
|
6145
|
+
message: auth.error
|
|
6146
|
+
}
|
|
6147
|
+
};
|
|
6148
|
+
}
|
|
6149
|
+
if (!options.project) {
|
|
6150
|
+
return {
|
|
6151
|
+
status: 500,
|
|
6152
|
+
json: {
|
|
6153
|
+
message: "Project is not configured"
|
|
6154
|
+
}
|
|
6155
|
+
};
|
|
6156
|
+
}
|
|
6157
|
+
const authDataRes = await getRemoteFileAuth();
|
|
6158
|
+
if (authDataRes.status !== 200) {
|
|
6159
|
+
return {
|
|
6160
|
+
status: 500,
|
|
6161
|
+
json: {
|
|
6162
|
+
message: authDataRes.json.message
|
|
6163
|
+
}
|
|
6164
|
+
};
|
|
6165
|
+
}
|
|
6166
|
+
const authData = authDataRes.json.remoteFileAuth;
|
|
6167
|
+
const execFetch = async headers => {
|
|
6168
|
+
try {
|
|
6169
|
+
const upstreamUrl = `${options.valContentUrl}/v1/${options.project}/ai/initialize`;
|
|
6170
|
+
const upstreamRes = await fetch(upstreamUrl, {
|
|
6171
|
+
method: "POST",
|
|
6172
|
+
headers,
|
|
6173
|
+
body: JSON.stringify({})
|
|
6174
|
+
});
|
|
6175
|
+
if (!upstreamRes.ok) {
|
|
6176
|
+
const text = await upstreamRes.text();
|
|
6177
|
+
return {
|
|
6178
|
+
status: 500,
|
|
6179
|
+
json: {
|
|
6180
|
+
message: `AI initialize failed: ${upstreamRes.status} ${text}`
|
|
6181
|
+
}
|
|
6182
|
+
};
|
|
6183
|
+
}
|
|
6184
|
+
const json = await upstreamRes.json();
|
|
6185
|
+
const wsUrl = options.valContentUrl.replace(/^https:/, "wss:").replace(/^http:/, "ws:") + `/v1/${options.project}/ai/connect`;
|
|
6186
|
+
return {
|
|
6187
|
+
status: 200,
|
|
6188
|
+
json: {
|
|
6189
|
+
nonce: json.nonce,
|
|
6190
|
+
wsUrl
|
|
6191
|
+
}
|
|
6192
|
+
};
|
|
6193
|
+
} catch (err) {
|
|
6194
|
+
return {
|
|
6195
|
+
status: 500,
|
|
6196
|
+
json: {
|
|
6197
|
+
message: err instanceof Error ? err.message : "AI initialize error"
|
|
6198
|
+
}
|
|
6199
|
+
};
|
|
6200
|
+
}
|
|
6201
|
+
};
|
|
6202
|
+
if (serverOps instanceof ValOpsFS) {
|
|
6203
|
+
return execFetch(getProfileAuthHeaders(authData, null, "application/json"));
|
|
6204
|
+
}
|
|
6205
|
+
if (!options.valSecret) {
|
|
6206
|
+
return {
|
|
6207
|
+
status: 500,
|
|
6208
|
+
json: {
|
|
6209
|
+
message: "Secret is not configured"
|
|
6210
|
+
}
|
|
6211
|
+
};
|
|
6212
|
+
}
|
|
6213
|
+
return withAuth(options.valSecret, cookies, "ai/initialize", data => {
|
|
6214
|
+
return execFetch(getProfileAuthHeaders(authData, data, "application/json"));
|
|
6215
|
+
});
|
|
6216
|
+
}
|
|
6217
|
+
},
|
|
6218
|
+
"/ai/sessions": {
|
|
6219
|
+
GET: async req => {
|
|
6220
|
+
const cookies = req.cookies;
|
|
6221
|
+
const auth = getAuth(cookies);
|
|
6222
|
+
if (auth.error) {
|
|
6223
|
+
return {
|
|
6224
|
+
status: 401,
|
|
6225
|
+
json: {
|
|
6226
|
+
message: auth.error
|
|
6227
|
+
}
|
|
6228
|
+
};
|
|
6229
|
+
}
|
|
6230
|
+
if (!options.project) {
|
|
6231
|
+
return {
|
|
6232
|
+
status: 500,
|
|
6233
|
+
json: {
|
|
6234
|
+
message: "Project is not configured"
|
|
6235
|
+
}
|
|
6236
|
+
};
|
|
6237
|
+
}
|
|
6238
|
+
const authDataRes = await getRemoteFileAuth();
|
|
6239
|
+
if (authDataRes.status !== 200) {
|
|
6240
|
+
return {
|
|
6241
|
+
status: 500,
|
|
6242
|
+
json: {
|
|
6243
|
+
message: authDataRes.json.message
|
|
6244
|
+
}
|
|
6245
|
+
};
|
|
6246
|
+
}
|
|
6247
|
+
const authData = authDataRes.json.remoteFileAuth;
|
|
6248
|
+
const execFetch = async headers => {
|
|
6249
|
+
try {
|
|
6250
|
+
const SessionsResponse = z.object({
|
|
6251
|
+
sessions: z.array(z.object({
|
|
6252
|
+
id: z.string(),
|
|
6253
|
+
name: z.string().nullable(),
|
|
6254
|
+
createdAt: z.string(),
|
|
6255
|
+
updatedAt: z.string()
|
|
6256
|
+
})),
|
|
6257
|
+
nextCursor: z.object({
|
|
6258
|
+
updatedAt: z.string(),
|
|
6259
|
+
id: z.string()
|
|
6260
|
+
}).nullable().optional()
|
|
6261
|
+
});
|
|
6262
|
+
const params = new URLSearchParams();
|
|
6263
|
+
if (req.query.limit) params.set("limit", req.query.limit);
|
|
6264
|
+
if (req.query.cursor_updatedAt) {
|
|
6265
|
+
params.set("cursor_updatedAt", req.query.cursor_updatedAt);
|
|
6266
|
+
}
|
|
6267
|
+
if (req.query.cursor_id) {
|
|
6268
|
+
params.set("cursor_id", req.query.cursor_id);
|
|
6269
|
+
}
|
|
6270
|
+
const qs = params.toString();
|
|
6271
|
+
const upstreamUrl = `${options.valContentUrl}/v1/${options.project}/ai/sessions${qs ? `?${qs}` : ""}`;
|
|
6272
|
+
const upstreamRes = await fetch(upstreamUrl, {
|
|
6273
|
+
headers
|
|
6274
|
+
});
|
|
6275
|
+
if (!upstreamRes.ok) {
|
|
6276
|
+
const text = await upstreamRes.text();
|
|
6277
|
+
return {
|
|
6278
|
+
status: 500,
|
|
6279
|
+
json: {
|
|
6280
|
+
message: `AI sessions failed: ${upstreamRes.status} ${text}`
|
|
6281
|
+
}
|
|
6282
|
+
};
|
|
6283
|
+
}
|
|
6284
|
+
const json = SessionsResponse.safeParse(await upstreamRes.json());
|
|
6285
|
+
if (!json.success) {
|
|
6286
|
+
return {
|
|
6287
|
+
status: 500,
|
|
6288
|
+
json: {
|
|
6289
|
+
message: "Could not parse AI sessions response: " + fromError(json.error).toString()
|
|
6290
|
+
}
|
|
6291
|
+
};
|
|
6292
|
+
}
|
|
6293
|
+
return {
|
|
6294
|
+
status: 200,
|
|
6295
|
+
json: json.data
|
|
6296
|
+
};
|
|
6297
|
+
} catch (err) {
|
|
6298
|
+
return {
|
|
6299
|
+
status: 500,
|
|
6300
|
+
json: {
|
|
6301
|
+
message: err instanceof Error ? err.message : "AI sessions error"
|
|
6302
|
+
}
|
|
6303
|
+
};
|
|
6304
|
+
}
|
|
6305
|
+
};
|
|
6306
|
+
if (serverOps instanceof ValOpsFS) {
|
|
6307
|
+
return execFetch(getProfileAuthHeaders(authData, null, "application/json"));
|
|
6308
|
+
}
|
|
6309
|
+
if (!options.valSecret) {
|
|
6310
|
+
return {
|
|
6311
|
+
status: 500,
|
|
6312
|
+
json: {
|
|
6313
|
+
message: "Secret is not configured"
|
|
6314
|
+
}
|
|
6315
|
+
};
|
|
6316
|
+
}
|
|
6317
|
+
return withAuth(options.valSecret, cookies, "ai/sessions", data => {
|
|
6318
|
+
return execFetch(getProfileAuthHeaders(authData, data, "application/json"));
|
|
6319
|
+
});
|
|
6320
|
+
},
|
|
6321
|
+
PATCH: async req => {
|
|
6322
|
+
const cookies = req.cookies;
|
|
6323
|
+
const auth = getAuth(cookies);
|
|
6324
|
+
if (auth.error) {
|
|
6325
|
+
return {
|
|
6326
|
+
status: 401,
|
|
6327
|
+
json: {
|
|
6328
|
+
message: auth.error
|
|
6329
|
+
}
|
|
6330
|
+
};
|
|
6331
|
+
}
|
|
6332
|
+
if (!options.project) {
|
|
6333
|
+
return {
|
|
6334
|
+
status: 500,
|
|
6335
|
+
json: {
|
|
6336
|
+
message: "Project is not configured"
|
|
6337
|
+
}
|
|
6338
|
+
};
|
|
6339
|
+
}
|
|
6340
|
+
const pathParts = (req.path || "").split("/").filter(Boolean);
|
|
6341
|
+
const sessionId = pathParts[0];
|
|
6342
|
+
if (!sessionId) {
|
|
6343
|
+
return {
|
|
6344
|
+
status: 500,
|
|
6345
|
+
json: {
|
|
6346
|
+
message: "Missing sessionId in path"
|
|
6347
|
+
}
|
|
6348
|
+
};
|
|
6349
|
+
}
|
|
6350
|
+
const authDataRes = await getRemoteFileAuth();
|
|
6351
|
+
if (authDataRes.status !== 200) {
|
|
6352
|
+
return {
|
|
6353
|
+
status: 500,
|
|
6354
|
+
json: {
|
|
6355
|
+
message: authDataRes.json.message
|
|
6356
|
+
}
|
|
6357
|
+
};
|
|
6358
|
+
}
|
|
6359
|
+
const authData = authDataRes.json.remoteFileAuth;
|
|
6360
|
+
const execFetch = async headers => {
|
|
6361
|
+
try {
|
|
6362
|
+
const upstreamUrl = `${options.valContentUrl}/v1/${options.project}/ai/sessions/${encodeURIComponent(sessionId)}`;
|
|
6363
|
+
const upstreamRes = await fetch(upstreamUrl, {
|
|
6364
|
+
method: "PATCH",
|
|
6365
|
+
headers,
|
|
6366
|
+
body: JSON.stringify({
|
|
6367
|
+
name: req.body.name
|
|
6368
|
+
})
|
|
6369
|
+
});
|
|
6370
|
+
if (!upstreamRes.ok) {
|
|
6371
|
+
const text = await upstreamRes.text();
|
|
6372
|
+
return {
|
|
6373
|
+
status: 500,
|
|
6374
|
+
json: {
|
|
6375
|
+
message: `AI session rename failed: ${upstreamRes.status} ${text}`
|
|
6376
|
+
}
|
|
6377
|
+
};
|
|
6378
|
+
}
|
|
6379
|
+
return {
|
|
6380
|
+
status: 200,
|
|
6381
|
+
json: {}
|
|
6382
|
+
};
|
|
6383
|
+
} catch (err) {
|
|
6384
|
+
return {
|
|
6385
|
+
status: 500,
|
|
6386
|
+
json: {
|
|
6387
|
+
message: err instanceof Error ? err.message : "AI session rename error"
|
|
6388
|
+
}
|
|
6389
|
+
};
|
|
6390
|
+
}
|
|
6391
|
+
};
|
|
6392
|
+
if (serverOps instanceof ValOpsFS) {
|
|
6393
|
+
return execFetch(getProfileAuthHeaders(authData, null, "application/json"));
|
|
6394
|
+
}
|
|
6395
|
+
if (!options.valSecret) {
|
|
6396
|
+
return {
|
|
6397
|
+
status: 500,
|
|
6398
|
+
json: {
|
|
6399
|
+
message: "Secret is not configured"
|
|
6400
|
+
}
|
|
6401
|
+
};
|
|
6402
|
+
}
|
|
6403
|
+
return withAuth(options.valSecret, cookies, "ai/sessions/rename", data => {
|
|
6404
|
+
return execFetch(getProfileAuthHeaders(authData, data, "application/json"));
|
|
6405
|
+
});
|
|
6406
|
+
}
|
|
6407
|
+
},
|
|
6408
|
+
"/ai/messages": {
|
|
6409
|
+
GET: async req => {
|
|
6410
|
+
const cookies = req.cookies;
|
|
6411
|
+
const auth = getAuth(cookies);
|
|
6412
|
+
if (auth.error) {
|
|
6413
|
+
return {
|
|
6414
|
+
status: 401,
|
|
6415
|
+
json: {
|
|
6416
|
+
message: auth.error
|
|
6417
|
+
}
|
|
6418
|
+
};
|
|
6419
|
+
}
|
|
6420
|
+
if (!options.project) {
|
|
6421
|
+
return {
|
|
6422
|
+
status: 500,
|
|
6423
|
+
json: {
|
|
6424
|
+
message: "Project is not configured"
|
|
6425
|
+
}
|
|
6426
|
+
};
|
|
6427
|
+
}
|
|
6428
|
+
const pathParts = (req.path || "").split("/").filter(Boolean);
|
|
6429
|
+
const sessionId = pathParts[0];
|
|
6430
|
+
if (!sessionId) {
|
|
6431
|
+
return {
|
|
6432
|
+
status: 500,
|
|
6433
|
+
json: {
|
|
6434
|
+
message: "Missing sessionId in path"
|
|
6435
|
+
}
|
|
6436
|
+
};
|
|
6437
|
+
}
|
|
6438
|
+
const authDataRes = await getRemoteFileAuth();
|
|
6439
|
+
if (authDataRes.status !== 200) {
|
|
6440
|
+
return {
|
|
6441
|
+
status: 500,
|
|
6442
|
+
json: {
|
|
6443
|
+
message: authDataRes.json.message
|
|
6444
|
+
}
|
|
6445
|
+
};
|
|
6446
|
+
}
|
|
6447
|
+
const authData = authDataRes.json.remoteFileAuth;
|
|
6448
|
+
const execFetch = async headers => {
|
|
6449
|
+
try {
|
|
6450
|
+
const SessionMessagesResponse = z.object({
|
|
6451
|
+
messages: z.array(z.object({
|
|
6452
|
+
role: z.string(),
|
|
6453
|
+
content: z.string()
|
|
6454
|
+
})),
|
|
6455
|
+
nextCursor: z.object({
|
|
6456
|
+
updatedAt: z.string(),
|
|
6457
|
+
id: z.string()
|
|
6458
|
+
}).nullable().optional()
|
|
6459
|
+
});
|
|
6460
|
+
const upstreamUrl = `${options.valContentUrl}/v1/${options.project}/ai/sessions/${encodeURIComponent(sessionId)}/messages`;
|
|
6461
|
+
const upstreamRes = await fetch(upstreamUrl, {
|
|
6462
|
+
headers
|
|
6463
|
+
});
|
|
6464
|
+
if (!upstreamRes.ok) {
|
|
6465
|
+
const text = await upstreamRes.text();
|
|
6466
|
+
return {
|
|
6467
|
+
status: 500,
|
|
6468
|
+
json: {
|
|
6469
|
+
message: `AI session messages failed: ${upstreamRes.status} ${text}`
|
|
6470
|
+
}
|
|
6471
|
+
};
|
|
6472
|
+
}
|
|
6473
|
+
const json = SessionMessagesResponse.safeParse(await upstreamRes.json());
|
|
6474
|
+
if (!json.success) {
|
|
6475
|
+
return {
|
|
6476
|
+
status: 500,
|
|
6477
|
+
json: {
|
|
6478
|
+
message: "Could not parse AI session messages response: " + fromError(json.error).toString()
|
|
6479
|
+
}
|
|
6480
|
+
};
|
|
6481
|
+
}
|
|
6482
|
+
return {
|
|
6483
|
+
status: 200,
|
|
6484
|
+
json: json.data
|
|
6485
|
+
};
|
|
6486
|
+
} catch (err) {
|
|
6487
|
+
return {
|
|
6488
|
+
status: 500,
|
|
6489
|
+
json: {
|
|
6490
|
+
message: err instanceof Error ? err.message : "AI session messages error"
|
|
6491
|
+
}
|
|
6492
|
+
};
|
|
6493
|
+
}
|
|
6494
|
+
};
|
|
6495
|
+
if (serverOps instanceof ValOpsFS) {
|
|
6496
|
+
return execFetch(getProfileAuthHeaders(authData, null, "application/json"));
|
|
6497
|
+
}
|
|
6498
|
+
if (!options.valSecret) {
|
|
6499
|
+
return {
|
|
6500
|
+
status: 500,
|
|
6501
|
+
json: {
|
|
6502
|
+
message: "Secret is not configured"
|
|
6503
|
+
}
|
|
6504
|
+
};
|
|
6505
|
+
}
|
|
6506
|
+
return withAuth(options.valSecret, cookies, "ai/sessions/messages", data => {
|
|
6507
|
+
return execFetch(getProfileAuthHeaders(authData, data, "application/json"));
|
|
6508
|
+
});
|
|
6509
|
+
}
|
|
6510
|
+
},
|
|
6083
6511
|
//#region files
|
|
6084
6512
|
"/files": {
|
|
6085
6513
|
GET: async req => {
|
|
@@ -6301,6 +6729,21 @@ async function withAuth(secret, cookies, errorMessageType, handler) {
|
|
|
6301
6729
|
};
|
|
6302
6730
|
}
|
|
6303
6731
|
}
|
|
6732
|
+
function getProfileAuthHeaders(auth, data, type) {
|
|
6733
|
+
if ("pat" in auth) {
|
|
6734
|
+
return {
|
|
6735
|
+
"x-val-pat": auth.pat,
|
|
6736
|
+
"Content-Type": type
|
|
6737
|
+
};
|
|
6738
|
+
}
|
|
6739
|
+
if ("apiKey" in auth && data) {
|
|
6740
|
+
return {
|
|
6741
|
+
...getAuthHeaders(auth.apiKey, type),
|
|
6742
|
+
"x-val-profile-id": data.sub
|
|
6743
|
+
};
|
|
6744
|
+
}
|
|
6745
|
+
throw new Error("Invalid auth");
|
|
6746
|
+
}
|
|
6304
6747
|
function getAuthHeaders(token, type) {
|
|
6305
6748
|
if (!type) {
|
|
6306
6749
|
return {
|
package/package.json
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"./package.json": "./package.json"
|
|
17
17
|
},
|
|
18
18
|
"types": "dist/valbuild-server.cjs.d.ts",
|
|
19
|
-
"version": "0.
|
|
19
|
+
"version": "0.95.0",
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@prettier/sync": "^0.6.1",
|
|
22
22
|
"@types/jest": "^30.0.0"
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
"typescript": "^5.9.3",
|
|
31
31
|
"zod": "^4.3.5",
|
|
32
32
|
"zod-validation-error": "^5.0.0",
|
|
33
|
-
"@valbuild/core": "0.
|
|
34
|
-
"@valbuild/shared": "0.
|
|
35
|
-
"@valbuild/ui": "0.
|
|
33
|
+
"@valbuild/core": "0.95.0",
|
|
34
|
+
"@valbuild/shared": "0.95.0",
|
|
35
|
+
"@valbuild/ui": "0.95.0"
|
|
36
36
|
},
|
|
37
37
|
"engines": {
|
|
38
38
|
"node": ">=18.17.0"
|