offsite 0.0.16 → 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 +251 -138
- 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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
2
|
-
import { chmodSync, closeSync, createReadStream, existsSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, watch, writeFileSync } from "node:fs";
|
|
2
|
+
import { chmodSync, closeSync, createReadStream, existsSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, watch, writeFileSync } from "node:fs";
|
|
3
3
|
import { createServer } from "node:http";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { consola } from "consola";
|
|
@@ -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 = "";
|
|
@@ -2722,8 +2730,15 @@ async function updateSession(id, patch) {
|
|
|
2722
2730
|
break;
|
|
2723
2731
|
case "opencode": await renameOpenCode(id, patch.title);
|
|
2724
2732
|
}
|
|
2725
|
-
if (patch.status === "inactive")
|
|
2726
|
-
|
|
2733
|
+
if (patch.status === "inactive") {
|
|
2734
|
+
const run = runs.get(id);
|
|
2735
|
+
if (run !== void 0) {
|
|
2736
|
+
if (run.running) await run.interrupt();
|
|
2737
|
+
run.stop();
|
|
2738
|
+
await run.closed;
|
|
2739
|
+
}
|
|
2740
|
+
db.delete(sessions$1).where(eq(sessions$1.sessionId, id)).run();
|
|
2741
|
+
} else if (patch.status !== void 0) {
|
|
2727
2742
|
const set = patch.status === "active" ? {
|
|
2728
2743
|
status: patch.status,
|
|
2729
2744
|
interactedAt: Date.now()
|
|
@@ -4312,13 +4327,13 @@ var Run = class {
|
|
|
4312
4327
|
}, IDLE_LIMIT);
|
|
4313
4328
|
}
|
|
4314
4329
|
ask(options) {
|
|
4315
|
-
const request = toRequest({
|
|
4330
|
+
const request = scheduleAutoResolve(this.sessionId, toRequest({
|
|
4316
4331
|
id: randomUUID(),
|
|
4317
4332
|
toolName: options.toolName,
|
|
4318
4333
|
input: options.input,
|
|
4319
4334
|
title: options.title,
|
|
4320
4335
|
canAlwaysAllow: options.suggestions.length > 0
|
|
4321
|
-
});
|
|
4336
|
+
}));
|
|
4322
4337
|
return new Promise((resolve) => {
|
|
4323
4338
|
this.pending.set(request.id, {
|
|
4324
4339
|
request,
|
|
@@ -4332,7 +4347,6 @@ var Run = class {
|
|
|
4332
4347
|
request
|
|
4333
4348
|
});
|
|
4334
4349
|
broadcastActivity();
|
|
4335
|
-
scheduleAutoResolve(this.sessionId, request);
|
|
4336
4350
|
options.signal.addEventListener("abort", () => this.settle(request.id, {
|
|
4337
4351
|
behavior: "deny",
|
|
4338
4352
|
message: "Cancelled"
|
|
@@ -4384,6 +4398,153 @@ var Run = class {
|
|
|
4384
4398
|
}
|
|
4385
4399
|
};
|
|
4386
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
|
|
4387
4548
|
//#region src/services/git.ts
|
|
4388
4549
|
const DIFF_LIMIT = 8e4;
|
|
4389
4550
|
const UNTRACKED_LIMIT = 100;
|
|
@@ -4642,6 +4803,7 @@ async function isRepo(cwd) {
|
|
|
4642
4803
|
}
|
|
4643
4804
|
async function repoDiff(repo, prefix) {
|
|
4644
4805
|
const tracked = await git(repo, [
|
|
4806
|
+
"--no-optional-locks",
|
|
4645
4807
|
"diff",
|
|
4646
4808
|
await diffBase(repo),
|
|
4647
4809
|
"--no-color",
|
|
@@ -4692,6 +4854,7 @@ function newFilePatch(cwd, file, prefix) {
|
|
|
4692
4854
|
const name = `${prefix}${file}`;
|
|
4693
4855
|
const header = `diff --git a/${name} b/${name}\nnew file\n--- /dev/null\n+++ b/${name}\n`;
|
|
4694
4856
|
const full = path.join(cwd, file);
|
|
4857
|
+
if (lstatSync(full).isSymbolicLink()) return `${header}@@ -0,0 +1 @@\n+${readlinkSync(full)}\n`;
|
|
4695
4858
|
if (statSync(full).size > NEW_FILE_LIMIT) return `${header}Binary or large file\n`;
|
|
4696
4859
|
const content = readFileSync(full, "utf8");
|
|
4697
4860
|
if (content.includes("\0")) return `${header}Binary or large file\n`;
|
|
@@ -4700,119 +4863,6 @@ function newFilePatch(cwd, file, prefix) {
|
|
|
4700
4863
|
return `${header}@@ -0,0 +1,${lines.length} @@\n${lines.map((line) => `+${line}\n`).join("")}`;
|
|
4701
4864
|
}
|
|
4702
4865
|
//#endregion
|
|
4703
|
-
//#region src/services/files.ts
|
|
4704
|
-
const FILE_LIMIT = 2e4;
|
|
4705
|
-
const OUTPUT_LIMIT$1 = 262144;
|
|
4706
|
-
const READ_LIMIT = 1048576;
|
|
4707
|
-
const images = /* @__PURE__ */ new Map([
|
|
4708
|
-
[".png", "image/png"],
|
|
4709
|
-
[".jpg", "image/jpeg"],
|
|
4710
|
-
[".jpeg", "image/jpeg"],
|
|
4711
|
-
[".gif", "image/gif"],
|
|
4712
|
-
[".webp", "image/webp"],
|
|
4713
|
-
[".svg", "image/svg+xml"]
|
|
4714
|
-
]);
|
|
4715
|
-
const skipped = /* @__PURE__ */ new Set([
|
|
4716
|
-
"node_modules",
|
|
4717
|
-
".git",
|
|
4718
|
-
"dist",
|
|
4719
|
-
"target",
|
|
4720
|
-
".next"
|
|
4721
|
-
]);
|
|
4722
|
-
async function listFiles(cwd) {
|
|
4723
|
-
try {
|
|
4724
|
-
const { stdout } = await git(cwd, [
|
|
4725
|
-
"ls-files",
|
|
4726
|
-
"--cached",
|
|
4727
|
-
"--others",
|
|
4728
|
-
"--exclude-standard"
|
|
4729
|
-
]);
|
|
4730
|
-
return stdout.split("\n").filter(Boolean).slice(0, FILE_LIMIT);
|
|
4731
|
-
} catch {
|
|
4732
|
-
return walk(cwd);
|
|
4733
|
-
}
|
|
4734
|
-
}
|
|
4735
|
-
function readProjectFile(cwd, file) {
|
|
4736
|
-
const root = realpathSync(cwd);
|
|
4737
|
-
const full = realpathSync(path.resolve(root, file));
|
|
4738
|
-
const relative = path.relative(root, full);
|
|
4739
|
-
if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) throw new Error(`${file} is outside the project`);
|
|
4740
|
-
const stats = statSync(full);
|
|
4741
|
-
if (!stats.isFile()) throw new Error(`${file} is not a file`);
|
|
4742
|
-
if (stats.size > READ_LIMIT) return {
|
|
4743
|
-
kind: "large",
|
|
4744
|
-
size: stats.size,
|
|
4745
|
-
limit: READ_LIMIT
|
|
4746
|
-
};
|
|
4747
|
-
const data = readFileSync(full);
|
|
4748
|
-
const mediaType = images.get(path.extname(full).toLowerCase());
|
|
4749
|
-
if (mediaType !== void 0) return {
|
|
4750
|
-
kind: "image",
|
|
4751
|
-
size: stats.size,
|
|
4752
|
-
mediaType,
|
|
4753
|
-
data: data.toString("base64")
|
|
4754
|
-
};
|
|
4755
|
-
if (data.includes(0)) return {
|
|
4756
|
-
kind: "binary",
|
|
4757
|
-
size: stats.size
|
|
4758
|
-
};
|
|
4759
|
-
return {
|
|
4760
|
-
kind: "text",
|
|
4761
|
-
size: stats.size,
|
|
4762
|
-
content: data.toString("utf8")
|
|
4763
|
-
};
|
|
4764
|
-
}
|
|
4765
|
-
function saveUpload(name, data) {
|
|
4766
|
-
const dir = path.join(home, "uploads", randomUUID());
|
|
4767
|
-
mkdirSync(dir, { recursive: true });
|
|
4768
|
-
const file = path.join(dir, path.basename(name));
|
|
4769
|
-
writeFileSync(file, data);
|
|
4770
|
-
return file;
|
|
4771
|
-
}
|
|
4772
|
-
async function taskOutput(sessionId, file) {
|
|
4773
|
-
const tasks = path.dirname(file);
|
|
4774
|
-
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");
|
|
4775
|
-
let stats;
|
|
4776
|
-
try {
|
|
4777
|
-
stats = await lstat(file);
|
|
4778
|
-
} catch (error) {
|
|
4779
|
-
if (hasCode(error, "ENOENT")) return null;
|
|
4780
|
-
throw error;
|
|
4781
|
-
}
|
|
4782
|
-
if (!stats.isFile()) throw new Error("Background task output is not a regular file");
|
|
4783
|
-
const start = Math.max(0, stats.size - OUTPUT_LIMIT$1);
|
|
4784
|
-
const handle = await open(file, "r");
|
|
4785
|
-
try {
|
|
4786
|
-
const buffer = Buffer.alloc(stats.size - start);
|
|
4787
|
-
const read = await handle.read(buffer, 0, buffer.length, start);
|
|
4788
|
-
return {
|
|
4789
|
-
text: buffer.subarray(0, read.bytesRead).toString("utf8"),
|
|
4790
|
-
truncated: start > 0
|
|
4791
|
-
};
|
|
4792
|
-
} finally {
|
|
4793
|
-
await handle.close();
|
|
4794
|
-
}
|
|
4795
|
-
}
|
|
4796
|
-
function walk(root) {
|
|
4797
|
-
const files = [];
|
|
4798
|
-
const pending = [""];
|
|
4799
|
-
while (pending.length > 0 && files.length < FILE_LIMIT) {
|
|
4800
|
-
const dir = pending.pop() ?? "";
|
|
4801
|
-
let entries;
|
|
4802
|
-
try {
|
|
4803
|
-
entries = readdirSync(path.join(root, dir), { withFileTypes: true });
|
|
4804
|
-
} catch {
|
|
4805
|
-
continue;
|
|
4806
|
-
}
|
|
4807
|
-
for (const entry of entries) {
|
|
4808
|
-
const relative = dir === "" ? entry.name : `${dir}/${entry.name}`;
|
|
4809
|
-
if (entry.isDirectory() && !skipped.has(entry.name)) pending.push(relative);
|
|
4810
|
-
if (entry.isFile()) files.push(relative);
|
|
4811
|
-
}
|
|
4812
|
-
}
|
|
4813
|
-
return files;
|
|
4814
|
-
}
|
|
4815
|
-
//#endregion
|
|
4816
4866
|
//#region src/services/runs.ts
|
|
4817
4867
|
const locks = /* @__PURE__ */ new Map();
|
|
4818
4868
|
function stopAll() {
|
|
@@ -6026,6 +6076,7 @@ const handlers = {
|
|
|
6026
6076
|
capabilities: (params) => capabilities(params.provider, params.accountId, params.projectId === null ? os.homedir() : projectPath(params.projectId)),
|
|
6027
6077
|
"files.list": (params) => listFiles(projectPath(params.projectId)),
|
|
6028
6078
|
"files.read": (params) => readProjectFile(projectPath(params.projectId), params.path),
|
|
6079
|
+
"files.media": (params) => linkProjectMedia(projectPath(params.projectId), params.path),
|
|
6029
6080
|
"files.upload": (params) => saveUpload(params.name, Buffer.from(params.data, "base64")),
|
|
6030
6081
|
"tasks.output": (params) => taskOutput(params.sessionId, params.path),
|
|
6031
6082
|
"git.status": (params) => gitRepos(projectPath(params.projectId)),
|
|
@@ -6204,6 +6255,10 @@ const code = args.auth && args.pair && !hasDevices() ? addCode(null) : null;
|
|
|
6204
6255
|
const tickets = /* @__PURE__ */ new Map();
|
|
6205
6256
|
const server = createServer((request, response) => {
|
|
6206
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
|
+
}
|
|
6207
6262
|
if (url.pathname.startsWith("/api/")) {
|
|
6208
6263
|
answerApi(url.pathname, request, response);
|
|
6209
6264
|
return;
|
|
@@ -6300,6 +6355,64 @@ function answerApi(pathname, request, response) {
|
|
|
6300
6355
|
response.statusCode = 404;
|
|
6301
6356
|
response.end("not found");
|
|
6302
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
|
+
}
|
|
6303
6416
|
function answerJson(response, body) {
|
|
6304
6417
|
response.setHeader("content-type", "application/json");
|
|
6305
6418
|
response.end(JSON.stringify(body));
|