@spencer-kit/coder-studio 0.4.4 → 0.4.6
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/CHANGELOG.md +14 -0
- package/dist/esm/bin.mjs +1091 -438
- package/dist/esm/bin.mjs.map +4 -4
- package/dist/esm/server-runner.mjs +1087 -434
- package/dist/esm/server-runner.mjs.map +4 -4
- package/dist/web/assets/components-BGEBLvHB.css +1 -0
- package/dist/web/assets/components-BuRmXlh8.js +109 -0
- package/dist/web/assets/components-BuRmXlh8.js.map +1 -0
- package/dist/web/assets/main-DZPwC4NB.js +2 -0
- package/dist/web/assets/main-DZPwC4NB.js.map +1 -0
- package/dist/web/assets/ui-preview-BzE9924q.js +17 -0
- package/dist/web/assets/ui-preview-BzE9924q.js.map +1 -0
- package/dist/web/index.html +3 -3
- package/dist/web/ui-preview.html +3 -3
- package/package.json +3 -3
- package/dist/web/assets/components-C4SKshs2.js +0 -110
- package/dist/web/assets/components-C4SKshs2.js.map +0 -1
- package/dist/web/assets/components-CMahvybm.css +0 -1
- package/dist/web/assets/main-CZuF2VZA.js +0 -2
- package/dist/web/assets/main-CZuF2VZA.js.map +0 -1
- package/dist/web/assets/ui-preview-DCeC0YmD.js +0 -17
- package/dist/web/assets/ui-preview-DCeC0YmD.js.map +0 -1
|
@@ -1097,6 +1097,281 @@ var init_session_store = __esm({
|
|
|
1097
1097
|
}
|
|
1098
1098
|
});
|
|
1099
1099
|
|
|
1100
|
+
// packages/server/src/uploads/paths.ts
|
|
1101
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
1102
|
+
import { lstat, mkdir } from "node:fs/promises";
|
|
1103
|
+
import path4 from "node:path";
|
|
1104
|
+
function sanitizeOriginalName(input) {
|
|
1105
|
+
let sanitized = "";
|
|
1106
|
+
for (const char of input.trim()) {
|
|
1107
|
+
sanitized += KEEP_FILENAME_CHAR.test(char) ? char : "_";
|
|
1108
|
+
}
|
|
1109
|
+
sanitized = sanitized.replace(/^\.+/, "");
|
|
1110
|
+
if (sanitized.length === 0 || /^[_\s]*$/.test(sanitized)) {
|
|
1111
|
+
return "file";
|
|
1112
|
+
}
|
|
1113
|
+
if (sanitized.length <= MAX_FILENAME_LENGTH) {
|
|
1114
|
+
return sanitized;
|
|
1115
|
+
}
|
|
1116
|
+
const lastDot = sanitized.lastIndexOf(".");
|
|
1117
|
+
if (lastDot > 0 && sanitized.length - lastDot <= 16) {
|
|
1118
|
+
const ext = sanitized.slice(lastDot);
|
|
1119
|
+
const stem = sanitized.slice(0, MAX_FILENAME_LENGTH - ext.length);
|
|
1120
|
+
return stem + ext;
|
|
1121
|
+
}
|
|
1122
|
+
return sanitized.slice(0, MAX_FILENAME_LENGTH);
|
|
1123
|
+
}
|
|
1124
|
+
function validateWorkspaceId(id) {
|
|
1125
|
+
if (!WORKSPACE_ID_RE.test(id)) {
|
|
1126
|
+
throw new Error(`invalid workspace id: ${JSON.stringify(id)}`);
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
async function assertDirectorySegmentSafe(segmentPath) {
|
|
1130
|
+
const info = await lstat(segmentPath);
|
|
1131
|
+
if (info.isSymbolicLink()) {
|
|
1132
|
+
throw new Error(`symlinked upload path segment is not allowed: ${segmentPath}`);
|
|
1133
|
+
}
|
|
1134
|
+
if (!info.isDirectory()) {
|
|
1135
|
+
throw new Error(`upload path segment is not a directory: ${segmentPath}`);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
async function ensureSafeUploadDir(rootDir, targetDir2) {
|
|
1139
|
+
const resolvedRoot = path4.resolve(rootDir);
|
|
1140
|
+
const resolvedTarget = path4.resolve(targetDir2);
|
|
1141
|
+
if (resolvedTarget !== resolvedRoot && !resolvedTarget.startsWith(`${resolvedRoot}${path4.sep}`)) {
|
|
1142
|
+
throw new Error(`target dir escaped uploads root: ${resolvedTarget}`);
|
|
1143
|
+
}
|
|
1144
|
+
try {
|
|
1145
|
+
await assertDirectorySegmentSafe(resolvedRoot);
|
|
1146
|
+
} catch (error) {
|
|
1147
|
+
const code = error.code;
|
|
1148
|
+
if (code !== "ENOENT") {
|
|
1149
|
+
throw error;
|
|
1150
|
+
}
|
|
1151
|
+
await mkdir(resolvedRoot, { recursive: true });
|
|
1152
|
+
await assertDirectorySegmentSafe(resolvedRoot);
|
|
1153
|
+
}
|
|
1154
|
+
const relative5 = path4.relative(resolvedRoot, resolvedTarget);
|
|
1155
|
+
if (!relative5) {
|
|
1156
|
+
return;
|
|
1157
|
+
}
|
|
1158
|
+
let current = resolvedRoot;
|
|
1159
|
+
for (const segment of relative5.split(path4.sep)) {
|
|
1160
|
+
current = path4.join(current, segment);
|
|
1161
|
+
try {
|
|
1162
|
+
await assertDirectorySegmentSafe(current);
|
|
1163
|
+
continue;
|
|
1164
|
+
} catch (error) {
|
|
1165
|
+
const code = error.code;
|
|
1166
|
+
if (code !== "ENOENT") {
|
|
1167
|
+
throw error;
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
try {
|
|
1171
|
+
await mkdir(current);
|
|
1172
|
+
} catch (error) {
|
|
1173
|
+
const code = error.code;
|
|
1174
|
+
if (code !== "EEXIST") {
|
|
1175
|
+
throw error;
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
await assertDirectorySegmentSafe(current);
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
function generateBucketPath(input) {
|
|
1182
|
+
validateWorkspaceId(input.workspaceId);
|
|
1183
|
+
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
1184
|
+
const dateStr = now.toISOString().slice(0, 10);
|
|
1185
|
+
const dir = path4.join(input.uploadsDir, input.workspaceId, dateStr);
|
|
1186
|
+
const sanitizedName = sanitizeOriginalName(input.originalName);
|
|
1187
|
+
const uuid8 = randomUUID2().replace(/-/g, "").slice(0, 8);
|
|
1188
|
+
const absolutePath = path4.resolve(dir, `${uuid8}-${sanitizedName}`);
|
|
1189
|
+
const uploadsRoot = `${path4.resolve(input.uploadsDir)}${path4.sep}`;
|
|
1190
|
+
if (!absolutePath.startsWith(uploadsRoot)) {
|
|
1191
|
+
throw new Error(`generated upload path escaped uploads root: ${absolutePath}`);
|
|
1192
|
+
}
|
|
1193
|
+
return {
|
|
1194
|
+
dir,
|
|
1195
|
+
absolutePath,
|
|
1196
|
+
uuid8,
|
|
1197
|
+
sanitizedName
|
|
1198
|
+
};
|
|
1199
|
+
}
|
|
1200
|
+
var MAX_FILENAME_LENGTH, KEEP_FILENAME_CHAR, WORKSPACE_ID_RE;
|
|
1201
|
+
var init_paths = __esm({
|
|
1202
|
+
"packages/server/src/uploads/paths.ts"() {
|
|
1203
|
+
"use strict";
|
|
1204
|
+
MAX_FILENAME_LENGTH = 64;
|
|
1205
|
+
KEEP_FILENAME_CHAR = /[a-zA-Z0-9._一-鿿 \-]/;
|
|
1206
|
+
WORKSPACE_ID_RE = /^[a-zA-Z0-9_-]+$/;
|
|
1207
|
+
}
|
|
1208
|
+
});
|
|
1209
|
+
|
|
1210
|
+
// packages/server/src/routes/appearance-assets.ts
|
|
1211
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
1212
|
+
import { createReadStream, createWriteStream } from "node:fs";
|
|
1213
|
+
import { rm, stat } from "node:fs/promises";
|
|
1214
|
+
import { isAbsolute, join as join2, relative, resolve as resolve2, sep } from "node:path";
|
|
1215
|
+
import { pipeline } from "node:stream/promises";
|
|
1216
|
+
function isAllowedAppearanceMime(mime2) {
|
|
1217
|
+
return ALLOWED_APPEARANCE_MIME_TYPES.has(mime2);
|
|
1218
|
+
}
|
|
1219
|
+
function isPathInsideRoot(rootPath, targetPath) {
|
|
1220
|
+
const rel = relative(rootPath, targetPath);
|
|
1221
|
+
return rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
|
|
1222
|
+
}
|
|
1223
|
+
function resolveAssetStoragePath(uploadsDir, storagePath) {
|
|
1224
|
+
const resolvedUploadsDir = resolve2(uploadsDir);
|
|
1225
|
+
const resolvedStoragePath = resolve2(storagePath);
|
|
1226
|
+
return isPathInsideRoot(resolvedUploadsDir, resolvedStoragePath) ? resolvedStoragePath : null;
|
|
1227
|
+
}
|
|
1228
|
+
async function cleanupWrittenFile(filePath) {
|
|
1229
|
+
if (!filePath) {
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
1232
|
+
await rm(filePath, { force: true });
|
|
1233
|
+
}
|
|
1234
|
+
async function rejectAndCleanup(reply, filePath, statusCode, error) {
|
|
1235
|
+
await cleanupWrittenFile(filePath);
|
|
1236
|
+
return reply.status(statusCode).send({ ok: false, error });
|
|
1237
|
+
}
|
|
1238
|
+
function registerAppearanceAssetsRoutes(app, deps) {
|
|
1239
|
+
app.post("/api/appearance-assets", async (request, reply) => {
|
|
1240
|
+
if (!request.isMultipart()) {
|
|
1241
|
+
return reply.status(400).send({ ok: false, error: "expected_multipart" });
|
|
1242
|
+
}
|
|
1243
|
+
let writtenPath;
|
|
1244
|
+
let pendingRecord;
|
|
1245
|
+
try {
|
|
1246
|
+
const parts = request.parts();
|
|
1247
|
+
for await (const part of parts) {
|
|
1248
|
+
if (part.type !== "file") {
|
|
1249
|
+
continue;
|
|
1250
|
+
}
|
|
1251
|
+
if (part.fieldname !== "file") {
|
|
1252
|
+
part.file.resume();
|
|
1253
|
+
return rejectAndCleanup(reply, writtenPath, 400, "file_required");
|
|
1254
|
+
}
|
|
1255
|
+
if (pendingRecord) {
|
|
1256
|
+
part.file.resume();
|
|
1257
|
+
return rejectAndCleanup(reply, writtenPath, 400, "too_many_files");
|
|
1258
|
+
}
|
|
1259
|
+
if (!isAllowedAppearanceMime(part.mimetype)) {
|
|
1260
|
+
part.file.resume();
|
|
1261
|
+
return rejectAndCleanup(reply, writtenPath, 400, "invalid_file_type");
|
|
1262
|
+
}
|
|
1263
|
+
const assetId = randomUUID3();
|
|
1264
|
+
const createdAt = Date.now();
|
|
1265
|
+
const dateStr = new Date(createdAt).toISOString().slice(0, 10);
|
|
1266
|
+
const safeName = sanitizeOriginalName(part.filename || "file");
|
|
1267
|
+
const fileName = part.filename?.trim() ? part.filename.trim() : safeName;
|
|
1268
|
+
const dir = join2(deps.uploadsDir, APPEARANCE_ASSET_BUCKET, dateStr);
|
|
1269
|
+
const storagePath = join2(dir, `${assetId}-${safeName}`);
|
|
1270
|
+
try {
|
|
1271
|
+
await ensureSafeUploadDir(deps.uploadsDir, dir);
|
|
1272
|
+
await pipeline(part.file, createWriteStream(storagePath));
|
|
1273
|
+
} catch (error) {
|
|
1274
|
+
request.log.warn({ err: error }, "appearance asset write failed");
|
|
1275
|
+
return rejectAndCleanup(reply, storagePath, 500, "write_failed");
|
|
1276
|
+
}
|
|
1277
|
+
if (part.file.truncated) {
|
|
1278
|
+
return rejectAndCleanup(reply, storagePath, 413, "file_too_large");
|
|
1279
|
+
}
|
|
1280
|
+
let fileSize;
|
|
1281
|
+
try {
|
|
1282
|
+
const fileStat = await stat(storagePath);
|
|
1283
|
+
fileSize = fileStat.size;
|
|
1284
|
+
} catch (error) {
|
|
1285
|
+
request.log.warn({ err: error }, "appearance asset stat failed");
|
|
1286
|
+
return rejectAndCleanup(reply, storagePath, 500, "write_failed");
|
|
1287
|
+
}
|
|
1288
|
+
writtenPath = storagePath;
|
|
1289
|
+
pendingRecord = {
|
|
1290
|
+
id: assetId,
|
|
1291
|
+
fileName,
|
|
1292
|
+
mime: part.mimetype,
|
|
1293
|
+
size: fileSize,
|
|
1294
|
+
storagePath,
|
|
1295
|
+
createdAt
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1298
|
+
} catch (error) {
|
|
1299
|
+
if (error.code === "FST_REQ_FILE_TOO_LARGE") {
|
|
1300
|
+
return rejectAndCleanup(reply, writtenPath, 413, "file_too_large");
|
|
1301
|
+
}
|
|
1302
|
+
request.log.warn({ err: error }, "appearance asset parse failed");
|
|
1303
|
+
return rejectAndCleanup(reply, writtenPath, 400, "parse_failed");
|
|
1304
|
+
}
|
|
1305
|
+
if (!pendingRecord) {
|
|
1306
|
+
return rejectAndCleanup(reply, writtenPath, 400, "file_required");
|
|
1307
|
+
}
|
|
1308
|
+
try {
|
|
1309
|
+
deps.repo.set(pendingRecord);
|
|
1310
|
+
} catch (error) {
|
|
1311
|
+
request.log.warn({ err: error }, "appearance asset metadata write failed");
|
|
1312
|
+
return rejectAndCleanup(reply, writtenPath, 500, "write_failed");
|
|
1313
|
+
}
|
|
1314
|
+
return reply.send({
|
|
1315
|
+
ok: true,
|
|
1316
|
+
asset: {
|
|
1317
|
+
assetId: pendingRecord.id,
|
|
1318
|
+
url: `/api/appearance-assets/${pendingRecord.id}`,
|
|
1319
|
+
mime: pendingRecord.mime,
|
|
1320
|
+
size: pendingRecord.size
|
|
1321
|
+
}
|
|
1322
|
+
});
|
|
1323
|
+
});
|
|
1324
|
+
app.get(
|
|
1325
|
+
"/api/appearance-assets/:assetId",
|
|
1326
|
+
async (request, reply) => {
|
|
1327
|
+
const record = deps.repo.get(request.params.assetId);
|
|
1328
|
+
if (!record) {
|
|
1329
|
+
return reply.status(404).send({ ok: false, error: "not_found" });
|
|
1330
|
+
}
|
|
1331
|
+
const storagePath = resolveAssetStoragePath(deps.uploadsDir, record.storagePath);
|
|
1332
|
+
if (!storagePath) {
|
|
1333
|
+
return reply.status(404).send({ ok: false, error: "not_found" });
|
|
1334
|
+
}
|
|
1335
|
+
let fileSize;
|
|
1336
|
+
try {
|
|
1337
|
+
const fileStat = await stat(storagePath);
|
|
1338
|
+
if (!fileStat.isFile()) {
|
|
1339
|
+
return reply.status(404).send({ ok: false, error: "not_found" });
|
|
1340
|
+
}
|
|
1341
|
+
fileSize = fileStat.size;
|
|
1342
|
+
} catch {
|
|
1343
|
+
return reply.status(404).send({ ok: false, error: "not_found" });
|
|
1344
|
+
}
|
|
1345
|
+
reply.header("Content-Type", record.mime).header("Content-Length", String(fileSize)).header("Cache-Control", "no-store").header("X-Content-Type-Options", "nosniff");
|
|
1346
|
+
return reply.send(createReadStream(storagePath));
|
|
1347
|
+
}
|
|
1348
|
+
);
|
|
1349
|
+
app.delete(
|
|
1350
|
+
"/api/appearance-assets/:assetId",
|
|
1351
|
+
async (request, reply) => {
|
|
1352
|
+
const record = deps.repo.get(request.params.assetId);
|
|
1353
|
+
if (!record) {
|
|
1354
|
+
return reply.status(404).send({ ok: false, error: "not_found" });
|
|
1355
|
+
}
|
|
1356
|
+
const storagePath = resolveAssetStoragePath(deps.uploadsDir, record.storagePath);
|
|
1357
|
+
if (storagePath) {
|
|
1358
|
+
await rm(storagePath, { force: true });
|
|
1359
|
+
}
|
|
1360
|
+
deps.repo.delete(request.params.assetId);
|
|
1361
|
+
return reply.send({ ok: true });
|
|
1362
|
+
}
|
|
1363
|
+
);
|
|
1364
|
+
}
|
|
1365
|
+
var ALLOWED_APPEARANCE_MIME_TYPES, APPEARANCE_ASSET_BUCKET;
|
|
1366
|
+
var init_appearance_assets = __esm({
|
|
1367
|
+
"packages/server/src/routes/appearance-assets.ts"() {
|
|
1368
|
+
"use strict";
|
|
1369
|
+
init_paths();
|
|
1370
|
+
ALLOWED_APPEARANCE_MIME_TYPES = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/webp"]);
|
|
1371
|
+
APPEARANCE_ASSET_BUCKET = "appearance/default";
|
|
1372
|
+
}
|
|
1373
|
+
});
|
|
1374
|
+
|
|
1100
1375
|
// packages/server/src/fs/image.ts
|
|
1101
1376
|
import { extname } from "path";
|
|
1102
1377
|
function getImageTypeInfo(filePath) {
|
|
@@ -1121,8 +1396,8 @@ var init_image = __esm({
|
|
|
1121
1396
|
});
|
|
1122
1397
|
|
|
1123
1398
|
// packages/server/src/fs/path-safety.ts
|
|
1124
|
-
import
|
|
1125
|
-
function
|
|
1399
|
+
import path5 from "node:path";
|
|
1400
|
+
function isPathInsideRoot2(rootPath, targetPath, pathApi = path5) {
|
|
1126
1401
|
const rel = pathApi.relative(rootPath, targetPath);
|
|
1127
1402
|
return rel !== ".." && !rel.startsWith(`..${pathApi.sep}`) && !pathApi.isAbsolute(rel);
|
|
1128
1403
|
}
|
|
@@ -1133,19 +1408,19 @@ var init_path_safety = __esm({
|
|
|
1133
1408
|
});
|
|
1134
1409
|
|
|
1135
1410
|
// packages/server/src/fs/file-io.ts
|
|
1136
|
-
import * as
|
|
1411
|
+
import * as path6 from "node:path";
|
|
1137
1412
|
import { createHash } from "crypto";
|
|
1138
1413
|
import {
|
|
1139
1414
|
readFile as fsReadFile,
|
|
1140
1415
|
rename as fsRename,
|
|
1141
1416
|
writeFile as fsWriteFile,
|
|
1142
|
-
mkdir,
|
|
1143
|
-
rm,
|
|
1144
|
-
stat
|
|
1417
|
+
mkdir as mkdir2,
|
|
1418
|
+
rm as rm2,
|
|
1419
|
+
stat as stat2
|
|
1145
1420
|
} from "fs/promises";
|
|
1146
1421
|
async function statSafe(path14) {
|
|
1147
1422
|
try {
|
|
1148
|
-
return await
|
|
1423
|
+
return await stat2(path14);
|
|
1149
1424
|
} catch {
|
|
1150
1425
|
return null;
|
|
1151
1426
|
}
|
|
@@ -1156,7 +1431,7 @@ async function createFile(rootPath, relPath) {
|
|
|
1156
1431
|
if (existing) {
|
|
1157
1432
|
throw { code: "already_exists", message: "File already exists" };
|
|
1158
1433
|
}
|
|
1159
|
-
await
|
|
1434
|
+
await mkdir2(path6.dirname(abs), { recursive: true });
|
|
1160
1435
|
await fsWriteFile(abs, "", "utf-8");
|
|
1161
1436
|
}
|
|
1162
1437
|
async function createDirectory(rootPath, relPath) {
|
|
@@ -1165,7 +1440,7 @@ async function createDirectory(rootPath, relPath) {
|
|
|
1165
1440
|
if (existing) {
|
|
1166
1441
|
throw { code: "already_exists", message: "Directory already exists" };
|
|
1167
1442
|
}
|
|
1168
|
-
await
|
|
1443
|
+
await mkdir2(abs, { recursive: true });
|
|
1169
1444
|
}
|
|
1170
1445
|
async function deleteEntry(rootPath, relPath) {
|
|
1171
1446
|
const abs = resolveSafe(rootPath, relPath);
|
|
@@ -1173,15 +1448,15 @@ async function deleteEntry(rootPath, relPath) {
|
|
|
1173
1448
|
if (!existing) {
|
|
1174
1449
|
throw { code: "not_found", message: "Target not found" };
|
|
1175
1450
|
}
|
|
1176
|
-
await
|
|
1451
|
+
await rm2(abs, { recursive: true });
|
|
1177
1452
|
}
|
|
1178
1453
|
async function renameEntry(rootPath, fromPath, toPath) {
|
|
1179
1454
|
const fromAbs = resolveSafe(rootPath, fromPath);
|
|
1180
1455
|
const toAbs = resolveSafe(rootPath, toPath);
|
|
1181
1456
|
const source = await statSafe(fromAbs);
|
|
1182
1457
|
const target = await statSafe(toAbs);
|
|
1183
|
-
const fromParent =
|
|
1184
|
-
const toParent =
|
|
1458
|
+
const fromParent = path6.dirname(fromAbs);
|
|
1459
|
+
const toParent = path6.dirname(toAbs);
|
|
1185
1460
|
if (!source) {
|
|
1186
1461
|
throw { code: "not_found", message: "Source not found" };
|
|
1187
1462
|
}
|
|
@@ -1196,10 +1471,10 @@ async function renameEntry(rootPath, fromPath, toPath) {
|
|
|
1196
1471
|
}
|
|
1197
1472
|
await fsRename(fromAbs, toAbs);
|
|
1198
1473
|
}
|
|
1199
|
-
function resolveSafe(root, relPath, pathApi =
|
|
1474
|
+
function resolveSafe(root, relPath, pathApi = path6) {
|
|
1200
1475
|
const absRoot = pathApi.resolve(root);
|
|
1201
1476
|
const abs = pathApi.resolve(absRoot, relPath);
|
|
1202
|
-
if (!
|
|
1477
|
+
if (!isPathInsideRoot2(absRoot, abs, pathApi)) {
|
|
1203
1478
|
throw { code: "path_escape", message: "Path escapes workspace root" };
|
|
1204
1479
|
}
|
|
1205
1480
|
return abs;
|
|
@@ -1247,7 +1522,7 @@ async function writeFile(rootPath, relPath, content, baseHash) {
|
|
|
1247
1522
|
};
|
|
1248
1523
|
}
|
|
1249
1524
|
}
|
|
1250
|
-
await
|
|
1525
|
+
await mkdir2(path6.dirname(abs), { recursive: true });
|
|
1251
1526
|
await fsWriteFile(abs, content, "utf-8");
|
|
1252
1527
|
const newHash = createHash("sha256").update(content).digest("hex");
|
|
1253
1528
|
return { newHash };
|
|
@@ -1411,11 +1686,11 @@ var init_status_parser = __esm({
|
|
|
1411
1686
|
|
|
1412
1687
|
// packages/server/src/git/cli.ts
|
|
1413
1688
|
import { execFile } from "child_process";
|
|
1414
|
-
import { mkdir as
|
|
1689
|
+
import { mkdir as mkdir3, mkdtemp, rm as rm3, writeFile as writeFile2 } from "fs/promises";
|
|
1415
1690
|
import os2 from "os";
|
|
1416
|
-
import
|
|
1691
|
+
import path7 from "path";
|
|
1417
1692
|
async function runGit(cwd, args, options = {}) {
|
|
1418
|
-
return new Promise((
|
|
1693
|
+
return new Promise((resolve6, reject) => {
|
|
1419
1694
|
const gitArgs = [
|
|
1420
1695
|
...options.config?.flatMap(([key, value]) => ["-c", `${key}=${value}`]) ?? [],
|
|
1421
1696
|
...args
|
|
@@ -1442,7 +1717,7 @@ async function runGit(cwd, args, options = {}) {
|
|
|
1442
1717
|
if (err) {
|
|
1443
1718
|
reject(new GitError(err.message, stderr));
|
|
1444
1719
|
} else {
|
|
1445
|
-
|
|
1720
|
+
resolve6({ stdout, stderr });
|
|
1446
1721
|
}
|
|
1447
1722
|
}
|
|
1448
1723
|
);
|
|
@@ -1901,10 +2176,10 @@ async function prepareGitAuthExecution(auth, remoteMetadata) {
|
|
|
1901
2176
|
}
|
|
1902
2177
|
};
|
|
1903
2178
|
}
|
|
1904
|
-
const tempDir = await mkdtemp(
|
|
1905
|
-
const hooksDir =
|
|
1906
|
-
const askPassPath =
|
|
1907
|
-
await
|
|
2179
|
+
const tempDir = await mkdtemp(path7.join(os2.tmpdir(), "coder-studio-git-auth-"));
|
|
2180
|
+
const hooksDir = path7.join(tempDir, "hooks");
|
|
2181
|
+
const askPassPath = path7.join(tempDir, "askpass.sh");
|
|
2182
|
+
await mkdir3(hooksDir, { recursive: true, mode: 448 });
|
|
1908
2183
|
await writeFile2(
|
|
1909
2184
|
askPassPath,
|
|
1910
2185
|
[
|
|
@@ -1933,7 +2208,7 @@ async function prepareGitAuthExecution(auth, remoteMetadata) {
|
|
|
1933
2208
|
["credential.username", auth.username]
|
|
1934
2209
|
],
|
|
1935
2210
|
cleanup: async () => {
|
|
1936
|
-
await
|
|
2211
|
+
await rm3(tempDir, { recursive: true, force: true });
|
|
1937
2212
|
}
|
|
1938
2213
|
};
|
|
1939
2214
|
}
|
|
@@ -2181,8 +2456,8 @@ var init_image_revision = __esm({
|
|
|
2181
2456
|
});
|
|
2182
2457
|
|
|
2183
2458
|
// packages/server/src/routes/file-asset.ts
|
|
2184
|
-
import { createReadStream } from "fs";
|
|
2185
|
-
import { realpath, stat as
|
|
2459
|
+
import { createReadStream as createReadStream2 } from "fs";
|
|
2460
|
+
import { realpath, stat as stat3 } from "fs/promises";
|
|
2186
2461
|
function registerFileAssetRoutes(app, deps) {
|
|
2187
2462
|
app.get(
|
|
2188
2463
|
"/api/file",
|
|
@@ -2226,7 +2501,7 @@ function registerFileAssetRoutes(app, deps) {
|
|
|
2226
2501
|
realpath(workspace.path),
|
|
2227
2502
|
realpath(absPath)
|
|
2228
2503
|
]);
|
|
2229
|
-
if (!
|
|
2504
|
+
if (!isPathInsideRoot2(realWorkspacePath, realAssetPath)) {
|
|
2230
2505
|
return reply.status(400).send({ ok: false, error: "path_escape" });
|
|
2231
2506
|
}
|
|
2232
2507
|
} catch {
|
|
@@ -2234,7 +2509,7 @@ function registerFileAssetRoutes(app, deps) {
|
|
|
2234
2509
|
}
|
|
2235
2510
|
let fileSize;
|
|
2236
2511
|
try {
|
|
2237
|
-
const stats = await
|
|
2512
|
+
const stats = await stat3(absPath);
|
|
2238
2513
|
if (!stats.isFile()) {
|
|
2239
2514
|
return reply.status(404).send({ ok: false, error: "not_a_file" });
|
|
2240
2515
|
}
|
|
@@ -2243,7 +2518,7 @@ function registerFileAssetRoutes(app, deps) {
|
|
|
2243
2518
|
return reply.status(404).send({ ok: false, error: "not_found" });
|
|
2244
2519
|
}
|
|
2245
2520
|
reply.header("Content-Type", typeInfo.mime).header("Content-Length", String(fileSize)).header("Cache-Control", "no-store").header("X-Content-Type-Options", "nosniff");
|
|
2246
|
-
return reply.send(
|
|
2521
|
+
return reply.send(createReadStream2(absPath));
|
|
2247
2522
|
}
|
|
2248
2523
|
);
|
|
2249
2524
|
}
|
|
@@ -2313,7 +2588,7 @@ var init_render_markdown = __esm({
|
|
|
2313
2588
|
});
|
|
2314
2589
|
|
|
2315
2590
|
// packages/server/src/preview/resource-loader.ts
|
|
2316
|
-
import { readFile as readFile2, realpath as realpath2, stat as
|
|
2591
|
+
import { readFile as readFile2, realpath as realpath2, stat as stat4 } from "node:fs/promises";
|
|
2317
2592
|
import { posix as posix2 } from "node:path";
|
|
2318
2593
|
import mime from "mime-types";
|
|
2319
2594
|
function resolvePreviewResourcePath(entryPath, requestedPath) {
|
|
@@ -2331,10 +2606,10 @@ async function loadPreviewResource(workspaceRootPath, workspaceRelativePath) {
|
|
|
2331
2606
|
realpath2(workspaceRootPath),
|
|
2332
2607
|
realpath2(absolutePath)
|
|
2333
2608
|
]);
|
|
2334
|
-
if (!
|
|
2609
|
+
if (!isPathInsideRoot2(realWorkspacePath, realAssetPath)) {
|
|
2335
2610
|
throw new Error("path_escape");
|
|
2336
2611
|
}
|
|
2337
|
-
const [bytes, stats] = await Promise.all([readFile2(realAssetPath),
|
|
2612
|
+
const [bytes, stats] = await Promise.all([readFile2(realAssetPath), stat4(realAssetPath)]);
|
|
2338
2613
|
if (!stats.isFile()) {
|
|
2339
2614
|
throw new Error("not_a_file");
|
|
2340
2615
|
}
|
|
@@ -2478,118 +2753,8 @@ var init_constants = __esm({
|
|
|
2478
2753
|
}
|
|
2479
2754
|
});
|
|
2480
2755
|
|
|
2481
|
-
// packages/server/src/uploads/paths.ts
|
|
2482
|
-
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
2483
|
-
import { lstat, mkdir as mkdir3 } from "node:fs/promises";
|
|
2484
|
-
import path7 from "node:path";
|
|
2485
|
-
function sanitizeOriginalName(input) {
|
|
2486
|
-
let sanitized = "";
|
|
2487
|
-
for (const char of input.trim()) {
|
|
2488
|
-
sanitized += KEEP_FILENAME_CHAR.test(char) ? char : "_";
|
|
2489
|
-
}
|
|
2490
|
-
sanitized = sanitized.replace(/^\.+/, "");
|
|
2491
|
-
if (sanitized.length === 0 || /^[_\s]*$/.test(sanitized)) {
|
|
2492
|
-
return "file";
|
|
2493
|
-
}
|
|
2494
|
-
if (sanitized.length <= MAX_FILENAME_LENGTH) {
|
|
2495
|
-
return sanitized;
|
|
2496
|
-
}
|
|
2497
|
-
const lastDot = sanitized.lastIndexOf(".");
|
|
2498
|
-
if (lastDot > 0 && sanitized.length - lastDot <= 16) {
|
|
2499
|
-
const ext = sanitized.slice(lastDot);
|
|
2500
|
-
const stem = sanitized.slice(0, MAX_FILENAME_LENGTH - ext.length);
|
|
2501
|
-
return stem + ext;
|
|
2502
|
-
}
|
|
2503
|
-
return sanitized.slice(0, MAX_FILENAME_LENGTH);
|
|
2504
|
-
}
|
|
2505
|
-
function validateWorkspaceId(id) {
|
|
2506
|
-
if (!WORKSPACE_ID_RE.test(id)) {
|
|
2507
|
-
throw new Error(`invalid workspace id: ${JSON.stringify(id)}`);
|
|
2508
|
-
}
|
|
2509
|
-
}
|
|
2510
|
-
async function assertDirectorySegmentSafe(segmentPath) {
|
|
2511
|
-
const info = await lstat(segmentPath);
|
|
2512
|
-
if (info.isSymbolicLink()) {
|
|
2513
|
-
throw new Error(`symlinked upload path segment is not allowed: ${segmentPath}`);
|
|
2514
|
-
}
|
|
2515
|
-
if (!info.isDirectory()) {
|
|
2516
|
-
throw new Error(`upload path segment is not a directory: ${segmentPath}`);
|
|
2517
|
-
}
|
|
2518
|
-
}
|
|
2519
|
-
async function ensureSafeUploadDir(rootDir, targetDir2) {
|
|
2520
|
-
const resolvedRoot = path7.resolve(rootDir);
|
|
2521
|
-
const resolvedTarget = path7.resolve(targetDir2);
|
|
2522
|
-
if (resolvedTarget !== resolvedRoot && !resolvedTarget.startsWith(`${resolvedRoot}${path7.sep}`)) {
|
|
2523
|
-
throw new Error(`target dir escaped uploads root: ${resolvedTarget}`);
|
|
2524
|
-
}
|
|
2525
|
-
try {
|
|
2526
|
-
await assertDirectorySegmentSafe(resolvedRoot);
|
|
2527
|
-
} catch (error) {
|
|
2528
|
-
const code = error.code;
|
|
2529
|
-
if (code !== "ENOENT") {
|
|
2530
|
-
throw error;
|
|
2531
|
-
}
|
|
2532
|
-
await mkdir3(resolvedRoot, { recursive: true });
|
|
2533
|
-
await assertDirectorySegmentSafe(resolvedRoot);
|
|
2534
|
-
}
|
|
2535
|
-
const relative4 = path7.relative(resolvedRoot, resolvedTarget);
|
|
2536
|
-
if (!relative4) {
|
|
2537
|
-
return;
|
|
2538
|
-
}
|
|
2539
|
-
let current = resolvedRoot;
|
|
2540
|
-
for (const segment of relative4.split(path7.sep)) {
|
|
2541
|
-
current = path7.join(current, segment);
|
|
2542
|
-
try {
|
|
2543
|
-
await assertDirectorySegmentSafe(current);
|
|
2544
|
-
continue;
|
|
2545
|
-
} catch (error) {
|
|
2546
|
-
const code = error.code;
|
|
2547
|
-
if (code !== "ENOENT") {
|
|
2548
|
-
throw error;
|
|
2549
|
-
}
|
|
2550
|
-
}
|
|
2551
|
-
try {
|
|
2552
|
-
await mkdir3(current);
|
|
2553
|
-
} catch (error) {
|
|
2554
|
-
const code = error.code;
|
|
2555
|
-
if (code !== "EEXIST") {
|
|
2556
|
-
throw error;
|
|
2557
|
-
}
|
|
2558
|
-
}
|
|
2559
|
-
await assertDirectorySegmentSafe(current);
|
|
2560
|
-
}
|
|
2561
|
-
}
|
|
2562
|
-
function generateBucketPath(input) {
|
|
2563
|
-
validateWorkspaceId(input.workspaceId);
|
|
2564
|
-
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
2565
|
-
const dateStr = now.toISOString().slice(0, 10);
|
|
2566
|
-
const dir = path7.join(input.uploadsDir, input.workspaceId, dateStr);
|
|
2567
|
-
const sanitizedName = sanitizeOriginalName(input.originalName);
|
|
2568
|
-
const uuid8 = randomUUID2().replace(/-/g, "").slice(0, 8);
|
|
2569
|
-
const absolutePath = path7.resolve(dir, `${uuid8}-${sanitizedName}`);
|
|
2570
|
-
const uploadsRoot = `${path7.resolve(input.uploadsDir)}${path7.sep}`;
|
|
2571
|
-
if (!absolutePath.startsWith(uploadsRoot)) {
|
|
2572
|
-
throw new Error(`generated upload path escaped uploads root: ${absolutePath}`);
|
|
2573
|
-
}
|
|
2574
|
-
return {
|
|
2575
|
-
dir,
|
|
2576
|
-
absolutePath,
|
|
2577
|
-
uuid8,
|
|
2578
|
-
sanitizedName
|
|
2579
|
-
};
|
|
2580
|
-
}
|
|
2581
|
-
var MAX_FILENAME_LENGTH, KEEP_FILENAME_CHAR, WORKSPACE_ID_RE;
|
|
2582
|
-
var init_paths = __esm({
|
|
2583
|
-
"packages/server/src/uploads/paths.ts"() {
|
|
2584
|
-
"use strict";
|
|
2585
|
-
MAX_FILENAME_LENGTH = 64;
|
|
2586
|
-
KEEP_FILENAME_CHAR = /[a-zA-Z0-9._一-鿿 \-]/;
|
|
2587
|
-
WORKSPACE_ID_RE = /^[a-zA-Z0-9_-]+$/;
|
|
2588
|
-
}
|
|
2589
|
-
});
|
|
2590
|
-
|
|
2591
2756
|
// packages/server/src/uploads/cleanup.ts
|
|
2592
|
-
import { readdir, rm as
|
|
2757
|
+
import { readdir, rm as rm4, rmdir, stat as stat5, unlink } from "node:fs/promises";
|
|
2593
2758
|
import path8 from "node:path";
|
|
2594
2759
|
async function listFilesRecursive(root) {
|
|
2595
2760
|
let entries;
|
|
@@ -2611,7 +2776,7 @@ async function listFilesRecursive(root) {
|
|
|
2611
2776
|
if (!entry.isFile()) {
|
|
2612
2777
|
continue;
|
|
2613
2778
|
}
|
|
2614
|
-
const fileStat = await
|
|
2779
|
+
const fileStat = await stat5(childPath);
|
|
2615
2780
|
files.push({
|
|
2616
2781
|
absPath: childPath,
|
|
2617
2782
|
size: fileStat.size,
|
|
@@ -2644,7 +2809,7 @@ async function pruneEmptyDirectories(root) {
|
|
|
2644
2809
|
async function deleteWorkspaceUploads(uploadsDir, workspaceId) {
|
|
2645
2810
|
validateWorkspaceId(workspaceId);
|
|
2646
2811
|
const bucket = path8.join(uploadsDir, workspaceId);
|
|
2647
|
-
await
|
|
2812
|
+
await rm4(bucket, { recursive: true, force: true });
|
|
2648
2813
|
}
|
|
2649
2814
|
async function enforceBucketCap(uploadsDir, workspaceId, capBytes, logger) {
|
|
2650
2815
|
validateWorkspaceId(workspaceId);
|
|
@@ -2727,9 +2892,9 @@ var init_cleanup = __esm({
|
|
|
2727
2892
|
});
|
|
2728
2893
|
|
|
2729
2894
|
// packages/server/src/routes/uploads.ts
|
|
2730
|
-
import { createWriteStream } from "node:fs";
|
|
2731
|
-
import { rm as
|
|
2732
|
-
import { pipeline } from "node:stream/promises";
|
|
2895
|
+
import { createWriteStream as createWriteStream2 } from "node:fs";
|
|
2896
|
+
import { rm as rm5, stat as stat6, writeFile as writeFile3 } from "node:fs/promises";
|
|
2897
|
+
import { pipeline as pipeline2 } from "node:stream/promises";
|
|
2733
2898
|
function inferClipboardFilename(filename, mimeType, now) {
|
|
2734
2899
|
const trimmed = filename?.trim();
|
|
2735
2900
|
if (trimmed) {
|
|
@@ -2749,7 +2914,7 @@ function inferClipboardFilename(filename, mimeType, now) {
|
|
|
2749
2914
|
return `screenshot-${hhmmss}.${ext}`;
|
|
2750
2915
|
}
|
|
2751
2916
|
async function cleanupWrittenFiles(files) {
|
|
2752
|
-
await Promise.all(files.map((file) =>
|
|
2917
|
+
await Promise.all(files.map((file) => rm5(file.path, { force: true })));
|
|
2753
2918
|
}
|
|
2754
2919
|
function getRequestLogger(request) {
|
|
2755
2920
|
const logger = request.log;
|
|
@@ -2758,7 +2923,7 @@ function getRequestLogger(request) {
|
|
|
2758
2923
|
}
|
|
2759
2924
|
return void 0;
|
|
2760
2925
|
}
|
|
2761
|
-
async function
|
|
2926
|
+
async function rejectAndCleanup2(reply, written, statusCode, error) {
|
|
2762
2927
|
await cleanupWrittenFiles(written);
|
|
2763
2928
|
return reply.status(statusCode).send({ ok: false, error });
|
|
2764
2929
|
}
|
|
@@ -2771,7 +2936,7 @@ function getActiveWorkspace(deps, workspaceId) {
|
|
|
2771
2936
|
async function ensureWorkspaceStillActive(deps, workspaceId, reply, written) {
|
|
2772
2937
|
const workspace = getActiveWorkspace(deps, workspaceId);
|
|
2773
2938
|
if (!workspace) {
|
|
2774
|
-
await
|
|
2939
|
+
await rejectAndCleanup2(reply, written, 404, "workspace_not_found");
|
|
2775
2940
|
return null;
|
|
2776
2941
|
}
|
|
2777
2942
|
return workspace;
|
|
@@ -2798,22 +2963,22 @@ function registerUploadsRoute(app, deps) {
|
|
|
2798
2963
|
if (part.type === "field" && part.fieldname === "workspaceId") {
|
|
2799
2964
|
const lockedWorkspaceId = lockWorkspaceId(workspaceId, String(part.value));
|
|
2800
2965
|
if (lockedWorkspaceId === "mismatch") {
|
|
2801
|
-
return
|
|
2966
|
+
return rejectAndCleanup2(reply, written, 400, "workspace_mismatch");
|
|
2802
2967
|
}
|
|
2803
2968
|
workspaceId = lockedWorkspaceId;
|
|
2804
2969
|
if (!getActiveWorkspace(deps, workspaceId)) {
|
|
2805
|
-
return
|
|
2970
|
+
return rejectAndCleanup2(reply, written, 404, "workspace_not_found");
|
|
2806
2971
|
}
|
|
2807
2972
|
workspaceValidated = true;
|
|
2808
2973
|
continue;
|
|
2809
2974
|
}
|
|
2810
2975
|
if (part.type === "field" && part.fieldname === "files") {
|
|
2811
2976
|
if (!workspaceId) {
|
|
2812
|
-
return
|
|
2977
|
+
return rejectAndCleanup2(reply, written, 400, "workspace_required");
|
|
2813
2978
|
}
|
|
2814
2979
|
fileCount += 1;
|
|
2815
2980
|
if (fileCount > MAX_FILES_PER_BATCH) {
|
|
2816
|
-
return
|
|
2981
|
+
return rejectAndCleanup2(reply, written, 400, "too_many_files");
|
|
2817
2982
|
}
|
|
2818
2983
|
const now2 = /* @__PURE__ */ new Date();
|
|
2819
2984
|
const originalName2 = inferClipboardFilename(void 0, part.mimetype, now2);
|
|
@@ -2830,20 +2995,20 @@ function registerUploadsRoute(app, deps) {
|
|
|
2830
2995
|
await ensureSafeUploadDir(deps.uploadsDir, target2.dir);
|
|
2831
2996
|
await writeFile3(target2.absolutePath, String(part.value));
|
|
2832
2997
|
} catch (error) {
|
|
2833
|
-
await
|
|
2998
|
+
await rm5(target2.absolutePath, { force: true });
|
|
2834
2999
|
await cleanupWrittenFiles(written);
|
|
2835
3000
|
logger?.warn({ err: error }, "upload write failed");
|
|
2836
3001
|
return reply.status(500).send({ ok: false, error: "write_failed" });
|
|
2837
3002
|
}
|
|
2838
3003
|
try {
|
|
2839
|
-
const fileStat = await
|
|
3004
|
+
const fileStat = await stat6(target2.absolutePath);
|
|
2840
3005
|
written.push({
|
|
2841
3006
|
path: target2.absolutePath,
|
|
2842
3007
|
originalName: originalName2,
|
|
2843
3008
|
size: fileStat.size
|
|
2844
3009
|
});
|
|
2845
3010
|
} catch (error) {
|
|
2846
|
-
await
|
|
3011
|
+
await rm5(target2.absolutePath, { force: true });
|
|
2847
3012
|
await cleanupWrittenFiles(written);
|
|
2848
3013
|
logger?.warn({ err: error }, "upload stat failed");
|
|
2849
3014
|
return reply.status(500).send({ ok: false, error: "write_failed" });
|
|
@@ -2858,12 +3023,12 @@ function registerUploadsRoute(app, deps) {
|
|
|
2858
3023
|
}
|
|
2859
3024
|
if (!workspaceId) {
|
|
2860
3025
|
part.file.resume();
|
|
2861
|
-
return
|
|
3026
|
+
return rejectAndCleanup2(reply, written, 400, "workspace_required");
|
|
2862
3027
|
}
|
|
2863
3028
|
fileCount += 1;
|
|
2864
3029
|
if (fileCount > MAX_FILES_PER_BATCH) {
|
|
2865
3030
|
part.file.resume();
|
|
2866
|
-
return
|
|
3031
|
+
return rejectAndCleanup2(reply, written, 400, "too_many_files");
|
|
2867
3032
|
}
|
|
2868
3033
|
const now = /* @__PURE__ */ new Date();
|
|
2869
3034
|
const originalName = inferClipboardFilename(part.filename, part.mimetype, now);
|
|
@@ -2879,27 +3044,27 @@ function registerUploadsRoute(app, deps) {
|
|
|
2879
3044
|
}
|
|
2880
3045
|
try {
|
|
2881
3046
|
await ensureSafeUploadDir(deps.uploadsDir, target.dir);
|
|
2882
|
-
await
|
|
3047
|
+
await pipeline2(part.file, createWriteStream2(target.absolutePath));
|
|
2883
3048
|
} catch (error) {
|
|
2884
|
-
await
|
|
3049
|
+
await rm5(target.absolutePath, { force: true });
|
|
2885
3050
|
await cleanupWrittenFiles(written);
|
|
2886
3051
|
logger?.warn({ err: error }, "upload write failed");
|
|
2887
3052
|
return reply.status(500).send({ ok: false, error: "write_failed" });
|
|
2888
3053
|
}
|
|
2889
3054
|
if (part.file.truncated) {
|
|
2890
|
-
await
|
|
3055
|
+
await rm5(target.absolutePath, { force: true });
|
|
2891
3056
|
await cleanupWrittenFiles(written);
|
|
2892
3057
|
return reply.status(413).send({ ok: false, error: "file_too_large" });
|
|
2893
3058
|
}
|
|
2894
3059
|
try {
|
|
2895
|
-
const fileStat = await
|
|
3060
|
+
const fileStat = await stat6(target.absolutePath);
|
|
2896
3061
|
written.push({
|
|
2897
3062
|
path: target.absolutePath,
|
|
2898
3063
|
originalName,
|
|
2899
3064
|
size: fileStat.size
|
|
2900
3065
|
});
|
|
2901
3066
|
} catch (error) {
|
|
2902
|
-
await
|
|
3067
|
+
await rm5(target.absolutePath, { force: true });
|
|
2903
3068
|
await cleanupWrittenFiles(written);
|
|
2904
3069
|
logger?.warn({ err: error }, "upload stat failed");
|
|
2905
3070
|
return reply.status(500).send({ ok: false, error: "write_failed" });
|
|
@@ -2910,37 +3075,135 @@ function registerUploadsRoute(app, deps) {
|
|
|
2910
3075
|
if (error.code === "FST_REQ_FILE_TOO_LARGE") {
|
|
2911
3076
|
return reply.status(413).send({ ok: false, error: "file_too_large" });
|
|
2912
3077
|
}
|
|
2913
|
-
logger?.warn({ err: error }, "upload parse failed");
|
|
2914
|
-
return reply.status(400).send({ ok: false, error: "parse_failed" });
|
|
2915
|
-
}
|
|
2916
|
-
if (!workspaceId) {
|
|
2917
|
-
return
|
|
2918
|
-
}
|
|
2919
|
-
if (!workspaceValidated) {
|
|
2920
|
-
return
|
|
2921
|
-
}
|
|
2922
|
-
if (written.length === 0) {
|
|
2923
|
-
return
|
|
2924
|
-
}
|
|
2925
|
-
if (!await ensureWorkspaceStillActive(deps, workspaceId, reply, written)) {
|
|
2926
|
-
return;
|
|
2927
|
-
}
|
|
2928
|
-
void enforceBucketCap(deps.uploadsDir, workspaceId, UPLOAD_BUCKET_MAX_BYTES, logger).catch(
|
|
2929
|
-
(error) => logger?.warn({ err: error }, "bucket cap enforcement failed")
|
|
2930
|
-
);
|
|
2931
|
-
return reply.send({ ok: true, files: written });
|
|
2932
|
-
});
|
|
2933
|
-
}
|
|
2934
|
-
var init_uploads = __esm({
|
|
2935
|
-
"packages/server/src/routes/uploads.ts"() {
|
|
2936
|
-
"use strict";
|
|
2937
|
-
init_cleanup();
|
|
2938
|
-
init_constants();
|
|
2939
|
-
init_paths();
|
|
3078
|
+
logger?.warn({ err: error }, "upload parse failed");
|
|
3079
|
+
return reply.status(400).send({ ok: false, error: "parse_failed" });
|
|
3080
|
+
}
|
|
3081
|
+
if (!workspaceId) {
|
|
3082
|
+
return rejectAndCleanup2(reply, written, 400, "workspace_required");
|
|
3083
|
+
}
|
|
3084
|
+
if (!workspaceValidated) {
|
|
3085
|
+
return rejectAndCleanup2(reply, written, 404, "workspace_not_found");
|
|
3086
|
+
}
|
|
3087
|
+
if (written.length === 0) {
|
|
3088
|
+
return rejectAndCleanup2(reply, written, 400, "no_files");
|
|
3089
|
+
}
|
|
3090
|
+
if (!await ensureWorkspaceStillActive(deps, workspaceId, reply, written)) {
|
|
3091
|
+
return;
|
|
3092
|
+
}
|
|
3093
|
+
void enforceBucketCap(deps.uploadsDir, workspaceId, UPLOAD_BUCKET_MAX_BYTES, logger).catch(
|
|
3094
|
+
(error) => logger?.warn({ err: error }, "bucket cap enforcement failed")
|
|
3095
|
+
);
|
|
3096
|
+
return reply.send({ ok: true, files: written });
|
|
3097
|
+
});
|
|
3098
|
+
}
|
|
3099
|
+
var init_uploads = __esm({
|
|
3100
|
+
"packages/server/src/routes/uploads.ts"() {
|
|
3101
|
+
"use strict";
|
|
3102
|
+
init_cleanup();
|
|
3103
|
+
init_constants();
|
|
3104
|
+
init_paths();
|
|
3105
|
+
}
|
|
3106
|
+
});
|
|
3107
|
+
|
|
3108
|
+
// packages/server/src/storage/repositories/json-file-store.ts
|
|
3109
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, renameSync, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
3110
|
+
import { dirname as dirname3 } from "node:path";
|
|
3111
|
+
function hasCode(error, code) {
|
|
3112
|
+
return Boolean(
|
|
3113
|
+
error && typeof error === "object" && "code" in error && error.code === code
|
|
3114
|
+
);
|
|
3115
|
+
}
|
|
3116
|
+
function readJsonFile(filePath) {
|
|
3117
|
+
try {
|
|
3118
|
+
return JSON.parse(readFileSync3(filePath, "utf-8"));
|
|
3119
|
+
} catch (error) {
|
|
3120
|
+
if (hasCode(error, "ENOENT")) {
|
|
3121
|
+
return void 0;
|
|
3122
|
+
}
|
|
3123
|
+
throw error;
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3126
|
+
function writeJsonFileAtomic(filePath, value) {
|
|
3127
|
+
mkdirSync2(dirname3(filePath), { recursive: true });
|
|
3128
|
+
const tempPath = `${filePath}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
3129
|
+
try {
|
|
3130
|
+
writeFileSync2(tempPath, JSON.stringify(value, null, 2) + "\n", "utf-8");
|
|
3131
|
+
renameSync(tempPath, filePath);
|
|
3132
|
+
} catch (error) {
|
|
3133
|
+
rmSync(tempPath, { force: true });
|
|
3134
|
+
throw error;
|
|
3135
|
+
}
|
|
3136
|
+
}
|
|
3137
|
+
var init_json_file_store = __esm({
|
|
3138
|
+
"packages/server/src/storage/repositories/json-file-store.ts"() {
|
|
3139
|
+
"use strict";
|
|
3140
|
+
}
|
|
3141
|
+
});
|
|
3142
|
+
|
|
3143
|
+
// packages/server/src/storage/repositories/appearance-asset-repo.ts
|
|
3144
|
+
function isRecord(value) {
|
|
3145
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
3146
|
+
}
|
|
3147
|
+
function normalizeAppearanceAssetFile(value) {
|
|
3148
|
+
if (isRecord(value) && value.version === 1 && isRecord(value.assets)) {
|
|
3149
|
+
return value.assets;
|
|
3150
|
+
}
|
|
3151
|
+
if (isRecord(value)) {
|
|
3152
|
+
return value;
|
|
3153
|
+
}
|
|
3154
|
+
return {};
|
|
3155
|
+
}
|
|
3156
|
+
var AppearanceAssetRepo;
|
|
3157
|
+
var init_appearance_asset_repo = __esm({
|
|
3158
|
+
"packages/server/src/storage/repositories/appearance-asset-repo.ts"() {
|
|
3159
|
+
"use strict";
|
|
3160
|
+
init_json_file_store();
|
|
3161
|
+
AppearanceAssetRepo = class {
|
|
3162
|
+
constructor(options) {
|
|
3163
|
+
this.options = options;
|
|
3164
|
+
}
|
|
3165
|
+
options;
|
|
3166
|
+
loadFileAssets() {
|
|
3167
|
+
const parsed = readJsonFile(
|
|
3168
|
+
this.options.filePath
|
|
3169
|
+
);
|
|
3170
|
+
if (parsed !== void 0) {
|
|
3171
|
+
return { ...normalizeAppearanceAssetFile(parsed) };
|
|
3172
|
+
}
|
|
3173
|
+
return {};
|
|
3174
|
+
}
|
|
3175
|
+
saveFileAssets(assets) {
|
|
3176
|
+
const payload = {
|
|
3177
|
+
version: 1,
|
|
3178
|
+
assets
|
|
3179
|
+
};
|
|
3180
|
+
writeJsonFileAtomic(this.options.filePath, payload);
|
|
3181
|
+
}
|
|
3182
|
+
get(id) {
|
|
3183
|
+
return this.loadFileAssets()[id];
|
|
3184
|
+
}
|
|
3185
|
+
set(record) {
|
|
3186
|
+
const next = this.loadFileAssets();
|
|
3187
|
+
next[record.id] = record;
|
|
3188
|
+
this.saveFileAssets(next);
|
|
3189
|
+
}
|
|
3190
|
+
delete(id) {
|
|
3191
|
+
const next = this.loadFileAssets();
|
|
3192
|
+
if (!Object.prototype.hasOwnProperty.call(next, id)) {
|
|
3193
|
+
return;
|
|
3194
|
+
}
|
|
3195
|
+
delete next[id];
|
|
3196
|
+
this.saveFileAssets(next);
|
|
3197
|
+
}
|
|
3198
|
+
list() {
|
|
3199
|
+
return Object.values(this.loadFileAssets());
|
|
3200
|
+
}
|
|
3201
|
+
};
|
|
2940
3202
|
}
|
|
2941
3203
|
});
|
|
2942
3204
|
|
|
2943
3205
|
// packages/server/src/app.ts
|
|
3206
|
+
import { join as join3, resolve as resolve3 } from "node:path";
|
|
2944
3207
|
import compress from "@fastify/compress";
|
|
2945
3208
|
import cors from "@fastify/cors";
|
|
2946
3209
|
import multipart from "@fastify/multipart";
|
|
@@ -2948,6 +3211,10 @@ import staticPlugin from "@fastify/static";
|
|
|
2948
3211
|
import websocket from "@fastify/websocket";
|
|
2949
3212
|
import Fastify from "fastify";
|
|
2950
3213
|
async function buildFastifyApp(deps) {
|
|
3214
|
+
const stateRoot = deps.config.stateDir === IN_MEMORY_STATE_DIR ? resolve3(deps.config.uploadsDir, "..") : deps.config.stateDir;
|
|
3215
|
+
const appearanceAssetRepo = deps.appearanceAssetRepo ?? new AppearanceAssetRepo({
|
|
3216
|
+
filePath: join3(stateRoot, "state", "appearance-assets.json")
|
|
3217
|
+
});
|
|
2951
3218
|
const app = Fastify({
|
|
2952
3219
|
logger: deps.logger ?? {
|
|
2953
3220
|
level: "info",
|
|
@@ -3030,6 +3297,10 @@ async function buildFastifyApp(deps) {
|
|
|
3030
3297
|
registerFileAssetRoutes(app, {
|
|
3031
3298
|
workspaceMgr: deps.workspaceMgr
|
|
3032
3299
|
});
|
|
3300
|
+
registerAppearanceAssetsRoutes(app, {
|
|
3301
|
+
uploadsDir: deps.config.uploadsDir,
|
|
3302
|
+
repo: appearanceAssetRepo
|
|
3303
|
+
});
|
|
3033
3304
|
const previewSessions = new PreviewSessionStore();
|
|
3034
3305
|
registerPreviewRoutes(app, {
|
|
3035
3306
|
workspaceMgr: deps.workspaceMgr,
|
|
@@ -3083,11 +3354,14 @@ async function buildFastifyApp(deps) {
|
|
|
3083
3354
|
var init_app = __esm({
|
|
3084
3355
|
"packages/server/src/app.ts"() {
|
|
3085
3356
|
"use strict";
|
|
3357
|
+
init_state_paths();
|
|
3086
3358
|
init_auth();
|
|
3087
3359
|
init_session_store();
|
|
3360
|
+
init_appearance_assets();
|
|
3088
3361
|
init_file_asset();
|
|
3089
3362
|
init_preview();
|
|
3090
3363
|
init_uploads();
|
|
3364
|
+
init_appearance_asset_repo();
|
|
3091
3365
|
init_constants();
|
|
3092
3366
|
init_web_ui_routing();
|
|
3093
3367
|
}
|
|
@@ -3324,12 +3598,12 @@ var init_auto_fetch = __esm({
|
|
|
3324
3598
|
}
|
|
3325
3599
|
acquireWorkspaceOperation(workspaceId) {
|
|
3326
3600
|
const state = this.getOrCreateState(workspaceId);
|
|
3327
|
-
return new Promise((
|
|
3601
|
+
return new Promise((resolve6) => {
|
|
3328
3602
|
const grant = () => {
|
|
3329
3603
|
state.inFlight = true;
|
|
3330
3604
|
state.nextFetchAt = void 0;
|
|
3331
3605
|
let released = false;
|
|
3332
|
-
|
|
3606
|
+
resolve6(() => {
|
|
3333
3607
|
if (released) {
|
|
3334
3608
|
return;
|
|
3335
3609
|
}
|
|
@@ -4332,7 +4606,7 @@ var init_manager = __esm({
|
|
|
4332
4606
|
// packages/server/src/provider-runtime/command-runner.ts
|
|
4333
4607
|
import { spawn as spawn2 } from "node:child_process";
|
|
4334
4608
|
async function runCommandAsString(file, args, options) {
|
|
4335
|
-
return new Promise((
|
|
4609
|
+
return new Promise((resolve6, reject) => {
|
|
4336
4610
|
const child = spawn2(file, args, {
|
|
4337
4611
|
cwd: options?.cwd,
|
|
4338
4612
|
env: options?.env,
|
|
@@ -4359,7 +4633,7 @@ async function runCommandAsString(file, args, options) {
|
|
|
4359
4633
|
const stdout = Buffer.concat(stdoutChunks).toString("utf8");
|
|
4360
4634
|
const stderr = Buffer.concat(stderrChunks).toString("utf8");
|
|
4361
4635
|
if (code === 0) {
|
|
4362
|
-
|
|
4636
|
+
resolve6({ stdout, stderr });
|
|
4363
4637
|
return;
|
|
4364
4638
|
}
|
|
4365
4639
|
reject(
|
|
@@ -4478,9 +4752,9 @@ var init_definitions = __esm({
|
|
|
4478
4752
|
});
|
|
4479
4753
|
|
|
4480
4754
|
// packages/server/src/lsp-tools/install-manager.ts
|
|
4481
|
-
import { randomUUID as
|
|
4482
|
-
import { mkdirSync as
|
|
4483
|
-
import { dirname as
|
|
4755
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
4756
|
+
import { mkdirSync as mkdirSync3 } from "node:fs";
|
|
4757
|
+
import { dirname as dirname4, join as join4 } from "node:path";
|
|
4484
4758
|
function toSnapshotStep(step) {
|
|
4485
4759
|
return {
|
|
4486
4760
|
id: step.id,
|
|
@@ -4644,7 +4918,7 @@ var init_install_manager = __esm({
|
|
|
4644
4918
|
async prepare(input) {
|
|
4645
4919
|
const definition = getLspToolDefinition(input.serverKind);
|
|
4646
4920
|
const managed = definition.managed;
|
|
4647
|
-
const jobId =
|
|
4921
|
+
const jobId = randomUUID4();
|
|
4648
4922
|
const platform = this.deps.platform ?? process.platform;
|
|
4649
4923
|
if (!managed || input.workspace.targetRuntime !== "native") {
|
|
4650
4924
|
return {
|
|
@@ -4713,13 +4987,13 @@ var init_install_manager = __esm({
|
|
|
4713
4987
|
}
|
|
4714
4988
|
};
|
|
4715
4989
|
}
|
|
4716
|
-
const installRoot =
|
|
4717
|
-
const executablePath = input.serverKind === "python" ?
|
|
4990
|
+
const installRoot = join4(this.deps.manifestStore.getRoot(), input.serverKind, managed.version);
|
|
4991
|
+
const executablePath = input.serverKind === "python" ? join4(
|
|
4718
4992
|
installRoot,
|
|
4719
4993
|
"venv",
|
|
4720
4994
|
platform === "win32" ? "Scripts" : "bin",
|
|
4721
4995
|
platform === "win32" ? "pylsp.exe" : "pylsp"
|
|
4722
|
-
) : input.serverKind === "go" ?
|
|
4996
|
+
) : input.serverKind === "go" ? join4(installRoot, "bin", platform === "win32" ? "gopls.exe" : "gopls") : join4(installRoot, "bin", platform === "win32" ? "rust-analyzer.exe" : "rust-analyzer");
|
|
4723
4997
|
const plannedSteps = this.planInstallSteps({
|
|
4724
4998
|
serverKind: input.serverKind,
|
|
4725
4999
|
installRoot,
|
|
@@ -4746,16 +5020,16 @@ var init_install_manager = __esm({
|
|
|
4746
5020
|
const platform = this.deps.platform ?? process.platform;
|
|
4747
5021
|
job.status = "running";
|
|
4748
5022
|
this.jobs.set(job.jobId, job);
|
|
4749
|
-
const installRoot =
|
|
4750
|
-
const executablePath = serverKind === "python" ?
|
|
5023
|
+
const installRoot = join4(this.deps.manifestStore.getRoot(), serverKind, managed.version);
|
|
5024
|
+
const executablePath = serverKind === "python" ? join4(
|
|
4751
5025
|
installRoot,
|
|
4752
5026
|
"venv",
|
|
4753
5027
|
platform === "win32" ? "Scripts" : "bin",
|
|
4754
5028
|
platform === "win32" ? "pylsp.exe" : "pylsp"
|
|
4755
|
-
) : serverKind === "go" ?
|
|
5029
|
+
) : serverKind === "go" ? join4(installRoot, "bin", platform === "win32" ? "gopls.exe" : "gopls") : join4(installRoot, "bin", platform === "win32" ? "rust-analyzer.exe" : "rust-analyzer");
|
|
4756
5030
|
const commandExists = this.deps.commandExists ?? ((command) => checkCommandAvailable(command, this.deps));
|
|
4757
5031
|
const pythonCommand = serverKind === "python" ? await resolveManagedPythonCommand(commandExists, platform) : null;
|
|
4758
|
-
|
|
5032
|
+
mkdirSync3(dirname4(executablePath), { recursive: true });
|
|
4759
5033
|
for (const step of job.steps) {
|
|
4760
5034
|
job.currentStepId = step.id;
|
|
4761
5035
|
step.status = "running";
|
|
@@ -4822,9 +5096,9 @@ var init_install_manager = __esm({
|
|
|
4822
5096
|
}
|
|
4823
5097
|
planInstallSteps(input) {
|
|
4824
5098
|
if (input.serverKind === "python") {
|
|
4825
|
-
const venvRoot =
|
|
4826
|
-
const binDir =
|
|
4827
|
-
const pipPath =
|
|
5099
|
+
const venvRoot = join4(input.installRoot, "venv");
|
|
5100
|
+
const binDir = join4(venvRoot, input.platform === "win32" ? "Scripts" : "bin");
|
|
5101
|
+
const pipPath = join4(binDir, input.platform === "win32" ? "pip.exe" : "pip");
|
|
4828
5102
|
return [
|
|
4829
5103
|
{
|
|
4830
5104
|
id: "create-python-venv",
|
|
@@ -4861,7 +5135,7 @@ var init_install_manager = __esm({
|
|
|
4861
5135
|
args: ["install", `golang.org/x/tools/gopls@${input.version}`],
|
|
4862
5136
|
env: {
|
|
4863
5137
|
...process.env,
|
|
4864
|
-
GOBIN:
|
|
5138
|
+
GOBIN: join4(input.installRoot, "bin")
|
|
4865
5139
|
}
|
|
4866
5140
|
},
|
|
4867
5141
|
{
|
|
@@ -4921,7 +5195,7 @@ var init_install_manager = __esm({
|
|
|
4921
5195
|
// packages/server/src/lsp-tools/manager.ts
|
|
4922
5196
|
import { existsSync as existsSync3 } from "node:fs";
|
|
4923
5197
|
import { createRequire as createRequire2 } from "node:module";
|
|
4924
|
-
import { join as
|
|
5198
|
+
import { join as join5 } from "node:path";
|
|
4925
5199
|
function parseOverrideArgs(raw, envVarName) {
|
|
4926
5200
|
try {
|
|
4927
5201
|
const parsed = JSON.parse(raw);
|
|
@@ -5055,8 +5329,8 @@ var init_manager2 = __esm({
|
|
|
5055
5329
|
}
|
|
5056
5330
|
try {
|
|
5057
5331
|
const packageJsonPath = require3.resolve(`${definition.bundled.packageName}/package.json`);
|
|
5058
|
-
const packageRoot =
|
|
5059
|
-
const entryPath =
|
|
5332
|
+
const packageRoot = join5(packageJsonPath, "..");
|
|
5333
|
+
const entryPath = join5(packageRoot, definition.bundled.entry);
|
|
5060
5334
|
if (!existsSync3(entryPath)) {
|
|
5061
5335
|
return null;
|
|
5062
5336
|
}
|
|
@@ -5108,8 +5382,8 @@ var init_manager2 = __esm({
|
|
|
5108
5382
|
});
|
|
5109
5383
|
|
|
5110
5384
|
// packages/server/src/lsp-tools/manifest-store.ts
|
|
5111
|
-
import { existsSync as existsSync4, mkdirSync as
|
|
5112
|
-
import { join as
|
|
5385
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
5386
|
+
import { join as join6 } from "node:path";
|
|
5113
5387
|
var FileManifestStore;
|
|
5114
5388
|
var init_manifest_store = __esm({
|
|
5115
5389
|
"packages/server/src/lsp-tools/manifest-store.ts"() {
|
|
@@ -5128,29 +5402,29 @@ var init_manifest_store = __esm({
|
|
|
5128
5402
|
return null;
|
|
5129
5403
|
}
|
|
5130
5404
|
try {
|
|
5131
|
-
return JSON.parse(
|
|
5405
|
+
return JSON.parse(readFileSync4(path14, "utf8"));
|
|
5132
5406
|
} catch {
|
|
5133
5407
|
return null;
|
|
5134
5408
|
}
|
|
5135
5409
|
}
|
|
5136
5410
|
write(serverKind, manifest) {
|
|
5137
5411
|
const path14 = this.pathFor(serverKind);
|
|
5138
|
-
|
|
5139
|
-
|
|
5412
|
+
mkdirSync4(join6(this.root, serverKind), { recursive: true });
|
|
5413
|
+
writeFileSync3(path14, JSON.stringify(manifest, null, 2));
|
|
5140
5414
|
}
|
|
5141
5415
|
pathFor(serverKind) {
|
|
5142
|
-
return
|
|
5416
|
+
return join6(this.root, serverKind, "manifest.json");
|
|
5143
5417
|
}
|
|
5144
5418
|
};
|
|
5145
5419
|
}
|
|
5146
5420
|
});
|
|
5147
5421
|
|
|
5148
5422
|
// packages/server/src/lsp-tools/tool-root.ts
|
|
5149
|
-
import { mkdirSync as
|
|
5150
|
-
import { join as
|
|
5423
|
+
import { mkdirSync as mkdirSync5 } from "node:fs";
|
|
5424
|
+
import { join as join7 } from "node:path";
|
|
5151
5425
|
function resolveLspToolRoot(stateDir) {
|
|
5152
|
-
const root =
|
|
5153
|
-
|
|
5426
|
+
const root = join7(stateDir, "lsp-tools");
|
|
5427
|
+
mkdirSync5(root, { recursive: true });
|
|
5154
5428
|
return root;
|
|
5155
5429
|
}
|
|
5156
5430
|
var init_tool_root = __esm({
|
|
@@ -5160,8 +5434,8 @@ var init_tool_root = __esm({
|
|
|
5160
5434
|
});
|
|
5161
5435
|
|
|
5162
5436
|
// packages/server/src/provider-runtime/e2e-provider-mock.ts
|
|
5163
|
-
import { chmodSync, existsSync as existsSync5, mkdirSync as
|
|
5164
|
-
import { dirname as
|
|
5437
|
+
import { chmodSync, existsSync as existsSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
5438
|
+
import { dirname as dirname5, join as join8 } from "node:path";
|
|
5165
5439
|
function createE2EProviderMockOverrides(env = process.env) {
|
|
5166
5440
|
const statePath = env.CODER_STUDIO_E2E_PROVIDER_STATE_PATH;
|
|
5167
5441
|
if (!statePath) {
|
|
@@ -5246,7 +5520,7 @@ function readMockState(statePath) {
|
|
|
5246
5520
|
if (!existsSync5(statePath)) {
|
|
5247
5521
|
return {};
|
|
5248
5522
|
}
|
|
5249
|
-
const raw =
|
|
5523
|
+
const raw = readFileSync5(statePath, "utf8");
|
|
5250
5524
|
if (!raw.trim()) {
|
|
5251
5525
|
return {};
|
|
5252
5526
|
}
|
|
@@ -5261,22 +5535,22 @@ function readMockState(statePath) {
|
|
|
5261
5535
|
function writeMockState(statePath, updater) {
|
|
5262
5536
|
const nextState = readMockState(statePath);
|
|
5263
5537
|
updater(nextState);
|
|
5264
|
-
|
|
5265
|
-
|
|
5538
|
+
mkdirSync6(dirname5(statePath), { recursive: true });
|
|
5539
|
+
writeFileSync4(statePath, JSON.stringify(nextState, null, 2));
|
|
5266
5540
|
return nextState;
|
|
5267
5541
|
}
|
|
5268
5542
|
function ensureProviderCommand(binDir, providerId) {
|
|
5269
|
-
|
|
5270
|
-
const scriptPath =
|
|
5271
|
-
|
|
5543
|
+
mkdirSync6(binDir, { recursive: true });
|
|
5544
|
+
const scriptPath = join8(binDir, providerId);
|
|
5545
|
+
writeFileSync4(scriptPath, PROVIDER_COMMAND_SCRIPTS[providerId], "utf8");
|
|
5272
5546
|
chmodSync(scriptPath, 493);
|
|
5273
5547
|
}
|
|
5274
5548
|
function appendDebugLog(path14, line) {
|
|
5275
5549
|
if (!path14) {
|
|
5276
5550
|
return;
|
|
5277
5551
|
}
|
|
5278
|
-
|
|
5279
|
-
|
|
5552
|
+
mkdirSync6(dirname5(path14), { recursive: true });
|
|
5553
|
+
writeFileSync4(path14, `${line}
|
|
5280
5554
|
`, { flag: "a" });
|
|
5281
5555
|
}
|
|
5282
5556
|
var PROVIDER_INSTALL_PACKAGES, PROVIDER_COMMAND_SCRIPTS;
|
|
@@ -5311,7 +5585,7 @@ done
|
|
|
5311
5585
|
});
|
|
5312
5586
|
|
|
5313
5587
|
// packages/server/src/provider-runtime/install-manager.ts
|
|
5314
|
-
import { randomUUID as
|
|
5588
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
5315
5589
|
function getErrorDetails2(error) {
|
|
5316
5590
|
if (error instanceof Error) {
|
|
5317
5591
|
const record = error;
|
|
@@ -5451,7 +5725,7 @@ var init_install_manager2 = __esm({
|
|
|
5451
5725
|
);
|
|
5452
5726
|
if (missingProviderCommands.length === 0) {
|
|
5453
5727
|
return {
|
|
5454
|
-
jobId:
|
|
5728
|
+
jobId: randomUUID5(),
|
|
5455
5729
|
providerId: provider.id,
|
|
5456
5730
|
strategyIds: [],
|
|
5457
5731
|
status: "succeeded",
|
|
@@ -5515,7 +5789,7 @@ var init_install_manager2 = __esm({
|
|
|
5515
5789
|
}
|
|
5516
5790
|
}
|
|
5517
5791
|
}
|
|
5518
|
-
const jobId =
|
|
5792
|
+
const jobId = randomUUID5();
|
|
5519
5793
|
if (remainingPrerequisites.size > 0) {
|
|
5520
5794
|
const failedStep = this.createCheckStep(
|
|
5521
5795
|
"prerequisite",
|
|
@@ -6086,41 +6360,6 @@ var init_provider_config = __esm({
|
|
|
6086
6360
|
}
|
|
6087
6361
|
});
|
|
6088
6362
|
|
|
6089
|
-
// packages/server/src/storage/repositories/json-file-store.ts
|
|
6090
|
-
import { mkdirSync as mkdirSync6, readFileSync as readFileSync5, renameSync, rmSync, writeFileSync as writeFileSync4 } from "node:fs";
|
|
6091
|
-
import { dirname as dirname5 } from "node:path";
|
|
6092
|
-
function hasCode(error, code) {
|
|
6093
|
-
return Boolean(
|
|
6094
|
-
error && typeof error === "object" && "code" in error && error.code === code
|
|
6095
|
-
);
|
|
6096
|
-
}
|
|
6097
|
-
function readJsonFile(filePath) {
|
|
6098
|
-
try {
|
|
6099
|
-
return JSON.parse(readFileSync5(filePath, "utf-8"));
|
|
6100
|
-
} catch (error) {
|
|
6101
|
-
if (hasCode(error, "ENOENT")) {
|
|
6102
|
-
return void 0;
|
|
6103
|
-
}
|
|
6104
|
-
throw error;
|
|
6105
|
-
}
|
|
6106
|
-
}
|
|
6107
|
-
function writeJsonFileAtomic(filePath, value) {
|
|
6108
|
-
mkdirSync6(dirname5(filePath), { recursive: true });
|
|
6109
|
-
const tempPath = `${filePath}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
6110
|
-
try {
|
|
6111
|
-
writeFileSync4(tempPath, JSON.stringify(value, null, 2) + "\n", "utf-8");
|
|
6112
|
-
renameSync(tempPath, filePath);
|
|
6113
|
-
} catch (error) {
|
|
6114
|
-
rmSync(tempPath, { force: true });
|
|
6115
|
-
throw error;
|
|
6116
|
-
}
|
|
6117
|
-
}
|
|
6118
|
-
var init_json_file_store = __esm({
|
|
6119
|
-
"packages/server/src/storage/repositories/json-file-store.ts"() {
|
|
6120
|
-
"use strict";
|
|
6121
|
-
}
|
|
6122
|
-
});
|
|
6123
|
-
|
|
6124
6363
|
// packages/server/src/storage/repositories/session-repo.ts
|
|
6125
6364
|
function rowToSession(row) {
|
|
6126
6365
|
return {
|
|
@@ -6157,11 +6396,11 @@ function sessionToRow(session) {
|
|
|
6157
6396
|
title: session.title ?? null
|
|
6158
6397
|
};
|
|
6159
6398
|
}
|
|
6160
|
-
function
|
|
6399
|
+
function isRecord2(value) {
|
|
6161
6400
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
6162
6401
|
}
|
|
6163
6402
|
function isSession(value) {
|
|
6164
|
-
if (!
|
|
6403
|
+
if (!isRecord2(value)) {
|
|
6165
6404
|
return false;
|
|
6166
6405
|
}
|
|
6167
6406
|
return typeof value.id === "string" && typeof value.workspaceId === "string" && typeof value.terminalId === "string" && typeof value.providerId === "string" && typeof value.startedAt === "number" && typeof value.lastActiveAt === "number" && (value.capability === "full" || value.capability === "limited" || value.capability === "unsupported") && typeof value.state === "string";
|
|
@@ -6173,7 +6412,7 @@ function normalizeStoredSession(session) {
|
|
|
6173
6412
|
};
|
|
6174
6413
|
}
|
|
6175
6414
|
function normalizeSessionFile(value) {
|
|
6176
|
-
if (
|
|
6415
|
+
if (isRecord2(value) && value.version === 1 && isRecord2(value.sessions)) {
|
|
6177
6416
|
const normalized = {};
|
|
6178
6417
|
for (const entry of Object.values(value.sessions)) {
|
|
6179
6418
|
if (isSession(entry)) {
|
|
@@ -6191,7 +6430,7 @@ function normalizeSessionFile(value) {
|
|
|
6191
6430
|
}
|
|
6192
6431
|
return normalized;
|
|
6193
6432
|
}
|
|
6194
|
-
if (
|
|
6433
|
+
if (isRecord2(value)) {
|
|
6195
6434
|
const normalized = {};
|
|
6196
6435
|
for (const [sessionId, entry] of Object.entries(value)) {
|
|
6197
6436
|
if (isSession(entry)) {
|
|
@@ -6497,7 +6736,87 @@ var init_state_shadow_comparator = __esm({
|
|
|
6497
6736
|
function generateSessionId() {
|
|
6498
6737
|
return `sess_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
6499
6738
|
}
|
|
6500
|
-
|
|
6739
|
+
function readTerminalEscapeSequence(text, start) {
|
|
6740
|
+
const match = text.slice(start).match(TERMINAL_ESCAPE_SEQUENCE_PATTERN);
|
|
6741
|
+
return match?.[0] ?? null;
|
|
6742
|
+
}
|
|
6743
|
+
function classifyRecentInputEcho(bytes) {
|
|
6744
|
+
const text = bytes.toString("utf8");
|
|
6745
|
+
let opaque = false;
|
|
6746
|
+
let remainingVisibleText = "";
|
|
6747
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
6748
|
+
const char = text[index];
|
|
6749
|
+
if (char === "\x1B") {
|
|
6750
|
+
const escape = readTerminalEscapeSequence(text, index);
|
|
6751
|
+
if (escape) {
|
|
6752
|
+
opaque = true;
|
|
6753
|
+
index += escape.length - 1;
|
|
6754
|
+
continue;
|
|
6755
|
+
}
|
|
6756
|
+
}
|
|
6757
|
+
if (char === "\r" || char === "\n" || char === " ") {
|
|
6758
|
+
opaque = true;
|
|
6759
|
+
continue;
|
|
6760
|
+
}
|
|
6761
|
+
if (char === "\x7F" || char === "\b") {
|
|
6762
|
+
opaque = true;
|
|
6763
|
+
continue;
|
|
6764
|
+
}
|
|
6765
|
+
if (char < " ") {
|
|
6766
|
+
opaque = true;
|
|
6767
|
+
continue;
|
|
6768
|
+
}
|
|
6769
|
+
remainingVisibleText += char;
|
|
6770
|
+
}
|
|
6771
|
+
return { opaque, remainingVisibleText };
|
|
6772
|
+
}
|
|
6773
|
+
function extractVisibleTerminalText(bytes) {
|
|
6774
|
+
const text = bytes.toString("utf8");
|
|
6775
|
+
let visibleText = "";
|
|
6776
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
6777
|
+
const char = text[index];
|
|
6778
|
+
if (char === "\x1B") {
|
|
6779
|
+
const escape = readTerminalEscapeSequence(text, index);
|
|
6780
|
+
if (escape) {
|
|
6781
|
+
index += escape.length - 1;
|
|
6782
|
+
continue;
|
|
6783
|
+
}
|
|
6784
|
+
continue;
|
|
6785
|
+
}
|
|
6786
|
+
if (char === "\x7F" || char === "\b") {
|
|
6787
|
+
visibleText = visibleText.slice(0, -1);
|
|
6788
|
+
continue;
|
|
6789
|
+
}
|
|
6790
|
+
if (char === "\r" || char === "\n") {
|
|
6791
|
+
visibleText += "\n";
|
|
6792
|
+
continue;
|
|
6793
|
+
}
|
|
6794
|
+
if (char === " ") {
|
|
6795
|
+
visibleText += " ";
|
|
6796
|
+
continue;
|
|
6797
|
+
}
|
|
6798
|
+
if (char < " ") {
|
|
6799
|
+
continue;
|
|
6800
|
+
}
|
|
6801
|
+
visibleText += char;
|
|
6802
|
+
}
|
|
6803
|
+
return visibleText;
|
|
6804
|
+
}
|
|
6805
|
+
function commonPrefixLength(left, right) {
|
|
6806
|
+
const maxLength = Math.min(left.length, right.length);
|
|
6807
|
+
let index = 0;
|
|
6808
|
+
while (index < maxLength && left[index] === right[index]) {
|
|
6809
|
+
index += 1;
|
|
6810
|
+
}
|
|
6811
|
+
return index;
|
|
6812
|
+
}
|
|
6813
|
+
function chunkHasLineRepaintControl(text) {
|
|
6814
|
+
return text.includes("\r") || /\x1b\[[0-9;?]*K/.test(text) || /\x1b\[[0-9;?]*[ABCDGHF]/.test(text);
|
|
6815
|
+
}
|
|
6816
|
+
function visibleOutputHasMultipleLines(visibleOutput) {
|
|
6817
|
+
return visibleOutput.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).length > 1;
|
|
6818
|
+
}
|
|
6819
|
+
var NOOP_SESSION_LOGGER, RECENT_INPUT_ECHO_WINDOW_MS, RECENT_INPUT_ECHO_MAX_EVENTS, RECENT_INPUT_ECHO_MAX_BYTES, RECENT_OPAQUE_INPUT_ECHO_WINDOW_MS, RESUME_OUTPUT_AGGREGATION_WINDOW_MS, RESUME_OUTPUT_AGGREGATION_MAX_BYTES, TERMINAL_ESCAPE_SEQUENCE_PATTERN, SessionManager, ActiveSession;
|
|
6501
6820
|
var init_manager3 = __esm({
|
|
6502
6821
|
"packages/server/src/session/manager.ts"() {
|
|
6503
6822
|
"use strict";
|
|
@@ -6510,6 +6829,13 @@ var init_manager3 = __esm({
|
|
|
6510
6829
|
warn: () => {
|
|
6511
6830
|
}
|
|
6512
6831
|
};
|
|
6832
|
+
RECENT_INPUT_ECHO_WINDOW_MS = 3e3;
|
|
6833
|
+
RECENT_INPUT_ECHO_MAX_EVENTS = 12;
|
|
6834
|
+
RECENT_INPUT_ECHO_MAX_BYTES = 8192;
|
|
6835
|
+
RECENT_OPAQUE_INPUT_ECHO_WINDOW_MS = 200;
|
|
6836
|
+
RESUME_OUTPUT_AGGREGATION_WINDOW_MS = 75;
|
|
6837
|
+
RESUME_OUTPUT_AGGREGATION_MAX_BYTES = 4096;
|
|
6838
|
+
TERMINAL_ESCAPE_SEQUENCE_PATTERN = /^\x1b(?:\[[0-9;?<>]*[ -/]*[@-~]|\][^\x07\x1b]*(?:\x07|\x1b\\)|P[\s\S]*?\x1b\\|[@-_])/;
|
|
6513
6839
|
SessionManager = class {
|
|
6514
6840
|
constructor(deps) {
|
|
6515
6841
|
this.deps = deps;
|
|
@@ -6544,7 +6870,8 @@ var init_manager3 = __esm({
|
|
|
6544
6870
|
...cmd.env,
|
|
6545
6871
|
CODER_STUDIO_SESSION_ID: sessionId
|
|
6546
6872
|
},
|
|
6547
|
-
title: req.provider.displayName
|
|
6873
|
+
title: req.provider.displayName,
|
|
6874
|
+
themeBackground: req.themeBackground
|
|
6548
6875
|
};
|
|
6549
6876
|
const terminal = this.deps.terminalMgr.create(terminalSpec);
|
|
6550
6877
|
const active = new ActiveSession({
|
|
@@ -6686,6 +7013,7 @@ var init_manager3 = __esm({
|
|
|
6686
7013
|
this.applyTerminalInputActivity(session, activity, text, { armTurnCompletion: true });
|
|
6687
7014
|
}
|
|
6688
7015
|
applyTerminalInputActivity(session, activity, text, options) {
|
|
7016
|
+
const completedSynchronously = !options.armTurnCompletion && session.state === "idle" && !session.awaitingTurnCompletion;
|
|
6689
7017
|
if (activity === "control" || activity === "typing") {
|
|
6690
7018
|
return;
|
|
6691
7019
|
}
|
|
@@ -6694,16 +7022,10 @@ var init_manager3 = __esm({
|
|
|
6694
7022
|
session.awaitingTurnCompletion = true;
|
|
6695
7023
|
session.sawOutputSinceTurnStart = false;
|
|
6696
7024
|
}
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
session.state = "running";
|
|
6700
|
-
session.lastActiveAt = Date.now();
|
|
6701
|
-
this.deps.db.update(session.id, {
|
|
6702
|
-
state: "running",
|
|
6703
|
-
lastActiveAt: session.lastActiveAt
|
|
6704
|
-
});
|
|
6705
|
-
this.emitStateChanged(session, prev2, "running");
|
|
7025
|
+
if (completedSynchronously) {
|
|
7026
|
+
return;
|
|
6706
7027
|
}
|
|
7028
|
+
this.transitionSessionToRunning(session);
|
|
6707
7029
|
return;
|
|
6708
7030
|
}
|
|
6709
7031
|
if (activity !== "submit") return;
|
|
@@ -6716,17 +7038,11 @@ var init_manager3 = __esm({
|
|
|
6716
7038
|
session.sawOutputSinceTurnStart = false;
|
|
6717
7039
|
}
|
|
6718
7040
|
const titleChanged = this.maybeAssignTitle(session, submittedText);
|
|
6719
|
-
const prev = session.state;
|
|
6720
7041
|
const shouldResume = session.state === "idle" || session.state === "starting";
|
|
6721
|
-
if (shouldResume) {
|
|
6722
|
-
session
|
|
6723
|
-
session.lastActiveAt = Date.now();
|
|
6724
|
-
this.deps.db.update(session.id, {
|
|
6725
|
-
state: "running",
|
|
6726
|
-
lastActiveAt: session.lastActiveAt
|
|
6727
|
-
});
|
|
6728
|
-
this.emitStateChanged(session, prev, "running");
|
|
7042
|
+
if (shouldResume && !completedSynchronously) {
|
|
7043
|
+
this.transitionSessionToRunning(session);
|
|
6729
7044
|
} else if (titleChanged) {
|
|
7045
|
+
const prev = session.state;
|
|
6730
7046
|
this.emitStateChanged(session, prev, session.state);
|
|
6731
7047
|
}
|
|
6732
7048
|
}
|
|
@@ -6740,9 +7056,11 @@ var init_manager3 = __esm({
|
|
|
6740
7056
|
}
|
|
6741
7057
|
const text = activity === "submit" || activity === "internal_submit" ? submittedText ?? bytes.toString("utf-8") : void 0;
|
|
6742
7058
|
const rollbackArm = this.armTurnCompletionBeforeWrite(session, activity);
|
|
7059
|
+
const rollbackRecentInput = this.recordRecentInputBeforeWrite(session, bytes, activity);
|
|
6743
7060
|
try {
|
|
6744
7061
|
this.deps.terminalMgr.write(session.terminalId, bytes);
|
|
6745
7062
|
} catch (error) {
|
|
7063
|
+
rollbackRecentInput?.();
|
|
6746
7064
|
rollbackArm?.();
|
|
6747
7065
|
throw error;
|
|
6748
7066
|
}
|
|
@@ -6804,6 +7122,19 @@ var init_manager3 = __esm({
|
|
|
6804
7122
|
this.deps.db.update(session.id, { title });
|
|
6805
7123
|
return true;
|
|
6806
7124
|
}
|
|
7125
|
+
transitionSessionToRunning(session) {
|
|
7126
|
+
if (session.state === "running") {
|
|
7127
|
+
return;
|
|
7128
|
+
}
|
|
7129
|
+
const prev = session.state;
|
|
7130
|
+
session.state = "running";
|
|
7131
|
+
session.lastActiveAt = Date.now();
|
|
7132
|
+
this.deps.db.update(session.id, {
|
|
7133
|
+
state: "running",
|
|
7134
|
+
lastActiveAt: session.lastActiveAt
|
|
7135
|
+
});
|
|
7136
|
+
this.emitStateChanged(session, prev, "running");
|
|
7137
|
+
}
|
|
6807
7138
|
/**
|
|
6808
7139
|
* Handle terminal exit event
|
|
6809
7140
|
*/
|
|
@@ -6865,6 +7196,197 @@ var init_manager3 = __esm({
|
|
|
6865
7196
|
session.sawOutputSinceTurnStart = previousSawOutputSinceTurnStart;
|
|
6866
7197
|
};
|
|
6867
7198
|
}
|
|
7199
|
+
recordRecentInputBeforeWrite(session, bytes, activity) {
|
|
7200
|
+
if (activity === "system") {
|
|
7201
|
+
return null;
|
|
7202
|
+
}
|
|
7203
|
+
const { opaque, remainingVisibleText } = classifyRecentInputEcho(bytes);
|
|
7204
|
+
if (!opaque && remainingVisibleText.length === 0) {
|
|
7205
|
+
return null;
|
|
7206
|
+
}
|
|
7207
|
+
const recentInput = {
|
|
7208
|
+
at: Date.now(),
|
|
7209
|
+
activity,
|
|
7210
|
+
byteLength: bytes.byteLength,
|
|
7211
|
+
opaque,
|
|
7212
|
+
remainingVisibleText
|
|
7213
|
+
};
|
|
7214
|
+
session.recentInputEchoes.push(recentInput);
|
|
7215
|
+
this.pruneRecentInputEchoes(session, recentInput.at);
|
|
7216
|
+
return () => {
|
|
7217
|
+
const index = session.recentInputEchoes.indexOf(recentInput);
|
|
7218
|
+
if (index !== -1) {
|
|
7219
|
+
session.recentInputEchoes.splice(index, 1);
|
|
7220
|
+
}
|
|
7221
|
+
};
|
|
7222
|
+
}
|
|
7223
|
+
pruneRecentInputEchoes(session, now = Date.now()) {
|
|
7224
|
+
session.recentInputEchoes = session.recentInputEchoes.filter(
|
|
7225
|
+
(entry) => now - entry.at <= RECENT_INPUT_ECHO_WINDOW_MS && (entry.opaque || entry.remainingVisibleText.length > 0)
|
|
7226
|
+
);
|
|
7227
|
+
let totalBytes = session.recentInputEchoes.reduce((sum, entry) => sum + entry.byteLength, 0);
|
|
7228
|
+
while (session.recentInputEchoes.length > RECENT_INPUT_ECHO_MAX_EVENTS || totalBytes > RECENT_INPUT_ECHO_MAX_BYTES) {
|
|
7229
|
+
const removed = session.recentInputEchoes.shift();
|
|
7230
|
+
if (!removed) {
|
|
7231
|
+
break;
|
|
7232
|
+
}
|
|
7233
|
+
totalBytes -= removed.byteLength;
|
|
7234
|
+
}
|
|
7235
|
+
}
|
|
7236
|
+
clearPendingResumeAggregation(session) {
|
|
7237
|
+
if (session.pendingResumeAggregation?.timer) {
|
|
7238
|
+
clearTimeout(session.pendingResumeAggregation.timer);
|
|
7239
|
+
}
|
|
7240
|
+
session.pendingResumeAggregation = null;
|
|
7241
|
+
}
|
|
7242
|
+
consumeRecentLiteralEcho(session, visibleOutput) {
|
|
7243
|
+
let offset = 0;
|
|
7244
|
+
let matchedLiteralEcho = false;
|
|
7245
|
+
for (const entry of session.recentInputEchoes) {
|
|
7246
|
+
if (offset >= visibleOutput.length) {
|
|
7247
|
+
break;
|
|
7248
|
+
}
|
|
7249
|
+
if (entry.remainingVisibleText.length === 0) {
|
|
7250
|
+
continue;
|
|
7251
|
+
}
|
|
7252
|
+
const currentVisibleText = entry.remainingVisibleText;
|
|
7253
|
+
const matchLength = commonPrefixLength(visibleOutput.slice(offset), currentVisibleText);
|
|
7254
|
+
if (matchLength === 0) {
|
|
7255
|
+
break;
|
|
7256
|
+
}
|
|
7257
|
+
entry.remainingVisibleText = currentVisibleText.slice(matchLength);
|
|
7258
|
+
offset += matchLength;
|
|
7259
|
+
matchedLiteralEcho = true;
|
|
7260
|
+
if (matchLength < currentVisibleText.length) {
|
|
7261
|
+
break;
|
|
7262
|
+
}
|
|
7263
|
+
}
|
|
7264
|
+
return {
|
|
7265
|
+
matchedLiteralEcho,
|
|
7266
|
+
leftoverVisibleOutput: visibleOutput.slice(offset)
|
|
7267
|
+
};
|
|
7268
|
+
}
|
|
7269
|
+
hasRecentNonSubmitInput(session, now) {
|
|
7270
|
+
return session.recentInputEchoes.some(
|
|
7271
|
+
(entry) => entry.activity !== "submit" && entry.activity !== "internal_submit" && now - entry.at <= RECENT_OPAQUE_INPUT_ECHO_WINDOW_MS
|
|
7272
|
+
);
|
|
7273
|
+
}
|
|
7274
|
+
hasRecentOpaqueNonSubmitInput(session, now) {
|
|
7275
|
+
return session.recentInputEchoes.some(
|
|
7276
|
+
(entry) => entry.opaque && entry.activity !== "submit" && entry.activity !== "internal_submit" && now - entry.at <= RECENT_OPAQUE_INPUT_ECHO_WINDOW_MS
|
|
7277
|
+
);
|
|
7278
|
+
}
|
|
7279
|
+
hasRecentControlInput(session, now) {
|
|
7280
|
+
return session.recentInputEchoes.some(
|
|
7281
|
+
(entry) => entry.activity === "control" && now - entry.at <= RECENT_OPAQUE_INPUT_ECHO_WINDOW_MS
|
|
7282
|
+
);
|
|
7283
|
+
}
|
|
7284
|
+
getRecentVisibleInputText(session) {
|
|
7285
|
+
return session.recentInputEchoes.filter((entry) => entry.activity !== "submit" && entry.activity !== "internal_submit").map((entry) => entry.remainingVisibleText).join("");
|
|
7286
|
+
}
|
|
7287
|
+
isLikelyPureInputRepaint(session, chunk, visibleOutput, now) {
|
|
7288
|
+
if (!this.hasRecentNonSubmitInput(session, now)) {
|
|
7289
|
+
return false;
|
|
7290
|
+
}
|
|
7291
|
+
if (!visibleOutput.trim()) {
|
|
7292
|
+
return true;
|
|
7293
|
+
}
|
|
7294
|
+
const chunkText = chunk.toString("utf8");
|
|
7295
|
+
if (chunkText.includes("\n")) {
|
|
7296
|
+
return false;
|
|
7297
|
+
}
|
|
7298
|
+
if (visibleOutputHasMultipleLines(visibleOutput)) {
|
|
7299
|
+
return false;
|
|
7300
|
+
}
|
|
7301
|
+
if (!chunkHasLineRepaintControl(chunkText)) {
|
|
7302
|
+
return false;
|
|
7303
|
+
}
|
|
7304
|
+
if (this.hasRecentOpaqueNonSubmitInput(session, now)) {
|
|
7305
|
+
return true;
|
|
7306
|
+
}
|
|
7307
|
+
const recentVisibleInputText = this.getRecentVisibleInputText(session);
|
|
7308
|
+
const trimmedVisibleOutput = visibleOutput.trim();
|
|
7309
|
+
if (!recentVisibleInputText || !trimmedVisibleOutput) {
|
|
7310
|
+
return false;
|
|
7311
|
+
}
|
|
7312
|
+
return recentVisibleInputText === trimmedVisibleOutput || recentVisibleInputText.startsWith(trimmedVisibleOutput) || trimmedVisibleOutput.endsWith(recentVisibleInputText);
|
|
7313
|
+
}
|
|
7314
|
+
assessTerminalOutput(session, chunk) {
|
|
7315
|
+
const now = Date.now();
|
|
7316
|
+
this.pruneRecentInputEchoes(session, now);
|
|
7317
|
+
if (session.recentInputEchoes.length === 0) {
|
|
7318
|
+
const hasVisibleText = extractVisibleTerminalText(chunk).trim().length > 0;
|
|
7319
|
+
return {
|
|
7320
|
+
shouldResumeRunning: hasVisibleText,
|
|
7321
|
+
countsAsTurnOutput: hasVisibleText,
|
|
7322
|
+
shouldAggregateForResume: false
|
|
7323
|
+
};
|
|
7324
|
+
}
|
|
7325
|
+
const visibleOutput = extractVisibleTerminalText(chunk);
|
|
7326
|
+
const { matchedLiteralEcho, leftoverVisibleOutput } = this.consumeRecentLiteralEcho(
|
|
7327
|
+
session,
|
|
7328
|
+
visibleOutput
|
|
7329
|
+
);
|
|
7330
|
+
const hasRecentNonSubmitInput = this.hasRecentNonSubmitInput(session, now);
|
|
7331
|
+
const hasRecentControlInput = this.hasRecentControlInput(session, now);
|
|
7332
|
+
this.pruneRecentInputEchoes(session, now);
|
|
7333
|
+
const trimmedLeftoverVisibleOutput = leftoverVisibleOutput.trim();
|
|
7334
|
+
const trimmedVisibleOutput = visibleOutput.trim();
|
|
7335
|
+
const suppressImmediateResumeAfterControl = hasRecentControlInput && !matchedLiteralEcho;
|
|
7336
|
+
const chunkText = chunk.toString("utf8");
|
|
7337
|
+
const shouldAggregateForResume = session.state === "idle" && hasRecentNonSubmitInput && !matchedLiteralEcho && trimmedVisibleOutput.length > 0 && chunkHasLineRepaintControl(chunkText) && !visibleOutputHasMultipleLines(visibleOutput);
|
|
7338
|
+
const shouldResumeRunning = suppressImmediateResumeAfterControl ? false : trimmedLeftoverVisibleOutput.length > 0 ? !this.isLikelyPureInputRepaint(session, chunk, leftoverVisibleOutput, now) : !matchedLiteralEcho && trimmedVisibleOutput.length > 0 && !this.isLikelyPureInputRepaint(session, chunk, visibleOutput, now);
|
|
7339
|
+
const countsAsTurnOutput = !suppressImmediateResumeAfterControl && (trimmedLeftoverVisibleOutput.length > 0 ? !this.isLikelyPureInputRepaint(session, chunk, leftoverVisibleOutput, now) : !matchedLiteralEcho && trimmedVisibleOutput.length > 0 && !this.isLikelyPureInputRepaint(session, chunk, visibleOutput, now));
|
|
7340
|
+
return {
|
|
7341
|
+
shouldResumeRunning,
|
|
7342
|
+
countsAsTurnOutput,
|
|
7343
|
+
shouldAggregateForResume
|
|
7344
|
+
};
|
|
7345
|
+
}
|
|
7346
|
+
flushPendingResumeAggregation(session) {
|
|
7347
|
+
const pending = session.pendingResumeAggregation;
|
|
7348
|
+
if (!pending) {
|
|
7349
|
+
return;
|
|
7350
|
+
}
|
|
7351
|
+
this.clearPendingResumeAggregation(session);
|
|
7352
|
+
if (session.state !== "idle") {
|
|
7353
|
+
return;
|
|
7354
|
+
}
|
|
7355
|
+
const combinedChunk = Buffer.concat(pending.chunks, pending.byteLength);
|
|
7356
|
+
const outputAssessment = this.assessTerminalOutput(session, combinedChunk);
|
|
7357
|
+
if (outputAssessment.countsAsTurnOutput) {
|
|
7358
|
+
session.sawOutputSinceTurnStart = true;
|
|
7359
|
+
}
|
|
7360
|
+
}
|
|
7361
|
+
schedulePendingResumeAggregation(session, chunk) {
|
|
7362
|
+
const pending = session.pendingResumeAggregation;
|
|
7363
|
+
if (!pending) {
|
|
7364
|
+
const nextPending = {
|
|
7365
|
+
startedAt: Date.now(),
|
|
7366
|
+
chunks: [chunk],
|
|
7367
|
+
byteLength: chunk.byteLength,
|
|
7368
|
+
timer: null
|
|
7369
|
+
};
|
|
7370
|
+
nextPending.timer = setTimeout(() => {
|
|
7371
|
+
const activeSession = this.sessions.get(session.id);
|
|
7372
|
+
if (!activeSession) {
|
|
7373
|
+
return;
|
|
7374
|
+
}
|
|
7375
|
+
this.flushPendingResumeAggregation(activeSession);
|
|
7376
|
+
}, RESUME_OUTPUT_AGGREGATION_WINDOW_MS);
|
|
7377
|
+
session.pendingResumeAggregation = nextPending;
|
|
7378
|
+
return;
|
|
7379
|
+
}
|
|
7380
|
+
pending.chunks.push(chunk);
|
|
7381
|
+
pending.byteLength += chunk.byteLength;
|
|
7382
|
+
const combinedChunk = Buffer.concat(pending.chunks, pending.byteLength);
|
|
7383
|
+
const visibleOutput = extractVisibleTerminalText(combinedChunk);
|
|
7384
|
+
const combinedText = combinedChunk.toString("utf8");
|
|
7385
|
+
const shouldFlushNow = pending.byteLength >= RESUME_OUTPUT_AGGREGATION_MAX_BYTES || combinedText.includes("\n") || visibleOutputHasMultipleLines(visibleOutput);
|
|
7386
|
+
if (shouldFlushNow) {
|
|
7387
|
+
this.flushPendingResumeAggregation(session);
|
|
7388
|
+
}
|
|
7389
|
+
}
|
|
6868
7390
|
flushPendingPtyIdle(session) {
|
|
6869
7391
|
const ptyState = this.comparators.get(session.id)?.snapshot().ptyState;
|
|
6870
7392
|
if (ptyState !== "idle") {
|
|
@@ -6873,6 +7395,7 @@ var init_manager3 = __esm({
|
|
|
6873
7395
|
this.transitionSessionToIdle(session);
|
|
6874
7396
|
}
|
|
6875
7397
|
transitionSessionToIdle(activeSession) {
|
|
7398
|
+
this.clearPendingResumeAggregation(activeSession);
|
|
6876
7399
|
const prev = activeSession.state;
|
|
6877
7400
|
if (prev !== "running" && prev !== "starting") {
|
|
6878
7401
|
return;
|
|
@@ -6935,7 +7458,24 @@ var init_manager3 = __esm({
|
|
|
6935
7458
|
return;
|
|
6936
7459
|
}
|
|
6937
7460
|
const activeSession = this.sessions.get(session.id);
|
|
6938
|
-
if (activeSession
|
|
7461
|
+
if (!activeSession) {
|
|
7462
|
+
return;
|
|
7463
|
+
}
|
|
7464
|
+
if (activeSession.pendingResumeAggregation && activeSession.state !== "idle") {
|
|
7465
|
+
this.clearPendingResumeAggregation(activeSession);
|
|
7466
|
+
}
|
|
7467
|
+
if (activeSession.pendingResumeAggregation) {
|
|
7468
|
+
this.schedulePendingResumeAggregation(activeSession, event.chunk);
|
|
7469
|
+
detector.feed(event.chunk);
|
|
7470
|
+
return;
|
|
7471
|
+
}
|
|
7472
|
+
const outputAssessment = this.assessTerminalOutput(activeSession, event.chunk);
|
|
7473
|
+
if (outputAssessment.shouldAggregateForResume) {
|
|
7474
|
+
this.schedulePendingResumeAggregation(activeSession, event.chunk);
|
|
7475
|
+
detector.feed(event.chunk);
|
|
7476
|
+
return;
|
|
7477
|
+
}
|
|
7478
|
+
if (outputAssessment.countsAsTurnOutput) {
|
|
6939
7479
|
activeSession.sawOutputSinceTurnStart = true;
|
|
6940
7480
|
}
|
|
6941
7481
|
detector.feed(event.chunk);
|
|
@@ -6952,6 +7492,7 @@ var init_manager3 = __esm({
|
|
|
6952
7492
|
this.comparators.delete(sessionId);
|
|
6953
7493
|
}
|
|
6954
7494
|
finishSession(session, exitCode) {
|
|
7495
|
+
this.clearPendingResumeAggregation(session);
|
|
6955
7496
|
const prev = session.state;
|
|
6956
7497
|
session.state = "ended";
|
|
6957
7498
|
session.endedAt = Date.now();
|
|
@@ -6983,6 +7524,8 @@ var init_manager3 = __esm({
|
|
|
6983
7524
|
latestSubmittedUserInput;
|
|
6984
7525
|
awaitingTurnCompletion = false;
|
|
6985
7526
|
sawOutputSinceTurnStart = false;
|
|
7527
|
+
recentInputEchoes = [];
|
|
7528
|
+
pendingResumeAggregation = null;
|
|
6986
7529
|
constructor(data) {
|
|
6987
7530
|
this.id = data.id;
|
|
6988
7531
|
this.workspaceId = data.workspaceId;
|
|
@@ -7025,17 +7568,17 @@ var init_manager3 = __esm({
|
|
|
7025
7568
|
});
|
|
7026
7569
|
|
|
7027
7570
|
// packages/server/src/storage/repositories/auth-login-block-repo.ts
|
|
7028
|
-
function
|
|
7571
|
+
function isRecord3(value) {
|
|
7029
7572
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7030
7573
|
}
|
|
7031
7574
|
function isAuthLoginBlockRecord(value) {
|
|
7032
|
-
if (!
|
|
7575
|
+
if (!isRecord3(value)) {
|
|
7033
7576
|
return false;
|
|
7034
7577
|
}
|
|
7035
7578
|
return typeof value.ip === "string" && typeof value.failedCount === "number" && typeof value.firstFailedAt === "number" && typeof value.lastFailedAt === "number" && (typeof value.blockedUntil === "number" || value.blockedUntil === null);
|
|
7036
7579
|
}
|
|
7037
7580
|
function normalizeFailures(value) {
|
|
7038
|
-
if (!
|
|
7581
|
+
if (!isRecord3(value)) {
|
|
7039
7582
|
return {};
|
|
7040
7583
|
}
|
|
7041
7584
|
const normalized = {};
|
|
@@ -7049,9 +7592,9 @@ function normalizeFailures(value) {
|
|
|
7049
7592
|
return normalized;
|
|
7050
7593
|
}
|
|
7051
7594
|
function normalizeState(value) {
|
|
7052
|
-
if (
|
|
7595
|
+
if (isRecord3(value) && value.version === 1) {
|
|
7053
7596
|
const blocks = {};
|
|
7054
|
-
if (
|
|
7597
|
+
if (isRecord3(value.blocks)) {
|
|
7055
7598
|
for (const entry of Object.values(value.blocks)) {
|
|
7056
7599
|
if (isAuthLoginBlockRecord(entry)) {
|
|
7057
7600
|
blocks[entry.ip] = entry;
|
|
@@ -7064,7 +7607,7 @@ function normalizeState(value) {
|
|
|
7064
7607
|
failures: normalizeFailures(value.failures)
|
|
7065
7608
|
};
|
|
7066
7609
|
}
|
|
7067
|
-
if (
|
|
7610
|
+
if (isRecord3(value)) {
|
|
7068
7611
|
const blocks = {};
|
|
7069
7612
|
for (const [ip, entry] of Object.entries(value)) {
|
|
7070
7613
|
if (isAuthLoginBlockRecord(entry)) {
|
|
@@ -7170,17 +7713,17 @@ var init_auth_login_block_repo = __esm({
|
|
|
7170
7713
|
});
|
|
7171
7714
|
|
|
7172
7715
|
// packages/server/src/storage/repositories/auth-session-repo.ts
|
|
7173
|
-
function
|
|
7716
|
+
function isRecord4(value) {
|
|
7174
7717
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7175
7718
|
}
|
|
7176
7719
|
function isAuthSession(value) {
|
|
7177
|
-
if (!
|
|
7720
|
+
if (!isRecord4(value)) {
|
|
7178
7721
|
return false;
|
|
7179
7722
|
}
|
|
7180
7723
|
return typeof value.token === "string" && typeof value.createdAt === "number" && typeof value.lastSeenAt === "number";
|
|
7181
7724
|
}
|
|
7182
7725
|
function normalizeSessionFile2(value) {
|
|
7183
|
-
if (
|
|
7726
|
+
if (isRecord4(value) && value.version === 1 && isRecord4(value.sessions)) {
|
|
7184
7727
|
const normalized = {};
|
|
7185
7728
|
for (const entry of Object.values(value.sessions)) {
|
|
7186
7729
|
if (isAuthSession(entry)) {
|
|
@@ -7198,7 +7741,7 @@ function normalizeSessionFile2(value) {
|
|
|
7198
7741
|
}
|
|
7199
7742
|
return normalized;
|
|
7200
7743
|
}
|
|
7201
|
-
if (
|
|
7744
|
+
if (isRecord4(value)) {
|
|
7202
7745
|
const normalized = {};
|
|
7203
7746
|
for (const [token, entry] of Object.entries(value)) {
|
|
7204
7747
|
if (isAuthSession(entry)) {
|
|
@@ -7273,14 +7816,14 @@ var init_auth_session_repo = __esm({
|
|
|
7273
7816
|
});
|
|
7274
7817
|
|
|
7275
7818
|
// packages/server/src/storage/repositories/provider-config-repo.ts
|
|
7276
|
-
function
|
|
7819
|
+
function isRecord5(value) {
|
|
7277
7820
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7278
7821
|
}
|
|
7279
7822
|
function normalizeProviderConfigFile(value) {
|
|
7280
|
-
if (
|
|
7823
|
+
if (isRecord5(value) && value.version === 1 && isRecord5(value.providers)) {
|
|
7281
7824
|
return value.providers;
|
|
7282
7825
|
}
|
|
7283
|
-
if (
|
|
7826
|
+
if (isRecord5(value)) {
|
|
7284
7827
|
return value;
|
|
7285
7828
|
}
|
|
7286
7829
|
return {};
|
|
@@ -7354,14 +7897,14 @@ var init_provider_config_repo = __esm({
|
|
|
7354
7897
|
});
|
|
7355
7898
|
|
|
7356
7899
|
// packages/server/src/storage/repositories/settings-repo.ts
|
|
7357
|
-
function
|
|
7900
|
+
function isRecord6(value) {
|
|
7358
7901
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7359
7902
|
}
|
|
7360
7903
|
function normalizeSettingsFile(value) {
|
|
7361
|
-
if (
|
|
7904
|
+
if (isRecord6(value) && value.version === 1 && isRecord6(value.settings)) {
|
|
7362
7905
|
return { ...value.settings };
|
|
7363
7906
|
}
|
|
7364
|
-
if (
|
|
7907
|
+
if (isRecord6(value)) {
|
|
7365
7908
|
return { ...value };
|
|
7366
7909
|
}
|
|
7367
7910
|
return {};
|
|
@@ -7512,11 +8055,11 @@ var init_supervisor_repo = __esm({
|
|
|
7512
8055
|
});
|
|
7513
8056
|
|
|
7514
8057
|
// packages/server/src/storage/repositories/terminal-repo.ts
|
|
7515
|
-
function
|
|
8058
|
+
function isRecord7(value) {
|
|
7516
8059
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7517
8060
|
}
|
|
7518
8061
|
function isTerminal(value) {
|
|
7519
|
-
if (!
|
|
8062
|
+
if (!isRecord7(value)) {
|
|
7520
8063
|
return false;
|
|
7521
8064
|
}
|
|
7522
8065
|
return typeof value.id === "string" && typeof value.workspaceId === "string" && (value.kind === "agent" || value.kind === "shell") && typeof value.cwd === "string" && Array.isArray(value.argv) && typeof value.cols === "number" && typeof value.rows === "number" && typeof value.alive === "boolean" && typeof value.createdAt === "number";
|
|
@@ -7528,7 +8071,7 @@ function normalizeTerminal(value) {
|
|
|
7528
8071
|
};
|
|
7529
8072
|
}
|
|
7530
8073
|
function normalizeTerminalFile(value) {
|
|
7531
|
-
if (
|
|
8074
|
+
if (isRecord7(value) && value.version === 1 && isRecord7(value.terminals)) {
|
|
7532
8075
|
const normalized = {};
|
|
7533
8076
|
for (const entry of Object.values(value.terminals)) {
|
|
7534
8077
|
if (isTerminal(entry)) {
|
|
@@ -7546,7 +8089,7 @@ function normalizeTerminalFile(value) {
|
|
|
7546
8089
|
}
|
|
7547
8090
|
return normalized;
|
|
7548
8091
|
}
|
|
7549
|
-
if (
|
|
8092
|
+
if (isRecord7(value)) {
|
|
7550
8093
|
const normalized = {};
|
|
7551
8094
|
for (const [terminalId, entry] of Object.entries(value)) {
|
|
7552
8095
|
if (isTerminal(entry)) {
|
|
@@ -7712,12 +8255,12 @@ var init_terminal_repo = __esm({
|
|
|
7712
8255
|
});
|
|
7713
8256
|
|
|
7714
8257
|
// packages/server/src/storage/repositories/update-state-repo.ts
|
|
7715
|
-
function
|
|
8258
|
+
function isRecord8(value) {
|
|
7716
8259
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7717
8260
|
}
|
|
7718
8261
|
function normalizeUpdateState(value, currentVersion) {
|
|
7719
8262
|
const defaults = createDefaultUpdateState(currentVersion);
|
|
7720
|
-
if (!
|
|
8263
|
+
if (!isRecord8(value)) {
|
|
7721
8264
|
return defaults;
|
|
7722
8265
|
}
|
|
7723
8266
|
return {
|
|
@@ -7780,17 +8323,17 @@ var init_update_state_repo = __esm({
|
|
|
7780
8323
|
});
|
|
7781
8324
|
|
|
7782
8325
|
// packages/server/src/storage/repositories/workspace-repo.ts
|
|
7783
|
-
function
|
|
8326
|
+
function isRecord9(value) {
|
|
7784
8327
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7785
8328
|
}
|
|
7786
8329
|
function isWorkspace(value) {
|
|
7787
|
-
if (!
|
|
8330
|
+
if (!isRecord9(value)) {
|
|
7788
8331
|
return false;
|
|
7789
8332
|
}
|
|
7790
|
-
return typeof value.id === "string" && typeof value.path === "string" && (value.targetRuntime === "native" || value.targetRuntime === "wsl") && typeof value.openedAt === "number" && typeof value.lastActiveAt === "number" &&
|
|
8333
|
+
return typeof value.id === "string" && typeof value.path === "string" && (value.targetRuntime === "native" || value.targetRuntime === "wsl") && typeof value.openedAt === "number" && typeof value.lastActiveAt === "number" && isRecord9(value.uiState);
|
|
7791
8334
|
}
|
|
7792
8335
|
function normalizeWorkspaceFile(value) {
|
|
7793
|
-
if (
|
|
8336
|
+
if (isRecord9(value) && value.version === 1 && isRecord9(value.workspaces)) {
|
|
7794
8337
|
const normalized = {};
|
|
7795
8338
|
for (const entry of Object.values(value.workspaces)) {
|
|
7796
8339
|
if (isWorkspace(entry)) {
|
|
@@ -7808,7 +8351,7 @@ function normalizeWorkspaceFile(value) {
|
|
|
7808
8351
|
}
|
|
7809
8352
|
return normalized;
|
|
7810
8353
|
}
|
|
7811
|
-
if (
|
|
8354
|
+
if (isRecord9(value)) {
|
|
7812
8355
|
const normalized = {};
|
|
7813
8356
|
for (const [workspaceId, entry] of Object.entries(value)) {
|
|
7814
8357
|
if (isWorkspace(entry)) {
|
|
@@ -8053,13 +8596,13 @@ function ensureNodePtySpawnHelperExecutable(deps = {}) {
|
|
|
8053
8596
|
return;
|
|
8054
8597
|
}
|
|
8055
8598
|
const arch = deps.arch ?? process.arch;
|
|
8056
|
-
const
|
|
8599
|
+
const resolve6 = deps.resolve ?? ((id) => require4.resolve(id));
|
|
8057
8600
|
const fileExists = deps.existsSync ?? existsSync6;
|
|
8058
|
-
const
|
|
8601
|
+
const stat11 = deps.statSync ?? statSync;
|
|
8059
8602
|
const chmod = deps.chmodSync ?? chmodSync2;
|
|
8060
8603
|
let packageJsonPath;
|
|
8061
8604
|
try {
|
|
8062
|
-
packageJsonPath =
|
|
8605
|
+
packageJsonPath = resolve6(NODE_PTY_PKG);
|
|
8063
8606
|
} catch {
|
|
8064
8607
|
return;
|
|
8065
8608
|
}
|
|
@@ -8073,7 +8616,7 @@ function ensureNodePtySpawnHelperExecutable(deps = {}) {
|
|
|
8073
8616
|
if (!fileExists(helperPath)) {
|
|
8074
8617
|
return;
|
|
8075
8618
|
}
|
|
8076
|
-
const currentMode =
|
|
8619
|
+
const currentMode = stat11(helperPath).mode;
|
|
8077
8620
|
const executableMode = currentMode | 73;
|
|
8078
8621
|
if (executableMode === currentMode) {
|
|
8079
8622
|
return;
|
|
@@ -8124,7 +8667,7 @@ async function escalateKillWithPolling(pid, signal, options) {
|
|
|
8124
8667
|
const startTime = Date.now();
|
|
8125
8668
|
const deadline = startTime + timeoutMs;
|
|
8126
8669
|
while (Date.now() < deadline) {
|
|
8127
|
-
await new Promise((
|
|
8670
|
+
await new Promise((resolve6) => setTimeout(resolve6, pollIntervalMs));
|
|
8128
8671
|
if (!isProcessAlive(pid)) {
|
|
8129
8672
|
return true;
|
|
8130
8673
|
}
|
|
@@ -8488,7 +9031,7 @@ async function runCommand(command, timeoutMs, options = {}) {
|
|
|
8488
9031
|
if (options.signal?.aborted) {
|
|
8489
9032
|
throw createSupervisorEvalAbortedError();
|
|
8490
9033
|
}
|
|
8491
|
-
return await new Promise((
|
|
9034
|
+
return await new Promise((resolve6, reject) => {
|
|
8492
9035
|
const child = spawn3(command.argv[0], command.argv.slice(1), {
|
|
8493
9036
|
cwd: command.cwd,
|
|
8494
9037
|
detached: process.platform !== "win32",
|
|
@@ -8518,7 +9061,7 @@ async function runCommand(command, timeoutMs, options = {}) {
|
|
|
8518
9061
|
}
|
|
8519
9062
|
settled = true;
|
|
8520
9063
|
cleanup();
|
|
8521
|
-
|
|
9064
|
+
resolve6(value);
|
|
8522
9065
|
};
|
|
8523
9066
|
const terminate = (error) => {
|
|
8524
9067
|
if (terminationError) {
|
|
@@ -9346,12 +9889,12 @@ var init_scheduler = __esm({
|
|
|
9346
9889
|
|
|
9347
9890
|
// packages/server/src/supervisor/manager.ts
|
|
9348
9891
|
function createDeferredCompletion() {
|
|
9349
|
-
let
|
|
9892
|
+
let resolve6 = () => {
|
|
9350
9893
|
};
|
|
9351
9894
|
const promise = new Promise((innerResolve) => {
|
|
9352
|
-
|
|
9895
|
+
resolve6 = innerResolve;
|
|
9353
9896
|
});
|
|
9354
|
-
return { promise, resolve:
|
|
9897
|
+
return { promise, resolve: resolve6 };
|
|
9355
9898
|
}
|
|
9356
9899
|
function generateSupervisorId() {
|
|
9357
9900
|
return `sup_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
@@ -10706,10 +11249,10 @@ var init_manager4 = __esm({
|
|
|
10706
11249
|
if (signal?.aborted) {
|
|
10707
11250
|
throw { code: "supervisor_eval_aborted", message: "Supervisor evaluator aborted" };
|
|
10708
11251
|
}
|
|
10709
|
-
await new Promise((
|
|
11252
|
+
await new Promise((resolve6, reject) => {
|
|
10710
11253
|
const timer = setTimeout(() => {
|
|
10711
11254
|
signal?.removeEventListener("abort", onAbort);
|
|
10712
|
-
|
|
11255
|
+
resolve6();
|
|
10713
11256
|
}, delayMs);
|
|
10714
11257
|
timer.unref?.();
|
|
10715
11258
|
const onAbort = () => {
|
|
@@ -10740,31 +11283,31 @@ __export(target_store_exports, {
|
|
|
10740
11283
|
saveTargetMemory: () => saveTargetMemory,
|
|
10741
11284
|
saveTargetMeta: () => saveTargetMeta
|
|
10742
11285
|
});
|
|
10743
|
-
import { mkdir as mkdir4, mkdtemp as mkdtemp2, readdir as readdir2, readFile as readFile3, rename, rm as
|
|
10744
|
-
import { dirname as dirname6, join as
|
|
11286
|
+
import { mkdir as mkdir4, mkdtemp as mkdtemp2, readdir as readdir2, readFile as readFile3, rename, rm as rm6, writeFile as writeFile4 } from "node:fs/promises";
|
|
11287
|
+
import { dirname as dirname6, join as join9 } from "node:path";
|
|
10745
11288
|
function targetDir(workspacePath, targetId) {
|
|
10746
|
-
return
|
|
11289
|
+
return join9(workspacePath, ".coder-studio", "supervisor", "targets", targetId);
|
|
10747
11290
|
}
|
|
10748
11291
|
function metaPath(workspacePath, targetId) {
|
|
10749
|
-
return
|
|
11292
|
+
return join9(targetDir(workspacePath, targetId), "meta.json");
|
|
10750
11293
|
}
|
|
10751
11294
|
function memoryPath(workspacePath, targetId) {
|
|
10752
|
-
return
|
|
11295
|
+
return join9(targetDir(workspacePath, targetId), "memory.json");
|
|
10753
11296
|
}
|
|
10754
11297
|
function cyclesPath(workspacePath, targetId) {
|
|
10755
|
-
return
|
|
11298
|
+
return join9(targetDir(workspacePath, targetId), "cycles.jsonl");
|
|
10756
11299
|
}
|
|
10757
11300
|
function targetsRoot(workspacePath) {
|
|
10758
|
-
return
|
|
11301
|
+
return join9(workspacePath, ".coder-studio", "supervisor", "targets");
|
|
10759
11302
|
}
|
|
10760
11303
|
function metaFilePath(dirPath) {
|
|
10761
|
-
return
|
|
11304
|
+
return join9(dirPath, "meta.json");
|
|
10762
11305
|
}
|
|
10763
11306
|
function memoryFilePath(dirPath) {
|
|
10764
|
-
return
|
|
11307
|
+
return join9(dirPath, "memory.json");
|
|
10765
11308
|
}
|
|
10766
11309
|
function cyclesFilePath(dirPath) {
|
|
10767
|
-
return
|
|
11310
|
+
return join9(dirPath, "cycles.jsonl");
|
|
10768
11311
|
}
|
|
10769
11312
|
function hasCode2(error, code) {
|
|
10770
11313
|
return Boolean(
|
|
@@ -10783,7 +11326,7 @@ function errorMessage(error, fallback) {
|
|
|
10783
11326
|
}
|
|
10784
11327
|
return fallback;
|
|
10785
11328
|
}
|
|
10786
|
-
function
|
|
11329
|
+
function isRecord10(value) {
|
|
10787
11330
|
return Boolean(value) && typeof value === "object";
|
|
10788
11331
|
}
|
|
10789
11332
|
function readNonEmptyString(value) {
|
|
@@ -10827,7 +11370,7 @@ function fallbackAcceptanceCriteria(title) {
|
|
|
10827
11370
|
return [`${title} is complete`];
|
|
10828
11371
|
}
|
|
10829
11372
|
function normalizeItem(value, fallbackKind) {
|
|
10830
|
-
if (!
|
|
11373
|
+
if (!isRecord10(value)) {
|
|
10831
11374
|
return null;
|
|
10832
11375
|
}
|
|
10833
11376
|
const id = readNonEmptyString(value.id);
|
|
@@ -10858,7 +11401,7 @@ function normalizeLegacyPlanItems(plan) {
|
|
|
10858
11401
|
}
|
|
10859
11402
|
return plan.flatMap((value) => {
|
|
10860
11403
|
const item = normalizeItem(
|
|
10861
|
-
|
|
11404
|
+
isRecord10(value) ? {
|
|
10862
11405
|
id: value.id,
|
|
10863
11406
|
kind: "stage",
|
|
10864
11407
|
title: value.title,
|
|
@@ -10882,7 +11425,7 @@ function resolveActiveItemId(items, candidate) {
|
|
|
10882
11425
|
return items.find((item) => item.status === "in_progress")?.id ?? items.find((item) => item.status === "pending")?.id ?? items[0]?.id;
|
|
10883
11426
|
}
|
|
10884
11427
|
function normalizeTargetMemory(raw, targetId) {
|
|
10885
|
-
if (!
|
|
11428
|
+
if (!isRecord10(raw)) {
|
|
10886
11429
|
return buildTargetMemory(targetId, 0);
|
|
10887
11430
|
}
|
|
10888
11431
|
const updatedAt = readTimestamp(raw.updatedAt, 0);
|
|
@@ -10909,7 +11452,7 @@ function normalizeTargetMemory(raw, targetId) {
|
|
|
10909
11452
|
};
|
|
10910
11453
|
}
|
|
10911
11454
|
function normalizePersistedSupervisor(raw, fallback) {
|
|
10912
|
-
if (!
|
|
11455
|
+
if (!isRecord10(raw)) {
|
|
10913
11456
|
return void 0;
|
|
10914
11457
|
}
|
|
10915
11458
|
const id = readNonEmptyString(raw.id) ?? fallback.targetId;
|
|
@@ -10945,7 +11488,7 @@ function normalizePersistedSupervisor(raw, fallback) {
|
|
|
10945
11488
|
};
|
|
10946
11489
|
}
|
|
10947
11490
|
function normalizeTargetMeta(raw, fallbackTargetId) {
|
|
10948
|
-
if (!
|
|
11491
|
+
if (!isRecord10(raw)) {
|
|
10949
11492
|
const targetId2 = fallbackTargetId ?? "";
|
|
10950
11493
|
return {
|
|
10951
11494
|
targetId: targetId2,
|
|
@@ -11066,7 +11609,7 @@ async function resetTargetFiles(workspacePath, input) {
|
|
|
11066
11609
|
const parentDir = dirname6(dir);
|
|
11067
11610
|
const backupDir = `${dir}.backup-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
11068
11611
|
await mkdir4(parentDir, { recursive: true });
|
|
11069
|
-
const stagingDir = await mkdtemp2(
|
|
11612
|
+
const stagingDir = await mkdtemp2(join9(parentDir, `${input.targetId}.reset-`));
|
|
11070
11613
|
let backupCreated = false;
|
|
11071
11614
|
let promoted = false;
|
|
11072
11615
|
let restored = false;
|
|
@@ -11102,17 +11645,17 @@ async function resetTargetFiles(workspacePath, input) {
|
|
|
11102
11645
|
}
|
|
11103
11646
|
} catch (error) {
|
|
11104
11647
|
if (restored || !backupCreated) {
|
|
11105
|
-
await
|
|
11648
|
+
await rm6(stagingDir, { recursive: true, force: true }).catch(() => {
|
|
11106
11649
|
});
|
|
11107
11650
|
}
|
|
11108
11651
|
throw error;
|
|
11109
11652
|
}
|
|
11110
11653
|
if (backupCreated) {
|
|
11111
|
-
await
|
|
11654
|
+
await rm6(backupDir, { recursive: true, force: true }).catch(() => {
|
|
11112
11655
|
});
|
|
11113
11656
|
}
|
|
11114
11657
|
if (!promoted) {
|
|
11115
|
-
await
|
|
11658
|
+
await rm6(stagingDir, { recursive: true, force: true }).catch(() => {
|
|
11116
11659
|
});
|
|
11117
11660
|
}
|
|
11118
11661
|
}
|
|
@@ -11210,7 +11753,7 @@ async function cloneTargetFiles(workspacePath, input) {
|
|
|
11210
11753
|
return nextCycles.filter(countsTowardCycleTotal).length;
|
|
11211
11754
|
}
|
|
11212
11755
|
async function deleteTarget(workspacePath, targetId) {
|
|
11213
|
-
await
|
|
11756
|
+
await rm6(targetDir(workspacePath, targetId), { recursive: true, force: true });
|
|
11214
11757
|
}
|
|
11215
11758
|
async function markTargetSuperseded(workspacePath, targetId, nextTargetId, updatedAt) {
|
|
11216
11759
|
const meta = await readTargetMeta(workspacePath, targetId);
|
|
@@ -11523,8 +12066,8 @@ var init_terminal_snapshot_buffer = __esm({
|
|
|
11523
12066
|
if (this.pendingWriteCount === 0) {
|
|
11524
12067
|
return Promise.resolve();
|
|
11525
12068
|
}
|
|
11526
|
-
return new Promise((
|
|
11527
|
-
this.drainResolvers.push(
|
|
12069
|
+
return new Promise((resolve6) => {
|
|
12070
|
+
this.drainResolvers.push(resolve6);
|
|
11528
12071
|
});
|
|
11529
12072
|
}
|
|
11530
12073
|
resolveDrainIfIdle() {
|
|
@@ -11533,8 +12076,8 @@ var init_terminal_snapshot_buffer = __esm({
|
|
|
11533
12076
|
}
|
|
11534
12077
|
const resolvers = this.drainResolvers;
|
|
11535
12078
|
this.drainResolvers = [];
|
|
11536
|
-
for (const
|
|
11537
|
-
|
|
12079
|
+
for (const resolve6 of resolvers) {
|
|
12080
|
+
resolve6();
|
|
11538
12081
|
}
|
|
11539
12082
|
}
|
|
11540
12083
|
requireTerminal() {
|
|
@@ -11588,6 +12131,39 @@ var init_types2 = __esm({
|
|
|
11588
12131
|
function isTerminalTraceEnabled() {
|
|
11589
12132
|
return process.env.CODER_STUDIO_TERMINAL_TRACE === "1";
|
|
11590
12133
|
}
|
|
12134
|
+
function parseHexColor(input) {
|
|
12135
|
+
const trimmed = input.trim();
|
|
12136
|
+
if (!trimmed.startsWith("#")) {
|
|
12137
|
+
return null;
|
|
12138
|
+
}
|
|
12139
|
+
const hex = trimmed.slice(1);
|
|
12140
|
+
let r;
|
|
12141
|
+
let g;
|
|
12142
|
+
let b;
|
|
12143
|
+
if (hex.length === 3) {
|
|
12144
|
+
r = Number.parseInt(hex[0] + hex[0], 16);
|
|
12145
|
+
g = Number.parseInt(hex[1] + hex[1], 16);
|
|
12146
|
+
b = Number.parseInt(hex[2] + hex[2], 16);
|
|
12147
|
+
} else if (hex.length === 6 || hex.length === 8) {
|
|
12148
|
+
r = Number.parseInt(hex.slice(0, 2), 16);
|
|
12149
|
+
g = Number.parseInt(hex.slice(2, 4), 16);
|
|
12150
|
+
b = Number.parseInt(hex.slice(4, 6), 16);
|
|
12151
|
+
} else {
|
|
12152
|
+
return null;
|
|
12153
|
+
}
|
|
12154
|
+
if ([r, g, b].some((v) => Number.isNaN(v))) {
|
|
12155
|
+
return null;
|
|
12156
|
+
}
|
|
12157
|
+
return { r, g, b };
|
|
12158
|
+
}
|
|
12159
|
+
function computeColorFgBg(background) {
|
|
12160
|
+
const rgb = parseHexColor(background);
|
|
12161
|
+
if (!rgb) {
|
|
12162
|
+
return void 0;
|
|
12163
|
+
}
|
|
12164
|
+
const luma = (0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b) / 255;
|
|
12165
|
+
return luma > 0.5 ? "0;15" : "15;0";
|
|
12166
|
+
}
|
|
11591
12167
|
function countOccurrences(text, needle) {
|
|
11592
12168
|
return text.split(needle).length - 1;
|
|
11593
12169
|
}
|
|
@@ -11644,6 +12220,7 @@ var init_manager5 = __esm({
|
|
|
11644
12220
|
*/
|
|
11645
12221
|
create(spec) {
|
|
11646
12222
|
const id = generateId();
|
|
12223
|
+
const derivedColorFgBg = spec.themeBackground ? computeColorFgBg(spec.themeBackground) : void 0;
|
|
11647
12224
|
const terminalEnv = {
|
|
11648
12225
|
...Object.fromEntries(
|
|
11649
12226
|
Object.entries(process.env).filter((e) => e[1] != null)
|
|
@@ -11651,6 +12228,7 @@ var init_manager5 = __esm({
|
|
|
11651
12228
|
TERM: "xterm-256color",
|
|
11652
12229
|
COLORTERM: "truecolor",
|
|
11653
12230
|
FORCE_COLOR: "3",
|
|
12231
|
+
...derivedColorFgBg ? { COLORFGBG: derivedColorFgBg } : {},
|
|
11654
12232
|
...spec.env
|
|
11655
12233
|
};
|
|
11656
12234
|
let pty;
|
|
@@ -11853,10 +12431,10 @@ var init_manager5 = __esm({
|
|
|
11853
12431
|
}
|
|
11854
12432
|
return existing.promise;
|
|
11855
12433
|
}
|
|
11856
|
-
let
|
|
12434
|
+
let resolve6 = () => {
|
|
11857
12435
|
};
|
|
11858
12436
|
const promise = new Promise((innerResolve) => {
|
|
11859
|
-
|
|
12437
|
+
resolve6 = innerResolve;
|
|
11860
12438
|
});
|
|
11861
12439
|
let markKillCompleted = () => {
|
|
11862
12440
|
};
|
|
@@ -11869,7 +12447,7 @@ var init_manager5 = __esm({
|
|
|
11869
12447
|
markKillCompleted,
|
|
11870
12448
|
finalized: false,
|
|
11871
12449
|
promise,
|
|
11872
|
-
resolve:
|
|
12450
|
+
resolve: resolve6
|
|
11873
12451
|
});
|
|
11874
12452
|
void terminal.pty.kill(signal).finally(() => {
|
|
11875
12453
|
const waiter = this.explicitCloseWaiters.get(terminalId);
|
|
@@ -12345,10 +12923,10 @@ var init_update_service = __esm({
|
|
|
12345
12923
|
|
|
12346
12924
|
// packages/server/src/workspace/validator.ts
|
|
12347
12925
|
import { constants } from "fs";
|
|
12348
|
-
import { access, stat as
|
|
12926
|
+
import { access, stat as stat7 } from "fs/promises";
|
|
12349
12927
|
async function validatePath(path14) {
|
|
12350
12928
|
try {
|
|
12351
|
-
const stats = await
|
|
12929
|
+
const stats = await stat7(path14);
|
|
12352
12930
|
if (!stats.isDirectory()) {
|
|
12353
12931
|
return { valid: false, error: "Path is not a directory" };
|
|
12354
12932
|
}
|
|
@@ -12383,12 +12961,12 @@ var init_validator = __esm({
|
|
|
12383
12961
|
// packages/server/src/fs/gitignore.ts
|
|
12384
12962
|
import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
|
|
12385
12963
|
import ignore from "ignore";
|
|
12386
|
-
import { join as
|
|
12964
|
+
import { join as join10, relative as relative2 } from "path";
|
|
12387
12965
|
function normalizePath(path14) {
|
|
12388
12966
|
return path14.replace(/\\/g, "/");
|
|
12389
12967
|
}
|
|
12390
12968
|
function relativeToRoot(rootPath, path14) {
|
|
12391
|
-
return normalizePath(
|
|
12969
|
+
return normalizePath(relative2(rootPath, path14));
|
|
12392
12970
|
}
|
|
12393
12971
|
function isDefaultTreeIgnored(name) {
|
|
12394
12972
|
return name.startsWith(".") || name === "node_modules" || name === ".git";
|
|
@@ -12406,7 +12984,7 @@ function isIgnoredByGitignore(ig, path14) {
|
|
|
12406
12984
|
return ig.ignores(path14) || ig.ignores(`${path14}/`);
|
|
12407
12985
|
}
|
|
12408
12986
|
function createGitignoreFilter(rootPath, dirPath) {
|
|
12409
|
-
const gitignorePath =
|
|
12987
|
+
const gitignorePath = join10(rootPath, ".gitignore");
|
|
12410
12988
|
if (!existsSync7(gitignorePath)) {
|
|
12411
12989
|
return (name) => !isDefaultTreeIgnored(name);
|
|
12412
12990
|
}
|
|
@@ -12416,7 +12994,7 @@ function createGitignoreFilter(rootPath, dirPath) {
|
|
|
12416
12994
|
if (isAlwaysTreeIgnored(name)) {
|
|
12417
12995
|
return false;
|
|
12418
12996
|
}
|
|
12419
|
-
const relativePath = relativeToRoot(rootPath,
|
|
12997
|
+
const relativePath = relativeToRoot(rootPath, join10(dirPath, name));
|
|
12420
12998
|
return !isIgnoredByGitignore(ig, relativePath);
|
|
12421
12999
|
};
|
|
12422
13000
|
}
|
|
@@ -13087,8 +13665,8 @@ var init_fencing = __esm({
|
|
|
13087
13665
|
});
|
|
13088
13666
|
|
|
13089
13667
|
// packages/server/src/commands/terminal.ts
|
|
13090
|
-
import { stat as
|
|
13091
|
-
import { basename, isAbsolute } from "node:path";
|
|
13668
|
+
import { stat as stat8 } from "node:fs/promises";
|
|
13669
|
+
import { basename, isAbsolute as isAbsolute2 } from "node:path";
|
|
13092
13670
|
import { z as z6 } from "zod";
|
|
13093
13671
|
function decodeTerminalInput(args) {
|
|
13094
13672
|
if ("bytes" in args) {
|
|
@@ -13195,7 +13773,8 @@ var init_terminal = __esm({
|
|
|
13195
13773
|
workspaceId: z6.string(),
|
|
13196
13774
|
cols: z6.number().int().positive().optional(),
|
|
13197
13775
|
rows: z6.number().int().positive().optional(),
|
|
13198
|
-
cwdPath: z6.string().optional()
|
|
13776
|
+
cwdPath: z6.string().optional(),
|
|
13777
|
+
themeBackground: z6.string().regex(/^#[0-9a-fA-F]{3,8}$/).optional()
|
|
13199
13778
|
}),
|
|
13200
13779
|
async (args, ctx) => {
|
|
13201
13780
|
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
@@ -13204,7 +13783,7 @@ var init_terminal = __esm({
|
|
|
13204
13783
|
}
|
|
13205
13784
|
let cwd = workspace.path;
|
|
13206
13785
|
if (args.cwdPath && args.cwdPath !== ".") {
|
|
13207
|
-
if (
|
|
13786
|
+
if (isAbsolute2(args.cwdPath)) {
|
|
13208
13787
|
throw { code: "invalid_cwd_path", message: "cwdPath must be workspace-relative" };
|
|
13209
13788
|
}
|
|
13210
13789
|
let resolvedCwd;
|
|
@@ -13216,7 +13795,7 @@ var init_terminal = __esm({
|
|
|
13216
13795
|
}
|
|
13217
13796
|
throw error;
|
|
13218
13797
|
}
|
|
13219
|
-
const cwdStats = await
|
|
13798
|
+
const cwdStats = await stat8(resolvedCwd).catch(() => null);
|
|
13220
13799
|
if (!cwdStats) {
|
|
13221
13800
|
throw { code: "cwd_not_found", message: `Directory not found: ${args.cwdPath}` };
|
|
13222
13801
|
}
|
|
@@ -13233,7 +13812,8 @@ var init_terminal = __esm({
|
|
|
13233
13812
|
title: shell.title,
|
|
13234
13813
|
cwd,
|
|
13235
13814
|
cols: args.cols ?? 120,
|
|
13236
|
-
rows: args.rows ?? 30
|
|
13815
|
+
rows: args.rows ?? 30,
|
|
13816
|
+
themeBackground: args.themeBackground
|
|
13237
13817
|
});
|
|
13238
13818
|
return terminal;
|
|
13239
13819
|
}
|
|
@@ -13945,7 +14525,7 @@ var init_hub = __esm({
|
|
|
13945
14525
|
}
|
|
13946
14526
|
}
|
|
13947
14527
|
awaitBinaryPayload(clientId) {
|
|
13948
|
-
return new Promise((
|
|
14528
|
+
return new Promise((resolve6, reject) => {
|
|
13949
14529
|
const timer = setTimeout(() => {
|
|
13950
14530
|
const waiters = this.pendingBinaryWaiters.get(clientId);
|
|
13951
14531
|
if (!waiters) return;
|
|
@@ -13957,7 +14537,7 @@ var init_hub = __esm({
|
|
|
13957
14537
|
}
|
|
13958
14538
|
reject(new Error("Timeout waiting for terminal input binary payload"));
|
|
13959
14539
|
}, BINARY_PAYLOAD_TIMEOUT_MS);
|
|
13960
|
-
const waiter = { resolve:
|
|
14540
|
+
const waiter = { resolve: resolve6, reject, timer };
|
|
13961
14541
|
const queue = this.pendingBinaryWaiters.get(clientId);
|
|
13962
14542
|
if (queue) {
|
|
13963
14543
|
queue.push(waiter);
|
|
@@ -14264,7 +14844,7 @@ var init_hub = __esm({
|
|
|
14264
14844
|
// packages/server/src/commands/workspace.ts
|
|
14265
14845
|
import { readdir as readdir3, realpath as realpath3 } from "node:fs/promises";
|
|
14266
14846
|
import { homedir as homedir2 } from "node:os";
|
|
14267
|
-
import { isAbsolute as
|
|
14847
|
+
import { isAbsolute as isAbsolute3, join as join11, resolve as resolve4 } from "node:path";
|
|
14268
14848
|
import { z as z7 } from "zod";
|
|
14269
14849
|
function resolveBrowsePath(path14) {
|
|
14270
14850
|
const home = homedir2();
|
|
@@ -14272,9 +14852,9 @@ function resolveBrowsePath(path14) {
|
|
|
14272
14852
|
return home;
|
|
14273
14853
|
}
|
|
14274
14854
|
if (path14.startsWith("~/")) {
|
|
14275
|
-
return
|
|
14855
|
+
return join11(home, path14.slice(2));
|
|
14276
14856
|
}
|
|
14277
|
-
return
|
|
14857
|
+
return isAbsolute3(path14) ? path14 : resolve4(home, path14);
|
|
14278
14858
|
}
|
|
14279
14859
|
async function buildRootPaths(currentPath) {
|
|
14280
14860
|
const roots = /* @__PURE__ */ new Set(["/"]);
|
|
@@ -14307,11 +14887,11 @@ var init_workspace = __esm({
|
|
|
14307
14887
|
const entries = await readdir3(basePath, { withFileTypes: true });
|
|
14308
14888
|
const directories = entries.filter((entry) => entry.isDirectory()).map((entry) => ({
|
|
14309
14889
|
name: entry.name,
|
|
14310
|
-
path:
|
|
14890
|
+
path: join11(basePath, entry.name)
|
|
14311
14891
|
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
14312
14892
|
return {
|
|
14313
14893
|
currentPath: basePath,
|
|
14314
|
-
parentPath: basePath !== "/" ?
|
|
14894
|
+
parentPath: basePath !== "/" ? join11(basePath, "..") : null,
|
|
14315
14895
|
directories,
|
|
14316
14896
|
rootPaths: await buildRootPaths(basePath)
|
|
14317
14897
|
};
|
|
@@ -14775,8 +15355,8 @@ var init_pane_layout = __esm({
|
|
|
14775
15355
|
// packages/server/src/commands/session.ts
|
|
14776
15356
|
import { z as z12 } from "zod";
|
|
14777
15357
|
function delay(ms) {
|
|
14778
|
-
return new Promise((
|
|
14779
|
-
setTimeout(
|
|
15358
|
+
return new Promise((resolve6) => {
|
|
15359
|
+
setTimeout(resolve6, ms);
|
|
14780
15360
|
});
|
|
14781
15361
|
}
|
|
14782
15362
|
function getProviderFromRegistry(providerId, registry) {
|
|
@@ -14805,7 +15385,8 @@ var init_session2 = __esm({
|
|
|
14805
15385
|
z12.object({
|
|
14806
15386
|
workspaceId: z12.string(),
|
|
14807
15387
|
providerId: z12.string(),
|
|
14808
|
-
draft: z12.string().optional()
|
|
15388
|
+
draft: z12.string().optional(),
|
|
15389
|
+
themeBackground: z12.string().regex(/^#[0-9a-fA-F]{3,8}$/).optional()
|
|
14809
15390
|
}),
|
|
14810
15391
|
async (args, ctx) => {
|
|
14811
15392
|
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
@@ -14833,7 +15414,8 @@ var init_session2 = __esm({
|
|
|
14833
15414
|
workspacePath: workspace.path,
|
|
14834
15415
|
providerId: args.providerId,
|
|
14835
15416
|
provider,
|
|
14836
|
-
draft: args.draft
|
|
15417
|
+
draft: args.draft,
|
|
15418
|
+
themeBackground: args.themeBackground
|
|
14837
15419
|
});
|
|
14838
15420
|
}
|
|
14839
15421
|
);
|
|
@@ -14930,8 +15512,8 @@ var init_session2 = __esm({
|
|
|
14930
15512
|
// packages/server/src/fs/content-search.ts
|
|
14931
15513
|
import { spawn as spawn5 } from "child_process";
|
|
14932
15514
|
import { existsSync as existsSync8 } from "fs";
|
|
14933
|
-
import { readdir as readdir4, readFile as readFile4, stat as
|
|
14934
|
-
import { basename as basename2, join as
|
|
15515
|
+
import { readdir as readdir4, readFile as readFile4, stat as stat9 } from "fs/promises";
|
|
15516
|
+
import { basename as basename2, join as join12, relative as relative3 } from "path";
|
|
14935
15517
|
import { createInterface } from "readline";
|
|
14936
15518
|
async function searchFileContents(rootPath, options) {
|
|
14937
15519
|
const query = options.query.trim();
|
|
@@ -14954,7 +15536,7 @@ async function searchFileContents(rootPath, options) {
|
|
|
14954
15536
|
return finalizeResults(result, options.maxFiles, options.maxMatchesPerFile);
|
|
14955
15537
|
}
|
|
14956
15538
|
async function searchWithRipgrep(rootPath, query, maxFiles) {
|
|
14957
|
-
const hasGitignore = existsSync8(
|
|
15539
|
+
const hasGitignore = existsSync8(join12(rootPath, ".gitignore"));
|
|
14958
15540
|
const args = [
|
|
14959
15541
|
"--json",
|
|
14960
15542
|
"--line-number",
|
|
@@ -14973,7 +15555,7 @@ async function searchWithRipgrep(rootPath, query, maxFiles) {
|
|
|
14973
15555
|
args.push("--no-require-git");
|
|
14974
15556
|
}
|
|
14975
15557
|
args.push(query, ".");
|
|
14976
|
-
return new Promise((
|
|
15558
|
+
return new Promise((resolve6, reject) => {
|
|
14977
15559
|
const child = spawn5("rg", args, { cwd: rootPath, stdio: ["ignore", "pipe", "pipe"] });
|
|
14978
15560
|
const stdout = createInterface({ input: child.stdout });
|
|
14979
15561
|
const files = /* @__PURE__ */ new Map();
|
|
@@ -14992,7 +15574,7 @@ async function searchWithRipgrep(rootPath, query, maxFiles) {
|
|
|
14992
15574
|
if (!rawPath) {
|
|
14993
15575
|
return;
|
|
14994
15576
|
}
|
|
14995
|
-
const relativePath = normalizeRelativePath(
|
|
15577
|
+
const relativePath = normalizeRelativePath(relative3(rootPath, join12(rootPath, rawPath)));
|
|
14996
15578
|
const preview = (event.data?.lines?.text ?? "").replace(/\r?\n$/, "");
|
|
14997
15579
|
const lineNumber = event.data?.line_number ?? 1;
|
|
14998
15580
|
const submatches = event.data?.submatches ?? [];
|
|
@@ -15022,7 +15604,7 @@ async function searchWithRipgrep(rootPath, query, maxFiles) {
|
|
|
15022
15604
|
child.on("close", (code) => {
|
|
15023
15605
|
void stdout.close();
|
|
15024
15606
|
if (code === 0 || code === 1) {
|
|
15025
|
-
|
|
15607
|
+
resolve6({
|
|
15026
15608
|
files: sortAccumulators(files),
|
|
15027
15609
|
totalMatchCount,
|
|
15028
15610
|
hasMoreFiles
|
|
@@ -15045,7 +15627,7 @@ async function searchWithNode(rootPath, query, maxFiles) {
|
|
|
15045
15627
|
const filteredEntries = entries.filter((entry) => filter(entry.name));
|
|
15046
15628
|
filteredEntries.sort((a, b) => a.name.localeCompare(b.name));
|
|
15047
15629
|
for (const entry of filteredEntries) {
|
|
15048
|
-
const fullPath =
|
|
15630
|
+
const fullPath = join12(dirPath, entry.name);
|
|
15049
15631
|
if (entry.isDirectory()) {
|
|
15050
15632
|
await walk(fullPath);
|
|
15051
15633
|
continue;
|
|
@@ -15053,7 +15635,7 @@ async function searchWithNode(rootPath, query, maxFiles) {
|
|
|
15053
15635
|
if (!entry.isFile()) {
|
|
15054
15636
|
continue;
|
|
15055
15637
|
}
|
|
15056
|
-
const fileStat = await
|
|
15638
|
+
const fileStat = await stat9(fullPath);
|
|
15057
15639
|
if (fileStat.size > FALLBACK_MAX_FILE_BYTES) {
|
|
15058
15640
|
continue;
|
|
15059
15641
|
}
|
|
@@ -15061,7 +15643,7 @@ async function searchWithNode(rootPath, query, maxFiles) {
|
|
|
15061
15643
|
if (isBinaryFile(buffer)) {
|
|
15062
15644
|
continue;
|
|
15063
15645
|
}
|
|
15064
|
-
const relativePath = normalizeRelativePath(
|
|
15646
|
+
const relativePath = normalizeRelativePath(relative3(rootPath, fullPath));
|
|
15065
15647
|
const file = collectMatchesFromText(relativePath, buffer.toString("utf-8"), query);
|
|
15066
15648
|
if (!file) {
|
|
15067
15649
|
continue;
|
|
@@ -15168,10 +15750,10 @@ var init_content_search = __esm({
|
|
|
15168
15750
|
});
|
|
15169
15751
|
|
|
15170
15752
|
// packages/server/src/fs/tree.ts
|
|
15171
|
-
import { readdir as readdir5, stat as
|
|
15172
|
-
import { join as
|
|
15753
|
+
import { readdir as readdir5, stat as stat10 } from "fs/promises";
|
|
15754
|
+
import { join as join13, relative as relative4 } from "path";
|
|
15173
15755
|
async function readTree(rootPath, subdir) {
|
|
15174
|
-
const targetPath = subdir ?
|
|
15756
|
+
const targetPath = subdir ? join13(rootPath, subdir) : rootPath;
|
|
15175
15757
|
const filter = createTreeVisibilityFilter();
|
|
15176
15758
|
const entries = await readdir5(targetPath, { withFileTypes: true });
|
|
15177
15759
|
const nodes = [];
|
|
@@ -15179,8 +15761,8 @@ async function readTree(rootPath, subdir) {
|
|
|
15179
15761
|
if (!filter(entry.name)) {
|
|
15180
15762
|
continue;
|
|
15181
15763
|
}
|
|
15182
|
-
const fullPath =
|
|
15183
|
-
const relPath =
|
|
15764
|
+
const fullPath = join13(targetPath, entry.name);
|
|
15765
|
+
const relPath = relative4(rootPath, fullPath);
|
|
15184
15766
|
if (entry.isDirectory()) {
|
|
15185
15767
|
nodes.push({
|
|
15186
15768
|
name: entry.name,
|
|
@@ -15190,7 +15772,7 @@ async function readTree(rootPath, subdir) {
|
|
|
15190
15772
|
// Not loaded yet - client will request on expand
|
|
15191
15773
|
});
|
|
15192
15774
|
} else if (entry.isFile()) {
|
|
15193
|
-
const stats = await
|
|
15775
|
+
const stats = await stat10(fullPath);
|
|
15194
15776
|
nodes.push({
|
|
15195
15777
|
name: entry.name,
|
|
15196
15778
|
path: relPath,
|
|
@@ -15223,8 +15805,8 @@ async function searchFiles(rootPath, query, limit = 10) {
|
|
|
15223
15805
|
const filteredEntries = entries.filter((entry) => filter(entry.name));
|
|
15224
15806
|
filteredEntries.sort((a, b) => a.name.localeCompare(b.name));
|
|
15225
15807
|
for (const entry of filteredEntries) {
|
|
15226
|
-
const fullPath =
|
|
15227
|
-
const relPath =
|
|
15808
|
+
const fullPath = join13(dirPath, entry.name);
|
|
15809
|
+
const relPath = relative4(rootPath, fullPath);
|
|
15228
15810
|
if (entry.isDirectory()) {
|
|
15229
15811
|
await walk(fullPath);
|
|
15230
15812
|
continue;
|
|
@@ -15259,7 +15841,7 @@ async function searchFiles(rootPath, query, limit = 10) {
|
|
|
15259
15841
|
}
|
|
15260
15842
|
return a.path.toLowerCase().localeCompare(b.path.toLowerCase());
|
|
15261
15843
|
}).slice(0, limit)) {
|
|
15262
|
-
const stats = await
|
|
15844
|
+
const stats = await stat10(match.fullPath);
|
|
15263
15845
|
files.push({
|
|
15264
15846
|
name: match.name,
|
|
15265
15847
|
path: match.path,
|
|
@@ -15518,7 +16100,7 @@ var init_file = __esm({
|
|
|
15518
16100
|
});
|
|
15519
16101
|
|
|
15520
16102
|
// packages/server/src/git/diff.ts
|
|
15521
|
-
import { mkdtemp as mkdtemp3, readFile as readFile5, rm as
|
|
16103
|
+
import { mkdtemp as mkdtemp3, readFile as readFile5, rm as rm7 } from "fs/promises";
|
|
15522
16104
|
import os3 from "os";
|
|
15523
16105
|
import path11 from "path";
|
|
15524
16106
|
async function isTrackedPath(cwd, filePath) {
|
|
@@ -15553,7 +16135,7 @@ async function getUntrackedFileDiff(cwd, filePath) {
|
|
|
15553
16135
|
});
|
|
15554
16136
|
return result.stdout;
|
|
15555
16137
|
} finally {
|
|
15556
|
-
await
|
|
16138
|
+
await rm7(tempDir, { recursive: true, force: true });
|
|
15557
16139
|
}
|
|
15558
16140
|
}
|
|
15559
16141
|
async function pathExists(cwd, filePath) {
|
|
@@ -15986,25 +16568,25 @@ var init_git2 = __esm({
|
|
|
15986
16568
|
// packages/server/src/config/config-io.ts
|
|
15987
16569
|
import { existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync7, renameSync as renameSync2, writeFileSync as writeFileSync5 } from "node:fs";
|
|
15988
16570
|
import { homedir as homedir3 } from "node:os";
|
|
15989
|
-
import { basename as basename3, dirname as dirname7, join as
|
|
16571
|
+
import { basename as basename3, dirname as dirname7, join as join14 } from "node:path";
|
|
15990
16572
|
function resolveConfigPath(configType) {
|
|
15991
16573
|
if (configType === "codex") {
|
|
15992
16574
|
const testHome = process.env.CODER_STUDIO_CODEX_HOME;
|
|
15993
16575
|
if (testHome && testHome.trim()) {
|
|
15994
|
-
return
|
|
16576
|
+
return join14(testHome, "config.toml");
|
|
15995
16577
|
}
|
|
15996
16578
|
const codexHome = process.env.CODEX_HOME;
|
|
15997
16579
|
if (codexHome && codexHome.trim()) {
|
|
15998
|
-
return
|
|
16580
|
+
return join14(codexHome, "config.toml");
|
|
15999
16581
|
}
|
|
16000
|
-
return
|
|
16582
|
+
return join14(homedir3(), ".codex", "config.toml");
|
|
16001
16583
|
}
|
|
16002
16584
|
if (configType === "claude") {
|
|
16003
16585
|
const testHome = process.env.CODER_STUDIO_CLAUDE_HOME;
|
|
16004
16586
|
if (testHome && testHome.trim()) {
|
|
16005
|
-
return
|
|
16587
|
+
return join14(testHome, "settings.json");
|
|
16006
16588
|
}
|
|
16007
|
-
return
|
|
16589
|
+
return join14(homedir3(), ".claude", "settings.json");
|
|
16008
16590
|
}
|
|
16009
16591
|
throw new Error(`Unknown config type: ${configType}`);
|
|
16010
16592
|
}
|
|
@@ -16049,7 +16631,7 @@ function createBackup(filePath) {
|
|
|
16049
16631
|
const base = basename3(filePath, `.${ext}`);
|
|
16050
16632
|
const dir = dirname7(filePath);
|
|
16051
16633
|
const ts = formatTimestamp(/* @__PURE__ */ new Date());
|
|
16052
|
-
const backupPath =
|
|
16634
|
+
const backupPath = join14(dir, `${base}.bak.${ts}.${ext}`);
|
|
16053
16635
|
writeFileSync5(backupPath, original, "utf-8");
|
|
16054
16636
|
return backupPath;
|
|
16055
16637
|
}
|
|
@@ -16077,7 +16659,36 @@ function flattenSettings(obj, prefix = "") {
|
|
|
16077
16659
|
}
|
|
16078
16660
|
return result;
|
|
16079
16661
|
}
|
|
16080
|
-
|
|
16662
|
+
function resolveAppearancePersonalizationOverrideKeysToDelete(settings) {
|
|
16663
|
+
const appearance = settings.appearance;
|
|
16664
|
+
if (!appearance || typeof appearance !== "object" || Array.isArray(appearance)) {
|
|
16665
|
+
return [];
|
|
16666
|
+
}
|
|
16667
|
+
const personalization = appearance.personalization;
|
|
16668
|
+
if (!personalization || typeof personalization !== "object" || Array.isArray(personalization)) {
|
|
16669
|
+
return [];
|
|
16670
|
+
}
|
|
16671
|
+
if (!isFullAppearancePersonalizationSnapshot(personalization)) {
|
|
16672
|
+
return [];
|
|
16673
|
+
}
|
|
16674
|
+
const keysToDelete = [];
|
|
16675
|
+
for (const branch of PERSONALIZATION_OVERRIDE_BRANCHES) {
|
|
16676
|
+
const overrides = personalization[branch];
|
|
16677
|
+
if (!overrides || typeof overrides !== "object" || Array.isArray(overrides)) {
|
|
16678
|
+
continue;
|
|
16679
|
+
}
|
|
16680
|
+
for (const field of PERSONALIZATION_OVERRIDE_FIELDS) {
|
|
16681
|
+
if (!Object.prototype.hasOwnProperty.call(overrides, field)) {
|
|
16682
|
+
keysToDelete.push(`appearance.personalization.${branch}.${field}`);
|
|
16683
|
+
}
|
|
16684
|
+
}
|
|
16685
|
+
}
|
|
16686
|
+
return keysToDelete;
|
|
16687
|
+
}
|
|
16688
|
+
function isFullAppearancePersonalizationSnapshot(personalization) {
|
|
16689
|
+
return Object.prototype.hasOwnProperty.call(personalization, "version") && Object.prototype.hasOwnProperty.call(personalization, "common") && Object.prototype.hasOwnProperty.call(personalization, "desktop") && Object.prototype.hasOwnProperty.call(personalization, "mobile");
|
|
16690
|
+
}
|
|
16691
|
+
var PersonalizationOverridesSchema, PERSONALIZATION_OVERRIDE_BRANCHES, PERSONALIZATION_OVERRIDE_FIELDS, SettingsSchema;
|
|
16081
16692
|
var init_settings2 = __esm({
|
|
16082
16693
|
"packages/server/src/commands/settings.ts"() {
|
|
16083
16694
|
"use strict";
|
|
@@ -16086,6 +16697,23 @@ var init_settings2 = __esm({
|
|
|
16086
16697
|
init_provider_config();
|
|
16087
16698
|
init_settings();
|
|
16088
16699
|
init_dispatch();
|
|
16700
|
+
PersonalizationOverridesSchema = z15.object({
|
|
16701
|
+
backgroundAssetId: z15.string().min(1).nullable().optional(),
|
|
16702
|
+
backgroundDimness: z15.number().int().min(0).max(100).optional(),
|
|
16703
|
+
backgroundBlur: z15.number().int().min(0).max(40).optional(),
|
|
16704
|
+
glassEnabled: z15.boolean().optional(),
|
|
16705
|
+
glassIntensity: z15.number().int().min(0).max(100).optional(),
|
|
16706
|
+
surfaceOpacity: z15.number().int().min(0).max(100).optional()
|
|
16707
|
+
});
|
|
16708
|
+
PERSONALIZATION_OVERRIDE_BRANCHES = ["desktop", "mobile"];
|
|
16709
|
+
PERSONALIZATION_OVERRIDE_FIELDS = [
|
|
16710
|
+
"backgroundAssetId",
|
|
16711
|
+
"backgroundDimness",
|
|
16712
|
+
"backgroundBlur",
|
|
16713
|
+
"glassEnabled",
|
|
16714
|
+
"glassIntensity",
|
|
16715
|
+
"surfaceOpacity"
|
|
16716
|
+
];
|
|
16089
16717
|
SettingsSchema = z15.object({
|
|
16090
16718
|
defaultProviderId: z15.string().optional(),
|
|
16091
16719
|
notifications: z15.object({
|
|
@@ -16112,7 +16740,22 @@ var init_settings2 = __esm({
|
|
|
16112
16740
|
terminalFontSize: z15.number().int().min(10).max(18).optional(),
|
|
16113
16741
|
desktopTerminalFontSize: z15.number().int().min(10).max(18).optional(),
|
|
16114
16742
|
mobileTerminalFontSize: z15.number().int().min(10).max(18).optional(),
|
|
16115
|
-
locale: z15.enum(["zh", "en"]).optional()
|
|
16743
|
+
locale: z15.enum(["zh", "en"]).optional(),
|
|
16744
|
+
personalization: z15.object({
|
|
16745
|
+
version: z15.literal(1).optional(),
|
|
16746
|
+
common: z15.object({
|
|
16747
|
+
backgroundMode: z15.enum(["none", "image"]).optional(),
|
|
16748
|
+
backgroundAssetId: z15.string().min(1).nullable().optional(),
|
|
16749
|
+
backgroundFit: z15.enum(["cover", "contain"]).optional(),
|
|
16750
|
+
backgroundDimness: z15.number().int().min(0).max(100).optional(),
|
|
16751
|
+
backgroundBlur: z15.number().int().min(0).max(40).optional(),
|
|
16752
|
+
glassEnabled: z15.boolean().optional(),
|
|
16753
|
+
glassIntensity: z15.number().int().min(0).max(100).optional(),
|
|
16754
|
+
surfaceOpacity: z15.number().int().min(0).max(100).optional()
|
|
16755
|
+
}).optional(),
|
|
16756
|
+
desktop: PersonalizationOverridesSchema.optional(),
|
|
16757
|
+
mobile: PersonalizationOverridesSchema.optional()
|
|
16758
|
+
}).optional()
|
|
16116
16759
|
}).optional(),
|
|
16117
16760
|
lsp: z15.object({
|
|
16118
16761
|
mode: z15.enum(["auto", "off"]).optional()
|
|
@@ -16182,7 +16825,11 @@ var init_settings2 = __esm({
|
|
|
16182
16825
|
const nextSettings = args.settings;
|
|
16183
16826
|
const providers = nextSettings.providers && typeof nextSettings.providers === "object" && !Array.isArray(nextSettings.providers) ? nextSettings.providers : void 0;
|
|
16184
16827
|
const { providers: _providers, ...nonProviderSettings } = nextSettings;
|
|
16828
|
+
const overrideKeysToDelete = resolveAppearancePersonalizationOverrideKeysToDelete(nextSettings);
|
|
16185
16829
|
const flatSettings = flattenSettings(nonProviderSettings);
|
|
16830
|
+
for (const key of overrideKeysToDelete) {
|
|
16831
|
+
ctx.settingsRepo.delete(key);
|
|
16832
|
+
}
|
|
16186
16833
|
for (const [key, value] of Object.entries(flatSettings)) {
|
|
16187
16834
|
ctx.settingsRepo.set(key, value);
|
|
16188
16835
|
}
|
|
@@ -16828,6 +17475,7 @@ async function listWorktrees(repoPath) {
|
|
|
16828
17475
|
current.name = branch.split("/").pop() || branch;
|
|
16829
17476
|
} else if (line === "detached") {
|
|
16830
17477
|
current.branch = "detached HEAD";
|
|
17478
|
+
current.name = path12.basename(current.path ?? "") || "detached";
|
|
16831
17479
|
} else if (line === "") {
|
|
16832
17480
|
if (current.path) {
|
|
16833
17481
|
worktrees.push(current);
|
|
@@ -17409,12 +18057,12 @@ var init_commands = __esm({
|
|
|
17409
18057
|
// packages/server/src/server.ts
|
|
17410
18058
|
import { mkdtempSync, rmSync as rmSync2 } from "node:fs";
|
|
17411
18059
|
import { tmpdir } from "node:os";
|
|
17412
|
-
import { join as
|
|
18060
|
+
import { join as join15 } from "node:path";
|
|
17413
18061
|
async function createServer(configOverrides) {
|
|
17414
18062
|
const config = parseServerConfig(configOverrides);
|
|
17415
18063
|
const configuredStateDir = resolveConfiguredStateDir(config);
|
|
17416
18064
|
const shouldCleanupStateRoot = configuredStateDir === IN_MEMORY_STATE_DIR;
|
|
17417
|
-
const stateRoot = shouldCleanupStateRoot ? mkdtempSync(
|
|
18065
|
+
const stateRoot = shouldCleanupStateRoot ? mkdtempSync(join15(tmpdir(), "coder-studio-state-")) : configuredStateDir;
|
|
17418
18066
|
ensureStateDir(config);
|
|
17419
18067
|
const eventBus = new EventBus();
|
|
17420
18068
|
const activationMgr = new ActivationManager();
|
|
@@ -17424,10 +18072,10 @@ async function createServer(configOverrides) {
|
|
|
17424
18072
|
let commandContext;
|
|
17425
18073
|
let lspMgr = null;
|
|
17426
18074
|
const terminalRepo = new TerminalRepo({
|
|
17427
|
-
filePath:
|
|
18075
|
+
filePath: join15(stateRoot, "state", "terminals.json")
|
|
17428
18076
|
});
|
|
17429
18077
|
const sessionRepo = new SessionRepo({
|
|
17430
|
-
filePath:
|
|
18078
|
+
filePath: join15(stateRoot, "state", "sessions.json")
|
|
17431
18079
|
});
|
|
17432
18080
|
const terminalMgr = new TerminalManager({
|
|
17433
18081
|
ptyHost: createPtyHost(),
|
|
@@ -17435,10 +18083,10 @@ async function createServer(configOverrides) {
|
|
|
17435
18083
|
db: terminalRepo
|
|
17436
18084
|
});
|
|
17437
18085
|
const settingsRepo = new SettingsRepo({
|
|
17438
|
-
filePath:
|
|
18086
|
+
filePath: join15(stateRoot, "state", "settings.json")
|
|
17439
18087
|
});
|
|
17440
18088
|
const updateStateRepo = new UpdateStateRepo({
|
|
17441
|
-
filePath:
|
|
18089
|
+
filePath: join15(stateRoot, "state", "update-state.json"),
|
|
17442
18090
|
currentVersion: config.appVersion ?? "0.0.0"
|
|
17443
18091
|
});
|
|
17444
18092
|
const autoFetch = new AutoFetchScheduler({
|
|
@@ -17471,10 +18119,10 @@ async function createServer(configOverrides) {
|
|
|
17471
18119
|
}
|
|
17472
18120
|
});
|
|
17473
18121
|
const providerConfigRepo = new ProviderConfigRepo({
|
|
17474
|
-
filePath:
|
|
18122
|
+
filePath: join15(stateRoot, "state", "provider-configs.json")
|
|
17475
18123
|
});
|
|
17476
18124
|
const workspaceRepo = new WorkspaceRepo({
|
|
17477
|
-
filePath:
|
|
18125
|
+
filePath: join15(stateRoot, "state", "workspaces.json")
|
|
17478
18126
|
});
|
|
17479
18127
|
const sessionMgr = new SessionManager({
|
|
17480
18128
|
terminalMgr,
|
|
@@ -17509,10 +18157,13 @@ async function createServer(configOverrides) {
|
|
|
17509
18157
|
)
|
|
17510
18158
|
});
|
|
17511
18159
|
const authSessionRepo = new AuthSessionRepo({
|
|
17512
|
-
filePath:
|
|
18160
|
+
filePath: join15(stateRoot, "state", "auth-sessions.json")
|
|
17513
18161
|
});
|
|
17514
18162
|
const authLoginBlockRepo = new AuthLoginBlockRepo({
|
|
17515
|
-
filePath:
|
|
18163
|
+
filePath: join15(stateRoot, "state", "auth-login-blocks.json")
|
|
18164
|
+
});
|
|
18165
|
+
const appearanceAssetRepo = new AppearanceAssetRepo({
|
|
18166
|
+
filePath: join15(stateRoot, "state", "appearance-assets.json")
|
|
17516
18167
|
});
|
|
17517
18168
|
const app = await buildFastifyApp({
|
|
17518
18169
|
wsHub,
|
|
@@ -17521,6 +18172,7 @@ async function createServer(configOverrides) {
|
|
|
17521
18172
|
config,
|
|
17522
18173
|
authSessionRepo,
|
|
17523
18174
|
authLoginBlockRepo,
|
|
18175
|
+
appearanceAssetRepo,
|
|
17524
18176
|
logger: {
|
|
17525
18177
|
level: "info",
|
|
17526
18178
|
transport: {
|
|
@@ -17587,7 +18239,7 @@ async function createServer(configOverrides) {
|
|
|
17587
18239
|
...config.update,
|
|
17588
18240
|
currentVersion: config.appVersion ?? "0.0.0"
|
|
17589
18241
|
},
|
|
17590
|
-
updateWorkerLogFilePath:
|
|
18242
|
+
updateWorkerLogFilePath: join15(stateRoot, "logs", "update-worker.log"),
|
|
17591
18243
|
countRunningTerminals: () => terminalMgr.getAll().filter((terminal) => terminal.alive).length,
|
|
17592
18244
|
countRunningSessions: () => sessionMgr.getAll().filter((session) => session.state === "starting" || session.state === "running").length,
|
|
17593
18245
|
countActiveSupervisors: () => supervisorMgr?.countActive() ?? 0
|
|
@@ -17699,6 +18351,7 @@ var init_server = __esm({
|
|
|
17699
18351
|
init_e2e_provider_mock();
|
|
17700
18352
|
init_install_manager2();
|
|
17701
18353
|
init_manager3();
|
|
18354
|
+
init_appearance_asset_repo();
|
|
17702
18355
|
init_auth_login_block_repo();
|
|
17703
18356
|
init_auth_session_repo();
|
|
17704
18357
|
init_provider_config_repo();
|
|
@@ -17817,9 +18470,9 @@ import { fileURLToPath as fileURLToPath4 } from "url";
|
|
|
17817
18470
|
init_state_paths();
|
|
17818
18471
|
import { existsSync as existsSync10, mkdirSync as mkdirSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
|
|
17819
18472
|
import { homedir as homedir4 } from "os";
|
|
17820
|
-
import { join as
|
|
18473
|
+
import { join as join16 } from "path";
|
|
17821
18474
|
function getCliConfigPath() {
|
|
17822
|
-
return
|
|
18475
|
+
return join16(homedir4(), ".coder-studio", "config.json");
|
|
17823
18476
|
}
|
|
17824
18477
|
function normalizeLegacyDataDir(input) {
|
|
17825
18478
|
return normalizeLegacyStateDir(input);
|
|
@@ -17847,11 +18500,11 @@ function readCliConfig() {
|
|
|
17847
18500
|
|
|
17848
18501
|
// packages/cli/src/embed.ts
|
|
17849
18502
|
import { existsSync as existsSync11 } from "fs";
|
|
17850
|
-
import { dirname as dirname8, resolve as
|
|
18503
|
+
import { dirname as dirname8, resolve as resolve5 } from "path";
|
|
17851
18504
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
17852
18505
|
var __filename = fileURLToPath2(import.meta.url);
|
|
17853
18506
|
var __dirname = dirname8(__filename);
|
|
17854
|
-
var WEB_ASSETS_DIR =
|
|
18507
|
+
var WEB_ASSETS_DIR = resolve5(__dirname, "../web");
|
|
17855
18508
|
function getStaticAssetsDir() {
|
|
17856
18509
|
return WEB_ASSETS_DIR;
|
|
17857
18510
|
}
|
|
@@ -17915,13 +18568,13 @@ function getCliPackageName(importMetaUrl) {
|
|
|
17915
18568
|
|
|
17916
18569
|
// packages/cli/src/update-runtime.ts
|
|
17917
18570
|
import { existsSync as existsSync13 } from "node:fs";
|
|
17918
|
-
import { dirname as dirname9, join as
|
|
18571
|
+
import { dirname as dirname9, join as join17 } from "node:path";
|
|
17919
18572
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
17920
18573
|
function resolveWorkerEntryPath(importMetaUrl) {
|
|
17921
18574
|
const currentDir = dirname9(fileURLToPath3(importMetaUrl));
|
|
17922
18575
|
const candidates = [
|
|
17923
|
-
|
|
17924
|
-
|
|
18576
|
+
join17(currentDir, "update-worker.mjs"),
|
|
18577
|
+
join17(currentDir, "../src/update-worker.ts")
|
|
17925
18578
|
];
|
|
17926
18579
|
return candidates.find((candidate) => existsSync13(candidate));
|
|
17927
18580
|
}
|