offsite 0.0.17 → 0.0.18
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/apps/server/dist/main.mjs +240 -135
- package/apps/server/dist/main.mjs.map +1 -1
- package/apps/web/dist/assets/HugeiconsIcon-CXlzk0hX.js +1 -0
- package/apps/web/dist/assets/angular-html-CPrDeWzk.js +1 -0
- package/apps/web/dist/assets/{angular-ts-DGEt84_g.js → angular-ts-DCisMPVX.js} +1 -1
- package/apps/web/dist/assets/{apl-bz_iDiNf.js → apl-BRH60u60.js} +1 -1
- package/apps/web/dist/assets/{astro-jXSlLJ7l.js → astro-Dq6p_VTJ.js} +1 -1
- package/apps/web/dist/assets/{blade-2sxYYh5A.js → blade-CC3PQRrX.js} +1 -1
- package/apps/web/dist/assets/c-iCihek07.js +1 -0
- package/apps/web/dist/assets/{chapel-DUoqr6M4.js → chapel-DmtgEXO_.js} +1 -1
- package/apps/web/dist/assets/{cobol-Cn0D9nVy.js → cobol-CGlx5WnO.js} +1 -1
- package/apps/web/dist/assets/{coffee-C1tCcS8h.js → coffee-CiiSLhB6.js} +1 -1
- package/apps/web/dist/assets/cpp-71xkOQct.js +1 -0
- package/apps/web/dist/assets/{crystal-DF-BMake.js → crystal-Bn4W9FYe.js} +1 -1
- package/apps/web/dist/assets/css-AeYbVOD-.js +1 -0
- package/apps/web/dist/assets/{edge-D8r2HbS9.js → edge-D8qeodQq.js} +1 -1
- package/apps/web/dist/assets/{elixir-K6buEKBc.js → elixir-BkREp_f2.js} +1 -1
- package/apps/web/dist/assets/{elm-B78wD7Un.js → elm-o4NJxfws.js} +1 -1
- package/apps/web/dist/assets/{erb-DuhNLWPu.js → erb-CpQ5AKmM.js} +1 -1
- package/apps/web/dist/assets/{git-rebase-C6n0imm6.js → git-rebase-BDPpS8w6.js} +1 -1
- package/apps/web/dist/assets/{glimmer-js-B65Nng8o.js → glimmer-js-BTqwx0oO.js} +1 -1
- package/apps/web/dist/assets/{glimmer-ts-Cme47tem.js → glimmer-ts-N9jKyowB.js} +1 -1
- package/apps/web/dist/assets/glsl-D5MA2a_X.js +1 -0
- package/apps/web/dist/assets/graphql-Ctjuxyvl.js +1 -0
- package/apps/web/dist/assets/{hack-W5aeWZGp.js → hack-ZTcFnVsP.js} +1 -1
- package/apps/web/dist/assets/haml-ChtpF0E7.js +1 -0
- package/apps/web/dist/assets/{handlebars-CYz9msjm.js → handlebars-CpsBgm7t.js} +1 -1
- package/apps/web/dist/assets/{html-derivative-fPnJ7c-5.js → html-derivative-DNPJNw-0.js} +1 -1
- package/apps/web/dist/assets/html-hnucWepv.js +1 -0
- package/apps/web/dist/assets/{http-DIR9LW29.js → http-kmH8AyfE.js} +1 -1
- package/apps/web/dist/assets/{hurl-73h4sQYH.js → hurl-DCZHXKU4.js} +1 -1
- package/apps/web/dist/assets/image-viewer-CBM7yEMl.js +1 -0
- package/apps/web/dist/assets/index-DkRVBJlp.css +2 -0
- package/apps/web/dist/assets/index-h6Rg_ww0.js +1206 -0
- package/apps/web/dist/assets/java-DdHNQ9hk.js +1 -0
- package/apps/web/dist/assets/javascript-obGGvMPs.js +1 -0
- package/apps/web/dist/assets/{jinja-wBZAPGc9.js → jinja-81yqctvv.js} +1 -1
- package/apps/web/dist/assets/{jison-bZuttet2.js → jison-CBjhYtSs.js} +1 -1
- package/apps/web/dist/assets/json-DAgxKhMr.js +1 -0
- package/apps/web/dist/assets/jsx-gTgmArGx.js +1 -0
- package/apps/web/dist/assets/jsx-runtime-BNakU3Ej.js +1 -0
- package/apps/web/dist/assets/{julia-Bj20nWQB.js → julia-KRkZs2hS.js} +1 -1
- package/apps/web/dist/assets/{just-pKKGVoyp.js → just-BuSkJCkO.js} +1 -1
- package/apps/web/dist/assets/{latex-DN5Fi2zg.js → latex-CvI9akcJ.js} +1 -1
- package/apps/web/dist/assets/{liquid-DtPrOPBs.js → liquid-VY2h0b2O.js} +1 -1
- package/apps/web/dist/assets/lua-FONpmTUP.js +1 -0
- package/apps/web/dist/assets/{marko-BAsjT454.js → marko-BRIeiu02.js} +1 -1
- package/apps/web/dist/assets/{mdc-DwMa0Ya_.js → mdc-CX_1c8hS.js} +1 -1
- package/apps/web/dist/assets/{nginx-BlYeGp57.js → nginx-BTMw9COX.js} +1 -1
- package/apps/web/dist/assets/{nim-DASYTjR5.js → nim-BrYFxDF2.js} +1 -1
- package/apps/web/dist/assets/{org-DSeAzk6D.js → org-Cd0u8vWD.js} +1 -1
- package/apps/web/dist/assets/{perl-BGPYGeVd.js → perl-1pN6i0RM.js} +1 -1
- package/apps/web/dist/assets/{php-kgT6t38x.js → php-C7NaTRTO.js} +1 -1
- package/apps/web/dist/assets/{pug-COTHnC13.js → pug-Co4hciMS.js} +1 -1
- package/apps/web/dist/assets/{qml-DhqsM4c7.js → qml-_a-QhfPD.js} +1 -1
- package/apps/web/dist/assets/r-BHcO8Vjq.js +1 -0
- package/apps/web/dist/assets/{razor-BGH58TzE.js → razor-DVe451rE.js} +1 -1
- package/apps/web/dist/assets/regexp-DR20uMnK.js +1 -0
- package/apps/web/dist/assets/rolldown-runtime-hePW80VL.js +1 -0
- package/apps/web/dist/assets/{rst-UweowKJr.js → rst-hVyWO_Fc.js} +1 -1
- package/apps/web/dist/assets/{ruby-DtfMo-6m.js → ruby-Zd-8tM08.js} +1 -1
- package/apps/web/dist/assets/{sas-DkQGY_o2.js → sas-D3h2pRQf.js} +1 -1
- package/apps/web/dist/assets/scss-DDJaLTnA.js +1 -0
- package/apps/web/dist/assets/shellscript-Da78Ox-3.js +1 -0
- package/apps/web/dist/assets/{shellsession-CT0a4IuI.js → shellsession-CVsQGYfZ.js} +1 -1
- package/apps/web/dist/assets/{soy-B3ufTOzS.js → soy-CXX9LSpm.js} +1 -1
- package/apps/web/dist/assets/sql-ByhjMBKn.js +1 -0
- package/apps/web/dist/assets/{stata-BYhfUPEu.js → stata-CjrVU8-t.js} +1 -1
- package/apps/web/dist/assets/{surrealql-UIZa84-V.js → surrealql-BZoq2lXR.js} +1 -1
- package/apps/web/dist/assets/{svelte-SkphxuD6.js → svelte-CZ5lJGR6.js} +1 -1
- package/apps/web/dist/assets/{templ-C8T3Zgrx.js → templ-EWf5LxrK.js} +1 -1
- package/apps/web/dist/assets/tex-BK-H2Wn4.js +1 -0
- package/apps/web/dist/assets/{ts-tags-Bk8_Hqg_.js → ts-tags-C4CDubZg.js} +1 -1
- package/apps/web/dist/assets/tsx-CUOciP0b.js +1 -0
- package/apps/web/dist/assets/{twig-pKLKVHRj.js → twig-D6Rh4Rda.js} +1 -1
- package/apps/web/dist/assets/typescript-BydbNFcO.js +1 -0
- package/apps/web/dist/assets/{typst-DCcUGqaw.js → typst-Bq_qwoSs.js} +1 -1
- package/apps/web/dist/assets/video-player-D73_kRr7.js +1416 -0
- package/apps/web/dist/assets/video-viewer-CUqarcH1.js +1 -0
- package/apps/web/dist/assets/{vue-B_0IsDtY.js → vue-D5NIjYCb.js} +1 -1
- package/apps/web/dist/assets/{vue-html-B42dVyQs.js → vue-html-DkKkQnZL.js} +1 -1
- package/apps/web/dist/assets/{vue-vine-CUms6qs_.js → vue-vine-C-CFC0K-.js} +1 -1
- package/apps/web/dist/assets/xml-BlnrEAEy.js +1 -0
- package/apps/web/dist/assets/{xsl-73EQy__K.js → xsl-BOPS8gLt.js} +1 -1
- package/apps/web/dist/assets/yaml-Du_6cdrH.js +1 -0
- package/apps/web/dist/assets/zoomable-image-CVx2-A2g.js +41 -0
- package/apps/web/dist/index.html +5 -2
- package/apps/web/dist/sw.js +1 -1
- package/package.json +1 -1
- package/apps/web/dist/assets/angular-html-DS1oV12F.js +0 -1
- package/apps/web/dist/assets/c-DapwzVu-.js +0 -1
- package/apps/web/dist/assets/cpp-DxWN2Okg.js +0 -1
- package/apps/web/dist/assets/css-DO_8FP-m.js +0 -1
- package/apps/web/dist/assets/glsl-DCAs0c4j.js +0 -1
- package/apps/web/dist/assets/graphql-CjoRmsAT.js +0 -1
- package/apps/web/dist/assets/haml-C_s1GJd7.js +0 -1
- package/apps/web/dist/assets/html-rJzVMoNs.js +0 -1
- package/apps/web/dist/assets/index-CbDKWecy.js +0 -1206
- package/apps/web/dist/assets/index-aoUjY0qi.css +0 -2
- package/apps/web/dist/assets/java-CC9QxJEZ.js +0 -1
- package/apps/web/dist/assets/javascript-BhEdsWyv.js +0 -1
- package/apps/web/dist/assets/json-BwYVcFZr.js +0 -1
- package/apps/web/dist/assets/jsx-DZ0EGwxr.js +0 -1
- package/apps/web/dist/assets/lua-C1ueigSQ.js +0 -1
- package/apps/web/dist/assets/r-D-KnVREb.js +0 -1
- package/apps/web/dist/assets/regexp-BHxEHonG.js +0 -1
- package/apps/web/dist/assets/scss-QYV6e_ks.js +0 -1
- package/apps/web/dist/assets/shellscript-CBTXJvxj.js +0 -1
- package/apps/web/dist/assets/sql-BhGvXJN_.js +0 -1
- package/apps/web/dist/assets/tex-Be-e7W9g.js +0 -1
- package/apps/web/dist/assets/tsx-B_hC-_lk.js +0 -1
- package/apps/web/dist/assets/typescript-D8MumUKS.js +0 -1
- package/apps/web/dist/assets/xml-Dh5mAVtG.js +0 -1
- package/apps/web/dist/assets/yaml-C5lWNsLB.js +0 -1
|
@@ -823,11 +823,9 @@ const configSchema = z.strictObject({
|
|
|
823
823
|
opencode: z.record(z.string(), modelSetting).default({})
|
|
824
824
|
}).prefault({}),
|
|
825
825
|
delivery: z.enum(["queue", "steer"]).default("steer"),
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
permissionDecision: z.enum(["allow", "deny"]).default("allow")
|
|
830
|
-
}).prefault({}),
|
|
826
|
+
autoSkipQuestionTimeout: z.number().positive().nullable().default(180),
|
|
827
|
+
autoPermissionMode: z.enum(["allow", "deny"]).default("allow"),
|
|
828
|
+
autoPermissionTimeout: z.number().positive().nullable().default(15),
|
|
831
829
|
terminal: z.strictObject({
|
|
832
830
|
shell: z.strictObject({
|
|
833
831
|
macos: z.string().nullable().default(null),
|
|
@@ -914,20 +912,27 @@ function broadcastActivity() {
|
|
|
914
912
|
});
|
|
915
913
|
}
|
|
916
914
|
function scheduleAutoResolve(sessionId, request) {
|
|
917
|
-
if (request.kind === "plan") return;
|
|
918
|
-
const
|
|
919
|
-
const
|
|
920
|
-
if (
|
|
915
|
+
if (request.kind === "plan") return request;
|
|
916
|
+
const config = readConfig();
|
|
917
|
+
const seconds = request.kind === "question" ? config.autoSkipQuestionTimeout : config.autoPermissionTimeout;
|
|
918
|
+
if (seconds === null) return request;
|
|
919
|
+
const decision = request.kind === "question" ? "allow" : config.autoPermissionMode;
|
|
921
920
|
setTimeout(async () => {
|
|
922
921
|
const run = runs.get(sessionId);
|
|
923
922
|
if (run === void 0) return;
|
|
924
923
|
try {
|
|
925
|
-
|
|
926
|
-
else await run.respond(request.id, readConfig().autoResolve.permissionDecision, null);
|
|
924
|
+
await run.respond(request.id, decision, request.kind === "question" ? {} : null);
|
|
927
925
|
} catch (error) {
|
|
928
926
|
console.warn(`could not auto-resolve ${request.id}: ${error}`);
|
|
929
927
|
}
|
|
930
|
-
},
|
|
928
|
+
}, seconds * 1e3);
|
|
929
|
+
return {
|
|
930
|
+
...request,
|
|
931
|
+
autoResolve: {
|
|
932
|
+
at: Date.now() + seconds * 1e3,
|
|
933
|
+
decision
|
|
934
|
+
}
|
|
935
|
+
};
|
|
931
936
|
}
|
|
932
937
|
//#endregion
|
|
933
938
|
//#region src/lib/codex/requests.ts
|
|
@@ -1257,11 +1262,12 @@ var CodexRun = class CodexRun {
|
|
|
1257
1262
|
});
|
|
1258
1263
|
return;
|
|
1259
1264
|
}
|
|
1260
|
-
const
|
|
1261
|
-
if (
|
|
1265
|
+
const created = toRequest$1(String(id), method, params, this.items);
|
|
1266
|
+
if (created === null) {
|
|
1262
1267
|
this.server.replyError(id, `offsite does not handle ${method}`);
|
|
1263
1268
|
return;
|
|
1264
1269
|
}
|
|
1270
|
+
const request = scheduleAutoResolve(this.sessionId, created);
|
|
1265
1271
|
this.pending.set(request.id, {
|
|
1266
1272
|
request,
|
|
1267
1273
|
id,
|
|
@@ -1274,7 +1280,6 @@ var CodexRun = class CodexRun {
|
|
|
1274
1280
|
request
|
|
1275
1281
|
});
|
|
1276
1282
|
broadcastActivity();
|
|
1277
|
-
scheduleAutoResolve(this.sessionId, request);
|
|
1278
1283
|
}
|
|
1279
1284
|
async startTurn(prompt) {
|
|
1280
1285
|
clearTimeout(this.idleTimer);
|
|
@@ -1746,7 +1751,7 @@ function toolItems(base, tool) {
|
|
|
1746
1751
|
case "skill": return [{
|
|
1747
1752
|
...base,
|
|
1748
1753
|
kind: "skill",
|
|
1749
|
-
skill: text$2(input.
|
|
1754
|
+
skill: text$2(input.id),
|
|
1750
1755
|
output,
|
|
1751
1756
|
status
|
|
1752
1757
|
}];
|
|
@@ -2299,14 +2304,17 @@ var OpenCodeRun = class OpenCodeRun {
|
|
|
2299
2304
|
});
|
|
2300
2305
|
}
|
|
2301
2306
|
addPending(pending) {
|
|
2302
|
-
this.pending.
|
|
2307
|
+
const request = scheduleAutoResolve(this.sessionId, pending.request);
|
|
2308
|
+
this.pending.set(request.id, {
|
|
2309
|
+
...pending,
|
|
2310
|
+
request
|
|
2311
|
+
});
|
|
2303
2312
|
publish$2(this.sessionId, {
|
|
2304
2313
|
type: "request",
|
|
2305
2314
|
sessionId: this.sessionId,
|
|
2306
|
-
request
|
|
2315
|
+
request
|
|
2307
2316
|
});
|
|
2308
2317
|
broadcastActivity();
|
|
2309
|
-
scheduleAutoResolve(this.sessionId, pending.request);
|
|
2310
2318
|
}
|
|
2311
2319
|
finishTurn() {
|
|
2312
2320
|
this.partial = "";
|
|
@@ -4319,13 +4327,13 @@ var Run = class {
|
|
|
4319
4327
|
}, IDLE_LIMIT);
|
|
4320
4328
|
}
|
|
4321
4329
|
ask(options) {
|
|
4322
|
-
const request = toRequest({
|
|
4330
|
+
const request = scheduleAutoResolve(this.sessionId, toRequest({
|
|
4323
4331
|
id: randomUUID(),
|
|
4324
4332
|
toolName: options.toolName,
|
|
4325
4333
|
input: options.input,
|
|
4326
4334
|
title: options.title,
|
|
4327
4335
|
canAlwaysAllow: options.suggestions.length > 0
|
|
4328
|
-
});
|
|
4336
|
+
}));
|
|
4329
4337
|
return new Promise((resolve) => {
|
|
4330
4338
|
this.pending.set(request.id, {
|
|
4331
4339
|
request,
|
|
@@ -4339,7 +4347,6 @@ var Run = class {
|
|
|
4339
4347
|
request
|
|
4340
4348
|
});
|
|
4341
4349
|
broadcastActivity();
|
|
4342
|
-
scheduleAutoResolve(this.sessionId, request);
|
|
4343
4350
|
options.signal.addEventListener("abort", () => this.settle(request.id, {
|
|
4344
4351
|
behavior: "deny",
|
|
4345
4352
|
message: "Cancelled"
|
|
@@ -4391,6 +4398,153 @@ var Run = class {
|
|
|
4391
4398
|
}
|
|
4392
4399
|
};
|
|
4393
4400
|
//#endregion
|
|
4401
|
+
//#region ../core/src/media.ts
|
|
4402
|
+
const mediaTypes = /* @__PURE__ */ new Map([
|
|
4403
|
+
[".png", "image/png"],
|
|
4404
|
+
[".jpg", "image/jpeg"],
|
|
4405
|
+
[".jpeg", "image/jpeg"],
|
|
4406
|
+
[".gif", "image/gif"],
|
|
4407
|
+
[".webp", "image/webp"],
|
|
4408
|
+
[".svg", "image/svg+xml"],
|
|
4409
|
+
[".mp4", "video/mp4"],
|
|
4410
|
+
[".m4v", "video/mp4"],
|
|
4411
|
+
[".webm", "video/webm"],
|
|
4412
|
+
[".mov", "video/quicktime"],
|
|
4413
|
+
[".mp3", "audio/mpeg"],
|
|
4414
|
+
[".wav", "audio/wav"],
|
|
4415
|
+
[".ogg", "audio/ogg"],
|
|
4416
|
+
[".opus", "audio/ogg"],
|
|
4417
|
+
[".m4a", "audio/mp4"],
|
|
4418
|
+
[".aac", "audio/aac"],
|
|
4419
|
+
[".flac", "audio/flac"]
|
|
4420
|
+
]);
|
|
4421
|
+
//#endregion
|
|
4422
|
+
//#region src/services/files.ts
|
|
4423
|
+
const FILE_LIMIT = 2e4;
|
|
4424
|
+
const OUTPUT_LIMIT$1 = 262144;
|
|
4425
|
+
const READ_LIMIT = 1048576;
|
|
4426
|
+
const MEDIA_LINK_LIFETIME = 36e5;
|
|
4427
|
+
const mediaLinks = /* @__PURE__ */ new Map();
|
|
4428
|
+
const skipped = /* @__PURE__ */ new Set([
|
|
4429
|
+
"node_modules",
|
|
4430
|
+
".git",
|
|
4431
|
+
".DS_Store",
|
|
4432
|
+
"dist",
|
|
4433
|
+
"target",
|
|
4434
|
+
".next",
|
|
4435
|
+
"__pycache__",
|
|
4436
|
+
".venv"
|
|
4437
|
+
]);
|
|
4438
|
+
async function listFiles(cwd) {
|
|
4439
|
+
const files = [];
|
|
4440
|
+
const pending = [""];
|
|
4441
|
+
while (pending.length > 0 && files.length < FILE_LIMIT) {
|
|
4442
|
+
const dir = pending.pop() ?? "";
|
|
4443
|
+
let entries;
|
|
4444
|
+
try {
|
|
4445
|
+
entries = await readdir(path.join(cwd, dir), { withFileTypes: true });
|
|
4446
|
+
} catch {
|
|
4447
|
+
continue;
|
|
4448
|
+
}
|
|
4449
|
+
for (const entry of entries) {
|
|
4450
|
+
if (skipped.has(entry.name)) continue;
|
|
4451
|
+
const relative = dir === "" ? entry.name : `${dir}/${entry.name}`;
|
|
4452
|
+
if (entry.isDirectory()) pending.push(relative);
|
|
4453
|
+
if (entry.isFile()) files.push(relative);
|
|
4454
|
+
if (entry.isSymbolicLink()) {
|
|
4455
|
+
if ((await stat(path.join(cwd, relative)).catch(() => null))?.isFile() === true) files.push(relative);
|
|
4456
|
+
}
|
|
4457
|
+
}
|
|
4458
|
+
}
|
|
4459
|
+
return files.slice(0, FILE_LIMIT).sort();
|
|
4460
|
+
}
|
|
4461
|
+
function readProjectFile(cwd, file) {
|
|
4462
|
+
const full = resolveProjectFile(cwd, file);
|
|
4463
|
+
const stats = statSync(full);
|
|
4464
|
+
if (!stats.isFile()) throw new Error(`${file} is not a file`);
|
|
4465
|
+
if (stats.size > READ_LIMIT) return {
|
|
4466
|
+
kind: "large",
|
|
4467
|
+
size: stats.size,
|
|
4468
|
+
limit: READ_LIMIT
|
|
4469
|
+
};
|
|
4470
|
+
const data = readFileSync(full);
|
|
4471
|
+
const mediaType = mediaTypes.get(path.extname(full).toLowerCase());
|
|
4472
|
+
if (mediaType?.startsWith("image/") === true) return {
|
|
4473
|
+
kind: "image",
|
|
4474
|
+
size: stats.size,
|
|
4475
|
+
mediaType,
|
|
4476
|
+
data: data.toString("base64")
|
|
4477
|
+
};
|
|
4478
|
+
if (data.includes(0)) return {
|
|
4479
|
+
kind: "binary",
|
|
4480
|
+
size: stats.size
|
|
4481
|
+
};
|
|
4482
|
+
return {
|
|
4483
|
+
kind: "text",
|
|
4484
|
+
size: stats.size,
|
|
4485
|
+
content: data.toString("utf8")
|
|
4486
|
+
};
|
|
4487
|
+
}
|
|
4488
|
+
function linkProjectMedia(cwd, file) {
|
|
4489
|
+
const full = resolveProjectFile(cwd, file);
|
|
4490
|
+
const mediaType = mediaTypes.get(path.extname(full).toLowerCase());
|
|
4491
|
+
if (mediaType === void 0) throw new Error(`${file} is not an image, video or audio file`);
|
|
4492
|
+
for (const [token, link] of mediaLinks) if (link.expiresAt <= Date.now()) mediaLinks.delete(token);
|
|
4493
|
+
const token = randomBytes(32).toString("base64url");
|
|
4494
|
+
mediaLinks.set(token, {
|
|
4495
|
+
file: full,
|
|
4496
|
+
mediaType,
|
|
4497
|
+
expiresAt: Date.now() + MEDIA_LINK_LIFETIME
|
|
4498
|
+
});
|
|
4499
|
+
return token;
|
|
4500
|
+
}
|
|
4501
|
+
function findMediaLink(token) {
|
|
4502
|
+
if (token === null) return null;
|
|
4503
|
+
const link = mediaLinks.get(token);
|
|
4504
|
+
if (link === void 0 || link.expiresAt <= Date.now()) return null;
|
|
4505
|
+
return link;
|
|
4506
|
+
}
|
|
4507
|
+
function saveUpload(name, data) {
|
|
4508
|
+
const dir = path.join(home, "uploads", randomUUID());
|
|
4509
|
+
mkdirSync(dir, { recursive: true });
|
|
4510
|
+
const file = path.join(dir, path.basename(name));
|
|
4511
|
+
writeFileSync(file, data);
|
|
4512
|
+
return file;
|
|
4513
|
+
}
|
|
4514
|
+
async function taskOutput(sessionId, file) {
|
|
4515
|
+
const tasks = path.dirname(file);
|
|
4516
|
+
if (!/^[\w-]+$/.test(sessionId) || !path.isAbsolute(file) || path.normalize(file) !== file || !/^\w+\.output$/.test(path.basename(file)) || path.basename(tasks) !== "tasks" || path.basename(path.dirname(tasks)) !== sessionId) throw new Error("Not a background task output file of this thread");
|
|
4517
|
+
let stats;
|
|
4518
|
+
try {
|
|
4519
|
+
stats = await lstat(file);
|
|
4520
|
+
} catch (error) {
|
|
4521
|
+
if (hasCode(error, "ENOENT")) return null;
|
|
4522
|
+
throw error;
|
|
4523
|
+
}
|
|
4524
|
+
if (!stats.isFile()) throw new Error("Background task output is not a regular file");
|
|
4525
|
+
const start = Math.max(0, stats.size - OUTPUT_LIMIT$1);
|
|
4526
|
+
const handle = await open(file, "r");
|
|
4527
|
+
try {
|
|
4528
|
+
const buffer = Buffer.alloc(stats.size - start);
|
|
4529
|
+
const read = await handle.read(buffer, 0, buffer.length, start);
|
|
4530
|
+
return {
|
|
4531
|
+
text: buffer.subarray(0, read.bytesRead).toString("utf8"),
|
|
4532
|
+
truncated: start > 0
|
|
4533
|
+
};
|
|
4534
|
+
} finally {
|
|
4535
|
+
await handle.close();
|
|
4536
|
+
}
|
|
4537
|
+
}
|
|
4538
|
+
function resolveProjectFile(cwd, file) {
|
|
4539
|
+
const root = realpathSync(cwd);
|
|
4540
|
+
const resolved = path.resolve(root, file);
|
|
4541
|
+
if (!existsSync(resolved)) throw new Error(`${file} does not exist`);
|
|
4542
|
+
const full = realpathSync(resolved);
|
|
4543
|
+
const relative = path.relative(root, full);
|
|
4544
|
+
if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) throw new Error(`${file} is outside the project`);
|
|
4545
|
+
return full;
|
|
4546
|
+
}
|
|
4547
|
+
//#endregion
|
|
4394
4548
|
//#region src/services/git.ts
|
|
4395
4549
|
const DIFF_LIMIT = 8e4;
|
|
4396
4550
|
const UNTRACKED_LIMIT = 100;
|
|
@@ -4649,6 +4803,7 @@ async function isRepo(cwd) {
|
|
|
4649
4803
|
}
|
|
4650
4804
|
async function repoDiff(repo, prefix) {
|
|
4651
4805
|
const tracked = await git(repo, [
|
|
4806
|
+
"--no-optional-locks",
|
|
4652
4807
|
"diff",
|
|
4653
4808
|
await diffBase(repo),
|
|
4654
4809
|
"--no-color",
|
|
@@ -4708,119 +4863,6 @@ function newFilePatch(cwd, file, prefix) {
|
|
|
4708
4863
|
return `${header}@@ -0,0 +1,${lines.length} @@\n${lines.map((line) => `+${line}\n`).join("")}`;
|
|
4709
4864
|
}
|
|
4710
4865
|
//#endregion
|
|
4711
|
-
//#region src/services/files.ts
|
|
4712
|
-
const FILE_LIMIT = 2e4;
|
|
4713
|
-
const OUTPUT_LIMIT$1 = 262144;
|
|
4714
|
-
const READ_LIMIT = 1048576;
|
|
4715
|
-
const images = /* @__PURE__ */ new Map([
|
|
4716
|
-
[".png", "image/png"],
|
|
4717
|
-
[".jpg", "image/jpeg"],
|
|
4718
|
-
[".jpeg", "image/jpeg"],
|
|
4719
|
-
[".gif", "image/gif"],
|
|
4720
|
-
[".webp", "image/webp"],
|
|
4721
|
-
[".svg", "image/svg+xml"]
|
|
4722
|
-
]);
|
|
4723
|
-
const skipped = /* @__PURE__ */ new Set([
|
|
4724
|
-
"node_modules",
|
|
4725
|
-
".git",
|
|
4726
|
-
"dist",
|
|
4727
|
-
"target",
|
|
4728
|
-
".next"
|
|
4729
|
-
]);
|
|
4730
|
-
async function listFiles(cwd) {
|
|
4731
|
-
try {
|
|
4732
|
-
const { stdout } = await git(cwd, [
|
|
4733
|
-
"ls-files",
|
|
4734
|
-
"--cached",
|
|
4735
|
-
"--others",
|
|
4736
|
-
"--exclude-standard"
|
|
4737
|
-
]);
|
|
4738
|
-
return stdout.split("\n").filter(Boolean).slice(0, FILE_LIMIT);
|
|
4739
|
-
} catch {
|
|
4740
|
-
return walk(cwd);
|
|
4741
|
-
}
|
|
4742
|
-
}
|
|
4743
|
-
function readProjectFile(cwd, file) {
|
|
4744
|
-
const root = realpathSync(cwd);
|
|
4745
|
-
const full = realpathSync(path.resolve(root, file));
|
|
4746
|
-
const relative = path.relative(root, full);
|
|
4747
|
-
if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) throw new Error(`${file} is outside the project`);
|
|
4748
|
-
const stats = statSync(full);
|
|
4749
|
-
if (!stats.isFile()) throw new Error(`${file} is not a file`);
|
|
4750
|
-
if (stats.size > READ_LIMIT) return {
|
|
4751
|
-
kind: "large",
|
|
4752
|
-
size: stats.size,
|
|
4753
|
-
limit: READ_LIMIT
|
|
4754
|
-
};
|
|
4755
|
-
const data = readFileSync(full);
|
|
4756
|
-
const mediaType = images.get(path.extname(full).toLowerCase());
|
|
4757
|
-
if (mediaType !== void 0) return {
|
|
4758
|
-
kind: "image",
|
|
4759
|
-
size: stats.size,
|
|
4760
|
-
mediaType,
|
|
4761
|
-
data: data.toString("base64")
|
|
4762
|
-
};
|
|
4763
|
-
if (data.includes(0)) return {
|
|
4764
|
-
kind: "binary",
|
|
4765
|
-
size: stats.size
|
|
4766
|
-
};
|
|
4767
|
-
return {
|
|
4768
|
-
kind: "text",
|
|
4769
|
-
size: stats.size,
|
|
4770
|
-
content: data.toString("utf8")
|
|
4771
|
-
};
|
|
4772
|
-
}
|
|
4773
|
-
function saveUpload(name, data) {
|
|
4774
|
-
const dir = path.join(home, "uploads", randomUUID());
|
|
4775
|
-
mkdirSync(dir, { recursive: true });
|
|
4776
|
-
const file = path.join(dir, path.basename(name));
|
|
4777
|
-
writeFileSync(file, data);
|
|
4778
|
-
return file;
|
|
4779
|
-
}
|
|
4780
|
-
async function taskOutput(sessionId, file) {
|
|
4781
|
-
const tasks = path.dirname(file);
|
|
4782
|
-
if (!/^[\w-]+$/.test(sessionId) || !path.isAbsolute(file) || path.normalize(file) !== file || !/^\w+\.output$/.test(path.basename(file)) || path.basename(tasks) !== "tasks" || path.basename(path.dirname(tasks)) !== sessionId) throw new Error("Not a background task output file of this thread");
|
|
4783
|
-
let stats;
|
|
4784
|
-
try {
|
|
4785
|
-
stats = await lstat(file);
|
|
4786
|
-
} catch (error) {
|
|
4787
|
-
if (hasCode(error, "ENOENT")) return null;
|
|
4788
|
-
throw error;
|
|
4789
|
-
}
|
|
4790
|
-
if (!stats.isFile()) throw new Error("Background task output is not a regular file");
|
|
4791
|
-
const start = Math.max(0, stats.size - OUTPUT_LIMIT$1);
|
|
4792
|
-
const handle = await open(file, "r");
|
|
4793
|
-
try {
|
|
4794
|
-
const buffer = Buffer.alloc(stats.size - start);
|
|
4795
|
-
const read = await handle.read(buffer, 0, buffer.length, start);
|
|
4796
|
-
return {
|
|
4797
|
-
text: buffer.subarray(0, read.bytesRead).toString("utf8"),
|
|
4798
|
-
truncated: start > 0
|
|
4799
|
-
};
|
|
4800
|
-
} finally {
|
|
4801
|
-
await handle.close();
|
|
4802
|
-
}
|
|
4803
|
-
}
|
|
4804
|
-
function walk(root) {
|
|
4805
|
-
const files = [];
|
|
4806
|
-
const pending = [""];
|
|
4807
|
-
while (pending.length > 0 && files.length < FILE_LIMIT) {
|
|
4808
|
-
const dir = pending.pop() ?? "";
|
|
4809
|
-
let entries;
|
|
4810
|
-
try {
|
|
4811
|
-
entries = readdirSync(path.join(root, dir), { withFileTypes: true });
|
|
4812
|
-
} catch {
|
|
4813
|
-
continue;
|
|
4814
|
-
}
|
|
4815
|
-
for (const entry of entries) {
|
|
4816
|
-
const relative = dir === "" ? entry.name : `${dir}/${entry.name}`;
|
|
4817
|
-
if (entry.isDirectory() && !skipped.has(entry.name)) pending.push(relative);
|
|
4818
|
-
if (entry.isFile()) files.push(relative);
|
|
4819
|
-
}
|
|
4820
|
-
}
|
|
4821
|
-
return files;
|
|
4822
|
-
}
|
|
4823
|
-
//#endregion
|
|
4824
4866
|
//#region src/services/runs.ts
|
|
4825
4867
|
const locks = /* @__PURE__ */ new Map();
|
|
4826
4868
|
function stopAll() {
|
|
@@ -6034,6 +6076,7 @@ const handlers = {
|
|
|
6034
6076
|
capabilities: (params) => capabilities(params.provider, params.accountId, params.projectId === null ? os.homedir() : projectPath(params.projectId)),
|
|
6035
6077
|
"files.list": (params) => listFiles(projectPath(params.projectId)),
|
|
6036
6078
|
"files.read": (params) => readProjectFile(projectPath(params.projectId), params.path),
|
|
6079
|
+
"files.media": (params) => linkProjectMedia(projectPath(params.projectId), params.path),
|
|
6037
6080
|
"files.upload": (params) => saveUpload(params.name, Buffer.from(params.data, "base64")),
|
|
6038
6081
|
"tasks.output": (params) => taskOutput(params.sessionId, params.path),
|
|
6039
6082
|
"git.status": (params) => gitRepos(projectPath(params.projectId)),
|
|
@@ -6212,6 +6255,10 @@ const code = args.auth && args.pair && !hasDevices() ? addCode(null) : null;
|
|
|
6212
6255
|
const tickets = /* @__PURE__ */ new Map();
|
|
6213
6256
|
const server = createServer((request, response) => {
|
|
6214
6257
|
const url = new URL(request.url ?? "/", "http://localhost");
|
|
6258
|
+
if (url.pathname === "/api/media") {
|
|
6259
|
+
serveMedia(url.searchParams.get("token"), request, response);
|
|
6260
|
+
return;
|
|
6261
|
+
}
|
|
6215
6262
|
if (url.pathname.startsWith("/api/")) {
|
|
6216
6263
|
answerApi(url.pathname, request, response);
|
|
6217
6264
|
return;
|
|
@@ -6308,6 +6355,64 @@ function answerApi(pathname, request, response) {
|
|
|
6308
6355
|
response.statusCode = 404;
|
|
6309
6356
|
response.end("not found");
|
|
6310
6357
|
}
|
|
6358
|
+
function serveMedia(token, request, response) {
|
|
6359
|
+
if (request.method !== "GET") {
|
|
6360
|
+
response.statusCode = 405;
|
|
6361
|
+
response.end("use GET");
|
|
6362
|
+
return;
|
|
6363
|
+
}
|
|
6364
|
+
const link = findMediaLink(token);
|
|
6365
|
+
if (link === null) {
|
|
6366
|
+
response.statusCode = 404;
|
|
6367
|
+
response.end("This media link expired");
|
|
6368
|
+
return;
|
|
6369
|
+
}
|
|
6370
|
+
const stats = statSync(link.file, { throwIfNoEntry: false });
|
|
6371
|
+
if (stats === void 0 || !stats.isFile()) {
|
|
6372
|
+
response.statusCode = 404;
|
|
6373
|
+
response.end("not found");
|
|
6374
|
+
return;
|
|
6375
|
+
}
|
|
6376
|
+
response.setHeader("content-type", link.mediaType);
|
|
6377
|
+
response.setHeader("accept-ranges", "bytes");
|
|
6378
|
+
response.setHeader("cache-control", "private, max-age=3600");
|
|
6379
|
+
response.setHeader("content-security-policy", "sandbox");
|
|
6380
|
+
response.setHeader("x-content-type-options", "nosniff");
|
|
6381
|
+
if (request.headers.range === void 0) {
|
|
6382
|
+
response.setHeader("content-length", stats.size);
|
|
6383
|
+
createReadStream(link.file).on("error", () => response.destroy()).pipe(response);
|
|
6384
|
+
return;
|
|
6385
|
+
}
|
|
6386
|
+
const range = byteRange(request.headers.range, stats.size);
|
|
6387
|
+
if (range === null) {
|
|
6388
|
+
response.statusCode = 416;
|
|
6389
|
+
response.setHeader("content-range", `bytes */${stats.size}`);
|
|
6390
|
+
response.end();
|
|
6391
|
+
return;
|
|
6392
|
+
}
|
|
6393
|
+
response.statusCode = 206;
|
|
6394
|
+
response.setHeader("content-range", `bytes ${range.start}-${range.end}/${stats.size}`);
|
|
6395
|
+
response.setHeader("content-length", range.end - range.start + 1);
|
|
6396
|
+
createReadStream(link.file, range).on("error", () => response.destroy()).pipe(response);
|
|
6397
|
+
}
|
|
6398
|
+
function byteRange(header, size) {
|
|
6399
|
+
const match = /^bytes=(?:(\d+)-(\d*)|-(\d+))$/.exec(header);
|
|
6400
|
+
if (match === null) return null;
|
|
6401
|
+
if (match[3] !== void 0) {
|
|
6402
|
+
if (size === 0 || Number(match[3]) === 0) return null;
|
|
6403
|
+
return {
|
|
6404
|
+
start: Math.max(size - Number(match[3]), 0),
|
|
6405
|
+
end: size - 1
|
|
6406
|
+
};
|
|
6407
|
+
}
|
|
6408
|
+
const start = Number(match[1]);
|
|
6409
|
+
const end = match[2] === "" ? size - 1 : Math.min(Number(match[2]), size - 1);
|
|
6410
|
+
if (start > end) return null;
|
|
6411
|
+
return {
|
|
6412
|
+
start,
|
|
6413
|
+
end
|
|
6414
|
+
};
|
|
6415
|
+
}
|
|
6311
6416
|
function answerJson(response, body) {
|
|
6312
6417
|
response.setHeader("content-type", "application/json");
|
|
6313
6418
|
response.end(JSON.stringify(body));
|