@spencer-kit/coder-studio 0.4.3 → 0.4.5
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 +1415 -463
- package/dist/esm/bin.mjs.map +4 -4
- package/dist/esm/server-runner.mjs +1377 -425
- package/dist/esm/server-runner.mjs.map +4 -4
- package/dist/esm/update-worker.mjs +118 -3
- package/dist/esm/update-worker.mjs.map +2 -2
- package/dist/web/assets/components-BGEBLvHB.css +1 -0
- package/dist/web/assets/components-CFZItGai.js +109 -0
- package/dist/web/assets/components-CFZItGai.js.map +1 -0
- package/dist/web/assets/main-ShJ51B7m.js +2 -0
- package/dist/web/assets/main-ShJ51B7m.js.map +1 -0
- package/dist/web/assets/ui-preview-CKpnmBtY.js +17 -0
- package/dist/web/assets/ui-preview-CKpnmBtY.js.map +1 -0
- package/dist/web/index.html +3 -3
- package/dist/web/ui-preview.html +3 -3
- package/package.json +1 -1
- package/src/update-worker.test.ts +53 -12
- package/src/update-worker.ts +158 -3
- package/dist/web/assets/components-AKM1pxhf.css +0 -1
- package/dist/web/assets/components-BZf_jLGv.js +0 -110
- package/dist/web/assets/components-BZf_jLGv.js.map +0 -1
- package/dist/web/assets/main-CEv6hML1.js +0 -2
- package/dist/web/assets/main-CEv6hML1.js.map +0 -1
- package/dist/web/assets/ui-preview-Cd3euuid.js +0 -17
- package/dist/web/assets/ui-preview-Cd3euuid.js.map +0 -1
|
@@ -931,10 +931,10 @@ function resolveSpawnArgv(argv, deps = {}) {
|
|
|
931
931
|
}
|
|
932
932
|
const restArgs = argv.slice(1);
|
|
933
933
|
const readFileSync10 = deps.readFileSync ?? ((file) => fs2.readFileSync(file, "utf8"));
|
|
934
|
-
const
|
|
934
|
+
const existsSync14 = deps.existsSync ?? fs2.existsSync;
|
|
935
935
|
const pathEnv = deps.pathEnv ?? process.env.Path ?? process.env.PATH ?? "";
|
|
936
936
|
const pathExt = deps.pathExt ?? process.env.PATHEXT ?? DEFAULT_PATHEXT;
|
|
937
|
-
const resolved = resolveExecutablePath(command, pathEnv, pathExt,
|
|
937
|
+
const resolved = resolveExecutablePath(command, pathEnv, pathExt, existsSync14);
|
|
938
938
|
if (!resolved) {
|
|
939
939
|
return [...argv];
|
|
940
940
|
}
|
|
@@ -983,17 +983,17 @@ function expandShimVars(value, dp0Dir) {
|
|
|
983
983
|
function parsePathExt(pathExt) {
|
|
984
984
|
return pathExt.split(";").map((entry) => entry.trim().toLowerCase()).filter((entry) => entry.length > 0);
|
|
985
985
|
}
|
|
986
|
-
function resolveExecutablePath(command, pathEnv, pathExt,
|
|
986
|
+
function resolveExecutablePath(command, pathEnv, pathExt, existsSync14) {
|
|
987
987
|
const hasExt = path3.win32.extname(command).length > 0;
|
|
988
988
|
const extensions = parsePathExt(pathExt);
|
|
989
989
|
if (path3.win32.isAbsolute(command)) {
|
|
990
|
-
if (
|
|
990
|
+
if (existsSync14(command)) {
|
|
991
991
|
return command;
|
|
992
992
|
}
|
|
993
993
|
if (!hasExt) {
|
|
994
994
|
for (const ext of extensions) {
|
|
995
995
|
const candidate = command + ext;
|
|
996
|
-
if (
|
|
996
|
+
if (existsSync14(candidate)) {
|
|
997
997
|
return candidate;
|
|
998
998
|
}
|
|
999
999
|
}
|
|
@@ -1004,14 +1004,14 @@ function resolveExecutablePath(command, pathEnv, pathExt, existsSync13) {
|
|
|
1004
1004
|
for (const dir of dirs) {
|
|
1005
1005
|
if (hasExt) {
|
|
1006
1006
|
const candidate = path3.win32.join(dir, command);
|
|
1007
|
-
if (
|
|
1007
|
+
if (existsSync14(candidate)) {
|
|
1008
1008
|
return candidate;
|
|
1009
1009
|
}
|
|
1010
1010
|
continue;
|
|
1011
1011
|
}
|
|
1012
1012
|
for (const ext of extensions) {
|
|
1013
1013
|
const candidate = path3.win32.join(dir, command + ext);
|
|
1014
|
-
if (
|
|
1014
|
+
if (existsSync14(candidate)) {
|
|
1015
1015
|
return candidate;
|
|
1016
1016
|
}
|
|
1017
1017
|
}
|
|
@@ -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 relative3 = path7.relative(resolvedRoot, resolvedTarget);
|
|
2536
|
-
if (!relative3) {
|
|
2537
|
-
return;
|
|
2538
|
-
}
|
|
2539
|
-
let current = resolvedRoot;
|
|
2540
|
-
for (const segment of relative3.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" });
|
|
@@ -2914,13 +3079,13 @@ function registerUploadsRoute(app, deps) {
|
|
|
2914
3079
|
return reply.status(400).send({ ok: false, error: "parse_failed" });
|
|
2915
3080
|
}
|
|
2916
3081
|
if (!workspaceId) {
|
|
2917
|
-
return
|
|
3082
|
+
return rejectAndCleanup2(reply, written, 400, "workspace_required");
|
|
2918
3083
|
}
|
|
2919
3084
|
if (!workspaceValidated) {
|
|
2920
|
-
return
|
|
3085
|
+
return rejectAndCleanup2(reply, written, 404, "workspace_not_found");
|
|
2921
3086
|
}
|
|
2922
3087
|
if (written.length === 0) {
|
|
2923
|
-
return
|
|
3088
|
+
return rejectAndCleanup2(reply, written, 400, "no_files");
|
|
2924
3089
|
}
|
|
2925
3090
|
if (!await ensureWorkspaceStillActive(deps, workspaceId, reply, written)) {
|
|
2926
3091
|
return;
|
|
@@ -2940,7 +3105,105 @@ var init_uploads = __esm({
|
|
|
2940
3105
|
}
|
|
2941
3106
|
});
|
|
2942
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
|
+
};
|
|
3202
|
+
}
|
|
3203
|
+
});
|
|
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;
|
|
@@ -6686,6 +7012,7 @@ var init_manager3 = __esm({
|
|
|
6686
7012
|
this.applyTerminalInputActivity(session, activity, text, { armTurnCompletion: true });
|
|
6687
7013
|
}
|
|
6688
7014
|
applyTerminalInputActivity(session, activity, text, options) {
|
|
7015
|
+
const completedSynchronously = !options.armTurnCompletion && session.state === "idle" && !session.awaitingTurnCompletion;
|
|
6689
7016
|
if (activity === "control" || activity === "typing") {
|
|
6690
7017
|
return;
|
|
6691
7018
|
}
|
|
@@ -6694,16 +7021,10 @@ var init_manager3 = __esm({
|
|
|
6694
7021
|
session.awaitingTurnCompletion = true;
|
|
6695
7022
|
session.sawOutputSinceTurnStart = false;
|
|
6696
7023
|
}
|
|
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");
|
|
7024
|
+
if (completedSynchronously) {
|
|
7025
|
+
return;
|
|
6706
7026
|
}
|
|
7027
|
+
this.transitionSessionToRunning(session);
|
|
6707
7028
|
return;
|
|
6708
7029
|
}
|
|
6709
7030
|
if (activity !== "submit") return;
|
|
@@ -6716,17 +7037,11 @@ var init_manager3 = __esm({
|
|
|
6716
7037
|
session.sawOutputSinceTurnStart = false;
|
|
6717
7038
|
}
|
|
6718
7039
|
const titleChanged = this.maybeAssignTitle(session, submittedText);
|
|
6719
|
-
const prev = session.state;
|
|
6720
7040
|
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");
|
|
7041
|
+
if (shouldResume && !completedSynchronously) {
|
|
7042
|
+
this.transitionSessionToRunning(session);
|
|
6729
7043
|
} else if (titleChanged) {
|
|
7044
|
+
const prev = session.state;
|
|
6730
7045
|
this.emitStateChanged(session, prev, session.state);
|
|
6731
7046
|
}
|
|
6732
7047
|
}
|
|
@@ -6740,9 +7055,11 @@ var init_manager3 = __esm({
|
|
|
6740
7055
|
}
|
|
6741
7056
|
const text = activity === "submit" || activity === "internal_submit" ? submittedText ?? bytes.toString("utf-8") : void 0;
|
|
6742
7057
|
const rollbackArm = this.armTurnCompletionBeforeWrite(session, activity);
|
|
7058
|
+
const rollbackRecentInput = this.recordRecentInputBeforeWrite(session, bytes, activity);
|
|
6743
7059
|
try {
|
|
6744
7060
|
this.deps.terminalMgr.write(session.terminalId, bytes);
|
|
6745
7061
|
} catch (error) {
|
|
7062
|
+
rollbackRecentInput?.();
|
|
6746
7063
|
rollbackArm?.();
|
|
6747
7064
|
throw error;
|
|
6748
7065
|
}
|
|
@@ -6804,6 +7121,19 @@ var init_manager3 = __esm({
|
|
|
6804
7121
|
this.deps.db.update(session.id, { title });
|
|
6805
7122
|
return true;
|
|
6806
7123
|
}
|
|
7124
|
+
transitionSessionToRunning(session) {
|
|
7125
|
+
if (session.state === "running") {
|
|
7126
|
+
return;
|
|
7127
|
+
}
|
|
7128
|
+
const prev = session.state;
|
|
7129
|
+
session.state = "running";
|
|
7130
|
+
session.lastActiveAt = Date.now();
|
|
7131
|
+
this.deps.db.update(session.id, {
|
|
7132
|
+
state: "running",
|
|
7133
|
+
lastActiveAt: session.lastActiveAt
|
|
7134
|
+
});
|
|
7135
|
+
this.emitStateChanged(session, prev, "running");
|
|
7136
|
+
}
|
|
6807
7137
|
/**
|
|
6808
7138
|
* Handle terminal exit event
|
|
6809
7139
|
*/
|
|
@@ -6865,6 +7195,197 @@ var init_manager3 = __esm({
|
|
|
6865
7195
|
session.sawOutputSinceTurnStart = previousSawOutputSinceTurnStart;
|
|
6866
7196
|
};
|
|
6867
7197
|
}
|
|
7198
|
+
recordRecentInputBeforeWrite(session, bytes, activity) {
|
|
7199
|
+
if (activity === "system") {
|
|
7200
|
+
return null;
|
|
7201
|
+
}
|
|
7202
|
+
const { opaque, remainingVisibleText } = classifyRecentInputEcho(bytes);
|
|
7203
|
+
if (!opaque && remainingVisibleText.length === 0) {
|
|
7204
|
+
return null;
|
|
7205
|
+
}
|
|
7206
|
+
const recentInput = {
|
|
7207
|
+
at: Date.now(),
|
|
7208
|
+
activity,
|
|
7209
|
+
byteLength: bytes.byteLength,
|
|
7210
|
+
opaque,
|
|
7211
|
+
remainingVisibleText
|
|
7212
|
+
};
|
|
7213
|
+
session.recentInputEchoes.push(recentInput);
|
|
7214
|
+
this.pruneRecentInputEchoes(session, recentInput.at);
|
|
7215
|
+
return () => {
|
|
7216
|
+
const index = session.recentInputEchoes.indexOf(recentInput);
|
|
7217
|
+
if (index !== -1) {
|
|
7218
|
+
session.recentInputEchoes.splice(index, 1);
|
|
7219
|
+
}
|
|
7220
|
+
};
|
|
7221
|
+
}
|
|
7222
|
+
pruneRecentInputEchoes(session, now = Date.now()) {
|
|
7223
|
+
session.recentInputEchoes = session.recentInputEchoes.filter(
|
|
7224
|
+
(entry) => now - entry.at <= RECENT_INPUT_ECHO_WINDOW_MS && (entry.opaque || entry.remainingVisibleText.length > 0)
|
|
7225
|
+
);
|
|
7226
|
+
let totalBytes = session.recentInputEchoes.reduce((sum, entry) => sum + entry.byteLength, 0);
|
|
7227
|
+
while (session.recentInputEchoes.length > RECENT_INPUT_ECHO_MAX_EVENTS || totalBytes > RECENT_INPUT_ECHO_MAX_BYTES) {
|
|
7228
|
+
const removed = session.recentInputEchoes.shift();
|
|
7229
|
+
if (!removed) {
|
|
7230
|
+
break;
|
|
7231
|
+
}
|
|
7232
|
+
totalBytes -= removed.byteLength;
|
|
7233
|
+
}
|
|
7234
|
+
}
|
|
7235
|
+
clearPendingResumeAggregation(session) {
|
|
7236
|
+
if (session.pendingResumeAggregation?.timer) {
|
|
7237
|
+
clearTimeout(session.pendingResumeAggregation.timer);
|
|
7238
|
+
}
|
|
7239
|
+
session.pendingResumeAggregation = null;
|
|
7240
|
+
}
|
|
7241
|
+
consumeRecentLiteralEcho(session, visibleOutput) {
|
|
7242
|
+
let offset = 0;
|
|
7243
|
+
let matchedLiteralEcho = false;
|
|
7244
|
+
for (const entry of session.recentInputEchoes) {
|
|
7245
|
+
if (offset >= visibleOutput.length) {
|
|
7246
|
+
break;
|
|
7247
|
+
}
|
|
7248
|
+
if (entry.remainingVisibleText.length === 0) {
|
|
7249
|
+
continue;
|
|
7250
|
+
}
|
|
7251
|
+
const currentVisibleText = entry.remainingVisibleText;
|
|
7252
|
+
const matchLength = commonPrefixLength(visibleOutput.slice(offset), currentVisibleText);
|
|
7253
|
+
if (matchLength === 0) {
|
|
7254
|
+
break;
|
|
7255
|
+
}
|
|
7256
|
+
entry.remainingVisibleText = currentVisibleText.slice(matchLength);
|
|
7257
|
+
offset += matchLength;
|
|
7258
|
+
matchedLiteralEcho = true;
|
|
7259
|
+
if (matchLength < currentVisibleText.length) {
|
|
7260
|
+
break;
|
|
7261
|
+
}
|
|
7262
|
+
}
|
|
7263
|
+
return {
|
|
7264
|
+
matchedLiteralEcho,
|
|
7265
|
+
leftoverVisibleOutput: visibleOutput.slice(offset)
|
|
7266
|
+
};
|
|
7267
|
+
}
|
|
7268
|
+
hasRecentNonSubmitInput(session, now) {
|
|
7269
|
+
return session.recentInputEchoes.some(
|
|
7270
|
+
(entry) => entry.activity !== "submit" && entry.activity !== "internal_submit" && now - entry.at <= RECENT_OPAQUE_INPUT_ECHO_WINDOW_MS
|
|
7271
|
+
);
|
|
7272
|
+
}
|
|
7273
|
+
hasRecentOpaqueNonSubmitInput(session, now) {
|
|
7274
|
+
return session.recentInputEchoes.some(
|
|
7275
|
+
(entry) => entry.opaque && entry.activity !== "submit" && entry.activity !== "internal_submit" && now - entry.at <= RECENT_OPAQUE_INPUT_ECHO_WINDOW_MS
|
|
7276
|
+
);
|
|
7277
|
+
}
|
|
7278
|
+
hasRecentControlInput(session, now) {
|
|
7279
|
+
return session.recentInputEchoes.some(
|
|
7280
|
+
(entry) => entry.activity === "control" && now - entry.at <= RECENT_OPAQUE_INPUT_ECHO_WINDOW_MS
|
|
7281
|
+
);
|
|
7282
|
+
}
|
|
7283
|
+
getRecentVisibleInputText(session) {
|
|
7284
|
+
return session.recentInputEchoes.filter((entry) => entry.activity !== "submit" && entry.activity !== "internal_submit").map((entry) => entry.remainingVisibleText).join("");
|
|
7285
|
+
}
|
|
7286
|
+
isLikelyPureInputRepaint(session, chunk, visibleOutput, now) {
|
|
7287
|
+
if (!this.hasRecentNonSubmitInput(session, now)) {
|
|
7288
|
+
return false;
|
|
7289
|
+
}
|
|
7290
|
+
if (!visibleOutput.trim()) {
|
|
7291
|
+
return true;
|
|
7292
|
+
}
|
|
7293
|
+
const chunkText = chunk.toString("utf8");
|
|
7294
|
+
if (chunkText.includes("\n")) {
|
|
7295
|
+
return false;
|
|
7296
|
+
}
|
|
7297
|
+
if (visibleOutputHasMultipleLines(visibleOutput)) {
|
|
7298
|
+
return false;
|
|
7299
|
+
}
|
|
7300
|
+
if (!chunkHasLineRepaintControl(chunkText)) {
|
|
7301
|
+
return false;
|
|
7302
|
+
}
|
|
7303
|
+
if (this.hasRecentOpaqueNonSubmitInput(session, now)) {
|
|
7304
|
+
return true;
|
|
7305
|
+
}
|
|
7306
|
+
const recentVisibleInputText = this.getRecentVisibleInputText(session);
|
|
7307
|
+
const trimmedVisibleOutput = visibleOutput.trim();
|
|
7308
|
+
if (!recentVisibleInputText || !trimmedVisibleOutput) {
|
|
7309
|
+
return false;
|
|
7310
|
+
}
|
|
7311
|
+
return recentVisibleInputText === trimmedVisibleOutput || recentVisibleInputText.startsWith(trimmedVisibleOutput) || trimmedVisibleOutput.endsWith(recentVisibleInputText);
|
|
7312
|
+
}
|
|
7313
|
+
assessTerminalOutput(session, chunk) {
|
|
7314
|
+
const now = Date.now();
|
|
7315
|
+
this.pruneRecentInputEchoes(session, now);
|
|
7316
|
+
if (session.recentInputEchoes.length === 0) {
|
|
7317
|
+
const hasVisibleText = extractVisibleTerminalText(chunk).trim().length > 0;
|
|
7318
|
+
return {
|
|
7319
|
+
shouldResumeRunning: hasVisibleText,
|
|
7320
|
+
countsAsTurnOutput: hasVisibleText,
|
|
7321
|
+
shouldAggregateForResume: false
|
|
7322
|
+
};
|
|
7323
|
+
}
|
|
7324
|
+
const visibleOutput = extractVisibleTerminalText(chunk);
|
|
7325
|
+
const { matchedLiteralEcho, leftoverVisibleOutput } = this.consumeRecentLiteralEcho(
|
|
7326
|
+
session,
|
|
7327
|
+
visibleOutput
|
|
7328
|
+
);
|
|
7329
|
+
const hasRecentNonSubmitInput = this.hasRecentNonSubmitInput(session, now);
|
|
7330
|
+
const hasRecentControlInput = this.hasRecentControlInput(session, now);
|
|
7331
|
+
this.pruneRecentInputEchoes(session, now);
|
|
7332
|
+
const trimmedLeftoverVisibleOutput = leftoverVisibleOutput.trim();
|
|
7333
|
+
const trimmedVisibleOutput = visibleOutput.trim();
|
|
7334
|
+
const suppressImmediateResumeAfterControl = hasRecentControlInput && !matchedLiteralEcho;
|
|
7335
|
+
const chunkText = chunk.toString("utf8");
|
|
7336
|
+
const shouldAggregateForResume = session.state === "idle" && hasRecentNonSubmitInput && !matchedLiteralEcho && trimmedVisibleOutput.length > 0 && chunkHasLineRepaintControl(chunkText) && !visibleOutputHasMultipleLines(visibleOutput);
|
|
7337
|
+
const shouldResumeRunning = suppressImmediateResumeAfterControl ? false : trimmedLeftoverVisibleOutput.length > 0 ? !this.isLikelyPureInputRepaint(session, chunk, leftoverVisibleOutput, now) : !matchedLiteralEcho && trimmedVisibleOutput.length > 0 && !this.isLikelyPureInputRepaint(session, chunk, visibleOutput, now);
|
|
7338
|
+
const countsAsTurnOutput = !suppressImmediateResumeAfterControl && (trimmedLeftoverVisibleOutput.length > 0 ? !this.isLikelyPureInputRepaint(session, chunk, leftoverVisibleOutput, now) : !matchedLiteralEcho && trimmedVisibleOutput.length > 0 && !this.isLikelyPureInputRepaint(session, chunk, visibleOutput, now));
|
|
7339
|
+
return {
|
|
7340
|
+
shouldResumeRunning,
|
|
7341
|
+
countsAsTurnOutput,
|
|
7342
|
+
shouldAggregateForResume
|
|
7343
|
+
};
|
|
7344
|
+
}
|
|
7345
|
+
flushPendingResumeAggregation(session) {
|
|
7346
|
+
const pending = session.pendingResumeAggregation;
|
|
7347
|
+
if (!pending) {
|
|
7348
|
+
return;
|
|
7349
|
+
}
|
|
7350
|
+
this.clearPendingResumeAggregation(session);
|
|
7351
|
+
if (session.state !== "idle") {
|
|
7352
|
+
return;
|
|
7353
|
+
}
|
|
7354
|
+
const combinedChunk = Buffer.concat(pending.chunks, pending.byteLength);
|
|
7355
|
+
const outputAssessment = this.assessTerminalOutput(session, combinedChunk);
|
|
7356
|
+
if (outputAssessment.countsAsTurnOutput) {
|
|
7357
|
+
session.sawOutputSinceTurnStart = true;
|
|
7358
|
+
}
|
|
7359
|
+
}
|
|
7360
|
+
schedulePendingResumeAggregation(session, chunk) {
|
|
7361
|
+
const pending = session.pendingResumeAggregation;
|
|
7362
|
+
if (!pending) {
|
|
7363
|
+
const nextPending = {
|
|
7364
|
+
startedAt: Date.now(),
|
|
7365
|
+
chunks: [chunk],
|
|
7366
|
+
byteLength: chunk.byteLength,
|
|
7367
|
+
timer: null
|
|
7368
|
+
};
|
|
7369
|
+
nextPending.timer = setTimeout(() => {
|
|
7370
|
+
const activeSession = this.sessions.get(session.id);
|
|
7371
|
+
if (!activeSession) {
|
|
7372
|
+
return;
|
|
7373
|
+
}
|
|
7374
|
+
this.flushPendingResumeAggregation(activeSession);
|
|
7375
|
+
}, RESUME_OUTPUT_AGGREGATION_WINDOW_MS);
|
|
7376
|
+
session.pendingResumeAggregation = nextPending;
|
|
7377
|
+
return;
|
|
7378
|
+
}
|
|
7379
|
+
pending.chunks.push(chunk);
|
|
7380
|
+
pending.byteLength += chunk.byteLength;
|
|
7381
|
+
const combinedChunk = Buffer.concat(pending.chunks, pending.byteLength);
|
|
7382
|
+
const visibleOutput = extractVisibleTerminalText(combinedChunk);
|
|
7383
|
+
const combinedText = combinedChunk.toString("utf8");
|
|
7384
|
+
const shouldFlushNow = pending.byteLength >= RESUME_OUTPUT_AGGREGATION_MAX_BYTES || combinedText.includes("\n") || visibleOutputHasMultipleLines(visibleOutput);
|
|
7385
|
+
if (shouldFlushNow) {
|
|
7386
|
+
this.flushPendingResumeAggregation(session);
|
|
7387
|
+
}
|
|
7388
|
+
}
|
|
6868
7389
|
flushPendingPtyIdle(session) {
|
|
6869
7390
|
const ptyState = this.comparators.get(session.id)?.snapshot().ptyState;
|
|
6870
7391
|
if (ptyState !== "idle") {
|
|
@@ -6873,6 +7394,7 @@ var init_manager3 = __esm({
|
|
|
6873
7394
|
this.transitionSessionToIdle(session);
|
|
6874
7395
|
}
|
|
6875
7396
|
transitionSessionToIdle(activeSession) {
|
|
7397
|
+
this.clearPendingResumeAggregation(activeSession);
|
|
6876
7398
|
const prev = activeSession.state;
|
|
6877
7399
|
if (prev !== "running" && prev !== "starting") {
|
|
6878
7400
|
return;
|
|
@@ -6935,7 +7457,24 @@ var init_manager3 = __esm({
|
|
|
6935
7457
|
return;
|
|
6936
7458
|
}
|
|
6937
7459
|
const activeSession = this.sessions.get(session.id);
|
|
6938
|
-
if (activeSession
|
|
7460
|
+
if (!activeSession) {
|
|
7461
|
+
return;
|
|
7462
|
+
}
|
|
7463
|
+
if (activeSession.pendingResumeAggregation && activeSession.state !== "idle") {
|
|
7464
|
+
this.clearPendingResumeAggregation(activeSession);
|
|
7465
|
+
}
|
|
7466
|
+
if (activeSession.pendingResumeAggregation) {
|
|
7467
|
+
this.schedulePendingResumeAggregation(activeSession, event.chunk);
|
|
7468
|
+
detector.feed(event.chunk);
|
|
7469
|
+
return;
|
|
7470
|
+
}
|
|
7471
|
+
const outputAssessment = this.assessTerminalOutput(activeSession, event.chunk);
|
|
7472
|
+
if (outputAssessment.shouldAggregateForResume) {
|
|
7473
|
+
this.schedulePendingResumeAggregation(activeSession, event.chunk);
|
|
7474
|
+
detector.feed(event.chunk);
|
|
7475
|
+
return;
|
|
7476
|
+
}
|
|
7477
|
+
if (outputAssessment.countsAsTurnOutput) {
|
|
6939
7478
|
activeSession.sawOutputSinceTurnStart = true;
|
|
6940
7479
|
}
|
|
6941
7480
|
detector.feed(event.chunk);
|
|
@@ -6952,6 +7491,7 @@ var init_manager3 = __esm({
|
|
|
6952
7491
|
this.comparators.delete(sessionId);
|
|
6953
7492
|
}
|
|
6954
7493
|
finishSession(session, exitCode) {
|
|
7494
|
+
this.clearPendingResumeAggregation(session);
|
|
6955
7495
|
const prev = session.state;
|
|
6956
7496
|
session.state = "ended";
|
|
6957
7497
|
session.endedAt = Date.now();
|
|
@@ -6983,6 +7523,8 @@ var init_manager3 = __esm({
|
|
|
6983
7523
|
latestSubmittedUserInput;
|
|
6984
7524
|
awaitingTurnCompletion = false;
|
|
6985
7525
|
sawOutputSinceTurnStart = false;
|
|
7526
|
+
recentInputEchoes = [];
|
|
7527
|
+
pendingResumeAggregation = null;
|
|
6986
7528
|
constructor(data) {
|
|
6987
7529
|
this.id = data.id;
|
|
6988
7530
|
this.workspaceId = data.workspaceId;
|
|
@@ -7025,17 +7567,17 @@ var init_manager3 = __esm({
|
|
|
7025
7567
|
});
|
|
7026
7568
|
|
|
7027
7569
|
// packages/server/src/storage/repositories/auth-login-block-repo.ts
|
|
7028
|
-
function
|
|
7570
|
+
function isRecord3(value) {
|
|
7029
7571
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7030
7572
|
}
|
|
7031
7573
|
function isAuthLoginBlockRecord(value) {
|
|
7032
|
-
if (!
|
|
7574
|
+
if (!isRecord3(value)) {
|
|
7033
7575
|
return false;
|
|
7034
7576
|
}
|
|
7035
7577
|
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
7578
|
}
|
|
7037
7579
|
function normalizeFailures(value) {
|
|
7038
|
-
if (!
|
|
7580
|
+
if (!isRecord3(value)) {
|
|
7039
7581
|
return {};
|
|
7040
7582
|
}
|
|
7041
7583
|
const normalized = {};
|
|
@@ -7049,9 +7591,9 @@ function normalizeFailures(value) {
|
|
|
7049
7591
|
return normalized;
|
|
7050
7592
|
}
|
|
7051
7593
|
function normalizeState(value) {
|
|
7052
|
-
if (
|
|
7594
|
+
if (isRecord3(value) && value.version === 1) {
|
|
7053
7595
|
const blocks = {};
|
|
7054
|
-
if (
|
|
7596
|
+
if (isRecord3(value.blocks)) {
|
|
7055
7597
|
for (const entry of Object.values(value.blocks)) {
|
|
7056
7598
|
if (isAuthLoginBlockRecord(entry)) {
|
|
7057
7599
|
blocks[entry.ip] = entry;
|
|
@@ -7064,7 +7606,7 @@ function normalizeState(value) {
|
|
|
7064
7606
|
failures: normalizeFailures(value.failures)
|
|
7065
7607
|
};
|
|
7066
7608
|
}
|
|
7067
|
-
if (
|
|
7609
|
+
if (isRecord3(value)) {
|
|
7068
7610
|
const blocks = {};
|
|
7069
7611
|
for (const [ip, entry] of Object.entries(value)) {
|
|
7070
7612
|
if (isAuthLoginBlockRecord(entry)) {
|
|
@@ -7170,17 +7712,17 @@ var init_auth_login_block_repo = __esm({
|
|
|
7170
7712
|
});
|
|
7171
7713
|
|
|
7172
7714
|
// packages/server/src/storage/repositories/auth-session-repo.ts
|
|
7173
|
-
function
|
|
7715
|
+
function isRecord4(value) {
|
|
7174
7716
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7175
7717
|
}
|
|
7176
7718
|
function isAuthSession(value) {
|
|
7177
|
-
if (!
|
|
7719
|
+
if (!isRecord4(value)) {
|
|
7178
7720
|
return false;
|
|
7179
7721
|
}
|
|
7180
7722
|
return typeof value.token === "string" && typeof value.createdAt === "number" && typeof value.lastSeenAt === "number";
|
|
7181
7723
|
}
|
|
7182
7724
|
function normalizeSessionFile2(value) {
|
|
7183
|
-
if (
|
|
7725
|
+
if (isRecord4(value) && value.version === 1 && isRecord4(value.sessions)) {
|
|
7184
7726
|
const normalized = {};
|
|
7185
7727
|
for (const entry of Object.values(value.sessions)) {
|
|
7186
7728
|
if (isAuthSession(entry)) {
|
|
@@ -7198,7 +7740,7 @@ function normalizeSessionFile2(value) {
|
|
|
7198
7740
|
}
|
|
7199
7741
|
return normalized;
|
|
7200
7742
|
}
|
|
7201
|
-
if (
|
|
7743
|
+
if (isRecord4(value)) {
|
|
7202
7744
|
const normalized = {};
|
|
7203
7745
|
for (const [token, entry] of Object.entries(value)) {
|
|
7204
7746
|
if (isAuthSession(entry)) {
|
|
@@ -7273,14 +7815,14 @@ var init_auth_session_repo = __esm({
|
|
|
7273
7815
|
});
|
|
7274
7816
|
|
|
7275
7817
|
// packages/server/src/storage/repositories/provider-config-repo.ts
|
|
7276
|
-
function
|
|
7818
|
+
function isRecord5(value) {
|
|
7277
7819
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7278
7820
|
}
|
|
7279
7821
|
function normalizeProviderConfigFile(value) {
|
|
7280
|
-
if (
|
|
7822
|
+
if (isRecord5(value) && value.version === 1 && isRecord5(value.providers)) {
|
|
7281
7823
|
return value.providers;
|
|
7282
7824
|
}
|
|
7283
|
-
if (
|
|
7825
|
+
if (isRecord5(value)) {
|
|
7284
7826
|
return value;
|
|
7285
7827
|
}
|
|
7286
7828
|
return {};
|
|
@@ -7354,14 +7896,14 @@ var init_provider_config_repo = __esm({
|
|
|
7354
7896
|
});
|
|
7355
7897
|
|
|
7356
7898
|
// packages/server/src/storage/repositories/settings-repo.ts
|
|
7357
|
-
function
|
|
7899
|
+
function isRecord6(value) {
|
|
7358
7900
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7359
7901
|
}
|
|
7360
7902
|
function normalizeSettingsFile(value) {
|
|
7361
|
-
if (
|
|
7903
|
+
if (isRecord6(value) && value.version === 1 && isRecord6(value.settings)) {
|
|
7362
7904
|
return { ...value.settings };
|
|
7363
7905
|
}
|
|
7364
|
-
if (
|
|
7906
|
+
if (isRecord6(value)) {
|
|
7365
7907
|
return { ...value };
|
|
7366
7908
|
}
|
|
7367
7909
|
return {};
|
|
@@ -7512,11 +8054,11 @@ var init_supervisor_repo = __esm({
|
|
|
7512
8054
|
});
|
|
7513
8055
|
|
|
7514
8056
|
// packages/server/src/storage/repositories/terminal-repo.ts
|
|
7515
|
-
function
|
|
8057
|
+
function isRecord7(value) {
|
|
7516
8058
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7517
8059
|
}
|
|
7518
8060
|
function isTerminal(value) {
|
|
7519
|
-
if (!
|
|
8061
|
+
if (!isRecord7(value)) {
|
|
7520
8062
|
return false;
|
|
7521
8063
|
}
|
|
7522
8064
|
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 +8070,7 @@ function normalizeTerminal(value) {
|
|
|
7528
8070
|
};
|
|
7529
8071
|
}
|
|
7530
8072
|
function normalizeTerminalFile(value) {
|
|
7531
|
-
if (
|
|
8073
|
+
if (isRecord7(value) && value.version === 1 && isRecord7(value.terminals)) {
|
|
7532
8074
|
const normalized = {};
|
|
7533
8075
|
for (const entry of Object.values(value.terminals)) {
|
|
7534
8076
|
if (isTerminal(entry)) {
|
|
@@ -7546,7 +8088,7 @@ function normalizeTerminalFile(value) {
|
|
|
7546
8088
|
}
|
|
7547
8089
|
return normalized;
|
|
7548
8090
|
}
|
|
7549
|
-
if (
|
|
8091
|
+
if (isRecord7(value)) {
|
|
7550
8092
|
const normalized = {};
|
|
7551
8093
|
for (const [terminalId, entry] of Object.entries(value)) {
|
|
7552
8094
|
if (isTerminal(entry)) {
|
|
@@ -7712,12 +8254,12 @@ var init_terminal_repo = __esm({
|
|
|
7712
8254
|
});
|
|
7713
8255
|
|
|
7714
8256
|
// packages/server/src/storage/repositories/update-state-repo.ts
|
|
7715
|
-
function
|
|
8257
|
+
function isRecord8(value) {
|
|
7716
8258
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7717
8259
|
}
|
|
7718
8260
|
function normalizeUpdateState(value, currentVersion) {
|
|
7719
8261
|
const defaults = createDefaultUpdateState(currentVersion);
|
|
7720
|
-
if (!
|
|
8262
|
+
if (!isRecord8(value)) {
|
|
7721
8263
|
return defaults;
|
|
7722
8264
|
}
|
|
7723
8265
|
return {
|
|
@@ -7780,17 +8322,17 @@ var init_update_state_repo = __esm({
|
|
|
7780
8322
|
});
|
|
7781
8323
|
|
|
7782
8324
|
// packages/server/src/storage/repositories/workspace-repo.ts
|
|
7783
|
-
function
|
|
8325
|
+
function isRecord9(value) {
|
|
7784
8326
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7785
8327
|
}
|
|
7786
8328
|
function isWorkspace(value) {
|
|
7787
|
-
if (!
|
|
8329
|
+
if (!isRecord9(value)) {
|
|
7788
8330
|
return false;
|
|
7789
8331
|
}
|
|
7790
|
-
return typeof value.id === "string" && typeof value.path === "string" && (value.targetRuntime === "native" || value.targetRuntime === "wsl") && typeof value.openedAt === "number" && typeof value.lastActiveAt === "number" &&
|
|
8332
|
+
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
8333
|
}
|
|
7792
8334
|
function normalizeWorkspaceFile(value) {
|
|
7793
|
-
if (
|
|
8335
|
+
if (isRecord9(value) && value.version === 1 && isRecord9(value.workspaces)) {
|
|
7794
8336
|
const normalized = {};
|
|
7795
8337
|
for (const entry of Object.values(value.workspaces)) {
|
|
7796
8338
|
if (isWorkspace(entry)) {
|
|
@@ -7808,7 +8350,7 @@ function normalizeWorkspaceFile(value) {
|
|
|
7808
8350
|
}
|
|
7809
8351
|
return normalized;
|
|
7810
8352
|
}
|
|
7811
|
-
if (
|
|
8353
|
+
if (isRecord9(value)) {
|
|
7812
8354
|
const normalized = {};
|
|
7813
8355
|
for (const [workspaceId, entry] of Object.entries(value)) {
|
|
7814
8356
|
if (isWorkspace(entry)) {
|
|
@@ -8053,13 +8595,13 @@ function ensureNodePtySpawnHelperExecutable(deps = {}) {
|
|
|
8053
8595
|
return;
|
|
8054
8596
|
}
|
|
8055
8597
|
const arch = deps.arch ?? process.arch;
|
|
8056
|
-
const
|
|
8598
|
+
const resolve6 = deps.resolve ?? ((id) => require4.resolve(id));
|
|
8057
8599
|
const fileExists = deps.existsSync ?? existsSync6;
|
|
8058
|
-
const
|
|
8600
|
+
const stat11 = deps.statSync ?? statSync;
|
|
8059
8601
|
const chmod = deps.chmodSync ?? chmodSync2;
|
|
8060
8602
|
let packageJsonPath;
|
|
8061
8603
|
try {
|
|
8062
|
-
packageJsonPath =
|
|
8604
|
+
packageJsonPath = resolve6(NODE_PTY_PKG);
|
|
8063
8605
|
} catch {
|
|
8064
8606
|
return;
|
|
8065
8607
|
}
|
|
@@ -8073,7 +8615,7 @@ function ensureNodePtySpawnHelperExecutable(deps = {}) {
|
|
|
8073
8615
|
if (!fileExists(helperPath)) {
|
|
8074
8616
|
return;
|
|
8075
8617
|
}
|
|
8076
|
-
const currentMode =
|
|
8618
|
+
const currentMode = stat11(helperPath).mode;
|
|
8077
8619
|
const executableMode = currentMode | 73;
|
|
8078
8620
|
if (executableMode === currentMode) {
|
|
8079
8621
|
return;
|
|
@@ -8124,7 +8666,7 @@ async function escalateKillWithPolling(pid, signal, options) {
|
|
|
8124
8666
|
const startTime = Date.now();
|
|
8125
8667
|
const deadline = startTime + timeoutMs;
|
|
8126
8668
|
while (Date.now() < deadline) {
|
|
8127
|
-
await new Promise((
|
|
8669
|
+
await new Promise((resolve6) => setTimeout(resolve6, pollIntervalMs));
|
|
8128
8670
|
if (!isProcessAlive(pid)) {
|
|
8129
8671
|
return true;
|
|
8130
8672
|
}
|
|
@@ -8279,13 +8821,19 @@ import { spawn as spawn3 } from "node:child_process";
|
|
|
8279
8821
|
function buildPrompt(context, mode) {
|
|
8280
8822
|
if (mode === "decompose") {
|
|
8281
8823
|
return [
|
|
8282
|
-
"You are an autonomous supervisor for
|
|
8824
|
+
"You are an autonomous planner-supervisor for this target-scoped software task.",
|
|
8825
|
+
"Your purpose is to drive the work from objective to high-quality delivery with minimal babysitting.",
|
|
8826
|
+
'Do not optimize for merely reaching "done"; optimize for a result that is correct, verified, coherent, and not obviously low-quality or rushed.',
|
|
8283
8827
|
"Your first job is to decompose the target into a supervision structure before evaluation begins.",
|
|
8284
8828
|
"",
|
|
8285
8829
|
"Return JSON only.",
|
|
8286
8830
|
"No prose before or after the JSON.",
|
|
8287
8831
|
"",
|
|
8288
8832
|
"Decomposition policy:",
|
|
8833
|
+
"- Create an execution plan, not just a task list.",
|
|
8834
|
+
"- Break the objective into the smallest reasonable set of milestones that maximize clarity, reduce uncertainty, and preserve steady forward progress.",
|
|
8835
|
+
"- Order milestones by dependency, risk reduction, and delivery leverage.",
|
|
8836
|
+
"- Prefer a plan structure that makes execution easier, verification clearer, and replanning cheaper.",
|
|
8289
8837
|
"- Do not ask the user any questions.",
|
|
8290
8838
|
"- Do not ask for clarification, confirmation, or approval.",
|
|
8291
8839
|
"- Do not propose options for the user to choose from.",
|
|
@@ -8299,6 +8847,30 @@ function buildPrompt(context, mode) {
|
|
|
8299
8847
|
"- Each item must be concrete, milestone-sized, and useful for subsequent evaluation.",
|
|
8300
8848
|
"- Do not leave the structure empty.",
|
|
8301
8849
|
"",
|
|
8850
|
+
"Decomposition principles:",
|
|
8851
|
+
"- Prefer milestones that produce a concrete artifact, observable behavior change, test result, or verification result.",
|
|
8852
|
+
"- Make dependencies explicit.",
|
|
8853
|
+
"- Separate implementation, verification, integration, and cleanup when that improves delivery reliability.",
|
|
8854
|
+
"- If a step is too vague to verify independently, split it further.",
|
|
8855
|
+
"- Prefer plans that keep the agent moving with minimal ambiguity between milestones.",
|
|
8856
|
+
"- Use stage-based planning by default unless there are clearly independent deliverables that justify subtargets.",
|
|
8857
|
+
"- Build the plan so it can recover from failed attempts: prefer decompositions that allow narrowing scope, isolating failures, checking assumptions, and restoring a working baseline when needed.",
|
|
8858
|
+
"- Keep the decomposition practical for execution, not merely neat on paper.",
|
|
8859
|
+
"",
|
|
8860
|
+
"Planning quality bar:",
|
|
8861
|
+
"- Prefer fewer, stronger milestones over many thin or vague ones.",
|
|
8862
|
+
"- Every item should imply a concrete deliverable and observable acceptance criteria.",
|
|
8863
|
+
'- Avoid vague items such as "improve", "clean up", or "refactor" unless tied to a specific delivery or verification target.',
|
|
8864
|
+
"- Include quality and verification checkpoints where they materially improve the final result.",
|
|
8865
|
+
"- Do not decompose in a way that encourages superficial completion.",
|
|
8866
|
+
"",
|
|
8867
|
+
"Planning boundary:",
|
|
8868
|
+
"- You are responsible for execution structure, sequencing, quality control, and verification structure.",
|
|
8869
|
+
"- Do not hard-code unnecessary implementation detail too early.",
|
|
8870
|
+
"- If multiple implementation paths exist, prefer a plan that keeps execution adaptable until evidence makes one path clearly better.",
|
|
8871
|
+
"- Do not hide assumptions inside the plan.",
|
|
8872
|
+
"- Do not create a brittle plan that depends on perfect execution.",
|
|
8873
|
+
"",
|
|
8302
8874
|
"Item requirements:",
|
|
8303
8875
|
'- Each item must include "id", "kind", "title", "objective", "deliverable", "acceptanceCriteria", and "status".',
|
|
8304
8876
|
'- "kind" must match the selected decompositionMode: all "stage" or all "subtarget".',
|
|
@@ -8331,8 +8903,13 @@ function buildPrompt(context, mode) {
|
|
|
8331
8903
|
].join("\n");
|
|
8332
8904
|
}
|
|
8333
8905
|
const lines = [
|
|
8334
|
-
"You are an autonomous supervisor for
|
|
8335
|
-
"Your
|
|
8906
|
+
"You are an autonomous planner-supervisor for this target-scoped software task.",
|
|
8907
|
+
"Your purpose is to drive the work from objective to high-quality delivery with minimal babysitting.",
|
|
8908
|
+
'Do not optimize for merely reaching "done"; optimize for a result that is correct, verified, coherent, and not obviously low-quality or rushed.',
|
|
8909
|
+
"Act as an autonomous execution supervisor.",
|
|
8910
|
+
"Your job is to keep the agent moving toward the objective, maintain delivery quality, detect low-yield paths early, and redirect work when needed.",
|
|
8911
|
+
"Do not passively observe progress; actively steer it toward successful, high-quality completion.",
|
|
8912
|
+
"Drive execution through the supervised agent rather than by independently performing the work yourself.",
|
|
8336
8913
|
"",
|
|
8337
8914
|
"Return JSON only.",
|
|
8338
8915
|
"No prose before or after the JSON.",
|
|
@@ -8363,7 +8940,12 @@ function buildPrompt(context, mode) {
|
|
|
8363
8940
|
'- When advancing to the next item, mark the previous item as "done" and set activeItemId to the next item explicitly.',
|
|
8364
8941
|
"- If the active item is blocked, give guidance that is most likely to unblock it.",
|
|
8365
8942
|
"- If the active item is obsolete, explain the reason briefly and move to the next useful item.",
|
|
8366
|
-
"- If the
|
|
8943
|
+
"- If the current path is low-yield, brittle, repetitive, or producing low-quality output, redirect early.",
|
|
8944
|
+
"- Diagnose stalls precisely: implementation failure, verification failure, environment failure, scope misframing, weak solution quality, or missing evidence.",
|
|
8945
|
+
"- Choose the next action that most improves objective-level progress, not merely the most local continuation.",
|
|
8946
|
+
"- Do not repeat the same tactic after failure unless new evidence justifies retrying it.",
|
|
8947
|
+
"- Maintain commitment to the objective, not blind commitment to the current tactic.",
|
|
8948
|
+
"- Replan locally when needed, but keep the overall execution coherent and objective-driven.",
|
|
8367
8949
|
"- Do not rewrite the decomposition structure during normal evaluation cycles.",
|
|
8368
8950
|
"",
|
|
8369
8951
|
"Allowed statuses:",
|
|
@@ -8388,6 +8970,21 @@ function buildPrompt(context, mode) {
|
|
|
8388
8970
|
"- If implementation is needed, point to the likely area, behavior, or file/module based on available evidence.",
|
|
8389
8971
|
"- If the agent asked a question, answer it directly in the guidance and continue with a concrete next action.",
|
|
8390
8972
|
"",
|
|
8973
|
+
"Delivery quality bar:",
|
|
8974
|
+
"- Do not accept shallow, brittle, or obviously rushed solutions.",
|
|
8975
|
+
"- Do not optimize for the smallest change if it leads to poor maintainability, weak verification, or fragile behavior.",
|
|
8976
|
+
"- Prefer solutions that are robust, coherent with the existing codebase, and likely to hold up beyond the happy path.",
|
|
8977
|
+
"- Require appropriate verification for the kind of work being done.",
|
|
8978
|
+
"- Consider edge cases, integration impact, regressions, and maintainability where relevant.",
|
|
8979
|
+
"- If a solution technically works but is low-quality, incomplete, poorly verified, or obviously a shortcut, treat the milestone as not yet complete.",
|
|
8980
|
+
"- Do not let superficial progress masquerade as real delivery.",
|
|
8981
|
+
"",
|
|
8982
|
+
"Completion standard:",
|
|
8983
|
+
"- A milestone is complete only when its deliverable and acceptanceCriteria are supported by observable evidence and the result meets a reasonable quality bar.",
|
|
8984
|
+
"- The objective is complete only when the final result is implemented, verified, and not obviously compromised in quality.",
|
|
8985
|
+
"- Do not mark work complete merely because code was changed, a command passed once, or a minimal patch exists.",
|
|
8986
|
+
"- Optimize for finished, verified, and defensible delivery.",
|
|
8987
|
+
"",
|
|
8391
8988
|
"Evaluation policy:",
|
|
8392
8989
|
"- Update progress incrementally against the existing decomposition.",
|
|
8393
8990
|
"- Use itemUpdates to reflect evidence-backed status changes only.",
|
|
@@ -8433,7 +9030,7 @@ async function runCommand(command, timeoutMs, options = {}) {
|
|
|
8433
9030
|
if (options.signal?.aborted) {
|
|
8434
9031
|
throw createSupervisorEvalAbortedError();
|
|
8435
9032
|
}
|
|
8436
|
-
return await new Promise((
|
|
9033
|
+
return await new Promise((resolve6, reject) => {
|
|
8437
9034
|
const child = spawn3(command.argv[0], command.argv.slice(1), {
|
|
8438
9035
|
cwd: command.cwd,
|
|
8439
9036
|
detached: process.platform !== "win32",
|
|
@@ -8463,7 +9060,7 @@ async function runCommand(command, timeoutMs, options = {}) {
|
|
|
8463
9060
|
}
|
|
8464
9061
|
settled = true;
|
|
8465
9062
|
cleanup();
|
|
8466
|
-
|
|
9063
|
+
resolve6(value);
|
|
8467
9064
|
};
|
|
8468
9065
|
const terminate = (error) => {
|
|
8469
9066
|
if (terminationError) {
|
|
@@ -9291,12 +9888,12 @@ var init_scheduler = __esm({
|
|
|
9291
9888
|
|
|
9292
9889
|
// packages/server/src/supervisor/manager.ts
|
|
9293
9890
|
function createDeferredCompletion() {
|
|
9294
|
-
let
|
|
9891
|
+
let resolve6 = () => {
|
|
9295
9892
|
};
|
|
9296
9893
|
const promise = new Promise((innerResolve) => {
|
|
9297
|
-
|
|
9894
|
+
resolve6 = innerResolve;
|
|
9298
9895
|
});
|
|
9299
|
-
return { promise, resolve:
|
|
9896
|
+
return { promise, resolve: resolve6 };
|
|
9300
9897
|
}
|
|
9301
9898
|
function generateSupervisorId() {
|
|
9302
9899
|
return `sup_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
@@ -10651,10 +11248,10 @@ var init_manager4 = __esm({
|
|
|
10651
11248
|
if (signal?.aborted) {
|
|
10652
11249
|
throw { code: "supervisor_eval_aborted", message: "Supervisor evaluator aborted" };
|
|
10653
11250
|
}
|
|
10654
|
-
await new Promise((
|
|
11251
|
+
await new Promise((resolve6, reject) => {
|
|
10655
11252
|
const timer = setTimeout(() => {
|
|
10656
11253
|
signal?.removeEventListener("abort", onAbort);
|
|
10657
|
-
|
|
11254
|
+
resolve6();
|
|
10658
11255
|
}, delayMs);
|
|
10659
11256
|
timer.unref?.();
|
|
10660
11257
|
const onAbort = () => {
|
|
@@ -10685,31 +11282,31 @@ __export(target_store_exports, {
|
|
|
10685
11282
|
saveTargetMemory: () => saveTargetMemory,
|
|
10686
11283
|
saveTargetMeta: () => saveTargetMeta
|
|
10687
11284
|
});
|
|
10688
|
-
import { mkdir as mkdir4, mkdtemp as mkdtemp2, readdir as readdir2, readFile as readFile3, rename, rm as
|
|
10689
|
-
import { dirname as dirname6, join as
|
|
11285
|
+
import { mkdir as mkdir4, mkdtemp as mkdtemp2, readdir as readdir2, readFile as readFile3, rename, rm as rm6, writeFile as writeFile4 } from "node:fs/promises";
|
|
11286
|
+
import { dirname as dirname6, join as join9 } from "node:path";
|
|
10690
11287
|
function targetDir(workspacePath, targetId) {
|
|
10691
|
-
return
|
|
11288
|
+
return join9(workspacePath, ".coder-studio", "supervisor", "targets", targetId);
|
|
10692
11289
|
}
|
|
10693
11290
|
function metaPath(workspacePath, targetId) {
|
|
10694
|
-
return
|
|
11291
|
+
return join9(targetDir(workspacePath, targetId), "meta.json");
|
|
10695
11292
|
}
|
|
10696
11293
|
function memoryPath(workspacePath, targetId) {
|
|
10697
|
-
return
|
|
11294
|
+
return join9(targetDir(workspacePath, targetId), "memory.json");
|
|
10698
11295
|
}
|
|
10699
11296
|
function cyclesPath(workspacePath, targetId) {
|
|
10700
|
-
return
|
|
11297
|
+
return join9(targetDir(workspacePath, targetId), "cycles.jsonl");
|
|
10701
11298
|
}
|
|
10702
11299
|
function targetsRoot(workspacePath) {
|
|
10703
|
-
return
|
|
11300
|
+
return join9(workspacePath, ".coder-studio", "supervisor", "targets");
|
|
10704
11301
|
}
|
|
10705
11302
|
function metaFilePath(dirPath) {
|
|
10706
|
-
return
|
|
11303
|
+
return join9(dirPath, "meta.json");
|
|
10707
11304
|
}
|
|
10708
11305
|
function memoryFilePath(dirPath) {
|
|
10709
|
-
return
|
|
11306
|
+
return join9(dirPath, "memory.json");
|
|
10710
11307
|
}
|
|
10711
11308
|
function cyclesFilePath(dirPath) {
|
|
10712
|
-
return
|
|
11309
|
+
return join9(dirPath, "cycles.jsonl");
|
|
10713
11310
|
}
|
|
10714
11311
|
function hasCode2(error, code) {
|
|
10715
11312
|
return Boolean(
|
|
@@ -10728,7 +11325,7 @@ function errorMessage(error, fallback) {
|
|
|
10728
11325
|
}
|
|
10729
11326
|
return fallback;
|
|
10730
11327
|
}
|
|
10731
|
-
function
|
|
11328
|
+
function isRecord10(value) {
|
|
10732
11329
|
return Boolean(value) && typeof value === "object";
|
|
10733
11330
|
}
|
|
10734
11331
|
function readNonEmptyString(value) {
|
|
@@ -10772,7 +11369,7 @@ function fallbackAcceptanceCriteria(title) {
|
|
|
10772
11369
|
return [`${title} is complete`];
|
|
10773
11370
|
}
|
|
10774
11371
|
function normalizeItem(value, fallbackKind) {
|
|
10775
|
-
if (!
|
|
11372
|
+
if (!isRecord10(value)) {
|
|
10776
11373
|
return null;
|
|
10777
11374
|
}
|
|
10778
11375
|
const id = readNonEmptyString(value.id);
|
|
@@ -10803,7 +11400,7 @@ function normalizeLegacyPlanItems(plan) {
|
|
|
10803
11400
|
}
|
|
10804
11401
|
return plan.flatMap((value) => {
|
|
10805
11402
|
const item = normalizeItem(
|
|
10806
|
-
|
|
11403
|
+
isRecord10(value) ? {
|
|
10807
11404
|
id: value.id,
|
|
10808
11405
|
kind: "stage",
|
|
10809
11406
|
title: value.title,
|
|
@@ -10827,7 +11424,7 @@ function resolveActiveItemId(items, candidate) {
|
|
|
10827
11424
|
return items.find((item) => item.status === "in_progress")?.id ?? items.find((item) => item.status === "pending")?.id ?? items[0]?.id;
|
|
10828
11425
|
}
|
|
10829
11426
|
function normalizeTargetMemory(raw, targetId) {
|
|
10830
|
-
if (!
|
|
11427
|
+
if (!isRecord10(raw)) {
|
|
10831
11428
|
return buildTargetMemory(targetId, 0);
|
|
10832
11429
|
}
|
|
10833
11430
|
const updatedAt = readTimestamp(raw.updatedAt, 0);
|
|
@@ -10854,7 +11451,7 @@ function normalizeTargetMemory(raw, targetId) {
|
|
|
10854
11451
|
};
|
|
10855
11452
|
}
|
|
10856
11453
|
function normalizePersistedSupervisor(raw, fallback) {
|
|
10857
|
-
if (!
|
|
11454
|
+
if (!isRecord10(raw)) {
|
|
10858
11455
|
return void 0;
|
|
10859
11456
|
}
|
|
10860
11457
|
const id = readNonEmptyString(raw.id) ?? fallback.targetId;
|
|
@@ -10890,7 +11487,7 @@ function normalizePersistedSupervisor(raw, fallback) {
|
|
|
10890
11487
|
};
|
|
10891
11488
|
}
|
|
10892
11489
|
function normalizeTargetMeta(raw, fallbackTargetId) {
|
|
10893
|
-
if (!
|
|
11490
|
+
if (!isRecord10(raw)) {
|
|
10894
11491
|
const targetId2 = fallbackTargetId ?? "";
|
|
10895
11492
|
return {
|
|
10896
11493
|
targetId: targetId2,
|
|
@@ -11011,7 +11608,7 @@ async function resetTargetFiles(workspacePath, input) {
|
|
|
11011
11608
|
const parentDir = dirname6(dir);
|
|
11012
11609
|
const backupDir = `${dir}.backup-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
11013
11610
|
await mkdir4(parentDir, { recursive: true });
|
|
11014
|
-
const stagingDir = await mkdtemp2(
|
|
11611
|
+
const stagingDir = await mkdtemp2(join9(parentDir, `${input.targetId}.reset-`));
|
|
11015
11612
|
let backupCreated = false;
|
|
11016
11613
|
let promoted = false;
|
|
11017
11614
|
let restored = false;
|
|
@@ -11047,17 +11644,17 @@ async function resetTargetFiles(workspacePath, input) {
|
|
|
11047
11644
|
}
|
|
11048
11645
|
} catch (error) {
|
|
11049
11646
|
if (restored || !backupCreated) {
|
|
11050
|
-
await
|
|
11647
|
+
await rm6(stagingDir, { recursive: true, force: true }).catch(() => {
|
|
11051
11648
|
});
|
|
11052
11649
|
}
|
|
11053
11650
|
throw error;
|
|
11054
11651
|
}
|
|
11055
11652
|
if (backupCreated) {
|
|
11056
|
-
await
|
|
11653
|
+
await rm6(backupDir, { recursive: true, force: true }).catch(() => {
|
|
11057
11654
|
});
|
|
11058
11655
|
}
|
|
11059
11656
|
if (!promoted) {
|
|
11060
|
-
await
|
|
11657
|
+
await rm6(stagingDir, { recursive: true, force: true }).catch(() => {
|
|
11061
11658
|
});
|
|
11062
11659
|
}
|
|
11063
11660
|
}
|
|
@@ -11155,7 +11752,7 @@ async function cloneTargetFiles(workspacePath, input) {
|
|
|
11155
11752
|
return nextCycles.filter(countsTowardCycleTotal).length;
|
|
11156
11753
|
}
|
|
11157
11754
|
async function deleteTarget(workspacePath, targetId) {
|
|
11158
|
-
await
|
|
11755
|
+
await rm6(targetDir(workspacePath, targetId), { recursive: true, force: true });
|
|
11159
11756
|
}
|
|
11160
11757
|
async function markTargetSuperseded(workspacePath, targetId, nextTargetId, updatedAt) {
|
|
11161
11758
|
const meta = await readTargetMeta(workspacePath, targetId);
|
|
@@ -11468,8 +12065,8 @@ var init_terminal_snapshot_buffer = __esm({
|
|
|
11468
12065
|
if (this.pendingWriteCount === 0) {
|
|
11469
12066
|
return Promise.resolve();
|
|
11470
12067
|
}
|
|
11471
|
-
return new Promise((
|
|
11472
|
-
this.drainResolvers.push(
|
|
12068
|
+
return new Promise((resolve6) => {
|
|
12069
|
+
this.drainResolvers.push(resolve6);
|
|
11473
12070
|
});
|
|
11474
12071
|
}
|
|
11475
12072
|
resolveDrainIfIdle() {
|
|
@@ -11478,8 +12075,8 @@ var init_terminal_snapshot_buffer = __esm({
|
|
|
11478
12075
|
}
|
|
11479
12076
|
const resolvers = this.drainResolvers;
|
|
11480
12077
|
this.drainResolvers = [];
|
|
11481
|
-
for (const
|
|
11482
|
-
|
|
12078
|
+
for (const resolve6 of resolvers) {
|
|
12079
|
+
resolve6();
|
|
11483
12080
|
}
|
|
11484
12081
|
}
|
|
11485
12082
|
requireTerminal() {
|
|
@@ -11798,10 +12395,10 @@ var init_manager5 = __esm({
|
|
|
11798
12395
|
}
|
|
11799
12396
|
return existing.promise;
|
|
11800
12397
|
}
|
|
11801
|
-
let
|
|
12398
|
+
let resolve6 = () => {
|
|
11802
12399
|
};
|
|
11803
12400
|
const promise = new Promise((innerResolve) => {
|
|
11804
|
-
|
|
12401
|
+
resolve6 = innerResolve;
|
|
11805
12402
|
});
|
|
11806
12403
|
let markKillCompleted = () => {
|
|
11807
12404
|
};
|
|
@@ -11814,7 +12411,7 @@ var init_manager5 = __esm({
|
|
|
11814
12411
|
markKillCompleted,
|
|
11815
12412
|
finalized: false,
|
|
11816
12413
|
promise,
|
|
11817
|
-
resolve:
|
|
12414
|
+
resolve: resolve6
|
|
11818
12415
|
});
|
|
11819
12416
|
void terminal.pty.kill(signal).finally(() => {
|
|
11820
12417
|
const waiter = this.explicitCloseWaiters.get(terminalId);
|
|
@@ -12290,10 +12887,10 @@ var init_update_service = __esm({
|
|
|
12290
12887
|
|
|
12291
12888
|
// packages/server/src/workspace/validator.ts
|
|
12292
12889
|
import { constants } from "fs";
|
|
12293
|
-
import { access, stat as
|
|
12890
|
+
import { access, stat as stat7 } from "fs/promises";
|
|
12294
12891
|
async function validatePath(path14) {
|
|
12295
12892
|
try {
|
|
12296
|
-
const stats = await
|
|
12893
|
+
const stats = await stat7(path14);
|
|
12297
12894
|
if (!stats.isDirectory()) {
|
|
12298
12895
|
return { valid: false, error: "Path is not a directory" };
|
|
12299
12896
|
}
|
|
@@ -12328,12 +12925,12 @@ var init_validator = __esm({
|
|
|
12328
12925
|
// packages/server/src/fs/gitignore.ts
|
|
12329
12926
|
import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
|
|
12330
12927
|
import ignore from "ignore";
|
|
12331
|
-
import { join as
|
|
12928
|
+
import { join as join10, relative as relative2 } from "path";
|
|
12332
12929
|
function normalizePath(path14) {
|
|
12333
12930
|
return path14.replace(/\\/g, "/");
|
|
12334
12931
|
}
|
|
12335
12932
|
function relativeToRoot(rootPath, path14) {
|
|
12336
|
-
return normalizePath(
|
|
12933
|
+
return normalizePath(relative2(rootPath, path14));
|
|
12337
12934
|
}
|
|
12338
12935
|
function isDefaultTreeIgnored(name) {
|
|
12339
12936
|
return name.startsWith(".") || name === "node_modules" || name === ".git";
|
|
@@ -12351,7 +12948,7 @@ function isIgnoredByGitignore(ig, path14) {
|
|
|
12351
12948
|
return ig.ignores(path14) || ig.ignores(`${path14}/`);
|
|
12352
12949
|
}
|
|
12353
12950
|
function createGitignoreFilter(rootPath, dirPath) {
|
|
12354
|
-
const gitignorePath =
|
|
12951
|
+
const gitignorePath = join10(rootPath, ".gitignore");
|
|
12355
12952
|
if (!existsSync7(gitignorePath)) {
|
|
12356
12953
|
return (name) => !isDefaultTreeIgnored(name);
|
|
12357
12954
|
}
|
|
@@ -12361,7 +12958,7 @@ function createGitignoreFilter(rootPath, dirPath) {
|
|
|
12361
12958
|
if (isAlwaysTreeIgnored(name)) {
|
|
12362
12959
|
return false;
|
|
12363
12960
|
}
|
|
12364
|
-
const relativePath = relativeToRoot(rootPath,
|
|
12961
|
+
const relativePath = relativeToRoot(rootPath, join10(dirPath, name));
|
|
12365
12962
|
return !isIgnoredByGitignore(ig, relativePath);
|
|
12366
12963
|
};
|
|
12367
12964
|
}
|
|
@@ -13032,8 +13629,8 @@ var init_fencing = __esm({
|
|
|
13032
13629
|
});
|
|
13033
13630
|
|
|
13034
13631
|
// packages/server/src/commands/terminal.ts
|
|
13035
|
-
import { stat as
|
|
13036
|
-
import { basename, isAbsolute } from "node:path";
|
|
13632
|
+
import { stat as stat8 } from "node:fs/promises";
|
|
13633
|
+
import { basename, isAbsolute as isAbsolute2 } from "node:path";
|
|
13037
13634
|
import { z as z6 } from "zod";
|
|
13038
13635
|
function decodeTerminalInput(args) {
|
|
13039
13636
|
if ("bytes" in args) {
|
|
@@ -13149,7 +13746,7 @@ var init_terminal = __esm({
|
|
|
13149
13746
|
}
|
|
13150
13747
|
let cwd = workspace.path;
|
|
13151
13748
|
if (args.cwdPath && args.cwdPath !== ".") {
|
|
13152
|
-
if (
|
|
13749
|
+
if (isAbsolute2(args.cwdPath)) {
|
|
13153
13750
|
throw { code: "invalid_cwd_path", message: "cwdPath must be workspace-relative" };
|
|
13154
13751
|
}
|
|
13155
13752
|
let resolvedCwd;
|
|
@@ -13161,7 +13758,7 @@ var init_terminal = __esm({
|
|
|
13161
13758
|
}
|
|
13162
13759
|
throw error;
|
|
13163
13760
|
}
|
|
13164
|
-
const cwdStats = await
|
|
13761
|
+
const cwdStats = await stat8(resolvedCwd).catch(() => null);
|
|
13165
13762
|
if (!cwdStats) {
|
|
13166
13763
|
throw { code: "cwd_not_found", message: `Directory not found: ${args.cwdPath}` };
|
|
13167
13764
|
}
|
|
@@ -13890,7 +14487,7 @@ var init_hub = __esm({
|
|
|
13890
14487
|
}
|
|
13891
14488
|
}
|
|
13892
14489
|
awaitBinaryPayload(clientId) {
|
|
13893
|
-
return new Promise((
|
|
14490
|
+
return new Promise((resolve6, reject) => {
|
|
13894
14491
|
const timer = setTimeout(() => {
|
|
13895
14492
|
const waiters = this.pendingBinaryWaiters.get(clientId);
|
|
13896
14493
|
if (!waiters) return;
|
|
@@ -13902,7 +14499,7 @@ var init_hub = __esm({
|
|
|
13902
14499
|
}
|
|
13903
14500
|
reject(new Error("Timeout waiting for terminal input binary payload"));
|
|
13904
14501
|
}, BINARY_PAYLOAD_TIMEOUT_MS);
|
|
13905
|
-
const waiter = { resolve:
|
|
14502
|
+
const waiter = { resolve: resolve6, reject, timer };
|
|
13906
14503
|
const queue = this.pendingBinaryWaiters.get(clientId);
|
|
13907
14504
|
if (queue) {
|
|
13908
14505
|
queue.push(waiter);
|
|
@@ -14209,7 +14806,7 @@ var init_hub = __esm({
|
|
|
14209
14806
|
// packages/server/src/commands/workspace.ts
|
|
14210
14807
|
import { readdir as readdir3, realpath as realpath3 } from "node:fs/promises";
|
|
14211
14808
|
import { homedir as homedir2 } from "node:os";
|
|
14212
|
-
import { isAbsolute as
|
|
14809
|
+
import { isAbsolute as isAbsolute3, join as join11, resolve as resolve4 } from "node:path";
|
|
14213
14810
|
import { z as z7 } from "zod";
|
|
14214
14811
|
function resolveBrowsePath(path14) {
|
|
14215
14812
|
const home = homedir2();
|
|
@@ -14217,9 +14814,9 @@ function resolveBrowsePath(path14) {
|
|
|
14217
14814
|
return home;
|
|
14218
14815
|
}
|
|
14219
14816
|
if (path14.startsWith("~/")) {
|
|
14220
|
-
return
|
|
14817
|
+
return join11(home, path14.slice(2));
|
|
14221
14818
|
}
|
|
14222
|
-
return
|
|
14819
|
+
return isAbsolute3(path14) ? path14 : resolve4(home, path14);
|
|
14223
14820
|
}
|
|
14224
14821
|
async function buildRootPaths(currentPath) {
|
|
14225
14822
|
const roots = /* @__PURE__ */ new Set(["/"]);
|
|
@@ -14252,11 +14849,11 @@ var init_workspace = __esm({
|
|
|
14252
14849
|
const entries = await readdir3(basePath, { withFileTypes: true });
|
|
14253
14850
|
const directories = entries.filter((entry) => entry.isDirectory()).map((entry) => ({
|
|
14254
14851
|
name: entry.name,
|
|
14255
|
-
path:
|
|
14852
|
+
path: join11(basePath, entry.name)
|
|
14256
14853
|
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
14257
14854
|
return {
|
|
14258
14855
|
currentPath: basePath,
|
|
14259
|
-
parentPath: basePath !== "/" ?
|
|
14856
|
+
parentPath: basePath !== "/" ? join11(basePath, "..") : null,
|
|
14260
14857
|
directories,
|
|
14261
14858
|
rootPaths: await buildRootPaths(basePath)
|
|
14262
14859
|
};
|
|
@@ -14720,8 +15317,8 @@ var init_pane_layout = __esm({
|
|
|
14720
15317
|
// packages/server/src/commands/session.ts
|
|
14721
15318
|
import { z as z12 } from "zod";
|
|
14722
15319
|
function delay(ms) {
|
|
14723
|
-
return new Promise((
|
|
14724
|
-
setTimeout(
|
|
15320
|
+
return new Promise((resolve6) => {
|
|
15321
|
+
setTimeout(resolve6, ms);
|
|
14725
15322
|
});
|
|
14726
15323
|
}
|
|
14727
15324
|
function getProviderFromRegistry(providerId, registry) {
|
|
@@ -14872,20 +15469,260 @@ var init_session2 = __esm({
|
|
|
14872
15469
|
}
|
|
14873
15470
|
});
|
|
14874
15471
|
|
|
15472
|
+
// packages/server/src/fs/content-search.ts
|
|
15473
|
+
import { spawn as spawn5 } from "child_process";
|
|
15474
|
+
import { existsSync as existsSync8 } from "fs";
|
|
15475
|
+
import { readdir as readdir4, readFile as readFile4, stat as stat9 } from "fs/promises";
|
|
15476
|
+
import { basename as basename2, join as join12, relative as relative3 } from "path";
|
|
15477
|
+
import { createInterface } from "readline";
|
|
15478
|
+
async function searchFileContents(rootPath, options) {
|
|
15479
|
+
const query = options.query.trim();
|
|
15480
|
+
if (!query) {
|
|
15481
|
+
return {
|
|
15482
|
+
files: [],
|
|
15483
|
+
totalMatchCount: 0,
|
|
15484
|
+
hasMoreFiles: false,
|
|
15485
|
+
truncatedMatchFileCount: 0
|
|
15486
|
+
};
|
|
15487
|
+
}
|
|
15488
|
+
const result = await searchWithRipgrep(rootPath, query, options.maxFiles).catch(
|
|
15489
|
+
async (error) => {
|
|
15490
|
+
if (error.code === "ENOENT") {
|
|
15491
|
+
return searchWithNode(rootPath, query, options.maxFiles);
|
|
15492
|
+
}
|
|
15493
|
+
throw error;
|
|
15494
|
+
}
|
|
15495
|
+
);
|
|
15496
|
+
return finalizeResults(result, options.maxFiles, options.maxMatchesPerFile);
|
|
15497
|
+
}
|
|
15498
|
+
async function searchWithRipgrep(rootPath, query, maxFiles) {
|
|
15499
|
+
const hasGitignore = existsSync8(join12(rootPath, ".gitignore"));
|
|
15500
|
+
const args = [
|
|
15501
|
+
"--json",
|
|
15502
|
+
"--line-number",
|
|
15503
|
+
"--column",
|
|
15504
|
+
"--fixed-strings",
|
|
15505
|
+
"--sort",
|
|
15506
|
+
"path",
|
|
15507
|
+
"--with-filename",
|
|
15508
|
+
"--glob",
|
|
15509
|
+
"!**/.git/**",
|
|
15510
|
+
"--glob",
|
|
15511
|
+
"!**/node_modules/**"
|
|
15512
|
+
];
|
|
15513
|
+
if (hasGitignore) {
|
|
15514
|
+
args.push("--hidden");
|
|
15515
|
+
args.push("--no-require-git");
|
|
15516
|
+
}
|
|
15517
|
+
args.push(query, ".");
|
|
15518
|
+
return new Promise((resolve6, reject) => {
|
|
15519
|
+
const child = spawn5("rg", args, { cwd: rootPath, stdio: ["ignore", "pipe", "pipe"] });
|
|
15520
|
+
const stdout = createInterface({ input: child.stdout });
|
|
15521
|
+
const files = /* @__PURE__ */ new Map();
|
|
15522
|
+
let totalMatchCount = 0;
|
|
15523
|
+
let hasMoreFiles = false;
|
|
15524
|
+
let stderr = "";
|
|
15525
|
+
stdout.on("line", (line) => {
|
|
15526
|
+
if (!line.trim()) {
|
|
15527
|
+
return;
|
|
15528
|
+
}
|
|
15529
|
+
const event = JSON.parse(line);
|
|
15530
|
+
if (event.type !== "match") {
|
|
15531
|
+
return;
|
|
15532
|
+
}
|
|
15533
|
+
const rawPath = event.data?.path?.text;
|
|
15534
|
+
if (!rawPath) {
|
|
15535
|
+
return;
|
|
15536
|
+
}
|
|
15537
|
+
const relativePath = normalizeRelativePath(relative3(rootPath, join12(rootPath, rawPath)));
|
|
15538
|
+
const preview = (event.data?.lines?.text ?? "").replace(/\r?\n$/, "");
|
|
15539
|
+
const lineNumber = event.data?.line_number ?? 1;
|
|
15540
|
+
const submatches = event.data?.submatches ?? [];
|
|
15541
|
+
totalMatchCount += submatches.length;
|
|
15542
|
+
if (!files.has(relativePath) && files.size >= maxFiles) {
|
|
15543
|
+
hasMoreFiles = true;
|
|
15544
|
+
return;
|
|
15545
|
+
}
|
|
15546
|
+
for (const submatch of submatches) {
|
|
15547
|
+
pushMatch(files, relativePath, {
|
|
15548
|
+
line: lineNumber,
|
|
15549
|
+
column: byteOffsetToColumn(preview, submatch.start),
|
|
15550
|
+
endColumn: byteOffsetToColumn(preview, submatch.end),
|
|
15551
|
+
preview,
|
|
15552
|
+
previewColumnStart: byteOffsetToColumn(preview, submatch.start),
|
|
15553
|
+
previewColumnEnd: byteOffsetToColumn(preview, submatch.end)
|
|
15554
|
+
});
|
|
15555
|
+
}
|
|
15556
|
+
});
|
|
15557
|
+
child.stderr.on("data", (chunk) => {
|
|
15558
|
+
stderr += chunk.toString();
|
|
15559
|
+
});
|
|
15560
|
+
child.on("error", (error) => {
|
|
15561
|
+
void stdout.close();
|
|
15562
|
+
reject(error);
|
|
15563
|
+
});
|
|
15564
|
+
child.on("close", (code) => {
|
|
15565
|
+
void stdout.close();
|
|
15566
|
+
if (code === 0 || code === 1) {
|
|
15567
|
+
resolve6({
|
|
15568
|
+
files: sortAccumulators(files),
|
|
15569
|
+
totalMatchCount,
|
|
15570
|
+
hasMoreFiles
|
|
15571
|
+
});
|
|
15572
|
+
return;
|
|
15573
|
+
}
|
|
15574
|
+
reject(
|
|
15575
|
+
Object.assign(new Error(stderr || `rg exited with code ${code ?? "unknown"}`), { code })
|
|
15576
|
+
);
|
|
15577
|
+
});
|
|
15578
|
+
});
|
|
15579
|
+
}
|
|
15580
|
+
async function searchWithNode(rootPath, query, maxFiles) {
|
|
15581
|
+
const files = /* @__PURE__ */ new Map();
|
|
15582
|
+
let totalMatchCount = 0;
|
|
15583
|
+
let hasMoreFiles = false;
|
|
15584
|
+
async function walk(dirPath) {
|
|
15585
|
+
const filter = createGitignoreFilter(rootPath, dirPath);
|
|
15586
|
+
const entries = await readdir4(dirPath, { withFileTypes: true });
|
|
15587
|
+
const filteredEntries = entries.filter((entry) => filter(entry.name));
|
|
15588
|
+
filteredEntries.sort((a, b) => a.name.localeCompare(b.name));
|
|
15589
|
+
for (const entry of filteredEntries) {
|
|
15590
|
+
const fullPath = join12(dirPath, entry.name);
|
|
15591
|
+
if (entry.isDirectory()) {
|
|
15592
|
+
await walk(fullPath);
|
|
15593
|
+
continue;
|
|
15594
|
+
}
|
|
15595
|
+
if (!entry.isFile()) {
|
|
15596
|
+
continue;
|
|
15597
|
+
}
|
|
15598
|
+
const fileStat = await stat9(fullPath);
|
|
15599
|
+
if (fileStat.size > FALLBACK_MAX_FILE_BYTES) {
|
|
15600
|
+
continue;
|
|
15601
|
+
}
|
|
15602
|
+
const buffer = await readFile4(fullPath);
|
|
15603
|
+
if (isBinaryFile(buffer)) {
|
|
15604
|
+
continue;
|
|
15605
|
+
}
|
|
15606
|
+
const relativePath = normalizeRelativePath(relative3(rootPath, fullPath));
|
|
15607
|
+
const file = collectMatchesFromText(relativePath, buffer.toString("utf-8"), query);
|
|
15608
|
+
if (!file) {
|
|
15609
|
+
continue;
|
|
15610
|
+
}
|
|
15611
|
+
totalMatchCount += file.matchCount;
|
|
15612
|
+
if (files.size >= maxFiles) {
|
|
15613
|
+
hasMoreFiles = true;
|
|
15614
|
+
continue;
|
|
15615
|
+
}
|
|
15616
|
+
files.set(relativePath, file);
|
|
15617
|
+
}
|
|
15618
|
+
}
|
|
15619
|
+
await walk(rootPath);
|
|
15620
|
+
return {
|
|
15621
|
+
files: sortAccumulators(files),
|
|
15622
|
+
totalMatchCount,
|
|
15623
|
+
hasMoreFiles
|
|
15624
|
+
};
|
|
15625
|
+
}
|
|
15626
|
+
function collectMatchesFromText(relativePath, content, query) {
|
|
15627
|
+
const file = {
|
|
15628
|
+
path: relativePath,
|
|
15629
|
+
name: basename2(relativePath),
|
|
15630
|
+
matches: [],
|
|
15631
|
+
matchCount: 0
|
|
15632
|
+
};
|
|
15633
|
+
const lines = content.split(/\r?\n/);
|
|
15634
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
|
|
15635
|
+
const preview = lines[lineIndex] ?? "";
|
|
15636
|
+
if (!preview) {
|
|
15637
|
+
continue;
|
|
15638
|
+
}
|
|
15639
|
+
let fromIndex = 0;
|
|
15640
|
+
while (fromIndex <= preview.length) {
|
|
15641
|
+
const matchIndex = preview.indexOf(query, fromIndex);
|
|
15642
|
+
if (matchIndex === -1) {
|
|
15643
|
+
break;
|
|
15644
|
+
}
|
|
15645
|
+
const startColumn = matchIndex + 1;
|
|
15646
|
+
const endColumn = startColumn + query.length;
|
|
15647
|
+
file.matches.push({
|
|
15648
|
+
line: lineIndex + 1,
|
|
15649
|
+
column: startColumn,
|
|
15650
|
+
endColumn,
|
|
15651
|
+
preview,
|
|
15652
|
+
previewColumnStart: startColumn,
|
|
15653
|
+
previewColumnEnd: endColumn
|
|
15654
|
+
});
|
|
15655
|
+
file.matchCount += 1;
|
|
15656
|
+
fromIndex = matchIndex + Math.max(query.length, 1);
|
|
15657
|
+
}
|
|
15658
|
+
}
|
|
15659
|
+
return file.matchCount > 0 ? file : null;
|
|
15660
|
+
}
|
|
15661
|
+
function pushMatch(files, relativePath, match) {
|
|
15662
|
+
let file = files.get(relativePath);
|
|
15663
|
+
if (!file) {
|
|
15664
|
+
file = {
|
|
15665
|
+
path: relativePath,
|
|
15666
|
+
name: basename2(relativePath),
|
|
15667
|
+
matches: [],
|
|
15668
|
+
matchCount: 0
|
|
15669
|
+
};
|
|
15670
|
+
files.set(relativePath, file);
|
|
15671
|
+
}
|
|
15672
|
+
file.matches.push(match);
|
|
15673
|
+
file.matchCount += 1;
|
|
15674
|
+
}
|
|
15675
|
+
function sortAccumulators(files) {
|
|
15676
|
+
return Array.from(files.values()).sort((a, b) => a.path.localeCompare(b.path));
|
|
15677
|
+
}
|
|
15678
|
+
function finalizeResults(result, maxFiles, maxMatchesPerFile) {
|
|
15679
|
+
const visibleFiles = result.files.slice(0, maxFiles).map((file) => ({
|
|
15680
|
+
path: file.path,
|
|
15681
|
+
name: file.name,
|
|
15682
|
+
matchCount: file.matchCount,
|
|
15683
|
+
hasMoreMatches: file.matchCount > maxMatchesPerFile,
|
|
15684
|
+
matches: file.matches.slice(0, maxMatchesPerFile)
|
|
15685
|
+
}));
|
|
15686
|
+
return {
|
|
15687
|
+
files: visibleFiles,
|
|
15688
|
+
totalMatchCount: result.totalMatchCount,
|
|
15689
|
+
hasMoreFiles: result.hasMoreFiles || result.files.length > maxFiles,
|
|
15690
|
+
truncatedMatchFileCount: visibleFiles.filter((file) => file.hasMoreMatches).length
|
|
15691
|
+
};
|
|
15692
|
+
}
|
|
15693
|
+
function normalizeRelativePath(path14) {
|
|
15694
|
+
return path14.replace(/\\/g, "/");
|
|
15695
|
+
}
|
|
15696
|
+
function byteOffsetToColumn(preview, byteOffset) {
|
|
15697
|
+
return Buffer.from(preview, "utf8").subarray(0, byteOffset).toString("utf8").length + 1;
|
|
15698
|
+
}
|
|
15699
|
+
function isBinaryFile(buffer) {
|
|
15700
|
+
const sample = buffer.subarray(0, 8e3);
|
|
15701
|
+
return sample.includes(0);
|
|
15702
|
+
}
|
|
15703
|
+
var FALLBACK_MAX_FILE_BYTES;
|
|
15704
|
+
var init_content_search = __esm({
|
|
15705
|
+
"packages/server/src/fs/content-search.ts"() {
|
|
15706
|
+
"use strict";
|
|
15707
|
+
init_gitignore();
|
|
15708
|
+
FALLBACK_MAX_FILE_BYTES = 1e6;
|
|
15709
|
+
}
|
|
15710
|
+
});
|
|
15711
|
+
|
|
14875
15712
|
// packages/server/src/fs/tree.ts
|
|
14876
|
-
import { readdir as
|
|
14877
|
-
import { join as
|
|
15713
|
+
import { readdir as readdir5, stat as stat10 } from "fs/promises";
|
|
15714
|
+
import { join as join13, relative as relative4 } from "path";
|
|
14878
15715
|
async function readTree(rootPath, subdir) {
|
|
14879
|
-
const targetPath = subdir ?
|
|
15716
|
+
const targetPath = subdir ? join13(rootPath, subdir) : rootPath;
|
|
14880
15717
|
const filter = createTreeVisibilityFilter();
|
|
14881
|
-
const entries = await
|
|
15718
|
+
const entries = await readdir5(targetPath, { withFileTypes: true });
|
|
14882
15719
|
const nodes = [];
|
|
14883
15720
|
for (const entry of entries) {
|
|
14884
15721
|
if (!filter(entry.name)) {
|
|
14885
15722
|
continue;
|
|
14886
15723
|
}
|
|
14887
|
-
const fullPath =
|
|
14888
|
-
const relPath =
|
|
15724
|
+
const fullPath = join13(targetPath, entry.name);
|
|
15725
|
+
const relPath = relative4(rootPath, fullPath);
|
|
14889
15726
|
if (entry.isDirectory()) {
|
|
14890
15727
|
nodes.push({
|
|
14891
15728
|
name: entry.name,
|
|
@@ -14895,7 +15732,7 @@ async function readTree(rootPath, subdir) {
|
|
|
14895
15732
|
// Not loaded yet - client will request on expand
|
|
14896
15733
|
});
|
|
14897
15734
|
} else if (entry.isFile()) {
|
|
14898
|
-
const stats = await
|
|
15735
|
+
const stats = await stat10(fullPath);
|
|
14899
15736
|
nodes.push({
|
|
14900
15737
|
name: entry.name,
|
|
14901
15738
|
path: relPath,
|
|
@@ -14924,18 +15761,18 @@ async function searchFiles(rootPath, query, limit = 10) {
|
|
|
14924
15761
|
const matches = [];
|
|
14925
15762
|
async function walk(dirPath) {
|
|
14926
15763
|
const filter = createGitignoreFilter(rootPath, dirPath);
|
|
14927
|
-
const entries = await
|
|
15764
|
+
const entries = await readdir5(dirPath, { withFileTypes: true });
|
|
14928
15765
|
const filteredEntries = entries.filter((entry) => filter(entry.name));
|
|
14929
15766
|
filteredEntries.sort((a, b) => a.name.localeCompare(b.name));
|
|
14930
15767
|
for (const entry of filteredEntries) {
|
|
14931
|
-
const fullPath =
|
|
14932
|
-
const relPath =
|
|
15768
|
+
const fullPath = join13(dirPath, entry.name);
|
|
15769
|
+
const relPath = relative4(rootPath, fullPath);
|
|
14933
15770
|
if (entry.isDirectory()) {
|
|
14934
15771
|
await walk(fullPath);
|
|
14935
15772
|
continue;
|
|
14936
15773
|
}
|
|
14937
15774
|
if (entry.isFile()) {
|
|
14938
|
-
const rank =
|
|
15775
|
+
const rank = scoreFileMatch(relPath, entry.name, normalizedQuery);
|
|
14939
15776
|
if (rank === null) {
|
|
14940
15777
|
continue;
|
|
14941
15778
|
}
|
|
@@ -14964,7 +15801,7 @@ async function searchFiles(rootPath, query, limit = 10) {
|
|
|
14964
15801
|
}
|
|
14965
15802
|
return a.path.toLowerCase().localeCompare(b.path.toLowerCase());
|
|
14966
15803
|
}).slice(0, limit)) {
|
|
14967
|
-
const stats = await
|
|
15804
|
+
const stats = await stat10(match.fullPath);
|
|
14968
15805
|
files.push({
|
|
14969
15806
|
name: match.name,
|
|
14970
15807
|
path: match.path,
|
|
@@ -14975,6 +15812,13 @@ async function searchFiles(rootPath, query, limit = 10) {
|
|
|
14975
15812
|
}
|
|
14976
15813
|
return { files };
|
|
14977
15814
|
}
|
|
15815
|
+
function scoreFileMatch(path14, name, query) {
|
|
15816
|
+
const filenameRank = scoreFilenameMatch(name, query);
|
|
15817
|
+
if (filenameRank !== null) {
|
|
15818
|
+
return filenameRank;
|
|
15819
|
+
}
|
|
15820
|
+
return scorePathMatch(path14, query);
|
|
15821
|
+
}
|
|
14978
15822
|
function scoreFilenameMatch(name, query) {
|
|
14979
15823
|
const normalizedName = name.toLowerCase();
|
|
14980
15824
|
const baseName = normalizedName.replace(/\.[^.]+$/, "");
|
|
@@ -15001,6 +15845,22 @@ function scoreFilenameMatch(name, query) {
|
|
|
15001
15845
|
}
|
|
15002
15846
|
return null;
|
|
15003
15847
|
}
|
|
15848
|
+
function scorePathMatch(path14, query) {
|
|
15849
|
+
const normalizedPath = path14.toLowerCase();
|
|
15850
|
+
if (normalizedPath === query) {
|
|
15851
|
+
return 7;
|
|
15852
|
+
}
|
|
15853
|
+
if (normalizedPath.startsWith(query)) {
|
|
15854
|
+
return 8;
|
|
15855
|
+
}
|
|
15856
|
+
if (normalizedPath.includes(query)) {
|
|
15857
|
+
return 9;
|
|
15858
|
+
}
|
|
15859
|
+
if (isSubsequence(query, normalizedPath)) {
|
|
15860
|
+
return 10;
|
|
15861
|
+
}
|
|
15862
|
+
return null;
|
|
15863
|
+
}
|
|
15004
15864
|
function isSubsequence(query, candidate) {
|
|
15005
15865
|
let index = 0;
|
|
15006
15866
|
for (const char of candidate) {
|
|
@@ -15025,6 +15885,7 @@ import { z as z13 } from "zod";
|
|
|
15025
15885
|
var init_file = __esm({
|
|
15026
15886
|
"packages/server/src/commands/file.ts"() {
|
|
15027
15887
|
"use strict";
|
|
15888
|
+
init_content_search();
|
|
15028
15889
|
init_file_io();
|
|
15029
15890
|
init_tree();
|
|
15030
15891
|
init_dispatch();
|
|
@@ -15057,6 +15918,26 @@ var init_file = __esm({
|
|
|
15057
15918
|
return searchFiles(workspace.path, args.query, args.limit ?? 10);
|
|
15058
15919
|
}
|
|
15059
15920
|
);
|
|
15921
|
+
registerCommand(
|
|
15922
|
+
"file.searchContent",
|
|
15923
|
+
z13.object({
|
|
15924
|
+
workspaceId: z13.string(),
|
|
15925
|
+
query: z13.string(),
|
|
15926
|
+
maxFiles: z13.number().int().positive().max(100),
|
|
15927
|
+
maxMatchesPerFile: z13.number().int().positive().max(100)
|
|
15928
|
+
}),
|
|
15929
|
+
async (args, ctx) => {
|
|
15930
|
+
const workspace = ctx.workspaceMgr.get(args.workspaceId);
|
|
15931
|
+
if (!workspace) {
|
|
15932
|
+
throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
|
|
15933
|
+
}
|
|
15934
|
+
return searchFileContents(workspace.path, {
|
|
15935
|
+
query: args.query,
|
|
15936
|
+
maxFiles: args.maxFiles,
|
|
15937
|
+
maxMatchesPerFile: args.maxMatchesPerFile
|
|
15938
|
+
});
|
|
15939
|
+
}
|
|
15940
|
+
);
|
|
15060
15941
|
registerCommand(
|
|
15061
15942
|
"file.read",
|
|
15062
15943
|
z13.object({
|
|
@@ -15179,7 +16060,7 @@ var init_file = __esm({
|
|
|
15179
16060
|
});
|
|
15180
16061
|
|
|
15181
16062
|
// packages/server/src/git/diff.ts
|
|
15182
|
-
import { mkdtemp as mkdtemp3, readFile as
|
|
16063
|
+
import { mkdtemp as mkdtemp3, readFile as readFile5, rm as rm7 } from "fs/promises";
|
|
15183
16064
|
import os3 from "os";
|
|
15184
16065
|
import path11 from "path";
|
|
15185
16066
|
async function isTrackedPath(cwd, filePath) {
|
|
@@ -15214,12 +16095,12 @@ async function getUntrackedFileDiff(cwd, filePath) {
|
|
|
15214
16095
|
});
|
|
15215
16096
|
return result.stdout;
|
|
15216
16097
|
} finally {
|
|
15217
|
-
await
|
|
16098
|
+
await rm7(tempDir, { recursive: true, force: true });
|
|
15218
16099
|
}
|
|
15219
16100
|
}
|
|
15220
16101
|
async function pathExists(cwd, filePath) {
|
|
15221
16102
|
try {
|
|
15222
|
-
await
|
|
16103
|
+
await readFile5(resolveSafe(cwd, filePath));
|
|
15223
16104
|
return true;
|
|
15224
16105
|
} catch {
|
|
15225
16106
|
return false;
|
|
@@ -15227,7 +16108,7 @@ async function pathExists(cwd, filePath) {
|
|
|
15227
16108
|
}
|
|
15228
16109
|
async function readTextAtRevision(cwd, revision, filePath) {
|
|
15229
16110
|
if (revision === "WORKTREE") {
|
|
15230
|
-
return
|
|
16111
|
+
return readFile5(resolveSafe(cwd, filePath), "utf-8");
|
|
15231
16112
|
}
|
|
15232
16113
|
try {
|
|
15233
16114
|
const gitSpec = revision === "INDEX" ? `:${filePath}` : `${revision}:${filePath}`;
|
|
@@ -15645,33 +16526,33 @@ var init_git2 = __esm({
|
|
|
15645
16526
|
});
|
|
15646
16527
|
|
|
15647
16528
|
// packages/server/src/config/config-io.ts
|
|
15648
|
-
import { existsSync as
|
|
16529
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync7, renameSync as renameSync2, writeFileSync as writeFileSync5 } from "node:fs";
|
|
15649
16530
|
import { homedir as homedir3 } from "node:os";
|
|
15650
|
-
import { basename as
|
|
16531
|
+
import { basename as basename3, dirname as dirname7, join as join14 } from "node:path";
|
|
15651
16532
|
function resolveConfigPath(configType) {
|
|
15652
16533
|
if (configType === "codex") {
|
|
15653
16534
|
const testHome = process.env.CODER_STUDIO_CODEX_HOME;
|
|
15654
16535
|
if (testHome && testHome.trim()) {
|
|
15655
|
-
return
|
|
16536
|
+
return join14(testHome, "config.toml");
|
|
15656
16537
|
}
|
|
15657
16538
|
const codexHome = process.env.CODEX_HOME;
|
|
15658
16539
|
if (codexHome && codexHome.trim()) {
|
|
15659
|
-
return
|
|
16540
|
+
return join14(codexHome, "config.toml");
|
|
15660
16541
|
}
|
|
15661
|
-
return
|
|
16542
|
+
return join14(homedir3(), ".codex", "config.toml");
|
|
15662
16543
|
}
|
|
15663
16544
|
if (configType === "claude") {
|
|
15664
16545
|
const testHome = process.env.CODER_STUDIO_CLAUDE_HOME;
|
|
15665
16546
|
if (testHome && testHome.trim()) {
|
|
15666
|
-
return
|
|
16547
|
+
return join14(testHome, "settings.json");
|
|
15667
16548
|
}
|
|
15668
|
-
return
|
|
16549
|
+
return join14(homedir3(), ".claude", "settings.json");
|
|
15669
16550
|
}
|
|
15670
16551
|
throw new Error(`Unknown config type: ${configType}`);
|
|
15671
16552
|
}
|
|
15672
16553
|
function readConfigFile(configType) {
|
|
15673
16554
|
const configPath = resolveConfigPath(configType);
|
|
15674
|
-
if (!
|
|
16555
|
+
if (!existsSync9(configPath)) {
|
|
15675
16556
|
return { configPath, content: "", exists: false };
|
|
15676
16557
|
}
|
|
15677
16558
|
try {
|
|
@@ -15685,11 +16566,11 @@ function writeConfigFile(configType, content) {
|
|
|
15685
16566
|
try {
|
|
15686
16567
|
const configPath = resolveConfigPath(configType);
|
|
15687
16568
|
const parentDir = dirname7(configPath);
|
|
15688
|
-
if (!
|
|
16569
|
+
if (!existsSync9(parentDir)) {
|
|
15689
16570
|
mkdirSync7(parentDir, { recursive: true });
|
|
15690
16571
|
}
|
|
15691
16572
|
let backupPath = null;
|
|
15692
|
-
if (
|
|
16573
|
+
if (existsSync9(configPath)) {
|
|
15693
16574
|
backupPath = createBackup(configPath);
|
|
15694
16575
|
}
|
|
15695
16576
|
const tempPath = `${configPath}.tmp`;
|
|
@@ -15707,10 +16588,10 @@ function writeConfigFile(configType, content) {
|
|
|
15707
16588
|
function createBackup(filePath) {
|
|
15708
16589
|
const original = readFileSync7(filePath, "utf-8");
|
|
15709
16590
|
const ext = filePath.split(".").pop() ?? "";
|
|
15710
|
-
const base =
|
|
16591
|
+
const base = basename3(filePath, `.${ext}`);
|
|
15711
16592
|
const dir = dirname7(filePath);
|
|
15712
16593
|
const ts = formatTimestamp(/* @__PURE__ */ new Date());
|
|
15713
|
-
const backupPath =
|
|
16594
|
+
const backupPath = join14(dir, `${base}.bak.${ts}.${ext}`);
|
|
15714
16595
|
writeFileSync5(backupPath, original, "utf-8");
|
|
15715
16596
|
return backupPath;
|
|
15716
16597
|
}
|
|
@@ -15738,7 +16619,36 @@ function flattenSettings(obj, prefix = "") {
|
|
|
15738
16619
|
}
|
|
15739
16620
|
return result;
|
|
15740
16621
|
}
|
|
15741
|
-
|
|
16622
|
+
function resolveAppearancePersonalizationOverrideKeysToDelete(settings) {
|
|
16623
|
+
const appearance = settings.appearance;
|
|
16624
|
+
if (!appearance || typeof appearance !== "object" || Array.isArray(appearance)) {
|
|
16625
|
+
return [];
|
|
16626
|
+
}
|
|
16627
|
+
const personalization = appearance.personalization;
|
|
16628
|
+
if (!personalization || typeof personalization !== "object" || Array.isArray(personalization)) {
|
|
16629
|
+
return [];
|
|
16630
|
+
}
|
|
16631
|
+
if (!isFullAppearancePersonalizationSnapshot(personalization)) {
|
|
16632
|
+
return [];
|
|
16633
|
+
}
|
|
16634
|
+
const keysToDelete = [];
|
|
16635
|
+
for (const branch of PERSONALIZATION_OVERRIDE_BRANCHES) {
|
|
16636
|
+
const overrides = personalization[branch];
|
|
16637
|
+
if (!overrides || typeof overrides !== "object" || Array.isArray(overrides)) {
|
|
16638
|
+
continue;
|
|
16639
|
+
}
|
|
16640
|
+
for (const field of PERSONALIZATION_OVERRIDE_FIELDS) {
|
|
16641
|
+
if (!Object.prototype.hasOwnProperty.call(overrides, field)) {
|
|
16642
|
+
keysToDelete.push(`appearance.personalization.${branch}.${field}`);
|
|
16643
|
+
}
|
|
16644
|
+
}
|
|
16645
|
+
}
|
|
16646
|
+
return keysToDelete;
|
|
16647
|
+
}
|
|
16648
|
+
function isFullAppearancePersonalizationSnapshot(personalization) {
|
|
16649
|
+
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");
|
|
16650
|
+
}
|
|
16651
|
+
var PersonalizationOverridesSchema, PERSONALIZATION_OVERRIDE_BRANCHES, PERSONALIZATION_OVERRIDE_FIELDS, SettingsSchema;
|
|
15742
16652
|
var init_settings2 = __esm({
|
|
15743
16653
|
"packages/server/src/commands/settings.ts"() {
|
|
15744
16654
|
"use strict";
|
|
@@ -15747,6 +16657,23 @@ var init_settings2 = __esm({
|
|
|
15747
16657
|
init_provider_config();
|
|
15748
16658
|
init_settings();
|
|
15749
16659
|
init_dispatch();
|
|
16660
|
+
PersonalizationOverridesSchema = z15.object({
|
|
16661
|
+
backgroundAssetId: z15.string().min(1).nullable().optional(),
|
|
16662
|
+
backgroundDimness: z15.number().int().min(0).max(100).optional(),
|
|
16663
|
+
backgroundBlur: z15.number().int().min(0).max(40).optional(),
|
|
16664
|
+
glassEnabled: z15.boolean().optional(),
|
|
16665
|
+
glassIntensity: z15.number().int().min(0).max(100).optional(),
|
|
16666
|
+
surfaceOpacity: z15.number().int().min(0).max(100).optional()
|
|
16667
|
+
});
|
|
16668
|
+
PERSONALIZATION_OVERRIDE_BRANCHES = ["desktop", "mobile"];
|
|
16669
|
+
PERSONALIZATION_OVERRIDE_FIELDS = [
|
|
16670
|
+
"backgroundAssetId",
|
|
16671
|
+
"backgroundDimness",
|
|
16672
|
+
"backgroundBlur",
|
|
16673
|
+
"glassEnabled",
|
|
16674
|
+
"glassIntensity",
|
|
16675
|
+
"surfaceOpacity"
|
|
16676
|
+
];
|
|
15750
16677
|
SettingsSchema = z15.object({
|
|
15751
16678
|
defaultProviderId: z15.string().optional(),
|
|
15752
16679
|
notifications: z15.object({
|
|
@@ -15773,7 +16700,22 @@ var init_settings2 = __esm({
|
|
|
15773
16700
|
terminalFontSize: z15.number().int().min(10).max(18).optional(),
|
|
15774
16701
|
desktopTerminalFontSize: z15.number().int().min(10).max(18).optional(),
|
|
15775
16702
|
mobileTerminalFontSize: z15.number().int().min(10).max(18).optional(),
|
|
15776
|
-
locale: z15.enum(["zh", "en"]).optional()
|
|
16703
|
+
locale: z15.enum(["zh", "en"]).optional(),
|
|
16704
|
+
personalization: z15.object({
|
|
16705
|
+
version: z15.literal(1).optional(),
|
|
16706
|
+
common: z15.object({
|
|
16707
|
+
backgroundMode: z15.enum(["none", "image"]).optional(),
|
|
16708
|
+
backgroundAssetId: z15.string().min(1).nullable().optional(),
|
|
16709
|
+
backgroundFit: z15.enum(["cover", "contain"]).optional(),
|
|
16710
|
+
backgroundDimness: z15.number().int().min(0).max(100).optional(),
|
|
16711
|
+
backgroundBlur: z15.number().int().min(0).max(40).optional(),
|
|
16712
|
+
glassEnabled: z15.boolean().optional(),
|
|
16713
|
+
glassIntensity: z15.number().int().min(0).max(100).optional(),
|
|
16714
|
+
surfaceOpacity: z15.number().int().min(0).max(100).optional()
|
|
16715
|
+
}).optional(),
|
|
16716
|
+
desktop: PersonalizationOverridesSchema.optional(),
|
|
16717
|
+
mobile: PersonalizationOverridesSchema.optional()
|
|
16718
|
+
}).optional()
|
|
15777
16719
|
}).optional(),
|
|
15778
16720
|
lsp: z15.object({
|
|
15779
16721
|
mode: z15.enum(["auto", "off"]).optional()
|
|
@@ -15843,7 +16785,11 @@ var init_settings2 = __esm({
|
|
|
15843
16785
|
const nextSettings = args.settings;
|
|
15844
16786
|
const providers = nextSettings.providers && typeof nextSettings.providers === "object" && !Array.isArray(nextSettings.providers) ? nextSettings.providers : void 0;
|
|
15845
16787
|
const { providers: _providers, ...nonProviderSettings } = nextSettings;
|
|
16788
|
+
const overrideKeysToDelete = resolveAppearancePersonalizationOverrideKeysToDelete(nextSettings);
|
|
15846
16789
|
const flatSettings = flattenSettings(nonProviderSettings);
|
|
16790
|
+
for (const key of overrideKeysToDelete) {
|
|
16791
|
+
ctx.settingsRepo.delete(key);
|
|
16792
|
+
}
|
|
15847
16793
|
for (const [key, value] of Object.entries(flatSettings)) {
|
|
15848
16794
|
ctx.settingsRepo.set(key, value);
|
|
15849
16795
|
}
|
|
@@ -16489,6 +17435,7 @@ async function listWorktrees(repoPath) {
|
|
|
16489
17435
|
current.name = branch.split("/").pop() || branch;
|
|
16490
17436
|
} else if (line === "detached") {
|
|
16491
17437
|
current.branch = "detached HEAD";
|
|
17438
|
+
current.name = path12.basename(current.path ?? "") || "detached";
|
|
16492
17439
|
} else if (line === "") {
|
|
16493
17440
|
if (current.path) {
|
|
16494
17441
|
worktrees.push(current);
|
|
@@ -17070,12 +18017,12 @@ var init_commands = __esm({
|
|
|
17070
18017
|
// packages/server/src/server.ts
|
|
17071
18018
|
import { mkdtempSync, rmSync as rmSync2 } from "node:fs";
|
|
17072
18019
|
import { tmpdir } from "node:os";
|
|
17073
|
-
import { join as
|
|
18020
|
+
import { join as join15 } from "node:path";
|
|
17074
18021
|
async function createServer(configOverrides) {
|
|
17075
18022
|
const config = parseServerConfig(configOverrides);
|
|
17076
18023
|
const configuredStateDir = resolveConfiguredStateDir(config);
|
|
17077
18024
|
const shouldCleanupStateRoot = configuredStateDir === IN_MEMORY_STATE_DIR;
|
|
17078
|
-
const stateRoot = shouldCleanupStateRoot ? mkdtempSync(
|
|
18025
|
+
const stateRoot = shouldCleanupStateRoot ? mkdtempSync(join15(tmpdir(), "coder-studio-state-")) : configuredStateDir;
|
|
17079
18026
|
ensureStateDir(config);
|
|
17080
18027
|
const eventBus = new EventBus();
|
|
17081
18028
|
const activationMgr = new ActivationManager();
|
|
@@ -17085,10 +18032,10 @@ async function createServer(configOverrides) {
|
|
|
17085
18032
|
let commandContext;
|
|
17086
18033
|
let lspMgr = null;
|
|
17087
18034
|
const terminalRepo = new TerminalRepo({
|
|
17088
|
-
filePath:
|
|
18035
|
+
filePath: join15(stateRoot, "state", "terminals.json")
|
|
17089
18036
|
});
|
|
17090
18037
|
const sessionRepo = new SessionRepo({
|
|
17091
|
-
filePath:
|
|
18038
|
+
filePath: join15(stateRoot, "state", "sessions.json")
|
|
17092
18039
|
});
|
|
17093
18040
|
const terminalMgr = new TerminalManager({
|
|
17094
18041
|
ptyHost: createPtyHost(),
|
|
@@ -17096,10 +18043,10 @@ async function createServer(configOverrides) {
|
|
|
17096
18043
|
db: terminalRepo
|
|
17097
18044
|
});
|
|
17098
18045
|
const settingsRepo = new SettingsRepo({
|
|
17099
|
-
filePath:
|
|
18046
|
+
filePath: join15(stateRoot, "state", "settings.json")
|
|
17100
18047
|
});
|
|
17101
18048
|
const updateStateRepo = new UpdateStateRepo({
|
|
17102
|
-
filePath:
|
|
18049
|
+
filePath: join15(stateRoot, "state", "update-state.json"),
|
|
17103
18050
|
currentVersion: config.appVersion ?? "0.0.0"
|
|
17104
18051
|
});
|
|
17105
18052
|
const autoFetch = new AutoFetchScheduler({
|
|
@@ -17132,10 +18079,10 @@ async function createServer(configOverrides) {
|
|
|
17132
18079
|
}
|
|
17133
18080
|
});
|
|
17134
18081
|
const providerConfigRepo = new ProviderConfigRepo({
|
|
17135
|
-
filePath:
|
|
18082
|
+
filePath: join15(stateRoot, "state", "provider-configs.json")
|
|
17136
18083
|
});
|
|
17137
18084
|
const workspaceRepo = new WorkspaceRepo({
|
|
17138
|
-
filePath:
|
|
18085
|
+
filePath: join15(stateRoot, "state", "workspaces.json")
|
|
17139
18086
|
});
|
|
17140
18087
|
const sessionMgr = new SessionManager({
|
|
17141
18088
|
terminalMgr,
|
|
@@ -17170,10 +18117,13 @@ async function createServer(configOverrides) {
|
|
|
17170
18117
|
)
|
|
17171
18118
|
});
|
|
17172
18119
|
const authSessionRepo = new AuthSessionRepo({
|
|
17173
|
-
filePath:
|
|
18120
|
+
filePath: join15(stateRoot, "state", "auth-sessions.json")
|
|
17174
18121
|
});
|
|
17175
18122
|
const authLoginBlockRepo = new AuthLoginBlockRepo({
|
|
17176
|
-
filePath:
|
|
18123
|
+
filePath: join15(stateRoot, "state", "auth-login-blocks.json")
|
|
18124
|
+
});
|
|
18125
|
+
const appearanceAssetRepo = new AppearanceAssetRepo({
|
|
18126
|
+
filePath: join15(stateRoot, "state", "appearance-assets.json")
|
|
17177
18127
|
});
|
|
17178
18128
|
const app = await buildFastifyApp({
|
|
17179
18129
|
wsHub,
|
|
@@ -17182,6 +18132,7 @@ async function createServer(configOverrides) {
|
|
|
17182
18132
|
config,
|
|
17183
18133
|
authSessionRepo,
|
|
17184
18134
|
authLoginBlockRepo,
|
|
18135
|
+
appearanceAssetRepo,
|
|
17185
18136
|
logger: {
|
|
17186
18137
|
level: "info",
|
|
17187
18138
|
transport: {
|
|
@@ -17248,7 +18199,7 @@ async function createServer(configOverrides) {
|
|
|
17248
18199
|
...config.update,
|
|
17249
18200
|
currentVersion: config.appVersion ?? "0.0.0"
|
|
17250
18201
|
},
|
|
17251
|
-
updateWorkerLogFilePath:
|
|
18202
|
+
updateWorkerLogFilePath: join15(stateRoot, "logs", "update-worker.log"),
|
|
17252
18203
|
countRunningTerminals: () => terminalMgr.getAll().filter((terminal) => terminal.alive).length,
|
|
17253
18204
|
countRunningSessions: () => sessionMgr.getAll().filter((session) => session.state === "starting" || session.state === "running").length,
|
|
17254
18205
|
countActiveSupervisors: () => supervisorMgr?.countActive() ?? 0
|
|
@@ -17360,6 +18311,7 @@ var init_server = __esm({
|
|
|
17360
18311
|
init_e2e_provider_mock();
|
|
17361
18312
|
init_install_manager2();
|
|
17362
18313
|
init_manager3();
|
|
18314
|
+
init_appearance_asset_repo();
|
|
17363
18315
|
init_auth_login_block_repo();
|
|
17364
18316
|
init_auth_session_repo();
|
|
17365
18317
|
init_provider_config_repo();
|
|
@@ -17476,18 +18428,18 @@ import { fileURLToPath as fileURLToPath4 } from "url";
|
|
|
17476
18428
|
|
|
17477
18429
|
// packages/cli/src/config-store.ts
|
|
17478
18430
|
init_state_paths();
|
|
17479
|
-
import { existsSync as
|
|
18431
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
|
|
17480
18432
|
import { homedir as homedir4 } from "os";
|
|
17481
|
-
import { join as
|
|
18433
|
+
import { join as join16 } from "path";
|
|
17482
18434
|
function getCliConfigPath() {
|
|
17483
|
-
return
|
|
18435
|
+
return join16(homedir4(), ".coder-studio", "config.json");
|
|
17484
18436
|
}
|
|
17485
18437
|
function normalizeLegacyDataDir(input) {
|
|
17486
18438
|
return normalizeLegacyStateDir(input);
|
|
17487
18439
|
}
|
|
17488
18440
|
function readCliConfig() {
|
|
17489
18441
|
const path14 = getCliConfigPath();
|
|
17490
|
-
if (!
|
|
18442
|
+
if (!existsSync10(path14)) {
|
|
17491
18443
|
return null;
|
|
17492
18444
|
}
|
|
17493
18445
|
try {
|
|
@@ -17507,17 +18459,17 @@ function readCliConfig() {
|
|
|
17507
18459
|
}
|
|
17508
18460
|
|
|
17509
18461
|
// packages/cli/src/embed.ts
|
|
17510
|
-
import { existsSync as
|
|
17511
|
-
import { dirname as dirname8, resolve as
|
|
18462
|
+
import { existsSync as existsSync11 } from "fs";
|
|
18463
|
+
import { dirname as dirname8, resolve as resolve5 } from "path";
|
|
17512
18464
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
17513
18465
|
var __filename = fileURLToPath2(import.meta.url);
|
|
17514
18466
|
var __dirname = dirname8(__filename);
|
|
17515
|
-
var WEB_ASSETS_DIR =
|
|
18467
|
+
var WEB_ASSETS_DIR = resolve5(__dirname, "../web");
|
|
17516
18468
|
function getStaticAssetsDir() {
|
|
17517
18469
|
return WEB_ASSETS_DIR;
|
|
17518
18470
|
}
|
|
17519
18471
|
function hasWebAssets() {
|
|
17520
|
-
return
|
|
18472
|
+
return existsSync11(WEB_ASSETS_DIR);
|
|
17521
18473
|
}
|
|
17522
18474
|
|
|
17523
18475
|
// packages/cli/src/node-version.ts
|
|
@@ -17551,12 +18503,12 @@ function assertSupportedNodeVersion(version = process.versions.node) {
|
|
|
17551
18503
|
}
|
|
17552
18504
|
|
|
17553
18505
|
// packages/cli/src/package-manifest.ts
|
|
17554
|
-
import { existsSync as
|
|
18506
|
+
import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
|
|
17555
18507
|
function resolveCliPackageManifestUrl(importMetaUrl) {
|
|
17556
18508
|
const manifestUrl = [
|
|
17557
18509
|
new URL("../package.json", importMetaUrl),
|
|
17558
18510
|
new URL("../../package.json", importMetaUrl)
|
|
17559
|
-
].find((candidate) =>
|
|
18511
|
+
].find((candidate) => existsSync12(candidate));
|
|
17560
18512
|
if (!manifestUrl) {
|
|
17561
18513
|
throw new Error("Unable to locate CLI package.json");
|
|
17562
18514
|
}
|
|
@@ -17575,16 +18527,16 @@ function getCliPackageName(importMetaUrl) {
|
|
|
17575
18527
|
}
|
|
17576
18528
|
|
|
17577
18529
|
// packages/cli/src/update-runtime.ts
|
|
17578
|
-
import { existsSync as
|
|
17579
|
-
import { dirname as dirname9, join as
|
|
18530
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
18531
|
+
import { dirname as dirname9, join as join17 } from "node:path";
|
|
17580
18532
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
17581
18533
|
function resolveWorkerEntryPath(importMetaUrl) {
|
|
17582
18534
|
const currentDir = dirname9(fileURLToPath3(importMetaUrl));
|
|
17583
18535
|
const candidates = [
|
|
17584
|
-
|
|
17585
|
-
|
|
18536
|
+
join17(currentDir, "update-worker.mjs"),
|
|
18537
|
+
join17(currentDir, "../src/update-worker.ts")
|
|
17586
18538
|
];
|
|
17587
|
-
return candidates.find((candidate) =>
|
|
18539
|
+
return candidates.find((candidate) => existsSync13(candidate));
|
|
17588
18540
|
}
|
|
17589
18541
|
function getUpdateRuntimeInfo(importMetaUrl) {
|
|
17590
18542
|
const workerEntryPath = resolveWorkerEntryPath(importMetaUrl);
|